diff options
Diffstat (limited to 'drivers/usb/core/hcd.c')
| -rw-r--r-- | drivers/usb/core/hcd.c | 68 | 
1 files changed, 45 insertions, 23 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 0f8b7c93310e..7ee6e4cc0d89 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2761,6 +2761,26 @@ static void usb_put_invalidate_rhdev(struct usb_hcd *hcd)  }  /** + * usb_stop_hcd - Halt the HCD + * @hcd: the usb_hcd that has to be halted + * + * Stop the root-hub polling timer and invoke the HCD's ->stop callback. + */ +static void usb_stop_hcd(struct usb_hcd *hcd) +{ +	hcd->rh_pollable = 0; +	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); +	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. */ +	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); +	del_timer_sync(&hcd->rh_timer); +} + +/**   * usb_add_hcd - finish generic HCD structure initialization and register   * @hcd: the usb_hcd structure to initialize   * @irqnum: Interrupt line to allocate @@ -2775,6 +2795,7 @@ int usb_add_hcd(struct usb_hcd *hcd,  {  	int retval;  	struct usb_device *rhdev; +	struct usb_hcd *shared_hcd;  	if (!hcd->skip_phy_initialization && usb_hcd_is_primary_hcd(hcd)) {  		hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev); @@ -2935,24 +2956,31 @@ int usb_add_hcd(struct usb_hcd *hcd,  		goto err_hcd_driver_start;  	} +	/* starting here, usbcore will pay attention to the shared HCD roothub */ +	shared_hcd = hcd->shared_hcd; +	if (!usb_hcd_is_primary_hcd(hcd) && shared_hcd && HCD_DEFER_RH_REGISTER(shared_hcd)) { +		retval = register_root_hub(shared_hcd); +		if (retval != 0) +			goto err_register_root_hub; + +		if (shared_hcd->uses_new_polling && HCD_POLL_RH(shared_hcd)) +			usb_hcd_poll_rh_status(shared_hcd); +	} +  	/* starting here, usbcore will pay attention to this root hub */ -	retval = register_root_hub(hcd); -	if (retval != 0) -		goto err_register_root_hub; +	if (!HCD_DEFER_RH_REGISTER(hcd)) { +		retval = register_root_hub(hcd); +		if (retval != 0) +			goto err_register_root_hub; -	if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) -		usb_hcd_poll_rh_status(hcd); +		if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) +			usb_hcd_poll_rh_status(hcd); +	}  	return retval;  err_register_root_hub: -	hcd->rh_pollable = 0; -	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); -	del_timer_sync(&hcd->rh_timer); -	hcd->driver->stop(hcd); -	hcd->state = HC_STATE_HALT; -	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); -	del_timer_sync(&hcd->rh_timer); +	usb_stop_hcd(hcd);  err_hcd_driver_start:  	if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)  		free_irq(irqnum, hcd); @@ -2985,6 +3013,7 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);  void usb_remove_hcd(struct usb_hcd *hcd)  {  	struct usb_device *rhdev = hcd->self.root_hub; +	bool rh_registered;  	dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); @@ -2995,6 +3024,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)  	dev_dbg(hcd->self.controller, "roothub graceful disconnect\n");  	spin_lock_irq (&hcd_root_hub_lock); +	rh_registered = hcd->rh_registered;  	hcd->rh_registered = 0;  	spin_unlock_irq (&hcd_root_hub_lock); @@ -3004,7 +3034,8 @@ void usb_remove_hcd(struct usb_hcd *hcd)  	cancel_work_sync(&hcd->died_work);  	mutex_lock(&usb_bus_idr_lock); -	usb_disconnect(&rhdev);		/* Sets rhdev to NULL */ +	if (rh_registered) +		usb_disconnect(&rhdev);		/* Sets rhdev to NULL */  	mutex_unlock(&usb_bus_idr_lock);  	/* @@ -3022,16 +3053,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)  	 * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke  	 * the hub_status_data() callback.  	 */ -	hcd->rh_pollable = 0; -	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); -	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. */ -	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); -	del_timer_sync(&hcd->rh_timer); +	usb_stop_hcd(hcd);  	if (usb_hcd_is_primary_hcd(hcd)) {  		if (hcd->irq > 0)  |