static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
QTAILQ_HEAD_INITIALIZER(chardevs);
+CharDriverState *qemu_chr_alloc(void)
+{
+ CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
+ qemu_mutex_init(&chr->chr_write_lock);
+ return chr;
+}
+
void qemu_chr_be_event(CharDriverState *s, int event)
{
/* Keep track if the char device is open */
int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
{
- return s->chr_write(s, buf, len);
+ int ret;
+
+ qemu_mutex_lock(&s->chr_write_lock);
+ ret = s->chr_write(s, buf, len);
+ qemu_mutex_unlock(&s->chr_write_lock);
+ return ret;
}
int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
{
int offset = 0;
- int res;
+ int res = 0;
+ qemu_mutex_lock(&s->chr_write_lock);
while (offset < len) {
do {
res = s->chr_write(s, buf + offset, len - offset);
}
} while (res == -1 && errno == EAGAIN);
- if (res == 0) {
+ if (res <= 0) {
break;
}
- if (res < 0) {
- return res;
- }
-
offset += res;
}
+ qemu_mutex_unlock(&s->chr_write_lock);
+ if (res < 0) {
+ return res;
+ }
return offset;
}
{
CharDriverState *chr;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
chr->chr_write = null_chr_write;
chr->explicit_be_open = true;
return chr;
int prod[MAX_MUX];
int cons[MAX_MUX];
int timestamps;
+
+ /* Protected by the CharDriverState chr_write_lock. */
int linestart;
int64_t timestamps_start;
} MuxDriver;
+/* Called with chr_write_lock held. */
static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
MuxDriver *d = chr->opaque;
int ret;
if (!d->timestamps) {
- ret = d->drv->chr_write(d->drv, buf, len);
+ ret = qemu_chr_fe_write(d->drv, buf, len);
} else {
int i;
(secs / 60) % 60,
secs % 60,
(int)(ti % 1000));
- d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1));
+ qemu_chr_fe_write(d->drv, (uint8_t *)buf1, strlen(buf1));
d->linestart = 0;
}
- ret += d->drv->chr_write(d->drv, buf+i, 1);
+ ret += qemu_chr_fe_write(d->drv, buf+i, 1);
if (buf[i] == '\n') {
d->linestart = 1;
}
"\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
term_escape_char);
}
- chr->chr_write(chr, (uint8_t *)cbuf, strlen(cbuf));
+ qemu_chr_fe_write(chr, (uint8_t *)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, (uint8_t *)ebuf, strlen(ebuf));
+ qemu_chr_fe_write(chr, (uint8_t *)ebuf, strlen(ebuf));
else
- chr->chr_write(chr, (uint8_t *)&mux_help[i][j], 1);
+ qemu_chr_fe_write(chr, (uint8_t *)&mux_help[i][j], 1);
}
}
}
case 'x':
{
const char *term = "QEMU: Terminated\n\r";
- chr->chr_write(chr,(uint8_t *)term,strlen(term));
+ qemu_chr_fe_write(chr, (uint8_t *)term, strlen(term));
exit(0);
break;
}
.notify = muxes_realize_done,
};
+static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
+{
+ MuxDriver *d = s->opaque;
+ return d->drv->chr_add_watch(d->drv, cond);
+}
+
static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
{
CharDriverState *chr;
MuxDriver *d;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
d = g_malloc0(sizeof(MuxDriver));
chr->opaque = d;
chr->chr_accept_input = mux_chr_accept_input;
/* Frontend guest-open / -close notification is not support with muxes */
chr->chr_set_fe_open = NULL;
+ if (drv->chr_add_watch) {
+ chr->chr_add_watch = mux_chr_add_watch;
+ }
/* only default to opened state if we've realized the initial
* set of muxes
*/
QTAILQ_ENTRY(FDCharDriver) node;
} FDCharDriver;
+/* Called with chr_write_lock held. */
static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
FDCharDriver *s = chr->opaque;
CharDriverState *chr;
FDCharDriver *s;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
s = g_malloc0(sizeof(FDCharDriver));
s->fd_in = io_channel_from_fd(fd_in);
s->fd_out = io_channel_from_fd(fd_out);
typedef struct {
GIOChannel *fd;
- int connected;
int read_bytes;
+
+ /* Protected by the CharDriverState chr_write_lock. */
+ int connected;
guint timer_tag;
+ guint open_tag;
} PtyCharDriver;
-static void pty_chr_update_read_handler(CharDriverState *chr);
+static void pty_chr_update_read_handler_locked(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;
+ qemu_mutex_lock(&chr->chr_write_lock);
s->timer_tag = 0;
+ s->open_tag = 0;
if (!s->connected) {
/* Next poll ... */
- pty_chr_update_read_handler(chr);
+ pty_chr_update_read_handler_locked(chr);
}
+ qemu_mutex_unlock(&chr->chr_write_lock);
return FALSE;
}
+/* Called with chr_write_lock held. */
static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
{
PtyCharDriver *s = chr->opaque;
}
}
+/* Called with chr_write_lock held. */
+static void pty_chr_update_read_handler_locked(CharDriverState *chr)
+{
+ PtyCharDriver *s = chr->opaque;
+ GPollFD pfd;
+
+ pfd.fd = g_io_channel_unix_get_fd(s->fd);
+ pfd.events = G_IO_OUT;
+ pfd.revents = 0;
+ g_poll(&pfd, 1, 0);
+ if (pfd.revents & G_IO_HUP) {
+ pty_chr_state(chr, 0);
+ } else {
+ pty_chr_state(chr, 1);
+ }
+}
+
+static void pty_chr_update_read_handler(CharDriverState *chr)
+{
+ qemu_mutex_lock(&chr->chr_write_lock);
+ pty_chr_update_read_handler_locked(chr);
+ qemu_mutex_unlock(&chr->chr_write_lock);
+}
+
+/* Called with chr_write_lock held. */
static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
PtyCharDriver *s = chr->opaque;
if (!s->connected) {
/* guest sends data, check for (re-)connect */
- pty_chr_update_read_handler(chr);
+ pty_chr_update_read_handler_locked(chr);
return 0;
}
return io_channel_send(s->fd, buf, len);
static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
{
PtyCharDriver *s = chr->opaque;
+ if (!s->connected) {
+ return NULL;
+ }
return g_io_create_watch(s->fd, cond);
}
return TRUE;
}
-static void pty_chr_update_read_handler(CharDriverState *chr)
+static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
{
+ CharDriverState *chr = opaque;
PtyCharDriver *s = chr->opaque;
- GPollFD pfd;
- pfd.fd = g_io_channel_unix_get_fd(s->fd);
- pfd.events = G_IO_OUT;
- pfd.revents = 0;
- g_poll(&pfd, 1, 0);
- if (pfd.revents & G_IO_HUP) {
- pty_chr_state(chr, 0);
- } else {
- pty_chr_state(chr, 1);
- }
+ s->open_tag = 0;
+ qemu_chr_be_generic_open(chr);
+ return FALSE;
}
+/* Called with chr_write_lock held. */
static void pty_chr_state(CharDriverState *chr, int connected)
{
PtyCharDriver *s = chr->opaque;
if (!connected) {
+ if (s->open_tag) {
+ g_source_remove(s->open_tag);
+ s->open_tag = 0;
+ }
remove_fd_in_watch(chr);
s->connected = 0;
/* (re-)connect poll interval for idle guests: once per second.
s->timer_tag = 0;
}
if (!s->connected) {
+ g_assert(s->open_tag == 0);
s->connected = 1;
- qemu_chr_be_generic_open(chr);
+ s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
}
if (!chr->fd_in_tag) {
chr->fd_in_tag = io_add_watch_poll(s->fd, pty_chr_read_poll,
PtyCharDriver *s = chr->opaque;
int fd;
- remove_fd_in_watch(chr);
+ qemu_mutex_lock(&chr->chr_write_lock);
+ pty_chr_state(chr, 0);
fd = g_io_channel_unix_get_fd(s->fd);
g_io_channel_unref(s->fd);
close(fd);
g_source_remove(s->timer_tag);
s->timer_tag = 0;
}
+ qemu_mutex_unlock(&chr->chr_write_lock);
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
close(slave_fd);
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
chr->filename = g_strdup_printf("pty:%s", pty_name);
ret->pty = g_strdup(pty_name);
drv->fd = fd;
drv->mode = IEEE1284_MODE_COMPAT;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
chr->chr_write = null_chr_write;
chr->chr_ioctl = pp_ioctl;
chr->chr_close = pp_close;
{
CharDriverState *chr;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
chr->opaque = (void *)(intptr_t)fd;
chr->chr_write = null_chr_write;
chr->chr_ioctl = pp_ioctl;
typedef struct {
int max_size;
HANDLE hcom, hrecv, hsend;
- OVERLAPPED orecv, osend;
+ OVERLAPPED orecv;
BOOL fpipe;
DWORD len;
+
+ /* Protected by the CharDriverState chr_write_lock. */
+ OVERLAPPED osend;
} WinCharState;
typedef struct {
return -1;
}
+/* Called with chr_write_lock held. */
static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
{
WinCharState *s = chr->opaque;
CharDriverState *chr;
WinCharState *s;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
s = g_malloc0(sizeof(WinCharState));
chr->opaque = s;
chr->chr_write = win_chr_write;
CharDriverState *chr;
WinCharState *s;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
s = g_malloc0(sizeof(WinCharState));
chr->opaque = s;
chr->chr_write = win_chr_write;
CharDriverState *chr;
WinCharState *s;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
s = g_malloc0(sizeof(WinCharState));
s->hcom = fd_out;
chr->opaque = s;
DWORD dwMode;
int is_console = 0;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
stdio = g_malloc0(sizeof(WinStdioCharState));
stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
int max_size;
} NetCharDriver;
+/* Called with chr_write_lock held. */
static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
NetCharDriver *s = chr->opaque;
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
s = g_malloc0(sizeof(NetCharDriver));
s->fd = fd;
}
#endif
+/* Called with chr_write_lock held. */
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
if (to_copy) {
+ int i;
+
memcpy(fds, s->read_msgfds, to_copy * sizeof(int));
+ /* Close unused fds */
+ for (i = to_copy; i < s->read_msgfds_num; i++) {
+ close(s->read_msgfds[i]);
+ }
+
g_free(s->read_msgfds);
s->read_msgfds = 0;
s->read_msgfds_num = 0;
uint8_t buf[READ_BUF_LEN];
int len, size;
+ if (cond & G_IO_HUP) {
+ /* connection closed */
+ tcp_chr_disconnect(chr);
+ return TRUE;
+ }
+
if (!s->connected || s->max_size <= 0) {
return TRUE;
}
}
#endif
-static gboolean tcp_chr_chan_close(GIOChannel *channel, GIOCondition cond,
- void *opaque)
-{
- CharDriverState *chr = opaque;
-
- if (cond != G_IO_HUP) {
- return FALSE;
- }
-
- /* connection closed */
- tcp_chr_disconnect(chr);
- if (chr->fd_hup_tag) {
- g_source_remove(chr->fd_hup_tag);
- chr->fd_hup_tag = 0;
- }
-
- return TRUE;
-}
-
static void tcp_chr_connect(void *opaque)
{
CharDriverState *chr = opaque;
if (s->chan) {
chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
tcp_chr_read, chr);
- chr->fd_hup_tag = g_io_add_watch(s->chan, G_IO_HUP, tcp_chr_chan_close,
- chr);
}
qemu_chr_be_generic_open(chr);
}
return NULL;
}
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
s = g_malloc0(sizeof(TCPCharDriver));
s->connected = 0;
return d->prod - d->cons;
}
+/* Called with chr_write_lock held. */
static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
RingBufCharDriver *d = chr->opaque;
RingBufCharDriver *d = chr->opaque;
int i;
+ qemu_mutex_lock(&chr->chr_write_lock);
for (i = 0; i < len && d->cons != d->prod; i++) {
buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
}
+ qemu_mutex_unlock(&chr->chr_write_lock);
return i;
}
CharDriverState *chr;
RingBufCharDriver *d;
- chr = g_malloc0(sizeof(CharDriverState));
+ chr = qemu_chr_alloc();
d = g_malloc(sizeof(*d));
d->size = opts->has_size ? opts->size : 65536;
strcmp(filename, "pty") == 0 ||
strcmp(filename, "msmouse") == 0 ||
strcmp(filename, "braille") == 0 ||
+ strcmp(filename, "testdev") == 0 ||
strcmp(filename, "stdio") == 0) {
qemu_opt_set(opts, "backend", filename);
return opts;
}
src = s->chr_add_watch(s, cond);
+ if (!src) {
+ return -EINVAL;
+ }
+
g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
tag = g_source_attach(src, NULL);
g_source_unref(src);
info->value = g_malloc0(sizeof(*info->value));
info->value->label = g_strdup(chr->label);
info->value->filename = g_strdup(chr->filename);
+ info->value->frontend_open = chr->fe_open;
info->next = chr_list;
chr_list = info;
chr = chr_baum_init();
break;
#endif
+ case CHARDEV_BACKEND_KIND_TESTDEV:
+ chr = chr_testdev_init();
+ break;
case CHARDEV_BACKEND_KIND_STDIO:
chr = qemu_chr_open_stdio(backend->stdio);
break;