diff options
Diffstat (limited to 'drivers/usb/core/hcd.c')
| -rw-r--r-- | drivers/usb/core/hcd.c | 32 | 
1 files changed, 25 insertions, 7 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index caae4625a1f..53f14c82ff2 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -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);  }  |