altos: Optimize FEC encode and decode
[fw/altos] / src / drivers / ao_cc1120.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; 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_cc1120.h>
20 #include <ao_exti.h>
21 #include <ao_fec.h>
22 #include <ao_packet.h>
23
24 uint8_t ao_radio_wake;
25 uint8_t ao_radio_mutex;
26 uint8_t ao_radio_abort;
27 uint8_t ao_radio_in_recv;
28
29 #define CC1120_DEBUG    AO_FEC_DEBUG
30 #define CC1120_TRACE    0
31
32 #if CC1120_TRACE
33 #define fec_dump_bytes(b,l,n) ao_fec_dump_bytes(b,l,n)
34 #else
35 #define fec_dump_bytes(b,l,n)
36 #endif
37
38 const uint32_t  ao_radio_cal = 0x6ca333;
39
40 #define FOSC    32000000
41
42 #define ao_radio_select()       ao_spi_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS)
43 #define ao_radio_deselect()     ao_spi_put_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS)
44 #define ao_radio_spi_send(d,l)  ao_spi_send((d), (l), AO_CC1120_SPI_BUS)
45 #define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC1120_SPI_BUS)
46 #define ao_radio_spi_recv(d,l)  ao_spi_recv((d), (l), AO_CC1120_SPI_BUS)
47 #define ao_radio_duplex(o,i,l)  ao_spi_duplex((o), (i), (l), AO_CC1120_SPI_BUS)
48
49 static uint8_t
50 ao_radio_reg_read(uint16_t addr)
51 {
52         uint8_t data[2];
53         uint8_t d;
54
55 #if CC1120_TRACE
56         printf("\t\tao_radio_reg_read (%04x): ", addr); flush();
57 #endif
58         if (CC1120_IS_EXTENDED(addr)) {
59                 data[0] = ((1 << CC1120_READ)  |
60                            (0 << CC1120_BURST) |
61                            CC1120_EXTENDED);
62                 data[1] = addr;
63                 d = 2;
64         } else {
65                 data[0] = ((1 << CC1120_READ)  |
66                            (0 << CC1120_BURST) |
67                            addr);
68                 d = 1;
69         }
70         ao_radio_select();
71         ao_radio_spi_send(data, d);
72         ao_radio_spi_recv(data, 1);
73         ao_radio_deselect();
74 #if CC1120_TRACE
75         printf (" %02x\n", data[0]);
76 #endif
77         return data[0];
78 }
79
80 static void
81 ao_radio_reg_write(uint16_t addr, uint8_t value)
82 {
83         uint8_t data[3];
84         uint8_t d;
85
86 #if CC1120_TRACE
87         printf("\t\tao_radio_reg_write (%04x): %02x\n", addr, value);
88 #endif
89         if (CC1120_IS_EXTENDED(addr)) {
90                 data[0] = ((0 << CC1120_READ)  |
91                            (0 << CC1120_BURST) |
92                            CC1120_EXTENDED);
93                 data[1] = addr;
94                 d = 2;
95         } else {
96                 data[0] = ((0 << CC1120_READ)  |
97                            (0 << CC1120_BURST) |
98                            addr);
99                 d = 1;
100         }
101         data[d] = value;
102         ao_radio_select();
103         ao_radio_spi_send(data, d+1);
104         ao_radio_deselect();
105 }
106
107 static void
108 ao_radio_burst_read_start (uint16_t addr)
109 {
110         uint8_t data[2];
111         uint8_t d;
112
113         if (CC1120_IS_EXTENDED(addr)) {
114                 data[0] = ((1 << CC1120_READ)  |
115                            (1 << CC1120_BURST) |
116                            CC1120_EXTENDED);
117                 data[1] = addr;
118                 d = 2;
119         } else {
120                 data[0] = ((1 << CC1120_READ)  |
121                            (1 << CC1120_BURST) |
122                            addr);
123                 d = 1;
124         }
125         ao_radio_select();
126         ao_radio_spi_send(data, d);
127 }
128
129 static void
130 ao_radio_burst_read_stop (void)
131 {
132         ao_radio_deselect();
133 }
134
135
136 static uint8_t
137 ao_radio_strobe(uint8_t addr)
138 {
139         uint8_t in;
140
141 #if CC1120_TRACE
142         printf("\t\tao_radio_strobe (%02x): ", addr); flush();
143 #endif
144         ao_radio_select();
145         ao_radio_duplex(&addr, &in, 1);
146         ao_radio_deselect();
147 #if CC1120_TRACE
148         printf("%02x\n", in); flush();
149 #endif
150         return in;
151 }
152
153 static uint8_t
154 ao_radio_fifo_read(uint8_t *data, uint8_t len)
155 {
156         uint8_t addr = ((1 << CC1120_READ)  |
157                         (1 << CC1120_BURST) |
158                         CC1120_FIFO);
159         uint8_t status;
160
161         ao_radio_select();
162         ao_radio_duplex(&addr, &status, 1);
163         ao_radio_spi_recv(data, len);
164         ao_radio_deselect();
165         return status;
166 }
167
168 static uint8_t
169 ao_radio_fifo_write(uint8_t *data, uint8_t len)
170 {
171         uint8_t addr = ((0 << CC1120_READ)  |
172                         (1 << CC1120_BURST) |
173                         CC1120_FIFO);
174         uint8_t status;
175
176         ao_radio_select();
177         ao_radio_duplex(&addr, &status, 1);
178         ao_radio_spi_send(data, len);
179         ao_radio_deselect();
180         return status;
181 }
182
183 static uint8_t
184 ao_radio_fifo_write_fixed(uint8_t data, uint8_t len)
185 {
186         uint8_t addr = ((0 << CC1120_READ)  |
187                         (1 << CC1120_BURST) |
188                         CC1120_FIFO);
189         uint8_t status;
190
191         ao_radio_select();
192         ao_radio_duplex(&addr, &status, 1);
193         ao_radio_spi_send_fixed(data, len);
194         ao_radio_deselect();
195         return status;
196 }
197
198 static uint8_t
199 ao_radio_tx_fifo_space(void)
200 {
201         return CC1120_FIFO_SIZE - ao_radio_reg_read(CC1120_NUM_TXBYTES);
202 }
203
204 static uint8_t
205 ao_radio_status(void)
206 {
207         return ao_radio_strobe (CC1120_SNOP);
208 }
209
210 void
211 ao_radio_recv_abort(void)
212 {
213         ao_radio_abort = 1;
214         ao_wakeup(&ao_radio_wake);
215 }
216
217 #define ao_radio_rdf_value 0x55
218
219 static uint8_t
220 ao_radio_marc_status(void)
221 {
222         return ao_radio_reg_read(CC1120_MARC_STATUS1);
223 }
224
225 static void
226 ao_radio_tx_isr(void)
227 {
228         ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
229         ao_radio_wake = 1;
230         ao_wakeup(&ao_radio_wake);
231 }
232
233 static void
234 ao_radio_start_tx(void)
235 {
236         ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_tx_isr);
237         ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
238         ao_radio_strobe(CC1120_STX);
239 }
240
241 static void
242 ao_radio_idle(void)
243 {
244         for (;;) {
245                 uint8_t state = ao_radio_strobe(CC1120_SIDLE);
246                 if ((state >> CC1120_STATUS_STATE) == CC1120_STATUS_STATE_IDLE)
247                         break;
248         }
249 }
250
251 /*
252  * Packet deviation is 20.5kHz
253  *
254  *      fdev = fosc >> 24 * (256 + dev_m) << dev_e
255  *
256  *      32e6Hz / (2 ** 24) * (256 + 80) * (2 ** 5) = 20508Hz
257  */
258
259 #define PACKET_DEV_E    5
260 #define PACKET_DEV_M    80
261
262 /*
263  * For our packet data, set the symbol rate to 38360 Baud
264  *
265  *              (2**20 + DATARATE_M) * 2 ** DATARATE_E
266  *      Rdata = -------------------------------------- * fosc
267  *                           2 ** 39
268  *
269  *
270  *      DATARATE_M = 239914
271  *      DATARATE_E = 9
272  */
273 #define PACKET_DRATE_E  9
274 #define PACKET_DRATE_M  239914
275
276 static const uint16_t packet_setup[] = {
277         CC1120_DEVIATION_M,     PACKET_DEV_M,
278         CC1120_MODCFG_DEV_E,    ((CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1120_MODCFG_DEV_E_MODEM_MODE) |
279                                  (CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1120_MODCFG_DEV_E_MOD_FORMAT) |
280                                  (PACKET_DEV_E << CC1120_MODCFG_DEV_E_DEV_E)),
281         CC1120_DRATE2,          ((PACKET_DRATE_E << CC1120_DRATE2_DATARATE_E) |
282                                  (((PACKET_DRATE_M >> 16) & CC1120_DRATE2_DATARATE_M_19_16_MASK) << CC1120_DRATE2_DATARATE_M_19_16)),
283         CC1120_DRATE1,          ((PACKET_DRATE_M >> 8) & 0xff),
284         CC1120_DRATE0,          ((PACKET_DRATE_M >> 0) & 0xff),
285         CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
286                                  (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
287         CC1120_PKT_CFG1,        ((0 << CC1120_PKT_CFG1_WHITE_DATA) |
288                                  (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) |
289                                  (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) |
290                                  (0 << CC1120_PKT_CFG1_APPEND_STATUS)),
291         CC1120_PKT_CFG0,        ((0 << CC1120_PKT_CFG0_RESERVED7) |
292                                  (CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1120_PKT_CFG0_LENGTH_CONFIG) |
293                                  (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |
294                                  (0 << CC1120_PKT_CFG0_UART_MODE_EN) |
295                                  (0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
296 };
297
298 static const uint16_t packet_tx_setup[] = {
299         CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
300                                  (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
301         CC1120_IOCFG2,          CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG,
302 };
303
304 static const uint16_t packet_rx_setup[] = {
305         CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
306                                  (CC1120_PKT_CFG2_PKT_FORMAT_SYNCHRONOUS_SERIAL << CC1120_PKT_CFG2_PKT_FORMAT)),
307         CC1120_IOCFG2,          CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT,
308 };
309
310 /*
311  * RDF deviation is 5kHz
312  *
313  *      fdev = fosc >> 24 * (256 + dev_m) << dev_e
314  *
315  *      32e6Hz / (2 ** 24) * (256 + 71) * (2 ** 3) = 4989
316  */
317
318 #define RDF_DEV_E       3
319 #define RDF_DEV_M       71
320 #define RDF_PACKET_LEN  50
321
322 /*
323  * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
324  *
325  *              (2**20 - DATARATE_M) * 2 ** DATARATE_E
326  *      Rdata = -------------------------------------- * fosc
327  *                           2 ** 39
328  *
329  *      DATARATE_M = 511705
330  *      DATARATE_E = 6
331  *
332  * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
333  */
334 #define RDF_DRATE_E     5
335 #define RDF_DRATE_M     25166
336 #define RDF_PACKET_LEN  50
337
338 static const uint16_t rdf_setup[] = {
339         CC1120_DEVIATION_M,     RDF_DEV_M,
340         CC1120_MODCFG_DEV_E,    ((CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1120_MODCFG_DEV_E_MODEM_MODE) |
341                                  (CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1120_MODCFG_DEV_E_MOD_FORMAT) |
342                                  (RDF_DEV_E << CC1120_MODCFG_DEV_E_DEV_E)),
343         CC1120_DRATE2,          ((RDF_DRATE_E << CC1120_DRATE2_DATARATE_E) |
344                                  (((RDF_DRATE_M >> 16) & CC1120_DRATE2_DATARATE_M_19_16_MASK) << CC1120_DRATE2_DATARATE_M_19_16)),
345         CC1120_DRATE1,          ((RDF_DRATE_M >> 8) & 0xff),
346         CC1120_DRATE0,          ((RDF_DRATE_M >> 0) & 0xff),
347         CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
348                                  (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
349         CC1120_PKT_CFG1,        ((0 << CC1120_PKT_CFG1_WHITE_DATA) |
350                                  (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) |
351                                  (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) |
352                                  (0 << CC1120_PKT_CFG1_APPEND_STATUS)),
353         CC1120_PKT_CFG0,        ((0 << CC1120_PKT_CFG0_RESERVED7) |
354                                  (CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1120_PKT_CFG0_LENGTH_CONFIG) |
355                                  (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |
356                                  (0 << CC1120_PKT_CFG0_UART_MODE_EN) |
357                                  (0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
358 };
359
360 static uint8_t ao_radio_mode;
361
362 #define AO_RADIO_MODE_BITS_PACKET       1
363 #define AO_RADIO_MODE_BITS_PACKET_TX    2
364 #define AO_RADIO_MODE_BITS_TX_BUF       4
365 #define AO_RADIO_MODE_BITS_TX_FINISH    8
366 #define AO_RADIO_MODE_BITS_PACKET_RX    16
367 #define AO_RADIO_MODE_BITS_RDF          32
368
369 #define AO_RADIO_MODE_NONE              0
370 #define AO_RADIO_MODE_PACKET_TX_BUF     (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF)
371 #define AO_RADIO_MODE_PACKET_TX_FINISH  (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH)
372 #define AO_RADIO_MODE_PACKET_RX         (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_RX)
373 #define AO_RADIO_MODE_RDF               (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH)
374
375 static void
376 ao_radio_set_mode(uint8_t new_mode)
377 {
378         uint8_t changes;
379         int i;
380
381         if (new_mode == ao_radio_mode)
382                 return;
383
384         changes = new_mode & (~ao_radio_mode);
385         if (changes & AO_RADIO_MODE_BITS_PACKET)
386                 for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2)
387                         ao_radio_reg_write(packet_setup[i], packet_setup[i+1]);
388
389         if (changes & AO_RADIO_MODE_BITS_PACKET_TX)
390                 for (i = 0; i < sizeof (packet_tx_setup) / sizeof (packet_tx_setup[0]); i += 2)
391                         ao_radio_reg_write(packet_tx_setup[i], packet_tx_setup[i+1]);
392                 
393         if (changes & AO_RADIO_MODE_BITS_TX_BUF)
394                 ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_TXFIFO_THR);
395
396         if (changes & AO_RADIO_MODE_BITS_TX_FINISH)
397                 ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG);
398
399         if (changes & AO_RADIO_MODE_BITS_PACKET_RX)
400                 for (i = 0; i < sizeof (packet_rx_setup) / sizeof (packet_rx_setup[0]); i += 2)
401                         ao_radio_reg_write(packet_rx_setup[i], packet_rx_setup[i+1]);
402                 
403         if (changes & AO_RADIO_MODE_BITS_RDF)
404                 for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2)
405                         ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]);
406         ao_radio_mode = new_mode;
407 }
408
409 static const uint16_t radio_setup[] = {
410 #include "ao_cc1120_CC1120.h"
411 };
412
413 static uint8_t  ao_radio_configured = 0;
414
415 static void
416 ao_radio_setup(void)
417 {
418         int     i;
419
420         ao_radio_strobe(CC1120_SRES);
421
422         for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2)
423                 ao_radio_reg_write(radio_setup[i], radio_setup[i+1]);
424
425         ao_radio_mode = 0;
426
427         ao_config_get();
428
429         ao_radio_configured = 1;
430 }
431
432 static void
433 ao_radio_get(uint8_t len)
434 {
435         static uint32_t last_radio_setting;
436         static uint8_t  last_len;
437
438         ao_mutex_get(&ao_radio_mutex);
439         if (!ao_radio_configured)
440                 ao_radio_setup();
441         if (ao_config.radio_setting != last_radio_setting) {
442                 ao_radio_reg_write(CC1120_FREQ2, ao_config.radio_setting >> 16);
443                 ao_radio_reg_write(CC1120_FREQ1, ao_config.radio_setting >> 8);
444                 ao_radio_reg_write(CC1120_FREQ0, ao_config.radio_setting);
445                 last_radio_setting = ao_config.radio_setting;
446         }
447         if (len != last_len) {
448                 ao_radio_reg_write(CC1120_PKT_LEN, len);
449                 last_len = len;
450         }
451 }
452
453 #define ao_radio_put()  ao_mutex_put(&ao_radio_mutex)
454
455 void
456 ao_radio_rdf(uint8_t len)
457 {
458         int i;
459
460         ao_radio_abort = 0;
461         ao_radio_get(len);
462
463         ao_radio_set_mode(AO_RADIO_MODE_RDF);
464         ao_radio_wake = 0;
465
466         ao_radio_fifo_write_fixed(ao_radio_rdf_value, len);
467
468         ao_radio_start_tx();
469
470         cli();
471         while (!ao_radio_wake && !ao_radio_abort)
472                 ao_sleep(&ao_radio_wake);
473         sei();
474         if (!ao_radio_wake)
475                 ao_radio_idle();
476         ao_radio_put();
477 }
478
479 void
480 ao_radio_rdf_abort(void)
481 {
482         ao_radio_abort = 1;
483         ao_wakeup(&ao_radio_wake);
484 }
485
486 static void
487 ao_radio_test(void)
488 {
489         uint8_t mode = 2;
490         uint8_t radio_on;
491         ao_cmd_white();
492         if (ao_cmd_lex_c != '\n') {
493                 ao_cmd_decimal();
494                 mode = (uint8_t) ao_cmd_lex_u32;
495         }
496         mode++;
497         if ((mode & 2) && !radio_on) {
498 #if HAS_MONITOR
499                 ao_monitor_disable();
500 #endif
501 #if PACKET_HAS_SLAVE
502                 ao_packet_slave_stop();
503 #endif
504                 ao_radio_get(0xff);
505                 ao_radio_strobe(CC1120_STX);
506 #if CC1120_TRACE
507                 { int t; 
508                         for (t = 0; t < 10; t++) {
509                                 printf ("status: %02x\n", ao_radio_status());
510                                 ao_delay(AO_MS_TO_TICKS(100));
511                         }
512                 }
513 #endif
514                 radio_on = 1;
515         }
516         if (mode == 3) {
517                 printf ("Hit a character to stop..."); flush();
518                 getchar();
519                 putchar('\n');
520         }
521         if ((mode & 1) && radio_on) {
522                 ao_radio_idle();
523                 ao_radio_put();
524                 radio_on = 0;
525 #if HAS_MONITOR
526                 ao_monitor_enable();
527 #endif
528         }
529 }
530
531 void
532 ao_radio_send(const void *d, uint8_t size)
533 {
534         uint8_t         marc_status;
535         static uint8_t  encode[256];
536         uint8_t         *e = encode;
537         uint8_t         encode_len;
538         uint8_t         this_len;
539         uint8_t         started = 0;
540         uint8_t         fifo_space;
541
542         encode_len = ao_fec_encode(d, size, encode);
543
544         ao_radio_get(encode_len);
545
546         started = 0;
547         fifo_space = CC1120_FIFO_SIZE;
548         while (encode_len) {
549                 this_len = encode_len;
550
551                 if (this_len > fifo_space) {
552                         this_len = fifo_space;
553                         ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF);
554                 } else {
555                         ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_FINISH);
556                 }
557
558                 ao_radio_fifo_write(e, this_len);
559                 e += this_len;
560                 encode_len -= this_len;
561
562                 if (!started) {
563                         ao_radio_start_tx();
564                         started = 1;
565                 } else {
566                         ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
567                 }
568                         
569                 do {
570                         ao_radio_wake = 0;
571                         cli();
572                         while (!ao_radio_wake)
573                                 ao_sleep(&ao_radio_wake);
574                         sei();
575                         if (!encode_len)
576                                 break;
577                         fifo_space = ao_radio_tx_fifo_space();
578                 } while (!fifo_space);
579         }
580         ao_radio_put();
581 }
582
583 #define AO_RADIO_MAX_RECV       90
584
585 uint8_t rx_data[(AO_RADIO_MAX_RECV + 4) * 2 * 8];
586 uint16_t        rx_data_count;
587 uint16_t        rx_data_consumed;
588 uint16_t rx_data_cur;
589 uint8_t rx_ignore;
590 uint8_t rx_waiting;
591
592 #if AO_PROFILE
593 static uint32_t rx_start_tick, rx_packet_tick, rx_done_tick, rx_last_done_tick;
594
595 uint32_t        ao_rx_start_tick, ao_rx_packet_tick, ao_rx_done_tick, ao_rx_last_done_tick;
596
597 #include <ao_profile.h>
598 #endif
599
600 static void
601 ao_radio_rx_isr(void)
602 {
603         uint8_t d;
604
605         d = stm_spi2.dr;
606         stm_spi2.dr = 0;
607         if (rx_ignore == 0) {
608                 if (rx_data_cur >= rx_data_count)
609                         ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
610                 else
611                         rx_data[rx_data_cur++] = d;
612                 if (rx_waiting && rx_data_cur - rx_data_consumed >= AO_FEC_DECODE_BLOCK) {
613 #if AO_PROFILE
614                         if (rx_data_consumed == 0)
615                                 rx_packet_tick = ao_profile_tick();
616 #endif
617                         rx_waiting = 0;
618                         ao_wakeup(&ao_radio_wake);
619                 }
620         } else {
621                 --rx_ignore;
622         }
623 }
624
625 static uint16_t
626 ao_radio_rx_wait(void)
627 {
628         cli();
629         rx_waiting = 1;
630         while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK &&
631                !ao_radio_abort) {
632                 ao_sleep(&ao_radio_wake);
633         }
634         rx_waiting = 0;
635         sei();
636         if (ao_radio_abort)
637                 return 0;
638         rx_data_consumed += AO_FEC_DECODE_BLOCK;
639         return AO_FEC_DECODE_BLOCK;
640 }
641
642 uint8_t
643 ao_radio_recv(__xdata void *d, uint8_t size)
644 {
645         uint8_t         len;
646         uint16_t        i;
647         uint8_t         rssi;
648         uint8_t         ret;
649         static int been_here = 0;
650
651         size -= 2;                      /* status bytes */
652         if (size > AO_RADIO_MAX_RECV) {
653                 ao_delay(AO_SEC_TO_TICKS(1));
654                 return 0;
655         }
656 #if AO_PROFILE
657         rx_start_tick = ao_profile_tick();
658 #endif
659         len = size + 2;                 /* CRC bytes */
660         len += 1 + ~(len & 1);          /* 1 or two pad bytes */
661         len *= 2;                       /* 1/2 rate convolution */
662         rx_data_count = len * 8;        /* bytes to bits */
663         rx_data_cur = 0;
664         rx_data_consumed = 0;
665         rx_ignore = 2;
666
667         ao_radio_abort = 0;
668         ao_radio_in_recv = 1;
669         /* configure interrupt pin */
670         ao_radio_get(len);
671         ao_radio_set_mode(AO_RADIO_MODE_PACKET_RX);
672
673         ao_radio_wake = 0;
674
675         stm_spi2.cr2 = 0;
676
677         /* clear any RXNE */
678         (void) stm_spi2.dr;
679
680         ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr);
681         ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
682
683         ao_radio_strobe(CC1120_SRX);
684
685         ao_radio_burst_read_start(CC1120_SOFT_RX_DATA_OUT);
686
687         ret = ao_fec_decode(rx_data, rx_data_count, d, size + 2, ao_radio_rx_wait);
688
689         ao_radio_burst_read_stop();
690
691         ao_radio_strobe(CC1120_SIDLE);
692
693         /* Convert from 'real' rssi to cc1111-style values */
694
695         rssi = (((int8_t) ao_radio_reg_read(CC1120_RSSI1)) + 74) * 2;
696
697         ao_radio_put();
698
699         /* Construct final packet */
700
701         if (ret && ((uint8_t *) d)[size] == 0 && ((uint8_t *)d)[size+1] == 0)
702                 ((uint8_t *) d)[size + 1] = 0x80;
703         else
704                 ((uint8_t *) d)[size + 1] = 0x00;
705
706         ((uint8_t *) d)[size] = (uint8_t) rssi;
707
708         ao_radio_in_recv = 0;
709
710         if (ao_radio_abort)
711                 ao_delay(1);
712
713 #if AO_PROFILE
714         rx_last_done_tick = rx_done_tick;
715         rx_done_tick = ao_profile_tick();
716
717         ao_rx_start_tick = rx_start_tick;
718         ao_rx_packet_tick = rx_packet_tick;
719         ao_rx_done_tick = rx_done_tick;
720         ao_rx_last_done_tick = rx_last_done_tick;
721 #endif
722
723         return ret;
724 }
725
726
727 #if CC1120_DEBUG
728 static char *cc1120_state_name[] = {
729         [CC1120_STATUS_STATE_IDLE] = "IDLE",
730         [CC1120_STATUS_STATE_RX] = "RX",
731         [CC1120_STATUS_STATE_TX] = "TX",
732         [CC1120_STATUS_STATE_FSTXON] = "FSTXON",
733         [CC1120_STATUS_STATE_CALIBRATE] = "CALIBRATE",
734         [CC1120_STATUS_STATE_SETTLING] = "SETTLING",
735         [CC1120_STATUS_STATE_RX_FIFO_ERROR] = "RX_FIFO_ERROR",
736         [CC1120_STATUS_STATE_TX_FIFO_ERROR] = "TX_FIFO_ERROR",
737 };
738
739 struct ao_cc1120_reg {
740         uint16_t        addr;
741         char            *name;
742 };
743
744 const static struct ao_cc1120_reg ao_cc1120_reg[] = {
745         { .addr = CC1120_IOCFG3,        .name = "IOCFG3" },
746         { .addr = CC1120_IOCFG2,        .name = "IOCFG2" },
747         { .addr = CC1120_IOCFG1,        .name = "IOCFG1" },
748         { .addr = CC1120_IOCFG0,        .name = "IOCFG0" },
749         { .addr = CC1120_SYNC3, .name = "SYNC3" },
750         { .addr = CC1120_SYNC2, .name = "SYNC2" },
751         { .addr = CC1120_SYNC1, .name = "SYNC1" },
752         { .addr = CC1120_SYNC0, .name = "SYNC0" },
753         { .addr = CC1120_SYNC_CFG1,     .name = "SYNC_CFG1" },
754         { .addr = CC1120_SYNC_CFG0,     .name = "SYNC_CFG0" },
755         { .addr = CC1120_DEVIATION_M,   .name = "DEVIATION_M" },
756         { .addr = CC1120_MODCFG_DEV_E,  .name = "MODCFG_DEV_E" },
757         { .addr = CC1120_DCFILT_CFG,    .name = "DCFILT_CFG" },
758         { .addr = CC1120_PREAMBLE_CFG1, .name = "PREAMBLE_CFG1" },
759         { .addr = CC1120_PREAMBLE_CFG0, .name = "PREAMBLE_CFG0" },
760         { .addr = CC1120_FREQ_IF_CFG,   .name = "FREQ_IF_CFG" },
761         { .addr = CC1120_IQIC,  .name = "IQIC" },
762         { .addr = CC1120_CHAN_BW,       .name = "CHAN_BW" },
763         { .addr = CC1120_MDMCFG1,       .name = "MDMCFG1" },
764         { .addr = CC1120_MDMCFG0,       .name = "MDMCFG0" },
765         { .addr = CC1120_DRATE2,        .name = "DRATE2" },
766         { .addr = CC1120_DRATE1,        .name = "DRATE1" },
767         { .addr = CC1120_DRATE0,        .name = "DRATE0" },
768         { .addr = CC1120_AGC_REF,       .name = "AGC_REF" },
769         { .addr = CC1120_AGC_CS_THR,    .name = "AGC_CS_THR" },
770         { .addr = CC1120_AGC_GAIN_ADJUST,       .name = "AGC_GAIN_ADJUST" },
771         { .addr = CC1120_AGC_CFG3,      .name = "AGC_CFG3" },
772         { .addr = CC1120_AGC_CFG2,      .name = "AGC_CFG2" },
773         { .addr = CC1120_AGC_CFG1,      .name = "AGC_CFG1" },
774         { .addr = CC1120_AGC_CFG0,      .name = "AGC_CFG0" },
775         { .addr = CC1120_FIFO_CFG,      .name = "FIFO_CFG" },
776         { .addr = CC1120_DEV_ADDR,      .name = "DEV_ADDR" },
777         { .addr = CC1120_SETTLING_CFG,  .name = "SETTLING_CFG" },
778         { .addr = CC1120_FS_CFG,        .name = "FS_CFG" },
779         { .addr = CC1120_WOR_CFG1,      .name = "WOR_CFG1" },
780         { .addr = CC1120_WOR_CFG0,      .name = "WOR_CFG0" },
781         { .addr = CC1120_WOR_EVENT0_MSB,        .name = "WOR_EVENT0_MSB" },
782         { .addr = CC1120_WOR_EVENT0_LSB,        .name = "WOR_EVENT0_LSB" },
783         { .addr = CC1120_PKT_CFG2,      .name = "PKT_CFG2" },
784         { .addr = CC1120_PKT_CFG1,      .name = "PKT_CFG1" },
785         { .addr = CC1120_PKT_CFG0,      .name = "PKT_CFG0" },
786         { .addr = CC1120_RFEND_CFG1,    .name = "RFEND_CFG1" },
787         { .addr = CC1120_RFEND_CFG0,    .name = "RFEND_CFG0" },
788         { .addr = CC1120_PA_CFG2,       .name = "PA_CFG2" },
789         { .addr = CC1120_PA_CFG1,       .name = "PA_CFG1" },
790         { .addr = CC1120_PA_CFG0,       .name = "PA_CFG0" },
791         { .addr = CC1120_PKT_LEN,       .name = "PKT_LEN" },
792         { .addr = CC1120_IF_MIX_CFG,    .name = "IF_MIX_CFG" },
793         { .addr = CC1120_FREQOFF_CFG,   .name = "FREQOFF_CFG" },
794         { .addr = CC1120_TOC_CFG,       .name = "TOC_CFG" },
795         { .addr = CC1120_MARC_SPARE,    .name = "MARC_SPARE" },
796         { .addr = CC1120_ECG_CFG,       .name = "ECG_CFG" },
797         { .addr = CC1120_SOFT_TX_DATA_CFG,      .name = "SOFT_TX_DATA_CFG" },
798         { .addr = CC1120_EXT_CTRL,      .name = "EXT_CTRL" },
799         { .addr = CC1120_RCCAL_FINE,    .name = "RCCAL_FINE" },
800         { .addr = CC1120_RCCAL_COARSE,  .name = "RCCAL_COARSE" },
801         { .addr = CC1120_RCCAL_OFFSET,  .name = "RCCAL_OFFSET" },
802         { .addr = CC1120_FREQOFF1,      .name = "FREQOFF1" },
803         { .addr = CC1120_FREQOFF0,      .name = "FREQOFF0" },
804         { .addr = CC1120_FREQ2, .name = "FREQ2" },
805         { .addr = CC1120_FREQ1, .name = "FREQ1" },
806         { .addr = CC1120_FREQ0, .name = "FREQ0" },
807         { .addr = CC1120_IF_ADC2,       .name = "IF_ADC2" },
808         { .addr = CC1120_IF_ADC1,       .name = "IF_ADC1" },
809         { .addr = CC1120_IF_ADC0,       .name = "IF_ADC0" },
810         { .addr = CC1120_FS_DIG1,       .name = "FS_DIG1" },
811         { .addr = CC1120_FS_DIG0,       .name = "FS_DIG0" },
812         { .addr = CC1120_FS_CAL3,       .name = "FS_CAL3" },
813         { .addr = CC1120_FS_CAL2,       .name = "FS_CAL2" },
814         { .addr = CC1120_FS_CAL1,       .name = "FS_CAL1" },
815         { .addr = CC1120_FS_CAL0,       .name = "FS_CAL0" },
816         { .addr = CC1120_FS_CHP,        .name = "FS_CHP" },
817         { .addr = CC1120_FS_DIVTWO,     .name = "FS_DIVTWO" },
818         { .addr = CC1120_FS_DSM1,       .name = "FS_DSM1" },
819         { .addr = CC1120_FS_DSM0,       .name = "FS_DSM0" },
820         { .addr = CC1120_FS_DVC1,       .name = "FS_DVC1" },
821         { .addr = CC1120_FS_DVC0,       .name = "FS_DVC0" },
822         { .addr = CC1120_FS_LBI,        .name = "FS_LBI" },
823         { .addr = CC1120_FS_PFD,        .name = "FS_PFD" },
824         { .addr = CC1120_FS_PRE,        .name = "FS_PRE" },
825         { .addr = CC1120_FS_REG_DIV_CML,        .name = "FS_REG_DIV_CML" },
826         { .addr = CC1120_FS_SPARE,      .name = "FS_SPARE" },
827         { .addr = CC1120_FS_VCO4,       .name = "FS_VCO4" },
828         { .addr = CC1120_FS_VCO3,       .name = "FS_VCO3" },
829         { .addr = CC1120_FS_VCO2,       .name = "FS_VCO2" },
830         { .addr = CC1120_FS_VCO1,       .name = "FS_VCO1" },
831         { .addr = CC1120_FS_VCO0,       .name = "FS_VCO0" },
832         { .addr = CC1120_GBIAS6,        .name = "GBIAS6" },
833         { .addr = CC1120_GBIAS5,        .name = "GBIAS5" },
834         { .addr = CC1120_GBIAS4,        .name = "GBIAS4" },
835         { .addr = CC1120_GBIAS3,        .name = "GBIAS3" },
836         { .addr = CC1120_GBIAS2,        .name = "GBIAS2" },
837         { .addr = CC1120_GBIAS1,        .name = "GBIAS1" },
838         { .addr = CC1120_GBIAS0,        .name = "GBIAS0" },
839         { .addr = CC1120_IFAMP, .name = "IFAMP" },
840         { .addr = CC1120_LNA,   .name = "LNA" },
841         { .addr = CC1120_RXMIX, .name = "RXMIX" },
842         { .addr = CC1120_XOSC5, .name = "XOSC5" },
843         { .addr = CC1120_XOSC4, .name = "XOSC4" },
844         { .addr = CC1120_XOSC3, .name = "XOSC3" },
845         { .addr = CC1120_XOSC2, .name = "XOSC2" },
846         { .addr = CC1120_XOSC1, .name = "XOSC1" },
847         { .addr = CC1120_XOSC0, .name = "XOSC0" },
848         { .addr = CC1120_ANALOG_SPARE,  .name = "ANALOG_SPARE" },
849         { .addr = CC1120_PA_CFG3,       .name = "PA_CFG3" },
850         { .addr = CC1120_WOR_TIME1,     .name = "WOR_TIME1" },
851         { .addr = CC1120_WOR_TIME0,     .name = "WOR_TIME0" },
852         { .addr = CC1120_WOR_CAPTURE1,  .name = "WOR_CAPTURE1" },
853         { .addr = CC1120_WOR_CAPTURE0,  .name = "WOR_CAPTURE0" },
854         { .addr = CC1120_BIST,  .name = "BIST" },
855         { .addr = CC1120_DCFILTOFFSET_I1,       .name = "DCFILTOFFSET_I1" },
856         { .addr = CC1120_DCFILTOFFSET_I0,       .name = "DCFILTOFFSET_I0" },
857         { .addr = CC1120_DCFILTOFFSET_Q1,       .name = "DCFILTOFFSET_Q1" },
858         { .addr = CC1120_DCFILTOFFSET_Q0,       .name = "DCFILTOFFSET_Q0" },
859         { .addr = CC1120_IQIE_I1,       .name = "IQIE_I1" },
860         { .addr = CC1120_IQIE_I0,       .name = "IQIE_I0" },
861         { .addr = CC1120_IQIE_Q1,       .name = "IQIE_Q1" },
862         { .addr = CC1120_IQIE_Q0,       .name = "IQIE_Q0" },
863         { .addr = CC1120_RSSI1, .name = "RSSI1" },
864         { .addr = CC1120_RSSI0, .name = "RSSI0" },
865         { .addr = CC1120_MARCSTATE,     .name = "MARCSTATE" },
866         { .addr = CC1120_LQI_VAL,       .name = "LQI_VAL" },
867         { .addr = CC1120_PQT_SYNC_ERR,  .name = "PQT_SYNC_ERR" },
868         { .addr = CC1120_DEM_STATUS,    .name = "DEM_STATUS" },
869         { .addr = CC1120_FREQOFF_EST1,  .name = "FREQOFF_EST1" },
870         { .addr = CC1120_FREQOFF_EST0,  .name = "FREQOFF_EST0" },
871         { .addr = CC1120_AGC_GAIN3,     .name = "AGC_GAIN3" },
872         { .addr = CC1120_AGC_GAIN2,     .name = "AGC_GAIN2" },
873         { .addr = CC1120_AGC_GAIN1,     .name = "AGC_GAIN1" },
874         { .addr = CC1120_AGC_GAIN0,     .name = "AGC_GAIN0" },
875         { .addr = CC1120_SOFT_RX_DATA_OUT,      .name = "SOFT_RX_DATA_OUT" },
876         { .addr = CC1120_SOFT_TX_DATA_IN,       .name = "SOFT_TX_DATA_IN" },
877         { .addr = CC1120_ASK_SOFT_RX_DATA,      .name = "ASK_SOFT_RX_DATA" },
878         { .addr = CC1120_RNDGEN,        .name = "RNDGEN" },
879         { .addr = CC1120_MAGN2, .name = "MAGN2" },
880         { .addr = CC1120_MAGN1, .name = "MAGN1" },
881         { .addr = CC1120_MAGN0, .name = "MAGN0" },
882         { .addr = CC1120_ANG1,  .name = "ANG1" },
883         { .addr = CC1120_ANG0,  .name = "ANG0" },
884         { .addr = CC1120_CHFILT_I2,     .name = "CHFILT_I2" },
885         { .addr = CC1120_CHFILT_I1,     .name = "CHFILT_I1" },
886         { .addr = CC1120_CHFILT_I0,     .name = "CHFILT_I0" },
887         { .addr = CC1120_CHFILT_Q2,     .name = "CHFILT_Q2" },
888         { .addr = CC1120_CHFILT_Q1,     .name = "CHFILT_Q1" },
889         { .addr = CC1120_CHFILT_Q0,     .name = "CHFILT_Q0" },
890         { .addr = CC1120_GPIO_STATUS,   .name = "GPIO_STATUS" },
891         { .addr = CC1120_FSCAL_CTRL,    .name = "FSCAL_CTRL" },
892         { .addr = CC1120_PHASE_ADJUST,  .name = "PHASE_ADJUST" },
893         { .addr = CC1120_PARTNUMBER,    .name = "PARTNUMBER" },
894         { .addr = CC1120_PARTVERSION,   .name = "PARTVERSION" },
895         { .addr = CC1120_SERIAL_STATUS, .name = "SERIAL_STATUS" },
896         { .addr = CC1120_RX_STATUS,     .name = "RX_STATUS" },
897         { .addr = CC1120_TX_STATUS,     .name = "TX_STATUS" },
898         { .addr = CC1120_MARC_STATUS1,  .name = "MARC_STATUS1" },
899         { .addr = CC1120_MARC_STATUS0,  .name = "MARC_STATUS0" },
900         { .addr = CC1120_PA_IFAMP_TEST, .name = "PA_IFAMP_TEST" },
901         { .addr = CC1120_FSRF_TEST,     .name = "FSRF_TEST" },
902         { .addr = CC1120_PRE_TEST,      .name = "PRE_TEST" },
903         { .addr = CC1120_PRE_OVR,       .name = "PRE_OVR" },
904         { .addr = CC1120_ADC_TEST,      .name = "ADC_TEST" },
905         { .addr = CC1120_DVC_TEST,      .name = "DVC_TEST" },
906         { .addr = CC1120_ATEST, .name = "ATEST" },
907         { .addr = CC1120_ATEST_LVDS,    .name = "ATEST_LVDS" },
908         { .addr = CC1120_ATEST_MODE,    .name = "ATEST_MODE" },
909         { .addr = CC1120_XOSC_TEST1,    .name = "XOSC_TEST1" },
910         { .addr = CC1120_XOSC_TEST0,    .name = "XOSC_TEST0" },
911         { .addr = CC1120_RXFIRST,       .name = "RXFIRST" },
912         { .addr = CC1120_TXFIRST,       .name = "TXFIRST" },
913         { .addr = CC1120_RXLAST,        .name = "RXLAST" },
914         { .addr = CC1120_TXLAST,        .name = "TXLAST" },
915         { .addr = CC1120_NUM_TXBYTES,   .name = "NUM_TXBYTES" },
916         { .addr = CC1120_NUM_RXBYTES,   .name = "NUM_RXBYTES" },
917         { .addr = CC1120_FIFO_NUM_TXBYTES,      .name = "FIFO_NUM_TXBYTES" },
918         { .addr = CC1120_FIFO_NUM_RXBYTES,      .name = "FIFO_NUM_RXBYTES" },
919 };
920
921 #define AO_NUM_CC1120_REG       (sizeof ao_cc1120_reg / sizeof ao_cc1120_reg[0])
922
923 static void ao_radio_show(void) {
924         uint8_t status = ao_radio_status();
925         int     i;
926
927         ao_radio_get(0xff);
928         status = ao_radio_status();
929         printf ("Status:   %02x\n", status);
930         printf ("CHIP_RDY: %d\n", (status >> CC1120_STATUS_CHIP_RDY) & 1);
931         printf ("STATE:    %s\n", cc1120_state_name[(status >> CC1120_STATUS_STATE) & CC1120_STATUS_STATE_MASK]);
932         printf ("MARC:     %02x\n", ao_radio_marc_status());
933
934         for (i = 0; i < AO_NUM_CC1120_REG; i++)
935                 printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc1120_reg[i].addr), ao_cc1120_reg[i].name);
936         ao_radio_put();
937 }
938
939 static void ao_radio_beep(void) {
940         ao_radio_rdf(RDF_PACKET_LEN);
941 }
942
943 static void ao_radio_packet(void) {
944         static const uint8_t packet[] = {
945 #if 1
946                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
947                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
948                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
949                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
950 #else
951                 3, 1, 2, 3
952 #endif
953         };
954
955         ao_radio_send(packet, sizeof (packet));
956 }
957
958 void
959 ao_radio_test_recv()
960 {
961         uint8_t bytes[34];
962         uint8_t b;
963
964         if (ao_radio_recv(bytes, 34)) {
965                 if (bytes[33] & 0x80)
966                         printf ("CRC OK");
967                 else
968                         printf ("CRC BAD");
969                 printf (" RSSI %d", (int16_t) ((int8_t) bytes[32] >> 1) - 74);
970                 for (b = 0; b < 32; b++)
971                         printf (" %02x", bytes[b]);
972                 printf ("\n");
973         }
974 }
975
976 #endif
977
978 static const struct ao_cmds ao_radio_cmds[] = {
979         { ao_radio_test,        "C <1 start, 0 stop, none both>\0Radio carrier test" },
980 #if CC1120_DEBUG
981         { ao_radio_show,        "R\0Show CC1120 status" },
982         { ao_radio_beep,        "b\0Emit an RDF beacon" },
983         { ao_radio_packet,      "p\0Send a test packet" },
984         { ao_radio_test_recv,   "q\0Recv a test packet" },
985 #endif
986         { 0, NULL }
987 };
988
989 void
990 ao_radio_init(void)
991 {
992         int     i;
993
994         ao_radio_configured = 0;
995         ao_spi_init_cs (AO_CC1120_SPI_CS_PORT, (1 << AO_CC1120_SPI_CS_PIN));
996
997         AO_CC1120_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC1120_SPI_CS_PIN));
998         for (i = 0; i < 10000; i++) {
999                 if ((SPI_2_GPIO->idr & (1 << SPI_2_MISO)) == 0)
1000                         break;
1001         }
1002         AO_CC1120_SPI_CS_PORT->bsrr = (1 << AO_CC1120_SPI_CS_PIN);
1003         if (i == 10000)
1004                 ao_panic(AO_PANIC_SELF_TEST);
1005
1006         /* Enable the EXTI interrupt for the appropriate pin */
1007         ao_enable_port(AO_CC1120_INT_PORT);
1008         ao_exti_setup(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN,
1009                       AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
1010                       ao_radio_tx_isr);
1011
1012         ao_cmd_register(&ao_radio_cmds[0]);
1013 }