altos: Remove old AO_SEND_ALL_BARO bits
[fw/altos] / src / kernel / ao_cmd.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_task.h"
20
21 __pdata uint16_t ao_cmd_lex_i;
22 __pdata uint32_t ao_cmd_lex_u32;
23 __pdata char    ao_cmd_lex_c;
24 __pdata enum ao_cmd_status ao_cmd_status;
25
26 #if AO_PYRO_NUM
27 #define CMD_LEN 128
28 #else
29 #define CMD_LEN 48
30 #endif
31
32 static __xdata char     cmd_line[CMD_LEN];
33 static __pdata uint8_t  cmd_len;
34 static __pdata uint8_t  cmd_i;
35
36 void
37 ao_put_string(__code char *s)
38 {
39         char    c;
40         while ((c = *s++))
41                 putchar(c);
42 }
43
44 static void
45 backspace(void)
46 {
47         ao_put_string ("\010 \010");
48 }
49
50 static void
51 readline(void)
52 {
53         char c;
54         if (ao_echo())
55                 ao_put_string("> ");
56         cmd_len = 0;
57         for (;;) {
58                 flush();
59                 c = getchar();
60                 /* backspace/delete */
61                 if (c == '\010' || c == '\177') {
62                         if (cmd_len != 0) {
63                                 if (ao_echo())
64                                         backspace();
65                                 --cmd_len;
66                         }
67                         continue;
68                 }
69
70                 /* ^U */
71                 if (c == '\025') {
72                         while (cmd_len != 0) {
73                                 if (ao_echo())
74                                         backspace();
75                                 --cmd_len;
76                         }
77                         continue;
78                 }
79
80                 /* map CR to NL */
81                 if (c == '\r')
82                         c = '\n';
83
84                 if (c == '\n') {
85                         if (ao_echo())
86                                 putchar('\n');
87                         break;
88                 }
89
90                 if (cmd_len >= CMD_LEN - 2)
91                         continue;
92                 cmd_line[cmd_len++] = c;
93                 if (ao_echo())
94                         putchar(c);
95         }
96         cmd_line[cmd_len++] = '\n';
97         cmd_line[cmd_len++] = '\0';
98         cmd_i = 0;
99 }
100
101 void
102 ao_cmd_lex(void)
103 {
104         ao_cmd_lex_c = '\n';
105         if (cmd_i < cmd_len)
106                 ao_cmd_lex_c = cmd_line[cmd_i++];
107 }
108
109 static void
110 putnibble(uint8_t v)
111 {
112         if (v < 10)
113                 putchar(v + '0');
114         else
115                 putchar(v + ('a' - 10));
116 }
117
118 uint8_t
119 ao_getnibble(void)
120 {
121         char    c;
122
123         c = getchar();
124         if ('0' <= c && c <= '9')
125                 return c - '0';
126         if ('a' <= c && c <= 'f')
127                 return c - ('a' - 10);
128         if ('A' <= c && c <= 'F')
129                 return c - ('A' - 10);
130         ao_cmd_status = ao_cmd_lex_error;
131         return 0;
132 }
133
134 void
135 ao_cmd_put16(uint16_t v)
136 {
137         ao_cmd_put8(v >> 8);
138         ao_cmd_put8(v);
139 }
140
141 void
142 ao_cmd_put8(uint8_t v)
143 {
144         putnibble((v >> 4) & 0xf);
145         putnibble(v & 0xf);
146 }
147
148 uint8_t
149 ao_cmd_is_white(void)
150 {
151         return ao_cmd_lex_c == ' ' || ao_cmd_lex_c == '\t';
152 }
153
154 void
155 ao_cmd_white(void)
156 {
157         while (ao_cmd_is_white())
158                 ao_cmd_lex();
159 }
160
161 int8_t
162 ao_cmd_hexchar(char c)
163 {
164         if ('0' <= c && c <= '9')
165                 return (c - '0');
166         if ('a' <= c && c <= 'f')
167                 return (c - 'a' + 10);
168         if ('A' <= c && c <= 'F')
169                 return (c - 'A' + 10);
170         return -1;
171 }
172
173 void
174 ao_cmd_hexbyte(void)
175 {
176         uint8_t i;
177         int8_t  n;
178
179         ao_cmd_lex_i = 0;
180         ao_cmd_white();
181         for (i = 0; i < 2; i++) {
182                 n = ao_cmd_hexchar(ao_cmd_lex_c);
183                 if (n < 0) {
184                         ao_cmd_status = ao_cmd_syntax_error;
185                         break;
186                 }
187                 ao_cmd_lex_i = (ao_cmd_lex_i << 4) | n;
188                 ao_cmd_lex();
189         }
190 }
191
192 void
193 ao_cmd_hex(void)
194 {
195         __pdata uint8_t r = ao_cmd_lex_error;
196         int8_t  n;
197
198         ao_cmd_lex_i = 0;
199         ao_cmd_white();
200         for(;;) {
201                 n = ao_cmd_hexchar(ao_cmd_lex_c);
202                 if (n < 0)
203                         break;
204                 ao_cmd_lex_i = (ao_cmd_lex_i << 4) | n;
205                 r = ao_cmd_success;
206                 ao_cmd_lex();
207         }
208         if (r != ao_cmd_success)
209                 ao_cmd_status = r;
210 }
211
212 void
213 ao_cmd_decimal(void) __reentrant
214 {
215         uint8_t r = ao_cmd_lex_error;
216
217         ao_cmd_lex_u32 = 0;
218         ao_cmd_white();
219         for(;;) {
220                 if ('0' <= ao_cmd_lex_c && ao_cmd_lex_c <= '9')
221                         ao_cmd_lex_u32 = (ao_cmd_lex_u32 * 10) + (ao_cmd_lex_c - '0');
222                 else
223                         break;
224                 r = ao_cmd_success;
225                 ao_cmd_lex();
226         }
227         if (r != ao_cmd_success)
228                 ao_cmd_status = r;
229         ao_cmd_lex_i = (uint16_t) ao_cmd_lex_u32;
230 }
231
232 uint8_t
233 ao_match_word(__code char *word)
234 {
235         while (*word) {
236                 if (ao_cmd_lex_c != *word) {
237                         ao_cmd_status = ao_cmd_syntax_error;
238                         return 0;
239                 }
240                 word++;
241                 ao_cmd_lex();
242         }
243         return 1;
244 }
245
246 static void
247 echo(void)
248 {
249         ao_cmd_hex();
250         if (ao_cmd_status == ao_cmd_success)
251                 ao_stdios[ao_cur_stdio].echo = ao_cmd_lex_i != 0;
252 }
253
254 static void
255 ao_reboot(void)
256 {
257         ao_cmd_white();
258         if (!ao_match_word("eboot"))
259                 return;
260         /* Delay waiting for the packet master to be turned off
261          * so that we don't end up back in idle mode because we
262          * received a packet after boot.
263          */
264         flush();
265         ao_delay(AO_SEC_TO_TICKS(1));
266         ao_arch_reboot();
267         ao_panic(AO_PANIC_REBOOT);
268 }
269
270 #ifndef HAS_VERSION
271 #define HAS_VERSION 1
272 #endif
273
274 #if HAS_VERSION
275 static void
276 version(void)
277 {
278         printf("manufacturer     %s\n"
279                "product          %s\n"
280                "serial-number    %u\n"
281 #if HAS_FLIGHT || HAS_TRACKER
282                "current-flight   %u\n"
283 #endif
284 #if HAS_LOG
285                "log-format       %u\n"
286 #if !DISABLE_LOG_SPACE
287                "log-space        %lu\n"
288 #endif
289 #endif
290 #if defined(AO_BOOT_APPLICATION_BASE) && defined(AO_BOOT_APPLICATION_BOUND)
291                "program-space    %u\n"
292 #endif
293 #if AO_VALUE_32
294                "altitude-32      1\n"
295 #endif
296                , ao_manufacturer
297                , ao_product
298                , ao_serial_number
299 #if HAS_FLIGHT || HAS_TRACKER
300                , ao_flight_number
301 #endif
302 #if HAS_LOG
303                , ao_log_format
304 #if !DISABLE_LOG_SPACE
305                , (unsigned long) ao_storage_log_max
306 #endif
307 #endif
308 #if defined(AO_BOOT_APPLICATION_BASE) && defined(AO_BOOT_APPLICATION_BOUND)
309                , (uint32_t) AO_BOOT_APPLICATION_BOUND - (uint32_t) AO_BOOT_APPLICATION_BASE
310 #endif
311                 );
312         printf("software-version %s\n", ao_version);
313 }
314 #endif
315
316 #ifndef NUM_CMDS
317 #define NUM_CMDS        11
318 #endif
319
320 static __code struct ao_cmds    *__xdata (ao_cmds[NUM_CMDS]);
321 static __pdata uint8_t          ao_ncmds;
322
323 static void
324 help(void)
325 {
326         __pdata uint8_t cmds;
327         __pdata uint8_t cmd;
328         __code struct ao_cmds * __pdata cs;
329         __code const char *h;
330         uint8_t e;
331
332         for (cmds = 0; cmds < ao_ncmds; cmds++) {
333                 cs = ao_cmds[cmds];
334                 for (cmd = 0; cs[cmd].func; cmd++) {
335                         h = cs[cmd].help;
336                         ao_put_string(h);
337                         e = strlen(h);
338                         h += e + 1;
339                         e = 45 - e;
340                         while (e--)
341                                 putchar(' ');
342                         ao_put_string(h);
343                         putchar('\n');
344                 }
345         }
346 }
347
348 static void
349 report(void)
350 {
351         switch(ao_cmd_status) {
352         case ao_cmd_lex_error:
353         case ao_cmd_syntax_error:
354                 puts("Syntax error");
355                 ao_cmd_status = 0;
356         default:
357                 break;
358         }
359 }
360
361 void
362 ao_cmd_register(__code struct ao_cmds *cmds)
363 {
364         if (ao_ncmds >= NUM_CMDS)
365                 ao_panic(AO_PANIC_CMD);
366         ao_cmds[ao_ncmds++] = cmds;
367 }
368
369 void
370 ao_cmd(void)
371 {
372         __pdata char    c;
373         uint8_t cmd, cmds;
374         __code struct ao_cmds * __xdata cs;
375         void (*__xdata func)(void);
376
377         for (;;) {
378                 readline();
379                 ao_cmd_lex();
380                 ao_cmd_white();
381                 c = ao_cmd_lex_c;
382                 ao_cmd_lex();
383                 if (c == '\r' || c == '\n')
384                         continue;
385                 func = (void (*)(void)) NULL;
386                 for (cmds = 0; cmds < ao_ncmds; cmds++) {
387                         cs = ao_cmds[cmds];
388                         for (cmd = 0; cs[cmd].func; cmd++)
389                                 if (cs[cmd].help[0] == c) {
390                                         func = cs[cmd].func;
391                                         break;
392                                 }
393                         if (func)
394                                 break;
395                 }
396                 if (func)
397                         (*func)();
398                 else
399                         ao_cmd_status = ao_cmd_syntax_error;
400                 report();
401         }
402 }
403
404 #if HAS_BOOT_LOADER
405
406 #include <ao_boot.h>
407
408 static void
409 ao_loader(void)
410 {
411         flush();
412         ao_boot_loader();
413 }
414 #endif
415
416 __xdata struct ao_task ao_cmd_task;
417
418 __code struct ao_cmds   ao_base_cmds[] = {
419         { help,         "?\0Help" },
420 #if HAS_TASK_INFO
421         { ao_task_info, "T\0Tasks" },
422 #endif
423         { echo,         "E <0 off, 1 on>\0Echo" },
424         { ao_reboot,    "r eboot\0Reboot" },
425 #if HAS_VERSION
426         { version,      "v\0Version" },
427 #endif
428 #if HAS_BOOT_LOADER
429         { ao_loader,    "X\0Switch to boot loader" },
430 #endif
431         { 0,    NULL },
432 };
433
434 void
435 ao_cmd_init(void)
436 {
437         ao_cmd_register(&ao_base_cmds[0]);
438         ao_add_task(&ao_cmd_task, ao_cmd, "cmd");
439 }