Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 12 Dec 2007 22:14:48 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 130741 for review
Message-ID:  <200712122214.lBCMEmFx073811@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=130741

Change 130741 by hselasky@hselasky_laptop001 on 2007/12/12 22:14:19

	
	This commit is related to USB device side support.
	
	FYI: The comments below follow the diff.
	
	o In general: The code does the same like before
	  only that some functions have been refactored.
	
	o Bug corrected: "usb_linux_free_usb_device" must
	  be called every time you change the configuration.
	
	o Simplified "usbd_fill_deviceinfo" a little bit.
	
	o "usbd_reset_probed": In USB Device Mode it is 
	  always the peer that sets the configuration,
	  and not the device itself. Setting "udev->probed"
	  to "USBD_PROBED_IFACE_AND_FOUND" ensures that we
	  never change the configuration value.
	
	o We now ignore any set-address errors due to 
	  buggy USB devices and rather try to see
	  if reading the first descriptor fails at
	  the new address.
	
	o Removed some comments that are no longer true.
	
	o New helper functions "usbd_bus_port_get_device" and
	"usbd_bus_port_set_device".

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/usb_subr.c#69 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/usb_subr.c#69 (text+ko) ====

@@ -744,8 +744,8 @@
 
 error:
 	/* passed end, or bad desc */
-	printf("%s: bad descriptor(s), addr=%d!\n",
-	    __FUNCTION__, udev->address);
+	PRINTFN(-1, ("%s: bad descriptor(s), addr=%d!\n",
+	    __FUNCTION__, udev->address));
 
 	/* free old pipes if any */
 	usbd_free_pipe_data(udev, iface_index, 0 - 1);
@@ -760,21 +760,28 @@
 
 	/* mtx_assert() */
 
+	/* free Linux compat device, if any */
+	if (udev->linux_dev) {
+		usb_linux_free_usb_device(udev->linux_dev);
+		udev->linux_dev = NULL;
+	}
 	/* free all pipes, if any */
 	usbd_free_pipe_data(udev, 0, 0);
 
 	/* free all interfaces, if any */
 	while (iface != iface_end) {
 		iface->idesc = NULL;
+		iface->alt_index = 0;
 		iface++;
 	}
 
-	if (udev->cdesc != NULL) {
-		/* free "cdesc" after "ifaces" */
+	/* free "cdesc" after "ifaces", if any */
+	if (udev->cdesc) {
 		free(udev->cdesc, M_USB);
+		udev->cdesc = NULL;
 	}
-	udev->cdesc = NULL;
-	udev->config = USB_UNCONFIG_NO;
+	/* set unconfigured state */
+	udev->curr_config_no = USB_UNCONFIG_NO;
 	return;
 }
 
@@ -823,6 +830,9 @@
 	return (USBD_INVAL);
 }
 
+/*------------------------------------------------------------------------*
+ *	usbd_set_config_index
+ *------------------------------------------------------------------------*/
 usbd_status_t
 usbd_set_config_index(struct usbd_device *udev, uint8_t index, uint8_t msg)
 {
@@ -942,8 +952,8 @@
 		goto error;
 	}
 	udev->power = power;
-	udev->self_powered = selfpowered;
-	udev->config = cdp->bConfigurationValue;
+	udev->flags.self_powered = selfpowered;
+	udev->curr_config_no = cdp->bConfigurationValue;
 
 	/* Set the actual configuration value. */
 	err = usbreq_set_config(udev, &usb_global_lock,
@@ -994,10 +1004,14 @@
 int
 usbd_fill_deviceinfo(struct usbd_device *udev, struct usb_device_info *di)
 {
+	enum {
+		MAX_PORT = (sizeof(di->udi_ports) / sizeof(di->udi_ports[0])),
+	};
 	struct usbd_port *p;
-	uint16_t s;
+	struct usbd_interface *iface;
+	struct usbd_device *child;
 	uint8_t i;
-	uint8_t err;
+	uint8_t max;
 
 	if ((udev == NULL) || (di == NULL)) {
 		return (ENXIO);
@@ -1020,111 +1034,241 @@
 	di->udi_class = udev->ddesc.bDeviceClass;
 	di->udi_subclass = udev->ddesc.bDeviceSubClass;
 	di->udi_protocol = udev->ddesc.bDeviceProtocol;
-	di->udi_config = udev->config;
-	di->udi_power = udev->self_powered ? 0 : udev->power;
+	di->udi_config = udev->curr_config_no;
+	di->udi_power = udev->flags.self_powered ? 0 : udev->power;
 	di->udi_speed = udev->speed;
 
-	for (i = 0;
-	    (i < (sizeof(udev->subdevs) / sizeof(udev->subdevs[0]))) &&
-	    (i < USB_MAX_DEVNAMES);
-	    i++) {
-		if (udev->subdevs[i] &&
-		    device_is_attached(udev->subdevs[i])) {
+	for (i = 0; i != MIN(USB_MAX_DEVNAMES, USB_MAX_INTERFACES); i++) {
+		iface = usbd_get_iface(udev, i);
+		if (iface && iface->subdev &&
+		    device_is_attached(iface->subdev)) {
 			strlcpy(di->udi_devnames[i],
-			    device_get_nameunit(udev->subdevs[i]),
+			    device_get_nameunit(iface->subdev),
 			    USB_MAX_DEVNAMELEN);
 		}
 	}
 
 	if (udev->hub) {
-		for (i = 0;
-		    (i < (sizeof(di->udi_ports) / sizeof(di->udi_ports[0]))) &&
-		    (i < udev->hub->hubdesc.bNbrPorts);
-		    i++) {
-			p = &udev->hub->ports[i];
+
+		max = udev->hub->nports;
+		if (max > MAX_PORT) {
+			max = MAX_PORT;
+		}
+		di->udi_nports = max;
+
+		p = udev->hub->ports;
+		for (i = 0; i != max; i++, p++) {
+
+			child = usbd_bus_port_get_device(udev->bus, p);
 
-			if (p->device_addr != USB_START_ADDR) {
-				err = p->device_addr;
+			if (child) {
+				di->udi_ports[i] = p->device_index;
 			} else {
-				s = UGETW(p->status.wPortStatus);
-				if (s & UPS_PORT_ENABLED) {
-					err = USB_PORT_ENABLED;
-				} else if (s & UPS_SUSPEND) {
-					err = USB_PORT_SUSPENDED;
-				} else if (s & UPS_PORT_POWER) {
-					err = USB_PORT_POWERED;
-				} else {
-					err = USB_PORT_DISABLED;
-				}
+				di->udi_ports[i] = USB_PORT_POWERED;
 			}
-			di->udi_ports[i] = err;
 		}
-		di->udi_nports = udev->hub->hubdesc.bNbrPorts;
 	}
 	mtx_unlock(&usb_global_lock);
 	return (0);
 }
 
-/* The following function will remove detached
- * devices from the interface list. This can
- * happen during USB device module unload.
- */
+static void
+usbd_reset_probed(struct usbd_device *udev)
+{
+	udev->probed = (udev->flags.usb_mode == USB_MODE_HOST) ?
+	USBD_PROBED_NOTHING : USBD_PROBED_IFACE_AND_FOUND;
+	return;
+}
+
 static void
-usbd_remove_detached_devices(struct usbd_device *udev)
+usbd_detach_device_sub(struct usbd_device *udev, device_t *ppdev,
+    uint8_t free_subdev)
 {
-	device_t *subdev = udev->subdevs;
-	device_t *subdev_end = udev->subdevs_end;
-	uint8_t detached_first = 0;
+	device_t dev;
+	int err;
+
+	if (!free_subdev) {
+
+		*ppdev = NULL;
+
+	} else if (*ppdev) {
+
+		/*
+		 * NOTE: It is important to clear "*ppdev" before deleting
+		 * the child due to some device methods being called late
+		 * during the delete process !
+		 */
+		dev = *ppdev;
+		*ppdev = NULL;
 
-	PRINTFN(3, ("udev=%p\n", udev));
+		device_printf(dev, "at %s, port %d, addr %d "
+		    "(disconnected)\n",
+		    device_get_nameunit(udev->parent_dev),
+		    udev->port_no, udev->address);
 
-	while (subdev != subdev_end) {
-		if (subdev[0]) {
-			if (device_is_attached(subdev[0]) == 0) {
-				if (device_delete_child(device_get_parent(subdev[0]),
-				    subdev[0]) == 0) {
-					subdev[0] = NULL;
-					if (subdev == udev->subdevs) {
-						detached_first = 1;
-					}
-				} else {
-					/*
-					 * Panic here, else one can get a
-					 * double call to device_detach().
-					 * USB devices should never fail on
-					 * detach!
-					 */
-					panic("device_delete_child() failed!\n");
+		if (device_is_attached(dev)) {
+			if (udev->flags.suspended) {
+				err = DEVICE_RESUME(dev);
+				if (err) {
+					device_printf(dev, "Resume failed!\n");
 				}
 			}
+			if (device_detach(dev)) {
+				goto error;
+			}
+		}
+		if (device_delete_child(udev->parent_dev, dev)) {
+			goto error;
+		}
+	}
+	return;
+
+error:
+	/* Detach is not allowed to fail in the USB world */
+	panic("An USB driver would not detach!\n");
+	return;
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_detach_device
+ *
+ * The following function will detach the matching interfaces.
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usbd_detach_device(struct usbd_device *udev, uint8_t iface_index,
+    uint8_t free_subdev)
+{
+	struct usbd_interface *iface;
+	uint8_t i;
+
+	if (udev == NULL) {
+		/* nothing to do */
+		return;
+	}
+	PRINTFN(3, ("udev=%p\n", udev));
+
+	/*
+	 * First detach the child to give the child's detach routine a
+	 * chance to detach the sub-devices in the correct order.
+	 * Then delete the child using "device_delete_child()" which
+	 * will detach all sub-devices from the bottom and upwards!
+	 */
+	if (iface_index != USB_IFACE_INDEX_ANY) {
+		i = iface_index;
+		iface_index = i + 1;
+	} else {
+		usbd_detach_device_sub(udev, &(udev->global_dev), free_subdev);
+		i = 0;
+		iface_index = USB_MAX_INTERFACES;
+	}
+
+	/* do the detach */
+
+	for (; i != iface_index; i++) {
+
+		iface = usbd_get_iface(udev, i);
+		if (iface == NULL) {
+			/* looks like the end of the USB interfaces */
+			break;
 		}
-		subdev++;
+		usbd_detach_device_sub(udev, &(iface->subdev), free_subdev);
+	}
+
+	if (iface_index == USB_IFACE_INDEX_ANY) {
+		/*
+		 * All devices are gone. Reset the "probed" variable.
+		 */
+		usbd_reset_probed(udev);
 	}
+	return;
+}
 
-	if (detached_first) {
-		if ((udev->probed == USBD_PROBED_SPECIFIC_AND_FOUND) ||
-		    (udev->probed == USBD_PROBED_GENERIC_AND_FOUND)) {
+/*------------------------------------------------------------------------*
+ *	usbd_probe_and_attach_sub
+ *
+ * Returns:
+ *    0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+usbd_probe_and_attach_sub(struct usbd_device *udev,
+    struct usb_attach_arg *uaa, device_t *ppdev)
+{
+	device_t dev;
+	int err;
+
+	dev = *ppdev;
+
+	if (dev) {
+
+		/* clean up after module unload */
+
+		if (device_is_attached(dev)) {
+			/* already a device there */
+			return (0);
+		}
+		/* XXX clear "*ppdev" as early as possible */
+
+		*ppdev = NULL;
+
+		if (device_delete_child(udev->parent_dev, dev)) {
+
 			/*
-			 * The first and only device is gone. Reset the
-			 * "probed" variable.
+			 * Panic here, else one can get a double call
+			 * to device_detach().  USB devices should
+			 * never fail on detach!
 			 */
-			udev->probed = USBD_PROBED_NOTHING;
+			panic("device_delete_child() failed!\n");
+		}
+	}
+	if (uaa->temp_dev == NULL) {
+
+		/* create a new child */
+		uaa->temp_dev = device_add_child(udev->parent_dev, NULL, -1);
+		if (uaa->temp_dev == NULL) {
+			device_printf(udev->parent_dev,
+			    "Device creation failed!\n");
+			return (1);	/* failure */
+		}
+		device_set_ivars(uaa->temp_dev, uaa);
+		device_quiet(uaa->temp_dev);
+	}
+	if (device_probe_and_attach(uaa->temp_dev) == 0) {
+		/*
+		 * The USB attach arguments are only available during probe
+		 * and attach !
+		 */
+		*ppdev = uaa->temp_dev;
+		uaa->temp_dev = NULL;
+		device_set_ivars(*ppdev, NULL);
+
+		if (udev->flags.suspended) {
+			err = DEVICE_SUSPEND(*ppdev);
+			device_printf(*ppdev, "Suspend failed\n");
 		}
+		return (0);		/* success */
 	}
-	return;
+	return (1);			/* failure */
 }
 
 /*------------------------------------------------------------------------*
  *	usbd_probe_and_attach
  *
- * This function is called from "uhub_explore_sub()"
+ * This function is called from "uhub_explore_sub()" and
+ * "usbd_serve_request_callback_sub()"
+ *
+ * Returns:
+ *    0: Success
+ * Else: A control transfer failed
  *------------------------------------------------------------------------*/
 usbd_status_t
-usbd_probe_and_attach(device_t parent, struct usbd_device *udev)
+usbd_probe_and_attach(struct usbd_device *udev, uint8_t iface_index)
 {
 	struct usb_attach_arg uaa;
-	device_t bdev = NULL;
-	usbd_status_t err = 0;
+	struct usbd_interface *iface;
+	usbd_status_t err;
+	uint8_t nconfig;
 	uint8_t config;
 	uint8_t i;
 
@@ -1132,143 +1276,123 @@
 		PRINTF(("udev == NULL\n"));
 		return (USBD_INVAL);
 	}
-	usbd_remove_detached_devices(udev);
+	if (udev->flags.usb_mode == USB_MODE_DEVICE) {
+		if (udev->curr_config_no == USB_UNCONFIG_NO) {
+			/* do nothing - no configuration has been set */
+			return (0);
+		}
+	}
+	if (udev->probed == USBD_PROBED_SPECIFIC_AND_FOUND) {
+		if ((udev->global_dev == NULL) ||
+		    (!device_is_attached(udev->global_dev))) {
+			/* reset */
+			udev->probed = USBD_PROBED_NOTHING;
+		}
+	}
+	err = 0;
+	config = 0;
 
 	bzero(&uaa, sizeof(uaa));
 
 	/* probe and attach */
 
 	uaa.device = udev;
+	uaa.usb_mode = udev->flags.usb_mode;
 	uaa.port = udev->port_no;
 	uaa.configno = -1;
 	uaa.vendor = UGETW(udev->ddesc.idVendor);
 	uaa.product = UGETW(udev->ddesc.idProduct);
 	uaa.release = UGETW(udev->ddesc.bcdDevice);
 
-	if ((udev->probed == USBD_PROBED_SPECIFIC_AND_FOUND) ||
-	    (udev->probed == USBD_PROBED_GENERIC_AND_FOUND)) {
-		/* nothing more to probe */
-		goto done;
-	}
-	bdev = device_add_child(parent, NULL, -1);
-	if (!bdev) {
-		device_printf(udev->bus->bdev,
-		    "Device creation failed\n");
-		err = USBD_INVAL;
-		goto done;
-	}
-	device_set_ivars(bdev, &uaa);
-	device_quiet(bdev);
-
+	/* first try device specific drivers */
 	if (udev->probed == USBD_PROBED_NOTHING) {
-		/* first try device specific drivers */
 		PRINTF(("trying device specific drivers\n"));
 
-		if (device_probe_and_attach(bdev) == 0) {
-			device_set_ivars(bdev, NULL);	/* no longer accessible */
-			udev->subdevs[0] = bdev;
+		if (!usbd_probe_and_attach_sub(
+		    udev, &uaa, &(udev->global_dev))) {
 			udev->probed = USBD_PROBED_SPECIFIC_AND_FOUND;
-			bdev = 0;
 			goto done;
 		}
 		PRINTF(("no device specific driver found; "
 		    "looping over %d configurations\n",
 		    udev->ddesc.bNumConfigurations));
 	}
-	/* next try interface drivers */
+	/* next try the USB interface drivers */
+
+	nconfig = udev->ddesc.bNumConfigurations;
+
+	for (config = 0; config != nconfig; config++) {
 
-	if ((udev->probed == USBD_PROBED_NOTHING) ||
-	    (udev->probed == USBD_PROBED_IFACE_AND_FOUND)) {
-		for (config = 0; config < udev->ddesc.bNumConfigurations; config++) {
-			struct usbd_interface *iface;
+		/*
+		 * Only set the config index the first time the
+		 * devices are probed !
+		 */
+		if (udev->probed == USBD_PROBED_NOTHING) {
 
-			/*
-			 * only set config index the first time the devices
-			 * are probed
-			 */
-			if (udev->probed == USBD_PROBED_NOTHING) {
-				err = usbd_set_config_index(udev, config, 1);
-				if (err) {
-					device_printf(parent,
-					    "port %d, set config at addr %d "
-					    "failed, error=%s\n",
-					    udev->port_no, udev->address,
-					    usbd_errstr(err));
-					goto done;
-				}
-				/*
-				 * ``bNumInterface'' is checked by
-				 * ``usbd_set_config_index()''
-				 *
-				 * ``USBD_CLR_IFACE_NO_PROBE()'' is run by
-				 * ``usbd_fill_iface_data()'', which is
-				 * called by ``usbd_set_config_index()''
-				 */
+			err = usbd_set_config_index(udev, config, 1);
+			if (err) {
+				goto done;
 			}
-			/*
-			 * else the configuration is already set
-			 */
+		}
+		/*
+		 * else the configuration is already set
+		 */
+
+		if ((udev->probed == USBD_PROBED_NOTHING) ||
+		    (udev->probed == USBD_PROBED_IFACE_AND_FOUND)) {
 
 			uaa.configno = udev->cdesc->bConfigurationValue;
-			uaa.ifaces_start = udev->ifaces;
-			uaa.ifaces_end = udev->ifaces + udev->cdesc->bNumInterface;
+
+			/* check if only one interface should be probed */
+
+			if (iface_index != USB_IFACE_INDEX_ANY) {
+				i = iface_index;
+				iface_index = i + 1;
+			} else {
+				i = 0;
+				iface_index = USB_MAX_INTERFACES;
+			}
 
-			for (iface = uaa.ifaces_start;
-			    iface != uaa.ifaces_end;
-			    iface++) {
-				uaa.iface = iface;
-				uaa.iface_index = (i = (iface - udev->ifaces));
+			/* do the probe and attach */
 
-				if (uaa.iface_index >= (sizeof(udev->subdevs) /
-				    sizeof(udev->subdevs[0]))) {
-					device_printf(udev->bus->bdev,
-					    "Too many subdevices\n");
-					break;
-				}
-				if ((USBD_GET_IFACE_NO_PROBE(udev, i) == 0) &&
-				    (udev->subdevs[i] == NULL) &&
-				    (device_probe_and_attach(bdev) == 0)) {
-					/* "ivars" are no longer accessible: */
-					device_set_ivars(bdev, NULL);
-					udev->subdevs[i] = bdev;
-					udev->probed = USBD_PROBED_IFACE_AND_FOUND;
-					bdev = 0;
+			for (; i != iface_index; i++) {
 
+				iface = usbd_get_iface(udev, i);
+				if (iface == NULL) {
 					/*
-					 * create another child for the next
-					 * iface [if any]
+					 * Looks like the end of the USB
+					 * interfaces !
 					 */
-					bdev = device_add_child(parent, NULL, -1);
-					if (!bdev) {
-						device_printf(udev->bus->bdev,
-						    "Device creation failed\n");
+					PRINTFN(1, ("end of interfaces "
+					    "at %u\n", i));
+					break;
+				}
+				if (USBD_GET_IFACE_NO_PROBE(udev, i)) {
+					/* somebody grabbed the interface */
+					PRINTFN(1, ("no probe %d\n", i));
+					continue;
+				}
+				uaa.iface_index = i;
+				uaa.iface = iface;
 
-						/*
-						 * need to update
-						 * "IFACE_NO_PROBE":
-						 */
-						break;
-					}
-					device_set_ivars(bdev, &uaa);
-					device_quiet(bdev);
+				if (!usbd_probe_and_attach_sub(
+				    udev, &uaa, &(iface->subdev))) {
+					udev->probed = USBD_PROBED_IFACE_AND_FOUND;
 				}
 			}
-
-			if (udev->probed == USBD_PROBED_IFACE_AND_FOUND) {
-				break;
-			}
+		}
+		if (udev->probed != USBD_PROBED_NOTHING) {
+			/* nothing more to do */
+			break;
 		}
 	}
+
 	if (udev->probed == USBD_PROBED_NOTHING) {
 		/* set config index 0 */
 
+		config = 0;
 		err = usbd_set_config_index(udev, 0, 1);
 		if (err) {
-			device_printf(parent,
-			    "port %d, set config at addr %d "
-			    "failed, error=%s\n",
-			    udev->port_no, udev->address,
-			    usbd_errstr(err));
 			goto done;
 		}
 		PRINTF(("no interface drivers found\n"));
@@ -1276,16 +1400,12 @@
 		/* finally try the generic driver */
 		uaa.iface = NULL;
 		uaa.iface_index = 0;
-		uaa.ifaces_start = NULL;
-		uaa.ifaces_end = NULL;
 		uaa.usegeneric = 1;
 		uaa.configno = -1;
 
-		if (device_probe_and_attach(bdev) == 0) {
-			device_set_ivars(bdev, NULL);	/* no longer accessible */
-			udev->subdevs[0] = bdev;
-			udev->probed = USBD_PROBED_GENERIC_AND_FOUND;
-			bdev = 0;
+		if (!usbd_probe_and_attach_sub(
+		    udev, &uaa, &(udev->global_dev))) {
+			udev->probed = USBD_PROBED_SPECIFIC_AND_FOUND;
 			goto done;
 		}
 		/*
@@ -1296,13 +1416,90 @@
 		PRINTF(("generic attach failed\n"));
 	}
 done:
-	if (bdev) {
+	if (err) {
+		device_printf(udev->parent_dev,
+		    "port %d, set config %d at addr %d "
+		    "failed, error=%s\n",
+		    udev->port_no, config, udev->address,
+		    usbd_errstr(err));
+	}
+	if (uaa.temp_dev) {
 		/* remove the last created child; it is unused */
-		device_delete_child(parent, bdev);
+
+		if (device_delete_child(udev->parent_dev, uaa.temp_dev)) {
+			PRINTFN(-1, ("device delete child failed!\n"));
+		}
 	}
 	return (err);
 }
 
+/*------------------------------------------------------------------------*
+ *	usbd_suspend_resume_sub
+ *------------------------------------------------------------------------*/
+static void
+usbd_suspend_resume_sub(struct usbd_device *udev, device_t dev, uint8_t do_suspend)
+{
+	int err;
+
+	if (dev == NULL) {
+		return;
+	}
+	if (do_suspend) {
+		err = DEVICE_SUSPEND(dev);
+	} else {
+		err = DEVICE_RESUME(dev);
+	}
+	if (err) {
+		device_printf(dev, "%s failed!\n",
+		    do_suspend ? "Suspend" : "Resume");
+	}
+	return;
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_suspend_resume_device
+ *
+ * The following function will suspend or resume the USB device.
+ *------------------------------------------------------------------------*/
+usbd_status_t
+usbd_suspend_resume(struct usbd_device *udev, uint8_t do_suspend)
+{
+	struct usbd_interface *iface;
+	uint8_t i;
+
+	if (udev == NULL) {
+		/* nothing to do */
+		return (0);
+	}
+	PRINTFN(3, ("udev=%p\n", udev));
+
+	mtx_lock(&(udev->bus->mtx));
+	if (udev->flags.suspended == do_suspend) {
+		mtx_unlock(&(udev->bus->mtx));
+		/* nothing to do */
+		return (0);
+	}
+	udev->flags.suspended = do_suspend;
+	mtx_unlock(&(udev->bus->mtx));
+
+	/* do the global_dev first, if any */
+
+	usbd_suspend_resume_sub(udev, udev->global_dev, do_suspend);
+
+	/* do the suspend or resume */
+
+	for (i = 0; i != USB_MAX_INTERFACES; i++) {
+
+		iface = usbd_get_iface(udev, i);
+		if (iface == NULL) {
+			/* looks like the end of the USB interfaces */
+			break;
+		}
+		usbd_suspend_resume_sub(udev, iface->subdev, do_suspend);
+	}
+	return (0);
+}
+
 static const uint8_t
 	usbd_hub_speed_combs[USB_SPEED_MAX][USB_SPEED_MAX] = {
 	/* HUB *//* subdevice */
@@ -1315,48 +1512,53 @@
 };
 
 /*------------------------------------------------------------------------*
- *	usbd_new_device
+ *	usbd_alloc_device
+ *
+ * This function allocates a new USB device. This function is called
+ * when a new device has been put in the powered state, but not yet in
+ * the addressed state. Get initial descriptor, set the address, get
+ * full descriptor and get strings.
  *
- * Called when a new device has been put in the powered state,
- * but not yet in the addressed state.
- * Get initial descriptor, set the address, get full descriptor,
- * and attach a driver.
+ * Return values:
+ *    0: Failure
+ * Else: Success
  *------------------------------------------------------------------------*/
-usbd_status_t
-usbd_new_device(device_t parent, struct usbd_bus *bus,
+struct usbd_device *
+usbd_alloc_device(device_t parent_dev, struct usbd_bus *bus,
     struct usbd_device *parent_hub, uint8_t depth,
-    uint8_t speed, uint8_t port_index, uint8_t port_no)
+    uint8_t port_index, uint8_t port_no, uint8_t speed, uint8_t usb_mode)
 {
+	struct usbd_device *udev;
 	struct usbd_device *adev;
-	struct usbd_device *udev;
 	struct usbd_device *hub;
-	usbd_status_t err = 0;
-	uint8_t buf[4];
-	uint8_t addr;
+	usbd_status_t err;
+	uint8_t device_index;
 
-	PRINTF(("bus=%p port=%d depth=%d speed=%d\n",
-	    bus, port_no, depth, speed));
+	PRINTFN(0, ("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, "
+	    "port_index=%u, port_no=%u, speed=%u, usb_mode=%u\n",
+	    parent_dev, bus, parent_hub, depth, port_index, port_no,
+	    speed, usb_mode));
 
-	/* find an unused and valid address */
-
-#if (USB_MAX_DEVICES < USB_START_ADDR)
-#error "USB_MAX_DEVICES < USB_START_ADDR"
-#endif
-	for (addr = USB_START_ADDR + 1; addr < USB_MAX_DEVICES; addr++) {
-		if (bus->devices[addr] == NULL)
+	/*
+	 * Find an unused device index. In USB Host mode this is the
+	 * same as the device address.
+	 *
+	 * NOTE: Index 1 is reserved for the Root HUB.
+	 */
+	for (device_index = USB_ROOT_HUB_ADDR; device_index !=
+	    USB_MAX_DEVICES; device_index++) {
+		if (bus->devices[device_index] == NULL)
 			break;
 	}
 
-	if (addr == USB_MAX_DEVICES) {
-
+	if (device_index == USB_MAX_DEVICES) {
 		device_printf(bus->bdev,
-		    "No free USB addresses, "
-		    "new device ignored.\n");
-		return (USBD_NO_ADDR);
+		    "No free USB device index for new device!\n");
+		return (NULL);
 	}
-	udev = malloc(sizeof(udev[0]), M_USB, M_WAITOK | M_ZERO);
+	udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO);
 	if (udev == NULL) {
-		return (USBD_NOMEM);
+		return (NULL);
 	}
 	/* initialize our SX-lock */
 	sx_init(udev->default_sx, "USB device SX lock");
@@ -1364,10 +1566,23 @@
 	/* initialize our mutex */
 	mtx_init(udev->default_mtx, "USB device mutex", NULL, MTX_DEF);
 
-	/* make a relationship between port and device address */
-	if (parent_hub) {
-		parent_hub->hub->ports[port_index].device_addr = addr;
-	}
+	/* initialize some USB device fields */
+	udev->parent_hub = parent_hub;
+	udev->parent_dev = parent_dev;
+	udev->port_index = port_index;
+	udev->port_no = port_no;
+	udev->depth = depth;
+	udev->bus = bus;
+	udev->address = USB_START_ADDR;	/* default value */
+
+	/* we are not ready yet */
+	udev->flags.detaching = 1;
+	udev->refcount = 1;
+
+	/* register our device */
+	usbd_bus_port_set_device(bus, parent_hub ?
+	    parent_hub->hub->ports + port_index : NULL, udev, device_index);
+
 	/* set up default endpoint descriptor */
 	udev->default_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE;
 	udev->default_ep_desc.bDescriptorType = UDESC_ENDPOINT;
@@ -1375,19 +1590,20 @@
 	udev->default_ep_desc.bmAttributes = UE_CONTROL;
 	udev->default_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET;
 	udev->default_ep_desc.wMaxPacketSize[1] = 0;
-	udev->ddesc.bMaxPacketSize = 0;
 	udev->default_ep_desc.bInterval = 0;
+	udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
 
-	udev->parent_hub = parent_hub;
-	udev->port_index = port_index;
-	udev->port_no = port_no;
-	udev->bus = bus;
 	udev->quirks = &usbd_no_quirk;
-	udev->address = USB_START_ADDR;
-	udev->depth = depth;
+	udev->speed = speed;
+	udev->flags.usb_mode = usb_mode;
+
+	/* setup probed variable */
+
+	usbd_reset_probed(udev);
 
-	hub = parent_hub;
+	/* check speed combination */
 
+	hub = udev->parent_hub;
 	if (hub) {
 		if (usbd_hub_speed_combs[hub->speed][speed] == 0) {
 #ifdef USB_DEBUG
@@ -1400,8 +1616,10 @@
 			goto done;
 		}
 	}
+	/* search for our High Speed USB HUB, if any */
+
 	adev = udev;
-	hub = parent_hub;
+	hub = udev->parent_hub;
 
 	while (hub) {
 		if (hub->speed == USB_SPEED_HIGH) {
@@ -1413,38 +1631,50 @@
 		hub = hub->parent_hub;
 	}
 
-	udev->speed = speed;
-
 	/* init the default pipe */
 	usbd_fill_pipe_data(udev, 0,
 	    &udev->default_ep_desc,
 	    &udev->default_pipe);
 
-	err = usbreq_set_address(udev, &usb_global_lock, addr);
-	if (err) {
-		PRINTF(("set address %d failed\n", addr));
-		err = USBD_SET_ADDR_FAILED;
-		goto done;
-	}
-	/* allow device time to set new address */
-	usbd_delay_ms(udev, USB_SET_ADDRESS_SETTLE);
-	udev->address = addr;		/* new device address now */
+	if (udev->flags.usb_mode == USB_MODE_HOST) {
+
+		err = usbreq_set_address(udev, &usb_global_lock, device_index);
+
+		/* This is the new USB device address from now on */
+
+		udev->address = device_index;
 
-	mtx_lock(&(bus->mtx));
-	bus->devices[addr] = udev;
-	if (parent_hub == NULL) {
-		/* make a copy */
-		bus->devices[USB_START_ADDR] = udev;
+		/*
+		 * We ignore any set-address errors, hence there are
+		 * buggy USB devices out there that actually receive
+		 * the SETUP PID, but manage to set the address before
+		 * the STATUS stage is ACK'ed. If the device responds
+		 * to the subsequent get-descriptor at the new
+		 * address, then we know that the set-address command
+		 * was successful.
+		 */
+		if (err) {
+			PRINTFN(-1, ("set address %d failed "
+			    "(ignored)\n", udev->address));
+		}
+		/* allow device time to set new address */
+		usbd_delay_ms(udev, USB_SET_ADDRESS_SETTLE);
 	}
-	mtx_unlock(&(bus->mtx));
-
-	/* get the first 8 bytes of the device descriptor */
+	/*
+	 * Get the first 8 bytes of the device descriptor !
+	 *
+	 * NOTE: "usbd_do_request" will check the device descriptor
+	 * next time we do a request to see if the maximum packet size
+	 * changed! The 8 first bytes of the device descriptor
+	 * contains the maximum packet size to use on control endpoint
+	 * 0. If this value is different from "USB_MAX_IPACKET" a new
+	 * USB control request will be setup!
+	 */
 	err = usbreq_get_desc(udev, &usb_global_lock, &udev->ddesc,
-	    USB_MAX_IPACKET, USB_MAX_IPACKET,
-	    0, UDESC_DEVICE, 0, 0);
+	    USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
 	if (err) {
-		PRINTF(("addr=%d, getting first desc failed\n",
-		    udev->address));
+		PRINTFN(-1, ("getting device descriptor "
+		    "at addr %d failed!\n", udev->address));
 		goto done;
 	}
 	PRINTF(("adding unit addr=%d, rev=%02x, class=%d, "
@@ -1487,23 +1717,24 @@
 	if (udev->ddesc.iManufacturer ||
 	    udev->ddesc.iProduct ||
 	    udev->ddesc.iSerialNumber) {
-		/* setup language ID */
-		err = usbreq_get_string_desc(udev, &usb_global_lock, buf, 4,
-		    0, USB_LANGUAGE_TABLE);
+		/* read out the language ID string */
+		err = usbreq_get_string_desc(udev, &usb_global_lock,
+		    udev->scratch[0].data, 4, sizeof(udev->scratch),
+		    USB_LANGUAGE_TABLE);
 	} else {
 		err = USBD_INVAL;
 	}

>>> TRUNCATED FOR MAIL (1000 lines) <<<



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200712122214.lBCMEmFx073811>