diff options
Diffstat (limited to 'nuttx/arch/arm/src/stm32/stm32_otgfshost.c')
-rw-r--r-- | nuttx/arch/arm/src/stm32/stm32_otgfshost.c | 4297 |
1 files changed, 0 insertions, 4297 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_otgfshost.c b/nuttx/arch/arm/src/stm32/stm32_otgfshost.c deleted file mode 100644 index 80a9392dc..000000000 --- a/nuttx/arch/arm/src/stm32/stm32_otgfshost.c +++ /dev/null @@ -1,4297 +0,0 @@ -/******************************************************************************* - * arch/arm/src/stm32/stm32_otgfshost.c - * - * Copyright (C) 2012 Gregory Nutt. All rights reserved. - * Authors: Gregory Nutt <gnutt@nuttx.org> - * - * 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 <unistd.h> -#include <semaphore.h> -#include <string.h> -#include <errno.h> -#include <debug.h> - -#include <nuttx/arch.h> -#include <nuttx/kmalloc.h> -#include <nuttx/clock.h> -#include <nuttx/usb/usb.h> -#include <nuttx/usb/usbhost.h> - -#include <arch/irq.h> - -#include "chip.h" /* Includes default GPIO settings */ -#include <arch/board/board.h> /* May redefine GPIO settings */ - -#include "up_arch.h" -#include "up_internal.h" - -#include "stm32_usbhost.h" - -#if defined(CONFIG_USBHOST) && defined(CONFIG_STM32_OTGFS) - -/******************************************************************************* - * Definitions - *******************************************************************************/ -/* Configuration ***************************************************************/ -/* - * STM32 USB OTG FS Host Driver Support - * - * Pre-requisites - * - * CONFIG_USBHOST - Enable general USB host support - * CONFIG_STM32_OTGFS - Enable the STM32 USB OTG FS block - * CONFIG_STM32_SYSCFG - Needed - * - * Options: - * - * CONFIG_STM32_OTGFS_RXFIFO_SIZE - Size of the RX FIFO in 32-bit words. - * Default 128 (512 bytes) - * CONFIG_STM32_OTGFS_NPTXFIFO_SIZE - Size of the non-periodic Tx FIFO - * in 32-bit words. Default 96 (384 bytes) - * CONFIG_STM32_OTGFS_PTXFIFO_SIZE - Size of the periodic Tx FIFO in 32-bit - * words. Default 96 (384 bytes) - * CONFIG_STM32_OTGFS_DESCSIZE - Maximum size of a descriptor. Default: 128 - * CONFIG_STM32_OTGFS_SOFINTR - Enable SOF interrupts. Why would you ever - * want to do that? - * CONFIG_STM32_USBHOST_REGDEBUG - Enable very low-level register access - * debug. Depends on CONFIG_DEBUG. - * CONFIG_STM32_USBHOST_PKTDUMP - Dump all incoming and outgoing USB - * packets. Depends on CONFIG_DEBUG. - */ - -/* Pre-requistites (partial) */ - -#ifndef CONFIG_STM32_SYSCFG -# error "CONFIG_STM32_SYSCFG is required" -#endif - -/* Default RxFIFO size */ - -#ifndef CONFIG_STM32_OTGFS_RXFIFO_SIZE -# define CONFIG_STM32_OTGFS_RXFIFO_SIZE 128 -#endif - -/* Default host non-periodic Tx FIFO size */ - -#ifndef CONFIG_STM32_OTGFS_NPTXFIFO_SIZE -# define CONFIG_STM32_OTGFS_NPTXFIFO_SIZE 96 -#endif - -/* Default host periodic Tx fifo size register */ - -#ifndef CONFIG_STM32_OTGFS_PTXFIFO_SIZE -# define CONFIG_STM32_OTGFS_PTXFIFO_SIZE 96 -#endif - -/* Maximum size of a descriptor */ - -#ifndef CONFIG_STM32_OTGFS_DESCSIZE -# define CONFIG_STM32_OTGFS_DESCSIZE 128 -#endif - -/* Register/packet debug depends on CONFIG_DEBUG */ - -#ifndef CONFIG_DEBUG -# undef CONFIG_STM32_USBHOST_REGDEBUG -# undef CONFIG_STM32_USBHOST_PKTDUMP -#endif - -/* HCD Setup *******************************************************************/ -/* Hardware capabilities */ - -#define STM32_NHOST_CHANNELS 8 /* Number of host channels */ -#define STM32_MAX_PACKET_SIZE 64 /* Full speed max packet size */ -#define STM32_EP0_DEF_PACKET_SIZE 8 /* EP0 default packet size */ -#define STM32_EP0_MAX_PACKET_SIZE 64 /* EP0 FS max packet size */ -#define STM32_MAX_TX_FIFOS 15 /* Max number of TX FIFOs */ -#define STM32_MAX_PKTCOUNT 256 /* Max packet count */ -#define STM32_RETRY_COUNT 3 /* Number of ctrl transfer retries */ -#define STM32_DEF_DEVADDR 0 /* Default device address */ - -/* Delays **********************************************************************/ - -#define STM32_READY_DELAY 200000 /* In loop counts */ -#define STM32_FLUSH_DELAY 200000 /* In loop counts */ -#define STM32_SETUP_DELAY (5000 / MSEC_PER_TICK) /* 5 seconds in system ticks */ -#define STM32_DATANAK_DELAY (5000 / MSEC_PER_TICK) /* 5 seconds in system ticks */ - -/* Ever-present MIN/MAX macros */ - -#ifndef MIN -# define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX -# define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -/******************************************************************************* - * Private Types - *******************************************************************************/ - -/* The following enumeration represents the various states of the USB host - * state machine (for debug purposes only) - */ - -enum stm32_smstate_e -{ - SMSTATE_DETACHED = 0, /* Not attached to a device */ - SMSTATE_ATTACHED, /* Attached to a device */ - SMSTATE_ENUM, /* Attached, enumerating */ - SMSTATE_CLASS_BOUND, /* Enumeration complete, class bound */ -}; - -/* This enumeration provides the reason for the channel halt. */ - -enum stm32_chreason_e -{ - CHREASON_IDLE = 0, /* Inactive (initial state) */ - CHREASON_FREED, /* Channel is no longer in use */ - CHREASON_XFRC, /* Transfer complete */ - CHREASON_NAK, /* NAK received */ - CHREASON_NYET, /* NotYet received */ - CHREASON_STALL, /* Endpoint stalled */ - CHREASON_TXERR, /* Transfer error received */ - CHREASON_DTERR, /* Data toggle error received */ - CHREASON_FRMOR /* Frame overrun */ -}; - -/* This structure retains the state of one host channel. NOTE: Since there - * is only one channel operation active at a time, some of the fields in - * in the structure could be moved in struct stm32_ubhost_s to achieve - * some memory savings. - */ - -struct stm32_chan_s -{ - sem_t waitsem; /* Channel wait semaphore */ - volatile uint8_t result; /* The result of the transfer */ - volatile uint8_t chreason; /* Channel halt reason. See enum stm32_chreason_e */ - uint8_t epno; /* Device endpoint number (0-127) */ - uint8_t eptype; /* See OTGFS_EPTYPE_* definitions */ - uint8_t pid; /* Data PID */ - uint8_t npackets; /* Number of packets (for data toggle) */ - bool inuse; /* True: This channel is "in use" */ - volatile bool indata1; /* IN data toggle. True: DATA01 (Bulk and INTR only) */ - volatile bool outdata1; /* OUT data toggle. True: DATA01 */ - bool in; /* True: IN endpoint */ - volatile bool waiter; /* True: Thread is waiting for a channel event */ - uint16_t maxpacket; /* Max packet size */ - volatile uint16_t buflen; /* Buffer length (remaining) */ - volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */ - FAR uint8_t *buffer; /* Transfer buffer pointer */ -}; - -/* This structure retains the state of the USB host controller */ - -struct stm32_usbhost_s -{ - /* Common device fields. This must be the first thing defined in the - * structure so that it is possible to simply cast from struct usbhost_s - * to structstm32_usbhost_s. - */ - - struct usbhost_driver_s drvr; - - /* The bound device class driver */ - - struct usbhost_class_s *class; - - /* Overall driver status */ - - volatile uint8_t smstate; /* The state of the USB host state machine */ - uint8_t devaddr; /* Device address */ - uint8_t ep0in; /* EP0 IN control channel index */ - uint8_t ep0out; /* EP0 OUT control channel index */ - uint8_t ep0size; /* EP0 max packet size */ - uint8_t chidx; /* ID of channel waiting for space in Tx FIFO */ - bool lowspeed; /* True: low speed device */ - volatile bool connected; /* Connected to device */ - volatile bool eventwait; /* True: Thread is waiting for a port event */ - sem_t exclsem; /* Support mutually exclusive access */ - sem_t eventsem; /* Semaphore to wait for a port event */ - - /* The state of each host channel */ - - struct stm32_chan_s chan[STM32_MAX_TX_FIFOS]; -}; - -/******************************************************************************* - * Private Function Prototypes - *******************************************************************************/ - -/* Register operations ********************************************************/ - -#ifdef CONFIG_STM32_USBHOST_REGDEBUG -static void stm32_printreg(uint32_t addr, uint32_t val, bool iswrite); -static void stm32_checkreg(uint32_t addr, uint32_t val, bool iswrite); -static uint32_t stm32_getreg(uint32_t addr); -static void stm32_putreg(uint32_t addr, uint32_t value); -#else -# define stm32_getreg(addr) getreg32(addr) -# define stm32_putreg(addr,val) putreg32(val,addr) -#endif - -static inline void stm32_modifyreg(uint32_t addr, uint32_t clrbits, - uint32_t setbits); - -#ifdef CONFIG_STM32_USBHOST_PKTDUMP -# define stm32_pktdump(m,b,n) lib_dumpbuffer(m,b,n) -#else -# define stm32_pktdump(m,b,n) -#endif - -/* Semaphores ******************************************************************/ - -static void stm32_takesem(sem_t *sem); -#define stm32_givesem(s) sem_post(s); - -/* Byte stream access helper functions *****************************************/ - -static inline uint16_t stm32_getle16(const uint8_t *val); - -/* Channel management **********************************************************/ - -static int stm32_chan_alloc(FAR struct stm32_usbhost_s *priv); -static inline void stm32_chan_free(FAR struct stm32_usbhost_s *priv, int chidx); -static inline void stm32_chan_freeall(FAR struct stm32_usbhost_s *priv); -static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx); -static void stm32_chan_halt(FAR struct stm32_usbhost_s *priv, int chidx, - enum stm32_chreason_e chreason); -static int stm32_chan_waitsetup(FAR struct stm32_usbhost_s *priv, - FAR struct stm32_chan_s *chan); -static int stm32_chan_wait(FAR struct stm32_usbhost_s *priv, - FAR struct stm32_chan_s *chan); -static void stm32_chan_wakeup(FAR struct stm32_usbhost_s *priv, - FAR struct stm32_chan_s *chan); - -/* Control/data transfer logic *************************************************/ - -static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx); -#if 0 /* Not used */ -static inline uint16_t stm32_getframe(void); -#endif -static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, - FAR const struct usb_ctrlreq_s *req); -static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, - FAR uint8_t *buffer, unsigned int buflen); -static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, - FAR uint8_t *buffer, unsigned int buflen); -static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, - FAR uint8_t *buffer, size_t buflen); -static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, - FAR uint8_t *buffer, size_t buflen); - -/* Interrupt handling **********************************************************/ -/* Lower level interrupt handlers */ - -static void stm32_gint_wrpacket(FAR struct stm32_usbhost_s *priv, - FAR uint8_t *buffer, int chidx, int buflen); -static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv, - int chidx); -static inline void stm32_gint_hcoutisr(FAR struct stm32_usbhost_s *priv, - int chidx); -static void stm32_gint_connected(FAR struct stm32_usbhost_s *priv); -static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv); - -/* Second level interrupt handlers */ - -#ifdef CONFIG_STM32_OTGFS_SOFINTR -static inline void stm32_gint_sofisr(FAR struct stm32_usbhost_s *priv); -#endif -static inline void stm32_gint_rxflvlisr(FAR struct stm32_usbhost_s *priv); -static inline void stm32_gint_nptxfeisr(FAR struct stm32_usbhost_s *priv); -static inline void stm32_gint_ptxfeisr(FAR struct stm32_usbhost_s *priv); -static inline void stm32_gint_hcisr(FAR struct stm32_usbhost_s *priv); -static inline void stm32_gint_hprtisr(FAR struct stm32_usbhost_s *priv); -static inline void stm32_gint_discisr(FAR struct stm32_usbhost_s *priv); -static inline void stm32_gint_iisooxfrisr(FAR struct stm32_usbhost_s *priv); - -/* First level, global interrupt handler */ - -static int stm32_gint_isr(int irq, FAR void *context); - -/* Interrupt controls */ - -static void stm32_gint_enable(void); -static void stm32_gint_disable(void); -static inline void stm32_hostinit_enable(void); -static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx); - -/* USB host controller operations **********************************************/ - -static int stm32_wait(FAR struct usbhost_driver_s *drvr, bool connected); -static int stm32_enumerate(FAR struct usbhost_driver_s *drvr); -static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, - uint16_t maxpacketsize); -static int stm32_epalloc(FAR struct usbhost_driver_s *drvr, - FAR const FAR struct usbhost_epdesc_s *epdesc, - FAR usbhost_ep_t *ep); -static int stm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); -static int stm32_alloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, FAR size_t *maxlen); -static int stm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); -static int stm32_ioalloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, size_t buflen); -static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); -static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, - FAR const struct usb_ctrlreq_s *req, - FAR uint8_t *buffer); -static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, - FAR const struct usb_ctrlreq_s *req, - FAR const uint8_t *buffer); -static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen); -static void stm32_disconnect(FAR struct usbhost_driver_s *drvr); - -/* Initialization **************************************************************/ - -static void stm32_portreset(FAR struct stm32_usbhost_s *priv); -static void stm32_flush_txfifos(uint32_t txfnum); -static void stm32_flush_rxfifo(void); -static void stm32_vbusdrive(FAR struct stm32_usbhost_s *priv, bool state); -static void stm32_host_initialize(FAR struct stm32_usbhost_s *priv); - -static inline void stm32_sw_initialize(FAR struct stm32_usbhost_s *priv); -static inline int stm32_hw_initialize(FAR struct stm32_usbhost_s *priv); - -/******************************************************************************* - * Private Data - *******************************************************************************/ - -/* In this driver implementation, support is provided for only a single a single - * USB device. All status information can be simply retained in a single global - * instance. - */ - -static struct stm32_usbhost_s g_usbhost = -{ - .drvr = - { - .wait = stm32_wait, - .enumerate = stm32_enumerate, - .ep0configure = stm32_ep0configure, - .epalloc = stm32_epalloc, - .epfree = stm32_epfree, - .alloc = stm32_alloc, - .free = stm32_free, - .ioalloc = stm32_ioalloc, - .iofree = stm32_iofree, - .ctrlin = stm32_ctrlin, - .ctrlout = stm32_ctrlout, - .transfer = stm32_transfer, - .disconnect = stm32_disconnect, - }, - .class = NULL, -}; - -/******************************************************************************* - * Public Data - *******************************************************************************/ - -/******************************************************************************* - * Private Functions - *******************************************************************************/ - -/******************************************************************************* - * Name: stm32_printreg - * - * Description: - * Print the contents of an STM32xx register operation - * - *******************************************************************************/ - -#ifdef CONFIG_STM32_USBHOST_REGDEBUG -static void stm32_printreg(uint32_t addr, uint32_t val, bool iswrite) -{ - lldbg("%08x%s%08x\n", addr, iswrite ? "<-" : "->", val); -} -#endif - -/******************************************************************************* - * Name: stm32_checkreg - * - * Description: - * Get the contents of an STM32 register - * - *******************************************************************************/ - -#ifdef CONFIG_STM32_USBHOST_REGDEBUG -static void stm32_checkreg(uint32_t addr, uint32_t val, bool iswrite) -{ - static uint32_t prevaddr = 0; - static uint32_t preval = 0; - static uint32_t count = 0; - static bool prevwrite = false; - - /* Is this the same value that we read from/wrote to the same register last time? - * Are we polling the register? If so, suppress the output. - */ - - if (addr == prevaddr && val == preval && prevwrite == iswrite) - { - /* Yes.. Just increment the count */ - - count++; - } - else - { - /* No this is a new address or value or operation. Were there any - * duplicate accesses before this one? - */ - - if (count > 0) - { - /* Yes.. Just one? */ - - if (count == 1) - { - /* Yes.. Just one */ - - stm32_printreg(prevaddr, preval, prevwrite); - } - else - { - /* No.. More than one. */ - - lldbg("[repeats %d more times]\n", count); - } - } - - /* Save the new address, value, count, and operation for next time */ - - prevaddr = addr; - preval = val; - count = 0; - prevwrite = iswrite; - - /* Show the new regisgter access */ - - stm32_printreg(addr, val, iswrite); - } -} -#endif - -/******************************************************************************* - * Name: stm32_getreg - * - * Description: - * Get the contents of an STM32 register - * - *******************************************************************************/ - -#ifdef CONFIG_STM32_USBHOST_REGDEBUG -static uint32_t stm32_getreg(uint32_t addr) -{ - /* Read the value from the register */ - - uint32_t val = getreg32(addr); - - /* Check if we need to print this value */ - - stm32_checkreg(addr, val, false); - return val; -} -#endif - -/******************************************************************************* - * Name: stm32_putreg - * - * Description: - * Set the contents of an STM32 register to a value - * - *******************************************************************************/ - -#ifdef CONFIG_STM32_USBHOST_REGDEBUG -static void stm32_putreg(uint32_t addr, uint32_t val) -{ - /* Check if we need to print this value */ - - stm32_checkreg(addr, val, true); - - /* Write the value */ - - putreg32(val, addr); -} -#endif - -/******************************************************************************* - * Name: stm32_modifyreg - * - * Description: - * Modify selected bits of an STM32 register. - * - *******************************************************************************/ - -static inline void stm32_modifyreg(uint32_t addr, uint32_t clrbits, uint32_t setbits) -{ - stm32_putreg(addr, (((stm32_getreg(addr)) & ~clrbits) | setbits)); -} - -/**************************************************************************** - * Name: stm32_takesem - * - * Description: - * This is just a wrapper to handle the annoying behavior of semaphore - * waits that return due to the receipt of a signal. - * - *******************************************************************************/ - -static void stm32_takesem(sem_t *sem) -{ - /* Take the semaphore (perhaps waiting) */ - - while (sem_wait(sem) != 0) - { - /* The only case that an error should occr here is if the wait was - * awakened by a signal. - */ - - ASSERT(errno == EINTR); - } -} - -/**************************************************************************** - * Name: stm32_getle16 - * - * Description: - * Get a (possibly unaligned) 16-bit little endian value. - * - *******************************************************************************/ - -static inline uint16_t stm32_getle16(const uint8_t *val) -{ - return (uint16_t)val[1] << 8 | (uint16_t)val[0]; -} - -/******************************************************************************* - * Name: stm32_chan_alloc - * - * Description: - * Allocate a channel. - * - *******************************************************************************/ - -static int stm32_chan_alloc(FAR struct stm32_usbhost_s *priv) -{ - int chidx; - - /* Search the table of channels */ - - for (chidx = 0 ; chidx < STM32_NHOST_CHANNELS ; chidx++) - { - /* Is this channel available? */ - - if (!priv->chan[chidx].inuse) - { - /* Yes... make it "in use" and return the index */ - - priv->chan[chidx].inuse = true; - return chidx; - } - } - - /* All of the channels are "in-use" */ - - return -EBUSY; -} - -/******************************************************************************* - * Name: stm32_chan_free - * - * Description: - * Free a previoiusly allocated channel. - * - *******************************************************************************/ - -static void stm32_chan_free(FAR struct stm32_usbhost_s *priv, int chidx) -{ - DEBUGASSERT((unsigned)chidx < STM32_NHOST_CHANNELS); - - /* Halt the channel */ - - stm32_chan_halt(priv, chidx, CHREASON_FREED); - - /* Mark the channel available */ - - priv->chan[chidx].inuse = false; -} - -/******************************************************************************* - * Name: stm32_chan_freeall - * - * Description: - * Free all channels. - * - *******************************************************************************/ - -static inline void stm32_chan_freeall(FAR struct stm32_usbhost_s *priv) -{ - uint8_t chidx; - - /* Free all host channels */ - - for (chidx = 2; chidx < STM32_NHOST_CHANNELS ; chidx ++) - { - stm32_chan_free(priv, chidx); - } -} - -/******************************************************************************* - * Name: stm32_chan_configure - * - * Description: - * Configure or re-configure a host channel. Host channels are configured - * when endpoint is allocated and EP0 (only) is re-configured with the - * max packet size or device address changes. - * - *******************************************************************************/ - -static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) -{ - uint32_t regval; - - /* Clear any old pending interrupts for this host channel. */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), 0xffffffff); - - /* Enable channel interrupts required for transfers on this channel. */ - - regval = 0; - - switch (priv->chan[chidx].eptype) - { - case OTGFS_EPTYPE_CTRL: - case OTGFS_EPTYPE_BULK: - { - /* Interrupts required for CTRL and BULK endpoints */ - - regval |= (OTGFS_HCINT_XFRC | OTGFS_HCINT_STALL | OTGFS_HCINT_NAK | - OTGFS_HCINT_TXERR | OTGFS_HCINT_DTERR); - - /* Additional setting for IN/OUT endpoints */ - - if (priv->chan[chidx].in) - { - regval |= OTGFS_HCINT_BBERR; - } - else - { - regval |= OTGFS_HCINT_NYET; - } - } - break; - - case OTGFS_EPTYPE_INTR: - { - /* Interrupts required for INTR endpoints */ - - regval |= (OTGFS_HCINT_XFRC | OTGFS_HCINT_STALL | OTGFS_HCINT_NAK | - OTGFS_HCINT_TXERR | OTGFS_HCINT_FRMOR | OTGFS_HCINT_DTERR); - - /* Additional setting for IN endpoints */ - - if (priv->chan[chidx].in) - { - regval |= OTGFS_HCINT_BBERR; - } - } - break; - - case OTGFS_EPTYPE_ISOC: - { - /* Interrupts required for ISOC endpoints */ - - regval |= (OTGFS_HCINT_XFRC | OTGFS_HCINT_ACK | OTGFS_HCINT_FRMOR); - - /* Additional setting for IN endpoints */ - - if (priv->chan[chidx].in) - { - regval |= (OTGFS_HCINT_TXERR | OTGFS_HCINT_BBERR); - } - } - break; - } - - stm32_putreg(STM32_OTGFS_HCINTMSK(chidx), regval); - - /* Enable the top level host channel interrupt. */ - - stm32_modifyreg(STM32_OTGFS_HAINTMSK, 0, OTGFS_HAINT(chidx)); - - /* Make sure host channel interrupts are enabled. */ - - stm32_modifyreg(STM32_OTGFS_GINTMSK, 0, OTGFS_GINT_HC); - - /* Program the HCCHAR register */ - - regval = ((uint32_t)priv->chan[chidx].maxpacket << OTGFS_HCCHAR_MPSIZ_SHIFT) | - ((uint32_t)priv->chan[chidx].epno << OTGFS_HCCHAR_EPNUM_SHIFT) | - ((uint32_t)priv->chan[chidx].eptype << OTGFS_HCCHAR_EPTYP_SHIFT) | - ((uint32_t)priv->devaddr << OTGFS_HCCHAR_DAD_SHIFT); - - /* Special case settings for low speed devices */ - - if (priv->lowspeed) - { - regval |= OTGFS_HCCHAR_LSDEV; - } - - /* Special case settings for IN endpoints */ - - if (priv->chan[chidx].in) - { - regval |= OTGFS_HCCHAR_EPDIR_IN; - } - - /* Special case settings for INTR endpoints */ - - if (priv->chan[chidx].eptype == OTGFS_EPTYPE_INTR) - { - regval |= OTGFS_HCCHAR_ODDFRM; - } - - /* Write the channel configuration */ - - stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval); -} - -/******************************************************************************* - * Name: stm32_chan_halt - * - * Description: - * Halt the channel associated with 'chidx' by setting the CHannel DISable - * (CHDIS) bit in in the HCCHAR register. - * - *******************************************************************************/ - -static void stm32_chan_halt(FAR struct stm32_usbhost_s *priv, int chidx, - enum stm32_chreason_e chreason) -{ - uint32_t hcchar; - uint32_t intmsk; - uint32_t eptype; - unsigned int avail; - - /* Save the reason for the halt. We need this in the channel halt interrrupt - * handling logic to know what to do next. - */ - - priv->chan[chidx].chreason = (uint8_t)chreason; - - /* "The application can disable any channel by programming the OTG_FS_HCCHARx - * register with the CHDIS and CHENA bits set to 1. This enables the OTG_FS - * host to flush the posted requests (if any) and generates a channel halted - * interrupt. The application must wait for the CHH interrupt in OTG_FS_HCINTx - * before reallocating the channel for other transactions. The OTG_FS host - * does not interrupt the transaction that has already been started on the - * USB." - */ - - hcchar = stm32_getreg(STM32_OTGFS_HCCHAR(chidx)); - hcchar |= (OTGFS_HCCHAR_CHDIS | OTGFS_HCCHAR_CHENA); - - /* Get the endpoint type from the HCCHAR register */ - - eptype = hcchar & OTGFS_HCCHAR_EPTYP_MASK; - - /* Check for space in the Tx FIFO to issue the halt. - * - * "Before disabling a channel, the application must ensure that there is at - * least one free space available in the non-periodic request queue (when - * disabling a non-periodic channel) or the periodic request queue (when - * disabling a periodic channel). The application can simply flush the - * posted requests when the Request queue is full (before disabling the - * channel), by programming the OTG_FS_HCCHARx register with the CHDIS bit - * set to 1, and the CHENA bit cleared to 0. - */ - - if (eptype == OTGFS_HCCHAR_EPTYP_CTRL || eptype == OTGFS_HCCHAR_EPTYP_BULK) - { - /* Get the number of words available in the non-periodic Tx FIFO. */ - - avail = stm32_getreg(STM32_OTGFS_HNPTXSTS) & OTGFS_HNPTXSTS_NPTXFSAV_MASK; - } - else /* if (eptype == OTGFS_HCCHAR_EPTYP_ISOC || eptype == OTGFS_HCCHAR_EPTYP_INTR) */ - { - /* Get the number of words available in the non-periodic Tx FIFO. */ - - avail = stm32_getreg(STM32_OTGFS_HPTXSTS) & OTGFS_HPTXSTS_PTXFSAVL_MASK; - } - - /* Check if there is any space available in the Tx FIFO. */ - - if (avail == 0) - { - /* The Tx FIFO is full... disable the channel to flush the requests */ - - hcchar &= ~OTGFS_HCCHAR_CHENA; - } - - /* Unmask the CHannel Halted (CHH) interrupt */ - - intmsk = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); - intmsk |= OTGFS_HCINT_CHH; - stm32_putreg(STM32_OTGFS_HCINTMSK(chidx), intmsk); - - /* Halt the channel by setting CHDIS (and maybe CHENA) in the HCCHAR */ - - stm32_putreg(STM32_OTGFS_HCCHAR(chidx), hcchar); -} - -/******************************************************************************* - * Name: stm32_chan_waitsetup - * - * Description: - * Set the request for the transfer complete event well BEFORE enabling the - * transfer (as soon as we are absolutely committed to the to avoid transfer). - * We do this to minimize race conditions. This logic would have to be expanded - * if we want to have more than one packet in flight at a time! - * - * Assumptions: - * Called from a normal thread context BEFORE the transfer has been started. - * - *******************************************************************************/ - -static int stm32_chan_waitsetup(FAR struct stm32_usbhost_s *priv, - FAR struct stm32_chan_s *chan) -{ - irqstate_t flags = irqsave(); - int ret = -ENODEV; - - /* Is the device still connected? */ - - if (priv->connected) - { - /* Yes.. then set waiter to indicate that we expect to be informed when - * either (1) the device is disconnected, or (2) the transfer completed. - */ - - chan->waiter = true; - ret = OK; - } - - irqrestore(flags); - return ret; -} - -/******************************************************************************* - * Name: stm32_chan_wait - * - * Description: - * Wait for a transfer on a channel to complete. - * - * Assumptions: - * Called from a normal thread context - * - *******************************************************************************/ - -static int stm32_chan_wait(FAR struct stm32_usbhost_s *priv, - FAR struct stm32_chan_s *chan) -{ - irqstate_t flags; - int ret; - - /* Disable interrupts so that the following operations will be atomic. On - * the OTG FS global interrupt needs to be disabled. However, here we disable - * all interrupts to exploit that fact that interrupts will be re-enabled - * while we wait. - */ - - flags = irqsave(); - - /* Loop, testing for an end of transfer conditino. The channel 'result' - * was set to EBUSY and 'waiter' was set to true before the transfer; 'waiter' - * will be set to false and 'result' will be set appropriately when the - * tranfer is completed. - */ - - do - { - /* Wait for the transfer to complete. NOTE the transfer may already - * completed before we get here or the transfer may complete while we - * wait here. - */ - - ret = sem_wait(&chan->waitsem); - - /* sem_wait should succeeed. But it is possible that we could be - * awakened by a signal too. - */ - - DEBUGASSERT(ret == OK || get_errno() == EINTR); - } - while (chan->waiter); - - /* The transfer is complete re-enable interrupts and return the result */ - - ret = -(int)chan->result; - irqrestore(flags); - return ret; -} - -/******************************************************************************* - * Name: stm32_chan_wakeup - * - * Description: - * A channel transfer has completed... wakeup any threads waiting for the - * transfer to complete. - * - * Assumptions: - * This function is called from the transfer complete interrupt handler for - * the channel. Interrupts are disabled. - * - *******************************************************************************/ - -static void stm32_chan_wakeup(FAR struct stm32_usbhost_s *priv, - FAR struct stm32_chan_s *chan) -{ - /* Is the the transfer complete? Is there a thread waiting for this transfer - * to complete? - */ - - if (chan->result != EBUSY && chan->waiter) - { - ullvdbg("Wakeup with result: %d\n", chan->result); - stm32_givesem(&chan->waitsem); - chan->waiter = false; - } -} - -/******************************************************************************* - * Name: stm32_transfer_start - * - * Description: - * Start at transfer on the select IN or OUT channel. - * - *******************************************************************************/ - -static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx) -{ - FAR struct stm32_chan_s *chan; - uint32_t regval; - unsigned int npackets; - unsigned int maxpacket; - unsigned int avail; - unsigned int wrsize; - unsigned int minsize; - - /* Set up the initial state of the transfer */ - - chan = &priv->chan[chidx]; - uvdbg("chidx: %d buflen: %d\n", chidx, chan->buflen); - - chan->result = EBUSY; - chan->inflight = 0; - priv->chidx = chidx; - - /* Compute the expected number of packets associated to the transfer. - * If the transfer length is zero (or less than the size of one maximum - * size packet), then one packet is expected. - */ - - /* If the transfer size is greater than one packet, then calculate the - * number of packets that will be received/sent, including any partial - * final packet. - */ - - maxpacket = chan->maxpacket; - - if (chan->buflen > maxpacket) - { - npackets = (chan->buflen + maxpacket - 1) / maxpacket; - - /* Clip if the buffer length if it exceeds the maximum number of - * packets that can be transferred (this should not happen). - */ - - if (npackets > STM32_MAX_PKTCOUNT) - { - npackets = STM32_MAX_PKTCOUNT; - chan->buflen = STM32_MAX_PKTCOUNT * maxpacket; - ulldbg("CLIP: chidx: %d buflen: %d\n", chidx, chan->buflen); - } - } - else - { - /* One packet will be sent/received (might be a zero length packet) */ - - npackets = 1; - } - - /* If it is an IN transfer, then adjust the size of the buffer UP to - * a full number of packets. Hmmm... couldn't this cause an overrun - * into unallocated memory? - */ - -#if 0 /* Think about this */ - if (chan->in) - { - /* Force the buffer length to an even multiple of maxpacket */ - - chan->buflen = npackets * maxpacket; - } -#endif - - /* Save the number of packets in the transfer. We will need this in - * order to set the next data toggle correctly when the transfer - * completes. - */ - - chan->npackets = (uint8_t)npackets; - - /* Setup the HCTSIZn register */ - - regval = ((uint32_t)chan->buflen << OTGFS_HCTSIZ_XFRSIZ_SHIFT) | - ((uint32_t)npackets << OTGFS_HCTSIZ_PKTCNT_SHIFT) | - ((uint32_t)chan->pid << OTGFS_HCTSIZ_DPID_SHIFT); - stm32_putreg(STM32_OTGFS_HCTSIZ(chidx), regval); - - /* Setup the HCCHAR register: Frame oddness and host channel enable */ - - regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx)); - - /* Set/clear the Odd Frame bit. Check for an even frame; if so set Odd - * Frame. This field is applicable for only periodic (isochronous and - * interrupt) channels. - */ - - if ((stm32_getreg(STM32_OTGFS_HFNUM) & 1) == 0) - { - regval |= OTGFS_HCCHAR_ODDFRM; - } - - regval &= ~OTGFS_HCCHAR_CHDIS; - regval |= OTGFS_HCCHAR_CHENA; - stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval); - - /* If this is an out transfer, then we need to do more.. we need to copy - * the outgoing data into the correct TxFIFO. - */ - - if (!chan->in && chan->buflen > 0) - { - /* Handle non-periodic (CTRL and BULK) OUT transfers differently than - * periodic (INTR and ISOC) OUT transfers. - */ - - minsize = MIN(chan->buflen, chan->maxpacket); - - switch (chan->eptype) - { - case OTGFS_EPTYPE_CTRL: /* Non periodic transfer */ - case OTGFS_EPTYPE_BULK: - { - /* Read the Non-periodic Tx FIFO status register */ - - regval = stm32_getreg(STM32_OTGFS_HNPTXSTS); - avail = ((regval & OTGFS_HNPTXSTS_NPTXFSAV_MASK) >> OTGFS_HNPTXSTS_NPTXFSAV_SHIFT) << 2; - } - break; - - /* Periodic transfer */ - - case OTGFS_EPTYPE_INTR: - case OTGFS_EPTYPE_ISOC: - { - /* Read the Non-periodic Tx FIFO status register */ - - regval = stm32_getreg(STM32_OTGFS_HPTXSTS); - avail = ((regval & OTGFS_HPTXSTS_PTXFSAVL_MASK) >> OTGFS_HPTXSTS_PTXFSAVL_SHIFT) << 2; - } - break; - - default: - DEBUGASSERT(false); - return; - } - - /* Is there space in the TxFIFO to hold the minimum size packet? */ - - if (minsize <= avail) - { - /* Yes.. Get the size of the biggest thing that we can put in the Tx FIFO now */ - - wrsize = chan->buflen; - if (wrsize > avail) - { - /* Clip the write size to the number of full, max sized packets - * that will fit in the Tx FIFO. - */ - - unsigned int wrpackets = avail / chan->maxpacket; - wrsize = wrpackets * chan->maxpacket; - } - - /* Write packet into the Tx FIFO. */ - - stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); - } - - /* Did we put the entire buffer into the Tx FIFO? */ - - if (chan->buflen > avail) - { - /* No, there was insufficient space to hold the entire transfer ... - * Enable the Tx FIFO interrupt to handle the transfer when the Tx - * FIFO becomes empty. - */ - - stm32_txfe_enable(priv, chidx); - } - } -} - -/******************************************************************************* - * Name: stm32_getframe - * - * Description: - * Get the current frame number. The frame number (FRNUM) field increments - * when a new SOF is transmitted on the USB, and is cleared to 0 when it - * reaches 0x3fff. - * - *******************************************************************************/ - -#if 0 /* Not used */ -static inline uint16_t stm32_getframe(void) -{ - return (uint16_t)(stm32_getreg(STM32_OTGFS_HFNUM) & OTGFS_HFNUM_FRNUM_MASK); -} -#endif - -/******************************************************************************* - * Name: stm32_ctrl_sendsetup - * - * Description: - * Send an IN/OUT SETUP packet. - * - *******************************************************************************/ - -static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, - FAR const struct usb_ctrlreq_s *req) -{ - FAR struct stm32_chan_s *chan; - uint32_t start; - uint32_t elapsed; - int ret; - - /* Loop while the device reports NAK (and a timeout is not exceeded */ - - chan = &priv->chan[priv->ep0out]; - start = clock_systimer(); - - do - { - /* Send the SETUP packet */ - - chan->pid = OTGFS_PID_SETUP; - chan->buffer = (FAR uint8_t *)req; - chan->buflen = USB_SIZEOF_CTRLREQ; - - /* Set up for the wait BEFORE starting the transfer */ - - ret = stm32_chan_waitsetup(priv, chan); - if (ret != OK) - { - udbg("ERROR: Device disconnected\n"); - return ret; - } - - /* Start the transfer */ - - stm32_transfer_start(priv, priv->ep0out); - - /* Wait for the transfer to complete */ - - ret = stm32_chan_wait(priv, chan); - - /* Return on success and for all failures other than EAGAIN. EAGAIN - * means that the device NAKed the SETUP command and that we should - * try a few more times. - */ - - if (ret != -EAGAIN) - { - /* Output some debug information if the transfer failed */ - - if (ret < 0) - { - udbg("Transfer failed: %d\n", ret); - } - - /* Return the result in any event */ - - return ret; - } - - /* Get the elapsed time (in frames) */ - - elapsed = clock_systimer() - start; - } - while (elapsed < STM32_SETUP_DELAY); - - return -ETIMEDOUT; -} - -/******************************************************************************* - * Name: stm32_ctrl_senddata - * - * Description: - * Send data in the data phase of an OUT control transfer. Or send status - * in the status phase of an IN control transfer - * - *******************************************************************************/ - -static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, - FAR uint8_t *buffer, unsigned int buflen) -{ - FAR struct stm32_chan_s *chan = &priv->chan[priv->ep0out]; - int ret; - - /* Save buffer information */ - - chan->buffer = buffer; - chan->buflen = buflen; - - /* Set the DATA PID */ - - if (buflen == 0) - { - /* For status OUT stage with buflen == 0, set PID DATA1 */ - - chan->outdata1 = true; - } - - /* Set the Data PID as per the outdata1 boolean */ - - chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - - /* Set up for the wait BEFORE starting the transfer */ - - ret = stm32_chan_waitsetup(priv, chan); - if (ret != OK) - { - udbg("ERROR: Device disconnected\n"); - return ret; - } - - /* Start the transfer */ - - stm32_transfer_start(priv, priv->ep0out); - - /* Wait for the transfer to complete and return the result */ - - return stm32_chan_wait(priv, chan); -} - -/******************************************************************************* - * Name: stm32_ctrl_recvdata - * - * Description: - * Receive data in the data phase of an IN control transfer. Or receive status - * in the status phase of an OUT control transfer - * - *******************************************************************************/ - -static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, - FAR uint8_t *buffer, unsigned int buflen) -{ - FAR struct stm32_chan_s *chan = &priv->chan[priv->ep0in]; - int ret; - - /* Save buffer information */ - - chan->pid = OTGFS_PID_DATA1; - chan->buffer = buffer; - chan->buflen = buflen; - - /* Set up for the wait BEFORE starting the transfer */ - - ret = stm32_chan_waitsetup(priv, chan); - if (ret != OK) - { - udbg("ERROR: Device disconnected\n"); - return ret; - } - - /* Start the transfer */ - - stm32_transfer_start(priv, priv->ep0in); - - /* Wait for the transfer to complete and return the result */ - - return stm32_chan_wait(priv, chan); -} - -/******************************************************************************* - * Name: stm32_in_transfer - * - * Description: - * Transfer 'buflen' bytes into 'buffer' from an IN channel. - * - *******************************************************************************/ - -static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, - FAR uint8_t *buffer, size_t buflen) -{ - FAR struct stm32_chan_s *chan; - uint32_t start; - uint32_t elapsed; - int ret = OK; - - /* Loop until the transfer completes (i.e., buflen is decremented to zero) - * or a fatal error occurs (any error other than a simple NAK) - */ - - chan = &priv->chan[chidx]; - chan->buffer = buffer; - chan->buflen = buflen; - - start = clock_systimer(); - while (chan->buflen > 0) - { - /* Set up for the wait BEFORE starting the transfer */ - - ret = stm32_chan_waitsetup(priv, chan); - if (ret != OK) - { - udbg("ERROR: Device disconnected\n"); - return ret; - } - - /* Set up for the transfer based on the direction and the endpoint type */ - - switch (chan->eptype) - { - default: - case OTGFS_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - return -ENOSYS; - } - - case OTGFS_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the IN data PID */ - - chan->pid = OTGFS_PID_DATA0; - } - break; - - case OTGFS_EPTYPE_BULK: /* Bulk */ - case OTGFS_EPTYPE_INTR: /* Interrupt */ - { - /* Setup the IN data PID */ - - chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - } - break; - } - - /* Start the transfer */ - - stm32_transfer_start(priv, chidx); - - /* Wait for the transfer to complete and get the result */ - - ret = stm32_chan_wait(priv, chan); - - /* EAGAIN indicates that the device NAKed the transfer and we need - * do try again. Anything else (success or other errors) will - * cause use to return - */ - - if (ret != OK) - { - udbg("Transfer failed: %d\n", ret); - - /* Check for a special case: If (1) the transfer was NAKed and (2) - * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we - * should be able to just flush the Rx and Tx FIFOs and try again. - * We can detect this latter case becasue the then the transfer - * buffer pointer and buffer size will be unaltered. - */ - - elapsed = clock_systimer() - start; - if (ret != -EAGAIN || /* Not a NAK condition OR */ - elapsed >= STM32_DATANAK_DELAY || /* Timeout has elapsed OR */ - chan->buflen != buflen) /* Data has been partially transferred */ - { - /* Break out and return the error */ - - break; - } - } - } - - return ret; -} - -/******************************************************************************* - * Name: stm32_out_transfer - * - * Description: - * Transfer the 'buflen' bytes in 'buffer' through an OUT channel. - * - *******************************************************************************/ - -static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, - FAR uint8_t *buffer, size_t buflen) -{ - FAR struct stm32_chan_s *chan; - uint32_t start; - uint32_t elapsed; - size_t xfrlen; - int ret = OK; - - /* Loop until the transfer completes (i.e., buflen is decremented to zero) - * or a fatal error occurs (any error other than a simple NAK) - */ - - chan = &priv->chan[chidx]; - start = clock_systimer(); - - while (buflen > 0) - { - /* Transfer one packet at a time. The hardware is capable of queueing - * multiple OUT packets, but I just haven't figured out how to handle - * the case where a single OUT packet in the group is NAKed. - */ - - xfrlen = MIN(chan->maxpacket, buflen); - chan->buffer = buffer; - chan->buflen = xfrlen; - - /* Set up for the wait BEFORE starting the transfer */ - - ret = stm32_chan_waitsetup(priv, chan); - if (ret != OK) - { - udbg("ERROR: Device disconnected\n"); - return ret; - } - - /* Set up for the transfer based on the direction and the endpoint type */ - - switch (chan->eptype) - { - default: - case OTGFS_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - return -ENOSYS; - } - - case OTGFS_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the OUT data PID */ - - chan->pid = OTGFS_PID_DATA0; - } - break; - - case OTGFS_EPTYPE_BULK: /* Bulk */ - { - /* Setup the OUT data PID */ - - chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - } - break; - - case OTGFS_EPTYPE_INTR: /* Interrupt */ - { - /* Setup the OUT data PID */ - - chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - - /* Toggle the OUT data PID for the next transfer */ - - chan->outdata1 ^= true; - } - } - - /* Start the transfer */ - - stm32_transfer_start(priv, chidx); - - /* Wait for the transfer to complete and get the result */ - - ret = stm32_chan_wait(priv, chan); - - /* Handle transfer failures */ - - if (ret != OK) - { - udbg("Transfer failed: %d\n", ret); - - /* Check for a special case: If (1) the transfer was NAKed and (2) - * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we - * should be able to just flush the Rx and Tx FIFOs and try again. - * We can detect this latter case becasue the then the transfer - * buffer pointer and buffer size will be unaltered. - */ - - elapsed = clock_systimer() - start; - if (ret != -EAGAIN || /* Not a NAK condition OR */ - elapsed >= STM32_DATANAK_DELAY || /* Timeout has elapsed OR */ - chan->buflen != xfrlen) /* Data has been partially transferred */ - { - /* Break out and return the error */ - - break; - } - - /* Is this flush really necessary? What does the hardware do with the - * data in the FIFO when the NAK occurs? Does it discard it? - */ - - stm32_flush_txfifos(OTGFS_GRSTCTL_TXFNUM_HALL); - - /* Get the device a little time to catch up. Then retry the transfer - * using the same buffer pointer and length. - */ - - usleep(20*1000); - } - else - { - /* Successfully transferred. Update the buffer pointer and length */ - - buffer += xfrlen; - buflen -= xfrlen; - } - } - - return ret; -} - -/******************************************************************************* - * Name: stm32_gint_wrpacket - * - * Description: - * Transfer the 'buflen' bytes in 'buffer' to the Tx FIFO associated with - * 'chidx' (non-DMA). - * - *******************************************************************************/ - -static void stm32_gint_wrpacket(FAR struct stm32_usbhost_s *priv, - FAR uint8_t *buffer, int chidx, int buflen) -{ - FAR uint32_t *src; - uint32_t fifo; - int buflen32; - - stm32_pktdump("Sending", buffer, buflen); - - /* Get the number of 32-byte words associated with this byte size */ - - buflen32 = (buflen + 3) >> 2; - - /* Get the address of the Tx FIFO associated with this channel */ - - fifo = STM32_OTGFS_DFIFO_HCH(chidx); - - /* Transfer all of the data into the Tx FIFO */ - - src = (FAR uint32_t *)buffer; - for (; buflen32 > 0; buflen32--) - { - uint32_t data = *src++; - stm32_putreg(fifo, data); - } - - /* Increment the count of bytes "in-flight" in the Tx FIFO */ - - priv->chan[chidx].inflight += buflen; -} - -/******************************************************************************* - * Name: stm32_gint_hcinisr - * - * Description: - * USB OTG FS host IN channels interrupt handler - * - * One the completion of the transfer, the channel result byte may be set as - * follows: - * - * OK - Transfer completed successfully - * EAGAIN - If devices NAKs the transfer or NYET occurs - * EPERM - If the endpoint stalls - * EIO - On a TX or data toggle error - * EPIPE - Frame overrun - * - * EBUSY in the result field indicates that the transfer has not completed. - * - *******************************************************************************/ - -static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv, - int chidx) -{ - FAR struct stm32_chan_s *chan = &priv->chan[chidx]; - uint32_t regval; - uint32_t pending; - - /* Read the HCINT register to get the pending HC interrupts. Read the - * HCINTMSK register to get the set of enabled HC interrupts. - */ - - pending = stm32_getreg(STM32_OTGFS_HCINT(chidx)); - regval = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); - - /* AND the two to get the set of enabled, pending HC interrupts */ - - pending &= regval; - ullvdbg("HCINTMSK%d: %08x pending: %08x\n", chidx, regval, pending); - - /* Check for a pending ACK response received/transmitted (ACK) interrupt */ - - if ((pending & OTGFS_HCINT_ACK) != 0) - { - /* Clear the pending the ACK response received/transmitted (ACK) interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_ACK); - } - - /* Check for a pending STALL response receive (STALL) interrupt */ - - else if ((pending & OTGFS_HCINT_STALL) != 0) - { - /* Clear the NAK and STALL Conditions. */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), (OTGFS_HCINT_NAK | OTGFS_HCINT_STALL)); - - /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is - * received on the channel. - */ - - stm32_chan_halt(priv, chidx, CHREASON_STALL); - - /* When there is a STALL, clear any pending NAK so that it is not - * processed below. - */ - - pending &= ~OTGFS_HCINT_NAK; - } - - /* Check for a pending Data Toggle ERRor (DTERR) interrupt */ - - else if ((pending & OTGFS_HCINT_DTERR) != 0) - { - /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is - * received on the channel. - */ - - stm32_chan_halt(priv, chidx, CHREASON_DTERR); - - /* Clear the NAK and data toggle error conditions */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), (OTGFS_HCINT_NAK | OTGFS_HCINT_DTERR)); - } - - /* Check for a pending FRaMe OverRun (FRMOR) interrupt */ - - if ((pending & OTGFS_HCINT_FRMOR) != 0) - { - /* Halt the channel -- the CHH interrrupt is expected next */ - - stm32_chan_halt(priv, chidx, CHREASON_FRMOR); - - /* Clear the FRaMe OverRun (FRMOR) condition */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_FRMOR); - } - - /* Check for a pending TransFeR Completed (XFRC) interrupt */ - - else if ((pending & OTGFS_HCINT_XFRC) != 0) - { - /* Clear the TransFeR Completed (XFRC) condition */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_XFRC); - - /* Then handle the transfer completion event based on the endpoint type */ - - if (chan->eptype == OTGFS_EPTYPE_CTRL || chan->eptype == OTGFS_EPTYPE_BULK) - { - /* Halt the channel -- the CHH interrrupt is expected next */ - - stm32_chan_halt(priv, chidx, CHREASON_XFRC); - - /* Clear any pending NAK condition. The 'indata1' data toggle - * should have been appropriately updated by the the RxFIFO - * logic as each packet was received. - */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_NAK); - } - else if (chan->eptype == OTGFS_EPTYPE_INTR) - { - /* Force the next transfer on an ODD frame */ - - regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx)); - regval |= OTGFS_HCCHAR_ODDFRM; - stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval); - - /* Set the request done state */ - - chan->result = OK; - } - } - - /* Check for a pending CHannel Halted (CHH) interrupt */ - - else if ((pending & OTGFS_HCINT_CHH) != 0) - { - /* Mask the CHannel Halted (CHH) interrupt */ - - regval = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); - regval &= ~OTGFS_HCINT_CHH; - stm32_putreg(STM32_OTGFS_HCINTMSK(chidx), regval); - - /* Update the request state based on the host state machine state */ - - if (chan->chreason == CHREASON_XFRC) - { - /* Set the request done reult */ - - chan->result = OK; - } - else if (chan->chreason == CHREASON_STALL) - { - /* Set the request stall result */ - - chan->result = EPERM; - } - else if ((chan->chreason == CHREASON_TXERR) || - (chan->chreason == CHREASON_DTERR)) - { - /* Set the request I/O error result */ - - chan->result = EIO; - } - else if (chan->chreason == CHREASON_NAK) - { - /* Halt on NAK only happens on an INTR channel. Fetch the HCCHAR register - * and check for an interrupt endpoint. - */ - - regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx)); - if ((regval & OTGFS_HCCHAR_EPTYP_MASK) == OTGFS_HCCHAR_EPTYP_INTR) - { - /* Toggle the IN data toggle (Used by Bulk and INTR only) */ - - chan->indata1 ^= true; - } - - /* Set the NAK error result */ - - chan->result = EAGAIN; - } - else /* if (chan->chreason == CHREASON_FRMOR) */ - { - /* Set the frame overrun error result */ - - chan->result = EPIPE; - } - - /* Clear the CHannel Halted (CHH) condition */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_CHH); - } - - /* Check for a pending Transaction ERror (TXERR) interrupt */ - - else if ((pending & OTGFS_HCINT_TXERR) != 0) - { - /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is - * received on the channel. - */ - - stm32_chan_halt(priv, chidx, CHREASON_TXERR); - - /* Clear the Transaction ERror (TXERR) condition */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_TXERR); - } - - /* Check for a pending NAK response received (NAK) interrupt */ - - else if ((pending & OTGFS_HCINT_NAK) != 0) - { - /* For a BULK tranfer, the hardware is capable of retrying - * automatically on a NAK. However, this is not always - * what we need to do. So we always halt the transfer and - * return control to high level logic in the even of a NAK. - */ - -#if 0 - /* Halt the interrupt channel */ - - if (chan->eptype == OTGFS_EPTYPE_CTRL) - { - /* Halt the channel -- the CHH interrrupt is expected next */ - - stm32_chan_halt(priv, chidx, CHREASON_NAK); - } - - /* Re-activate CTRL and BULK channels */ - - else if (chan->eptype == OTGFS_EPTYPE_CTRL || - chan->eptype == OTGFS_EPTYPE_BULK) - { - /* Re-activate the channel by clearing CHDIS and assuring that - * CHENA is set - */ - - regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx)); - regval |= OTGFS_HCCHAR_CHENA; - regval &= ~OTGFS_HCCHAR_CHDIS; - stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval); - } -#else - /* Halt all transfers on the NAK -- the CHH interrrupt is expected next */ - - stm32_chan_halt(priv, chidx, CHREASON_NAK); -#endif - /* Clear the NAK condition */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_NAK); - } - - /* Check for a transfer complete event */ - - stm32_chan_wakeup(priv, chan); -} - -/******************************************************************************* - * Name: stm32_gint_hcoutisr - * - * Description: - * USB OTG FS host OUT channels interrupt handler - * - * One the completion of the transfer, the channel result byte may be set as - * follows: - * - * OK - Transfer completed successfully - * EAGAIN - If devices NAKs the transfer or NYET occurs - * EPERM - If the endpoint stalls - * EIO - On a TX or data toggle error - * EPIPE - Frame overrun - * - * EBUSY in the result field indicates that the transfer has not completed. - * - *******************************************************************************/ - -static inline void stm32_gint_hcoutisr(FAR struct stm32_usbhost_s *priv, - int chidx) -{ - FAR struct stm32_chan_s *chan = &priv->chan[chidx]; - uint32_t regval; - uint32_t pending; - - /* Read the HCINT register to get the pending HC interrupts. Read the - * HCINTMSK register to get the set of enabled HC interrupts. - */ - - pending = stm32_getreg(STM32_OTGFS_HCINT(chidx)); - regval = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); - - /* AND the two to get the set of enabled, pending HC interrupts */ - - pending &= regval; - ullvdbg("HCINTMSK%d: %08x pending: %08x\n", chidx, regval, pending); - - /* Check for a pending ACK response received/transmitted (ACK) interrupt */ - - if ((pending & OTGFS_HCINT_ACK) != 0) - { - /* Clear the pending the ACK response received/transmitted (ACK) interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_ACK); - } - - /* Check for a pending FRaMe OverRun (FRMOR) interrupt */ - - else if ((pending & OTGFS_HCINT_FRMOR) != 0) - { - /* Halt the channel (probably not necessary for FRMOR) */ - - stm32_chan_halt(priv, chidx, CHREASON_FRMOR); - - /* Clear the pending the FRaMe OverRun (FRMOR) interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_FRMOR); - } - - /* Check for a pending TransFeR Completed (XFRC) interrupt */ - - else if ((pending & OTGFS_HCINT_XFRC) != 0) - { - /* Decrement the number of bytes remaining by the number of - * bytes that were "in-flight". - */ - - priv->chan[chidx].buffer += priv->chan[chidx].inflight; - priv->chan[chidx].buflen -= priv->chan[chidx].inflight; - priv->chan[chidx].inflight = 0; - - /* Halt the channel -- the CHH interrrupt is expected next */ - - stm32_chan_halt(priv, chidx, CHREASON_XFRC); - - /* Clear the pending the TransFeR Completed (XFRC) interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_XFRC); - } - - /* Check for a pending STALL response receive (STALL) interrupt */ - - else if ((pending & OTGFS_HCINT_STALL) != 0) - { - /* Clear the pending the STALL response receiv (STALL) interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_STALL); - - /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is - * received on the channel. - */ - - stm32_chan_halt(priv, chidx, CHREASON_STALL); - } - - /* Check for a pending NAK response received (NAK) interrupt */ - - else if ((pending & OTGFS_HCINT_NAK) != 0) - { - /* Halt the channel -- the CHH interrrupt is expected next */ - - stm32_chan_halt(priv, chidx, CHREASON_NAK); - - /* Clear the pending the NAK response received (NAK) interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_NAK); - } - - /* Check for a pending Transaction ERror (TXERR) interrupt */ - - else if ((pending & OTGFS_HCINT_TXERR) != 0) - { - /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is - * received on the channel. - */ - - stm32_chan_halt(priv, chidx, CHREASON_TXERR); - - /* Clear the pending the Transaction ERror (TXERR) interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_TXERR); - } - - /* Check for a NYET interrupt */ - -#if 0 /* NYET is a reserved bit in the HCINT register */ - else if ((pending & OTGFS_HCINT_NYET) != 0) - { - /* Halt the channel */ - - stm32_chan_halt(priv, chidx, CHREASON_NYET); - - /* Clear the pending the NYET interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_NYET); - } -#endif - - /* Check for a pending Data Toggle ERRor (DTERR) interrupt */ - - else if (pending & OTGFS_HCINT_DTERR) - { - /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is - * received on the channel. - */ - - stm32_chan_halt(priv, chidx, CHREASON_DTERR); - - /* Clear the pending the Data Toggle ERRor (DTERR) and NAK interrupts */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), (OTGFS_HCINT_DTERR | OTGFS_HCINT_NAK)); - } - - /* Check for a pending CHannel Halted (CHH) interrupt */ - - else if ((pending & OTGFS_HCINT_CHH) != 0) - { - /* Mask the CHannel Halted (CHH) interrupt */ - - regval = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); - regval &= ~OTGFS_HCINT_CHH; - stm32_putreg(STM32_OTGFS_HCINTMSK(chidx), regval); - - if (chan->chreason == CHREASON_XFRC) - { - /* Set the request done result */ - - chan->result = OK; - - /* Read the HCCHAR register to get the HCCHAR register to get - * the endpoint type. - */ - - regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx)); - - /* Is it a bulk endpoint? Were an odd number of packets - * transferred? - */ - - if ((regval & OTGFS_HCCHAR_EPTYP_MASK) == OTGFS_HCCHAR_EPTYP_BULK && - (chan->npackets & 1) != 0) - { - /* Yes to both... toggle the data out PID */ - - chan->outdata1 ^= true; - } - } - else if (chan->chreason == CHREASON_NAK || - chan->chreason == CHREASON_NYET) - { - /* Set the try again later result */ - - chan->result = EAGAIN; - } - else if (chan->chreason == CHREASON_STALL) - { - /* Set the request stall result */ - - chan->result = EPERM; - } - else if ((chan->chreason == CHREASON_TXERR) || - (chan->chreason == CHREASON_DTERR)) - { - /* Set the I/O failure result */ - - chan->result = EIO; - } - else /* if (chan->chreason == CHREASON_FRMOR) */ - { - /* Set the frame error result */ - - chan->result = EPIPE; - } - - /* Clear the pending the CHannel Halted (CHH) interrupt */ - - stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_CHH); - } - - /* Check for a transfer complete event */ - - stm32_chan_wakeup(priv, chan); -} - -/******************************************************************************* - * Name: stm32_gint_connected - * - * Description: - * Handle a connection event. - * - *******************************************************************************/ - -static void stm32_gint_connected(FAR struct stm32_usbhost_s *priv) -{ - /* We we previously disconnected? */ - - if (!priv->connected) - { - /* Yes.. then now we are connected */ - - ullvdbg("Connected\n"); - priv->connected = true; - DEBUGASSERT(priv->smstate == SMSTATE_DETACHED); - - /* Notify any waiters */ - - priv->smstate = SMSTATE_ATTACHED; - if (priv->eventwait) - { - stm32_givesem(&priv->eventsem); - priv->eventwait = false; - } - } -} - -/******************************************************************************* - * Name: stm32_gint_disconnected - * - * Description: - * Handle a disconnection event. - * - *******************************************************************************/ - -static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv) -{ - /* Were we previously connected? */ - - if (!priv->connected) - { - /* Yes.. then we no longer connected */ - - ullvdbg("Disconnected\n"); - - /* Are we bound to a class driver? */ - - if (priv->class) - { - /* Yes.. Disconnect the class driver */ - - CLASS_DISCONNECTED(priv->class); - priv->class = NULL; - } - - /* Re-Initilaize Host for new Enumeration */ - - priv->smstate = SMSTATE_DETACHED; - priv->ep0size = STM32_EP0_MAX_PACKET_SIZE; - priv->devaddr = STM32_DEF_DEVADDR; - priv->connected = false; - priv->lowspeed = false; - stm32_chan_freeall(priv); - - /* Notify any waiters that there is a change in the connection state */ - - if (priv->eventwait) - { - stm32_givesem(&priv->eventsem); - priv->eventwait = false; - } - } -} - -/******************************************************************************* - * Name: stm32_gint_sofisr - * - * Description: - * USB OTG FS start-of-frame interrupt handler - * - *******************************************************************************/ - -#ifdef CONFIG_STM32_OTGFS_SOFINTR -static inline void stm32_gint_sofisr(FAR struct stm32_usbhost_s *priv) -{ - /* Handle SOF interrupt */ -#warning "Do what?" - - /* Clear pending SOF interrupt */ - - stm32_putreg(STM32_OTGFS_GINTSTS, OTGFS_GINT_SOF); -} -#endif - -/******************************************************************************* - * Name: stm32_gint_rxflvlisr - * - * Description: - * USB OTG FS RxFIFO non-empty interrupt handler - * - *******************************************************************************/ - -static inline void stm32_gint_rxflvlisr(FAR struct stm32_usbhost_s *priv) -{ - FAR uint32_t *dest; - uint32_t grxsts; - uint32_t intmsk; - uint32_t hcchar; - uint32_t hctsiz; - uint32_t fifo; - int bcnt; - int bcnt32; - int chidx; - int i; - - /* Disable the RxFIFO non-empty interrupt */ - - intmsk = stm32_getreg(STM32_OTGFS_GINTMSK); - intmsk &= ~OTGFS_GINT_RXFLVL; - stm32_putreg(STM32_OTGFS_GINTMSK, intmsk); - - /* Read and pop the next status from the Rx FIFO */ - - grxsts = stm32_getreg(STM32_OTGFS_GRXSTSP); - ullvdbg("GRXSTS: %08x\n", grxsts); - - /* Isolate the channel number/index in the status word */ - - chidx = (grxsts & OTGFS_GRXSTSH_CHNUM_MASK) >> OTGFS_GRXSTSH_CHNUM_SHIFT; - - /* Get the host channel characteristics register (HCCHAR) for this channel */ - - hcchar = stm32_getreg(STM32_OTGFS_HCCHAR(chidx)); - - /* Then process the interrupt according to the packet status */ - - switch (grxsts & OTGFS_GRXSTSH_PKTSTS_MASK) - { - case OTGFS_GRXSTSH_PKTSTS_INRECVD: /* IN data packet received */ - { - /* Read the data into the host buffer. */ - - bcnt = (grxsts & OTGFS_GRXSTSH_BCNT_MASK) >> OTGFS_GRXSTSH_BCNT_SHIFT; - if (bcnt > 0 && priv->chan[chidx].buffer != NULL) - { - /* Transfer the packet from the Rx FIFO into the user buffer */ - - dest = (FAR uint32_t *)priv->chan[chidx].buffer; - fifo = STM32_OTGFS_DFIFO_HCH(0); - bcnt32 = (bcnt + 3) >> 2; - - for (i = 0; i < bcnt32; i++) - { - *dest++ = stm32_getreg(fifo); - } - - stm32_pktdump("Received", priv->chan[chidx].buffer, bcnt); - - /* Toggle the IN data pid (Used by Bulk and INTR only) */ - - priv->chan[chidx].indata1 ^= true; - - /* Manage multiple packet transfers */ - - priv->chan[chidx].buffer += bcnt; - priv->chan[chidx].buflen -= bcnt; - - /* Check if more packets are expected */ - - hctsiz = stm32_getreg(STM32_OTGFS_HCTSIZ(chidx)); - if ((hctsiz & OTGFS_HCTSIZ_PKTCNT_MASK) != 0) - { - /* Re-activate the channel when more packets are expected */ - - hcchar |= OTGFS_HCCHAR_CHENA; - hcchar &= ~OTGFS_HCCHAR_CHDIS; - stm32_putreg(STM32_OTGFS_HCCHAR(chidx), hcchar); - } - } - } - break; - - case OTGFS_GRXSTSH_PKTSTS_INDONE: /* IN transfer completed */ - case OTGFS_GRXSTSH_PKTSTS_DTOGERR: /* Data toggle error */ - case OTGFS_GRXSTSH_PKTSTS_HALTED: /* Channel halted */ - default: - break; - } - - /* Re-enable the RxFIFO non-empty interrupt */ - - intmsk |= OTGFS_GINT_RXFLVL; - stm32_putreg(STM32_OTGFS_GINTMSK, intmsk); -} - -/******************************************************************************* - * Name: stm32_gint_nptxfeisr - * - * Description: - * USB OTG FS non-periodic TxFIFO empty interrupt handler - * - *******************************************************************************/ - -static inline void stm32_gint_nptxfeisr(FAR struct stm32_usbhost_s *priv) -{ - FAR struct stm32_chan_s *chan; - uint32_t regval; - unsigned int wrsize; - unsigned int minsize; - unsigned int avail; - unsigned int chidx; - - /* Recover the index of the channel that is waiting for space in the Tx - * FIFO. - */ - - chidx = priv->chidx; - chan = &priv->chan[chidx]; - - /* Reduce the buffer size by the number of bytes that were previously placed - * in the Tx FIFO. - */ - - chan->buffer += chan->inflight; - chan->buflen -= chan->inflight; - chan->inflight = 0; - - /* If we have now transfered the entire buffer, then this transfer is - * complete (this case really should never happen because we disable - * the NPTXFE interrupt on the final packet). - */ - - if (chan->buflen <= 0) - { - /* Disable further Tx FIFO empty interrupts and bail. */ - - stm32_modifyreg(STM32_OTGFS_GINTMSK, OTGFS_GINT_NPTXFE, 0); - return; - } - - /* Read the status from the top of the non-periodic TxFIFO */ - - regval = stm32_getreg(STM32_OTGFS_HNPTXSTS); - - /* Extract the number of bytes available in the non-periodic Tx FIFO. */ - - avail = ((regval & OTGFS_HNPTXSTS_NPTXFSAV_MASK) >> OTGFS_HNPTXSTS_NPTXFSAV_SHIFT) << 2; - - /* Get minimal size packet that can be sent. Something is serioulsy - * configured wrong if one packet will not fit into the empty Tx FIFO. - */ - - minsize = MIN(chan->buflen, chan->maxpacket); - DEBUGASSERT(chan->buflen > 0 && avail >= minsize); - - /* Get the size to put in the Tx FIFO now */ - - wrsize = chan->buflen; - if (wrsize > avail) - { - /* Clip the write size to the number of full, max sized packets - * that will fit in the Tx FIFO. - */ - - unsigned int wrpackets = avail / chan->maxpacket; - wrsize = wrpackets * chan->maxpacket; - } - - /* Otherwise, this will be the last packet to be sent in this transaction. - * We now need to disable further NPTXFE interrupts. - */ - - else - { - stm32_modifyreg(STM32_OTGFS_GINTMSK, OTGFS_GINT_NPTXFE, 0); - } - - /* Write the next group of packets into the Tx FIFO */ - - ullvdbg("HNPTXSTS: %08x chidx: %d avail: %d buflen: %d wrsize: %d\n", - regval, chidx, avail, chan->buflen, wrsize); - - stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); -} - -/******************************************************************************* - * Name: stm32_gint_ptxfeisr - * - * Description: - * USB OTG FS periodic TxFIFO empty interrupt handler - * - *******************************************************************************/ - -static inline void stm32_gint_ptxfeisr(FAR struct stm32_usbhost_s *priv) -{ - FAR struct stm32_chan_s *chan; - uint32_t regval; - unsigned int wrsize; - unsigned int minsize; - unsigned int avail; - unsigned int chidx; - - /* Recover the index of the channel that is waiting for space in the Tx - * FIFO. - */ - - chidx = priv->chidx; - chan = &priv->chan[chidx]; - - /* Reduce the buffer size by the number of bytes that were previously placed - * in the Tx FIFO. - */ - - chan->buffer += chan->inflight; - chan->buflen -= chan->inflight; - chan->inflight = 0; - - /* If we have now transfered the entire buffer, then this transfer is - * complete (this case really should never happen because we disable - * the PTXFE interrupt on the final packet). - */ - - if (chan->buflen <= 0) - { - /* Disable further Tx FIFO empty interrupts and bail. */ - - stm32_modifyreg(STM32_OTGFS_GINTMSK, OTGFS_GINT_PTXFE, 0); - return; - } - - /* Read the status from the top of the periodic TxFIFO */ - - regval = stm32_getreg(STM32_OTGFS_HPTXSTS); - - /* Extract the number of bytes available in the periodic Tx FIFO. */ - - avail = ((regval & OTGFS_HPTXSTS_PTXFSAVL_MASK) >> OTGFS_HPTXSTS_PTXFSAVL_SHIFT) << 2; - - /* Get minimal size packet that can be sent. Something is serioulsy - * configured wrong if one packet will not fit into the empty Tx FIFO. - */ - - minsize = MIN(chan->buflen, chan->maxpacket); - DEBUGASSERT(chan->buflen > 0 && avail >= minsize); - - /* Get the size to put in the Tx FIFO now */ - - wrsize = chan->buflen; - if (wrsize > avail) - { - /* Clip the write size to the number of full, max sized packets - * that will fit in the Tx FIFO. - */ - - unsigned int wrpackets = avail / chan->maxpacket; - wrsize = wrpackets * chan->maxpacket; - } - - /* Otherwise, this will be the last packet to be sent in this transaction. - * We now need to disable further PTXFE interrupts. - */ - - else - { - stm32_modifyreg(STM32_OTGFS_GINTMSK, OTGFS_GINT_PTXFE, 0); - } - - /* Write the next group of packets into the Tx FIFO */ - - ullvdbg("HPTXSTS: %08x chidx: %d avail: %d buflen: %d wrsize: %d\n", - regval, chidx, avail, chan->buflen, wrsize); - - stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); -} - -/******************************************************************************* - * Name: stm32_gint_hcisr - * - * Description: - * USB OTG FS host channels interrupt handler - * - *******************************************************************************/ - -static inline void stm32_gint_hcisr(FAR struct stm32_usbhost_s *priv) -{ - uint32_t haint; - uint32_t hcchar; - int i = 0; - - /* Read the Host all channels interrupt register and test each bit in the - * register. Each bit i, i=0...(STM32_NHOST_CHANNELS-1), corresponds to - * a pending interrupt on channel i. - */ - - haint = stm32_getreg(STM32_OTGFS_HAINT); - for (i = 0; i < STM32_NHOST_CHANNELS; i++) - { - /* Is an interrupt pending on this channel? */ - - if ((haint & OTGFS_HAINT(i)) != 0) - { - /* Yes... read the HCCHAR register to get the direction bit */ - - hcchar = stm32_getreg(STM32_OTGFS_HCCHAR(i)); - - /* Was this an interrupt on an IN or an OUT channel? */ - - if ((hcchar & OTGFS_HCCHAR_EPDIR) != 0) - { - /* Handle the HC IN channel interrupt */ - - stm32_gint_hcinisr(priv, i); - } - else - { - /* Handle the HC OUT channel interrupt */ - - stm32_gint_hcoutisr(priv, i); - } - } - } -} - -/******************************************************************************* - * Name: stm32_gint_hprtisr - * - * Description: - * USB OTG FS host port interrupt handler - * - *******************************************************************************/ - -static inline void stm32_gint_hprtisr(FAR struct stm32_usbhost_s *priv) -{ - uint32_t hprt; - uint32_t newhprt; - uint32_t hcfg; - - /* Read the port status and control register (HPRT) */ - - hprt = stm32_getreg(STM32_OTGFS_HPRT); - - /* Setup to clear the interrupt bits in GINTSTS by setting the corresponding - * bits in the HPRT. The HCINT interrupt bit is cleared when the appropriate - * status bits in the HPRT register are cleared. - */ - - newhprt = hprt & ~(OTGFS_HPRT_PENA | OTGFS_HPRT_PCDET | - OTGFS_HPRT_PENCHNG | OTGFS_HPRT_POCCHNG); - - /* Check for Port Overcurrent CHaNGe (POCCHNG) */ - - if ((hprt & OTGFS_HPRT_POCCHNG) != 0) - { - /* Set up to clear the POCCHNG status in the new HPRT contents. */ - - newhprt |= OTGFS_HPRT_POCCHNG; - } - - /* Check for Port Connect DETected (PCDET). The core sets this bit when a - * device connection is detected. - */ - - if ((hprt & OTGFS_HPRT_PCDET) != 0) - { - /* Set up to clear the PCDET status in the new HPRT contents. Then - * process the new connection event. - */ - - newhprt |= OTGFS_HPRT_PCDET; - stm32_gint_connected(priv); - } - - /* Check for Port Enable CHaNGed (PENCHNG) */ - - if ((hprt & OTGFS_HPRT_PENCHNG) != 0) - { - /* Set up to clear the PENCHNG status in the new HPRT contents. */ - - newhprt |= OTGFS_HPRT_PENCHNG; - - /* Was the port enabled? */ - - if ((hprt & OTGFS_HPRT_PENA) != 0) - { - /* Yes.. handle the new connection event */ - - stm32_gint_connected(priv); - - /* Check the Host ConFiGuration register (HCFG) */ - - hcfg = stm32_getreg(STM32_OTGFS_HCFG); - - /* Is this a low speed or full speed connection (OTG FS does not - * support high speed) - */ - - if ((hprt & OTGFS_HPRT_PSPD_MASK) == OTGFS_HPRT_PSPD_LS) - { - /* Set the Host Frame Interval Register for the 6KHz speed */ - - stm32_putreg(STM32_OTGFS_HFIR, 6000); - - /* Are we switching from FS to LS? */ - - if ((hcfg & OTGFS_HCFG_FSLSPCS_MASK) != OTGFS_HCFG_FSLSPCS_LS6MHz) - { - /* Yes... configure for LS */ - - hcfg &= ~OTGFS_HCFG_FSLSPCS_MASK; - hcfg |= OTGFS_HCFG_FSLSPCS_LS6MHz; - stm32_putreg(STM32_OTGFS_HCFG, hcfg); - - /* And reset the port */ - - stm32_portreset(priv); - } - } - else /* if ((hprt & OTGFS_HPRT_PSPD_MASK) == OTGFS_HPRT_PSPD_FS) */ - { - stm32_putreg(STM32_OTGFS_HFIR, 48000); - - /* Are we switching from LS to FS? */ - - if ((hcfg & OTGFS_HCFG_FSLSPCS_MASK) != OTGFS_HCFG_FSLSPCS_FS48MHz) - { - /* Yes... configure for FS */ - - hcfg &= ~OTGFS_HCFG_FSLSPCS_MASK; - hcfg |= OTGFS_HCFG_FSLSPCS_FS48MHz; - stm32_putreg(STM32_OTGFS_HCFG, hcfg); - - /* And reset the port */ - - stm32_portreset(priv); - } - } - } - } - - /* Clear port interrupts by setting bits in the HPRT */ - - stm32_putreg(STM32_OTGFS_HPRT, newhprt); -} - -/******************************************************************************* - * Name: stm32_gint_discisr - * - * Description: - * USB OTG FS disconnect detected interrupt handler - * - *******************************************************************************/ - -static inline void stm32_gint_discisr(FAR struct stm32_usbhost_s *priv) -{ - /* Handle the disconnection event */ - - stm32_gint_disconnected(priv); - - /* Clear the dicsonnect interrupt */ - - stm32_putreg(STM32_OTGFS_GINTSTS, OTGFS_GINT_DISC); -} - -/******************************************************************************* - * Name: stm32_gint_iisooxfrisr - * - * Description: - * USB OTG FS incomplete isochronous interrupt handler - * - *******************************************************************************/ - -static inline void stm32_gint_iisooxfrisr(FAR struct stm32_usbhost_s *priv) -{ - uint32_t regval; - - /* CHENA : Set to enable the channel - * CHDIS : Set to stop transmitting/receiving data on a channel - */ - - regval = stm32_getreg(STM32_OTGFS_HCCHAR(0)); - regval |= (OTGFS_HCCHAR_CHDIS | OTGFS_HCCHAR_CHENA); - stm32_putreg(STM32_OTGFS_HCCHAR(0), regval); - - /* Clear the incomplete isochronous OUT interrupt */ - - stm32_putreg(STM32_OTGFS_GINTSTS, OTGFS_GINT_IISOOXFR); -} - -/******************************************************************************* - * Name: stm32_gint_isr - * - * Description: - * USB OTG FS global interrupt handler - * - *******************************************************************************/ - -static int stm32_gint_isr(int irq, FAR void *context) -{ - /* At present, there is only support for a single OTG FS host. Hence it is - * pre-allocated as g_usbhost. 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_usbhost_s *priv = &g_usbhost; - uint32_t pending; - - /* If OTG were supported, we would need to check if we are in host or - * device mode when the global interrupt occurs. Here we support only - * host mode - */ - - /* Loop while there are pending interrupts to process. This loop may save a - * little interrupt handling overhead. - */ - - for (;;) - { - /* Get the unmasked bits in the GINT status */ - - pending = stm32_getreg(STM32_OTGFS_GINTSTS); - pending &= stm32_getreg(STM32_OTGFS_GINTMSK); - - /* Return from the interrupt when there are no furhter pending - * interrupts. - */ - - if (pending == 0) - { - return OK; - } - - /* Otherwise, process each pending, unmasked GINT interrupts */ - - ullvdbg("GINTSTS: %08x\n", pending); - - /* Handle the start of frame interrupt */ - -#ifdef CONFIG_STM32_OTGFS_SOFINTR - if ((pending & OTGFS_GINT_SOF) != 0) - { - stm32_gint_sofisr(priv); - } -#endif - - /* Handle the RxFIFO non-empty interrupt */ - - if ((pending & OTGFS_GINT_RXFLVL) != 0) - { - stm32_gint_rxflvlisr(priv); - } - - /* Handle the non-periodic TxFIFO empty interrupt */ - - if ((pending & OTGFS_GINT_NPTXFE) != 0) - { - stm32_gint_nptxfeisr(priv); - } - - /* Handle the periodic TxFIFO empty interrupt */ - - if ((pending & OTGFS_GINT_PTXFE) != 0) - { - stm32_gint_ptxfeisr(priv); - } - - /* Handle the host channels interrupt */ - - if ((pending & OTGFS_GINT_HC) != 0) - { - stm32_gint_hcisr(priv); - } - - /* Handle the host port interrupt */ - - if ((pending & OTGFS_GINT_HPRT) != 0) - { - stm32_gint_hprtisr(priv); - } - - /* Handle the disconnect detected interrupt */ - - if ((pending & OTGFS_GINT_DISC) != 0) - { - stm32_gint_discisr(priv); - } - - /* Handle the incomplete isochronous OUT transfer */ - - if ((pending & OTGFS_GINT_IISOOXFR) != 0) - { - stm32_gint_iisooxfrisr(priv); - } - } - - /* We won't get here */ - - return OK; -} - -/******************************************************************************* - * Name: stm32_gint_enable and stm32_gint_disable - * - * Description: - * Respectively enable or disable the global OTG FS interrupt. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - *******************************************************************************/ - -static void stm32_gint_enable(void) -{ - uint32_t regval; - - /* Set the GINTMSK bit to unmask the interrupt */ - - regval = stm32_getreg(STM32_OTGFS_GAHBCFG); - regval |= OTGFS_GAHBCFG_GINTMSK; - stm32_putreg(STM32_OTGFS_GAHBCFG, regval); -} - -static void stm32_gint_disable(void) -{ - uint32_t regval; - - /* Clear the GINTMSK bit to mask the interrupt */ - - regval = stm32_getreg(STM32_OTGFS_GAHBCFG); - regval &= ~OTGFS_GAHBCFG_GINTMSK; - stm32_putreg(STM32_OTGFS_GAHBCFG, regval); -} - -/******************************************************************************* - * Name: stm32_hostinit_enable - * - * Description: - * Enable host interrupts. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - *******************************************************************************/ - -static inline void stm32_hostinit_enable(void) -{ - uint32_t regval; - - /* Disable all interrupts. */ - - stm32_putreg(STM32_OTGFS_GINTMSK, 0); - - /* Clear any pending interrupts. */ - - stm32_putreg(STM32_OTGFS_GINTSTS, 0xffffffff); - - /* Clear any pending USB OTG Interrupts (should be done elsewhere if OTG is supported) */ - - stm32_putreg(STM32_OTGFS_GOTGINT, 0xffffffff); - - /* Clear any pending USB OTG interrupts */ - - stm32_putreg(STM32_OTGFS_GINTSTS, 0xbfffffff); - - /* Enable the host interrupts */ - /* Common interrupts: - * - * OTGFS_GINT_WKUP : Resume/remote wakeup detected interrupt - * OTGFS_GINT_USBSUSP : USB suspend - */ - - regval = (OTGFS_GINT_WKUP | OTGFS_GINT_USBSUSP); - - /* If OTG were supported, we would need to enable the following as well: - * - * OTGFS_GINT_OTG : OTG interrupt - * OTGFS_GINT_SRQ : Session request/new session detected interrupt - * OTGFS_GINT_CIDSCHG : Connector ID status change - */ - - /* Host-specific interrupts - * - * OTGFS_GINT_SOF : Start of frame - * OTGFS_GINT_RXFLVL : RxFIFO non-empty - * OTGFS_GINT_IISOOXFR : Incomplete isochronous OUT transfer - * OTGFS_GINT_HPRT : Host port interrupt - * OTGFS_GINT_HC : Host channels interrupt - * OTGFS_GINT_DISC : Disconnect detected interrupt - */ - -#ifdef CONFIG_STM32_OTGFS_SOFINTR - regval |= (OTGFS_GINT_SOF | OTGFS_GINT_RXFLVL | OTGFS_GINT_IISOOXFR | - OTGFS_GINT_HPRT | OTGFS_GINT_HC | OTGFS_GINT_DISC); -#else - regval |= (OTGFS_GINT_RXFLVL | OTGFS_GINT_IISOOXFR | OTGFS_GINT_HPRT | - OTGFS_GINT_HC | OTGFS_GINT_DISC); -#endif - stm32_putreg(STM32_OTGFS_GINTMSK, regval); -} - -/******************************************************************************* - * Name: stm32_txfe_enable - * - * Description: - * Enable Tx FIFO empty interrupts. This is necessary when the entire - * transfer will not fit into Tx FIFO. The transfer will then be completed - * when the Tx FIFO is empty. NOTE: The Tx FIFO interrupt is disabled - * the the fifo empty interrupt handler when the transfer is complete. - * - * Input Parameters: - * priv - Driver state structure reference - * chidx - The channel that requires the Tx FIFO empty interrupt - * - * Returned Value: - * None - * - * Assumptions: - * Called from user task context. Interrupts must be disabled to assure - * exclusive access to the GINTMSK register. - * - *******************************************************************************/ - -static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx) -{ - FAR struct stm32_chan_s *chan = &priv->chan[chidx]; - irqstate_t flags; - uint32_t regval; - - /* Disable all interrupts so that we have exclusive access to the GINTMSK - * (it would be sufficent just to disable the GINT interrupt). - */ - - flags = irqsave(); - - /* Should we enable the periodic or non-peridic Tx FIFO empty interrupts */ - - regval = stm32_getreg(STM32_OTGFS_GINTMSK); - switch (chan->eptype) - { - default: - case OTGFS_EPTYPE_CTRL: /* Non periodic transfer */ - case OTGFS_EPTYPE_BULK: - regval |= OTGFS_GINT_NPTXFE; - break; - - case OTGFS_EPTYPE_INTR: /* Periodic transfer */ - case OTGFS_EPTYPE_ISOC: - regval |= OTGFS_GINT_PTXFE; - break; - } - - /* Enable interrupts */ - - stm32_putreg(STM32_OTGFS_GINTMSK, regval); - irqrestore(flags); -} - -/******************************************************************************* - * USB Host Controller Operations - *******************************************************************************/ - -/******************************************************************************* - * Name: stm32_wait - * - * Description: - * Wait for a device to be connected or disconneced. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * connected - TRUE: Wait for device to be connected; FALSE: wait for device - * to be disconnected - * - * Returned Values: - * Zero (OK) is returned when a device in connected. This function will not - * return until either (1) a device is connected or (2) some failure occurs. - * On a failure, a negated errno value is returned indicating the nature of - * the failure - * - * Assumptions: - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - *******************************************************************************/ - -static int stm32_wait(FAR struct usbhost_driver_s *drvr, bool connected) -{ - FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; - irqstate_t flags; - - /* Are we already connected? */ - - flags = irqsave(); - while (priv->connected == connected) - { - /* No... wait for the connection/disconnection */ - - priv->eventwait = true; - stm32_takesem(&priv->eventsem); - } - - irqrestore(flags); - - udbg("Connected:%s\n", priv->connected ? "YES" : "NO"); - return OK; -} - -/******************************************************************************* - * Name: stm32_enumerate - * - * Description: - * Enumerate the connected device. As part of this enumeration process, - * the driver will (1) get the device's configuration descriptor, (2) - * extract the class ID info from the configuration descriptor, (3) call - * usbhost_findclass() to find the class that supports this device, (4) - * call the create() method on the struct usbhost_registry_s interface - * to get a class instance, and finally (5) call the configdesc() method - * of the struct usbhost_class_s interface. After that, the class is in - * charge of the sequence of operations. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - *******************************************************************************/ - -static int stm32_enumerate(FAR struct usbhost_driver_s *drvr) -{ - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; - uint32_t regval; - int chidx; - int ret; - - /* Are we connected to a device? The caller should have called the wait() - * method first to be assured that a device is connected. - */ - - while (!priv->connected) - { - /* No, return an error */ - - udbg("Not connected\n"); - return -ENODEV; - } - - DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED); - - /* Allocate and initialize the control OUT channel */ - - chidx = stm32_chan_alloc(priv); - DEBUGASSERT(chidx >= 0); - - priv->ep0out = chidx; - priv->chan[chidx].epno = 0; - priv->chan[chidx].in = false; - priv->chan[chidx].eptype = OTGFS_EPTYPE_CTRL; - priv->chan[chidx].maxpacket = STM32_EP0_DEF_PACKET_SIZE; - priv->chan[chidx].indata1 = false; - priv->chan[chidx].outdata1 = false; - - /* Allocate and initialize the control IN channel */ - - chidx = stm32_chan_alloc(priv); - DEBUGASSERT(chidx >= 0); - - priv->ep0in = chidx; - priv->chan[chidx].epno = 0; - priv->chan[chidx].in = true; - priv->chan[chidx].eptype = OTGFS_EPTYPE_CTRL; - priv->chan[chidx].maxpacket = STM32_EP0_DEF_PACKET_SIZE; - priv->chan[chidx].indata1 = false; - priv->chan[chidx].outdata1 = false; - - /* USB 2.0 spec says at least 50ms delay before port reset. We wait 100ms. */ - - usleep(100*1000); - - /* Reset the host port */ - - stm32_portreset(priv); - - /* Get the current device speed */ - - regval = stm32_getreg(STM32_OTGFS_HPRT); - priv->lowspeed = ((regval & OTGFS_HPRT_PSPD_MASK) == OTGFS_HPRT_PSPD_LS); - - /* Configure control channels */ - - stm32_chan_configure(priv, priv->ep0out) ; - stm32_chan_configure(priv, priv->ep0in) ; - - /* Let the common usbhost_enumerate do all of the real work. Note that the - * FunctionAddress (USB address) is hardcoded to one. - */ - - uvdbg("Enumerate the device\n"); - priv->smstate = SMSTATE_ENUM; - ret = usbhost_enumerate(drvr, 1, &priv->class); - - /* The enumeration may fail either because of some HCD interfaces failure - * or because the device class is not supported. In either case, we just - * need to perform the disconnection operation and make ready for a new - * enumeration. - */ - - if (ret < 0) - { - /* Return to the disconnected state */ - - stm32_gint_disconnected(priv); - } - - return ret; -} - -/************************************************************************************ - * Name: stm32_ep0configure - * - * Description: - * Configure endpoint 0. This method is normally used internally by the - * enumerate() method but is made available at the interface to support an - * external implementation of the enumeration logic. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * funcaddr - The USB address of the function containing the endpoint that EP0 - * controls - * maxpacketsize - The maximum number of bytes that can be sent to or - * received from the endpoint in a single data packet - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ************************************************************************************/ - -static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, - uint16_t maxpacketsize) -{ - FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; - - DEBUGASSERT(drvr && funcaddr < 128 && maxpacketsize < 2048); - - /* We must have exclusive access to the USB host hardware and state structures */ - - stm32_takesem(&priv->exclsem); - - /* Save the device address and EP0 max packet size */ - - priv->devaddr = funcaddr; - priv->ep0size = maxpacketsize; - - /* Configure the EP0 OUT channel */ - - priv->chan[priv->ep0out].maxpacket = maxpacketsize; - stm32_chan_configure(priv, priv->ep0out); - - /* Configure the EP0 IN channel */ - - priv->chan[priv->ep0in].maxpacket = maxpacketsize; - stm32_chan_configure(priv, priv->ep0in); - - stm32_givesem(&priv->exclsem); - return OK; -} - -/************************************************************************************ - * Name: stm32_epalloc - * - * Description: - * Allocate and configure one endpoint. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * epdesc - Describes the endpoint to be allocated. - * ep - A memory location provided by the caller in which to receive the - * allocated endpoint desciptor. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ************************************************************************************/ - -static int stm32_epalloc(FAR struct usbhost_driver_s *drvr, - FAR const struct usbhost_epdesc_s *epdesc, - FAR usbhost_ep_t *ep) -{ - FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; - FAR struct stm32_chan_s *chan; - int chidx; - int ret; - - /* Sanity check. NOTE that this method should only be called if a device is - * connected (because we need a valid low speed indication). - */ - - DEBUGASSERT(priv && epdesc && ep && priv->connected); - - /* We must have exclusive access to the USB host hardware and state structures */ - - stm32_takesem(&priv->exclsem); - - /* Allocate a host channel for the endpoint */ - - chidx = stm32_chan_alloc(priv); - if (chidx < 0) - { - udbg("Failed to allocate a host channel\n"); - ret = -ENOMEM; - goto errout; - } - - /* Decode the endpoint descriptor to initialize the channel data structures. - * Note: Here we depend on the fact that the endpoint point type is - * encoded in the same way in the endpoint descriptor as it is in the OTG - * FS hardware. - */ - - chan = &priv->chan[chidx]; - chan->epno = epdesc->addr & USB_EPNO_MASK; - chan->in = epdesc->in; - chan->eptype = epdesc->xfrtype; - chan->maxpacket = epdesc->mxpacketsize; - chan->indata1 = false; - chan->outdata1 = false; - - /* Then configure the endpoint */ - - stm32_chan_configure(priv, chidx) ; - - /* Return the index to the allocated channel as the endpoint "handle" */ - - *ep = (usbhost_ep_t)chidx; - ret = OK; - -errout: - stm32_givesem(&priv->exclsem); - return ret; -} - -/************************************************************************************ - * Name: stm32_epfree - * - * Description: - * Free and endpoint previously allocated by DRVR_EPALLOC. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * ep - The endpint to be freed. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ************************************************************************************/ - -static int stm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) -{ - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; - int chidx = (int)ep; - - DEBUGASSERT(priv && chidx < STM32_MAX_TX_FIFOS); - - /* We must have exclusive access to the USB host hardware and state structures */ - - stm32_takesem(&priv->exclsem); - - /* Halt the channel and mark the channel avaiable */ - - stm32_chan_free(priv, chidx); - - stm32_givesem(&priv->exclsem); - return OK; -} - -/******************************************************************************* - * Name: stm32_alloc - * - * Description: - * Some hardware supports special memory in which request and descriptor data can - * be accessed more efficiently. This method provides a mechanism to allocate - * the request/descriptor memory. If the underlying hardware does not support - * such "special" memory, this functions may simply map to kmalloc. - * - * This interface was optimized under a particular assumption. It was assumed - * that the driver maintains a pool of small, pre-allocated buffers for descriptor - * traffic. NOTE that size is not an input, but an output: The size of the - * pre-allocated buffer is returned. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * buffer - The address of a memory location provided by the caller in which to - * return the allocated buffer memory address. - * maxlen - The address of a memory location provided by the caller in which to - * return the maximum size of the allocated buffer memory. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - *******************************************************************************/ - -static int stm32_alloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, FAR size_t *maxlen) -{ - FAR uint8_t *alloc; - - DEBUGASSERT(drvr && buffer && maxlen); - - /* There is no special memory requirement for the STM32. */ - - alloc = (FAR uint8_t *)kmalloc(CONFIG_STM32_OTGFS_DESCSIZE); - if (!alloc) - { - return -ENOMEM; - } - - /* Return the allocated address and size of the descriptor buffer */ - - *buffer = alloc; - *maxlen = CONFIG_STM32_OTGFS_DESCSIZE; - return OK; -} - -/******************************************************************************* - * Name: stm32_free - * - * Description: - * Some hardware supports special memory in which request and descriptor data can - * be accessed more efficiently. This method provides a mechanism to free that - * request/descriptor memory. If the underlying hardware does not support - * such "special" memory, this functions may simply map to kfree(). - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * buffer - The address of the allocated buffer memory to be freed. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * - Never called from an interrupt handler. - * - *******************************************************************************/ - -static int stm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) -{ - /* There is no special memory requirement */ - - DEBUGASSERT(drvr && buffer); - kfree(buffer); - return OK; -} - -/************************************************************************************ - * Name: stm32_ioalloc - * - * Description: - * Some hardware supports special memory in which larger IO buffers can - * be accessed more efficiently. This method provides a mechanism to allocate - * the request/descriptor memory. If the underlying hardware does not support - * such "special" memory, this functions may simply map to kmalloc. - * - * This interface differs from DRVR_ALLOC in that the buffers are variable-sized. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * buffer - The address of a memory location provided by the caller in which to - * return the allocated buffer memory address. - * buflen - The size of the buffer required. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ************************************************************************************/ - -static int stm32_ioalloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, size_t buflen) -{ - FAR uint8_t *alloc; - - DEBUGASSERT(drvr && buffer && buflen > 0); - - /* There is no special memory requirement */ - - alloc = (FAR uint8_t *)kmalloc(buflen); - if (!alloc) - { - return -ENOMEM; - } - - /* Return the allocated buffer */ - - *buffer = alloc; - return OK; -} - -/************************************************************************************ - * Name: stm32_iofree - * - * Description: - * Some hardware supports special memory in which IO data can be accessed more - * efficiently. This method provides a mechanism to free that IO buffer - * memory. If the underlying hardware does not support such "special" memory, - * this functions may simply map to kfree(). - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * buffer - The address of the allocated buffer memory to be freed. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ************************************************************************************/ - -static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) -{ - /* There is no special memory requirement */ - - DEBUGASSERT(drvr && buffer); - kfree(buffer); - return OK; -} - -/******************************************************************************* - * Name: stm32_ctrlin and stm32_ctrlout - * - * Description: - * Process a IN or OUT request on the control endpoint. These methods - * will enqueue the request and wait for it to complete. Only one transfer may be - * queued; Neither these methods nor the transfer() method can be called again - * until the control transfer functions returns. - * - * These are blocking methods; these functions will not return until the - * control transfer has completed. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * req - Describes the request to be sent. This request must lie in memory - * created by DRVR_ALLOC. - * buffer - A buffer used for sending the request and for returning any - * responses. This buffer must be large enough to hold the length value - * in the request description. buffer must have been allocated using DRVR_ALLOC - * - * NOTE: On an IN transaction, req and buffer may refer to the same allocated - * memory. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - *******************************************************************************/ - -static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, - FAR const struct usb_ctrlreq_s *req, - FAR uint8_t *buffer) -{ - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; - uint16_t buflen; - uint32_t start; - uint32_t elapsed; - int retries; - int ret; - - DEBUGASSERT(drvr && req); - uvdbg("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", - req->type, req->req, req->value[1], req->value[0], - req->index[1], req->index[0], req->len[1], req->len[0]); - - /* Extract values from the request */ - - buflen = stm32_getle16(req->len); - - /* We must have exclusive access to the USB host hardware and state structures */ - - stm32_takesem(&priv->exclsem); - - /* Loop, retrying until the retry time expires */ - - for (retries = 0; retries < STM32_RETRY_COUNT; retries++) - { - /* Send the SETUP request */ - - ret = stm32_ctrl_sendsetup(priv, req); - if (ret < 0) - { - udbg("stm32_ctrl_sendsetup failed: %d\n", ret); - continue; - } - - /* Get the start time. Loop again until the timeout expires */ - - start = clock_systimer(); - do - { - /* Handle the IN data phase (if any) */ - - if (buflen > 0) - { - ret = stm32_ctrl_recvdata(priv, buffer, buflen); - if (ret < 0) - { - udbg("stm32_ctrl_recvdata failed: %d\n", ret); - } - } - - /* Handle the status OUT phase */ - - if (ret == OK) - { - priv->chan[priv->ep0out].outdata1 ^= true; - ret = stm32_ctrl_senddata(priv, NULL, 0); - if (ret == OK) - { - /* All success transactions exit here */ - - stm32_givesem(&priv->exclsem); - return OK; - } - - udbg("stm32_ctrl_senddata failed: %d\n", ret); - } - - /* Get the elapsed time (in frames) */ - - elapsed = clock_systimer() - start; - } - while (elapsed < STM32_DATANAK_DELAY); - } - - /* All failures exit here after all retries and timeouts have been exhausted */ - - stm32_givesem(&priv->exclsem); - return -ETIMEDOUT; -} - -static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, - FAR const struct usb_ctrlreq_s *req, - FAR const uint8_t *buffer) -{ - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; - uint16_t buflen; - uint32_t start; - uint32_t elapsed; - int retries; - int ret; - - DEBUGASSERT(drvr && req); - uvdbg("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", - req->type, req->req, req->value[1], req->value[0], - req->index[1], req->index[0], req->len[1], req->len[0]); - - /* Extract values from the request */ - - buflen = stm32_getle16(req->len); - - /* We must have exclusive access to the USB host hardware and state structures */ - - stm32_takesem(&priv->exclsem); - - /* Loop, retrying until the retry time expires */ - - for (retries = 0; retries < STM32_RETRY_COUNT; retries++) - { - /* Send the SETUP request */ - - /* Send the SETUP request */ - - ret = stm32_ctrl_sendsetup(priv, req); - if (ret < 0) - { - udbg("stm32_ctrl_sendsetup failed: %d\n", ret); - continue; - } - - /* Get the start time. Loop again until the timeout expires */ - - start = clock_systimer(); - do - { - /* Handle the data OUT phase (if any) */ - - if (buflen > 0) - { - /* Start DATA out transfer (only one DATA packet) */ - - priv->chan[priv->ep0out].outdata1 = true; - ret = stm32_ctrl_senddata(priv, NULL, 0); - if (ret < 0) - { - udbg("stm32_ctrl_senddata failed: %d\n", ret); - } - } - - /* Handle the status IN phase */ - - if (ret == OK) - { - ret = stm32_ctrl_recvdata(priv, NULL, 0); - if (ret == OK) - { - /* All success transactins exit here */ - - stm32_givesem(&priv->exclsem); - return OK; - } - - udbg("stm32_ctrl_recvdata failed: %d\n", ret); - } - - /* Get the elapsed time (in frames) */ - - elapsed = clock_systimer() - start; - } - while (elapsed < STM32_DATANAK_DELAY); - } - - /* All failures exit here after all retries and timeouts have been exhausted */ - - stm32_givesem(&priv->exclsem); - return -ETIMEDOUT; -} - -/******************************************************************************* - * Name: stm32_transfer - * - * Description: - * Process a request to handle a transfer descriptor. This method will - * enqueue the transfer request and return immediately. Only one transfer may be - * queued; Neither this method nor the ctrlin or ctrlout methods can be called - * again until this function returns. - * - * This is a blocking method; this functions will not return until the - * transfer has completed. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * ep - The IN or OUT endpoint descriptor for the device endpoint on which to - * perform the transfer. - * buffer - A buffer containing the data to be sent (OUT endpoint) or received - * (IN endpoint). buffer must have been allocated using DRVR_ALLOC - * buflen - The length of the data to be sent or received. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure: - * - * EAGAIN - If devices NAKs the transfer (or NYET or other error where - * it may be appropriate to restart the entire transaction). - * EPERM - If the endpoint stalls - * EIO - On a TX or data toggle error - * EPIPE - Overrun errors - * - * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - *******************************************************************************/ - -static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen) -{ - FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; - unsigned int chidx = (unsigned int)ep; - int ret; - - uvdbg("chidx: %d buflen: %d\n", (unsigned int)ep, buflen); - - DEBUGASSERT(priv && buffer && chidx < STM32_MAX_TX_FIFOS && buflen > 0); - - /* We must have exclusive access to the USB host hardware and state structures */ - - stm32_takesem(&priv->exclsem); - - /* Handle IN and OUT transfer slightly differently */ - - if (priv->chan[chidx].in) - { - ret = stm32_in_transfer(priv, chidx, buffer, buflen); - } - else - { - ret = stm32_out_transfer(priv, chidx, buffer, buflen); - } - - stm32_givesem(&priv->exclsem); - return ret; -} - -/******************************************************************************* - * Name: stm32_disconnect - * - * Description: - * Called by the class when an error occurs and driver has been disconnected. - * The USB host driver should discard the handle to the class instance (it is - * stale) and not attempt any further interaction with the class driver instance - * (until a new instance is received from the create() method). The driver - * should not called the class' disconnected() method. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * - * Returned Values: - * None - * - * Assumptions: - * - Only a single class bound to a single device is supported. - * - Never called from an interrupt handler. - * - *******************************************************************************/ - -static void stm32_disconnect(FAR struct usbhost_driver_s *drvr) -{ - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; - priv->class = NULL; -} - -/******************************************************************************* - * Initialization - *******************************************************************************/ -/******************************************************************************* - * Name: stm32_portreset - * - * Description: - * Reset the USB host port. - * - * NOTE: "Before starting to drive a USB reset, the application waits for the - * OTG interrupt triggered by the debounce done bit (DBCDNE bit in - * OTG_FS_GOTGINT), which indicates that the bus is stable again after the - * electrical debounce caused by the attachment of a pull-up resistor on DP - * (FS) or DM (LS). - * - * Input Parameters: - * priv -- USB host driver private data structure. - * - * Returned Value: - * None - * - *******************************************************************************/ - -static void stm32_portreset(FAR struct stm32_usbhost_s *priv) -{ - uint32_t regval; - - regval = stm32_getreg(STM32_OTGFS_HPRT); - regval &= ~(OTGFS_HPRT_PENA|OTGFS_HPRT_PCDET|OTGFS_HPRT_PENCHNG|OTGFS_HPRT_POCCHNG); - regval |= OTGFS_HPRT_PRST; - stm32_putreg(STM32_OTGFS_HPRT, regval); - - up_mdelay(10); - - regval &= ~OTGFS_HPRT_PRST; - stm32_putreg(STM32_OTGFS_HPRT, regval); - - up_mdelay(20); -} - -/******************************************************************************* - * Name: stm32_flush_txfifos - * - * Description: - * Flush the selected Tx FIFO. - * - * Input Parameters: - * txfnum -- USB host driver private data structure. - * - * Returned Value: - * None. - * - *******************************************************************************/ - -static void stm32_flush_txfifos(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); -} - -/******************************************************************************* - * Name: stm32_flush_rxfifo - * - * Description: - * Flush the Rx FIFO. - * - * Input Parameters: - * priv -- USB host driver private data structure. - * - * Returned Value: - * None. - * - *******************************************************************************/ - -static void stm32_flush_rxfifo(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); -} - -/******************************************************************************* - * Name: stm32_vbusdrive - * - * Description: - * Drive the Vbus +5V. - * - * Input Parameters: - * priv - USB host driver private data structure. - * state - True: Drive, False: Don't drive - * - * Returned Value: - * None. - * - *******************************************************************************/ - -static void stm32_vbusdrive(FAR struct stm32_usbhost_s *priv, bool state) -{ - uint32_t regval; - - /* Enable/disable the external charge pump */ - - stm32_usbhost_vbusdrive(0, state); - - /* Turn on the Host port power. */ - - regval = stm32_getreg(STM32_OTGFS_HPRT); - regval &= ~(OTGFS_HPRT_PENA|OTGFS_HPRT_PCDET|OTGFS_HPRT_PENCHNG|OTGFS_HPRT_POCCHNG); - - if (((regval & OTGFS_HPRT_PPWR) == 0) && state) - { - regval |= OTGFS_HPRT_PPWR; - stm32_putreg(STM32_OTGFS_HPRT, regval); - } - - if (((regval & OTGFS_HPRT_PPWR) != 0) && !state) - { - regval &= ~OTGFS_HPRT_PPWR; - stm32_putreg(STM32_OTGFS_HPRT, regval); - } - - up_mdelay(200); -} - -/******************************************************************************* - * Name: stm32_host_initialize - * - * Description: - * Initialize/re-initialize hardware for host mode operation. At present, - * this function is called only from stm32_hw_initialize(). But if OTG mode - * were supported, this function would also be called to swtich between - * host and device modes on a connector ID change interrupt. - * - * Input Parameters: - * priv -- USB host driver private data structure. - * - * Returned Value: - * None. - * - *******************************************************************************/ - -static void stm32_host_initialize(FAR struct stm32_usbhost_s *priv) -{ - uint32_t regval; - uint32_t offset; - int i; - - /* Restart the PHY Clock */ - - stm32_putreg(STM32_OTGFS_PCGCCTL, 0); - - /* Initialize Host Configuration (HCFG) register */ - - regval = stm32_getreg(STM32_OTGFS_HCFG); - regval &= ~OTGFS_HCFG_FSLSPCS_MASK; - regval |= OTGFS_HCFG_FSLSPCS_FS48MHz; - stm32_putreg(STM32_OTGFS_HCFG, regval); - - /* Reset the host port */ - - stm32_portreset(priv); - - /* Clear the FS-/LS-only support bit in the HCFG register */ - - regval = stm32_getreg(STM32_OTGFS_HCFG); - regval &= ~OTGFS_HCFG_FSLSS; - stm32_putreg(STM32_OTGFS_HCFG, regval); - - /* Carve up FIFO memory for the Rx FIFO and the periodic and non-periodic Tx FIFOs */ - /* Configure Rx FIFO size (GRXFSIZ) */ - - stm32_putreg(STM32_OTGFS_GRXFSIZ, CONFIG_STM32_OTGFS_RXFIFO_SIZE); - offset = CONFIG_STM32_OTGFS_RXFIFO_SIZE; - - /* Setup the host non-periodic Tx FIFO size (HNPTXFSIZ) */ - - regval = (offset | (CONFIG_STM32_OTGFS_NPTXFIFO_SIZE << OTGFS_HNPTXFSIZ_NPTXFD_SHIFT)); - stm32_putreg(STM32_OTGFS_HNPTXFSIZ, regval); - offset += CONFIG_STM32_OTGFS_NPTXFIFO_SIZE; - - /* Set up the host periodic Tx fifo size register (HPTXFSIZ) */ - - regval = (offset | (CONFIG_STM32_OTGFS_PTXFIFO_SIZE << OTGFS_HPTXFSIZ_PTXFD_SHIFT)); - stm32_putreg(STM32_OTGFS_HPTXFSIZ, regval); - - /* If OTG were supported, we sould need to clear HNP enable bit in the - * USB_OTG control register about here. - */ - - /* Flush all FIFOs */ - - stm32_flush_txfifos(OTGFS_GRSTCTL_TXFNUM_HALL); - stm32_flush_rxfifo(); - - /* Clear all pending HC Interrupts */ - - for (i = 0; i < STM32_NHOST_CHANNELS; i++) - { - stm32_putreg(STM32_OTGFS_HCINT(i), 0xffffffff); - stm32_putreg(STM32_OTGFS_HCINTMSK(i), 0); - } - - /* Driver Vbus +5V (the smoke test). Should be done elsewhere in OTG - * mode. - */ - - stm32_vbusdrive(priv, true); - - /* Enable host interrupts */ - - stm32_hostinit_enable(); -} - -/******************************************************************************* - * Name: stm32_sw_initialize - * - * Description: - * One-time setup of the host driver state structure. - * - * Input Parameters: - * priv -- USB host driver private data structure. - * - * Returned Value: - * None. - * - *******************************************************************************/ - -static inline void stm32_sw_initialize(FAR struct stm32_usbhost_s *priv) -{ - int i; - - /* Initialize the state data structure */ - - sem_init(&priv->eventsem, 0, 0); - sem_init(&priv->exclsem, 0, 1); - - priv->smstate = SMSTATE_DETACHED; - priv->ep0size = STM32_EP0_MAX_PACKET_SIZE; - priv->devaddr = STM32_DEF_DEVADDR; - priv->connected = false; - priv->lowspeed = false; - - /* Put all of the channels back in their initial, allocated state */ - - memset(priv->chan, 0, STM32_MAX_TX_FIFOS * sizeof(struct stm32_chan_s)); - - /* Initialize each channel */ - - for (i = 0; i < STM32_MAX_TX_FIFOS; i++) - { - FAR struct stm32_chan_s *chan = &priv->chan[i]; - sem_init(&chan->waitsem, 0, 0); - } -} - -/******************************************************************************* - * Name: stm32_hw_initialize - * - * Description: - * One-time setup of the host controller harware for normal operations. - * - * Input Parameters: - * priv -- USB host driver private data structure. - * - * Returned Value: - * Zero on success; a negated errno value on failure. - * - *******************************************************************************/ - -static inline int stm32_hw_initialize(FAR struct stm32_usbhost_s *priv) -{ - uint32_t regval; - unsigned long timeout; - - /* Set the PHYSEL bit in the GUSBCFG register to select the OTG FS serial - * transceiver: "This bit is always 1 with write-only access" - */ - - regval = stm32_getreg(STM32_OTGFS_GUSBCFG);; - regval |= OTGFS_GUSBCFG_PHYSEL; - stm32_putreg(STM32_OTGFS_GUSBCFG, regval); - - /* 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(STM32_OTGFS_GRSTCTL, OTGFS_GRSTCTL_CSRST); - 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_STM32_OTGFS_SOFOUTPUT - regval |= OTGFS_GCCFG_SOFOUTEN; -#endif - stm32_putreg(STM32_OTGFS_GCCFG, regval); - up_mdelay(20); - - /* Initialize OTG features: In order to support OTP, the HNPCAP and SRPCAP - * bits would need to be set in the GUSBCFG register about here. - */ - - /* Force Host Mode */ - - regval = stm32_getreg(STM32_OTGFS_GUSBCFG); - regval &= ~OTGFS_GUSBCFG_FDMOD; - regval |= OTGFS_GUSBCFG_FHMOD; - stm32_putreg(STM32_OTGFS_GUSBCFG, regval); - up_mdelay(50); - - /* Initialize host mode and return success */ - - stm32_host_initialize(priv); - return OK; -} - -/******************************************************************************* - * Public Functions - *******************************************************************************/ - -/******************************************************************************* - * Name: usbhost_initialize - * - * Description: - * Initialize USB host device controller hardware. - * - * Input Parameters: - * controller -- If the device supports more than USB host controller, then - * this identifies which controller is being intialized. Normally, this - * is just zero. - * - * Returned Value: - * And instance of the USB host interface. The controlling task should - * use this interface to (1) call the wait() method to wait for a device - * to be connected, and (2) call the enumerate() method to bind the device - * to a class driver. - * - * Assumptions: - * - This function should called in the initialization sequence in order - * to initialize the USB device functionality. - * - Class drivers should be initialized prior to calling this function. - * Otherwise, there is a race condition if the device is already connected. - * - *******************************************************************************/ - -FAR struct usbhost_driver_s *usbhost_initialize(int controller) -{ - /* At present, there is only support for a single OTG FS host. Hence it is - * pre-allocated as g_usbhost. 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_usbhost_s *priv = &g_usbhost; - - /* Sanity checks */ - - DEBUGASSERT(controller == 0); - - /* Make sure that interrupts from the OTG FS core are disabled */ - - stm32_gint_disable(); - - /* Reset the state of the host driver */ - - stm32_sw_initialize(priv); - - /* Alternate function pin configuration. Here we assume that: - * - * 1. GPIOA, SYSCFG, and OTG FS peripheral clocking have 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 for DM, DP, ID, and SOF. - * - * PIN* SIGNAL DIRECTION - * ---- ----------- ---------- - * PA8 OTG_FS_SOF SOF clock output - * PA9 OTG_FS_VBUS VBUS input for device, Driven by external regulator by - * host (not an alternate function) - * PA10 OTG_FS_ID OTG ID pin (only needed in Dual mode) - * PA11 OTG_FS_DM D- I/O - * PA12 OTG_FS_DP D+ I/O - * - * *Pins may vary from device-to-device. - */ - - stm32_configgpio(GPIO_OTGFS_DM); - stm32_configgpio(GPIO_OTGFS_DP); - stm32_configgpio(GPIO_OTGFS_ID); /* Only needed for OTG */ - - /* SOF output pin configuration is configurable */ - -#ifdef CONFIG_STM32_OTGFS_SOFOUTPUT - stm32_configgpio(GPIO_OTGFS_SOF); -#endif - - /* Initialize the USB OTG FS core */ - - stm32_hw_initialize(priv); - - /* Attach USB host controller interrupt handler */ - - if (irq_attach(STM32_IRQ_OTGFS, stm32_gint_isr) != 0) - { - udbg("Failed to attach IRQ\n"); - return NULL; - } - - /* Enable USB OTG FS global interrupts */ - - stm32_gint_enable(); - - /* Enable interrupts at the interrupt controller */ - - up_enable_irq(STM32_IRQ_OTGFS); - return &priv->drvr; -} - -#endif /* CONFIG_USBHOST && CONFIG_STM32_OTGFS */ |