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