Refactor to use new protocol module and sipo.
[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 Protocol module.
35  *
36  * This module supply a simple ascii protocol to send commands to
37  * the device like pc "terminal". To use it we need to define all command
38  * that we want supply, and then we should register they using a user defined
39  * function. All commands can take arguments or/and return a value.
40  *
41  * \code
42  * #include "verstag.h"
43  * #include <mware/parser.h>
44  *
45  * //Define a function ver, that return 3 int.
46  * //This macro will expand into a fuction named "ver" that not take
47  * //an input and return 3 int (ddd).
48  * MAKE_CMD(ver, "", "ddd",
49  * ({
50  *      args[1].l = VERS_MAJOR;
51  *      args[2].l = VERS_MINOR;
52  *      args[3].l = VERS_REV;
53  *      0;
54  * }), 0);
55  *
56  *
57  * //Define the function to pass at protocol_init, to register
58  * //all defined protocol function.
59  * static void protocol_registerCmds(void)
60  * {
61  *        REGISTER_CMD(ver);
62  * }
63  *
64  *
65  * //Init the protocol module whit comunication channel and
66  * //the callback to register the defined protocol functions.
67  * protocol_init(&socket.fd, protocol_registerCmds);
68  * \endcode
69  *
70  * \author Giovanni Bajo <rasky@develer.com>
71  * \author Marco Benelli <marco@develer.com>
72  * \author Bernie Innocenti <bernie@codewiz.org>
73  * \author Daniele Basile <asterix@develer.com>
74  */
75
76 #include "protocol.h"
77
78 #include "cfg/cfg_parser.h"
79
80 #include <cfg/compiler.h>
81 #include <cfg/debug.h>
82
83 #include <drv/timer.h>
84
85 #include <mware/readline.h>
86 #include <mware/parser.h>
87
88 #include <io/kfile.h>
89
90 #include <stdlib.h>
91 #include <string.h>
92
93 // DEBUG: set to 1 to force interactive mode
94 #define FORCE_INTERACTIVE         1
95
96 /**
97  * True if we are in interactive mode, false if we are in protocol mode.
98  * In interactive mode, commands are read through readline() (prompt,
99  * completion, history) and replies/errors are sent to the output channel.
100  * In protocol mode, we implement the default protocol
101  */
102 static bool interactive;
103
104 /// Readline context, used for interactive mode.
105 static struct RLContext rl_ctx;
106
107 /**
108  * Send a NAK asking the host to send the current message again.
109  *
110  * \a fd kfile handler for serial.
111  * \a err  human-readable description of the error for debug purposes.
112  */
113 INLINE void NAK(KFile *fd, const char *err)
114 {
115 #ifdef _DEBUG
116         kfile_printf(fd, "NAK \"%s\"\r\n", err);
117 #else
118         kfile_printf(fd, "NAK\r\n");
119 #endif
120 }
121
122 static void protocol_prompt(KFile *fd)
123 {
124         kfile_print(fd, ">> ");
125 }
126
127 /*
128  * Print args on s, with format specified in t->result_fmt.
129  * Return number of valid arguments or -1 in case of error.
130  */
131 static bool protocol_reply(KFile *fd, const struct CmdTemplate *t, const parms *args)
132 {
133         unsigned short offset = strlen(t->arg_fmt) + 1;
134         unsigned short nres = strlen(t->result_fmt);
135
136         for (unsigned short i = 0; i < nres; ++i)
137         {
138                 if (t->result_fmt[i] == 'd')
139                 {
140                         kfile_printf(fd, " %ld", args[offset+i].l);
141                 }
142                 else if (t->result_fmt[i] == 's')
143                 {
144                         kfile_printf(fd, " %s", args[offset+i].s);
145                 }
146
147                 else
148                 {
149                         //abort();
150                         kprintf("errore\n");
151                 }
152         }
153         kfile_printf(fd, "\r\n");
154         return true;
155 }
156
157 static void protocol_parse(KFile *fd, const char *buf)
158 {
159         const struct CmdTemplate *templ;
160
161         /* Command check.  */
162         templ = parser_get_cmd_template(buf);
163         if (!templ)
164         {
165                 kfile_print(fd, "-1 Invalid command.\r\n");
166                 protocol_prompt(fd);
167                 return;
168         }
169
170         parms args[CONFIG_PARSER_MAX_ARGS];
171
172         /* Args Check.  TODO: Handle different case. see doc/PROTOCOL .  */
173         if (!parser_get_cmd_arguments(buf, templ, args))
174         {
175                 kfile_print(fd, "-2 Invalid arguments.\r\n");
176                 protocol_prompt(fd);
177                 return;
178         }
179
180         /* Execute. */
181         if(!parser_execute_cmd(templ, args))
182         {
183                 NAK(fd, "Error in executing command.");
184         }
185         if (!protocol_reply(fd, templ, args))
186         {
187                 NAK(fd, "Invalid return format.");
188         }
189
190         protocol_prompt(fd);
191         return;
192 }
193
194 void protocol_run(KFile *fd)
195 {
196         static char linebuf[80];
197
198         if (!interactive)
199         {
200                 kfile_gets(fd, linebuf, sizeof(linebuf));
201
202                 /* Clear errors on channel */
203                 kfile_clearerr(fd);
204
205                 /* check message minimum length */
206                 if (linebuf[0])
207                 {
208                         /* If we enter lines beginning with sharp(#)
209                         they are stripped out from commands */
210                         if(linebuf[0] != '#')
211                         {
212                                 if (linebuf[0] == 0x1B && linebuf[1] == 0x1B)  // ESC
213                                 {
214                                         interactive = true;
215                                         kfile_printf(fd, "Entering interactive mode\r\n");
216                                 }
217                                 else
218                                 {
219                                         protocol_parse(fd, linebuf);
220                                 }
221                         }
222                 }
223         }
224         else
225         {
226                 const char *buf;
227
228                 /*
229                  * Read a line from channel. We use a temporary buffer
230                  * because otherwise we would have to extract a message
231                  * from the port immediately: there might not be any
232                  * available, and one might get free while we read
233                  * the line. We also add a fake ID at the start to
234                  * fool the parser.
235                  */
236                 buf = rl_readline(&rl_ctx);
237
238                 /* If we enter lines beginning with sharp(#)
239                 they are stripped out from commands */
240                 if(buf && buf[0] != '#')
241                 {
242                         if (buf[0] != '\0')
243                         {
244                                 // exit special case to immediately change serial input
245                                 if (!strcmp(buf, "exit") || !strcmp(buf, "quit"))
246                                 {
247                                         rl_clear_history(&rl_ctx);
248                                         kfile_printf(fd, "Leaving interactive mode...\r\n");
249                                         interactive = FORCE_INTERACTIVE;
250                                 }
251                                 else
252                                 {
253                                         //TODO: remove sequence numbers
254                                         linebuf[0] = '0';
255                                         linebuf[1] = ' ';
256
257                                         strncpy(linebuf + 2, buf, sizeof(linebuf) - 3);
258                                         linebuf[sizeof(linebuf) - 1] = '\0';
259                                         protocol_parse(fd, linebuf);
260                                 }
261                         }
262                 }
263         }
264 }
265
266
267 /* Initialization: readline context, parser and register commands.  */
268 void protocol_init(KFile *fd, protocol_t cmds_register)
269 {
270         interactive = FORCE_INTERACTIVE;
271
272         rl_init_ctx(&rl_ctx);
273         //rl_setprompt(&rl_ctx, ">> ");
274         rl_sethook_get(&rl_ctx, (getc_hook)kfile_getc, fd);
275         rl_sethook_put(&rl_ctx, (putc_hook)kfile_putc, fd);
276         rl_sethook_match(&rl_ctx, parser_rl_match, NULL);
277         rl_sethook_clear(&rl_ctx, (clear_hook)kfile_clearerr,fd);
278
279         parser_init();
280         ASSERT(cmds_register);
281         cmds_register();
282
283         protocol_prompt(fd);
284 }