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