Skip to content
Snippets Groups Projects
lpc214x_usbdev.c 98.2 KiB
Newer Older
static int lpc214x_wrrequest(struct lpc214x_ep_s *privep)
{
  struct lpc214x_req_s *privreq;
  ubyte *buf;
  int nbytes;
  int bytesleft;

  /* Check the request from the head of the endpoint request queue */

  privreq = lpc214x_rqpeek(privep);
      usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPINQEMPTY), 0);
  ullvdbg("epphy=%d req=%p: len=%d xfrd=%d nullpkt=%d\n",
          privep->epphy, privreq, privreq->req.len, privreq->req.xfrd, privep->txnullpkt);
patacongo's avatar
patacongo committed
  /* Ignore any attempt to send a zero length packet on anything but EP0IN */

  if (privreq->req.len == 0)
    {
patacongo's avatar
patacongo committed
      if (privep->epphy == LPC214X_EP0_IN)
        {
          lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0);
        }
      else
        {
          usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EPINNULLPACKET), 0);
        }

      /* In any event, the request is complete */

      lpc214x_reqcomplete(privep, OK);
      return OK;
    }

  /* Otherwise send the data in the packet (in the DMA on case, we
   * may be resuming transfer already in progress.
   */
#warning REVISIT... If the EP supports double buffering, then we can do better

  /* Get the number of bytes left to be sent in the packet */

  bytesleft = privreq->req.len - privreq->req.xfrd;

  /* Send the next packet if (1) there are more bytes to be sent, or
   * (2) the last packet sent was exactly maxpacketsize (bytesleft == 0)
   */

  usbtrace(TRACE_WRITE(privep->epphy), privreq->req.xfrd);
  if (bytesleft >  0 || privep->txnullpkt)
      /* Indicate that there is data in the TX FIFO.  This will be cleared
       * when the EPIN interrupt is received
       */
      privep->txbusy = 1;
      /* Try to send maxpacketsize -- unless we don't have that many
       * bytes to send.
      privep->txnullpkt = 0;
      if (bytesleft > privep->ep.maxpacket)
          nbytes = privep->ep.maxpacket;
        }
      else
        {
          nbytes = bytesleft;
          if ((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
            {
              privep->txnullpkt = (bytesleft == privep->ep.maxpacket);
            }
      /* Send the largest number of bytes that we can in this packet */
      buf = privreq->req.buf + privreq->req.xfrd;
      lpc214x_epwrite(privep->epphy, buf, nbytes);
      /* Update for the next time through the loop */
      privreq->req.xfrd += nbytes;
    }
  /* If all of the bytes were sent (including any final null packet)
   * then we are finished with the transfer
   */
  if (privreq->req.xfrd >= privreq->req.len && !privep->txnullpkt)
    {
      usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
      privep->txnullpkt = 0;
      lpc214x_reqcomplete(privep, OK);
  return OK;
}

/*******************************************************************************
 * Name: lpc214x_rdrequest
 *
 * Description:
 *   Receive to the next queued read request
 *
 *******************************************************************************/

static int lpc214x_rdrequest(struct lpc214x_ep_s *privep)
{
  struct lpc214x_req_s *privreq;
  ubyte *buf;
  int nbytesread;

  /* Check the request from the head of the endpoint request queue */

  privreq = lpc214x_rqpeek(privep);
      usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPOUTQEMPTY), 0);
  ullvdbg("len=%d xfrd=%d nullpkt=%d\n",
          privreq->req.len, privreq->req.xfrd, privep->txnullpkt);
  /* Ignore any attempt to receive a zero length packet */

  if (privreq->req.len == 0)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EPOUTNULLPACKET), 0);
      lpc214x_reqcomplete(privep, OK);
      return OK;
    }

  usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd);

  /* Receive the next packet */
  buf        = privreq->req.buf + privreq->req.xfrd;
  nbytesread = lpc214x_epread(privep->epphy, buf, privep->ep.maxpacket);
  if (nbytesread < 0)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EPREAD), nbytesread);
      return ERROR;
    }
  /* If the receive buffer is full or if the last packet was not full
   * then we are finished with the transfer.
   */

  privreq->req.xfrd += nbytesread;
  if (privreq->req.xfrd >= privreq->req.len || nbytesread < privep->ep.maxpacket)
    {
      usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
      lpc214x_reqcomplete(privep, OK);
}

/*******************************************************************************
 * Name: lpc214x_cancelrequests
 *
 * Description:
 *   Cancel all pending requests for an endpoint
 *
 *******************************************************************************/

static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep)
{
  while (!lpc214x_rqempty(privep))
patacongo's avatar
patacongo committed
      usbtrace(TRACE_COMPLETE(privep->epphy),
               (lpc214x_rqpeek(privep))->req.xfrd);
      lpc214x_reqcomplete(privep, -ESHUTDOWN);
    }
}

/*******************************************************************************
 * Name: lpc214x_epfindbyaddr
 *
 * Description:
 *   Find the physical endpoint structure corresponding to a logic endpoint
 *   address
 *
 *******************************************************************************/

static struct lpc214x_ep_s *lpc214x_epfindbyaddr(struct lpc214x_usbdev_s *priv,
              uint16 eplog)
{
  struct lpc214x_ep_s *privep;
  int i;

  /* Endpoint zero is a special case */

  if (USB_EPNO(eplog) == 0)
    {
      return &priv->eplist[0];
    }

  /* Handle the remaining */

  for (i = 1; i < LPC214X_NPHYSENDPOINTS; i++)
    {
      privep = &priv->eplist[i];

      /* Same logical endpoint number? (includes direction bit) */

      if (eplog == privep->ep.eplog)
        {
          /* Return endpoint found */

          return privep;
        }
    }

  /* Return endpoint not found */

  return NULL;
}

/*******************************************************************************
 * Name: lpc214x_eprealize
 *
 * Description:
 *   Enable or disable an endpoint
 *
 *******************************************************************************/

static void lpc214x_eprealize(struct lpc214x_ep_s *privep, boolean prio, uint32 packetsize)
{
  struct lpc214x_usbdev_s *priv = privep->dev;
  uint32 mask;
  uint32 reg;

  /* Initialize endpoint software priority */

  mask = 1 << privep->epphy;
  if (prio)
    {
      priv->softprio = priv->softprio | mask;
    }
  else
    {
      priv->softprio = priv->softprio & ~mask;
    }

  /* Clear realize interrupt bit */

  lpc214x_putreg(USBDEV_DEVINT_EPRLZED, LPC214X_USBDEV_DEVINTCLR);

  /* Realize the endpoint */

  reg  = lpc214x_getreg(LPC214X_USBDEV_REEP);
  reg |= (1 << privep->epphy);
  lpc214x_putreg(reg, LPC214X_USBDEV_REEP);

  /* Set endpoint maximum packet size */

  lpc214x_putreg(privep->epphy, LPC214X_USBDEV_EPIND);
  lpc214x_putreg(packetsize, LPC214X_USBDEV_MAXPSIZE);

  /* Wait for Realize complete */

  while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_EPRLZED) == 0);

  /* Clear realize interrupt bit */

  lpc214x_putreg(USBDEV_DEVINT_EPRLZED,LPC214X_USBDEV_DEVINTCLR);
}

/*******************************************************************************
 * Name: lpc214x_epclrinterrupt
 *
 * Description:
 *   Clear the EP interrupt flag and return the current EP status
 *
 *******************************************************************************/

static ubyte lpc214x_epclrinterrupt(ubyte epphy)
{
  /* Clear the endpoint interrupt */

  lpc214x_putreg(1 << epphy, LPC214X_USBDEV_EPINTCLR);

  /* Wait for data in the command data register */

  while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

  /* Return the value of the command data register */

  return lpc214x_getreg(LPC214X_USBDEV_CMDDATA);
}

/*******************************************************************************
 * Name: lpc214x_ep0configure
 *
 * Description:
 *   Configure endpoint 0
 *
 *******************************************************************************/

static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv)
{
  uint32 inten;

  /* EndPoint 0 initialization */

  lpc214x_eprealize(&priv->eplist[LPC214X_CTRLEP_OUT], 0, CONFIG_USBDEV_EP0_MAXSIZE);
  lpc214x_eprealize(&priv->eplist[LPC214X_CTRLEP_IN], 1, CONFIG_USBDEV_EP0_MAXSIZE);

  /* Enable EP0 interrupts (not DMA) */

  inten = lpc214x_getreg(LPC214X_USBDEV_EPINTEN);
  inten |= 3; /* EP0 Rx and Tx */
  lpc214x_putreg(inten, LPC214X_USBDEV_EPINTEN);
}

/*******************************************************************************
 * Name: lpc214x_dmareset
 *
 * Description: Reset USB DMA
 *
 *******************************************************************************/

#ifdef CONFIG_LPC214X_USBDEV_DMA
static inline void lpc214x_dmareset(uint32 enable)
{
  int i;

  /* Disable All DMA interrupts */

  lpc214x_putreg(0, LPC214X_USBDEV_DMAINTEN);
  lpc214x_putreg(0xffffffff, LPC214X_USBDEV_EPDMADIS);

  /* DMA Request clear */

  putreq32(0xffffffff, LPC214X_USBDEV_DMARCLR);

  /* End of Transfer Interrupt Clear */

  putreq32(0xffffffff, LPC214X_USBDEV_EOTINTCLR);

  /* New DD Request Interrupt Clear */

  putreq32(0xffffffff, LPC214X_USBDEV_NDDRINTCLR);

  /* System Error Interrupt Clear */

  putreq32(0xffffffff, LPC214X_USBDEV_SYSERRINTCLR);

  /* Nullify all pointers in the UDCA */

  for (i = 0; i < LPC214X_NPHYSENDPOINTS; ++i)
    {
      USB_UDCA[i] = NULL;
    }

  /* Set USB UDCA Head register */

  lpc214x_putreg((uint32)USB_UDCA, LPC214X_USBDEV_UDCAH);

  /* Invalidate all DMA descriptors */

  for (i = 0; i < CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS; ++i)
    {
      memset(&USB_DDESC[i], 0, USB_DDESCSIZE);
    }

  /* Enable DMA interrupts */

  lpc214x_putreg(enable, LPC214X_USBDEV_DMAINTEN);
}
#endif

/*******************************************************************************
 * Name: lpc214x_usbreset
 *
 * Description:
 *   Reset Usb engine
 *
 *******************************************************************************/

static void lpc214x_usbreset(struct lpc214x_usbdev_s *priv)
{
  /* Disable all endpoint interrupts */

  lpc214x_putreg(0, LPC214X_USBDEV_EPINTEN);

  /* Frame is Hp interrupt */

  lpc214x_putreg(1, LPC214X_USBDEV_DEVINTPRI);

  /* Clear all pending interrupts */

  lpc214x_putreg(0xffffffff, LPC214X_USBDEV_EPINTCLR);
  lpc214x_putreg(0xffffffff, LPC214X_USBDEV_DEVINTCLR);

  /* Periperhal address is needed */

  priv->paddrset = 0;

  /* Endpoints not yet configured */

  lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);

  /* EndPoint 0 initialization */

  lpc214x_ep0configure(priv);

#ifdef CONFIG_LPC214X_USBDEV_DMA
  /* Enable End_of_Transfer_Interrupt and System_Error_Interrupt USB DMA
   * interrupts
   */

  lpc214x_dmareset(CONFIG_LPC214X_USBDEV_DMAINTMASK);

#endif

  /* Enable Device interrupts */

  lpc214x_putreg(USB_SLOW_INT|USB_DEVSTATUS_INT|USB_FAST_INT|USB_FRAME_INT|USB_ERROR_INT,
                 LPC214X_USBDEV_DEVINTEN);
}

/*******************************************************************************
 * Name: lpc214x_dispatchrequest
 *
 * Description:
 *   Provide unhandled setup actions to the class driver. This is logically part
 *   of the USB interrupt handler.
 *
 *******************************************************************************/

static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv,
patacongo's avatar
patacongo committed
                                    const struct usb_ctrlreq_s *ctrl)
{
  int ret;

  usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DISPATCH), 0);
  if (priv && priv->driver)
    {
patacongo's avatar
patacongo committed
      /* Forward to the control request to the class driver implementation */

patacongo's avatar
patacongo committed
      ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl);
      if (ret < 0)
        {
          /* Stall on failure */

patacongo's avatar
patacongo committed
          usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DISPATCHSTALL), 0);
          priv->stalled = 1;
        }
    }
}

/*******************************************************************************
 * Name: lpc214x_ep0setup
 *
 * Description:
 *   USB Ctrl EP Setup Event. This is logically part of the USB interrupt
 *   handler.  This event occurs when a setup packet is receive on EP0 OUT.
 *
 *******************************************************************************/

static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv)
{
  struct lpc214x_ep_s *ep0 = &priv->eplist[LPC214X_EP0_OUT];
  struct lpc214x_ep_s *privep;
  struct lpc214x_req_s *privreq = lpc214x_rqpeek(ep0);
patacongo's avatar
patacongo committed
  struct usb_ctrlreq_s ctrl;
patacongo's avatar
patacongo committed
  uint16 value;
patacongo's avatar
patacongo committed
  uint16 len;
  ubyte  response[2];
  int    ret;

  /* Starting a control request? */

  if (priv->usbdev.speed == USB_SPEED_UNKNOWN)
    {
      priv->usbdev.speed = USB_SPEED_FULL;
      lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 1);
    }

  /* Terminate any pending requests */

  while (!lpc214x_rqempty(ep0))
    {
      sint16 result = OK;
      if (privreq->req.xfrd != privreq->req.len)
        {
          result = -EPROTO;
        }

patacongo's avatar
patacongo committed
      usbtrace(TRACE_COMPLETE(ep0->epphy), privreq->req.xfrd);
      lpc214x_reqcomplete(ep0, result);
    }

  /* Assume NOT stalled */

  ep0->stalled  = 0;
  priv->stalled = 0;

  /* Read EP0 data */

  ret = lpc214x_epread(LPC214X_EP0_OUT, (ubyte*)&ctrl, USB_SIZEOF_CTRLREQ);
patacongo's avatar
patacongo committed
  /* And extract the little-endian 16-bit values to host order */

  value = GETUINT16(ctrl.value);
  index = GETUINT16(ctrl.index);
  len   = GETUINT16(ctrl.len);

  ullvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
          ctrl.type, ctrl.req, value, index, len);
  /* Dispatch any non-standard requests */

patacongo's avatar
patacongo committed
  if ((ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
patacongo's avatar
patacongo committed
      lpc214x_dispatchrequest(priv, &ctrl);
      return;
    }

  /* Handle standard request.  Pick off the things of interest to the
   * USB device controller driver; pass what is left to the class driver
   */

patacongo's avatar
patacongo committed
  switch (ctrl.req)
    {
    case USB_REQ_GETSTATUS:
      {
patacongo's avatar
patacongo committed
        /* type:  device-to-host; recipient = device, interface, endpoint
         * value: 0
         * index: zero interface endpoint
         * len:   2; data = status
         */
patacongo's avatar
patacongo committed
        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETSTATUS), 0);
patacongo's avatar
patacongo committed
        if (!priv->paddrset || len != 2 ||
            (ctrl.type & USB_REQ_DIR_IN) == 0 || value != 0)
patacongo's avatar
patacongo committed
            switch (ctrl.type & USB_REQ_RECIPIENT_MASK)
              {
              case USB_REQ_RECIPIENT_ENDPOINT:
                {
                  usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPGETSTATUS), 0);
                  privep = lpc214x_epfindbyaddr(priv, index);
                  if (!privep)
                      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADEPGETSTATUS), 0);
                      priv->stalled = 1;
                    }
                  else
                    {
                       if ((lpc214x_usbcmd(CMD_USB_EP_SELECT|privep->epphy, 0) & CMD_USB_EPSELECT_ST) != 0)
                         {
                           response[0] = 1; /* Stalled */
                         }
                       else
                         {
                           response[0] = 0; /* Not stalled */
                         }
                      response[1] = 0;
                      lpc214x_epwrite(LPC214X_EP0_IN, response, 2);
                      priv->ep0state = LPC214X_EP0SHORTWRITE;
                    }
                }
                break;

              case USB_REQ_RECIPIENT_DEVICE:
                {
                  if (index == 0)
                    {
                      usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVGETSTATUS), 0);

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

                      response[0] = (priv->selfpowered << USB_FEATURE_SELFPOWERED) |
                                    (1 << USB_FEATURE_REMOTEWAKEUP);
                      response[1] = 0;
                      lpc214x_epwrite(LPC214X_EP0_IN, response, 2);
                      priv->ep0state = LPC214X_EP0SHORTWRITE;
                    }
                  else
                    {
patacongo's avatar
patacongo committed
                      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADDEVGETSTATUS), 0);
                      priv->stalled = 1;
                    }
                }
                break;

              case USB_REQ_RECIPIENT_INTERFACE:
                {
                  usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_IFGETSTATUS), 0);
                  response[0] = 0;
                  response[1] = 0;
                  lpc214x_epwrite(LPC214X_EP0_IN, response, 2);
                  priv->ep0state = LPC214X_EP0SHORTWRITE;
                }
                break;

              default:
                {
patacongo's avatar
patacongo committed
                  usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADGETSTATUS), 0);
                  priv->stalled = 1;
                }
                break;
              }
          }
      }
      break;

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

        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_CLEARFEATURE), 0);
patacongo's avatar
patacongo committed
        if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT)
patacongo's avatar
patacongo committed
            lpc214x_dispatchrequest(priv, &ctrl);
        else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
                 (privep = lpc214x_epfindbyaddr(priv, index)) != NULL)
            privep->halted = 0;
            ret = lpc214x_epstall(&privep->ep, TRUE);
            lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0);
            priv->ep0state = LPC214X_EP0STATUSIN;
          }
        else
          {
patacongo's avatar
patacongo committed
            usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADCLEARFEATURE), 0);
            priv->stalled = 1;
patacongo's avatar
patacongo committed
        /* type:  host-to-device; recipient = device, interface, endpoint
         * value: feature selector
         * index: zero interface endpoint;
         * len:   0; data = none
         */

        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SETFEATURE), 0);
patacongo's avatar
patacongo committed
        if (((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) &&
patacongo's avatar
patacongo committed
            value == USB_FEATURE_TESTMODE)
            ullvdbg("test mode: %d\n", index);
patacongo's avatar
patacongo committed
        else if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT)
patacongo's avatar
patacongo committed
           lpc214x_dispatchrequest(priv, &ctrl);
        else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
                 (privep = lpc214x_epfindbyaddr(priv, index)) != NULL)
            privep->halted = 1;
            lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0);
            priv->ep0state = LPC214X_EP0STATUSIN;
          }
        else
          {
patacongo's avatar
patacongo committed
            usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADSETFEATURE), 0);
            priv->stalled = 1;
patacongo's avatar
patacongo committed
        /* type:  host-to-device; recipient = device
         * value: device address
         * index: 0
         * len:   0; data = none
         */

patacongo's avatar
patacongo committed
        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0SETUPSETADDRESS), value);
patacongo's avatar
patacongo committed
        if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
patacongo's avatar
patacongo committed
            index  == 0 && len == 0 && value < 128)
            /* Save the address.  We cannot actually change to the next address until
             * the completion of the status phase.
             */
            priv->paddr = ctrl.value[0];

patacongo's avatar
patacongo committed
            /* Note that if we send the SETADDRESS command twice, that will force the
             * address change.  Otherwise, the hardware will automatically set the
             * address at the end of the status phase.
             */

            lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, CMD_USB_SETADDRESS_DEVEN | priv->paddr);

            /* Send a NULL packet. The status phase completes when the null packet has
             * been sent successfully.
             */

            lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0);
            priv->ep0state = LPC214X_EP0SETADDRESS;
          }
        else
          {
patacongo's avatar
patacongo committed
            usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADSETADDRESS), 0);
            priv->stalled = 1;
          }
      }
      break;

    case USB_REQ_GETDESCRIPTOR:
patacongo's avatar
patacongo committed
      /* 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:
patacongo's avatar
patacongo committed
      /* type:  host-to-device; recipient = device
       * value: descriptor type and index
       * index: 0 or language ID;
       * len:   descriptor len; data = descriptor
       */
patacongo's avatar
patacongo committed
        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETSETDESC), 0);
        if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE)
patacongo's avatar
patacongo committed
            lpc214x_dispatchrequest(priv, &ctrl);
patacongo's avatar
patacongo committed
            usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADGETSETDESC), 0);
patacongo's avatar
patacongo committed
    case USB_REQ_GETCONFIGURATION:
      /* type:  device-to-host; recipient = device
       * value: 0;
       * index: 0;
       * len:   1; data = configuration value
       */
      {
        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETCONFIG), 0);
        if (priv->paddrset && (ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
patacongo's avatar
patacongo committed
            value == 0 && index == 0 && len == 1)
patacongo's avatar
patacongo committed
          {
            lpc214x_dispatchrequest(priv, &ctrl);
          }
        else
          {
            usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADGETCONFIG), 0);
            priv->stalled = 1;
          }
      }
      break;

    case USB_REQ_SETCONFIGURATION:
patacongo's avatar
patacongo committed
      /* type:  host-to-device; recipient = device
       * value: configuration value
       * index: 0;
       * len:   0; data = none
       */
      {
        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SETCONFIG), 0);
        if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
patacongo's avatar
patacongo committed
            index == 0 && len == 0)
patacongo's avatar
patacongo committed
          {
            lpc214x_dispatchrequest(priv, &ctrl);
          }
        else
          {
            usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADSETCONFIG), 0);
            priv->stalled = 1;
          }
      }
      break;

    case USB_REQ_GETINTERFACE:
patacongo's avatar
patacongo committed
      /* type:  device-to-host; recipient = interface
       * value: 0
       * index: interface;
       * len:   1; data = alt interface
       */
    case USB_REQ_SETINTERFACE:
patacongo's avatar
patacongo committed
      /* type:  host-to-device; recipient = interface
       * value: alternate setting
       * index: interface;
       * len:   0; data = none
       */
patacongo's avatar
patacongo committed
        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETSETIF), 0);
        lpc214x_dispatchrequest(priv, &ctrl);
      }
      break;

    case USB_REQ_SYNCHFRAME:
patacongo's avatar
patacongo committed
      /* type:  device-to-host; recipient = endpoint
       * value: 0
       * index: endpoint;
       * len:   2; data = frame number
       */
      {
        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SYNCHFRAME), 0);
      }
patacongo's avatar
patacongo committed
      {
        usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDCTRLREQ), 0);
        priv->stalled = 1;
      }
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0SETUPSTALLED), priv->ep0state);
      lpc214x_epstall(&ep0->ep, FALSE);
      lpc214x_epstall(&ep0->ep, FALSE);
    }
}

/*******************************************************************************
 * Name: lpc214x_ep0dataoutinterrupt
 *
 * Description:
 *   USB Ctrl EP Data OUT Event. This is logically part of the USB interrupt
 *   handler.  Each non-isochronous OUT endpoint gives an interrupt when they
 *   receive a packet without error.
 *
 *******************************************************************************/

static inline void lpc214x_ep0dataoutinterrupt(struct lpc214x_usbdev_s *priv)
{
  struct lpc214x_ep_s *ep0;
  uint32 pktlen;

  /* Copy new setup packet into setup buffer */

  switch (priv->ep0state)
    {
    case LPC214X_EP0SHORTWRITE:
      {
        priv->ep0state = LPC214X_EP0STATUSOUT;
        pktlen = lpc214x_epread(LPC214X_EP0_OUT, NULL, CONFIG_USBDEV_EP0_MAXSIZE);
        if (LPC214X_READOVERRUN(pktlen))
          {
            lpc214x_ep0setup(priv);
          }
      }
      break;

    case LPC214X_EP0SHORTWRSENT:
      {
        priv->ep0state = LPC214X_EP0REQUEST;
        pktlen = lpc214x_epread(LPC214X_EP0_OUT, NULL, CONFIG_USBDEV_EP0_MAXSIZE);
        if (LPC214X_READOVERRUN(pktlen))
          {
            lpc214x_ep0setup(priv);
          }
      }
      break;

    case LPC214X_EP0REQUEST:
        /* Process the next request action (if any) */

        lpc214x_rdrequest(&priv->eplist[LPC214X_EP0_OUT]);
      }
      break;

    default:
      priv->stalled = 1;
      break;
    }

  if (priv->stalled)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0OUTSTALLED), priv->ep0state);
      ep0 = &priv->eplist[LPC214X_EP0_OUT];
      lpc214x_epstall(&ep0->ep, FALSE);
      lpc214x_epstall(&ep0->ep, FALSE);
    }
  return;
}

/*******************************************************************************
 * Name: lpc214x_ep0dataininterrupt
 *
 * Description:
 *   USB Ctrl EP Data IN Event. This is logically part of the USB interrupt
 *   handler.  All non-isochronous IN endpoints give this interrupt when a
 *   packet is successfully transmitted (OR a NAK handshake is sent on the bus
 *   provided that the interrupt on NAK feature is enabled).
 *
 *******************************************************************************/

static inline void lpc214x_ep0dataininterrupt(struct lpc214x_usbdev_s *priv)
{
  struct lpc214x_ep_s *ep0;

  switch (priv->ep0state)
    {
    case LPC214X_EP0STATUSOUT:
    case LPC214X_EP0STATUSIN:
      priv->ep0state = LPC214X_EP0REQUEST;
      break;

    case LPC214X_EP0SHORTWRITE:
      priv->ep0state = LPC214X_EP0SHORTWRSENT;
      break;

    case LPC214X_EP0SETADDRESS:
      {
patacongo's avatar
patacongo committed
        /* If the address was set to a non-zero value, then thiscompletes the
         * default phase, and begins the address phase (still not fully configured)
patacongo's avatar
patacongo committed
        usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0INSETADDRESS), (uint16)priv->paddr);
        lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);
        if (priv->paddr)
          {
            priv->paddrset = 1;
            priv->ep0state = LPC214X_EP0REQUEST;
    case LPC214X_EP0REQUEST:
      {
        /* Process the next request action (if any) */

        ep0 = &priv->eplist[LPC214X_EP0_IN];
        ep0->txbusy = 0;
        lpc214x_wrrequest(ep0);
    default:
      priv->stalled = 1;
      break;
    }

  if (priv->stalled)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0INSTALLED), priv->ep0state);
      ep0 = &priv->eplist[LPC214X_EP0_IN];
      lpc214x_epstall(&ep0->ep, FALSE);
      lpc214x_epstall(&ep0->ep, FALSE);
    }
}

/*******************************************************************************
 * Name: lpc214x_usbinterrupt
 *
 * Description:
 *   USB interrupt handler
 *
 *******************************************************************************/

static int lpc214x_usbinterrupt(int irq, FAR void *context)
{
  struct lpc214x_usbdev_s *priv = &g_usbdev;
  struct lpc214x_ep_s *privep ;

  uint32 devintstatus;  /* Sampled state of the device interrupt status register */
  uint32 epintstatus;   /* Sampled state of the endpoint interrupt status register */
#ifdef CONFIG_LPC214X_USBDEV_DMA
  uint32 dmaintstatus;  /* Sampled state of dma interrupt status register */
#endif
  uint32 softprio;      /* Current priority interrupt bitset */
  uint32 pending;       /* Pending subset of priority interrupt bitset */
  ubyte  epphy;         /* Physical endpoint number being processed */
  int    i;

  usbtrace(TRACE_INTENTRY(LPC214X_TRACEINTID_USB), 0);

  /* Read the device interrupt status register */

  devintstatus = lpc214x_getreg(LPC214X_USBDEV_DEVINTST);