/**************************************************************************** * arch/arm/src/stm32/stm32_usbdev.c * * Copyright (C) 2009-2012 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: * - RM0008 Reference manual, STMicro document ID 13902 * - STM32F10xxx USB development kit, UM0424, STMicro * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "up_arch.h" #include "stm32.h" #include "stm32_syscfg.h" #include "stm32_gpio.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_USB_PRI # define CONFIG_USB_PRI NVIC_SYSH_PRIORITY_DEFAULT #endif /* USB Interrupts. Should be re-mapped if CAN is used. */ #ifdef CONFIG_STM32_STM32F30XX # ifdef CONFIG_STM32_USB_ITRMP # define STM32_IRQ_USBHP STM32_IRQ_USBHP_2 # define STM32_IRQ_USBLP STM32_IRQ_USBLP_2 # define STM32_IRQ_USBWKUP STM32_IRQ_USBWKUP_2 # else # define STM32_IRQ_USBHP STM32_IRQ_USBHP_1 # define STM32_IRQ_USBLP STM32_IRQ_USBLP_1 # define STM32_IRQ_USBWKUP STM32_IRQ_USBWKUP_1 # endif #endif /* Extremely detailed register debug that you would normally never want * enabled. */ #ifndef CONFIG_DEBUG # undef CONFIG_STM32_USBDEV_REGDEBUG #endif /* Initial interrupt mask: Reset + Suspend + Correct Transfer */ #define STM32_CNTR_SETUP (USB_CNTR_RESETM|USB_CNTR_SUSPM|USB_CNTR_CTRM) /* 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 * 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. */ #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 /* 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 * CAN/USB memory. We will use the first STM32_NENDPOINTS*4 words for the buffer * table. That is exactly 64 bytes, leaving 7*64 bytes for endpoint buffers. */ #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) /* 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) #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)<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 #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 /* 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 /* 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 /**************************************************************************** * Private Type Definitions ****************************************************************************/ /* The various states of a control pipe */ enum stm32_devstate_e { DEVSTATE_IDLE = 0, /* No request in progress */ 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_t w; uint8_t 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; uint8_t bufno; /* Allocated buffer number */ uint8_t stalled:1; /* true: Endpoint is stalled */ uint8_t halted:1; /* true: Endpoint feature halted */ uint8_t txbusy:1; /* true: TX endpoint FIFO full */ uint8_t 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 */ uint8_t devstate; /* Driver state (see enum stm32_devstate_e) */ uint8_t rsmstate; /* Resume state (see enum stm32_rsmstate_e) */ uint8_t nesofs; /* ESOF counter (for resume support) */ uint8_t rxpending:1; /* 1: OUT data in PMA, but no read requests */ uint8_t selfpowered:1; /* 1: Device is self powered */ uint8_t epavail; /* Bitset of available endpoints */ uint8_t bufavail; /* Bitset of available buffers */ uint16_t rxstatus; /* Saved during interrupt processing */ uint16_t txstatus; /* " " " " " " " " */ uint16_t 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_t stm32_getreg(uint32_t addr); static void stm32_putreg(uint16_t val, uint32_t addr); static void stm32_checksetup(void); static void stm32_dumpep(int epno); #else # define stm32_getreg(addr) getreg16(addr) # define stm32_putreg(val,addr) putreg16(val,addr) # define stm32_checksetup() # define stm32_dumpep(epno) #endif /* Low-Level Helpers ********************************************************/ static inline void stm32_seteptxcount(uint8_t epno, uint16_t count); static inline void stm32_seteptxaddr(uint8_t epno, uint16_t addr); static inline uint16_t stm32_geteptxaddr(uint8_t epno); static void stm32_seteprxcount(uint8_t epno, uint16_t count); static inline uint16_t stm32_geteprxcount(uint8_t epno); static inline void stm32_seteprxaddr(uint8_t epno, uint16_t addr); static inline uint16_t stm32_geteprxaddr(uint8_t epno); static inline void stm32_setepaddress(uint8_t epno, uint16_t addr); static inline void stm32_seteptype(uint8_t epno, uint16_t type); static inline void stm32_seteptxaddr(uint8_t epno, uint16_t addr); static inline void stm32_setstatusout(uint8_t epno); static inline void stm32_clrstatusout(uint8_t epno); static void stm32_clrrxdtog(uint8_t epno); static void stm32_clrtxdtog(uint8_t epno); static void stm32_clrepctrrx(uint8_t epno); static void stm32_clrepctrtx(uint8_t epno); static void stm32_seteptxstatus(uint8_t epno, uint16_t state); static void stm32_seteprxstatus(uint8_t epno, uint16_t state); static inline uint16_t stm32_geteptxstatus(uint8_t epno); static inline uint16_t stm32_geteprxstatus(uint8_t epno); static bool stm32_eptxstalled(uint8_t epno); static bool stm32_eprxstalled(uint8_t epno); static void stm32_setimask(struct stm32_usbdev_s *priv, uint16_t setbits, uint16_t clrbits); /* Suspend/Resume Helpers ***************************************************/ 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 uint8_t *buffer, uint16_t pma, uint16_t nbytes); static inline void stm32_copyfrompma(uint8_t *buffer, uint16_t pma, uint16_t nbytes); 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, int16_t result); static void stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result); static void stm32_epwrite(struct stm32_usbdev_s *buf, struct stm32_ep_s *privep, const uint8_t *data, uint32_t 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); static void stm32_cancelrequests(struct stm32_ep_s *privep); /* Interrupt level processing ***********************************************/ static void stm32_dispatchrequest(struct stm32_usbdev_s *priv); static void stm32_epdone(struct stm32_usbdev_s *priv, uint8_t epno); static void stm32_setdevaddr(struct stm32_usbdev_s *priv, uint8_t value); static void stm32_ep0setup(struct stm32_usbdev_s *priv); static void stm32_ep0out(struct stm32_usbdev_s *priv); static void stm32_ep0in(struct stm32_usbdev_s *priv); static inline void stm32_ep0done(struct stm32_usbdev_s *priv, uint16_t istr); 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 * stm32_epreserve(struct stm32_usbdev_s *priv, uint8_t epset); static inline void stm32_epunreserve(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep); static inline bool stm32_epreserved(struct stm32_usbdev_s *priv, int epno); static int stm32_epallocpma(struct stm32_usbdev_s *priv); static inline void stm32_epfreepma(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep); /* Endpoint operations ******************************************************/ static int stm32_epconfigure(struct usbdev_ep_s *ep, const struct usb_epdesc_s *desc, bool 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, bool resume); /* USB device controller operations *****************************************/ static struct usbdev_ep_s * stm32_allocep(struct usbdev_s *dev, uint8_t epno, bool in, uint8_t 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, bool selfpowered); /* Initialization/Reset *****************************************************/ static void stm32_reset(struct stm32_usbdev_s *priv); static void stm32_hwreset(struct stm32_usbdev_s *priv); static void stm32_hwsetup(struct stm32_usbdev_s *priv); static void stm32_hwshutdown(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_t stm32_getreg(uint32_t addr) { static uint32_t prevaddr = 0; static uint16_t preval = 0; static uint32_t count = 0; /* Read the value from the register */ uint16_t 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. */ if (addr == prevaddr && val == preval) { 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); return val; } #endif /**************************************************************************** * Name: stm32_putreg ****************************************************************************/ #if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) static void stm32_putreg(uint16_t val, uint32_t addr) { /* Show the register value being written */ lldbg("%08x<-%04x\n", addr, val); /* Write the value */ putreg16(val, addr); } #endif /**************************************************************************** * Name: stm32_dumpep ****************************************************************************/ #if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) static void stm32_dumpep(int epno) { uint32_t 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); 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)); } #endif /**************************************************************************** * Name: stm32_checksetup ****************************************************************************/ #if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) static void stm32_checksetup(void) { uint32_t cfgr = getreg32(STM32_RCC_CFGR); uint32_t apb1rstr = getreg32(STM32_RCC_APB1RSTR); uint32_t 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 /**************************************************************************** * Low-Level Helpers ****************************************************************************/ /**************************************************************************** * Name: stm32_seteptxcount ****************************************************************************/ static inline void stm32_seteptxcount(uint8_t epno, uint16_t count) { volatile uint32_t *epaddr = (uint32_t*)STM32_USB_COUNT_TX(epno); *epaddr = count; } /**************************************************************************** * Name: stm32_seteptxaddr ****************************************************************************/ static inline void stm32_seteptxaddr(uint8_t epno, uint16_t addr) { volatile uint32_t *txaddr = (uint32_t*)STM32_USB_ADDR_TX(epno); *txaddr = addr; } /**************************************************************************** * Name: stm32_geteptxaddr ****************************************************************************/ static inline uint16_t stm32_geteptxaddr(uint8_t epno) { volatile uint32_t *txaddr = (uint32_t*)STM32_USB_ADDR_TX(epno); return (uint16_t)*txaddr; } /**************************************************************************** * Name: stm32_seteprxcount ****************************************************************************/ static void stm32_seteprxcount(uint8_t epno, uint16_t count) { volatile uint32_t *epaddr = (uint32_t*)STM32_USB_COUNT_RX(epno); uint32_t rxcount = 0; uint16_t 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 */ if (count > 62) { /* Blocks of 32 (with 0 meaning one block of 32) */ nblocks = (count >> 5) - 1 ; DEBUGASSERT(nblocks <= 0x0f); rxcount = (uint32_t)((nblocks << USB_COUNT_RX_NUM_BLOCK_SHIFT) | USB_COUNT_RX_BL_SIZE); } else if (count > 0) { /* Blocks of 2 (with 1 meaning one block of 2) */ nblocks = (count + 1) >> 1; DEBUGASSERT(nblocks > 0 && nblocks < 0x1f); rxcount = (uint32_t)(nblocks << USB_COUNT_RX_NUM_BLOCK_SHIFT); } *epaddr = rxcount; } /**************************************************************************** * Name: stm32_geteprxcount ****************************************************************************/ static inline uint16_t stm32_geteprxcount(uint8_t epno) { volatile uint32_t *epaddr = (uint32_t*)STM32_USB_COUNT_RX(epno); return (*epaddr) & USB_COUNT_RX_MASK; } /**************************************************************************** * Name: stm32_seteprxaddr ****************************************************************************/ static inline void stm32_seteprxaddr(uint8_t epno, uint16_t addr) { volatile uint32_t *rxaddr = (uint32_t*)STM32_USB_ADDR_RX(epno); *rxaddr = addr; } /**************************************************************************** * Name: stm32_seteprxaddr ****************************************************************************/ static inline uint16_t stm32_geteprxaddr(uint8_t epno) { volatile uint32_t *rxaddr = (uint32_t*)STM32_USB_ADDR_RX(epno); return (uint16_t)*rxaddr; } /**************************************************************************** * Name: stm32_setepaddress ****************************************************************************/ static inline void stm32_setepaddress(uint8_t epno, uint16_t addr) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t regval; regval = stm32_getreg(epaddr); regval &= EPR_NOTOG_MASK; regval &= ~USB_EPR_EA_MASK; regval |= (addr << USB_EPR_EA_SHIFT); stm32_putreg(regval, epaddr); } /**************************************************************************** * Name: stm32_seteptype ****************************************************************************/ static inline void stm32_seteptype(uint8_t epno, uint16_t type) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t 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(uint8_t epno) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t regval; /* For a BULK endpoint the EP_KIND bit is used to enabled double buffering; * for a CONTROL endpoint, it is set to indicate that a status OUT * 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(uint8_t epno) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t regval; /* For a BULK endpoint the EP_KIND bit is used to enabled double buffering; * for a CONTROL endpoint, it is set to indicate that a status OUT * 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(uint8_t epno) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t 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(uint8_t epno) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t 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(uint8_t epno) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t 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(uint8_t epno) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t regval; regval = stm32_getreg(epaddr); regval &= EPR_NOTOG_MASK; regval &= ~USB_EPR_CTR_TX; stm32_putreg(regval, epaddr); } /**************************************************************************** * Name: stm32_geteptxstatus ****************************************************************************/ static inline uint16_t stm32_geteptxstatus(uint8_t epno) { return (uint16_t)(stm32_getreg(STM32_USB_EPR(epno)) & USB_EPR_STATTX_MASK); } /**************************************************************************** * Name: stm32_geteprxstatus ****************************************************************************/ static inline uint16_t stm32_geteprxstatus(uint8_t epno) { return (stm32_getreg(STM32_USB_EPR(epno)) & USB_EPR_STATRX_MASK); } /**************************************************************************** * Name: stm32_seteptxstatus ****************************************************************************/ static void stm32_seteptxstatus(uint8_t epno, uint16_t state) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t regval; /* The bits in the STAT_TX field can be toggled by software to set their * value. When set to 0, the value remains unchanged; when set to one, * value toggles. */ regval = stm32_getreg(epaddr); /* The exclusive OR will set STAT_TX bits to 1 if there value is different * from the bits requested in 'state' */ regval ^= state; regval &= EPR_TXDTOG_MASK; stm32_putreg(regval, epaddr); } /**************************************************************************** * Name: stm32_seteprxstatus ****************************************************************************/ static void stm32_seteprxstatus(uint8_t epno, uint16_t state) { uint32_t epaddr = STM32_USB_EPR(epno); uint16_t regval; /* The bits in the STAT_RX field can be toggled by software to set their * value. When set to 0, the value remains unchanged; when set to one, * value toggles. */ regval = stm32_getreg(epaddr); /* The exclusive OR will set STAT_RX bits to 1 if there value is different * from the bits requested in 'state' */ regval ^= state; regval &= EPR_RXDTOG_MASK; stm32_putreg(regval, epaddr); } /**************************************************************************** * Name: stm32_eptxstalled ****************************************************************************/ static inline bool stm32_eptxstalled(uint8_t epno) { return (stm32_geteptxstatus(epno) == USB_EPR_STATTX_STALL); } /**************************************************************************** * Name: stm32_eprxstalled ****************************************************************************/ static inline bool stm32_eprxstalled(uint8_t epno) { return (stm32_geteprxstatus(epno) == USB_EPR_STATRX_STALL); } /**************************************************************************** * Request Helpers ****************************************************************************/ /**************************************************************************** * Name: stm32_copytopma ****************************************************************************/ static void stm32_copytopma(const uint8_t *buffer, uint16_t pma, uint16_t nbytes) { uint16_t *dest; uint16_t ms; uint16_t ls; int nwords = (nbytes + 1) >> 1; int i; /* Copy loop. Source=user buffer, Dest=packet memory */ dest = (uint16_t*)(STM32_USBRAM_BASE + ((uint32_t)pma << 1)); for (i = nwords; i != 0; i--) { /* Read two bytes and pack into on 16-bit word */ ls = (uint16_t)(*buffer++); ms = (uint16_t)(*buffer++); *dest = ms << 8 | ls; /* Source address increments by 2*sizeof(uint8_t) = 2; Dest address * increments by 2*sizeof(uint16_t) = 4. */ dest += 2; } } /**************************************************************************** * Name: stm32_copyfrompma ****************************************************************************/ static inline void stm32_copyfrompma(uint8_t *buffer, uint16_t pma, uint16_t nbytes) { uint32_t *src; int nwords = (nbytes + 1) >> 1; int i; /* Copy loop. Source=packet memory, Dest=user buffer */ src = (uint32_t*)(STM32_USBRAM_BASE + ((uint32_t)pma << 1)); for (i = nwords; i != 0; i--) { /* Copy 16-bits from packet memory to user buffer. */ *(uint16_t*)buffer = *src++; /* Source address increments by 1*sizeof(uint32_t) = 4; Dest address * increments by 2*sizeof(uint8_t) = 2. */ buffer += 2; } } /**************************************************************************** * Name: stm32_rqdequeue ****************************************************************************/ static struct stm32_req_s *stm32_rqdequeue(struct stm32_ep_s *privep) { struct stm32_req_s *ret = privep->head; if (ret) { privep->head = ret->flink; if (!privep->head) { privep->tail = NULL; } ret->flink = NULL; } return ret; } /**************************************************************************** * Name: stm32_rqenqueue ****************************************************************************/ static void stm32_rqenqueue(struct stm32_ep_s *privep, struct stm32_req_s *req) { req->flink = NULL; if (!privep->head) { privep->head = req; privep->tail = req; } else { privep->tail->flink = req; privep->tail = req; } } /**************************************************************************** * Name: stm32_abortrequest ****************************************************************************/ static inline void stm32_abortrequest(struct stm32_ep_s *privep, struct stm32_req_s *privreq, int16_t result) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_REQABORTED), (uint16_t)USB_EPNO(privep->ep.eplog)); /* Save the result in the request structure */ privreq->req.result = result; /* Callback to the request completion handler */ privreq->req.callback(&privep->ep, &privreq->req); } /**************************************************************************** * Name: stm32_reqcomplete ****************************************************************************/ static void stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result) { struct stm32_req_s *privreq; irqstate_t flags; /* Remove the completed request at the head of the endpoint request list */ flags = irqsave(); privreq = stm32_rqdequeue(privep); irqrestore(flags); if (privreq) { /* If endpoint 0, temporarily reflect the state of protocol stalled * in the callback. */ bool stalled = privep->stalled; if (USB_EPNO(privep->ep.eplog) == EP0) { privep->stalled = (privep->dev->devstate == DEVSTATE_STALLED); } /* Save the result in the request structure */ privreq->req.result = result; /* Callback to the request completion handler */ privreq->flink = NULL; privreq->req.callback(&privep->ep, &privreq->req); /* Restore the stalled indication */ privep->stalled = stalled; } } /**************************************************************************** * Name: tm32_epwrite ****************************************************************************/ static void stm32_epwrite(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep, const uint8_t *buf, uint32_t nbytes) { uint8_t epno = USB_EPNO(privep->ep.eplog); usbtrace(TRACE_WRITE(epno), nbytes); /* Check for a zero-length packet */ if (nbytes > 0) { /* Copy the data from the user buffer into packet memory for this * endpoint */ stm32_copytopma(buf, stm32_geteptxaddr(epno), nbytes); } /* Send the packet (might be a null packet nbytes == 0) */ stm32_seteptxcount(epno, nbytes); priv->txstatus = USB_EPR_STATTX_VALID; /* Indicate that there is data in the TX packet memory. This will be cleared * when the next data out interrupt is received. */ privep->txbusy = true; } /**************************************************************************** * Name: stm32_wrrequest ****************************************************************************/ static int stm32_wrrequest(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep) { struct stm32_req_s *privreq; uint8_t *buf; uint8_t epno; int nbytes; int bytesleft; /* We get here when an IN endpoint interrupt occurs. So now we know that * there is no TX transfer in progress. */ privep->txbusy = false; /* Check the request from the head of the endpoint request queue */ privreq = stm32_rqpeek(privep); if (!privreq) { /* There is no TX transfer in progress and no new pending TX * requests to send. */ usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINQEMPTY), 0); priv->devstate = DEVSTATE_IDLE; return OK; } epno = USB_EPNO(privep->ep.eplog); ullvdbg("epno=%d req=%p: len=%d xfrd=%d nullpkt=%d\n", epno, privreq, privreq->req.len, privreq->req.xfrd, privep->txnullpkt); /* Get the number of bytes left to be sent in the packet */ bytesleft = privreq->req.len - privreq->req.xfrd; nbytes = bytesleft; #warning "REVISIT: If the EP supports double buffering, then we can do better" /* Send the next packet */ if (nbytes > 0) { /* Either send the maxpacketsize or all of the remaining data in * the request. */ privep->txnullpkt = 0; if (nbytes >= privep->ep.maxpacket) { nbytes = privep->ep.maxpacket; /* Handle the case where this packet is exactly the * maxpacketsize. Do we need to send a zero-length packet * in this case? */ if (bytesleft == privep->ep.maxpacket && (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0) { privep->txnullpkt = 1; } } } /* Send the packet (might be a null packet nbytes == 0) */ buf = privreq->req.buf + privreq->req.xfrd; stm32_epwrite(priv, privep, buf, nbytes); priv->devstate = DEVSTATE_WRREQUEST; /* Update for the next data IN interrupt */ privreq->req.xfrd += nbytes; bytesleft = privreq->req.len - privreq->req.xfrd; /* If all of the bytes were sent (including any final null packet) * then we are finished with the transfer */ if (bytesleft == 0 && !privep->txnullpkt) { usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd); privep->txnullpkt = 0; stm32_reqcomplete(privep, OK); priv->devstate = DEVSTATE_IDLE; } return OK; } /**************************************************************************** * Name: stm32_rdrequest ****************************************************************************/ static int stm32_rdrequest(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep) { struct stm32_req_s *privreq; uint32_t src; uint8_t *dest; uint8_t epno; int pmalen; int readlen; /* Check the request from the head of the endpoint request queue */ epno = USB_EPNO(privep->ep.eplog); privreq = stm32_rqpeek(privep); if (!privreq) { /* Incoming data available in PMA, but no packet to receive the data. * Mark that the RX data is pending and hope that a packet is returned * soon. */ usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTQEMPTY), epno); return OK; } ullvdbg("EP%d: len=%d xfrd=%d\n", epno, privreq->req.len, privreq->req.xfrd); /* Ignore any attempt to receive a zero length packet */ if (privreq->req.len == 0) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTNULLPACKET), 0); stm32_reqcomplete(privep, OK); return OK; } usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd); /* Get the source and destination transfer addresses */ dest = privreq->req.buf + privreq->req.xfrd; src = stm32_geteprxaddr(epno); /* Get the number of bytes to read from packet memory */ pmalen = stm32_geteprxcount(epno); readlen = MIN(privreq->req.len, pmalen); /* Receive the next packet */ stm32_copyfrompma(dest, src, readlen); priv->devstate = DEVSTATE_RDREQUEST; /* If the receive buffer is full or this is a partial packet, * then we are finished with the transfer */ privreq->req.xfrd += readlen; if (pmalen < privep->ep.maxpacket || privreq->req.xfrd >= privreq->req.len) { /* Complete the transfer and mark the state IDLE. The endpoint * RX will be marked valid when the data phase completes. */ usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); stm32_reqcomplete(privep, OK); priv->devstate = DEVSTATE_IDLE; } return OK; } /**************************************************************************** * Name: stm32_cancelrequests ****************************************************************************/ static void stm32_cancelrequests(struct stm32_ep_s *privep) { while (!stm32_rqempty(privep)) { usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), (stm32_rqpeek(privep))->req.xfrd); stm32_reqcomplete(privep, -ESHUTDOWN); } } /**************************************************************************** * Interrupt Level Processing ****************************************************************************/ /**************************************************************************** * Name: stm32_dispatchrequest ****************************************************************************/ static void stm32_dispatchrequest(struct stm32_usbdev_s *priv) { int ret; usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DISPATCH), 0); if (priv && priv->driver) { /* Forward to the control request to the class driver implementation */ ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl, NULL, 0); if (ret < 0) { /* Stall on failure */ usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DISPATCHSTALL), 0); priv->devstate = DEVSTATE_STALLED; } } } /**************************************************************************** * Name: stm32_epdone ****************************************************************************/ static void stm32_epdone(struct stm32_usbdev_s *priv, uint8_t epno) { struct stm32_ep_s *privep; uint16_t epr; /* Decode and service non control endpoints interrupt */ epr = stm32_getreg(STM32_USB_EPR(epno)); privep = &priv->eplist[epno]; /* OUT: host-to-device * CTR_RX is set by the hardware when an OUT/SETUP transaction * successfully completed on this endpoint. */ if ((epr & USB_EPR_CTR_RX) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTDONE), epr); /* Handle read requests. First check if a read request is available to * accept the host data. */ if (!stm32_rqempty(privep)) { /* Read host data into the current read request */ stm32_rdrequest(priv, privep); /* "After the received data is processed, the application software * should set the STAT_RX bits to '11' (Valid) in the USB_EPnR, * enabling further transactions. " */ priv->rxstatus = USB_EPR_STATRX_VALID; } /* NAK further OUT packets if there there no more read requests */ if (stm32_rqempty(privep)) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTPENDING), (uint16_t)epno); /* Mark the RX processing as pending and NAK any OUT actions * on this endpoint. "While the STAT_RX bits are equal to '10' * (NAK), any OUT request addressed to that endpoint is NAKed, * indicating a flow control condition: the USB host will retry * the transaction until it succeeds." */ priv->rxstatus = USB_EPR_STATRX_NAK; priv->rxpending = true; } /* Clear the interrupt status and set the new RX status */ stm32_clrepctrrx(epno); stm32_seteprxstatus(epno, priv->rxstatus); } /* IN: device-to-host * CTR_TX is set when an IN transaction successfully completes on * an endpoint */ else if ((epr & USB_EPR_CTR_TX) != 0) { /* Clear interrupt status */ stm32_clrepctrtx(epno); usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINDONE), epr); /* Handle write requests */ priv->txstatus = USB_EPR_STATTX_NAK; stm32_wrrequest(priv, privep); /* Set the new TX status */ stm32_seteptxstatus(epno, priv->txstatus); } } /**************************************************************************** * Name: stm32_setdevaddr ****************************************************************************/ static void stm32_setdevaddr(struct stm32_usbdev_s *priv, uint8_t value) { int epno; /* Set address in every allocated endpoint */ for (epno = 0; epno < STM32_NENDPOINTS; epno++) { if (stm32_epreserved(priv, epno)) { stm32_setepaddress((uint8_t)epno, (uint8_t)epno); } } /* Set the device address and enable function */ stm32_putreg(value|USB_DADDR_EF, STM32_USB_DADDR); } /**************************************************************************** * Name: stm32_ep0setup ****************************************************************************/ static void stm32_ep0setup(struct stm32_usbdev_s *priv) { struct stm32_ep_s *ep0 = &priv->eplist[EP0]; struct stm32_req_s *privreq = stm32_rqpeek(ep0); struct stm32_ep_s *privep; union wb_u value; union wb_u index; union wb_u len; union wb_u response; bool handled = false; uint8_t epno; int nbytes = 0; /* Assume zero-length packet */ int ret; /* Terminate any pending requests (doesn't work if the pending request * was a zero-length transfer!) */ while (!stm32_rqempty(ep0)) { int16_t result = OK; if (privreq->req.xfrd != privreq->req.len) { result = -EPROTO; } usbtrace(TRACE_COMPLETE(ep0->ep.eplog), privreq->req.xfrd); stm32_reqcomplete(ep0, result); } /* Assume NOT stalled; no TX in progress */ ep0->stalled = 0; ep0->txbusy = 0; /* Get a 32-bit PMA address and use that to get the 8-byte setup request */ stm32_copyfrompma((uint8_t*)&priv->ctrl, stm32_geteprxaddr(EP0), USB_SIZEOF_CTRLREQ); /* And 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); ullvdbg("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n", priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w); priv->devstate = DEVSTATE_IDLE; /* Dispatch any non-standard requests */ if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_NOSTDREQ), priv->ctrl.type); /* Let the class implementation handle all non-standar requests */ stm32_dispatchrequest(priv); return; } /* 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(STM32_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(STM32_TRACEERR_BADEPGETSTATUS), 0); priv->devstate = DEVSTATE_STALLED; } else { switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK) { case USB_REQ_RECIPIENT_ENDPOINT: { epno = USB_EPNO(index.b[LSB]); usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPGETSTATUS), epno); if (epno >= STM32_NENDPOINTS) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS), epno); priv->devstate = DEVSTATE_STALLED; } else { privep = &priv->eplist[epno]; response.w = 0; /* Not stalled */ nbytes = 2; /* Response size: 2 bytes */ if (USB_ISEPIN(index.b[LSB])) { /* IN endpoint */ if (stm32_eptxstalled(epno)) { /* IN Endpoint stalled */ response.b[LSB] = 1; /* Stalled */ } } else { /* OUT endpoint */ if (stm32_eprxstalled(epno)) { /* OUT Endpoint stalled */ response.b[LSB] = 1; /* Stalled */ } } } } break; case USB_REQ_RECIPIENT_DEVICE: { if (index.w == 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVGETSTATUS), 0); /* Features: Remote Wakeup=YES; selfpowered=? */ response.w = 0; response.b[LSB] = (priv->selfpowered << USB_FEATURE_SELFPOWERED) | (1 << USB_FEATURE_REMOTEWAKEUP); nbytes = 2; /* Response size: 2 bytes */ } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADDEVGETSTATUS), 0); priv->devstate = DEVSTATE_STALLED; } } break; case USB_REQ_RECIPIENT_INTERFACE: { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IFGETSTATUS), 0); response.w = 0; nbytes = 2; /* Response size: 2 bytes */ } break; default: { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSTATUS), 0); priv->devstate = DEVSTATE_STALLED; } 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(STM32_TRACEINTID_CLEARFEATURE), priv->ctrl.type); if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) { /* Let the class implementation handle all recipients (except for the * endpoint recipient) */ stm32_dispatchrequest(priv); handled = true; } else { /* Endpoint recipient */ epno = USB_EPNO(index.b[LSB]); if (epno < STM32_NENDPOINTS && index.b[MSB] == 0 && value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) { privep = &priv->eplist[epno]; privep->halted = 0; ret = stm32_epstall(&privep->ep, true); } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADCLEARFEATURE), 0); priv->devstate = DEVSTATE_STALLED; } } } 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(STM32_TRACEINTID_SETFEATURE), priv->ctrl.type); if (((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) && value.w == USB_FEATURE_TESTMODE) { /* Special case recipient=device test mode */ ullvdbg("test mode: %d\n", index.w); } else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) { /* The class driver handles all recipients except recipient=endpoint */ stm32_dispatchrequest(priv); handled = true; } else { /* Handler recipient=endpoint */ epno = USB_EPNO(index.b[LSB]); if (epno < STM32_NENDPOINTS && index.b[MSB] == 0 && value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) { privep = &priv->eplist[epno]; privep->halted = 1; ret = stm32_epstall(&privep->ep, false); } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0); priv->devstate = DEVSTATE_STALLED; } } } break; case USB_REQ_SETADDRESS: { /* type: host-to-device; recipient = device * value: device address * index: 0 * len: 0; data = none */ usbtrace(TRACE_INTDECODE(STM32_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(STM32_TRACEERR_BADSETADDRESS), 0); priv->devstate = DEVSTATE_STALLED; } /* 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 zero- * length packet transfer completes. */ } 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(STM32_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 */ stm32_dispatchrequest(priv); handled = true; } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSETDESC), 0); priv->devstate = DEVSTATE_STALLED; } } break; case USB_REQ_GETCONFIGURATION: /* type: device-to-host; recipient = device * value: 0; * index: 0; * len: 1; data = configuration value */ { usbtrace(TRACE_INTDECODE(STM32_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 */ stm32_dispatchrequest(priv); handled = true; } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETCONFIG), 0); priv->devstate = DEVSTATE_STALLED; } } break; case USB_REQ_SETCONFIGURATION: /* type: host-to-device; recipient = device * value: configuration value * index: 0; * len: 0; data = none */ { usbtrace(TRACE_INTDECODE(STM32_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 */ stm32_dispatchrequest(priv); handled = true; } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETCONFIG), 0); priv->devstate = DEVSTATE_STALLED; } } 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(STM32_TRACEINTID_GETSETIF), priv->ctrl.type); stm32_dispatchrequest(priv); handled = true; } break; case USB_REQ_SYNCHFRAME: /* type: device-to-host; recipient = endpoint * value: 0 * index: endpoint; * len: 2; data = frame number */ { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SYNCHFRAME), 0); } break; default: { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDCTRLREQ), priv->ctrl.req); priv->devstate = DEVSTATE_STALLED; } break; } /* 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 * 'handled' will be set to true and devstate != DEVSTATE_STALLED; * 3. An error was detected in either the above logic or by the class implementation * logic. In either case, priv->state will be set DEVSTATE_STALLED * 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->devstate != DEVSTATE_STALLED && !handled) { /* We will response. First, restrict the data length to the length * requested in the setup packet */ if (nbytes > len.w) { nbytes = len.w; } /* Send the response (might be a zero-length packet) */ stm32_epwrite(priv, ep0, response.b, nbytes); priv->devstate = DEVSTATE_IDLE; } } /**************************************************************************** * Name: stm32_ep0in ****************************************************************************/ static void stm32_ep0in(struct stm32_usbdev_s *priv) { /* There is no longer anything in the EP0 TX packet memory */ priv->eplist[EP0].txbusy = false; /* Are we processing the completion of one packet of an outgoing request * from the class driver? */ if (priv->devstate == DEVSTATE_WRREQUEST) { stm32_wrrequest(priv, &priv->eplist[EP0]); } /* No.. Are we processing the completion of a status response? */ else if (priv->devstate == DEVSTATE_IDLE) { /* Look at the saved SETUP command. Was it a SET ADDRESS request? * If so, then now is the time to set the address. */ if (priv->ctrl.req == USB_REQ_SETADDRESS && (priv->ctrl.type & REQRECIPIENT_MASK) == (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_DEVICE)) { union wb_u value; value.w = GETUINT16(priv->ctrl.value); stm32_setdevaddr(priv, value.b[LSB]); } } else { priv->devstate = DEVSTATE_STALLED; } } /**************************************************************************** * Name: stm32_ep0out ****************************************************************************/ static void stm32_ep0out(struct stm32_usbdev_s *priv) { struct stm32_ep_s *privep = &priv->eplist[EP0]; switch (priv->devstate) { case DEVSTATE_RDREQUEST: /* Write request in progress */ case DEVSTATE_IDLE: /* No transfer in progress */ stm32_rdrequest(priv, privep); break; default: /* Unexpected state OR host aborted the OUT transfer before it * completed, STALL the endpoint in either case */ priv->devstate = DEVSTATE_STALLED; break; } } /**************************************************************************** * Name: stm32_ep0done ****************************************************************************/ static inline void stm32_ep0done(struct stm32_usbdev_s *priv, uint16_t istr) { uint16_t epr; /* Initialize RX and TX status. We shouldn't have to actually look at the * status because the hardware is supposed to set the both RX and TX status * to NAK when an EP0 SETUP occurs (of course, this might not be a setup) */ priv->rxstatus = USB_EPR_STATRX_NAK; priv->txstatus = USB_EPR_STATTX_NAK; /* Set both RX and TX status to NAK */ stm32_seteprxstatus(EP0, USB_EPR_STATRX_NAK); stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK); /* Check the direction bit to determine if this the completion of an EP0 * packet sent to or received from the host PC. */ if ((istr & USB_ISTR_DIR) == 0) { /* EP0 IN: device-to-host (DIR=0) */ usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0IN), istr); stm32_clrepctrtx(EP0); stm32_ep0in(priv); } else { /* EP0 OUT: host-to-device (DIR=1) */ epr = stm32_getreg(STM32_USB_EPR(EP0)); /* CTR_TX is set when an IN transaction successfully * completes on an endpoint */ if ((epr & USB_EPR_CTR_TX) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0INDONE), epr); stm32_clrepctrtx(EP0); stm32_ep0in(priv); } /* SETUP is set by the hardware when the last completed * transaction was a control endpoint SETUP */ else if ((epr & USB_EPR_SETUP) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPDONE), epr); stm32_clrepctrrx(EP0); stm32_ep0setup(priv); } /* Set by the hardware when an OUT/SETUP transaction successfully * completed on this endpoint. */ else if ((epr & USB_EPR_CTR_RX) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0OUTDONE), epr); stm32_clrepctrrx(EP0); stm32_ep0out(priv); } /* None of the above */ else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0BADCTR), epr); return; /* Does this ever happen? */ } } /* Make sure that the EP0 packet size is still OK (superstitious?) */ stm32_seteprxcount(EP0, STM32_EP0MAXPACKET); /* Now figure out the new RX/TX status. Here are all possible * consequences of the above EP0 operations: * * rxstatus txstatus devstate MEANING * -------- -------- --------- --------------------------------- * NAK NAK IDLE Nothing happened * NAK VALID IDLE EP0 response sent from USBDEV driver * NAK VALID WRREQUEST EP0 response sent from class driver * NAK --- STALL Some protocol error occurred * * First handle the STALL condition: */ if (priv->devstate == DEVSTATE_STALLED) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED), priv->devstate); priv->rxstatus = USB_EPR_STATRX_STALL; priv->txstatus = USB_EPR_STATTX_STALL; } /* Was a transmission started? If so, txstatus will be VALID. The * only special case to handle is when both are set to NAK. In that * case, we need to set RX status to VALID in order to accept the next * SETUP request. */ else if (priv->rxstatus == USB_EPR_STATRX_NAK && priv->txstatus == USB_EPR_STATTX_NAK) { priv->rxstatus = USB_EPR_STATRX_VALID; } /* Now set the new TX and RX status */ stm32_seteprxstatus(EP0, priv->rxstatus); stm32_seteptxstatus(EP0, priv->txstatus); } /**************************************************************************** * Name: stm32_lptransfer ****************************************************************************/ static void stm32_lptransfer(struct stm32_usbdev_s *priv) { uint8_t epno; uint16_t istr; /* Stay in loop while LP interrupts are pending */ while (((istr = stm32_getreg(STM32_USB_ISTR)) & USB_ISTR_CTR) != 0) { stm32_putreg((uint16_t)~USB_ISTR_CTR, STM32_USB_ISTR); /* Extract highest priority endpoint number */ epno = (uint8_t)(istr & USB_ISTR_EPID_MASK); /* Handle EP0 completion events */ if (epno == 0) { stm32_ep0done(priv, istr); } /* Handle other endpoint completion events */ else { stm32_epdone(priv, epno); } } } /**************************************************************************** * Name: stm32_hpinterrupt ****************************************************************************/ static int stm32_hpinterrupt(int irq, void *context) { /* For now there is only one USB controller, but we will always refer to * it using a pointer to make any future ports to multiple USB controllers * easier. */ struct stm32_usbdev_s *priv = &g_usbdev; uint16_t istr; uint8_t epno; /* High priority interrupts are only triggered by a correct transfer event * for isochronous and double-buffer bulk transfers. */ istr = stm32_getreg(STM32_USB_ISTR); usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_HPINTERRUPT), istr); while ((istr & USB_ISTR_CTR) != 0) { stm32_putreg((uint16_t)~USB_ISTR_CTR, STM32_USB_ISTR); /* Extract highest priority endpoint number */ epno = (uint8_t)(istr & USB_ISTR_EPID_MASK); /* And handle the completion event */ stm32_epdone(priv, epno); /* Fetch the status again for the next time through the loop */ istr = stm32_getreg(STM32_USB_ISTR); } usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_HPINTERRUPT), 0); return OK; } /**************************************************************************** * Name: stm32_lpinterrupt ****************************************************************************/ static int stm32_lpinterrupt(int irq, void *context) { /* For now there is only one USB controller, but we will always refer to * it using a pointer to make any future ports to multiple USB controllers * easier. */ struct stm32_usbdev_s *priv = &g_usbdev; uint16_t istr = stm32_getreg(STM32_USB_ISTR); usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_LPINTERRUPT), istr); /* Handle Reset interrupts. When this event occurs, the peripheral is left * in the same conditions it is left by the system reset (but with the * USB controller enabled). */ if ((istr & USB_ISTR_RESET) != 0) { /* Reset interrupt received. Clear the RESET interrupt status. */ stm32_putreg(~USB_ISTR_RESET, STM32_USB_ISTR); usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_RESET), istr); /* Restore our power-up state and exit now because istr is no longer * valid. */ stm32_reset(priv); goto exit_lpinterrupt; } /* Handle Wakeup interrupts. This interrupt is only enable while the USB is * suspended. */ if ((istr & USB_ISTR_WKUP & priv->imask) != 0) { /* Wakeup interrupt received. Clear the WKUP interrupt status. The * cause of the resume is indicated in the FNR register */ stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR); usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_WKUP), stm32_getreg(STM32_USB_FNR)); /* Perform the wakeup action */ stm32_initresume(priv); priv->rsmstate = RSMSTATE_IDLE; /* Disable ESOF polling, disable the wakeup interrupt, and * re-enable the suspend interrupt. Clear any pending SUSP * interrupts. */ stm32_setimask(priv, USB_CNTR_SUSPM, USB_CNTR_ESOFM|USB_CNTR_WKUPM); stm32_putreg(~USB_CNTR_SUSPM, STM32_USB_ISTR); } if ((istr & USB_ISTR_SUSP & priv->imask) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SUSP), 0); stm32_suspend(priv); /* Clear of the ISTR bit must be done after setting of USB_CNTR_FSUSP */ stm32_putreg(~USB_ISTR_SUSP, STM32_USB_ISTR); } if ((istr & USB_ISTR_ESOF & priv->imask) != 0) { stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR); /* Resume handling timing is made with ESOFs */ usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_ESOF), 0); stm32_esofpoll(priv); } if ((istr & USB_ISTR_CTR & priv->imask) != 0) { /* Low priority endpoint correct transfer interrupt */ usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_LPCTR), istr); stm32_lptransfer(priv); } exit_lpinterrupt: usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_LPINTERRUPT), stm32_getreg(STM32_USB_EP0R)); return OK; } /**************************************************************************** * Name: stm32_setimask ****************************************************************************/ static void stm32_setimask(struct stm32_usbdev_s *priv, uint16_t setbits, uint16_t clrbits) { uint16_t regval; /* Adjust the interrupt mask bits in the shadow copy first */ priv->imask &= ~clrbits; priv->imask |= setbits; /* Then make the interrupt mask bits in the CNTR register match the shadow * register (Hmmm... who is shadowing whom?) */ regval = stm32_getreg(STM32_USB_CNTR); regval &= ~USB_CNTR_ALLINTS; regval |= priv->imask; stm32_putreg(regval, STM32_USB_CNTR); } /**************************************************************************** * Suspend/Resume Helpers ****************************************************************************/ /**************************************************************************** * Name: stm32_suspend ****************************************************************************/ static void stm32_suspend(struct stm32_usbdev_s *priv) { uint16_t regval; /* Notify the class driver of the suspend event */ if (priv->driver) { CLASS_SUSPEND(priv->driver, &priv->usbdev); } /* Disable ESOF polling, disable the SUSP interrupt, and enable the WKUP * interrupt. Clear any pending WKUP interrupt. */ stm32_setimask(priv, USB_CNTR_WKUPM, USB_CNTR_ESOFM|USB_CNTR_SUSPM); stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR); /* Set the FSUSP bit in the CNTR register. This activates suspend mode * within the USB peripheral and disables further SUSP interrupts. */ regval = stm32_getreg(STM32_USB_CNTR); regval |= USB_CNTR_FSUSP; stm32_putreg(regval, STM32_USB_CNTR); /* If we are not a self-powered device, the got to low-power mode */ if (!priv->selfpowered) { /* Setting LPMODE in the CNTR register removes static power * consumption in the USB analog transceivers but keeps them * able to detect resume activity */ regval = stm32_getreg(STM32_USB_CNTR); regval |= USB_CNTR_LPMODE; stm32_putreg(regval, STM32_USB_CNTR); } /* Let the board-specific logic know that we have entered the suspend * state */ stm32_usbsuspend((struct usbdev_s *)priv, false); } /**************************************************************************** * Name: stm32_initresume ****************************************************************************/ static void stm32_initresume(struct stm32_usbdev_s *priv) { uint16_t regval; /* This function is called when either (1) a WKUP interrupt is received from * the host PC, or (2) the class device implementation calls the wakeup() * method. */ /* Clear the USB low power mode (lower power mode was not set if this is * a self-powered device. Also, low power mode is automatically cleared by * hardware when a WKUP interrupt event occurs). */ regval = stm32_getreg(STM32_USB_CNTR); regval &= (~USB_CNTR_LPMODE); stm32_putreg(regval, STM32_USB_CNTR); /* Restore full power -- whatever that means for this particular board */ stm32_usbsuspend((struct usbdev_s *)priv, true); /* Reset FSUSP bit and enable normal interrupt handling */ stm32_putreg(STM32_CNTR_SETUP, STM32_USB_CNTR); /* Notify the class driver of the resume event */ if (priv->driver) { CLASS_RESUME(priv->driver, &priv->usbdev); } } /**************************************************************************** * Name: stm32_esofpoll ****************************************************************************/ static void stm32_esofpoll(struct stm32_usbdev_s *priv) { uint16_t regval; /* Called periodically from ESOF interrupt after RSMSTATE_STARTED */ switch (priv->rsmstate) { /* One ESOF after internal resume requested */ case RSMSTATE_STARTED: regval = stm32_getreg(STM32_USB_CNTR); regval |= USB_CNTR_RESUME; stm32_putreg(regval, STM32_USB_CNTR); priv->rsmstate = RSMSTATE_WAITING; priv->nesofs = 10; break; /* Countdown before completing the operation */ case RSMSTATE_WAITING: priv->nesofs--; if (priv->nesofs == 0) { /* Okay.. we are ready to resume normal operation */ regval = stm32_getreg(STM32_USB_CNTR); regval &= (~USB_CNTR_RESUME); stm32_putreg(regval, STM32_USB_CNTR); priv->rsmstate = RSMSTATE_IDLE; /* Disable ESOF polling, disable the SUSP interrupt, and enable * the WKUP interrupt. Clear any pending WKUP interrupt. */ stm32_setimask(priv, USB_CNTR_WKUPM, USB_CNTR_ESOFM|USB_CNTR_SUSPM); stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR); } break; case RSMSTATE_IDLE: default: priv->rsmstate = RSMSTATE_IDLE; break; } } /**************************************************************************** * Endpoint Helpers ****************************************************************************/ /**************************************************************************** * Name: stm32_epreserve ****************************************************************************/ static inline struct stm32_ep_s * stm32_epreserve(struct stm32_usbdev_s *priv, uint8_t epset) { struct stm32_ep_s *privep = NULL; irqstate_t flags; int epndx = 0; flags = irqsave(); epset &= priv->epavail; if (epset) { /* Select the lowest bit in the set of matching, available endpoints * (skipping EP0) */ for (epndx = 1; epndx < STM32_NENDPOINTS; epndx++) { uint8_t bit = STM32_ENDP_BIT(epndx); if ((epset & bit) != 0) { /* Mark the endpoint no longer available */ priv->epavail &= ~bit; /* And return the pointer to the standard endpoint structure */ privep = &priv->eplist[epndx]; break; } } } irqrestore(flags); return privep; } /**************************************************************************** * Name: stm32_epunreserve ****************************************************************************/ static inline void stm32_epunreserve(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep) { irqstate_t flags = irqsave(); priv->epavail |= STM32_ENDP_BIT(USB_EPNO(privep->ep.eplog)); irqrestore(flags); } /**************************************************************************** * Name: stm32_epreserved ****************************************************************************/ static inline bool stm32_epreserved(struct stm32_usbdev_s *priv, int epno) { return ((priv->epavail & STM32_ENDP_BIT(epno)) == 0); } /**************************************************************************** * Name: stm32_epallocpma ****************************************************************************/ static int stm32_epallocpma(struct stm32_usbdev_s *priv) { irqstate_t flags; int bufno = ERROR; int bufndx; flags = irqsave(); for (bufndx = 2; bufndx < STM32_NBUFFERS; bufndx++) { /* Check if this buffer is available */ uint8_t bit = STM32_BUFFER_BIT(bufndx); if ((priv->bufavail & bit) != 0) { /* Yes.. Mark the endpoint no longer available */ priv->bufavail &= ~bit; /* And return the index of the allocated buffer */ bufno = bufndx; break; } } irqrestore(flags); return bufno; } /**************************************************************************** * Name: stm32_epfreepma ****************************************************************************/ static inline void stm32_epfreepma(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep) { irqstate_t flags = irqsave(); priv->epavail |= STM32_ENDP_BIT(privep->bufno); irqrestore(flags); } /**************************************************************************** * Endpoint operations ****************************************************************************/ /**************************************************************************** * Name: stm32_epconfigure ****************************************************************************/ static int stm32_epconfigure(struct usbdev_ep_s *ep, const struct usb_epdesc_s *desc, bool last) { struct stm32_ep_s *privep = (struct stm32_ep_s *)ep; uint16_t pma; uint16_t setting; uint16_t maxpacket; uint8_t epno; #ifdef CONFIG_DEBUG if (!ep || !desc) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); ulldbg("ERROR: ep=%p desc=%p\n"); return -EINVAL; } #endif /* Get the unadorned endpoint address */ epno = USB_EPNO(desc->addr); usbtrace(TRACE_EPCONFIGURE, (uint16_t)epno); DEBUGASSERT(epno == USB_EPNO(ep->eplog)); /* Set the requested type */ switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK) { case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ setting = USB_EPR_EPTYPE_INTERRUPT; break; case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ setting = USB_EPR_EPTYPE_BULK; break; case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ #warning "REVISIT: Need to review isochronous EP setup" setting = USB_EPR_EPTYPE_ISOC; break; case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint */ setting = USB_EPR_EPTYPE_CONTROL; break; default: usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPTYPE), (uint16_t)desc->type); return -EINVAL; } stm32_seteptype(epno, setting); /* Get the address of the PMA buffer allocated for this endpoint */ #warning "REVISIT: Should configure BULK EPs using double buffer feature" pma = STM32_BUFNO2BUF(privep->bufno); /* Get the maxpacket size of the endpoint. */ maxpacket = GETUINT16(desc->mxpacketsize); DEBUGASSERT(maxpacket <= STM32_MAXPACKET_SIZE); ep->maxpacket = maxpacket; /* Get the subset matching the requested direction */ if (USB_ISEPIN(desc->addr)) { /* The full, logical EP number includes direction */ ep->eplog = USB_EPIN(epno); /* Set up TX; disable RX */ stm32_seteptxaddr(epno, pma); stm32_seteptxstatus(epno, USB_EPR_STATTX_NAK); stm32_seteprxstatus(epno, USB_EPR_STATRX_DIS); } else { /* The full, logical EP number includes direction */ ep->eplog = USB_EPOUT(epno); /* Set up RX; disable TX */ stm32_seteprxaddr(epno, pma); stm32_seteprxcount(epno, maxpacket); stm32_seteprxstatus(epno, USB_EPR_STATRX_VALID); stm32_seteptxstatus(epno, USB_EPR_STATTX_DIS); } stm32_dumpep(epno); return OK; } /**************************************************************************** * Name: stm32_epdisable ****************************************************************************/ static int stm32_epdisable(struct usbdev_ep_s *ep) { struct stm32_ep_s *privep = (struct stm32_ep_s *)ep; irqstate_t flags; uint8_t epno; #ifdef CONFIG_DEBUG if (!ep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); ulldbg("ERROR: ep=%p\n", ep); return -EINVAL; } #endif epno = USB_EPNO(ep->eplog); usbtrace(TRACE_EPDISABLE, epno); /* Cancel any ongoing activity */ flags = irqsave(); stm32_cancelrequests(privep); /* Disable TX; disable RX */ stm32_seteprxcount(epno, 0); stm32_seteprxstatus(epno, USB_EPR_STATRX_DIS); stm32_seteptxstatus(epno, USB_EPR_STATTX_DIS); irqrestore(flags); return OK; } /**************************************************************************** * Name: stm32_epallocreq ****************************************************************************/ static struct usbdev_req_s *stm32_epallocreq(struct usbdev_ep_s *ep) { struct stm32_req_s *privreq; #ifdef CONFIG_DEBUG if (!ep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return NULL; } #endif usbtrace(TRACE_EPALLOCREQ, USB_EPNO(ep->eplog)); privreq = (struct stm32_req_s *)malloc(sizeof(struct stm32_req_s)); if (!privreq) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_ALLOCFAIL), 0); return NULL; } memset(privreq, 0, sizeof(struct stm32_req_s)); return &privreq->req; } /**************************************************************************** * Name: stm32_epfreereq ****************************************************************************/ static void stm32_epfreereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req) { struct stm32_req_s *privreq = (struct stm32_req_s*)req; #ifdef CONFIG_DEBUG if (!ep || !req) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return; } #endif usbtrace(TRACE_EPFREEREQ, USB_EPNO(ep->eplog)); free(privreq); } /**************************************************************************** * Name: stm32_epsubmit ****************************************************************************/ static int stm32_epsubmit(struct usbdev_ep_s *ep, struct usbdev_req_s *req) { struct stm32_req_s *privreq = (struct stm32_req_s *)req; struct stm32_ep_s *privep = (struct stm32_ep_s *)ep; struct stm32_usbdev_s *priv; irqstate_t flags; uint8_t epno; int ret = OK; #ifdef CONFIG_DEBUG if (!req || !req->callback || !req->buf || !ep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); ulldbg("ERROR: req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf, ep); return -EINVAL; } #endif usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog)); priv = privep->dev; #ifdef CONFIG_DEBUG if (!priv->driver) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOTCONFIGURED), priv->usbdev.speed); ulldbg("ERROR: driver=%p\n", priv->driver); return -ESHUTDOWN; } #endif /* Handle the request from the class driver */ epno = USB_EPNO(ep->eplog); req->result = -EINPROGRESS; req->xfrd = 0; flags = irqsave(); /* If we are stalled, then drop all requests on the floor */ if (privep->stalled) { stm32_abortrequest(privep, privreq, -EBUSY); ulldbg("ERROR: stalled\n"); ret = -EBUSY; } /* Handle IN (device-to-host) requests. NOTE: If the class device is * using the bi-directional EP0, then we assume that they intend the EP0 * IN functionality. */ else if (USB_ISEPIN(ep->eplog) || epno == EP0) { /* Add the new request to the request queue for the IN endpoint */ stm32_rqenqueue(privep, privreq); usbtrace(TRACE_INREQQUEUED(epno), req->len); /* If the IN endpoint FIFO is available, then transfer the data now */ if (!privep->txbusy) { priv->txstatus = USB_EPR_STATTX_NAK; ret = stm32_wrrequest(priv, privep); /* Set the new TX status */ stm32_seteptxstatus(epno, priv->txstatus); } } /* Handle OUT (host-to-device) requests */ else { /* Add the new request to the request queue for the OUT endpoint */ privep->txnullpkt = 0; stm32_rqenqueue(privep, privreq); usbtrace(TRACE_OUTREQQUEUED(epno), req->len); /* This there a incoming data pending the availability of a request? */ if (priv->rxpending) { /* Set STAT_RX bits to '11' in the USB_EPnR, enabling further * transactions. "While the STAT_RX bits are equal to '10' * (NAK), any OUT request addressed to that endpoint is NAKed, * indicating a flow control condition: the USB host will retry * the transaction until it succeeds." */ priv->rxstatus = USB_EPR_STATRX_VALID; stm32_seteprxstatus(epno, priv->rxstatus); /* Data is no longer pending */ priv->rxpending = false; } } irqrestore(flags); return ret; } /**************************************************************************** * Name: stm32_epcancel ****************************************************************************/ static int stm32_epcancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req) { struct stm32_ep_s *privep = (struct stm32_ep_s *)ep; struct stm32_usbdev_s *priv; irqstate_t flags; #ifdef CONFIG_DEBUG if (!ep || !req) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -EINVAL; } #endif usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog)); priv = privep->dev; flags = irqsave(); stm32_cancelrequests(privep); irqrestore(flags); return OK; } /**************************************************************************** * Name: stm32_epstall ****************************************************************************/ static int stm32_epstall(struct usbdev_ep_s *ep, bool resume) { struct stm32_ep_s *privep; struct stm32_usbdev_s *priv; uint8_t epno = USB_EPNO(ep->eplog); uint16_t status; irqstate_t flags; #ifdef CONFIG_DEBUG if (!ep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -EINVAL; } #endif privep = (struct stm32_ep_s *)ep; priv = (struct stm32_usbdev_s *)privep->dev; epno = USB_EPNO(ep->eplog); /* STALL or RESUME the endpoint */ flags = irqsave(); usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, USB_EPNO(ep->eplog)); /* Get status of the endpoint; stall the request if the endpoint is * disabled */ if (USB_ISEPIN(ep->eplog)) { status = stm32_geteptxstatus(epno); } else { status = stm32_geteprxstatus(epno); } if (status == 0) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPDISABLED), 0); priv->devstate = DEVSTATE_STALLED; return -ENODEV; } /* Handle the resume condition */ if (resume) { /* Resuming a stalled endpoint */ usbtrace(TRACE_EPRESUME, epno); privep->stalled = false; if (USB_ISEPIN(ep->eplog)) { /* IN endpoint */ if (stm32_eptxstalled(epno)) { stm32_clrtxdtog(epno); /* Restart any queued write requests */ priv->txstatus = USB_EPR_STATTX_NAK; (void)stm32_wrrequest(priv, privep); /* Set the new TX status */ stm32_seteptxstatus(epno, priv->txstatus); } } else { /* OUT endpoint */ if (stm32_eprxstalled(epno)) { if (epno == EP0) { /* After clear the STALL, enable the default endpoint receiver */ stm32_seteprxcount(epno, ep->maxpacket); } else { stm32_clrrxdtog(epno); } priv->rxstatus = USB_EPR_STATRX_VALID; stm32_seteprxstatus(epno, USB_EPR_STATRX_VALID); } } } /* Handle the stall condition */ else { usbtrace(TRACE_EPSTALL, epno); privep->stalled = true; if (USB_ISEPIN(ep->eplog)) { /* IN endpoint */ priv->txstatus = USB_EPR_STATTX_STALL; stm32_seteptxstatus(epno, USB_EPR_STATTX_STALL); } else { /* OUT endpoint */ priv->rxstatus = USB_EPR_STATRX_STALL; stm32_seteprxstatus(epno, USB_EPR_STATRX_STALL); } } irqrestore(flags); return OK; } /**************************************************************************** * Device Controller Operations ****************************************************************************/ /**************************************************************************** * Name: stm32_allocep ****************************************************************************/ static struct usbdev_ep_s *stm32_allocep(struct usbdev_s *dev, uint8_t epno, bool in, uint8_t eptype) { struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev; struct stm32_ep_s *privep = NULL; uint8_t epset = STM32_ENDP_ALLSET; int bufno; usbtrace(TRACE_DEVALLOCEP, (uint16_t)epno); #ifdef CONFIG_DEBUG if (!dev) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return NULL; } #endif /* Ignore any direction bits in the logical address */ epno = USB_EPNO(epno); /* A logical address of 0 means that any endpoint will do */ if (epno > 0) { /* Otherwise, we will return the endpoint structure only for the requested * 'logical' endpoint. All of the other checks will still be performed. * * First, verify that the logical endpoint is in the range supported by * by the hardware. */ if (epno >= STM32_NENDPOINTS) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16_t)epno); return NULL; } /* Convert the logical address to a physical OUT endpoint address and * remove all of the candidate endpoints from the bitset except for the * the IN/OUT pair for this logical address. */ epset = STM32_ENDP_BIT(epno); } /* Check if the selected endpoint number is available */ privep = stm32_epreserve(priv, epset); if (!privep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPRESERVE), (uint16_t)epset); goto errout; } epno = USB_EPNO(privep->ep.eplog); /* Allocate a PMA buffer for this endpoint */ #warning "REVISIT: Should configure BULK EPs using double buffer feature" bufno = stm32_epallocpma(priv); if (bufno < 0) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPBUFFER), 0); goto errout_with_ep; } privep->bufno = (uint8_t)bufno; return &privep->ep; errout_with_ep: stm32_epunreserve(priv, privep); errout: return NULL; } /**************************************************************************** * Name: stm32_freeep ****************************************************************************/ static void stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep) { struct stm32_usbdev_s *priv; struct stm32_ep_s *privep; #ifdef CONFIG_DEBUG if (!dev || !ep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return; } #endif priv = (struct stm32_usbdev_s *)dev; privep = (struct stm32_ep_s *)ep; usbtrace(TRACE_DEVFREEEP, (uint16_t)USB_EPNO(ep->eplog)); if (priv && privep) { /* Free the PMA buffer assigned to this endpoint */ stm32_epfreepma(priv, privep); /* Mark the endpoint as available */ stm32_epunreserve(priv, privep); } } /**************************************************************************** * Name: stm32_getframe ****************************************************************************/ static int stm32_getframe(struct usbdev_s *dev) { uint16_t fnr; #ifdef CONFIG_DEBUG if (!dev) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -EINVAL; } #endif /* Return the last frame number detected by the hardware */ fnr = stm32_getreg(STM32_USB_FNR); usbtrace(TRACE_DEVGETFRAME, fnr); return (fnr & USB_FNR_FN_MASK); } /**************************************************************************** * Name: stm32_wakeup ****************************************************************************/ static int stm32_wakeup(struct usbdev_s *dev) { struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev; irqstate_t flags; usbtrace(TRACE_DEVWAKEUP, 0); #ifdef CONFIG_DEBUG if (!dev) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -EINVAL; } #endif /* Start the resume sequence. The actual resume steps will be driven * by the ESOF interrupt. */ flags = irqsave(); stm32_initresume(priv); priv->rsmstate = RSMSTATE_STARTED; /* Disable the SUSP interrupt (until we are fully resumed), disable * the WKUP interrupt (we are already waking up), and enable the * ESOF interrupt that will drive the resume operations. Clear any * pending ESOF interrupt. */ stm32_setimask(priv, USB_CNTR_ESOFM, USB_CNTR_WKUPM|USB_CNTR_SUSPM); stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR); irqrestore(flags); return OK; } /**************************************************************************** * Name: stm32_selfpowered ****************************************************************************/ static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered) { struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev; usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); #ifdef CONFIG_DEBUG if (!dev) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -ENODEV; } #endif priv->selfpowered = selfpowered; return OK; } /**************************************************************************** * Initialization/Reset ****************************************************************************/ /**************************************************************************** * Name: stm32_reset ****************************************************************************/ static void stm32_reset(struct stm32_usbdev_s *priv) { int epno; /* Put the USB controller in reset, disable all interrupts */ stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR); /* Tell the class driver that we are disconnected. The class driver * should then accept any new configurations. */ CLASS_DISCONNECT(priv->driver, &priv->usbdev); /* Reset the device state structure */ priv->devstate = DEVSTATE_IDLE; priv->rsmstate = RSMSTATE_IDLE; priv->rxpending = false; /* Reset endpoints */ for (epno = 0; epno < STM32_NENDPOINTS; epno++) { struct stm32_ep_s *privep = &priv->eplist[epno]; /* Cancel any queued requests. Since they are canceled * with status -ESHUTDOWN, then will not be requeued * until the configuration is reset. NOTE: This should * not be necessary... the CLASS_DISCONNECT above should * result in the class implementation calling stm32_epdisable * for each of its configured endpoints. */ stm32_cancelrequests(privep); /* Reset endpoint status */ privep->stalled = false; privep->halted = false; privep->txbusy = false; privep->txnullpkt = false; } /* Re-configure the USB controller in its initial, unconnected state */ stm32_hwreset(priv); priv->usbdev.speed = USB_SPEED_FULL; } /**************************************************************************** * Name: stm32_hwreset ****************************************************************************/ static void stm32_hwreset(struct stm32_usbdev_s *priv) { /* Put the USB controller into reset, clear all interrupt enables */ stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR); /* Disable interrupts (and perhaps take the USB controller out of reset) */ priv->imask = 0; stm32_putreg(priv->imask, STM32_USB_CNTR); /* Set the STM32 BTABLE address */ stm32_putreg(STM32_BTABLE_ADDRESS & 0xfff8, STM32_USB_BTABLE); /* Initialize EP0 */ stm32_seteptype(EP0, USB_EPR_EPTYPE_CONTROL); stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK); stm32_seteprxaddr(EP0, STM32_EP0_RXADDR); stm32_seteprxcount(EP0, STM32_EP0MAXPACKET); stm32_seteptxaddr(EP0, STM32_EP0_TXADDR); stm32_clrstatusout(EP0); stm32_seteprxstatus(EP0, USB_EPR_STATRX_VALID); /* Set the device to respond on default address */ stm32_setdevaddr(priv, 0); /* Clear any pending interrupts */ stm32_putreg(0, STM32_USB_ISTR); /* Enable interrupts at the USB controller */ stm32_setimask(priv, STM32_CNTR_SETUP, (USB_CNTR_ALLINTS & ~STM32_CNTR_SETUP)); stm32_dumpep(EP0); } /**************************************************************************** * Name: stm32_hwsetup ****************************************************************************/ static void stm32_hwsetup(struct stm32_usbdev_s *priv) { int epno; /* Power the USB controller, put the USB controller into reset, disable * all USB interrupts */ stm32_putreg(USB_CNTR_FRES|USB_CNTR_PDWN, STM32_USB_CNTR); /* Disconnect the device / disable the pull-up. We don't want the * host to enumerate us until the class driver is registered. */ stm32_usbpullup(&priv->usbdev, false); /* Initialize the device state structure. NOTE: many fields * have the initial value of zero and, hence, are not explicitly * initialized here. */ memset(priv, 0, sizeof(struct stm32_usbdev_s)); priv->usbdev.ops = &g_devops; priv->usbdev.ep0 = &priv->eplist[EP0].ep; priv->epavail = STM32_ENDP_ALLSET & ~STM32_ENDP_BIT(EP0); priv->bufavail = STM32_BUFFER_ALLSET & ~STM32_BUFFER_EP0; /* Initialize the endpoint list */ for (epno = 0; epno < STM32_NENDPOINTS; epno++) { /* Set endpoint operations, reference to driver structure (not * really necessary because there is only one controller), and * the (physical) endpoint number which is just the index to the * endpoint. */ priv->eplist[epno].ep.ops = &g_epops; priv->eplist[epno].dev = priv; priv->eplist[epno].ep.eplog = epno; /* We will use a fixed maxpacket size for all endpoints (perhaps * ISOC endpoints could have larger maxpacket???). A smaller * packet size can be selected when the endpoint is configured. */ priv->eplist[epno].ep.maxpacket = STM32_MAXPACKET_SIZE; } /* Select a smaller endpoint size for EP0 */ #if STM32_EP0MAXPACKET < STM32_MAXPACKET_SIZE priv->eplist[EP0].ep.maxpacket = STM32_EP0MAXPACKET; #endif /* Configure the USB controller. USB uses the following GPIO pins: * * PA9 - VBUS * PA10 - ID * PA11 - DM * PA12 - DP * * "As soon as the USB is enabled, these pins [DM and DP] are connected to * the USB internal transceiver automatically." */ /* Power up the USB controller, holding it in reset. There is a delay of * about 1uS after applying power before the USB will behave predictably. * A 5MS delay is more than enough. NOTE that we leave the USB controller * in the reset state; the hardware will not be initialized until the * class driver has been bound. */ stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR); up_mdelay(5); } /**************************************************************************** * Name: stm32_hwshutdown ****************************************************************************/ static void stm32_hwshutdown(struct stm32_usbdev_s *priv) { priv->usbdev.speed = USB_SPEED_UNKNOWN; /* Disable all interrupts and force the USB controller into reset */ stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR); /* Clear any pending interrupts */ stm32_putreg(0, STM32_USB_ISTR); /* Disconnect the device / disable the pull-up */ stm32_usbpullup(&priv->usbdev, false); /* Power down the USB controller */ stm32_putreg(USB_CNTR_FRES|USB_CNTR_PDWN, STM32_USB_CNTR); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: up_usbinitialize * Description: * Initialize the USB driver * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ void up_usbinitialize(void) { /* For now there is only one USB controller, but we will always refer to * it using a pointer to make any future ports to multiple USB controllers * easier. */ struct stm32_usbdev_s *priv = &g_usbdev; usbtrace(TRACE_DEVINIT, 0); stm32_checksetup(); /* Configure USB GPIO alternate function pins */ #ifdef CONFIG_STM32_STM32F30XX (void)stm32_configgpio(GPIO_USB_DM); (void)stm32_configgpio(GPIO_USB_DP); #endif /* Power up the USB controller, but leave it in the reset state */ stm32_hwsetup(priv); /* Remap the USB interrupt as needed (Only supported by the STM32 F3 family) */ #ifdef CONFIG_STM32_STM32F30XX # ifdef CONFIG_STM32_USB_ITRMP /* Clear the ITRMP bit to use the legacy, shared USB/CAN interrupts */ modifyreg32(STM32_RCC_APB1ENR, SYSCFG_CFGR1_USB_ITRMP, 0); # else /* Set the ITRMP bit to use the STM32 F3's dedicated USB interrupts */ modifyreg32(STM32_RCC_APB1ENR, 0, SYSCFG_CFGR1_USB_ITRMP); # endif #endif /* Attach USB controller interrupt handlers. The hardware will not be * initialized and interrupts will not be enabled until the class device * driver is bound. Getting the IRQs here only makes sure that we have * them when we need them later. */ if (irq_attach(STM32_IRQ_USBHP, stm32_hpinterrupt) != 0) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION), (uint16_t)STM32_IRQ_USBHP); goto errout; } if (irq_attach(STM32_IRQ_USBLP, stm32_lpinterrupt) != 0) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION), (uint16_t)STM32_IRQ_USBLP); goto errout; } return; errout: up_usbuninitialize(); } /**************************************************************************** * Name: up_usbuninitialize * Description: * Initialize the USB driver * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ void up_usbuninitialize(void) { /* For now there is only one USB controller, but we will always refer to * it using a pointer to make any future ports to multiple USB controllers * easier. */ struct stm32_usbdev_s *priv = &g_usbdev; irqstate_t flags; flags = irqsave(); usbtrace(TRACE_DEVUNINIT, 0); /* Disable and detach the USB IRQs */ up_disable_irq(STM32_IRQ_USBHP); up_disable_irq(STM32_IRQ_USBLP); irq_detach(STM32_IRQ_USBHP); irq_detach(STM32_IRQ_USBLP); if (priv->driver) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0); usbdev_unregister(priv->driver); } /* Put the hardware in an inactive state */ stm32_hwshutdown(priv); irqrestore(flags); } /**************************************************************************** * Name: usbdev_register * * Description: * Register a USB device class driver. The class driver's bind() method will be * called to bind it to a USB device driver. * ****************************************************************************/ int usbdev_register(struct usbdevclass_driver_s *driver) { /* For now there is only one USB controller, but we will always refer to * it using a pointer to make any future ports to multiple USB controllers * easier. */ struct stm32_usbdev_s *priv = &g_usbdev; int ret; usbtrace(TRACE_DEVREGISTER, 0); #ifdef CONFIG_DEBUG if (!driver || !driver->ops->bind || !driver->ops->unbind || !driver->ops->disconnect || !driver->ops->setup) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -EINVAL; } if (priv->driver) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVER), 0); return -EBUSY; } #endif /* First hook up the driver */ priv->driver = driver; /* Then bind the class driver */ ret = CLASS_BIND(driver, &priv->usbdev); if (ret) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BINDFAILED), (uint16_t)-ret); priv->driver = NULL; } else { /* Setup the USB controller -- enabling interrupts at the USB controller */ stm32_hwreset(priv); /* Enable USB controller interrupts at the NVIC */ up_enable_irq(STM32_IRQ_USBHP); up_enable_irq(STM32_IRQ_USBLP); /* Set the interrrupt priority */ up_prioritize_irq(STM32_IRQ_USBHP, CONFIG_USB_PRI); up_prioritize_irq(STM32_IRQ_USBLP, CONFIG_USB_PRI); /* Enable pull-up to connect the device. The host should enumerate us * some time after this */ stm32_usbpullup(&priv->usbdev, true); priv->usbdev.speed = USB_SPEED_FULL; } return ret; } /**************************************************************************** * Name: usbdev_unregister * * Description: * Un-register usbdev class driver. If the USB device is connected to a * USB host, it will first disconnect(). The driver is also requested to * unbind() and clean up any device state, before this procedure finally * returns. * ****************************************************************************/ int usbdev_unregister(struct usbdevclass_driver_s *driver) { /* For now there is only one USB controller, but we will always refer to * it using a pointer to make any future ports to multiple USB controllers * easier. */ struct stm32_usbdev_s *priv = &g_usbdev; irqstate_t flags; usbtrace(TRACE_DEVUNREGISTER, 0); #ifdef CONFIG_DEBUG if (driver != priv->driver) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -EINVAL; } #endif /* Reset the hardware and cancel all requests. All requests must be * canceled while the class driver is still bound. */ flags = irqsave(); stm32_reset(priv); /* Unbind the class driver */ CLASS_UNBIND(driver, &priv->usbdev); /* Disable USB controller interrupts (but keep them attached) */ up_disable_irq(STM32_IRQ_USBHP); up_disable_irq(STM32_IRQ_USBLP); /* Put the hardware in an inactive state. Then bring the hardware back up * in the reset state (this is probably not necessary, the stm32_reset() * call above was probably sufficient). */ stm32_hwshutdown(priv); stm32_hwsetup(priv); /* Unhook the driver */ priv->driver = NULL; irqrestore(flags); return OK; } #endif /* CONFIG_USBDEV && CONFIG_STM32_USB */