static void khci_ep0setup()

in arch/arm/src/kinetis/kinetis_usbdev.c [1964:2586]


static void khci_ep0setup(struct khci_usbdev_s *priv)
{
  volatile struct usbotg_bdtentry_s *bdt;
  struct khci_ep_s *ep0;
  struct khci_ep_s *privep;
  union wb_u value;
  union wb_u index;
  union wb_u len;
  union wb_u response;
  uint32_t regval;
  bool dispatched = false;
  uint8_t epno;
  int nbytes = 0; /* Assume zero-length packet */
  int ret;

  /* Cancel any pending requests. */

  ep0 = &priv->eplist[EP0];
  khci_cancelrequests(ep0, -EPROTO);

  /* Assume NOT stalled; no TX in progress; no RX overrun.  Data 0/1 toggling
   * is not used on SETUP packets, but any following EP0 IN transfer should
   * beginning with DATA1.
   */

  ep0->stalled = false;
  ep0->rxdata1 = 0;
  ep0->txdata1 = 1;

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

  value.w = GETUINT16(priv->ctrl.value);
  index.w = GETUINT16(priv->ctrl.index);
  len.w   = GETUINT16(priv->ctrl.len);
  response.w    = 0;

  /* Check to see if called from the DATA phase of a SETUP Transfer */

  if (priv->ctrlstate != CTRLSTATE_SETUP_READY &&
      priv->ctrlstate != CTRLSTATE_SETUP_OUT)
    {
      /* Not the data phase */

      uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n",
            priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w);

      /* Is this an setup with OUT and data of length > 0 */

      if (USB_REQ_ISOUT(priv->ctrl.type) && len.w > 0)
        {
          usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_EP0SETUPOUT), len.w);

          priv->ep0datlen = 0;
          priv->ep0datreq = len.w;

          /* At this point priv->ctrl is the setup packet. */

          khci_ep0nextsetup(priv);
          priv->ctrlstate = CTRLSTATE_SETUP_OUT;
        }
      else
        {
          priv->ctrlstate = CTRLSTATE_SETUP_READY;
        }
    }

  if (priv->ctrlstate == CTRLSTATE_SETUP_READY)
    {
      /* Dispatch any non-standard requests */

      if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
        {
          usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_NOSTDREQ),
                   priv->ctrl.type);

          /* Let the class implementation handle all non-standard requests */

          khci_dispatchrequest(priv);
          dispatched = true;
        }
      else
        {
          /* Handle standard request.  Pick off the things of interest to the
           * USB device controller driver; pass what is left to the class
           * driver
           */

          switch (priv->ctrl.req)
            {
            case USB_REQ_GETSTATUS:
              {
                /* type:  device-to-host; recipient = device, interface,
                 *        endpoint
                 * value: 0
                 * index: zero interface endpoint
                 * len:   2; data = status
                 */

                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_GETSTATUS),
                         priv->ctrl.type);
                if (len.w != 2 || (priv->ctrl.type & USB_REQ_DIR_IN) == 0 ||
                    index.b[MSB] != 0 || value.w != 0)
                  {
                    usbtrace(TRACE_DEVERROR(
                             KHCI_TRACEERR_BADEPGETSTATUS), 0);
                    priv->ctrlstate = CTRLSTATE_STALL;
                  }
                else
                  {
                    switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK)
                      {
                       case USB_REQ_RECIPIENT_ENDPOINT:
                        {
                          epno = USB_EPNO(index.b[LSB]);
                          usbtrace(TRACE_INTDECODE(
                                   KHCI_TRACEINTID_EPGETSTATUS), epno);
                          if (epno >= KHCI_NENDPOINTS)
                            {
                              usbtrace(TRACE_DEVERROR(
                                       KHCI_TRACEERR_BADEPGETSTATUS), epno);
                              priv->ctrlstate = CTRLSTATE_STALL;
                            }
                          else
                            {
                              privep            = &priv->eplist[epno];
                              response.w        = 0; /* Not stalled */
                              nbytes            = 2; /* Response size: 2 bytes */

                              if (USB_ISEPIN(index.b[LSB]))
                                {
                                  /* IN endpoint */

                                  bdt = privep->bdtin;
                                }
                              else
                                {
                                  /* OUT endpoint */

                                  bdt = privep->bdtout;
                                }

                              /* BSTALL set if stalled */

                              if ((bdt->status & USB_BDT_BSTALL) != 0)
                                {
                                  response.b[LSB] = 1; /* Stalled, set bit 0 */
                                }
                            }
                        }
                        break;

                      case USB_REQ_RECIPIENT_DEVICE:
                        {
                         if (index.w == 0)
                            {
                              usbtrace(TRACE_INTDECODE(
                                       KHCI_TRACEINTID_DEVGETSTATUS), 0);

                              /* Features:
                               * Remote Wakeup=YES;
                               * selfpowered=?
                               */

                              response.w      = 0;
                              response.b[LSB] =
                                  (priv->selfpowered <<
                                   USB_FEATURE_SELFPOWERED) |
                                  (priv->rwakeup <<
                                   USB_FEATURE_REMOTEWAKEUP);
                              nbytes        = 2; /* Response size: 2 bytes */
                            }
                          else
                            {
                              usbtrace(TRACE_DEVERROR(
                                       KHCI_TRACEERR_BADDEVGETSTATUS), 0);
                              priv->ctrlstate = CTRLSTATE_STALL;
                            }
                        }
                        break;

                      case USB_REQ_RECIPIENT_INTERFACE:
                        {
                          usbtrace(TRACE_INTDECODE(
                                   KHCI_TRACEINTID_IFGETSTATUS), 0);
                          response.w        = 0;
                          nbytes            = 2; /* Response size: 2 bytes */
                        }
                        break;

                      default:
                        {
                          usbtrace(TRACE_DEVERROR(
                                   KHCI_TRACEERR_BADGETSTATUS), 0);
                          priv->ctrlstate = CTRLSTATE_STALL;
                        }
                        break;
                      }
                  }
              }
              break;

            case USB_REQ_CLEARFEATURE:
              {
                /* type:  host-to-device; recipient = device, interface or
                 *        endpoint
                 * value: feature selector
                 * index: zero interface endpoint;
                 * len:   zero, data = none
                 */

                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_CLEARFEATURE),
                         priv->ctrl.type);
                if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
                     USB_REQ_RECIPIENT_DEVICE)
                  {
                    /* Disable B device from performing HNP */

        #ifdef CONFIG_KINETIS_USBOTG
                    if (value.w == USBOTG_FEATURE_B_HNP_ENABLE)
                      {
                        /* Disable HNP */
        #warning Missing Logic
                      }

                    /* Disable A device HNP support */

                    else if (value.w == USBOTG_FEATURE_A_HNP_SUPPORT)
                      {
                        /* Disable HNP support */
        #warning Missing Logic
                      }

                    /* Disable alternate HNP support */

                    else if (value.w == USBOTG_FEATURE_A_ALT_HNP_SUPPORT)
                      {
                        /* Disable alternate HNP */
        #warning Missing Logic
                      }
                    else
        #endif
                    /* Disable remote wakeup */

                    if (value.w == USB_FEATURE_REMOTEWAKEUP)
                      {
                        priv->rwakeup     = 0;
                      }
                    else
                      {
                        /* Let the class implementation handle all other
                         * device features
                         */

                        khci_dispatchrequest(priv);
                        dispatched = true;
                      }
                  }
                else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
                    USB_REQ_RECIPIENT_ENDPOINT)
                  {
                    epno = USB_EPNO(index.b[LSB]);
                    if (epno > 0 && epno <
                        KHCI_NENDPOINTS && index.b[MSB] == 0 &&
                        value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
                      {
                        privep            = &priv->eplist[epno];
                        privep->halted    = false;
                        ret               = khci_epstall(&privep->ep, true);
                        UNUSED(ret);
                      }
                    else
                      {
                        usbtrace(TRACE_DEVERROR(
                                 KHCI_TRACEERR_BADCLEARFEATURE), 0);
                        priv->ctrlstate = CTRLSTATE_STALL;
                      }
                  }
                else
                  {
                    /* Let the class implementation handle all other
                     * recipients.
                     */

                    khci_dispatchrequest(priv);
                    dispatched = true;
                  }
              }
              break;

            case USB_REQ_SETFEATURE:
              {
                /* type:  host-to-device; recipient = device, interface,
                 *        endpoint
                 * value: feature selector
                 * index: zero interface endpoint;
                 * len:   0; data = none
                 */

                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_SETFEATURE),
                         priv->ctrl.type);

                if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
                     USB_REQ_RECIPIENT_DEVICE)
                  {
                    /* Enable B device to perform HNP */

        #ifdef CONFIG_KINETIS_USBOTG
                    if (value.w == USBOTG_FEATURE_B_HNP_ENABLE)
                      {
                        /* Enable HNP */
        #warning "Missing logic"
                      }

                    /* Enable A device HNP supports */

                    else if (value.w == USBOTG_FEATURE_A_HNP_SUPPORT)
                      {
                        /* Enable HNP support */
        #warning "Missing logic"
                      }

                    /* Another port on the A device supports HNP */

                    else if (value.w == USBOTG_FEATURE_A_ALT_HNP_SUPPORT)
                      {
                        /* Enable alternate HNP */
        #warning "Missing logic"
                      }
                    else
        #endif

                    if (value.w == USB_FEATURE_REMOTEWAKEUP)
                      {
                        priv->rwakeup     = 0;
                      }
                    else if (value.w == USB_FEATURE_TESTMODE)
                      {
                        /* Special case recipient=device test mode */

                        uinfo("test mode: %d\n", index.w);
                      }
                    else
                      {
                        /* Let the class implementation handle all other
                         * device features
                         */

                        khci_dispatchrequest(priv);
                        dispatched = true;
                      }
                  }
                else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
                    USB_REQ_RECIPIENT_ENDPOINT)
                  {
                    /* Handler recipient=endpoint */

                    epno = USB_EPNO(index.b[LSB]);
                    if (epno < KHCI_NENDPOINTS && index.b[MSB] == 0 &&
                        value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
                      {
                        privep            = &priv->eplist[epno];
                        privep->halted    = true;
                        ret               = khci_epstall(&privep->ep, false);
                        UNUSED(ret);
                      }
                    else
                      {
                        usbtrace(TRACE_DEVERROR(
                                 KHCI_TRACEERR_BADSETFEATURE), 0);
                        priv->ctrlstate = CTRLSTATE_STALL;
                      }
                  }
                else
                  {
                    /* The class driver handles all recipients except
                     * recipient=endpoint
                     */

                    khci_dispatchrequest(priv);
                    dispatched = true;
                  }
              }
              break;

            case USB_REQ_SETADDRESS:
              {
                /* type:  host-to-device; recipient = device
                 * value: device address
                 * index: 0
                 * len:   0; data = none
                 */

                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_EP0SETUPSETADDRESS),
                         value.w);
                if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
                    USB_REQ_RECIPIENT_DEVICE ||
                    index.w != 0 || len.w != 0 || value.w > 127)
                  {
                    usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_BADSETADDRESS), 0);
                    priv->ctrlstate = CTRLSTATE_STALL;
                  }
                else
                  {
                    /* Note that setting of the device address will be
                     * deferred. A zero-length packet will be sent and the
                     * device address will be set when the zerolength packet
                     * transfer completes.
                     */

                    priv->devstate = DEVSTATE_ADDRPENDING;
                  }
              }
              break;

            case USB_REQ_GETDESCRIPTOR:
              /* type:  device-to-host; recipient = device
               * value: descriptor type and index
               * index: 0 or language ID;
               * len:   descriptor len; data = descriptor
               */

            case USB_REQ_SETDESCRIPTOR:
              /* type:  host-to-device; recipient = device
               * value: descriptor type and index
               * index: 0 or language ID;
               * len:   descriptor len; data = descriptor
               */

              {
                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_GETSETDESC),
                         priv->ctrl.type);
                if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
                    USB_REQ_RECIPIENT_DEVICE)
                  {
                    /* The request seems valid...
                     * let the class implementation handle it
                     */

                    khci_dispatchrequest(priv);
                    dispatched = true;
                  }
                else
                  {
                    usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_BADGETSETDESC), 0);
                    priv->ctrlstate = CTRLSTATE_STALL;
                  }
              }
              break;

            case USB_REQ_GETCONFIGURATION:
              /* type:  device-to-host; recipient = device
               * value: 0;
               * index: 0;
               * len:   1; data = configuration value
               */

              {
                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_GETCONFIG),
                         priv->ctrl.type);
                if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
                    USB_REQ_RECIPIENT_DEVICE && value.w == 0 &&
                    index.w == 0 && len.w == 1)
                  {
                    /* The request seems valid... let the class
                     * implementation handle it
                     */

                    khci_dispatchrequest(priv);
                    dispatched = true;
                  }
                else
                  {
                    usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_BADGETCONFIG), 0);
                    priv->ctrlstate = CTRLSTATE_STALL;
                  }
              }
              break;

            case USB_REQ_SETCONFIGURATION:
              /* type:  host-to-device; recipient = device
               * value: configuration value
               * index: 0;
               * len:   0; data = none
               */

              {
                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_SETCONFIG),
                         priv->ctrl.type);
                if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
                    USB_REQ_RECIPIENT_DEVICE && index.w == 0 && len.w == 0)
                  {
                     /* The request seems valid. Let the class implementation
                      * handle it
                      */

                     khci_dispatchrequest(priv);
                     dispatched = true;
                  }
                else
                  {
                    usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_BADSETCONFIG), 0);
                    priv->ctrlstate = CTRLSTATE_STALL;
                  }
              }
              break;

            case USB_REQ_GETINTERFACE:
              /* type:  device-to-host; recipient = interface
               * value: 0
               * index: interface;
               * len:   1; data = alt interface
               */

            case USB_REQ_SETINTERFACE:
              /* type:  host-to-device; recipient = interface
               * value: alternate setting
               * index: interface;
               * len:   0; data = none
               */

              {
                /* Let the class implementation handle the request */

                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_GETSETIF),
                         priv->ctrl.type);
                khci_dispatchrequest(priv);
                dispatched = true;
              }
              break;

            case USB_REQ_SYNCHFRAME:
              /* type:  device-to-host; recipient = endpoint
               * value: 0
               * index: endpoint;
               * len:   2; data = frame number
               */

              {
                usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_SYNCHFRAME), 0);
              }
              break;

            default:
              {
                usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_INVALIDCTRLREQ),
                         priv->ctrl.req);
                priv->ctrlstate = CTRLSTATE_STALL;
              }
              break;
            }
        }
    }

  /* PKTDIS bit is set when a Setup Transaction is received. Clear to resume
   * packet processing.
   */

  regval = khci_getreg(KINETIS_USB0_CTL);
  regval &= ~USB_CTL_TXSUSPENDTOKENBUSY;
  khci_putreg(regval, KINETIS_USB0_CTL);

  /* At this point, the request has been handled and there are three possible
   * outcomes:
   *
   * 1. The setup request was successfully handled above and a response
   *    packet must be sent (may be a zero length packet).
   * 2. The request was successfully handled by the class implementation.
   *    In case, the EP0 IN response has already been queued and the local
   *    variable 'dispatched' will be set to true and ctrlstate !=
   *    CTRLSTATE_STALL;
   * 3. An error was detected in either the above logic or by the class
   *    implementation logic.  In either case, priv->state will be set
   *    CTRLSTATE_STALL to indicate this case.
   *
   * NOTE:
   * Non-standard requests are a special case.  They are handled by the
   * class implementation and this function returned early above, skipping
   * this logic altogether.
   */

  if (priv->ctrlstate == CTRLSTATE_SETUP_READY)
    {
      if (!dispatched)
        {
          /* The SETUP command was not dispatched to the class driver and the
           * SETUP command did not cause a stall. We will respond.  First,
           * restrict the data length to the length requested in the setup
           * packet
           */

          if (nbytes > len.w)
            {
              nbytes = len.w;
            }

          /* Send the EP0 SETUP response (might be a zero-length packet) */

          khci_epwrite(ep0, ep0->bdtin, response.b, nbytes);
        }

      priv->ctrlstate = CTRLSTATE_WAITSETUP;
    }
  else if (priv->ctrlstate == CTRLSTATE_STALL)
    {
      /* Did we stall?
       * This might have occurred from the above logic OR the stall condition
       * may have been set less obviously in khci_dispatchrequest().
       * In either case, we handle the stall condition the same.
       *
       * However, bad things happen if we try to stall a SETUP packet.  So
       * lets So lets not.  If we wait a bit, things will recover.
       * Hmmm.. If we completed the data phase (perhaps by sending a NULL
       * packet), then I think we could stall the endpoint and perhaps speed
       * things up a bit???.
       */

      priv->ctrlstate = CTRLSTATE_WAITSETUP;
    }

  /* Set up the BDT to accept the next setup command. */

  khci_ep0nextsetup(priv);
}