Commit a8995751 authored by Thomas Pugliese's avatar Thomas Pugliese Committed by Greg Kroah-Hartman
Browse files

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: default avatarThomas Pugliese <thomas.pugliese@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6e6581e0
Loading
Loading
Loading
Loading
+3 −8
Original line number Diff line number Diff line
@@ -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;
+27 −0
Original line number Diff line number Diff line
@@ -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);
+3 −2
Original line number Diff line number Diff line
@@ -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)
{
	if (wusbhc->uwb_rc)
		uwb_pal_unregister(&wusbhc->pal);
}
+3 −0
Original line number Diff line number Diff line
@@ -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;
+2 −7
Original line number Diff line number Diff line
@@ -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,6 +451,7 @@ EXPORT_SYMBOL_GPL(wusbhc_giveback_urb);
 */
void wusbhc_reset_all(struct wusbhc *wusbhc)
{
	if (wusbhc->uwb_rc)
		uwb_rc_reset_all(wusbhc->uwb_rc);
}
EXPORT_SYMBOL_GPL(wusbhc_reset_all);
Loading