added echo command in tcl. Issues a LOG_USER() for the single argument.
[fw/openocd] / src / openocd.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #define OPENOCD_VERSION "Open On-Chip Debugger " VERSION " (" PKGBLDDATE ") svn:" PKGBLDREV
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "log.h"
28 #include "types.h"
29 #include "jtag.h"
30 #include "configuration.h"
31 #include "interpreter.h"
32 #include "xsvf.h"
33 #include "target.h"
34 #include "flash.h"
35 #include "nand.h"
36 #include "pld.h"
37
38 #include "command.h"
39 #include "server.h"
40 #include "telnet_server.h"
41 #include "gdb_server.h"
42
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include <strings.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <errno.h>
51
52 #define JIM_EMBEDDED
53 #include "jim.h"
54
55
56 /* Give TELNET a way to find out what version this is */
57 int handle_version_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
58 {
59         command_print(cmd_ctx, OPENOCD_VERSION);
60
61         return ERROR_OK;
62 }
63
64 static int daemon_startup = 0;
65
66 int handle_daemon_startup_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
67 {
68         if (argc==0)
69                 return ERROR_OK;
70         if (argc > 1 )
71                 return ERROR_COMMAND_SYNTAX_ERROR;
72         
73         daemon_startup = strcmp("reset", args[0])==0;
74         
75         command_print(cmd_ctx, OPENOCD_VERSION);
76
77         return ERROR_OK;
78 }
79
80
81 void exit_handler(void)
82 {
83         /* close JTAG interface */
84         if (jtag && jtag->quit)
85                 jtag->quit();
86 }
87
88
89 /* OpenOCD can't really handle failure of this command. Patches welcome! :-) */
90 int handle_init_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
91 {
92         int retval;
93         static int initialized=0;
94         if (initialized)
95                 return ERROR_OK;
96         
97         initialized=1;
98         
99         command_set_output_handler(cmd_ctx, configuration_output_handler, NULL);
100
101         atexit(exit_handler);
102
103         
104         if (target_init(cmd_ctx) != ERROR_OK)
105                 return ERROR_FAIL;
106         LOG_DEBUG("target init complete");
107
108         if ((retval=jtag_interface_init(cmd_ctx)) != ERROR_OK)
109         {
110                 /* we must be able to set up the jtag interface */
111                 return retval;
112         }
113         LOG_DEBUG("jtag interface init complete");
114
115         /* Try to initialize & examine the JTAG chain at this point, but
116          * continue startup regardless
117          */
118         if (jtag_init(cmd_ctx) == ERROR_OK)
119         {
120                 LOG_DEBUG("jtag init complete");
121                 if (target_examine(cmd_ctx) == ERROR_OK)
122                 {
123                         LOG_DEBUG("jtag examine complete");
124                 }
125         }
126
127         
128         if (flash_init_drivers(cmd_ctx) != ERROR_OK)
129                 return ERROR_FAIL;
130         LOG_DEBUG("flash init complete");
131
132         if (nand_init(cmd_ctx) != ERROR_OK)
133                 return ERROR_FAIL;
134         LOG_DEBUG("NAND init complete");
135
136         if (pld_init(cmd_ctx) != ERROR_OK)
137                 return ERROR_FAIL;
138         LOG_DEBUG("pld init complete");
139
140         /* initialize tcp server */
141         server_init();
142
143         /* initialize telnet subsystem */
144         telnet_init("Open On-Chip Debugger");
145         gdb_init();
146
147         return ERROR_OK;
148 }
149
150
151 /* implementations of OpenOCD that uses multithreading needs to lock OpenOCD while calling
152  * OpenOCD fn's. No-op in vanilla OpenOCD
153  */
154 void lockBigLock()
155 {
156 }
157 void unlockBigLock()
158 {
159 }
160
161
162
163
164
165 Jim_Interp *interp;
166 command_context_t *active_cmd_ctx;
167
168 static void tcl_output(void *privData, const char *file, int line, 
169                 const char *function, const char *string)
170 {               
171         Jim_Obj *tclOutput=(Jim_Obj *)privData;
172
173         Jim_AppendString(interp, tclOutput, string, strlen(string));
174 }
175
176 /* try to execute as Jim command, otherwise fall back to standard command.
177
178         Note that even if the Jim command caused an error, then we succeeded
179         to execute it, hence this fn pretty much always returns ERROR_OK. 
180
181  */
182 int jim_command(command_context_t *context, char *line)
183 {
184         int retval=ERROR_OK;
185         /* FIX!!!! in reality there is only one cmd_ctx handler, but consider
186         what might happen here if there are multiple handlers w/reentrant callback
187         fn's... shudder!  */
188         active_cmd_ctx=context;
189         int retcode=Jim_Eval(interp, line);
190         
191         const char *result;
192         int reslen;
193     result = Jim_GetString(Jim_GetResult(interp), &reslen);
194     if (retcode == JIM_ERR) {
195             int len, i;
196         
197             LOG_USER_N("Runtime error, file \"%s\", line %d:" JIM_NL,
198                     interp->errorFileName, interp->errorLine);
199             LOG_USER_N("    %s" JIM_NL,
200                     Jim_GetString(interp->result, NULL));
201             Jim_ListLength(interp, interp->stackTrace, &len);
202             for (i = 0; i < len; i+= 3) {
203                 Jim_Obj *objPtr;
204                 const char *proc, *file, *line;
205         
206                 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
207                 proc = Jim_GetString(objPtr, NULL);
208                 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
209                         JIM_NONE);
210                 file = Jim_GetString(objPtr, NULL);
211                 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
212                         JIM_NONE);
213                 line = Jim_GetString(objPtr, NULL);
214                 LOG_USER_N("In procedure '%s' called at file \"%s\", line %s" JIM_NL,
215                         proc, file, line);
216             }
217     } else if (retcode == JIM_EXIT) {
218         // ignore.
219         //exit(Jim_GetExitCode(interp));
220     } else {
221         if (reslen) {
222                 int i;
223                 char buff[256+1];
224                 for (i=0; i<reslen; i+=256)
225                 {
226                         int chunk;
227                         chunk=reslen-i;
228                         if (chunk>256)
229                                 chunk=256;
230                         strncpy(buff, result, chunk);
231                         buff[chunk]=0; 
232                 LOG_USER_N("%s", buff);
233                 }
234                 LOG_USER_N("%s", "\n");
235         }
236     }
237         return retval;
238 }
239
240 static int startLoop=0;
241
242 static int
243 Jim_Command_openocd_ignore(Jim_Interp *interp, 
244                                    int argc,
245                                    Jim_Obj *const *argv,
246                                    int ignore)
247 {
248         int retval;
249     char *cmd = (char*)Jim_GetString(argv[1], NULL);
250
251         lockBigLock();
252         
253     Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
254     
255     if (startLoop)
256     {
257         // We don't know whether or not the telnet/gdb server is running...
258         target_call_timer_callbacks_now();
259     }
260         
261         log_add_callback(tcl_output, tclOutput);
262     retval=command_run_line_internal(active_cmd_ctx, cmd);
263     
264     if (startLoop)
265     {
266         target_call_timer_callbacks_now();
267     }
268         log_remove_callback(tcl_output, tclOutput);
269     
270         Jim_SetResult(interp, tclOutput);
271     unlockBigLock();
272         
273     return (ignore||(retval==ERROR_OK))?JIM_OK:JIM_ERR;
274 }
275
276 static int
277 Jim_Command_openocd(Jim_Interp *interp, 
278                                    int argc,
279                                    Jim_Obj *const *argv)
280 {
281         return Jim_Command_openocd_ignore(interp, argc, argv, 1); 
282 }
283
284 static int
285 Jim_Command_openocd_throw(Jim_Interp *interp, 
286                                    int argc,
287                                    Jim_Obj *const *argv)
288 {
289         return Jim_Command_openocd_ignore(interp, argc, argv, 0); 
290 }
291   
292
293
294
295 /* find full path to file */
296 static int
297 Jim_Command_find(Jim_Interp *interp, 
298                                    int argc,
299                                    Jim_Obj *const *argv)
300 {
301         if (argc!=2)
302                 return JIM_ERR;
303         char *file = (char*)Jim_GetString(argv[1], NULL);
304         char *full_path=find_file(file);
305         if (full_path==NULL)
306                 return JIM_ERR;
307     Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
308     free(full_path);
309     
310         Jim_SetResult(interp, result);
311         return JIM_OK;
312 }
313
314 static int
315 Jim_Command_echo(Jim_Interp *interp, 
316                                    int argc,
317                                    Jim_Obj *const *argv)
318 {
319         if (argc!=2)
320                 return JIM_ERR;
321         char *str = (char*)Jim_GetString(argv[1], NULL);
322         LOG_USER("%s", str);
323         return JIM_OK;
324 }
325
326 void initJim(void)
327 {
328     Jim_InitEmbedded();
329   
330     /* Create an interpreter */
331     interp = Jim_CreateInterp();
332     /* Add all the Jim core commands */
333     Jim_RegisterCoreCommands(interp);
334     Jim_CreateCommand(interp, "openocd", Jim_Command_openocd, NULL, NULL);
335     Jim_CreateCommand(interp, "openocd_throw", Jim_Command_openocd_throw, NULL, NULL);
336     Jim_CreateCommand(interp, "find", Jim_Command_find, NULL, NULL);
337     Jim_CreateCommand(interp, "echo", Jim_Command_echo, NULL, NULL);
338 }
339
340 int main(int argc, char *argv[])
341 {
342         initJim();
343         
344         /* initialize commandline interface */
345         command_context_t *cmd_ctx, *cfg_cmd_ctx;
346         cmd_ctx = command_init();
347
348         register_command(cmd_ctx, NULL, "version", handle_version_command,
349                                          COMMAND_EXEC, "show OpenOCD version");
350         register_command(cmd_ctx, NULL, "daemon_startup", handle_daemon_startup_command, COMMAND_CONFIG, 
351                         "deprecated - use \"init\" and \"reset\" at end of startup script instead");
352         
353         /* register subsystem commands */
354         server_register_commands(cmd_ctx);
355         telnet_register_commands(cmd_ctx);
356         gdb_register_commands(cmd_ctx);
357         log_register_commands(cmd_ctx);
358         jtag_register_commands(cmd_ctx);
359         interpreter_register_commands(cmd_ctx);
360         xsvf_register_commands(cmd_ctx);
361         target_register_commands(cmd_ctx);
362         flash_register_commands(cmd_ctx);
363         nand_register_commands(cmd_ctx);
364         pld_register_commands(cmd_ctx);
365         
366         if (log_init(cmd_ctx) != ERROR_OK)
367                 return EXIT_FAILURE;
368         LOG_DEBUG("log init complete");
369
370         LOG_OUTPUT( OPENOCD_VERSION "\n" );
371         
372         
373         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
374         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
375         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
376         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
377         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
378         LOG_OUTPUT( "$URL$\n");
379         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
380         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
381         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
382         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
383         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
384
385         register_command(cmd_ctx, NULL, "init", handle_init_command,
386                                          COMMAND_ANY, "initializes target and servers - nop on subsequent invocations");
387
388         cfg_cmd_ctx = copy_command_context(cmd_ctx);
389         cfg_cmd_ctx->mode = COMMAND_CONFIG;
390         command_set_output_handler(cfg_cmd_ctx, configuration_output_handler, NULL);
391         
392         if (parse_cmdline_args(cfg_cmd_ctx, argc, argv) != ERROR_OK)
393                 return EXIT_FAILURE;
394
395     Jim_Eval(interp, "source [find tcl/commands.tcl]");
396
397         if (parse_config_file(cfg_cmd_ctx) != ERROR_OK)
398                 return EXIT_FAILURE;
399         
400         command_done(cfg_cmd_ctx);
401
402         if (command_run_line(cmd_ctx, "init")!=ERROR_OK)
403                 return EXIT_FAILURE;
404         
405         if (daemon_startup)
406                 command_run_line(cmd_ctx, "reset");
407
408
409         startLoop=1;
410
411         /* handle network connections */
412         server_loop(cmd_ctx);
413
414         /* shut server down */
415         server_quit();
416
417         unregister_all_commands(cmd_ctx);
418         
419         /* free commandline interface */
420         command_done(cmd_ctx);
421
422         return EXIT_SUCCESS;
423 }
424