- jtag_khz/speed are now single parameter only. These are used
[fw/openocd] / src / helper / command.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   part of this file is taken from libcli (libcli.sourceforge.net)       *
6  *   Copyright (C) David Parrish (david@dparrish.com)                      *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the                         *
20  *   Free Software Foundation, Inc.,                                       *
21  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
22  ***************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "replacements.h"
28 #include "target.h"
29 #include "command.h"
30 #include "configuration.h"
31
32 #include "log.h"
33 #include "time_support.h"
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <unistd.h>
41 #include <errno.h>
42
43 int fast_and_dangerous = 0;
44 Jim_Interp *interp = NULL;
45
46 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
47 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
48
49 int run_command(command_context_t *context, command_t *c, char *words[], int num_words);
50
51 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
52 {               
53         Jim_Obj *tclOutput=(Jim_Obj *)privData;
54
55         Jim_AppendString(interp, tclOutput, string, strlen(string));
56 }
57
58 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
59 {
60         /* the private data is stashed in the interp structure */
61         command_t *c;
62         command_context_t *context;
63         int *retval;
64         int i;
65         int nwords;
66         char **words;
67
68         target_call_timer_callbacks_now();
69         LOG_USER_N("%s", ""); /* Keep GDB connection alive*/ 
70         
71         c = interp->cmdPrivData;
72         LOG_DEBUG("script_command - %s", c->name);
73
74         nwords = argc;
75         words = malloc(sizeof(char *) * nwords);
76         for (i = 0; i < nwords; i++)
77         {
78                 int len;
79
80                 words[i] = strdup(Jim_GetString(argv[i], &len));
81                 if (words[i] == NULL) 
82                 {
83                         return JIM_ERR;
84                 }
85                 LOG_DEBUG("script_command - %s, argv[%u]=%s", c->name, i, words[i]);
86         }
87
88         /* grab the command context from the associated data */
89         context = Jim_GetAssocData(interp, "context");
90         retval = Jim_GetAssocData(interp, "retval"); 
91         if (context != NULL && retval != NULL)
92         {
93                 /* capture log output and return it */
94                 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
95                 log_add_callback(tcl_output, tclOutput);
96                 
97                 *retval = run_command(context, c, words, nwords);
98                 
99                 log_remove_callback(tcl_output, tclOutput);
100                 
101                 /* We dump output into this local variable */
102                 Jim_SetVariableStr(interp, "ocd_output", tclOutput);
103         }
104
105         for (i = 0; i < nwords; i++)
106                 free(words[i]);
107         free(words);
108
109         return (*retval==ERROR_OK)?JIM_OK:JIM_ERR;
110 }
111
112 command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)
113 {
114         command_t *c, *p;
115         
116         if (!context || !name)
117                 return NULL;
118                                 
119         c = malloc(sizeof(command_t));
120         
121         c->name = strdup(name);
122         c->parent = parent;
123         c->children = NULL;
124         c->handler = handler;
125         c->mode = mode;
126         if (!help)
127                 help="";
128         c->next = NULL;
129         
130         /* place command in tree */
131         if (parent)
132         {
133                 if (parent->children)
134                 {
135                         /* find last child */
136                         for (p = parent->children; p && p->next; p = p->next);
137                         if (p)
138                                 p->next = c;
139                 }
140                 else
141                 {
142                         parent->children = c;
143                 }
144         }
145         else
146         {
147                 if (context->commands)
148                 {
149                         /* find last command */
150                         for (p = context->commands; p && p->next; p = p->next);
151                         if (p)
152                                 p->next = c;
153                 }
154                 else
155                 {
156                         context->commands = c;
157                 }
158         }
159         
160         /* just a placeholder, no handler */
161         if (c->handler==NULL)
162                 return c;
163
164         /* If this is a two level command, e.g. "flash banks", then the
165          * "unknown" proc in startup.tcl must redirect to  this command.
166          * 
167          * "flash banks" is translated by "unknown" to "flash_banks"
168          * if such a proc exists
169          */
170         /* Print help for command */
171         const char *t1="";
172         const char *t2="";
173         const char *t3="";
174         /* maximum of two levels :-) */
175         if (c->parent!=NULL)
176         {
177                 t1=c->parent->name;
178                 t2="_";
179         }
180         t3=c->name;
181         const char *full_name=alloc_printf("ocd_%s%s%s", t1, t2, t3);
182         Jim_CreateCommand(interp, full_name, script_command, c, NULL);
183         free((void *)full_name);
184         
185         /* we now need to add an overrideable proc */
186         const char *override_name=alloc_printf("proc %s%s%s {args} {return [eval \"ocd_%s%s%s $args\"]}", t1, t2, t3, t1, t2, t3);
187         Jim_Eval(interp, override_name);        
188         free((void *)override_name);
189         
190         /* accumulate help text in Tcl helptext list.  */
191     Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
192     if (Jim_IsShared(helptext))
193         helptext = Jim_DuplicateObj(interp, helptext);
194         Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
195         
196         Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
197
198         /* maximum of two levels :-) */
199         if (c->parent!=NULL)
200         {
201                 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
202         } 
203         Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
204         
205         Jim_ListAppendElement(interp, cmd_entry, cmd_list);
206         Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
207         Jim_ListAppendElement(interp, helptext, cmd_entry);
208         return c;
209 }
210
211 int unregister_all_commands(command_context_t *context)
212 {
213         command_t *c, *c2;
214         
215         if (context == NULL)
216                 return ERROR_OK;
217         
218         while(NULL != context->commands)
219         {
220                 c = context->commands;
221                 
222                 while(NULL != c->children)
223                 {
224                         c2 = c->children;
225                         c->children = c->children->next;
226                         free(c2->name);
227                         c2->name = NULL;
228                         free(c2);
229                         c2 = NULL;
230                 }
231                 
232                 context->commands = context->commands->next;
233                 
234                 free(c->name);
235                 c->name = NULL;
236                 free(c);
237                 c = NULL;               
238         }
239         
240         return ERROR_OK;
241 }
242
243 int unregister_command(command_context_t *context, char *name)
244 {
245         command_t *c, *p = NULL, *c2;
246         
247         if ((!context) || (!name))
248                 return ERROR_INVALID_ARGUMENTS;
249         
250         /* find command */
251         for (c = context->commands; c; c = c->next)
252         {
253                 if (strcmp(name, c->name) == 0)
254                 {
255                         /* unlink command */
256                         if (p)
257                         {
258                                 p->next = c->next;
259                         }
260                         else
261                         {
262                                 context->commands = c->next;
263                         }
264                         
265                         /* unregister children */
266                         if (c->children)
267                         {
268                                 for (c2 = c->children; c2; c2 = c2->next)
269                                 {
270                                         free(c2->name);
271                                         free(c2);
272                                 }
273                         }
274                         
275                         /* delete command */
276                         free(c->name);
277                         free(c);
278                 }
279                 
280                 /* remember the last command for unlinking */
281                 p = c;
282         }
283         
284         return ERROR_OK;
285 }
286
287 void command_output_text(command_context_t *context, const char *data)
288 {
289         if( context && context->output_handler && data  ){
290                 context->output_handler( context, data );
291         }
292 }
293
294 void command_print_n(command_context_t *context, char *format, ...)
295 {
296         char *string;
297         
298         va_list ap;
299         va_start(ap, format);
300
301         string = alloc_vprintf(format, ap);
302         if (string != NULL)
303         {
304                 /* we want this collected in the log + we also want to pick it up as a tcl return
305                  * value.
306                  * 
307                  * The latter bit isn't precisely neat, but will do for now.
308                  */
309                 LOG_USER_N("%s", string);
310                 // We already printed it above
311                 //command_output_text(context, string);
312                 free(string);
313         }
314
315         va_end(ap);
316 }
317
318 void command_print(command_context_t *context, char *format, ...)
319 {
320         char *string;
321
322         va_list ap;
323         va_start(ap, format);
324
325         string = alloc_vprintf(format, ap);
326         if (string != NULL)
327         {
328                 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
329                 /* we want this collected in the log + we also want to pick it up as a tcl return
330                  * value.
331                  * 
332                  * The latter bit isn't precisely neat, but will do for now.
333                  */
334                 LOG_USER_N("%s", string);
335                 // We already printed it above
336                 //command_output_text(context, string);
337                 free(string);
338         }
339
340         va_end(ap);
341 }
342
343 int run_command(command_context_t *context, command_t *c, char *words[], int num_words)
344 {
345         int start_word=0;
346         if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) ))
347         {
348                 /* Config commands can not run after the config stage */
349                 LOG_ERROR("Illegal mode for command");
350                 return ERROR_FAIL;
351         }
352         
353         int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
354         if (retval == ERROR_COMMAND_SYNTAX_ERROR)
355         {
356                 /* Print help for command */
357                 const char *t1="";
358                 const char *t2="";
359                 const char *t3="";
360                 /* maximum of two levels :-) */
361                 if (c->parent!=NULL)
362                 {
363                         t1=c->parent->name;
364                         t2=" ";
365                 }
366                 t3=c->name;
367                 command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
368         }
369         else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
370         {
371                 /* just fall through for a shutdown request */
372         }
373         else if (retval != ERROR_OK)
374         {
375                 /* we do not print out an error message because the command *should*
376                  * have printed out an error
377                  */
378                 LOG_DEBUG("Command failed with error code %d", retval); 
379         }
380         
381         return retval; 
382 }
383
384 int command_run_line(command_context_t *context, char *line)
385 {
386         /* all the parent commands have been registered with the interpreter
387          * so, can just evaluate the line as a script and check for
388          * results
389          */
390         /* run the line thru a script engine */
391         int retval;
392         int retcode;
393         Jim_DeleteAssocData(interp, "context"); /* remove existing */
394         retcode = Jim_SetAssocData(interp, "context", NULL, context);
395         if (retcode != JIM_OK)
396                 return ERROR_FAIL;
397
398         /* associated the return value */
399         retval = ERROR_OK;
400         Jim_DeleteAssocData(interp, "retval"); /* remove existing */
401         retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
402         if (retcode != JIM_OK)
403                 return ERROR_FAIL;
404
405         retcode = Jim_Eval(interp, line);       
406         if (retcode == JIM_ERR) {
407                 if (retval!=ERROR_COMMAND_CLOSE_CONNECTION)
408                 {
409                         /* We do not print the connection closed error message */
410                         Jim_PrintErrorMessage(interp);
411                 }
412                 if (retval==ERROR_OK)
413                 {
414                         /* It wasn't a low level OpenOCD command that failed */
415                         return ERROR_FAIL; 
416                 }
417                 return retval;
418         } else if (retcode == JIM_EXIT) {
419                 /* ignore. */
420                 /* exit(Jim_GetExitCode(interp)); */
421         } else {
422                 const char *result;
423                 int reslen;
424
425                 result = Jim_GetString(Jim_GetResult(interp), &reslen);
426                 if (reslen) {
427                         int i;
428                         char buff[256+1];
429                         for (i = 0; i < reslen; i += 256)
430                         {
431                                 int chunk;
432                                 chunk = reslen - i;
433                                 if (chunk > 256)
434                                         chunk = 256;
435                                 strncpy(buff, result+i, chunk);
436                                 buff[chunk] = 0; 
437                                 LOG_USER_N("%s", buff);
438                         }
439                         LOG_USER_N("%s", "\n");
440                 }
441         }
442         return retval;
443 }
444
445 int command_run_linef(command_context_t *context, char *format, ...)
446 {
447         int retval=ERROR_FAIL;
448         char *string;
449         va_list ap;
450         va_start(ap, format);
451         string = alloc_vprintf(format, ap);
452         if (string!=NULL)
453         {
454                 retval=command_run_line(context, string);
455         }
456         va_end(ap);
457         return retval;
458 }
459
460 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
461 {
462         context->output_handler = output_handler;
463         context->output_handler_priv = priv;
464 }
465
466 command_context_t* copy_command_context(command_context_t* context)
467 {
468         command_context_t* copy_context = malloc(sizeof(command_context_t));
469
470         *copy_context = *context;
471
472         return copy_context;
473 }
474
475 int command_done(command_context_t *context)
476 {
477         free(context);
478         context = NULL;
479         
480         return ERROR_OK;
481 }
482
483 /* find full path to file */
484 static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
485 {
486         if (argc != 2)
487                 return JIM_ERR;
488         const char *file = Jim_GetString(argv[1], NULL);
489         char *full_path = find_file(file);
490         if (full_path == NULL)
491                 return JIM_ERR;
492         Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
493         free(full_path);
494         
495         Jim_SetResult(interp, result);
496         return JIM_OK;
497 }
498
499 static int jim_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
500 {
501         if (argc != 2)
502                 return JIM_ERR;
503         const char *str = Jim_GetString(argv[1], NULL);
504         LOG_USER("%s", str);
505         return JIM_OK;
506 }
507
508 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
509 {
510         size_t nbytes;
511         const char *ptr;
512         Jim_Interp *interp;
513         command_context_t *context;
514
515         /* make it a char easier to read code */
516         ptr = _ptr;
517         interp = cookie;
518         nbytes = size * n;
519         if (ptr == NULL || interp == NULL || nbytes == 0) {
520                 return 0;
521         }
522
523         context = Jim_GetAssocData(interp, "context");
524         if (context == NULL)
525         {
526                 LOG_ERROR("openocd_jim_fwrite: no command context");
527                 /* TODO: Where should this go? */               
528                 return n;
529         }
530
531         /* do we have to chunk it? */
532         if (ptr[nbytes] == 0)
533         {
534                 /* no it is a C style string */
535                 command_output_text(context, ptr);
536                 return strlen(ptr);
537         }
538         /* GRR we must chunk - not null terminated */
539         while (nbytes) {
540                 char chunk[128+1];
541                 int x;
542
543                 x = nbytes;
544                 if (x > 128) {
545                         x = 128;
546                 }
547                 /* copy it */
548                 memcpy(chunk, ptr, x);
549                 /* terminate it */
550                 chunk[n] = 0;
551                 /* output it */
552                 command_output_text(context, chunk);
553                 ptr += x;
554                 nbytes -= x;
555         }
556         
557         return n;
558 }
559
560 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie)
561 {
562         /* TCL wants to read... tell him no */
563         return 0;
564 }
565
566 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
567 {
568         char *cp;
569         int n;
570         Jim_Interp *interp;
571         command_context_t *context;
572
573         n = -1;
574         interp = cookie;
575         if (interp == NULL)
576                 return n;
577
578         context = Jim_GetAssocData(interp, "context");
579         if (context == NULL)
580         {
581                 LOG_ERROR("openocd_jim_vfprintf: no command context");
582                 return n;
583         }
584
585         cp = alloc_vprintf(fmt, ap);
586         if (cp)
587         {
588                 command_output_text(context, cp);
589                 n = strlen(cp);
590                 free(cp);
591         }
592         return n;
593 }
594
595 static int openocd_jim_fflush(void *cookie)
596 {
597         /* nothing to flush */
598         return 0;
599 }
600
601 static char* openocd_jim_fgets(char *s, int size, void *cookie)
602 {
603         /* not supported */
604         errno = ENOTSUP;
605         return NULL;
606 }
607
608 command_context_t* command_init()
609 {
610         command_context_t* context = malloc(sizeof(command_context_t));
611         extern unsigned const char startup_tcl[];
612
613         context->mode = COMMAND_EXEC;
614         context->commands = NULL;
615         context->current_target = 0;
616         context->output_handler = NULL;
617         context->output_handler_priv = NULL;
618
619 #ifdef JIM_EMBEDDED
620         Jim_InitEmbedded();
621         /* Create an interpreter */
622         interp = Jim_CreateInterp();
623         /* Add all the Jim core commands */
624         Jim_RegisterCoreCommands(interp);
625 #endif
626
627         Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
628         Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
629
630         /* Set Jim's STDIO */
631         interp->cookie_stdin = interp;
632         interp->cookie_stdout = interp;
633         interp->cookie_stderr = interp;
634         interp->cb_fwrite = openocd_jim_fwrite;
635         interp->cb_fread = openocd_jim_fread ;
636         interp->cb_vfprintf = openocd_jim_vfprintf;
637         interp->cb_fflush = openocd_jim_fflush;
638         interp->cb_fgets = openocd_jim_fgets;
639         
640         add_default_dirs();
641
642         if (Jim_Eval(interp, startup_tcl)==JIM_ERR)
643         {
644                 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD compile time)");
645                 Jim_PrintErrorMessage(interp);
646                 exit(-1);
647         }
648
649         register_command(context, NULL, "sleep", handle_sleep_command,
650                                          COMMAND_ANY, "sleep for <n> milliseconds");
651         
652         register_command(context, NULL, "fast", handle_fast_command,
653                                          COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
654         
655         return context;
656 }
657
658 int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
659 {
660         if (!cmd_ctx)
661                 return ERROR_INVALID_ARGUMENTS;
662
663         cmd_ctx->mode = mode;
664         return ERROR_OK;
665 }
666
667 /* sleep command sleeps for <n> miliseconds
668  * this is useful in target startup scripts
669  */
670 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
671 {
672         unsigned long duration = 0;
673         
674         if (argc == 1)
675         {
676                 duration = strtoul(args[0], NULL, 0);
677                 usleep(duration * 1000);
678         }
679
680         return ERROR_OK;
681 }
682
683 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
684 {
685         if (argc!=1)
686                 return ERROR_COMMAND_SYNTAX_ERROR;
687         
688         fast_and_dangerous = strcmp("enable", args[0])==0;
689         
690         return ERROR_OK;
691 }
692
693 void register_jim(struct command_context_s *cmd_ctx, const char *name, int (*cmd)(Jim_Interp *interp, int argc, Jim_Obj *const *argv), const char *help)
694 {
695         Jim_CreateCommand(interp, name, cmd, NULL, NULL);
696
697         /* FIX!!! it would be prettier to invoke add_help_text... 
698            accumulate help text in Tcl helptext list.  */
699         Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
700         if (Jim_IsShared(helptext))
701                 helptext = Jim_DuplicateObj(interp, helptext);
702     
703         Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
704         
705         Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
706         Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, name, -1));
707         
708         Jim_ListAppendElement(interp, cmd_entry, cmd_list);
709         Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
710         Jim_ListAppendElement(interp, helptext, cmd_entry);
711 }