summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsakumisu <[email protected]>2021-10-15 23:02:42 +0800
committersakumisu <[email protected]>2021-10-16 15:15:25 +0800
commit4397f9009cdafd1ed65f0d6b0f304c55a92c9213 (patch)
tree5fc2d115e891954366a2fe26c091d92295634e1f
parent0672902d62afb696f6f049bbe83213571d5533eb (diff)
add mcu port without test,just for reference
-rw-r--r--port/README.md1
-rw-r--r--port/atmel/usb_dc_sam.c924
-rw-r--r--port/atmel/usb_dc_sam0.c728
-rw-r--r--port/atmel/usbd_ATSAM3U2C.c807
-rw-r--r--port/dw/usb_dc_dw.c1212
-rw-r--r--port/freescale/usb_dc_kinetis.c1048
-rw-r--r--port/freescale/usbd_kinetis.c819
-rw-r--r--port/maxim/max32620/usbd_max32620.c622
-rw-r--r--port/maxim/max32625/usbd_max32625.c647
-rw-r--r--port/nordic/usb_dc_nrfx.c1928
-rw-r--r--port/nuvoton/m48ssidae/usbd_m480.c835
-rw-r--r--port/nxp/lpc11u35/usbd_LPC11Uxx.c825
-rw-r--r--port/nxp/lpc4322/usbd_LPC43xx_USB0.c901
-rw-r--r--port/stm32/back/usb_dc_stm32.c1111
-rw-r--r--port/stm32/back/usbd_STM32F103.c770
-rw-r--r--port/stm32/back/usbd_STM32F205.c669
-rw-r--r--port/stm32/usbd_stm32f103_devfs.c549
-rw-r--r--port/stm32/usbd_stm32f103_devfs_asm.S866
-rw-r--r--port/stm32/usbd_stm32f105_otgfs.c485
-rw-r--r--port/stm32/usbd_stm32f429_otgfs.c482
-rw-r--r--port/stm32/usbd_stm32f429_otghs.c486
-rw-r--r--port/stm32/usbd_stm32f446_otgfs.c467
-rw-r--r--port/stm32/usbd_stm32f446_otghs.c467
-rw-r--r--port/stm32/usbd_stm32l052_devfs.c472
-rw-r--r--port/stm32/usbd_stm32l052_devfs_asm.S850
-rw-r--r--port/stm32/usbd_stm32l100_devfs.c457
-rw-r--r--port/stm32/usbd_stm32l100_devfs_asm.S831
-rw-r--r--port/stm32/usbd_stm32l433_devfs.c473
-rw-r--r--port/stm32/usbd_stm32l476_otgfs.c500
29 files changed, 21232 insertions, 0 deletions
diff --git a/port/README.md b/port/README.md
new file mode 100644
index 00000000..6535aaf2
--- /dev/null
+++ b/port/README.md
@@ -0,0 +1 @@
+## 提取 port 接口备用 \ No newline at end of file
diff --git a/port/atmel/usb_dc_sam.c b/port/atmel/usb_dc_sam.c
new file mode 100644
index 00000000..e4744239
--- /dev/null
+++ b/port/atmel/usb_dc_sam.c
@@ -0,0 +1,924 @@
+/*
+ * Copyright (c) 2018 Aurelien Jarno <[email protected]>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT atmel_sam_usbhs
+
+#include <usb/usb_device.h>
+#include <soc.h>
+#include <string.h>
+
+#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
+#include <logging/log.h>
+LOG_MODULE_REGISTER(usb_dc_sam);
+
+/*
+ * This is defined in the support files for the SAM S7x, but not for
+ * the SAM E7x nor SAM V7x.
+ */
+#ifndef USBHS_RAM_ADDR
+#define USBHS_RAM_ADDR (0xA0100000)
+#endif
+
+/*
+ * The new Atmel DFP headers provide mode-specific interrupt register field
+ * definitions. Map the existing generic definitions to these.
+ */
+#ifndef USBHS_DEVEPTISR_CTRL_RXSTPI
+#define USBHS_DEVEPTISR_CTRL_RXSTPI USBHS_DEVEPTISR_RXSTPI
+#endif
+#ifndef USBHS_DEVEPTICR_CTRL_RXSTPIC
+#define USBHS_DEVEPTICR_CTRL_RXSTPIC USBHS_DEVEPTICR_RXSTPIC
+#endif
+#ifndef USBHS_DEVEPTIMR_CTRL_STALLRQ
+#define USBHS_DEVEPTIMR_CTRL_STALLRQ USBHS_DEVEPTIMR_STALLRQ
+#endif
+#ifndef USBHS_DEVEPTIER_CTRL_RXSTPES
+#define USBHS_DEVEPTIER_CTRL_RXSTPES USBHS_DEVEPTIER_RXSTPES
+#endif
+#ifndef USBHS_DEVEPTIER_CTRL_STALLRQS
+#define USBHS_DEVEPTIER_CTRL_STALLRQS USBHS_DEVEPTIER_STALLRQS
+#endif
+#ifndef USBHS_DEVEPTIDR_CTRL_STALLRQC
+#define USBHS_DEVEPTIDR_CTRL_STALLRQC USBHS_DEVEPTIDR_STALLRQC
+#endif
+
+#define NUM_OF_EP_MAX DT_INST_PROP(0, num_bidir_endpoints)
+#if DT_INST_NODE_HAS_PROP(0, maximum_speed)
+#define USB_MAXIMUM_SPEED DT_ENUM_IDX(DT_DRV_INST(0), maximum_speed)
+#else
+#define USB_MAXIMUM_SPEED 2 /* Default to high-speed */
+#endif
+
+struct usb_device_ep_data {
+ uint16_t mps;
+ usb_dc_ep_callback cb_in;
+ usb_dc_ep_callback cb_out;
+ uint8_t *fifo;
+};
+
+struct usb_device_data {
+ bool addr_enabled;
+ usb_dc_status_callback status_cb;
+ struct usb_device_ep_data ep_data[NUM_OF_EP_MAX];
+};
+
+static struct usb_device_data dev_data;
+
+/* Enable the USB device clock */
+static void usb_dc_enable_clock(void)
+{
+ /* Start the USB PLL */
+ PMC->CKGR_UCKR |= CKGR_UCKR_UPLLEN;
+
+ /* Wait for it to be ready */
+ while (!(PMC->PMC_SR & PMC_SR_LOCKU)) {
+ k_yield();
+ }
+
+ /* In low power mode, provide a 48MHZ clock instead of the 480MHz one */
+ if ((USBHS->USBHS_DEVCTRL & USBHS_DEVCTRL_SPDCONF_Msk)
+ == USBHS_DEVCTRL_SPDCONF_LOW_POWER) {
+ /* Configure the USB_48M clock to be UPLLCK/10 */
+ PMC->PMC_MCKR &= ~PMC_MCKR_UPLLDIV2;
+ PMC->PMC_USB = PMC_USB_USBDIV(9) | PMC_USB_USBS;
+
+ /* Enable USB_48M clock */
+ PMC->PMC_SCER |= PMC_SCER_USBCLK;
+ }
+}
+
+/* Disable the USB device clock */
+static void usb_dc_disable_clock(void)
+{
+ /* Disable USB_48M clock */
+ PMC->PMC_SCER &= ~PMC_SCER_USBCLK;
+
+ /* Disable the USB PLL */
+ PMC->CKGR_UCKR &= ~CKGR_UCKR_UPLLEN;
+}
+
+/* Check if the USB device is attached */
+static bool usb_dc_is_attached(void)
+{
+ return (USBHS->USBHS_DEVCTRL & USBHS_DEVCTRL_DETACH) == 0;
+}
+
+/* Check if an endpoint is configured */
+static bool usb_dc_ep_is_configured(uint8_t ep_idx)
+{
+ return USBHS->USBHS_DEVEPTISR[ep_idx] & USBHS_DEVEPTISR_CFGOK;
+}
+
+/* Check if an endpoint is enabled */
+static bool usb_dc_ep_is_enabled(uint8_t ep_idx)
+{
+ return USBHS->USBHS_DEVEPT & BIT(USBHS_DEVEPT_EPEN0_Pos + ep_idx);
+}
+
+/* Reset and endpoint */
+static void usb_dc_ep_reset(uint8_t ep_idx)
+{
+ USBHS->USBHS_DEVEPT |= BIT(USBHS_DEVEPT_EPRST0_Pos + ep_idx);
+ USBHS->USBHS_DEVEPT &= ~BIT(USBHS_DEVEPT_EPRST0_Pos + ep_idx);
+ __DSB();
+}
+
+/* Enable endpoint interrupts, depending of the type and direction */
+static void usb_dc_ep_enable_interrupts(uint8_t ep_idx)
+{
+ if (ep_idx == 0U) {
+ /* Control endpoint: enable SETUP and OUT */
+ USBHS->USBHS_DEVEPTIER[ep_idx] = USBHS_DEVEPTIER_CTRL_RXSTPES;
+ USBHS->USBHS_DEVEPTIER[ep_idx] = USBHS_DEVEPTIER_RXOUTES;
+ } else if ((USBHS->USBHS_DEVEPTCFG[ep_idx] & USBHS_DEVEPTCFG_EPDIR_Msk)
+ == USBHS_DEVEPTCFG_EPDIR_IN) {
+ /* IN direction: acknowledge FIFO empty interrupt */
+ USBHS->USBHS_DEVEPTICR[ep_idx] = USBHS_DEVEPTICR_TXINIC;
+ USBHS->USBHS_DEVEPTIER[ep_idx] = USBHS_DEVEPTIER_TXINES;
+ } else {
+ /* OUT direction */
+ USBHS->USBHS_DEVEPTIER[ep_idx] = USBHS_DEVEPTIER_RXOUTES;
+ }
+}
+
+/* Reset the endpoint FIFO pointer to the beginning of the endpoint memory */
+static void usb_dc_ep_fifo_reset(uint8_t ep_idx)
+{
+ uint8_t *p;
+
+ p = (uint8_t *)(USBHS_RAM_ADDR + 0x8000 * ep_idx);
+ dev_data.ep_data[ep_idx].fifo = p;
+}
+
+/* Fetch a byte from the endpoint FIFO */
+static uint8_t usb_dc_ep_fifo_get(uint8_t ep_idx)
+{
+ return *(dev_data.ep_data[ep_idx].fifo++);
+}
+
+/* Put a byte from the endpoint FIFO */
+static void usb_dc_ep_fifo_put(uint8_t ep_idx, uint8_t data)
+{
+ *(dev_data.ep_data[ep_idx].fifo++) = data;
+}
+
+/* Handle interrupts on a control endpoint */
+static void usb_dc_ep0_isr(void)
+{
+ uint32_t sr = USBHS->USBHS_DEVEPTISR[0] & USBHS->USBHS_DEVEPTIMR[0];
+ uint32_t dev_ctrl = USBHS->USBHS_DEVCTRL;
+
+ if (sr & USBHS_DEVEPTISR_CTRL_RXSTPI) {
+ /* SETUP data received */
+ usb_dc_ep_fifo_reset(0);
+ dev_data.ep_data[0].cb_out(USB_EP_DIR_OUT, USB_DC_EP_SETUP);
+ }
+ if (sr & USBHS_DEVEPTISR_RXOUTI) {
+ /* OUT (to device) data received */
+ usb_dc_ep_fifo_reset(0);
+ dev_data.ep_data[0].cb_out(USB_EP_DIR_OUT, USB_DC_EP_DATA_OUT);
+ }
+ if (sr & USBHS_DEVEPTISR_TXINI) {
+ /* Disable the interrupt */
+ USBHS->USBHS_DEVEPTIDR[0] = USBHS_DEVEPTIDR_TXINEC;
+
+ /* IN (to host) transmit complete */
+ usb_dc_ep_fifo_reset(0);
+ dev_data.ep_data[0].cb_in(USB_EP_DIR_IN, USB_DC_EP_DATA_IN);
+
+ if (!(dev_ctrl & USBHS_DEVCTRL_ADDEN) &&
+ (dev_ctrl & USBHS_DEVCTRL_UADD_Msk) != 0U) {
+ /* Commit the pending address update. This
+ * must be done after the ack to the host
+ * completes else the ack will get dropped.
+ */
+ USBHS->USBHS_DEVCTRL = dev_ctrl | USBHS_DEVCTRL_ADDEN;
+ }
+ }
+}
+
+/* Handle interrupts on a non-control endpoint */
+static void usb_dc_ep_isr(uint8_t ep_idx)
+{
+ uint32_t sr = USBHS->USBHS_DEVEPTISR[ep_idx] &
+ USBHS->USBHS_DEVEPTIMR[ep_idx];
+
+ if (sr & USBHS_DEVEPTISR_RXOUTI) {
+ uint8_t ep = ep_idx | USB_EP_DIR_OUT;
+
+ /* Acknowledge the interrupt */
+ USBHS->USBHS_DEVEPTICR[ep_idx] = USBHS_DEVEPTICR_RXOUTIC;
+
+ /* OUT (to device) data received */
+ usb_dc_ep_fifo_reset(ep_idx);
+ dev_data.ep_data[ep_idx].cb_out(ep, USB_DC_EP_DATA_OUT);
+ }
+ if (sr & USBHS_DEVEPTISR_TXINI) {
+ uint8_t ep = ep_idx | USB_EP_DIR_IN;
+
+ /* Acknowledge the interrupt */
+ USBHS->USBHS_DEVEPTICR[ep_idx] = USBHS_DEVEPTICR_TXINIC;
+
+ /* IN (to host) transmit complete */
+ usb_dc_ep_fifo_reset(ep_idx);
+ dev_data.ep_data[ep_idx].cb_in(ep, USB_DC_EP_DATA_IN);
+ }
+}
+
+/* Top level interrupt handler */
+static void usb_dc_isr(void)
+{
+ uint32_t sr = USBHS->USBHS_DEVISR & USBHS->USBHS_DEVIMR;
+
+ /* End of resume interrupt */
+ if (sr & USBHS_DEVISR_EORSM) {
+ /* Acknowledge the interrupt */
+ USBHS->USBHS_DEVICR = USBHS_DEVICR_EORSMC;
+
+ /* Callback function */
+ dev_data.status_cb(USB_DC_RESUME, NULL);
+ }
+
+ /* End of reset interrupt */
+ if (sr & USBHS_DEVISR_EORST) {
+ /* Acknowledge the interrupt */
+ USBHS->USBHS_DEVICR = USBHS_DEVICR_EORSTC;
+
+ if (usb_dc_ep_is_enabled(0)) {
+ /* The device clears some of the configuration of EP0
+ * when it receives the EORST. Re-enable interrupts.
+ */
+ usb_dc_ep_enable_interrupts(0);
+ }
+
+ /* Free all endpoint memory */
+ for (int idx = 1; idx < NUM_OF_EP_MAX; idx++) {
+ usb_dc_ep_disable(idx);
+ USBHS->USBHS_DEVEPTCFG[idx] &= ~USBHS_DEVEPTCFG_ALLOC;
+ }
+
+ /* Callback function */
+ dev_data.status_cb(USB_DC_RESET, NULL);
+ }
+
+ /* Suspend interrupt */
+ if (sr & USBHS_DEVISR_SUSP) {
+ /* Acknowledge the interrupt */
+ USBHS->USBHS_DEVICR = USBHS_DEVICR_SUSPC;
+
+ /* Callback function */
+ dev_data.status_cb(USB_DC_SUSPEND, NULL);
+ }
+
+#ifdef CONFIG_USB_DEVICE_SOF
+ /* SOF interrupt */
+ if (sr & USBHS_DEVISR_SOF) {
+ /* Acknowledge the interrupt */
+ USBHS->USBHS_DEVICR = USBHS_DEVICR_SOFC;
+
+ /* Callback function */
+ dev_data.status_cb(USB_DC_SOF, NULL);
+ }
+#endif
+
+ /* EP0 endpoint interrupt */
+ if (sr & USBHS_DEVISR_PEP_0) {
+ usb_dc_ep0_isr();
+ }
+
+ /* Other endpoints interrupt */
+ for (int ep_idx = 1; ep_idx < NUM_OF_EP_MAX; ep_idx++) {
+ if (sr & BIT(USBHS_DEVISR_PEP_0_Pos + ep_idx)) {
+ usb_dc_ep_isr(ep_idx);
+ }
+ }
+}
+
+/* Attach USB for device connection */
+int usb_dc_attach(void)
+{
+ uint32_t regval;
+
+ /* Start the peripheral clock */
+ soc_pmc_peripheral_enable(DT_INST_PROP(0, peripheral_id));
+
+ /* Enable the USB controller in device mode with the clock frozen */
+ USBHS->USBHS_CTRL = USBHS_CTRL_UIMOD | USBHS_CTRL_USBE |
+ USBHS_CTRL_FRZCLK;
+ __DSB();
+
+ /* Select the speed */
+ regval = USBHS_DEVCTRL_DETACH;
+#if USB_MAXIMUM_SPEED == 0
+ /* low-speed */
+ regval |= USBHS_DEVCTRL_LS;
+ regval |= USBHS_DEVCTRL_SPDCONF_LOW_POWER;
+#elif USB_MAXIMUM_SPEED == 1
+ /* full-speed */
+ regval |= USBHS_DEVCTRL_SPDCONF_LOW_POWER;
+#elif USB_MAXIMUM_SPEED == 2
+ /* high-speed */
+ regval |= USBHS_DEVCTRL_SPDCONF_NORMAL;
+#else
+#error "Unsupported maximum speed defined in device tree."
+#endif
+ USBHS->USBHS_DEVCTRL = regval;
+
+ /* Enable the USB clock */
+ usb_dc_enable_clock();
+
+ /* Unfreeze the clock */
+ USBHS->USBHS_CTRL = USBHS_CTRL_UIMOD | USBHS_CTRL_USBE;
+
+ /* Enable device interrupts */
+ USBHS->USBHS_DEVIER = USBHS_DEVIER_EORSMES;
+ USBHS->USBHS_DEVIER = USBHS_DEVIER_EORSTES;
+ USBHS->USBHS_DEVIER = USBHS_DEVIER_SUSPES;
+#ifdef CONFIG_USB_DEVICE_SOF
+ USBHS->USBHS_DEVIER = USBHS_DEVIER_SOFES;
+#endif
+
+ /* Connect and enable the interrupt */
+ IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
+ usb_dc_isr, 0, 0);
+ irq_enable(DT_INST_IRQN(0));
+
+ /* Attach the device */
+ USBHS->USBHS_DEVCTRL &= ~USBHS_DEVCTRL_DETACH;
+
+ LOG_DBG("");
+ return 0;
+}
+
+/* Detach the USB device */
+int usb_dc_detach(void)
+{
+ /* Detach the device */
+ USBHS->USBHS_DEVCTRL &= ~USBHS_DEVCTRL_DETACH;
+
+ /* Disable the USB clock */
+ usb_dc_disable_clock();
+
+ /* Disable the USB controller and freeze the clock */
+ USBHS->USBHS_CTRL = USBHS_CTRL_UIMOD | USBHS_CTRL_FRZCLK;
+
+ /* Disable the peripheral clock */
+ soc_pmc_peripheral_enable(DT_INST_PROP(0, peripheral_id));
+
+ /* Disable interrupt */
+ irq_disable(DT_INST_IRQN(0));
+
+ LOG_DBG("");
+ return 0;
+}
+
+/* Reset the USB device */
+int usb_dc_reset(void)
+{
+ /* Reset the controller */
+ USBHS->USBHS_CTRL = USBHS_CTRL_UIMOD | USBHS_CTRL_FRZCLK;
+
+ /* Clear private data */
+ (void)memset(&dev_data, 0, sizeof(dev_data));
+
+ LOG_DBG("");
+ return 0;
+}
+
+/* Set USB device address */
+int usb_dc_set_address(uint8_t addr)
+{
+ /*
+ * Set the address but keep it disabled for now. It should be enabled
+ * only after the ack to the host completes.
+ */
+ USBHS->USBHS_DEVCTRL &= ~(USBHS_DEVCTRL_UADD_Msk | USBHS_DEVCTRL_ADDEN);
+ USBHS->USBHS_DEVCTRL |= USBHS_DEVCTRL_UADD(addr);
+ LOG_DBG("");
+
+ return 0;
+}
+
+/* Set USB device controller status callback */
+void usb_dc_set_status_callback(const usb_dc_status_callback cb)
+{
+ LOG_DBG("");
+
+ dev_data.status_cb = cb;
+}
+
+/* Check endpoint capabilities */
+int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data * const cfg)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(cfg->ep_addr);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ if (ep_idx == 0U) {
+ if (cfg->ep_type != USB_DC_EP_CONTROL) {
+ LOG_ERR("pre-selected as control endpoint");
+ return -1;
+ }
+ } else if (ep_idx & BIT(0)) {
+ if (USB_EP_GET_DIR(cfg->ep_addr) != USB_EP_DIR_IN) {
+ LOG_INF("pre-selected as IN endpoint");
+ return -1;
+ }
+ } else {
+ if (USB_EP_GET_DIR(cfg->ep_addr) != USB_EP_DIR_OUT) {
+ LOG_INF("pre-selected as OUT endpoint");
+ return -1;
+ }
+ }
+
+ if (cfg->ep_mps < 1 || cfg->ep_mps > 1024 ||
+ (cfg->ep_type == USB_DC_EP_CONTROL && cfg->ep_mps > 64)) {
+ LOG_ERR("invalid endpoint size");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Configure endpoint */
+int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data *const cfg)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(cfg->ep_addr);
+ bool ep_configured[NUM_OF_EP_MAX];
+ bool ep_enabled[NUM_OF_EP_MAX];
+ uint32_t regval = 0U;
+ int log2ceil_mps;
+
+ if (usb_dc_ep_check_cap(cfg) != 0) {
+ return -EINVAL;
+ }
+
+ if (!usb_dc_is_attached()) {
+ LOG_ERR("device not attached");
+ return -ENODEV;
+ }
+
+ if (usb_dc_ep_is_enabled(ep_idx)) {
+ LOG_WRN("endpoint already configured & enabled 0x%x", ep_idx);
+ return -EBUSY;
+ }
+
+ LOG_INF("Configure ep %x, mps %d, type %d", cfg->ep_addr, cfg->ep_mps,
+ cfg->ep_type);
+
+ /* Reset the endpoint */
+ usb_dc_ep_reset(ep_idx);
+
+ /* Map the endpoint type */
+ switch (cfg->ep_type) {
+ case USB_DC_EP_CONTROL:
+ regval |= USBHS_DEVEPTCFG_EPTYPE_CTRL;
+ break;
+ case USB_DC_EP_ISOCHRONOUS:
+ regval |= USBHS_DEVEPTCFG_EPTYPE_ISO;
+ break;
+ case USB_DC_EP_BULK:
+ regval |= USBHS_DEVEPTCFG_EPTYPE_BLK;
+ break;
+ case USB_DC_EP_INTERRUPT:
+ regval |= USBHS_DEVEPTCFG_EPTYPE_INTRPT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Map the endpoint direction */
+ if (USB_EP_DIR_IS_OUT(cfg->ep_addr) ||
+ cfg->ep_type == USB_DC_EP_CONTROL) {
+ regval |= USBHS_DEVEPTCFG_EPDIR_OUT;
+ } else {
+ regval |= USBHS_DEVEPTCFG_EPDIR_IN;
+ }
+
+ /*
+ * Map the endpoint size to the buffer size. Only power of 2 buffer
+ * sizes between 8 and 1024 are possible, get the next power of 2.
+ */
+ log2ceil_mps = 32 - __builtin_clz((MAX(cfg->ep_mps, 8) << 1) - 1) - 1;
+ regval |= USBHS_DEVEPTCFG_EPSIZE(log2ceil_mps - 3);
+ dev_data.ep_data[ep_idx].mps = cfg->ep_mps;
+
+ /* Use double bank buffering for isochronous endpoints */
+ if (cfg->ep_type == USB_DC_EP_ISOCHRONOUS) {
+ regval |= USBHS_DEVEPTCFG_EPBK_2_BANK;
+ } else {
+ regval |= USBHS_DEVEPTCFG_EPBK_1_BANK;
+ }
+
+ /* Configure the endpoint */
+ USBHS->USBHS_DEVEPTCFG[ep_idx] = regval;
+
+ /*
+ * Allocate the memory. This part is a bit tricky as memory can only be
+ * allocated if all above endpoints are disabled and not allocated. Loop
+ * backward through the above endpoints, disable them if they are
+ * enabled, deallocate their memory if needed. Then loop again through
+ * all the above endpoints to allocate and enabled them.
+ */
+ for (int i = NUM_OF_EP_MAX - 1; i > ep_idx; i--) {
+ ep_configured[i] = usb_dc_ep_is_configured(i);
+ ep_enabled[i] = usb_dc_ep_is_enabled(i);
+
+ if (ep_enabled[i]) {
+ LOG_INF("Temporary disable ep idx %x", i);
+ usb_dc_ep_disable(i);
+ }
+ if (ep_configured[i]) {
+ USBHS->USBHS_DEVEPTCFG[i] &= ~USBHS_DEVEPTCFG_ALLOC;
+ }
+ }
+ ep_configured[ep_idx] = true;
+ ep_enabled[ep_idx] = false;
+ for (int i = ep_idx; i < NUM_OF_EP_MAX; i++) {
+ if (ep_configured[i]) {
+ USBHS->USBHS_DEVEPTCFG[i] |= USBHS_DEVEPTCFG_ALLOC;
+ }
+ if (ep_enabled[i]) {
+ usb_dc_ep_enable(i);
+ }
+ }
+
+ /* Check that the endpoint is correctly configured */
+ if (!usb_dc_ep_is_configured(ep_idx)) {
+ LOG_ERR("endpoint configurationf failed");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Set stall condition for the selected endpoint */
+int usb_dc_ep_set_stall(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ USBHS->USBHS_DEVEPTIER[ep_idx] = USBHS_DEVEPTIER_CTRL_STALLRQS;
+
+ LOG_DBG("ep 0x%x", ep);
+ return 0;
+}
+
+/* Clear stall condition for the selected endpoint */
+int usb_dc_ep_clear_stall(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ USBHS->USBHS_DEVEPTIDR[ep_idx] = USBHS_DEVEPTIDR_CTRL_STALLRQC;
+
+ LOG_DBG("ep 0x%x", ep);
+ return 0;
+}
+
+/* Check if the selected endpoint is stalled */
+int usb_dc_ep_is_stalled(uint8_t ep, uint8_t *stalled)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (!stalled) {
+ return -EINVAL;
+ }
+
+ *stalled = (USBHS->USBHS_DEVEPTIMR[ep_idx] &
+ USBHS_DEVEPTIMR_CTRL_STALLRQ) != 0;
+
+ LOG_DBG("ep 0x%x", ep);
+ return 0;
+}
+
+/* Halt the selected endpoint */
+int usb_dc_ep_halt(uint8_t ep)
+{
+ return usb_dc_ep_set_stall(ep);
+}
+
+/* Enable the selected endpoint */
+int usb_dc_ep_enable(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (!usb_dc_ep_is_configured(ep_idx)) {
+ LOG_ERR("endpoint not configured");
+ return -ENODEV;
+ }
+
+ /* Enable endpoint */
+ USBHS->USBHS_DEVEPT |= BIT(USBHS_DEVEPT_EPEN0_Pos + ep_idx);
+
+ /* Enable endpoint interrupts */
+ USBHS->USBHS_DEVIER = BIT(USBHS_DEVIER_PEP_0_Pos + ep_idx);
+
+ /* Enable SETUP, IN or OUT endpoint interrupts */
+ usb_dc_ep_enable_interrupts(ep_idx);
+
+ LOG_INF("Enable ep 0x%x", ep);
+
+ return 0;
+}
+
+/* Disable the selected endpoint */
+int usb_dc_ep_disable(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ /* Disable endpoint interrupt */
+ USBHS->USBHS_DEVIDR = BIT(USBHS_DEVIDR_PEP_0_Pos + ep_idx);
+
+ /* Disable endpoint and SETUP, IN or OUT interrupts */
+ USBHS->USBHS_DEVEPT &= ~BIT(USBHS_DEVEPT_EPEN0_Pos + ep_idx);
+
+ LOG_INF("Disable ep 0x%x", ep);
+
+ return 0;
+}
+
+/* Flush the selected endpoint */
+int usb_dc_ep_flush(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (!usb_dc_ep_is_enabled(ep_idx)) {
+ LOG_ERR("endpoint not enabled");
+ return -ENODEV;
+ }
+
+ /* Disable the IN interrupt */
+ USBHS->USBHS_DEVEPTIDR[ep_idx] = USBHS_DEVEPTIDR_TXINEC;
+
+ /* Kill the last written bank if needed */
+ if (USBHS->USBHS_DEVEPTISR[ep_idx] & USBHS_DEVEPTISR_NBUSYBK_Msk) {
+ USBHS->USBHS_DEVEPTIER[ep_idx] = USBHS_DEVEPTIER_KILLBKS;
+ __DSB();
+ while (USBHS->USBHS_DEVEPTIMR[ep_idx] &
+ USBHS_DEVEPTIMR_KILLBK) {
+ k_yield();
+ }
+ }
+
+ /* Reset the endpoint */
+ usb_dc_ep_reset(ep_idx);
+
+ /* Reenable interrupts */
+ usb_dc_ep_enable_interrupts(ep_idx);
+
+ LOG_DBG("ep 0x%x", ep);
+ return 0;
+}
+
+/* Write data to the specified endpoint */
+int usb_dc_ep_write(uint8_t ep, const uint8_t *data, uint32_t data_len, uint32_t *ret_bytes)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint32_t packet_len;
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (!usb_dc_ep_is_enabled(ep_idx)) {
+ LOG_ERR("endpoint not enabled");
+ return -ENODEV;
+ }
+
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_IN) {
+ LOG_ERR("wrong endpoint direction");
+ return -EINVAL;
+ }
+
+ if ((USBHS->USBHS_DEVEPTIMR[ep_idx] & USBHS_DEVEPTIMR_CTRL_STALLRQ)
+ != 0) {
+ LOG_WRN("endpoint is stalled");
+ return -EBUSY;
+ }
+
+ /* Write the data to the FIFO */
+ packet_len = MIN(data_len, dev_data.ep_data[ep_idx].mps);
+ for (int i = 0; i < packet_len; i++) {
+ usb_dc_ep_fifo_put(ep_idx, data[i]);
+ }
+ __DSB();
+
+ if (ep_idx == 0U) {
+ /*
+ * Control endpoint: clear the interrupt flag to send the data,
+ * and re-enable the interrupts to trigger an interrupt at the
+ * end of the transfer.
+ */
+ USBHS->USBHS_DEVEPTICR[ep_idx] = USBHS_DEVEPTICR_TXINIC;
+ USBHS->USBHS_DEVEPTIER[ep_idx] = USBHS_DEVEPTIER_TXINES;
+ } else {
+ /*
+ * Other endpoint types: clear the FIFO control flag to send
+ * the data.
+ */
+ USBHS->USBHS_DEVEPTIDR[ep_idx] = USBHS_DEVEPTIDR_FIFOCONC;
+ }
+
+ if (ret_bytes) {
+ *ret_bytes = packet_len;
+ }
+
+ LOG_DBG("ep 0x%x write %d bytes from %d", ep, packet_len, data_len);
+ return 0;
+}
+
+/* Read data from the specified endpoint */
+int usb_dc_ep_read(uint8_t ep, uint8_t *data, uint32_t max_data_len, uint32_t *read_bytes)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ int rc;
+
+ rc = usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes);
+
+ if (rc) {
+ return rc;
+ }
+
+ if (!data && !max_data_len) {
+ /* When both buffer and max data to read are zero the above
+ * call would fetch the data len and we simply return.
+ */
+ return 0;
+ }
+
+ /* If the packet has been read entirely, get the next one */
+ if (!(USBHS->USBHS_DEVEPTISR[ep_idx] & USBHS_DEVEPTISR_RWALL)) {
+ rc = usb_dc_ep_read_continue(ep);
+ }
+
+ LOG_DBG("ep 0x%x", ep);
+ return rc;
+}
+
+/* Set callback function for the specified endpoint */
+int usb_dc_ep_set_callback(uint8_t ep, const usb_dc_ep_callback cb)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (USB_EP_DIR_IS_IN(ep)) {
+ dev_data.ep_data[ep_idx].cb_in = cb;
+ } else {
+ dev_data.ep_data[ep_idx].cb_out = cb;
+ }
+
+ LOG_DBG("ep 0x%x", ep);
+ return 0;
+}
+
+/* Read data from the specified endpoint */
+int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
+ uint32_t *read_bytes)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint32_t data_len = (USBHS->USBHS_DEVEPTISR[ep_idx] &
+ USBHS_DEVEPTISR_BYCT_Msk) >> USBHS_DEVEPTISR_BYCT_Pos;
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (!usb_dc_ep_is_enabled(ep_idx)) {
+ LOG_ERR("endpoint not enabled");
+ return -ENODEV;
+ }
+
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_OUT) {
+ LOG_ERR("wrong endpoint direction");
+ return -EINVAL;
+ }
+
+ if ((USBHS->USBHS_DEVEPTIMR[ep_idx] & USBHS_DEVEPTIMR_CTRL_STALLRQ)
+ != 0) {
+ LOG_WRN("endpoint is stalled");
+ return -EBUSY;
+ }
+
+ if (!data && !max_data_len) {
+ /*
+ * When both buffer and max data to read are zero return
+ * the available data in buffer.
+ */
+ if (read_bytes) {
+ *read_bytes = data_len;
+ }
+ return 0;
+ }
+
+ if (data_len > max_data_len) {
+ LOG_WRN("Not enough space to copy all the data!");
+ data_len = max_data_len;
+ }
+
+ if (data != NULL) {
+ for (int i = 0; i < data_len; i++) {
+ data[i] = usb_dc_ep_fifo_get(ep_idx);
+ }
+ }
+
+ if (read_bytes) {
+ *read_bytes = data_len;
+ }
+
+ LOG_DBG("ep 0x%x read %d bytes", ep, data_len);
+ return 0;
+}
+
+/* Continue reading data from the endpoint */
+int usb_dc_ep_read_continue(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (!usb_dc_ep_is_enabled(ep_idx)) {
+ LOG_ERR("endpoint not enabled");
+ return -ENODEV;
+ }
+
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_OUT) {
+ LOG_ERR("wrong endpoint direction");
+ return -EINVAL;
+ }
+
+ if (ep_idx == 0U) {
+ /*
+ * Control endpoint: clear the interrupt flag to send the data.
+ * It is easier to clear both SETUP and OUT flag than checking
+ * the stage of the transfer.
+ */
+ USBHS->USBHS_DEVEPTICR[ep_idx] = USBHS_DEVEPTICR_RXOUTIC;
+ USBHS->USBHS_DEVEPTICR[ep_idx] = USBHS_DEVEPTICR_CTRL_RXSTPIC;
+ } else {
+ /*
+ * Other endpoint types: clear the FIFO control flag to
+ * receive more data.
+ */
+ USBHS->USBHS_DEVEPTIDR[ep_idx] = USBHS_DEVEPTIDR_FIFOCONC;
+ }
+
+ LOG_DBG("ep 0x%x continue", ep);
+ return 0;
+}
+
+/* Endpoint max packet size (mps) */
+int usb_dc_ep_mps(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= NUM_OF_EP_MAX) {
+ LOG_ERR("wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ return dev_data.ep_data[ep_idx].mps;
+}
diff --git a/port/atmel/usb_dc_sam0.c b/port/atmel/usb_dc_sam0.c
new file mode 100644
index 00000000..911ab799
--- /dev/null
+++ b/port/atmel/usb_dc_sam0.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright (c) 2018 Google LLC.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT atmel_sam0_usb
+
+#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
+#include <logging/log.h>
+LOG_MODULE_REGISTER(usb_dc_sam0);
+
+#include <usb/usb_device.h>
+#include <soc.h>
+#include <string.h>
+
+#define NVM_USB_PAD_TRANSN_POS 45
+#define NVM_USB_PAD_TRANSN_SIZE 5
+#define NVM_USB_PAD_TRANSP_POS 50
+#define NVM_USB_PAD_TRANSP_SIZE 5
+#define NVM_USB_PAD_TRIM_POS 55
+#define NVM_USB_PAD_TRIM_SIZE 3
+
+#define USB_SAM0_IN_EP 0x80
+
+#define REGS ((Usb *)DT_INST_REG_ADDR(0))
+#define USB_NUM_ENDPOINTS DT_INST_PROP(0, num_bidir_endpoints)
+
+/* The endpoint size stored in USB.PCKSIZE.SIZE */
+enum usb_sam0_pcksize_size {
+ USB_SAM0_PCKSIZE_SIZE_8 = 0,
+ USB_SAM0_PCKSIZE_SIZE_16,
+ USB_SAM0_PCKSIZE_SIZE_32,
+ USB_SAM0_PCKSIZE_SIZE_64,
+ USB_SAM0_PCKSIZE_SIZE_128,
+ USB_SAM0_PCKSIZE_SIZE_256,
+ USB_SAM0_PCKSIZE_SIZE_512,
+ USB_SAM0_PCKSIZE_SIZE_1023,
+};
+
+static const uint16_t usb_sam0_pcksize_bytes[] = {
+ [USB_SAM0_PCKSIZE_SIZE_8] = 8,
+ [USB_SAM0_PCKSIZE_SIZE_16] = 16,
+ [USB_SAM0_PCKSIZE_SIZE_32] = 32,
+ [USB_SAM0_PCKSIZE_SIZE_64] = 64,
+ [USB_SAM0_PCKSIZE_SIZE_128] = 128,
+ [USB_SAM0_PCKSIZE_SIZE_256] = 256,
+ [USB_SAM0_PCKSIZE_SIZE_512] = 512,
+ [USB_SAM0_PCKSIZE_SIZE_1023] = 1023,
+};
+
+BUILD_ASSERT(ARRAY_SIZE(usb_sam0_pcksize_bytes) == 8);
+
+struct usb_sam0_data {
+ UsbDeviceDescriptor descriptors[USB_NUM_ENDPOINTS];
+
+ usb_dc_status_callback cb;
+ usb_dc_ep_callback ep_cb[2][USB_NUM_ENDPOINTS];
+
+ uint8_t addr;
+ uint32_t out_at;
+};
+
+static struct usb_sam0_data usb_sam0_data_0;
+
+static struct usb_sam0_data *usb_sam0_get_data(void)
+{
+ return &usb_sam0_data_0;
+}
+
+/* Handles interrupts on an endpoint */
+static void usb_sam0_ep_isr(uint8_t ep)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+ UsbDevice *regs = &REGS->DEVICE;
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep];
+ uint32_t intflag = endpoint->EPINTFLAG.reg;
+
+ endpoint->EPINTFLAG.reg = intflag;
+
+ if ((intflag & USB_DEVICE_EPINTFLAG_RXSTP) != 0U) {
+ /* Setup */
+ data->ep_cb[0][ep](ep, USB_DC_EP_SETUP);
+ }
+
+ if ((intflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0U) {
+ /* Out (to device) data received */
+ data->ep_cb[0][ep](ep, USB_DC_EP_DATA_OUT);
+ }
+
+ if ((intflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0U) {
+ /* In (to host) transmit complete */
+ data->ep_cb[1][ep](ep | USB_SAM0_IN_EP, USB_DC_EP_DATA_IN);
+
+ if (data->addr != 0U) {
+ /* Commit the pending address update. This
+ * must be done after the ack to the host
+ * completes else the ack will get dropped.
+ */
+ regs->DADD.reg = data->addr;
+ data->addr = 0U;
+ }
+ }
+}
+
+/* Top level interrupt handler */
+static void usb_sam0_isr(void)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+ UsbDevice *regs = &REGS->DEVICE;
+ uint32_t intflag = regs->INTFLAG.reg;
+ uint32_t epint = regs->EPINTSMRY.reg;
+ uint8_t ep;
+
+ /* Acknowledge all interrupts */
+ regs->INTFLAG.reg = intflag;
+
+ if ((intflag & USB_DEVICE_INTFLAG_EORST) != 0U) {
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[0];
+
+ /* The device clears some of the configuration of EP0
+ * when it receives the EORST. Re-enable interrupts.
+ */
+ endpoint->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 |
+ USB_DEVICE_EPINTENSET_TRCPT1 |
+ USB_DEVICE_EPINTENSET_RXSTP;
+
+ data->cb(USB_DC_RESET, NULL);
+ }
+
+ /* Dispatch the endpoint interrupts */
+ for (ep = 0U; epint != 0U; epint >>= 1) {
+ /* Scan bit-by-bit as the Cortex-M0 doesn't have ffs */
+ if ((epint & 1) != 0U) {
+ usb_sam0_ep_isr(ep);
+ }
+ ep++;
+ }
+}
+
+/* Wait for the device to process the last config write */
+static void usb_sam0_wait_syncbusy(void)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+
+ while (regs->SYNCBUSY.reg != 0) {
+ }
+}
+
+/* Load the pad calibration from the built-in fuses */
+static void usb_sam0_load_padcal(void)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+ uint32_t pad_transn;
+ uint32_t pad_transp;
+ uint32_t pad_trim;
+
+#ifdef USB_FUSES_TRANSN_ADDR
+ pad_transn = *(uint32_t *)USB_FUSES_TRANSN_ADDR;
+#else
+ pad_transn = (*((uint32_t *)(NVMCTRL_OTP4) +
+ (NVM_USB_PAD_TRANSN_POS / 32)) >>
+ (NVM_USB_PAD_TRANSN_POS % 32)) &
+ ((1 << NVM_USB_PAD_TRANSN_SIZE) - 1);
+
+ if (pad_transn == 0x1F) {
+ pad_transn = 5U;
+ }
+#endif
+
+ regs->PADCAL.bit.TRANSN = pad_transn;
+
+#ifdef USB_FUSES_TRANSP_ADDR
+ pad_transp = *(uint32_t *)USB_FUSES_TRANSP_ADDR;
+#else
+ pad_transp = (*((uint32_t *)(NVMCTRL_OTP4) +
+ (NVM_USB_PAD_TRANSP_POS / 32)) >>
+ (NVM_USB_PAD_TRANSP_POS % 32)) &
+ ((1 << NVM_USB_PAD_TRANSP_SIZE) - 1);
+
+ if (pad_transp == 0x1F) {
+ pad_transp = 29U;
+ }
+#endif
+
+ regs->PADCAL.bit.TRANSP = pad_transp;
+
+#ifdef USB_FUSES_TRIM_ADDR
+ pad_trim = *(uint32_t *)USB_FUSES_TRIM_ADDR;
+#else
+ pad_trim = (*((uint32_t *)(NVMCTRL_OTP4) +
+ (NVM_USB_PAD_TRIM_POS / 32)) >>
+ (NVM_USB_PAD_TRIM_POS % 32)) &
+ ((1 << NVM_USB_PAD_TRIM_SIZE) - 1);
+
+ if (pad_trim == 0x7) {
+ pad_trim = 3U;
+ }
+#endif
+
+ regs->PADCAL.bit.TRIM = pad_trim;
+}
+
+#define SAM0_USB_IRQ_CONNECT(n) \
+ do { \
+ IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, n, irq), \
+ DT_INST_IRQ_BY_IDX(0, n, priority), \
+ usb_sam0_isr, 0, 0); \
+ irq_enable(DT_INST_IRQ_BY_IDX(0, n, irq)); \
+ } while (0)
+
+/* Attach by initializing the device */
+int usb_dc_attach(void)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+ struct usb_sam0_data *data = usb_sam0_get_data();
+
+#ifdef MCLK
+ /* Enable the clock in MCLK */
+ MCLK->APBBMASK.bit.USB_ = 1;
+
+ /* Enable the GCLK - use 48 MHz source */
+ GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN(2)
+ | GCLK_PCHCTRL_CHEN;
+
+ while (GCLK->SYNCBUSY.reg) {
+ }
+#else
+ /* Enable the clock in PM */
+ PM->APBBMASK.bit.USB_ = 1;
+
+ /* Enable the GCLK */
+ GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_USB | GCLK_CLKCTRL_GEN_GCLK0 |
+ GCLK_CLKCTRL_CLKEN;
+
+ while (GCLK->STATUS.bit.SYNCBUSY) {
+ }
+#endif /* !MCLK */
+
+ /* Configure */
+ regs->CTRLA.bit.SWRST = 1;
+ usb_sam0_wait_syncbusy();
+
+ /* Change QOS values to have the best performance and correct USB
+ * behaviour
+ */
+ regs->QOSCTRL.bit.CQOS = 2;
+ regs->QOSCTRL.bit.DQOS = 2;
+
+ usb_sam0_load_padcal();
+
+ regs->CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_RUNSTDBY;
+ regs->CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_HS;
+
+ (void)memset(data->descriptors, 0, sizeof(data->descriptors));
+ regs->DESCADD.reg = (uintptr_t)&data->descriptors[0];
+
+ regs->INTENSET.reg = USB_DEVICE_INTENSET_EORST;
+
+ /* Connect and enable the interrupt */
+#if DT_INST_IRQ_HAS_CELL(0, irq)
+ SAM0_USB_IRQ_CONNECT(0);
+#endif
+#if DT_INST_IRQ_HAS_IDX(0, 1)
+ SAM0_USB_IRQ_CONNECT(1);
+#endif
+#if DT_INST_IRQ_HAS_IDX(0, 2)
+ SAM0_USB_IRQ_CONNECT(2);
+#endif
+#if DT_INST_IRQ_HAS_IDX(0, 3)
+ SAM0_USB_IRQ_CONNECT(3);
+#endif
+
+ /* Enable and attach */
+ regs->CTRLA.bit.ENABLE = 1;
+ usb_sam0_wait_syncbusy();
+ regs->CTRLB.bit.DETACH = 0;
+
+ return 0;
+}
+
+/* Detach from the bus */
+int usb_dc_detach(void)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+
+ regs->CTRLB.bit.DETACH = 1;
+ usb_sam0_wait_syncbusy();
+
+ return 0;
+}
+
+/* Remove the interrupt and reset the device */
+int usb_dc_reset(void)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+
+ irq_disable(DT_INST_IRQN(0));
+
+ regs->CTRLA.bit.SWRST = 1;
+ usb_sam0_wait_syncbusy();
+
+ return 0;
+}
+
+/* Queue a change in address. This is processed later when the
+ * current transfers are compelete.
+ */
+int usb_dc_set_address(const uint8_t addr)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+
+ data->addr = addr | USB_DEVICE_DADD_ADDEN;
+
+ return 0;
+}
+
+void usb_dc_set_status_callback(const usb_dc_status_callback cb)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+
+ data->cb = cb;
+}
+
+int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data * const cfg)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(cfg->ep_addr);
+
+ if ((cfg->ep_type == USB_DC_EP_CONTROL) && ep_idx) {
+ LOG_ERR("invalid endpoint configuration");
+ return -1;
+ }
+
+ if (ep_idx > USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address too high");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data *const cfg)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t ep_idx = USB_EP_GET_IDX(cfg->ep_addr);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+ UsbDeviceDescriptor *desc = &data->descriptors[ep_idx];
+ UsbDeviceDescBank *bank;
+ void *buf;
+ int type;
+ int size = -1;
+ int i;
+
+ /* Map the type to native type */
+ switch (cfg->ep_type) {
+ case USB_DC_EP_CONTROL:
+ type = 1;
+ break;
+ case USB_DC_EP_ISOCHRONOUS:
+ type = 2;
+ break;
+ case USB_DC_EP_BULK:
+ type = 3;
+ break;
+ case USB_DC_EP_INTERRUPT:
+ type = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Map the endpoint size to native size */
+ for (i = 0; i < ARRAY_SIZE(usb_sam0_pcksize_bytes); i++) {
+ if (usb_sam0_pcksize_bytes[i] == cfg->ep_mps) {
+ size = i;
+ break;
+ }
+ }
+
+ if (size < 0) {
+ return -EINVAL;
+ }
+
+ if (USB_EP_DIR_IS_IN(cfg->ep_addr)) {
+ bank = &desc->DeviceDescBank[1];
+ } else {
+ bank = &desc->DeviceDescBank[0];
+ }
+
+ buf = (void *)bank->ADDR.reg;
+
+ if (bank->PCKSIZE.bit.SIZE != size || buf == NULL) {
+ /* Release the previous buffer, if any */
+ k_free(buf);
+
+ buf = k_malloc(cfg->ep_mps);
+ if (buf == NULL) {
+ return -ENOMEM;
+ }
+ bank->PCKSIZE.bit.SIZE = size;
+ bank->ADDR.reg = (uintptr_t)buf;
+ }
+
+ if (USB_EP_DIR_IS_IN(cfg->ep_addr)) {
+ endpoint->EPCFG.bit.EPTYPE1 = type;
+ endpoint->EPSTATUSCLR.bit.BK1RDY = 1;
+ } else {
+ endpoint->EPCFG.bit.EPTYPE0 = type;
+ endpoint->EPSTATUSCLR.bit.BK0RDY = 1;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_set_stall(const uint8_t ep)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t for_in = USB_EP_GET_DIR(ep);
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ if (for_in) {
+ endpoint->EPSTATUSSET.bit.STALLRQ1 = 1;
+ } else {
+ endpoint->EPSTATUSSET.bit.STALLRQ0 = 1;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_clear_stall(const uint8_t ep)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t for_in = USB_EP_GET_DIR(ep);
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ if (for_in) {
+ endpoint->EPSTATUSCLR.bit.STALLRQ1 = 1;
+ } else {
+ endpoint->EPSTATUSCLR.bit.STALLRQ0 = 1;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_is_stalled(const uint8_t ep, uint8_t *stalled)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t for_in = USB_EP_GET_DIR(ep);
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ if (stalled == NULL) {
+ LOG_ERR("parameter must not be NULL");
+ return -1;
+ }
+
+ if (for_in) {
+ *stalled = endpoint->EPSTATUS.bit.STALLRQ1;
+ } else {
+ *stalled = endpoint->EPSTATUS.bit.STALLRQ0;
+ }
+
+ return 0;
+}
+
+/* Halt the selected endpoint */
+int usb_dc_ep_halt(uint8_t ep)
+{
+ return usb_dc_ep_set_stall(ep);
+}
+
+/* Flush the selected endpoint */
+int usb_dc_ep_flush(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ /* TODO */
+ LOG_WRN("flush not implemented");
+
+ return 0;
+}
+
+/* Enable an endpoint and the endpoint interrupts */
+int usb_dc_ep_enable(const uint8_t ep)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t for_in = USB_EP_GET_DIR(ep);
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -EINVAL;
+ }
+
+ if (for_in) {
+ endpoint->EPSTATUSCLR.bit.BK1RDY = 1;
+ } else {
+ endpoint->EPSTATUSCLR.bit.BK0RDY = 1;
+ }
+
+ endpoint->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 |
+ USB_DEVICE_EPINTENSET_TRCPT1 |
+ USB_DEVICE_EPINTENSET_RXSTP;
+
+ return 0;
+}
+
+/* Disable the selected endpoint */
+int usb_dc_ep_disable(uint8_t ep)
+{
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -EINVAL;
+ }
+
+ endpoint->EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT0
+ | USB_DEVICE_EPINTENCLR_TRCPT1
+ | USB_DEVICE_EPINTENCLR_RXSTP;
+
+ return 0;
+}
+
+/* Write a single payload to the IN buffer on the endpoint */
+int usb_dc_ep_write(uint8_t ep, const uint8_t *buf, uint32_t len, uint32_t *ret_bytes)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+ UsbDeviceDescriptor *desc = &data->descriptors[ep_idx];
+ uint32_t addr = desc->DeviceDescBank[1].ADDR.reg;
+ uint32_t capacity = usb_sam0_pcksize_bytes[
+ desc->DeviceDescBank[1].PCKSIZE.bit.SIZE];
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ if (endpoint->EPSTATUS.bit.BK1RDY) {
+ /* Write in progress, drop */
+ return -EAGAIN;
+ }
+
+ len = Z_MIN(len, capacity);
+
+ /* Note that this code does not use the hardware's
+ * multi-packet and automatic zero-length packet features as
+ * the upper layers in Zephyr implement these in code.
+ */
+ memcpy((void *)addr, buf, len);
+ desc->DeviceDescBank[1].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+ desc->DeviceDescBank[1].PCKSIZE.bit.BYTE_COUNT = len;
+ endpoint->EPINTFLAG.reg =
+ USB_DEVICE_EPINTFLAG_TRCPT1 | USB_DEVICE_EPINTFLAG_TRFAIL1;
+ endpoint->EPSTATUSSET.bit.BK1RDY = 1;
+
+ if (ret_bytes != NULL) {
+ *ret_bytes = len;
+ }
+
+ return 0;
+}
+
+/* Read data from an OUT endpoint */
+int usb_dc_ep_read_ex(uint8_t ep, uint8_t *buf, uint32_t max_data_len,
+ uint32_t *read_bytes, bool wait)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+ UsbDeviceDescriptor *desc = &data->descriptors[ep_idx];
+ uint32_t addr = desc->DeviceDescBank[0].ADDR.reg;
+ uint32_t bytes = desc->DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT;
+ uint32_t take;
+ int remain;
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ if (!endpoint->EPSTATUS.bit.BK0RDY) {
+ return -EAGAIN;
+ }
+
+ /* The code below emulates the Quark FIFO which the Zephyr USB
+ * API is based on. Reading with buf == NULL returns the
+ * number of bytes available and starts the read. The caller
+ * then keeps calling until all bytes are consumed which
+ * also marks the OUT buffer as freed.
+ */
+ if (buf == NULL) {
+ data->out_at = 0U;
+
+ if (read_bytes != NULL) {
+ *read_bytes = bytes;
+ }
+
+ return 0;
+ }
+
+ remain = bytes - data->out_at;
+ take = MIN(max_data_len, remain);
+ memcpy(buf, (uint8_t *)addr + data->out_at, take);
+
+ if (read_bytes != NULL) {
+ *read_bytes = take;
+ }
+
+ if (take == remain) {
+ if (!wait) {
+ endpoint->EPSTATUSCLR.bit.BK0RDY = 1;
+ data->out_at = 0U;
+ }
+ } else {
+ data->out_at += take;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_read(uint8_t ep, uint8_t *buf, uint32_t max_data_len, uint32_t *read_bytes)
+{
+ return usb_dc_ep_read_ex(ep, buf, max_data_len, read_bytes, false);
+}
+
+int usb_dc_ep_read_wait(uint8_t ep, uint8_t *buf, uint32_t max_data_len,
+ uint32_t *read_bytes)
+{
+ return usb_dc_ep_read_ex(ep, buf, max_data_len, read_bytes, true);
+}
+
+int usb_dc_ep_read_continue(uint8_t ep)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ endpoint->EPSTATUSCLR.bit.BK0RDY = 1;
+ data->out_at = 0U;
+
+ return 0;
+}
+
+int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+ uint8_t for_in = USB_EP_GET_DIR(ep);
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ data->ep_cb[for_in ? 1 : 0][ep_idx] = cb;
+
+ return 0;
+}
+
+int usb_dc_ep_mps(const uint8_t ep)
+{
+ struct usb_sam0_data *data = usb_sam0_get_data();
+ UsbDevice *regs = &REGS->DEVICE;
+ uint8_t for_in = USB_EP_GET_DIR(ep);
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ UsbDeviceDescriptor *desc = &data->descriptors[ep_idx];
+ UsbDeviceEndpoint *endpoint = &regs->DeviceEndpoint[ep_idx];
+
+ if (ep_idx >= USB_NUM_ENDPOINTS) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ if (for_in) {
+ /* if endpoint is not configured, this should return 0 */
+ if (endpoint->EPCFG.bit.EPTYPE1 == 0) {
+ return 0;
+ }
+
+ return usb_sam0_pcksize_bytes[
+ desc->DeviceDescBank[1].PCKSIZE.bit.SIZE];
+ } else {
+ /* if endpoint is not configured, this should return 0 */
+ if (endpoint->EPCFG.bit.EPTYPE0 == 0) {
+ return 0;
+ }
+
+ return usb_sam0_pcksize_bytes[
+ desc->DeviceDescBank[0].PCKSIZE.bit.SIZE];
+ }
+}
diff --git a/port/atmel/usbd_ATSAM3U2C.c b/port/atmel/usbd_ATSAM3U2C.c
new file mode 100644
index 00000000..b556a612
--- /dev/null
+++ b/port/atmel/usbd_ATSAM3U2C.c
@@ -0,0 +1,807 @@
+/**
+ * @file usbd_ATSAM3U2C.c
+ * @brief
+ *
+ * DAPLink Interface Firmware
+ * Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 "rl_usb.h"
+#include "sam3u.h"
+#include "util.h"
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+#define UDPHS_EPTFIFO_BASE (0x20180000) /* (UDPHS_EPTFIFO) Base Address */
+uint32_t eptsta_copy[USBD_EP_NUM + 1];
+
+/*
+ * Calculate EP size code Function
+ * Called during EndPoint configuration
+ * Return Value: EP size code for given EP size
+ */
+
+static int USBD_CalcSizeEP(uint32_t size)
+{
+ if (size <= 8) {
+ return (0);
+ } else if (size <= 16) {
+ return (1);
+ } else if (size <= 32) {
+ return (2);
+ } else if (size <= 64) {
+ return (3);
+ } else if (size <= 128) {
+ return (4);
+ } else if (size <= 256) {
+ return (5);
+ } else if (size <= 512) {
+ return (6);
+ } else if (size <= 1024) {
+ return (7);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Retrieve maximum EP size Function
+ * Called during EndPoint configuration
+ * Return Value: maximum size for given EP
+ */
+
+static int USBD_GetSizeEP(uint32_t EPNum)
+{
+ switch (EPNum & 0x0F) {
+ case 0:
+ return (64); /* Maximum size is 64 bytes */
+
+ case 1:
+ case 2:
+ return (512); /* Maximum size is 512 bytes */
+
+ case 3:
+ case 4:
+ return (64); /* Maximum size is 64 bytes */
+
+ case 5:
+ case 6:
+ return (1024); /* Maximum size is 1024 bytes */
+
+ default:
+ return (0); /* Non existant endpoint */
+ }
+}
+
+
+/*
+ * USB Device Interrupt enable
+ * Called by USBD_Init to enable the USB Interrupt
+ * Return Value: None
+ */
+
+void USBD_IntrEna(void)
+{
+ NVIC_EnableIRQ(UDPHS_IRQn); /* Enable USB interrupt */
+}
+
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB Device
+ * Return Value: None
+ */
+
+void USBD_Init(void)
+{
+ uint32_t n;
+ /* Enables the 48MHz USB Clock UDPCK and System Peripheral USB Clock */
+ PMC->PMC_WPMR = 0x504D4300; /* Disable write protect */
+ PMC->PMC_PCER0 = (1 << ID_UDPHS); /* enable clock for UPDHS */
+ PMC->CKGR_UCKR = (CKGR_UCKR_UPLLCOUNT(15) | CKGR_UCKR_UPLLEN);
+
+ while (!(PMC->PMC_SR & PMC_SR_LOCKU)); /* wait until PLL is locked */
+
+ PMC->PMC_WPMR = 0x504D4301; /* Enable write protect */
+ /* Configure the pull-up on D+ and disconnect it */
+ UDPHS->UDPHS_CTRL |= UDPHS_CTRL_DETACH; /* Detach */
+ UDPHS->UDPHS_CTRL |= UDPHS_CTRL_PULLD_DIS; /* Disable Pull Down */
+ /* Reset IP UDPHS */
+ UDPHS->UDPHS_CTRL &= ~UDPHS_CTRL_EN_UDPHS;
+ UDPHS->UDPHS_CTRL |= UDPHS_CTRL_EN_UDPHS;
+#if (!USBD_HS_ENABLE) /* If HS disabled */
+ UDPHS->UDPHS_TST |= (3 & UDPHS_TST_SPEED_CFG_Msk);
+#endif
+
+ /* Disable DMA for UDPHS */
+ for (n = 1; n < (UDPHSDMA_NUMBER); n++) {
+ /* RESET endpoint canal DMA: */
+ UDPHS->UDPHS_DMA[n].UDPHS_DMACONTROL = 0; /* STOP command */
+ /* Disable endpoint */
+ UDPHS->UDPHS_EPT[n].UDPHS_EPTCTLDIS = 0xFFFFFFFF;
+ /* Clear status endpoint */
+ UDPHS->UDPHS_EPT[n].UDPHS_EPTCLRSTA = 0xFFFFFFFF;
+ /* Reset endpoint config */
+ UDPHS->UDPHS_EPT[n].UDPHS_EPTCTLENB = 0;
+ /* Reset DMA channel (Buff count and Control field) */
+ UDPHS->UDPHS_DMA[n].UDPHS_DMACONTROL = (0x1 << 1); /* NON STOP command */
+ /* Reset DMA channel 0 (STOP) */
+ UDPHS->UDPHS_DMA[n].UDPHS_DMACONTROL = 0; /* STOP command */
+ /* Clear DMA channel status (read the register for clear it) */
+ UDPHS->UDPHS_DMA[n].UDPHS_DMASTATUS = UDPHS->UDPHS_DMA[n].UDPHS_DMASTATUS;
+ }
+
+ UDPHS->UDPHS_IEN = 0;
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_UPSTR_RES |
+ UDPHS_CLRINT_ENDOFRSM |
+ UDPHS_CLRINT_WAKE_UP |
+ UDPHS_CLRINT_ENDRESET |
+ UDPHS_CLRINT_INT_SOF |
+ UDPHS_CLRINT_MICRO_SOF |
+ UDPHS_CLRINT_DET_SUSPD;
+ USBD_IntrEna(); /* Enable USB interrupt */
+}
+
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+
+void USBD_Connect(BOOL con)
+{
+ if (con) {
+ UDPHS->UDPHS_CTRL &= ~UDPHS_CTRL_DETACH; /* Pull Up on DP */
+ UDPHS->UDPHS_CTRL |= UDPHS_CTRL_PULLD_DIS; /* Disable Pull Down */
+ } else {
+ UDPHS->UDPHS_CTRL |= UDPHS_CTRL_DETACH; /* Detach */
+ UDPHS->UDPHS_CTRL &= ~UDPHS_CTRL_PULLD_DIS; /* Enable Pull Down */
+ }
+}
+
+/*
+ * USB Device Reset Function
+ * Called automatically on USB Device Reset
+ * Return Value: None
+ */
+extern U8 USBD_ConfigDescriptor_HS[];
+extern U8 USBD_ConfigDescriptor[];
+void USBD_Reset(void)
+{
+ uint32_t ep, EPMask;
+ EPMask = ((1 << (USBD_EP_NUM + 1)) - 1);
+
+ /* Reset & Disable USB Endpoints */
+ for (ep = 0; ep <= USBD_EP_NUM; ep++) {
+ UDPHS->UDPHS_EPT[ep].UDPHS_EPTCFG = 0;
+ UDPHS->UDPHS_EPT[ep].UDPHS_EPTCTLDIS = (0x1 << 0); /* Disable EP */
+ eptsta_copy[ep] = 0;
+ }
+
+ UDPHS->UDPHS_EPTRST = EPMask; /* Reset EPs */
+ UDPHS->UDPHS_EPTRST = 0;
+ /* Setup USB Interrupts */ /* note: Micro_SOF not yet handled */
+#ifdef __RTX
+ UDPHS->UDPHS_IEN = ((USBD_RTX_DevTask != 0) ? UDPHS_IEN_DET_SUSPD : 0) |
+ ((USBD_RTX_DevTask != 0) ? UDPHS_IEN_MICRO_SOF : 0) |
+ ((USBD_RTX_DevTask != 0) ? UDPHS_IEN_INT_SOF : 0) |
+ ((USBD_RTX_DevTask != 0) ? UDPHS_IEN_ENDRESET : 0) |
+// ((USBD_RTX_DevTask != 0) ? UDPHS_IEN_WAKE_UP : 0) |
+// ((USBD_RTX_DevTask != 0) ? UDPHS_IEN_UPSTR_RES : 0) |
+#else
+ UDPHS->UDPHS_IEN = ((USBD_P_Suspend_Event != 0) ? UDPHS_IEN_DET_SUSPD : 0) |
+ ((USBD_P_SOF_Event != 0) ? UDPHS_IEN_MICRO_SOF : 0) |
+ ((USBD_P_SOF_Event != 0) ? UDPHS_IEN_INT_SOF : 0) |
+ ((USBD_P_Reset_Event != 0) ? UDPHS_IEN_ENDRESET : 0) |
+// ((USBD_P_WakeUp_Event != 0) ? UDPHS_IEN_WAKE_UP : 0) |
+// ((USBD_P_Resume_Event != 0) ? UDPHS_IEN_UPSTR_RES : 0) |
+#endif
+ (EPMask << 8);
+ /* Setup Control Endpoint 0 */
+ UDPHS->UDPHS_EPT[0].UDPHS_EPTCFG = UDPHS_EPTCFG_BK_NUMBER_1 |
+ UDPHS_EPTCFG_EPT_TYPE_CTRL8 |
+ USBD_CalcSizeEP(USBD_MAX_PACKET0) ;
+ UDPHS->UDPHS_EPT[0].UDPHS_EPTCTLENB = UDPHS_EPTCTLENB_RXRDY_TXKL |
+ UDPHS_EPTCTLENB_TX_COMPLT |
+ UDPHS_EPTCTLENB_RX_SETUP |
+ UDPHS_EPTCTLENB_STALL_SNT |
+ UDPHS_EPTCTLENB_NYET_DIS |
+ UDPHS_EPTCTLENB_EPT_ENABL;
+
+
+#if (USBD_HS_ENABLE == 1)
+ U8 * config_desc = USBD_ConfigDescriptor_HS;
+#else
+ U8 * config_desc = USBD_ConfigDescriptor;
+#endif
+
+ while (((USB_ENDPOINT_DESCRIPTOR *)config_desc)->bLength > 0) {
+ if (((USB_ENDPOINT_DESCRIPTOR *)config_desc)->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE) {
+ uint32_t num, type, dir, size, banks, interval;
+ USB_ENDPOINT_DESCRIPTOR *pEPD = (USB_ENDPOINT_DESCRIPTOR *)config_desc;
+ num = pEPD->bEndpointAddress & 0x0F;
+ type = pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+ dir = pEPD->bEndpointAddress >> 7;
+ interval = pEPD->bInterval;
+ size = USBD_CalcSizeEP(pEPD->wMaxPacketSize);
+ banks = 1;
+ UDPHS->UDPHS_EPT[num].UDPHS_EPTCFG = (interval << 8) |
+ (banks << 6) |
+ (type << 4) |
+ (dir << 3) |
+ (size << 0) ;
+ }
+ config_desc += ((USB_ENDPOINT_DESCRIPTOR *)config_desc)->bLength;
+ }
+}
+
+
+/*
+ * USB Device Suspend Function
+ * Called automatically on USB Device Suspend
+ * Return Value: None
+ */
+
+void USBD_Suspend(void)
+{
+ UDPHS->UDPHS_IEN &= ~UDPHS_IEN_DET_SUSPD;
+ UDPHS->UDPHS_IEN |= UDPHS_IEN_WAKE_UP;
+}
+
+
+/*
+ * USB Device Resume Function
+ * Called automatically on USB Device Resume
+ * Return Value: None
+ */
+
+void USBD_Resume(void)
+{
+ UDPHS->UDPHS_IEN &= ~UDPHS_IEN_WAKE_UP;
+ UDPHS->UDPHS_IEN |= UDPHS_IEN_DET_SUSPD;
+}
+
+
+/*
+ * USB Device Remote Wakeup Function
+ * Called automatically on USB Device Remote Wakeup
+ * Return Value: None
+ */
+
+void USBD_WakeUp(void)
+{
+ UDPHS->UDPHS_IEN |= UDPHS_IEN_UPSTR_RES;
+ UDPHS->UDPHS_CTRL |= UDPHS_CTRL_REWAKEUP;
+}
+
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+
+void USBD_WakeUpCfg(BOOL cfg)
+{
+ if (cfg) {
+ /* Enable wakeup mechanism */
+ } else {
+ UDPHS->UDPHS_CTRL &= ~UDPHS_CTRL_REWAKEUP;
+ UDPHS->UDPHS_IEN &= ~UDPHS_IEN_UPSTR_RES;
+ }
+}
+
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * setup: Called in setup stage (!=0), else after status stage
+ * Return Value: None
+ */
+
+void USBD_SetAddress(uint32_t adr, uint32_t setup)
+{
+ if (setup) {
+ return;
+ }
+
+ if (adr) {
+ UDPHS->UDPHS_CTRL |= (UDPHS_CTRL_FADDR_EN | adr);
+ } else {
+ UDPHS->UDPHS_CTRL &= ~(UDPHS_CTRL_FADDR_EN | UDPHS_CTRL_DEV_ADDR_Msk);
+ }
+}
+
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+
+void USBD_Configure(BOOL cfg)
+{
+ /* Performed by Hardware */
+}
+
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+
+void USBD_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ uint32_t num;//, type, dir, size, banks, interval;
+ num = pEPD->bEndpointAddress & 0x0F;
+ /*type = pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+ dir = pEPD->bEndpointAddress >> 7;
+ interval = pEPD->bInterval;
+ size = USBD_CalcSizeEP(pEPD->wMaxPacketSize);
+ banks = 1;
+ */
+ /* Check if MaxPacketSize fits for EndPoint */
+ if (pEPD->wMaxPacketSize <= USBD_GetSizeEP(num)) {
+ /*UDPHS->UDPHS_EPT[num].UDPHS_EPTCFG = (interval << 8) |
+ (banks << 6) |
+ (type << 4) |
+ (dir << 3) |
+ //(size << 0) ;
+ 6;*/
+ UDPHS->UDPHS_EPT[num].UDPHS_EPTCTLENB =
+ (0x1 << 9) | /* Received OUT Data Interrupt Enable */
+ (0x1 << 10) | /* Transmitted IN Data Complete Interrupt Enable */
+ (0x0 << 4) | /* NYET Disable (Only for High Speed Bulk OUT endpoints) */
+ (0x1 << 13) ; /* Stall Sent /ISO CRC Error/Number of Transaction Error */
+ }
+}
+
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+
+void USBD_DirCtrlEP(uint32_t dir)
+{
+ /* Performed by Hardware */
+}
+
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_EnableEP(uint32_t EPNum)
+{
+ UDPHS->UDPHS_EPT[EPNum & 0x0F].UDPHS_EPTCTLENB = (0x1 << 0); /* EP Enable */
+ eptsta_copy[EPNum & 0x0F] = UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTSETSTA;
+}
+
+
+/*
+ * Disable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_DisableEP(uint32_t EPNum)
+{
+ UDPHS->UDPHS_EPT[EPNum & 0x0F].UDPHS_EPTCTLDIS = (0x1 << 0); /* EP Disable */
+ eptsta_copy[EPNum & 0x0F] = UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTSETSTA;
+}
+
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ResetEP(uint32_t EPNum)
+{
+ EPNum &= 0x0F;
+ UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTCLRSTA = (0x1 << 6) | /* Data Toggle Clear*/
+ (0x1 << 5); /* Stall Req Set */
+ UDPHS->UDPHS_EPTRST |= (1 << EPNum); /* Reset endpoint */
+ UDPHS->UDPHS_EPTRST &= ~(1 << EPNum);
+ eptsta_copy[EPNum] = UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTSETSTA;
+}
+
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_SetStallEP(uint32_t EPNum)
+{
+ UDPHS->UDPHS_EPT[EPNum & 0x0F].UDPHS_EPTSETSTA = (0x1 << 5); /* Stall Set */
+ eptsta_copy[EPNum & 0x0F] |= 0x1 << 5;
+}
+
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClrStallEP(uint32_t EPNum)
+{
+ UDPHS->UDPHS_EPT[EPNum & 0x0F].UDPHS_EPTCLRSTA = (0x1 << 6) | /* Clr Toggle */
+ (0x1 << 5); /* Stall Clear*/
+ eptsta_copy[EPNum & 0x0F] &= ~(0x1 << 5);
+}
+
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+
+uint32_t USBD_ReadEP(uint32_t EPNum, uint8_t *pData, uint32_t size)
+{
+ uint32_t cnt, n, copy_sz;
+ uint8_t *pEPFIFO; /* Pointer to EP FIFO */
+ uint32_t eptsta;
+ EPNum &= 0x0F;
+ eptsta = eptsta_copy[EPNum];
+ pEPFIFO = (uint8_t *)((uint32_t *)UDPHS_EPTFIFO_BASE + (16384 * EPNum));
+ cnt = (eptsta >> 20) & 0x07FF; /* Get by */
+ copy_sz = cnt > size ? size : cnt;
+
+ for (n = 0; n < copy_sz; n++) {
+ *pData++ = *pEPFIFO++;
+ }
+
+ util_assert(cnt == copy_sz);
+
+ if ((cnt == copy_sz) && (eptsta & (0x1 << 9))) {
+ UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTCLRSTA = (0x1 << 9); /* Rece OUT Clear */
+ }
+
+ /* RX_Setup must be cleared after Setup packet is read */
+ if (eptsta & (0x1 << 12)) {
+ UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTCLRSTA = (0x1 << 12); /* Rece SETUP Clear */
+ }
+ UDPHS->UDPHS_IEN |= (1 << (EPNum + 8)); /* Enable EP int after data read*/
+ return (cnt);
+}
+
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+
+uint32_t USBD_WriteEP(uint32_t EPNum, uint8_t *pData, uint32_t cnt)
+{
+ uint32_t n;
+ uint8_t *pEPFIFO; /* Pointer to the endpoint FIFO */
+ uint32_t eptsta;
+ EPNum &= 0x0F;
+ eptsta = eptsta_copy[EPNum];
+
+ // Cached value should match the real value
+ util_assert((UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTSTA & (0x1 << 5)) == (eptsta & (0x1 << 5)));
+ if (eptsta & (0x1 << 5)) { /* If EP is stall */
+ return (cnt);
+ }
+
+ // Both register and cached value should indicate that the bank is ready (bit 11 clear)
+ util_assert(!(UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTSTA & (0x1 << 11)));
+ util_assert(!(eptsta & (0x1 << 11)));
+
+ pEPFIFO = (uint8_t *)((uint32_t *)UDPHS_EPTFIFO_BASE + (16384 * EPNum));
+
+ for (n = 0; n < cnt; n++) {
+ *pEPFIFO++ = *pData++; /* Write data to FIFO */
+ }
+
+ UDPHS->UDPHS_EPT[EPNum].UDPHS_EPTSETSTA = (0x1 << 11); /* Set packet ready */
+ return (cnt);
+}
+
+
+/*
+ * Get USB Device Last Frame Number
+ * Parameters: None
+ * Return Value: Frame Number
+ */
+
+uint32_t USBD_GetFrame(void)
+{
+ uint32_t val;
+
+ if ((UDPHS->UDPHS_FNUM & (1UL << 31)) == 0) {
+ if (USBD_HighSpeed) {
+ val = UDPHS->UDPHS_FNUM & 0x7FFFFFFF;
+ } else {
+ val = (UDPHS->UDPHS_FNUM & UDPHS_FNUM_FRAME_NUMBER_Msk) >> 3;
+ }
+ } else {
+ val = 0xFFFFFFFF;
+ }
+
+ return (val);
+}
+
+
+#ifdef __RTX
+uint32_t LastError; /* Last Error */
+
+/*
+ * Get USB Device Last Error Code
+ * Parameters: None
+ * Return Value: Error Code
+ */
+
+uint32_t USBD_GetError(void)
+{
+ return (LastError);
+}
+#endif
+
+
+/*
+ * USB Device Interrupt Service Routine
+ */
+
+void UDPHS_IRQHandler(void)
+{
+ NVIC_DisableIRQ(UDPHS_IRQn);
+ USBD_SignalHandler();
+}
+
+/*
+ * USB Device Service Routine
+ */
+
+void USBD_Handler(void)
+{
+ uint32_t intsta, eptsta, n;
+ intsta = UDPHS->UDPHS_INTSTA & UDPHS->UDPHS_IEN;
+
+ /* End of Bus Reset Interrupt */
+ if (intsta & UDPHS_INTSTA_ENDRESET) {
+ /* Get used speed (HighSpeed or FullSpeed) */
+ USBD_HighSpeed = (UDPHS->UDPHS_INTSTA & UDPHS_INTSTA_SPEED) ? 1 : 0;
+ USBD_Reset();
+ usbd_reset_core();
+#ifdef __RTX
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_ENDRESET;
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Reset_Event) {
+ USBD_P_Reset_Event();
+ }
+
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_ENDRESET;
+#endif
+ }
+
+ /* USB Suspend Interrupt */
+ if (intsta & UDPHS_INTSTA_DET_SUSPD) {
+ USBD_Suspend();
+#ifdef __RTX
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_DET_SUSPD;
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Suspend_Event) {
+ USBD_P_Suspend_Event();
+ }
+
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_DET_SUSPD;
+#endif
+ }
+
+ /* USB Resume Interrupt */
+ if (intsta & UDPHS_INTSTA_WAKE_UP) {
+ USBD_Resume();
+#ifdef __RTX
+ UDPHS->UDPHS_CLRINT = UDPHS_INTSTA_WAKE_UP;
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Resume_Event) {
+ USBD_P_Resume_Event();
+ }
+
+ UDPHS->UDPHS_CLRINT = UDPHS_INTSTA_WAKE_UP;
+#endif
+ }
+
+ /* USB Remote Wakeup Interrupt */
+ if (intsta & UDPHS_INTSTA_UPSTR_RES) {
+ UDPHS->UDPHS_CLRINT = UDPHS_INTSTA_UPSTR_RES;
+ }
+
+ /* Start of Frame Interrupt */
+ if (intsta & UDPHS_INTSTA_INT_SOF) {
+ /* Process the SOF interrupt even in high speed mode.
+ The SOF and MICRO_SOF interrupt are never generated at the same
+ time. Instead, when in high speed mode there is 1 SOF
+ interrupt and 7 MICRO_SOF interrupts every 1ms. */
+
+#ifdef __RTX
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_INT_SOF;
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SOF, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_INT_SOF;
+#endif
+ }
+
+ /* Micro Frame Interrupt */
+ if (intsta & UDPHS_INTSTA_MICRO_SOF) {
+ if (USBD_HighSpeed == 1) {
+#ifdef __RTX
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_MICRO_SOF;
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SOF, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_MICRO_SOF;
+#endif
+
+ } else {
+ UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_MICRO_SOF;
+ }
+ }
+
+ /* Endpoint Interrupts */
+ for (n = 0; n <= USBD_EP_NUM; n++) {
+ if (intsta & (1 << (n + 8))) {
+ eptsta = UDPHS->UDPHS_EPT[n].UDPHS_EPTSTA; /* Read EP status */
+ eptsta_copy[n] = eptsta;
+
+ /* Data Packet Sent Interrupt */
+ if (eptsta & (1 << 10)) { /* Transmitted IN Data Complete Int */
+ UDPHS->UDPHS_EPT[n].UDPHS_EPTCLRSTA = (1 << 10); /* Tx IN Clear */
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[n]) { /* IN Packet */
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[n]);
+ }
+
+#else
+
+ if (USBD_P_EP[n]) {
+ USBD_P_EP[n](USBD_EVT_IN);
+ }
+
+#endif
+ }
+
+ /* Data Packet Received Interrupt */
+ if (eptsta & (1 << 9)) { /* Received OUT Data Interrupt */
+ UDPHS->UDPHS_IEN &= ~(1 << (n + 8)); /* Disable EP int until read*/
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[n]) { /* OUT Packet */
+ isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[n]);
+ }
+
+#else
+
+ if (USBD_P_EP[n]) {
+ USBD_P_EP[n](USBD_EVT_OUT);
+ }
+
+#endif
+ }
+
+ /* STALL Packet Sent Interrupt */
+ if (eptsta & (0x1 << 13)) { /* Stall Sent */
+ if ((UDPHS->UDPHS_EPT[n].UDPHS_EPTCFG & UDPHS_EPTCFG_EPT_TYPE_Msk) == UDPHS_EPTCFG_EPT_TYPE_CTRL8) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[n]) {
+ isr_evt_set(USBD_EVT_IN_STALL, USBD_RTX_EPTask[n]);
+ }
+
+#else
+
+ if (USBD_P_EP[n]) {
+ USBD_P_EP[n](USBD_EVT_IN_STALL);
+ }
+
+#endif
+ }
+
+ UDPHS->UDPHS_EPT[n].UDPHS_EPTCLRSTA = UDPHS_EPTCLRSTA_STALL_SNT;
+ }
+
+ /* Setup Packet Received Interrupt */
+ if (eptsta & (0x1 << 12)) { /* Received SETUP Interrupt */
+ UDPHS->UDPHS_IEN &= ~(1 << (n + 8)); /* Disable EP int until read*/
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[n]) { /* SETUP Packet */
+ isr_evt_set(USBD_EVT_SETUP, USBD_RTX_EPTask[n]);
+ }
+
+#else
+
+ if (USBD_P_EP[n]) {
+ USBD_P_EP[n](USBD_EVT_SETUP);
+ }
+
+#endif
+ }
+ }
+ }
+
+ NVIC_EnableIRQ(UDPHS_IRQn);
+}
diff --git a/port/dw/usb_dc_dw.c b/port/dw/usb_dc_dw.c
new file mode 100644
index 00000000..b427342d
--- /dev/null
+++ b/port/dw/usb_dc_dw.c
@@ -0,0 +1,1212 @@
+/* usb_dc_dw.c - USB DesignWare device controller driver */
+
+#define DT_DRV_COMPAT snps_designware_usb
+
+/*
+ * Copyright (c) 2016 Intel Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief USB DesignWare device controller driver
+ *
+ * USB DesignWare device controller driver. The driver implements the low
+ * level control routines to deal directly with the hardware.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <sys/byteorder.h>
+#include <usb/usb_device.h>
+#include "usb_dw_registers.h"
+#include <soc.h>
+
+#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
+#include <logging/log.h>
+LOG_MODULE_REGISTER(usb_dc_dw);
+
+/* Number of SETUP back-to-back packets */
+#define USB_DW_SUP_CNT (1)
+
+/*
+ * USB endpoint private structure.
+ */
+struct usb_ep_ctrl_prv {
+ uint8_t ep_ena;
+ uint8_t fifo_num;
+ uint32_t fifo_size;
+ uint16_t mps; /* Max ep pkt size */
+ usb_dc_ep_callback cb;/* Endpoint callback function */
+ uint32_t data_len;
+};
+
+/*
+ * USB controller private structure.
+ */
+struct usb_dw_ctrl_prv {
+ usb_dc_status_callback status_cb;
+ struct usb_ep_ctrl_prv in_ep_ctrl[USB_DW_IN_EP_NUM];
+ struct usb_ep_ctrl_prv out_ep_ctrl[USB_DW_OUT_EP_NUM];
+ int n_tx_fifos;
+ uint8_t attached;
+};
+
+
+static struct usb_dw_ctrl_prv usb_dw_ctrl;
+
+static void usb_dw_reg_dump(void)
+{
+ uint8_t i;
+
+ LOG_DBG("USB registers: GOTGCTL : 0x%x GOTGINT : 0x%x GAHBCFG : "
+ "0x%x", USB_DW->gotgctl, USB_DW->gotgint, USB_DW->gahbcfg);
+ LOG_DBG(" GUSBCFG : 0x%x GINTSTS : 0x%x GINTMSK : 0x%x",
+ USB_DW->gusbcfg, USB_DW->gintsts, USB_DW->gintmsk);
+ LOG_DBG(" DCFG : 0x%x DCTL : 0x%x DSTS : 0x%x",
+ USB_DW->dcfg, USB_DW->dctl, USB_DW->dsts);
+ LOG_DBG(" DIEPMSK : 0x%x DOEPMSK : 0x%x DAINT : 0x%x",
+ USB_DW->diepmsk, USB_DW->doepmsk, USB_DW->daint);
+ LOG_DBG(" DAINTMSK: 0x%x GHWCFG1 : 0x%x GHWCFG2 : 0x%x",
+ USB_DW->daintmsk, USB_DW->ghwcfg1, USB_DW->ghwcfg2);
+ LOG_DBG(" GHWCFG3 : 0x%x GHWCFG4 : 0x%x",
+ USB_DW->ghwcfg3, USB_DW->ghwcfg4);
+
+ for (i = 0U; i < USB_DW_OUT_EP_NUM; i++) {
+ LOG_DBG("\n EP %d registers: DIEPCTL : 0x%x DIEPINT : "
+ "0x%x", i, USB_DW->in_ep_reg[i].diepctl,
+ USB_DW->in_ep_reg[i].diepint);
+ LOG_DBG(" DIEPTSIZ: 0x%x DIEPDMA : 0x%x DOEPCTL : "
+ "0x%x", USB_DW->in_ep_reg[i].dieptsiz,
+ USB_DW->in_ep_reg[i].diepdma,
+ USB_DW->out_ep_reg[i].doepctl);
+ LOG_DBG(" DOEPINT : 0x%x DOEPTSIZ: 0x%x DOEPDMA : "
+ "0x%x", USB_DW->out_ep_reg[i].doepint,
+ USB_DW->out_ep_reg[i].doeptsiz,
+ USB_DW->out_ep_reg[i].doepdma);
+ }
+}
+
+static uint8_t usb_dw_ep_is_valid(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ /* Check if ep enabled */
+ if ((USB_EP_DIR_IS_OUT(ep)) && ep_idx < USB_DW_OUT_EP_NUM) {
+ return 1;
+ } else if ((USB_EP_DIR_IS_IN(ep)) && ep_idx < USB_DW_IN_EP_NUM) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static uint8_t usb_dw_ep_is_enabled(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ /* Check if ep enabled */
+ if ((USB_EP_DIR_IS_OUT(ep)) &&
+ usb_dw_ctrl.out_ep_ctrl[ep_idx].ep_ena) {
+ return 1;
+ } else if ((USB_EP_DIR_IS_IN(ep)) &&
+ usb_dw_ctrl.in_ep_ctrl[ep_idx].ep_ena) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline void usb_dw_udelay(uint32_t us)
+{
+ k_busy_wait(us);
+}
+
+static int usb_dw_reset(void)
+{
+ uint32_t cnt = 0U;
+
+ /* Wait for AHB master idle state. */
+ while (!(USB_DW->grstctl & USB_DW_GRSTCTL_AHB_IDLE)) {
+ usb_dw_udelay(1);
+
+ if (++cnt > USB_DW_CORE_RST_TIMEOUT_US) {
+ LOG_ERR("USB reset HANG! AHB Idle GRSTCTL=0x%08x",
+ USB_DW->grstctl);
+ return -EIO;
+ }
+ }
+
+ /* Core Soft Reset */
+ cnt = 0U;
+ USB_DW->grstctl |= USB_DW_GRSTCTL_C_SFT_RST;
+
+ do {
+ if (++cnt > USB_DW_CORE_RST_TIMEOUT_US) {
+ LOG_DBG("USB reset HANG! Soft Reset GRSTCTL=0x%08x",
+ USB_DW->grstctl);
+ return -EIO;
+ }
+ usb_dw_udelay(1);
+ } while (USB_DW->grstctl & USB_DW_GRSTCTL_C_SFT_RST);
+
+ /* Wait for 3 PHY Clocks */
+ usb_dw_udelay(100);
+
+ return 0;
+}
+
+static int usb_dw_num_dev_eps(void)
+{
+ return (USB_DW->ghwcfg2 >> 10) & 0xf;
+}
+
+static void usb_dw_flush_tx_fifo(int ep)
+{
+ int fnum = usb_dw_ctrl.in_ep_ctrl[ep].fifo_num;
+
+ USB_DW->grstctl = (fnum << 6) | (1<<5);
+ while (USB_DW->grstctl & (1<<5)) {
+ }
+}
+
+static int usb_dw_tx_fifo_avail(int ep)
+{
+ return USB_DW->in_ep_reg[ep].dtxfsts &
+ USB_DW_DTXFSTS_TXF_SPC_AVAIL_MASK;
+}
+
+/* Choose a FIFO number for an IN endpoint */
+static int usb_dw_set_fifo(uint8_t ep)
+{
+ int ep_idx = USB_EP_GET_IDX(ep);
+ volatile uint32_t *reg = &USB_DW->in_ep_reg[ep_idx].diepctl;
+ uint32_t val;
+ int fifo = 0;
+ int ded_fifo = !!(USB_DW->ghwcfg4 & USB_DW_HWCFG4_DEDFIFOMODE);
+
+ if (!ded_fifo) {
+ /* No support for shared-FIFO mode yet, existing
+ * Zephyr hardware doesn't use it
+ */
+ return -ENOTSUP;
+ }
+
+ /* In dedicated-FIFO mode, all IN endpoints must have a unique
+ * FIFO number associated with them in the TXFNUM field of
+ * DIEPCTLx, with EP0 always being assigned to FIFO zero (the
+ * reset default, so we don't touch it).
+ *
+ * FIXME: would be better (c.f. the dwc2 driver in Linux) to
+ * choose a FIFO based on the hardware depth: we want the
+ * smallest one that fits our configured maximum packet size
+ * for the endpoint. This just picks the next available one.
+ */
+ if (ep_idx != 0) {
+ fifo = ++usb_dw_ctrl.n_tx_fifos;
+ if (fifo >= usb_dw_num_dev_eps()) {
+ return -EINVAL;
+ }
+
+ reg = &USB_DW->in_ep_reg[ep_idx].diepctl;
+ val = *reg & ~USB_DW_DEPCTL_TXFNUM_MASK;
+ val |= fifo << USB_DW_DEPCTL_TXFNUM_OFFSET;
+ *reg = val;
+ }
+
+ usb_dw_ctrl.in_ep_ctrl[ep_idx].fifo_num = fifo;
+
+ usb_dw_flush_tx_fifo(ep_idx);
+
+ val = usb_dw_tx_fifo_avail(ep_idx);
+ usb_dw_ctrl.in_ep_ctrl[ep_idx].fifo_size = val;
+
+ return 0;
+}
+
+static int usb_dw_ep_set(uint8_t ep,
+ uint32_t ep_mps, enum usb_dc_ep_transfer_type ep_type)
+{
+ volatile uint32_t *p_depctl;
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ LOG_DBG("%s ep %x, mps %d, type %d", __func__, ep, ep_mps, ep_type);
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ p_depctl = &USB_DW->out_ep_reg[ep_idx].doepctl;
+ usb_dw_ctrl.out_ep_ctrl[ep_idx].mps = ep_mps;
+ } else {
+ p_depctl = &USB_DW->in_ep_reg[ep_idx].diepctl;
+ usb_dw_ctrl.in_ep_ctrl[ep_idx].mps = ep_mps;
+ }
+
+ if (!ep_idx) {
+ /* Set max packet size for EP0 */
+ *p_depctl &= ~USB_DW_DEPCTL0_MSP_MASK;
+
+ switch (ep_mps) {
+ case 8:
+ *p_depctl |= USB_DW_DEPCTL0_MSP_8 <<
+ USB_DW_DEPCTL_MSP_OFFSET;
+ break;
+ case 16:
+ *p_depctl |= USB_DW_DEPCTL0_MSP_16 <<
+ USB_DW_DEPCTL_MSP_OFFSET;
+ break;
+ case 32:
+ *p_depctl |= USB_DW_DEPCTL0_MSP_32 <<
+ USB_DW_DEPCTL_MSP_OFFSET;
+ break;
+ case 64:
+ *p_depctl |= USB_DW_DEPCTL0_MSP_64 <<
+ USB_DW_DEPCTL_MSP_OFFSET;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* No need to set EP0 type */
+ } else {
+ /* Set max packet size for EP */
+ if (ep_mps > (USB_DW_DEPCTLn_MSP_MASK >>
+ USB_DW_DEPCTL_MSP_OFFSET)) {
+ return -EINVAL;
+ }
+
+ *p_depctl &= ~USB_DW_DEPCTLn_MSP_MASK;
+ *p_depctl |= ep_mps << USB_DW_DEPCTL_MSP_OFFSET;
+
+ /* Set endpoint type */
+ *p_depctl &= ~USB_DW_DEPCTL_EP_TYPE_MASK;
+
+ switch (ep_type) {
+ case USB_DC_EP_CONTROL:
+ *p_depctl |= USB_DW_DEPCTL_EP_TYPE_CONTROL <<
+ USB_DW_DEPCTL_EP_TYPE_OFFSET;
+ break;
+ case USB_DC_EP_BULK:
+ *p_depctl |= USB_DW_DEPCTL_EP_TYPE_BULK <<
+ USB_DW_DEPCTL_EP_TYPE_OFFSET;
+ break;
+ case USB_DC_EP_INTERRUPT:
+ *p_depctl |= USB_DW_DEPCTL_EP_TYPE_INTERRUPT <<
+ USB_DW_DEPCTL_EP_TYPE_OFFSET;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* sets the Endpoint Data PID to DATA0 */
+ *p_depctl |= USB_DW_DEPCTL_SETDOPID;
+ }
+
+ if (USB_EP_DIR_IS_IN(ep)) {
+ int ret = usb_dw_set_fifo(ep);
+
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void usb_dw_prep_rx(const uint8_t ep, uint8_t setup)
+{
+ enum usb_dw_out_ep_idx ep_idx = USB_EP_GET_IDX(ep);
+ uint32_t ep_mps = usb_dw_ctrl.out_ep_ctrl[ep_idx].mps;
+
+ /* Set max RX size to EP mps so we get an interrupt
+ * each time a packet is received
+ */
+
+ USB_DW->out_ep_reg[ep_idx].doeptsiz =
+ (USB_DW_SUP_CNT << USB_DW_DOEPTSIZ_SUP_CNT_OFFSET) |
+ (1 << USB_DW_DEPTSIZ_PKT_CNT_OFFSET) | ep_mps;
+
+ /* Clear NAK and enable ep */
+ if (!setup) {
+ USB_DW->out_ep_reg[ep_idx].doepctl |= USB_DW_DEPCTL_CNAK;
+ }
+
+ USB_DW->out_ep_reg[ep_idx].doepctl |= USB_DW_DEPCTL_EP_ENA;
+
+ LOG_DBG("USB OUT EP%d armed", ep_idx);
+}
+
+static int usb_dw_tx(uint8_t ep, const uint8_t *const data,
+ uint32_t data_len)
+{
+ enum usb_dw_in_ep_idx ep_idx = USB_EP_GET_IDX(ep);
+ uint32_t max_xfer_size, max_pkt_cnt, pkt_cnt, avail_space;
+ uint32_t ep_mps = usb_dw_ctrl.in_ep_ctrl[ep_idx].mps;
+ unsigned int key;
+ uint32_t i;
+
+ /* Wait for FIFO space available */
+ do {
+ avail_space = usb_dw_tx_fifo_avail(ep_idx);
+ if (avail_space == usb_dw_ctrl.in_ep_ctrl[ep_idx].fifo_size) {
+ break;
+ }
+ /* Make sure we don't hog the CPU */
+ k_yield();
+ } while (1);
+
+ key = irq_lock();
+
+ avail_space *= 4U;
+ if (!avail_space) {
+ LOG_ERR("USB IN EP%d no space available, DTXFSTS %x", ep_idx,
+ USB_DW->in_ep_reg[ep_idx].dtxfsts);
+ irq_unlock(key);
+ return -EAGAIN;
+ }
+
+ /* For now tx-fifo sizes are not configured (cf usb_dw_set_fifo). Here
+ * we force available fifo size to be a multiple of ep mps in order to
+ * prevent splitting data incorrectly.
+ */
+ avail_space -= avail_space % ep_mps;
+ if (data_len > avail_space) {
+ data_len = avail_space;
+ }
+
+ if (data_len != 0U) {
+ /* Get max packet size and packet count for ep */
+ if (ep_idx == USB_DW_IN_EP_0) {
+ max_pkt_cnt =
+ USB_DW_DIEPTSIZ0_PKT_CNT_MASK >>
+ USB_DW_DEPTSIZ_PKT_CNT_OFFSET;
+ max_xfer_size =
+ USB_DW_DEPTSIZ0_XFER_SIZE_MASK >>
+ USB_DW_DEPTSIZ_XFER_SIZE_OFFSET;
+ } else {
+ max_pkt_cnt =
+ USB_DW_DIEPTSIZn_PKT_CNT_MASK >>
+ USB_DW_DEPTSIZ_PKT_CNT_OFFSET;
+ max_xfer_size =
+ USB_DW_DEPTSIZn_XFER_SIZE_MASK >>
+ USB_DW_DEPTSIZ_XFER_SIZE_OFFSET;
+ }
+
+ /* Check if transfer len is too big */
+ if (data_len > max_xfer_size) {
+ LOG_WRN("USB IN EP%d len too big (%d->%d)", ep_idx,
+ data_len, max_xfer_size);
+ data_len = max_xfer_size;
+ }
+
+ /*
+ * Program the transfer size and packet count as follows:
+ *
+ * transfer size = N * ep_maxpacket + short_packet
+ * pktcnt = N + (short_packet exist ? 1 : 0)
+ */
+
+ pkt_cnt = (data_len + ep_mps - 1) / ep_mps;
+ if (pkt_cnt > max_pkt_cnt) {
+ LOG_WRN("USB IN EP%d pkt count too big (%d->%d)",
+ ep_idx, pkt_cnt, pkt_cnt);
+ pkt_cnt = max_pkt_cnt;
+ data_len = pkt_cnt * ep_mps;
+ }
+ } else {
+ /* Zero length packet */
+ pkt_cnt = 1U;
+ }
+
+ /* Set number of packets and transfer size */
+ USB_DW->in_ep_reg[ep_idx].dieptsiz =
+ (pkt_cnt << USB_DW_DEPTSIZ_PKT_CNT_OFFSET) | data_len;
+
+ /* Clear NAK and enable ep */
+ USB_DW->in_ep_reg[ep_idx].diepctl |= (USB_DW_DEPCTL_EP_ENA |
+ USB_DW_DEPCTL_CNAK);
+
+ /*
+ * Write data to FIFO, make sure that we are protected against
+ * other USB register accesses. According to "DesignWare Cores
+ * USB 1.1/2.0 Device Subsystem-AHB/VCI Databook": "During FIFO
+ * access, the application must not access the UDC/Subsystem
+ * registers or vendor registers (for ULPI mode). After starting
+ * to access a FIFO, the application must complete the transaction
+ * before accessing the register."
+ */
+ for (i = 0U; i < data_len; i += 4U) {
+ uint32_t val = data[i];
+
+ if (i + 1 < data_len) {
+ val |= ((uint32_t)data[i+1]) << 8;
+ }
+ if (i + 2 < data_len) {
+ val |= ((uint32_t)data[i+2]) << 16;
+ }
+ if (i + 3 < data_len) {
+ val |= ((uint32_t)data[i+3]) << 24;
+ }
+
+ USB_DW_EP_FIFO(ep_idx) = val;
+ }
+
+ irq_unlock(key);
+
+ LOG_DBG("USB IN EP%d write %u bytes", ep_idx, data_len);
+
+ return data_len;
+}
+
+static int usb_dw_init(void)
+{
+ uint8_t ep;
+ int ret;
+
+ ret = usb_dw_reset();
+ if (ret) {
+ return ret;
+ }
+
+#ifdef CONFIG_USB_DW_USB_2_0
+ /* set the PHY interface to be 16-bit UTMI */
+ USB_DW->gusbcfg = (USB_DW->gusbcfg & ~USB_DW_GUSBCFG_PHY_IF_MASK) |
+ USB_DW_GUSBCFG_PHY_IF_16_BIT;
+
+ /* Set USB2.0 High Speed */
+ USB_DW->dcfg |= USB_DW_DCFG_DEV_SPD_USB2_HS;
+#else
+ /* Set device speed to Full Speed */
+ USB_DW->dcfg |= USB_DW_DCFG_DEV_SPD_FS;
+#endif
+
+ /* Set NAK for all OUT EPs */
+ for (ep = 0U; ep < USB_DW_OUT_EP_NUM; ep++) {
+ USB_DW->out_ep_reg[ep].doepctl = USB_DW_DEPCTL_SNAK;
+ }
+
+ /* Enable global interrupts */
+ USB_DW->gintmsk = USB_DW_GINTSTS_OEP_INT |
+ USB_DW_GINTSTS_IEP_INT |
+ USB_DW_GINTSTS_ENUM_DONE |
+ USB_DW_GINTSTS_USB_RST |
+ USB_DW_GINTSTS_WK_UP_INT |
+ USB_DW_GINTSTS_USB_SUSP;
+
+ /* Enable global interrupt */
+ USB_DW->gahbcfg |= USB_DW_GAHBCFG_GLB_INTR_MASK;
+
+ /* Disable soft disconnect */
+ USB_DW->dctl &= ~USB_DW_DCTL_SFT_DISCON;
+
+ usb_dw_reg_dump();
+
+ return 0;
+}
+
+static void usb_dw_handle_reset(void)
+{
+ LOG_DBG("USB RESET event");
+
+ /* Inform upper layers */
+ if (usb_dw_ctrl.status_cb) {
+ usb_dw_ctrl.status_cb(USB_DC_RESET, NULL);
+ }
+
+ /* Clear device address during reset. */
+ USB_DW->dcfg &= ~USB_DW_DCFG_DEV_ADDR_MASK;
+
+ /* enable global EP interrupts */
+ USB_DW->doepmsk = 0U;
+ USB_DW->gintmsk |= USB_DW_GINTSTS_RX_FLVL;
+ USB_DW->diepmsk |= USB_DW_DIEPINT_XFER_COMPL;
+}
+
+static void usb_dw_handle_enum_done(void)
+{
+ uint32_t speed;
+
+ speed = (USB_DW->dsts & ~USB_DW_DSTS_ENUM_SPD_MASK) >>
+ USB_DW_DSTS_ENUM_SPD_OFFSET;
+
+ LOG_DBG("USB ENUM DONE event, %s speed detected",
+ speed == USB_DW_DSTS_ENUM_LS ? "Low" : "Full");
+
+ /* Inform upper layers */
+ if (usb_dw_ctrl.status_cb) {
+ usb_dw_ctrl.status_cb(USB_DC_CONNECTED, NULL);
+ }
+}
+
+/* USB ISR handler */
+static inline void usb_dw_int_rx_flvl_handler(void)
+{
+ uint32_t grxstsp = USB_DW->grxstsp;
+ uint32_t status, xfer_size;
+ uint8_t ep_idx;
+ usb_dc_ep_callback ep_cb;
+
+ /* Packet in RX FIFO */
+
+ ep_idx = grxstsp & USB_DW_GRXSTSR_EP_NUM_MASK;
+ status = (grxstsp & USB_DW_GRXSTSR_PKT_STS_MASK) >>
+ USB_DW_GRXSTSR_PKT_STS_OFFSET;
+ xfer_size = (grxstsp & USB_DW_GRXSTSR_PKT_CNT_MASK) >>
+ USB_DW_GRXSTSR_PKT_CNT_OFFSET;
+
+ LOG_DBG("USB OUT EP%u: RX_FLVL status %u, size %u",
+ ep_idx, status, xfer_size);
+
+ usb_dw_ctrl.out_ep_ctrl[ep_idx].data_len = xfer_size;
+ ep_cb = usb_dw_ctrl.out_ep_ctrl[ep_idx].cb;
+
+ switch (status) {
+ case USB_DW_GRXSTSR_PKT_STS_SETUP:
+ /* Call the registered callback if any */
+ if (ep_cb) {
+ ep_cb(USB_EP_GET_ADDR(ep_idx, USB_EP_DIR_OUT),
+ USB_DC_EP_SETUP);
+ }
+
+ break;
+ case USB_DW_GRXSTSR_PKT_STS_OUT_DATA:
+ if (ep_cb) {
+ ep_cb(USB_EP_GET_ADDR(ep_idx, USB_EP_DIR_OUT),
+ USB_DC_EP_DATA_OUT);
+ }
+
+ break;
+ case USB_DW_GRXSTSR_PKT_STS_OUT_DATA_DONE:
+ case USB_DW_GRXSTSR_PKT_STS_SETUP_DONE:
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void usb_dw_int_iep_handler(void)
+{
+ uint32_t ep_int_status;
+ uint8_t ep_idx;
+ usb_dc_ep_callback ep_cb;
+
+ for (ep_idx = 0U; ep_idx < USB_DW_IN_EP_NUM; ep_idx++) {
+ if (USB_DW->daint & USB_DW_DAINT_IN_EP_INT(ep_idx)) {
+ /* Read IN EP interrupt status */
+ ep_int_status = USB_DW->in_ep_reg[ep_idx].diepint &
+ USB_DW->diepmsk;
+
+ /* Clear IN EP interrupts */
+ USB_DW->in_ep_reg[ep_idx].diepint = ep_int_status;
+
+ LOG_DBG("USB IN EP%u interrupt status: 0x%x",
+ ep_idx, ep_int_status);
+
+ ep_cb = usb_dw_ctrl.in_ep_ctrl[ep_idx].cb;
+ if (ep_cb &&
+ (ep_int_status & USB_DW_DIEPINT_XFER_COMPL)) {
+
+ /* Call the registered callback */
+ ep_cb(USB_EP_GET_ADDR(ep_idx, USB_EP_DIR_IN),
+ USB_DC_EP_DATA_IN);
+ }
+ }
+ }
+
+ /* Clear interrupt. */
+ USB_DW->gintsts = USB_DW_GINTSTS_IEP_INT;
+}
+
+static inline void usb_dw_int_oep_handler(void)
+{
+ uint32_t ep_int_status;
+ uint8_t ep_idx;
+
+ for (ep_idx = 0U; ep_idx < USB_DW_OUT_EP_NUM; ep_idx++) {
+ if (USB_DW->daint & USB_DW_DAINT_OUT_EP_INT(ep_idx)) {
+ /* Read OUT EP interrupt status */
+ ep_int_status = USB_DW->out_ep_reg[ep_idx].doepint &
+ USB_DW->doepmsk;
+
+ /* Clear OUT EP interrupts */
+ USB_DW->out_ep_reg[ep_idx].doepint = ep_int_status;
+
+ LOG_DBG("USB OUT EP%u interrupt status: 0x%x\n",
+ ep_idx, ep_int_status);
+ }
+ }
+
+ /* Clear interrupt. */
+ USB_DW->gintsts = USB_DW_GINTSTS_OEP_INT;
+}
+
+static void usb_dw_isr_handler(const void *unused)
+{
+ uint32_t int_status;
+
+ ARG_UNUSED(unused);
+
+ /* Read interrupt status */
+ while ((int_status = (USB_DW->gintsts & USB_DW->gintmsk))) {
+
+ LOG_DBG("USB GINTSTS 0x%x", int_status);
+
+ if (int_status & USB_DW_GINTSTS_USB_RST) {
+ /* Clear interrupt. */
+ USB_DW->gintsts = USB_DW_GINTSTS_USB_RST;
+
+ /* Reset detected */
+ usb_dw_handle_reset();
+ }
+
+ if (int_status & USB_DW_GINTSTS_ENUM_DONE) {
+ /* Clear interrupt. */
+ USB_DW->gintsts = USB_DW_GINTSTS_ENUM_DONE;
+
+ /* Enumeration done detected */
+ usb_dw_handle_enum_done();
+ }
+
+ if (int_status & USB_DW_GINTSTS_USB_SUSP) {
+ /* Clear interrupt. */
+ USB_DW->gintsts = USB_DW_GINTSTS_USB_SUSP;
+
+ if (usb_dw_ctrl.status_cb) {
+ usb_dw_ctrl.status_cb(USB_DC_SUSPEND, NULL);
+ }
+ }
+
+ if (int_status & USB_DW_GINTSTS_WK_UP_INT) {
+ /* Clear interrupt. */
+ USB_DW->gintsts = USB_DW_GINTSTS_WK_UP_INT;
+
+ if (usb_dw_ctrl.status_cb) {
+ usb_dw_ctrl.status_cb(USB_DC_RESUME, NULL);
+ }
+ }
+
+ if (int_status & USB_DW_GINTSTS_RX_FLVL) {
+ /* Packet in RX FIFO */
+ usb_dw_int_rx_flvl_handler();
+ }
+
+ if (int_status & USB_DW_GINTSTS_IEP_INT) {
+ /* IN EP interrupt */
+ usb_dw_int_iep_handler();
+ }
+
+ if (int_status & USB_DW_GINTSTS_OEP_INT) {
+ /* No OUT interrupt expected in FIFO mode,
+ * just clear interruot
+ */
+ usb_dw_int_oep_handler();
+ }
+ }
+}
+
+int usb_dc_attach(void)
+{
+ int ret;
+
+ if (usb_dw_ctrl.attached) {
+ return 0;
+ }
+
+ ret = usb_dw_init();
+ if (ret) {
+ return ret;
+ }
+
+ /* Connect and enable USB interrupt */
+ IRQ_CONNECT(DT_INST_IRQN(0),
+ DT_INST_IRQ(0, priority),
+ usb_dw_isr_handler, 0,
+ DT_INST_IRQ(0, sense));
+ irq_enable(DT_INST_IRQN(0));
+
+ usb_dw_ctrl.attached = 1U;
+
+ return 0;
+}
+
+int usb_dc_detach(void)
+{
+ if (!usb_dw_ctrl.attached) {
+ return 0;
+ }
+
+ irq_disable(DT_INST_IRQN(0));
+
+ /* Enable soft disconnect */
+ USB_DW->dctl |= USB_DW_DCTL_SFT_DISCON;
+
+ usb_dw_ctrl.attached = 0U;
+
+ return 0;
+}
+
+int usb_dc_reset(void)
+{
+ int ret;
+
+ ret = usb_dw_reset();
+
+ /* Clear private data */
+ (void)memset(&usb_dw_ctrl, 0, sizeof(usb_dw_ctrl));
+
+ return ret;
+}
+
+int usb_dc_set_address(const uint8_t addr)
+{
+ if (addr > (USB_DW_DCFG_DEV_ADDR_MASK >> USB_DW_DCFG_DEV_ADDR_OFFSET)) {
+ return -EINVAL;
+ }
+
+ USB_DW->dcfg &= ~USB_DW_DCFG_DEV_ADDR_MASK;
+ USB_DW->dcfg |= addr << USB_DW_DCFG_DEV_ADDR_OFFSET;
+
+ return 0;
+}
+
+int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data * const cfg)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(cfg->ep_addr);
+
+ LOG_DBG("ep %x, mps %d, type %d", cfg->ep_addr, cfg->ep_mps,
+ cfg->ep_type);
+
+ if ((cfg->ep_type == USB_DC_EP_CONTROL) && ep_idx) {
+ LOG_ERR("invalid endpoint configuration");
+ return -1;
+ }
+
+ if (cfg->ep_mps > DW_USB_MAX_PACKET_SIZE) {
+ LOG_WRN("unsupported packet size");
+ return -1;
+ }
+
+ if ((USB_EP_DIR_IS_OUT(cfg->ep_addr)) &&
+ (ep_idx >= DW_USB_OUT_EP_NUM)) {
+ LOG_WRN("OUT endpoint address out of range");
+ return -1;
+ }
+
+ if ((USB_EP_DIR_IS_IN(cfg->ep_addr)) &&
+ (ep_idx >= DW_USB_IN_EP_NUM)) {
+ LOG_WRN("IN endpoint address out of range");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data * const ep_cfg)
+{
+ uint8_t ep;
+
+ if (!ep_cfg) {
+ return -EINVAL;
+ }
+
+ ep = ep_cfg->ep_addr;
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ usb_dw_ep_set(ep, ep_cfg->ep_mps, ep_cfg->ep_type);
+
+ return 0;
+}
+
+int usb_dc_ep_set_stall(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ USB_DW->out_ep_reg[ep_idx].doepctl |= USB_DW_DEPCTL_STALL;
+ } else {
+ USB_DW->in_ep_reg[ep_idx].diepctl |= USB_DW_DEPCTL_STALL;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_clear_stall(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ if (!ep_idx) {
+ /* Not possible to clear stall for EP0 */
+ return -EINVAL;
+ }
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ USB_DW->out_ep_reg[ep_idx].doepctl &= ~USB_DW_DEPCTL_STALL;
+ } else {
+ USB_DW->in_ep_reg[ep_idx].diepctl &= ~USB_DW_DEPCTL_STALL;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_halt(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ volatile uint32_t *p_depctl;
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ if (!ep_idx) {
+ /* Cannot disable EP0, just set stall */
+ usb_dc_ep_set_stall(ep);
+ } else {
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ p_depctl = &USB_DW->out_ep_reg[ep_idx].doepctl;
+ } else {
+ p_depctl = &USB_DW->in_ep_reg[ep_idx].diepctl;
+ }
+
+ /* Set STALL and disable endpoint if enabled */
+ if (*p_depctl & USB_DW_DEPCTL_EP_ENA) {
+ *p_depctl |= USB_DW_DEPCTL_EP_DIS | USB_DW_DEPCTL_STALL;
+ } else {
+ *p_depctl |= USB_DW_DEPCTL_STALL;
+ }
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_is_stalled(const uint8_t ep, uint8_t *const stalled)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ if (!stalled) {
+ return -EINVAL;
+ }
+
+ *stalled = 0U;
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ if (USB_DW->out_ep_reg[ep_idx].doepctl & USB_DW_DEPCTL_STALL) {
+ *stalled = 1U;
+ }
+ } else {
+ if (USB_DW->in_ep_reg[ep_idx].diepctl & USB_DW_DEPCTL_STALL) {
+ *stalled = 1U;
+ }
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_enable(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ /* enable EP interrupts */
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ USB_DW->daintmsk |= USB_DW_DAINT_OUT_EP_INT(ep_idx);
+ } else {
+ USB_DW->daintmsk |= USB_DW_DAINT_IN_EP_INT(ep_idx);
+ }
+
+ /* Activate Ep */
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ USB_DW->out_ep_reg[ep_idx].doepctl |= USB_DW_DEPCTL_USB_ACT_EP;
+ usb_dw_ctrl.out_ep_ctrl[ep_idx].ep_ena = 1U;
+ } else {
+ USB_DW->in_ep_reg[ep_idx].diepctl |= USB_DW_DEPCTL_USB_ACT_EP;
+ usb_dw_ctrl.in_ep_ctrl[ep_idx].ep_ena = 1U;
+ }
+
+ if (USB_EP_DIR_IS_OUT(ep) &&
+ usb_dw_ctrl.out_ep_ctrl[ep_idx].cb != usb_transfer_ep_callback) {
+ /* Start reading now, except for transfer managed eps */
+ usb_dw_prep_rx(ep, 0);
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_disable(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ /* Disable EP interrupts */
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ USB_DW->daintmsk &= ~USB_DW_DAINT_OUT_EP_INT(ep_idx);
+ USB_DW->doepmsk &= ~USB_DW_DOEPINT_SET_UP;
+ } else {
+ USB_DW->daintmsk &= ~USB_DW_DAINT_IN_EP_INT(ep_idx);
+ USB_DW->diepmsk &= ~USB_DW_DIEPINT_XFER_COMPL;
+ USB_DW->gintmsk &= ~USB_DW_GINTSTS_RX_FLVL;
+ }
+
+ /* De-activate, disable and set NAK for Ep */
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ USB_DW->out_ep_reg[ep_idx].doepctl &=
+ ~(USB_DW_DEPCTL_USB_ACT_EP |
+ USB_DW_DEPCTL_EP_ENA |
+ USB_DW_DEPCTL_SNAK);
+ usb_dw_ctrl.out_ep_ctrl[ep_idx].ep_ena = 0U;
+ } else {
+ USB_DW->in_ep_reg[ep_idx].diepctl &=
+ ~(USB_DW_DEPCTL_USB_ACT_EP |
+ USB_DW_DEPCTL_EP_ENA |
+ USB_DW_DEPCTL_SNAK);
+ usb_dw_ctrl.in_ep_ctrl[ep_idx].ep_ena = 0U;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_flush(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint32_t cnt;
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ /* RX FIFO is global and cannot be flushed per EP */
+ return -EINVAL;
+ }
+
+ /* Each endpoint has dedicated Tx FIFO */
+ USB_DW->grstctl |= ep_idx << USB_DW_GRSTCTL_TX_FNUM_OFFSET;
+ USB_DW->grstctl |= USB_DW_GRSTCTL_TX_FFLSH;
+
+ cnt = 0U;
+
+ do {
+ if (++cnt > USB_DW_CORE_RST_TIMEOUT_US) {
+ LOG_ERR("USB TX FIFO flush HANG!");
+ return -EIO;
+ }
+ usb_dw_udelay(1);
+ } while (USB_DW->grstctl & USB_DW_GRSTCTL_TX_FFLSH);
+
+ return 0;
+}
+
+int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data,
+ const uint32_t data_len, uint32_t * const ret_bytes)
+{
+ int ret;
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ /* Check if IN ep */
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_IN) {
+ return -EINVAL;
+ }
+
+ /* Check if ep enabled */
+ if (!usb_dw_ep_is_enabled(ep)) {
+ return -EINVAL;
+ }
+
+ ret = usb_dw_tx(ep, data, data_len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (ret_bytes) {
+ *ret_bytes = ret;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
+ uint32_t *read_bytes)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint32_t i, j, data_len, bytes_to_copy;
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ /* Check if OUT ep */
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_OUT) {
+ LOG_ERR("Wrong endpoint direction");
+ return -EINVAL;
+ }
+
+ /* Allow to read 0 bytes */
+ if (!data && max_data_len) {
+ LOG_ERR("Wrong arguments");
+ return -EINVAL;
+ }
+
+ /* Check if ep enabled */
+ if (!usb_dw_ep_is_enabled(ep)) {
+ LOG_ERR("Not enabled endpoint");
+ return -EINVAL;
+ }
+
+ data_len = usb_dw_ctrl.out_ep_ctrl[ep_idx].data_len;
+
+ if (!data && !max_data_len) {
+ /* When both buffer and max data to read are zero return
+ * the available data in buffer
+ */
+ if (read_bytes) {
+ *read_bytes = data_len;
+ }
+ return 0;
+ }
+
+
+ if (data_len > max_data_len) {
+ LOG_ERR("Not enough room to copy all the rcvd data!");
+ bytes_to_copy = max_data_len;
+ } else {
+ bytes_to_copy = data_len;
+ }
+
+ LOG_DBG("Read EP%d, req %d, read %d bytes", ep, max_data_len,
+ bytes_to_copy);
+
+ /* Data in the FIFOs is always stored per 32-bit words */
+ for (i = 0U; i < (bytes_to_copy & ~0x3); i += 4U) {
+ *(uint32_t *)(data + i) = USB_DW_EP_FIFO(ep_idx);
+ }
+ if (bytes_to_copy & 0x3) {
+ /* Not multiple of 4 */
+ uint32_t last_dw = USB_DW_EP_FIFO(ep_idx);
+
+ for (j = 0U; j < (bytes_to_copy & 0x3); j++) {
+ *(data + i + j) =
+ (sys_cpu_to_le32(last_dw) >> (j * 8U)) & 0xFF;
+ }
+ }
+
+ usb_dw_ctrl.out_ep_ctrl[ep_idx].data_len -= bytes_to_copy;
+
+ if (read_bytes) {
+ *read_bytes = bytes_to_copy;
+ }
+
+ return 0;
+
+}
+
+int usb_dc_ep_read_continue(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ /* Check if OUT ep */
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_OUT) {
+ LOG_ERR("Wrong endpoint direction");
+ return -EINVAL;
+ }
+
+ if (!usb_dw_ctrl.out_ep_ctrl[ep_idx].data_len) {
+ usb_dw_prep_rx(ep_idx, 0);
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
+ const uint32_t max_data_len, uint32_t * const read_bytes)
+{
+ if (usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes) != 0) {
+ return -EINVAL;
+ }
+
+ if (!data && !max_data_len) {
+ /* When both buffer and max data to read are zero the above
+ * call would fetch the data len and we simply return.
+ */
+ return 0;
+ }
+
+ if (usb_dc_ep_read_continue(ep) != 0) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ if (USB_EP_DIR_IS_IN(ep)) {
+ usb_dw_ctrl.in_ep_ctrl[ep_idx].cb = cb;
+ } else {
+ usb_dw_ctrl.out_ep_ctrl[ep_idx].cb = cb;
+ }
+
+ return 0;
+}
+
+void usb_dc_set_status_callback(const usb_dc_status_callback cb)
+{
+ usb_dw_ctrl.status_cb = cb;
+}
+
+int usb_dc_ep_mps(const uint8_t ep)
+{
+ enum usb_dw_out_ep_idx ep_idx = USB_EP_GET_IDX(ep);
+
+ if (!usb_dw_ctrl.attached || !usb_dw_ep_is_valid(ep)) {
+ LOG_ERR("Not attached / Invalid endpoint: EP 0x%x", ep);
+ return -EINVAL;
+ }
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ return usb_dw_ctrl.out_ep_ctrl[ep_idx].mps;
+ } else {
+ return usb_dw_ctrl.in_ep_ctrl[ep_idx].mps;
+ }
+}
diff --git a/port/freescale/usb_dc_kinetis.c b/port/freescale/usb_dc_kinetis.c
new file mode 100644
index 00000000..24cef356
--- /dev/null
+++ b/port/freescale/usb_dc_kinetis.c
@@ -0,0 +1,1048 @@
+/* usb_dc_kinetis.c - Kinetis USBFSOTG usb device driver */
+
+/*
+ * Copyright (c) 2017 PHYTEC Messtechnik GmbH
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT nxp_kinetis_usbd
+
+#include <soc.h>
+#include <string.h>
+#include <stdio.h>
+#include <kernel.h>
+#include <sys/byteorder.h>
+#include <usb/usb_device.h>
+#include <device.h>
+
+#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
+#include <logging/log.h>
+LOG_MODULE_REGISTER(usb_dc_kinetis);
+
+#define NUM_OF_EP_MAX DT_INST_PROP(0, num_bidir_endpoints)
+
+#define BD_OWN_MASK (1 << 5)
+#define BD_DATA01_MASK (1 << 4)
+#define BD_KEEP_MASK (1 << 3)
+#define BD_NINC_MASK (1 << 2)
+#define BD_DTS_MASK (1 << 1)
+#define BD_STALL_MASK (1 << 0)
+
+#define KINETIS_SETUP_TOKEN 0x0d
+#define KINETIS_IN_TOKEN 0x09
+#define KINETIS_OUT_TOKEN 0x01
+
+#define USBFSOTG_PERID 0x04
+#define USBFSOTG_REV 0x33
+
+#define KINETIS_EP_NUMOF_MASK 0xf
+#define KINETIS_ADDR2IDX(addr) ((addr) & (KINETIS_EP_NUMOF_MASK))
+
+/*
+ * Buffer Descriptor (BD) entry provides endpoint buffer control
+ * information for USBFS controller. Every endpoint direction requires
+ * two BD entries.
+ */
+struct buf_descriptor {
+ union {
+ uint32_t bd_fields;
+
+ struct {
+ uint32_t reserved_1_0 : 2;
+ uint32_t tok_pid : 4;
+ uint32_t data01 : 1;
+ uint32_t own : 1;
+ uint32_t reserved_15_8 : 8;
+ uint32_t bc : 16;
+ } get __packed;
+
+ struct {
+ uint32_t reserved_1_0 : 2;
+ uint32_t bd_ctrl : 6;
+ uint32_t reserved_15_8 : 8;
+ uint32_t bc : 16;
+ } set __packed;
+
+ } __packed;
+ uint32_t buf_addr;
+} __packed;
+
+/*
+ * Buffer Descriptor Table for the endpoints buffer management.
+ * The driver configuration with 16 fully bidirectional endpoints would require
+ * four BD entries per endpoint and 512 bytes of memory.
+ */
+static struct buf_descriptor __aligned(512) bdt[(NUM_OF_EP_MAX) * 2 * 2];
+
+#define BD_IDX_EP0TX_EVEN 2
+#define BD_IDX_EP0TX_ODD 3
+
+#define EP_BUF_NUMOF_BLOCKS (NUM_OF_EP_MAX / 2)
+
+K_HEAP_DEFINE(ep_buf_pool, 512 * EP_BUF_NUMOF_BLOCKS + 128);
+
+struct ep_mem_block {
+ void *data;
+};
+
+struct usb_ep_ctrl_data {
+ struct ep_status {
+ uint16_t in_enabled : 1;
+ uint16_t out_enabled : 1;
+ uint16_t in_data1 : 1;
+ uint16_t out_data1 : 1;
+ uint16_t in_odd : 1;
+ uint16_t out_odd : 1;
+ uint16_t in_stalled : 1;
+ uint16_t out_stalled : 1;
+ } status;
+ uint16_t mps_in;
+ uint16_t mps_out;
+ struct ep_mem_block mblock_in;
+ struct ep_mem_block mblock_out;
+ usb_dc_ep_callback cb_in;
+ usb_dc_ep_callback cb_out;
+};
+
+#define USBD_THREAD_STACK_SIZE 1024
+
+struct usb_device_data {
+ usb_dc_status_callback status_cb;
+ uint8_t address;
+ uint32_t bd_active;
+ struct usb_ep_ctrl_data ep_ctrl[NUM_OF_EP_MAX];
+ bool attached;
+
+ K_KERNEL_STACK_MEMBER(thread_stack, USBD_THREAD_STACK_SIZE);
+ struct k_thread thread;
+};
+
+static struct usb_device_data dev_data;
+
+#define USB_DC_CB_TYPE_MGMT 0
+#define USB_DC_CB_TYPE_EP 1
+
+struct cb_msg {
+ uint8_t ep;
+ uint8_t type;
+ uint32_t cb;
+};
+
+K_MSGQ_DEFINE(usb_dc_msgq, sizeof(struct cb_msg), 10, 4);
+static void usb_kinetis_isr_handler(void);
+static void usb_kinetis_thread_main(void *arg1, void *unused1, void *unused2);
+
+/*
+ * This function returns the BD element index based on
+ * endpoint address and the odd bit.
+ */
+static inline uint8_t get_bdt_idx(uint8_t ep, uint8_t odd)
+{
+ if (ep & USB_EP_DIR_IN) {
+ return ((((KINETIS_ADDR2IDX(ep)) * 4) + 2 + (odd & 1)));
+ }
+ return ((((KINETIS_ADDR2IDX(ep)) * 4) + (odd & 1)));
+}
+
+static int kinetis_usb_init(void)
+{
+ /* enable USB voltage regulator */
+ SIM->SOPT1 |= SIM_SOPT1_USBREGEN_MASK;
+
+ USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
+ k_busy_wait(2000);
+
+ USB0->CTL = 0;
+ /* enable USB module, AKA USBEN bit in CTL1 register */
+ USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
+
+ if ((USB0->PERID != USBFSOTG_PERID) ||
+ (USB0->REV != USBFSOTG_REV)) {
+ return -1;
+ }
+
+ USB0->BDTPAGE1 = (uint8_t)(((uint32_t)bdt) >> 8);
+ USB0->BDTPAGE2 = (uint8_t)(((uint32_t)bdt) >> 16);
+ USB0->BDTPAGE3 = (uint8_t)(((uint32_t)bdt) >> 24);
+
+ /* clear interrupt flags */
+ USB0->ISTAT = 0xFF;
+
+ /* enable reset interrupt */
+ USB0->INTEN = USB_INTEN_USBRSTEN_MASK;
+
+ USB0->USBCTRL = USB_USBCTRL_PDE_MASK;
+
+ k_thread_create(&dev_data.thread, dev_data.thread_stack,
+ USBD_THREAD_STACK_SIZE,
+ usb_kinetis_thread_main, NULL, NULL, NULL,
+ K_PRIO_COOP(2), 0, K_NO_WAIT);
+
+ /* Connect and enable USB interrupt */
+ IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
+ usb_kinetis_isr_handler, 0, 0);
+
+ irq_enable(DT_INST_IRQN(0));
+
+ LOG_DBG("");
+
+ return 0;
+}
+
+int usb_dc_reset(void)
+{
+ for (uint8_t i = 0; i < 16; i++) {
+ USB0->ENDPOINT[i].ENDPT = 0;
+ }
+ (void)memset(bdt, 0, sizeof(bdt));
+ dev_data.bd_active = 0U;
+ dev_data.address = 0U;
+
+ USB0->CTL |= USB_CTL_ODDRST_MASK;
+ USB0->CTL &= ~USB_CTL_ODDRST_MASK;
+
+ /* Clear interrupt status flags */
+ USB0->ISTAT = 0xFF;
+ /* Clear error flags */
+ USB0->ERRSTAT = 0xFF;
+ /* Enable all error interrupt sources */
+ USB0->ERREN = 0xFF;
+ /* Reset default address */
+ USB0->ADDR = 0x00;
+
+ USB0->INTEN = (USB_INTEN_USBRSTEN_MASK |
+ USB_INTEN_TOKDNEEN_MASK |
+ USB_INTEN_SLEEPEN_MASK |
+ USB_INTEN_SOFTOKEN_MASK |
+ USB_INTEN_STALLEN_MASK |
+ USB_INTEN_ERROREN_MASK);
+
+ LOG_DBG("");
+
+ return 0;
+}
+
+int usb_dc_attach(void)
+{
+ if (dev_data.attached) {
+ LOG_WRN("already attached");
+ }
+
+ kinetis_usb_init();
+
+ /*
+ * Call usb_dc_reset here because the device stack does not make it
+ * after USB_DC_RESET status event.
+ */
+ usb_dc_reset();
+
+ dev_data.attached = 1;
+ LOG_DBG("attached");
+
+ /* non-OTG device mode, enable DP Pullup */
+ USB0->CONTROL = USB_CONTROL_DPPULLUPNONOTG_MASK;
+
+ return 0;
+}
+
+int usb_dc_detach(void)
+{
+ LOG_DBG("");
+ /* disable USB and DP Pullup */
+ USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
+ USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
+
+ return 0;
+}
+
+int usb_dc_set_address(const uint8_t addr)
+{
+ LOG_DBG("");
+
+ if (!dev_data.attached) {
+ return -EINVAL;
+ }
+
+ /*
+ * The device stack tries to set the address before
+ * sending the ACK with ZLP, which is totally stupid,
+ * as workaround the addresse will be buffered and
+ * placed later inside isr handler (see KINETIS_IN_TOKEN).
+ */
+ dev_data.address = 0x80 | (addr & 0x7f);
+
+ return 0;
+}
+
+int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data * const cfg)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(cfg->ep_addr);
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("endpoint index/address out of range");
+ return -EINVAL;
+ }
+
+ switch (cfg->ep_type) {
+ case USB_DC_EP_CONTROL:
+ if (cfg->ep_mps > USB_MAX_CTRL_MPS) {
+ return -EINVAL;
+ }
+ return 0;
+ case USB_DC_EP_BULK:
+ if (cfg->ep_mps > USB_MAX_FS_BULK_MPS) {
+ return -EINVAL;
+ }
+ break;
+ case USB_DC_EP_INTERRUPT:
+ if (cfg->ep_mps > USB_MAX_FS_INT_MPS) {
+ return -EINVAL;
+ }
+ break;
+ case USB_DC_EP_ISOCHRONOUS:
+ if (cfg->ep_mps > USB_MAX_FS_ISO_MPS) {
+ return -EINVAL;
+ }
+ break;
+ default:
+ LOG_ERR("Unknown endpoint type!");
+ return -EINVAL;
+ }
+
+ if (ep_idx & BIT(0)) {
+ if (USB_EP_GET_DIR(cfg->ep_addr) != USB_EP_DIR_IN) {
+ LOG_INF("pre-selected as IN endpoint");
+ return -1;
+ }
+ } else {
+ if (USB_EP_GET_DIR(cfg->ep_addr) != USB_EP_DIR_OUT) {
+ LOG_INF("pre-selected as OUT endpoint");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data * const cfg)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(cfg->ep_addr);
+ struct usb_ep_ctrl_data *ep_ctrl;
+ struct ep_mem_block *block;
+ uint8_t idx_even;
+ uint8_t idx_odd;
+
+ if (usb_dc_ep_check_cap(cfg)) {
+ return -EINVAL;
+ }
+
+ idx_even = get_bdt_idx(cfg->ep_addr, 0);
+ idx_odd = get_bdt_idx(cfg->ep_addr, 1);
+ ep_ctrl = &dev_data.ep_ctrl[ep_idx];
+
+ if (ep_idx && (dev_data.ep_ctrl[ep_idx].status.in_enabled ||
+ dev_data.ep_ctrl[ep_idx].status.out_enabled)) {
+ LOG_WRN("endpoint already configured");
+ return -EALREADY;
+ }
+
+ LOG_DBG("ep %x, mps %d, type %d", cfg->ep_addr, cfg->ep_mps,
+ cfg->ep_type);
+
+ if (USB_EP_DIR_IS_OUT(cfg->ep_addr)) {
+ block = &(ep_ctrl->mblock_out);
+ } else {
+ block = &(ep_ctrl->mblock_in);
+ }
+
+ if (bdt[idx_even].buf_addr) {
+ k_heap_free(&ep_buf_pool, block->data);
+ }
+
+ USB0->ENDPOINT[ep_idx].ENDPT = 0;
+ (void)memset(&bdt[idx_even], 0, sizeof(struct buf_descriptor));
+ (void)memset(&bdt[idx_odd], 0, sizeof(struct buf_descriptor));
+
+ block->data = k_heap_alloc(&ep_buf_pool, cfg->ep_mps * 2U, K_MSEC(10));
+ if (block->data != NULL) {
+ (void)memset(block->data, 0, cfg->ep_mps * 2U);
+ } else {
+ LOG_ERR("Memory allocation time-out");
+ return -ENOMEM;
+ }
+
+ bdt[idx_even].buf_addr = (uint32_t)block->data;
+ LOG_INF("idx_even %x", (uint32_t)block->data);
+ bdt[idx_odd].buf_addr = (uint32_t)((uint8_t *)block->data + cfg->ep_mps);
+ LOG_INF("idx_odd %x", (uint32_t)((uint8_t *)block->data + cfg->ep_mps));
+
+ if (cfg->ep_addr & USB_EP_DIR_IN) {
+ dev_data.ep_ctrl[ep_idx].mps_in = cfg->ep_mps;
+ } else {
+ dev_data.ep_ctrl[ep_idx].mps_out = cfg->ep_mps;
+ }
+
+ bdt[idx_even].set.bc = cfg->ep_mps;
+ bdt[idx_odd].set.bc = cfg->ep_mps;
+
+ dev_data.ep_ctrl[ep_idx].status.out_data1 = false;
+ dev_data.ep_ctrl[ep_idx].status.in_data1 = false;
+
+ switch (cfg->ep_type) {
+ case USB_DC_EP_CONTROL:
+ LOG_DBG("configure control endpoint");
+ USB0->ENDPOINT[ep_idx].ENDPT |= (USB_ENDPT_EPHSHK_MASK |
+ USB_ENDPT_EPRXEN_MASK |
+ USB_ENDPT_EPTXEN_MASK);
+ break;
+ case USB_DC_EP_BULK:
+ case USB_DC_EP_INTERRUPT:
+ USB0->ENDPOINT[ep_idx].ENDPT |= USB_ENDPT_EPHSHK_MASK;
+ if (USB_EP_DIR_IS_OUT(cfg->ep_addr)) {
+ USB0->ENDPOINT[ep_idx].ENDPT |= USB_ENDPT_EPRXEN_MASK;
+ } else {
+ USB0->ENDPOINT[ep_idx].ENDPT |= USB_ENDPT_EPTXEN_MASK;
+ }
+ break;
+ case USB_DC_EP_ISOCHRONOUS:
+ if (USB_EP_DIR_IS_OUT(cfg->ep_addr)) {
+ USB0->ENDPOINT[ep_idx].ENDPT |= USB_ENDPT_EPRXEN_MASK;
+ } else {
+ USB0->ENDPOINT[ep_idx].ENDPT |= USB_ENDPT_EPTXEN_MASK;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_set_stall(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint8_t bd_idx;
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ LOG_DBG("ep %x, idx %d", ep, ep_idx);
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ dev_data.ep_ctrl[ep_idx].status.out_stalled = 1U;
+ bd_idx = get_bdt_idx(ep,
+ ~dev_data.ep_ctrl[ep_idx].status.out_odd);
+ } else {
+ dev_data.ep_ctrl[ep_idx].status.in_stalled = 1U;
+ bd_idx = get_bdt_idx(ep,
+ dev_data.ep_ctrl[ep_idx].status.in_odd);
+ }
+
+ bdt[bd_idx].set.bd_ctrl = BD_STALL_MASK | BD_DTS_MASK | BD_OWN_MASK;
+
+ return 0;
+}
+
+int usb_dc_ep_clear_stall(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint8_t bd_idx;
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ LOG_DBG("ep %x, idx %d", ep, ep_idx);
+ USB0->ENDPOINT[ep_idx].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ dev_data.ep_ctrl[ep_idx].status.out_stalled = 0U;
+ dev_data.ep_ctrl[ep_idx].status.out_data1 = false;
+ bd_idx = get_bdt_idx(ep,
+ ~dev_data.ep_ctrl[ep_idx].status.out_odd);
+ bdt[bd_idx].set.bd_ctrl = 0U;
+ bdt[bd_idx].set.bd_ctrl = BD_DTS_MASK | BD_OWN_MASK;
+ } else {
+ dev_data.ep_ctrl[ep_idx].status.in_stalled = 0U;
+ dev_data.ep_ctrl[ep_idx].status.in_data1 = false;
+ bd_idx = get_bdt_idx(ep,
+ dev_data.ep_ctrl[ep_idx].status.in_odd);
+ bdt[bd_idx].set.bd_ctrl = 0U;
+ }
+
+ /* Resume TX token processing, see USBx_CTL field descriptions */
+ if (ep == 0U) {
+ USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_is_stalled(const uint8_t ep, uint8_t *const stalled)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ LOG_DBG("ep %x, idx %d", ep_idx, ep);
+ if (!stalled) {
+ return -EINVAL;
+ }
+
+ *stalled = 0U;
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ *stalled = dev_data.ep_ctrl[ep_idx].status.out_stalled;
+ } else {
+ *stalled = dev_data.ep_ctrl[ep_idx].status.in_stalled;
+ }
+
+ uint8_t bd_idx = get_bdt_idx(ep,
+ dev_data.ep_ctrl[ep_idx].status.in_odd);
+ LOG_WRN("active bd ctrl: %x", bdt[bd_idx].set.bd_ctrl);
+ bd_idx = get_bdt_idx(ep,
+ ~dev_data.ep_ctrl[ep_idx].status.in_odd);
+ LOG_WRN("next bd ctrl: %x", bdt[bd_idx].set.bd_ctrl);
+
+ return 0;
+}
+
+int usb_dc_ep_halt(const uint8_t ep)
+{
+ return usb_dc_ep_set_stall(ep);
+}
+
+int usb_dc_ep_enable(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint8_t idx_even;
+ uint8_t idx_odd;
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ idx_even = get_bdt_idx(ep, 0);
+ idx_odd = get_bdt_idx(ep, 1);
+
+ if (ep_idx && (dev_data.ep_ctrl[ep_idx].status.in_enabled ||
+ dev_data.ep_ctrl[ep_idx].status.out_enabled)) {
+ LOG_WRN("endpoint 0x%x already enabled", ep);
+ return -EALREADY;
+ }
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ bdt[idx_even].set.bd_ctrl = BD_DTS_MASK | BD_OWN_MASK;
+ bdt[idx_odd].set.bd_ctrl = 0U;
+ dev_data.ep_ctrl[ep_idx].status.out_odd = 0U;
+ dev_data.ep_ctrl[ep_idx].status.out_stalled = 0U;
+ dev_data.ep_ctrl[ep_idx].status.out_data1 = false;
+ dev_data.ep_ctrl[ep_idx].status.out_enabled = true;
+ } else {
+ bdt[idx_even].bd_fields = 0U;
+ bdt[idx_odd].bd_fields = 0U;
+ dev_data.ep_ctrl[ep_idx].status.in_odd = 0U;
+ dev_data.ep_ctrl[ep_idx].status.in_stalled = 0U;
+ dev_data.ep_ctrl[ep_idx].status.in_data1 = false;
+ dev_data.ep_ctrl[ep_idx].status.in_enabled = true;
+ }
+
+ LOG_INF("ep 0x%x, ep_idx %d", ep, ep_idx);
+
+ return 0;
+}
+
+int usb_dc_ep_disable(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint8_t idx_even;
+ uint8_t idx_odd;
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ idx_even = get_bdt_idx(ep, 0);
+ idx_odd = get_bdt_idx(ep, 1);
+
+ LOG_INF("ep %x, idx %d", ep_idx, ep);
+
+ bdt[idx_even].bd_fields = 0U;
+ bdt[idx_odd].bd_fields = 0U;
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ dev_data.ep_ctrl[ep_idx].status.out_enabled = false;
+ } else {
+ dev_data.ep_ctrl[ep_idx].status.in_enabled = false;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_flush(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ LOG_DBG("ep %x, idx %d", ep_idx, ep);
+
+ return 0;
+}
+
+int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data,
+ const uint32_t data_len, uint32_t * const ret_bytes)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint32_t len_to_send = data_len;
+ uint8_t odd;
+ uint8_t bd_idx;
+ uint8_t *bufp;
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ odd = dev_data.ep_ctrl[ep_idx].status.in_odd;
+ bd_idx = get_bdt_idx(ep, odd);
+ bufp = (uint8_t *)bdt[bd_idx].buf_addr;
+
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_IN) {
+ LOG_ERR("Wrong endpoint direction");
+ return -EINVAL;
+ }
+
+ if (dev_data.ep_ctrl[ep_idx].status.in_stalled) {
+ LOG_WRN("endpoint is stalled");
+ return -EBUSY;
+ }
+
+ while (bdt[bd_idx].get.own) {
+ LOG_DBG("ep 0x%x is busy", ep);
+ k_yield();
+ }
+
+ LOG_DBG("bd idx %x bufp %p odd %d", bd_idx, bufp, odd);
+
+ if (data_len > dev_data.ep_ctrl[ep_idx].mps_in) {
+ len_to_send = dev_data.ep_ctrl[ep_idx].mps_in;
+ }
+
+ bdt[bd_idx].set.bc = len_to_send;
+
+ for (uint32_t n = 0; n < len_to_send; n++) {
+ bufp[n] = data[n];
+ }
+
+ dev_data.ep_ctrl[ep_idx].status.in_odd = ~odd;
+ if (dev_data.ep_ctrl[ep_idx].status.in_data1) {
+ bdt[bd_idx].set.bd_ctrl = BD_DTS_MASK |
+ BD_DATA01_MASK |
+ BD_OWN_MASK;
+ } else {
+ bdt[bd_idx].set.bd_ctrl = BD_DTS_MASK | BD_OWN_MASK;
+ }
+
+ /* Toggle next Data1 */
+ dev_data.ep_ctrl[ep_idx].status.in_data1 ^= 1;
+
+ LOG_DBG("ep 0x%x write %d bytes from %d", ep, len_to_send, data_len);
+
+ if (ret_bytes) {
+ *ret_bytes = len_to_send;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
+ uint32_t *read_bytes)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint32_t data_len;
+ uint8_t bd_idx;
+ uint8_t *bufp;
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ /* select the index of active endpoint buffer */
+ bd_idx = get_bdt_idx(ep, dev_data.ep_ctrl[ep_idx].status.out_odd);
+ bufp = (uint8_t *)bdt[bd_idx].buf_addr;
+
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_OUT) {
+ LOG_ERR("Wrong endpoint direction");
+ return -EINVAL;
+ }
+
+ if (dev_data.ep_ctrl[ep_idx].status.out_stalled) {
+ LOG_WRN("endpoint is stalled");
+ return -EBUSY;
+ }
+
+ /* Allow to read 0 bytes */
+ if (!data && max_data_len) {
+ LOG_ERR("Wrong arguments");
+ return -EINVAL;
+ }
+
+ while (bdt[bd_idx].get.own) {
+ LOG_ERR("Endpoint is occupied by the controller");
+ return -EBUSY;
+ }
+
+ data_len = bdt[bd_idx].get.bc;
+
+ if (!data && !max_data_len) {
+ /*
+ * When both buffer and max data to read are zero return
+ * the available data in buffer.
+ */
+ if (read_bytes) {
+ *read_bytes = data_len;
+ }
+ return 0;
+ }
+
+ if (data_len > max_data_len) {
+ LOG_WRN("Not enough room to copy all the data!");
+ data_len = max_data_len;
+ }
+
+ if (data != NULL) {
+ for (uint32_t i = 0; i < data_len; i++) {
+ data[i] = bufp[i];
+ }
+ }
+
+ LOG_DBG("Read idx %d, req %d, read %d bytes", bd_idx, max_data_len,
+ data_len);
+
+ if (read_bytes) {
+ *read_bytes = data_len;
+ }
+
+ return 0;
+}
+
+
+int usb_dc_ep_read_continue(uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+ uint8_t bd_idx;
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ bd_idx = get_bdt_idx(ep, dev_data.ep_ctrl[ep_idx].status.out_odd);
+
+ if (USB_EP_GET_DIR(ep) != USB_EP_DIR_OUT) {
+ LOG_ERR("Wrong endpoint direction");
+ return -EINVAL;
+ }
+
+ if (bdt[bd_idx].get.own) {
+ /* May occur when usb_transfer initializes the OUT transfer */
+ LOG_WRN("Current buffer is claimed by the controller");
+ return 0;
+ }
+
+ /* select the index of the next endpoint buffer */
+ bd_idx = get_bdt_idx(ep, ~dev_data.ep_ctrl[ep_idx].status.out_odd);
+ /* Update next toggle bit */
+ dev_data.ep_ctrl[ep_idx].status.out_data1 ^= 1;
+ bdt[bd_idx].set.bc = dev_data.ep_ctrl[ep_idx].mps_out;
+
+ /* Reset next buffer descriptor and set next toggle bit */
+ if (dev_data.ep_ctrl[ep_idx].status.out_data1) {
+ bdt[bd_idx].set.bd_ctrl = BD_DTS_MASK |
+ BD_DATA01_MASK |
+ BD_OWN_MASK;
+ } else {
+ bdt[bd_idx].set.bd_ctrl = BD_DTS_MASK | BD_OWN_MASK;
+ }
+
+ /* Resume TX token processing, see USBx_CTL field descriptions */
+ if (ep_idx == 0U) {
+ USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
+ }
+
+ LOG_DBG("idx next %x", bd_idx);
+
+ return 0;
+}
+
+int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
+ const uint32_t max_data_len, uint32_t *const read_bytes)
+{
+ int retval = usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes);
+
+ if (retval) {
+ return retval;
+ }
+
+ if (!data && !max_data_len) {
+ /* When both buffer and max data to read are zero the above
+ * call would fetch the data len and we simply return.
+ */
+ return 0;
+ }
+
+ if (usb_dc_ep_read_continue(ep) != 0) {
+ return -EINVAL;
+ }
+
+ LOG_DBG("");
+
+ return 0;
+}
+
+int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (!dev_data.attached) {
+ return -EINVAL;
+ }
+
+ if (ep & USB_EP_DIR_IN) {
+ dev_data.ep_ctrl[ep_idx].cb_in = cb;
+ } else {
+ dev_data.ep_ctrl[ep_idx].cb_out = cb;
+ }
+ LOG_DBG("ep_idx %x", ep_idx);
+
+ return 0;
+}
+
+void usb_dc_set_status_callback(const usb_dc_status_callback cb)
+{
+ LOG_DBG("");
+
+ dev_data.status_cb = cb;
+}
+
+int usb_dc_ep_mps(const uint8_t ep)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(ep);
+
+ if (ep_idx > (NUM_OF_EP_MAX - 1)) {
+ LOG_ERR("Wrong endpoint index/address");
+ return -EINVAL;
+ }
+
+ if (ep & USB_EP_DIR_IN) {
+ return dev_data.ep_ctrl[ep_idx].mps_in;
+ } else {
+ return dev_data.ep_ctrl[ep_idx].mps_out;
+ }
+}
+
+static inline void reenable_all_endpoints(void)
+{
+ for (uint8_t ep_idx = 0; ep_idx < NUM_OF_EP_MAX; ep_idx++) {
+ if (dev_data.ep_ctrl[ep_idx].status.out_enabled) {
+ usb_dc_ep_enable(ep_idx);
+ }
+ if (dev_data.ep_ctrl[ep_idx].status.in_enabled) {
+ usb_dc_ep_enable(ep_idx | USB_EP_DIR_IN);
+ }
+ }
+}
+
+static void usb_kinetis_isr_handler(void)
+{
+ uint8_t istatus = USB0->ISTAT;
+ uint8_t status = USB0->STAT;
+ struct cb_msg msg;
+
+
+ if (istatus & USB_ISTAT_USBRST_MASK) {
+ dev_data.address = 0U;
+ USB0->ADDR = (uint8_t)0;
+ /*
+ * Device reset is not possible because the stack does not
+ * configure the endpoints after the USB_DC_RESET event,
+ * therefore, reenable all endpoints and set they BDT into a
+ * defined state.
+ */
+ USB0->CTL |= USB_CTL_ODDRST_MASK;
+ USB0->CTL &= ~USB_CTL_ODDRST_MASK;
+ reenable_all_endpoints();
+ msg.ep = 0U;
+ msg.type = USB_DC_CB_TYPE_MGMT;
+ msg.cb = USB_DC_RESET;
+ k_msgq_put(&usb_dc_msgq, &msg, K_NO_WAIT);
+ }
+
+ if (istatus == USB_ISTAT_ERROR_MASK) {
+ USB0->ERRSTAT = 0xFF;
+ msg.ep = 0U;
+ msg.type = USB_DC_CB_TYPE_MGMT;
+ msg.cb = USB_DC_ERROR;
+ k_msgq_put(&usb_dc_msgq, &msg, K_NO_WAIT);
+ }
+
+ if (istatus & USB_ISTAT_STALL_MASK) {
+ if (dev_data.ep_ctrl[0].status.out_stalled) {
+ usb_dc_ep_clear_stall(0);
+ }
+ if (dev_data.ep_ctrl[0].status.in_stalled) {
+ usb_dc_ep_clear_stall(0x80);
+ }
+ }
+
+ if (istatus & USB_ISTAT_TOKDNE_MASK) {
+
+ uint8_t ep_idx = status >> USB_STAT_ENDP_SHIFT;
+ uint8_t ep = ((status << 4) & USB_EP_DIR_IN) | ep_idx;
+ uint8_t odd = (status & USB_STAT_ODD_MASK) >> USB_STAT_ODD_SHIFT;
+ uint8_t idx = get_bdt_idx(ep, odd);
+ uint8_t token_pid = bdt[idx].get.tok_pid;
+
+ msg.ep = ep;
+ msg.type = USB_DC_CB_TYPE_EP;
+
+ switch (token_pid) {
+ case KINETIS_SETUP_TOKEN:
+ dev_data.ep_ctrl[ep_idx].status.out_odd = odd;
+ /* clear tx entries */
+ bdt[BD_IDX_EP0TX_EVEN].bd_fields = 0U;
+ bdt[BD_IDX_EP0TX_ODD].bd_fields = 0U;
+ /*
+ * Set/Reset here the toggle bits for control endpoint
+ * because the device stack does not care about it.
+ */
+ dev_data.ep_ctrl[ep_idx].status.in_data1 = true;
+ dev_data.ep_ctrl[ep_idx].status.out_data1 = false;
+ dev_data.ep_ctrl[ep_idx].status.out_odd = odd;
+
+ msg.cb = USB_DC_EP_SETUP;
+ k_msgq_put(&usb_dc_msgq, &msg, K_NO_WAIT);
+ break;
+ case KINETIS_OUT_TOKEN:
+ dev_data.ep_ctrl[ep_idx].status.out_odd = odd;
+
+ msg.cb = USB_DC_EP_DATA_OUT;
+ k_msgq_put(&usb_dc_msgq, &msg, K_NO_WAIT);
+ break;
+ case KINETIS_IN_TOKEN:
+ /* SET ADDRESS workaround */
+ if (dev_data.address & 0x80) {
+ USB0->ADDR = dev_data.address & 0x7f;
+ dev_data.address = 0U;
+ }
+
+ msg.cb = USB_DC_EP_DATA_IN;
+ k_msgq_put(&usb_dc_msgq, &msg, K_NO_WAIT);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (istatus & USB_ISTAT_SLEEP_MASK) {
+ /* Enable resume interrupt */
+ USB0->INTEN |= USB_INTEN_RESUMEEN_MASK;
+ msg.ep = 0U;
+ msg.type = USB_DC_CB_TYPE_MGMT;
+ msg.cb = USB_DC_SUSPEND;
+ k_msgq_put(&usb_dc_msgq, &msg, K_NO_WAIT);
+ }
+
+ if (istatus & USB_ISTAT_RESUME_MASK) {
+ /* Disable resume interrupt */
+ USB0->INTEN &= ~USB_INTEN_RESUMEEN_MASK;
+ msg.ep = 0U;
+ msg.type = USB_DC_CB_TYPE_MGMT;
+ msg.cb = USB_DC_RESUME;
+ k_msgq_put(&usb_dc_msgq, &msg, K_NO_WAIT);
+ }
+
+ /* Clear interrupt status bits */
+ USB0->ISTAT = istatus;
+}
+
+/*
+ * This thread is only used to not run the USB device stack and endpoint
+ * callbacks in the ISR context, which happens when an callback function
+ * is called. TODO: something similar should be implemented in the USB
+ * device stack so that it can be used by all drivers.
+ */
+static void usb_kinetis_thread_main(void *arg1, void *unused1, void *unused2)
+{
+ ARG_UNUSED(arg1);
+ ARG_UNUSED(unused1);
+ ARG_UNUSED(unused2);
+ struct cb_msg msg;
+ uint8_t ep_idx;
+
+ while (true) {
+ k_msgq_get(&usb_dc_msgq, &msg, K_FOREVER);
+ ep_idx = USB_EP_GET_IDX(msg.ep);
+
+ if (msg.type == USB_DC_CB_TYPE_EP) {
+ switch (msg.cb) {
+ case USB_DC_EP_SETUP:
+ if (dev_data.ep_ctrl[ep_idx].cb_out) {
+ dev_data.ep_ctrl[ep_idx].cb_out(msg.ep,
+ USB_DC_EP_SETUP);
+ }
+ break;
+ case USB_DC_EP_DATA_OUT:
+ if (dev_data.ep_ctrl[ep_idx].cb_out) {
+ dev_data.ep_ctrl[ep_idx].cb_out(msg.ep,
+ USB_DC_EP_DATA_OUT);
+ }
+ break;
+ case USB_DC_EP_DATA_IN:
+ if (dev_data.ep_ctrl[ep_idx].cb_in) {
+ dev_data.ep_ctrl[ep_idx].cb_in(msg.ep,
+ USB_DC_EP_DATA_IN);
+ }
+ break;
+ default:
+ LOG_ERR("unknown msg");
+ break;
+ }
+ } else if (dev_data.status_cb) {
+ switch (msg.cb) {
+ case USB_DC_RESET:
+ dev_data.status_cb(USB_DC_RESET, NULL);
+ break;
+ case USB_DC_ERROR:
+ dev_data.status_cb(USB_DC_ERROR, NULL);
+ break;
+ case USB_DC_SUSPEND:
+ dev_data.status_cb(USB_DC_SUSPEND, NULL);
+ break;
+ case USB_DC_RESUME:
+ dev_data.status_cb(USB_DC_RESUME, NULL);
+ break;
+ default:
+ LOG_ERR("unknown msg");
+ break;
+ }
+ }
+ }
+}
diff --git a/port/freescale/usbd_kinetis.c b/port/freescale/usbd_kinetis.c
new file mode 100644
index 00000000..1bfb9347
--- /dev/null
+++ b/port/freescale/usbd_kinetis.c
@@ -0,0 +1,819 @@
+/**
+ * @file usbd_kinetis.c
+ * @brief
+ *
+ * DAPLink Interface Firmware
+ * Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 "rl_usb.h"
+#include "fsl_device_registers.h"
+#include "cortex_m.h"
+#include "util.h"
+#include "string.h"
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+typedef struct __BUF_DESC {
+ uint8_t stat;
+ uint8_t reserved;
+ uint16_t bc;
+ uint32_t buf_addr;
+} BUF_DESC;
+
+BUF_DESC __align(512) BD[(USBD_EP_NUM + 1) * 2 * 2];
+uint8_t EPBuf[(USBD_EP_NUM + 1) * 2 * 2][64];
+uint8_t OutEpSize[USBD_EP_NUM + 1];
+uint8_t StatQueue[(USBD_EP_NUM + 1) * 2 * 2 + 1];
+uint32_t StatQueueHead = 0;
+uint32_t StatQueueTail = 0;
+uint32_t LastIstat = 0;
+uint8_t UsbSuspended = 0;
+uint8_t Ep0ZlpOut = 0;
+
+uint32_t Data1 = 0x55555555;
+
+#define BD_OWN_MASK 0x80
+#define BD_DATA01_MASK 0x40
+#define BD_KEEP_MASK 0x20
+#define BD_NINC_MASK 0x10
+#define BD_DTS_MASK 0x08
+#define BD_STALL_MASK 0x04
+
+#define TX 1
+#define RX 0
+#define ODD 0
+#define EVEN 1
+#define IDX(Ep, dir, Ev_Odd) ((((Ep & 0x0F) * 4) + (2 * dir) + (1 * Ev_Odd)))
+
+#define SETUP_TOKEN 0x0D
+#define IN_TOKEN 0x09
+#define OUT_TOKEN 0x01
+#define TOK_PID(idx) ((BD[idx].stat >> 2) & 0x0F)
+
+inline static void protected_and(uint32_t *addr, uint32_t val)
+{
+ cortex_int_state_t state;
+ state = cortex_int_get_and_disable();
+ *addr = *addr & val;
+ cortex_int_restore(state);
+}
+inline static void protected_or(uint32_t *addr, uint32_t val)
+{
+ cortex_int_state_t state;
+ state = cortex_int_get_and_disable();
+ *addr = *addr | val;
+ cortex_int_restore(state);
+}
+inline static void protected_xor(uint32_t *addr, uint32_t val)
+{
+ cortex_int_state_t state;
+ state = cortex_int_get_and_disable();
+ *addr = *addr ^ val;
+ cortex_int_restore(state);
+}
+
+inline static void stat_enque(uint32_t stat)
+{
+ cortex_int_state_t state;
+ state = cortex_int_get_and_disable();
+ StatQueue[StatQueueTail] = stat;
+ StatQueueTail = (StatQueueTail + 1) % sizeof(StatQueue);
+ cortex_int_restore(state);
+}
+
+inline static uint32_t stat_deque()
+{
+ cortex_int_state_t state;
+ uint32_t stat;
+ state = cortex_int_get_and_disable();
+ stat = StatQueue[StatQueueHead];
+ StatQueueHead = (StatQueueHead + 1) % sizeof(StatQueue);
+ cortex_int_restore(state);
+
+ return stat;
+}
+
+inline static uint32_t stat_is_empty()
+{
+ cortex_int_state_t state;
+ uint32_t empty;
+ state = cortex_int_get_and_disable();
+ empty = StatQueueHead == StatQueueTail;
+ cortex_int_restore(state);
+ return empty;
+}
+
+/*
+ * USB Device Interrupt enable
+ * Called by USBD_Init to enable the USB Interrupt
+ * Return Value: None
+ */
+
+void USBD_IntrEna(void)
+{
+ NVIC_EnableIRQ(USB0_IRQn); /* Enable OTG interrupt */
+}
+
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB
+ * Return Value: None
+ */
+
+void USBD_Init(void)
+{
+ OutEpSize[0] = USBD_MAX_PACKET0;
+ /* Enable all clocks needed for USB to function */
+ /* Set USB clock to 48 MHz */
+ SIM->SOPT2 |= SIM_SOPT2_USBSRC_MASK | /* MCGPLLCLK used as src */
+ SIM_SOPT2_PLLFLLSEL_MASK ; /* Select MCGPLLCLK as clock */
+#if defined(TARGET_MK20D5)
+ SIM->CLKDIV2 &= ~(SIM_CLKDIV2_USBFRAC_MASK | /* Clear CLKDIV2 FS values */
+ SIM_CLKDIV2_USBDIV_MASK);
+ SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0) ; /* USB clk = (PLL*1/2) */
+ /* = ( 48*1/1)=48 */
+#endif // TARGET_MK20D5
+ SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK; /* Enable USBOTG clock */
+ USBD_IntrEna();
+ USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
+
+ while (USB0->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
+
+ USB0->BDTPAGE1 = (uint8_t)((uint32_t) BD >> 8);
+ USB0->BDTPAGE2 = (uint8_t)((uint32_t) BD >> 16);
+ USB0->BDTPAGE3 = (uint8_t)((uint32_t) BD >> 24);
+ USB0->ISTAT = 0xFF; /* clear interrupt flags */
+ /* enable interrupts */
+ USB0->INTEN = USB_INTEN_USBRSTEN_MASK |
+ USB_INTEN_TOKDNEEN_MASK |
+ USB_INTEN_SLEEPEN_MASK |
+#ifdef __RTX
+ ((USBD_RTX_DevTask != 0) ? USB_INTEN_SOFTOKEN_MASK : 0) |
+ ((USBD_RTX_DevTask != 0) ? USB_INTEN_ERROREN_MASK : 0) ;
+#else
+ ((USBD_P_SOF_Event != 0) ? USB_INTEN_SOFTOKEN_MASK : 0) |
+ ((USBD_P_Error_Event != 0) ? USB_INTEN_ERROREN_MASK : 0) ;
+#endif
+ USB0->USBCTRL = USB_USBCTRL_PDE_MASK;/* pull dawn on D+ and D- */
+ USB0->USBTRC0 |= (1 << 6); /* bit 6 must be set to 1 */
+}
+
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+
+void USBD_Connect(uint32_t con)
+{
+ if (con) {
+ USB0->CTL |= USB_CTL_USBENSOFEN_MASK; /* enable USB */
+ USB0->CONTROL = USB_CONTROL_DPPULLUPNONOTG_MASK; /* pull up on D+ */
+ } else {
+ USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK; /* disable USB */
+ USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;/* pull down on D+ */
+ }
+}
+
+
+/*
+ * USB Device Reset Function
+ * Called automatically on USB Device Reset
+ * Return Value: None
+ */
+
+void USBD_Reset(void)
+{
+ uint32_t i;
+
+ NVIC_DisableIRQ(USB0_IRQn);
+
+ for (i = 1; i < 16; i++) {
+ USB0->ENDPOINT[i].ENDPT = 0x00;
+ }
+
+ memset(StatQueue, 0, sizeof(StatQueue));
+ StatQueueHead = 0;
+ StatQueueTail = 0;
+ LastIstat = 0;
+ UsbSuspended = 0;
+ Ep0ZlpOut = 0;
+
+ /* EP0 control endpoint */
+ BD[IDX(0, RX, ODD)].bc = USBD_MAX_PACKET0;
+ BD[IDX(0, RX, ODD)].buf_addr = (uint32_t) & (EPBuf[IDX(0, RX, ODD)][0]);
+ BD[IDX(0, RX, ODD)].stat = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
+ BD[IDX(0, RX, EVEN)].stat = 0;
+ BD[IDX(0, TX, ODD)].buf_addr = (uint32_t) & (EPBuf[IDX(0, TX, ODD)][0]);
+ BD[IDX(0, TX, EVEN)].buf_addr = 0;
+ USB0->ENDPOINT[0].ENDPT = USB_ENDPT_EPHSHK_MASK | /* enable ep handshaking */
+ USB_ENDPT_EPTXEN_MASK | /* enable TX (IN) tran. */
+ USB_ENDPT_EPRXEN_MASK; /* enable RX (OUT) tran. */
+ Data1 = 0x55555555;
+ USB0->CTL |= USB_CTL_ODDRST_MASK;
+ USB0->ISTAT = 0xFF; /* clear all interrupt status flags */
+ USB0->ERRSTAT = 0xFF; /* clear all error flags */
+ USB0->ERREN = 0xFF; /* enable error interrupt sources */
+ USB0->ADDR = 0x00; /* set default address */
+
+ NVIC_EnableIRQ(USB0_IRQn);
+}
+
+
+/*
+ * USB Device Suspend Function
+ * Called automatically on USB Device Suspend
+ * Return Value: None
+ */
+
+void USBD_Suspend(void)
+{
+ USB0->INTEN |= USB_INTEN_RESUMEEN_MASK;
+}
+
+
+/*
+ * USB Device Resume Function
+ * Called automatically on USB Device Resume
+ * Return Value: None
+ */
+
+void USBD_Resume(void)
+{
+ USB0->INTEN &= ~USB_INTEN_RESUMEEN_MASK;
+}
+
+
+/*
+ * USB Device Remote Wakeup Function
+ * Called automatically on USB Device Remote Wakeup
+ * Return Value: None
+ */
+
+void USBD_WakeUp(void)
+{
+ uint32_t i = 50000;
+
+ if (USBD_DeviceStatus & USB_GETSTATUS_REMOTE_WAKEUP) {
+ USB0->CTL |= USB_CTL_RESUME_MASK;
+
+ while (i--) {
+ __nop();
+ }
+
+ USB0->CTL &= ~USB_CTL_RESUME_MASK;
+ }
+}
+
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+
+void USBD_WakeUpCfg(uint32_t cfg)
+{
+ /* Not needed */
+}
+
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * Return Value: None
+ */
+
+void USBD_SetAddress(uint32_t adr, uint32_t setup)
+{
+ if (!setup) {
+ USB0->ADDR = adr & 0x7F;
+ }
+}
+
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+
+void USBD_Configure(uint32_t cfg)
+{
+}
+
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+
+void USBD_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ uint32_t num, val;
+ num = pEPD->bEndpointAddress;
+ val = pEPD->wMaxPacketSize;
+
+ if (!(pEPD->bEndpointAddress & 0x80)) {
+ OutEpSize[num] = val;
+ }
+
+ USBD_ResetEP(num);
+}
+
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+
+void USBD_DirCtrlEP(uint32_t dir)
+{
+ /* Not needed */
+}
+
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_EnableEP(uint32_t EPNum)
+{
+ if (EPNum & 0x80) {
+ EPNum &= 0x0F;
+ USB0->ENDPOINT[EPNum].ENDPT |= USB_ENDPT_EPHSHK_MASK | /*en ep handshaking*/
+ USB_ENDPT_EPTXEN_MASK; /*en TX (IN) tran */
+ } else {
+ USB0->ENDPOINT[EPNum].ENDPT |= USB_ENDPT_EPHSHK_MASK | /*en ep handshaking*/
+ USB_ENDPT_EPRXEN_MASK; /*en RX (OUT) tran.*/
+ }
+}
+
+
+/*
+ * Disable USB Endpoint
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_DisableEP(uint32_t EPNum)
+{
+ if (EPNum & 0x80) {
+ EPNum &= 0x0F;
+ USB0->ENDPOINT[EPNum].ENDPT &= ~(USB_ENDPT_EPHSHK_MASK |/*dis handshaking */
+ USB_ENDPT_EPTXEN_MASK);/*dis TX(IN) tran */
+ } else {
+ USB0->ENDPOINT[EPNum].ENDPT &= ~(USB_ENDPT_EPHSHK_MASK |/*dis handshaking */
+ USB_ENDPT_EPRXEN_MASK);/*dis RX(OUT) tran*/
+ }
+}
+
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ResetEP(uint32_t EPNum)
+{
+ if (EPNum & 0x80) {
+ EPNum &= 0x0F;
+ protected_or(&Data1, (1 << ((EPNum * 2) + 1)));
+ BD[IDX(EPNum, TX, ODD)].buf_addr = (uint32_t) & (EPBuf[IDX(EPNum, TX, ODD)][0]);
+ BD[IDX(EPNum, TX, EVEN)].buf_addr = 0;
+ } else {
+ protected_and(&Data1, ~(1 << (EPNum * 2)));
+ BD[IDX(EPNum, RX, ODD)].bc = OutEpSize[EPNum];
+ BD[IDX(EPNum, RX, ODD)].buf_addr = (uint32_t) & (EPBuf[IDX(EPNum, RX, ODD)][0]);
+ BD[IDX(EPNum, RX, ODD)].stat = BD_OWN_MASK | BD_DTS_MASK;
+ BD[IDX(EPNum, RX, EVEN)].stat = 0;
+ }
+}
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_SetStallEP(uint32_t EPNum)
+{
+ EPNum &= 0x0F;
+ USB0->ENDPOINT[EPNum].ENDPT |= USB_ENDPT_EPSTALL_MASK;
+}
+
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClrStallEP(uint32_t EPNum)
+{
+ USB0->ENDPOINT[EPNum & 0x0F].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
+ USBD_ResetEP(EPNum);
+}
+
+
+/*
+ * Clear USB Device Endpoint Buffer
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClearEPBuf(uint32_t EPNum)
+{
+}
+
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+
+uint32_t USBD_ReadEP(uint32_t EPNum, uint8_t *pData, uint32_t size)
+{
+ uint32_t n, sz, idx, setup = 0;
+ idx = IDX(EPNum, RX, 0);
+ sz = BD[idx].bc;
+
+ if ((EPNum == 0) && Ep0ZlpOut) {
+ // This packet was a zero length data out packet. It has already
+ // been processed by USB0_IRQHandler. Only toggle the DATAx bit
+ // and return a size of 0.
+ protected_xor(&Data1, (1 << (idx / 2)));
+ return 0;
+ }
+
+ if ((EPNum == 0) && (TOK_PID(idx) == SETUP_TOKEN)) {
+ setup = 1;
+ }
+
+ if (size < sz) {
+ util_assert(0);
+ sz = size;
+ }
+
+ for (n = 0; n < sz; n++) {
+ pData[n] = EPBuf[idx][n];
+ }
+
+ BD[idx].bc = OutEpSize[EPNum];
+
+ if ((Data1 >> (idx / 2) & 1) == ((BD[idx].stat >> 6) & 1)) {
+ uint32_t xfer_size = (pData[7] << 8) | (pData[6] << 0);
+ if (setup && (0 == xfer_size)) { /* if no setup data stage, */
+ protected_and(&Data1, ~1); /* set DATA0 */
+ } else {
+ protected_xor(&Data1, (1 << (idx / 2)));
+ }
+ }
+
+ if ((Data1 >> (idx / 2)) & 1) {
+ BD[idx].stat = BD_DTS_MASK | BD_DATA01_MASK;
+ BD[idx].stat |= BD_OWN_MASK;
+ } else {
+ BD[idx].stat = BD_DTS_MASK;
+ BD[idx].stat |= BD_OWN_MASK;
+ }
+
+ return (sz);
+}
+
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+
+uint32_t USBD_WriteEP(uint32_t EPNum, uint8_t *pData, uint32_t cnt)
+{
+ uint32_t idx, n;
+ EPNum &= 0x0F;
+ idx = IDX(EPNum, TX, 0);
+ BD[idx].bc = cnt;
+
+ for (n = 0; n < cnt; n++) {
+ EPBuf[idx][n] = pData[n];
+ }
+
+ if ((Data1 >> (idx / 2)) & 1) {
+ BD[idx].stat = BD_OWN_MASK | BD_DTS_MASK;
+ } else {
+ BD[idx].stat = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
+ }
+
+ protected_xor(&Data1, (1 << (idx / 2)));
+ return (cnt);
+}
+
+/*
+ * Get USB Device Last Frame Number
+ * Parameters: None
+ * Return Value: Frame Number
+ */
+
+uint32_t USBD_GetFrame(void)
+{
+ return ((USB0->FRMNUML | (USB0->FRMNUMH << 8) & 0x07FF));
+}
+
+#ifdef __RTX
+U32 LastError; /* Last Error */
+
+/*
+ * Get USB Device Last Error Code
+ * Parameters: None
+ * Return Value: Error Code
+ */
+
+U32 USBD_GetError(void)
+{
+ return (LastError);
+}
+#endif
+
+
+/*
+ * USB Device Interrupt Service Routine
+ */
+void USB0_IRQHandler(void)
+{
+ uint32_t istat, num, dir, ev_odd;
+ uint32_t new_istat;
+ uint8_t suspended = 0;
+
+ new_istat = istat = USB0->ISTAT;
+
+ // Read all tokens
+ if (istat & USB_ISTAT_TOKDNE_MASK) {
+ while (istat & USB_ISTAT_TOKDNE_MASK) {
+ uint8_t stat = USB0->STAT;
+ num = (stat >> 4) & 0x0F;
+ dir = (stat >> 3) & 0x01;
+ ev_odd = (stat >> 2) & 0x01;
+
+ // Consume all zero length OUT packets on endpoint 0 to prevent
+ // a subsequent SETUP packet from being dropped
+ if ((0 == num) && (RX == dir)) {
+ uint32_t idx;
+ idx = IDX(num, dir, ev_odd);
+ if ((TOK_PID(idx) == OUT_TOKEN) && (BD[idx].bc == 0)) {
+ BD[idx].bc = OutEpSize[num];
+ if (BD[idx].stat & BD_DATA01_MASK) {
+ BD[idx].stat = BD_OWN_MASK | BD_DTS_MASK;
+ } else {
+ BD[idx].stat = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
+ }
+ stat |= 1 << 0;
+ }
+ }
+
+ stat_enque(stat);
+ USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
+
+ // Check if USB is suspending before checking istat
+ suspended = suspended || USB0->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK;
+ istat = USB0->ISTAT;
+ }
+ }
+
+ // Set global istat and suspended flags
+ new_istat |= istat;
+ protected_or(&LastIstat, new_istat);
+ UsbSuspended |= suspended ? 1 : 0;
+ USB0->ISTAT = istat;
+
+ USBD_SignalHandler();
+}
+
+/*
+ * USB Device Service Routine
+ */
+
+void USBD_Handler(void)
+{
+ uint32_t istr, num, dir, ev_odd;
+ cortex_int_state_t state;
+ uint8_t suspended = 0;
+
+ // Get ISTAT
+ state = cortex_int_get_and_disable();
+ istr = LastIstat;
+ LastIstat = 0;
+ suspended = UsbSuspended;
+ UsbSuspended = 0;
+ cortex_int_restore(state);
+
+
+ /* reset interrupt */
+ if (istr & USB_ISTAT_USBRST_MASK) {
+ USBD_Reset();
+ usbd_reset_core();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Reset_Event) {
+ USBD_P_Reset_Event();
+ }
+
+#endif
+ }
+
+ /* suspend interrupt */
+ if (istr & USB_ISTAT_SLEEP_MASK) {
+ USBD_Suspend();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Suspend_Event) {
+ USBD_P_Suspend_Event();
+ }
+
+#endif
+ }
+
+ /* resume interrupt */
+ if (istr & USB_ISTAT_RESUME_MASK) {
+ USBD_Resume();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Resume_Event) {
+ USBD_P_Resume_Event();
+ }
+
+#endif
+ }
+
+ /* Start Of Frame */
+ if (istr & USB_ISTAT_SOFTOK_MASK) {
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SOF, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+
+#endif
+ }
+
+ /* Error interrupt */
+ if (istr == USB_ISTAT_ERROR_MASK) {
+#ifdef __RTX
+ LastError = USB0->ERRSTAT;
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_ERROR, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Error_Event) {
+ USBD_P_Error_Event(USB0->ERRSTAT);
+ }
+
+#endif
+ USB0->ERRSTAT = 0xFF;
+ }
+
+ /* token interrupt */
+ if (istr & USB_ISTAT_TOKDNE_MASK) {
+ while (!stat_is_empty()) {
+ uint32_t stat;
+
+ stat = stat_deque();
+ num = (stat >> 4) & 0x0F;
+ dir = (stat >> 3) & 0x01;
+ ev_odd = (stat >> 2) & 0x01;
+
+ /* setup packet */
+ if ((num == 0) && (TOK_PID((IDX(num, dir, ev_odd))) == SETUP_TOKEN)) {
+ Data1 &= ~0x02;
+ BD[IDX(0, TX, EVEN)].stat &= ~BD_OWN_MASK;
+ BD[IDX(0, TX, ODD)].stat &= ~BD_OWN_MASK;
+ Ep0ZlpOut = 0;
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num]) {
+ isr_evt_set(USBD_EVT_SETUP, USBD_RTX_EPTask[num]);
+ }
+
+#else
+
+ if (USBD_P_EP[num]) {
+ USBD_P_EP[num](USBD_EVT_SETUP);
+ }
+
+#endif
+
+ } else {
+ /* OUT packet */
+ if (TOK_PID((IDX(num, dir, ev_odd))) == OUT_TOKEN) {
+ if (0 == num) {
+ Ep0ZlpOut = stat & (1 << 0);
+ }
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num]) {
+ isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[num]);
+ }
+
+#else
+
+ if (USBD_P_EP[num]) {
+ USBD_P_EP[num](USBD_EVT_OUT);
+ }
+
+#endif
+ }
+
+ /* IN packet */
+ if (TOK_PID((IDX(num, dir, ev_odd))) == IN_TOKEN) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num]) {
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[num]);
+ }
+
+#else
+
+ if (USBD_P_EP[num]) {
+ USBD_P_EP[num](USBD_EVT_IN);
+ }
+
+#endif
+ }
+ }
+ }
+ }
+
+ if (suspended) {
+ USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
+ }
+}
diff --git a/port/maxim/max32620/usbd_max32620.c b/port/maxim/max32620/usbd_max32620.c
new file mode 100644
index 00000000..f2e627b0
--- /dev/null
+++ b/port/maxim/max32620/usbd_max32620.c
@@ -0,0 +1,622 @@
+/* CMSIS-DAP Interface Firmware
+ * Copyright (c) 2009-2013 ARM Limited
+ *
+ * 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 <string.h>
+#include "rl_usb.h"
+#include "util.h"
+
+#include "max32620.h"
+#include "usb_regs.h"
+#include "clkman_regs.h"
+#include "pwrman_regs.h"
+#include "tmr_regs.h"
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+#define EPNUM_MASK (~USB_ENDPOINT_DIRECTION_MASK)
+
+#define INIT_INTS (MXC_F_USB_DEV_INTEN_BRST | MXC_F_USB_DEV_INTFL_BRST_DN | MXC_F_USB_DEV_INTEN_VBUS | MXC_F_USB_DEV_INTFL_NO_VBUS)
+#define CONNECT_INTS (MXC_F_USB_DEV_INTEN_SETUP | MXC_F_USB_DEV_INTEN_EP_IN | MXC_F_USB_DEV_INTEN_EP_OUT | MXC_F_USB_DEV_INTEN_DMA_ERR)
+
+typedef struct {
+ volatile uint32_t buf0_desc;
+ volatile uint32_t buf0_address;
+ volatile uint32_t buf1_desc;
+ volatile uint32_t buf1_address;
+} ep_buffer_t;
+
+typedef struct {
+ ep_buffer_t out_buffer;
+ ep_buffer_t in_buffer;
+} ep0_buffer_t;
+
+typedef struct {
+ ep0_buffer_t ep0;
+ ep_buffer_t ep[MXC_USB_NUM_EP - 1];
+} ep_buffer_descriptor_t;
+
+typedef struct {
+ U8 type;
+ U16 len;
+} ep_info_t;
+
+/* static storage for endpoint buffer descriptor table, must be 512 byte aligned for DMA */
+__attribute__ ((aligned (512)))
+ep_buffer_descriptor_t ep_buffer_descriptor;
+
+static uint32_t ep_buffer[MXC_USB_NUM_EP][MXC_USB_MAX_PACKET / sizeof(uint32_t)];
+static ep_info_t ep_info[MXC_USB_NUM_EP];
+static volatile int suspended;
+static volatile int setup_waiting;
+static volatile int ep0_expect_zlp;
+
+#if CDC_ENDPOINT
+/* CDC-ACM class processes FIFOs in the SOF interrupt. The USB Device interface
+ * of Maxim's microcontrollers does not provide and SOF interrupt. A periodic
+ * timer interrupt is used instead.
+ */
+/******************************************************************************/
+void TMR0_IRQHandler(void)
+{
+ MXC_TMR0->intfl = MXC_TMR0->intfl;
+
+ if (usbd_configured()) {
+ USBD_CDC_ACM_SOF_Event();
+ }
+}
+#endif
+
+/******************************************************************************/
+static ep_buffer_t *get_desc(U32 EPNum)
+{
+ ep_buffer_t *desc;
+
+ if (EPNum == 0x80) {
+ desc = &ep_buffer_descriptor.ep0.in_buffer;
+ } else if (EPNum == 0x00) {
+ desc = &ep_buffer_descriptor.ep0.out_buffer;
+ } else {
+ desc = &ep_buffer_descriptor.ep[(EPNum & EPNUM_MASK) - 1];
+ }
+
+ return desc;
+}
+
+/*
+ * USB Device Interrupt enable
+ * Called by USBD_Init to enable the USB Interrupt
+ * Return Value: None
+ */
+
+void USBD_IntrEna(void)
+{
+ NVIC_EnableIRQ(USB_IRQn); /* Enable OTG interrupt */
+}
+
+/******************************************************************************/
+/*
+ * Usb interrupt enable/disable
+ * Parameters: ena: enable/disable
+ * 0: disable interrupt
+ * 1: enable interrupt
+ */
+#ifdef __RTX
+void __svc(1) USBD_Intr (int ena);
+void __SVC_1 (int ena)
+{
+ if (ena) {
+ NVIC_EnableIRQ(USB_IRQn); /* Enable USB interrupt */
+ } else {
+ NVIC_DisableIRQ(USB_IRQn); /* Disable USB interrupt */
+ }
+}
+#endif
+
+/******************************************************************************/
+static void reset_state(void)
+{
+ unsigned int ep;
+
+ suspended = 0;
+ setup_waiting = 0;
+ ep0_expect_zlp = 0;
+ memset(ep_info, 0, sizeof(ep_info));
+
+ MXC_USB->ep[0] |= (MXC_S_USB_EP_DIR_CONTROL | MXC_F_USB_EP_INT_EN | MXC_F_USB_EP_DT);
+ for (ep = 1; ep < MXC_USB_NUM_EP; ep++) {
+ MXC_USB->ep[ep] = MXC_F_USB_EP_DT;
+ }
+}
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB Device
+ * Return Value: None
+ */
+void USBD_Init (void)
+{
+ uint32_t reg;
+
+ /* Enable USB power domain */
+ MXC_PWRMAN->pwr_rst_ctrl |= MXC_F_PWRMAN_PWR_RST_CTRL_USB_POWERED;
+ /* Setup the USB clocking, select */
+ MXC_CLKMAN->clk_ctrl |= MXC_F_CLKMAN_CLK_CTRL_USB_CLOCK_ENABLE;
+ /* Force USB clock gater */
+ reg = MXC_CLKMAN->clk_gate_ctrl0;
+ reg &= ~MXC_F_CLKMAN_CLK_GATE_CTRL0_USB_CLK_GATER;
+ reg |= (0x2 << MXC_F_CLKMAN_CLK_GATE_CTRL0_USB_CLK_GATER_POS);
+ MXC_CLKMAN->clk_gate_ctrl0 = reg;
+
+ MXC_USB->cn = 0;
+ MXC_USB->cn = MXC_F_USB_CN_USB_EN;
+ MXC_USB->dev_inten = 0;
+ MXC_USB->dev_intfl = 0xFFFF; // clear interrupts
+ MXC_USB->dev_cn = 0;
+ MXC_USB->dev_cn |= MXC_F_USB_DEV_CN_URST;
+ MXC_USB->dev_cn = 0;
+
+ reset_state();
+
+ /* set the descriptor location */
+ MXC_USB->ep_base = (uint32_t)&ep_buffer_descriptor;
+
+ /* enable some interrupts */
+ MXC_USB->dev_inten = INIT_INTS;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+void USBD_Connect (BOOL con)
+{
+ if (con) {
+ MXC_USB->dev_intfl = 0xFFFF; // clear interrupts
+ MXC_USB->dev_inten |= CONNECT_INTS;
+ MXC_USB->ep[0] |= MXC_F_USB_EP_INT_EN;
+ MXC_USB->dev_cn |= (MXC_F_USB_DEV_CN_CONNECT | MXC_F_USB_DEV_CN_FIFO_MODE);
+ } else {
+ MXC_USB->dev_inten &= ~CONNECT_INTS;
+ MXC_USB->ep[0] &= ~MXC_F_USB_EP_INT_EN;
+ MXC_USB->dev_cn &= ~MXC_F_USB_DEV_CN_CONNECT;
+ }
+}
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+void USBD_WakeUpCfg (BOOL cfg)
+{
+}
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * Return Value: None
+ */
+void USBD_SetAddress (U32 adr, U32 setup)
+{
+ /* Performed by Hardware */
+}
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+void USBD_Configure (BOOL cfg)
+{
+#if CDC_ENDPOINT
+ /* CDC-ACM class processes FIFOs in the SOF interrupt. The USB Device interface
+ * of Maxim's microcontrollers does not provide and SOF interrupt. A periodic
+ * timer interrupt is used instead.
+ */
+
+ #define SOF_INT_US 1000
+
+ if (cfg) {
+ // Setup timer interrupt for SOF
+ MXC_TMR0->ctrl = MXC_S_TMR_CTRL_MODE_CONTINUOUS;
+ MXC_TMR0->count32 = 0;
+ MXC_TMR0->term_cnt32 = (SystemCoreClock / 1000000) * SOF_INT_US;
+
+ // Enable the interrupt
+ MXC_TMR0->intfl = MXC_TMR0->intfl;
+ NVIC_EnableIRQ(TMR0_0_IRQn);
+ MXC_TMR0->inten = MXC_F_TMR_INTEN_TIMER0;
+
+ // Start the timer
+ MXC_TMR0->ctrl |= MXC_F_TMR_CTRL_ENABLE0;
+
+ } else {
+ // Disable tmr
+ MXC_TMR0->ctrl &= ~(MXC_F_TMR_CTRL_ENABLE0);
+ }
+#endif
+}
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+void USBD_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ U32 EPNum;
+
+ EPNum = pEPD->bEndpointAddress & EPNUM_MASK;
+
+ if (EPNum < MXC_USB_NUM_EP) {
+
+ // Clear existing configurations
+ MXC_USB->ep[EPNum] = MXC_F_USB_EP_DT;
+
+ if (pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) {
+ ep_info[EPNum].type = MXC_S_USB_EP_DIR_IN;
+ } else {
+ ep_info[EPNum].type = MXC_S_USB_EP_DIR_OUT;
+ }
+
+ ep_info[EPNum].len = pEPD->wMaxPacketSize;
+ }
+}
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+void USBD_DirCtrlEP (U32 dir)
+{
+ /* Not needed */
+}
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_EnableEP (U32 EPNum)
+{
+ ep_buffer_t *desc = get_desc(EPNum);
+
+ EPNum &= EPNUM_MASK;
+ MXC_USB->ep[EPNum] |= (MXC_F_USB_EP_INT_EN | ep_info[EPNum].type | MXC_F_USB_EP_DT);
+
+ if (ep_info[EPNum].type == MXC_S_USB_EP_DIR_OUT) {
+ // This is an OUT endpoint. Go ahead and register a request.
+ desc = get_desc(EPNum);
+ desc->buf0_address = (uint32_t)ep_buffer[EPNum];
+ desc->buf0_desc = sizeof(ep_buffer[EPNum]);
+ MXC_USB->out_owner = (1 << EPNum);
+ }
+}
+
+/*
+ * Disable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_DisableEP (U32 EPNum)
+{
+ EPNum &= EPNUM_MASK;
+ MXC_USB->ep[EPNum] = 0;
+}
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_ResetEP (U32 EPNum)
+{
+ ep_buffer_t *desc = get_desc(EPNum);
+
+ EPNum &= EPNUM_MASK;
+ MXC_USB->ep[EPNum] |= MXC_F_USB_EP_DT;
+
+ if (ep_info[EPNum].type == MXC_S_USB_EP_DIR_OUT) {
+ // This is an OUT endpoint. Go ahead and register a request.
+ desc = get_desc(EPNum);
+ desc->buf0_address = (uint32_t)ep_buffer[EPNum];
+ desc->buf0_desc = sizeof(ep_buffer[EPNum]);
+ MXC_USB->out_owner = (1 << EPNum);
+ }
+}
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_SetStallEP (U32 EPNum)
+{
+ EPNum &= EPNUM_MASK;
+
+ if (EPNum == 0) {
+ MXC_USB->ep[0] |= (MXC_F_USB_EP_ST_STALL | MXC_F_USB_EP_STALL);
+ } else {
+ MXC_USB->ep[EPNum] |= MXC_F_USB_EP_STALL;
+ }
+}
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_ClrStallEP (U32 EPNum)
+{
+ USBD_ResetEP(EPNum);
+ MXC_USB->ep[EPNum & EPNUM_MASK] &= ~MXC_F_USB_EP_STALL;
+}
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+U32 USBD_ReadEP (U32 EPNum, U8 *pData, U32 size)
+{
+ U32 cnt;
+ ep_buffer_t *desc = get_desc(EPNum);
+ USB_SETUP_PACKET *sup;
+
+ EPNum &= EPNUM_MASK;
+
+ if ((EPNum == 0) && setup_waiting) {
+ cnt = USBD_MAX_PACKET0;
+
+ if (size < cnt) {
+ cnt = size;
+ }
+ setup_waiting = 0;
+ memcpy(pData, (void*)&MXC_USB->setup0, cnt);
+ sup = (USB_SETUP_PACKET*)pData;
+
+ if ( (sup->bmRequestType.Dir == REQUEST_HOST_TO_DEVICE) && (sup->wLength > 0) ) {
+ // There is an OUT stage for this setup packet. Register a request.
+ if (!(MXC_USB->out_owner & 1)) {
+ desc = &ep_buffer_descriptor.ep0.out_buffer;
+ desc->buf0_address = (uint32_t)ep_buffer[0];
+ desc->buf0_desc = sup->wLength;
+ MXC_USB->out_owner = 1;
+ }
+ }
+ } else {
+ cnt = desc->buf0_desc;
+
+ if (size < cnt) {
+ cnt = size;
+ }
+ memcpy(pData, ep_buffer[EPNum], cnt);
+
+ // Register the next request.
+ desc->buf0_address = (uint32_t)ep_buffer[EPNum];
+ desc->buf0_desc = sizeof(ep_buffer[EPNum]);
+ MXC_USB->out_owner = (1 << EPNum);
+ }
+
+ return cnt;
+}
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+U32 USBD_WriteEP (U32 EPNum, U8 *pData, U32 cnt)
+{
+ ep_buffer_t *desc = get_desc(EPNum);
+ uint32_t mask;
+
+ EPNum &= EPNUM_MASK;
+ mask = (1 << EPNum);
+
+ if (MXC_USB->in_owner & mask) {
+ return 0;
+ }
+
+ if (EPNum == 0) {
+ // Prepare to ACK the status stage.
+ MXC_USB->ep[0] |= MXC_F_USB_EP_ST_ACK;
+
+ if ((cnt == 0) && !ep0_expect_zlp) {
+ // This is a status stage ACK. Handled in hardware.
+ return 0;
+ } else if (cnt == USBD_MAX_PACKET0) {
+ ep0_expect_zlp = 1;
+ } else {
+ ep0_expect_zlp = 0;
+ }
+ }
+
+ if (cnt > MXC_USB_MAX_PACKET) {
+ cnt = MXC_USB_MAX_PACKET;
+ }
+
+ /* prepare data to be sent */
+ memcpy(ep_buffer[EPNum], pData, cnt);
+ desc->buf0_address = (uint32_t)ep_buffer[EPNum];
+ desc->buf0_desc = cnt;
+
+ /* start the transaction */
+ MXC_USB->in_owner = mask;
+
+ return cnt;
+}
+
+/*
+ * USB Device Interrupt Service Routine
+ */
+void USB_IRQHandler (void)
+{
+ NVIC_DisableIRQ(USB_IRQn);
+ USBD_SignalHandler();
+}
+
+void USBD_Handler(void)
+{
+
+#ifdef __RTX
+ while(1) {}
+#else
+
+#endif
+
+ uint32_t irq_flags;
+ unsigned int ep;
+ uint32_t ep_int, mask;
+
+ // Read and clear interrupts
+ irq_flags = MXC_USB->dev_intfl;
+ MXC_USB->dev_intfl = irq_flags;
+
+ /* reset interrupt */
+ if (irq_flags & MXC_F_USB_DEV_INTFL_BRST) {
+ if (suspended) {
+ suspended = 0;
+#ifdef __RTX
+ if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask); }
+#else
+ if (USBD_P_Resume_Event) { USBD_P_Resume_Event(); }
+#endif
+ }
+
+ reset_state();
+ usbd_reset_core();
+
+#ifdef __RTX
+ if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask); }
+#else
+ if (USBD_P_Reset_Event) { USBD_P_Reset_Event(); }
+#endif
+
+ }
+
+ /* reset done interrupt */
+ if (irq_flags & MXC_F_USB_DEV_INTFL_BRST_DN) {
+ reset_state();
+ }
+
+ /* suspend interrupt */
+ if (irq_flags & MXC_F_USB_DEV_INTFL_SUSP) {
+ suspended = 1;
+#ifdef __RTX
+ if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask); }
+#else
+ if (USBD_P_Suspend_Event) { USBD_P_Suspend_Event(); }
+#endif
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_VBUS) {
+#ifdef __RTX
+ if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_POWER_ON, USBD_RTX_DevTask); }
+#else
+ if (USBD_P_Power_Event) { USBD_P_Power_Event(1); }
+#endif
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_NO_VBUS) {
+#ifdef __RTX
+ if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_POWER_OFF, USBD_RTX_DevTask); }
+#else
+ if (USBD_P_Power_Event) { USBD_P_Power_Event(0); }
+#endif
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_SETUP) {
+ setup_waiting = 1;
+#ifdef __RTX
+ if (USBD_RTX_EPTask[0]) { isr_evt_set(USBD_EVT_SETUP, USBD_RTX_EPTask[0]); }
+#else
+ if (USBD_P_EP[0]) { USBD_P_EP[0](USBD_EVT_SETUP); }
+#endif
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_EP_IN) {
+
+ // Read and clear endpoint interrupts
+ ep_int = MXC_USB->in_int;
+ MXC_USB->in_int = ep_int;
+
+ mask = 1;
+ for (ep = 0; ep < MXC_USB_NUM_EP; ep++) {
+ if (ep_int & mask) {
+#ifdef __RTX
+ if (USBD_RTX_EPTask[ep]) { isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[ep]); }
+#else
+ if (USBD_P_EP[ep]) { USBD_P_EP[ep](USBD_EVT_IN); }
+#endif
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_EP_OUT) {
+
+ // Read and clear endpoint interrupts
+ ep_int = MXC_USB->out_int;
+ MXC_USB->out_int = ep_int;
+
+ mask = 1;
+ for (ep = 0; ep < MXC_USB_NUM_EP; ep++) {
+ if (ep_int & mask) {
+#ifdef __RTX
+ if (USBD_RTX_EPTask[ep]) { isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[ep]); }
+#else
+ if (USBD_P_EP[ep]) { USBD_P_EP[ep](USBD_EVT_OUT); }
+#endif
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_DMA_ERR) {
+ // Read and clear endpoint interrupts
+ ep_int = MXC_USB->dma_err_int;
+ MXC_USB->dma_err_int = ep_int;
+ while(1); // not recoverable
+ }
+
+ NVIC_EnableIRQ(USB_IRQn);
+
+}
diff --git a/port/maxim/max32625/usbd_max32625.c b/port/maxim/max32625/usbd_max32625.c
new file mode 100644
index 00000000..c9fdf188
--- /dev/null
+++ b/port/maxim/max32625/usbd_max32625.c
@@ -0,0 +1,647 @@
+/* CMSIS-DAP Interface Firmware
+ * Copyright (c) 2009-2013 ARM Limited
+ *
+ * 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 <string.h>
+#include "rl_usb.h"
+#include "util.h"
+
+#include "max32625.h"
+#include "usb_regs.h"
+#include "clkman_regs.h"
+#include "pwrman_regs.h"
+#include "tmr_regs.h"
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+#define EPNUM_MASK (~USB_ENDPOINT_DIRECTION_MASK)
+
+#define INIT_INTS (MXC_F_USB_DEV_INTEN_BRST | MXC_F_USB_DEV_INTFL_BRST_DN | MXC_F_USB_DEV_INTEN_VBUS | MXC_F_USB_DEV_INTFL_NO_VBUS)
+#define CONNECT_INTS (MXC_F_USB_DEV_INTEN_SETUP | MXC_F_USB_DEV_INTEN_EP_IN | MXC_F_USB_DEV_INTEN_EP_OUT | MXC_F_USB_DEV_INTEN_DMA_ERR)
+
+typedef struct {
+ volatile uint32_t buf0_desc;
+ volatile uint32_t buf0_address;
+ volatile uint32_t buf1_desc;
+ volatile uint32_t buf1_address;
+} ep_buffer_t;
+
+typedef struct {
+ ep_buffer_t out_buffer;
+ ep_buffer_t in_buffer;
+} ep0_buffer_t;
+
+typedef struct {
+ ep0_buffer_t ep0;
+ ep_buffer_t ep[MXC_USB_NUM_EP - 1];
+} ep_buffer_descriptor_t;
+
+typedef struct {
+ U8 type;
+ U16 len;
+} ep_info_t;
+
+/* static storage for endpoint buffer descriptor table, must be 512 byte aligned for DMA */
+__attribute__ ((aligned (512)))
+ep_buffer_descriptor_t ep_buffer_descriptor;
+
+static uint32_t ep_buffer[MXC_USB_NUM_EP][MXC_USB_MAX_PACKET / sizeof(uint32_t)];
+static ep_info_t ep_info[MXC_USB_NUM_EP];
+static volatile int suspended;
+static volatile int setup_waiting;
+static volatile int ep0_expect_zlp;
+
+#if CDC_ENDPOINT
+/* CDC-ACM class processes FIFOs in the SOF interrupt. The USB Device interface
+ * of Maxim's microcontrollers does not provide and SOF interrupt. A periodic
+ * timer interrupt is used instead.
+ */
+/******************************************************************************/
+void TMR0_IRQHandler(void)
+{
+ MXC_TMR0->intfl = MXC_TMR0->intfl;
+
+ if (usbd_configured()) {
+ USBD_CDC_ACM_SOF_Event();
+ }
+}
+#endif
+
+/******************************************************************************/
+static ep_buffer_t *get_desc(U32 EPNum)
+{
+ ep_buffer_t *desc;
+
+ if (EPNum == 0x80) {
+ desc = &ep_buffer_descriptor.ep0.in_buffer;
+ } else if (EPNum == 0x00) {
+ desc = &ep_buffer_descriptor.ep0.out_buffer;
+ } else {
+ desc = &ep_buffer_descriptor.ep[(EPNum & EPNUM_MASK) - 1];
+ }
+
+ return desc;
+}
+
+/*
+ * USB Device Interrupt enable
+ * Called by USBD_Init to enable the USB Interrupt
+ * Return Value: None
+ */
+
+void USBD_IntrEna(void)
+{
+ NVIC_EnableIRQ(USB_IRQn); /* Enable OTG interrupt */
+}
+
+/******************************************************************************/
+/*
+ * Usb interrupt enable/disable
+ * Parameters: ena: enable/disable
+ * 0: disable interrupt
+ * 1: enable interrupt
+ */
+#ifdef __RTX
+void __svc(1) USBD_Intr (int ena);
+void __SVC_1 (int ena)
+{
+ if (ena) {
+ NVIC_EnableIRQ(USB_IRQn); /* Enable USB interrupt */
+ } else {
+ NVIC_DisableIRQ(USB_IRQn); /* Disable USB interrupt */
+ }
+}
+#endif
+
+/******************************************************************************/
+static void reset_state(void)
+{
+ unsigned int ep;
+
+ suspended = 0;
+ setup_waiting = 0;
+ ep0_expect_zlp = 0;
+ memset(ep_info, 0, sizeof(ep_info));
+
+ MXC_USB->ep[0] |= (MXC_S_USB_EP_DIR_CONTROL | MXC_F_USB_EP_INT_EN | MXC_F_USB_EP_DT);
+ for (ep = 1; ep < MXC_USB_NUM_EP; ep++) {
+ MXC_USB->ep[ep] = MXC_F_USB_EP_DT;
+ }
+}
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB Device
+ * Return Value: None
+ */
+void USBD_Init (void)
+{
+ uint32_t reg;
+
+ /* Enable USB power domain */
+ MXC_PWRMAN->pwr_rst_ctrl |= MXC_F_PWRMAN_PWR_RST_CTRL_USB_POWERED;
+ /* Setup the USB clocking, select */
+ MXC_CLKMAN->clk_ctrl |= MXC_F_CLKMAN_CLK_CTRL_USB_CLOCK_ENABLE;
+ /* Force USB clock gater */
+ reg = MXC_CLKMAN->clk_gate_ctrl0;
+ reg &= ~MXC_F_CLKMAN_CLK_GATE_CTRL0_USB_CLK_GATER;
+ reg |= (0x2 << MXC_F_CLKMAN_CLK_GATE_CTRL0_USB_CLK_GATER_POS);
+ MXC_CLKMAN->clk_gate_ctrl0 = reg;
+
+ MXC_USB->cn = 0;
+ MXC_USB->cn = MXC_F_USB_CN_USB_EN;
+ MXC_USB->dev_inten = 0;
+ MXC_USB->dev_intfl = 0xFFFF; // clear interrupts
+ MXC_USB->dev_cn = 0;
+ MXC_USB->dev_cn |= MXC_F_USB_DEV_CN_URST;
+ MXC_USB->dev_cn = 0;
+
+ reset_state();
+
+ /* set the descriptor location */
+ MXC_USB->ep_base = (uint32_t)&ep_buffer_descriptor;
+
+ /* enable some interrupts */
+ MXC_USB->dev_inten = INIT_INTS;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+void USBD_Connect (BOOL con)
+{
+ if (con) {
+ MXC_USB->dev_intfl = 0xFFFF; // clear interrupts
+ MXC_USB->dev_inten |= CONNECT_INTS;
+ MXC_USB->ep[0] |= MXC_F_USB_EP_INT_EN;
+ MXC_USB->dev_cn |= (MXC_F_USB_DEV_CN_CONNECT | MXC_F_USB_DEV_CN_FIFO_MODE);
+ } else {
+ MXC_USB->dev_inten &= ~CONNECT_INTS;
+ MXC_USB->ep[0] &= ~MXC_F_USB_EP_INT_EN;
+ MXC_USB->dev_cn &= ~MXC_F_USB_DEV_CN_CONNECT;
+ }
+}
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+void USBD_WakeUpCfg (BOOL cfg)
+{
+}
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * Return Value: None
+ */
+void USBD_SetAddress (U32 adr, U32 setup)
+{
+ /* Performed by Hardware */
+}
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+void USBD_Configure (BOOL cfg)
+{
+#if CDC_ENDPOINT
+ /* CDC-ACM class processes FIFOs in the SOF interrupt. The USB Device interface
+ * of Maxim's microcontrollers does not provide and SOF interrupt. A periodic
+ * timer interrupt is used instead.
+ */
+
+ #define SOF_INT_US 1000
+
+ if (cfg) {
+ // Setup timer interrupt for SOF
+ MXC_TMR0->ctrl = MXC_S_TMR_CTRL_MODE_CONTINUOUS;
+ MXC_TMR0->count32 = 0;
+ MXC_TMR0->term_cnt32 = (SystemCoreClock / 1000000) * SOF_INT_US;
+
+ // Enable the interrupt
+ MXC_TMR0->intfl = MXC_TMR0->intfl;
+ NVIC_EnableIRQ(TMR0_0_IRQn);
+ MXC_TMR0->inten = MXC_F_TMR_INTEN_TIMER0;
+
+ // Start the timer
+ MXC_TMR0->ctrl |= MXC_F_TMR_CTRL_ENABLE0;
+
+ } else {
+ // Disable tmr
+ MXC_TMR0->ctrl &= ~(MXC_F_TMR_CTRL_ENABLE0);
+ }
+#endif
+}
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+void USBD_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ U32 EPNum;
+
+ EPNum = pEPD->bEndpointAddress & EPNUM_MASK;
+
+ if (EPNum < MXC_USB_NUM_EP) {
+
+ // Clear existing configurations
+ MXC_USB->ep[EPNum] = MXC_F_USB_EP_DT;
+
+ if (pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) {
+ ep_info[EPNum].type = MXC_S_USB_EP_DIR_IN;
+ } else {
+ ep_info[EPNum].type = MXC_S_USB_EP_DIR_OUT;
+ }
+
+ ep_info[EPNum].len = pEPD->wMaxPacketSize;
+ }
+}
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+void USBD_DirCtrlEP (U32 dir)
+{
+ /* Not needed */
+}
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_EnableEP (U32 EPNum)
+{
+ ep_buffer_t *desc = get_desc(EPNum);
+
+ EPNum &= EPNUM_MASK;
+ MXC_USB->ep[EPNum] |= (MXC_F_USB_EP_INT_EN | ep_info[EPNum].type | MXC_F_USB_EP_DT);
+
+ if (ep_info[EPNum].type == MXC_S_USB_EP_DIR_OUT) {
+ // This is an OUT endpoint. Go ahead and register a request.
+ desc = get_desc(EPNum);
+ desc->buf0_address = (uint32_t)ep_buffer[EPNum];
+ desc->buf0_desc = sizeof(ep_buffer[EPNum]);
+ MXC_USB->out_owner = (1 << EPNum);
+ }
+}
+
+/*
+ * Disable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_DisableEP (U32 EPNum)
+{
+ EPNum &= EPNUM_MASK;
+ MXC_USB->ep[EPNum] = 0;
+}
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_ResetEP (U32 EPNum)
+{
+ ep_buffer_t *desc = get_desc(EPNum);
+
+ EPNum &= EPNUM_MASK;
+ MXC_USB->ep[EPNum] |= MXC_F_USB_EP_DT;
+
+ if (ep_info[EPNum].type == MXC_S_USB_EP_DIR_OUT) {
+ // This is an OUT endpoint. Go ahead and register a request.
+ desc = get_desc(EPNum);
+ desc->buf0_address = (uint32_t)ep_buffer[EPNum];
+ desc->buf0_desc = sizeof(ep_buffer[EPNum]);
+ MXC_USB->out_owner = (1 << EPNum);
+ }
+}
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_SetStallEP (U32 EPNum)
+{
+ EPNum &= EPNUM_MASK;
+
+ if (EPNum == 0) {
+ MXC_USB->ep[0] |= (MXC_F_USB_EP_ST_STALL | MXC_F_USB_EP_STALL);
+ } else {
+ MXC_USB->ep[EPNum] |= MXC_F_USB_EP_STALL;
+ }
+}
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+void USBD_ClrStallEP (U32 EPNum)
+{
+ USBD_ResetEP(EPNum);
+ MXC_USB->ep[EPNum & EPNUM_MASK] &= ~MXC_F_USB_EP_STALL;
+}
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+U32 USBD_ReadEP (U32 EPNum, U8 *pData, U32 size)
+{
+ U32 cnt;
+ ep_buffer_t *desc = get_desc(EPNum);
+ USB_SETUP_PACKET *sup;
+
+ EPNum &= EPNUM_MASK;
+
+ if ((EPNum == 0) && setup_waiting) {
+ cnt = USBD_MAX_PACKET0;
+
+ if (size < cnt) {
+ cnt = size;
+ }
+ setup_waiting = 0;
+ memcpy(pData, (void*)&MXC_USB->setup0, cnt);
+ sup = (USB_SETUP_PACKET*)pData;
+
+ if ( (sup->bmRequestType.Dir == REQUEST_HOST_TO_DEVICE) && (sup->wLength > 0) ) {
+ // There is an OUT stage for this setup packet. Register a request.
+ if (!(MXC_USB->out_owner & 1)) {
+ desc = &ep_buffer_descriptor.ep0.out_buffer;
+ desc->buf0_address = (uint32_t)ep_buffer[0];
+ desc->buf0_desc = sup->wLength;
+ MXC_USB->out_owner = 1;
+ }
+ }
+ } else {
+ cnt = desc->buf0_desc;
+
+ if (size < cnt) {
+ cnt = size;
+ }
+ memcpy(pData, ep_buffer[EPNum], cnt);
+
+ // Register the next request.
+ desc->buf0_address = (uint32_t)ep_buffer[EPNum];
+ desc->buf0_desc = sizeof(ep_buffer[EPNum]);
+ MXC_USB->out_owner = (1 << EPNum);
+ }
+
+ return cnt;
+}
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+U32 USBD_WriteEP (U32 EPNum, U8 *pData, U32 cnt)
+{
+ ep_buffer_t *desc = get_desc(EPNum);
+ uint32_t mask;
+
+ EPNum &= EPNUM_MASK;
+ mask = (1 << EPNum);
+
+ if (MXC_USB->in_owner & mask) {
+ return 0;
+ }
+
+ if (EPNum == 0) {
+ // Prepare to ACK the status stage.
+ MXC_USB->ep[0] |= MXC_F_USB_EP_ST_ACK;
+
+ if ((cnt == 0) && !ep0_expect_zlp) {
+ // This is a status stage ACK. Handled in hardware.
+ return 0;
+ } else if (cnt == USBD_MAX_PACKET0) {
+ ep0_expect_zlp = 1;
+ } else {
+ ep0_expect_zlp = 0;
+ }
+ }
+
+ if (cnt > MXC_USB_MAX_PACKET) {
+ cnt = MXC_USB_MAX_PACKET;
+ }
+
+ /* prepare data to be sent */
+ memcpy(ep_buffer[EPNum], pData, cnt);
+ desc->buf0_address = (uint32_t)ep_buffer[EPNum];
+ desc->buf0_desc = cnt;
+
+ /* start the transaction */
+ MXC_USB->in_owner = mask;
+
+ return cnt;
+}
+
+/*
+ * USB Device Interrupt Service Routine
+ */
+void USB_IRQHandler (void)
+{
+ NVIC_DisableIRQ(USB_IRQn);
+ USBD_SignalHandler();
+}
+
+void USBD_Handler(void)
+{
+ uint32_t irq_flags;
+ unsigned int ep;
+ uint32_t ep_int, mask;
+
+ // Read and clear interrupts
+ irq_flags = MXC_USB->dev_intfl;
+ MXC_USB->dev_intfl = irq_flags;
+
+ /* reset interrupt */
+ if (irq_flags & MXC_F_USB_DEV_INTFL_BRST) {
+ if (suspended) {
+ suspended = 0;
+#ifdef __RTX
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask);
+ }
+#else
+ if (USBD_P_Resume_Event) {
+ USBD_P_Resume_Event();
+ }
+#endif
+ }
+
+ reset_state();
+ usbd_reset_core();
+
+#ifdef __RTX
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask);
+ }
+#else
+ if (USBD_P_Reset_Event) {
+ USBD_P_Reset_Event();
+ }
+#endif
+
+ }
+
+ /* reset done interrupt */
+ if (irq_flags & MXC_F_USB_DEV_INTFL_BRST_DN) {
+ reset_state();
+ }
+
+ /* suspend interrupt */
+ if (irq_flags & MXC_F_USB_DEV_INTFL_SUSP) {
+ suspended = 1;
+#ifdef __RTX
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask);
+ }
+#else
+ if (USBD_P_Suspend_Event) {
+ USBD_P_Suspend_Event();
+ }
+#endif
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_VBUS) {
+#ifdef __RTX
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_POWER_ON, USBD_RTX_DevTask);
+ }
+#else
+ if (USBD_P_Power_Event) {
+ USBD_P_Power_Event(1);
+ }
+#endif
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_NO_VBUS) {
+#ifdef __RTX
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_POWER_OFF, USBD_RTX_DevTask);
+ }
+#else
+ if (USBD_P_Power_Event) {
+ USBD_P_Power_Event(0);
+ }
+#endif
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_SETUP) {
+ setup_waiting = 1;
+#ifdef __RTX
+ if (USBD_RTX_EPTask[0]) {
+ isr_evt_set(USBD_EVT_SETUP, USBD_RTX_EPTask[0]);
+ }
+#else
+ if (USBD_P_EP[0]) {
+ USBD_P_EP[0](USBD_EVT_SETUP);
+ }
+#endif
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_EP_IN) {
+
+ // Read and clear endpoint interrupts
+ ep_int = MXC_USB->in_int;
+ MXC_USB->in_int = ep_int;
+
+ mask = 1;
+ for (ep = 0; ep < MXC_USB_NUM_EP; ep++) {
+ if (ep_int & mask) {
+#ifdef __RTX
+ if (USBD_RTX_EPTask[ep]) {
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[ep]);
+ }
+#else
+ if (USBD_P_EP[ep]) {
+ USBD_P_EP[ep](USBD_EVT_IN);
+ }
+#endif
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_EP_OUT) {
+
+ // Read and clear endpoint interrupts
+ ep_int = MXC_USB->out_int;
+ MXC_USB->out_int = ep_int;
+
+ mask = 1;
+ for (ep = 0; ep < MXC_USB_NUM_EP; ep++) {
+ if (ep_int & mask) {
+#ifdef __RTX
+ if (USBD_RTX_EPTask[ep]) {
+ isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[ep]);
+ }
+#else
+ if (USBD_P_EP[ep]) {
+ USBD_P_EP[ep](USBD_EVT_OUT);
+ }
+#endif
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ if (irq_flags & MXC_F_USB_DEV_INTFL_DMA_ERR) {
+ // Read and clear endpoint interrupts
+ ep_int = MXC_USB->dma_err_int;
+ MXC_USB->dma_err_int = ep_int;
+ while(1); // not recoverable
+ }
+
+ NVIC_EnableIRQ(USB_IRQn);
+
+}
diff --git a/port/nordic/usb_dc_nrfx.c b/port/nordic/usb_dc_nrfx.c
new file mode 100644
index 00000000..eb6c6c9c
--- /dev/null
+++ b/port/nordic/usb_dc_nrfx.c
@@ -0,0 +1,1928 @@
+/*
+ * Copyright (c) 2018, Nordic Semiconductor ASA
+ * Copyright (c) 2018 Sundar Subramaniyan <[email protected]>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file usb_dc_nrfx.c
+ * @brief Nordic USB device controller driver
+ *
+ * The driver implements the interface between the USBD peripheral
+ * driver from nrfx package and the operating system.
+ */
+
+#include <soc.h>
+#include <string.h>
+#include <stdio.h>
+#include <kernel.h>
+#include <drivers/usb/usb_dc.h>
+#include <usb/usb_device.h>
+#include <drivers/clock_control.h>
+#include <drivers/clock_control/nrf_clock_control.h>
+#include <nrfx_usbd.h>
+#include <nrfx_power.h>
+
+
+#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
+#include <logging/log.h>
+LOG_MODULE_REGISTER(usb_nrfx);
+
+/* USB device controller access from devicetree */
+#define DT_DRV_COMPAT nordic_nrf_usbd
+
+/**
+ * @brief nRF USBD peripheral states
+ */
+enum usbd_periph_state {
+ USBD_DETACHED,
+ USBD_ATTACHED,
+ USBD_POWERED,
+ USBD_SUSPENDED,
+ USBD_RESUMED,
+ USBD_DEFAULT,
+ USBD_ADDRESS_SET,
+ USBD_CONFIGURED,
+};
+
+/**
+ * @brief Endpoint event types.
+ */
+enum usbd_ep_event_type {
+ EP_EVT_SETUP_RECV,
+ EP_EVT_RECV_REQ,
+ EP_EVT_RECV_COMPLETE,
+ EP_EVT_WRITE_COMPLETE,
+};
+
+/**
+ * @brief USBD peripheral event types.
+ */
+enum usbd_event_type {
+ USBD_EVT_POWER,
+ USBD_EVT_EP,
+ USBD_EVT_RESET,
+ USBD_EVT_SOF,
+ USBD_EVT_REINIT
+};
+
+/**
+ * @brief Endpoint configuration.
+ *
+ * @param cb Endpoint callback.
+ * @param max_sz Max packet size supported by endpoint.
+ * @param en Enable/Disable flag.
+ * @param addr Endpoint address.
+ * @param type Endpoint transfer type.
+ */
+struct nrf_usbd_ep_cfg {
+ usb_dc_ep_callback cb;
+ uint32_t max_sz;
+ bool en;
+ uint8_t addr;
+ enum usb_dc_ep_transfer_type type;
+
+};
+
+struct usbd_mem_block {
+ void *data;
+};
+
+/**
+ * @brief Endpoint buffer
+ *
+ * @param len Remaining length to be read/written.
+ * @param block Mempool block, for freeing up buffer after use.
+ * @param data Pointer to the data buffer for the endpoint.
+ * @param curr Pointer to the current offset in the endpoint buffer.
+ */
+struct nrf_usbd_ep_buf {
+ uint32_t len;
+ struct usbd_mem_block block;
+ uint8_t *data;
+ uint8_t *curr;
+};
+
+/**
+ * @brief Endpoint context
+ *
+ * @param cfg Endpoint configuration
+ * @param buf Endpoint buffer
+ * @param read_complete A flag indicating that DMA read operation
+ * has been completed.
+ * @param read_pending A flag indicating that the Host has requested
+ * a data transfer.
+ * @param write_in_progress A flag indicating that write operation has
+ * been scheduled.
+ * @param trans_zlp Flag required for Control IN Endpoint. It
+ * indicates that ZLP is required to end data
+ * stage of the control request.
+ */
+struct nrf_usbd_ep_ctx {
+ struct nrf_usbd_ep_cfg cfg;
+ struct nrf_usbd_ep_buf buf;
+ volatile bool read_complete;
+ volatile bool read_pending;
+ volatile bool write_in_progress;
+ bool trans_zlp;
+};
+
+/**
+ * @brief Endpoint event structure
+ *
+ * @param ep Endpoint control block pointer
+ * @param evt_type Event type
+ */
+struct usbd_ep_event {
+ struct nrf_usbd_ep_ctx *ep;
+ enum usbd_ep_event_type evt_type;
+};
+
+/**
+ * @brief Power event structure
+ *
+ * @param state New USBD peripheral state.
+ */
+struct usbd_pwr_event {
+ enum usbd_periph_state state;
+};
+
+/**
+ * @brief Endpoint USB event
+ * Used by ISR to send events to work handler
+ *
+ * @param node Used by the kernel for FIFO management
+ * @param block Mempool block pointer for freeing up after use
+ * @param evt Event data field
+ * @param evt_type Type of event that has occurred from the USBD peripheral
+ */
+struct usbd_event {
+ sys_snode_t node;
+ struct usbd_mem_block block;
+ union {
+ struct usbd_ep_event ep_evt;
+ struct usbd_pwr_event pwr_evt;
+ } evt;
+ enum usbd_event_type evt_type;
+};
+
+/**
+ * @brief Fifo element slab
+ * Used for allocating fifo elements to pass from ISR to work handler
+ * TODO: The number of FIFO elements is an arbitrary number now but it should
+ * be derived from the theoretical number of backlog events possible depending
+ * on the number of endpoints configured.
+ */
+#define FIFO_ELEM_SZ sizeof(struct usbd_event)
+#define FIFO_ELEM_ALIGN sizeof(unsigned int)
+
+K_MEM_SLAB_DEFINE(fifo_elem_slab, FIFO_ELEM_SZ,
+ CONFIG_USB_NRFX_EVT_QUEUE_SIZE, FIFO_ELEM_ALIGN);
+
+
+/** Number of IN Endpoints configured (including control) */
+#define CFG_EPIN_CNT (DT_INST_PROP(0, num_in_endpoints) + \
+ DT_INST_PROP(0, num_bidir_endpoints))
+
+/** Number of OUT Endpoints configured (including control) */
+#define CFG_EPOUT_CNT (DT_INST_PROP(0, num_out_endpoints) + \
+ DT_INST_PROP(0, num_bidir_endpoints))
+
+/** Number of ISO IN Endpoints */
+#define CFG_EP_ISOIN_CNT DT_INST_PROP(0, num_isoin_endpoints)
+
+/** Number of ISO OUT Endpoints */
+#define CFG_EP_ISOOUT_CNT DT_INST_PROP(0, num_isoout_endpoints)
+
+/** ISO endpoint index */
+#define EP_ISOIN_INDEX CFG_EPIN_CNT
+#define EP_ISOOUT_INDEX (CFG_EPIN_CNT + CFG_EP_ISOIN_CNT + CFG_EPOUT_CNT)
+
+#define EP_BUF_MAX_SZ 64UL
+#define ISO_EP_BUF_MAX_SZ 1024UL
+
+/**
+ * @brief Output endpoint buffers
+ * Used as buffers for the endpoints' data transfer
+ * Max buffers size possible: 1536 Bytes (8 EP * 64B + 1 ISO * 1024B)
+ */
+static uint8_t ep_out_bufs[CFG_EPOUT_CNT][EP_BUF_MAX_SZ]
+ __aligned(sizeof(uint32_t));
+static uint8_t ep_isoout_bufs[CFG_EP_ISOOUT_CNT][ISO_EP_BUF_MAX_SZ]
+ __aligned(sizeof(uint32_t));
+
+/** Total endpoints configured */
+#define CFG_EP_CNT (CFG_EPIN_CNT + CFG_EP_ISOIN_CNT + \
+ CFG_EPOUT_CNT + CFG_EP_ISOOUT_CNT)
+
+/**
+ * @brief USBD control structure
+ *
+ * @param status_cb Status callback for USB DC notifications
+ * @param setup Setup packet for Control requests
+ * @param hfxo_cli Onoff client used to control HFXO
+ * @param hfxo_mgr Pointer to onoff manager associated with HFXO.
+ * @param clk_requested Flag used to protect against double stop.
+ * @param attached USBD Attached flag
+ * @param ready USBD Ready flag set after pullup
+ * @param usb_work USBD work item
+ * @param drv_lock Mutex for thread-safe nrfx driver use
+ * @param ep_ctx Endpoint contexts
+ * @param ctrl_read_len State of control read operation (EP0).
+ */
+struct nrf_usbd_ctx {
+ usb_dc_status_callback status_cb;
+ struct usb_setup_packet setup;
+ struct onoff_client hfxo_cli;
+ struct onoff_manager *hfxo_mgr;
+ atomic_t clk_requested;
+
+ bool attached;
+ bool ready;
+
+ struct k_work usb_work;
+ struct k_mutex drv_lock;
+
+ struct nrf_usbd_ep_ctx ep_ctx[CFG_EP_CNT];
+
+ uint16_t ctrl_read_len;
+};
+
+
+/* FIFO used for queuing up events from ISR. */
+K_FIFO_DEFINE(usbd_evt_fifo);
+
+/* Work queue used for handling the ISR events (i.e. for notifying the USB
+ * device stack, for executing the endpoints callbacks, etc.) out of the ISR
+ * context.
+ * The system work queue cannot be used for this purpose as it might be used in
+ * applications for scheduling USB transfers and this could lead to a deadlock
+ * when the USB device stack would not be notified about certain event because
+ * of a system work queue item waiting for a USB transfer to be finished.
+ */
+static struct k_work_q usbd_work_queue;
+static K_KERNEL_STACK_DEFINE(usbd_work_queue_stack,
+ CONFIG_USB_NRFX_WORK_QUEUE_STACK_SIZE);
+
+
+static struct nrf_usbd_ctx usbd_ctx = {
+ .attached = false,
+ .ready = false,
+};
+
+static inline struct nrf_usbd_ctx *get_usbd_ctx(void)
+{
+ return &usbd_ctx;
+}
+
+static inline bool dev_attached(void)
+{
+ return get_usbd_ctx()->attached;
+}
+
+static inline bool dev_ready(void)
+{
+ return get_usbd_ctx()->ready;
+}
+
+static inline nrfx_usbd_ep_t ep_addr_to_nrfx(uint8_t ep)
+{
+ return (nrfx_usbd_ep_t)ep;
+}
+
+static inline uint8_t nrfx_addr_to_ep(nrfx_usbd_ep_t ep)
+{
+ return (uint8_t)ep;
+}
+
+static inline bool ep_is_valid(const uint8_t ep)
+{
+ uint8_t ep_num = USB_EP_GET_IDX(ep);
+
+ if (NRF_USBD_EPIN_CHECK(ep)) {
+ if (unlikely(ep_num == NRF_USBD_EPISO_FIRST)) {
+ if (CFG_EP_ISOIN_CNT == 0) {
+ return false;
+ }
+ } else {
+ if (ep_num >= CFG_EPIN_CNT) {
+ return false;
+ }
+ }
+ } else {
+ if (unlikely(ep_num == NRF_USBD_EPISO_FIRST)) {
+ if (CFG_EP_ISOOUT_CNT == 0) {
+ return false;
+ }
+ } else {
+ if (ep_num >= CFG_EPOUT_CNT) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static struct nrf_usbd_ep_ctx *endpoint_ctx(const uint8_t ep)
+{
+ struct nrf_usbd_ctx *ctx;
+ uint8_t ep_num;
+
+ if (!ep_is_valid(ep)) {
+ return NULL;
+ }
+
+ ctx = get_usbd_ctx();
+ ep_num = NRF_USBD_EP_NR_GET(ep);
+
+ if (NRF_USBD_EPIN_CHECK(ep)) {
+ if (unlikely(NRF_USBD_EPISO_CHECK(ep))) {
+ return &ctx->ep_ctx[EP_ISOIN_INDEX];
+ } else {
+ return &ctx->ep_ctx[ep_num];
+ }
+ } else {
+ if (unlikely(NRF_USBD_EPISO_CHECK(ep))) {
+ return &ctx->ep_ctx[EP_ISOOUT_INDEX];
+ } else {
+ return &ctx->ep_ctx[CFG_EPIN_CNT +
+ CFG_EP_ISOIN_CNT +
+ ep_num];
+ }
+ }
+
+ return NULL;
+}
+
+static struct nrf_usbd_ep_ctx *in_endpoint_ctx(const uint8_t ep)
+{
+ return endpoint_ctx(NRF_USBD_EPIN(ep));
+}
+
+static struct nrf_usbd_ep_ctx *out_endpoint_ctx(const uint8_t ep)
+{
+ return endpoint_ctx(NRF_USBD_EPOUT(ep));
+}
+
+/**
+ * @brief Schedule USBD event processing.
+ *
+ * Should be called after usbd_evt_put().
+ */
+static inline void usbd_work_schedule(void)
+{
+ k_work_submit_to_queue(&usbd_work_queue, &get_usbd_ctx()->usb_work);
+}
+
+/**
+ * @brief Free previously allocated USBD event.
+ *
+ * Should be called after usbd_evt_get().
+ *
+ * @param Pointer to the USBD event structure.
+ */
+static inline void usbd_evt_free(struct usbd_event *ev)
+{
+ k_mem_slab_free(&fifo_elem_slab, (void **)&ev->block.data);
+}
+
+/**
+ * @brief Enqueue USBD event.
+ *
+ * @param Pointer to the previously allocated and filled event structure.
+ */
+static inline void usbd_evt_put(struct usbd_event *ev)
+{
+ k_fifo_put(&usbd_evt_fifo, ev);
+}
+
+/**
+ * @brief Get next enqueued USBD event if present.
+ */
+static inline struct usbd_event *usbd_evt_get(void)
+{
+ return k_fifo_get(&usbd_evt_fifo, K_NO_WAIT);
+}
+
+/**
+ * @brief Drop all enqueued events.
+ */
+static inline void usbd_evt_flush(void)
+{
+ struct usbd_event *ev;
+
+ do {
+ ev = usbd_evt_get();
+ if (ev) {
+ usbd_evt_free(ev);
+ }
+ } while (ev != NULL);
+}
+
+/**
+ * @brief Allocate USBD event.
+ *
+ * This function should be called prior to usbd_evt_put().
+ *
+ * @returns Pointer to the allocated event or NULL if there was no space left.
+ */
+static inline struct usbd_event *usbd_evt_alloc(void)
+{
+ struct usbd_event *ev;
+ struct usbd_mem_block block;
+
+ if (k_mem_slab_alloc(&fifo_elem_slab,
+ (void **)&block.data, K_NO_WAIT)) {
+ LOG_ERR("USBD event allocation failed!");
+
+ /*
+ * Allocation may fail if workqueue thread is starved or event
+ * queue size is too small (CONFIG_USB_NRFX_EVT_QUEUE_SIZE).
+ * Wipe all events, free the space and schedule
+ * reinitialization.
+ */
+ usbd_evt_flush();
+
+ if (k_mem_slab_alloc(&fifo_elem_slab, (void **)&block.data, K_NO_WAIT)) {
+ LOG_ERR("USBD event memory corrupted");
+ __ASSERT_NO_MSG(0);
+ return NULL;
+ }
+
+ ev = (struct usbd_event *)block.data;
+ ev->block = block;
+ ev->evt_type = USBD_EVT_REINIT;
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+
+ return NULL;
+ }
+
+ ev = (struct usbd_event *)block.data;
+ ev->block = block;
+
+ return ev;
+}
+
+static void usb_dc_power_event_handler(nrfx_power_usb_evt_t event)
+{
+ enum usbd_periph_state new_state;
+
+ switch (event) {
+ case NRFX_POWER_USB_EVT_DETECTED:
+ new_state = USBD_ATTACHED;
+ break;
+ case NRFX_POWER_USB_EVT_READY:
+ new_state = USBD_POWERED;
+ break;
+ case NRFX_POWER_USB_EVT_REMOVED:
+ new_state = USBD_DETACHED;
+ break;
+ default:
+ LOG_ERR("Unknown USB power event %d", event);
+ return;
+ }
+
+ struct usbd_event *ev = usbd_evt_alloc();
+
+ if (!ev) {
+ return;
+ }
+
+ ev->evt_type = USBD_EVT_POWER;
+ ev->evt.pwr_evt.state = new_state;
+
+
+ usbd_evt_put(ev);
+
+ if (usbd_ctx.attached) {
+ usbd_work_schedule();
+ }
+}
+
+/* Stopping HFXO, algorithm supports case when stop comes before clock is
+ * started. In that case, it is stopped from the callback context.
+ */
+static int hfxo_stop(struct nrf_usbd_ctx *ctx)
+{
+ if (atomic_cas(&ctx->clk_requested, 1, 0)) {
+ return onoff_cancel_or_release(ctx->hfxo_mgr, &ctx->hfxo_cli);
+ }
+
+ return 0;
+}
+
+static int hfxo_start(struct nrf_usbd_ctx *ctx)
+{
+ if (atomic_cas(&ctx->clk_requested, 0, 1)) {
+ sys_notify_init_spinwait(&ctx->hfxo_cli.notify);
+
+ return onoff_request(ctx->hfxo_mgr, &ctx->hfxo_cli);
+ }
+
+ return 0;
+}
+
+static void usbd_enable_endpoints(struct nrf_usbd_ctx *ctx)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+ int i;
+
+ for (i = 0; i < CFG_EPIN_CNT; i++) {
+ ep_ctx = in_endpoint_ctx(i);
+ __ASSERT_NO_MSG(ep_ctx);
+
+ if (ep_ctx->cfg.en) {
+ nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep_ctx->cfg.addr));
+ }
+ }
+
+ if (CFG_EP_ISOIN_CNT) {
+ ep_ctx = in_endpoint_ctx(NRF_USBD_EPIN(8));
+ __ASSERT_NO_MSG(ep_ctx);
+
+ if (ep_ctx->cfg.en) {
+ nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep_ctx->cfg.addr));
+ }
+ }
+
+ for (i = 0; i < CFG_EPOUT_CNT; i++) {
+ ep_ctx = out_endpoint_ctx(i);
+ __ASSERT_NO_MSG(ep_ctx);
+
+ if (ep_ctx->cfg.en) {
+ nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep_ctx->cfg.addr));
+ }
+ }
+
+ if (CFG_EP_ISOOUT_CNT) {
+ ep_ctx = out_endpoint_ctx(NRF_USBD_EPOUT(8));
+ __ASSERT_NO_MSG(ep_ctx);
+
+ if (ep_ctx->cfg.en) {
+ nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep_ctx->cfg.addr));
+ }
+ }
+}
+
+/**
+ * @brief Reset endpoint state.
+ *
+ * Resets the internal logic state for a given endpoint.
+ *
+ * @param[in] ep_cts Endpoint structure control block
+ */
+static void ep_ctx_reset(struct nrf_usbd_ep_ctx *ep_ctx)
+{
+ ep_ctx->buf.data = ep_ctx->buf.block.data;
+ ep_ctx->buf.curr = ep_ctx->buf.data;
+ ep_ctx->buf.len = 0U;
+
+ /* Abort ongoing write operation. */
+ if (ep_ctx->write_in_progress) {
+ nrfx_usbd_ep_abort(ep_addr_to_nrfx(ep_ctx->cfg.addr));
+ }
+
+ ep_ctx->read_complete = true;
+ ep_ctx->read_pending = false;
+ ep_ctx->write_in_progress = false;
+ ep_ctx->trans_zlp = false;
+}
+
+/**
+ * @brief Initialize all endpoint structures.
+ *
+ * Endpoint buffers are allocated during the first call of this function.
+ * This function may also be called again on every USB reset event
+ * to reinitialize the state of all endpoints.
+ */
+static int eps_ctx_init(void)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+ uint32_t i;
+
+ for (i = 0U; i < CFG_EPIN_CNT; i++) {
+ ep_ctx = in_endpoint_ctx(i);
+ __ASSERT_NO_MSG(ep_ctx);
+ ep_ctx_reset(ep_ctx);
+ }
+
+ for (i = 0U; i < CFG_EPOUT_CNT; i++) {
+ ep_ctx = out_endpoint_ctx(i);
+ __ASSERT_NO_MSG(ep_ctx);
+
+ if (!ep_ctx->buf.block.data) {
+ ep_ctx->buf.block.data = ep_out_bufs[i];
+ }
+
+ ep_ctx_reset(ep_ctx);
+ }
+
+ if (CFG_EP_ISOIN_CNT) {
+ ep_ctx = in_endpoint_ctx(NRF_USBD_EPIN(8));
+ __ASSERT_NO_MSG(ep_ctx);
+ ep_ctx_reset(ep_ctx);
+ }
+
+ if (CFG_EP_ISOOUT_CNT) {
+ BUILD_ASSERT(CFG_EP_ISOOUT_CNT <= 1);
+
+ ep_ctx = out_endpoint_ctx(NRF_USBD_EPOUT(8));
+ __ASSERT_NO_MSG(ep_ctx);
+
+ if (!ep_ctx->buf.block.data) {
+ ep_ctx->buf.block.data = ep_isoout_bufs[0];
+ }
+
+ ep_ctx_reset(ep_ctx);
+ }
+
+ return 0;
+}
+
+static void eps_ctx_uninit(void)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+ uint32_t i;
+
+ for (i = 0U; i < CFG_EPIN_CNT; i++) {
+ ep_ctx = in_endpoint_ctx(i);
+ __ASSERT_NO_MSG(ep_ctx);
+ memset(ep_ctx, 0, sizeof(*ep_ctx));
+ }
+
+ for (i = 0U; i < CFG_EPOUT_CNT; i++) {
+ ep_ctx = out_endpoint_ctx(i);
+ __ASSERT_NO_MSG(ep_ctx);
+ memset(ep_ctx, 0, sizeof(*ep_ctx));
+ }
+
+ if (CFG_EP_ISOIN_CNT) {
+ ep_ctx = in_endpoint_ctx(NRF_USBD_EPIN(8));
+ __ASSERT_NO_MSG(ep_ctx);
+ memset(ep_ctx, 0, sizeof(*ep_ctx));
+ }
+
+ if (CFG_EP_ISOOUT_CNT) {
+ ep_ctx = out_endpoint_ctx(NRF_USBD_EPOUT(8));
+ __ASSERT_NO_MSG(ep_ctx);
+ memset(ep_ctx, 0, sizeof(*ep_ctx));
+ }
+}
+
+static inline void usbd_work_process_pwr_events(struct usbd_pwr_event *pwr_evt)
+{
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+ int err;
+
+ switch (pwr_evt->state) {
+ case USBD_ATTACHED:
+ if (!nrfx_usbd_is_enabled()) {
+ LOG_DBG("USB detected");
+ nrfx_usbd_enable();
+ err = hfxo_start(ctx);
+ __ASSERT_NO_MSG(err >= 0);
+ }
+
+ /* No callback here.
+ * Stack will be notified when the peripheral is ready.
+ */
+ break;
+
+ case USBD_POWERED:
+ usbd_enable_endpoints(ctx);
+ nrfx_usbd_start(true);
+ ctx->ready = true;
+
+ LOG_DBG("USB Powered");
+
+ if (ctx->status_cb) {
+ ctx->status_cb(USB_DC_CONNECTED, NULL);
+ }
+ break;
+
+ case USBD_DETACHED:
+ ctx->ready = false;
+ nrfx_usbd_disable();
+ err = hfxo_stop(ctx);
+ __ASSERT_NO_MSG(err >= 0);
+
+ LOG_DBG("USB Removed");
+
+ if (ctx->status_cb) {
+ ctx->status_cb(USB_DC_DISCONNECTED, NULL);
+ }
+ break;
+
+ case USBD_SUSPENDED:
+ if (dev_ready()) {
+ nrfx_usbd_suspend();
+ LOG_DBG("USB Suspend state");
+
+ if (ctx->status_cb) {
+ ctx->status_cb(USB_DC_SUSPEND, NULL);
+ }
+ }
+ break;
+ case USBD_RESUMED:
+ if (ctx->status_cb && dev_ready()) {
+ LOG_DBG("USB resume");
+ ctx->status_cb(USB_DC_RESUME, NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static inline void usbd_work_process_setup(struct nrf_usbd_ep_ctx *ep_ctx)
+{
+ __ASSERT_NO_MSG(ep_ctx);
+ __ASSERT(ep_ctx->cfg.type == USB_DC_EP_CONTROL,
+ "Invalid event on CTRL EP.");
+
+ struct usb_setup_packet *usbd_setup;
+
+ /* SETUP packets are handled by USBD hardware.
+ * For compatibility with the USB stack,
+ * SETUP packet must be reassembled.
+ */
+ usbd_setup = (struct usb_setup_packet *)ep_ctx->buf.data;
+ memset(usbd_setup, 0, sizeof(struct usb_setup_packet));
+ usbd_setup->bmRequestType = nrf_usbd_setup_bmrequesttype_get(NRF_USBD);
+ usbd_setup->bRequest = nrf_usbd_setup_brequest_get(NRF_USBD);
+ usbd_setup->wValue = nrf_usbd_setup_wvalue_get(NRF_USBD);
+ usbd_setup->wIndex = nrf_usbd_setup_windex_get(NRF_USBD);
+ usbd_setup->wLength = nrf_usbd_setup_wlength_get(NRF_USBD);
+ ep_ctx->buf.len = sizeof(struct usb_setup_packet);
+
+ /* Copy setup packet to driver internal structure */
+ memcpy(&usbd_ctx.setup, usbd_setup, sizeof(struct usb_setup_packet));
+
+ LOG_DBG("SETUP: bR:0x%02x bmRT:0x%02x wV:0x%04x wI:0x%04x wL:%d",
+ (uint32_t)usbd_setup->bRequest,
+ (uint32_t)usbd_setup->bmRequestType,
+ (uint32_t)usbd_setup->wValue,
+ (uint32_t)usbd_setup->wIndex,
+ (uint32_t)usbd_setup->wLength);
+
+ /* Inform the stack. */
+ ep_ctx->cfg.cb(ep_ctx->cfg.addr, USB_DC_EP_SETUP);
+
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+
+ if ((REQTYPE_GET_DIR(usbd_setup->bmRequestType)
+ == REQTYPE_DIR_TO_DEVICE)
+ && (usbd_setup->wLength)) {
+ ctx->ctrl_read_len = usbd_setup->wLength;
+ /* Allow data chunk on EP0 OUT */
+ nrfx_usbd_setup_data_clear();
+ } else {
+ ctx->ctrl_read_len = 0U;
+ }
+}
+
+static inline void usbd_work_process_recvreq(struct nrf_usbd_ctx *ctx,
+ struct nrf_usbd_ep_ctx *ep_ctx)
+{
+ if (!ep_ctx->read_pending) {
+ return;
+ }
+ if (!ep_ctx->read_complete) {
+ return;
+ }
+
+ ep_ctx->read_pending = false;
+ ep_ctx->read_complete = false;
+
+ k_mutex_lock(&ctx->drv_lock, K_FOREVER);
+ NRFX_USBD_TRANSFER_OUT(transfer, ep_ctx->buf.data,
+ ep_ctx->cfg.max_sz);
+ nrfx_err_t err = nrfx_usbd_ep_transfer(
+ ep_addr_to_nrfx(ep_ctx->cfg.addr), &transfer);
+ if (err != NRFX_SUCCESS) {
+ LOG_ERR("nRF USBD transfer error (OUT): 0x%02x", err);
+ }
+ k_mutex_unlock(&ctx->drv_lock);
+}
+
+
+static inline void usbd_work_process_ep_events(struct usbd_ep_event *ep_evt)
+{
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+ struct nrf_usbd_ep_ctx *ep_ctx = ep_evt->ep;
+
+ __ASSERT_NO_MSG(ep_ctx);
+
+ switch (ep_evt->evt_type) {
+ case EP_EVT_SETUP_RECV:
+ usbd_work_process_setup(ep_ctx);
+ break;
+
+ case EP_EVT_RECV_REQ:
+ usbd_work_process_recvreq(ctx, ep_ctx);
+ break;
+
+ case EP_EVT_RECV_COMPLETE:
+ ep_ctx->cfg.cb(ep_ctx->cfg.addr,
+ USB_DC_EP_DATA_OUT);
+ break;
+
+ case EP_EVT_WRITE_COMPLETE:
+ if (ep_ctx->cfg.type == USB_DC_EP_CONTROL &&
+ !ep_ctx->trans_zlp) {
+ /* Trigger the hardware to perform
+ * status stage, but only if there is
+ * no ZLP required.
+ */
+ k_mutex_lock(&ctx->drv_lock, K_FOREVER);
+ nrfx_usbd_setup_clear();
+ k_mutex_unlock(&ctx->drv_lock);
+ }
+ ep_ctx->cfg.cb(ep_ctx->cfg.addr,
+ USB_DC_EP_DATA_IN);
+ break;
+ default:
+ break;
+ }
+}
+
+static void usbd_event_transfer_ctrl(nrfx_usbd_evt_t const *const p_event)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx =
+ endpoint_ctx(p_event->data.eptransfer.ep);
+
+ if (NRF_USBD_EPIN_CHECK(p_event->data.eptransfer.ep)) {
+ switch (p_event->data.eptransfer.status) {
+ case NRFX_USBD_EP_OK: {
+ struct usbd_event *ev = usbd_evt_alloc();
+
+ if (!ev) {
+ return;
+ }
+
+ ep_ctx->write_in_progress = false;
+ ev->evt_type = USBD_EVT_EP;
+ ev->evt.ep_evt.evt_type = EP_EVT_WRITE_COMPLETE;
+ ev->evt.ep_evt.ep = ep_ctx;
+
+ LOG_DBG("ctrl write complete");
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ }
+ break;
+
+ default: {
+ LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
+ p_event->data.eptransfer.status,
+ p_event->data.eptransfer.ep);
+ }
+ break;
+ }
+ } else {
+ switch (p_event->data.eptransfer.status) {
+ case NRFX_USBD_EP_WAITING: {
+ struct usbd_event *ev = usbd_evt_alloc();
+
+ if (!ev) {
+ return;
+ }
+
+ LOG_DBG("ctrl read request");
+
+ ep_ctx->read_pending = true;
+ ev->evt_type = USBD_EVT_EP;
+ ev->evt.ep_evt.evt_type = EP_EVT_RECV_REQ;
+ ev->evt.ep_evt.ep = ep_ctx;
+
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ }
+ break;
+
+ case NRFX_USBD_EP_OK: {
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+ struct usbd_event *ev = usbd_evt_alloc();
+
+ if (!ev) {
+ return;
+ }
+ nrfx_usbd_ep_status_t err_code;
+
+ ev->evt_type = USBD_EVT_EP;
+ ev->evt.ep_evt.evt_type = EP_EVT_RECV_COMPLETE;
+ ev->evt.ep_evt.ep = ep_ctx;
+
+ err_code = nrfx_usbd_ep_status_get(
+ p_event->data.eptransfer.ep, &ep_ctx->buf.len);
+
+ if (err_code != NRFX_USBD_EP_OK) {
+ LOG_ERR("_ep_status_get failed! Code: %d",
+ err_code);
+ __ASSERT_NO_MSG(0);
+ }
+ LOG_DBG("ctrl read done: %d", ep_ctx->buf.len);
+
+ if (ctx->ctrl_read_len > ep_ctx->buf.len) {
+ ctx->ctrl_read_len -= ep_ctx->buf.len;
+ /* Allow next data chunk on EP0 OUT */
+ nrfx_usbd_setup_data_clear();
+ } else {
+ ctx->ctrl_read_len = 0U;
+ }
+
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ }
+ break;
+
+ default: {
+ LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
+ p_event->data.eptransfer.status,
+ p_event->data.eptransfer.ep);
+ }
+ break;
+ }
+ }
+}
+
+static void usbd_event_transfer_data(nrfx_usbd_evt_t const *const p_event)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx =
+ endpoint_ctx(p_event->data.eptransfer.ep);
+
+ if (NRF_USBD_EPIN_CHECK(p_event->data.eptransfer.ep)) {
+ switch (p_event->data.eptransfer.status) {
+ case NRFX_USBD_EP_OK: {
+ struct usbd_event *ev = usbd_evt_alloc();
+
+ if (!ev) {
+ return;
+ }
+
+ LOG_DBG("write complete, ep 0x%02x",
+ (uint32_t)p_event->data.eptransfer.ep);
+
+ ep_ctx->write_in_progress = false;
+ ev->evt_type = USBD_EVT_EP;
+ ev->evt.ep_evt.evt_type = EP_EVT_WRITE_COMPLETE;
+ ev->evt.ep_evt.ep = ep_ctx;
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ }
+ break;
+
+ case NRFX_USBD_EP_ABORTED: {
+ LOG_DBG("Endpoint 0x%02x write aborted",
+ p_event->data.eptransfer.ep);
+ }
+ break;
+
+ default: {
+ LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
+ p_event->data.eptransfer.status,
+ p_event->data.eptransfer.ep);
+ }
+ break;
+ }
+
+ } else {
+ switch (p_event->data.eptransfer.status) {
+ case NRFX_USBD_EP_WAITING: {
+ struct usbd_event *ev = usbd_evt_alloc();
+
+ if (!ev) {
+ return;
+ }
+
+ LOG_DBG("read request, ep 0x%02x",
+ (uint32_t)p_event->data.eptransfer.ep);
+
+ ep_ctx->read_pending = true;
+ ev->evt_type = USBD_EVT_EP;
+ ev->evt.ep_evt.evt_type = EP_EVT_RECV_REQ;
+ ev->evt.ep_evt.ep = ep_ctx;
+
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ }
+ break;
+
+ case NRFX_USBD_EP_OK: {
+ struct usbd_event *ev = usbd_evt_alloc();
+
+ if (!ev) {
+ return;
+ }
+
+ ep_ctx->buf.len = nrf_usbd_ep_amount_get(NRF_USBD,
+ p_event->data.eptransfer.ep);
+
+ LOG_DBG("read complete, ep 0x%02x, len %d",
+ (uint32_t)p_event->data.eptransfer.ep,
+ ep_ctx->buf.len);
+
+ ev->evt_type = USBD_EVT_EP;
+ ev->evt.ep_evt.evt_type = EP_EVT_RECV_COMPLETE;
+ ev->evt.ep_evt.ep = ep_ctx;
+
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ }
+ break;
+
+ default: {
+ LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
+ p_event->data.eptransfer.status,
+ p_event->data.eptransfer.ep);
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * @brief nRFx USBD driver event handler function.
+ */
+static void usbd_event_handler(nrfx_usbd_evt_t const *const p_event)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+ struct usbd_event evt = {0};
+ bool put_evt = false;
+
+ switch (p_event->type) {
+ case NRFX_USBD_EVT_SUSPEND:
+ LOG_DBG("SUSPEND state detected");
+ evt.evt_type = USBD_EVT_POWER;
+ evt.evt.pwr_evt.state = USBD_SUSPENDED;
+ put_evt = true;
+ break;
+ case NRFX_USBD_EVT_RESUME:
+ LOG_DBG("RESUMING from suspend");
+ evt.evt_type = USBD_EVT_POWER;
+ evt.evt.pwr_evt.state = USBD_RESUMED;
+ put_evt = true;
+ break;
+ case NRFX_USBD_EVT_WUREQ:
+ LOG_DBG("RemoteWU initiated");
+ evt.evt_type = USBD_EVT_POWER;
+ evt.evt.pwr_evt.state = USBD_RESUMED;
+ put_evt = true;
+ break;
+ case NRFX_USBD_EVT_RESET:
+ evt.evt_type = USBD_EVT_RESET;
+ put_evt = true;
+ break;
+ case NRFX_USBD_EVT_SOF:
+ if (IS_ENABLED(CONFIG_USB_DEVICE_SOF)) {
+ evt.evt_type = USBD_EVT_SOF;
+ put_evt = true;
+ }
+ break;
+
+ case NRFX_USBD_EVT_EPTRANSFER:
+ ep_ctx = endpoint_ctx(p_event->data.eptransfer.ep);
+ switch (ep_ctx->cfg.type) {
+ case USB_DC_EP_CONTROL:
+ usbd_event_transfer_ctrl(p_event);
+ break;
+ case USB_DC_EP_BULK:
+ case USB_DC_EP_INTERRUPT:
+ usbd_event_transfer_data(p_event);
+ break;
+ case USB_DC_EP_ISOCHRONOUS:
+ usbd_event_transfer_data(p_event);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case NRFX_USBD_EVT_SETUP: {
+ nrfx_usbd_setup_t drv_setup;
+
+ nrfx_usbd_setup_get(&drv_setup);
+ if ((drv_setup.bRequest != REQ_SET_ADDRESS)
+ || (REQTYPE_GET_TYPE(drv_setup.bmRequestType)
+ != REQTYPE_TYPE_STANDARD)) {
+ /* SetAddress is habdled by USBD hardware.
+ * No software action required.
+ */
+
+ struct nrf_usbd_ep_ctx *ep_ctx =
+ endpoint_ctx(NRF_USBD_EPOUT(0));
+
+ evt.evt_type = USBD_EVT_EP;
+ evt.evt.ep_evt.ep = ep_ctx;
+ evt.evt.ep_evt.evt_type = EP_EVT_SETUP_RECV;
+ put_evt = true;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (put_evt) {
+ struct usbd_event *ev;
+
+ ev = usbd_evt_alloc();
+ if (!ev) {
+ return;
+ }
+ ev->evt_type = evt.evt_type;
+ ev->evt = evt.evt;
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ }
+}
+
+static inline void usbd_reinit(void)
+{
+ int ret;
+ nrfx_err_t err;
+
+ nrfx_power_usbevt_disable();
+ nrfx_usbd_disable();
+ nrfx_usbd_uninit();
+
+ usbd_evt_flush();
+
+ ret = eps_ctx_init();
+ __ASSERT_NO_MSG(ret == 0);
+
+ nrfx_power_usbevt_enable();
+ err = nrfx_usbd_init(usbd_event_handler);
+
+ if (err != NRFX_SUCCESS) {
+ LOG_DBG("nRF USBD driver reinit failed. Code: %d", err);
+ __ASSERT_NO_MSG(0);
+ }
+}
+
+/**
+ * @brief funciton to generate fake receive request for
+ * ISO OUT EP.
+ *
+ * ISO OUT endpoint does not generate irq by itself and reading
+ * from ISO OUT ep is sunchronized with SOF frame. For more details
+ * refer to Nordic usbd specification.
+ */
+static void usbd_sof_trigger_iso_read(void)
+{
+ struct usbd_event *ev;
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ ep_ctx = endpoint_ctx(NRFX_USBD_EPOUT8);
+ if (!ep_ctx) {
+ LOG_ERR("There is no ISO ep");
+ return;
+ }
+
+ if (ep_ctx->cfg.en) {
+ /* Dissect receive request
+ * if the iso OUT ep is enabled
+ */
+ ep_ctx->read_pending = true;
+ ep_ctx->read_complete = true;
+ ev = usbd_evt_alloc();
+ if (!ev) {
+ LOG_ERR("Failed to alloc evt");
+ return;
+ }
+ ev->evt_type = USBD_EVT_EP;
+ ev->evt.ep_evt.evt_type = EP_EVT_RECV_REQ;
+ ev->evt.ep_evt.ep = ep_ctx;
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ } else {
+ LOG_DBG("Endpoint is not enabled");
+ }
+}
+
+/* Work handler */
+static void usbd_work_handler(struct k_work *item)
+{
+ struct nrf_usbd_ctx *ctx;
+ struct usbd_event *ev;
+
+ ctx = CONTAINER_OF(item, struct nrf_usbd_ctx, usb_work);
+
+ while ((ev = usbd_evt_get()) != NULL) {
+ if (!dev_ready() && ev->evt_type != USBD_EVT_POWER) {
+ /* Drop non-power events when cable is detached. */
+ usbd_evt_free(ev);
+ continue;
+ }
+
+ switch (ev->evt_type) {
+ case USBD_EVT_EP:
+ if (!ctx->attached) {
+ LOG_ERR("not attached, EP 0x%02x event dropped",
+ (uint32_t)ev->evt.ep_evt.ep->cfg.addr);
+ }
+ usbd_work_process_ep_events(&ev->evt.ep_evt);
+ break;
+ case USBD_EVT_POWER:
+ usbd_work_process_pwr_events(&ev->evt.pwr_evt);
+ break;
+ case USBD_EVT_RESET:
+ LOG_DBG("USBD reset event");
+ k_mutex_lock(&ctx->drv_lock, K_FOREVER);
+ eps_ctx_init();
+ k_mutex_unlock(&ctx->drv_lock);
+
+ if (ctx->status_cb) {
+ ctx->status_cb(USB_DC_RESET, NULL);
+ }
+ break;
+ case USBD_EVT_SOF:
+ usbd_sof_trigger_iso_read();
+
+ if (ctx->status_cb) {
+ ctx->status_cb(USB_DC_SOF, NULL);
+ }
+ break;
+ case USBD_EVT_REINIT: {
+ /*
+ * Reinitialize the peripheral after queue
+ * overflow.
+ */
+ LOG_ERR("USBD event queue full!");
+ usbd_reinit();
+ break;
+ }
+ default:
+ LOG_ERR("Unknown USBD event: %"PRId16, ev->evt_type);
+ break;
+ }
+ usbd_evt_free(ev);
+ }
+}
+
+int usb_dc_attach(void)
+{
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+ nrfx_err_t err;
+ int ret;
+
+ if (ctx->attached) {
+ return 0;
+ }
+
+ k_mutex_init(&ctx->drv_lock);
+ ctx->hfxo_mgr =
+ z_nrf_clock_control_get_onoff(
+ COND_CODE_1(NRF_CLOCK_HAS_HFCLK192M,
+ (CLOCK_CONTROL_NRF_SUBSYS_HF192M),
+ (CLOCK_CONTROL_NRF_SUBSYS_HF)));
+
+ IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
+ nrfx_isr, nrfx_usbd_irq_handler, 0);
+
+ err = nrfx_usbd_init(usbd_event_handler);
+
+ if (err != NRFX_SUCCESS) {
+ LOG_DBG("nRF USBD driver init failed. Code: %d", (uint32_t)err);
+ return -EIO;
+ }
+ nrfx_power_usbevt_enable();
+
+ ret = eps_ctx_init();
+ if (ret == 0) {
+ ctx->attached = true;
+ }
+
+ if (!k_fifo_is_empty(&usbd_evt_fifo)) {
+ usbd_work_schedule();
+ }
+
+ if (nrfx_power_usbstatus_get() != NRFX_POWER_USB_STATE_DISCONNECTED) {
+ /* USBDETECTED event is be generated on cable attachment and
+ * when cable is already attached during reset, but not when
+ * the peripheral is re-enabled.
+ * When USB-enabled bootloader is used, target application
+ * will not receive this event and it needs to be generated
+ * again here.
+ */
+ usb_dc_power_event_handler(NRFX_POWER_USB_EVT_DETECTED);
+ }
+
+ return ret;
+}
+
+int usb_dc_detach(void)
+{
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+
+ k_mutex_lock(&ctx->drv_lock, K_FOREVER);
+
+ usbd_evt_flush();
+ eps_ctx_uninit();
+
+ if (nrfx_usbd_is_enabled()) {
+ nrfx_usbd_disable();
+ }
+
+ if (nrfx_usbd_is_initialized()) {
+ nrfx_usbd_uninit();
+ }
+
+ (void)hfxo_stop(ctx);
+ nrfx_power_usbevt_disable();
+
+ ctx->attached = false;
+ k_mutex_unlock(&ctx->drv_lock);
+
+ return 0;
+}
+
+int usb_dc_reset(void)
+{
+ int ret;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ LOG_DBG("USBD Reset");
+
+ ret = usb_dc_detach();
+ if (ret) {
+ return ret;
+ }
+
+ ret = usb_dc_attach();
+ if (ret) {
+ return ret;
+ }
+
+ return 0;
+}
+
+int usb_dc_set_address(const uint8_t addr)
+{
+ struct nrf_usbd_ctx *ctx;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ /**
+ * Nothing to do here. The USBD HW already takes care of initiating
+ * STATUS stage. Just double check the address for sanity.
+ */
+ __ASSERT(addr == (uint8_t)NRF_USBD->USBADDR, "USB Address incorrect!");
+
+ ctx = get_usbd_ctx();
+
+ LOG_DBG("Address set to: %d", addr);
+
+ return 0;
+}
+
+
+int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data *const ep_cfg)
+{
+ uint8_t ep_idx = NRF_USBD_EP_NR_GET(ep_cfg->ep_addr);
+
+ LOG_DBG("ep 0x%02x, mps %d, type %d", ep_cfg->ep_addr, ep_cfg->ep_mps,
+ ep_cfg->ep_type);
+
+ if ((ep_cfg->ep_type == USB_DC_EP_CONTROL) && ep_idx) {
+ LOG_ERR("invalid endpoint configuration");
+ return -1;
+ }
+
+ if (!NRF_USBD_EP_VALIDATE(ep_cfg->ep_addr)) {
+ LOG_ERR("invalid endpoint index/address");
+ return -1;
+ }
+
+ if ((ep_cfg->ep_type == USB_DC_EP_ISOCHRONOUS) &&
+ (!NRF_USBD_EPISO_CHECK(ep_cfg->ep_addr))) {
+ LOG_WRN("invalid endpoint type");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data *const ep_cfg)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached()) {
+ return -ENODEV;
+ }
+
+ /**
+ * TODO:
+ * For ISO endpoints, application has to use EPIN/OUT 8
+ * but right now there's no standard way of knowing the
+ * ISOIN/ISOOUT endpoint number in advance to configure
+ * accordingly. So either this needs to be chosen in the
+ * menuconfig in application area or perhaps in device tree
+ * at compile time or introduce a new API to read the endpoint
+ * configuration at runtime before configuring them.
+ */
+ ep_ctx = endpoint_ctx(ep_cfg->ep_addr);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ ep_ctx->cfg.addr = ep_cfg->ep_addr;
+ ep_ctx->cfg.type = ep_cfg->ep_type;
+ ep_ctx->cfg.max_sz = ep_cfg->ep_mps;
+
+ if (!NRF_USBD_EPISO_CHECK(ep_cfg->ep_addr)) {
+ if ((ep_cfg->ep_mps & (ep_cfg->ep_mps - 1)) != 0U) {
+ LOG_ERR("EP max packet size must be a power of 2");
+ return -EINVAL;
+ }
+ }
+
+ nrfx_usbd_ep_max_packet_size_set(ep_addr_to_nrfx(ep_cfg->ep_addr),
+ ep_cfg->ep_mps);
+
+ return 0;
+}
+
+int usb_dc_ep_set_stall(const uint8_t ep)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ switch (ep_ctx->cfg.type) {
+ case USB_DC_EP_CONTROL:
+ nrfx_usbd_setup_stall();
+ break;
+ case USB_DC_EP_BULK:
+ case USB_DC_EP_INTERRUPT:
+ nrfx_usbd_ep_stall(ep_addr_to_nrfx(ep));
+ break;
+ case USB_DC_EP_ISOCHRONOUS:
+ LOG_ERR("STALL unsupported on ISO endpoint");
+ return -EINVAL;
+ }
+
+ ep_ctx->buf.len = 0U;
+ ep_ctx->buf.curr = ep_ctx->buf.data;
+
+ LOG_DBG("STALL on EP 0x%02x", ep);
+
+ return 0;
+}
+
+int usb_dc_ep_clear_stall(const uint8_t ep)
+{
+
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ if (NRF_USBD_EPISO_CHECK(ep)) {
+ /* ISO transactions do not support a handshake phase. */
+ return -EINVAL;
+ }
+
+ nrfx_usbd_ep_dtoggle_clear(ep_addr_to_nrfx(ep));
+ nrfx_usbd_ep_stall_clear(ep_addr_to_nrfx(ep));
+ LOG_DBG("Unstall on EP 0x%02x", ep);
+
+ return 0;
+}
+
+int usb_dc_ep_halt(const uint8_t ep)
+{
+ return usb_dc_ep_set_stall(ep);
+}
+
+int usb_dc_ep_is_stalled(const uint8_t ep, uint8_t *const stalled)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ if (!stalled) {
+ return -EINVAL;
+ }
+
+ *stalled = (uint8_t) nrfx_usbd_ep_stall_check(ep_addr_to_nrfx(ep));
+
+ return 0;
+}
+
+int usb_dc_ep_enable(const uint8_t ep)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached()) {
+ return -ENODEV;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ if (!NRF_USBD_EPISO_CHECK(ep)) {
+ /* ISO transactions for full-speed device do not support
+ * toggle sequencing and should only send DATA0 PID.
+ */
+ nrfx_usbd_ep_dtoggle_clear(ep_addr_to_nrfx(ep));
+ /** Endpoint is enabled on SetInterface request.
+ * This should also clear EP's halt status.
+ */
+ nrfx_usbd_ep_stall_clear(ep_addr_to_nrfx(ep));
+ }
+ if (ep_ctx->cfg.en) {
+ return -EALREADY;
+ }
+
+ LOG_DBG("EP enable: 0x%02x", ep);
+
+ ep_ctx->cfg.en = true;
+
+ /* Defer the endpoint enable if USBD is not ready yet. */
+ if (dev_ready()) {
+ nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep));
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_disable(const uint8_t ep)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ if (!ep_ctx->cfg.en) {
+ return -EALREADY;
+ }
+
+ LOG_DBG("EP disable: 0x%02x", ep);
+
+ nrfx_usbd_ep_disable(ep_addr_to_nrfx(ep));
+ ep_ctx->cfg.en = false;
+
+ return 0;
+}
+
+int usb_dc_ep_flush(const uint8_t ep)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ ep_ctx->buf.len = 0U;
+ ep_ctx->buf.curr = ep_ctx->buf.data;
+
+ nrfx_usbd_transfer_out_drop(ep_addr_to_nrfx(ep));
+
+ return 0;
+}
+
+int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data,
+ const uint32_t data_len, uint32_t *const ret_bytes)
+{
+ LOG_DBG("ep_write: ep 0x%02x, len %d", ep, data_len);
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+ struct nrf_usbd_ep_ctx *ep_ctx;
+ int result = 0;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ if (NRF_USBD_EPOUT_CHECK(ep)) {
+ return -EINVAL;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ if (!ep_ctx->cfg.en) {
+ LOG_ERR("Endpoint 0x%02x is not enabled", ep);
+ return -EINVAL;
+ }
+
+ k_mutex_lock(&ctx->drv_lock, K_FOREVER);
+
+ /* USBD driver does not allow scheduling multiple DMA transfers
+ * for one EP at a time. Next USB transfer on this endpoint can be
+ * triggered after the completion of previous one.
+ */
+ if (ep_ctx->write_in_progress) {
+ k_mutex_unlock(&ctx->drv_lock);
+ return -EAGAIN;
+ }
+
+ /** Clear the ZLP flag if current write is ZLP. After the ZLP will be
+ * send the driver will perform status stage.
+ */
+ if (!data_len && ep_ctx->trans_zlp) {
+ ep_ctx->trans_zlp = false;
+ }
+
+ /** If writing to a Control Endpoint there might be a need to transfer
+ * ZLP. If the Hosts asks for more data that the device may return and
+ * the last packet is wMaxPacketSize long. The driver must send ZLP.
+ * For consistance with the Zephyr USB stack sending ZLP must be issued
+ * from the stack level. Making trans_zlp flag true results in blocking
+ * the driver from starting setup stage without required ZLP.
+ */
+ if (ep_ctx->cfg.type == USB_DC_EP_CONTROL) {
+ if (data_len && usbd_ctx.setup.wLength > data_len &&
+ !(data_len % ep_ctx->cfg.max_sz)) {
+ ep_ctx->trans_zlp = true;
+ }
+ }
+
+ /* Setup stage is handled by hardware.
+ * Detect the setup stage initiated by the stack
+ * and perform appropriate action.
+ */
+ if ((ep_ctx->cfg.type == USB_DC_EP_CONTROL)
+ && (nrfx_usbd_last_setup_dir_get() != ep)) {
+ nrfx_usbd_setup_clear();
+ k_mutex_unlock(&ctx->drv_lock);
+ return 0;
+ }
+
+ ep_ctx->write_in_progress = true;
+ NRFX_USBD_TRANSFER_IN(transfer, data, data_len, 0);
+ nrfx_err_t err = nrfx_usbd_ep_transfer(ep_addr_to_nrfx(ep), &transfer);
+
+ if (err != NRFX_SUCCESS) {
+ ep_ctx->write_in_progress = false;
+ if (ret_bytes) {
+ *ret_bytes = 0;
+ }
+ result = -EIO;
+ LOG_ERR("nRF USBD write error: %d", (uint32_t)err);
+ } else {
+ if (ret_bytes) {
+ *ret_bytes = data_len;
+ }
+ }
+
+ k_mutex_unlock(&ctx->drv_lock);
+ return result;
+}
+
+int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
+ uint32_t *read_bytes)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+ uint32_t bytes_to_copy;
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ if (NRF_USBD_EPIN_CHECK(ep)) {
+ return -EINVAL;
+ }
+
+ if (!data && max_data_len) {
+ return -EINVAL;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ if (!ep_ctx->cfg.en) {
+ LOG_ERR("Endpoint 0x%02x is not enabled", ep);
+ return -EINVAL;
+ }
+
+ k_mutex_lock(&ctx->drv_lock, K_FOREVER);
+
+ bytes_to_copy = MIN(max_data_len, ep_ctx->buf.len);
+
+ if (!data && !max_data_len) {
+ if (read_bytes) {
+ *read_bytes = ep_ctx->buf.len;
+ }
+ k_mutex_unlock(&ctx->drv_lock);
+ return 0;
+ }
+
+ memcpy(data, ep_ctx->buf.curr, bytes_to_copy);
+
+ ep_ctx->buf.curr += bytes_to_copy;
+ ep_ctx->buf.len -= bytes_to_copy;
+ if (read_bytes) {
+ *read_bytes = bytes_to_copy;
+ }
+
+ k_mutex_unlock(&ctx->drv_lock);
+ return 0;
+}
+
+int usb_dc_ep_read_continue(uint8_t ep)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+
+ if (!dev_attached() || !dev_ready()) {
+ return -ENODEV;
+ }
+
+ if (NRF_USBD_EPIN_CHECK(ep)) {
+ return -EINVAL;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ if (!ep_ctx->cfg.en) {
+ LOG_ERR("Endpoint 0x%02x is not enabled", ep);
+ return -EINVAL;
+ }
+
+ k_mutex_lock(&ctx->drv_lock, K_FOREVER);
+ if (!ep_ctx->buf.len) {
+ ep_ctx->buf.curr = ep_ctx->buf.data;
+ ep_ctx->read_complete = true;
+
+ if (ep_ctx->read_pending) {
+ struct usbd_event *ev = usbd_evt_alloc();
+
+ if (!ev) {
+ k_mutex_unlock(&ctx->drv_lock);
+ return -ENOMEM;
+ }
+
+ ev->evt_type = USBD_EVT_EP;
+ ev->evt.ep_evt.ep = ep_ctx;
+ ev->evt.ep_evt.evt_type = EP_EVT_RECV_REQ;
+ usbd_evt_put(ev);
+ usbd_work_schedule();
+ }
+ }
+ k_mutex_unlock(&ctx->drv_lock);
+
+ return 0;
+}
+
+int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
+ const uint32_t max_data_len, uint32_t *const read_bytes)
+{
+ LOG_DBG("ep_read: ep 0x%02x, maxlen %d", ep, max_data_len);
+ int ret;
+
+ ret = usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes);
+ if (ret) {
+ return ret;
+ }
+
+ if (!data && !max_data_len) {
+ return ret;
+ }
+
+ ret = usb_dc_ep_read_continue(ep);
+ return ret;
+}
+
+int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached()) {
+ return -ENODEV;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ ep_ctx->cfg.cb = cb;
+
+ return 0;
+}
+
+void usb_dc_set_status_callback(const usb_dc_status_callback cb)
+{
+ get_usbd_ctx()->status_cb = cb;
+}
+
+int usb_dc_ep_mps(const uint8_t ep)
+{
+ struct nrf_usbd_ep_ctx *ep_ctx;
+
+ if (!dev_attached()) {
+ return -ENODEV;
+ }
+
+ ep_ctx = endpoint_ctx(ep);
+ if (!ep_ctx) {
+ return -EINVAL;
+ }
+
+ return ep_ctx->cfg.max_sz;
+}
+
+int usb_dc_wakeup_request(void)
+{
+ bool res = nrfx_usbd_wakeup_req();
+
+ if (!res) {
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static int usb_init(const struct device *arg)
+{
+ struct nrf_usbd_ctx *ctx = get_usbd_ctx();
+
+#ifdef CONFIG_HAS_HW_NRF_USBREG
+ /* Use CLOCK/POWER priority for compatibility with other series where
+ * USB events are handled by CLOCK interrupt handler.
+ */
+ IRQ_CONNECT(USBREGULATOR_IRQn,
+ DT_IRQ(DT_INST(0, nordic_nrf_clock), priority),
+ nrfx_isr, nrfx_usbreg_irq_handler, 0);
+ irq_enable(USBREGULATOR_IRQn);
+#endif
+
+ static const nrfx_power_config_t power_config = {
+ .dcdcen = IS_ENABLED(CONFIG_SOC_DCDC_NRF52X) ||
+ IS_ENABLED(CONFIG_SOC_DCDC_NRF53X_APP),
+#if NRFX_POWER_SUPPORTS_DCDCEN_VDDH
+ .dcdcenhv = IS_ENABLED(CONFIG_SOC_DCDC_NRF53X_HV),
+#endif
+ };
+
+ static const nrfx_power_usbevt_config_t usbevt_config = {
+ .handler = usb_dc_power_event_handler
+ };
+
+ /* Ignore the return value, as NRFX_ERROR_ALREADY_INITIALIZED is not
+ * a problem here.
+ */
+ (void)nrfx_power_init(&power_config);
+ nrfx_power_usbevt_init(&usbevt_config);
+
+ k_work_q_start(&usbd_work_queue,
+ usbd_work_queue_stack,
+ K_KERNEL_STACK_SIZEOF(usbd_work_queue_stack),
+ CONFIG_SYSTEM_WORKQUEUE_PRIORITY);
+
+ k_work_init(&ctx->usb_work, usbd_work_handler);
+
+ return 0;
+}
+
+SYS_INIT(usb_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
diff --git a/port/nuvoton/m48ssidae/usbd_m480.c b/port/nuvoton/m48ssidae/usbd_m480.c
new file mode 100644
index 00000000..a4ec824a
--- /dev/null
+++ b/port/nuvoton/m48ssidae/usbd_m480.c
@@ -0,0 +1,835 @@
+/*
+ * Copyright (c) 2004-2016 ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <rl_usb.h>
+#include "NuMicro.h"
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+/* Bit definition of CEPCTL register */
+#define HSUSBD_CEPCTL_NAKCLR ((uint32_t)0x00000000UL)
+#define HSUSBD_CEPCTL_STALL ((uint32_t)0x00000002UL)
+#define HSUSBD_CEPCTL_ZEROLEN ((uint32_t)0x00000004UL)
+#define HSUSBD_CEPCTL_FLUSH ((uint32_t)0x00000008UL)
+
+/* Bit definition of EPxRSPCTL register */
+#define HSUSBD_EP_RSPCTL_FLUSH ((uint32_t)0x00000001UL)
+#define HSUSBD_EP_RSPCTL_MODE_AUTO ((uint32_t)0x00000000UL)
+#define HSUSBD_EP_RSPCTL_MODE_MANUAL ((uint32_t)0x00000002UL)
+#define HSUSBD_EP_RSPCTL_MODE_FLY ((uint32_t)0x00000004UL)
+#define HSUSBD_EP_RSPCTL_MODE_MASK ((uint32_t)0x00000006UL)
+#define HSUSBD_EP_RSPCTL_TOGGLE ((uint32_t)0x00000008UL)
+#define HSUSBD_EP_RSPCTL_HALT ((uint32_t)0x00000010UL)
+#define HSUSBD_EP_RSPCTL_ZEROLEN ((uint32_t)0x00000020UL)
+#define HSUSBD_EP_RSPCTL_SHORTTXEN ((uint32_t)0x00000040UL)
+#define HSUSBD_EP_RSPCTL_DISBUF ((uint32_t)0x00000080UL)
+
+/* Bit definition of EPxCFG register */
+#define HSUSBD_EP_CFG_VALID ((uint32_t)0x00000001UL)
+#define HSUSBD_EP_CFG_TYPE_BULK ((uint32_t)0x00000002UL)
+#define HSUSBD_EP_CFG_TYPE_INT ((uint32_t)0x00000004UL)
+#define HSUSBD_EP_CFG_TYPE_ISO ((uint32_t)0x00000006UL)
+#define HSUSBD_EP_CFG_TYPE_MASK ((uint32_t)0x00000006UL)
+#define HSUSBD_EP_CFG_DIR_OUT ((uint32_t)0x00000000UL)
+#define HSUSBD_EP_CFG_DIR_IN ((uint32_t)0x00000008UL)
+
+#define HSUSBD_ENABLE_USB() ((uint32_t)(HSUSBD->PHYCTL |= (HSUSBD_PHYCTL_PHYEN_Msk|HSUSBD_PHYCTL_DPPUEN_Msk)))
+#define HSUSBD_DISABLE_USB() ((uint32_t)(HSUSBD->PHYCTL &= ~HSUSBD_PHYCTL_DPPUEN_Msk))
+#define HSUSBD_ENABLE_PHY() ((uint32_t)(HSUSBD->PHYCTL |= HSUSBD_PHYCTL_PHYEN_Msk))
+#define HSUSBD_DISABLE_PHY() ((uint32_t)(HSUSBD->PHYCTL &= ~HSUSBD_PHYCTL_PHYEN_Msk))
+#define HSUSBD_SET_SE0() ((uint32_t)(HSUSBD->PHYCTL &= ~HSUSBD_PHYCTL_DPPUEN_Msk))
+#define HSUSBD_CLR_SE0() ((uint32_t)(HSUSBD->PHYCTL |= HSUSBD_PHYCTL_DPPUEN_Msk))
+#define HSUSBD_SET_ADDR(addr) (HSUSBD->FADDR = (addr))
+#define HSUSBD_GET_ADDR() ((uint32_t)(HSUSBD->FADDR))
+#define HSUSBD_ENABLE_USB_INT(intr) (HSUSBD->GINTEN = (intr))
+#define HSUSBD_ENABLE_BUS_INT(intr) (HSUSBD->BUSINTEN = (intr))
+#define HSUSBD_GET_BUS_INT_FLAG() (HSUSBD->BUSINTSTS)
+#define HSUSBD_CLR_BUS_INT_FLAG(flag) (HSUSBD->BUSINTSTS = (flag))
+#define HSUSBD_ENABLE_CEP_INT(intr) (HSUSBD->CEPINTEN = (intr))
+#define HSUSBD_CLR_CEP_INT_FLAG(flag) (HSUSBD->CEPINTSTS = (flag))
+#define HSUSBD_SET_CEP_STATE(flag) (HSUSBD->CEPCTL = (flag))
+#define HSUSBD_START_CEP_IN(size) (HSUSBD->CEPTXCNT = (size))
+#define HSUSBD_SET_MAX_PAYLOAD(ep, size) (HSUSBD->EP[(ep)].EPMPS = (size))
+#define HSUSBD_ENABLE_EP_INT(ep, intr) (HSUSBD->EP[(ep)].EPINTEN = (intr))
+#define HSUSBD_GET_EP_INT_FLAG(ep) (HSUSBD->EP[(ep)].EPINTSTS)
+#define HSUSBD_CLR_EP_INT_FLAG(ep, flag) (HSUSBD->EP[(ep)].EPINTSTS = (flag))
+#define HSUSBD_SET_DMA_LEN(len) (HSUSBD->DMACNT = (len))
+#define HSUSBD_SET_DMA_ADDR(addr) (HSUSBD->DMAADDR = (addr))
+#define HSUSBD_SET_DMA_READ(epnum) (HSUSBD->DMACTL = (HSUSBD->DMACTL & ~HSUSBD_DMACTL_EPNUM_Msk) | HSUSBD_DMACTL_DMARD_Msk | (epnum) | 0x100)
+#define HSUSBD_SET_DMA_WRITE(epnum) (HSUSBD->DMACTL = (HSUSBD->DMACTL & ~(HSUSBD_DMACTL_EPNUM_Msk | HSUSBD_DMACTL_DMARD_Msk | 0x100)) | (epnum))
+#define HSUSBD_ENABLE_DMA() (HSUSBD->DMACTL |= HSUSBD_DMACTL_DMAEN_Msk)
+#define HSUSBD_IS_ATTACHED() ((uint32_t)(HSUSBD->PHYCTL & HSUSBD_PHYCTL_VBUSDET_Msk))
+
+#define HSUSBD_MAX_EP 12UL
+#define CEP 0xFFUL
+#define EPA 0UL
+#define EPB 1UL
+#define EPC 2UL
+#define EPD 3UL
+#define EPE 4UL
+#define EPF 5UL
+#define EPG 6UL
+#define EPH 7UL
+#define EPI 8UL
+#define EPJ 9UL
+#define EPK 10UL
+#define EPL 11UL
+
+#define USBD_EP_TO_NUM(ep) ((ep == CEP) ? (0) : (1 + (ep - EPA)))
+#define USBD_NUM_TO_EP(num) ((num == 0) ? (CEP) : (EPA + (num - 1)))
+
+#define CEP_BUF_BASE 0
+#define CEP_BUF_LEN USBD_MAX_PACKET0
+
+static uint32_t g_u32FreeBufAddr;
+static uint8_t g_u8StatusIn;
+
+/*
+ * USB Device Interrupt enable
+ * Called by USBD_Init to enable the USB Interrupt
+ * Return Value: None
+ */
+
+#ifdef __RTX
+void __svc(1) USBD_IntrEna(void);
+void __SVC_1(void)
+{
+#else
+void USBD_IntrEna(void)
+{
+#endif
+ NVIC_EnableIRQ(USBD20_IRQn);
+}
+
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB
+ * Return Value: None
+ */
+
+void USBD_Init(void)
+{
+ uint32_t volatile i;
+ /* Initial USB engine */
+ HSUSBD_ENABLE_PHY();
+
+ /* wait PHY clock ready */
+ while (1) {
+ HSUSBD->EP[EPA].EPMPS = 0x20UL;
+
+ if (HSUSBD->EP[EPA].EPMPS == 0x20UL) {
+ break;
+ }
+ }
+
+ for (i = 0; i < 0x10000; i++);
+
+ if (HSUSBD->OPER & HSUSBD_OPER_CURSPD_Msk) {
+ USBD_HighSpeed = __TRUE;
+ } else {
+ USBD_HighSpeed = __FALSE;
+ }
+
+ /* Enable USB BUS, CEP global interrupt */
+ HSUSBD_ENABLE_USB_INT(HSUSBD_GINTEN_USBIEN_Msk | HSUSBD_GINTEN_CEPIEN_Msk);
+ /* Enable BUS interrupt */
+ HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_RESUMEIEN_Msk | HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_VBUSDETIEN_Msk | HSUSBD_BUSINTEN_SOFIEN_Msk);
+ /* Reset Address to 0 */
+ HSUSBD_SET_ADDR(0);
+ /* Control endpoint */
+ HSUSBD->CEPBUFST = CEP_BUF_BASE;
+ HSUSBD->CEPBUFEND = CEP_BUF_BASE + CEP_BUF_LEN - 1UL;
+ HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_SETUPPKIEN_Msk | HSUSBD_CEPINTEN_STSDONEIEN_Msk | HSUSBD_CEPINTEN_RXPKIEN_Msk | HSUSBD_CEPINTEN_TXPKIEN_Msk);
+ USBD_IntrEna();
+}
+
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+
+void USBD_Connect(BOOL con)
+{
+ if (con) {
+ HSUSBD_CLR_SE0();
+ } else {
+ HSUSBD_SET_SE0();
+ }
+}
+
+
+/*
+ * USB Device Reset Function
+ * Called automatically on USB Device Reset
+ * Return Value: None
+ */
+
+void USBD_Reset(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < HSUSBD_MAX_EP; i++) {
+ HSUSBD->EP[EPA + i].EPRSPCTL = HSUSBD_EPRSPCTL_FLUSH_Msk;
+ }
+
+ if (HSUSBD->OPER & HSUSBD_OPER_CURSPD_Msk) {
+ USBD_HighSpeed = __TRUE;
+ } else {
+ USBD_HighSpeed = __FALSE;
+ }
+
+ g_u32FreeBufAddr = CEP_BUF_BASE + CEP_BUF_LEN;
+ g_u8StatusIn = 0;
+ HSUSBD_SET_ADDR(0);
+}
+
+
+/*
+ * USB Device Suspend Function
+ * Called automatically on USB Device Suspend
+ * Return Value: None
+ */
+
+void USBD_Suspend(void)
+{
+}
+
+
+/*
+ * USB Device Resume Function
+ * Called automatically on USB Device Resume
+ * Return Value: None
+ */
+
+void USBD_Resume(void)
+{
+}
+
+
+/*
+ * USB Device Remote Wakeup Function
+ * Called automatically on USB Device Remote Wakeup
+ * Return Value: None
+ */
+
+void USBD_WakeUp(void)
+{
+}
+
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+
+void USBD_WakeUpCfg(BOOL cfg)
+{
+}
+
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * setup: Called in setup stage (!=0), else after status stage
+ * Return Value: None
+ */
+
+void USBD_SetAddress(U32 adr, U32 setup)
+{
+ if (setup) {
+ return;
+ }
+
+ HSUSBD_SET_ADDR(adr);
+}
+
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+
+void USBD_Configure(BOOL cfg)
+{
+ if (cfg == __FALSE) {
+ g_u32FreeBufAddr = CEP_BUF_BASE + CEP_BUF_LEN;
+ }
+}
+
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+
+void USBD_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ uint32_t u32Num, u32Ep, u32Size, u32Type, u32Dir;
+ u32Num = pEPD->bEndpointAddress & 0x0F;
+ u32Ep = USBD_NUM_TO_EP(u32Num);
+ u32Size = pEPD->wMaxPacketSize;
+
+ switch (pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK) {
+ case USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ u32Type = HSUSBD_EP_CFG_TYPE_ISO;
+ break;
+
+ case USB_ENDPOINT_TYPE_BULK:
+ u32Type = HSUSBD_EP_CFG_TYPE_BULK;
+ break;
+
+ case USB_ENDPOINT_TYPE_INTERRUPT:
+ u32Type = HSUSBD_EP_CFG_TYPE_INT;
+ break;
+ }
+
+ if (pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) {
+ u32Dir = HSUSBD_EP_CFG_DIR_IN;
+ } else {
+ u32Dir = HSUSBD_EP_CFG_DIR_OUT;
+ }
+
+ HSUSBD->EP[u32Ep].EPBUFST = g_u32FreeBufAddr;
+ HSUSBD->EP[u32Ep].EPBUFEND = g_u32FreeBufAddr + u32Size - 1UL;
+ HSUSBD_SET_MAX_PAYLOAD(u32Ep, u32Size);
+ HSUSBD->EP[u32Ep].EPRSPCTL = (HSUSBD_EP_RSPCTL_FLUSH | HSUSBD_EP_RSPCTL_MODE_AUTO);
+ HSUSBD->EP[u32Ep].EPCFG = (u32Type | u32Dir | HSUSBD_EP_CFG_VALID | (u32Num << 4));
+ g_u32FreeBufAddr += u32Size;
+}
+
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+
+void USBD_DirCtrlEP(U32 dir)
+{
+}
+
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_EnableEP(U32 EPNum)
+{
+ uint32_t u32Ep, u32Num, u32Intr;
+ u32Num = EPNum & 0x0F;
+ u32Ep = USBD_NUM_TO_EP(u32Num);
+ HSUSBD->GINTEN |= (0x1UL << (HSUSBD_GINTEN_EPAIEN_Pos + (u32Num - USBD_EP_TO_NUM(EPA))));
+
+ if (EPNum & 0x80) {
+ u32Intr = HSUSBD_EPINTEN_TXPKIEN_Msk;
+ } else {
+ u32Intr = HSUSBD_EPINTEN_RXPKIEN_Msk | HSUSBD_EPINTEN_SHORTRXIEN_Msk | HSUSBD_EPINTEN_BUFFULLIEN_Msk;
+ }
+
+ HSUSBD_ENABLE_EP_INT(u32Ep, u32Intr);
+}
+
+
+/*
+ * Disable USB Endpoint
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_DisableEP(U32 EPNum)
+{
+ uint32_t u32Ep, u32Num;
+ u32Num = EPNum & 0x0F;
+ u32Ep = USBD_NUM_TO_EP(u32Num);
+ HSUSBD->GINTEN &= ~(0x1UL << (HSUSBD_GINTEN_EPAIEN_Pos + (u32Num - USBD_EP_TO_NUM(EPA))));
+ HSUSBD_ENABLE_EP_INT(u32Ep, 0);
+}
+
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ResetEP(U32 EPNum)
+{
+ uint32_t u32Ep, u32Num;
+ u32Num = EPNum & 0x0F;
+ u32Ep = USBD_NUM_TO_EP(u32Num);
+ HSUSBD->EP[u32Ep].EPRSPCTL = (HSUSBD->EP[u32Ep].EPRSPCTL & HSUSBD_EP_RSPCTL_MODE_MASK) | HSUSBD_EPRSPCTL_FLUSH_Msk;
+}
+
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_SetStallEP(U32 EPNum)
+{
+ uint32_t u32Ep, u32Num;
+ u32Num = EPNum & 0x0F;
+ u32Ep = USBD_NUM_TO_EP(u32Num);
+
+ if (u32Ep == CEP) {
+ HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_STALL);
+ } else {
+ HSUSBD->EP[u32Ep].EPRSPCTL = (HSUSBD->EP[u32Ep].EPRSPCTL & 0xF7UL) | HSUSBD_EP_RSPCTL_HALT;
+ }
+}
+
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClrStallEP(U32 EPNum)
+{
+ uint32_t u32Ep, u32Num;
+ u32Num = EPNum & 0x0F;
+ u32Ep = USBD_NUM_TO_EP(u32Num);
+
+ if (u32Ep == CEP) {
+ HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_NAKCLR_Msk);
+ } else {
+ HSUSBD->EP[u32Ep].EPRSPCTL = (HSUSBD->EP[u32Ep].EPRSPCTL & HSUSBD_EP_RSPCTL_MODE_MASK) | HSUSBD_EP_RSPCTL_TOGGLE;
+ }
+}
+
+
+/*
+ * Clear USB Device Endpoint Buffer
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClearEPBuf(U32 EPNum)
+{
+}
+
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+
+U32 USBD_ReadEP(U32 EPNum, U8 *pData, U32 bufsz)
+{
+ uint32_t u32Ep, u32Num, u32Len, i;
+ u32Num = EPNum & 0x0F;
+ u32Ep = USBD_NUM_TO_EP(u32Num);
+
+ if (u32Num == 0) {
+ if (pData == (uint8_t *)&USBD_SetupPacket) {
+ *((uint16_t *)(pData + 0)) = (uint16_t)(HSUSBD->SETUP1_0 & 0xFFFFUL);
+ *((uint16_t *)(pData + 2)) = (uint16_t)(HSUSBD->SETUP3_2 & 0xFFFFUL);
+ *((uint16_t *)(pData + 4)) = (uint16_t)(HSUSBD->SETUP5_4 & 0xFFFFUL);
+ *((uint16_t *)(pData + 6)) = (uint16_t)(HSUSBD->SETUP7_6 & 0xFFFFUL);
+ return 8;
+ } else {
+ u32Len = HSUSBD->CEPDATCNT & 0xFFFFUL;
+
+ if (u32Len > bufsz) {
+ u32Len = bufsz;
+ }
+
+ for (i = 0; i < bufsz; i++) {
+ pData[i] = inpb(&HSUSBD->CEPDAT);
+ }
+
+ return u32Len;
+ }
+ } else {
+ u32Len = HSUSBD->EP[u32Ep].EPDATCNT & 0xFFFFUL;
+
+ if (u32Len > bufsz) {
+ u32Len = bufsz;
+ }
+
+ for (i = 0; i < u32Len; i++) {
+ pData[i] = HSUSBD->EP[u32Ep].EPDAT_BYTE;
+ }
+
+ return u32Len;
+ }
+}
+
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+
+U32 USBD_WriteEP(U32 EPNum, U8 *pData, U32 cnt)
+{
+ uint32_t u32Ep, u32Num, i;
+ u32Num = EPNum & 0x0F;
+ u32Ep = USBD_NUM_TO_EP(u32Num);
+
+ if (u32Num == 0) {
+ if (pData != NULL) {
+ if (cnt > 0) {
+ for (i = 0; i < cnt; i++) {
+ HSUSBD->CEPDAT_BYTE = pData[i];
+ }
+
+ HSUSBD_START_CEP_IN(cnt);
+ } else {
+ HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_ZEROLEN);
+ }
+ } else if (cnt == 0) {
+ g_u8StatusIn = 1;
+ HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
+ HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_NAKCLR);
+ }
+
+ return cnt;
+ } else {
+ if (cnt > 0) {
+ for (i = 0; i < cnt; i++) {
+ HSUSBD->EP[u32Ep].EPDAT_BYTE = pData[i];
+ }
+
+ HSUSBD->EP[u32Ep].EPRSPCTL = (HSUSBD->EP[u32Ep].EPRSPCTL & HSUSBD_EP_RSPCTL_HALT) | HSUSBD_EP_RSPCTL_SHORTTXEN;
+ } else {
+ HSUSBD->EP[u32Ep].EPRSPCTL = (HSUSBD->EP[u32Ep].EPRSPCTL & HSUSBD_EP_RSPCTL_HALT) | HSUSBD_EP_RSPCTL_ZEROLEN;
+ }
+
+ return cnt;
+ }
+}
+
+
+/*
+ * Get USB Device Last Frame Number
+ * Parameters: None
+ * Return Value: Frame Number
+ */
+
+U32 USBD_GetFrame(void)
+{
+ return 0;
+}
+
+
+#ifdef __RTX
+U32 LastError;
+
+/*
+ * Get USB Last Error Code
+ * Parameters: None
+ * Return Value: Error Code
+ */
+
+U32 USBD_GetError(void)
+{
+ return (LastError);
+}
+#endif
+
+
+/*
+ * USB Device Interrupt Service Routine
+ */
+
+void USBD20_IRQHandler(void)
+{
+ NVIC_DisableIRQ(USBD20_IRQn);
+ USBD_SignalHandler();
+}
+
+void USBD_Handler_Main()
+{
+ __IO uint32_t IrqStL, IrqSt;
+ uint32_t u32Ep, u32Num, i;
+ IrqStL = HSUSBD->GINTSTS & HSUSBD->GINTEN;
+
+ if (!IrqStL) {
+ return;
+ }
+
+ if (IrqStL & HSUSBD_GINTSTS_USBIF_Msk) {
+ IrqSt = HSUSBD->BUSINTSTS & HSUSBD->BUSINTEN;
+
+ if (IrqSt & HSUSBD_BUSINTSTS_SOFIF_Msk) {
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SOF, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+
+#endif
+ HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_SOFIF_Msk);
+ }
+
+ if (IrqSt & HSUSBD_BUSINTSTS_RSTIF_Msk) {
+ USBD_Reset();
+ usbd_reset_core();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Reset_Event) {
+ USBD_P_Reset_Event();
+ }
+
+#endif
+ HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_RESUMEIEN_Msk | HSUSBD_BUSINTEN_SUSPENDIEN_Msk | HSUSBD_BUSINTEN_SOFIEN_Msk);
+ HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_RSTIF_Msk);
+ HSUSBD_CLR_CEP_INT_FLAG(0x1FFC);
+ }
+
+ if (IrqSt & HSUSBD_BUSINTSTS_RESUMEIF_Msk) {
+ USBD_WakeUp();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Resume_Event) {
+ USBD_P_Resume_Event();
+ }
+
+#endif
+ HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_SUSPENDIEN_Msk | HSUSBD_BUSINTEN_SOFIEN_Msk);
+ HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_RESUMEIF_Msk);
+ }
+
+ if (IrqSt & HSUSBD_BUSINTSTS_SUSPENDIF_Msk) {
+ USBD_Suspend();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Suspend_Event) {
+ USBD_P_Suspend_Event();
+ }
+
+#endif
+ HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_RESUMEIEN_Msk | HSUSBD_BUSINTEN_SOFIEN_Msk);
+ HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_SUSPENDIF_Msk);
+ }
+
+ if (IrqSt & HSUSBD_BUSINTSTS_VBUSDETIF_Msk) {
+ if (HSUSBD_IS_ATTACHED()) {
+ HSUSBD_ENABLE_USB();
+ } else {
+ HSUSBD_DISABLE_USB();
+ }
+
+ HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_VBUSDETIF_Msk);
+ }
+ }
+
+ if (IrqStL & HSUSBD_GINTSTS_CEPIF_Msk) {
+ IrqSt = HSUSBD->CEPINTSTS & HSUSBD->CEPINTEN;
+
+ if (IrqSt & HSUSBD_CEPINTSTS_SETUPPKIF_Msk) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[0]) {
+ isr_evt_set(USBD_EVT_SETUP, USBD_RTX_EPTask[0]);
+ }
+
+#else
+
+ if (USBD_P_EP[0]) {
+ USBD_P_EP[0](USBD_EVT_SETUP);
+ }
+
+#endif
+ HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_SETUPPKIF_Msk);
+ return;
+ }
+
+ if (IrqSt & HSUSBD_CEPINTSTS_TXPKIF_Msk) {
+ HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
+ HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_NAKCLR);
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[0]) {
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[0]);
+ }
+
+#else
+
+ if (USBD_P_EP[0]) {
+ USBD_P_EP[0](USBD_EVT_IN);
+ }
+
+#endif
+ HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_TXPKIF_Msk);
+ return;
+ }
+
+ if (IrqSt & HSUSBD_CEPINTSTS_RXPKIF_Msk) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[0]) {
+ isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[0]);
+ }
+
+#else
+
+ if (USBD_P_EP[0]) {
+ USBD_P_EP[0](USBD_EVT_OUT);
+ }
+
+#endif
+ HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_RXPKIF_Msk);
+ return;
+ }
+
+ if (IrqSt & HSUSBD_CEPINTSTS_STSDONEIF_Msk) {
+ if (g_u8StatusIn == 0) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[0]) {
+ isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[0]);
+ }
+
+#else
+
+ if (USBD_P_EP[0]) {
+ USBD_P_EP[0](USBD_EVT_OUT);
+ }
+
+#endif
+ } else {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[0]) {
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[0]);
+ }
+
+#else
+
+ if (USBD_P_EP[0]) {
+ USBD_P_EP[0](USBD_EVT_IN);
+ }
+
+#endif
+ }
+
+ g_u8StatusIn = 0;
+ HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
+ return;
+ }
+ }
+
+ for (i = 0; i < HSUSBD_MAX_EP; i++) {
+ u32Ep = EPA + i;
+ u32Num = USBD_EP_TO_NUM(u32Ep);
+
+ if (IrqStL & (0x1UL << (HSUSBD_GINTSTS_EPAIF_Pos + i))) {
+ IrqSt = HSUSBD->EP[u32Ep].EPINTSTS & HSUSBD->EP[u32Ep].EPINTEN;
+
+ if (IrqSt & HSUSBD_EPINTSTS_TXPKIF_Msk) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[u32Num]) {
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[u32Num]);
+ }
+
+#else
+
+ if (USBD_P_EP[u32Num]) {
+ USBD_P_EP[u32Num](USBD_EVT_IN);
+ }
+
+#endif
+ }
+
+ if (IrqSt & (HSUSBD_EPINTSTS_RXPKIF_Msk | HSUSBD_EPINTSTS_SHORTRXIF_Msk | HSUSBD_EPINTEN_BUFFULLIEN_Msk)) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[u32Num]) {
+ isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[u32Num]);
+ }
+
+#else
+
+ if (USBD_P_EP[u32Num]) {
+ USBD_P_EP[u32Num](USBD_EVT_OUT);
+ }
+
+#endif
+ }
+
+ HSUSBD_CLR_EP_INT_FLAG(u32Ep, IrqSt);
+ }
+ }
+}
+
+void USBD_Handler(void)
+{
+ USBD_Handler_Main();
+ NVIC_EnableIRQ(USBD20_IRQn);
+}
diff --git a/port/nxp/lpc11u35/usbd_LPC11Uxx.c b/port/nxp/lpc11u35/usbd_LPC11Uxx.c
new file mode 100644
index 00000000..c609675f
--- /dev/null
+++ b/port/nxp/lpc11u35/usbd_LPC11Uxx.c
@@ -0,0 +1,825 @@
+/**
+ * @file usbd_LPC11Uxx.c
+ * @brief
+ *
+ * DAPLink Interface Firmware
+ * Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 "rl_usb.h"
+#include "LPC11Uxx.h"
+#include "compiler.h"
+#include "util.h"
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+
+#define BUF_ACTIVE (1UL << 31)
+#define EP_DISABLED (1UL << 30)
+#define EP_STALL (1UL << 29)
+#define TOOGLE_RESET (1UL << 28)
+#define EP_TYPE (1UL << 26)
+
+#define N_BYTES(n) ((n & 0x3FF) << 16)
+#define BUF_ADDR(addr) (((addr) >> 6) & 0xFFFF)
+
+#define EP_OUT_IDX(EPNum) (EPNum * 2 )
+#define EP_IN_IDX(EPNum) (EPNum * 2 + 1)
+
+#define EP_LIST_BASE 0x20004000
+#define EP_BUF_BASE (U32)(EP_LIST_BASE + 0x100)
+
+typedef struct BUF_INFO {
+ U32 buf_len;
+ U32 buf_ptr;
+} EP_BUF_INFO;
+
+EP_BUF_INFO EPBufInfo[(USBD_EP_NUM + 1) * 2];
+volatile U32 EPList[(USBD_EP_NUM + 1) * 2] __at(EP_LIST_BASE);
+
+static U32 addr = 3 * 64 + EP_BUF_BASE;
+static U32 ctrl_out_next = 0;
+
+/*
+ * Get EP CmdStat pointer
+ * Parameters: EPNum: endpoint number
+ *
+ */
+
+U32 *GetEpCmdStatPtr(U32 EPNum)
+{
+ U32 ptr = 0;
+
+ if (EPNum & 0x80) {
+ EPNum &= ~0x80;
+ ptr = 8;
+ }
+
+ ptr += EP_LIST_BASE + EPNum * 16;
+ return ((U32 *)ptr);
+}
+
+
+/*
+ * Usb interrupt enable/disable
+ * Parameters: ena: enable/disable
+ * 0: disable interrupt
+ * 1: enable interrupt
+ */
+
+#ifdef __RTX
+void __svc(1) USBD_Intr(int ena);
+void __SVC_1(int ena)
+{
+ if (ena) {
+ NVIC_EnableIRQ(USB_IRQn); /* Enable USB interrupt */
+ } else {
+ NVIC_DisableIRQ(USB_IRQn); /* Disable USB interrupt */
+ }
+}
+#endif
+
+
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB Device
+ * Return Value: None
+ */
+
+void USBD_Init(void)
+{
+ LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 6);
+ LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 14) |
+ (1UL << 27);
+ LPC_USB->DEVCMDSTAT |= (1UL << 9); /* PLL ON */
+ LPC_IOCON->PIO0_3 &= ~(0x1F);
+ LPC_IOCON->PIO0_3 |= (1UL << 0); /* Secondary function VBUS */
+ LPC_IOCON->PIO0_6 &= ~7;
+ LPC_IOCON->PIO0_6 |= (1UL << 0); /* Secondary function USB CON */
+ LPC_SYSCON->PDRUNCFG &= ~((1UL << 8) | /* USB PLL powered */
+ (1UL << 10)); /* USB transceiver powered */
+ LPC_USB->DATABUFSTART = EP_BUF_BASE & 0xFFC00000;
+ LPC_USB->EPLISTSTART = EP_LIST_BASE;
+ NVIC_EnableIRQ(USB_IRQn);
+ USBD_Reset();
+}
+
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+
+void USBD_Connect(BOOL con)
+{
+ if (con) {
+ LPC_USB->DEVCMDSTAT |= (1UL << 16); /* Set device connect status */
+ } else {
+ LPC_USB->DEVCMDSTAT &= ~(1UL << 16); /* Clear device connect status */
+ }
+
+ return;
+}
+
+
+/*
+ * USB Device Reset Function
+ * Called automatically on USB Device Reset
+ * Return Value: None
+ */
+
+void USBD_Reset(void)
+{
+ U32 i;
+ U32 *ptr;
+ addr = 3 * 64 + EP_BUF_BASE;
+
+ for (i = 2; i < (5 * 4); i++) {
+ EPList[i] = (1UL << 30); /* EPs disabled */
+ }
+
+ ctrl_out_next = 0;
+ EPBufInfo[0].buf_len = USBD_MAX_PACKET0;
+ EPBufInfo[0].buf_ptr = EP_BUF_BASE;
+ EPBufInfo[1].buf_len = USBD_MAX_PACKET0;
+ EPBufInfo[1].buf_ptr = EP_BUF_BASE + 2 * 64;
+ ptr = GetEpCmdStatPtr(0);
+ *ptr = N_BYTES(EPBufInfo[0].buf_len) | /* EP0 OUT */
+ BUF_ADDR(EPBufInfo[0].buf_ptr) |
+ BUF_ACTIVE;
+ ptr++;
+ *ptr = BUF_ADDR(EPBufInfo[0].buf_ptr + 64);/* SETUP */
+ LPC_USB->DEVCMDSTAT |= (1UL << 7); /*USB device enable */
+ LPC_USB->INTSTAT = 0x2FC; /* clear EP interrupt flags */
+ LPC_USB->INTEN = ((1UL << 30) | /* SOF intr enable */
+ (1UL << 0) | /* EP0 OUT intr enable */
+ (1UL << 1) | /* EP0 IN intr enable */
+ (1UL << 31)); /* stat change int en */
+}
+
+
+/*
+ * USB Device Suspend Function
+ * Called automatically on USB Device Suspend
+ * Return Value: None
+ */
+
+void USBD_Suspend(void)
+{
+ /* Performed by Hardware */
+}
+
+
+/*
+ * USB Device Resume Function
+ * Called automatically on USB Device Resume
+ * Return Value: None
+ */
+
+void USBD_Resume(void)
+{
+ /* Performed by Hardware */
+}
+
+
+/*
+ * USB Device Remote Wakeup Function
+ * Called automatically on USB Device Remote Wakeup
+ * Return Value: None
+ */
+
+void USBD_WakeUp(void)
+{
+ LPC_SYSCON->USBCLKCTRL = 1;
+ LPC_USB->DEVCMDSTAT &= ~(1UL << 17); /*clear device suspend status */
+
+ while (LPC_USB->DEVCMDSTAT & (1UL << 17));
+
+ LPC_SYSCON->USBCLKCTRL = 0;
+}
+
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+
+void USBD_WakeUpCfg(BOOL cfg)
+{
+ if (cfg == __TRUE) {
+ LPC_USB->DEVCMDSTAT &= ~(1UL << 9); /*PPL_ON=0, in suspend clk is stoped */
+ } else {
+ LPC_USB->DEVCMDSTAT |= (1UL << 9); /*PPL_ON=1, in suspend clk isnt stoped */
+ LPC_SYSCON->USBCLKCTRL = 0;
+ }
+}
+
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * Return Value: None
+ */
+
+void USBD_SetAddress(U32 adr, U32 setup)
+{
+ if (!setup) {
+ LPC_USB->DEVCMDSTAT &= ~0x7F;
+ LPC_USB->DEVCMDSTAT |= adr | (1UL << 7);
+ }
+}
+
+
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+
+void USBD_Configure(BOOL cfg)
+{
+ addr = 3 * 64 + EP_BUF_BASE;
+}
+
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+
+void USBD_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ U32 num, val, type;
+ U32 *ptr;
+ num = pEPD->bEndpointAddress;
+ val = pEPD->wMaxPacketSize;
+ type = pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+
+ /* IN EPs */
+ if (num & 0x80) {
+ num &= ~0x80;
+ EPBufInfo[EP_IN_IDX(num)].buf_len = val;
+ EPBufInfo[EP_IN_IDX(num)].buf_ptr = addr;
+ addr += ((val + 63) >> 6) * 64; /* calc new free buffer address */
+ ptr = GetEpCmdStatPtr(num | 0x80);
+ *ptr = EP_DISABLED;
+
+ if (type == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
+ *ptr |= EP_TYPE;
+ }
+ }
+
+ /* OUT EPs */
+ else {
+ EPBufInfo[EP_OUT_IDX(num)].buf_len = val;
+ EPBufInfo[EP_OUT_IDX(num)].buf_ptr = addr;
+ ptr = GetEpCmdStatPtr(num);
+ *ptr = N_BYTES(EPBufInfo[EP_OUT_IDX(num)].buf_len) |
+ BUF_ADDR(EPBufInfo[EP_OUT_IDX(num)].buf_ptr) |
+ EP_DISABLED;
+
+ if (type == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
+ *ptr |= EP_TYPE;
+ }
+
+ addr += ((val + 63) >> 6) * 64; /* calc new free buffer address */
+ }
+}
+
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+
+void USBD_DirCtrlEP(U32 dir)
+{
+ /* Not needed */
+}
+
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_EnableEP(U32 EPNum)
+{
+ U32 *ptr;;
+ ptr = GetEpCmdStatPtr(EPNum);
+
+ /* IN EP */
+ if (EPNum & 0x80) {
+ EPNum &= ~0x80;
+ *ptr &= ~EP_DISABLED;
+ LPC_USB->INTSTAT = (1 << EP_IN_IDX(EPNum));
+ LPC_USB->INTEN |= (1 << EP_IN_IDX(EPNum));
+ }
+
+ /* OUT EP */
+ else {
+ *ptr &= ~EP_DISABLED;
+ *ptr |= BUF_ACTIVE;
+ LPC_USB->INTSTAT = (1 << EP_OUT_IDX(EPNum));
+ LPC_USB->INTEN |= (1 << EP_OUT_IDX(EPNum));
+ }
+}
+
+
+/*
+ * Disable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_DisableEP(U32 EPNum)
+{
+ U32 *ptr;
+ ptr = GetEpCmdStatPtr(EPNum);
+ *ptr = EP_DISABLED;
+
+ if (EPNum & 0x80) {
+ EPNum &= ~0x80;
+ LPC_USB->INTEN &= ~(1 << EP_IN_IDX(EPNum));
+
+ } else {
+ LPC_USB->INTEN &= ~(1 << EP_OUT_IDX(EPNum));
+ }
+}
+
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ResetEP(U32 EPNum)
+{
+ U32 *ptr;
+ ptr = GetEpCmdStatPtr(EPNum);
+ *ptr |= TOOGLE_RESET;
+}
+
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_SetStallEP(U32 EPNum)
+{
+ U32 *ptr;
+ ptr = GetEpCmdStatPtr(EPNum);
+
+ if (EPNum & 0x7F) {
+ if (*ptr & BUF_ACTIVE) {
+ *ptr &= ~(BUF_ACTIVE);
+ }
+
+ } else {
+ if (EPNum & 0x80) {
+ EPNum &= ~0x80;
+ LPC_USB->EPSKIP |= (1 << EP_IN_IDX(EPNum));
+
+ while (LPC_USB->EPSKIP & (1 << EP_IN_IDX(EPNum)));
+
+ } else {
+ LPC_USB->EPSKIP |= (1 << EP_OUT_IDX(EPNum));
+
+ while (LPC_USB->EPSKIP & (1 << EP_OUT_IDX(EPNum)));
+ }
+ }
+
+ if ((EPNum & 0x7F) == 0) {
+ /* Endpoint is stalled so control out won't be next */
+ ctrl_out_next = 0;
+ }
+
+ *ptr |= EP_STALL;
+}
+
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClrStallEP(U32 EPNum)
+{
+ U32 *ptr;
+ ptr = GetEpCmdStatPtr(EPNum);
+
+ if (EPNum & 0x80) {
+ *ptr &= ~EP_STALL;
+
+ } else {
+ *ptr &= ~EP_STALL;
+ *ptr |= BUF_ACTIVE;
+ }
+
+ USBD_ResetEP(EPNum);
+}
+
+
+/*
+ * Clear USB Device Endpoint Buffer
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClearEPBuf(U32 EPNum)
+{
+ U32 cnt, i;
+ U8 *dataptr;
+
+ if (EPNum & 0x80) {
+ EPNum &= ~0x80;
+ dataptr = (U8 *)EPBufInfo[EP_IN_IDX(EPNum)].buf_ptr;
+ cnt = EPBufInfo[EP_IN_IDX(EPNum)].buf_len;
+
+ for (i = 0; i < cnt; i++) {
+ dataptr[i] = 0;
+ }
+
+ } else {
+ dataptr = (U8 *)EPBufInfo[EP_OUT_IDX(EPNum)].buf_ptr;
+ cnt = EPBufInfo[EP_OUT_IDX(EPNum)].buf_len;
+
+ for (i = 0; i < cnt; i++) {
+ dataptr[i] = 0;
+ }
+ }
+}
+
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+
+U32 USBD_ReadEP(U32 EPNum, U8 *pData, U32 size)
+{
+ U32 cnt, i, xfer_size;
+ volatile U32 *ptr;
+ U8 *dataptr;
+ ptr = GetEpCmdStatPtr(EPNum);
+ int timeout = 256;
+
+ /* Setup packet */
+ if ((EPNum == 0) && !ctrl_out_next && (LPC_USB->DEVCMDSTAT & (1UL << 8))) {
+ cnt = USBD_MAX_PACKET0;
+
+ if (size < cnt) {
+ util_assert(0);
+ cnt = size;
+ }
+
+ dataptr = (U8 *)(EPBufInfo[EP_OUT_IDX(EPNum)].buf_ptr + 64);
+
+ for (i = 0; i < cnt; i++) {
+ pData[i] = dataptr[i];
+ }
+
+ xfer_size = (pData[7] << 8) | (pData[6] << 0);
+ if ((xfer_size > 0) && (pData[0] & (1 << 7))) {
+ /* This control transfer has a data IN stage */
+ /* and ends with a zero length data OUT transfer. */
+ /* Ensure the data OUT token is not skipped even if */
+ /* a SETUP token arrives before USBD_ReadEP has */
+ /* been called. */
+ ctrl_out_next = 1;
+ }
+
+ LPC_USB->EPSKIP |= (1 << EP_IN_IDX(EPNum));
+
+ while (LPC_USB->EPSKIP & (1 << EP_IN_IDX(EPNum)));
+
+ if (*(ptr + 2) & EP_STALL) {
+ *(ptr + 2) &= ~(EP_STALL);
+ }
+
+ if (*ptr & EP_STALL) {
+ *ptr &= ~(EP_STALL);
+ }
+
+ LPC_USB->DEVCMDSTAT |= (1UL << 8);
+ }
+
+ /* OUT packet */
+ else {
+ ptr = GetEpCmdStatPtr(EPNum);
+ cnt = EPBufInfo[EP_OUT_IDX(EPNum)].buf_len - ((*ptr >> 16) & 0x3FF);
+ dataptr = (U8 *)EPBufInfo[EP_OUT_IDX(EPNum)].buf_ptr;
+
+ while ((timeout-- > 0) && (*ptr & BUF_ACTIVE)); //spin on the hardware until it's done
+ util_assert(!(*ptr & BUF_ACTIVE)); //check for timeout
+
+ if (size < cnt) {
+ util_assert(0);
+ cnt = size;
+ }
+
+ cnt = cnt < size ? cnt : size;
+
+ for (i = 0; i < cnt; i++) {
+ pData[i] = dataptr[i];
+ }
+
+ *ptr = N_BYTES(EPBufInfo[EP_OUT_IDX(EPNum)].buf_len) |
+ BUF_ADDR(EPBufInfo[EP_OUT_IDX(EPNum)].buf_ptr) |
+ BUF_ACTIVE;
+
+ if (EPNum == 0) {
+ /* If ctrl_out_next is set then this should be a zero length */
+ /* data OUT packet. */
+ util_assert(!ctrl_out_next || (cnt == 0));
+ ctrl_out_next = 0;
+ if (LPC_USB->DEVCMDSTAT & (1UL << 8)) {
+ // A setup packet is still pending so trigger another interrupt
+ LPC_USB->INTSETSTAT |= (1 << 0);
+ }
+ }
+ }
+
+ return (cnt);
+}
+
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+
+U32 USBD_WriteEP(U32 EPNum, U8 *pData, U32 cnt)
+{
+ U32 i;
+ volatile U32 *ptr;
+ U32 *dataptr;
+ ptr = GetEpCmdStatPtr(EPNum);
+ EPNum &= ~0x80;
+
+ while (*ptr & BUF_ACTIVE);
+
+ *ptr &= ~(0x3FFFFFF);
+ *ptr |= BUF_ADDR(EPBufInfo[EP_IN_IDX(EPNum)].buf_ptr) |
+ N_BYTES(cnt);
+ dataptr = (U32 *)EPBufInfo[EP_IN_IDX(EPNum)].buf_ptr;
+
+ for (i = 0; i < (cnt + 3) / 4; i++) {
+ dataptr[i] = * ((__packed U32 *)pData);
+ pData += 4;
+ }
+
+ if (EPNum && (*ptr & EP_STALL)) {
+ return (0);
+ }
+
+ *ptr |= BUF_ACTIVE;
+ return (cnt);
+}
+
+
+/*
+ * Get USB Device Last Frame Number
+ * Parameters: None
+ * Return Value: Frame Number
+ */
+
+U32 USBD_GetFrame(void)
+{
+ return (LPC_USB->INFO & 0x7FF);
+}
+
+
+/*
+ * USB Device Interrupt Service Routine
+ */
+
+void USB_IRQHandler(void)
+{
+ NVIC_DisableIRQ(USB_IRQn);
+ USBD_SignalHandler();
+}
+
+void USBD_Handler(void)
+{
+ U32 sts, val, num, i;
+ sts = LPC_USB->INTSTAT;
+ LPC_USB->INTSTAT = sts;
+
+ /* Device Status Interrupt (Reset, Connect change, Suspend/Resume) */
+ if (sts & (1UL << 31)) {
+ val = LPC_USB->DEVCMDSTAT;
+
+ /* reset interrupt */
+ if (val & (1UL << 26)) {
+ LPC_USB->DEVCMDSTAT |= (1UL << 26);
+ USBD_Reset();
+ usbd_reset_core();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Reset_Event) {
+ USBD_P_Reset_Event();
+ }
+
+#endif
+ }
+
+ /* connect interrupt */
+ if (val & (1UL << 24)) {
+ LPC_USB->DEVCMDSTAT |= (1UL << 24);
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ if (val & (1UL << 16)) {
+ isr_evt_set(USBD_EVT_POWER_ON, USBD_RTX_DevTask);
+ } else {
+ isr_evt_set(USBD_EVT_POWER_OFF, USBD_RTX_DevTask);
+ }
+ }
+
+#else
+
+ if (USBD_P_Power_Event) {
+ USBD_P_Power_Event((val >> 16) & 1);
+ }
+
+#endif
+ }
+
+ /* suspend/resume interrupt */
+ if (val & (1 << 25)) {
+ LPC_USB->DEVCMDSTAT |= (1UL << 25);
+
+ /* suspend interrupt */
+ if (val & (1UL << 17)) {
+ USBD_Suspend();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Suspend_Event) {
+ USBD_P_Suspend_Event();
+ }
+
+#endif
+ }
+
+ /* resume interrupt */
+ else {
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Resume_Event) {
+ USBD_P_Resume_Event();
+ }
+
+#endif
+ }
+ }
+ }
+
+ /* Start of Frame */
+ if (sts & (1UL << 30)) {
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SOF, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+
+#endif
+ }
+
+ /* EndPoint Interrupt */
+ if (sts & 0x3FF) {
+ const uint32_t endpoint_count = ((USBD_EP_NUM + 1) * 2);
+
+ for (i = 0; i < endpoint_count; i++) {
+ // Iterate through endpoints in the reverse order so IN endpoints
+ // get processed before OUT endpoints if they are both pending.
+ num = endpoint_count - i - 1;
+
+ if (sts & (1UL << num)) {
+ /* Setup */
+ if ((num == 0) && !ctrl_out_next && (LPC_USB->DEVCMDSTAT & (1UL << 8))) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num / 2]) {
+ isr_evt_set(USBD_EVT_SETUP, USBD_RTX_EPTask[num / 2]);
+ }
+
+#else
+
+ if (USBD_P_EP[num / 2]) {
+ USBD_P_EP[num / 2](USBD_EVT_SETUP);
+ }
+
+#endif
+ }
+
+ /* OUT */
+ else if ((num % 2) == 0) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num / 2]) {
+ isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[num / 2]);
+ }
+
+#else
+
+ if (USBD_P_EP[num / 2]) {
+ USBD_P_EP[num / 2](USBD_EVT_OUT);
+ }
+
+#endif
+ }
+
+ /* IN */
+ else {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num / 2]) {
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[num / 2]);
+ }
+
+#else
+
+ if (USBD_P_EP[num / 2]) {
+ USBD_P_EP[num / 2](USBD_EVT_IN);
+ }
+
+#endif
+ }
+ }
+ }
+ }
+
+ NVIC_EnableIRQ(USB_IRQn);
+}
diff --git a/port/nxp/lpc4322/usbd_LPC43xx_USB0.c b/port/nxp/lpc4322/usbd_LPC43xx_USB0.c
new file mode 100644
index 00000000..b9d09f5a
--- /dev/null
+++ b/port/nxp/lpc4322/usbd_LPC43xx_USB0.c
@@ -0,0 +1,901 @@
+/**
+ * @file usbd_LPC43xx_USBD0.c
+ * @brief
+ *
+ * DAPLink Interface Firmware
+ * Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 "rl_usb.h"
+#include "usb.h"
+#include "LPC43xx.h"
+#include "compiler.h"
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+/* Endpoint queue head */
+typedef struct __EPQH {
+ uint32_t cap;
+ uint32_t curr_dTD;
+ uint32_t next_dTD;
+ uint32_t dTD_token;
+ uint32_t buf[5];
+ uint32_t reserved;
+ uint32_t setup[2];
+ uint32_t reserved1[4];
+} EPQH;
+
+/* Endpoint transfer descriptor */
+typedef struct __dTD {
+ uint32_t next_dTD;
+ uint32_t dTD_token;
+ uint32_t buf[5];
+ uint32_t reserved;
+} dTD;
+
+/* Endpoint */
+typedef struct __EP {
+ uint8_t *buf;
+ uint32_t maxPacket;
+} EP;
+
+EPQH __align(2048) EPQHx[(USBD_EP_NUM + 1) * 2];
+dTD __align(32) dTDx[(USBD_EP_NUM + 1) * 2];
+
+EP Ep[(USBD_EP_NUM + 1) * 2];
+uint32_t BufUsed;
+uint32_t IsoEp;
+uint32_t cmpl_pnd;
+
+#define LPC_USBx LPC_USB0
+#define ENDPTCTRL(EPNum) *(volatile uint32_t *)((uint32_t)(&LPC_USBx->ENDPTCTRL0) + 4 * EPNum)
+#define EP_OUT_IDX(EPNum) (EPNum * 2 )
+#define EP_IN_IDX(EPNum) (EPNum * 2 + 1)
+#define HS(en) (USBD_HS_ENABLE * en)
+
+/* reserve RAM for endpoint buffers */
+#if USBD_VENDOR_ENABLE
+/* custom class: user defined buffer size */
+#define EP_BUF_POOL_SIZE 0x1000
+uint8_t __align(4096) EPBufPool[EP_BUF_POOL_SIZE]
+#else
+/* supported classes are used */
+uint8_t __align(4096) EPBufPool[
+ USBD_MAX_PACKET0 * 2 +
+ USBD_HID_ENABLE * (HS(USBD_HID_HS_ENABLE) ? USBD_HID_HS_WMAXPACKETSIZE : USBD_HID_WMAXPACKETSIZE) * 2 +
+ USBD_MSC_ENABLE * (HS(USBD_MSC_HS_ENABLE) ? USBD_MSC_HS_WMAXPACKETSIZE : USBD_MSC_WMAXPACKETSIZE) * 2 +
+ USBD_ADC_ENABLE * (HS(USBD_ADC_HS_ENABLE) ? USBD_ADC_HS_WMAXPACKETSIZE : USBD_ADC_WMAXPACKETSIZE) +
+ USBD_CDC_ACM_ENABLE * ((HS(USBD_CDC_ACM_HS_ENABLE) ? USBD_CDC_ACM_HS_WMAXPACKETSIZE : USBD_CDC_ACM_WMAXPACKETSIZE) +
+ (HS(USBD_CDC_ACM_HS_ENABLE) ? USBD_CDC_ACM_HS_WMAXPACKETSIZE1 : USBD_CDC_ACM_WMAXPACKETSIZE1) * 2) +
+ USBD_BULK_ENABLE * (HS(USBD_BULK_HS_ENABLE) ? USBD_BULK_HS_WMAXPACKETSIZE : USBD_BULK_WMAXPACKETSIZE) * 2
+];
+#endif
+
+void USBD_PrimeEp(uint32_t EPNum, uint32_t cnt);
+
+/*
+ * Usb interrupt enable/disable
+ * Parameters: ena: enable/disable
+ * 0: disable interrupt
+ * 1: enable interrupt
+ */
+
+#ifdef __RTX
+void __svc(1) USBD_Intr(int ena);
+void __SVC_1(int ena)
+{
+#else
+void USBD_Intr(int ena)
+{
+#endif
+
+ if (ena) {
+ NVIC_EnableIRQ(USB0_IRQn); /* Enable USB interrupt */
+ } else {
+ NVIC_DisableIRQ(USB0_IRQn); /* Disable USB interrupt */
+ }
+}
+
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB Device
+ * Return Value: None
+ */
+
+void USBD_Init(void)
+{
+ USBD_Intr(0);
+ /* BASE_USB0_CLK */
+ LPC_CGU->BASE_USB0_CLK = (0x01 << 11) | /* Autoblock En */
+ (0x07 << 24) ; /* Clock source: PLL0 */
+ LPC_CCU1->CLK_M4_USB0_CFG |= 1;
+
+ while (!(LPC_CCU1->CLK_M4_USB0_STAT & 1));
+
+ LPC_SCU->SFSP6_3 = 1; /* pwr en */
+ LPC_SCU->SFSP6_6 = 3; /* pwr fault */
+ LPC_SCU->SFSP8_1 = 1; /* port indicator LED control out 1 */
+ LPC_SCU->SFSP8_2 = 1; /* port indicator LED control out 0 */
+ LPC_USBx->USBCMD_D |= (1UL << 1); /* usb reset */
+
+ while (LPC_USBx->USBCMD_D & (1UL << 1));
+
+ LPC_CREG->CREG0 &= ~(1 << 5);
+ LPC_USBx->USBMODE_D = 2 | (1UL << 3);/* device mode */
+#if USBD_HS_ENABLE
+ LPC_USBx->PORTSC1_D &= ~(1UL << 24);
+#else
+ LPC_USBx->PORTSC1_D |= (1UL << 24);
+#endif
+ LPC_USBx->OTGSC = 1 | (1UL << 3);
+ Ep[EP_OUT_IDX(0)].maxPacket = USBD_MAX_PACKET0;
+ LPC_USBx->USBINTR_D = (1UL << 0) | /* usb int enable */
+ (1UL << 2) | /* port change detect int enable */
+ (1UL << 8) | /* suspend int enable */
+ (1UL << 16) | /* nak int enable */
+ (1UL << 6) | /* reset int enable */
+#ifdef __RTX
+ ((USBD_RTX_DevTask != 0) ? (1UL << 7) : 0) | /* SOF */
+ ((USBD_RTX_DevTask != 0) ? (1UL << 1) : 0) ; /* Error */
+#else
+ ((USBD_P_SOF_Event != 0) ? (1UL << 7) : 0) | /* SOF */
+ ((USBD_P_Error_Event != 0) ? (1UL << 1) : 0) ; /* Error */
+#endif
+ USBD_Reset();
+ USBD_Intr(1);
+}
+
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+
+void USBD_Connect(uint32_t con)
+{
+ if (con) {
+ LPC_USBx->USBCMD_D |= 1; /* run */
+ } else {
+ LPC_USBx->USBCMD_D &= ~1; /* stop */
+ }
+}
+
+
+/*
+ * USB Device Reset Function
+ * Called automatically on USB Device Reset
+ * Return Value: None
+ */
+
+void USBD_Reset(void)
+{
+ uint32_t i;
+ uint8_t *ptr;
+ cmpl_pnd = 0;
+
+ for (i = 1; i < USBD_EP_NUM + 1; i++) {
+ ENDPTCTRL(i) &= ~((1UL << 7) | (1UL << 23));
+ }
+
+ /* clear interrupts */
+ LPC_USBx->ENDPTNAK = 0xFFFFFFFF;
+ LPC_USBx->ENDPTNAKEN = 0;
+ LPC_USBx->USBSTS_D = 0xFFFFFFFF;
+ LPC_USBx->ENDPTSETUPSTAT = LPC_USBx->ENDPTSETUPSTAT;
+ LPC_USBx->ENDPTCOMPLETE = LPC_USBx->ENDPTCOMPLETE;
+
+ while (LPC_USBx->ENDPTPRIME);
+
+ LPC_USBx->ENDPTFLUSH = 0xFFFFFFFF;
+
+ while (LPC_USBx->ENDPTFLUSH);
+
+ LPC_USBx->USBCMD_D &= ~0x00FF0000; /* immediate intrrupt treshold */
+ /* clear endpoint queue heads */
+ ptr = (uint8_t *)EPQHx;
+
+ for (i = 0; i < sizeof(EPQHx); i++) {
+ ptr[i] = 0;
+ }
+
+ /* clear endpoint transfer descriptors */
+ ptr = (uint8_t *)dTDx;
+
+ for (i = 0; i < sizeof(dTDx); i++) {
+ ptr[i] = 0;
+ }
+
+ Ep[EP_OUT_IDX(0)].maxPacket = USBD_MAX_PACKET0;
+ Ep[EP_OUT_IDX(0)].buf = EPBufPool;
+ BufUsed = USBD_MAX_PACKET0;
+ Ep[EP_IN_IDX(0)].maxPacket = USBD_MAX_PACKET0;
+ Ep[EP_IN_IDX(0)].buf = &(EPBufPool[BufUsed]);
+ BufUsed += USBD_MAX_PACKET0;
+ dTDx[EP_OUT_IDX(0)].next_dTD = 1;
+ dTDx[EP_IN_IDX(0)].next_dTD = 1;
+ dTDx[EP_OUT_IDX(0)].dTD_token = (USBD_MAX_PACKET0 << 16) | /* total bytes */
+ (1UL << 15); /* int on compl */
+ dTDx[EP_IN_IDX(0)].dTD_token = (USBD_MAX_PACKET0 << 16) | /* total bytes */
+ (1UL << 15); /* int on compl */
+ EPQHx[EP_OUT_IDX(0)].next_dTD = (uint32_t) &dTDx[EP_OUT_IDX(0)];
+ EPQHx[EP_IN_IDX(0)].next_dTD = (uint32_t) &dTDx[EP_IN_IDX(0)];
+ EPQHx[EP_OUT_IDX(0)].cap = ((USBD_MAX_PACKET0 & 0x0EFF) << 16) |
+ (1UL << 29) |
+ (1UL << 15); /* int on setup */
+ EPQHx[EP_IN_IDX(0)].cap = (USBD_MAX_PACKET0 << 16) |
+ (1UL << 29) |
+ (1UL << 15); /* int on setup */
+ LPC_USBx->ENDPOINTLISTADDR = (uint32_t)EPQHx;
+ LPC_USBx->USBMODE_D |= (1UL << 3); /* Setup lockouts off */
+ LPC_USBx->ENDPTCTRL0 = 0x00C000C0;
+ USBD_PrimeEp(0, Ep[EP_OUT_IDX(0)].maxPacket);
+}
+
+
+/*
+ * USB Device Suspend Function
+ * Called automatically on USB Device Suspend
+ * Return Value: None
+ */
+
+void USBD_Suspend(void)
+{
+ /* Performed by Hardware */
+}
+
+
+/*
+ * USB Device Resume Function
+ * Called automatically on USB Device Resume
+ * Return Value: None
+ */
+
+void USBD_Resume(void)
+{
+ /* Performed by Hardware */
+}
+
+
+/*
+ * USB Device Remote Wakeup Function
+ * Called automatically on USB Device Remote Wakeup
+ * Return Value: None
+ */
+
+void USBD_WakeUp(void)
+{
+ LPC_USBx->PORTSC1_D |= (1UL << 6);
+}
+
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+
+void USBD_WakeUpCfg(uint32_t cfg)
+{
+ /* Not needed */
+}
+
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * Return Value: None
+ */
+
+void USBD_SetAddress(uint32_t adr, uint32_t setup)
+{
+ if (setup == 0) {
+ LPC_USBx->DEVICEADDR = (adr << 25);
+ LPC_USBx->DEVICEADDR |= (1UL << 24);
+ }
+}
+
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+
+void USBD_Configure(uint32_t cfg)
+{
+ uint32_t i;
+
+ if (!cfg) {
+ for (i = 2; i < (2 * (USBD_EP_NUM + 1)); i++) {
+ Ep[i].buf = 0;
+ Ep[i].maxPacket = 0;
+ }
+
+ BufUsed = 2 * USBD_MAX_PACKET0;
+ }
+}
+
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+
+void USBD_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ uint32_t num, val, type, idx;
+
+ if ((pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK)) {
+ val = 16;
+ num = pEPD->bEndpointAddress & ~0x80;
+ idx = EP_IN_IDX(num);
+
+ } else {
+ val = 0;
+ num = pEPD->bEndpointAddress;
+ idx = EP_OUT_IDX(num);
+ }
+
+ type = pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+
+ if (!(Ep[idx].buf)) {
+ Ep[idx].buf = &(EPBufPool[BufUsed]);
+ Ep[idx].maxPacket = pEPD->wMaxPacketSize;
+ BufUsed += pEPD->wMaxPacketSize;
+
+ /* Isochronous endpoint */
+ if (type == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
+ IsoEp |= (1UL << (num + val));
+ }
+ }
+
+ dTDx[idx].buf[0] = (uint32_t)(Ep[idx].buf);
+ dTDx[idx].next_dTD = 1;
+ EPQHx[idx].cap = (Ep[idx].maxPacket << 16) |
+ (1UL << 29);
+ ENDPTCTRL(num) &= ~(0xFFFF << val);
+ ENDPTCTRL(num) |= ((type << 2) << val) |
+ ((1UL << 6) << val); /* Data toogle reset */
+}
+
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+
+void USBD_DirCtrlEP(uint32_t dir)
+{
+ /* Not needed */
+}
+
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_EnableEP(uint32_t EPNum)
+{
+ if (EPNum & 0x80) {
+ EPNum &= 0x7F;
+ ENDPTCTRL(EPNum) |= (1UL << 23); /* EP enabled */
+ } else {
+ ENDPTCTRL(EPNum) |= (1UL << 7); /* EP enabled */
+ }
+}
+
+
+/*
+ * Disable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_DisableEP(uint32_t EPNum)
+{
+ if (EPNum & 0x80) {
+ EPNum &= 0x7F;
+ ENDPTCTRL(EPNum) &= ~(1UL << 23); /* EP disabled */
+ } else {
+ ENDPTCTRL(EPNum) &= ~(1UL << 7); /* EP disabled */
+ }
+}
+
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ResetEP(uint32_t EPNum)
+{
+ if (EPNum & 0x80) {
+ EPNum &= 0x7F;
+ EPQHx[EP_IN_IDX(EPNum)].dTD_token &= 0xC0;
+ LPC_USBx->ENDPTFLUSH = (1UL << (EPNum + 16)); /* flush endpoint */
+
+ while (LPC_USBx->ENDPTFLUSH & (1UL << (EPNum + 16)));
+
+ ENDPTCTRL(EPNum) |= (1UL << 22); /* data toggle reset */
+
+ } else {
+ EPQHx[EP_OUT_IDX(EPNum)].dTD_token &= 0xC0;
+ LPC_USBx->ENDPTFLUSH = (1UL << EPNum); /* flush endpoint */
+
+ while (LPC_USBx->ENDPTFLUSH & (1UL << EPNum));
+
+ ENDPTCTRL(EPNum) |= (1UL << 6); /* data toggle reset */
+ USBD_PrimeEp(EPNum, Ep[EP_OUT_IDX(EPNum)].maxPacket);
+ }
+}
+
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_SetStallEP(uint32_t EPNum)
+{
+ if (EPNum & 0x80) {
+ EPNum &= 0x7F;
+ ENDPTCTRL(EPNum) |= (1UL << 16); /* IN endpoint stall */
+ } else {
+ ENDPTCTRL(EPNum) |= (1UL << 0); /* OUT endpoint stall */
+ }
+}
+
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClrStallEP(uint32_t EPNum)
+{
+ if (EPNum & 0x80) {
+ EPNum &= 0x7F;
+ ENDPTCTRL(EPNum) &= ~(1UL << 16); /* clear stall */
+ ENDPTCTRL(EPNum) |= (1UL << 22); /* data toggle reset */
+
+ while (ENDPTCTRL(EPNum) & (1UL << 16));
+
+ USBD_ResetEP(EPNum | 0x80);
+
+ } else {
+ ENDPTCTRL(EPNum) &= ~(1UL << 0); /* clear stall */
+ ENDPTCTRL(EPNum) |= (1UL << 6); /* data toggle reset */
+ }
+}
+
+
+/*
+ * Clear USB Device Endpoint Buffer
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClearEPBuf(uint32_t EPNum)
+{
+}
+
+
+/*
+ * USB Device Prime endpoint function
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * cnt: Bytes to transfer/receive
+ * Return Value: None
+ */
+
+void USBD_PrimeEp(uint32_t EPNum, uint32_t cnt)
+{
+ uint32_t idx, val;
+
+ /* IN endpoint */
+ if (EPNum & 0x80) {
+ EPNum &= 0x7F;
+ idx = EP_IN_IDX(EPNum);
+ val = (1UL << (EPNum + 16));
+ }
+
+ /* OUT endpoint */
+ else {
+ val = (1UL << EPNum);
+ idx = EP_OUT_IDX(EPNum);
+ }
+
+ dTDx[idx].buf[0] = (uint32_t)(Ep[idx].buf);
+ dTDx[idx].next_dTD = 1;
+
+ if (IsoEp & val) {
+ if (Ep[idx].maxPacket <= cnt) {
+ dTDx[idx].dTD_token = (1 << 10); /* MultO = 1 */
+
+ } else if ((Ep[idx].maxPacket * 2) <= cnt) {
+ dTDx[idx].dTD_token = (2 << 10); /* MultO = 2 */
+
+ } else {
+ dTDx[idx].dTD_token = (3 << 10); /* MultO = 3 */
+ }
+
+ } else {
+ dTDx[idx].dTD_token = 0;
+ }
+
+ dTDx[idx].dTD_token |= (cnt << 16) | /* bytes to transfer */
+ (1UL << 15) | /* int on complete */
+ 0x80; /* status - active */
+ EPQHx[idx].next_dTD = (uint32_t)(&dTDx[idx]);
+ EPQHx[idx].dTD_token &= ~0xC0;
+ LPC_USBx->ENDPTPRIME = (val);
+
+ while ((LPC_USBx->ENDPTPRIME & val));
+}
+
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+
+uint32_t USBD_ReadEP(uint32_t EPNum, uint8_t *pData, U32 size)
+{
+ uint32_t cnt = 0;
+ uint32_t i;
+
+ /* Setup packet */
+ if ((LPC_USBx->ENDPTSETUPSTAT & 1) && (!EPNum)) {
+ LPC_USBx->ENDPTSETUPSTAT = 1;
+
+ while (LPC_USBx->ENDPTSETUPSTAT & 1);
+
+ do {
+ *((__packed uint32_t *) pData) = EPQHx[EP_OUT_IDX(0)].setup[0];
+ *((__packed uint32_t *)(pData + 4)) = EPQHx[EP_OUT_IDX(0)].setup[1];
+ cnt = 8;
+ LPC_USBx->USBCMD_D |= (1UL << 13);
+ } while (!(LPC_USBx->USBCMD_D & (1UL << 13)));
+
+ LPC_USBx->USBCMD_D &= (~(1UL << 13));
+ LPC_USBx->ENDPTFLUSH = (1UL << EPNum) | (1UL << (EPNum + 16));
+
+ while (LPC_USBx->ENDPTFLUSH & ((1UL << (EPNum + 16)) | (1UL << EPNum)));
+
+ while (LPC_USBx->ENDPTSETUPSTAT & 1);
+
+ USBD_PrimeEp(EPNum, Ep[EP_OUT_IDX(EPNum)].maxPacket);
+ }
+
+ /* OUT Packet */
+ else {
+ if (Ep[EP_OUT_IDX(EPNum)].buf) {
+ cnt = Ep[EP_OUT_IDX(EPNum)].maxPacket -
+ ((dTDx[EP_OUT_IDX(EPNum)].dTD_token >> 16) & 0x7FFF);
+
+ cnt = cnt < size ? cnt : size;
+
+ for (i = 0; i < cnt; i++) {
+ pData[i] = Ep[EP_OUT_IDX(EPNum)].buf[i];
+ }
+ }
+
+ LPC_USBx->ENDPTCOMPLETE = (1UL << EPNum);
+ cmpl_pnd &= ~(1UL << EPNum);
+ USBD_PrimeEp(EPNum, Ep[EP_OUT_IDX(EPNum)].maxPacket);
+ }
+
+ return (cnt);
+}
+
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+
+uint32_t USBD_WriteEP(uint32_t EPNum, uint8_t *pData, uint32_t cnt)
+{
+ uint32_t i;
+ EPNum &= 0x7f;
+
+ for (i = 0; i < cnt; i++) {
+ Ep[EP_IN_IDX(EPNum)].buf[i] = pData[i];
+ }
+
+ USBD_PrimeEp(EPNum | 0x80, cnt);
+ return (cnt);
+}
+
+
+/*
+ * Get USB Device Last Frame Number
+ * Parameters: None
+ * Return Value: Frame Number
+ */
+
+uint32_t USBD_GetFrame(void)
+{
+ return ((LPC_USBx->FRINDEX_D >> 3) & 0x0FFF);
+}
+
+
+#ifdef __RTX
+uint32_t LastError; /* Last Error */
+
+/*
+ * Get USB Device Last Error Code
+ * Parameters: None
+ * Return Value: Error Code
+ */
+
+uint32_t USBD_GetError(void)
+{
+ return (LastError);
+}
+#endif
+
+
+/*
+ * USB Device Interrupt Service Routine
+ */
+
+void USB0_IRQHandler(void)
+{
+ NVIC_DisableIRQ(USB0_IRQn);
+ USBD_SignalHandler();
+}
+
+void USBD_Handler(void)
+{
+ uint32_t sts, cmpl, num;
+ sts = LPC_USBx->USBSTS_D & LPC_USBx->USBINTR_D;
+ cmpl = LPC_USBx->ENDPTCOMPLETE;
+ LPC_USBx->USBSTS_D = sts; /* clear interupt flags */
+
+ /* reset interrupt */
+ if (sts & (1UL << 6)) {
+ USBD_Reset();
+ usbd_reset_core();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Reset_Event) {
+ USBD_P_Reset_Event();
+ }
+
+#endif
+ }
+
+ /* suspend interrupt */
+ if (sts & (1UL << 8)) {
+ USBD_Suspend();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Suspend_Event) {
+ USBD_P_Suspend_Event();
+ }
+
+#endif
+ }
+
+ /* SOF interrupt */
+ if (sts & (1UL << 7)) {
+ if (IsoEp) {
+ for (num = 0; num < USBD_EP_NUM + 1; num++) {
+ if (IsoEp & (1UL << num)) {
+ USBD_PrimeEp(num, Ep[EP_OUT_IDX(num)].maxPacket);
+ }
+ }
+
+ } else {
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SOF, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+
+#endif
+ }
+ }
+
+ /* port change detect interrupt */
+ if (sts & (1UL << 2)) {
+ if (((LPC_USBx->PORTSC1_D >> 26) & 0x03) == 2) {
+ USBD_HighSpeed = __TRUE;
+ }
+
+ USBD_Resume();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Resume_Event) {
+ USBD_P_Resume_Event();
+ }
+
+#endif
+ }
+
+ /* USB interrupt - completed transfer */
+ if (sts & 1) {
+ /* Setup Packet */
+ if (LPC_USBx->ENDPTSETUPSTAT) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[0]) {
+ isr_evt_set(USBD_EVT_SETUP, USBD_RTX_EPTask[0]);
+ }
+
+#else
+
+ if (USBD_P_EP[0]) {
+ USBD_P_EP[0](USBD_EVT_SETUP);
+ }
+
+#endif
+ }
+
+ /* IN Packet */
+ if (cmpl & (0x3F << 16)) {
+ for (num = 0; num < USBD_EP_NUM + 1; num++) {
+ if (((cmpl >> 16) & 0x3F) & (1UL << num)) {
+ LPC_USBx->ENDPTCOMPLETE = (1UL << (num + 16)); /* Clear completed */
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num]) {
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[num]);
+ }
+
+#else
+
+ if (USBD_P_EP[num]) {
+ USBD_P_EP[num](USBD_EVT_IN);
+ }
+
+#endif
+ }
+ }
+ }
+
+ /* OUT Packet */
+ if (cmpl & 0x3F) {
+ for (num = 0; num < USBD_EP_NUM + 1; num++) {
+ if ((cmpl ^ cmpl_pnd) & cmpl & (1UL << num)) {
+ cmpl_pnd |= 1UL << num;
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num]) {
+ isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[num]);
+
+ } else if (IsoEp & (1UL << num)) {
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SOF, USBD_RTX_DevTask);
+ }
+ }
+
+#else
+
+ if (USBD_P_EP[num]) {
+ USBD_P_EP[num](USBD_EVT_OUT);
+
+ } else if (IsoEp & (1UL << num)) {
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+ }
+
+#endif
+ }
+ }
+ }
+ }
+
+ /* error interrupt */
+ if (sts & (1UL << 1)) {
+ for (num = 0; num < USBD_EP_NUM + 1; num++) {
+ if (cmpl & (1UL << num)) {
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ LastError = dTDx[EP_OUT_IDX(num)].dTD_token & 0xE8;
+ isr_evt_set(USBD_EVT_ERROR, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Error_Event) {
+ USBD_P_Error_Event(dTDx[EP_OUT_IDX(num)].dTD_token & 0xE8);
+ }
+
+#endif
+ }
+
+ if (cmpl & (1UL << (num + 16))) {
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ LastError = dTDx[EP_IN_IDX(num)].dTD_token & 0xE8;
+ isr_evt_set(USBD_EVT_ERROR, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Error_Event) {
+ USBD_P_Error_Event(dTDx[EP_IN_IDX(num)].dTD_token & 0xE8);
+ }
+
+#endif
+ }
+ }
+ }
+
+ NVIC_EnableIRQ(USB0_IRQn);
+}
diff --git a/port/stm32/back/usb_dc_stm32.c b/port/stm32/back/usb_dc_stm32.c
new file mode 100644
index 00000000..6677dbf7
--- /dev/null
+++ b/port/stm32/back/usb_dc_stm32.c
@@ -0,0 +1,1111 @@
+/* USB device controller driver for STM32 devices */
+
+/*
+ * Copyright (c) 2017 Christer Weinigel.
+ * Copyright (c) 2017, I-SENSE group of ICCS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief USB device controller driver for STM32 devices
+ *
+ * This driver uses the STM32 Cube low level drivers to talk to the USB
+ * device controller on the STM32 family of devices using the
+ * STM32Cube HAL layer.
+ *
+ * There is a bit of an impedance mismatch between the Zephyr
+ * usb_device and the STM32 Cube HAL layer where higher levels make
+ * assumptions about the low level drivers that don't quite match how
+ * the low level drivers actually work.
+ *
+ * The usb_dc_ep_read function expects to get the data it wants
+ * immediately while the HAL_PCD_EP_Receive function only starts a
+ * read transaction and the data won't be available until call to
+ * HAL_PCD_DataOutStageCallback. To work around this I've
+ * had to add an extra packet buffer in the driver which wastes memory
+ * and also leads to an extra copy of all received data. It would be
+ * better if higher drivers could call start_read and get_read_count
+ * in this driver directly.
+ *
+ * To enable the driver together with the CDC_ACM high level driver,
+ * add the following to your board's defconfig:
+ *
+ * CONFIG_USB=y
+ * CONFIG_USB_DC_STM32=y
+ * CONFIG_USB_CDC_ACM=y
+ * CONFIG_USB_DEVICE_STACK=y
+ *
+ * To use the USB device as a console, also add:
+ *
+ * CONFIG_UART_CONSOLE_ON_DEV_NAME="CDC_ACM"
+ * CONFIG_USB_UART_CONSOLE=y
+ * CONFIG_UART_LINE_CTRL=y
+ */
+
+#include <soc.h>
+#include <stm32_ll_bus.h>
+#include <stm32_ll_pwr.h>
+#include <stm32_ll_rcc.h>
+#include <stm32_ll_system.h>
+#include <string.h>
+#include <usb/usb_device.h>
+#include <drivers/clock_control/stm32_clock_control.h>
+#include <sys/util.h>
+#include <drivers/gpio.h>
+#include <pinmux/stm32/pinmux_stm32.h>
+#include "stm32_hsem.h"
+
+#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
+#include <logging/log.h>
+LOG_MODULE_REGISTER(usb_dc_stm32);
+
+#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs) && DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
+#error "Only one interface should be enabled at a time, OTG FS or OTG HS"
+#endif
+
+#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
+#define DT_DRV_COMPAT st_stm32_otghs
+#define USB_IRQ_NAME otghs
+#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs)
+#define DT_DRV_COMPAT st_stm32_otgfs
+#define USB_IRQ_NAME otgfs
+#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usb)
+#define DT_DRV_COMPAT st_stm32_usb
+#define USB_IRQ_NAME usb
+#if DT_INST_NODE_HAS_PROP(0, enable_pin_remap)
+#define USB_ENABLE_PIN_REMAP DT_INST_PROP(0, enable_pin_remap)
+#endif
+#endif
+
+#define USB_BASE_ADDRESS DT_INST_REG_ADDR(0)
+#define USB_IRQ DT_INST_IRQ_BY_NAME(0, USB_IRQ_NAME, irq)
+#define USB_IRQ_PRI DT_INST_IRQ_BY_NAME(0, USB_IRQ_NAME, priority)
+#define USB_NUM_BIDIR_ENDPOINTS DT_INST_PROP(0, num_bidir_endpoints)
+#define USB_RAM_SIZE DT_INST_PROP(0, ram_size)
+#define USB_CLOCK_BITS DT_INST_CLOCKS_CELL(0, bits)
+#define USB_CLOCK_BUS DT_INST_CLOCKS_CELL(0, bus)
+#if DT_INST_NODE_HAS_PROP(0, maximum_speed)
+#define USB_MAXIMUM_SPEED DT_INST_PROP(0, maximum_speed)
+#endif
+static const struct soc_gpio_pinctrl usb_pinctrl[] =
+ ST_STM32_DT_INST_PINCTRL(0, 0);
+
+
+#define USB_OTG_HS_EMB_PHY (DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usbphyc) && \
+ DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs))
+
+/*
+ * USB and USB_OTG_FS are defined in STM32Cube HAL and allows to distinguish
+ * between two kind of USB DC. STM32 F0, F3, L0 and G4 series support USB device
+ * controller. STM32 F4 and F7 series support USB_OTG_FS device controller.
+ * STM32 F1 and L4 series support either USB or USB_OTG_FS device controller.
+ *
+ * WARNING: Don't mix USB defined in STM32Cube HAL and CONFIG_USB from Zephyr
+ * Kconfig system.
+ */
+#ifdef USB
+
+#define EP0_MPS 64U
+#define EP_MPS 64U
+
+/*
+ * USB BTABLE is stored in the PMA. The size of BTABLE is 4 bytes
+ * per endpoint.
+ *
+ */
+#define USB_BTABLE_SIZE (8 * USB_NUM_BIDIR_ENDPOINTS)
+
+#else /* USB_OTG_FS */
+
+/*
+ * STM32L4 series USB LL API doesn't provide HIGH and HIGH_IN_FULL speed
+ * defines.
+ */
+#if defined(CONFIG_SOC_SERIES_STM32L4X)
+#define USB_OTG_SPEED_HIGH 0U
+#define USB_OTG_SPEED_HIGH_IN_FULL 1U
+#endif /* CONFIG_SOC_SERIES_STM32L4X */
+
+#define EP0_MPS USB_OTG_MAX_EP0_SIZE
+
+#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
+#define EP_MPS USB_OTG_HS_MAX_PACKET_SIZE
+#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs) || DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usb)
+#define EP_MPS USB_OTG_FS_MAX_PACKET_SIZE
+#endif
+
+/* We need one RX FIFO and n TX-IN FIFOs */
+#define FIFO_NUM (1 + USB_NUM_BIDIR_ENDPOINTS)
+
+/* 4-byte words FIFO */
+#define FIFO_WORDS (USB_RAM_SIZE / 4)
+
+/* Allocate FIFO memory evenly between the FIFOs */
+#define FIFO_EP_WORDS (FIFO_WORDS / FIFO_NUM)
+
+#endif /* USB */
+
+/* Size of a USB SETUP packet */
+#define SETUP_SIZE 8
+
+/* Helper macros to make it easier to work with endpoint numbers */
+#define EP0_IDX 0
+#define EP0_IN (EP0_IDX | USB_EP_DIR_IN)
+#define EP0_OUT (EP0_IDX | USB_EP_DIR_OUT)
+
+/* Endpoint state */
+struct usb_dc_stm32_ep_state {
+ uint16_t ep_mps; /** Endpoint max packet size */
+ uint16_t ep_pma_buf_len; /** Previously allocated buffer size */
+ uint8_t ep_type; /** Endpoint type (STM32 HAL enum) */
+ uint8_t ep_stalled; /** Endpoint stall flag */
+ usb_dc_ep_callback cb; /** Endpoint callback function */
+ uint32_t read_count; /** Number of bytes in read buffer */
+ uint32_t read_offset; /** Current offset in read buffer */
+ struct k_sem write_sem; /** Write boolean semaphore */
+};
+
+/* Driver state */
+struct usb_dc_stm32_state {
+ PCD_HandleTypeDef pcd; /* Storage for the HAL_PCD api */
+ usb_dc_status_callback status_cb; /* Status callback */
+ struct usb_dc_stm32_ep_state out_ep_state[USB_NUM_BIDIR_ENDPOINTS];
+ struct usb_dc_stm32_ep_state in_ep_state[USB_NUM_BIDIR_ENDPOINTS];
+ uint8_t ep_buf[USB_NUM_BIDIR_ENDPOINTS][EP_MPS];
+
+#ifdef USB
+ uint32_t pma_offset;
+#endif /* USB */
+};
+
+static struct usb_dc_stm32_state usb_dc_stm32_state;
+
+/* Internal functions */
+
+static struct usb_dc_stm32_ep_state *usb_dc_stm32_get_ep_state(uint8_t ep)
+{
+ struct usb_dc_stm32_ep_state *ep_state_base;
+
+ if (USB_EP_GET_IDX(ep) >= USB_NUM_BIDIR_ENDPOINTS) {
+ return NULL;
+ }
+
+ if (USB_EP_DIR_IS_OUT(ep)) {
+ ep_state_base = usb_dc_stm32_state.out_ep_state;
+ } else {
+ ep_state_base = usb_dc_stm32_state.in_ep_state;
+ }
+
+ return ep_state_base + USB_EP_GET_IDX(ep);
+}
+
+static void usb_dc_stm32_isr(const void *arg)
+{
+ HAL_PCD_IRQHandler(&usb_dc_stm32_state.pcd);
+}
+
+#ifdef CONFIG_USB_DEVICE_SOF
+void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
+{
+ usb_dc_stm32_state.status_cb(USB_DC_SOF, NULL);
+}
+#endif
+
+static int usb_dc_stm32_clock_enable(void)
+{
+ const struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
+ struct stm32_pclken pclken = {
+ .bus = USB_CLOCK_BUS,
+ .enr = USB_CLOCK_BITS,
+ };
+
+ /*
+ * Some SoCs in STM32F0/L0/L4 series disable USB clock by
+ * default. We force USB clock source to MSI or PLL clock for this
+ * SoCs. However, if these parts have an HSI48 clock, use
+ * that instead. Example reference manual RM0360 for
+ * STM32F030x4/x6/x8/xC and STM32F070x6/xB.
+ */
+#if defined(RCC_HSI48_SUPPORT) || defined(CONFIG_SOC_SERIES_STM32WBX)
+
+ /*
+ * In STM32L0 series, HSI48 requires VREFINT and its buffer
+ * with 48 MHz RC to be enabled.
+ * See ENREF_HSI48 in referenc maual RM0367 section10.2.3:
+ * "Reference control and status register (SYSCFG_CFGR3)"
+ */
+#ifdef CONFIG_SOC_SERIES_STM32L0X
+ if (LL_APB2_GRP1_IsEnabledClock(LL_APB2_GRP1_PERIPH_SYSCFG)) {
+ LL_SYSCFG_VREFINT_EnableHSI48();
+ } else {
+ LOG_ERR("System Configuration Controller clock is "
+ "disabled. Unable to enable VREFINT which "
+ "is required by HSI48.");
+ }
+#endif /* CONFIG_SOC_SERIES_STM32L0X */
+
+ z_stm32_hsem_lock(CFG_HW_CLK48_CONFIG_SEMID, HSEM_LOCK_DEFAULT_RETRY);
+
+ LL_RCC_HSI48_Enable();
+ while (!LL_RCC_HSI48_IsReady()) {
+ /* Wait for HSI48 to become ready */
+ }
+
+ LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_HSI48);
+
+#if !defined(CONFIG_SOC_SERIES_STM32WBX)
+ /* Specially for STM32WB, don't unlock the HSEM to prevent M0 core
+ * to disable HSI48 clock used for RNG.
+ */
+ z_stm32_hsem_unlock(CFG_HW_CLK48_CONFIG_SEMID);
+#endif /* CONFIG_SOC_SERIES_STM32WBX */
+
+#elif defined(LL_RCC_USB_CLKSOURCE_NONE)
+ /* When MSI is configured in PLL mode with a 32.768 kHz clock source,
+ * the MSI frequency can be automatically trimmed by hardware to reach
+ * better than ±0.25% accuracy. In this mode the MSI can feed the USB
+ * device. For now, we only use MSI for USB if not already used as
+ * system clock source.
+ */
+#if defined(CONFIG_CLOCK_STM32_MSI_PLL_MODE) && !defined(CONFIG_CLOCK_STM32_SYSCLK_SRC_MSI)
+ LL_RCC_MSI_Enable();
+ while (!LL_RCC_MSI_IsReady()) {
+ /* Wait for MSI to become ready */
+ }
+ /* Force 48 MHz mode */
+ LL_RCC_MSI_EnableRangeSelection();
+ LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_11);
+ LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_MSI);
+#else
+ if (LL_RCC_PLL_IsReady()) {
+ LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLL);
+ } else {
+ LOG_ERR("Unable to set USB clock source to PLL.");
+ }
+#endif /* CONFIG_CLOCK_STM32_MSI_PLL_MODE && !CONFIG_CLOCK_STM32_SYSCLK_SRC_MSI */
+
+#elif defined(RCC_CFGR_OTGFSPRE)
+ /* On STM32F105 and STM32F107 parts the USB OTGFSCLK is derived from
+ * PLL1, and must result in a 48 MHz clock... the options to achieve
+ * this are as below, controlled by the RCC_CFGR_OTGFSPRE bit.
+ * - PLLCLK * 2 / 2 i.e: PLLCLK == 48 MHz
+ * - PLLCLK * 2 / 3 i.e: PLLCLK == 72 MHz
+ *
+ * this requires that the system is running from PLLCLK
+ */
+ if (LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {
+ switch (sys_clock_hw_cycles_per_sec()) {
+ case 48000000U:
+ LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLL_DIV_2);
+ break;
+ case 72000000U:
+ LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLL_DIV_3);
+ break;
+ default:
+ LOG_ERR("Unable to set USB clock source (incompatible PLLCLK rate)");
+ return -EIO;
+ }
+ } else {
+ LOG_ERR("Unable to set USB clock source (not using PLL1)");
+ return -EIO;
+ }
+
+#endif /* RCC_HSI48_SUPPORT / LL_RCC_USB_CLKSOURCE_NONE / RCC_CFGR_OTGFSPRE */
+
+ if (clock_control_on(clk, (clock_control_subsys_t *)&pclken) != 0) {
+ LOG_ERR("Unable to enable USB clock");
+ return -EIO;
+ }
+
+#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
+#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usbphyc)
+ LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_OTGHSULPI);
+ LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_OTGPHYC);
+#else
+ /* Disable ULPI interface (for external high-speed PHY) clock */
+ LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_OTGHSULPI);
+ LL_AHB1_GRP1_DisableClockLowPower(LL_AHB1_GRP1_PERIPH_OTGHSULPI);
+#endif
+#endif
+
+ return 0;
+}
+
+#if defined(USB_OTG_FS) || defined(USB_OTG_HS)
+static uint32_t usb_dc_stm32_get_maximum_speed(void)
+{
+ /*
+ * If max-speed is not passed via DT, set it to USB controller's
+ * maximum hardware capability.
+ */
+#if USB_OTG_HS_EMB_PHY
+ uint32_t speed = USB_OTG_SPEED_HIGH;
+#else
+ uint32_t speed = USB_OTG_SPEED_FULL;
+#endif
+
+#ifdef USB_MAXIMUM_SPEED
+
+ if (!strncmp(USB_MAXIMUM_SPEED, "high-speed", 10)) {
+ speed = USB_OTG_SPEED_HIGH;
+ } else if (!strncmp(USB_MAXIMUM_SPEED, "full-speed", 10)) {
+#if USB_OTG_HS_EMB_PHY
+ speed = USB_OTG_SPEED_HIGH_IN_FULL;
+#else
+ speed = USB_OTG_SPEED_FULL;
+#endif
+ } else {
+ LOG_DBG("Unsupported maximum speed defined in device tree. "
+ "USB controller will default to its maximum HW "
+ "capability");
+ }
+#endif
+
+ return speed;
+}
+#endif /* USB_OTG_FS || USB_OTG_HS */
+
+static int usb_dc_stm32_init(void)
+{
+ HAL_StatusTypeDef status;
+ unsigned int i;
+
+#ifdef USB
+ usb_dc_stm32_state.pcd.Instance = USB;
+ usb_dc_stm32_state.pcd.Init.speed = PCD_SPEED_FULL;
+ usb_dc_stm32_state.pcd.Init.dev_endpoints = USB_NUM_BIDIR_ENDPOINTS;
+ usb_dc_stm32_state.pcd.Init.phy_itface = PCD_PHY_EMBEDDED;
+ usb_dc_stm32_state.pcd.Init.ep0_mps = PCD_EP0MPS_64;
+ usb_dc_stm32_state.pcd.Init.low_power_enable = 0;
+#else /* USB_OTG_FS || USB_OTG_HS */
+#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
+ usb_dc_stm32_state.pcd.Instance = USB_OTG_HS;
+#else
+ usb_dc_stm32_state.pcd.Instance = USB_OTG_FS;
+#endif
+ usb_dc_stm32_state.pcd.Init.dev_endpoints = USB_NUM_BIDIR_ENDPOINTS;
+ usb_dc_stm32_state.pcd.Init.speed = usb_dc_stm32_get_maximum_speed();
+#if USB_OTG_HS_EMB_PHY
+ usb_dc_stm32_state.pcd.Init.phy_itface = USB_OTG_HS_EMBEDDED_PHY;
+#else
+ usb_dc_stm32_state.pcd.Init.phy_itface = PCD_PHY_EMBEDDED;
+#endif
+ usb_dc_stm32_state.pcd.Init.ep0_mps = USB_OTG_MAX_EP0_SIZE;
+ usb_dc_stm32_state.pcd.Init.vbus_sensing_enable = DISABLE;
+
+#ifndef CONFIG_SOC_SERIES_STM32F1X
+ usb_dc_stm32_state.pcd.Init.dma_enable = DISABLE;
+#endif
+
+#endif /* USB */
+
+#ifdef CONFIG_USB_DEVICE_SOF
+ usb_dc_stm32_state.pcd.Init.Sof_enable = 1;
+#endif /* CONFIG_USB_DEVICE_SOF */
+
+ LOG_DBG("Pinctrl signals configuration");
+ status = stm32_dt_pinctrl_configure(usb_pinctrl,
+ ARRAY_SIZE(usb_pinctrl),
+ (uint32_t)usb_dc_stm32_state.pcd.Instance);
+ if (status < 0) {
+ LOG_ERR("USB pinctrl setup failed (%d)", status);
+ return status;
+ }
+
+ LOG_DBG("HAL_PCD_Init");
+ status = HAL_PCD_Init(&usb_dc_stm32_state.pcd);
+ if (status != HAL_OK) {
+ LOG_ERR("PCD_Init failed, %d", (int)status);
+ return -EIO;
+ }
+
+ LOG_DBG("HAL_PCD_Start");
+ status = HAL_PCD_Start(&usb_dc_stm32_state.pcd);
+ if (status != HAL_OK) {
+ LOG_ERR("PCD_Start failed, %d", (int)status);
+ return -EIO;
+ }
+
+ usb_dc_stm32_state.out_ep_state[EP0_IDX].ep_mps = EP0_MPS;
+ usb_dc_stm32_state.out_ep_state[EP0_IDX].ep_type = EP_TYPE_CTRL;
+ usb_dc_stm32_state.in_ep_state[EP0_IDX].ep_mps = EP0_MPS;
+ usb_dc_stm32_state.in_ep_state[EP0_IDX].ep_type = EP_TYPE_CTRL;
+
+#ifdef USB
+ /* Start PMA configuration for the endpoints after the BTABLE. */
+ usb_dc_stm32_state.pma_offset = USB_BTABLE_SIZE;
+
+ for (i = 0U; i < USB_NUM_BIDIR_ENDPOINTS; i++) {
+ k_sem_init(&usb_dc_stm32_state.in_ep_state[i].write_sem, 1, 1);
+ }
+#else /* USB_OTG_FS */
+ /* TODO: make this dynamic (depending usage) */
+ HAL_PCDEx_SetRxFiFo(&usb_dc_stm32_state.pcd, FIFO_EP_WORDS);
+ for (i = 0U; i < USB_NUM_BIDIR_ENDPOINTS; i++) {
+ HAL_PCDEx_SetTxFiFo(&usb_dc_stm32_state.pcd, i,
+ FIFO_EP_WORDS);
+ k_sem_init(&usb_dc_stm32_state.in_ep_state[i].write_sem, 1, 1);
+ }
+#endif /* USB */
+
+ IRQ_CONNECT(USB_IRQ, USB_IRQ_PRI,
+ usb_dc_stm32_isr, 0, 0);
+ irq_enable(USB_IRQ);
+ return 0;
+}
+
+/* Zephyr USB device controller API implementation */
+
+int usb_dc_attach(void)
+{
+ int ret;
+
+ LOG_DBG("");
+
+#ifdef SYSCFG_CFGR1_USB_IT_RMP
+ /*
+ * STM32F302/F303: USB IRQ collides with CAN_1 IRQ (§14.1.3, RM0316)
+ * Remap IRQ by default to enable use of both IPs simultaneoulsy
+ * This should be done before calling any HAL function
+ */
+ if (LL_APB2_GRP1_IsEnabledClock(LL_APB2_GRP1_PERIPH_SYSCFG)) {
+ LL_SYSCFG_EnableRemapIT_USB();
+ } else {
+ LOG_ERR("System Configuration Controller clock is "
+ "disabled. Unable to enable IRQ remapping.");
+ }
+#endif
+
+ /*
+ * For STM32F0 series SoCs on QFN28 and TSSOP20 packages enable PIN
+ * pair PA11/12 mapped instead of PA9/10 (e.g. stm32f070x6)
+ */
+#if USB_ENABLE_PIN_REMAP == 1
+ if (LL_APB1_GRP2_IsEnabledClock(LL_APB1_GRP2_PERIPH_SYSCFG)) {
+ LL_SYSCFG_EnablePinRemap();
+ } else {
+ LOG_ERR("System Configuration Controller clock is "
+ "disabled. Unable to enable pin remapping.");
+ }
+#endif
+
+ ret = usb_dc_stm32_clock_enable();
+ if (ret) {
+ return ret;
+ }
+
+ ret = usb_dc_stm32_init();
+ if (ret) {
+ return ret;
+ }
+
+ /*
+ * Required for at least STM32L4 devices as they electrically
+ * isolate USB features from VDDUSB. It must be enabled before
+ * USB can function. Refer to section 5.1.3 in DM00083560 or
+ * DM00310109.
+ */
+#ifdef PWR_CR2_USV
+#if defined(LL_APB1_GRP1_PERIPH_PWR)
+ if (LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_PWR)) {
+ LL_PWR_EnableVddUSB();
+ } else {
+ LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
+ LL_PWR_EnableVddUSB();
+ LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_PWR);
+ }
+#else
+ LL_PWR_EnableVddUSB();
+#endif /* defined(LL_APB1_GRP1_PERIPH_PWR) */
+#endif /* PWR_CR2_USV */
+
+ return 0;
+}
+
+int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+
+ LOG_DBG("ep 0x%02x", ep);
+
+ if (!ep_state) {
+ return -EINVAL;
+ }
+
+ ep_state->cb = cb;
+
+ return 0;
+}
+
+void usb_dc_set_status_callback(const usb_dc_status_callback cb)
+{
+ LOG_DBG("");
+
+ usb_dc_stm32_state.status_cb = cb;
+}
+
+int usb_dc_set_address(const uint8_t addr)
+{
+ HAL_StatusTypeDef status;
+
+ LOG_DBG("addr %u (0x%02x)", addr, addr);
+
+ status = HAL_PCD_SetAddress(&usb_dc_stm32_state.pcd, addr);
+ if (status != HAL_OK) {
+ LOG_ERR("HAL_PCD_SetAddress failed(0x%02x), %d", addr,
+ (int)status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_start_read(uint8_t ep, uint8_t *data, uint32_t max_data_len)
+{
+ HAL_StatusTypeDef status;
+
+ LOG_DBG("ep 0x%02x, len %u", ep, max_data_len);
+
+ /* we flush EP0_IN by doing a 0 length receive on it */
+ if (!USB_EP_DIR_IS_OUT(ep) && (ep != EP0_IN || max_data_len)) {
+ LOG_ERR("invalid ep 0x%02x", ep);
+ return -EINVAL;
+ }
+
+ if (max_data_len > EP_MPS) {
+ max_data_len = EP_MPS;
+ }
+
+ status = HAL_PCD_EP_Receive(&usb_dc_stm32_state.pcd, ep,
+ usb_dc_stm32_state.ep_buf[USB_EP_GET_IDX(ep)],
+ max_data_len);
+ if (status != HAL_OK) {
+ LOG_ERR("HAL_PCD_EP_Receive failed(0x%02x), %d", ep,
+ (int)status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_get_read_count(uint8_t ep, uint32_t *read_bytes)
+{
+ if (!USB_EP_DIR_IS_OUT(ep) || !read_bytes) {
+ LOG_ERR("invalid ep 0x%02x", ep);
+ return -EINVAL;
+ }
+
+ *read_bytes = HAL_PCD_EP_GetRxCount(&usb_dc_stm32_state.pcd, ep);
+
+ return 0;
+}
+
+int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data * const cfg)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(cfg->ep_addr);
+
+ LOG_DBG("ep %x, mps %d, type %d", cfg->ep_addr, cfg->ep_mps,
+ cfg->ep_type);
+
+ if ((cfg->ep_type == USB_DC_EP_CONTROL) && ep_idx) {
+ LOG_ERR("invalid endpoint configuration");
+ return -1;
+ }
+
+ if (ep_idx > (USB_NUM_BIDIR_ENDPOINTS - 1)) {
+ LOG_ERR("endpoint index/address out of range");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data * const ep_cfg)
+{
+ uint8_t ep = ep_cfg->ep_addr;
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+
+ LOG_DBG("ep 0x%02x, previous ep_mps %u, ep_mps %u, ep_type %u",
+ ep_cfg->ep_addr, ep_state->ep_mps, ep_cfg->ep_mps,
+ ep_cfg->ep_type);
+
+ if (!ep_state) {
+ return -EINVAL;
+ }
+
+#ifdef USB
+ if (ep_cfg->ep_mps > ep_state->ep_pma_buf_len) {
+ if (USB_RAM_SIZE <=
+ (usb_dc_stm32_state.pma_offset + ep_cfg->ep_mps)) {
+ return -EINVAL;
+ }
+ HAL_PCDEx_PMAConfig(&usb_dc_stm32_state.pcd, ep, PCD_SNG_BUF,
+ usb_dc_stm32_state.pma_offset);
+ ep_state->ep_pma_buf_len = ep_cfg->ep_mps;
+ usb_dc_stm32_state.pma_offset += ep_cfg->ep_mps;
+ }
+#endif
+ ep_state->ep_mps = ep_cfg->ep_mps;
+
+ switch (ep_cfg->ep_type) {
+ case USB_DC_EP_CONTROL:
+ ep_state->ep_type = EP_TYPE_CTRL;
+ break;
+ case USB_DC_EP_ISOCHRONOUS:
+ ep_state->ep_type = EP_TYPE_ISOC;
+ break;
+ case USB_DC_EP_BULK:
+ ep_state->ep_type = EP_TYPE_BULK;
+ break;
+ case USB_DC_EP_INTERRUPT:
+ ep_state->ep_type = EP_TYPE_INTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_set_stall(const uint8_t ep)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+ HAL_StatusTypeDef status;
+
+ LOG_DBG("ep 0x%02x", ep);
+
+ if (!ep_state) {
+ return -EINVAL;
+ }
+
+ status = HAL_PCD_EP_SetStall(&usb_dc_stm32_state.pcd, ep);
+ if (status != HAL_OK) {
+ LOG_ERR("HAL_PCD_EP_SetStall failed(0x%02x), %d", ep,
+ (int)status);
+ return -EIO;
+ }
+
+ ep_state->ep_stalled = 1U;
+
+ return 0;
+}
+
+int usb_dc_ep_clear_stall(const uint8_t ep)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+ HAL_StatusTypeDef status;
+
+ LOG_DBG("ep 0x%02x", ep);
+
+ if (!ep_state) {
+ return -EINVAL;
+ }
+
+ status = HAL_PCD_EP_ClrStall(&usb_dc_stm32_state.pcd, ep);
+ if (status != HAL_OK) {
+ LOG_ERR("HAL_PCD_EP_ClrStall failed(0x%02x), %d", ep,
+ (int)status);
+ return -EIO;
+ }
+
+ ep_state->ep_stalled = 0U;
+ ep_state->read_count = 0U;
+
+ return 0;
+}
+
+int usb_dc_ep_is_stalled(const uint8_t ep, uint8_t *const stalled)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+
+ LOG_DBG("ep 0x%02x", ep);
+
+ if (!ep_state || !stalled) {
+ return -EINVAL;
+ }
+
+ *stalled = ep_state->ep_stalled;
+
+ return 0;
+}
+
+int usb_dc_ep_enable(const uint8_t ep)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+ HAL_StatusTypeDef status;
+
+ LOG_DBG("ep 0x%02x", ep);
+
+ if (!ep_state) {
+ return -EINVAL;
+ }
+
+ LOG_DBG("HAL_PCD_EP_Open(0x%02x, %u, %u)", ep, ep_state->ep_mps,
+ ep_state->ep_type);
+
+ status = HAL_PCD_EP_Open(&usb_dc_stm32_state.pcd, ep,
+ ep_state->ep_mps, ep_state->ep_type);
+ if (status != HAL_OK) {
+ LOG_ERR("HAL_PCD_EP_Open failed(0x%02x), %d", ep,
+ (int)status);
+ return -EIO;
+ }
+
+ if (USB_EP_DIR_IS_OUT(ep) && ep != EP0_OUT) {
+ return usb_dc_ep_start_read(ep,
+ usb_dc_stm32_state.ep_buf[USB_EP_GET_IDX(ep)],
+ EP_MPS);
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_disable(const uint8_t ep)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+ HAL_StatusTypeDef status;
+
+ LOG_DBG("ep 0x%02x", ep);
+
+ if (!ep_state) {
+ return -EINVAL;
+ }
+
+ status = HAL_PCD_EP_Close(&usb_dc_stm32_state.pcd, ep);
+ if (status != HAL_OK) {
+ LOG_ERR("HAL_PCD_EP_Close failed(0x%02x), %d", ep,
+ (int)status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data,
+ const uint32_t data_len, uint32_t * const ret_bytes)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+ HAL_StatusTypeDef status;
+ uint32_t len = data_len;
+ int ret = 0;
+
+ LOG_DBG("ep 0x%02x, len %u", ep, data_len);
+
+ if (!ep_state || !USB_EP_DIR_IS_IN(ep)) {
+ LOG_ERR("invalid ep 0x%02x", ep);
+ return -EINVAL;
+ }
+
+ ret = k_sem_take(&ep_state->write_sem, K_NO_WAIT);
+ if (ret) {
+ LOG_ERR("Unable to get write lock (%d)", ret);
+ return -EAGAIN;
+ }
+
+ if (!k_is_in_isr()) {
+ irq_disable(USB_IRQ);
+ }
+
+ if (ep == EP0_IN && len > USB_MAX_CTRL_MPS) {
+ len = USB_MAX_CTRL_MPS;
+ }
+
+ status = HAL_PCD_EP_Transmit(&usb_dc_stm32_state.pcd, ep,
+ (void *)data, len);
+ if (status != HAL_OK) {
+ LOG_ERR("HAL_PCD_EP_Transmit failed(0x%02x), %d", ep,
+ (int)status);
+ k_sem_give(&ep_state->write_sem);
+ ret = -EIO;
+ }
+
+ if (!ret && ep == EP0_IN && len > 0) {
+ /* Wait for an empty package as from the host.
+ * This also flushes the TX FIFO to the host.
+ */
+ usb_dc_ep_start_read(ep, NULL, 0);
+ }
+
+ if (!k_is_in_isr()) {
+ irq_enable(USB_IRQ);
+ }
+
+ if (!ret && ret_bytes) {
+ *ret_bytes = len;
+ }
+
+ return ret;
+}
+
+int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
+ uint32_t *read_bytes)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+ uint32_t read_count;
+
+ if (!ep_state) {
+ LOG_ERR("Invalid Endpoint %x", ep);
+ return -EINVAL;
+ }
+
+ read_count = ep_state->read_count;
+
+ LOG_DBG("ep 0x%02x, %u bytes, %u+%u, %p", ep, max_data_len,
+ ep_state->read_offset, read_count, data);
+
+ if (!USB_EP_DIR_IS_OUT(ep)) { /* check if OUT ep */
+ LOG_ERR("Wrong endpoint direction: 0x%02x", ep);
+ return -EINVAL;
+ }
+
+ /* When both buffer and max data to read are zero, just ingore reading
+ * and return available data in buffer. Otherwise, return data
+ * previously stored in the buffer.
+ */
+ if (data) {
+ read_count = MIN(read_count, max_data_len);
+ memcpy(data, usb_dc_stm32_state.ep_buf[USB_EP_GET_IDX(ep)] +
+ ep_state->read_offset, read_count);
+ ep_state->read_count -= read_count;
+ ep_state->read_offset += read_count;
+ } else if (max_data_len) {
+ LOG_ERR("Wrong arguments");
+ }
+
+ if (read_bytes) {
+ *read_bytes = read_count;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_read_continue(uint8_t ep)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+
+ if (!ep_state || !USB_EP_DIR_IS_OUT(ep)) { /* Check if OUT ep */
+ LOG_ERR("Not valid endpoint: %02x", ep);
+ return -EINVAL;
+ }
+
+ /* If no more data in the buffer, start a new read transaction.
+ * DataOutStageCallback will called on transaction complete.
+ */
+ if (!ep_state->read_count) {
+ usb_dc_ep_start_read(ep, usb_dc_stm32_state.ep_buf[USB_EP_GET_IDX(ep)],
+ EP_MPS);
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_read(const uint8_t ep, uint8_t *const data, const uint32_t max_data_len,
+ uint32_t * const read_bytes)
+{
+ if (usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes) != 0) {
+ return -EINVAL;
+ }
+
+ if (usb_dc_ep_read_continue(ep) != 0) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int usb_dc_ep_halt(const uint8_t ep)
+{
+ return usb_dc_ep_set_stall(ep);
+}
+
+int usb_dc_ep_flush(const uint8_t ep)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+
+ if (!ep_state) {
+ return -EINVAL;
+ }
+
+ LOG_ERR("Not implemented");
+
+ return 0;
+}
+
+int usb_dc_ep_mps(const uint8_t ep)
+{
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+
+ if (!ep_state) {
+ return -EINVAL;
+ }
+
+ return ep_state->ep_mps;
+}
+
+int usb_dc_detach(void)
+{
+ LOG_ERR("Not implemented");
+
+#ifdef CONFIG_SOC_SERIES_STM32WBX
+ /* Specially for STM32WB, unlock the HSEM when USB is no more used. */
+ z_stm32_hsem_unlock(CFG_HW_CLK48_CONFIG_SEMID);
+
+ /*
+ * TODO: AN5289 notes a process of locking Sem0, with possible waits
+ * via interrupts before switching off CLK48, but lacking any actual
+ * examples of that, that remains to be implemented. See
+ * https://github.com/zephyrproject-rtos/zephyr/pull/25850
+ */
+#endif /* CONFIG_SOC_SERIES_STM32WBX */
+
+ return 0;
+}
+
+int usb_dc_reset(void)
+{
+ LOG_ERR("Not implemented");
+
+ return 0;
+}
+
+/* Callbacks from the STM32 Cube HAL code */
+
+void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
+{
+ int i;
+
+ LOG_DBG("");
+
+ HAL_PCD_EP_Open(&usb_dc_stm32_state.pcd, EP0_IN, EP0_MPS, EP_TYPE_CTRL);
+ HAL_PCD_EP_Open(&usb_dc_stm32_state.pcd, EP0_OUT, EP0_MPS,
+ EP_TYPE_CTRL);
+
+ /* The DataInCallback will never be called at this point for any pending
+ * transactions. Reset the IN semaphores to prevent perpetual locked state.
+ * */
+ for (i = 0; i < USB_NUM_BIDIR_ENDPOINTS; i++) {
+ k_sem_give(&usb_dc_stm32_state.in_ep_state[i].write_sem);
+ }
+
+ if (usb_dc_stm32_state.status_cb) {
+ usb_dc_stm32_state.status_cb(USB_DC_RESET, NULL);
+ }
+}
+
+void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd)
+{
+ LOG_DBG("");
+
+ if (usb_dc_stm32_state.status_cb) {
+ usb_dc_stm32_state.status_cb(USB_DC_CONNECTED, NULL);
+ }
+}
+
+void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
+{
+ LOG_DBG("");
+
+ if (usb_dc_stm32_state.status_cb) {
+ usb_dc_stm32_state.status_cb(USB_DC_DISCONNECTED, NULL);
+ }
+}
+
+void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
+{
+ LOG_DBG("");
+
+ if (usb_dc_stm32_state.status_cb) {
+ usb_dc_stm32_state.status_cb(USB_DC_SUSPEND, NULL);
+ }
+}
+
+void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd)
+{
+ LOG_DBG("");
+
+ if (usb_dc_stm32_state.status_cb) {
+ usb_dc_stm32_state.status_cb(USB_DC_RESUME, NULL);
+ }
+}
+
+void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
+{
+ struct usb_setup_packet *setup = (void *)usb_dc_stm32_state.pcd.Setup;
+ struct usb_dc_stm32_ep_state *ep_state;
+
+ LOG_DBG("");
+
+ ep_state = usb_dc_stm32_get_ep_state(EP0_OUT); /* can't fail for ep0 */
+ __ASSERT(ep_state, "No corresponding ep_state for EP0");
+
+ ep_state->read_count = SETUP_SIZE;
+ ep_state->read_offset = 0U;
+ memcpy(&usb_dc_stm32_state.ep_buf[EP0_IDX],
+ usb_dc_stm32_state.pcd.Setup, ep_state->read_count);
+
+ if (ep_state->cb) {
+ ep_state->cb(EP0_OUT, USB_DC_EP_SETUP);
+
+ if (!(setup->wLength == 0U) &&
+ !(REQTYPE_GET_DIR(setup->bmRequestType) ==
+ REQTYPE_DIR_TO_HOST)) {
+ usb_dc_ep_start_read(EP0_OUT,
+ usb_dc_stm32_state.ep_buf[EP0_IDX],
+ setup->wLength);
+ }
+ }
+}
+
+void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(epnum);
+ uint8_t ep = ep_idx | USB_EP_DIR_OUT;
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+
+ LOG_DBG("epnum 0x%02x, rx_count %u", epnum,
+ HAL_PCD_EP_GetRxCount(&usb_dc_stm32_state.pcd, epnum));
+
+ /* Transaction complete, data is now stored in the buffer and ready
+ * for the upper stack (usb_dc_ep_read to retrieve).
+ */
+ usb_dc_ep_get_read_count(ep, &ep_state->read_count);
+ ep_state->read_offset = 0U;
+
+ if (ep_state->cb) {
+ ep_state->cb(ep, USB_DC_EP_DATA_OUT);
+ }
+}
+
+void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
+{
+ uint8_t ep_idx = USB_EP_GET_IDX(epnum);
+ uint8_t ep = ep_idx | USB_EP_DIR_IN;
+ struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
+
+ LOG_DBG("epnum 0x%02x", epnum);
+
+ __ASSERT(ep_state, "No corresponding ep_state for ep");
+
+ k_sem_give(&ep_state->write_sem);
+
+ if (ep_state->cb) {
+ ep_state->cb(ep, USB_DC_EP_DATA_IN);
+ }
+}
+
+#if defined(USB) && defined(CONFIG_USB_DC_STM32_DISCONN_ENABLE)
+void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state)
+{
+ const struct device *usb_disconnect;
+
+ usb_disconnect = device_get_binding(
+ DT_GPIO_LABEL(DT_INST(0, st_stm32_usb), disconnect_gpios));
+
+ gpio_pin_configure(usb_disconnect,
+ DT_GPIO_PIN(DT_INST(0, st_stm32_usb), disconnect_gpios),
+ DT_GPIO_FLAGS(DT_INST(0, st_stm32_usb), disconnect_gpios) |
+ (state ? GPIO_OUTPUT_ACTIVE : GPIO_OUTPUT_INACTIVE));
+}
+#endif /* USB && CONFIG_USB_DC_STM32_DISCONN_ENABLE */
diff --git a/port/stm32/back/usbd_STM32F103.c b/port/stm32/back/usbd_STM32F103.c
new file mode 100644
index 00000000..319d1166
--- /dev/null
+++ b/port/stm32/back/usbd_STM32F103.c
@@ -0,0 +1,770 @@
+/*
+ * Copyright (c) 2004-2016 ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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.
+ */
+
+/*----------------------------------------------------------------------------
+* RL-ARM - USB
+*----------------------------------------------------------------------------
+* Name: usbd_STM32F103.c
+* Purpose: Hardware Layer module for ST STM32F103
+* Rev.: V4.70
+*---------------------------------------------------------------------------*/
+
+/* Double Buffering is not supported */
+
+#include <rl_usb.h>
+#include "stm32f1xx.h"
+#include "usbreg.h"
+#include "IO_Config.h"
+#include "cortex_m.h"
+#include "string.h"
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+#define USB_ISTR_W0C_MASK (ISTR_PMAOVR | ISTR_ERR | ISTR_WKUP | ISTR_SUSP | ISTR_RESET | ISTR_SOF | ISTR_ESOF)
+#define VAL_MASK 0xFFFF
+#define VAL_SHIFT 16
+#define EP_NUM_MASK 0xFFFF
+#define EP_NUM_SHIFT 0
+
+#define USB_DBL_BUF_EP 0x0000
+
+#define EP_BUF_ADDR (sizeof(EP_BUF_DSCR)*(USBD_EP_NUM+1)) /* Endpoint Buf Adr */
+
+EP_BUF_DSCR *pBUF_DSCR = (EP_BUF_DSCR *)USB_PMA_ADDR; /* Ptr to EP Buf Desc */
+
+U16 FreeBufAddr; /* Endpoint Free Buffer Address */
+
+uint32_t StatQueue[(USBD_EP_NUM + 1) * 2 + 1];
+uint32_t StatQueueHead = 0;
+uint32_t StatQueueTail = 0;
+uint32_t LastIstr = 0;
+
+
+inline static void stat_enque(uint32_t stat)
+{
+ cortex_int_state_t state;
+ state = cortex_int_get_and_disable();
+ StatQueue[StatQueueTail] = stat;
+ StatQueueTail = (StatQueueTail + 1) % (sizeof(StatQueue) / sizeof(StatQueue[0]));
+ cortex_int_restore(state);
+}
+
+inline static uint32_t stat_deque()
+{
+ cortex_int_state_t state;
+ uint32_t stat;
+ state = cortex_int_get_and_disable();
+ stat = StatQueue[StatQueueHead];
+ StatQueueHead = (StatQueueHead + 1) % (sizeof(StatQueue) / sizeof(StatQueue[0]));
+ cortex_int_restore(state);
+
+ return stat;
+}
+
+inline static uint32_t stat_is_empty()
+{
+ cortex_int_state_t state;
+ uint32_t empty;
+ state = cortex_int_get_and_disable();
+ empty = StatQueueHead == StatQueueTail;
+ cortex_int_restore(state);
+ return empty;
+}
+
+
+/*
+ * Reset Endpoint
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void EP_Reset(U32 EPNum)
+{
+ U32 num, val;
+ num = EPNum & 0x0F;
+ val = EPxREG(num);
+
+ if (EPNum & 0x80) { /* IN Endpoint */
+ EPxREG(num) = val & (EP_MASK | EP_DTOG_TX);
+ } else { /* OUT Endpoint */
+ EPxREG(num) = val & (EP_MASK | EP_DTOG_RX);
+ }
+}
+
+
+/*
+ * Set Endpoint Status
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * stat: New Status
+ * Return Value: None
+ */
+
+void EP_Status(U32 EPNum, U32 stat)
+{
+ U32 num, val;
+ num = EPNum & 0x0F;
+ val = EPxREG(num);
+
+ if (EPNum & 0x80) { /* IN Endpoint */
+ EPxREG(num) = EP_VAL_UNCHANGED(val) | ((val ^ stat) & EP_STAT_TX);
+ } else { /* OUT Endpoint */
+ EPxREG(num) = EP_VAL_UNCHANGED(val) | ((val ^ stat) & EP_STAT_RX);
+ }
+}
+
+
+/*
+ * USB Device Interrupt enable
+ * Called by USBD_Init to enable the USB Interrupt
+ * Return Value: None
+ */
+
+#ifdef __RTX
+void __svc(1) USBD_IntrEna(void);
+void __SVC_1(void)
+{
+#else
+void USBD_IntrEna(void)
+{
+#endif
+ NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
+}
+
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB
+ * Return Value: None
+ */
+
+void USBD_Init(void)
+{
+ RCC->APB1ENR |= (1 << 23); /* enable clock for USB */
+ USBD_IntrEna(); /* Enable USB Interrupts */
+ /* Control USB connecting via SW */
+ USB_CONNECT_OFF();
+}
+
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+
+void USBD_Connect(BOOL con)
+{
+ if (con) {
+ CNTR = CNTR_FRES; /* Force USB Reset */
+ CNTR = 0;
+ ISTR = 0; /* Clear Interrupt Status */
+ CNTR = CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM; /* USB Interrupt Mask */
+ USB_CONNECT_ON();
+ } else {
+ CNTR = CNTR_FRES | CNTR_PDWN; /* Switch Off USB Device */
+ USB_CONNECT_OFF();
+ }
+}
+
+
+/*
+ * USB Device Reset Function
+ * Called automatically on USB Device Reset
+ * Return Value: None
+ */
+
+void USBD_Reset(void)
+{
+ NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
+
+ /* Double Buffering is not yet supported */
+ ISTR = 0; /* Clear Interrupt Status */
+ CNTR = CNTR_CTRM | CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM |
+#ifdef __RTX
+ ((USBD_RTX_DevTask != 0) ? CNTR_ERRM : 0) |
+ ((USBD_RTX_DevTask != 0) ? CNTR_PMAOVRM : 0) |
+ ((USBD_RTX_DevTask != 0) ? CNTR_SOFM : 0) |
+ ((USBD_RTX_DevTask != 0) ? CNTR_ESOFM : 0);
+#else
+ ((USBD_P_Error_Event != 0) ? CNTR_ERRM : 0) |
+ ((USBD_P_Error_Event != 0) ? CNTR_PMAOVRM : 0) |
+ ((USBD_P_SOF_Event != 0) ? CNTR_SOFM : 0) |
+ ((USBD_P_SOF_Event != 0) ? CNTR_ESOFM : 0);
+#endif
+ FreeBufAddr = EP_BUF_ADDR;
+ BTABLE = 0x00; /* set BTABLE Address */
+ /* Setup Control Endpoint 0 */
+ pBUF_DSCR->ADDR_TX = FreeBufAddr;
+ FreeBufAddr += USBD_MAX_PACKET0;
+ pBUF_DSCR->ADDR_RX = FreeBufAddr;
+ FreeBufAddr += USBD_MAX_PACKET0;
+
+ if (USBD_MAX_PACKET0 > 62) {
+ pBUF_DSCR->COUNT_RX = ((USBD_MAX_PACKET0 << 5) - 1) | 0x8000;
+ } else {
+ pBUF_DSCR->COUNT_RX = USBD_MAX_PACKET0 << 9;
+ }
+
+ EPxREG(0) = EP_CONTROL | EP_RX_VALID;
+ DADDR = DADDR_EF | 0; /* Enable USB Default Address */
+
+ NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
+}
+
+
+/*
+ * USB Device Suspend Function
+ * Called automatically on USB Device Suspend
+ * Return Value: None
+ */
+
+void USBD_Suspend(void)
+{
+ CNTR |= CNTR_FSUSP; /* Force Suspend */
+ CNTR |= CNTR_LPMODE; /* Low Power Mode */
+}
+
+
+/*
+ * USB Device Resume Function
+ * Called automatically on USB Device Resume
+ * Return Value: None
+ */
+
+void USBD_Resume(void)
+{
+ /* Performed by Hardware */
+}
+
+
+/*
+ * USB Device Remote Wakeup Function
+ * Called automatically on USB Device Remote Wakeup
+ * Return Value: None
+ */
+
+void USBD_WakeUp(void)
+{
+ CNTR &= ~CNTR_FSUSP; /* Clear Suspend */
+}
+
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+
+void USBD_WakeUpCfg(BOOL cfg)
+{
+ /* Not needed */
+}
+
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * setup: Called in setup stage (!=0), else after status stage
+ * Return Value: None
+ */
+
+void USBD_SetAddress(U32 adr, U32 setup)
+{
+ if (setup) {
+ return;
+ }
+
+ DADDR = DADDR_EF | adr;
+}
+
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+
+void USBD_Configure(BOOL cfg)
+{
+ if (cfg == __FALSE) {
+ FreeBufAddr = EP_BUF_ADDR;
+ FreeBufAddr += 2 * USBD_MAX_PACKET0; /* reset Buffer address */
+ }
+}
+
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+
+void USBD_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ /* Double Buffering is not yet supported */
+ U32 num, val;
+ num = pEPD->bEndpointAddress & 0x0F;
+ val = pEPD->wMaxPacketSize;
+
+ if (pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) {
+ (pBUF_DSCR + num)->ADDR_TX = FreeBufAddr;
+ val = (val + 1) & ~1;
+ } else {
+ (pBUF_DSCR + num)->ADDR_RX = FreeBufAddr;
+
+ if (val > 62) {
+ val = (val + 31) & ~31;
+ (pBUF_DSCR + num)->COUNT_RX = ((val << 5) - 1) | 0x8000;
+ } else {
+ val = (val + 1) & ~1;
+ (pBUF_DSCR + num)->COUNT_RX = val << 9;
+ }
+ }
+
+ FreeBufAddr += val;
+
+ switch (pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK) {
+ case USB_ENDPOINT_TYPE_CONTROL:
+ val = EP_CONTROL;
+ break;
+
+ case USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ val = EP_ISOCHRONOUS;
+ break;
+
+ case USB_ENDPOINT_TYPE_BULK:
+ val = EP_BULK;
+
+ if (USB_DBL_BUF_EP & (1 << num)) {
+ val |= EP_KIND;
+ }
+
+ break;
+
+ case USB_ENDPOINT_TYPE_INTERRUPT:
+ val = EP_INTERRUPT;
+ break;
+ }
+
+ val |= num;
+ EPxREG(num) = val;
+}
+
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+
+void USBD_DirCtrlEP(U32 dir)
+{
+ /* Not needed */
+}
+
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_EnableEP(U32 EPNum)
+{
+ EP_Status(EPNum, EP_TX_NAK | EP_RX_VALID); /* EP is able to receive */
+}
+
+
+/*
+ * Disable USB Endpoint
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_DisableEP(U32 EPNum)
+{
+ EP_Status(EPNum, EP_TX_DIS | EP_RX_DIS);
+}
+
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ResetEP(U32 EPNum)
+{
+ EP_Reset(EPNum);
+}
+
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_SetStallEP(U32 EPNum)
+{
+ EP_Status(EPNum, EP_TX_STALL | EP_RX_STALL);
+}
+
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClrStallEP(U32 EPNum)
+{
+ EP_Reset(EPNum); /* reset DTog Bits */
+ EP_Status(EPNum, EP_TX_VALID | EP_RX_VALID);
+}
+
+
+/*
+ * Clear USB Device Endpoint Buffer
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClearEPBuf(U32 EPNum)
+{
+ ;
+}
+
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+
+U32 USBD_ReadEP(U32 EPNum, U8 *pData, U32 bufsz)
+{
+ /* Double Buffering is not yet supported */
+ U32 num, cnt, *pv, n;
+ num = EPNum & 0x0F;
+ pv = (U32 *)(USB_PMA_ADDR + 2 * ((pBUF_DSCR + num)->ADDR_RX));
+ cnt = (pBUF_DSCR + num)->COUNT_RX & EP_COUNT_MASK;
+ if (cnt > bufsz) {
+ cnt = bufsz;
+ }
+
+ for (n = 0; n < (cnt + 1) / 2; n++) {
+ *((__packed U16 *)pData) = *pv++;
+ pData += 2;
+ }
+
+ EP_Status(EPNum, EP_RX_VALID);
+ return (cnt);
+}
+
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+
+U32 USBD_WriteEP(U32 EPNum, U8 *pData, U32 cnt)
+{
+ /* Double Buffering is not yet supported */
+ U32 num, *pv, n;
+ U16 statusEP;
+ num = EPNum & 0x0F;
+ pv = (U32 *)(USB_PMA_ADDR + 2 * ((pBUF_DSCR + num)->ADDR_TX));
+
+ for (n = 0; n < (cnt + 1) / 2; n++) {
+ *pv++ = *((__packed U16 *)pData);
+ pData += 2;
+ }
+
+ (pBUF_DSCR + num)->COUNT_TX = cnt;
+ statusEP = EPxREG(num);
+
+ if ((statusEP & EP_STAT_TX) != EP_TX_STALL) {
+ EP_Status(EPNum, EP_TX_VALID); /* do not make EP valid if stalled */
+ }
+
+ return (cnt);
+}
+
+
+/*
+ * Get USB Device Last Frame Number
+ * Parameters: None
+ * Return Value: Frame Number
+ */
+
+U32 USBD_GetFrame(void)
+{
+ return (FNR & FNR_FN);
+}
+
+
+#ifdef __RTX
+U32 LastError; /* Last Error */
+
+/*
+ * Get USB Last Error Code
+ * Parameters: None
+ * Return Value: Error Code
+ */
+
+U32 USBD_GetError(void)
+{
+ return (LastError);
+}
+#endif
+
+
+/*
+ * USB Device Interrupt Service Routine
+ */
+
+void USB_LP_CAN1_RX0_IRQHandler(void)
+{
+ uint32_t istr;
+ uint32_t num;
+ uint32_t val;
+
+ istr = ISTR;
+ // Zero out endpoint ID since this is read from the queue
+ LastIstr |= istr & ~(ISTR_DIR | ISTR_EP_ID);
+ // Clear interrupts that are pending
+ ISTR = ~(istr & USB_ISTR_W0C_MASK);
+ if (istr & ISTR_CTR) {
+ while ((istr = ISTR) & ISTR_CTR) {
+ num = istr & ISTR_EP_ID;
+ val = EPxREG(num);
+
+ // Process and filter out the zero length status out endpoint to prevent
+ // the next SETUP packet from being dropped.
+ if ((0 == num) && (val & EP_CTR_RX) && !(val & EP_SETUP)
+ && (0 == ((pBUF_DSCR + num)->COUNT_RX & EP_COUNT_MASK))) {
+ if (val & EP_CTR_TX) {
+ // Drop the RX event but not TX
+ stat_enque((((val & VAL_MASK) & ~EP_CTR_RX) << VAL_SHIFT) |
+ ((num & EP_NUM_MASK) << EP_NUM_SHIFT));
+ } else {
+ // Drop the event
+ }
+ } else {
+ stat_enque(((val & VAL_MASK) << VAL_SHIFT) |
+ ((num & EP_NUM_MASK) << EP_NUM_SHIFT));
+ }
+
+
+ if (val & EP_CTR_RX) {
+ EPxREG(num) = EP_VAL_UNCHANGED(val) & ~EP_CTR_RX;
+ }
+
+ if (val & EP_CTR_TX) {
+ EPxREG(num) = EP_VAL_UNCHANGED(val) & ~EP_CTR_TX;
+ }
+ }
+ }
+
+ USBD_SignalHandler();
+}
+
+void USBD_Handler(void)
+{
+ U32 istr, num, val, num_val;
+ cortex_int_state_t state;
+
+ // Get ISTR
+ state = cortex_int_get_and_disable();
+ istr = LastIstr;
+ LastIstr = 0;
+ cortex_int_restore(state);
+
+ /* USB Reset Request */
+ if (istr & ISTR_RESET) {
+ USBD_Reset();
+ usbd_reset_core();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Reset_Event) {
+ USBD_P_Reset_Event();
+ }
+
+#endif
+ }
+
+ /* USB Suspend Request */
+ if (istr & ISTR_SUSP) {
+ USBD_Suspend();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Suspend_Event) {
+ USBD_P_Suspend_Event();
+ }
+
+#endif
+ }
+
+ /* USB Wakeup */
+ if (istr & ISTR_WKUP) {
+ USBD_WakeUp();
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Resume_Event) {
+ USBD_P_Resume_Event();
+ }
+
+#endif
+ }
+
+ /* Start of Frame */
+ if (istr & ISTR_SOF) {
+#ifdef __RTX
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_SOF, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+
+#endif
+ }
+
+ /* PMA Over/underrun */
+ if (istr & ISTR_PMAOVR) {
+#ifdef __RTX
+ LastError = 2;
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_ERROR, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Error_Event) {
+ USBD_P_Error_Event(2);
+ }
+
+#endif
+ }
+
+ /* Error: No Answer, CRC Error, Bit Stuff Error, Frame Format Error */
+ if (istr & ISTR_ERR) {
+#ifdef __RTX
+ LastError = 1;
+
+ if (USBD_RTX_DevTask) {
+ isr_evt_set(USBD_EVT_ERROR, USBD_RTX_DevTask);
+ }
+
+#else
+
+ if (USBD_P_Error_Event) {
+ USBD_P_Error_Event(1);
+ }
+
+#endif
+ }
+
+ /* Endpoint Interrupts */
+ while ((istr & ISTR_CTR) && !stat_is_empty()) {
+ num_val = stat_deque();
+ num = (num_val >> EP_NUM_SHIFT) & EP_NUM_MASK;
+ val = (num_val >> VAL_SHIFT) & VAL_MASK;
+ if (val & EP_CTR_TX) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num]) {
+ isr_evt_set(USBD_EVT_IN, USBD_RTX_EPTask[num]);
+ }
+
+#else
+
+ if (USBD_P_EP[num]) {
+ USBD_P_EP[num](USBD_EVT_IN);
+ }
+
+#endif
+ }
+
+ if (val & EP_CTR_RX) {
+#ifdef __RTX
+
+ if (USBD_RTX_EPTask[num]) {
+ isr_evt_set((val & EP_SETUP) ? USBD_EVT_SETUP : USBD_EVT_OUT, USBD_RTX_EPTask[num]);
+ }
+
+#else
+
+ if (USBD_P_EP[num]) {
+ USBD_P_EP[num]((val & EP_SETUP) ? USBD_EVT_SETUP : USBD_EVT_OUT);
+ }
+
+#endif
+ }
+ }
+}
diff --git a/port/stm32/back/usbd_STM32F205.c b/port/stm32/back/usbd_STM32F205.c
new file mode 100644
index 00000000..bd3878d6
--- /dev/null
+++ b/port/stm32/back/usbd_STM32F205.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) 2004-2016 ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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.
+ */
+
+/*----------------------------------------------------------------------------
+* RL-ARM - USB
+*----------------------------------------------------------------------------
+* Name: usbd_STM32F103.c
+* Purpose: Hardware Layer module for ST STM32F103
+* Rev.: V4.70
+*---------------------------------------------------------------------------*/
+
+/* Double Buffering is not supported */
+
+#include <rl_usb.h>
+#include "stm32f2xx.h"
+#include "IO_Config.h"
+#include "cortex_m.h"
+#include "string.h"
+#include <stdio.h>
+
+#define __NO_USB_LIB_C
+#include "usb_config.c"
+
+#define USB_ISTR_W0C_MASK (ISTR_PMAOVR | ISTR_ERR | ISTR_WKUP | ISTR_SUSP | ISTR_RESET | ISTR_SOF | ISTR_ESOF)
+#define VAL_MASK 0xFFFF
+#define VAL_SHIFT 16
+#define EP_NUM_MASK 0xFFFF
+#define EP_NUM_SHIFT 0
+
+#define USB_DBL_BUF_EP 0x0000
+#define USB_DEVICE ((USB_OTG_DeviceTypeDef *)(USB_OTG_HS_PERIPH_BASE + USB_OTG_DEVICE_BASE))
+#define USB_INEP(i) ((USB_OTG_INEndpointTypeDef *)(USB_OTG_HS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE)))
+#define USB_OUTEP(i) ((USB_OTG_OUTEndpointTypeDef *)(USB_OTG_HS_PERIPH_BASE + USB_OTG_OUT_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE)))
+#define USB_DFIFO(i) *(__IO uint32_t *)(USB_OTG_HS_PERIPH_BASE + USB_OTG_FIFO_BASE + ((i) * USB_OTG_FIFO_SIZE))
+
+
+UART_HandleTypeDef huart1;
+uint32_t gint_flag = 0;
+uint32_t rx_state = 0;
+uint32_t recbuf32[5][512];
+uint32_t recstate[5];
+
+/*
+ * USB Device Interrupt enable
+ * Called by USBD_Init to enable the USB Interrupt
+ * Return Value: None
+ */
+
+#ifdef __RTX
+void __svc(1) USBD_IntrEna(void);
+void __SVC_1(void)
+{
+#else
+void USBD_IntrEna(void)
+{
+#endif
+ HAL_NVIC_SetPriority(OTG_HS_EP1_OUT_IRQn, 0, 0);
+ HAL_NVIC_EnableIRQ(OTG_HS_EP1_OUT_IRQn);
+ HAL_NVIC_SetPriority(OTG_HS_EP1_IN_IRQn, 0, 0);
+ HAL_NVIC_EnableIRQ(OTG_HS_EP1_IN_IRQn);
+ HAL_NVIC_SetPriority(OTG_HS_IRQn, 0, 0);
+ HAL_NVIC_EnableIRQ(OTG_HS_IRQn);
+}
+
+/*
+ * USB Device Initialize Function
+ * Called by the User to initialize USB
+ * Return Value: None
+ */
+void USBD_Init(void)
+{
+ U8 i;
+ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
+ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
+ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
+ GPIOA->MODER &= ~(GPIO_MODER_MODE3 | GPIO_MODER_MODE5);
+ GPIOA->MODER |= 2U << GPIO_MODER_MODER3_Pos | 2U << GPIO_MODER_MODER5_Pos;
+ GPIOA->OTYPER &= ~(GPIO_OTYPER_OT_3 | GPIO_OTYPER_OT_5);
+ GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3 | GPIO_OSPEEDER_OSPEEDR5;
+ GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR3 | GPIO_PUPDR_PUPDR5);
+ GPIOA->AFR[0] &= ~(GPIO_AFRL_AFRL3 | GPIO_AFRL_AFRL5);
+ GPIOA->AFR[0] |= 0xAU << 12 | 0xAU << 20;
+
+ GPIOB->MODER &= ~(GPIO_MODER_MODE0 | GPIO_MODER_MODE1 | GPIO_MODER_MODE5 | GPIO_MODER_MODE10
+ | GPIO_MODER_MODE11 | GPIO_MODER_MODE12 | GPIO_MODER_MODE13);
+ GPIOB->MODER |= 2U << GPIO_MODER_MODER0_Pos | 2U << GPIO_MODER_MODER1_Pos | 2U << GPIO_MODER_MODER5_Pos
+ | 2U << GPIO_MODER_MODER10_Pos | 2U << GPIO_MODER_MODER11_Pos | 2U << GPIO_MODER_MODER12_Pos
+ | 2U << GPIO_MODER_MODER13_Pos;
+ GPIOB->OTYPER &= ~(GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_1 | GPIO_OTYPER_OT_5 | GPIO_OTYPER_OT_10
+ | GPIO_OTYPER_OT_11 | GPIO_OTYPER_OT_12 | GPIO_OTYPER_OT_13);
+ GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR0 | GPIO_OSPEEDER_OSPEEDR1 | GPIO_OSPEEDER_OSPEEDR5
+ | GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12 | GPIO_OSPEEDER_OSPEEDR13;
+ GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR0 | GPIO_PUPDR_PUPDR1 | GPIO_PUPDR_PUPDR5 | GPIO_PUPDR_PUPDR10
+ | GPIO_PUPDR_PUPDR11 | GPIO_PUPDR_PUPDR12 | GPIO_PUPDR_PUPDR13);
+ GPIOB->AFR[0] &= ~(GPIO_AFRL_AFRL0 | GPIO_AFRL_AFRL1 | GPIO_AFRL_AFRL5);
+ GPIOB->AFR[0] |= 0xAU << 0 | 0xAU << 4 | 0xAU << 20;
+ GPIOB->AFR[1] &= ~(GPIO_AFRH_AFRH2| GPIO_AFRH_AFRH3 | GPIO_AFRH_AFRH4 | GPIO_AFRH_AFRH5);
+ GPIOB->AFR[1] |= 0xAU << 8 | 0xAU << 12 | 0xAU << 16 | 0xAU << 20;
+
+ GPIOC->MODER &= ~(GPIO_MODER_MODE0 | GPIO_MODER_MODE2 | GPIO_MODER_MODE3);
+ GPIOC->MODER |= 2U << GPIO_MODER_MODER0_Pos | 2U << GPIO_MODER_MODER2_Pos | 2U << GPIO_MODER_MODER3_Pos;
+ GPIOC->OTYPER &= ~(GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_2 | GPIO_OTYPER_OT_3);
+ GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR0 | GPIO_OSPEEDER_OSPEEDR2 | GPIO_OSPEEDER_OSPEEDR3;
+ GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPDR0 | GPIO_PUPDR_PUPDR2 | GPIO_PUPDR_PUPDR3);
+ GPIOC->AFR[0] &= ~(GPIO_AFRL_AFRL0 | GPIO_AFRL_AFRL2 | GPIO_AFRL_AFRL3);
+ GPIOC->AFR[0] |= 0xAU << 0 | 0xAU << 8 | 0xAU << 12;
+
+
+ RCC->AHB1ENR |= RCC_AHB1ENR_OTGHSEN;
+ RCC->AHB1ENR |= RCC_AHB1ENR_OTGHSULPIEN;
+
+ USB_OTG_HS->GCCFG = USB_OTG_GCCFG_NOVBUSSENS;
+ USB_OTG_HS->GAHBCFG = USB_OTG_GAHBCFG_GINT | 7 << USB_OTG_GAHBCFG_HBSTLEN_Pos;
+ USB_OTG_HS->GUSBCFG = 9U << USB_OTG_GUSBCFG_TRDT_Pos | USB_OTG_GUSBCFG_FDMOD;
+ USB_DEVICE->DCFG = 0U << USB_OTG_DCFG_DSPD_Pos;
+ USB_OTG_HS->GINTMSK |= USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_USBSUSPM |
+ USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_WUIM |
+#ifdef __RTX
+ ((USBD_RTX_DevTask != 0) ? USB_OTG_GINTMSK_SOFM : 0) |
+ ((USBD_RTX_DevTask != 0) ? USB_OTG_GINTMSK_ESUSPM : 0);
+#else
+ ((USBD_P_SOF_Event != 0) ? USB_OTG_GINTMSK_SOFM : 0) |
+ ((USBD_P_SOF_Event != 0) ? USB_OTG_GINTMSK_ESUSPM : 0);
+#endif
+ for (i = 0U; i < 15U; i++)
+ {
+ USB_OTG_HS->DIEPTXF[i] = 0U;
+ }
+ USB_OTG_HS->GRXFSIZ = 0x180;
+ USB_OTG_HS->DIEPTXF0_HNPTXFSIZ = 0x80 << 16 | 0x180;
+ USB_OTG_HS->DIEPTXF[0] = 0x80 << 16 | 0x200;
+ USB_OTG_HS->DIEPTXF[1] = 0x80 << 16 | 0x280;
+ USB_OTG_HS->DIEPTXF[2] = 0x80 << 16 | 0x300;
+ USB_OTG_HS->DIEPTXF[3] = 0x80 << 16 | 0x380;
+
+ USB_OTG_HS->GINTSTS = 0xFFFFFFFF;
+ USBD_IntrEna();
+ USB_DEVICE->DCTL |= USB_OTG_DCTL_SDIS;
+}
+
+/*
+ * USB Device Connect Function
+ * Called by the User to Connect/Disconnect USB Device
+ * Parameters: con: Connect/Disconnect
+ * Return Value: None
+ */
+
+void USBD_Connect(BOOL con)
+{
+ USB_DEVICE->DCTL &= ~USB_OTG_DCTL_SDIS;
+}
+/*
+ * USB Device Suspend Function
+ * Called automatically on USB Device Suspend
+ * Return Value: None
+ */
+
+void USBD_Suspend(void)
+{
+}
+
+
+/*
+ * USB Device Resume Function
+ * Called automatically on USB Device Resume
+ * Return Value: None
+ */
+
+void USBD_Resume(void)
+{
+}
+
+
+/*
+ * USB Device Remote Wakeup Function
+ * Called automatically on USB Device Remote Wakeup
+ * Return Value: None
+ */
+
+void USBD_WakeUp(void)
+{
+}
+
+
+/*
+ * USB Device Remote Wakeup Configuration Function
+ * Parameters: cfg: Device Enable/Disable
+ * Return Value: None
+ */
+
+void USBD_WakeUpCfg(BOOL cfg)
+{
+}
+
+
+/*
+ * USB Device Set Address Function
+ * Parameters: adr: USB Device Address
+ * setup: Called in setup stage (!=0), else after status stage
+ * Return Value: None
+ */
+
+void USBD_SetAddress(U32 adr, U32 setup)
+{
+ USB_DEVICE->DCFG &= ~(USB_OTG_DCFG_DAD);
+ USB_DEVICE->DCFG |= ((uint32_t)adr << 4) & USB_OTG_DCFG_DAD;
+}
+
+
+/*
+ * USB Device Configure Function
+ * Parameters: cfg: Device Configure/Deconfigure
+ * Return Value: None
+ */
+
+void USBD_Configure(BOOL cfg)
+{
+}
+
+
+/*
+ * Configure USB Device Endpoint according to Descriptor
+ * Parameters: pEPD: Pointer to Device Endpoint Descriptor
+ * Return Value: None
+ */
+void USBD_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD)
+{
+ uint32_t num, mpsize, ep_type;
+ num = pEPD->bEndpointAddress & 0x0F;
+ mpsize = pEPD->wMaxPacketSize;
+ switch (pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK) {
+ case USB_ENDPOINT_TYPE_CONTROL:
+ ep_type = 0;
+ break;
+
+ case USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ ep_type = 1;
+ break;
+
+ case USB_ENDPOINT_TYPE_BULK:
+ ep_type = 2;
+ break;
+
+ case USB_ENDPOINT_TYPE_INTERRUPT:
+ ep_type = 3;
+ break;
+ }
+ if (pEPD->bEndpointAddress & 0x80) {
+ USB_DEVICE->DAINTMSK |= USB_OTG_DAINTMSK_IEPM & (uint32_t)(1UL << num);
+ USB_INEP(num)->DIEPCTL = (mpsize & USB_OTG_DIEPCTL_MPSIZ) |
+ ((uint32_t)ep_type << USB_OTG_DIEPCTL_EPTYP_Pos) |
+ (num << USB_OTG_DIEPCTL_TXFNUM_Pos) |
+ USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
+ } else {
+ USB_DEVICE->DAINTMSK |= USB_OTG_DAINTMSK_OEPM & ((uint32_t)(1UL << num) << 16);
+ USB_OUTEP(num)->DOEPCTL = (mpsize & USB_OTG_DOEPCTL_MPSIZ) |
+ ((uint32_t)ep_type << USB_OTG_DOEPCTL_EPTYP_Pos) |
+ USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
+ }
+}
+
+
+/*
+ * Set Direction for USB Device Control Endpoint
+ * Parameters: dir: Out (dir == 0), In (dir <> 0)
+ * Return Value: None
+ */
+
+void USBD_DirCtrlEP(U32 dir)
+{
+}
+
+
+/*
+ * Enable USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_EnableEP(U32 EPNum)
+{
+ if (EPNum & 0x80) {
+ USB_INEP(EPNum & 0x0F)->DIEPCTL |= USB_OTG_DIEPCTL_USBAEP;
+ } else {
+ USB_OUTEP(EPNum & 0x0F)->DOEPCTL |= USB_OTG_DOEPCTL_USBAEP;
+ }
+}
+
+
+/*
+ * Disable USB Endpoint
+ * Parameters: EPNum: Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_DisableEP(U32 EPNum)
+{
+}
+
+
+/*
+ * Reset USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ResetEP(U32 EPNum)
+{
+ if (EPNum & 0x80) {
+ if ((EPNum & 0xF) == 0) {
+ USB_OUTEP(EPNum & 0x0F)->DOEPTSIZ = 1U << USB_OTG_DOEPTSIZ_STUPCNT_Pos |
+ (USB_OUTEP(EPNum & 0x0F)->DOEPCTL & USB_OTG_DOEPCTL_MPSIZ) << USB_OTG_DOEPTSIZ_XFRSIZ_Pos;
+ USB_OUTEP(EPNum & 0x0F)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
+ } else {
+ USB_OUTEP(EPNum & 0x0F)->DOEPTSIZ = 1U << USB_OTG_DOEPTSIZ_PKTCNT_Pos |
+ (USB_OUTEP(EPNum & 0x0F)->DOEPCTL & USB_OTG_DOEPCTL_MPSIZ) << USB_OTG_DOEPTSIZ_XFRSIZ_Pos;
+ USB_OUTEP(EPNum & 0x0F)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
+ }
+
+ }
+}
+
+
+/*
+ * Set Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_SetStallEP(U32 EPNum)
+{
+ if (EPNum & 0x80) {
+ USB_INEP(EPNum & 0xF)->DIEPCTL |= USB_OTG_DIEPCTL_STALL;
+ } else
+ USB_OUTEP(EPNum & 0xF)->DOEPCTL |= USB_OTG_DIEPCTL_STALL;
+}
+
+
+/*
+ * Clear Stall for USB Device Endpoint
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClrStallEP(U32 EPNum)
+{
+ if (EPNum & 0x80) {
+ USB_INEP(EPNum & 0xF)->DIEPCTL &= ~USB_OTG_DIEPCTL_STALL;
+ if ((USB_INEP(EPNum & 0xF)->DIEPCTL & (0x2U << USB_OTG_DIEPCTL_EPTYP_Pos) )) {
+ USB_INEP(EPNum & 0xF)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
+ }
+ } else {
+ USB_OUTEP(EPNum & 0xF)->DOEPCTL &= ~USB_OTG_DIEPCTL_STALL;
+ if ((USB_OUTEP(EPNum & 0xF)->DOEPCTL & (0x2U << USB_OTG_DOEPCTL_EPTYP_Pos) )) {
+ USB_OUTEP(EPNum & 0xF)->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
+ }
+ }
+ USBD_ClearEPBuf(EPNum);
+}
+
+
+/*
+ * Clear USB Device Endpoint Buffer
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * Return Value: None
+ */
+
+void USBD_ClearEPBuf(U32 EPNum)
+{
+ EPNum &= 0x0F;
+ USB_OTG_HS->GRSTCTL = (USB_OTG_GRSTCTL_TXFFLSH | (EPNum << 6));
+ while ((USB_OTG_HS->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH);
+}
+
+
+/*
+ * Read USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * Return Value: Number of bytes read
+ */
+
+U32 USBD_ReadEP(U32 EPNum, U8 *pData, U32 bufsz)
+{
+ uint32_t count32b;
+ uint32_t i;
+ if ((EPNum & 0xF) == 2 || (EPNum & 0xF) == 3) {
+ i = (recstate[EPNum & 0xF] & USB_OTG_GRXSTSP_BCNT) >> USB_OTG_GRXSTSP_BCNT_Pos;
+ if (i < bufsz)
+ bufsz = i;
+ count32b = ((uint32_t)bufsz + 3U) / 4U;
+ for (i = 0U; i < count32b; i++) {
+ ((__packed uint32_t *)pData)[i] = recbuf32[EPNum & 0xF][i];
+ }
+ } else {
+ i = (rx_state & USB_OTG_GRXSTSP_BCNT) >> USB_OTG_GRXSTSP_BCNT_Pos;
+ if (i < bufsz)
+ bufsz = i;
+ count32b = ((uint32_t)bufsz + 3U) / 4U;
+ for (i = 0U; i < count32b; i++) {
+ ((__packed uint32_t *)pData)[i] = USB_DFIFO(0);
+ }
+ }
+ USB_OTG_HS->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM;
+ if ((EPNum & 0xF) != 0) {
+ USB_OUTEP(EPNum & 0xF)->DOEPINT = (USB_OTG_DOEPINT_XFRC);
+ USB_OUTEP(EPNum & 0xF)->DOEPTSIZ = 1U << USB_OTG_DOEPTSIZ_PKTCNT_Pos |
+ (USB_OUTEP(EPNum & 0xF)->DOEPCTL & USB_OTG_DOEPCTL_MPSIZ) << USB_OTG_DOEPTSIZ_XFRSIZ_Pos;
+ USB_OUTEP(EPNum & 0xF)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
+ }
+ return bufsz;
+}
+
+
+/*
+ * Write USB Device Endpoint Data
+ * Parameters: EPNum: Device Endpoint Number
+ * EPNum.0..3: Address
+ * EPNum.7: Dir
+ * pData: Pointer to Data Buffer
+ * cnt: Number of bytes to write
+ * Return Value: Number of bytes written
+ */
+
+U32 USBD_WriteEP(U32 EPNum, U8 *pData, U32 cnt)
+{
+ uint32_t len32b;
+ uint32_t i;
+ EPNum &= 0x0F;
+ USB_INEP(EPNum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_PKTCNT);
+ USB_INEP(EPNum)->DIEPTSIZ |= 1U << USB_OTG_DIEPTSIZ_PKTCNT_Pos;
+ USB_INEP(EPNum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_XFRSIZ);
+ USB_INEP(EPNum)->DIEPTSIZ |= cnt;
+ USB_INEP(EPNum)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);
+ if (cnt != 0) {
+ len32b = (cnt + 3U) / 4U;
+ while (USB_INEP(EPNum)->DTXFSTS < len32b);
+ for (i = 0U; i < len32b; i++) {
+ USB_DFIFO(EPNum) = ((uint32_t*)pData)[i];
+ }
+ }
+ return cnt;
+}
+
+
+/*
+ * Get USB Device Last Frame Number
+ * Parameters: None
+ * Return Value: Frame Number
+ */
+
+U32 USBD_GetFrame(void)
+{
+}
+
+
+#ifdef __RTX
+U32 LastError; /* Last Error */
+
+/*
+ * Get USB Last Error Code
+ * Parameters: None
+ * Return Value: Error Code
+ */
+
+U32 USBD_GetError(void)
+{
+ return (LastError);
+}
+#endif
+
+
+
+void USBD_Handler(void)
+{
+ uint32_t i;
+ uint32_t ep_intr;
+ uint32_t epnum;
+ uint32_t epint;
+
+ if (gint_flag & USB_OTG_GINTSTS_SOF) {
+ USB_OTG_HS->GINTSTS |= USB_OTG_GINTSTS_SOF;
+ if (USBD_P_SOF_Event) {
+ USBD_P_SOF_Event();
+ }
+ }
+
+ if (gint_flag & USB_OTG_GINTSTS_MMIS) {
+ USB_OTG_HS->GINTSTS |= USB_OTG_GINTSTS_MMIS;
+ }
+
+ if (gint_flag & USB_OTG_GINTSTS_ESUSP) {
+ USB_OTG_HS->GINTSTS |= USB_OTG_GINTSTS_ESUSP;
+ }
+
+ if (gint_flag & USB_OTG_GINTSTS_USBSUSP) {
+ USB_OTG_HS->GINTSTS |= USB_OTG_GINTSTS_USBSUSP;
+ if (USBD_P_Suspend_Event) {
+ USBD_P_Suspend_Event();
+ }
+ }
+
+ if (gint_flag & USB_OTG_GINTSTS_RXFLVL) {
+ rx_state = USB_OTG_HS->GRXSTSP;
+ epnum = rx_state & USB_OTG_GRXSTSP_EPNUM;
+ if (((rx_state & USB_OTG_GRXSTSP_PKTSTS) >> USB_OTG_GRXSTSP_PKTSTS_Pos) == 2U)
+ {
+ if (epnum == 2 || epnum == 3) {
+ uint32_t count32;
+ count32 = (rx_state & (USB_OTG_GRXSTSP_BCNT)) >> USB_OTG_GRXSTSP_BCNT_Pos;
+ for (i = 0U; i < (count32 + 3) / 4; i++) {
+ recbuf32[epnum][i] = USB_DFIFO(0U);
+ }
+ recstate[epnum] = rx_state;
+ }
+
+ if ((rx_state & USB_OTG_GRXSTSP_BCNT) != 0U)
+ {
+ USB_OTG_HS->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM;
+ if (USBD_P_EP[epnum]) {
+ USBD_P_EP[epnum](USBD_EVT_OUT);
+ }
+ }
+ }
+ else if (((rx_state & USB_OTG_GRXSTSP_PKTSTS) >> USB_OTG_GRXSTSP_PKTSTS_Pos) == 6U)
+ {
+ if (epnum == 2 || epnum == 3) {
+ uint32_t count32;
+ count32 = (rx_state & (USB_OTG_GRXSTSP_BCNT)) >> USB_OTG_GRXSTSP_BCNT_Pos;
+ for (i = 0U; i < (count32 + 3) / 4; i++) {
+ recbuf32[epnum][i] = USB_DFIFO(0U);
+ }
+ recstate[epnum] = rx_state;
+ }
+
+ USB_OTG_HS->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM;
+ if (USBD_P_EP[epnum]) {
+ USBD_P_EP[epnum](USBD_EVT_SETUP);
+ }
+ }
+ }
+
+ if (gint_flag & USB_OTG_GINTSTS_USBRST) {
+ USB_OTG_HS->GINTSTS |= USB_OTG_GINTSTS_USBRST;
+ for (i = 0; i < 5; i++)
+ USB_OUTEP(i)->DOEPCTL = USB_OTG_DOEPCTL_SNAK;
+ USB_DEVICE->DAINTMSK = 0x10001;
+ USB_DEVICE->DOEPMSK = USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_STUPM;
+ USB_DEVICE->DIEPMSK = USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_TOM;
+ USB_OUTEP(0)->DOEPTSIZ = 1U << USB_OTG_DOEPTSIZ_STUPCNT_Pos;
+ usbd_reset_core();
+ if (USBD_P_Reset_Event) {
+ USBD_P_Reset_Event();
+ }
+ }
+
+ if (gint_flag & USB_OTG_GINTSTS_ENUMDNE) {
+ USBD_HighSpeed = (USB_DEVICE->DSTS & USB_OTG_DSTS_ENUMSPD) ? 0 : 1;
+ USB_OTG_HS->GINTSTS |= USB_OTG_GINTSTS_ENUMDNE;
+ USB_INEP(0)->DIEPTSIZ = 64 << USB_OTG_DIEPCTL_MPSIZ_Pos;
+ USB_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA;
+ }
+
+ if (gint_flag & USB_OTG_GINTMSK_IEPINT) {
+ epnum = 0U;
+ ep_intr = USB_DEVICE->DAINT &0xFFFF;
+ ep_intr &= USB_DEVICE->DAINTMSK;
+ while (ep_intr != 0U) {
+ if ((ep_intr & 0x1U) != 0U) {
+ epint = USB_INEP((uint32_t)epnum)->DIEPINT;
+ epint &= USB_DEVICE->DIEPMSK;
+ if ((epint & USB_OTG_DIEPINT_XFRC) == USB_OTG_DIEPINT_XFRC) {
+ USB_INEP(epnum)->DIEPINT = USB_OTG_DIEPINT_XFRC;
+ if (USBD_P_EP[epnum]) {
+ USBD_P_EP[epnum](USBD_EVT_IN);
+ }
+ }
+ }
+ ep_intr >>= 1U;
+ epnum++;
+ }
+ }
+
+ if (gint_flag & USB_OTG_GINTMSK_OEPINT) {
+ epnum = 0U;
+ ep_intr = USB_DEVICE->DAINT;
+ ep_intr &= USB_DEVICE->DAINTMSK;
+ ep_intr >>= 16;
+ while (ep_intr != 0U) {
+ if ((ep_intr & 0x1U) != 0U) {
+ epint = USB_OUTEP((uint32_t)epnum)->DOEPINT;
+ epint &= USB_DEVICE->DOEPMSK;
+ if ((epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP) {
+ USB_OUTEP(epnum)->DOEPINT = (USB_OTG_DOEPINT_STUP);
+ USB_OUTEP(epnum)->DOEPTSIZ = 1U << USB_OTG_DOEPTSIZ_STUPCNT_Pos;
+ USB_OUTEP(epnum)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
+ }
+ if ((epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC && epnum == 0) {
+ USB_OUTEP(epnum)->DOEPINT = (USB_OTG_DOEPINT_XFRC);
+ USB_OUTEP(epnum)->DOEPTSIZ = 1U << USB_OTG_DOEPTSIZ_PKTCNT_Pos |
+ (USB_OUTEP(epnum)->DOEPCTL & USB_OTG_DOEPCTL_MPSIZ) << USB_OTG_DOEPTSIZ_XFRSIZ_Pos;
+ USB_OUTEP(epnum)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
+ }
+ }
+ ep_intr >>= 1U;
+ epnum++;
+ }
+ }
+
+ HAL_NVIC_EnableIRQ(OTG_HS_EP1_OUT_IRQn);
+ HAL_NVIC_EnableIRQ(OTG_HS_EP1_IN_IRQn);
+ HAL_NVIC_EnableIRQ(OTG_HS_IRQn);
+}
+
+void OTG_HS_EP1_OUT_IRQHandler(void)
+{
+ gint_flag = USB_OTG_HS->GINTSTS;
+ HAL_NVIC_DisableIRQ(OTG_HS_EP1_OUT_IRQn);
+ HAL_NVIC_DisableIRQ(OTG_HS_EP1_IN_IRQn);
+ HAL_NVIC_DisableIRQ(OTG_HS_IRQn);
+ USBD_SignalHandler();
+}
+
+void OTG_HS_EP1_IN_IRQHandler(void)
+{
+ gint_flag = USB_OTG_HS->GINTSTS;
+ HAL_NVIC_DisableIRQ(OTG_HS_EP1_OUT_IRQn);
+ HAL_NVIC_DisableIRQ(OTG_HS_EP1_IN_IRQn);
+ HAL_NVIC_DisableIRQ(OTG_HS_IRQn);
+ USBD_SignalHandler();
+}
+
+void OTG_HS_IRQHandler(void)
+{
+ gint_flag = USB_OTG_HS->GINTSTS;
+ HAL_NVIC_DisableIRQ(OTG_HS_EP1_OUT_IRQn);
+ HAL_NVIC_DisableIRQ(OTG_HS_EP1_IN_IRQn);
+ HAL_NVIC_DisableIRQ(OTG_HS_IRQn);
+ USBD_SignalHandler();
+}
diff --git a/port/stm32/usbd_stm32f103_devfs.c b/port/stm32/usbd_stm32f103_devfs.c
new file mode 100644
index 00000000..0ed54d42
--- /dev/null
+++ b/port/stm32/usbd_stm32f103_devfs.c
@@ -0,0 +1,549 @@
+/* This file is the part of the Lightweight USB device Stack for STM32 microcontrollers
+ *
+ * Copyright ©2016 Dmitry Filimonchuk <dmitrystu[at]gmail[dot]com>
+ * Copyright ©2017 Max Chan <max[at]maxchan[dot]info>
+ *
+ * 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_STM32F103)
+
+#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) (x)
+
+typedef union _pma_table pma_table;
+
+#if defined(STM32F302x8) || defined(STM32F302xE) || defined(STM32F303xE)
+ #if !defined(USB_PMASIZE)
+ #pragma message "PMA memory size is not defined. Use 768 bytes by default"
+ #define USB_PMASIZE 0x300
+ #endif
+ #define PMA_STEP 1
+
+ typedef struct {
+ uint16_t addr;
+ uint16_t cnt;
+ } pma_rec;
+
+ inline static pma_table *EPT(uint8_t ep) {
+ return (pma_table*)((ep & 0x07) * 8 + USB_PMAADDR);
+ }
+
+ inline static uint16_t *PMA(uint16_t addr) {
+ return (uint16_t*)(USB_PMAADDR + addr);
+ }
+
+#else
+ #if !defined(USB_PMASIZE)
+ #pragma message "PMA memory size is not defined. Use 512 bytes by default"
+ #define USB_PMASIZE 0x200
+ #endif
+ #define PMA_STEP 2
+
+ typedef struct {
+ uint16_t addr;
+ uint16_t :16;
+ uint16_t cnt;
+ uint16_t :16;
+ } pma_rec;
+
+ inline static pma_table *EPT(uint8_t ep) {
+ return (pma_table*)((ep & 0x07) * 16 + USB_PMAADDR);
+ }
+
+ inline static uint16_t *PMA(uint16_t addr) {
+ return (uint16_t*)(USB_PMAADDR + 2 * addr);
+ }
+#endif
+
+union _pma_table {
+ struct {
+ pma_rec tx;
+ pma_rec rx;
+ };
+ struct {
+ pma_rec tx0;
+ pma_rec tx1;
+ };
+ struct {
+ pma_rec rx0;
+ pma_rec rx1;
+ };
+};
+
+/** \brief Helper function. Enables GPIOx for DP.
+ * Looks ugly. But compiler should optimize this
+ * to single line
+ */
+inline static void set_gpiox() {
+#if defined(STM32F1) && defined(USBD_DP_PORT)
+ if (USBD_DP_PORT == GPIOA) {RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; return;}
+ if (USBD_DP_PORT == GPIOB) {RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; return;}
+ if (USBD_DP_PORT == GPIOC) {RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; return;}
+ if (USBD_DP_PORT == GPIOD) {RCC->APB2ENR |= RCC_APB2ENR_IOPDEN; return;}
+ #if defined(GPIOE)
+ if (USBD_DP_PORT == GPIOE) {RCC->APB2ENR |= RCC_APB2ENR_IOPEEN; return;}
+ #endif
+ #if defined(GPIOF)
+ if (USBD_DP_PORT == GPIOF) {RCC->APB2ENR |= RCC_APB2ENR_IOPFEN; return;}
+ #endif
+#elif defined(STM32F3) && defined(USBD_DP_PORT)
+ if (USBD_DP_PORT == GPIOA) {RCC->AHBENR |= RCC_AHBENR_GPIOAEN; return;}
+ if (USBD_DP_PORT == GPIOB) {RCC->AHBENR |= RCC_AHBENR_GPIOBEN; return;}
+ if (USBD_DP_PORT == GPIOC) {RCC->AHBENR |= RCC_AHBENR_GPIOCEN; return;}
+ if (USBD_DP_PORT == GPIOD) {RCC->AHBENR |= RCC_AHBENR_GPIODEN; return;}
+ #if defined(GPIOE)
+ if (USBD_DP_PORT == GPIOE) {RCC->AHBENR |= RCC_AHBENR_GPIOEEN; return;}
+ #endif
+ #if defined(GPIOF)
+ if (USBD_DP_PORT == GPIOF) {RCC->AHBENR |= RCC_AHBENR_GPIOFEN; return;}
+ #endif
+ #if defined(GPIOG)
+ if (USBD_DP_PORT == GPIOG) {RCC->AHBENR |= RCC_AHBENR_GPIOGEN; return;}
+ #endif
+ #if defined(GPIOH)
+ if (USBD_DP_PORT == GPIOH) {RCC->AHBENR |= RCC_AHBENR_GPIOHEN; return;}
+ #endif
+#endif
+ return;
+}
+
+/** \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->tx.addr) && (tbl->tx.addr < _result)) _result = tbl->tx.addr;
+ if ((tbl->rx.addr) && (tbl->rx.addr < _result)) _result = tbl->rx.addr;
+ }
+ return (_result < (0x020 + sz)) ? 0 : (_result - sz);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->APB1ENR & RCC_APB1ENR_USBEN)) return STATUS_VAL(0);
+#if defined(USBD_DP_PORT) && defined(USBD_DP_PIN)
+ if (USBD_DP_PORT->IDR & _BV(USBD_DP_PIN)) return STATUS_VAL(USBD_HW_ENABLED | USBD_HW_SPEED_FS);
+ return STATUS_VAL(USBD_HW_ENABLED);
+#else
+ return STATUS_VAL(USBD_HW_ENABLED | USBD_HW_SPEED_FS);
+#endif
+}
+
+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 uint8_t connect(bool connect) {
+#if defined(USBD_DP_PORT) && defined(USBD_DP_PIN) && defined(STM32F3)
+ uint32_t _t = USBD_DP_PORT->MODER & ~(0x03 << (2 * USBD_DP_PIN));
+ if (connect) {
+ _t |= (0x01 << (2 * USBD_DP_PIN));
+ USBD_DP_PORT->BSRR = (0x0001 << USBD_DP_PIN);
+ }
+ USBD_DP_PORT->MODER = _t;
+#elif defined(USBD_DP_PORT) && defined(USBD_DP_PIN) && defined(STM32F1)
+#if (USBD_DP_PIN < 8)
+ uint32_t _t = USBD_DP_PORT->CRL & ~(0x0F << (4 * USBD_DP_PIN));
+ if (connect) {
+ _t |= (0x02 << (4 * USBD_DP_PIN));
+ USBD_DP_PORT->BSRR = (0x0001 << USBD_DP_PIN);
+ } else {
+ _t |= (0x04 << (4 * USBD_DP_PIN));
+ }
+ USBD_DP_PORT->CRL = _t;
+#else
+ uint32_t _t = USBD_DP_PORT->CRH & ~(0x0F << (4 * (USBD_DP_PIN - 8)));
+ if (connect) {
+ _t |= (0x02 << (4 * (USBD_DP_PIN - 8)));
+ USBD_DP_PORT->BSRR = (0x0001 << USBD_DP_PIN);
+ } else {
+ _t |= (0x04 << (4 * (USBD_DP_PIN - 8)));
+ }
+ USBD_DP_PORT->CRH = _t;
+#endif
+#endif
+ return usbd_lane_unk;
+}
+
+static void enable(bool enable) {
+ if (enable) {
+ set_gpiox();
+ RCC->APB1ENR |= RCC_APB1ENR_USBEN;
+ RCC->APB1RSTR |= RCC_APB1RSTR_USBRST;
+ RCC->APB1RSTR &= ~RCC_APB1RSTR_USBRST;
+ 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->APB1ENR & RCC_APB1ENR_USBEN) {
+ RCC->APB1RSTR |= RCC_APB1RSTR_USBRST;
+ RCC->APB1ENR &= ~RCC_APB1ENR_USBEN;
+ /* disconnecting DP if configured */
+ connect(0);
+ }
+}
+
+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 = 0x8000U - 0x400U + (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 tmp;
+ uint16_t *pma = PMA(rx->addr);
+ uint16_t rxcnt = rx->cnt & 0x03FF;
+ rx->cnt &= ~0x3FF;
+ for(int idx = 0; idx < rxcnt; idx++) {
+ if ((idx & 0x01) == 0) {
+ tmp = *pma;
+ pma += PMA_STEP;
+ }
+ if (idx < blen) {
+ buf[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ } else {
+ return blen;
+ }
+ }
+ 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(const uint8_t *buf, uint16_t blen, pma_rec *tx) {
+ uint16_t *pma = PMA(tx->addr);
+ uint16_t tmp = 0;
+ tx->cnt = blen;
+ for (int idx=0; idx < blen; idx++) {
+ tmp |= buf[idx] << ((idx & 0x01) ? 8 : 0);
+ if ((idx & 0x01) || (idx + 1) == blen) {
+ *pma = tmp;
+ pma += PMA_STEP;
+ tmp = 0;
+ }
+ }
+}
+
+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 + 0x08));
+ 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_STM32F103
diff --git a/port/stm32/usbd_stm32f103_devfs_asm.S b/port/stm32/usbd_stm32f103_devfs_asm.S
new file mode 100644
index 00000000..ee7bcf23
--- /dev/null
+++ b/port/stm32/usbd_stm32f103_devfs_asm.S
@@ -0,0 +1,866 @@
+/* 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.
+ */
+
+#if !defined (__ASSEMBLER__)
+ #define __ASSEMBLER__
+#endif
+
+#include "usb.h"
+#if defined(USBD_STM32F103)
+#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
+
+#if defined(STM32F302x8) || defined(STM32F302xE) || defined(STM32F303xE)
+ #define RXADDR0 0x00
+ #define RXCOUNT0 0x02
+ #define RXADDR1 0x04
+ #define RXCOUNT1 0x06
+ #define USB_PMASZ 0x300
+ #define USB_PMASTP 0x02
+ #define EPT_SHIFT 3
+
+#else
+ #define RXADDR0 0x00
+ #define RXCOUNT0 0x04
+ #define RXADDR1 0x08
+ #define RXCOUNT1 0x0C
+ #define USB_PMASZ 0x200
+ #define USB_PMASTP 0x04
+ #define EPT_SHIFT 4
+
+#endif
+
+#define TXADDR0 RXADDR0
+#define TXCOUNT0 RXCOUNT0
+#define TXADDR1 RXADDR1
+#define TXCOUNT1 RXCOUNT1
+
+#define TXADDR RXADDR0
+#define TXCOUNT RXCOUNT0
+#define RXADDR RXADDR1
+#define RXCOUNT RXCOUNT1
+
+#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)
+
+
+#if (USBD_DP_PORT == GPIOA)
+ #define RCC_GPIOxEN RCC_GPIOAEN
+#elif (USBD_DP_PORT == GPIOB)
+ #define RCC_GPIOxEN RCC_GPIOBEN
+#elif (USBD_DP_PORT == GPIOC)
+ #define RCC_GPIOxEN RCC_GPIOCEN
+#elif (USBD_DP_PORT == GPIOD)
+ #define RCC_GPIOxEN RCC_GPIODEN
+#elif (USBD_DP_PORT == GPIOE)
+ #define RCC_GPIOxEN RCC_GPIOEEN
+#elif (USBD_DP_PORT == GPIOF)
+ #define RCC_GPIOxEN RCC_GPIOFEN
+#elif (USBD_DP_PORT == GPIOG)
+ #define RCC_GPIOxEN RCC_GPIOGEN
+#elif (USBD_DP_PORT == GPIOH)
+ #define RCC_GPIOxEN RCC_GPIOHEN
+#else
+ #warning "No USB DP control used for F303"
+ #undef USBD_DP_PORT
+ #undef USBD_DP_PIN
+#endif
+
+ .syntax unified
+ .cpu cortex-m3
+ .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, 0x00] //UID0
+ bl .L_fnv1a
+ ldr r2, [r5, 0x04] //UID1
+ bl .L_fnv1a
+ ldr r2, [r5, 0x08] //UID2
+ bl .L_fnv1a
+ movs r3, #28
+.L_gsn_loop:
+ lsrs r1, r4, r3
+ and r1, #0x0F
+ cmp r1, #0x09
+ ite gt
+ addgt r1, #55
+ addle r1, #48
+.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_uid_base: .long UID_BASE
+.L_fnv1a_offset: .long 2166136261
+.L_fnv1a_prime: .long 16777619
+
+ .size _get_serial_desc, . - _get_serial_desc
+
+ .thumb_func
+ .type _connect, %function
+_connect:
+#if defined(USBD_DP_PORT) && defined(USBD_DP_PIN) && defined(STM32F1)
+ #if (USBD_DP_PIN < 8)
+ #define GPIO_CRx GPIO_CRL
+ #define DP_PIN (4 * USBD_DP_PIN)
+ #else
+ #define GPIO_CRx GPIO_CRH
+ #define DP_PIN (4 * (USBD_DP_PIN - 8))
+ #endif
+ ldr r3, =USBD_DP_PORT
+ ldr r2, [r3, #GPIO_CRx]
+ movs r1, #0x0F
+ bics r2, r2, r1, LSL #DP_PIN
+ movs r1, #0x04
+ cbz r0, .L_store
+ movs r1, #0x01
+ lsls r1, #USBD_DP_PIN
+ str r1, [r3, #GPIO_BSRR]
+ movs r1, #0x02
+.L_store:
+ orrs r2, r2, r1, LSL #DP_PIN
+ str r2, [r3, #GPIO_CRx]
+#elif defined(USBD_DP_PORT) && defined(USBD_DP_PIN) && defined(STM32F3)
+ ldr r3, =USBD_DP_PORT
+ ldr r2, [r3, #GPIO_MODER]
+ movs r1, #0x03
+ bics r2, r2, r1, LSL #(2 * USBD_DP_PIN)
+ movs r1, #0x01
+ cbz r0, .L_store
+ orrs r2, r2, r1, LSL #(2 * USBD_DP_PIN)
+ lsls r1, #USBD_DP_PIN
+ str r1, [r3, #GPIO_BSRR]
+.L_store:
+ str r2, [r3, #GPIO_MODER]
+#endif
+ movs r0, #usbd_lane_unk
+ 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 r2, =RCC_BASE //RCC
+ movs r1, #0x01
+ lsls r3, r1, #23 //USBEN or USBRST
+ cbz r0, .L_disable
+.L_enable:
+/* enabling and resetting USB peripheral */
+/* enabling DP control GPIO port */
+#if defined(USBD_DP_PORT) && defined(USBD_DP_PIN) && defined(STM32F3)
+ lsls r1, #RCC_GPIOxEN
+ ldr r0, [r2, #RCC_AHBENR]
+ orrs r0, r1
+ str r0, [r2, #RCC_AHBENR]
+#elif defined(USBD_DP_PORT) && defined(USBD_DP_PIN)
+ lsls r1, #RCC_GPIOxEN
+ ldr r0, [r2, #RCC_APB2ENR]
+ orrs r0, r1
+ str r0, [r2, #RCC_APB2ENR]
+#endif
+ ldr r1, =USB_REGBASE
+ ldr r0, [r2, #RCC_APB1ENR]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1ENR] //RCC->APB1ENR |= USBEN
+ ldr r0, [r2, #RCC_APB1RSTR]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1RSTR] //RCC->APB1RSTR |= USBRST
+ bics r0, r3
+ str r0, [r2, #RCC_APB1RSTR] //RCC->APB1RSTR &= ~USBRST
+/* setting up USB CNTR */
+#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, #USB_CNTR] //set USB->CNTR
+ bx lr
+.L_disable:
+ ldr r0, [r2, #RCC_APB1ENR]
+ tst r0, r3
+ beq .L_enable_end // usb is already disabled
+/* disabling USB peripheral */
+ bics r0, r3
+ str r0, [r2, #RCC_APB1ENR]
+ movs r0, #0
+ b _connect // jump to disconnect subroutine
+.L_enable_end:
+ bx lr
+ .size _enable, . - _enable
+
+ .thumb_func
+ .type _getinfo, %function
+_getinfo:
+ movs r0, #0
+ ldr r2, =RCC_BASE
+ ldr r1, [r2, #RCC_APB1ENR]
+ lsrs r1, #24 //USBEN -> CF
+ bcc .L_getinfo_end
+ adds r0, #USBD_HW_ENABLED
+#if defined(USBD_DP_PORT) && defined(USBD_DP_PIN)
+ ldr r2, =USBD_DP_PORT
+ ldr r1, [r2, #GPIO_IDR]
+ lsrs r1, #USBD_DP_PIN //USBD_DP_PIN -> CF
+ bcc .L_getinfo_end
+#endif
+ 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 0 on error
+ */
+_ep_read:
+ push {r4, r5, r6, lr}
+ ldr r3, =USB_EPBASE
+ ldr r6, =USB_PMABASE
+ lsls r0, #28
+ add r3, r3, r0, LSR #26 //*EPR -> R3
+ add r4, r6, r0, LSR (28 - EPT_SHIFT) //*EPT -> R4
+ ldrh r5, [r3] // reading epr
+/* validating endpoint */
+ movs r0, #0x37
+ ands r0, r0, r5, LSR #8
+ 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:
+ eors r0, r5, r5, LSR #8
+ 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, #RXADDR1 // set RXADDR0
+.L_epr_sngl:
+ ldrh r0, [r4, #RXCOUNT]
+ lsrs r5, r0, #0x0A
+ lsls r5, #0x0A // r5 = r5 & ~0x03FF
+ strh r5, [r4, #RXCOUNT]
+ eors r0, r5 // r0 &= 0x3FF (RX count)
+ ldrh r5, [r4, #RXADDR]
+ adds r5, r4, r5, LSL (EPT_SHIFT - 3)
+ 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, #USB_PMASTP
+ 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
+ and r5, r5, r2, LSR #16
+ strh r5, [r3] // set ep to VALID state
+.L_epr_exit:
+ pop {r4, r5, r6, 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
+ * result -> R0
+ */
+_ep_write:
+ push {r4, r5, r6, lr}
+ ldr r3, =USB_EPBASE
+ ldr r6, =USB_PMABASE
+ lsls r0, #28
+ add r3, r3, r0, LSR #26 //*EPR -> R3
+ add r4, r6, r0, LSR (28 - EPT_SHIFT) //*EPT -> R4
+ ldrh r5, [r3] // reading epr
+ movs r0, #0x73
+ and r0, r0, r5, LSR #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 // -1 error
+ 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, #RXADDR1 // TXADDR1 -> R4
+.L_epw_sngl:
+ strh r2, [r4, #TXCOUNT]
+ mov r0, r2 // save count for return
+ ldrh r5, [r4, #TXADDR]
+ adds r5, r6, r5, LSL (EPT_SHIFT - 3)
+.L_epw_write:
+ cmp r2, #1
+ blo .L_epw_write_end
+ ldrb r4, [r1]
+ beq .L_epw_store
+ ldrb r6, [r1, #1]
+ orr r4, r4, r6, LSL #8
+.L_epw_store:
+ strh r4, [r5]
+ adds r5, #USB_PMASTP
+ 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
+ and r5, r5, r2, LSR #16
+ 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, #16
+ ldr r3, =USB_PMASZ
+ ldr r0, =USB_PMABASE
+.L_gnp_chkaddr:
+ ldrh r4, [r0, #0] //txaddr
+ tst r4, r4
+ beq .L_gnp_nxtaddr
+ cmp r3, r4
+ blo .L_gnp_nxtaddr
+ mov r3, r4
+.L_gnp_nxtaddr:
+ adds r0, #RXADDR1
+ subs r1, #1
+ bne .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
+ adds r2, r3
+ bics r2, r3 //R2 -> halfword aligned epsize
+ cmp r1, #0x06 //DBLBULK (0x01)
+ beq .L_epc_settype
+ movs r3, #0x00
+ cmp r1, #0x02 //BULK
+ beq .L_epc_settype
+ movs r3, #0x02
+ cmp r1, #0x00 //CONTROL
+ beq .L_epc_settype
+ movs r3, #0x04
+ cmp r1, #0x01 //ISO
+ beq .L_epc_settype
+ movs r3, #0x06 //INTERRUPT
+.L_epc_settype:
+ lsls r3, #8
+ movs r4, #0x07
+ ands r4, r0
+ 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
+ adds r5, r5, r4, LSL (EPT_SHIFT - 2)
+ 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 // is ISO
+ bne .L_epc_txsetstate //
+.L_epc_txdbl:
+ 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
+ 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:
+ movs r3, r2
+ cmp r2, #62
+ bls .L_epc_rxbb
+ 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
+ adds r5, r5, r4, LSL (EPT_SHIFT - 2)
+/* RX or RX1 */
+ bl _get_next_pma
+ bcc .L_epc_fail
+ 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
+ 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
+ adds r3, r3, r1, LSL (EPT_SHIFT - 2)
+/* 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, #11 //RESETM -> CF
+ bcs .L_ep_resetm
+ 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
+ /* 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
+ add r0, r0, r2, LSL #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, #0] //R1 USB->CNTR
+ movs r5, #0x08
+ bics r1, r5 //clr FSUSP
+ strh r1, [r3, #0] //USB->CNTR R2
+ movs r1, #usbd_evt_wkup
+ movs r4, #ISTRBIT(12)
+ b .L_ep_clristr
+
+.L_ep_suspm:
+ ldrh r1, [r3, #0] //R1 USB->CNTR
+ movs r5, #0x08
+ orrs r1, r5 //set FSUSP
+ strh r1, [r3, #0] //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, #0
+ strh r4, [r0, #TXADDR]
+ strh r4, [r0, #TXCOUNT]
+ strh r4, [r0, #RXADDR]
+ strh r4, [r0, #RXCOUNT]
+ adds r2, #0x04
+ adds r0, #(0x04 * USB_PMASTP)
+ subs r1, #1
+ bpl .L_ep_reset_loop
+ movs r2, #0x00
+ strh r2, [r3, #0x10] // 0 -> 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_STM32F103
diff --git a/port/stm32/usbd_stm32f105_otgfs.c b/port/stm32/usbd_stm32f105_otgfs.c
new file mode 100644
index 00000000..54530dac
--- /dev/null
+++ b/port/stm32/usbd_stm32f105_otgfs.c
@@ -0,0 +1,485 @@
+/* This file is the part of the Lightweight USB device Stack for STM32 microcontrollers
+ *
+ * Copyright ©2016 Dmitry Filimonchuk <dmitrystu[at]gmail[dot]com>
+ * Adapted from the stm32f429 driver by Fabian Inostroza
+ *
+ * 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_STM32F105)
+
+#define MAX_EP 4
+#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)
+
+#define STATUS_VAL(x) (USBD_HW_ADDRFST | (x))
+
+static USB_OTG_GlobalTypeDef * const OTG = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_GLOBAL_BASE);
+static USB_OTG_DeviceTypeDef * const OTGD = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_DEVICE_BASE);
+static volatile uint32_t * const OTGPCTL = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_PCGCCTL_BASE);
+
+
+inline static uint32_t* EPFIFO(uint32_t ep) {
+ return (uint32_t*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + (ep << 12));
+}
+
+inline static USB_OTG_INEndpointTypeDef* EPIN(uint32_t ep) {
+ return (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + (ep << 5));
+}
+
+inline static USB_OTG_OUTEndpointTypeDef* EPOUT(uint32_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);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->AHBENR & RCC_AHBENR_OTGFSEN)) return STATUS_VAL(0);
+ if (!(OTGD->DCTL & USB_OTG_DCTL_SDIS)) 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) {
+ 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;
+ }
+ }
+}
+
+static 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;
+ }
+}
+
+static void enable(bool enable) {
+ if (enable) {
+ /* enabling USB_OTG in RCC */
+ _BST(RCC->AHBENR, RCC_AHBENR_OTGFSEN);
+ /* 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 SOF output */
+#if defined (USBD_VBUS_DETECT) && defined(USBD_SOF_OUT)
+ OTG->GCCFG = USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_SOFOUTEN;
+#elif defined(USBD_VBUS_DETECT)
+ OTG->GCCFG = USB_OTG_GCCFG_VBUSBSEN;
+#elif defined(USBD_SOF_OUT)
+ OTG->GCCFG = USB_OTG_GCCFG_SOFOUTEN;
+#else
+ OTG->GCCFG &= ~(USB_OTG_GCCFG_SOFOUTEN | USB_OTG_GCCFG_VBUSBSEN);
+#endif
+ /* enable PHY clock */
+ *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));
+ /* setting max RX FIFO size */
+ OTG->GRXFSIZ = RX_FIFO_SZ;
+ /* setting up EP0 TX FIFO SZ as 64 byte */
+ OTG->DIEPTXF0_HNPTXFSIZ = RX_FIFO_SZ | (0x10 << 16);
+ /* unmask EP interrupts */
+ OTGD->DIEPMSK = USB_OTG_DIEPMSK_XFRCM;
+ /* unmask core interrupts */
+ OTG->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM |
+#if !defined(USBD_SOF_DISABLED)
+ USB_OTG_GINTMSK_SOFM |
+#endif
+ 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 */
+ _BST(OTG->GAHBCFG, USB_OTG_GAHBCFG_GINT);
+ } else {
+ if (RCC->AHBENR & RCC_AHBENR_OTGFSEN) {
+ _BST(RCC->AHBRSTR, RCC_AHBRSTR_OTGFSRST);
+ _BCL(RCC->AHBRSTR, RCC_AHBRSTR_OTGFSRST);
+ _BCL(RCC->AHBENR, RCC_AHBENR_OTGFSEN);
+ }
+ }
+}
+
+static uint8_t connect(bool connect) {
+ if (connect) {
+/* The ST made a strange thing again. Really i dont'understand what is the reason to name
+ signal as PWRDWN (Power down PHY) when it works as "Power up" */
+ _BST(OTG->GCCFG, USB_OTG_GCCFG_PWRDWN);
+ _BCL(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ } else {
+ _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ _BCL(OTG->GCCFG, USB_OTG_GCCFG_PWRDWN);
+ }
+ return usbd_lane_unk;
+}
+
+static 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->DIEPTXF0_HNPTXFSIZ;
+ /* 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;
+}
+
+static bool ep_config(uint8_t ep, uint8_t eptype, uint16_t epsize) {
+ if (ep == 0) {
+ /* configuring 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 << USB_OTG_DOEPTSIZ_STUPCNT_Pos) | \
+ (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos);
+ 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;
+}
+
+static 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;
+ }
+ /* 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;
+ }
+ epo->DOEPINT = 0xFF;
+}
+
+static int32_t ep_read(uint8_t ep, void* buf, uint16_t blen) {
+ uint32_t len, tmp;
+ volatile uint32_t *fifo = EPFIFO(0);
+ /* 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 idx = 0; idx < len; idx++) {
+ if ((idx & 0x03) == 0x00) {
+ tmp = *fifo;
+ }
+ if (idx < blen) {
+ ((uint8_t*)buf)[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ }
+ }
+ return (len < blen) ? len : blen;
+}
+
+static int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ volatile uint32_t* fifo = EPFIFO(ep);
+ USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+ /* transfer data size in 32-bit words */
+ 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 << USB_OTG_DIEPTSIZ_PKTCNT_Pos) + blen;
+ _BMD(epi->DIEPCTL, USB_OTG_DIEPCTL_STALL, USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK);
+ /* push data to FIFO */
+ tmp = 0;
+ for (int idx = 0; idx < blen; idx++) {
+ tmp |= (uint32_t)((uint8_t*)buf)[idx] << ((idx & 0x03) << 3);
+ if ((idx & 0x03) == 0x03 || (idx + 1) == blen) {
+ *fifo = tmp;
+ tmp = 0;
+ }
+ }
+ return blen;
+}
+
+static uint16_t get_frame (void) {
+ return _FLD2VAL(USB_OTG_DSTS_FNSOF, OTGD->DSTS);
+}
+
+static 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: /* OUT recieved */
+ evt = usbd_evt_eprx;
+ break;
+ case 0x06: /* SETUP recieved */
+ /* flushing TX if sonething stuck in control endpoint */
+ if (EPIN(ep)->DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT) {
+ Flush_TX(ep);
+ }
+ evt = usbd_evt_epsetup;
+ break;
+ case 0x03: /* OUT completed */
+ case 0x04: /* SETUP completed */
+ _BST(EPOUT(ep)->DOEPCTL, USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
+ default:
+ /* pop GRXSTSP */
+ OTG->GRXSTSP;
+ continue;
+ }
+#if !defined(USBD_SOF_DISABLED)
+ } else if (_t & USB_OTG_GINTSTS_SOF) {
+ OTG->GINTSTS = USB_OTG_GINTSTS_SOF;
+ evt = usbd_evt_sof;
+#endif
+ } 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;
+}
+
+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 + 0x08));
+ 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_otgfs = {
+ 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_STM32F105
diff --git a/port/stm32/usbd_stm32f429_otgfs.c b/port/stm32/usbd_stm32f429_otgfs.c
new file mode 100644
index 00000000..1633e041
--- /dev/null
+++ b/port/stm32/usbd_stm32f429_otgfs.c
@@ -0,0 +1,482 @@
+/* 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_STM32F429FS)
+
+#define MAX_EP 4
+#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)
+
+#define STATUS_VAL(x) (USBD_HW_ADDRFST | (x))
+
+static USB_OTG_GlobalTypeDef * const OTG = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_GLOBAL_BASE);
+static USB_OTG_DeviceTypeDef * const OTGD = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_DEVICE_BASE);
+static volatile uint32_t * const OTGPCTL = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_PCGCCTL_BASE);
+
+
+inline static uint32_t* EPFIFO(uint32_t ep) {
+ return (uint32_t*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + (ep << 12));
+}
+
+inline static USB_OTG_INEndpointTypeDef* EPIN(uint32_t ep) {
+ return (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + (ep << 5));
+}
+
+inline static USB_OTG_OUTEndpointTypeDef* EPOUT(uint32_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);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->AHB2ENR & RCC_AHB2ENR_OTGFSEN)) return STATUS_VAL(0);
+ if (!(OTGD->DCTL & USB_OTG_DCTL_SDIS)) 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) {
+ 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;
+ }
+ }
+}
+
+static 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;
+ }
+}
+
+static void enable(bool enable) {
+ if (enable) {
+ /* enabling USB_OTG in RCC */
+ _BST(RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN);
+ /* waiting AHB ready */
+ _WBS(OTG->GRSTCTL, USB_OTG_GRSTCTL_AHBIDL);
+ /* configure OTG as device */
+ _BMD(OTG->GUSBCFG,
+ USB_OTG_GUSBCFG_SRPCAP | _VAL2FLD(USB_OTG_GUSBCFG_TRDT, 0x0F),
+ USB_OTG_GUSBCFG_FDMOD | _VAL2FLD(USB_OTG_GUSBCFG_TRDT, 0x06));
+ /* configuring Vbus sense and SOF output */
+#if defined (USBD_VBUS_DETECT) && defined(USBD_SOF_OUT)
+ OTG->GCCFG = USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_SOFOUTEN;
+#elif defined(USBD_VBUS_DETECT)
+ OTG->GCCFG = USB_OTG_GCCFG_VBUSBSEN;
+#elif defined(USBD_SOF_OUT)
+ OTG->GCCFG = USB_OTG_GCCFG_NOVBUSSENS | USB_OTG_GCCFG_SOFOUTEN;
+#else
+ OTG->GCCFG = USB_OTG_GCCFG_NOVBUSSENS;
+#endif
+ /* enable PHY clock */
+ *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));
+ /* setting max RX FIFO size */
+ OTG->GRXFSIZ = RX_FIFO_SZ;
+ /* setting up EP0 TX FIFO SZ as 64 byte */
+ OTG->DIEPTXF0_HNPTXFSIZ = RX_FIFO_SZ | (0x10 << 16);
+ /* unmask EP interrupts */
+ OTGD->DIEPMSK = USB_OTG_DIEPMSK_XFRCM;
+ /* unmask core interrupts */
+ OTG->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM |
+#if !defined(USBD_SOF_DISABLED)
+ USB_OTG_GINTMSK_SOFM |
+#endif
+ 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 */
+ _BST(OTG->GAHBCFG, USB_OTG_GAHBCFG_GINT);
+ } else {
+ if (RCC->AHB2ENR & RCC_AHB2ENR_OTGFSEN) {
+ _BST(RCC->AHB2RSTR, RCC_AHB2RSTR_OTGFSRST);
+ _BCL(RCC->AHB2RSTR, RCC_AHB2RSTR_OTGFSRST);
+ _BCL(RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN);
+ }
+ }
+}
+
+static uint8_t connect(bool connect) {
+ if (connect) {
+/* The ST made a strange thing again. Really i dont'understand what is the reason to name
+ signal as PWRDWN (Power down PHY) when it works as "Power up" */
+ _BST(OTG->GCCFG, USB_OTG_GCCFG_PWRDWN);
+ _BCL(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ } else {
+ _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ _BCL(OTG->GCCFG, USB_OTG_GCCFG_PWRDWN);
+ }
+ return usbd_lane_unk;
+}
+
+static 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->DIEPTXF0_HNPTXFSIZ;
+ /* 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;
+}
+
+static 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;
+}
+
+static 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;
+ }
+ /* 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;
+ }
+ epo->DOEPINT = 0xFF;
+}
+
+static int32_t ep_read(uint8_t ep, void* buf, uint16_t blen) {
+ uint32_t len, tmp;
+ volatile uint32_t *fifo = EPFIFO(0);
+ /* 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 idx = 0; idx < len; idx++) {
+ if ((idx & 0x03) == 0x00) {
+ tmp = *fifo;
+ }
+ if (idx < blen) {
+ ((uint8_t*)buf)[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ }
+ }
+ return (len < blen) ? len : blen;
+}
+
+static int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ volatile uint32_t* fifo = EPFIFO(ep);
+ USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+ /* transfer data size in 32-bit words */
+ 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);
+ /* push data to FIFO */
+ tmp = 0;
+ for (int idx = 0; idx < blen; idx++) {
+ tmp |= (uint32_t)((uint8_t*)buf)[idx] << ((idx & 0x03) << 3);
+ if ((idx & 0x03) == 0x03 || (idx + 1) == blen) {
+ *fifo = tmp;
+ tmp = 0;
+ }
+ }
+ return blen;
+}
+
+static uint16_t get_frame (void) {
+ return _FLD2VAL(USB_OTG_DSTS_FNSOF, OTGD->DSTS);
+}
+
+static 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: /* OUT recieved */
+ evt = usbd_evt_eprx;
+ break;
+ case 0x06: /* SETUP recieved */
+ /* flushing TX if sonething stuck in control endpoint */
+ if (EPIN(ep)->DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT) {
+ Flush_TX(ep);
+ }
+ evt = usbd_evt_epsetup;
+ break;
+ case 0x03: /* OUT completed */
+ case 0x04: /* SETUP completed */
+ _BST(EPOUT(ep)->DOEPCTL, USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
+ default:
+ /* pop GRXSTSP */
+ OTG->GRXSTSP;
+ continue;
+ }
+#if !defined(USBD_SOF_DISABLED)
+ } else if (_t & USB_OTG_GINTSTS_SOF) {
+ OTG->GINTSTS = USB_OTG_GINTSTS_SOF;
+ evt = usbd_evt_sof;
+#endif
+ } 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;
+}
+
+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 + 0x08));
+ 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_otgfs = {
+ 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_STM32F429FS
diff --git a/port/stm32/usbd_stm32f429_otghs.c b/port/stm32/usbd_stm32f429_otghs.c
new file mode 100644
index 00000000..edab38ff
--- /dev/null
+++ b/port/stm32/usbd_stm32f429_otghs.c
@@ -0,0 +1,486 @@
+/* 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_STM32F429HS)
+
+#define MAX_EP 6
+#define MAX_RX_PACKET 128
+#define MAX_CONTROL_EP 1
+#define MAX_FIFO_SZ 1024 /*in 32-bit chunks */
+
+#define RX_FIFO_SZ (10 + (2 * (MAX_RX_PACKET / 4) + 1))
+
+#define STATUS_VAL(x) (USBD_HW_ADDRFST | (x))
+
+static USB_OTG_GlobalTypeDef * const OTG = (void*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_GLOBAL_BASE);
+static USB_OTG_DeviceTypeDef * const OTGD = (void*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_DEVICE_BASE);
+static volatile uint32_t * const OTGPCTL = (void*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_PCGCCTL_BASE);
+
+
+inline static uint32_t* EPFIFO(uint32_t ep) {
+ return (uint32_t*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_FIFO_BASE + (ep << 12));
+}
+
+inline static USB_OTG_INEndpointTypeDef* EPIN(uint32_t ep) {
+ return (void*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + (ep << 5));
+}
+
+inline static USB_OTG_OUTEndpointTypeDef* EPOUT(uint32_t ep) {
+ return (void*)(USB_OTG_HS_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);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->AHB1ENR & RCC_AHB1ENR_OTGHSEN)) return STATUS_VAL(0);
+ if (!(OTGD->DCTL & USB_OTG_DCTL_SDIS)) {
+ if (_FLD2VAL(USB_OTG_DSTS_ENUMSPD, OTGD->DSTS) == 0x00) return STATUS_VAL(USBD_HW_ENABLED | USBD_HW_SPEED_HS);
+ if (_FLD2VAL(USB_OTG_DSTS_ENUMSPD, OTGD->DSTS) == 0x03) return STATUS_VAL(USBD_HW_ENABLED | USBD_HW_SPEED_FS);
+ }
+ return STATUS_VAL(USBD_HW_ENABLED | USBD_HW_SPEED_NC);
+}
+
+static 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;
+ }
+ }
+}
+
+static 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;
+ }
+}
+
+static void enable(bool enable) {
+ if (enable) {
+ /* enabling USB_OTG in RCC */
+ _BST(RCC->AHB1ENR, RCC_AHB1ENR_OTGHSEN);
+ _WBS(OTG->GRSTCTL, USB_OTG_GRSTCTL_AHBIDL);
+ /* configure OTG as device */
+ OTG->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_PHYSEL |
+ _VAL2FLD(USB_OTG_GUSBCFG_TRDT, 0x09) |
+ _VAL2FLD(USB_OTG_GUSBCFG_TOCAL, 0x01);
+/* VBUS detect alternafe function AF12 for PB13 is missed in tech documentation */
+#if defined(USBD_VBUS_DETECT) && defined(USBD_SOF_OUT)
+ OTG->GCCFG = USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_SOFOUTEN;
+#elif defined(USBD_VBUS_DETECT)
+ OTG->GCCFG = USB_OTG_GCCFG_VBUSBSEN;
+#elif defined(USBD_SOF_OUT)
+ OTG->GCCFG = USB_OTG_GCCFG_NOVBUSSENS | USB_OTG_GCCFG_SOFOUTEN;
+#else
+ OTG->GCCFG = USB_OTG_GCCFG_NOVBUSSENS;
+#endif
+ /* do core soft reset */
+ _BST(OTG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
+ _WBC(OTG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
+ /* Setup USB FS speed */
+ _BMD(OTGD->DCFG, USB_OTG_DCFG_DSPD, _VAL2FLD(USB_OTG_DCFG_DSPD, 0x03));
+ /* start PHY clock */
+ *OTGPCTL = 0;
+ /* soft disconnect device */
+ _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ /* setting max RX FIFO size */
+ OTG->GRXFSIZ = RX_FIFO_SZ;
+ /* setting up EP0 TX FIFO SZ as 64 byte */
+ OTG->DIEPTXF0_HNPTXFSIZ = RX_FIFO_SZ | (0x10 << 16);
+ /* unmask EP interrupts */
+ OTGD->DIEPMSK = USB_OTG_DIEPMSK_XFRCM;
+ /* unmask core interrupts */
+ OTG->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM |
+#if !defined(USBD_SOF_DISABLED)
+ USB_OTG_GINTMSK_SOFM |
+#endif
+ 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 */
+ _BST(OTG->GAHBCFG, USB_OTG_GAHBCFG_GINT);
+ } else {
+ if (RCC->AHB1ENR & RCC_AHB1ENR_OTGHSEN) {
+ _BST(RCC->AHB1RSTR, RCC_AHB1RSTR_OTGHRST);
+ _BCL(RCC->AHB1RSTR, RCC_AHB1RSTR_OTGHRST);
+ _BCL(RCC->AHB1ENR, RCC_AHB1ENR_OTGHSEN);
+ }
+ }
+}
+
+static uint8_t connect(bool connect) {
+ if (connect) {
+ _BST(OTG->GCCFG, USB_OTG_GCCFG_PWRDWN);
+ _BCL(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ } else {
+ _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ _BCL(OTG->GCCFG, USB_OTG_GCCFG_PWRDWN);
+ }
+ return usbd_lane_unk;
+}
+
+static 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->DIEPTXF0_HNPTXFSIZ;
+ /* 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;
+}
+
+static 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;
+}
+
+static 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;
+ }
+ /* 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;
+ }
+ epo->DOEPINT = 0xFF;
+}
+
+static int32_t ep_read(uint8_t ep, void* buf, uint16_t blen) {
+ uint32_t len, tmp;
+ volatile uint32_t *fifo = EPFIFO(0);
+ /* 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 idx = 0; idx < len; idx++) {
+ if ((idx & 0x03) == 0x00) {
+ tmp = *fifo;
+ }
+ if (idx < blen) {
+ ((uint8_t*)buf)[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ }
+ }
+ return (len < blen) ? len : blen;
+}
+
+static int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ volatile uint32_t* fifo = EPFIFO(ep);
+ USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+ /* transfer data size in 32-bit words */
+ len = (blen + 3) >> 2;
+ /* no enough space in TX fifo */
+ if (len > _FLD2VAL(USB_OTG_DTXFSTS_INEPTFSAV, epi->DTXFSTS)) return -1;
+ if (ep != 0 && epi->DIEPCTL & USB_OTG_DIEPCTL_EPENA) {
+ return -1;
+ }
+ _BMD(epi->DIEPTSIZ,
+ USB_OTG_DIEPTSIZ_PKTCNT | USB_OTG_DIEPTSIZ_MULCNT | USB_OTG_DIEPTSIZ_XFRSIZ,
+ _VAL2FLD(USB_OTG_DIEPTSIZ_PKTCNT, 1) | _VAL2FLD(USB_OTG_DIEPTSIZ_MULCNT, 1 ) | _VAL2FLD(USB_OTG_DIEPTSIZ_XFRSIZ, blen));
+ _BMD(epi->DIEPCTL, USB_OTG_DIEPCTL_STALL, USB_OTG_DOEPCTL_CNAK);
+ _BST(epi->DIEPCTL, USB_OTG_DOEPCTL_EPENA);
+ /* push data to FIFO */
+ tmp = 0;
+ for (int idx = 0; idx < blen; idx++) {
+ tmp |= (uint32_t)((uint8_t*)buf)[idx] << ((idx & 0x03) << 3);
+ if ((idx & 0x03) == 0x03 || (idx + 1) == blen) {
+ *fifo = tmp;
+ tmp = 0;
+ }
+ }
+ return blen;
+}
+
+static uint16_t get_frame (void) {
+ return _FLD2VAL(USB_OTG_DSTS_FNSOF, OTGD->DSTS);
+}
+
+static 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) {
+ _BST(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: /* OUT recieved */
+ evt = usbd_evt_eprx;
+ break;
+ case 0x06: /* SETUP recieved */
+ /* flushing TX if something stuck in control endpoint */
+ if (EPIN(ep)->DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT) {
+ Flush_TX(ep);
+ }
+ evt = usbd_evt_epsetup;
+ break;
+ case 0x03: /* OUT completed */
+ case 0x04: /* SETUP completed */
+ _BST(EPOUT(ep)->DOEPCTL, USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
+ default:
+ /* pop GRXSTSP */
+ OTG->GRXSTSP;
+ continue;
+ }
+#if !defined(USBD_SOF_DISABLED)
+ } else if (_t & USB_OTG_GINTSTS_SOF) {
+ OTG->GINTSTS = USB_OTG_GINTSTS_SOF;
+ evt = usbd_evt_sof;
+#endif
+ } 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;
+}
+
+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 + 0x08));
+ 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_otghs = {
+ 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_STM32F429HS
diff --git a/port/stm32/usbd_stm32f446_otgfs.c b/port/stm32/usbd_stm32f446_otgfs.c
new file mode 100644
index 00000000..93cb2c33
--- /dev/null
+++ b/port/stm32/usbd_stm32f446_otgfs.c
@@ -0,0 +1,467 @@
+/* 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_STM32F446FS)
+
+#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)
+
+#define STATUS_VAL(x) (USBD_HW_ADDRFST | (x))
+
+static USB_OTG_GlobalTypeDef * const OTG = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_GLOBAL_BASE);
+static USB_OTG_DeviceTypeDef * const OTGD = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_DEVICE_BASE);
+static volatile uint32_t * const OTGPCTL = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_PCGCCTL_BASE);
+
+
+inline static uint32_t* EPFIFO(uint32_t ep) {
+ return (uint32_t*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + (ep << 12));
+}
+
+inline static USB_OTG_INEndpointTypeDef* EPIN(uint32_t ep) {
+ return (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + (ep << 5));
+}
+
+inline static USB_OTG_OUTEndpointTypeDef* EPOUT(uint32_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);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->AHB2ENR & RCC_AHB2ENR_OTGFSEN)) return STATUS_VAL(0);
+ if (!(OTGD->DCTL & USB_OTG_DCTL_SDIS)) 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) {
+ 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;
+ }
+ }
+}
+
+static 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;
+ }
+}
+
+static void enable(bool enable) {
+ if (enable) {
+ /* enabling USB_OTG in RCC */
+ _BST(RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN);
+ _WBS(OTG->GRSTCTL, USB_OTG_GRSTCTL_AHBIDL);
+ /* 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 defined(USBD_VBUS_DETECT)
+ 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 |
+#if !defined(USBD_SOF_DISABLED)
+ USB_OTG_GINTMSK_SOFM |
+#endif
+ 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->DIEPTXF0_HNPTXFSIZ = RX_FIFO_SZ | (0x10 << 16);
+ } else {
+ if (RCC->AHB2ENR & RCC_AHB2ENR_OTGFSEN) {
+ _BST(RCC->AHB2RSTR, RCC_AHB2RSTR_OTGFSRST);
+ _BCL(RCC->AHB2RSTR, RCC_AHB2RSTR_OTGFSRST);
+ _BCL(RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN);
+ }
+ }
+}
+
+static uint8_t connect(bool connect) {
+ if (connect) {
+ _BCL(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ } else {
+ _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ }
+ return usbd_lane_unk;
+}
+
+static 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->DIEPTXF0_HNPTXFSIZ;
+ /* 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;
+}
+
+static 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;
+}
+
+static 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;
+ }
+ /* 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;
+ }
+ epo->DOEPINT = 0xFF;
+}
+
+static int32_t ep_read(uint8_t ep, void* buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ 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;
+ if ((OTG->GRXSTSR & USB_OTG_GRXSTSP_EPNUM) != ep) return -1;
+ /* pop data from fifo */
+ len = _FLD2VAL(USB_OTG_GRXSTSP_BCNT, OTG->GRXSTSP);
+ for (int idx = 0; idx < len; idx++) {
+ if ((idx & 0x03) == 0x00) {
+ tmp = *fifo;
+ }
+ if (idx < blen) {
+ ((uint8_t*)buf)[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ }
+ }
+ _BST(epo->DOEPCTL, USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
+ return (len < blen) ? len : blen;
+}
+
+static int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ volatile uint32_t* fifo = EPFIFO(ep);
+ USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+ /* transfer data size in 32-bit words */
+ 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);
+ /* push data to FIFO */
+ tmp = 0;
+ for (int idx = 0; idx < blen; idx++) {
+ tmp |= (uint32_t)((uint8_t*)buf)[idx] << ((idx & 0x03) << 3);
+ if ((idx & 0x03) == 0x03 || (idx + 1) == blen) {
+ *fifo = tmp;
+ tmp = 0;
+ }
+ }
+ return blen;
+}
+
+static uint16_t get_frame (void) {
+ return _FLD2VAL(USB_OTG_DSTS_FNSOF, OTGD->DSTS);
+}
+
+static 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;
+ }
+#if !defined(USBD_SOF_DISABLED)
+ } else if (_t & USB_OTG_GINTSTS_SOF) {
+ OTG->GINTSTS = USB_OTG_GINTSTS_SOF;
+ evt = usbd_evt_sof;
+#endif
+ } 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;
+}
+
+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 + 0x08));
+ 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_otgfs = {
+ 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_STM32L446FS
diff --git a/port/stm32/usbd_stm32f446_otghs.c b/port/stm32/usbd_stm32f446_otghs.c
new file mode 100644
index 00000000..d65d2e28
--- /dev/null
+++ b/port/stm32/usbd_stm32f446_otghs.c
@@ -0,0 +1,467 @@
+/* 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_STM32F446HS)
+
+#define MAX_EP 9
+#define MAX_RX_PACKET 128
+#define MAX_CONTROL_EP 1
+#define MAX_FIFO_SZ 1024 /*in 32-bit chunks */
+
+#define RX_FIFO_SZ ((4 * MAX_CONTROL_EP + 6) + ((MAX_RX_PACKET / 4) + 1) + (MAX_EP * 2) + 1)
+
+#define STATUS_VAL(x) (USBD_HW_ADDRFST | (x))
+
+static USB_OTG_GlobalTypeDef * const OTG = (void*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_GLOBAL_BASE);
+static USB_OTG_DeviceTypeDef * const OTGD = (void*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_DEVICE_BASE);
+static volatile uint32_t * const OTGPCTL = (void*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_PCGCCTL_BASE);
+
+
+inline static uint32_t* EPFIFO(uint32_t ep) {
+ return (uint32_t*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_FIFO_BASE + (ep << 12));
+}
+
+inline static USB_OTG_INEndpointTypeDef* EPIN(uint32_t ep) {
+ return (void*)(USB_OTG_HS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + (ep << 5));
+}
+
+inline static USB_OTG_OUTEndpointTypeDef* EPOUT(uint32_t ep) {
+ return (void*)(USB_OTG_HS_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);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->AHB2ENR & RCC_AHB2ENR_OTGFSEN)) return STATUS_VAL(0);
+ if (!(OTGD->DCTL & USB_OTG_DCTL_SDIS)) 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) {
+ 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;
+ }
+ }
+}
+
+static 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;
+ }
+}
+
+static void enable(bool enable) {
+ if (enable) {
+ /* enabling USB_OTG in RCC */
+ _BST(RCC->AHB1ENR, RCC_AHB1ENR_OTGHSEN);
+ _WBS(OTG->GRSTCTL, USB_OTG_GRSTCTL_AHBIDL);
+ /* 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 defined(USBD_VBUS_DETECT)
+ 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));
+ /* setting max RX FIFO size */
+ OTG->GRXFSIZ = RX_FIFO_SZ;
+ /* setting up EP0 TX FIFO SZ as 64 byte */
+ OTG->DIEPTXF0_HNPTXFSIZ = RX_FIFO_SZ | (0x10 << 16);
+ /* unmask EP interrupts */
+ OTGD->DIEPMSK = USB_OTG_DIEPMSK_XFRCM;
+ /* unmask core interrupts */
+ OTG->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM |
+#if !defined(USBD_SOF_DISABLED)
+ USB_OTG_GINTMSK_SOFM |
+#endif
+ 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;
+ } else {
+ if (RCC->AHB1ENR & RCC_AHB1ENR_OTGHSEN) {
+ _BST(RCC->AHB1RSTR, RCC_AHB1RSTR_OTGHRST);
+ _BCL(RCC->AHB1RSTR, RCC_AHB1RSTR_OTGHRST);
+ _BCL(RCC->AHB1ENR, RCC_AHB1ENR_OTGHSEN);
+ }
+ }
+}
+
+static uint8_t connect(bool connect) {
+ if (connect) {
+ _BCL(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ } else {
+ _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ }
+ return usbd_lane_unk;
+}
+
+static 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->DIEPTXF0_HNPTXFSIZ;
+ /* 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;
+}
+
+static 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;
+}
+
+static 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;
+ }
+ /* 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;
+ }
+ epo->DOEPINT = 0xFF;
+}
+
+static int32_t ep_read(uint8_t ep, void* buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ 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;
+ if ((OTG->GRXSTSR & USB_OTG_GRXSTSP_EPNUM) != ep) return -1;
+ /* pop data from fifo */
+ len = _FLD2VAL(USB_OTG_GRXSTSP_BCNT, OTG->GRXSTSP);
+ for (int idx = 0; idx < len; idx++) {
+ if ((idx & 0x03) == 0x00) {
+ tmp = *fifo;
+ }
+ if (idx < blen) {
+ ((uint8_t*)buf)[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ }
+ }
+ _BST(epo->DOEPCTL, USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
+ return (len < blen) ? len : blen;
+}
+
+static int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ volatile uint32_t* fifo = EPFIFO(ep);
+ USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+ /* transfer data size in 32-bit words */
+ 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);
+ /* push data to FIFO */
+ tmp = 0;
+ for (int idx = 0; idx < blen; idx++) {
+ tmp |= (uint32_t)((uint8_t*)buf)[idx] << ((idx & 0x03) << 3);
+ if ((idx & 0x03) == 0x03 || (idx + 1) == blen) {
+ *fifo = tmp;
+ tmp = 0;
+ }
+ }
+ return blen;
+}
+
+static uint16_t get_frame (void) {
+ return _FLD2VAL(USB_OTG_DSTS_FNSOF, OTGD->DSTS);
+}
+
+static 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;
+ }
+#if !defined(USBD_SOF_DISABLED)
+ } else if (_t & USB_OTG_GINTSTS_SOF) {
+ OTG->GINTSTS = USB_OTG_GINTSTS_SOF;
+ evt = usbd_evt_sof;
+#endif
+ } 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;
+}
+
+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 + 0x08));
+ 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_otghs = {
+ 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_STM32L446FS
diff --git a/port/stm32/usbd_stm32l052_devfs.c b/port/stm32/usbd_stm32l052_devfs.c
new file mode 100644
index 00000000..1b9e0eb3
--- /dev/null
+++ b/port/stm32/usbd_stm32l052_devfs.c
@@ -0,0 +1,472 @@
+/* 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_STM32L052)
+
+#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->APB1ENR & RCC_APB1ENR_USBEN)) 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->APB1ENR |= RCC_APB1ENR_USBEN;
+ RCC->APB1RSTR |= RCC_APB1RSTR_USBRST;
+ RCC->APB1RSTR &= ~RCC_APB1RSTR_USBRST;
+#if defined(USBD_PINS_REMAP) && (defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F070x6))
+ RCC->APB2ENR |= RCC_APB2ENR_SYSCFGCOMPEN;
+ SYSCFG->CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP; // remap USB pins for small packages
+#endif
+ 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->APB1ENR & RCC_APB1ENR_USBEN) {
+ USB->BCDR = 0;
+ RCC->APB1RSTR |= RCC_APB1RSTR_USBRST;
+ RCC->APB1ENR &= ~RCC_APB1ENR_USBEN;
+ }
+}
+
+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 tmp;
+ uint16_t *pma = (void*)(USB_PMAADDR + rx->addr);
+ uint16_t rxcnt = rx->cnt & 0x03FF;
+ rx->cnt &= ~0x3FF;
+ for(int idx = 0; idx < rxcnt; idx++) {
+ if ((idx & 0x01) == 0) {
+ tmp = *pma++;
+ }
+ if (idx < blen) {
+ buf[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ } else {
+ return blen;
+ }
+ }
+ 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);
+ uint16_t tmp = 0;
+ tx->cnt = blen;
+ for (int idx=0; idx < blen; idx++) {
+ tmp |= buf[idx] << ((idx & 0x01) ? 8 : 0);
+ if ((idx & 0x01) || (idx + 1) == blen) {
+ *pma++ = tmp;
+ tmp = 0;
+ }
+ }
+}
+
+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/port/stm32/usbd_stm32l052_devfs_asm.S b/port/stm32/usbd_stm32l052_devfs_asm.S
new file mode 100644
index 00000000..a5a0ab4c
--- /dev/null
+++ b/port/stm32/usbd_stm32l052_devfs_asm.S
@@ -0,0 +1,850 @@
+/* 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.
+ */
+
+#if !defined (__ASSEMBLER__)
+ #define __ASSEMBLER__
+#endif
+
+#include "usb.h"
+#if defined(USBD_STM32L052) || defined(USBD_STM32L433)
+#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
+
+
+#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
+
+ .align 4
+ .section .rodata.usbd_devfs_asm
+ .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, 0x00] //UID0
+ bl .L_fnv1a
+ ldr r2, [r5, 0x04] //UID1
+ bl .L_fnv1a
+ ldr r2, [r5, 0x14] //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_APB1ENR]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1ENR] //RCC->APB1ENR |= USBEN
+ ldr r0, [r2, #RCC_APB1RSTR]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1RSTR] //RCC->APB1RSTR |= USBRST
+ bics r0, r3
+ str r0, [r2, #RCC_APB1RSTR] //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_APB1ENR]
+ 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_APB1RSTR]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1RSTR] //RCC->APB1RSTR |= USBRST
+ ldr r0, [r2, #RCC_APB1ENR]
+ bics r0, r3
+ str r0, [r2, #RCC_APB1ENR] //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_APB1ENR]
+ 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
diff --git a/port/stm32/usbd_stm32l100_devfs.c b/port/stm32/usbd_stm32l100_devfs.c
new file mode 100644
index 00000000..29bd2598
--- /dev/null
+++ b/port/stm32/usbd_stm32l100_devfs.c
@@ -0,0 +1,457 @@
+/* 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_STM32L100)
+
+#ifndef USB_PMASIZE
+ #pragma message "PMA memory size is not defined. Use 512 bytes by default"
+ #define USB_PMASIZE 0x200
+#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) (x)
+
+typedef struct {
+ uint16_t addr;
+ uint16_t :16;
+ uint16_t cnt;
+ uint16_t :16;
+} 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) * 16 + 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->tx.addr) && (tbl->tx.addr < _result)) _result = tbl->tx.addr;
+ if ((tbl->rx.addr) && (tbl->rx.addr < _result)) _result = tbl->rx.addr;
+ }
+ return (_result < (0x020 + sz)) ? 0 : (_result - sz);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->APB1ENR & RCC_APB1ENR_USBEN)) return STATUS_VAL(0);
+ if (SYSCFG->PMC & SYSCFG_PMC_USB_PU) 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->APB1ENR |= RCC_APB1ENR_USBEN;
+ RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
+ RCC->APB1RSTR |= RCC_APB1RSTR_USBRST;
+ RCC->APB1RSTR &= ~RCC_APB1RSTR_USBRST;
+ 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->APB1ENR & RCC_APB1ENR_USBEN) {
+ SYSCFG->PMC &= ~SYSCFG_PMC_USB_PU;
+ RCC->APB1RSTR |= RCC_APB1RSTR_USBRST;
+ RCC->APB1ENR &= ~RCC_APB1ENR_USBEN;
+ }
+}
+
+static uint8_t connect(bool connect) {
+ if (connect) {
+ SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
+ } else {
+ SYSCFG->PMC &= ~SYSCFG_PMC_USB_PU;
+ }
+ return usbd_lane_unk;
+}
+
+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 tmp;
+ uint16_t *pma = (void*)(USB_PMAADDR + 2 * rx->addr);
+ uint16_t rxcnt = rx->cnt & 0x03FF;
+ rx->cnt &= ~0x3FF;
+ for(int idx = 0; idx < rxcnt; idx++) {
+ if ((idx & 0x01) == 0) {
+ tmp = *pma++;
+ }
+ if (idx < blen) {
+ buf[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ } else {
+ return blen;
+ }
+ }
+ 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(const uint8_t *buf, uint16_t blen, pma_rec *tx) {
+ uint16_t *pma = (void*)(USB_PMAADDR + 2 * (tx->addr));
+ uint16_t tmp = 0;
+ tx->cnt = blen;
+ for (int idx=0; idx < blen; idx++) {
+ tmp |= buf[idx] << ((idx & 0x01) ? 8 : 0);
+ if ((idx & 0x01) || (idx + 1) == blen) {
+ *pma++ = tmp;
+ tmp = 0;
+ }
+ }
+}
+
+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_STM32L100
diff --git a/port/stm32/usbd_stm32l100_devfs_asm.S b/port/stm32/usbd_stm32l100_devfs_asm.S
new file mode 100644
index 00000000..dc3c48b2
--- /dev/null
+++ b/port/stm32/usbd_stm32l100_devfs_asm.S
@@ -0,0 +1,831 @@
+/* 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.
+ */
+
+#if !defined (__ASSEMBLER__)
+ #define __ASSEMBLER__
+#endif
+
+#include "usb.h"
+#if defined(USBD_STM32L100)
+#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 0x04
+#define RXADDR1 0x08
+#define RXCOUNT1 0x0C
+
+#define TXADDR0 0x00
+#define TXCOUNT0 0x04
+#define TXADDR1 0x08
+#define TXCOUNT1 0x0C
+
+#define TXADDR 0x00
+#define TXCOUNT 0x04
+#define RXADDR 0x08
+#define RXCOUNT 0x0C
+
+
+
+#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-m3
+ .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, 0x00] //UID0
+ bl .L_fnv1a
+ ldr r2, [r5, 0x04] //UID1
+ bl .L_fnv1a
+ ldr r2, [r5, 0x14] //UID2
+ bl .L_fnv1a
+ movs r3, #28
+.L_gsn_loop:
+ lsrs r1, r4, r3
+ and r1, #0x0F
+ cmp r1, #0x09
+ ite gt
+ addgt r1, #55
+ addle r1, #48
+.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_uid_base: .long UID_BASE
+.L_fnv1a_offset: .long 2166136261
+.L_fnv1a_prime: .long 16777619
+
+ .size _get_serial_desc, . - _get_serial_desc
+
+ .thumb_func
+ .type _connect, %function
+_connect:
+ ldr r1, =SYSCFG_BASE
+ movs r3, #0x01
+ ldr r2, [r1, #SYSCFG_PMC]
+ bics r2, r3
+ cbz r0, .L_conn_store
+ orrs r2, r3
+.L_conn_store:
+ str r2, [r1, #SYSCFG_PMC]
+ movs r0, #usbd_lane_unk
+ bx lr
+ .size _connect, . - _connect
+
+ .thumb_func
+ .type _getinfo, %function
+_getinfo:
+ movs r0, 0
+ ldr r2, =RCC_BASE
+ ldr r1, [r2, #RCC_APB1ENR]
+ lsrs r1, #24 //USBEN -> CF
+ bcc .L_getinfo_end
+ adds r0, #USBD_HW_ENABLED
+ ldr r2, =SYSCFG_BASE
+ ldr r1, [r2, #SYSCFG_PMC]
+ lsrs r1, #1 //PU -> CF
+ bcc .L_getinfo_end
+ adds r0, #USBD_HW_SPEED_FS
+.L_getinfo_end:
+ bx lr
+ .size _getinfo, . - _getinfo
+
+ .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 r2, =RCC_BASE //RCC
+ movs r3, #0x01
+ lsls r3, #23 //USBEN or USBRST
+ cbz r0, .L_disable
+.L_enable:
+/* enabling and resetting USB peripheral */
+ ldr r1, =USB_REGBASE
+ ldr r0, [r2, #RCC_APB1ENR]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1ENR] //RCC->APB1ENR |= USBEN
+ ldr r0, [r2, #RCC_APB1RSTR]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB1RSTR] //RCC->APB1RSTR |= USBRST
+ bics r0, r3
+ str r0, [r2, #RCC_APB1RSTR] //RCC->APB1RSTR &= ~USBRST
+/* enabling SYSCFG peripheral */
+ movs r3, #0x01 //SYSCFGEN
+ ldr r0, [r2, #RCC_APB2ENR]
+ orrs r0, r3
+ str r0, [r2, #RCC_APB2ENR]
+/* setting up USB CNTR */
+#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, #USB_CNTR] //set USB->CNTR
+ bx lr
+.L_disable:
+ ldr r0, [r2, #RCC_APB1ENR]
+ tst r0, r3
+ beq .L_enable_end // usb is already disabled
+/* disabling USB peripheral */
+ bics r0, r3
+ str r0, [r2, #RCC_APB1ENR]
+/* disabling USB_PU in SYSCFG_PMC */
+ movs r3, #0x01
+ ldr r1, =SYSCFG_BASE
+ ldr r0, [r1, #SYSCFG_PMC]
+ bics r0, r3
+ str r0, [r1, #SYSCFG_PMC]
+ bx lr
+.L_enable_end:
+ bx lr
+ .size _enable, . - _enable
+
+ .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 0 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, #2
+ 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, #0x08 // set RXADDR0
+.L_epr_sngl:
+ ldrh r0, [r4, #RXCOUNT]
+ lsrs r5, r0, #0x0A
+ lsls r5, #0x0A // r5 = r5 & ~0x03FF
+ strh r5, [r4, #RXCOUNT]
+ lsls r0, #22
+ lsrs r0, #22 // r0 &= 0x3FF (RX count)
+ ldrh r5, [r4, #RXADDR]
+ ldr r4, =USB_PMABASE
+ lsls r5, #0x01
+ 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, #4
+ 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
+ * result -> R0
+ */
+_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, #2
+ 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, #8 // TXADDR1 -> R4
+.L_epw_sngl:
+ strh r2, [r4, #TXCOUNT]
+ mov r0, r2 // save count for return
+ ldrh r5, [r4, #TXADDR]
+ ldr r4, =USB_PMABASE
+ lsls r5, #1
+ 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, #4
+ 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, #16
+ movs r3, #1
+ lsls r3, #9 //R3 MAX_PMA_SIZE 512b
+ ldr r0, =USB_PMABASE
+.L_gnp_chkaddr:
+ ldrh r4, [r0, #0] //txaddr
+ tst r4, r4
+ beq .L_gnp_nxtaddr
+ cmp r3, r4
+ blo .L_gnp_nxtaddr
+ mov r3, r4
+.L_gnp_nxtaddr:
+ adds r0, #8
+ subs r1, #1
+ bne .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, #2
+ adds r5, r4
+ 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 // is ISO
+ bne .L_epc_txsetstate //
+.L_epc_txdbl:
+ 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, #2
+ 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:
+ movs r3, r2
+ cmp r2, #62
+ bls .L_epc_rxbb
+ movs r3, #0x1F
+ adds r2, r3
+ bics r2, r3
+ lsrs r3, r2, #5
+ adds r3, #0x3E
+.L_epc_rxbb:
+ lsls r3, #9
+ ldr r5, =USB_PMABASE
+ lsls r4, #2
+ adds r5, r4
+/* RX or RX1 */
+ bl _get_next_pma
+ bcc .L_epc_fail
+ 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, #2
+ 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, #11 //RESETM -> CF
+ bcs .L_ep_resetm
+ 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
+ /* 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, #0] //R1 USB->CNTR
+ movs r5, #0x08
+ bics r1, r5 //clr FSUSP
+ strh r1, [r3, #0] //USB->CNTR R2
+ movs r1, #usbd_evt_wkup
+ movs r4, #ISTRBIT(12)
+ b .L_ep_clristr
+
+.L_ep_suspm:
+ ldrh r1, [r3, #0] //R1 USB->CNTR
+ movs r5, #0x08
+ orrs r1, r5 //set FSUSP
+ strh r1, [r3, #0] //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, #0
+ strh r4, [r0, #TXADDR]
+ strh r4, [r0, #TXCOUNT]
+ strh r4, [r0, #RXADDR]
+ strh r4, [r0, #RXCOUNT]
+ adds r2, #0x04
+ adds r0, #0x10
+ subs r1, #1
+ bpl .L_ep_reset_loop
+ movs r2, #0x00
+ strh r2, [r3, #0x10] // 0 -> 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_STM32L100
diff --git a/port/stm32/usbd_stm32l433_devfs.c b/port/stm32/usbd_stm32l433_devfs.c
new file mode 100644
index 00000000..c738c67f
--- /dev/null
+++ b/port/stm32/usbd_stm32l433_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_STM32L433)
+
+#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 + 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->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*)(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/port/stm32/usbd_stm32l476_otgfs.c b/port/stm32/usbd_stm32l476_otgfs.c
new file mode 100644
index 00000000..a0246b50
--- /dev/null
+++ b/port/stm32/usbd_stm32l476_otgfs.c
@@ -0,0 +1,500 @@
+/* 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_STM32L476)
+
+#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)
+
+#define STATUS_VAL(x) (USBD_HW_BC | USBD_HW_ADDRFST | (x))
+
+static USB_OTG_GlobalTypeDef * const OTG = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_GLOBAL_BASE);
+static USB_OTG_DeviceTypeDef * const OTGD = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_DEVICE_BASE);
+static volatile uint32_t * const OTGPCTL = (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_PCGCCTL_BASE);
+
+
+inline static uint32_t* EPFIFO(uint32_t ep) {
+ return (uint32_t*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + (ep << 12));
+}
+
+inline static USB_OTG_INEndpointTypeDef* EPIN(uint32_t ep) {
+ return (void*)(USB_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + (ep << 5));
+}
+
+inline static USB_OTG_OUTEndpointTypeDef* EPOUT(uint32_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);
+}
+
+static uint32_t getinfo(void) {
+ if (!(RCC->AHB2ENR & RCC_AHB2ENR_OTGFSEN)) return STATUS_VAL(0);
+ if (!(OTGD->DCTL & USB_OTG_DCTL_SDIS)) 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) {
+ 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;
+ }
+ }
+}
+
+static 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;
+ }
+}
+
+static 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 defined(USBD_VBUS_DETECT)
+ 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 |
+#if !defined(USBD_SOF_DISABLED)
+ USB_OTG_GINTMSK_SOFM |
+#endif
+ 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->DIEPTXF0_HNPTXFSIZ = 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);
+ }
+ }
+}
+
+static uint8_t connect(bool connect) {
+ uint8_t res;
+#if defined(USBD_VBUS_DETECT)
+ #define SET_GCCFG(x) OTG->GCCFG = USB_OTG_GCCFG_VBDEN | (x)
+#else
+ #define SET_GCCFG(x) OTG->GCCFG = (x)
+#endif
+ SET_GCCFG(USB_OTG_GCCFG_BCDEN | USB_OTG_GCCFG_DCDEN);
+ if (OTG->GCCFG & USB_OTG_GCCFG_DCDET) {
+ SET_GCCFG(USB_OTG_GCCFG_BCDEN | USB_OTG_GCCFG_PDEN);
+ if (OTG->GCCFG & USB_OTG_GCCFG_PS2DET) {
+ res = usbd_lane_unk;
+ } else if (OTG->GCCFG & USB_OTG_GCCFG_PDET) {
+ SET_GCCFG(USB_OTG_GCCFG_BCDEN | USB_OTG_GCCFG_SDEN);
+ if (OTG->GCCFG & USB_OTG_GCCFG_SDET) {
+ res = usbd_lane_dcp;
+ } else {
+ res = usbd_lane_cdp;
+ }
+ } else {
+ res = usbd_lane_sdp;
+ }
+ } else {
+ res = usbd_lane_dsc;
+ }
+ SET_GCCFG(USB_OTG_GCCFG_PWRDWN);
+ if (connect) {
+ _BCL(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ } else {
+ _BST(OTGD->DCTL, USB_OTG_DCTL_SDIS);
+ }
+ return res;
+}
+
+static 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->DIEPTXF0_HNPTXFSIZ;
+ /* 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;
+}
+
+static 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;
+}
+
+static 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;
+ }
+ /* 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;
+ }
+ epo->DOEPINT = 0xFF;
+}
+
+static int32_t ep_read(uint8_t ep, void* buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ 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;
+ if ((OTG->GRXSTSR & USB_OTG_GRXSTSP_EPNUM) != ep) return -1;
+ /* pop data from fifo */
+ len = _FLD2VAL(USB_OTG_GRXSTSP_BCNT, OTG->GRXSTSP);
+ for (int idx = 0; idx < len; idx++) {
+ if ((idx & 0x03) == 0x00) {
+ tmp = *fifo;
+ }
+ if (idx < blen) {
+ ((uint8_t*)buf)[idx] = tmp & 0xFF;
+ tmp >>= 8;
+ }
+ }
+ _BST(epo->DOEPCTL, USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
+ return (len < blen) ? len : blen;
+}
+
+static int32_t ep_write(uint8_t ep, void *buf, uint16_t blen) {
+ uint32_t len, tmp;
+ ep &= 0x7F;
+ volatile uint32_t* fifo = EPFIFO(ep);
+ USB_OTG_INEndpointTypeDef* epi = EPIN(ep);
+ /* transfer data size in 32-bit words */
+ 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);
+ /* push data to FIFO */
+ tmp = 0;
+ for (int idx = 0; idx < blen; idx++) {
+ tmp |= (uint32_t)((uint8_t*)buf)[idx] << ((idx & 0x03) << 3);
+ if ((idx & 0x03) == 0x03 || (idx + 1) == blen) {
+ *fifo = tmp;
+ tmp = 0;
+ }
+ }
+ return blen;
+}
+
+static uint16_t get_frame (void) {
+ return _FLD2VAL(USB_OTG_DSTS_FNSOF, OTGD->DSTS);
+}
+
+static 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;
+ }
+#if !defined(USBD_SOF_DISABLED)
+ } else if (_t & USB_OTG_GINTSTS_SOF) {
+ OTG->GINTSTS = USB_OTG_GINTSTS_SOF;
+ evt = usbd_evt_sof;
+#endif
+ } 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;
+}
+
+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 + 0x08));
+ 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_otgfs = {
+ 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_STM32L476