add mcu port without test,just for reference

This commit is contained in:
sakumisu
2021-10-15 23:02:42 +08:00
parent 0672902d62
commit 4397f9009c
29 changed files with 21232 additions and 0 deletions

1
port/README.md Normal file
View File

@@ -0,0 +1 @@
## 提取 port 接口备用

924
port/atmel/usb_dc_sam.c Normal file
View File

@@ -0,0 +1,924 @@
/*
* Copyright (c) 2018 Aurelien Jarno <aurelien@aurel32.net>
*
* 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;
}

728
port/atmel/usb_dc_sam0.c Normal file
View File

@@ -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];
}
}

807
port/atmel/usbd_ATSAM3U2C.c Normal file
View File

@@ -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);
}

1212
port/dw/usb_dc_dw.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}

1928
port/nordic/usb_dc_nrfx.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@@ -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
}
}
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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