Skip to content
Snippets Groups Projects
lpc214x_usbdev.c 92.4 KiB
Newer Older
/*******************************************************************************
 * arch/arm/src/lpc214x/lpc214x_usbdev.c
 *
 *   Copyright (C) 2008 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *******************************************************************************/

/*******************************************************************************
 * Included Files
 *******************************************************************************/

#include <nuttx/config.h>
#include <sys/types.h>

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/arch.h>
#include <nuttx/usb.h>
#include <nuttx/usbdev.h>
#include <nuttx/usbdev_trace.h>

#include <arch/irq.h>
#include <arch/board/board.h>

#include "up_arch.h"
#include "up_internal.h"
#include "lpc214x_usbdev.h"
#include "lpc214x_pll.h"
#include "lpc214x_power.h"

/*******************************************************************************
 * Definitions
 *******************************************************************************/

/* Configuration ***************************************************************/

#ifndef CONFIG_USBDEV_EP0_MAXSIZE
#  define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif

#ifndef  CONFIG_USBDEV_MAXPOWER
#  define CONFIG_USBDEV_MAXPOWER 100  /* mA */
#endif

#define USB_SLOW_INT USBDEV_DEVINT_EPSLOW
#define USB_DEVSTATUS_INT USBDEV_DEVINT_DEVSTAT

#ifdef CONFIG_LPC214X_USBDEV_EPFAST_INTERRUPT
#  define USB_FAST_INT USBDEV_DEVINT_EPFAST
#else
#  define USB_FAST_INT 0
#endif

/* Extremely detailed register debug that you would normally never want
 * enabled.
 */

#undef CONFIG_LPC214X_USBDEV_REGDEBUG

/* Enable reading SOF from interrupt handler vs. simply reading on demand.  Probably
 * a bad idea... Unless there is some issue with sampling the SOF from hardware
 * asynchronously.
 */

#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT
#  define USB_FRAME_INT USBDEV_DEVINT_FRAME
#else
#  define USB_FRAME_INT 0
#endif

#ifdef CONFIG_DEBUG
#  define USB_ERROR_INT USBDEV_DEVINT_EPRINT
#else
#  define USB_ERROR_INT 0
#endif

/* Number of DMA descriptors */

#ifdef CONFIG_LPC214X_USBDEV_DMA
# error DMA SUPPORT NOT YET FULLY IMPLEMENTED
#  ifndef CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS
#    define CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS 8
#  elif CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS > 30
#    define CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS 30
#  endif
#endif

/* Debug ***********************************************************************/

/* Trace error codes */

#define LPC214X_TRACEERR_ALLOCFAIL        0x0001
patacongo's avatar
patacongo committed
#define LPC214X_TRACEERR_BADCLEARFEATURE  0x0002
#define LPC214X_TRACEERR_BADDEVGETSTATUS  0x0003
#define LPC214X_TRACEERR_BADEPNO          0x0004
#define LPC214X_TRACEERR_BADEPGETSTATUS   0x0005
#define LPC214X_TRACEERR_BADEPTYPE        0x0006
#define LPC214X_TRACEERR_BADGETCONFIG     0x0007
#define LPC214X_TRACEERR_BADGETSETDESC    0x0008
#define LPC214X_TRACEERR_BADGETSTATUS     0x0009
#define LPC214X_TRACEERR_BADREQUEST       0x000a
#define LPC214X_TRACEERR_BADSETADDRESS    0x000b
#define LPC214X_TRACEERR_BADSETCONFIG     0x000c
#define LPC214X_TRACEERR_BADSETFEATURE    0x000d
#define LPC214X_TRACEERR_BINDFAILED       0x000e
#define LPC214X_TRACEERR_DMABUSY          0x000f
#define LPC214X_TRACEERR_DRIVER           0x0010
#define LPC214X_TRACEERR_DRIVERREGISTERED 0x0011
#define LPC214X_TRACEERR_EPREAD           0x0012
#define LPC214X_TRACEERR_INVALIDCMD       0x0013
#define LPC214X_TRACEERR_INVALIDCTRLREQ   0x0014
#define LPC214X_TRACEERR_INVALIDPARMS     0x0015
#define LPC214X_TRACEERR_IRQREGISTRATION  0x0016
#define LPC214X_TRACEERR_NODMADESC        0x0017
#define LPC214X_TRACEERR_NOEP             0x0018
#define LPC214X_TRACEERR_NOTCONFIGURED    0x0019
#define LPC214X_TRACEERR_NULLPACKET       0x001a
#define LPC214X_TRACEERR_NULLREQUEST      0x001b
#define LPC214X_TRACEERR_STALLED          0x001c

/* Trace interrupt codes */

#define LPC214X_TRACEINTID_USB            0x0001
#define LPC214X_TRACEINTID_CLEARFEATURE   0x0002
#define LPC214X_TRACEINTID_CONNECTCHG     0x0003
#define LPC214X_TRACEINTID_CONNECTED      0x0004
#define LPC214X_TRACEINTID_DEVGETSTATUS   0x0005
#define LPC214X_TRACEINTID_DEVRESET       0x0006
#define LPC214X_TRACEINTID_DEVSTAT        0x0007
#define LPC214X_TRACEINTID_DISCONNECTED   0x0008
#define LPC214X_TRACEINTID_DISPATCH       0x0009
#define LPC214X_TRACEINTID_EP0IN          0x000a
#define LPC214X_TRACEINTID_EP0OUT         0x000b
#define LPC214X_TRACEINTID_EP0SETUP       0x000c
#define LPC214X_TRACEINTID_EPDMA          0x000d
#define LPC214X_TRACEINTID_EPFAST         0x000e
#define LPC214X_TRACEINTID_EPGETSTATUS    0x000f
#define LPC214X_TRACEINTID_EPIN           0x0010
#define LPC214X_TRACEINTID_EPOUT          0x0011
#define LPC214X_TRACEINTID_EPRINT         0x0012
#define LPC214X_TRACEINTID_EPSLOW         0x0013
#define LPC214X_TRACEINTID_FRAME          0x0014
patacongo's avatar
patacongo committed
#define LPC214X_TRACEINTID_GETCONFIG      0x0015
#define LPC214X_TRACEINTID_GETSETDESC     0x0016
#define LPC214X_TRACEINTID_GETSETIF       0x0017
#define LPC214X_TRACEINTID_GETSTATUS      0x0018
#define LPC214X_TRACEINTID_IFGETSTATUS    0x0019
#define LPC214X_TRACEERR_REQABORTED       0x001a
#define LPC214X_TRACEINTID_SETADDRESS     0x001b
#define LPC214X_TRACEINTID_SETCONFIG      0x001c
#define LPC214X_TRACEINTID_SETFEATURE     0x001d
#define LPC214X_TRACEINTID_SUSPENDCHG     0x001e
#define LPC214X_TRACEINTID_SYNCHFRAME     0x001f

/* Hardware interface **********************************************************/

/* Macros for testing the device status response */

#define DEVSTATUS_CONNECT(s)    (((s)&USBDEV_DEVSTATUS_CONNECT)!=0)
#define DEVSTATUS_CONNCHG(s)    (((s)&USBDEV_DEVSTATUS_CONNCHG)!=0)
#define DEVSTATUS_SUSPEND(s)    (((s)&USBDEV_DEVSTATUS_SUSPEND)!=0)
#define DEVSTATUS_SUSPCHG(s)    (((s)&USBDEV_DEVSTATUS_SUSPCHG)!=0)
#define DEVSTATUS_RESET(s)      (((s)&USBDEV_DEVSTATUS_RESET)!=0)

/* If this bit is set in the lpc214x_epread response, it means that the
 * recevied packet was overwritten by a later setup packet (ep0 only).
 */

#define LPC214X_READOVERRUN_BIT (0x80000000)
#define LPC214X_READOVERRUN(s)  (((s) & LPC214X_READOVERRUN_BIT) != 0)

/* USB RAM  ********************************************************************
 *
 * UBS_UDCA is is list of 32 pointers to DMA desciptors located at the
 * beginning of USB RAM.  Each pointer points to a DMA descriptor with
 * assocated DMA buffer.
 */

#define USB_UDCA           (uint32*)LPC214X_USBDEV_RAMBASE)
#define USB_USCASIZE       (LPC214X_NPHYSENDPOINTS*sizeof(uint32))

/* Each descriptor must be aligned to a 128 address boundary */

#define USB_DDALIGNDOWN(a) ((a)&~0x7f)
#define USB_DDALIGNUP(a)   USB_DDALIGNDOWN((a)+0x7f)

#define USB_DDSIZE USB_DDALIGNDOWN((LPC214X_USBDEV_RAMSIZE-USB_USCASIZE)/CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS)
#define USB_DDESC  ((struct lpc214x_dmadesc_s*)(LPC214X_USBDEV_RAMBASE+USB_USCASIZE))

#ifdef CONFIG_USBDEV_ISOCHRONOUS
#  define USB_DDESCSIZE (5*sizeof(uint32))
#else
#  define USB_DDESCSIZE (4*sizeof(uint32))
#endif

/* Endpoints ******************************************************************/

/* Number of endpoints */

#define LPC214X_NLOGENDPOINTS        (16)          /* ep0-15 */
#define LPC214X_NPHYSENDPOINTS       (32)          /* x2 for IN and OUT */

/* Odd physical endpoint numbers are IN; even are out */

#define LPC214X_EPPHYIN(epphy)       (((epphy)&1)!=0)
#define LPC214X_EPPHYOUT(epphy)      (((epphy)&1)==0)

/* Mapping to more traditional endpoint numbers */

#define LPC214X_EP_LOG2PHYOUT(ep)    ((ep)&0x0f)<<1))
#define LPC214X_EP_LOG2PHYIN(ep)     (LPC214X_EP_OUT(ep)|0x01)
#define LPC214X_EP_LOG2PHY(ep)       ((((ep)&0x0f)<<1)|(((ep)&0x80)>>7))

/* Each endpoint has somewhat different characteristics */

#define LPC214X_EPALLSET             (0xffffffff)  /* All endpoints */
#define LPC214X_EPOUTSET             (0x55555555)  /* Even phy endpoint numbers are OUT EPs */
#define LPC214X_EPINSET              (0xaaaaaaaa)  /* Odd endpoint numbers are IN EPs */
#define LPC214X_EPCTRLSET            (0x00000003)  /* EP0 IN/OUT are control endpoints */
#define LPC214X_EPINTRSET            (0x0c30c30c)  /* Interrupt endpoints */
#define LPC214X_EPBULKSET            (0xf0c30c30)  /* Bulk endpoints */
#define LPC214X_EPISOCSET            (0x030c30c0)  /* Isochronous endpoints */
#define LPC214X_EPDBLBUFFER          (0xf3cf3cf0)  /* Double buffered endpoints */
#define LPC214X_EP0MAXPACKET         (64)          /* EP0 max packet size (1-64) */
#define LPC214X_BULKMAXPACKET        (64)          /* Bulk endpoint max packet (8/16/32/64) */
#define LPC214X_INTRMAXPACKET        (64)          /* Interrupt endpoint max packet (1 to 64) */
#define LPC214X_ISOCMAXPACKET        (512)         /* Acutally 1..1023 */

/* EP0 status */

#define LPC214X_EP0IDLE              (0)           /* Nothing in progress */
#define LPC214X_EP0STATUSIN          (1)           /* Status sent */
#define LPC214X_EP0STATUSOUT         (2)           /* Status received */
#define LPC214X_EP0SHORTWRITE        (3)           /* Short data sent with no request */
#define LPC214X_EP0SHORTWRSENT       (4)           /* Short data write complete */
#define LPC214X_EP0SETADDRESS        (5)           /* Set address received */

/* Request queue operations ****************************************************/

#define lpc214x_rqempty(ep)          ((ep)->head == NULL)
#define lpc214x_rqpeek(ep)           ((ep)->head)

/*******************************************************************************
 * Private Types
 *******************************************************************************/

/* A container for a request so that the request make be retained in a list */

struct lpc214x_req_s
{
  struct usbdev_req_s    req;           /* Standard USB request */
  struct lpc214x_req_s  *flink;         /* Supports a singly linked list */
};

/* This is the internal representation of an endpoint */

struct lpc214x_ep_s
{
  /* Common endpoint fields.  This must be the first thing defined in the
   * structure so that it is possible to simply cast from struct usbdev_ep_s
   * to struct lpc214x_ep_s.
   */

  struct usbdev_ep_s      ep;            /* Standard endpoint structure */

  /* LPC214X-specific fields */

  struct lpc214x_usbdev_s *dev;          /* Reference to private driver data */
  struct lpc214x_req_s    *head;         /* Request list for this endpoint */
  struct lpc214x_req_s    *tail;
  ubyte                   eplog;         /* Logical EP address from descriptor */
  ubyte                   epphy;         /* Physical EP address */
  ubyte                   stalled:1;     /* Endpoint is halted */
  ubyte                   halted:1;      /* Endpoint feature halted */
};

/* This represents a DMA descriptor */

#ifdef CONFIG_LPC214X_USBDEV_DMA
struct lpc214x_dmadesc_s
{
  uint32                  nextdesc;      /* Address of the next DMA descripto in RAM */
  uint32                  config;        /* Misc. bit encoded configuration information */
  uint32                  start;         /* DMA start address */
  uint32                  status;        /* Misc. bit encoded status inforamation */
#ifdef CONFIG_USBDEV_ISOCHRONOUS
  uint32                  size;          /* Isochronous packet size address */
#endif
  ubyte                   buffer[USB_DDSIZE-USB_DDESCSIZE];
};
#endif

/* This structure retains the state of the USB device controller */

struct lpc214x_usbdev_s
{
  /* Common device fields.  This must be the first thing defined in the
   * structure so that it is possible to simply cast from struct usbdev_s
   * to structlpc214x_usbdev_s.
   */

  struct usbdev_s         usbdev;

  /* The bound device class driver */

  struct usbdevclass_driver_s *driver;

  /* LPC214X-specific fields */

  ubyte                   devstatus;     /* Last response to device status command */
  ubyte                   ep0state;      /* State of certain EP0 operations */
  ubyte                   stalled:1;     /* 1: Protocol stalled */
  ubyte                   selfpowered:1; /* 1: Device is self powered */
  ubyte                   paddrset:1;    /* 1: Peripheral addr has been set */
  ubyte                   attached:1;    /* 1: Host attached */
  ubyte                   rxpending:1;   /* 1: RX pending */
  uint32                  epavail;       /* Available endpoints */
  uint32                  softprio;      /* Bitset of high priority interrupts */
  uint32                  wravail;       /* Bitset of available endpoints */
#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTER
Loading
Loading full blame...