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