use host serial port


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1609 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/Changelog b/Changelog
index 6aff139..32c417b 100644
--- a/Changelog
+++ b/Changelog
@@ -7,7 +7,8 @@
   - new audio options: '-soundhw' and '-audio-help' (malc)
   - ES1370 PCI audio device (malc)
   - Initial USB support
-   
+  - Linux host serial port access
+
 version 0.7.2:
   
   - x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit)
diff --git a/hw/serial.c b/hw/serial.c
index ac04e65..75be4de 100644
--- a/hw/serial.c
+++ b/hw/serial.c
@@ -85,6 +85,7 @@
     int thr_ipending;
     int irq;
     CharDriverState *chr;
+    int last_break_enable;
 };
 
 static void serial_update_irq(SerialState *s)
@@ -103,6 +104,32 @@
     }
 }
 
+static void serial_update_parameters(SerialState *s)
+{
+    int speed, parity, data_bits, stop_bits;
+
+    if (s->lcr & 0x08) {
+        if (s->lcr & 0x10)
+            parity = 'E';
+        else
+            parity = 'O';
+    } else {
+            parity = 'N';
+    }
+    if (s->lcr & 0x04) 
+        stop_bits = 2;
+    else
+        stop_bits = 1;
+    data_bits = (s->lcr & 0x03) + 5;
+    if (s->divider == 0)
+        return;
+    speed = 115200 / s->divider;
+#if 0    
+    printf("speed=%d parity=%c data=%d stop=%d\n", 
+           speed, parity, data_bits, stop_bits);
+#endif
+}
+
 static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
     SerialState *s = opaque;
@@ -117,6 +144,7 @@
     case 0:
         if (s->lcr & UART_LCR_DLAB) {
             s->divider = (s->divider & 0xff00) | val;
+            serial_update_parameters(s);
         } else {
             s->thr_ipending = 0;
             s->lsr &= ~UART_LSR_THRE;
@@ -132,6 +160,7 @@
     case 1:
         if (s->lcr & UART_LCR_DLAB) {
             s->divider = (s->divider & 0x00ff) | (val << 8);
+            serial_update_parameters(s);
         } else {
             s->ier = val & 0x0f;
             if (s->lsr & UART_LSR_THRE) {
@@ -143,7 +172,16 @@
     case 2:
         break;
     case 3:
-        s->lcr = val;
+        {
+            int break_enable;
+            s->lcr = val;
+            serial_update_parameters(s);
+            break_enable = (val >> 6) & 1;
+            if (break_enable != s->last_break_enable) {
+                s->last_break_enable = break_enable;
+                qemu_chr_set_serial_break(s, break_enable);
+            }
+        }
         break;
     case 4:
         s->mcr = val & 0x1f;
diff --git a/qemu-doc.texi b/qemu-doc.texi
index df5f4e3..253484f 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -363,8 +363,15 @@
 [Linux only] Pseudo TTY (a new PTY is automatically allocated)
 @item null
 void device
+@item /dev/XXX
+[Linux only]Use host tty, e.g. @file{/dev/ttyS0}. The host serial port
+parameters are set according to the emulated ones.
+@item file:filename
+Write output to filename. No character can be read.
 @item stdio
 [Unix only] standard input/output
+@item pipe:filename
+[Unix only] name pipe @var{filename}
 @end table
 The default device is @code{vc} in graphical mode and @code{stdio} in
 non graphical mode.
diff --git a/vl.c b/vl.c
index b1cd655..75ec23b 100644
--- a/vl.c
+++ b/vl.c
@@ -1013,6 +1013,21 @@
     return s->chr_write(s, buf, len);
 }
 
+void qemu_chr_set_serial_parameters(CharDriverState *s,
+                                    int speed, int parity,
+                                    int data_bits, int stop_bits)
+{
+    if (s->chr_set_serial_parameters)
+        s->chr_set_serial_parameters(s, speed, parity, data_bits, stop_bits);
+}
+
+void qemu_chr_set_serial_break(CharDriverState *s, int enable)
+{
+    if (s->chr_set_serial_break)
+        s->chr_set_serial_break(s, enable);
+}
+
+
 void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
 {
     char buf[4096];
@@ -1111,12 +1126,14 @@
 {
     FDCharDriver *s = chr->opaque;
 
-    if (nographic && s->fd_in == 0) {
-        s->fd_can_read = fd_can_read;
-        s->fd_read = fd_read;
-        s->fd_opaque = opaque;
-    } else {
-        qemu_add_fd_read_handler(s->fd_in, fd_can_read, fd_read, opaque);
+    if (s->fd_in >= 0) {
+        if (nographic && s->fd_in == 0) {
+            s->fd_can_read = fd_can_read;
+            s->fd_read = fd_read;
+            s->fd_opaque = opaque;
+        } else {
+            qemu_add_fd_read_handler(s->fd_in, fd_can_read, fd_read, opaque);
+        }
     }
 }
 
@@ -1142,6 +1159,27 @@
     return chr;
 }
 
+CharDriverState *qemu_chr_open_file_out(const char *file_out)
+{
+    int fd_out;
+
+    fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY);
+    if (fd_out < 0)
+        return NULL;
+    return qemu_chr_open_fd(-1, fd_out);
+}
+
+CharDriverState *qemu_chr_open_pipe(const char *filename)
+{
+    int fd;
+
+    fd = open(filename, O_RDWR | O_BINARY);
+    if (fd < 0)
+        return NULL;
+    return qemu_chr_open_fd(fd, fd);
+}
+
+
 /* for STDIO, we handle the case where several clients use it
    (nographic mode) */
 
@@ -1334,6 +1372,127 @@
     fprintf(stderr, "char device redirected to %s\n", slave_name);
     return qemu_chr_open_fd(master_fd, master_fd);
 }
+
+static void tty_serial_init(int fd, int speed, 
+                            int parity, int data_bits, int stop_bits)
+{
+    struct termios tty;
+    speed_t spd;
+
+    tcgetattr (0, &tty);
+
+    switch(speed) {
+    case 50:
+        spd = B50;
+        break;
+    case 75:
+        spd = B75;
+        break;
+    case 300:
+        spd = B300;
+        break;
+    case 600:
+        spd = B600;
+        break;
+    case 1200:
+        spd = B1200;
+        break;
+    case 2400:
+        spd = B2400;
+        break;
+    case 4800:
+        spd = B4800;
+        break;
+    case 9600:
+        spd = B9600;
+        break;
+    case 19200:
+        spd = B19200;
+        break;
+    case 38400:
+        spd = B38400;
+        break;
+    case 57600:
+        spd = B57600;
+        break;
+    default:
+    case 115200:
+        spd = B115200;
+        break;
+    }
+
+    cfsetispeed(&tty, spd);
+    cfsetospeed(&tty, spd);
+
+    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+                          |INLCR|IGNCR|ICRNL|IXON);
+    tty.c_oflag |= OPOST;
+    tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
+    tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS);
+    switch(data_bits) {
+    default:
+    case 8:
+        tty.c_cflag |= CS8;
+        break;
+    case 7:
+        tty.c_cflag |= CS7;
+        break;
+    case 6:
+        tty.c_cflag |= CS6;
+        break;
+    case 5:
+        tty.c_cflag |= CS5;
+        break;
+    }
+    switch(parity) {
+    default:
+    case 'N':
+        break;
+    case 'E':
+        tty.c_cflag |= PARENB;
+        break;
+    case 'O':
+        tty.c_cflag |= PARENB | PARODD;
+        break;
+    }
+    
+    tcsetattr (fd, TCSANOW, &tty);
+}
+
+static void tty_set_serial_parameters(CharDriverState *chr,
+                                      int speed, int parity,
+                                      int data_bits, int stop_bits)
+{
+    FDCharDriver *s = chr->opaque;
+    tty_serial_init(s->fd_in, speed, parity, data_bits, stop_bits);
+}
+
+static void tty_set_serial_break(CharDriverState *chr, int enable)
+{
+    FDCharDriver *s = chr->opaque;
+    /* XXX: find a better solution */
+    if (enable)
+        tcsendbreak(s->fd_in, 1);
+}
+
+CharDriverState *qemu_chr_open_tty(const char *filename)
+{
+    CharDriverState *chr;
+    int fd;
+
+    fd = open(filename, O_RDWR);
+    if (fd < 0)
+        return NULL;
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+    tty_serial_init(fd, 115200, 'N', 8, 1);
+    chr = qemu_chr_open_fd(fd, fd);
+    if (!chr)
+        return NULL;
+    chr->chr_set_serial_parameters = tty_set_serial_parameters;
+    chr->chr_set_serial_break = tty_set_serial_break;
+    return chr;
+}
+
 #else
 CharDriverState *qemu_chr_open_pty(void)
 {
@@ -1345,10 +1504,15 @@
 
 CharDriverState *qemu_chr_open(const char *filename)
 {
+    const char *p;
     if (!strcmp(filename, "vc")) {
         return text_console_init(&display_state);
     } else if (!strcmp(filename, "null")) {
         return qemu_chr_open_null();
+    } else if (strstart(filename, "file:", &p)) {
+        return qemu_chr_open_file_out(p);
+    } else if (strstart(filename, "pipe:", &p)) {
+        return qemu_chr_open_pipe(p);
     } else 
 #ifndef _WIN32
     if (!strcmp(filename, "pty")) {
@@ -1357,6 +1521,11 @@
         return qemu_chr_open_stdio();
     } else 
 #endif
+#if defined(__linux__)
+    if (strstart(filename, "/dev/", NULL)) {
+        return qemu_chr_open_tty(filename);
+    } else 
+#endif
     {
         return NULL;
     }
@@ -3010,7 +3179,6 @@
            "-no-code-copy   disable code copy acceleration\n"
 #endif
 #ifdef TARGET_I386
-           "-isa            simulate an ISA-only system (default is PCI system)\n"
            "-std-vga        simulate a standard VGA card with VESA Bochs Extensions\n"
            "                (default is CL-GD5446 PCI VGA)\n"
 #endif
diff --git a/vl.h b/vl.h
index d31da19..40de851 100644
--- a/vl.h
+++ b/vl.h
@@ -207,6 +207,10 @@
     void (*chr_add_read_handler)(struct CharDriverState *s, 
                                  IOCanRWHandler *fd_can_read, 
                                  IOReadHandler *fd_read, void *opaque);
+    void (*chr_set_serial_parameters)(struct CharDriverState *s,
+                                      int speed, int parity,
+                                      int data_bits, int stop_bits);
+    void (*chr_set_serial_break)(struct CharDriverState *s, int enable);
     IOEventHandler *chr_event;
     void (*chr_send_event)(struct CharDriverState *chr, int event);
     void *opaque;
@@ -219,7 +223,11 @@
                                IOCanRWHandler *fd_can_read, 
                                IOReadHandler *fd_read, void *opaque);
 void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event);
-                               
+void qemu_chr_set_serial_parameters(CharDriverState *s,
+                                    int speed, int parity,
+                                    int data_bits, int stop_bits);
+void qemu_chr_set_serial_break(CharDriverState *s, int enable);
+
 /* consoles */
 
 typedef struct DisplayState DisplayState;