diff --git a/Makefile b/Makefile
index 0c9290b..02b0ff4 100644
--- a/Makefile
+++ b/Makefile
@@ -201,6 +201,11 @@ stm32l433cc: clean
DEFINES='STM32L4 STM32L433xx USBD_SOF_DISABLED' \
CFLAGS='-mcpu=cortex-m4'
+stm32l552xe: clean
+ @$(MAKE) demo STARTUP='$(CMSISDEV)/ST/STM32L5xx/Source/Templates/gcc/startup_stm32l55sxx.s' \
+ LDSCRIPT='demo/stm32l552xe.ld' \
+ DEFINES='STM32L5 STM32L552xx USBD_SOF_DISABLED' \
+ CFLAGS='-mcpu=cortex-m33'
stm32f070xb: clean
@$(MAKE) demo STARTUP='$(CMSISDEV)/ST/STM32F0xx/Source/Templates/gcc/startup_stm32f070xb.s' \
diff --git a/demo/stm32l552xe.ld b/demo/stm32l552xe.ld
new file mode 100644
index 0000000..fe8b8f5
--- /dev/null
+++ b/demo/stm32l552xe.ld
@@ -0,0 +1,9 @@
+ENTRY(Reset_Handler)
+MEMORY
+{
+ ROM (rx): ORIGIN = 0x08000000, LENGTH = 512K
+ RAM (rwx): ORIGIN = 0x30000000, LENGTH = 192K
+ SRAM (rwx): ORIGIN = 0x0A030000, LENGTH = 16K
+}
+
+INCLUDE sections.ld
diff --git a/inc/usb.h b/inc/usb.h
index 5c3e589..8232f88 100644
--- a/inc/usb.h
+++ b/inc/usb.h
@@ -61,6 +61,20 @@
#endif
#endif
+#elif defined(STM32L552xx)
+
+ #define USBD_STM32L552
+
+ #if !defined(__ASSEMBLER__)
+ extern const struct usbd_driver usbd_devfs;
+ extern const struct usbd_driver usbd_devfs_asm;
+ #if defined(USBD_ASM_DRIVER)
+ #define usbd_hw usbd_devfs_asm
+ #else
+ #define usbd_hw usbd_devfs
+ #endif
+ #endif
+
#elif defined(STM32L1)
#define USBD_STM32L100
diff --git a/readme.md b/readme.md
index 504c87d..86e79c1 100644
--- a/readme.md
+++ b/readme.md
@@ -73,6 +73,16 @@ All requirements can be downloaded into a directory specified in environment var
usbd_otgfs |
usbd_stm32l476_otgfs.c |
+
+ | STM32L552 |
+ Doublebuffered[2] 8[1] endpoints BC1.2 |
+ usbd_devfs |
+ usbd_stm32l552_devfs.c |
+
+
+ | usbd_devfs_asm |
+ usbd_stm32l552_devfs_asm.S |
+
| STM32F401 STM32F411 |
Doublebuffered 4 endpoints VBUS detection SOF output |
diff --git a/src/memmap.inc b/src/memmap.inc
index ca931c2..a35e8a0 100644
--- a/src/memmap.inc
+++ b/src/memmap.inc
@@ -71,6 +71,23 @@
#define UID_BASE 0x1FFF7590
#define RCC_USBEN 26
+#elif defined(STM32L552xx)
+
+ #define USB_EPBASE 0x4000D400
+ #define USB_REGBASE 0x4000D440
+ #define USB_CNTR 0x00
+ #define USB_ISTR 0x04
+ #define USB_FNR 0x08
+ #define USB_DADDR 0x0C
+ #define USB_BTABLE 0x10
+ #define USB_BCDR 0x18
+ #define USB_PMABASE 0x4000D800
+ #define RCC_BASE 0x40021000
+ #define RCC_APB1RSTR2 0x3C
+ #define RCC_APB1ENR2 0x5C
+ #define UID_BASE 0x0BFA0590
+ #define RCC_USBEN 21
+
#elif defined(STM32G4)
#define USB_EPBASE 0x40005C00
diff --git a/src/usbd_stm32l552_devfs.c b/src/usbd_stm32l552_devfs.c
new file mode 100644
index 0000000..73ebeb4
--- /dev/null
+++ b/src/usbd_stm32l552_devfs.c
@@ -0,0 +1,470 @@
+/* This file is the part of the Lightweight USB device Stack for STM32 microcontrollers
+ *
+ * Copyright ©2016 Dmitry Filimonchuk
+ *
+ * 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
+#include
+#include "stm32.h"
+#include "usb.h"
+
+#if defined(USBD_STM32L552)
+
+#define USB_PMAADDR USB_PMAADDR_NS
+
+#ifndef USB_PMASIZE
+ #pragma message "PMA memory size is not defined. Use 1k by default"
+ #define USB_PMASIZE 0x400
+#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 + USB_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 + USB_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->APB1ENR2 & RCC_APB1ENR2_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->APB1ENR2 |= RCC_APB1ENR2_USBFSEN;
+ RCC->APB1RSTR2 |= RCC_APB1RSTR2_USBFSRST;
+ RCC->APB1RSTR2 &= ~RCC_APB1RSTR2_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->APB1ENR2 & RCC_APB1ENR2_USBFSEN) {
+ USB->BCDR = 0;
+ RCC->APB1RSTR2 |= RCC_APB1RSTR2_USBFSRST;
+ RCC->APB1ENR2 &= ~RCC_APB1ENR2_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*)(USB_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*)(USB_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_STM32L052
diff --git a/src/usbd_stm32l552_devfs_asm.S b/src/usbd_stm32l552_devfs_asm.S
new file mode 100644
index 0000000..c300333
--- /dev/null
+++ b/src/usbd_stm32l552_devfs_asm.S
@@ -0,0 +1,861 @@
+/* This file is the part of the Lightweight USB device Stack for STM32 microcontrollers
+ *
+ * Copyright ©2016 Dmitry Filimonchuk
+ *
+ * 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.
+ */
+
+#if !defined (__ASSEMBLER__)
+ #define __ASSEMBLER__
+#endif
+
+#include "usb.h"
+#if defined(USBD_STM32L552)
+#include "memmap.inc"
+
+#define EP_SETUP 0x0800
+#define EP_TYPE 0x0600
+#define EP_KIND 0x0100
+#define EP_ADDR 0x000F
+
+#define EP_RX_CTR 0x8000
+#define EP_RX_DTOG 0x4000
+#define EP_RX_STAT 0x3000
+#define EP_RX_SWBUF 0x0040
+
+#define EP_RX_DIS 0x0000
+#define EP_RX_STAL 0x1000
+#define EP_RX_NAK 0x2000
+#define EP_RX_VAL 0x3000
+
+#define EP_TX_CTR 0x0080
+#define EP_TX_DTOG 0x0040
+#define EP_TX_STAT 0x0030
+#define EP_TX_SWBUF 0x4000
+
+#define EP_TX_DIS 0x0000
+#define EP_TX_STAL 0x0010
+#define EP_TX_NAK 0x0020
+#define EP_TX_VAL 0x0030
+
+#define RXADDR0 0x00
+#define RXCOUNT0 0x02
+#define RXADDR1 0x04
+#define RXCOUNT1 0x06
+
+#define TXADDR0 0x00
+#define TXCOUNT0 0x02
+#define TXADDR1 0x04
+#define TXCOUNT1 0x06
+
+#define TXADDR 0x00
+#define TXCOUNT 0x02
+#define RXADDR 0x04
+#define RXCOUNT 0x06
+
+#if defined(STM32F070x6) || defined(STM32F070xB) || \
+ defined(STM32F042x6) || defined(STM32F048xx) || \
+ defined(STM32F072xB) || defined(STM32F078xx)
+ #define UID_OFFSET_0 0x00
+ #define UID_OFFSET_1 0x04
+ #define UID_OFFSET_2 0x08
+#else
+ #define UID_OFFSET_0 0x00
+ #define UID_OFFSET_1 0x04
+ #define UID_OFFSET_2 0x14
+#endif
+
+#define EP_NOTOG (EP_RX_CTR | EP_TX_CTR | EP_SETUP | EP_TYPE | EP_KIND | EP_ADDR)
+
+#define TGL_SET(mask, bits) ((EP_NOTOG | (mask))<<16 | (bits))
+
+#define TX_STALL TGL_SET(EP_TX_STAT, EP_TX_STAL)
+#define RX_STALL TGL_SET(EP_RX_STAT, EP_RX_STAL)
+#define TX_USTALL TGL_SET(EP_TX_STAT | EP_TX_DTOG, EP_TX_NAK)
+#define RX_USTALL TGL_SET(EP_RX_STAT | EP_RX_DTOG, EP_RX_VAL)
+#define DTX_USTALL TGL_SET(EP_TX_STAT | EP_TX_DTOG | EP_TX_SWBUF, EP_TX_VAL)
+#define DRX_USTALL TGL_SET(EP_RX_STAT | EP_RX_DTOG | EP_RX_SWBUF, EP_RX_VAL | EP_RX_SWBUF)
+
+
+ .syntax unified
+ .cpu cortex-m0plus
+ .thumb
+
+ .section .rodata.usbd_devfs_asm
+ .align 4
+ .globl usbd_devfs_asm
+usbd_devfs_asm:
+ .long _getinfo
+ .long _enable
+ .long _connect
+ .long _setaddr
+ .long _ep_config
+ .long _ep_deconfig
+ .long _ep_read
+ .long _ep_write
+ .long _ep_setstall
+ .long _ep_isstalled
+ .long _evt_poll
+ .long _get_frame
+ .long _get_serial_desc
+ .size usbd_devfs_asm, . - usbd_devfs_asm
+
+ .text
+ .align 2
+ .thumb_func
+ .type _get_serial_desc, %function
+
+/* uint16_t get_serial_desc (void *buffer)
+ * R0 <- buffer for the string descriptor
+ * descrpitor size -> R0
+ */
+_get_serial_desc:
+ push {r4, r5, lr}
+ movs r1,18 //descriptor size 18 bytes
+ strb r1,[r0]
+ movs r1, #0x03 //DTYPE_STRING
+ strb r1,[r0, #0x01]
+ ldr r5, .L_uid_base //UID3 this is the serial number
+ ldr r4, .L_fnv1a_offset //FNV1A offset
+ ldr r2, [r5, UID_OFFSET_0] //UID0
+ bl .L_fnv1a
+ ldr r2, [r5, UID_OFFSET_1] //UID1
+ bl .L_fnv1a
+ ldr r2, [r5, UID_OFFSET_2] //UID2
+ bl .L_fnv1a
+ movs r3, #28
+.L_gsn_loop:
+ movs r1, r4
+ lsrs r1, r3
+ lsls r1, #28
+ lsrs r1, #28
+ adds r1, #0x30 //'0'
+ cmp r1, #0x3A
+ blo .L_gsn_store
+ adds r1, #0x07 //'A' - '0'
+.L_gsn_store:
+ adds r0, #0x02
+ strb r1, [r0]
+ lsrs r1, #0x08
+ strb r1, [r0, #0x01]
+ subs r3, #0x04
+ bpl .L_gsn_loop
+ movs r0, #18
+ pop {r4, r5, pc}
+
+.L_fnv1a:
+ movs r3, #0x04
+.L_fnv1a_loop:
+ uxtb r1, r2
+ eors r4, r1
+ ldr r1, .L_fnv1a_prime //FNV1A prime
+ muls r4, r1
+ lsrs r2, #0x08
+ subs r3, #0x01
+ bne .L_fnv1a_loop
+ bx lr
+
+ .align 2
+.L_fnv1a_prime: .long 16777619
+.L_fnv1a_offset: .long 2166136261
+.L_uid_base: .long UID_BASE
+
+ .size _get_serial_desc, . - _get_serial_desc
+
+ .thumb_func
+ .type _connect, %function
+_connect:
+ ldr r3, =USB_REGBASE
+ movs r1, #0x03 //BCDEN + DCDEN
+ movs r2, #usbd_lane_dsc
+ strh r1, [r3, #USB_BCDR]
+ ldrh r1, [r3, #USB_BCDR]
+ lsrs r1, #0x05 //DCDET->CF
+ bcc .L_connect
+ movs r1, #0x05 //BCDEN + PDEN
+ movs r2, #usbd_lane_unk
+ strh r1, [r3, #USB_BCDR]
+ ldrh r1, [r3, #USB_BCDR]
+ lsls r1, #25 //PS2DET->CF
+ bcs .L_connect
+ movs r2, #usbd_lane_sdp
+ lsls r1, #2 //PDET->CF
+ bcc .L_connect
+ movs r1, #0x09 //BCDEN + SDET
+ movs r2, #usbd_lane_cdp
+ strh r1, [r3, #USB_BCDR]
+ ldrh r1, [r3, #USB_BCDR]
+ lsrs r1, #7 //SDET->CF
+ bcc .L_connect
+ movs r2, #usbd_lane_dcp
+.L_connect:
+ subs r1, r0, #1
+ sbcs r0, r1
+ lsls r0, #15
+ strh r0, [r3, #USB_BCDR]
+ mov r0, r2
+ bx lr
+ .size _connect, . - _connect
+
+ .thumb_func
+ .type _setaddr, %function
+_setaddr:
+ ldr r1, =USB_REGBASE
+ adds r0, #0x80
+ strh r0, [r1, #USB_DADDR] //USB->DADDR
+ bx lr
+ .size _setaddr, . - _setaddr
+
+ .thumb_func
+ .type _get_frame, %function
+_get_frame:
+ ldr r0, =USB_REGBASE
+ ldrh r0, [r0, #USB_FNR] //FNR
+ lsls r0, #21
+ lsrs r0, #21
+ bx lr
+ .size _get_frame, . - _get_frame
+
+ .thumb_func
+ .type _enable, %function
+_enable:
+ ldr r1, =USB_REGBASE //USB->CNTR
+ ldr r2, =RCC_BASE //RCC
+ movs r3, #0x01
+ lsls r3, #RCC_USBEN //USBEN or USBRST
+ tst r0, r0
+ beq .L_disable
+.L_enable:
+ ldr r0, [r2, #RCC_APB1ENR2]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1ENR2] //RCC->RCC_APB1ENR2 |= USBEN
+ ldr r0, [r2, #RCC_APB1RSTR2]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1RSTR2] //RCC->APB1RSTR |= USBRST
+ bics r0, r3
+ str r0, [r2, #RCC_APB1RSTR2] //RCC->APB1RSTR &= ~USBRST
+
+#if defined(USBD_PINS_REMAP) && (defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F070x6))
+ ldr r0, [r2, #RCC_APB2ENR]
+ movs r3, #0x01
+ orrs r0, r3
+ str r0, [r2, #RCC_APB2ENR] //RCC->APB2ENR |= RCC_APB2ENR_SYSCFGCOMPEN
+
+ ldr r2, =SYSCFG_BASE //SYSCFG->CFGR1
+ ldr r0, [r2]
+ lsls r3, #SYSCFG_USBREMAP
+ orrs r0, r3
+ str r0, [r2] //SYSCFG->CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP; // remap USB pins for small packages
+#endif
+
+#if !defined(USBD_SOF_DISABLED)
+ movs r0, #0xBE // CTRM | ERRM | WKUPM | SUSPM | RESETM | SOFM
+#else
+ movs r0, #0xBC // CTRM | ERRM | WKUPM | SUSPM | RESETM
+#endif
+ lsls r0, #0x08
+ strh r0, [r1] //set USB->CNTR
+ bx lr
+.L_disable:
+ ldr r0, [r2, #RCC_APB1ENR2]
+ tst r0, r3
+ beq .L_enable_end // usb is disabled
+ movs r0, #0x00
+ strh r0, [r1, #USB_BCDR] //USB->BCDR disable USB I/O
+ ldr r0, [r2, #RCC_APB1RSTR2]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1RSTR2] //RCC->APB1RSTR |= USBRST
+ ldr r0, [r2, #RCC_APB1ENR2]
+ bics r0, r3
+ str r0, [r2, #RCC_APB1ENR2] //RCC->APB1ENR &= ~USBEN
+.L_enable_end:
+ bx lr
+ .size _enable, . - _enable
+
+ .thumb_func
+ .type _getinfo, %function
+_getinfo:
+ movs r0, #USBD_HW_BC
+ ldr r2, =RCC_BASE
+ ldr r1, [r2, #RCC_APB1ENR2]
+ lsrs r1, #24 //USBEN -> CF
+ bcc .L_getinfo_end
+ adds r0, #USBD_HW_ENABLED
+ ldr r2, =USB_REGBASE
+ ldr r1, [r2, #USB_BCDR]
+ lsrs r1, #15 //DPPU -> CF
+ bcc .L_getinfo_end
+ adds r0, #USBD_HW_SPEED_FS
+.L_getinfo_end:
+ bx lr
+ .size _getinfo, . - _getinfo
+
+ .thumb_func
+ .type _ep_setstall, %function
+/*void ep_settall(uint8_t ep, bool stall)
+ * in R0 <- endpoint number
+ * in R1 <- 0 if unstall, !0 if stall
+ */
+_ep_setstall:
+ push {r4, lr}
+ lsls r2, r0, #28
+ lsrs r2, #26
+ ldr r3, =USB_EPBASE
+ adds r3, r2 // epr -> r3
+ movs r2, 0x30 // TX_STAT_MASK -> r2
+ ldrh r4, [r3]
+ lsls r4, #21
+ lsrs r4, #29 // EP_TYPE | EP_KIND -> R4 LSB
+ cmp r4, #0x04 // ISO ?
+ beq .L_eps_exit
+ cmp r0, #0x80
+ blo .L_eps_rx
+.L_eps_tx:
+ ldr r0, =TX_STALL //stall TX
+ cmp r1, #0x00
+ bne .L_eps_reg_set
+.L_eps_tx_unstall:
+ ldr r0, =DTX_USTALL //unstall dblbulk or iso TX (VALID and clr DTOG_TX & SWBUF_TX)
+ cmp r4, #0x01 // if doublebuffered bulk endpoint
+ beq .L_eps_reg_set
+ ldr r0, =TX_USTALL // unstall other TX (NAKED + clr DTOG_TX)
+ b .L_eps_reg_set
+.L_eps_rx:
+ lsls r2, #8 // RX_STAT_MASK -> R2
+ ldr r0,=RX_STALL //stall RX
+ cmp r1, #0x00
+ bne .L_eps_reg_set
+.L_eps_rx_unstall:
+ ldr r0, =DRX_USTALL //unstall dblbulk or iso (VALID. clr DTOG_RX set SWBUF_RX)
+ cmp r4, #0x01 // if dblbulk
+ beq .L_eps_reg_set
+ ldr r0, =RX_USTALL // unstall other RX (VALID + clr
+/* R0 - mask and toggle bits
+ * R2 - mask for STAT bits
+ * R3 - endpoint register pointer
+ */
+.L_eps_reg_set:
+ ldrh r1, [r3] // *epr -> r1
+ ands r2, r1 // check if endpoint disabled
+ beq .L_eps_exit // do nothing
+ eors r1, r0
+ lsrs r0, #16
+ ands r1, r0
+ strh r1, [r3]
+.L_eps_exit:
+ pop {r4, pc}
+ .size _ep_setstall, . - _ep_setstall
+
+
+ .thumb_func
+ .type _ep_isstalled, %function
+/* bool ep_isstalled(uint8t ep) */
+_ep_isstalled:
+ ldr r1, =USB_EPBASE
+ lsls r2, r0, #28
+ lsrs r2, #26
+ ldr r1, [r1, r2]
+ lsls r1, #17
+ cmp r0, #0x80
+ bhs .L_eis_check
+ lsls r1, #8
+.L_eis_check:
+ lsrs r1, r1, #28
+ subs r1, #0x01
+ subs r0, r1, #0x01
+ sbcs r1, r1
+ rsbs r0, r1, #0
+ bx lr
+ .size _ep_isstalled, . - _ep_isstalled
+
+
+ .thumb_func
+ .type _ep_read, %function
+/* int32_t _ep_read(uint8_t ep, void *buf, uint16_t blen)
+ * in R0 <- endpoint
+ * in R1 <- *buffer
+ * in R2 <- length of the buffer
+ * out length of the recieved data -> R0 or -1 on error
+ */
+_ep_read:
+ push {r4, r5, lr}
+ ldr r3, =USB_EPBASE
+ ldr r4, =USB_PMABASE
+ lsls r0, #28
+ lsrs r0, #26
+ adds r3, r0 // *EPR -> R3
+ lsls r0, #1
+ adds r4, r0 // *EPT -> R4
+ ldrh r5, [r3] // reading epr
+/* validating endpoint */
+ movs r0, #0x37
+ lsls r0, #0x08
+ ands r0, r5
+ lsrs r0, #0x08
+ cmp r0, #0x34 // (OK) RX_VALID + ISO
+ beq .L_epr_iso
+ cmp r0, #0x31 // (OK) RX_VALID + DBLBULK
+ beq .L_epr_dbl
+ cmp r0, #0x20 // (OK) RX_NAKED + BULK
+ beq .L_epr_sngl
+ cmp r0, #0x22 // (OK) RX_NAKED + CTRL
+ beq .L_epr_sngl
+ cmp r0, #0x26 // (OK) RX_NAKED + INTR
+ beq .L_epr_sngl
+ movs r0, #0xFF // endpoint contains no valid data
+ sxtb r0, r0
+ b .L_epr_exit
+/* processing */
+.L_epr_dbl:
+ lsrs r0, r5, #8
+ eors r0, r5
+ lsrs r0, #7 // SW_RX ^ DTOG_RX -> CF
+ bcs .L_epr_notog // jmp if SW_RX != DTOG_RX (VALID)
+ ldr r0, =EP_NOTOG
+ ands r5, r0
+ adds r5, #EP_RX_SWBUF
+ strh r5, [r3] // toggling SW_RX
+.L_epr_notog:
+ ldrh r5, [r3]
+ lsls r5, #8 // shift SW_RX to DTOG_RX
+.L_epr_iso:
+ lsrs r5, #15 // DTOG_RX -> CF
+ bcs .L_epr_sngl
+ subs r4, #0x04 // set RXADDR0
+.L_epr_sngl:
+ ldrh r0, [r4, #RXCOUNT]
+ lsrs r5, r0, #0x0A
+ lsls r5, #0x0A // r5 = r0 & ~0x03FF
+ strh r5, [r4, #RXCOUNT]
+ lsls r0, #22
+ lsrs r0, #22 // r0 &= 0x3FF (RX count)
+ ldrh r5, [r4, #RXADDR]
+ ldr r4, =USB_PMABASE
+ adds r5, r4 // R5 now has a physical address
+ cmp r2, r0
+ blo .L_epr_read
+ mov r2, r0 // if buffer is larger
+.L_epr_read:
+ cmp r2, #1
+ blo .L_epr_read_end
+ ldrh r4, [r5]
+ strb r4, [r1]
+ beq .L_epr_read_end
+ lsrs r4, #8
+ strb r4, [r1, #1]
+ adds r1, #2
+ adds r5, #2
+ subs r2, #2
+ bhi .L_epr_read
+.L_epr_read_end:
+ ldrh r5, [r3] // reload EPR
+ lsls r1, r5, #21
+ lsrs r1, #29
+ cmp r1, #0x04
+ beq .L_epr_exit // ep is iso. no needs to set it to valid
+ cmp r1, #0x01
+ beq .L_epr_exit // ep is dblbulk. no needs to set it to valid
+ ldr r2, =TGL_SET(EP_RX_STAT , EP_RX_VAL)
+ eors r5, r2
+ lsrs r2, #16
+ ands r5, r2
+ strh r5, [r3] // set ep to VALID state
+.L_epr_exit:
+ pop {r4, r5, pc}
+ .size _ep_read, . - _ep_read
+
+
+
+ .thumb_func
+ .type _ep_write, %function
+/* int32_t ep_write(uint8_t ep, void *buf, uint16_t blen)
+ * R0 -> endpoint
+ * R1 -> *buffer
+ * R2 -> data length
+ *
+ */
+_ep_write:
+ push {r4, r5, r6, lr}
+ ldr r3, =USB_EPBASE
+ ldr r4, =USB_PMABASE
+ lsls r0, #28
+ lsrs r0, #26
+ adds r3, r0 // *EPR -> R3
+ lsls r0, #1
+ adds r4, r0 // TXADDR0 -> R4
+ ldrh r5, [r3] // reading epr
+ movs r0, #0x73
+ lsls r0, #4
+ ands r0, r5
+ lsrs r0, #4
+ cmp r0, #0x43 // (OK) TX_VALID + ISO
+ beq .L_epw_iso
+ cmp r0, #0x12 // (OK) TX_NAK + DBLBULK
+ beq .L_epw_dbl
+ cmp r0, #0x02 // (OK) TX_NAK + BULK
+ beq .L_epw_sngl
+ cmp r0, #0x22 // (OK) TX_NAK + CONTROL
+ beq .L_epw_sngl
+ cmp r0, #0x62 // (OK) TX_NAK + INTERRUPT
+ beq .L_epw_sngl
+ movs r0, #0xFF
+ sxtb r0, r0
+ b .L_epw_exit
+.L_epw_dbl:
+ mvns r5, r5
+ lsrs r5, #8 // ~SWBUF_TX -> DTOG_TX
+.L_epw_iso:
+ lsrs r5, #7 // DTOG_TX -> CF
+ bcs .L_epw_sngl
+ adds r4, #4 // TXADDR1 -> R4
+.L_epw_sngl:
+ strh r2, [r4, #TXCOUNT]
+ mov r0, r2 // save count for return
+ ldrh r5, [r4, #TXADDR]
+ ldr r4, =USB_PMABASE
+ adds r5, r4 // PMA BUFFER -> R5
+.L_epw_write:
+ cmp r2, #1
+ blo .L_epw_write_end
+ ldrb r4, [r1]
+ beq .L_epw_store
+ ldrb r6, [r1, #1]
+ lsls r6, #8
+ orrs r4, r6
+.L_epw_store:
+ strh r4, [r5]
+ adds r5, #2
+ adds r1, #2
+ subs r2, #2
+ bhi .L_epw_write
+.L_epw_write_end:
+ ldrh r5, [r3] // reload EPR
+ lsls r1, r5, #21
+ lsrs r1, #29
+ cmp r1, #0x04
+ beq .L_epw_exit // isochronous ep. do nothing
+ ldr r2, =TGL_SET(EP_TX_STAT, EP_TX_VAL)
+ cmp r1, #0x01
+ bne .L_epw_setstate // NOT a doublebuffered bulk
+ ldr r2, =TGL_SET(EP_TX_SWBUF, EP_TX_SWBUF)
+ bics r5, r2 // clear TX_SWBUF
+.L_epw_setstate:
+ eors r5, r2
+ lsrs r2, #16
+ ands r5, r2
+ strh r5, [r3]
+.L_epw_exit:
+ pop {r4, r5, r6, pc}
+ .size _ep_write, .- _ep_write
+
+/* internal function */
+/* requester size passed in R2 */
+/* result returns in R0 CF=1 if OK*/
+
+_get_next_pma:
+ push {r1, r3, r4, lr}
+ movs r1, #0x3C
+ movs r3, #1
+ lsls r3, #10 //R3 MAX_PMA_SIZE
+ ldr r0, =USB_PMABASE
+.L_gnp_chkaddr:
+ ldrh r4, [r0, r1]
+ tst r4, r4
+ beq .L_gnp_nxtaddr
+ cmp r3, r4
+ blo .L_gnp_nxtaddr
+ mov r3, r4
+.L_gnp_nxtaddr:
+ subs r1, #0x04
+ bhs .L_gnp_chkaddr
+ subs r0, r3, r2
+ blo .L_gnp_exit
+ cmp r0, #0x20 //check for the pma table overlap
+.L_gnp_exit:
+ pop {r1, r3, r4, pc}
+ .size _get_next_pma, . - _get_next_pma
+
+ .thumb_func
+ .type _ep_config, %function
+/* bool ep_config(uint8_t ep, uint8_t eptype, uint16_t epsize)
+ * R0 <- ep
+ * R1 <- eptype
+ * R2 <- epsize
+ * result -> R0
+ */
+_ep_config:
+ push {r4, r5, lr}
+ movs r3, 0x01
+ ands r3, r2
+ adds r2, r3 //R2 -> halfword aligned epsize
+ movs r3, #0x00 //BULK
+ cmp r1, #0x02 // is eptype bulk ?
+ beq .L_epc_settype
+ movs r3, #0x01 //DBLBULK
+ cmp r1, #0x06
+ beq .L_epc_settype
+ movs r3, #0x02 //CONTROL
+ cmp r1, #0x00
+ beq .L_epc_settype
+ movs r3, #0x04 //ISO
+ cmp r1, #0x01
+ beq .L_epc_settype
+ movs r3, #0x06 //INTERRUPT
+.L_epc_settype:
+ lsls r3, #8
+ lsls r4, r0, #28
+ lsrs r4, #28
+ orrs r3, r4
+ lsls r4, #2
+ ldr r5, =USB_EPBASE
+ strh r3, [r5, r4] //setup EPTYPE EPKIND EPADDR
+ cmp r1, #0x00 // is a control ep ?
+ beq .L_epc_setuptx
+ cmp r0, #0x80
+ blo .L_epc_setuprx
+.L_epc_setuptx:
+ ldr r5, =USB_PMABASE
+ lsls r4, #1
+ adds r5, r4
+/* setup buffer table */
+/* TX or TX0 */
+ bl _get_next_pma
+ bcc .L_epc_fail
+ strh r0, [r5, #TXADDR] //store txaddr or txaddr0
+ movs r0, #0x00
+ strh r0, [r5, #TXCOUNT] //store txcnt
+ cmp r1, #0x06 // is DBLBULK
+ beq .L_epc_txdbl
+ ldr r3, =TX_USTALL //set state NAKED , clr DTOG_TX
+ cmp r1, #0x01
+ bne .L_epc_txsetstate //if single buffered
+.L_epc_txdbl:
+/* TX1 */
+ ldr r3, =DTX_USTALL //set state VALID clr DTOG_TX & SWBUF_TX
+ bl _get_next_pma
+ bcc .L_epc_fail
+ strh r0, [r5, #TXADDR1] //store txaddr1
+ movs r0, #0x00
+ strh r0, [r5, #TXCOUNT1] //store txcnt
+.L_epc_txsetstate:
+ ldr r5, =USB_EPBASE
+ lsrs r4, #1
+ ldrh r0, [r5, r4]
+ eors r0, r3
+ lsrs r3, #16
+ ands r0, r3
+ strh r0, [r5, r4]
+ cmp r1, #0x00 //is a control ep ?
+ bne .L_epc_exit
+.L_epc_setuprx:
+/* calculating RX_COUNT field. result in R3*/
+ movs r3, r2
+ cmp r2, #62
+ bls .L_epc_rxbb
+ /* ep size must be 32-byte aligned if >= 64 */
+ movs r3, #0x1F
+ adds r2, r3
+ bics r2, r3
+ lsrs r3, r2, #4
+ adds r3, #0x3E
+.L_epc_rxbb:
+ lsls r3, #9
+ ldr r5, =USB_PMABASE
+ lsls r4, #1
+ adds r5, r4
+/* setup buffer table */
+ bl _get_next_pma
+ bcc .L_epc_fail
+/* set RX or RX1 */
+ strh r0, [r5, #RXADDR]
+ strh r3, [r5, #RXCOUNT]
+ ldr r0, =RX_USTALL
+/* check if doublebuffered */
+ cmp r1, 0x06 //if dblbulk
+ beq .L_epc_rxdbl
+ cmp r1, 0x01 // iso
+ bne .L_epc_rxsetstate
+.L_epc_rxdbl:
+ bl _get_next_pma
+ bcc .L_epc_fail
+ strh r0, [r5, #RXADDR0] //store rxaddr0
+ strh r3, [r5, #RXCOUNT0] //store rxcnt0
+ ldr r0, =DRX_USTALL
+.L_epc_rxsetstate:
+ ldr r5, =USB_EPBASE
+ lsrs r4, #1
+ ldrh r3, [r5, r4]
+ eors r3, r0
+ lsrs r0, #16
+ ands r3, r0
+ strh r3, [r5, r4]
+.L_epc_exit:
+ movs r0, #0x01
+ pop {r4, r5, pc}
+.L_epc_fail:
+ movs r0, #0x00
+ pop {r4, r5, pc}
+
+ .size _ep_config, . - _ep_config
+
+ .thumb_func
+ .type _ep_deconfig, %function
+
+
+/* void ep_deconfig( uint8_t ep)
+ * R0 <- ep
+ */
+_ep_deconfig:
+ lsls r1, r0, #28
+ lsrs r1, #26
+ ldr r2, =USB_EPBASE
+ ldr r3, =USB_PMABASE
+ adds r2, r1
+ lsls r1, #1
+ adds r3, r1
+/* clearing endpoint register */
+ ldr r1, =EP_NOTOG
+ ldrh r0, [r2]
+ bics r0, r1
+ strh r0, [r2]
+/* clearing PMA data */
+ movs r0, #0x00
+ strh r0, [r3, #TXADDR]
+ strh r0, [r3, #TXCOUNT]
+ strh r0, [r3, #RXADDR]
+ strh r0, [r3, #RXCOUNT]
+ bx lr
+ .size _ep_deconfig, . - _ep_config
+
+
+#define ISTRSHIFT 8
+#define ISTRBIT(bit) ((1 << bit) >> ISTRSHIFT)
+
+
+ .thumb_func
+ .type _evt_poll, %function
+/*void evt_poll(usbd_device *dev, usbd_evt_callback callback)*/
+_evt_poll:
+ push {r0, r1, r4, r5}
+ ldr r3, =USB_REGBASE
+ ldrh r0, [r3, #4] //USB->ISTR -> R2
+/* ep_index -> R2 */
+ movs r2, 0x07
+ ands r2, r0
+/* checking USB->ISTR for events */
+#if !defined(USBD_SOF_DISABLED)
+ lsrs r1, r0, #10 //SOFM -> CF
+ bcs .L_ep_sofm
+#endif
+ lsrs r1, r0, #16 //CTRM -> CF
+ bcs .L_ep_ctrm
+ lsrs r1, r0, #14 //ERRM -> CF
+ bcs .L_ep_errm
+ lsrs r1, r0, #13 //WKUPM -> CF
+ bcs .L_ep_wkupm
+ lsrs r1, r0, #12 //SUSPM -> CF
+ bcs .L_ep_suspm
+ lsrs r1, r0, #11 //RESETM -> CF
+ bcs .L_ep_resetm
+ /* exit with no callback */
+ pop {r0, r1, r4 , r5}
+ bx lr
+
+.L_ep_ctrm:
+ movs r5, #0x80 // CTR_TX mask to R5
+ ldr r0,=USB_EPBASE
+ lsrs r0, #2
+ adds r0, r2
+ lsls r0, #2 // R0 ep register address
+ ldrh r4, [r0] // R4 EPR valur
+ lsrs r3, r4, #8 // CTR_TX -> CF
+ bcc .L_ep_ctr_rx
+/* CTR_TX event */
+ movs r1, #usbd_evt_eptx
+ orrs r2, r5 // set endpoint tx
+ b .L_ep_clr_ctr
+.L_ep_ctr_rx:
+/* CTR_RX RX or SETUP */
+ lsls r5, #0x08 // set mask to CRT_RX
+ movs r1, #usbd_evt_eprx
+ lsls r3, r4, #21 //SETUP -> CF
+ bcc .L_ep_clr_ctr
+ movs r1, #usbd_evt_epsetup
+.L_ep_clr_ctr:
+ bics r4, r5 //clear CTR flag
+ ldr r5, =EP_NOTOG
+ ands r4, r5
+ strh r4, [r0] // store
+ b .L_ep_callback
+.L_ep_errm:
+ movs r1, #usbd_evt_error
+ movs r4, #ISTRBIT(13)
+ b .L_ep_clristr
+#if !defined(USBD_SOF_DISABLED)
+.L_ep_sofm:
+ movs r1, #usbd_evt_sof
+ movs r4, #ISTRBIT(9)
+ b .L_ep_clristr
+#endif
+.L_ep_wkupm:
+ ldrh r1, [r3, #USB_CNTR] //R1 USB->CNTR
+ movs r5, #0x08
+ bics r1, r5 //clr FSUSP
+ strh r1, [r3, #USB_CNTR] //USB->CNTR R2
+ movs r1, #usbd_evt_wkup
+ movs r4, #ISTRBIT(12)
+ b .L_ep_clristr
+
+.L_ep_suspm:
+ ldrh r1, [r3, #USB_CNTR] //R1 USB->CNTR
+ movs r5, #0x08
+ orrs r1, r5 //set FSUSP
+ strh r1, [r3, #USB_CNTR] //USB->CNTR R2
+ movs r1, #usbd_evt_susp
+ movs r4, #ISTRBIT(11)
+ b .L_ep_clristr
+/* do reset routine */
+.L_ep_resetm:
+ movs r1, #7
+ ldr r2, =USB_EPBASE
+ ldr r0, =USB_PMABASE
+ ldr r5, =EP_NOTOG
+.L_ep_reset_loop:
+ ldrh r4, [r2]
+ bics r4, r5
+ strh r4, [r2]
+ movs r4, #0x00
+ strh r4, [r0, #TXADDR]
+ strh r4, [r0, #TXCOUNT]
+ strh r4, [r0, #RXADDR]
+ strh r4, [r0, #RXCOUNT]
+ adds r2, #4
+ adds r0, #8
+ subs r1, #1
+ bhs .L_ep_reset_loop
+ strh r4, [r3, #USB_BTABLE]
+ movs r1, #usbd_evt_reset
+ movs r4, #ISTRBIT(10)
+.L_ep_clristr:
+ lsls r4, #ISTRSHIFT
+ ldrh r0, [r3, #4]
+ bics r0, r4
+ strh r0, [r3, #4]
+.L_ep_callback:
+ pop {r0, r3, r4, r5 }
+ bx r3
+ .size _evt_poll, . - _evt_poll
+
+ .pool
+
+ .end
+
+#endif //USBD_STM32L052