summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/usb_device.c')
-rw-r--r--sys/dev/usb/usb_device.c2192
1 files changed, 2192 insertions, 0 deletions
diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c
new file mode 100644
index 0000000..439ad2b
--- /dev/null
+++ b/sys/dev/usb/usb_device.c
@@ -0,0 +1,2192 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+#include "usbdevs.h"
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_msctest.h>
+#include <dev/usb/usb_generic.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+/* function prototypes */
+
+static void usb2_fill_pipe_data(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_detach_device_sub(struct usb2_device *, device_t *,
+ uint8_t);
+static uint8_t usb2_probe_and_attach_sub(struct usb2_device *,
+ struct usb2_attach_arg *);
+static void usb2_init_attach_arg(struct usb2_device *,
+ struct usb2_attach_arg *);
+static void usb2_suspend_resume_sub(struct usb2_device *, device_t,
+ uint8_t);
+static void usb2_clear_stall_proc(struct usb2_proc_msg *_pm);
+static void usb2_check_strings(struct usb2_device *);
+static usb2_error_t usb2_fill_iface_data(struct usb2_device *, uint8_t,
+ uint8_t);
+static void usb2_notify_addq(const char *type, struct usb2_device *);
+static void usb2_fifo_free_wrap(struct usb2_device *, uint8_t, uint8_t);
+
+/* This variable is global to allow easy access to it: */
+
+int usb2_template = 0;
+
+SYSCTL_INT(_hw_usb2, OID_AUTO, template, CTLFLAG_RW,
+ &usb2_template, 0, "Selected USB device side template");
+
+
+/*------------------------------------------------------------------------*
+ * usb2_get_pipe_by_addr
+ *
+ * This function searches for an USB pipe by endpoint address and
+ * direction.
+ *
+ * Returns:
+ * NULL: Failure
+ * Else: Success
+ *------------------------------------------------------------------------*/
+struct usb2_pipe *
+usb2_get_pipe_by_addr(struct usb2_device *udev, uint8_t ea_val)
+{
+ struct usb2_pipe *pipe = udev->pipes;
+ struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX;
+ enum {
+ EA_MASK = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR),
+ };
+
+ /*
+ * According to the USB specification not all bits are used
+ * for the endpoint address. Keep defined bits only:
+ */
+ ea_val &= EA_MASK;
+
+ /*
+ * Iterate accross all the USB pipes searching for a match
+ * based on the endpoint address:
+ */
+ for (; pipe != pipe_end; pipe++) {
+
+ if (pipe->edesc == NULL) {
+ continue;
+ }
+ /* do the mask and check the value */
+ if ((pipe->edesc->bEndpointAddress & EA_MASK) == ea_val) {
+ goto found;
+ }
+ }
+
+ /*
+ * The default pipe is always present and is checked separately:
+ */
+ if ((udev->default_pipe.edesc) &&
+ ((udev->default_pipe.edesc->bEndpointAddress & EA_MASK) == ea_val)) {
+ pipe = &udev->default_pipe;
+ goto found;
+ }
+ return (NULL);
+
+found:
+ return (pipe);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_pipe
+ *
+ * This function searches for an USB pipe based on the information
+ * given by the passed "struct usb2_config" pointer.
+ *
+ * Return values:
+ * NULL: No match.
+ * Else: Pointer to "struct usb2_pipe".
+ *------------------------------------------------------------------------*/
+struct usb2_pipe *
+usb2_get_pipe(struct usb2_device *udev, uint8_t iface_index,
+ const struct usb2_config *setup)
+{
+ struct usb2_pipe *pipe = udev->pipes;
+ struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX;
+ uint8_t index = setup->ep_index;
+ uint8_t ea_mask;
+ uint8_t ea_val;
+ uint8_t type_mask;
+ uint8_t type_val;
+
+ DPRINTFN(10, "udev=%p iface_index=%d address=0x%x "
+ "type=0x%x dir=0x%x index=%d\n",
+ udev, iface_index, setup->endpoint,
+ setup->type, setup->direction, setup->ep_index);
+
+ /* setup expected endpoint direction mask and value */
+
+ if (setup->direction == UE_DIR_ANY) {
+ /* match any endpoint direction */
+ ea_mask = 0;
+ ea_val = 0;
+ } else {
+ /* match the given endpoint direction */
+ ea_mask = (UE_DIR_IN | UE_DIR_OUT);
+ ea_val = (setup->direction & (UE_DIR_IN | UE_DIR_OUT));
+ }
+
+ /* setup expected endpoint address */
+
+ if (setup->endpoint == UE_ADDR_ANY) {
+ /* match any endpoint address */
+ } else {
+ /* match the given endpoint address */
+ ea_mask |= UE_ADDR;
+ ea_val |= (setup->endpoint & UE_ADDR);
+ }
+
+ /* setup expected endpoint type */
+
+ if (setup->type == UE_BULK_INTR) {
+ /* this will match BULK and INTERRUPT endpoints */
+ type_mask = 2;
+ type_val = 2;
+ } else if (setup->type == UE_TYPE_ANY) {
+ /* match any endpoint type */
+ type_mask = 0;
+ type_val = 0;
+ } else {
+ /* match the given endpoint type */
+ type_mask = UE_XFERTYPE;
+ type_val = (setup->type & UE_XFERTYPE);
+ }
+
+ /*
+ * Iterate accross all the USB pipes searching for a match
+ * based on the endpoint address. Note that we are searching
+ * the pipes from the beginning of the "udev->pipes" array.
+ */
+ for (; pipe != pipe_end; pipe++) {
+
+ if ((pipe->edesc == NULL) ||
+ (pipe->iface_index != iface_index)) {
+ continue;
+ }
+ /* do the masks and check the values */
+
+ if (((pipe->edesc->bEndpointAddress & ea_mask) == ea_val) &&
+ ((pipe->edesc->bmAttributes & type_mask) == type_val)) {
+ if (!index--) {
+ goto found;
+ }
+ }
+ }
+
+ /*
+ * Match against default pipe last, so that "any pipe", "any
+ * address" and "any direction" returns the first pipe of the
+ * interface. "iface_index" and "direction" is ignored:
+ */
+ if ((udev->default_pipe.edesc) &&
+ ((udev->default_pipe.edesc->bEndpointAddress & ea_mask) == ea_val) &&
+ ((udev->default_pipe.edesc->bmAttributes & type_mask) == type_val) &&
+ (!index)) {
+ pipe = &udev->default_pipe;
+ goto found;
+ }
+ return (NULL);
+
+found:
+ return (pipe);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_interface_count
+ *
+ * This function stores the number of USB interfaces excluding
+ * alternate settings, which the USB config descriptor reports into
+ * the unsigned 8-bit integer pointed to by "count".
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_interface_count(struct usb2_device *udev, uint8_t *count)
+{
+ if (udev->cdesc == NULL) {
+ *count = 0;
+ return (USB_ERR_NOT_CONFIGURED);
+ }
+ *count = udev->cdesc->bNumInterface;
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
+
+/*------------------------------------------------------------------------*
+ * usb2_fill_pipe_data
+ *
+ * This function will initialise the USB pipe structure pointed to by
+ * the "pipe" argument.
+ *------------------------------------------------------------------------*/
+static void
+usb2_fill_pipe_data(struct usb2_device *udev, uint8_t iface_index,
+ struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe)
+{
+ bzero(pipe, sizeof(*pipe));
+
+ (udev->bus->methods->pipe_init) (udev, edesc, pipe);
+
+ if (pipe->methods == NULL) {
+ /* the pipe is invalid: just return */
+ return;
+ }
+ /* initialise USB pipe structure */
+ pipe->edesc = edesc;
+ pipe->iface_index = iface_index;
+ TAILQ_INIT(&pipe->pipe_q.head);
+ pipe->pipe_q.command = &usb2_pipe_start;
+
+ /* clear stall, if any */
+ if (udev->bus->methods->clear_stall) {
+ USB_BUS_LOCK(udev->bus);
+ (udev->bus->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_fill_iface_data
+ *
+ * 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.
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_fill_iface_data(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t alt_index)
+{
+ 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;
+
+ if (iface == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ 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++;
+ }
+
+ pipe = &udev->pipes[0];
+
+ 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.
+ */
+ 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() */
+
+ /* free Linux compat device, if any */
+ if (udev->linux_dev) {
+ usb_linux_free_device(udev->linux_dev);
+ udev->linux_dev = NULL;
+ }
+ /* 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->perm.mode = 0; /* disable permissions */
+ iface++;
+ }
+
+ /* free "cdesc" after "ifaces", if any */
+ if (udev->cdesc) {
+ free(udev->cdesc, M_USB);
+ udev->cdesc = NULL;
+ }
+ /* set unconfigured state */
+ udev->curr_config_no = USB_UNCONFIG_NO;
+ udev->curr_config_index = USB_UNCONFIG_INDEX;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_config_index
+ *
+ * This function selects configuration by index, independent of the
+ * actual configuration number. This function should not be used by
+ * USB drivers.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_set_config_index(struct usb2_device *udev, uint8_t index)
+{
+ struct usb2_status ds;
+ struct usb2_hub_descriptor hd;
+ 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;
+
+ DPRINTFN(6, "udev=%p index=%d\n", udev, index);
+
+ /* automatic locking */
+ if (sx_xlocked(udev->default_sx + 1)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ sx_xlock(udev->default_sx + 1);
+ }
+
+ /* detach all interface drivers */
+ usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 1);
+
+ /* free all FIFOs except control endpoint FIFOs */
+ usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, 0);
+
+ /* free all configuration data structures */
+ usb2_free_iface_data(udev);
+
+ if (index == USB_UNCONFIG_INDEX) {
+ /*
+ * Leave unallocated when unconfiguring the
+ * device. "usb2_free_iface_data()" will also reset
+ * the current config number and index.
+ */
+ err = usb2_req_set_config(udev, &Giant, USB_UNCONFIG_NO);
+ goto done;
+ }
+ /* get the full config descriptor */
+ err = usb2_req_get_config_desc_full(udev,
+ &Giant, &cdp, M_USB, index);
+ if (err) {
+ goto done;
+ }
+ /* set the new config descriptor */
+
+ 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) &&
+ (cdp->bmAttributes & UC_SELF_POWERED) &&
+ (udev->flags.usb2_mode == USB_MODE_HOST)) {
+ /* May be self powered. */
+ if (cdp->bmAttributes & UC_BUS_POWERED) {
+ /* Must ask device. */
+ if (udev->flags.uq_power_claim) {
+ /*
+ * HUB claims to be self powered, but isn't.
+ * It seems that the power status can be
+ * determined by the HUB characteristics.
+ */
+ err = usb2_req_get_hub_descriptor
+ (udev, &Giant, &hd, 1);
+ if (err) {
+ DPRINTFN(0, "could not read "
+ "HUB descriptor: %s\n",
+ usb2_errstr(err));
+
+ } else if (UGETW(hd.wHubCharacteristics) &
+ UHD_PWR_INDIVIDUAL) {
+ selfpowered = 1;
+ }
+ DPRINTF("characteristics=0x%04x\n",
+ UGETW(hd.wHubCharacteristics));
+ } else {
+ err = usb2_req_get_device_status
+ (udev, &Giant, &ds);
+ if (err) {
+ DPRINTFN(0, "could not read "
+ "device status: %s\n",
+ usb2_errstr(err));
+ } else if (UGETW(ds.wStatus) & UDS_SELF_POWERED) {
+ selfpowered = 1;
+ }
+ DPRINTF("status=0x%04x \n",
+ UGETW(ds.wStatus));
+ }
+ } else
+ selfpowered = 1;
+ }
+ DPRINTF("udev=%p cdesc=%p (addr %d) cno=%d attr=0x%02x, "
+ "selfpowered=%d, power=%d\n",
+ udev, cdp,
+ cdp->bConfigurationValue, udev->address, cdp->bmAttributes,
+ selfpowered, cdp->bMaxPower * 2);
+
+ /* Check if we have enough power. */
+ power = cdp->bMaxPower * 2;
+
+ if (udev->parent_hub) {
+ max_power = udev->parent_hub->hub->portpower;
+ } else {
+ max_power = USB_MAX_POWER;
+ }
+
+ if (power > max_power) {
+ DPRINTFN(0, "power exceeded %d > %d\n", power, max_power);
+ err = USB_ERR_NO_POWER;
+ goto done;
+ }
+ /* Only update "self_powered" in USB Host Mode */
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+ udev->flags.self_powered = selfpowered;
+ }
+ udev->power = power;
+ udev->curr_config_no = cdp->bConfigurationValue;
+ udev->curr_config_index = index;
+
+ /* Set the actual configuration value. */
+ err = usb2_req_set_config(udev, &Giant, cdp->bConfigurationValue);
+ 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;
+ }
+ }
+
+done:
+ DPRINTF("error=%s\n", usb2_errstr(err));
+ if (err) {
+ usb2_free_iface_data(udev);
+ }
+ if (do_unlock) {
+ sx_unlock(udev->default_sx + 1);
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_alt_interface_index
+ *
+ * This function will select an alternate interface index for the
+ * given interface index. The interface should not be in use when this
+ * function is called. That means there should be no open USB
+ * transfers. Else an error is returned.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_set_alt_interface_index(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t alt_index)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ usb2_error_t err;
+ uint8_t do_unlock;
+
+ /* automatic locking */
+ if (sx_xlocked(udev->default_sx + 1)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ sx_xlock(udev->default_sx + 1);
+ }
+ if (iface == NULL) {
+ err = USB_ERR_INVAL;
+ goto done;
+ }
+ if (udev->flags.usb2_mode == USB_MODE_DEVICE) {
+ usb2_detach_device(udev, iface_index, 1);
+ }
+ /*
+ * Free all generic FIFOs for this interface, except control
+ * endpoint FIFOs:
+ */
+ usb2_fifo_free_wrap(udev, iface_index, 0);
+
+ err = usb2_fill_iface_data(udev, iface_index, alt_index);
+ if (err) {
+ goto done;
+ }
+ err = usb2_req_set_alt_interface_no
+ (udev, &Giant, iface_index,
+ iface->idesc->bAlternateSetting);
+
+done:
+ if (do_unlock) {
+ sx_unlock(udev->default_sx + 1);
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_endpoint_stall
+ *
+ * This function is used to make a BULK or INTERRUPT endpoint
+ * send STALL tokens.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_set_endpoint_stall(struct usb2_device *udev, struct usb2_pipe *pipe,
+ uint8_t do_stall)
+{
+ struct usb2_xfer *xfer;
+ uint8_t et;
+ uint8_t was_stalled;
+
+ if (pipe == NULL) {
+ /* nothing to do */
+ DPRINTF("Cannot find endpoint\n");
+ /*
+ * Pretend that the clear or set stall request is
+ * successful else some USB host stacks can do
+ * strange things, especially when a control endpoint
+ * stalls.
+ */
+ return (0);
+ }
+ et = (pipe->edesc->bmAttributes & UE_XFERTYPE);
+
+ if ((et != UE_BULK) &&
+ (et != UE_INTERRUPT)) {
+ /*
+ * Should not stall control
+ * nor isochronous endpoints.
+ */
+ DPRINTF("Invalid endpoint\n");
+ return (0);
+ }
+ USB_BUS_LOCK(udev->bus);
+
+ /* store current stall state */
+ was_stalled = pipe->is_stalled;
+
+ /* check for no change */
+ if (was_stalled && do_stall) {
+ /* if the pipe is already stalled do nothing */
+ USB_BUS_UNLOCK(udev->bus);
+ DPRINTF("No change\n");
+ return (0);
+ }
+ /* set stalled state */
+ pipe->is_stalled = 1;
+
+ if (do_stall || (!was_stalled)) {
+ if (!was_stalled) {
+ /* lookup the current USB transfer, if any */
+ xfer = pipe->pipe_q.curr;
+ } else {
+ xfer = NULL;
+ }
+
+ /*
+ * If "xfer" is non-NULL the "set_stall" method will
+ * complete the USB transfer like in case of a timeout
+ * setting the error code "USB_ERR_STALLED".
+ */
+ (udev->bus->methods->set_stall) (udev, xfer, pipe);
+ }
+ if (!do_stall) {
+ pipe->toggle_next = 0; /* reset data toggle */
+ pipe->is_stalled = 0; /* clear stalled state */
+
+ (udev->bus->methods->clear_stall) (udev, pipe);
+
+ /* start up the current or next transfer, if any */
+ usb2_command_wrapper(&pipe->pipe_q, pipe->pipe_q.curr);
+ }
+ USB_BUS_UNLOCK(udev->bus);
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_reset_iface_endpoints - used in USB device side mode
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_reset_iface_endpoints(struct usb2_device *udev, uint8_t iface_index)
+{
+ struct usb2_pipe *pipe;
+ struct usb2_pipe *pipe_end;
+ usb2_error_t err;
+
+ pipe = udev->pipes;
+ pipe_end = udev->pipes + USB_EP_MAX;
+
+ for (; pipe != pipe_end; pipe++) {
+
+ if ((pipe->edesc == NULL) ||
+ (pipe->iface_index != iface_index)) {
+ continue;
+ }
+ /* simulate a clear stall from the peer */
+ err = usb2_set_endpoint_stall(udev, pipe, 0);
+ if (err) {
+ /* just ignore */
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_detach_device_sub
+ *
+ * This function will try to detach an USB device. If it fails a panic
+ * will result.
+ *------------------------------------------------------------------------*/
+static void
+usb2_detach_device_sub(struct usb2_device *udev, device_t *ppdev,
+ uint8_t free_subdev)
+{
+ 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;
+
+ device_printf(dev, "at %s, port %d, addr %d "
+ "(disconnected)\n",
+ device_get_nameunit(udev->parent_dev),
+ udev->port_no, udev->address);
+
+ 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");
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_detach_device
+ *
+ * The following function will detach the matching interfaces.
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_detach_device(struct usb2_device *udev, uint8_t iface_index,
+ uint8_t free_subdev)
+{
+ struct usb2_interface *iface;
+ uint8_t i;
+ uint8_t do_unlock;
+
+ if (udev == NULL) {
+ /* nothing to do */
+ return;
+ }
+ 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);
+ }
+
+ /*
+ * 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 {
+ i = 0;
+ iface_index = USB_IFACE_MAX;
+ }
+
+ /* do the detach */
+
+ for (; i != iface_index; i++) {
+
+ iface = usb2_get_iface(udev, i);
+ if (iface == NULL) {
+ /* 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_probe_and_attach_sub
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_probe_and_attach_sub(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa)
+{
+ struct usb2_interface *iface;
+ device_t dev;
+ int err;
+
+ iface = uaa->iface;
+ if (iface->parent_iface_index != USB_IFACE_INDEX_ANY) {
+ /* leave interface alone */
+ return (0);
+ }
+ dev = iface->subdev;
+ if (dev) {
+
+ /* clean up after module unload */
+
+ if (device_is_attached(dev)) {
+ /* already a device there */
+ return (0);
+ }
+ /* clear "iface->subdev" as early as possible */
+
+ iface->subdev = NULL;
+
+ if (device_delete_child(udev->parent_dev, dev)) {
+
+ /*
+ * 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 (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);
+ }
+ /*
+ * Set "subdev" before probe and attach so that "devd" gets
+ * the information it needs.
+ */
+ iface->subdev = uaa->temp_dev;
+
+ if (device_probe_and_attach(iface->subdev) == 0) {
+ /*
+ * The USB attach arguments are only available during probe
+ * and attach !
+ */
+ uaa->temp_dev = NULL;
+ device_set_ivars(iface->subdev, NULL);
+
+ if (udev->flags.suspended) {
+ err = DEVICE_SUSPEND(iface->subdev);
+ device_printf(iface->subdev, "Suspend failed\n");
+ }
+ return (0); /* success */
+ } else {
+ /* No USB driver found */
+ iface->subdev = NULL;
+ }
+ return (1); /* failure */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_parent_iface
+ *
+ * Using this function will lock the alternate interface setting on an
+ * interface. It is typically used for multi interface drivers. In USB
+ * device side mode it is assumed that the alternate interfaces all
+ * have the same endpoint descriptors. The default parent index value
+ * is "USB_IFACE_INDEX_ANY". Then the alternate setting value is not
+ * locked.
+ *------------------------------------------------------------------------*/
+void
+usb2_set_parent_iface(struct usb2_device *udev, uint8_t iface_index,
+ uint8_t parent_index)
+{
+ struct usb2_interface *iface;
+
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface) {
+ iface->parent_iface_index = parent_index;
+ }
+}
+
+static void
+usb2_init_attach_arg(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa)
+{
+ bzero(uaa, sizeof(*uaa));
+
+ uaa->device = udev;
+ uaa->usb2_mode = udev->flags.usb2_mode;
+ uaa->port = udev->port_no;
+
+ uaa->info.idVendor = UGETW(udev->ddesc.idVendor);
+ uaa->info.idProduct = UGETW(udev->ddesc.idProduct);
+ uaa->info.bcdDevice = UGETW(udev->ddesc.bcdDevice);
+ uaa->info.bDeviceClass = udev->ddesc.bDeviceClass;
+ uaa->info.bDeviceSubClass = udev->ddesc.bDeviceSubClass;
+ uaa->info.bDeviceProtocol = udev->ddesc.bDeviceProtocol;
+ uaa->info.bConfigIndex = udev->curr_config_index;
+ uaa->info.bConfigNum = udev->curr_config_no;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_probe_and_attach
+ *
+ * This function is called from "uhub_explore_sub()",
+ * "usb2_handle_set_config()" and "usb2_handle_request()".
+ *
+ * Returns:
+ * 0: Success
+ * Else: A control transfer failed
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_probe_and_attach(struct usb2_device *udev, uint8_t iface_index)
+{
+ struct usb2_attach_arg uaa;
+ struct usb2_interface *iface;
+ uint8_t i;
+ uint8_t j;
+ uint8_t do_unlock;
+
+ if (udev == NULL) {
+ DPRINTF("udev == NULL\n");
+ 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);
+ }
+
+ if (udev->curr_config_index == USB_UNCONFIG_INDEX) {
+ /* do nothing - no configuration has been set */
+ goto done;
+ }
+ /* setup USB attach arguments */
+
+ usb2_init_attach_arg(udev, &uaa);
+
+ /* Check if only one interface should be probed: */
+ if (iface_index != USB_IFACE_INDEX_ANY) {
+ i = iface_index;
+ j = i + 1;
+ } else {
+ i = 0;
+ j = USB_IFACE_MAX;
+ }
+
+ /* Do the probe and attach */
+ for (; i != j; i++) {
+
+ iface = usb2_get_iface(udev, i);
+ if (iface == NULL) {
+ /*
+ * Looks like the end of the USB
+ * interfaces !
+ */
+ DPRINTFN(2, "end of interfaces "
+ "at %u\n", i);
+ break;
+ }
+ if (iface->idesc == NULL) {
+ /* no interface descriptor */
+ continue;
+ }
+ uaa.iface = iface;
+
+ uaa.info.bInterfaceClass =
+ iface->idesc->bInterfaceClass;
+ uaa.info.bInterfaceSubClass =
+ iface->idesc->bInterfaceSubClass;
+ uaa.info.bInterfaceProtocol =
+ iface->idesc->bInterfaceProtocol;
+ uaa.info.bIfaceIndex = i;
+ uaa.info.bIfaceNum =
+ iface->idesc->bInterfaceNumber;
+ uaa.use_generic = 0;
+
+ DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n",
+ uaa.info.bInterfaceClass,
+ uaa.info.bInterfaceSubClass,
+ uaa.info.bInterfaceProtocol,
+ uaa.info.bIfaceIndex,
+ uaa.info.bIfaceNum);
+
+ /* try specific interface drivers first */
+
+ if (usb2_probe_and_attach_sub(udev, &uaa)) {
+ /* ignore */
+ }
+ /* try generic interface drivers last */
+
+ uaa.use_generic = 1;
+
+ if (usb2_probe_and_attach_sub(udev, &uaa)) {
+ /* ignore */
+ }
+ }
+
+ if (uaa.temp_dev) {
+ /* remove the last created child; it is unused */
+
+ if (device_delete_child(udev->parent_dev, uaa.temp_dev)) {
+ DPRINTFN(0, "device delete child failed!\n");
+ }
+ }
+done:
+ if (do_unlock) {
+ sx_unlock(udev->default_sx + 1);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_suspend_resume_sub
+ *
+ * This function is called when the suspend or resume methods should
+ * be executed on an USB device.
+ *------------------------------------------------------------------------*/
+static void
+usb2_suspend_resume_sub(struct usb2_device *udev, device_t dev, uint8_t do_suspend)
+{
+ int err;
+
+ if (dev == NULL) {
+ return;
+ }
+ if (!device_is_attached(dev)) {
+ 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");
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_suspend_resume
+ *
+ * The following function will suspend or resume the USB device.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_suspend_resume(struct usb2_device *udev, uint8_t do_suspend)
+{
+ struct usb2_interface *iface;
+ uint8_t i;
+
+ if (udev == NULL) {
+ /* nothing to do */
+ return (0);
+ }
+ DPRINTFN(4, "udev=%p do_suspend=%d\n", udev, do_suspend);
+
+ sx_assert(udev->default_sx + 1, SA_LOCKED);
+
+ USB_BUS_LOCK(udev->bus);
+ /* filter the suspend events */
+ if (udev->flags.suspended == do_suspend) {
+ USB_BUS_UNLOCK(udev->bus);
+ /* nothing to do */
+ return (0);
+ }
+ udev->flags.suspended = do_suspend;
+ USB_BUS_UNLOCK(udev->bus);
+
+ /* do the suspend or resume */
+
+ for (i = 0; i != USB_IFACE_MAX; i++) {
+
+ iface = usb2_get_iface(udev, i);
+ if (iface == NULL) {
+ /* looks like the end of the USB interfaces */
+ break;
+ }
+ usb2_suspend_resume_sub(udev, iface->subdev, do_suspend);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_clear_stall_proc
+ *
+ * This function performs generic USB clear stall operations.
+ *------------------------------------------------------------------------*/
+static void
+usb2_clear_stall_proc(struct usb2_proc_msg *_pm)
+{
+ struct usb2_clear_stall_msg *pm = (void *)_pm;
+ struct usb2_device *udev = pm->udev;
+
+ /* Change lock */
+ USB_BUS_UNLOCK(udev->bus);
+ mtx_lock(udev->default_mtx);
+
+ /* Start clear stall callback */
+ usb2_transfer_start(udev->default_xfer[1]);
+
+ /* Change lock */
+ mtx_unlock(udev->default_mtx);
+ USB_BUS_LOCK(udev->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_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.
+ *
+ * Return values:
+ * 0: Failure
+ * Else: Success
+ *------------------------------------------------------------------------*/
+struct usb2_device *
+usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus,
+ struct usb2_device *parent_hub, uint8_t depth,
+ uint8_t port_index, uint8_t port_no, uint8_t speed, uint8_t usb2_mode)
+{
+ struct usb2_attach_arg uaa;
+ struct usb2_device *udev;
+ struct usb2_device *adev;
+ struct usb2_device *hub;
+ uint8_t *scratch_ptr;
+ uint32_t scratch_size;
+ usb2_error_t err;
+ uint8_t device_index;
+
+ DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, "
+ "port_index=%u, port_no=%u, speed=%u, usb2_mode=%u\n",
+ parent_dev, bus, parent_hub, depth, port_index, port_no,
+ speed, usb2_mode);
+
+ /*
+ * Find an unused device index. In USB Host mode this is the
+ * same as the device address.
+ *
+ * Device index zero is not used and device index 1 should
+ * always be the root hub.
+ */
+ for (device_index = USB_ROOT_HUB_ADDR;
+ (device_index != bus->devices_max) &&
+ (bus->devices[device_index] != NULL);
+ device_index++) /* nop */;
+
+ if (device_index == bus->devices_max) {
+ device_printf(bus->bdev,
+ "No free USB device index for new device!\n");
+ return (NULL);
+ }
+
+ if (depth > 0x10) {
+ device_printf(bus->bdev,
+ "Invalid device depth!\n");
+ return (NULL);
+ }
+ udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO);
+ if (udev == NULL) {
+ return (NULL);
+ }
+ /* initialise our SX-lock */
+ sx_init(udev->default_sx, "0123456789ABCDEF - USB device SX lock" + depth);
+
+ /* initialise our SX-lock */
+ sx_init(udev->default_sx + 1, "0123456789ABCDEF - USB config SX lock" + depth);
+
+ usb2_cv_init(udev->default_cv, "WCTRL");
+ usb2_cv_init(udev->default_cv + 1, "UGONE");
+
+ /* initialise our mutex */
+ mtx_init(udev->default_mtx, "USB device mutex", NULL, MTX_DEF);
+
+ /* initialise generic clear stall */
+ udev->cs_msg[0].hdr.pm_callback = &usb2_clear_stall_proc;
+ udev->cs_msg[0].udev = udev;
+ udev->cs_msg[1].hdr.pm_callback = &usb2_clear_stall_proc;
+ udev->cs_msg[1].udev = udev;
+
+ /* initialise 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 */
+ udev->plugtime = (uint32_t)ticks;
+ /*
+ * We need to force the power mode to "on" because there are plenty
+ * of USB devices out there that do not work very well with
+ * automatic suspend and resume!
+ */
+ udev->power_mode = USB_POWER_MODE_ON;
+ udev->pwr_save.last_xfer_time = ticks;
+
+ /* we are not ready yet */
+ udev->refcount = 1;
+
+ /* set up default endpoint descriptor */
+ udev->default_ep_desc.bLength = sizeof(udev->default_ep_desc);
+ udev->default_ep_desc.bDescriptorType = UDESC_ENDPOINT;
+ udev->default_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
+ udev->default_ep_desc.bmAttributes = UE_CONTROL;
+ udev->default_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET;
+ udev->default_ep_desc.wMaxPacketSize[1] = 0;
+ udev->default_ep_desc.bInterval = 0;
+ udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
+
+ udev->speed = speed;
+ udev->flags.usb2_mode = usb2_mode;
+
+ /* speed combination should be checked by the parent HUB */
+
+ hub = udev->parent_hub;
+
+ /* search for our High Speed USB HUB, if any */
+
+ adev = udev;
+ hub = udev->parent_hub;
+
+ while (hub) {
+ if (hub->speed == USB_SPEED_HIGH) {
+ udev->hs_hub_addr = hub->address;
+ udev->hs_port_no = adev->port_no;
+ break;
+ }
+ adev = hub;
+ hub = hub->parent_hub;
+ }
+
+ /* init the default pipe */
+ usb2_fill_pipe_data(udev, 0,
+ &udev->default_ep_desc,
+ &udev->default_pipe);
+
+ /* set device index */
+ udev->device_index = device_index;
+
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+
+ err = usb2_req_set_address(udev, &Giant, device_index);
+
+ /* This is the new USB device address from now on */
+
+ udev->address = device_index;
+
+ /*
+ * 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) {
+ DPRINTFN(0, "set address %d failed "
+ "(ignored)\n", udev->address);
+ }
+ /* allow device time to set new address */
+ usb2_pause_mtx(&Giant,
+ USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE));
+ } else {
+ /* We are not self powered */
+ udev->flags.self_powered = 0;
+
+ /* Set unconfigured state */
+ udev->curr_config_no = USB_UNCONFIG_NO;
+ udev->curr_config_index = USB_UNCONFIG_INDEX;
+
+ /* Setup USB descriptors */
+ err = (usb2_temp_setup_by_index_p) (udev, usb2_template);
+ if (err) {
+ DPRINTFN(0, "setting up USB template failed maybe the USB "
+ "template module has not been loaded\n");
+ goto done;
+ }
+ }
+
+ /*
+ * Get the first 8 bytes of the device descriptor !
+ *
+ * NOTE: "usb2_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 = usb2_req_get_desc(udev, &Giant, &udev->ddesc,
+ USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
+ if (err) {
+ DPRINTFN(0, "getting device descriptor "
+ "at addr %d failed!\n", udev->address);
+ /* XXX try to re-enumerate the device */
+ err = usb2_req_re_enumerate(udev, &Giant);
+ if (err) {
+ goto done;
+ }
+ }
+ DPRINTF("adding unit addr=%d, rev=%02x, class=%d, "
+ "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
+ udev->address, UGETW(udev->ddesc.bcdUSB),
+ udev->ddesc.bDeviceClass,
+ udev->ddesc.bDeviceSubClass,
+ udev->ddesc.bDeviceProtocol,
+ udev->ddesc.bMaxPacketSize,
+ udev->ddesc.bLength,
+ udev->speed);
+
+ /* get the full device descriptor */
+ err = usb2_req_get_device_desc(udev, &Giant, &udev->ddesc);
+ if (err) {
+ DPRINTF("addr=%d, getting full desc failed\n",
+ udev->address);
+ goto done;
+ }
+ /*
+ * Setup temporary USB attach args so that we can figure out some
+ * basic quirks for this device.
+ */
+ usb2_init_attach_arg(udev, &uaa);
+
+ if (usb2_test_quirk(&uaa, UQ_BUS_POWERED)) {
+ udev->flags.uq_bus_powered = 1;
+ }
+ if (usb2_test_quirk(&uaa, UQ_POWER_CLAIM)) {
+ udev->flags.uq_power_claim = 1;
+ }
+ if (usb2_test_quirk(&uaa, UQ_NO_STRINGS)) {
+ udev->flags.no_strings = 1;
+ }
+ /*
+ * Workaround for buggy USB devices.
+ *
+ * It appears that some string-less USB chips will crash and
+ * disappear if any attempts are made to read any string
+ * descriptors.
+ *
+ * Try to detect such chips by checking the strings in the USB
+ * device descriptor. If no strings are present there we
+ * simply disable all USB strings.
+ */
+ scratch_ptr = udev->bus->scratch[0].data;
+ scratch_size = sizeof(udev->bus->scratch[0].data);
+
+ if (udev->ddesc.iManufacturer ||
+ udev->ddesc.iProduct ||
+ udev->ddesc.iSerialNumber) {
+ /* read out the language ID string */
+ err = usb2_req_get_string_desc(udev, &Giant,
+ (char *)scratch_ptr, 4, scratch_size,
+ USB_LANGUAGE_TABLE);
+ } else {
+ err = USB_ERR_INVAL;
+ }
+
+ if (err || (scratch_ptr[0] < 4)) {
+ udev->flags.no_strings = 1;
+ } else {
+ /* pick the first language as the default */
+ udev->langid = UGETW(scratch_ptr + 2);
+ }
+
+ /* assume 100mA bus powered for now. Changed when configured. */
+ udev->power = USB_MIN_POWER;
+
+ /* get serial number string */
+ err = usb2_req_get_string_any
+ (udev, &Giant, (char *)scratch_ptr,
+ scratch_size, udev->ddesc.iSerialNumber);
+
+ strlcpy(udev->serial, (char *)scratch_ptr, sizeof(udev->serial));
+
+ /* get manufacturer string */
+ err = usb2_req_get_string_any
+ (udev, &Giant, (char *)scratch_ptr,
+ scratch_size, udev->ddesc.iManufacturer);
+
+ strlcpy(udev->manufacturer, (char *)scratch_ptr, sizeof(udev->manufacturer));
+
+ /* get product string */
+ err = usb2_req_get_string_any
+ (udev, &Giant, (char *)scratch_ptr,
+ scratch_size, udev->ddesc.iProduct);
+
+ strlcpy(udev->product, (char *)scratch_ptr, sizeof(udev->product));
+
+ /* finish up all the strings */
+ usb2_check_strings(udev);
+
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+ uint8_t config_index;
+ uint8_t config_quirk;
+ uint8_t set_config_failed = 0;
+
+ /*
+ * Most USB devices should attach to config index 0 by
+ * default
+ */
+ if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_0)) {
+ config_index = 0;
+ config_quirk = 1;
+ } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_1)) {
+ config_index = 1;
+ config_quirk = 1;
+ } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_2)) {
+ config_index = 2;
+ config_quirk = 1;
+ } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_3)) {
+ config_index = 3;
+ config_quirk = 1;
+ } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_4)) {
+ config_index = 4;
+ config_quirk = 1;
+ } else {
+ config_index = 0;
+ config_quirk = 0;
+ }
+
+repeat_set_config:
+
+ DPRINTF("setting config %u\n", config_index);
+
+ /* get the USB device configured */
+ sx_xlock(udev->default_sx + 1);
+ err = usb2_set_config_index(udev, config_index);
+ sx_unlock(udev->default_sx + 1);
+ if (err) {
+ if (udev->ddesc.bNumConfigurations != 0) {
+ if (!set_config_failed) {
+ set_config_failed = 1;
+ /* XXX try to re-enumerate the device */
+ err = usb2_req_re_enumerate(
+ udev, &Giant);
+ if (err == 0)
+ goto repeat_set_config;
+ }
+ DPRINTFN(0, "Failure selecting "
+ "configuration index %u: %s, port %u, "
+ "addr %u (ignored)\n",
+ config_index, usb2_errstr(err), udev->port_no,
+ udev->address);
+ }
+ /*
+ * Some USB devices do not have any
+ * configurations. Ignore any set config
+ * failures!
+ */
+ err = 0;
+ } else if (config_quirk) {
+ /* user quirk selects configuration index */
+ } else if ((config_index + 1) < udev->ddesc.bNumConfigurations) {
+
+ if ((udev->cdesc->bNumInterface < 2) &&
+ (usb2_get_no_endpoints(udev->cdesc) == 0)) {
+ DPRINTFN(0, "Found no endpoints "
+ "(trying next config)!\n");
+ config_index++;
+ goto repeat_set_config;
+ }
+ if (config_index == 0) {
+ /*
+ * Try to figure out if we have an
+ * auto-install disk there:
+ */
+ if (usb2_test_autoinstall(udev, 0, 0) == 0) {
+ DPRINTFN(0, "Found possible auto-install "
+ "disk (trying next config)\n");
+ config_index++;
+ goto repeat_set_config;
+ }
+ }
+ } else if (usb2_test_huawei_autoinst_p(udev, &uaa) == 0) {
+ DPRINTFN(0, "Found Huawei auto-install disk!\n");
+ err = USB_ERR_STALLED; /* fake an error */
+ }
+ } else {
+ err = 0; /* set success */
+ }
+
+ DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n",
+ udev->address, udev, udev->parent_hub);
+
+ /* register our device - we are ready */
+ usb2_bus_port_set_device(bus, parent_hub ?
+ parent_hub->hub->ports + port_index : NULL, udev, device_index);
+
+ /* make a symlink for UGEN */
+ if (snprintf((char *)scratch_ptr, scratch_size,
+ USB_DEVICE_NAME "%u.%u.0.0",
+ device_get_unit(udev->bus->bdev),
+ udev->device_index)) {
+ /* ignore */
+ }
+ udev->ugen_symlink =
+ usb2_alloc_symlink((char *)scratch_ptr, "ugen%u.%u",
+ device_get_unit(udev->bus->bdev),
+ udev->device_index);
+
+ printf("ugen%u.%u: <%s> at %s\n",
+ device_get_unit(udev->bus->bdev),
+ udev->device_index, udev->manufacturer,
+ device_get_nameunit(udev->bus->bdev));
+
+ usb2_notify_addq("+", udev);
+done:
+ if (err) {
+ /* free device */
+ usb2_free_device(udev);
+ udev = NULL;
+ }
+ return (udev);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_free_device
+ *
+ * This function is NULL safe and will free an USB device.
+ *------------------------------------------------------------------------*/
+void
+usb2_free_device(struct usb2_device *udev)
+{
+ struct usb2_bus *bus;
+
+ if (udev == NULL) {
+ /* already freed */
+ return;
+ }
+ DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no);
+
+ usb2_notify_addq("-", udev);
+
+ bus = udev->bus;
+
+ printf("ugen%u.%u: <%s> at %s (disconnected)\n",
+ device_get_unit(bus->bdev),
+ udev->device_index, udev->manufacturer,
+ device_get_nameunit(bus->bdev));
+
+ /*
+ * Destroy UGEN symlink, if any
+ */
+ if (udev->ugen_symlink) {
+ usb2_free_symlink(udev->ugen_symlink);
+ udev->ugen_symlink = NULL;
+ }
+ /*
+ * Unregister our device first which will prevent any further
+ * references:
+ */
+ usb2_bus_port_set_device(bus, udev->parent_hub ?
+ udev->parent_hub->hub->ports + udev->port_index : NULL,
+ NULL, USB_ROOT_HUB_ADDR);
+
+ /* wait for all pending references to go away: */
+
+ mtx_lock(&usb2_ref_lock);
+ udev->refcount--;
+ while (udev->refcount != 0) {
+ usb2_cv_wait(udev->default_cv + 1, &usb2_ref_lock);
+ }
+ mtx_unlock(&usb2_ref_lock);
+
+ 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);
+ }
+ /* free all FIFOs */
+ usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, 1);
+
+ /*
+ * Free all interface related data and FIFOs, if any.
+ */
+ usb2_free_iface_data(udev);
+
+ /* unsetup any leftover default USB transfers */
+ usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX);
+
+ /* template unsetup, if any */
+ (usb2_temp_unsetup_p) (udev);
+
+ /*
+ * Make sure that our clear-stall messages are not queued
+ * anywhere:
+ */
+ USB_BUS_LOCK(udev->bus);
+ usb2_proc_mwait(&udev->bus->non_giant_callback_proc,
+ &udev->cs_msg[0], &udev->cs_msg[1]);
+ USB_BUS_UNLOCK(udev->bus);
+
+ sx_destroy(udev->default_sx);
+ sx_destroy(udev->default_sx + 1);
+
+ usb2_cv_destroy(udev->default_cv);
+ usb2_cv_destroy(udev->default_cv + 1);
+
+ mtx_destroy(udev->default_mtx);
+
+ /* free device */
+ free(udev, M_USB);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_iface
+ *
+ * This function is the safe way to get the USB interface structure
+ * pointer by interface index.
+ *
+ * Return values:
+ * NULL: Interface not present.
+ * Else: Pointer to USB interface structure.
+ *------------------------------------------------------------------------*/
+struct usb2_interface *
+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)) {
+ return (NULL);
+ }
+ return (iface);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_find_descriptor
+ *
+ * This function will lookup the first descriptor that matches the
+ * criteria given by the arguments "type" and "subtype". Descriptors
+ * will only be searched within the interface having the index
+ * "iface_index". If the "id" argument points to an USB descriptor,
+ * it will be skipped before the search is started. This allows
+ * searching for multiple descriptors using the same criteria. Else
+ * the search is started after the interface descriptor.
+ *
+ * Return values:
+ * NULL: End of descriptors
+ * Else: A descriptor matching the criteria
+ *------------------------------------------------------------------------*/
+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)
+{
+ struct usb2_descriptor *desc;
+ struct usb2_config_descriptor *cd;
+ struct usb2_interface *iface;
+
+ cd = usb2_get_config_descriptor(udev);
+ if (cd == NULL) {
+ return (NULL);
+ }
+ if (id == NULL) {
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface == NULL) {
+ return (NULL);
+ }
+ id = usb2_get_interface_descriptor(iface);
+ if (id == NULL) {
+ return (NULL);
+ }
+ }
+ desc = (void *)id;
+
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+
+ if (desc->bDescriptorType == UDESC_INTERFACE) {
+ break;
+ }
+ if (((desc->bDescriptorType & type_mask) == type) &&
+ ((desc->bDescriptorSubtype & subtype_mask) == subtype)) {
+ return (desc);
+ }
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_devinfo
+ *
+ * This function will dump information from the device descriptor
+ * belonging to the USB device pointed to by "udev", to the string
+ * pointed to by "dst_ptr" having a maximum length of "dst_len" bytes
+ * including the terminating zero.
+ *------------------------------------------------------------------------*/
+void
+usb2_devinfo(struct usb2_device *udev, char *dst_ptr, uint16_t dst_len)
+{
+ struct usb2_device_descriptor *udd = &udev->ddesc;
+ uint16_t bcdDevice;
+ uint16_t bcdUSB;
+
+ bcdUSB = UGETW(udd->bcdUSB);
+ bcdDevice = UGETW(udd->bcdDevice);
+
+ if (udd->bDeviceClass != 0xFF) {
+ snprintf(dst_ptr, dst_len, "%s %s, class %d/%d, rev %x.%02x/"
+ "%x.%02x, addr %d", udev->manufacturer, udev->product,
+ udd->bDeviceClass, udd->bDeviceSubClass,
+ (bcdUSB >> 8), bcdUSB & 0xFF,
+ (bcdDevice >> 8), bcdDevice & 0xFF,
+ udev->address);
+ } else {
+ snprintf(dst_ptr, dst_len, "%s %s, rev %x.%02x/"
+ "%x.%02x, addr %d", udev->manufacturer, udev->product,
+ (bcdUSB >> 8), bcdUSB & 0xFF,
+ (bcdDevice >> 8), bcdDevice & 0xFF,
+ udev->address);
+ }
+}
+
+#if USB_VERBOSE
+/*
+ * Descriptions of of known vendors and devices ("products").
+ */
+struct usb_knowndev {
+ uint16_t vendor;
+ uint16_t product;
+ uint32_t flags;
+ const char *vendorname;
+ const char *productname;
+};
+
+#define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */
+
+#include "usbdevs.h"
+#include "usbdevs_data.h"
+#endif /* USB_VERBOSE */
+
+/*------------------------------------------------------------------------*
+ * usb2_check_strings
+ *
+ * This function checks the manufacturer and product strings and will
+ * fill in defaults for missing strings.
+ *------------------------------------------------------------------------*/
+static void
+usb2_check_strings(struct usb2_device *udev)
+{
+ struct usb2_device_descriptor *udd = &udev->ddesc;
+ const char *vendor;
+ const char *product;
+
+#if USB_VERBOSE
+ const struct usb_knowndev *kdp;
+
+#endif
+ uint16_t vendor_id;
+ uint16_t product_id;
+
+ usb2_trim_spaces(udev->manufacturer);
+ usb2_trim_spaces(udev->product);
+
+ if (udev->manufacturer[0]) {
+ vendor = udev->manufacturer;
+ } else {
+ vendor = NULL;
+ }
+
+ if (udev->product[0]) {
+ product = udev->product;
+ } else {
+ product = NULL;
+ }
+
+ vendor_id = UGETW(udd->idVendor);
+ product_id = UGETW(udd->idProduct);
+
+#if USB_VERBOSE
+ if (vendor == NULL || product == NULL) {
+
+ for (kdp = usb_knowndevs;
+ kdp->vendorname != NULL;
+ kdp++) {
+ if (kdp->vendor == vendor_id &&
+ (kdp->product == product_id ||
+ (kdp->flags & USB_KNOWNDEV_NOPROD) != 0))
+ break;
+ }
+ if (kdp->vendorname != NULL) {
+ if (vendor == NULL)
+ vendor = kdp->vendorname;
+ if (product == NULL)
+ product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ?
+ kdp->productname : NULL;
+ }
+ }
+#endif
+ if (vendor && *vendor) {
+ if (udev->manufacturer != vendor) {
+ strlcpy(udev->manufacturer, vendor,
+ sizeof(udev->manufacturer));
+ }
+ } else {
+ snprintf(udev->manufacturer,
+ sizeof(udev->manufacturer), "vendor 0x%04x", vendor_id);
+ }
+
+ if (product && *product) {
+ if (udev->product != product) {
+ strlcpy(udev->product, product,
+ sizeof(udev->product));
+ }
+ } else {
+ snprintf(udev->product,
+ sizeof(udev->product), "product 0x%04x", product_id);
+ }
+}
+
+/*
+ * Returns:
+ * See: USB_MODE_XXX
+ */
+uint8_t
+usb2_get_mode(struct usb2_device *udev)
+{
+ return (udev->flags.usb2_mode);
+}
+
+/*
+ * Returns:
+ * See: USB_SPEED_XXX
+ */
+uint8_t
+usb2_get_speed(struct usb2_device *udev)
+{
+ return (udev->speed);
+}
+
+uint32_t
+usb2_get_isoc_fps(struct usb2_device *udev)
+{
+ ; /* indent fix */
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ return (1000);
+ default:
+ return (8000);
+ }
+}
+
+struct usb2_device_descriptor *
+usb2_get_device_descriptor(struct usb2_device *udev)
+{
+ if (udev == NULL)
+ return (NULL); /* be NULL safe */
+ return (&udev->ddesc);
+}
+
+struct usb2_config_descriptor *
+usb2_get_config_descriptor(struct usb2_device *udev)
+{
+ if (udev == NULL)
+ return (NULL); /* be NULL safe */
+ return (udev->cdesc);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_test_quirk - test a device for a given quirk
+ *
+ * Return values:
+ * 0: The USB device does not have the given quirk.
+ * Else: The USB device has the given quirk.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_test_quirk(const struct usb2_attach_arg *uaa, uint16_t quirk)
+{
+ uint8_t found;
+
+ found = (usb2_test_quirk_p) (&uaa->info, quirk);
+ return (found);
+}
+
+struct usb2_interface_descriptor *
+usb2_get_interface_descriptor(struct usb2_interface *iface)
+{
+ if (iface == NULL)
+ return (NULL); /* be NULL safe */
+ return (iface->idesc);
+}
+
+uint8_t
+usb2_get_interface_altindex(struct usb2_interface *iface)
+{
+ return (iface->alt_index);
+}
+
+uint8_t
+usb2_get_bus_index(struct usb2_device *udev)
+{
+ return ((uint8_t)device_get_unit(udev->bus->bdev));
+}
+
+uint8_t
+usb2_get_device_index(struct usb2_device *udev)
+{
+ return (udev->device_index);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_notify_addq
+ *
+ * This function will generate events for dev.
+ *------------------------------------------------------------------------*/
+static void
+usb2_notify_addq(const char *type, struct usb2_device *udev)
+{
+ char *data = NULL;
+ struct malloc_type *mt;
+
+ mtx_lock(&malloc_mtx);
+ mt = malloc_desc2type("bus"); /* XXX M_BUS */
+ mtx_unlock(&malloc_mtx);
+ if (mt == NULL)
+ return;
+
+ data = malloc(512, mt, M_NOWAIT);
+ if (data == NULL)
+ return;
+
+ /* String it all together. */
+ if (udev->parent_hub) {
+ snprintf(data, 1024,
+ "%s"
+ "ugen%u.%u "
+ "vendor=0x%04x "
+ "product=0x%04x "
+ "devclass=0x%02x "
+ "devsubclass=0x%02x "
+ "sernum=\"%s\" "
+ "at "
+ "port=%u "
+ "on "
+ "ugen%u.%u\n",
+ type,
+ device_get_unit(udev->bus->bdev),
+ udev->device_index,
+ UGETW(udev->ddesc.idVendor),
+ UGETW(udev->ddesc.idProduct),
+ udev->ddesc.bDeviceClass,
+ udev->ddesc.bDeviceSubClass,
+ udev->serial,
+ udev->port_no,
+ device_get_unit(udev->bus->bdev),
+ udev->parent_hub->device_index);
+ } else {
+ snprintf(data, 1024,
+ "%s"
+ "ugen%u.%u "
+ "vendor=0x%04x "
+ "product=0x%04x "
+ "devclass=0x%02x "
+ "devsubclass=0x%02x "
+ "sernum=\"%s\" "
+ "at port=%u "
+ "on "
+ "%s\n",
+ type,
+ device_get_unit(udev->bus->bdev),
+ udev->device_index,
+ UGETW(udev->ddesc.idVendor),
+ UGETW(udev->ddesc.idProduct),
+ udev->ddesc.bDeviceClass,
+ udev->ddesc.bDeviceSubClass,
+ udev->serial,
+ udev->port_no,
+ device_get_nameunit(device_get_parent(udev->bus->bdev)));
+ }
+ devctl_queue_data(data);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_free_wrap
+ *
+ * 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.
+ *------------------------------------------------------------------------*/
+static void
+usb2_fifo_free_wrap(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t flag)
+{
+ struct usb2_fifo *f;
+ uint16_t i;
+
+ /*
+ * Free any USB FIFOs on the given interface:
+ */
+ for (i = 0; i != USB_FIFO_MAX; i++) {
+ f = udev->fifo[i];
+ if (f == NULL) {
+ continue;
+ }
+ /* Check if the interface index matches */
+ if (iface_index == f->iface_index) {
+ if (f->methods != &usb2_ugen_methods) {
+ /*
+ * Don't free any non-generic FIFOs in
+ * this case.
+ */
+ continue;
+ }
+ if ((f->dev_ep_index == 0) &&
+ (f->fs_xfer == NULL)) {
+ /* no need to free this FIFO */
+ continue;
+ }
+ } else if (iface_index == USB_IFACE_INDEX_ANY) {
+ if ((f->methods == &usb2_ugen_methods) &&
+ (f->dev_ep_index == 0) && (flag == 0) &&
+ (f->fs_xfer == NULL)) {
+ /* no need to free this FIFO */
+ continue;
+ }
+ } else {
+ /* no need to free this FIFO */
+ continue;
+ }
+ /* free this FIFO */
+ usb2_fifo_free(f);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_peer_can_wakeup
+ *
+ * Return values:
+ * 0: Peer cannot do resume signalling.
+ * Else: Peer can do resume signalling.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_peer_can_wakeup(struct usb2_device *udev)
+{
+ const struct usb2_config_descriptor *cdp;
+
+ cdp = udev->cdesc;
+ if ((cdp != NULL) && (udev->flags.usb2_mode == USB_MODE_HOST)) {
+ return (cdp->bmAttributes & UC_REMOTE_WAKEUP);
+ }
+ return (0); /* not supported */
+}
OpenPOWER on IntegriCloud