Explorar el Código

STM32F7: DWC OTG USB Controller

Keir Fraser hace 5 años
padre
commit
a2a4615d28
Se han modificado 17 ficheros con 1063 adiciones y 416 borrados
  1. 1 1
      bootloader/Makefile
  2. 3 2
      bootloader/usb/Makefile
  3. 0 4
      inc/stm32/common.h
  4. 0 274
      inc/stm32/common_regs.h
  5. 0 3
      inc/stm32/f1.h
  6. 0 2
      inc/stm32/f7.h
  7. 1 1
      src/Makefile
  8. 3 13
      src/main.c
  9. 9 6
      src/stm32f7.c
  10. 3 2
      src/usb/Makefile
  11. 3 3
      src/usb/cdc_acm.c
  12. 102 3
      src/usb/core.c
  13. 5 3
      src/usb/defs.h
  14. 429 0
      src/usb/hw_dwc_otg.c
  15. 365 0
      src/usb/hw_dwc_otg.h
  16. 24 99
      src/usb/hw_f1.c
  17. 115 0
      src/usb/hw_f1.h

+ 1 - 1
bootloader/Makefile

@@ -13,7 +13,7 @@ OBJS-$(stm32f1) += fpec.o
 
 OBJS-$(debug) += console.o
 
-SUBDIRS-$(stm32f1) += usb
+SUBDIRS += usb
 
 .PHONY: $(RPATH)/build_info.c
 build_info.o: CFLAGS += -DFW_MAJOR=$(FW_MAJOR) -DFW_MINOR=$(FW_MINOR)

+ 3 - 2
bootloader/usb/Makefile

@@ -3,6 +3,7 @@ RPATH = ../../src/usb
 OBJS += config.o
 OBJS += core.o
 OBJS += cdc_acm.o
-OBJS += hw_f1.o
+OBJS-$(stm32f1) += hw_f1.o
+OBJS-$(stm32f7) += hw_dwc_otg.o
 
-$(OBJS): CFLAGS += -include $(ROOT)/src/usb/defs.h
+$(OBJS) $(OBJS-y): CFLAGS += -include $(ROOT)/src/usb/defs.h

+ 0 - 4
inc/stm32/common.h

@@ -25,10 +25,6 @@
 #define SPI volatile struct spi * const
 #define I2C volatile struct i2c * const
 #define USART volatile struct usart * const
-#define USB volatile struct usb * const
-#define USB_BUFD volatile struct usb_bufd * const
-#define USB_BUF volatile uint32_t * const
-#define USB_OTG volatile struct usb_otg * const
 
 /* NVIC table */
 extern uint32_t vector_table[];

+ 0 - 274
inc/stm32/common_regs.h

@@ -321,280 +321,6 @@ struct spi {
 #define SPI2_BASE 0x40003800
 #define SPI3_BASE 0x40003C00
 
-/* USB Full Speed */
-struct usb {
-    uint32_t epr[8];   /* 4*n: Endpoint n */
-    uint32_t rsvd[8];
-    uint32_t cntr;     /* 40: Control */
-    uint32_t istr;     /* 44: Interrupt status */
-    uint32_t fnr;      /* 48: Frame number */
-    uint32_t daddr;    /* 4C: Device address */
-    uint32_t btable;   /* 50: Buffer table address */
-};
-
-struct usb_bufd {
-    union {
-        struct {
-            uint32_t addr_tx;  /* 00: Transmission buffer address */
-            uint32_t count_tx; /* 04: Transmission byte count */
-            uint32_t addr_rx;  /* 08: Reception buffer address */
-            uint32_t count_rx; /* 0C: Reception byte count */
-        };
-        struct {
-            uint32_t addr_0;  /* 00: Double buffer #0 address */
-            uint32_t count_0; /* 04: Double buffer #0 byte count */
-            uint32_t addr_1;  /* 08: Double buffer #1 address */
-            uint32_t count_1; /* 0C: Double buffer #1 byte count */
-        };
-    };
-};
-
-#define USB_EPR_CTR_RX       (1u<<15)
-#define USB_EPR_DTOG_RX      (1u<<14)
-#define USB_EPR_STAT_RX(x)   ((x)<<12)
-#define USB_EPR_SETUP        (1u<<11)
-#define USB_EPR_EP_TYPE(x)   ((x)<<9)
-#define USB_EPR_EP_KIND_DBL_BUF (1<<8)    /* USB_EP_TYPE_BULK */
-#define USB_EPR_EP_KIND_STATUS_OUT (1<<8) /* USB_EP_TYPE_CONTROL */
-#define USB_EPR_CTR_TX       (1u<< 7)
-#define USB_EPR_DTOG_TX      (1u<< 6)
-#define USB_EPR_STAT_TX(x)   ((x)<<4)
-#define USB_EPR_EA(x)        ((x)<<0)
-
-#define USB_STAT_DISABLED    (0u)
-#define USB_STAT_STALL       (1u)
-#define USB_STAT_NAK         (2u)
-#define USB_STAT_VALID       (3u)
-#define USB_STAT_MASK        (3u)
-
-#define USB_EP_TYPE_BULK     (0u)
-#define USB_EP_TYPE_CONTROL  (1u)
-#define USB_EP_TYPE_ISO      (2u)
-#define USB_EP_TYPE_INTERRUPT (3u)
-#define USB_EP_TYPE_MASK     (3u)
-
-#define USB_CNTR_CTRM        (1u<<15)
-#define USB_CNTR_PMAOVRM     (1u<<14)
-#define USB_CNTR_ERRM        (1u<<13)
-#define USB_CNTR_WKUPM       (1u<<12)
-#define USB_CNTR_SUSPM       (1u<<11)
-#define USB_CNTR_RESETM      (1u<<10)
-#define USB_CNTR_SOFM        (1u<< 9)
-#define USB_CNTR_ESOFM       (1u<< 8)
-#define USB_CNTR_RESUME      (1u<< 4)
-#define USB_CNTR_FSUSP       (1u<< 3)
-#define USB_CNTR_LP_MODE     (1u<< 2)
-#define USB_CNTR_PDWN        (1u<< 1)
-#define USB_CNTR_FRES        (1u<< 0)
-
-#define USB_ISTR_CTR         (1u<<15)
-#define USB_ISTR_PMAOVR      (1u<<14)
-#define USB_ISTR_ERR         (1u<<13)
-#define USB_ISTR_WKUP        (1u<<12)
-#define USB_ISTR_SUSP        (1u<<11)
-#define USB_ISTR_RESET       (1u<<10)
-#define USB_ISTR_SOF         (1u<< 9)
-#define USB_ISTR_ESOF        (1u<< 8)
-#define USB_ISTR_DIR         (1u<< 4)
-#define USB_ISTR_GET_EP_ID(x) ((x)&0xf)
-
-#define USB_FNR_RXDP         (1u<<15)
-#define USB_FNR_RXDM         (1u<<14)
-#define USB_FNR_LCK          (1u<<13)
-#define USB_FNR_GET_LSOF(x)  (((x)>>11)&3)
-#define USB_FNR_GET_FN(x)    ((x)&0x7ff)
-
-#define USB_DADDR_EF         (1u<< 7)
-#define USB_DADDR_ADD(x)     ((x)<<0)
-
-/* USB On-The-Go Full Speed interface */
-struct usb_otg {
-    uint32_t gotctl;   /* 00: Control and status */
-    uint32_t gotgint;  /* 04: Interrupt */
-    uint32_t gahbcfg;  /* 08: AHB configuration */
-    uint32_t gusbcfg;  /* 0C: USB configuration */
-    uint32_t grstctl;  /* 10: Reset */
-    uint32_t gintsts;  /* 14: Core interrupt */
-    uint32_t gintmsk;  /* 18: Interrupt mask */
-    uint32_t grxstsr;  /* 1C: Receive status debug read */
-    uint32_t grxstsp;  /* 20: Receive status debug pop */
-    uint32_t grxfsiz;  /* 24: Receive FIFO size */
-    union {
-        uint32_t hnptxfsiz;  /* 28: Host non-periodic transmit FIFO size */
-        uint32_t dieptxf0;   /* 28: Endpoint 0 transmit FIFO size */
-    };
-    uint32_t hnptxsts; /* 2C: Non-periodic transmit FIFO/queue status */
-    uint32_t _0[2];
-    uint32_t gccfg;    /* 38: General core configuration */
-    uint32_t cid;      /* 3C: Core ID */
-    uint32_t _1[48];
-    uint32_t hptxfsiz; /* 100: Host periodic transmit FIFO size */
-    uint32_t dieptxf1; /* 104: Device IN endpoint transmit FIFO #1 size */
-    uint32_t dieptxf2; /* 108: Device IN endpoint transmit FIFO #2 size */
-    uint32_t dieptxf3; /* 10C: Device IN endpoint transmit FIFO #3 size */
-    uint32_t _2[188];
-    uint32_t hcfg;     /* 400: Host configuration */
-    uint32_t hfir;     /* 404: Host frame interval */
-    uint32_t hfnum;    /* 408: Host frame number / frame time remaining */
-    uint32_t _3[1];    /* 40C: */
-    uint32_t hptxsts;  /* 410: Host periodic transmit FIFO / queue status */
-    uint32_t haint;    /* 414: Host all channels interrupt status */
-    uint32_t haintmsk; /* 418: Host all channels interrupt mask */
-    uint32_t _4[9];
-    uint32_t hprt;     /* 440: Host port control and status */
-    uint32_t _5[47];
-    struct {
-        uint32_t charac; /* +00: Host channel-x characteristics */
-        uint32_t _0[1];
-        uint32_t intsts; /* +08: Host channel-x interrupt status */
-        uint32_t intmsk; /* +0C: Host channel-x interrupt mask */
-        uint32_t tsiz;   /* +10: Host channel x transfer size */
-        uint32_t _1[3];
-    } hc[8];           /* 500..5E0: */
-    uint32_t _6[128];
-
-    uint32_t dcfg;     /* 800: Device configuration */
-    uint32_t dctl;     /* 804: Device control */
-    uint32_t dsts;     /* 808: Device status */
-    uint32_t _7[1];
-    uint32_t diepmsk;  /* 810: Device IN endpoint common interrupt mask */
-    uint32_t doepmsk;  /* 814: Device OUT endpoint common interrupt mask */
-    uint32_t daint;    /* 818: Device all endpoints interrupt status */
-    uint32_t daintmsk; /* 81C: Device all endpoints interrupt mask */
-    uint32_t _8[2];
-    uint32_t dvbusdis; /* 828: Device VBUS discharge time */
-    uint32_t dvbuspulse; /* 82C: Device VBUS pulsing time */
-    uint32_t _9[1];
-    uint32_t diepempmsk; /* 834: Device IN endpoint FIFO empty int. mask */
-    uint32_t _10[50];
-    struct {
-        uint32_t ctl;    /* +00: Device IN endpoint-x control */
-        uint32_t _0[1];
-        uint32_t intsts; /* +08: Device IN endpoint-x interrupt status */
-        uint32_t _1[3];
-        uint32_t txfsts; /* +18: Device IN endpoint-x transmit FIFO status */
-        uint32_t _2[1];
-    } diep[4];         /* 900..960: */
-    uint32_t _11[96];
-    struct {
-        uint32_t ctl;    /* +00: Device OUT endpoint-x control */
-        uint32_t _0[1];
-        uint32_t intsts; /* +08: Device OUT endpoint-x interrupt status */
-        uint32_t _1[1];
-        uint32_t tsiz;   /* +10: Device OUT endpoint-x transmit FIFO status */
-        uint32_t _2[3];
-    } doep[4];         /* B00..B60: */
-    uint32_t _12[160];
-
-    uint32_t pcgcctl;  /* E00: Power and clock gating control */
-};
-
-#define OTG_GAHBCFG_PTXFELVL (1u<< 8)
-#define OTG_GAHBCFG_TXFELVL  (1u<< 7)
-#define OTG_GAHBCFG_GINTMSK  (1u<< 0)
-
-#define OTG_GUSBCFG_CTXPKT   (1u<<31)
-#define OTG_GUSBCFG_FDMOD    (1u<<30)
-#define OTG_GUSBCFG_FHMOD    (1u<<29)
-#define OTG_GUSBCFG_TRDT(x)  ((x)<<10)
-#define OTG_GUSBCFG_HNPCAP   (1u<< 9)
-#define OTG_GUSBCFG_SRPCAP   (1u<< 8)
-#define OTG_GUSBCFG_PHYSEL   (1u<< 6)
-#define OTG_GUSBCFG_TOCAL(x) ((x)<< 0)
-
-/* GINTSTS and GINTMSK */
-#define OTG_GINT_WKUPINT     (1u<<31) /* Host + Device */
-#define OTG_GINT_SRQINT      (1u<<30) /* H + D */
-#define OTG_GINT_DISCINT     (1u<<29) /* H */
-#define OTG_GINT_CIDSCHG     (1u<<28) /* H + D */
-#define OTG_GINT_PTXFE       (1u<<26) /* H */
-#define OTG_GINT_HCINT       (1u<<25) /* H */
-#define OTG_GINT_HPRTINT     (1u<<24) /* H */
-#define OTG_GINT_IPXFR       (1u<<21) /* H */
-#define OTG_GINT_IISOIXFR    (1u<<20) /* D */
-#define OTG_GINT_OEPINT      (1u<<19) /* D */
-#define OTG_GINT_IEPINT      (1u<<18) /* D */
-#define OTG_GINT_EOPF        (1u<<15) /* D */
-#define OTG_GINT_ISOODRP     (1u<<14) /* D */
-#define OTG_GINT_ENUMDNE     (1u<<13) /* D */
-#define OTG_GINT_USBRST      (1u<<12) /* D */
-#define OTG_GINT_USBSUSP     (1u<<11) /* D */
-#define OTG_GINT_ESUSP       (1u<<10) /* D */
-#define OTG_GINT_GONAKEFF    (1u<< 7) /* D */
-#define OTG_GINT_GINAKEFF    (1u<< 6) /* D */
-#define OTG_GINT_NPTXFE      (1u<< 5) /* H */
-#define OTG_GINT_RXFLVL      (1u<< 4) /* H + D */
-#define OTG_GINT_SOF         (1u<< 3) /* H + D */
-#define OTG_GINT_OTGINT      (1u<< 2) /* H + D */
-#define OTG_GINT_MMIS        (1u<< 1) /* H + D */
-#define OTG_GINT_CMOD        (1u<< 0) /* H + D */
-
-#define OTG_RXSTS_PKTSTS_IN  (2u)
-#define OTG_RXSTS_PKTSTS(r)  (((r)>>17)&0xf)
-#define OTG_RXSTS_BCNT(r)    (((r)>>4)&0x7ff)
-#define OTG_RXSTS_CHNUM(r)   ((r)&0xf)
-
-#define OTG_GCCFG_SOFOUTEN   (1u<<20)
-#define OTG_GCCFG_VBUSBSEN   (1u<<19)
-#define OTG_GCCFG_VBUSASEN   (1u<<18)
-#define OTG_GCCFG_PWRDWN     (1u<<16)
-
-#define OTG_HCFG_FSLSS       (1u<<2)
-#define OTG_HCFG_FSLSPCS     (3u<<0)
-#define OTG_HCFG_FSLSPCS_48  (1u<<0)
-#define OTG_HCFG_FSLSPCS_6   (2u<<0)
-
-#define OTG_HPRT_PSPD_FULL   (1u<<17)
-#define OTG_HPRT_PSPD_LOW    (2u<<17)
-#define OTG_HPRT_PSPD_MASK   (1u<<17) /* read-only */
-#define OTG_HPRT_PPWR        (1u<<12)
-#define OTG_HPRT_PRST        (1u<< 8)
-#define OTG_HPRT_PSUSP       (1u<< 7)
-#define OTG_HPRT_PRES        (1u<< 6)
-#define OTG_HPRT_POCCHNG     (1u<< 5) /* raises HPRTINT */
-#define OTG_HPRT_POCA        (1u<< 4)
-#define OTG_HPRT_PENCHNG     (1u<< 3) /* raises HPRTINT */
-#define OTG_HPRT_PENA        (1u<< 2)
-#define OTG_HPRT_PCDET       (1u<< 1) /* raises HPRTINT */
-#define OTG_HPRT_PCSTS       (1u<< 0)
-#define OTG_HPRT_INTS (OTG_HPRT_POCCHNG|OTG_HPRT_PENCHNG|OTG_HPRT_PCDET| \
-                       OTG_HPRT_PENA) /* PENA is also set-to-clear  */
-
-/* HCINTSTS and HCINTMSK */
-#define OTG_HCINT_DTERR      (1u<<10)
-#define OTG_HCINT_FRMOR      (1u<< 9)
-#define OTG_HCINT_BBERR      (1u<< 8)
-#define OTG_HCINT_TXERR      (1u<< 7)
-#define OTG_HCINT_NYET       (1u<< 6) /* high-speed only; not STM32F10x */
-#define OTG_HCINT_ACK        (1u<< 5)
-#define OTG_HCINT_NAK        (1u<< 4)
-#define OTG_HCINT_STALL      (1u<< 3)
-#define OTG_HCINT_CHH        (1u<< 1)
-#define OTG_HCINT_XFRC       (1u<< 0)
-
-#define OTG_HCCHAR_CHENA     (1u<<31)
-#define OTG_HCCHAR_CHDIS     (1u<<30)
-#define OTG_HCCHAR_ODDFRM    (1u<<29)
-#define OTG_HCCHAR_DAD(x)    ((x)<<22)
-#define OTG_HCCHAR_MCNT(x)   ((x)<<20)
-#define OTG_HCCHAR_ETYP_CTRL (0u<<18)
-#define OTG_HCCHAR_ETYP_ISO  (1u<<18)
-#define OTG_HCCHAR_ETYP_BULK (2u<<18)
-#define OTG_HCCHAR_ETYP_INT  (3u<<18)
-#define OTG_HCCHAR_LSDEV     (1u<<17)
-#define OTG_HCCHAR_EPDIR_OUT (0u<<15)
-#define OTG_HCCHAR_EPDIR_IN  (1u<<15)
-#define OTG_HCCHAR_EPNUM(x)  ((x)<<11)
-#define OTG_HCCHAR_MPSIZ(x)  ((x)<< 0)
-
-#define OTG_HCTSIZ_DPID_DATA0 (0u<<29)
-#define OTG_HCTSIZ_DPID_DATA2 (1u<<29)
-#define OTG_HCTSIZ_DPID_DATA1 (2u<<29)
-#define OTG_HCTSIZ_DPID_MDATA (3u<<29)
-#define OTG_HCTSIZ_DPID_SETUP (3u<<29)
-#define OTG_HCTSIZ_PKTCNT(x)  ((x)<<19)
-#define OTG_HCTSIZ_XFRSIZ(x)  ((x)<< 0)
-
 /*
  * Local variables:
  * mode: C

+ 0 - 3
inc/stm32/f1.h

@@ -49,9 +49,6 @@ static I2C i2c2 = (struct i2c *)I2C2_BASE;
 static USART usart1 = (struct usart *)USART1_BASE;
 static USART usart2 = (struct usart *)USART2_BASE;
 static USART usart3 = (struct usart *)USART3_BASE;
-static USB usb = (struct usb *)USB_BASE;
-static USB_BUFD usb_bufd = (struct usb_bufd *)USB_BUF_BASE;
-static USB_BUF usb_buf = (uint32_t *)USB_BUF_BASE;
 
 #define SYSCLK_MHZ 72
 #define FLASH_PAGE_SIZE 1024

+ 0 - 2
inc/stm32/f7.h

@@ -62,8 +62,6 @@ static USART usart3 = (struct usart *)USART3_BASE;
 static USART usart4 = (struct usart *)USART4_BASE;
 static USART usart5 = (struct usart *)USART5_BASE;
 static USART usart6 = (struct usart *)USART6_BASE;
-static USB_OTG usb_otg_fs = (struct usb_otg *)USB_OTG_FS_BASE;
-static USB_OTG usb_otg_hs = (struct usb_otg *)USB_OTG_HS_BASE;
 
 #define SYSCLK_MHZ 216
 #define AHB_MHZ (SYSCLK_MHZ / 1)  /* 216MHz */

+ 1 - 1
src/Makefile

@@ -12,7 +12,7 @@ OBJS-$(stm32f1) += floppy.o
 
 OBJS-$(debug) += console.o
 
-SUBDIRS-$(stm32f1) += usb
+SUBDIRS += usb
 
 .PHONY: build_info.c
 build_info.o: CFLAGS += -DFW_MAJOR=$(FW_MAJOR) -DFW_MINOR=$(FW_MINOR)

+ 3 - 13
src/main.c

@@ -40,22 +40,12 @@ int main(void)
     printk("** Keir Fraser <keir.xen@gmail.com>\n");
     printk("** https://github.com/keirf/Greaseweazle\n\n");
 
-
-    {
-        int i;
-        gpio_configure_pin(gpioa, 15, GPO_pushpull(_2MHz, HIGH));
-        for (i = 0;; i++) {
-            printk("Hello %d\n", i);
-            delay_ms(200);
-            gpio_write_pin(gpioa, 15, LOW);
-            delay_ms(200);
-            gpio_write_pin(gpioa, 15, HIGH);
-        }
-    }
-
     floppy_init();
     usb_init();
 
+    /* XXX */
+    gpio_configure_pin(gpioa, 15, GPO_pushpull(_2MHz, HIGH));
+
     for (;;) {
         canary_check();
         usb_process();

+ 9 - 6
src/stm32f7.c

@@ -12,15 +12,17 @@
 /* XXX */
 void floppy_init(void) {}
 void floppy_process(void) {}
-void usb_init(void) {}
-void usb_process(void) {}
 void fpec_init(void) {}
 void fpec_page_erase(uint32_t flash_address) {}
 void fpec_write(const void *data, unsigned int size, uint32_t flash_address) {}
-void usb_read(uint8_t ep, void *buf, uint32_t len) {}
-void usb_write(uint8_t ep, const void *buf, uint32_t len) {}
-bool_t ep_tx_ready(uint8_t ep) { return FALSE; }
-int ep_rx_ready(uint8_t ep) { return -1; }
+#ifndef BOOTLOADER
+static void floppy_reset(void) {}
+static void floppy_configure(void) {}
+const struct usb_class_ops usb_cdc_acm_ops = {
+    .reset = floppy_reset,
+    .configure = floppy_configure
+};
+#endif
 
 static void clock_init(void)
 {
@@ -79,6 +81,7 @@ static void peripheral_init(void)
                     RCC_AHB1ENR_GPIOCEN |
                     RCC_AHB1ENR_GPIOBEN | 
                     RCC_AHB1ENR_GPIOAEN);
+    rcc->apb2enr = (RCC_APB2ENR_SYSCFGEN);
     peripheral_clock_delay();
 
     /* Release JTAG pins. */

+ 3 - 2
src/usb/Makefile

@@ -1,6 +1,7 @@
 OBJS += config.o
 OBJS += core.o
 OBJS += cdc_acm.o
-OBJS += hw_f1.o
+OBJS-$(stm32f1) += hw_f1.o
+OBJS-$(stm32f7) += hw_dwc_otg.o
 
-$(OBJS): CFLAGS += -include defs.h
+$(OBJS) $(OBJS-y): CFLAGS += -include defs.h

+ 3 - 3
src/usb/cdc_acm.c

@@ -100,16 +100,16 @@ bool_t cdc_acm_handle_class_request(void)
 
 bool_t cdc_acm_set_configuration(void)
 {
-    uint8_t bulk_type = USB_EP_TYPE_BULK_DBLBUF;
+    uint8_t bulk_type = EPT_DBLBUF;
 
 #ifdef BOOTLOADER
     /* We don't bother with the complicated double-buffered endpoints. The 
      * regular bulk endpoints are fast enough and possibly more reliable. */
-    bulk_type = USB_EP_TYPE_BULK;
+    bulk_type = EPT_BULK;
 #endif
 
     /* Notification Element (D->H) */
-    usb_configure_ep(0x81, USB_EP_TYPE_INTERRUPT, 0);
+    usb_configure_ep(0x81, EPT_INTERRUPT, 0);
     /* Bulk Pipe (H->D) */
     usb_configure_ep(0x02, bulk_type, USB_FS_MPS);
     /* Bulk Pipe (D->H) */

+ 102 - 3
src/usb/core.c

@@ -10,9 +10,8 @@
  */
 
 struct ep0 ep0;
-uint8_t pending_addr;
 
-bool_t handle_control_request(void)
+static bool_t handle_control_request(void)
 {
     struct usb_device_request *req = &ep0.req;
     bool_t handled = TRUE;
@@ -54,7 +53,7 @@ bool_t handle_control_request(void)
     } else if ((req->bmRequestType == 0x00)
                && (req->bRequest == SET_ADDRESS)) {
 
-        pending_addr = req->wValue & 0x7f;
+        usb_setaddr(req->wValue & 0x7f);
 
     } else if ((req->bmRequestType == 0x00)
               && (req->bRequest == SET_CONFIGURATION)) {
@@ -89,6 +88,106 @@ bool_t handle_control_request(void)
     return handled;
 }
 
+static void usb_write_ep0(void)
+{
+    uint32_t len;
+
+    if ((ep0.tx.todo < 0) || !ep_tx_ready(0))
+        return;
+
+    len = min_t(uint32_t, ep0.tx.todo, USB_FS_MPS);
+    usb_write(0, ep0.tx.p, len);
+
+    ep0.tx.p += len;
+    ep0.tx.todo -= len;
+
+    if (ep0.tx.todo == 0) {
+        /* USB Spec 1.1, Section 5.5.3: Data stage of a control transfer is
+         * complete when we have transferred the exact amount of data specified
+         * during Setup *or* transferred a short/zero packet. */
+        if (!ep0.tx.trunc || (len < USB_FS_MPS))
+            ep0.tx.todo = -1;
+    }
+}
+
+void handle_rx_ep0(bool_t is_setup)
+{
+    bool_t ready = FALSE;
+    uint8_t ep = 0;
+
+    if (is_setup) {
+
+        /* Control Transfer: Setup Stage. */
+        ep0.data_len = 0;
+        ep0.tx.todo = -1;
+        usb_read(ep, &ep0.req, sizeof(ep0.req));
+        ready = ep0_data_in() || (ep0.req.wLength == 0);
+
+    } else if (ep0.data_len < 0) {
+
+        /* Unexpected Transaction */
+        usb_stall(0);
+        usb_read(ep, NULL, 0);
+
+    } else if (ep0_data_out()) {
+
+        /* OUT Control Transfer: Data from Host. */
+        uint32_t len = ep_rx_ready(ep);
+        int l = 0;
+        if (ep0.data_len < sizeof(ep0.data))
+            l = min_t(int, sizeof(ep0.data)-ep0.data_len, len);
+        usb_read(ep, &ep0.data[ep0.data_len], l);
+        ep0.data_len += len;
+        if (ep0.data_len >= ep0.req.wLength) {
+            ep0.data_len = ep0.req.wLength; /* clip */
+            ready = TRUE;
+        }
+
+    } else {
+
+        /* IN Control Transfer: Status from Host. */
+        usb_read(ep, NULL, 0);
+        ep0.tx.todo = -1;
+        ep0.data_len = -1; /* Complete */
+
+    }
+
+    /* Are we ready to handle the Control Request? */
+    if (!ready)
+        return;
+
+    /* Attempt to handle the Control Request: */
+    if (!handle_control_request()) {
+
+        /* Unhandled Control Transfer: STALL */
+        usb_stall(0);
+        ep0.data_len = -1; /* Complete */
+
+    } else if (ep0_data_in()) {
+
+        /* IN Control Transfer: Send Data to Host. */
+        ep0.tx.p = ep0.data;
+        ep0.tx.todo = ep0.data_len;
+        ep0.tx.trunc = (ep0.data_len < ep0.req.wLength);
+        usb_write_ep0();
+
+    } else {
+
+        /* OUT Control Transfer: Send Status to Host. */
+        ep0.tx.p = NULL;
+        ep0.tx.todo = 0;
+        ep0.tx.trunc = FALSE;
+        usb_write_ep0();
+        ep0.data_len = -1; /* Complete */
+
+    }
+}
+
+void handle_tx_ep0(void)
+{
+    usb_write_ep0();
+}
+
 /*
  * Local variables:
  * mode: C

+ 5 - 3
src/usb/defs.h

@@ -67,12 +67,14 @@ bool_t cdc_acm_handle_class_request(void);
 bool_t cdc_acm_set_configuration(void);
 
 /* USB Core */
-extern uint8_t pending_addr;
-bool_t handle_control_request(void);
+void handle_rx_ep0(bool_t is_setup);
+void handle_tx_ep0(void);
 
 /* USB Hardware */
-#define USB_EP_TYPE_BULK_DBLBUF 4u /* Create a double-buffered Endpoint */
+enum { EPT_CONTROL=0, EPT_ISO, EPT_BULK, EPT_INTERRUPT, EPT_DBLBUF };
 void usb_configure_ep(uint8_t ep, uint8_t type, uint32_t size);
+void usb_stall(uint8_t ep);
+void usb_setaddr(uint8_t addr);
 
 #define WARN printk
 

+ 429 - 0
src/usb/hw_dwc_otg.c

@@ -0,0 +1,429 @@
+/*
+ * hw_dwc_otg.c
+ * 
+ * USB handling for DWC-OTG USB 2.0 controller.
+ * 
+ * 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_dwc_otg.h"
+
+static struct {
+    int rx_count;
+    uint32_t rx_data[USB_FS_MPS / 4];
+    bool_t rx_ready, tx_ready;
+} eps[conf_nr_ep];
+
+static void core_reset(void)
+{
+    do { delay_us(1); } while (!(otg->grstctl & OTG_GRSTCTL_AHBIDL));
+    otg->grstctl |= OTG_GRSTCTL_CSRST;
+    do { delay_us(1); } while (otg->grstctl & OTG_GRSTCTL_CSRST);
+}
+
+static void flush_tx_fifo(int num)
+{
+    otg->grstctl = OTG_GRSTCTL_TXFFLSH | OTG_GRSTCTL_TXFNUM(num);
+    do { delay_us(1); } while (otg->grstctl & OTG_GRSTCTL_TXFFLSH);
+}
+
+static void flush_rx_fifo(void)
+{
+    otg->grstctl = OTG_GRSTCTL_RXFFLSH;
+    do { delay_us(1); } while (otg->grstctl & OTG_GRSTCTL_RXFFLSH);
+}
+
+static void ep0_out_start(void)
+{
+    otg_doep[0].tsiz = (OTG_DOEPTSZ_STUPCNT |
+                        OTG_DOEPTSZ_PKTCNT(1)) |
+                        OTG_DOEPTSZ_XFERSIZ(3*8);
+}
+
+static bool_t is_high_speed(void)
+{
+    /* 0 == DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ */
+    return ((otgd->dsts >> 1) & 3) == 0;
+}
+
+static void prepare_rx(uint8_t ep)
+{
+    OTG_DOEP doep = &otg_doep[ep];
+    uint16_t len = (ep == 0) ? 64 : (doep->ctl & 0x7ff);
+    uint32_t tsiz = doep->tsiz & 0xe0000000;
+
+    tsiz |= OTG_DOEPTSZ_PKTCNT(1);
+    tsiz |= OTG_DOEPTSZ_XFERSIZ(len);
+    doep->tsiz = tsiz;
+
+    doep->ctl |= OTG_DOEPCTL_CNAK | OTG_DOEPCTL_EPENA;
+}
+
+static void read_packet(void *p, int len)
+{
+    uint32_t *_p = p;
+    unsigned int n = (len + 3) / 4;
+    while (n--)
+        *_p++ = otg_dfifo[0].x[0];
+}
+
+static void write_packet(const void *p, uint8_t ep, int len)
+{
+    const uint32_t *_p = p;
+    unsigned int n = (len + 3) / 4;
+    while (n--)
+        otg_dfifo[ep].x[0] = *_p++;
+}
+
+void usb_init(void)
+{
+    int i;
+
+    /*
+     * HAL_PCD_MspInit
+     */
+
+    switch (conf_port) {
+    case PORT_FS:
+        gpio_set_af(gpioa, 11, 10);
+        gpio_set_af(gpioa, 12, 10);
+        gpio_configure_pin(gpioa, 11, AFO_pushpull(IOSPD_HIGH));
+        gpio_configure_pin(gpioa, 12, AFO_pushpull(IOSPD_HIGH));
+        rcc->ahb2enr |= RCC_AHB2ENR_OTGFSEN;
+        break;
+    case PORT_HS:
+        gpio_set_af(gpiob, 14, 12);
+        gpio_set_af(gpiob, 15, 12);
+        gpio_configure_pin(gpiob, 14, AFO_pushpull(IOSPD_HIGH));
+        gpio_configure_pin(gpiob, 15, AFO_pushpull(IOSPD_HIGH));
+        rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
+        break;
+    default:
+        ASSERT(0);
+    }
+
+    peripheral_clock_delay();
+
+    /*
+     * USB_CoreInit
+     */
+
+    if (conf_iface == IFACE_FS) {
+        /* Select Full Speed PHY. */
+        otg->gusbcfg |= OTG_GUSBCFG_PHYSEL;
+        /* PHY selection must be followed by a core reset. */
+        core_reset();
+        /* Activate FS transceiver. */
+        otg->gccfg |= OTG_GCCFG_PWRDWN;
+    } else {
+        ASSERT(0);
+    }
+
+    /*
+     * USB_SetCurrentMode
+     */
+
+    /* Mode select followed by required 25ms delay. */
+    otg->gusbcfg |= OTG_GUSBCFG_FDMOD;
+    delay_ms(25);
+
+    /*
+     * USB_DevInit
+     */
+
+    for (i = 0; i < ARRAY_SIZE(otg->dieptxf); i++)
+        otg->dieptxf[i] = 0;
+
+    /* Override Vbus sense */
+    otg->gccfg &= ~OTG_GCCFG_VBDEN;
+    otg->gotgctl |= (OTG_GOTGCTL_BVALOVAL | OTG_GOTGCTL_BVALOEN); 
+
+    /* Restart PHY clock. */
+    otg_pcgcctl->pcgcctl = 0;
+
+    /* USB_SetDevSpeed */
+    if (conf_iface == IFACE_FS) {
+        otgd->dcfg = OTG_DCFG_DSPD(3); /* Full Speed */
+    } else {
+        ASSERT(0);
+    }
+
+    flush_tx_fifo(0x10);
+    flush_rx_fifo();
+
+    /* Clear endpoints state. */
+    otgd->diepmsk = otgd->doepmsk = otgd->daintmsk = 0;
+    for (i = 0; i < conf_nr_ep; i++) {
+        bool_t ena = !!(otg_diep[i].ctl & OTG_DIEPCTL_EPENA);
+        otg_diep[i].ctl =
+            (!ena ? 0 : ((i == 0)
+                         ? OTG_DIEPCTL_SNAK
+                         : OTG_DIEPCTL_SNAK | OTG_DIEPCTL_EPDIS));
+        otg_diep[i].tsiz = 0;
+        otg_diep[i].intsts = 0xffff;
+    }
+    for (i = 0; i < conf_nr_ep; i++) {
+        bool_t ena = !!(otg_doep[i].ctl & OTG_DOEPCTL_EPENA);
+        otg_doep[i].ctl =
+            (!ena ? 0 : ((i == 0)
+                         ? OTG_DOEPCTL_SNAK
+                         : OTG_DOEPCTL_SNAK | OTG_DOEPCTL_EPDIS));
+        otg_doep[i].tsiz = 0;
+        otg_doep[i].intsts = 0xffff;
+    }
+    otg->gintsts = ~0;
+    otg->gintmsk = (OTG_GINT_USBRST |
+                    OTG_GINT_ENUMDNE |
+                    OTG_GINT_IEPINT |
+                    OTG_GINT_OEPINT |
+                    OTG_GINT_RXFLVL);
+
+    /* Set the FIFOs. */
+    otg->grxfsiz = 512 / 4;
+    otg->dieptxf0 = ((128 / 4) << 16) | (512 / 4);
+    for (i = 1; i < conf_nr_ep; i++)
+        otg->dieptxf[i-1] = ((128 / 4) << 16) | ((512+i*128) / 4);
+
+    /* HAL_PCD_Start, USB_DevConnect */
+    otgd->dctl &= ~OTG_DCTL_SDIS;
+    delay_ms(3);
+}
+
+int ep_rx_ready(uint8_t ep)
+{
+    return eps[ep].rx_ready ? eps[ep].rx_count : -1;
+}
+
+bool_t ep_tx_ready(uint8_t ep)
+{
+    return eps[ep].tx_ready;
+}
+
+void usb_read(uint8_t ep, void *buf, uint32_t len)
+{
+    memcpy(buf, eps[ep].rx_data, len);
+    eps[ep].rx_ready = FALSE;
+    prepare_rx(ep);
+}
+
+void usb_write(uint8_t ep, const void *buf, uint32_t len)
+{
+    OTG_DIEP diep = &otg_diep[ep];
+
+    diep->tsiz = OTG_DIEPTSIZ_PKTCNT(1) | len;
+
+//    if (len != 0)
+//        otgd->diepempmsk |= 1u << ep;
+
+    diep->ctl |= OTG_DIEPCTL_CNAK | OTG_DIEPCTL_EPENA;
+    write_packet(buf, ep, len);
+    eps[ep].tx_ready = FALSE;
+}
+
+void usb_stall(uint8_t ep)
+{
+    otg_diep[ep].ctl |= OTG_DIEPCTL_STALL;
+    otg_doep[ep].ctl |= OTG_DOEPCTL_STALL;
+}
+
+void usb_configure_ep(uint8_t ep, uint8_t type, uint32_t size)
+{
+    bool_t in = !!(ep & 0x80);
+    ep &= 0x7f;
+
+    if (type == EPT_DBLBUF)
+        type = EPT_BULK;
+
+    if (in || (ep == 0)) {
+        otgd->daintmsk |= 1u << ep;
+        if (!(otg_diep[ep].ctl & OTG_DIEPCTL_USBAEP)) {
+            otg_diep[ep].ctl |= 
+                OTG_DIEPCTL_MPSIZ(size) |
+                OTG_DIEPCTL_EPTYP(type) |
+                OTG_DIEPCTL_TXFNUM(ep) |
+                OTG_DIEPCTL_SD0PID |
+                OTG_DIEPCTL_USBAEP;
+        }
+        eps[ep].tx_ready = TRUE;
+    }
+
+    if (!in) {
+        otgd->daintmsk |= 1u << (ep + 16);
+        if (!(otg_doep[ep].ctl & OTG_DOEPCTL_USBAEP)) {
+            otg_doep[ep].ctl |= 
+                OTG_DOEPCTL_MPSIZ(size) |
+                OTG_DOEPCTL_EPTYP(type) |
+                OTG_DIEPCTL_SD0PID |
+                OTG_DOEPCTL_USBAEP;
+        }
+        eps[ep].rx_ready = FALSE;
+        prepare_rx(ep);
+    }
+}
+
+void usb_setaddr(uint8_t addr)
+{
+    otgd->dcfg = (otgd->dcfg & ~OTG_DCFG_DAD(0x7f)) | OTG_DCFG_DAD(addr);
+}
+
+static void handle_reset(void)
+{
+    int i;
+
+    /* Initialise core. */
+    otgd->dctl &= ~OTG_DCTL_RWUSIG;
+    flush_tx_fifo(0x10);
+    for (i = 0; i < conf_nr_ep; i++) {
+        otg_diep[i].ctl &= ~(OTG_DIEPCTL_STALL |
+                             OTG_DIEPCTL_USBAEP |
+                             OTG_DIEPCTL_MPSIZ(0x7ff) |
+                             OTG_DIEPCTL_TXFNUM(0xf) |
+                             OTG_DIEPCTL_SD0PID |
+                             OTG_DIEPCTL_EPTYP(3));
+        otg_doep[i].ctl &= ~(OTG_DOEPCTL_STALL |
+                             OTG_DOEPCTL_USBAEP |
+                             OTG_DOEPCTL_MPSIZ(0x7ff) |
+                             OTG_DOEPCTL_SD0PID |
+                             OTG_DOEPCTL_EPTYP(3));
+    }
+    otgd->daintmsk = 0x10001u;
+    otgd->doepmsk |= (OTG_DOEPMSK_STUPM |
+                      OTG_DOEPMSK_XFRCM |
+                      OTG_DOEPMSK_EPDM |
+                      OTG_DOEPMSK_STSPHSRXM |
+                      OTG_DOEPMSK_NAKM);
+    otgd->diepmsk |= (OTG_DIEPMSK_TOM |
+                      OTG_DIEPMSK_XFRCM |
+                      OTG_DIEPMSK_EPDM);
+
+    /* Set address 0. */
+    otgd->dcfg &= ~OTG_DCFG_DAD(0x7f);
+    ep0_out_start();
+
+    /* 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;
+}
+
+static void handle_rx_transfer(void)
+{
+    uint32_t grxsts = otg->grxstsp;
+    unsigned int bcnt = OTG_RXSTS_BCNT(grxsts);
+    unsigned int ep = OTG_RXSTS_CHNUM(grxsts);
+    switch (OTG_RXSTS_PKTSTS(grxsts)) {
+    case STS_SETUP_UPDT:
+        bcnt = 8;
+    case STS_DATA_UPDT:
+        read_packet(eps[ep].rx_data, bcnt);
+        eps[ep].rx_count = bcnt;
+        break;
+    default:
+        break;
+    }
+}
+
+static void handle_oepint(uint8_t ep)
+{
+    uint32_t oepint = otg_doep[ep].intsts & otgd->doepmsk;
+
+    otg_doep[ep].intsts = oepint;
+
+    if (oepint & OTG_DOEPMSK_XFRCM) {
+        eps[ep].rx_ready = TRUE;
+        if (ep == 0)
+            handle_rx_ep0(FALSE);
+    }
+
+    if (oepint & OTG_DOEPMSK_STUPM) {
+        eps[ep].rx_ready = TRUE;
+        if (ep == 0)
+            handle_rx_ep0(TRUE);
+    }
+}
+
+static void handle_iepint(uint8_t ep)
+{
+    uint32_t iepint = otg_diep[ep].intsts, iepmsk;
+
+    iepmsk = otgd->diepmsk | (((otgd->diepempmsk >> ep) & 1) << 7);
+    iepint = otg_diep[ep].intsts & iepmsk;
+
+    otg_diep[ep].intsts = iepint;
+
+    if (iepint & OTG_DIEPINT_XFRC) {
+        otgd->diepempmsk &= ~(1 << ep);
+        eps[ep].tx_ready = TRUE;
+        if (ep == 0)
+            handle_tx_ep0();
+    }
+
+    if (iepint & OTG_DIEPINT_TXFE) {
+        ASSERT(0);
+    }
+}
+
+void usb_process(void)
+{
+    uint32_t gintsts = otg->gintsts & otg->gintmsk;
+
+    if (gintsts & OTG_GINT_OEPINT) {
+        uint16_t mask = (otgd->daint & otgd->daintmsk) >> 16;
+        int ep;
+        for (ep = 0; mask != 0; mask >>= 1, ep++) {
+            if (mask & 1)
+                handle_oepint(ep);
+        }
+    }
+
+    if (gintsts & OTG_GINT_IEPINT) {
+        uint16_t mask = otgd->daint & otgd->daintmsk;
+        int ep;
+        for (ep = 0; mask != 0; mask >>= 1, ep++) {
+            if (mask & 1)
+                handle_iepint(ep);
+        }
+    }
+
+    if (gintsts & OTG_GINT_ENUMDNE) {
+        bool_t hs;
+        printk("[ENUMDNE]\n");
+        /* USB_ActivateSetup */
+        otg_diep[0].ctl &= ~OTG_DIEPCTL_MPSIZ(0x7ff);
+        otgd->dctl |= OTG_DCTL_CGINAK;
+        /* USB_SetTurnaroundTime */
+        hs = is_high_speed();
+        /* Ref. Table 232, FS Mode */
+        otg->gusbcfg |= OTG_GUSBCFG_TRDT(hs ? 9 : 6);
+        usb_configure_ep(0, EPT_CONTROL, USB_FS_MPS);
+        otg->gintsts = OTG_GINT_ENUMDNE;
+    }
+
+    if (gintsts & OTG_GINT_USBRST) {
+        printk("[USBRST]\n");
+        handle_reset();
+        otg->gintsts = OTG_GINT_USBRST;
+    }
+
+    if (gintsts & OTG_GINT_RXFLVL) {
+        handle_rx_transfer();
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 365 - 0
src/usb/hw_dwc_otg.h

@@ -0,0 +1,365 @@
+/*
+ * hw_dwc_otg.h
+ * 
+ * USB register definitions for DWC-OTG USB 2.0 controller.
+ * 
+ * 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>.
+ */
+
+#define PORT_FS 0
+#define PORT_HS 1
+
+#define IFACE_FS 0
+#define IFACE_HS_EMBEDDED 1
+#define IFACE_HS_ULPI     2
+
+#define conf_port PORT_HS
+#define conf_iface IFACE_FS
+#define conf_nr_ep 4
+
+/* USB On-The-Go Full Speed interface */
+struct otg {
+    /* GLOBAL */
+    uint32_t gotgctl;  /* 00: Control and status */
+    uint32_t gotgint;  /* 04: Interrupt */
+    uint32_t gahbcfg;  /* 08: AHB configuration */
+    uint32_t gusbcfg;  /* 0C: USB configuration */
+    uint32_t grstctl;  /* 10: Reset */
+    uint32_t gintsts;  /* 14: Core interrupt */
+    uint32_t gintmsk;  /* 18: Interrupt mask */
+    uint32_t grxstsr;  /* 1C: Receive status debug read */
+    uint32_t grxstsp;  /* 20: Receive status read & pop */
+    uint32_t grxfsiz;  /* 24: Receive FIFO size */
+    union {
+        uint32_t hnptxfsiz;  /* 28: Host non-periodic transmit FIFO size */
+        uint32_t dieptxf0;   /* 28: Endpoint 0 transmit FIFO size */
+    };
+    uint32_t hnptxsts; /* 2C: Non-periodic transmit FIFO/queue status */
+    uint32_t _0[2];
+    uint32_t gccfg;    /* 38: General core configuration */
+    uint32_t cid;      /* 3C: Core ID */
+    uint32_t _1[48];
+    uint32_t hptxfsiz; /* 100: Host periodic transmit FIFO size */
+    uint32_t dieptxf[15]; /* 104: Device IN endpoint transmit FIFO sizes */
+};
+
+struct otgh {
+    /* HOST */
+    uint32_t hcfg;     /* 400: Host configuration */
+    uint32_t hfir;     /* 404: Host frame interval */
+    uint32_t hfnum;    /* 408: Host frame number / frame time remaining */
+    uint32_t _3[1];    /* 40C: */
+    uint32_t hptxsts;  /* 410: Host periodic transmit FIFO / queue status */
+    uint32_t haint;    /* 414: Host all channels interrupt status */
+    uint32_t haintmsk; /* 418: Host all channels interrupt mask */
+    uint32_t _4[9];
+    uint32_t hprt;     /* 440: Host port control and status */
+    uint32_t _5[47];
+    struct {
+        uint32_t charac; /* +00: Host channel-x characteristics */
+        uint32_t _0[1];
+        uint32_t intsts; /* +08: Host channel-x interrupt status */
+        uint32_t intmsk; /* +0C: Host channel-x interrupt mask */
+        uint32_t tsiz;   /* +10: Host channel x transfer size */
+        uint32_t _1[3];
+    } hc[8];           /* 500..5E0: */
+};
+
+struct otgd {
+    /* DEVICE */
+    uint32_t dcfg;     /* 800: Device configuration */
+    uint32_t dctl;     /* 804: Device control */
+    uint32_t dsts;     /* 808: Device status */
+    uint32_t _7[1];
+    uint32_t diepmsk;  /* 810: Device IN endpoint common interrupt mask */
+    uint32_t doepmsk;  /* 814: Device OUT endpoint common interrupt mask */
+    uint32_t daint;    /* 818: Device all endpoints interrupt status */
+    uint32_t daintmsk; /* 81C: Device all endpoints interrupt mask */
+    uint32_t _8[2];
+    uint32_t dvbusdis; /* 828: Device VBUS discharge time */
+    uint32_t dvbuspulse; /* 82C: Device VBUS pulsing time */
+    uint32_t _9[1];
+    uint32_t diepempmsk; /* 834: Device IN endpoint FIFO empty int. mask */
+};
+
+struct otg_diep { /* 900.. */
+    /* DEVICE IN */
+    uint32_t ctl;    /* +00: Device IN endpoint-x control */
+    uint32_t _0[1];
+    uint32_t intsts; /* +08: Device IN endpoint-x interrupt status */
+    uint32_t _1[1];
+    uint32_t tsiz;   /* +10: Device IN endpoint-x transfer size */
+    uint32_t dma;    /* +14: Device IN endpoint-x DMA address */
+    uint32_t txfsts; /* +18: Device IN endpoint-x transmit FIFO status */
+    uint32_t _3[1];
+};
+
+struct otg_doep { /* B00.. */
+    /* DEVICE OUT */
+    uint32_t ctl;    /* +00: Device OUT endpoint-x control */
+    uint32_t _0[1];
+    uint32_t intsts; /* +08: Device OUT endpoint-x interrupt status */
+    uint32_t _1[1];
+    uint32_t tsiz;   /* +10: Device OUT endpoint-x transmit FIFO status */
+    uint32_t dma;    /* +14: Device OUT endpoint-x DMA address */
+    uint32_t _2[2];
+};
+
+struct otg_pcgcctl {
+    uint32_t pcgcctl;  /* E00: Power and clock gating control */
+};
+
+struct otg_dfifo { /* 1000.. */
+    uint32_t x[0x1000/4];
+};
+
+#define OTG_GOTGCTL_CURMOD   (1u<<21)
+#define OTG_GOTGCTL_OTGVER   (1u<<20)
+#define OTG_GOTGCTL_BSVLD    (1u<<19)
+#define OTG_GOTGCTL_ASVLD    (1u<<18)
+#define OTG_GOTGCTL_DBCT     (1u<<17)
+#define OTG_GOTGCTL_CIDSTS   (1u<<16)
+#define OTG_GOTGCTL_EHEN     (1u<<12)
+#define OTG_GOTGCTL_DHNPEN   (1u<<11)
+#define OTG_GOTGCTL_HSHNPEN  (1u<<10)
+#define OTG_GOTGCTL_HNPRQ    (1u<< 9)
+#define OTG_GOTGCTL_HNGSCS   (1u<< 8)
+#define OTG_GOTGCTL_BVALOVAL (1u<< 7)
+#define OTG_GOTGCTL_BVALOEN  (1u<< 6)
+#define OTG_GOTGCTL_AVALOVAL (1u<< 5)
+#define OTG_GOTGCTL_AVALOEN  (1u<< 4)
+#define OTG_GOTGCTL_VBVALOVAL (1u<< 3)
+#define OTG_GOTGCTL_VBVALOEN (1u<< 2)
+#define OTG_GOTGCTL_SRQ      (1u<< 1)
+#define OTG_GOTGCTL_SRQSCS   (1u<< 0)
+
+#define OTG_GAHBCFG_PTXFELVL (1u<< 8)
+#define OTG_GAHBCFG_TXFELVL  (1u<< 7)
+#define OTG_GAHBCFG_GINTMSK  (1u<< 0)
+
+#define OTG_GUSBCFG_CTXPKT   (1u<<31)
+#define OTG_GUSBCFG_FDMOD    (1u<<30)
+#define OTG_GUSBCFG_FHMOD    (1u<<29)
+#define OTG_GUSBCFG_TRDT(x)  ((x)<<10)
+#define OTG_GUSBCFG_HNPCAP   (1u<< 9)
+#define OTG_GUSBCFG_SRPCAP   (1u<< 8)
+#define OTG_GUSBCFG_PHYSEL   (1u<< 6)
+#define OTG_GUSBCFG_TOCAL(x) ((x)<< 0)
+
+#define OTG_GRSTCTL_AHBIDL   (1u<<31)
+#define OTG_GRSTCTL_DMAREQ   (1u<<30)
+#define OTG_GRSTCTL_TXFNUM(x) ((x)<<6)
+#define OTG_GRSTCTL_TXFFLSH  (1u<< 5)
+#define OTG_GRSTCTL_RXFFLSH  (1u<< 4)
+#define OTG_GRSTCTL_PSRST    (1u<< 1)
+#define OTG_GRSTCTL_CSRST    (1u<< 0)
+
+/* GINTSTS and GINTMSK */
+#define OTG_GINT_WKUPINT     (1u<<31) /* Host + Device */
+#define OTG_GINT_SRQINT      (1u<<30) /* H + D */
+#define OTG_GINT_DISCINT     (1u<<29) /* H */
+#define OTG_GINT_CIDSCHG     (1u<<28) /* H + D */
+#define OTG_GINT_PTXFE       (1u<<26) /* H */
+#define OTG_GINT_HCINT       (1u<<25) /* H */
+#define OTG_GINT_HPRTINT     (1u<<24) /* H */
+#define OTG_GINT_IPXFR       (1u<<21) /* H */
+#define OTG_GINT_IISOIXFR    (1u<<20) /* D */
+#define OTG_GINT_OEPINT      (1u<<19) /* D */
+#define OTG_GINT_IEPINT      (1u<<18) /* D */
+#define OTG_GINT_EOPF        (1u<<15) /* D */
+#define OTG_GINT_ISOODRP     (1u<<14) /* D */
+#define OTG_GINT_ENUMDNE     (1u<<13) /* D */
+#define OTG_GINT_USBRST      (1u<<12) /* D */
+#define OTG_GINT_USBSUSP     (1u<<11) /* D */
+#define OTG_GINT_ESUSP       (1u<<10) /* D */
+#define OTG_GINT_GONAKEFF    (1u<< 7) /* D */
+#define OTG_GINT_GINAKEFF    (1u<< 6) /* D */
+#define OTG_GINT_NPTXFE      (1u<< 5) /* H */
+#define OTG_GINT_RXFLVL      (1u<< 4) /* H + D */
+#define OTG_GINT_SOF         (1u<< 3) /* H + D */
+#define OTG_GINT_OTGINT      (1u<< 2) /* H + D */
+#define OTG_GINT_MMIS        (1u<< 1) /* H + D */
+#define OTG_GINT_CMOD        (1u<< 0) /* H + D */
+
+#define STS_GOUT_NAK                           1U
+#define STS_DATA_UPDT                          2U
+#define STS_XFER_COMP                          3U
+#define STS_SETUP_COMP                         4U
+#define STS_SETUP_UPDT                         6U
+#define OTG_RXSTS_PKTSTS(r)  (((r)>>17)&0xf)
+#define OTG_RXSTS_BCNT(r)    (((r)>>4)&0x7ff)
+#define OTG_RXSTS_CHNUM(r)   ((r)&0xf)
+
+#define OTG_GCCFG_VBDEN      (1u<<21)
+#define OTG_GCCFG_PWRDWN     (1u<<16)
+
+#define OTG_HCFG_FSLSS       (1u<<2)
+#define OTG_HCFG_FSLSPCS     (3u<<0)
+#define OTG_HCFG_FSLSPCS_48  (1u<<0)
+#define OTG_HCFG_FSLSPCS_6   (2u<<0)
+
+#define OTG_HPRT_PSPD_FULL   (1u<<17)
+#define OTG_HPRT_PSPD_LOW    (2u<<17)
+#define OTG_HPRT_PSPD_MASK   (1u<<17) /* read-only */
+#define OTG_HPRT_PPWR        (1u<<12)
+#define OTG_HPRT_PRST        (1u<< 8)
+#define OTG_HPRT_PSUSP       (1u<< 7)
+#define OTG_HPRT_PRES        (1u<< 6)
+#define OTG_HPRT_POCCHNG     (1u<< 5) /* raises HPRTINT */
+#define OTG_HPRT_POCA        (1u<< 4)
+#define OTG_HPRT_PENCHNG     (1u<< 3) /* raises HPRTINT */
+#define OTG_HPRT_PENA        (1u<< 2)
+#define OTG_HPRT_PCDET       (1u<< 1) /* raises HPRTINT */
+#define OTG_HPRT_PCSTS       (1u<< 0)
+#define OTG_HPRT_INTS (OTG_HPRT_POCCHNG|OTG_HPRT_PENCHNG|OTG_HPRT_PCDET| \
+                       OTG_HPRT_PENA) /* PENA is also set-to-clear  */
+
+/* HCINTSTS and HCINTMSK */
+#define OTG_HCINT_DTERR      (1u<<10)
+#define OTG_HCINT_FRMOR      (1u<< 9)
+#define OTG_HCINT_BBERR      (1u<< 8)
+#define OTG_HCINT_TXERR      (1u<< 7)
+#define OTG_HCINT_NYET       (1u<< 6) /* high-speed only; not STM32F10x */
+#define OTG_HCINT_ACK        (1u<< 5)
+#define OTG_HCINT_NAK        (1u<< 4)
+#define OTG_HCINT_STALL      (1u<< 3)
+#define OTG_HCINT_CHH        (1u<< 1)
+#define OTG_HCINT_XFRC       (1u<< 0)
+
+#define OTG_HCCHAR_CHENA     (1u<<31)
+#define OTG_HCCHAR_CHDIS     (1u<<30)
+#define OTG_HCCHAR_ODDFRM    (1u<<29)
+#define OTG_HCCHAR_DAD(x)    ((x)<<22)
+#define OTG_HCCHAR_MCNT(x)   ((x)<<20)
+#define OTG_HCCHAR_ETYP_CTRL (0u<<18)
+#define OTG_HCCHAR_ETYP_ISO  (1u<<18)
+#define OTG_HCCHAR_ETYP_BULK (2u<<18)
+#define OTG_HCCHAR_ETYP_INT  (3u<<18)
+#define OTG_HCCHAR_LSDEV     (1u<<17)
+#define OTG_HCCHAR_EPDIR_OUT (0u<<15)
+#define OTG_HCCHAR_EPDIR_IN  (1u<<15)
+#define OTG_HCCHAR_EPNUM(x)  ((x)<<11)
+#define OTG_HCCHAR_MPSIZ(x)  ((x)<< 0)
+
+#define OTG_HCTSIZ_DPID_DATA0 (0u<<29)
+#define OTG_HCTSIZ_DPID_DATA2 (1u<<29)
+#define OTG_HCTSIZ_DPID_DATA1 (2u<<29)
+#define OTG_HCTSIZ_DPID_MDATA (3u<<29)
+#define OTG_HCTSIZ_DPID_SETUP (3u<<29)
+#define OTG_HCTSIZ_PKTCNT(x)  ((x)<<19)
+#define OTG_HCTSIZ_XFRSIZ(x)  ((x)<< 0)
+
+#define OTG_DCFG_PERSCHIVL(x) ((x)<<24)
+#define OTG_DCFG_ERRATIM      (1u<<15)
+#define OTG_DCFG_XCVRDLY      (1u<<14)
+#define OTG_DCFG_PFIVL(x)     ((x)<<11)
+#define OTG_DCFG_DAD(x)       ((x)<<4)
+#define OTG_DCFG_NZLSOHSK     (1u<< 2)
+#define OTG_DCFG_DSPD(x)      ((x)<<0)
+
+#define OTG_DCTL_DSBESLRJCT   (1u<<18)
+#define OTG_DCTL_POPRGDNE     (1u<<11)
+#define OTG_DCTL_CGONAK       (1u<<10)
+#define OTG_DCTL_SGONAK       (1u<< 9)
+#define OTG_DCTL_CGINAK       (1u<< 8)
+#define OTG_DCTL_SGINAK       (1u<< 7)
+#define OTG_DCTL_GONSTS       (1u<< 3)
+#define OTG_DCTL_GINSTS       (1u<< 2)
+#define OTG_DCTL_SDIS         (1u<< 1)
+#define OTG_DCTL_RWUSIG       (1u<< 0)
+
+#define OTG_DIEPMSK_NAKM      (1u<<13)
+#define OTG_DIEPMSK_TXFURM    (1u<< 8)
+#define OTG_DIEPMSK_INEPNEM   (1u<< 6)
+#define OTG_DIEPMSK_INEPNMM   (1u<< 5)
+#define OTG_DIEPMSK_ITTXFEMSK (1u<< 4)
+#define OTG_DIEPMSK_TOM       (1u<< 3)
+#define OTG_DIEPMSK_AHBERRM   (1u<< 2)
+#define OTG_DIEPMSK_EPDM      (1u<< 1)
+#define OTG_DIEPMSK_XFRCM     (1u<< 0)
+
+#define OTG_DIEPINT_TXFE      (1u<< 7)
+#define OTG_DIEPINT_XFRC      (1u<< 0)
+
+#define OTG_DOEPMSK_NYETMSK   (1u<<14)
+#define OTG_DOEPMSK_NAKM      (1u<<13)
+#define OTG_DOEPMSK_BERRM     (1u<<12)
+#define OTG_DOEPMSK_OUTPKTERRM (1u<< 8)
+#define OTG_DOEPMSK_B2BSTUPM  (1u<< 6)
+#define OTG_DOEPMSK_STSPHSRXM (1u<< 5)
+#define OTG_DOEPMSK_OTEPDM    (1u<< 4)
+#define OTG_DOEPMSK_STUPM     (1u<< 3)
+#define OTG_DOEPMSK_AHBERRM   (1u<< 2)
+#define OTG_DOEPMSK_EPDM      (1u<< 1)
+#define OTG_DOEPMSK_XFRCM     (1u<< 0)
+
+#define OTG_DIEPCTL_EPENA     (1u<<31)
+#define OTG_DIEPCTL_EPDIS     (1u<<30)
+#define OTG_DIEPCTL_SODDFRM   (1u<<29)
+#define OTG_DIEPCTL_SD0PID    (1u<<28)
+#define OTG_DIEPCTL_SNAK      (1u<<27)
+#define OTG_DIEPCTL_CNAK      (1u<<26)
+#define OTG_DIEPCTL_TXFNUM(x) ((x)<<22)
+#define OTG_DIEPCTL_STALL     (1u<<21)
+#define OTG_DIEPCTL_EPTYP(x)  ((x)<<18)
+#define OTG_DIEPCTL_NAKSTS    (1u<<17)
+#define OTG_DIEPCTL_DPID      (1u<<16)
+#define OTG_DIEPCTL_USBAEP    (1u<<15)
+#define OTG_DIEPCTL_MPSIZ(x)  ((x)<<0)
+
+#define OTG_DIEPTSIZ_PKTCNT(x) ((x)<<19)
+#define OTG_DIEPTSIZ_XFRSIZ(x) ((x)<<0)
+
+#define OTG_DOEPCTL_EPENA     (1u<<31)
+#define OTG_DOEPCTL_EPDIS     (1u<<30)
+#define OTG_DOEPCTL_SD1PID    (1u<<29)
+#define OTG_DOEPCTL_SD0PID    (1u<<28)
+#define OTG_DOEPCTL_SNAK      (1u<<27)
+#define OTG_DOEPCTL_CNAK      (1u<<26)
+#define OTG_DOEPCTL_STALL     (1u<<21)
+#define OTG_DOEPCTL_SNPM      (1u<<20)
+#define OTG_DOEPCTL_EPTYP(x)  ((x)<<18)
+#define OTG_DOEPCTL_NAKSTS    (1u<<17)
+#define OTG_DOEPCTL_DPID      (1u<<16)
+#define OTG_DOEPCTL_USBAEP    (1u<<15)
+#define OTG_DOEPCTL_MPSIZ(x)  ((x)<<0)
+
+#define OTG_DOEPTSZ_STUPCNT   (3u<<29)
+#define OTG_DOEPTSZ_PKTCNT(x) ((x)<<19)
+#define OTG_DOEPTSZ_XFERSIZ(x) ((x)<<0)
+
+/* C pointer types */
+#define OTG volatile struct otg * const
+#define OTGH volatile struct otgh * const
+#define OTGD volatile struct otgd * const
+#define OTG_DIEP volatile struct otg_diep * const
+#define OTG_DOEP volatile struct otg_doep * const
+#define OTG_PCGCCTL volatile struct otg_pcgcctl * const
+#define OTG_DFIFO volatile struct otg_dfifo * const
+
+/* C-accessible registers. */
+#if conf_port == PORT_FS
+#define OTG_BASE USB_OTG_FS_BASE
+#else
+#define OTG_BASE USB_OTG_HS_BASE
+#endif
+static OTG otg = (struct otg *)(OTG_BASE + 0x000);
+static OTGH otgh = (struct otgh *)(OTG_BASE + 0x400);
+static OTGD otgd = (struct otgd *)(OTG_BASE + 0x800);
+static OTG_DIEP otg_diep = (struct otg_diep *)(OTG_BASE + 0x900);
+static OTG_DOEP otg_doep = (struct otg_doep *)(OTG_BASE + 0xb00);
+static OTG_PCGCCTL otg_pcgcctl = (struct otg_pcgcctl *)(OTG_BASE + 0xe00);
+static OTG_DFIFO otg_dfifo = (struct otg_dfifo *)(OTG_BASE + 0x1000);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 24 - 99
src/usb/hw_f1.c

@@ -9,7 +9,10 @@
  * 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 
@@ -170,29 +173,7 @@ void usb_write(uint8_t ep, const void *buf, uint32_t len)
     usb->epr[ep] = epr;
 }
 
-static void usb_write_ep0(void)
-{
-    uint32_t len;
-
-    if ((ep0.tx.todo < 0) || !ep_tx_ready(0))
-        return;
-
-    len = min_t(uint32_t, ep0.tx.todo, USB_FS_MPS);
-    usb_write(0, ep0.tx.p, len);
-
-    ep0.tx.p += len;
-    ep0.tx.todo -= len;
-
-    if (ep0.tx.todo == 0) {
-        /* USB Spec 1.1, Section 5.5.3: Data stage of a control transfer is
-         * complete when we have transferred the exact amount of data specified
-         * during Setup *or* transferred a short/zero packet. */
-        if (!ep0.tx.trunc || (len < USB_FS_MPS))
-            ep0.tx.todo = -1;
-    }
-}
-
-static void usb_stall(uint8_t ep)
+void usb_stall(uint8_t ep)
 {
     uint16_t epr = usb->epr[ep];
     epr &= 0x073f;
@@ -203,6 +184,13 @@ static void usb_stall(uint8_t ep)
 
 void usb_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;
@@ -214,16 +202,18 @@ void usb_configure_ep(uint8_t ep, uint8_t type, uint32_t size)
     old_epr = usb->epr[ep];
     new_epr = 0;
 
-    dbl_buf = (type == USB_EP_TYPE_BULK_DBLBUF);
+    dbl_buf = (type == EPT_DBLBUF);
     if (dbl_buf) {
         ASSERT(ep != 0);
-        type = USB_EP_TYPE_BULK;
+        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);
@@ -265,6 +255,11 @@ void usb_configure_ep(uint8_t ep, uint8_t type, uint32_t size)
     usb->epr[ep] = new_epr;
 }
 
+void usb_setaddr(uint8_t addr)
+{
+    pending_addr = addr;
+}
+
 static void handle_reset(void)
 {
     /* Reinitialise class-specific subsystem. */
@@ -280,7 +275,7 @@ static void handle_reset(void)
     /* Prepare for Enumeration: Set up Endpoint 0 at Address 0. */
     pending_addr = 0;
     buf_end = 64;
-    usb_configure_ep(0, USB_EP_TYPE_CONTROL, USB_FS_MPS);
+    usb_configure_ep(0, EPT_CONTROL, USB_FS_MPS);
     usb->daddr = USB_DADDR_EF | USB_DADDR_ADD(0);
     usb->istr &= ~USB_ISTR_RESET;
 }
@@ -297,83 +292,13 @@ static void clear_ctr(uint8_t ep, uint16_t ctr)
 static void handle_rx_transfer(uint8_t ep)
 {
     uint16_t epr = usb->epr[ep];
-    bool_t ready;
 
     clear_ctr(ep, USB_EPR_CTR_RX);
     eps[ep].rx_ready = TRUE;
 
     /* We only handle Control Transfers here (endpoint 0). */
-    if (ep != 0)
-        return;
-
-    ready = FALSE;
-
-    if (epr & USB_EPR_SETUP) {
-
-        /* Control Transfer: Setup Stage. */
-        ep0.data_len = 0;
-        ep0.tx.todo = -1;
-        usb_read(ep, &ep0.req, sizeof(ep0.req));
-        ready = ep0_data_in() || (ep0.req.wLength == 0);
-
-    } else if (ep0.data_len < 0) {
-
-        /* Unexpected Transaction */
-        usb_stall(0);
-        usb_read(ep, NULL, 0);
-
-    } else if (ep0_data_out()) {
-
-        /* OUT Control Transfer: Data from Host. */
-        uint32_t len = usb_bufd[ep].count_rx & 0x3ff;
-        int l = 0;
-        if (ep0.data_len < sizeof(ep0.data))
-            l = min_t(int, sizeof(ep0.data)-ep0.data_len, len);
-        usb_read(ep, &ep0.data[ep0.data_len], l);
-        ep0.data_len += len;
-        if (ep0.data_len >= ep0.req.wLength) {
-            ep0.data_len = ep0.req.wLength; /* clip */
-            ready = TRUE;
-        }
-
-    } else {
-
-        /* IN Control Transfer: Status from Host. */
-        usb_read(ep, NULL, 0);
-        ep0.tx.todo = -1;
-        ep0.data_len = -1; /* Complete */
-
-    }
-
-    /* Are we ready to handle the Control Request? */
-    if (!ready)
-        return;
-
-    /* Attempt to handle the Control Request: */
-    if (!handle_control_request()) {
-
-        /* Unhandled Control Transfer: STALL */
-        usb_stall(0);
-        ep0.data_len = -1; /* Complete */
-
-    } else if (ep0_data_in()) {
-
-        /* IN Control Transfer: Send Data to Host. */
-        ep0.tx.p = ep0.data;
-        ep0.tx.todo = ep0.data_len;
-        ep0.tx.trunc = (ep0.data_len < ep0.req.wLength);
-        usb_write_ep0();
-
-    } else {
-
-        /* OUT Control Transfer: Send Status to Host. */
-        ep0.tx.p = NULL;
-        ep0.tx.todo = 0;
-        ep0.tx.trunc = FALSE;
-        usb_write_ep0();
-        ep0.data_len = -1; /* Complete */
-
-    }
+    if (ep == 0)
+        handle_rx_ep0(!!(epr & USB_EPR_SETUP));
 }
 
 static void handle_tx_transfer(uint8_t ep)
@@ -385,7 +310,7 @@ static void handle_tx_transfer(uint8_t ep)
     if (ep != 0)
         return;
 
-    usb_write_ep0();
+    handle_tx_ep0();
 
     if (pending_addr && (ep0.tx.todo == -1)) {
         /* We have just completed the Status stage of a SET_ADDRESS request. 

+ 115 - 0
src/usb/hw_f1.h

@@ -0,0 +1,115 @@
+/*
+ * hw_f1.h
+ * 
+ * USB register definitions for STM32F10x devices (except 105/107).
+ * 
+ * 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>.
+ */
+
+struct usb {
+    uint32_t epr[8];   /* 4*n: Endpoint n */
+    uint32_t rsvd[8];
+    uint32_t cntr;     /* 40: Control */
+    uint32_t istr;     /* 44: Interrupt status */
+    uint32_t fnr;      /* 48: Frame number */
+    uint32_t daddr;    /* 4C: Device address */
+    uint32_t btable;   /* 50: Buffer table address */
+};
+
+struct usb_bufd {
+    union {
+        struct {
+            uint32_t addr_tx;  /* 00: Transmission buffer address */
+            uint32_t count_tx; /* 04: Transmission byte count */
+            uint32_t addr_rx;  /* 08: Reception buffer address */
+            uint32_t count_rx; /* 0C: Reception byte count */
+        };
+        struct {
+            uint32_t addr_0;  /* 00: Double buffer #0 address */
+            uint32_t count_0; /* 04: Double buffer #0 byte count */
+            uint32_t addr_1;  /* 08: Double buffer #1 address */
+            uint32_t count_1; /* 0C: Double buffer #1 byte count */
+        };
+    };
+};
+
+#define USB_EPR_CTR_RX       (1u<<15)
+#define USB_EPR_DTOG_RX      (1u<<14)
+#define USB_EPR_STAT_RX(x)   ((x)<<12)
+#define USB_EPR_SETUP        (1u<<11)
+#define USB_EPR_EP_TYPE(x)   ((x)<<9)
+#define USB_EPR_EP_KIND_DBL_BUF (1<<8)    /* USB_EP_TYPE_BULK */
+#define USB_EPR_EP_KIND_STATUS_OUT (1<<8) /* USB_EP_TYPE_CONTROL */
+#define USB_EPR_CTR_TX       (1u<< 7)
+#define USB_EPR_DTOG_TX      (1u<< 6)
+#define USB_EPR_STAT_TX(x)   ((x)<<4)
+#define USB_EPR_EA(x)        ((x)<<0)
+
+#define USB_STAT_DISABLED    (0u)
+#define USB_STAT_STALL       (1u)
+#define USB_STAT_NAK         (2u)
+#define USB_STAT_VALID       (3u)
+#define USB_STAT_MASK        (3u)
+
+#define USB_EP_TYPE_BULK     (0u)
+#define USB_EP_TYPE_CONTROL  (1u)
+#define USB_EP_TYPE_ISO      (2u)
+#define USB_EP_TYPE_INTERRUPT (3u)
+#define USB_EP_TYPE_MASK     (3u)
+
+#define USB_CNTR_CTRM        (1u<<15)
+#define USB_CNTR_PMAOVRM     (1u<<14)
+#define USB_CNTR_ERRM        (1u<<13)
+#define USB_CNTR_WKUPM       (1u<<12)
+#define USB_CNTR_SUSPM       (1u<<11)
+#define USB_CNTR_RESETM      (1u<<10)
+#define USB_CNTR_SOFM        (1u<< 9)
+#define USB_CNTR_ESOFM       (1u<< 8)
+#define USB_CNTR_RESUME      (1u<< 4)
+#define USB_CNTR_FSUSP       (1u<< 3)
+#define USB_CNTR_LP_MODE     (1u<< 2)
+#define USB_CNTR_PDWN        (1u<< 1)
+#define USB_CNTR_FRES        (1u<< 0)
+
+#define USB_ISTR_CTR         (1u<<15)
+#define USB_ISTR_PMAOVR      (1u<<14)
+#define USB_ISTR_ERR         (1u<<13)
+#define USB_ISTR_WKUP        (1u<<12)
+#define USB_ISTR_SUSP        (1u<<11)
+#define USB_ISTR_RESET       (1u<<10)
+#define USB_ISTR_SOF         (1u<< 9)
+#define USB_ISTR_ESOF        (1u<< 8)
+#define USB_ISTR_DIR         (1u<< 4)
+#define USB_ISTR_GET_EP_ID(x) ((x)&0xf)
+
+#define USB_FNR_RXDP         (1u<<15)
+#define USB_FNR_RXDM         (1u<<14)
+#define USB_FNR_LCK          (1u<<13)
+#define USB_FNR_GET_LSOF(x)  (((x)>>11)&3)
+#define USB_FNR_GET_FN(x)    ((x)&0x7ff)
+
+#define USB_DADDR_EF         (1u<< 7)
+#define USB_DADDR_ADD(x)     ((x)<<0)
+
+/* C pointer types */
+#define USB volatile struct usb * const
+#define USB_BUFD volatile struct usb_bufd * const
+#define USB_BUF volatile uint32_t * const
+
+/* C-accessible registers. */
+static USB usb = (struct usb *)USB_BASE;
+static USB_BUFD usb_bufd = (struct usb_bufd *)USB_BUF_BASE;
+static USB_BUF usb_buf = (uint32_t *)USB_BUF_BASE;
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */