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