Example #1
0
static inline int usbhost_devdesc(FAR const struct usb_devdesc_s *devdesc,
                                  FAR struct usbhost_id_s *id)
{
  /* Clear the ID info */

  memset(id, 0, sizeof(struct usbhost_id_s));

  /* Pick off the class ID info */

  id->base     = devdesc->classid;
  id->subclass = devdesc->subclass;
  id->proto    = devdesc->protocol;

  /* Pick off the VID and PID as well (for vendor specfic devices) */

  id->vid = usbhost_getle16(devdesc->vendor);
  id->pid = usbhost_getle16(devdesc->product);

  uvdbg("class:%d subclass:%04x protocol:%04x vid:%d pid:%d\n",
        id->base, id->subclass, id->proto, id->vid, id->pid);
  return OK;
}
Example #2
0
int usbhost_enumerate(FAR struct usbhost_hubport_s *hport,
                      FAR struct usbhost_class_s **devclass)
{
  FAR struct usb_ctrlreq_s *ctrlreq = NULL;
  struct usbhost_id_s id;
  size_t maxlen;
  unsigned int cfglen;
  uint8_t maxpacketsize;
  uint8_t descsize;
  uint8_t funcaddr = 0;
  FAR uint8_t *buffer = NULL;
  int ret;

  DEBUGASSERT(hport != NULL && hport->drvr != NULL);

  /* Allocate descriptor buffers for use in this function.  We will need two:
   * One for the request and one for the data buffer.
   */

  ret = DRVR_ALLOC(hport->drvr, (FAR uint8_t **)&ctrlreq, &maxlen);
  if (ret < 0)
    {
      udbg("DRVR_ALLOC failed: %d\n", ret);
      return ret;
    }

  ret = DRVR_ALLOC(hport->drvr, &buffer, &maxlen);
  if (ret < 0)
    {
      udbg("DRVR_ALLOC failed: %d\n", ret);
      goto errout;
    }

  /* Pick an appropriate packet size for this device
   *
   * USB 2.0, Paragraph 5.5.3 "Control Transfer Packet Size Constraints"
   *
   *  "An endpoint for control transfers specifies the maximum data
   *   payload size that the endpoint can accept from or transmit to
   *   the bus. The allowable maximum control transfer data payload
   *   sizes for full-speed devices is 8, 16, 32, or 64 bytes; for
   *   high-speed devices, it is 64 bytes and for low-speed devices,
   *   it is 8 bytes. This maximum applies to the data payloads of the
   *   Data packets following a Setup..."
   */

  if (hport->speed == USB_SPEED_HIGH)
    {
      /* For high-speed, we must use 64 bytes */

      maxpacketsize = 64;
      descsize      = USB_SIZEOF_DEVDESC;
    }
  else
    {
      /* Eight will work for both low- and full-speed */

      maxpacketsize = 8;
      descsize      = 8;
    }

  /* Configure EP0 with the initial maximum packet size */

  DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, 0, hport->speed,
                    maxpacketsize);

  /* Read first bytes of the device descriptor */

  ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE;
  ctrlreq->req  = USB_REQ_GETDESCRIPTOR;
  usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8));
  usbhost_putle16(ctrlreq->index, 0);
  usbhost_putle16(ctrlreq->len, descsize);

  ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer);
  if (ret < 0)
    {
      udbg("ERROR: Failed to get device descriptor, length=%d: %d\n",
           descsize, ret);
      goto errout;
    }

  /* Extract the correct max packetsize from the device descriptor */

  maxpacketsize = ((struct usb_devdesc_s *)buffer)->mxpacketsize;
  uvdbg("maxpacksetsize: %d\n", maxpacketsize);

  /* And reconfigure EP0 with the correct maximum packet size */

  DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, 0, hport->speed,
                    maxpacketsize);

  /* Now read the full device descriptor (if we have not already done so) */

  if (descsize < USB_SIZEOF_DEVDESC)
    {
      ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE;
      ctrlreq->req  = USB_REQ_GETDESCRIPTOR;
      usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8));
      usbhost_putle16(ctrlreq->index, 0);
      usbhost_putle16(ctrlreq->len, USB_SIZEOF_DEVDESC);

      ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer);
      if (ret < 0)
        {
          udbg("ERROR: Failed to get device descriptor, length=%d: %d\n",
               USB_SIZEOF_DEVDESC, ret);
          goto errout;
        }
    }

  /* Get class identification information from the device descriptor.  Most
   * devices set this to USB_CLASS_PER_INTERFACE (zero) and provide the
   * identification information in the interface descriptor(s).  That allows
   * a device to support multiple, different classes.
   */

  (void)usbhost_devdesc((struct usb_devdesc_s *)buffer, &id);

  /* Assign a function address to the device connected to this port */

  funcaddr = usbhost_devaddr_create(hport);
  if (funcaddr < 0)
    {
      udbg("ERROR: usbhost_devaddr_create failed: %d\n", ret);
      goto errout;
    }

  /* Set the USB device address */

  ctrlreq->type = USB_REQ_DIR_OUT | USB_REQ_RECIPIENT_DEVICE;
  ctrlreq->req  = USB_REQ_SETADDRESS;
  usbhost_putle16(ctrlreq->value, (uint16_t)funcaddr);
  usbhost_putle16(ctrlreq->index, 0);
  usbhost_putle16(ctrlreq->len, 0);

  ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL);
  if (ret < 0)
    {
      udbg("ERROR: Failed to set address: %d\n");
      goto errout;
    }

  usleep(2*1000);

  /* Assign the function address to the port */

  DEBUGASSERT(hport->funcaddr == 0 && funcaddr != 0);
  hport->funcaddr = funcaddr;

  /* And reconfigure EP0 with the correct address */

  DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, hport->funcaddr,
                    hport->speed, maxpacketsize);

  /* Get the configuration descriptor (only), index == 0.  Should not be
   * hard-coded! More logic is needed in order to handle devices with
   * multiple configurations.
   */

  ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE;
  ctrlreq->req  = USB_REQ_GETDESCRIPTOR;
  usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_CONFIG << 8));
  usbhost_putle16(ctrlreq->index, 0);
  usbhost_putle16(ctrlreq->len, USB_SIZEOF_CFGDESC);

  ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer);
  if (ret < 0)
   {
      udbg("ERROR: Failed to get configuration descriptor, length=%d: %d\n",
           USB_SIZEOF_CFGDESC, ret);
      goto errout;
    }

  /* Extract the full size of the configuration data */

  cfglen = (unsigned int)usbhost_getle16(((struct usb_cfgdesc_s *)buffer)->totallen);
  uvdbg("sizeof config data: %d\n", cfglen);

  /* Get all of the configuration descriptor data, index == 0 (Should not be
   * hard-coded!)
   */

  ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE;
  ctrlreq->req  = USB_REQ_GETDESCRIPTOR;
  usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_CONFIG << 8));
  usbhost_putle16(ctrlreq->index, 0);
  usbhost_putle16(ctrlreq->len, cfglen);

  ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer);
  if (ret < 0)
    {
      udbg("ERROR: Failed to get configuration descriptor, length=%d: %d\n",
           cfglen, ret);
      goto errout;
    }

  /* Select device configuration 1 (Should not be hard-coded!) */

  ctrlreq->type = USB_REQ_DIR_OUT | USB_REQ_RECIPIENT_DEVICE;
  ctrlreq->req  = USB_REQ_SETCONFIGURATION;
  usbhost_putle16(ctrlreq->value, 1);
  usbhost_putle16(ctrlreq->index, 0);
  usbhost_putle16(ctrlreq->len, 0);

  ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL);
  if (ret < 0)
    {
      udbg("ERROR: Failed to set configuration: %d\n", ret);
      goto errout;
    }

  /* Was the class identification information provided in the device
   * descriptor? Or do we need to find it in the interface descriptor(s)?
   */

  if (id.base == USB_CLASS_PER_INTERFACE)
    {
      /* Get the class identification information for this device from the
       * interface descriptor(s).  Hmmm.. More logic is need to handle the
       * case of multiple interface descriptors.
       */

      ret = usbhost_configdesc(buffer, cfglen, &id);
      if (ret < 0)
        {
          udbg("ERROR: usbhost_configdesc failed: %d\n", ret);
          goto errout;
        }
    }

  /* Some devices may require some delay before initialization */

  usleep(100*1000);

  /* Parse the configuration descriptor and bind to the class instance for the
   * device.  This needs to be the last thing done because the class driver
   * will begin configuring the device.
   */

  ret = usbhost_classbind(hport, buffer, cfglen, &id, devclass);
  if (ret < 0)
    {
      udbg("ERROR: usbhost_classbind failed %d\n", ret);
    }

errout:
  if (ret < 0)
    {
      /* Release the device function address on any failure */

      usbhost_devaddr_destroy(hport, funcaddr);
      hport->funcaddr = 0;
    }

  /* Release temporary buffers in any event */

  if (buffer != NULL)
    {
      DRVR_FREE(hport->drvr, buffer);
    }

  if (ctrlreq)
    {
      DRVR_FREE(hport->drvr, (FAR uint8_t *)ctrlreq);
    }

  return ret;
}
Example #3
0
static void usbhost_hub_event(FAR void *arg)
{
  FAR struct usbhost_class_s *hubclass;
  FAR struct usbhost_hubport_s *hport;
  FAR struct usbhost_hubport_s *connport;
  FAR struct usbhost_hubpriv_s *priv;
  FAR struct usb_ctrlreq_s *ctrlreq;
  struct usb_portstatus_s portstatus;
  irqstate_t flags;
  uint16_t status;
  uint16_t change;
  uint16_t mask;
  uint16_t feat;
  uint8_t statuschange;
  int port;
  int ret;

  DEBUGASSERT(arg != NULL);
  hubclass = (FAR struct usbhost_class_s *)arg;
  priv     = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv;

  /* Has the hub been disconnected? */

  if (priv->disconnected)
    {
      uvdbg("Disconnected\n");
      return;
    }

  /* No.. then set up to process the hub event */

  DEBUGASSERT(priv->ctrlreq);
  ctrlreq = priv->ctrlreq;

  DEBUGASSERT(hubclass->hport);
  hport = hubclass->hport;

  statuschange = priv->buffer[0];
  uvdbg("StatusChange: %02x\n", statuschange);

  /* Check for status change on any port */

  for (port = 1; port <= priv->nports; port++)
    {
      /* Check if port status has changed */

      if ((statuschange & (1 << port)) == 0)
        {
          continue;
        }

      uvdbg("Port %d status change\n", port);

      /* Port status changed, check what happened */

      statuschange &= ~(1 << port);

      /* Read hub port status */

      ctrlreq->type = USB_REQ_DIR_IN | USBHUB_REQ_TYPE_PORT;
      ctrlreq->req  = USBHUB_REQ_GETSTATUS;
      usbhost_putle16(ctrlreq->value, 0);
      usbhost_putle16(ctrlreq->index, port);
      usbhost_putle16(ctrlreq->len, USB_SIZEOF_PORTSTS);

      ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq,
                        (FAR uint8_t *)&portstatus);
      if (ret < 0)
        {
          udbg("ERROR: Failed to read port %d status: %d\n", port, ret);
          continue;
        }

      status = usbhost_getle16(portstatus.status);
      change = usbhost_getle16(portstatus.change);

      /* First, clear all change bits */

      mask = 1;
      feat = USBHUB_PORT_FEAT_CCONNECTION;
      while (change)
        {
          if (change & mask)
            {
              ctrlreq->type = USBHUB_REQ_TYPE_PORT;
              ctrlreq->req  = USBHUB_REQ_CLEARFEATURE;
              usbhost_putle16(ctrlreq->value, feat);
              usbhost_putle16(ctrlreq->index, port);
              usbhost_putle16(ctrlreq->len, 0);

              ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL);
              if (ret < 0)
                {
                  udbg("ERROR: Failed to clear port %d change mask %04x: %d\n",
                       port, mask, ret);
                }

              change &= (~mask);
            }

          mask <<= 1;
          feat++;
        }

      change = usbhost_getle16(portstatus.change);

      /* Handle connect or disconnect, no power management */

      if ((change & USBHUB_PORT_STAT_CCONNECTION) != 0)
        {
          uint16_t debouncetime = 0;
          uint16_t debouncestable = 0;
          uint16_t connection = 0xffff;

          uvdbg("Port %d status %04x change %04x\n", port, status, change);

          /* Debounce */

          while (debouncetime < 1500)
            {
              ctrlreq->type = USB_REQ_DIR_IN | USBHUB_REQ_TYPE_PORT;
              ctrlreq->req  = USBHUB_REQ_GETSTATUS;
              usbhost_putle16(ctrlreq->value, 0);
              usbhost_putle16(ctrlreq->index, port);
              usbhost_putle16(ctrlreq->len, USB_SIZEOF_PORTSTS);

              ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq,
                                (FAR uint8_t *)&portstatus);
              if (ret < 0)
                {
                  udbg("ERROR: Failed to get port %d status: %d\n", port, ret);
                  break;
                }

              status = usbhost_getle16(portstatus.status);
              change = usbhost_getle16(portstatus.change);

              if ((change & USBHUB_PORT_STAT_CCONNECTION) == 0 &&
                  (status & USBHUB_PORT_STAT_CONNECTION)  == connection)
                {
                  debouncestable += 25;
                  if (debouncestable >= 100)
                    {
                      uvdbg("Port %d debouncestable=%d\n", port, debouncestable);
                      break;
                    }
                }
              else
                {
                  debouncestable = 0;
                  connection = status & USBHUB_PORT_STAT_CONNECTION;
                }

              if ((change & USBHUB_PORT_STAT_CCONNECTION) != 0)
                {
                  ctrlreq->type = USBHUB_REQ_TYPE_PORT;
                  ctrlreq->req  = USBHUB_REQ_CLEARFEATURE;
                  usbhost_putle16(ctrlreq->value, USBHUB_PORT_FEAT_CCONNECTION);
                  usbhost_putle16(ctrlreq->index, port);
                  usbhost_putle16(ctrlreq->len, 0);

                  (void)DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL);
                }

              debouncetime += 25;
              usleep(25*1000);
            }

          if (ret < 0 || debouncetime >= 1500)
            {
              udbg("ERROR: Failed to debounce port %d: %d\n", port, ret);
              continue;
            }

          if ((status & USBHUB_PORT_STAT_CONNECTION) != 0)
            {
              /* Device connected to a port on the hub */

              uvdbg("Connection on port %d\n", port);

              ctrlreq->type = USBHUB_REQ_TYPE_PORT;
              ctrlreq->req  = USBHUB_REQ_SETFEATURE;
              usbhost_putle16(ctrlreq->value, USBHUB_PORT_FEAT_RESET);
              usbhost_putle16(ctrlreq->index, port);
              usbhost_putle16(ctrlreq->len, 0);

              ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL);
              if (ret < 0)
                {
                  udbg("ERROR: Failed to reset port %d: %d\n", port, ret);
                  continue;
                }

              usleep(100*1000);

              ctrlreq->type = USB_REQ_DIR_IN | USBHUB_REQ_TYPE_PORT;
              ctrlreq->req  = USBHUB_REQ_GETSTATUS;
              usbhost_putle16(ctrlreq->value, 0);
              usbhost_putle16(ctrlreq->index, port);
              usbhost_putle16(ctrlreq->len, USB_SIZEOF_PORTSTS);

              ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq,
                                (FAR uint8_t *)&portstatus);
              if (ret < 0)
                {
                  udbg("ERROR: Failed to get port %d status: %d\n", port, ret);
                  continue;
                }

              status = usbhost_getle16(portstatus.status);
              change = usbhost_getle16(portstatus.change);

              uvdbg("port %d status %04x change %04x after reset\n",
                    port, status, change);

              if ((status & USBHUB_PORT_STAT_RESET)  == 0 &&
                  (status & USBHUB_PORT_STAT_ENABLE) != 0)
                {
                  if ((change & USBHUB_PORT_STAT_CRESET) != 0)
                    {
                      ctrlreq->type = USBHUB_REQ_TYPE_PORT;
                      ctrlreq->req  = USBHUB_REQ_CLEARFEATURE;
                      usbhost_putle16(ctrlreq->value, USBHUB_PORT_FEAT_CRESET);
                      usbhost_putle16(ctrlreq->index, port);
                      usbhost_putle16(ctrlreq->len, 0);

                      (void)DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL);
                    }

                  connport = &priv->hport[port];
                  if ((status & USBHUB_PORT_STAT_HIGH_SPEED) != 0)
                    {
                      connport->speed = USB_SPEED_HIGH;
                    }
                  else if ((status & USBHUB_PORT_STAT_LOW_SPEED) != 0)
                    {
                      connport->speed = USB_SPEED_LOW;
                    }
                  else
                    {
                      connport->speed = USB_SPEED_FULL;
                    }

                  /* Activate the hub port by assigning it a control endpoint. */

                  ret = usbhost_hport_activate(connport);
                  if (ret < 0)
                    {
                      udbg("ERROR: usbhost_hport_activate failed: %d\n", ret);
                    }
                  else
                    {
                      /* Inform waiters that a new device has been connected */

                      ret = DRVR_CONNECT(connport->drvr, connport, true);
                      if (ret < 0)
                        {
                          udbg("ERROR: DRVR_CONNECT failed: %d\n", ret);
                          usbhost_hport_deactivate(connport);
                        }
                    }
                }
              else
                {
                  udbg("ERROR: Failed to enable port %d\n", port);
                  continue;
                }
            }
          else
            {
              /* Device disconnected from a port on the hub.  Release port
               * resources.
               */

              uvdbg("Disconnection on port %d\n", port);

              /* Free any devices classes connect on this hub port */

              connport = &priv->hport[port];
              if (connport->devclass != NULL)
                {
                  CLASS_DISCONNECTED(connport->devclass);
                  connport->devclass = NULL;
                }

              /* Free any resources used by the hub port */

              usbhost_hport_deactivate(connport);
            }
        }
      else if (change)
        {
          udbg("WARNING: status %04x change %04x not handled\n", status, change);
        }
    }

  /* Check for hub status change */

  if ((statuschange & 1) != 0)
    {
      /* Hub status changed */

      udbg("WARNING: Hub status changed, not handled\n");
    }

  /* The preceding sequence of events may take a significant amount of
   * time and it is possible that the hub may have been removed while this
   * logic operated.  In any event, we will get here after several failures.
   * But we do not want to schedule another hub event if the hub has been
   * removed.
   */

  flags = irqsave();
  if (!priv->disconnected)
    {
      /* Wait for the next hub event */

      ret = DRVR_ASYNCH(hport->drvr, priv->intin, (FAR uint8_t *)priv->buffer,
                        INTIN_BUFSIZE, usbhost_callback, hubclass);
      if (ret < 0)
        {
          udbg("ERROR: Failed to queue interrupt endpoint: %d\n", ret);
        }
    }

  irqrestore(flags);
}
Example #4
0
static inline int usbhost_hubdesc(FAR struct usbhost_class_s *hubclass)
{
  FAR struct usbhost_hubpriv_s *priv;
  FAR struct usbhost_hubport_s *hport;
  FAR struct usb_ctrlreq_s *ctrlreq;
  struct usb_hubdesc_s hubdesc;
  uint16_t hubchar;
  int ret;

  uvdbg("Read hub descriptor\n");

  DEBUGASSERT(hubclass != NULL);
  priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv;

  DEBUGASSERT(hubclass->hport);
  hport  = hubclass->hport;

  /* Get the hub descriptor */

  ctrlreq = priv->ctrlreq;
  DEBUGASSERT(ctrlreq);

  ctrlreq->type = USB_REQ_DIR_IN | USBHUB_REQ_TYPE_HUB;
  ctrlreq->req  = USBHUB_REQ_GETDESCRIPTOR;
  usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_HUB << 8));
  usbhost_putle16(ctrlreq->index, 0);
  usbhost_putle16(ctrlreq->len, USB_SIZEOF_HUBDESC);

  ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, (FAR uint8_t *)&hubdesc);
  if (ret < 0)
    {
      udbg("ERROR: Failed to read hub descriptor: %d\n", ret);
      return ret;
    }

  priv->nports      = hubdesc.nports;

  hubchar           = usbhost_getle16(hubdesc.characteristics);
  priv->lpsm        = (hubchar & USBHUB_CHAR_LPSM_MASK) >> USBHUB_CHAR_LPSM_SHIFT;
  priv->compounddev = (hubchar & USBHUB_CHAR_COMPOUND) ? true : false;
  priv->ocmode      = (hubchar & USBHUB_CHAR_OCPM_MASK) >> USBHUB_CHAR_OCPM_SHIFT;
  priv->indicator   = (hubchar & USBHUB_CHAR_PORTIND) ? true : false;

  priv->pwrondelay  = (2 * hubdesc.pwrondelay);
  priv->ctrlcurrent = hubdesc.ctrlcurrent;

  uvdbg("Hub Descriptor:\n");
  uvdbg("  bDescLength:         %d\n", hubdesc.len);
  uvdbg("  bDescriptorType:     0x%02x\n", hubdesc.type);
  uvdbg("  bNbrPorts:           %d\n", hubdesc.nports);
  uvdbg("  wHubCharacteristics: 0x%04x\n", usbhost_getle16(hubdesc.characteristics));
  uvdbg("    lpsm:              %d\n", priv->lpsm);
  uvdbg("    compounddev:       %s\n", priv->compounddev ? "TRUE" : "FALSE");
  uvdbg("    ocmode:            %d\n", priv->ocmode);
  uvdbg("    indicator:         %s\n", priv->indicator ? "TRUE" : "FALSE");
  uvdbg("  bPwrOn2PwrGood:      %d\n", hubdesc.pwrondelay);
  uvdbg("    pwrondelay:        %d\n", priv->pwrondelay);
  uvdbg("  bHubContrCurrent:    %d\n", hubdesc.ctrlcurrent);
  uvdbg("  DeviceRemovable:     %d\n", hubdesc.devattached);
  uvdbg("  PortPwrCtrlMask:     %d\n", hubdesc.pwrctrlmask);

  return OK;
}
Example #5
0
static inline int usbhost_cfgdesc(FAR struct usbhost_class_s *hubclass,
                                  FAR const uint8_t *configdesc, int desclen)
{
  FAR struct usbhost_hubpriv_s *priv;
  FAR struct usbhost_hubport_s *hport;
  FAR struct usb_cfgdesc_s *cfgdesc;
  FAR struct usb_desc_s *desc;
  FAR struct usbhost_epdesc_s intindesc;
  int remaining;
  uint8_t found = 0;
  int ret;

  DEBUGASSERT(hubclass != NULL);
  priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv;

  DEBUGASSERT(hubclass->hport);
  hport = hubclass->hport;

  DEBUGASSERT(configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s));

  /* Initialize the interrupt IN endpoint information (only to prevent
   * compiler complaints)
   */

  intindesc.hport        = hport;
  intindesc.addr         = 0;
  intindesc.in           = true;
  intindesc.xfrtype      = USB_EP_ATTR_XFER_INT;
  intindesc.interval     = 0;
  intindesc.mxpacketsize = 0;

  /* Verify that we were passed a configuration descriptor */

  cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
  if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
    {
      return -EINVAL;
    }

  /* Get the total length of the configuration descriptor (little endian).
   * It might be a good check to get the number of interfaces here too.
   */

  remaining = (int)usbhost_getle16(cfgdesc->totallen);

  /* Skip to the next entry descriptor */

  configdesc += cfgdesc->len;
  remaining  -= cfgdesc->len;

  /* Loop where there are more descriptors to examine */

  while (remaining >= sizeof(struct usb_desc_s))
    {
      /* What is the next descriptor? */

      desc = (FAR struct usb_desc_s *)configdesc;
      switch (desc->type)
        {
        /* Interface descriptor. We really should get the number of endpoints
         * from this descriptor too.
         */

        case USB_DESC_TYPE_INTERFACE:
          {
            FAR struct usb_ifdesc_s *ifdesc =
              (FAR struct usb_ifdesc_s *)configdesc;

            uvdbg("Interface descriptor\n");
            DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);

            /* Save the interface number and mark ONLY the interface found */

            priv->ifno = ifdesc->ifno;
            found      = USBHOST_IFFOUND;
          }
          break;

        /* Endpoint descriptor.  Here, we expect one interrupt IN endpoints. */

        case USB_DESC_TYPE_ENDPOINT:
          {
            FAR struct usb_epdesc_s *epdesc =
              (FAR struct usb_epdesc_s *)configdesc;

            uvdbg("Endpoint descriptor\n");
            DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);

            /* Check for an interrupt endpoint. */

            if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) ==
                USB_EP_ATTR_XFER_INT)
              {
                /* Yes.. it is a interrupt endpoint.  IN or OUT? */

                if (USB_ISEPOUT(epdesc->addr))
                  {
                    /* It is an OUT interrupt endpoint. Ignore */

                    uvdbg("Interrupt OUT EP addr:%d mxpacketsize:%d\n",
                          (epdesc->addr & USB_EP_ADDR_NUMBER_MASK),
                          usbhost_getle16(epdesc->mxpacketsize));
                  }
                else
                  {
                    /* It is an IN interrupt endpoint. */

                    found |= USBHOST_EPINFOUND;

                    /* Save the interrupt IN endpoint information */

                    intindesc.addr         = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
                    intindesc.interval     = epdesc->interval;
                    intindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);

                    uvdbg("Interrupt IN EP: addr=%d interval=%d mxpacketsize=%d\n",
                          intindesc.addr, intindesc.interval, intindesc.mxpacketsize);
                  }
              }
          }
          break;

        /* Other descriptors are just ignored for now */

        default:
          break;
        }

      /* If we found everything we need with this interface, then break out
       * of the loop early.
       */

      if (found == USBHOST_ALLFOUND)
        {
          break;
        }

      /* Increment the address of the next descriptor */

      configdesc += desc->len;
      remaining  -= desc->len;
    }

  /* Sanity checking... did we find all of things that we need? */

  if (found != USBHOST_ALLFOUND)
    {
      ulldbg("ERROR: Found IF=%s EPIN=%s\n",
             (found & USBHOST_IFFOUND) != 0  ? "YES" : "NO",
             (found & USBHOST_EPINFOUND) != 0 ? "YES" : "NO");
      return -EINVAL;
    }

  /* We are good... Allocate the interrupt IN endpoint */

  ret = DRVR_EPALLOC(hport->drvr, &intindesc, &priv->intin);
  if (ret < 0)
    {
      udbg("ERROR: Failed to allocate Interrupt IN endpoint: %d\n", ret);
      (void)DRVR_EPFREE(hport->drvr, priv->intin);
      return ret;
    }

  ullvdbg("Endpoint allocated\n");
  return OK;
}
static inline uint32_t usbhost_getle32(const uint8_t *val)
{
  /* Little endian means LS halfword first in byte stream */

  return (uint32_t)usbhost_getle16(&val[2]) << 16 | (uint32_t)usbhost_getle16(val);
}
static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
                                  FAR const uint8_t *configdesc, int desclen)
{
  FAR struct usbhost_hubport_s *hport;
  FAR struct usb_cfgdesc_s *cfgdesc;
  FAR struct usb_desc_s *desc;
  FAR struct usbhost_epdesc_s bindesc;
  FAR struct usbhost_epdesc_s boutdesc;
  int remaining;
  uint8_t found = 0;
  int ret;

  DEBUGASSERT(priv != NULL && priv->usbclass.hport &&
              configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s));
  hport = priv->usbclass.hport;

  /* Verify that we were passed a configuration descriptor */

  cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
  if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
    {
      return -EINVAL;
    }

  /* Get the total length of the configuration descriptor (little endian).
   * It might be a good check to get the number of interfaces here too.
   */

  remaining = (int)usbhost_getle16(cfgdesc->totallen);

  /* Skip to the next entry descriptor */

  configdesc += cfgdesc->len;
  remaining  -= cfgdesc->len;

  /* Loop where there are more dscriptors to examine */

  while (remaining >= sizeof(struct usb_desc_s))
    {
      /* What is the next descriptor? */

      desc = (FAR struct usb_desc_s *)configdesc;
      switch (desc->type)
        {
        /* Interface descriptor. We really should get the number of endpoints
         * from this descriptor too.
         */

        case USB_DESC_TYPE_INTERFACE:
          {
            FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc;

            uvdbg("Interface descriptor\n");
            DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);

            /* Save the interface number and mark ONLY the interface found */

            priv->ifno = ifdesc->ifno;
            found      = USBHOST_IFFOUND;
          }
          break;

        /* Endpoint descriptor.  Here, we expect two bulk endpoints, an IN
         * and an OUT.
         */

        case USB_DESC_TYPE_ENDPOINT:
          {
            FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc;

            uvdbg("Endpoint descriptor\n");
            DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);

            /* Check for a bulk endpoint. */

            if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_BULK)
              {
                /* Yes.. it is a bulk endpoint.  IN or OUT? */

                if (USB_ISEPOUT(epdesc->addr))
                  {
                    /* It is an OUT bulk endpoint.  There should be only one
                     * bulk OUT endpoint.
                     */

                    if ((found & USBHOST_BOUTFOUND) != 0)
                      {
                        /* Oops.. more than one endpoint.  We don't know
                         * what to do with this.
                         */

                        return -EINVAL;
                      }
                    found |= USBHOST_BOUTFOUND;

                    /* Save the bulk OUT endpoint information */

                    boutdesc.hport        = hport;
                    boutdesc.addr         = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
                    boutdesc.in           = false;
                    boutdesc.xfrtype      = USB_EP_ATTR_XFER_BULK;
                    boutdesc.interval     = epdesc->interval;
                    boutdesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
                    uvdbg("Bulk OUT EP addr:%d mxpacketsize:%d\n",
                          boutdesc.addr, boutdesc.mxpacketsize);
                  }
                else
                  {
                    /* It is an IN bulk endpoint.  There should be only one
                     * bulk IN endpoint.
                     */

                    if ((found & USBHOST_BINFOUND) != 0)
                      {
                        /* Oops.. more than one endpoint.  We don't know
                         * what to do with this.
                         */

                        return -EINVAL;
                      }

                    found |= USBHOST_BINFOUND;

                    /* Save the bulk IN endpoint information */

                    bindesc.hport        = hport;
                    bindesc.addr         = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
                    bindesc.in           = 1;
                    bindesc.xfrtype      = USB_EP_ATTR_XFER_BULK;
                    bindesc.interval     = epdesc->interval;
                    bindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
                    uvdbg("Bulk IN EP addr:%d mxpacketsize:%d\n",
                          bindesc.addr, bindesc.mxpacketsize);
                  }
              }
          }
          break;

        /* Other descriptors are just ignored for now */

        default:
          break;
        }

      /* If we found everything we need with this interface, then break out
       * of the loop early.
       */

      if (found == USBHOST_ALLFOUND)
        {
          break;
        }

      /* Increment the address of the next descriptor */

      configdesc += desc->len;
      remaining  -= desc->len;
    }

  /* Sanity checking... did we find all of things that we need? */

  if (found != USBHOST_ALLFOUND)
    {
      ulldbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n",
             (found & USBHOST_IFFOUND) != 0  ? "YES" : "NO",
             (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO",
             (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO");
      return -EINVAL;
    }

  /* We are good... Allocate the endpoints */

  ret = DRVR_EPALLOC(hport->drvr, &boutdesc, &priv->epout);
  if (ret < 0)
    {
      udbg("ERROR: Failed to allocate Bulk OUT endpoint\n");
      return ret;
    }

  ret = DRVR_EPALLOC(hport->drvr, &bindesc, &priv->epin);
  if (ret < 0)
    {
      udbg("ERROR: Failed to allocate Bulk IN endpoint\n");
      (void)DRVR_EPFREE(hport->drvr, priv->epout);
      return ret;
    }

  ullvdbg("Endpoints allocated\n");
  return OK;
}