diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/devio.c | 10 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 42 |
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 | ||
730 | static unsigned highspeed_hubs; | 730 | static unsigned highspeed_hubs; |
731 | 731 | ||
732 | /* Called after the hub driver is unbound from a hub with children */ | ||
733 | static 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 | |||
732 | static void hub_disconnect(struct usb_interface *intf) | 750 | static 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 | ||
767 | static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) | 805 | static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) |