From 02fd767ab60a9957faa2bff29c62ed954abc34e7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 19 Jan 2016 18:30:57 -0800 Subject: src/chaoskey-v0.1: Add HV enable support Turn on the HV supply when the OS starts. Signed-off-by: Keith Packard --- src/chaoskey-v0.1/ao_pins.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/chaoskey-v0.1/ao_pins.h') diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h index 72963dba..8f3eb7b1 100644 --- a/src/chaoskey-v0.1/ao_pins.h +++ b/src/chaoskey-v0.1/ao_pins.h @@ -57,6 +57,11 @@ #define AO_NUM_ADC 1 +/* HV enable */ + +#define AO_HV_ENABLE_PORT (&stm_gpioa) +#define AO_HV_ENABLE_BIT 8 + /* CRC */ #define AO_CRC_WIDTH 32 #define AO_CRC_INIT 0xffffffff -- cgit v1.2.3 From f2d3202de9a5847923f72afe2969eb7ccd7342c7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Jan 2016 00:14:36 -0800 Subject: altos/chaoskey: Add support for flipping between raw and cooked bits Plug the 'force bootloader' thing onto the board while it's running and it will generate raw bits instead of running them through the CRC to whiten. Useful for validating the raw hardware. Signed-off-by: Keith Packard --- ao-bringup/turnon_chaoskey | 50 +++++++++++++++++++++++++++++++++++++++++ src/chaoskey-v0.1/ao_pins.h | 7 +++++- src/drivers/ao_trng_send.c | 55 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 100 insertions(+), 12 deletions(-) create mode 100755 ao-bringup/turnon_chaoskey (limited to 'src/chaoskey-v0.1/ao_pins.h') diff --git a/ao-bringup/turnon_chaoskey b/ao-bringup/turnon_chaoskey new file mode 100755 index 00000000..8c8a6758 --- /dev/null +++ b/ao-bringup/turnon_chaoskey @@ -0,0 +1,50 @@ +#!/bin/sh + +if [ -x /usr/bin/ao-flash-stm32f0x ]; then + FLASH_STM=/usr/bin/ao-flash-stm32f0x +else + echo "Can't find ao-flash-stm32f0x! Aborting." + exit 1 +fi + +if [ -x /usr/bin/ao-usbload ]; then + USBLOAD=/usr/bin/ao-usbload +else + echo "Can't find ao-usbload! Aborting." + exit 1 +fi + +VERSION=0.1 +PRODUCT=ChaosKey + +echo "ChaosKey v$VERSION Turn-On and Calibration Program" +echo "Copyright 2015 by Keith Packard. Released under GPL v2" +echo +echo "Expectations:" +echo "\tChaosKey v$VERSION powered from USB" +echo "\t\twith ST-Link-V2 cabled to debug header" +echo + + +case $# in + 1) + SERIAL="$1" + echo "$PRODUCT-$VERSION serial number: $SERIAL" + ;; + 0) + echo -n "$PRODUCT-$VERSION serial number: " + read SERIAL + ;; + *) + echo "Usage: $0 " 1>&2 + exit 1; + ;; +esac + +$FLASH_STM ../src/chaoskey-v$VERSION/flash-loader/chaoskey-v$VERSION-*.elf || exit 1 + +sleep 2 + +$USBLOAD --serial=$SERIAL ../src/chaoskey-v$VERSION/chaoskey-v$VERSION*.ihx || exit 1 + +exit $? diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h index 8f3eb7b1..95d9a576 100644 --- a/src/chaoskey-v0.1/ao_pins.h +++ b/src/chaoskey-v0.1/ao_pins.h @@ -67,6 +67,11 @@ #define AO_CRC_INIT 0xffffffff /* TRNG */ -#define AO_LED_TRNG_ACTIVE AO_LED_GREEN +#define AO_LED_TRNG_COOKED AO_LED_GREEN +#define AO_LED_TRNG_RAW AO_LED_RED + +/* Mode pin */ +#define AO_RAW_PORT (&stm_gpioa) +#define AO_RAW_BIT 15 #endif /* _AO_PINS_H_ */ diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c index bac6035c..64c016b2 100644 --- a/src/drivers/ao_trng_send.c +++ b/src/drivers/ao_trng_send.c @@ -19,17 +19,48 @@ #include #include #include +#include static void -ao_trng_send(void) +ao_trng_send_raw(uint16_t *buf) +{ + uint16_t i; + uint16_t t; + uint16_t *rnd = (uint16_t *) ao_adc_ring; + + t = ao_adc_get(AO_USB_IN_SIZE>>1); /* one 16-bit value per two output bytes */ + for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { + *buf++ = rnd[t]; + t = (t + 1) & (AO_ADC_RING_SIZE - 1); + } +} + +static void +ao_trng_send_cooked(uint16_t *buf) { - static uint16_t *buffer[2]; - int usb_buf_id; uint16_t i; - uint16_t *buf; uint16_t t; uint32_t *rnd = (uint32_t *) ao_adc_ring; + t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ + for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { + *buf++ = ao_crc_in_32_out_16(rnd[t]); + t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1); + } +} + +static inline int +ao_send_raw(void) +{ + return !ao_gpio_get(AO_RAW_PORT, AO_RAW_BIT, AO_RAW_PIN); +} + +static void +ao_trng_send(void) +{ + static uint16_t *buffer[2]; + int usb_buf_id; + if (!buffer[0]) { buffer[0] = ao_usb_alloc(); buffer[1] = ao_usb_alloc(); @@ -42,15 +73,16 @@ ao_trng_send(void) ao_crc_reset(); for (;;) { - ao_led_on(AO_LED_TRNG_ACTIVE); - t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ - buf = buffer[usb_buf_id]; - for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { - *buf++ = ao_crc_in_32_out_16(rnd[t]); - t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1); + if (ao_send_raw()) { + ao_led_on(AO_LED_TRNG_RAW); + ao_trng_send_raw(buffer[usb_buf_id]); + ao_led_off(AO_LED_TRNG_RAW); + } else { + ao_led_on(AO_LED_TRNG_COOKED); + ao_trng_send_cooked(buffer[usb_buf_id]); + ao_led_off(AO_LED_TRNG_COOKED); } ao_adc_ack(AO_USB_IN_SIZE); - ao_led_off(AO_LED_TRNG_ACTIVE); ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); usb_buf_id = 1-usb_buf_id; } @@ -61,5 +93,6 @@ static struct ao_task ao_trng_send_task; void ao_trng_send_init(void) { + ao_enable_input(AO_RAW_PORT, AO_RAW_BIT, AO_EXTI_MODE_PULL_UP); ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send"); } -- cgit v1.2.3 From 1473f9234ffd34d8f37bc489dfc9fc4d7f1b3eed Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 7 Feb 2016 00:15:47 +1100 Subject: altos/chaoskey-v0.1: Add power management and change USB classes Enable power management. Expose only a single IN endpoint. Signed-off-by: Keith Packard --- src/chaoskey-v0.1/Makefile | 3 +++ src/chaoskey-v0.1/ao_chaoskey.c | 8 -------- src/chaoskey-v0.1/ao_pins.h | 24 ++++++++++++++++-------- 3 files changed, 19 insertions(+), 16 deletions(-) (limited to 'src/chaoskey-v0.1/ao_pins.h') diff --git a/src/chaoskey-v0.1/Makefile b/src/chaoskey-v0.1/Makefile index ac4a6788..4948a4c5 100644 --- a/src/chaoskey-v0.1/Makefile +++ b/src/chaoskey-v0.1/Makefile @@ -13,6 +13,7 @@ INC = \ ao_product.h \ ao_task.h \ ao_adc_fast.h \ + ao_power.h \ stm32f0.h # @@ -33,6 +34,8 @@ ALTOS_SRC = \ ao_usb_stm.c \ ao_trng_send.c \ ao_task.c \ + ao_power.c \ + ao_gpio.c \ ao_product.c PRODUCT=ChaosKey-v0.1 diff --git a/src/chaoskey-v0.1/ao_chaoskey.c b/src/chaoskey-v0.1/ao_chaoskey.c index c9490184..48c8bf04 100644 --- a/src/chaoskey-v0.1/ao_chaoskey.c +++ b/src/chaoskey-v0.1/ao_chaoskey.c @@ -20,12 +20,6 @@ #include #include -static void -ao_hv_init(void) -{ - ao_enable_output(AO_HV_ENABLE_PORT, AO_HV_ENABLE_BIT, AO_HV_ENABLE_PIN, 1); -} - void main(void) { ao_led_init(LEDS_AVAILABLE); @@ -39,8 +33,6 @@ void main(void) ao_usb_init(); - ao_hv_init(); - ao_trng_send_init(); ao_led_off(AO_LED_RED); diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h index 95d9a576..a8109282 100644 --- a/src/chaoskey-v0.1/ao_pins.h +++ b/src/chaoskey-v0.1/ao_pins.h @@ -27,6 +27,9 @@ #define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN) +#define AO_POWER_MANAGEMENT 1 +#define AO_LED_POWER AO_LED_RED + #define HAS_BEEP 0 /* 48MHz clock based on USB */ @@ -40,10 +43,15 @@ #define AO_APB_PRESCALER 1 #define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1 -#define HAS_USB 1 -#define AO_USB_DIRECTIO 1 -#define AO_PA11_PA12_RMP 0 -#define AO_USB_INTERFACE_CLASS 0xff +#define HAS_USB 1 +#define AO_USB_DIRECTIO 1 +#define AO_PA11_PA12_RMP 0 +#define AO_USB_DEVICE_CLASS 0xff +#define AO_USB_INTERFACE_CLASS_DATA 0xff +#define AO_USB_HAS_OUT 0 +#define AO_USB_HAS_IN 1 +#define AO_USB_HAS_INT 0 +#define AO_USB_SELF_POWER 0 #define IS_FLASH_LOADER 0 @@ -57,10 +65,10 @@ #define AO_NUM_ADC 1 -/* HV enable */ +/* TRNG enable */ -#define AO_HV_ENABLE_PORT (&stm_gpioa) -#define AO_HV_ENABLE_BIT 8 +#define AO_TRNG_ENABLE_PORT (&stm_gpioa) +#define AO_TRNG_ENABLE_BIT 8 /* CRC */ #define AO_CRC_WIDTH 32 @@ -68,7 +76,7 @@ /* TRNG */ #define AO_LED_TRNG_COOKED AO_LED_GREEN -#define AO_LED_TRNG_RAW AO_LED_RED +#define AO_LED_TRNG_RAW AO_LED_GREEN /* Mode pin */ #define AO_RAW_PORT (&stm_gpioa) -- cgit v1.2.3 From 05354b8fee6a9af05d66bb7f4761f597da038fdd Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 15 Feb 2016 15:26:00 -0800 Subject: altos/chaoskey: Use SoC device ID as serial number To make manufacturing these devices tractable, we don't want to require a custom firmware load for each device, but we still want a unique serial number. Fortunately, the SoC provides a 96-bit ID which we can use. Signed-off-by: Keith Packard --- src/chaoskey-v0.1/ao_pins.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/chaoskey-v0.1/ao_pins.h') diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h index a8109282..6ec582a9 100644 --- a/src/chaoskey-v0.1/ao_pins.h +++ b/src/chaoskey-v0.1/ao_pins.h @@ -52,6 +52,7 @@ #define AO_USB_HAS_IN 1 #define AO_USB_HAS_INT 0 #define AO_USB_SELF_POWER 0 +#define AO_USB_DEVICE_ID_SERIAL 1 #define IS_FLASH_LOADER 0 -- cgit v1.2.3 From 05fcb717bfc44aba3c1cfd43281e323505a46402 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 2 Mar 2016 13:54:58 -0800 Subject: altos/chaoskey: Add another USB endpoint to read raw data This replaces having the single output switch based on a pin value and allows us to box the device and still fetch raw data. For now, this will use a special libusb2 program, ao-chaosread, to pull bits as I haven't figure out how to make linux provide two /dev entries for one USB device. Signed-off-by: Keith Packard --- src/chaoskey-v0.1/ao_pins.h | 2 + src/drivers/ao_trng_send.c | 149 +++++++++++++++++++++++---------- src/kernel/ao_product.c | 14 +++- src/kernel/ao_usb.h | 5 ++ src/stmf0/ao_arch_funcs.h | 3 + src/stmf0/ao_usb_stm.c | 195 +++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 321 insertions(+), 47 deletions(-) (limited to 'src/chaoskey-v0.1/ao_pins.h') diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h index 6ec582a9..73f76307 100644 --- a/src/chaoskey-v0.1/ao_pins.h +++ b/src/chaoskey-v0.1/ao_pins.h @@ -50,7 +50,9 @@ #define AO_USB_INTERFACE_CLASS_DATA 0xff #define AO_USB_HAS_OUT 0 #define AO_USB_HAS_IN 1 +#define AO_USB_HAS_IN2 1 #define AO_USB_HAS_INT 0 +#define USE_USB_STDIO 0 #define AO_USB_SELF_POWER 0 #define AO_USB_DEVICE_ID_SERIAL 1 diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c index f8222993..171a345f 100644 --- a/src/drivers/ao_trng_send.c +++ b/src/drivers/ao_trng_send.c @@ -22,54 +22,97 @@ #include #include +static struct ao_task ao_trng_send_task, ao_trng_send_raw_task; static uint8_t trng_running; static AO_TICK_TYPE trng_power_time; -/* Make sure there's at least 8 bits of variance in the samples */ -#define MIN_VARIANCE (128 * 128) - -#define DECLARE_STATS int32_t sum = 0, sum2 = 0 - -#define ADD_STATS(value) do { \ - sum += (value); \ - sum2 += (value) * (value); \ - } while(0) - -#define GOOD_STATS(i) (((sum2 - (sum * sum) / i) / i) >= MIN_VARIANCE) - #define TRNG_ENABLE_DELAY AO_MS_TO_TICKS(100) -static int -ao_trng_send_raw(uint16_t *buf) +static uint8_t random_mutex; + +static void +ao_trng_get_raw(uint16_t *buf) { uint16_t i; uint16_t t; uint16_t v; - DECLARE_STATS; - t = ao_adc_get(AO_USB_IN_SIZE>>1); /* one 16-bit value per two output bytes */ for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { v = ao_adc_ring[t]; *buf++ = v; t = (t + 1) & (AO_ADC_RING_SIZE - 1); + } + ao_adc_ack(AO_USB_IN_SIZE>>1); +} + +static void +ao_trng_send_raw(void) +{ + static uint16_t *buffer[2]; + int usb_buf_id; - ADD_STATS(v); + if (!buffer[0]) { + buffer[0] = ao_usb_alloc(); + buffer[1] = ao_usb_alloc(); + if (!buffer[0]) + ao_exit(); + } + + usb_buf_id = 0; + + for (;;) { + ao_mutex_get(&random_mutex); + if (!trng_running) { + AO_TICK_TYPE delay; + + delay = trng_power_time + TRNG_ENABLE_DELAY - ao_time(); + if (delay > TRNG_ENABLE_DELAY) + delay = TRNG_ENABLE_DELAY; + + /* Delay long enough for the HV power supply + * to stabilize so that the first bits we read + * aren't of poor quality + */ + ao_delay(delay); + trng_running = TRUE; + } +#ifdef AO_LED_TRNG_RAW + ao_led_on(AO_LED_TRNG_RAW); +#endif + ao_trng_get_raw(buffer[usb_buf_id]); +#ifdef AO_LED_TRNG_RAW + ao_led_off(AO_LED_TRNG_RAW); +#endif + ao_mutex_put(&random_mutex); + ao_usb_write2(buffer[usb_buf_id], AO_USB_IN_SIZE); + usb_buf_id = 1-usb_buf_id; } - return GOOD_STATS(AO_USB_IN_SIZE / sizeof (uint16_t)); } +/* Make sure there's at least 8 bits of variance in the samples */ +#define MIN_VARIANCE (128 * 128) + +/* Make sure the signal is spread around a bit */ +#define MAX_VARIANCE (512 * 512) + +#define ADD_STATS(value) do { \ + sum += (value); \ + sum2 += (value) * (value); \ + } while(0) + +#define VARIANCE(n) ((sum2 - (sum / (n) * sum)) / (n)) + static int -ao_trng_send_cooked(uint16_t *buf) +ao_trng_get_cooked(uint16_t *buf) { uint16_t i; uint16_t t; uint32_t *rnd = (uint32_t *) ao_adc_ring; + int32_t sum, sum2, var; - DECLARE_STATS; - + sum = sum2 = 0; t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ - for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { uint32_t v; uint16_t v1, v2; @@ -86,14 +129,13 @@ ao_trng_send_cooked(uint16_t *buf) ADD_STATS(v1); ADD_STATS(v2); } - return GOOD_STATS(2 * AO_USB_IN_SIZE / sizeof (uint16_t)); + ao_adc_ack(AO_USB_IN_SIZE); + var = VARIANCE(2 * AO_USB_IN_SIZE / sizeof (uint16_t)); + return var >= MIN_VARIANCE && var <= MAX_VARIANCE; } -static inline int -ao_send_raw(void) -{ - return !ao_gpio_get(AO_RAW_PORT, AO_RAW_BIT, AO_RAW_PIN); -} +#define AO_TRNG_START_WAIT 1024 +#define AO_TRNG_START_CHECK 32 static void ao_trng_send(void) @@ -101,13 +143,14 @@ ao_trng_send(void) static uint16_t *buffer[2]; int usb_buf_id; int good_bits; - int failed = 0; + int failed; + int s; if (!buffer[0]) { buffer[0] = ao_usb_alloc(); buffer[1] = ao_usb_alloc(); if (!buffer[0]) - return; + ao_exit(); } usb_buf_id = 0; @@ -119,7 +162,33 @@ ao_trng_send(void) ao_crc_reset(); + ao_delay(TRNG_ENABLE_DELAY); + + for (s = 0; s < AO_TRNG_START_WAIT; s++) { + if (ao_trng_get_cooked(buffer[0])) + break; + ao_delay(AO_MS_TO_TICKS(10)); + } + + /* Validate the hardware before enabling USB */ + failed = 0; + for (s = 0; s < AO_TRNG_START_CHECK; s++) { + if (!ao_trng_get_cooked(buffer[0])) { + failed++; + ao_delay(AO_MS_TO_TICKS(10)); + } + } + if (failed > AO_TRNG_START_CHECK / 4) + ao_panic(AO_PANIC_DMA); + + ao_add_task(&ao_trng_send_raw_task, ao_trng_send_raw, "trng_send_raw"); + +#ifdef AO_USB_START_DISABLED + ao_usb_enable(); +#endif + for (;;) { + ao_mutex_get(&random_mutex); if (!trng_running) { AO_TICK_TYPE delay; @@ -134,16 +203,14 @@ ao_trng_send(void) ao_delay(delay); trng_running = TRUE; } - if (ao_send_raw()) { - ao_led_on(AO_LED_TRNG_RAW); - good_bits = ao_trng_send_raw(buffer[usb_buf_id]); - ao_led_off(AO_LED_TRNG_RAW); - } else { - ao_led_on(AO_LED_TRNG_COOKED); - good_bits = ao_trng_send_cooked(buffer[usb_buf_id]); - ao_led_off(AO_LED_TRNG_COOKED); - } - ao_adc_ack(AO_USB_IN_SIZE); +#ifdef AO_LED_TRNG_COOKED + ao_led_on(AO_LED_TRNG_COOKED); +#endif + good_bits = ao_trng_get_cooked(buffer[usb_buf_id]); +#ifdef AO_LED_TRNG_COOKED + ao_led_off(AO_LED_TRNG_COOKED); +#endif + ao_mutex_put(&random_mutex); if (good_bits) { ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); usb_buf_id = 1-usb_buf_id; @@ -159,8 +226,6 @@ ao_trng_send(void) } } -static struct ao_task ao_trng_send_task; - #if AO_POWER_MANAGEMENT static void ao_trng_suspend(void *arg) diff --git a/src/kernel/ao_product.c b/src/kernel/ao_product.c index 4769d86e..3a829b3a 100644 --- a/src/kernel/ao_product.c +++ b/src/kernel/ao_product.c @@ -54,7 +54,7 @@ const char ao_product[] = AO_iProduct_STRING; #define HEADER_LEN 9 #define CONTROL_CLASS_LEN 35 -#define DATA_LEN (9 + 7 * AO_USB_HAS_OUT + 7 * AO_USB_HAS_IN) +#define DATA_LEN (9 + 7 * AO_USB_HAS_OUT + 7 * AO_USB_HAS_IN + 7 * AO_USB_HAS_IN2) #define TOTAL_LENGTH (HEADER_LEN + AO_USB_HAS_INT * CONTROL_CLASS_LEN + DATA_LEN) #define NUM_INTERFACES (AO_USB_HAS_INT + 1) @@ -140,7 +140,7 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] = AO_USB_DESC_INTERFACE, AO_USB_HAS_INT, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ - AO_USB_HAS_OUT + AO_USB_HAS_IN, /* bNumEndPoints */ + AO_USB_HAS_OUT + AO_USB_HAS_IN + AO_USB_HAS_IN2, /* bNumEndPoints */ AO_USB_INTERFACE_CLASS_DATA, /* bInterfaceClass = data */ 0x00, /* bInterfaceSubClass */ 0x00, /* bInterfaceProtocol */ @@ -166,6 +166,16 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] = 0x00, /* bInterval */ #endif +#if AO_USB_HAS_IN2 + /* Data EP in 2 */ + 0x07, + AO_USB_DESC_ENDPOINT, + AO_USB_IN2_EP|0x80, /* bEndpointAddress */ + 0x02, /* bmAttributes = bulk */ + LE_WORD(AO_USB_IN_SIZE),/* wMaxPacketSize */ + 0x00, /* bInterval */ +#endif + /* String descriptors */ 0x04, AO_USB_DESC_STRING, diff --git a/src/kernel/ao_usb.h b/src/kernel/ao_usb.h index 773b5cb7..8f3e7813 100644 --- a/src/kernel/ao_usb.h +++ b/src/kernel/ao_usb.h @@ -105,6 +105,7 @@ extern __code __at (0x00aa) uint8_t ao_usb_descriptors []; #ifndef AO_USB_OUT_EP #define AO_USB_OUT_EP 4 #define AO_USB_IN_EP 5 +#define AO_USB_IN2_EP 6 #endif #ifndef AO_USB_HAS_OUT @@ -119,6 +120,10 @@ extern __code __at (0x00aa) uint8_t ao_usb_descriptors []; #define AO_USB_HAS_INT 1 #endif +#ifndef AO_USB_HAS_IN2 +#define AO_USB_HAS_IN2 0 +#endif + /* * USB bulk packets can only come in 8, 16, 32 and 64 * byte sizes, so we'll use 64 for everything diff --git a/src/stmf0/ao_arch_funcs.h b/src/stmf0/ao_arch_funcs.h index 8d585f80..4e3ef018 100644 --- a/src/stmf0/ao_arch_funcs.h +++ b/src/stmf0/ao_arch_funcs.h @@ -413,6 +413,9 @@ ao_usb_free(uint16_t *buffer); void ao_usb_write(uint16_t *buffer, uint16_t len); + +void +ao_usb_write2(uint16_t *buffer, uint16_t len); #endif /* AO_USB_DIRECTIO */ #endif /* _AO_ARCH_FUNCS_H_ */ diff --git a/src/stmf0/ao_usb_stm.c b/src/stmf0/ao_usb_stm.c index 17e8709c..e68da8eb 100644 --- a/src/stmf0/ao_usb_stm.c +++ b/src/stmf0/ao_usb_stm.c @@ -89,23 +89,42 @@ static uint16_t ao_usb_sram_addr; static uint16_t *ao_usb_ep0_tx_buffer; static uint16_t *ao_usb_ep0_rx_buffer; +#if AO_USB_HAS_INT /* Pointer to interrupt buffer in USB memory */ static uint16_t ao_usb_int_tx_offset; +#endif /* Pointer to bulk data tx/rx buffers in USB memory */ +#if AO_USB_HAS_IN static uint16_t ao_usb_in_tx_offset; static uint16_t *ao_usb_in_tx_buffer; -static uint16_t ao_usb_out_rx_offset; -static uint16_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; +#endif +#if AO_USB_HAS_OUT +static uint16_t ao_usb_out_rx_offset; +static uint16_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_rx_buffer[AO_USB_OUT_SIZE]; static uint8_t ao_usb_rx_count, ao_usb_rx_pos; +#endif +#if AO_USB_HAS_IN2 +static uint16_t ao_usb_in2_tx_offset; +static uint16_t *ao_usb_in2_tx_buffer; + +/* System ram shadow of USB buffer; writing individual bytes is + * too much of a pain (sigh) */ +static uint8_t ao_usb_tx2_buffer[AO_USB_IN_SIZE]; +static uint8_t ao_usb_tx2_count; +#endif + /* * End point register indices */ @@ -114,6 +133,7 @@ static uint8_t ao_usb_rx_count, ao_usb_rx_pos; #define AO_USB_INT_EPR 1 #define AO_USB_OUT_EPR 2 #define AO_USB_IN_EPR 3 +#define AO_USB_IN2_EPR 4 /* Marks when we don't need to send an IN packet. * This happens only when the last IN packet is not full, @@ -128,6 +148,16 @@ static uint8_t ao_usb_in_flushed; */ static uint8_t ao_usb_in_pending; +#if AO_USB_HAS_IN2 +/* 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_in2_pending; +static uint16_t in2_count; +static uint8_t ao_usb_in2_flushed; +#endif + /* Marks when an OUT packet has been received by the hardware * but not pulled to the shadow buffer. */ @@ -343,16 +373,29 @@ ao_usb_alloc_buffers(void) ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_sram_addr += AO_USB_CONTROL_SIZE; + +#if AO_USB_HAS_INT ao_usb_int_tx_offset = ao_usb_sram_addr; ao_usb_sram_addr += AO_USB_INT_SIZE; +#endif +#if AO_USB_HAS_OUT ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_out_rx_offset = ao_usb_sram_addr; ao_usb_sram_addr += AO_USB_OUT_SIZE; +#endif +#if AO_USB_HAS_IN ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_in_tx_offset = ao_usb_sram_addr; ao_usb_sram_addr += AO_USB_IN_SIZE; +#endif + +#if AO_USB_HAS_IN2 + ao_usb_in2_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); + ao_usb_in2_tx_offset = ao_usb_sram_addr; + ao_usb_sram_addr += AO_USB_IN_SIZE; +#endif } static void @@ -438,6 +481,18 @@ ao_usb_set_configuration(void) STM_USB_EPR_STAT_TX_NAK); #endif +#if AO_USB_HAS_IN2 + /* Set up the IN2 end point */ + ao_usb_bdt[AO_USB_IN2_EPR].single.addr_tx = ao_usb_in2_tx_offset; + ao_usb_bdt[AO_USB_IN2_EPR].single.count_tx = 0; + + ao_usb_init_ep(AO_USB_IN2_EPR, + AO_USB_IN2_EP, + STM_USB_EPR_EP_TYPE_BULK, + STM_USB_EPR_STAT_RX_DISABLED, + STM_USB_EPR_STAT_TX_NAK); +#endif + ao_usb_running = 1; #if AO_USB_DIRECTIO ao_wakeup(&ao_usb_running); @@ -581,7 +636,7 @@ hex_to_ucs2(uint32_t in, uint8_t *out) for (i = 28; i >= 0; i -= 4) { uint8_t bits = (in >> i) & 0xf; - *out++ = (bits < 10) ? ('0' + bits) : ('a' + bits); + *out++ = ((bits < 10) ? '0' : ('a' - 10)) + bits; *out++ = 0; } } @@ -835,6 +890,16 @@ stm_usb_isr(void) ao_wakeup(&ao_usb_in_pending); } break; +#if AO_USB_HAS_IN2 + case AO_USB_IN2_EPR: + ++in2_count; + _tx_dbg1("TX2 ISR", epr); + if (ao_usb_epr_ctr_tx(epr)) { + ao_usb_in2_pending = 0; + ao_wakeup(&ao_usb_in2_pending); + } + break; +#endif case AO_USB_INT_EPR: ++int_count; if (ao_usb_epr_ctr_tx(epr)) @@ -861,6 +926,7 @@ stm_usb_isr(void) #endif } +#if AO_USB_HAS_IN /* Queue the current IN buffer for transmission */ static void _ao_usb_in_send(void) @@ -939,7 +1005,90 @@ ao_usb_putchar(char c) } ao_arch_release_interrupts(); } +#endif + +#if AO_USB_HAS_IN +/* Queue the current IN buffer for transmission */ +static void +_ao_usb_in2_send(void) +{ + _tx_dbg0("in2_send start"); + debug ("send2 %d\n", ao_usb_tx_count); + while (ao_usb_in2_pending) + ao_sleep(&ao_usb_in2_pending); + ao_usb_in2_pending = 1; + if (ao_usb_tx2_count != AO_USB_IN_SIZE) + ao_usb_in2_flushed = 1; + ao_usb_copy_tx(ao_usb_tx2_buffer, ao_usb_in2_tx_buffer, ao_usb_tx2_count); + ao_usb_bdt[AO_USB_IN2_EPR].single.addr_tx = ao_usb_in_tx_offset; + ao_usb_bdt[AO_USB_IN2_EPR].single.count_tx = ao_usb_tx_count; + ao_usb_tx2_count = 0; + _ao_usb_set_stat_tx(AO_USB_IN2_EPR, STM_USB_EPR_STAT_TX_VALID); + _tx_dbg0("in2_send end"); +} + +/* Wait for a free IN buffer. Interrupts are blocked */ +static void +_ao_usb_in2_wait(void) +{ + for (;;) { + /* Check if the current buffer is writable */ + if (ao_usb_tx2_count < AO_USB_IN_SIZE) + break; + + _tx_dbg0("in2_wait top"); + /* Wait for an IN buffer to be ready */ + while (ao_usb_in2_pending) + ao_sleep(&ao_usb_in2_pending); + _tx_dbg0("in_wait bottom"); + } +} + +void +ao_usb_flush2(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_in2_flushed) { + _tx_dbg0("flush2 top"); + _ao_usb_in2_send(); + _tx_dbg0("flush2 end"); + } + ao_arch_release_interrupts(); +} + +void +ao_usb_putchar2(char c) +{ + if (!ao_usb_running) + return; + + ao_arch_block_interrupts(); + _ao_usb_in2_wait(); + + ao_usb_in2_flushed = 0; + ao_usb_tx2_buffer[ao_usb_tx2_count++] = (uint8_t) c; + + /* Send the packet when full */ + if (ao_usb_tx2_count == AO_USB_IN_SIZE) { + _tx_dbg0("putchar2 full"); + _ao_usb_in2_send(); + _tx_dbg0("putchar2 flushed"); + } + ao_arch_release_interrupts(); +} +#endif +#if AO_USB_HAS_OUT static void _ao_usb_out_recv(void) { @@ -996,6 +1145,7 @@ ao_usb_getchar(void) ao_arch_release_interrupts(); return c; } +#endif #if AO_USB_DIRECTIO uint16_t * @@ -1050,6 +1200,43 @@ ao_usb_write(uint16_t *buffer, uint16_t len) _ao_usb_set_stat_tx(AO_USB_IN_EPR, STM_USB_EPR_STAT_TX_VALID); ao_arch_release_interrupts(); } + +#if AO_USB_HAS_IN2 +void +ao_usb_write2(uint16_t *buffer, uint16_t len) +{ + ao_arch_block_interrupts(); + + /* Wait for everything to be ready at the same time */ + for (;;) { + /* Make sure USB is connected */ + if (!ao_usb_running) { + ao_sleep(&ao_usb_running); + continue; + } + + /* Flush any pending regular I/O */ + if (ao_usb_tx2_count) { + _ao_usb_in2_send(); + continue; + } + + /* Wait for an idle IN buffer */ + if (ao_usb_in2_pending) { + ao_sleep(&ao_usb_in2_pending); + continue; + } + break; + } + + ao_usb_in2_pending = 1; + ao_usb_in2_flushed = (len != AO_USB_IN_SIZE); + ao_usb_bdt[AO_USB_IN2_EPR].single.addr_tx = ao_usb_packet_buffer_offset(buffer); + ao_usb_bdt[AO_USB_IN2_EPR].single.count_tx = len; + _ao_usb_set_stat_tx(AO_USB_IN2_EPR, STM_USB_EPR_STAT_TX_VALID); + ao_arch_release_interrupts(); +} +#endif #endif void @@ -1180,7 +1367,9 @@ ao_usb_init(void) /* Set PA11/PA12 remapping bit */ stm_syscfg.cfgr1 |= (AO_PA11_PA12_RMP << STM_SYSCFG_CFGR1_PA11_PA12_RMP); +#ifndef AO_USB_START_DISABLED ao_usb_enable(); +#endif #if AO_USB_DEVICE_ID_SERIAL ao_usb_serial_init(); -- cgit v1.2.3