protocol.c

Go to the documentation of this file.
00001 
00045 #include "protocol.h"
00046 #include "cmd_ctor.h"  // MAKE_CMD, REGISTER_CMD
00047 #include "verstag.h"
00048 #include "hw/hw_adc.h"
00049 #include "hw/hw_input.h"
00050 
00051 #include <drv/timer.h>
00052 #include <drv/ser.h>
00053 #include <drv/sipo.h>
00054 #include <drv/wdt.h>
00055 #include <drv/buzzer.h>
00056 
00057 #include <mware/readline.h>
00058 #include <mware/parser.h>
00059 
00060 #include <cfg/compiler.h>
00061 #include <cfg/debug.h>
00062 
00063 #include <kern/kfile.h>
00064 
00065 #include <stdlib.h>
00066 #include <string.h>
00067 
00068 // Define the format string for ADC
00069 #define ADC_FORMAT_STR "dddd"
00070 
00071 // DEBUG: set to 1 to force interactive mode
00072 #define FORCE_INTERACTIVE         1
00073 
00081 static bool interactive;
00082 
00084 static struct RLContext rl_ctx;
00085 
00086 uint8_t reg_status_dout;
00093 INLINE void NAK(KFile *fd, const char *err)
00094 {
00095 #ifdef _DEBUG
00096     kfile_printf(fd, "NAK \"%s\"\r\n", err);
00097 #else
00098     kfile_printf(fd, "NAK\r\n");
00099 #endif
00100 }
00101 
00102 static void protocol_prompt(KFile *fd)
00103 {
00104     kfile_print(fd, ">> ");
00105 }
00106 
00107 /*
00108  * Print args on s, with format specified in t->result_fmt.
00109  * Return number of valid arguments or -1 in case of error.
00110  */
00111 static bool protocol_reply(KFile *fd, const struct CmdTemplate *t,
00112               const parms *args)
00113 {
00114     unsigned short offset = strlen(t->arg_fmt) + 1;
00115     unsigned short nres = strlen(t->result_fmt);
00116 
00117     for (unsigned short i = 0; i < nres; ++i)
00118     {
00119         if (t->result_fmt[i] == 'd')
00120         {
00121             kfile_printf(fd, " %ld", args[offset+i].l);
00122         }
00123         else if (t->result_fmt[i] == 's')
00124         {
00125             kfile_printf(fd, " %s", args[offset+i].s);
00126         }
00127 
00128         else
00129         {
00130             abort();
00131         }
00132     }
00133     kfile_printf(fd, "\r\n");
00134     return true;
00135 }
00136 
00137 static void protocol_parse(KFile *fd, const char *buf)
00138 {
00139     const struct CmdTemplate *templ;
00140 
00141     /* Command check.  */
00142     templ = parser_get_cmd_template(buf);
00143     if (!templ)
00144     {
00145         kfile_print(fd, "-1 Invalid command.\r\n");
00146         protocol_prompt(fd);
00147         return;
00148     }
00149 
00150     parms args[PARSER_MAX_ARGS];
00151 
00152     /* Args Check.  TODO: Handle different case. see doc/PROTOCOL .  */
00153     if (!parser_get_cmd_arguments(buf, templ, args))
00154     {
00155         kfile_print(fd, "-2 Invalid arguments.\r\n");
00156         protocol_prompt(fd);
00157         return;
00158     }
00159 
00160     /* Execute. */
00161     if(!parser_execute_cmd(templ, args))
00162     {
00163         NAK(fd, "Error in executing command.");
00164     }
00165     if (!protocol_reply(fd, templ, args))
00166     {
00167         NAK(fd, "Invalid return format.");
00168     }
00169 
00170     protocol_prompt(fd);
00171     return;
00172 }
00173 
00174 void protocol_run(KFile *fd)
00175 {
00180     static char linebuf[80];
00181 
00182     if (!interactive)
00183     {
00184         kfile_gets(fd, linebuf, sizeof(linebuf));
00185 
00186         // reset serial port error anyway
00187         kfile_clearerr(fd);
00188 
00189         // check message minimum length
00190         if (linebuf[0])
00191         {
00192             /* If we enter lines beginning with sharp(#)
00193             they are stripped out from commands */
00194             if(linebuf[0] != '#')
00195             {
00196                 if (linebuf[0] == 0x1B && linebuf[1] == 0x1B)  // ESC
00197                 {
00198                     interactive = true;
00199                     kfile_printf(fd, "Entering interactive mode\r\n");
00200                 }
00201                 else
00202                 {
00203                     protocol_parse(fd, linebuf);
00204                 }
00205             }
00206         }
00207     }
00208     else
00209     {
00210         const char *buf;
00211 
00212         /*
00213          * Read a line from serial. We use a temporary buffer
00214          * because otherwise we would have to extract a message
00215          * from the port immediately: there might not be any
00216          * available, and one might get free while we read
00217          * the line. We also add a fake ID at the start to
00218          * fool the parser.
00219          */
00220         buf = rl_readline(&rl_ctx);
00221 
00222         /* If we enter lines beginning with sharp(#)
00223         they are stripped out from commands */
00224         if(buf && buf[0] != '#')
00225         {
00226             if (buf[0] != '\0')
00227             {
00228                 // exit special case to immediately change serial input
00229                 if (!strcmp(buf, "exit") || !strcmp(buf, "quit"))
00230                 {
00231                     rl_clear_history(&rl_ctx);
00232                     kfile_printf(fd, "Leaving interactive mode...\r\n");
00233                     interactive = FORCE_INTERACTIVE;
00234                 }
00235                 else
00236                 {
00237                     //TODO: remove sequence numbers
00238                     linebuf[0] = '0';
00239                     linebuf[1] = ' ';
00240 
00241                     strncpy(linebuf + 2, buf, sizeof(linebuf) - 3);
00242                     linebuf[sizeof(linebuf) - 1] = '\0';
00243                     protocol_parse(fd, linebuf);
00244                 }
00245             }
00246         }
00247     }
00248 }
00249 
00250 /*
00251  * Commands.
00252  * TODO: Command declarations and definitions should be in another file(s).
00253  * Maybe we should use CMD_HUNK_TEMPLATE.
00254  *
00255  */
00256 
00257 MAKE_CMD(ver, "", "ddd",
00258 ({
00259     args[1].l = VERS_MAJOR;
00260     args[2].l = VERS_MINOR;
00261     args[3].l = VERS_REV;
00262     0;
00263 }), 0);
00264 
00265 /* Sleep. Example of declaring function body directly in macro call.  */
00266 MAKE_CMD(sleep, "d", "",
00267 ({
00268     timer_delay((mtime_t)args[1].l);
00269     0;
00270 }), 0)
00271 
00272 /* Ping.  */
00273 MAKE_CMD(ping, "", "",
00274 ({
00275     //Silence "args not used" warning.
00276     (void)args;
00277     0;
00278 }), 0)
00279 
00280 /* Dout  */
00281 MAKE_CMD(dout, "d", "",
00282 ({
00283     sipo_putchar((uint8_t)args[1].l);
00284 
00285     //Store status of dout ports.
00286     reg_status_dout = (uint8_t)args[1].l;
00287     0;
00288 }), 0)
00289 
00290 /* rdout  read the status of out ports.*/
00291 MAKE_CMD(rdout, "", "d",
00292 ({
00293     args[1].l = reg_status_dout;
00294     0;
00295 }), 0)
00296 
00297 
00298 /* Reset */
00299 MAKE_CMD(reset, "", "",
00300 ({
00301     //Silence "args not used" warning.
00302     (void)args;
00303     wdt_init(7);
00304     wdt_start();
00305 
00306     /*We want to have an infinite loop that lock access on watchdog timer.
00307     This piece of code it's equivalent to a while(true), but we have done this because
00308     gcc generate a warning message that suggest to use "noreturn" parameter in function reset.*/
00309     ASSERT(args);
00310     while(args);
00311     0;
00312 
00313 }), 0)
00314 
00315 /* Din */
00316 MAKE_CMD(din, "", "d",
00317 ({
00318     args[1].l = INPUT_GET();
00319     0;
00320 }), 0)
00321 
00322 
00323 
00324 /* Ain */
00325 MAKE_CMD(ain, "", ADC_FORMAT_STR,
00326 ({
00327     STATIC_ASSERT((sizeof(ADC_FORMAT_STR) - 1) == ADC_CHANNEL_NUM);
00328     for(int i = 0; i < ADC_CHANNEL_NUM; i++)
00329         args[i+1].l = adc_read_ai_channel(i);
00330 
00331     0;
00332 }), 0)
00333 
00334 /* Beep  */
00335 MAKE_CMD(beep, "d", "",
00336 ({
00337     buz_beep(args[1].l);
00338     0;
00339 }), 0)
00340 
00341 /* Register commands.  */
00342 static void protocol_registerCmds(void)
00343 {
00344     REGISTER_CMD(ver);
00345     REGISTER_CMD(sleep);
00346     REGISTER_CMD(ping);
00347     REGISTER_CMD(dout);
00348     //Set off all dout ports.
00349     reg_status_dout = 0;
00350     REGISTER_CMD(rdout);
00351     REGISTER_CMD(reset);
00352     REGISTER_CMD(din);
00353     REGISTER_CMD(ain);
00354     REGISTER_CMD(beep);
00355 }
00356 
00357 /* Initialization: readline context, parser and register commands.  */
00358 void protocol_init(KFile *fd)
00359 {
00360     interactive = FORCE_INTERACTIVE;
00361 
00362     rl_init_ctx(&rl_ctx);
00363     //rl_setprompt(&rl_ctx, ">> ");
00364     rl_sethook_get(&rl_ctx, (getc_hook)kfile_getc, fd);
00365     rl_sethook_put(&rl_ctx, (putc_hook)kfile_putc, fd);
00366     rl_sethook_match(&rl_ctx, parser_rl_match, NULL);
00367     rl_sethook_clear(&rl_ctx, (clear_hook)kfile_clearerr,fd);
00368 
00369     parser_init();
00370 
00371     protocol_registerCmds();
00372 
00373     protocol_prompt(fd);
00374 }