|
@@ -667,7 +667,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
|
|
|
unsigned long flags;
|
|
|
char buffer[6]; /* Any root hubs with > 31 ports? */
|
|
|
|
|
|
- if (unlikely(!hcd->rh_registered))
|
|
|
+ if (unlikely(!hcd->rh_pollable))
|
|
|
return;
|
|
|
if (!hcd->uses_new_polling && !hcd->status_urb)
|
|
|
return;
|
|
@@ -2217,6 +2217,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|
|
retval = -ENOMEM;
|
|
|
goto err_allocate_root_hub;
|
|
|
}
|
|
|
+ hcd->self.root_hub = rhdev;
|
|
|
|
|
|
switch (hcd->driver->flags & HCD_MASK) {
|
|
|
case HCD_USB11:
|
|
@@ -2231,7 +2232,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|
|
default:
|
|
|
goto err_set_rh_speed;
|
|
|
}
|
|
|
- hcd->self.root_hub = rhdev;
|
|
|
|
|
|
/* wakeup flag init defaults to "everything works" for root hubs,
|
|
|
* but drivers can override it in reset() if needed, along with
|
|
@@ -2246,6 +2246,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|
|
dev_err(hcd->self.controller, "can't setup\n");
|
|
|
goto err_hcd_driver_setup;
|
|
|
}
|
|
|
+ hcd->rh_pollable = 1;
|
|
|
|
|
|
/* NOTE: root hub and controller capabilities may not be the same */
|
|
|
if (device_can_wakeup(hcd->self.controller)
|
|
@@ -2315,9 +2316,12 @@ error_create_attr_group:
|
|
|
cancel_work_sync(&hcd->wakeup_work);
|
|
|
#endif
|
|
|
mutex_lock(&usb_bus_list_lock);
|
|
|
- usb_disconnect(&hcd->self.root_hub);
|
|
|
+ usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
|
|
mutex_unlock(&usb_bus_list_lock);
|
|
|
err_register_root_hub:
|
|
|
+ hcd->rh_pollable = 0;
|
|
|
+ hcd->poll_rh = 0;
|
|
|
+ del_timer_sync(&hcd->rh_timer);
|
|
|
hcd->driver->stop(hcd);
|
|
|
hcd->state = HC_STATE_HALT;
|
|
|
hcd->poll_rh = 0;
|
|
@@ -2328,8 +2332,7 @@ err_hcd_driver_start:
|
|
|
err_request_irq:
|
|
|
err_hcd_driver_setup:
|
|
|
err_set_rh_speed:
|
|
|
- hcd->self.root_hub = NULL;
|
|
|
- usb_put_dev(rhdev);
|
|
|
+ usb_put_dev(hcd->self.root_hub);
|
|
|
err_allocate_root_hub:
|
|
|
usb_deregister_bus(&hcd->self);
|
|
|
err_register_bus:
|
|
@@ -2348,9 +2351,12 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
|
|
|
*/
|
|
|
void usb_remove_hcd(struct usb_hcd *hcd)
|
|
|
{
|
|
|
+ struct usb_device *rhdev = hcd->self.root_hub;
|
|
|
+
|
|
|
dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
|
|
|
|
|
|
- sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group);
|
|
|
+ usb_get_dev(rhdev);
|
|
|
+ sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group);
|
|
|
|
|
|
if (HC_IS_RUNNING (hcd->state))
|
|
|
hcd->state = HC_STATE_QUIESCING;
|
|
@@ -2365,17 +2371,29 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
|
|
#endif
|
|
|
|
|
|
mutex_lock(&usb_bus_list_lock);
|
|
|
- usb_disconnect(&hcd->self.root_hub);
|
|
|
+ usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
|
|
mutex_unlock(&usb_bus_list_lock);
|
|
|
|
|
|
+ /* Prevent any more root-hub status calls from the timer.
|
|
|
+ * The HCD might still restart the timer (if a port status change
|
|
|
+ * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke
|
|
|
+ * the hub_status_data() callback.
|
|
|
+ */
|
|
|
+ hcd->rh_pollable = 0;
|
|
|
+ hcd->poll_rh = 0;
|
|
|
+ del_timer_sync(&hcd->rh_timer);
|
|
|
+
|
|
|
hcd->driver->stop(hcd);
|
|
|
hcd->state = HC_STATE_HALT;
|
|
|
|
|
|
+ /* In case the HCD restarted the timer, stop it again. */
|
|
|
hcd->poll_rh = 0;
|
|
|
del_timer_sync(&hcd->rh_timer);
|
|
|
|
|
|
if (hcd->irq >= 0)
|
|
|
free_irq(hcd->irq, hcd);
|
|
|
+
|
|
|
+ usb_put_dev(hcd->self.root_hub);
|
|
|
usb_deregister_bus(&hcd->self);
|
|
|
hcd_buffer_destroy(hcd);
|
|
|
}
|