diff options
-rw-r--r-- | drivers/usb/core/hcd.c | 32 | ||||
-rw-r--r-- | include/linux/usb/hcd.h | 1 |
2 files changed, 26 insertions, 7 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index caae4625a1f1..53f14c82ff2e 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) | |||
667 | unsigned long flags; | 667 | unsigned long flags; |
668 | char buffer[6]; /* Any root hubs with > 31 ports? */ | 668 | char buffer[6]; /* Any root hubs with > 31 ports? */ |
669 | 669 | ||
670 | if (unlikely(!hcd->rh_registered)) | 670 | if (unlikely(!hcd->rh_pollable)) |
671 | return; | 671 | return; |
672 | if (!hcd->uses_new_polling && !hcd->status_urb) | 672 | if (!hcd->uses_new_polling && !hcd->status_urb) |
673 | return; | 673 | return; |
@@ -2217,6 +2217,7 @@ int usb_add_hcd(struct usb_hcd *hcd, | |||
2217 | retval = -ENOMEM; | 2217 | retval = -ENOMEM; |
2218 | goto err_allocate_root_hub; | 2218 | goto err_allocate_root_hub; |
2219 | } | 2219 | } |
2220 | hcd->self.root_hub = rhdev; | ||
2220 | 2221 | ||
2221 | switch (hcd->driver->flags & HCD_MASK) { | 2222 | switch (hcd->driver->flags & HCD_MASK) { |
2222 | case HCD_USB11: | 2223 | case HCD_USB11: |
@@ -2231,7 +2232,6 @@ int usb_add_hcd(struct usb_hcd *hcd, | |||
2231 | default: | 2232 | default: |
2232 | goto err_set_rh_speed; | 2233 | goto err_set_rh_speed; |
2233 | } | 2234 | } |
2234 | hcd->self.root_hub = rhdev; | ||
2235 | 2235 | ||
2236 | /* wakeup flag init defaults to "everything works" for root hubs, | 2236 | /* wakeup flag init defaults to "everything works" for root hubs, |
2237 | * but drivers can override it in reset() if needed, along with | 2237 | * but drivers can override it in reset() if needed, along with |
@@ -2246,6 +2246,7 @@ int usb_add_hcd(struct usb_hcd *hcd, | |||
2246 | dev_err(hcd->self.controller, "can't setup\n"); | 2246 | dev_err(hcd->self.controller, "can't setup\n"); |
2247 | goto err_hcd_driver_setup; | 2247 | goto err_hcd_driver_setup; |
2248 | } | 2248 | } |
2249 | hcd->rh_pollable = 1; | ||
2249 | 2250 | ||
2250 | /* NOTE: root hub and controller capabilities may not be the same */ | 2251 | /* NOTE: root hub and controller capabilities may not be the same */ |
2251 | if (device_can_wakeup(hcd->self.controller) | 2252 | if (device_can_wakeup(hcd->self.controller) |
@@ -2315,9 +2316,12 @@ error_create_attr_group: | |||
2315 | cancel_work_sync(&hcd->wakeup_work); | 2316 | cancel_work_sync(&hcd->wakeup_work); |
2316 | #endif | 2317 | #endif |
2317 | mutex_lock(&usb_bus_list_lock); | 2318 | mutex_lock(&usb_bus_list_lock); |
2318 | usb_disconnect(&hcd->self.root_hub); | 2319 | usb_disconnect(&rhdev); /* Sets rhdev to NULL */ |
2319 | mutex_unlock(&usb_bus_list_lock); | 2320 | mutex_unlock(&usb_bus_list_lock); |
2320 | err_register_root_hub: | 2321 | err_register_root_hub: |
2322 | hcd->rh_pollable = 0; | ||
2323 | hcd->poll_rh = 0; | ||
2324 | del_timer_sync(&hcd->rh_timer); | ||
2321 | hcd->driver->stop(hcd); | 2325 | hcd->driver->stop(hcd); |
2322 | hcd->state = HC_STATE_HALT; | 2326 | hcd->state = HC_STATE_HALT; |
2323 | hcd->poll_rh = 0; | 2327 | hcd->poll_rh = 0; |
@@ -2328,8 +2332,7 @@ err_hcd_driver_start: | |||
2328 | err_request_irq: | 2332 | err_request_irq: |
2329 | err_hcd_driver_setup: | 2333 | err_hcd_driver_setup: |
2330 | err_set_rh_speed: | 2334 | err_set_rh_speed: |
2331 | hcd->self.root_hub = NULL; | 2335 | usb_put_dev(hcd->self.root_hub); |
2332 | usb_put_dev(rhdev); | ||
2333 | err_allocate_root_hub: | 2336 | err_allocate_root_hub: |
2334 | usb_deregister_bus(&hcd->self); | 2337 | usb_deregister_bus(&hcd->self); |
2335 | err_register_bus: | 2338 | err_register_bus: |
@@ -2348,9 +2351,12 @@ EXPORT_SYMBOL_GPL(usb_add_hcd); | |||
2348 | */ | 2351 | */ |
2349 | void usb_remove_hcd(struct usb_hcd *hcd) | 2352 | void usb_remove_hcd(struct usb_hcd *hcd) |
2350 | { | 2353 | { |
2354 | struct usb_device *rhdev = hcd->self.root_hub; | ||
2355 | |||
2351 | dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); | 2356 | dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); |
2352 | 2357 | ||
2353 | sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group); | 2358 | usb_get_dev(rhdev); |
2359 | sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group); | ||
2354 | 2360 | ||
2355 | if (HC_IS_RUNNING (hcd->state)) | 2361 | if (HC_IS_RUNNING (hcd->state)) |
2356 | hcd->state = HC_STATE_QUIESCING; | 2362 | hcd->state = HC_STATE_QUIESCING; |
@@ -2365,17 +2371,29 @@ void usb_remove_hcd(struct usb_hcd *hcd) | |||
2365 | #endif | 2371 | #endif |
2366 | 2372 | ||
2367 | mutex_lock(&usb_bus_list_lock); | 2373 | mutex_lock(&usb_bus_list_lock); |
2368 | usb_disconnect(&hcd->self.root_hub); | 2374 | usb_disconnect(&rhdev); /* Sets rhdev to NULL */ |
2369 | mutex_unlock(&usb_bus_list_lock); | 2375 | mutex_unlock(&usb_bus_list_lock); |
2370 | 2376 | ||
2377 | /* Prevent any more root-hub status calls from the timer. | ||
2378 | * The HCD might still restart the timer (if a port status change | ||
2379 | * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke | ||
2380 | * the hub_status_data() callback. | ||
2381 | */ | ||
2382 | hcd->rh_pollable = 0; | ||
2383 | hcd->poll_rh = 0; | ||
2384 | del_timer_sync(&hcd->rh_timer); | ||
2385 | |||
2371 | hcd->driver->stop(hcd); | 2386 | hcd->driver->stop(hcd); |
2372 | hcd->state = HC_STATE_HALT; | 2387 | hcd->state = HC_STATE_HALT; |
2373 | 2388 | ||
2389 | /* In case the HCD restarted the timer, stop it again. */ | ||
2374 | hcd->poll_rh = 0; | 2390 | hcd->poll_rh = 0; |
2375 | del_timer_sync(&hcd->rh_timer); | 2391 | del_timer_sync(&hcd->rh_timer); |
2376 | 2392 | ||
2377 | if (hcd->irq >= 0) | 2393 | if (hcd->irq >= 0) |
2378 | free_irq(hcd->irq, hcd); | 2394 | free_irq(hcd->irq, hcd); |
2395 | |||
2396 | usb_put_dev(hcd->self.root_hub); | ||
2379 | usb_deregister_bus(&hcd->self); | 2397 | usb_deregister_bus(&hcd->self); |
2380 | hcd_buffer_destroy(hcd); | 2398 | hcd_buffer_destroy(hcd); |
2381 | } | 2399 | } |
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 2e3a4ea1a3da..11b638195901 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h | |||
@@ -95,6 +95,7 @@ struct usb_hcd { | |||
95 | #define HCD_FLAG_SAW_IRQ 0x00000002 | 95 | #define HCD_FLAG_SAW_IRQ 0x00000002 |
96 | 96 | ||
97 | unsigned rh_registered:1;/* is root hub registered? */ | 97 | unsigned rh_registered:1;/* is root hub registered? */ |
98 | unsigned rh_pollable:1; /* may we poll the root hub? */ | ||
98 | 99 | ||
99 | /* The next flag is a stopgap, to be removed when all the HCDs | 100 | /* The next flag is a stopgap, to be removed when all the HCDs |
100 | * support the new root-hub polling mechanism. */ | 101 | * support the new root-hub polling mechanism. */ |