serial.c revision 46283
1/* Generic serial interface routines
2   Copyright 1992, 1993, 1996, 1997 Free Software Foundation, Inc.
3
4This file is part of GDB.
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2 of the License, or
9(at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, 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
28static struct serial_ops *serial_ops_list = NULL;
29
30/* This is the last serial stream opened.  Used by connect command. */
31
32static serial_t last_serial_opened = NULL;
33
34/* Pointer to list of scb's. */
35
36static 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
41static char *serial_logfile = NULL;
42static GDB_FILE *serial_logfp = NULL;
43
44static struct serial_ops *serial_interface_lookup PARAMS ((char *));
45static void serial_logchar PARAMS ((int, int, int));
46static char logbase_hex[] = "hex";
47static char logbase_octal[] = "octal";
48static char logbase_ascii[] = "ascii";
49static char *logbase_enums[] = {logbase_hex, logbase_octal, logbase_ascii, NULL};
50static char *serial_logbase = logbase_ascii;
51
52
53static 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
61static void
62serial_logchar (ch_type, ch, timeout)
63     int ch_type;
64     int ch;
65     int timeout;
66{
67  if (ch_type != serial_current_type)
68    {
69      fprintf_unfiltered (serial_logfp, "\n%c ", ch_type);
70      serial_current_type = ch_type;
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
110void
111serial_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
127int
128serial_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
148int
149serial_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
168int
169serial_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
178static struct serial_ops *
179serial_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
191void
192serial_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
201serial_t
202serial_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
259serial_t
260serial_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
297void
298serial_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      /* XXX - What if serial_logfp == gdb_stdout or gdb_stderr? */
312      gdb_fclose (&serial_logfp);
313      serial_logfp = NULL;
314    }
315
316/* This is bogus.  It's not our fault if you pass us a bad scb...!  Rob, you
317   should fix your code instead.  */
318
319  if (!scb)
320    return;
321
322  scb->refcnt--;
323  if (scb->refcnt > 0)
324    return;
325
326  if (really_close)
327    scb->ops->close (scb);
328
329  if (scb->name)
330    free (scb->name);
331
332  if (scb_base == scb)
333    scb_base = scb_base->next;
334  else
335    for (tmp_scb = scb_base; tmp_scb; tmp_scb = tmp_scb->next)
336      {
337	if (tmp_scb->next != scb)
338	  continue;
339
340	tmp_scb->next = tmp_scb->next->next;
341	break;
342      }
343
344  free(scb);
345}
346
347#if 0
348/*
349The connect command is #if 0 because I hadn't thought of an elegant
350way to wait for I/O on two serial_t's simultaneously.  Two solutions
351came to mind:
352
353	1) Fork, and have have one fork handle the to user direction,
354	   and have the other hand the to target direction.  This
355	   obviously won't cut it for MSDOS.
356
357	2) Use something like select.  This assumes that stdin and
358	   the target side can both be waited on via the same
359	   mechanism.  This may not be true for DOS, if GDB is
360	   talking to the target via a TCP socket.
361-grossman, 8 Jun 93
362*/
363
364/* Connect the user directly to the remote system.  This command acts just like
365   the 'cu' or 'tip' command.  Use <CR>~. or <CR>~^D to break out.  */
366
367static serial_t tty_desc;		/* Controlling terminal */
368
369static void
370cleanup_tty(ttystate)
371     serial_ttystate ttystate;
372{
373  printf_unfiltered ("\r\n[Exiting connect mode]\r\n");
374  SERIAL_SET_TTY_STATE (tty_desc, ttystate);
375  free (ttystate);
376  SERIAL_CLOSE (tty_desc);
377}
378
379static void
380connect_command (args, fromtty)
381     char	*args;
382     int	fromtty;
383{
384  int c;
385  char cur_esc = 0;
386  serial_ttystate ttystate;
387  serial_t port_desc;		/* TTY port */
388
389  dont_repeat();
390
391  if (args)
392    fprintf_unfiltered(gdb_stderr, "This command takes no args.  They have been ignored.\n");
393
394  printf_unfiltered("[Entering connect mode.  Use ~. or ~^D to escape]\n");
395
396  tty_desc = SERIAL_FDOPEN (0);
397  port_desc = last_serial_opened;
398
399  ttystate = SERIAL_GET_TTY_STATE (tty_desc);
400
401  SERIAL_RAW (tty_desc);
402  SERIAL_RAW (port_desc);
403
404  make_cleanup (cleanup_tty, ttystate);
405
406  while (1)
407    {
408      int mask;
409
410      mask = SERIAL_WAIT_2 (tty_desc, port_desc, -1);
411
412      if (mask & 2)
413	{			/* tty input */
414	  char cx;
415
416	  while (1)
417	    {
418	      c = SERIAL_READCHAR(tty_desc, 0);
419
420	      if (c == SERIAL_TIMEOUT)
421		  break;
422
423	      if (c < 0)
424		perror_with_name("connect");
425
426	      cx = c;
427	      SERIAL_WRITE(port_desc, &cx, 1);
428
429	      switch (cur_esc)
430		{
431		case 0:
432		  if (c == '\r')
433		    cur_esc = c;
434		  break;
435		case '\r':
436		  if (c == '~')
437		    cur_esc = c;
438		  else
439		    cur_esc = 0;
440		  break;
441		case '~':
442		  if (c == '.' || c == '\004')
443		    return;
444		  else
445		    cur_esc = 0;
446		}
447	    }
448	}
449
450      if (mask & 1)
451	{			/* Port input */
452	  char cx;
453
454	  while (1)
455	    {
456	      c = SERIAL_READCHAR(port_desc, 0);
457
458	      if (c == SERIAL_TIMEOUT)
459		  break;
460
461	      if (c < 0)
462		perror_with_name("connect");
463
464	      cx = c;
465
466	      SERIAL_WRITE(tty_desc, &cx, 1);
467	    }
468	}
469    }
470}
471#endif /* 0 */
472
473/* VARARGS */
474void
475#ifdef ANSI_PROTOTYPES
476serial_printf (serial_t desc, const char *format, ...)
477#else
478serial_printf (va_alist)
479     va_dcl
480#endif
481{
482  va_list args;
483  char *buf;
484#ifdef ANSI_PROTOTYPES
485  va_start (args, format);
486#else
487  serial_t desc;
488  char *format;
489
490  va_start (args);
491  desc = va_arg (args, serial_t);
492  format = va_arg (args, char *);
493#endif
494
495  vasprintf (&buf, format, args);
496  SERIAL_WRITE (desc, buf, strlen (buf));
497
498  free (buf);
499  va_end (args);
500}
501
502void
503_initialize_serial ()
504{
505#if 0
506  add_com ("connect", class_obscure, connect_command,
507	   "Connect the terminal directly up to the command monitor.\n\
508Use <CR>~. or <CR>~^D to break out.");
509#endif /* 0 */
510
511  add_show_from_set
512    (add_set_cmd ("remotelogfile", no_class,
513		  var_filename, (char *) &serial_logfile,
514		  "Set filename for remote session recording.\n\
515This file is used to record the remote session for future playback\n\
516by gdbserver.",
517		  &setlist),
518     &showlist);
519
520  add_show_from_set
521    (add_set_enum_cmd ("remotelogbase", no_class,
522		       logbase_enums, (char *) &serial_logbase,
523		       "Set numerical base for remote session logging",
524		       &setlist),
525     &showlist);
526}
527