This patch adds support for GDB packets and serial console demuxing. To use
it start flterm with --gdb-passthrough. Then start gdb and run the
following commands:
  set remote interrupt-on-connect on
  target remote <pseudo terminal printed by flterm>

Note: there is _no_ 'set remote interrupt-sequence BREAK'. flterm takes
care of converting Ctrl+C (ETX) to a BREAK.
---
 tools/flterm.c |  217 +++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 192 insertions(+), 25 deletions(-)

diff --git a/tools/flterm.c b/tools/flterm.c
index 5271a73..6940902 100644
--- a/tools/flterm.c
+++ b/tools/flterm.c
@@ -1,6 +1,8 @@
 /*
  * Milkymist VJ SoC
- * Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
+ * Copyright (C) 2011 Michael Walle
+ * Copyright (C) 2004 MontaVista Software, Inc
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -36,6 +38,8 @@
 #define DEFAULT_CMDLINEADR     (0x41000000)
 #define DEFAULT_INITRDADR      (0x41002000)
 
+#define GDBBUFLEN 1000
+
 unsigned int crc16_table[256] = {
        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
        0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
@@ -71,6 +75,8 @@ unsigned int crc16_table[256] = {
        0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
 };
 
+static int debug = 0;
+
 static unsigned short crc16(const void *_buffer, int len)
 {
        const unsigned char *buffer = (const unsigned char *)_buffer;
@@ -283,18 +289,104 @@ static void answer_magic(int serialfd,
        close(kernelfd);
 }
 
+static int hex(unsigned char c)
+{
+       if(c >= 'a' && c <= 'f') {
+               return c - 'a' + 10;
+       }
+       if(c >= '0' && c <= '9') {
+               return c - '0';
+       }
+       if(c >= 'A' && c <= 'F') {
+               return c - 'A' + 10;
+       }
+       return 0;
+}
+
+/*
+ * This is taken from kdmx2.
+ * Author: Tom Rini <[email protected]>
+ */
+static void gdb_process_packet(int infd, int outfd, int altfd)
+{
+       /* gdb packet handling */
+       char gdbbuf[GDBBUFLEN + 1];
+       int pos = 0;
+       unsigned char runcksum = 0;
+       unsigned char recvcksum = 0;
+       struct pollfd fds;
+       char c;
+       int seen_hash = 0;
+
+       fds.fd = infd;
+       fds.events = POLLIN;
+
+       memset(gdbbuf, 0, sizeof(gdbbuf));
+       gdbbuf[0] = '$';
+       pos++;
+
+       while (1) {
+               fds.revents = 0;
+               if(poll(&fds, 1, 100) == 0) {
+                       /* timeout */
+                       if (altfd != -1) {
+                               write(altfd, gdbbuf, pos);
+                       }
+                       break;
+               }
+               if (pos == GDBBUFLEN) {
+                       if (altfd != -1) {
+                               write(altfd, gdbbuf, pos);
+                       }
+                       break;
+               }
+               read(infd, &c, 1);
+               gdbbuf[pos++] = c;
+               if(c == '#') {
+                       seen_hash = 1;
+               } else if (seen_hash == 0) {
+                       runcksum += c;
+               } else if (seen_hash == 1) {
+                       recvcksum = hex(c) << 4;
+                       seen_hash = 2;
+               } else if (seen_hash == 2) {
+                       recvcksum |= hex(c);
+                       seen_hash = 3;
+               }
+
+               if (seen_hash == 3) {
+                       /* we're done */
+                       runcksum %= 256;
+                       if (recvcksum == runcksum) {
+                               if (debug) {
+                                       fprintf(stderr, "[GDB %s]\n", gdbbuf);
+                               }
+                               write(outfd, gdbbuf, pos);
+                       } else {
+                               if (altfd != -1) {
+                                       write(altfd, gdbbuf, pos);
+                               }
+                       }
+                       seen_hash = 0;
+                       break;
+               }
+       }
+}
+
 static void do_terminal(char *serial_port,
-       int doublerate,
+       int doublerate, int gdb_passthrough,
        const char *kernel_image, unsigned int kernel_address,
        const char *cmdline, unsigned int cmdline_address,
        const char *initrd_image, unsigned int initrd_address)
 {
        int serialfd;
+       int gdbfd = -1;
        struct termios my_termios;
        char c;
        int recognized;
-       struct pollfd fds[2];
+       struct pollfd fds[3];
        int flags;
+       int rsp_pending = 0;
        
        /* Open and configure the serial port */
        serialfd = open(serial_port, O_RDWR|O_NOCTTY);
@@ -302,7 +394,7 @@ static void do_terminal(char *serial_port,
                perror("Unable to open serial port");
                return;
        }
-       
+
        /* Thanks to Julien Schmitt (GTKTerm) for figuring out the correct 
parameters
         * to put into that weird struct.
         */
@@ -319,7 +411,7 @@ static void do_terminal(char *serial_port,
        tcsetattr(serialfd, TCSANOW, &my_termios);
        tcflush(serialfd, TCOFLUSH);
        tcflush(serialfd, TCIFLUSH);
-       
+
        /* Prepare the fdset for poll() */
        fds[0].fd = 0;
        fds[0].events = POLLIN;
@@ -329,47 +421,103 @@ static void do_terminal(char *serial_port,
        recognized = 0;
        flags = fcntl(serialfd, F_GETFL, 0);
        while(1) {
+               if (gdbfd == -1 && gdb_passthrough) {
+                       gdbfd = open("/dev/ptmx", O_RDWR);
+                       if(grantpt(gdbfd) != 0) {
+                               perror("grantpt()");
+                               return;
+                       }
+                       if(unlockpt(gdbfd) != 0) {
+                               perror("unlockpt()");
+                               return;
+                       }
+                       printf("[GDB passthrough] use %s as GDB remote 
device\n",
+                                       ptsname(gdbfd));
+                       fds[2].fd = gdbfd;
+                       fds[2].events = POLLIN;
+               }
+
                fds[0].revents = 0;
                fds[1].revents = 0;
-               
+               fds[2].revents = 0;
+
                /* poll() behaves strangely when the serial port descriptor is 
in
                 * blocking mode. So work around this.
                 */
                fcntl(serialfd, F_SETFL, flags|O_NONBLOCK);
-               if(poll(&fds[0], 2, -1) < 0) break;
+               if(poll(&fds[0], (gdbfd == -1) ? 2 : 3, -1) < 0) break;
                fcntl(serialfd, F_SETFL, flags);
-               
+
                if(fds[0].revents & POLLIN) {
-                       read(0, &c, 1);
+                       if (read(0, &c, 1) <= 0) break;
                        if(write(serialfd, &c, 1) <= 0) break;
                }
-               
+
+               if(fds[2].revents & POLLIN) {
+                       rsp_pending = 1;
+                       if (read(gdbfd, &c, 1) <= 0) break;
+                       if (c == '\03') {
+                               /* convert ETX to breaks */
+                               if (debug) {
+                                       fprintf(stderr, "[GDB BREAK]\n");
+                               }
+                               tcsendbreak(serialfd, 0);
+                       } else if (c == '$') {
+                               gdb_process_packet(gdbfd, serialfd, -1);
+                       } else if (c == '+' || c == '-') {
+                               write(serialfd, &c, 1);
+                       } else {
+                               fprintf(stderr, "Internal error (line %d)", 
__LINE__);
+                               exit(1);
+                       }
+               }
+
+               if(fds[2].revents & POLLHUP) {
+                       /* close and reopen new pair */
+                       close(gdbfd);
+                       gdbfd = -1;
+                       continue;
+               }
+
                if(fds[1].revents & POLLIN) {
                        if(read(serialfd, &c, 1) <= 0) break;
-                       write(0, &c, 1);
+                       if(gdbfd != -1 && rsp_pending && (c == '+' || c == 
'-')) {
+                               rsp_pending = 0;
+                               write(gdbfd, &c, 1);
+                       } else if (gdbfd != -1 && c == '$') {
+                               gdb_process_packet(serialfd, gdbfd, 0);
+                       } else {
+                               /* write to terminal */
+                               write(0, &c, 1);
                        
-                       if(c == sfl_magic_req[recognized]) {
-                               recognized++;
-                               if(recognized == SFL_MAGIC_LEN) {
-                                       /* We've got the magic string ! */
-                                       recognized = 0;
-                                       answer_magic(serialfd,
-                                               kernel_image, kernel_address,
-                                               cmdline, cmdline_address,
-                                               initrd_image, initrd_address);
+                               if(c == sfl_magic_req[recognized]) {
+                                       recognized++;
+                                       if(recognized == SFL_MAGIC_LEN) {
+                                               /* We've got the magic string ! 
*/
+                                               recognized = 0;
+                                               answer_magic(serialfd,
+                                                       kernel_image, 
kernel_address,
+                                                       cmdline, 
cmdline_address,
+                                                       initrd_image, 
initrd_address);
+                                       }
+                               } else {
+                                       if(c == sfl_magic_req[0]) recognized = 
1; else recognized = 0;
                                }
-                       } else {
-                               if(c == sfl_magic_req[0]) recognized = 1; else 
recognized = 0;
                        }
                }
        }
        
        close(serialfd);
+       if(gdbfd != -1) {
+               close(gdbfd);
+       }
 }
 
 enum {
        OPTION_PORT,
+       OPTION_GDB_PASSTHROUGH,
        OPTION_DOUBLERATE,
+       OPTION_DEBUG,
        OPTION_KERNEL,
        OPTION_KERNELADR,
        OPTION_CMDLINE,
@@ -385,6 +533,16 @@ static const struct option options[] = {
                .val = OPTION_PORT
        },
        {
+               .name = "gdb-passthrough",
+               .has_arg = 0,
+               .val = OPTION_GDB_PASSTHROUGH
+       },
+       {
+               .name = "debug",
+               .has_arg = 0,
+               .val = OPTION_DEBUG
+       },
+       {
                .name = "double-rate",
                .has_arg = 0,
                .val = OPTION_DOUBLERATE
@@ -433,7 +591,8 @@ static void print_usage()
        fprintf(stderr, "it under the terms of the GNU General Public License 
as published by\n");
        fprintf(stderr, "the Free Software Foundation, version 3 of the 
License.\n\n");
 
-       fprintf(stderr, "Usage: flterm --port <port> [--double-rate]\n");
+       fprintf(stderr, "Usage: flterm --port <port>\n");
+       fprintf(stderr, "              [--double-rate] [--gdb-passthrough] 
[--debug]\n");
        fprintf(stderr, "              --kernel <kernel_image> [--kernel-adr 
<address>]\n");
        fprintf(stderr, "              [--cmdline <cmdline> [--cmdline-adr 
<address>]]\n");
        fprintf(stderr, "              [--initrd <initrd_image> [--initrd-adr 
<address>]]\n\n");
@@ -448,6 +607,7 @@ int main(int argc, char *argv[])
        int opt;
        char *serial_port;
        int doublerate;
+       int gdb_passthrough;
        char *kernel_image;
        unsigned int kernel_address;
        char *cmdline;
@@ -460,6 +620,7 @@ int main(int argc, char *argv[])
        /* Fetch command line arguments */
        serial_port = NULL;
        doublerate = 0;
+       gdb_passthrough = 0;
        kernel_image = NULL;
        kernel_address = DEFAULT_KERNELADR;
        cmdline = NULL;
@@ -479,6 +640,12 @@ int main(int argc, char *argv[])
                        case OPTION_DOUBLERATE:
                                doublerate = 1;
                                break;
+                       case OPTION_DEBUG:
+                               debug = 1;
+                               break;
+                       case OPTION_GDB_PASSTHROUGH:
+                               gdb_passthrough = 1;
+                               break;
                        case OPTION_KERNEL:
                                free(kernel_image);
                                kernel_image = strdup(optarg);
@@ -519,9 +686,9 @@ int main(int argc, char *argv[])
        ntty = otty;
        ntty.c_lflag &= ~(ECHO | ICANON);
        tcsetattr(0, TCSANOW, &ntty);
-       
+
        /* Do the bulk of the work */
-       do_terminal(serial_port, doublerate,
+       do_terminal(serial_port, doublerate, gdb_passthrough,
                kernel_image, kernel_address,
                cmdline, cmdline_address,
                initrd_image, initrd_address);
-- 
1.7.2.3

_______________________________________________
http://lists.milkymist.org/listinfo.cgi/devel-milkymist.org
IRC: #milkymist@Freenode
Twitter: www.twitter.com/milkymistvj
Ideas? http://milkymist.uservoice.com

Reply via email to