svn commit: r213432 - head/sys/dev/usb

Hans Petter Selasky hselasky at FreeBSD.org
Mon Oct 4 22:21:30 UTC 2010


Author: hselasky
Date: Mon Oct  4 22:21:30 2010
New Revision: 213432
URL: http://svn.freebsd.org/changeset/base/213432

Log:
  Serialise USB re-enumeration with the USB explore thread.
  This patch can solve problems when multiple USB devices are
  re-enumerated at the same time on the same bus.
  
  Approved by:    thompsa (mentor)

Modified:
  head/sys/dev/usb/usb_dev.c
  head/sys/dev/usb/usb_device.h
  head/sys/dev/usb/usb_generic.c
  head/sys/dev/usb/usb_hub.c

Modified: head/sys/dev/usb/usb_dev.c
==============================================================================
--- head/sys/dev/usb/usb_dev.c	Mon Oct  4 22:04:22 2010	(r213431)
+++ head/sys/dev/usb/usb_dev.c	Mon Oct  4 22:21:30 2010	(r213432)
@@ -964,7 +964,6 @@ usb_dev_uninit(void *arg)
 	if (usb_dev != NULL) {
 		destroy_dev(usb_dev);
 		usb_dev = NULL;
-	
 	}
 	mtx_destroy(&usb_ref_lock);
 	sx_destroy(&usb_sym_lock);
@@ -1058,21 +1057,45 @@ usb_ioctl(struct cdev *dev, u_long cmd, 
 		err = usb_ioctl_f_sub(f, cmd, addr, td);
 	}
 	KASSERT(f != NULL, ("fifo not found"));
-	if (err == ENOIOCTL) {
-		err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
-		DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
-		if (err == ENOIOCTL) {
-			if (usb_usb_ref_device(cpd, &refs)) {
-				err = ENXIO;
-				goto done;
-			}
-			err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
-			DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
-		}
+	if (err != ENOIOCTL)
+		goto done;
+
+	err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
+
+	DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
+
+	if (err != ENOIOCTL)
+		goto done;
+
+	if (usb_usb_ref_device(cpd, &refs)) {
+		err = ENXIO;
+		goto done;
 	}
-	if (err == ENOIOCTL) {
+
+	err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
+
+	DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
+
+	if (err == ENOIOCTL)
 		err = ENOTTY;
+
+	if (err)
+		goto done;
+
+	/* Wait for re-enumeration, if any */
+
+	while (f->udev->re_enumerate_wait != 0) {
+
+		usb_unref_device(cpd, &refs);
+
+		usb_pause_mtx(NULL, hz / 128);
+
+		if (usb_ref_device(cpd, &refs, 1 /* need uref */)) {
+			err = ENXIO;
+			goto done;
+		}
 	}
+
 done:
 	usb_unref_device(cpd, &refs);
 	return (err);

Modified: head/sys/dev/usb/usb_device.h
==============================================================================
--- head/sys/dev/usb/usb_device.h	Mon Oct  4 22:04:22 2010	(r213431)
+++ head/sys/dev/usb/usb_device.h	Mon Oct  4 22:21:30 2010	(r213432)
@@ -160,6 +160,7 @@ struct usb_device {
 	uint8_t	hs_port_no;		/* high-speed HUB port number */
 	uint8_t	driver_added_refcount;	/* our driver added generation count */
 	uint8_t	power_mode;		/* see USB_POWER_XXX */
+	uint8_t re_enumerate_wait;	/* set if re-enum. is in progress */
 	uint8_t ifaces_max;		/* number of interfaces present */
 	uint8_t endpoints_max;		/* number of endpoints present */
 

Modified: head/sys/dev/usb/usb_generic.c
==============================================================================
--- head/sys/dev/usb/usb_generic.c	Mon Oct  4 22:04:22 2010	(r213431)
+++ head/sys/dev/usb/usb_generic.c	Mon Oct  4 22:21:30 2010	(r213432)
@@ -109,7 +109,7 @@ static int	usb_gen_fill_deviceinfo(struc
 static int	ugen_re_enumerate(struct usb_fifo *);
 static int	ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int);
 static uint8_t	ugen_fs_get_complete(struct usb_fifo *, uint8_t *);
-static int ugen_fs_uninit(struct usb_fifo *f);
+static int	ugen_fs_uninit(struct usb_fifo *f);
 
 /* structures */
 
@@ -951,23 +951,19 @@ ugen_re_enumerate(struct usb_fifo *f)
 	if (error) {
 		return (error);
 	}
-	/* get the device unconfigured */
-	error = ugen_set_config(f, USB_UNCONFIG_INDEX);
-	if (error) {
-		return (error);
+	if (udev->flags.usb_mode != USB_MODE_HOST) {
+		/* not possible in device side mode */
+		return (ENOTTY);
 	}
-	/* do a bus-reset */
-	mtx_lock(f->priv_mtx);
-	error = usbd_req_re_enumerate(udev, f->priv_mtx);
-	mtx_unlock(f->priv_mtx);
-
-	if (error) {
-		return (ENXIO);
+	/* make sure all FIFO's are gone */
+	/* else there can be a deadlock */
+	if (ugen_fs_uninit(f)) {
+		/* ignore any errors */
+		DPRINTFN(6, "no FIFOs\n");
 	}
-	/* restore configuration to index 0 */
-	error = ugen_set_config(f, 0);
-	if (error) {
-		return (error);
+	if (udev->re_enumerate_wait == 0) {
+		udev->re_enumerate_wait = 1;
+		usb_needs_explore(udev->bus, 0);
 	}
 	return (0);
 }
@@ -1775,9 +1771,11 @@ ugen_set_power_mode(struct usb_fifo *f, 
 
 	/* if we are powered off we need to re-enumerate first */
 	if (old_mode == USB_POWER_MODE_OFF) {
-		err = ugen_re_enumerate(f);
-		if (err)
-			return (err);
+		if (udev->flags.usb_mode == USB_MODE_HOST) {
+			if (udev->re_enumerate_wait == 0)
+				udev->re_enumerate_wait = 1;
+		}
+		/* set power mode will wake up the explore thread */
 	}
 
 	/* set new power mode */

Modified: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c	Mon Oct  4 22:04:22 2010	(r213431)
+++ head/sys/dev/usb/usb_hub.c	Mon Oct  4 22:21:30 2010	(r213432)
@@ -236,6 +236,26 @@ uhub_explore_sub(struct uhub_softc *sc, 
 		/* nothing to do */
 		goto done;
 	}
+	/* check if device should be re-enumerated */
+
+	if (child->flags.usb_mode == USB_MODE_HOST) {
+		usbd_enum_lock(child);
+		if (child->re_enumerate_wait) {
+			err = usbd_set_config_index(child, USB_UNCONFIG_INDEX);
+			if (err == 0)
+				err = usbd_req_re_enumerate(child, NULL);
+			if (err == 0)
+				err = usbd_set_config_index(child, 0);
+			if (err == 0) {
+				err = usb_probe_and_attach(child,
+				    USB_IFACE_INDEX_ANY);
+			}
+			child->re_enumerate_wait = 0;
+			err = 0;
+		}
+		usbd_enum_unlock(child);
+	}
+
 	/* check if probe and attach should be done */
 
 	if (child->driver_added_refcount != refcount) {
@@ -1763,6 +1783,8 @@ static uint8_t
 usb_peer_should_wakeup(struct usb_device *udev)
 {
 	return ((udev->power_mode == USB_POWER_MODE_ON) ||
+	    (udev->driver_added_refcount != udev->bus->driver_added_refcount) ||
+	    (udev->re_enumerate_wait != 0) ||
 	    (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
 	    (udev->pwr_save.write_refs != 0) ||
 	    ((udev->pwr_save.read_refs != 0) &&


More information about the svn-src-all mailing list