static int usbhost_waiter(int argc, char *argv[])
{
    struct usbhost_hubport_s *hport;

    uvdbg("Running\n");
    for (;;)
    {
        /* Wait for the device to change state */

        DEBUGVERIFY(CONN_WAIT(g_usbconn, &hport));
        uvdbg("%s\n", hport->connected ? "connected" : "disconnected");

        /* Did we just become connected? */

        if (hport->connected)
        {
            /* Yes.. enumerate the newly connected device */

            (void)CONN_ENUMERATE(g_usbconn, hport);
        }
    }

    /* Keep the compiler from complaining */

    return 0;
}
Exemple #2
0
int stm32_usbhost_initialize(void)
{
  int pid;
  int ret;

  /* First, register all of the class drivers needed to support the drivers
   * that we care about:
   */

  uvdbg("Register class drivers\n");
  ret = usbhost_storageinit();
  if (ret != OK)
    {
      udbg("Failed to register the mass storage class\n");
    }

  /* Then get an instance of the USB host interface */

  uvdbg("Initialize USB host\n");
  g_drvr = usbhost_initialize(0);
  if (g_drvr)
    {
      /* Start a thread to handle device connection. */

      uvdbg("Start usbhost_waiter\n");

      pid = TASK_CREATE("usbhost", CONFIG_USBHOST_DEFPRIO,
                        CONFIG_USBHOST_STACKSIZE,
                        (main_t)usbhost_waiter, (FAR char * const *)NULL);
      return pid < 0 ? -ENOEXEC : OK;
    }

  return -ENODEV;
}
Exemple #3
0
static int usbhost_waiter(int argc, char *argv[])
{
  bool connected = false;
  int ret;

  uvdbg("Running\n");
  for (;;)
    {
      /* Wait for the device to change state */

      ret = DRVR_WAIT(g_drvr, connected);
      DEBUGASSERT(ret == OK);

      connected = !connected;
      uvdbg("%s\n", connected ? "connected" : "disconnected");

      /* Did we just become connected? */

      if (connected)
        {
          /* Yes.. enumerate the newly connected device */

          (void)DRVR_ENUMERATE(g_drvr);
        }
    }

  /* Keep the compiler from complaining */

  return 0;
}
Exemple #4
0
int stm32_usbhost_initialize(void)
{
  int pid;
  int ret;

  /* First, register all of the class drivers needed to support the drivers
   * that we care about:
   */

  uvdbg("Register class drivers\n");

#ifdef CONFIG_USBHOST_HUB
  /* Initialize USB hub class support */

  ret = usbhost_hub_initialize();
  if (ret < 0)
    {
      udbg("ERROR: usbhost_hub_initialize failed: %d\n", ret);
    }
#endif

#ifdef CONFIG_USBHOST_MSC
  /* Register the USB mass storage class class */

  ret = usbhost_msc_initialize();
  if (ret != OK)
    {
      udbg("ERROR: Failed to register the mass storage class: %d\n", ret);
    }
#endif

#ifdef CONFIG_USBHOST_CDCACM
  /* Register the CDC/ACM serial class */

  ret = usbhost_cdcacm_initialize();
  if (ret != OK)
    {
      udbg("ERROR: Failed to register the CDC/ACM serial class: %d\n", ret);
    }
#endif

  /* Then get an instance of the USB host interface */

  uvdbg("Initialize USB host\n");
  g_usbconn = stm32_otghshost_initialize(0);
  if (g_usbconn)
    {
      /* Start a thread to handle device connection. */

      uvdbg("Start usbhost_waiter\n");

      pid = task_create("usbhost", CONFIG_STM32F429IDISCO_USBHOST_PRIO,
                        CONFIG_STM32F429IDISCO_USBHOST_STACKSIZE,
                        (main_t)usbhost_waiter, (FAR char * const *)NULL);
      return pid < 0 ? -ENOEXEC : OK;
    }

  return -ENODEV;
}
static inline int usbhost_configdesc(const uint8_t *configdesc, int cfglen,
                                     struct usbhost_id_s *id)
{
  FAR struct usb_cfgdesc_s *cfgdesc;
  FAR struct usb_ifdesc_s *ifdesc;
  int remaining;

  DEBUGASSERT(configdesc != NULL && cfglen >= USB_SIZEOF_CFGDESC);

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

  cfgdesc = (struct usb_cfgdesc_s *)configdesc;
  uvdbg("cfg len:%d total len:%d\n", cfgdesc->len, cfglen);

  if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
    {
      return -EINVAL;
    }

  /* Skip to the next entry descriptor */

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

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

  memset(id, 0, sizeof(FAR struct usb_desc_s));
  while (remaining >= sizeof(struct usb_desc_s))
    {
      /* What is the next descriptor? Is it an interface descriptor? */

      ifdesc = (struct usb_ifdesc_s *)configdesc;
      if (ifdesc->type == USB_DESC_TYPE_INTERFACE)
        {
          /* Yes, extract the class information from the interface descriptor.
           * Typically these values are zero meaning that the "real" ID
           * information resides in the device descriptor.
           */

          DEBUGASSERT(remaining >= sizeof(struct usb_ifdesc_s));
          id->base     = ifdesc->classid;
          id->subclass = ifdesc->subclass;
          id->proto    = ifdesc->protocol;
          uvdbg("class:%d subclass:%d protocol:%d\n",
                id->base, id->subclass, id->proto);
          return OK;
        }

     /* Increment the address of the next descriptor */

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

  return -ENOENT;
}
static int usbhost_disconnected(struct usbhost_class_s *hubclass)
{
  FAR struct usbhost_hubpriv_s *priv;
  irqstate_t flags;
  int ret;

  uvdbg("Disconnected\n");

  /* Execute the disconnect action from the worker thread. */

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

  /* Mark the driver disconnected.  This will cause the callback to ignore
   * any subsequent completions of asynchronous transfers.
   */

  flags = irqsave();
  priv->disconnected = true;

  /* Cancel any pending work. There may be pending HUB work associated with
   * hub interrupt pipe events.  That work may be lost by this action.
   */

  (void)work_cancel(LPWORK, &priv->work);

  /* Schedule the disconnection work */

  ret = work_queue(LPWORK, &priv->work,
                   (worker_t)usbhost_disconnect_event, hubclass, 0);
  irqrestore(flags);
  return ret;
}
static void usbhost_hport_deactivate(FAR struct usbhost_hubport_s *hport)
{
  uvdbg("Deactivating: %s port %d\n",
        ROOTHUB(hport) ? "Root" : "Hub", hport->port);

  /* Don't free the control pipe of root hub ports! */

  if (!ROOTHUB(hport) && hport->ep0 != NULL)
    {
      /* Free the control endpoint */

      DRVR_EPFREE(hport->drvr, hport->ep0);
      hport->ep0 = NULL;
    }

  /* Free the function address if one has been assigned */

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

  /* If this is a downstream hub port, then there should be no class driver
   * associated wit it.
   */

  DEBUGASSERT(ROOTHUB(hport) || hport->devclass == NULL);
}
static inline int usbhost_classbind(FAR struct usbhost_hubport_s *hport,
                                    const uint8_t *configdesc, int desclen,
                                    struct usbhost_id_s *id,
                                    FAR struct usbhost_class_s **usbclass)
{
  FAR struct usbhost_class_s *devclass;
  FAR const struct usbhost_registry_s *reg;
  int ret = -EINVAL;

  /* Is there is a class implementation registered to support this device. */

  reg = usbhost_findclass(id);
  uvdbg("usbhost_findclass: %p\n", reg);
  if (reg != NULL)
    {
      /* Yes.. there is a class for this device.  Get an instance of
       * its interface.
       */

      ret = -ENOMEM;
      devclass = CLASS_CREATE(reg, hport, id);
      uvdbg("CLASS_CREATE: %p\n", devclass);
      if (devclass != NULL)
        {
          /* Then bind the newly instantiated class instance */

          ret = CLASS_CONNECT(devclass, configdesc, desclen);
          if (ret < 0)
            {
              /* On failures, call the class disconnect method which
               * should then free the allocated devclass instance.
               */

              udbg("CLASS_CONNECT failed: %d\n", ret);
              CLASS_DISCONNECTED(devclass);
            }
          else
            {
              *usbclass = devclass;
            }
        }
    }

  uvdbg("Returning: %d\n", ret);
  return ret;
}
const struct usbhost_registry_s *usbhost_findclass(const struct usbhost_id_s *id)
{
  struct usbhost_registry_s *usbclass;
  irqstate_t flags;
  int ndx;

  DEBUGASSERT(id);
  uvdbg("Looking for class:%d subclass:%d protocol:%d vid:%04x pid:%04x\n",
        id->base, id->subclass, id->proto, id->vid, id->pid);

  /* g_classregistry is a singly-linked list of class ID information added by
   * calls to usbhost_registerclass().  Since this list is accessed from USB
   * host controller interrupt handling logic, accesses to this list must be
   * protected by disabling interrupts.
   */

  flags = irqsave();

  /* Examine each register class in the linked list */

  for (usbclass = g_classregistry; usbclass; usbclass = usbclass->flink)
    {
      /* If the registered class supports more than one ID, subclass, or
       * protocol, then try each.
       */

     uvdbg("Checking class:%p nids:%d\n", usbclass, usbclass->nids);
     for (ndx = 0; ndx < usbclass->nids; ndx++)
        {
          /* Did we find a matching ID? */

          if (usbhost_idmatch(&usbclass->id[ndx], id))
            {
              /* Yes.. restore interrupts and return the class info */

              irqrestore(flags);
              return usbclass;
            }
        }
    }

  /* Not found... restore interrupts and return NULL */

  irqrestore(flags);
  return NULL;
}
Exemple #10
0
void sam_usbhost_vbusdrive(int rhport, bool enable)
{
  pio_pinset_t pinset = 0;

  uvdbg("RHPort%d: enable=%d\n", rhport+1, enable);

  /* Pick the PIO configuration associated with the selected root hub port */

  switch (rhport)
    {
    case SAM_RHPORT1:
#ifndef CONFIG_SAMA5_UHPHS_RHPORT1
      udbg("ERROR: RHPort1 is not available in this configuration\n");
      return;
#else
      pinset = PIO_USBA_VBUS_ENABLE;
      break;
#endif

    case SAM_RHPORT2:
#ifndef CONFIG_SAMA5_UHPHS_RHPORT2
      udbg("ERROR: RHPort2 is not available in this configuration\n");
      return;
#else
      pinset = PIO_USBB_VBUS_ENABLE;
      break;
#endif

    case SAM_RHPORT3:
#ifndef CONFIG_SAMA5_UHPHS_RHPORT3
      udbg("ERROR: RHPort3 is not available in this configuration\n");
      return;
#else
      pinset = PIO_USBC_VBUS_ENABLE;
      break;
#endif

    default:
      udbg("ERROR: RHPort%d is not supported\n", rhport+1);
      return;
    }

  /* Then enable or disable VBUS power */

  if (enable)
    {
      /* Enable the Power Switch by driving the enable pin low */

      sam_piowrite(pinset, false);
    }
  else
    {
      /* Disable the Power Switch by driving the enable pin high */

      sam_piowrite(pinset, true);
    }
}
Exemple #11
0
static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
{
  FAR struct usbhost_state_s *priv;

  DEBUGASSERT(!up_interrupt_context());
  priv = (FAR struct usbhost_state_s *)kmalloc(sizeof(struct usbhost_state_s));
  uvdbg("Allocated: %p\n", priv);;
  return priv;
}
static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass)
{
  DEBUGASSERT(usbclass != NULL);

  /* Free the class instance (perhaps calling sched_kmm_free() in case we are
   * executing from an interrupt handler.
   */

  uvdbg("Freeing: %p\n", usbclass);
  kmm_free(usbclass);
}
Exemple #13
0
static int usbhost_waiter(struct usbhost_connection_s *dev)
#endif
{
  bool connected[SAM_OHCI_NRHPORT] = {false, false, false};
  int rhpndx;
  int ret;

  uvdbg("%s Waiter Running\n", hcistr);
  for (;;)
    {
      /* Wait for the device to change state */

      rhpndx = CONN_WAIT(dev, connected);
      DEBUGASSERT(rhpndx >= 0 && rhpndx < SAM_OHCI_NRHPORT);

      connected[rhpndx] = !connected[rhpndx];

      uvdbg("%s RHport%d %s\n",
            hcistr, rhpndx + 1, connected[rhpndx] ? "connected" : "disconnected");

      /* Did we just become connected? */

      if (connected[rhpndx])
        {
          /* Yes.. enumerate the newly connected device */

          ret = CONN_ENUMERATE(dev, rhpndx);
          if (ret < 0)
            {
              uvdbg("%s RHport%d CONN_ENUMERATE failed: %d\n", hcistr, rhpndx+1, ret);
              connected[rhpndx] = false;
            }
        }
    }

  /* Keep the compiler from complaining */

  return 0;
}
static int usbhost_disconnected(struct usbhost_class_s *usbclass)
{
  FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)usbclass;
  irqstate_t flags;

  DEBUGASSERT(priv != NULL);

  /* Set an indication to any users of the device that the device is no
   * longer available.
   */

  flags              = irqsave();
  priv->disconnected = true;

  /* Now check the number of references on the class instance.  If it is one,
   * then we can free the class instance now.  Otherwise, we will have to
   * wait until the holders of the references free them by closing the
   * block driver.
   */

  ullvdbg("crefs: %d\n", priv->crefs);
  if (priv->crefs == 1)
    {
      /* Destroy the class instance.  If we are executing from an interrupt
       * handler, then defer the destruction to the worker thread.
       * Otherwise, destroy the instance now.
       */

      if (up_interrupt_context())
        {
          /* Destroy the instance on the worker thread. */

          uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy);
          DEBUGASSERT(priv->work.worker == NULL);
          (void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
       }
      else
        {
          /* Do the work now */

          usbhost_destroy(priv);
        }
    }

  irqrestore(flags);
  return OK;
}
static void usbhost_destroy(FAR void *arg)
{
  FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg;
  FAR struct usbhost_hubport_s *hport;
  FAR struct usbhost_driver_s *drvr;

  DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
  hport = priv->usbclass.hport;

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

  uvdbg("crefs: %d\n", priv->crefs);

  /* Unregister the driver */

  /* Release the device name used by this connection */

  usbhost_freedevno(priv);

  /* Free the endpoints */

  /* Free any transfer buffers */

  /* Free the function address assigned to this device */

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

  /* Destroy the semaphores */

  /* Disconnect the USB host device */

  DRVR_DISCONNECT(drvr, hport);

  /* And free the class instance.  Hmmm.. this may execute on the worker
   * thread and the work structure is part of what is getting freed.  That
   * should be okay because once the work contained is removed from the
   * queue, it should not longer be accessed by the worker thread.
   */

  usbhost_freeclass(priv);
}
static bool usbhost_idmatch(const struct usbhost_id_s *classid,
                            const struct usbhost_id_s *devid)
{
  uvdbg("Compare to class:%d subclass:%d protocol:%d vid:%04x pid:%04x\n",
         classid->base, classid->subclass, classid->proto,
         classid->vid, classid->pid);

  /* The base class ID, subclass and protocol have to match up in any event */

  if (devid->base     == classid->base &&
      devid->subclass == classid->subclass &&
      devid->proto    == classid->proto)
    {
      /* If this is a vendor-specific class ID, then the VID and PID have to
       * match as well.
       */

      if (devid->base == USB_CLASS_VENDOR_SPEC)
        {
          /* Vendor specific... do the VID and PID also match? */

          if (devid->vid == classid->vid && devid->pid == classid->pid)
            {
              /* Yes.. then we have a match */

              return true;
            }
        }
      else
        {
          /* Not vendor specific?  Then we have a match */

          return true;
        }
    }

  /* No match.. not supported */

  return false;
}
static int usbhost_hport_activate(FAR struct usbhost_hubport_s *hport)
{
  struct usbhost_epdesc_s epdesc;
  int ret;

  uvdbg("Activating port %d\n", hport->port);

  epdesc.hport        = hport;
  epdesc.addr         = 0;
  epdesc.in           = false;
  epdesc.xfrtype      = USB_EP_ATTR_XFER_CONTROL;
  epdesc.interval     = 0;
  epdesc.mxpacketsize = (hport->speed == USB_SPEED_HIGH) ? 64 : 8;

  ret = DRVR_EPALLOC(hport->drvr, &epdesc, &hport->ep0);
  if (ret < 0)
    {
      udbg("ERROR: Failed to allocate ep0: %d\n", ret);
    }

  return ret;
}
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;
}
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;
}
Exemple #20
0
static int composite_setup(FAR struct usbdevclass_driver_s *driver,
                           FAR struct usbdev_s *dev,
                           FAR const struct usb_ctrlreq_s *ctrl,
                           FAR uint8_t *dataout, size_t outlen)
{
  FAR struct composite_dev_s *priv;
  FAR struct usbdev_req_s *ctrlreq;
  uint16_t value;
#ifdef CONFIG_DEBUG_USB
  uint16_t index;
#endif
  uint16_t len;
  bool dispatched = false;
  int ret = -EOPNOTSUPP;

#ifdef CONFIG_DEBUG
  if (!driver || !dev || !dev->ep0 || !ctrl)
    {
      usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_SETUPINVALIDARGS), 0);
      return -EIO;
     }
#endif

  /* Extract a reference to private data */

  usbtrace(TRACE_CLASSSETUP, ctrl->req);
  priv = ((FAR struct composite_driver_s*)driver)->dev;

#ifdef CONFIG_DEBUG
  if (!priv)
    {
      usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND2), 0);
      return -ENODEV;
    }
#endif
  ctrlreq   = priv->ctrlreq;

  /* Extract the little-endian 16-bit values to host order */

  value     = GETUINT16(ctrl->value);
#ifdef CONFIG_DEBUG_USB
  index     = GETUINT16(ctrl->index);
#endif
  len       = GETUINT16(ctrl->len);

  uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
        ctrl->type, ctrl->req, value, index, len);

  if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD)
    {
      /**********************************************************************
       * Standard Requests
       **********************************************************************/

      switch (ctrl->req)
        {
        case USB_REQ_GETDESCRIPTOR:
          {
            /* The value field specifies the descriptor type in the MS byte and the
             * descriptor index in the LS byte (order is little endian)
             */

            switch (ctrl->value[1])
              {
              case USB_DESC_TYPE_DEVICE:
                {
                  ret = USB_SIZEOF_DEVDESC;
                  memcpy(ctrlreq->buf, composite_getdevdesc(), ret);
                }
                break;

#ifdef CONFIG_USBDEV_DUALSPEED
              case USB_DESC_TYPE_DEVICEQUALIFIER:
                {
                  ret = USB_SIZEOF_QUALDESC;
                  memcpy(ctrlreq->buf, composite_getqualdesc(), ret);
                }
                break;

              case USB_DESC_TYPE_OTHERSPEEDCONFIG:
#endif

              case USB_DESC_TYPE_CONFIG:
                {
#ifdef CONFIG_USBDEV_DUALSPEED
                  ret = composite_mkcfgdesc(ctrlreq->buf, dev->speed,
                                            ctrl->value[1]);
#else
                  ret = composite_mkcfgdesc(ctrlreq->buf);
#endif
                }
                break;

              case USB_DESC_TYPE_STRING:
                {
                  /* value == string index. Zero is the language ID. */

                  uint8_t strid = ctrl->value[0];
                  FAR struct usb_strdesc_s *buf = (FAR struct usb_strdesc_s *)ctrlreq->buf;

                  if (strid <= COMPOSITE_NSTRIDS)
                    {
                      ret = composite_mkstrdesc(strid, buf);
                    }
#if DEV1_NSTRIDS > 0
                  else if (strid <= DEV1_STRIDBASE + DEV1_NSTRIDS)
                    {
                      ret = DEV1_MKSTRDESC(strid, buf);
                    }
#endif
#if DEV2_NSTRIDS > 0
                  else if (strid <= DEV2_STRIDBASE + DEV2_NSTRIDS)
                    {
                      ret = DEV2_MKSTRDESC(strid, buf);
                    }
#endif
                }
                break;

              default:
                {
                  usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_GETUNKNOWNDESC), value);
                }
                break;
              }
          }
          break;

        case USB_REQ_SETCONFIGURATION:
          {
            if (ctrl->type == 0)
              {
                /* Save the configuration and inform the constituent classes */

                ret = CLASS_SETUP(priv->dev1, dev, ctrl, dataout, outlen);
                dispatched = true;

                if (ret >= 0)
                  {
                    ret = CLASS_SETUP(priv->dev2, dev, ctrl, dataout, outlen);
                    if (ret >= 0)
                      {
                        priv->config = value;
                      }
                  }
              }
          }
          break;

        case USB_REQ_GETCONFIGURATION:
          {
            if (ctrl->type == USB_DIR_IN)
              {
                ctrlreq->buf[0] = priv->config;
                ret = 1;
              }
          }
          break;

        case USB_REQ_SETINTERFACE:
          {
            if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE &&
                priv->config == COMPOSITE_CONFIGID)
              {
                ret = composite_classsetup(priv, dev, ctrl, dataout, outlen);
                dispatched = true;
              }
          }
          break;

        case USB_REQ_GETINTERFACE:
          {
            if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) &&
                priv->config == COMPOSITE_CONFIGIDNONE)
              {
                ret = composite_classsetup(priv, dev, ctrl, dataout, outlen);
                dispatched = true;
              }
           }
           break;

        default:
          usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req);
          break;
        }
    }
  else
    {
      uint8_t recipient;

      /**********************************************************************
       * Non-Standard Class Requests
       **********************************************************************/

       /* Class implementations should handle there own interface and endpoint
        * requests.
        */

       recipient = ctrl->type & USB_REQ_RECIPIENT_MASK;
       if (recipient == USB_REQ_RECIPIENT_INTERFACE || recipient == USB_REQ_RECIPIENT_ENDPOINT)
         {
           ret = composite_classsetup(priv, dev, ctrl, dataout, outlen);
           dispatched = true;
         }
    }


  /* Respond to the setup command if (1) data was returned, and (2) the request was
   * NOT successfully dispatched to the component class driver.  On an error return
   * value (ret < 0), the USB driver will stall EP0.
   */

  if (ret >= 0 && !dispatched)
    {
      /* Setup the request */

      ctrlreq->len   = MIN(len, ret);
      ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;

      /* And submit the request to the USB controller driver */

      ret = EP_SUBMIT(dev->ep0, ctrlreq);
      if (ret < 0)
        {
          usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EPRESPQ), (uint16_t)-ret);
          ctrlreq->result = OK;
          composite_ep0incomplete(dev->ep0, ctrlreq);
        }
    }

  return ret;
}
Exemple #21
0
static int usbclass_setup(struct usbdevclass_driver_s *driver,
                          struct usbdev_s *dev,
                          const struct usb_ctrlreq_s *ctrl,
                          uint8_t * dataout, size_t outlen)
{
    struct apbridge_dev_s *priv;
    struct usbdev_req_s *ctrlreq;
    uint16_t value;
    uint16_t index;
    uint16_t len;
    int ret = -EOPNOTSUPP;

#ifdef CONFIG_DEBUG
    if (!driver || !dev || !dev->ep0 || !ctrl) {
        usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
        return -EIO;
    }
#endif

    /* Extract reference to private data */

    usbtrace(TRACE_CLASSSETUP, ctrl->req);
    priv = ((struct apbridge_driver_s *)driver)->dev;

#ifdef CONFIG_DEBUG
    if (!priv || !priv->ctrlreq) {
        usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
        return -ENODEV;
    }
#endif
    ctrlreq = priv->ctrlreq;
    ctrlreq->priv = (void *)USB_REQ;

    /* Extract the little-endian 16-bit values to host order */

    value = GETUINT16(ctrl->value);
    index = GETUINT16(ctrl->index);
    len = GETUINT16(ctrl->len);

    uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
          ctrl->type, ctrl->req, value, index, len);

    switch (ctrl->type & USB_REQ_TYPE_MASK) {
     /***********************************************************************
      * Standard Requests
      ***********************************************************************/

    case USB_REQ_TYPE_STANDARD:
        {
            switch (ctrl->req) {
            case USB_REQ_GETDESCRIPTOR:
                {
                    /* The value field specifies the descriptor type in the MS byte and the
                     * descriptor index in the LS byte (order is little endian)
                     */

                    switch (ctrl->value[1]) {
                    case USB_DESC_TYPE_DEVICE:
                        {
                            ret = USB_SIZEOF_DEVDESC;
                            memcpy(ctrlreq->buf, &g_devdesc, ret);
                        }
                        break;

                    case USB_DESC_TYPE_DEVICEQUALIFIER:
                        {
                            ret = USB_SIZEOF_QUALDESC;
                            memcpy(ctrlreq->buf, &g_qualdesc, ret);
                        }
                        break;

                    case USB_DESC_TYPE_OTHERSPEEDCONFIG:
                    case USB_DESC_TYPE_CONFIG:
                        {
                            ret =
                                usbclass_mkcfgdesc(ctrlreq->buf, dev->speed,
                                                   ctrl->req);
                        }
                        break;

                    case USB_DESC_TYPE_STRING:
                        {
                            /* index == language code. */

                            ret =
                                usbclass_mkstrdesc(ctrl->value[0],
                                                   (struct usb_strdesc_s *)
                                                   ctrlreq->buf);
                        }
                        break;

                    default:
                        {
                            usbtrace(TRACE_CLSERROR
                                     (USBSER_TRACEERR_GETUNKNOWNDESC), value);
                        }
                        break;
                    }
                }
                break;

            case USB_REQ_SETCONFIGURATION:
                {
                    if (ctrl->type == 0) {
                        ret = usbclass_setconfig(priv, value);
                    }
                }
                break;

            case USB_REQ_GETCONFIGURATION:
                {
                    if (ctrl->type == USB_DIR_IN) {
                        *(uint8_t *) ctrlreq->buf = priv->config;
                        ret = 1;
                    }
                }
                break;

            case USB_REQ_SETINTERFACE:
                {
                    if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE) {
                        if (priv->config == APBRIDGE_CONFIGID &&
                            index == APBRIDGE_INTERFACEID &&
                            value == APBRIDGE_ALTINTERFACEID) {
                            usbclass_resetconfig(priv);
                            usbclass_setconfig(priv, priv->config);
                            ret = 0;
                        }
                    }
                }
                break;

            case USB_REQ_GETINTERFACE:
                {
                    if (ctrl->type ==
                        (USB_DIR_IN | USB_REQ_RECIPIENT_INTERFACE)
                        && priv->config == APBRIDGE_CONFIGIDNONE) {
                        if (index != APBRIDGE_INTERFACEID) {
                            ret = -EDOM;
                        } else {
                            *(uint8_t *) ctrlreq->buf =
                                APBRIDGE_ALTINTERFACEID;
                            ret = 1;
                        }
                    }
                }
                break;

            default:
                usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDSTDREQ),
                         ctrl->req);
                break;
            }
        }
        break;

        /* Put here vendor request */
    case USB_REQ_TYPE_VENDOR:
        {
            if ((ctrl->type & USB_REQ_RECIPIENT_MASK) ==
                USB_REQ_RECIPIENT_INTERFACE) {
                if (ctrl->req == APBRIDGE_RWREQUEST_SVC) {
                    if ((ctrl->type & USB_DIR_IN) != 0) {
                        *(uint32_t *) ctrlreq->buf = 0xdeadbeef;
                        ret = 4;
                    } else {
                        ctrlreq->priv = (void *)GREYBUS_SVC_REQ;
                        ret = len;
                    }
                } else if (ctrl->req == APBRIDGE_RWREQUEST_LOG) {
                    if ((ctrl->type & USB_DIR_IN) == 0) {
                    } else {
#if defined(CONFIG_APB_USB_LOG)
                        ctrlreq->priv = (void *)GREYBUS_LOG;
                        ret = usb_get_log(ctrlreq->buf, len);
#else
                        ret = 0;
#endif
                    }
                } else {
                    usbtrace(TRACE_CLSERROR
                             (USBSER_TRACEERR_UNSUPPORTEDCLASSREQ),
                             ctrl->type);
                }
            }
        }
        break;

    default:
        usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type);
        break;
    }

    /* Respond to the setup command if data was returned.  On an error return
     * value (ret < 0), the USB driver will stall.
     */

    if (ret >= 0) {
        ctrlreq->len = min(len, ret);
        ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
        ret = EP_SUBMIT(dev->ep0, ctrlreq);
        if (ret < 0) {
            usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ),
                     (uint16_t) - ret);
            ctrlreq->result = OK;
            usbclass_ep0incomplete(dev->ep0, ctrlreq);
        }
    }

    return ret;
}
Exemple #22
0
static int usbmsc_setup(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev, FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, size_t outlen)
{
	FAR struct usbmsc_dev_s *priv;
	FAR struct usbdev_req_s *ctrlreq;
	uint16_t value;
	uint16_t index;
	uint16_t len;
	int ret = -EOPNOTSUPP;

#ifdef CONFIG_DEBUG
	if (!driver || !dev || !dev->ep0 || !ctrl) {
		usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SETUPINVALIDARGS), 0);
		return -EIO;
	}
#endif

	/* Extract reference to private data */

	usbtrace(TRACE_CLASSSETUP, ctrl->req);
	priv = ((FAR struct usbmsc_driver_s *)driver)->dev;

#ifdef CONFIG_DEBUG
	if (!priv || !priv->ctrlreq) {
		usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EP0NOTBOUND2), 0);
		return -ENODEV;
	}
#endif
	ctrlreq = priv->ctrlreq;

	/* Extract the little-endian 16-bit values to host order */

	value = GETUINT16(ctrl->value);
	index = GETUINT16(ctrl->index);
	len = GETUINT16(ctrl->len);

	uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", ctrl->type, ctrl->req, value, index, len);

	if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) {
		/**********************************************************************
		 * Standard Requests
		 **********************************************************************/

		switch (ctrl->req) {
		case USB_REQ_GETDESCRIPTOR: {
			/* The value field specifies the descriptor type in the MS byte and the
			 * descriptor index in the LS byte (order is little endian)
			 */

			switch (ctrl->value[1]) {
				/* If the mass storage device is used in as part of a composite
				 * device, then the device descriptor is is provided by logic
				 * in the composite device implementation.
				 */

#ifndef CONFIG_USBMSC_COMPOSITE
			case USB_DESC_TYPE_DEVICE: {
				ret = USB_SIZEOF_DEVDESC;
				memcpy(ctrlreq->buf, usbmsc_getdevdesc(), ret);
			}
			break;
#endif

				/* If the mass storage device is used in as part of a composite device,
				 * then the device qualifier descriptor is provided by logic in the
				 * composite device implementation.
				 */

#if !defined(CONFIG_USBMSC_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
			case USB_DESC_TYPE_DEVICEQUALIFIER: {
				ret = USB_SIZEOF_QUALDESC;
				memcpy(ctrlreq->buf, usbmsc_getqualdesc(), ret);
			}
			break;

			case USB_DESC_TYPE_OTHERSPEEDCONFIG:
#endif

				/* If the mass storage device is used in as part of a composite device,
				 * then the configuration descriptor is provided by logic in the
				 * composite device implementation.
				 */

#ifndef CONFIG_USBMSC_COMPOSITE
			case USB_DESC_TYPE_CONFIG: {
#ifdef CONFIG_USBDEV_DUALSPEED
				ret = usbmsc_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->value[1]);
#else
				ret = usbmsc_mkcfgdesc(ctrlreq->buf);
#endif
			}
			break;
#endif

				/* If the mass storage device is used in as part of a composite device,
				 * then the language string descriptor is provided by logic in the
				 * composite device implementation.
				 */

#ifndef CONFIG_USBMSC_COMPOSITE
			case USB_DESC_TYPE_STRING: {
				/* index == language code. */

				ret = usbmsc_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *)ctrlreq->buf);
			}
			break;
#endif

			default: {
				usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_GETUNKNOWNDESC), value);
			}
			break;
			}
		}
		break;

		case USB_REQ_SETCONFIGURATION: {
			if (ctrl->type == 0) {
				/* Signal the worker thread to instantiate the new configuration */

				priv->theventset |= USBMSC_EVENT_CFGCHANGE;
				priv->thvalue = value;
				usbmsc_scsi_signal(priv);

				/* Return here... the response will be provided later by the
				 * worker thread.
				 */

				return OK;
			}
		}
		break;

			/* If the mass storage device is used in as part of a composite device,
			 * then the overall composite class configuration is managed by logic
			 * in the composite device implementation.
			 */

#ifndef CONFIG_USBMSC_COMPOSITE
		case USB_REQ_GETCONFIGURATION: {
			if (ctrl->type == USB_DIR_IN) {
				ctrlreq->buf[0] = priv->config;
				ret = 1;
			}
		}
		break;
#endif

		case USB_REQ_SETINTERFACE: {
			if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE) {
				if (priv->config == USBMSC_CONFIGID && index == USBMSC_INTERFACEID && value == USBMSC_ALTINTERFACEID) {
					/* Signal to instantiate the interface change */

					priv->theventset |= USBMSC_EVENT_IFCHANGE;
					usbmsc_scsi_signal(priv);

					/* Return here... the response will be provided later by the
					 * worker thread.
					 */

					return OK;
				}
			}
		}
		break;

		case USB_REQ_GETINTERFACE: {
			if (ctrl->type == (USB_DIR_IN | USB_REQ_RECIPIENT_INTERFACE) && priv->config == USBMSC_CONFIGIDNONE) {
				if (index != USBMSC_INTERFACEID) {
					ret = -EDOM;
				} else {
					ctrlreq->buf[0] = USBMSC_ALTINTERFACEID;
					ret = 1;
				}
			}
		}
		break;

		default:
			usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req);
			break;
		}
	} else if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS) {
		/**********************************************************************
		 * Bulk-Only Mass Storage Class Requests
		 **********************************************************************/

		/* Verify that we are configured */

		if (!priv->config) {
			usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_NOTCONFIGURED), 0);
			return ret;
		}

		switch (ctrl->req) {
		case USBMSC_REQ_MSRESET: {	/* Reset mass storage device and interface */
			if (ctrl->type == USBMSC_TYPE_SETUPOUT && value == 0 && len == 0) {
				/* Only one interface is supported */

				if (index != USBMSC_INTERFACEID) {
					usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_MSRESETNDX), index);
					ret = -EDOM;
				} else {
					/* Signal to stop the current operation and reinitialize state */

					priv->theventset |= USBMSC_EVENT_RESET;
					usbmsc_scsi_signal(priv);

					/* Return here... the response will be provided later by the
					 * worker thread.
					 */

					return OK;
				}
			}
		}
		break;

		case USBMSC_REQ_GETMAXLUN: {	/* Return number LUNs supported */
			if (ctrl->type == USBMSC_TYPE_SETUPIN && value == 0 && len == 1) {
				/* Only one interface is supported */

				if (index != USBMSC_INTERFACEID) {
					usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_GETMAXLUNNDX), index);
					ret = -EDOM;
				} else {
					ctrlreq->buf[0] = priv->nluns - 1;
					ret = 1;
				}
			}
		}
		break;

		default:
			usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_BADREQUEST), ctrl->req);
			break;
		}
	} else {
		usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNSUPPORTEDTYPE), ctrl->type);
	}

	/* Respond to the setup command if data was returned.  On an error return
	 * value (ret < 0), the USB driver will stall EP0.
	 */

	if (ret >= 0) {
		/* Configure the response */

		ctrlreq->len = MIN(len, ret);
		ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;

		/* Send the response -- either directly to the USB controller or
		 * indirectly in the case where this class is a member of a composite
		 * device.
		 */

#ifndef CONFIG_USBMSC_COMPOSITE
		ret = EP_SUBMIT(dev->ep0, ctrlreq);
#else
		ret = composite_ep0submit(driver, dev, ctrlreq);
#endif
		if (ret < 0) {
			usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EPRESPQ), (uint16_t)-ret);
#if 0							/* Not necessary */
			ctrlreq->result = OK;
			usbmsc_ep0incomplete(dev->ep0, ctrlreq);
#endif
		}
	}

	return ret;
}
Exemple #23
0
static
#endif
int usbmsc_exportluns(FAR void *handle)
{
	FAR struct usbmsc_alloc_s *alloc = (FAR struct usbmsc_alloc_s *)handle;
	FAR struct usbmsc_dev_s *priv;
#ifndef CONFIG_USBMSC_COMPOSITE
	FAR struct usbmsc_driver_s *drvr;
#endif
	irqstate_t flags;
	int ret = OK;

#ifdef CONFIG_DEBUG
	if (!alloc) {
		usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EXPORTLUNSINVALIDARGS), 0);
		return -ENXIO;
	}
#endif

	priv = &alloc->dev;
#ifndef CONFIG_USBMSC_COMPOSITE
	drvr = &alloc->drvr;
#endif

	/* Start the worker thread
	 *
	 * REVISIT:  g_usbmsc_handoff is a global and, hence, really requires
	 * some protection against re-entrant usage.
	 */

	usbmsc_scsi_lock(priv);

	priv->thstate = USBMSC_STATE_NOTSTARTED;
	priv->theventset = USBMSC_EVENT_NOEVENTS;

	g_usbmsc_handoff = priv;

	uvdbg("Starting SCSI worker thread\n");
	priv->thpid = kernel_thread("scsid", CONFIG_USBMSC_SCSI_PRIO, CONFIG_USBMSC_SCSI_STACKSIZE, usbmsc_scsi_main, NULL);
	if (priv->thpid <= 0) {
		usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_THREADCREATE), (uint16_t)errno);
		goto errout_with_lock;
	}

	/* Wait for the worker thread to run and initialize */

	uvdbg("Waiting for the SCSI worker thread\n");
	usbmsc_sync_wait(priv);
	DEBUGASSERT(g_usbmsc_handoff == NULL);

	/* Register the USB storage class driver (unless we are part of a composite device) */

#ifndef CONFIG_USBMSC_COMPOSITE
	ret = usbdev_register(&drvr->drvr);
	if (ret != OK) {
		usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_DEVREGISTER), (uint16_t)-ret);
		goto errout_with_lock;
	}
#endif

	/* Signal to start the thread */

	uvdbg("Signalling for the SCSI worker thread\n");
	flags = irqsave();
	priv->theventset |= USBMSC_EVENT_READY;
	usbmsc_scsi_signal(priv);
	irqrestore(flags);

errout_with_lock:
	usbmsc_scsi_unlock(priv);
	return ret;
}
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);
}
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;
}
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 int usbhost_devinit(FAR struct usbhost_state_s *priv)
{
  int ret = OK;

  /* Set aside a transfer buffer for exclusive use by the class driver */

  /* Increment the reference count.  This will prevent usbhost_destroy() from
   * being called asynchronously if the device is removed.
   */

  priv->crefs++;
  DEBUGASSERT(priv->crefs == 2);

  /* Configure the device */

  /* Register the driver */

  if (ret >= 0)
    {
      char devname[DEV_NAMELEN];

      uvdbg("Register block driver\n");
      usbhost_mkdevname(priv, devname);
      // ret = register_blockdriver(devname, &g_bops, 0, priv);
    }

  /* Check if we successfully initialized. We now have to be concerned
   * about asynchronous modification of crefs because the block
   * driver has been registerd.
   */

  if (ret >= 0)
    {
      usbhost_takesem(&priv->exclsem);
      DEBUGASSERT(priv->crefs >= 2);

      /* Handle a corner case where (1) open() has been called so the
       * reference count is > 2, but the device has been disconnected.
       * In this case, the class instance needs to persist until close()
       * is called.
       */

      if (priv->crefs <= 2 && priv->disconnected)
        {
          /* We don't have to give the semaphore because it will be
           * destroyed when usb_destroy is called.
           */

          ret = -ENODEV;
        }
      else
        {
          /* Ready for normal operation as a block device driver */

          uvdbg("Successfully initialized\n");
          priv->crefs--;
          usbhost_givesem(&priv->exclsem);
        }
    }

  return ret;
}
static void usbhost_disconnect_event(FAR void *arg)
{
  FAR struct usbhost_class_s *hubclass = (FAR struct usbhost_class_s *)arg;
  FAR struct usbhost_hubpriv_s *priv;
  FAR struct usbhost_hubport_s *hport;
  FAR struct usbhost_hubport_s *child;
  irqstate_t flags;
  int port;

  uvdbg("Disconnecting\n");

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

  uvdbg("Destroying hub on port  %d\n", hport->port);

  /* Set an indication to any users of the device that the device is no
   * longer available.
   */

  flags = irqsave();

  /* Cancel any pending transfers on the interrupt IN pipe */

  DRVR_CANCEL(hport->drvr, priv->intin);

  /* Cancel any pending port status change events */

  work_cancel(LPWORK, &priv->work);

  /* Disable power to all downstream ports */

  (void)usbhost_hubpwr(priv, hport, false);

  /* Free the allocated control request */

  DRVR_FREE(hport->drvr, (FAR uint8_t *)priv->ctrlreq);

  /* Free buffer for status change (INT) endpoint */

  DRVR_IOFREE(hport->drvr, priv->buffer);

  /* Destroy the interrupt IN endpoint */

  DRVR_EPFREE(hport->drvr, priv->intin);

  /* Release per-port resources */

  for (port = 0; port < USBHUB_MAX_PORTS; port++)
    {
      /* Free any devices classes connect on this hub port */

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

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

      usbhost_hport_deactivate(child);
    }

  /* Deactivate the parent hub port (unless it is the root hub port) */

  usbhost_hport_deactivate(hport);

  /* Destroy the semaphores */

  sem_destroy(&priv->exclsem);

  /* Disconnect the USB host device */

  DRVR_DISCONNECT(hport->drvr, hport);

  /* Free the class instance */

  kmm_free(hubclass);
  hport->devclass = NULL;
  irqrestore(flags);
}
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;
}