#include "qemu/timer.h"
#include "char/char.h"
#include "hw/usb.h"
-#include "hw/baum.h"
-#include "hw/msmouse.h"
#include "qmp-commands.h"
#include <unistd.h>
#include "ui/qemu-spice.h"
#define READ_BUF_LEN 4096
-#define CBUFF_SIZE 65536
/***********************************************************/
/* character device */
s->chr_event(s->handler_opaque, event);
}
-static void qemu_chr_fire_open_event(void *opaque)
+static gboolean qemu_chr_generic_open_bh(gpointer opaque)
{
CharDriverState *s = opaque;
qemu_chr_be_event(s, CHR_EVENT_OPENED);
- qemu_free_timer(s->open_timer);
- s->open_timer = NULL;
+ s->idle_tag = 0;
+ return FALSE;
}
void qemu_chr_generic_open(CharDriverState *s)
{
- if (s->open_timer == NULL) {
- s->open_timer = qemu_new_timer_ms(rt_clock,
- qemu_chr_fire_open_event, s);
- qemu_mod_timer(s->open_timer, qemu_get_clock_ms(rt_clock) - 1);
+ if (s->idle_tag == 0) {
+ s->idle_tag = g_idle_add(qemu_chr_generic_open_bh, s);
}
}
return len;
}
-static CharDriverState *qemu_chr_open_null(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_null(void)
{
CharDriverState *chr;
}
return len1 - len;
}
+
+int recv_all(int fd, void *_buf, int len1, bool single_read)
+{
+ int ret, len;
+ uint8_t *buf = _buf;
+
+ len = len1;
+ while ((len > 0) && (ret = read(fd, buf, len)) != 0) {
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ return -1;
+ }
+ continue;
+ } else {
+ if (single_read) {
+ return ret;
+ }
+ buf += ret;
+ len -= ret;
+ }
+ }
+ return len1 - len;
+}
+
#endif /* !_WIN32 */
-#define STDIO_MAX_CLIENTS 1
-static int stdio_nb_clients;
+typedef struct IOWatchPoll
+{
+ GSource *src;
+ int max_size;
+
+ IOCanReadHandler *fd_can_read;
+ void *opaque;
+
+ QTAILQ_ENTRY(IOWatchPoll) node;
+} IOWatchPoll;
+
+static QTAILQ_HEAD(, IOWatchPoll) io_watch_poll_list =
+ QTAILQ_HEAD_INITIALIZER(io_watch_poll_list);
+
+static IOWatchPoll *io_watch_poll_from_source(GSource *source)
+{
+ IOWatchPoll *i;
+
+ QTAILQ_FOREACH(i, &io_watch_poll_list, node) {
+ if (i->src == source) {
+ return i;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_)
+{
+ IOWatchPoll *iwp = io_watch_poll_from_source(source);
+
+ iwp->max_size = iwp->fd_can_read(iwp->opaque);
+ if (iwp->max_size == 0) {
+ return FALSE;
+ }
+
+ return g_io_watch_funcs.prepare(source, timeout_);
+}
+
+static gboolean io_watch_poll_check(GSource *source)
+{
+ IOWatchPoll *iwp = io_watch_poll_from_source(source);
+
+ if (iwp->max_size == 0) {
+ return FALSE;
+ }
+
+ return g_io_watch_funcs.check(source);
+}
+
+static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
+ gpointer user_data)
+{
+ return g_io_watch_funcs.dispatch(source, callback, user_data);
+}
+
+static void io_watch_poll_finalize(GSource *source)
+{
+ IOWatchPoll *iwp = io_watch_poll_from_source(source);
+ QTAILQ_REMOVE(&io_watch_poll_list, iwp, node);
+ g_io_watch_funcs.finalize(source);
+}
+
+static GSourceFuncs io_watch_poll_funcs = {
+ .prepare = io_watch_poll_prepare,
+ .check = io_watch_poll_check,
+ .dispatch = io_watch_poll_dispatch,
+ .finalize = io_watch_poll_finalize,
+};
+
+/* Can only be used for read */
+static guint io_add_watch_poll(GIOChannel *channel,
+ IOCanReadHandler *fd_can_read,
+ GIOFunc fd_read,
+ gpointer user_data)
+{
+ IOWatchPoll *iwp;
+ GSource *src;
+ guint tag;
+
+ src = g_io_create_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP);
+ g_source_set_funcs(src, &io_watch_poll_funcs);
+ g_source_set_callback(src, (GSourceFunc)fd_read, user_data, NULL);
+ tag = g_source_attach(src, NULL);
+ g_source_unref(src);
+
+ iwp = g_malloc0(sizeof(*iwp));
+ iwp->src = src;
+ iwp->max_size = 0;
+ iwp->fd_can_read = fd_can_read;
+ iwp->opaque = user_data;
+
+ QTAILQ_INSERT_HEAD(&io_watch_poll_list, iwp, node);
+
+ return tag;
+}
#ifndef _WIN32
+static GIOChannel *io_channel_from_fd(int fd)
+{
+ GIOChannel *chan;
-typedef struct {
- int fd_in, fd_out;
- int max_size;
-} FDCharDriver;
+ if (fd == -1) {
+ return NULL;
+ }
+ chan = g_io_channel_unix_new(fd);
-static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ g_io_channel_set_encoding(chan, NULL, NULL);
+ g_io_channel_set_buffered(chan, FALSE);
+
+ return chan;
+}
+#endif
+
+static GIOChannel *io_channel_from_socket(int fd)
{
- FDCharDriver *s = chr->opaque;
- return send_all(s->fd_out, buf, len);
+ GIOChannel *chan;
+
+ if (fd == -1) {
+ return NULL;
+ }
+
+#ifdef _WIN32
+ chan = g_io_channel_win32_new_socket(fd);
+#else
+ chan = g_io_channel_unix_new(fd);
+#endif
+
+ g_io_channel_set_encoding(chan, NULL, NULL);
+ g_io_channel_set_buffered(chan, FALSE);
+
+ return chan;
}
-static int fd_chr_read_poll(void *opaque)
+static int io_channel_send_all(GIOChannel *fd, const void *_buf, int len1)
{
- CharDriverState *chr = opaque;
- FDCharDriver *s = chr->opaque;
+ GIOStatus status;
+ gsize bytes_written;
+ int len;
+ const uint8_t *buf = _buf;
- s->max_size = qemu_chr_be_can_write(chr);
- return s->max_size;
+ len = len1;
+ while (len > 0) {
+ status = g_io_channel_write_chars(fd, (const gchar *)buf, len,
+ &bytes_written, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
+ if (status == G_IO_STATUS_AGAIN) {
+ errno = EAGAIN;
+ return -1;
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ } else if (status == G_IO_STATUS_EOF) {
+ break;
+ } else {
+ buf += bytes_written;
+ len -= bytes_written;
+ }
+ }
+ return len1 - len;
+}
+
+#ifndef _WIN32
+
+typedef struct FDCharDriver {
+ CharDriverState *chr;
+ GIOChannel *fd_in, *fd_out;
+ guint fd_in_tag;
+ int max_size;
+ QTAILQ_ENTRY(FDCharDriver) node;
+} FDCharDriver;
+
+static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ FDCharDriver *s = chr->opaque;
+
+ return io_channel_send_all(s->fd_out, buf, len);
}
-static void fd_chr_read(void *opaque)
+static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
FDCharDriver *s = chr->opaque;
- int size, len;
+ int len;
uint8_t buf[READ_BUF_LEN];
+ GIOStatus status;
+ gsize bytes_read;
len = sizeof(buf);
- if (len > s->max_size)
+ if (len > s->max_size) {
len = s->max_size;
- if (len == 0)
- return;
- size = read(s->fd_in, buf, len);
- if (size == 0) {
- /* FD has been closed. Remove it from the active list. */
- qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+ }
+ if (len == 0) {
+ return FALSE;
+ }
+
+ status = g_io_channel_read_chars(chan, (gchar *)buf,
+ len, &bytes_read, NULL);
+ if (status == G_IO_STATUS_EOF) {
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
- return;
+ return FALSE;
}
- if (size > 0) {
- qemu_chr_be_write(chr, buf, size);
+ if (status == G_IO_STATUS_NORMAL) {
+ qemu_chr_be_write(chr, buf, bytes_read);
}
+
+ return TRUE;
+}
+
+static int fd_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ FDCharDriver *s = chr->opaque;
+
+ s->max_size = qemu_chr_be_can_write(chr);
+ return s->max_size;
+}
+
+static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+{
+ FDCharDriver *s = chr->opaque;
+ return g_io_create_watch(s->fd_out, cond);
}
static void fd_chr_update_read_handler(CharDriverState *chr)
{
FDCharDriver *s = chr->opaque;
- if (s->fd_in >= 0) {
- if (display_type == DT_NOGRAPHIC && s->fd_in == 0) {
- } else {
- qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll,
- fd_chr_read, NULL, chr);
- }
+ if (s->fd_in_tag) {
+ g_source_remove(s->fd_in_tag);
+ }
+
+ if (s->fd_in) {
+ s->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll, fd_chr_read, chr);
}
}
{
FDCharDriver *s = chr->opaque;
- if (s->fd_in >= 0) {
- if (display_type == DT_NOGRAPHIC && s->fd_in == 0) {
- } else {
- qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
- }
+ if (s->fd_in_tag) {
+ g_source_remove(s->fd_in_tag);
+ s->fd_in_tag = 0;
+ }
+
+ if (s->fd_in) {
+ g_io_channel_unref(s->fd_in);
+ }
+ if (s->fd_out) {
+ g_io_channel_unref(s->fd_out);
}
g_free(s);
chr = g_malloc0(sizeof(CharDriverState));
s = g_malloc0(sizeof(FDCharDriver));
- s->fd_in = fd_in;
- s->fd_out = fd_out;
+ s->fd_in = io_channel_from_fd(fd_in);
+ s->fd_out = io_channel_from_fd(fd_out);
+ fcntl(fd_out, F_SETFL, O_NONBLOCK);
+ s->chr = chr;
chr->opaque = s;
+ chr->chr_add_watch = fd_chr_add_watch;
chr->chr_write = fd_chr_write;
chr->chr_update_read_handler = fd_chr_update_read_handler;
chr->chr_close = fd_chr_close;
return chr;
}
-static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts)
-{
- int fd_out;
-
- TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"),
- O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666));
- if (fd_out < 0) {
- return NULL;
- }
- return qemu_chr_open_fd(-1, fd_out);
-}
-
-static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
{
int fd_in, fd_out;
char filename_in[256], filename_out[256];
- const char *filename = qemu_opt_get(opts, "path");
+ const char *filename = opts->device;
if (filename == NULL) {
fprintf(stderr, "chardev: pipe: no filename given\n");
return qemu_chr_open_fd(fd_in, fd_out);
}
-
-/* for STDIO, we handle the case where several clients use it
- (nographic mode) */
-
-#define TERM_FIFO_MAX_SIZE 1
-
-static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
-static int term_fifo_size;
-
-static int stdio_read_poll(void *opaque)
-{
- CharDriverState *chr = opaque;
-
- /* try to flush the queue if needed */
- if (term_fifo_size != 0 && qemu_chr_be_can_write(chr) > 0) {
- qemu_chr_be_write(chr, term_fifo, 1);
- term_fifo_size = 0;
- }
- /* see if we can absorb more chars */
- if (term_fifo_size == 0)
- return 1;
- else
- return 0;
-}
-
-static void stdio_read(void *opaque)
-{
- int size;
- uint8_t buf[1];
- CharDriverState *chr = opaque;
-
- size = read(0, buf, 1);
- if (size == 0) {
- /* stdin has been closed. Remove it from the active list. */
- qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
- qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
- return;
- }
- if (size > 0) {
- if (qemu_chr_be_can_write(chr) > 0) {
- qemu_chr_be_write(chr, buf, 1);
- } else if (term_fifo_size == 0) {
- term_fifo[term_fifo_size++] = buf[0];
- }
- }
-}
-
/* init terminal so that we can grab keys */
static struct termios oldtty;
static int old_fd0_flags;
static void qemu_chr_close_stdio(struct CharDriverState *chr)
{
term_exit();
- stdio_nb_clients--;
- qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
fd_chr_close(chr);
}
-static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
{
CharDriverState *chr;
- if (stdio_nb_clients >= STDIO_MAX_CLIENTS) {
- return NULL;
- }
if (is_daemonized()) {
error_report("cannot use stdio with -daemonize");
return NULL;
}
- if (stdio_nb_clients == 0) {
- old_fd0_flags = fcntl(0, F_GETFL);
- tcgetattr (0, &oldtty);
- fcntl(0, F_SETFL, O_NONBLOCK);
- atexit(term_exit);
- }
+ old_fd0_flags = fcntl(0, F_GETFL);
+ tcgetattr (0, &oldtty);
+ fcntl(0, F_SETFL, O_NONBLOCK);
+ atexit(term_exit);
chr = qemu_chr_open_fd(0, 1);
chr->chr_close = qemu_chr_close_stdio;
chr->chr_set_echo = qemu_chr_set_echo_stdio;
- qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr);
- stdio_nb_clients++;
- stdio_allow_signal = qemu_opt_get_bool(opts, "signal",
- display_type != DT_NOGRAPHIC);
+ stdio_allow_signal = display_type != DT_NOGRAPHIC;
+ if (opts->has_signal) {
+ stdio_allow_signal = opts->signal;
+ }
qemu_chr_fe_set_echo(chr, false);
return chr;
#define HAVE_CHARDEV_TTY 1
typedef struct {
- int fd;
+ GIOChannel *fd;
+ guint fd_tag;
int connected;
int polling;
int read_bytes;
- QEMUTimer *timer;
+ guint timer_tag;
} PtyCharDriver;
static void pty_chr_update_read_handler(CharDriverState *chr);
static void pty_chr_state(CharDriverState *chr, int connected);
+static gboolean pty_chr_timer(gpointer opaque)
+{
+ struct CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ if (s->connected) {
+ goto out;
+ }
+ if (s->polling) {
+ /* If we arrive here without polling being cleared due
+ * read returning -EIO, then we are (re-)connected */
+ pty_chr_state(chr, 1);
+ goto out;
+ }
+
+ /* Next poll ... */
+ pty_chr_update_read_handler(chr);
+
+out:
+ return FALSE;
+}
+
+static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ if (s->timer_tag) {
+ g_source_remove(s->timer_tag);
+ s->timer_tag = 0;
+ }
+
+ if (ms == 1000) {
+ s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
+ } else {
+ s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
+ }
+}
+
static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
PtyCharDriver *s = chr->opaque;
pty_chr_update_read_handler(chr);
return 0;
}
- return send_all(s->fd, buf, len);
+ return io_channel_send_all(s->fd, buf, len);
+}
+
+static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+{
+ PtyCharDriver *s = chr->opaque;
+ return g_io_create_watch(s->fd, cond);
}
static int pty_chr_read_poll(void *opaque)
return s->read_bytes;
}
-static void pty_chr_read(void *opaque)
+static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
PtyCharDriver *s = chr->opaque;
- int size, len;
+ gsize size, len;
uint8_t buf[READ_BUF_LEN];
+ GIOStatus status;
len = sizeof(buf);
if (len > s->read_bytes)
len = s->read_bytes;
if (len == 0)
- return;
- size = read(s->fd, buf, len);
- if ((size == -1 && errno == EIO) ||
- (size == 0)) {
+ return FALSE;
+ status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
pty_chr_state(chr, 0);
- return;
- }
- if (size > 0) {
+ return FALSE;
+ } else {
pty_chr_state(chr, 1);
qemu_chr_be_write(chr, buf, size);
}
+ return TRUE;
}
static void pty_chr_update_read_handler(CharDriverState *chr)
{
PtyCharDriver *s = chr->opaque;
- qemu_set_fd_handler2(s->fd, pty_chr_read_poll,
- pty_chr_read, NULL, chr);
+ if (s->fd_tag) {
+ g_source_remove(s->fd_tag);
+ }
+
+ s->fd_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr);
s->polling = 1;
/*
* Short timeout here: just need wait long enougth that qemu makes
* timeout to the normal (much longer) poll interval before the
* timer triggers.
*/
- qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 10);
+ pty_chr_rearm_timer(chr, 10);
}
static void pty_chr_state(CharDriverState *chr, int connected)
PtyCharDriver *s = chr->opaque;
if (!connected) {
- qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ g_source_remove(s->fd_tag);
+ s->fd_tag = 0;
s->connected = 0;
s->polling = 0;
/* (re-)connect poll interval for idle guests: once per second.
* We check more frequently in case the guests sends data to
* the virtual device linked to our pty. */
- qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 1000);
+ pty_chr_rearm_timer(chr, 1000);
} else {
if (!s->connected)
qemu_chr_generic_open(chr);
}
}
-static void pty_chr_timer(void *opaque)
-{
- struct CharDriverState *chr = opaque;
- PtyCharDriver *s = chr->opaque;
-
- if (s->connected)
- return;
- if (s->polling) {
- /* If we arrive here without polling being cleared due
- * read returning -EIO, then we are (re-)connected */
- pty_chr_state(chr, 1);
- return;
- }
-
- /* Next poll ... */
- pty_chr_update_read_handler(chr);
-}
static void pty_chr_close(struct CharDriverState *chr)
{
PtyCharDriver *s = chr->opaque;
+ int fd;
- qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
- close(s->fd);
- qemu_del_timer(s->timer);
- qemu_free_timer(s->timer);
+ if (s->fd_tag) {
+ g_source_remove(s->fd_tag);
+ }
+ fd = g_io_channel_unix_get_fd(s->fd);
+ g_io_channel_unref(s->fd);
+ close(fd);
+ if (s->timer_tag) {
+ g_source_remove(s->timer_tag);
+ }
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_pty(const char *id,
+ ChardevReturn *ret)
{
CharDriverState *chr;
PtyCharDriver *s;
struct termios tty;
- const char *label;
- int master_fd, slave_fd, len;
+ int master_fd, slave_fd;
#if defined(__OpenBSD__) || defined(__DragonFly__)
char pty_name[PATH_MAX];
#define q_ptsname(x) pty_name
chr = g_malloc0(sizeof(CharDriverState));
- len = strlen(q_ptsname(master_fd)) + 5;
- chr->filename = g_malloc(len);
- snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd));
- qemu_opt_set(opts, "path", q_ptsname(master_fd));
+ chr->filename = g_strdup_printf("pty:%s", q_ptsname(master_fd));
+ ret->pty = g_strdup(q_ptsname(master_fd));
+ ret->has_pty = true;
- label = qemu_opts_id(opts);
- fprintf(stderr, "char device redirected to %s%s%s%s\n",
- q_ptsname(master_fd),
- label ? " (label " : "",
- label ? label : "",
- label ? ")" : "");
+ fprintf(stderr, "char device redirected to %s (label %s)\n",
+ q_ptsname(master_fd), id);
s = g_malloc0(sizeof(PtyCharDriver));
chr->opaque = s;
chr->chr_write = pty_chr_write;
chr->chr_update_read_handler = pty_chr_update_read_handler;
chr->chr_close = pty_chr_close;
+ chr->chr_add_watch = pty_chr_add_watch;
- s->fd = master_fd;
- s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr);
+ s->fd = io_channel_from_fd(master_fd);
+ s->timer_tag = 0;
return chr;
}
case CHR_IOCTL_SERIAL_SET_PARAMS:
{
QEMUSerialSetParams *ssp = arg;
- tty_serial_init(s->fd_in, ssp->speed, ssp->parity,
+ tty_serial_init(g_io_channel_unix_get_fd(s->fd_in),
+ ssp->speed, ssp->parity,
ssp->data_bits, ssp->stop_bits);
}
break;
case CHR_IOCTL_SERIAL_SET_BREAK:
{
int enable = *(int *)arg;
- if (enable)
- tcsendbreak(s->fd_in, 1);
+ if (enable) {
+ tcsendbreak(g_io_channel_unix_get_fd(s->fd_in), 1);
+ }
}
break;
case CHR_IOCTL_SERIAL_GET_TIOCM:
{
int sarg = 0;
int *targ = (int *)arg;
- ioctl(s->fd_in, TIOCMGET, &sarg);
+ ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &sarg);
*targ = 0;
if (sarg & TIOCM_CTS)
*targ |= CHR_TIOCM_CTS;
{
int sarg = *(int *)arg;
int targ = 0;
- ioctl(s->fd_in, TIOCMGET, &targ);
+ ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &targ);
targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
| CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
if (sarg & CHR_TIOCM_CTS)
targ |= TIOCM_DTR;
if (sarg & CHR_TIOCM_RTS)
targ |= TIOCM_RTS;
- ioctl(s->fd_in, TIOCMSET, &targ);
+ ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMSET, &targ);
}
break;
default:
int fd = -1;
if (s) {
- fd = s->fd_in;
+ fd = g_io_channel_unix_get_fd(s->fd_in);
}
fd_chr_close(chr);
chr->chr_close = qemu_chr_close_tty;
return chr;
}
-
-static CharDriverState *qemu_chr_open_tty(QemuOpts *opts)
-{
- const char *filename = qemu_opt_get(opts, "path");
- int fd;
-
- TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK));
- if (fd < 0) {
- return NULL;
- }
- return qemu_chr_open_tty_fd(fd);
-}
#endif /* __linux__ || __sun__ */
#if defined(__linux__)
#else /* _WIN32 */
-static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
-
typedef struct {
int max_size;
HANDLE hcom, hrecv, hsend;
return chr;
}
-static CharDriverState *qemu_chr_open_win(QemuOpts *opts)
-{
- return qemu_chr_open_win_path(qemu_opt_get(opts, "path"));
-}
-
static int win_chr_pipe_poll(void *opaque)
{
CharDriverState *chr = opaque;
}
-static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
{
- const char *filename = qemu_opt_get(opts, "path");
+ const char *filename = opts->device;
CharDriverState *chr;
WinCharState *s;
return chr;
}
-static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_win_con(void)
{
return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
}
-static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts)
-{
- const char *file_out = qemu_opt_get(opts, "path");
- HANDLE fd_out;
-
- fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
- OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (fd_out == INVALID_HANDLE_VALUE) {
- return NULL;
- }
-
- return qemu_chr_open_win_file(fd_out);
-}
-
static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
{
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
g_free(chr->opaque);
g_free(chr);
- stdio_nb_clients--;
}
-static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
{
CharDriverState *chr;
WinStdioCharState *stdio;
DWORD dwMode;
int is_console = 0;
- if (stdio_nb_clients >= STDIO_MAX_CLIENTS
- || ((display_type != DT_NOGRAPHIC) && (stdio_nb_clients != 0))) {
- return NULL;
- }
-
chr = g_malloc0(sizeof(CharDriverState));
stdio = g_malloc0(sizeof(WinStdioCharState));
chr->chr_write = win_stdio_write;
chr->chr_close = win_stdio_close;
- if (stdio_nb_clients == 0) {
- if (is_console) {
- if (qemu_add_wait_object(stdio->hStdIn,
- win_stdio_wait_func, chr)) {
- fprintf(stderr, "qemu_add_wait_object: failed\n");
- }
- } else {
- DWORD dwId;
-
- stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread,
- chr, 0, &dwId);
-
- if (stdio->hInputThread == INVALID_HANDLE_VALUE
- || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
- || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "cannot create stdio thread or event\n");
- exit(1);
- }
- if (qemu_add_wait_object(stdio->hInputReadyEvent,
- win_stdio_thread_wait_func, chr)) {
- fprintf(stderr, "qemu_add_wait_object: failed\n");
- }
+ if (is_console) {
+ if (qemu_add_wait_object(stdio->hStdIn,
+ win_stdio_wait_func, chr)) {
+ fprintf(stderr, "qemu_add_wait_object: failed\n");
+ }
+ } else {
+ DWORD dwId;
+
+ stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread,
+ chr, 0, &dwId);
+
+ if (stdio->hInputThread == INVALID_HANDLE_VALUE
+ || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
+ || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "cannot create stdio thread or event\n");
+ exit(1);
+ }
+ if (qemu_add_wait_object(stdio->hInputReadyEvent,
+ win_stdio_thread_wait_func, chr)) {
+ fprintf(stderr, "qemu_add_wait_object: failed\n");
}
}
dwMode |= ENABLE_LINE_INPUT;
- stdio_clients[stdio_nb_clients++] = chr;
- if (stdio_nb_clients == 1 && is_console) {
+ if (is_console) {
/* set the terminal in raw mode */
/* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
dwMode |= ENABLE_PROCESSED_INPUT;
}
#endif /* !_WIN32 */
+
/***********************************************************/
/* UDP Net console */
typedef struct {
int fd;
+ GIOChannel *chan;
+ guint tag;
uint8_t buf[READ_BUF_LEN];
int bufcnt;
int bufptr;
static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
NetCharDriver *s = chr->opaque;
+ gsize bytes_written;
+ GIOStatus status;
- return send(s->fd, (const void *)buf, len, 0);
+ status = g_io_channel_write_chars(s->chan, (const gchar *)buf, len, &bytes_written, NULL);
+ if (status == G_IO_STATUS_EOF) {
+ return 0;
+ } else if (status != G_IO_STATUS_NORMAL) {
+ return -1;
+ }
+
+ return bytes_written;
}
static int udp_chr_read_poll(void *opaque)
return s->max_size;
}
-static void udp_chr_read(void *opaque)
+static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
NetCharDriver *s = chr->opaque;
+ gsize bytes_read = 0;
+ GIOStatus status;
if (s->max_size == 0)
- return;
- s->bufcnt = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0);
+ return FALSE;
+ status = g_io_channel_read_chars(s->chan, (gchar *)s->buf, sizeof(s->buf),
+ &bytes_read, NULL);
+ s->bufcnt = bytes_read;
s->bufptr = s->bufcnt;
- if (s->bufcnt <= 0)
- return;
+ if (status != G_IO_STATUS_NORMAL) {
+ return FALSE;
+ }
s->bufptr = 0;
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
s->bufptr++;
s->max_size = qemu_chr_be_can_write(chr);
}
+
+ return TRUE;
}
static void udp_chr_update_read_handler(CharDriverState *chr)
{
NetCharDriver *s = chr->opaque;
- if (s->fd >= 0) {
- qemu_set_fd_handler2(s->fd, udp_chr_read_poll,
- udp_chr_read, NULL, chr);
+ if (s->tag) {
+ g_source_remove(s->tag);
+ s->tag = 0;
+ }
+
+ if (s->chan) {
+ s->tag = io_add_watch_poll(s->chan, udp_chr_read_poll, udp_chr_read, chr);
}
}
static void udp_chr_close(CharDriverState *chr)
{
NetCharDriver *s = chr->opaque;
- if (s->fd >= 0) {
- qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ if (s->tag) {
+ g_source_remove(s->tag);
+ }
+ if (s->chan) {
+ g_io_channel_unref(s->chan);
closesocket(s->fd);
}
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_udp_fd(int fd)
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
- Error *local_err = NULL;
- int fd = -1;
chr = g_malloc0(sizeof(CharDriverState));
s = g_malloc0(sizeof(NetCharDriver));
- fd = inet_dgram_opts(opts, &local_err);
- if (fd < 0) {
- goto return_err;
- }
-
s->fd = fd;
+ s->chan = io_channel_from_socket(s->fd);
s->bufcnt = 0;
s->bufptr = 0;
chr->opaque = s;
chr->chr_update_read_handler = udp_chr_update_read_handler;
chr->chr_close = udp_chr_close;
return chr;
+}
-return_err:
- if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
- }
- g_free(chr);
- g_free(s);
- if (fd >= 0) {
- closesocket(fd);
+static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
+{
+ Error *local_err = NULL;
+ int fd = -1;
+
+ fd = inet_dgram_opts(opts, &local_err);
+ if (fd < 0) {
+ return NULL;
}
- return NULL;
+ return qemu_chr_open_udp_fd(fd);
}
/***********************************************************/
/* TCP Net console */
typedef struct {
+
+ GIOChannel *chan, *listen_chan;
+ guint tag, listen_tag;
int fd, listen_fd;
int connected;
int max_size;
int msgfd;
} TCPCharDriver;
-static void tcp_chr_accept(void *opaque);
+static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
if (s->connected) {
- return send_all(s->fd, buf, len);
+ return io_channel_send_all(s->chan, buf, len);
} else {
/* XXX: indicate an error ? */
return len;
}
#endif
-static void tcp_chr_read(void *opaque)
+static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+{
+ TCPCharDriver *s = chr->opaque;
+ return g_io_create_watch(s->chan, cond);
+}
+
+static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
uint8_t buf[READ_BUF_LEN];
int len, size;
- if (!s->connected || s->max_size <= 0)
- return;
+ if (!s->connected || s->max_size <= 0) {
+ return FALSE;
+ }
len = sizeof(buf);
if (len > s->max_size)
len = s->max_size;
if (size == 0) {
/* connection closed */
s->connected = 0;
- if (s->listen_fd >= 0) {
- qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr);
+ if (s->listen_chan) {
+ s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
}
- qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ g_source_remove(s->tag);
+ s->tag = 0;
+ g_io_channel_unref(s->chan);
+ s->chan = NULL;
closesocket(s->fd);
s->fd = -1;
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
if (size > 0)
qemu_chr_be_write(chr, buf, size);
}
+
+ return TRUE;
}
#ifndef _WIN32
TCPCharDriver *s = chr->opaque;
s->connected = 1;
- if (s->fd >= 0) {
- qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
- tcp_chr_read, NULL, chr);
+ if (s->chan) {
+ s->tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, tcp_chr_read, chr);
}
qemu_chr_generic_open(chr);
}
send(fd, (char *)buf, 3, 0);
}
-static void socket_set_nodelay(int fd)
-{
- int val = 1;
- setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
-}
-
static int tcp_chr_add_client(CharDriverState *chr, int fd)
{
TCPCharDriver *s = chr->opaque;
if (s->do_nodelay)
socket_set_nodelay(fd);
s->fd = fd;
- qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL);
+ s->chan = io_channel_from_socket(fd);
+ g_source_remove(s->listen_tag);
+ s->listen_tag = 0;
tcp_chr_connect(chr);
return 0;
}
-static void tcp_chr_accept(void *opaque)
+static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
}
fd = qemu_accept(s->listen_fd, addr, &len);
if (fd < 0 && errno != EINTR) {
- return;
+ return FALSE;
} else if (fd >= 0) {
if (s->do_telnetopt)
tcp_chr_telnet_init(fd);
}
if (tcp_chr_add_client(chr, fd) < 0)
close(fd);
+
+ return TRUE;
}
static void tcp_chr_close(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
if (s->fd >= 0) {
- qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ if (s->tag) {
+ g_source_remove(s->tag);
+ }
+ if (s->chan) {
+ g_io_channel_unref(s->chan);
+ }
closesocket(s->fd);
}
if (s->listen_fd >= 0) {
- qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL);
+ if (s->listen_tag) {
+ g_source_remove(s->listen_tag);
+ }
+ if (s->listen_chan) {
+ g_io_channel_unref(s->listen_chan);
+ }
closesocket(s->listen_fd);
}
g_free(s);
chr->chr_close = tcp_chr_close;
chr->get_msgfd = tcp_get_msgfd;
chr->chr_add_client = tcp_chr_add_client;
+ chr->chr_add_watch = tcp_chr_add_watch;
if (is_listen) {
s->listen_fd = fd;
- qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr);
+ s->listen_chan = io_channel_from_socket(s->listen_fd);
+ s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
if (is_telnet) {
s->do_telnetopt = 1;
}
s->connected = 1;
s->fd = fd;
socket_set_nodelay(fd);
+ s->chan = io_channel_from_socket(s->fd);
tcp_chr_connect(chr);
}
if (is_listen && is_waitconnect) {
printf("QEMU waiting for connection on: %s\n",
chr->filename);
- tcp_chr_accept(chr);
+ tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
socket_set_nonblock(s->listen_fd);
}
return chr;
}
/*********************************************************/
-/*CircularMemory chardev*/
+/* Ring buffer chardev */
typedef struct {
size_t size;
size_t prod;
size_t cons;
uint8_t *cbuf;
-} CirMemCharDriver;
+} RingBufCharDriver;
-static bool cirmem_chr_is_empty(const CharDriverState *chr)
+static size_t ringbuf_count(const CharDriverState *chr)
{
- const CirMemCharDriver *d = chr->opaque;
+ const RingBufCharDriver *d = chr->opaque;
- return d->cons == d->prod;
+ return d->prod - d->cons;
}
-static size_t qemu_chr_cirmem_count(const CharDriverState *chr)
+static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
- const CirMemCharDriver *d = chr->opaque;
-
- return (d->prod - d->cons);
-}
-
-static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
- CirMemCharDriver *d = chr->opaque;
+ RingBufCharDriver *d = chr->opaque;
int i;
if (!buf || (len < 0)) {
}
for (i = 0; i < len; i++ ) {
- /* Avoid writing the IAC information to the queue. */
- if ((unsigned char)buf[i] == IAC) {
- continue;
- }
-
- d->cbuf[d->prod++ % d->size] = buf[i];
- if ((d->prod - d->cons) > d->size) {
+ d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
+ if (d->prod - d->cons > d->size) {
d->cons = d->prod - d->size;
}
}
return 0;
}
-static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len)
+static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
{
- CirMemCharDriver *d = chr->opaque;
+ RingBufCharDriver *d = chr->opaque;
int i;
- for (i = 0; i < len && !cirmem_chr_is_empty(chr); i++) {
- buf[i] = d->cbuf[d->cons++ % d->size];
+ for (i = 0; i < len && d->cons != d->prod; i++) {
+ buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
}
return i;
}
-static void cirmem_chr_close(struct CharDriverState *chr)
+static void ringbuf_chr_close(struct CharDriverState *chr)
{
- CirMemCharDriver *d = chr->opaque;
+ RingBufCharDriver *d = chr->opaque;
g_free(d->cbuf);
g_free(d);
chr->opaque = NULL;
}
-static CharDriverState *qemu_chr_open_cirmemchr(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts,
+ Error **errp)
{
CharDriverState *chr;
- CirMemCharDriver *d;
+ RingBufCharDriver *d;
chr = g_malloc0(sizeof(CharDriverState));
d = g_malloc(sizeof(*d));
- d->size = qemu_opt_get_number(opts, "maxcapacity", 0);
- if (d->size == 0) {
- d->size = CBUFF_SIZE;
- }
+ d->size = opts->has_size ? opts->size : 65536;
/* The size must be power of 2 */
if (d->size & (d->size - 1)) {
- fprintf(stderr, "chardev: size of memory device must be power of 2\n");
+ error_setg(errp, "size of ringbuf chardev must be power of two");
goto fail;
}
d->cbuf = g_malloc0(d->size);
chr->opaque = d;
- chr->chr_write = cirmem_chr_write;
- chr->chr_close = cirmem_chr_close;
+ chr->chr_write = ringbuf_chr_write;
+ chr->chr_close = ringbuf_chr_close;
return chr;
return NULL;
}
-static bool qemu_is_chr(const CharDriverState *chr, const char *filename)
+static bool chr_is_ringbuf(const CharDriverState *chr)
{
- return strcmp(chr->filename, filename);
+ return chr->chr_write == ringbuf_chr_write;
}
-void qmp_memchar_write(const char *device, const char *data,
+void qmp_ringbuf_write(const char *device, const char *data,
bool has_format, enum DataFormat format,
Error **errp)
{
return;
}
- if (qemu_is_chr(chr, "memory")) {
- error_setg(errp,"%s is not memory char device", device);
+ if (!chr_is_ringbuf(chr)) {
+ error_setg(errp,"%s is not a ringbuf device", device);
return;
}
write_count = strlen(data);
}
- ret = cirmem_chr_write(chr, write_data, write_count);
+ ret = ringbuf_chr_write(chr, write_data, write_count);
if (write_data != (uint8_t *)data) {
g_free((void *)write_data);
}
}
-char *qmp_memchar_read(const char *device, int64_t size,
+char *qmp_ringbuf_read(const char *device, int64_t size,
bool has_format, enum DataFormat format,
Error **errp)
{
return NULL;
}
- if (qemu_is_chr(chr, "memory")) {
- error_setg(errp,"%s is not memory char device", device);
+ if (!chr_is_ringbuf(chr)) {
+ error_setg(errp,"%s is not a ringbuf device", device);
return NULL;
}
return NULL;
}
- count = qemu_chr_cirmem_count(chr);
- if (count == 0) {
- return g_strdup("");
- }
-
+ count = ringbuf_count(chr);
size = size > count ? count : size;
- read_data = g_malloc0(size + 1);
+ read_data = g_malloc(size + 1);
- cirmem_chr_read(chr, read_data, size);
+ ringbuf_chr_read(chr, read_data, size);
if (has_format && (format == DATA_FORMAT_BASE64)) {
data = g_base64_encode(read_data, size);
g_free(read_data);
} else {
+ /*
+ * FIXME should read only complete, valid UTF-8 characters up
+ * to @size bytes. Invalid sequences should be replaced by a
+ * suitable replacement character. Except when (and only
+ * when) ring buffer lost characters since last read, initial
+ * continuation characters should be dropped.
+ */
+ read_data[size] = 0;
data = (char *)read_data;
}
qemu_opt_set(opts, "path", filename);
return opts;
}
- if (strstart(filename, "memory", &p)) {
- qemu_opt_set(opts, "backend", "memory");
- qemu_opt_set(opts, "maxcapacity", p);
- return opts;
- }
if (strstart(filename, "file:", &p)) {
qemu_opt_set(opts, "backend", "file");
qemu_opt_set(opts, "path", p);
return NULL;
}
-#ifdef HAVE_CHARDEV_PARPORT
+static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ const char *path = qemu_opt_get(opts, "path");
-static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
+ if (path == NULL) {
+ error_setg(errp, "chardev: file: no filename given");
+ return;
+ }
+ backend->file = g_new0(ChardevFile, 1);
+ backend->file->out = g_strdup(path);
+}
+
+static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
{
- const char *filename = qemu_opt_get(opts, "path");
- int fd;
+ backend->stdio = g_new0(ChardevStdio, 1);
+ backend->stdio->has_signal = true;
+ backend->stdio->signal =
+ qemu_opt_get_bool(opts, "signal", display_type != DT_NOGRAPHIC);
+}
- fd = qemu_open(filename, O_RDWR);
- if (fd < 0) {
- return NULL;
+static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ const char *device = qemu_opt_get(opts, "path");
+
+ if (device == NULL) {
+ error_setg(errp, "chardev: serial/tty: no device path given");
+ return;
}
- return qemu_chr_open_pp_fd(fd);
+ backend->serial = g_new0(ChardevHostdev, 1);
+ backend->serial->device = g_strdup(device);
}
-#endif
+static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ const char *device = qemu_opt_get(opts, "path");
+
+ if (device == NULL) {
+ error_setg(errp, "chardev: parallel: no device path given");
+ return;
+ }
+ backend->parallel = g_new0(ChardevHostdev, 1);
+ backend->parallel->device = g_strdup(device);
+}
+
+static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ const char *device = qemu_opt_get(opts, "path");
+
+ if (device == NULL) {
+ error_setg(errp, "chardev: pipe: no device path given");
+ return;
+ }
+ backend->pipe = g_new0(ChardevHostdev, 1);
+ backend->pipe->device = g_strdup(device);
+}
+
+static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ int val;
+
+ backend->memory = g_new0(ChardevRingbuf, 1);
+
+ val = qemu_opt_get_number(opts, "size", 0);
+ if (val != 0) {
+ backend->memory->has_size = true;
+ backend->memory->size = val;
+ }
+}
-static const struct {
+typedef struct CharDriver {
const char *name;
+ /* old, pre qapi */
CharDriverState *(*open)(QemuOpts *opts);
-} backend_table[] = {
- { .name = "null", .open = qemu_chr_open_null },
- { .name = "socket", .open = qemu_chr_open_socket },
- { .name = "udp", .open = qemu_chr_open_udp },
- { .name = "msmouse", .open = qemu_chr_open_msmouse },
- { .name = "vc", .open = text_console_init },
- { .name = "memory", .open = qemu_chr_open_cirmemchr },
-#ifdef _WIN32
- { .name = "file", .open = qemu_chr_open_win_file_out },
- { .name = "pipe", .open = qemu_chr_open_win_pipe },
- { .name = "console", .open = qemu_chr_open_win_con },
- { .name = "serial", .open = qemu_chr_open_win },
- { .name = "stdio", .open = qemu_chr_open_win_stdio },
-#else
- { .name = "file", .open = qemu_chr_open_file_out },
- { .name = "pipe", .open = qemu_chr_open_pipe },
- { .name = "stdio", .open = qemu_chr_open_stdio },
-#endif
-#ifdef CONFIG_BRLAPI
- { .name = "braille", .open = chr_baum_init },
-#endif
-#ifdef HAVE_CHARDEV_TTY
- { .name = "tty", .open = qemu_chr_open_tty },
- { .name = "serial", .open = qemu_chr_open_tty },
- { .name = "pty", .open = qemu_chr_open_pty },
-#endif
-#ifdef HAVE_CHARDEV_PARPORT
- { .name = "parallel", .open = qemu_chr_open_pp },
- { .name = "parport", .open = qemu_chr_open_pp },
-#endif
-#ifdef CONFIG_SPICE
- { .name = "spicevmc", .open = qemu_chr_open_spice },
-#if SPICE_SERVER_VERSION >= 0x000c02
- { .name = "spiceport", .open = qemu_chr_open_spice_port },
-#endif
-#endif
-};
+ /* new, qapi-based */
+ int kind;
+ void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
+} CharDriver;
+
+static GSList *backends;
+
+void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *))
+{
+ CharDriver *s;
+
+ s = g_malloc0(sizeof(*s));
+ s->name = g_strdup(name);
+ s->open = open;
+
+ backends = g_slist_append(backends, s);
+}
+
+void register_char_driver_qapi(const char *name, int kind,
+ void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp))
+{
+ CharDriver *s;
+
+ s = g_malloc0(sizeof(*s));
+ s->name = g_strdup(name);
+ s->kind = kind;
+ s->parse = parse;
+
+ backends = g_slist_append(backends, s);
+}
CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
void (*init)(struct CharDriverState *s),
Error **errp)
{
+ CharDriver *cd;
CharDriverState *chr;
- int i;
+ GSList *i;
if (qemu_opts_id(opts) == NULL) {
- error_setg(errp, "chardev: no id specified\n");
+ error_setg(errp, "chardev: no id specified");
goto err;
}
if (qemu_opt_get(opts, "backend") == NULL) {
- error_setg(errp, "chardev: \"%s\" missing backend\n",
+ error_setg(errp, "chardev: \"%s\" missing backend",
qemu_opts_id(opts));
goto err;
}
- for (i = 0; i < ARRAY_SIZE(backend_table); i++) {
- if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0)
+ for (i = backends; i; i = i->next) {
+ cd = i->data;
+
+ if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
break;
+ }
}
- if (i == ARRAY_SIZE(backend_table)) {
- error_setg(errp, "chardev: backend \"%s\" not found\n",
+ if (i == NULL) {
+ error_setg(errp, "chardev: backend \"%s\" not found",
qemu_opt_get(opts, "backend"));
- goto err;
+ return NULL;
}
- chr = backend_table[i].open(opts);
+ if (!cd->open) {
+ /* using new, qapi init */
+ ChardevBackend *backend = g_new0(ChardevBackend, 1);
+ ChardevReturn *ret = NULL;
+ const char *id = qemu_opts_id(opts);
+ const char *bid = NULL;
+
+ if (qemu_opt_get_bool(opts, "mux", 0)) {
+ bid = g_strdup_printf("%s-base", id);
+ }
+
+ chr = NULL;
+ backend->kind = cd->kind;
+ if (cd->parse) {
+ cd->parse(opts, backend, errp);
+ if (error_is_set(errp)) {
+ goto qapi_out;
+ }
+ }
+ ret = qmp_chardev_add(bid ? bid : id, backend, errp);
+ if (error_is_set(errp)) {
+ goto qapi_out;
+ }
+
+ if (bid) {
+ qapi_free_ChardevBackend(backend);
+ qapi_free_ChardevReturn(ret);
+ backend = g_new0(ChardevBackend, 1);
+ backend->mux = g_new0(ChardevMux, 1);
+ backend->kind = CHARDEV_BACKEND_KIND_MUX;
+ backend->mux->chardev = g_strdup(bid);
+ ret = qmp_chardev_add(id, backend, errp);
+ if (error_is_set(errp)) {
+ goto qapi_out;
+ }
+ }
+
+ chr = qemu_chr_find(id);
+
+ qapi_out:
+ qapi_free_ChardevBackend(backend);
+ qapi_free_ChardevReturn(ret);
+ return chr;
+ }
+
+ chr = cd->open(opts);
if (!chr) {
- error_setg(errp, "chardev: opening backend \"%s\" failed\n",
+ error_setg(errp, "chardev: opening backend \"%s\" failed",
qemu_opt_get(opts, "backend"));
goto err;
}
}
}
+guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
+ GIOFunc func, void *user_data)
+{
+ GSource *src;
+ guint tag;
+
+ if (s->chr_add_watch == NULL) {
+ return -ENOSYS;
+ }
+
+ src = s->chr_add_watch(s, cond);
+ g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
+ tag = g_source_attach(src, NULL);
+ g_source_unref(src);
+
+ return tag;
+}
+
void qemu_chr_delete(CharDriverState *chr)
{
QTAILQ_REMOVE(&chardevs, chr, next);
.name = "debug",
.type = QEMU_OPT_NUMBER,
},{
- .name = "maxcapacity",
- .type = QEMU_OPT_NUMBER,
+ .name = "size",
+ .type = QEMU_OPT_SIZE,
},
{ /* end of list */ }
},
return qemu_chr_open_win_file(out);
}
-static CharDriverState *qmp_chardev_open_port(ChardevPort *port, Error **errp)
+static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
+ Error **errp)
{
- switch (port->type) {
- case CHARDEV_PORT_KIND_SERIAL:
- return qemu_chr_open_win_path(port->device);
- default:
- error_setg(errp, "unknown chardev port (%d)", port->type);
- return NULL;
- }
+ return qemu_chr_open_win_path(serial->device);
+}
+
+static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
+ Error **errp)
+{
+ error_setg(errp, "character device backend type 'parallel' not supported");
+ return NULL;
}
#else /* WIN32 */
return qemu_chr_open_fd(in, out);
}
-static CharDriverState *qmp_chardev_open_port(ChardevPort *port, Error **errp)
+static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
+ Error **errp)
{
- switch (port->type) {
#ifdef HAVE_CHARDEV_TTY
- case CHARDEV_PORT_KIND_SERIAL:
- {
- int flags, fd;
- flags = O_RDWR;
- fd = qmp_chardev_open_file_source(port->device, flags, errp);
- if (error_is_set(errp)) {
- return NULL;
- }
- socket_set_nonblock(fd);
- return qemu_chr_open_tty_fd(fd);
+ int fd;
+
+ fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
+ if (error_is_set(errp)) {
+ return NULL;
}
+ socket_set_nonblock(fd);
+ return qemu_chr_open_tty_fd(fd);
+#else
+ error_setg(errp, "character device backend type 'serial' not supported");
+ return NULL;
#endif
+}
+
+static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
+ Error **errp)
+{
#ifdef HAVE_CHARDEV_PARPORT
- case CHARDEV_PORT_KIND_PARALLEL:
- {
- int flags, fd;
- flags = O_RDWR;
- fd = qmp_chardev_open_file_source(port->device, flags, errp);
- if (error_is_set(errp)) {
- return NULL;
- }
- return qemu_chr_open_pp_fd(fd);
- }
-#endif
- default:
- error_setg(errp, "unknown chardev port (%d)", port->type);
+ int fd;
+
+ fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
+ if (error_is_set(errp)) {
return NULL;
}
+ return qemu_chr_open_pp_fd(fd);
+#else
+ error_setg(errp, "character device backend type 'parallel' not supported");
+ return NULL;
+#endif
}
#endif /* WIN32 */
is_telnet, is_waitconnect, errp);
}
+static CharDriverState *qmp_chardev_open_dgram(ChardevDgram *dgram,
+ Error **errp)
+{
+ int fd;
+
+ fd = socket_dgram(dgram->remote, dgram->local, errp);
+ if (error_is_set(errp)) {
+ return NULL;
+ }
+ return qemu_chr_open_udp_fd(fd);
+}
+
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
Error **errp)
{
ChardevReturn *ret = g_new0(ChardevReturn, 1);
- CharDriverState *chr = NULL;
+ CharDriverState *base, *chr = NULL;
chr = qemu_chr_find(id);
if (chr) {
case CHARDEV_BACKEND_KIND_FILE:
chr = qmp_chardev_open_file(backend->file, errp);
break;
- case CHARDEV_BACKEND_KIND_PORT:
- chr = qmp_chardev_open_port(backend->port, errp);
+ case CHARDEV_BACKEND_KIND_SERIAL:
+ chr = qmp_chardev_open_serial(backend->serial, errp);
+ break;
+ case CHARDEV_BACKEND_KIND_PARALLEL:
+ chr = qmp_chardev_open_parallel(backend->parallel, errp);
+ break;
+ case CHARDEV_BACKEND_KIND_PIPE:
+ chr = qemu_chr_open_pipe(backend->pipe);
break;
case CHARDEV_BACKEND_KIND_SOCKET:
chr = qmp_chardev_open_socket(backend->socket, errp);
break;
+ case CHARDEV_BACKEND_KIND_DGRAM:
+ chr = qmp_chardev_open_dgram(backend->dgram, errp);
+ break;
#ifdef HAVE_CHARDEV_TTY
case CHARDEV_BACKEND_KIND_PTY:
- {
- /* qemu_chr_open_pty sets "path" in opts */
- QemuOpts *opts;
- opts = qemu_opts_create_nofail(qemu_find_opts("chardev"));
- chr = qemu_chr_open_pty(opts);
- ret->pty = g_strdup(qemu_opt_get(opts, "path"));
- ret->has_pty = true;
- qemu_opts_del(opts);
+ chr = qemu_chr_open_pty(id, ret);
break;
- }
#endif
case CHARDEV_BACKEND_KIND_NULL:
- chr = qemu_chr_open_null(NULL);
+ chr = qemu_chr_open_null();
+ break;
+ case CHARDEV_BACKEND_KIND_MUX:
+ base = qemu_chr_find(backend->mux->chardev);
+ if (base == NULL) {
+ error_setg(errp, "mux: base chardev %s not found",
+ backend->mux->chardev);
+ break;
+ }
+ chr = qemu_chr_open_mux(base);
+ break;
+ case CHARDEV_BACKEND_KIND_MSMOUSE:
+ chr = qemu_chr_open_msmouse();
+ break;
+#ifdef CONFIG_BRLAPI
+ case CHARDEV_BACKEND_KIND_BRAILLE:
+ chr = chr_baum_init();
+ break;
+#endif
+ case CHARDEV_BACKEND_KIND_STDIO:
+ chr = qemu_chr_open_stdio(backend->stdio);
+ break;
+#ifdef _WIN32
+ case CHARDEV_BACKEND_KIND_CONSOLE:
+ chr = qemu_chr_open_win_con();
+ break;
+#endif
+#ifdef CONFIG_SPICE
+ case CHARDEV_BACKEND_KIND_SPICEVMC:
+ chr = qemu_chr_open_spice_vmc(backend->spicevmc->type);
+ break;
+ case CHARDEV_BACKEND_KIND_SPICEPORT:
+ chr = qemu_chr_open_spice_port(backend->spiceport->fqdn);
+ break;
+#endif
+ case CHARDEV_BACKEND_KIND_VC:
+ chr = vc_init(backend->vc);
+ break;
+ case CHARDEV_BACKEND_KIND_MEMORY:
+ chr = qemu_chr_open_ringbuf(backend->memory, errp);
break;
default:
error_setg(errp, "unknown chardev backend (%d)", backend->kind);
}
if (chr) {
chr->label = g_strdup(id);
- chr->avail_connections = 1;
+ chr->avail_connections =
+ (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
QTAILQ_INSERT_TAIL(&chardevs, chr, next);
return ret;
} else {
}
qemu_chr_delete(chr);
}
+
+static void register_types(void)
+{
+ register_char_driver_qapi("null", CHARDEV_BACKEND_KIND_NULL, NULL);
+ register_char_driver("socket", qemu_chr_open_socket);
+ register_char_driver("udp", qemu_chr_open_udp);
+ register_char_driver_qapi("memory", CHARDEV_BACKEND_KIND_MEMORY,
+ qemu_chr_parse_ringbuf);
+ register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE,
+ qemu_chr_parse_file_out);
+ register_char_driver_qapi("stdio", CHARDEV_BACKEND_KIND_STDIO,
+ qemu_chr_parse_stdio);
+ register_char_driver_qapi("serial", CHARDEV_BACKEND_KIND_SERIAL,
+ qemu_chr_parse_serial);
+ register_char_driver_qapi("tty", CHARDEV_BACKEND_KIND_SERIAL,
+ qemu_chr_parse_serial);
+ register_char_driver_qapi("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
+ qemu_chr_parse_parallel);
+ register_char_driver_qapi("parport", CHARDEV_BACKEND_KIND_PARALLEL,
+ qemu_chr_parse_parallel);
+ register_char_driver_qapi("pty", CHARDEV_BACKEND_KIND_PTY, NULL);
+ register_char_driver_qapi("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL);
+ register_char_driver_qapi("pipe", CHARDEV_BACKEND_KIND_PIPE,
+ qemu_chr_parse_pipe);
+}
+
+type_init(register_types);