Monitor multiplexing, by Jason Wessel.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2434 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
925fd0f202
commit
20d8a3edb0
32
monitor.c
32
monitor.c
|
@ -54,7 +54,8 @@ typedef struct term_cmd_t {
|
||||||
const char *help;
|
const char *help;
|
||||||
} term_cmd_t;
|
} term_cmd_t;
|
||||||
|
|
||||||
static CharDriverState *monitor_hd;
|
#define MAX_MON 4
|
||||||
|
static CharDriverState *monitor_hd[MAX_MON];
|
||||||
static int hide_banner;
|
static int hide_banner;
|
||||||
|
|
||||||
static term_cmd_t term_cmds[];
|
static term_cmd_t term_cmds[];
|
||||||
|
@ -69,8 +70,11 @@ CPUState *mon_cpu = NULL;
|
||||||
|
|
||||||
void term_flush(void)
|
void term_flush(void)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
if (term_outbuf_index > 0) {
|
if (term_outbuf_index > 0) {
|
||||||
qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index);
|
for (i = 0; i < MAX_MON; i++)
|
||||||
|
if (monitor_hd[i] && monitor_hd[i]->focus == 0)
|
||||||
|
qemu_chr_write(monitor_hd[i], term_outbuf, term_outbuf_index);
|
||||||
term_outbuf_index = 0;
|
term_outbuf_index = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2452,9 +2456,25 @@ static void term_event(void *opaque, int event)
|
||||||
monitor_start_input();
|
monitor_start_input();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int is_first_init = 1;
|
||||||
|
|
||||||
void monitor_init(CharDriverState *hd, int show_banner)
|
void monitor_init(CharDriverState *hd, int show_banner)
|
||||||
{
|
{
|
||||||
monitor_hd = hd;
|
int i;
|
||||||
|
|
||||||
|
if (is_first_init) {
|
||||||
|
for (i = 0; i < MAX_MON; i++) {
|
||||||
|
monitor_hd[i] = NULL;
|
||||||
|
}
|
||||||
|
is_first_init = 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < MAX_MON; i++) {
|
||||||
|
if (monitor_hd[i] == NULL) {
|
||||||
|
monitor_hd[i] = hd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hide_banner = !show_banner;
|
hide_banner = !show_banner;
|
||||||
|
|
||||||
qemu_chr_add_handlers(hd, term_can_read, term_read, term_event, NULL);
|
qemu_chr_add_handlers(hd, term_can_read, term_read, term_event, NULL);
|
||||||
|
@ -2475,8 +2495,12 @@ static void monitor_readline_cb(void *opaque, const char *input)
|
||||||
void monitor_readline(const char *prompt, int is_password,
|
void monitor_readline(const char *prompt, int is_password,
|
||||||
char *buf, int buf_size)
|
char *buf, int buf_size)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (is_password) {
|
if (is_password) {
|
||||||
qemu_chr_send_event(monitor_hd, CHR_EVENT_FOCUS);
|
for (i = 0; i < MAX_MON; i++)
|
||||||
|
if (monitor_hd[i] && monitor_hd[i]->focus == 0)
|
||||||
|
qemu_chr_send_event(monitor_hd[i], CHR_EVENT_FOCUS);
|
||||||
}
|
}
|
||||||
readline_start(prompt, is_password, monitor_readline_cb, NULL);
|
readline_start(prompt, is_password, monitor_readline_cb, NULL);
|
||||||
monitor_readline_buf = buf;
|
monitor_readline_buf = buf;
|
||||||
|
|
|
@ -610,6 +610,18 @@ A unix domain socket is used instead of a tcp socket. The option works the
|
||||||
same as if you had specified @code{-serial tcp} except the unix domain socket
|
same as if you had specified @code{-serial tcp} except the unix domain socket
|
||||||
@var{path} is used for connections.
|
@var{path} is used for connections.
|
||||||
|
|
||||||
|
@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 telnet server
|
||||||
|
listening on port 4444 would be:
|
||||||
|
@table @code
|
||||||
|
@item -serial mon:telnet::4444,server,nowait
|
||||||
|
@end table
|
||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@item -parallel dev
|
@item -parallel dev
|
||||||
|
@ -629,6 +641,19 @@ serial port).
|
||||||
The default device is @code{vc} in graphical mode and @code{stdio} in
|
The default device is @code{vc} in graphical mode and @code{stdio} in
|
||||||
non graphical mode.
|
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
|
@item -s
|
||||||
Wait gdb connection to port 1234 (@pxref{gdb_usage}).
|
Wait gdb connection to port 1234 (@pxref{gdb_usage}).
|
||||||
@item -p port
|
@item -p port
|
||||||
|
@ -711,6 +736,8 @@ Print this help
|
||||||
Exit emulator
|
Exit emulator
|
||||||
@item Ctrl-a s
|
@item Ctrl-a s
|
||||||
Save disk data back to file (if -snapshot)
|
Save disk data back to file (if -snapshot)
|
||||||
|
@item Ctrl-a t
|
||||||
|
toggle console timestamps
|
||||||
@item Ctrl-a b
|
@item Ctrl-a b
|
||||||
Send break (magic sysrq in Linux)
|
Send break (magic sysrq in Linux)
|
||||||
@item Ctrl-a c
|
@item Ctrl-a c
|
||||||
|
|
403
vl.c
403
vl.c
|
@ -1228,6 +1228,218 @@ static CharDriverState *qemu_chr_open_null(void)
|
||||||
return chr;
|
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 *chr_can_read[MAX_MUX];
|
||||||
|
IOReadHandler *chr_read[MAX_MUX];
|
||||||
|
IOEventHandler *chr_event[MAX_MUX];
|
||||||
|
void *ext_opaque[MAX_MUX];
|
||||||
|
CharDriverState *drv;
|
||||||
|
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;
|
||||||
|
int ret;
|
||||||
|
if (!term_timestamps) {
|
||||||
|
ret = d->drv->chr_write(d->drv, buf, len);
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
ret += d->drv->chr_write(d->drv, buf+i, 1);
|
||||||
|
if (buf[i] == '\n') {
|
||||||
|
char buf1[64];
|
||||||
|
int64_t ti;
|
||||||
|
int secs;
|
||||||
|
|
||||||
|
ti = get_clock();
|
||||||
|
if (term_timestamps_start == -1)
|
||||||
|
term_timestamps_start = ti;
|
||||||
|
ti -= term_timestamps_start;
|
||||||
|
secs = ti / 1000000000;
|
||||||
|
snprintf(buf1, sizeof(buf1),
|
||||||
|
"[%02d:%02d:%02d.%03d] ",
|
||||||
|
secs / 3600,
|
||||||
|
(secs / 60) % 60,
|
||||||
|
secs % 60,
|
||||||
|
(int)((ti / 1000000) % 1000));
|
||||||
|
d->drv->chr_write(d->drv, buf1, strlen(buf1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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':
|
||||||
|
{
|
||||||
|
char *term = "QEMU: Terminated\n\r";
|
||||||
|
chr->chr_write(chr,term,strlen(term));
|
||||||
|
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->opaque, CHR_EVENT_BREAK);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
/* Switch to the next registered device */
|
||||||
|
chr->focus++;
|
||||||
|
if (chr->focus >= d->mux_cnt)
|
||||||
|
chr->focus = 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_can_read(void *opaque)
|
||||||
|
{
|
||||||
|
CharDriverState *chr = opaque;
|
||||||
|
MuxDriver *d = chr->opaque;
|
||||||
|
if (d->chr_can_read[chr->focus])
|
||||||
|
return d->chr_can_read[chr->focus](d->ext_opaque[chr->focus]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mux_chr_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->chr_read[chr->focus](d->ext_opaque[chr->focus], &buf[i], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mux_chr_event(void *opaque, int event)
|
||||||
|
{
|
||||||
|
CharDriverState *chr = opaque;
|
||||||
|
MuxDriver *d = chr->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Send the event to all registered listeners */
|
||||||
|
for (i = 0; i < d->mux_cnt; i++)
|
||||||
|
if (d->chr_event[i])
|
||||||
|
d->chr_event[i](d->ext_opaque[i], event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mux_chr_update_read_handler(CharDriverState *chr)
|
||||||
|
{
|
||||||
|
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] = chr->handler_opaque;
|
||||||
|
d->chr_can_read[d->mux_cnt] = chr->chr_can_read;
|
||||||
|
d->chr_read[d->mux_cnt] = chr->chr_read;
|
||||||
|
d->chr_event[d->mux_cnt] = chr->chr_event;
|
||||||
|
/* Fix up the real driver with mux routines */
|
||||||
|
if (d->mux_cnt == 0) {
|
||||||
|
qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read,
|
||||||
|
mux_chr_event, chr);
|
||||||
|
}
|
||||||
|
chr->focus = 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;
|
||||||
|
chr->focus = -1;
|
||||||
|
chr->chr_write = mux_chr_write;
|
||||||
|
chr->chr_update_read_handler = mux_chr_update_read_handler;
|
||||||
|
return chr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
static void socket_cleanup(void)
|
static void socket_cleanup(void)
|
||||||
|
@ -1319,10 +1531,8 @@ typedef struct {
|
||||||
int max_size;
|
int max_size;
|
||||||
} FDCharDriver;
|
} FDCharDriver;
|
||||||
|
|
||||||
#define STDIO_MAX_CLIENTS 2
|
#define STDIO_MAX_CLIENTS 1
|
||||||
|
static int stdio_nb_clients = 0;
|
||||||
static int stdio_nb_clients;
|
|
||||||
static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
|
|
||||||
|
|
||||||
static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
|
@ -1435,102 +1645,15 @@ static CharDriverState *qemu_chr_open_pipe(const char *filename)
|
||||||
/* for STDIO, we handle the case where several clients use it
|
/* for STDIO, we handle the case where several clients use it
|
||||||
(nographic mode) */
|
(nographic mode) */
|
||||||
|
|
||||||
#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
|
|
||||||
|
|
||||||
#define TERM_FIFO_MAX_SIZE 1
|
#define TERM_FIFO_MAX_SIZE 1
|
||||||
|
|
||||||
static int term_got_escape, client_index;
|
|
||||||
static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
|
static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
|
||||||
static int term_fifo_size;
|
static int term_fifo_size;
|
||||||
static int term_timestamps;
|
|
||||||
static int64_t term_timestamps_start;
|
|
||||||
|
|
||||||
void term_print_help(void)
|
|
||||||
{
|
|
||||||
printf("\n"
|
|
||||||
"C-a h print this help\n"
|
|
||||||
"C-a x exit emulator\n"
|
|
||||||
"C-a s save disk data back to file (if -snapshot)\n"
|
|
||||||
"C-a b send break (magic sysrq)\n"
|
|
||||||
"C-a t toggle console timestamps\n"
|
|
||||||
"C-a c switch between console and monitor\n"
|
|
||||||
"C-a C-a send C-a\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* called when a char is received */
|
|
||||||
static void stdio_received_byte(int ch)
|
|
||||||
{
|
|
||||||
if (term_got_escape) {
|
|
||||||
term_got_escape = 0;
|
|
||||||
switch(ch) {
|
|
||||||
case 'h':
|
|
||||||
term_print_help();
|
|
||||||
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 (client_index < stdio_nb_clients) {
|
|
||||||
CharDriverState *chr;
|
|
||||||
FDCharDriver *s;
|
|
||||||
|
|
||||||
chr = stdio_clients[client_index];
|
|
||||||
s = chr->opaque;
|
|
||||||
qemu_chr_event(chr, CHR_EVENT_BREAK);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
client_index++;
|
|
||||||
if (client_index >= stdio_nb_clients)
|
|
||||||
client_index = 0;
|
|
||||||
if (client_index == 0) {
|
|
||||||
/* send a new line in the monitor to get the prompt */
|
|
||||||
ch = '\r';
|
|
||||||
goto send_char;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
term_timestamps = !term_timestamps;
|
|
||||||
term_timestamps_start = -1;
|
|
||||||
break;
|
|
||||||
case TERM_ESCAPE:
|
|
||||||
goto send_char;
|
|
||||||
}
|
|
||||||
} else if (ch == TERM_ESCAPE) {
|
|
||||||
term_got_escape = 1;
|
|
||||||
} else {
|
|
||||||
send_char:
|
|
||||||
if (client_index < stdio_nb_clients) {
|
|
||||||
uint8_t buf[1];
|
|
||||||
CharDriverState *chr;
|
|
||||||
|
|
||||||
chr = stdio_clients[client_index];
|
|
||||||
if (qemu_chr_can_read(chr) > 0) {
|
|
||||||
buf[0] = ch;
|
|
||||||
qemu_chr_read(chr, buf, 1);
|
|
||||||
} else if (term_fifo_size == 0) {
|
|
||||||
term_fifo[term_fifo_size++] = ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int stdio_read_poll(void *opaque)
|
static int stdio_read_poll(void *opaque)
|
||||||
{
|
{
|
||||||
CharDriverState *chr;
|
CharDriverState *chr = opaque;
|
||||||
|
|
||||||
if (client_index < stdio_nb_clients) {
|
|
||||||
chr = stdio_clients[client_index];
|
|
||||||
/* try to flush the queue if needed */
|
/* try to flush the queue if needed */
|
||||||
if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) {
|
if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) {
|
||||||
qemu_chr_read(chr, term_fifo, 1);
|
qemu_chr_read(chr, term_fifo, 1);
|
||||||
|
@ -1541,15 +1664,13 @@ static int stdio_read_poll(void *opaque)
|
||||||
return 1;
|
return 1;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stdio_read(void *opaque)
|
static void stdio_read(void *opaque)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
uint8_t buf[1];
|
uint8_t buf[1];
|
||||||
|
CharDriverState *chr = opaque;
|
||||||
|
|
||||||
size = read(0, buf, 1);
|
size = read(0, buf, 1);
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
|
@ -1557,40 +1678,12 @@ static void stdio_read(void *opaque)
|
||||||
qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
|
qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (size > 0)
|
if (size > 0) {
|
||||||
stdio_received_byte(buf[0]);
|
if (qemu_chr_can_read(chr) > 0) {
|
||||||
|
qemu_chr_read(chr, buf, 1);
|
||||||
|
} else if (term_fifo_size == 0) {
|
||||||
|
term_fifo[term_fifo_size++] = buf[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
FDCharDriver *s = chr->opaque;
|
|
||||||
if (!term_timestamps) {
|
|
||||||
return unix_write(s->fd_out, buf, len);
|
|
||||||
} else {
|
|
||||||
int i;
|
|
||||||
char buf1[64];
|
|
||||||
|
|
||||||
for(i = 0; i < len; i++) {
|
|
||||||
unix_write(s->fd_out, buf + i, 1);
|
|
||||||
if (buf[i] == '\n') {
|
|
||||||
int64_t ti;
|
|
||||||
int secs;
|
|
||||||
|
|
||||||
ti = get_clock();
|
|
||||||
if (term_timestamps_start == -1)
|
|
||||||
term_timestamps_start = ti;
|
|
||||||
ti -= term_timestamps_start;
|
|
||||||
secs = ti / 1000000000;
|
|
||||||
snprintf(buf1, sizeof(buf1),
|
|
||||||
"[%02d:%02d:%02d.%03d] ",
|
|
||||||
secs / 3600,
|
|
||||||
(secs / 60) % 60,
|
|
||||||
secs % 60,
|
|
||||||
(int)((ti / 1000000) % 1000));
|
|
||||||
unix_write(s->fd_out, buf1, strlen(buf1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1635,24 +1728,13 @@ static CharDriverState *qemu_chr_open_stdio(void)
|
||||||
{
|
{
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
|
|
||||||
if (nographic) {
|
|
||||||
if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
|
if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
|
||||||
return NULL;
|
return NULL;
|
||||||
chr = qemu_chr_open_fd(0, 1);
|
chr = qemu_chr_open_fd(0, 1);
|
||||||
chr->chr_write = stdio_write;
|
qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr);
|
||||||
if (stdio_nb_clients == 0)
|
stdio_nb_clients++;
|
||||||
qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL);
|
|
||||||
client_index = stdio_nb_clients;
|
|
||||||
} else {
|
|
||||||
if (stdio_nb_clients != 0)
|
|
||||||
return NULL;
|
|
||||||
chr = qemu_chr_open_fd(0, 1);
|
|
||||||
}
|
|
||||||
stdio_clients[stdio_nb_clients++] = chr;
|
|
||||||
if (stdio_nb_clients == 1) {
|
|
||||||
/* set the terminal in raw mode */
|
|
||||||
term_init();
|
term_init();
|
||||||
}
|
|
||||||
return chr;
|
return chr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2815,6 +2897,16 @@ CharDriverState *qemu_chr_open(const char *filename)
|
||||||
if (strstart(filename, "udp:", &p)) {
|
if (strstart(filename, "udp:", &p)) {
|
||||||
return qemu_chr_open_udp(p);
|
return qemu_chr_open_udp(p);
|
||||||
} else
|
} 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
|
#ifndef _WIN32
|
||||||
if (strstart(filename, "unix:", &p)) {
|
if (strstart(filename, "unix:", &p)) {
|
||||||
return qemu_chr_open_tcp(p, 0, 1);
|
return qemu_chr_open_tcp(p, 0, 1);
|
||||||
|
@ -6416,6 +6508,7 @@ enum {
|
||||||
QEMU_OPTION_cirrusvga,
|
QEMU_OPTION_cirrusvga,
|
||||||
QEMU_OPTION_g,
|
QEMU_OPTION_g,
|
||||||
QEMU_OPTION_std_vga,
|
QEMU_OPTION_std_vga,
|
||||||
|
QEMU_OPTION_echr,
|
||||||
QEMU_OPTION_monitor,
|
QEMU_OPTION_monitor,
|
||||||
QEMU_OPTION_serial,
|
QEMU_OPTION_serial,
|
||||||
QEMU_OPTION_parallel,
|
QEMU_OPTION_parallel,
|
||||||
|
@ -6497,6 +6590,7 @@ const QEMUOption qemu_options[] = {
|
||||||
#endif
|
#endif
|
||||||
{ "localtime", 0, QEMU_OPTION_localtime },
|
{ "localtime", 0, QEMU_OPTION_localtime },
|
||||||
{ "std-vga", 0, QEMU_OPTION_std_vga },
|
{ "std-vga", 0, QEMU_OPTION_std_vga },
|
||||||
|
{ "echr", 1, QEMU_OPTION_echr },
|
||||||
{ "monitor", 1, QEMU_OPTION_monitor },
|
{ "monitor", 1, QEMU_OPTION_monitor },
|
||||||
{ "serial", 1, QEMU_OPTION_serial },
|
{ "serial", 1, QEMU_OPTION_serial },
|
||||||
{ "parallel", 1, QEMU_OPTION_parallel },
|
{ "parallel", 1, QEMU_OPTION_parallel },
|
||||||
|
@ -6932,8 +7026,8 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_nographic:
|
case QEMU_OPTION_nographic:
|
||||||
pstrcpy(monitor_device, sizeof(monitor_device), "stdio");
|
|
||||||
pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio");
|
pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio");
|
||||||
|
pstrcpy(monitor_device, sizeof(monitor_device), "stdio");
|
||||||
nographic = 1;
|
nographic = 1;
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_kernel:
|
case QEMU_OPTION_kernel:
|
||||||
|
@ -7094,6 +7188,14 @@ int main(int argc, char **argv)
|
||||||
graphic_depth = depth;
|
graphic_depth = depth;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case QEMU_OPTION_monitor:
|
||||||
pstrcpy(monitor_device, sizeof(monitor_device), optarg);
|
pstrcpy(monitor_device, sizeof(monitor_device), optarg);
|
||||||
break;
|
break;
|
||||||
|
@ -7384,12 +7486,27 @@ int main(int argc, char **argv)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Maintain compatibility with multiple stdio monitors */
|
||||||
|
if (!strcmp(monitor_device,"stdio")) {
|
||||||
|
for (i = 0; i < MAX_SERIAL_PORTS; i++) {
|
||||||
|
if (!strcmp(serial_devices[i],"mon:stdio")) {
|
||||||
|
monitor_device[0] = '\0';
|
||||||
|
break;
|
||||||
|
} else if (!strcmp(serial_devices[i],"stdio")) {
|
||||||
|
monitor_device[0] = '\0';
|
||||||
|
pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "mon:stdio");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (monitor_device[0] != '\0') {
|
||||||
monitor_hd = qemu_chr_open(monitor_device);
|
monitor_hd = qemu_chr_open(monitor_device);
|
||||||
if (!monitor_hd) {
|
if (!monitor_hd) {
|
||||||
fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
|
fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
monitor_init(monitor_hd, !nographic);
|
monitor_init(monitor_hd, !nographic);
|
||||||
|
}
|
||||||
|
|
||||||
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
|
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
|
||||||
const char *devname = serial_devices[i];
|
const char *devname = serial_devices[i];
|
||||||
|
|
1
vl.h
1
vl.h
|
@ -303,6 +303,7 @@ typedef struct CharDriverState {
|
||||||
void (*chr_send_event)(struct CharDriverState *chr, int event);
|
void (*chr_send_event)(struct CharDriverState *chr, int event);
|
||||||
void (*chr_close)(struct CharDriverState *chr);
|
void (*chr_close)(struct CharDriverState *chr);
|
||||||
void *opaque;
|
void *opaque;
|
||||||
|
int focus;
|
||||||
QEMUBH *bh;
|
QEMUBH *bh;
|
||||||
} CharDriverState;
|
} CharDriverState;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue