From: Frank Rowand <frank.row...@sonymobile.com>

Initial version of kdmx.c

kdmx is a program designed to split GDB packets and other trafic coming
from a target on a serial line into 2 separate pseudo-ttys.

Author: Tom Rini <tr...@mvista.com>
Significant update by: Frank Rowand <frank.row...@sonymobile.com>

Updates include:
   - Fix issues that prevented use of serial USB port.
   - Detect and handle I/O errors.
   - Add additional error checking.
   - Detect and handle disconnect by gdb and terminal emulator.
   - Add ability to send BREAK to serial port.
   - Replace do_read() and do_write() debug mode.
   - Expand usage text.
   - Allow terminal emulator to send the null character to the serial port.
   - Restructure and change to Linux kernel coding style.
   - General clean up.


Signed-off-by: Frank Rowand <frank.row...@sonymobile.com>

---
 kdmx/kdmx.c |  937 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 937 insertions(+)

Index: b/kdmx/kdmx.c
===================================================================
--- /dev/null
+++ b/kdmx/kdmx.c
@@ -0,0 +1,937 @@
+/*
+ * kdmx.c
+ *
+ * The guts of a gdb/console demuxer.  We take from one input both
+ * GDB packets (typically from KGDB, but could be gdbserver) and regular
+ * console I/O and split them into two pseudo-ttys.
+ *
+ * Author: Tom Rini <tr...@mvista.com>
+ * Rename and significant update by: Frank Rowand <frank.row...@sonymobile.com>
+ *
+ * 2004 (c) MontaVista Software, Inc.
+ * Copyright (c) 2014 Sony Mobile Communications Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.  See the file COPYING for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+/*
+ * "Version Update Fix"
+ * Format: YYMMDDX
+ *   YY is year
+ *   MM is month
+ *   DD is day
+ *   X  is an incrementing value a..z, restarting at 'a' for each new YYMMDD
+ */
+#define VUFX "140904a"
+
+#define pr_debug(fmt, ...) \
+({ \
+       if (debug) \
+               fprintf(stderr, fmt, ##__VA_ARGS__); \
+})
+
+#define pr_err(fmt, ...) \
+       fprintf(stderr, fmt, ##__VA_ARGS__)
+
+#define pr_info(fmt, ...) \
+       printf(fmt, ##__VA_ARGS__)
+
+int print_g;
+int print_s;
+int print_t;
+int print_G;
+int print_S;
+int print_T;
+int print_label;
+
+int prev_fd = -1;
+
+#define DW_FLAG_BREAK 0x00000001
+
+enum {
+       PREV_IO_NONE,
+       PREV_IO_READ,
+       PREV_IO_WRITE
+} prev_io = PREV_IO_NONE;
+
+#define DEFAULT_SERIAL "/dev/ttyS0"
+#define BUFMAX 2048
+#define SERIAL_PORT_PATHLEN    512
+
+int debug;
+int passthru_null_from_term;
+
+speed_t new_baudrate = B9600;  /* see /usr/include/bits/termios.h */
+
+int serial_fd;
+int term_fd;           /* terminal emulator pty master */
+int gdb_fd;            /* gdb pty master */
+
+int die(const char *msg)
+{
+       perror(msg);
+       exit(EXIT_FAILURE);
+}
+
+/*
+ * Open /dev/ptmx and get us a master/slave combo.  This assumes a
+ * Unix98 style environment.
+ */
+void get_pty(int *master)
+{
+       /* Get the master */
+       (*master) = open("/dev/ptmx", O_RDWR);
+       if (grantpt((*master)))
+               die("grantpt");
+
+       if (unlockpt((*master)))
+               die("unlockpt");
+}
+
+/*
+ * Read one char at a time, and return it.  Optionally print out
+ * what / where it happened.
+ */
+char do_read(int fd, int *ret_errno)
+{
+       unsigned char buf;
+       int ret;
+
+       /* Perform the read */
+       ret = read(fd, &buf, sizeof(buf));
+       if (ret == -1) {
+               *ret_errno = errno;
+               return -1;
+       }
+
+       /*
+        * select() reports read fd is readable on end-of-file.
+        * Do not get into an infinite loop in that case.
+        */
+       if (ret == 0) {
+               pr_err("End of file on ");
+               if (fd == gdb_fd)
+                       pr_err("gdb pty\n");
+               if (fd == serial_fd)
+                       pr_err("serial port\n");
+               if (fd == term_fd)
+                       pr_err("terminal pty\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (print_label) {
+               if ((prev_io != PREV_IO_READ) || (prev_fd != fd)) {
+                       if (print_g && (fd == gdb_fd)) {
+                               pr_debug("\ng> ");
+                               prev_fd = fd;
+                               prev_io = PREV_IO_READ;
+                       } else if (print_S && (fd == serial_fd)) {
+                               pr_debug("\ns< ");
+                               prev_fd = fd;
+                               prev_io = PREV_IO_READ;
+                       } else  if (print_t && (fd == term_fd)) {
+                               pr_debug("\nt> ");
+                               prev_fd = fd;
+                               prev_io = PREV_IO_READ;
+                       }
+               }
+       }
+
+       if ((print_g && (fd == gdb_fd))    ||
+           (print_S && (fd == serial_fd)) ||
+           (print_t && (fd == term_fd))
+          ) {
+
+               if ((buf > 0x1f) && (buf < 0x7f))
+                       pr_debug("%c", buf);
+               else
+                       pr_debug(" 0x%02x ", buf);
+
+               if (((fd != serial_fd) && (buf == '\r')) ||
+                   ((fd == serial_fd) && (buf == '\n'))
+                  ) {
+                       pr_debug("\n");
+               }
+       }
+
+       return buf;
+}
+
+/* Write a buffer, and optionally print out what / where it happened */
+void do_write(int fd, char *buf, size_t len, int dw_flag)
+{
+       char *_buf;
+       int _len;
+       int k;
+       int retry_count = 0;
+       int sel_ret;    /* select() */
+       ssize_t ret;    /* write()  */
+       fd_set writefds;
+
+       if ((print_label && len) || (dw_flag & DW_FLAG_BREAK)) {
+               if ((prev_io != PREV_IO_WRITE) || (prev_fd != fd)) {
+                       if (print_G && (fd == gdb_fd)) {
+                               pr_debug("\ng< ");
+                               prev_fd = fd;
+                               prev_io = PREV_IO_WRITE;
+                       } else if (print_s && (fd == serial_fd)) {
+                               pr_debug("\ns> ");
+                               prev_fd = fd;
+                               prev_io = PREV_IO_WRITE;
+                       } else if (print_T && (fd == term_fd)) {
+                               pr_debug("\nt< ");
+                               prev_fd = fd;
+                               prev_io = PREV_IO_WRITE;
+                       }
+               }
+       }
+
+
+
+       if ((dw_flag & DW_FLAG_BREAK) && (fd == serial_fd)) {
+               int ret;
+               ret = ioctl(fd, TCSBRK, 0);
+               if (ret)
+                       perror("BREAK ioctl() on serial port");
+               else if (print_s)
+                       /* zzz not a unique string.... */
+                       pr_debug("__BREAK__");
+       }
+
+
+
+       /* Perform the write */
+       while (len > 0) {
+
+               if (retry_count++) {
+                       FD_ZERO(&writefds);
+                       FD_SET(fd, &writefds);
+                       sel_ret = select(fd + 1, NULL, &writefds, NULL, NULL);
+                       if (sel_ret == -1)
+                               die("select");
+               }
+
+               _buf = buf;
+               ret = write(fd, buf, len);
+               _len = ret;
+
+               if (ret == -1) {
+                       if (errno != EAGAIN) {
+                               perror("write() ");
+                               if (fd == gdb_fd)
+                                       pr_err("  write() was to gdb pty\n");
+                               else if (fd == serial_fd)
+                                       pr_err("  write() was to serial 
port\n");
+                               else if (fd == term_fd)
+                                       pr_err("  write() was to term pty\n");
+                               exit(EXIT_FAILURE);
+                       }
+               } else if (ret != len) {
+                       buf += ret;
+                       len -= ret;
+                       pr_debug("do_write() failed to write full buffer, len = 
%ld, ret = %ld\n",
+                                len, ret);
+               } else {
+                       len = 0;
+               }
+
+               if ((print_G && (fd == gdb_fd))    ||
+                   (print_s && (fd == serial_fd)) ||
+                   (print_T && (fd == term_fd))
+                  ) {
+
+                       for (k = 0; k < _len; k++, _buf++) {
+
+                               if ((*_buf > 0x1f) && (*_buf < 0x7f))
+                                       pr_debug("%c", *_buf);
+                               else
+                                       pr_debug(" 0x%02x ", *_buf);
+
+                               if (((fd == serial_fd) && (*_buf == '\r')) ||
+                                   ((fd != serial_fd) && (*_buf == '\n'))
+                                  ) {
+                                       pr_debug("\n");
+                               }
+                       }
+               }
+       }
+}
+
+
+/* Convert a single hex char to its int value */
+int hex(unsigned char ch)
+{
+       if (ch >= 'a' && ch <= 'f')
+               return ch - 'a' + 10;
+       if (ch >= '0' && ch <= '9')
+               return ch - '0';
+       if (ch >= 'A' && ch <= 'F')
+               return ch - 'A' + 10;
+       return 0;       /* No value */
+}
+
+void parse_debug(char *options)
+{
+       for (; *options; options++) {
+               switch (*options) {
+               case 'g':
+                       print_g = 1;
+                       break;
+               case 's':
+                       print_s = 1;
+                       break;
+               case 't':
+                       print_t = 1;
+                       break;
+               case 'G':
+                       print_G = 1;
+                       break;
+               case 'S':
+                       print_S = 1;
+                       break;
+               case 'T':
+                       print_T = 1;
+                       break;
+               default:
+                       pr_err("Invalid 'D' sub-option\n");
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+/*
+ * String input of a baud rate.  Convert to an int, make sure it is valid.
+ */
+void parse_baud(char *rate)
+{
+       int baudrate;
+
+       baudrate = strtol(rate, NULL, 10);
+
+       /*
+        * 9600 or better (an artificial limit)
+        *
+        * When modifying list of cases, update usage() to match.
+        */
+       switch (baudrate) {
+       case 9600:
+               new_baudrate = B9600;
+               break;
+       case 19200:
+               new_baudrate = B19200;
+               break;
+       case 38400:
+               new_baudrate = B38400;
+               break;
+       case 57600:
+               new_baudrate = B57600;
+               break;
+       case 115200:
+               new_baudrate = B115200;
+               break;
+       case 230400:
+               new_baudrate = B230400;
+               break;
+       default:
+               pr_err("Invalid baud rate given\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+void
+usage(void)
+{
+       /*
+        * Output should fit in 79 columns:
+        *
+        *              1         2         3         4         5         6     
    7         \n");
+        *     
12345678901234567890123456789012345678901234567890123456789012345678901234567890
+        */
+
+       pr_err("\n");
+       pr_err("Usage: kdmx [options]\n");
+       pr_err("\n");
+       pr_err("    -?       Print this message\n");
+       pr_err("    -b rate  Set serial port baud rate. default: 9600\n");
+       pr_err("               9600, 19200, 38400, 57600, 115200, 230400\n");
+       pr_err("    -d       Enable debug messages to stderr\n");
+       pr_err("    -Dx      Enable debug print of data stream(s) to stderr\n");
+       pr_err("               Any number of values for 'x' allowed\n");
+       pr_err("               x == lower case is from host   to target\n");
+       pr_err("               x == upper case is from target to host\n");
+       pr_err("                 g  from gdb\n");
+       pr_err("                 s  to serial port\n");
+       pr_err("                 t  from terminal emulator (eg minicom)\n");
+       pr_err("                 G  to gdb\n");
+       pr_err("                 S  from serial port\n");
+       pr_err("                 T  to terminal emulator (eg minicom)\n");
+       pr_err("    -h       Print this message\n");
+       pr_err("    -n       Allow terminal emulator to send null (\\0) 
characters\n");
+       pr_err("    -p port  Serial port path. default: %s\n", DEFAULT_SERIAL);
+       pr_err("    -v       Print version\n");
+       pr_err("\n");
+       pr_err("  Console multiplexor for kgdb Linux kernel debugger.\n");
+       pr_err("  Splits the console traffic to/from a serial port between 
two\n");
+       pr_err("  pseudo-terminals.  One pty connects to a terminal emulator, 
such as\n");
+       pr_err("  minicom.  The second pty connects to gdb. The names of the 
ptys are\n");
+       pr_err("  reported when this program is executed.\n");
+       pr_err("\n");
+       pr_err("  To exit from kdmx, kill the program or issue a control-c.\n");
+       pr_err("\n");
+       pr_err("\n");
+       pr_err("  KNOWN ISSUES:\n");
+       pr_err("\n");
+       pr_err("  The intended user of this program is an advanced kernel 
debugger, that is\n");
+       pr_err("  a 'wizard'.  With this intended user, there are some 'sharp 
edges' that\n");
+       pr_err("  have not been removed from kdmx -- use this program with 
caution.\n");
+       pr_err("\n");
+       pr_err("  kdmx:\n");
+       pr_err("\n");
+       pr_err("    - The BREAK signal can not be sent from the terminal 
emulator to kdmx.\n");
+       pr_err("      Workaround: Enter '~B' in the terminal emulator, 
immediately following\n");
+       pr_err("      a carriage return.  kdmx will strip the '~B' from the 
input stream and\n");
+       pr_err("      replace it with a BREAK signal on the serial port.\n");
+       pr_err("\n");
+       pr_err("  target system:\n");
+       pr_err("\n");
+       pr_err("    - Usage:\n");
+       pr_err("        Trigger the connect sequence on the target before 
trying to connect\n");
+       pr_err("        from gdb.  Methods to trigger include:\n");
+       pr_err("          1) send a magic sysrq command via the proc file 
system:\n");
+       pr_err("             echo g > /proc/sysrq-trigger\n");
+       pr_err("          2) send a magic sysrq command via a BREAK 
sequence:\n");
+       pr_err("             '~Bg'\n");
+       pr_err("          3) via the boot command line.  Add 'kgdbwait' to the 
command line.\n");
+       pr_err("\n");
+       pr_err("  gdb:\n");
+       pr_err("\n");
+       pr_err("    - Usage:\n");
+       pr_err("        After triggering the connect sequence on the target, 
connect from\n");
+       pr_err("        gdb.  For example:\n");
+       pr_err("        (gdb) target remote /dev/pts/30\n");
+       pr_err("\n");
+       pr_err("  Minicom:\n");
+       pr_err("\n");
+       pr_err("    - If minicom is connected to kdmx when kdmx is killed, then 
minicom will\n");
+       pr_err("      repeatedly attempt to reopen the slave pty that it was 
connected to.\n");
+       pr_err("      If a new terminal emulator (such as gnome-terminal, xfce 
Terminal, or\n");
+       pr_err("      Konsole) is executed during this time, it is likely to 
create a new\n");
+       pr_err("      slave pty with the same name as the slave pty that 
minicom is attempting\n");
+       pr_err("      to reopen.  Both the terminal emulator and minicom will 
open the same\n");
+       pr_err("      slave tty, with the result that some output will go to 
minicom and some\n");
+       pr_err("      will go to the terminal emulator.  The terminal emulator 
will appear\n");
+       pr_err("      to randomly lose characters.\n");
+       pr_err("      Workaround:  terminate minicom before killing kdmx.\n");
+       pr_err("      [minicom version 2.5]\n");
+       pr_err("\n");
+       pr_err("    - If minicom is connected to kdmx when kdmx is killed, then 
minicom will\n");
+       pr_err("      not respond to 'CTRL-A Z', which is a normal way to quit 
minicom.\n");
+       pr_err("      Workaround: terminate minicom before killing kdmx.\n");
+       pr_err("      [minicom version 2.5]\n");
+       pr_err("\n");
+       pr_err("  Debugging the serial connection:\n");
+       pr_err("\n");
+       pr_err("    - It may to useful to view the traffic between kdmx and the 
serial port.\n");
+       pr_err("      An example kdmx invocation to enable this is:\n");
+       pr_err("        kdmx -DsS -d -p/dev/ttyUSB0 -b115200  2>kdmx_debug\n");
+       pr_err("      Then in another terminal window:\n");
+       pr_err("        tail -f kdmx_debug\n");
+       pr_err("      '-DsS' enables debug print of the data to and from the 
serial port\n");
+       pr_err("      '2>FILE' redirects the debug data to the FILE.\n");
+       pr_err("\n");
+}
+
+
+int debug_opened = 0;          /* Has GDB opened up yet? */
+int response_pending = 0;      /* waiting for an ACK / NAK ? */
+
+void
+handle_gdb(void)
+{
+       char rcv;
+       int ret_errno;
+       int old_gdb_fd;
+       int ret;
+       char *name;
+       char *old_name;
+
+       debug_opened = 1;
+       response_pending = 1;
+
+       rcv = do_read(gdb_fd, &ret_errno);
+       if (rcv == -1) {
+
+               /*
+                * <ctrl>C in gdb or the gdb kill command will result in
+                * reads of the gdb pty returning errno == EIO.
+                *
+                * The gdb session for the two cases look like:
+                *
+                *    ^C
+                *    ^CInterrupted while waiting for the program.
+                *    Give up (and stop debugging it)? (y or n) y
+                *    (gdb)
+                *
+                *    (gdb) k
+                *    Kill the program being debugged? (y or n) y
+                *    (gdb)
+                *
+                */
+
+               if (ret_errno != EIO) {
+                       pr_err("read() of gdb pty returned unexpected errno 
%d\n",
+                              ret_errno);
+                       exit(EXIT_FAILURE);
+               }
+
+               pr_err("read() of gdb pty returned EIO,\n");
+               pr_err("  closing and re-opening gdb pty.\n");
+               pr_err("  EIO is expected if gdb closed the connection.\n");
+
+               /*
+                * The gdb / kgdb (gdb_server) conversation is done.  Do not
+                * divert '-' and '+' from the serial port to gdb.
+                */
+               response_pending = 0;
+
+               old_gdb_fd = gdb_fd;
+               old_name = ptsname(gdb_fd);
+
+               ret = close(gdb_fd);
+               if (ret)
+                       perror("close of gdb pty failed");
+
+               get_pty(&gdb_fd);
+               if (gdb_fd != old_gdb_fd) {
+                       perror("new gdb pty fd is different than old gdb pty, 
giving up\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               name = ptsname(gdb_fd);
+               pr_info("%s is slave pty for gdb\n",
+                       name ? name : "ptsname() ERROR");
+
+               if (strcmp(name, old_name))
+                       pr_err("WARNING: gdb slave pty path has changed\n");
+               else
+                       pr_err("gdb slave pty path is unchanged\n");
+
+       } else {
+               do_write(serial_fd, &rcv, sizeof(rcv), 0);
+       }
+}
+
+void
+reset_term(int ret_errno)
+{
+       int old_term_fd;
+       int ret;
+       char *name;
+       char *old_name;
+
+       /*
+        * Terminating minicom will result in reads of
+        * the terminal emulator pty returning errno == EIO.
+        */
+
+       if (ret_errno != EIO) {
+               pr_err("read() of terminal emulator pty returned unexpected 
errno %d\n",
+                      ret_errno);
+               exit(EXIT_FAILURE);
+       }
+
+       pr_err("read() of terminal emulator pty returned EIO,\n");
+       pr_err("  closing and re-opening terminal emulator pty.\n");
+       pr_err("  EIO is expected if terminal emulator exited.\n");
+
+       old_term_fd = term_fd;
+       old_name = ptsname(term_fd);
+
+       ret = close(term_fd);
+       if (ret)
+               perror("close of terminal emulator pty failed");
+
+       get_pty(&term_fd);
+       if (term_fd != old_term_fd) {
+               perror("new terminal emulator pty fd is different than old 
terminal emulator pty, giving up\n");
+               exit(EXIT_FAILURE);
+       }
+
+       name = ptsname(term_fd);
+       pr_info("%s is slave pty for for terminal emulator\n",
+               name ? name : "ptsname() ERROR");
+
+       if (strcmp(name, old_name))
+               pr_err("WARNING: terminal emulator slave pty path has 
changed\n");
+       else
+               pr_err("terminal emulator slave pty path is unchanged\n");
+}
+
+void
+handle_serial(void)
+{
+       int buf_pos;
+       int sel_ret;
+       int ret_errno;
+       int seen_hash;
+       fd_set readfds;
+       char buf[BUFMAX + 1];
+       unsigned char run_csum;
+       unsigned char rcv_csum;
+       char rcv;
+
+       memset(buf, 0, sizeof(buf));
+
+       rcv = do_read(serial_fd, &ret_errno);
+       if (rcv == -1) {
+               pr_err("read() of serial port returned unexpected errno %d\n",
+                      ret_errno);
+               exit(EXIT_FAILURE);
+       }
+
+       /* response to a GDB packet? */
+       if (response_pending && (rcv == '+' || rcv == '-')) {
+               response_pending = 0;
+               /* Write it out */
+               do_write(gdb_fd, &rcv, sizeof(rcv), 0);
+
+       } else if (rcv == '$') {
+
+               /*
+                * Both console and KGDB traffic are on the same serial
+                * port. If get a '$' from the the serial port, it may be
+                * the start of a packet to gdb, stop checking whether the
+                * console pty is readable until finished processing the
+                * GDB packet.
+                *
+                * If the buffer gets to BUFMAX, assume that it is not a
+                * GDB packet.
+                */
+               buf[0] = '$';   /* already received */
+               buf_pos = 1;
+
+               run_csum = 0;
+               seen_hash = 0;
+               while (1) {
+                       struct timeval tv;
+                       /*
+                        * If don't get anything for 0.07 seconds, assume
+                        * it's not a gdb packet.
+                        */
+                       tv.tv_sec = 0;
+                       tv.tv_usec = 70000;
+
+                       FD_ZERO(&readfds);
+                       FD_SET(serial_fd, &readfds);
+
+                       sel_ret = select(serial_fd + 1, &readfds, NULL, NULL, 
&tv);
+                       if (sel_ret == 0) {
+                               /* Timeout, write buffer to terminal emulator */
+                               do_write(term_fd, buf, buf_pos, 0);
+                               break;
+                       } else if (sel_ret == -1) {
+                               die("select");
+                       }
+
+                       /*
+                        * If have already read BUFMAX characters,
+                        * assume it's not a gdb packet.
+                        */
+                       if (buf_pos == BUFMAX) {
+                               do_write(term_fd, buf, buf_pos, 0);
+                               break;
+                       }
+
+                       /* read one char */
+                       rcv = do_read(serial_fd, &ret_errno);
+                       if (rcv == -1) {
+                               pr_err("read() of serial port returned 
unexpected errno %d\n",
+                                      ret_errno);
+                               exit(EXIT_FAILURE);
+                       }
+                       buf[buf_pos++] = rcv;
+                       if (rcv == '#') {
+                               seen_hash = 1;
+                       } else if (!seen_hash) {
+                               run_csum += rcv;
+                       } else if (seen_hash == 1) {
+                               rcv_csum = hex(rcv) << 4;
+                               seen_hash = 2;
+                       } else if (seen_hash == 2) {
+                               rcv_csum |= hex(rcv);
+                               seen_hash = 3;
+                       }
+
+                       if (seen_hash == 3) {
+                               /* packet completed */
+                               run_csum %= 256;
+                               if (rcv_csum == run_csum) {
+                                       if (debug_opened)
+                                               do_write(gdb_fd, buf, buf_pos, 
0);
+                               } else {
+                                       /* write corrupt packet to terminal */
+                                       do_write(term_fd, buf, buf_pos, 0);
+                               }
+                               seen_hash = 0;
+                               break;
+                       }
+               }
+
+       } else
+               /* Just pass this along to the console */
+               do_write(term_fd, &rcv, sizeof(rcv), 0);
+}
+
+void
+handle_term(void)
+{
+       static int in_escape;
+       static char last_cons_rcv;
+
+       int ret_errno;
+       int drop_char = 0;
+       char escape_char = '~';         /* using same value as ssh */
+       char rcv;
+
+       rcv = do_read(term_fd, &ret_errno);
+       if (rcv == -1) {
+               reset_term(ret_errno);
+               return;
+       }
+
+       if (in_escape) {
+               in_escape = 0;
+               drop_char = 1;
+
+               if (rcv == 'B') {
+                       do_write(serial_fd, NULL, 0, DW_FLAG_BREAK);
+               } else {
+                       pr_err("invalid escape character: 0x%2x '%c'\n", rcv,
+                              (rcv >= 0x20) && (rcv <=  0x7e) ? rcv : '.');
+               }
+       }
+
+       if ((last_cons_rcv == '\r') && (rcv == escape_char)) {
+               in_escape = 1;
+               drop_char = 1;
+       }
+
+       last_cons_rcv = rcv;
+
+       if (drop_char)
+               return;
+
+       if ((rcv != 0) || passthru_null_from_term)
+               do_write(serial_fd, &rcv, sizeof(rcv), 0);
+}
+
+int
+main(int argc, char **argv)
+{
+       int select_nfds;
+       struct termios termios;
+       char serial_port_path[SERIAL_PORT_PATHLEN + 1];
+       char *name;
+       fd_set readfds;
+
+       /* default serial port */
+       memset(serial_port_path, 0, sizeof(serial_port_path));
+       strcpy(serial_port_path, DEFAULT_SERIAL);
+
+       /* parse options */
+       while (1) {
+               int opt;
+
+               optopt = '?';
+
+               opt = getopt(argc, argv, "?b:dhl:np:vD:");
+
+               if (opt == -1)
+                       break;
+
+               switch (opt) {
+
+               case 'b':
+                       parse_baud(optarg);
+                       break;
+
+               case 'd':
+                       debug = 1;
+                       break;
+
+               case 'D':
+                       parse_debug(optarg);
+                       break;
+
+               /* case 'h':  see "default:" */
+
+               case 'n':
+                       passthru_null_from_term = 1;
+                       break;
+
+               case 'p':
+                       if (strlen(optarg) > SERIAL_PORT_PATHLEN) {
+                               pr_err("Path length for serial port too 
long\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       strcpy(serial_port_path, optarg);
+                       break;
+
+               case 'v':
+                       pr_err("kdmx %s\n", VUFX);
+                       exit(EXIT_SUCCESS);
+                       break;
+
+               case '?':
+                       if (optopt != 0) {
+                               /*
+                                * getopt() reports error as opt == '?' and
+                                * optopt == the unknown argument or argument
+                                * with bad value
+                                */
+                               pr_err("Use the '-h' argument to print usage 
information\n");
+                               exit(EXIT_FAILURE);
+                       } else {
+                               usage();
+                               exit(EXIT_SUCCESS);
+                       }
+                       break;
+
+               case 'h':
+                       usage();
+                       exit(EXIT_SUCCESS);
+                       break;
+
+               default:
+                       /*
+                        * getopt() reports error as opt == '?'
+                        * Should only get here if option added to
+                        * getopt(,, opstring) but not added to this case.
+                        */
+                       pr_err("Use the '-h' argument to print usage 
information\n");
+                       exit(EXIT_SUCCESS);
+                       break;
+               }
+
+       }
+
+       /* print stream label only if multiple streams are printed */
+       print_label = print_g + print_s + print_t + print_G + print_S + print_T;
+       if (print_label < 2)
+               print_label = 0;
+
+       pr_debug("serial port: %s\n", serial_port_path);
+
+       serial_fd = open(serial_port_path, O_RDWR|O_NDELAY|O_NOCTTY);
+       if (serial_fd == -1) {
+               char msg[SERIAL_PORT_PATHLEN + strlen("open of ") + 1];
+               memset(msg, 0, sizeof(msg));
+               sprintf(msg, "open of %s", serial_port_path);
+               die(msg);
+       }
+
+       switch (new_baudrate) {
+       case B9600:
+               pr_debug("Initalizing the serial port to 9600 8n1\n");
+               break;
+       case B19200:
+               pr_debug("Initalizing the serial port to 19200 8n1\n");
+               break;
+       case B38400:
+               pr_debug("Initalizing the serial port to 38400 8n1\n");
+               break;
+       case B57600:
+               pr_debug("Initalizing the serial port to 57600 8n1\n");
+               break;
+       case B115200:
+               pr_debug("Initalizing the serial port to 115200 8n1\n");
+               break;
+       case B230400:
+               pr_debug("Initalizing the serial port to 230400 8n1\n");
+               break;
+       default:
+               /*
+                * parse_baud() was updated without updating this switch
+                */
+               pr_debug("Initalizing the serial port to ???\n");
+               break;
+       }
+
+       /* Get the current infos on the serial port */
+       if (tcgetattr(serial_fd, &termios))
+               die("tcgetattr() of serial port");
+
+       /* Modify the speed */
+       cfsetispeed(&termios, new_baudrate);
+       cfsetospeed(&termios, new_baudrate);
+
+       termios.c_iflag = IGNBRK;
+       termios.c_iflag |= IXON | IXOFF | IXANY;
+
+       termios.c_oflag = 0;
+
+       termios.c_cflag &= ~(PARENB|CSTOPB);
+       termios.c_cflag &= ~(CSIZE);
+       termios.c_cflag |= CS8 | CLOCAL | CREAD;
+
+       termios.c_lflag = 0;
+
+       termios.c_cc[VMIN] = 1;
+       termios.c_cc[VTIME] = 5;
+
+       if (tcsetattr(serial_fd, TCSANOW, &termios))
+               die("tcsetattr");
+
+       get_pty(&term_fd);
+       get_pty(&gdb_fd);
+       select_nfds = gdb_fd + 1;
+
+       name = ptsname(term_fd);
+       pr_info("%s is slave pty for for terminal emulator\n",
+               name ? name : "ptsname() ERROR");
+
+       name = ptsname(gdb_fd);
+       pr_info("%s is slave pty for gdb\n", name ? name : "ptsname() ERROR");
+
+       while (1) {
+
+               FD_ZERO(&readfds);
+
+               FD_SET(gdb_fd,    &readfds);
+               FD_SET(serial_fd, &readfds);
+               FD_SET(term_fd,   &readfds);
+
+               if (select(select_nfds, &readfds, NULL, NULL, NULL) == -1)
+                       die("select");
+
+               /* Order of handling readable descriptors matters */
+
+               if (FD_ISSET(serial_fd, &readfds))
+
+                       handle_serial();
+
+               else if (FD_ISSET(gdb_fd, &readfds))
+
+                       handle_gdb();
+
+               else if (FD_ISSET(term_fd, &readfds))
+
+                       handle_term();
+       }
+}

------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
_______________________________________________
Kgdb-bugreport mailing list
Kgdb-bugreport@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kgdb-bugreport

Reply via email to