aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKay Sievers <kay.sievers@vrfy.org>2012-01-28 14:57:46 -0500
committerDave Airlie <airlied@redhat.com>2012-03-15 09:35:22 -0400
commitce880cb860f36694d2cdebfac9e6ae18176fe4c4 (patch)
tree6fa674bd935d7f4c48dec243970c6b0b92f0c0a7
parent8229c885fe361e521ac64de36b16011e54a30de0 (diff)
udlfb: remove sysfs framebuffer device with USB .disconnect()
The USB graphics card driver delays the unregistering of the framebuffer device to a workqueue, which breaks the userspace visible remove uevent sequence. Recent userspace tools started to support USB graphics card hotplug out-of-the-box and rely on proper events sent by the kernel. The framebuffer device is a direct child of the USB interface which is removed immediately after the USB .disconnect() callback. But the fb device in /sys stays around until its final cleanup, at a time where all the parent devices have been removed already. To work around that, we remove the sysfs fb device directly in the USB .disconnect() callback and leave only the cleanup of the internal fb data to the delayed work. Before: add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb0 (graphics) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) remove /2-1.2:1.0/graphics/fb0 (graphics) After: add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb1 (graphics) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb1 (graphics) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) Cc: stable@vger.kernel.org Tested-by: Bernie Thompson <bernie@plugable.com> Acked-by: Bernie Thompson <bernie@plugable.com> Signed-off-by: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
-rw-r--r--drivers/video/fbmem.c18
-rw-r--r--drivers/video/udlfb.c2
-rw-r--r--include/linux/fb.h1
3 files changed, 19 insertions, 2 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index ac9141b85356..c6ce416ab587 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1665,6 +1665,7 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
1665 if (ret) 1665 if (ret)
1666 return -EINVAL; 1666 return -EINVAL;
1667 1667
1668 unlink_framebuffer(fb_info);
1668 if (fb_info->pixmap.addr && 1669 if (fb_info->pixmap.addr &&
1669 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) 1670 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
1670 kfree(fb_info->pixmap.addr); 1671 kfree(fb_info->pixmap.addr);
@@ -1672,7 +1673,6 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
1672 registered_fb[i] = NULL; 1673 registered_fb[i] = NULL;
1673 num_registered_fb--; 1674 num_registered_fb--;
1674 fb_cleanup_device(fb_info); 1675 fb_cleanup_device(fb_info);
1675 device_destroy(fb_class, MKDEV(FB_MAJOR, i));
1676 event.info = fb_info; 1676 event.info = fb_info;
1677 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); 1677 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
1678 1678
@@ -1681,6 +1681,22 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
1681 return 0; 1681 return 0;
1682} 1682}
1683 1683
1684int unlink_framebuffer(struct fb_info *fb_info)
1685{
1686 int i;
1687
1688 i = fb_info->node;
1689 if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
1690 return -EINVAL;
1691
1692 if (fb_info->dev) {
1693 device_destroy(fb_class, MKDEV(FB_MAJOR, i));
1694 fb_info->dev = NULL;
1695 }
1696 return 0;
1697}
1698EXPORT_SYMBOL(unlink_framebuffer);
1699
1684void remove_conflicting_framebuffers(struct apertures_struct *a, 1700void remove_conflicting_framebuffers(struct apertures_struct *a,
1685 const char *name, bool primary) 1701 const char *name, bool primary)
1686{ 1702{
diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c
index a19773149bd7..a40c05ebbdc2 100644
--- a/drivers/video/udlfb.c
+++ b/drivers/video/udlfb.c
@@ -1739,7 +1739,7 @@ static void dlfb_usb_disconnect(struct usb_interface *interface)
1739 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) 1739 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
1740 device_remove_file(info->dev, &fb_device_attrs[i]); 1740 device_remove_file(info->dev, &fb_device_attrs[i]);
1741 device_remove_bin_file(info->dev, &edid_attr); 1741 device_remove_bin_file(info->dev, &edid_attr);
1742 1742 unlink_framebuffer(info);
1743 usb_set_intfdata(interface, NULL); 1743 usb_set_intfdata(interface, NULL);
1744 1744
1745 /* if clients still have us open, will be freed on last close */ 1745 /* if clients still have us open, will be freed on last close */
diff --git a/include/linux/fb.h b/include/linux/fb.h
index c18122f40543..a395b8c76992 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1003,6 +1003,7 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
1003/* drivers/video/fbmem.c */ 1003/* drivers/video/fbmem.c */
1004extern int register_framebuffer(struct fb_info *fb_info); 1004extern int register_framebuffer(struct fb_info *fb_info);
1005extern int unregister_framebuffer(struct fb_info *fb_info); 1005extern int unregister_framebuffer(struct fb_info *fb_info);
1006extern int unlink_framebuffer(struct fb_info *fb_info);
1006extern void remove_conflicting_framebuffers(struct apertures_struct *a, 1007extern void remove_conflicting_framebuffers(struct apertures_struct *a,
1007 const char *name, bool primary); 1008 const char *name, bool primary);
1008extern int fb_prepare_logo(struct fb_info *fb_info, int rotate); 1009extern int fb_prepare_logo(struct fb_info *fb_info, int rotate);