You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/05/09 02:34:47 UTC

[incubator-nuttx] 01/03: stm32 usbfs: Add copy of stm32_usbdev

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 654960da4b339547155d97a8e768c75b2f41e3ed
Author: Zou Hanya <ha...@gmail.com>
AuthorDate: Sat May 7 06:11:50 2022 +0900

    stm32 usbfs: Add copy of stm32_usbdev
---
 arch/arm/src/stm32/hardware/stm32_usbfs.h |  225 ++
 arch/arm/src/stm32/stm32_usbfs.c          | 4010 +++++++++++++++++++++++++++++
 arch/arm/src/stm32/stm32_usbfs.h          |   82 +
 3 files changed, 4317 insertions(+)

diff --git a/arch/arm/src/stm32/hardware/stm32_usbfs.h b/arch/arm/src/stm32/hardware/stm32_usbfs.h
new file mode 100644
index 0000000000..bebec9a0ce
--- /dev/null
+++ b/arch/arm/src/stm32/hardware/stm32_usbfs.h
@@ -0,0 +1,225 @@
+/****************************************************************************
+ * arch/arm/src/stm32/hardware/stm32_usbdev.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32_HARDWARE_STM32_USBDEV_H
+#define __ARCH_ARM_SRC_STM32_HARDWARE_STM32_USBDEV_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "chip.h"
+
+#if defined(CONFIG_STM32_STM32L15XX) || defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) \
+    || defined(CONFIG_STM32_STM32F37XX)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+/* Endpoint Registers */
+
+#define STM32_USB_EPR_OFFSET(n)      ((n) << 2) /* USB endpoint n register (16-bits) */
+
+#define STM32_USB_EP0R_OFFSET        0x0000  /* USB endpoint 0 register (16-bits) */
+#define STM32_USB_EP1R_OFFSET        0x0004  /* USB endpoint 1 register (16-bits) */
+#define STM32_USB_EP2R_OFFSET        0x0008  /* USB endpoint 2 register (16-bits) */
+#define STM32_USB_EP3R_OFFSET        0x000c  /* USB endpoint 3 register (16-bits) */
+#define STM32_USB_EP4R_OFFSET        0x0010  /* USB endpoint 4 register (16-bits) */
+#define STM32_USB_EP5R_OFFSET        0x0014  /* USB endpoint 5 register (16-bits) */
+#define STM32_USB_EP6R_OFFSET        0x0018  /* USB endpoint 6 register (16-bits) */
+#define STM32_USB_EP7R_OFFSET        0x001c  /* USB endpoint 7 register (16-bits) */
+
+/* Common Registers */
+
+#define STM32_USB_CNTR_OFFSET        0x0040  /* USB control register (16-bits) */
+#define STM32_USB_ISTR_OFFSET        0x0044  /* USB interrupt status register (16-bits) */
+#define STM32_USB_FNR_OFFSET         0x0048  /* USB frame number register (16-bits) */
+#define STM32_USB_DADDR_OFFSET       0x004c  /* USB device address (16-bits) */
+#define STM32_USB_BTABLE_OFFSET      0x0050  /* Buffer table address (16-bits) */
+
+/* Buffer Descriptor Table (Relatative to BTABLE address) */
+
+#define STM32_USB_ADDR_TX_WOFFSET   (0)     /* Transmission buffer address n (16-bits) */
+#define STM32_USB_COUNT_TX_WOFFSET  (2)     /* Transmission byte count n (16-bits) */
+#define STM32_USB_ADDR_RX_WOFFSET   (4)     /* Reception buffer address n (16-bits) */
+#define STM32_USB_COUNT_RX_WOFFSET  (6)     /* Reception byte count n (16-bits) */
+
+#define STM32_USB_BTABLE_RADDR(ep,o) ((((uint32_t)getreg16(STM32_USB_BTABLE) + ((ep) << 3)) + (o))  << 1)
+#define STM32_USB_ADDR_TX_OFFSET(ep)  STM32_USB_BTABLE_RADDR(ep,STM32_USB_ADDR_TX_WOFFSET)
+#define STM32_USB_COUNT_TX_OFFSET(ep) STM32_USB_BTABLE_RADDR(ep,STM32_USB_COUNT_TX_WOFFSET)
+#define STM32_USB_ADDR_RX_OFFSET(ep)  STM32_USB_BTABLE_RADDR(ep,STM32_USB_ADDR_RX_WOFFSET)
+#define STM32_USB_COUNT_RX_OFFSET(ep) STM32_USB_BTABLE_RADDR(ep,STM32_USB_COUNT_RX_WOFFSET)
+
+/* Register Addresses *******************************************************/
+
+/* Endpoint Registers */
+
+#define STM32_USB_EPR(n)             (STM32_USB_BASE+STM32_USB_EPR_OFFSET(n))
+#define STM32_USB_EP0R               (STM32_USB_BASE+STM32_USB_EP0R_OFFSET)
+#define STM32_USB_EP1R               (STM32_USB_BASE+STM32_USB_EP1R_OFFSET)
+#define STM32_USB_EP2R               (STM32_USB_BASE+STM32_USB_EP2R_OFFSET)
+#define STM32_USB_EP3R               (STM32_USB_BASE+STM32_USB_EP3R_OFFSET)
+#define STM32_USB_EP4R               (STM32_USB_BASE+STM32_USB_EP4R_OFFSET)
+#define STM32_USB_EP5R               (STM32_USB_BASE+STM32_USB_EP5R_OFFSET)
+#define STM32_USB_EP6R               (STM32_USB_BASE+STM32_USB_EP6R_OFFSET)
+#define STM32_USB_EP7R               (STM32_USB_BASE+STM32_USB_EP7R_OFFSET)
+
+/* Common Registers */
+
+#define STM32_USB_CNTR               (STM32_USB_BASE+STM32_USB_CNTR_OFFSET)
+#define STM32_USB_ISTR               (STM32_USB_BASE+STM32_USB_ISTR_OFFSET)
+#define STM32_USB_FNR                (STM32_USB_BASE+STM32_USB_FNR_OFFSET)
+#define STM32_USB_DADDR              (STM32_USB_BASE+STM32_USB_DADDR_OFFSET)
+#define STM32_USB_BTABLE             (STM32_USB_BASE+STM32_USB_BTABLE_OFFSET)
+
+/* Buffer Descriptor Table (Relatative to BTABLE address) */
+
+#define STM32_USB_BTABLE_ADDR(ep,o)  (STM32_USBRAM_BASE+STM32_USB_BTABLE_RADDR(ep,o))
+#define STM32_USB_ADDR_TX(ep)        STM32_USB_BTABLE_ADDR(ep,STM32_USB_ADDR_TX_WOFFSET)
+#define STM32_USB_COUNT_TX(ep)       STM32_USB_BTABLE_ADDR(ep,STM32_USB_COUNT_TX_WOFFSET)
+#define STM32_USB_ADDR_RX(ep)        STM32_USB_BTABLE_ADDR(ep,STM32_USB_ADDR_RX_WOFFSET)
+#define STM32_USB_COUNT_RX(ep)       STM32_USB_BTABLE_ADDR(ep,STM32_USB_COUNT_RX_WOFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* USB endpoint register */
+
+#define USB_EPR_EA_SHIFT             (0)       /* Bits 3:0 [3:0]: Endpoint Address */
+#define USB_EPR_EA_MASK              (0X0f << USB_EPR_EA_SHIFT)
+#define USB_EPR_STATTX_SHIFT         (4)       /* Bits 5-4: Status bits, for transmission transfers */
+#define USB_EPR_STATTX_MASK          (3 << USB_EPR_STATTX_SHIFT)
+#  define USB_EPR_STATTX_DIS         (0 << USB_EPR_STATTX_SHIFT) /* EndPoint TX DISabled */
+#  define USB_EPR_STATTX_STALL       (1 << USB_EPR_STATTX_SHIFT) /* EndPoint TX STALLed */
+#  define USB_EPR_STATTX_NAK         (2 << USB_EPR_STATTX_SHIFT) /* EndPoint TX NAKed */
+#  define USB_EPR_STATTX_VALID       (3 << USB_EPR_STATTX_SHIFT) /* EndPoint TX VALID */
+#  define USB_EPR_STATTX_DTOG1       (1 << USB_EPR_STATTX_SHIFT) /* EndPoint TX Data Toggle bit1 */
+#  define USB_EPR_STATTX_DTOG2       (2 << USB_EPR_STATTX_SHIFT) /* EndPoint TX Data Toggle bit2 */
+
+#define USB_EPR_DTOG_TX              (1 << 6)  /* Bit 6: Data Toggle, for transmission transfers */
+#define USB_EPR_CTR_TX               (1 << 7)  /* Bit 7: Correct Transfer for transmission */
+#define USB_EPR_EP_KIND              (1 << 8)  /* Bit 8: Endpoint Kind */
+#define USB_EPR_EPTYPE_SHIFT         (9)       /* Bits 10-9: Endpoint type */
+#define USB_EPR_EPTYPE_MASK          (3 << USB_EPR_EPTYPE_SHIFT)
+#  define USB_EPR_EPTYPE_BULK        (0 << USB_EPR_EPTYPE_SHIFT) /* EndPoint BULK */
+#  define USB_EPR_EPTYPE_CONTROL     (1 << USB_EPR_EPTYPE_SHIFT) /* EndPoint CONTROL */
+#  define USB_EPR_EPTYPE_ISOC        (2 << USB_EPR_EPTYPE_SHIFT) /* EndPoint ISOCHRONOUS */
+#  define USB_EPR_EPTYPE_INTERRUPT   (3 << USB_EPR_EPTYPE_SHIFT) /* EndPoint INTERRUPT */
+
+#define USB_EPR_SETUP                (1 << 11) /* Bit 11: Setup transaction completed */
+#define USB_EPR_STATRX_SHIFT         (12)      /* Bits 13-12: Status bits, for reception transfers */
+#define USB_EPR_STATRX_MASK          (3 << USB_EPR_STATRX_SHIFT)
+#  define USB_EPR_STATRX_DIS         (0 << USB_EPR_STATRX_SHIFT) /* EndPoint RX DISabled */
+#  define USB_EPR_STATRX_STALL       (1 << USB_EPR_STATRX_SHIFT) /* EndPoint RX STALLed */
+#  define USB_EPR_STATRX_NAK         (2 << USB_EPR_STATRX_SHIFT) /* EndPoint RX NAKed */
+#  define USB_EPR_STATRX_VALID       (3 << USB_EPR_STATRX_SHIFT) /* EndPoint RX VALID */
+#  define USB_EPR_STATRX_DTOG1       (1 << USB_EPR_STATRX_SHIFT) /* EndPoint RX Data TOGgle bit1 */
+#  define USB_EPR_STATRX_DTOG2       (2 << USB_EPR_STATRX_SHIFT) /* EndPoint RX Data TOGgle bit1 */
+
+#define USB_EPR_DTOG_RX              (1 << 14) /* Bit 14: Data Toggle, for reception transfers */
+#define USB_EPR_CTR_RX               (1 << 15) /* Bit 15: Correct Transfer for reception */
+
+/* USB control register */
+
+#define USB_CNTR_FRES                (1 << 0)  /* Bit 0: Force USB Reset */
+#define USB_CNTR_PDWN                (1 << 1)  /* Bit 1: Power down */
+#define USB_CNTR_LPMODE              (1 << 2)  /* Bit 2: Low-power mode */
+#define USB_CNTR_FSUSP               (1 << 3)  /* Bit 3: Force suspend */
+#define USB_CNTR_RESUME              (1 << 4)  /* Bit 4: Resume request */
+#define USB_CNTR_ESOFM               (1 << 8)  /* Bit 8: Expected Start Of Frame Interrupt Mask */
+#define USB_CNTR_SOFM                (1 << 9)  /* Bit 9: Start Of Frame Interrupt Mask */
+#define USB_CNTR_RESETM              (1 << 10) /* Bit 10: USB Reset Interrupt Mask */
+#define USB_CNTR_SUSPM               (1 << 11) /* Bit 11: Suspend mode Interrupt Mask */
+#define USB_CNTR_WKUPM               (1 << 12) /* Bit 12: Wakeup Interrupt Mask */
+#define USB_CNTR_ERRM                (1 << 13) /* Bit 13: Error Interrupt Mask */
+#define USB_CNTR_DMAOVRNM            (1 << 14) /* Bit 14: Packet Memory Area Over / Underrun Interrupt Mask */
+#define USB_CNTR_CTRM                (1 << 15) /* Bit 15: Correct Transfer Interrupt Mask */
+
+#define USB_CNTR_ALLINTS             (USB_CNTR_ESOFM|USB_CNTR_SOFM|USB_CNTR_RESETM|USB_CNTR_SUSPM|\
+                                      USB_CNTR_WKUPM|USB_CNTR_ERRM|USB_CNTR_DMAOVRNM|USB_CNTR_CTRM)
+
+/* USB interrupt status register */
+
+#define USB_ISTR_EPID_SHIFT          (0)       /* Bits 3-0: Endpoint Identifier */
+#define USB_ISTR_EPID_MASK           (0x0f << USB_ISTR_EPID_SHIFT)
+#define USB_ISTR_DIR                 (1 << 4)  /* Bit 4: Direction of transaction */
+#define USB_ISTR_ESOF                (1 << 8)  /* Bit 8: Expected Start Of Frame */
+#define USB_ISTR_SOF                 (1 << 9)  /* Bit 9: Start Of Frame */
+#define USB_ISTR_RESET               (1 << 10) /* Bit 10: USB RESET request */
+#define USB_ISTR_SUSP                (1 << 11) /* Bit 11: Suspend mode request */
+#define USB_ISTR_WKUP                (1 << 12) /* Bit 12: Wake up */
+#define USB_ISTR_ERR                 (1 << 13) /* Bit 13: Error */
+#define USB_ISTR_DMAOVRN             (1 << 14) /* Bit 14: Packet Memory Area Over / Underrun */
+#define USB_ISTR_CTR                 (1 << 15) /* Bit 15: Correct Transfer */
+
+#define USB_ISTR_ALLINTS             (USB_ISTR_ESOF|USB_ISTR_SOF|USB_ISTR_RESET|USB_ISTR_SUSP|\
+                                      USB_ISTR_WKUP|USB_ISTR_ERR|USB_ISTR_DMAOVRN|USB_ISTR_CTR)
+
+/* USB frame number register */
+
+#define USB_FNR_FN_SHIFT             (0)       /* Bits 10-0: Frame Number */
+#define USB_FNR_FN_MASK              (0x07ff << USB_FNR_FN_SHIFT)
+#define USB_FNR_LSOF_SHIFT           (11)      /* Bits 12-11: Lost SOF */
+#define USB_FNR_LSOF_MASK            (3 << USB_FNR_LSOF_SHIFT)
+#define USB_FNR_LCK                  (1 << 13) /* Bit 13: Locked */
+#define USB_FNR_RXDM                 (1 << 14) /* Bit 14: Receive Data - Line Status */
+#define USB_FNR_RXDP                 (1 << 15) /* Bit 15: Receive Data + Line Status */
+
+/* USB device address */
+
+#define USB_DADDR_ADD_SHIFT          (0)       /* Bits 6-0: Device Address */
+#define USB_DADDR_ADD_MASK           (0x7f << USB_DADDR_ADD_SHIFT)
+#define USB_DADDR_EF                 (1 << 7)  /* Bit 7: Enable Function */
+
+/* Buffer table address */
+
+#define USB_BTABLE_SHIFT             (3)       /* Bits 15:3: Buffer Table */
+#define USB_BTABLE_MASK              (0x1fff << USB_BTABLE_SHIFT)
+
+/* Transmission buffer address */
+
+#define USB_ADDR_TX_ZERO             (1 << 0)  /* Bit 0 Must always be written as ‘0’ */
+#define USB_ADDR_TX_SHIFT            (1)       /* Bits 15-1: Transmission Buffer Address */
+#define USB_ADDR_TX_MASK             (0x7fff << USB_ADDR_ADDR_TX_SHIFT)
+
+/* Transmission byte count */
+
+#define USB_COUNT_TX_SHIFT           (0)       /* Bits 9-0: Transmission Byte Count */
+#define USB_COUNT_TX_MASK            (0x03ff << USB_COUNT_COUNT_TX_SHIFT)
+
+/* Reception buffer address */
+
+#define USB_ADDR_RX_ZERO             (1 << 0)  /* Bit 0 This bit must always be written as ‘0’ */
+#define USB_ADDR_RX_SHIFT            (1)       /* Bits 15:1 ADDRn_RX[15:1]: Reception Buffer Address */
+#define USB_ADDR_RX_MASK             (0x7fff << USB_ADDR_RX_SHIFT)
+
+/* Reception byte count */
+
+#define USB_COUNT_RX_BL_SIZE         (1 << 15) /* Bit 15: BLock SIZE. */
+#define USB_COUNT_RX_NUM_BLOCK_SHIFT (10)      /* Bits 14-10: Number of blocks */
+#define USB_COUNT_RX_NUM_BLOCK_MASK  (0x1f << USB_COUNT_RX_NUM_BLOCK_SHIFT)
+#define USB_COUNT_RX_SHIFT           (0)       /* Bits 9-0: Reception Byte Count */
+#define USB_COUNT_RX_MASK            (0x03ff << USB_COUNT_RX_SHIFT)
+
+#endif /* CONFIG_STM32_STM32F10XX || CONFIG_STM32_STM32F30XX || CONFIG_STM32_STM32F37XX */
+#endif /* __ARCH_ARM_SRC_STM32_HARDWARE_STM32_USBDEV_H */
diff --git a/arch/arm/src/stm32/stm32_usbfs.c b/arch/arm/src/stm32/stm32_usbfs.c
new file mode 100644
index 0000000000..22018c925c
--- /dev/null
+++ b/arch/arm/src/stm32/stm32_usbfs.c
@@ -0,0 +1,4010 @@
+/****************************************************************************
+ * arch/arm/src/stm32/stm32_usbdev.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* References:
+ *   - RM0008 Reference manual, STMicro document ID 13902
+ *   - STM32F10xxx USB development kit, UM0424, STMicro
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.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 "arm_internal.h"
+#include "stm32.h"
+#include "stm32_syscfg.h"
+#include "stm32_gpio.h"
+#include "stm32_usbdev.h"
+
+#if defined(CONFIG_USBDEV) && defined(CONFIG_STM32_USB)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_USBDEV_EP0_MAXSIZE
+#  define CONFIG_USBDEV_EP0_MAXSIZE 64
+#endif
+
+#ifndef CONFIG_USBDEV_SETUP_MAXDATASIZE
+#  define CONFIG_USBDEV_SETUP_MAXDATASIZE CONFIG_USBDEV_EP0_MAXSIZE
+#endif
+
+/* USB Interrupts.  Should be re-mapped if CAN is used. */
+
+#ifdef CONFIG_STM32_STM32F30XX
+#  ifdef CONFIG_STM32_USB_ITRMP
+#    define STM32_IRQ_USBHP   STM32_IRQ_USBHP_2
+#    define STM32_IRQ_USBLP   STM32_IRQ_USBLP_2
+#    define STM32_IRQ_USBWKUP STM32_IRQ_USBWKUP_2
+#  else
+#    define STM32_IRQ_USBHP   STM32_IRQ_USBHP_1
+#    define STM32_IRQ_USBLP   STM32_IRQ_USBLP_1
+#    define STM32_IRQ_USBWKUP STM32_IRQ_USBWKUP_1
+#  endif
+#endif
+
+/* Extremely detailed register debug that you would normally never want
+ * enabled.
+ */
+
+#ifndef CONFIG_DEBUG_USB_INFO
+#  undef CONFIG_STM32_USBDEV_REGDEBUG
+#endif
+
+/* Initial interrupt mask: Reset + Suspend + Correct Transfer */
+
+#define STM32_CNTR_SETUP     (USB_CNTR_RESETM|USB_CNTR_SUSPM|USB_CNTR_CTRM)
+
+/* Endpoint identifiers. The STM32 supports up to 16 mono-directional or 8
+ * bidirectional endpoints.  However, when you take into account PMA buffer
+ * usage (see below) and the fact that EP0 is bidirectional, then there is
+ * a functional limitation of EP0 + 5 mono-directional endpoints = 6.  We'll
+ * define STM32_NENDPOINTS to be 8, however, because that is how many
+ * endpoint register sets there are.
+ */
+
+#define STM32_NENDPOINTS      (8)
+#define EP0                   (0)
+#define EP1                   (1)
+#define EP2                   (2)
+#define EP3                   (3)
+#define EP4                   (4)
+#define EP5                   (5)
+#define EP6                   (6)
+#define EP7                   (7)
+
+#define STM32_ENDP_BIT(ep)    (1 << (ep))
+#define STM32_ENDP_ALLSET     0xff
+
+/* Packet sizes.  We us a fixed 64 max packet size for all endpoint types */
+
+#define STM32_MAXPACKET_SHIFT (6)
+#define STM32_MAXPACKET_SIZE  (1 << (STM32_MAXPACKET_SHIFT))
+#define STM32_MAXPACKET_MASK  (STM32_MAXPACKET_SIZE-1)
+
+#define STM32_EP0MAXPACKET    STM32_MAXPACKET_SIZE
+
+/* Buffer descriptor table.  We assume that USB has exclusive use of CAN/USB
+ * memory.  The buffer table is positioned at the beginning of the 512-byte
+ * CAN/USB memory.  We will use the first STM32_NENDPOINTS*4 words for the
+ * buffer table.
+ * That is exactly 64 bytes, leaving 7*64 bytes for endpoint buffers.
+ */
+
+#define STM32_BTABLE_ADDRESS  (0x00)   /* Start at the beginning of USB/CAN RAM */
+#define STM32_DESC_SIZE       (8)      /* Each descriptor is 4*2=8 bytes in size */
+#define STM32_BTABLE_SIZE     (STM32_NENDPOINTS*STM32_DESC_SIZE)
+
+/* Buffer layout.  Assume that all buffers are 64-bytes (maxpacketsize), then
+ * we have space for only 7 buffers; endpoint 0 will require two buffers,
+ * leaving 5 for other endpoints.
+ */
+
+#define STM32_BUFFER_START    STM32_BTABLE_SIZE
+#define STM32_EP0_RXADDR      STM32_BUFFER_START
+#define STM32_EP0_TXADDR      (STM32_EP0_RXADDR+STM32_EP0MAXPACKET)
+
+#define STM32_BUFFER_EP0      0x03
+#define STM32_NBUFFERS        7
+#define STM32_BUFFER_BIT(bn)  (1 << (bn))
+#define STM32_BUFFER_ALLSET   0x7f
+#define STM32_BUFNO2BUF(bn)   (STM32_BUFFER_START+((bn)<<STM32_MAXPACKET_SHIFT))
+
+/* USB-related masks */
+
+#define REQRECIPIENT_MASK     (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)
+
+/* Endpoint register masks (handling toggle fields) */
+
+#define EPR_NOTOG_MASK        (USB_EPR_CTR_RX  | USB_EPR_SETUP  | USB_EPR_EPTYPE_MASK |\
+                               USB_EPR_EP_KIND | USB_EPR_CTR_TX | USB_EPR_EA_MASK)
+#define EPR_TXDTOG_MASK       (USB_EPR_STATTX_MASK | EPR_NOTOG_MASK)
+#define EPR_RXDTOG_MASK       (USB_EPR_STATRX_MASK | EPR_NOTOG_MASK)
+
+/* Request queue operations *************************************************/
+
+#define stm32_rqempty(ep)     ((ep)->head == NULL)
+#define stm32_rqpeek(ep)      ((ep)->head)
+
+/* USB trace ****************************************************************/
+
+/* Trace error codes */
+
+#define STM32_TRACEERR_ALLOCFAIL            0x0001
+#define STM32_TRACEERR_BADCLEARFEATURE      0x0002
+#define STM32_TRACEERR_BADDEVGETSTATUS      0x0003
+#define STM32_TRACEERR_BADEPGETSTATUS       0x0004
+#define STM32_TRACEERR_BADEPNO              0x0005
+#define STM32_TRACEERR_BADEPTYPE            0x0006
+#define STM32_TRACEERR_BADGETCONFIG         0x0007
+#define STM32_TRACEERR_BADGETSETDESC        0x0008
+#define STM32_TRACEERR_BADGETSTATUS         0x0009
+#define STM32_TRACEERR_BADSETADDRESS        0x000a
+#define STM32_TRACEERR_BADSETCONFIG         0x000b
+#define STM32_TRACEERR_BADSETFEATURE        0x000c
+#define STM32_TRACEERR_BINDFAILED           0x000d
+#define STM32_TRACEERR_DISPATCHSTALL        0x000e
+#define STM32_TRACEERR_DRIVER               0x000f
+#define STM32_TRACEERR_DRIVERREGISTERED     0x0010
+#define STM32_TRACEERR_EP0BADCTR            0x0011
+#define STM32_TRACEERR_EP0SETUPSTALLED      0x0012
+#define STM32_TRACEERR_EPBUFFER             0x0013
+#define STM32_TRACEERR_EPDISABLED           0x0014
+#define STM32_TRACEERR_EPOUTNULLPACKET      0x0015
+#define STM32_TRACEERR_EPRESERVE            0x0016
+#define STM32_TRACEERR_INVALIDCTRLREQ       0x0017
+#define STM32_TRACEERR_INVALIDPARMS         0x0018
+#define STM32_TRACEERR_IRQREGISTRATION      0x0019
+#define STM32_TRACEERR_NOTCONFIGURED        0x001a
+#define STM32_TRACEERR_REQABORTED           0x001b
+
+/* Trace interrupt codes */
+
+#define STM32_TRACEINTID_CLEARFEATURE       0x0001
+#define STM32_TRACEINTID_DEVGETSTATUS       0x0002
+#define STM32_TRACEINTID_DISPATCH           0x0003
+#define STM32_TRACEINTID_EP0IN              0x0004
+#define STM32_TRACEINTID_EP0INDONE          0x0005
+#define STM32_TRACEINTID_EP0OUTDONE         0x0006
+#define STM32_TRACEINTID_EP0SETUPDONE       0x0007
+#define STM32_TRACEINTID_EP0SETUPSETADDRESS 0x0008
+#define STM32_TRACEINTID_EPGETSTATUS        0x0009
+#define STM32_TRACEINTID_EPINDONE           0x000a
+#define STM32_TRACEINTID_EPINQEMPTY         0x000b
+#define STM32_TRACEINTID_EPOUTDONE          0x000c
+#define STM32_TRACEINTID_EPOUTPENDING       0x000d
+#define STM32_TRACEINTID_EPOUTQEMPTY        0x000e
+#define STM32_TRACEINTID_ESOF               0x000f
+#define STM32_TRACEINTID_GETCONFIG          0x0010
+#define STM32_TRACEINTID_GETSETDESC         0x0011
+#define STM32_TRACEINTID_GETSETIF           0x0012
+#define STM32_TRACEINTID_GETSTATUS          0x0013
+#define STM32_TRACEINTID_HPINTERRUPT        0x0014
+#define STM32_TRACEINTID_IFGETSTATUS        0x0015
+#define STM32_TRACEINTID_LPCTR              0x0016
+#define STM32_TRACEINTID_LPINTERRUPT        0x0017
+#define STM32_TRACEINTID_NOSTDREQ           0x0018
+#define STM32_TRACEINTID_RESET              0x0019
+#define STM32_TRACEINTID_SETCONFIG          0x001a
+#define STM32_TRACEINTID_SETFEATURE         0x001b
+#define STM32_TRACEINTID_SUSP               0x001c
+#define STM32_TRACEINTID_SYNCHFRAME         0x001d
+#define STM32_TRACEINTID_WKUP               0x001e
+#define STM32_TRACEINTID_EP0SETUPOUT        0x001f
+#define STM32_TRACEINTID_EP0SETUPOUTDATA    0x0020
+
+/* Ever-present MIN and MAX macros */
+
+#ifndef MIN
+#  define MIN(a,b) (a < b ? a : b)
+#endif
+
+#ifndef MAX
+#  define MAX(a,b) (a > b ? a : b)
+#endif
+
+/* Byte ordering in host-based values */
+
+#ifdef CONFIG_ENDIAN_BIG
+#  define LSB 1
+#  define MSB 0
+#else
+#  define LSB 0
+#  define MSB 1
+#endif
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+/* The various states of a control pipe */
+
+enum stm32_ep0state_e
+{
+  EP0STATE_IDLE = 0,        /* No request in progress */
+  EP0STATE_SETUP_OUT,       /* Set up received with data for device OUT in progress */
+  EP0STATE_SETUP_READY,     /* Set up was received prior and is in ctrl,
+                             * now the data has arrived */
+  EP0STATE_WRREQUEST,       /* Write request in progress */
+  EP0STATE_RDREQUEST,       /* Read request in progress */
+  EP0STATE_STALLED          /* We are stalled */
+};
+
+/* Resume states */
+
+enum stm32_rsmstate_e
+{
+  RSMSTATE_IDLE = 0,        /* Device is either fully suspended or running */
+  RSMSTATE_STARTED,         /* Resume sequence has been started */
+  RSMSTATE_WAITING          /* Waiting (on ESOFs) for end of sequence */
+};
+
+union wb_u
+{
+  uint16_t w;
+  uint8_t  b[2];
+};
+
+/* A container for a request so that the request make be retained in a list */
+
+struct stm32_req_s
+{
+  struct usbdev_req_s    req;           /* Standard USB request */
+  struct stm32_req_s    *flink;         /* Supports a singly linked list */
+};
+
+/* This is the internal representation of an endpoint */
+
+struct stm32_ep_s
+{
+  /* Common endpoint fields.  This must be the first thing defined in the
+   * structure so that it is possible to simply cast from struct usbdev_ep_s
+   * to struct stm32_ep_s.
+   */
+
+  struct usbdev_ep_s     ep;            /* Standard endpoint structure */
+
+  /* STR71X-specific fields */
+
+  struct stm32_usbdev_s *dev;           /* Reference to private driver data */
+  struct stm32_req_s    *head;          /* Request list for this endpoint */
+  struct stm32_req_s    *tail;
+  uint8_t                bufno;         /* Allocated buffer number */
+  uint8_t                stalled:1;     /* true: Endpoint is stalled */
+  uint8_t                halted:1;      /* true: Endpoint feature halted */
+  uint8_t                txbusy:1;      /* true: TX endpoint FIFO full */
+  uint8_t                txnullpkt:1;   /* Null packet needed at end of transfer */
+};
+
+struct stm32_usbdev_s
+{
+  /* Common device fields.  This must be the first thing defined in the
+   * structure so that it is possible to simply cast from struct usbdev_s
+   * to structstm32_usbdev_s.
+   */
+
+  struct usbdev_s usbdev;
+
+  /* The bound device class driver */
+
+  struct usbdevclass_driver_s *driver;
+
+  /* STM32-specific fields */
+
+  uint8_t                ep0state;      /* State of EP0 (see enum stm32_ep0state_e) */
+  uint8_t                rsmstate;      /* Resume state (see enum stm32_rsmstate_e) */
+  uint8_t                nesofs;        /* ESOF counter (for resume support) */
+  uint8_t                rxpending:1;   /* 1: OUT data in PMA, but no read requests */
+  uint8_t                selfpowered:1; /* 1: Device is self powered */
+  uint8_t                epavail;       /* Bitset of available endpoints */
+  uint8_t                bufavail;      /* Bitset of available buffers */
+  uint16_t               rxstatus;      /* Saved during interrupt processing */
+  uint16_t               txstatus;      /* "   " "    " "       " "        " */
+  uint16_t               imask;         /* Current interrupt mask */
+
+  /* E0 SETUP data buffering.
+   *
+   * ctrl
+   *   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 ep0 packet receipt
+   *   logic stm32_ep0_rdrequest will save the accompanying EP0 OUT data
+   *   in ep0data[] before the SETUP command is re-processed.
+   *
+   * ep0datlen
+   *   Length of OUT DATA received in ep0data[]
+   */
+
+  struct usb_ctrlreq_s   ctrl;          /* Last EP0 request */
+
+  uint8_t                ep0data[CONFIG_USBDEV_SETUP_MAXDATASIZE];
+  uint16_t               ep0datlen;
+
+  /* The endpoint list */
+
+  struct stm32_ep_s      eplist[STM32_NENDPOINTS];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+#ifdef CONFIG_STM32_USBDEV_REGDEBUG
+static uint16_t stm32_getreg(uint32_t addr);
+static void stm32_putreg(uint16_t val, uint32_t addr);
+static void stm32_checksetup(void);
+static void stm32_dumpep(int epno);
+#else
+# define stm32_getreg(addr)      getreg16(addr)
+# define stm32_putreg(val,addr)  putreg16(val,addr)
+# define stm32_checksetup()
+# define stm32_dumpep(epno)
+#endif
+
+/* Low-Level Helpers ********************************************************/
+
+static inline void
+              stm32_seteptxcount(uint8_t epno, uint16_t count);
+static inline void
+              stm32_seteptxaddr(uint8_t epno, uint16_t addr);
+static inline uint16_t
+              stm32_geteptxaddr(uint8_t epno);
+static void   stm32_seteprxcount(uint8_t epno, uint16_t count);
+static inline uint16_t
+              stm32_geteprxcount(uint8_t epno);
+static inline void
+              stm32_seteprxaddr(uint8_t epno, uint16_t addr);
+static inline uint16_t
+              stm32_geteprxaddr(uint8_t epno);
+static inline void
+              stm32_setepaddress(uint8_t epno, uint16_t addr);
+static inline void
+              stm32_seteptype(uint8_t epno, uint16_t type);
+static inline void
+              stm32_seteptxaddr(uint8_t epno, uint16_t addr);
+static inline void
+              stm32_setstatusout(uint8_t epno);
+static inline void
+              stm32_clrstatusout(uint8_t epno);
+static void   stm32_clrrxdtog(uint8_t epno);
+static void   stm32_clrtxdtog(uint8_t epno);
+static void   stm32_clrepctrrx(uint8_t epno);
+static void   stm32_clrepctrtx(uint8_t epno);
+static void   stm32_seteptxstatus(uint8_t epno, uint16_t state);
+static void   stm32_seteprxstatus(uint8_t epno, uint16_t state);
+static inline uint16_t
+              stm32_geteptxstatus(uint8_t epno);
+static inline uint16_t
+              stm32_geteprxstatus(uint8_t epno);
+static bool   stm32_eptxstalled(uint8_t epno);
+static bool   stm32_eprxstalled(uint8_t epno);
+static void   stm32_setimask(struct stm32_usbdev_s *priv,
+                             uint16_t setbits,
+                             uint16_t clrbits);
+
+/* Suspend/Resume Helpers ***************************************************/
+
+static void   stm32_suspend(struct stm32_usbdev_s *priv);
+static void   stm32_initresume(struct stm32_usbdev_s *priv);
+static void   stm32_esofpoll(struct stm32_usbdev_s *priv) ;
+
+/* Request Helpers **********************************************************/
+
+static void   stm32_copytopma(const uint8_t *buffer, uint16_t pma,
+                              uint16_t nbytes);
+static inline void stm32_copyfrompma(uint8_t *buffer,
+                                     uint16_t pma, uint16_t nbytes);
+static struct stm32_req_s *
+              stm32_rqdequeue(struct stm32_ep_s *privep);
+static void   stm32_rqenqueue(struct stm32_ep_s *privep,
+                struct stm32_req_s *req);
+static inline void
+              stm32_abortrequest(struct stm32_ep_s *privep,
+                struct stm32_req_s *privreq, int16_t result);
+static void   stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result);
+static void   stm32_epwrite(struct stm32_usbdev_s *buf,
+                            struct stm32_ep_s *privep,
+                            const uint8_t *data, uint32_t nbytes);
+static int    stm32_wrrequest(struct stm32_usbdev_s *priv,
+                struct stm32_ep_s *privep);
+inline static int
+              stm32_wrrequest_ep0(struct stm32_usbdev_s *priv,
+                struct stm32_ep_s *privep);
+static inline int
+              stm32_ep0_rdrequest(struct stm32_usbdev_s *priv);
+static int    stm32_rdrequest(struct stm32_usbdev_s *priv,
+                struct stm32_ep_s *privep);
+static void   stm32_cancelrequests(struct stm32_ep_s *privep);
+
+/* Interrupt level processing ***********************************************/
+
+static void   stm32_dispatchrequest(struct stm32_usbdev_s *priv);
+static void   stm32_epdone(struct stm32_usbdev_s *priv, uint8_t epno);
+static void   stm32_setdevaddr(struct stm32_usbdev_s *priv, uint8_t value);
+static void   stm32_ep0setup(struct stm32_usbdev_s *priv);
+static void   stm32_ep0out(struct stm32_usbdev_s *priv);
+static void   stm32_ep0in(struct stm32_usbdev_s *priv);
+static inline void
+              stm32_ep0done(struct stm32_usbdev_s *priv, uint16_t istr);
+static void   stm32_lptransfer(struct stm32_usbdev_s *priv);
+static int    stm32_hpinterrupt(int irq, void *context, void *arg);
+static int    stm32_lpinterrupt(int irq, void *context, void *arg);
+
+/* Endpoint helpers *********************************************************/
+
+static inline struct stm32_ep_s *
+              stm32_epreserve(struct stm32_usbdev_s *priv, uint8_t epset);
+static inline void
+              stm32_epunreserve(struct stm32_usbdev_s *priv,
+                struct stm32_ep_s *privep);
+static inline bool
+              stm32_epreserved(struct stm32_usbdev_s *priv, int epno);
+static int    stm32_epallocpma(struct stm32_usbdev_s *priv);
+static inline void
+              stm32_epfreepma(struct stm32_usbdev_s *priv,
+                struct stm32_ep_s *privep);
+
+/* Endpoint operations ******************************************************/
+
+static int    stm32_epconfigure(struct usbdev_ep_s *ep,
+                const struct usb_epdesc_s *desc, bool last);
+static int    stm32_epdisable(struct usbdev_ep_s *ep);
+static struct usbdev_req_s *
+              stm32_epallocreq(struct usbdev_ep_s *ep);
+static void   stm32_epfreereq(struct usbdev_ep_s *ep,
+                struct usbdev_req_s *);
+static int    stm32_epsubmit(struct usbdev_ep_s *ep,
+                struct usbdev_req_s *req);
+static int    stm32_epcancel(struct usbdev_ep_s *ep,
+                struct usbdev_req_s *req);
+static int    stm32_epstall(struct usbdev_ep_s *ep, bool resume);
+
+/* USB device controller operations *****************************************/
+
+static struct usbdev_ep_s *
+              stm32_allocep(struct usbdev_s *dev, uint8_t epno, bool in,
+                uint8_t eptype);
+static void   stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep);
+static int    stm32_getframe(struct usbdev_s *dev);
+static int    stm32_wakeup(struct usbdev_s *dev);
+static int    stm32_selfpowered(struct usbdev_s *dev, bool selfpowered);
+
+/* Initialization/Reset *****************************************************/
+
+static void   stm32_reset(struct stm32_usbdev_s *priv);
+static void   stm32_hwreset(struct stm32_usbdev_s *priv);
+static void   stm32_hwsetup(struct stm32_usbdev_s *priv);
+static void   stm32_hwshutdown(struct stm32_usbdev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Since there is only a single USB interface, all status information can be
+ * be simply retained in a single global instance.
+ */
+
+static struct stm32_usbdev_s g_usbdev;
+
+static const struct usbdev_epops_s g_epops =
+{
+  .configure   = stm32_epconfigure,
+  .disable     = stm32_epdisable,
+  .allocreq    = stm32_epallocreq,
+  .freereq     = stm32_epfreereq,
+  .submit      = stm32_epsubmit,
+  .cancel      = stm32_epcancel,
+  .stall       = stm32_epstall,
+};
+
+static const struct usbdev_ops_s g_devops =
+{
+  .allocep     = stm32_allocep,
+  .freeep      = stm32_freeep,
+  .getframe    = stm32_getframe,
+  .wakeup      = stm32_wakeup,
+  .selfpowered = stm32_selfpowered,
+  .pullup      = stm32_usbpullup,
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_TRACE_STRINGS
+const struct trace_msg_t g_usb_trace_strings_intdecode[] =
+{
+  TRACE_STR(STM32_TRACEINTID_CLEARFEATURE),
+  TRACE_STR(STM32_TRACEINTID_DEVGETSTATUS),
+  TRACE_STR(STM32_TRACEINTID_DISPATCH),
+  TRACE_STR(STM32_TRACEINTID_EP0IN),
+  TRACE_STR(STM32_TRACEINTID_EP0INDONE),
+  TRACE_STR(STM32_TRACEINTID_EP0OUTDONE),
+  TRACE_STR(STM32_TRACEINTID_EP0SETUPDONE),
+  TRACE_STR(STM32_TRACEINTID_EP0SETUPSETADDRESS),
+  TRACE_STR(STM32_TRACEINTID_EPGETSTATUS),
+  TRACE_STR(STM32_TRACEINTID_EPINDONE),
+  TRACE_STR(STM32_TRACEINTID_EPINQEMPTY),
+  TRACE_STR(STM32_TRACEINTID_EPOUTDONE),
+  TRACE_STR(STM32_TRACEINTID_EPOUTPENDING),
+  TRACE_STR(STM32_TRACEINTID_EPOUTQEMPTY),
+  TRACE_STR(STM32_TRACEINTID_ESOF),
+  TRACE_STR(STM32_TRACEINTID_GETCONFIG),
+  TRACE_STR(STM32_TRACEINTID_GETSETDESC),
+  TRACE_STR(STM32_TRACEINTID_GETSETIF),
+  TRACE_STR(STM32_TRACEINTID_GETSTATUS),
+  TRACE_STR(STM32_TRACEINTID_HPINTERRUPT),
+  TRACE_STR(STM32_TRACEINTID_IFGETSTATUS),
+  TRACE_STR(STM32_TRACEINTID_LPCTR),
+  TRACE_STR(STM32_TRACEINTID_LPINTERRUPT),
+  TRACE_STR(STM32_TRACEINTID_NOSTDREQ),
+  TRACE_STR(STM32_TRACEINTID_RESET),
+  TRACE_STR(STM32_TRACEINTID_SETCONFIG),
+  TRACE_STR(STM32_TRACEINTID_SETFEATURE),
+  TRACE_STR(STM32_TRACEINTID_SUSP),
+  TRACE_STR(STM32_TRACEINTID_SYNCHFRAME),
+  TRACE_STR(STM32_TRACEINTID_WKUP),
+  TRACE_STR(STM32_TRACEINTID_EP0SETUPOUT),
+  TRACE_STR(STM32_TRACEINTID_EP0SETUPOUTDATA),
+  TRACE_STR_END
+};
+#endif
+
+#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_BADEPGETSTATUS),
+  TRACE_STR(STM32_TRACEERR_BADEPNO),
+  TRACE_STR(STM32_TRACEERR_BADEPTYPE),
+  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_BINDFAILED),
+  TRACE_STR(STM32_TRACEERR_DISPATCHSTALL),
+  TRACE_STR(STM32_TRACEERR_DRIVER),
+  TRACE_STR(STM32_TRACEERR_DRIVERREGISTERED),
+  TRACE_STR(STM32_TRACEERR_EP0BADCTR),
+  TRACE_STR(STM32_TRACEERR_EP0SETUPSTALLED),
+  TRACE_STR(STM32_TRACEERR_EPBUFFER),
+  TRACE_STR(STM32_TRACEERR_EPDISABLED),
+  TRACE_STR(STM32_TRACEERR_EPOUTNULLPACKET),
+  TRACE_STR(STM32_TRACEERR_EPRESERVE),
+  TRACE_STR(STM32_TRACEERR_INVALIDCTRLREQ),
+  TRACE_STR(STM32_TRACEERR_INVALIDPARMS),
+  TRACE_STR(STM32_TRACEERR_IRQREGISTRATION),
+  TRACE_STR(STM32_TRACEERR_NOTCONFIGURED),
+  TRACE_STR(STM32_TRACEERR_REQABORTED),
+  TRACE_STR_END
+};
+#endif
+
+/****************************************************************************
+ * Private Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Register Operations
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_getreg
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_USBDEV_REGDEBUG
+static uint16_t stm32_getreg(uint32_t addr)
+{
+  static uint32_t prevaddr = 0;
+  static uint16_t preval = 0;
+  static uint32_t count = 0;
+
+  /* Read the value from the register */
+
+  uint16_t val = getreg16(addr);
+
+  /* Is this the same value that we read from the same register last time?
+   * Are we polling the register?  If so, suppress some of the output.
+   */
+
+  if (addr == prevaddr && val == preval)
+    {
+      if (count == 0xffffffff || ++count > 3)
+        {
+          if (count == 4)
+            {
+              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->%04x\n", addr, val);
+  return val;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32_putreg
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_USBDEV_REGDEBUG
+static void stm32_putreg(uint16_t val, uint32_t addr)
+{
+  /* Show the register value being written */
+
+  uinfo("%08x<-%04x\n", addr, val);
+
+  /* Write the value */
+
+  putreg16(val, addr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32_dumpep
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_USBDEV_REGDEBUG
+static void stm32_dumpep(int epno)
+{
+  uint32_t addr;
+
+  /* Common registers */
+
+  uinfo("CNTR:   %04x\n", getreg16(STM32_USB_CNTR));
+  uinfo("ISTR:   %04x\n", getreg16(STM32_USB_ISTR));
+  uinfo("FNR:    %04x\n", getreg16(STM32_USB_FNR));
+  uinfo("DADDR:  %04x\n", getreg16(STM32_USB_DADDR));
+  uinfo("BTABLE: %04x\n", getreg16(STM32_USB_BTABLE));
+
+  /* Endpoint register */
+
+  addr = STM32_USB_EPR(epno);
+  uinfo("EPR%d:   [%08x] %04x\n", epno, addr, getreg16(addr));
+
+  /* Endpoint descriptor */
+
+  addr = STM32_USB_BTABLE_ADDR(epno, 0);
+  uinfo("DESC:   %08x\n", addr);
+
+  /* Endpoint buffer descriptor */
+
+  addr = STM32_USB_ADDR_TX(epno);
+  uinfo("  TX ADDR:  [%08x] %04x\n",  addr, getreg16(addr));
+
+  addr = STM32_USB_COUNT_TX(epno);
+  uinfo("     COUNT: [%08x] %04x\n",  addr, getreg16(addr));
+
+  addr = STM32_USB_ADDR_RX(epno);
+  uinfo("  RX ADDR:  [%08x] %04x\n",  addr, getreg16(addr));
+
+  addr = STM32_USB_COUNT_RX(epno);
+  uinfo("     COUNT: [%08x] %04x\n",  addr, getreg16(addr));
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32_checksetup
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_USBDEV_REGDEBUG
+static void stm32_checksetup(void)
+{
+  uint32_t cfgr     = getreg32(STM32_RCC_CFGR);
+  uint32_t apb1rstr = getreg32(STM32_RCC_APB1RSTR);
+  uint32_t apb1enr  = getreg32(STM32_RCC_APB1ENR);
+
+  uinfo("CFGR: %08x APB1RSTR: %08x APB1ENR: %08x\n",
+         cfgr, apb1rstr, apb1enr);
+
+  if ((apb1rstr & RCC_APB1RSTR_USBRST) != 0 ||
+      (apb1enr & RCC_APB1ENR_USBEN) == 0)
+    {
+      uerr("ERROR: USB is NOT setup correctly\n");
+    }
+}
+#endif
+
+/****************************************************************************
+ * Low-Level Helpers
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_seteptxcount
+ ****************************************************************************/
+
+static inline void stm32_seteptxcount(uint8_t epno, uint16_t count)
+{
+  volatile uint32_t *epaddr = (uint32_t *)STM32_USB_COUNT_TX(epno);
+  *epaddr = count;
+}
+
+/****************************************************************************
+ * Name: stm32_seteptxaddr
+ ****************************************************************************/
+
+static inline void stm32_seteptxaddr(uint8_t epno, uint16_t addr)
+{
+  volatile uint32_t *txaddr = (uint32_t *)STM32_USB_ADDR_TX(epno);
+  *txaddr = addr;
+}
+
+/****************************************************************************
+ * Name: stm32_geteptxaddr
+ ****************************************************************************/
+
+static inline uint16_t stm32_geteptxaddr(uint8_t epno)
+{
+  volatile uint32_t *txaddr = (uint32_t *)STM32_USB_ADDR_TX(epno);
+  return (uint16_t)*txaddr;
+}
+
+/****************************************************************************
+ * Name: stm32_seteprxcount
+ ****************************************************************************/
+
+static void stm32_seteprxcount(uint8_t epno, uint16_t count)
+{
+  volatile uint32_t *epaddr = (uint32_t *)STM32_USB_COUNT_RX(epno);
+  uint32_t rxcount = 0;
+  uint16_t nblocks;
+
+  /* The upper bits of the RX COUNT value contain the size of allocated
+   * RX buffer.  This is based on a block size of 2 or 32:
+   *
+   * USB_COUNT_RX_BL_SIZE not set:
+   *   nblocks is in units of 2 bytes.
+   *     00000 - not allowed
+   *     00001 - 2 bytes
+   *     ....
+   *     11111 - 62 bytes
+   *
+   * USB_COUNT_RX_BL_SIZE set:
+   *     00000 - 32 bytes
+   *     00001 - 64 bytes
+   *     ...
+   *     01111 - 512 bytes
+   *     1xxxx - Not allowed
+   */
+
+  if (count > 62)
+    {
+      /* Blocks of 32 (with 0 meaning one block of 32) */
+
+      nblocks = (count >> 5) - 1 ;
+      DEBUGASSERT(nblocks <= 0x0f);
+      rxcount = (uint32_t)((nblocks <<
+                 USB_COUNT_RX_NUM_BLOCK_SHIFT) | USB_COUNT_RX_BL_SIZE);
+    }
+  else if (count > 0)
+    {
+      /* Blocks of 2 (with 1 meaning one block of 2) */
+
+      nblocks = (count + 1) >> 1;
+      DEBUGASSERT(nblocks > 0 && nblocks < 0x1f);
+      rxcount = (uint32_t)(nblocks << USB_COUNT_RX_NUM_BLOCK_SHIFT);
+    }
+
+  *epaddr = rxcount;
+}
+
+/****************************************************************************
+ * Name: stm32_geteprxcount
+ ****************************************************************************/
+
+static inline uint16_t stm32_geteprxcount(uint8_t epno)
+{
+  volatile uint32_t *epaddr = (uint32_t *)STM32_USB_COUNT_RX(epno);
+  return (*epaddr) & USB_COUNT_RX_MASK;
+}
+
+/****************************************************************************
+ * Name: stm32_seteprxaddr
+ ****************************************************************************/
+
+static inline void stm32_seteprxaddr(uint8_t epno, uint16_t addr)
+{
+  volatile uint32_t *rxaddr = (uint32_t *)STM32_USB_ADDR_RX(epno);
+  *rxaddr = addr;
+}
+
+/****************************************************************************
+ * Name: stm32_seteprxaddr
+ ****************************************************************************/
+
+static inline uint16_t stm32_geteprxaddr(uint8_t epno)
+{
+  volatile uint32_t *rxaddr = (uint32_t *)STM32_USB_ADDR_RX(epno);
+  return (uint16_t)*rxaddr;
+}
+
+/****************************************************************************
+ * Name: stm32_setepaddress
+ ****************************************************************************/
+
+static inline void stm32_setepaddress(uint8_t epno, uint16_t addr)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  regval  = stm32_getreg(epaddr);
+  regval &= EPR_NOTOG_MASK;
+  regval &= ~USB_EPR_EA_MASK;
+  regval |= (addr << USB_EPR_EA_SHIFT);
+  stm32_putreg(regval, epaddr);
+}
+
+/****************************************************************************
+ * Name: stm32_seteptype
+ ****************************************************************************/
+
+static inline void stm32_seteptype(uint8_t epno, uint16_t type)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  regval  = stm32_getreg(epaddr);
+  regval &= EPR_NOTOG_MASK;
+  regval &= ~USB_EPR_EPTYPE_MASK;
+  regval |= type;
+  stm32_putreg(regval, epaddr);
+}
+
+/****************************************************************************
+ * Name: stm32_setstatusout
+ ****************************************************************************/
+
+static inline void stm32_setstatusout(uint8_t epno)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  /* For a BULK endpoint the EP_KIND bit is used to enabled double buffering;
+   * for a CONTROL endpoint, it is set to indicate that a status OUT
+   * transaction is expected.  The bit is not used with out endpoint types.
+   */
+
+  regval  = stm32_getreg(epaddr);
+  regval &= EPR_NOTOG_MASK;
+  regval |= USB_EPR_EP_KIND;
+  stm32_putreg(regval, epaddr);
+}
+
+/****************************************************************************
+ * Name: stm32_clrstatusout
+ ****************************************************************************/
+
+static inline void stm32_clrstatusout(uint8_t epno)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  /* For a BULK endpoint the EP_KIND bit is used to enabled double buffering;
+   * for a CONTROL endpoint, it is set to indicate that a status OUT
+   * transaction is expected.  The bit is not used with out endpoint types.
+   */
+
+  regval  = stm32_getreg(epaddr);
+  regval &= EPR_NOTOG_MASK;
+  regval &= ~USB_EPR_EP_KIND;
+  stm32_putreg(regval, epaddr);
+}
+
+/****************************************************************************
+ * Name: stm32_clrrxdtog
+ ****************************************************************************/
+
+static void stm32_clrrxdtog(uint8_t epno)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  regval = stm32_getreg(epaddr);
+  if ((regval & USB_EPR_DTOG_RX) != 0)
+    {
+      regval &= EPR_NOTOG_MASK;
+      regval |= USB_EPR_DTOG_RX;
+      stm32_putreg(regval, epaddr);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_clrtxdtog
+ ****************************************************************************/
+
+static void stm32_clrtxdtog(uint8_t epno)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  regval = stm32_getreg(epaddr);
+  if ((regval & USB_EPR_DTOG_TX) != 0)
+    {
+      regval &= EPR_NOTOG_MASK;
+      regval |= USB_EPR_DTOG_TX;
+      stm32_putreg(regval, epaddr);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_clrepctrrx
+ ****************************************************************************/
+
+static void stm32_clrepctrrx(uint8_t epno)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  regval  = stm32_getreg(epaddr);
+  regval &= EPR_NOTOG_MASK;
+  regval &= ~USB_EPR_CTR_RX;
+  stm32_putreg(regval, epaddr);
+}
+
+/****************************************************************************
+ * Name: stm32_clrepctrtx
+ ****************************************************************************/
+
+static void stm32_clrepctrtx(uint8_t epno)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  regval  = stm32_getreg(epaddr);
+  regval &= EPR_NOTOG_MASK;
+  regval &= ~USB_EPR_CTR_TX;
+  stm32_putreg(regval, epaddr);
+}
+
+/****************************************************************************
+ * Name: stm32_geteptxstatus
+ ****************************************************************************/
+
+static inline uint16_t stm32_geteptxstatus(uint8_t epno)
+{
+  return (uint16_t)(stm32_getreg(STM32_USB_EPR(epno)) &
+                    USB_EPR_STATTX_MASK);
+}
+
+/****************************************************************************
+ * Name: stm32_geteprxstatus
+ ****************************************************************************/
+
+static inline uint16_t stm32_geteprxstatus(uint8_t epno)
+{
+  return (stm32_getreg(STM32_USB_EPR(epno)) & USB_EPR_STATRX_MASK);
+}
+
+/****************************************************************************
+ * Name: stm32_seteptxstatus
+ ****************************************************************************/
+
+static void stm32_seteptxstatus(uint8_t epno, uint16_t state)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  /* The bits in the STAT_TX field can be toggled by software to set their
+   * value. When set to 0, the value remains unchanged; when set to one,
+   * value toggles.
+   */
+
+  regval = stm32_getreg(epaddr);
+
+  /* The exclusive OR will set STAT_TX bits to 1 if there value is different
+   * from the bits requested in 'state'
+   */
+
+  regval ^= state;
+  regval &= EPR_TXDTOG_MASK;
+  stm32_putreg(regval, epaddr);
+}
+
+/****************************************************************************
+ * Name: stm32_seteprxstatus
+ ****************************************************************************/
+
+static void stm32_seteprxstatus(uint8_t epno, uint16_t state)
+{
+  uint32_t epaddr = STM32_USB_EPR(epno);
+  uint16_t regval;
+
+  /* The bits in the STAT_RX field can be toggled by software to set their
+   * value. When set to 0, the value remains unchanged; when set to one,
+   * value toggles.
+   */
+
+  regval = stm32_getreg(epaddr);
+
+  /* The exclusive OR will set STAT_RX bits to 1 if there value is different
+   * from the bits requested in 'state'
+   */
+
+  regval ^= state;
+  regval &= EPR_RXDTOG_MASK;
+  stm32_putreg(regval, epaddr);
+}
+
+/****************************************************************************
+ * Name: stm32_eptxstalled
+ ****************************************************************************/
+
+static inline bool stm32_eptxstalled(uint8_t epno)
+{
+  return (stm32_geteptxstatus(epno) == USB_EPR_STATTX_STALL);
+}
+
+/****************************************************************************
+ * Name: stm32_eprxstalled
+ ****************************************************************************/
+
+static inline bool stm32_eprxstalled(uint8_t epno)
+{
+  return (stm32_geteprxstatus(epno) == USB_EPR_STATRX_STALL);
+}
+
+/****************************************************************************
+ * Request Helpers
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_copytopma
+ ****************************************************************************/
+
+static void stm32_copytopma(const uint8_t *buffer,
+                            uint16_t pma, uint16_t nbytes)
+{
+  uint16_t *dest;
+  uint16_t  ms;
+  uint16_t  ls;
+  int     nwords = (nbytes + 1) >> 1;
+  int     i;
+
+  /* Copy loop.  Source=user buffer, Dest=packet memory */
+
+  dest = (uint16_t *)(STM32_USBRAM_BASE + ((uint32_t)pma << 1));
+  for (i = nwords; i != 0; i--)
+    {
+      /* Read two bytes and pack into on 16-bit word */
+
+      ls = (uint16_t)(*buffer++);
+      ms = (uint16_t)(*buffer++);
+      *dest = ms << 8 | ls;
+
+      /* Source address increments by 2*sizeof(uint8_t) = 2; Dest address
+       * increments by 2*sizeof(uint16_t) = 4.
+       */
+
+      dest += 2;
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_copyfrompma
+ ****************************************************************************/
+
+static inline void
+stm32_copyfrompma(uint8_t *buffer, uint16_t pma, uint16_t nbytes)
+{
+  uint32_t *src;
+  int     nwords = (nbytes + 1) >> 1;
+  int     i;
+
+  /* Copy loop.  Source=packet memory, Dest=user buffer */
+
+  src = (uint32_t *)(STM32_USBRAM_BASE + ((uint32_t)pma << 1));
+  for (i = nwords; i != 0; i--)
+    {
+      /* Copy 16-bits from packet memory to user buffer. */
+
+      *(uint16_t *)buffer = *src++;
+
+      /* Source address increments by 1*sizeof(uint32_t) = 4; Dest address
+       * increments by 2*sizeof(uint8_t) = 2.
+       */
+
+      buffer += 2;
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_rqdequeue
+ ****************************************************************************/
+
+static struct stm32_req_s *stm32_rqdequeue(struct stm32_ep_s *privep)
+{
+  struct stm32_req_s *ret = privep->head;
+
+  if (ret)
+    {
+      privep->head = ret->flink;
+      if (!privep->head)
+        {
+          privep->tail = NULL;
+        }
+
+      ret->flink = NULL;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32_rqenqueue
+ ****************************************************************************/
+
+static void stm32_rqenqueue(struct stm32_ep_s *privep,
+                            struct stm32_req_s *req)
+{
+  req->flink = NULL;
+  if (!privep->head)
+    {
+      privep->head = req;
+      privep->tail = req;
+    }
+  else
+    {
+      privep->tail->flink = req;
+      privep->tail        = req;
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_abortrequest
+ ****************************************************************************/
+
+static inline void
+stm32_abortrequest(struct stm32_ep_s *privep,
+                   struct stm32_req_s *privreq, int16_t result)
+{
+  usbtrace(TRACE_DEVERROR(STM32_TRACEERR_REQABORTED),
+          (uint16_t)USB_EPNO(privep->ep.eplog));
+
+  /* Save the result in the request structure */
+
+  privreq->req.result = result;
+
+  /* Callback to the request completion handler */
+
+  privreq->req.callback(&privep->ep, &privreq->req);
+}
+
+/****************************************************************************
+ * Name: stm32_reqcomplete
+ ****************************************************************************/
+
+static void stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result)
+{
+  struct stm32_req_s *privreq;
+  irqstate_t flags;
+
+  /* Remove the completed request at the head of the endpoint request list */
+
+  flags = enter_critical_section();
+  privreq = stm32_rqdequeue(privep);
+  leave_critical_section(flags);
+
+  if (privreq)
+    {
+      /* If endpoint 0, temporarily reflect the state of protocol stalled
+       * in the callback.
+       */
+
+      bool stalled = privep->stalled;
+      if (USB_EPNO(privep->ep.eplog) == EP0)
+        {
+          privep->stalled = (privep->dev->ep0state == EP0STATE_STALLED);
+        }
+
+      /* Save the result in the request structure */
+
+      privreq->req.result = result;
+
+      /* Callback to the request completion handler */
+
+      privreq->flink = NULL;
+      privreq->req.callback(&privep->ep, &privreq->req);
+
+      /* Restore the stalled indication */
+
+      privep->stalled = stalled;
+    }
+}
+
+/****************************************************************************
+ * Name: tm32_epwrite
+ ****************************************************************************/
+
+static void stm32_epwrite(struct stm32_usbdev_s *priv,
+                          struct stm32_ep_s *privep,
+                          const uint8_t *buf, uint32_t nbytes)
+{
+  uint8_t epno = USB_EPNO(privep->ep.eplog);
+  usbtrace(TRACE_WRITE(epno), nbytes);
+
+  /* Check for a zero-length packet */
+
+  if (nbytes > 0)
+    {
+      /* Copy the data from the user buffer into packet memory for this
+       * endpoint
+       */
+
+      stm32_copytopma(buf, stm32_geteptxaddr(epno), nbytes);
+    }
+
+  /* Send the packet (might be a null packet nbytes == 0) */
+
+  stm32_seteptxcount(epno, nbytes);
+  priv->txstatus = USB_EPR_STATTX_VALID;
+
+  /* Indicate that there is data in the TX packet memory.  This will be
+   * cleared when the next data out interrupt is received.
+   */
+
+  privep->txbusy = true;
+}
+
+/****************************************************************************
+ * Name: stm32_wrrequest_ep0
+ *
+ * Description:
+ *   Handle the ep0 state on writes.
+ *
+ ****************************************************************************/
+
+inline static int stm32_wrrequest_ep0(struct stm32_usbdev_s *priv,
+                                      struct stm32_ep_s *privep)
+{
+  int ret;
+  ret = stm32_wrrequest(priv, privep);
+  priv->ep0state = ((ret == OK) ? EP0STATE_WRREQUEST : EP0STATE_IDLE);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32_wrrequest
+ ****************************************************************************/
+
+static int stm32_wrrequest(struct stm32_usbdev_s *priv,
+                           struct stm32_ep_s *privep)
+{
+  struct stm32_req_s *privreq;
+  uint8_t *buf;
+  uint8_t epno;
+  int nbytes;
+  int bytesleft;
+
+  /* We get here when an IN endpoint interrupt occurs.  So now we know that
+   * there is no TX transfer in progress.
+   */
+
+  privep->txbusy = false;
+
+  /* Check the request from the head of the endpoint request queue */
+
+  privreq = stm32_rqpeek(privep);
+  if (!privreq)
+    {
+      /* There is no TX transfer in progress and no new pending TX
+       * requests to send.
+       */
+
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINQEMPTY), 0);
+      return -ENOENT;
+    }
+
+  epno = USB_EPNO(privep->ep.eplog);
+  uinfo("epno=%d req=%p: len=%d xfrd=%d nullpkt=%d\n",
+        epno, privreq, privreq->req.len,
+        privreq->req.xfrd, privep->txnullpkt);
+  UNUSED(epno);
+
+  /* Get the number of bytes left to be sent in the packet */
+
+  bytesleft         = privreq->req.len - privreq->req.xfrd;
+  nbytes            = bytesleft;
+
+#warning "REVISIT: If the EP supports double buffering, then we can do better"
+
+  /* Either (1) we are committed to sending the null packet
+   * (because txnullpkt == 1 && nbytes == 0), or (2) we have not yet send
+   * the last packet (nbytes > 0).
+   * In either case, it is appropriate to clearn txnullpkt now.
+   */
+
+  privep->txnullpkt = 0;
+
+  /* If we are not sending a NULL packet, then clip the size to maxpacket
+   * and check if we need to send a following NULL packet.
+   */
+
+  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)
+            {
+              privep->txnullpkt = 1;
+            }
+        }
+    }
+
+  /* Send the packet (might be a null packet nbytes == 0) */
+
+  buf = privreq->req.buf + privreq->req.xfrd;
+  stm32_epwrite(priv, privep, buf, nbytes);
+
+  /* Update for the next data IN interrupt */
+
+  privreq->req.xfrd += nbytes;
+  bytesleft          = privreq->req.len - privreq->req.xfrd;
+
+  /* If all of the bytes were sent (including any final null packet)
+   * then we are finished with the request buffer).
+   */
+
+  if (bytesleft == 0 && !privep->txnullpkt)
+    {
+      /* Return the write request to the class driver */
+
+      usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
+               privreq->req.xfrd);
+      privep->txnullpkt = 0;
+      stm32_reqcomplete(privep, OK);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_ep0_rdrequest
+ *
+ * Description:
+ *   This function is called from the stm32_ep0out handler when the ep0state
+ *   is EP0STATE_SETUP_OUT and upon new incoming data is available in the
+ *   endpoint 0's buffer.
+ *   This function will simply copy the OUT data into ep0data.
+ *
+ ****************************************************************************/
+
+static inline int stm32_ep0_rdrequest(struct stm32_usbdev_s *priv)
+{
+  uint32_t src;
+  int pmalen;
+  int readlen;
+
+  /* Get the number of bytes to read from packet memory */
+
+  pmalen  = stm32_geteprxcount(EP0);
+
+  uinfo("EP0: pmalen=%d\n", pmalen);
+  usbtrace(TRACE_READ(EP0), pmalen);
+
+  /* Read the data into our special buffer for SETUP data */
+
+  readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, pmalen);
+  src     = stm32_geteprxaddr(EP0);
+
+  /* Receive the next packet */
+
+  stm32_copyfrompma(&priv->ep0data[0], src, readlen);
+
+  /* Now we can process the setup command */
+
+  priv->ep0state  = EP0STATE_SETUP_READY;
+  priv->ep0datlen = readlen;
+  usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPOUTDATA),
+           readlen);
+
+  stm32_ep0setup(priv);
+  priv->ep0datlen = 0; /* mark the date consumed */
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_rdrequest
+ ****************************************************************************/
+
+static int stm32_rdrequest(struct stm32_usbdev_s *priv,
+                           struct stm32_ep_s *privep)
+{
+  struct stm32_req_s *privreq;
+  uint32_t src;
+  uint8_t *dest;
+  uint8_t epno;
+  int pmalen;
+  int readlen;
+
+  /* Check the request from the head of the endpoint request queue */
+
+  epno    = USB_EPNO(privep->ep.eplog);
+  privreq = stm32_rqpeek(privep);
+  if (!privreq)
+    {
+      /* Incoming data available in PMA, but no packet to receive the data.
+       * Mark that the RX data is pending and hope that a packet is returned
+       * soon.
+       */
+
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTQEMPTY), epno);
+      return -ENOENT;
+    }
+
+  uinfo("EP%d: len=%d xfrd=%d\n", epno, privreq->req.len, privreq->req.xfrd);
+
+  /* Ignore any attempt to receive a zero length packet */
+
+  if (privreq->req.len == 0)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTNULLPACKET), 0);
+      stm32_reqcomplete(privep, OK);
+      return OK;
+    }
+
+  usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
+
+  /* Get the source and destination transfer addresses */
+
+  dest    = privreq->req.buf + privreq->req.xfrd;
+  src     = stm32_geteprxaddr(epno);
+
+  /* Get the number of bytes to read from packet memory */
+
+  pmalen  = stm32_geteprxcount(epno);
+  readlen = MIN(privreq->req.len, pmalen);
+
+  /* Receive the next packet */
+
+  stm32_copyfrompma(dest, src, readlen);
+
+  /* If the receive buffer is full or this is a partial packet,
+   * then we are finished with the request buffer).
+   */
+
+  privreq->req.xfrd += readlen;
+  if (pmalen < privep->ep.maxpacket || privreq->req.xfrd >= privreq->req.len)
+    {
+      /* Return the read request to the class driver. */
+
+      usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd);
+      stm32_reqcomplete(privep, OK);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_cancelrequests
+ ****************************************************************************/
+
+static void stm32_cancelrequests(struct stm32_ep_s *privep)
+{
+  while (!stm32_rqempty(privep))
+    {
+      usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
+               (stm32_rqpeek(privep))->req.xfrd);
+      stm32_reqcomplete(privep, -ESHUTDOWN);
+    }
+}
+
+/****************************************************************************
+ * Interrupt Level Processing
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_dispatchrequest
+ ****************************************************************************/
+
+static void stm32_dispatchrequest(struct stm32_usbdev_s *priv)
+{
+  int ret;
+
+  usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DISPATCH), 0);
+  if (priv && priv->driver)
+    {
+      /* Forward to the control request to the class driver implementation */
+
+      ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl,
+                        priv->ep0data, priv->ep0datlen);
+      if (ret < 0)
+        {
+          /* Stall on failure */
+
+          usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DISPATCHSTALL), 0);
+          priv->ep0state = EP0STATE_STALLED;
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_epdone
+ ****************************************************************************/
+
+static void stm32_epdone(struct stm32_usbdev_s *priv, uint8_t epno)
+{
+  struct stm32_ep_s *privep;
+  uint16_t epr;
+
+  /* Decode and service non control endpoints interrupt */
+
+  epr    = stm32_getreg(STM32_USB_EPR(epno));
+  privep = &priv->eplist[epno];
+
+  /* OUT: host-to-device
+   * CTR_RX is set by the hardware when an OUT/SETUP transaction
+   * successfully completed on this endpoint.
+   */
+
+  if ((epr & USB_EPR_CTR_RX) != 0)
+    {
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTDONE), epr);
+
+      /* Handle read requests.  First check if a read request is available to
+       * accept the host data.
+       */
+
+      if (!stm32_rqempty(privep))
+        {
+          /* Read host data into the current read request */
+
+          stm32_rdrequest(priv, privep);
+
+          /* "After the received data is processed, the application software
+           *  should set the STAT_RX bits to '11' (Valid) in the USB_EPnR,
+           *  enabling further transactions. "
+           */
+
+          priv->rxstatus  = USB_EPR_STATRX_VALID;
+        }
+
+      /* NAK further OUT packets if there there no more read requests */
+
+      if (stm32_rqempty(privep))
+        {
+          usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTPENDING),
+                  (uint16_t)epno);
+
+          /* Mark the RX processing as pending and NAK any OUT actions
+           * on this endpoint.  "While the STAT_RX bits are equal to '10'
+           * (NAK), any OUT request addressed to that endpoint is NAKed,
+           * indicating a flow control condition: the USB host will retry
+           * the transaction until it succeeds."
+           */
+
+          priv->rxstatus  = USB_EPR_STATRX_NAK;
+          priv->rxpending = true;
+        }
+
+      /* Clear the interrupt status and set the new RX status */
+
+      stm32_clrepctrrx(epno);
+      stm32_seteprxstatus(epno, priv->rxstatus);
+    }
+
+  /* IN: device-to-host
+   * CTR_TX is set when an IN transaction successfully completes on
+   * an endpoint
+   */
+
+  else if ((epr & USB_EPR_CTR_TX) != 0)
+    {
+      /* Clear interrupt status */
+
+      stm32_clrepctrtx(epno);
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINDONE), epr);
+
+      /* Handle write requests */
+
+      priv->txstatus = USB_EPR_STATTX_NAK;
+      if (epno == EP0)
+        {
+          stm32_wrrequest_ep0(priv, privep);
+        }
+      else
+        {
+          stm32_wrrequest(priv, privep);
+        }
+
+      /* Set the new TX status */
+
+      stm32_seteptxstatus(epno, priv->txstatus);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_setdevaddr
+ ****************************************************************************/
+
+static void stm32_setdevaddr(struct stm32_usbdev_s *priv, uint8_t value)
+{
+  int epno;
+
+  /* Set address in every allocated endpoint */
+
+  for (epno = 0; epno < STM32_NENDPOINTS; epno++)
+    {
+      if (stm32_epreserved(priv, epno))
+        {
+          stm32_setepaddress((uint8_t)epno, (uint8_t)epno);
+        }
+    }
+
+  /* Set the device address and enable function */
+
+  stm32_putreg(value | USB_DADDR_EF, STM32_USB_DADDR);
+}
+
+/****************************************************************************
+ * Name: stm32_ep0setup
+ ****************************************************************************/
+
+static void stm32_ep0setup(struct stm32_usbdev_s *priv)
+{
+  struct stm32_ep_s   *ep0     = &priv->eplist[EP0];
+  struct stm32_req_s  *privreq = stm32_rqpeek(ep0);
+  struct stm32_ep_s   *privep;
+  union wb_u           value;
+  union wb_u           index;
+  union wb_u           len;
+  union wb_u           response;
+  bool                 handled = false;
+  uint8_t              epno;
+  int                  nbytes = 0; /* Assume zero-length packet */
+
+  /* Terminate any pending requests (doesn't work if the pending request
+   * was a zero-length transfer!)
+   */
+
+  while (!stm32_rqempty(ep0))
+    {
+      int16_t result = OK;
+      if (privreq->req.xfrd != privreq->req.len)
+        {
+          result = -EPROTO;
+        }
+
+      usbtrace(TRACE_COMPLETE(ep0->ep.eplog), privreq->req.xfrd);
+      stm32_reqcomplete(ep0, result);
+    }
+
+  /* Assume NOT stalled; no TX in progress */
+
+  ep0->stalled  = 0;
+  ep0->txbusy   = 0;
+
+  value.w       = 0;
+  index.w       = 0;
+  len.w         = 0;
+  response.w    = 0;
+
+  /* Check to see if called from the DATA phase of a SETUP Transfer */
+
+  if (priv->ep0state != EP0STATE_SETUP_READY)
+    {
+      /* Not the data phase */
+
+      /* Get a 32-bit PMA address and use that to get the 8-byte setup
+       * request
+       */
+
+      stm32_copyfrompma((uint8_t *)&priv->ctrl, stm32_geteprxaddr(EP0),
+                        USB_SIZEOF_CTRLREQ);
+
+      /* And extract the little-endian 16-bit values to host order */
+
+      value.w = GETUINT16(priv->ctrl.value);
+      index.w = GETUINT16(priv->ctrl.index);
+      len.w   = GETUINT16(priv->ctrl.len);
+
+      uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n",
+            priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w);
+
+      /* Is this an setup with OUT and data of length > 0 */
+
+      if (USB_REQ_ISOUT(priv->ctrl.type) && len.w > 0)
+        {
+          usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPOUT), len.w);
+
+          /* At this point priv->ctrl is the setup packet. */
+
+          priv->ep0state = EP0STATE_SETUP_OUT;
+          return;
+        }
+      else
+        {
+          priv->ep0state = EP0STATE_SETUP_READY;
+        }
+    }
+
+  /* Dispatch any non-standard requests */
+
+  if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
+    {
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_NOSTDREQ), priv->ctrl.type);
+
+      /* Let the class implementation handle all non-standar requests */
+
+      stm32_dispatchrequest(priv);
+      return;
+    }
+
+  /* Handle standard request.  Pick off the things of interest to the
+   * USB device controller driver; pass what is left to the class driver
+   */
+
+  switch (priv->ctrl.req)
+    {
+    case USB_REQ_GETSTATUS:
+      {
+        /* type:  device-to-host; recipient = device, interface, endpoint
+         * value: 0
+         * index: zero interface endpoint
+         * len:   2; data = status
+         */
+
+        usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSTATUS),
+                 priv->ctrl.type);
+        if (len.w != 2 || (priv->ctrl.type &
+            USB_REQ_DIR_IN) == 0 ||
+            index.b[MSB] != 0 || value.w != 0)
+          {
+            usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS), 0);
+            priv->ep0state = EP0STATE_STALLED;
+          }
+        else
+          {
+            switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK)
+              {
+               case USB_REQ_RECIPIENT_ENDPOINT:
+                {
+                  epno = USB_EPNO(index.b[LSB]);
+                  usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPGETSTATUS),
+                           epno);
+                  if (epno >= STM32_NENDPOINTS)
+                    {
+                      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS),
+                               epno);
+                      priv->ep0state = EP0STATE_STALLED;
+                    }
+                  else
+                    {
+                      response.w = 0; /* Not stalled */
+                      nbytes     = 2; /* Response size: 2 bytes */
+
+                      if (USB_ISEPIN(index.b[LSB]))
+                        {
+                          /* IN endpoint */
+
+                          if (stm32_eptxstalled(epno))
+                            {
+                              /* IN Endpoint stalled */
+
+                              response.b[LSB] = 1; /* Stalled */
+                            }
+                          }
+                      else
+                        {
+                          /* OUT endpoint */
+
+                          if (stm32_eprxstalled(epno))
+                            {
+                              /* OUT Endpoint stalled */
+
+                              response.b[LSB] = 1; /* Stalled */
+                            }
+                        }
+                    }
+                }
+                break;
+
+              case USB_REQ_RECIPIENT_DEVICE:
+                {
+                 if (index.w == 0)
+                    {
+                      usbtrace(TRACE_INTDECODE(
+                               STM32_TRACEINTID_DEVGETSTATUS), 0);
+
+                      /* Features:  Remote Wakeup=YES; selfpowered=? */
+
+                      response.w      = 0;
+                      response.b[LSB] = (priv->selfpowered <<
+                                         USB_FEATURE_SELFPOWERED) |
+                                        (1 << USB_FEATURE_REMOTEWAKEUP);
+                      nbytes          = 2; /* Response size: 2 bytes */
+                    }
+                  else
+                    {
+                      usbtrace(TRACE_DEVERROR(
+                               STM32_TRACEERR_BADDEVGETSTATUS), 0);
+                      priv->ep0state = EP0STATE_STALLED;
+                    }
+                }
+                break;
+
+              case USB_REQ_RECIPIENT_INTERFACE:
+                {
+                  usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IFGETSTATUS), 0);
+                  response.w = 0;
+                  nbytes     = 2; /* Response size: 2 bytes */
+                }
+                break;
+
+              default:
+                {
+                  usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSTATUS), 0);
+                  priv->ep0state = EP0STATE_STALLED;
+                }
+                break;
+              }
+          }
+      }
+      break;
+
+    case USB_REQ_CLEARFEATURE:
+      {
+        /* type:  host-to-device; recipient = device, interface or endpoint
+         * value: feature selector
+         * index: zero interface endpoint;
+         * len:   zero, data = none
+         */
+
+        usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_CLEARFEATURE),
+                 priv->ctrl.type);
+        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
+             USB_REQ_RECIPIENT_ENDPOINT)
+          {
+            /* Let the class implementation handle all recipients
+             * (except for the endpoint recipient)
+             */
+
+            stm32_dispatchrequest(priv);
+            handled = true;
+          }
+        else
+          {
+            /* Endpoint recipient */
+
+            epno = USB_EPNO(index.b[LSB]);
+            if (epno < STM32_NENDPOINTS && index.b[MSB] == 0 &&
+                value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
+              {
+                privep         = &priv->eplist[epno];
+                privep->halted = 0;
+                stm32_epstall(&privep->ep, true);
+              }
+            else
+              {
+                usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADCLEARFEATURE), 0);
+                priv->ep0state = EP0STATE_STALLED;
+              }
+          }
+      }
+      break;
+
+    case USB_REQ_SETFEATURE:
+      {
+        /* type:  host-to-device; recipient = device, interface, endpoint
+         * value: feature selector
+         * index: zero interface endpoint;
+         * len:   0; data = none
+         */
+
+        usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETFEATURE),
+                 priv->ctrl.type);
+        if (((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
+              USB_REQ_RECIPIENT_DEVICE) &&
+              value.w == USB_FEATURE_TESTMODE)
+          {
+            /* Special case recipient=device test mode */
+
+            uinfo("test mode: %d\n", index.w);
+          }
+        else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
+                  USB_REQ_RECIPIENT_ENDPOINT)
+          {
+            /* The class driver handles all recipients except
+             * recipient=endpoint
+             */
+
+            stm32_dispatchrequest(priv);
+            handled = true;
+          }
+        else
+          {
+            /* Handler recipient=endpoint */
+
+            epno = USB_EPNO(index.b[LSB]);
+            if (epno < STM32_NENDPOINTS && index.b[MSB] == 0 &&
+                value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
+              {
+                privep         = &priv->eplist[epno];
+                privep->halted = 1;
+                stm32_epstall(&privep->ep, false);
+              }
+            else
+              {
+                usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0);
+                priv->ep0state = EP0STATE_STALLED;
+              }
+          }
+      }
+      break;
+
+    case USB_REQ_SETADDRESS:
+      {
+        /* type:  host-to-device; recipient = device
+         * value: device address
+         * index: 0
+         * len:   0; data = none
+         */
+
+        usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPSETADDRESS),
+                 value.w);
+        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
+             USB_REQ_RECIPIENT_DEVICE ||
+             index.w != 0 || len.w != 0 || value.w > 127)
+          {
+            usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETADDRESS), 0);
+            priv->ep0state = EP0STATE_STALLED;
+          }
+        /* Note that setting of the device address will be deferred.
+         * A zero-length packet will be sent and the device address
+         * will be set when the zero- length packet transfer completes.
+         */
+      }
+      break;
+
+    case USB_REQ_GETDESCRIPTOR:
+      /* type:  device-to-host; recipient = device
+       * value: descriptor type and index
+       * index: 0 or language ID;
+       * len:   descriptor len; data = descriptor
+       */
+
+    case USB_REQ_SETDESCRIPTOR:
+      /* type:  host-to-device; recipient = device
+       * value: descriptor type and index
+       * index: 0 or language ID;
+       * len:   descriptor len; data = descriptor
+       */
+
+      {
+        usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETDESC),
+                 priv->ctrl.type);
+
+        /* The request seems valid...
+         * let the class implementation handle it
+         */
+
+        stm32_dispatchrequest(priv);
+        handled = 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),
+                 priv->ctrl.type);
+        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
+             USB_REQ_RECIPIENT_DEVICE &&
+             value.w == 0 && index.w == 0 && len.w == 1)
+          {
+            /* The request seems valid...
+             * let the class implementation handle it
+             */
+
+            stm32_dispatchrequest(priv);
+            handled = true;
+          }
+        else
+          {
+            usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETCONFIG), 0);
+            priv->ep0state = EP0STATE_STALLED;
+          }
+      }
+      break;
+
+    case USB_REQ_SETCONFIGURATION:
+      /* type:  host-to-device; recipient = device
+       * value: configuration value
+       * index: 0;
+       * len:   0; data = none
+       */
+
+      {
+        usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETCONFIG),
+                 priv->ctrl.type);
+        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
+             USB_REQ_RECIPIENT_DEVICE &&
+             index.w == 0 && len.w == 0)
+          {
+             /* The request seems valid...
+              * let the class implementation handle it
+              */
+
+             stm32_dispatchrequest(priv);
+             handled = true;
+          }
+        else
+          {
+            usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETCONFIG), 0);
+            priv->ep0state = EP0STATE_STALLED;
+          }
+      }
+      break;
+
+    case USB_REQ_GETINTERFACE:
+      /* type:  device-to-host; recipient = interface
+       * value: 0
+       * index: interface;
+       * len:   1; data = alt interface
+       */
+
+    case USB_REQ_SETINTERFACE:
+      /* type:  host-to-device; recipient = interface
+       * value: alternate setting
+       * index: interface;
+       * len:   0; data = none
+       */
+
+      {
+        /* Let the class implementation handle the request */
+
+        usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETIF),
+                 priv->ctrl.type);
+        stm32_dispatchrequest(priv);
+        handled = true;
+      }
+      break;
+
+    case USB_REQ_SYNCHFRAME:
+      /* type:  device-to-host; recipient = endpoint
+       * value: 0
+       * index: endpoint;
+       * len:   2; data = frame number
+       */
+
+      {
+        usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SYNCHFRAME), 0);
+      }
+      break;
+
+    default:
+      {
+        usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDCTRLREQ),
+                 priv->ctrl.req);
+        priv->ep0state = EP0STATE_STALLED;
+      }
+      break;
+    }
+
+  /* At this point, the request has been handled and there are three possible
+   * outcomes:
+   *
+   * 1. The setup request was successfully handled above and a response
+   *    packet must be sent (may be a zero length packet).
+   * 2. The request was successfully handled by the class implementation.
+   *    In case, the EP0 IN response has already been queued and the local
+   *    variable 'handled' will be set to true and
+   *    ep0state != EP0STATE_STALLED;
+   * 3. An error was detected in either the above logic or by the class
+   *    implementation logic.  In either case, priv->state will be set
+   *    EP0STATE_STALLED to indicate this case.
+   *
+   * NOTE:
+   * Non-standard requests are a special case.  They are handled by the
+   * class implementation and this function returned early above, skipping
+   * this logic altogether.
+   */
+
+  if (priv->ep0state != EP0STATE_STALLED && !handled)
+    {
+      /* We will response.  First, restrict the data length to the length
+       * requested in the setup packet
+       */
+
+      if (nbytes > len.w)
+        {
+          nbytes = len.w;
+        }
+
+      /* Send the response (might be a zero-length packet) */
+
+      stm32_epwrite(priv, ep0, response.b, nbytes);
+      priv->ep0state = EP0STATE_IDLE;
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_ep0in
+ ****************************************************************************/
+
+static void stm32_ep0in(struct stm32_usbdev_s *priv)
+{
+  /* There is no longer anything in the EP0 TX packet memory */
+
+  priv->eplist[EP0].txbusy = false;
+
+  /* Are we processing the completion of one packet of an outgoing request
+   * from the class driver?
+   */
+
+  if (priv->ep0state == EP0STATE_WRREQUEST)
+    {
+      stm32_wrrequest_ep0(priv, &priv->eplist[EP0]);
+    }
+
+  /* No.. Are we processing the completion of a status response? */
+
+  else if (priv->ep0state == EP0STATE_IDLE)
+    {
+      /* Look at the saved SETUP command.  Was it a SET ADDRESS request?
+       * If so, then now is the time to set the address.
+       */
+
+      if (priv->ctrl.req == USB_REQ_SETADDRESS &&
+          (priv->ctrl.type & REQRECIPIENT_MASK) ==
+           (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_DEVICE))
+        {
+          union wb_u value;
+          value.w = GETUINT16(priv->ctrl.value);
+          stm32_setdevaddr(priv, value.b[LSB]);
+        }
+    }
+  else
+    {
+      priv->ep0state = EP0STATE_STALLED;
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_ep0out
+ ****************************************************************************/
+
+static void stm32_ep0out(struct stm32_usbdev_s *priv)
+{
+  int ret;
+
+  struct stm32_ep_s *privep = &priv->eplist[EP0];
+  switch (priv->ep0state)
+    {
+      case EP0STATE_RDREQUEST:           /* Read request in progress */
+      case EP0STATE_IDLE:                /* No transfer in progress */
+        ret = stm32_rdrequest(priv, privep);
+        priv->ep0state = ((ret == OK) ? EP0STATE_RDREQUEST : EP0STATE_IDLE);
+        break;
+
+      case EP0STATE_SETUP_OUT:           /* SETUP was waiting for data */
+        ret = stm32_ep0_rdrequest(priv); /* Off load the data and run the
+                                          * last set up command with the OUT
+                                          * data
+                                          */
+        priv->ep0state = EP0STATE_IDLE;  /* There is no notion of receiving OUT
+                                          * data greater then the length of
+                                          * CONFIG_USBDEV_SETUP_MAXDATASIZE
+                                          * so we are done
+                                          */
+        break;
+
+      default:
+        /* Unexpected state OR host aborted the OUT transfer before it
+         * completed, STALL the endpoint in either case
+         */
+
+        priv->ep0state = EP0STATE_STALLED;
+        break;
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_ep0done
+ ****************************************************************************/
+
+static inline void stm32_ep0done(struct stm32_usbdev_s *priv, uint16_t istr)
+{
+  uint16_t epr;
+
+  /* Initialize RX and TX status.  We shouldn't have to actually look at the
+   * status because the hardware is supposed to set the both RX and TX status
+   * to NAK when an EP0 SETUP occurs (of course, this might not be a setup)
+   */
+
+  priv->rxstatus = USB_EPR_STATRX_NAK;
+  priv->txstatus = USB_EPR_STATTX_NAK;
+
+  /* Set both RX and TX status to NAK  */
+
+  stm32_seteprxstatus(EP0, USB_EPR_STATRX_NAK);
+  stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK);
+
+  /* Check the direction bit to determine if this the completion of an EP0
+   * packet sent to or received from the host PC.
+   */
+
+  if ((istr & USB_ISTR_DIR) == 0)
+    {
+      /* EP0 IN: device-to-host (DIR=0) */
+
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0IN), istr);
+      stm32_clrepctrtx(EP0);
+      stm32_ep0in(priv);
+    }
+  else
+    {
+      /* EP0 OUT: host-to-device (DIR=1) */
+
+      epr = stm32_getreg(STM32_USB_EPR(EP0));
+
+      /* CTR_TX is set when an IN transaction successfully
+       * completes on an endpoint
+       */
+
+      if ((epr & USB_EPR_CTR_TX) != 0)
+        {
+          usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0INDONE), epr);
+          stm32_clrepctrtx(EP0);
+          stm32_ep0in(priv);
+        }
+
+      /* SETUP is set by the hardware when the last completed
+       * transaction was a control endpoint SETUP
+       */
+
+      else if ((epr & USB_EPR_SETUP) != 0)
+        {
+          usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPDONE), epr);
+          stm32_clrepctrrx(EP0);
+          stm32_ep0setup(priv);
+        }
+
+      /* Set by the hardware when an OUT/SETUP transaction successfully
+       * completed on this endpoint.
+       */
+
+      else if ((epr & USB_EPR_CTR_RX) != 0)
+        {
+          usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0OUTDONE), epr);
+          stm32_clrepctrrx(EP0);
+          stm32_ep0out(priv);
+        }
+
+      /* None of the above */
+
+      else
+        {
+          usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0BADCTR), epr);
+          return; /* Does this ever happen? */
+        }
+    }
+
+  /* Make sure that the EP0 packet size is still OK (superstitious?) */
+
+  stm32_seteprxcount(EP0, STM32_EP0MAXPACKET);
+
+  /* Now figure out the new RX/TX status.  Here are all possible
+   * consequences of the above EP0 operations:
+   *
+   * rxstatus txstatus ep0state  MEANING
+   * -------- -------- --------- ---------------------------------
+   * NAK      NAK      IDLE      Nothing happened
+   * NAK      VALID    IDLE      EP0 response sent from USBDEV driver
+   * NAK      VALID    WRREQUEST EP0 response sent from class driver
+   * NAK      ---      STALL     Some protocol error occurred
+   *
+   * First handle the STALL condition:
+   */
+
+  if (priv->ep0state == EP0STATE_STALLED)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED),
+               priv->ep0state);
+      priv->rxstatus = USB_EPR_STATRX_STALL;
+      priv->txstatus = USB_EPR_STATTX_STALL;
+    }
+
+  /* Was a transmission started?  If so, txstatus will be VALID.  The
+   * only special case to handle is when both are set to NAK.  In that
+   * case, we need to set RX status to VALID in order to accept the next
+   * SETUP request.
+   */
+
+  else if (priv->rxstatus == USB_EPR_STATRX_NAK &&
+           priv->txstatus == USB_EPR_STATTX_NAK)
+    {
+      priv->rxstatus = USB_EPR_STATRX_VALID;
+    }
+
+  /* Now set the new TX and RX status */
+
+  stm32_seteprxstatus(EP0, priv->rxstatus);
+  stm32_seteptxstatus(EP0, priv->txstatus);
+}
+
+/****************************************************************************
+ * Name: stm32_lptransfer
+ ****************************************************************************/
+
+static void stm32_lptransfer(struct stm32_usbdev_s *priv)
+{
+  uint8_t  epno;
+  uint16_t istr;
+
+  /* Stay in loop while LP interrupts are pending */
+
+  while (((istr = stm32_getreg(STM32_USB_ISTR)) & USB_ISTR_CTR) != 0)
+    {
+      stm32_putreg((uint16_t)~USB_ISTR_CTR, STM32_USB_ISTR);
+
+      /* Extract highest priority endpoint number */
+
+      epno = (uint8_t)(istr & USB_ISTR_EPID_MASK);
+
+      /* Handle EP0 completion events */
+
+      if (epno == 0)
+        {
+          stm32_ep0done(priv, istr);
+        }
+
+      /* Handle other endpoint completion events */
+
+      else
+        {
+          stm32_epdone(priv, epno);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_hpinterrupt
+ ****************************************************************************/
+
+static int stm32_hpinterrupt(int irq, void *context, void *arg)
+{
+  /* For now there is only one USB controller, but we will always refer to
+   * it using a pointer to make any future ports to multiple USB controllers
+   * easier.
+   */
+
+  struct stm32_usbdev_s *priv = &g_usbdev;
+  uint16_t istr;
+  uint8_t  epno;
+
+  /* High priority interrupts are only triggered by a correct transfer event
+   * for isochronous and double-buffer bulk transfers.
+   */
+
+  istr = stm32_getreg(STM32_USB_ISTR);
+  usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_HPINTERRUPT), istr);
+  while ((istr & USB_ISTR_CTR) != 0)
+    {
+      stm32_putreg((uint16_t)~USB_ISTR_CTR, STM32_USB_ISTR);
+
+      /* Extract highest priority endpoint number */
+
+      epno = (uint8_t)(istr & USB_ISTR_EPID_MASK);
+
+      /* And handle the completion event */
+
+      stm32_epdone(priv, epno);
+
+      /* Fetch the status again for the next time through the loop */
+
+      istr = stm32_getreg(STM32_USB_ISTR);
+    }
+
+  usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_HPINTERRUPT), 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_lpinterrupt
+ ****************************************************************************/
+
+static int stm32_lpinterrupt(int irq, void *context, void *arg)
+{
+  /* For now there is only one USB controller, but we will always refer to
+   * it using a pointer to make any future ports to multiple USB controllers
+   * easier.
+   */
+
+  struct stm32_usbdev_s *priv = &g_usbdev;
+  uint16_t istr = stm32_getreg(STM32_USB_ISTR);
+
+  usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_LPINTERRUPT), istr);
+
+  /* Handle Reset interrupts.  When this event occurs, the peripheral is left
+   * in the same conditions it is left by the system reset (but with the
+   * USB controller enabled).
+   */
+
+  if ((istr & USB_ISTR_RESET) != 0)
+    {
+      /* Reset interrupt received. Clear the RESET interrupt status. */
+
+      stm32_putreg(~USB_ISTR_RESET, STM32_USB_ISTR);
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_RESET), istr);
+
+      /* Restore our power-up state and exit now because istr is no longer
+       * valid.
+       */
+
+      stm32_reset(priv);
+      goto exit_lpinterrupt;
+    }
+
+  /* Handle Wakeup interrupts.
+   * This interrupt is only enable while the USB is suspended.
+   */
+
+  if ((istr & USB_ISTR_WKUP & priv->imask) != 0)
+    {
+      /* Wakeup interrupt received. Clear the WKUP interrupt status.  The
+       * cause of the resume is indicated in the FNR register
+       */
+
+      stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR);
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_WKUP),
+               stm32_getreg(STM32_USB_FNR));
+
+      /* Perform the wakeup action */
+
+      stm32_initresume(priv);
+      priv->rsmstate = RSMSTATE_IDLE;
+
+      /* Disable ESOF polling, disable the wakeup interrupt, and
+       * re-enable the suspend interrupt.  Clear any pending SUSP
+       * interrupts.
+       */
+
+      stm32_setimask(priv, USB_CNTR_SUSPM, USB_CNTR_ESOFM | USB_CNTR_WKUPM);
+      stm32_putreg(~USB_CNTR_SUSPM, STM32_USB_ISTR);
+    }
+
+  if ((istr & USB_ISTR_SUSP & priv->imask) != 0)
+    {
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SUSP), 0);
+      stm32_suspend(priv);
+
+      /* Clear of the ISTR bit must be done after setting
+       * of USB_CNTR_FSUSP
+       */
+
+      stm32_putreg(~USB_ISTR_SUSP, STM32_USB_ISTR);
+    }
+
+  if ((istr & USB_ISTR_ESOF & priv->imask) != 0)
+    {
+      stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR);
+
+      /* Resume handling timing is made with ESOFs */
+
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_ESOF), 0);
+      stm32_esofpoll(priv);
+    }
+
+  if ((istr & USB_ISTR_CTR & priv->imask) != 0)
+    {
+      /* Low priority endpoint correct transfer interrupt */
+
+      usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_LPCTR), istr);
+      stm32_lptransfer(priv);
+    }
+
+exit_lpinterrupt:
+  usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_LPINTERRUPT),
+           stm32_getreg(STM32_USB_EP0R));
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_setimask
+ ****************************************************************************/
+
+static void
+stm32_setimask(struct stm32_usbdev_s *priv,
+               uint16_t setbits, uint16_t clrbits)
+{
+  uint16_t regval;
+
+  /* Adjust the interrupt mask bits in the shadow copy first */
+
+  priv->imask &= ~clrbits;
+  priv->imask |= setbits;
+
+  /* Then make the interrupt mask bits in the CNTR register match the shadow
+   * register (Hmmm... who is shadowing whom?)
+   */
+
+  regval  = stm32_getreg(STM32_USB_CNTR);
+  regval &= ~USB_CNTR_ALLINTS;
+  regval |= priv->imask;
+  stm32_putreg(regval, STM32_USB_CNTR);
+}
+
+/****************************************************************************
+ * Suspend/Resume Helpers
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_suspend
+ ****************************************************************************/
+
+static void stm32_suspend(struct stm32_usbdev_s *priv)
+{
+  uint16_t regval;
+
+  /* Notify the class driver of the suspend event */
+
+  if (priv->driver)
+    {
+      CLASS_SUSPEND(priv->driver, &priv->usbdev);
+    }
+
+  /* Disable ESOF polling, disable the SUSP interrupt, and enable the WKUP
+   * interrupt.  Clear any pending WKUP interrupt.
+   */
+
+  stm32_setimask(priv, USB_CNTR_WKUPM, USB_CNTR_ESOFM | USB_CNTR_SUSPM);
+  stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR);
+
+  /* Set the FSUSP bit in the CNTR register.  This activates suspend mode
+   * within the USB peripheral and disables further SUSP interrupts.
+   */
+
+  regval  = stm32_getreg(STM32_USB_CNTR);
+  regval |= USB_CNTR_FSUSP;
+  stm32_putreg(regval, STM32_USB_CNTR);
+
+  /* If we are not a self-powered device, the got to low-power mode */
+
+  if (!priv->selfpowered)
+    {
+      /* Setting LPMODE in the CNTR register removes static power
+       * consumption in the USB analog transceivers but keeps them
+       * able to detect resume activity
+       */
+
+      regval = stm32_getreg(STM32_USB_CNTR);
+      regval |= USB_CNTR_LPMODE;
+      stm32_putreg(regval, STM32_USB_CNTR);
+    }
+
+  /* Let the board-specific logic know that we have entered the suspend
+   * state
+   */
+
+  stm32_usbsuspend((struct usbdev_s *)priv, false);
+}
+
+/****************************************************************************
+ * Name: stm32_initresume
+ ****************************************************************************/
+
+static void stm32_initresume(struct stm32_usbdev_s *priv)
+{
+  uint16_t regval;
+
+  /* This function is called when either (1) a WKUP interrupt is received
+   * from the host PC, or (2) the class device implementation calls the
+   * wakeup() method.
+   */
+
+  /* Clear the USB low power mode (lower power mode was not set if this is
+   * a self-powered device.  Also, low power mode is automatically cleared by
+   * hardware when a WKUP interrupt event occurs).
+   */
+
+  regval = stm32_getreg(STM32_USB_CNTR);
+  regval &= (~USB_CNTR_LPMODE);
+  stm32_putreg(regval, STM32_USB_CNTR);
+
+  /* Restore full power -- whatever that means for this particular board */
+
+  stm32_usbsuspend((struct usbdev_s *)priv, true);
+
+  /* Reset FSUSP bit and enable normal interrupt handling */
+
+  stm32_putreg(STM32_CNTR_SETUP, STM32_USB_CNTR);
+
+  /* Notify the class driver of the resume event */
+
+  if (priv->driver)
+    {
+      CLASS_RESUME(priv->driver, &priv->usbdev);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_esofpoll
+ ****************************************************************************/
+
+static void stm32_esofpoll(struct stm32_usbdev_s *priv)
+{
+  uint16_t regval;
+
+    /* Called periodically from ESOF interrupt after RSMSTATE_STARTED */
+
+  switch (priv->rsmstate)
+    {
+    /* One ESOF after internal resume requested */
+
+    case RSMSTATE_STARTED:
+      regval         = stm32_getreg(STM32_USB_CNTR);
+      regval        |= USB_CNTR_RESUME;
+      stm32_putreg(regval, STM32_USB_CNTR);
+      priv->rsmstate = RSMSTATE_WAITING;
+      priv->nesofs   = 10;
+      break;
+
+    /* Countdown before completing the operation */
+
+    case RSMSTATE_WAITING:
+      priv->nesofs--;
+      if (priv->nesofs == 0)
+        {
+          /* Okay.. we are ready to resume normal operation */
+
+          regval         = stm32_getreg(STM32_USB_CNTR);
+          regval        &= (~USB_CNTR_RESUME);
+          stm32_putreg(regval, STM32_USB_CNTR);
+          priv->rsmstate = RSMSTATE_IDLE;
+
+          /* Disable ESOF polling, disable the SUSP interrupt, and enable
+           * the WKUP interrupt.  Clear any pending WKUP interrupt.
+           */
+
+          stm32_setimask(priv,
+                         USB_CNTR_WKUPM, USB_CNTR_ESOFM | USB_CNTR_SUSPM);
+          stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR);
+        }
+      break;
+
+    case RSMSTATE_IDLE:
+    default:
+      priv->rsmstate = RSMSTATE_IDLE;
+      break;
+    }
+}
+
+/****************************************************************************
+ * Endpoint Helpers
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_epreserve
+ ****************************************************************************/
+
+static inline struct stm32_ep_s *
+stm32_epreserve(struct stm32_usbdev_s *priv, uint8_t epset)
+{
+  struct stm32_ep_s *privep = NULL;
+  irqstate_t flags;
+  int epndx = 0;
+
+  flags = enter_critical_section();
+  epset &= priv->epavail;
+  if (epset)
+    {
+      /* Select the lowest bit in the set of matching, available endpoints
+       * (skipping EP0)
+       */
+
+      for (epndx = 1; epndx < STM32_NENDPOINTS; epndx++)
+        {
+          uint8_t bit = STM32_ENDP_BIT(epndx);
+          if ((epset & bit) != 0)
+            {
+              /* Mark the endpoint no longer available */
+
+              priv->epavail &= ~bit;
+
+              /* And return the pointer to the standard endpoint structure */
+
+              privep = &priv->eplist[epndx];
+              break;
+            }
+        }
+    }
+
+  leave_critical_section(flags);
+  return privep;
+}
+
+/****************************************************************************
+ * Name: stm32_epunreserve
+ ****************************************************************************/
+
+static inline void
+stm32_epunreserve(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep)
+{
+  irqstate_t flags = enter_critical_section();
+  priv->epavail   |= STM32_ENDP_BIT(USB_EPNO(privep->ep.eplog));
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32_epreserved
+ ****************************************************************************/
+
+static inline bool
+stm32_epreserved(struct stm32_usbdev_s *priv, int epno)
+{
+  return ((priv->epavail & STM32_ENDP_BIT(epno)) == 0);
+}
+
+/****************************************************************************
+ * Name: stm32_epallocpma
+ ****************************************************************************/
+
+static int stm32_epallocpma(struct stm32_usbdev_s *priv)
+{
+  irqstate_t flags;
+  int bufno = ERROR;
+  int bufndx;
+
+  flags = enter_critical_section();
+  for (bufndx = 2; bufndx < STM32_NBUFFERS; bufndx++)
+    {
+      /* Check if this buffer is available */
+
+      uint8_t bit = STM32_BUFFER_BIT(bufndx);
+      if ((priv->bufavail & bit) != 0)
+        {
+          /* Yes.. Mark the endpoint no longer available */
+
+          priv->bufavail &= ~bit;
+
+          /* And return the index of the allocated buffer */
+
+          bufno = bufndx;
+          break;
+        }
+    }
+
+  leave_critical_section(flags);
+  return bufno;
+}
+
+/****************************************************************************
+ * Name: stm32_epfreepma
+ ****************************************************************************/
+
+static inline void
+stm32_epfreepma(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep)
+{
+  irqstate_t flags = enter_critical_section();
+  priv->epavail   |= STM32_ENDP_BIT(privep->bufno);
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Endpoint operations
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_epconfigure
+ ****************************************************************************/
+
+static int stm32_epconfigure(struct usbdev_ep_s *ep,
+                             const struct usb_epdesc_s *desc,
+                             bool last)
+{
+  struct stm32_ep_s *privep = (struct stm32_ep_s *)ep;
+  uint16_t pma;
+  uint16_t setting;
+  uint16_t maxpacket;
+  uint8_t  epno;
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (!ep || !desc)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      uerr("ERROR: ep=%p desc=%p\n");
+      return -EINVAL;
+    }
+#endif
+
+  /* Get the unadorned endpoint address */
+
+  epno = USB_EPNO(desc->addr);
+  usbtrace(TRACE_EPCONFIGURE, (uint16_t)epno);
+  DEBUGASSERT(epno == USB_EPNO(ep->eplog));
+
+  /* Set the requested type */
+
+  switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK)
+    {
+    case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */
+      setting = USB_EPR_EPTYPE_INTERRUPT;
+      break;
+
+    case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */
+      setting = USB_EPR_EPTYPE_BULK;
+      break;
+
+    case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */
+#warning "REVISIT: Need to review isochronous EP setup"
+      setting = USB_EPR_EPTYPE_ISOC;
+      break;
+
+    case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint */
+      setting = USB_EPR_EPTYPE_CONTROL;
+      break;
+
+    default:
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPTYPE),
+              (uint16_t)desc->type);
+      return -EINVAL;
+    }
+
+  stm32_seteptype(epno, setting);
+
+  /* Get the address of the PMA buffer allocated for this endpoint */
+
+#warning "REVISIT: Should configure BULK EPs using double buffer feature"
+  pma = STM32_BUFNO2BUF(privep->bufno);
+
+  /* Get the maxpacket size of the endpoint. */
+
+  maxpacket = GETUINT16(desc->mxpacketsize);
+  DEBUGASSERT(maxpacket <= STM32_MAXPACKET_SIZE);
+  ep->maxpacket = maxpacket;
+
+  /* Get the subset matching the requested direction */
+
+  if (USB_ISEPIN(desc->addr))
+    {
+      /* The full, logical EP number includes direction */
+
+      ep->eplog = USB_EPIN(epno);
+
+      /* Set up TX; disable RX */
+
+      stm32_seteptxaddr(epno, pma);
+      stm32_seteptxstatus(epno, USB_EPR_STATTX_NAK);
+      stm32_seteprxstatus(epno, USB_EPR_STATRX_DIS);
+    }
+  else
+    {
+      /* The full, logical EP number includes direction */
+
+      ep->eplog = USB_EPOUT(epno);
+
+      /* Set up RX; disable TX */
+
+      stm32_seteprxaddr(epno, pma);
+      stm32_seteprxcount(epno, maxpacket);
+      stm32_seteprxstatus(epno, USB_EPR_STATRX_VALID);
+      stm32_seteptxstatus(epno, USB_EPR_STATTX_DIS);
+    }
+
+  stm32_dumpep(epno);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_epdisable
+ ****************************************************************************/
+
+static int stm32_epdisable(struct usbdev_ep_s *ep)
+{
+  struct stm32_ep_s *privep = (struct stm32_ep_s *)ep;
+  irqstate_t flags;
+  uint8_t epno;
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (!ep)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      uerr("ERROR: ep=%p\n", ep);
+      return -EINVAL;
+    }
+#endif
+
+  epno = USB_EPNO(ep->eplog);
+  usbtrace(TRACE_EPDISABLE, epno);
+
+  /* Cancel any ongoing activity */
+
+  flags = enter_critical_section();
+  stm32_cancelrequests(privep);
+
+  /* Disable TX; disable RX */
+
+  stm32_seteprxcount(epno, 0);
+  stm32_seteprxstatus(epno, USB_EPR_STATRX_DIS);
+  stm32_seteptxstatus(epno, USB_EPR_STATTX_DIS);
+
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_epallocreq
+ ****************************************************************************/
+
+static struct usbdev_req_s *stm32_epallocreq(struct usbdev_ep_s *ep)
+{
+  struct stm32_req_s *privreq;
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (!ep)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return NULL;
+    }
+#endif
+  usbtrace(TRACE_EPALLOCREQ, USB_EPNO(ep->eplog));
+
+  privreq = (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_epfreereq
+ ****************************************************************************/
+
+static void stm32_epfreereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
+{
+  struct stm32_req_s *privreq = (struct stm32_req_s *)req;
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (!ep || !req)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return;
+    }
+#endif
+  usbtrace(TRACE_EPFREEREQ, USB_EPNO(ep->eplog));
+
+  kmm_free(privreq);
+}
+
+/****************************************************************************
+ * Name: stm32_epsubmit
+ ****************************************************************************/
+
+static int stm32_epsubmit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
+{
+  struct stm32_req_s *privreq = (struct stm32_req_s *)req;
+  struct stm32_ep_s *privep = (struct stm32_ep_s *)ep;
+  struct stm32_usbdev_s *priv;
+  irqstate_t flags;
+  uint8_t epno;
+  int ret = OK;
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (!req || !req->callback || !req->buf || !ep)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      uerr("ERROR: req=%p callback=%p buf=%p ep=%p\n",
+           req, req->callback, req->buf, ep);
+      return -EINVAL;
+    }
+#endif
+
+  usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog));
+  priv = privep->dev;
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (!priv->driver)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOTCONFIGURED),
+               priv->usbdev.speed);
+      uerr("ERROR: driver=%p\n", priv->driver);
+      return -ESHUTDOWN;
+    }
+#endif
+
+  /* Handle the request from the class driver */
+
+  epno        = USB_EPNO(ep->eplog);
+  req->result = -EINPROGRESS;
+  req->xfrd   = 0;
+  flags       = enter_critical_section();
+
+  /* If we are stalled, then drop all requests on the floor */
+
+  if (privep->stalled)
+    {
+      stm32_abortrequest(privep, privreq, -EBUSY);
+      uerr("ERROR: stalled\n");
+      ret = -EBUSY;
+    }
+
+  /* Handle IN (device-to-host) requests.  NOTE:  If the class device is
+   * using the bi-directional EP0, then we assume that they intend the EP0
+   * IN functionality.
+   */
+
+  else if (USB_ISEPIN(ep->eplog) || epno == EP0)
+    {
+      /* Add the new request to the request queue for the IN endpoint */
+
+      stm32_rqenqueue(privep, privreq);
+      usbtrace(TRACE_INREQQUEUED(epno), req->len);
+
+      /* If the IN endpoint FIFO is available, then transfer the data now */
+
+      if (!privep->txbusy)
+        {
+          priv->txstatus = USB_EPR_STATTX_NAK;
+          if (epno == EP0)
+            {
+              ret = stm32_wrrequest_ep0(priv, privep);
+            }
+          else
+            {
+              ret = stm32_wrrequest(priv, privep);
+            }
+
+          /* Set the new TX status */
+
+          stm32_seteptxstatus(epno, priv->txstatus);
+        }
+    }
+
+  /* Handle OUT (host-to-device) requests */
+
+  else
+    {
+      /* Add the new request to the request queue for the OUT endpoint */
+
+      privep->txnullpkt = 0;
+      stm32_rqenqueue(privep, privreq);
+      usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
+
+      /* This there a incoming data pending the availability of a request? */
+
+      if (priv->rxpending)
+        {
+          /* Set STAT_RX bits to '11' in the USB_EPnR, enabling further
+           * transactions. "While the STAT_RX bits are equal to '10'
+           * (NAK), any OUT request addressed to that endpoint is NAKed,
+           * indicating a flow control condition: the USB host will retry
+           * the transaction until it succeeds."
+           */
+
+          priv->rxstatus  = USB_EPR_STATRX_VALID;
+          stm32_seteprxstatus(epno, priv->rxstatus);
+
+          /* Data is no longer pending */
+
+          priv->rxpending = false;
+        }
+    }
+
+  leave_critical_section(flags);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32_epcancel
+ ****************************************************************************/
+
+static int stm32_epcancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
+{
+  struct stm32_ep_s *privep = (struct stm32_ep_s *)ep;
+  irqstate_t flags;
+
+#ifdef CONFIG_DEBUG_USB
+  if (!ep || !req)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return -EINVAL;
+    }
+#endif
+  usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog));
+
+  flags = enter_critical_section();
+  stm32_cancelrequests(privep);
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_epstall
+ ****************************************************************************/
+
+static int stm32_epstall(struct usbdev_ep_s *ep, bool resume)
+{
+  struct stm32_ep_s *privep;
+  struct stm32_usbdev_s *priv;
+  uint8_t epno;
+  uint16_t status;
+  irqstate_t flags;
+
+#ifdef CONFIG_DEBUG_USB
+  if (!ep)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return -EINVAL;
+    }
+#endif
+
+  privep = (struct stm32_ep_s *)ep;
+  priv   = (struct stm32_usbdev_s *)privep->dev;
+  epno   = USB_EPNO(ep->eplog);
+
+  /* STALL or RESUME the endpoint */
+
+  flags = enter_critical_section();
+  usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, USB_EPNO(ep->eplog));
+
+  /* Get status of the endpoint; stall the request if the endpoint is
+   * disabled
+   */
+
+  if (USB_ISEPIN(ep->eplog))
+    {
+      status = stm32_geteptxstatus(epno);
+    }
+  else
+    {
+      status = stm32_geteprxstatus(epno);
+    }
+
+  if (status == 0)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPDISABLED), 0);
+
+      if (epno == 0)
+        {
+          priv->ep0state = EP0STATE_STALLED;
+        }
+
+      leave_critical_section(flags);
+      return -ENODEV;
+    }
+
+  /* Handle the resume condition */
+
+  if (resume)
+    {
+      /* Resuming a stalled endpoint */
+
+      usbtrace(TRACE_EPRESUME, epno);
+      privep->stalled = false;
+
+      if (USB_ISEPIN(ep->eplog))
+        {
+          /* IN endpoint */
+
+          if (stm32_eptxstalled(epno))
+            {
+              stm32_clrtxdtog(epno);
+
+              /* Restart any queued write requests */
+
+              priv->txstatus = USB_EPR_STATTX_NAK;
+              if (epno == EP0)
+                {
+                  stm32_wrrequest_ep0(priv, privep);
+                }
+              else
+                {
+                  stm32_wrrequest(priv, privep);
+                }
+
+              /* Set the new TX status */
+
+              stm32_seteptxstatus(epno, priv->txstatus);
+            }
+        }
+      else
+        {
+          /* OUT endpoint */
+
+          if (stm32_eprxstalled(epno))
+            {
+              if (epno == EP0)
+                {
+                  /* After clear the STALL, enable the default endpoint
+                   * receiver
+                   */
+
+                  stm32_seteprxcount(epno, ep->maxpacket);
+                }
+              else
+                {
+                  stm32_clrrxdtog(epno);
+                }
+
+              priv->rxstatus = USB_EPR_STATRX_VALID;
+              stm32_seteprxstatus(epno, USB_EPR_STATRX_VALID);
+            }
+        }
+    }
+
+  /* Handle the stall condition */
+
+  else
+    {
+      usbtrace(TRACE_EPSTALL, epno);
+      privep->stalled = true;
+
+      if (USB_ISEPIN(ep->eplog))
+        {
+          /* IN endpoint */
+
+          priv->txstatus = USB_EPR_STATTX_STALL;
+          stm32_seteptxstatus(epno, USB_EPR_STATTX_STALL);
+        }
+      else
+        {
+          /* OUT endpoint */
+
+          priv->rxstatus = USB_EPR_STATRX_STALL;
+          stm32_seteprxstatus(epno, USB_EPR_STATRX_STALL);
+        }
+    }
+
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Device Controller Operations
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_allocep
+ ****************************************************************************/
+
+static struct usbdev_ep_s *stm32_allocep(struct usbdev_s *dev, uint8_t epno,
+                                         bool in, uint8_t eptype)
+{
+  struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev;
+  struct stm32_ep_s *privep = NULL;
+  uint8_t epset = STM32_ENDP_ALLSET;
+  int bufno;
+
+  usbtrace(TRACE_DEVALLOCEP, (uint16_t)epno);
+#ifdef CONFIG_DEBUG_USB
+  if (!dev)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return NULL;
+    }
+#endif
+
+  /* Ignore any direction bits in the logical address */
+
+  epno = USB_EPNO(epno);
+
+  /* A logical address of 0 means that any endpoint will do */
+
+  if (epno > 0)
+    {
+      /* Otherwise, we will return the endpoint structure only for the
+       * requested 'logical' endpoint.
+       *  All of the other checks will still be performed.
+       *
+       * First, verify that the logical endpoint is in the range supported by
+       * by the hardware.
+       */
+
+      if (epno >= STM32_NENDPOINTS)
+        {
+          usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16_t)epno);
+          return NULL;
+        }
+
+      /* Convert the logical address to a physical OUT endpoint address and
+       * remove all of the candidate endpoints from the bitset except for the
+       * the IN/OUT pair for this logical address.
+       */
+
+      epset = STM32_ENDP_BIT(epno);
+    }
+
+  /* Check if the selected endpoint number is available */
+
+  privep = stm32_epreserve(priv, epset);
+  if (!privep)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPRESERVE), (uint16_t)epset);
+      goto errout;
+    }
+
+  /* Allocate a PMA buffer for this endpoint */
+
+#warning "REVISIT: Should configure BULK EPs using double buffer feature"
+  bufno = stm32_epallocpma(priv);
+  if (bufno < 0)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPBUFFER), 0);
+      goto errout_with_ep;
+    }
+
+  privep->bufno = (uint8_t)bufno;
+  return &privep->ep;
+
+errout_with_ep:
+  stm32_epunreserve(priv, privep);
+errout:
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: stm32_freeep
+ ****************************************************************************/
+
+static void stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep)
+{
+  struct stm32_usbdev_s *priv;
+  struct stm32_ep_s *privep;
+
+#ifdef CONFIG_DEBUG_USB
+  if (!dev || !ep)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return;
+    }
+#endif
+  priv   = (struct stm32_usbdev_s *)dev;
+  privep = (struct stm32_ep_s *)ep;
+  usbtrace(TRACE_DEVFREEEP, (uint16_t)USB_EPNO(ep->eplog));
+
+  if (priv && privep)
+    {
+      /* Free the PMA buffer assigned to this endpoint */
+
+      stm32_epfreepma(priv, privep);
+
+      /* Mark the endpoint as available */
+
+      stm32_epunreserve(priv, privep);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_getframe
+ ****************************************************************************/
+
+static int stm32_getframe(struct usbdev_s *dev)
+{
+  uint16_t fnr;
+
+#ifdef CONFIG_DEBUG_USB
+  if (!dev)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return -EINVAL;
+    }
+#endif
+
+  /* Return the last frame number detected by the hardware */
+
+  fnr = stm32_getreg(STM32_USB_FNR);
+  usbtrace(TRACE_DEVGETFRAME, fnr);
+  return (fnr & USB_FNR_FN_MASK);
+}
+
+/****************************************************************************
+ * Name: stm32_wakeup
+ ****************************************************************************/
+
+static int stm32_wakeup(struct usbdev_s *dev)
+{
+  struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev;
+  irqstate_t flags;
+
+  usbtrace(TRACE_DEVWAKEUP, 0);
+#ifdef CONFIG_DEBUG_USB
+  if (!dev)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return -EINVAL;
+    }
+#endif
+
+  /* Start the resume sequence.  The actual resume steps will be driven
+   * by the ESOF interrupt.
+   */
+
+  flags = enter_critical_section();
+  stm32_initresume(priv);
+  priv->rsmstate = RSMSTATE_STARTED;
+
+  /* Disable the SUSP interrupt (until we are fully resumed), disable
+   * the WKUP interrupt (we are already waking up), and enable the
+   * ESOF interrupt that will drive the resume operations.  Clear any
+   * pending ESOF interrupt.
+   */
+
+  stm32_setimask(priv, USB_CNTR_ESOFM, USB_CNTR_WKUPM | USB_CNTR_SUSPM);
+  stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR);
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_selfpowered
+ ****************************************************************************/
+
+static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered)
+{
+  struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev;
+
+  usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered);
+
+#ifdef CONFIG_DEBUG_USB
+  if (!dev)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
+      return -ENODEV;
+    }
+#endif
+
+  priv->selfpowered = selfpowered;
+  return OK;
+}
+
+/****************************************************************************
+ * Initialization/Reset
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_reset
+ ****************************************************************************/
+
+static void stm32_reset(struct stm32_usbdev_s *priv)
+{
+  int epno;
+
+  /* Put the USB controller in reset, disable all interrupts */
+
+  stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
+
+  /* Tell the class driver that we are disconnected.  The class driver
+   * should then accept any new configurations.
+   */
+
+  CLASS_DISCONNECT(priv->driver, &priv->usbdev);
+
+  /* Reset the device state structure */
+
+  priv->ep0state  = EP0STATE_IDLE;
+  priv->rsmstate  = RSMSTATE_IDLE;
+  priv->rxpending = false;
+
+  /* Reset endpoints */
+
+  for (epno = 0; epno < STM32_NENDPOINTS; epno++)
+    {
+      struct stm32_ep_s *privep = &priv->eplist[epno];
+
+      /* Cancel any queued requests.  Since they are canceled
+       * with status -ESHUTDOWN, then will not be requeued
+       * until the configuration is reset.  NOTE:  This should
+       * not be necessary... the CLASS_DISCONNECT above should
+       * result in the class implementation calling stm32_epdisable
+       * for each of its configured endpoints.
+       */
+
+      stm32_cancelrequests(privep);
+
+      /* Reset endpoint status */
+
+      privep->stalled   = false;
+      privep->halted    = false;
+      privep->txbusy    = false;
+      privep->txnullpkt = false;
+    }
+
+  /* Re-configure the USB controller in its initial, unconnected state */
+
+  stm32_hwreset(priv);
+  priv->usbdev.speed = USB_SPEED_FULL;
+}
+
+/****************************************************************************
+ * Name: stm32_hwreset
+ ****************************************************************************/
+
+static void stm32_hwreset(struct stm32_usbdev_s *priv)
+{
+  /* Put the USB controller into reset, clear all interrupt enables */
+
+  stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
+
+  /* Disable interrupts (and perhaps take the USB controller out of reset) */
+
+  priv->imask = 0;
+  stm32_putreg(priv->imask, STM32_USB_CNTR);
+
+  /* Set the STM32 BTABLE address */
+
+  stm32_putreg(STM32_BTABLE_ADDRESS & 0xfff8, STM32_USB_BTABLE);
+
+  /* Initialize EP0 */
+
+  stm32_seteptype(EP0, USB_EPR_EPTYPE_CONTROL);
+  stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK);
+  stm32_seteprxaddr(EP0, STM32_EP0_RXADDR);
+  stm32_seteprxcount(EP0, STM32_EP0MAXPACKET);
+  stm32_seteptxaddr(EP0, STM32_EP0_TXADDR);
+  stm32_clrstatusout(EP0);
+  stm32_seteprxstatus(EP0, USB_EPR_STATRX_VALID);
+
+  /* Set the device to respond on default address */
+
+  stm32_setdevaddr(priv, 0);
+
+  /* Clear any pending interrupts */
+
+  stm32_putreg(0, STM32_USB_ISTR);
+
+  /* Enable interrupts at the USB controller */
+
+  stm32_setimask(priv, STM32_CNTR_SETUP,
+                (USB_CNTR_ALLINTS & ~STM32_CNTR_SETUP));
+  stm32_dumpep(EP0);
+}
+
+/****************************************************************************
+ * Name: stm32_hwsetup
+ ****************************************************************************/
+
+static void stm32_hwsetup(struct stm32_usbdev_s *priv)
+{
+  int epno;
+
+  /* Power the USB controller, put the USB controller into reset, disable
+   * all USB interrupts
+   */
+
+  stm32_putreg(USB_CNTR_FRES | USB_CNTR_PDWN, STM32_USB_CNTR);
+
+  /* Disconnect the device / disable the pull-up.  We don't want the
+   * host to enumerate us until the class driver is registered.
+   */
+
+  stm32_usbpullup(&priv->usbdev, false);
+
+  /* Initialize the device state structure.  NOTE: many fields
+   * have the initial value of zero and, hence, are not explicitly
+   * initialized here.
+   */
+
+  memset(priv, 0, sizeof(struct stm32_usbdev_s));
+  priv->usbdev.ops   = &g_devops;
+  priv->usbdev.ep0   = &priv->eplist[EP0].ep;
+  priv->epavail      = STM32_ENDP_ALLSET & ~STM32_ENDP_BIT(EP0);
+  priv->bufavail     = STM32_BUFFER_ALLSET & ~STM32_BUFFER_EP0;
+
+  /* Initialize the endpoint list */
+
+  for (epno = 0; epno < STM32_NENDPOINTS; epno++)
+    {
+      /* Set endpoint operations, reference to driver structure (not
+       * really necessary because there is only one controller), and
+       * the (physical) endpoint number which is just the index to the
+       * endpoint.
+       */
+
+      priv->eplist[epno].ep.ops    = &g_epops;
+      priv->eplist[epno].dev       = priv;
+      priv->eplist[epno].ep.eplog  = epno;
+
+      /* We will use a fixed maxpacket size for all endpoints (perhaps
+       * ISOC endpoints could have larger maxpacket???).  A smaller
+       * packet size can be selected when the endpoint is configured.
+       */
+
+      priv->eplist[epno].ep.maxpacket = STM32_MAXPACKET_SIZE;
+    }
+
+  /* Select a smaller endpoint size for EP0 */
+
+#if STM32_EP0MAXPACKET < STM32_MAXPACKET_SIZE
+  priv->eplist[EP0].ep.maxpacket = STM32_EP0MAXPACKET;
+#endif
+
+  /* Configure the USB controller.  USB uses the following GPIO pins:
+   *
+   *   PA9  - VBUS
+   *   PA10 - ID
+   *   PA11 - DM
+   *   PA12 - DP
+   *
+   * "As soon as the USB is enabled, these pins [DM and DP] are connected to
+   * the USB internal transceiver automatically."
+   */
+
+  /* Power up the USB controller, holding it in reset.  There is a delay of
+   * about 1uS after applying power before the USB will behave predictably.
+   * A 5MS delay is more than enough.  NOTE that we leave the USB controller
+   * in the reset state; the hardware will not be initialized until the
+   * class driver has been bound.
+   */
+
+  stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
+  up_mdelay(5);
+}
+
+/****************************************************************************
+ * Name: stm32_hwshutdown
+ ****************************************************************************/
+
+static void stm32_hwshutdown(struct stm32_usbdev_s *priv)
+{
+  priv->usbdev.speed = USB_SPEED_UNKNOWN;
+
+  /* Disable all interrupts and force the USB controller into reset */
+
+  stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
+
+  /* Clear any pending interrupts */
+
+  stm32_putreg(0, STM32_USB_ISTR);
+
+  /* Disconnect the device / disable the pull-up */
+
+  stm32_usbpullup(&priv->usbdev, false);
+
+  /* Power down the USB controller */
+
+  stm32_putreg(USB_CNTR_FRES | USB_CNTR_PDWN, STM32_USB_CNTR);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: arm_usbinitialize
+ * Description:
+ *   Initialize the USB driver
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void arm_usbinitialize(void)
+{
+  /* For now there is only one USB controller, but we will always refer to
+   * it using a pointer to make any future ports to multiple USB controllers
+   * easier.
+   */
+
+  struct stm32_usbdev_s *priv = &g_usbdev;
+
+  usbtrace(TRACE_DEVINIT, 0);
+  stm32_checksetup();
+
+  /* Configure USB GPIO alternate function pins */
+
+#ifdef CONFIG_STM32_STM32F30XX
+  stm32_configgpio(GPIO_USB_DM);
+  stm32_configgpio(GPIO_USB_DP);
+#endif
+
+  /* Power up the USB controller, but leave it in the reset state */
+
+  stm32_hwsetup(priv);
+
+  /* Remap the USB interrupt as needed
+   * (Only supported by the STM32 F3 family)
+   */
+
+#ifdef CONFIG_STM32_STM32F30XX
+#  ifdef CONFIG_STM32_USB_ITRMP
+  /* Clear the ITRMP bit to use the legacy, shared USB/CAN interrupts */
+
+  modifyreg32(STM32_RCC_APB1ENR, SYSCFG_CFGR1_USB_ITRMP, 0);
+#  else
+  /* Set the ITRMP bit to use the STM32 F3's dedicated USB interrupts */
+
+  modifyreg32(STM32_RCC_APB1ENR, 0, SYSCFG_CFGR1_USB_ITRMP);
+#  endif
+#endif
+
+  /* Attach USB controller interrupt handlers.  The hardware will not be
+   * initialized and interrupts will not be enabled until the class device
+   * driver is bound.  Getting the IRQs here only makes sure that we have
+   * them when we need them later.
+   */
+
+  if (irq_attach(STM32_IRQ_USBHP, stm32_hpinterrupt, NULL) != 0)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION),
+               (uint16_t)STM32_IRQ_USBHP);
+      goto errout;
+    }
+
+  if (irq_attach(STM32_IRQ_USBLP, stm32_lpinterrupt, NULL) != 0)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION),
+               (uint16_t)STM32_IRQ_USBLP);
+      goto errout;
+    }
+
+  return;
+
+errout:
+  arm_usbuninitialize();
+}
+
+/****************************************************************************
+ * Name: arm_usbuninitialize
+ * Description:
+ *   Initialize the USB driver
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void arm_usbuninitialize(void)
+{
+  /* For now there is only one USB controller, but we will always refer to
+   * it using a pointer to make any future ports to multiple USB controllers
+   * easier.
+   */
+
+  struct stm32_usbdev_s *priv = &g_usbdev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  usbtrace(TRACE_DEVUNINIT, 0);
+
+  /* Disable and detach the USB IRQs */
+
+  up_disable_irq(STM32_IRQ_USBHP);
+  up_disable_irq(STM32_IRQ_USBLP);
+  irq_detach(STM32_IRQ_USBHP);
+  irq_detach(STM32_IRQ_USBLP);
+
+  if (priv->driver)
+    {
+      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0);
+      usbdev_unregister(priv->driver);
+    }
+
+  /* Put the hardware in an inactive state */
+
+  stm32_hwshutdown(priv);
+  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)
+{
+  /* For now there is only one USB controller, but we will always refer to
+   * it using a pointer to make any future ports to multiple USB controllers
+   * easier.
+   */
+
+  struct stm32_usbdev_s *priv = &g_usbdev;
+  int ret;
+
+  usbtrace(TRACE_DEVREGISTER, 0);
+
+#ifdef CONFIG_DEBUG_USB
+  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);
+    }
+  else
+    {
+      /* Setup the USB controller -- enabling interrupts at the USB
+       * controller
+       */
+
+      stm32_hwreset(priv);
+
+      /* Enable USB controller interrupts at the NVIC */
+
+      up_enable_irq(STM32_IRQ_USBHP);
+      up_enable_irq(STM32_IRQ_USBLP);
+
+      /* Enable pull-up to connect the device.  The host should enumerate us
+       * some time after this
+       */
+
+      stm32_usbpullup(&priv->usbdev, true);
+      priv->usbdev.speed = USB_SPEED_FULL;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: usbdev_unregister
+ *
+ * Description:
+ *   Un-register usbdev class driver. If the USB device is connected to a
+ *   USB host, it will first disconnect().  The driver is also requested to
+ *   unbind() and clean up any device state, before this procedure finally
+ *   returns.
+ *
+ ****************************************************************************/
+
+int usbdev_unregister(struct usbdevclass_driver_s *driver)
+{
+  /* For now there is only one USB controller, but we will always refer to
+   * it using a pointer to make any future ports to multiple USB controllers
+   * easier.
+   */
+
+  struct stm32_usbdev_s *priv = &g_usbdev;
+  irqstate_t flags;
+
+  usbtrace(TRACE_DEVUNREGISTER, 0);
+
+#ifdef CONFIG_DEBUG_USB
+  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_reset(priv);
+
+  /* Unbind the class driver */
+
+  CLASS_UNBIND(driver, &priv->usbdev);
+
+  /* Disable USB controller interrupts (but keep them attached) */
+
+  up_disable_irq(STM32_IRQ_USBHP);
+  up_disable_irq(STM32_IRQ_USBLP);
+
+  /* Put the hardware in an inactive state.  Then bring the hardware back up
+   * in the reset state (this is probably not necessary, the stm32_reset()
+   * call above was probably sufficient).
+   */
+
+  stm32_hwshutdown(priv);
+  stm32_hwsetup(priv);
+
+  /* Unhook the driver */
+
+  priv->driver = NULL;
+  leave_critical_section(flags);
+  return OK;
+}
+
+#endif /* CONFIG_USBDEV && CONFIG_STM32_USB */
diff --git a/arch/arm/src/stm32/stm32_usbfs.h b/arch/arm/src/stm32/stm32_usbfs.h
new file mode 100644
index 0000000000..f5adfcb9f1
--- /dev/null
+++ b/arch/arm/src/stm32/stm32_usbfs.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+ * arch/arm/src/stm32/stm32_usbdev.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32_STM32_USBDEV_H
+#define __ARCH_ARM_SRC_STM32_STM32_USBDEV_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/usb/usbdev.h>
+#include <stdint.h>
+
+#include "chip.h"
+#include "hardware/stm32_usbdev.h"
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name:  stm32_usbpullup
+ *
+ * Description:
+ *   If USB is supported and the board supports a pullup via GPIO (for USB
+ *   software connect and disconnect), then the board software must provide
+ *   stm32_pullup. See include/nuttx/usb/usbdev.h for additional description
+ *   of this method.
+ *
+ ****************************************************************************/
+
+int stm32_usbpullup(struct usbdev_s *dev,  bool enable);
+
+/****************************************************************************
+ * Name:  stm32_usbsuspend
+ *
+ * Description:
+ *   Board logic must provide the stm32_usbsuspend logic if the USBDEV driver
+ *   is used.  This function is called whenever the USB enters or leaves
+ *   suspend mode. This is an opportunity for the board logic to shutdown
+ *   clocks, power, etc. while the USB is suspended.
+ *
+ ****************************************************************************/
+
+void stm32_usbsuspend(struct usbdev_s *dev, bool resume);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_ARM_SRC_STM32_STM32_USBDEV_H */