11f441f897e5ada0b393fed94fb2b3a5be872616
[fw/altos] / src / drivers / ao_trng_send.c
1 /*
2  * Copyright © 2015 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_adc_fast.h>
20 #include <ao_crc.h>
21 #include <ao_trng_send.h>
22 #include <ao_exti.h>
23 #include <ao_power.h>
24
25 static uint8_t          trng_running;
26 static AO_TICK_TYPE     trng_power_time;
27
28 static void
29 ao_trng_send_raw(uint16_t *buf)
30 {
31         uint16_t        i;
32         uint16_t        t;
33         uint16_t        *rnd = (uint16_t *) ao_adc_ring;
34
35         t = ao_adc_get(AO_USB_IN_SIZE>>1);      /* one 16-bit value per two output bytes */
36         for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
37                 *buf++ = rnd[t];
38                 t = (t + 1) & (AO_ADC_RING_SIZE - 1);
39         }
40 }
41
42 static void
43 ao_trng_send_cooked(uint16_t *buf)
44 {
45         uint16_t        i;
46         uint16_t        t;
47         uint32_t        *rnd = (uint32_t *) ao_adc_ring;
48
49         t = ao_adc_get(AO_USB_IN_SIZE) >> 1;    /* two 16-bit values per two output bytes */
50         for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
51                 *buf++ = ao_crc_in_32_out_16(rnd[t]);
52                 t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1);
53         }
54 }
55
56 static inline int
57 ao_send_raw(void)
58 {
59         return !ao_gpio_get(AO_RAW_PORT, AO_RAW_BIT, AO_RAW_PIN);
60 }
61
62 static void
63 ao_trng_send(void)
64 {
65         static uint16_t *buffer[2];
66         int     usb_buf_id;
67
68         if (!buffer[0]) {
69                 buffer[0] = ao_usb_alloc();
70                 buffer[1] = ao_usb_alloc();
71                 if (!buffer[0])
72                         return;
73         }
74
75         usb_buf_id = 0;
76
77 #ifdef AO_TRNG_ENABLE_PORT
78         ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1);
79 #endif
80         trng_power_time = ao_time();
81
82         ao_crc_reset();
83
84         for (;;) {
85                 if (!trng_running) {
86                         AO_TICK_TYPE    delay;
87
88                         delay = trng_power_time + AO_MS_TO_TICKS(100) - ao_time();
89                         if (delay > AO_MS_TO_TICKS(100))
90                                 delay = AO_MS_TO_TICKS(100);
91
92                         /* Delay long enough for the HV power supply
93                          * to stabilize so that the first bits we read
94                          * aren't of poor quality
95                          */
96                         ao_delay(delay);
97                         trng_running = TRUE;
98                 }
99                 if (ao_send_raw()) {
100                         ao_led_on(AO_LED_TRNG_RAW);
101                         ao_trng_send_raw(buffer[usb_buf_id]);
102                         ao_led_off(AO_LED_TRNG_RAW);
103                 } else {
104                         ao_led_on(AO_LED_TRNG_COOKED);
105                         ao_trng_send_cooked(buffer[usb_buf_id]);
106                         ao_led_off(AO_LED_TRNG_COOKED);
107                 }
108                 ao_adc_ack(AO_USB_IN_SIZE);
109                 ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
110                 usb_buf_id = 1-usb_buf_id;
111         }
112 }
113
114 static struct ao_task ao_trng_send_task;
115
116 #if AO_POWER_MANAGEMENT
117
118 static void ao_trng_suspend(void *arg)
119 {
120         (void) arg;
121 #ifdef AO_TRNG_ENABLE_PORT
122         ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 0);
123 #endif
124         trng_running = FALSE;
125 }
126
127 static void ao_trng_resume(void *arg)
128 {
129         (void) arg;
130 #ifdef AO_TRNG_ENABLE_PORT
131         ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1);
132 #endif
133         trng_power_time = ao_time();
134 }
135
136 static struct ao_power ao_trng_power = {
137         .suspend = ao_trng_suspend,
138         .resume = ao_trng_resume
139 };
140
141 #endif
142
143 void
144 ao_trng_send_init(void)
145 {
146 #ifdef AO_TRNG_ENABLE_PORT
147         ao_enable_output(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 0);
148         ao_power_register(&ao_trng_power);
149 #endif
150         ao_enable_input(AO_RAW_PORT, AO_RAW_BIT, AO_EXTI_MODE_PULL_UP);
151         ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send");
152 }