altos/stm32f4: Add start of stm32f413 USB support
authorKeith Packard <keithp@keithp.com>
Tue, 2 Oct 2018 19:46:19 +0000 (12:46 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 13 Oct 2018 15:23:25 +0000 (08:23 -0700)
This code doesn't work yet.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/stm32f4/ao_usb_gen.c [new file with mode: 0644]
src/stm32f4/ao_usb_gen.h [new file with mode: 0644]
src/stm32f4/ao_usb_stm32f4.c [new file with mode: 0644]

diff --git a/src/stm32f4/ao_usb_gen.c b/src/stm32f4/ao_usb_gen.c
new file mode 100644 (file)
index 0000000..760afad
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_usb_gen.h"
+
+static uint8_t         ao_usb_ep0_state;
+
+/* Pending EP0 IN data */
+static const uint8_t   *ao_usb_ep0_in_data;    /* Remaining data */
+static uint8_t                 ao_usb_ep0_in_len;      /* Remaining amount */
+
+/* Temp buffer for smaller EP0 in data */
+static uint8_t ao_usb_ep0_in_buf[2];
+
+/* Pending EP0 OUT data */
+static uint8_t *ao_usb_ep0_out_data;
+static uint8_t         ao_usb_ep0_out_len;
+
+/* System ram shadow of USB buffer; writing individual bytes is
+ * too much of a pain (sigh) */
+static uint8_t ao_usb_tx_buffer[AO_USB_IN_SIZE];
+static uint8_t ao_usb_tx_count;
+
+static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE];
+static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
+
+/*
+ * End point register indices
+ */
+
+#define AO_USB_CONTROL_EPR     0
+#define AO_USB_INT_EPR         1
+#define AO_USB_OUT_EPR         2
+#define AO_USB_IN_EPR          3
+
+/* Marks when we don't need to send an IN packet.
+ * This happens only when the last IN packet is not full,
+ * otherwise the host will expect to keep seeing packets.
+ * Send a zero-length packet as required
+ */
+static uint8_t ao_usb_in_flushed;
+
+/* Marks when we have delivered an IN packet to the hardware
+ * and it has not been received yet. ao_sleep on this address
+ * to wait for it to be delivered.
+ */
+static uint8_t ao_usb_in_pending;
+
+/* Marks when an OUT packet has been received by the hardware
+ * but not pulled to the shadow buffer.
+ */
+static uint8_t ao_usb_out_avail;
+uint8_t                ao_usb_running;
+static uint8_t ao_usb_configuration;
+
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+static void
+ao_usb_set_address(uint8_t address)
+{
+       ao_usb_dev_set_address(address);
+       ao_usb_address_pending = 0;
+}
+
+#define TX_DBG 0
+#define RX_DBG 0
+
+#if TX_DBG
+#define _tx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _tx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _tx_dbg0(msg)
+#define _tx_dbg1(msg,value)
+#endif
+
+#if RX_DBG
+#define _rx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _rx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _rx_dbg0(msg)
+#define _rx_dbg1(msg,value)
+#endif
+
+#if TX_DBG || RX_DBG
+static void _dbg(int line, char *msg, uint32_t value);
+#endif
+
+/*
+ * Set just endpoint 0, for use during startup
+ */
+
+static void
+ao_usb_set_ep0(void)
+{
+       ao_usb_dev_ep0_init();
+
+       ao_usb_set_address(0);
+
+       ao_usb_running = 0;
+
+       /* Reset our internal state
+        */
+
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+       ao_usb_ep0_in_data = NULL;
+       ao_usb_ep0_in_len = 0;
+
+       ao_usb_ep0_out_data = 0;
+       ao_usb_ep0_out_len = 0;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+#if 0
+       /* Set up the INT end point */
+       ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0;
+       ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_INT_SIZE;
+
+       ao_usb_init_ep(AO_USB_INT_EPR,
+                      AO_USB_INT_EP);
+
+       /* Set up the OUT end point */
+       ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+                                                     (((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+       ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_OUT_SIZE;
+
+       ao_usb_init_ep(AO_USB_OUT_EPR,
+                      AO_USB_OUT_EP,
+                      STM_USB_EPR_EP_TYPE_BULK,
+                      STM_USB_EPR_STAT_RX_VALID,
+                      STM_USB_EPR_STAT_TX_DISABLED);
+
+       /* Set up the IN end point */
+       ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0;
+       ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_IN_SIZE;
+
+       ao_usb_init_ep(AO_USB_IN_EPR,
+                      AO_USB_IN_EP,
+                      STM_USB_EPR_EP_TYPE_BULK,
+                      STM_USB_EPR_STAT_RX_DISABLED,
+                      STM_USB_EPR_STAT_TX_NAK);
+#endif
+
+       ao_usb_in_flushed = 0;
+       ao_usb_in_pending = 0;
+       ao_wakeup(&ao_usb_in_pending);
+
+       ao_usb_out_avail = 0;
+       ao_usb_configuration = 0;
+
+       ao_usb_running = 1;
+       ao_wakeup(&ao_usb_running);
+}
+
+static uint16_t        control_count;
+static uint16_t        in_count;
+static uint16_t        out_count;
+#if USB_DEBUG
+static uint16_t int_count;
+static uint16_t        reset_count;
+#endif
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+       uint8_t this_len;
+
+       /* Check to see if the endpoint is still busy */
+       if (ao_usb_dev_ep0_in_busy()) {
+               return;
+       }
+
+       this_len = ao_usb_ep0_in_len;
+       if (this_len > AO_USB_CONTROL_SIZE)
+               this_len = AO_USB_CONTROL_SIZE;
+
+       if (this_len < AO_USB_CONTROL_SIZE)
+               ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+       ao_usb_ep0_in_len -= this_len;
+
+       ao_usb_dev_ep0_in(ao_usb_ep0_in_data, this_len);
+       ao_usb_ep0_in_data += this_len;
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+       uint16_t        len;
+
+       len = ao_usb_dev_ep0_out(ao_usb_ep0_out_data, ao_usb_ep0_out_len);
+       ao_usb_ep0_out_len -= len;
+       ao_usb_ep0_out_data += len;
+}
+
+static void
+ao_usb_ep0_in_reset(void)
+{
+       ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+       ao_usb_ep0_in_len = 0;
+}
+
+static void
+ao_usb_ep0_in_queue_byte(uint8_t a)
+{
+       if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_buf))
+               ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_in_set(const uint8_t *data, uint8_t len)
+{
+       ao_usb_ep0_in_data = data;
+       ao_usb_ep0_in_len = len;
+}
+
+static void
+ao_usb_ep0_out_set(uint8_t *data, uint8_t len)
+{
+       ao_usb_ep0_out_data = data;
+       ao_usb_ep0_out_len = len;
+}
+
+static void
+ao_usb_ep0_in_start(uint16_t max)
+{
+       /* Don't send more than asked for */
+       if (ao_usb_ep0_in_len > max)
+               ao_usb_ep0_in_len = max;
+
+       ao_usb_dev_ep0_in(ao_usb_ep0_in_data, ao_usb_ep0_in_len);
+}
+
+struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value, uint16_t length)
+{
+       const uint8_t           *descriptor;
+       uint8_t         type = value >> 8;
+       uint8_t         index = value;
+
+       descriptor = ao_usb_descriptors;
+       while (descriptor[0] != 0) {
+               if (descriptor[1] == type && index-- == 0) {
+                       uint8_t len;
+                       if (type == AO_USB_DESC_CONFIGURATION)
+                               len = descriptor[2];
+                       else
+                               len = descriptor[0];
+                       if (len > length)
+                               len = length;
+                       ao_usb_ep0_in_set(descriptor, len);
+                       break;
+               }
+               descriptor += descriptor[0];
+       }
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+       uint16_t        setup_len;
+
+       /* Pull the setup packet out of the fifo */
+       setup_len = ao_usb_dev_ep0_out(&ao_usb_setup, 8);
+       if (setup_len != 8) {
+               return;
+       }
+
+       if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0)
+               ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+       else
+               ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+
+       ao_usb_ep0_in_reset();
+
+       switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+       case AO_USB_TYPE_STANDARD:
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_ADDRESS:
+                               ao_usb_address = ao_usb_setup.value;
+                               ao_usb_address_pending = 1;
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length);
+                               break;
+                       case AO_USB_REQ_GET_CONFIGURATION:
+                               ao_usb_ep0_in_queue_byte(ao_usb_configuration);
+                               break;
+                       case AO_USB_REQ_SET_CONFIGURATION:
+                               ao_usb_configuration = ao_usb_setup.value;
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_GET_INTERFACE:
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_INTERFACE:
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_ENDPOINT:
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       }
+                       break;
+               }
+               break;
+       case AO_USB_TYPE_CLASS:
+               switch (ao_usb_setup.request) {
+               case AO_USB_SET_LINE_CODING:
+                       ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_GET_LINE_CODING:
+                       ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_SET_CONTROL_LINE_STATE:
+                       break;
+               }
+               break;
+       }
+
+       /* If we're not waiting to receive data from the host,
+        * queue an IN response
+        */
+       if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+               ao_usb_ep0_in_start(ao_usb_setup.length);
+}
+
+static void
+ao_usb_ep0_handle(uint8_t receive)
+{
+       if (receive & AO_USB_EP0_GOT_RESET) {
+               ao_usb_set_ep0();
+               return;
+       }
+       if (receive & AO_USB_EP0_GOT_SETUP) {
+               ao_usb_ep0_setup();
+       }
+       if (receive & AO_USB_EP0_GOT_RX_DATA) {
+               if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) {
+                       ao_usb_ep0_fill();
+                       if (ao_usb_ep0_out_len == 0) {
+                               ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+                               ao_usb_ep0_in_start(0);
+                       }
+               }
+       }
+       if (receive & AO_USB_EP0_GOT_TX_ACK) {
+#if HAS_FLIGHT && AO_USB_FORCE_IDLE
+               ao_flight_force_idle = 1;
+#endif
+               /* Wait until the IN packet is received from addr 0
+                * before assigning our local address
+                */
+               if (ao_usb_address_pending)
+                       ao_usb_set_address(ao_usb_address);
+               if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+                       ao_usb_ep0_flush();
+       }
+}
+
+void
+ao_usb_ep0_interrupt(uint8_t mask)
+{
+       if (mask) {
+               ++control_count;
+               ao_usb_ep0_handle(mask);
+       }
+}
+
+void
+ao_usb_in_interrupt(uint32_t mask)
+{
+       if (mask & (1 << AO_USB_IN_EPR)) {
+               ++in_count;
+               _tx_dbg1("TX ISR", epr);
+               ao_usb_in_pending = 0;
+               ao_wakeup(&ao_usb_in_pending);
+       }
+}
+
+void
+ao_usb_out_interrupt(uint32_t mask)
+{
+       if (mask & (1 << AO_USB_OUT_EPR)) {
+               ++out_count;
+               _rx_dbg1("RX ISR", epr);
+               ao_usb_out_avail = 1;
+               _rx_dbg0("out avail set");
+               ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+               _rx_dbg0("stdin awoken");
+       }
+}
+
+void
+ao_usb_int_interrupt(uint32_t mask)
+{
+       (void) mask;
+}
+
+void
+stm_usb_fs_wkup(void)
+{
+       /* USB wakeup, just clear the bit for now */
+//     stm_usb.istr &= ~(1 << STM_USB_ISTR_WKUP);
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+_ao_usb_in_send(void)
+{
+       _tx_dbg0("in_send start");
+
+       while (ao_usb_in_pending)
+               ao_sleep(&ao_usb_in_pending);
+
+       ao_usb_in_pending = 1;
+       if (ao_usb_tx_count != AO_USB_IN_SIZE)
+               ao_usb_in_flushed = 1;
+
+       ao_usb_dev_ep_in(AO_USB_IN_EPR, ao_usb_tx_buffer, ao_usb_tx_count);
+       ao_usb_tx_count = 0;
+
+       _tx_dbg0("in_send end");
+}
+
+/* Wait for a free IN buffer. Interrupts are blocked */
+static void
+_ao_usb_in_wait(void)
+{
+       for (;;) {
+               /* Check if the current buffer is writable */
+               if (ao_usb_tx_count < AO_USB_IN_SIZE)
+                       break;
+
+               _tx_dbg0("in_wait top");
+               /* Wait for an IN buffer to be ready */
+               while (ao_usb_in_pending)
+                       ao_sleep(&ao_usb_in_pending);
+               _tx_dbg0("in_wait bottom");
+       }
+}
+
+void
+ao_usb_flush(void)
+{
+       if (!ao_usb_running)
+               return;
+
+       /* Anytime we've sent a character since
+        * the last time we flushed, we'll need
+        * to send a packet -- the only other time
+        * we would send a packet is when that
+        * packet was full, in which case we now
+        * want to send an empty packet
+        */
+       ao_arch_block_interrupts();
+       while (!ao_usb_in_flushed) {
+               _tx_dbg0("flush top");
+               _ao_usb_in_send();
+               _tx_dbg0("flush end");
+       }
+       ao_arch_release_interrupts();
+}
+
+void
+ao_usb_putchar(char c)
+{
+       if (!ao_usb_running)
+               return;
+
+       ao_arch_block_interrupts();
+       _ao_usb_in_wait();
+
+       ao_usb_in_flushed = 0;
+       ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c;
+
+       /* Send the packet when full */
+       if (ao_usb_tx_count == AO_USB_IN_SIZE) {
+               _tx_dbg0("putchar full");
+               _ao_usb_in_send();
+               _tx_dbg0("putchar flushed");
+       }
+       ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_out_recv(void)
+{
+       _rx_dbg0("out_recv top");
+       ao_usb_out_avail = 0;
+
+       ao_usb_rx_count = ao_usb_dev_ep_out(AO_USB_OUT_EPR, ao_usb_rx_buffer, sizeof (ao_usb_rx_buffer));
+
+       _rx_dbg1("out_recv count", ao_usb_rx_count);
+
+       ao_usb_rx_pos = 0;
+}
+
+int
+_ao_usb_pollchar(void)
+{
+       uint8_t c;
+
+       if (!ao_usb_running)
+               return AO_READ_AGAIN;
+
+       for (;;) {
+               if (ao_usb_rx_pos != ao_usb_rx_count)
+                       break;
+
+               _rx_dbg0("poll check");
+               /* Check to see if a packet has arrived */
+               if (!ao_usb_out_avail) {
+                       _rx_dbg0("poll none");
+                       return AO_READ_AGAIN;
+               }
+               _ao_usb_out_recv();
+       }
+
+       /* Pull a character out of the fifo */
+       c = ao_usb_rx_buffer[ao_usb_rx_pos++];
+       return c;
+}
+
+char
+ao_usb_getchar(void)
+{
+       int     c;
+
+       ao_arch_block_interrupts();
+       while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+               ao_sleep(AO_USB_OUT_SLEEP_ADDR);
+       ao_arch_release_interrupts();
+       return c;
+}
+
+#ifndef HAS_USB_DISABLE
+#define HAS_USB_DISABLE 1
+#endif
+
+#if HAS_USB_DISABLE
+void
+ao_usb_disable(void)
+{
+       ao_usb_dev_disable();
+}
+#endif
+
+void
+ao_usb_enable(void)
+{
+       ao_usb_dev_enable();
+
+       ao_usb_configuration = 0;
+}
+
+#if USB_ECHO
+struct ao_task ao_usb_echo_task;
+
+static void
+ao_usb_echo(void)
+{
+       char    c;
+
+       for (;;) {
+               c = ao_usb_getchar();
+               ao_usb_putchar(c);
+               ao_usb_flush();
+       }
+}
+#endif
+
+#if USB_DEBUG
+static void
+ao_usb_irq(void)
+{
+       printf ("control: %d out: %d in: %d int: %d reset: %d\n",
+               control_count, out_count, in_count, int_count, reset_count);
+}
+
+const struct ao_cmds ao_usb_cmds[] = {
+       { ao_usb_irq, "I\0Show USB interrupt counts" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_usb_init(void)
+{
+       ao_usb_enable();
+
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+#if USB_ECHO
+       ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
+#endif
+#if USB_DEBUG
+       ao_cmd_register(&ao_usb_cmds[0]);
+#endif
+#if !USB_ECHO
+#if USE_USB_STDIO
+       ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+#endif
+#endif
+}
+
+#if TX_DBG || RX_DBG
+
+struct ao_usb_dbg {
+       int             line;
+       char            *msg;
+       uint32_t        value;
+       uint32_t        prival;
+#if TX_DBG
+       uint16_t        in_count;
+       uint32_t        in_epr;
+       uint32_t        in_pending;
+       uint32_t        tx_count;
+       uint32_t        in_flushed;
+#endif
+#if RX_DBG
+       uint8_t         rx_count;
+       uint8_t         rx_pos;
+       uint8_t         out_avail;
+       uint32_t        out_epr;
+#endif
+};
+
+#define NUM_USB_DBG    16
+
+static struct ao_usb_dbg dbg[NUM_USB_DBG];
+static int dbg_i;
+
+static void _dbg(int line, char *msg, uint32_t value)
+{
+       uint32_t        prival;
+       dbg[dbg_i].line = line;
+       dbg[dbg_i].msg = msg;
+       dbg[dbg_i].value = value;
+#if AO_NONMASK_INTERRUPT
+       asm("mrs %0,basepri" : "=&r" (prival));
+#else
+       asm("mrs %0,primask" : "=&r" (prival));
+#endif
+       dbg[dbg_i].prival = prival;
+#if TX_DBG
+       dbg[dbg_i].in_count = in_count;
+       dbg[dbg_i].in_epr = stm_usb.epr[AO_USB_IN_EPR];
+       dbg[dbg_i].in_pending = ao_usb_in_pending;
+       dbg[dbg_i].tx_count = ao_usb_tx_count;
+       dbg[dbg_i].in_flushed = ao_usb_in_flushed;
+#endif
+#if RX_DBG
+       dbg[dbg_i].rx_count = ao_usb_rx_count;
+       dbg[dbg_i].rx_pos = ao_usb_rx_pos;
+       dbg[dbg_i].out_avail = ao_usb_out_avail;
+       dbg[dbg_i].out_epr = stm_usb.epr[AO_USB_OUT_EPR];
+#endif
+       if (++dbg_i == NUM_USB_DBG)
+               dbg_i = 0;
+}
+#endif
diff --git a/src/stm32f4/ao_usb_gen.h b/src/stm32f4/ao_usb_gen.h
new file mode 100644 (file)
index 0000000..b059ddb
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_USB_GEN_H_
+#define _AO_USB_GEN_H_
+
+#include "ao.h"
+#include "ao_usb.h"
+#include "ao_product.h"
+#include <stdint.h>
+
+#define USB_ECHO       0
+
+#ifndef USE_USB_STDIO
+#define USE_USB_STDIO  1
+#endif
+
+#if USE_USB_STDIO
+#define AO_USB_OUT_SLEEP_ADDR  (&ao_stdin_ready)
+#else
+#define AO_USB_OUT_SLEEP_ADDR  (&ao_usb_out_avail)
+#endif
+
+struct ao_usb_setup {
+       uint8_t         dir_type_recip;
+       uint8_t         request;
+       uint16_t        value;
+       uint16_t        index;
+       uint16_t        length;
+} ao_usb_setup;
+
+#define AO_USB_EP0_GOT_RESET   1
+#define AO_USB_EP0_GOT_SETUP   2
+#define AO_USB_EP0_GOT_RX_DATA 4
+#define AO_USB_EP0_GOT_TX_ACK  8
+
+/*
+ * End point register indices
+ */
+
+#define AO_USB_CONTROL_EPR     0
+#define AO_USB_INT_EPR         1
+#define AO_USB_OUT_EPR         2
+#define AO_USB_IN_EPR          3
+
+/* Device interfaces required */
+
+/* Queue IN bytes to EP0 */
+void
+ao_usb_dev_ep0_init(void);
+
+void
+ao_usb_dev_ep0_in(const void *data, uint16_t len);
+
+bool
+ao_usb_dev_ep0_in_busy(void);
+
+/* Receive OUT bytes from EP0 */
+uint16_t
+ao_usb_dev_ep0_out(void *data, uint16_t len);
+
+/* Set device address */
+void
+ao_usb_dev_set_address(uint8_t address);
+
+void
+ao_usb_dev_enable(void);
+
+void
+ao_usb_dev_disable(void);
+
+void
+ao_usb_dev_init(void);
+
+/* Queue IN bytes to EPn */
+void
+ao_usb_dev_ep_in(uint8_t ep, const void *data, uint16_t len);
+
+bool
+ao_usb_dev_ep_in_busy(uint8_t ep);
+
+/* Receive OUT bytes from EPn */
+uint16_t
+ao_usb_dev_ep_out(uint8_t ep, void *data, uint16_t len);
+
+
+/* General interfaces provided */
+
+void
+ao_usb_ep0_interrupt(uint8_t mask);
+
+void
+ao_usb_in_interrupt(uint32_t mask);
+
+void
+ao_usb_out_interrupt(uint32_t mask);
+
+#endif /* _AO_USB_GEN_H_ */
+
diff --git a/src/stm32f4/ao_usb_stm32f4.c b/src/stm32f4/ao_usb_stm32f4.c
new file mode 100644 (file)
index 0000000..e65c365
--- /dev/null
@@ -0,0 +1,1144 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_usb_gen.h"
+
+static uint32_t        grxstsp;
+
+static inline uint8_t
+grxstsp_enum(void)
+{
+       return (grxstsp >> STM_USB_GRXSTSP_EPNUM) & STM_USB_GRXSTSP_EPNUM_MASK;
+}
+
+static inline uint8_t
+grxstsp_pktsts(void)
+{
+       return (grxstsp >> STM_USB_GRXSTSP_PKTSTS) & STM_USB_GRXSTSP_PKTSTS_MASK;
+}
+
+static inline uint16_t
+grxstsp_bcnt(void)
+{
+       return (grxstsp >> STM_USB_GRXSTSP_BCNT) & STM_USB_GRXSTSP_BCNT_MASK;
+}
+
+static void
+ao_usb_dev_ep_out_start(uint8_t ep)
+{
+       stm_usb.doep[ep].doeptsiz = ((1 << STM_USB_DOEPTSIZ_PKTCNT) |
+                                    (3 << STM_USB_DOEPTSIZ_STUPCNT) |
+                                    (24 << STM_USB_DOEPTSIZ_XFRSIZ));
+
+//     stm_usb.doep[ep].doepctl |= (1 << STM_USB_DOEPCTL_EPENA);
+}
+
+static void
+ao_usb_mask_in_bits(vuint32_t *addr, uint32_t shift, uint32_t mask, uint32_t bits)
+{
+       uint32_t        value;
+
+       value = *addr;
+       value &= ~(mask << shift);
+       value |= (bits << shift);
+       *addr = value;
+}
+
+static void
+ao_usb_activate_ep0(void)
+{
+       stm_usb.diep[0].diepctl = ((0 << STM_USB_DIEPCTL_TXFNUM) |
+                                  (0 << STM_USB_DIEPCTL_STALL) |
+                                  (STM_USB_DIEPCTL_EPTYP_CONTROL << STM_USB_DIEPCTL_EPTYP) |
+                                  (1 << STM_USB_DIEPCTL_USBAEP) |
+                                  (STM_USB_DIEPCTL_MPSIZ0_64 << STM_USB_DIEPCTL_MPSIZ));
+       stm_usb.doep[0].doepctl = ((0 << STM_USB_DOEPCTL_SNPM) |
+                                  (STM_USB_DOEPCTL_EPTYP_CONTROL << STM_USB_DOEPCTL_EPTYP) |
+                                  (1 << STM_USB_DOEPCTL_USBAEP) |
+                                  (STM_USB_DOEPCTL_MPSIZ0_64 << STM_USB_DOEPCTL_MPSIZ));
+}
+
+#if 0
+static void
+ao_usb_activate_in(int epnum)
+{
+       stm_usb.daintmsk |= (1 << (epnum + STM_USB_DAINTMSK_IEPM));
+       stm_usb.diep[epnum].diepctl = ((epnum << STM_USB_DIEPCTL_TXFNUM) |
+                                      (0 << STM_USB_DIEPCTL_STALL) |
+                                      (STM_USB_DIEPCTL_EPTYP_BULK << STM_USB_DIEPCTL_EPTYP) |
+                                      (1 << STM_USB_DIEPCTL_USBAEP) |
+                                      (64 << STM_USB_DIEPCTL_MPSIZ));
+}
+
+static void
+ao_usb_activate_out(int epnum)
+{
+       stm_usb.daintmsk |= (1 << (epnum + STM_USB_DAINTMSK_OEPM));
+       stm_usb.doep[epnum].doepctl = ((0 << STM_USB_DOEPCTL_SNPM) |
+                                      (STM_USB_DOEPCTL_EPTYP_BULK << STM_USB_DOEPCTL_EPTYP) |
+                                      (1 << STM_USB_DOEPCTL_USBAEP) |
+                                      (64 << STM_USB_DOEPCTL_MPSIZ));
+}
+#endif
+
+static void
+ao_usb_enum_done(void)
+{
+       /* Set turn-around delay. 6 is for high hclk (> 32MHz) */
+       ao_usb_mask_in_bits(&stm_usb.gusbcfg, STM_USB_GUSBCFG_TRDT, STM_USB_GUSBCFG_TRDT_MASK, 6);
+
+       ao_usb_activate_ep0();
+}
+
+static void
+ao_usb_flush_tx_fifo(uint32_t fifo)
+{
+       stm_usb.grstctl = ((1 << STM_USB_GRSTCTL_TXFFLSH) |
+                          (fifo << STM_USB_GRSTCTL_TXFNUM));
+       while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_TXFFLSH)) != 0)
+               ao_arch_nop();
+}
+
+static void
+ao_usb_flush_rx_fifo(void)
+{
+       stm_usb.grstctl = (1 << STM_USB_GRSTCTL_RXFFLSH);
+       while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_RXFFLSH)) != 0)
+               ao_arch_nop();
+}
+
+/* reset and enable EP0 */
+void
+ao_usb_dev_ep0_init(void)
+{
+       uint32_t        diepctl;
+
+       /* Flush TX fifo */
+       ao_usb_flush_tx_fifo(STM_USB_GRSTCTL_TXFNUM_ALL);
+
+       /* Clear interrupts */
+       for (int i = 0; i < 6; i++) {
+               stm_usb.diep[i].diepint = 0xfffffffful;
+               stm_usb.doep[i].doepint = 0xfffffffful;
+       }
+       stm_usb.daint = 0xfffffffful;
+
+       /* Enable EP0 in/out interrupts */
+       /* 2. Unmask interrupt bits */
+       stm_usb.daintmsk |= ((1 << (STM_USB_DAINTMSK_IEPM + 0)) |
+                            (1 << (STM_USB_DAINTMSK_OEPM + 0)));
+
+       stm_usb.doepmsk |= ((1 << STM_USB_DOEPMSK_STUPM) |
+                           (1 << STM_USB_DOEPMSK_EPDM) |
+                           (1 << STM_USB_DOEPMSK_XFRCM));
+       stm_usb.diepmsk |= ((1 << STM_USB_DIEPMSK_TOM) |
+                           (1 << STM_USB_DIEPMSK_XFRCM) |
+                           (1 << STM_USB_DIEPMSK_EPDM));
+
+       /* 1. Set NAK bit for all OUT endpoints */
+       stm_usb.doep[0].doepctl |= (1 << STM_USB_DOEPCTL_CNAK);
+       for (int i = 1; i < 6; i++)
+               stm_usb.doep[i].doepctl |= (1 << STM_USB_DOEPCTL_SNAK);
+
+       /* 3. Setup FIFO ram allocation */
+
+       /* XXX make principled decisions here */
+       stm_usb.grxfsiz = 0x80;
+
+       stm_usb.dieptxf0 = ((0x40 << STM_USB_DIEPTXF0_TX0FD) |          /* size = 256 bytes */
+                           (0x80 << STM_USB_DIEPTXF0_TX0FSA));         /* start address = 0x80 */
+
+       /* 4. Program OUT endpoint 0 to receive a SETUP packet */
+
+       uint32_t        doeptsiz;
+
+       doeptsiz = ((1 << STM_USB_DOEPTSIZ_PKTCNT) |
+                   (0x40 << STM_USB_DOEPTSIZ_XFRSIZ) |
+                   (1 << STM_USB_DOEPTSIZ_STUPCNT));
+
+       stm_usb.doep[0].doeptsiz = doeptsiz;
+
+       /* Program MPSIZ field to set maximum packet size */
+
+       diepctl = ((0 << STM_USB_DIEPCTL_EPENA ) |
+                  (0 << STM_USB_DIEPCTL_EPDIS ) |
+                  (0 << STM_USB_DIEPCTL_SNAK ) |
+                  (0 << STM_USB_DIEPCTL_CNAK ) |
+                  (0 << STM_USB_DIEPCTL_TXFNUM) |
+                  (0 << STM_USB_DIEPCTL_STALL ) |
+                  (STM_USB_DIEPCTL_EPTYP_CONTROL << STM_USB_DIEPCTL_EPTYP ) |
+                  (0 << STM_USB_DIEPCTL_NAKSTS ) |
+                  (0 << STM_USB_DIEPCTL_EONUM ) |
+                  (1 << STM_USB_DIEPCTL_USBAEP ) |
+                  (STM_USB_DIEPCTL_MPSIZ0_64 << STM_USB_DIEPCTL_MPSIZ));
+
+       stm_usb.diep[0].diepctl = diepctl;
+
+       uint32_t        doepctl;
+
+       doepctl = ((0 << STM_USB_DOEPCTL_EPENA ) |
+                  (0 << STM_USB_DOEPCTL_EPDIS ) |
+                  (0 << STM_USB_DOEPCTL_SNAK ) |
+                  (0 << STM_USB_DOEPCTL_CNAK ) |
+                  (0 << STM_USB_DOEPCTL_STALL ) |
+                  (0 << STM_USB_DOEPCTL_SNPM ) |
+                  (STM_USB_DOEPCTL_EPTYP_CONTROL << STM_USB_DOEPCTL_EPTYP ) |
+                  (0 << STM_USB_DOEPCTL_NAKSTS ) |
+                  (1 << STM_USB_DOEPCTL_USBAEP ) |
+                  (STM_USB_DOEPCTL_MPSIZ0_64 << STM_USB_DOEPCTL_MPSIZ));
+
+       stm_usb.doep[0].doepctl = doepctl;
+
+       /* Clear interrupts */
+       stm_usb.diep[0].diepint = 0xffffffff;
+       stm_usb.doep[0].doepint = 0xffffffff;
+
+       ao_usb_dev_ep_out_start(0);
+}
+
+void
+ao_usb_dev_ep0_in(const void *data, uint16_t len)
+{
+       return ao_usb_dev_ep_in(0, data, len);
+}
+
+bool
+ao_usb_dev_ep0_in_busy(void)
+{
+       return false;
+}
+
+uint16_t
+ao_usb_dev_ep0_out(void *data, uint16_t len)
+{
+       return ao_usb_dev_ep_out(0, data, len);
+}
+
+/* Queue IN bytes to EPn */
+void
+ao_usb_dev_ep_in(uint8_t ep, const void *data, uint16_t len)
+{
+       int     l = len;
+
+       while (l > 0) {
+               stm_usb.dfifo[ep].fifo = *((__packed uint32_t *) data);
+               l -= 4;
+               data += 4;
+       }
+
+       /* Set the IN data size */
+       stm_usb.diep[ep].dieptsiz = ((1 << STM_USB_DIEPTSIZ_PKTCNT) |
+                                   (len << STM_USB_DIEPTSIZ_XFRSIZ));
+
+       /* Enable the TX empty interrupt */
+       stm_usb.diepempmsk |= (1 << ep);
+
+       /* Enable the endpoint to queue the packet for transmission */
+       stm_usb.diep[ep].diepctl |= (1 << STM_USB_DIEPCTL_EPENA);
+}
+
+bool
+ao_usb_dev_ep_in_busy(uint8_t ep)
+{
+       (void) ep;
+       return false;
+}
+
+/* Receive OUT bytes from EPn */
+uint16_t
+ao_usb_dev_ep_out(uint8_t ep, void *data, uint16_t len)
+{
+       uint16_t        received;
+       int             l = len;
+       uint32_t        t;
+
+       if (grxstsp_enum() != ep)
+               return 0;
+
+       received = grxstsp_bcnt();
+       if (received > len)
+               received = len;
+
+       while (l >= 4) {
+               *((__packed uint32_t *) data) = stm_usb.dfifo[0].fifo;
+               l -= 4;
+               data += 4;
+       }
+
+       if (l != 0) {
+               t = stm_usb.dfifo[0].fifo;
+               memcpy(data, &t, l);
+       }
+
+       ao_usb_dev_ep_out_start(ep);
+       return received;
+}
+
+void
+ao_usb_dev_set_address(uint8_t address)
+{
+       uint32_t        dcfg;
+
+       dcfg = stm_usb.dcfg;
+
+       dcfg &= ~(STM_USB_DCFG_DAD_MASK << STM_USB_DCFG_DAD);
+       dcfg |= address & STM_USB_DCFG_DAD_MASK;
+       stm_usb.dcfg = dcfg;
+}
+
+static void
+ao_usb_core_reset(void)
+{
+       /* Wait for AHB master IDLE state. */
+       while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_AHBIDL)) == 0)
+               ao_arch_nop();
+
+
+       /* Core soft reset */
+       stm_usb.grstctl |= (1 << STM_USB_GRSTCTL_CSRST);
+
+       /* Wait for reset to complete */
+
+       while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_CSRST)) != 0)
+               ao_arch_nop();
+}
+
+static void
+ao_usb_core_init(void)
+{
+       /* Enable embedded PHY */
+       stm_usb.gusbcfg |= (1 << STM_USB_GUSBCFG_PHYSEL);
+
+       /* Core reset */
+       ao_usb_core_reset();
+
+       /* Deactivate power down */
+       stm_usb.gccfg = (1 << STM_USB_GCCFG_PWRDWN);
+}
+
+static void
+ao_usb_delay(uint32_t ms)
+{
+       AO_TICK_TYPE    now = ao_time();
+       AO_TICK_TYPE    then = now + AO_MS_TO_TICKS(ms);
+
+       while ((int16_t) (then - ao_time()) > 0)
+               ao_arch_nop();
+}
+
+static void
+ao_usb_set_device_mode(void)
+{
+       uint32_t        gusbcfg;
+
+       gusbcfg = stm_usb.gusbcfg;
+       gusbcfg &= ~((1 << STM_USB_GUSBCFG_FHMOD) |
+                    (1 << STM_USB_GUSBCFG_FDMOD));
+       gusbcfg |= (1 << STM_USB_GUSBCFG_FDMOD);
+       stm_usb.gusbcfg = gusbcfg;
+       ao_usb_delay(50);
+}
+
+static void
+ao_usb_device_init(void)
+{
+       /* deactivate vbus sensing */
+       stm_usb.gccfg &= ~(1 << STM_USB_GCCFG_VBDEN);
+
+       /* Force device mode */
+       stm_usb.gotgctl |= ((1 << STM_USB_GOTGCTL_BVALOEN) |
+                           (1 << STM_USB_GOTGCTL_BVALOVAL));
+
+       /* Restart the phy clock */
+       stm_usb.pcgcctl = 0;
+
+       /* Device mode configuration */
+       stm_usb.dcfg |= (STM_USB_DCFG_PFIVL_80 << STM_USB_DCFG_PFIVL);
+
+       /* Set full speed phy */
+       stm_usb.dcfg |= (STM_USB_DCFG_DSPD_FULL_SPEED << STM_USB_DCFG_DSPD);
+
+       /* Flush the fifos */
+       ao_usb_flush_tx_fifo(STM_USB_GRSTCTL_TXFNUM_ALL);
+       ao_usb_flush_rx_fifo();
+
+       /* Clear all pending device interrupts */
+       stm_usb.diepmsk = 0;
+       stm_usb.doepmsk = 0;
+       stm_usb.daint = 0xffffffffUL;
+       stm_usb.daintmsk = 0;
+
+       /* Reset all endpoints */
+       for (int i = 0; i < 6; i++) {
+
+               /* Reset IN endpoint */
+               if (stm_usb.diep[i].diepctl & (1 << STM_USB_DIEPCTL_EPENA))
+                       stm_usb.diep[i].diepctl = ((1 << STM_USB_DIEPCTL_EPDIS) |
+                                                  (1 << STM_USB_DIEPCTL_SNAK));
+               else
+                       stm_usb.diep[i].diepctl = 0;
+               stm_usb.diep[i].dieptsiz = 0;
+               stm_usb.diep[i].diepint = 0xfffffffful;
+
+               /* Reset OUT endpoint */
+               if (stm_usb.doep[i].doepctl & (1 << STM_USB_DOEPCTL_EPENA))
+                       stm_usb.doep[i].doepctl = ((1 << STM_USB_DOEPCTL_EPDIS) |
+                                                  (1 << STM_USB_DOEPCTL_SNAK));
+               else
+                       stm_usb.doep[i].doepctl = 0;
+
+               stm_usb.doep[i].doeptsiz = 0;
+               stm_usb.doep[i].doepint = 0xfffffffful;
+       }
+
+       /* Disable all interrupts */
+       stm_usb.gintmsk = 0;
+
+       /* Clear pending interrupts */
+       stm_usb.gintsts = 0xfffffffful;
+
+       /* Enable core interrupts */
+       stm_usb.gintmsk = ((1 << STM_USB_GINTMSK_WUIM ) |
+                          (0 << STM_USB_GINTMSK_SRQIM ) |
+                          (0 << STM_USB_GINTMSK_DISCINT ) |
+                          (0 << STM_USB_GINTMSK_CIDSCHGM ) |
+                          (0 << STM_USB_GINTMSK_LPMINTM ) |
+                          (0 << STM_USB_GINTMSK_PTXFEM ) |
+                          (0 << STM_USB_GINTMSK_HCIM) |
+                          (0 << STM_USB_GINTMSK_PRTIM ) |
+                          (0 << STM_USB_GINTMSK_RSTDETM ) |
+                          (1 << STM_USB_GINTMSK_IISOOXFRM ) |
+                          (1 << STM_USB_GINTMSK_IISOIXFRM ) |
+                          (1 << STM_USB_GINTMSK_OEPINT) |
+                          (1 << STM_USB_GINTMSK_IEPINT) |
+                          (0 << STM_USB_GINTMSK_EOPFM ) |
+                          (0 << STM_USB_GINTMSK_ISOODRPM ) |
+                          (1 << STM_USB_GINTMSK_ENUMDNEM) |
+                          (1 << STM_USB_GINTMSK_USBRST) |
+                          (1 << STM_USB_GINTMSK_USBSUSPM ) |
+                          (0 << STM_USB_GINTMSK_ESUSPM ) |
+                          (0 << STM_USB_GINTMSK_GONAKEFFM ) |
+                          (0 << STM_USB_GINTMSK_GINAKEFFM ) |
+                          (0 << STM_USB_GINTMSK_NPTXFEM ) |
+                          (0 << STM_USB_GINTMSK_RXFLVLM) |
+                          (0 << STM_USB_GINTMSK_SOFM ) |
+                          (0 << STM_USB_GINTMSK_OTGINT ) |
+                          (0 << STM_USB_GINTMSK_MMISM));
+}
+
+static void
+ao_usb_device_connect(void)
+{
+       /* Enable pull-up/pull-down */
+       stm_usb.dctl &= ~(1 << STM_USB_DCTL_SDIS);
+       ao_usb_delay(20);
+}
+
+#if 0
+static void
+ao_usb_device_disconnect(void)
+{
+       /* Disable pull-up/pull-down */
+       stm_usb.dctl |= (1 << STM_USB_DCTL_SDIS);
+       ao_usb_delay(20);
+}
+#endif
+
+void
+ao_usb_dev_enable(void)
+{
+       ao_arch_block_interrupts();
+
+       /* Configure GPIOs */
+       ao_enable_port(&stm_gpioa);
+#if 0
+       stm_afr_set(&stm_gpioa,  8, STM_AFR_AF10);      /* USB_FS_SOF */
+       stm_afr_set(&stm_gpioa,  9, STM_AFR_AF10);      /* USB_FS_VBUS */
+       stm_afr_set(&stm_gpioa, 10, STM_AFR_AF10);      /* USB_FS_ID */
+#endif
+       stm_afr_set(&stm_gpioa, 11, STM_AFR_AF10);
+       stm_ospeedr_set(&stm_gpioa, 11, STM_OSPEEDR_HIGH);
+       stm_pupdr_set(&stm_gpioa, 11, STM_PUPDR_NONE);
+       stm_afr_set(&stm_gpioa, 12, STM_AFR_AF10);
+       stm_ospeedr_set(&stm_gpioa, 12, STM_OSPEEDR_HIGH);
+       stm_pupdr_set(&stm_gpioa, 12, STM_PUPDR_NONE);
+
+       /* Power on USB */
+       stm_rcc.ahb2enr |= (1 << STM_RCC_AHB2ENR_OTGFSEN);
+
+       /* Route interrupts */
+       stm_nvic_set_priority(STM_ISR_OTG_FS_POS, AO_STM_NVIC_LOW_PRIORITY);
+       stm_nvic_set_enable(STM_ISR_OTG_FS_POS);
+
+       /* Core init */
+       ao_usb_core_init();
+
+       /* Set device mode */
+       ao_usb_set_device_mode();
+
+       /* Reset FIFO allocations */
+       for (int i = 1; i < 16; i++)
+               stm_usb.dieptxf[i-1] = 0x0;
+
+       ao_usb_device_init();
+
+       /* Connect */
+       ao_usb_device_connect();
+}
+
+void
+ao_usb_dev_disable(void)
+{
+       stm_usb.gusbcfg = ((1 << STM_USB_GUSBCFG_FDMOD) |
+                          (0 << STM_USB_GUSBCFG_FHMOD) |
+                          (6 << STM_USB_GUSBCFG_TRDT) |
+                          (0 << STM_USB_GUSBCFG_HNPCAP) |
+                          (0 << STM_USB_GUSBCFG_SRPCAP) |
+                          (1 << STM_USB_GUSBCFG_PHYSEL) |
+                          (0 << STM_USB_GUSBCFG_TOCAL));
+
+       stm_usb.gahbcfg = ((0 << STM_USB_GAHBCFG_PTXFELVL) |
+                          (1 << STM_USB_GAHBCFG_TXFELVL) |
+                          (0 << STM_USB_GAHBCFG_GINTMSK));
+
+       stm_usb.dctl = ((0 << STM_USB_DCTL_POPRGDNE) |
+                       (1 << STM_USB_DCTL_SDIS));
+
+       stm_rcc.ahb2enr &= ~(1 << STM_RCC_AHB2ENR_OTGFSEN);
+}
+
+void
+stm_otg_fs_isr(void)
+{
+       uint32_t        gintsts = stm_usb.gintsts;
+       uint8_t         ep0_receive = 0;
+       uint32_t        out_interrupt = 0;
+       uint32_t        in_interrupt = 0;
+
+       /* Clear all received interrupts */
+       stm_usb.gintsts = gintsts;
+
+       if (gintsts & (1 << STM_USB_GINTSTS_USBRST)) {
+               ep0_receive |= AO_USB_EP0_GOT_RESET;
+       }
+
+       if (gintsts & (1 << STM_USB_GINTSTS_ENUMDNE)) {
+               ao_usb_enum_done();
+       }
+
+       if (gintsts & ((1 << STM_USB_GINTSTS_OEPINT) |
+                      (1 << STM_USB_GINTSTS_IEPINT)))
+       {
+               uint32_t        daint = stm_usb.daint;
+               uint32_t        oepint = (daint >> STM_USB_DAINT_OEPINT) & STM_USB_DAINT_OEPINT_MASK;
+               uint32_t        iepint = (daint >> STM_USB_DAINT_IEPINT) & STM_USB_DAINT_IEPINT_MASK;
+
+               for (int ep = 0; ep < 6; ep++) {
+                       if (gintsts & (1 << STM_USB_GINTSTS_OEPINT)) {
+                               if (oepint & (1 << ep)) {
+                                       uint32_t        doepint = stm_usb.doep[ep].doepint;
+
+                                       stm_usb.doep[ep].doepint = doepint;
+                                       if (doepint & (1 << STM_USB_DOEPINT_XFRC)) {
+                                               if (ep == 0)
+                                                       ep0_receive |= AO_USB_EP0_GOT_SETUP;
+                                               else
+                                                       out_interrupt |= (1 << ep);
+                                       }
+                                       grxstsp = stm_usb.grxstsp;
+                               }
+                       }
+
+                       if (gintsts & (1 << STM_USB_GINTSTS_IEPINT)) {
+                               if (iepint & (1 << ep)) {
+                                       uint32_t        diepint = stm_usb.diep[ep].diepint;
+
+                                       stm_usb.diep[ep].diepint = diepint;
+                                       if (diepint & (1 << STM_USB_DIEPINT_XFRC)) {
+                                               if (ep == 0)
+                                                       ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+                                               else
+                                                       in_interrupt |= (1 << ep);
+                                       }
+                               }
+                       }
+               }
+       } else {
+               grxstsp = 0;
+       }
+
+       if (ep0_receive)
+               ao_usb_ep0_interrupt(ep0_receive);
+
+       if (out_interrupt)
+               ao_usb_out_interrupt(out_interrupt);
+
+       if (in_interrupt)
+               ao_usb_in_interrupt(in_interrupt);
+}
+
+/*
+
+  running                              before plugging in              at first packet
+  gotgctl = 0x04cd0000,                        0x04c10000,                     0x04cd0000              *************
+
+      CURMOD = 0
+      OTGVER = 0
+      BSVLD = 1                                BSVLD = 0
+      ASVLD = 1                                ASVLD = 0
+      DBCT = 0
+      CIDSTS = 1
+
+  gotgint = 0x00100000,                0x00100000,                     0x00100000
+
+      IDCHNG = 1
+
+  gahbcfg = 0x1,                       0x1,                            0x00000001
+
+      TXFELVL = 0      trigger half empty
+      GINTMSK = 1      interrupts enabled
+
+  gusbcfg = 0x40001840,                0x40001440                      0x40001840              *************
+
+      FDMOD = 1        force device mode
+      FHMOD = 0
+      TRDT = 6                         5                               6
+      HNPCAP = 0
+      SRPCAP = 0
+      PHYSEL = 1
+      TOCAL = 0
+
+  grstctl = 0x80000040,                0x80000000                      0x80000400              ***********
+
+      AHBIDL = 1
+      TXFNUM = 1                               TXFNUM = 0              TXFNUM = 0x20 (flush all)
+      TXFFLSH = 0
+      RXFFLSH = 0
+      FCRST = 0
+      PSRST = 0
+      CSRST = 0
+
+  gintsts = 0x0480b43a,                0x04008022                      0x04888438              ***********
+
+      WKUPINT = 0                              0
+      SRQINT = 0                               0
+      DISCINT = 0                              0
+      CIDSCHG = 0                              0
+      LPMINT = 0                               0
+      PTXFE = 1                                        PTXFE = 1               PTXFE = 1
+      HCINT = 0
+      HPRTINT = 0
+      RSTDET = 1                               RSTDET = 0              RSTDET = 1
+      IPXFER = 0
+      IISOIXFR = 0
+      OEPINT = 0                                                       OEPINT = 1
+      IEPINT = 0
+      EOPF = 1                                 EOPF = 1                EOPF = 1
+      ISOODRP = 0
+      ENUMDNE = 1
+      USBRST = 1
+      USBSUSP = 0
+      ESUSP = 1                                                                ESUSP = 1
+      GONAKEFF = 0
+      GINAKEFF = 0
+      NPTXFE = 1                               NPTXFE = 1              NPTXFE = 1
+      RXFLVL = 1                                                       RXFLVL = 1
+      SOF = 1                                                          SOF = 1
+      OTGINT = 0
+      MMIS = 1                                 MMIS = 1                MMIS = 0
+      CMOD = 0
+
+  gintmsk = 0xc03c3814,                0xc03c3814,
+
+      WUIM = 1
+      SRQIM = 1
+      DISCINT = 0
+      CIDSCHGM = 0
+      LPMINTM = 0
+      PTXFEM = 0
+      HCIM = 0
+      PRTIM = 0
+      RSTDETM = 0
+      IISOOXFRM = 1
+      IISOIXFRM = 1
+      OEPINT = 1
+      IEPINT = 1
+      EOPFM = 0
+      ISOODRPM = 0
+      ENUMDNEM = 1
+      USBRST = 1
+      USBSUSPM = 1
+      ESUSPM =0
+      GONAKEFFM = 0
+      GINAKEFFM = 0
+      NPTXFEM = 0
+      RXFLVLM = 1
+      SOFM = 0
+      OTGINT = 1
+      MMISM = 0
+
+  grxstsr = 0xac0080,                  0x0                             0x14c0080       ***************
+
+      STSPHST = 0                                                      STSPHST = 0
+      FRMNUM = 5                                                       FRMNUM = 10
+      PKTSTS = 6       -- SETUP data packet                            PKTSTS = 6 -- SETUP data packet
+      DPID = 0                                                         DPID = 0
+      BCNT = 8                                                         BCNT = 8
+      EPNUM = 0                                                                EPNUM = 0
+
+  grxstsp = 0xac0080,                  0x0                             0x14c0080
+
+      (same)
+
+  grxfsiz = 0x80,                      0x80                            0x80
+
+      RXFD = 128       512 bytes
+
+  dieptxf0 = 0x00400080,               0x00400080                      0x00400080
+
+      TX0FD = 64       256 bytes
+      TX0FSA = 0x80
+
+  gccfg = 0x21fff0,                    0x21fff0                        0x21fff0
+
+      VBDEN = 1
+      SDEN = 0
+      PDEN = 0
+      DCDEN = 0
+      BCDEN = 0
+      PWRDN = 1
+      PS2DET = 0
+      SDET = 0
+      PDET = 0
+      DCDET = 0
+
+  cid = 0x2000,                                0x2000                          0x2000
+
+      PRODUCT_ID = 0x2000
+
+  glpmcfg = 0x0,                       0x0                             0x0
+
+      ENBESL = 0
+      LPMRCNTTST = 0
+      SNDLPM = 0
+      LPMRCNT = 0
+      LPMCHIDX = 0
+      L1RSMOK = 0
+      SLPSTS = 0
+      LPMRSP = 0
+      L1DSEN = 0
+      BESLTHRS = 0
+      L1SSEN = 0
+      REMWAKE = 0
+      BESL = 0
+      LPMACK = 0
+      LPMEN = 0
+
+  dieptxf = {0x8000c0, 0x0, 0x0, 0x0, 0x0},    {0x8000c0, 0x0, 0x0, 0x0, 0x0}, {0x8000c0, 0x0, 0x0, 0x0, 0x0}, 
+
+      INEXPTXFD 0 = 0x80       512 bytes
+      INEXPTXSA 0 = 0xc0
+
+  dcfg = 0x82000b3,                    0x8200003,                      0x8200003
+
+      ERRATIM = 0
+      PFIVL = 0
+      DAD = 0xb                                DAD = 0x0                       DAD = 0
+      NZLSOHSK = 0
+      DSPD = 3 Full speed USB 1.1
+
+  dctl = 0x0,                          0x0                             0x0
+
+      DSBESLRJCT = 0
+      POPRGDNE = 0
+      CGONAK = 0
+      SGONAK = 0
+      CGINAK = 0
+      SGINAK = 0
+      TCTL = 0
+      GONSTS = 0
+      GINSTS = 0
+      SDIS = 0
+      RWUSIG = 0
+
+  dsts = 0x0043ff06,                   0x00000006                      0x00400c06
+
+      DEVLNSTS = 1     (D+ low, D- high)
+      FNSOF = 0x3ff                                                    FNSOF = 0xc
+      EERR = 0
+      ENUMSPD = 3      Full speed                                      ENUMSPD = 3
+      SUSPSTS = 0                                                      SUSPSTS = 0
+
+  diepmsk = 0xb,                       0x0                             0xb
+
+      NAKM = 0
+      TXFURM = 0
+      INEPNEM = 0
+      INEPNMM = 0
+      ITTXFEMSK = 0
+      TOM = 1
+      EPDM = 1
+      XFRCM = 1
+
+  doepmsk = 0x2b,                      0x0                             0x2b
+
+      NYETMSK = 0
+      NAKMSK = 0
+      BERRM =0
+      OUTPKTERRM = 0
+      STSPHSRXM = 1
+      OTEPDM = 0
+      STUPM = 1
+      EPDM = 1
+      XFRCM = 1
+
+  daint = 0x0,                                 0x0                     0x10000
+
+  daintmsk = 0x30003,                  0x0                     0x10001
+
+      OEPM = 0x3       endpoints 0 and 1                       OEPM = 0x1      endpoint 0
+      IEPM = 0x3       endpoints 0 and 1                       IEPM = 0x1      endpoint 0
+
+  dvbusdis = 0x17d7,                   0x17d7                  0x17d7
+
+      VBUSDT = 0x17d7  reset value
+
+  dvbuspulse = 0x5b8,                  0x5b8                   0x5b8
+
+      DVBUSP = 0x5b8   reset value
+
+  diepempmsk = 0x0,                    0x0                     0x0
+
+      INEPTXFEM = 0            no endpoints
+
+  diep = {{
+      diepctl = 0x28000,
+
+         EPENA = 0
+         EPDIS = 0
+         SNAK = 0
+         CNAK = 0
+         TXFNUM = 0
+         STALL = 0
+         EPTYP = 0
+         NAKSTS = 1
+         USBAEP = 1
+         MPSIZ = 0             64 bytes
+
+      diepint = 0x20c0,
+
+         NAK = 1
+         PKTDRPSTS = 0
+         TXFIFOUDRN = 0
+         TXFE = 1
+         INEPNE = 1
+         ITTXFE = 0
+         TOC = 0
+         EPDISD = 0
+         XFRC = 0
+
+      dieptsiz = 0x0,
+
+         PKTCNT = 0
+         XFRSIZ = 0
+
+      dtxfsts = 0x40,
+
+         INEPTFSAV = 0x40      256 bytes available
+
+    }, {
+      diepctl = 0x00490040,
+
+         EPENA = 0
+         EPDIS = 0
+         SODDFRM = 0
+         SD0PID  = 0
+         SNAK = 0
+         CNAK = 0
+         TXFNUM = 1
+         STALL = 0
+         EPTYP = 2             bulk
+         NAKSTS = 0
+         EONUM = 1
+         USBAEP = 0
+         MPSIZ = 64    256 bytes
+
+      diepint = 0x2090,
+
+         NAK = 1
+         PKTDRPSTS = 0
+         TXFIFOUDRN = 0
+         TXFE = 1
+         INEPNE = 0
+         INPENM = 0
+         ITTXFE = 1
+         TOC = 0
+         EPDISD = 0
+         XFRC = 0
+
+      dieptsiz = 0x0,
+
+         MCNT = 0
+         PKTCNT = 0
+         XFRSIZ = 0
+
+      dtxfsts = 0x80,
+
+         INEPTFSAV = 0x80              512 bytes available
+
+    }, {
+      diepctl = 0x0,
+      pad_04 = 0x0,
+      diepint = 0x80,
+      pad_0c = 0x0,
+      dieptsiz = 0x0,
+      pad_14 = 0x43425355,
+      dtxfsts = 0x40,
+      pad_1c = 0x400000
+    }, {
+      diepctl = 0x0,
+      pad_04 = 0x0,
+      diepint = 0x80,
+      pad_0c = 0x0,
+      dieptsiz = 0x0,
+      pad_14 = 0x43425355,
+      dtxfsts = 0x40,
+      pad_1c = 0x400000
+    }, {
+      diepctl = 0x0,
+      pad_04 = 0x0,
+      diepint = 0x80,
+      pad_0c = 0x0,
+      dieptsiz = 0x0,
+      pad_14 = 0x43425355,
+      dtxfsts = 0x40,
+      pad_1c = 0x400000
+    }, {
+      diepctl = 0x0,
+      pad_04 = 0x0,
+      diepint = 0x80,
+      pad_0c = 0x0,
+      dieptsiz = 0x0,
+      pad_14 = 0x43425355,
+      dtxfsts = 0x40,
+      pad_1c = 0x400000
+    }},
+
+  doep = {{
+      doepctl = 0x80028000,            0x00008000,                     0x28000
+
+         EPENA = 1                         EPENA = 0                   EPENA = 0
+         EPDIS = 0
+         SNAK =0
+         CNAK = 0
+         STALL = 0
+         SNPM = 0
+         EPTYP = 0
+         NAKSTS = 1                        NAKSTS = 0                  NAKSTS = 1
+         USPAEP = 1                                                    USPAEP = 1
+         MPSIZ = 0             64 bytes                                MPSIZ = 0
+
+      doepint = 0x8010,                0x0                             0x8008
+
+         NYET = 0
+         NAK = 0
+         BERR = 0
+         OUTPKTERR = 0
+         STSPHSRX = 0
+         OTEPDIS = 1
+         STUP = 0                                                      STUP = 1
+         EPDISD = 0
+         XFRC = 0
+
+      doeptsiz = 0x38,                         0x0                             0x20080008
+
+         STPCNT = 0    1 packet                                        STPCNT = 1
+         PKTCNT = 0                                                    PKTCNT = 1
+         XFRSIZ = 0x38 56 bytes (64 - 8)                               XFRSIZ = 8
+
+    }, {
+      doepctl = 0x800b0040,
+
+         EPENA = 1
+         EPDIS = 0
+         SD1PID = 0
+         SD0PID = 0
+         SNAK = 0
+         CNAK = 0
+         STALL = 0
+         SNPM =0
+         EPTYP = 2             Bulk
+         NAKSTS = 1
+         EONUM = 1
+         USBAEP = 0
+         MPSIZ = 0x40  64 bytes
+
+      doepint = 0x0,
+      doeptsiz = 0x21,
+
+         RXDPID = 0
+         PKTCNT = 0
+         XFRSIZ = 0x21    33 bytes ?
+
+    }, {
+      doepctl = 0x0,
+      pad_04 = 0x0,
+      doepint = 0x0,
+      pad_0c = 0x0,
+      doeptsiz = 0x0,
+      pad_14 = 0x43425355,
+      pad_18 = 0x40,
+      pad_1c = 0x400000
+    }, {
+      doepctl = 0x0,
+      pad_04 = 0x0,
+      doepint = 0x0,
+      pad_0c = 0x0,
+      doeptsiz = 0x0,
+      pad_14 = 0x43425355,
+      pad_18 = 0x40,
+      pad_1c = 0x400000
+    }, {
+      doepctl = 0x0,
+      pad_04 = 0x0,
+      doepint = 0x0,
+      pad_0c = 0x0,
+      doeptsiz = 0x0,
+      pad_14 = 0x43425355,
+      pad_18 = 0x40,
+      pad_1c = 0x400000
+    }, {
+      doepctl = 0x0,
+      pad_04 = 0x0,
+      doepint = 0x0,
+      pad_0c = 0x0,
+      doeptsiz = 0x0,
+      pad_14 = 0x43425355,
+      pad_18 = 0x40,
+      pad_1c = 0x400000
+    }},
+
+  pcgcctl = 0x0,               0x0,                    0x0
+
+      SUSP = 0
+      PHYSLEEP = 0
+      ENL1GTG = 0
+      PHYSUSP = 0
+      GATEHCLK = 0
+      STPPCLK = 0
+
+  dfifo = {{
+       fifo =                                          0x1000680,
+                                                               
+
+      Clock configuration:
+
+$5 = {
+  cr = 0x0f077d83, 
+
+       PLLI2SRDY = 1
+       PLLI2SON = 1
+       PLLRDY = 1
+       PLLON = 1
+       CSSON = 0
+       HSEBYP = 1
+       HSERDY = 1
+       HSEON = 1
+       HSICAL = 0x7d
+       HSITRIM = 0x10
+       HSIRDY = 1
+       HSION = 1
+
+  pllcfgr = 0x27403208,
+
+       PLLR = 2
+       PLLQ = 7
+       PLLSRC = 1      HSE
+       PLLP = 0        2               
+       PLLN = 0xc8     200
+       PLLM = 8
+
+               clk_pllin = 8000000 / 8 = 1000000
+               vco = 1000000 * 200 = 200000000
+               clk_pll1p = 200000000 / 2 = 100000000 (100MHz)
+               clk_pll1q = 200000000 / 7 = ???
+               clk_pll1r = 200000000 / 2 = 100000000 (100MHz)
+
+  cfgr = 0x0000100a, 
+  cir = 0x00000000, 
+  ahb1rstr = 0x0, 
+  ahb2rstr = 0x0, 
+  ahb3rstr = 0x0, 
+  pad_1c = 0x0, 
+  apb1rstr = 0x0, 
+  apb2rstr = 0x0, 
+  pad_28 = 0x0, 
+  pad_2c = 0x0, 
+  ahb1enr = 0x40107f, 
+  ahb2enr = 0x80, 
+  ahbdnr = 0x3, 
+  pad_3c = 0x0, 
+  apb1enr = 0x11000410, 
+  apb2enr = 0xc800, 
+  pad_48 = 0x0, 
+  pad_4c = 0x0, 
+  ahb1lpenr = 0x6390ff, 
+  ahb2lpenr = 0xd0, 
+  ahb3lpenr = 0x3, 
+  pad_5c = 0x0, 
+  apb1lpenr = 0xfffecfff, 
+  apb2lpenr = 0x357f9f3, 
+  pad_68 = 0x0, 
+  pad_6c = 0x0, 
+  bdcr = 0x8200, 
+  csr = 0x1e000003, 
+  pad_78 = 0x0, 
+  pad_7c = 0x0, 
+  sscgr = 0x0, 
+  plli2scfgr = 0x44003008, 
+
+       PLLI2SR = 4
+       PLLI2SQ = 4
+       PLLI2SSRC = 0   HSE (due to PLLSRC)
+       PLLI2SN = 0xc0  192
+       PLLI2SM = 8
+
+               clk_plli2sin = 8000000 / 8 = 1000000
+               vcoi2s = 1000000 * 192 = 192000000
+               ck_pl2q = 192000000 / 4 = 48000000
+               ck_pl2r = 192000000 / 4 = 48000000
+
+  pad_88 = 0x0, 
+  dckcfgr = 0x0, 
+
+
+  ckgatenr = 0x0,
+
+       All clock gates enabled
+
+  dckcfgr2 = 0x08000000
+
+       LPTIMER1SEL = 0         APB
+       CKSDIOSEL = 0           CK_48MHz
+       CK48MSEL = 1            PLLI2S_Q
+       I2CFMP1SEL = 0          APB
+}
+      
+
+
+*/