Browse Source

USB DCD: Encapsulate Device Controller Drivers so that both can be built.

Keir Fraser 4 years ago
parent
commit
833e887117
10 changed files with 581 additions and 344 deletions
  1. 1 0
      bootloader/usb/Makefile
  2. 1 0
      src/usb/Makefile
  3. 21 0
      src/usb/defs.h
  4. 52 2
      src/usb/hw_at32f415.c
  5. 30 12
      src/usb/hw_dwc_otg.c
  6. 0 2
      src/usb/hw_dwc_otg.h
  7. 24 324
      src/usb/hw_f1.c
  8. 52 2
      src/usb/hw_f7.c
  9. 398 0
      src/usb/hw_usbd.c
  10. 2 2
      src/usb/hw_usbd.h

+ 1 - 0
bootloader/usb/Makefile

@@ -4,6 +4,7 @@ OBJS += config.o
 OBJS += core.o
 OBJS += core.o
 OBJS += cdc_acm.o
 OBJS += cdc_acm.o
 
 
+OBJS-$(stm32f1) += hw_usbd.o
 OBJS-$(stm32f1) += hw_f1.o
 OBJS-$(stm32f1) += hw_f1.o
 
 
 OBJS-$(stm32f7) += hw_dwc_otg.o
 OBJS-$(stm32f7) += hw_dwc_otg.o

+ 1 - 0
src/usb/Makefile

@@ -2,6 +2,7 @@ OBJS += config.o
 OBJS += core.o
 OBJS += core.o
 OBJS += cdc_acm.o
 OBJS += cdc_acm.o
 
 
+OBJS-$(stm32f1) += hw_usbd.o
 OBJS-$(stm32f1) += hw_f1.o
 OBJS-$(stm32f1) += hw_f1.o
 
 
 OBJS-$(stm32f7) += hw_dwc_otg.o
 OBJS-$(stm32f7) += hw_dwc_otg.o

+ 21 - 0
src/usb/defs.h

@@ -84,6 +84,27 @@ void hw_usb_init(void);
 void hw_usb_deinit(void);
 void hw_usb_deinit(void);
 bool_t hw_has_highspeed(void);
 bool_t hw_has_highspeed(void);
 
 
+struct usb_driver {
+    void (*init)(void);
+    void (*deinit)(void);
+    void (*process)(void);
+
+    bool_t (*has_highspeed)(void);
+    bool_t (*is_highspeed)(void);
+
+    void (*setaddr)(uint8_t addr);
+
+    void (*configure_ep)(uint8_t epnr, uint8_t type, uint32_t size);
+    int (*ep_rx_ready)(uint8_t epnr);
+    bool_t (*ep_tx_ready)(uint8_t epnr);
+    void (*read)(uint8_t epnr, void *buf, uint32_t len);
+    void (*write)(uint8_t epnr, const void *buf, uint32_t len);
+    void (*stall)(uint8_t epnr);
+};
+
+extern const struct usb_driver dwc_otg;
+extern const struct usb_driver usbd;
+
 #define WARN printk
 #define WARN printk
 
 
 /*
 /*

+ 52 - 2
src/usb/hw_at32f415.c

@@ -22,16 +22,66 @@ void hw_usb_init(void)
     /* Activate FS transceiver. */
     /* Activate FS transceiver. */
     otg->gccfg = OTG_GCCFG_PWRDWN | OTG_GCCFG_VBUSBSEN;
     otg->gccfg = OTG_GCCFG_PWRDWN | OTG_GCCFG_VBUSBSEN;
 
 
-    dwc_otg_init();
+    dwc_otg.init();
 }
 }
 
 
 void hw_usb_deinit(void)
 void hw_usb_deinit(void)
 {
 {
-    dwc_otg_deinit();
+    dwc_otg.deinit();
 
 
     rcc->ahbenr &= ~RCC_AHBENR_OTGFSEN;
     rcc->ahbenr &= ~RCC_AHBENR_OTGFSEN;
 }
 }
 
 
+bool_t hw_has_highspeed(void)
+{
+    return dwc_otg.has_highspeed();
+}
+
+bool_t usb_is_highspeed(void)
+{
+    return dwc_otg.is_highspeed();
+}
+
+int ep_rx_ready(uint8_t epnr)
+{
+    return dwc_otg.ep_rx_ready(epnr);
+}
+
+bool_t ep_tx_ready(uint8_t epnr)
+{
+    return dwc_otg.ep_tx_ready(epnr);
+}
+ 
+void usb_read(uint8_t epnr, void *buf, uint32_t len)
+{
+    dwc_otg.read(epnr, buf, len);
+}
+
+void usb_write(uint8_t epnr, const void *buf, uint32_t len)
+{
+    dwc_otg.write(epnr, buf, len);
+}
+ 
+void usb_stall(uint8_t epnr)
+{
+    dwc_otg.stall(epnr);
+}
+
+void usb_configure_ep(uint8_t epnr, uint8_t type, uint32_t size)
+{
+    dwc_otg.configure_ep(epnr, type, size);
+}
+
+void usb_setaddr(uint8_t addr)
+{
+    dwc_otg.setaddr(addr);
+}
+
+void usb_process(void)
+{
+    dwc_otg.process();
+}
+
 /*
 /*
  * Local variables:
  * Local variables:
  * mode: C
  * mode: C

+ 30 - 12
src/usb/hw_dwc_otg.c

@@ -27,12 +27,12 @@ static struct ep {
     bool_t rx_active, tx_ready;
     bool_t rx_active, tx_ready;
 } eps[conf_nr_ep];
 } eps[conf_nr_ep];
 
 
-bool_t hw_has_highspeed(void)
+static bool_t dwc_otg_has_highspeed(void)
 {
 {
     return conf_iface == IFACE_HS_EMBEDDED;
     return conf_iface == IFACE_HS_EMBEDDED;
 }
 }
 
 
-bool_t usb_is_highspeed(void)
+static bool_t dwc_otg_is_highspeed(void)
 {
 {
     return is_hs;
     return is_hs;
 }
 }
@@ -129,7 +129,7 @@ static void fifos_init(void)
     }
     }
 }
 }
 
 
-void dwc_otg_init(void)
+static void dwc_otg_init(void)
 {
 {
     int i;
     int i;
 
 
@@ -199,32 +199,32 @@ void dwc_otg_init(void)
     delay_ms(3);
     delay_ms(3);
 }
 }
 
 
-void dwc_otg_deinit(void)
+static void dwc_otg_deinit(void)
 {
 {
     /* HAL_PCD_Stop, USB_DevDisconnect */
     /* HAL_PCD_Stop, USB_DevDisconnect */
     otgd->dctl |= OTG_DCTL_SDIS;
     otgd->dctl |= OTG_DCTL_SDIS;
     peripheral_clock_delay();
     peripheral_clock_delay();
 }
 }
 
 
-int ep_rx_ready(uint8_t epnr)
+static int dwc_otg_ep_rx_ready(uint8_t epnr)
 {
 {
     struct ep *ep = &eps[epnr];
     struct ep *ep = &eps[epnr];
     return (ep->rxc != ep->rxp) ? ep->rx[RX_MASK(ep, rxc)].count : -1;
     return (ep->rxc != ep->rxp) ? ep->rx[RX_MASK(ep, rxc)].count : -1;
 }
 }
 
 
-bool_t ep_tx_ready(uint8_t epnr)
+static bool_t dwc_otg_ep_tx_ready(uint8_t epnr)
 {
 {
     return eps[epnr].tx_ready;
     return eps[epnr].tx_ready;
 }
 }
 
 
-void usb_read(uint8_t epnr, void *buf, uint32_t len)
+static void dwc_otg_read(uint8_t epnr, void *buf, uint32_t len)
 {
 {
     struct ep *ep = &eps[epnr];
     struct ep *ep = &eps[epnr];
     memcpy(buf, ep->rx[RX_MASK(ep, rxc++)].data, len);
     memcpy(buf, ep->rx[RX_MASK(ep, rxc++)].data, len);
     prepare_rx(epnr);
     prepare_rx(epnr);
 }
 }
 
 
-void usb_write(uint8_t epnr, const void *buf, uint32_t len)
+static void dwc_otg_write(uint8_t epnr, const void *buf, uint32_t len)
 {
 {
     OTG_DIEP diep = &otg_diep[epnr];
     OTG_DIEP diep = &otg_diep[epnr];
 
 
@@ -238,13 +238,13 @@ void usb_write(uint8_t epnr, const void *buf, uint32_t len)
     eps[epnr].tx_ready = FALSE;
     eps[epnr].tx_ready = FALSE;
 }
 }
 
 
-void usb_stall(uint8_t epnr)
+static void dwc_otg_stall(uint8_t epnr)
 {
 {
     otg_diep[epnr].ctl |= OTG_DIEPCTL_STALL;
     otg_diep[epnr].ctl |= OTG_DIEPCTL_STALL;
     otg_doep[epnr].ctl |= OTG_DOEPCTL_STALL;
     otg_doep[epnr].ctl |= OTG_DOEPCTL_STALL;
 }
 }
 
 
-void usb_configure_ep(uint8_t epnr, uint8_t type, uint32_t size)
+static void dwc_otg_configure_ep(uint8_t epnr, uint8_t type, uint32_t size)
 {
 {
     int i;
     int i;
     struct ep *ep;
     struct ep *ep;
@@ -297,7 +297,7 @@ void usb_configure_ep(uint8_t epnr, uint8_t type, uint32_t size)
     }
     }
 }
 }
 
 
-void usb_setaddr(uint8_t addr)
+static void dwc_otg_setaddr(uint8_t addr)
 {
 {
     otgd->dcfg = (otgd->dcfg & ~OTG_DCFG_DAD(0x7f)) | OTG_DCFG_DAD(addr);
     otgd->dcfg = (otgd->dcfg & ~OTG_DCFG_DAD(0x7f)) | OTG_DCFG_DAD(addr);
 }
 }
@@ -415,7 +415,7 @@ static void handle_iepint(uint8_t epnr)
     }
     }
 }
 }
 
 
-void usb_process(void)
+static void dwc_otg_process(void)
 {
 {
     uint32_t gintsts = otg->gintsts & otg->gintmsk;
     uint32_t gintsts = otg->gintsts & otg->gintmsk;
 
 
@@ -462,6 +462,24 @@ void usb_process(void)
     }
     }
 }
 }
 
 
+const struct usb_driver dwc_otg = {
+    .init = dwc_otg_init,
+    .deinit = dwc_otg_deinit,
+    .process = dwc_otg_process,
+
+    .has_highspeed = dwc_otg_has_highspeed,
+    .is_highspeed = dwc_otg_is_highspeed,
+
+    .setaddr = dwc_otg_setaddr,
+
+    .configure_ep = dwc_otg_configure_ep,
+    .ep_rx_ready = dwc_otg_ep_rx_ready,
+    .ep_tx_ready = dwc_otg_ep_tx_ready,
+    .read = dwc_otg_read,
+    .write = dwc_otg_write,
+    .stall = dwc_otg_stall
+};
+
 /*
 /*
  * Local variables:
  * Local variables:
  * mode: C
  * mode: C

+ 0 - 2
src/usb/hw_dwc_otg.h

@@ -379,8 +379,6 @@ static OTG_DFIFO otg_dfifo = (struct otg_dfifo *)(OTG_BASE + 0x1000);
 /* DWC OTG private interface to MCU-specific layer. */
 /* DWC OTG private interface to MCU-specific layer. */
 extern int conf_iface;
 extern int conf_iface;
 void core_reset(void);
 void core_reset(void);
-void dwc_otg_init(void);
-void dwc_otg_deinit(void);
 
 
 /*
 /*
  * Local variables:
  * Local variables:

+ 24 - 324
src/usb/hw_f1.c

@@ -1,7 +1,7 @@
 /*
 /*
  * hw_f1.c
  * hw_f1.c
  * 
  * 
- * USB handling for STM32F10x devices (except 105/107).
+ * STM32F103 USBD.
  * 
  * 
  * Written & released by Keir Fraser <keir.xen@gmail.com>
  * Written & released by Keir Fraser <keir.xen@gmail.com>
  * 
  * 
@@ -9,364 +9,64 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
  */
 
 
-#include "hw_f1.h"
-
-static uint16_t buf_end;
-static uint8_t pending_addr;
-
-static struct {
-    /* We track which endpoints have been marked CTR (Correct TRansfer). On 
-     * receive side, checking EPR_STAT_RX == NAK races update of the Buffer 
-     * Descriptor's COUNT_RX by the hardware. */
-    bool_t rx_ready;
-    bool_t tx_ready;
-} eps[8];
-
-bool_t hw_has_highspeed(void)
-{
-    return FALSE;
-}
-
-bool_t usb_is_highspeed(void)
-{
-    return FALSE;
-}
-
 void hw_usb_init(void)
 void hw_usb_init(void)
 {
 {
-    /* Turn on clock. */
-    rcc->apb1enr |= RCC_APB1ENR_USBEN;
-
-    /* Exit power-down state. */
-    usb->cntr &= ~USB_CNTR_PDWN;
-    delay_us(10);
-
-    /* Exit reset state. */
-    usb->cntr &= ~USB_CNTR_FRES;
-    delay_us(10);
-
-    /* Clear IRQ state. */
-    usb->istr = 0;
-
-    /* Indicate we are connected by pulling up D+. */
-    gpio_configure_pin(gpioa, 0, GPO_pushpull(_2MHz, HIGH));
+    usbd.init();
 }
 }
 
 
 void hw_usb_deinit(void)
 void hw_usb_deinit(void)
 {
 {
-    gpio_configure_pin(gpioa, 0, GPI_floating);
-    rcc->apb1enr &= ~RCC_APB1ENR_USBEN;
+    usbd.deinit();
 }
 }
 
 
-#if 0
-static void dump_ep(uint8_t ep)
-{
-    const static char *names[] = { "DISA", "STAL", "NAK ", "VALI" };
-    uint16_t epr;
-    ep &= 0x7f;
-    epr = usb->epr[ep];
-    printk("[EP%u: Rx:%c%c(%s)%04x:%02u Tx:%c%c(%s)%04x:%02u %c]",
-           ep,
-           (epr & USB_EPR_CTR_RX) ? 'C' : ' ',
-           (epr & USB_EPR_DTOG_RX) ? 'D' : ' ',
-           names[(epr>>12)&3],
-           usb_bufd[ep].addr_rx, usb_bufd[ep].count_rx & 0x3ff,
-           (epr & USB_EPR_CTR_TX) ? 'C' : ' ',
-           (epr & USB_EPR_DTOG_TX) ? 'D' : ' ',
-           names[(epr>>4)&3],
-           usb_bufd[ep].addr_tx, usb_bufd[ep].count_tx & 0x3ff,
-           (epr & USB_EPR_SETUP) ? 'S' : ' ');
-}
-#endif
-
-int ep_rx_ready(uint8_t ep)
-{
-    uint16_t count, epr;
-    volatile struct usb_bufd *bd;
-
-    if (!eps[ep].rx_ready)
-        return -1;
-
-    bd = &usb_bufd[ep];
-    epr = usb->epr[ep];
-
-    count = !(epr & USB_EPR_EP_KIND_DBL_BUF) ? bd->count_rx
-        : (epr & 0x0040) ? bd->count_1 : bd->count_0;
-
-    return count & 0x3ff;
-}
-
-bool_t ep_tx_ready(uint8_t ep)
-{
-    return eps[ep].tx_ready;
-}
-
-void usb_read(uint8_t ep, void *buf, uint32_t len)
+bool_t hw_has_highspeed(void)
 {
 {
-    unsigned int i, base;
-    uint16_t epr = usb->epr[ep], *p = buf;
-    volatile struct usb_bufd *bd = &usb_bufd[ep];
-
-    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
-        base = (epr & 0x0040) ? bd->addr_1 : bd->addr_0;
-        /* If HW is pointing at same buffer as us, we have to process both 
-         * buffers, and should defer clearing rx_ready until we've done so. */
-        if ((epr ^ (epr>>8)) & 0x40)
-            eps[ep].rx_ready = FALSE;
-    } else {
-        base = bd->addr_rx;
-        eps[ep].rx_ready = FALSE;
-    }
-    base = (uint16_t)base >> 1;
-
-    for (i = 0; i < len/2; i++)
-        *p++ = usb_buf[base + i];
-    if (len&1)
-        *(uint8_t *)p = usb_buf[base + i];
-
-    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
-        /* Toggle SW_BUF. Status remains VALID at all times. */
-        epr &= 0x070f; /* preserve rw & t fields */
-        epr |= 0x80c0; /* preserve rc_w0 fields, toggle SW_BUF */
-        if (eps[ep].rx_ready) {
-            /* Clear CTR_RX if we have already spotted the next packet. */
-            epr &= ~USB_EPR_CTR_RX;
-        }
-    } else {
-        /* Set status NAK->VALID. */
-        epr &= 0x370f; /* preserve rw & t fields (except STAT_RX) */
-        epr |= 0x8080; /* preserve rc_w0 fields */
-        epr ^= USB_EPR_STAT_RX(USB_STAT_VALID); /* modify STAT_RX */
-    }
-    usb->epr[ep] = epr;
+    return usbd.has_highspeed();
 }
 }
 
 
-void usb_write(uint8_t ep, const void *buf, uint32_t len)
+bool_t usb_is_highspeed(void)
 {
 {
-    unsigned int i, base;
-    uint16_t epr = usb->epr[ep];
-    const uint16_t *p = buf;
-    volatile struct usb_bufd *bd = &usb_bufd[ep];
-
-    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
-        if (epr & 0x4000) {
-            base = bd->addr_1;
-            bd->count_1 = len;
-        } else {
-            base = bd->addr_0;
-            bd->count_0 = len;
-        }
-        /* If HW is pointing at same buffer as us, we have space for two
-         * packets, and do not need to clear tx_ready. */
-        if ((epr ^ (epr>>8)) & 0x40)
-            eps[ep].tx_ready = FALSE;
-    } else {
-        base = bd->addr_tx;
-        bd->count_tx = len;
-        eps[ep].tx_ready = FALSE;
-    }
-    base = (uint16_t)base >> 1;
-
-    for (i = 0; i < len/2; i++)
-        usb_buf[base + i] = *p++;
-    if (len&1)
-        usb_buf[base + i] = *(const uint8_t *)p;
-
-    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
-        /* Toggle SW_BUF. Status remains VALID at all times. */
-        epr &= 0x070f; /* preserve rw & t fields */
-        epr |= 0xc080; /* preserve rc_w0 fields, toggle SW_BUF */
-        if (eps[ep].tx_ready) {
-            /* Clear CTR_TX if we have already spotted the next empty space. */
-            epr &= ~USB_EPR_CTR_TX;
-        }
-    } else {
-        /* Set status NAK->VALID. */
-        epr &= 0x073f; /* preserve rw & t fields (except STAT_TX) */
-        epr |= 0x8080; /* preserve rc_w0 fields */
-        epr ^= USB_EPR_STAT_TX(USB_STAT_VALID); /* modify STAT_TX */
-    }
-    usb->epr[ep] = epr;
+    return usbd.is_highspeed();
 }
 }
 
 
-void usb_stall(uint8_t ep)
+int ep_rx_ready(uint8_t epnr)
 {
 {
-    uint16_t epr = usb->epr[ep];
-    epr &= 0x073f;
-    epr |= 0x8080;
-    epr ^= USB_EPR_STAT_TX(USB_STAT_STALL);
-    usb->epr[ep] = epr;
+    return usbd.ep_rx_ready(epnr);
 }
 }
 
 
-void usb_configure_ep(uint8_t ep, uint8_t type, uint32_t size)
+bool_t ep_tx_ready(uint8_t epnr)
 {
 {
-    static const uint8_t types[] = {
-        [EPT_CONTROL] = USB_EP_TYPE_CONTROL,
-        [EPT_ISO] = USB_EP_TYPE_ISO,
-        [EPT_BULK] = USB_EP_TYPE_BULK,
-        [EPT_INTERRUPT] = USB_EP_TYPE_INTERRUPT
-    };
-
-    uint16_t old_epr, new_epr;
-    bool_t in, dbl_buf;
-    volatile struct usb_bufd *bd;
-
-    in = !!(ep & 0x80);
-    ep &= 0x7f;
-    bd = &usb_bufd[ep];
-
-    old_epr = usb->epr[ep];
-    new_epr = 0;
-
-    dbl_buf = (type == EPT_DBLBUF);
-    if (dbl_buf) {
-        ASSERT(ep != 0);
-        type = EPT_BULK;
-        new_epr |= USB_EPR_EP_KIND_DBL_BUF;
-        bd->addr_0 = buf_end;
-        bd->addr_1 = buf_end + size;
-        buf_end += 2*size;
-    }
-
-    type = types[type];
-
-    /* Sets: Type and Endpoint Address.
-     * Clears: CTR_RX and CTR_TX. */
-    new_epr |= USB_EPR_EP_TYPE(type) | USB_EPR_EA(ep);
-
-    if (in || (ep == 0)) {
-        if (dbl_buf) {
-            bd->count_0 = bd->count_1 = 0;
-            /* TX: Sets SW_BUF. */
-            new_epr |= (old_epr & 0x4000) ^ 0x4000;
-            /* TX: Clears data toggle and sets status to VALID. */
-            new_epr |= (old_epr & 0x0070) ^ USB_EPR_STAT_TX(USB_STAT_VALID);
-        } else {
-            bd->addr_tx = buf_end;
-            buf_end += size;
-            bd->count_tx = 0;
-            /* TX: Clears data toggle and sets status to NAK. */
-            new_epr |= (old_epr & 0x0070) ^ USB_EPR_STAT_TX(USB_STAT_NAK);
-        }
-        /* IN Endpoint is immediately ready to transmit. */
-        eps[ep].tx_ready = TRUE;
-    }
-
-    if (!in) {
-        if (dbl_buf) {
-            bd->count_0 = bd->count_1 = 0x8400; /* USB_FS_MPS = 64 bytes */
-            /* RX: Clears SW_BUF. */
-            new_epr |= old_epr & 0x0040;
-        } else {
-            bd->addr_rx = buf_end;
-            buf_end += size;
-            bd->count_rx = 0x8400; /* USB_FS_MPS = 64 bytes */
-        }
-        /* RX: Clears data toggle and sets status to VALID. */
-        new_epr |= (old_epr & 0x7000) ^ USB_EPR_STAT_RX(USB_STAT_VALID);
-        /* OUT Endpoint must wait for a packet from the Host. */
-        eps[ep].rx_ready = FALSE;
-    }
-
-    usb->epr[ep] = new_epr;
+    return usbd.ep_tx_ready(epnr);
 }
 }
-
-void usb_setaddr(uint8_t addr)
+ 
+void usb_read(uint8_t epnr, void *buf, uint32_t len)
 {
 {
-    pending_addr = addr;
+    usbd.read(epnr, buf, len);
 }
 }
 
 
-static void handle_reset(void)
+void usb_write(uint8_t epnr, const void *buf, uint32_t len)
 {
 {
-    /* Reinitialise class-specific subsystem. */
-    usb_cdc_acm_ops.reset();
-
-    /* Clear endpoint soft state. */
-    memset(eps, 0, sizeof(eps));
-
-    /* Clear any in-progress Control Transfer. */
-    ep0.data_len = -1;
-    ep0.tx.todo = -1;
-
-    /* Prepare for Enumeration: Set up Endpoint 0 at Address 0. */
-    pending_addr = 0;
-    buf_end = 64;
-    usb_configure_ep(0, EPT_CONTROL, EP0_MPS);
-    usb->daddr = USB_DADDR_EF | USB_DADDR_ADD(0);
-    usb->istr &= ~USB_ISTR_RESET;
+    usbd.write(epnr, buf, len);
 }
 }
-
-static void clear_ctr(uint8_t ep, uint16_t ctr)
+ 
+void usb_stall(uint8_t epnr)
 {
 {
-    uint16_t epr = usb->epr[ep];
-    epr &= 0x070f; /* preserve rw & t fields */
-    epr |= 0x8080; /* preserve rc_w0 fields */
-    epr &= ~ctr;   /* clear specified rc_w0 field */
-    usb->epr[ep] = epr;
+    usbd.stall(epnr);
 }
 }
 
 
-static void handle_rx_transfer(uint8_t ep)
+void usb_configure_ep(uint8_t epnr, uint8_t type, uint32_t size)
 {
 {
-    uint16_t epr = usb->epr[ep];
-
-    clear_ctr(ep, USB_EPR_CTR_RX);
-    eps[ep].rx_ready = TRUE;
-
-    /* We only handle Control Transfers here (endpoint 0). */
-    if (ep == 0)
-        handle_rx_ep0(!!(epr & USB_EPR_SETUP));
+    usbd.configure_ep(epnr, type, size);
 }
 }
 
 
-static void handle_tx_transfer(uint8_t ep)
+void usb_setaddr(uint8_t addr)
 {
 {
-    clear_ctr(ep, USB_EPR_CTR_TX);
-    eps[ep].tx_ready = TRUE;
-
-    /* We only handle Control Transfers here (endpoint 0). */
-    if (ep != 0)
-        return;
-
-    handle_tx_ep0();
-
-    if (pending_addr && (ep0.tx.todo == -1)) {
-        /* We have just completed the Status stage of a SET_ADDRESS request. 
-         * Now is the time to apply the address update. */
-        usb->daddr = USB_DADDR_EF | USB_DADDR_ADD(pending_addr);
-        pending_addr = 0;
-    }
+    usbd.setaddr(addr);
 }
 }
 
 
 void usb_process(void)
 void usb_process(void)
 {
 {
-    uint16_t istr = usb->istr;
-    usb->istr = ~istr & 0x7f00;
-
-    if (istr & USB_ISTR_CTR) {
-        uint8_t ep = USB_ISTR_GET_EP_ID(istr);
-        //dump_ep(ep);
-        if (istr & USB_ISTR_DIR)
-            handle_rx_transfer(ep);
-        else
-            handle_tx_transfer(ep);
-        //printk(" -> "); dump_ep(ep); printk("\n");
-    }
-
-    if (istr & USB_ISTR_PMAOVR) {
-        printk("[PMAOVR]\n");
-    }
-
-    if (istr & USB_ISTR_ERR) {
-        printk("[ERR]\n");
-    }
-
-    if (istr & USB_ISTR_WKUP) {
-        printk("[WKUP]\n");
-    }
-
-    if (istr & USB_ISTR_RESET) {
-        printk("[RESET]\n");
-        handle_reset();
-    }
+    usbd.process();
 }
 }
 
 
 /*
 /*

+ 52 - 2
src/usb/hw_f7.c

@@ -89,12 +89,12 @@ void hw_usb_init(void)
         core_reset();
         core_reset();
     }
     }
 
 
-    dwc_otg_init();
+    dwc_otg.init();
 }
 }
 
 
 void hw_usb_deinit(void)
 void hw_usb_deinit(void)
 {
 {
-    dwc_otg_deinit();
+    dwc_otg.deinit();
 
 
     /* HAL_PCD_MspDeInit */
     /* HAL_PCD_MspDeInit */
     switch (conf_port) {
     switch (conf_port) {
@@ -113,6 +113,56 @@ void hw_usb_deinit(void)
     }
     }
 }
 }
 
 
+bool_t hw_has_highspeed(void)
+{
+    return dwc_otg.has_highspeed();
+}
+
+bool_t usb_is_highspeed(void)
+{
+    return dwc_otg.is_highspeed();
+}
+
+int ep_rx_ready(uint8_t epnr)
+{
+    return dwc_otg.ep_rx_ready(epnr);
+}
+
+bool_t ep_tx_ready(uint8_t epnr)
+{
+    return dwc_otg.ep_tx_ready(epnr);
+}
+ 
+void usb_read(uint8_t epnr, void *buf, uint32_t len)
+{
+    dwc_otg.read(epnr, buf, len);
+}
+
+void usb_write(uint8_t epnr, const void *buf, uint32_t len)
+{
+    dwc_otg.write(epnr, buf, len);
+}
+ 
+void usb_stall(uint8_t epnr)
+{
+    dwc_otg.stall(epnr);
+}
+
+void usb_configure_ep(uint8_t epnr, uint8_t type, uint32_t size)
+{
+    dwc_otg.configure_ep(epnr, type, size);
+}
+
+void usb_setaddr(uint8_t addr)
+{
+    dwc_otg.setaddr(addr);
+}
+
+void usb_process(void)
+{
+    dwc_otg.process();
+}
+
 /*
 /*
  * Local variables:
  * Local variables:
  * mode: C
  * mode: C

+ 398 - 0
src/usb/hw_usbd.c

@@ -0,0 +1,398 @@
+/*
+ * hw_usbd.c
+ * 
+ * USB handling for STM32F10x USBD peripheral.
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+#include "hw_usbd.h"
+
+static uint16_t buf_end;
+static uint8_t pending_addr;
+
+static struct {
+    /* We track which endpoints have been marked CTR (Correct TRansfer). On 
+     * receive side, checking EPR_STAT_RX == NAK races update of the Buffer 
+     * Descriptor's COUNT_RX by the hardware. */
+    bool_t rx_ready;
+    bool_t tx_ready;
+} eps[8];
+
+static bool_t usbd_has_highspeed(void)
+{
+    return FALSE;
+}
+
+static bool_t usbd_is_highspeed(void)
+{
+    return FALSE;
+}
+
+static void usbd_init(void)
+{
+    /* Turn on clock. */
+    rcc->apb1enr |= RCC_APB1ENR_USBEN;
+
+    /* Exit power-down state. */
+    usb->cntr &= ~USB_CNTR_PDWN;
+    delay_us(10);
+
+    /* Exit reset state. */
+    usb->cntr &= ~USB_CNTR_FRES;
+    delay_us(10);
+
+    /* Clear IRQ state. */
+    usb->istr = 0;
+
+    /* Indicate we are connected by pulling up D+. */
+    gpio_configure_pin(gpioa, 0, GPO_pushpull(_2MHz, HIGH));
+}
+
+static void usbd_deinit(void)
+{
+    gpio_configure_pin(gpioa, 0, GPI_floating);
+    rcc->apb1enr &= ~RCC_APB1ENR_USBEN;
+}
+
+#if 0
+static void dump_ep(uint8_t ep)
+{
+    const static char *names[] = { "DISA", "STAL", "NAK ", "VALI" };
+    uint16_t epr;
+    ep &= 0x7f;
+    epr = usb->epr[ep];
+    printk("[EP%u: Rx:%c%c(%s)%04x:%02u Tx:%c%c(%s)%04x:%02u %c]",
+           ep,
+           (epr & USB_EPR_CTR_RX) ? 'C' : ' ',
+           (epr & USB_EPR_DTOG_RX) ? 'D' : ' ',
+           names[(epr>>12)&3],
+           usb_bufd[ep].addr_rx, usb_bufd[ep].count_rx & 0x3ff,
+           (epr & USB_EPR_CTR_TX) ? 'C' : ' ',
+           (epr & USB_EPR_DTOG_TX) ? 'D' : ' ',
+           names[(epr>>4)&3],
+           usb_bufd[ep].addr_tx, usb_bufd[ep].count_tx & 0x3ff,
+           (epr & USB_EPR_SETUP) ? 'S' : ' ');
+}
+#endif
+
+static int usbd_ep_rx_ready(uint8_t ep)
+{
+    uint16_t count, epr;
+    volatile struct usb_bufd *bd;
+
+    if (!eps[ep].rx_ready)
+        return -1;
+
+    bd = &usb_bufd[ep];
+    epr = usb->epr[ep];
+
+    count = !(epr & USB_EPR_EP_KIND_DBL_BUF) ? bd->count_rx
+        : (epr & 0x0040) ? bd->count_1 : bd->count_0;
+
+    return count & 0x3ff;
+}
+
+static bool_t usbd_ep_tx_ready(uint8_t ep)
+{
+    return eps[ep].tx_ready;
+}
+
+static void usbd_read(uint8_t ep, void *buf, uint32_t len)
+{
+    unsigned int i, base;
+    uint16_t epr = usb->epr[ep], *p = buf;
+    volatile struct usb_bufd *bd = &usb_bufd[ep];
+
+    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
+        base = (epr & 0x0040) ? bd->addr_1 : bd->addr_0;
+        /* If HW is pointing at same buffer as us, we have to process both 
+         * buffers, and should defer clearing rx_ready until we've done so. */
+        if ((epr ^ (epr>>8)) & 0x40)
+            eps[ep].rx_ready = FALSE;
+    } else {
+        base = bd->addr_rx;
+        eps[ep].rx_ready = FALSE;
+    }
+    base = (uint16_t)base >> 1;
+
+    for (i = 0; i < len/2; i++)
+        *p++ = usb_buf[base + i];
+    if (len&1)
+        *(uint8_t *)p = usb_buf[base + i];
+
+    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
+        /* Toggle SW_BUF. Status remains VALID at all times. */
+        epr &= 0x070f; /* preserve rw & t fields */
+        epr |= 0x80c0; /* preserve rc_w0 fields, toggle SW_BUF */
+        if (eps[ep].rx_ready) {
+            /* Clear CTR_RX if we have already spotted the next packet. */
+            epr &= ~USB_EPR_CTR_RX;
+        }
+    } else {
+        /* Set status NAK->VALID. */
+        epr &= 0x370f; /* preserve rw & t fields (except STAT_RX) */
+        epr |= 0x8080; /* preserve rc_w0 fields */
+        epr ^= USB_EPR_STAT_RX(USB_STAT_VALID); /* modify STAT_RX */
+    }
+    usb->epr[ep] = epr;
+}
+
+static void usbd_write(uint8_t ep, const void *buf, uint32_t len)
+{
+    unsigned int i, base;
+    uint16_t epr = usb->epr[ep];
+    const uint16_t *p = buf;
+    volatile struct usb_bufd *bd = &usb_bufd[ep];
+
+    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
+        if (epr & 0x4000) {
+            base = bd->addr_1;
+            bd->count_1 = len;
+        } else {
+            base = bd->addr_0;
+            bd->count_0 = len;
+        }
+        /* If HW is pointing at same buffer as us, we have space for two
+         * packets, and do not need to clear tx_ready. */
+        if ((epr ^ (epr>>8)) & 0x40)
+            eps[ep].tx_ready = FALSE;
+    } else {
+        base = bd->addr_tx;
+        bd->count_tx = len;
+        eps[ep].tx_ready = FALSE;
+    }
+    base = (uint16_t)base >> 1;
+
+    for (i = 0; i < len/2; i++)
+        usb_buf[base + i] = *p++;
+    if (len&1)
+        usb_buf[base + i] = *(const uint8_t *)p;
+
+    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
+        /* Toggle SW_BUF. Status remains VALID at all times. */
+        epr &= 0x070f; /* preserve rw & t fields */
+        epr |= 0xc080; /* preserve rc_w0 fields, toggle SW_BUF */
+        if (eps[ep].tx_ready) {
+            /* Clear CTR_TX if we have already spotted the next empty space. */
+            epr &= ~USB_EPR_CTR_TX;
+        }
+    } else {
+        /* Set status NAK->VALID. */
+        epr &= 0x073f; /* preserve rw & t fields (except STAT_TX) */
+        epr |= 0x8080; /* preserve rc_w0 fields */
+        epr ^= USB_EPR_STAT_TX(USB_STAT_VALID); /* modify STAT_TX */
+    }
+    usb->epr[ep] = epr;
+}
+
+static void usbd_stall(uint8_t ep)
+{
+    uint16_t epr = usb->epr[ep];
+    epr &= 0x073f;
+    epr |= 0x8080;
+    epr ^= USB_EPR_STAT_TX(USB_STAT_STALL);
+    usb->epr[ep] = epr;
+}
+
+static void usbd_configure_ep(uint8_t ep, uint8_t type, uint32_t size)
+{
+    static const uint8_t types[] = {
+        [EPT_CONTROL] = USB_EP_TYPE_CONTROL,
+        [EPT_ISO] = USB_EP_TYPE_ISO,
+        [EPT_BULK] = USB_EP_TYPE_BULK,
+        [EPT_INTERRUPT] = USB_EP_TYPE_INTERRUPT
+    };
+
+    uint16_t old_epr, new_epr;
+    bool_t in, dbl_buf;
+    volatile struct usb_bufd *bd;
+
+    in = !!(ep & 0x80);
+    ep &= 0x7f;
+    bd = &usb_bufd[ep];
+
+    old_epr = usb->epr[ep];
+    new_epr = 0;
+
+    dbl_buf = (type == EPT_DBLBUF);
+    if (dbl_buf) {
+        ASSERT(ep != 0);
+        type = EPT_BULK;
+        new_epr |= USB_EPR_EP_KIND_DBL_BUF;
+        bd->addr_0 = buf_end;
+        bd->addr_1 = buf_end + size;
+        buf_end += 2*size;
+    }
+
+    type = types[type];
+
+    /* Sets: Type and Endpoint Address.
+     * Clears: CTR_RX and CTR_TX. */
+    new_epr |= USB_EPR_EP_TYPE(type) | USB_EPR_EA(ep);
+
+    if (in || (ep == 0)) {
+        if (dbl_buf) {
+            bd->count_0 = bd->count_1 = 0;
+            /* TX: Sets SW_BUF. */
+            new_epr |= (old_epr & 0x4000) ^ 0x4000;
+            /* TX: Clears data toggle and sets status to VALID. */
+            new_epr |= (old_epr & 0x0070) ^ USB_EPR_STAT_TX(USB_STAT_VALID);
+        } else {
+            bd->addr_tx = buf_end;
+            buf_end += size;
+            bd->count_tx = 0;
+            /* TX: Clears data toggle and sets status to NAK. */
+            new_epr |= (old_epr & 0x0070) ^ USB_EPR_STAT_TX(USB_STAT_NAK);
+        }
+        /* IN Endpoint is immediately ready to transmit. */
+        eps[ep].tx_ready = TRUE;
+    }
+
+    if (!in) {
+        if (dbl_buf) {
+            bd->count_0 = bd->count_1 = 0x8400; /* USB_FS_MPS = 64 bytes */
+            /* RX: Clears SW_BUF. */
+            new_epr |= old_epr & 0x0040;
+        } else {
+            bd->addr_rx = buf_end;
+            buf_end += size;
+            bd->count_rx = 0x8400; /* USB_FS_MPS = 64 bytes */
+        }
+        /* RX: Clears data toggle and sets status to VALID. */
+        new_epr |= (old_epr & 0x7000) ^ USB_EPR_STAT_RX(USB_STAT_VALID);
+        /* OUT Endpoint must wait for a packet from the Host. */
+        eps[ep].rx_ready = FALSE;
+    }
+
+    usb->epr[ep] = new_epr;
+}
+
+static void usbd_setaddr(uint8_t addr)
+{
+    pending_addr = addr;
+}
+
+static void handle_reset(void)
+{
+    /* Reinitialise class-specific subsystem. */
+    usb_cdc_acm_ops.reset();
+
+    /* Clear endpoint soft state. */
+    memset(eps, 0, sizeof(eps));
+
+    /* Clear any in-progress Control Transfer. */
+    ep0.data_len = -1;
+    ep0.tx.todo = -1;
+
+    /* Prepare for Enumeration: Set up Endpoint 0 at Address 0. */
+    pending_addr = 0;
+    buf_end = 64;
+    usb_configure_ep(0, EPT_CONTROL, EP0_MPS);
+    usb->daddr = USB_DADDR_EF | USB_DADDR_ADD(0);
+    usb->istr &= ~USB_ISTR_RESET;
+}
+
+static void clear_ctr(uint8_t ep, uint16_t ctr)
+{
+    uint16_t epr = usb->epr[ep];
+    epr &= 0x070f; /* preserve rw & t fields */
+    epr |= 0x8080; /* preserve rc_w0 fields */
+    epr &= ~ctr;   /* clear specified rc_w0 field */
+    usb->epr[ep] = epr;
+}
+
+static void handle_rx_transfer(uint8_t ep)
+{
+    uint16_t epr = usb->epr[ep];
+
+    clear_ctr(ep, USB_EPR_CTR_RX);
+    eps[ep].rx_ready = TRUE;
+
+    /* We only handle Control Transfers here (endpoint 0). */
+    if (ep == 0)
+        handle_rx_ep0(!!(epr & USB_EPR_SETUP));
+}
+
+static void handle_tx_transfer(uint8_t ep)
+{
+    clear_ctr(ep, USB_EPR_CTR_TX);
+    eps[ep].tx_ready = TRUE;
+
+    /* We only handle Control Transfers here (endpoint 0). */
+    if (ep != 0)
+        return;
+
+    handle_tx_ep0();
+
+    if (pending_addr && (ep0.tx.todo == -1)) {
+        /* We have just completed the Status stage of a SET_ADDRESS request. 
+         * Now is the time to apply the address update. */
+        usb->daddr = USB_DADDR_EF | USB_DADDR_ADD(pending_addr);
+        pending_addr = 0;
+    }
+}
+
+static void usbd_process(void)
+{
+    uint16_t istr = usb->istr;
+    usb->istr = ~istr & 0x7f00;
+
+    if (istr & USB_ISTR_CTR) {
+        uint8_t ep = USB_ISTR_GET_EP_ID(istr);
+        //dump_ep(ep);
+        if (istr & USB_ISTR_DIR)
+            handle_rx_transfer(ep);
+        else
+            handle_tx_transfer(ep);
+        //printk(" -> "); dump_ep(ep); printk("\n");
+    }
+
+    if (istr & USB_ISTR_PMAOVR) {
+        printk("[PMAOVR]\n");
+    }
+
+    if (istr & USB_ISTR_ERR) {
+        printk("[ERR]\n");
+    }
+
+    if (istr & USB_ISTR_WKUP) {
+        printk("[WKUP]\n");
+    }
+
+    if (istr & USB_ISTR_RESET) {
+        printk("[RESET]\n");
+        handle_reset();
+    }
+}
+
+const struct usb_driver usbd = {
+    .init = usbd_init,
+    .deinit = usbd_deinit,
+    .process = usbd_process,
+
+    .has_highspeed = usbd_has_highspeed,
+    .is_highspeed = usbd_is_highspeed,
+
+    .setaddr = usbd_setaddr,
+
+    .configure_ep = usbd_configure_ep,
+    .ep_rx_ready = usbd_ep_rx_ready,
+    .ep_tx_ready = usbd_ep_tx_ready,
+    .read = usbd_read,
+    .write = usbd_write,
+    .stall = usbd_stall
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 2 - 2
src/usb/hw_f1.h → src/usb/hw_usbd.h

@@ -1,7 +1,7 @@
 /*
 /*
- * hw_f1.h
+ * hw_usbd.h
  * 
  * 
- * USB register definitions for STM32F10x devices (except 105/107).
+ * USB register definitions for STM32F10x USBD peripheral.
  * 
  * 
  * Written & released by Keir Fraser <keir.xen@gmail.com>
  * Written & released by Keir Fraser <keir.xen@gmail.com>
  * 
  *