2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* linux/drivers/video/fbmem.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1994 Martin Schaller
|
|
|
|
*
|
|
|
|
* 2001 - Documented with DocBook
|
|
|
|
* - Brad Douglas <brad@neruo.com>
|
|
|
|
*
|
|
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
|
|
* License. See the file COPYING in the main directory of this archive
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
2005-11-09 05:39:19 +00:00
|
|
|
#include <linux/compat.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/mman.h>
|
2006-07-10 11:44:12 +00:00
|
|
|
#include <linux/vt.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/linux_logo.h>
|
|
|
|
#include <linux/proc_fs.h>
|
2008-04-28 09:15:42 +00:00
|
|
|
#include <linux/seq_file.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/console.h>
|
|
|
|
#include <linux/kmod.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/efi.h>
|
2007-07-17 11:05:27 +00:00
|
|
|
#include <linux/fb.h>
|
fbcon: Make fbcon a built-time depency for fbdev
There's a bunch of folks who're trying to make printk less
contended and faster, but there's a problem: printk uses the
console_lock, and the console lock has become the BKL for all things
fbdev/fbcon, which in turn pulled in half the drm subsystem under that
lock. That's awkward.
There reasons for that is probably just a historical accident:
- fbcon is a runtime option of fbdev, i.e. at runtime you can pick
whether your fbdev driver instances are used as kernel consoles.
Unfortunately this wasn't implemented with some module option, but
through some module loading magic: As long as you don't load
fbcon.ko, there's no fbdev console support, but loading it (in any
order wrt fbdev drivers) will create console instances for all fbdev
drivers.
- This was implemented through a notifier chain. fbcon.ko enumerates
all fbdev instances at load time and also registers itself as
listener in the fbdev notifier. The fbdev core tries to register new
fbdev instances with fbcon using the notifier.
- On top of that the modifier chain is also used at runtime by the
fbdev subsystem to e.g. control backlights for panels.
- The problem is that the notifier puts a mutex locking context
between fbdev and fbcon, which mixes up the locking contexts for
both the runtime usage and the register time usage to notify fbcon.
And at runtime fbcon (through the fbdev core) might call into the
notifier from a printk critical section while console_lock is held.
- This means console_lock must be an outer lock for the entire fbdev
subsystem, which also means it must be acquired when registering a
new framebuffer driver as the outermost lock since we might call
into fbcon (through the notifier) which would result in a locking
inversion if fbcon would acquire the console_lock from its notifier
callback (which it needs to register the console).
- console_lock can be held anywhere, since printk can be called
anywhere, and through the above story, plus drm/kms being an fbdev
driver, we pull in a shocking amount of locking hiercharchy
underneath the console_lock. Which makes cleaning up printk really
hard (not even splitting console_lock into an rwsem is all that
useful due to this).
There's various ways to address this, but the cleanest would be to
make fbcon a compile-time option, where fbdev directly calls the fbcon
register functions from register_framebuffer, or dummy static inline
versions if fbcon is disabled. Maybe augmented with a runtime knob to
disable fbcon, if that's needed (for debugging perhaps).
But this could break some users who rely on the magic "loading
fbcon.ko enables/disables fbdev framebuffers at runtime" thing, even
if that's unlikely. Hence we must be careful:
1. Create a compile-time dependency between fbcon and fbdev in the
least minimal way. This is what this patch does.
2. Wait at least 1 year to give possible users time to scream about
how we broke their setup. Unlikely, since all distros make fbcon
compile-in, and embedded platforms only compile stuff they know they
need anyway. But still.
3. Convert the notifier to direct functions calls, with dummy static
inlines if fbcon is disabled. We'll still need the fb notifier for the
other uses (like backlights), but we can probably move it into the fb
core (atm it must be built-into vmlinux).
4. Push console_lock down the call-chain, until it is down in
console_register again.
5. Finally start to clean up and rework the printk/console locking.
For context of this saga see
commit 50e244cc793d511b86adea24972f3a7264cae114
Author: Alan Cox <alan@linux.intel.com>
Date: Fri Jan 25 10:28:15 2013 +1000
fb: rework locking to fix lock ordering on takeover
plus the pile of commits on top that tried to make this all work
without terminally upsetting lockdep. We've uncovered all this when
console_lock lockdep annotations where added in
commit daee779718a319ff9f83e1ba3339334ac650bb22
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date: Sat Sep 22 19:52:11 2012 +0200
console: implement lockdep support for console_lock
On the patch itself:
- Switch CONFIG_FRAMEBUFFER_CONSOLE to be a boolean, using the overall
CONFIG_FB tristate to decided whether it should be a module or
built-in.
- At first I thought I could force the build depency with just a dummy
symbol that fbcon.ko exports and fb.ko uses. But that leads to a
module depency cycle (it works fine when built-in).
Since this tight binding is the entire goal the simplest solution is
to move all the fbcon modules (and there's a bunch of optinal
source-files which are each modules of their own, for no good
reason) into the overall fb.ko core module. That's a bit more than
what I would have liked to do in this patch, but oh well.
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2017-08-01 15:32:07 +00:00
|
|
|
#include <linux/fbcon.h>
|
2017-07-17 21:10:26 +00:00
|
|
|
#include <linux/mem_encrypt.h>
|
2018-09-01 14:08:45 +00:00
|
|
|
#include <linux/pci.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-07-17 11:05:27 +00:00
|
|
|
#include <asm/fb.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Frame buffer device initialization and setup routines
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define FBPIXMAPSIZE (1024 * 8)
|
|
|
|
|
2011-05-11 21:49:36 +00:00
|
|
|
static DEFINE_MUTEX(registration_lock);
|
2013-08-15 14:12:55 +00:00
|
|
|
|
2006-12-08 10:40:29 +00:00
|
|
|
struct fb_info *registered_fb[FB_MAX] __read_mostly;
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(registered_fb);
|
|
|
|
|
2006-12-08 10:40:29 +00:00
|
|
|
int num_registered_fb __read_mostly;
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(num_registered_fb);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-01-16 16:42:35 +00:00
|
|
|
bool fb_center_logo __read_mostly;
|
|
|
|
|
2019-08-27 11:09:21 +00:00
|
|
|
int fb_logo_count __read_mostly = -1;
|
|
|
|
|
2011-05-11 21:49:36 +00:00
|
|
|
static struct fb_info *get_fb_info(unsigned int idx)
|
|
|
|
{
|
|
|
|
struct fb_info *fb_info;
|
|
|
|
|
|
|
|
if (idx >= FB_MAX)
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
|
|
|
|
mutex_lock(®istration_lock);
|
|
|
|
fb_info = registered_fb[idx];
|
|
|
|
if (fb_info)
|
|
|
|
atomic_inc(&fb_info->count);
|
|
|
|
mutex_unlock(®istration_lock);
|
|
|
|
|
|
|
|
return fb_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void put_fb_info(struct fb_info *fb_info)
|
|
|
|
{
|
|
|
|
if (!atomic_dec_and_test(&fb_info->count))
|
|
|
|
return;
|
|
|
|
if (fb_info->fbops->fb_destroy)
|
|
|
|
fb_info->fbops->fb_destroy(fb_info);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Helpers
|
|
|
|
*/
|
|
|
|
|
2005-09-09 20:04:37 +00:00
|
|
|
int fb_get_color_depth(struct fb_var_screeninfo *var,
|
|
|
|
struct fb_fix_screeninfo *fix)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-09-09 20:04:37 +00:00
|
|
|
int depth = 0;
|
|
|
|
|
|
|
|
if (fix->visual == FB_VISUAL_MONO01 ||
|
|
|
|
fix->visual == FB_VISUAL_MONO10)
|
|
|
|
depth = 1;
|
|
|
|
else {
|
|
|
|
if (var->green.length == var->blue.length &&
|
|
|
|
var->green.length == var->red.length &&
|
|
|
|
var->green.offset == var->blue.offset &&
|
|
|
|
var->green.offset == var->red.offset)
|
|
|
|
depth = var->green.length;
|
|
|
|
else
|
|
|
|
depth = var->green.length + var->red.length +
|
|
|
|
var->blue.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
return depth;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(fb_get_color_depth);
|
|
|
|
|
|
|
|
/*
|
2005-06-22 00:16:58 +00:00
|
|
|
* Data padding functions.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2005-06-22 00:17:07 +00:00
|
|
|
void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-09-09 20:10:04 +00:00
|
|
|
__fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2005-06-22 00:17:07 +00:00
|
|
|
EXPORT_SYMBOL(fb_pad_aligned_buffer);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-06-22 00:17:07 +00:00
|
|
|
void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height,
|
|
|
|
u32 shift_high, u32 shift_low, u32 mod)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
u8 mask = (u8) (0xfff << shift_high), tmp;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = height; i--; ) {
|
|
|
|
for (j = 0; j < idx; j++) {
|
|
|
|
tmp = dst[j];
|
|
|
|
tmp &= mask;
|
|
|
|
tmp |= *src >> shift_low;
|
|
|
|
dst[j] = tmp;
|
|
|
|
tmp = *src << shift_high;
|
|
|
|
dst[j+1] = tmp;
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
tmp = dst[idx];
|
|
|
|
tmp &= mask;
|
|
|
|
tmp |= *src >> shift_low;
|
|
|
|
dst[idx] = tmp;
|
|
|
|
if (shift_high < mod) {
|
|
|
|
tmp = *src << shift_high;
|
|
|
|
dst[idx+1] = tmp;
|
|
|
|
}
|
|
|
|
src++;
|
|
|
|
dst += d_pitch;
|
|
|
|
}
|
|
|
|
}
|
2005-06-22 00:17:07 +00:00
|
|
|
EXPORT_SYMBOL(fb_pad_unaligned_buffer);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* we need to lock this section since fb_cursor
|
|
|
|
* may use fb_imageblit()
|
|
|
|
*/
|
|
|
|
char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
|
|
|
|
{
|
|
|
|
u32 align = buf->buf_align - 1, offset;
|
|
|
|
char *addr = buf->addr;
|
|
|
|
|
|
|
|
/* If IO mapped, we need to sync before access, no sharing of
|
|
|
|
* the pixmap is done
|
|
|
|
*/
|
|
|
|
if (buf->flags & FB_PIXMAP_IO) {
|
|
|
|
if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
|
|
|
|
info->fbops->fb_sync(info);
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See if we fit in the remaining pixmap space */
|
|
|
|
offset = buf->offset + align;
|
|
|
|
offset &= ~align;
|
|
|
|
if (offset + size > buf->size) {
|
|
|
|
/* We do not fit. In order to be able to re-use the buffer,
|
|
|
|
* we must ensure no asynchronous DMA'ing or whatever operation
|
|
|
|
* is in progress, we sync for that.
|
|
|
|
*/
|
|
|
|
if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
|
|
|
|
info->fbops->fb_sync(info);
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
buf->offset = offset + size;
|
|
|
|
addr += offset;
|
|
|
|
|
|
|
|
return addr;
|
|
|
|
}
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(fb_get_buffer_offset);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_LOGO
|
|
|
|
|
|
|
|
static inline unsigned safe_shift(unsigned d, int n)
|
|
|
|
{
|
|
|
|
return n < 0 ? d >> -n : d << n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_set_logocmap(struct fb_info *info,
|
|
|
|
const struct linux_logo *logo)
|
|
|
|
{
|
|
|
|
struct fb_cmap palette_cmap;
|
|
|
|
u16 palette_green[16];
|
|
|
|
u16 palette_blue[16];
|
|
|
|
u16 palette_red[16];
|
|
|
|
int i, j, n;
|
|
|
|
const unsigned char *clut = logo->clut;
|
|
|
|
|
|
|
|
palette_cmap.start = 0;
|
|
|
|
palette_cmap.len = 16;
|
|
|
|
palette_cmap.red = palette_red;
|
|
|
|
palette_cmap.green = palette_green;
|
|
|
|
palette_cmap.blue = palette_blue;
|
|
|
|
palette_cmap.transp = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < logo->clutsize; i += n) {
|
|
|
|
n = logo->clutsize - i;
|
|
|
|
/* palette_cmap provides space for only 16 colors at once */
|
|
|
|
if (n > 16)
|
|
|
|
n = 16;
|
|
|
|
palette_cmap.start = 32 + i;
|
|
|
|
palette_cmap.len = n;
|
|
|
|
for (j = 0; j < n; ++j) {
|
|
|
|
palette_cmap.red[j] = clut[0] << 8 | clut[0];
|
|
|
|
palette_cmap.green[j] = clut[1] << 8 | clut[1];
|
|
|
|
palette_cmap.blue[j] = clut[2] << 8 | clut[2];
|
|
|
|
clut += 3;
|
|
|
|
}
|
|
|
|
fb_set_cmap(&palette_cmap, info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_set_logo_truepalette(struct fb_info *info,
|
|
|
|
const struct linux_logo *logo,
|
|
|
|
u32 *palette)
|
|
|
|
{
|
2006-12-08 10:40:29 +00:00
|
|
|
static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned char redmask, greenmask, bluemask;
|
|
|
|
int redshift, greenshift, blueshift;
|
|
|
|
int i;
|
|
|
|
const unsigned char *clut = logo->clut;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to create a temporary palette since console palette is only
|
|
|
|
* 16 colors long.
|
|
|
|
*/
|
|
|
|
/* Bug: Doesn't obey msb_right ... (who needs that?) */
|
|
|
|
redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8];
|
|
|
|
greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8];
|
|
|
|
bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8];
|
|
|
|
redshift = info->var.red.offset - (8 - info->var.red.length);
|
|
|
|
greenshift = info->var.green.offset - (8 - info->var.green.length);
|
|
|
|
blueshift = info->var.blue.offset - (8 - info->var.blue.length);
|
|
|
|
|
|
|
|
for ( i = 0; i < logo->clutsize; i++) {
|
|
|
|
palette[i+32] = (safe_shift((clut[0] & redmask), redshift) |
|
|
|
|
safe_shift((clut[1] & greenmask), greenshift) |
|
|
|
|
safe_shift((clut[2] & bluemask), blueshift));
|
|
|
|
clut += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_set_logo_directpalette(struct fb_info *info,
|
|
|
|
const struct linux_logo *logo,
|
|
|
|
u32 *palette)
|
|
|
|
{
|
|
|
|
int redshift, greenshift, blueshift;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
redshift = info->var.red.offset;
|
|
|
|
greenshift = info->var.green.offset;
|
|
|
|
blueshift = info->var.blue.offset;
|
|
|
|
|
2008-11-19 23:36:10 +00:00
|
|
|
for (i = 32; i < 32 + logo->clutsize; i++)
|
2005-04-16 22:20:36 +00:00
|
|
|
palette[i] = i << redshift | i << greenshift | i << blueshift;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_set_logo(struct fb_info *info,
|
|
|
|
const struct linux_logo *logo, u8 *dst,
|
|
|
|
int depth)
|
|
|
|
{
|
2005-09-09 20:04:37 +00:00
|
|
|
int i, j, k;
|
2005-04-16 22:20:36 +00:00
|
|
|
const u8 *src = logo->data;
|
2005-09-09 20:04:37 +00:00
|
|
|
u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0;
|
|
|
|
u8 fg = 1, d;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-07-31 07:37:36 +00:00
|
|
|
switch (fb_get_color_depth(&info->var, &info->fix)) {
|
|
|
|
case 1:
|
|
|
|
fg = 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
fg = 3;
|
|
|
|
break;
|
|
|
|
default:
|
2005-04-16 22:20:36 +00:00
|
|
|
fg = 7;
|
2007-07-31 07:37:36 +00:00
|
|
|
break;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-09-09 20:04:37 +00:00
|
|
|
if (info->fix.visual == FB_VISUAL_MONO01 ||
|
|
|
|
info->fix.visual == FB_VISUAL_MONO10)
|
|
|
|
fg = ~((u8) (0xfff << info->var.green.length));
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
switch (depth) {
|
|
|
|
case 4:
|
|
|
|
for (i = 0; i < logo->height; i++)
|
|
|
|
for (j = 0; j < logo->width; src++) {
|
|
|
|
*dst++ = *src >> 4;
|
|
|
|
j++;
|
|
|
|
if (j < logo->width) {
|
|
|
|
*dst++ = *src & 0x0f;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
for (i = 0; i < logo->height; i++) {
|
|
|
|
for (j = 0; j < logo->width; src++) {
|
|
|
|
d = *src ^ xor;
|
2017-08-18 17:56:40 +00:00
|
|
|
for (k = 7; k >= 0 && j < logo->width; k--) {
|
2005-04-16 22:20:36 +00:00
|
|
|
*dst++ = ((d >> k) & 1) ? fg : 0;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors),
|
|
|
|
* linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on
|
|
|
|
* the visual format and color depth of the framebuffer, the DAC, the
|
|
|
|
* pseudo_palette, and the logo data will be adjusted accordingly.
|
|
|
|
*
|
|
|
|
* Case 1 - linux_logo_clut224:
|
|
|
|
* Color exceeds the number of console colors (16), thus we set the hardware DAC
|
|
|
|
* using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set.
|
|
|
|
*
|
|
|
|
* For visuals that require color info from the pseudo_palette, we also construct
|
|
|
|
* one for temporary use. The "needs_directpalette" or "needs_truepalette" flags
|
|
|
|
* will be set.
|
|
|
|
*
|
|
|
|
* Case 2 - linux_logo_vga16:
|
|
|
|
* The number of colors just matches the console colors, thus there is no need
|
|
|
|
* to set the DAC or the pseudo_palette. However, the bitmap is packed, ie,
|
|
|
|
* each byte contains color information for two pixels (upper and lower nibble).
|
|
|
|
* To be consistent with fb_imageblit() usage, we therefore separate the two
|
|
|
|
* nibbles into separate bytes. The "depth" flag will be set to 4.
|
|
|
|
*
|
|
|
|
* Case 3 - linux_logo_mono:
|
|
|
|
* This is similar with Case 2. Each byte contains information for 8 pixels.
|
|
|
|
* We isolate each bit and expand each into a byte. The "depth" flag will
|
|
|
|
* be set to 1.
|
|
|
|
*/
|
|
|
|
static struct logo_data {
|
|
|
|
int depth;
|
|
|
|
int needs_directpalette;
|
|
|
|
int needs_truepalette;
|
|
|
|
int needs_cmapreset;
|
|
|
|
const struct linux_logo *logo;
|
2006-12-08 10:40:29 +00:00
|
|
|
} fb_logo __read_mostly;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-09 05:39:10 +00:00
|
|
|
static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height)
|
|
|
|
{
|
|
|
|
u32 size = width * height, i;
|
|
|
|
|
|
|
|
out += size - 1;
|
|
|
|
|
|
|
|
for (i = size; i--; )
|
|
|
|
*out-- = *in++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height)
|
|
|
|
{
|
2006-06-26 07:26:56 +00:00
|
|
|
int i, j, h = height - 1;
|
2005-11-09 05:39:10 +00:00
|
|
|
|
|
|
|
for (i = 0; i < height; i++)
|
|
|
|
for (j = 0; j < width; j++)
|
2006-06-26 07:26:56 +00:00
|
|
|
out[height * j + h - i] = *in++;
|
2005-11-09 05:39:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height)
|
|
|
|
{
|
|
|
|
int i, j, w = width - 1;
|
|
|
|
|
|
|
|
for (i = 0; i < height; i++)
|
|
|
|
for (j = 0; j < width; j++)
|
|
|
|
out[height * (w - j) + i] = *in++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_rotate_logo(struct fb_info *info, u8 *dst,
|
|
|
|
struct fb_image *image, int rotate)
|
|
|
|
{
|
|
|
|
u32 tmp;
|
|
|
|
|
|
|
|
if (rotate == FB_ROTATE_UD) {
|
|
|
|
fb_rotate_logo_ud(image->data, dst, image->width,
|
|
|
|
image->height);
|
2007-05-08 07:37:57 +00:00
|
|
|
image->dx = info->var.xres - image->width - image->dx;
|
|
|
|
image->dy = info->var.yres - image->height - image->dy;
|
2005-11-09 05:39:10 +00:00
|
|
|
} else if (rotate == FB_ROTATE_CW) {
|
|
|
|
fb_rotate_logo_cw(image->data, dst, image->width,
|
|
|
|
image->height);
|
|
|
|
tmp = image->width;
|
|
|
|
image->width = image->height;
|
|
|
|
image->height = tmp;
|
2007-05-08 07:37:57 +00:00
|
|
|
tmp = image->dy;
|
|
|
|
image->dy = image->dx;
|
|
|
|
image->dx = info->var.xres - image->width - tmp;
|
2006-06-26 07:26:56 +00:00
|
|
|
} else if (rotate == FB_ROTATE_CCW) {
|
2005-11-09 05:39:10 +00:00
|
|
|
fb_rotate_logo_ccw(image->data, dst, image->width,
|
|
|
|
image->height);
|
2006-06-26 07:26:56 +00:00
|
|
|
tmp = image->width;
|
|
|
|
image->width = image->height;
|
|
|
|
image->height = tmp;
|
2007-05-08 07:37:57 +00:00
|
|
|
tmp = image->dx;
|
|
|
|
image->dx = image->dy;
|
|
|
|
image->dy = info->var.yres - image->height - tmp;
|
2005-11-09 05:39:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
image->data = dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
|
2007-05-08 07:37:56 +00:00
|
|
|
int rotate, unsigned int num)
|
2005-11-09 05:39:10 +00:00
|
|
|
{
|
2007-05-08 07:37:56 +00:00
|
|
|
unsigned int x;
|
2005-11-09 05:39:10 +00:00
|
|
|
|
2019-02-08 18:24:47 +00:00
|
|
|
if (image->width > info->var.xres || image->height > info->var.yres)
|
|
|
|
return;
|
|
|
|
|
2005-11-09 05:39:10 +00:00
|
|
|
if (rotate == FB_ROTATE_UR) {
|
2007-05-08 07:37:56 +00:00
|
|
|
for (x = 0;
|
|
|
|
x < num && image->dx + image->width <= info->var.xres;
|
|
|
|
x++) {
|
2005-11-09 05:39:10 +00:00
|
|
|
info->fbops->fb_imageblit(info, image);
|
2007-05-08 07:37:56 +00:00
|
|
|
image->dx += image->width + 8;
|
2005-11-09 05:39:10 +00:00
|
|
|
}
|
|
|
|
} else if (rotate == FB_ROTATE_UD) {
|
2018-12-20 18:13:07 +00:00
|
|
|
u32 dx = image->dx;
|
|
|
|
|
|
|
|
for (x = 0; x < num && image->dx <= dx; x++) {
|
2005-11-09 05:39:10 +00:00
|
|
|
info->fbops->fb_imageblit(info, image);
|
2007-05-08 07:37:56 +00:00
|
|
|
image->dx -= image->width + 8;
|
2005-11-09 05:39:10 +00:00
|
|
|
}
|
|
|
|
} else if (rotate == FB_ROTATE_CW) {
|
2007-05-08 07:37:56 +00:00
|
|
|
for (x = 0;
|
|
|
|
x < num && image->dy + image->height <= info->var.yres;
|
|
|
|
x++) {
|
2005-11-09 05:39:10 +00:00
|
|
|
info->fbops->fb_imageblit(info, image);
|
2007-05-08 07:37:56 +00:00
|
|
|
image->dy += image->height + 8;
|
2005-11-09 05:39:10 +00:00
|
|
|
}
|
|
|
|
} else if (rotate == FB_ROTATE_CCW) {
|
2018-12-20 18:13:07 +00:00
|
|
|
u32 dy = image->dy;
|
|
|
|
|
|
|
|
for (x = 0; x < num && image->dy <= dy; x++) {
|
2005-11-09 05:39:10 +00:00
|
|
|
info->fbops->fb_imageblit(info, image);
|
2007-05-08 07:37:56 +00:00
|
|
|
image->dy -= image->height + 8;
|
2005-11-09 05:39:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-17 11:05:50 +00:00
|
|
|
static int fb_show_logo_line(struct fb_info *info, int rotate,
|
|
|
|
const struct linux_logo *logo, int y,
|
|
|
|
unsigned int n)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
u32 *palette = NULL, *saved_pseudo_palette = NULL;
|
2005-11-09 05:39:10 +00:00
|
|
|
unsigned char *logo_new = NULL, *logo_rotate = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct fb_image image;
|
|
|
|
|
|
|
|
/* Return if the frame buffer is not mapped or suspended */
|
2007-07-17 11:05:50 +00:00
|
|
|
if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||
|
2017-08-01 15:33:02 +00:00
|
|
|
info->fbops->owner)
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
image.depth = 8;
|
2007-07-17 11:05:50 +00:00
|
|
|
image.data = logo->data;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (fb_logo.needs_cmapreset)
|
2007-07-17 11:05:50 +00:00
|
|
|
fb_set_logocmap(info, logo);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-07-17 11:05:50 +00:00
|
|
|
if (fb_logo.needs_truepalette ||
|
2005-04-16 22:20:36 +00:00
|
|
|
fb_logo.needs_directpalette) {
|
|
|
|
palette = kmalloc(256 * 4, GFP_KERNEL);
|
|
|
|
if (palette == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (fb_logo.needs_truepalette)
|
2007-07-17 11:05:50 +00:00
|
|
|
fb_set_logo_truepalette(info, logo, palette);
|
2005-04-16 22:20:36 +00:00
|
|
|
else
|
2007-07-17 11:05:50 +00:00
|
|
|
fb_set_logo_directpalette(info, logo, palette);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
saved_pseudo_palette = info->pseudo_palette;
|
|
|
|
info->pseudo_palette = palette;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fb_logo.depth <= 4) {
|
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 20:55:00 +00:00
|
|
|
logo_new = kmalloc_array(logo->width, logo->height,
|
|
|
|
GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (logo_new == NULL) {
|
|
|
|
kfree(palette);
|
|
|
|
if (saved_pseudo_palette)
|
|
|
|
info->pseudo_palette = saved_pseudo_palette;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
image.data = logo_new;
|
2007-07-17 11:05:50 +00:00
|
|
|
fb_set_logo(info, logo, logo_new, fb_logo.depth);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2019-01-16 16:42:35 +00:00
|
|
|
if (fb_center_logo) {
|
2018-12-20 18:13:08 +00:00
|
|
|
int xres = info->var.xres;
|
|
|
|
int yres = info->var.yres;
|
|
|
|
|
|
|
|
if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW) {
|
|
|
|
xres = info->var.yres;
|
|
|
|
yres = info->var.xres;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (n && (n * (logo->width + 8) - 8 > xres))
|
|
|
|
--n;
|
|
|
|
image.dx = (xres - n * (logo->width + 8) - 8) / 2;
|
|
|
|
image.dy = y ?: (yres - logo->height) / 2;
|
2019-01-16 16:42:35 +00:00
|
|
|
} else {
|
|
|
|
image.dx = 0;
|
|
|
|
image.dy = y;
|
2018-12-20 18:13:08 +00:00
|
|
|
}
|
2019-01-16 16:42:35 +00:00
|
|
|
|
2007-07-17 11:05:50 +00:00
|
|
|
image.width = logo->width;
|
|
|
|
image.height = logo->height;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-09 05:39:10 +00:00
|
|
|
if (rotate) {
|
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 20:55:00 +00:00
|
|
|
logo_rotate = kmalloc_array(logo->width, logo->height,
|
|
|
|
GFP_KERNEL);
|
2005-11-09 05:39:10 +00:00
|
|
|
if (logo_rotate)
|
|
|
|
fb_rotate_logo(info, logo_rotate, &image, rotate);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2005-11-09 05:39:10 +00:00
|
|
|
|
2007-07-17 11:05:50 +00:00
|
|
|
fb_do_show_logo(info, &image, rotate, n);
|
2005-11-09 05:39:10 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(palette);
|
|
|
|
if (saved_pseudo_palette != NULL)
|
|
|
|
info->pseudo_palette = saved_pseudo_palette;
|
|
|
|
kfree(logo_new);
|
2005-11-09 05:39:10 +00:00
|
|
|
kfree(logo_rotate);
|
2018-12-20 18:13:08 +00:00
|
|
|
return image.dy + logo->height;
|
2007-07-17 11:05:50 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 11:05:50 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_FB_LOGO_EXTRA
|
|
|
|
|
|
|
|
#define FB_LOGO_EX_NUM_MAX 10
|
|
|
|
static struct logo_data_extra {
|
|
|
|
const struct linux_logo *logo;
|
|
|
|
unsigned int n;
|
|
|
|
} fb_logo_ex[FB_LOGO_EX_NUM_MAX];
|
|
|
|
static unsigned int fb_logo_ex_num;
|
|
|
|
|
|
|
|
void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n)
|
|
|
|
{
|
|
|
|
if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fb_logo_ex[fb_logo_ex_num].logo = logo;
|
|
|
|
fb_logo_ex[fb_logo_ex_num].n = n;
|
|
|
|
fb_logo_ex_num++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height,
|
|
|
|
unsigned int yres)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/* FIXME: logo_ex supports only truecolor fb. */
|
|
|
|
if (info->fix.visual != FB_VISUAL_TRUECOLOR)
|
|
|
|
fb_logo_ex_num = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < fb_logo_ex_num; i++) {
|
2009-01-06 22:42:37 +00:00
|
|
|
if (fb_logo_ex[i].logo->type != fb_logo.logo->type) {
|
|
|
|
fb_logo_ex[i].logo = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
2007-07-17 11:05:50 +00:00
|
|
|
height += fb_logo_ex[i].logo->height;
|
|
|
|
if (height > yres) {
|
|
|
|
height -= fb_logo_ex[i].logo->height;
|
|
|
|
fb_logo_ex_num = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < fb_logo_ex_num; i++)
|
2018-12-20 18:13:08 +00:00
|
|
|
y = fb_show_logo_line(info, rotate,
|
|
|
|
fb_logo_ex[i].logo, y, fb_logo_ex[i].n);
|
2007-07-17 11:05:50 +00:00
|
|
|
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* !CONFIG_FB_LOGO_EXTRA */
|
|
|
|
|
|
|
|
static inline int fb_prepare_extra_logos(struct fb_info *info,
|
|
|
|
unsigned int height,
|
|
|
|
unsigned int yres)
|
|
|
|
{
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
|
|
|
|
{
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_FB_LOGO_EXTRA */
|
|
|
|
|
|
|
|
|
|
|
|
int fb_prepare_logo(struct fb_info *info, int rotate)
|
|
|
|
{
|
|
|
|
int depth = fb_get_color_depth(&info->var, &info->fix);
|
|
|
|
unsigned int yres;
|
2018-12-20 18:13:08 +00:00
|
|
|
int height;
|
2007-07-17 11:05:50 +00:00
|
|
|
|
|
|
|
memset(&fb_logo, 0, sizeof(struct logo_data));
|
|
|
|
|
|
|
|
if (info->flags & FBINFO_MISC_TILEBLITTING ||
|
2019-08-27 11:09:21 +00:00
|
|
|
info->fbops->owner || !fb_logo_count)
|
2007-07-17 11:05:50 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
|
|
|
|
depth = info->var.blue.length;
|
|
|
|
if (info->var.red.length < depth)
|
|
|
|
depth = info->var.red.length;
|
|
|
|
if (info->var.green.length < depth)
|
|
|
|
depth = info->var.green.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {
|
|
|
|
/* assume console colormap */
|
|
|
|
depth = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return if no suitable logo was found */
|
|
|
|
fb_logo.logo = fb_find_logo(depth);
|
|
|
|
|
|
|
|
if (!fb_logo.logo) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD)
|
|
|
|
yres = info->var.yres;
|
|
|
|
else
|
|
|
|
yres = info->var.xres;
|
|
|
|
|
|
|
|
if (fb_logo.logo->height > yres) {
|
|
|
|
fb_logo.logo = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* What depth we asked for might be different from what we get */
|
|
|
|
if (fb_logo.logo->type == LINUX_LOGO_CLUT224)
|
|
|
|
fb_logo.depth = 8;
|
|
|
|
else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
|
|
|
|
fb_logo.depth = 4;
|
|
|
|
else
|
|
|
|
fb_logo.depth = 1;
|
|
|
|
|
2007-07-31 07:37:36 +00:00
|
|
|
|
2019-12-18 03:00:25 +00:00
|
|
|
if (fb_logo.depth > 4 && depth > 4) {
|
|
|
|
switch (info->fix.visual) {
|
|
|
|
case FB_VISUAL_TRUECOLOR:
|
|
|
|
fb_logo.needs_truepalette = 1;
|
|
|
|
break;
|
|
|
|
case FB_VISUAL_DIRECTCOLOR:
|
|
|
|
fb_logo.needs_directpalette = 1;
|
|
|
|
fb_logo.needs_cmapreset = 1;
|
|
|
|
break;
|
|
|
|
case FB_VISUAL_PSEUDOCOLOR:
|
|
|
|
fb_logo.needs_cmapreset = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-07-31 07:37:36 +00:00
|
|
|
|
2018-12-20 18:13:08 +00:00
|
|
|
height = fb_logo.logo->height;
|
2019-01-16 16:42:35 +00:00
|
|
|
if (fb_center_logo)
|
|
|
|
height += (yres - fb_logo.logo->height) / 2;
|
2018-12-20 18:13:08 +00:00
|
|
|
|
|
|
|
return fb_prepare_extra_logos(info, height, yres);
|
2007-07-17 11:05:50 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 11:05:50 +00:00
|
|
|
int fb_show_logo(struct fb_info *info, int rotate)
|
|
|
|
{
|
2019-08-27 11:09:21 +00:00
|
|
|
unsigned int count;
|
2007-07-17 11:05:50 +00:00
|
|
|
int y;
|
|
|
|
|
2019-08-27 11:09:21 +00:00
|
|
|
if (!fb_logo_count)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
count = fb_logo_count < 0 ? num_online_cpus() : fb_logo_count;
|
|
|
|
y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, count);
|
2007-07-17 11:05:50 +00:00
|
|
|
y = fb_show_extra_logos(info, y, rotate);
|
|
|
|
|
|
|
|
return y;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
#else
|
2005-11-09 05:39:10 +00:00
|
|
|
int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; }
|
|
|
|
int fb_show_logo(struct fb_info *info, int rotate) { return 0; }
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif /* CONFIG_LOGO */
|
2014-04-24 12:28:22 +00:00
|
|
|
EXPORT_SYMBOL(fb_prepare_logo);
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(fb_show_logo);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-04-28 09:15:42 +00:00
|
|
|
static void *fb_seq_start(struct seq_file *m, loff_t *pos)
|
|
|
|
{
|
2011-05-11 21:49:36 +00:00
|
|
|
mutex_lock(®istration_lock);
|
2008-04-28 09:15:42 +00:00
|
|
|
return (*pos < FB_MAX) ? pos : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
|
{
|
|
|
|
(*pos)++;
|
|
|
|
return (*pos < FB_MAX) ? pos : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_seq_stop(struct seq_file *m, void *v)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2011-05-11 21:49:36 +00:00
|
|
|
mutex_unlock(®istration_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-04-28 09:15:42 +00:00
|
|
|
static int fb_seq_show(struct seq_file *m, void *v)
|
|
|
|
{
|
|
|
|
int i = *(loff_t *)v;
|
|
|
|
struct fb_info *fi = registered_fb[i];
|
|
|
|
|
|
|
|
if (fi)
|
|
|
|
seq_printf(m, "%d %s\n", fi->node, fi->fix.id);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-09 21:03:33 +00:00
|
|
|
static const struct seq_operations __maybe_unused proc_fb_seq_ops = {
|
2008-04-28 09:15:42 +00:00
|
|
|
.start = fb_seq_start,
|
|
|
|
.next = fb_seq_next,
|
|
|
|
.stop = fb_seq_stop,
|
|
|
|
.show = fb_seq_show,
|
|
|
|
};
|
|
|
|
|
2011-05-11 21:58:34 +00:00
|
|
|
/*
|
|
|
|
* We hold a reference to the fb_info in file->private_data,
|
|
|
|
* but if the current registered fb has changed, we don't
|
|
|
|
* actually want to use it.
|
|
|
|
*
|
|
|
|
* So look up the fb_info using the inode minor number,
|
|
|
|
* and just verify it against the reference we have.
|
|
|
|
*/
|
|
|
|
static struct fb_info *file_fb_info(struct file *file)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2013-01-23 22:07:38 +00:00
|
|
|
struct inode *inode = file_inode(file);
|
2005-04-16 22:20:36 +00:00
|
|
|
int fbidx = iminor(inode);
|
|
|
|
struct fb_info *info = registered_fb[fbidx];
|
2011-05-11 21:58:34 +00:00
|
|
|
|
|
|
|
if (info != file->private_data)
|
|
|
|
info = NULL;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
unsigned long p = *ppos;
|
|
|
|
struct fb_info *info = file_fb_info(file);
|
fbmem: fix fb_read, fb_write unaligned accesses
fb_{read,write} access the framebuffer using lots of fb_{read,write}l's
but don't check that the file position is aligned which can cause problems
on some architectures which do not support unaligned accesses.
Since the operations are essentially memcpy_{from,to}io, new
fb_memcpy_{from,to}fb macros have been defined and these are used instead.
For Sparc, fb_{read,write} macros use sbus_{read,write}, so this defines
new sbus_memcpy_{from,to}io functions the same as memcpy_{from,to}io but
using sbus_{read,write}b instead of {read,write}b.
Signed-off-by: James Hogan <james@albanarts.com>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-27 22:33:28 +00:00
|
|
|
u8 *buffer, *dst;
|
|
|
|
u8 __iomem *src;
|
|
|
|
int c, cnt = 0, err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned long total_size;
|
|
|
|
|
|
|
|
if (!info || ! info->screen_base)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (info->state != FBINFO_STATE_RUNNING)
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (info->fbops->fb_read)
|
2007-05-08 07:39:02 +00:00
|
|
|
return info->fbops->fb_read(info, buf, count, ppos);
|
2020-07-29 13:41:44 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
total_size = info->screen_size;
|
2006-01-10 04:53:37 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (total_size == 0)
|
|
|
|
total_size = info->fix.smem_len;
|
|
|
|
|
|
|
|
if (p >= total_size)
|
2006-01-10 04:53:37 +00:00
|
|
|
return 0;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (count >= total_size)
|
2006-01-10 04:53:37 +00:00
|
|
|
count = total_size;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (count + p > total_size)
|
|
|
|
count = total_size - p;
|
|
|
|
|
|
|
|
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!buffer)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
fbmem: fix fb_read, fb_write unaligned accesses
fb_{read,write} access the framebuffer using lots of fb_{read,write}l's
but don't check that the file position is aligned which can cause problems
on some architectures which do not support unaligned accesses.
Since the operations are essentially memcpy_{from,to}io, new
fb_memcpy_{from,to}fb macros have been defined and these are used instead.
For Sparc, fb_{read,write} macros use sbus_{read,write}, so this defines
new sbus_memcpy_{from,to}io functions the same as memcpy_{from,to}io but
using sbus_{read,write}b instead of {read,write}b.
Signed-off-by: James Hogan <james@albanarts.com>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-27 22:33:28 +00:00
|
|
|
src = (u8 __iomem *) (info->screen_base + p);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (info->fbops->fb_sync)
|
|
|
|
info->fbops->fb_sync(info);
|
|
|
|
|
|
|
|
while (count) {
|
|
|
|
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
|
|
|
|
dst = buffer;
|
fbmem: fix fb_read, fb_write unaligned accesses
fb_{read,write} access the framebuffer using lots of fb_{read,write}l's
but don't check that the file position is aligned which can cause problems
on some architectures which do not support unaligned accesses.
Since the operations are essentially memcpy_{from,to}io, new
fb_memcpy_{from,to}fb macros have been defined and these are used instead.
For Sparc, fb_{read,write} macros use sbus_{read,write}, so this defines
new sbus_memcpy_{from,to}io functions the same as memcpy_{from,to}io but
using sbus_{read,write}b instead of {read,write}b.
Signed-off-by: James Hogan <james@albanarts.com>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-27 22:33:28 +00:00
|
|
|
fb_memcpy_fromfb(dst, src, c);
|
|
|
|
dst += c;
|
|
|
|
src += c;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (copy_to_user(buf, buffer, c)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*ppos += c;
|
|
|
|
buf += c;
|
|
|
|
cnt += c;
|
|
|
|
count -= c;
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(buffer);
|
2006-01-10 04:53:37 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return (err) ? err : cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
unsigned long p = *ppos;
|
2011-05-11 21:58:34 +00:00
|
|
|
struct fb_info *info = file_fb_info(file);
|
fbmem: fix fb_read, fb_write unaligned accesses
fb_{read,write} access the framebuffer using lots of fb_{read,write}l's
but don't check that the file position is aligned which can cause problems
on some architectures which do not support unaligned accesses.
Since the operations are essentially memcpy_{from,to}io, new
fb_memcpy_{from,to}fb macros have been defined and these are used instead.
For Sparc, fb_{read,write} macros use sbus_{read,write}, so this defines
new sbus_memcpy_{from,to}io functions the same as memcpy_{from,to}io but
using sbus_{read,write}b instead of {read,write}b.
Signed-off-by: James Hogan <james@albanarts.com>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-27 22:33:28 +00:00
|
|
|
u8 *buffer, *src;
|
|
|
|
u8 __iomem *dst;
|
|
|
|
int c, cnt = 0, err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned long total_size;
|
|
|
|
|
|
|
|
if (!info || !info->screen_base)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (info->state != FBINFO_STATE_RUNNING)
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (info->fbops->fb_write)
|
2007-05-08 07:39:02 +00:00
|
|
|
return info->fbops->fb_write(info, buf, count, ppos);
|
2020-07-29 13:41:44 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
total_size = info->screen_size;
|
2006-01-10 04:53:37 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (total_size == 0)
|
|
|
|
total_size = info->fix.smem_len;
|
|
|
|
|
|
|
|
if (p > total_size)
|
2006-04-19 05:22:12 +00:00
|
|
|
return -EFBIG;
|
2006-01-10 04:53:37 +00:00
|
|
|
|
2006-04-19 05:22:12 +00:00
|
|
|
if (count > total_size) {
|
|
|
|
err = -EFBIG;
|
2006-01-10 04:53:37 +00:00
|
|
|
count = total_size;
|
2006-04-19 05:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (count + p > total_size) {
|
|
|
|
if (!err)
|
|
|
|
err = -ENOSPC;
|
2006-01-10 04:53:37 +00:00
|
|
|
|
|
|
|
count = total_size - p;
|
2006-04-19 05:22:12 +00:00
|
|
|
}
|
2006-01-10 04:53:37 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!buffer)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
fbmem: fix fb_read, fb_write unaligned accesses
fb_{read,write} access the framebuffer using lots of fb_{read,write}l's
but don't check that the file position is aligned which can cause problems
on some architectures which do not support unaligned accesses.
Since the operations are essentially memcpy_{from,to}io, new
fb_memcpy_{from,to}fb macros have been defined and these are used instead.
For Sparc, fb_{read,write} macros use sbus_{read,write}, so this defines
new sbus_memcpy_{from,to}io functions the same as memcpy_{from,to}io but
using sbus_{read,write}b instead of {read,write}b.
Signed-off-by: James Hogan <james@albanarts.com>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-27 22:33:28 +00:00
|
|
|
dst = (u8 __iomem *) (info->screen_base + p);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (info->fbops->fb_sync)
|
|
|
|
info->fbops->fb_sync(info);
|
|
|
|
|
|
|
|
while (count) {
|
|
|
|
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
|
|
|
|
src = buffer;
|
2006-01-10 04:53:37 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (copy_from_user(src, buf, c)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2006-01-10 04:53:37 +00:00
|
|
|
|
fbmem: fix fb_read, fb_write unaligned accesses
fb_{read,write} access the framebuffer using lots of fb_{read,write}l's
but don't check that the file position is aligned which can cause problems
on some architectures which do not support unaligned accesses.
Since the operations are essentially memcpy_{from,to}io, new
fb_memcpy_{from,to}fb macros have been defined and these are used instead.
For Sparc, fb_{read,write} macros use sbus_{read,write}, so this defines
new sbus_memcpy_{from,to}io functions the same as memcpy_{from,to}io but
using sbus_{read,write}b instead of {read,write}b.
Signed-off-by: James Hogan <james@albanarts.com>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-27 22:33:28 +00:00
|
|
|
fb_memcpy_tofb(dst, src, c);
|
|
|
|
dst += c;
|
|
|
|
src += c;
|
2005-04-16 22:20:36 +00:00
|
|
|
*ppos += c;
|
|
|
|
buf += c;
|
|
|
|
cnt += c;
|
|
|
|
count -= c;
|
|
|
|
}
|
2006-01-10 04:53:37 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(buffer);
|
|
|
|
|
2006-04-19 05:22:12 +00:00
|
|
|
return (cnt) ? cnt : err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
|
|
|
|
{
|
2005-12-13 06:17:17 +00:00
|
|
|
struct fb_fix_screeninfo *fix = &info->fix;
|
2008-07-24 04:31:28 +00:00
|
|
|
unsigned int yres = info->var.yres;
|
|
|
|
int err = 0;
|
2005-12-13 06:17:17 +00:00
|
|
|
|
|
|
|
if (var->yoffset > 0) {
|
|
|
|
if (var->vmode & FB_VMODE_YWRAP) {
|
|
|
|
if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
|
|
|
|
err = -EINVAL;
|
|
|
|
else
|
|
|
|
yres = 0;
|
|
|
|
} else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
|
|
|
|
err = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (var->xoffset > 0 && (!fix->xpanstep ||
|
|
|
|
(var->xoffset % fix->xpanstep)))
|
|
|
|
err = -EINVAL;
|
|
|
|
|
2008-07-24 04:31:28 +00:00
|
|
|
if (err || !info->fbops->fb_pan_display ||
|
2009-09-22 23:47:41 +00:00
|
|
|
var->yoffset > info->var.yres_virtual - yres ||
|
|
|
|
var->xoffset > info->var.xres_virtual - info->var.xres)
|
2005-12-13 06:17:17 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if ((err = info->fbops->fb_pan_display(var, info)))
|
|
|
|
return err;
|
2010-10-27 22:33:27 +00:00
|
|
|
info->var.xoffset = var->xoffset;
|
|
|
|
info->var.yoffset = var->yoffset;
|
|
|
|
if (var->vmode & FB_VMODE_YWRAP)
|
|
|
|
info->var.vmode |= FB_VMODE_YWRAP;
|
|
|
|
else
|
|
|
|
info->var.vmode &= ~FB_VMODE_YWRAP;
|
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(fb_pan_display);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-05-08 07:39:37 +00:00
|
|
|
static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
|
|
|
|
u32 activate)
|
|
|
|
{
|
|
|
|
struct fb_blit_caps caps, fbcaps;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
memset(&caps, 0, sizeof(caps));
|
|
|
|
memset(&fbcaps, 0, sizeof(fbcaps));
|
|
|
|
caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
|
2019-05-28 09:02:54 +00:00
|
|
|
fbcon_get_requirement(info, &caps);
|
2007-05-08 07:39:37 +00:00
|
|
|
info->fbops->fb_get_caps(info, &fbcaps, var);
|
|
|
|
|
|
|
|
if (((fbcaps.x ^ caps.x) & caps.x) ||
|
|
|
|
((fbcaps.y ^ caps.y) & caps.y) ||
|
|
|
|
(fbcaps.len < caps.len))
|
|
|
|
err = -EINVAL;
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
int
|
|
|
|
fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
|
|
|
|
{
|
2007-05-08 07:39:52 +00:00
|
|
|
int ret = 0;
|
2019-05-28 09:02:58 +00:00
|
|
|
u32 activate;
|
|
|
|
struct fb_var_screeninfo old_var;
|
|
|
|
struct fb_videomode mode;
|
2019-05-28 09:02:59 +00:00
|
|
|
struct fb_event event;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (var->activate & FB_ACTIVATE_INV_MODE) {
|
|
|
|
struct fb_videomode mode1, mode2;
|
|
|
|
|
|
|
|
fb_var_to_videomode(&mode1, var);
|
|
|
|
fb_var_to_videomode(&mode2, &info->var);
|
|
|
|
/* make sure we don't delete the videomode of current var */
|
|
|
|
ret = fb_mode_is_equal(&mode1, &mode2);
|
|
|
|
|
2019-05-28 09:02:53 +00:00
|
|
|
if (!ret)
|
|
|
|
fbcon_mode_deleted(info, &mode1);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (!ret)
|
2019-05-28 09:02:53 +00:00
|
|
|
fb_delete_videomode(&mode1, &info->modelist);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-05-08 07:39:52 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
return ret ? -EINVAL : 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
if (!(var->activate & FB_ACTIVATE_FORCE) &&
|
|
|
|
!memcmp(&info->var, var, sizeof(struct fb_var_screeninfo)))
|
|
|
|
return 0;
|
2007-05-08 07:39:22 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
activate = var->activate;
|
2011-12-13 13:02:26 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
/* When using FOURCC mode, make sure the red, green, blue and
|
|
|
|
* transp fields are set to 0.
|
|
|
|
*/
|
|
|
|
if ((info->fix.capabilities & FB_CAP_FOURCC) &&
|
|
|
|
var->grayscale > 1) {
|
|
|
|
if (var->red.offset || var->green.offset ||
|
|
|
|
var->blue.offset || var->transp.offset ||
|
|
|
|
var->red.length || var->green.length ||
|
|
|
|
var->blue.length || var->transp.length ||
|
|
|
|
var->red.msb_right || var->green.msb_right ||
|
|
|
|
var->blue.msb_right || var->transp.msb_right)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
if (!info->fbops->fb_check_var) {
|
|
|
|
*var = info->var;
|
|
|
|
return 0;
|
|
|
|
}
|
2007-05-08 07:39:52 +00:00
|
|
|
|
2020-07-07 19:26:03 +00:00
|
|
|
/* bitfill_aligned() assumes that it's at least 8x8 */
|
|
|
|
if (var->xres < 8 || var->yres < 8)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
ret = info->fbops->fb_check_var(var, info);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
|
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
if (info->fbops->fb_get_caps) {
|
|
|
|
ret = fb_check_caps(info, var, activate);
|
2007-05-08 07:39:37 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2007-05-08 07:39:37 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
old_var = info->var;
|
|
|
|
info->var = *var;
|
2007-05-08 07:39:37 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
if (info->fbops->fb_set_par) {
|
|
|
|
ret = info->fbops->fb_set_par(info);
|
2009-09-22 23:47:44 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
if (ret) {
|
|
|
|
info->var = old_var;
|
|
|
|
printk(KERN_WARNING "detected "
|
|
|
|
"fb_set_par error, "
|
|
|
|
"error code: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
fb_pan_display(info, &info->var);
|
|
|
|
fb_set_cmap(&info->cmap, info);
|
|
|
|
fb_var_to_videomode(&mode, &info->var);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:58 +00:00
|
|
|
if (info->modelist.prev && info->modelist.next &&
|
|
|
|
!list_empty(&info->modelist))
|
|
|
|
ret = fb_add_videomode(&mode, &info->modelist);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:59 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:59 +00:00
|
|
|
event.info = info;
|
|
|
|
event.data = &mode;
|
|
|
|
fb_notifier_call_chain(FB_EVENT_MODE_CHANGE, &event);
|
|
|
|
|
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(fb_set_var);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
fb_blank(struct fb_info *info, int blank)
|
2020-07-29 13:41:44 +00:00
|
|
|
{
|
2012-05-29 22:07:12 +00:00
|
|
|
struct fb_event event;
|
2019-07-25 14:32:24 +00:00
|
|
|
int ret = -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-12-18 03:00:25 +00:00
|
|
|
if (blank > FB_BLANK_POWERDOWN)
|
|
|
|
blank = FB_BLANK_POWERDOWN;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-05-29 22:07:12 +00:00
|
|
|
event.info = info;
|
|
|
|
event.data = ␣
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (info->fbops->fb_blank)
|
2019-12-18 03:00:25 +00:00
|
|
|
ret = info->fbops->fb_blank(blank, info);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-05-29 22:07:12 +00:00
|
|
|
if (!ret)
|
2006-07-30 10:04:17 +00:00
|
|
|
fb_notifier_call_chain(FB_EVENT_BLANK, &event);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-12-18 03:00:25 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(fb_blank);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 20:53:37 +00:00
|
|
|
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-11-29 10:29:36 +00:00
|
|
|
const struct fb_ops *fb;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct fb_var_screeninfo var;
|
|
|
|
struct fb_fix_screeninfo fix;
|
2009-02-04 23:12:03 +00:00
|
|
|
struct fb_cmap cmap_from;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct fb_cmap_user cmap;
|
|
|
|
void __user *argp = (void __user *)arg;
|
2008-10-19 03:27:51 +00:00
|
|
|
long ret = 0;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case FBIOGET_VSCREENINFO:
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2009-02-04 23:12:03 +00:00
|
|
|
var = info->var;
|
|
|
|
unlock_fb_info(info);
|
|
|
|
|
|
|
|
ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
|
2008-10-19 03:27:51 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIOPUT_VSCREENINFO:
|
2009-02-04 23:12:03 +00:00
|
|
|
if (copy_from_user(&var, argp, sizeof(var)))
|
|
|
|
return -EFAULT;
|
2011-01-25 23:07:35 +00:00
|
|
|
console_lock();
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2008-10-19 03:27:51 +00:00
|
|
|
ret = fb_set_var(info, &var);
|
2020-07-30 10:47:14 +00:00
|
|
|
if (!ret)
|
|
|
|
fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
|
2009-02-04 23:12:03 +00:00
|
|
|
unlock_fb_info(info);
|
2013-11-05 10:00:57 +00:00
|
|
|
console_unlock();
|
2009-02-04 23:12:03 +00:00
|
|
|
if (!ret && copy_to_user(argp, &var, sizeof(var)))
|
2008-10-19 03:27:51 +00:00
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIOGET_FSCREENINFO:
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2020-01-13 11:08:14 +00:00
|
|
|
memcpy(&fix, &info->fix, sizeof(fix));
|
2018-08-22 08:54:04 +00:00
|
|
|
if (info->flags & FBINFO_HIDE_SMEM_START)
|
|
|
|
fix.smem_start = 0;
|
2009-02-04 23:12:03 +00:00
|
|
|
unlock_fb_info(info);
|
|
|
|
|
|
|
|
ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
|
2008-10-19 03:27:51 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIOPUTCMAP:
|
|
|
|
if (copy_from_user(&cmap, argp, sizeof(cmap)))
|
2009-02-04 23:12:03 +00:00
|
|
|
return -EFAULT;
|
|
|
|
ret = fb_set_user_cmap(&cmap, info);
|
2008-10-19 03:27:51 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIOGETCMAP:
|
|
|
|
if (copy_from_user(&cmap, argp, sizeof(cmap)))
|
2009-02-04 23:12:03 +00:00
|
|
|
return -EFAULT;
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2009-02-04 23:12:03 +00:00
|
|
|
cmap_from = info->cmap;
|
|
|
|
unlock_fb_info(info);
|
|
|
|
ret = fb_cmap_to_user(&cmap_from, &cmap);
|
2008-10-19 03:27:51 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIOPAN_DISPLAY:
|
2009-02-04 23:12:03 +00:00
|
|
|
if (copy_from_user(&var, argp, sizeof(var)))
|
|
|
|
return -EFAULT;
|
2011-01-25 23:07:35 +00:00
|
|
|
console_lock();
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2008-10-19 03:27:51 +00:00
|
|
|
ret = fb_pan_display(info, &var);
|
2009-02-04 23:12:03 +00:00
|
|
|
unlock_fb_info(info);
|
2013-11-05 10:00:57 +00:00
|
|
|
console_unlock();
|
2008-10-19 03:27:51 +00:00
|
|
|
if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
|
2009-02-04 23:12:03 +00:00
|
|
|
return -EFAULT;
|
2008-10-19 03:27:51 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIO_CURSOR:
|
2008-10-19 03:27:51 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIOGET_CON2FBMAP:
|
2019-05-28 09:03:01 +00:00
|
|
|
ret = fbcon_get_con2fb_map_ioctl(argp);
|
2008-10-19 03:27:51 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIOPUT_CON2FBMAP:
|
2019-05-28 09:03:01 +00:00
|
|
|
ret = fbcon_set_con2fb_map_ioctl(argp);
|
2008-10-19 03:27:51 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
case FBIOBLANK:
|
2011-01-25 23:07:35 +00:00
|
|
|
console_lock();
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2008-10-19 03:27:51 +00:00
|
|
|
ret = fb_blank(info, arg);
|
2019-05-28 09:02:56 +00:00
|
|
|
/* might again call into fb_blank */
|
|
|
|
fbcon_fb_blanked(info, arg);
|
2009-02-04 23:12:03 +00:00
|
|
|
unlock_fb_info(info);
|
2013-11-05 10:00:57 +00:00
|
|
|
console_unlock();
|
2009-02-04 23:12:03 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
default:
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2009-02-04 23:12:03 +00:00
|
|
|
fb = info->fbops;
|
|
|
|
if (fb->fb_ioctl)
|
2008-10-19 03:27:51 +00:00
|
|
|
ret = fb->fb_ioctl(info, cmd, arg);
|
2009-02-04 23:12:03 +00:00
|
|
|
else
|
|
|
|
ret = -ENOTTY;
|
|
|
|
unlock_fb_info(info);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-11-06 20:53:37 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
2011-05-11 21:58:34 +00:00
|
|
|
struct fb_info *info = file_fb_info(file);
|
2008-11-06 20:53:37 +00:00
|
|
|
|
2011-05-11 21:58:34 +00:00
|
|
|
if (!info)
|
|
|
|
return -ENODEV;
|
2009-02-04 23:12:03 +00:00
|
|
|
return do_fb_ioctl(info, cmd, arg);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
2005-11-09 05:39:19 +00:00
|
|
|
struct fb_fix_screeninfo32 {
|
|
|
|
char id[16];
|
|
|
|
compat_caddr_t smem_start;
|
|
|
|
u32 smem_len;
|
|
|
|
u32 type;
|
|
|
|
u32 type_aux;
|
|
|
|
u32 visual;
|
|
|
|
u16 xpanstep;
|
|
|
|
u16 ypanstep;
|
|
|
|
u16 ywrapstep;
|
|
|
|
u32 line_length;
|
|
|
|
compat_caddr_t mmio_start;
|
|
|
|
u32 mmio_len;
|
|
|
|
u32 accel;
|
|
|
|
u16 reserved[3];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct fb_cmap32 {
|
|
|
|
u32 start;
|
|
|
|
u32 len;
|
|
|
|
compat_caddr_t red;
|
|
|
|
compat_caddr_t green;
|
|
|
|
compat_caddr_t blue;
|
|
|
|
compat_caddr_t transp;
|
|
|
|
};
|
|
|
|
|
2008-11-06 20:53:37 +00:00
|
|
|
static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
2005-11-09 05:39:19 +00:00
|
|
|
{
|
2020-09-18 10:09:04 +00:00
|
|
|
struct fb_cmap32 cmap32;
|
|
|
|
struct fb_cmap cmap_from;
|
|
|
|
struct fb_cmap_user cmap;
|
2005-11-09 05:39:19 +00:00
|
|
|
|
2020-09-18 10:09:04 +00:00
|
|
|
if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32)))
|
2005-11-09 05:39:19 +00:00
|
|
|
return -EFAULT;
|
|
|
|
|
2020-09-18 10:09:04 +00:00
|
|
|
cmap = (struct fb_cmap_user) {
|
|
|
|
.start = cmap32.start,
|
|
|
|
.len = cmap32.len,
|
|
|
|
.red = compat_ptr(cmap32.red),
|
|
|
|
.green = compat_ptr(cmap32.green),
|
|
|
|
.blue = compat_ptr(cmap32.blue),
|
|
|
|
.transp = compat_ptr(cmap32.transp),
|
|
|
|
};
|
2005-11-09 05:39:19 +00:00
|
|
|
|
2020-09-18 10:09:04 +00:00
|
|
|
if (cmd == FBIOPUTCMAP)
|
|
|
|
return fb_set_user_cmap(&cmap, info);
|
2005-11-09 05:39:19 +00:00
|
|
|
|
2020-09-18 10:09:04 +00:00
|
|
|
lock_fb_info(info);
|
|
|
|
cmap_from = info->cmap;
|
|
|
|
unlock_fb_info(info);
|
|
|
|
|
|
|
|
return fb_cmap_to_user(&cmap_from, &cmap);
|
2005-11-09 05:39:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
|
|
|
|
struct fb_fix_screeninfo32 __user *fix32)
|
|
|
|
{
|
|
|
|
__u32 data;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
|
|
|
|
|
|
|
|
data = (__u32) (unsigned long) fix->smem_start;
|
|
|
|
err |= put_user(data, &fix32->smem_start);
|
|
|
|
|
|
|
|
err |= put_user(fix->smem_len, &fix32->smem_len);
|
|
|
|
err |= put_user(fix->type, &fix32->type);
|
|
|
|
err |= put_user(fix->type_aux, &fix32->type_aux);
|
|
|
|
err |= put_user(fix->visual, &fix32->visual);
|
|
|
|
err |= put_user(fix->xpanstep, &fix32->xpanstep);
|
|
|
|
err |= put_user(fix->ypanstep, &fix32->ypanstep);
|
|
|
|
err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
|
|
|
|
err |= put_user(fix->line_length, &fix32->line_length);
|
|
|
|
|
|
|
|
data = (__u32) (unsigned long) fix->mmio_start;
|
|
|
|
err |= put_user(data, &fix32->mmio_start);
|
|
|
|
|
|
|
|
err |= put_user(fix->mmio_len, &fix32->mmio_len);
|
|
|
|
err |= put_user(fix->accel, &fix32->accel);
|
|
|
|
err |= copy_to_user(fix32->reserved, fix->reserved,
|
|
|
|
sizeof(fix->reserved));
|
|
|
|
|
2013-06-18 07:05:29 +00:00
|
|
|
if (err)
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
2005-11-09 05:39:19 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 20:53:37 +00:00
|
|
|
static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
2005-11-09 05:39:19 +00:00
|
|
|
{
|
|
|
|
struct fb_fix_screeninfo fix;
|
|
|
|
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2017-05-27 03:51:09 +00:00
|
|
|
fix = info->fix;
|
2018-08-22 08:54:04 +00:00
|
|
|
if (info->flags & FBINFO_HIDE_SMEM_START)
|
|
|
|
fix.smem_start = 0;
|
2017-05-27 03:51:09 +00:00
|
|
|
unlock_fb_info(info);
|
|
|
|
return do_fscreeninfo_to_user(&fix, compat_ptr(arg));
|
2005-11-09 05:39:19 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 20:53:37 +00:00
|
|
|
static long fb_compat_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2011-05-11 21:58:34 +00:00
|
|
|
struct fb_info *info = file_fb_info(file);
|
2019-11-29 10:29:36 +00:00
|
|
|
const struct fb_ops *fb;
|
2005-11-09 05:39:19 +00:00
|
|
|
long ret = -ENOIOCTLCMD;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2011-05-11 21:58:34 +00:00
|
|
|
if (!info)
|
|
|
|
return -ENODEV;
|
|
|
|
fb = info->fbops;
|
2005-11-09 05:39:19 +00:00
|
|
|
switch(cmd) {
|
|
|
|
case FBIOGET_VSCREENINFO:
|
|
|
|
case FBIOPUT_VSCREENINFO:
|
|
|
|
case FBIOPAN_DISPLAY:
|
|
|
|
case FBIOGET_CON2FBMAP:
|
|
|
|
case FBIOPUT_CON2FBMAP:
|
|
|
|
arg = (unsigned long) compat_ptr(arg);
|
2020-07-07 21:05:39 +00:00
|
|
|
fallthrough;
|
2005-11-09 05:39:19 +00:00
|
|
|
case FBIOBLANK:
|
2008-11-06 20:53:37 +00:00
|
|
|
ret = do_fb_ioctl(info, cmd, arg);
|
|
|
|
break;
|
2005-11-09 05:39:19 +00:00
|
|
|
|
|
|
|
case FBIOGET_FSCREENINFO:
|
2008-11-06 20:53:37 +00:00
|
|
|
ret = fb_get_fscreeninfo(info, cmd, arg);
|
2005-11-09 05:39:19 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FBIOGETCMAP:
|
|
|
|
case FBIOPUTCMAP:
|
2008-11-06 20:53:37 +00:00
|
|
|
ret = fb_getput_cmap(info, cmd, arg);
|
2005-11-09 05:39:19 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (fb->fb_compat_ioctl)
|
2006-01-14 21:21:25 +00:00
|
|
|
ret = fb->fb_compat_ioctl(info, cmd, arg);
|
2005-11-09 05:39:19 +00:00
|
|
|
break;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-07-17 11:05:27 +00:00
|
|
|
static int
|
2005-04-16 22:20:36 +00:00
|
|
|
fb_mmap(struct file *file, struct vm_area_struct * vma)
|
|
|
|
{
|
2011-05-11 21:58:34 +00:00
|
|
|
struct fb_info *info = file_fb_info(file);
|
2019-11-29 10:29:31 +00:00
|
|
|
int (*fb_mmap_fn)(struct fb_info *info, struct vm_area_struct *vma);
|
2013-04-19 16:57:35 +00:00
|
|
|
unsigned long mmio_pgoff;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned long start;
|
|
|
|
u32 len;
|
|
|
|
|
2011-05-11 21:58:34 +00:00
|
|
|
if (!info)
|
|
|
|
return -ENODEV;
|
2009-06-30 18:41:29 +00:00
|
|
|
mutex_lock(&info->mm_lock);
|
2019-11-29 10:29:31 +00:00
|
|
|
|
|
|
|
fb_mmap_fn = info->fbops->fb_mmap;
|
|
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
|
|
|
|
if (info->fbdefio)
|
|
|
|
fb_mmap_fn = fb_deferred_io_mmap;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (fb_mmap_fn) {
|
2005-04-16 22:20:36 +00:00
|
|
|
int res;
|
2017-07-17 21:10:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The framebuffer needs to be accessed decrypted, be sure
|
|
|
|
* SME protection is removed ahead of the call
|
|
|
|
*/
|
|
|
|
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
|
2019-11-29 10:29:31 +00:00
|
|
|
res = fb_mmap_fn(info, vma);
|
2009-06-30 18:41:29 +00:00
|
|
|
mutex_unlock(&info->mm_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-04-19 16:57:35 +00:00
|
|
|
/*
|
|
|
|
* Ugh. This can be either the frame buffer mapping, or
|
|
|
|
* if pgoff points past it, the mmio mapping.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
start = info->fix.smem_start;
|
2013-04-19 16:57:35 +00:00
|
|
|
len = info->fix.smem_len;
|
|
|
|
mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
|
|
|
|
if (vma->vm_pgoff >= mmio_pgoff) {
|
2013-04-24 05:35:20 +00:00
|
|
|
if (info->var.accel_flags) {
|
|
|
|
mutex_unlock(&info->mm_lock);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2013-04-19 16:57:35 +00:00
|
|
|
vma->vm_pgoff -= mmio_pgoff;
|
2005-04-16 22:20:36 +00:00
|
|
|
start = info->fix.mmio_start;
|
2013-04-19 16:57:35 +00:00
|
|
|
len = info->fix.mmio_len;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-06-30 18:41:29 +00:00
|
|
|
mutex_unlock(&info->mm_lock);
|
2013-04-19 16:57:35 +00:00
|
|
|
|
2010-08-11 01:02:45 +00:00
|
|
|
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
2013-04-19 16:57:35 +00:00
|
|
|
fb_pgprotect(file, vma, start);
|
|
|
|
|
|
|
|
return vm_iomap_memory(vma, start, len);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fb_open(struct inode *inode, struct file *file)
|
2008-11-06 20:53:37 +00:00
|
|
|
__acquires(&info->lock)
|
|
|
|
__releases(&info->lock)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int fbidx = iminor(inode);
|
|
|
|
struct fb_info *info;
|
|
|
|
int res = 0;
|
|
|
|
|
2011-05-11 21:49:36 +00:00
|
|
|
info = get_fb_info(fbidx);
|
|
|
|
if (!info) {
|
2008-07-09 08:28:38 +00:00
|
|
|
request_module("fb%d", fbidx);
|
2011-05-11 21:49:36 +00:00
|
|
|
info = get_fb_info(fbidx);
|
|
|
|
if (!info)
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
if (IS_ERR(info))
|
|
|
|
return PTR_ERR(info);
|
|
|
|
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2008-05-15 22:30:36 +00:00
|
|
|
if (!try_module_get(info->fbops->owner)) {
|
|
|
|
res = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
2006-01-10 04:53:48 +00:00
|
|
|
file->private_data = info;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (info->fbops->fb_open) {
|
|
|
|
res = info->fbops->fb_open(info,1);
|
|
|
|
if (res)
|
|
|
|
module_put(info->fbops->owner);
|
|
|
|
}
|
2021-06-01 14:30:30 +00:00
|
|
|
#ifdef CONFIG_FB_DEFERRED_IO
|
|
|
|
if (info->fbdefio)
|
|
|
|
fb_deferred_io_open(info, inode, file);
|
|
|
|
#endif
|
2008-05-15 22:30:36 +00:00
|
|
|
out:
|
2019-05-28 09:02:47 +00:00
|
|
|
unlock_fb_info(info);
|
2011-05-11 21:49:36 +00:00
|
|
|
if (res)
|
|
|
|
put_fb_info(info);
|
2005-04-16 22:20:36 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-07-29 13:41:44 +00:00
|
|
|
static int
|
2005-04-16 22:20:36 +00:00
|
|
|
fb_release(struct inode *inode, struct file *file)
|
2008-11-06 20:53:37 +00:00
|
|
|
__acquires(&info->lock)
|
|
|
|
__releases(&info->lock)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-01-10 04:53:48 +00:00
|
|
|
struct fb_info * const info = file->private_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(info);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (info->fbops->fb_release)
|
|
|
|
info->fbops->fb_release(info,1);
|
|
|
|
module_put(info->fbops->owner);
|
2019-05-28 09:02:47 +00:00
|
|
|
unlock_fb_info(info);
|
2011-05-11 21:49:36 +00:00
|
|
|
put_fb_info(info);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-12 16:28:11 +00:00
|
|
|
#if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU)
|
2017-01-04 09:12:55 +00:00
|
|
|
unsigned long get_fb_unmapped_area(struct file *filp,
|
|
|
|
unsigned long addr, unsigned long len,
|
|
|
|
unsigned long pgoff, unsigned long flags)
|
|
|
|
{
|
|
|
|
struct fb_info * const info = filp->private_data;
|
|
|
|
unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len);
|
|
|
|
|
|
|
|
if (pgoff > fb_size || len > fb_size - pgoff)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return (unsigned long)info->screen_base + pgoff;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-12-08 10:40:29 +00:00
|
|
|
static const struct file_operations fb_fops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.read = fb_read,
|
|
|
|
.write = fb_write,
|
2008-10-19 03:27:51 +00:00
|
|
|
.unlocked_ioctl = fb_ioctl,
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = fb_compat_ioctl,
|
|
|
|
#endif
|
|
|
|
.mmap = fb_mmap,
|
|
|
|
.open = fb_open,
|
|
|
|
.release = fb_release,
|
2017-01-04 09:12:55 +00:00
|
|
|
#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
|
2017-07-12 16:28:11 +00:00
|
|
|
(defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
|
|
|
|
!defined(CONFIG_MMU))
|
2005-04-16 22:20:36 +00:00
|
|
|
.get_unmapped_area = get_fb_unmapped_area,
|
|
|
|
#endif
|
2007-05-08 07:37:41 +00:00
|
|
|
#ifdef CONFIG_FB_DEFERRED_IO
|
|
|
|
.fsync = fb_deferred_io_fsync,
|
|
|
|
#endif
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-15 16:52:59 +00:00
|
|
|
.llseek = default_llseek,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
2006-06-26 07:27:05 +00:00
|
|
|
struct class *fb_class;
|
|
|
|
EXPORT_SYMBOL(fb_class);
|
2008-04-28 09:14:49 +00:00
|
|
|
|
|
|
|
static int fb_check_foreignness(struct fb_info *fi)
|
|
|
|
{
|
|
|
|
const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
|
|
|
|
|
|
|
|
fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
|
|
|
|
|
|
|
|
#ifdef __BIG_ENDIAN
|
|
|
|
fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
|
|
|
|
#else
|
|
|
|
fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
|
|
|
|
#endif /* __BIG_ENDIAN */
|
|
|
|
|
|
|
|
if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
|
|
|
|
pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
|
|
|
|
"support this framebuffer\n", fi->fix.id);
|
|
|
|
return -ENOSYS;
|
|
|
|
} else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
|
|
|
|
pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
|
|
|
|
"support this framebuffer\n", fi->fix.id);
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-05-16 15:27:03 +00:00
|
|
|
static bool apertures_overlap(struct aperture *gen, struct aperture *hw)
|
2009-06-16 22:34:38 +00:00
|
|
|
{
|
|
|
|
/* is the generic aperture base the same as the HW one */
|
2010-05-16 15:27:03 +00:00
|
|
|
if (gen->base == hw->base)
|
2009-06-16 22:34:38 +00:00
|
|
|
return true;
|
|
|
|
/* is the generic aperture base inside the hw base->hw base+size */
|
2010-12-21 01:41:15 +00:00
|
|
|
if (gen->base > hw->base && gen->base < hw->base + hw->size)
|
2009-06-16 22:34:38 +00:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2010-05-16 15:27:03 +00:00
|
|
|
|
2010-05-16 15:29:56 +00:00
|
|
|
static bool fb_do_apertures_overlap(struct apertures_struct *gena,
|
|
|
|
struct apertures_struct *hwa)
|
2010-05-16 15:27:03 +00:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
if (!hwa || !gena)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (i = 0; i < hwa->count; ++i) {
|
|
|
|
struct aperture *h = &hwa->ranges[i];
|
|
|
|
for (j = 0; j < gena->count; ++j) {
|
|
|
|
struct aperture *g = &gena->ranges[j];
|
|
|
|
printk(KERN_DEBUG "checking generic (%llx %llx) vs hw (%llx %llx)\n",
|
2010-05-21 19:44:47 +00:00
|
|
|
(unsigned long long)g->base,
|
|
|
|
(unsigned long long)g->size,
|
|
|
|
(unsigned long long)h->base,
|
|
|
|
(unsigned long long)h->size);
|
2010-05-16 15:27:03 +00:00
|
|
|
if (apertures_overlap(g, h))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-05-28 09:02:49 +00:00
|
|
|
static void do_unregister_framebuffer(struct fb_info *fb_info);
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
|
2010-05-16 15:33:09 +00:00
|
|
|
#define VGA_FB_PHYS 0xA0000
|
2019-05-28 09:02:49 +00:00
|
|
|
static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
|
|
|
|
const char *name, bool primary)
|
2010-05-16 15:29:56 +00:00
|
|
|
{
|
2019-05-28 09:02:49 +00:00
|
|
|
int i;
|
2010-05-16 15:29:56 +00:00
|
|
|
|
|
|
|
/* check all firmware fbs and kick off if the base addr overlaps */
|
2018-07-24 17:11:26 +00:00
|
|
|
for_each_registered_fb(i) {
|
2010-05-16 15:33:09 +00:00
|
|
|
struct apertures_struct *gen_aper;
|
2010-05-16 15:29:56 +00:00
|
|
|
|
|
|
|
if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
|
|
|
|
continue;
|
|
|
|
|
2010-05-16 15:33:09 +00:00
|
|
|
gen_aper = registered_fb[i]->apertures;
|
|
|
|
if (fb_do_apertures_overlap(gen_aper, a) ||
|
|
|
|
(primary && gen_aper && gen_aper->count &&
|
|
|
|
gen_aper->ranges[0].base == VGA_FB_PHYS)) {
|
|
|
|
|
2018-09-01 14:08:44 +00:00
|
|
|
printk(KERN_INFO "fb%d: switching to %s from %s\n",
|
|
|
|
i, name, registered_fb[i]->fix.id);
|
2019-05-28 09:02:49 +00:00
|
|
|
do_unregister_framebuffer(registered_fb[i]);
|
2010-05-16 15:29:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2015-08-25 13:45:14 +00:00
|
|
|
static bool lockless_register_fb;
|
|
|
|
module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, 0400);
|
|
|
|
MODULE_PARM_DESC(lockless_register_fb,
|
|
|
|
"Lockless framebuffer registration for debugging [default=off]");
|
|
|
|
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
static int do_register_framebuffer(struct fb_info *fb_info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2013-12-16 15:57:39 +00:00
|
|
|
int i, ret;
|
2005-09-09 20:09:58 +00:00
|
|
|
struct fb_videomode mode;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-04-28 09:14:49 +00:00
|
|
|
if (fb_check_foreignness(fb_info))
|
|
|
|
return -ENOSYS;
|
|
|
|
|
2019-05-28 09:02:49 +00:00
|
|
|
do_remove_conflicting_framebuffers(fb_info->apertures,
|
|
|
|
fb_info->fix.id,
|
|
|
|
fb_is_primary_device(fb_info));
|
2009-06-16 22:34:38 +00:00
|
|
|
|
2011-05-14 10:24:15 +00:00
|
|
|
if (num_registered_fb == FB_MAX)
|
|
|
|
return -ENXIO;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
num_registered_fb++;
|
|
|
|
for (i = 0 ; i < FB_MAX; i++)
|
|
|
|
if (!registered_fb[i])
|
|
|
|
break;
|
|
|
|
fb_info->node = i;
|
2011-05-11 21:49:36 +00:00
|
|
|
atomic_set(&fb_info->count, 1);
|
2009-07-08 16:20:11 +00:00
|
|
|
mutex_init(&fb_info->lock);
|
|
|
|
mutex_init(&fb_info->mm_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-22 03:03:34 +00:00
|
|
|
fb_info->dev = device_create(fb_class, fb_info->device,
|
|
|
|
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
|
2006-09-14 14:30:59 +00:00
|
|
|
if (IS_ERR(fb_info->dev)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Not fatal */
|
2006-09-14 14:30:59 +00:00
|
|
|
printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
|
|
|
|
fb_info->dev = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
} else
|
2006-09-14 14:30:59 +00:00
|
|
|
fb_init_device(fb_info);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (fb_info->pixmap.addr == NULL) {
|
|
|
|
fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
|
|
|
|
if (fb_info->pixmap.addr) {
|
|
|
|
fb_info->pixmap.size = FBPIXMAPSIZE;
|
|
|
|
fb_info->pixmap.buf_align = 1;
|
|
|
|
fb_info->pixmap.scan_align = 1;
|
2005-06-22 00:17:08 +00:00
|
|
|
fb_info->pixmap.access_align = 32;
|
2005-04-16 22:20:36 +00:00
|
|
|
fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
|
|
|
|
}
|
2020-07-29 13:41:44 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
fb_info->pixmap.offset = 0;
|
|
|
|
|
2007-05-08 07:39:09 +00:00
|
|
|
if (!fb_info->pixmap.blit_x)
|
|
|
|
fb_info->pixmap.blit_x = ~(u32)0;
|
|
|
|
|
|
|
|
if (!fb_info->pixmap.blit_y)
|
|
|
|
fb_info->pixmap.blit_y = ~(u32)0;
|
|
|
|
|
2005-09-09 20:09:58 +00:00
|
|
|
if (!fb_info->modelist.prev || !fb_info->modelist.next)
|
2005-04-16 22:20:36 +00:00
|
|
|
INIT_LIST_HEAD(&fb_info->modelist);
|
|
|
|
|
2013-02-04 13:37:21 +00:00
|
|
|
if (fb_info->skip_vt_switch)
|
|
|
|
pm_vt_switch_required(fb_info->dev, false);
|
|
|
|
else
|
|
|
|
pm_vt_switch_required(fb_info->dev, true);
|
|
|
|
|
2005-09-09 20:09:58 +00:00
|
|
|
fb_var_to_videomode(&mode, &fb_info->var);
|
|
|
|
fb_add_videomode(&mode, &fb_info->modelist);
|
2005-04-16 22:20:36 +00:00
|
|
|
registered_fb[i] = fb_info;
|
|
|
|
|
2019-05-28 09:02:41 +00:00
|
|
|
#ifdef CONFIG_GUMSTIX_AM200EPD
|
|
|
|
{
|
|
|
|
struct fb_event event;
|
|
|
|
event.info = fb_info;
|
|
|
|
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-08-25 13:45:14 +00:00
|
|
|
if (!lockless_register_fb)
|
|
|
|
console_lock();
|
2018-07-31 11:06:58 +00:00
|
|
|
else
|
|
|
|
atomic_inc(&ignore_console_lock_warning);
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(fb_info);
|
2019-05-28 09:02:41 +00:00
|
|
|
ret = fbcon_fb_registered(fb_info);
|
2009-04-13 21:39:39 +00:00
|
|
|
unlock_fb_info(fb_info);
|
2019-05-28 09:02:47 +00:00
|
|
|
|
2015-08-25 13:45:14 +00:00
|
|
|
if (!lockless_register_fb)
|
|
|
|
console_unlock();
|
2018-07-31 11:06:58 +00:00
|
|
|
else
|
|
|
|
atomic_dec(&ignore_console_lock_warning);
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 09:02:49 +00:00
|
|
|
static void unbind_console(struct fb_info *fb_info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
fb: fix lost console when the user unplugs a USB adapter
I have a USB display adapter using the udlfb driver and I use it on an ARM
board that doesn't have any graphics card. When I plug the adapter in, the
console is properly displayed, however when I unplug and re-plug the
adapter, the console is not displayed and I can't access it until I reboot
the board.
The reason is this:
When the adapter is unplugged, dlfb_usb_disconnect calls
unlink_framebuffer, then it waits until the reference count drops to zero
and then it deallocates the framebuffer. However, the console that is
attached to the framebuffer device keeps the reference count non-zero, so
the framebuffer device is never destroyed. When the USB adapter is plugged
again, it creates a new device /dev/fb1 and the console is not attached to
it.
This patch fixes the bug by unbinding the console from unlink_framebuffer.
The code to unbind the console is moved from do_unregister_framebuffer to
a function unbind_console. When the console is unbound, the reference
count drops to zero and the udlfb driver frees the framebuffer. When the
adapter is plugged back, a new framebuffer is created and the console is
attached to it.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Bernie Thompson <bernie@plugable.com>
Cc: Ladislav Michl <ladis@linux-mips.org>
Cc: stable@vger.kernel.org
[b.zolnierkie: preserve old behavior for do_unregister_framebuffer()]
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2018-07-25 13:41:54 +00:00
|
|
|
int i = fb_info->node;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:49 +00:00
|
|
|
if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
|
|
|
|
return;
|
2009-04-13 21:39:39 +00:00
|
|
|
|
2013-01-25 00:28:18 +00:00
|
|
|
console_lock();
|
2019-05-28 09:02:47 +00:00
|
|
|
lock_fb_info(fb_info);
|
2019-05-28 09:02:48 +00:00
|
|
|
fbcon_fb_unbind(fb_info);
|
2009-04-13 21:39:39 +00:00
|
|
|
unlock_fb_info(fb_info);
|
2013-11-05 10:00:57 +00:00
|
|
|
console_unlock();
|
fb: fix lost console when the user unplugs a USB adapter
I have a USB display adapter using the udlfb driver and I use it on an ARM
board that doesn't have any graphics card. When I plug the adapter in, the
console is properly displayed, however when I unplug and re-plug the
adapter, the console is not displayed and I can't access it until I reboot
the board.
The reason is this:
When the adapter is unplugged, dlfb_usb_disconnect calls
unlink_framebuffer, then it waits until the reference count drops to zero
and then it deallocates the framebuffer. However, the console that is
attached to the framebuffer device keeps the reference count non-zero, so
the framebuffer device is never destroyed. When the USB adapter is plugged
again, it creates a new device /dev/fb1 and the console is not attached to
it.
This patch fixes the bug by unbinding the console from unlink_framebuffer.
The code to unbind the console is moved from do_unregister_framebuffer to
a function unbind_console. When the console is unbound, the reference
count drops to zero and the udlfb driver frees the framebuffer. When the
adapter is plugged back, a new framebuffer is created and the console is
attached to it.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Bernie Thompson <bernie@plugable.com>
Cc: Ladislav Michl <ladis@linux-mips.org>
Cc: stable@vger.kernel.org
[b.zolnierkie: preserve old behavior for do_unregister_framebuffer()]
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2018-07-25 13:41:54 +00:00
|
|
|
}
|
|
|
|
|
2019-11-14 12:51:06 +00:00
|
|
|
static void unlink_framebuffer(struct fb_info *fb_info)
|
fb: fix lost console when the user unplugs a USB adapter
I have a USB display adapter using the udlfb driver and I use it on an ARM
board that doesn't have any graphics card. When I plug the adapter in, the
console is properly displayed, however when I unplug and re-plug the
adapter, the console is not displayed and I can't access it until I reboot
the board.
The reason is this:
When the adapter is unplugged, dlfb_usb_disconnect calls
unlink_framebuffer, then it waits until the reference count drops to zero
and then it deallocates the framebuffer. However, the console that is
attached to the framebuffer device keeps the reference count non-zero, so
the framebuffer device is never destroyed. When the USB adapter is plugged
again, it creates a new device /dev/fb1 and the console is not attached to
it.
This patch fixes the bug by unbinding the console from unlink_framebuffer.
The code to unbind the console is moved from do_unregister_framebuffer to
a function unbind_console. When the console is unbound, the reference
count drops to zero and the udlfb driver frees the framebuffer. When the
adapter is plugged back, a new framebuffer is created and the console is
attached to it.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Bernie Thompson <bernie@plugable.com>
Cc: Ladislav Michl <ladis@linux-mips.org>
Cc: stable@vger.kernel.org
[b.zolnierkie: preserve old behavior for do_unregister_framebuffer()]
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2018-07-25 13:41:54 +00:00
|
|
|
{
|
2019-05-28 09:02:50 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
i = fb_info->node;
|
|
|
|
if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!fb_info->dev)
|
|
|
|
return;
|
|
|
|
|
|
|
|
device_destroy(fb_class, MKDEV(FB_MAJOR, i));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2013-02-04 13:37:21 +00:00
|
|
|
pm_vt_switch_unregister(fb_info->dev);
|
|
|
|
|
2019-05-28 09:02:50 +00:00
|
|
|
unbind_console(fb_info);
|
|
|
|
|
|
|
|
fb_info->dev = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void do_unregister_framebuffer(struct fb_info *fb_info)
|
|
|
|
{
|
|
|
|
unlink_framebuffer(fb_info);
|
2006-06-26 07:27:09 +00:00
|
|
|
if (fb_info->pixmap.addr &&
|
|
|
|
(fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(fb_info->pixmap.addr);
|
|
|
|
fb_destroy_modelist(&fb_info->modelist);
|
fb: fix lost console when the user unplugs a USB adapter
I have a USB display adapter using the udlfb driver and I use it on an ARM
board that doesn't have any graphics card. When I plug the adapter in, the
console is properly displayed, however when I unplug and re-plug the
adapter, the console is not displayed and I can't access it until I reboot
the board.
The reason is this:
When the adapter is unplugged, dlfb_usb_disconnect calls
unlink_framebuffer, then it waits until the reference count drops to zero
and then it deallocates the framebuffer. However, the console that is
attached to the framebuffer device keeps the reference count non-zero, so
the framebuffer device is never destroyed. When the USB adapter is plugged
again, it creates a new device /dev/fb1 and the console is not attached to
it.
This patch fixes the bug by unbinding the console from unlink_framebuffer.
The code to unbind the console is moved from do_unregister_framebuffer to
a function unbind_console. When the console is unbound, the reference
count drops to zero and the udlfb driver frees the framebuffer. When the
adapter is plugged back, a new framebuffer is created and the console is
attached to it.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Bernie Thompson <bernie@plugable.com>
Cc: Ladislav Michl <ladis@linux-mips.org>
Cc: stable@vger.kernel.org
[b.zolnierkie: preserve old behavior for do_unregister_framebuffer()]
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2018-07-25 13:41:54 +00:00
|
|
|
registered_fb[fb_info->node] = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
num_registered_fb--;
|
2006-09-14 14:30:59 +00:00
|
|
|
fb_cleanup_device(fb_info);
|
2019-05-28 09:02:41 +00:00
|
|
|
#ifdef CONFIG_GUMSTIX_AM200EPD
|
|
|
|
{
|
|
|
|
struct fb_event event;
|
|
|
|
event.info = fb_info;
|
|
|
|
fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
|
|
|
|
}
|
|
|
|
#endif
|
2013-01-25 00:28:18 +00:00
|
|
|
console_lock();
|
2019-05-28 09:02:41 +00:00
|
|
|
fbcon_fb_unregistered(fb_info);
|
2013-01-25 00:28:18 +00:00
|
|
|
console_unlock();
|
2009-06-16 22:34:38 +00:00
|
|
|
|
|
|
|
/* this may free fb info */
|
2011-05-11 21:49:36 +00:00
|
|
|
put_fb_info(fb_info);
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:08:45 +00:00
|
|
|
/**
|
|
|
|
* remove_conflicting_framebuffers - remove firmware-configured framebuffers
|
|
|
|
* @a: memory range, users of which are to be removed
|
|
|
|
* @name: requesting driver name
|
|
|
|
* @primary: also kick vga16fb if present
|
|
|
|
*
|
|
|
|
* This function removes framebuffer devices (initialized by firmware/bootloader)
|
|
|
|
* which use memory range described by @a. If @a is NULL all such devices are
|
|
|
|
* removed.
|
|
|
|
*/
|
2013-12-16 15:57:39 +00:00
|
|
|
int remove_conflicting_framebuffers(struct apertures_struct *a,
|
|
|
|
const char *name, bool primary)
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
{
|
2018-09-01 14:08:44 +00:00
|
|
|
bool do_free = false;
|
|
|
|
|
|
|
|
if (!a) {
|
|
|
|
a = alloc_apertures(1);
|
|
|
|
if (!a)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
a->ranges[0].base = 0;
|
|
|
|
a->ranges[0].size = ~0;
|
|
|
|
do_free = true;
|
|
|
|
}
|
2013-12-16 15:57:39 +00:00
|
|
|
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
mutex_lock(®istration_lock);
|
2019-05-28 09:02:49 +00:00
|
|
|
do_remove_conflicting_framebuffers(a, name, primary);
|
2011-05-11 21:49:36 +00:00
|
|
|
mutex_unlock(®istration_lock);
|
2013-12-16 15:57:39 +00:00
|
|
|
|
2018-09-01 14:08:44 +00:00
|
|
|
if (do_free)
|
|
|
|
kfree(a);
|
|
|
|
|
2019-05-28 09:02:49 +00:00
|
|
|
return 0;
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(remove_conflicting_framebuffers);
|
|
|
|
|
2018-09-01 14:08:45 +00:00
|
|
|
/**
|
|
|
|
* remove_conflicting_pci_framebuffers - remove firmware-configured framebuffers for PCI devices
|
|
|
|
* @pdev: PCI device
|
|
|
|
* @name: requesting driver name
|
|
|
|
*
|
|
|
|
* This function removes framebuffer devices (eg. initialized by firmware)
|
2019-08-22 09:06:43 +00:00
|
|
|
* using memory range configured for any of @pdev's memory bars.
|
2018-09-01 14:08:45 +00:00
|
|
|
*
|
|
|
|
* The function assumes that PCI device with shadowed ROM drives a primary
|
|
|
|
* display and so kicks out vga16fb.
|
|
|
|
*/
|
2019-08-22 09:06:43 +00:00
|
|
|
int remove_conflicting_pci_framebuffers(struct pci_dev *pdev, const char *name)
|
2018-09-01 14:08:45 +00:00
|
|
|
{
|
|
|
|
struct apertures_struct *ap;
|
|
|
|
bool primary = false;
|
2019-04-01 15:46:57 +00:00
|
|
|
int err, idx, bar;
|
|
|
|
|
2019-09-27 23:43:08 +00:00
|
|
|
for (idx = 0, bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
|
2019-04-01 15:46:57 +00:00
|
|
|
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
|
|
|
|
continue;
|
|
|
|
idx++;
|
|
|
|
}
|
2018-09-01 14:08:45 +00:00
|
|
|
|
2019-04-01 15:46:57 +00:00
|
|
|
ap = alloc_apertures(idx);
|
2018-09-01 14:08:45 +00:00
|
|
|
if (!ap)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-09-27 23:43:08 +00:00
|
|
|
for (idx = 0, bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
|
2019-04-01 15:46:57 +00:00
|
|
|
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
|
|
|
|
continue;
|
|
|
|
ap->ranges[idx].base = pci_resource_start(pdev, bar);
|
|
|
|
ap->ranges[idx].size = pci_resource_len(pdev, bar);
|
2019-08-22 09:06:43 +00:00
|
|
|
pci_dbg(pdev, "%s: bar %d: 0x%lx -> 0x%lx\n", __func__, bar,
|
|
|
|
(unsigned long)pci_resource_start(pdev, bar),
|
|
|
|
(unsigned long)pci_resource_end(pdev, bar));
|
2019-04-01 15:46:57 +00:00
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
2018-09-01 14:08:45 +00:00
|
|
|
#ifdef CONFIG_X86
|
|
|
|
primary = pdev->resource[PCI_ROM_RESOURCE].flags &
|
|
|
|
IORESOURCE_ROM_SHADOW;
|
|
|
|
#endif
|
|
|
|
err = remove_conflicting_framebuffers(ap, name, primary);
|
|
|
|
kfree(ap);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(remove_conflicting_pci_framebuffers);
|
|
|
|
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
/**
|
|
|
|
* register_framebuffer - registers a frame buffer device
|
|
|
|
* @fb_info: frame buffer info structure
|
|
|
|
*
|
|
|
|
* Registers a frame buffer device @fb_info.
|
|
|
|
*
|
|
|
|
* Returns negative errno on error, or zero for success.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
register_framebuffer(struct fb_info *fb_info)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(®istration_lock);
|
|
|
|
ret = do_register_framebuffer(fb_info);
|
|
|
|
mutex_unlock(®istration_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(register_framebuffer);
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* unregister_framebuffer - releases a frame buffer device
|
|
|
|
* @fb_info: frame buffer info structure
|
|
|
|
*
|
|
|
|
* Unregisters a frame buffer device @fb_info.
|
|
|
|
*
|
|
|
|
* Returns negative errno on error, or zero for success.
|
|
|
|
*
|
|
|
|
* This function will also notify the framebuffer console
|
|
|
|
* to release the driver.
|
|
|
|
*
|
|
|
|
* This is meant to be called within a driver's module_exit()
|
|
|
|
* function. If this is called outside module_exit(), ensure
|
|
|
|
* that the driver implements fb_open() and fb_release() to
|
|
|
|
* check that no processes are using the device.
|
|
|
|
*/
|
2019-05-28 09:02:49 +00:00
|
|
|
void
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
unregister_framebuffer(struct fb_info *fb_info)
|
|
|
|
{
|
|
|
|
mutex_lock(®istration_lock);
|
2019-05-28 09:02:49 +00:00
|
|
|
do_unregister_framebuffer(fb_info);
|
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old
conflicting framebuffers, the new registration_lock doesn't protect that
situation. And we can't just add the same locking to the function,
because these functions call each other: register_framebuffer() calls
remove_conflicting_framebuffers, which in turn calls
unregister_framebuffer for any conflicting entry.
In order to fix it, this just creates wrapper functions around all three
functions and makes the versions that actually do the work be called
"do_xxx()", leaving just the wrapper that gets the lock and calls the
worker function.
So the rule becomes simply that "do_xxxx()" has to be called with the
lock held, and now do_register_framebuffer() can just call
do_remove_conflicting_framebuffers(), and that in turn can call
_do_unregister_framebuffer(), and there is no deadlock, and we can hold
the registration lock over the whole sequence, fixing the races.
It also makes error cases simpler, and fixes one situation where we
would return from unregister_framebuffer() without releasing the lock,
pointed out by Bruno Prémont.
Tested-by: Bruno Prémont <bonbons@linux-vserver.org>
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-05-13 23:16:41 +00:00
|
|
|
mutex_unlock(®istration_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(unregister_framebuffer);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* fb_set_suspend - low level driver signals suspend
|
|
|
|
* @info: framebuffer affected
|
|
|
|
* @state: 0 = resuming, !=0 = suspending
|
|
|
|
*
|
|
|
|
* This is meant to be used by low level drivers to
|
|
|
|
* signal suspend/resume to the core & clients.
|
|
|
|
* It must be called with the console semaphore held
|
|
|
|
*/
|
|
|
|
void fb_set_suspend(struct fb_info *info, int state)
|
|
|
|
{
|
2019-05-28 09:02:33 +00:00
|
|
|
WARN_CONSOLE_UNLOCKED();
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (state) {
|
2019-05-28 09:02:52 +00:00
|
|
|
fbcon_suspended(info);
|
2005-04-16 22:20:36 +00:00
|
|
|
info->state = FBINFO_STATE_SUSPENDED;
|
|
|
|
} else {
|
|
|
|
info->state = FBINFO_STATE_RUNNING;
|
2019-05-28 09:02:52 +00:00
|
|
|
fbcon_resumed(info);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2013-08-15 14:12:55 +00:00
|
|
|
EXPORT_SYMBOL(fb_set_suspend);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* fbmem_init - init frame buffer subsystem
|
|
|
|
*
|
|
|
|
* Initialize the frame buffer subsystem.
|
|
|
|
*
|
|
|
|
* NOTE: This function is _only_ to be called by drivers/char/mem.c.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int __init
|
|
|
|
fbmem_init(void)
|
|
|
|
{
|
2016-05-03 02:22:27 +00:00
|
|
|
int ret;
|
|
|
|
|
2018-04-13 17:44:18 +00:00
|
|
|
if (!proc_create_seq("fb", 0, NULL, &proc_fb_seq_ops))
|
2016-05-03 02:22:27 +00:00
|
|
|
return -ENOMEM;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2016-05-03 02:22:27 +00:00
|
|
|
ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
|
|
|
|
if (ret) {
|
2005-04-16 22:20:36 +00:00
|
|
|
printk("unable to get major %d for fb devs\n", FB_MAJOR);
|
2016-05-03 02:22:27 +00:00
|
|
|
goto err_chrdev;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-03-23 18:01:41 +00:00
|
|
|
fb_class = class_create(THIS_MODULE, "graphics");
|
2005-04-16 22:20:36 +00:00
|
|
|
if (IS_ERR(fb_class)) {
|
2016-05-03 02:22:27 +00:00
|
|
|
ret = PTR_ERR(fb_class);
|
|
|
|
pr_warn("Unable to create fb class; errno = %d\n", ret);
|
2005-04-16 22:20:36 +00:00
|
|
|
fb_class = NULL;
|
2016-05-03 02:22:27 +00:00
|
|
|
goto err_class;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
fbcon: Make fbcon a built-time depency for fbdev
There's a bunch of folks who're trying to make printk less
contended and faster, but there's a problem: printk uses the
console_lock, and the console lock has become the BKL for all things
fbdev/fbcon, which in turn pulled in half the drm subsystem under that
lock. That's awkward.
There reasons for that is probably just a historical accident:
- fbcon is a runtime option of fbdev, i.e. at runtime you can pick
whether your fbdev driver instances are used as kernel consoles.
Unfortunately this wasn't implemented with some module option, but
through some module loading magic: As long as you don't load
fbcon.ko, there's no fbdev console support, but loading it (in any
order wrt fbdev drivers) will create console instances for all fbdev
drivers.
- This was implemented through a notifier chain. fbcon.ko enumerates
all fbdev instances at load time and also registers itself as
listener in the fbdev notifier. The fbdev core tries to register new
fbdev instances with fbcon using the notifier.
- On top of that the modifier chain is also used at runtime by the
fbdev subsystem to e.g. control backlights for panels.
- The problem is that the notifier puts a mutex locking context
between fbdev and fbcon, which mixes up the locking contexts for
both the runtime usage and the register time usage to notify fbcon.
And at runtime fbcon (through the fbdev core) might call into the
notifier from a printk critical section while console_lock is held.
- This means console_lock must be an outer lock for the entire fbdev
subsystem, which also means it must be acquired when registering a
new framebuffer driver as the outermost lock since we might call
into fbcon (through the notifier) which would result in a locking
inversion if fbcon would acquire the console_lock from its notifier
callback (which it needs to register the console).
- console_lock can be held anywhere, since printk can be called
anywhere, and through the above story, plus drm/kms being an fbdev
driver, we pull in a shocking amount of locking hiercharchy
underneath the console_lock. Which makes cleaning up printk really
hard (not even splitting console_lock into an rwsem is all that
useful due to this).
There's various ways to address this, but the cleanest would be to
make fbcon a compile-time option, where fbdev directly calls the fbcon
register functions from register_framebuffer, or dummy static inline
versions if fbcon is disabled. Maybe augmented with a runtime knob to
disable fbcon, if that's needed (for debugging perhaps).
But this could break some users who rely on the magic "loading
fbcon.ko enables/disables fbdev framebuffers at runtime" thing, even
if that's unlikely. Hence we must be careful:
1. Create a compile-time dependency between fbcon and fbdev in the
least minimal way. This is what this patch does.
2. Wait at least 1 year to give possible users time to scream about
how we broke their setup. Unlikely, since all distros make fbcon
compile-in, and embedded platforms only compile stuff they know they
need anyway. But still.
3. Convert the notifier to direct functions calls, with dummy static
inlines if fbcon is disabled. We'll still need the fb notifier for the
other uses (like backlights), but we can probably move it into the fb
core (atm it must be built-into vmlinux).
4. Push console_lock down the call-chain, until it is down in
console_register again.
5. Finally start to clean up and rework the printk/console locking.
For context of this saga see
commit 50e244cc793d511b86adea24972f3a7264cae114
Author: Alan Cox <alan@linux.intel.com>
Date: Fri Jan 25 10:28:15 2013 +1000
fb: rework locking to fix lock ordering on takeover
plus the pile of commits on top that tried to make this all work
without terminally upsetting lockdep. We've uncovered all this when
console_lock lockdep annotations where added in
commit daee779718a319ff9f83e1ba3339334ac650bb22
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date: Sat Sep 22 19:52:11 2012 +0200
console: implement lockdep support for console_lock
On the patch itself:
- Switch CONFIG_FRAMEBUFFER_CONSOLE to be a boolean, using the overall
CONFIG_FB tristate to decided whether it should be a module or
built-in.
- At first I thought I could force the build depency with just a dummy
symbol that fbcon.ko exports and fb.ko uses. But that leads to a
module depency cycle (it works fine when built-in).
Since this tight binding is the entire goal the simplest solution is
to move all the fbcon modules (and there's a bunch of optinal
source-files which are each modules of their own, for no good
reason) into the overall fb.ko core module. That's a bit more than
what I would have liked to do in this patch, but oh well.
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2017-08-01 15:32:07 +00:00
|
|
|
|
|
|
|
fb_console_init();
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
2016-05-03 02:22:27 +00:00
|
|
|
|
|
|
|
err_class:
|
|
|
|
unregister_chrdev(FB_MAJOR, "fb");
|
|
|
|
err_chrdev:
|
|
|
|
remove_proc_entry("fb", NULL);
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MODULE
|
|
|
|
module_init(fbmem_init);
|
|
|
|
static void __exit
|
|
|
|
fbmem_exit(void)
|
|
|
|
{
|
fbcon: Make fbcon a built-time depency for fbdev
There's a bunch of folks who're trying to make printk less
contended and faster, but there's a problem: printk uses the
console_lock, and the console lock has become the BKL for all things
fbdev/fbcon, which in turn pulled in half the drm subsystem under that
lock. That's awkward.
There reasons for that is probably just a historical accident:
- fbcon is a runtime option of fbdev, i.e. at runtime you can pick
whether your fbdev driver instances are used as kernel consoles.
Unfortunately this wasn't implemented with some module option, but
through some module loading magic: As long as you don't load
fbcon.ko, there's no fbdev console support, but loading it (in any
order wrt fbdev drivers) will create console instances for all fbdev
drivers.
- This was implemented through a notifier chain. fbcon.ko enumerates
all fbdev instances at load time and also registers itself as
listener in the fbdev notifier. The fbdev core tries to register new
fbdev instances with fbcon using the notifier.
- On top of that the modifier chain is also used at runtime by the
fbdev subsystem to e.g. control backlights for panels.
- The problem is that the notifier puts a mutex locking context
between fbdev and fbcon, which mixes up the locking contexts for
both the runtime usage and the register time usage to notify fbcon.
And at runtime fbcon (through the fbdev core) might call into the
notifier from a printk critical section while console_lock is held.
- This means console_lock must be an outer lock for the entire fbdev
subsystem, which also means it must be acquired when registering a
new framebuffer driver as the outermost lock since we might call
into fbcon (through the notifier) which would result in a locking
inversion if fbcon would acquire the console_lock from its notifier
callback (which it needs to register the console).
- console_lock can be held anywhere, since printk can be called
anywhere, and through the above story, plus drm/kms being an fbdev
driver, we pull in a shocking amount of locking hiercharchy
underneath the console_lock. Which makes cleaning up printk really
hard (not even splitting console_lock into an rwsem is all that
useful due to this).
There's various ways to address this, but the cleanest would be to
make fbcon a compile-time option, where fbdev directly calls the fbcon
register functions from register_framebuffer, or dummy static inline
versions if fbcon is disabled. Maybe augmented with a runtime knob to
disable fbcon, if that's needed (for debugging perhaps).
But this could break some users who rely on the magic "loading
fbcon.ko enables/disables fbdev framebuffers at runtime" thing, even
if that's unlikely. Hence we must be careful:
1. Create a compile-time dependency between fbcon and fbdev in the
least minimal way. This is what this patch does.
2. Wait at least 1 year to give possible users time to scream about
how we broke their setup. Unlikely, since all distros make fbcon
compile-in, and embedded platforms only compile stuff they know they
need anyway. But still.
3. Convert the notifier to direct functions calls, with dummy static
inlines if fbcon is disabled. We'll still need the fb notifier for the
other uses (like backlights), but we can probably move it into the fb
core (atm it must be built-into vmlinux).
4. Push console_lock down the call-chain, until it is down in
console_register again.
5. Finally start to clean up and rework the printk/console locking.
For context of this saga see
commit 50e244cc793d511b86adea24972f3a7264cae114
Author: Alan Cox <alan@linux.intel.com>
Date: Fri Jan 25 10:28:15 2013 +1000
fb: rework locking to fix lock ordering on takeover
plus the pile of commits on top that tried to make this all work
without terminally upsetting lockdep. We've uncovered all this when
console_lock lockdep annotations where added in
commit daee779718a319ff9f83e1ba3339334ac650bb22
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date: Sat Sep 22 19:52:11 2012 +0200
console: implement lockdep support for console_lock
On the patch itself:
- Switch CONFIG_FRAMEBUFFER_CONSOLE to be a boolean, using the overall
CONFIG_FB tristate to decided whether it should be a module or
built-in.
- At first I thought I could force the build depency with just a dummy
symbol that fbcon.ko exports and fb.ko uses. But that leads to a
module depency cycle (it works fine when built-in).
Since this tight binding is the entire goal the simplest solution is
to move all the fbcon modules (and there's a bunch of optinal
source-files which are each modules of their own, for no good
reason) into the overall fb.ko core module. That's a bit more than
what I would have liked to do in this patch, but oh well.
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2017-08-01 15:32:07 +00:00
|
|
|
fb_console_exit();
|
|
|
|
|
2008-04-15 21:34:33 +00:00
|
|
|
remove_proc_entry("fb", NULL);
|
2005-03-23 18:01:41 +00:00
|
|
|
class_destroy(fb_class);
|
2005-07-27 18:46:04 +00:00
|
|
|
unregister_chrdev(FB_MAJOR, "fb");
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module_exit(fbmem_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DESCRIPTION("Framebuffer base");
|
|
|
|
#else
|
|
|
|
subsys_initcall(fbmem_init);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int fb_new_modelist(struct fb_info *info)
|
|
|
|
{
|
|
|
|
struct fb_var_screeninfo var = info->var;
|
|
|
|
struct list_head *pos, *n;
|
|
|
|
struct fb_modelist *modelist;
|
|
|
|
struct fb_videomode *m, mode;
|
2019-06-24 22:37:24 +00:00
|
|
|
int err;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
list_for_each_safe(pos, n, &info->modelist) {
|
|
|
|
modelist = list_entry(pos, struct fb_modelist, list);
|
|
|
|
m = &modelist->mode;
|
|
|
|
fb_videomode_to_var(&var, m);
|
|
|
|
var.activate = FB_ACTIVATE_TEST;
|
|
|
|
err = fb_set_var(info, &var);
|
|
|
|
fb_var_to_videomode(&mode, &var);
|
|
|
|
if (err || !fb_mode_is_equal(m, &mode)) {
|
|
|
|
list_del(pos);
|
|
|
|
kfree(pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-28 09:02:53 +00:00
|
|
|
if (list_empty(&info->modelist))
|
|
|
|
return 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:53 +00:00
|
|
|
fbcon_new_modelist(info);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-05-28 09:02:53 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|