Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/flipperdevices/libusb_stm32.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Filimonchuk <dmitrystu@gmail.com>2021-10-17 20:16:16 +0300
committerGitHub <noreply@github.com>2021-10-17 20:16:16 +0300
commit75c581119b99c86d302e612fad78b65f492f27eb (patch)
tree69e49c1754acb2ebb77c513f0bb1b69af0c987eb
parent63c806d9b58168a3e2da8c328087ba4f0fe119f7 (diff)
parentfe3890e10e35a837184cb05f835ef6ab14bfd04f (diff)
Merge pull request #98 from flipperdevices/master
Add support for STM32WB55
-rw-r--r--Makefile8
-rw-r--r--demo/cdc_startup.c16
-rw-r--r--demo/stm32wb55xg.ld16
-rw-r--r--inc/usb.h8
-rw-r--r--readme.md6
-rw-r--r--src/usbd_stm32wb55_devfs.c473
6 files changed, 527 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index ebf62c9..2cc6fd2 100644
--- a/Makefile
+++ b/Makefile
@@ -201,6 +201,7 @@ stm32l433cc: clean
DEFINES='STM32L4 STM32L433xx USBD_SOF_DISABLED' \
CFLAGS='-mcpu=cortex-m4'
+
stm32f070xb: clean
@$(MAKE) demo STARTUP='$(CMSISDEV)/ST/STM32F0xx/Source/Templates/gcc/startup_stm32f070xb.s' \
LDSCRIPT='demo/stm32f070xb.ld' \
@@ -278,3 +279,10 @@ stm32f042f6: clean
LDSCRIPT='demo/stm32f042x6.ld' \
DEFINES='STM32F0 STM32F042x6 USBD_SOF_DISABLED' \
CFLAGS='-mcpu=cortex-m0 -DUSBD_PINS_REMAP'
+
+stm32wb55xg: clean
+ @$(MAKE) demo STARTUP='$(CMSISDEV)/ST/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.s' \
+ LDSCRIPT='demo/stm32wb55xg.ld' \
+ DEFINES='STM32WB STM32WB55xx USBD_SOF_DISABLED' \
+ CFLAGS='-mcpu=cortex-m4'
+
diff --git a/demo/cdc_startup.c b/demo/cdc_startup.c
index 202b70b..8e71eea 100644
--- a/demo/cdc_startup.c
+++ b/demo/cdc_startup.c
@@ -269,6 +269,22 @@ static void cdc_init_rcc (void) {
/* using HSI16 as AHB/CPU clock, HSI48 as USB PHY clock */
_BST(RCC->CRRCR, RCC_CRRCR_HSI48ON);
_WBS(RCC->CRRCR, RCC_CRRCR_HSI48RDY);
+#elif defined(STM32WB55xx)
+ /* using HSI16 as AHB/CPU clock, HSI48 as USB PHY clock */
+ _BST(RCC->CR, RCC_CR_HSION);
+ _WBS(RCC->CR, RCC_CR_HSIRDY);
+ _BMD(RCC->CFGR, RCC_CFGR_SW, (0x01UL << RCC_CFGR_SW_Pos)); // HSI16
+ _WVL(RCC->CFGR, RCC_CFGR_SWS, (0x01UL << RCC_CFGR_SWS_Pos));
+ _BST(RCC->CRRCR, RCC_CRRCR_HSI48ON);
+ _WBS(RCC->CRRCR, RCC_CRRCR_HSI48RDY);
+ _BMD(RCC->CCIPR, RCC_CCIPR_CLK48SEL, 0);
+ /* setup PA11 PA12 to AF10 (USB FS) */
+ _BST(RCC->AHB2ENR, RCC_AHB2ENR_GPIOAEN);
+ _BMD(GPIOA->MODER, (0x03 << 22) | (0x03 << 24), (0x02 << 22) | (0x02 << 24));
+ _BST(GPIOA->AFR[1], (0x0A << 12) | (0x0A << 16));
+ /* Disabling USB Vddusb power isolation. Vusb connected to Vdd */
+ _BST(PWR->CR2, PWR_CR2_USV);
+
#else
#error Not supported
#endif
diff --git a/demo/stm32wb55xg.ld b/demo/stm32wb55xg.ld
new file mode 100644
index 0000000..b89fd5d
--- /dev/null
+++ b/demo/stm32wb55xg.ld
@@ -0,0 +1,16 @@
+ENTRY(Reset_Handler)
+MEMORY
+{
+ ROM (rx): ORIGIN = 0x08000000, LENGTH = 1024K
+ RAM (rwx): ORIGIN = 0x20000008, LENGTH = 0x2fff8
+ RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
+}
+
+SECTIONS
+{
+ MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED
+ MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED
+ MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED
+}
+
+INCLUDE sections.ld
diff --git a/inc/usb.h b/inc/usb.h
index 5924b01..5a73df8 100644
--- a/inc/usb.h
+++ b/inc/usb.h
@@ -151,6 +151,14 @@
#define usbd_hw usbd_otgfs
#endif
+#elif defined(STM32WB55xx)
+ #define USBD_STM32WB55
+
+ #if !defined(__ASSEMBLER__)
+ extern const struct usbd_driver usbd_devfs;
+ #define usbd_hw usbd_devfs
+ #endif
+
#else
#error Unsupported STM32 family
#endif
diff --git a/readme.md b/readme.md
index 4659241..bde1c77 100644
--- a/readme.md
+++ b/readme.md
@@ -62,6 +62,12 @@ All requirements can be downloaded into a directory specified in environment var
<td>usbd_stm32f103_devfs_asm.S</td>
</tr>
<tr>
+ <td>STM32WB55</td>
+ <td>Doublebuffered<sup>[2]</sup><br />External DP pullup<br />8<sup>[1]</sup> endpoints</td>
+ <td>usbd_devfs</td>
+ <td>usbd_stm32wb55_devfs.c</td>
+ </tr>
+ <tr>
<td>STM32L4x5 STM32L4x6</td>
<td nowrap>Doublebuffered<br />6 endpoints<br /> BC1.2<br />VBUS detection</td>
<td>usbd_otgfs</td>
diff --git a/src/usbd_stm32wb55_devfs.c b/src/usbd_stm32wb55_devfs.c
new file mode 100644
index 0000000..884343c
--- /dev/null
+++ b/src/usbd_stm32wb55_devfs.c
@@ -0,0 +1,473 @@
+/* This file is the part of the Lightweight USB device Stack for STM32 microcontrollers
+ *
+ * Copyright ©2016 Dmitry Filimonchuk <dmitrystu[at]gmail[dot]com>
+ *
+ * Licensed 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.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "stm32.h"
+#include "usb.h"
+
+#if defined(USBD_STM32WB55)
+
+#ifndef USB_PMASIZE
+ #pragma message "PMA memory size is not defined. Use 1k by default"
+ #define USB_PMASIZE 0x400
+#endif
+
+#if !defined(RCC_APB1ENR1_USBFSEN)
+ #define RCC_APB1ENR1_USBFSEN RCC_APB1ENR1_USBEN
+ #define RCC_APB1RSTR1_USBFSRST RCC_APB1RSTR1_USBRST
+#endif
+
+#define USB_EP_SWBUF_TX USB_EP_DTOG_RX
+#define USB_EP_SWBUF_RX USB_EP_DTOG_TX
+
+#define EP_TOGGLE_SET(epr, bits, mask) *(epr) = (*(epr) ^ (bits)) & (USB_EPREG_MASK | (mask))
+
+#define EP_TX_STALL(epr) EP_TOGGLE_SET((epr), USB_EP_TX_STALL, USB_EPTX_STAT)
+#define EP_RX_STALL(epr) EP_TOGGLE_SET((epr), USB_EP_RX_STALL, USB_EPRX_STAT)
+#define EP_TX_UNSTALL(epr) EP_TOGGLE_SET((epr), USB_EP_TX_NAK, USB_EPTX_STAT | USB_EP_DTOG_TX)
+#define EP_RX_UNSTALL(epr) EP_TOGGLE_SET((epr), USB_EP_RX_VALID, USB_EPRX_STAT | USB_EP_DTOG_RX)
+#define EP_DTX_UNSTALL(epr) EP_TOGGLE_SET((epr), USB_EP_TX_VALID, USB_EPTX_STAT | USB_EP_DTOG_TX | USB_EP_SWBUF_TX)
+#define EP_DRX_UNSTALL(epr) EP_TOGGLE_SET((epr), USB_EP_RX_VALID | USB_EP_SWBUF_RX, USB_EPRX_STAT | USB_EP_DTOG_RX | USB_EP_SWBUF_RX)
+#define EP_TX_VALID(epr) EP_TOGGLE_SET((epr), USB_EP_TX_VALID, USB_EPTX_STAT)
+#define EP_RX_VALID(epr) EP_TOGGLE_SET((epr), USB_EP_RX_VALID, USB_EPRX_STAT)
+
+#define STATUS_VAL(x) (USBD_HW_BC | (x))
+
+typedef struct {
+ uint16_t addr;
+ uint16_t cnt;
+} pma_rec;
+
+typedef union pma_table {
+ struct {
+ pma_rec tx;
+ pma_rec rx;
+ };
+ struct {
+ pma_rec tx0;
+ pma_rec tx1;
+ };
+ struct {
+ pma_rec rx0;
+ pma_rec rx1;
+ };
+} pma_table;
+
+
+/** \brief Helper function. Returns pointer to the buffer descriptor table.
+ */
+inline static pma_table *EPT(uint8_t ep) {
+ return (pma_table*)((ep & 0x07) * 8 + USB1_PMAADDR);
+
+}
+
+/** \brief Helper function. Returns pointer to the endpoint control register.
+ */
+inline static volatile uint16_t *EPR(uint8_t ep) {
+ return (uint16_t*)((ep & 0x07) * 4 + USB1_BASE);
+}
+
+
+/** \brief Helper function. Returns next available PMA buffer.
+ *
+ * \param sz uint16_t Requested buffer size.
+ * \return uint16_t Buffer address for PMA table.
+ * \note PMA buffers grown from top to bottom like stack.
+ */
+static uint16_t get_next_pma(uint16_t sz) {
+ unsigned _result = USB_PMASIZE;
+ for (int i = 0; i < 8; i++) {
+ pma_table *tbl = EPT(i);
+ if ((tbl->rx.addr) && (tbl->rx.addr < _result)) _result = tbl->rx.addr;
+ if ((tbl->tx.addr) && (tbl->tx.addr < _result)) _result = tbl->tx.addr;
+ }
+ return (_result < (0x020 + sz)) ? 0 : (_result - sz);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->APB1ENR1 & RCC_APB1ENR1_USBFSEN)) return STATUS_VAL(0);
+ if (USB->BCDR & USB_BCDR_DPPU) return STATUS_VAL(USBD_HW_ENABLED | USBD_HW_SPEED_FS);
+ return STATUS_VAL(USBD_HW_ENABLED);
+}
+
+static void ep_setstall(uint8_t ep, bool stall) {
+ volatile uint16_t *reg = EPR(ep);
+ /* ISOCHRONOUS endpoint can't be stalled or unstalled */
+ if (USB_EP_ISOCHRONOUS == (*reg & USB_EP_T_FIELD)) return;
+ /* If it's an IN endpoint */
+ if (ep & 0x80) {
+ /* DISABLED endpoint can't be stalled or unstalled */
+ if (USB_EP_TX_DIS == (*reg & USB_EPTX_STAT)) return;
+ if (stall) {
+ EP_TX_STALL(reg);
+ } else {
+ /* if it's a doublebuffered endpoint */
+ if ((USB_EP_KIND | USB_EP_BULK) == (*reg & (USB_EP_T_FIELD | USB_EP_KIND))) {
+ /* set endpoint to VALID and clear DTOG_TX & SWBUF_TX */
+ EP_DTX_UNSTALL(reg);
+ } else {
+ /* set endpoint to NAKED and clear DTOG_TX */
+ EP_TX_UNSTALL(reg);
+ }
+ }
+ } else {
+ if (USB_EP_RX_DIS == (*reg & USB_EPRX_STAT)) return;
+ if (stall) {
+ EP_RX_STALL(reg);
+ } else {
+ /* if it's a doublebuffered endpoint */
+ if ((USB_EP_KIND | USB_EP_BULK) == (*reg & (USB_EP_T_FIELD | USB_EP_KIND))) {
+ /* set endpoint to VALID, clear DTOG_RX, set SWBUF_RX */
+ EP_DRX_UNSTALL(reg);
+ } else {
+ /* set endpoint to VALID and clear DTOG_RX */
+ EP_RX_UNSTALL(reg);
+ }
+ }
+ }
+}
+
+static bool ep_isstalled(uint8_t ep) {
+ if (ep & 0x80) {
+ return (USB_EP_TX_STALL == (USB_EPTX_STAT & *EPR(ep)));
+ } else {
+ return (USB_EP_RX_STALL == (USB_EPRX_STAT & *EPR(ep)));
+ }
+}
+
+static void enable(bool enable) {
+ if (enable) {
+ RCC->APB1ENR1 |= RCC_APB1ENR1_USBFSEN;
+ RCC->APB1RSTR1 |= RCC_APB1RSTR1_USBFSRST;
+ RCC->APB1RSTR1 &= ~RCC_APB1RSTR1_USBFSRST;
+ USB->CNTR = USB_CNTR_CTRM | USB_CNTR_RESETM | USB_CNTR_ERRM |
+#if !defined(USBD_SOF_DISABLED)
+ USB_CNTR_SOFM |
+#endif
+ USB_CNTR_SUSPM | USB_CNTR_WKUPM;
+ } else if (RCC->APB1ENR1 & RCC_APB1ENR1_USBFSEN) {
+ USB->BCDR = 0;
+ RCC->APB1RSTR1 |= RCC_APB1RSTR1_USBFSRST;
+ RCC->APB1ENR1 &= ~RCC_APB1ENR1_USBFSEN;
+ }
+}
+
+static uint8_t connect(bool connect) {
+ uint8_t res;
+ USB->BCDR = USB_BCDR_BCDEN | USB_BCDR_DCDEN;
+ if (USB->BCDR & USB_BCDR_DCDET) {
+ USB->BCDR = USB_BCDR_BCDEN | USB_BCDR_PDEN;
+ if (USB->BCDR & USB_BCDR_PS2DET) {
+ res = usbd_lane_unk;
+ } else if (USB->BCDR & USB_BCDR_PDET) {
+ USB->BCDR = USB_BCDR_BCDEN | USB_BCDR_SDEN;
+ if (USB->BCDR & USB_BCDR_SDET) {
+ res = usbd_lane_dcp;
+ } else {
+ res = usbd_lane_cdp;
+ }
+ } else {
+ res = usbd_lane_sdp;
+ }
+ } else {
+ res = usbd_lane_dsc;
+ }
+ USB->BCDR = (connect) ? USB_BCDR_DPPU : 0;
+ return res;
+}
+
+static void setaddr (uint8_t addr) {
+ USB->DADDR = USB_DADDR_EF | addr;
+}
+
+static bool ep_config(uint8_t ep, uint8_t eptype, uint16_t epsize) {
+ volatile uint16_t *reg = EPR(ep);
+ pma_table *tbl = EPT(ep);
+ /* epsize must be 2-byte aligned */
+ epsize = (~0x01U) & (epsize + 1);
+
+ switch (eptype) {
+ case USB_EPTYPE_CONTROL:
+ *reg = USB_EP_CONTROL | (ep & 0x07);
+ break;
+ case USB_EPTYPE_ISOCHRONUS:
+ *reg = USB_EP_ISOCHRONOUS | (ep & 0x07);
+ break;
+ case USB_EPTYPE_BULK:
+ *reg = USB_EP_BULK | (ep & 0x07);
+ break;
+ case USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF:
+ *reg = USB_EP_BULK | USB_EP_KIND | (ep & 0x07);
+ break;
+ default:
+ *reg = USB_EP_INTERRUPT | (ep & 0x07);
+ break;
+ }
+ /* if it TX or CONTROL endpoint */
+ if ((ep & 0x80) || (eptype == USB_EPTYPE_CONTROL)) {
+ uint16_t _pma;
+ _pma = get_next_pma(epsize);
+ if (_pma == 0) return false;
+ tbl->tx.addr = _pma;
+ tbl->tx.cnt = 0;
+ if ((eptype == USB_EPTYPE_ISOCHRONUS) ||
+ (eptype == (USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF))) {
+ _pma = get_next_pma(epsize);
+ if (_pma == 0) return false;
+ tbl->tx1.addr = _pma;
+ tbl->tx1.cnt = 0;
+ EP_DTX_UNSTALL(reg);
+ } else {
+ EP_TX_UNSTALL(reg);
+ }
+ }
+ if (!(ep & 0x80)) {
+ uint16_t _rxcnt;
+ uint16_t _pma;
+ if (epsize > 62) {
+ /* using 32-byte blocks. epsize must be 32-byte aligned */
+ epsize = (~0x1FU) & (epsize + 0x1FU);
+ _rxcnt = 0x8000 - 0x400 + (epsize << 5);
+ } else {
+ _rxcnt = epsize << 9;
+ }
+ _pma = get_next_pma(epsize);
+ if (_pma == 0) return false;
+ tbl->rx.addr = _pma;
+ tbl->rx.cnt = _rxcnt;
+ if ((eptype == USB_EPTYPE_ISOCHRONUS) ||
+ (eptype == (USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF))) {
+ _pma = get_next_pma(epsize);
+ if (_pma == 0) return false;
+ tbl->rx0.addr = _pma;
+ tbl->rx0.cnt = _rxcnt;
+ EP_DRX_UNSTALL(reg);
+ } else {
+ EP_RX_UNSTALL(reg);
+ }
+ }
+ return true;
+}
+
+static void ep_deconfig(uint8_t ep) {
+ pma_table *ept = EPT(ep);
+ *EPR(ep) &= ~USB_EPREG_MASK;
+ ept->rx.addr = 0;
+ ept->rx.cnt = 0;
+ ept->tx.addr = 0;
+ ept->tx.cnt = 0;
+}
+
+static uint16_t pma_read (uint8_t *buf, uint16_t blen, pma_rec *rx) {
+ uint16_t *pma = (void*)(USB1_PMAADDR + rx->addr);
+ uint16_t rxcnt = rx->cnt & 0x03FF;
+ rx->cnt &= ~0x3FF;
+
+ if (blen > rxcnt) {
+ blen = rxcnt;
+ }
+ rxcnt = blen;
+ while (blen) {
+ uint16_t _t = *pma;
+ *buf++ = _t & 0xFF;
+ if (--blen) {
+ *buf++ = _t >> 8;
+ pma++;
+ blen--;
+ } else break;
+ }
+ return rxcnt;
+}
+
+static int32_t ep_read(uint8_t ep, void *buf, uint16_t blen) {
+ pma_table *tbl = EPT(ep);
+ volatile uint16_t *reg = EPR(ep);
+ switch (*reg & (USB_EPRX_STAT | USB_EP_T_FIELD | USB_EP_KIND)) {
+ /* doublebuffered bulk endpoint */
+ case (USB_EP_RX_VALID | USB_EP_BULK | USB_EP_KIND):
+ /* switching SWBUF if EP is NAKED */
+ switch (*reg & (USB_EP_DTOG_RX | USB_EP_SWBUF_RX)) {
+ case 0:
+ case (USB_EP_DTOG_RX | USB_EP_SWBUF_RX):
+ *reg = (*reg & USB_EPREG_MASK) | USB_EP_SWBUF_RX;
+ break;
+ default:
+ break;
+ }
+ if (*reg & USB_EP_SWBUF_RX) {
+ return pma_read(buf, blen, &(tbl->rx1));
+ } else {
+ return pma_read(buf, blen, &(tbl->rx0));
+ }
+ /* isochronous endpoint */
+ case (USB_EP_RX_VALID | USB_EP_ISOCHRONOUS):
+ if (*reg & USB_EP_DTOG_RX) {
+ return pma_read(buf, blen, &(tbl->rx1));
+ } else {
+ return pma_read(buf, blen, &(tbl->rx0));
+ }
+ /* regular endpoint */
+ case (USB_EP_RX_NAK | USB_EP_BULK):
+ case (USB_EP_RX_NAK | USB_EP_CONTROL):
+ case (USB_EP_RX_NAK | USB_EP_INTERRUPT):
+ {
+ int32_t res = pma_read(buf, blen, &(tbl->rx));
+ /* setting endpoint to VALID state */
+ EP_RX_VALID(reg);
+ return res;
+ }
+ /* invalid or not ready */
+ default:
+ return -1;
+ }
+}
+
+static void pma_write(uint8_t *buf, uint16_t blen, pma_rec *tx) {
+ uint16_t *pma = (void*)(USB1_PMAADDR + tx->addr);
+ tx->cnt = blen;
+ while (blen > 1) {
+ *pma++ = buf[1] << 8 | buf[0];
+ buf += 2;
+ blen -= 2;
+ }
+ if (blen) *pma = *buf;
+}
+
+static int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
+ pma_table *tbl = EPT(ep);
+ volatile uint16_t *reg = EPR(ep);
+ switch (*reg & (USB_EPTX_STAT | USB_EP_T_FIELD | USB_EP_KIND)) {
+ /* doublebuffered bulk endpoint */
+ case (USB_EP_TX_NAK | USB_EP_BULK | USB_EP_KIND):
+ if (*reg & USB_EP_SWBUF_TX) {
+ pma_write(buf, blen, &(tbl->tx1));
+ } else {
+ pma_write(buf, blen, &(tbl->tx0));
+ }
+ *reg = (*reg & USB_EPREG_MASK) | USB_EP_SWBUF_TX;
+ break;
+ /* isochronous endpoint */
+ case (USB_EP_TX_VALID | USB_EP_ISOCHRONOUS):
+ if (!(*reg & USB_EP_DTOG_TX)) {
+ pma_write(buf, blen, &(tbl->tx1));
+ } else {
+ pma_write(buf, blen, &(tbl->tx0));
+ }
+ break;
+ /* regular endpoint */
+ case (USB_EP_TX_NAK | USB_EP_BULK):
+ case (USB_EP_TX_NAK | USB_EP_CONTROL):
+ case (USB_EP_TX_NAK | USB_EP_INTERRUPT):
+ pma_write(buf, blen, &(tbl->tx));
+ EP_TX_VALID(reg);
+ break;
+ /* invalid or not ready */
+ default:
+ return -1;
+ }
+ return blen;
+}
+
+static uint16_t get_frame (void) {
+ return USB->FNR & USB_FNR_FN;
+}
+
+static void evt_poll(usbd_device *dev, usbd_evt_callback callback) {
+ uint8_t _ev, _ep;
+ uint16_t _istr = USB->ISTR;
+ _ep = _istr & USB_ISTR_EP_ID;
+ if (_istr & USB_ISTR_CTR) {
+ volatile uint16_t *reg = EPR(_ep);
+ if (*reg & USB_EP_CTR_TX) {
+ *reg &= (USB_EPREG_MASK ^ USB_EP_CTR_TX);
+ _ep |= 0x80;
+ _ev = usbd_evt_eptx;
+ } else {
+ *reg &= (USB_EPREG_MASK ^ USB_EP_CTR_RX);
+ _ev = (*reg & USB_EP_SETUP) ? usbd_evt_epsetup : usbd_evt_eprx;
+ }
+ } else if (_istr & USB_ISTR_RESET) {
+ USB->ISTR &= ~USB_ISTR_RESET;
+ USB->BTABLE = 0;
+ for (int i = 0; i < 8; i++) {
+ ep_deconfig(i);
+ }
+ _ev = usbd_evt_reset;
+#if !defined(USBD_SOF_DISABLED)
+ } else if (_istr & USB_ISTR_SOF) {
+ _ev = usbd_evt_sof;
+ USB->ISTR &= ~USB_ISTR_SOF;
+#endif
+ } else if (_istr & USB_ISTR_WKUP) {
+ _ev = usbd_evt_wkup;
+ USB->CNTR &= ~USB_CNTR_FSUSP;
+ USB->ISTR &= ~USB_ISTR_WKUP;
+ } else if (_istr & USB_ISTR_SUSP) {
+ _ev = usbd_evt_susp;
+ USB->CNTR |= USB_CNTR_FSUSP;
+ USB->ISTR &= ~USB_ISTR_SUSP;
+ } else if (_istr & USB_ISTR_ERR) {
+ USB->ISTR &= ~USB_ISTR_ERR;
+ _ev = usbd_evt_error;
+ } else {
+ return;
+ }
+ callback(dev, _ev, _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;
+}
+
+static 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;
+}
+
+ __attribute__((externally_visible)) const struct usbd_driver usbd_devfs = {
+ getinfo,
+ enable,
+ connect,
+ setaddr,
+ ep_config,
+ ep_deconfig,
+ ep_read,
+ ep_write,
+ ep_setstall,
+ ep_isstalled,
+ evt_poll,
+ get_frame,
+ get_serialno_desc,
+};
+
+#endif //USBD_STM32WB55