altos: Make ao_config_set_radio public
[fw/altos] / src / core / ao_config.c
1 /*
2  * Copyright © 2009 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
20 __xdata struct ao_config ao_config;
21 __pdata uint8_t ao_config_loaded;
22 __pdata uint8_t ao_config_dirty;
23 __xdata uint8_t ao_config_mutex;
24
25 #define AO_CONFIG_DEFAULT_MAIN_DEPLOY   250
26 #define AO_CONFIG_DEFAULT_RADIO_CHANNEL 0
27 #define AO_CONFIG_DEFAULT_CALLSIGN      "N0CALL"
28 #define AO_CONFIG_DEFAULT_ACCEL_ZERO_G  16000
29 #define AO_CONFIG_DEFAULT_APOGEE_DELAY  0
30 #define AO_CONFIG_DEFAULT_IGNITE_MODE   AO_IGNITE_MODE_DUAL
31 #define AO_CONFIG_DEFAULT_PAD_ORIENTATION       AO_PAD_ORIENTATION_ANTENNA_UP
32 #if HAS_EEPROM
33 #ifndef USE_INTERNAL_FLASH
34 #error Please define USE_INTERNAL_FLASH
35 #endif
36 #endif
37 #ifndef AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX
38 #if USE_INTERNAL_FLASH
39 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ao_storage_config
40 #else
41 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ((uint32_t) 192 * (uint32_t) 1024)
42 #endif
43 #endif
44
45 #if HAS_EEPROM
46 static void
47 _ao_config_put(void)
48 {
49         ao_storage_setup();
50         ao_storage_erase(ao_storage_config);
51         ao_storage_write(ao_storage_config, &ao_config, sizeof (ao_config));
52 #if HAS_FLIGHT
53         ao_log_write_erase(0);
54 #endif
55         ao_storage_flush();
56 }
57
58 void
59 ao_config_put(void)
60 {
61         ao_mutex_get(&ao_config_mutex);
62         _ao_config_put();
63         ao_mutex_put(&ao_config_mutex);
64 }
65 #endif
66
67 void
68 ao_config_set_radio(void)
69 {
70         ao_config.radio_setting = ao_freq_to_set(ao_config.frequency, ao_config.radio_cal);
71 }
72
73 static void
74 _ao_config_get(void)
75 {
76         if (ao_config_loaded)
77                 return;
78 #if HAS_EEPROM
79         ao_storage_setup();
80         ao_storage_read(ao_storage_config, &ao_config, sizeof (ao_config));
81 #endif
82         if (ao_config.major != AO_CONFIG_MAJOR) {
83                 ao_config.major = AO_CONFIG_MAJOR;
84                 ao_config.minor = 0;
85
86                 /* Version 0 stuff */
87                 ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
88                 ao_config.radio_channel = AO_CONFIG_DEFAULT_RADIO_CHANNEL;
89                 ao_xmemset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
90                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
91                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
92                 ao_config_dirty = 1;
93         }
94         if (ao_config.minor != AO_CONFIG_MINOR) {
95                 /* Fixups for minor version 1 */
96                 if (ao_config.minor < 1)
97                         ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
98                 /* Fixups for minor version 2 */
99                 if (ao_config.minor < 2) {
100                         ao_config.accel_plus_g = 0;
101                         ao_config.accel_minus_g = 0;
102                 }
103                 /* Fixups for minor version 3 */
104                 if (ao_config.minor < 3)
105                         ao_config.radio_cal = ao_radio_cal;
106                 /* Fixups for minor version 4 */
107                 if (ao_config.minor < 4)
108                         ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
109                 /* Fixupes for minor version 5 */
110                 if (ao_config.minor < 5)
111                         ao_config.ignite_mode = AO_CONFIG_DEFAULT_IGNITE_MODE;
112                 if (ao_config.minor < 6)
113                         ao_config.pad_orientation = AO_CONFIG_DEFAULT_PAD_ORIENTATION;
114                 if (ao_config.minor < 8)
115                         ao_config.radio_enable = TRUE;
116 #if HAS_AES
117                 if (ao_config.minor < 9)
118                         ao_xmemset(&ao_config.aes_key, '\0', AO_AES_LEN);
119 #endif
120                 if (ao_config.minor < 10) {
121                         ao_config.frequency = 434550;
122 #if HAS_RADIO_CHANNELS
123                         ao_xmemset(&ao_config.radio_channels, '\0', sizeof (ao_config.radio_channels));
124                         ao_xmemcpy(&ao_config.radio_channels[0].name[0],
125                                    CODE_TO_XDATA("Channel 0"), sizeof("Channel 0"));
126                         ao_config.radio_channels[0].kHz = 434550;
127 #endif                             
128                 }
129                 ao_config.minor = AO_CONFIG_MINOR;
130                 ao_config_dirty = 1;
131         }
132         ao_config_set_radio();
133         ao_config_loaded = 1;
134 }
135
136 static void
137 _ao_config_edit_start(void)
138 {
139         ao_mutex_get(&ao_config_mutex);
140         _ao_config_get();
141 }
142
143 static void
144 _ao_config_edit_finish(void)
145 {
146         ao_config_dirty = 1;
147         ao_mutex_put(&ao_config_mutex);
148 }
149
150 void
151 ao_config_get(void)
152 {
153         _ao_config_edit_start();
154         ao_mutex_put(&ao_config_mutex);
155 }
156
157 void
158 ao_config_callsign_show(void)
159 {
160         printf ("Callsign: \"%s\"\n", ao_config.callsign);
161 }
162
163 void
164 ao_config_callsign_set(void) __reentrant
165 {
166         uint8_t c;
167         static __xdata char callsign[AO_MAX_CALLSIGN + 1];
168
169         ao_xmemset(callsign, '\0', sizeof callsign);
170         ao_cmd_white();
171         c = 0;
172         while (ao_cmd_lex_c != '\n') {
173                 if (c < AO_MAX_CALLSIGN)
174                         callsign[c++] = ao_cmd_lex_c;
175                 else
176                         ao_cmd_status = ao_cmd_lex_error;
177                 ao_cmd_lex();
178         }
179         if (ao_cmd_status != ao_cmd_success)
180                 return;
181         _ao_config_edit_start();
182         ao_xmemcpy(&ao_config.callsign, &callsign,
183                AO_MAX_CALLSIGN + 1);
184         _ao_config_edit_finish();
185 }
186
187 void
188 ao_config_radio_channel_show(void) __reentrant
189 {
190         printf("Radio channel: %d\n",
191                ao_config.radio_channel);
192 }
193
194 void
195 ao_config_radio_channel_set(void) __reentrant
196 {
197         ao_cmd_decimal();
198         if (ao_cmd_status != ao_cmd_success)
199                 return;
200         _ao_config_edit_start();
201         ao_config.radio_channel = ao_cmd_lex_i;
202         _ao_config_edit_finish();
203         ao_radio_recv_abort();
204 }
205
206 void
207 ao_config_frequency_show(void) __reentrant
208 {
209         printf("Frequency: %ld\n",
210                ao_config.frequency);
211 }
212
213 void
214 ao_config_frequency_set(void) __reentrant
215 {
216         ao_cmd_decimal();
217         if (ao_cmd_status != ao_cmd_success)
218                 return;
219         _ao_config_edit_start();
220         ao_config.frequency = ao_cmd_lex_u32;
221         ao_config.radio_channel = 0;
222         ao_config_set_radio();
223         _ao_config_edit_finish();
224         ao_radio_recv_abort();
225 }
226
227 #if HAS_ADC
228
229 void
230 ao_config_main_deploy_show(void) __reentrant
231 {
232         printf("Main deploy: %d meters\n",
233                ao_config.main_deploy);
234 }
235
236 void
237 ao_config_main_deploy_set(void) __reentrant
238 {
239         ao_cmd_decimal();
240         if (ao_cmd_status != ao_cmd_success)
241                 return;
242         _ao_config_edit_start();
243         ao_config.main_deploy = ao_cmd_lex_i;
244         _ao_config_edit_finish();
245 }
246
247 #if HAS_ACCEL
248 void
249 ao_config_accel_calibrate_show(void) __reentrant
250 {
251         printf("Accel cal +1g: %d -1g: %d\n",
252                ao_config.accel_plus_g, ao_config.accel_minus_g);
253 }
254
255 #define ACCEL_CALIBRATE_SAMPLES 1024
256 #define ACCEL_CALIBRATE_SHIFT   10
257
258 static int16_t
259 ao_config_accel_calibrate_auto(char *orientation) __reentrant
260 {
261         uint16_t        i;
262         int32_t         accel_total;
263         uint8_t         cal_adc_ring;
264
265         printf("Orient antenna %s and press a key...", orientation);
266         flush();
267         (void) getchar();
268         puts("\r\n"); flush();
269         puts("Calibrating..."); flush();
270         i = ACCEL_CALIBRATE_SAMPLES;
271         accel_total = 0;
272         cal_adc_ring = ao_sample_adc;
273         while (i) {
274                 ao_sleep(DATA_TO_XDATA(&ao_sample_adc));
275                 while (i && cal_adc_ring != ao_sample_adc) {
276                         accel_total += (int32_t) ao_adc_ring[cal_adc_ring].accel;
277                         cal_adc_ring = ao_adc_ring_next(cal_adc_ring);
278                         i--;
279                 }
280         }
281         return accel_total >> ACCEL_CALIBRATE_SHIFT;
282 }
283
284 void
285 ao_config_accel_calibrate_set(void) __reentrant
286 {
287         int16_t up, down;
288         ao_cmd_decimal();
289         if (ao_cmd_status != ao_cmd_success)
290                 return;
291         if (ao_cmd_lex_i == 0) {
292                 up = ao_config_accel_calibrate_auto("up");
293                 down = ao_config_accel_calibrate_auto("down");
294         } else {
295                 up = ao_cmd_lex_i;
296                 ao_cmd_decimal();
297                 if (ao_cmd_status != ao_cmd_success)
298                         return;
299                 down = ao_cmd_lex_i;
300         }
301         if (up >= down) {
302                 printf("Invalid accel: up (%d) down (%d)\n",
303                        up, down);
304                 return;
305         }
306         _ao_config_edit_start();
307         ao_config.accel_plus_g = up;
308         ao_config.accel_minus_g = down;
309         _ao_config_edit_finish();
310 }
311 #endif /* HAS_ACCEL */
312
313 void
314 ao_config_apogee_delay_show(void) __reentrant
315 {
316         printf("Apogee delay: %d seconds\n",
317                ao_config.apogee_delay);
318 }
319
320 void
321 ao_config_apogee_delay_set(void) __reentrant
322 {
323         ao_cmd_decimal();
324         if (ao_cmd_status != ao_cmd_success)
325                 return;
326         _ao_config_edit_start();
327         ao_config.apogee_delay = ao_cmd_lex_i;
328         _ao_config_edit_finish();
329 }
330
331 #endif /* HAS_ADC */
332
333 void
334 ao_config_radio_cal_show(void) __reentrant
335 {
336         printf("Radio cal: %ld\n", ao_config.radio_cal);
337 }
338
339 void
340 ao_config_radio_cal_set(void) __reentrant
341 {
342         ao_cmd_decimal();
343         if (ao_cmd_status != ao_cmd_success)
344                 return;
345         _ao_config_edit_start();
346         ao_config.radio_cal = ao_cmd_lex_u32;
347         ao_config_set_radio();
348         _ao_config_edit_finish();
349 }
350
351 #if HAS_LOG
352 void
353 ao_config_log_show(void) __reentrant
354 {
355         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
356 }
357
358 void
359 ao_config_log_set(void) __reentrant
360 {
361         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
362         uint16_t        config = (uint16_t) (ao_storage_config >> 10);
363
364         ao_cmd_decimal();
365         if (ao_cmd_status != ao_cmd_success)
366                 return;
367         if (ao_log_present())
368                 printf("Storage must be empty before changing log size\n");
369         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
370                 printf("Flight log size must be multiple of %d kB\n", block);
371         else if (ao_cmd_lex_i > config)
372                 printf("Flight log max %d kB\n", config);
373         else {
374                 _ao_config_edit_start();
375                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
376                 _ao_config_edit_finish();
377         }
378 }
379 #endif /* HAS_LOG */
380
381 #if HAS_IGNITE
382 void
383 ao_config_ignite_mode_show(void) __reentrant
384 {
385         printf("Ignite mode: %d\n", ao_config.ignite_mode);
386 }
387
388 void
389 ao_config_ignite_mode_set(void) __reentrant
390 {
391         ao_cmd_decimal();
392         if (ao_cmd_status != ao_cmd_success)
393                 return;
394         _ao_config_edit_start();
395         ao_config.ignite_mode = ao_cmd_lex_i;
396         _ao_config_edit_finish();
397 }
398 #endif
399
400 #if HAS_ACCEL
401 void
402 ao_config_pad_orientation_show(void) __reentrant
403 {
404         printf("Pad orientation: %d\n", ao_config.pad_orientation);
405 }
406
407 void
408 ao_config_pad_orientation_set(void) __reentrant
409 {
410         ao_cmd_decimal();
411         if (ao_cmd_status != ao_cmd_success)
412                 return;
413         _ao_config_edit_start();
414         ao_cmd_lex_i &= 1;
415         if (ao_config.pad_orientation != ao_cmd_lex_i) {
416                 uint16_t t;
417                 t = ao_config.accel_plus_g;
418                 ao_config.accel_plus_g = 0x7fff - ao_config.accel_minus_g;
419                 ao_config.accel_minus_g = 0x7fff - t;
420         }
421         ao_config.pad_orientation = ao_cmd_lex_i;
422         _ao_config_edit_finish();
423 }
424 #endif
425
426 void
427 ao_config_radio_enable_show(void) __reentrant
428 {
429         printf("Radio enable: %d\n", ao_config.radio_enable);
430 }
431
432 void
433 ao_config_radio_enable_set(void) __reentrant
434 {
435         ao_cmd_decimal();
436         if (ao_cmd_status != ao_cmd_success)
437                 return;
438         _ao_config_edit_start();
439         ao_config.radio_enable = ao_cmd_lex_i;
440         _ao_config_edit_finish();
441 }
442         
443 #if HAS_AES
444 void
445 ao_config_key_show(void) __reentrant
446 {
447         uint8_t i;
448         printf("AES key: ");
449         for (i = 0; i < AO_AES_LEN; i++)
450                 printf ("%02x", ao_config.aes_key[i]);
451         printf("\n");
452 }
453
454 void
455 ao_config_key_set(void) __reentrant
456 {
457         uint8_t i;
458
459         _ao_config_edit_start();
460         for (i = 0; i < AO_AES_LEN; i++) {
461                 ao_cmd_hexbyte();
462                 if (ao_cmd_status != ao_cmd_success)
463                         break;
464                 ao_config.aes_key[i] = ao_cmd_lex_i;
465         }
466         _ao_config_edit_finish();
467 }
468 #endif
469
470 #if HAS_RADIO_CHANNELS
471 void
472 ao_config_radio_config_show(void) __reentrant
473 {
474         uint8_t i;
475         for (i = 0; i < AO_NUM_CHANNELS; i++)
476                 if (ao_config.radio_channels[i].name[0]) {
477                         printf("%2d %-16.16s %ld\n",
478                                i,
479                                ao_config.radio_channels[i].name,
480                                ao_config.radio_channels[i].kHz);
481                 }
482 }
483
484 void
485 ao_config_radio_config_set(void) __reentrant
486 {
487         __xdata struct ao_radio_channel * ch;
488         uint8_t i;
489         ao_cmd_decimal();
490         if (ao_cmd_status != ao_cmd_success)
491                 return;
492         if ((uint8_t) ao_cmd_lex_i >= AO_NUM_CHANNELS) {
493                 ao_cmd_status = ao_cmd_syntax_error;
494                 return;
495         }
496         ch = &ao_config.radio_channels[(uint8_t) ao_cmd_lex_i];
497         _ao_config_edit_start();
498         ao_cmd_white();
499         i = 0;
500         while (ao_cmd_lex_c != '/' && ao_cmd_lex_c != '\n' && i < AO_CHANNEL_NAME_LEN) {
501                 ch->name[i++] = ao_cmd_lex_c;
502                 ao_cmd_lex();
503         }
504         if (i < AO_CHANNEL_NAME_LEN) {
505                 ch->name[i] = '\0';
506                 ao_cmd_lex();
507         }
508         ao_cmd_decimal();
509         ch->kHz = ao_cmd_lex_u32;
510         _ao_config_edit_finish();
511 }
512 #endif
513
514 struct ao_config_var {
515         __code char     *str;
516         void            (*set)(void) __reentrant;
517         void            (*show)(void) __reentrant;
518 };
519
520 static void
521 ao_config_help(void) __reentrant;
522
523 static void
524 ao_config_show(void) __reentrant;
525
526 static void
527 ao_config_write(void) __reentrant;
528
529 __code struct ao_config_var ao_config_vars[] = {
530 #if HAS_ADC
531         { "m <meters>\0Main deploy (m)",
532           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
533         { "d <delay>\0Apogee delay (s)",
534           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
535 #endif /* HAS_ADC */
536         { "r <channel>\0Radio channel",
537           ao_config_radio_channel_set,  ao_config_radio_channel_show },
538         { "F <freq>\0Frequency (kHz)",
539           ao_config_frequency_set, ao_config_frequency_show },
540         { "c <call>\0Callsign (8 char max)",
541           ao_config_callsign_set,       ao_config_callsign_show },
542         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
543           ao_config_radio_enable_set, ao_config_radio_enable_show },
544 #if HAS_ACCEL
545         { "a <+g> <-g>\0Accel calib (0 for auto)",
546           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
547 #endif /* HAS_ACCEL */
548         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
549           ao_config_radio_cal_set,      ao_config_radio_cal_show },
550 #if HAS_LOG
551         { "l <size>\0Flight log size (kB)",
552           ao_config_log_set,            ao_config_log_show },
553 #endif
554 #if HAS_IGNITE
555         { "i <0 dual, 1 apogee, 2 main>\0Set igniter mode",
556           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
557 #endif
558 #if HAS_ACCEL
559         { "o <0 antenna up, 1 antenna down>\0Set pad orientation",
560           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
561 #endif
562 #if HAS_AES
563         { "k <32 hex digits>\0Set AES encryption key",
564           ao_config_key_set, ao_config_key_show },
565 #endif
566 #if HAS_RADIO_CHANNELS
567         { "C <n> <name>/<freq> <radio>\0Set radio chan config",
568           ao_config_radio_config_set,ao_config_radio_config_show },
569 #endif
570         { "s\0Show",
571           ao_config_show,               0 },
572 #if HAS_EEPROM
573         { "w\0Write to eeprom",
574           ao_config_write,              0 },
575 #endif
576         { "?\0Help",
577           ao_config_help,               0 },
578         { 0, 0, 0 }
579 };
580
581 void
582 ao_config_set(void)
583 {
584         char    c;
585         uint8_t cmd;
586         void (*__xdata func)(void) __reentrant;
587
588         ao_cmd_white();
589         c = ao_cmd_lex_c;
590         ao_cmd_lex();
591         func = 0;
592         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
593                 if (ao_config_vars[cmd].str[0] == c) {
594                         (*ao_config_vars[cmd].set)();
595                         return;
596                 }
597         ao_cmd_status = ao_cmd_syntax_error;
598 }
599
600 static void
601 ao_config_help(void) __reentrant
602 {
603         uint8_t cmd;
604         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
605                 printf("%-20s %s\n",
606                        ao_config_vars[cmd].str,
607                        ao_config_vars[cmd].str+1+
608                        strlen(ao_config_vars[cmd].str));
609 }
610
611 static void
612 ao_config_show(void) __reentrant
613 {
614         uint8_t cmd;
615         ao_config_get();
616         printf("Config version: %d.%d\n",
617                ao_config.major, ao_config.minor);
618         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
619                 if (ao_config_vars[cmd].show)
620                         (*ao_config_vars[cmd].show)();
621 }
622
623 #if HAS_EEPROM
624 static void
625 ao_config_write(void) __reentrant
626 {
627         uint8_t saved = 0;
628         ao_mutex_get(&ao_config_mutex);
629         if (ao_config_dirty) {
630                 _ao_config_put();
631                 ao_config_dirty = 0;
632                 saved = 1;
633         }
634         ao_mutex_put(&ao_config_mutex);
635         if (saved)
636                 puts("Saved");
637         else
638                 puts("Nothing to save");
639 }
640 #endif
641
642 __code struct ao_cmds ao_config_cmds[] = {
643         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
644         { 0, NULL },
645 };
646
647 void
648 ao_config_init(void)
649 {
650         ao_cmd_register(&ao_config_cmds[0]);
651 }