496e308ee13a26805f515da99efea46accacccaf
[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 /* 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, "pc") == 0)
216     ops = serial_interface_lookup ("pc");
217   else if (strchr (name, ':'))
218     ops = serial_interface_lookup ("tcp");
219   else if (strncmp (name, "lpt", 3) == 0)
220     ops = serial_interface_lookup ("parallel");
221   else
222     ops = serial_interface_lookup ("hardwire");
223
224   if (!ops)
225     return NULL;
226
227   scb = (serial_t)xmalloc (sizeof (struct _serial_t));
228
229   scb->ops = ops;
230
231   scb->bufcnt = 0;
232   scb->bufp = scb->buf;
233
234   if (scb->ops->open(scb, name))
235     {
236       free (scb);
237       return NULL;
238     }
239
240   scb->name = strsave (name);
241   scb->next = scb_base;
242   scb->refcnt = 1;
243   scb_base = scb;
244
245   last_serial_opened = scb;
246
247   if (serial_logfile != NULL)
248     {
249       serial_logfp = gdb_fopen (serial_logfile, "w");
250       if (serial_logfp == NULL)
251         perror_with_name (serial_logfile);
252     }
253
254   return scb;
255 }
256
257 serial_t
258 serial_fdopen (fd)
259      const int fd;
260 {
261   serial_t scb;
262   struct serial_ops *ops;
263
264   for (scb = scb_base; scb; scb = scb->next)
265     if (scb->fd == fd)
266       {
267         scb->refcnt++;
268         return scb;
269       }
270
271   ops = serial_interface_lookup ("hardwire");
272
273   if (!ops)
274     return NULL;
275
276   scb = (serial_t)xmalloc (sizeof (struct _serial_t));
277
278   scb->ops = ops;
279
280   scb->bufcnt = 0;
281   scb->bufp = scb->buf;
282
283   scb->fd = fd;
284
285   scb->name = NULL;
286   scb->next = scb_base;
287   scb->refcnt = 1;
288   scb_base = scb;
289
290   last_serial_opened = scb;
291
292   return scb;
293 }
294
295 void
296 serial_close (scb, really_close)
297      serial_t scb;
298      int really_close;
299 {
300   serial_t tmp_scb;
301
302   last_serial_opened = NULL;
303
304   if (serial_logfp)
305     {
306       fputs_unfiltered ("\nEnd of log\n", serial_logfp);
307       serial_current_type = 0;
308
309       fclose (serial_logfp);    /* XXX - What if serial_logfp == stdout or stderr? */
310       serial_logfp = NULL;
311     }
312
313 /* This is bogus.  It's not our fault if you pass us a bad scb...!  Rob, you
314    should fix your code instead.  */
315
316   if (!scb)
317     return;
318
319   scb->refcnt--;
320   if (scb->refcnt > 0)
321     return;
322
323   if (really_close)
324     scb->ops->close (scb);
325
326   if (scb->name)
327     free (scb->name);
328
329   if (scb_base == scb)
330     scb_base = scb_base->next;
331   else
332     for (tmp_scb = scb_base; tmp_scb; tmp_scb = tmp_scb->next)
333       {
334         if (tmp_scb->next != scb)
335           continue;
336
337         tmp_scb->next = tmp_scb->next->next;
338         break;
339       }
340
341   free(scb);
342 }
343
344 #if 0
345 /*
346 The connect command is #if 0 because I hadn't thought of an elegant
347 way to wait for I/O on two serial_t's simultaneously.  Two solutions
348 came to mind:
349
350         1) Fork, and have have one fork handle the to user direction,
351            and have the other hand the to target direction.  This
352            obviously won't cut it for MSDOS.
353
354         2) Use something like select.  This assumes that stdin and
355            the target side can both be waited on via the same
356            mechanism.  This may not be true for DOS, if GDB is
357            talking to the target via a TCP socket.
358 -grossman, 8 Jun 93
359 */
360
361 /* Connect the user directly to the remote system.  This command acts just like
362    the 'cu' or 'tip' command.  Use <CR>~. or <CR>~^D to break out.  */
363
364 static serial_t tty_desc;               /* Controlling terminal */
365
366 static void
367 cleanup_tty(ttystate)
368      serial_ttystate ttystate;
369 {
370   printf_unfiltered ("\r\n[Exiting connect mode]\r\n");
371   SERIAL_SET_TTY_STATE (tty_desc, ttystate);
372   free (ttystate);
373   SERIAL_CLOSE (tty_desc);
374 }
375
376 static void
377 connect_command (args, fromtty)
378      char       *args;
379      int        fromtty;
380 {
381   int c;
382   char cur_esc = 0;
383   serial_ttystate ttystate;
384   serial_t port_desc;           /* TTY port */
385
386   dont_repeat();
387
388   if (args)
389     fprintf_unfiltered(gdb_stderr, "This command takes no args.  They have been ignored.\n");
390         
391   printf_unfiltered("[Entering connect mode.  Use ~. or ~^D to escape]\n");
392
393   tty_desc = SERIAL_FDOPEN (0);
394   port_desc = last_serial_opened;
395
396   ttystate = SERIAL_GET_TTY_STATE (tty_desc);
397
398   SERIAL_RAW (tty_desc);
399   SERIAL_RAW (port_desc);
400
401   make_cleanup (cleanup_tty, ttystate);
402
403   while (1)
404     {
405       int mask;
406
407       mask = SERIAL_WAIT_2 (tty_desc, port_desc, -1);
408
409       if (mask & 2)
410         {                       /* tty input */
411           char cx;
412
413           while (1)
414             {
415               c = SERIAL_READCHAR(tty_desc, 0);
416
417               if (c == SERIAL_TIMEOUT)
418                   break;
419
420               if (c < 0)
421                 perror_with_name("connect");
422
423               cx = c;
424               SERIAL_WRITE(port_desc, &cx, 1);
425
426               switch (cur_esc)
427                 {
428                 case 0:
429                   if (c == '\r')
430                     cur_esc = c;
431                   break;
432                 case '\r':
433                   if (c == '~')
434                     cur_esc = c;
435                   else
436                     cur_esc = 0;
437                   break;
438                 case '~':
439                   if (c == '.' || c == '\004')
440                     return;
441                   else
442                     cur_esc = 0;
443                 }
444             }
445         }
446
447       if (mask & 1)
448         {                       /* Port input */
449           char cx;
450
451           while (1)
452             {
453               c = SERIAL_READCHAR(port_desc, 0);
454
455               if (c == SERIAL_TIMEOUT)
456                   break;
457
458               if (c < 0)
459                 perror_with_name("connect");
460
461               cx = c;
462
463               SERIAL_WRITE(tty_desc, &cx, 1);
464             }
465         }
466     }
467 }
468 #endif /* 0 */
469
470 /* VARARGS */
471 void
472 #ifdef ANSI_PROTOTYPES
473 serial_printf (serial_t desc, const char *format, ...)
474 #else
475 serial_printf (va_alist)
476      va_dcl
477 #endif
478 {
479   va_list args;
480   char *buf;
481 #ifdef ANSI_PROTOTYPES
482   va_start (args, format);
483 #else
484   serial_t desc;
485   char *format;
486
487   va_start (args);
488   desc = va_arg (args, serial_t);
489   format = va_arg (args, char *);
490 #endif
491
492   vasprintf (&buf, format, args);
493   SERIAL_WRITE (desc, buf, strlen (buf));
494
495   free (buf);
496   va_end (args);
497 }
498
499 void
500 _initialize_serial ()
501 {
502 #if 0
503   add_com ("connect", class_obscure, connect_command,
504            "Connect the terminal directly up to the command monitor.\n\
505 Use <CR>~. or <CR>~^D to break out.");
506 #endif /* 0 */
507
508   add_show_from_set (add_set_cmd ("remotelogfile", no_class,
509                                   var_filename, (char *)&serial_logfile,
510                                   "Set filename for remote session recording.\n\
511 This file is used to record the remote session for future playback\n\
512 by gdbserver.", &setlist),
513                      &showlist);
514
515   add_show_from_set (add_set_enum_cmd ("remotelogbase", no_class,
516                                        logbase_enums,
517                                        (char *)&serial_logbase,
518                                        "Set ...",
519                                        &setlist),
520                            &showlist);
521 }