diff options
Diffstat (limited to 'nuttx/arch/arm/src/stm32/stm32_usbdev.c')
-rw-r--r-- | nuttx/arch/arm/src/stm32/stm32_usbdev.c | 3721 |
1 files changed, 0 insertions, 3721 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_usbdev.c b/nuttx/arch/arm/src/stm32/stm32_usbdev.c deleted file mode 100644 index 6036eb3d5..000000000 --- a/nuttx/arch/arm/src/stm32/stm32_usbdev.c +++ /dev/null @@ -1,3721 +0,0 @@ -/**************************************************************************** - * arch/arm/src/stm32/stm32_usbdev.c - * - * Copyright (C) 2009-2013 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt <gnutt@nuttx.orgr> - * - * 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 <nuttx/config.h> - -#include <sys/types.h> -#include <stdint.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <debug.h> - -#include <nuttx/arch.h> -#include <nuttx/usb/usb.h> -#include <nuttx/usb/usbdev.h> -#include <nuttx/usb/usbdev_trace.h> - -#include <arch/irq.h> - -#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)<<STM32_MAXPACKET_SHIFT)) - -/* USB-related masks */ - -#define REQRECIPIENT_MASK (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK) - -/* Endpoint register masks (handling toggle fields) */ - -#define EPR_NOTOG_MASK (USB_EPR_CTR_RX | USB_EPR_SETUP | USB_EPR_EPTYPE_MASK |\ - USB_EPR_EP_KIND | USB_EPR_CTR_TX | USB_EPR_EA_MASK) -#define EPR_TXDTOG_MASK (USB_EPR_STATTX_MASK | EPR_NOTOG_MASK) -#define EPR_RXDTOG_MASK (USB_EPR_STATRX_MASK | EPR_NOTOG_MASK) - -/* Request queue operations *************************************************/ - -#define stm32_rqempty(ep) ((ep)->head == NULL) -#define stm32_rqpeek(ep) ((ep)->head) - -/* USB trace ****************************************************************/ -/* Trace error codes */ - -#define STM32_TRACEERR_ALLOCFAIL 0x0001 -#define STM32_TRACEERR_BADCLEARFEATURE 0x0002 -#define STM32_TRACEERR_BADDEVGETSTATUS 0x0003 -#define STM32_TRACEERR_BADEPGETSTATUS 0x0004 -#define STM32_TRACEERR_BADEPNO 0x0005 -#define STM32_TRACEERR_BADEPTYPE 0x0006 -#define STM32_TRACEERR_BADGETCONFIG 0x0007 -#define STM32_TRACEERR_BADGETSETDESC 0x0008 -#define STM32_TRACEERR_BADGETSTATUS 0x0009 -#define STM32_TRACEERR_BADSETADDRESS 0x000a -#define STM32_TRACEERR_BADSETCONFIG 0x000b -#define STM32_TRACEERR_BADSETFEATURE 0x000c -#define STM32_TRACEERR_BINDFAILED 0x000d -#define STM32_TRACEERR_DISPATCHSTALL 0x000e -#define STM32_TRACEERR_DRIVER 0x000f -#define STM32_TRACEERR_DRIVERREGISTERED 0x0010 -#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_ep0state_e -{ - EP0STATE_IDLE = 0, /* No request in progress */ - EP0STATE_RDREQUEST, /* Read request in progress */ - EP0STATE_WRREQUEST, /* Write request in progress */ - EP0STATE_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 ep0state; /* State of EP0 (see enum stm32_ep0state_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->ep0state == EP0STATE_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); - return -ENOENT; - } - - 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); - - /* 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 request buffer). - */ - - if (bytesleft == 0 && !privep->txnullpkt) - { - /* Return the write request to the class driver */ - - usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd); - privep->txnullpkt = 0; - stm32_reqcomplete(privep, OK); - } - - 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 -ENOENT; - } - - 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); - - /* If the receive buffer is full or this is a partial packet, - * then we are finished with the request buffer). - */ - - privreq->req.xfrd += readlen; - if (pmalen < privep->ep.maxpacket || privreq->req.xfrd >= privreq->req.len) - { - /* Return the read request to the class driver. */ - - usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); - stm32_reqcomplete(privep, OK); - } - - 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->ep0state = EP0STATE_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 */ - - (void)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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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->ep0state = EP0STATE_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 ep0state != EP0STATE_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 EP0STATE_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->ep0state != EP0STATE_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->ep0state = EP0STATE_IDLE; - } -} - -/**************************************************************************** - * Name: stm32_ep0in - ****************************************************************************/ - -static void stm32_ep0in(struct stm32_usbdev_s *priv) -{ - int ret; - - /* 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->ep0state == EP0STATE_WRREQUEST) - { - ret = stm32_wrrequest(priv, &priv->eplist[EP0]); - priv->ep0state = ((ret == OK) ? EP0STATE_WRREQUEST : EP0STATE_IDLE); - } - - /* No.. Are we processing the completion of a status response? */ - - else if (priv->ep0state == EP0STATE_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->ep0state = EP0STATE_STALLED; - } -} - -/**************************************************************************** - * Name: stm32_ep0out - ****************************************************************************/ - -static void stm32_ep0out(struct stm32_usbdev_s *priv) -{ - int ret; - - struct stm32_ep_s *privep = &priv->eplist[EP0]; - switch (priv->ep0state) - { - case EP0STATE_RDREQUEST: /* Write request in progress */ - case EP0STATE_IDLE: /* No transfer in progress */ - ret = stm32_rdrequest(priv, privep); - priv->ep0state = ((ret == OK) ? EP0STATE_RDREQUEST : EP0STATE_IDLE); - break; - - default: - /* Unexpected state OR host aborted the OUT transfer before it - * completed, STALL the endpoint in either case - */ - - priv->ep0state = EP0STATE_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 ep0state 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->ep0state == EP0STATE_STALLED) - { - usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED), priv->ep0state); - 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); - - if (epno == 0) - { - priv->ep0state = EP0STATE_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->ep0state = EP0STATE_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 */ |