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