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