import gdb-1999-05-25 snapshot
[platform/upstream/binutils.git] / gdb / serial.c
1 /* Generic serial interface routines
2    Copyright 1992, 1993, 1996, 1997 Free Software Foundation, Inc.
3
4 This file is part of GDB.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include "defs.h"
21 #include <ctype.h>
22 #include "serial.h"
23 #include "gdb_string.h"
24 #include "gdbcmd.h"
25
26 extern void _initialize_serial PARAMS ((void));
27
28 /* Linked list of serial I/O handlers */
29
30 static struct serial_ops *serial_ops_list = NULL;
31
32 /* This is the last serial stream opened.  Used by connect command. */
33
34 static serial_t last_serial_opened = NULL;
35
36 /* Pointer to list of scb's. */
37
38 static serial_t scb_base;
39
40 /* Non-NULL gives filename which contains a recording of the remote session,
41    suitable for playback by gdbserver. */
42
43 static char *serial_logfile = NULL;
44 static GDB_FILE *serial_logfp = NULL;
45
46 static struct serial_ops *serial_interface_lookup PARAMS ((char *));
47 static void serial_logchar PARAMS ((int, int, int));
48 static char logbase_hex[] = "hex";
49 static char logbase_octal[] = "octal";
50 static char logbase_ascii[] = "ascii";
51 static char *logbase_enums[] = {logbase_hex, logbase_octal, logbase_ascii, NULL};
52 static char *serial_logbase = logbase_ascii;
53
54 \f
55 static int serial_current_type = 0;
56
57 /* Log char CH of type CHTYPE, with TIMEOUT */
58
59 /* Define bogus char to represent a BREAK.  Should be careful to choose a value
60    that can't be confused with a normal char, or an error code.  */
61 #define SERIAL_BREAK 1235
62
63 static void
64 serial_logchar (ch_type, ch, timeout)
65      int ch_type;
66      int ch;
67      int timeout;
68 {
69   if (ch_type != serial_current_type)
70     {
71       fprintf_unfiltered (serial_logfp, "\n%c ", ch_type);
72       serial_current_type = ch_type;
73     }
74
75   if (serial_logbase != logbase_ascii)
76     fputc_unfiltered (' ', serial_logfp);
77
78   switch (ch)
79     {
80     case SERIAL_TIMEOUT:
81       fprintf_unfiltered (serial_logfp, "<Timeout: %d seconds>", timeout);
82       return;
83     case SERIAL_ERROR:
84       fprintf_unfiltered (serial_logfp, "<Error: %s>", safe_strerror (errno));
85       return;
86     case SERIAL_EOF:
87       fputs_unfiltered ("<Eof>", serial_logfp);
88       return;
89     case SERIAL_BREAK:
90       fputs_unfiltered ("<Break>", serial_logfp);
91       return;
92     default:
93       if (serial_logbase == logbase_hex)
94         fprintf_unfiltered (serial_logfp, "%02x", ch & 0xff);
95       else if (serial_logbase == logbase_octal)
96         fprintf_unfiltered (serial_logfp, "%03o", ch & 0xff);
97       else
98         switch (ch)
99           {
100           case '\\':    fputs_unfiltered ("\\\\", serial_logfp); break; 
101           case '\b':    fputs_unfiltered ("\\b", serial_logfp); break;  
102           case '\f':    fputs_unfiltered ("\\f", serial_logfp); break;  
103           case '\n':    fputs_unfiltered ("\\n", serial_logfp); break;  
104           case '\r':    fputs_unfiltered ("\\r", serial_logfp); break;  
105           case '\t':    fputs_unfiltered ("\\t", serial_logfp); break;  
106           case '\v':    fputs_unfiltered ("\\v", serial_logfp); break;  
107           default:      fprintf_unfiltered (serial_logfp, isprint (ch) ? "%c" : "\\x%02x", ch & 0xFF); break;
108           }
109     }
110 }
111
112 void
113 serial_log_command (cmd)
114      const char *cmd;
115 {
116   if (!serial_logfp)
117     return;
118
119   serial_current_type = 'c';
120
121   fputs_unfiltered ("\nc ", serial_logfp);
122   fputs_unfiltered (cmd, serial_logfp);
123
124   /* Make sure that the log file is as up-to-date as possible,
125      in case we are getting ready to dump core or something. */
126   gdb_flush (serial_logfp);
127 }
128
129 int
130 serial_write (scb, str, len)
131      serial_t scb;
132      const char *str;
133      int len;
134 {
135   if (serial_logfp != NULL)
136     {
137       int count;
138
139       for (count = 0; count < len; count++)
140         serial_logchar ('w', str[count] & 0xff, 0);
141
142       /* Make sure that the log file is as up-to-date as possible,
143          in case we are getting ready to dump core or something. */
144       gdb_flush (serial_logfp);
145     }
146
147   return (scb -> ops -> write (scb, str, len));
148 }
149
150 int
151 serial_readchar (scb, timeout)
152      serial_t scb;
153      int timeout;
154 {
155   int ch;
156
157   ch = scb -> ops -> readchar (scb, timeout);
158   if (serial_logfp != NULL)
159     {
160       serial_logchar ('r', ch, timeout);
161
162       /* Make sure that the log file is as up-to-date as possible,
163          in case we are getting ready to dump core or something. */
164       gdb_flush (serial_logfp);
165     }
166
167   return (ch);
168 }
169
170 int
171 serial_send_break (scb)
172      serial_t scb;
173 {
174   if (serial_logfp != NULL)
175     serial_logchar ('w', SERIAL_BREAK, 0);
176
177   return (scb -> ops -> send_break (scb));
178 }
179
180 static struct serial_ops *
181 serial_interface_lookup (name)
182      char *name;
183 {
184   struct serial_ops *ops;
185
186   for (ops = serial_ops_list; ops; ops = ops->next)
187     if (strcmp (name, ops->name) == 0)
188       return ops;
189
190   return NULL;
191 }
192
193 void
194 serial_add_interface(optable)
195      struct serial_ops *optable;
196 {
197   optable->next = serial_ops_list;
198   serial_ops_list = optable;
199 }
200
201 /* Open up a device or a network socket, depending upon the syntax of NAME. */
202
203 serial_t
204 serial_open (name)
205      const char *name;
206 {
207   serial_t scb;
208   struct serial_ops *ops;
209
210   for (scb = scb_base; scb; scb = scb->next)
211     if (scb->name && strcmp (scb->name, name) == 0)
212       {
213         scb->refcnt++;
214         return scb;
215       }
216
217   if (strcmp (name, "ocd") == 0)
218     ops = serial_interface_lookup ("ocd");
219   else if (strcmp (name, "pc") == 0)
220     ops = serial_interface_lookup ("pc");
221   else if (strchr (name, ':'))
222     ops = serial_interface_lookup ("tcp");
223   else if (strncmp (name, "lpt", 3) == 0)
224     ops = serial_interface_lookup ("parallel");
225   else
226     ops = serial_interface_lookup ("hardwire");
227
228   if (!ops)
229     return NULL;
230
231   scb = (serial_t)xmalloc (sizeof (struct _serial_t));
232
233   scb->ops = ops;
234
235   scb->bufcnt = 0;
236   scb->bufp = scb->buf;
237
238   if (scb->ops->open(scb, name))
239     {
240       free (scb);
241       return NULL;
242     }
243
244   scb->name = strsave (name);
245   scb->next = scb_base;
246   scb->refcnt = 1;
247   scb_base = scb;
248
249   last_serial_opened = scb;
250
251   if (serial_logfile != NULL)
252     {
253       serial_logfp = gdb_fopen (serial_logfile, "w");
254       if (serial_logfp == NULL)
255         perror_with_name (serial_logfile);
256     }
257
258   return scb;
259 }
260
261 serial_t
262 serial_fdopen (fd)
263      const int fd;
264 {
265   serial_t scb;
266   struct serial_ops *ops;
267
268   for (scb = scb_base; scb; scb = scb->next)
269     if (scb->fd == fd)
270       {
271         scb->refcnt++;
272         return scb;
273       }
274
275   ops = serial_interface_lookup ("hardwire");
276
277   if (!ops)
278     return NULL;
279
280   scb = (serial_t)xmalloc (sizeof (struct _serial_t));
281
282   scb->ops = ops;
283
284   scb->bufcnt = 0;
285   scb->bufp = scb->buf;
286
287   scb->fd = fd;
288
289   scb->name = NULL;
290   scb->next = scb_base;
291   scb->refcnt = 1;
292   scb_base = scb;
293
294   last_serial_opened = scb;
295
296   return scb;
297 }
298
299 void
300 serial_close (scb, really_close)
301      serial_t scb;
302      int really_close;
303 {
304   serial_t tmp_scb;
305
306   last_serial_opened = NULL;
307
308   if (serial_logfp)
309     {
310       fputs_unfiltered ("\nEnd of log\n", serial_logfp);
311       serial_current_type = 0;
312
313       /* XXX - What if serial_logfp == gdb_stdout or gdb_stderr? */
314       gdb_fclose (&serial_logfp); 
315       serial_logfp = NULL;
316     }
317
318 /* This is bogus.  It's not our fault if you pass us a bad scb...!  Rob, you
319    should fix your code instead.  */
320
321   if (!scb)
322     return;
323
324   scb->refcnt--;
325   if (scb->refcnt > 0)
326     return;
327
328   if (really_close)
329     scb->ops->close (scb);
330
331   if (scb->name)
332     free (scb->name);
333
334   if (scb_base == scb)
335     scb_base = scb_base->next;
336   else
337     for (tmp_scb = scb_base; tmp_scb; tmp_scb = tmp_scb->next)
338       {
339         if (tmp_scb->next != scb)
340           continue;
341
342         tmp_scb->next = tmp_scb->next->next;
343         break;
344       }
345
346   free(scb);
347 }
348
349 #if 0
350 /*
351 The connect command is #if 0 because I hadn't thought of an elegant
352 way to wait for I/O on two serial_t's simultaneously.  Two solutions
353 came to mind:
354
355         1) Fork, and have have one fork handle the to user direction,
356            and have the other hand the to target direction.  This
357            obviously won't cut it for MSDOS.
358
359         2) Use something like select.  This assumes that stdin and
360            the target side can both be waited on via the same
361            mechanism.  This may not be true for DOS, if GDB is
362            talking to the target via a TCP socket.
363 -grossman, 8 Jun 93
364 */
365
366 /* Connect the user directly to the remote system.  This command acts just like
367    the 'cu' or 'tip' command.  Use <CR>~. or <CR>~^D to break out.  */
368
369 static serial_t tty_desc;               /* Controlling terminal */
370
371 static void
372 cleanup_tty(ttystate)
373      serial_ttystate ttystate;
374 {
375   printf_unfiltered ("\r\n[Exiting connect mode]\r\n");
376   SERIAL_SET_TTY_STATE (tty_desc, ttystate);
377   free (ttystate);
378   SERIAL_CLOSE (tty_desc);
379 }
380
381 static void
382 connect_command (args, fromtty)
383      char       *args;
384      int        fromtty;
385 {
386   int c;
387   char cur_esc = 0;
388   serial_ttystate ttystate;
389   serial_t port_desc;           /* TTY port */
390
391   dont_repeat();
392
393   if (args)
394     fprintf_unfiltered(gdb_stderr, "This command takes no args.  They have been ignored.\n");
395         
396   printf_unfiltered("[Entering connect mode.  Use ~. or ~^D to escape]\n");
397
398   tty_desc = SERIAL_FDOPEN (0);
399   port_desc = last_serial_opened;
400
401   ttystate = SERIAL_GET_TTY_STATE (tty_desc);
402
403   SERIAL_RAW (tty_desc);
404   SERIAL_RAW (port_desc);
405
406   make_cleanup (cleanup_tty, ttystate);
407
408   while (1)
409     {
410       int mask;
411
412       mask = SERIAL_WAIT_2 (tty_desc, port_desc, -1);
413
414       if (mask & 2)
415         {                       /* tty input */
416           char cx;
417
418           while (1)
419             {
420               c = SERIAL_READCHAR(tty_desc, 0);
421
422               if (c == SERIAL_TIMEOUT)
423                   break;
424
425               if (c < 0)
426                 perror_with_name("connect");
427
428               cx = c;
429               SERIAL_WRITE(port_desc, &cx, 1);
430
431               switch (cur_esc)
432                 {
433                 case 0:
434                   if (c == '\r')
435                     cur_esc = c;
436                   break;
437                 case '\r':
438                   if (c == '~')
439                     cur_esc = c;
440                   else
441                     cur_esc = 0;
442                   break;
443                 case '~':
444                   if (c == '.' || c == '\004')
445                     return;
446                   else
447                     cur_esc = 0;
448                 }
449             }
450         }
451
452       if (mask & 1)
453         {                       /* Port input */
454           char cx;
455
456           while (1)
457             {
458               c = SERIAL_READCHAR(port_desc, 0);
459
460               if (c == SERIAL_TIMEOUT)
461                   break;
462
463               if (c < 0)
464                 perror_with_name("connect");
465
466               cx = c;
467
468               SERIAL_WRITE(tty_desc, &cx, 1);
469             }
470         }
471     }
472 }
473 #endif /* 0 */
474
475 /* VARARGS */
476 void
477 #ifdef ANSI_PROTOTYPES
478 serial_printf (serial_t desc, const char *format, ...)
479 #else
480 serial_printf (va_alist)
481      va_dcl
482 #endif
483 {
484   va_list args;
485   char *buf;
486 #ifdef ANSI_PROTOTYPES
487   va_start (args, format);
488 #else
489   serial_t desc;
490   char *format;
491
492   va_start (args);
493   desc = va_arg (args, serial_t);
494   format = va_arg (args, char *);
495 #endif
496
497   vasprintf (&buf, format, args);
498   SERIAL_WRITE (desc, buf, strlen (buf));
499
500   free (buf);
501   va_end (args);
502 }
503
504 void
505 _initialize_serial ()
506 {
507 #if 0
508   add_com ("connect", class_obscure, connect_command,
509            "Connect the terminal directly up to the command monitor.\n\
510 Use <CR>~. or <CR>~^D to break out.");
511 #endif /* 0 */
512
513   add_show_from_set 
514     (add_set_cmd ("remotelogfile", no_class,
515                   var_filename, (char *) &serial_logfile,
516                   "Set filename for remote session recording.\n\
517 This file is used to record the remote session for future playback\n\
518 by gdbserver.", 
519                   &setlist),
520      &showlist);
521
522   add_show_from_set 
523     (add_set_enum_cmd ("remotelogbase", no_class,
524                        logbase_enums, (char *) &serial_logbase,
525                        "Set numerical base for remote session logging",
526                        &setlist),
527      &showlist);
528 }