USB: HWA: fix device probe failure
authorThomas Pugliese <thomas.pugliese@gmail.com>
Mon, 24 Jun 2013 19:26:35 +0000 (14:26 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 24 Jun 2013 23:20:43 +0000 (16:20 -0700)
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>
drivers/usb/host/hwa-hc.c
drivers/usb/wusbcore/mmc.c
drivers/usb/wusbcore/pal.c
drivers/usb/wusbcore/reservation.c
drivers/usb/wusbcore/wusbhc.c
drivers/uwb/pal.c
drivers/uwb/uwb-internal.h

index 4af750e..483990c 100644 (file)
@@ -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;
index 021467f..b71760c 100644 (file)
@@ -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);
index d0b172c..59e100c 100644 (file)
@@ -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);
 }
index 6f4fafd..ead79f7 100644 (file)
@@ -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;
index e712af3..742c607 100644 (file)
@@ -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);
 
index 8ee7d90..690577d 100644 (file)
@@ -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) {
index a7494bf..9a103b1 100644 (file)
@@ -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);