/******************************************************************************* * arch/arm/src/stm32/stm32_usbdev.c * * Copyright (C) 2012 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * *******************************************************************************/ /******************************************************************************* * Included Files *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chip.h" #include "up_arch.h" #include "up_internal.h" #include "stm32_otgfs.h" #if defined(CONFIG_USBDEV) && defined(CONFIG_STM32_OTGFS) /******************************************************************************* * Definitions *******************************************************************************/ /* Configuration ***************************************************************/ #ifndef CONFIG_USBDEV_EP0_MAXSIZE # define CONFIG_USBDEV_EP0_MAXSIZE 64 #endif #ifndef CONFIG_USBDEV_MAXPOWER # define CONFIG_USBDEV_MAXPOWER 100 /* mA */ #endif #ifndef CONFIG_USBDEV_RXFIFO_SIZE # define CONFIG_USBDEV_RXFIFO_SIZE 128 #endif #ifndef CONFIG_USBDEV_EP0_TXFIFO_SIZE # define CONFIG_USBDEV_EP0_TXFIFO_SIZE 128 #endif #if CONFIG_USBDEV_EP0_TXFIFO_SIZE < 16 || CONFIG_USBDEV_EP0_TXFIFO_SIZE > 256 # error "CONFIG_USBDEV_EP0_TXFIFO_SIZE is out of range" #endif #ifndef CONFIG_USBDEV_EP1_TXFIFO_SIZE # define CONFIG_USBDEV_EP1_TXFIFO_SIZE 128 #endif #ifndef CONFIG_USBDEV_EP2_TXFIFO_SIZE # define CONFIG_USBDEV_EP2_TXFIFO_SIZE 128 #endif #ifndef CONFIG_USBDEV_EP3_TXFIFO_SIZE # define CONFIG_USBDEV_EP3_TXFIFO_SIZE 128 #endif /* Debug ***********************************************************************/ /* Trace error codes */ #define STM32_TRACEERR_ALLOCFAIL 0x0001 #define STM32_TRACEERR_BADCLEARFEATURE 0x0002 #define STM32_TRACEERR_BADDEVGETSTATUS 0x0003 #define STM32_TRACEERR_BADEPNO 0x0004 #define STM32_TRACEERR_BADEPGETSTATUS 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_BADTESTMODE 0x000d #define STM32_TRACEERR_BINDFAILED 0x000e #define STM32_TRACEERR_DISPATCHSTALL 0x000f #define STM32_TRACEERR_DRIVER 0x0010 #define STM32_TRACEERR_DRIVERREGISTERED 0x0011 #define STM32_TRACEERR_EP0SETUPSTALLED 0x0012 #define STM32_TRACEERR_EPINNULLPACKET 0x0013 #define STM32_TRACEERR_EPOUTNULLPACKET 0x0014 #define STM32_TRACEERR_INVALIDCTRLREQ 0x0015 #define STM32_TRACEERR_INVALIDPARMS 0x0016 #define STM32_TRACEERR_IRQREGISTRATION 0x0017 #define STM32_TRACEERR_NOEP 0x0018 #define STM32_TRACEERR_NOTCONFIGURED 0x0019 #define STM32_TRACEERR_REQABORTED 0x001a /* Trace interrupt codes */ #define STM32_TRACEINTID_USB 0x0001 /* USB Interrupt entry/exit */ #define STM32_TRACEINTID_EPOUT 0x0101 /* First level interrupt decode */ #define STM32_TRACEINTID_EPIN 0x0102 #define STM32_TRACEINTID_MISMATCH 0x0103 #define STM32_TRACEINTID_WAKEUP 0x0104 #define STM32_TRACEINTID_SUSPEND 0x0105 #define STM32_TRACEINTID_SOF 0x0106 #define STM32_TRACEINTID_RXFIFO 0x0107 #define STM32_TRACEINTID_DEVRESET 0x0108 #define STM32_TRACEINTID_ENUMDNE 0x0109 #define STM32_TRACEINTID_IISOIXFR 0x010a #define STM32_TRACEINTID_IPXFR 0x010b #define STM32_TRACEINTID_SRQ 0x010c #define STM32_TRACEINTID_OTG 0x010d #define STM32_TRACEINTID_EPOUT_XFRC 0x0200 /* EPOUT second level decode */ #define STM32_TRACEINTID_EPOUT_EPDISD 0x0201 #define STM32_TRACEINTID_EPOUT_SETUP 0x0202 #define STM32_TRACEINTID_DISPATCH 0x0203 #define STM32_TRACEINTID_GETSTATUS 0x0210 /* EPOUT third level decode */ #define STM32_TRACEINTID_EPGETSTATUS 0x0211 #define STM32_TRACEINTID_DEVGETSTATUS 0x0212 #define STM32_TRACEINTID_IFGETSTATUS 0x0213 #define STM32_TRACEINTID_CLEARFEATURE 0x0214 #define STM32_TRACEINTID_SETFEATURE 0x0215 #define STM32_TRACEINTID_SETADDRESS 0x0216 #define STM32_TRACEINTID_GETSETDESC 0x0217 #define STM32_TRACEINTID_GETCONFIG 0x0218 #define STM32_TRACEINTID_SETCONFIG 0x0219 #define STM32_TRACEINTID_GETSETIF 0x021a #define STM32_TRACEINTID_SYNCHFRAME 0x021b #define STM32_TRACEINTID_EPIN_XFRC 0x0300 /* EPIN second level decode */ #define STM32_TRACEINTID_EPIN_TOC 0x0301 #define STM32_TRACEINTID_EPIN_ITTXFE 0x0302 #define STM32_TRACEINTID_EPIN_INEPNE 0x0303 #define STM32_TRACEINTID_EPIN_EPDISD 0x0304 #define STM32_TRACEINTID_EPIN_TXFE 0x0305 #define STM32_TRACEINTID_EP0COMPLETE 0x0400 /* Request handling */ #define STM32_TRACEINTID_EPINCOMPLETE 0x0401 #define STM32_TRACEINTID_EPINQEMPTY 0x0402 #define STM32_TRACEINTID_EPOUTCOMPLETE 0x0403 #define STM32_TRACEINTID_EPOUTQEMPTY 0x0404 /* Endpoints ******************************************************************/ /* Number of endpoints */ #define STM32_NENDPOINTS (4) /* ep0-3 x 2 for IN and OUT */ /* Odd physical endpoint numbers are IN; even are OUT */ #define STM32_EPPHYIN2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_IN) #define STM32_EPPHYOUT2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_OUT) /* Endpoint 0 */ #define EP0 (0) /* Each endpoint has somewhat different characteristics */ #define STM32_EPALLSET (0xff) /* All endpoints */ #define STM32_EPOUTSET (0x55) /* Even phy endpoint numbers are OUT EPs */ #define STM32_EPINSET (0xaa) /* Odd endpoint numbers are IN EPs */ #define STM32_EPCTRLSET (0x03) /* EP0 IN/OUT are control endpoints */ #define STM32_EPINTRSET (0xa8) /* Interrupt endpoints */ #define STM32_EPBULKSET (0xfc) /* Bulk endpoints */ #define STM32_EPISOCSET (0xfc) /* Isochronous endpoints */ /* Maximum packet sizes for full speed endpoints */ #define STM32_MAXPACKET (64) /* Max packet size (1-64) */ /* Delays **********************************************************************/ #define STM32_READY_DELAY 200000 #define STM32_FLUSH_DELAY 200000 /* Request queue operations ****************************************************/ #define stm32_rqempty(ep) ((ep)->head == NULL) #define stm32_rqpeek(ep) ((ep)->head) /* Standard stuff **************************************************************/ #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX # define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif /******************************************************************************* * Private Types *******************************************************************************/ /* Overall device state */ enum stm32_devstate_e { DEVSTATE_DEFAULT = 0, /* Power-up, unconfigured state */ DEVSTATE_ADDRESSED, /* Device address has been assigned, not configured */ DEVSTATE_CONFIGURED, /* Address assigned and configured */ DEVSTATE_SUSPENDED, /* Suspended */ }; /* Endpoint 0 states */ enum stm32_ep0state_e { EP0STATE_IDLE = 0, /* Idle State, leave on receiving a setup packet or epsubmit */ EP0STATE_SETUP_OUT, /* Setup Packet received - SET/CLEAR */ EP0STATE_SETUP_IN, /* Setup Packet received - GET */ EP0STATE_SHORTWRITE, /* Short write (without a usb_request) */ EP0STATE_NAK_OUT, /* Waiting for Host to elicit status phase (GET) */ EP0STATE_NAK_IN, /* Waiting for Host to elicit status phase (SET/CLEAR) */ EP0STATE_STATUS_OUT, /* Wait for status phase to complete */ EP0STATE_STATUS_IN, /* Wait for status phase to complete */ EP0STATE_DATA_IN, /* Waiting for data out stage (with a usb_request) */ EP0STATE_DATA_OUT /* Waiting for data in phase to complete */ }; /* Parsed control request */ struct stm32_ctrlreq_s { uint8_t type; uint8_t req; uint16_t value; uint16_t index; uint16_t len; }; /* A container for a request so that the request may 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 */ /* STM32-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 epphy; /* Physical EP address */ uint8_t eptype:2; /* Endpoint type */ uint8_t configured:1; /* 1: Endpoint has been configured */ uint8_t active:1; /* 1: A request is being processed */ uint8_t stalled:1; /* 1: Endpoint is stalled */ uint8_t isin:1; /* 1: IN Endpoint */ uint8_t odd:1; /* 1: Odd frame */ uint8_t zlp:1; /* 1: Transmit a zero-length-packet (IN EPs only) */ }; /* This structure retains the state of the USB device controller */ 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 struct stm32_usbdev_s. */ struct usbdev_s usbdev; /* The bound device class driver */ struct usbdevclass_driver_s *driver; /* STM32-specific fields */ uint8_t stalled:1; /* 1: Protocol stalled */ uint8_t selfpowered:1; /* 1: Device is self powered */ uint8_t connected:1; /* 1: Host connected */ uint8_t addressed:1; /* 1: Peripheral address has been set */ uint8_t configured:1; /* 1: Class driver has been configured */ uint8_t wakeup:1; /* 1: Device remote wake-up */ uint8_t dotest:1; /* 1: Test mode selected */ uint8_t devstate; /* See enum stm32_devstate_e */ uint8_t ep0state; /* See enum stm32_ep0state_e */ uint8_t savestate; /* Saved devstate */ uint8_t ep0resp[2]; /* buffer for EP0 short transfers */ uint8_t testmode; /* Selected test mode */ uint32_t epavail; /* Bitset of available endpoints */ struct usb_ctrlreq_s ctrlreq; /* Received SETUP request */ /* The endpoint list */ struct stm32_ep_s epin[STM32_NENDPOINTS]; struct stm32_ep_s epout[STM32_NENDPOINTS]; }; /******************************************************************************* * Private Function Prototypes *******************************************************************************/ /* Register operations ********************************************************/ #if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) static uint32_t stm32_getreg(uint32_t addr); static void stm32_putreg(uint32_t val, uint32_t addr); #else # define stm32_getreg(addr) getreg32(addr) # define stm32_putreg(val,addr) putreg32(val,addr) #endif /* Request queue operations ****************************************************/ static FAR struct stm32_req_s *stm32_remfirst(FAR struct stm32_ep_s *privep); static bool stm32_addlast(FAR struct stm32_ep_s *privep, FAR struct stm32_req_s *req); /* Low level data transfers and request operations *****************************/ static inline void stm32_ep0xfer(uint8_t epphy, uint8_t *data, uint32_t nbytes); static void stm32_ep0read(FAR uint8_t *dest, uint16_t len); static void stm32_flushep(struct stm32_ep_s *privep); 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 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, int16_t status); /* Interrupt handling **********************************************************/ static struct stm32_ep_s *stm32_epfindbyaddr(struct stm32_usbdev_s *priv, uint16_t eplog); static int stm32_dispatchrequest(struct stm32_usbdev_s *priv, const struct usb_ctrlreq_s *ctrl); static void stm32_ep0configure(struct stm32_usbdev_s *priv); static void stm32_usbreset(struct stm32_usbdev_s *priv); static void stm32_ep0complete(struct stm32_usbdev_s *priv, uint8_t epphy); static bool stm32_epcomplete(struct stm32_usbdev_s *priv, uint8_t epphy); /* Second level IN endpoint interrupt processing */ static inline void stm32_runtestmode(FAR struct stm32_usbdev_s *priv); static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno); static inline void stm32_epininterrupt(FAR struct stm32_usbdev_s *priv); /* Second level OUT endpoint interrupt processing */ static inline void stm32_setaddress(struct stm32_usbdev_s *priv, uint16_t address); static inline void stm32_testmode(FAR struct stm32_usbdev_s *priv, uint16_t index); static inline void stm32_stdrequest(struct stm32_usbdev_s *priv, FAR struct stm32_ctrlreq_s *ctrlreq); static inline void stm32_ep0setup(struct stm32_usbdev_s *priv); static inline void stm32_epout(FAR struct stm32_usbdev_s *priv, uint8_t epno); static inline void stm32_epoutinterrupt(FAR struct stm32_usbdev_s *priv); /* Other second level interrupt processing */ static inline void stm32_enuminterrupt(FAR struct stm32_usbdev_s *priv); #ifdef CONFIG_USBDEV_ISOCHRONOUS static inline void stm32_isocininterrupt(FAR struct stm32_usbdev_s *priv); static inline void stm32_isocoutinterrupt(FAR struct stm32_usbdev_s *priv); #endif /* First level interrupt processing */ static int stm32_usbinterrupt(int irq, FAR void *context); /* Endpoint operations *********************************************************/ static int stm32_epconfigure(FAR struct usbdev_ep_s *ep, const struct usb_epdesc_s *desc, bool last); static int stm32_epdisable(FAR struct usbdev_ep_s *ep); static FAR struct usbdev_req_s *stm32_epallocreq(FAR struct usbdev_ep_s *ep); static void stm32_epfreereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *); #ifdef CONFIG_ARCH_USBDEV_DMA static void *stm32_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes); static void stm32_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf); #endif static int stm32_epsubmit(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req); static int stm32_epcancel(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req); /* Stall handling */ static int stm32_epsetstall(FAR struct stm32_ep_s *privep); static int stm32_epclrstall(FAR struct stm32_ep_s *privep); static int stm32_epstall(FAR struct usbdev_ep_s *ep, bool resume); static void stm32_ep0stall(FAR struct stm32_usbdev_s *priv); /* Endpoint allocation */ static FAR struct usbdev_ep_s *stm32_allocep(FAR struct usbdev_s *dev, uint8_t epno, bool in, uint8_t eptype); static void stm32_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep); /* USB device controller operations ********************************************/ 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); static int stm32_pullup(struct usbdev_s *dev, bool enable); /* Initialization **************************************************************/ static int stm32_flushtxfifo(uint32_t txfnum); static int stm32_flushrxfifo(void); static void stm32_swinitialize(FAR struct stm32_usbdev_s *priv); static void stm32_hwinitialize(FAR 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_otgfsdev; static const struct usbdev_epops_s g_epops = { .configure = stm32_epconfigure, .disable = stm32_epdisable, .allocreq = stm32_epallocreq, .freereq = stm32_epfreereq, #ifdef CONFIG_ARCH_USBDEV_DMA .allocbuffer = stm32_epallocbuffer, .freebuffer = stm32_epfreebuffer, #endif .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_pullup, }; /******************************************************************************* * Public Data *******************************************************************************/ /******************************************************************************* * Private Functions *******************************************************************************/ /******************************************************************************* * Name: stm32_getreg * * Description: * Get the contents of an STM32 register * *******************************************************************************/ #if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) static uint32_t stm32_getreg(uint32_t addr) { static uint32_t prevaddr = 0; static uint32_t preval = 0; static uint32_t count = 0; /* Read the value from the register */ uint32_t val = getreg32(addr); /* Is this the same value that we read from the same registe 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->%08x\n", addr, val); return val; } #endif /******************************************************************************* * Name: stm32_putreg * * Description: * Set the contents of an STM32 register to a value * *******************************************************************************/ #if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) static void stm32_putreg(uint32_t val, uint32_t addr) { /* Show the register value being written */ lldbg("%08x<-%08x\n", addr, val); /* Write the value */ putreg32(val, addr); } #endif /******************************************************************************* * Name: stm32_remfirst * * Description: * Remove a request from the head of an endpoint request queue * *******************************************************************************/ static FAR struct stm32_req_s *stm32_remfirst(FAR struct stm32_ep_s *privep) { FAR 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_addlast * * Description: * Add a request to the end of an endpoint request queue * *******************************************************************************/ static bool stm32_addlast(FAR struct stm32_ep_s *privep, FAR struct stm32_req_s *req) { bool is_empty = !privep->head; req->flink = NULL; if (is_empty) { privep->head = req; privep->tail = req; } else { privep->tail->flink = req; privep->tail = req; } return is_empty; } /******************************************************************************* * Name: stm32_ep0xfer * * Description: * Schedule a short transfer for Endpoint 0 (IN or OUT) * *******************************************************************************/ static inline void stm32_ep0xfer(uint8_t epphy, uint8_t *buf, uint32_t nbytes) { #warning "Missing Logic" } /******************************************************************************* * Name: stm32_ep0read * * Description: * Read a Setup packet from the DTD. * *******************************************************************************/ static void stm32_ep0read(FAR uint8_t *dest, uint16_t len) { uint32_t regaddr; int i; /* Get the address of the EP0 FIFO */ regaddr = STM32_OTGFS_DFIFO_DEP(0); /* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */ for (i = 0; i < len; i += 4) { union { uint32_t w; uint8_t b[4]; } data; /* Read 1 x 32-bits of EP0 packet data */ data.w = stm32_getreg(regaddr); /* Write 4 x 8-bits of EP0 packet data */ *dest++ = data.b[0]; *dest++ = data.b[1]; *dest++ = data.b[2]; *dest++ = data.b[3]; } } /******************************************************************************* * Name: stm32_flushep * * Description: * Flush any primed descriptors from this ep * *******************************************************************************/ static void stm32_flushep(struct stm32_ep_s *privep) { if (privep->isin) { stm32_flushtxfifo(OTGFS_GRSTCTL_TXFNUM_D(privep->epphy)); } else { stm32_flushrxfifo(); } } /******************************************************************************* * Name: stm32_abortrequest * * Description: * Discard a request * *******************************************************************************/ 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)privep->epphy); /* 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 * * Description: * Handle termination of the request at the head of the endpoint request queue. * *******************************************************************************/ static void stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result) { FAR struct stm32_req_s *privreq; /* Remove the request at the head of the request list */ privreq = stm32_remfirst(privep); DEBUGASSERT(privreq != NULL); /* If endpoint 0, temporarily reflect the state of protocol stalled * in the callback. */ bool stalled = privep->stalled; if (privep->epphy == EP0) { privep->stalled = privep->dev->stalled; } /* Save the result in the request structure */ privreq->req.result = result; /* Callback to the request completion handler */ privreq->req.callback(&privep->ep, &privreq->req); /* Restore the stalled indication */ privep->stalled = stalled; } /**************************************************************************** * Name: stm32_wrrequest * * Description: * Begin or continue write request processing. * ****************************************************************************/ 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->active = 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); privep->active = false; return OK; } epno = USB_EPNO(privep->ep.eplog); ullvdbg("epno=%d req=%p: len=%d xfrd=%d nullpkt=%d\n", epno, privreq, privreq->req.len, privreq->req.xfrd, privep->zlp); /* Get the number of bytes left to be sent in the packet */ bytesleft = privreq->req.len - privreq->req.xfrd; nbytes = bytesleft; /* Send the next packet */ if (nbytes > 0) { /* Either send the maxpacketsize or all of the remaining data in * the request. */ privep->zlp = 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->zlp = 1; } } } /* Send the packet (might be a null packet nbytes == 0) */ buf = privreq->req.buf + privreq->req.xfrd; stm32_epwrite(priv, privep, buf, nbytes); privep->active = true; /* Update for the next data IN interrupt */ privreq->req.xfrd += nbytes; bytesleft = privreq->req.len - privreq->req.xfrd; /* If all of the bytes were sent (including any final null packet) * then we are finished with the transfer */ if (bytesleft == 0 && !privep->zlp) { usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd); privep->zlp = 0; stm32_reqcomplete(privep, OK); privep->active = false; } return OK; } /******************************************************************************* * Name: stm32_rdrequest * * Description: * Begin or continue read request processing. * *******************************************************************************/ 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); privep->active = false; return OK; } ullvdbg("EP%d: len=%d xfrd=%d\n", epno, privreq->req.len, privreq->req.xfrd); /* Ignore any attempt to receive a zero length packet */ if (privreq->req.len == 0) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTNULLPACKET), 0); stm32_reqcomplete(privep, OK); return OK; } usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd); /* Get the source and destination transfer addresses */ dest = privreq->req.buf + privreq->req.xfrd; src = stm32_geteprxaddr(epno); /* Get the number of bytes to read from packet memory */ pmalen = stm32_geteprxcount(epno); readlen = MIN(privreq->req.len, pmalen); /* Receive the next packet */ stm32_copyfrompma(dest, src, readlen); privep->active = true; /* If the receive buffer is full or this is a partial packet, * then we are finished with the transfer */ privreq->req.xfrd += readlen; if (pmalen < privep->ep.maxpacket || privreq->req.xfrd >= privreq->req.len) { /* Complete the transfer and mark the state IDLE. The endpoint * RX will be marked valid when the data phase completes. */ usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); stm32_reqcomplete(privep, OK); privep->active = false; } return OK; } /******************************************************************************* * Name: stm32_cancelrequests * * Description: * Cancel all pending requests for an endpoint * *******************************************************************************/ static void stm32_cancelrequests(struct stm32_ep_s *privep, int16_t status) { if (!stm32_rqempty(privep)) { stm32_flushep(privep); } while (!stm32_rqempty(privep)) { usbtrace(TRACE_COMPLETE(privep->epphy), (stm32_rqpeek(privep))->req.xfrd); stm32_reqcomplete(privep, status); } } /******************************************************************************* * Name: stm32_epfindbyaddr * * Description: * Find the physical endpoint structure corresponding to a logic endpoint * address * *******************************************************************************/ static struct stm32_ep_s *stm32_epfindbyaddr(struct stm32_usbdev_s *priv, uint16_t eplog) { struct stm32_ep_s *privep; uint8_t epphy = USB_EPNO(eplog); if (epphy >= STM32_NENDPOINTS) { return NULL; } /* Is this an IN or an OUT endpoint? */ if (USB_ISEPIN(eplog)) { privep = &priv->epin[epphy]; } else { privep = &priv->epout[epphy]; } /* Verify that the endpoint has been configured */ if (!privep->configured) { return NULL; } /* Return endpoint reference */ DEBUGASSERT(privep->epphy == epphy); return privep; } /******************************************************************************* * Name: stm32_dispatchrequest * * Description: * Provide unhandled setup actions to the class driver. This is logically part * of the USB interrupt handler. * *******************************************************************************/ static int stm32_dispatchrequest(struct stm32_usbdev_s *priv, const struct usb_ctrlreq_s *ctrl) { int ret = -EIO; usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DISPATCH), 0); if (priv->driver) { /* Forward to the control request to the class driver implementation */ ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl); } if (ret < 0) { /* Stall on failure */ usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DISPATCHSTALL), 0); priv->stalled = true; } return ret; } /******************************************************************************* * Name: stm32_ep0configure * * Description: * Reset Usb engine * *******************************************************************************/ static void stm32_ep0configure(struct stm32_usbdev_s *priv) { /* Enable ep0 IN and ep0 OUT */ #warning "Missing Logic" /* Enable EP0 */ #warning "Missing Logic" } /******************************************************************************* * Name: stm32_usbreset * * Description: * Reset Usb engine * *******************************************************************************/ static void stm32_usbreset(struct stm32_usbdev_s *priv) { int epphy; /* Disable all endpoints */ #warning "Missing Logic" /* Clear all pending interrupts */ #warning "Missing Logic" /* Flush all FIFOs */ #warning "Missing Logic" /* Reset endpoints */ for (epphy = 0; epphy < STM32_NENDPOINTS; epphy++) { struct stm32_ep_s *privep = &priv->epin[epphy]; stm32_cancelrequests (privep, -ESHUTDOWN); /* Reset endpoint status */ privep->stalled = false; } /* Tell the class driver that we are disconnected. The class * driver should then accept any new configurations. */ if (priv->driver) { CLASS_DISCONNECT(priv->driver, &priv->usbdev); } /* Set USB address to 0 */ stm32_setaddress(priv, 0); /* EndPoint 0 initialization */ stm32_ep0configure(priv); /* Re-enable device interrupts */ #warning "Missing Logic" } /******************************************************************************* * Name: stm32_ep0complete * * Description: * Transfer complete handler for Endpoint 0 * *******************************************************************************/ static void stm32_ep0complete(struct stm32_usbdev_s *priv, uint8_t epphy) { FAR struct stm32_ep_s *privep = &priv->epin[epphy]; usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0COMPLETE), (uint16_t)priv->ep0state); switch (priv->ep0state) { case EP0STATE_DATA_IN: if (stm32_rqempty(privep)) { return; } if (stm32_epcomplete(priv, epphy)) { priv->ep0state = EP0STATE_NAK_OUT; } break; case EP0STATE_DATA_OUT: if (stm32_rqempty(privep)) { return; } if (stm32_epcomplete(priv, epphy)) { priv->ep0state = EP0STATE_NAK_IN; } break; case EP0STATE_SHORTWRITE: priv->ep0state = EP0STATE_NAK_OUT; break; case EP0STATE_STATUS_IN: priv->ep0state = EP0STATE_IDLE; break; case EP0STATE_STATUS_OUT: priv->ep0state = EP0STATE_IDLE; break; default: #ifdef CONFIG_DEBUG DEBUGASSERT(priv->ep0state != EP0STATE_DATA_IN && priv->ep0state != EP0STATE_DATA_OUT && priv->ep0state != EP0STATE_SHORTWRITE && priv->ep0state != EP0STATE_STATUS_IN && priv->ep0state != EP0STATE_STATUS_OUT); #endif priv->stalled = true; break; } if (priv->stalled) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED), priv->ep0state); stm32_ep0stall(priv); } } /******************************************************************************* * Name: stm32_epcomplete * * Description: * Transfer complete handler for Endpoints other than 0 * returns whether the request at the head has completed * *******************************************************************************/ bool stm32_epcomplete(struct stm32_usbdev_s *priv, uint8_t epphy) { struct stm32_ep_s *privep = &priv->epin[epphy]; struct stm32_req_s *privreq = privep->head; int xfrd; if (privreq == NULL) /* This shouldn't really happen */ { if (privep->isin) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINQEMPTY), 0); } else { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTQEMPTY), 0); } return true; } /* Get the number of bytes transferred */ #warning "Missing logic" privreq->req.xfrd += xfrd; bool complete = true; if (privep->isin) { /* write(IN) completes when request finished, unless we need to terminate with a ZLP */ bool need_zlp = (xfrd == privep->ep.maxpacket) && ((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0); complete = (privreq->req.xfrd >= privreq->req.len && !need_zlp); usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINCOMPLETE), complete); } else { /* read(OUT) completes when request filled, or a short transfer is received */ usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTCOMPLETE), complete); } /* Handle the transfer completion by returning the request to class driver */ if (complete) { usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); stm32_reqcomplete(privep, OK); /* If the transfer is complete, process the next request in the request list */ if (!stm32_rqempty(privep)) { /* Process the next request in the request list */ #warning "Missing logic" } } return complete; } /******************************************************************************* * Name: stm32_runtestmode * * Description: * Execute the test mode setup by the SET FEATURE request * *******************************************************************************/ static inline void stm32_runtestmode(FAR struct stm32_usbdev_s *priv) { uint32_t regval = stm32_getreg(STM32_OTGFS_DCTL); regval &= OTGFS_DCTL_TCTL_MASK; regval |= (uint32_t)priv->testmode << OTGFS_DCTL_TCTL_SHIFT; stm32_putreg(regval , STM32_OTGFS_DCTL); priv->dotest = 0; priv->testmode = OTGFS_TESTMODE_DISABLED; } /******************************************************************************* * Name: stm32_epin * * Description: * This is part of the IN endpoint interrupt processing. This function * handles the IN event for a single endpoint. * *******************************************************************************/ static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno) { FAR struct stm32_ep_s *privep = &priv->epin[epno]; /* Endpoint 0 is a special case. */ if (epno == 0) { /* In the EP0STATE_DATA_IN state, we are sending data from request * buffer. In that case, we must continue the request processing. */ if (priv->ep0state == EP0STATE_DATA_OUT) { /* Continue processing data from the EP0 OUT request queue */ (void)stm32_wrrequest(priv, privep); } /* If we are not actively processing an OUT request, then we * need to setup to receive the next control request. */ if (!privep->active) { stm32_recvctlstatus(priv); } /* Test mode is another special case */ if (priv->dotest) { stm32_runtestmode(priv); } } /* For other endpoints, the only possibility is that we are continuing * or finishing an IN request. */ else if (priv->devstate == DEVSTATE_CONFIGURED) { /* Continue processing data from the EP0 OUT request queue */ (void)stm32_wrrequest(priv, privep); } } /******************************************************************************* * Name: stm32_epininterrupt * * Description: * USB IN endpoint interrupt handler * *******************************************************************************/ static inline void stm32_epininterrupt(FAR struct stm32_usbdev_s *priv) { uint32_t diepint; uint32_t daint; uint32_t mask; uint32_t empty; int epno; /* Get the pending, enabled interrupts for the IN endpoint from the endpoint * interrupt status register. */ daint = stm32_getreg(STM32_OTGFS_DAINT); daint &= stm32_getreg(STM32_OTGFS_DAINTMSK); daint &= OTGFS_DAINT_IEP_MASK; /* Process each pending IN endpoint interrupt */ epno = 0; while (daint) { /* Is an IN interrupt pending for this endpoint? */ if ((daint & 1) != 0) { /* Get IN interrupt mask register. Bits 0-6 correspond to enabled * interrupts as will be found in the DIEPINT interrupt status * register. */ mask = stm32_getreg(STM32_OTGFS_DIEPMSK); /* Check for FIFO not empty. Bits n corresponds to endpoint n. * That condition corresponds to bit 7 of the DIEPINT interrupt * status register. */ empty = stm32_getreg(STM32_OTGFS_DIEPEMPMSK); if ((empty & OTGFS_DIEPEMPMSK(epno)) != 0) { mask |= OTGFS_DIEPINT_TXFE; } /* Now, read the interrupt status and mask out all disabled * interrupts. */ diepint = stm32_getreg(STM32_OTGFS_DIEPINT(epno)) & mask; /* Decode and process the enabled, pending interrupts */ /* Transfer completed interrupt */ if ((diepint & OTGFS_DIEPINT_XFRC) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_XFRC), (uint16_t)diepint); empty &= ~OTGFS_DIEPEMPMSK(epno); stm32_putreg(empty, STM32_OTGFS_DIEPEMPMSK); stm32_putreg(OTGFS_DIEPINT_XFRC, STM32_OTGFS_DIEPINT(epno)); /* IN complete */ stm32_epin(priv, epno); } /* Timeout condition */ if ((diepint & OTGFS_DIEPINT_TOC) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_TOC), (uint16_t)diepint); stm32_putreg(OTGFS_DIEPINT_TOC, STM32_OTGFS_DIEPINT(epno)); } /* IN token received when TxFIFO is empty */ if ((diepint & OTGFS_DIEPINT_ITTXFE) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_ITTXFE), (uint16_t)diepint); stm32_putreg(OTGFS_DIEPINT_ITTXFE, STM32_OTGFS_DIEPINT(epno)); } /* IN endpoint NAK effective */ if ((diepint & OTGFS_DIEPINT_INEPNE) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_INEPNE), (uint16_t)diepint); stm32_putreg(OTGFS_DIEPINT_INEPNE, STM32_OTGFS_DIEPINT(epno)); } /* Endpoint disabled interrupt */ if ((diepint & OTGFS_DIEPINT_EPDISD) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_EPDISD), (uint16_t)diepint); stm32_putreg(OTGFS_DIEPINT_EPDISD, STM32_OTGFS_DIEPINT(epno)); } /* Transmit FIFO empty */ if ((diepint & OTGFS_DIEPINT_TXFE) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_TXFE), (uint16_t)diepint); stm32_txfifoempty(priv, epno); stm32_putreg(OTGFS_DIEPINT_TXFE, STM32_OTGFS_DIEPINT(epno)); } } epno++; daint >>= 1; } } /******************************************************************************* * Name: stm32_setaddress * * Description: * Set the devices USB address * *******************************************************************************/ static inline void stm32_setaddress(struct stm32_usbdev_s *priv, uint16_t address) { uint32_t regval; /* Set the device address in the DCFG register */ regval = stm32_getreg(STM32_OTGFS_DCFG); regval &= ~OTGFS_DCFG_DAD_MASK; regval |= ((uint32_t)address << OTGFS_DCFG_DAD_SHIFT); stm32_putreg(regval, STM32_OTGFS_DCFG); /* Are we now addressed? (i.e., do we have a non-NULL device * address?) */ if (address != 0) { priv->devstate = DEVSTATE_ADDRESSED; priv->addressed = true; } else { priv->devstate = DEVSTATE_DEFAULT; priv->addressed = false; } } /******************************************************************************* * Name: stm32_testmode * * Description: * Select test mode * *******************************************************************************/ static inline void stm32_testmode(FAR struct stm32_usbdev_s *priv, uint16_t index) { uint32_t regval; uint8_t testmode; regval = stm32_getreg(STM32_OTGFS_DCTL); testmode = index >> 8; switch (testmode) { case 1: priv->testmode = OTGFS_TESTMODE_J; break; case 2: priv->testmode = OTGFS_TESTMODE_K; break; case 3: priv->testmode = OTGFS_TESTMODE_SE0_NAK; break; case 4: priv->testmode = OTGFS_TESTMODE_PACKET; break; case 5: priv->testmode = OTGFS_TESTMODE_FORCE; break; default: usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADTESTMODE), testmode); priv->dotest = false; priv->testmode = OTGFS_TESTMODE_DISABLED; priv->stalled = true; } priv->dotest = true; stm32_ep0nullpacket(priv); } /******************************************************************************* * Name: stm32_stdrequest * * Description: * Handle a stanard request on EP0. Pick off the things of interest to the * USB device controller driver; pass what is left to the class driver. * *******************************************************************************/ static inline void stm32_stdrequest(struct stm32_usbdev_s *priv, FAR struct stm32_ctrlreq_s *ctrlreq) { FAR struct stm32_ep_s *privep; /* Handle standard request */ switch (ctrlreq->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), 0); if (!priv->addressed || ctrlreq->len != 2 || (ctrlreq->type & USB_REQ_DIR_IN) == 0 || ctrlreq->value != 0) { priv->stalled = true; } else { switch (ctrlreq->type & USB_REQ_RECIPIENT_MASK) { case USB_REQ_RECIPIENT_ENDPOINT: { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPGETSTATUS), 0); privep = stm32_epfindbyaddr(priv, ctrlreq->index); if (!privep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS), 0); priv->stalled = true; } else { if (privep->stalled) { priv->ep0resp[0] = (1 << USB_FEATURE_ENDPOINTHALT); } else { priv->ep0resp[0] = 0; /* Not stalled */ } priv->ep0resp[1] = 0; stm32_ep0xfer(EP0, priv->ep0resp, 2); priv->ep0state = EP0STATE_SHORTWRITE; } } break; case USB_REQ_RECIPIENT_DEVICE: { if (ctrlreq->index == 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVGETSTATUS), 0); /* Features: Remote Wakeup and selfpowered */ priv->ep0resp[0] = (priv->selfpowered << USB_FEATURE_SELFPOWERED); priv->ep0resp[0] |= (priv->wakeup << USB_FEATURE_REMOTEWAKEUP); priv->ep0resp[1] = 0; stm32_ep0xfer(EP0, priv->ep0resp, 2); priv->ep0state = EP0STATE_SHORTWRITE; } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADDEVGETSTATUS), 0); priv->stalled = true; } } break; case USB_REQ_RECIPIENT_INTERFACE: { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IFGETSTATUS), 0); priv->ep0resp[0] = 0; priv->ep0resp[1] = 0; stm32_ep0xfer(EP0, priv->ep0resp, 2); priv->ep0state = EP0STATE_SHORTWRITE; } break; default: { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSTATUS), 0); priv->stalled = true; } 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), 0); if (priv->addressed != 0 && ctrlreq->len == 0) { uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK; if (recipient == USB_REQ_RECIPIENT_ENDPOINT && ctrlreq->value == USB_FEATURE_ENDPOINTHALT && (privep = stm32_epfindbyaddr(priv, ctrlreq->index)) != NULL) { stm32_epclrstall(privep); stm32_ep0nullpacket(priv); priv->ep0state = EP0STATE_NAK_IN; } else if (recipient == USB_REQ_RECIPIENT_DEVICE && ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) { priv->wakeup = 0; stm32_ep0nullpacket(priv); } else { /* Actually, I think we could just stall here. */ (void)stm32_dispatchrequest(priv, &priv->ctrlreq); } } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADCLEARFEATURE), 0); priv->stalled = true; } } 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), 0); if (priv->addressed != 0 && ctrlreq->len == 0) { uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK; if (recipient == USB_REQ_RECIPIENT_ENDPOINT && ctrlreq->value == USB_FEATURE_ENDPOINTHALT && (privep = stm32_epfindbyaddr(priv, ctrlreq->index)) != NULL) { stm32_epsetstall(privep); stm32_ep0nullpacket(priv); priv->ep0state = EP0STATE_NAK_IN; } else if (recipient == USB_REQ_RECIPIENT_DEVICE && ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) { priv->wakeup = 1; stm32_ep0nullpacket(priv); } else if (recipient == USB_REQ_RECIPIENT_DEVICE && ctrlreq->value == USB_FEATURE_TESTMODE && ((ctrlreq->index & 0xff) == 0)) { stm32_testmode(priv, ctrlreq->index); } else if (priv->configured) { /* Actually, I think we could just stall here. */ (void)stm32_dispatchrequest(priv, &priv->ctrlreq); } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0); priv->stalled = true; } } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0); priv->stalled = true; } } 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_SETADDRESS), ctrlreq->value); if ((ctrlreq->type &USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && ctrlreq->index == 0 && ctrlreq->len == 0 && ctrlreq->value < 128 && priv->devstate != DEVSTATE_CONFIGURED) { /* Save the address. We cannot actually change to the next address until * the completion of the status phase. */ stm32_setaddress(priv, (uint16_t)priv->ctrlreq.value[0]); stm32_ep0nullpacket(priv); priv->ep0state = EP0STATE_NAK_IN; } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETADDRESS), 0); priv->stalled = true; } } 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), 0); if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) { (void)stm32_dispatchrequest(priv, &priv->ctrlreq); } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSETDESC), 0); priv->stalled = true; } } 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), 0); if (priv->addressed && (ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && ctrlreq->value == 0 && ctrlreq->index == 0 && ctrlreq->len == 1) { (void)stm32_dispatchrequest(priv, &priv->ctrlreq); } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETCONFIG), 0); priv->stalled = true; } } 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), 0); if (priv->addressed && (ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && ctrlreq->index == 0 && ctrlreq->len == 0) { /* Give the configuration to the class driver */ int ret = stm32_dispatchrequest(priv, &priv->ctrlreq); /* If the class driver accepted the configuration, then mark the * device state as configured (or not, depending on the * configuration). */ if (ret == OK) { uint8_t cfg = (uint8_t)ctrlreq->value; if (cfg != 0) { priv->devstate = DEVSTATE_CONFIGURED; priv->configured = true; } else { priv->devstate = DEVSTATE_ADDRESSED; priv->configured = false; } } } else { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETCONFIG), 0); priv->stalled = true; } } 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 */ { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETIF), 0); (void)stm32_dispatchrequest(priv, &priv->ctrlreq); } 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), 0); priv->stalled = true; } break; } } /******************************************************************************* * Name: stm32_ep0setup * * Description: * USB Ctrl EP Setup Event. This is logically part of the USB interrupt * handler. This event occurs when a setup packet is receive on EP0 OUT. * *******************************************************************************/ static inline void stm32_ep0setup(struct stm32_usbdev_s *priv) { struct stm32_ctrlreq_s ctrlreq; /* Terminate any pending requests - since all DTDs will have been retired * because of the setup packet. */ stm32_cancelrequests(&priv->epout[EP0], -EPROTO); stm32_cancelrequests(&priv->epin[EP0], -EPROTO); /* Assume NOT stalled */ priv->epout[EP0].stalled = false; priv->epin[EP0].stalled = false; priv->stalled = false; /* Read EP0 setup data */ stm32_ep0read((FAR uint8_t*)&ctrlreq, USB_SIZEOF_CTRLREQ); /* Starting a control request - update state */ priv->ep0state = (priv->ctrlreq.type & USB_REQ_DIR_IN) ? EP0STATE_SETUP_IN : EP0STATE_SETUP_OUT; /* And extract the little-endian 16-bit values to host order */ ctrlreq.type = priv->ctrlreq.type; ctrlreq.req = priv->ctrlreq.req; ctrlreq.value = GETUINT16(priv->ctrlreq.value); ctrlreq.index = GETUINT16(priv->ctrlreq.index); ctrlreq.len = GETUINT16(priv->ctrlreq.len); ullvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", ctrlreq.type, ctrlreq.req, ctrlreq.value, ctrlreq.index, ctrlreq.len); /* Check for a standard request */ if ((ctrlreq.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) { /* Dispatch any non-standard requests */ (void)stm32_dispatchrequest(priv, &priv->ctrlreq); } else { /* Handle standard requests. */ stm32_stdrequest(priv, &ctrlreq); } if (priv->stalled) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED), priv->ep0state); stm32_ep0stall(priv); } } /******************************************************************************* * Name: stm32_epout * * Description: * This is part of the OUT endpoint interrupt processing. This function * handles the OUT event for a single endpoint. * *******************************************************************************/ static inline void stm32_epout(FAR struct stm32_usbdev_s *priv, uint8_t epno) { FAR struct stm32_ep_s *privep; /* Endpoint 0 is a special case. */ if (epno == 0) { privep = &priv->epout[EP0]; /* In the EP0STATE_DATA_OUT state, we are receiving data from request * buffer. In that case, we must continue the request processing. */ if (priv->ep0state == EP0STATE_DATA_OUT) { /* Continue processing data from the EP0 OUT request queue */ (void)stm32_rdrequest(priv, privep); } /* If we are not actively processing an OUT request, then we * need to setup to receive the next control request. */ if (!privep->active) { priv->ep0state = EP0STATE_STATUS_OUT; stm32_rxsetup(priv, privep, NULL, 0); stm32_ep0outstart(priv); } } /* For other endpoints, the only possibility is that we are continuing * or finishing an OUT request. */ else if (priv->devstate == DEVSTATE_CONFIGURED) { (void)stm32_rdrequest(priv, &priv->epout[epno]); } } /******************************************************************************* * Name: stm32_epoutinterrupt * * Description: * USB OUT endpoint interrupt handler * *******************************************************************************/ static inline void stm32_epoutinterrupt(FAR struct stm32_usbdev_s *priv) { uint32_t daint; uint32_t regval; uint32_t doepint; int epno; /* Get the pending, enabled interrupts for the OUT endpoint from the endpoint * interrupt status register. */ regval = stm32_getreg(STM32_OTGFS_DAINT); regval &= stm32_getreg(STM32_OTGFS_DAINTMSK); daint = (regval & OTGFS_DAINT_OEP_MASK) >> OTGFS_DAINT_OEP_SHIFT; /* Process each pending IN endpoint interrupt */ epno = 0; while (daint) { /* Is an OUT interrupt pending for this endpoint? */ if ((daint & 1) != 0) { /* Yes.. get the OUT endpoint interrupt status */ doepint = stm32_getreg(STM32_OTGFS_DOEPINT(epno)); doepint &= stm32_getreg(STM32_OTGFS_DOEPMSK); /* Transfer completed interrupt */ if ((doepint & OTGFS_DOEPINT_XFRC) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_XFRC), (uint16_t)diepint); /* Clear the bit in DOEPINTn for this interrupt */ stm32_putreg(OTGFS_DOEPINT_XFRC, STM32_OTGFS_DOEPINT(epno)); /* Handle the RX transer data ready event */ stm32_epout(priv, epno); } /* Endpoint disabled interrupt */ if ((doepint & OTGFS_DOEPINT_EPDISD) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_EPDISD), (uint16_t)diepint); /* Clear the bit in DOEPINTn for this interrupt */ stm32_putreg(OTGFS_DOEPINT_EPDISD, STM32_OTGFS_DOEPINT(epno)); } /* Setup Phase Done (control EPs) */ if ((doepint & OTGFS_DOEPINT_SETUP) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_SETUP), (uint16_t)diepint); /* Handle the receipt of the SETUP packet */ stm32_ep0setup(priv); stm32_putreg(OTGFS_DOEPINT_SETUP, STM32_OTGFS_DOEPINT(epno)); } } epno++; daint >>= 1; } } /******************************************************************************* * Name: stm32_isocininterrupt * * Description: * Enumeration done interrupt * *******************************************************************************/ static inline void stm32_enuminterrupt(FAR struct stm32_usbdev_s *priv) { uint32_t regval; /* Activate EP0 */ stm32_ep0activate(priv); /* Set USB turn-around time for the full speed device with internal PHY interface. */ regval = stm32_getreg(STM32_OTGFS_GUSBCFG); regval &= ~OTGFS_GUSBCFG_TRDT_MASK; regval |= OTGFS_GUSBCFG_TRDT(5); stm32_putreg(regval, STM32_OTGFS_GUSBCFG); } /******************************************************************************* * Name: stm32_isocininterrupt * * Description: * Incomplete isochronous IN transfer interrupt * *******************************************************************************/ #ifdef CONFIG_USBDEV_ISOCHRONOUS static inline void stm32_isocininterrupt(FAR struct stm32_usbdev_s *priv) { #warning "Missing logic" } #endif /******************************************************************************* * Name: stm32_isocoutinterrupt * * Description: * Incomplete periodic transfer interrupt * *******************************************************************************/ #ifdef CONFIG_USBDEV_ISOCHRONOUS static inline void stm32_isocoutinterrupt(FAR struct stm32_usbdev_s *priv) { #warning "Missing logic" } #endif /******************************************************************************* * Name: stm32_usbinterrupt * * Description: * USB interrupt handler * *******************************************************************************/ static int stm32_usbinterrupt(int irq, FAR void *context) { /* At present, there is only a single OTG FS device support. Hence it is * pre-allocated as g_otgfsdev. However, in most code, the private data * structure will be referenced using the 'priv' pointer (rather than the * global data) in order to simplify any future support for multiple devices. */ FAR struct stm32_usbdev_s *priv = &g_otgfsdev; uint32_t regval; /* Assure that we are in device mode */ DEBUGASSERT((stm32_getreg(STM32_OTGFS_GINTSTS) & OTGFS_GINTSTS_CMOD) == OTGFS_GINTSTS_DEVMODE); /* Get the state of all enabled interrupts */ regval = stm32_getreg(STM32_OTGFS_GINTSTS); regval &= stm32_getreg(STM32_OTGFS_GINTMSK); usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_USB), (uint16_t)regval); /* OUT endpoint interrupt */ if ((regval & OTGFS_GINT_OEP) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT), (uint16_t)regval); (void)stm32_epoutinterrupt(priv); } /* IN endpoint interrupt */ if ((regval & OTGFS_GINT_IEP) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN), (uint16_t)regval); stm32_epininterrupt(priv); } /* Mode mismatch interrupt */ #ifdef CONFIG_DEBUG_USB if ((regval & OTGFS_GINT_MMIS) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_MISMATCH), (uint16_t)regval); stm32_putreg(OTGFS_GINT_MMIS, STM32_OTGFS_GINTSTS); } #endif /* Resume/remote wakeup detected interrupt */ if ((regval & OTGFS_GINT_WKUP) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_WAKEUP), (uint16_t)regval); (void)stm32_resumeinterrupt(priv); } /* USB suspend interrupt */ if ((regval & OTGFS_GINT_USBSUSP) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SUSPEND), (uint16_t)regval); (void)stm32_suspendinterrupt(priv); } /* Start of frame interrupt */ #ifdef CONFIG_USBDEV_SOFINTERRUPT if ((regval & OTGFS_GINT_SOF) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SOF), (uint16_t)regval); stm32_putreg(OTGFS_GINT_SOF, STM32_OTGFS_GINTSTS); } #endif /* RxFIFO non-empty interrupt */ if ((regval & OTGFS_GINT_RXFLVL) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_RXFIFO), (uint16_t)regval); (void)stm32_rxinterrupt(priv); } /* USB reset interrupt */ if ((regval & OTGFS_GINT_USBRST) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVRESET), (uint16_t)regval); stm32_usbreset(priv); usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_USB), 0); return OK; } /* Enumeration done interrupt */ if ((regval & OTGFS_GINT_ENUMDNE) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_ENUMDNE), (uint16_t)regval); (void)stm32_enuminterrupt(priv); stm32_putreg(OTGFS_GINT_ENUMDNE, STM32_OTGFS_GINTSTS); } /* Incomplete isochronous IN transfer interrupt */ #ifdef CONFIG_USBDEV_ISOCHRONOUS if ((regval & OTGFS_GINT_IISOIXFR) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IISOIXFR), (uint16_t)regval); stm32_isocininterrupt(priv); stm32_putreg(OTGFS_GINT_IISOIXFR, STM32_OTGFS_GINTSTS); } /* Incomplete periodic transfer interrupt*/ if ((regval & OTGFS_GINT_IPXFR) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IPXFR), (uint16_t)regval); stm32_isocoutinterrupt(priv); stm32_putreg(OTGFS_GINT_IPXFR, STM32_OTGFS_GINTSTS); } #endif /* Session request/new session detected interrupt */ #ifdef CONFIG_USBDEV_VBUSSENSING if ((regval & OTGFS_GINT_SRQ) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SRQ), (uint16_t)regval); (void)stm32_sessioninterrupt(priv); } /* OTG interrupt */ if ((regval & OTGFS_GINT_OTG) != 0) { usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OTG), (uint16_t)regval); (void)stm32_otginterrupt(priv); } #endif usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_USB), 0); return OK; } /******************************************************************************* * Endpoint operations *******************************************************************************/ /******************************************************************************* * Name: stm32_epconfigure * * Description: * Configure endpoint, making it usable * * Input Parameters: * ep - the struct usbdev_ep_s instance obtained from allocep() * desc - A struct usb_epdesc_s instance describing the endpoint * last - true if this this last endpoint to be configured. Some hardware * needs to take special action when all of the endpoints have been * configured. * *******************************************************************************/ static int stm32_epconfigure(FAR struct usbdev_ep_s *ep, FAR const struct usb_epdesc_s *desc, bool last) { FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; uint16_t maxsize; usbtrace(TRACE_EPCONFIGURE, privep->epphy); DEBUGASSERT(desc->addr == ep->eplog); /* Initialise EP capabilities */ maxsize = GETUINT16(desc->mxpacketsize); #warning "Missing Logic" /* Setup Endpoint Control Register */ if (privep->isin) { /* Reset the data toggles */ #warning "Missing logic" /* Set the endpoint type */ switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK) { case USB_EP_ATTR_XFER_CONTROL: break; case USB_EP_ATTR_XFER_ISOC: break; case USB_EP_ATTR_XFER_BULK: break; case USB_EP_ATTR_XFER_INT: break; } #warning "Missing logic" } else { /* Reset the data toggles */ #warning "Missing logic" /* Set the endpoint type */ switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK) { case USB_EP_ATTR_XFER_CONTROL: break; case USB_EP_ATTR_XFER_ISOC: break; case USB_EP_ATTR_XFER_BULK: break; case USB_EP_ATTR_XFER_INT: break; } #warning "Missing logic" } /* Reset endpoint status */ privep->stalled = false; /* Enable the endpoint */ if (privep->isin) { #warning "Missing logic" } else { #warning "Missing logic" } return OK; } /******************************************************************************* * Name: stm32_epdisable * * Description: * The endpoint will no longer be used * *******************************************************************************/ static int stm32_epdisable(FAR struct usbdev_ep_s *ep) { FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; irqstate_t flags; #ifdef CONFIG_DEBUG if (!ep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -EINVAL; } #endif usbtrace(TRACE_EPDISABLE, privep->epphy); flags = irqsave(); /* Disable Endpoint */ if (privep->isin) { #warning "Missing logic" } else { #warning "Missing logic" } privep->stalled = true; /* Cancel any ongoing activity */ stm32_cancelrequests(privep, -ESHUTDOWN); irqrestore(flags); return OK; } /******************************************************************************* * Name: stm32_epallocreq * * Description: * Allocate an I/O request * *******************************************************************************/ static FAR struct usbdev_req_s *stm32_epallocreq(FAR struct usbdev_ep_s *ep) { FAR struct stm32_req_s *privreq; #ifdef CONFIG_DEBUG if (!ep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return NULL; } #endif usbtrace(TRACE_EPALLOCREQ, ((FAR struct stm32_ep_s *)ep)->epphy); privreq = (FAR 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 * * Description: * Free an I/O request * *******************************************************************************/ static void stm32_epfreereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { FAR struct stm32_req_s *privreq = (FAR struct stm32_req_s *)req; #ifdef CONFIG_DEBUG if (!ep || !req) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return; } #endif usbtrace(TRACE_EPFREEREQ, ((FAR struct stm32_ep_s *)ep)->epphy); free(privreq); } /******************************************************************************* * Name: stm32_epallocbuffer * * Description: * Allocate an I/O buffer * *******************************************************************************/ #ifdef CONFIG_ARCH_USBDEV_DMA static void *stm32_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes) { usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); return malloc(bytes) } #endif /******************************************************************************* * Name: stm32_epfreebuffer * * Description: * Free an I/O buffer * *******************************************************************************/ #ifdef CONFIG_LPC313x_USBDEV_DMA static void stm32_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf) { usbtrace(TRACE_EPFREEBUFFER, privep->epphy); free(buf); } #endif /******************************************************************************* * Name: stm32_epsubmit * * Description: * Submit an I/O request to the endpoint * *******************************************************************************/ static int stm32_epsubmit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { FAR struct stm32_req_s *privreq = (FAR struct stm32_req_s *)req; FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; FAR struct stm32_usbdev_s *priv; irqstate_t flags; int ret = OK; #ifdef CONFIG_DEBUG if (!req || !req->callback || !req->buf || !ep) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); ullvdbg("req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf, ep); return -EINVAL; } #endif usbtrace(TRACE_EPSUBMIT, privep->epphy); priv = privep->dev; if (!priv->driver || priv->usbdev.speed == USB_SPEED_UNKNOWN) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOTCONFIGURED), priv->usbdev.speed); return -ESHUTDOWN; } /* Handle the request from the class driver */ req->result = -EINPROGRESS; req->xfrd = 0; /* Disable Interrupts */ flags = irqsave(); /* If we are stalled, then drop all requests on the floor */ if (privep->stalled) { ret = -EBUSY; } else { /* Add the new request to the request queue for the endpoint */ if (stm32_addlast(privep, privreq)) { if (privep->isin) { usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); stm32_wrrequest(priv, privep); } else { usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len); stm32_rdrequest(priv, privep); } } } irqrestore(flags); return ret; } /******************************************************************************* * Name: stm32_epcancel * * Description: * Cancel an I/O request previously sent to an endpoint * *******************************************************************************/ static int stm32_epcancel(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; FAR 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, privep->epphy); priv = privep->dev; flags = irqsave(); /* FIXME: if the request is the first, then we need to flush the EP * otherwise just remove it from the list * * but ... all other implementations cancel all requests ... */ stm32_cancelrequests(privep, -ESHUTDOWN); irqrestore(flags); return OK; } /******************************************************************************* * Name: stm32_epsetstall * * Description: * Stall an endpoint * *******************************************************************************/ static int stm32_epsetstall(FAR struct stm32_ep_s *privep) { uint32_t regaddr; uint32_t regval; usbtrace(TRACE_EPSTALL, privep->epphy); /* Is this an IN endpoint? */ if (privep->isin == 1) { /* Get the IN endpoint device control register */ regaddr = STM32_OTGFS_DIEPCTL(privep->epphy); regval = stm32_getreg(regaddr); /* Is the endpoint enabled? */ if ((regval & OTGFS_DIEPCTL_EPENA) != 0) { /* Yes.. the endpoint is enabled, disable it */ regval = OTGFS_DIEPCTL_EPDIS; } else { regval = 0; } /* Then stall the endpoint */ regval |= OTGFS_DIEPCTL_STALL; stm32_putreg(regval, regaddr); } else { /* Get the OUT endpoint device control register */ regaddr = STM32_OTGFS_DOEPCTL(privep->epphy); regval = stm32_getreg(regaddr); /* Then stall the endpoint */ regval |= OTGFS_DOEPCTL_STALL; stm32_putreg(regval, regaddr); } /* The endpoint is now stalled */ privep->stalled = true; return OK; } /******************************************************************************* * Name: stm32_epclrstall * * Description: * Resume a stalled endpoint * *******************************************************************************/ static int stm32_epclrstall(FAR struct stm32_ep_s *privep) { uint32_t regaddr; uint32_t regval; uint32_t stallbit; uint32_t data0bit; usbtrace(TRACE_EPRESUME, privep->epphy); /* Is this an IN endpoint? */ if (privep->isin == 1) { /* Clear the stall bit in the IN endpoint device control register */ regaddr = STM32_OTGFS_DIEPCTL(privep->epphy); stallbit = OTGFS_DIEPCTL_STALL; data0bit = OTGFS_DIEPCTL_SD0PID; } else { /* Clear the stall bit in the IN endpoint device control register */ regaddr = STM32_OTGFS_DOEPCTL(privep->epphy); stallbit = OTGFS_DOEPCTL_STALL; data0bit = OTGFS_DOEPCTL_SD0PID; } /* Clear the stall bit */ regval = stm32_getreg(regaddr); regval &= ~stallbit; /* Set the DATA0 pid for interrupt and bulk endpoints */ if (privep->eptype == USB_EP_ATTR_XFER_INT || privep->eptype == USB_EP_ATTR_XFER_BULK) { /* Writing this bit sets the DATA0 PID */ regval |= data0bit; } stm32_putreg(regval, regaddr); /* The endpoint is no longer stalled */ privep->stalled = false; return OK; } /******************************************************************************* * Name: stm32_epstall * * Description: * Stall or resume an endpoint * *******************************************************************************/ static int stm32_epstall(FAR struct usbdev_ep_s *ep, bool resume) { FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; irqstate_t flags; int ret; /* Set or clear the stall condition as requested */ flags = irqsave(); if (resume) { ret = stm32_epclrstall(privep); } else { ret = stm32_epsetstall(privep); } irqrestore(flags); return ret; } /******************************************************************************* * Name: stm32_ep0stall * * Description: * Stall endpoint 0 * *******************************************************************************/ static void stm32_ep0stall(FAR struct stm32_usbdev_s *priv) { stm32_epsetstall(&priv->epin[EP0]); stm32_epsetstall(&priv->epout[EP0]); priv->stalled = true; stm32_ep0outstart(priv); } /******************************************************************************* * Device operations *******************************************************************************/ /******************************************************************************* * Name: stm32_allocep * * Description: * Allocate an endpoint matching the parameters. * * Input Parameters: * eplog - 7-bit logical endpoint number (direction bit ignored). Zero means * that any endpoint matching the other requirements will suffice. The * assigned endpoint can be found in the eplog field. * in - true: IN (device-to-host) endpoint requested * eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK, * USB_EP_ATTR_XFER_INT} * *******************************************************************************/ static FAR struct usbdev_ep_s *stm32_allocep(FAR struct usbdev_s *dev, uint8_t eplog, bool in, uint8_t eptype) { FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev; uint32_t epset = STM32_EPALLSET & ~STM32_EPCTRLSET; irqstate_t flags; int epndx = 0; usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog); /* Ignore any direction bits in the logical address */ eplog = USB_EPNO(eplog); /* A logical address of 0 means that any endpoint will do */ if (eplog > 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 (eplog >= STM32_NENDPOINTS) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16_t)eplog); 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 &= 3 << (eplog << 1); } /* Get the subset matching the requested direction */ if (in) { epset &= STM32_EPINSET; } else { epset &= STM32_EPOUTSET; } /* Get the subset matching the requested type */ switch (eptype) { case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ epset &= STM32_EPINTRSET; break; case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ epset &= STM32_EPBULKSET; break; case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ epset &= STM32_EPISOCSET; break; case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */ default: usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPTYPE), (uint16_t)eptype); return NULL; } /* Is the resulting endpoint supported by the STM32? */ if (epset) { /* Yes.. now see if any of the request endpoints are available */ flags = irqsave(); epset &= priv->epavail; if (epset) { /* Select the lowest bit in the set of matching, available endpoints */ for (epndx = 2; epndx < STM32_NENDPOINTS; epndx++) { uint32_t bit = 1 << epndx; if ((epset & bit) != 0) { /* Mark the IN/OUT endpoint no longer available */ priv->epavail &= ~(3 << (bit & ~1)); irqrestore(flags); /* And return the pointer to the standard endpoint structure */ return &priv->epin[epndx].ep; } } /* Shouldn't get here */ } irqrestore(flags); } usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOEP), (uint16_t)eplog); return NULL; } /******************************************************************************* * Name: stm32_freeep * * Description: * Free the previously allocated endpoint * *******************************************************************************/ static void stm32_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep) { FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev; FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; irqstate_t flags; usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy); if (priv && privep) { /* Mark the endpoint as available */ flags = irqsave(); priv->epavail |= (1 << privep->epphy); irqrestore(flags); } } /******************************************************************************* * Name: stm32_getframe * * Description: * Returns the current frame number * *******************************************************************************/ static int stm32_getframe(struct usbdev_s *dev) { /* Return the last frame number detected by the hardware */ usbtrace(TRACE_DEVGETFRAME, 0); #warning "Missing logic" return 0; } /******************************************************************************* * Name: stm32_wakeup * * Description: * Tries to wake up the host connected to this device * *******************************************************************************/ static int stm32_wakeup(struct usbdev_s *dev) { irqstate_t flags; usbtrace(TRACE_DEVWAKEUP, 0); flags = irqsave(); #warning "Missing logic" irqrestore(flags); return OK; } /******************************************************************************* * Name: stm32_selfpowered * * Description: * Sets/clears the device selfpowered feature * *******************************************************************************/ static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered) { FAR struct stm32_usbdev_s *priv = (FAR 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; } /******************************************************************************* * Name: stm32_pullup * * Description: * Software-controlled connect to/disconnect from USB host * *******************************************************************************/ static int stm32_pullup(struct usbdev_s *dev, bool enable) { usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); irqstate_t flags = irqsave(); if (enable) { #warning "Missing logic" } else { #warning "Missing logic" } irqrestore(flags); return OK; } /******************************************************************************* * Name: stm32_flushtxfifo * * Description: * Flush the specific TX fifo. * *******************************************************************************/ static int stm32_flushtxfifo(uint32_t txfnum) { uint32_t regval; uint32_t timeout; /* Initiate the TX FIFO flush operation */ regval = OTGFS_GRSTCTL_TXFFLSH | txfnum; stm32_putreg(regval, STM32_OTGFS_GRSTCTL); /* Wait for the FLUSH to complete */ for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) { regval = stm32_getreg(STM32_OTGFS_GRSTCTL); if ((regval & OTGFS_GRSTCTL_TXFFLSH) == 0) { break; } } /* Wait for 3 PHY Clocks */ up_udelay(3); return OK; } /******************************************************************************* * Name: stm32_flushrxfifo * * Description: * Flush the RX fifo. * *******************************************************************************/ static int stm32_flushrxfifo(void) { uint32_t regval; uint32_t timeout; /* Initiate the RX FIFO flush operation */ stm32_putreg(OTGFS_GRSTCTL_RXFFLSH, STM32_OTGFS_GRSTCTL); /* Wait for the FLUSH to complete */ for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) { regval = stm32_getreg(STM32_OTGFS_GRSTCTL); if ((regval & OTGFS_GRSTCTL_RXFFLSH) == 0) { break; } } /* Wait for 3 PHY Clocks */ up_udelay(3); return OK; } /******************************************************************************* * Name: stm32_swinitialize * * Description: * Initialize all driver data structures. * *******************************************************************************/ static void stm32_swinitialize(FAR struct stm32_usbdev_s *priv) { FAR struct stm32_ep_s *privep; int i; /* Initialize the device state structure */ memset(priv, 0, sizeof(struct stm32_usbdev_s)); priv->usbdev.ops = &g_devops; priv->usbdev.ep0 = &priv->epin[EP0].ep; priv->epavail = STM32_EPALLSET; /* Initialize the endpoint lists */ for (i = 0; i < STM32_NENDPOINTS; i++) { /* 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). */ privep = &priv->epin[i]; privep->ep.ops = &g_epops; privep->dev = priv; privep->isin = 1; /* The index, i, is the physical endpoint address; Map this * to a logical endpoint address usable by the class driver. */ privep->epphy = i; privep->ep.eplog = STM32_EPPHYIN2LOG(i); /* Control until endpoint is activated */ privep->eptype = USB_EP_ATTR_XFER_CONTROL; privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE; } /* Initialize the endpoint lists */ for (i = 0; i < STM32_NENDPOINTS; i++) { /* 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). */ privep = &priv->epout[i]; privep->ep.ops = &g_epops; privep->dev = priv; /* The index, i, is the physical endpoint address; Map this * to a logical endpoint address usable by the class driver. */ privep->epphy = i; privep->ep.eplog = STM32_EPPHYOUT2LOG(i); /* Control until endpoint is activated */ privep->eptype = USB_EP_ATTR_XFER_CONTROL; privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE; } } /******************************************************************************* * Name: stm32_hwinitialize * * Description: * Configure the OTG FS core for operation. * *******************************************************************************/ static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv) { uint32_t regval; uint32_t timeout; uint32_t address; int i; /* At startup the core is in FS mode. */ /* Disable the USB global interrupt by clearing GINTMSK in the global OTG * FS AHB configuration register. */ stm32_putreg(0, STM32_OTGFS_GAHBCFG); /* Common USB OTG core initialization */ /* Reset after a PHY select and set Host mode. First, wait for AHB master * IDLE state. */ for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) { up_udelay(3); regval = stm32_getreg(STM32_OTGFS_GRSTCTL); if ((regval & OTGFS_GRSTCTL_AHBIDL) != 0) { break; } } /* Then perform the core soft reset. */ stm32_putreg(OTGFS_GRSTCTL_CSRST, STM32_OTGFS_GRSTCTL); for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) { regval = stm32_getreg(STM32_OTGFS_GRSTCTL); if ((regval & OTGFS_GRSTCTL_CSRST) == 0) { break; } } /* Wait for 3 PHY Clocks */ up_udelay(3); /* Deactivate the power down */ regval = (OTGFS_GCCFG_PWRDWN | OTGFS_GCCFG_VBUSASEN | OTGFS_GCCFG_VBUSBSEN); #ifndef CONFIG_USBDEV_VBUSSENSING regval |= OTGFS_GCCFG_NOVBUSSENS; #endif #ifdef CONFIG_USBDEV_SOFOUTPUT regval |= OTGFS_GCCFG_SOFOUTEN; #endif stm32_putreg(regval, STM32_OTGFS_GCCFG); up_mdelay(20); /* Force Device Mode */ regval = stm32_getreg(STM32_OTGFS_GUSBCFG); regval &= ~OTGFS_GUSBCFG_FHMOD; regval |= OTGFS_GUSBCFG_FDMOD; stm32_putreg(regval, STM32_OTGFS_GUSBCFG); up_mdelay(50); /* Initialize device mode */ /* Restart the Phy Clock */ stm32_putreg(0, STM32_OTGFS_PCGCCTL); /* Device configuration register */ regval = stm32_getreg(STM32_OTGFS_DCFG); regval &= ~OTGFS_DCFG_PFIVL_MASK; regval |= OTGFS_DCFG_PFIVL_80PCT; stm32_putreg(regval, STM32_OTGFS_DCFG); /* Set full speed phy */ regval = stm32_getreg(STM32_OTGFS_DCFG); regval &= ~OTGFS_DCFG_DSPD_MASK; regval |= OTGFS_DCFG_DSPD_FS; stm32_putreg(regval, STM32_OTGFS_DCFG); /* set Rx FIFO size */ stm32_putreg(CONFIG_USBDEV_RXFIFO_SIZE, STM32_OTGFS_GRXFSIZ); /* EP0 TX */ address = CONFIG_USBDEV_RXFIFO_SIZE; regval = (address << OTGFS_DIEPTXF0_TX0FD_SHIFT) || (CONFIG_USBDEV_EP0_TXFIFO_SIZE << OTGFS_DIEPTXF0_TX0FSA_SHIFT); stm32_putreg(regval, STM32_OTGFS_DIEPTXF0); /* EP1 TX */ address += CONFIG_USBDEV_EP0_TXFIFO_SIZE; regval = (address << OTGFS_DIEPTXF_INEPTXSA_SHIFT) || (CONFIG_USBDEV_EP1_TXFIFO_SIZE << OTGFS_DIEPTXF_INEPTXFD_SHIFT); stm32_putreg(regval, STM32_OTGFS_DIEPTXF1); /* EP2 TX */ address += CONFIG_USBDEV_EP1_TXFIFO_SIZE; regval = (address << OTGFS_DIEPTXF_INEPTXSA_SHIFT) || (CONFIG_USBDEV_EP2_TXFIFO_SIZE << OTGFS_DIEPTXF_INEPTXFD_SHIFT); stm32_putreg(regval, STM32_OTGFS_DIEPTXF2); /* EP3 TX */ address += CONFIG_USBDEV_EP2_TXFIFO_SIZE; regval = (address << OTGFS_DIEPTXF_INEPTXSA_SHIFT) || (CONFIG_USBDEV_EP3_TXFIFO_SIZE << OTGFS_DIEPTXF_INEPTXFD_SHIFT); stm32_putreg(regval, STM32_OTGFS_DIEPTXF3); /* Flush the FIFOs */ stm32_flushtxfifo(OTGFS_GRSTCTL_TXFNUM_DALL); stm32_flushrxfifo(); /* Clear all pending Device Interrupts */ stm32_putreg(0, STM32_OTGFS_DIEPMSK); stm32_putreg(0, STM32_OTGFS_DOEPMSK); stm32_putreg(0xffffffff, STM32_OTGFS_DAINT); stm32_putreg(0, STM32_OTGFS_DAINTMSK); /* Configure all IN endpoints */ for (i = 0; i < STM32_NENDPOINTS; i++) { regval = stm32_getreg(STM32_OTGFS_DIEPCTL(i)); if ((regval & OTGFS_DIEPCTL_EPENA) != 0) { /* The endpoint is already enabled */ regval = OTGFS_DIEPCTL_EPENA | OTGFS_DIEPCTL_SNAK; } else { regval = 0; } stm32_putreg(regval, STM32_OTGFS_DIEPCTL(i)); stm32_putreg(0, STM32_OTGFS_DIEPTSIZ(i)); stm32_putreg(0xff, STM32_OTGFS_DIEPINT(i)); } /* Configure all OUT endpoints */ for (i = 0; i < STM32_NENDPOINTS; i++) { regval = stm32_getreg(STM32_OTGFS_DOEPCTL(i)); if ((regval & OTGFS_DOEPCTL_EPENA) != 0) { /* The endpoint is already enabled */ regval = OTGFS_DOEPCTL_EPENA | OTGFS_DOEPCTL_SNAK; } else { regval = 0; } stm32_putreg(regval, STM32_OTGFS_DOEPCTL(i)); stm32_putreg(0, STM32_OTGFS_DOEPTSIZ(i)); stm32_putreg(0xff, STM32_OTGFS_DOEPINT(i)); } /* Disable all interrupts. */ stm32_putreg(0, STM32_OTGFS_GINTMSK); /* Clear any pending interrupts */ stm32_putreg(0xbfffffff, STM32_OTGFS_GINTSTS); /* Enable the common interrupts */ /* Clear any pending USB_OTG Interrupts */ stm32_putreg(0xffffffff, STM32_OTGFS_GOTGINT); /* Clear any pending interrupts */ stm32_putreg(0xbfffffff, STM32_OTGFS_GINTSTS); /* Enable the interrupts in the INTMSK */ regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_ENUMDNE | OTGFS_GINT_IEP | OTGFS_GINT_OEP | regval); #ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (OTGFS_GINT_IISOIXFR | OTGFS_GINT_IPXFR); #endif #ifdef CONFIG_USBDEV_SOFINTERRUPT regval |= OTGFS_GINT_SOF; #endif #ifdef CONFIG_USBDEV_VBUSSENSING regval |= (OTGFS_GINT_OTG | OTGFS_GINT_SRQ); #endif #ifdef CONFIG_DEBUG_USB regval |= OTGFS_GINT_MMIS; #endif stm32_putreg(regval, STM32_OTGFS_GINTMSK); /* Ensable the USB global interrupt by setting GINTMSK in the global OTG * FS AHB configuration register. */ stm32_putreg(OTGFS_GAHBCFG_GINTMSK, STM32_OTGFS_GAHBCFG); } /******************************************************************************* * Public Functions *******************************************************************************/ /******************************************************************************* * Name: up_usbinitialize * * Description: * Initialize USB hardware. * * Assumptions: * - This function is called very early in the initialization sequence * - PLL and GIO pin initialization is not performed here but should been in * the low-level boot logic: PLL1 must be configured for operation at 48MHz * and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect * LED. * *******************************************************************************/ void up_usbinitialize(void) { /* At present, there is only a single OTG FS device support. Hence it is * pre-allocated as g_otgfsdev. However, in most code, the private data * structure will be referenced using the 'priv' pointer (rather than the * global data) in order to simplify any future support for multiple devices. */ FAR struct stm32_usbdev_s *priv = &g_otgfsdev; int ret; usbtrace(TRACE_DEVINIT, 0); /* Here we assume that: * * 1. GPIOA and OTG FS peripheral clocking has already been enabled as part * of the boot sequence. * 2. Board-specific logic has already enabled other board specific GPIOs * for things like soft pull-up, VBUS sensing, power controls, and over- * current detection. */ /* Configure OTG FS alternate function pins */ stm32_configgpio(GPIO_OTGFS_DM); stm32_configgpio(GPIO_OTGFS_DP); stm32_configgpio(GPIO_OTGFS_ID); stm32_configgpio(GPIO_OTGFS_SOF); /* Uninitialize the hardware so that we know that we are starting from a * known state. */ up_usbuninitialize(); /* Initialie the driver data structure */ stm32_swinitialize(priv); /* Attach the OTG FS interrupt handler */ ret = irq_attach(STM32_IRQ_OTGFS, stm32_usbinterrupt); if (ret < 0) { udbg("irq_attach failed\n", ret); goto errout; } /* Initialize the USB OTG core */ stm32_hwinitialize(priv); /* Disconnect device */ stm32_pullup(&priv->usbdev, false); /* Reset/Re-initialize the USB hardware */ stm32_usbreset(priv); /* Enable USB controller interrupts at the NVIC */ up_enable_irq(STM32_IRQ_OTGFS); /* Set the interrrupt priority */ up_prioritize_irq(STM32_IRQ_OTGFS, CONFIG_OTGFS_PRI); return; errout: up_usbuninitialize(); } /******************************************************************************* * Name: up_usbuninitialize *******************************************************************************/ void up_usbuninitialize(void) { /* At present, there is only a single OTG FS device support. Hence it is * pre-allocated as g_otgfsdev. However, in most code, the private data * structure will be referenced using the 'priv' pointer (rather than the * global data) in order to simplify any future support for multiple devices. */ struct stm32_usbdev_s *priv = &g_otgfsdev; irqstate_t flags; usbtrace(TRACE_DEVUNINIT, 0); if (priv->driver) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0); usbdev_unregister(priv->driver); } /* Disconnect device */ flags = irqsave(); stm32_pullup(&priv->usbdev, false); priv->usbdev.speed = USB_SPEED_UNKNOWN; /* Disable and detach IRQs */ up_disable_irq(STM32_IRQ_OTGFS); irq_detach(STM32_IRQ_OTGFS); /* Reset the controller */ #warning "Missing logic" ; /* Turn off USB power and clocking */ #warning "Missing logic" 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) { 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 (g_otgfsdev.driver) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVER), 0); return -EBUSY; } #endif /* First hook up the driver */ g_otgfsdev.driver = driver; /* Then bind the class driver */ ret = CLASS_BIND(driver, &g_otgfsdev.usbdev); if (ret) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BINDFAILED), (uint16_t)-ret); g_otgfsdev.driver = NULL; } else { /* Enable USB controller interrupts */ up_enable_irq(STM32_IRQ_OTGFS); /* FIXME: nothing seems to call DEV_CONNECT(), but we need to set * the RS bit to enable the controller. It kind of makes sense * to do this after the class has bound to us... * GEN: This bug is really in the class driver. It should make the * soft connect when it is ready to be enumerated. I have added * that logic to the class drivers but left this logic here. */ stm32_pullup(&g_otgfsdev.usbdev, true); } 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) { usbtrace(TRACE_DEVUNREGISTER, 0); #ifdef CONFIG_DEBUG if (driver != g_otgfsdev.driver) { usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); return -EINVAL; } #endif /* Unbind the class driver */ CLASS_UNBIND(driver, &g_otgfsdev.usbdev); /* Disable USB controller interrupts */ up_disable_irq(STM32_IRQ_OTGFS); /* Unhook the driver */ g_otgfsdev.driver = NULL; return OK; } #endif /* CONFIG_USBDEV && CONFIG_STM32_OTGFSDEV */