aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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)