#include <ao_exti.h>
#include <ao_power.h>
+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;
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)
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;
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;
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;
}
}
-static struct ao_task ao_trng_send_task;
-
#if AO_POWER_MANAGEMENT
static void ao_trng_suspend(void *arg)
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
*/
#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,
*/
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.
*/
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
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);
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;
}
}
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))
#endif
}
+#if AO_USB_HAS_IN
/* Queue the current IN buffer for transmission */
static void
_ao_usb_in_send(void)
}
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)
{
ao_arch_release_interrupts();
return c;
}
+#endif
#if AO_USB_DIRECTIO
uint16_t *
_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
/* 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();