Start to clean up.
[bertos.git] / bertos / net / protocol.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2003, 2004, 2006, 2012 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 2000 Bernie Innocenti <bernie@codewiz.org>
31  *
32  * -->
33  *
34  * \brief Implementation of the command protocol between the board and the host
35  *
36  *
37  * \author Giovanni Bajo <rasky@develer.com>
38  * \author Marco Benelli <marco@develer.com>
39  * \author Bernie Innocenti <bernie@codewiz.org>
40  * \author Daniele Basile <asterix@develer.com>
41  */
42
43 #include "protocol.h"
44 #include "verstag.h"
45
46 #include "cfg/cfg_parser.h"
47 #include <cfg/compiler.h>
48 #include <cfg/debug.h>
49
50 #include <drv/timer.h>
51
52 #include <mware/readline.h>
53 #include <mware/parser.h>
54
55 #include <io/kfile.h>
56
57 #include <stdlib.h>
58 #include <string.h>
59
60 // DEBUG: set to 1 to force interactive mode
61 #define FORCE_INTERACTIVE         1
62
63 /**
64  * True if we are in interactive mode, false if we are in protocol mode.
65  * In interactive mode, commands are read through readline() (prompt,
66  * completion, history) and replies/errors are sent to the output channel.
67  * In protocol mode, we implement the default protocol
68  */
69 static bool interactive;
70
71 /// Readline context, used for interactive mode.
72 static struct RLContext rl_ctx;
73
74 /**
75  * Send a NAK asking the host to send the current message again.
76  *
77  * \a fd kfile handler for serial.
78  * \a err  human-readable description of the error for debug purposes.
79  */
80 INLINE void NAK(KFile *fd, const char *err)
81 {
82 #ifdef _DEBUG
83         kfile_printf(fd, "NAK \"%s\"\r\n", err);
84 #else
85         kfile_printf(fd, "NAK\r\n");
86 #endif
87 }
88
89 static void protocol_prompt(KFile *fd)
90 {
91         kfile_print(fd, ">> ");
92 }
93
94 /*
95  * Print args on s, with format specified in t->result_fmt.
96  * Return number of valid arguments or -1 in case of error.
97  */
98 static bool protocol_reply(KFile *fd, const struct CmdTemplate *t,
99                           const parms *args)
100 {
101         unsigned short offset = strlen(t->arg_fmt) + 1;
102         unsigned short nres = strlen(t->result_fmt);
103
104         for (unsigned short i = 0; i < nres; ++i)
105         {
106                 if (t->result_fmt[i] == 'd')
107                 {
108                         kfile_printf(fd, " %ld", args[offset+i].l);
109                 }
110                 else if (t->result_fmt[i] == 's')
111                 {
112                         kfile_printf(fd, " %s", args[offset+i].s);
113                 }
114
115                 else
116                 {
117                         //abort();
118                         kprintf("errore\n");
119                 }
120         }
121         kfile_printf(fd, "\r\n");
122         return true;
123 }
124
125 static void protocol_parse(KFile *fd, const char *buf)
126 {
127         const struct CmdTemplate *templ;
128
129         /* Command check.  */
130         templ = parser_get_cmd_template(buf);
131         if (!templ)
132         {
133                 kfile_print(fd, "-1 Invalid command.\r\n");
134                 protocol_prompt(fd);
135                 return;
136         }
137
138         parms args[CONFIG_PARSER_MAX_ARGS];
139
140         /* Args Check.  TODO: Handle different case. see doc/PROTOCOL .  */
141         if (!parser_get_cmd_arguments(buf, templ, args))
142         {
143                 kfile_print(fd, "-2 Invalid arguments.\r\n");
144                 protocol_prompt(fd);
145                 return;
146         }
147
148         /* Execute. */
149         if(!parser_execute_cmd(templ, args))
150         {
151                 NAK(fd, "Error in executing command.");
152         }
153         if (!protocol_reply(fd, templ, args))
154         {
155                 NAK(fd, "Invalid return format.");
156         }
157
158         protocol_prompt(fd);
159         return;
160 }
161
162 void protocol_run(KFile *fd)
163 {
164         static char linebuf[80];
165
166         if (!interactive)
167         {
168                 kfile_gets(fd, linebuf, sizeof(linebuf));
169
170                 /* Clear errors on channel */
171                 kfile_clearerr(fd);
172
173                 /* check message minimum length */
174                 if (linebuf[0])
175                 {
176                         /* If we enter lines beginning with sharp(#)
177                         they are stripped out from commands */
178                         if(linebuf[0] != '#')
179                         {
180                                 if (linebuf[0] == 0x1B && linebuf[1] == 0x1B)  // ESC
181                                 {
182                                         interactive = true;
183                                         kfile_printf(fd, "Entering interactive mode\r\n");
184                                 }
185                                 else
186                                 {
187                                         protocol_parse(fd, linebuf);
188                                 }
189                         }
190                 }
191         }
192         else
193         {
194                 const char *buf;
195
196                 /*
197                  * Read a line from channel. We use a temporary buffer
198                  * because otherwise we would have to extract a message
199                  * from the port immediately: there might not be any
200                  * available, and one might get free while we read
201                  * the line. We also add a fake ID at the start to
202                  * fool the parser.
203                  */
204                 buf = rl_readline(&rl_ctx);
205
206                 /* If we enter lines beginning with sharp(#)
207                 they are stripped out from commands */
208                 if(buf && buf[0] != '#')
209                 {
210                         if (buf[0] != '\0')
211                         {
212                                 // exit special case to immediately change serial input
213                                 if (!strcmp(buf, "exit") || !strcmp(buf, "quit"))
214                                 {
215                                         rl_clear_history(&rl_ctx);
216                                         kfile_printf(fd, "Leaving interactive mode...\r\n");
217                                         interactive = FORCE_INTERACTIVE;
218                                 }
219                                 else
220                                 {
221                                         //TODO: remove sequence numbers
222                                         linebuf[0] = '0';
223                                         linebuf[1] = ' ';
224
225                                         strncpy(linebuf + 2, buf, sizeof(linebuf) - 3);
226                                         linebuf[sizeof(linebuf) - 1] = '\0';
227                                         protocol_parse(fd, linebuf);
228                                 }
229                         }
230                 }
231         }
232 }
233
234 /*
235  * Commands.
236  * TODO: Command declarations and definitions should be in another file(s).
237  * Maybe we should use CMD_HUNK_TEMPLATE.
238  *
239  */
240
241 MAKE_CMD(ver, "", "ddd",
242 ({
243         args[1].l = VERS_MAJOR;
244         args[2].l = VERS_MINOR;
245         args[3].l = VERS_REV;
246         0;
247 }), 0);
248
249
250 /* Register commands.  */
251 static void protocol_registerCmds(void)
252 {
253         REGISTER_CMD(ver);
254 }
255
256 /* Initialization: readline context, parser and register commands.  */
257 void protocol_init(KFile *fd)
258 {
259         interactive = FORCE_INTERACTIVE;
260
261         rl_init_ctx(&rl_ctx);
262         //rl_setprompt(&rl_ctx, ">> ");
263         rl_sethook_get(&rl_ctx, (getc_hook)kfile_getc, fd);
264         rl_sethook_put(&rl_ctx, (putc_hook)kfile_putc, fd);
265         rl_sethook_match(&rl_ctx, parser_rl_match, NULL);
266         rl_sethook_clear(&rl_ctx, (clear_hook)kfile_clearerr,fd);
267
268         parser_init();
269         protocol_registerCmds();
270         protocol_prompt(fd);
271 }