aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2005-08-10 17:12:31 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-09-08 19:28:19 -0400
commitbf193d3cd2a3b73f2df74f57106114867946c09c (patch)
treecc47350df18dd7bf00a3adb136476e1896f20c19
parent8b28c7526a302bbfa618f7eab4ef961edd68c9a0 (diff)
[PATCH] USB: Disconnect children when unbinding the hub driver
This patch (as554) makes the hub driver disconnect any child USB devices when it is unbound from a hub. Normally this will never happen, but there are a few oddball ways to unbind the hub driver while leaving the children intact. For example, the new "unbind" sysfs attribute can be used for this purpose. Given that unbinding hubs with children is now safe, the patch also removes the code that prevented people from doing so using usbfs. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/core/devio.c10
-rw-r--r--drivers/usb/core/hub.c42
2 files changed, 40 insertions, 12 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 56c082f34927..b4265aa7d45e 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1238,7 +1238,6 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
1238 int retval = 0; 1238 int retval = 0;
1239 struct usb_interface *intf = NULL; 1239 struct usb_interface *intf = NULL;
1240 struct usb_driver *driver = NULL; 1240 struct usb_driver *driver = NULL;
1241 int i;
1242 1241
1243 /* get input parameters and alloc buffer */ 1242 /* get input parameters and alloc buffer */
1244 if (copy_from_user(&ctrl, arg, sizeof (ctrl))) 1243 if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
@@ -1270,15 +1269,6 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
1270 /* disconnect kernel driver from interface */ 1269 /* disconnect kernel driver from interface */
1271 case USBDEVFS_DISCONNECT: 1270 case USBDEVFS_DISCONNECT:
1272 1271
1273 /* don't allow the user to unbind the hub driver from
1274 * a hub with children to manage */
1275 for (i = 0; i < ps->dev->maxchild; ++i) {
1276 if (ps->dev->children[i])
1277 retval = -EBUSY;
1278 }
1279 if (retval)
1280 break;
1281
1282 down_write(&usb_bus_type.subsys.rwsem); 1272 down_write(&usb_bus_type.subsys.rwsem);
1283 if (intf->dev.driver) { 1273 if (intf->dev.driver) {
1284 driver = to_usb_driver(intf->dev.driver); 1274 driver = to_usb_driver(intf->dev.driver);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 4a4b41f2665a..9f54e8330f78 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -729,10 +729,29 @@ fail:
729 729
730static unsigned highspeed_hubs; 730static unsigned highspeed_hubs;
731 731
732/* Called after the hub driver is unbound from a hub with children */
733static void hub_remove_children_work(void *__hub)
734{
735 struct usb_hub *hub = __hub;
736 struct usb_device *hdev = hub->hdev;
737 int i;
738
739 kfree(hub);
740
741 usb_lock_device(hdev);
742 for (i = 0; i < hdev->maxchild; ++i) {
743 if (hdev->children[i])
744 usb_disconnect(&hdev->children[i]);
745 }
746 usb_unlock_device(hdev);
747 usb_put_dev(hdev);
748}
749
732static void hub_disconnect(struct usb_interface *intf) 750static void hub_disconnect(struct usb_interface *intf)
733{ 751{
734 struct usb_hub *hub = usb_get_intfdata (intf); 752 struct usb_hub *hub = usb_get_intfdata (intf);
735 struct usb_device *hdev; 753 struct usb_device *hdev;
754 int n, port1;
736 755
737 usb_set_intfdata (intf, NULL); 756 usb_set_intfdata (intf, NULL);
738 hdev = hub->hdev; 757 hdev = hub->hdev;
@@ -760,8 +779,27 @@ static void hub_disconnect(struct usb_interface *intf)
760 hub->buffer = NULL; 779 hub->buffer = NULL;
761 } 780 }
762 781
763 /* Free the memory */ 782 /* If there are any children then this is an unbind only, not a
764 kfree(hub); 783 * physical disconnection. The active ports must be disabled
784 * and later on we must call usb_disconnect(). We can't call
785 * it now because we may not hold the hub's device lock.
786 */
787 n = 0;
788 for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
789 if (hdev->children[port1 - 1]) {
790 ++n;
791 hub_port_disable(hub, port1, 1);
792 }
793 }
794
795 if (n == 0)
796 kfree(hub);
797 else {
798 /* Reuse the hub->leds work_struct for our own purposes */
799 INIT_WORK(&hub->leds, hub_remove_children_work, hub);
800 schedule_work(&hub->leds);
801 usb_get_dev(hdev);
802 }
765} 803}
766 804
767static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) 805static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)