X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fvideo%2Ffbmem.c;h=d412a1ddc12fabb50df8562af40b5df25351d2bd;hb=2245fda810f870dce9b030e6aa604320abba53a5;hp=6240aedb41543f6870f1df4f3ddfaec8344a7f7a;hpb=6d9885a8ce45cd9b7d36517ee823a480eaf95c02;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 6240aed..d412a1d 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -11,43 +11,31 @@ * for more details. */ -#include #include #include #include #include -#include #include #include #include #include #include #include -#include +#include #include #include #include +#include #include -#ifdef CONFIG_KMOD #include -#endif -#include #include -#include #include #include +#include -#if defined(__mc68000__) || defined(CONFIG_APUS) -#include -#endif - -#include -#include -#include -#include +#include -#include /* * Frame buffer device initialization and setup routines @@ -55,9 +43,19 @@ #define FBPIXMAPSIZE (1024 * 8) -static struct notifier_block *fb_notifier_list; -struct fb_info *registered_fb[FB_MAX]; -int num_registered_fb; +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 @@ -162,7 +160,6 @@ char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size } #ifdef CONFIG_LOGO -#include static inline unsigned safe_shift(unsigned d, int n) { @@ -207,7 +204,7 @@ static void fb_set_logo_truepalette(struct fb_info *info, const struct linux_logo *logo, u32 *palette) { - unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; + static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; unsigned char redmask, greenmask, bluemask; int redshift, greenshift, blueshift; int i; @@ -244,7 +241,7 @@ static void fb_set_logo_directpalette(struct fb_info *info, 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; } @@ -257,8 +254,17 @@ static void fb_set_logo(struct fb_info *info, u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0; u8 fg = 1, d; - if (fb_get_color_depth(&info->var, &info->fix) == 3) + switch (fb_get_color_depth(&info->var, &info->fix)) { + case 1: + fg = 1; + break; + case 2: + fg = 3; + break; + default: fg = 7; + break; + } if (info->fix.visual == FB_VISUAL_MONO01 || info->fix.visual == FB_VISUAL_MONO10) @@ -322,7 +328,7 @@ static struct logo_data { int needs_truepalette; int needs_cmapreset; const struct linux_logo *logo; -} fb_logo; +} fb_logo __read_mostly; static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height) { @@ -336,11 +342,11 @@ static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height) static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height) { - int i, j, w = width - 1; + int i, j, h = height - 1; for (i = 0; i < height; i++) for (j = 0; j < width; j++) - out[height * j + w - i] = *in++; + out[height * j + h - i] = *in++; } static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height) @@ -358,159 +364,101 @@ static void fb_rotate_logo(struct fb_info *info, u8 *dst, u32 tmp; if (rotate == FB_ROTATE_UD) { - image->dx = info->var.xres - image->width; - image->dy = info->var.yres - image->height; fb_rotate_logo_ud(image->data, dst, image->width, image->height); + image->dx = info->var.xres - image->width - image->dx; + image->dy = info->var.yres - image->height - image->dy; } else if (rotate == FB_ROTATE_CW) { - tmp = image->width; - image->width = image->height; - image->height = tmp; - image->dx = info->var.xres - image->height; fb_rotate_logo_cw(image->data, dst, image->width, image->height); - } else if (rotate == FB_ROTATE_CCW) { tmp = image->width; image->width = image->height; image->height = tmp; - image->dy = info->var.yres - image->width; + tmp = image->dy; + image->dy = image->dx; + image->dx = info->var.xres - image->width - tmp; + } else if (rotate == FB_ROTATE_CCW) { fb_rotate_logo_ccw(image->data, dst, image->width, image->height); + tmp = image->width; + image->width = image->height; + image->height = tmp; + tmp = image->dx; + image->dx = image->dy; + image->dy = info->var.yres - image->height - tmp; } image->data = dst; } static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, - int rotate) + int rotate, unsigned int num) { - int x; + unsigned int x; if (rotate == FB_ROTATE_UR) { - for (x = 0; x < num_online_cpus() && - x * (fb_logo.logo->width + 8) <= - info->var.xres - fb_logo.logo->width; x++) { + for (x = 0; + x < num && image->dx + image->width <= info->var.xres; + x++) { info->fbops->fb_imageblit(info, image); - image->dx += fb_logo.logo->width + 8; + image->dx += image->width + 8; } } else if (rotate == FB_ROTATE_UD) { - for (x = 0; x < num_online_cpus() && - x * (fb_logo.logo->width + 8) <= - info->var.xres - fb_logo.logo->width; x++) { + for (x = 0; x < num && image->dx >= 0; x++) { info->fbops->fb_imageblit(info, image); - image->dx -= fb_logo.logo->width + 8; + image->dx -= image->width + 8; } } else if (rotate == FB_ROTATE_CW) { - for (x = 0; x < num_online_cpus() && - x * (fb_logo.logo->width + 8) <= - info->var.yres - fb_logo.logo->width; x++) { + for (x = 0; + x < num && image->dy + image->height <= info->var.yres; + x++) { info->fbops->fb_imageblit(info, image); - image->dy += fb_logo.logo->width + 8; + image->dy += image->height + 8; } } else if (rotate == FB_ROTATE_CCW) { - for (x = 0; x < num_online_cpus() && - x * (fb_logo.logo->width + 8) <= - info->var.yres - fb_logo.logo->width; x++) { + for (x = 0; x < num && image->dy >= 0; x++) { info->fbops->fb_imageblit(info, image); - image->dy -= fb_logo.logo->width + 8; - } - } -} - -int fb_prepare_logo(struct fb_info *info, int rotate) -{ - int depth = fb_get_color_depth(&info->var, &info->fix); - int yres; - - memset(&fb_logo, 0, sizeof(struct logo_data)); - - if (info->flags & FBINFO_MISC_TILEBLITTING) - 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 (depth >= 8) { - 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; + image->dy -= image->height + 8; } } - - /* 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; - return fb_logo.logo->height; } -int fb_show_logo(struct fb_info *info, int rotate) +static int fb_show_logo_line(struct fb_info *info, int rotate, + const struct linux_logo *logo, int y, + unsigned int n) { u32 *palette = NULL, *saved_pseudo_palette = NULL; unsigned char *logo_new = NULL, *logo_rotate = NULL; struct fb_image image; /* Return if the frame buffer is not mapped or suspended */ - if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING) + if (logo == NULL || info->state != FBINFO_STATE_RUNNING || + info->flags & FBINFO_MODULE) return 0; image.depth = 8; - image.data = fb_logo.logo->data; + image.data = logo->data; if (fb_logo.needs_cmapreset) - fb_set_logocmap(info, fb_logo.logo); + fb_set_logocmap(info, logo); - if (fb_logo.needs_truepalette || + if (fb_logo.needs_truepalette || fb_logo.needs_directpalette) { palette = kmalloc(256 * 4, GFP_KERNEL); if (palette == NULL) return 0; if (fb_logo.needs_truepalette) - fb_set_logo_truepalette(info, fb_logo.logo, palette); + fb_set_logo_truepalette(info, logo, palette); else - fb_set_logo_directpalette(info, fb_logo.logo, palette); + fb_set_logo_directpalette(info, logo, palette); saved_pseudo_palette = info->pseudo_palette; info->pseudo_palette = palette; } if (fb_logo.depth <= 4) { - logo_new = kmalloc(fb_logo.logo->width * fb_logo.logo->height, - GFP_KERNEL); + logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL); if (logo_new == NULL) { kfree(palette); if (saved_pseudo_palette) @@ -518,60 +466,236 @@ int fb_show_logo(struct fb_info *info, int rotate) return 0; } image.data = logo_new; - fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth); + fb_set_logo(info, logo, logo_new, fb_logo.depth); } image.dx = 0; - image.dy = 0; - image.width = fb_logo.logo->width; - image.height = fb_logo.logo->height; + image.dy = y; + image.width = logo->width; + image.height = logo->height; if (rotate) { - logo_rotate = kmalloc(fb_logo.logo->width * - fb_logo.logo->height, GFP_KERNEL); + logo_rotate = kmalloc(logo->width * + logo->height, GFP_KERNEL); if (logo_rotate) fb_rotate_logo(info, logo_rotate, &image, rotate); } - fb_do_show_logo(info, &image, rotate); + fb_do_show_logo(info, &image, rotate, n); kfree(palette); if (saved_pseudo_palette != NULL) info->pseudo_palette = saved_pseudo_palette; kfree(logo_new); kfree(logo_rotate); - return fb_logo.logo->height; + return logo->height; +} + + +#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++) { + 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; + 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++) + y += fb_show_logo_line(info, rotate, + fb_logo_ex[i].logo, y, fb_logo_ex[i].n); + + 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; + + memset(&fb_logo, 0, sizeof(struct logo_data)); + + if (info->flags & FBINFO_MISC_TILEBLITTING || + info->flags & FBINFO_MODULE) + 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; + + + 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; + } + } + + return fb_prepare_extra_logos(info, fb_logo.logo->height, yres); +} + +int fb_show_logo(struct fb_info *info, int rotate) +{ + int y; + + y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, + num_online_cpus()); + y = fb_show_extra_logos(info, y, rotate); + + return y; } #else int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; } int fb_show_logo(struct fb_info *info, int rotate) { return 0; } #endif /* CONFIG_LOGO */ -static int fbmem_read_proc(char *buf, char **start, off_t offset, - int len, int *eof, void *private) +static void *fb_seq_start(struct seq_file *m, loff_t *pos) { - struct fb_info **fi; - int clen; - - clen = 0; - for (fi = registered_fb; fi < ®istered_fb[FB_MAX] && len < 4000; fi++) - if (*fi) - clen += sprintf(buf + clen, "%d %s\n", - (*fi)->node, - (*fi)->fix.id); - *start = buf + offset; - if (clen > offset) - clen -= offset; - else - clen = 0; - return clen < len ? clen : len; + 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) +{ +} + +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; +} + +static const struct seq_operations proc_fb_seq_ops = { + .start = fb_seq_start, + .next = fb_seq_next, + .stop = fb_seq_stop, + .show = fb_seq_show, +}; + +static int proc_fb_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &proc_fb_seq_ops); } +static const struct file_operations fb_proc_fops = { + .owner = THIS_MODULE, + .open = proc_fb_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; u32 *buffer, *dst; @@ -586,20 +710,22 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) return -EPERM; if (info->fbops->fb_read) - return info->fbops->fb_read(file, buf, count, ppos); + return info->fbops->fb_read(info, buf, count, ppos); total_size = info->screen_size; + if (total_size == 0) total_size = info->fix.smem_len; if (p >= total_size) - return 0; + return 0; + if (count >= total_size) - count = total_size; + count = total_size; + if (count + p > total_size) count = total_size - p; - cnt = 0; buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL); if (!buffer) @@ -636,6 +762,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) } kfree(buffer); + return (err) ? err : cnt; } @@ -643,12 +770,12 @@ static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; u32 *buffer, *src; u32 __iomem *dst; - int c, i, cnt = 0, err; + int c, i, cnt = 0, err = 0; unsigned long total_size; if (!info || !info->screen_base) @@ -658,22 +785,28 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) return -EPERM; if (info->fbops->fb_write) - return info->fbops->fb_write(file, buf, count, ppos); + return info->fbops->fb_write(info, buf, count, ppos); total_size = info->screen_size; + if (total_size == 0) total_size = info->fix.smem_len; if (p > total_size) - return -ENOSPC; - if (count >= total_size) - count = total_size; - err = 0; + return -EFBIG; + + if (count > total_size) { + err = -EFBIG; + count = total_size; + } + if (count + p > total_size) { - count = total_size - p; - err = -ENOSPC; + if (!err) + err = -ENOSPC; + + count = total_size - p; } - cnt = 0; + buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL); if (!buffer) @@ -687,12 +820,15 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) while (count) { c = (count > PAGE_SIZE) ? PAGE_SIZE : count; src = buffer; + if (copy_from_user(src, buf, c)) { err = -EFAULT; break; } + for (i = c >> 2; i--; ) fb_writel(*src++, dst++); + if (c & 3) { u8 *src8 = (u8 *) src; u8 __iomem *dst8 = (u8 __iomem *) dst; @@ -702,34 +838,44 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) dst = (u32 __iomem *) dst8; } + *ppos += c; buf += c; cnt += c; count -= c; } - kfree(buffer); - return (err) ? err : cnt; -} + kfree(buffer); -#ifdef CONFIG_KMOD -static void try_to_load(int fb) -{ - request_module("fb%d", fb); + return (cnt) ? cnt : err; } -#endif /* CONFIG_KMOD */ int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) { - int xoffset = var->xoffset; - int yoffset = var->yoffset; - int err; - - if (xoffset < 0 || yoffset < 0 || !info->fbops->fb_pan_display || - xoffset + info->var.xres > info->var.xres_virtual || - yoffset + info->var.yres > info->var.yres_virtual) - return -EINVAL; + struct fb_fix_screeninfo *fix = &info->fix; + unsigned int yres = info->var.yres; + int err = 0; + + 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; + + if (err || !info->fbops->fb_pan_display || + var->yoffset + yres > info->var.yres_virtual || + var->xoffset + info->var.xres > info->var.xres_virtual) + return -EINVAL; + if ((err = info->fbops->fb_pan_display(var, info))) return err; info->var.xoffset = var->xoffset; @@ -741,14 +887,37 @@ fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) return 0; } +static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var, + u32 activate) +{ + struct fb_event event; + 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; + event.info = info; + event.data = ∩︀ + fb_notifier_call_chain(FB_EVENT_GET_REQ, &event); + 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; +} + int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) { - int err, flags = info->flags; + int flags = info->flags; + int ret = 0; if (var->activate & FB_ACTIVATE_INV_MODE) { struct fb_videomode mode1, mode2; - int ret = 0; fb_var_to_videomode(&mode1, var); fb_var_to_videomode(&mode2, &info->var); @@ -760,58 +929,70 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) event.info = info; event.data = &mode1; - ret = notifier_call_chain(&fb_notifier_list, - FB_EVENT_MODE_DELETE, &event); + ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event); } if (!ret) fb_delete_videomode(&mode1, &info->modelist); - return ret; + + ret = (ret) ? -EINVAL : 0; + goto done; } if ((var->activate & FB_ACTIVATE_FORCE) || memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) { + u32 activate = var->activate; + if (!info->fbops->fb_check_var) { *var = info->var; - return 0; + goto done; } - if ((err = info->fbops->fb_check_var(var, info))) - return err; + ret = info->fbops->fb_check_var(var, info); + + if (ret) + goto done; if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { struct fb_videomode mode; - int err = 0; + + if (info->fbops->fb_get_caps) { + ret = fb_check_caps(info, var, activate); + + if (ret) + goto done; + } info->var = *var; + if (info->fbops->fb_set_par) info->fbops->fb_set_par(info); fb_pan_display(info, &info->var); - fb_set_cmap(&info->cmap, info); - fb_var_to_videomode(&mode, &info->var); if (info->modelist.prev && info->modelist.next && !list_empty(&info->modelist)) - err = fb_add_videomode(&mode, &info->modelist); + ret = fb_add_videomode(&mode, &info->modelist); - if (!err && (flags & FBINFO_MISC_USEREVENT)) { + if (!ret && (flags & FBINFO_MISC_USEREVENT)) { struct fb_event event; - int evnt = (var->activate & FB_ACTIVATE_ALL) ? + int evnt = (activate & FB_ACTIVATE_ALL) ? FB_EVENT_MODE_CHANGE_ALL : FB_EVENT_MODE_CHANGE; info->flags &= ~FBINFO_MISC_USEREVENT; event.info = info; - notifier_call_chain(&fb_notifier_list, evnt, - &event); + event.data = &mode; + fb_notifier_call_chain(evnt, &event); } } } - return 0; + + done: + return ret; } int @@ -830,111 +1011,149 @@ fb_blank(struct fb_info *info, int blank) event.info = info; event.data = ␣ - notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event); + fb_notifier_call_chain(FB_EVENT_BLANK, &event); } return ret; } -static int -fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) { - int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; + struct fb_ops *fb; 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; - int i; - - if (!fb) - return -ENODEV; + long ret = 0; + switch (cmd) { case FBIOGET_VSCREENINFO: - return 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))) return -EFAULT; + if (!lock_fb_info(info)) + return -ENODEV; acquire_console_sem(); info->flags |= FBINFO_MISC_USEREVENT; - i = fb_set_var(info, &var); + ret = fb_set_var(info, &var); info->flags &= ~FBINFO_MISC_USEREVENT; release_console_sem(); - if (i) return i; - if (copy_to_user(argp, &var, sizeof(var))) - return -EFAULT; - return 0; + unlock_fb_info(info); + if (!ret && copy_to_user(argp, &var, sizeof(var))) + ret = -EFAULT; + break; case FBIOGET_FSCREENINFO: - return 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))) return -EFAULT; - return (fb_set_user_cmap(&cmap, info)); + ret = fb_set_user_cmap(&cmap, info); + break; case FBIOGETCMAP: if (copy_from_user(&cmap, argp, sizeof(cmap))) return -EFAULT; - return fb_cmap_to_user(&info->cmap, &cmap); + 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))) return -EFAULT; + if (!lock_fb_info(info)) + return -ENODEV; acquire_console_sem(); - i = fb_pan_display(info, &var); + ret = fb_pan_display(info, &var); release_console_sem(); - if (i) - return i; - if (copy_to_user(argp, &var, sizeof(var))) + unlock_fb_info(info); + if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) return -EFAULT; - return 0; + break; case FBIO_CURSOR: - return -EINVAL; + ret = -EINVAL; + break; case FBIOGET_CON2FBMAP: if (copy_from_user(&con2fb, argp, sizeof(con2fb))) return -EFAULT; if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) - return -EINVAL; + return -EINVAL; con2fb.framebuffer = -1; - event.info = info; event.data = &con2fb; - notifier_call_chain(&fb_notifier_list, - FB_EVENT_GET_CONSOLE_MAP, &event); - return copy_to_user(argp, &con2fb, - sizeof(con2fb)) ? -EFAULT : 0; + 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))) - return - EFAULT; - if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES) - return -EINVAL; + return -EFAULT; + if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) + return -EINVAL; if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) - return -EINVAL; -#ifdef CONFIG_KMOD - if (!registered_fb[con2fb.framebuffer]) - try_to_load(con2fb.framebuffer); -#endif /* CONFIG_KMOD */ + return -EINVAL; if (!registered_fb[con2fb.framebuffer]) - return -EINVAL; - event.info = info; + request_module("fb%d", con2fb.framebuffer); + if (!registered_fb[con2fb.framebuffer]) { + ret = -EINVAL; + break; + } event.data = &con2fb; - return notifier_call_chain(&fb_notifier_list, - 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; - i = fb_blank(info, arg); + ret = fb_blank(info, arg); info->flags &= ~FBINFO_MISC_USEREVENT; release_console_sem(); - return i; + unlock_fb_info(info); + break; default: - if (fb->fb_ioctl == NULL) - return -EINVAL; - return fb->fb_ioctl(inode, file, cmd, arg, info); + 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) +{ + struct inode *inode = file->f_path.dentry->d_inode; + int fbidx = iminor(inode); + struct fb_info *info = registered_fb[fbidx]; + + return do_fb_ioctl(info, cmd, arg); } #ifdef CONFIG_COMPAT @@ -964,8 +1183,8 @@ struct fb_cmap32 { compat_caddr_t transp; }; -static int fb_getput_cmap(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, + unsigned long arg) { struct fb_cmap_user __user *cmap; struct fb_cmap32 __user *cmap32; @@ -988,7 +1207,7 @@ static int fb_getput_cmap(struct inode *inode, struct file *file, put_user(compat_ptr(data), &cmap->transp)) return -EFAULT; - err = fb_ioctl(inode, file, cmd, (unsigned long) cmap); + err = do_fb_ioctl(info, cmd, (unsigned long) cmap); if (!err) { if (copy_in_user(&cmap32->start, @@ -1030,8 +1249,8 @@ static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, return err; } -static int fb_get_fscreeninfo(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, + unsigned long arg) { mm_segment_t old_fs; struct fb_fix_screeninfo fix; @@ -1042,7 +1261,7 @@ static int fb_get_fscreeninfo(struct inode *inode, struct file *file, old_fs = get_fs(); set_fs(KERNEL_DS); - err = fb_ioctl(inode, file, cmd, (unsigned long) &fix); + err = do_fb_ioctl(info, cmd, (unsigned long) &fix); set_fs(old_fs); if (!err) @@ -1051,16 +1270,15 @@ static int fb_get_fscreeninfo(struct inode *inode, struct file *file, return err; } -static long -fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long fb_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; struct fb_ops *fb = info->fbops; long ret = -ENOIOCTLCMD; - lock_kernel(); switch(cmd) { case FBIOGET_VSCREENINFO: case FBIOPUT_VSCREENINFO: @@ -1069,39 +1287,38 @@ fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case FBIOPUT_CON2FBMAP: arg = (unsigned long) compat_ptr(arg); case FBIOBLANK: - ret = fb_ioctl(inode, file, cmd, arg); + ret = do_fb_ioctl(info, cmd, arg); break; case FBIOGET_FSCREENINFO: - ret = fb_get_fscreeninfo(inode, file, cmd, arg); + ret = fb_get_fscreeninfo(info, cmd, arg); break; case FBIOGETCMAP: case FBIOPUTCMAP: - ret = fb_getput_cmap(inode, file, cmd, arg); + ret = fb_getput_cmap(info, cmd, arg); break; default: if (fb->fb_compat_ioctl) - ret = fb->fb_compat_ioctl(file, cmd, arg, info); + ret = fb->fb_compat_ioctl(info, cmd, arg); break; } - unlock_kernel(); return ret; } #endif -static int +static int fb_mmap(struct file *file, struct vm_area_struct * vma) +__acquires(&info->lock) +__releases(&info->lock) { - int fbidx = iminor(file->f_dentry->d_inode); + int fbidx = iminor(file->f_path.dentry->d_inode); struct fb_info *info = registered_fb[fbidx]; struct fb_ops *fb = info->fbops; unsigned long off; -#if !defined(__sparc__) || defined(__sparc_v9__) unsigned long start; u32 len; -#endif if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; @@ -1110,19 +1327,13 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) return -ENODEV; if (fb->fb_mmap) { int res; - lock_kernel(); - res = fb->fb_mmap(info, file, vma); - unlock_kernel(); + mutex_lock(&info->lock); + res = fb->fb_mmap(info, vma); + mutex_unlock(&info->lock); return res; } -#if defined(__sparc__) && !defined(__sparc_v9__) - /* Should never get here, all fb drivers should have their own - mmap routines */ - return -EINVAL; -#else - /* !sparc32... */ - lock_kernel(); + mutex_lock(&info->lock); /* frame buffer memory */ start = info->fix.smem_start; @@ -1131,13 +1342,13 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) /* memory mapped io */ off -= len; if (info->var.accel_flags) { - unlock_kernel(); + mutex_unlock(&info->lock); return -EINVAL; } start = info->fix.mmio_start; len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); } - unlock_kernel(); + mutex_unlock(&info->lock); start &= PAGE_MASK; if ((vma->vm_end - vma->vm_start + off) > len) return -EINVAL; @@ -1145,56 +1356,17 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) vma->vm_pgoff = off >> PAGE_SHIFT; /* This is an IO map - tell maydump to skip this VMA */ vma->vm_flags |= VM_IO | VM_RESERVED; -#if defined(__sparc_v9__) - if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; -#else -#if defined(__mc68000__) -#if defined(CONFIG_SUN3) - pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE; -#elif defined(CONFIG_MMU) - if (CPU_IS_020_OR_030) - pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030; - if (CPU_IS_040_OR_060) { - pgprot_val(vma->vm_page_prot) &= _CACHEMASK040; - /* Use no-cache mode, serialized */ - pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S; - } -#endif -#elif defined(__powerpc__) - vma->vm_page_prot = phys_mem_access_prot(file, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot); -#elif defined(__alpha__) - /* Caching is off in the I/O space quadrant by design. */ -#elif defined(__i386__) || defined(__x86_64__) - if (boot_cpu_data.x86 > 3) - pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; -#elif defined(__mips__) - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -#elif defined(__hppa__) - pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; -#elif defined(__arm__) || defined(__sh__) || defined(__m32r__) - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); -#elif defined(__ia64__) - if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start)) - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - else - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -#else -#warning What do we have to do here?? -#endif + fb_pgprotect(file, vma, off); if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; -#endif /* !__sparc_v9__ */ return 0; -#endif /* !sparc32 */ } static int fb_open(struct inode *inode, struct file *file) +__acquires(&info->lock) +__releases(&info->lock) { int fbidx = iminor(inode); struct fb_info *info; @@ -1202,42 +1374,52 @@ fb_open(struct inode *inode, struct file *file) if (fbidx >= FB_MAX) return -ENODEV; -#ifdef CONFIG_KMOD - if (!(info = registered_fb[fbidx])) - try_to_load(fbidx); -#endif /* CONFIG_KMOD */ - if (!(info = registered_fb[fbidx])) - return -ENODEV; - if (!try_module_get(info->fbops->owner)) + info = registered_fb[fbidx]; + if (!info) + request_module("fb%d", fbidx); + info = registered_fb[fbidx]; + if (!info) return -ENODEV; + mutex_lock(&info->lock); + if (!try_module_get(info->fbops->owner)) { + res = -ENODEV; + goto out; + } + file->private_data = info; if (info->fbops->fb_open) { res = info->fbops->fb_open(info,1); if (res) module_put(info->fbops->owner); } +#ifdef CONFIG_FB_DEFERRED_IO + if (info->fbdefio) + fb_deferred_io_open(info, inode, file); +#endif +out: + mutex_unlock(&info->lock); return res; } static int fb_release(struct inode *inode, struct file *file) +__acquires(&info->lock) +__releases(&info->lock) { - int fbidx = iminor(inode); - struct fb_info *info; + struct fb_info * const info = file->private_data; - lock_kernel(); - info = registered_fb[fbidx]; + mutex_lock(&info->lock); if (info->fbops->fb_release) info->fbops->fb_release(info,1); module_put(info->fbops->owner); - unlock_kernel(); + mutex_unlock(&info->lock); return 0; } -static struct file_operations fb_fops = { +static const struct file_operations fb_fops = { .owner = THIS_MODULE, .read = fb_read, .write = fb_write, - .ioctl = fb_ioctl, + .unlocked_ioctl = fb_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = fb_compat_ioctl, #endif @@ -1247,9 +1429,38 @@ static struct file_operations fb_fops = { #ifdef HAVE_ARCH_FB_UNMAPPED_AREA .get_unmapped_area = get_fb_unmapped_area, #endif +#ifdef CONFIG_FB_DEFERRED_IO + .fsync = fb_deferred_io_fsync, +#endif }; -static struct class *fb_class; +struct class *fb_class; +EXPORT_SYMBOL(fb_class); + +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; +} /** * register_framebuffer - registers a frame buffer device @@ -1270,20 +1481,25 @@ register_framebuffer(struct fb_info *fb_info) if (num_registered_fb == FB_MAX) return -ENXIO; + + if (fb_check_foreignness(fb_info)) + return -ENOSYS; + num_registered_fb++; for (i = 0 ; i < FB_MAX; i++) if (!registered_fb[i]) break; fb_info->node = i; + mutex_init(&fb_info->lock); - fb_info->class_device = class_device_create(fb_class, NULL, MKDEV(FB_MAJOR, i), - fb_info->device, "fb%d", i); - if (IS_ERR(fb_info->class_device)) { + fb_info->dev = device_create(fb_class, fb_info->device, + MKDEV(FB_MAJOR, i), NULL, "fb%d", i); + if (IS_ERR(fb_info->dev)) { /* Not fatal */ - printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->class_device)); - fb_info->class_device = NULL; + printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev)); + fb_info->dev = NULL; } else - fb_init_class_device(fb_info); + fb_init_device(fb_info); if (fb_info->pixmap.addr == NULL) { fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); @@ -1297,6 +1513,12 @@ register_framebuffer(struct fb_info *fb_info) } fb_info->pixmap.offset = 0; + 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; + if (!fb_info->modelist.prev || !fb_info->modelist.next) INIT_LIST_HEAD(&fb_info->modelist); @@ -1304,11 +1526,11 @@ register_framebuffer(struct fb_info *fb_info) fb_add_videomode(&mode, &fb_info->modelist); registered_fb[i] = fb_info; - devfs_mk_cdev(MKDEV(FB_MAJOR, i), - S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); event.info = fb_info; - notifier_call_chain(&fb_notifier_list, - FB_EVENT_FB_REGISTERED, &event); + if (!lock_fb_info(fb_info)) + return -ENODEV; + fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); + unlock_fb_info(fb_info); return 0; } @@ -1321,44 +1543,51 @@ register_framebuffer(struct fb_info *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. */ int unregister_framebuffer(struct fb_info *fb_info) { - int i; + struct fb_event event; + int i, ret = 0; i = fb_info->node; - if (!registered_fb[i]) - return -EINVAL; - devfs_remove("fb/%d", i); + if (!registered_fb[i]) { + ret = -EINVAL; + 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 (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) + if (ret) { + ret = -EINVAL; + goto done; + } + + if (fb_info->pixmap.addr && + (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr); fb_destroy_modelist(&fb_info->modelist); registered_fb[i]=NULL; num_registered_fb--; - fb_cleanup_class_device(fb_info); - class_device_destroy(fb_class, MKDEV(FB_MAJOR, i)); - return 0; -} - -/** - * fb_register_client - register a client notifier - * @nb: notifier block to callback on events - */ -int fb_register_client(struct notifier_block *nb) -{ - return notifier_chain_register(&fb_notifier_list, nb); -} - -/** - * fb_unregister_client - unregister a client notifier - * @nb: notifier block to callback on events - */ -int fb_unregister_client(struct notifier_block *nb) -{ - return notifier_chain_unregister(&fb_notifier_list, nb); + fb_cleanup_device(fb_info); + device_destroy(fb_class, MKDEV(FB_MAJOR, i)); + event.info = fb_info; + fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); +done: + return ret; } /** @@ -1374,14 +1603,17 @@ void fb_set_suspend(struct fb_info *info, int state) { struct fb_event event; + if (!lock_fb_info(info)) + return; event.info = info; if (state) { - notifier_call_chain(&fb_notifier_list, FB_EVENT_SUSPEND, &event); + fb_notifier_call_chain(FB_EVENT_SUSPEND, &event); info->state = FBINFO_STATE_SUSPENDED; } else { info->state = FBINFO_STATE_RUNNING; - notifier_call_chain(&fb_notifier_list, FB_EVENT_RESUME, &event); + fb_notifier_call_chain(FB_EVENT_RESUME, &event); } + unlock_fb_info(info); } /** @@ -1396,9 +1628,8 @@ void fb_set_suspend(struct fb_info *info, int state) static int __init fbmem_init(void) { - create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL); + proc_create("fb", 0, NULL, &fb_proc_fops); - devfs_mk_dir("fb"); if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) printk("unable to get major %d for fb devs\n", FB_MAJOR); @@ -1415,6 +1646,7 @@ module_init(fbmem_init); static void __exit fbmem_exit(void) { + remove_proc_entry("fb", NULL); class_destroy(fb_class); unregister_chrdev(FB_MAJOR, "fb"); } @@ -1451,41 +1683,18 @@ int fb_new_modelist(struct fb_info *info) err = 1; if (!list_empty(&info->modelist)) { + if (!lock_fb_info(info)) + return -ENODEV; event.info = info; - err = notifier_call_chain(&fb_notifier_list, - FB_EVENT_NEW_MODELIST, - &event); + err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); + unlock_fb_info(info); } return err; } -/** - * fb_con_duit - user<->fbcon passthrough - * @info: struct fb_info - * @event: notification event to be passed to fbcon - * @data: private data - * - * DESCRIPTION - * This function is an fbcon-user event passing channel - * which bypasses fbdev. This is hopefully temporary - * until a user interface for fbcon is created - */ -int fb_con_duit(struct fb_info *info, int event, void *data) -{ - struct fb_event evnt; - - evnt.info = info; - evnt.data = data; - - return notifier_call_chain(&fb_notifier_list, event, &evnt); -} -EXPORT_SYMBOL(fb_con_duit); - -static char *video_options[FB_MAX]; -static int ofonly; - -extern const char *global_mode_option; +static char *video_options[FB_MAX] __read_mostly; +static int ofonly __read_mostly; /** * fb_get_options - get kernel boot parameters @@ -1527,6 +1736,7 @@ int fb_get_options(char *name, char **option) return retval; } +#ifndef MODULE /** * video_setup - process command line options * @options: string of options @@ -1553,7 +1763,7 @@ static int __init video_setup(char *options) } if (!global && !strstr(options, "fb:")) { - global_mode_option = options; + fb_mode_option = options; global = 1; } @@ -1567,9 +1777,10 @@ static int __init video_setup(char *options) } } - return 0; + return 1; } __setup("video=", video_setup); +#endif /* * Visible symbols for modules @@ -1579,16 +1790,12 @@ EXPORT_SYMBOL(register_framebuffer); EXPORT_SYMBOL(unregister_framebuffer); EXPORT_SYMBOL(num_registered_fb); EXPORT_SYMBOL(registered_fb); -EXPORT_SYMBOL(fb_prepare_logo); EXPORT_SYMBOL(fb_show_logo); EXPORT_SYMBOL(fb_set_var); EXPORT_SYMBOL(fb_blank); EXPORT_SYMBOL(fb_pan_display); EXPORT_SYMBOL(fb_get_buffer_offset); EXPORT_SYMBOL(fb_set_suspend); -EXPORT_SYMBOL(fb_register_client); -EXPORT_SYMBOL(fb_unregister_client); EXPORT_SYMBOL(fb_get_options); -EXPORT_SYMBOL(fb_new_modelist); MODULE_LICENSE("GPL");