diff options
author | Thomas Pugliese <thomas.pugliese@gmail.com> | 2013-06-24 15:26:35 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-06-24 19:20:43 -0400 |
commit | a899575191ba85a71401f2c8f36bec7b14487043 (patch) | |
tree | 74070049b20ec44cf625db68ac62751e4b33cab0 | |
parent | 6e6581e0a94758a4e69fab82407d39312eeff5c4 (diff) |
USB: HWA: fix device probe failure
This patch fixes a race condition that caused the HWA_HC interface probe
function to occasionally fail. The HWA_HC would attempt to register
itself with the HWA_RC by searching for a uwb_rc class device with the
same parent device ptr. If the probe function for the HWA_RC interface
had yet to run, the uwb_rc class device would not have been created
causing the look up to fail and the HWA_HC probe function to return an
error causing the device to be unusable.
The fix is for the HWA to delay registering with the HWA_RC until
receiving the command from userspace to start the wireless channel. It
is the responsibility of userspace to ensure that the uwb_rc class
device has been created before starting the HWA channel.
Signed-off-by: Thomas Pugliese <thomas.pugliese@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/host/hwa-hc.c | 11 | ||||
-rw-r--r-- | drivers/usb/wusbcore/mmc.c | 27 | ||||
-rw-r--r-- | drivers/usb/wusbcore/pal.c | 5 | ||||
-rw-r--r-- | drivers/usb/wusbcore/reservation.c | 3 | ||||
-rw-r--r-- | drivers/usb/wusbcore/wusbhc.c | 9 | ||||
-rw-r--r-- | drivers/uwb/pal.c | 2 | ||||
-rw-r--r-- | drivers/uwb/uwb-internal.h | 3 |
7 files changed, 42 insertions, 18 deletions
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 4af750ecfe8f..483990c716aa 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c | |||
@@ -683,12 +683,9 @@ static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface) | |||
683 | wa->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */ | 683 | wa->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */ |
684 | wa->usb_iface = usb_get_intf(iface); | 684 | wa->usb_iface = usb_get_intf(iface); |
685 | wusbhc->dev = dev; | 685 | wusbhc->dev = dev; |
686 | wusbhc->uwb_rc = uwb_rc_get_by_grandpa(iface->dev.parent); | 686 | /* defer getting the uwb_rc handle until it is needed since it |
687 | if (wusbhc->uwb_rc == NULL) { | 687 | * may not have been registered by the hwa_rc driver yet. */ |
688 | result = -ENODEV; | 688 | wusbhc->uwb_rc = NULL; |
689 | dev_err(dev, "Cannot get associated UWB Host Controller\n"); | ||
690 | goto error_rc_get; | ||
691 | } | ||
692 | result = wa_fill_descr(wa); /* Get the device descriptor */ | 689 | result = wa_fill_descr(wa); /* Get the device descriptor */ |
693 | if (result < 0) | 690 | if (result < 0) |
694 | goto error_fill_descriptor; | 691 | goto error_fill_descriptor; |
@@ -731,8 +728,6 @@ error_wusbhc_create: | |||
731 | /* WA Descr fill allocs no resources */ | 728 | /* WA Descr fill allocs no resources */ |
732 | error_security_create: | 729 | error_security_create: |
733 | error_fill_descriptor: | 730 | error_fill_descriptor: |
734 | uwb_rc_put(wusbhc->uwb_rc); | ||
735 | error_rc_get: | ||
736 | usb_put_intf(iface); | 731 | usb_put_intf(iface); |
737 | usb_put_dev(usb_dev); | 732 | usb_put_dev(usb_dev); |
738 | return result; | 733 | return result; |
diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c index 021467f86d9e..b71760c8d3ad 100644 --- a/drivers/usb/wusbcore/mmc.c +++ b/drivers/usb/wusbcore/mmc.c | |||
@@ -195,6 +195,7 @@ int wusbhc_start(struct wusbhc *wusbhc) | |||
195 | struct device *dev = wusbhc->dev; | 195 | struct device *dev = wusbhc->dev; |
196 | 196 | ||
197 | WARN_ON(wusbhc->wuie_host_info != NULL); | 197 | WARN_ON(wusbhc->wuie_host_info != NULL); |
198 | BUG_ON(wusbhc->uwb_rc == NULL); | ||
198 | 199 | ||
199 | result = wusbhc_rsv_establish(wusbhc); | 200 | result = wusbhc_rsv_establish(wusbhc); |
200 | if (result < 0) { | 201 | if (result < 0) { |
@@ -276,12 +277,38 @@ int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) | |||
276 | } | 277 | } |
277 | wusbhc->chid = *chid; | 278 | wusbhc->chid = *chid; |
278 | } | 279 | } |
280 | |||
281 | /* register with UWB if we haven't already since we are about to start | ||
282 | the radio. */ | ||
283 | if ((chid) && (wusbhc->uwb_rc == NULL)) { | ||
284 | wusbhc->uwb_rc = uwb_rc_get_by_grandpa(wusbhc->dev->parent); | ||
285 | if (wusbhc->uwb_rc == NULL) { | ||
286 | result = -ENODEV; | ||
287 | dev_err(wusbhc->dev, "Cannot get associated UWB Host Controller\n"); | ||
288 | goto error_rc_get; | ||
289 | } | ||
290 | |||
291 | result = wusbhc_pal_register(wusbhc); | ||
292 | if (result < 0) { | ||
293 | dev_err(wusbhc->dev, "Cannot register as a UWB PAL\n"); | ||
294 | goto error_pal_register; | ||
295 | } | ||
296 | } | ||
279 | mutex_unlock(&wusbhc->mutex); | 297 | mutex_unlock(&wusbhc->mutex); |
280 | 298 | ||
281 | if (chid) | 299 | if (chid) |
282 | result = uwb_radio_start(&wusbhc->pal); | 300 | result = uwb_radio_start(&wusbhc->pal); |
283 | else | 301 | else |
284 | uwb_radio_stop(&wusbhc->pal); | 302 | uwb_radio_stop(&wusbhc->pal); |
303 | |||
304 | return result; | ||
305 | |||
306 | error_pal_register: | ||
307 | uwb_rc_put(wusbhc->uwb_rc); | ||
308 | wusbhc->uwb_rc = NULL; | ||
309 | error_rc_get: | ||
310 | mutex_unlock(&wusbhc->mutex); | ||
311 | |||
285 | return result; | 312 | return result; |
286 | } | 313 | } |
287 | EXPORT_SYMBOL_GPL(wusbhc_chid_set); | 314 | EXPORT_SYMBOL_GPL(wusbhc_chid_set); |
diff --git a/drivers/usb/wusbcore/pal.c b/drivers/usb/wusbcore/pal.c index d0b172c5ecc7..59e100c2eb50 100644 --- a/drivers/usb/wusbcore/pal.c +++ b/drivers/usb/wusbcore/pal.c | |||
@@ -45,10 +45,11 @@ int wusbhc_pal_register(struct wusbhc *wusbhc) | |||
45 | } | 45 | } |
46 | 46 | ||
47 | /** | 47 | /** |
48 | * wusbhc_pal_register - unregister the WUSB HC as a UWB PAL | 48 | * wusbhc_pal_unregister - unregister the WUSB HC as a UWB PAL |
49 | * @wusbhc: the WUSB HC | 49 | * @wusbhc: the WUSB HC |
50 | */ | 50 | */ |
51 | void wusbhc_pal_unregister(struct wusbhc *wusbhc) | 51 | void wusbhc_pal_unregister(struct wusbhc *wusbhc) |
52 | { | 52 | { |
53 | uwb_pal_unregister(&wusbhc->pal); | 53 | if (wusbhc->uwb_rc) |
54 | uwb_pal_unregister(&wusbhc->pal); | ||
54 | } | 55 | } |
diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c index 6f4fafdc2401..ead79f793927 100644 --- a/drivers/usb/wusbcore/reservation.c +++ b/drivers/usb/wusbcore/reservation.c | |||
@@ -80,6 +80,9 @@ int wusbhc_rsv_establish(struct wusbhc *wusbhc) | |||
80 | struct uwb_dev_addr bcid; | 80 | struct uwb_dev_addr bcid; |
81 | int ret; | 81 | int ret; |
82 | 82 | ||
83 | if (rc == NULL) | ||
84 | return -ENODEV; | ||
85 | |||
83 | rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc); | 86 | rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc); |
84 | if (rsv == NULL) | 87 | if (rsv == NULL) |
85 | return -ENOMEM; | 88 | return -ENOMEM; |
diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c index e712af3e46c2..742c607d1fa3 100644 --- a/drivers/usb/wusbcore/wusbhc.c +++ b/drivers/usb/wusbcore/wusbhc.c | |||
@@ -325,13 +325,7 @@ int wusbhc_b_create(struct wusbhc *wusbhc) | |||
325 | goto error_create_attr_group; | 325 | goto error_create_attr_group; |
326 | } | 326 | } |
327 | 327 | ||
328 | result = wusbhc_pal_register(wusbhc); | ||
329 | if (result < 0) | ||
330 | goto error_pal_register; | ||
331 | return 0; | 328 | return 0; |
332 | |||
333 | error_pal_register: | ||
334 | sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); | ||
335 | error_create_attr_group: | 329 | error_create_attr_group: |
336 | return result; | 330 | return result; |
337 | } | 331 | } |
@@ -457,7 +451,8 @@ EXPORT_SYMBOL_GPL(wusbhc_giveback_urb); | |||
457 | */ | 451 | */ |
458 | void wusbhc_reset_all(struct wusbhc *wusbhc) | 452 | void wusbhc_reset_all(struct wusbhc *wusbhc) |
459 | { | 453 | { |
460 | uwb_rc_reset_all(wusbhc->uwb_rc); | 454 | if (wusbhc->uwb_rc) |
455 | uwb_rc_reset_all(wusbhc->uwb_rc); | ||
461 | } | 456 | } |
462 | EXPORT_SYMBOL_GPL(wusbhc_reset_all); | 457 | EXPORT_SYMBOL_GPL(wusbhc_reset_all); |
463 | 458 | ||
diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c index 8ee7d90a8c68..690577d2a35b 100644 --- a/drivers/uwb/pal.c +++ b/drivers/uwb/pal.c | |||
@@ -44,10 +44,12 @@ int uwb_pal_register(struct uwb_pal *pal) | |||
44 | int ret; | 44 | int ret; |
45 | 45 | ||
46 | if (pal->device) { | 46 | if (pal->device) { |
47 | /* create a link to the uwb_rc in the PAL device's directory. */ | ||
47 | ret = sysfs_create_link(&pal->device->kobj, | 48 | ret = sysfs_create_link(&pal->device->kobj, |
48 | &rc->uwb_dev.dev.kobj, "uwb_rc"); | 49 | &rc->uwb_dev.dev.kobj, "uwb_rc"); |
49 | if (ret < 0) | 50 | if (ret < 0) |
50 | return ret; | 51 | return ret; |
52 | /* create a link to the PAL in the UWB device's directory. */ | ||
51 | ret = sysfs_create_link(&rc->uwb_dev.dev.kobj, | 53 | ret = sysfs_create_link(&rc->uwb_dev.dev.kobj, |
52 | &pal->device->kobj, pal->name); | 54 | &pal->device->kobj, pal->name); |
53 | if (ret < 0) { | 55 | if (ret < 0) { |
diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index a7494bf10081..9a103b100f1e 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h | |||
@@ -55,7 +55,8 @@ static inline struct uwb_rc *__uwb_rc_get(struct uwb_rc *rc) | |||
55 | 55 | ||
56 | static inline void __uwb_rc_put(struct uwb_rc *rc) | 56 | static inline void __uwb_rc_put(struct uwb_rc *rc) |
57 | { | 57 | { |
58 | uwb_dev_put(&rc->uwb_dev); | 58 | if (rc) |
59 | uwb_dev_put(&rc->uwb_dev); | ||
59 | } | 60 | } |
60 | 61 | ||
61 | extern int uwb_rc_reset(struct uwb_rc *rc); | 62 | extern int uwb_rc_reset(struct uwb_rc *rc); |