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