diff options
Diffstat (limited to 'drivers/video/fbmem.c')
-rw-r--r-- | drivers/video/fbmem.c | 300 |
1 files changed, 178 insertions, 122 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index b06647517c0e..5aac00eb1830 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c | |||
@@ -42,9 +42,34 @@ | |||
42 | 42 | ||
43 | #define FBPIXMAPSIZE (1024 * 8) | 43 | #define FBPIXMAPSIZE (1024 * 8) |
44 | 44 | ||
45 | static DEFINE_MUTEX(registration_lock); | ||
45 | struct fb_info *registered_fb[FB_MAX] __read_mostly; | 46 | struct fb_info *registered_fb[FB_MAX] __read_mostly; |
46 | int num_registered_fb __read_mostly; | 47 | int num_registered_fb __read_mostly; |
47 | 48 | ||
49 | static struct fb_info *get_fb_info(unsigned int idx) | ||
50 | { | ||
51 | struct fb_info *fb_info; | ||
52 | |||
53 | if (idx >= FB_MAX) | ||
54 | return ERR_PTR(-ENODEV); | ||
55 | |||
56 | mutex_lock(®istration_lock); | ||
57 | fb_info = registered_fb[idx]; | ||
58 | if (fb_info) | ||
59 | atomic_inc(&fb_info->count); | ||
60 | mutex_unlock(®istration_lock); | ||
61 | |||
62 | return fb_info; | ||
63 | } | ||
64 | |||
65 | static void put_fb_info(struct fb_info *fb_info) | ||
66 | { | ||
67 | if (!atomic_dec_and_test(&fb_info->count)) | ||
68 | return; | ||
69 | if (fb_info->fbops->fb_destroy) | ||
70 | fb_info->fbops->fb_destroy(fb_info); | ||
71 | } | ||
72 | |||
48 | int lock_fb_info(struct fb_info *info) | 73 | int lock_fb_info(struct fb_info *info) |
49 | { | 74 | { |
50 | mutex_lock(&info->lock); | 75 | mutex_lock(&info->lock); |
@@ -647,6 +672,7 @@ int fb_show_logo(struct fb_info *info, int rotate) { return 0; } | |||
647 | 672 | ||
648 | static void *fb_seq_start(struct seq_file *m, loff_t *pos) | 673 | static void *fb_seq_start(struct seq_file *m, loff_t *pos) |
649 | { | 674 | { |
675 | mutex_lock(®istration_lock); | ||
650 | return (*pos < FB_MAX) ? pos : NULL; | 676 | return (*pos < FB_MAX) ? pos : NULL; |
651 | } | 677 | } |
652 | 678 | ||
@@ -658,6 +684,7 @@ static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos) | |||
658 | 684 | ||
659 | static void fb_seq_stop(struct seq_file *m, void *v) | 685 | static void fb_seq_stop(struct seq_file *m, void *v) |
660 | { | 686 | { |
687 | mutex_unlock(®istration_lock); | ||
661 | } | 688 | } |
662 | 689 | ||
663 | static int fb_seq_show(struct seq_file *m, void *v) | 690 | static int fb_seq_show(struct seq_file *m, void *v) |
@@ -690,16 +717,33 @@ static const struct file_operations fb_proc_fops = { | |||
690 | .release = seq_release, | 717 | .release = seq_release, |
691 | }; | 718 | }; |
692 | 719 | ||
693 | static ssize_t | 720 | /* |
694 | fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 721 | * We hold a reference to the fb_info in file->private_data, |
722 | * but if the current registered fb has changed, we don't | ||
723 | * actually want to use it. | ||
724 | * | ||
725 | * So look up the fb_info using the inode minor number, | ||
726 | * and just verify it against the reference we have. | ||
727 | */ | ||
728 | static struct fb_info *file_fb_info(struct file *file) | ||
695 | { | 729 | { |
696 | unsigned long p = *ppos; | ||
697 | struct inode *inode = file->f_path.dentry->d_inode; | 730 | struct inode *inode = file->f_path.dentry->d_inode; |
698 | int fbidx = iminor(inode); | 731 | int fbidx = iminor(inode); |
699 | struct fb_info *info = registered_fb[fbidx]; | 732 | struct fb_info *info = registered_fb[fbidx]; |
700 | u32 *buffer, *dst; | 733 | |
701 | u32 __iomem *src; | 734 | if (info != file->private_data) |
702 | int c, i, cnt = 0, err = 0; | 735 | info = NULL; |
736 | return info; | ||
737 | } | ||
738 | |||
739 | static ssize_t | ||
740 | fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
741 | { | ||
742 | unsigned long p = *ppos; | ||
743 | struct fb_info *info = file_fb_info(file); | ||
744 | u8 *buffer, *dst; | ||
745 | u8 __iomem *src; | ||
746 | int c, cnt = 0, err = 0; | ||
703 | unsigned long total_size; | 747 | unsigned long total_size; |
704 | 748 | ||
705 | if (!info || ! info->screen_base) | 749 | if (!info || ! info->screen_base) |
@@ -730,7 +774,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
730 | if (!buffer) | 774 | if (!buffer) |
731 | return -ENOMEM; | 775 | return -ENOMEM; |
732 | 776 | ||
733 | src = (u32 __iomem *) (info->screen_base + p); | 777 | src = (u8 __iomem *) (info->screen_base + p); |
734 | 778 | ||
735 | if (info->fbops->fb_sync) | 779 | if (info->fbops->fb_sync) |
736 | info->fbops->fb_sync(info); | 780 | info->fbops->fb_sync(info); |
@@ -738,17 +782,9 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
738 | while (count) { | 782 | while (count) { |
739 | c = (count > PAGE_SIZE) ? PAGE_SIZE : count; | 783 | c = (count > PAGE_SIZE) ? PAGE_SIZE : count; |
740 | dst = buffer; | 784 | dst = buffer; |
741 | for (i = c >> 2; i--; ) | 785 | fb_memcpy_fromfb(dst, src, c); |
742 | *dst++ = fb_readl(src++); | 786 | dst += c; |
743 | if (c & 3) { | 787 | src += c; |
744 | u8 *dst8 = (u8 *) dst; | ||
745 | u8 __iomem *src8 = (u8 __iomem *) src; | ||
746 | |||
747 | for (i = c & 3; i--;) | ||
748 | *dst8++ = fb_readb(src8++); | ||
749 | |||
750 | src = (u32 __iomem *) src8; | ||
751 | } | ||
752 | 788 | ||
753 | if (copy_to_user(buf, buffer, c)) { | 789 | if (copy_to_user(buf, buffer, c)) { |
754 | err = -EFAULT; | 790 | err = -EFAULT; |
@@ -769,12 +805,10 @@ static ssize_t | |||
769 | fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | 805 | fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) |
770 | { | 806 | { |
771 | unsigned long p = *ppos; | 807 | unsigned long p = *ppos; |
772 | struct inode *inode = file->f_path.dentry->d_inode; | 808 | struct fb_info *info = file_fb_info(file); |
773 | int fbidx = iminor(inode); | 809 | u8 *buffer, *src; |
774 | struct fb_info *info = registered_fb[fbidx]; | 810 | u8 __iomem *dst; |
775 | u32 *buffer, *src; | 811 | int c, cnt = 0, err = 0; |
776 | u32 __iomem *dst; | ||
777 | int c, i, cnt = 0, err = 0; | ||
778 | unsigned long total_size; | 812 | unsigned long total_size; |
779 | 813 | ||
780 | if (!info || !info->screen_base) | 814 | if (!info || !info->screen_base) |
@@ -811,7 +845,7 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | |||
811 | if (!buffer) | 845 | if (!buffer) |
812 | return -ENOMEM; | 846 | return -ENOMEM; |
813 | 847 | ||
814 | dst = (u32 __iomem *) (info->screen_base + p); | 848 | dst = (u8 __iomem *) (info->screen_base + p); |
815 | 849 | ||
816 | if (info->fbops->fb_sync) | 850 | if (info->fbops->fb_sync) |
817 | info->fbops->fb_sync(info); | 851 | info->fbops->fb_sync(info); |
@@ -825,19 +859,9 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | |||
825 | break; | 859 | break; |
826 | } | 860 | } |
827 | 861 | ||
828 | for (i = c >> 2; i--; ) | 862 | fb_memcpy_tofb(dst, src, c); |
829 | fb_writel(*src++, dst++); | 863 | dst += c; |
830 | 864 | src += c; | |
831 | if (c & 3) { | ||
832 | u8 *src8 = (u8 *) src; | ||
833 | u8 __iomem *dst8 = (u8 __iomem *) dst; | ||
834 | |||
835 | for (i = c & 3; i--; ) | ||
836 | fb_writeb(*src8++, dst8++); | ||
837 | |||
838 | dst = (u32 __iomem *) dst8; | ||
839 | } | ||
840 | |||
841 | *ppos += c; | 865 | *ppos += c; |
842 | buf += c; | 866 | buf += c; |
843 | cnt += c; | 867 | cnt += c; |
@@ -877,13 +901,13 @@ fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) | |||
877 | 901 | ||
878 | if ((err = info->fbops->fb_pan_display(var, info))) | 902 | if ((err = info->fbops->fb_pan_display(var, info))) |
879 | return err; | 903 | return err; |
880 | info->var.xoffset = var->xoffset; | 904 | info->var.xoffset = var->xoffset; |
881 | info->var.yoffset = var->yoffset; | 905 | info->var.yoffset = var->yoffset; |
882 | if (var->vmode & FB_VMODE_YWRAP) | 906 | if (var->vmode & FB_VMODE_YWRAP) |
883 | info->var.vmode |= FB_VMODE_YWRAP; | 907 | info->var.vmode |= FB_VMODE_YWRAP; |
884 | else | 908 | else |
885 | info->var.vmode &= ~FB_VMODE_YWRAP; | 909 | info->var.vmode &= ~FB_VMODE_YWRAP; |
886 | return 0; | 910 | return 0; |
887 | } | 911 | } |
888 | 912 | ||
889 | static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var, | 913 | static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var, |
@@ -1054,11 +1078,11 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1054 | return -EFAULT; | 1078 | return -EFAULT; |
1055 | if (!lock_fb_info(info)) | 1079 | if (!lock_fb_info(info)) |
1056 | return -ENODEV; | 1080 | return -ENODEV; |
1057 | acquire_console_sem(); | 1081 | console_lock(); |
1058 | info->flags |= FBINFO_MISC_USEREVENT; | 1082 | info->flags |= FBINFO_MISC_USEREVENT; |
1059 | ret = fb_set_var(info, &var); | 1083 | ret = fb_set_var(info, &var); |
1060 | info->flags &= ~FBINFO_MISC_USEREVENT; | 1084 | info->flags &= ~FBINFO_MISC_USEREVENT; |
1061 | release_console_sem(); | 1085 | console_unlock(); |
1062 | unlock_fb_info(info); | 1086 | unlock_fb_info(info); |
1063 | if (!ret && copy_to_user(argp, &var, sizeof(var))) | 1087 | if (!ret && copy_to_user(argp, &var, sizeof(var))) |
1064 | ret = -EFAULT; | 1088 | ret = -EFAULT; |
@@ -1090,9 +1114,9 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1090 | return -EFAULT; | 1114 | return -EFAULT; |
1091 | if (!lock_fb_info(info)) | 1115 | if (!lock_fb_info(info)) |
1092 | return -ENODEV; | 1116 | return -ENODEV; |
1093 | acquire_console_sem(); | 1117 | console_lock(); |
1094 | ret = fb_pan_display(info, &var); | 1118 | ret = fb_pan_display(info, &var); |
1095 | release_console_sem(); | 1119 | console_unlock(); |
1096 | unlock_fb_info(info); | 1120 | unlock_fb_info(info); |
1097 | if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) | 1121 | if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) |
1098 | return -EFAULT; | 1122 | return -EFAULT; |
@@ -1137,11 +1161,11 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1137 | case FBIOBLANK: | 1161 | case FBIOBLANK: |
1138 | if (!lock_fb_info(info)) | 1162 | if (!lock_fb_info(info)) |
1139 | return -ENODEV; | 1163 | return -ENODEV; |
1140 | acquire_console_sem(); | 1164 | console_lock(); |
1141 | info->flags |= FBINFO_MISC_USEREVENT; | 1165 | info->flags |= FBINFO_MISC_USEREVENT; |
1142 | ret = fb_blank(info, arg); | 1166 | ret = fb_blank(info, arg); |
1143 | info->flags &= ~FBINFO_MISC_USEREVENT; | 1167 | info->flags &= ~FBINFO_MISC_USEREVENT; |
1144 | release_console_sem(); | 1168 | console_unlock(); |
1145 | unlock_fb_info(info); | 1169 | unlock_fb_info(info); |
1146 | break; | 1170 | break; |
1147 | default: | 1171 | default: |
@@ -1159,10 +1183,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1159 | 1183 | ||
1160 | static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 1184 | static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
1161 | { | 1185 | { |
1162 | struct inode *inode = file->f_path.dentry->d_inode; | 1186 | struct fb_info *info = file_fb_info(file); |
1163 | int fbidx = iminor(inode); | ||
1164 | struct fb_info *info = registered_fb[fbidx]; | ||
1165 | 1187 | ||
1188 | if (!info) | ||
1189 | return -ENODEV; | ||
1166 | return do_fb_ioctl(info, cmd, arg); | 1190 | return do_fb_ioctl(info, cmd, arg); |
1167 | } | 1191 | } |
1168 | 1192 | ||
@@ -1283,12 +1307,13 @@ static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, | |||
1283 | static long fb_compat_ioctl(struct file *file, unsigned int cmd, | 1307 | static long fb_compat_ioctl(struct file *file, unsigned int cmd, |
1284 | unsigned long arg) | 1308 | unsigned long arg) |
1285 | { | 1309 | { |
1286 | struct inode *inode = file->f_path.dentry->d_inode; | 1310 | struct fb_info *info = file_fb_info(file); |
1287 | int fbidx = iminor(inode); | 1311 | struct fb_ops *fb; |
1288 | struct fb_info *info = registered_fb[fbidx]; | ||
1289 | struct fb_ops *fb = info->fbops; | ||
1290 | long ret = -ENOIOCTLCMD; | 1312 | long ret = -ENOIOCTLCMD; |
1291 | 1313 | ||
1314 | if (!info) | ||
1315 | return -ENODEV; | ||
1316 | fb = info->fbops; | ||
1292 | switch(cmd) { | 1317 | switch(cmd) { |
1293 | case FBIOGET_VSCREENINFO: | 1318 | case FBIOGET_VSCREENINFO: |
1294 | case FBIOPUT_VSCREENINFO: | 1319 | case FBIOPUT_VSCREENINFO: |
@@ -1321,16 +1346,18 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, | |||
1321 | static int | 1346 | static int |
1322 | fb_mmap(struct file *file, struct vm_area_struct * vma) | 1347 | fb_mmap(struct file *file, struct vm_area_struct * vma) |
1323 | { | 1348 | { |
1324 | int fbidx = iminor(file->f_path.dentry->d_inode); | 1349 | struct fb_info *info = file_fb_info(file); |
1325 | struct fb_info *info = registered_fb[fbidx]; | 1350 | struct fb_ops *fb; |
1326 | struct fb_ops *fb = info->fbops; | ||
1327 | unsigned long off; | 1351 | unsigned long off; |
1328 | unsigned long start; | 1352 | unsigned long start; |
1329 | u32 len; | 1353 | u32 len; |
1330 | 1354 | ||
1355 | if (!info) | ||
1356 | return -ENODEV; | ||
1331 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) | 1357 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) |
1332 | return -EINVAL; | 1358 | return -EINVAL; |
1333 | off = vma->vm_pgoff << PAGE_SHIFT; | 1359 | off = vma->vm_pgoff << PAGE_SHIFT; |
1360 | fb = info->fbops; | ||
1334 | if (!fb) | 1361 | if (!fb) |
1335 | return -ENODEV; | 1362 | return -ENODEV; |
1336 | mutex_lock(&info->mm_lock); | 1363 | mutex_lock(&info->mm_lock); |
@@ -1379,14 +1406,16 @@ __releases(&info->lock) | |||
1379 | struct fb_info *info; | 1406 | struct fb_info *info; |
1380 | int res = 0; | 1407 | int res = 0; |
1381 | 1408 | ||
1382 | if (fbidx >= FB_MAX) | 1409 | info = get_fb_info(fbidx); |
1383 | return -ENODEV; | 1410 | if (!info) { |
1384 | info = registered_fb[fbidx]; | ||
1385 | if (!info) | ||
1386 | request_module("fb%d", fbidx); | 1411 | request_module("fb%d", fbidx); |
1387 | info = registered_fb[fbidx]; | 1412 | info = get_fb_info(fbidx); |
1388 | if (!info) | 1413 | if (!info) |
1389 | return -ENODEV; | 1414 | return -ENODEV; |
1415 | } | ||
1416 | if (IS_ERR(info)) | ||
1417 | return PTR_ERR(info); | ||
1418 | |||
1390 | mutex_lock(&info->lock); | 1419 | mutex_lock(&info->lock); |
1391 | if (!try_module_get(info->fbops->owner)) { | 1420 | if (!try_module_get(info->fbops->owner)) { |
1392 | res = -ENODEV; | 1421 | res = -ENODEV; |
@@ -1404,6 +1433,8 @@ __releases(&info->lock) | |||
1404 | #endif | 1433 | #endif |
1405 | out: | 1434 | out: |
1406 | mutex_unlock(&info->lock); | 1435 | mutex_unlock(&info->lock); |
1436 | if (res) | ||
1437 | put_fb_info(info); | ||
1407 | return res; | 1438 | return res; |
1408 | } | 1439 | } |
1409 | 1440 | ||
@@ -1419,6 +1450,7 @@ __releases(&info->lock) | |||
1419 | info->fbops->fb_release(info,1); | 1450 | info->fbops->fb_release(info,1); |
1420 | module_put(info->fbops->owner); | 1451 | module_put(info->fbops->owner); |
1421 | mutex_unlock(&info->lock); | 1452 | mutex_unlock(&info->lock); |
1453 | put_fb_info(info); | ||
1422 | return 0; | 1454 | return 0; |
1423 | } | 1455 | } |
1424 | 1456 | ||
@@ -1439,6 +1471,7 @@ static const struct file_operations fb_fops = { | |||
1439 | #ifdef CONFIG_FB_DEFERRED_IO | 1471 | #ifdef CONFIG_FB_DEFERRED_IO |
1440 | .fsync = fb_deferred_io_fsync, | 1472 | .fsync = fb_deferred_io_fsync, |
1441 | #endif | 1473 | #endif |
1474 | .llseek = default_llseek, | ||
1442 | }; | 1475 | }; |
1443 | 1476 | ||
1444 | struct class *fb_class; | 1477 | struct class *fb_class; |
@@ -1475,7 +1508,7 @@ static bool apertures_overlap(struct aperture *gen, struct aperture *hw) | |||
1475 | if (gen->base == hw->base) | 1508 | if (gen->base == hw->base) |
1476 | return true; | 1509 | return true; |
1477 | /* is the generic aperture base inside the hw base->hw base+size */ | 1510 | /* is the generic aperture base inside the hw base->hw base+size */ |
1478 | if (gen->base > hw->base && gen->base <= hw->base + hw->size) | 1511 | if (gen->base > hw->base && gen->base < hw->base + hw->size) |
1479 | return true; | 1512 | return true; |
1480 | return false; | 1513 | return false; |
1481 | } | 1514 | } |
@@ -1504,8 +1537,10 @@ static bool fb_do_apertures_overlap(struct apertures_struct *gena, | |||
1504 | return false; | 1537 | return false; |
1505 | } | 1538 | } |
1506 | 1539 | ||
1540 | static int do_unregister_framebuffer(struct fb_info *fb_info); | ||
1541 | |||
1507 | #define VGA_FB_PHYS 0xA0000 | 1542 | #define VGA_FB_PHYS 0xA0000 |
1508 | void remove_conflicting_framebuffers(struct apertures_struct *a, | 1543 | static void do_remove_conflicting_framebuffers(struct apertures_struct *a, |
1509 | const char *name, bool primary) | 1544 | const char *name, bool primary) |
1510 | { | 1545 | { |
1511 | int i; | 1546 | int i; |
@@ -1524,46 +1559,35 @@ void remove_conflicting_framebuffers(struct apertures_struct *a, | |||
1524 | (primary && gen_aper && gen_aper->count && | 1559 | (primary && gen_aper && gen_aper->count && |
1525 | gen_aper->ranges[0].base == VGA_FB_PHYS)) { | 1560 | gen_aper->ranges[0].base == VGA_FB_PHYS)) { |
1526 | 1561 | ||
1527 | printk(KERN_ERR "fb: conflicting fb hw usage " | 1562 | printk(KERN_INFO "fb: conflicting fb hw usage " |
1528 | "%s vs %s - removing generic driver\n", | 1563 | "%s vs %s - removing generic driver\n", |
1529 | name, registered_fb[i]->fix.id); | 1564 | name, registered_fb[i]->fix.id); |
1530 | unregister_framebuffer(registered_fb[i]); | 1565 | do_unregister_framebuffer(registered_fb[i]); |
1531 | } | 1566 | } |
1532 | } | 1567 | } |
1533 | } | 1568 | } |
1534 | EXPORT_SYMBOL(remove_conflicting_framebuffers); | ||
1535 | |||
1536 | /** | ||
1537 | * register_framebuffer - registers a frame buffer device | ||
1538 | * @fb_info: frame buffer info structure | ||
1539 | * | ||
1540 | * Registers a frame buffer device @fb_info. | ||
1541 | * | ||
1542 | * Returns negative errno on error, or zero for success. | ||
1543 | * | ||
1544 | */ | ||
1545 | 1569 | ||
1546 | int | 1570 | static int do_register_framebuffer(struct fb_info *fb_info) |
1547 | register_framebuffer(struct fb_info *fb_info) | ||
1548 | { | 1571 | { |
1549 | int i; | 1572 | int i; |
1550 | struct fb_event event; | 1573 | struct fb_event event; |
1551 | struct fb_videomode mode; | 1574 | struct fb_videomode mode; |
1552 | 1575 | ||
1553 | if (num_registered_fb == FB_MAX) | ||
1554 | return -ENXIO; | ||
1555 | |||
1556 | if (fb_check_foreignness(fb_info)) | 1576 | if (fb_check_foreignness(fb_info)) |
1557 | return -ENOSYS; | 1577 | return -ENOSYS; |
1558 | 1578 | ||
1559 | remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, | 1579 | do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, |
1560 | fb_is_primary_device(fb_info)); | 1580 | fb_is_primary_device(fb_info)); |
1561 | 1581 | ||
1582 | if (num_registered_fb == FB_MAX) | ||
1583 | return -ENXIO; | ||
1584 | |||
1562 | num_registered_fb++; | 1585 | num_registered_fb++; |
1563 | for (i = 0 ; i < FB_MAX; i++) | 1586 | for (i = 0 ; i < FB_MAX; i++) |
1564 | if (!registered_fb[i]) | 1587 | if (!registered_fb[i]) |
1565 | break; | 1588 | break; |
1566 | fb_info->node = i; | 1589 | fb_info->node = i; |
1590 | atomic_set(&fb_info->count, 1); | ||
1567 | mutex_init(&fb_info->lock); | 1591 | mutex_init(&fb_info->lock); |
1568 | mutex_init(&fb_info->mm_lock); | 1592 | mutex_init(&fb_info->mm_lock); |
1569 | 1593 | ||
@@ -1609,36 +1633,14 @@ register_framebuffer(struct fb_info *fb_info) | |||
1609 | return 0; | 1633 | return 0; |
1610 | } | 1634 | } |
1611 | 1635 | ||
1612 | 1636 | static int do_unregister_framebuffer(struct fb_info *fb_info) | |
1613 | /** | ||
1614 | * unregister_framebuffer - releases a frame buffer device | ||
1615 | * @fb_info: frame buffer info structure | ||
1616 | * | ||
1617 | * Unregisters a frame buffer device @fb_info. | ||
1618 | * | ||
1619 | * Returns negative errno on error, or zero for success. | ||
1620 | * | ||
1621 | * This function will also notify the framebuffer console | ||
1622 | * to release the driver. | ||
1623 | * | ||
1624 | * This is meant to be called within a driver's module_exit() | ||
1625 | * function. If this is called outside module_exit(), ensure | ||
1626 | * that the driver implements fb_open() and fb_release() to | ||
1627 | * check that no processes are using the device. | ||
1628 | */ | ||
1629 | |||
1630 | int | ||
1631 | unregister_framebuffer(struct fb_info *fb_info) | ||
1632 | { | 1637 | { |
1633 | struct fb_event event; | 1638 | struct fb_event event; |
1634 | int i, ret = 0; | 1639 | int i, ret = 0; |
1635 | 1640 | ||
1636 | i = fb_info->node; | 1641 | i = fb_info->node; |
1637 | if (!registered_fb[i]) { | 1642 | if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info) |
1638 | ret = -EINVAL; | 1643 | return -EINVAL; |
1639 | goto done; | ||
1640 | } | ||
1641 | |||
1642 | 1644 | ||
1643 | if (!lock_fb_info(fb_info)) | 1645 | if (!lock_fb_info(fb_info)) |
1644 | return -ENODEV; | 1646 | return -ENODEV; |
@@ -1646,16 +1648,14 @@ unregister_framebuffer(struct fb_info *fb_info) | |||
1646 | ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); | 1648 | ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); |
1647 | unlock_fb_info(fb_info); | 1649 | unlock_fb_info(fb_info); |
1648 | 1650 | ||
1649 | if (ret) { | 1651 | if (ret) |
1650 | ret = -EINVAL; | 1652 | return -EINVAL; |
1651 | goto done; | ||
1652 | } | ||
1653 | 1653 | ||
1654 | if (fb_info->pixmap.addr && | 1654 | if (fb_info->pixmap.addr && |
1655 | (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) | 1655 | (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) |
1656 | kfree(fb_info->pixmap.addr); | 1656 | kfree(fb_info->pixmap.addr); |
1657 | fb_destroy_modelist(&fb_info->modelist); | 1657 | fb_destroy_modelist(&fb_info->modelist); |
1658 | registered_fb[i]=NULL; | 1658 | registered_fb[i] = NULL; |
1659 | num_registered_fb--; | 1659 | num_registered_fb--; |
1660 | fb_cleanup_device(fb_info); | 1660 | fb_cleanup_device(fb_info); |
1661 | device_destroy(fb_class, MKDEV(FB_MAJOR, i)); | 1661 | device_destroy(fb_class, MKDEV(FB_MAJOR, i)); |
@@ -1663,9 +1663,65 @@ unregister_framebuffer(struct fb_info *fb_info) | |||
1663 | fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); | 1663 | fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); |
1664 | 1664 | ||
1665 | /* this may free fb info */ | 1665 | /* this may free fb info */ |
1666 | if (fb_info->fbops->fb_destroy) | 1666 | put_fb_info(fb_info); |
1667 | fb_info->fbops->fb_destroy(fb_info); | 1667 | return 0; |
1668 | done: | 1668 | } |
1669 | |||
1670 | void remove_conflicting_framebuffers(struct apertures_struct *a, | ||
1671 | const char *name, bool primary) | ||
1672 | { | ||
1673 | mutex_lock(®istration_lock); | ||
1674 | do_remove_conflicting_framebuffers(a, name, primary); | ||
1675 | mutex_unlock(®istration_lock); | ||
1676 | } | ||
1677 | EXPORT_SYMBOL(remove_conflicting_framebuffers); | ||
1678 | |||
1679 | /** | ||
1680 | * register_framebuffer - registers a frame buffer device | ||
1681 | * @fb_info: frame buffer info structure | ||
1682 | * | ||
1683 | * Registers a frame buffer device @fb_info. | ||
1684 | * | ||
1685 | * Returns negative errno on error, or zero for success. | ||
1686 | * | ||
1687 | */ | ||
1688 | int | ||
1689 | register_framebuffer(struct fb_info *fb_info) | ||
1690 | { | ||
1691 | int ret; | ||
1692 | |||
1693 | mutex_lock(®istration_lock); | ||
1694 | ret = do_register_framebuffer(fb_info); | ||
1695 | mutex_unlock(®istration_lock); | ||
1696 | |||
1697 | return ret; | ||
1698 | } | ||
1699 | |||
1700 | /** | ||
1701 | * unregister_framebuffer - releases a frame buffer device | ||
1702 | * @fb_info: frame buffer info structure | ||
1703 | * | ||
1704 | * Unregisters a frame buffer device @fb_info. | ||
1705 | * | ||
1706 | * Returns negative errno on error, or zero for success. | ||
1707 | * | ||
1708 | * This function will also notify the framebuffer console | ||
1709 | * to release the driver. | ||
1710 | * | ||
1711 | * This is meant to be called within a driver's module_exit() | ||
1712 | * function. If this is called outside module_exit(), ensure | ||
1713 | * that the driver implements fb_open() and fb_release() to | ||
1714 | * check that no processes are using the device. | ||
1715 | */ | ||
1716 | int | ||
1717 | unregister_framebuffer(struct fb_info *fb_info) | ||
1718 | { | ||
1719 | int ret; | ||
1720 | |||
1721 | mutex_lock(®istration_lock); | ||
1722 | ret = do_unregister_framebuffer(fb_info); | ||
1723 | mutex_unlock(®istration_lock); | ||
1724 | |||
1669 | return ret; | 1725 | return ret; |
1670 | } | 1726 | } |
1671 | 1727 | ||