nuttx/arch/arm/src/stm32f7/stm32_otgdev.c
Gregory Nutt 2aa85fd17e arch/arm, board/arm: Rename all up_* functions to arm_*
Summary

The naming standard at https://cwiki.apache.org/confluence/display/NUTTX/Naming+FAQ requires that all MCU-private functions begin with the name of the architecture, not up_.

This PR addresses only these name changes for the ARM-private functions prototyped in arm_internal.h

This change to the files only modifies the name of called functions.  nxstyle fixes were made for all core architecture files.  However, there are well over 5000 additional complaints from MCU drivers and board logic that are unrelated to to this change but were affected by the name change.  It is not humanly possible to fix all of these.   I ask that this change be treated like other cosmetic changes that we have done which do not require full nxstyle compliance.

Impact

There should be not impact of this change (other that one step toward more consistent naming).
Testing

stm32f4discovery:netnsh
2020-05-01 18:28:13 +01:00

5919 lines
174 KiB
C

/****************************************************************************
* arch/arm/src/stm32f7/stm32_otgdev.c
*
* Copyright (C) 2012-2014, 2016 Gregory Nutt. All rights reserved.
* Authors: Gregory Nutt <gnutt@nuttx.org>
* David Sidrane <david_s5@nscdg.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>
#include <nuttx/irq.h>
#include <arch/board/board.h>
#include "chip.h"
#include "stm32_gpio.h"
#include "stm32_otg.h"
#include "arm_arch.h"
#include "arm_internal.h"
#if defined(CONFIG_USBDEV) && (defined(CONFIG_STM32F7_OTGFS) || \
defined(CONFIG_STM32F7_OTGFSHS))
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
# ifndef CONFIG_USBDEV_EP0_MAXSIZE
# define CONFIG_USBDEV_EP0_MAXSIZE 64
# endif
# ifndef CONFIG_USBDEV_SETUP_MAXDATASIZE
# define CONFIG_USBDEV_SETUP_MAXDATASIZE CONFIG_USBDEV_EP0_MAXSIZE
# endif
# ifndef CONFIG_USBDEV_MAXPOWER
# define CONFIG_USBDEV_MAXPOWER 100 /* mA */
# endif
/* There is 1.25Kb of FIFO memory. The default partitions this memory
* so that there is a TxFIFO allocated for each endpoint and with more
* memory provided for the common RxFIFO. A more knowledge-able
* configuration would not allocate any TxFIFO space to OUT endpoints.
*/
# ifndef CONFIG_USBDEV_RXFIFO_SIZE
# define CONFIG_USBDEV_RXFIFO_SIZE \
(STM32_OTG_FIFO_SIZE - \
STM32_OTG_FIFO_SIZE / 4/ 2 / STM32_NENDPOINTS * 4 * STM32_NENDPOINTS)
# endif
# if STM32_NENDPOINTS > 0
# ifndef CONFIG_USBDEV_EP0_TXFIFO_SIZE
# define CONFIG_USBDEV_EP0_TXFIFO_SIZE \
((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP0_TXFIFO_SIZE 0
# endif
# if STM32_NENDPOINTS > 1
# ifndef CONFIG_USBDEV_EP1_TXFIFO_SIZE
# define CONFIG_USBDEV_EP1_TXFIFO_SIZE \
((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP1_TXFIFO_SIZE 0
# endif
# if STM32_NENDPOINTS > 2
# ifndef CONFIG_USBDEV_EP2_TXFIFO_SIZE
# define CONFIG_USBDEV_EP2_TXFIFO_SIZE \
((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP2_TXFIFO_SIZE 0
# endif
# if STM32_NENDPOINTS > 3
# ifndef CONFIG_USBDEV_EP3_TXFIFO_SIZE
# define CONFIG_USBDEV_EP3_TXFIFO_SIZE \
((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP3_TXFIFO_SIZE 0
# endif
# if STM32_NENDPOINTS > 4
# ifndef CONFIG_USBDEV_EP4_TXFIFO_SIZE
# define CONFIG_USBDEV_EP4_TXFIFO_SIZE \
((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP4_TXFIFO_SIZE 0
# endif
# if STM32_NENDPOINTS > 5
# ifndef CONFIG_USBDEV_EP5_TXFIFO_SIZE
# define CONFIG_USBDEV_EP5_TXFIFO_SIZE \
((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP5_TXFIFO_SIZE 0
# endif
# if STM32_NENDPOINTS > 6
# ifndef CONFIG_USBDEV_EP6_TXFIFO_SIZE
# define CONFIG_USBDEV_EP6_TXFIFO_SIZE (\
(STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP6_TXFIFO_SIZE 0
# endif
# if STM32_NENDPOINTS > 7
# ifndef CONFIG_USBDEV_EP7_TXFIFO_SIZE
# define CONFIG_USBDEV_EP7_TXFIFO_SIZE \
((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP7_TXFIFO_SIZE 0
# endif
# if STM32_NENDPOINTS > 8
# ifndef CONFIG_USBDEV_EP8_TXFIFO_SIZE
# define CONFIG_USBDEV_EP8_TXFIFO_SIZE \
((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE) / STM32_NENDPOINTS)
# endif
# else
# define CONFIG_USBDEV_EP8_TXFIFO_SIZE 0
# endif
/* The actual FIFO addresses that we use must be aligned to 4-byte boundaries;
* FIFO sizes must be provided in units of 32-bit words.
*/
# define STM32_RXFIFO_BYTES ((CONFIG_USBDEV_RXFIFO_SIZE + 3) & ~3)
# define STM32_RXFIFO_WORDS ((CONFIG_USBDEV_RXFIFO_SIZE + 3) >> 2)
# define STM32_EP0_TXFIFO_BYTES ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP0_TXFIFO_WORDS ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) >> 2)
# define STM32_EP1_TXFIFO_BYTES ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP1_TXFIFO_WORDS ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) >> 2)
# define STM32_EP2_TXFIFO_BYTES ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP2_TXFIFO_WORDS ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) >> 2)
# define STM32_EP3_TXFIFO_BYTES ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP3_TXFIFO_WORDS ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) >> 2)
# define STM32_EP4_TXFIFO_BYTES ((CONFIG_USBDEV_EP4_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP4_TXFIFO_WORDS ((CONFIG_USBDEV_EP4_TXFIFO_SIZE + 3) >> 2)
# define STM32_EP5_TXFIFO_BYTES ((CONFIG_USBDEV_EP5_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP5_TXFIFO_WORDS ((CONFIG_USBDEV_EP5_TXFIFO_SIZE + 3) >> 2)
# define STM32_EP6_TXFIFO_BYTES ((CONFIG_USBDEV_EP6_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP6_TXFIFO_WORDS ((CONFIG_USBDEV_EP6_TXFIFO_SIZE + 3) >> 2)
# define STM32_EP7_TXFIFO_BYTES ((CONFIG_USBDEV_EP7_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP7_TXFIFO_WORDS ((CONFIG_USBDEV_EP7_TXFIFO_SIZE + 3) >> 2)
# define STM32_EP8_TXFIFO_BYTES ((CONFIG_USBDEV_EP8_TXFIFO_SIZE + 3) & ~3)
# define STM32_EP8_TXFIFO_WORDS ((CONFIG_USBDEV_EP8_TXFIFO_SIZE + 3) >> 2)
# if (STM32_RXFIFO_BYTES + \
STM32_EP0_TXFIFO_BYTES + STM32_EP1_TXFIFO_BYTES + STM32_EP2_TXFIFO_BYTES + STM32_EP3_TXFIFO_BYTES + \
STM32_EP4_TXFIFO_BYTES + STM32_EP5_TXFIFO_BYTES + STM32_EP6_TXFIFO_BYTES + STM32_EP7_TXFIFO_BYTES + CONFIG_USBDEV_EP8_TXFIFO_SIZE \
) > STM32_OTG_FIFO_SIZE
# error "FIFO allocations exceed FIFO memory size"
# endif
# define OTG_GINT_RESERVED_FS (OTG_GINT_RES89 | \
OTG_GINT_RES1617 | \
OTG_GINT_RES22)
# define OTG_GINT_RESERVED_HS (OTG_GINT_RES89 | \
OTG_GINT_RES1617 | \
OTG_GINT_RES23 | \
OTG_GINT_RES27)
# define OTG_GINT_RC_W1_FS (OTG_GINT_MMIS | \
OTG_GINT_SOF | \
OTG_GINT_ESUSP | \
OTG_GINT_USBSUSP | \
OTG_GINT_USBRST | \
OTG_GINT_ENUMDNE | \
OTG_GINT_ISOODRP | \
OTG_GINT_EOPF | \
OTG_GINT_IISOIXFR | \
OTG_GINT_IISOOXFR | \
OTG_GINT_RSTDET | \
OTG_GINT_LPMINT | \
OTG_GINT_CIDSCHG | \
OTG_GINT_DISC | \
OTG_GINT_SRQ | \
OTG_GINT_WKUP)
# define OTG_GINT_RC_W1_HS (OTG_GINT_MMIS | \
OTG_GINT_SOF | \
OTG_GINT_ESUSP | \
OTG_GINT_USBSUSP | \
OTG_GINT_USBRST | \
OTG_GINT_ENUMDNE | \
OTG_GINT_ISOODRP | \
OTG_GINT_EOPF | \
OTG_GINT_IISOIXFR | \
OTG_GINT_IISOOXFR | \
OTG_GINT_DATAFSUSP| \
OTG_GINT_CIDSCHG | \
OTG_GINT_DISC | \
OTG_GINT_SRQ | \
OTG_GINT_WKUP)
/* Only stm32F723 has internal ULPI. We consider in HS only if a ULPI is present */
# ifdef CONFIG_STM32F7_OTGFSHS
# define OTG_GINT_RESERVED OTG_GINT_RESERVED_HS
# define OTG_GINT_RC_W1 OTG_GINT_RC_W1_HS
# else
# define OTG_GINT_RESERVED OTG_GINT_RESERVED_FS
# define OTG_GINT_RC_W1 OTG_GINT_RC_W1_FS
# endif
/* Debug ***********************************************************************/
/* Trace error codes */
# define STM32_TRACEERR_ALLOCFAIL 0x01
# define STM32_TRACEERR_BADCLEARFEATURE 0x02
# define STM32_TRACEERR_BADDEVGETSTATUS 0x03
# define STM32_TRACEERR_BADEPNO 0x04
# define STM32_TRACEERR_BADEPGETSTATUS 0x05
# define STM32_TRACEERR_BADGETCONFIG 0x06
# define STM32_TRACEERR_BADGETSETDESC 0x07
# define STM32_TRACEERR_BADGETSTATUS 0x08
# define STM32_TRACEERR_BADSETADDRESS 0x09
# define STM32_TRACEERR_BADSETCONFIG 0x0a
# define STM32_TRACEERR_BADSETFEATURE 0x0b
# define STM32_TRACEERR_BADTESTMODE 0x0c
# define STM32_TRACEERR_BINDFAILED 0x0d
# define STM32_TRACEERR_DISPATCHSTALL 0x0e
# define STM32_TRACEERR_DRIVER 0x0f
# define STM32_TRACEERR_DRIVERREGISTERED 0x10
# define STM32_TRACEERR_EP0NOSETUP 0x11
# define STM32_TRACEERR_EP0SETUPSTALLED 0x12
# define STM32_TRACEERR_EPINNULLPACKET 0x13
# define STM32_TRACEERR_EPINUNEXPECTED 0x14
# define STM32_TRACEERR_EPOUTNULLPACKET 0x15
# define STM32_TRACEERR_EPOUTUNEXPECTED 0x16
# define STM32_TRACEERR_INVALIDCTRLREQ 0x17
# define STM32_TRACEERR_INVALIDPARMS 0x18
# define STM32_TRACEERR_IRQREGISTRATION 0x19
# define STM32_TRACEERR_NOEP 0x1a
# define STM32_TRACEERR_NOTCONFIGURED 0x1b
# define STM32_TRACEERR_EPOUTQEMPTY 0x1c
# define STM32_TRACEERR_EPINREQEMPTY 0x1d
# define STM32_TRACEERR_NOOUTSETUP 0x1e
# define STM32_TRACEERR_POLLTIMEOUT 0x1f
/* Trace interrupt codes */
# define STM32_TRACEINTID_USB 1 /* USB Interrupt entry/exit */
# define STM32_TRACEINTID_INTPENDING 2 /* On each pass through the
* loop */
# define STM32_TRACEINTID_EPOUT (10 + 0) /* First level
* interrupt decode */
# define STM32_TRACEINTID_EPIN (10 + 1)
# define STM32_TRACEINTID_MISMATCH (10 + 2)
# define STM32_TRACEINTID_WAKEUP (10 + 3)
# define STM32_TRACEINTID_SUSPEND (10 + 4)
# define STM32_TRACEINTID_SOF (10 + 5)
# define STM32_TRACEINTID_RXFIFO (10 + 6)
# define STM32_TRACEINTID_DEVRESET (10 + 7)
# define STM32_TRACEINTID_ENUMDNE (10 + 8)
# define STM32_TRACEINTID_IISOIXFR (10 + 9)
# define STM32_TRACEINTID_IISOOXFR (10 + 10)
# define STM32_TRACEINTID_SRQ (10 + 11)
# define STM32_TRACEINTID_OTG (10 + 12)
# define STM32_TRACEINTID_EPOUT_XFRC (40 + 0) /* EPOUT second level
* decode */
# define STM32_TRACEINTID_EPOUT_EPDISD (40 + 1)
# define STM32_TRACEINTID_EPOUT_SETUP (40 + 2)
# define STM32_TRACEINTID_DISPATCH (40 + 3)
# define STM32_TRACEINTID_GETSTATUS (50 + 0) /* EPOUT third level
* decode */
# define STM32_TRACEINTID_EPGETSTATUS (50 + 1)
# define STM32_TRACEINTID_DEVGETSTATUS (50 + 2)
# define STM32_TRACEINTID_IFGETSTATUS (50 + 3)
# define STM32_TRACEINTID_CLEARFEATURE (50 + 4)
# define STM32_TRACEINTID_SETFEATURE (50 + 5)
# define STM32_TRACEINTID_SETADDRESS (50 + 6)
# define STM32_TRACEINTID_GETSETDESC (50 + 7)
# define STM32_TRACEINTID_GETCONFIG (50 + 8)
# define STM32_TRACEINTID_SETCONFIG (50 + 9)
# define STM32_TRACEINTID_GETSETIF (50 + 10)
# define STM32_TRACEINTID_SYNCHFRAME (50 + 11)
# define STM32_TRACEINTID_EPIN_XFRC (70 + 0) /* EPIN second level
* decode */
# define STM32_TRACEINTID_EPIN_TOC (70 + 1)
# define STM32_TRACEINTID_EPIN_ITTXFE (70 + 2)
# define STM32_TRACEINTID_EPIN_EPDISD (70 + 3)
# define STM32_TRACEINTID_EPIN_TXFE (70 + 4)
# define STM32_TRACEINTID_EPIN_EMPWAIT (80 + 0) /* EPIN second level
* decode */
# define STM32_TRACEINTID_OUTNAK (90 + 0) /* RXFLVL second level
* decode */
# define STM32_TRACEINTID_OUTRECVD (90 + 1)
# define STM32_TRACEINTID_OUTDONE (90 + 2)
# define STM32_TRACEINTID_SETUPDONE (90 + 3)
# define STM32_TRACEINTID_SETUPRECVD (90 + 4)
/* Endpoints ******************************************************************/
/* Odd physical endpoint numbers are IN; even are OUT */
# define STM32_EPPHYIN2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_IN)
# define STM32_EPPHYOUT2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_OUT)
/* Endpoint 0 */
# define EP0 (0)
/* The set of all endpoints available to the class implementation (1-3) */
# define STM32_EP_AVAILABLE (0xfe) /* All available endpoints */
/* Maximum packet sizes for full speed endpoints */
# ifdef CONFIG_STM32F7_OTGFSHS
# define STM32_MAXPACKET (512) /* Max packet size (1-512) */
# else
# define STM32_MAXPACKET (64) /* Max packet size (1-64) */
# endif
/* Delays **********************************************************************/
# define STM32_READY_DELAY 200000
# define STM32_FLUSH_DELAY 200000
/* Request queue operations ****************************************************/
# define stm32_rqempty(ep) ((ep)->head == NULL)
# define stm32_rqpeek(ep) ((ep)->head)
/* Standard stuff **************************************************************/
# ifndef MIN
# define MIN(a,b) ((a) < (b) ? (a) : (b))
# endif
# ifndef MAX
# define MAX(a,b) ((a) > (b) ? (a) : (b))
# endif
/****************************************************************************
* Private Types
****************************************************************************/
/* Overall device state */
enum stm32_devstate_e
{
DEVSTATE_DEFAULT = 0, /* Power-up, unconfigured state. This state
* simply means that the device is not yet been
* given an address. SET: At initialization,
* uninitialization, reset, and whenever the
* device address is set to zero TESTED: Never */
DEVSTATE_ADDRESSED, /* Device address has been assigned, not no
* configuration has yet been selected. SET:
* When either a non-zero device address is
* first assigned or when the device is
* unconfigured (with configuration == 0)
* TESTED: never */
DEVSTATE_CONFIGURED, /* Address assigned and configured: SET: When
* the device has been addressed and an
* non-zero configuration has been selected.
* TESTED: In many places to assure that the
* USB device has been properly configured by
* the host. */
};
/* Endpoint 0 states */
enum stm32_ep0state_e
{
EP0STATE_IDLE = 0, /* Idle State, leave on receiving a SETUP
* packet or epsubmit: SET: In stm32_epin() and
* stm32_epout() when we revert from request
* processing to SETUP processing. TESTED:
* Never */
EP0STATE_SETUP_OUT, /* OUT SETUP packet received. Waiting for the
* DATA OUT phase of SETUP Packet to complete
* before processing a SETUP command (without a
* USB request): SET: Set in
* stm32_rxinterrupt() when SETUP OUT packet is
* received. TESTED: In stm32_ep0out_receive() */
EP0STATE_SETUP_READY, /* IN SETUP packet received -OR- OUT SETUP
* packet and accompanying data have been
* received. Processing of SETUP command will
* happen soon. SET: (1)
* stm32_ep0out_receive() when the OUT SETUP
* data phase completes, or (2)
* stm32_rxinterrupt() when an IN SETUP is
* packet received. TESTED: Tested in
* stm32_epout_interrupt() when SETUP phase is
* done to see if the SETUP command is ready to
* be processed. Also tested in
* stm32_ep0out_setup() just to double-check
* that we have a SETUP request and any
* accompanying data. */
EP0STATE_SETUP_PROCESS, /* SETUP Packet is being processed by
* stm32_ep0out_setup(): SET: When SETUP packet
* received in EP0 OUT TESTED: Never */
EP0STATE_SETUPRESPONSE, /* Short SETUP response write (without a USB
* request): SET: When SETUP response is sent
* by stm32_ep0in_setupresponse() TESTED: Never */
EP0STATE_DATA_IN, /* Waiting for data out stage (with a USB
* request): SET: In stm32_epin_request() when
* a write request is processed on EP0.
* TESTED: In stm32_epin() to see if we should
* revert to SETUP processing. */
EP0STATE_DATA_OUT /* Waiting for data in phase to complete ( with
* a USB request) SET: In stm32_epout_request()
* when a read request is processed on EP0.
* TESTED: In stm32_epout() to see if we should
* revert to SETUP processing */
};
/* Parsed control request */
struct stm32_ctrlreq_s
{
uint8_t type;
uint8_t req;
uint16_t value;
uint16_t index;
uint16_t len;
};
/* A container for a request so that the request may be retained in a list */
struct stm32_req_s
{
struct usbdev_req_s req; /* Standard USB request */
struct stm32_req_s *flink; /* Supports a singly linked list */
};
/* This is the internal representation of an endpoint */
struct stm32_ep_s
{
/* Common endpoint fields. This must be the first thing defined in the
* structure so that it is possible to simply cast from struct usbdev_ep_s
* to struct stm32_ep_s.
*/
struct usbdev_ep_s ep; /* Standard endpoint structure */
/* STM32-specific fields */
struct stm32_usbdev_s *dev; /* Reference to private driver data */
struct stm32_req_s *head; /* Request list for this endpoint */
struct stm32_req_s *tail;
uint8_t epphy; /* Physical EP address */
uint8_t eptype:2; /* Endpoint type */
uint8_t active:1; /* 1: A request is being processed */
uint8_t stalled:1; /* 1: Endpoint is stalled */
uint8_t isin:1; /* 1: IN Endpoint */
uint8_t odd:1; /* 1: Odd frame */
uint8_t zlp:1; /* 1: Transmit a zero-length-packet (IN EPs
* only) */
};
/* This structure retains the state of the USB device controller */
struct stm32_usbdev_s
{
/* Common device fields. This must be the first thing defined in the
* structure so that it is possible to simply cast from struct usbdev_s to
* struct stm32_usbdev_s.
*/
struct usbdev_s usbdev;
/* The bound device class driver */
struct usbdevclass_driver_s *driver;
/* STM32-specific fields */
uint8_t stalled:1; /* 1: Protocol stalled */
uint8_t selfpowered:1; /* 1: Device is self powered */
uint8_t addressed:1; /* 1: Peripheral address has been set */
uint8_t configured:1; /* 1: Class driver has been configured */
uint8_t wakeup:1; /* 1: Device remote wake-up */
uint8_t dotest:1; /* 1: Test mode selected */
uint8_t devstate:4; /* See enum stm32_devstate_e */
uint8_t ep0state:4; /* See enum stm32_ep0state_e */
uint8_t testmode:4; /* Selected test mode */
uint8_t epavail[2]; /* Bitset of available OUT/IN endpoints */
/* E0 SETUP data buffering.
*
* ctrlreq:
* The 8-byte SETUP request is received on the EP0 OUT endpoint and is
* saved.
*
* ep0data:
* For OUT SETUP requests, the SETUP data phase must also complete
* before the SETUP command can be processed. The pack receipt logic
* will save the accompanying EP0 IN data in ep0data[] before the
* SETUP command is processed.
*
* For IN SETUP requests, the DATA phase will occur AFTER the SETUP
* control request is processed. In that case, ep0data[] may be used
* as the response buffer.
*
* ep0datlen:
* Length of OUT DATA received in ep0data[] (Not used with OUT data)
*/
struct usb_ctrlreq_s ctrlreq;
uint8_t ep0data[CONFIG_USBDEV_SETUP_MAXDATASIZE];
uint16_t ep0datlen;
/* The endpoint lists */
struct stm32_ep_s epin[STM32_NENDPOINTS];
struct stm32_ep_s epout[STM32_NENDPOINTS];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Register operations ******************************************************/
# if defined(CONFIG_STM32F7_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES)
static uint32_t stm32_getreg(uint32_t addr);
static void stm32_putreg(uint32_t val, uint32_t addr);
# else
# define stm32_getreg(addr) getreg32(addr)
# define stm32_putreg(val,addr) putreg32(val,addr)
# endif
/* Request queue operations **************************************************/
static FAR struct stm32_req_s *stm32_req_remfirst(FAR struct stm32_ep_s
*privep);
static bool stm32_req_addlast(FAR struct stm32_ep_s *privep,
FAR struct stm32_req_s *req);
/* Low level data transfers and request operations ***************************/
/* Special endpoint 0 data transfer logic */
static void stm32_ep0in_setupresponse(FAR struct stm32_usbdev_s *priv,
FAR uint8_t * data, uint32_t nbytes);
static inline void stm32_ep0in_transmitzlp(FAR struct stm32_usbdev_s *priv);
static void stm32_ep0in_activate(void);
static void stm32_ep0out_ctrlsetup(FAR struct stm32_usbdev_s *priv);
/* IN request and TxFIFO handling */
static void stm32_txfifo_write(FAR struct stm32_ep_s *privep,
FAR uint8_t * buf, int nbytes);
static void stm32_epin_transfer(FAR struct stm32_ep_s *privep,
FAR uint8_t * buf, int nbytes);
static void stm32_epin_request(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep);
/* OUT request and RxFIFO handling */
static void stm32_rxfifo_read(FAR struct stm32_ep_s *privep,
FAR uint8_t * dest, uint16_t len);
static void stm32_rxfifo_discard(FAR struct stm32_ep_s *privep, int len);
static void stm32_epout_complete(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep);
static inline void stm32_ep0out_receive(FAR struct stm32_ep_s *privep,
int bcnt);
static inline void stm32_epout_receive(FAR struct stm32_ep_s *privep, int bcnt);
static void stm32_epout_request(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep);
/* General request handling */
static void stm32_ep_flush(FAR struct stm32_ep_s *privep);
static void stm32_req_complete(FAR struct stm32_ep_s *privep, int16_t result);
static void stm32_req_cancel(FAR struct stm32_ep_s *privep, int16_t status);
/* Interrupt handling ********************************************************/
static struct stm32_ep_s *stm32_ep_findbyaddr(struct stm32_usbdev_s *priv,
uint16_t eplog);
static int stm32_req_dispatch(FAR struct stm32_usbdev_s *priv,
FAR const struct usb_ctrlreq_s *ctrl);
static void stm32_usbreset(FAR struct stm32_usbdev_s *priv);
/* Second level OUT endpoint interrupt processing */
static inline void stm32_ep0out_testmode(FAR struct stm32_usbdev_s *priv,
uint16_t index);
static inline void stm32_ep0out_stdrequest(struct stm32_usbdev_s *priv,
FAR struct stm32_ctrlreq_s *ctrlreq);
static inline void stm32_ep0out_setup(struct stm32_usbdev_s *priv);
static inline void stm32_epout(FAR struct stm32_usbdev_s *priv, uint8_t epno);
static inline void stm32_epout_interrupt(FAR struct stm32_usbdev_s *priv);
/* Second level IN endpoint interrupt processing */
static inline void stm32_epin_runtestmode(FAR struct stm32_usbdev_s *priv);
static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno);
static inline void stm32_epin_txfifoempty(FAR struct stm32_usbdev_s *priv,
int epno);
static inline void stm32_epin_interrupt(FAR struct stm32_usbdev_s *priv);
/* Other second level interrupt processing */
static inline void stm32_resumeinterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_suspendinterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_enuminterrupt(FAR struct stm32_usbdev_s *priv);
# ifdef CONFIG_USBDEV_ISOCHRONOUS
static inline void stm32_isocininterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_isocoutinterrupt(FAR struct stm32_usbdev_s *priv);
# endif
# ifdef CONFIG_USBDEV_VBUSSENSING
static inline void stm32_sessioninterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_otginterrupt(FAR struct stm32_usbdev_s *priv);
# endif
/* First level interrupt processing */
static int stm32_usbinterrupt(int irq, FAR void *context, FAR void *arg);
/* Endpoint operations *********************************************************/
/* Global OUT NAK controls */
static void stm32_enablegonak(FAR struct stm32_ep_s *privep);
static void stm32_disablegonak(FAR struct stm32_ep_s *privep);
/* Endpoint configuration */
static int stm32_epout_configure(FAR struct stm32_ep_s *privep,
uint8_t eptype, uint16_t maxpacket);
static int stm32_epin_configure(FAR struct stm32_ep_s *privep,
uint8_t eptype, uint16_t maxpacket);
static int stm32_ep_configure(FAR struct usbdev_ep_s *ep,
FAR const struct usb_epdesc_s *desc, bool last);
static void stm32_ep0_configure(FAR struct stm32_usbdev_s *priv);
/* Endpoint disable */
static void stm32_epout_disable(FAR struct stm32_ep_s *privep);
static void stm32_epin_disable(FAR struct stm32_ep_s *privep);
static int stm32_ep_disable(FAR struct usbdev_ep_s *ep);
/* Endpoint request management */
static FAR struct usbdev_req_s *stm32_ep_allocreq(FAR struct usbdev_ep_s *ep);
static void stm32_ep_freereq(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *);
/* Endpoint buffer management */
# ifdef CONFIG_USBDEV_DMA
static void *stm32_ep_allocbuffer(FAR struct usbdev_ep_s *ep, uint16_t bytes);
static void stm32_ep_freebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf);
# endif
/* Endpoint request submission */
static int stm32_ep_submit(FAR struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
/* Endpoint request cancellation */
static int stm32_ep_cancel(FAR struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
/* Stall handling */
static int stm32_epout_setstall(FAR struct stm32_ep_s *privep);
static int stm32_epin_setstall(FAR struct stm32_ep_s *privep);
static int stm32_ep_setstall(FAR struct stm32_ep_s *privep);
static int stm32_ep_clrstall(FAR struct stm32_ep_s *privep);
static int stm32_ep_stall(FAR struct usbdev_ep_s *ep, bool resume);
static void stm32_ep0_stall(FAR struct stm32_usbdev_s *priv);
/* Endpoint allocation */
static FAR struct usbdev_ep_s *stm32_ep_alloc(FAR struct usbdev_s *dev,
uint8_t epno, bool in,
uint8_t eptype);
static void stm32_ep_free(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep);
/* USB device controller operations ********************************************/
static int stm32_getframe(struct usbdev_s *dev);
static int stm32_wakeup(struct usbdev_s *dev);
static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered);
static int stm32_pullup(struct usbdev_s *dev, bool enable);
static void stm32_setaddress(struct stm32_usbdev_s *priv, uint16_t address);
static int stm32_txfifo_flush(uint32_t txfnum);
static int stm32_rxfifo_flush(void);
/* Initialization **************************************************************/
static void stm32_swinitialize(FAR struct stm32_usbdev_s *priv);
static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv);
/****************************************************************************
* Private Data
****************************************************************************/
/* Since there is only a single USB interface, all status information can be
* be simply retained in a single global instance.
*/
static struct stm32_usbdev_s g_otghsdev;
static const struct usbdev_epops_s g_epops =
{
.configure = stm32_ep_configure,
.disable = stm32_ep_disable,
.allocreq = stm32_ep_allocreq,
.freereq = stm32_ep_freereq,
# ifdef CONFIG_USBDEV_DMA
.allocbuffer = stm32_ep_allocbuffer,
.freebuffer = stm32_ep_freebuffer,
# endif
.submit = stm32_ep_submit,
.cancel = stm32_ep_cancel,
.stall = stm32_ep_stall,
};
static const struct usbdev_ops_s g_devops =
{
.allocep = stm32_ep_alloc,
.freeep = stm32_ep_free,
.getframe = stm32_getframe,
.wakeup = stm32_wakeup,
.selfpowered = stm32_selfpowered,
.pullup = stm32_pullup,
};
/* Device error strings that may be enabled for more descriptive USB trace
* output.
*/
# ifdef CONFIG_USBDEV_TRACE_STRINGS
const struct trace_msg_t g_usb_trace_strings_deverror[] =
{
TRACE_STR(STM32_TRACEERR_ALLOCFAIL),
TRACE_STR(STM32_TRACEERR_BADCLEARFEATURE),
TRACE_STR(STM32_TRACEERR_BADDEVGETSTATUS),
TRACE_STR(STM32_TRACEERR_BADEPNO),
TRACE_STR(STM32_TRACEERR_BADEPGETSTATUS),
TRACE_STR(STM32_TRACEERR_BADGETCONFIG),
TRACE_STR(STM32_TRACEERR_BADGETSETDESC),
TRACE_STR(STM32_TRACEERR_BADGETSTATUS),
TRACE_STR(STM32_TRACEERR_BADSETADDRESS),
TRACE_STR(STM32_TRACEERR_BADSETCONFIG),
TRACE_STR(STM32_TRACEERR_BADSETFEATURE),
TRACE_STR(STM32_TRACEERR_BADTESTMODE),
TRACE_STR(STM32_TRACEERR_BINDFAILED),
TRACE_STR(STM32_TRACEERR_DISPATCHSTALL),
TRACE_STR(STM32_TRACEERR_DRIVER),
TRACE_STR(STM32_TRACEERR_DRIVERREGISTERED),
TRACE_STR(STM32_TRACEERR_EP0NOSETUP),
TRACE_STR(STM32_TRACEERR_EP0SETUPSTALLED),
TRACE_STR(STM32_TRACEERR_EPINNULLPACKET),
TRACE_STR(STM32_TRACEERR_EPINUNEXPECTED),
TRACE_STR(STM32_TRACEERR_EPOUTNULLPACKET),
TRACE_STR(STM32_TRACEERR_EPOUTUNEXPECTED),
TRACE_STR(STM32_TRACEERR_INVALIDCTRLREQ),
TRACE_STR(STM32_TRACEERR_INVALIDPARMS),
TRACE_STR(STM32_TRACEERR_IRQREGISTRATION),
TRACE_STR(STM32_TRACEERR_NOEP),
TRACE_STR(STM32_TRACEERR_NOTCONFIGURED),
TRACE_STR(STM32_TRACEERR_EPOUTQEMPTY),
TRACE_STR(STM32_TRACEERR_EPINREQEMPTY),
TRACE_STR(STM32_TRACEERR_NOOUTSETUP),
TRACE_STR(STM32_TRACEERR_POLLTIMEOUT),
TRACE_STR_END
};
# endif
/* Interrupt event strings that may be enabled for more descriptive USB trace
* output.
*/
# ifdef CONFIG_USBDEV_TRACE_STRINGS
const struct trace_msg_t g_usb_trace_strings_intdecode[] =
{
TRACE_STR(STM32_TRACEINTID_USB),
TRACE_STR(STM32_TRACEINTID_INTPENDING),
TRACE_STR(STM32_TRACEINTID_EPOUT),
TRACE_STR(STM32_TRACEINTID_EPIN),
TRACE_STR(STM32_TRACEINTID_MISMATCH),
TRACE_STR(STM32_TRACEINTID_WAKEUP),
TRACE_STR(STM32_TRACEINTID_SUSPEND),
TRACE_STR(STM32_TRACEINTID_SOF),
TRACE_STR(STM32_TRACEINTID_RXFIFO),
TRACE_STR(STM32_TRACEINTID_DEVRESET),
TRACE_STR(STM32_TRACEINTID_ENUMDNE),
TRACE_STR(STM32_TRACEINTID_IISOIXFR),
TRACE_STR(STM32_TRACEINTID_IISOOXFR),
TRACE_STR(STM32_TRACEINTID_SRQ),
TRACE_STR(STM32_TRACEINTID_OTG),
TRACE_STR(STM32_TRACEINTID_EPOUT_XFRC),
TRACE_STR(STM32_TRACEINTID_EPOUT_EPDISD),
TRACE_STR(STM32_TRACEINTID_EPOUT_SETUP),
TRACE_STR(STM32_TRACEINTID_DISPATCH),
TRACE_STR(STM32_TRACEINTID_GETSTATUS),
TRACE_STR(STM32_TRACEINTID_EPGETSTATUS),
TRACE_STR(STM32_TRACEINTID_DEVGETSTATUS),
TRACE_STR(STM32_TRACEINTID_IFGETSTATUS),
TRACE_STR(STM32_TRACEINTID_CLEARFEATURE),
TRACE_STR(STM32_TRACEINTID_SETFEATURE),
TRACE_STR(STM32_TRACEINTID_SETADDRESS),
TRACE_STR(STM32_TRACEINTID_GETSETDESC),
TRACE_STR(STM32_TRACEINTID_GETCONFIG),
TRACE_STR(STM32_TRACEINTID_SETCONFIG),
TRACE_STR(STM32_TRACEINTID_GETSETIF),
TRACE_STR(STM32_TRACEINTID_SYNCHFRAME),
TRACE_STR(STM32_TRACEINTID_EPIN_XFRC),
TRACE_STR(STM32_TRACEINTID_EPIN_TOC),
TRACE_STR(STM32_TRACEINTID_EPIN_ITTXFE),
TRACE_STR(STM32_TRACEINTID_EPIN_EPDISD),
TRACE_STR(STM32_TRACEINTID_EPIN_TXFE),
TRACE_STR(STM32_TRACEINTID_EPIN_EMPWAIT),
TRACE_STR(STM32_TRACEINTID_OUTNAK),
TRACE_STR(STM32_TRACEINTID_OUTRECVD),
TRACE_STR(STM32_TRACEINTID_OUTDONE),
TRACE_STR(STM32_TRACEINTID_SETUPDONE),
TRACE_STR(STM32_TRACEINTID_SETUPRECVD),
TRACE_STR_END
};
# endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: stm32_getreg
*
* Description:
* Get the contents of an STM32 register
*
****************************************************************************/
# if defined(CONFIG_STM32F7_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES)
static uint32_t stm32_getreg(uint32_t addr)
{
static uint32_t prevaddr = 0;
static uint32_t preval = 0;
static uint32_t count = 0;
/* Read the value from the register */
uint32_t val = getreg32(addr);
/* Is this the same value that we read from the same register last time? Are
* we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr && val == preval)
{
if (count == 0xffffffff || ++count > 3)
{
if (count == 4)
{
uinfo("...\n");
}
return val;
}
}
/* No this is a new address or value */
else
{
/* Did we print "..." for the previous value? */
if (count > 3)
{
/* Yes.. then show how many times the value repeated */
uinfo("[repeats %d more times]\n", count - 3);
}
/* Save the new address, value, and count */
prevaddr = addr;
preval = val;
count = 1;
}
/* Show the register value read */
uinfo("%08x->%08x\n", addr, val);
return val;
}
# endif
/****************************************************************************
* Name: stm32_putreg
*
* Description:
* Set the contents of an STM32 register to a value
*
****************************************************************************/
# if defined(CONFIG_STM32F7_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES)
static void stm32_putreg(uint32_t val, uint32_t addr)
{
/* Show the register value being written */
uinfo("%08x<-%08x\n", addr, val);
/* Write the value */
putreg32(val, addr);
}
# endif
/****************************************************************************
* Name: stm32_req_remfirst
*
* Description:
* Remove a request from the head of an endpoint request queue
*
****************************************************************************/
static FAR struct stm32_req_s *stm32_req_remfirst(FAR struct stm32_ep_s *privep)
{
FAR struct stm32_req_s *ret = privep->head;
if (ret)
{
privep->head = ret->flink;
if (!privep->head)
{
privep->tail = NULL;
}
ret->flink = NULL;
}
return ret;
}
/****************************************************************************
* Name: stm32_req_addlast
*
* Description:
* Add a request to the end of an endpoint request queue
*
****************************************************************************/
static bool stm32_req_addlast(FAR struct stm32_ep_s *privep,
FAR struct stm32_req_s *req)
{
bool is_empty = !privep->head;
req->flink = NULL;
if (is_empty)
{
privep->head = req;
privep->tail = req;
}
else
{
privep->tail->flink = req;
privep->tail = req;
}
return is_empty;
}
/****************************************************************************
* Name: stm32_ep0in_setupresponse
*
* Description:
* Schedule a short transfer on Endpoint 0 (IN or OUT)
*
****************************************************************************/
static void stm32_ep0in_setupresponse(FAR struct stm32_usbdev_s *priv,
FAR uint8_t * buf, uint32_t nbytes)
{
stm32_epin_transfer(&priv->epin[EP0], buf, nbytes);
priv->ep0state = EP0STATE_SETUPRESPONSE;
stm32_ep0out_ctrlsetup(priv);
}
/****************************************************************************
* Name: stm32_ep0in_transmitzlp
*
* Description:
* Send a zero length packet (ZLP) on endpoint 0 IN
*
****************************************************************************/
static inline void stm32_ep0in_transmitzlp(FAR struct stm32_usbdev_s *priv)
{
stm32_ep0in_setupresponse(priv, NULL, 0);
}
/****************************************************************************
* Name: stm32_ep0in_activate
*
* Description:
* Activate the endpoint 0 IN endpoint.
*
****************************************************************************/
static void stm32_ep0in_activate(void)
{
uint32_t regval;
/* Set the max packet size of the IN EP. */
regval = stm32_getreg(STM32_OTG_DIEPCTL(0));
regval &= ~OTG_DIEPCTL0_MPSIZ_MASK;
# if CONFIG_USBDEV_EP0_MAXSIZE == 8
regval |= OTG_DIEPCTL0_MPSIZ_8;
# elif CONFIG_USBDEV_EP0_MAXSIZE == 16
regval |= OTG_DIEPCTL0_MPSIZ_16;
# elif CONFIG_USBDEV_EP0_MAXSIZE == 32
regval |= OTG_DIEPCTL0_MPSIZ_32;
# elif CONFIG_USBDEV_EP0_MAXSIZE == 64
regval |= OTG_DIEPCTL0_MPSIZ_64;
# else
# error "Unsupported value of CONFIG_USBDEV_EP0_MAXSIZE"
# endif
stm32_putreg(regval, STM32_OTG_DIEPCTL(0));
/* Clear global IN NAK */
regval = stm32_getreg(STM32_OTG_DCTL);
regval |= OTG_DCTL_CGINAK;
stm32_putreg(regval, STM32_OTG_DCTL);
}
/****************************************************************************
* Name: stm32_ep0out_ctrlsetup
*
* Description:
* Setup to receive a SETUP packet.
*
****************************************************************************/
static void stm32_ep0out_ctrlsetup(FAR struct stm32_usbdev_s *priv)
{
uint32_t regval;
/* Setup the hardware to perform the SETUP transfer */
regval = (USB_SIZEOF_CTRLREQ * 3 << OTG_DOEPTSIZ0_XFRSIZ_SHIFT) |
(OTG_DOEPTSIZ0_PKTCNT) | (3 << OTG_DOEPTSIZ0_STUPCNT_SHIFT);
stm32_putreg(regval, STM32_OTG_DOEPTSIZ(0));
/* Then clear NAKing and enable the transfer */
regval = stm32_getreg(STM32_OTG_DOEPCTL(0));
regval |= (OTG_DOEPCTL0_CNAK | OTG_DOEPCTL0_EPENA);
stm32_putreg(regval, STM32_OTG_DOEPCTL(0));
}
/****************************************************************************
* Name: stm32_txfifo_write
*
* Description:
* Send data to the endpoint's TxFIFO.
*
****************************************************************************/
static void stm32_txfifo_write(FAR struct stm32_ep_s *privep,
FAR uint8_t * buf, int nbytes)
{
uint32_t regaddr;
uint32_t regval;
int nwords;
int i;
/* Convert the number of bytes to words */
nwords = (nbytes + 3) >> 2;
/* Get the TxFIFO for this endpoint (same as the endpoint number) */
regaddr = STM32_OTG_DFIFO_DEP(privep->epphy);
/* Then transfer each word to the TxFIFO */
for (i = 0; i < nwords; i++)
{
/* Read four bytes from the source buffer (to avoid unaligned accesses)
* and pack these into one 32-bit word (little endian).
*/
regval = (uint32_t) * buf++;
regval |= ((uint32_t) * buf++) << 8;
regval |= ((uint32_t) * buf++) << 16;
regval |= ((uint32_t) * buf++) << 24;
/* Then write the packet data to the TxFIFO */
stm32_putreg(regval, regaddr);
}
}
/****************************************************************************
* Name: stm32_epin_transfer
*
* Description:
* Start the Tx data transfer
*
****************************************************************************/
static void stm32_epin_transfer(FAR struct stm32_ep_s *privep,
FAR uint8_t * buf, int nbytes)
{
uint32_t pktcnt;
uint32_t regval;
/* Read the DIEPSIZx register */
regval = stm32_getreg(STM32_OTG_DIEPTSIZ(privep->epphy));
/* Clear the XFRSIZ, PKTCNT, and MCNT field of the DIEPSIZx register */
regval &= ~(OTG_DIEPTSIZ_XFRSIZ_MASK | OTG_DIEPTSIZ_PKTCNT_MASK |
OTG_DIEPTSIZ_MCNT_MASK);
/* Are we sending a zero length packet (ZLP) */
if (nbytes == 0)
{
/* Yes.. leave the transfer size at zero and set the packet count to 1 */
pktcnt = 1;
}
else
{
/* No.. Program the transfer size and packet count . First calculate:
* xfrsize = The total number of bytes to be sent. pktcnt = the number of
* packets (of maxpacket bytes) required to perform the transfer.
*/
pktcnt =
((uint32_t) nbytes + (privep->ep.maxpacket - 1)) / privep->ep.maxpacket;
}
/* Set the XFRSIZ and PKTCNT */
regval |= (pktcnt << OTG_DIEPTSIZ_PKTCNT_SHIFT);
regval |= ((uint32_t) nbytes << OTG_DIEPTSIZ_XFRSIZ_SHIFT);
/* If this is an isochronous endpoint, then set the multi-count field to the
* PKTCNT as well.
*/
if (privep->eptype == USB_EP_ATTR_XFER_ISOC)
{
regval |= (pktcnt << OTG_DIEPTSIZ_MCNT_SHIFT);
}
/* Save DIEPSIZx register value */
stm32_putreg(regval, STM32_OTG_DIEPTSIZ(privep->epphy));
/* Read the DIEPCTLx register */
regval = stm32_getreg(STM32_OTG_DIEPCTL(privep->epphy));
/* If this is an isochronous endpoint, then set the even/odd frame bit the
* DIEPCTLx register.
*/
if (privep->eptype == USB_EP_ATTR_XFER_ISOC)
{
/* Check bit 0 of the frame number of the received SOF and set the
* even/odd frame to match.
*/
uint32_t status = stm32_getreg(STM32_OTG_DSTS);
if ((status & OTG_DSTS_SOFFN0) == OTG_DSTS_SOFFN_EVEN)
{
regval |= OTG_DIEPCTL_SEVNFRM;
}
else
{
regval |= OTG_DIEPCTL_SODDFRM;
}
}
/* EP enable, IN data in FIFO */
regval &= ~OTG_DIEPCTL_EPDIS;
regval |= (OTG_DIEPCTL_CNAK | OTG_DIEPCTL_EPENA);
stm32_putreg(regval, STM32_OTG_DIEPCTL(privep->epphy));
/* Transfer the data to the TxFIFO. At this point, the caller has already
* assured that there is sufficient space in the TxFIFO to hold the transfer
* we can just blindly continue.
*/
stm32_txfifo_write(privep, buf, nbytes);
}
/****************************************************************************
* Name: stm32_epin_request
*
* Description:
* Begin or continue write request processing.
*
****************************************************************************/
static void stm32_epin_request(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep)
{
struct stm32_req_s *privreq;
uint32_t regaddr;
uint32_t regval;
uint8_t *buf;
int nbytes;
int nwords;
int bytesleft;
/* We get here in one of four possible ways. From three interrupting
* events:
*
* 1. From stm32_epin as part of the transfer complete interrupt
* processing This interrupt indicates that the last transfer has
* completed.
* 2. As part of the ITTXFE interrupt processing. That interrupt
* indicates that an IN token was received when the associated
* TxFIFO was empty.
* 3. From stm32_epin_txfifoempty as part of the TXFE interrupt
* processing. The TXFEinterrupt is only enabled when the TxFIFO
* is full and the software must wait for space to become available
* in the TxFIFO.
*
* And this function may be called immediately when the write request
* is queued to start up the next transaction.
*
* 4. From stm32_ep_submit when a new write request is received WHILE
* the endpoint is not active (privep->active == false).
*/
/* Check the request from the head of the endpoint request queue */
privreq = stm32_rqpeek(privep);
if (!privreq)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPINREQEMPTY), privep->epphy);
/* There is no TX transfer in progress and no new pending TX requests to
* send. To stop transmitting any data on a particular IN endpoint, the
* application must set the IN NAK bit. To set this bit, the following
* field must be programmed.
*/
regaddr = STM32_OTG_DIEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
regval |= OTG_DIEPCTL_SNAK;
stm32_putreg(regval, regaddr);
/* The endpoint is no longer active */
privep->active = false;
return;
}
uinfo("EP%d req=%p: len=%d xfrd=%d zlp=%d\n",
privep->epphy, privreq, privreq->req.len,
privreq->req.xfrd, privep->zlp);
/* Check for a special case: If we are just starting a request (xfrd==0) and
* the class driver is trying to send a zero-length packet (len==0). Then
* set the ZLP flag so that the packet will be sent.
*/
if (privreq->req.len == 0)
{
/* The ZLP flag is set TRUE whenever we want to force the driver to send
* a zero-length-packet on the next pass through the loop (below). The
* flag is cleared whenever a packet is sent in the loop below.
*/
privep->zlp = true;
}
/* Add one more packet to the TxFIFO. We will wait for the transfer complete
* event before we add the next packet (or part of a packet to the TxFIFO).
* The documentation says that we can can multiple packets to the TxFIFO, but
* it seems that we need to get the transfer complete event before we can add
* the next (or maybe I have got something wrong?)
*/
# if 0
while (privreq->req.xfrd < privreq->req.len || privep->zlp)
# else
if (privreq->req.xfrd < privreq->req.len || privep->zlp)
# endif
{
/* Get the number of bytes left to be sent in the request */
bytesleft = privreq->req.len - privreq->req.xfrd;
nbytes = bytesleft;
/* Assume no zero-length-packet on the next pass through this loop */
privep->zlp = false;
/* Limit the size of the transfer to one full packet and handle
* zero-length packets (ZLPs).
*/
if (nbytes > 0)
{
/* Either send the maxpacketsize or all of the remaining data in the
* request.
*/
if (nbytes >= privep->ep.maxpacket)
{
nbytes = privep->ep.maxpacket;
/* Handle the case where this packet is exactly the
* maxpacketsize. Do we need to send a zero-length packet in
* this case?
*/
if (bytesleft == privep->ep.maxpacket &&
(privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
{
/* The ZLP flag is set TRUE whenever we want to force the
* driver to send a zero-length-packet on the next pass
* through this loop. The flag is cleared (above) whenever we
* are committed to sending any packet and set here when we
* want to force one more pass through the loop.
*/
privep->zlp = true;
}
}
}
/* Get the transfer size in 32-bit words */
nwords = (nbytes + 3) >> 2;
/* Get the number of 32-bit words available in the TxFIFO. The DXTFSTS
* indicates the amount of free space available in the endpoint TxFIFO.
* Values are in terms of 32-bit words:
*
* 0: Endpoint TxFIFO is full
* 1: 1 word available
* 2: 2 words available
* n: n words available
*/
regaddr = STM32_OTG_DTXFSTS(privep->epphy);
/* Check for space in the TxFIFO. If space in the TxFIFO is not
* available, then set up an interrupt to resume the transfer when the
* TxFIFO is empty.
*/
regval = stm32_getreg(regaddr);
if ((int)(regval & OTG_DTXFSTS_MASK) < nwords)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_EMPWAIT),
(uint16_t) regval);
/* There is insufficient space in the TxFIFO. Wait for a TxFIFO
* empty interrupt and try again.
*/
uint32_t empmsk = stm32_getreg(STM32_OTG_DIEPEMPMSK);
empmsk |= OTG_DIEPEMPMSK(privep->epphy);
stm32_putreg(empmsk, STM32_OTG_DIEPEMPMSK);
/* Terminate the transfer. We will try again when the TxFIFO empty
* interrupt is received.
*/
return;
}
/* Transfer data to the TxFIFO */
buf = privreq->req.buf + privreq->req.xfrd;
stm32_epin_transfer(privep, buf, nbytes);
/* If it was not before, the OUT endpoint is now actively transferring
* data.
*/
privep->active = true;
/* EP0 is a special case */
if (privep->epphy == EP0)
{
priv->ep0state = EP0STATE_DATA_IN;
}
/* Update for the next time through the loop */
privreq->req.xfrd += nbytes;
}
/* Note that the ZLP, if any, must be sent as a separate transfer. The need
* for a ZLP is indicated by privep->zlp. If all of the bytes were sent
* (including any final null packet) then we are finished with the transfer
*/
if (privreq->req.xfrd >= privreq->req.len && !privep->zlp)
{
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
/* We are finished with the request (although the transfer has not yet
* completed).
*/
stm32_req_complete(privep, OK);
}
}
/****************************************************************************
* Name: stm32_rxfifo_read
*
* Description:
* Read packet from the RxFIFO into a read request.
*
****************************************************************************/
static void stm32_rxfifo_read(FAR struct stm32_ep_s *privep,
FAR uint8_t * dest, uint16_t len)
{
uint32_t regaddr;
int i;
/* Get the address of the RxFIFO. Note: there is only one RxFIFO so we might
* as well use the address associated with EP0.
*/
regaddr = STM32_OTG_DFIFO_DEP(EP0);
/* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */
for (i = 0; i < len; i += 4)
{
union
{
uint32_t w;
uint8_t b[4];
} data;
/* Read 1 x 32-bits of EP0 packet data */
data.w = stm32_getreg(regaddr);
/* Write 4 x 8-bits of EP0 packet data */
*dest++ = data.b[0];
*dest++ = data.b[1];
*dest++ = data.b[2];
*dest++ = data.b[3];
}
}
/****************************************************************************
* Name: stm32_rxfifo_discard
*
* Description:
* Discard packet data from the RxFIFO.
*
****************************************************************************/
static void stm32_rxfifo_discard(FAR struct stm32_ep_s *privep, int len)
{
if (len > 0)
{
uint32_t regaddr;
int i;
/* Get the address of the RxFIFO Note: there is only one RxFIFO so we
* might as well use the address associated with EP0.
*/
regaddr = STM32_OTG_DFIFO_DEP(EP0);
/* Read 32-bits at time */
for (i = 0; i < len; i += 4)
{
volatile uint32_t data = stm32_getreg(regaddr);
UNUSED(data);
}
}
}
/****************************************************************************
* Name: stm32_epout_complete
*
* Description:
* This function is called when an OUT transfer complete interrupt is
* received. It completes the read request at the head of the endpoint's
* request queue.
*
****************************************************************************/
static void stm32_epout_complete(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep)
{
struct stm32_req_s *privreq;
/* Since a transfer just completed, there must be a read request at the head
* of the endpoint request queue.
*/
privreq = stm32_rqpeek(privep);
DEBUGASSERT(privreq);
if (!privreq)
{
/* An OUT transfer completed, but no packet to receive the data. This
* should not happen.
*/
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY), privep->epphy);
privep->active = false;
return;
}
uinfo("EP%d: len=%d xfrd=%d\n",
privep->epphy, privreq->req.len, privreq->req.xfrd);
/* Return the completed read request to the class driver and mark the state
* IDLE.
*/
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
stm32_req_complete(privep, OK);
privep->active = false;
/* Now set up the next read request (if any) */
stm32_epout_request(priv, privep);
}
/****************************************************************************
* Name: stm32_ep0out_receive
*
* Description:
* This function is called from the RXFLVL interrupt handler when new incoming
* data is available in the endpoint's RxFIFO. This function will simply
* copy the incoming data into pending request's data buffer.
*
****************************************************************************/
static inline void stm32_ep0out_receive(FAR struct stm32_ep_s *privep, int bcnt)
{
FAR struct stm32_usbdev_s *priv;
/* Sanity Checking */
DEBUGASSERT(privep && privep->ep.priv);
priv = (FAR struct stm32_usbdev_s *)privep->ep.priv;
uinfo("EP0: bcnt=%d\n", bcnt);
usbtrace(TRACE_READ(EP0), bcnt);
/* Verify that an OUT SETUP request as received before this data was received
* in the RxFIFO.
*/
if (priv->ep0state == EP0STATE_SETUP_OUT)
{
/* Read the data into our special buffer for SETUP data */
int readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, bcnt);
stm32_rxfifo_read(privep, priv->ep0data, readlen);
/* Do we have to discard any excess bytes? */
stm32_rxfifo_discard(privep, bcnt - readlen);
/* Now we can process the setup command */
privep->active = false;
priv->ep0state = EP0STATE_SETUP_READY;
priv->ep0datlen = readlen;
stm32_ep0out_setup(priv);
}
else
{
/* This is an error. We don't have any idea what to do with the EP0 data
* in this case. Just read and discard it so that the RxFIFO does not
* become constipated.
*/
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOOUTSETUP), priv->ep0state);
stm32_rxfifo_discard(privep, bcnt);
privep->active = false;
}
}
/****************************************************************************
* Name: stm32_epout_receive
*
* Description:
* This function is called from the RXFLVL interrupt handler when new incoming
* data is available in the endpoint's RxFIFO. This function will simply
* copy the incoming data into pending request's data buffer.
*
****************************************************************************/
static inline void stm32_epout_receive(FAR struct stm32_ep_s *privep, int bcnt)
{
struct stm32_req_s *privreq;
uint8_t *dest;
int buflen;
int readlen;
/* Get a reference to the request at the head of the endpoint's request
* queue.
*/
privreq = stm32_rqpeek(privep);
if (!privreq)
{
/* Incoming data is available in the RxFIFO, but there is no read setup
* to receive the receive the data. This should not happen for data
* endpoints; those endpoints should have been NAKing any OUT data
* tokens.
*
* We should get here normally on OUT data phase following an OUT
* SETUP command. EP0 data will still receive data in this case and
* it should not be NAKing.
*/
if (privep->epphy == 0)
{
stm32_ep0out_receive(privep, bcnt);
}
else
{
/* Otherwise, the data is lost. This really should not happen if
* NAKing is working as expected.
*/
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY), privep->epphy);
/* Discard the data in the RxFIFO */
stm32_rxfifo_discard(privep, bcnt);
}
privep->active = false;
return;
}
uinfo("EP%d: len=%d xfrd=%d\n", privep->epphy, privreq->req.len,
privreq->req.xfrd);
usbtrace(TRACE_READ(privep->epphy), bcnt);
/* Get the number of bytes to transfer from the RxFIFO */
buflen = privreq->req.len - privreq->req.xfrd;
DEBUGASSERT(buflen > 0 && buflen >= bcnt);
readlen = MIN(buflen, bcnt);
/* Get the destination of the data transfer */
dest = privreq->req.buf + privreq->req.xfrd;
/* Transfer the data from the RxFIFO to the request's data buffer */
stm32_rxfifo_read(privep, dest, readlen);
/* If there were more bytes in the RxFIFO than could be held in the read
* request, then we will have to discard those.
*/
stm32_rxfifo_discard(privep, bcnt - readlen);
/* Update the number of bytes transferred */
privreq->req.xfrd += readlen;
}
/****************************************************************************
* Name: stm32_epout_request
*
* Description:
* This function is called when either (1) new read request is received, or
* (2) a pending receive request completes. If there is no read in pending,
* then this function will initiate the next OUT (read) operation.
*
****************************************************************************/
static void stm32_epout_request(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep)
{
struct stm32_req_s *privreq;
uint32_t regaddr;
uint32_t regval;
uint32_t xfrsize;
uint32_t pktcnt;
/* Make sure that there is not already a pending request request. If there
* is, just return, leaving the newly received request in the request queue.
*/
if (!privep->active)
{
/* Loop until a valid request is found (or the request queue is empty).
* The loop is only need to look at the request queue again is an invalid
* read request is encountered.
*/
for (; ; )
{
/* Get a reference to the request at the head of the endpoint's
* request queue
*/
privreq = stm32_rqpeek(privep);
if (!privreq)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY),
privep->epphy);
/* There are no read requests to be setup. Configure the
* hardware to NAK any incoming packets. (This should already be
* the case. I think that the hardware will automatically NAK
* after a transfer is completed until SNAK is cleared).
*/
regaddr = STM32_OTG_DOEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
regval |= OTG_DOEPCTL_SNAK;
stm32_putreg(regval, regaddr);
/* This endpoint is no longer actively transferring */
privep->active = false;
return;
}
uinfo("EP%d: len=%d\n", privep->epphy, privreq->req.len);
/* Ignore any attempt to receive a zero length packet (this really
* should not happen.
*/
if (privreq->req.len <= 0)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTNULLPACKET), 0);
stm32_req_complete(privep, OK);
}
/* Otherwise, we have a usable read request... break out of the loop */
else
{
break;
}
}
/* Setup the pending read into the request buffer. First calculate:
*
* pktcnt = the number of packets (of maxpacket bytes) required to
* perform the transfer.
* xfrsize = The total number of bytes required (in units of maxpacket
* bytes).
*/
pktcnt =
(privreq->req.len + (privep->ep.maxpacket - 1)) / privep->ep.maxpacket;
xfrsize = pktcnt * privep->ep.maxpacket;
/* Then setup the hardware to perform this transfer */
regaddr = STM32_OTG_DOEPTSIZ(privep->epphy);
regval = stm32_getreg(regaddr);
regval &= ~(OTG_DOEPTSIZ_XFRSIZ_MASK | OTG_DOEPTSIZ_PKTCNT_MASK);
regval |= (xfrsize << OTG_DOEPTSIZ_XFRSIZ_SHIFT);
regval |= (pktcnt << OTG_DOEPTSIZ_PKTCNT_SHIFT);
stm32_putreg(regval, regaddr);
/* Then enable the transfer */
regaddr = STM32_OTG_DOEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
/* When an isochronous transfer is enabled the Even/Odd frame bit must
* also be set appropriately.
*/
# ifdef CONFIG_USBDEV_ISOCHRONOUS
if (privep->eptype == USB_EP_ATTR_XFER_ISOC)
{
if (privep->odd)
{
regval |= OTG_DOEPCTL_SODDFRM;
}
else
{
regval |= OTG_DOEPCTL_SEVNFRM;
}
}
# endif
/* Clearing NAKing and enable the transfer. */
regval |= (OTG_DOEPCTL_CNAK | OTG_DOEPCTL_EPENA);
stm32_putreg(regval, regaddr);
/* A transfer is now active on this endpoint */
privep->active = true;
/* EP0 is a special case. We need to know when to switch back to normal
* SETUP processing.
*/
if (privep->epphy == EP0)
{
priv->ep0state = EP0STATE_DATA_OUT;
}
}
}
/****************************************************************************
* Name: stm32_ep_flush
*
* Description:
* Flush any primed descriptors from this ep
*
****************************************************************************/
static void stm32_ep_flush(struct stm32_ep_s *privep)
{
if (privep->isin)
{
stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_D(privep->epphy));
}
else
{
stm32_rxfifo_flush();
}
}
/****************************************************************************
* Name: stm32_req_complete
*
* Description:
* Handle termination of the request at the head of the endpoint request queue.
*
****************************************************************************/
static void stm32_req_complete(struct stm32_ep_s *privep, int16_t result)
{
FAR struct stm32_req_s *privreq;
/* Remove the request at the head of the request list */
privreq = stm32_req_remfirst(privep);
DEBUGASSERT(privreq != NULL);
/* If endpoint 0, temporarily reflect the state of protocol stalled in the
* callback.
*/
bool stalled = privep->stalled;
if (privep->epphy == EP0)
{
privep->stalled = privep->dev->stalled;
}
/* Save the result in the request structure */
privreq->req.result = result;
/* Callback to the request completion handler */
privreq->req.callback(&privep->ep, &privreq->req);
/* Restore the stalled indication */
privep->stalled = stalled;
}
/****************************************************************************
* Name: stm32_req_cancel
*
* Description:
* Cancel all pending requests for an endpoint
*
****************************************************************************/
static void stm32_req_cancel(struct stm32_ep_s *privep, int16_t status)
{
if (!stm32_rqempty(privep))
{
stm32_ep_flush(privep);
}
while (!stm32_rqempty(privep))
{
usbtrace(TRACE_COMPLETE(privep->epphy), (stm32_rqpeek(privep))->req.xfrd);
stm32_req_complete(privep, status);
}
}
/****************************************************************************
* Name: stm32_ep_findbyaddr
*
* Description:
* Find the physical endpoint structure corresponding to a logic endpoint
* address
*
****************************************************************************/
static struct stm32_ep_s *stm32_ep_findbyaddr(struct stm32_usbdev_s *priv,
uint16_t eplog)
{
struct stm32_ep_s *privep;
uint8_t epphy = USB_EPNO(eplog);
if (epphy >= STM32_NENDPOINTS)
{
return NULL;
}
/* Is this an IN or an OUT endpoint? */
if (USB_ISEPIN(eplog))
{
privep = &priv->epin[epphy];
}
else
{
privep = &priv->epout[epphy];
}
/* Return endpoint reference */
DEBUGASSERT(privep->epphy == epphy);
return privep;
}
/****************************************************************************
* Name: stm32_req_dispatch
*
* Description:
* Provide unhandled setup actions to the class driver. This is logically part
* of the USB interrupt handler.
*
****************************************************************************/
static int stm32_req_dispatch(struct stm32_usbdev_s *priv,
const struct usb_ctrlreq_s *ctrl)
{
int ret = -EIO;
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DISPATCH), 0);
if (priv->driver)
{
/* Forward to the control request to the class driver implementation */
ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl,
priv->ep0data, priv->ep0datlen);
}
if (ret < 0)
{
/* Stall on failure */
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DISPATCHSTALL), 0);
priv->stalled = true;
}
return ret;
}
/****************************************************************************
* Name: stm32_usbreset
*
* Description:
* Reset Usb engine
*
****************************************************************************/
static void stm32_usbreset(struct stm32_usbdev_s *priv)
{
FAR struct stm32_ep_s *privep;
uint32_t regval;
int i;
/* Clear the Remote Wake-up Signaling */
regval = stm32_getreg(STM32_OTG_DCTL);
regval &= ~OTG_DCTL_RWUSIG;
stm32_putreg(regval, STM32_OTG_DCTL);
/* Flush the EP0 Tx FIFO */
stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_D(EP0));
/* Tell the class driver that we are disconnected. The class driver should
* then accept any new configurations.
*/
if (priv->driver)
{
CLASS_DISCONNECT(priv->driver, &priv->usbdev);
}
/* Mark all endpoints as available */
priv->epavail[0] = STM32_EP_AVAILABLE;
priv->epavail[1] = STM32_EP_AVAILABLE;
/* Disable all end point interrupts */
for (i = 0; i < STM32_NENDPOINTS; i++)
{
/* Disable endpoint interrupts */
stm32_putreg(0xff, STM32_OTG_DIEPINT(i));
stm32_putreg(0xff, STM32_OTG_DOEPINT(i));
/* Return write requests to the class implementation */
privep = &priv->epin[i];
stm32_req_cancel(privep, -ESHUTDOWN);
/* Reset IN endpoint status */
privep->stalled = false;
/* Return read requests to the class implementation */
privep = &priv->epout[i];
stm32_req_cancel(privep, -ESHUTDOWN);
/* Reset endpoint status */
privep->stalled = false;
}
stm32_putreg(0xffffffff, STM32_OTG_DAINT);
/* Mask all device endpoint interrupts except EP0 */
regval = (OTG_DAINT_IEP(EP0) | OTG_DAINT_OEP(EP0));
stm32_putreg(regval, STM32_OTG_DAINTMSK);
/* Unmask OUT interrupts */
regval = (OTG_DOEPMSK_XFRCM | OTG_DOEPMSK_STUPM | OTG_DOEPMSK_EPDM);
stm32_putreg(regval, STM32_OTG_DOEPMSK);
/* Unmask IN interrupts */
regval = (OTG_DIEPMSK_XFRCM | OTG_DIEPMSK_EPDM | OTG_DIEPMSK_TOM);
stm32_putreg(regval, STM32_OTG_DIEPMSK);
/* Reset device address to 0 */
stm32_setaddress(priv, 0);
priv->devstate = DEVSTATE_DEFAULT;
# if defined(CONFIG_STM32F7_INTERNAL_ULPI) || defined(CONFIG_STM32F7_EXTERNAL_ULPI)
priv->usbdev.speed = USB_SPEED_HIGH;
# else
priv->usbdev.speed = USB_SPEED_FULL;
# endif
/* Re-configure EP0 */
stm32_ep0_configure(priv);
/* Setup EP0 to receive SETUP packets */
stm32_ep0out_ctrlsetup(priv);
}
/****************************************************************************
* Name: stm32_ep0out_testmode
*
* Description:
* Select test mode
*
****************************************************************************/
static inline void stm32_ep0out_testmode(FAR struct stm32_usbdev_s *priv,
uint16_t index)
{
uint8_t testmode;
testmode = index >> 8;
switch (testmode)
{
case 1:
priv->testmode = OTG_TESTMODE_J;
break;
case 2:
priv->testmode = OTG_TESTMODE_K;
break;
case 3:
priv->testmode = OTG_TESTMODE_SE0_NAK;
break;
case 4:
priv->testmode = OTG_TESTMODE_PACKET;
break;
case 5:
priv->testmode = OTG_TESTMODE_FORCE;
break;
default:
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADTESTMODE), testmode);
priv->dotest = false;
priv->testmode = OTG_TESTMODE_DISABLED;
priv->stalled = true;
}
priv->dotest = true;
stm32_ep0in_transmitzlp(priv);
}
/****************************************************************************
* Name: stm32_ep0out_stdrequest
*
* Description:
* Handle a stanard request on EP0. Pick off the things of interest to the
* USB device controller driver; pass what is left to the class driver.
*
****************************************************************************/
static inline void stm32_ep0out_stdrequest(struct stm32_usbdev_s *priv,
FAR struct stm32_ctrlreq_s *ctrlreq)
{
FAR struct stm32_ep_s *privep;
/* Handle standard request */
switch (ctrlreq->req)
{
case USB_REQ_GETSTATUS:
{
/* type: device-to-host; recipient = device, interface, endpoint
* value: 0
* index: zero interface endpoint
* len: 2; data = status
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSTATUS), 0);
if (!priv->addressed ||
ctrlreq->len != 2 ||
USB_REQ_ISOUT(ctrlreq->type) || ctrlreq->value != 0)
{
priv->stalled = true;
}
else
{
switch (ctrlreq->type & USB_REQ_RECIPIENT_MASK)
{
case USB_REQ_RECIPIENT_ENDPOINT:
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPGETSTATUS), 0);
privep = stm32_ep_findbyaddr(priv, ctrlreq->index);
if (!privep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS),
0);
priv->stalled = true;
}
else
{
if (privep->stalled)
{
priv->ep0data[0] = (1 << USB_FEATURE_ENDPOINTHALT);
}
else
{
priv->ep0data[0] = 0; /* Not stalled */
}
priv->ep0data[1] = 0;
stm32_ep0in_setupresponse(priv, priv->ep0data, 2);
}
}
break;
case USB_REQ_RECIPIENT_DEVICE:
{
if (ctrlreq->index == 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVGETSTATUS),
0);
/* Features: Remote Wakeup and self-powered */
priv->ep0data[0] =
(priv->selfpowered << USB_FEATURE_SELFPOWERED);
priv->ep0data[0] |=
(priv->wakeup << USB_FEATURE_REMOTEWAKEUP);
priv->ep0data[1] = 0;
stm32_ep0in_setupresponse(priv, priv->ep0data, 2);
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADDEVGETSTATUS),
0);
priv->stalled = true;
}
}
break;
case USB_REQ_RECIPIENT_INTERFACE:
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IFGETSTATUS), 0);
priv->ep0data[0] = 0;
priv->ep0data[1] = 0;
stm32_ep0in_setupresponse(priv, priv->ep0data, 2);
}
break;
default:
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSTATUS), 0);
priv->stalled = true;
}
break;
}
}
}
break;
case USB_REQ_CLEARFEATURE:
{
/* type: host-to-device; recipient = device, interface or endpoint
* value: feature selector
* index: zero interface endpoint;
* len: zero, data = none
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_CLEARFEATURE), 0);
if (priv->addressed != 0 && ctrlreq->len == 0)
{
uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK;
if (recipient == USB_REQ_RECIPIENT_ENDPOINT &&
ctrlreq->value == USB_FEATURE_ENDPOINTHALT &&
(privep = stm32_ep_findbyaddr(priv, ctrlreq->index)) != NULL)
{
stm32_ep_clrstall(privep);
stm32_ep0in_transmitzlp(priv);
}
else if (recipient == USB_REQ_RECIPIENT_DEVICE &&
ctrlreq->value == USB_FEATURE_REMOTEWAKEUP)
{
priv->wakeup = 0;
stm32_ep0in_transmitzlp(priv);
}
else
{
/* Actually, I think we could just stall here. */
stm32_req_dispatch(priv, &priv->ctrlreq);
}
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADCLEARFEATURE), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_SETFEATURE:
{
/* type: host-to-device; recipient = device, interface, endpoint
* value: feature selector
* index: zero interface endpoint;
* len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETFEATURE), 0);
if (priv->addressed != 0 && ctrlreq->len == 0)
{
uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK;
if (recipient == USB_REQ_RECIPIENT_ENDPOINT &&
ctrlreq->value == USB_FEATURE_ENDPOINTHALT &&
(privep = stm32_ep_findbyaddr(priv, ctrlreq->index)) != NULL)
{
stm32_ep_setstall(privep);
stm32_ep0in_transmitzlp(priv);
}
else if (recipient == USB_REQ_RECIPIENT_DEVICE &&
ctrlreq->value == USB_FEATURE_REMOTEWAKEUP)
{
priv->wakeup = 1;
stm32_ep0in_transmitzlp(priv);
}
else if (recipient == USB_REQ_RECIPIENT_DEVICE &&
ctrlreq->value == USB_FEATURE_TESTMODE &&
((ctrlreq->index & 0xff) == 0))
{
stm32_ep0out_testmode(priv, ctrlreq->index);
}
else if (priv->configured)
{
/* Actually, I think we could just stall here. */
stm32_req_dispatch(priv, &priv->ctrlreq);
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0);
priv->stalled = true;
}
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_SETADDRESS:
{
/* type: host-to-device; recipient = device
* value: device address
* index: 0
* len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETADDRESS), ctrlreq->value);
if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE
&& ctrlreq->index == 0 && ctrlreq->len == 0 && ctrlreq->value < 128
&& priv->devstate != DEVSTATE_CONFIGURED)
{
/* Save the address. We cannot actually change to the next address
* until the completion of the status phase.
*/
stm32_setaddress(priv, (uint16_t) priv->ctrlreq.value[0]);
stm32_ep0in_transmitzlp(priv);
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETADDRESS), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_GETDESCRIPTOR:
/* type: device-to-host; recipient = device, interface
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
case USB_REQ_SETDESCRIPTOR:
/* type: host-to-device; recipient = device
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETDESC), 0);
if (((ctrlreq->type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE) ||
((ctrlreq->type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_INTERFACE))
{
stm32_req_dispatch(priv, &priv->ctrlreq);
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSETDESC), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_GETCONFIGURATION:
/* type: device-to-host; recipient = device
* value: 0;
* index: 0;
* len: 1; data = configuration value
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETCONFIG), 0);
if (priv->addressed &&
(ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE
&& ctrlreq->value == 0 && ctrlreq->index == 0 && ctrlreq->len == 1)
{
stm32_req_dispatch(priv, &priv->ctrlreq);
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETCONFIG), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_SETCONFIGURATION:
/* type: host-to-device; recipient = device
* value: configuration value
* index: 0;
* len: 0; data = none
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETCONFIG), 0);
if (priv->addressed &&
(ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE
&& ctrlreq->index == 0 && ctrlreq->len == 0)
{
/* Give the configuration to the class driver */
int ret = stm32_req_dispatch(priv, &priv->ctrlreq);
/* If the class driver accepted the configuration, then mark the
* device state as configured (or not, depending on the
* configuration).
*/
if (ret == OK)
{
uint8_t cfg = (uint8_t) ctrlreq->value;
if (cfg != 0)
{
priv->devstate = DEVSTATE_CONFIGURED;
priv->configured = true;
}
else
{
priv->devstate = DEVSTATE_ADDRESSED;
priv->configured = false;
}
}
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETCONFIG), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_GETINTERFACE:
/* type: device-to-host; recipient = interface
* value: 0
* index: interface;
* len: 1; data = alt interface
*/
case USB_REQ_SETINTERFACE:
/* type: host-to-device; recipient = interface
* value: alternate setting
* index: interface;
* len: 0; data = none
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETIF), 0);
stm32_req_dispatch(priv, &priv->ctrlreq);
}
break;
case USB_REQ_SYNCHFRAME:
/* type: device-to-host; recipient = endpoint
* value: 0
* index: endpoint;
* len: 2; data = frame number
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SYNCHFRAME), 0);
}
break;
default:
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDCTRLREQ), 0);
priv->stalled = true;
}
break;
}
}
/****************************************************************************
* Name: stm32_ep0out_setup
*
* Description:
* USB Ctrl EP Setup Event. This is logically part of the USB interrupt
* handler. This event occurs when a setup packet is receive on EP0 OUT.
*
****************************************************************************/
static inline void stm32_ep0out_setup(struct stm32_usbdev_s *priv)
{
struct stm32_ctrlreq_s ctrlreq;
/* Verify that a SETUP was received */
if (priv->ep0state != EP0STATE_SETUP_READY)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0NOSETUP), priv->ep0state);
return;
}
/* Terminate any pending requests */
stm32_req_cancel(&priv->epout[EP0], -EPROTO);
stm32_req_cancel(&priv->epin[EP0], -EPROTO);
/* Assume NOT stalled */
priv->epout[EP0].stalled = false;
priv->epin[EP0].stalled = false;
priv->stalled = false;
/* Starting to process a control request - update state */
priv->ep0state = EP0STATE_SETUP_PROCESS;
/* And extract the little-endian 16-bit values to host order */
ctrlreq.type = priv->ctrlreq.type;
ctrlreq.req = priv->ctrlreq.req;
ctrlreq.value = GETUINT16(priv->ctrlreq.value);
ctrlreq.index = GETUINT16(priv->ctrlreq.index);
ctrlreq.len = GETUINT16(priv->ctrlreq.len);
uinfo("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
ctrlreq.type, ctrlreq.req, ctrlreq.value, ctrlreq.index, ctrlreq.len);
/* Check for a standard request */
if ((ctrlreq.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
/* Dispatch any non-standard requests */
stm32_req_dispatch(priv, &priv->ctrlreq);
}
else
{
/* Handle standard requests. */
stm32_ep0out_stdrequest(priv, &ctrlreq);
}
/* Check if the setup processing resulted in a STALL */
if (priv->stalled)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED), priv->ep0state);
stm32_ep0_stall(priv);
}
/* Reset state/data associated with the SETUP request */
priv->ep0datlen = 0;
}
/****************************************************************************
* Name: stm32_epout
*
* Description:
* This is part of the OUT endpoint interrupt processing. This function
* handles the OUT event for a single endpoint.
*
****************************************************************************/
static inline void stm32_epout(FAR struct stm32_usbdev_s *priv, uint8_t epno)
{
FAR struct stm32_ep_s *privep;
/* Endpoint 0 is a special case. */
if (epno == 0)
{
privep = &priv->epout[EP0];
/* In the EP0STATE_DATA_OUT state, we are receiving data into the request
* buffer. In that case, we must continue the request processing.
*/
if (priv->ep0state == EP0STATE_DATA_OUT)
{
/* Continue processing data from the EP0 OUT request queue */
stm32_epout_complete(priv, privep);
/* If we are not actively processing an OUT request, then we need to
* setup to receive the next control request.
*/
if (!privep->active)
{
stm32_ep0out_ctrlsetup(priv);
priv->ep0state = EP0STATE_IDLE;
}
}
}
/* For other endpoints, the only possibility is that we are continuing or
* finishing an OUT request.
*/
else if (priv->devstate == DEVSTATE_CONFIGURED)
{
stm32_epout_complete(priv, &priv->epout[epno]);
}
}
/****************************************************************************
* Name: stm32_epout_interrupt
*
* Description:
* USB OUT endpoint interrupt handler. The core generates this interrupt when
* there is an interrupt is pending on one of the OUT endpoints of the core.
* The driver must read the OTG DAINT register to determine the exact number
* of the OUT endpoint on which the interrupt occurred, and then read the
* corresponding OTG DOEPINTx register to determine the exact cause of the
* interrupt.
*
****************************************************************************/
static inline void stm32_epout_interrupt(FAR struct stm32_usbdev_s *priv)
{
uint32_t daint;
uint32_t regval;
uint32_t doepint;
int epno;
/* Get the pending, enabled interrupts for the OUT endpoint from the endpoint
* interrupt status register.
*/
regval = stm32_getreg(STM32_OTG_DAINT);
regval &= stm32_getreg(STM32_OTG_DAINTMSK);
daint = (regval & OTG_DAINT_OEP_MASK) >> OTG_DAINT_OEP_SHIFT;
if (daint == 0)
{
/* We got an interrupt, but there is no unmasked endpoint that caused
* it?! When this happens, the interrupt flag never gets cleared and
* we are stuck in infinite interrupt loop.
*
* This shouldn't happen if we are diligent about handling timing
* issues when masking endpoint interrupts. However, this workaround
* avoids infinite loop and allows operation to continue normally.
* It works by clearing each endpoint flags, masked or not.
*/
regval = stm32_getreg(STM32_OTG_DAINT);
daint = (regval & OTG_DAINT_OEP_MASK) >> OTG_DAINT_OEP_SHIFT;
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTUNEXPECTED),
(uint16_t) regval);
epno = 0;
while (daint)
{
if ((daint & 1) != 0)
{
regval = stm32_getreg(STM32_OTG_DOEPINT(epno));
uerr("DOEPINT(%d) = %08x\n", epno, regval);
stm32_putreg(0xff, STM32_OTG_DOEPINT(epno));
}
epno++;
daint >>= 1;
}
return;
}
/* Process each pending IN endpoint interrupt */
epno = 0;
while (daint)
{
/* Is an OUT interrupt pending for this endpoint? */
if ((daint & 1) != 0)
{
/* Yes.. get the OUT endpoint interrupt status */
doepint = stm32_getreg(STM32_OTG_DOEPINT(epno));
doepint &= stm32_getreg(STM32_OTG_DOEPMSK);
/* Transfer completed interrupt. This interrupt is triggered when
* stm32_rxinterrupt() removes the last packet data from the RxFIFO.
* In this case, core internally sets the NAK bit for this endpoint
* to prevent it from receiving any more packets.
*/
if ((doepint & OTG_DOEPINT_XFRC) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_XFRC),
(uint16_t) doepint);
/* Clear the bit in DOEPINTn for this interrupt */
stm32_putreg(OTG_DOEPINT_XFRC, STM32_OTG_DOEPINT(epno));
/* Handle the RX transfer data ready event */
stm32_epout(priv, epno);
}
/* Endpoint disabled interrupt (ignored because this interrupt is
* used in polled mode by the endpoint disable logic).
*/
# if 1
/* REVISIT: */
if ((doepint & OTG_DOEPINT_EPDISD) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_EPDISD),
(uint16_t) doepint);
/* Clear the bit in DOEPINTn for this interrupt */
stm32_putreg(OTG_DOEPINT_EPDISD, STM32_OTG_DOEPINT(epno));
}
# endif
/* Setup Phase Done (control EPs) */
if ((doepint & OTG_DOEPINT_SETUP) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_SETUP),
priv->ep0state);
/* Handle the receipt of the IN SETUP packets now (OUT setup
* packet processing may be delayed until the accompanying OUT
* DATA is received)
*/
if (priv->ep0state == EP0STATE_SETUP_READY)
{
stm32_ep0out_setup(priv);
}
stm32_putreg(OTG_DOEPINT_SETUP, STM32_OTG_DOEPINT(epno));
}
}
epno++;
daint >>= 1;
}
}
/****************************************************************************
* Name: stm32_epin_runtestmode
*
* Description:
* Execute the test mode setup by the SET FEATURE request
*
****************************************************************************/
static inline void stm32_epin_runtestmode(FAR struct stm32_usbdev_s *priv)
{
uint32_t regval = stm32_getreg(STM32_OTG_DCTL);
regval &= OTG_DCTL_TCTL_MASK;
regval |= (uint32_t) priv->testmode << OTG_DCTL_TCTL_SHIFT;
stm32_putreg(regval, STM32_OTG_DCTL);
priv->dotest = 0;
priv->testmode = OTG_TESTMODE_DISABLED;
}
/****************************************************************************
* Name: stm32_epin
*
* Description:
* This is part of the IN endpoint interrupt processing. This function
* handles the IN event for a single endpoint.
*
****************************************************************************/
static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno)
{
FAR struct stm32_ep_s *privep = &priv->epin[epno];
/* Endpoint 0 is a special case. */
if (epno == 0)
{
/* In the EP0STATE_DATA_IN state, we are sending data from request
* buffer. In that case, we must continue the request processing
*/
if (priv->ep0state == EP0STATE_DATA_IN)
{
/* Continue processing data from the EP0 OUT request queue */
stm32_epin_request(priv, privep);
/* If we are not actively processing an OUT request, then we need to
* setup to receive the next control request.
*/
if (!privep->active)
{
stm32_ep0out_ctrlsetup(priv);
priv->ep0state = EP0STATE_IDLE;
}
}
/* Test mode is another special case */
if (priv->dotest)
{
stm32_epin_runtestmode(priv);
}
}
/* For other endpoints, the only possibility is that we are continuing or
* finishing an IN request.
*/
else if (priv->devstate == DEVSTATE_CONFIGURED)
{
/* Continue processing data from the endpoint write request queue */
stm32_epin_request(priv, privep);
}
}
/****************************************************************************
* Name: stm32_epin_txfifoempty
*
* Description:
* TxFIFO empty interrupt handling
*
****************************************************************************/
static inline void stm32_epin_txfifoempty(FAR struct stm32_usbdev_s *priv,
int epno)
{
FAR struct stm32_ep_s *privep = &priv->epin[epno];
/* Continue processing the write request queue. This may mean sending more
* data from the existing request or terminating the current requests and
* (perhaps) starting the IN transfer from the next write request.
*/
stm32_epin_request(priv, privep);
}
/****************************************************************************
* Name: stm32_epin_interrupt
*
* Description:
* USB IN endpoint interrupt handler. The core generates this interrupt when
* an interrupt is pending on one of the IN endpoints of the core. The driver
* must read the OTG DAINT register to determine the exact number of the IN
* endpoint on which the interrupt occurred, and then read the corresponding
* OTG DIEPINTx register to determine the exact cause of the interrupt.
*
****************************************************************************/
static inline void stm32_epin_interrupt(FAR struct stm32_usbdev_s *priv)
{
uint32_t diepint;
uint32_t daint;
uint32_t mask;
uint32_t empty;
int epno;
/* Get the pending, enabled interrupts for the IN endpoint from the endpoint
* interrupt status register.
*/
daint = stm32_getreg(STM32_OTG_DAINT);
daint &= stm32_getreg(STM32_OTG_DAINTMSK);
daint &= OTG_DAINT_IEP_MASK;
if (daint == 0)
{
/* We got an interrupt, but there is no unmasked endpoint that caused
* it?! When this happens, the interrupt flag never gets cleared and
* we are stuck in infinite interrupt loop.
*
* This shouldn't happen if we are diligent about handling timing
* issues when masking endpoint interrupts. However, this workaround
* avoids infinite loop and allows operation to continue normally.
* It works by clearing each endpoint flags, masked or not.
*/
daint = stm32_getreg(STM32_OTG_DAINT);
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPINUNEXPECTED), (uint16_t) daint);
daint &= OTG_DAINT_IEP_MASK;
epno = 0;
while (daint)
{
if ((daint & 1) != 0)
{
uerr("DIEPINT(%d) = %08x\n",
epno, stm32_getreg(STM32_OTG_DIEPINT(epno)));
stm32_putreg(0xff, STM32_OTG_DIEPINT(epno));
}
epno++;
daint >>= 1;
}
return;
}
/* Process each pending IN endpoint interrupt */
epno = 0;
while (daint)
{
/* Is an IN interrupt pending for this endpoint? */
if ((daint & 1) != 0)
{
/* Get IN interrupt mask register. Bits 0-6 correspond to enabled
* interrupts as will be found in the DIEPINT interrupt status
* register.
*/
mask = stm32_getreg(STM32_OTG_DIEPMSK);
/* Check if the TxFIFO not empty interrupt is enabled for this
* endpoint in the DIEPMSK register. Bits n corresponds to endpoint
* n in the register. That condition corresponds to bit 7 of the
* DIEPINT interrupt status register. There is no TXFE bit in the
* mask register, so we fake one here.
*/
empty = stm32_getreg(STM32_OTG_DIEPEMPMSK);
if ((empty & OTG_DIEPEMPMSK(epno)) != 0)
{
mask |= OTG_DIEPINT_TXFE;
}
/* Now, read the interrupt status and mask out all disabled
* interrupts.
*/
diepint = stm32_getreg(STM32_OTG_DIEPINT(epno)) & mask;
/* Decode and process the enabled, pending interrupts */
/* Transfer completed interrupt */
if ((diepint & OTG_DIEPINT_XFRC) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_XFRC),
(uint16_t) diepint);
/* It is possible that logic may be waiting for a the TxFIFO to
* become empty. We disable the TxFIFO empty interrupt here; it
* will be re-enabled if there is still insufficient space in the
* TxFIFO.
*/
empty &= ~OTG_DIEPEMPMSK(epno);
stm32_putreg(empty, STM32_OTG_DIEPEMPMSK);
stm32_putreg(OTG_DIEPINT_XFRC, STM32_OTG_DIEPINT(epno));
/* IN transfer complete */
stm32_epin(priv, epno);
}
/* Timeout condition */
if ((diepint & OTG_DIEPINT_TOC) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_TOC),
(uint16_t) diepint);
stm32_putreg(OTG_DIEPINT_TOC, STM32_OTG_DIEPINT(epno));
}
/* IN token received when TxFIFO is empty. Applies to non-periodic
* IN endpoints only. This interrupt indicates that an IN token was
* received when the associated TxFIFO (periodic/non-periodic) was
* empty. This interrupt is asserted on the endpoint for which the IN
* token was received.
*/
if ((diepint & OTG_DIEPINT_ITTXFE) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_ITTXFE),
(uint16_t) diepint);
stm32_epin_request(priv, &priv->epin[epno]);
stm32_putreg(OTG_DIEPINT_ITTXFE, STM32_OTG_DIEPINT(epno));
}
/* IN endpoint NAK effective (ignored as this used only in polled
* mode)
*/
# if 0
if ((diepint & OTG_DIEPINT_INEPNE) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_INEPNE),
(uint16_t) diepint);
stm32_putreg(OTG_DIEPINT_INEPNE, STM32_OTG_DIEPINT(epno));
}
# endif
/* Endpoint disabled interrupt (ignored as this used only in polled
* mode)
*/
# if 0
if ((diepint & OTG_DIEPINT_EPDISD) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_EPDISD),
(uint16_t) diepint);
stm32_putreg(OTG_DIEPINT_EPDISD, STM32_OTG_DIEPINT(epno));
}
# endif
/* Transmit FIFO empty */
if ((diepint & OTG_DIEPINT_TXFE) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_TXFE),
(uint16_t) diepint);
/* If we were waiting for TxFIFO to become empty, the we might
* have both XFRC and TXFE interrupts pending. Since we do the
* same thing for both cases, ignore the TXFE if we have already
* processed the XFRC.
*/
if ((diepint & OTG_DIEPINT_XFRC) == 0)
{
/* Mask further FIFO empty interrupts. This will be
* re-enabled whenever we need to wait for a FIFO event.
*/
empty &= ~OTG_DIEPEMPMSK(epno);
stm32_putreg(empty, STM32_OTG_DIEPEMPMSK);
/* Handle TxFIFO empty */
stm32_epin_txfifoempty(priv, epno);
}
/* Clear the pending TxFIFO empty interrupt */
stm32_putreg(OTG_DIEPINT_TXFE, STM32_OTG_DIEPINT(epno));
}
}
epno++;
daint >>= 1;
}
}
/****************************************************************************
* Name: stm32_resumeinterrupt
*
* Description:
* Resume/remote wakeup detected interrupt
*
****************************************************************************/
static inline void stm32_resumeinterrupt(FAR struct stm32_usbdev_s *priv)
{
uint32_t regval;
/* Restart the PHY clock and un-gate USB core clock (HCLK) */
# ifdef CONFIG_USBDEV_LOWPOWER
regval = stm32_getreg(STM32_OTG_PCGCCTL);
regval &= ~(OTG_PCGCCTL_STPPCLK | OTG_PCGCCTL_GATEHCLK);
stm32_putreg(regval, STM32_OTG_PCGCCTL);
# endif
/* Clear remote wake-up signaling */
regval = stm32_getreg(STM32_OTG_DCTL);
regval &= ~OTG_DCTL_RWUSIG;
stm32_putreg(regval, STM32_OTG_DCTL);
/* Restore full power -- whatever that means for this particular board */
stm32_usbsuspend((struct usbdev_s *)priv, true);
/* Notify the class driver of the resume event */
if (priv->driver)
{
CLASS_RESUME(priv->driver, &priv->usbdev);
}
}
/****************************************************************************
* Name: stm32_suspendinterrupt
*
* Description:
* USB suspend interrupt
*
****************************************************************************/
static inline void stm32_suspendinterrupt(FAR struct stm32_usbdev_s *priv)
{
# ifdef CONFIG_USBDEV_LOWPOWER
uint32_t regval;
# endif
/* Notify the class driver of the suspend event */
if (priv->driver)
{
CLASS_SUSPEND(priv->driver, &priv->usbdev);
}
# ifdef CONFIG_USBDEV_LOWPOWER
/* OTG_DSTS_SUSPSTS is set as long as the suspend condition is detected on
* USB. Check if we are still have the suspend condition, that we are
* connected to the host, and that we have been configured.
*/
regval = stm32_getreg(STM32_OTG_DSTS);
if ((regval & OTG_DSTS_SUSPSTS) != 0 && devstate == DEVSTATE_CONFIGURED)
{
/* Switch off OTG clocking. Setting OTG_PCGCCTL_STPPCLK stops the PHY
* clock.
*/
regval = stm32_getreg(STM32_OTG_PCGCCTL);
regval |= OTG_PCGCCTL_STPPCLK;
stm32_putreg(regval, STM32_OTG_PCGCCTL);
/* Setting OTG_PCGCCTL_GATEHCLK gate HCLK to modules other than the AHB
* Slave and Master and wakeup logic.
*/
regval |= OTG_PCGCCTL_GATEHCLK;
stm32_putreg(regval, STM32_OTG_PCGCCTL);
}
# endif
/* Let the board-specific logic know that we have entered the suspend state */
stm32_usbsuspend((FAR struct usbdev_s *)priv, false);
}
/****************************************************************************
* Name: stm32_rxinterrupt
*
* Description:
* RxFIFO non-empty interrupt. This interrupt indicates that there is at
* least one packet pending to be read from the RxFIFO.
*
****************************************************************************/
static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv)
{
FAR struct stm32_ep_s *privep;
uint32_t regval;
int bcnt;
int epphy;
/* Get the status from the top of the FIFO */
regval = stm32_getreg(STM32_OTG_GRXSTSP);
/* Decode status fields */
epphy = (regval & OTG_GRXSTSD_EPNUM_MASK) >> OTG_GRXSTSD_EPNUM_SHIFT;
if (epphy < STM32_NENDPOINTS)
{
privep = &priv->epout[epphy];
/* Handle the RX event according to the packet status field */
switch (regval & OTG_GRXSTSD_PKTSTS_MASK)
{
/* Global OUT NAK. This indicate that the global OUT NAK bit has
* taken effect.
*
* PKTSTS = Global OUT NAK, BCNT = 0, EPNUM = Don't Care, DPID =
* Don't Care.
*/
case OTG_GRXSTSD_PKTSTS_OUTNAK:
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTNAK), 0);
}
break;
/* OUT data packet received. PKTSTS = DataOUT, BCNT = size of the
* received data OUT packet, EPNUM = EPNUM on which the packet was
* received, DPID = Actual Data PID.
*/
case OTG_GRXSTSD_PKTSTS_OUTRECVD:
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTRECVD), epphy);
bcnt = (regval & OTG_GRXSTSD_BCNT_MASK) >> OTG_GRXSTSD_BCNT_SHIFT;
if (bcnt > 0)
{
stm32_epout_receive(privep, bcnt);
}
}
break;
/* OUT transfer completed. This indicates that an OUT data transfer
* for the specified OUT endpoint has completed. After this entry is
* popped from the receive FIFO, the core asserts a Transfer
* Completed interrupt on the specified OUT endpoint.
*
* PKTSTS = Data OUT Transfer Done, BCNT = 0, EPNUM = OUT EP Num on
* which the data transfer is complete, DPID = Don't Care.
*/
case OTG_GRXSTSD_PKTSTS_OUTDONE:
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTDONE), epphy);
}
break;
/* SETUP transaction completed. This indicates that the Setup stage
* for the specified endpoint has completed and the Data stage has
* started. After this entry is popped from the receive FIFO, the
* core asserts a Setup interrupt on the specified control OUT
* endpoint (triggers an interrupt).
*
* PKTSTS = Setup Stage Done, BCNT = 0, EPNUM = Control EP Num,
* DPID = Don't Care.
*/
case OTG_GRXSTSD_PKTSTS_SETUPDONE:
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETUPDONE), epphy);
/* Now that the Setup Phase is complete if it was an OUT enable the
* endpoint (Doing this here prevents the loss of the first FIFO
* word)
*/
if (priv->ep0state == EP0STATE_SETUP_OUT)
{
/* Clear NAKSTS so that we can receive the data */
regval = stm32_getreg(STM32_OTG_DOEPCTL(0));
regval |= OTG_DOEPCTL0_CNAK;
stm32_putreg(regval, STM32_OTG_DOEPCTL(0));
}
}
break;
/* SETUP data packet received. This indicates that a SETUP packet
* for the specified endpoint is now available for reading from the
* receive FIFO.
*
* PKTSTS = SETUP, BCNT = 8, EPNUM = Control EP Num, DPID = D0.
*/
case OTG_GRXSTSD_PKTSTS_SETUPRECVD:
{
uint16_t datlen;
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETUPRECVD), epphy);
/* Read EP0 setup data. NOTE: If multiple SETUP packets are
* received, the last one overwrites the previous setup packets and
* only that last SETUP packet will be processed.
*/
stm32_rxfifo_read(&priv->epout[EP0],
(FAR uint8_t *) & priv->ctrlreq,
USB_SIZEOF_CTRLREQ);
/* Was this an IN or an OUT SETUP packet. If it is an OUT SETUP,
* then we need to wait for the completion of the data phase to
* process the setup command. If it is an IN SETUP packet, then we
* must processing the command BEFORE we enter the DATA phase.
*
* If the data associated with the OUT SETUP packet is zero length,
* then, of course, we don't need to wait.
*/
datlen = GETUINT16(priv->ctrlreq.len);
if (USB_REQ_ISOUT(priv->ctrlreq.type) && datlen > 0)
{
priv->ep0state = EP0STATE_SETUP_OUT;
}
else
{
/* We can process the setup data as soon as SETUP done word is
* popped of the RxFIFO.
*/
priv->ep0state = EP0STATE_SETUP_READY;
}
}
break;
default:
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS),
(regval & OTG_GRXSTSD_PKTSTS_MASK) >>
OTG_GRXSTSD_PKTSTS_SHIFT);
}
break;
}
}
}
/****************************************************************************
* Name: stm32_enuminterrupt
*
* Description:
* Enumeration done interrupt
*
****************************************************************************/
static inline void stm32_enuminterrupt(FAR struct stm32_usbdev_s *priv)
{
uint32_t regval;
/* Activate EP0 */
stm32_ep0in_activate();
/* Set USB turn-around time for the full speed device with internal PHY
* interface.
*/
regval = stm32_getreg(STM32_OTG_GUSBCFG);
regval &= ~OTG_GUSBCFG_TRDT_MASK;
# ifdef CONFIG_STM32F7_OTGFSHS
regval |= OTG_GUSBCFG_TRDT(9);
# else
regval |= OTG_GUSBCFG_TRDT(6);
# endif
stm32_putreg(regval, STM32_OTG_GUSBCFG);
}
/****************************************************************************
* Name: stm32_isocininterrupt
*
* Description:
* Incomplete isochronous IN transfer interrupt. Assertion of the incomplete
* isochronous IN transfer interrupt indicates an incomplete isochronous IN
* transfer on at least one of the isochronous IN endpoints.
*
****************************************************************************/
# ifdef CONFIG_USBDEV_ISOCHRONOUS
static inline void stm32_isocininterrupt(FAR struct stm32_usbdev_s *priv)
{
int i;
/* The application must read the endpoint control register for all
* isochronous IN endpoints to detect endpoints with incomplete IN data
* transfers.
*/
for (i = 0; i < STM32_NENDPOINTS; i++)
{
/* Is this an isochronous IN endpoint? */
privep = &priv->epin[i];
if (privep->eptype != USB_EP_ATTR_XFER_ISOC)
{
/* No... keep looking */
continue;
}
/* Is there an active read request on the isochronous OUT endpoint? */
if (!privep->active)
{
/* No.. the endpoint is not actively transmitting data */
continue;
}
/* Check if this is the endpoint that had the incomplete transfer */
regaddr = STM32_OTG_DIEPCTL(privep->epphy);
doepctl = stm32_getreg(regaddr);
dsts = stm32_getreg(STM32_OTG_DSTS);
/* EONUM = 0:even frame, 1:odd frame SOFFN = Frame number of the received
* SOF
*/
eonum = ((doepctl & OTG_DIEPCTL_EONUM) != 0);
soffn = ((dsts & OTG_DSTS_SOFFN0) != 0);
if (eonum != soffn)
{
/* Not this endpoint */
continue;
}
/* For isochronous IN endpoints with incomplete transfers, the
* application must discard the data in the memory and disable the
* endpoint.
*/
stm32_req_complete(privep, -EIO);
# warning "Will clear OTG_DIEPCTL_USBAEP too"
stm32_epin_disable(privep);
break;
}
}
# endif
/****************************************************************************
* Name: stm32_isocoutinterrupt
*
* Description:
* Incomplete periodic transfer interrupt
*
****************************************************************************/
# ifdef CONFIG_USBDEV_ISOCHRONOUS
static inline void stm32_isocoutinterrupt(FAR struct stm32_usbdev_s *priv)
{
FAR struct stm32_ep_s *privep;
FAR struct stm32_req_s *privreq;
uint32_t regaddr;
uint32_t doepctl;
uint32_t dsts;
bool eonum;
bool soffn;
/* When it receives an IISOOXFR interrupt, the application must read the
* control registers of all isochronous OUT endpoints to determine which
* endpoints had an incomplete transfer in the current microframe. An
* endpoint transfer is incomplete if both the following conditions are
* true:
*
* DOEPCTLx:EONUM = DSTS:SOFFN[0], and
* DOEPCTLx:EPENA = 1
*/
for (i = 0; i < STM32_NENDPOINTS; i++)
{
/* Is this an isochronous OUT endpoint? */
privep = &priv->epout[i];
if (privep->eptype != USB_EP_ATTR_XFER_ISOC)
{
/* No... keep looking */
continue;
}
/* Is there an active read request on the isochronous OUT endpoint? */
if (!privep->active)
{
/* No.. the endpoint is not actively transmitting data */
continue;
}
/* Check if this is the endpoint that had the incomplete transfer */
regaddr = STM32_OTG_DOEPCTL(privep->epphy);
doepctl = stm32_getreg(regaddr);
dsts = stm32_getreg(STM32_OTG_DSTS);
/* EONUM = 0:even frame, 1:odd frame
* SOFFN = Frame number of the received SOF
*/
eonum = ((doepctl & OTG_DOEPCTL_EONUM) != 0);
soffn = ((dsts & OTG_DSTS_SOFFN0) != 0);
if (eonum != soffn)
{
/* Not this endpoint */
continue;
}
/* For isochronous OUT endpoints with incomplete transfers, the
* application must discard the data in the memory and disable the
* endpoint.
*/
stm32_req_complete(privep, -EIO);
# warning "Will clear OTG_DOEPCTL_USBAEP too"
stm32_epout_disable(privep);
break;
}
}
# endif
/****************************************************************************
* Name: stm32_sessioninterrupt
*
* Description:
* Session request/new session detected interrupt
*
****************************************************************************/
# ifdef CONFIG_USBDEV_VBUSSENSING
static inline void stm32_sessioninterrupt(FAR struct stm32_usbdev_s *priv)
{
# warning "Missing logic"
}
# endif
/****************************************************************************
* Name: stm32_otginterrupt
*
* Description:
* OTG interrupt
*
****************************************************************************/
# ifdef CONFIG_USBDEV_VBUSSENSING
static inline void stm32_otginterrupt(FAR struct stm32_usbdev_s *priv)
{
uint32_t regval;
/* Check for session end detected */
regval = stm32_getreg(STM32_OTG_GOTGINT);
if ((regval & OTG_GOTGINT_SEDET) != 0)
{
# warning "Missing logic"
}
/* Clear OTG interrupt */
stm32_putreg(regval, STM32_OTG_GOTGINT);
}
# endif
/****************************************************************************
* Name: stm32_usbinterrupt
*
* Description:
* USB interrupt handler
*
****************************************************************************/
static int stm32_usbinterrupt(int irq, FAR void *context, FAR void *arg)
{
/* At present, there is only a single OTG device support. Hence it is
* pre-allocated as g_otghsdev. However, in most code, the private data
* structure will be referenced using the 'priv' pointer (rather than the
* global data) in order to simplify any future support for multiple devices.
*/
FAR struct stm32_usbdev_s *priv = &g_otghsdev;
uint32_t regval;
uint32_t reserved;
usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_USB), 0);
/* Assure that we are in device mode */
DEBUGASSERT((stm32_getreg(STM32_OTG_GINTSTS) & OTG_GINTSTS_CMOD) ==
OTG_GINTSTS_DEVMODE);
/* Get the state of all enabled interrupts. We will do this repeatedly some
* interrupts (like RXFLVL) will generate additional interrupting events.
*/
for (; ; )
{
/* Get the set of pending, un-masked interrupts */
regval = stm32_getreg(STM32_OTG_GINTSTS);
reserved = (regval & OTG_GINT_RESERVED);
regval &= stm32_getreg(STM32_OTG_GINTMSK);
/* With out modifying the reserved bits, acknowledge all **Writable**
* pending irqs we will service below.
*/
stm32_putreg(((regval | reserved) & OTG_GINT_RC_W1), STM32_OTG_GINTSTS);
/* Break out of the loop when there are no further pending (and unmasked)
* interrupts to be processes.
*/
if (regval == 0)
{
break;
}
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_INTPENDING), (uint16_t) regval);
/* OUT endpoint interrupt. The core sets this bit to indicate that an
* interrupt is pending on one of the OUT endpoints of the core.
*/
if ((regval & OTG_GINT_OEP) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT), (uint16_t) regval);
stm32_epout_interrupt(priv);
}
/* IN endpoint interrupt. The core sets this bit to indicate that an
* interrupt is pending on one of the IN endpoints of the core.
*/
if ((regval & OTG_GINT_IEP) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN), (uint16_t) regval);
stm32_epin_interrupt(priv);
}
/* Host/device mode mismatch error interrupt */
# ifdef CONFIG_DEBUG_USB
if ((regval & OTG_GINT_MMIS) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_MISMATCH),
(uint16_t) regval);
}
# endif
/* Resume/remote wakeup detected interrupt */
if ((regval & OTG_GINT_WKUP) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_WAKEUP), (uint16_t) regval);
stm32_resumeinterrupt(priv);
}
/* USB suspend interrupt */
if ((regval & OTG_GINT_USBSUSP) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SUSPEND),
(uint16_t) regval);
stm32_suspendinterrupt(priv);
}
/* Start of frame interrupt */
# ifdef CONFIG_USBDEV_SOFINTERRUPT
if ((regval & OTG_GINT_SOF) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SOF), (uint16_t) regval);
}
# endif
/* RxFIFO non-empty interrupt. Indicates that there is at least one
* packet pending to be read from the RxFIFO.
*/
if ((regval & OTG_GINT_RXFLVL) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_RXFIFO), (uint16_t) regval);
stm32_rxinterrupt(priv);
}
/* USB reset interrupt */
if ((regval & (OTG_GINT_USBRST | OTG_GINT_RSTDET)) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVRESET),
(uint16_t) regval);
/* Perform the device reset */
stm32_usbreset(priv);
usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_USB), 0);
return OK;
}
/* Enumeration done interrupt */
if ((regval & OTG_GINT_ENUMDNE) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_ENUMDNE),
(uint16_t) regval);
stm32_enuminterrupt(priv);
}
/* Incomplete isochronous IN transfer interrupt. When the core finds
* non-empty any of the isochronous IN endpoint FIFOs scheduled for the
* current frame non-empty, the core generates an IISOIXFR interrupt.
*/
# ifdef CONFIG_USBDEV_ISOCHRONOUS
if ((regval & OTG_GINT_IISOIXFR) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IISOIXFR),
(uint16_t) regval);
stm32_isocininterrupt(priv);
}
/* Incomplete isochronous OUT transfer. For isochronous OUT endpoints,
* the XFRC interrupt may not always be asserted. If the core drops
* isochronous OUT data packets, the application could fail to detect the
* XFRC interrupt. The incomplete Isochronous OUT data interrupt
* indicates that an XFRC interrupt was not asserted on at least one of
* the isochronous OUT endpoints. At this point, the endpoint with the
* incomplete transfer remains enabled, but no active transfers remain in
* progress on this endpoint on the USB.
*/
if ((regval & OTG_GINT_IISOOXFR) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IISOOXFR),
(uint16_t) regval);
stm32_isocoutinterrupt(priv);
}
# endif
/* Session request/new session detected interrupt */
# ifdef CONFIG_USBDEV_VBUSSENSING
if ((regval & OTG_GINT_SRQ) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SRQ), (uint16_t) regval);
stm32_sessioninterrupt(priv);
}
/* OTG interrupt */
if ((regval & OTG_GINT_OTG) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OTG), (uint16_t) regval);
stm32_otginterrupt(priv);
}
# endif
}
usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_USB), 0);
return OK;
}
/****************************************************************************
* Endpoint operations
****************************************************************************/
/****************************************************************************
* Name: stm32_enablegonak
*
* Description:
* Enable global OUT NAK mode
*
****************************************************************************/
static void stm32_enablegonak(FAR struct stm32_ep_s *privep)
{
uint32_t regval;
/* First, make sure that there is no GNOAKEFF interrupt pending. */
# if 0
stm32_putreg(OTG_GINT_GONAKEFF, STM32_OTG_GINTSTS);
# endif
/* Enable Global OUT NAK mode in the core. */
regval = stm32_getreg(STM32_OTG_DCTL);
regval |= OTG_DCTL_SGONAK;
stm32_putreg(regval, STM32_OTG_DCTL);
# if 0
/* Wait for the GONAKEFF interrupt that indicates that the OUT NAK mode is in
* effect. When the interrupt handler pops the OUTNAK word from the RxFIFO,
* the core sets the GONAKEFF interrupt.
*/
while ((stm32_getreg(STM32_OTG_GINTSTS) & OTG_GINT_GONAKEFF) == 0);
stm32_putreg(OTG_GINT_GONAKEFF, STM32_OTG_GINTSTS);
# else
/* Since we are in the interrupt handler, we cannot wait inline for the
* GONAKEFF because it cannot occur until service the RXFLVL global interrupt
* and pop the OUTNAK word from the RxFIFO.
*
* Perhaps it is sufficient to wait for Global OUT NAK status to be reported
* in OTG DCTL register?
*/
while ((stm32_getreg(STM32_OTG_DCTL) & OTG_DCTL_GONSTS) == 0);
# endif
}
/****************************************************************************
* Name: stm32_disablegonak
*
* Description:
* Disable global OUT NAK mode
*
****************************************************************************/
static void stm32_disablegonak(FAR struct stm32_ep_s *privep)
{
uint32_t regval;
/* Set the "Clear the Global OUT NAK bit" to disable global OUT NAK mode */
regval = stm32_getreg(STM32_OTG_DCTL);
regval |= OTG_DCTL_CGONAK;
stm32_putreg(regval, STM32_OTG_DCTL);
}
/****************************************************************************
* Name: stm32_epout_configure
*
* Description:
* Configure an OUT endpoint, making it usable
*
* Input Parameters:
* privep - a pointer to an internal endpoint structure
* eptype - The type of the endpoint
* maxpacket - The max packet size of the endpoint
*
****************************************************************************/
static int stm32_epout_configure(FAR struct stm32_ep_s *privep,
uint8_t eptype, uint16_t maxpacket)
{
uint32_t mpsiz;
uint32_t regaddr;
uint32_t regval;
usbtrace(TRACE_EPCONFIGURE, privep->epphy);
/* For EP0, the packet size is encoded */
if (privep->epphy == EP0)
{
DEBUGASSERT(eptype == USB_EP_ATTR_XFER_CONTROL);
/* Map the size in bytes to the encoded value in the register */
switch (maxpacket)
{
case 8:
mpsiz = OTG_DOEPCTL0_MPSIZ_8;
break;
case 16:
mpsiz = OTG_DOEPCTL0_MPSIZ_16;
break;
case 32:
mpsiz = OTG_DOEPCTL0_MPSIZ_32;
break;
case 64:
mpsiz = OTG_DOEPCTL0_MPSIZ_64;
break;
default:
uerr("Unsupported maxpacket: %d\n", maxpacket);
return -EINVAL;
}
}
/* For other endpoints, the packet size is in bytes */
else
{
mpsiz = (maxpacket << OTG_DOEPCTL_MPSIZ_SHIFT);
}
/* If the endpoint is already active don't change the endpoint control
* register.
*/
regaddr = STM32_OTG_DOEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
if ((regval & OTG_DOEPCTL_USBAEP) == 0)
{
if (regval & OTG_DOEPCTL_NAKSTS)
{
regval |= OTG_DOEPCTL_CNAK;
}
regval &= ~(OTG_DOEPCTL_MPSIZ_MASK | OTG_DOEPCTL_EPTYP_MASK);
regval |= mpsiz;
regval |= (eptype << OTG_DOEPCTL_EPTYP_SHIFT);
regval |= (OTG_DOEPCTL_SD0PID | OTG_DOEPCTL_USBAEP);
stm32_putreg(regval, regaddr);
/* Save the endpoint configuration */
privep->ep.maxpacket = maxpacket;
privep->eptype = eptype;
privep->stalled = false;
}
/* Enable the interrupt for this endpoint */
regval = stm32_getreg(STM32_OTG_DAINTMSK);
regval |= OTG_DAINT_OEP(privep->epphy);
stm32_putreg(regval, STM32_OTG_DAINTMSK);
return OK;
}
/****************************************************************************
* Name: stm32_epin_configure
*
* Description:
* Configure an IN endpoint, making it usable
*
* Input Parameters:
* privep - a pointer to an internal endpoint structure
* eptype - The type of the endpoint
* maxpacket - The max packet size of the endpoint
*
****************************************************************************/
static int stm32_epin_configure(FAR struct stm32_ep_s *privep,
uint8_t eptype, uint16_t maxpacket)
{
uint32_t mpsiz;
uint32_t regaddr;
uint32_t regval;
usbtrace(TRACE_EPCONFIGURE, privep->epphy);
/* For EP0, the packet size is encoded */
if (privep->epphy == EP0)
{
DEBUGASSERT(eptype == USB_EP_ATTR_XFER_CONTROL);
/* Map the size in bytes to the encoded value in the register */
switch (maxpacket)
{
case 8:
mpsiz = OTG_DIEPCTL0_MPSIZ_8;
break;
case 16:
mpsiz = OTG_DIEPCTL0_MPSIZ_16;
break;
case 32:
mpsiz = OTG_DIEPCTL0_MPSIZ_32;
break;
case 64:
mpsiz = OTG_DIEPCTL0_MPSIZ_64;
break;
default:
uerr("Unsupported maxpacket: %d\n", maxpacket);
return -EINVAL;
}
}
/* For other endpoints, the packet size is in bytes */
else
{
mpsiz = (maxpacket << OTG_DIEPCTL_MPSIZ_SHIFT);
}
/* If the endpoint is already active don't change the endpoint control
* register.
*/
regaddr = STM32_OTG_DIEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
if ((regval & OTG_DIEPCTL_USBAEP) == 0)
{
if (regval & OTG_DIEPCTL_NAKSTS)
{
regval |= OTG_DIEPCTL_CNAK;
}
regval &= ~(OTG_DIEPCTL_MPSIZ_MASK | OTG_DIEPCTL_EPTYP_MASK |
OTG_DIEPCTL_TXFNUM_MASK);
regval |= mpsiz;
regval |= (eptype << OTG_DIEPCTL_EPTYP_SHIFT);
regval |= (privep->epphy << OTG_DIEPCTL_TXFNUM_SHIFT);
regval |= (OTG_DIEPCTL_SD0PID | OTG_DIEPCTL_USBAEP);
stm32_putreg(regval, regaddr);
/* Save the endpoint configuration */
privep->ep.maxpacket = maxpacket;
privep->eptype = eptype;
privep->stalled = false;
}
/* Enable the interrupt for this endpoint */
regval = stm32_getreg(STM32_OTG_DAINTMSK);
regval |= OTG_DAINT_IEP(privep->epphy);
stm32_putreg(regval, STM32_OTG_DAINTMSK);
return OK;
}
/****************************************************************************
* Name: stm32_ep_configure
*
* Description:
* Configure endpoint, making it usable
*
* Input Parameters:
* ep - the struct usbdev_ep_s instance obtained from allocep()
* desc - A struct usb_epdesc_s instance describing the endpoint
* last - true if this this last endpoint to be configured. Some hardware
* needs to take special action when all of the endpoints have been
* configured.
*
****************************************************************************/
static int stm32_ep_configure(FAR struct usbdev_ep_s *ep,
FAR const struct usb_epdesc_s *desc, bool last)
{
FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
uint16_t maxpacket;
uint8_t eptype;
int ret;
usbtrace(TRACE_EPCONFIGURE, privep->epphy);
DEBUGASSERT(desc->addr == ep->eplog);
/* Initialize EP capabilities */
maxpacket = GETUINT16(desc->mxpacketsize);
eptype = desc->attr & USB_EP_ATTR_XFERTYPE_MASK;
/* Setup Endpoint Control Register */
if (privep->isin)
{
ret = stm32_epin_configure(privep, eptype, maxpacket);
}
else
{
ret = stm32_epout_configure(privep, eptype, maxpacket);
}
return ret;
}
/****************************************************************************
* Name: stm32_ep0_configure
*
* Description:
* Reset Usb engine
*
****************************************************************************/
static void stm32_ep0_configure(FAR struct stm32_usbdev_s *priv)
{
/* Enable EP0 IN and OUT */
stm32_epin_configure(&priv->epin[EP0], USB_EP_ATTR_XFER_CONTROL,
CONFIG_USBDEV_EP0_MAXSIZE);
stm32_epout_configure(&priv->epout[EP0], USB_EP_ATTR_XFER_CONTROL,
CONFIG_USBDEV_EP0_MAXSIZE);
}
/****************************************************************************
* Name: stm32_epout_disable
*
* Description:
* Disable an OUT endpoint will no longer be used
*
****************************************************************************/
static void stm32_epout_disable(FAR struct stm32_ep_s *privep)
{
uint32_t regaddr;
uint32_t regval;
irqstate_t flags;
usbtrace(TRACE_EPDISABLE, privep->epphy);
/* Is this an IN or an OUT endpoint */
/* Before disabling any OUT endpoint, the application must enable Global OUT
* NAK mode in the core.
*/
flags = enter_critical_section();
stm32_enablegonak(privep);
/* Disable the required OUT endpoint by setting the EPDIS and SNAK bits int
* DOECPTL register.
*/
regaddr = STM32_OTG_DOEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
regval &= ~OTG_DOEPCTL_USBAEP;
regval |= (OTG_DOEPCTL_EPDIS | OTG_DOEPCTL_SNAK);
stm32_putreg(regval, regaddr);
/* Wait for the EPDISD interrupt which indicates that the OUT endpoint is
* completely disabled.
*/
# if 0 /* Doesn't happen */
regaddr = STM32_OTG_DOEPINT(privep->epphy);
while ((stm32_getreg(regaddr) & OTG_DOEPINT_EPDISD) == 0);
# else
/* REVISIT: */
up_udelay(10);
# endif
/* Clear the EPDISD interrupt indication */
stm32_putreg(OTG_DOEPINT_EPDISD, STM32_OTG_DOEPINT(privep->epphy));
/* Then disable the Global OUT NAK mode to continue receiving data from other
* non-disabled OUT endpoints.
*/
stm32_disablegonak(privep);
/* Disable endpoint interrupts */
regval = stm32_getreg(STM32_OTG_DAINTMSK);
regval &= ~OTG_DAINT_OEP(privep->epphy);
stm32_putreg(regval, STM32_OTG_DAINTMSK);
/* Cancel any queued read requests */
stm32_req_cancel(privep, -ESHUTDOWN);
leave_critical_section(flags);
}
/****************************************************************************
* Name: stm32_epin_disable
*
* Description:
* Disable an IN endpoint when it will no longer be used
*
****************************************************************************/
static void stm32_epin_disable(FAR struct stm32_ep_s *privep)
{
uint32_t regaddr;
uint32_t regval;
irqstate_t flags;
usbtrace(TRACE_EPDISABLE, privep->epphy);
/* After USB reset, the endpoint will already be deactivated by the hardware.
* Trying to disable again will just hang in the wait.
*/
regaddr = STM32_OTG_DIEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
if ((regval & OTG_DIEPCTL_USBAEP) == 0)
{
return;
}
/* This INEPNE wait logic is suggested by reference manual, but seems to get
* stuck to infinite loop.
*/
# if 0
/* Make sure that there is no pending IPEPNE interrupt (because we are to
* poll this bit below).
*/
stm32_putreg(OTG_DIEPINT_INEPNE, STM32_OTG_DIEPINT(privep->epphy));
/* Set the endpoint in NAK mode */
regaddr = STM32_OTG_DIEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
regval &= ~OTG_DIEPCTL_USBAEP;
regval |= (OTG_DIEPCTL_EPDIS | OTG_DIEPCTL_SNAK);
stm32_putreg(regval, regaddr);
/* Wait for the INEPNE interrupt that indicates that we are now in NAK mode */
regaddr = STM32_OTG_DIEPINT(privep->epphy);
while ((stm32_getreg(regaddr) & OTG_DIEPINT_INEPNE) == 0);
/* Clear the INEPNE interrupt indication */
stm32_putreg(OTG_DIEPINT_INEPNE, regaddr);
# endif
/* Deactivate and disable the endpoint by setting the EPDIS and SNAK bits the
* DIEPCTLx register.
*/
flags = enter_critical_section();
regaddr = STM32_OTG_DIEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
regval &= ~OTG_DIEPCTL_USBAEP;
regval |= (OTG_DIEPCTL_EPDIS | OTG_DIEPCTL_SNAK);
stm32_putreg(regval, regaddr);
/* Wait for the EPDISD interrupt which indicates that the IN endpoint is
* completely disabled.
*/
regaddr = STM32_OTG_DIEPINT(privep->epphy);
while ((stm32_getreg(regaddr) & OTG_DIEPINT_EPDISD) == 0);
/* Clear the EPDISD interrupt indication */
regval = stm32_getreg(regaddr);
regval |= OTG_DIEPINT_EPDISD;
stm32_putreg(regval, regaddr);
/* Flush any data remaining in the TxFIFO */
stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_D(privep->epphy));
/* Disable endpoint interrupts */
regval = stm32_getreg(STM32_OTG_DAINTMSK);
regval &= ~OTG_DAINT_IEP(privep->epphy);
stm32_putreg(regval, STM32_OTG_DAINTMSK);
/* Cancel any queued write requests */
stm32_req_cancel(privep, -ESHUTDOWN);
leave_critical_section(flags);
}
/****************************************************************************
* Name: stm32_ep_disable
*
* Description:
* The endpoint will no longer be used
*
****************************************************************************/
static int stm32_ep_disable(FAR struct usbdev_ep_s *ep)
{
FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
# ifdef CONFIG_DEBUG_FEATURES
if (!ep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
# endif
usbtrace(TRACE_EPDISABLE, privep->epphy);
/* Is this an IN or an OUT endpoint */
if (privep->isin)
{
/* Disable the IN endpoint */
stm32_epin_disable(privep);
}
else
{
/* Disable the OUT endpoint */
stm32_epout_disable(privep);
}
return OK;
}
/****************************************************************************
* Name: stm32_ep_allocreq
*
* Description:
* Allocate an I/O request
*
****************************************************************************/
static FAR struct usbdev_req_s *stm32_ep_allocreq(FAR struct usbdev_ep_s *ep)
{
FAR struct stm32_req_s *privreq;
# ifdef CONFIG_DEBUG_FEATURES
if (!ep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return NULL;
}
# endif
usbtrace(TRACE_EPALLOCREQ, ((FAR struct stm32_ep_s *)ep)->epphy);
privreq = (FAR struct stm32_req_s *)kmm_malloc(sizeof(struct stm32_req_s));
if (!privreq)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_ALLOCFAIL), 0);
return NULL;
}
memset(privreq, 0, sizeof(struct stm32_req_s));
return &privreq->req;
}
/****************************************************************************
* Name: stm32_ep_freereq
*
* Description:
* Free an I/O request
*
****************************************************************************/
static void stm32_ep_freereq(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req)
{
FAR struct stm32_req_s *privreq = (FAR struct stm32_req_s *)req;
# ifdef CONFIG_DEBUG_FEATURES
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return;
}
# endif
usbtrace(TRACE_EPFREEREQ, ((FAR struct stm32_ep_s *)ep)->epphy);
kmm_free(privreq);
}
/****************************************************************************
* Name: stm32_ep_allocbuffer
*
* Description:
* Allocate an I/O buffer
*
****************************************************************************/
# ifdef CONFIG_USBDEV_DMA
static void *stm32_ep_allocbuffer(FAR struct usbdev_ep_s *ep, uint16_t bytes)
{
usbtrace(TRACE_EPALLOCBUFFER, ((FAR struct stm32_ep_s *)ep)->epphy);
# ifdef CONFIG_USBDEV_DMAMEMORY
return usbdev_dma_alloc(bytes);
# else
return kmm_malloc(bytes);
# endif
}
# endif
/****************************************************************************
* Name: stm32_ep_freebuffer
*
* Description:
* Free an I/O buffer
*
****************************************************************************/
# ifdef CONFIG_USBDEV_DMA
static void stm32_ep_freebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf)
{
usbtrace(TRACE_EPALLOCBUFFER, ((FAR struct stm32_ep_s *)ep)->epphy);
# ifdef CONFIG_USBDEV_DMAMEMORY
usbdev_dma_free(buf);
# else
kmm_free(buf);
# endif
}
# endif
/****************************************************************************
* Name: stm32_ep_submit
*
* Description:
* Submit an I/O request to the endpoint
*
****************************************************************************/
static int stm32_ep_submit(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req)
{
FAR struct stm32_req_s *privreq = (FAR struct stm32_req_s *)req;
FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
FAR struct stm32_usbdev_s *priv;
irqstate_t flags;
int ret = OK;
/* Some sanity checking */
# ifdef CONFIG_DEBUG_FEATURES
if (!req || !req->callback || !req->buf || !ep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
uinfo("req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf,
ep);
return -EINVAL;
}
# endif
usbtrace(TRACE_EPSUBMIT, privep->epphy);
priv = privep->dev;
# ifdef CONFIG_DEBUG_FEATURES
if (!priv->driver)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOTCONFIGURED),
priv->usbdev.speed);
return -ESHUTDOWN;
}
# endif
/* Handle the request from the class driver */
req->result = -EINPROGRESS;
req->xfrd = 0;
/* Disable Interrupts */
flags = enter_critical_section();
/* If we are stalled, then drop all requests on the floor */
if (privep->stalled)
{
ret = -EBUSY;
}
else
{
/* Add the new request to the request queue for the endpoint. */
if (stm32_req_addlast(privep, privreq) && !privep->active)
{
/* If a request was added to an IN endpoint, then attempt to send the
* request data buffer now.
*/
if (privep->isin)
{
usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len);
/* If the endpoint is not busy with another write request, then
* process the newly received write request now.
*/
if (!privep->active)
{
stm32_epin_request(priv, privep);
}
}
/* If the request was added to an OUT endpoint, then attempt to setup
* a read into the request data buffer now (this will, of course,
* fail if there is already a read in place).
*/
else
{
usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len);
stm32_epout_request(priv, privep);
}
}
}
leave_critical_section(flags);
return ret;
}
/****************************************************************************
* Name: stm32_ep_cancel
*
* Description:
* Cancel an I/O request previously sent to an endpoint
*
****************************************************************************/
static int stm32_ep_cancel(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req)
{
FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
irqstate_t flags;
# ifdef CONFIG_DEBUG_FEATURES
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
# endif
usbtrace(TRACE_EPCANCEL, privep->epphy);
flags = enter_critical_section();
/* FIXME: if the request is the first, then we need to flush the EP otherwise
* just remove it from the list but ... all other implementations cancel all
* requests ...
*/
stm32_req_cancel(privep, -ESHUTDOWN);
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: stm32_epout_setstall
*
* Description:
* Stall an OUT endpoint
*
****************************************************************************/
static int stm32_epout_setstall(FAR struct stm32_ep_s *privep)
{
# if 1
/* This implementation follows the requirements from the STM32 F4 reference
* manual.
*/
uint32_t regaddr;
uint32_t regval;
/* Put the core in the Global OUT NAK mode */
stm32_enablegonak(privep);
/* Disable and STALL the OUT endpoint by setting the EPDIS and STALL bits in
* the DOECPTL register.
*/
regaddr = STM32_OTG_DOEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
regval |= (OTG_DOEPCTL_EPDIS | OTG_DOEPCTL_STALL);
stm32_putreg(regval, regaddr);
/* Wait for the EPDISD interrupt which indicates that the OUT endpoint is
* completely disabled.
*/
# if 0 /* Doesn't happen */
regaddr = STM32_OTG_DOEPINT(privep->epphy);
while ((stm32_getreg(regaddr) & OTG_DOEPINT_EPDISD) == 0);
# else
/* REVISIT: */
up_udelay(10);
# endif
/* Disable Global OUT NAK mode */
stm32_disablegonak(privep);
/* The endpoint is now stalled */
privep->stalled = true;
return OK;
# else
/* This implementation follows the STMicro code example. */
/* REVISIT: */
uint32_t regaddr;
uint32_t regval;
/* Stall the OUT endpoint by setting the STALL bit in the DOECPTL register. */
regaddr = STM32_OTG_DOEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
regval |= OTG_DOEPCTL_STALL;
stm32_putreg(regval, regaddr);
/* The endpoint is now stalled */
privep->stalled = true;
return OK;
# endif
}
/****************************************************************************
* Name: stm32_epin_setstall
*
* Description:
* Stall an IN endpoint
*
****************************************************************************/
static int stm32_epin_setstall(FAR struct stm32_ep_s *privep)
{
uint32_t regaddr;
uint32_t regval;
/* Get the IN endpoint device control register */
regaddr = STM32_OTG_DIEPCTL(privep->epphy);
regval = stm32_getreg(regaddr);
/* Then stall the endpoint */
regval |= OTG_DIEPCTL_STALL;
stm32_putreg(regval, regaddr);
/* The endpoint is now stalled */
privep->stalled = true;
return OK;
}
/****************************************************************************
* Name: stm32_ep_setstall
*
* Description:
* Stall an endpoint
*
****************************************************************************/
static int stm32_ep_setstall(FAR struct stm32_ep_s *privep)
{
usbtrace(TRACE_EPSTALL, privep->epphy);
/* Is this an IN endpoint? */
if (privep->isin == 1)
{
return stm32_epin_setstall(privep);
}
else
{
return stm32_epout_setstall(privep);
}
}
/****************************************************************************
* Name: stm32_ep_clrstall
*
* Description:
* Resume a stalled endpoint
*
****************************************************************************/
static int stm32_ep_clrstall(FAR struct stm32_ep_s *privep)
{
uint32_t regaddr;
uint32_t regval;
uint32_t stallbit;
uint32_t data0bit;
usbtrace(TRACE_EPRESUME, privep->epphy);
/* Is this an IN endpoint? */
if (privep->isin == 1)
{
/* Clear the stall bit in the IN endpoint device control register */
regaddr = STM32_OTG_DIEPCTL(privep->epphy);
stallbit = OTG_DIEPCTL_STALL;
data0bit = OTG_DIEPCTL_SD0PID;
}
else
{
/* Clear the stall bit in the IN endpoint device control register */
regaddr = STM32_OTG_DOEPCTL(privep->epphy);
stallbit = OTG_DOEPCTL_STALL;
data0bit = OTG_DOEPCTL_SD0PID;
}
/* Clear the stall bit */
regval = stm32_getreg(regaddr);
regval &= ~stallbit;
/* Set the DATA0 pid for interrupt and bulk endpoints */
if (privep->eptype == USB_EP_ATTR_XFER_INT ||
privep->eptype == USB_EP_ATTR_XFER_BULK)
{
/* Writing this bit sets the DATA0 PID */
regval |= data0bit;
}
stm32_putreg(regval, regaddr);
/* The endpoint is no longer stalled */
privep->stalled = false;
return OK;
}
/****************************************************************************
* Name: stm32_ep_stall
*
* Description:
* Stall or resume an endpoint
*
****************************************************************************/
static int stm32_ep_stall(FAR struct usbdev_ep_s *ep, bool resume)
{
FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
irqstate_t flags;
int ret;
/* Set or clear the stall condition as requested */
flags = enter_critical_section();
if (resume)
{
ret = stm32_ep_clrstall(privep);
}
else
{
ret = stm32_ep_setstall(privep);
}
leave_critical_section(flags);
return ret;
}
/****************************************************************************
* Name: stm32_ep0_stall
*
* Description:
* Stall endpoint 0
*
****************************************************************************/
static void stm32_ep0_stall(FAR struct stm32_usbdev_s *priv)
{
stm32_epin_setstall(&priv->epin[EP0]);
stm32_epout_setstall(&priv->epout[EP0]);
priv->stalled = true;
stm32_ep0out_ctrlsetup(priv);
}
/****************************************************************************
* Device operations
****************************************************************************/
/****************************************************************************
* Name: stm32_ep_alloc
*
* Description:
* Allocate an endpoint matching the parameters.
*
* Input Parameters:
* eplog - 7-bit logical endpoint number (direction bit ignored). Zero means
* that any endpoint matching the other requirements will suffice. The
* assigned endpoint can be found in the eplog field.
* in - true: IN (device-to-host) endpoint requested
* eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK,
* USB_EP_ATTR_XFER_INT}
*
****************************************************************************/
static FAR struct usbdev_ep_s *stm32_ep_alloc(FAR struct usbdev_s *dev,
uint8_t eplog, bool in,
uint8_t eptype)
{
FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev;
uint8_t epavail;
irqstate_t flags;
int epphy;
int epno = 0;
usbtrace(TRACE_DEVALLOCEP, (uint16_t) eplog);
/* Ignore any direction bits in the logical address */
epphy = USB_EPNO(eplog);
/* Get the set of available endpoints depending on the direction */
flags = enter_critical_section();
epavail = priv->epavail[in];
/* A physical address of 0 means that any endpoint will do */
if (epphy > 0)
{
/* Otherwise, we will return the endpoint structure only for the
* requested 'logical' endpoint. All of the other checks will still be
* performed.
*
* First, verify that the logical endpoint is in the range supported by
* the hardware.
*/
if (epphy >= STM32_NENDPOINTS)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16_t) epphy);
return NULL;
}
/* Remove all of the candidate endpoints from the bitset except for the
* this physical endpoint number.
*/
epavail &= (1 << epphy);
}
/* Is there an available endpoint? */
if (epavail)
{
/* Yes.. Select the lowest numbered endpoint in the set of available
* endpoints.
*/
for (epno = 1; epno < STM32_NENDPOINTS; epno++)
{
uint8_t bit = 1 << epno;
if ((epavail & bit) != 0)
{
/* Mark the endpoint no longer available */
priv->epavail[in] &= ~(1 << epno);
/* And return the pointer to the standard endpoint structure */
leave_critical_section(flags);
return in ? &priv->epin[epno].ep : &priv->epout[epno].ep;
}
}
/* We should not get here */
}
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOEP), (uint16_t) eplog);
leave_critical_section(flags);
return NULL;
}
/****************************************************************************
* Name: stm32_ep_free
*
* Description:
* Free the previously allocated endpoint
*
****************************************************************************/
static void stm32_ep_free(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep)
{
FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev;
FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
irqstate_t flags;
usbtrace(TRACE_DEVFREEEP, (uint16_t) privep->epphy);
if (priv && privep)
{
/* Mark the endpoint as available */
flags = enter_critical_section();
priv->epavail[privep->isin] |= (1 << privep->epphy);
leave_critical_section(flags);
}
}
/****************************************************************************
* Name: stm32_getframe
*
* Description:
* Returns the current frame number
*
****************************************************************************/
static int stm32_getframe(struct usbdev_s *dev)
{
uint32_t regval;
usbtrace(TRACE_DEVGETFRAME, 0);
/* Return the last frame number of the last SOF detected by the hardware */
regval = stm32_getreg(STM32_OTG_DSTS);
return (int)((regval & OTG_DSTS_SOFFN_MASK) >> OTG_DSTS_SOFFN_SHIFT);
}
/****************************************************************************
* Name: stm32_wakeup
*
* Description:
* Exit suspend mode.
*
****************************************************************************/
static int stm32_wakeup(struct usbdev_s *dev)
{
FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev;
uint32_t regval;
irqstate_t flags;
usbtrace(TRACE_DEVWAKEUP, 0);
/* Is wakeup enabled? */
flags = enter_critical_section();
if (priv->wakeup)
{
/* Yes... is the core suspended? */
regval = stm32_getreg(STM32_OTG_DSTS);
if ((regval & OTG_DSTS_SUSPSTS) != 0)
{
/* Re-start the PHY clock and un-gate USB core clock (HCLK) */
# ifdef CONFIG_USBDEV_LOWPOWER
regval = stm32_getreg(STM32_OTG_PCGCCTL);
regval &= ~(OTG_PCGCCTL_STPPCLK | OTG_PCGCCTL_GATEHCLK);
stm32_putreg(regval, STM32_OTG_PCGCCTL);
# endif
/* Activate Remote wakeup signaling */
regval = stm32_getreg(STM32_OTG_DCTL);
regval |= OTG_DCTL_RWUSIG;
stm32_putreg(regval, STM32_OTG_DCTL);
up_mdelay(5);
regval &= ~OTG_DCTL_RWUSIG;
stm32_putreg(regval, STM32_OTG_DCTL);
}
}
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: stm32_selfpowered
*
* Description:
* Sets/clears the device self-powered feature
*
****************************************************************************/
static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered)
{
FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev;
usbtrace(TRACE_DEVSELFPOWERED, (uint16_t) selfpowered);
# ifdef CONFIG_DEBUG_FEATURES
if (!dev)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -ENODEV;
}
# endif
priv->selfpowered = selfpowered;
return OK;
}
/****************************************************************************
* Name: stm32_pullup
*
* Description:
* Software-controlled connect to/disconnect from USB host
*
****************************************************************************/
static int stm32_pullup(struct usbdev_s *dev, bool enable)
{
uint32_t regval;
usbtrace(TRACE_DEVPULLUP, (uint16_t) enable);
irqstate_t flags = enter_critical_section();
regval = stm32_getreg(STM32_OTG_DCTL);
if (enable)
{
/* Connect the device by clearing the soft disconnect bit in the DCTL
* register.
*/
regval &= ~OTG_DCTL_SDIS;
}
else
{
/* Disconnect the device by setting the soft disconnect bit in the DCTL
* register.
*/
regval |= OTG_DCTL_SDIS;
}
stm32_putreg(regval, STM32_OTG_DCTL);
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: stm32_setaddress
*
* Description:
* Set the devices USB address
*
****************************************************************************/
static void stm32_setaddress(struct stm32_usbdev_s *priv, uint16_t address)
{
uint32_t regval;
/* Set the device address in the DCFG register */
regval = stm32_getreg(STM32_OTG_DCFG);
regval &= ~OTG_DCFG_DAD_MASK;
regval |= ((uint32_t) address << OTG_DCFG_DAD_SHIFT);
stm32_putreg(regval, STM32_OTG_DCFG);
/* Are we now addressed? (i.e., do we have a non-NULL device address?) */
if (address != 0)
{
priv->devstate = DEVSTATE_ADDRESSED;
priv->addressed = true;
}
else
{
priv->devstate = DEVSTATE_DEFAULT;
priv->addressed = false;
}
}
/****************************************************************************
* Name: stm32_txfifo_flush
*
* Description:
* Flush the specific TX fifo.
*
****************************************************************************/
static int stm32_txfifo_flush(uint32_t txfnum)
{
uint32_t regval;
uint32_t timeout;
/* Initiate the TX FIFO flush operation */
regval = OTG_GRSTCTL_TXFFLSH | txfnum;
stm32_putreg(regval, STM32_OTG_GRSTCTL);
/* Wait for the FLUSH to complete */
for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++)
{
regval = stm32_getreg(STM32_OTG_GRSTCTL);
if ((regval & OTG_GRSTCTL_TXFFLSH) == 0)
{
break;
}
}
/* Wait for 3 PHY Clocks */
up_udelay(3);
return OK;
}
/****************************************************************************
* Name: stm32_rxfifo_flush
*
* Description:
* Flush the RX fifo.
*
****************************************************************************/
static int stm32_rxfifo_flush(void)
{
uint32_t regval;
uint32_t timeout;
/* Initiate the RX FIFO flush operation */
stm32_putreg(OTG_GRSTCTL_RXFFLSH, STM32_OTG_GRSTCTL);
/* Wait for the FLUSH to complete */
for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++)
{
regval = stm32_getreg(STM32_OTG_GRSTCTL);
if ((regval & OTG_GRSTCTL_RXFFLSH) == 0)
{
break;
}
}
/* Wait for 3 PHY Clocks */
up_udelay(3);
return OK;
}
/****************************************************************************
* Name: stm32_swinitialize
*
* Description:
* Initialize all driver data structures.
*
****************************************************************************/
static void stm32_swinitialize(FAR struct stm32_usbdev_s *priv)
{
FAR struct stm32_ep_s *privep;
int i;
/* Initialize the device state structure */
memset(priv, 0, sizeof(struct stm32_usbdev_s));
priv->usbdev.ops = &g_devops;
priv->usbdev.ep0 = &priv->epin[EP0].ep;
priv->epavail[0] = STM32_EP_AVAILABLE;
priv->epavail[1] = STM32_EP_AVAILABLE;
priv->epin[EP0].ep.priv = priv;
priv->epout[EP0].ep.priv = priv;
/* Initialize the endpoint lists */
for (i = 0; i < STM32_NENDPOINTS; i++)
{
/* Set endpoint operations, reference to driver structure (not really
* necessary because there is only one controller), and the physical
* endpoint number (which is just the index to the endpoint).
*/
privep = &priv->epin[i];
privep->ep.ops = &g_epops;
privep->dev = priv;
privep->isin = 1;
/* The index, i, is the physical endpoint address; Map this to a logical
* endpoint address usable by the class driver.
*/
privep->epphy = i;
privep->ep.eplog = STM32_EPPHYIN2LOG(i);
/* Control until endpoint is activated */
privep->eptype = USB_EP_ATTR_XFER_CONTROL;
privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE;
}
/* Initialize the endpoint lists */
for (i = 0; i < STM32_NENDPOINTS; i++)
{
/* Set endpoint operations, reference to driver structure (not really
* necessary because there is only one controller), and the physical
* endpoint number (which is just the index to the endpoint).
*/
privep = &priv->epout[i];
privep->ep.ops = &g_epops;
privep->dev = priv;
/* The index, i, is the physical endpoint address; Map this to a logical
* endpoint address usable by the class driver.
*/
privep->epphy = i;
privep->ep.eplog = STM32_EPPHYOUT2LOG(i);
/* Control until endpoint is activated */
privep->eptype = USB_EP_ATTR_XFER_CONTROL;
privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE;
}
}
/****************************************************************************
* Name: stm32_hwinitialize
*
* Description:
* Configure the OTG core for operation.
*
****************************************************************************/
static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv)
{
uint32_t regval;
uint32_t timeout;
uint32_t address;
int i;
/* At start-up the core is in FS/HS mode. */
/* Disable global interrupts by clearing the GINTMASK bit in the GAHBCFG
* register; Set the TXFELVL bit in the GAHBCFG register so that TxFIFO
* interrupts will occur when the TxFIFO is truly empty (not just half full).
*/
stm32_putreg(OTG_GAHBCFG_TXFELVL, STM32_OTG_GAHBCFG);
# ifdef CONFIG_STM32F7_OTGFSHS
# ifdef CONFIG_STM32F7_NO_ULPI
regval = stm32_getreg(STM32_OTG_GUSBCFG);
regval |= OTG_GUSBCFG_PHYSEL;
stm32_putreg(regval, STM32_OTG_GUSBCFG);
# else /* CONFIG_STM32F7_NO_ULPI */
/* Switch off FS transceiver */
regval = stm32_getreg(STM32_OTG_GCCFG);
regval &= ~(OTG_GCCFG_PWRDWN);
stm32_putreg(regval, STM32_OTG_GCCFG);
/* Init The ULPI Interface: Set the PHYSEL bit in the GUSBCFG register to
* select the OTG HS serial 0: USB 2.0 external ULPI high-speed PHY or
* internal UTMI high-speed PHY" 1: USB 1.1 full-speed serial transceiver
*/
regval = stm32_getreg(STM32_OTG_GUSBCFG);
regval &= ~(OTG_GUSBCFG_TSDPS | OTG_GUSBCFG_ULPIFSLS | OTG_GUSBCFG_PHYSEL);
stm32_putreg(regval, STM32_OTG_GUSBCFG);
/* Select vbus source */
regval = stm32_getreg(STM32_OTG_GUSBCFG);
regval &= ~(OTG_GUSBCFG_ULPIEVBUSD | OTG_GUSBCFG_ULPIEVBUSI);
stm32_putreg(regval, STM32_OTG_GUSBCFG);
# ifdef CONFIG_STM32F7_INTERNAL_ULPI
/* Select UTMI/ULPI Interface */
regval = stm32_getreg(STM32_OTG_GUSBCFG);
regval &= ~OTG_GUSBCFG_ULPISEL;
stm32_putreg(regval, STM32_OTG_GUSBCFG);
/* Not in datasheet but in stmcube ? */
regval = stm32_getreg(STM32_OTG_GCCFG);
regval |= OTG_GCCFG_PHYHSEN;
stm32_putreg(regval, STM32_OTG_GCCFG);
/* Enable LDO */
stm32_putreg(USBPHYC_LDO_ENABLE, STM32_USBPHYC_LDO);
/* wait for LDO Ready */
while (!(stm32_getreg(STM32_USBPHYC_LDO) & USBPHYC_LDO_STATUS));
/* Controls PHY frequency operation selection */
stm32_putreg(USBPHYC_PLL1_SEL_25MHz, STM32_USBPHYC_PLL1);
/* Control the tuning interface of the High Speed PHY */
regval = stm32_getreg(STM32_USBPHYC_TUNE);
/* TODO set right value */
regval |= 0x00000f13;
stm32_putreg(regval, STM32_USBPHYC_TUNE);
/* Enable PLL internal PHY */
regval = stm32_getreg(STM32_USBPHYC_PLL1);
regval |= USBPHYC_PLL1_EN;
stm32_putreg(regval, STM32_USBPHYC_PLL1);
/* 2ms delay required for clk to be stable */
up_udelay(2000);
# endif /* CONFIG_STM32F7_INTERNAL_ULPI */
# endif /* CONFIG_STM32F7_NO_ULPI */
# endif /* CONFIG_STM32F7_OTGFSHS */
/* Common USB OTG core initialization */
/* Reset after a PHY select and set Host mode. First, wait for AHB master
* IDLE state.
*/
for (timeout = 0; timeout < STM32_READY_DELAY; timeout++)
{
up_udelay(3);
regval = stm32_getreg(STM32_OTG_GRSTCTL);
if ((regval & OTG_GRSTCTL_AHBIDL) != 0)
{
break;
}
}
/* Then perform the core soft reset. */
regval = stm32_getreg(STM32_OTG_GRSTCTL);
regval |= OTG_GRSTCTL_CSRST;
stm32_putreg(regval, STM32_OTG_GRSTCTL);
for (timeout = 0; timeout < STM32_READY_DELAY; timeout++)
{
regval = stm32_getreg(STM32_OTG_GRSTCTL);
if ((regval & OTG_GRSTCTL_CSRST) == 0)
{
break;
}
}
/* Wait for 3 PHY Clocks */
up_udelay(3);
/* Deactivate the power down */
/* Detection Enable when set */
regval = stm32_getreg(STM32_OTG_GCCFG);
# if (defined(CONFIG_STM32F7_OTGFS) || defined(CONFIG_STM32F7_NO_ULPI))
regval |= OTG_GCCFG_PWRDWN;
# endif
# ifdef CONFIG_USBDEV_VBUSSENSING
regval |= OTG_GCCFG_VBDEN;
# endif
stm32_putreg(regval, STM32_OTG_GCCFG);
up_mdelay(20);
/* When VBUS sensing is not used we need to force the B session valid */
# ifndef CONFIG_USBDEV_VBUSSENSING
regval = stm32_getreg(STM32_OTG_GOTGCTL);
regval |= (OTG_GOTGCTL_BVALOEN | OTG_GOTGCTL_BVALOVAL);
stm32_putreg(regval, STM32_OTG_GOTGCTL);
# endif
/* Force Device Mode */
regval = stm32_getreg(STM32_OTG_GUSBCFG);
regval &= ~OTG_GUSBCFG_FHMOD;
regval |= OTG_GUSBCFG_FDMOD;
stm32_putreg(regval, STM32_OTG_GUSBCFG);
up_mdelay(50);
/* Initialize device mode */
/* Restart the PHY Clock */
stm32_putreg(0, STM32_OTG_PCGCCTL);
/* Device configuration register */
regval = stm32_getreg(STM32_OTG_DCFG);
regval &= ~OTG_DCFG_PFIVL_MASK;
regval |= OTG_DCFG_PFIVL_80PCT;
stm32_putreg(regval, STM32_OTG_DCFG);
/* Set PHY speed */
regval = stm32_getreg(STM32_OTG_DCFG);
regval &= ~OTG_DCFG_DSPD_MASK;
# ifdef CONFIG_STM32F7_OTGFSHS
regval |= OTG_DCFG_DSPD_HS;
# else
regval |= OTG_DCFG_DSPD_FS;
# endif
stm32_putreg(regval, STM32_OTG_DCFG);
/* Set Rx FIFO size */
stm32_putreg(STM32_RXFIFO_WORDS, STM32_OTG_GRXFSIZ);
# if STM32_NENDPOINTS > 0
address = STM32_RXFIFO_WORDS;
regval = (address << OTG_DIEPTXF0_TX0FD_SHIFT) |
(STM32_EP0_TXFIFO_WORDS << OTG_DIEPTXF0_TX0FSA_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF0);
# endif
# if STM32_NENDPOINTS > 1
address += STM32_EP0_TXFIFO_WORDS;
regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) |
(STM32_EP1_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF(1));
# endif
# if STM32_NENDPOINTS > 2
address += STM32_EP1_TXFIFO_WORDS;
regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) |
(STM32_EP2_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF(2));
# endif
# if STM32_NENDPOINTS > 3
address += STM32_EP2_TXFIFO_WORDS;
regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) |
(STM32_EP3_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF(3));
# endif
# if STM32_NENDPOINTS > 4
address += STM32_EP3_TXFIFO_WORDS;
regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) |
(STM32_EP4_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF(4));
# endif
# if STM32_NENDPOINTS > 5
address += STM32_EP4_TXFIFO_WORDS;
regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) |
(STM32_EP5_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF(5));
# endif
# if STM32_NENDPOINTS > 6
address += STM32_EP5_TXFIFO_WORDS;
regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) |
(STM32_EP6_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF(6));
# endif
# if STM32_NENDPOINTS > 7
address += STM32_EP6_TXFIFO_WORDS;
regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) |
(STM32_EP7_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF(7));
# endif
# if STM32_NENDPOINTS > 8
address += STM32_EP7_TXFIFO_WORDS;
regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) |
(STM32_EP8_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT);
stm32_putreg(regval, STM32_OTG_DIEPTXF(8));
# endif
/* Flush the FIFOs */
stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_DALL);
stm32_rxfifo_flush();
/* Clear all pending Device Interrupts */
stm32_putreg(0, STM32_OTG_DIEPMSK);
stm32_putreg(0, STM32_OTG_DOEPMSK);
stm32_putreg(0, STM32_OTG_DIEPEMPMSK);
stm32_putreg(0xffffffff, STM32_OTG_DAINT);
stm32_putreg(0, STM32_OTG_DAINTMSK);
/* Configure all IN endpoints */
for (i = 0; i < STM32_NENDPOINTS; i++)
{
regval = stm32_getreg(STM32_OTG_DIEPCTL(i));
if ((regval & OTG_DIEPCTL_EPENA) != 0)
{
/* The endpoint is already enabled */
regval = OTG_DIEPCTL_EPDIS | OTG_DIEPCTL_SNAK;
}
else
{
regval = 0;
}
stm32_putreg(regval, STM32_OTG_DIEPCTL(i));
stm32_putreg(0, STM32_OTG_DIEPTSIZ(i));
stm32_putreg(0xff, STM32_OTG_DIEPINT(i));
}
/* Configure all OUT endpoints */
for (i = 0; i < STM32_NENDPOINTS; i++)
{
regval = stm32_getreg(STM32_OTG_DOEPCTL(i));
if ((regval & OTG_DOEPCTL_EPENA) != 0)
{
/* The endpoint is already enabled */
regval = OTG_DOEPCTL_EPDIS | OTG_DOEPCTL_SNAK;
}
else
{
regval = 0;
}
stm32_putreg(regval, STM32_OTG_DOEPCTL(i));
stm32_putreg(0, STM32_OTG_DOEPTSIZ(i));
stm32_putreg(0xff, STM32_OTG_DOEPINT(i));
}
/* Disable all interrupts. */
stm32_putreg(0, STM32_OTG_GINTMSK);
/* Clear any pending USB_OTG Interrupts */
stm32_putreg(0xffffffff, STM32_OTG_GOTGINT);
/* Clear any pending interrupts */
regval = stm32_getreg(STM32_OTG_GINTSTS);
regval &= OTG_GINT_RESERVED;
stm32_putreg(regval | OTG_GINT_RC_W1, STM32_OTG_GINTSTS);
# if defined(CONFIG_STM32F7_OTGFSHS) && defined(CONFIG_STM32F7_NO_ULPI)
/* Disable the ULPI Clock enable in RCC AHB1 Register. This must be done
* because if both the ULPI and the FS PHY clock enable bits are set at the
* same time, the ARM never awakens from WFI due to some bug / errata in the
* chip.
*/
regval = stm32_getreg(STM32_RCC_AHB1LPENR);
regval &= ~RCC_AHB1ENR_OTGHSULPIEN;
stm32_putreg(regval, STM32_RCC_AHB1LPENR);
# endif
/* Enable the interrupts in the INTMSK */
regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_ENUMDNE |
OTG_GINT_IEP | OTG_GINT_OEP | OTG_GINT_USBRST);
# ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR);
# endif
# ifdef CONFIG_USBDEV_SOFINTERRUPT
regval |= OTG_GINT_SOF;
# endif
# ifdef CONFIG_USBDEV_VBUSSENSING
regval |= (OTG_GINT_OTG | OTG_GINT_SRQ);
# endif
# ifdef CONFIG_DEBUG_USB
regval |= OTG_GINT_MMIS;
# endif
stm32_putreg(regval, STM32_OTG_GINTMSK);
/* Enable the USB global interrupt by setting GINTMSK in the global OTG AHB
* configuration register; Set the TXFELVL bit in the GAHBCFG register so
* that TxFIFO interrupts will occur when the TxFIFO is truly empty (not just
* half full).
*/
stm32_putreg(OTG_GAHBCFG_GINTMSK | OTG_GAHBCFG_TXFELVL, STM32_OTG_GAHBCFG);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: arm_usbinitialize
*
* Description:
* Initialize USB hardware.
*
* Assumptions:
* - This function is called very early in the initialization sequence
* - PLL and GIO pin initialization is not performed here but should been in
* the low-level boot logic: PLL1 must be configured for operation at 48MHz
* and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect
* LED.
*
****************************************************************************/
void arm_usbinitialize(void)
{
/* At present, there is only a single OTG device support. Hence it is
* pre-allocated as g_otghsdev. However, in most code, the private data
* structure will be referenced using the 'priv' pointer (rather than the
* global data) in order to simplify any future support for multiple devices.
*/
FAR struct stm32_usbdev_s *priv = &g_otghsdev;
int ret;
usbtrace(TRACE_DEVINIT, 0);
/* Here we assume that: 1. GPIOA and OTG peripheral clocking has already
* been enabled as part of the boot sequence. 2. Board-specific logic has
* already enabled other board specific GPIOs for things like soft pull-up,
* VBUS sensing, power controls, and over- current detection.
*/
/* Configure OTG alternate function pins */
stm32_configgpio(GPIO_OTG_DM);
stm32_configgpio(GPIO_OTG_DP);
stm32_configgpio(GPIO_OTG_ID); /* Only needed for OTG */
/* SOF output pin configuration is configurable. */
# ifdef CONFIG_STM32F7_OTG_SOFOUTPUT
stm32_configgpio(GPIO_OTG_SOF);
# endif
/* Uninitialize the hardware so that we know that we are starting from a
* known state.
*/
arm_usbuninitialize();
/* Initialie the driver data structure */
stm32_swinitialize(priv);
/* Attach the OTG interrupt handler */
ret = irq_attach(STM32_IRQ_OTG, stm32_usbinterrupt, NULL);
if (ret < 0)
{
uerr("irq_attach failed\n", ret);
goto errout;
}
/* Initialize the USB OTG core */
stm32_hwinitialize(priv);
/* Disconnect device */
stm32_pullup(&priv->usbdev, false);
/* Reset/Re-initialize the USB hardware */
stm32_usbreset(priv);
/* Enable USB controller interrupts at the NVIC */
up_enable_irq(STM32_IRQ_OTG);
return;
errout:
arm_usbuninitialize();
}
/****************************************************************************
* Name: arm_usbuninitialize
****************************************************************************/
void arm_usbuninitialize(void)
{
/* At present, there is only a single OTG device support. Hence it is
* pre-allocated as g_otghsdev. However, in most code, the private data
* structure will be referenced using the 'priv' pointer (rather than the
* global data) in order to simplify any future support for multiple devices.
*/
FAR struct stm32_usbdev_s *priv = &g_otghsdev;
irqstate_t flags;
int i;
usbtrace(TRACE_DEVUNINIT, 0);
if (priv->driver)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0);
usbdev_unregister(priv->driver);
}
/* Disconnect device */
flags = enter_critical_section();
stm32_pullup(&priv->usbdev, false);
priv->usbdev.speed = USB_SPEED_UNKNOWN;
/* Disable and detach IRQs */
up_disable_irq(STM32_IRQ_OTG);
irq_detach(STM32_IRQ_OTG);
/* Disable all endpoint interrupts */
for (i = 0; i < STM32_NENDPOINTS; i++)
{
stm32_putreg(0xff, STM32_OTG_DIEPINT(i));
stm32_putreg(0xff, STM32_OTG_DOEPINT(i));
}
stm32_putreg(0, STM32_OTG_DIEPMSK);
stm32_putreg(0, STM32_OTG_DOEPMSK);
stm32_putreg(0, STM32_OTG_DIEPEMPMSK);
stm32_putreg(0, STM32_OTG_DAINTMSK);
stm32_putreg(0xffffffff, STM32_OTG_DAINT);
/* Flush the FIFOs */
stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_DALL);
stm32_rxfifo_flush();
/* TODO: Turn off USB power and clocking */
priv->devstate = DEVSTATE_DEFAULT;
leave_critical_section(flags);
}
/****************************************************************************
* Name: usbdev_register
*
* Description:
* Register a USB device class driver. The class driver's bind() method will be
* called to bind it to a USB device driver.
*
****************************************************************************/
int usbdev_register(struct usbdevclass_driver_s *driver)
{
/* At present, there is only a single OTG device support. Hence it is
* pre-allocated as g_otghsdev. However, in most code, the private data
* structure will be referenced using the 'priv' pointer (rather than the
* global data) in order to simplify any future support for multiple devices.
*/
FAR struct stm32_usbdev_s *priv = &g_otghsdev;
int ret;
usbtrace(TRACE_DEVREGISTER, 0);
# ifdef CONFIG_DEBUG_FEATURES
if (!driver || !driver->ops->bind || !driver->ops->unbind ||
!driver->ops->disconnect || !driver->ops->setup)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
if (priv->driver)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVER), 0);
return -EBUSY;
}
# endif
/* First hook up the driver */
priv->driver = driver;
/* Then bind the class driver */
ret = CLASS_BIND(driver, &priv->usbdev);
if (ret)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BINDFAILED), (uint16_t) - ret);
priv->driver = NULL;
}
else
{
/* Enable USB controller interrupts */
up_enable_irq(STM32_IRQ_OTG);
/* FIXME: nothing seems to call DEV_CONNECT(), but we need to set the
* RS bit to enable the controller. It kind of makes sense to
* do this after the class has bound to us...
* GEN: This bug is really in the class driver. It should make the
* soft connect when it is ready to be enumerated. I have
* added that logic to the class drivers but left this logic
* here.
*/
stm32_pullup(&priv->usbdev, true);
# if defined(CONFIG_STM32F7_INTERNAL_ULPI) || defined(CONFIG_STM32F7_EXTERNAL_ULPI)
priv->usbdev.speed = USB_SPEED_HIGH;
# else
priv->usbdev.speed = USB_SPEED_FULL;
# endif
}
return ret;
}
/****************************************************************************
* Name: usbdev_unregister
*
* Description:
* Un-register usbdev class driver.If the USB device is connected to a USB host,
* it will first disconnect(). The driver is also requested to unbind() and clean
* up any device state, before this procedure finally returns.
*
****************************************************************************/
int usbdev_unregister(struct usbdevclass_driver_s *driver)
{
/* At present, there is only a single OTG device support. Hence it is
* pre-allocated as g_otghsdev. However, in most code, the private data
* structure will be referenced using the 'priv' pointer (rather than the
* global data) in order to simplify any future support for multiple devices.
*/
FAR struct stm32_usbdev_s *priv = &g_otghsdev;
irqstate_t flags;
usbtrace(TRACE_DEVUNREGISTER, 0);
# ifdef CONFIG_DEBUG_FEATURES
if (driver != priv->driver)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
# endif
/* Reset the hardware and cancel all requests. All requests must be canceled
* while the class driver is still bound.
*/
flags = enter_critical_section();
stm32_usbreset(priv);
leave_critical_section(flags);
/* Unbind the class driver */
CLASS_UNBIND(driver, &priv->usbdev);
/* Disable USB controller interrupts */
flags = enter_critical_section();
up_disable_irq(STM32_IRQ_OTG);
/* Disconnect device */
stm32_pullup(&priv->usbdev, false);
/* Unhook the driver */
priv->driver = NULL;
leave_critical_section(flags);
return OK;
}
#endif /* CONFIG_USBDEV && CONFIG_STM32F7_OTGDEV */