altos/stm32f1: More stm32f103 work
authorKeith Packard <keithp@keithp.com>
Sun, 19 Feb 2023 23:44:32 +0000 (15:44 -0800)
committerKeith Packard <keithp@keithp.com>
Thu, 1 Feb 2024 01:50:19 +0000 (17:50 -0800)
Serial driver works
Interrupts work

Signed-off-by: Keith Packard <keithp@keithp.com>
src/stm32f1/Makefile-raw.defs
src/stm32f1/altos-ram.ld
src/stm32f1/ao_arch.h
src/stm32f1/ao_arch_funcs.h
src/stm32f1/ao_clock.c
src/stm32f1/ao_exti.h [new file with mode: 0644]
src/stm32f1/ao_interrupt.c
src/stm32f1/ao_serial_stm.c [new file with mode: 0644]
src/stm32f1/ao_timer.c
src/stm32f1/ao_usb_stm.c [new file with mode: 0644]
src/stm32f1/stm32f1.h

index 91d30bd50dc68494e1bcf2fb5cbda51ad3c1ba5f..a3894ca89212a4210fb7fcbbaac9fcbf47a0be19 100644 (file)
@@ -8,6 +8,6 @@ LOADER=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
 MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin
 FLASH_ADDR=0x08000000
 
-LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f1 -Taltos-ram.ld -n
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f1 -Taltos-raw.ld -n
 
 .DEFAULT_GOAL=all
index d409d2cb49ddec0e619d73668b661d6e73230c89..278d940126d605fbd03dd63c108177c63087e97e 100644 (file)
@@ -1,7 +1,7 @@
 __flash = 0x20000000;
 __flash_size = 12k;
 __ram   = 0x20003000;
-__ram_size = 4k;
+__ram_size = 8k;
 __stack_size = 512;
 
 INCLUDE registers.ld
index de5f2159e65f4a0dc938e629e5aaeeacc47dc61b..743651330e96cad130890e3a162516837fc980bb 100644 (file)
@@ -55,4 +55,7 @@
 #define AO_STM_NVIC_LOW_PRIORITY       0xC0
 #define AO_STM_NVIC_CLOCK_PRIORITY     0xf0
 
+#define AO_PCLK1       AO_APB1CLK
+#define AO_PCLK2       AO_APB2CLK
+
 #endif /* _AO_ARCH_H_ */
index db3cf9e40c6a9a86bb5ac4869c879104f8109292..35e9374fc66ffd9fdf79dfd15f05d030170f1387 100644 (file)
@@ -71,4 +71,232 @@ ao_enable_output(struct stm_gpio *port, int bit, uint8_t v)
                      STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL);
 }
 
+#if USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_SW_FLOW
+#define HAS_SERIAL_SW_FLOW 1
+#else
+#define HAS_SERIAL_SW_FLOW 0
+#endif
+
+#if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_FLOW && !USE_SERIAL_3_SW_FLOW
+#define HAS_SERIAL_HW_FLOW 1
+#else
+#define HAS_SERIAL_HW_FLOW 0
+#endif
+
+/* ao_serial_stm.c */
+struct ao_stm_usart {
+       struct ao_fifo          rx_fifo;
+       struct ao_fifo          tx_fifo;
+       struct stm_usart        *reg;
+       uint8_t                 tx_running;
+       uint8_t                 draining;
+#if HAS_SERIAL_SW_FLOW
+       /* RTS - 0 if we have FIFO space, 1 if not
+        * CTS - 0 if we can send, 0 if not
+        */
+       struct stm_gpio         *gpio_rts;
+       struct stm_gpio         *gpio_cts;
+       uint8_t                 pin_rts;
+       uint8_t                 pin_cts;
+       uint8_t                 rts;
+#endif
+};
+
+void
+ao_debug_out(char c);
+
+#if HAS_SERIAL_1
+extern struct ao_stm_usart     ao_stm_usart1;
+#endif
+
+#if HAS_SERIAL_2
+extern struct ao_stm_usart     ao_stm_usart2;
+#endif
+
+#if HAS_SERIAL_3
+extern struct ao_stm_usart     ao_stm_usart3;
+#endif
+
+#define ARM_PUSH32(stack, val) (*(--(stack)) = (val))
+
+typedef uint32_t       ao_arch_irq_t;
+
+static inline void
+ao_arch_block_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+       asm("msr basepri,%0" : : "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+       asm("cpsid i");
+#endif
+}
+
+static inline void
+ao_arch_release_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+       asm("msr basepri,%0" : : "r" (0x0));
+#else
+       asm("cpsie i");
+#endif
+}
+
+static inline uint32_t
+ao_arch_irqsave(void) {
+       uint32_t        val;
+#ifdef AO_NONMASK_INTERRUPTS
+       asm("mrs %0,basepri" : "=r" (val));
+#else
+       asm("mrs %0,primask" : "=r" (val));
+#endif
+       ao_arch_block_interrupts();
+       return val;
+}
+
+static inline void
+ao_arch_irqrestore(uint32_t basepri) {
+#ifdef AO_NONMASK_INTERRUPTS
+       asm("msr basepri,%0" : : "r" (basepri));
+#else
+       asm("msr primask,%0" : : "r" (basepri));
+#endif
+}
+
+static inline void
+ao_arch_memory_barrier(void) {
+       asm volatile("" ::: "memory");
+}
+
+static inline void
+ao_arch_irq_check(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+       uint32_t        basepri;
+       asm("mrs %0,basepri" : "=r" (basepri));
+       if (basepri == 0)
+               ao_panic(AO_PANIC_IRQ);
+#else
+       uint32_t        primask;
+       asm("mrs %0,primask" : "=r" (primask));
+       if ((primask & 1) == 0)
+               ao_panic(AO_PANIC_IRQ);
+#endif
+}
+
+#if HAS_TASK
+static inline void
+ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start)
+{
+       uint32_t        a = (uint32_t) start;
+       int             i;
+
+       /* Return address (goes into LR) */
+       ARM_PUSH32(sp, a);
+
+       /* Clear register values r0-r12 */
+       i = 13;
+       while (i--)
+               ARM_PUSH32(sp, 0);
+
+       /* APSR */
+       ARM_PUSH32(sp, 0);
+
+       /* BASEPRI with interrupts enabled */
+       ARM_PUSH32(sp, 0);
+
+       task->sp32 = sp;
+}
+
+static inline void ao_arch_save_regs(void) {
+       /* Save general registers */
+       asm("push {r0-r12,lr}\n");
+
+       /* Save APSR */
+       asm("mrs r0,apsr");
+       asm("push {r0}");
+
+#ifdef AO_NONMASK_INTERRUPTS
+       /* Save BASEPRI */
+       asm("mrs r0,basepri");
+#else
+       /* Save PRIMASK */
+       asm("mrs r0,primask");
+#endif
+       asm("push {r0}");
+}
+
+static inline void ao_arch_save_stack(void) {
+       uint32_t        *sp;
+       asm("mov %0,sp" : "=&r" (sp) );
+       ao_cur_task->sp32 = (sp);
+}
+
+static inline void ao_arch_restore_stack(void) {
+       /* Switch stacks */
+       asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
+
+#ifdef AO_NONMASK_INTERRUPTS
+       /* Restore BASEPRI */
+       asm("pop {r0}");
+       asm("msr basepri,r0");
+#else
+       /* Restore PRIMASK */
+       asm("pop {r0}");
+       asm("msr primask,r0");
+#endif
+
+       /* Restore APSR */
+       asm("pop {r0}");
+       asm("msr apsr_nczvq,r0");
+
+       /* Restore general registers */
+       asm("pop {r0-r12,lr}\n");
+
+       /* Return to calling function */
+       asm("bx lr");
+}
+#define HAS_ARCH_START_SCHEDULER       1
+
+static inline void ao_arch_start_scheduler(void) {
+       uint32_t        sp;
+       uint32_t        control;
+
+       asm("mrs %0,msp" : "=&r" (sp));
+       asm("msr psp,%0" : : "r" (sp));
+       asm("mrs %0,control" : "=r" (control));
+       control |= (1 << 1);
+       asm("msr control,%0" : : "r" (control));
+       asm("isb");
+}
+
+#define ao_arch_isr_stack()
+
+#endif /* HAS_TASK */
+
+static inline void
+ao_arch_wait_interrupt(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+       asm(
+           "dsb\n"                     /* Serialize data */
+           "isb\n"                     /* Serialize instructions */
+           "cpsid i\n"                 /* Block all interrupts */
+           "msr basepri,%0\n"          /* Allow all interrupts through basepri */
+           "wfi\n"                     /* Wait for an interrupt */
+           "cpsie i\n"                 /* Allow all interrupts */
+           "msr basepri,%1\n"          /* Block interrupts through basepri */
+           : : "r" (0), "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+       asm("\twfi\n");
+       ao_arch_release_interrupts();
+       ao_arch_block_interrupts();
+#endif
+}
+
+#define ao_arch_critical(b) do {                       \
+               uint32_t __mask = ao_arch_irqsave();    \
+               do { b } while (0);                     \
+               ao_arch_irqrestore(__mask);             \
+       } while (0)
+
+#define ao_arch_reboot() \
+       (stm_scb.aircr = ((STM_SCB_AIRCR_VECTKEY_KEY << STM_SCB_AIRCR_VECTKEY) | \
+                         (1 << STM_SCB_AIRCR_SYSRESETREQ)))
+
 #endif /* _AO_ARCH_FUNCS_H_ */
index 1ce32a90343c8838c5df9435d3bd0069abaf6771..c203b01286f6074ff03b3637097e450c41538d65 100644 (file)
@@ -69,16 +69,12 @@ ao_clock_init(void)
                asm("nop");
 #endif
 
-#if 0
-       /* Set flash latency to tolerate 32MHz SYSCLK  -> 1 wait state */
+       /* Set flash latency to tolerate 72MHz SYSCLK  -> 2 wait states */
 
        /* Enable 64-bit access and prefetch */
-       stm_flash.acr |= (1 << STM_FLASH_ACR_ACC64);
-       stm_flash.acr |= (1 << STM_FLASH_ACR_PRFEN);
-
-       /* Enable 1 wait state so the CPU can run at 32MHz */
-       stm_flash.acr |= (1 << STM_FLASH_ACR_LATENCY);
-#endif
+       stm_flash.acr = ((1 << STM_FLASH_ACR_PRFTBE) |
+                        (0 << STM_FLASH_ACR_HLFCYA) |
+                        (STM_FLASH_ACR_LATENCY_2 << STM_FLASH_ACR_LATENCY));
 
        /* Enable power interface clock */
        stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
diff --git a/src/stm32f1/ao_exti.h b/src/stm32f1/ao_exti.h
new file mode 100644 (file)
index 0000000..1d19a48
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+#ifndef _AO_EXTI_H_
+#define _AO_EXTI_H_
+
+#define AO_EXTI_MODE_RISING    1
+#define AO_EXTI_MODE_FALLING   2
+#define AO_EXTI_MODE_PULL_NONE 0
+#define AO_EXTI_MODE_PULL_UP   4
+#define AO_EXTI_MODE_PULL_DOWN 8
+#define AO_EXTI_PRIORITY_LOW   16
+#define AO_EXTI_PRIORITY_MED   0
+#define AO_EXTI_PRIORITY_HIGH  32
+#define AO_EXTI_PIN_NOCONFIGURE        64
+
+void
+ao_exti_setup(struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)(void));
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode);
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)(void));
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_init(void);
+
+#endif /* _AO_EXTI_H_ */
index 831a2c2a1759c93e96d0c14296402541844ede68..2259aefc2619a6d4a84195047c0dcf06383f7ce4 100644 (file)
@@ -27,7 +27,7 @@ extern void main(void);
 
 void stm_halt_isr(void)
 {
-//     ao_panic(AO_PANIC_CRASH);
+       ao_panic(AO_PANIC_CRASH);
 }
 
 void stm_ignore_isr(void)
@@ -212,5 +212,5 @@ void __attribute__((constructor)) ao_setup(void) {
        }
 #endif
        /* Set interrupt vector table offset */
-       stm_nvic.vto = (uint32_t) &__interrupt_vector;
+       stm_scb.vtor = (uint32_t) &__interrupt_vector;
 }
diff --git a/src/stm32f1/ao_serial_stm.c b/src/stm32f1/ao_serial_stm.c
new file mode 100644 (file)
index 0000000..f5d5108
--- /dev/null
@@ -0,0 +1,576 @@
+/*
+ * Copyright © 2012 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.h>
+#include <ao_exti.h>
+
+void
+ao_debug_out(char c)
+{
+       if (c == '\n')
+               ao_debug_out('\r');
+       while (!(stm_usart1.sr & (1 << STM_USART_SR_TXE)));
+       stm_usart1.dr = c;
+}
+
+static volatile uint8_t tx;
+
+static int
+_ao_usart_tx_start(struct ao_stm_usart *usart)
+{
+       if (!ao_fifo_empty(usart->tx_fifo)) {
+#if HAS_SERIAL_SW_FLOW
+               if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts) == 1) {
+                       ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+                       return 0;
+               }
+#endif
+               if (usart->reg->sr & (1 << STM_USART_SR_TXE))
+               {
+                       usart->tx_running = 1;
+                       usart->reg->cr1 |= (1 << STM_USART_CR1_TXEIE) | (1 << STM_USART_CR1_TCIE);
+                       ao_fifo_remove(usart->tx_fifo, tx);
+                       usart->reg->dr = tx;
+                       ao_wakeup(&usart->tx_fifo);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+_ao_usart_cts(struct ao_stm_usart *usart)
+{
+       if (_ao_usart_tx_start(usart))
+               ao_exti_disable(usart->gpio_cts, usart->pin_cts);
+}
+#endif
+
+static void
+_ao_usart_rx(struct ao_stm_usart *usart, int is_stdin)
+{
+       if (usart->reg->sr & (1 << STM_USART_SR_RXNE)) {
+               if (!ao_fifo_full(usart->rx_fifo)) {
+                       ao_fifo_insert(usart->rx_fifo, (char) usart->reg->dr);
+                       ao_wakeup(&usart->rx_fifo);
+                       if (is_stdin)
+                               ao_wakeup(&ao_stdin_ready);
+#if HAS_SERIAL_SW_FLOW
+                       /* If the fifo is nearly full, turn off RTS and wait
+                        * for it to drain a bunch
+                        */
+                       if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
+                               ao_gpio_set(usart->gpio_rts, usart->pin_rts, 1);
+                               usart->rts = 0;
+                       }
+#endif
+               } else {
+                       usart->reg->cr1 &= ~(1UL << STM_USART_CR1_RXNEIE);
+               }
+       }
+}
+
+static void
+ao_usart_isr(struct ao_stm_usart *usart, int is_stdin)
+{
+       _ao_usart_rx(usart, is_stdin);
+
+       if (!_ao_usart_tx_start(usart))
+               usart->reg->cr1 &= ~(1UL << STM_USART_CR1_TXEIE);
+
+       if (usart->reg->sr & (1 << STM_USART_SR_TC)) {
+               usart->tx_running = 0;
+               usart->reg->cr1 &= ~(1UL << STM_USART_CR1_TCIE);
+               if (usart->draining) {
+                       usart->draining = 0;
+                       ao_wakeup(&usart->tx_fifo);
+               }
+       }
+}
+
+static int
+_ao_usart_pollchar(struct ao_stm_usart *usart)
+{
+       int     c;
+
+       if (ao_fifo_empty(usart->rx_fifo))
+               c = AO_READ_AGAIN;
+       else {
+               uint8_t u;
+               ao_fifo_remove(usart->rx_fifo,u);
+               if ((usart->reg->cr1 & (1 << STM_USART_CR1_RXNEIE)) == 0) {
+                       if (ao_fifo_barely(usart->rx_fifo))
+                               usart->reg->cr1 |= (1 << STM_USART_CR1_RXNEIE);
+               }
+#if HAS_SERIAL_SW_FLOW
+               /* If we've cleared RTS, check if there's space now and turn it back on */
+               if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
+                       ao_gpio_set(usart->gpio_rts, usart->pin_rts, 0);
+                       usart->rts = 1;
+               }
+#endif
+               c = u;
+       }
+       return c;
+}
+
+static char
+ao_usart_getchar(struct ao_stm_usart *usart)
+{
+       int c;
+       ao_arch_block_interrupts();
+       while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
+               ao_sleep(&usart->rx_fifo);
+       ao_arch_release_interrupts();
+       return (char) c;
+}
+
+static inline uint8_t
+_ao_usart_sleep_for(struct ao_stm_usart *usart, AO_TICK_TYPE timeout)
+{
+       return ao_sleep_for(&usart->rx_fifo, timeout);
+}
+
+static void
+ao_usart_putchar(struct ao_stm_usart *usart, char c)
+{
+       ao_arch_block_interrupts();
+       while (ao_fifo_full(usart->tx_fifo))
+               ao_sleep(&usart->tx_fifo);
+       ao_fifo_insert(usart->tx_fifo, c);
+       _ao_usart_tx_start(usart);
+       ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_drain(struct ao_stm_usart *usart)
+{
+       ao_arch_block_interrupts();
+       while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) {
+               usart->draining = 1;
+               ao_sleep(&usart->tx_fifo);
+       }
+       ao_arch_release_interrupts();
+}
+
+static const struct {
+       uint32_t baud;
+} ao_usart_speeds[] = {
+       [AO_SERIAL_SPEED_4800] = {
+               4800
+       },
+       [AO_SERIAL_SPEED_9600] = {
+               9600
+       },
+       [AO_SERIAL_SPEED_19200] = {
+               19200
+       },
+       [AO_SERIAL_SPEED_57600] = {
+               57600
+       },
+       [AO_SERIAL_SPEED_115200] = {
+               115200
+       },
+};
+
+static void
+ao_usart_set_speed(struct ao_stm_usart *usart, uint8_t speed)
+{
+       uint32_t        brr;
+
+       if (speed > AO_SERIAL_SPEED_115200)
+               return;
+       if (usart == &ao_stm_usart1)
+               brr = AO_PCLK2 / ao_usart_speeds[speed].baud;
+       else
+               brr = AO_PCLK1 / ao_usart_speeds[speed].baud;
+       usart->reg->brr = brr;
+}
+
+static void
+ao_usart_init(struct ao_stm_usart *usart, int hw_flow)
+{
+       usart->reg->cr1 = ((1 << STM_USART_CR1_UE) |
+                         (0 << STM_USART_CR1_M) |
+                         (0 << STM_USART_CR1_WAKE) |
+                         (0 << STM_USART_CR1_PCE) |
+                         (0 << STM_USART_CR1_PS) |
+                         (0 << STM_USART_CR1_PEIE) |
+                         (0 << STM_USART_CR1_TXEIE) |
+                         (0 << STM_USART_CR1_TCIE) |
+                         (1 << STM_USART_CR1_RXNEIE) |
+                         (0 << STM_USART_CR1_IDLEIE) |
+                         (1 << STM_USART_CR1_TE) |
+                         (1 << STM_USART_CR1_RE) |
+                         (0 << STM_USART_CR1_RWU) |
+                         (0 << STM_USART_CR1_SBK));
+
+       usart->reg->cr2 = ((0 << STM_USART_CR2_LINEN) |
+                         (STM_USART_CR2_STOP_1 << STM_USART_CR2_STOP) |
+                         (0 << STM_USART_CR2_CLKEN) |
+                         (0 << STM_USART_CR2_CPOL) |
+                         (0 << STM_USART_CR2_CPHA) |
+                         (0 << STM_USART_CR2_LBCL) |
+                         (0 << STM_USART_CR2_LBDIE) |
+                         (0 << STM_USART_CR2_LBDL) |
+                         (0 << STM_USART_CR2_ADD));
+
+       usart->reg->cr3 = ((0 << STM_USART_CR3_CTSIE) |
+                         (0 << STM_USART_CR3_CTSE) |
+                         (0 << STM_USART_CR3_RTSE) |
+                         (0 << STM_USART_CR3_DMAT) |
+                         (0 << STM_USART_CR3_DMAR) |
+                         (0 << STM_USART_CR3_SCEN) |
+                         (0 << STM_USART_CR3_NACK) |
+                         (0 << STM_USART_CR3_HDSEL) |
+                         (0 << STM_USART_CR3_IRLP) |
+                         (0 << STM_USART_CR3_IREN) |
+                         (0 << STM_USART_CR3_EIE));
+
+       if (hw_flow)
+               usart->reg->cr3 |= ((1 << STM_USART_CR3_CTSE) |
+                                   (1 << STM_USART_CR3_RTSE));
+
+       /* Pick a 9600 baud rate */
+       ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
+}
+
+#if HAS_SERIAL_HW_FLOW
+static void
+ao_usart_set_flow(struct ao_stm_usart *usart)
+{
+}
+#endif
+
+#if HAS_SERIAL_1
+
+struct ao_stm_usart ao_stm_usart1;
+
+void stm_usart1_isr(void) { ao_usart_isr(&ao_stm_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart1, c);
+}
+
+int
+_ao_serial1_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_stm_usart1);
+}
+
+uint8_t
+_ao_serial1_sleep_for(AO_TICK_TYPE timeout)
+{
+       return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
+}
+
+void
+ao_serial1_drain(void)
+{
+       ao_usart_drain(&ao_stm_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_stm_usart1);
+       ao_usart_set_speed(&ao_stm_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+#if HAS_SERIAL_2
+
+struct ao_stm_usart ao_stm_usart2;
+
+void stm_usart2_isr(void) { ao_usart_isr(&ao_stm_usart2, USE_SERIAL_2_STDIN); }
+
+char
+ao_serial2_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart2);
+}
+
+void
+ao_serial2_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart2, c);
+}
+
+int
+_ao_serial2_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_stm_usart2);
+}
+
+uint8_t
+_ao_serial2_sleep_for(AO_TICK_TYPE timeout)
+{
+       return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
+}
+
+void
+ao_serial2_drain(void)
+{
+       ao_usart_drain(&ao_stm_usart2);
+}
+
+void
+ao_serial2_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_stm_usart2);
+       ao_usart_set_speed(&ao_stm_usart2, speed);
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial2_cts(void)
+{
+       _ao_usart_cts(&ao_stm_usart2);
+}
+#endif
+
+#endif /* HAS_SERIAL_2 */
+
+#if HAS_SERIAL_3
+
+struct ao_stm_usart ao_stm_usart3;
+
+void stm_usart3_isr(void) { ao_usart_isr(&ao_stm_usart3, USE_SERIAL_3_STDIN); }
+
+char
+ao_serial3_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart3);
+}
+
+void
+ao_serial3_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart3, c);
+}
+
+int
+_ao_serial3_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_stm_usart3);
+}
+
+uint8_t
+_ao_serial3_sleep_for(AO_TICK_TYPE timeout)
+{
+       return _ao_usart_sleep_for(&ao_stm_usart3, timeout);
+}
+
+void
+ao_serial3_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_stm_usart3);
+       ao_usart_set_speed(&ao_stm_usart3, speed);
+}
+
+void
+ao_serial3_drain(void)
+{
+       ao_usart_drain(&ao_stm_usart3);
+}
+#endif /* HAS_SERIAL_3 */
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial_set_sw_rts_cts(struct ao_stm_usart *usart,
+                        void (*isr)(void),
+                        struct stm_gpio *port_rts,
+                        uint8_t pin_rts,
+                        struct stm_gpio *port_cts,
+                        uint8_t pin_cts)
+{
+       /* Pull RTS low to note that there's space in the FIFO
+        */
+       ao_enable_output(port_rts, pin_rts, 0);
+       usart->gpio_rts = port_rts;
+       usart->pin_rts = pin_rts;
+       usart->rts = 1;
+
+       ao_exti_setup(port_cts, pin_cts, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, isr);
+       usart->gpio_cts = port_cts;
+       usart->pin_cts = pin_cts;
+}
+#endif
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_1
+       /*
+        *      TX      RX
+        *      PA9     PA10
+        *      PB6     PB7     *
+        */
+
+#if SERIAL_1_PA9_PA10
+       ao_enable_port(&stm_gpioa);
+       stm_gpio_conf(&stm_gpioa, 9,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+       stm_gpio_conf(&stm_gpioa, 10,
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+       stm_set_afio_mapr(STM_AFIO_MAPR_USART1_REMAP,
+                         STM_AFIO_MAPR_USART1_REMAP_PA9_PA10,
+                         STM_AFIO_MAPR_USART1_REMAP_MASK);
+#else
+#if SERIAL_1_PB6_PB7
+       ao_enable_port(&stm_gpiob);
+       stm_gpio_conf(&stm_gpiob, 6,
+                     STM_GPIO_CR_MODE_OUTPUT_2MHZ,
+                     STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL);
+
+       stm_gpio_conf(&stm_gpiob, 7,
+                     STM_GPIO_CR_MODE_INPUT,
+                     STM_GPIO_CR_CNF_INPUT_FLOATING);
+
+       stm_set_afio_mapr(STM_AFIO_MAPR_USART1_REMAP,
+                         STM_AFIO_MAPR_USART1_REMAP_PB6_PB7,
+                         STM_AFIO_MAPR_USART1_REMAP_MASK);
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+#endif
+       /* Enable USART */
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART1EN);
+
+       ao_stm_usart1.reg = &stm_usart1;
+       ao_usart_init(&ao_stm_usart1, 0);
+
+       stm_nvic_set_enable(STM_ISR_USART1_POS);
+       stm_nvic_set_priority(STM_ISR_USART1_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
+       ao_add_stdio(_ao_serial1_pollchar,
+                    ao_serial1_putchar,
+                    NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_2
+       /*
+        *      TX      RX
+        *      PA2     PA3
+        *      PD5     PD6
+        */
+
+#if SERIAL_2_PA2_PA3
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+       stm_afr_set(&stm_gpioa, 2, STM_AFR_AF7);
+       stm_afr_set(&stm_gpioa, 3, STM_AFR_AF7);
+# if USE_SERIAL_2_FLOW
+#  if USE_SERIAL_2_SW_FLOW
+       ao_serial_set_sw_rts_cts(&ao_stm_usart2,
+                                ao_serial2_cts,
+                                SERIAL_2_PORT_RTS,
+                                SERIAL_2_PIN_RTS,
+                                SERIAL_2_PORT_CTS,
+                                SERIAL_2_PIN_CTS);
+#  else
+       stm_afr_set(&stm_gpioa, 0, STM_AFR_AF7);
+       stm_afr_set(&stm_gpioa, 1, STM_AFR_AF7);
+#  endif
+# endif
+#else
+#if SERIAL_2_PD5_PD6
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+
+       stm_afr_set(&stm_gpiod, 5, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiod, 6, STM_AFR_AF7);
+#if USE_SERIAL_2_FLOW
+#error "Don't know how to set flowcontrol for serial 2 on PD"
+#endif
+#else
+#error "No SERIAL_2 port configuration specified"
+#endif
+#endif
+       /* Enable USART */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART2EN);
+
+       ao_stm_usart2.reg = &stm_usart2;
+       ao_usart_init(&ao_stm_usart2, USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW);
+
+       stm_nvic_set_enable(STM_ISR_USART2_POS);
+       stm_nvic_set_priority(STM_ISR_USART2_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
+       ao_add_stdio(_ao_serial2_pollchar,
+                    ao_serial2_putchar,
+                    NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_3
+       /*
+        *      TX      RX
+        *      PB10    PB11
+        *      PC10    PC11
+        *      PD8     PD9
+        */
+#if SERIAL_3_PB10_PB11
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+
+       stm_afr_set(&stm_gpiob, 10, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiob, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PC10_PC11
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOCEN);
+
+       stm_afr_set(&stm_gpioc, 10, STM_AFR_AF7);
+       stm_afr_set(&stm_gpioc, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PD8_PD9
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+
+       stm_afr_set(&stm_gpiod, 8, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiod, 9, STM_AFR_AF7);
+#else
+#error "No SERIAL_3 port configuration specified"
+#endif
+#endif
+#endif
+       /* Enable USART */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART3EN);
+
+       ao_stm_usart3.reg = &stm_usart3;
+       ao_usart_init(&ao_stm_usart3, 0);
+
+       stm_nvic_set_enable(STM_ISR_USART3_POS);
+       stm_nvic_set_priority(STM_ISR_USART3_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_3_STDIN && !DELAY_SERIAL_3_STDIN
+       ao_add_stdio(_ao_serial3_pollchar,
+                    ao_serial3_putchar,
+                    NULL);
+#endif
+#endif
+}
index 116d33ace1981bf2a96b37257f5fcab6f4720b34..37c4e1df50cf9bd3e134538dcc26ab7f952fa83f 100644 (file)
@@ -51,7 +51,7 @@ void stm_systick_isr(void)
 {
        if (stm_systick.ctrl & (1 << STM_SYSTICK_CTRL_COUNTFLAG)) {
                ++ao_tick_count;
-//             ao_task_check_alarm();
+               ao_task_check_alarm();
 #ifdef AO_TIMER_HOOK
                AO_TIMER_HOOK;
 #endif
@@ -68,5 +68,5 @@ ao_timer_init(void)
        stm_systick.ctrl = ((1 << STM_SYSTICK_CTRL_ENABLE) |
                            (1 << STM_SYSTICK_CTRL_TICKINT) |
                            (STM_SYSTICK_CTRL_CLKSOURCE_HCLK_8 << STM_SYSTICK_CTRL_CLKSOURCE));
-//     stm_nvic.shpr15_12 |= (uint32_t) AO_STM_NVIC_CLOCK_PRIORITY << 24;
+       stm_scb.shpr3 |= (uint32_t) AO_STM_NVIC_CLOCK_PRIORITY << 24;
 }
diff --git a/src/stm32f1/ao_usb_stm.c b/src/stm32f1/ao_usb_stm.c
new file mode 100644 (file)
index 0000000..3d632dd
--- /dev/null
@@ -0,0 +1,1172 @@
+/*
+ * Copyright © 2023 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.h"
+#include "ao_usb.h"
+#include "ao_product.h"
+
+#define USB_DEBUG      0
+#define USB_DEBUG_DATA 0
+#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
+
+#if USB_DEBUG
+#define debug(format, args...) printf(format, ## args);
+#else
+#define debug(format, args...)
+#endif
+
+#if USB_DEBUG_DATA
+#define debug_data(format, args...)    printf(format, ## args);
+#else
+#define debug_data(format, args...)
+#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;
+
+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;
+
+/*
+ * Objects allocated in special USB memory
+ */
+
+/* Buffer description tables */
+static union stm_usb_bdt       *ao_usb_bdt;
+/* USB address of end of allocated storage */
+static uint16_t        ao_usb_sram_addr;
+
+/* Pointer to ep0 tx/rx buffers in USB memory */
+static uint32_t        *ao_usb_ep0_tx_buffer;
+static uint32_t        *ao_usb_ep0_rx_buffer;
+
+/* Pointer to bulk data tx/rx buffers in USB memory */
+static uint32_t        *ao_usb_in_tx_buffer;
+static uint32_t        *ao_usb_out_rx_buffer;
+
+/* 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;
+
+#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
+
+static uint8_t ao_usb_ep0_receive;
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+static inline uint32_t set_toggle(uint32_t     current_value,
+                                  uint32_t     mask,
+                                  uint32_t     desired_value)
+{
+       return (current_value ^ desired_value) & mask;
+}
+
+static inline uint32_t *ao_usb_packet_buffer_addr(uint16_t sram_addr)
+{
+       return (uint32_t *) (((void *) ((uint8_t *) stm_usb_sram + 2 * sram_addr)));
+}
+
+static inline uint32_t ao_usb_epr_stat_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_STAT_RX) & STM_USB_EPR_STAT_RX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_stat_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_STAT_TX) & STM_USB_EPR_STAT_TX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_ctr_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_CTR_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_ctr_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_CTR_TX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_setup(uint32_t epr) {
+       return (epr >> STM_USB_EPR_SETUP) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_DTOG_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_DTOG_TX) & 1;
+}
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+static void
+ao_usb_set_address(uint8_t address)
+{
+       debug("ao_usb_set_address %02x\n", address);
+       stm_usb.daddr = (1 << STM_USB_DADDR_EF) | address;
+       ao_usb_address_pending = 0;
+}
+
+/*
+ * Write these values to preserve register contents under HW changes
+ */
+
+#define STM_USB_EPR_INVARIANT  ((1 << STM_USB_EPR_CTR_RX) |            \
+                                (STM_USB_EPR_DTOG_RX_WRITE_INVARIANT << STM_USB_EPR_DTOG_RX) | \
+                                (STM_USB_EPR_STAT_RX_WRITE_INVARIANT << STM_USB_EPR_STAT_RX) | \
+                                (1 << STM_USB_EPR_CTR_TX) |            \
+                                (STM_USB_EPR_DTOG_TX_WRITE_INVARIANT << STM_USB_EPR_DTOG_TX) | \
+                                (STM_USB_EPR_STAT_TX_WRITE_INVARIANT << STM_USB_EPR_STAT_TX))
+
+#define STM_USB_EPR_INVARIANT_MASK     ((1 << STM_USB_EPR_CTR_RX) |    \
+                                        (STM_USB_EPR_DTOG_RX_MASK << STM_USB_EPR_DTOG_RX) | \
+                                        (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX) | \
+                                        (1 << STM_USB_EPR_CTR_TX) |    \
+                                        (STM_USB_EPR_DTOG_TX_MASK << STM_USB_EPR_DTOG_TX) | \
+                                        (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX))
+
+/*
+ * These bits are purely under sw control, so preserve them in the
+ * register by re-writing what was read
+ */
+#define STM_USB_EPR_PRESERVE_MASK      ((STM_USB_EPR_EP_TYPE_MASK << STM_USB_EPR_EP_TYPE) | \
+                                        (1 << STM_USB_EPR_EP_KIND) |   \
+                                        (STM_USB_EPR_EA_MASK << STM_USB_EPR_EA))
+
+#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 the state of the specified endpoint register to a new
+ * value. This is tricky because the bits toggle where the new
+ * value is one, and we need to write invariant values in other
+ * spots of the register. This hardware is strange...
+ */
+static void
+_ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+       uint32_t        epr_write, epr_old;
+
+       _tx_dbg1("set_stat_tx top", stat_tx);
+       epr_old = epr_write = stm_usb.epr[ep];
+       epr_write &= STM_USB_EPR_PRESERVE_MASK;
+       epr_write |= STM_USB_EPR_INVARIANT;
+       epr_write |= set_toggle(epr_old,
+                             STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX,
+                             stat_tx << STM_USB_EPR_STAT_TX);
+       stm_usb.epr[ep] = epr_write;
+       _tx_dbg1("set_stat_tx bottom", epr_write);
+}
+
+static void
+ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+       ao_arch_block_interrupts();
+       _ao_usb_set_stat_tx(ep, stat_tx);
+       ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+       uint32_t        epr_write, epr_old;
+
+       epr_write = epr_old = stm_usb.epr[ep];
+       epr_write &= STM_USB_EPR_PRESERVE_MASK;
+       epr_write |= STM_USB_EPR_INVARIANT;
+       epr_write |= set_toggle(epr_old,
+                             STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX,
+                             stat_rx << STM_USB_EPR_STAT_RX);
+       stm_usb.epr[ep] = epr_write;
+}
+
+static void
+ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+       ao_arch_block_interrupts();
+       _ao_usb_set_stat_rx(ep, stat_rx);
+       ao_arch_release_interrupts();
+}
+
+/*
+ * Set just endpoint 0, for use during startup
+ */
+
+static void
+ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint32_t stat_tx)
+{
+       uint32_t                epr;
+       ao_arch_block_interrupts();
+       epr = stm_usb.epr[ep];
+       epr = ((0 << STM_USB_EPR_CTR_RX) |
+              (epr & (1 << STM_USB_EPR_DTOG_RX)) |
+              set_toggle(epr,
+                         (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX),
+                         (stat_rx << STM_USB_EPR_STAT_RX)) |
+              (type << STM_USB_EPR_EP_TYPE) |
+              (0 << STM_USB_EPR_EP_KIND) |
+              (0 << STM_USB_EPR_CTR_TX) |
+              (epr & (1 << STM_USB_EPR_DTOG_TX)) |
+              set_toggle(epr,
+                         (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX),
+                         (stat_tx << STM_USB_EPR_STAT_TX)) |
+              (addr << STM_USB_EPR_EA));
+       stm_usb.epr[ep] = epr;
+       ao_arch_release_interrupts();
+       debug ("writing epr[%d] 0x%08x wrote 0x%08x\n",
+              ep, epr, stm_usb.epr[ep]);
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+       uint8_t e;
+
+       ao_usb_sram_addr = 0;
+
+       /* buffer table is at the start of USB memory */
+       stm_usb.btable = 0;
+       ao_usb_bdt = (void *) stm_usb_sram;
+
+       ao_usb_sram_addr += 8 * STM_USB_BDT_SIZE;
+
+       /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */
+
+       ao_usb_bdt[0].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[0].single.count_tx = 0;
+       ao_usb_ep0_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+       ao_usb_bdt[0].single.addr_rx = ao_usb_sram_addr;
+       ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+                                 (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+       ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+       ao_usb_init_ep(AO_USB_CONTROL_EPR, AO_USB_CONTROL_EP,
+                      STM_USB_EPR_EP_TYPE_CONTROL,
+                      STM_USB_EPR_STAT_RX_VALID,
+                      STM_USB_EPR_STAT_TX_NAK);
+
+       /* Clear all of the other endpoints */
+       for (e = 1; e < 8; e++) {
+               ao_usb_init_ep(e, 0,
+                              STM_USB_EPR_EP_TYPE_CONTROL,
+                              STM_USB_EPR_STAT_RX_DISABLED,
+                              STM_USB_EPR_STAT_TX_DISABLED);
+       }
+
+       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)
+{
+       debug ("ao_usb_set_configuration\n");
+
+       /* 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,
+                      STM_USB_EPR_EP_TYPE_INTERRUPT,
+                      STM_USB_EPR_STAT_RX_DISABLED,
+                      STM_USB_EPR_STAT_TX_NAK);
+
+       /* 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);
+
+       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 int_count;
+static uint16_t        in_count;
+static uint16_t        out_count;
+static uint16_t        reset_count;
+
+/* The USB memory holds 16 bit values on 32 bit boundaries
+ * and must be accessed only in 32 bit units. Sigh.
+ */
+
+static inline void
+ao_usb_write_byte(uint8_t byte, uint32_t *base, uint16_t offset)
+{
+       base += offset >> 1;
+       if (offset & 1) {
+               *base = (*base & 0xff) | ((uint32_t) byte << 8);
+       } else {
+               *base = (*base & 0xff00) | byte;
+       }
+}
+
+static inline void
+ao_usb_write_short(uint16_t data, uint32_t *base, uint16_t offset)
+{
+       base[offset>>1] = data;
+}
+
+static void
+ao_usb_write(const uint8_t *src, uint32_t *base, uint16_t bytes)
+{
+       uint16_t offset = 0;
+       if (!bytes)
+               return;
+       while (bytes >= 2) {
+               debug_data (" %02x %02x", src[0], src[1]);
+               ao_usb_write_short((uint16_t) ((uint16_t) (src[1] << 8) | (uint16_t) src[0]), base, offset);
+               offset += 2;
+               src += 2;
+               bytes -= 2;
+       }
+       if (bytes) {
+               debug_data (" %02x", src[0]);
+               ao_usb_write_byte(*src, base, offset);
+       }
+}
+
+static inline uint8_t
+ao_usb_read_byte(uint32_t *base, uint16_t offset)
+{
+       base += offset >> 1;
+       if (offset & 1)
+               return (*base >> 8) & 0xff;
+       else
+               return *base & 0xff;
+}
+
+static inline uint16_t
+ao_usb_read_short(uint32_t *base, uint16_t offset)
+{
+       return (uint16_t) (base[offset>>1]);
+}
+
+static void
+ao_usb_read(uint8_t *dst, uint32_t *base, uint16_t offset, uint16_t bytes)
+{
+       if (!bytes)
+               return;
+       if (offset & 1) {
+               *dst++ = ao_usb_read_byte(base, offset++);
+               debug_data (" %02x", dst[-1]);
+               bytes--;
+       }
+       while (bytes >= 2) {
+               uint16_t        s = ao_usb_read_short(base, offset);
+               dst[0] = (uint8_t) s;
+               dst[1] = (uint8_t) (s >> 8);
+               debug_data (" %02x %02x", dst[0], dst[1]);
+               offset += 2;
+               dst += 2;
+               bytes -= 2;
+       }
+       if (bytes) {
+               *dst = ao_usb_read_byte(base, offset);
+               debug_data (" %02x", dst[0]);
+       }
+}
+
+/* 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_epr_stat_tx(stm_usb.epr[0]) == STM_USB_EPR_STAT_TX_VALID) {
+               debug("EP0 not accepting IN data\n");
+               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;
+
+       debug_data ("Flush EP0 len %d:", this_len);
+       ao_usb_write(ao_usb_ep0_in_data, ao_usb_ep0_tx_buffer, this_len);
+       debug_data ("\n");
+       ao_usb_ep0_in_data += this_len;
+
+       /* Mark the endpoint as TX valid to send the packet */
+       ao_usb_bdt[AO_USB_CONTROL_EPR].single.count_tx = this_len;
+       ao_usb_set_stat_tx(AO_USB_CONTROL_EPR, STM_USB_EPR_STAT_TX_VALID);
+       debug ("queue tx. epr 0 now %08x\n", stm_usb.epr[AO_USB_CONTROL_EPR]);
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+       uint16_t        len = ao_usb_bdt[0].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK;
+
+       if (len > ao_usb_ep0_out_len)
+               len = ao_usb_ep0_out_len;
+       ao_usb_ep0_out_len -= (uint8_t) len;
+
+       /* Pull all of the data out of the packet */
+       debug_data ("Fill EP0 len %d:", len);
+       ao_usb_read(ao_usb_ep0_out_data, ao_usb_ep0_rx_buffer, 0, len);
+       debug_data ("\n");
+       ao_usb_ep0_out_data += len;
+
+       /* ACK the packet */
+       ao_usb_set_stat_rx(0, STM_USB_EPR_STAT_RX_VALID);
+}
+
+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 = (uint8_t) max;
+       ao_usb_ep0_flush();
+}
+
+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 = (uint8_t) (value >> 8);
+       uint8_t         index = (uint8_t) 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 = (uint8_t) length;
+                       ao_usb_ep0_in_set(descriptor, len);
+                       break;
+               }
+               descriptor += descriptor[0];
+       }
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+       /* Pull the setup packet out of the fifo */
+       ao_usb_ep0_out_set((uint8_t *) &ao_usb_setup, 8);
+       ao_usb_ep0_fill();
+       if (ao_usb_ep0_out_len != 0) {
+               debug ("invalid setup packet length\n");
+               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:
+               debug ("Standard setup packet\n");
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       debug ("Device setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               debug ("get status\n");
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_ADDRESS:
+                               debug ("set address %d\n", ao_usb_setup.value);
+                               ao_usb_address = (uint8_t) ao_usb_setup.value;
+                               ao_usb_address_pending = 1;
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               debug ("get descriptor %d\n", ao_usb_setup.value);
+                               ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length);
+                               break;
+                       case AO_USB_REQ_GET_CONFIGURATION:
+                               debug ("get configuration %d\n", ao_usb_configuration);
+                               ao_usb_ep0_in_queue_byte(ao_usb_configuration);
+                               break;
+                       case AO_USB_REQ_SET_CONFIGURATION:
+                               ao_usb_configuration = (uint8_t) ao_usb_setup.value;
+                               debug ("set configuration %d\n", ao_usb_configuration);
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+                       debug ("Interface setup packet\n");
+                       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:
+                       debug ("Endpoint setup packet\n");
+                       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:
+               debug ("Class setup packet\n");
+               switch (ao_usb_setup.request) {
+               case AO_USB_SET_LINE_CODING:
+                       debug ("set line coding\n");
+                       ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_GET_LINE_CODING:
+                       debug ("get line coding\n");
+                       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)
+{
+       ao_usb_ep0_receive = 0;
+       if (receive & AO_USB_EP0_GOT_RESET) {
+               debug ("\treset\n");
+               ao_usb_set_ep0();
+               return;
+       }
+       if (receive & AO_USB_EP0_GOT_SETUP) {
+               debug ("\tsetup\n");
+               ao_usb_ep0_setup();
+       }
+       if (receive & AO_USB_EP0_GOT_RX_DATA) {
+               debug ("\tgot rx data\n");
+               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) {
+               debug ("\tgot tx ack\n");
+
+#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
+stm_usb_lp_isr(void)
+{
+       uint32_t        istr = stm_usb.istr;
+
+       if (istr & (1 << STM_USB_ISTR_CTR)) {
+               uint8_t         ep = istr & STM_USB_ISTR_EP_ID_MASK;
+               uint32_t        epr, epr_write;
+
+               /* Preserve the SW write bits, don't mess with most HW writable bits,
+                * clear the CTR_RX and CTR_TX bits
+                */
+               epr = stm_usb.epr[ep];
+               epr_write = epr;
+               epr_write &= STM_USB_EPR_PRESERVE_MASK;
+               epr_write |= STM_USB_EPR_INVARIANT;
+               epr_write &= ~(1UL << STM_USB_EPR_CTR_RX);
+               epr_write &= ~(1UL << STM_USB_EPR_CTR_TX);
+               stm_usb.epr[ep] = epr_write;
+
+               switch (ep) {
+               case 0:
+                       ++control_count;
+                       if (ao_usb_epr_ctr_rx(epr)) {
+                               if (ao_usb_epr_setup(epr))
+                                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP;
+                               else
+                                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA;
+                       }
+                       if (ao_usb_epr_ctr_tx(epr))
+                               ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+                       ao_usb_ep0_handle(ao_usb_ep0_receive);
+                       break;
+               case AO_USB_OUT_EPR:
+                       ++out_count;
+                       if (ao_usb_epr_ctr_rx(epr)) {
+                               _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");
+                       }
+                       break;
+               case AO_USB_IN_EPR:
+                       ++in_count;
+                       _tx_dbg1("TX ISR", epr);
+                       if (ao_usb_epr_ctr_tx(epr)) {
+                               ao_usb_in_pending = 0;
+                               ao_wakeup(&ao_usb_in_pending);
+                       }
+                       break;
+               case AO_USB_INT_EPR:
+                       ++int_count;
+                       if (ao_usb_epr_ctr_tx(epr))
+                               _ao_usb_set_stat_tx(AO_USB_INT_EPR, STM_USB_EPR_STAT_TX_NAK);
+                       break;
+               }
+               return;
+       }
+
+       if (istr & (1 << STM_USB_ISTR_RESET)) {
+               ++reset_count;
+               stm_usb.istr &= ~(1UL << STM_USB_ISTR_RESET);
+               ao_usb_ep0_receive |= AO_USB_EP0_GOT_RESET;
+               ao_usb_ep0_handle(ao_usb_ep0_receive);
+       }
+}
+
+void
+stm_usb_wakeup_isr(void)
+{
+       /* USB wakeup, just clear the bit for now */
+       stm_usb.istr &= ~(1UL << STM_USB_ISTR_WKUP);
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+_ao_usb_in_send(void)
+{
+       _tx_dbg0("in_send start");
+       debug ("send %d\n", ao_usb_tx_count);
+       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_write(ao_usb_tx_buffer, ao_usb_in_tx_buffer, ao_usb_tx_count);
+       ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = ao_usb_tx_count;
+       ao_usb_tx_count = 0;
+       _ao_usb_set_stat_tx(AO_USB_IN_EPR, STM_USB_EPR_STAT_TX_VALID);
+       _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 = (uint8_t) (ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK);
+
+       _rx_dbg1("out_recv count", ao_usb_rx_count);
+       debug ("recv %d\n", ao_usb_rx_count);
+       debug_data("Fill OUT len %d:", ao_usb_rx_count);
+       ao_usb_read(ao_usb_rx_buffer, ao_usb_out_rx_buffer, 0, ao_usb_rx_count);
+       debug_data("\n");
+       ao_usb_rx_pos = 0;
+
+       /* ACK the packet */
+       _ao_usb_set_stat_rx(AO_USB_OUT_EPR, STM_USB_EPR_STAT_RX_VALID);
+}
+
+static 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 (char) c;
+}
+
+#ifndef HAS_USB_DISABLE
+#define HAS_USB_DISABLE 1
+#endif
+
+#if HAS_USB_DISABLE
+void
+ao_usb_disable(void)
+{
+       ao_arch_block_interrupts();
+       stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+       stm_usb.istr = 0;
+
+#if HAS_USB_PULLUP
+       /* Disable USB pull-up */
+       ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+#endif
+
+       /* Switch off the device */
+       stm_usb.cntr = (1 << STM_USB_CNTR_PDWN) | (1 << STM_USB_CNTR_FRES);
+
+       /* Disable the interface */
+       stm_rcc.apb1enr &= ~(1UL << STM_RCC_APB1ENR_USBEN);
+       ao_arch_release_interrupts();
+}
+#endif
+
+void
+ao_usb_enable(void)
+{
+       int     t;
+
+#if HAS_USB_PULLUP
+       ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+#endif
+
+       /* Enable USB device */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USBEN);
+
+       /* Do not touch the GPIOA configuration; USB takes priority
+        * over GPIO on pins A11 and A12, but if you select alternate
+        * input 10 (the documented correct selection), then USB is
+        * pulled low and doesn't work at all
+        */
+
+       ao_arch_block_interrupts();
+
+       /* Route interrupts */
+       stm_nvic_set_priority(STM_ISR_USB_LP_POS, AO_STM_NVIC_LOW_PRIORITY);
+       stm_nvic_set_enable(STM_ISR_USB_LP_POS);
+
+       ao_usb_configuration = 0;
+
+       stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+
+       /* Clear the power down bit */
+       stm_usb.cntr = 0;
+
+       /* Clear any spurious interrupts */
+       stm_usb.istr = 0;
+
+       debug ("ao_usb_enable\n");
+
+       /* Enable interrupts */
+       stm_usb.cntr = ((1 << STM_USB_CNTR_CTRM) |
+                       (0 << STM_USB_CNTR_PMAOVRM) |
+                       (0 << STM_USB_CNTR_ERRM) |
+                       (0 << STM_USB_CNTR_WKUPM) |
+                       (0 << STM_USB_CNTR_SUSPM) |
+                       (1 << STM_USB_CNTR_RESETM) |
+                       (0 << STM_USB_CNTR_SOFM) |
+                       (0 << STM_USB_CNTR_ESOFM) |
+                       (0 << STM_USB_CNTR_RESUME) |
+                       (0 << STM_USB_CNTR_FSUSP) |
+                       (0 << STM_USB_CNTR_LP_MODE) |
+                       (0 << STM_USB_CNTR_PDWN) |
+                       (0 << STM_USB_CNTR_FRES));
+
+       ao_arch_release_interrupts();
+
+       for (t = 0; t < 1000; t++)
+               ao_arch_nop();
+
+       /* Enable USB pull-up */
+#if HAS_USB_PULLUP
+       ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 1);
+#endif
+}
+
+#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)
+{
+#if HAS_USB_PULLUP
+       int     i;
+       ao_enable_output(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, 0);
+       for (i = 0; i < 40000; i++)
+               ao_arch_nop();
+#endif
+       ao_usb_enable();
+
+       debug ("ao_usb_init\n");
+       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
index 5ac99e6c3d2496e02301fa20145252d4bdbe8e64..b3b9773617459962b39bd29d53b8e42a2da7f17f 100644 (file)
@@ -182,8 +182,8 @@ extern struct stm_rcc stm_rcc;
 #define STM_RCC_APB1ENR_DACEN          29
 #define STM_RCC_APB1ENR_PWREN          28
 #define STM_RCC_APB1ENR_BKPEN          27
-#define STM_RCC_APB1ENR_CAN2EN         26
-#define STM_RCC_APB1ENR_CAN1EN         25
+#define STM_RCC_APB1ENR_CANEN          26
+#define STM_RCC_APB1ENR_USBEN          23
 #define STM_RCC_APB1ENR_I2C2EN         22
 #define STM_RCC_APB1ENR_I2C1EN         21
 #define STM_RCC_APB1ENR_UART5EN                20
@@ -193,6 +193,9 @@ extern struct stm_rcc stm_rcc;
 #define STM_RCC_APB1ENR_SPI3EN         15
 #define STM_RCC_APB1ENR_SPI2EN         14
 #define STM_RCC_APB1ENR_WWDGEN         11
+#define STM_RCC_APB1ENR_TIM14EN                8
+#define STM_RCC_APB1ENR_TIM13EN                7
+#define STM_RCC_APB1ENR_TIM12EN                6
 #define STM_RCC_APB1ENR_TIM7EN         5
 #define STM_RCC_APB1ENR_TIM6EN         4
 #define STM_RCC_APB1ENR_TIM5EN         3
@@ -219,7 +222,7 @@ struct stm_systick {
 
 extern struct stm_systick stm_systick;
 
-#define stm_systick    (*((struct stm_systick *) 0xe000e010))
+//#define stm_systick  (*((struct stm_systick *) 0xe000e010))
 
 #define STM_SYSTICK_CTRL_ENABLE                0
 #define STM_SYSTICK_CTRL_TICKINT       1
@@ -231,49 +234,36 @@ extern struct stm_systick stm_systick;
 /* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */
 
 struct stm_nvic {
-       vuint32_t       iser[8];        /* 0x000 0xe000e100 Set Enable Register */
+       vuint32_t       iser[3];        /* 0x000 0xe000e100 Set Enable Register */
 
-       uint8_t         _unused020[0x080 - 0x020];
+       uint8_t         _unused00c[0x080 - 0x00c];
 
-       vuint32_t       icer[8];        /* 0x080 0xe000e180 Clear Enable Register */
+       vuint32_t       icer[3];        /* 0x080 0xe000e180 Clear Enable Register */
 
-       uint8_t         _unused0a0[0x100 - 0x0a0];
+       uint8_t         _unused08c[0x100 - 0x08c];
 
-       vuint32_t       ispr[8];        /* 0x100 0xe000e200 Set Pending Register */
+       vuint32_t       ispr[3];        /* 0x100 0xe000e200 Set Pending Register */
 
-       uint8_t         _unused120[0x180 - 0x120];
+       uint8_t         _unused10c[0x180 - 0x10c];
 
-       vuint32_t       icpr[8];        /* 0x180 0xe000e280 Clear Pending Register */
+       vuint32_t       icpr[3];        /* 0x180 0xe000e280 Clear Pending Register */
 
-       uint8_t         _unused1a0[0x200 - 0x1a0];
+       uint8_t         _unused18c[0x200 - 0x18c];
 
-       vuint32_t       iabr[8];        /* 0x200 0xe000e300 Active Bit Register */
+       vuint32_t       iabr[3];        /* 0x200 0xe000e300 Active Bit Register */
 
-       uint8_t         _unused220[0x300 - 0x220];
+       uint8_t         _unused20c[0x300 - 0x20c];
 
-       vuint32_t       ipr[60];        /* 0x300 0xe000e400 Priority Register */
+       vuint32_t       ipr[31];        /* 0x300 0xe000e400 Priority Register */
 
-       uint8_t         _unused3f0[0xc00 - 0x3f0];
+       uint8_t         _unused37c[0xe00 - 0x37c];      /* covers SCB */
 
-       vuint32_t       cpuid_base;     /* 0xc00 0xe000ed00 CPUID Base Register */
-       vuint32_t       ics;            /* 0xc04 0xe000ed04 Interrupt Control State Register */
-       vuint32_t       vto;            /* 0xc08 0xe000ed08 Vector Table Offset Register */
-       vuint32_t       ai_rc;          /* 0xc0c 0xe000ed0c Application Interrupt/Reset Control Register */
-       vuint32_t       sc;             /* 0xc10 0xe000ed10 System Control Register */
-       vuint32_t       cc;             /* 0xc14 0xe000ed14 Configuration Control Register */
-
-       vuint32_t       shpr7_4;        /* 0xc18 0xe000ed18 System Hander Priority Registers */
-       vuint32_t       shpr11_8;       /* 0xc1c */
-       vuint32_t       shpr15_12;      /* 0xc20 */
-
-       uint8_t         _unusedc18[0xe00 - 0xc24];
-
-       vuint32_t       stir;           /* 0xe00 */
+       vuint32_t       stir;           /* 0xe00 0xe000ee00 Software Trigger Interrupt Register */
 };
 
 extern struct stm_nvic stm_nvic;
 
-#define stm_nvic (*((struct stm_nvic *) 0xe000e100))
+//#define stm_nvic (*((struct stm_nvic *) 0xe000e100))
 
 #define IRQ_REG(irq)   ((irq) >> 5)
 #define IRQ_BIT(irq)   ((irq) & 0x1f)
@@ -335,6 +325,67 @@ stm_nvic_get_priority(int irq) {
        return (stm_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
 }
 
+struct stm_scb {
+       vuint32_t       cpuid;
+       vuint32_t       icsr;
+       vuint32_t       vtor;
+       vuint32_t       aircr;
+
+       vuint32_t       scr;
+       vuint32_t       ccr;
+       vuint32_t       shpr1;
+       vuint32_t       shpr2;
+
+       vuint32_t       shpr3;
+       vuint32_t       shcrs;
+       vuint32_t       cfsr;
+       vuint32_t       hfsr;
+
+       uint32_t        unused_30;
+       vuint32_t       mmar;
+       vuint32_t       bfar;
+};
+
+extern struct stm_scb stm_scb;
+
+#define STM_SCB_AIRCR_VECTKEY          16
+#define  STM_SCB_AIRCR_VECTKEY_KEY             0x05fa
+#define STM_SCB_AIRCR_PRIGROUP         8
+#define STM_SCB_AIRCR_SYSRESETREQ      2
+#define STM_SCB_AIRCR_VECTCLRACTIVE    1
+#define STM_SCB_AIRCR_VECTRESET                0
+
+struct stm_flash {
+       vuint32_t       acr;
+       vuint32_t       keyr;
+       vuint32_t       optkeyr;
+       vuint32_t       sr;
+
+       vuint32_t       cr;
+       vuint32_t       ar;
+       uint32_t        _unused018;
+       vuint32_t       obr;
+
+       vuint32_t       wrpr;
+};
+
+extern struct stm_flash stm_flash;
+
+//#define stm_flash (*((struct stm_flash *) 0x40022000))
+
+#define STM_FLASH_ACR_PRFTBS   5
+#define STM_FLASH_ACR_PRFTBE   4
+#define STM_FLASH_ACR_HLFCYA   3
+#define STM_FLASH_ACR_LATENCY  0
+#define  STM_FLASH_ACR_LATENCY_0       0
+#define  STM_FLASH_ACR_LATENCY_1       1
+#define  STM_FLASH_ACR_LATENCY_2       2
+
+#define STM_FLASH_RDPRT_KEY    0x00A5
+#define STM_FLASH_KEY1         0x45670123
+#define STM_FLASH_KEY2         0xCDEF89AB
+
+
 struct stm_flash_data {
        vuint16_t       f_size;
        vuint16_t       unused02;
@@ -346,7 +397,7 @@ extern struct stm_flash_data        stm_flash_data;
 
 static inline uint32_t stm_flash_size(void) { return (uint32_t) stm_flash_data.f_size * 1024; }
 
-#define stm_flash_data (*((struct stm_flash_data *) 0x1ffff7e0))
+//#define stm_flash_data       (*((struct stm_flash_data *) 0x1ffff7e0))
 
 struct stm_gpio {
        vuint32_t       cr[2];
@@ -425,11 +476,11 @@ extern struct stm_gpio stm_gpioc;
 extern struct stm_gpio stm_gpiod;
 extern struct stm_gpio stm_gpioe;
 
-#define stm_gpioe  (*((struct stm_gpio *) 0x40011800))
-#define stm_gpiod  (*((struct stm_gpio *) 0x40011400))
-#define stm_gpioc  (*((struct stm_gpio *) 0x40011000))
-#define stm_gpiob  (*((struct stm_gpio *) 0x40010c00))
-#define stm_gpioa  (*((struct stm_gpio *) 0x40010800))
+//#define stm_gpioe  (*((struct stm_gpio *) 0x40011800))
+//#define stm_gpiod  (*((struct stm_gpio *) 0x40011400))
+//#define stm_gpioc  (*((struct stm_gpio *) 0x40011000))
+//#define stm_gpiob  (*((struct stm_gpio *) 0x40010c00))
+//#define stm_gpioa  (*((struct stm_gpio *) 0x40010800))
 
 struct stm_afio {
        vuint32_t       evcr;
@@ -440,7 +491,266 @@ struct stm_afio {
 
 extern struct stm_afio stm_afio;
 
-#define stm_afio       (*((struct stm_afio *) 0x40010000))
+//#define stm_afio     (*((struct stm_afio *) 0x40010000))
+
+#define STM_AFIO_MAPR_ADC2_ETRGREG_REMAP       20
+#define STM_AFIO_MAPR_ADC2_ETRGINJ_REMAP       19
+#define STM_AFIO_MAPR_ADC1_ETRGREG_REMAP       18
+#define STM_AFIO_MAPR_ADC1_ETRGINJ_REMAP       17
+#define STM_AFIO_MAPR_TIM5CH4_IREMAP           16
+#define STM_AFIO_MAPR_PD01_REMAP               15
+#define STM_AFIO_MAPR_CAN_REMAP                        13
+#define  STM_AFIO_MAPR_CAN_REMAP_PA11_PA12             0
+#define  STM_AFIO_MAPR_CAN_REMAP_PB8_PB9               2
+#define  STM_AFIO_MAPR_CAN_REMAP_PD0_PD1               3
+#define  STM_AFIO_MAPR_CAN_REMAP_MASK                  3
+#define STM_AFIO_MAPR_TIM4_REMAP               12
+#define STM_AFIO_MAPR_TIM3_REMAP               10
+#define  STM_AFIO_MAPR_TIM3_REMAP_PA6_PA7_PB0_PB1      0
+#define  STM_AFIO_MAPR_TIM3_REMAP_PB4_PB5_PB0_PB1      2
+#define  STM_AFIO_MAPR_TIM3_REMAP_PC6_PC7_PC8_PC9      3
+#define  STM_AFIO_MAPR_TIM3_REMAP_MASK                 3
+#define STM_AFIO_MAPR_TIM2_REMAP               8
+#define  STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PA2_PA3      0
+#define  STM_AFIO_MAPR_TIM2_REMAP_PA15_PB3_PA2_PA3     1
+#define  STM_AFIO_MAPR_TIM2_REMAP_PA0_PA1_PB10_PB11    2
+#define  STM_AFIO_MAPR_TIM2_REMAP_PA15_PB3_PB10_PB11   3
+#define  STM_AFIO_MAPR_TIM2_REMAP_MASK                 3
+#define STM_AFIO_MAPR_TIM1_REMAP               6
+#define  STM_AFIO_MAPR_TIM1_REMAP_PA12_PA8_PA9_PA10_PA11_PB12_PB13_PB14_PB15   0
+#define  STM_AFIO_MAPR_TIM1_REMAP_PA12_PA8_PA9_PA10_PA11_PA6_PA7_PB0_PB1       1
+#define  STM_AFIO_MAPR_TIM1_REMAP_PE7_PE9_PE11_PE13_PE14_PE15_PE8_PE10_PE12    3
+#define  STM_AFIO_MAPR_TIM1_REMAP_MASK                                         3
+#define STM_AFIO_MAPR_USART3_REMAP             4
+#define  STM_AFIO_MAPR_USART3_REMAP_PB10_PB11_PB12_PB13_PB14   0
+#define  STM_AFIO_MAPR_USART3_REMAP_PC10_PC11_PC12_PB13_PB14   1
+#define  STM_AFIO_MAPR_USART3_REMAP_PD8_PD9_PD10_PD11_PD12     3
+#define  STM_AFIO_MAPR_USART3_REMAP_MASK                       3
+#define STM_AFIO_MAPR_USART2_REMAP             3
+#define  STM_AFIO_MAPR_USART2_REMAP_PA0_PA1_PA2_PA3_PA4        0
+#define  STM_AFIO_MAPR_USART2_REMAP_PD3_PD4_PD5_PD6_PD7        1
+#define  STM_AFIO_MAPR_USART2_REMAP_MASK               1
+#define STM_AFIO_MAPR_USART1_REMAP             2
+#define  STM_AFIO_MAPR_USART1_REMAP_PA9_PA10           0
+#define  STM_AFIO_MAPR_USART1_REMAP_PB6_PB7            1
+#define  STM_AFIO_MAPR_USART1_REMAP_MASK               1
+#define STM_AFIO_MAPR_I2C1_REMAP               1
+#define  STM_AFIO_MAPR_I2C1_REMAP_PB6_PB7              0
+#define  STM_AFIO_MAPR_I2C1_REMAP_PB8_PB9              1
+#define  STM_AFIO_MAPR_I2C1_REMAP_MASK                 1
+#define STM_AFIO_MAPR_SPI1_REMAP               0
+#define  STM_AFIO_MAPR_SPI1_REMAP_PA4_PA5_PA6_PA7      0
+#define  STM_AFIO_MAPR_SPI1_REMAP_PA15_PB3_PB4_PB5     1
+#define  STM_AFIO_MAPR_SPI1_REMAP_MASK                 1
+
+static inline void
+stm_set_afio_mapr(uint8_t bit, uint32_t val, uint32_t mask) {
+       uint32_t        mapr = stm_afio.mapr;
+
+       mapr &= ~(mask << bit);
+       mapr |= (val << bit);
+       stm_afio.mapr = mapr;
+}
+
+struct stm_usart {
+       vuint32_t       sr;     /* status register */
+       vuint32_t       dr;     /* data register */
+       vuint32_t       brr;    /* baud rate register */
+       vuint32_t       cr1;    /* control register 1 */
+
+       vuint32_t       cr2;    /* control register 2 */
+       vuint32_t       cr3;    /* control register 3 */
+       vuint32_t       gtpr;   /* guard time and prescaler */
+};
+
+extern struct stm_usart stm_usart1;
+extern struct stm_usart stm_usart2;
+extern struct stm_usart stm_usart3;
+
+//#define stm_usart1   (*((struct stm_usart *) 0x40013800))
+//#define stm_usart2   (*((struct stm_usart *) 0x40004800))
+//#define stm_usart3   (*((struct stm_usart *) 0x40004400))
+
+#define STM_USART_SR_CTS       (9)     /* CTS flag */
+#define STM_USART_SR_LBD       (8)     /* LIN break detection flag */
+#define STM_USART_SR_TXE       (7)     /* Transmit data register empty */
+#define STM_USART_SR_TC                (6)     /* Transmission complete */
+#define STM_USART_SR_RXNE      (5)     /* Read data register not empty */
+#define STM_USART_SR_IDLE      (4)     /* IDLE line detected */
+#define STM_USART_SR_ORE       (3)     /* Overrun error */
+#define STM_USART_SR_NE                (2)     /* Noise detected flag */
+#define STM_USART_SR_FE                (1)     /* Framing error */
+#define STM_USART_SR_PE                (0)     /* Parity error */
+
+#define STM_USART_BRR_DIV_MANTISSA     (4)
+#define STM_USART_BRR_DIV_FRACTION     (0)
+
+#define STM_USART_CR1_UE       (13)    /* USART enable */
+#define STM_USART_CR1_M                (12)    /* Word length */
+#define STM_USART_CR1_WAKE     (11)    /* Wakeup method */
+#define STM_USART_CR1_PCE      (10)    /* Parity control enable */
+#define STM_USART_CR1_PS       (9)     /* Parity selection */
+#define STM_USART_CR1_PEIE     (8)     /* PE interrupt enable */
+#define STM_USART_CR1_TXEIE    (7)     /* TXE interrupt enable */
+#define STM_USART_CR1_TCIE     (6)     /* Transmission complete interrupt enable */
+#define STM_USART_CR1_RXNEIE   (5)     /* RXNE interrupt enable */
+#define STM_USART_CR1_IDLEIE   (4)     /* IDLE interrupt enable */
+#define STM_USART_CR1_TE       (3)     /* Transmitter enable */
+#define STM_USART_CR1_RE       (2)     /* Receiver enable */
+#define STM_USART_CR1_RWU      (1)     /* Receiver wakeup */
+#define STM_USART_CR1_SBK      (0)     /* Send break */
+
+#define STM_USART_CR2_LINEN    (14)    /* LIN mode enable */
+#define STM_USART_CR2_STOP     (12)    /* STOP bits */
+#define STM_USART_CR2_STOP_MASK        3UL
+#define STM_USART_CR2_STOP_1   0
+#define STM_USART_CR2_STOP_0_5 1
+#define STM_USART_CR2_STOP_2   2
+#define STM_USART_CR2_STOP_1_5 3
+
+#define STM_USART_CR2_CLKEN    (11)    /* Clock enable */
+#define STM_USART_CR2_CPOL     (10)    /* Clock polarity */
+#define STM_USART_CR2_CPHA     (9)     /* Clock phase */
+#define STM_USART_CR2_LBCL     (8)     /* Last bit clock pulse */
+#define STM_USART_CR2_LBDIE    (6)     /* LIN break detection interrupt enable */
+#define STM_USART_CR2_LBDL     (5)     /* lin break detection length */
+#define STM_USART_CR2_ADD      (0)
+#define STM_USART_CR2_ADD_MASK 0xfUL
+
+#define STM_USART_CR3_CTSIE    (10)    /* CTS interrupt enable */
+#define STM_USART_CR3_CTSE     (9)     /* CTS enable */
+#define STM_USART_CR3_RTSE     (8)     /* RTS enable */
+#define STM_USART_CR3_DMAT     (7)     /* DMA enable transmitter */
+#define STM_USART_CR3_DMAR     (6)     /* DMA enable receiver */
+#define STM_USART_CR3_SCEN     (5)     /* Smartcard mode enable */
+#define STM_USART_CR3_NACK     (4)     /* Smartcard NACK enable */
+#define STM_USART_CR3_HDSEL    (3)     /* Half-duplex selection */
+#define STM_USART_CR3_IRLP     (2)     /* IrDA low-power */
+#define STM_USART_CR3_IREN     (1)     /* IrDA mode enable */
+#define STM_USART_CR3_EIE      (0)     /* Error interrupt enable */
+
+struct stm_usb {
+       vuint32_t       epr[8];
+       uint8_t         reserved_20[0x40 - 0x20];
+       vuint32_t       cntr;
+       vuint32_t       istr;
+       vuint32_t       fnr;
+       vuint32_t       daddr;
+       vuint32_t       btable;
+};
+
+/*
+ * USB DM: PA11
+ * USB DP: PA12
+ *
+ * Need a pull-up on a separate GPIO
+ */
+#define STM_USB_EPR_CTR_RX     15
+#define  STM_USB_EPR_CTR_RX_WRITE_INVARIANT            1
+#define STM_USB_EPR_DTOG_RX    14
+#define STM_USB_EPR_DTOG_RX_WRITE_INVARIANT            0
+#define STM_USB_EPR_STAT_RX    12
+#define  STM_USB_EPR_STAT_RX_DISABLED                  0
+#define  STM_USB_EPR_STAT_RX_STALL                     1
+#define  STM_USB_EPR_STAT_RX_NAK                       2
+#define  STM_USB_EPR_STAT_RX_VALID                     3
+#define  STM_USB_EPR_STAT_RX_MASK                      3UL
+#define  STM_USB_EPR_STAT_RX_WRITE_INVARIANT           0
+#define STM_USB_EPR_SETUP      11
+#define STM_USB_EPR_EP_TYPE    9
+#define  STM_USB_EPR_EP_TYPE_BULK                      0
+#define  STM_USB_EPR_EP_TYPE_CONTROL                   1
+#define  STM_USB_EPR_EP_TYPE_ISO                       2
+#define  STM_USB_EPR_EP_TYPE_INTERRUPT                 3
+#define  STM_USB_EPR_EP_TYPE_MASK                      3UL
+#define STM_USB_EPR_EP_KIND    8
+#define  STM_USB_EPR_EP_KIND_DBL_BUF                   1       /* Bulk */
+#define  STM_USB_EPR_EP_KIND_STATUS_OUT                        1       /* Control */
+#define STM_USB_EPR_CTR_TX     7
+#define  STM_USB_CTR_TX_WRITE_INVARIANT                        1
+#define STM_USB_EPR_DTOG_TX    6
+#define  STM_USB_EPR_DTOG_TX_WRITE_INVARIANT           0
+#define STM_USB_EPR_STAT_TX    4
+#define  STM_USB_EPR_STAT_TX_DISABLED                  0
+#define  STM_USB_EPR_STAT_TX_STALL                     1
+#define  STM_USB_EPR_STAT_TX_NAK                       2
+#define  STM_USB_EPR_STAT_TX_VALID                     3
+#define  STM_USB_EPR_STAT_TX_WRITE_INVARIANT           0
+#define  STM_USB_EPR_STAT_TX_MASK                      3UL
+#define STM_USB_EPR_EA         0
+#define  STM_USB_EPR_EA_MASK                           0xfUL
+
+#define STM_USB_CNTR_CTRM      15
+#define STM_USB_CNTR_PMAOVRM   14
+#define STM_USB_CNTR_ERRM      13
+#define STM_USB_CNTR_WKUPM     12
+#define STM_USB_CNTR_SUSPM     11
+#define STM_USB_CNTR_RESETM    10
+#define STM_USB_CNTR_SOFM      9
+#define STM_USB_CNTR_ESOFM     8
+#define STM_USB_CNTR_RESUME    4
+#define STM_USB_CNTR_FSUSP     3
+#define STM_USB_CNTR_LP_MODE   2
+#define STM_USB_CNTR_PDWN      1
+#define STM_USB_CNTR_FRES      0
+
+#define STM_USB_ISTR_CTR       15
+#define STM_USB_ISTR_PMAOVR    14
+#define STM_USB_ISTR_ERR       13
+#define STM_USB_ISTR_WKUP      12
+#define STM_USB_ISTR_SUSP      11
+#define STM_USB_ISTR_RESET     10
+#define STM_USB_ISTR_SOF       9
+#define STM_USB_ISTR_ESOF      8
+#define STM_USB_ISTR_DIR       4
+#define STM_USB_ISTR_EP_ID     0
+#define  STM_USB_ISTR_EP_ID_MASK               0xfUL
+
+#define STM_USB_FNR_RXDP       15
+#define STM_USB_FNR_RXDM       14
+#define STM_USB_FNR_LCK                13
+#define STM_USB_FNR_LSOF       11
+#define  STM_USB_FNR_LSOF_MASK                 0x3UL
+#define STM_USB_FNR_FN         0
+#define  STM_USB_FNR_FN_MASK                   0x7ffUL
+
+#define STM_USB_DADDR_EF       7
+#define STM_USB_DADDR_ADD      0
+#define  STM_USB_DADDR_ADD_MASK                        0x7fUL
+
+extern struct stm_usb stm_usb;
+
+
+//#define stm_usb (*((struct stm_usb *) 0x40005c00))
+
+union stm_usb_bdt {
+       struct {
+               vuint32_t       addr_tx;
+               vuint32_t       count_tx;
+               vuint32_t       addr_rx;
+               vuint32_t       count_rx;
+       } single;
+       struct {
+               vuint32_t       addr;
+               vuint32_t       count;
+       } double_tx[2];
+       struct {
+               vuint32_t       addr;
+               vuint32_t       count;
+       } double_rx[2];
+};
+
+#define STM_USB_BDT_COUNT_RX_BL_SIZE   15
+#define STM_USB_BDT_COUNT_RX_NUM_BLOCK 10
+#define  STM_USB_BDT_COUNT_RX_NUM_BLOCK_MASK   0x1fUL
+#define STM_USB_BDT_COUNT_RX_COUNT_RX  0
+#define  STM_USB_BDT_COUNT_RX_COUNT_RX_MASK    0x3ffUL
+
+#define STM_USB_BDT_SIZE       8
+
+extern uint8_t stm_usb_sram[] __attribute__ ((aligned(4)));
+
+//#define stm_usb_sram ((uint8_t *)0x40006000);
 
 #define isr_decl(name) void stm_ ## name ## _isr(void)
 
@@ -519,4 +829,65 @@ isr_decl(dma2_channel4_5);
 
 #undef isr_decl
 
+#define STM_ISR_WWDG_POS               0
+#define STM_ISR_PVD_POS                        1
+#define STM_ISR_TAMPER_STAMP_POS       2
+#define STM_ISR_RTC_WKUP_POS           3
+#define STM_ISR_FLASH_POS              4
+#define STM_ISR_RCC_POS                        5
+#define STM_ISR_EXTI0_POS              6
+#define STM_ISR_EXTI1_POS              7
+#define STM_ISR_EXTI2_POS              8
+#define STM_ISR_EXTI3_POS              9
+#define STM_ISR_EXTI4_POS              10
+#define STM_ISR_DMA1_CHANNEL1_POS      11
+#define STM_ISR_DMA1_CHANNEL2_POS      12
+#define STM_ISR_DMA1_CHANNEL3_POS      13
+#define STM_ISR_DMA1_CHANNEL4_POS      14
+#define STM_ISR_DMA1_CHANNEL5_POS      15
+#define STM_ISR_DMA1_CHANNEL6_POS      16
+#define STM_ISR_DMA1_CHANNEL7_POS      17
+#define STM_ISR_ADC1_2_POS             18
+#define STM_ISR_USB_HP_POS             19
+#define STM_ISR_USB_LP_POS             20
+#define STM_ISR_CAN_RX1_POS            21
+#define STM_ISR_CAN_SCE_POS            22
+#define STM_ISR_EXTI9_5_POS            23
+#define STM_ISR_TIM1_BRK_POS           24
+#define STM_ISR_TIM1_UP_POS            25
+#define STM_ISR_TIM1_TRG_COM_POS       26
+#define STM_ISR_TIM1_CC_POS            27
+#define STM_ISR_TIM2_POS               28
+#define STM_ISR_TIM3_POS               29
+#define STM_ISR_TIM4_POS               30
+#define STM_ISR_I2C1_EV_POS            31
+#define STM_ISR_I2C1_ER_POS            32
+#define STM_ISR_I2C2_EV_POS            33
+#define STM_ISR_I2C2_ER_POS            34
+#define STM_ISR_SPI1_POS               35
+#define STM_ISR_SPI2_POS               36
+#define STM_ISR_USART1_POS             37
+#define STM_ISR_USART2_POS             38
+#define STM_ISR_USART3_POS             39
+#define STM_ISR_EXTI15_10_POS          40
+#define STM_ISR_RTC_ALARM_POS          41
+#define STM_ISR_USB_WAKEUP_POS         42
+#define STM_ISR_TIM8_BRK_POS           43
+#define STM_ISR_TIM8_UP_POS            44
+#define STM_ISR_TIM8_TRG_COM_POS       45
+#define STM_ISR_TIM8_CC_POS            46
+#define STM_ISR_ADC3_POS               47
+#define STM_ISR_FSMC_POS               48
+#define STM_ISR_SDIO_POS               49
+#define STM_ISR_TIM5_POS               50
+#define STM_ISR_SPI3_POS               51
+#define STM_ISR_UART4_POS              52
+#define STM_ISR_UART5_POS              53
+#define STM_ISR_TIM6_POS               54
+#define STM_ISR_TIM7_POS               55
+#define STM_ISR_DMA2_CHANNEL1_POS      56
+#define STM_ISR_DMA2_CHANNEL2_POS      57
+#define STM_ISR_DMA2_CHANNEL3_POS      58
+#define STM_ISR_DMA3_CHANNEL4_5_POS    59
+
 #endif