diff options
author | Thomas Pugliese <thomas.pugliese@gmail.com> | 2013-06-24 21:26:35 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-06-25 01:20:43 +0200 |
commit | a899575191ba85a71401f2c8f36bec7b14487043 (patch) | |
tree | 74070049b20ec44cf625db68ac62751e4b33cab0 | |
parent | wusbcore: add entries in Documentation/ABI for new wusbhc sysfs attributes (diff) | |
download | linux-a899575191ba85a71401f2c8f36bec7b14487043.tar.xz linux-a899575191ba85a71401f2c8f36bec7b14487043.zip |
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) wa->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */ wa->usb_iface = usb_get_intf(iface); wusbhc->dev = dev; - wusbhc->uwb_rc = uwb_rc_get_by_grandpa(iface->dev.parent); - if (wusbhc->uwb_rc == NULL) { - result = -ENODEV; - dev_err(dev, "Cannot get associated UWB Host Controller\n"); - goto error_rc_get; - } + /* defer getting the uwb_rc handle until it is needed since it + * may not have been registered by the hwa_rc driver yet. */ + wusbhc->uwb_rc = NULL; result = wa_fill_descr(wa); /* Get the device descriptor */ if (result < 0) goto error_fill_descriptor; @@ -731,8 +728,6 @@ error_wusbhc_create: /* WA Descr fill allocs no resources */ error_security_create: error_fill_descriptor: - uwb_rc_put(wusbhc->uwb_rc); -error_rc_get: usb_put_intf(iface); usb_put_dev(usb_dev); 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) struct device *dev = wusbhc->dev; WARN_ON(wusbhc->wuie_host_info != NULL); + BUG_ON(wusbhc->uwb_rc == NULL); result = wusbhc_rsv_establish(wusbhc); if (result < 0) { @@ -276,12 +277,38 @@ int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) } wusbhc->chid = *chid; } + + /* register with UWB if we haven't already since we are about to start + the radio. */ + if ((chid) && (wusbhc->uwb_rc == NULL)) { + wusbhc->uwb_rc = uwb_rc_get_by_grandpa(wusbhc->dev->parent); + if (wusbhc->uwb_rc == NULL) { + result = -ENODEV; + dev_err(wusbhc->dev, "Cannot get associated UWB Host Controller\n"); + goto error_rc_get; + } + + result = wusbhc_pal_register(wusbhc); + if (result < 0) { + dev_err(wusbhc->dev, "Cannot register as a UWB PAL\n"); + goto error_pal_register; + } + } mutex_unlock(&wusbhc->mutex); if (chid) result = uwb_radio_start(&wusbhc->pal); else uwb_radio_stop(&wusbhc->pal); + + return result; + +error_pal_register: + uwb_rc_put(wusbhc->uwb_rc); + wusbhc->uwb_rc = NULL; +error_rc_get: + mutex_unlock(&wusbhc->mutex); + return result; } 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) } /** - * wusbhc_pal_register - unregister the WUSB HC as a UWB PAL + * wusbhc_pal_unregister - unregister the WUSB HC as a UWB PAL * @wusbhc: the WUSB HC */ void wusbhc_pal_unregister(struct wusbhc *wusbhc) { - uwb_pal_unregister(&wusbhc->pal); + if (wusbhc->uwb_rc) + uwb_pal_unregister(&wusbhc->pal); } 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) struct uwb_dev_addr bcid; int ret; + if (rc == NULL) + return -ENODEV; + rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc); if (rsv == NULL) 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) goto error_create_attr_group; } - result = wusbhc_pal_register(wusbhc); - if (result < 0) - goto error_pal_register; return 0; - -error_pal_register: - sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); error_create_attr_group: return result; } @@ -457,7 +451,8 @@ EXPORT_SYMBOL_GPL(wusbhc_giveback_urb); */ void wusbhc_reset_all(struct wusbhc *wusbhc) { - uwb_rc_reset_all(wusbhc->uwb_rc); + if (wusbhc->uwb_rc) + uwb_rc_reset_all(wusbhc->uwb_rc); } EXPORT_SYMBOL_GPL(wusbhc_reset_all); 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) int ret; if (pal->device) { + /* create a link to the uwb_rc in the PAL device's directory. */ ret = sysfs_create_link(&pal->device->kobj, &rc->uwb_dev.dev.kobj, "uwb_rc"); if (ret < 0) return ret; + /* create a link to the PAL in the UWB device's directory. */ ret = sysfs_create_link(&rc->uwb_dev.dev.kobj, &pal->device->kobj, pal->name); 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) static inline void __uwb_rc_put(struct uwb_rc *rc) { - uwb_dev_put(&rc->uwb_dev); + if (rc) + uwb_dev_put(&rc->uwb_dev); } extern int uwb_rc_reset(struct uwb_rc *rc); |