diff options
-rw-r--r-- | Changelog | 3 | ||||
-rw-r--r-- | hw/serial.c | 40 | ||||
-rw-r--r-- | qemu-doc.texi | 7 | ||||
-rw-r--r-- | vl.c | 182 | ||||
-rw-r--r-- | vl.h | 10 |
5 files changed, 232 insertions, 10 deletions
@@ -7,7 +7,8 @@ version 0.7.3: - 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 @@ struct SerialState { 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_irq(SerialState *s) } } +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 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) 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 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) 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 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) 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 @@ Virtual console [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. @@ -1013,6 +1013,21 @@ int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len) 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 @@ static void fd_chr_add_read_handler(CharDriverState *chr, { 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 @@ CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) 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 @@ CharDriverState *qemu_chr_open_pty(void) 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_pty(void) 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 @@ CharDriverState *qemu_chr_open(const char *filename) 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 @@ void help(void) "-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 @@ -207,6 +207,10 @@ typedef struct CharDriverState { 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 @@ void qemu_chr_add_read_handler(CharDriverState *s, 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; |