#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>
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);
}
}
}
#endif /* !_WIN32 */
-#ifndef _WIN32
-
typedef struct IOWatchPoll
{
GSource *src;
return tag;
}
+#ifndef _WIN32
static GIOChannel *io_channel_from_fd(int fd)
{
GIOChannel *chan;
return chan;
}
+#endif
+
+static GIOChannel *io_channel_from_socket(int fd)
+{
+ 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 io_channel_send_all(GIOChannel *fd, const void *_buf, int len1)
{
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) {
+ if (status == G_IO_STATUS_AGAIN) {
+ errno = EAGAIN;
+ return -1;
+ } else {
+ errno = EINVAL;
return -1;
}
} else if (status == G_IO_STATUS_EOF) {
return len1 - len;
}
+#ifndef _WIN32
+
typedef struct FDCharDriver {
CharDriverState *chr;
GIOChannel *fd_in, *fd_out;
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;
s = g_malloc0(sizeof(FDCharDriver));
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;
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;
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)
{
CharDriverState *chr = opaque;
* 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)
/* (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)
{
fd = g_io_channel_unix_get_fd(s->fd);
g_io_channel_unref(s->fd);
close(fd);
- qemu_del_timer(s->timer);
- qemu_free_timer(s->timer);
+ if (s->timer_tag) {
+ g_source_remove(s->timer_tag);
+ }
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
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 = io_channel_from_fd(master_fd);
- s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr);
+ s->timer_tag = 0;
return chr;
}
}
#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);
}
s->fd = fd;
+ s->chan = io_channel_from_socket(s->fd);
s->bufcnt = 0;
s->bufptr = 0;
chr->opaque = s;
/* 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);
}
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;
#endif
-static const struct {
+typedef struct CharDriver {
const char *name;
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 = vc_init },
- { .name = "memory", .open = qemu_chr_open_ringbuf },
-#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
-};
+} 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);
+}
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");
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)) {
+ 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);
+ chr = cd->open(opts);
if (!chr) {
error_setg(errp, "chardev: opening backend \"%s\" failed",
qemu_opt_get(opts, "backend"));
}
}
+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);
}
qemu_chr_delete(chr);
}
+
+static void register_types(void)
+{
+ register_char_driver("null", qemu_chr_open_null);
+ register_char_driver("socket", qemu_chr_open_socket);
+ register_char_driver("udp", qemu_chr_open_udp);
+ register_char_driver("memory", qemu_chr_open_ringbuf);
+#ifdef _WIN32
+ register_char_driver("file", qemu_chr_open_win_file_out);
+ register_char_driver("pipe", qemu_chr_open_win_pipe);
+ register_char_driver("console", qemu_chr_open_win_con);
+ register_char_driver("serial", qemu_chr_open_win);
+ register_char_driver("stdio", qemu_chr_open_win_stdio);
+#else
+ register_char_driver("file", qemu_chr_open_file_out);
+ register_char_driver("pipe", qemu_chr_open_pipe);
+ register_char_driver("stdio", qemu_chr_open_stdio);
+#endif
+#ifdef HAVE_CHARDEV_TTY
+ register_char_driver("tty", qemu_chr_open_tty);
+ register_char_driver("serial", qemu_chr_open_tty);
+ register_char_driver("pty", qemu_chr_open_pty);
+#endif
+#ifdef HAVE_CHARDEV_PARPORT
+ register_char_driver("parallel", qemu_chr_open_pp);
+ register_char_driver("parport", qemu_chr_open_pp);
+#endif
+}
+
+type_init(register_types);