#include <linux/compat.h>
#include <linux/types.h>
#include <linux/errno.h>
-#include <linux/smp_lock.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
struct fb_info *registered_fb[FB_MAX] __read_mostly;
int num_registered_fb __read_mostly;
+int lock_fb_info(struct fb_info *info)
+{
+ mutex_lock(&info->lock);
+ if (!info->fbops) {
+ mutex_unlock(&info->lock);
+ return 0;
+ }
+ return 1;
+}
+EXPORT_SYMBOL(lock_fb_info);
+
/*
* Helpers
*/
greenshift = info->var.green.offset;
blueshift = info->var.blue.offset;
- for (i = 32; i < logo->clutsize; i++)
+ for (i = 32; i < 32 + logo->clutsize; i++)
palette[i] = i << redshift | i << greenshift | i << blueshift;
}
fb_logo_ex_num = 0;
for (i = 0; i < fb_logo_ex_num; i++) {
+ if (fb_logo_ex[i].logo->type != fb_logo.logo->type) {
+ fb_logo_ex[i].logo = NULL;
+ continue;
+ }
height += fb_logo_ex[i].logo->height;
if (height > yres) {
height -= fb_logo_ex[i].logo->height;
err = -EINVAL;
if (err || !info->fbops->fb_pan_display ||
- var->yoffset + yres > info->var.yres_virtual ||
- var->xoffset + info->var.xres > info->var.xres_virtual)
+ var->yoffset > info->var.yres_virtual - yres ||
+ var->xoffset > info->var.xres_virtual - info->var.xres)
return -EINVAL;
if ((err = info->fbops->fb_pan_display(var, info)))
goto done;
if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ struct fb_var_screeninfo old_var;
struct fb_videomode mode;
if (info->fbops->fb_get_caps) {
goto done;
}
+ old_var = info->var;
info->var = *var;
- if (info->fbops->fb_set_par)
- info->fbops->fb_set_par(info);
+ if (info->fbops->fb_set_par) {
+ ret = info->fbops->fb_set_par(info);
+
+ if (ret) {
+ info->var = old_var;
+ printk(KERN_WARNING "detected "
+ "fb_set_par error, "
+ "error code: %d\n", ret);
+ goto done;
+ }
+ }
fb_pan_display(info, &info->var);
fb_set_cmap(&info->cmap, info);
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
struct fb_con2fbmap con2fb;
+ struct fb_cmap cmap_from;
struct fb_cmap_user cmap;
struct fb_event event;
void __user *argp = (void __user *)arg;
long ret = 0;
- fb = info->fbops;
- if (!fb)
- return -ENODEV;
-
switch (cmd) {
case FBIOGET_VSCREENINFO:
- ret = copy_to_user(argp, &info->var,
- sizeof(var)) ? -EFAULT : 0;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ var = info->var;
+ unlock_fb_info(info);
+
+ ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
break;
case FBIOPUT_VSCREENINFO:
- if (copy_from_user(&var, argp, sizeof(var))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&var, argp, sizeof(var)))
+ return -EFAULT;
+ if (!lock_fb_info(info))
+ return -ENODEV;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
ret = fb_set_var(info, &var);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
- if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
+ unlock_fb_info(info);
+ if (!ret && copy_to_user(argp, &var, sizeof(var)))
ret = -EFAULT;
break;
case FBIOGET_FSCREENINFO:
- ret = copy_to_user(argp, &info->fix,
- sizeof(fix)) ? -EFAULT : 0;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ fix = info->fix;
+ unlock_fb_info(info);
+
+ ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
break;
case FBIOPUTCMAP:
if (copy_from_user(&cmap, argp, sizeof(cmap)))
- ret = -EFAULT;
- else
- ret = fb_set_user_cmap(&cmap, info);
+ return -EFAULT;
+ ret = fb_set_user_cmap(&cmap, info);
break;
case FBIOGETCMAP:
if (copy_from_user(&cmap, argp, sizeof(cmap)))
- ret = -EFAULT;
- else
- ret = fb_cmap_to_user(&info->cmap, &cmap);
+ return -EFAULT;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ cmap_from = info->cmap;
+ unlock_fb_info(info);
+ ret = fb_cmap_to_user(&cmap_from, &cmap);
break;
case FBIOPAN_DISPLAY:
- if (copy_from_user(&var, argp, sizeof(var))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&var, argp, sizeof(var)))
+ return -EFAULT;
+ if (!lock_fb_info(info))
+ return -ENODEV;
acquire_console_sem();
ret = fb_pan_display(info, &var);
release_console_sem();
+ unlock_fb_info(info);
if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
- ret = -EFAULT;
+ return -EFAULT;
break;
case FBIO_CURSOR:
ret = -EINVAL;
break;
case FBIOGET_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
- ret = -EFAULT;
- else if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
- ret = -EINVAL;
- else {
- con2fb.framebuffer = -1;
- event.info = info;
- event.data = &con2fb;
- fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP,
- &event);
- ret = copy_to_user(argp, &con2fb,
- sizeof(con2fb)) ? -EFAULT : 0;
- }
+ return -EFAULT;
+ if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
+ return -EINVAL;
+ con2fb.framebuffer = -1;
+ event.data = &con2fb;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ event.info = info;
+ fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
+ unlock_fb_info(info);
+ ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
break;
case FBIOPUT_CON2FBMAP:
- if (copy_from_user(&con2fb, argp, sizeof(con2fb))) {
- ret = -EFAULT;
- break;
- }
- if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) {
- ret = -EINVAL;
- break;
- }
- if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) {
- ret = -EINVAL;
- break;
- }
+ if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
+ return -EFAULT;
+ if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
+ return -EINVAL;
+ if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
+ return -EINVAL;
if (!registered_fb[con2fb.framebuffer])
request_module("fb%d", con2fb.framebuffer);
if (!registered_fb[con2fb.framebuffer]) {
ret = -EINVAL;
break;
}
- event.info = info;
event.data = &con2fb;
- ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP,
- &event);
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ event.info = info;
+ ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
+ unlock_fb_info(info);
break;
case FBIOBLANK:
+ if (!lock_fb_info(info))
+ return -ENODEV;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
ret = fb_blank(info, arg);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
- break;;
+ unlock_fb_info(info);
+ break;
default:
- if (fb->fb_ioctl == NULL)
- ret = -ENOTTY;
- else
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ fb = info->fbops;
+ if (fb->fb_ioctl)
ret = fb->fb_ioctl(info, cmd, arg);
+ else
+ ret = -ENOTTY;
+ unlock_fb_info(info);
}
return ret;
}
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-__acquires(&info->lock)
-__releases(&info->lock)
{
struct inode *inode = file->f_path.dentry->d_inode;
int fbidx = iminor(inode);
- struct fb_info *info;
- long ret;
+ struct fb_info *info = registered_fb[fbidx];
- info = registered_fb[fbidx];
- mutex_lock(&info->lock);
- ret = do_fb_ioctl(info, cmd, arg);
- mutex_unlock(&info->lock);
- return ret;
+ return do_fb_ioctl(info, cmd, arg);
}
#ifdef CONFIG_COMPAT
static long fb_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
-__acquires(&info->lock)
-__releases(&info->lock)
{
struct inode *inode = file->f_path.dentry->d_inode;
int fbidx = iminor(inode);
struct fb_ops *fb = info->fbops;
long ret = -ENOIOCTLCMD;
- mutex_lock(&info->lock);
switch(cmd) {
case FBIOGET_VSCREENINFO:
case FBIOPUT_VSCREENINFO:
ret = fb->fb_compat_ioctl(info, cmd, arg);
break;
}
- mutex_unlock(&info->lock);
return ret;
}
#endif
static int
fb_mmap(struct file *file, struct vm_area_struct * vma)
-__acquires(&info->lock)
-__releases(&info->lock)
{
int fbidx = iminor(file->f_path.dentry->d_inode);
struct fb_info *info = registered_fb[fbidx];
off = vma->vm_pgoff << PAGE_SHIFT;
if (!fb)
return -ENODEV;
+ mutex_lock(&info->mm_lock);
if (fb->fb_mmap) {
int res;
- mutex_lock(&info->lock);
res = fb->fb_mmap(info, vma);
- mutex_unlock(&info->lock);
+ mutex_unlock(&info->mm_lock);
return res;
}
- mutex_lock(&info->lock);
-
/* frame buffer memory */
start = info->fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
/* memory mapped io */
off -= len;
if (info->var.accel_flags) {
- mutex_unlock(&info->lock);
+ mutex_unlock(&info->mm_lock);
return -EINVAL;
}
start = info->fix.mmio_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
}
- mutex_unlock(&info->lock);
+ mutex_unlock(&info->mm_lock);
start &= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) > len)
return -EINVAL;
return 0;
}
+static bool fb_do_apertures_overlap(struct fb_info *gen, struct fb_info *hw)
+{
+ /* is the generic aperture base the same as the HW one */
+ if (gen->aperture_base == hw->aperture_base)
+ return true;
+ /* is the generic aperture base inside the hw base->hw base+size */
+ if (gen->aperture_base > hw->aperture_base && gen->aperture_base <= hw->aperture_base + hw->aperture_size)
+ return true;
+ return false;
+}
/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
if (fb_check_foreignness(fb_info))
return -ENOSYS;
+ /* check all firmware fbs and kick off if the base addr overlaps */
+ for (i = 0 ; i < FB_MAX; i++) {
+ if (!registered_fb[i])
+ continue;
+
+ if (registered_fb[i]->flags & FBINFO_MISC_FIRMWARE) {
+ if (fb_do_apertures_overlap(registered_fb[i], fb_info)) {
+ printk(KERN_ERR "fb: conflicting fb hw usage "
+ "%s vs %s - removing generic driver\n",
+ fb_info->fix.id,
+ registered_fb[i]->fix.id);
+ unregister_framebuffer(registered_fb[i]);
+ }
+ }
+ }
+
num_registered_fb++;
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
fb_info->node = i;
mutex_init(&fb_info->lock);
+ mutex_init(&fb_info->mm_lock);
fb_info->dev = device_create(fb_class, fb_info->device,
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
registered_fb[i] = fb_info;
event.info = fb_info;
+ if (!lock_fb_info(fb_info))
+ return -ENODEV;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
+ unlock_fb_info(fb_info);
return 0;
}
goto done;
}
+
+ if (!lock_fb_info(fb_info))
+ return -ENODEV;
event.info = fb_info;
ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
+ unlock_fb_info(fb_info);
if (ret) {
ret = -EINVAL;
device_destroy(fb_class, MKDEV(FB_MAJOR, i));
event.info = fb_info;
fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
+
+ /* this may free fb info */
+ if (fb_info->fbops->fb_destroy)
+ fb_info->fbops->fb_destroy(fb_info);
done:
return ret;
}
{
struct fb_event event;
+ if (!lock_fb_info(info))
+ return;
event.info = info;
if (state) {
fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
info->state = FBINFO_STATE_RUNNING;
fb_notifier_call_chain(FB_EVENT_RESUME, &event);
}
+ unlock_fb_info(info);
}
/**
err = 1;
if (!list_empty(&info->modelist)) {
+ if (!lock_fb_info(info))
+ return -ENODEV;
event.info = info;
err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
+ unlock_fb_info(info);
}
return err;
global = 1;
}
- if (!global && !strstr(options, "fb:")) {
+ if (!global && !strchr(options, ':')) {
fb_mode_option = options;
global = 1;
}