altos: Save some memory.
[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; (h = cs[cmd].help); cmd++)
282                         printf("%-45s %s\n", h, h + 1 + strlen(h));
283         }
284 }
285
286 static void
287 report(void)
288 {
289         switch(ao_cmd_status) {
290         case ao_cmd_lex_error:
291         case ao_cmd_syntax_error:
292                 puts("Syntax error");
293                 ao_cmd_status = 0;
294         default:
295                 break;
296         }
297 }
298
299 void
300 ao_cmd_register(__code struct ao_cmds *cmds)
301 {
302         if (ao_ncmds >= NUM_CMDS)
303                 ao_panic(AO_PANIC_CMD);
304         ao_cmds[ao_ncmds++] = cmds;
305 }
306
307 void
308 ao_cmd(void)
309 {
310         __pdata char    c;
311         uint8_t cmd, cmds;
312         __code struct ao_cmds * __xdata cs;
313         void (*__xdata func)(void);
314
315         for (;;) {
316                 readline();
317                 ao_cmd_lex();
318                 ao_cmd_white();
319                 c = ao_cmd_lex_c;
320                 ao_cmd_lex();
321                 if (c == '\r' || c == '\n')
322                         continue;
323                 func = (void (*)(void)) NULL;
324                 for (cmds = 0; cmds < ao_ncmds; cmds++) {
325                         cs = ao_cmds[cmds];
326                         for (cmd = 0; cs[cmd].func; cmd++)
327                                 if (cs[cmd].help[0] == c) {
328                                         func = cs[cmd].func;
329                                         break;
330                                 }
331                         if (func)
332                                 break;
333                 }
334                 if (func)
335                         (*func)();
336                 else
337                         ao_cmd_status = ao_cmd_syntax_error;
338                 report();
339         }
340 }
341
342 __xdata struct ao_task ao_cmd_task;
343
344 __code struct ao_cmds   ao_base_cmds[] = {
345         { help,         "?\0Help" },
346         { ao_task_info, "T\0Tasks" },
347         { echo,         "E <0 off, 1 on>\0Echo" },
348         { ao_reboot,    "r eboot\0Reboot" },
349         { version,      "v\0Version" },
350         { 0,    NULL },
351 };
352
353 void
354 ao_cmd_init(void)
355 {
356         ao_cmd_register(&ao_base_cmds[0]);
357         ao_add_task(&ao_cmd_task, ao_cmd, "cmd");
358 }