altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / src / drivers / ao_radio_master.c
1 /*
2  * Copyright © 2012 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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <ao.h>
20 #include <ao_radio_spi.h>
21 #include <ao_exti.h>
22 #include <ao_radio_cmac.h>
23
24 static struct ao_radio_spi_reply        ao_radio_spi_reply;
25 static struct ao_radio_spi_request      ao_radio_spi_request;
26 static volatile uint8_t                 ao_radio_wait_mode;
27 static volatile uint8_t                 ao_radio_done = 0;
28 static volatile uint8_t                 ao_radio_ready = 1;
29 static uint8_t                          ao_radio_mutex;
30 static uint8_t                          ao_radio_aes_seq;
31
32 int8_t                                  ao_radio_cmac_rssi;
33
34 #if 0
35 #define PRINTD(...) do { printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
36 #else
37 #define PRINTD(...) 
38 #endif
39
40 static void
41 ao_radio_isr(void)
42 {
43         if (ao_gpio_get(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN, AO_RADIO_INT)) {
44                 ao_radio_ready = 1;
45                 ao_wakeup((void *) &ao_radio_ready);
46         } else {
47                 ao_radio_done = 1;
48                 ao_wakeup((void *) &ao_radio_done);
49         }
50 }
51
52 static void
53 ao_radio_master_start(void)
54 {
55         ao_spi_get_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
56                        AO_RADIO_SPI_BUS,
57                        AO_SPI_SPEED_2MHz);
58 }
59
60 static void
61 ao_radio_master_stop(void)
62 {
63         ao_spi_put_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
64                        AO_RADIO_SPI_BUS);
65 }
66
67 static uint8_t
68 ao_radio_master_send(void)
69 {
70         uint8_t ret;
71
72         PRINTD("send %d\n", ao_radio_spi_request.len);
73         ao_radio_done = 0;
74
75         /* Wait for radio chip to be ready for a command
76          */
77
78         PRINTD("Waiting radio ready\n");
79         ao_arch_block_interrupts();
80         ao_radio_ready = ao_gpio_get(AO_RADIO_INT_PORT,
81                                      AO_RADIO_INT_PIN, AO_RADIO_INT);
82         ret = 0;
83         while (!ao_radio_ready) {
84                 ret = ao_sleep((void *) &ao_radio_ready);
85                 if (ret)
86                         break;
87         }
88         ao_arch_release_interrupts();
89         if (ret)
90                 return 0;
91
92         PRINTD("radio_ready %d radio_done %d\n", ao_radio_ready, ao_radio_done);
93
94         /* Send the command
95          */
96         ao_radio_wait_mode = 0;
97         ao_radio_master_start();
98         ao_spi_send(&ao_radio_spi_request,
99                     ao_radio_spi_request.len,
100                     AO_RADIO_SPI_BUS);
101         ao_radio_master_stop();
102         PRINTD("waiting for send done %d\n", ao_radio_done);
103         ao_arch_block_interrupts();
104         while (!ao_radio_done)
105                 if (ao_sleep((void *) &ao_radio_done))
106                         break;
107         ao_arch_release_interrupts();
108         PRINTD ("sent, radio done %d isr_0 %d isr_1 %d\n", ao_radio_done, isr_0_count, isr_1_count);
109         return ao_radio_done;
110 }
111
112 static void
113 ao_radio_get(uint8_t req, uint8_t len)
114 {
115         ao_config_get();
116         ao_mutex_get(&ao_radio_mutex);
117         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN + len;
118         ao_radio_spi_request.request = req;
119         ao_radio_spi_request.setting = ao_config.radio_setting;
120 }
121
122 static void
123 ao_radio_put(void)
124 {
125         ao_mutex_put(&ao_radio_mutex);
126 }
127
128 static void
129 ao_radio_get_data(void *d, uint8_t size)
130 {
131         PRINTD ("fetch\n");
132         ao_radio_master_start();
133         ao_spi_recv(&ao_radio_spi_reply,
134                     AO_RADIO_SPI_REPLY_HEADER_LEN + size,
135                     AO_RADIO_SPI_BUS);
136         ao_radio_master_stop();
137         ao_xmemcpy(d, ao_radio_spi_reply.payload, size);
138         PRINTD ("fetched %d\n", size);
139 }
140
141 void
142 ao_radio_recv_abort(void)
143 {
144         ao_radio_get(AO_RADIO_SPI_RECV_ABORT, 0);
145         ao_radio_master_send();
146         ao_radio_put();
147 }
148
149 void
150 ao_radio_send(const void *d, uint8_t size)
151 {
152         ao_radio_get(AO_RADIO_SPI_SEND, size);
153         ao_xmemcpy(&ao_radio_spi_request.payload, d, size);
154         ao_radio_master_send();
155         ao_radio_put();
156 }
157
158
159 uint8_t
160 ao_radio_recv(void *d, uint8_t size, uint8_t timeout)
161 {
162         int8_t  ret;
163         uint8_t recv;
164
165         /* Recv the data
166          */
167         
168         ao_radio_get(AO_RADIO_SPI_RECV, 0);
169         ao_radio_spi_request.recv_len = size;
170         ao_radio_spi_request.timeout = timeout;
171         recv = ao_radio_master_send();
172         if (!recv) {
173                 ao_radio_put();
174                 ao_radio_recv_abort();
175                 return 0;
176         }
177         ao_radio_get_data(d, size);
178         recv = ao_radio_spi_reply.status;
179         ao_radio_put();
180
181         return recv;
182 }
183
184 static void
185 ao_radio_cmac_set_key(void)
186 {
187         if (ao_radio_aes_seq == ao_config_aes_seq)
188                 return;
189         /* Set the key.
190          */
191         PRINTD ("set key\n");
192         ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN);
193         ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN);
194         ao_radio_master_send();
195         ao_radio_put();
196         PRINTD ("key set\n");
197         ao_radio_aes_seq = ao_config_aes_seq;
198 }
199
200 int8_t
201 ao_radio_cmac_send(void *packet, uint8_t len) 
202 {
203         if (len > AO_CMAC_MAX_LEN)
204                 return AO_RADIO_CMAC_LEN_ERROR;
205
206         ao_radio_cmac_set_key();
207
208         PRINTD ("cmac_send: send %d\n", len);
209
210         /* Send the data
211          */
212         
213         PRINTD ("sending packet\n");
214         ao_radio_get(AO_RADIO_SPI_CMAC_SEND, len);
215         ao_xmemcpy(&ao_radio_spi_request.payload, packet, len);
216         ao_radio_master_send();
217         ao_radio_put();
218         PRINTD ("packet sent\n");
219         return AO_RADIO_CMAC_OK;
220 }
221
222 int8_t
223 ao_radio_cmac_recv(void *packet, uint8_t len, uint16_t timeout) 
224 {
225         int8_t  ret;
226         int8_t  recv;
227
228         if (len > AO_CMAC_MAX_LEN)
229                 return AO_RADIO_CMAC_LEN_ERROR;
230
231         ao_radio_cmac_set_key();
232
233         /* Recv the data
234          */
235         PRINTD ("queuing recv\n");
236         ao_radio_get(AO_RADIO_SPI_CMAC_RECV, 0);
237         ao_radio_spi_request.recv_len = len;
238         ao_radio_spi_request.timeout = timeout;
239         recv = ao_radio_master_send();
240         PRINTD ("recv queued: %d\n", recv);
241         if (!recv) {
242                 ao_radio_put();
243                 ao_radio_recv_abort();
244                 return AO_RADIO_CMAC_TIMEOUT;
245         }
246
247         PRINTD ("fetching data\n");
248         ao_radio_get_data(packet, len);
249         recv = ao_radio_spi_reply.status;
250         ao_radio_cmac_rssi = ao_radio_spi_reply.rssi;
251         ao_radio_put();
252         PRINTD ("data fetched: %d %d\n", recv, ao_radio_cmac_rssi);
253         return recv;
254 }
255
256 static uint8_t  ao_radio_test_on;
257
258 void
259 ao_radio_test(uint8_t on)
260 {
261         if (on) {
262                 if (!ao_radio_test_on) {
263                         ao_radio_get(AO_RADIO_SPI_TEST_ON, 0);
264                         ao_radio_test_on = 1;
265                         ao_radio_master_send();
266                 }
267         } else {
268                 if (ao_radio_test_on) {
269                         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN;
270                         ao_radio_spi_request.request = AO_RADIO_SPI_TEST_OFF;
271                         ao_radio_master_send();
272                         ao_radio_test_on = 0;
273                         ao_radio_put();
274                 }
275         }
276 }
277
278 static void
279 ao_radio_test_cmd(void)
280 {
281         uint8_t mode = 2;
282         ao_cmd_white();
283         if (ao_cmd_lex_c != '\n') {
284                 ao_cmd_decimal();
285                 mode = (uint8_t) ao_cmd_lex_u32;
286         }
287         mode++;
288         if ((mode & 2))
289                 ao_radio_test(1);
290         if (mode == 3) {
291                 printf ("Hit a character to stop..."); flush();
292                 getchar();
293                 putchar('\n');
294         }
295         if ((mode & 1))
296                 ao_radio_test(0);
297 }
298
299 const struct ao_cmds ao_radio_cmds[] = {
300         { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
301         { 0,    NULL },
302 };
303
304 void
305 ao_radio_init(void)
306 {
307         ao_spi_init_cs(AO_RADIO_CS_PORT, (1 << AO_RADIO_CS_PIN));
308
309         ao_enable_port(AO_RADIO_INT_PORT);
310         ao_exti_setup(AO_RADIO_INT_PORT,
311                       AO_RADIO_INT_PIN,
312                       AO_EXTI_MODE_RISING|AO_EXTI_MODE_FALLING,
313                       ao_radio_isr);
314         ao_exti_enable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN);
315         ao_cmd_register(&ao_radio_cmds[0]);
316 }