Skip to content
Snippets Groups Projects
stm32_usbdev.c 107 KiB
Newer Older
patacongo's avatar
patacongo committed
/****************************************************************************
 * arch/arm/src/stm32/stm32_usbdev.c
 *
 *   Copyright (C) 2009 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * References:
 *   - RM0008 Reference manual, STMicro document ID 13902
 *   - STM32F10xxx USB development kit, UM0424, STMicro
 *
patacongo's avatar
patacongo committed
 * 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 "up_arch.h"
#include "stm32_internal.h"
#include "stm32_usbdev.h"

#if defined(CONFIG_USBDEV) && defined(CONFIG_STM32_USB)

/****************************************************************************
 * Pre-processor 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_STM32_USBDEV_EPFAST_INTERRUPT
#  define USB_FAST_INT USBDEV_DEVINT_EPFAST
#else
#  define USB_FAST_INT 0
#endif

#ifndef CONFIG_USB_PRI
patacongo's avatar
patacongo committed
#  define CONFIG_USB_PRI NVIC_SYSH_PRIORITY_DEFAULT
patacongo's avatar
patacongo committed
#endif

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

#ifndef CONFIG_DEBUG
#  undef CONFIG_STM32_USBDEV_REGDEBUG
#endif

patacongo's avatar
patacongo committed
/* Initial interrupt mask: Reset + Suspend + Correct Transfer */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
#define STM32_CNTR_SETUP     (USB_CNTR_RESETM|USB_CNTR_SUSPM|USB_CNTR_CTRM)
patacongo's avatar
patacongo committed

/* Endpoint identifiers. The STM32 supports up to 16 mono-directional or 8
 * bidirectional endpoints.  However, when you take into account PMA buffer
 * usage (see below) and the fact that EP0 is bidirectional, then there is
patacongo's avatar
patacongo committed
 * a functional limitation of EP0 + 5 mono-directional endpoints = 6.  We'll
 * define STM32_NENDPOINTS to be 8, however, because that is how many
 * endpoint register sets there are.
patacongo's avatar
patacongo committed
 */

#define STM32_NENDPOINTS      (8)
#define EP0                   (0)
#define EP1                   (1)
#define EP2                   (2)
#define EP3                   (3)
#define EP4                   (4)
#define EP5                   (5)
#define EP6                   (6)
#define EP7                   (7)

#define STM32_ENDP_BIT(ep)    (1 << (ep))
#define STM32_ENDP_ALLSET     0xff

/* Packet sizes.  We us a fixed 64 max packet size for all endpoint types */

#define STM32_MAXPACKET_SHIFT (6)
#define STM32_MAXPACKET_SIZE  (1 << (STM32_MAXPACKET_SHIFT))
#define STM32_MAXPACKET_MASK  (STM32_MAXPACKET_SIZE-1)

#define STM32_EP0MAXPACKET    STM32_MAXPACKET_SIZE 

patacongo's avatar
patacongo committed
/* Buffer descriptor table.  We assume that USB has exclusive use of CAN/USB
 * memory.  The buffer table is positioned at the beginning of the 512-byte
patacongo's avatar
patacongo committed
 * CAN/USB memory.  We will use the first STM32_NENDPOINTS*4 words for the buffer
patacongo's avatar
patacongo committed
 * table.  That is exactly 64 bytes, leaving 7*64 bytes for endpoint buffers.
patacongo's avatar
patacongo committed
#define STM32_BTABLE_ADDRESS  (0x00)   /* Start at the beginning of USB/CAN RAM */
#define STM32_DESC_SIZE       (8)      /* Each descriptor is 4*2=8 bytes in size */
#define STM32_BTABLE_SIZE     (STM32_NENDPOINTS*STM32_DESC_SIZE)
patacongo's avatar
patacongo committed

/* Buffer layout.  Assume that all buffers are 64-bytes (maxpacketsize), then
 * we have space for only 7 buffers; endpoint 0 will require two buffers, leaving
 * 5 for other endpoints.
 */

#define STM32_BUFFER_START    STM32_BTABLE_SIZE
#define STM32_EP0_RXADDR      STM32_BUFFER_START
#define STM32_EP0_TXADDR      (STM32_EP0_RXADDR+STM32_EP0MAXPACKET)

patacongo's avatar
patacongo committed
#warning "Doesn't the buffer size need to include 2 bytes for the CRC?"

patacongo's avatar
patacongo committed
#define STM32_BUFFER_EP0      0x03
#define STM32_NBUFFERS        7
#define STM32_BUFFER_BIT(bn)  (1 << (bn))
#define STM32_BUFFER_ALLSET   0x7f
#define STM32_BUFNO2BUF(bn)   (STM32_BUFFER_START+((bn)<<STM32_MAXPACKET_SHIFT))

/* USB-related masks */

#define REQRECIPIENT_MASK     (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)

/* Endpoint rister masks (handling toggle fields) */

#define EPR_NOTOG_MASK        (USB_EPR_CTR_RX  | USB_EPR_SETUP  | USB_EPR_EPTYPE_MASK |\
                               USB_EPR_EP_KIND | USB_EPR_CTR_TX | USB_EPR_EA_MASK)
#define EPR_TXDTOG_MASK       (USB_EPR_STATTX_MASK | EPR_NOTOG_MASK)
#define EPR_RXDTOG_MASK       (USB_EPR_STATRX_MASK | EPR_NOTOG_MASK)

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

#define stm32_rqempty(ep)     ((ep)->head == NULL)
#define stm32_rqpeek(ep)      ((ep)->head)

/* USB trace ****************************************************************/
/* Trace error codes */

#define STM32_TRACEERR_ALLOCFAIL            0x0001
#define STM32_TRACEERR_BADCLEARFEATURE      0x0002
#define STM32_TRACEERR_BADDEVGETSTATUS      0x0003
#define STM32_TRACEERR_BADEPGETSTATUS       0x0004
#define STM32_TRACEERR_BADEPNO              0x0005
#define STM32_TRACEERR_BADEPTYPE            0x0006
#define STM32_TRACEERR_BADGETCONFIG         0x0007
#define STM32_TRACEERR_BADGETSETDESC        0x0008
#define STM32_TRACEERR_BADGETSTATUS         0x0009
#define STM32_TRACEERR_BADSETADDRESS        0x000a
#define STM32_TRACEERR_BADSETCONFIG         0x000b
#define STM32_TRACEERR_BADSETFEATURE        0x000c
#define STM32_TRACEERR_BINDFAILED           0x000d
#define STM32_TRACEERR_DISPATCHSTALL        0x000e
#define STM32_TRACEERR_DRIVER               0x000f
#define STM32_TRACEERR_DRIVERREGISTERED     0x0010
patacongo's avatar
patacongo committed
#define STM32_TRACEERR_EP0BADCTR            0x0011
#define STM32_TRACEERR_EP0SETUPSTALLED      0x0012
#define STM32_TRACEERR_EPBUFFER             0x0013
#define STM32_TRACEERR_EPDISABLED           0x0014
#define STM32_TRACEERR_EPOUTNULLPACKET      0x0015
#define STM32_TRACEERR_EPRESERVE            0x0016
#define STM32_TRACEERR_INVALIDCTRLREQ       0x0017
#define STM32_TRACEERR_INVALIDPARMS         0x0018
#define STM32_TRACEERR_IRQREGISTRATION      0x0019
#define STM32_TRACEERR_NOTCONFIGURED        0x001a
#define STM32_TRACEERR_REQABORTED           0x001b
patacongo's avatar
patacongo committed

/* Trace interrupt codes */

#define STM32_TRACEINTID_CLEARFEATURE       0x0001
#define STM32_TRACEINTID_DEVGETSTATUS       0x0002
#define STM32_TRACEINTID_DISPATCH           0x0003
#define STM32_TRACEINTID_EP0IN              0x0004
#define STM32_TRACEINTID_EP0INDONE          0x0005
#define STM32_TRACEINTID_EP0OUTDONE         0x0006
#define STM32_TRACEINTID_EP0SETUPDONE       0x0007
#define STM32_TRACEINTID_EP0SETUPSETADDRESS 0x0008
#define STM32_TRACEINTID_EPGETSTATUS        0x0009
#define STM32_TRACEINTID_EPINDONE           0x000a
#define STM32_TRACEINTID_EPINQEMPTY         0x000b
#define STM32_TRACEINTID_EPOUTDONE          0x000c
#define STM32_TRACEINTID_EPOUTPENDING       0x000d
#define STM32_TRACEINTID_EPOUTQEMPTY        0x000e
#define STM32_TRACEINTID_ESOF               0x000f
#define STM32_TRACEINTID_GETCONFIG          0x0010
#define STM32_TRACEINTID_GETSETDESC         0x0011
#define STM32_TRACEINTID_GETSETIF           0x0012
#define STM32_TRACEINTID_GETSTATUS          0x0013
#define STM32_TRACEINTID_HPINTERRUPT        0x0014
#define STM32_TRACEINTID_IFGETSTATUS        0x0015
#define STM32_TRACEINTID_LPCTR              0x0016
#define STM32_TRACEINTID_LPINTERRUPT        0x0017
#define STM32_TRACEINTID_NOSTDREQ           0x0018
#define STM32_TRACEINTID_RESET              0x0019
#define STM32_TRACEINTID_SETCONFIG          0x001a
#define STM32_TRACEINTID_SETFEATURE         0x001b
#define STM32_TRACEINTID_SUSP               0x001c
#define STM32_TRACEINTID_SYNCHFRAME         0x001d
#define STM32_TRACEINTID_WKUP               0x001e
patacongo's avatar
patacongo committed

/* Ever-present MIN and MAX macros */

#ifndef MIN
#  define MIN(a,b) (a < b ? a : b)
#endif

#ifndef MAX
#  define MAX(a,b) (a > b ? a : b)
#endif

/* Byte ordering in host-based values */

#ifdef CONFIG_ENDIAN_BIG
#  define LSB 1
#  define MSB 0
#else
#  define LSB 0
#  define MSB 1
#endif

patacongo's avatar
patacongo committed
/****************************************************************************
 * Private Type Definitions
 ****************************************************************************/

/* The various states of a control pipe */

enum stm32_devstate_e 
{
patacongo's avatar
patacongo committed
  DEVSTATE_IDLE = 0,        /* No request in progress */
patacongo's avatar
patacongo committed
  DEVSTATE_RDREQUEST,       /* Read request in progress */
  DEVSTATE_WRREQUEST,       /* Write request in progress */
  DEVSTATE_STALLED          /* We are stalled */
};

/* Resume states */

enum stm32_rsmstate_e 
{
  RSMSTATE_IDLE = 0,        /* Device is either fully suspended or running */
  RSMSTATE_STARTED,         /* Resume sequence has been started */
  RSMSTATE_WAITING          /* Waiting (on ESOFs) for end of sequence */
};

union wb_u
{
  uint16 w;
  ubyte  b[2];
};

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

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

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

struct stm32_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 stm32_ep_s.
   */

  struct usbdev_ep_s      ep;           /* Standard endpoint structure */

  /* STR71X-specific fields */

  struct stm32_usbdev_s *dev;           /* Reference to private driver data */
  struct stm32_req_s    *head;          /* Request list for this endpoint */
  struct stm32_req_s    *tail;
  ubyte                  bufno;         /* Allocated buffer number */
  ubyte                  stalled:1;     /* TRUE: Endpoint is stalled */
  ubyte                  halted:1;      /* TRUE: Endpoint feature halted */
  ubyte                  txbusy:1;      /* TRUE: TX endpoint FIFO full */
patacongo's avatar
patacongo committed
  ubyte                  txnullpkt:1;   /* Null packet needed at end of transfer */
};

struct stm32_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 structstm32_usbdev_s.
   */

  struct usbdev_s usbdev;

  /* The bound device class driver */

  struct usbdevclass_driver_s *driver;

  /* STM32-specific fields */

  struct usb_ctrlreq_s     ctrl;          /* Last EP0 request */
  ubyte                    devstate;      /* Driver state (see enum stm32_devstate_e) */
  ubyte                    rsmstate;      /* Resume state (see enum stm32_rsmstate_e) */
  ubyte                    nesofs;        /* ESOF counter (for resume support) */
  ubyte                    rxpending:1;   /* 1: OUT data in PMA, but no read requests */
  ubyte                    selfpowered:1; /* 1: Device is self powered */
  ubyte                    epavail;       /* Bitset of available endpoints */
  ubyte                    bufavail;      /* Bitset of available buffers */
  uint16                   rxstatus;      /* Saved during interrupt processing */
  uint16                   txstatus;      /* "   " "    " "       " "        " */
  uint16                   imask;         /* Current interrupt mask */

  /* The endpoint list */

  struct stm32_ep_s        eplist[STM32_NENDPOINTS];
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Register operations ******************************************************/

#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint16 stm32_getreg(uint32 addr);
static void stm32_putreg(uint16 val, uint32 addr);
patacongo's avatar
patacongo committed
static void stm32_checksetup(void);
static void stm32_dumpep(int epno);
patacongo's avatar
patacongo committed
#else
# define stm32_getreg(addr)      getreg16(addr)
# define stm32_putreg(val,addr)  putreg16(val,addr)
patacongo's avatar
patacongo committed
# define stm32_checksetup()
# define stm32_dumpep(epno)
patacongo's avatar
patacongo committed
#endif

/* Low-Level Helpers ********************************************************/

static inline void
patacongo's avatar
patacongo committed
              stm32_seteptxcount(ubyte epno, uint16 count);
patacongo's avatar
patacongo committed
static inline void
patacongo's avatar
patacongo committed
              stm32_seteptxaddr(ubyte epno, uint16 addr);
static inline uint16
              stm32_geteptxaddr(ubyte epno);
patacongo's avatar
patacongo committed
static void   stm32_seteprxcount(ubyte epno, uint16 count);
static inline uint16
              stm32_geteprxcount(ubyte epno);
patacongo's avatar
patacongo committed
static inline void
patacongo's avatar
patacongo committed
              stm32_seteprxaddr(ubyte epno, uint16 addr);
static inline uint16
              stm32_geteprxaddr(ubyte epno);
patacongo's avatar
patacongo committed
static inline void
patacongo's avatar
patacongo committed
              stm32_setepaddress(ubyte epno, uint16 addr);
patacongo's avatar
patacongo committed
static inline void
              stm32_seteptype(ubyte epno, uint16 type);
static inline void
patacongo's avatar
patacongo committed
              stm32_seteptxaddr(ubyte epno, uint16 addr);
patacongo's avatar
patacongo committed
static inline void
              stm32_setstatusout(ubyte epno);
static inline void
              stm32_clrstatusout(ubyte epno);
static void   stm32_clrrxdtog(ubyte epno);
static void   stm32_clrtxdtog(ubyte epno);
static void   stm32_clrepctrrx(ubyte epno);
static void   stm32_clrepctrtx(ubyte epno);
static void   stm32_seteptxstatus(ubyte epno, uint16 state);
static void   stm32_seteprxstatus(ubyte epno, uint16 state);
static inline uint16
              stm32_geteptxstatus(ubyte epno);
static inline uint16
              stm32_geteprxstatus(ubyte epno);
static uint16 stm32_eptxstalled(ubyte epno);
static uint16 stm32_eprxstalled(ubyte epno);
patacongo's avatar
patacongo committed
static void   stm32_setimask(struct stm32_usbdev_s *priv, uint16 setbits,
                uint16 clrbits);
patacongo's avatar
patacongo committed

/* Suspend/Resume Helpers ***************************************************/

patacongo's avatar
patacongo committed
static void   stm32_suspend(struct stm32_usbdev_s *priv);
static void   stm32_initresume(struct stm32_usbdev_s *priv);
static void   stm32_esofpoll(struct stm32_usbdev_s *priv) ;

/* Request Helpers **********************************************************/

static void   stm32_copytopma(const ubyte *buffer, uint16 pma,
                uint16 nbytes);
patacongo's avatar
patacongo committed
static inline void
patacongo's avatar
patacongo committed
              stm32_copyfrompma(ubyte *buffer, uint16 pma, uint16 nbytes);
patacongo's avatar
patacongo committed
static struct stm32_req_s *
              stm32_rqdequeue(struct stm32_ep_s *privep);
static void   stm32_rqenqueue(struct stm32_ep_s *privep,
                struct stm32_req_s *req);
static inline void
              stm32_abortrequest(struct stm32_ep_s *privep,
                struct stm32_req_s *privreq, sint16 result);
static void   stm32_reqcomplete(struct stm32_ep_s *privep, sint16 result);
static void   stm32_epwrite(struct stm32_usbdev_s *buf,
                struct stm32_ep_s *privep, const ubyte *data, uint32 nbytes);
static int    stm32_wrrequest(struct stm32_usbdev_s *priv,
                struct stm32_ep_s *privep);
static int    stm32_rdrequest(struct stm32_usbdev_s *priv,
                struct stm32_ep_s *privep);
patacongo's avatar
patacongo committed

/* Interrupt level processing ***********************************************/

static int    stm32_dispatchrequest(struct stm32_usbdev_s *priv);
static void   stm32_epdone(struct stm32_usbdev_s *priv, ubyte epno);
static void   stm32_setdevaddr(struct stm32_usbdev_s *priv, ubyte value);
patacongo's avatar
patacongo committed
static void   stm32_ep0setup(struct stm32_usbdev_s *priv);
static void   stm32_ep0out(struct stm32_usbdev_s *priv);
patacongo's avatar
patacongo committed
static void   stm32_ep0in(struct stm32_usbdev_s *priv);
static inline void
              stm32_ep0done(struct stm32_usbdev_s *priv, uint16 istr);
patacongo's avatar
patacongo committed
static void   stm32_lptransfer(struct stm32_usbdev_s *priv);
static int    stm32_hpinterrupt(int irq, void *context);
static int    stm32_lpinterrupt(int irq, void *context);

/* Endpoint helpers *********************************************************/

static inline struct stm32_ep_s *
patacongo's avatar
patacongo committed
              stm32_epreserve(struct stm32_usbdev_s *priv, ubyte epset);
patacongo's avatar
patacongo committed
static inline void
patacongo's avatar
patacongo committed
              stm32_epunreserve(struct stm32_usbdev_s *priv,
patacongo's avatar
patacongo committed
                struct stm32_ep_s *privep);
static inline boolean
              stm32_epreserved(struct stm32_usbdev_s *priv, int epno);
patacongo's avatar
patacongo committed
static int    stm32_epallocpma(struct stm32_usbdev_s *priv);
patacongo's avatar
patacongo committed
static inline void
patacongo's avatar
patacongo committed
              stm32_epfreepma(struct stm32_usbdev_s *priv,
patacongo's avatar
patacongo committed
                struct stm32_ep_s *privep);

/* Endpoint operations ******************************************************/

static int    stm32_epconfigure(struct usbdev_ep_s *ep,
                const struct usb_epdesc_s *desc, boolean last);
static int    stm32_epdisable(struct usbdev_ep_s *ep);
static struct usbdev_req_s *
              stm32_epallocreq(struct usbdev_ep_s *ep);
static void   stm32_epfreereq(struct usbdev_ep_s *ep,
                struct usbdev_req_s *);
static int    stm32_epsubmit(struct usbdev_ep_s *ep,
                struct usbdev_req_s *req);
static int    stm32_epcancel(struct usbdev_ep_s *ep,
                struct usbdev_req_s *req);
static int    stm32_epstall(struct usbdev_ep_s *ep, boolean resume);

/* USB device controller operations *****************************************/

static struct usbdev_ep_s *
              stm32_allocep(struct usbdev_s *dev, ubyte epno, boolean in,
                ubyte eptype);
static void   stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep);
static int    stm32_getframe(struct usbdev_s *dev);
static int    stm32_wakeup(struct usbdev_s *dev);
static int    stm32_selfpowered(struct usbdev_s *dev, boolean selfpowered);

patacongo's avatar
patacongo committed
/* Initialization/Reset *****************************************************/
patacongo's avatar
patacongo committed

static void   stm32_reset(struct stm32_usbdev_s *priv);
static void   stm32_hwreset(struct stm32_usbdev_s *priv);

/****************************************************************************
 * Private Data
 ****************************************************************************/

/* Since there is only a single USB interface, all status information can be
 * be simply retained in a single global instance.
 */

static struct stm32_usbdev_s g_usbdev;

static const struct usbdev_epops_s g_epops =
{
  .configure   = stm32_epconfigure,
  .disable     = stm32_epdisable,
  .allocreq    = stm32_epallocreq,
  .freereq     = stm32_epfreereq,
  .submit      = stm32_epsubmit,
  .cancel      = stm32_epcancel,
  .stall       = stm32_epstall,
};

static const struct usbdev_ops_s g_devops =
{
  .allocep     = stm32_allocep,
  .freeep      = stm32_freeep,
  .getframe    = stm32_getframe,
  .wakeup      = stm32_wakeup,
  .selfpowered = stm32_selfpowered,
  .pullup      = stm32_usbpullup,
};

/****************************************************************************
 * Public Data
 ****************************************************************************/

/****************************************************************************
 * Private Private Functions
 ****************************************************************************/
  
/****************************************************************************
 * Register Operations
 ****************************************************************************/
/****************************************************************************
 * Name: stm32_getreg
 ****************************************************************************/

#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint16 stm32_getreg(uint32 addr)
{
  static uint32 prevaddr = 0;
  static uint16 preval = 0;
  static uint32 count = 0;

  /* Read the value from the register */

  uint16 val = getreg16(addr);

  /* Is this the same value that we read from the same register last time?
   * Are we polling the register?  If so, suppress some of the output.
   */

patacongo's avatar
patacongo committed
  if (addr == prevaddr && val == preval)
patacongo's avatar
patacongo committed
    {
      if (count == 0xffffffff || ++count > 3)
        {
           if (count == 4)
             {
               lldbg("...\n");
             }
          return val;
        }
    }

  /* No this is a new address or value */

  else
    {
       /* Did we print "..." for the previous value? */

       if (count > 3)
         {
           /* Yes.. then show how many times the value repeated */

           lldbg("[repeats %d more times]\n", count-3);
         }

       /* Save the new address, value, and count */

       prevaddr = addr;
       preval   = val;
       count    = 1;
    }

  /* Show the register value read */

  lldbg("%08x->%04x\n", addr, val);
patacongo's avatar
patacongo committed
  return val;
}
#endif

/****************************************************************************
 * Name: stm32_putreg
 ****************************************************************************/

#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void stm32_putreg(uint16 val, uint32 addr)
{
  /* Show the register value being written */

  lldbg("%08x<-%04x\n", addr, val);
patacongo's avatar
patacongo committed

  /* Write the value */

  putreg32(val, addr);
}
#endif

/****************************************************************************
 * Name: stm32_dumpep
 ****************************************************************************/

#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void stm32_dumpep(int epno)
{
  uint32 addr;

  /* Common registers */

  lldbg("CNTR:   %04x\n", getreg16(STM32_USB_CNTR));
  lldbg("ISTR:   %04x\n", getreg16(STM32_USB_ISTR));
  lldbg("FNR:    %04x\n", getreg16(STM32_USB_FNR));
  lldbg("DADDR:  %04x\n", getreg16(STM32_USB_DADDR));
  lldbg("BTABLE: %04x\n", getreg16(STM32_USB_BTABLE));

  /* Endpoint register */

  addr = STM32_USB_EPR(epno);
patacongo's avatar
patacongo committed
  lldbg("EPR%d:   [%08x] %04x\n", epno, addr, getreg16(addr));

  /* Endpoint descriptor */

  addr = STM32_USB_BTABLE_ADDR(epno, 0);
  lldbg("DESC:   %08x\n", addr);

  /* Endpoint buffer descriptor */

  addr = STM32_USB_ADDR_TX(epno);
  lldbg("  TX ADDR:  [%08x] %04x\n",  addr, getreg16(addr));

  addr = STM32_USB_COUNT_TX(epno);
  lldbg("     COUNT: [%08x] %04x\n",  addr, getreg16(addr));

  addr = STM32_USB_ADDR_RX(epno);
  lldbg("  RX ADDR:  [%08x] %04x\n",  addr, getreg16(addr));

  addr = STM32_USB_COUNT_RX(epno);
  lldbg("     COUNT: [%08x] %04x\n",  addr, getreg16(addr));
}
patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: stm32_checksetup
 ****************************************************************************/

#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void stm32_checksetup(void)
{
  uint32 cfgr     = getreg32(STM32_RCC_CFGR);
patacongo's avatar
patacongo committed
  uint32 apb1rstr = getreg32(STM32_RCC_APB1RSTR);
  uint32 apb1enr  = getreg32(STM32_RCC_APB1ENR);

  lldbg("CFGR: %08x APB1RSTR: %08x APB1ENR: %08x\n", cfgr, apb1rstr, apb1enr);

  if ((apb1rstr & RCC_APB1RSTR_USBRST) != 0 ||
      (apb1enr & RCC_APB1ENR_USBEN) == 0)
    {
      lldbg("ERROR: USB is NOT setup correctly\n");
    }
}
#endif

patacongo's avatar
patacongo committed
/****************************************************************************
 * Low-Level Helpers
 ****************************************************************************/
/****************************************************************************
patacongo's avatar
patacongo committed
 * Name: stm32_seteptxcount
patacongo's avatar
patacongo committed
 ****************************************************************************/

patacongo's avatar
patacongo committed
static inline void stm32_seteptxcount(ubyte epno, uint16 count) 
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  volatile uint32 *epaddr = (uint32*)STM32_USB_COUNT_TX(epno);
  *epaddr = count;
patacongo's avatar
patacongo committed
} 

/****************************************************************************
 * Name: stm32_seteptxaddr
 ****************************************************************************/

patacongo's avatar
patacongo committed
static inline void stm32_seteptxaddr(ubyte epno, uint16 addr)
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  volatile uint32 *txaddr = (uint32*)STM32_USB_ADDR_TX(epno);
  *txaddr = addr;
patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: stm32_geteptxaddr
 ****************************************************************************/

static inline uint16 stm32_geteptxaddr(ubyte epno)
{
  volatile uint32 *txaddr = (uint32*)STM32_USB_ADDR_TX(epno);
  return (uint16)*txaddr;
}

patacongo's avatar
patacongo committed
/****************************************************************************
patacongo's avatar
patacongo committed
 * Name: stm32_seteprxcount
patacongo's avatar
patacongo committed
 ****************************************************************************/

patacongo's avatar
patacongo committed
static void stm32_seteprxcount(ubyte epno, uint16 count) 
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  volatile uint32 *epaddr = (uint32*)STM32_USB_COUNT_RX(epno);
patacongo's avatar
patacongo committed
  uint16  nblocks;

  /* The upper bits of the RX COUNT value contain the size of allocated
   * RX buffer.  This is based on a block size of 2 or 32:
   *
   * USB_COUNT_RX_BL_SIZE not set:
   *   nblocks is in units of 2 bytes.
   *     00000 - not allowed
   *     00001 - 2 bytes
   *     ....
   *     11111 - 62 bytes
   *
   * USB_COUNT_RX_BL_SIZE set:
   *     00000 - 32 bytes
   *     00001 - 64 bytes
   *     ...
   *     01111 - 512 bytes
   *     1xxxx - Not allowed
   */

patacongo's avatar
patacongo committed
  if (count > 62)
    {
      /* Blocks of 32 (with 0 meaning one block of 32) */
patacongo's avatar
patacongo committed

      nblocks = (count >> 5) - 1 ;
      DEBUGASSERT(nblocks <= 0x0f);
      *epaddr = (uint32)((nblocks << USB_COUNT_RX_NUM_BLOCK_SHIFT) | USB_COUNT_RX_BL_SIZE);
patacongo's avatar
patacongo committed
    }
  else
    {
      /* Blocks of 2 (with 1 meaning one block of 2) */
patacongo's avatar
patacongo committed

      nblocks = (count + 1) >> 1;
      DEBUGASSERT(nblocks > 0 && nblocks < 0x1f);
      *epaddr = (uint32)(nblocks << USB_COUNT_RX_NUM_BLOCK_SHIFT);
patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: stm32_geteprxcount
 ****************************************************************************/

static inline uint16 stm32_geteprxcount(ubyte epno)
{
  volatile uint32 *epaddr = (uint32*)STM32_USB_COUNT_RX(epno);
  return (*epaddr) & USB_COUNT_RX_MASK;
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: stm32_seteprxaddr
 ****************************************************************************/

patacongo's avatar
patacongo committed
static inline void stm32_seteprxaddr(ubyte epno, uint16 addr)
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  volatile uint32 *rxaddr = (uint32*)STM32_USB_ADDR_RX(epno);
  *rxaddr = addr;
patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: stm32_seteprxaddr
 ****************************************************************************/

static inline uint16 stm32_geteprxaddr(ubyte epno)
{
  volatile uint32 *rxaddr = (uint32*)STM32_USB_ADDR_RX(epno);
  return (uint16)*rxaddr;
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: stm32_setepaddress
 ****************************************************************************/

patacongo's avatar
patacongo committed
static inline void stm32_setepaddress(ubyte epno, uint16 addr) 
patacongo's avatar
patacongo committed
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  regval  = stm32_getreg(epaddr);
  regval &= EPR_NOTOG_MASK;
  regval &= ~USB_EPR_EA_MASK;
patacongo's avatar
patacongo committed
  regval |= (addr << USB_EPR_EA_SHIFT);
patacongo's avatar
patacongo committed
  stm32_putreg(regval, epaddr);
} 

/****************************************************************************
 * Name: stm32_seteptype
 ****************************************************************************/

static inline void stm32_seteptype(ubyte epno, uint16 type)
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  regval  = stm32_getreg(epaddr);
  regval &= EPR_NOTOG_MASK;
  regval &= ~USB_EPR_EPTYPE_MASK;
  regval |= type;
  stm32_putreg(regval, epaddr);
}

/****************************************************************************
 * Name: stm32_setstatusout
 ****************************************************************************/

static inline void stm32_setstatusout(ubyte epno)
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  /* For a BULK endpoint the EP_KIND bit is used to enabled double buffering;
patacongo's avatar
patacongo committed
   * for a CONTROL endpoint, it is set to indicate that a status OUT
patacongo's avatar
patacongo committed
   * transaction is expected.  The bit is not used with out endpoint types.
   */

  regval  = stm32_getreg(epaddr);
  regval &= EPR_NOTOG_MASK;
  regval |= USB_EPR_EP_KIND;
  stm32_putreg(regval, epaddr);
}

/****************************************************************************
 * Name: stm32_clrstatusout
 ****************************************************************************/

static inline void stm32_clrstatusout(ubyte epno)
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  /* For a BULK endpoint the EP_KIND bit is used to enabled double buffering;
patacongo's avatar
patacongo committed
   * for a CONTROL endpoint, it is set to indicate that a status OUT
patacongo's avatar
patacongo committed
   * transaction is expected.  The bit is not used with out endpoint types.
   */

  regval  = stm32_getreg(epaddr);
  regval &= EPR_NOTOG_MASK;
  regval &= ~USB_EPR_EP_KIND;
  stm32_putreg(regval, epaddr);
}

/****************************************************************************
 * Name: stm32_clrrxdtog
 ****************************************************************************/

static void stm32_clrrxdtog(ubyte epno) 
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  regval = stm32_getreg(epaddr);
  if ((regval & USB_EPR_DTOG_RX) != 0)
    {
      regval &= EPR_NOTOG_MASK;
      regval |= USB_EPR_DTOG_RX;
      stm32_putreg(regval, epaddr);
    } 
} 

/****************************************************************************
 * Name: stm32_clrtxdtog
 ****************************************************************************/

static void stm32_clrtxdtog(ubyte epno) 
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  regval = stm32_getreg(epaddr);
  if ((regval & USB_EPR_DTOG_TX) != 0)
    {
      regval &= EPR_NOTOG_MASK;
      regval |= USB_EPR_DTOG_TX;
      stm32_putreg(regval, epaddr);
    }
} 

/****************************************************************************
 * Name: stm32_clrepctrrx
 ****************************************************************************/

static void stm32_clrepctrrx(ubyte epno) 
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  regval  = stm32_getreg(epaddr);
  regval &= EPR_NOTOG_MASK;
  regval &= ~USB_EPR_CTR_RX;
  stm32_putreg(regval, epaddr);
} 

/****************************************************************************
 * Name: stm32_clrepctrtx
 ****************************************************************************/

static void stm32_clrepctrtx(ubyte epno) 
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  regval  = stm32_getreg(epaddr);
  regval &= EPR_NOTOG_MASK;
  regval &= ~USB_EPR_CTR_TX;
  stm32_putreg(regval, epaddr);
} 

/****************************************************************************
 * Name: stm32_geteptxstatus
 ****************************************************************************/

static inline uint16 stm32_geteptxstatus(ubyte epno) 
{
  return (uint16)(stm32_getreg(STM32_USB_EPR(epno)) & USB_EPR_STATTX_MASK);
}

/****************************************************************************
 * Name: stm32_geteprxstatus
 ****************************************************************************/

static inline uint16 stm32_geteprxstatus(ubyte epno) 
{
  return (stm32_getreg(STM32_USB_EPR(epno)) & USB_EPR_STATRX_MASK);
}

/****************************************************************************
 * Name: stm32_seteptxstatus
 ****************************************************************************/

static void stm32_seteptxstatus(ubyte epno, uint16 state) 
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  regval = stm32_getreg(epaddr) & EPR_TXDTOG_MASK;

  /* Toggle first bit */

  if ((USB_EPR_STATTX_DTOG1 & state) != 0)
    {
      regval ^= USB_EPR_STATTX_DTOG1;
    }

  /* Toggle second bit */

  if ((USB_EPR_STATTX_DTOG2 & state) != 0)
    {
      regval ^= USB_EPR_STATTX_DTOG2;
    }

  stm32_putreg(regval, epaddr);
} 

/****************************************************************************
 * Name: stm32_seteprxstatus
 ****************************************************************************/

static void stm32_seteprxstatus(ubyte epno, uint16 state) 
{
  uint32 epaddr = STM32_USB_EPR(epno);
  uint16 regval;

  regval = stm32_getreg(epaddr) & EPR_RXDTOG_MASK;

  /* Toggle first bit */

patacongo's avatar
patacongo committed
  if ((USB_EPR_STATRX_DTOG1 & state) != 0)
patacongo's avatar
patacongo committed
    {
      regval ^= USB_EPR_STATRX_DTOG1;
    }

  /* Toggle second bit */

patacongo's avatar
patacongo committed
  if ((USB_EPR_STATRX_DTOG2 & state) != 0)
patacongo's avatar
patacongo committed
    {
      regval ^= USB_EPR_STATRX_DTOG2;
    }

  stm32_putreg(regval, epaddr);
} 

/****************************************************************************
 * Name: stm32_eptxstalled
 ****************************************************************************/

static inline uint16 stm32_eptxstalled(ubyte epno)