aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/usb/controller/usb_controller.c4
-rw-r--r--sys/dev/usb/usb_device.c536
-rw-r--r--sys/dev/usb/usb_device.h24
-rw-r--r--sys/dev/usb/usb_hub.c15
-rw-r--r--sys/dev/usb/usb_parse.c147
-rw-r--r--sys/dev/usb/usb_parse.h29
6 files changed, 423 insertions, 332 deletions
diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c
index 84066a281d29..28b99d367951 100644
--- a/sys/dev/usb/controller/usb_controller.c
+++ b/sys/dev/usb/controller/usb_controller.c
@@ -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);
diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c
index f97a9d0e49f4..33b27061e6a3 100644
--- a/sys/dev/usb/usb_device.c
+++ b/sys/dev/usb/usb_device.c
@@ -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 usb2_device *,
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 *udev, uint8_t *count)
*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 *udev, uint8_t iface_index,
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 *udev, struct usb2_pipe *pipe)
}
/*------------------------------------------------------------------------*
- * 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)
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 *udev)
/* 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 *udev, uint8_t index)
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 *udev, uint8_t index)
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, uint8_t index)
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 *udev, uint8_t index)
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 *udev, uint8_t index)
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_device *udev,
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_device *udev,
*/
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_device *udev, uint8_t iface_index)
*
* 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 *udev, uint8_t iface_index,
}
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 *udev, uint8_t iface_index,
/* 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, struct usb2_bus *bus,
}
/* 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 *udev)
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, uint8_t iface_index)
{
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, struct usb2_device *udev)
*
* 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 *udev,
}
} 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;
diff --git a/sys/dev/usb/usb_device.h b/sys/dev/usb/usb_device.h
index f72b508fff31..67f7b59ff3e5 100644
--- a/sys/dev/usb/usb_device.h
+++ b/sys/dev/usb/usb_device.h
@@ -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(struct usb2_device *udev,
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);
diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
index a720261e6c3e..02f546439489 100644
--- a/sys/dev/usb/usb_hub.c
+++ b/sys/dev/usb/usb_hub.c
@@ -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);
diff --git a/sys/dev/usb/usb_parse.c b/sys/dev/usb/usb_parse.c
index 381f546ac96e..710d8fa073d7 100644
--- a/sys/dev/usb/usb_parse.c
+++ b/sys/dev/usb/usb_parse.c
@@ -84,113 +84,109 @@ usb2_desc_foreach(struct usb2_config_descriptor *cd,
}
/*------------------------------------------------------------------------*
- * 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;
+ /* retrieve current descriptor */
+ id = (struct usb2_interface_descriptor *)ps->desc;
+ /* default is to start a new interface */
+ new_iface = 1;
- } else {
- curaidx++;
- }
-
- if ((iface_index == curidx) && (alt_index == curaidx)) {
- return (id);
- }
+ 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);
}
/*------------------------------------------------------------------------*
- * usb2_find_edesc
+ * usb2_edesc_foreach
*
- * This function will return the endpoint descriptor for the passed
- * interface index, alternate index and endpoint index.
+ * This function will iterate all the endpoint descriptors within an
+ * interface descriptor. Starting value for the "ped" argument should
+ * be a valid interface descriptor.
*
* Return values:
* NULL: End of descriptors
* Else: A valid endpoint descriptor
*------------------------------------------------------------------------*/
struct usb2_endpoint_descriptor *
-usb2_find_edesc(struct usb2_config_descriptor *cd,
- uint8_t iface_index, uint8_t alt_index, uint8_t ep_index)
+usb2_edesc_foreach(struct usb2_config_descriptor *cd,
+ struct usb2_endpoint_descriptor *ped)
{
- struct usb2_descriptor *desc = NULL;
- struct usb2_interface_descriptor *d;
- uint8_t curidx = 0;
-
- d = usb2_find_idesc(cd, iface_index, alt_index);
- if (d == NULL)
- return (NULL);
-
- if (ep_index >= d->bNumEndpoints) /* quick exit */
- return (NULL);
+ struct usb2_descriptor *desc;
- desc = ((void *)d);
+ desc = ((struct usb2_descriptor *)ped);
while ((desc = usb2_desc_foreach(cd, desc))) {
if (desc->bDescriptorType == UDESC_INTERFACE) {
break;
}
if (desc->bDescriptorType == UDESC_ENDPOINT) {
- if (curidx == ep_index) {
- if (desc->bLength <
- sizeof(struct usb2_endpoint_descriptor)) {
- /* endpoint index is invalid */
- break;
- }
- return ((void *)desc);
+ if (desc->bLength < sizeof(*ped)) {
+ /* endpoint index is invalid */
+ break;
}
- curidx++;
+ return ((struct usb2_endpoint_descriptor *)desc);
}
}
return (NULL);
}
/*------------------------------------------------------------------------*
- * usb2_get_no_endpoints
+ * usb2_get_no_descriptors
*
- * This function will count the total number of endpoints available.
+ * This function will count the total number of descriptors in the
+ * configuration descriptor of type "type".
*------------------------------------------------------------------------*/
-uint16_t
-usb2_get_no_endpoints(struct usb2_config_descriptor *cd)
+uint8_t
+usb2_get_no_descriptors(struct usb2_config_descriptor *cd, uint8_t type)
{
struct usb2_descriptor *desc = NULL;
- uint16_t count = 0;
+ uint8_t count = 0;
while ((desc = usb2_desc_foreach(cd, desc))) {
- if (desc->bDescriptorType == UDESC_ENDPOINT) {
+ if (desc->bDescriptorType == type) {
count++;
+ if (count == 0xFF)
+ break; /* crazy */
}
}
return (count);
@@ -200,25 +196,30 @@ usb2_get_no_endpoints(struct usb2_config_descriptor *cd)
* usb2_get_no_alts
*
* Return value:
- * Number of alternate settings for the given "ifaceno".
- *
- * NOTE: The returned can be larger than the actual number of
- * alternate settings.
+ * Number of alternate settings for the given interface descriptor pointer.
*------------------------------------------------------------------------*/
-uint16_t
-usb2_get_no_alts(struct usb2_config_descriptor *cd, uint8_t ifaceno)
+uint8_t
+usb2_get_no_alts(struct usb2_config_descriptor *cd,
+ struct usb2_interface_descriptor *id)
{
- struct usb2_descriptor *desc = NULL;
- struct usb2_interface_descriptor *id;
- uint16_t n = 0;
+ struct usb2_descriptor *desc;
+ uint8_t n = 0;
+ uint8_t ifaceno;
+
+ ifaceno = id->bInterfaceNumber;
+
+ desc = (struct usb2_descriptor *)id;
while ((desc = usb2_desc_foreach(cd, desc))) {
if ((desc->bDescriptorType == UDESC_INTERFACE) &&
(desc->bLength >= sizeof(*id))) {
- id = (void *)desc;
+ id = (struct usb2_interface_descriptor *)desc;
if (id->bInterfaceNumber == ifaceno) {
n++;
- }
+ if (n == 0xFF)
+ break; /* crazy */
+ } else
+ break; /* end */
}
}
return (n);
diff --git a/sys/dev/usb/usb_parse.h b/sys/dev/usb/usb_parse.h
index a9e6509905f7..b836b570ef4f 100644
--- a/sys/dev/usb/usb_parse.h
+++ b/sys/dev/usb/usb_parse.h
@@ -27,15 +27,28 @@
#ifndef _USB2_PARSE_H_
#define _USB2_PARSE_H_
+/* structures */
+
+struct usb2_idesc_parse_state {
+ struct usb2_descriptor *desc;
+ uint8_t iface_index; /* current interface index */
+ uint8_t iface_no_last;
+ uint8_t iface_index_alt; /* current alternate setting */
+};
+
+/* prototypes */
+
struct usb2_descriptor *usb2_desc_foreach(struct usb2_config_descriptor *cd,
struct usb2_descriptor *desc);
-struct usb2_interface_descriptor *usb2_find_idesc(
- struct usb2_config_descriptor *cd, uint8_t iface_index,
- uint8_t alt_index);
-struct usb2_endpoint_descriptor *usb2_find_edesc(
- struct usb2_config_descriptor *cd, uint8_t iface_index,
- uint8_t alt_index, uint8_t ep_index);
-uint16_t usb2_get_no_endpoints(struct usb2_config_descriptor *cd);
-uint16_t usb2_get_no_alts(struct usb2_config_descriptor *cd, uint8_t ifaceno);
+struct usb2_interface_descriptor *usb2_idesc_foreach(
+ struct usb2_config_descriptor *cd,
+ struct usb2_idesc_parse_state *ps);
+struct usb2_endpoint_descriptor *usb2_edesc_foreach(
+ struct usb2_config_descriptor *cd,
+ struct usb2_endpoint_descriptor *ped);
+uint8_t usb2_get_no_descriptors(struct usb2_config_descriptor *cd,
+ uint8_t type);
+uint8_t usb2_get_no_alts(struct usb2_config_descriptor *cd,
+ struct usb2_interface_descriptor *id);
#endif /* _USB2_PARSE_H_ */