svn commit: r190730 - in head/sys/dev/usb: . controller

Andrew Thompson thompsa at FreeBSD.org
Sun Apr 5 11:20:03 PDT 2009


Author: thompsa
Date: Sun Apr  5 18:19:52 2009
New Revision: 190730
URL: http://svn.freebsd.org/changeset/base/190730

Log:
  MFp4 //depot/projects/usb at 159866
  
  - memory usage reduction by only allocating the required USB pipes and USB
    interfaces.
  - cleanup some USB parsing functions to be more flexible.
  
  Submitted by:	Hans Petter Selasky

Modified:
  head/sys/dev/usb/controller/usb_controller.c
  head/sys/dev/usb/usb_device.c
  head/sys/dev/usb/usb_device.h
  head/sys/dev/usb/usb_hub.c
  head/sys/dev/usb/usb_parse.c
  head/sys/dev/usb/usb_parse.h

Modified: head/sys/dev/usb/controller/usb_controller.c
==============================================================================
--- head/sys/dev/usb/controller/usb_controller.c	Sun Apr  5 18:19:42 2009	(r190729)
+++ head/sys/dev/usb/controller/usb_controller.c	Sun Apr  5 18:19:52 2009	(r190730)
@@ -254,8 +254,8 @@ usb2_bus_detach(struct usb2_proc_msg *pm
 	 * Free USB Root device, but not any sub-devices, hence they
 	 * are freed by the caller of this function:
 	 */
-	usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 0);
-	usb2_free_device(udev);
+	usb2_free_device(udev,
+	    USB_UNCFG_FLAG_FREE_EP0);
 
 	mtx_unlock(&Giant);
 	USB_BUS_LOCK(bus);

Modified: head/sys/dev/usb/usb_device.c
==============================================================================
--- head/sys/dev/usb/usb_device.c	Sun Apr  5 18:19:42 2009	(r190729)
+++ head/sys/dev/usb/usb_device.c	Sun Apr  5 18:19:52 2009	(r190730)
@@ -57,10 +57,10 @@
 
 /* function prototypes */
 
-static void	usb2_fill_pipe_data(struct usb2_device *, uint8_t,
+static void	usb2_init_pipe(struct usb2_device *, uint8_t,
 		    struct usb2_endpoint_descriptor *, struct usb2_pipe *);
-static void	usb2_free_pipe_data(struct usb2_device *, uint8_t, uint8_t);
-static void	usb2_free_iface_data(struct usb2_device *);
+static void	usb2_unconfigure(struct usb2_device *, uint8_t);
+static void	usb2_detach_device(struct usb2_device *, uint8_t, uint8_t);
 static void	usb2_detach_device_sub(struct usb2_device *, device_t *,
 		    uint8_t);
 static uint8_t	usb2_probe_and_attach_sub(struct usb2_device *,
@@ -70,11 +70,10 @@ static void	usb2_init_attach_arg(struct 
 static void	usb2_suspend_resume_sub(struct usb2_device *, device_t,
 		    uint8_t);
 static void	usb2_clear_stall_proc(struct usb2_proc_msg *_pm);
+usb2_error_t	usb2_config_parse(struct usb2_device *, uint8_t, uint8_t);
 #if USB_HAVE_STRINGS
 static void	usb2_check_strings(struct usb2_device *);
 #endif
-static usb2_error_t usb2_fill_iface_data(struct usb2_device *, uint8_t,
-		    uint8_t);
 #if USB_HAVE_UGEN
 static void	usb2_notify_addq(const char *type, struct usb2_device *);
 static void	usb2_fifo_free_wrap(struct usb2_device *, uint8_t, uint8_t);
@@ -268,30 +267,32 @@ usb2_interface_count(struct usb2_device 
 		*count = 0;
 		return (USB_ERR_NOT_CONFIGURED);
 	}
-	*count = udev->cdesc->bNumInterface;
+	*count = udev->ifaces_max;
 	return (USB_ERR_NORMAL_COMPLETION);
 }
 
 
 /*------------------------------------------------------------------------*
- *	usb2_fill_pipe_data
+ *	usb2_init_pipe
  *
  * This function will initialise the USB pipe structure pointed to by
- * the "pipe" argument.
+ * the "pipe" argument. The structure pointed to by "pipe" must be
+ * zeroed before calling this function.
  *------------------------------------------------------------------------*/
 static void
-usb2_fill_pipe_data(struct usb2_device *udev, uint8_t iface_index,
+usb2_init_pipe(struct usb2_device *udev, uint8_t iface_index,
     struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe)
 {
+	struct usb2_bus_methods *methods;
 
-	bzero(pipe, sizeof(*pipe));
+	methods = udev->bus->methods;
 
-	(udev->bus->methods->pipe_init) (udev, edesc, pipe);
+	(methods->pipe_init) (udev, edesc, pipe);
 
-	if (pipe->methods == NULL) {
-		/* the pipe is invalid: just return */
+	/* check for invalid pipe */
+	if (pipe->methods == NULL)
 		return;
-	}
+
 	/* initialise USB pipe structure */
 	pipe->edesc = edesc;
 	pipe->iface_index = iface_index;
@@ -299,40 +300,14 @@ usb2_fill_pipe_data(struct usb2_device *
 	pipe->pipe_q.command = &usb2_pipe_start;
 
 	/* clear stall, if any */
-	if (udev->bus->methods->clear_stall) {
+	if (methods->clear_stall != NULL) {
 		USB_BUS_LOCK(udev->bus);
-		(udev->bus->methods->clear_stall) (udev, pipe);
+		(methods->clear_stall) (udev, pipe);
 		USB_BUS_UNLOCK(udev->bus);
 	}
 }
 
-/*------------------------------------------------------------------------*
- *	usb2_free_pipe_data
- *
- * This function will free USB pipe data for the given interface
- * index. Hence we do not have any dynamic allocations we simply clear
- * "pipe->edesc" to indicate that the USB pipe structure can be
- * reused. The pipes belonging to the given interface should not be in
- * use when this function is called and no check is performed to
- * prevent this.
- *------------------------------------------------------------------------*/
-static void
-usb2_free_pipe_data(struct usb2_device *udev,
-    uint8_t iface_index, uint8_t iface_mask)
-{
-	struct usb2_pipe *pipe = udev->pipes;
-	struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX;
-
-	while (pipe != pipe_end) {
-		if ((pipe->iface_index & iface_mask) == iface_index) {
-			/* free pipe */
-			pipe->edesc = NULL;
-		}
-		pipe++;
-	}
-}
-
-/*------------------------------------------------------------------------*
+/*-----------------------------------------------------------------------*
  *	usb2_pipe_foreach
  *
  * This function will iterate all the USB endpoints except the control
@@ -367,123 +342,38 @@ usb2_pipe_foreach(struct usb2_device *ud
 }
 
 /*------------------------------------------------------------------------*
- *	usb2_fill_iface_data
+ *	usb2_unconfigure
+ *
+ * This function will free all USB interfaces and USB pipes belonging
+ * to an USB device.
  *
- * This function will fill in interface data and allocate USB pipes
- * for all the endpoints that belong to the given interface. This
- * function is typically called when setting the configuration or when
- * setting an alternate interface.
+ * Flag values, see "USB_UNCFG_FLAG_XXX".
  *------------------------------------------------------------------------*/
-static usb2_error_t
-usb2_fill_iface_data(struct usb2_device *udev,
-    uint8_t iface_index, uint8_t alt_index)
+static void
+usb2_unconfigure(struct usb2_device *udev, uint8_t flag)
 {
-	struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
-	struct usb2_pipe *pipe;
-	struct usb2_pipe *pipe_end;
-	struct usb2_interface_descriptor *id;
-	struct usb2_endpoint_descriptor *ed = NULL;
-	struct usb2_descriptor *desc;
-	uint8_t nendpt;
+	uint8_t do_unlock;
 
-	if (iface == NULL) {
-		return (USB_ERR_INVAL);
+	/* automatic locking */
+	if (sx_xlocked(udev->default_sx + 1)) {
+		do_unlock = 0;
+	} else {
+		do_unlock = 1;
+		sx_xlock(udev->default_sx + 1);
 	}
-	DPRINTFN(5, "iface_index=%d alt_index=%d\n",
-	    iface_index, alt_index);
-
-	sx_assert(udev->default_sx + 1, SA_LOCKED);
-
-	pipe = udev->pipes;
-	pipe_end = udev->pipes + USB_EP_MAX;
 
-	/*
-	 * Check if any USB pipes on the given USB interface are in
-	 * use:
-	 */
-	while (pipe != pipe_end) {
-		if ((pipe->edesc != NULL) &&
-		    (pipe->iface_index == iface_index) &&
-		    (pipe->refcount != 0)) {
-			return (USB_ERR_IN_USE);
-		}
-		pipe++;
-	}
+	/* detach all interface drivers */
+	usb2_detach_device(udev, USB_IFACE_INDEX_ANY, flag);
 
-	pipe = &udev->pipes[0];
+#if USB_HAVE_UGEN
+	/* free all FIFOs except control endpoint FIFOs */
+	usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, flag);
 
-	id = usb2_find_idesc(udev->cdesc, iface_index, alt_index);
-	if (id == NULL) {
-		return (USB_ERR_INVAL);
-	}
 	/*
-	 * Free old pipes after we know that an interface descriptor exists,
-	 * if any.
+	 * Free all cdev's, if any.
 	 */
-	usb2_free_pipe_data(udev, iface_index, 0 - 1);
-
-	/* Setup USB interface structure */
-	iface->idesc = id;
-	iface->alt_index = alt_index;
-	iface->parent_iface_index = USB_IFACE_INDEX_ANY;
-
-	nendpt = id->bNumEndpoints;
-	DPRINTFN(5, "found idesc nendpt=%d\n", nendpt);
-
-	desc = (void *)id;
-
-	while (nendpt--) {
-		DPRINTFN(11, "endpt=%d\n", nendpt);
-
-		while ((desc = usb2_desc_foreach(udev->cdesc, desc))) {
-			if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
-			    (desc->bLength >= sizeof(*ed))) {
-				goto found;
-			}
-			if (desc->bDescriptorType == UDESC_INTERFACE) {
-				break;
-			}
-		}
-		goto error;
-
-found:
-		ed = (void *)desc;
-
-		/* find a free pipe */
-		while (pipe != pipe_end) {
-			if (pipe->edesc == NULL) {
-				/* pipe is free */
-				usb2_fill_pipe_data(udev, iface_index, ed, pipe);
-				break;
-			}
-			pipe++;
-		}
-	}
-	return (USB_ERR_NORMAL_COMPLETION);
-
-error:
-	/* passed end, or bad desc */
-	DPRINTFN(0, "%s: bad descriptor(s), addr=%d!\n",
-	    __FUNCTION__, udev->address);
-
-	/* free old pipes if any */
-	usb2_free_pipe_data(udev, iface_index, 0 - 1);
-	return (USB_ERR_INVAL);
-}
-
-/*------------------------------------------------------------------------*
- *	usb2_free_iface_data
- *
- * This function will free all USB interfaces and USB pipes belonging
- * to an USB device.
- *------------------------------------------------------------------------*/
-static void
-usb2_free_iface_data(struct usb2_device *udev)
-{
-	struct usb2_interface *iface = udev->ifaces;
-	struct usb2_interface *iface_end = udev->ifaces + USB_IFACE_MAX;
-
-	/* mtx_assert() */
+	usb2_cdev_free(udev);
+#endif
 
 #if USB_HAVE_COMPAT_LINUX
 	/* free Linux compat device, if any */
@@ -492,18 +382,10 @@ usb2_free_iface_data(struct usb2_device 
 		udev->linux_dev = NULL;
 	}
 #endif
-	/* free all pipes, if any */
-	usb2_free_pipe_data(udev, 0, 0);
 
-	/* free all interfaces, if any */
-	while (iface != iface_end) {
-		iface->idesc = NULL;
-		iface->alt_index = 0;
-		iface->parent_iface_index = USB_IFACE_INDEX_ANY;
-		iface++;
-	}
+	usb2_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_FREE);
 
-	/* free "cdesc" after "ifaces", if any */
+	/* free "cdesc" after "ifaces" and "pipes", if any */
 	if (udev->cdesc != NULL) {
 		if (udev->flags.usb2_mode != USB_MODE_DEVICE)
 			free(udev->cdesc, M_USB);
@@ -512,6 +394,10 @@ usb2_free_iface_data(struct usb2_device 
 	/* set unconfigured state */
 	udev->curr_config_no = USB_UNCONFIG_NO;
 	udev->curr_config_index = USB_UNCONFIG_INDEX;
+
+	if (do_unlock) {
+		sx_unlock(udev->default_sx + 1);
+	}
 }
 
 /*------------------------------------------------------------------------*
@@ -533,7 +419,6 @@ usb2_set_config_index(struct usb2_device
 	struct usb2_config_descriptor *cdp;
 	uint16_t power;
 	uint16_t max_power;
-	uint8_t nifc;
 	uint8_t selfpowered;
 	uint8_t do_unlock;
 	usb2_error_t err;
@@ -548,22 +433,12 @@ usb2_set_config_index(struct usb2_device
 		sx_xlock(udev->default_sx + 1);
 	}
 
-	/* detach all interface drivers */
-	usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 1);
-
-#if USB_HAVE_UGEN
-	/* free all FIFOs except control endpoint FIFOs */
-	usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, 0);
-
-	/* free all configuration data structures */
-	usb2_cdev_free(udev);
-#endif
-	usb2_free_iface_data(udev);
+	usb2_unconfigure(udev, USB_UNCFG_FLAG_FREE_SUBDEV);
 
 	if (index == USB_UNCONFIG_INDEX) {
 		/*
 		 * Leave unallocated when unconfiguring the
-		 * device. "usb2_free_iface_data()" will also reset
+		 * device. "usb2_unconfigure()" will also reset
 		 * the current config number and index.
 		 */
 		err = usb2_req_set_config(udev, NULL, USB_UNCONFIG_NO);
@@ -585,10 +460,6 @@ usb2_set_config_index(struct usb2_device
 
 	udev->cdesc = cdp;
 
-	if (cdp->bNumInterface > USB_IFACE_MAX) {
-		DPRINTFN(0, "too many interfaces: %d\n", cdp->bNumInterface);
-		cdp->bNumInterface = USB_IFACE_MAX;
-	}
 	/* Figure out if the device is self or bus powered. */
 	selfpowered = 0;
 	if ((!udev->flags.uq_bus_powered) &&
@@ -665,14 +536,17 @@ usb2_set_config_index(struct usb2_device
 	if (err) {
 		goto done;
 	}
-	/* Allocate and fill interface data. */
-	nifc = cdp->bNumInterface;
-	while (nifc--) {
-		err = usb2_fill_iface_data(udev, nifc, 0);
-		if (err) {
-			goto done;
-		}
+
+	err = usb2_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_ALLOC);
+	if (err) {
+		goto done;
+	}
+
+	err = usb2_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_INIT);
+	if (err) {
+		goto done;
 	}
+
 #if USB_HAVE_UGEN
 	/* create device nodes for each endpoint */
 	usb2_cdev_create(udev);
@@ -681,10 +555,7 @@ usb2_set_config_index(struct usb2_device
 done:
 	DPRINTF("error=%s\n", usb2_errstr(err));
 	if (err) {
-#if USB_HAVE_UGEN
-		usb2_cdev_free(udev);
-#endif
-		usb2_free_iface_data(udev);
+		usb2_unconfigure(udev, USB_UNCFG_FLAG_FREE_SUBDEV);
 	}
 	if (do_unlock) {
 		sx_unlock(udev->default_sx + 1);
@@ -693,6 +564,205 @@ done:
 }
 
 /*------------------------------------------------------------------------*
+ *	usb2_config_parse
+ *
+ * This function will allocate and free USB interfaces and USB pipes,
+ * parse the USB configuration structure and initialise the USB pipes
+ * and interfaces. If "iface_index" is not equal to
+ * "USB_IFACE_INDEX_ANY" then the "cmd" parameter is the
+ * alternate_setting to be selected for the given interface. Else the
+ * "cmd" parameter is defined by "USB_CFG_XXX". "iface_index" can be
+ * "USB_IFACE_INDEX_ANY" or a valid USB interface index. This function
+ * is typically called when setting the configuration or when setting
+ * an alternate interface.
+ *
+ * Returns:
+ *    0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_config_parse(struct usb2_device *udev, uint8_t iface_index, uint8_t cmd)
+{
+	struct usb2_idesc_parse_state ips;
+	struct usb2_interface_descriptor *id;
+	struct usb2_endpoint_descriptor *ed;
+	struct usb2_interface *iface;
+	struct usb2_pipe *pipe;
+	usb2_error_t err;
+	uint8_t ep_curr;
+	uint8_t ep_max;
+	uint8_t temp;
+	uint8_t do_init;
+	uint8_t alt_index;
+
+	if (iface_index != USB_IFACE_INDEX_ANY) {
+		/* parameter overload */
+		alt_index = cmd;
+		cmd = USB_CFG_INIT;
+	} else {
+		/* not used */
+		alt_index = 0;
+	}
+
+	err = 0;
+
+	DPRINTFN(5, "iface_index=%d cmd=%d\n",
+	    iface_index, cmd);
+
+	if (cmd == USB_CFG_FREE)
+		goto cleanup;
+
+	if (cmd == USB_CFG_INIT) {
+		sx_assert(udev->default_sx + 1, SA_LOCKED);
+
+		/* check for in-use pipes */
+
+		pipe = udev->pipes;
+		ep_max = udev->pipes_max;
+		while (ep_max--) {
+			/* look for matching pipes */
+			if ((iface_index == USB_IFACE_INDEX_ANY) ||
+			    (iface_index == pipe->iface_index)) {
+				if (pipe->refcount != 0) {
+					/*
+					 * This typically indicates a
+					 * more serious error.
+					 */
+					err = USB_ERR_IN_USE;
+				} else {
+					/* reset pipe */
+					memset(pipe, 0, sizeof(*pipe));
+					/* make sure we don't zero the pipe again */
+					pipe->iface_index = USB_IFACE_INDEX_ANY;
+				}
+			}
+			pipe++;
+		}
+
+		if (err)
+			return (err);
+	}
+
+	memset(&ips, 0, sizeof(ips));
+
+	ep_curr = 0;
+	ep_max = 0;
+
+	while ((id = usb2_idesc_foreach(udev->cdesc, &ips))) {
+
+		/* check for interface overflow */
+		if (ips.iface_index == USB_IFACE_MAX)
+			break;			/* crazy */
+
+		iface = udev->ifaces + ips.iface_index;
+
+		/* check for specific interface match */
+
+		if (cmd == USB_CFG_INIT) {
+			if ((iface_index != USB_IFACE_INDEX_ANY) && 
+			    (iface_index != ips.iface_index)) {
+				/* wrong interface */
+				do_init = 0;
+			} else if (alt_index != ips.iface_index_alt) {
+				/* wrong alternate setting */
+				do_init = 0;
+			} else {
+				/* initialise interface */
+				do_init = 1;
+			}
+		} else
+			do_init = 0;
+
+		/* check for new interface */
+		if (ips.iface_index_alt == 0) {
+			/* update current number of endpoints */
+			ep_curr = ep_max;
+		}
+		/* check for init */
+		if (do_init) {
+			/* setup the USB interface structure */
+			iface->idesc = id;
+			/* default setting */
+			iface->parent_iface_index = USB_IFACE_INDEX_ANY;
+			/* set alternate index */
+			iface->alt_index = alt_index;
+		}
+
+		DPRINTFN(5, "found idesc nendpt=%d\n", id->bNumEndpoints);
+
+		ed = (struct usb2_endpoint_descriptor *)id;
+
+		temp = ep_curr;
+
+		/* iterate all the endpoint descriptors */
+		while ((ed = usb2_edesc_foreach(udev->cdesc, ed))) {
+
+			if (temp == USB_EP_MAX)
+				break;			/* crazy */
+
+			pipe = udev->pipes + temp;
+
+			if (do_init) {
+				usb2_init_pipe(udev, 
+				    ips.iface_index, ed, pipe);
+			}
+
+			temp ++;
+
+			/* find maximum number of endpoints */
+			if (ep_max < temp)
+				ep_max = temp;
+
+			/* optimalisation */
+			id = (struct usb2_interface_descriptor *)ed;
+		}
+	}
+
+	/* NOTE: It is valid to have no interfaces and no endpoints! */
+
+	if (cmd == USB_CFG_ALLOC) {
+		udev->ifaces_max = ips.iface_index;
+		udev->ifaces = NULL;
+		if (udev->ifaces_max != 0) {
+			udev->ifaces = malloc(sizeof(*iface) * udev->ifaces_max,
+			        M_USB, M_WAITOK | M_ZERO);
+			if (udev->ifaces == NULL) {
+				err = USB_ERR_NOMEM;
+				goto done;
+			}
+		}
+		udev->pipes_max = ep_max;
+		udev->pipes = NULL;
+		if (udev->pipes_max != 0) {
+			udev->pipes = malloc(sizeof(*pipe) * udev->pipes_max,
+			        M_USB, M_WAITOK | M_ZERO);
+			if (udev->pipes == NULL) {
+				err = USB_ERR_NOMEM;
+				goto done;
+			}
+		}
+	}
+
+done:
+	if (err) {
+		if (cmd == USB_CFG_ALLOC) {
+cleanup:
+			/* cleanup */
+			if (udev->ifaces != NULL)
+				free(udev->ifaces, M_USB);
+			if (udev->pipes != NULL)
+				free(udev->pipes, M_USB);
+
+			udev->ifaces = NULL;
+			udev->pipes = NULL;
+			udev->ifaces_max = 0;
+			udev->pipes_max = 0;
+		}
+	}
+	return (err);
+}
+
+/*------------------------------------------------------------------------*
  *	usb2_set_alt_interface_index
  *
  * This function will select an alternate interface index for the
@@ -726,7 +796,8 @@ usb2_set_alt_interface_index(struct usb2
 		goto done;
 	}
 	if (udev->flags.usb2_mode == USB_MODE_DEVICE) {
-		usb2_detach_device(udev, iface_index, 1);
+		usb2_detach_device(udev, iface_index,
+		    USB_UNCFG_FLAG_FREE_SUBDEV);
 	} else {
 		if (iface->alt_index == alt_index) {
 			/* 
@@ -744,7 +815,8 @@ usb2_set_alt_interface_index(struct usb2
 	 */
 	usb2_fifo_free_wrap(udev, iface_index, 0);
 #endif
-	err = usb2_fill_iface_data(udev, iface_index, alt_index);
+
+	err = usb2_config_parse(udev, iface_index, alt_index);
 	if (err) {
 		goto done;
 	}
@@ -874,15 +946,17 @@ usb2_reset_iface_endpoints(struct usb2_d
  *
  * This function will try to detach an USB device. If it fails a panic
  * will result.
+ *
+ * Flag values, see "USB_UNCFG_FLAG_XXX".
  *------------------------------------------------------------------------*/
 static void
 usb2_detach_device_sub(struct usb2_device *udev, device_t *ppdev,
-    uint8_t free_subdev)
+    uint8_t flag)
 {
 	device_t dev;
 	int err;
 
-	if (!free_subdev) {
+	if (!(flag & USB_UNCFG_FLAG_FREE_SUBDEV)) {
 
 		*ppdev = NULL;
 
@@ -928,14 +1002,15 @@ error:
  *
  * The following function will detach the matching interfaces.
  * This function is NULL safe.
+ *
+ * Flag values, see "USB_UNCFG_FLAG_XXX".
  *------------------------------------------------------------------------*/
 void
 usb2_detach_device(struct usb2_device *udev, uint8_t iface_index,
-    uint8_t free_subdev)
+    uint8_t flag)
 {
 	struct usb2_interface *iface;
 	uint8_t i;
-	uint8_t do_unlock;
 
 	if (udev == NULL) {
 		/* nothing to do */
@@ -943,13 +1018,7 @@ usb2_detach_device(struct usb2_device *u
 	}
 	DPRINTFN(4, "udev=%p\n", udev);
 
-	/* automatic locking */
-	if (sx_xlocked(udev->default_sx + 1)) {
-		do_unlock = 0;
-	} else {
-		do_unlock = 1;
-		sx_xlock(udev->default_sx + 1);
-	}
+	sx_assert(udev->default_sx + 1, SA_LOCKED);
 
 	/*
 	 * First detach the child to give the child's detach routine a
@@ -974,11 +1043,7 @@ usb2_detach_device(struct usb2_device *u
 			/* looks like the end of the USB interfaces */
 			break;
 		}
-		usb2_detach_device_sub(udev, &iface->subdev, free_subdev);
-	}
-
-	if (do_unlock) {
-		sx_unlock(udev->default_sx + 1);
+		usb2_detach_device_sub(udev, &iface->subdev, flag);
 	}
 }
 
@@ -1445,7 +1510,7 @@ usb2_alloc_device(device_t parent_dev, s
 	}
 
 	/* init the default pipe */
-	usb2_fill_pipe_data(udev, 0,
+	usb2_init_pipe(udev, 0,
 	    &udev->default_ep_desc,
 	    &udev->default_pipe);
 
@@ -1682,7 +1747,8 @@ repeat_set_config:
 		} else if ((config_index + 1) < udev->ddesc.bNumConfigurations) {
 
 			if ((udev->cdesc->bNumInterface < 2) &&
-			    (usb2_get_no_endpoints(udev->cdesc) == 0)) {
+			    (usb2_get_no_descriptors(udev->cdesc,
+			    UDESC_ENDPOINT) == 0)) {
 				DPRINTFN(0, "Found no endpoints "
 				    "(trying next config)!\n");
 				config_index++;
@@ -1728,7 +1794,9 @@ repeat_set_config:
 done:
 	if (err) {
 		/* free device  */
-		usb2_free_device(udev);
+		usb2_free_device(udev,
+		    USB_UNCFG_FLAG_FREE_SUBDEV |
+		    USB_UNCFG_FLAG_FREE_EP0);
 		udev = NULL;
 	}
 	return (udev);
@@ -1846,14 +1914,21 @@ usb2_cdev_cleanup(void* arg)
  *	usb2_free_device
  *
  * This function is NULL safe and will free an USB device.
+ *
+ * Flag values, see "USB_UNCFG_FLAG_XXX".
  *------------------------------------------------------------------------*/
 void
-usb2_free_device(struct usb2_device *udev)
+usb2_free_device(struct usb2_device *udev, uint8_t flag)
 {
-	struct usb2_bus *bus = udev->bus;;
+	struct usb2_bus *bus;
+
+	if (udev == NULL)
+		return;		/* already freed */
 
 	DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no);
 
+	bus = udev->bus;;
+
 #if USB_HAVE_UGEN
 	usb2_notify_addq("-", udev);
 
@@ -1882,26 +1957,18 @@ usb2_free_device(struct usb2_device *ude
 		usb2_cv_wait(udev->default_cv + 1, &usb2_ref_lock);
 	}
 	mtx_unlock(&usb2_ref_lock);
+
+	destroy_dev_sched_cb(udev->default_dev, usb2_cdev_cleanup,
+	    udev->default_dev->si_drv1);
 #endif
 
 	if (udev->flags.usb2_mode == USB_MODE_DEVICE) {
 		/* stop receiving any control transfers (Device Side Mode) */
 		usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX);
 	}
-#if USB_HAVE_UGEN
-	/* free all FIFOs */
-	usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, 1);
 
-	/*
-	 * Free all interface related data and FIFOs, if any.
-	 */
-	usb2_cdev_free(udev);
-#endif
-	usb2_free_iface_data(udev);
-#if USB_HAVE_UGEN
-	destroy_dev_sched_cb(udev->default_dev, usb2_cdev_cleanup,
-	    udev->default_dev->si_drv1);
-#endif
+	/* the following will get the device unconfigured in software */
+	usb2_unconfigure(udev, flag);
 
 	/* unsetup any leftover default USB transfers */
 	usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX);
@@ -1948,12 +2015,8 @@ usb2_get_iface(struct usb2_device *udev,
 {
 	struct usb2_interface *iface = udev->ifaces + iface_index;
 
-	if ((iface < udev->ifaces) ||
-	    (iface_index >= USB_IFACE_MAX) ||
-	    (udev->cdesc == NULL) ||
-	    (iface_index >= udev->cdesc->bNumInterface)) {
+	if (iface_index >= udev->ifaces_max)
 		return (NULL);
-	}
 	return (iface);
 }
 
@@ -2303,12 +2366,12 @@ usb2_notify_addq(const char *type, struc
  *
  * This function will free the FIFOs.
  *
- * Flag values, if "iface_index" is equal to "USB_IFACE_INDEX_ANY".
- * 0: Free all FIFOs except generic control endpoints.
- * 1: Free all FIFOs.
- *
- * Flag values, if "iface_index" is not equal to "USB_IFACE_INDEX_ANY".
- * Not used.
+ * Description of "flag" argument: If the USB_UNCFG_FLAG_FREE_EP0 flag
+ * is set and "iface_index" is set to "USB_IFACE_INDEX_ANY", we free
+ * all FIFOs. If the USB_UNCFG_FLAG_FREE_EP0 flag is not set and
+ * "iface_index" is set to "USB_IFACE_INDEX_ANY", we free all non
+ * control endpoint FIFOs. If "iface_index" is not set to
+ * "USB_IFACE_INDEX_ANY" the flag has no effect.
  *------------------------------------------------------------------------*/
 static void
 usb2_fifo_free_wrap(struct usb2_device *udev,
@@ -2341,7 +2404,8 @@ usb2_fifo_free_wrap(struct usb2_device *
 			}
 		} else if (iface_index == USB_IFACE_INDEX_ANY) {
 			if ((f->methods == &usb2_ugen_methods) &&
-			    (f->dev_ep_index == 0) && (flag == 0) &&
+			    (f->dev_ep_index == 0) &&
+			    (!(flag & USB_UNCFG_FLAG_FREE_EP0)) &&
 			    (f->fs_xfer == NULL)) {
 				/* no need to free this FIFO */
 				continue;

Modified: head/sys/dev/usb/usb_device.h
==============================================================================
--- head/sys/dev/usb/usb_device.h	Sun Apr  5 18:19:42 2009	(r190729)
+++ head/sys/dev/usb/usb_device.h	Sun Apr  5 18:19:52 2009	(r190730)
@@ -32,6 +32,18 @@ struct usb_device;		/* linux compat */
 
 #define	USB_DEFAULT_XFER_MAX 2
 
+/* "usb2_parse_config()" commands */
+
+#define	USB_CFG_ALLOC 0
+#define	USB_CFG_FREE 1
+#define	USB_CFG_INIT 2
+
+/* "usb2_unconfigure()" flags */
+
+#define	USB_UNCFG_FLAG_NONE 0x00
+#define	USB_UNCFG_FLAG_FREE_SUBDEV 0x01		/* subdevices are freed */
+#define	USB_UNCFG_FLAG_FREE_EP0	0x02		/* endpoint zero is freed */
+
 struct usb2_clear_stall_msg {
 	struct usb2_proc_msg hdr;
 	struct usb2_device *udev;
@@ -103,10 +115,9 @@ struct usb2_device {
 	struct sx default_sx[2];
 	struct mtx default_mtx[1];
 	struct cv default_cv[2];
-	struct usb2_interface ifaces[USB_IFACE_MAX];
+	struct usb2_interface *ifaces;
 	struct usb2_pipe default_pipe;	/* Control Endpoint 0 */
-	struct cdev *default_dev;	/* Control Endpoint 0 device node */
-	struct usb2_pipe pipes[USB_EP_MAX];
+	struct usb2_pipe *pipes;
 	struct usb2_power_save pwr_save;/* power save data */
 
 	struct usb2_bus *bus;		/* our USB BUS */
@@ -123,6 +134,7 @@ struct usb2_device {
 #if USB_HAVE_UGEN
 	struct usb2_fifo *fifo[USB_FIFO_MAX];
 	struct usb2_symlink *ugen_symlink;	/* our generic symlink */
+	struct cdev *default_dev;	/* Control Endpoint 0 device node */
 	LIST_HEAD(,usb2_fs_privdata) pd_list;
 	char	ugen_name[20];		/* name of ugenX.X device */
 #endif
@@ -146,6 +158,8 @@ struct usb2_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 ifaces_max;		/* number of interfaces present */
+	uint8_t pipes_max;		/* number of pipes present */
 
 	/* the "flags" field is write-protected by "bus->mtx" */
 
@@ -184,10 +198,8 @@ usb2_error_t	usb2_set_endpoint_stall(str
 		    struct usb2_pipe *pipe, uint8_t do_stall);
 usb2_error_t	usb2_suspend_resume(struct usb2_device *udev,
 		    uint8_t do_suspend);
-void	usb2_detach_device(struct usb2_device *udev, uint8_t iface_index,
-	    uint8_t free_subdev);
 void	usb2_devinfo(struct usb2_device *udev, char *dst_ptr, uint16_t dst_len);
-void	usb2_free_device(struct usb2_device *udev);
+void	usb2_free_device(struct usb2_device *, uint8_t);
 void	*usb2_find_descriptor(struct usb2_device *udev, void *id,
 	    uint8_t iface_index, uint8_t type, uint8_t type_mask,
 	    uint8_t subtype, uint8_t subtype_mask);

Modified: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c	Sun Apr  5 18:19:42 2009	(r190729)
+++ head/sys/dev/usb/usb_hub.c	Sun Apr  5 18:19:52 2009	(r190730)
@@ -297,8 +297,9 @@ repeat:
 	/* detach any existing devices */
 
 	if (child) {
-		usb2_detach_device(child, USB_IFACE_INDEX_ANY, 1);
-		usb2_free_device(child);
+		usb2_free_device(child,
+		    USB_UNCFG_FLAG_FREE_SUBDEV |
+		    USB_UNCFG_FLAG_FREE_EP0);
 		child = NULL;
 	}
 	/* get fresh status */
@@ -417,8 +418,9 @@ repeat:
 
 error:
 	if (child) {
-		usb2_detach_device(child, USB_IFACE_INDEX_ANY, 1);
-		usb2_free_device(child);
+		usb2_free_device(child,
+		    USB_UNCFG_FLAG_FREE_SUBDEV |
+		    USB_UNCFG_FLAG_FREE_EP0);
 		child = NULL;
 	}
 	if (err == 0) {
@@ -852,9 +854,8 @@ uhub_detach(device_t dev)
 		 * Subdevices are not freed, because the caller of
 		 * uhub_detach() will do that.
 		 */
-		usb2_detach_device(child, USB_IFACE_INDEX_ANY, 0);
-		usb2_free_device(child);
-		child = NULL;
+		usb2_free_device(child,
+		    USB_UNCFG_FLAG_FREE_EP0);
 	}
 
 	usb2_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER);

Modified: head/sys/dev/usb/usb_parse.c
==============================================================================
--- head/sys/dev/usb/usb_parse.c	Sun Apr  5 18:19:42 2009	(r190729)
+++ head/sys/dev/usb/usb_parse.c	Sun Apr  5 18:19:52 2009	(r190730)
@@ -84,113 +84,109 @@ usb2_desc_foreach(struct usb2_config_des
 }
 
 /*------------------------------------------------------------------------*
- *	usb2_find_idesc
+ *	usb2_idesc_foreach
  *
- * This function will return the interface descriptor, if any, that
- * has index "iface_index" and alternate index "alt_index".
+ * This function will iterate the interface descriptors in the config
+ * descriptor. The parse state structure should be zeroed before
+ * calling this function the first time.
  *
  * Return values:
  *   NULL: End of descriptors
  *   Else: A valid interface descriptor
  *------------------------------------------------------------------------*/
 struct usb2_interface_descriptor *
-usb2_find_idesc(struct usb2_config_descriptor *cd,
-    uint8_t iface_index, uint8_t alt_index)
+usb2_idesc_foreach(struct usb2_config_descriptor *cd,
+    struct usb2_idesc_parse_state *ps)
 {
-	struct usb2_descriptor *desc = NULL;
 	struct usb2_interface_descriptor *id;
-	uint8_t curidx = 0;
-	uint8_t lastidx = 0;
-	uint8_t curaidx = 0;
-	uint8_t first = 1;
-
-	while ((desc = usb2_desc_foreach(cd, desc))) {
-		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
-		    (desc->bLength >= sizeof(*id))) {
-			id = (void *)desc;
-
-			if (first) {
-				first = 0;
-				lastidx = id->bInterfaceNumber;
-
-			} else if (id->bInterfaceNumber != lastidx) {
+	uint8_t new_iface;
 
-				lastidx = id->bInterfaceNumber;
-				curidx++;
-				curaidx = 0;
-
-			} else {
-				curaidx++;
-			}
-
-			if ((iface_index == curidx) && (alt_index == curaidx)) {
-				return (id);
-			}
+	/* retrieve current descriptor */
+	id = (struct usb2_interface_descriptor *)ps->desc;
+	/* default is to start a new interface */
+	new_iface = 1;
+
+	while (1) {
+		id = (struct usb2_interface_descriptor *)
+		    usb2_desc_foreach(cd, (struct usb2_descriptor *)id);
+		if (id == NULL)
+			break;
+		if ((id->bDescriptorType == UDESC_INTERFACE) &&
+		    (id->bLength >= sizeof(*id))) {
+			if (ps->iface_no_last == id->bInterfaceNumber)
+				new_iface = 0;
+			ps->iface_no_last = id->bInterfaceNumber;
+			break;
 		}
 	}
-	return (NULL);
+
+	if (ps->desc == NULL) {
+		/* first time */
+	} else if (new_iface) {
+		/* new interface */
+		ps->iface_index ++;
+		ps->iface_index_alt = 0;
+	} else {
+		/* new alternate interface */
+		ps->iface_index_alt ++;
+	}
+
+	/* store and return current descriptor */
+	ps->desc = (struct usb2_descriptor *)id;
+	return (id);
 }
 

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list