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