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