Index: qemu/vl.c =================================================================== --- qemu.orig/vl.c +++ qemu/vl.c @@ -1063,9 +1063,11 @@ void qemu_chr_add_read_handler(CharDrive s->chr_add_read_handler(s, fd_can_read, fd_read, opaque); } -void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event) +void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event, + void *event_opaque) { s->chr_event = chr_event; + s->event_opaque = event_opaque; } static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) @@ -1091,6 +1093,173 @@ CharDriverState *qemu_chr_open_null(void return chr; } +/* MUX driver for serial I/O splitting */ +static int term_timestamps; +static int64_t term_timestamps_start; +#define MAX_MUX 2 +typedef struct { + IOCanRWHandler *fd_can_read[MAX_MUX]; + IOReadHandler *fd_read[MAX_MUX]; + void *ext_opaque[MAX_MUX]; + CharDriverState *drv; + int idx; + int mux_cnt; + int term_got_escape; + int max_size; +} MuxDriver; + + +static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + MuxDriver *d = chr->opaque; + len = d->drv->chr_write(d->drv, buf, len); + return len; +} + +static char *mux_help[] = { + "% h print this help\n\r", + "% x exit emulator\n\r", + "% s save disk data back to file (if -snapshot)\n\r", + "% t toggle console timestamps\n\r" + "% b send break (magic sysrq)\n\r", + "% c switch between console and monitor\n\r", + "% % sends %\n\r", + NULL +}; + +static int term_escape_char = 0x01; /* ctrl-a is used for escape */ +static void mux_print_help(CharDriverState *chr) +{ + int i, j; + char ebuf[15] = "Escape-Char"; + char cbuf[50] = "\n\r"; + + if (term_escape_char > 0 && term_escape_char < 26) { + sprintf(cbuf,"\n\r"); + sprintf(ebuf,"C-%c", term_escape_char - 1 + 'a'); + } else { + sprintf(cbuf,"\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r", term_escape_char); + } + chr->chr_write(chr, cbuf, strlen(cbuf)); + for (i = 0; mux_help[i] != NULL; i++) { + for (j=0; mux_help[i][j] != '\0'; j++) { + if (mux_help[i][j] == '%') + chr->chr_write(chr, ebuf, strlen(ebuf)); + else + chr->chr_write(chr, &mux_help[i][j], 1); + } + } +} + +static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) +{ + if (d->term_got_escape) { + d->term_got_escape = 0; + if (ch == term_escape_char) + goto send_char; + switch(ch) { + case '?': + case 'h': + mux_print_help(chr); + break; + case 'x': + exit(0); + break; + case 's': + { + int i; + for (i = 0; i < MAX_DISKS; i++) { + if (bs_table[i]) + bdrv_commit(bs_table[i]); + } + } + break; + case 'b': + if (chr->chr_event) + chr->chr_event(chr->event_opaque, CHR_EVENT_BREAK); + break; + case 'c': + /* Switch to the next registered device */ + d->idx++; + if (d->idx >= d->mux_cnt) + d->idx = 0; + break; + case 't': + term_timestamps = !term_timestamps; + term_timestamps_start = -1; + break; + } + } else if (ch == term_escape_char) { + d->term_got_escape = 1; + } else { + send_char: + return 1; + } + return 0; +} + +static int mux_chr_fd_can_read(void *opaque) +{ + CharDriverState *chr = opaque; + MuxDriver *d = chr->opaque; + return d->fd_can_read[d->idx](d->ext_opaque[d->idx]); +} + +static void mux_chr_fd_read(void *opaque, const uint8_t *buf, int size) +{ + CharDriverState *chr = opaque; + MuxDriver *d = chr->opaque; + int i; + for(i = 0; i < size; i++) + if (mux_proc_byte(chr, d, buf[i])) + d->fd_read[d->idx](d->ext_opaque[d->idx], &buf[i], 1); +} + +static void mux_chr_add_read_handler(CharDriverState *chr, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque) +{ + MuxDriver *d = chr->opaque; + + if (d->mux_cnt >= MAX_MUX) { + fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n"); + return; + } + d->ext_opaque[d->mux_cnt] = opaque; + d->fd_can_read[d->mux_cnt] = fd_can_read; + d->fd_read[d->mux_cnt] = fd_read; + /* Fix up the real driver with mux routines */ + if (d->drv->chr_add_read_handler && d->mux_cnt == 0) + d->drv->chr_add_read_handler(d->drv, + mux_chr_fd_can_read, + mux_chr_fd_read, chr); + d->idx = d->mux_cnt; + d->mux_cnt++; +} + +CharDriverState *qemu_chr_open_mux(CharDriverState *drv) +{ + CharDriverState *chr; + MuxDriver *d; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + d = qemu_mallocz(sizeof(MuxDriver)); + if (!d) { + free(chr); + return NULL; + } + + chr->opaque = d; + d->drv = drv; + d->idx = -1; + chr->chr_write = mux_chr_write; + chr->chr_add_read_handler = mux_chr_add_read_handler; + return chr; +} + + #ifdef _WIN32 static void socket_cleanup(void) @@ -1287,15 +1456,11 @@ CharDriverState *qemu_chr_open_pipe(cons /* for STDIO, we handle the case where several clients use it (nographic mode) */ -#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */ - #define TERM_FIFO_MAX_SIZE 1 static int term_got_escape, client_index; static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; static int term_fifo_size; -static int term_timestamps; -static int64_t term_timestamps_start; void term_print_help(void) { @@ -1315,6 +1480,8 @@ static void stdio_received_byte(int ch) { if (term_got_escape) { term_got_escape = 0; + if (ch == term_escape_char) + goto send_char; switch(ch) { case 'h': term_print_help(); @@ -1355,10 +1522,8 @@ static void stdio_received_byte(int ch) term_timestamps = !term_timestamps; term_timestamps_start = -1; break; - case TERM_ESCAPE: - goto send_char; } - } else if (ch == TERM_ESCAPE) { + } else if (ch == term_escape_char) { term_got_escape = 1; } else { send_char: @@ -2544,6 +2709,16 @@ CharDriverState *qemu_chr_open(const cha if (strstart(filename, "udp:", &p)) { return qemu_chr_open_udp(p); } else + if (strstart(filename, "mon:", &p)) { + CharDriverState *drv = qemu_chr_open(p); + if (drv) { + drv = qemu_chr_open_mux(drv); + monitor_init(drv, !nographic); + return drv; + } + printf("Unable to open driver: %s\n", p); + return 0; + } else #ifndef _WIN32 if (strstart(filename, "file:", &p)) { return qemu_chr_open_file_out(p); @@ -5285,6 +5460,7 @@ enum { QEMU_OPTION_cirrusvga, QEMU_OPTION_g, QEMU_OPTION_std_vga, + QEMU_OPTION_echr, QEMU_OPTION_monitor, QEMU_OPTION_serial, QEMU_OPTION_parallel, @@ -5360,6 +5536,7 @@ const QEMUOption qemu_options[] = { #endif { "localtime", 0, QEMU_OPTION_localtime }, { "std-vga", 0, QEMU_OPTION_std_vga }, + { "echr", 1, QEMU_OPTION_echr }, { "monitor", 1, QEMU_OPTION_monitor }, { "serial", 1, QEMU_OPTION_serial }, { "parallel", 1, QEMU_OPTION_parallel }, @@ -5946,6 +6123,14 @@ int main(int argc, char **argv) graphic_depth = depth; } break; + case QEMU_OPTION_echr: + { + char *r; + term_escape_char = strtol(optarg, &r, 0); + if (r == optarg) + printf("Bad argument to echr\n"); + break; + } case QEMU_OPTION_monitor: pstrcpy(monitor_device, sizeof(monitor_device), optarg); break; @@ -6143,12 +6328,17 @@ int main(int argc, char **argv) #endif } - monitor_hd = qemu_chr_open(monitor_device); - if (!monitor_hd) { - fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device); - exit(1); + for (i = 0; i < MAX_SERIAL_PORTS; i++) + if (!strncmp(serial_devices[i],"mon:",4)) + break; + if (i >= MAX_SERIAL_PORTS) { + monitor_hd = qemu_chr_open(monitor_device); + if (!monitor_hd) { + fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device); + exit(1); + } + monitor_init(monitor_hd, !nographic); } - monitor_init(monitor_hd, !nographic); for(i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_devices[i][0] != '\0') { Index: qemu/hw/pl011.c =================================================================== --- qemu.orig/hw/pl011.c +++ qemu/hw/pl011.c @@ -244,7 +244,7 @@ void pl011_init(uint32_t base, void *pic s->flags = 0x90; if (chr){ qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s); - qemu_chr_add_event_handler(chr, pl011_event); + qemu_chr_add_event_handler(chr, pl011_event, s); } /* ??? Save/restore. */ } Index: qemu/hw/serial.c =================================================================== --- qemu.orig/hw/serial.c +++ qemu/hw/serial.c @@ -363,7 +363,7 @@ SerialState *serial_init(SetIRQFunc *set register_ioport_read(base, 8, 1, serial_ioport_read, s); s->chr = chr; qemu_chr_add_read_handler(chr, serial_can_receive1, serial_receive1, s); - qemu_chr_add_event_handler(chr, serial_event); + qemu_chr_add_event_handler(chr, serial_event, s); return s; } @@ -451,6 +451,6 @@ SerialState *serial_mm_init (SetIRQFunc cpu_register_physical_memory(base, 8 << it_shift, s_io_memory); s->chr = chr; qemu_chr_add_read_handler(chr, serial_can_receive1, serial_receive1, s); - qemu_chr_add_event_handler(chr, serial_event); + qemu_chr_add_event_handler(chr, serial_event, s); return s; } Index: qemu/hw/sh7750.c =================================================================== --- qemu.orig/hw/sh7750.c +++ qemu/hw/sh7750.c @@ -301,7 +301,7 @@ static void init_serial1(SH7750State * s s->serial1 = chr; qemu_chr_add_read_handler(chr, serial1_can_receive, serial1_receive, s); - qemu_chr_add_event_handler(chr, serial1_event); + qemu_chr_add_event_handler(chr, serial1_event, s); } /********************************************************************** @@ -417,7 +417,7 @@ static void init_serial2(SH7750State * s s->serial2 = chr; qemu_chr_add_read_handler(chr, serial2_can_receive, serial2_receive, s); - qemu_chr_add_event_handler(chr, serial2_event); + qemu_chr_add_event_handler(chr, serial2_event, s); } static void init_serial_ports(SH7750State * s) Index: qemu/hw/slavio_serial.c =================================================================== --- qemu.orig/hw/slavio_serial.c +++ qemu/hw/slavio_serial.c @@ -457,7 +457,7 @@ SerialState *slavio_serial_init(int base s->chn[i].type = ser; if (s->chn[i].chr) { qemu_chr_add_read_handler(s->chn[i].chr, serial_can_receive, serial_receive1, &s->chn[i]); - qemu_chr_add_event_handler(s->chn[i].chr, serial_event); + qemu_chr_add_event_handler(s->chn[i].chr, serial_event, &s->chn[i]); } } s->chn[0].otherchn = &s->chn[1]; Index: qemu/monitor.c =================================================================== --- qemu.orig/monitor.c +++ qemu/monitor.c @@ -84,8 +84,19 @@ void term_puts(const char *str) break; term_outbuf[term_outbuf_index++] = c; if (term_outbuf_index >= sizeof(term_outbuf) || - c == '\n') + c == '\n') { term_flush(); + /* Tack on an extra \r when we get \n for correct terminal + * emulation mainly for telnet. Most of the time, the + * stdio is attached to some type of controlling terminal + * and the xterm and vt1xx emulation will automatically + * eat these '\r' chars. + */ + if (c == '\n') + { + term_outbuf[term_outbuf_index++] = '\r'; + } + } } } Index: qemu/vl.h =================================================================== --- qemu.orig/vl.h +++ qemu/vl.h @@ -269,6 +269,7 @@ typedef struct CharDriverState { void (*chr_send_event)(struct CharDriverState *chr, int event); void (*chr_close)(struct CharDriverState *chr); void *opaque; + void *event_opaque; } CharDriverState; void qemu_chr_printf(CharDriverState *s, const char *fmt, ...); @@ -277,7 +278,8 @@ void qemu_chr_send_event(CharDriverState 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_add_event_handler(CharDriverState *s, IOEventHandler *chr_event, + void *event_opaque); int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg); /* consoles */ Index: qemu/qemu-doc.texi =================================================================== --- qemu.orig/qemu-doc.texi +++ qemu/qemu-doc.texi @@ -582,6 +582,18 @@ MAGIC_SYSRQ sequence if you use a telnet sequence. Typically in unix telnet you do it with Control-] and then type "send break" followed by pressing the enter key. +@item mon:dev_string +This is a special option to allow the monitor to be multiplexed onto +another serial port. The monitor is accessed with key sequence of +@key{Control-a} and then pressing @key{c}. See monitor access +@ref{pcsys_keys} in the -nographic section for more keys. +@var{dev_string} should be any one of the serial devices specified +above. An example to multiplex the monitor onto a server telnet server +listening on port 4444 would be: +@table @code +@item -serial mon:telnet::4444,server,nowait +@end table + @end table @item -parallel dev @@ -599,6 +611,19 @@ serial port). The default device is @code{vc} in graphical mode and @code{stdio} in non graphical mode. +@item -echr numeric_ascii_value +Change the escape character used for switching to the monitor when using +monitor and serial sharing. The default is @code{0x01} when using the +@code{-nographic} option. @code{0x01} is equal to pressing +@code{Control-a}. You can select a different character from the ascii +control keys where 1 through 26 map to Control-a through Control-z. For +instance you could use the either of the following to change the escape +character to Control-t. +@table @code +@item -echr 0x14 +@item -echr 20 +@end table + @item -s Wait gdb connection to port 1234 (@pxref{gdb_usage}). @item -p port @@ -669,6 +694,8 @@ Print this help Exit emulatior @item Ctrl-a s Save disk data back to file (if -snapshot) +@item Ctrl-a t +toggle console timestamps @item Ctrl-a b Send break (magic sysrq in Linux) @item Ctrl-a c