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