Преглед на файлове

USB-OTG FS driver added (Device mode only). Tested on STM32L476RG
HW capabilities fags added
fixed incorrect signedness for ep-read and ep-write return values
cdc-loop routine rewrited

Dmitry преди 9 години
родител
ревизия
52bd70a52f
променени са 15 файла, в които са добавени 680 реда и са изтрити 71 реда
  1. 6 1
      Makefile
  2. 72 52
      demo/cdc_loop.c
  3. 1 2
      demo/stm32l052x8.ld
  4. 2 3
      demo/stm32l100xc.ld
  5. 92 0
      demo/stm32l476xg.ld
  6. 9 2
      inc/usbd_core.h
  7. 17 0
      macro.h
  8. 4 3
      readme.md
  9. 1 2
      src/usb_32v0.c
  10. 3 1
      src/usb_32v0A.S
  11. 1 2
      src/usb_32v1.c
  12. 3 1
      src/usb_32v1A.S
  13. 458 0
      src/usb_32v2.c
  14. 6 2
      src/usbd_core.c
  15. 5 0
      usb.h

+ 6 - 1
Makefile

@@ -17,6 +17,12 @@ CFLAGS.stm32l100xc   = -mcpu=cortex-m3 -mfloat-abi=soft
 DEFINES.stm32l100xc  = STM32L1 STM32L100xC
 LDSCRIPT.stm32l100xc = demo/stm32l100xc.ld
 
+STARTUP.stm32l476rg  = $(CMSIS)/device/ST/STM32L4xx/Source/Templates/gcc/startup_stm32l476xx.s
+CFLAGS.stm32l476rg   = -mcpu=cortex-m4
+DEFINES.stm32l476rg  = STM32L4 STM32L476xx
+LDSCRIPT.stm32l476rg = demo/stm32l476xg.ld
+
+
 MCU         ?= stm32l100xc
 LDFLAGS     ?= --specs=nano.specs -nostartfiles -Wl,--gc-sections
 DSRC         = $(wildcard demo/*.c) $(wildcard demo/*.S) $(STARTUP.$(MCU))
@@ -49,7 +55,6 @@ program: $(DOUT).hex
 
 demo: clean $(DOUT).hex
 
-
 $(DOUT).hex : $(DOUT).elf
 	@echo building $@
 	@$(OBJCOPY) -O ihex $< $@

+ 72 - 52
demo/cdc_loop.c

@@ -1,6 +1,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <string.h>
+#include "macro.h"
 #include "stm32.h"
 #include "usb.h"
 #include "inc/usb_cdc.h"
@@ -140,7 +141,8 @@ static const struct usb_string_descriptor *const dtable[] = {
 
 usbd_device udev;
 uint32_t	ubuf[0x20];
-uint8_t     dbuf[0x100];
+uint8_t     fifo[0x200];
+uint32_t    fpos = 0;
 
 static struct usb_cdc_line_coding cdc_line = {
     .dwDTERate          = 38400,
@@ -200,49 +202,31 @@ static usbd_respond cdc_control(usbd_device *dev, usbd_ctlreq *req, usbd_rqc_cal
 
 
 static void cdc_rxonly (usbd_device *dev, uint8_t event, uint8_t ep) {
-    usbd_ep_read(dev, ep, dbuf, CDC_DATA_SZ);
+   usbd_ep_read(dev, ep, fifo, CDC_DATA_SZ);
 }
 
 static void cdc_txonly(usbd_device *dev, uint8_t event, uint8_t ep) {
     uint8_t _t = dev->driver->frame_no();
-    memset(dbuf, _t, CDC_DATA_SZ);   
-    usbd_ep_write(dev, ep, dbuf, CDC_DATA_SZ);
-
+    memset(fifo, _t, CDC_DATA_SZ);
+    usbd_ep_write(dev, ep, fifo, CDC_DATA_SZ);
 }
 
-
-static void cdc_loopback (usbd_device *dev, uint8_t event, uint8_t ep) {
-#define WAIT_TX     0x01
-#define WAIT_RX     0x02
-    static uint16_t rpos = 0;
-    static uint8_t  flags = WAIT_RX;
+static void cdc_loopback(usbd_device *dev, uint8_t event, uint8_t ep) {
     int _t;
     switch (event) {
-read_delayed:
+    case usbd_evt_eptx:
+        _t = usbd_ep_write(dev, CDC_TXD_EP, &fifo[0], (fpos < CDC_DATA_SZ) ? fpos : CDC_DATA_SZ);
+        if (_t > 0) {
+            memmove(&fifo[0], &fifo[_t], fpos - _t);
+            fpos -= _t;
+        }
     case usbd_evt_eprx:
-        if (rpos > (sizeof(dbuf) - CDC_DATA_SZ)) {
-            /* buffer overflow. stop accepting data */
-            flags &= ~WAIT_RX;
-        } else {
-            _t = usbd_ep_read(dev, CDC_RXD_EP, &dbuf[rpos], CDC_DATA_SZ);
+        if (fpos < (sizeof(fifo) - CDC_DATA_SZ)) {
+            _t = usbd_ep_read(dev, CDC_RXD_EP, &fifo[fpos], CDC_DATA_SZ);
             if (_t > 0) {
-                rpos += _t;
+                fpos += _t;
             }
         }
-        if (flags & WAIT_TX) break;
-        flags |= WAIT_TX;
-    case usbd_evt_eptx:
-        if (rpos > 0) {
-            _t = (rpos > CDC_DATA_SZ) ? CDC_DATA_SZ : rpos;
-            _t = usbd_ep_write(dev, CDC_TXD_EP, &dbuf[0], _t);
-            memmove(&dbuf[0], &dbuf[_t], (rpos - _t));
-            rpos -= _t;
-        } else {
-            flags &= ~WAIT_TX;
-            if (flags & WAIT_RX) break;
-            flags |= WAIT_RX;
-            goto read_delayed;
-        }
     default:
         break;
     }
@@ -252,9 +236,9 @@ static usbd_respond cdc_setconf (usbd_device *dev, uint8_t cfg) {
     switch (cfg) {
     case 0:
         /* deconfiguring device */
-        usbd_ep_deconfig(dev, CDC_RXD_EP);
-        usbd_ep_deconfig(dev, CDC_TXD_EP);
         usbd_ep_deconfig(dev, CDC_NTF_EP);
+        usbd_ep_deconfig(dev, CDC_TXD_EP);
+        usbd_ep_deconfig(dev, CDC_RXD_EP);
         usbd_reg_endpoint(dev, CDC_RXD_EP, 0);
         usbd_reg_endpoint(dev, CDC_TXD_EP, 0);
         return usbd_ack;
@@ -269,8 +253,8 @@ static usbd_respond cdc_setconf (usbd_device *dev, uint8_t cfg) {
 #else
         usbd_reg_endpoint(dev, CDC_RXD_EP, cdc_rxonly);
         usbd_reg_endpoint(dev, CDC_TXD_EP, cdc_txonly);
-        usbd_ep_write(dev, CDC_TXD_EP, 0, 0);
 #endif
+        usbd_ep_write(dev, CDC_TXD_EP, 0, 0);
         return usbd_ack;
     default:
         return usbd_fail;
@@ -284,26 +268,63 @@ static void cdc_init_usbd(void) {
     usbd_reg_descr(&udev, cdc_getdesc);
 }
 
-static void cdc_init_rcc(void) {
-    BST(RCC->APB1ENR, RCC_APB1ENR_PWREN);
-    BMD(PWR->CR, PWR_CR_VOS, PWR_CR_VOS_0);
-    WBC(PWR->CSR, PWR_CSR_VOSF);
+
+static void cdc_init_rcc (void) {
+#if defined(STM32L0)
+    _BST(RCC->APB1ENR, RCC_APB1ENR_PWREN);
+    _BMD(PWR->CR, PWR_CR_VOS, PWR_CR_VOS_0);
+    _WBC(PWR->CSR, PWR_CSR_VOSF);
     /* set FLASH latency to 1 */
-#if defined(STM32L1)
-    BST(FLASH->ACR, FLASH_ACR_ACC64);
-#endif
-    BST(FLASH->ACR, FLASH_ACR_LATENCY);
-    /* set PLL clock HSI * 6/4 (24 MHz) */
-    BMD(RCC->CFGR, RCC_CFGR_PLLDIV | RCC_CFGR_PLLMUL | RCC_CFGR_PLLSRC, RCC_CFGR_PLLDIV3 | RCC_CFGR_PLLMUL6);
-    BST(RCC->CR, RCC_CR_HSION);
-    WBS(RCC->CR, RCC_CR_HSIRDY);
-    BST(RCC->CR, RCC_CR_PLLON);
-    WBS(RCC->CR, RCC_CR_PLLRDY);
+    _BST(FLASH->ACR, FLASH_ACR_LATENCY);
+    /* set clock at 32MHz PLL 6/3 HSI */
+    _BMD(RCC->CFGR, RCC_CFGR_PLLDIV | RCC_CFGR_PLLMUL | RCC_CFGR_PLLSRC, RCC_CFGR_PLLDIV3 | RCC_CFGR_PLLMUL6);
+    _BST(RCC->CR, RCC_CR_HSION);
+    _WBS(RCC->CR, RCC_CR_HSIRDY);
+    _BST(RCC->CR, RCC_CR_PLLON);
+    _WBS(RCC->CR, RCC_CR_PLLRDY);
+    /* switch clock to PLL */
+    _BMD(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
+    _WVL(RCC->CFGR, RCC_CFGR_SWS, RCC_CFGR_SWS_PLL);
+
+#elif defined(STM32L1)
+    _BST(RCC->APB1ENR, RCC_APB1ENR_PWREN);
+    _BMD(PWR->CR, PWR_CR_VOS, PWR_CR_VOS_0);
+    _WBC(PWR->CSR, PWR_CSR_VOSF);
+    /* set FLASH latency to 1 */
+    _BST(FLASH->ACR, FLASH_ACR_ACC64);
+    _BST(FLASH->ACR, FLASH_ACR_LATENCY);
+    /* set clock at 32 MHz PLL 6/3 HSI */
+    _BMD(RCC->CFGR, RCC_CFGR_PLLDIV | RCC_CFGR_PLLMUL | RCC_CFGR_PLLSRC, RCC_CFGR_PLLDIV3 | RCC_CFGR_PLLMUL6);
+    _BST(RCC->CR, RCC_CR_HSION);
+    _WBS(RCC->CR, RCC_CR_HSIRDY);
+    _BST(RCC->CR, RCC_CR_PLLON);
+    _WBS(RCC->CR, RCC_CR_PLLRDY);
     /* switch clock to PLL */
-    BMD(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
-    WVL(RCC->CFGR, RCC_CFGR_SWS, RCC_CFGR_SWS_PLL);
+    _BMD(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
+    _WVL(RCC->CFGR, RCC_CFGR_SWS, RCC_CFGR_SWS_PLL);
+
+#elif defined(STM32L4)
+    _BST(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
+    /* Set power Range 1 */
+    _BMD(PWR->CR1, PWR_CR1_VOS, PWR_CR1_VOS_1);
+    _WBC(PWR->SR2, PWR_SR2_VOSF);
+    /* Adjust Flash latency */
+    _BST(FLASH->ACR, FLASH_ACR_LATENCY);
+    /* set clock 48Mhz MSI */
+    _BMD(RCC->CR, RCC_CR_MSIRANGE, RCC_CR_MSIRANGE_11 | RCC_CR_MSIRGSEL);
+    /* set MSI as 48MHz USB */
+    _BMD(RCC->CCIPR, RCC_CCIPR_CLK48SEL, RCC_CCIPR_CLK48SEL_0 | RCC_CCIPR_CLK48SEL_1);
+    /* enable GPIOA clock */
+    _BST(RCC->AHB2ENR, RCC_AHB2ENR_GPIOAEN);
+    /* set GP11 and GP12 as USB data pins AF10 */
+    _BST(GPIOA->AFR[1], (0x0A << 12) | (0x0A << 16));
+    _BMD(GPIOA->MODER, (0x03 << 22) | (0x03 << 24), (0x02 << 22) | (0x02 << 24));
+#else
+    #error Not supported
+#endif
 }
 
+
 void __libc_init_array(void) {
 
 }
@@ -312,7 +333,6 @@ void SystemInit(void) {
     cdc_init_rcc();
 }
 
-
 void main(void) {
     cdc_init_usbd();
     usbd_control(&udev, usbd_cmd_enable);

+ 1 - 2
demo/stm32l052x8.ld

@@ -3,7 +3,6 @@ MEMORY
 {
 	ROM  (rx): ORIGIN = 0x08000000, LENGTH =  64K
 	RAM (rwx): ORIGIN = 0x20000000, LENGTH =  8K
-	EEP  (rw): ORIGIN = 0x08080000, LENGTH =  2K
 }
 SECTIONS
 {
@@ -70,7 +69,7 @@ SECTIONS
 		__end__ = .;
 		*(.heap*)
 		__HeapLimit = .;
-	} > RAM 
+	} > RAM
 	.stack_dummy (NOLOAD) :
 	{
 		*(.stack)

+ 2 - 3
demo/stm32l100xc.ld

@@ -3,7 +3,6 @@ MEMORY
 {
 	ROM  (rx): ORIGIN = 0x08000000, LENGTH =  192K
 	RAM (rwx): ORIGIN = 0x20000000, LENGTH =  20K
-	EEP  (rw): ORIGIN = 0x08080000, LENGTH =  6K
 }
 SECTIONS
 {
@@ -70,7 +69,7 @@ SECTIONS
 		__end__ = .;
 		*(.heap*)
 		__HeapLimit = .;
-	} > RAM 
+	} > RAM
 	.stack_dummy (NOLOAD) :
 	{
 		*(.stack)
@@ -80,7 +79,7 @@ SECTIONS
 	__StackLimit = __StackTop - SIZEOF(.stack_dummy);
 	PROVIDE(__stack = __StackTop);
 	ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
-	
+
 	PROVIDE(_estack = __stack);
 	PROVIDE(_sidata = __etext);
 	PROVIDE(_sdata = __data_start__);

+ 92 - 0
demo/stm32l476xg.ld

@@ -0,0 +1,92 @@
+ENTRY(Reset_Handler)
+MEMORY
+{
+	ROM    (rx): ORIGIN = 0x08000000, LENGTH = 1024K
+	BANK0  (rx): ORIGIN = 0x08000000, LENGTH = 512K
+	BANK1  (rx): ORIGIN = 0x08080000, LENGTH = 512K
+	RAM   (rwx): ORIGIN = 0x20000000, LENGTH = 96K
+	RAM2  (rwx): ORIGIN = 0x10000000, LENGTH = 32K
+}
+SECTIONS
+{
+	.text :
+	{
+		KEEP(*(.isr_vector))
+		*(.text*)
+		KEEP(*(.init))  KEEP(*(.fini))
+		*crtbegin.o(.ctors)
+		*crtbegin?.o(.ctors)
+		*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
+		*(SORT(.ctors.*))
+		*(.ctors)
+		*crtbegin.o(.dtors)
+		*crtbegin?.o(.dtors)
+		*(EXCLUDE_FILE(*crtend?.o *crtend.o).dtors)
+		*(SORT(.dtors.*))
+		*(.dtors)
+		*(.rodata*)
+		KEEP(*(.eh_frame*))
+	} > ROM
+    .ARM.extab :
+    {
+    	*(.ARM.extab* .gnu.linkonce.armextab.*)
+    } > ROM
+	__exidx_start = .;
+	.ARM.exidx :
+	{
+		*(.ARM.exidx* .gnu.linkonce.armexidx.*)
+	} > ROM
+	__exidx_end = .;
+	__etext = .;
+	.data : AT (__etext)
+	{
+		__data_start__ = .;
+		*(vtable)
+		*(.data*)
+		. = ALIGN(4);
+		PROVIDE_HIDDEN (__preinit_array_start = .);
+		KEEP(*(.preinit_array))
+		PROVIDE_HIDDEN (__preinit_array_end = .);
+		. = ALIGN(4);
+		PROVIDE_HIDDEN (__init_array_start = .);
+		KEEP(*(SORT(.init_array.*)))
+		KEEP(*(.init_array))
+		PROVIDE_HIDDEN (__init_array_end = .);
+		. = ALIGN(4);
+		PROVIDE_HIDDEN (__fini_array_start = .);
+		KEEP(*(SORT(.fini_array.*)))
+		KEEP(*(.fini_array))
+		PROVIDE_HIDDEN (__fini_array_end = .);
+		. = ALIGN(4);
+		__data_end__ = .;
+	} > RAM
+	.bss (NOLOAD) :
+	{
+		__bss_start__ = .;
+		*(.bss*)
+		*(COMMON)
+		__bss_end__ = .;
+	} > RAM
+	.heap (NOLOAD) :
+	{
+		__end__ = .;
+		*(.heap*)
+		__HeapLimit = .;
+	} > RAM
+	.stack_dummy (NOLOAD) :
+	{
+		*(.stack)
+	} > RAM
+
+	__StackTop = ORIGIN(RAM) + LENGTH(RAM);
+	__StackLimit = __StackTop - SIZEOF(.stack_dummy);
+	PROVIDE(__stack = __StackTop);
+	ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
+
+	PROVIDE(_estack = __stack);
+	PROVIDE(_sidata = __etext);
+	PROVIDE(_sdata = __data_start__);
+	PROVIDE(_edata = __data_end__);
+	PROVIDE(_sbss = __bss_start__);
+	PROVIDE(_ebss = __bss_end__);
+}

+ 9 - 2
inc/usbd_core.h

@@ -63,6 +63,12 @@
 #define usbd_evt_count      9
 /** @} */
 
+/** \name USB HW capabilities
+ * @{ */
+#define USBD_HW_ADDRFST     (1 << 0)    /**< Set address before STATUS_OUT */
+
+/** @} */
+
 #if !defined(__ASSEMBLER__)
 
 /** USB device machine states */
@@ -265,6 +271,7 @@ struct _usbd_status {
 
 /** Represents a hardware USB driver call table */
 struct usbd_driver {
+    uint32_t                caps;               /**< HW capabilities **/
     usbd_hw_enable          enable;             /**< \copydoc usbd_hw_enable */
     usbd_hw_reset           reset;              /**< \copydoc usbd_hw_reset */
     usbd_hw_connect         connect;            /**< \copydoc usbd_hw_connect */
@@ -385,7 +392,7 @@ inline static void usbd_reg_event(usbd_device *dev, uint8_t evt, usbd_evt_callba
  * \param dev pointer to \ref usbd_device structure
  * \copydetails usbd_hw_ep_write
  */
-inline static uint16_t usbd_ep_write(usbd_device *dev, uint8_t ep, void *buf, uint16_t blen) {
+inline static int32_t usbd_ep_write(usbd_device *dev, uint8_t ep, void *buf, uint16_t blen) {
     return dev->driver->ep_write(ep, buf, blen);
 }
 
@@ -393,7 +400,7 @@ inline static uint16_t usbd_ep_write(usbd_device *dev, uint8_t ep, void *buf, ui
  * \param dev pointer to \ref usbd_device structure
  * \copydetails usbd_hw_ep_read
  */
-inline static uint16_t usbd_ep_read(usbd_device *dev, uint8_t ep, void *buf, uint16_t blen) {
+inline static int32_t usbd_ep_read(usbd_device *dev, uint8_t ep, void *buf, uint16_t blen) {
     return dev->driver->ep_read(ep, buf, blen);
 }
 

+ 17 - 0
macro.h

@@ -0,0 +1,17 @@
+#ifndef _MACRO_H_
+#define _MACRO_H_
+
+/* modify bitfield */
+#define _BMD(reg, msk, val)     (reg) = (((reg) & ~(msk)) | (val))
+/* set bitfield */
+#define _BST(reg, bits)         (reg) = ((reg) | (bits))
+/* clear bitfield */
+#define _BCL(reg, bits)         (reg) = ((reg) & ~(bits))
+/* wait until bitfield set */
+#define _WBS(reg, bits)         while(((reg) & (bits)) == 0)
+/* wait until bitfield clear */
+#define _WBC(reg, bits)         while(((reg) & (bits)) != 0)
+/* wait for bitfield value */
+#define _WVL(reg, msk, val)     while(((reg) & (msk)) != (val))
+
+#endif //_MACRO_H_

+ 4 - 3
readme.md

@@ -13,7 +13,7 @@
 | usb_stmv0a | GCC ASM    | 8         | Internal S/N, Doublebuffered | STM32L0x2 STM32L0x3 STM32L4x2 STM32L4x3 STM32F0x2 STM32F0x8 |
 | usb_stmv1  | GCC C      | 8         | Internal S/N, Doublebuffered | STM32L1xx  |
 | usb_stmv1a | GCC ASM    | 8         | Internal S/N, Doublebuffered | STM32L1xx  |
-
+| usb_stmv2  | GCC C      | 6         | Internal S/N, Doublebuffered | STM32L4x5 STM32L4x6 (OTG FS (Device mode)) |
 
 1. Single physical endpoint can be used to implement
   + one bi-directional/single-buffer logical endpoint (CONTROL)
@@ -36,8 +36,9 @@ make module MODULE=path/module.a DEFINES="mcu spcified defines" CFLAGS="cpu cpec
 ```
 + to build demo
 ```
-make demo MCU="stm32l100xc"
-make demo MCU="stm32l052x8"
+make demo MCU=stm32l100xc
+make demo MCU=stm32l052x8
+make demo MCU=stm32l476rg
 ```
 + to flash demo using st-flash
 ```

+ 1 - 2
src/usb_32v0.c

@@ -312,8 +312,6 @@ int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
     volatile uint16_t *reg = EPR(ep);
     switch (*reg & (USB_EPTX_STAT | USB_EP_T_FIELD | USB_EP_KIND)) {
     /* doublebuffered bulk endpoint */
-    /* it STAT_TX bits can be in NAKED state. No answer about this */
-    case (USB_EP_TX_VALID | USB_EP_BULK | USB_EP_KIND):
     case (USB_EP_TX_NAK   | USB_EP_BULK | USB_EP_KIND):
         if (*reg & USB_EP_SWBUF_TX) {
             pma_write(buf, blen, &(tbl->tx1));
@@ -419,6 +417,7 @@ uint16_t get_serialno_desc(void *buffer) {
 }
 
 const struct usbd_driver usb_stmv0 = {
+    0,
     enable,
     reset,
     connect,

+ 3 - 1
src/usb_32v0A.S

@@ -17,9 +17,10 @@
     #define __ASSEMBLER__
 #endif
 
+#if defined(USE_STMV0_DRIVER)
 #include "../usb.h"
 #include "memmap.inc"
-#if defined(USE_STMV0_DRIVER)
+
 
 #define EP_SETUP    0x0800
 #define EP_TYPE     0x0600
@@ -83,6 +84,7 @@
     .globl  usb_stmv0a
     .align  2
 usb_stmv0a:
+    .long   0
     .long   _enable
     .long   _reset
     .long   _connect

+ 1 - 2
src/usb_32v1.c

@@ -320,8 +320,6 @@ int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
     volatile uint16_t *reg = EPR(ep);
     switch (*reg & (USB_EPTX_STAT | USB_EP_T_FIELD | USB_EP_KIND)) {
     /* doublebuffered bulk endpoint */
-    /* it STAT_TX bits can be in NAKED state. No answer about this */
-    case (USB_EP_TX_VALID | USB_EP_BULK | USB_EP_KIND):
     case (USB_EP_TX_NAK   | USB_EP_BULK | USB_EP_KIND):
         if (*reg & USB_EP_SWBUF_TX) {
             pma_write(buf, blen, &(tbl->tx1));
@@ -428,6 +426,7 @@ uint16_t get_serialno_desc(void *buffer) {
 }
 
 const struct usbd_driver usb_stmv1 = {
+    0,
     enable,
     reset,
     connect,

+ 3 - 1
src/usb_32v1A.S

@@ -17,9 +17,10 @@
     #define __ASSEMBLER__
 #endif
 
+#if defined(USE_STMV1_DRIVER)
 #include "../usb.h"
 #include "memmap.inc"
-#if defined(USE_STMV1_DRIVER)
+
 
 
 #define EP_SETUP    0x0800
@@ -85,6 +86,7 @@
     .globl  usb_stmv1a
     .align  2
 usb_stmv1a:
+    .long   0
     .long   _enable
     .long   _reset
     .long   _connect

+ 458 - 0
src/usb_32v2.c

@@ -0,0 +1,458 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include "macro.h"
+#include "stm32.h"
+#include "../usb.h"
+
+#if defined(USE_STMV2_DRIVER)
+
+#define VBUS_DETECTION  0
+
+#define MAX_EP          6
+#define MAX_RX_PACKET   128
+#define MAX_CONTROL_EP  1
+#define MAX_FIFO_SZ     320  /*in 32-bit chunks */
+
+#define RX_FIFO_SZ      ((4 * MAX_CONTROL_EP + 6) + ((MAX_RX_PACKET / 4) + 1) + (MAX_EP * 2) + 1)
+
+USB_OTG_GlobalTypeDef * const OTG  = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_GLOBAL_BASE);
+USB_OTG_DeviceTypeDef * const OTGD = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_DEVICE_BASE);
+volatile uint32_t * const OTGPCTL  = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_PCGCCTL_BASE);
+
+
+inline static volatile uint32_t* EPFIFO(uint8_t ep) {
+    return (uint32_t*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + (ep << 12));
+}
+
+inline static USB_OTG_INEndpointTypeDef* EPIN(uint8_t ep) {
+    return (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + (ep << 5));
+}
+
+inline static USB_OTG_OUTEndpointTypeDef* EPOUT(uint8_t ep) {
+    return (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_OUT_ENDPOINT_BASE + (ep << 5));
+}
+
+inline static void Flush_RX(void) {
+    _BST(OTG->GRSTCTL, USB_OTG_GRSTCTL_RXFFLSH);
+    _WBC(OTG->GRSTCTL, USB_OTG_GRSTCTL_RXFFLSH);
+}
+
+inline static void Flush_TX(uint8_t ep) {
+    _BMD(OTG->GRSTCTL, USB_OTG_GRSTCTL_TXFNUM,
+         _VAL2FLD(USB_OTG_GRSTCTL_TXFNUM, ep) | USB_OTG_GRSTCTL_TXFFLSH);
+    _WBC(OTG->GRSTCTL, USB_OTG_GRSTCTL_TXFFLSH);
+}
+
+void ep_setstall(uint8_t ep, bool stall) {
+    if (ep & 0x80) {
+        ep &= 0x7F;
+        uint32_t _t = EPIN(ep)->DIEPCTL;
+        if (_t & USB_OTG_DIEPCTL_USBAEP) {
+            if (stall) {
+                _BST(_t, USB_OTG_DIEPCTL_STALL);
+            } else {
+                _BMD(_t, USB_OTG_DIEPCTL_STALL,
+                     USB_OTG_DIEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_SNAK);
+            }
+            EPIN(ep)->DIEPCTL = _t;
+        }
+    } else {
+        uint32_t _t = EPOUT(ep)->DOEPCTL;
+        if (_t & USB_OTG_DOEPCTL_USBAEP) {
+            if (stall) {
+                _BST(_t, USB_OTG_DOEPCTL_STALL);
+            } else {
+                _BMD(_t, USB_OTG_DOEPCTL_STALL,
+                     USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_CNAK);
+            }
+            EPOUT(ep)->DOEPCTL = _t;
+        }
+    }
+}
+
+bool ep_isstalled(uint8_t ep) {
+    if (ep & 0x80) {
+        ep &= 0x7F;
+        return (EPIN(ep)->DIEPCTL & USB_OTG_DIEPCTL_STALL) ? true : false;
+    } else {
+        return (EPOUT(ep)->DOEPCTL & USB_OTG_DOEPCTL_STALL) ? true : false;
+    }
+}
+
+void enable(bool enable) {
+    if (enable) {
+        /* enabling USB_OTG in RCC */
+        _BST(RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN);
+        /* Set Vbus enabled for USB */
+        _BST(PWR->CR2, PWR_CR2_USV);
+        /* select Internal PHY */
+        OTG->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL;
+        /* do core soft reset */
+        _WBS(OTG->GRSTCTL, USB_OTG_GRSTCTL_AHBIDL);
+        _BST(OTG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
+        _WBC(OTG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
+        /* configure OTG as device */
+        OTG->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_PHYSEL |
+                       _VAL2FLD(USB_OTG_GUSBCFG_TRDT, 0x06);
+        /* configuring Vbus sense and powerup PHY */
+#if (VBUS_DETECTION)
+        OTG->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_PWRDWN;
+#else
+        OTG->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN | USB_OTG_GOTGCTL_BVALOVAL;
+        OTG->GCCFG = USB_OTG_GCCFG_PWRDWN;
+#endif
+        /* restart PHY*/
+        *OTGPCTL = 0;
+        /* soft disconnect device */
+        _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+        /* Setup USB FS speed and frame interval */
+        _BMD(OTGD->DCFG, USB_OTG_DCFG_PERSCHIVL | USB_OTG_DCFG_DSPD,
+             _VAL2FLD(USB_OTG_DCFG_PERSCHIVL, 0) | _VAL2FLD(USB_OTG_DCFG_DSPD, 0x03));
+        /* unmask EP interrupts */
+        OTGD->DIEPMSK = USB_OTG_DIEPMSK_XFRCM;
+        /* unmask core interrupts */
+        OTG->GINTMSK  = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM |
+                        USB_OTG_GINTMSK_SOFM |
+                        USB_OTG_GINTMSK_USBSUSPM | USB_OTG_GINTMSK_WUIM |
+                        USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_RXFLVLM;
+        /* clear pending interrupts */
+        OTG->GINTSTS = 0xFFFFFFFF;
+        /* unmask global interrupt */
+        OTG->GAHBCFG = USB_OTG_GAHBCFG_GINT;
+        /* setting max RX FIFO size */
+        OTG->GRXFSIZ = RX_FIFO_SZ;
+        /* setting up EP0 TX FIFO SZ as 64 byte */
+        OTG->GNPTXFSIZ = RX_FIFO_SZ | (0x10 << 16);
+    } else {
+        if (RCC->AHB2ENR & RCC_AHB2ENR_OTGFSEN) {
+            _BCL(PWR->CR2, PWR_CR2_USV);
+            _BST(RCC->AHB2RSTR, RCC_AHB2RSTR_OTGFSRST);
+            _BCL(RCC->AHB2RSTR, RCC_AHB2RSTR_OTGFSRST);
+            _BCL(RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN);
+        }
+    }
+}
+
+void reset (void) {
+   // _BST(OTG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
+   // _WBC(OTG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
+}
+
+
+void connect(bool connect) {
+    if (connect) {
+        _BCL(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+    } else {
+        _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+    }
+}
+
+void setaddr (uint8_t addr) {
+    _BMD(OTGD->DCFG, USB_OTG_DCFG_DAD, addr << 4);
+}
+
+/**\brief Helper. Set up TX fifo
+ * \param ep endpoint index
+ * \param epsize required max packet size in bytes
+ * \return true if TX fifo is successfully set
+ */
+static bool set_tx_fifo(uint8_t ep, uint16_t epsize) {
+    uint32_t _fsa = OTG->GNPTXFSIZ;
+    /* calculating initial TX FIFO address. next from EP0 TX fifo */
+    _fsa = 0xFFFF & (_fsa + (_fsa >> 16));
+    /* looking for next free TX fifo address */
+    for (int i = 0; i < (MAX_EP - 1); i++) {
+        uint32_t _t = OTG->DIEPTXF[i];
+        if ((_t & 0xFFFF) < 0x200) {
+            _t = 0xFFFF & (_t + (_t >> 16));
+            if (_t > _fsa) {
+                _fsa = _t;
+            }
+        }
+    }
+    /* calculating requited TX fifo size */
+    /* getting in 32 bit terms */
+    epsize = (epsize + 0x03) >> 2;
+    /* it must be 16 32-bit words minimum */
+    if (epsize < 0x10) epsize = 0x10;
+    /* checking for the available fifo */
+    if ((_fsa + epsize) > MAX_FIFO_SZ) return false;
+    /* programming fifo register */
+    _fsa |= (epsize << 16);
+    OTG->DIEPTXF[ep - 1] = _fsa;
+    return true;
+}
+
+bool ep_config(uint8_t ep, uint8_t eptype, uint16_t epsize) {
+    if (ep == 0) {
+        /* configureing control endpoint EP0 */
+        uint32_t mpsize;
+        if (epsize <= 0x08) {
+            epsize = 0x08;
+            mpsize = 0x03;
+        } else if (epsize <= 0x10) {
+            epsize = 0x10;
+            mpsize = 0x02;
+        } else if (epsize <= 0x20) {
+            epsize = 0x20;
+            mpsize = 0x01;
+        } else {
+            epsize = 0x40;
+            mpsize = 0x00;
+        }
+        /* EP0 TX FIFO size is setted on init level */
+        /* enabling RX and TX interrupts from EP0 */
+        OTGD->DAINTMSK |= 0x00010001;
+        /* setting up EP0 TX and RX registers */
+        /*EPIN(ep)->DIEPTSIZ  = epsize;*/
+        EPIN(ep)->DIEPCTL = mpsize | USB_OTG_DIEPCTL_SNAK;
+        /* 1 setup packet, 1 packets total */
+        EPOUT(ep)->DOEPTSIZ = epsize | (1 << 29) | (1 << 19);
+        EPOUT(ep)->DOEPCTL = mpsize | USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
+        return true;
+    }
+    if (ep & 0x80) {
+        ep &= 0x7F;
+        USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+        /* configuring TX endpoint */
+        /* setting up TX fifo and size register */
+        if ((eptype == USB_EPTYPE_ISOCHRONUS) ||
+            (eptype == (USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF))) {
+            if (!set_tx_fifo(ep, epsize << 1)) return false;
+        } else {
+            if (!set_tx_fifo(ep, epsize)) return false;
+        }
+        /* enabling EP TX interrupt */
+        OTGD->DAINTMSK |= (0x0001UL << ep);
+        /* setting up TX control register*/
+        switch (eptype) {
+        case USB_EPTYPE_ISOCHRONUS:
+            epi->DIEPCTL = USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK |
+                           (0x01 << 18) | USB_OTG_DIEPCTL_USBAEP |
+                           USB_OTG_DIEPCTL_SD0PID_SEVNFRM |
+                           (ep << 22) | epsize;
+            break;
+        case USB_EPTYPE_BULK:
+        case USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF:
+            epi->DIEPCTL = USB_OTG_DIEPCTL_SNAK | USB_OTG_DIEPCTL_USBAEP |
+                            (0x02 << 18) | USB_OTG_DIEPCTL_SD0PID_SEVNFRM |
+                            (ep << 22) | epsize;
+            break;
+        default:
+            epi->DIEPCTL = USB_OTG_DIEPCTL_SNAK | USB_OTG_DIEPCTL_USBAEP |
+                            (0x03 << 18) | USB_OTG_DIEPCTL_SD0PID_SEVNFRM |
+                            (ep << 22) | epsize;
+            break;
+        }
+    } else {
+        /* configuring RX endpoint */
+        USB_OTG_OUTEndpointTypeDef* epo = EPOUT(ep);
+        /* setting up RX control register */
+        switch (eptype) {
+        case USB_EPTYPE_ISOCHRONUS:
+            epo->DOEPCTL = USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_CNAK |
+                           USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_USBAEP |
+                           (0x01 << 18) | epsize;
+            break;
+        case USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF:
+        case USB_EPTYPE_BULK:
+            epo->DOEPCTL = USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_CNAK |
+                           USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_USBAEP |
+                           (0x02 << 18) | epsize;
+            break;
+        default:
+            epo->DOEPCTL = USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_CNAK |
+                           USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_USBAEP |
+                           (0x03 << 18) | epsize;
+            break;
+        }
+    }
+    return true;
+}
+
+void ep_deconfig(uint8_t ep) {
+    ep &= 0x7F;
+    volatile USB_OTG_INEndpointTypeDef*  epi = EPIN(ep);
+    volatile USB_OTG_OUTEndpointTypeDef* epo = EPOUT(ep);
+    /* deconfiguring TX part */
+    /* disable interrupt */
+    OTGD->DAINTMSK &= ~(0x10001 << ep);
+    /* decativating endpoint */
+    _BCL(epi->DIEPCTL, USB_OTG_DIEPCTL_USBAEP);
+    /* flushing FIFO */
+    Flush_TX(ep);
+    /* disabling endpoint */
+    if ((epi->DIEPCTL & USB_OTG_DIEPCTL_EPENA) && (ep != 0)) {
+        epi->DIEPCTL = USB_OTG_DIEPCTL_EPDIS;
+        _WBS(epi->DIEPINT, USB_OTG_DIEPINT_EPDISD);
+    }
+    /* clean EP interrupts */
+    epi->DIEPINT = 0xFF;
+    /* deconfiguring TX FIFO */
+    if (ep > 0) {
+        OTG->DIEPTXF[ep-1] = 0x02000200 + 0x200 * ep;
+    }
+    /* deconfigureing RX part */
+    _BCL(epo->DOEPCTL, USB_OTG_DOEPCTL_USBAEP);
+    if ((epo->DOEPCTL & USB_OTG_DOEPCTL_EPENA) && (ep != 0)) {
+        epo->DOEPCTL = USB_OTG_DOEPCTL_EPDIS;
+        _WBS(epo->DOEPINT, USB_OTG_DOEPINT_EPDISD);
+    }
+    epo->DOEPINT = 0xFF;
+}
+
+int32_t ep_read(uint8_t ep, void* buf, uint16_t blen) {
+    uint32_t len;
+    volatile uint32_t *fifo = EPFIFO(0);
+    USB_OTG_OUTEndpointTypeDef* epo = EPOUT(ep);
+    /* no data in RX FIFO */
+    if (!(OTG->GINTSTS & USB_OTG_GINTSTS_RXFLVL)) return -1;
+    ep &= 0x7F;
+    if ((OTG->GRXSTSR & USB_OTG_GRXSTSP_EPNUM) != ep) return -1;
+    /* pop data from fifo */
+    len = _FLD2VAL(USB_OTG_GRXSTSP_BCNT, OTG->GRXSTSP);
+    for (int i = 0; i < len; i +=4) {
+        uint32_t _t = *fifo;
+        if (blen >= 4) {
+            *(__attribute__((packed))uint32_t*)buf = _t;
+            blen -= 4;
+            buf += 4;
+        } else {
+            while (blen){
+                *(uint8_t*)buf = 0xFF & _t;
+                _t >>= 8;
+                blen --;
+            }
+        }
+    }
+    _BST(epo->DOEPCTL, USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
+    return len;
+}
+
+int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
+    ep &= 0x7F;
+    volatile uint32_t* _fifo = EPFIFO(ep);
+    USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+    /* transfer data size in 32-bit words */
+    uint32_t  _len = (blen + 3) >> 2;
+    /* no enough space in TX fifo */
+    if (_len > epi->DTXFSTS) return -1;
+    if (ep != 0 && epi->DIEPCTL & USB_OTG_DIEPCTL_EPENA) {
+        return -1;
+    }
+    epi->DIEPTSIZ = 0;
+    epi->DIEPTSIZ = (1 << 19) + blen;
+    _BMD(epi->DIEPCTL, USB_OTG_DIEPCTL_STALL, USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK);
+    while (_len--) {
+        *_fifo = *(__attribute__((packed)) uint32_t*)buf;
+        buf += 4;
+    }
+    return blen;
+}
+
+uint16_t get_frame (void) {
+    return _FLD2VAL(USB_OTG_DSTS_FNSOF, OTGD->DSTS);
+}
+
+void evt_poll(usbd_device *dev, usbd_evt_callback callback) {
+    uint32_t evt;
+    uint32_t ep = 0;
+    while (1) {
+        uint32_t _t = OTG->GINTSTS;
+        /* bus RESET event */
+        if (_t & USB_OTG_GINTSTS_USBRST) {
+            OTG->GINTSTS = USB_OTG_GINTSTS_USBRST;
+            for (uint8_t i = 0; i < MAX_EP; i++ ) {
+                ep_deconfig(i);
+            }
+            Flush_RX();
+            continue;
+        } else if (_t & USB_OTG_GINTSTS_ENUMDNE) {
+            OTG->GINTSTS = USB_OTG_GINTSTS_ENUMDNE;
+            evt = usbd_evt_reset;
+        } else if (_t & USB_OTG_GINTSTS_IEPINT) {
+            for (;; ep++) {
+                USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+                if (ep >= MAX_EP) return;
+                if (epi->DIEPINT & USB_OTG_DIEPINT_XFRC) {
+                    epi->DIEPINT = USB_OTG_DIEPINT_XFRC;
+                    evt = usbd_evt_eptx;
+                    ep |= 0x80;
+                    break;
+                }
+            }
+        } else if (_t & USB_OTG_GINTSTS_RXFLVL) {
+            _t = OTG->GRXSTSR;
+            ep = _t & USB_OTG_GRXSTSP_EPNUM;
+            switch (_FLD2VAL(USB_OTG_GRXSTSP_PKTSTS, _t)) {
+            case 0x02:
+                evt = usbd_evt_eprx;
+                break;
+            case 0x06:
+                evt = usbd_evt_epsetup;
+                break;
+            default:
+                OTG->GRXSTSP;
+                continue;
+            }
+        } else if (_t & USB_OTG_GINTSTS_SOF) {
+            OTG->GINTSTS = USB_OTG_GINTSTS_SOF;
+            evt = usbd_evt_sof;
+        } else if (_t & USB_OTG_GINTSTS_USBSUSP) {
+            evt = usbd_evt_susp;
+            OTG->GINTSTS = USB_OTG_GINTSTS_USBSUSP;
+        } else if (_t & USB_OTG_GINTSTS_WKUINT) {
+            OTG->GINTSTS = USB_OTG_GINTSTS_WKUINT;
+            evt = usbd_evt_wkup;
+        } else {
+            /* no more supported events */
+            return;
+        }
+        return callback(dev, evt, ep);
+    }
+}
+
+static uint32_t fnv1a32_turn (uint32_t fnv, uint32_t data ) {
+    for (int i = 0; i < 4 ; i++) {
+        fnv ^= (data & 0xFF);
+        fnv *= 16777619;
+        data >>= 8;
+    }
+    return fnv;
+}
+
+uint16_t get_serialno_desc(void *buffer) {
+    struct  usb_string_descriptor *dsc = buffer;
+    uint16_t *str = dsc->wString;
+    uint32_t fnv = 2166136261;
+    fnv = fnv1a32_turn(fnv, *(uint32_t*)(UID_BASE + 0x00));
+    fnv = fnv1a32_turn(fnv, *(uint32_t*)(UID_BASE + 0x04));
+    fnv = fnv1a32_turn(fnv, *(uint32_t*)(UID_BASE + 0x14));
+    for (int i = 28; i >= 0; i -= 4 ) {
+        uint16_t c = (fnv >> i) & 0x0F;
+        c += (c < 10) ? '0' : ('A' - 10);
+        *str++ = c;
+    }
+    dsc->bDescriptorType = USB_DTYPE_STRING;
+    dsc->bLength = 18;
+    return 18;
+}
+
+const struct usbd_driver usb_stmv2 = {
+    USBD_HW_ADDRFST,
+    enable,
+    reset,
+    connect,
+    setaddr,
+    ep_config,
+    ep_deconfig,
+    ep_read,
+    ep_write,
+    ep_setstall,
+    ep_isstalled,
+    evt_poll,
+    get_frame,
+    get_serialno_desc,
+};
+
+#endif //USE_STM32V2_DRIVER

+ 6 - 2
src/usbd_core.c

@@ -101,7 +101,11 @@ static usbd_respond usbd_process_devrq (usbd_device *dev, usbd_ctlreq *req) {
         req->data[1] = 0;
         return usbd_ack;
     case USB_STD_SET_ADDRESS:
-        dev->complete_callback = usbd_set_address;
+        if (dev->driver->caps & USBD_HW_ADDRFST) {
+            usbd_set_address(dev, req);
+        } else {
+            dev->complete_callback = usbd_set_address;
+        }
         return usbd_ack;
     case USB_STD_SET_CONFIG:
         return usbd_configure(dev, req->wValue);
@@ -200,7 +204,7 @@ static void usbd_stall_pid(usbd_device *dev, uint8_t ep) {
  * \param ep endpoint number
  */
 static void usbd_process_eptx(usbd_device *dev, uint8_t ep) {
-    uint16_t _t;
+    int32_t _t;
     switch (dev->status.control_state) {
     case usbd_ctl_ztxdata:
     case usbd_ctl_txdata:

+ 5 - 0
usb.h

@@ -42,6 +42,8 @@
     #if (defined(FORCE_ASM_DRIVER) || defined(STM32L100xC)) && !defined(FORCE_C_DRIVER)
         #define USE_STMV1A_DRIVER
     #endif
+#elif defined(STM32L476xx)
+    #define USE_STMV2_DRIVER
 #else
     #error Unsupported STM32 family
 #endif
@@ -62,6 +64,9 @@
     #elif defined(USE_STMV1_DRIVER)
         extern const struct usbd_driver usb_stmv1;
         #define usbd_hw usb_stmv1
+    #elif defined(USE_STMV2_DRIVER)
+        extern const struct usbd_driver usb_stmv2;
+        #define usbd_hw usb_stmv2
     #endif
 #endif