altos: Add FAT32 support. And lots more testing.
[fw/altos] / src / drivers / ao_cc115l.c
1 /*
2  * Copyright © 2013 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_cc115l.h>
20 #include <ao_exti.h>
21 #include <ao_telemetry.h>
22 #include <ao_fec.h>
23
24 #define AO_RADIO_MAX_SEND       sizeof (struct ao_telemetry_generic)
25
26 static uint8_t ao_radio_mutex;
27
28 static uint8_t ao_radio_wake;           /* radio ready. Also used as sleep address */
29 static uint8_t ao_radio_abort;          /* radio operation should abort */
30 static uint8_t ao_radio_mcu_wake;       /* MARC status change */
31 static uint8_t ao_radio_marcstate;      /* Last read MARC state value */
32
33 #define CC115L_DEBUG    1
34 #define CC115L_TRACE    1
35
36 #define FOSC    26000000
37
38 #define ao_radio_select()       ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_1MHz)
39 #define ao_radio_deselect()     ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
40 #define ao_radio_spi_send(d,l)  ao_spi_send((d), (l), AO_CC115L_SPI_BUS)
41 #define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS)
42 #define ao_radio_spi_recv(d,l)  ao_spi_recv((d), (l), AO_CC115L_SPI_BUS)
43 #define ao_radio_duplex(o,i,l)  ao_spi_duplex((o), (i), (l), AO_CC115L_SPI_BUS)
44
45 static uint8_t
46 ao_radio_reg_read(uint16_t addr)
47 {
48         uint8_t data[1];
49         uint8_t d;
50
51 #if CC115L_TRACE
52         printf("\t\tao_radio_reg_read (%04x): ", addr); flush();
53 #endif
54         data[0] = ((1 << CC115L_READ)  |
55                    (0 << CC115L_BURST) |
56                    addr);
57         ao_radio_select();
58         ao_radio_spi_send(data, 1);
59         ao_radio_spi_recv(data, 1);
60         ao_radio_deselect();
61 #if CC115L_TRACE
62         printf (" %02x\n", data[0]);
63 #endif
64         return data[0];
65 }
66
67 static void
68 ao_radio_reg_write(uint16_t addr, uint8_t value)
69 {
70         uint8_t data[2];
71         uint8_t d;
72
73 #if CC115L_TRACE
74         printf("\t\tao_radio_reg_write (%04x): %02x\n", addr, value);
75 #endif
76         data[0] = ((0 << CC115L_READ)  |
77                    (0 << CC115L_BURST) |
78                    addr);
79         data[1] = value;
80         ao_radio_select();
81         ao_radio_spi_send(data, 2);
82         ao_radio_deselect();
83 }
84
85 static void
86 ao_radio_burst_read_start (uint16_t addr)
87 {
88         uint8_t data[1];
89         uint8_t d;
90
91         data[0] = ((1 << CC115L_READ)  |
92                    (1 << CC115L_BURST) |
93                    addr);
94         ao_radio_select();
95         ao_radio_spi_send(data, 1);
96 }
97
98 static void
99 ao_radio_burst_read_stop (void)
100 {
101         ao_radio_deselect();
102 }
103
104
105 static uint8_t
106 ao_radio_strobe(uint8_t addr)
107 {
108         uint8_t in;
109
110 #if CC115L_TRACE
111         printf("\t\tao_radio_strobe (%02x): ", addr); flush();
112 #endif
113         ao_radio_select();
114         ao_radio_duplex(&addr, &in, 1);
115         ao_radio_deselect();
116 #if CC115L_TRACE
117         printf("%02x\n", in); flush();
118 #endif
119         return in;
120 }
121
122 static uint8_t
123 ao_radio_fifo_write_start(void)
124 {
125         uint8_t addr = ((0 << CC115L_READ)  |
126                         (1 << CC115L_BURST) |
127                         CC115L_FIFO);
128         uint8_t status;
129
130         ao_radio_select();
131         ao_radio_duplex(&addr, &status, 1);
132         return status;
133 }
134
135 static inline uint8_t ao_radio_fifo_write_stop(uint8_t status) {
136         ao_radio_deselect();
137         return status;
138 }
139
140 static uint8_t
141 ao_radio_fifo_write(uint8_t *data, uint8_t len)
142 {
143         uint8_t status = ao_radio_fifo_write_start();
144 #if CC115L_TRACE
145         printf ("fifo_write %d\n", len);
146 #endif
147         ao_radio_spi_send(data, len);
148         return ao_radio_fifo_write_stop(status);
149 }
150
151 static uint8_t
152 ao_radio_fifo_write_fixed(uint8_t data, uint8_t len)
153 {
154         uint8_t status = ao_radio_fifo_write_start();
155
156 #if CC115L_TRACE
157         printf ("fifo_write_fixed %02x %d\n", data, len);
158 #endif
159         ao_radio_spi_send_fixed(data, len);
160         return ao_radio_fifo_write_stop(status);
161 }
162
163 static uint8_t
164 ao_radio_tx_fifo_space(void)
165 {
166         return CC115L_FIFO_SIZE - (ao_radio_reg_read(CC115L_TXBYTES) & CC115L_TXBYTES_NUM_TX_BYTES_MASK);
167 }
168
169 static uint8_t
170 ao_radio_status(void)
171 {
172         return ao_radio_strobe (CC115L_SNOP);
173 }
174
175 #define ao_radio_rdf_value 0x55
176
177 static uint8_t
178 ao_radio_get_marcstate(void)
179 {
180         return ao_radio_reg_read(CC115L_MARCSTATE) & CC115L_MARCSTATE_MASK;
181 }
182
183 static void
184 ao_radio_mcu_wakeup_isr(void)
185 {
186         ao_radio_mcu_wake = 1;
187         ao_wakeup(&ao_radio_wake);
188 }
189
190
191 static void
192 ao_radio_check_marcstate(void)
193 {
194         ao_radio_mcu_wake = 0;
195         ao_radio_marcstate = ao_radio_get_marcstate();
196         
197         /* Anyt other than 'tx finished' means an error occurred */
198         if (ao_radio_marcstate != CC115L_MARCSTATE_TX_END)
199                 ao_radio_abort = 1;
200 }
201
202 static void
203 ao_radio_isr(void)
204 {
205         ao_exti_disable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
206         ao_radio_wake = 1;
207         ao_wakeup(&ao_radio_wake);
208 }
209
210 static void
211 ao_radio_start_tx(void)
212 {
213         ao_radio_pa_on();
214         ao_exti_set_callback(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN, ao_radio_isr);
215         ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
216         ao_exti_enable(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN);
217         ao_radio_strobe(CC115L_STX);
218 }
219
220 static void
221 ao_radio_idle(void)
222 {
223         ao_radio_pa_off();
224         for (;;) {
225                 uint8_t state = ao_radio_strobe(CC115L_SIDLE);
226                 if ((state >> CC115L_STATUS_STATE) == CC115L_STATUS_STATE_IDLE)
227                         break;
228         }
229         /* Flush any pending TX bytes */
230         ao_radio_strobe(CC115L_SFTX);
231 }
232
233 /*
234  * Packet deviation is 20.5kHz
235  *
236  *      fdev = fosc >> 17 * (8 + dev_m) << dev_e
237  *
238  *      26e6 / (2 ** 17) * (8 + 5) * (2 ** 3) = 20630Hz
239  */
240
241 #define PACKET_DEV_E    3
242 #define PACKET_DEV_M    5
243
244 /*
245  * For our packet data, set the symbol rate to 38400 Baud
246  *
247  *              (256 + DATARATE_M) * 2 ** DATARATE_E
248  *      Rdata = -------------------------------------- * fosc
249  *                           2 ** 28
250  *
251  *              (256 + 131) * (2 ** 10) / (2**28) * 26e6 = 38383
252  *
253  *      DATARATE_M = 131
254  *      DATARATE_E = 10
255  */
256 #define PACKET_DRATE_E  10
257 #define PACKET_DRATE_M  131
258
259 static const uint16_t packet_setup[] = {
260         CC115L_DEVIATN,         ((PACKET_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
261                                  (PACKET_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
262         CC115L_MDMCFG4,         ((0xf << 4) |
263                                  (PACKET_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
264         CC115L_MDMCFG3,         (PACKET_DRATE_M),
265 };
266
267
268 /*
269  * RDF deviation is 5kHz
270  *
271  *      fdev = fosc >> 17 * (8 + dev_m) << dev_e
272  *
273  *      26e6 / (2 ** 17) * (8 + 4) * (2 ** 1) = 4761Hz
274  */
275
276 #define RDF_DEV_E       1
277 #define RDF_DEV_M       4
278
279 /*
280  * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
281  *
282  *              (256 + DATARATE_M) * 2 ** DATARATE_E
283  *      Rdata = -------------------------------------- * fosc
284  *                           2 ** 28
285  *
286  *              (256 + 67) * (2 ** 6) / (2**28) * 26e6 = 2002
287  *
288  *      DATARATE_M = 67
289  *      DATARATE_E = 6
290  *
291  * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
292  */
293 #define RDF_DRATE_E     6
294 #define RDF_DRATE_M     67
295 #define RDF_PACKET_LEN  50
296
297 static const uint16_t rdf_setup[] = {
298         CC115L_DEVIATN,         ((RDF_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
299                                  (RDF_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
300         CC115L_MDMCFG4,         ((0xf << 4) |
301                                  (RDF_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
302         CC115L_MDMCFG3,         (RDF_DRATE_M),
303 };
304
305 /*
306  * APRS deviation is the same as RDF
307  */
308
309 #define APRS_DEV_E      RDF_DEV_E
310 #define APRS_DEV_M      RDF_DEV_E
311
312 /*
313  * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate)
314  *
315  *              (256 + DATARATE_M) * 2 ** DATARATE_E
316  *      Rdata = -------------------------------------- * fosc
317  *                           2 ** 28
318  *
319  *              (256 + 131) * (2 ** 8) / (2**28) * 26e6 = 9596
320  *
321  *      DATARATE_M = 131
322  *      DATARATE_E = 8
323  *
324  */
325 #define APRS_DRATE_E    8
326 #define APRS_DRATE_M    131
327
328 static const uint16_t aprs_setup[] = {
329         CC115L_DEVIATN,         ((APRS_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
330                                  (APRS_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
331         CC115L_MDMCFG4,         ((0xf << 4) |
332                                  (APRS_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
333         CC115L_MDMCFG3,         (APRS_DRATE_M),
334 };
335
336 #define AO_PKTCTRL0_INFINITE    ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \
337                                  (0 << CC115L_PKTCTRL0_PKT_CRC_EN) |                                    \
338                                  (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_INFINITE << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG))
339 #define AO_PKTCTRL0_FIXED       ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \
340                                  (0 << CC115L_PKTCTRL0_PKT_CRC_EN) |                                    \
341                                  (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_FIXED << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG))
342
343 static uint16_t ao_radio_mode;
344
345 #define AO_RADIO_MODE_BITS_PACKET_TX    1
346 #define AO_RADIO_MODE_BITS_TX_BUF       2
347 #define AO_RADIO_MODE_BITS_TX_FINISH    4
348 #define AO_RADIO_MODE_BITS_RDF          8
349 #define AO_RADIO_MODE_BITS_APRS         16
350 #define AO_RADIO_MODE_BITS_INFINITE     32
351 #define AO_RADIO_MODE_BITS_FIXED        64
352
353 #define AO_RADIO_MODE_NONE              0
354 #define AO_RADIO_MODE_PACKET_TX_BUF     (AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF)
355 #define AO_RADIO_MODE_PACKET_TX_FINISH  (AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH)
356 #define AO_RADIO_MODE_RDF               (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH)
357 #define AO_RADIO_MODE_APRS_BUF          (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF)
358 #define AO_RADIO_MODE_APRS_LAST_BUF     (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_BUF)
359 #define AO_RADIO_MODE_APRS_FINISH       (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH)
360
361 static void
362 ao_radio_set_mode(uint16_t new_mode)
363 {
364         uint16_t changes;
365         int i;
366
367         if (new_mode == ao_radio_mode)
368                 return;
369
370         changes = new_mode & (~ao_radio_mode);
371         if (changes & AO_RADIO_MODE_BITS_PACKET_TX)
372                 for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2)
373                         ao_radio_reg_write(packet_setup[i], packet_setup[i+1]);
374
375         if (changes & AO_RADIO_MODE_BITS_TX_BUF)
376                 ao_radio_reg_write(AO_CC115L_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_TXFIFO_THR);
377
378         if (changes & AO_RADIO_MODE_BITS_TX_FINISH)
379                 ao_radio_reg_write(AO_CC115L_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PKT_SYNC_TX | (1 << CC115L_IOCFG_GPIO_INV));
380
381         if (changes & AO_RADIO_MODE_BITS_RDF)
382                 for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2)
383                         ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]);
384
385         if (changes & AO_RADIO_MODE_BITS_APRS)
386                 for (i = 0; i < sizeof (aprs_setup) / sizeof (aprs_setup[0]); i += 2)
387                         ao_radio_reg_write(aprs_setup[i], aprs_setup[i+1]);
388
389         if (changes & AO_RADIO_MODE_BITS_INFINITE)
390                 ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_INFINITE);
391
392         if (changes & AO_RADIO_MODE_BITS_FIXED)
393                 ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_FIXED);
394
395         ao_radio_mode = new_mode;
396 }
397
398 static const uint16_t radio_setup[] = {
399 #include "ao_rf_cc115l.h"
400 };
401
402 static uint8_t  ao_radio_configured = 0;
403
404 static void
405 ao_radio_setup(void)
406 {
407         int     i;
408
409 #if 0
410         ao_gpio_set(AO_CC115L_SPI_CS_PORT, AO_CC115L_SPI_CS_PIN, AO_CC115L_SPI_CS, 0);
411         for (i = 0; i < 10000; i++) {
412                 if (ao_gpio_get(SPI_2_PORT, SPI_2_MISO_PIN, SPI_2_MISO) == 0) {
413                         printf ("Chip clock alive\n");
414                         break;
415                 }
416         }
417         ao_gpio_set(AO_CC115L_SPI_CS_PORT, AO_CC115L_SPI_CS_PIN, AO_CC115L_SPI_CS, 1);
418         if (i == 10000)
419                 printf ("Chip clock not alive\n");
420 #endif
421
422         ao_radio_strobe(CC115L_SRES);
423         ao_delay(AO_MS_TO_TICKS(10));
424
425         printf ("Part %x\n", ao_radio_reg_read(CC115L_PARTNUM));
426         printf ("Version %x\n", ao_radio_reg_read(CC115L_VERSION));
427
428         for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2) {
429                 ao_radio_reg_write(radio_setup[i], radio_setup[i+1]);
430                 ao_radio_reg_read(radio_setup[i]);
431         }
432
433         ao_radio_mode = 0;
434
435         ao_config_get();
436
437         ao_radio_configured = 1;
438 }
439
440 static void
441 ao_radio_set_len(uint8_t len)
442 {
443         static uint8_t  last_len;
444
445         if (len != last_len) {
446                 ao_radio_reg_write(CC115L_PKTLEN, len);
447                 last_len = len;
448         }
449 }
450
451 static void
452 ao_radio_get(uint8_t len)
453 {
454         static uint32_t last_radio_setting;
455
456         ao_mutex_get(&ao_radio_mutex);
457         if (!ao_radio_configured)
458                 ao_radio_setup();
459         if (ao_config.radio_setting != last_radio_setting) {
460                 ao_radio_reg_write(CC115L_FREQ2, ao_config.radio_setting >> 16);
461                 ao_radio_reg_write(CC115L_FREQ1, ao_config.radio_setting >> 8);
462                 ao_radio_reg_write(CC115L_FREQ0, ao_config.radio_setting);
463                 last_radio_setting = ao_config.radio_setting;
464         }
465         ao_radio_set_len(len);
466 }
467
468 #define ao_radio_put()  ao_mutex_put(&ao_radio_mutex)
469
470 static void
471 ao_rdf_start(uint8_t len)
472 {
473         ao_radio_abort = 0;
474         ao_radio_get(len);
475
476         ao_radio_set_mode(AO_RADIO_MODE_RDF);
477         ao_radio_wake = 0;
478
479 }
480
481 static void
482 ao_rdf_run(void)
483 {
484         ao_radio_start_tx();
485
486         ao_arch_block_interrupts();
487         while (!ao_radio_wake && !ao_radio_abort && !ao_radio_mcu_wake)
488                 ao_sleep(&ao_radio_wake);
489         ao_arch_release_interrupts();
490         if (ao_radio_mcu_wake)
491                 ao_radio_check_marcstate();
492         ao_radio_pa_off();
493         if (!ao_radio_wake)
494                 ao_radio_idle();
495         ao_radio_put();
496 }
497
498 void
499 ao_radio_rdf(void)
500 {
501         ao_rdf_start(AO_RADIO_RDF_LEN);
502
503         ao_radio_fifo_write_fixed(ao_radio_rdf_value, AO_RADIO_RDF_LEN);
504
505         ao_rdf_run();
506 }
507
508 void
509 ao_radio_continuity(uint8_t c)
510 {
511         uint8_t i;
512         uint8_t status;
513
514         ao_rdf_start(AO_RADIO_CONT_TOTAL_LEN);
515
516         status = ao_radio_fifo_write_start();
517         for (i = 0; i < 3; i++) {
518                 ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
519                 if (i < c)
520                         ao_radio_spi_send_fixed(ao_radio_rdf_value, AO_RADIO_CONT_TONE_LEN);
521                 else
522                         ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_TONE_LEN);
523         }
524         ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
525         status = ao_radio_fifo_write_stop(status);
526         (void) status;
527         ao_rdf_run();
528 }
529
530 void
531 ao_radio_rdf_abort(void)
532 {
533         ao_radio_abort = 1;
534         ao_wakeup(&ao_radio_wake);
535 }
536
537 static void
538 ao_radio_test_cmd(void)
539 {
540         uint8_t mode = 2;
541         static uint8_t radio_on;
542         ao_cmd_white();
543         if (ao_cmd_lex_c != '\n') {
544                 ao_cmd_decimal();
545                 mode = (uint8_t) ao_cmd_lex_u32;
546         }
547         mode++;
548         if ((mode & 2) && !radio_on) {
549 #if HAS_MONITOR
550                 ao_monitor_disable();
551 #endif
552 #if PACKET_HAS_SLAVE
553                 ao_packet_slave_stop();
554 #endif
555                 ao_radio_get(0xff);
556                 ao_radio_pa_on();
557                 ao_radio_strobe(CC115L_STX);
558 #if CC115L_TRACE
559                 { int t; 
560                         for (t = 0; t < 10; t++) {
561                                 printf ("status: %02x\n", ao_radio_status());
562                                 ao_delay(AO_MS_TO_TICKS(100));
563                         }
564                 }
565 #endif
566                 radio_on = 1;
567         }
568         if (mode == 3) {
569                 printf ("Hit a character to stop..."); flush();
570                 getchar();
571                 putchar('\n');
572         }
573         if ((mode & 1) && radio_on) {
574                 ao_radio_idle();
575                 ao_radio_put();
576                 radio_on = 0;
577 #if HAS_MONITOR
578                 ao_monitor_enable();
579 #endif
580         }
581 }
582
583 static void
584 ao_radio_wait_isr(void)
585 {
586         ao_arch_block_interrupts();
587         while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort)
588                 ao_sleep(&ao_radio_wake);
589         ao_arch_release_interrupts();
590         if (ao_radio_mcu_wake)
591                 ao_radio_check_marcstate();
592 }
593
594 static uint8_t
595 ao_radio_wait_tx(uint8_t wait_fifo)
596 {
597         uint8_t fifo_space = 0;
598
599         do {
600                 ao_radio_wait_isr();
601                 if (!wait_fifo)
602                         return 0;
603                 fifo_space = ao_radio_tx_fifo_space();
604         } while (!fifo_space && !ao_radio_abort);
605         return fifo_space;
606 }
607
608 static uint8_t  tx_data[(AO_RADIO_MAX_SEND + 4) * 2];
609
610 void
611 ao_radio_send(const void *d, uint8_t size)
612 {
613         uint8_t         marc_status;
614         uint8_t         *e = tx_data;
615         uint8_t         encode_len;
616         uint8_t         this_len;
617         uint8_t         started = 0;
618         uint8_t         fifo_space;
619
620         encode_len = ao_fec_encode(d, size, tx_data);
621
622         ao_radio_get(encode_len);
623
624         started = 0;
625         fifo_space = CC115L_FIFO_SIZE;
626         while (encode_len) {
627                 this_len = encode_len;
628
629                 ao_radio_wake = 0;
630                 if (this_len > fifo_space) {
631                         this_len = fifo_space;
632                         ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF);
633                 } else {
634                         ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_FINISH);
635                 }
636
637                 ao_radio_fifo_write(e, this_len);
638                 e += this_len;
639                 encode_len -= this_len;
640
641                 if (!started) {
642                         ao_radio_start_tx();
643                         started = 1;
644                 } else {
645                         ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
646                 }
647                         
648                 fifo_space = ao_radio_wait_tx(encode_len != 0);
649                 if (ao_radio_abort) {
650                         ao_radio_idle();
651                         break;
652                 }
653         }
654         ao_radio_pa_off();
655         ao_radio_put();
656 }
657
658 #define AO_RADIO_LOTS   64
659
660 void
661 ao_radio_send_lots(ao_radio_fill_func fill)
662 {
663         uint8_t buf[AO_RADIO_LOTS], *b;
664         int     cnt;
665         int     total = 0;
666         uint8_t done = 0;
667         uint8_t started = 0;
668         uint8_t fifo_space;
669
670         ao_radio_get(0xff);
671         fifo_space = CC115L_FIFO_SIZE;
672         while (!done) {
673                 cnt = (*fill)(buf, sizeof(buf));
674                 if (cnt < 0) {
675                         done = 1;
676                         cnt = -cnt;
677                 }
678                 total += cnt;
679
680                 /* At the last buffer, set the total length */
681                 if (done)
682                         ao_radio_set_len(total & 0xff);
683
684                 b = buf;
685                 while (cnt) {
686                         uint8_t this_len = cnt;
687
688                         /* Wait for some space in the fifo */
689                         while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) {
690                                 ao_radio_wake = 0;
691                                 ao_radio_wait_isr();
692                         }
693                         if (ao_radio_abort)
694                                 break;
695                         if (this_len > fifo_space)
696                                 this_len = fifo_space;
697
698                         cnt -= this_len;
699
700                         if (done) {
701                                 if (cnt)
702                                         ao_radio_set_mode(AO_RADIO_MODE_APRS_LAST_BUF);
703                                 else
704                                         ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH);
705                         } else
706                                 ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF);
707
708                         ao_radio_fifo_write(b, this_len);
709                         b += this_len;
710
711                         if (!started) {
712                                 ao_radio_start_tx();
713                                 started = 1;
714                         } else
715                                 ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
716                 }
717                 if (ao_radio_abort) {
718                         ao_radio_idle();
719                         break;
720                 }
721                 /* Wait for the transmitter to go idle */
722                 ao_radio_wake = 0;
723                 ao_radio_wait_isr();
724         }
725         ao_radio_pa_off();
726         ao_radio_put();
727 }
728
729 static char *cc115l_state_name[] = {
730         [CC115L_STATUS_STATE_IDLE] = "IDLE",
731         [CC115L_STATUS_STATE_TX] = "TX",
732         [CC115L_STATUS_STATE_FSTXON] = "FSTXON",
733         [CC115L_STATUS_STATE_CALIBRATE] = "CALIBRATE",
734         [CC115L_STATUS_STATE_SETTLING] = "SETTLING",
735         [CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW] = "TX_FIFO_UNDERFLOW",
736 };
737
738 struct ao_cc115l_reg {
739         uint16_t        addr;
740         char            *name;
741 };
742
743 const static struct ao_cc115l_reg ao_cc115l_reg[] = {
744         { .addr = CC115L_IOCFG2, .name = "IOCFG2" },
745         { .addr = CC115L_IOCFG1, .name = "IOCFG1" },
746         { .addr = CC115L_IOCFG0, .name = "IOCFG0" },
747         { .addr = CC115L_FIFOTHR, .name = "FIFOTHR" },
748         { .addr = CC115L_SYNC1, .name = "SYNC1" },
749         { .addr = CC115L_SYNC0, .name = "SYNC0" },
750         { .addr = CC115L_PKTLEN, .name = "PKTLEN" },
751         { .addr = CC115L_PKTCTRL0, .name = "PKTCTRL0" },
752         { .addr = CC115L_CHANNR, .name = "CHANNR" },
753         { .addr = CC115L_FSCTRL0, .name = "FSCTRL0" },
754         { .addr = CC115L_FREQ2, .name = "FREQ2" },
755         { .addr = CC115L_FREQ1, .name = "FREQ1" },
756         { .addr = CC115L_FREQ0, .name = "FREQ0" },
757         { .addr = CC115L_MDMCFG4, .name = "MDMCFG4" },
758         { .addr = CC115L_MDMCFG3, .name = "MDMCFG3" },
759         { .addr = CC115L_MDMCFG2, .name = "MDMCFG2" },
760         { .addr = CC115L_MDMCFG1, .name = "MDMCFG1" },
761         { .addr = CC115L_MDMCFG0, .name = "MDMCFG0" },
762         { .addr = CC115L_DEVIATN, .name = "DEVIATN" },
763         { .addr = CC115L_MCSM1, .name = "MCSM1" },
764         { .addr = CC115L_MCSM0, .name = "MCSM0" },
765         { .addr = CC115L_RESERVED_0X20, .name = "RESERVED_0X20" },
766         { .addr = CC115L_FREND0, .name = "FREND0" },
767         { .addr = CC115L_FSCAL3, .name = "FSCAL3" },
768         { .addr = CC115L_FSCAL2, .name = "FSCAL2" },
769         { .addr = CC115L_FSCAL1, .name = "FSCAL1" },
770         { .addr = CC115L_FSCAL0, .name = "FSCAL0" },
771         { .addr = CC115L_RESERVED_0X29, .name = "RESERVED_0X29" },
772         { .addr = CC115L_RESERVED_0X2A, .name = "RESERVED_0X2A" },
773         { .addr = CC115L_RESERVED_0X2B, .name = "RESERVED_0X2B" },
774         { .addr = CC115L_TEST2, .name = "TEST2" },
775         { .addr = CC115L_TEST1, .name = "TEST1" },
776         { .addr = CC115L_TEST0, .name = "TEST0" },
777         { .addr = CC115L_PARTNUM, .name = "PARTNUM" },
778         { .addr = CC115L_VERSION, .name = "VERSION" },
779         { .addr = CC115L_MARCSTATE, .name = "MARCSTATE" },
780         { .addr = CC115L_PKTSTATUS, .name = "PKTSTATUS" },
781         { .addr = CC115L_TXBYTES, .name = "TXBYTES" },
782         { .addr = CC115L_PA, .name = "PA" },
783 };
784
785 #define AO_NUM_CC115L_REG       (sizeof ao_cc115l_reg / sizeof ao_cc115l_reg[0])
786
787 static void ao_radio_show(void) {
788         uint8_t status = ao_radio_status();
789         int     i;
790
791         ao_radio_get(0xff);
792         status = ao_radio_status();
793         printf ("Status:   %02x\n", status);
794         printf ("CHIP_RDY: %d\n", (status >> CC115L_STATUS_CHIP_RDY) & 1);
795         printf ("STATE:    %s\n", cc115l_state_name[(status >> CC115L_STATUS_STATE) & CC115L_STATUS_STATE_MASK]);
796         printf ("MARC:     %02x\n", ao_radio_get_marcstate());
797
798         for (i = 0; i < AO_NUM_CC115L_REG; i++)
799                 printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc115l_reg[i].addr), ao_cc115l_reg[i].name);
800         ao_radio_put();
801 }
802
803 static void ao_radio_beep(void) {
804         ao_radio_rdf();
805 }
806
807 static void ao_radio_packet(void) {
808         static const uint8_t packet[] = {
809 #if 1
810                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
811                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
812                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
813                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
814 #else
815                 3, 1, 2, 3
816 #endif
817         };
818
819         ao_radio_send(packet, sizeof (packet));
820 }
821
822 #if HAS_APRS
823 #include <ao_aprs.h>
824
825 static void
826 ao_radio_aprs()
827 {
828 #if PACKET_HAS_SLAVE
829         ao_packet_slave_stop();
830 #endif
831         ao_aprs_send();
832 }
833 #endif
834
835 static const struct ao_cmds ao_radio_cmds[] = {
836         { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
837 #if CC115L_DEBUG
838 #if HAS_APRS
839         { ao_radio_aprs,        "G\0Send APRS packet" },
840 #endif
841         { ao_radio_show,        "R\0Show CC115L status" },
842         { ao_radio_beep,        "b\0Emit an RDF beacon" },
843         { ao_radio_packet,      "p\0Send a test packet" },
844 #endif
845         { 0, NULL }
846 };
847
848 void
849 ao_radio_init(void)
850 {
851         int     i;
852
853         ao_radio_configured = 0;
854         ao_spi_init_cs (AO_CC115L_SPI_CS_PORT, (1 << AO_CC115L_SPI_CS_PIN));
855
856 #if 0
857         AO_CC115L_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC115L_SPI_CS_PIN));
858         for (i = 0; i < 10000; i++) {
859                 if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0)
860                         break;
861         }
862         AO_CC115L_SPI_CS_PORT->bsrr = (1 << AO_CC115L_SPI_CS_PIN);
863         if (i == 10000)
864                 ao_panic(AO_PANIC_SELF_TEST_CC115L);
865 #endif
866
867         /* Enable the EXTI interrupt for the appropriate pin */
868         ao_enable_port(AO_CC115L_INT_PORT);
869         ao_exti_setup(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN,
870                       AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
871                       ao_radio_isr);
872
873         /* Enable the hacked up GPIO3 pin */
874         ao_enable_port(AO_CC115L_MCU_WAKEUP_PORT);
875         ao_exti_setup(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN,
876                       AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED,
877                       ao_radio_mcu_wakeup_isr);
878
879         ao_radio_pa_init();
880
881         ao_cmd_register(&ao_radio_cmds[0]);
882 }