altos: Add temporary RF power settings
[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         static uint8_t  last_power_setting;
456
457         ao_mutex_get(&ao_radio_mutex);
458         if (!ao_radio_configured)
459                 ao_radio_setup();
460         if (ao_config.radio_setting != last_radio_setting) {
461                 ao_radio_reg_write(CC115L_FREQ2, ao_config.radio_setting >> 16);
462                 ao_radio_reg_write(CC115L_FREQ1, ao_config.radio_setting >> 8);
463                 ao_radio_reg_write(CC115L_FREQ0, ao_config.radio_setting);
464                 last_radio_setting = ao_config.radio_setting;
465         }
466         if (ao_config.radio_power != last_power_setting) {
467                 ao_radio_reg_write(CC115L_PA, ao_config.radio_power);
468                 last_power_setting = ao_config.radio_power;
469         }
470         ao_radio_set_len(len);
471 }
472
473 #define ao_radio_put()  ao_mutex_put(&ao_radio_mutex)
474
475 static void
476 ao_rdf_start(uint8_t len)
477 {
478         ao_radio_abort = 0;
479         ao_radio_get(len);
480
481         ao_radio_set_mode(AO_RADIO_MODE_RDF);
482         ao_radio_wake = 0;
483
484 }
485
486 static void
487 ao_rdf_run(void)
488 {
489         ao_radio_start_tx();
490
491         ao_arch_block_interrupts();
492         while (!ao_radio_wake && !ao_radio_abort && !ao_radio_mcu_wake)
493                 ao_sleep(&ao_radio_wake);
494         ao_arch_release_interrupts();
495         if (ao_radio_mcu_wake)
496                 ao_radio_check_marcstate();
497         ao_radio_pa_off();
498         if (!ao_radio_wake)
499                 ao_radio_idle();
500         ao_radio_put();
501 }
502
503 void
504 ao_radio_rdf(void)
505 {
506         ao_rdf_start(AO_RADIO_RDF_LEN);
507
508         ao_radio_fifo_write_fixed(ao_radio_rdf_value, AO_RADIO_RDF_LEN);
509
510         ao_rdf_run();
511 }
512
513 void
514 ao_radio_continuity(uint8_t c)
515 {
516         uint8_t i;
517         uint8_t status;
518
519         ao_rdf_start(AO_RADIO_CONT_TOTAL_LEN);
520
521         status = ao_radio_fifo_write_start();
522         for (i = 0; i < 3; i++) {
523                 ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
524                 if (i < c)
525                         ao_radio_spi_send_fixed(ao_radio_rdf_value, AO_RADIO_CONT_TONE_LEN);
526                 else
527                         ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_TONE_LEN);
528         }
529         ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
530         status = ao_radio_fifo_write_stop(status);
531         (void) status;
532         ao_rdf_run();
533 }
534
535 void
536 ao_radio_rdf_abort(void)
537 {
538         ao_radio_abort = 1;
539         ao_wakeup(&ao_radio_wake);
540 }
541
542 static void
543 ao_radio_test_cmd(void)
544 {
545         uint8_t mode = 2;
546         static uint8_t radio_on;
547         ao_cmd_white();
548         if (ao_cmd_lex_c != '\n') {
549                 ao_cmd_decimal();
550                 mode = (uint8_t) ao_cmd_lex_u32;
551         }
552         mode++;
553         if ((mode & 2) && !radio_on) {
554 #if HAS_MONITOR
555                 ao_monitor_disable();
556 #endif
557 #if PACKET_HAS_SLAVE
558                 ao_packet_slave_stop();
559 #endif
560                 ao_radio_get(0xff);
561                 ao_radio_pa_on();
562                 ao_radio_strobe(CC115L_STX);
563 #if CC115L_TRACE
564                 { int t; 
565                         for (t = 0; t < 10; t++) {
566                                 printf ("status: %02x\n", ao_radio_status());
567                                 ao_delay(AO_MS_TO_TICKS(100));
568                         }
569                 }
570 #endif
571                 radio_on = 1;
572         }
573         if (mode == 3) {
574                 printf ("Hit a character to stop..."); flush();
575                 getchar();
576                 putchar('\n');
577         }
578         if ((mode & 1) && radio_on) {
579                 ao_radio_idle();
580                 ao_radio_put();
581                 radio_on = 0;
582 #if HAS_MONITOR
583                 ao_monitor_enable();
584 #endif
585         }
586 }
587
588 static void
589 ao_radio_wait_isr(void)
590 {
591         ao_arch_block_interrupts();
592         while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort)
593                 ao_sleep(&ao_radio_wake);
594         ao_arch_release_interrupts();
595         if (ao_radio_mcu_wake)
596                 ao_radio_check_marcstate();
597 }
598
599 static uint8_t
600 ao_radio_wait_tx(uint8_t wait_fifo)
601 {
602         uint8_t fifo_space = 0;
603
604         do {
605                 ao_radio_wait_isr();
606                 if (!wait_fifo)
607                         return 0;
608                 fifo_space = ao_radio_tx_fifo_space();
609         } while (!fifo_space && !ao_radio_abort);
610         return fifo_space;
611 }
612
613 static uint8_t  tx_data[(AO_RADIO_MAX_SEND + 4) * 2];
614
615 void
616 ao_radio_send(const void *d, uint8_t size)
617 {
618         uint8_t         marc_status;
619         uint8_t         *e = tx_data;
620         uint8_t         encode_len;
621         uint8_t         this_len;
622         uint8_t         started = 0;
623         uint8_t         fifo_space;
624
625         encode_len = ao_fec_encode(d, size, tx_data);
626
627         ao_radio_get(encode_len);
628
629         started = 0;
630         fifo_space = CC115L_FIFO_SIZE;
631         while (encode_len) {
632                 this_len = encode_len;
633
634                 ao_radio_wake = 0;
635                 if (this_len > fifo_space) {
636                         this_len = fifo_space;
637                         ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF);
638                 } else {
639                         ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_FINISH);
640                 }
641
642                 ao_radio_fifo_write(e, this_len);
643                 e += this_len;
644                 encode_len -= this_len;
645
646                 if (!started) {
647                         ao_radio_start_tx();
648                         started = 1;
649                 } else {
650                         ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
651                 }
652                         
653                 fifo_space = ao_radio_wait_tx(encode_len != 0);
654                 if (ao_radio_abort) {
655                         ao_radio_idle();
656                         break;
657                 }
658         }
659         ao_radio_pa_off();
660         ao_radio_put();
661 }
662
663 #define AO_RADIO_LOTS   64
664
665 void
666 ao_radio_send_lots(ao_radio_fill_func fill)
667 {
668         uint8_t buf[AO_RADIO_LOTS], *b;
669         int     cnt;
670         int     total = 0;
671         uint8_t done = 0;
672         uint8_t started = 0;
673         uint8_t fifo_space;
674
675         ao_radio_get(0xff);
676         fifo_space = CC115L_FIFO_SIZE;
677         while (!done) {
678                 cnt = (*fill)(buf, sizeof(buf));
679                 if (cnt < 0) {
680                         done = 1;
681                         cnt = -cnt;
682                 }
683                 total += cnt;
684
685                 /* At the last buffer, set the total length */
686                 if (done)
687                         ao_radio_set_len(total & 0xff);
688
689                 b = buf;
690                 while (cnt) {
691                         uint8_t this_len = cnt;
692
693                         /* Wait for some space in the fifo */
694                         while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) {
695                                 ao_radio_wake = 0;
696                                 ao_radio_wait_isr();
697                         }
698                         if (ao_radio_abort)
699                                 break;
700                         if (this_len > fifo_space)
701                                 this_len = fifo_space;
702
703                         cnt -= this_len;
704
705                         if (done) {
706                                 if (cnt)
707                                         ao_radio_set_mode(AO_RADIO_MODE_APRS_LAST_BUF);
708                                 else
709                                         ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH);
710                         } else
711                                 ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF);
712
713                         ao_radio_fifo_write(b, this_len);
714                         b += this_len;
715
716                         if (!started) {
717                                 ao_radio_start_tx();
718                                 started = 1;
719                         } else
720                                 ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN);
721                 }
722                 if (ao_radio_abort) {
723                         ao_radio_idle();
724                         break;
725                 }
726                 /* Wait for the transmitter to go idle */
727                 ao_radio_wake = 0;
728                 ao_radio_wait_isr();
729         }
730         ao_radio_pa_off();
731         ao_radio_put();
732 }
733
734 static char *cc115l_state_name[] = {
735         [CC115L_STATUS_STATE_IDLE] = "IDLE",
736         [CC115L_STATUS_STATE_TX] = "TX",
737         [CC115L_STATUS_STATE_FSTXON] = "FSTXON",
738         [CC115L_STATUS_STATE_CALIBRATE] = "CALIBRATE",
739         [CC115L_STATUS_STATE_SETTLING] = "SETTLING",
740         [CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW] = "TX_FIFO_UNDERFLOW",
741 };
742
743 struct ao_cc115l_reg {
744         uint16_t        addr;
745         char            *name;
746 };
747
748 const static struct ao_cc115l_reg ao_cc115l_reg[] = {
749         { .addr = CC115L_IOCFG2, .name = "IOCFG2" },
750         { .addr = CC115L_IOCFG1, .name = "IOCFG1" },
751         { .addr = CC115L_IOCFG0, .name = "IOCFG0" },
752         { .addr = CC115L_FIFOTHR, .name = "FIFOTHR" },
753         { .addr = CC115L_SYNC1, .name = "SYNC1" },
754         { .addr = CC115L_SYNC0, .name = "SYNC0" },
755         { .addr = CC115L_PKTLEN, .name = "PKTLEN" },
756         { .addr = CC115L_PKTCTRL0, .name = "PKTCTRL0" },
757         { .addr = CC115L_CHANNR, .name = "CHANNR" },
758         { .addr = CC115L_FSCTRL0, .name = "FSCTRL0" },
759         { .addr = CC115L_FREQ2, .name = "FREQ2" },
760         { .addr = CC115L_FREQ1, .name = "FREQ1" },
761         { .addr = CC115L_FREQ0, .name = "FREQ0" },
762         { .addr = CC115L_MDMCFG4, .name = "MDMCFG4" },
763         { .addr = CC115L_MDMCFG3, .name = "MDMCFG3" },
764         { .addr = CC115L_MDMCFG2, .name = "MDMCFG2" },
765         { .addr = CC115L_MDMCFG1, .name = "MDMCFG1" },
766         { .addr = CC115L_MDMCFG0, .name = "MDMCFG0" },
767         { .addr = CC115L_DEVIATN, .name = "DEVIATN" },
768         { .addr = CC115L_MCSM1, .name = "MCSM1" },
769         { .addr = CC115L_MCSM0, .name = "MCSM0" },
770         { .addr = CC115L_RESERVED_0X20, .name = "RESERVED_0X20" },
771         { .addr = CC115L_FREND0, .name = "FREND0" },
772         { .addr = CC115L_FSCAL3, .name = "FSCAL3" },
773         { .addr = CC115L_FSCAL2, .name = "FSCAL2" },
774         { .addr = CC115L_FSCAL1, .name = "FSCAL1" },
775         { .addr = CC115L_FSCAL0, .name = "FSCAL0" },
776         { .addr = CC115L_RESERVED_0X29, .name = "RESERVED_0X29" },
777         { .addr = CC115L_RESERVED_0X2A, .name = "RESERVED_0X2A" },
778         { .addr = CC115L_RESERVED_0X2B, .name = "RESERVED_0X2B" },
779         { .addr = CC115L_TEST2, .name = "TEST2" },
780         { .addr = CC115L_TEST1, .name = "TEST1" },
781         { .addr = CC115L_TEST0, .name = "TEST0" },
782         { .addr = CC115L_PARTNUM, .name = "PARTNUM" },
783         { .addr = CC115L_VERSION, .name = "VERSION" },
784         { .addr = CC115L_MARCSTATE, .name = "MARCSTATE" },
785         { .addr = CC115L_PKTSTATUS, .name = "PKTSTATUS" },
786         { .addr = CC115L_TXBYTES, .name = "TXBYTES" },
787         { .addr = CC115L_PA, .name = "PA" },
788 };
789
790 #define AO_NUM_CC115L_REG       (sizeof ao_cc115l_reg / sizeof ao_cc115l_reg[0])
791
792 static void ao_radio_show(void) {
793         uint8_t status = ao_radio_status();
794         int     i;
795
796         ao_radio_get(0xff);
797         status = ao_radio_status();
798         printf ("Status:   %02x\n", status);
799         printf ("CHIP_RDY: %d\n", (status >> CC115L_STATUS_CHIP_RDY) & 1);
800         printf ("STATE:    %s\n", cc115l_state_name[(status >> CC115L_STATUS_STATE) & CC115L_STATUS_STATE_MASK]);
801         printf ("MARC:     %02x\n", ao_radio_get_marcstate());
802
803         for (i = 0; i < AO_NUM_CC115L_REG; i++)
804                 printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc115l_reg[i].addr), ao_cc115l_reg[i].name);
805         ao_radio_put();
806 }
807
808 static void ao_radio_beep(void) {
809         ao_radio_rdf();
810 }
811
812 static void ao_radio_packet(void) {
813         static const uint8_t packet[] = {
814 #if 1
815                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
816                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
817                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
818                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
819 #else
820                 3, 1, 2, 3
821 #endif
822         };
823
824         ao_radio_send(packet, sizeof (packet));
825 }
826
827 #if HAS_APRS
828 #include <ao_aprs.h>
829
830 static void
831 ao_radio_aprs()
832 {
833 #if PACKET_HAS_SLAVE
834         ao_packet_slave_stop();
835 #endif
836         ao_aprs_send();
837 }
838 #endif
839
840 static const struct ao_cmds ao_radio_cmds[] = {
841         { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
842 #if CC115L_DEBUG
843 #if HAS_APRS
844         { ao_radio_aprs,        "G\0Send APRS packet" },
845 #endif
846         { ao_radio_show,        "R\0Show CC115L status" },
847         { ao_radio_beep,        "b\0Emit an RDF beacon" },
848         { ao_radio_packet,      "p\0Send a test packet" },
849 #endif
850         { 0, NULL }
851 };
852
853 void
854 ao_radio_init(void)
855 {
856         int     i;
857
858         ao_radio_configured = 0;
859         ao_spi_init_cs (AO_CC115L_SPI_CS_PORT, (1 << AO_CC115L_SPI_CS_PIN));
860
861 #if 0
862         AO_CC115L_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC115L_SPI_CS_PIN));
863         for (i = 0; i < 10000; i++) {
864                 if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0)
865                         break;
866         }
867         AO_CC115L_SPI_CS_PORT->bsrr = (1 << AO_CC115L_SPI_CS_PIN);
868         if (i == 10000)
869                 ao_panic(AO_PANIC_SELF_TEST_CC115L);
870 #endif
871
872         /* Enable the EXTI interrupt for the appropriate pin */
873         ao_enable_port(AO_CC115L_INT_PORT);
874         ao_exti_setup(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN,
875                       AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
876                       ao_radio_isr);
877
878         /* Enable the hacked up GPIO3 pin */
879         ao_enable_port(AO_CC115L_MCU_WAKEUP_PORT);
880         ao_exti_setup(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN,
881                       AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED,
882                       ao_radio_mcu_wakeup_isr);
883
884         ao_radio_pa_init();
885
886         ao_cmd_register(&ao_radio_cmds[0]);
887 }