Add first simple protocol command (version request).
[bertos.git] / app / triface / protocol.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2006 Develer S.r.l. (http://www.develer.com/)
5  * -->
6  *
7  * \brief Implementation of the command protocol between the board and the host
8  *
9  *
10  * \version $Id$
11  *
12  * \author Giovanni Bajo <rasky@develer.com>
13  * \author Marco Benelli <marco@develer.com>
14  * \author Bernardo Innocenti <bernie@develer.com>
15  */
16
17 /*#*
18  *#* $Log$
19  *#* Revision 1.1  2006/06/01 12:29:21  marco
20  *#* Add first simple protocol command (version request).
21  *#*
22  *#*/
23
24 #include "protocol.h"
25
26 #include <drv/ser.h>
27 #include <mware/readline.h>
28 #include <mware/parser.h>
29 #include <cfg/compiler.h>
30 #include <cfg/debug.h>
31 #include <verstag.h>
32
33 #include <string.h>
34
35
36 // DEBUG: set to 1 to force interactive mode
37 #define FORCE_INTERACTIVE         1
38
39 /**
40  * True if we are in interactive mode, false if we are in protocol mode.
41  * In interactive mode, commands are read through readline() (prompt, completion,
42  * history) without IDs, and replies/errors are sent to the serial output.
43  * In protocol mode, we implement the default protocol
44  */
45 static bool interactive;
46
47 /// Readline context, used for interactive mode.
48 static struct RLContext rl_ctx;
49
50
51 /**
52  * Send a NAK asking the host to send the current message again.
53  *
54  * \param err  human-readable description of the error for debug purposes.
55  */
56 INLINE void NAK(Serial *ser, const char *err)
57 {
58 #ifdef _DEBUG
59         ser_printf(ser, "NAK \"%s\"\r\n", err);
60 #else
61         ser_printf(ser, "NAK\r\n");
62 #endif
63 }
64
65 static void protocol_parse(Serial *ser, const char *buf)
66 {
67         const struct CmdTemplate *templ;
68         parms args[8]; // FIXME FIXME!!
69
70         templ = parser_get_cmd_template(buf);
71
72         if (!templ)
73         {
74                 NAK(ser, "invalid command");
75                 return;
76         }
77
78         // Extract the arguments for the command
79         if (!parser_get_cmd_arguments(buf, templ, args))
80         {
81                 NAK(ser, "invalid arguments");
82                 return;
83         }
84
85         // EXEC!
86 }
87
88 void protocol_run(Serial *ser)
89 {
90         // \todo to be removed, we could probably access the serial FIFO directly
91         static char linebuf[80];
92
93         if (!interactive)
94         {
95                 ser_gets(ser, linebuf, sizeof(linebuf));
96
97                 // reset serial port error anyway
98                 ser_setstatus(ser, 0);
99
100                 // check message minimum length
101                 if (linebuf[0])
102                 {
103                         if (linebuf[0] == 0x1B && linebuf[1] == 0x1B)  // ESC
104                         {
105                                 interactive = true;
106                                 ser_printf(ser, "Entering interactive mode\r\n");
107                         }
108                         else
109                                 protocol_parse(ser, linebuf);
110                 }
111         }
112         else
113         {
114                 const char *buf;
115
116                 /*
117                  * Read a line from serial. We use a temporary buffer
118                  * because otherwise we would have to extract a message
119                  * from the port immediately: there might not be any
120                  * available, and one might get free while we read
121                  * the line. We also add a fake ID at the start to
122                  * fool the parser.
123                  */
124                 buf = rl_readline(&rl_ctx);
125
126                 if (buf && buf[0] != '\0')
127                 {
128                         // exit special case to immediately change serial input
129                         if (!strcmp(buf, "exit") || !strcmp(buf, "quit"))
130                         {
131                                 rl_clear_history(&rl_ctx);
132                                 ser_printf(ser, "Leaving interactive mode...\r\n");
133                                 interactive = FORCE_INTERACTIVE;
134                         }
135                         else
136                         {
137                                 //TODO: remove sequence numbers
138                                 linebuf[0] = '0';
139                                 linebuf[1] = ' ';
140
141                                 strncpy(linebuf + 2, buf, sizeof(linebuf) - 3);
142                                 linebuf[sizeof(linebuf) - 1] = '\0';
143                                 protocol_parse(ser, linebuf);
144                         }
145                 }
146         }
147 }
148
149 static ResultCode cmd_ver(const char **str)
150 {
151         *str = VERS_TAG;
152 kprintf("hello, version world!\nver=" VERS_TAG "\n");
153         return RC_REPLY;
154 }
155 static ResultCode cmd_add_hunk(parms args_results[])
156 {
157         return cmd_ver(&args_results[0].s);
158 }
159 const struct CmdTemplate cmd_ver_template =
160 {
161         "ver", "", "d", cmd_add_hunk
162 };
163
164 static void protocol_registerCmds(void)
165 {
166 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(quit));
167 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(exit));
168 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(wait));
169 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(delay));
170 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(SYN));
171 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(RST));
172 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(skip_end));
173 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(restart));
174 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(ver));
175 //      parser_register_cmd(&CMD_HUNK_TEMPLATE(power_off));
176
177         parser_register_cmd(&cmd_ver_template);
178 }
179 void protocol_init(Serial *ser)
180 {
181         interactive = FORCE_INTERACTIVE;
182
183         // Initialize the readline context used for interactive mode
184         rl_init_ctx(&rl_ctx);
185         rl_setprompt(&rl_ctx, ">> ");
186         rl_sethook_get(&rl_ctx, (getc_hook)ser_getchar, ser);
187         rl_sethook_put(&rl_ctx, (putc_hook)ser_putchar, ser);
188         rl_sethook_match(&rl_ctx, parser_rl_match, NULL);
189
190         parser_init();
191
192         protocol_registerCmds();
193 }