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