1dafab8dc0a5b8bdbe4a0063b9e7cbc345765537
[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 static 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_set_radio();
222         _ao_config_edit_finish();
223         ao_radio_recv_abort();
224 }
225
226 #if HAS_ADC
227
228 void
229 ao_config_main_deploy_show(void) __reentrant
230 {
231         printf("Main deploy: %d meters\n",
232                ao_config.main_deploy);
233 }
234
235 void
236 ao_config_main_deploy_set(void) __reentrant
237 {
238         ao_cmd_decimal();
239         if (ao_cmd_status != ao_cmd_success)
240                 return;
241         _ao_config_edit_start();
242         ao_config.main_deploy = ao_cmd_lex_i;
243         _ao_config_edit_finish();
244 }
245
246 #if HAS_ACCEL
247 void
248 ao_config_accel_calibrate_show(void) __reentrant
249 {
250         printf("Accel cal +1g: %d -1g: %d\n",
251                ao_config.accel_plus_g, ao_config.accel_minus_g);
252 }
253
254 #define ACCEL_CALIBRATE_SAMPLES 1024
255 #define ACCEL_CALIBRATE_SHIFT   10
256
257 static int16_t
258 ao_config_accel_calibrate_auto(char *orientation) __reentrant
259 {
260         uint16_t        i;
261         int32_t         accel_total;
262         uint8_t         cal_adc_ring;
263
264         printf("Orient antenna %s and press a key...", orientation);
265         flush();
266         (void) getchar();
267         puts("\r\n"); flush();
268         puts("Calibrating..."); flush();
269         i = ACCEL_CALIBRATE_SAMPLES;
270         accel_total = 0;
271         cal_adc_ring = ao_sample_adc;
272         while (i) {
273                 ao_sleep(DATA_TO_XDATA(&ao_sample_adc));
274                 while (i && cal_adc_ring != ao_sample_adc) {
275                         accel_total += (int32_t) ao_adc_ring[cal_adc_ring].accel;
276                         cal_adc_ring = ao_adc_ring_next(cal_adc_ring);
277                         i--;
278                 }
279         }
280         return accel_total >> ACCEL_CALIBRATE_SHIFT;
281 }
282
283 void
284 ao_config_accel_calibrate_set(void) __reentrant
285 {
286         int16_t up, down;
287         ao_cmd_decimal();
288         if (ao_cmd_status != ao_cmd_success)
289                 return;
290         if (ao_cmd_lex_i == 0) {
291                 up = ao_config_accel_calibrate_auto("up");
292                 down = ao_config_accel_calibrate_auto("down");
293         } else {
294                 up = ao_cmd_lex_i;
295                 ao_cmd_decimal();
296                 if (ao_cmd_status != ao_cmd_success)
297                         return;
298                 down = ao_cmd_lex_i;
299         }
300         if (up >= down) {
301                 printf("Invalid accel: up (%d) down (%d)\n",
302                        up, down);
303                 return;
304         }
305         _ao_config_edit_start();
306         ao_config.accel_plus_g = up;
307         ao_config.accel_minus_g = down;
308         _ao_config_edit_finish();
309 }
310 #endif /* HAS_ACCEL */
311
312 void
313 ao_config_apogee_delay_show(void) __reentrant
314 {
315         printf("Apogee delay: %d seconds\n",
316                ao_config.apogee_delay);
317 }
318
319 void
320 ao_config_apogee_delay_set(void) __reentrant
321 {
322         ao_cmd_decimal();
323         if (ao_cmd_status != ao_cmd_success)
324                 return;
325         _ao_config_edit_start();
326         ao_config.apogee_delay = ao_cmd_lex_i;
327         _ao_config_edit_finish();
328 }
329
330 #endif /* HAS_ADC */
331
332 void
333 ao_config_radio_cal_show(void) __reentrant
334 {
335         printf("Radio cal: %ld\n", ao_config.radio_cal);
336 }
337
338 void
339 ao_config_radio_cal_set(void) __reentrant
340 {
341         ao_cmd_decimal();
342         if (ao_cmd_status != ao_cmd_success)
343                 return;
344         _ao_config_edit_start();
345         ao_config.radio_cal = ao_cmd_lex_u32;
346         ao_config_set_radio();
347         _ao_config_edit_finish();
348 }
349
350 #if HAS_LOG
351 void
352 ao_config_log_show(void) __reentrant
353 {
354         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
355 }
356
357 void
358 ao_config_log_set(void) __reentrant
359 {
360         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
361         uint16_t        config = (uint16_t) (ao_storage_config >> 10);
362
363         ao_cmd_decimal();
364         if (ao_cmd_status != ao_cmd_success)
365                 return;
366         if (ao_log_present())
367                 printf("Storage must be empty before changing log size\n");
368         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
369                 printf("Flight log size must be multiple of %d kB\n", block);
370         else if (ao_cmd_lex_i > config)
371                 printf("Flight log max %d kB\n", config);
372         else {
373                 _ao_config_edit_start();
374                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
375                 _ao_config_edit_finish();
376         }
377 }
378 #endif /* HAS_LOG */
379
380 #if HAS_IGNITE
381 void
382 ao_config_ignite_mode_show(void) __reentrant
383 {
384         printf("Ignite mode: %d\n", ao_config.ignite_mode);
385 }
386
387 void
388 ao_config_ignite_mode_set(void) __reentrant
389 {
390         ao_cmd_decimal();
391         if (ao_cmd_status != ao_cmd_success)
392                 return;
393         _ao_config_edit_start();
394         ao_config.ignite_mode = ao_cmd_lex_i;
395         _ao_config_edit_finish();
396 }
397 #endif
398
399 #if HAS_ACCEL
400 void
401 ao_config_pad_orientation_show(void) __reentrant
402 {
403         printf("Pad orientation: %d\n", ao_config.pad_orientation);
404 }
405
406 void
407 ao_config_pad_orientation_set(void) __reentrant
408 {
409         ao_cmd_decimal();
410         if (ao_cmd_status != ao_cmd_success)
411                 return;
412         _ao_config_edit_start();
413         ao_cmd_lex_i &= 1;
414         if (ao_config.pad_orientation != ao_cmd_lex_i) {
415                 uint16_t t;
416                 t = ao_config.accel_plus_g;
417                 ao_config.accel_plus_g = 0x7fff - ao_config.accel_minus_g;
418                 ao_config.accel_minus_g = 0x7fff - t;
419         }
420         ao_config.pad_orientation = ao_cmd_lex_i;
421         _ao_config_edit_finish();
422 }
423 #endif
424
425 void
426 ao_config_radio_enable_show(void) __reentrant
427 {
428         printf("Radio enable: %d\n", ao_config.radio_enable);
429 }
430
431 void
432 ao_config_radio_enable_set(void) __reentrant
433 {
434         ao_cmd_decimal();
435         if (ao_cmd_status != ao_cmd_success)
436                 return;
437         _ao_config_edit_start();
438         ao_config.radio_enable = ao_cmd_lex_i;
439         _ao_config_edit_finish();
440 }
441         
442 #if HAS_AES
443 void
444 ao_config_key_show(void) __reentrant
445 {
446         uint8_t i;
447         printf("AES key: ");
448         for (i = 0; i < AO_AES_LEN; i++)
449                 printf ("%02x", ao_config.aes_key[i]);
450         printf("\n");
451 }
452
453 void
454 ao_config_key_set(void) __reentrant
455 {
456         uint8_t i;
457
458         _ao_config_edit_start();
459         for (i = 0; i < AO_AES_LEN; i++) {
460                 ao_cmd_hexbyte();
461                 if (ao_cmd_status != ao_cmd_success)
462                         break;
463                 ao_config.aes_key[i] = ao_cmd_lex_i;
464         }
465         _ao_config_edit_finish();
466 }
467 #endif
468
469 #if HAS_RADIO_CHANNELS
470 void
471 ao_config_radio_config_show(void) __reentrant
472 {
473         uint8_t i;
474         for (i = 0; i < AO_NUM_CHANNELS; i++)
475                 if (ao_config.radio_channels[i].name[0]) {
476                         printf("%2d %-16.16s %ld\n",
477                                i,
478                                ao_config.radio_channels[i].name,
479                                ao_config.radio_channels[i].kHz);
480                 }
481 }
482
483 void
484 ao_config_radio_config_set(void) __reentrant
485 {
486         __xdata struct ao_radio_channel * ch;
487         uint8_t i;
488         ao_cmd_decimal();
489         if (ao_cmd_status != ao_cmd_success)
490                 return;
491         if ((uint8_t) ao_cmd_lex_i >= AO_NUM_CHANNELS) {
492                 ao_cmd_status = ao_cmd_syntax_error;
493                 return;
494         }
495         ch = &ao_config.radio_channels[(uint8_t) ao_cmd_lex_i];
496         _ao_config_edit_start();
497         ao_cmd_white();
498         i = 0;
499         while (ao_cmd_lex_c != '/' && ao_cmd_lex_c != '\n' && i < AO_CHANNEL_NAME_LEN) {
500                 ch->name[i++] = ao_cmd_lex_c;
501                 ao_cmd_lex();
502         }
503         if (i < AO_CHANNEL_NAME_LEN) {
504                 ch->name[i] = '\0';
505                 ao_cmd_lex();
506         }
507         ao_cmd_decimal();
508         ch->kHz = ao_cmd_lex_u32;
509         _ao_config_edit_finish();
510 }
511 #endif
512
513 struct ao_config_var {
514         __code char     *str;
515         void            (*set)(void) __reentrant;
516         void            (*show)(void) __reentrant;
517 };
518
519 static void
520 ao_config_help(void) __reentrant;
521
522 static void
523 ao_config_show(void) __reentrant;
524
525 static void
526 ao_config_write(void) __reentrant;
527
528 __code struct ao_config_var ao_config_vars[] = {
529 #if HAS_ADC
530         { "m <meters>\0Main deploy (m)",
531           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
532         { "d <delay>\0Apogee delay (s)",
533           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
534 #endif /* HAS_ADC */
535         { "r <channel>\0Radio channel",
536           ao_config_radio_channel_set,  ao_config_radio_channel_show },
537         { "F <freq>\0Frequency (kHz)",
538           ao_config_frequency_set, ao_config_frequency_show },
539         { "c <call>\0Callsign (8 char max)",
540           ao_config_callsign_set,       ao_config_callsign_show },
541         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
542           ao_config_radio_enable_set, ao_config_radio_enable_show },
543 #if HAS_ACCEL
544         { "a <+g> <-g>\0Accel calib (0 for auto)",
545           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
546 #endif /* HAS_ACCEL */
547         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
548           ao_config_radio_cal_set,      ao_config_radio_cal_show },
549 #if HAS_LOG
550         { "l <size>\0Flight log size (kB)",
551           ao_config_log_set,            ao_config_log_show },
552 #endif
553 #if HAS_IGNITE
554         { "i <0 dual, 1 apogee, 2 main>\0Set igniter mode",
555           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
556 #endif
557 #if HAS_ACCEL
558         { "o <0 antenna up, 1 antenna down>\0Set pad orientation",
559           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
560 #endif
561 #if HAS_AES
562         { "k <32 hex digits>\0Set AES encryption key",
563           ao_config_key_set, ao_config_key_show },
564 #endif
565 #if HAS_RADIO_CHANNELS
566         { "C <n> <name>/<freq> <radio>\0Set radio chan config",
567           ao_config_radio_config_set,ao_config_radio_config_show },
568 #endif
569         { "s\0Show",
570           ao_config_show,               0 },
571 #if HAS_EEPROM
572         { "w\0Write to eeprom",
573           ao_config_write,              0 },
574 #endif
575         { "?\0Help",
576           ao_config_help,               0 },
577         { 0, 0, 0 }
578 };
579
580 void
581 ao_config_set(void)
582 {
583         char    c;
584         uint8_t cmd;
585         void (*__xdata func)(void) __reentrant;
586
587         ao_cmd_white();
588         c = ao_cmd_lex_c;
589         ao_cmd_lex();
590         func = 0;
591         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
592                 if (ao_config_vars[cmd].str[0] == c) {
593                         (*ao_config_vars[cmd].set)();
594                         return;
595                 }
596         ao_cmd_status = ao_cmd_syntax_error;
597 }
598
599 static void
600 ao_config_help(void) __reentrant
601 {
602         uint8_t cmd;
603         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
604                 printf("%-20s %s\n",
605                        ao_config_vars[cmd].str,
606                        ao_config_vars[cmd].str+1+
607                        strlen(ao_config_vars[cmd].str));
608 }
609
610 static void
611 ao_config_show(void) __reentrant
612 {
613         uint8_t cmd;
614         ao_config_get();
615         printf("Config version: %d.%d\n",
616                ao_config.major, ao_config.minor);
617         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
618                 if (ao_config_vars[cmd].show)
619                         (*ao_config_vars[cmd].show)();
620 }
621
622 #if HAS_EEPROM
623 static void
624 ao_config_write(void) __reentrant
625 {
626         uint8_t saved = 0;
627         ao_mutex_get(&ao_config_mutex);
628         if (ao_config_dirty) {
629                 _ao_config_put();
630                 ao_config_dirty = 0;
631                 saved = 1;
632         }
633         ao_mutex_put(&ao_config_mutex);
634         if (saved)
635                 puts("Saved");
636         else
637                 puts("Nothing to save");
638 }
639 #endif
640
641 __code struct ao_cmds ao_config_cmds[] = {
642         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
643         { 0, NULL },
644 };
645
646 void
647 ao_config_init(void)
648 {
649         ao_cmd_register(&ao_config_cmds[0]);
650 }