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