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