#define READ_RETRIES 10
#define TCP_MAX_FDS 16
+typedef struct MuxChardev MuxChardev;
+
/***********************************************************/
/* Socket address helpers */
/***********************************************************/
/* character device */
-static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
+static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
QTAILQ_HEAD_INITIALIZER(chardevs);
-static void qemu_chr_free_common(CharDriverState *chr);
-
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp)
+void qemu_chr_be_event(Chardev *s, int event)
{
- CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
- qemu_mutex_init(&chr->chr_write_lock);
-
- if (backend->has_logfile) {
- int flags = O_WRONLY | O_CREAT;
- if (backend->has_logappend &&
- backend->logappend) {
- flags |= O_APPEND;
- } else {
- flags |= O_TRUNC;
- }
- chr->logfd = qemu_open(backend->logfile, flags, 0666);
- if (chr->logfd < 0) {
- error_setg_errno(errp, errno,
- "Unable to open logfile %s",
- backend->logfile);
- g_free(chr);
- return NULL;
- }
- } else {
- chr->logfd = -1;
- }
-
- return chr;
-}
+ CharBackend *be = s->be;
-void qemu_chr_be_event(CharDriverState *s, int event)
-{
/* Keep track if the char device is open */
switch (event) {
case CHR_EVENT_OPENED:
break;
}
- if (!s->chr_event)
+ if (!be || !be->chr_event) {
return;
- s->chr_event(s->handler_opaque, event);
+ }
+
+ be->chr_event(be->opaque, event);
}
-void qemu_chr_be_generic_open(CharDriverState *s)
+void qemu_chr_be_generic_open(Chardev *s)
{
qemu_chr_be_event(s, CHR_EVENT_OPENED);
}
/* Not reporting errors from writing to logfile, as logs are
* defined to be "best effort" only */
-static void qemu_chr_fe_write_log(CharDriverState *s,
+static void qemu_chr_fe_write_log(Chardev *s,
const uint8_t *buf, size_t len)
{
size_t done = 0;
}
}
-static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int len, int *offset)
+static int qemu_chr_fe_write_buffer(Chardev *s,
+ const uint8_t *buf, int len, int *offset)
{
+ ChardevClass *cc = CHARDEV_GET_CLASS(s);
int res = 0;
*offset = 0;
qemu_mutex_lock(&s->chr_write_lock);
while (*offset < len) {
retry:
- res = s->chr_write(s, buf + *offset, len - *offset);
+ res = cc->chr_write(s, buf + *offset, len - *offset);
if (res < 0 && errno == EAGAIN) {
g_usleep(100);
goto retry;
return res;
}
-int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
+static bool qemu_chr_replay(Chardev *chr)
+{
+ return qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+}
+
+int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
{
+ Chardev *s = be->chr;
+ ChardevClass *cc;
int ret;
- if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+ if (!s) {
+ return 0;
+ }
+
+ if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
int offset;
replay_char_write_event_load(&ret, &offset);
assert(offset <= len);
return ret;
}
+ cc = CHARDEV_GET_CLASS(s);
qemu_mutex_lock(&s->chr_write_lock);
- ret = s->chr_write(s, buf, len);
+ ret = cc->chr_write(s, buf, len);
if (ret > 0) {
qemu_chr_fe_write_log(s, buf, ret);
qemu_mutex_unlock(&s->chr_write_lock);
- if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
}
return ret;
}
-int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
+static int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
{
int offset;
int res;
- if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+ if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
replay_char_write_event_load(&res, &offset);
assert(offset <= len);
qemu_chr_fe_write_buffer(s, buf, offset, &offset);
res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
- if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
replay_char_write_event_save(res, offset);
}
return offset;
}
-int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
+int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
+{
+ Chardev *s = be->chr;
+
+ if (!s) {
+ return 0;
+ }
+
+ return qemu_chr_write_all(s, buf, len);
+}
+
+int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
{
+ Chardev *s = be->chr;
int offset = 0, counter = 10;
int res;
- if (!s->chr_sync_read) {
+ if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
return 0;
}
-
- if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+
+ if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
return replay_char_read_all_load(buf);
}
while (offset < len) {
retry:
- res = s->chr_sync_read(s, buf + offset, len - offset);
+ res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
+ len - offset);
if (res == -1 && errno == EAGAIN) {
g_usleep(100);
goto retry;
}
if (res < 0) {
- if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
replay_char_read_all_save_error(res);
}
return res;
}
}
- if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
replay_char_read_all_save_buf(buf, offset);
}
return offset;
}
-int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
+int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
{
+ Chardev *s = be->chr;
int res;
- if (!s->chr_ioctl || s->replay) {
+
+ if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
res = -ENOTSUP;
} else {
- res = s->chr_ioctl(s, cmd, arg);
+ res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
}
return res;
}
-int qemu_chr_be_can_write(CharDriverState *s)
+int qemu_chr_be_can_write(Chardev *s)
{
- if (!s->chr_can_read)
+ CharBackend *be = s->be;
+
+ if (!be || !be->chr_can_read) {
return 0;
- return s->chr_can_read(s->handler_opaque);
+ }
+
+ return be->chr_can_read(be->opaque);
}
-void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
+void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len)
{
- if (s->chr_read) {
- s->chr_read(s->handler_opaque, buf, len);
+ CharBackend *be = s->be;
+
+ if (be && be->chr_read) {
+ be->chr_read(be->opaque, buf, len);
}
}
-void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
{
- if (s->replay) {
+ if (qemu_chr_replay(s)) {
if (replay_mode == REPLAY_MODE_PLAY) {
return;
}
}
}
-int qemu_chr_fe_get_msgfd(CharDriverState *s)
+int qemu_chr_fe_get_msgfd(CharBackend *be)
{
+ Chardev *s = be->chr;
int fd;
- int res = (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
- if (s->replay) {
- fprintf(stderr,
- "Replay: get msgfd is not supported for serial devices yet\n");
+ int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
+ if (s && qemu_chr_replay(s)) {
+ error_report("Replay: get msgfd is not supported "
+ "for serial devices yet");
exit(1);
}
return res;
}
-int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len)
+int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
{
- return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
+ Chardev *s = be->chr;
+
+ if (!s) {
+ return -1;
+ }
+
+ return CHARDEV_GET_CLASS(s)->get_msgfds ?
+ CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1;
}
-int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num)
+int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
{
- return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1;
+ Chardev *s = be->chr;
+
+ if (!s) {
+ return -1;
+ }
+
+ return CHARDEV_GET_CLASS(s)->set_msgfds ?
+ CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1;
}
-int qemu_chr_add_client(CharDriverState *s, int fd)
+int qemu_chr_add_client(Chardev *s, int fd)
{
- return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
+ return CHARDEV_GET_CLASS(s)->chr_add_client ?
+ CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1;
}
-void qemu_chr_accept_input(CharDriverState *s)
+void qemu_chr_fe_accept_input(CharBackend *be)
{
- if (s->chr_accept_input)
- s->chr_accept_input(s);
+ Chardev *s = be->chr;
+
+ if (!s) {
+ return;
+ }
+
+ if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
+ CHARDEV_GET_CLASS(s)->chr_accept_input(s);
+ }
qemu_notify_event();
}
-void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...)
+void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
{
char buf[READ_BUF_LEN];
va_list ap;
vsnprintf(buf, sizeof(buf), fmt, ap);
/* XXX this blocks entire thread. Rewrite to use
* qemu_chr_fe_write and background I/O callbacks */
- qemu_chr_fe_write_all(s, (uint8_t *)buf, strlen(buf));
+ qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
va_end(ap);
}
-static void remove_fd_in_watch(CharDriverState *chr);
+static void remove_fd_in_watch(Chardev *chr);
+static void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
+static void mux_set_focus(Chardev *chr, int focus);
-void qemu_chr_add_handlers_full(CharDriverState *s,
- IOCanReadHandler *fd_can_read,
- IOReadHandler *fd_read,
- IOEventHandler *fd_event,
- void *opaque,
- GMainContext *context)
+static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
+ bool *be_opened, Error **errp)
{
- int fe_open;
+ ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+ /* Any ChardevCommon member would work */
+ ChardevCommon *common = backend ? backend->u.null.data : NULL;
- if (!opaque && !fd_can_read && !fd_read && !fd_event) {
- fe_open = 0;
- remove_fd_in_watch(s);
- } else {
- fe_open = 1;
- }
- s->chr_can_read = fd_can_read;
- s->chr_read = fd_read;
- s->chr_event = fd_event;
- s->handler_opaque = opaque;
- if (fe_open && s->chr_update_read_handler) {
- s->chr_update_read_handler(s, context);
+ if (common && common->has_logfile) {
+ int flags = O_WRONLY | O_CREAT;
+ if (common->has_logappend &&
+ common->logappend) {
+ flags |= O_APPEND;
+ } else {
+ flags |= O_TRUNC;
+ }
+ chr->logfd = qemu_open(common->logfile, flags, 0666);
+ if (chr->logfd < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to open logfile %s",
+ common->logfile);
+ return;
+ }
}
- if (!s->explicit_fe_open) {
- qemu_chr_fe_set_open(s, fe_open);
+ if (cc->open) {
+ cc->open(chr, backend, be_opened, errp);
}
+}
- /* We're connecting to an already opened device, so let's make sure we
- also get the open event */
- if (fe_open && s->be_open) {
- qemu_chr_be_generic_open(s);
- }
+static void char_init(Object *obj)
+{
+ Chardev *chr = CHARDEV(obj);
+
+ chr->logfd = -1;
+ qemu_mutex_init(&chr->chr_write_lock);
}
-void qemu_chr_add_handlers(CharDriverState *s,
- IOCanReadHandler *fd_can_read,
- IOReadHandler *fd_read,
- IOEventHandler *fd_event,
- void *opaque)
+static void char_finalize(Object *obj)
{
- qemu_chr_add_handlers_full(s, fd_can_read, fd_read,
- fd_event, opaque, NULL);
+ Chardev *chr = CHARDEV(obj);
+
+ if (chr->be) {
+ chr->be->chr = NULL;
+ }
+ g_free(chr->filename);
+ g_free(chr->label);
+ if (chr->logfd != -1) {
+ close(chr->logfd);
+ }
+ qemu_mutex_destroy(&chr->chr_write_lock);
}
-static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static const TypeInfo char_type_info = {
+ .name = TYPE_CHARDEV,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(Chardev),
+ .instance_init = char_init,
+ .instance_finalize = char_finalize,
+ .abstract = true,
+ .class_size = sizeof(ChardevClass),
+};
+
+static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
return len;
}
-static CharDriverState *qemu_chr_open_null(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void null_chr_open(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
- CharDriverState *chr;
- ChardevCommon *common = backend->u.null.data;
+ *be_opened = false;
+}
- chr = qemu_chr_alloc(common, errp);
- if (!chr) {
- return NULL;
- }
- chr->chr_write = null_chr_write;
- chr->explicit_be_open = true;
- return chr;
+static const CharDriver null_driver = {
+ .kind = CHARDEV_BACKEND_KIND_NULL,
+};
+
+static void char_null_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = null_chr_open;
+ cc->chr_write = null_chr_write;
}
+static const TypeInfo char_null_type_info = {
+ .name = TYPE_CHARDEV_NULL,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(Chardev),
+ .class_init = char_null_class_init,
+};
+
/* MUX driver for serial I/O splitting */
#define MAX_MUX 4
#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
-typedef struct {
- IOCanReadHandler *chr_can_read[MAX_MUX];
- IOReadHandler *chr_read[MAX_MUX];
- IOEventHandler *chr_event[MAX_MUX];
- void *ext_opaque[MAX_MUX];
- CharDriverState *drv;
+struct MuxChardev {
+ Chardev parent;
+ CharBackend *backends[MAX_MUX];
+ CharBackend chr;
int focus;
int mux_cnt;
int term_got_escape;
int cons[MAX_MUX];
int timestamps;
- /* Protected by the CharDriverState chr_write_lock. */
+ /* Protected by the Chardev chr_write_lock. */
int linestart;
int64_t timestamps_start;
-} MuxDriver;
+};
+#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
/* Called with chr_write_lock held. */
-static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
- MuxDriver *d = chr->opaque;
+ MuxChardev *d = MUX_CHARDEV(chr);
int ret;
if (!d->timestamps) {
- ret = qemu_chr_fe_write(d->drv, buf, len);
+ ret = qemu_chr_fe_write(&d->chr, buf, len);
} else {
int i;
(int)(ti % 1000));
/* XXX this blocks entire thread. Rewrite to use
* qemu_chr_fe_write and background I/O callbacks */
- qemu_chr_fe_write_all(d->drv, (uint8_t *)buf1, strlen(buf1));
+ qemu_chr_fe_write_all(&d->chr,
+ (uint8_t *)buf1, strlen(buf1));
d->linestart = 0;
}
- ret += qemu_chr_fe_write(d->drv, buf+i, 1);
+ ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
if (buf[i] == '\n') {
d->linestart = 1;
}
};
int term_escape_char = 0x01; /* ctrl-a is used for escape */
-static void mux_print_help(CharDriverState *chr)
+static void mux_print_help(Chardev *chr)
{
int i, j;
char ebuf[15] = "Escape-Char";
}
/* XXX this blocks entire thread. Rewrite to use
* qemu_chr_fe_write and background I/O callbacks */
- qemu_chr_fe_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
+ qemu_chr_write_all(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] == '%')
- qemu_chr_fe_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
+ qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
else
- qemu_chr_fe_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
+ qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
}
}
}
-static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event)
+static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
{
- if (d->chr_event[mux_nr])
- d->chr_event[mux_nr](d->ext_opaque[mux_nr], event);
+ CharBackend *be = d->backends[mux_nr];
+
+ if (be && be->chr_event) {
+ be->chr_event(be->opaque, event);
+ }
}
-static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
+static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
{
if (d->term_got_escape) {
d->term_got_escape = 0;
case 'x':
{
const char *term = "QEMU: Terminated\n\r";
- qemu_chr_fe_write_all(chr, (uint8_t *)term, strlen(term));
+ qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
exit(0);
break;
}
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
break;
case 'c':
+ assert(d->mux_cnt > 0); /* handler registered with first fe */
/* Switch to the next registered device */
- mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
- d->focus++;
- if (d->focus >= d->mux_cnt)
- d->focus = 0;
- mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
+ mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
break;
case 't':
d->timestamps = !d->timestamps;
return 0;
}
-static void mux_chr_accept_input(CharDriverState *chr)
+static void mux_chr_accept_input(Chardev *chr)
{
- MuxDriver *d = chr->opaque;
+ MuxChardev *d = MUX_CHARDEV(chr);
int m = d->focus;
+ CharBackend *be = d->backends[m];
- while (d->prod[m] != d->cons[m] &&
- d->chr_can_read[m] &&
- d->chr_can_read[m](d->ext_opaque[m])) {
- d->chr_read[m](d->ext_opaque[m],
- &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
+ while (be && d->prod[m] != d->cons[m] &&
+ be->chr_can_read && be->chr_can_read(be->opaque)) {
+ be->chr_read(be->opaque,
+ &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
}
}
static int mux_chr_can_read(void *opaque)
{
- CharDriverState *chr = opaque;
- MuxDriver *d = chr->opaque;
+ MuxChardev *d = MUX_CHARDEV(opaque);
int m = d->focus;
+ CharBackend *be = d->backends[m];
- if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE)
+ if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
return 1;
- if (d->chr_can_read[m])
- return d->chr_can_read[m](d->ext_opaque[m]);
+ }
+
+ if (be && be->chr_can_read) {
+ return be->chr_can_read(be->opaque);
+ }
+
return 0;
}
static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
{
- CharDriverState *chr = opaque;
- MuxDriver *d = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ MuxChardev *d = MUX_CHARDEV(opaque);
int m = d->focus;
+ CharBackend *be = d->backends[m];
int i;
- mux_chr_accept_input (opaque);
+ mux_chr_accept_input(opaque);
- for(i = 0; i < size; i++)
+ for (i = 0; i < size; i++)
if (mux_proc_byte(chr, d, buf[i])) {
if (d->prod[m] == d->cons[m] &&
- d->chr_can_read[m] &&
- d->chr_can_read[m](d->ext_opaque[m]))
- d->chr_read[m](d->ext_opaque[m], &buf[i], 1);
+ be && be->chr_can_read &&
+ be->chr_can_read(be->opaque))
+ be->chr_read(be->opaque, &buf[i], 1);
else
d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
}
}
+static bool muxes_realized;
+
static void mux_chr_event(void *opaque, int event)
{
- CharDriverState *chr = opaque;
- MuxDriver *d = chr->opaque;
+ MuxChardev *d = MUX_CHARDEV(opaque);
int i;
+ if (!muxes_realized) {
+ return;
+ }
+
/* Send the event to all registered listeners */
for (i = 0; i < d->mux_cnt; i++)
mux_chr_send_event(d, i, event);
}
-static void mux_chr_update_read_handler(CharDriverState *chr,
- GMainContext *context)
-{
- 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_full(d->drv, mux_chr_can_read,
- mux_chr_read,
- mux_chr_event,
- chr, context);
- }
- if (d->focus != -1) {
- mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
- }
- d->focus = d->mux_cnt;
- d->mux_cnt++;
- mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
-}
-
-static bool muxes_realized;
-
/**
* Called after processing of default and command-line-specified
* chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
*/
static void muxes_realize_done(Notifier *notifier, void *unused)
{
- CharDriverState *chr;
+ Chardev *chr;
QTAILQ_FOREACH(chr, &chardevs, next) {
- if (chr->is_mux) {
- MuxDriver *d = chr->opaque;
+ if (CHARDEV_IS_MUX(chr)) {
+ MuxChardev *d = MUX_CHARDEV(chr);
int i;
/* send OPENED to all already-attached FEs */
.notify = muxes_realize_done,
};
-static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
+static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
+{
+ MuxChardev *d = MUX_CHARDEV(s);
+ Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
+ ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+
+ if (!cc->chr_add_watch) {
+ return NULL;
+ }
+
+ return cc->chr_add_watch(chr, cond);
+}
+
+static void mux_chr_free(struct Chardev *chr)
+{
+ MuxChardev *d = MUX_CHARDEV(chr);
+ int i;
+
+ for (i = 0; i < d->mux_cnt; i++) {
+ CharBackend *be = d->backends[i];
+ if (be) {
+ be->chr = NULL;
+ }
+ }
+ qemu_chr_fe_deinit(&d->chr);
+}
+
+static void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
{
- MuxDriver *d = s->opaque;
- return d->drv->chr_add_watch(d->drv, cond);
+ MuxChardev *d = MUX_CHARDEV(chr);
+
+ /* Fix up the real driver with mux routines */
+ qemu_chr_fe_set_handlers(&d->chr,
+ mux_chr_can_read,
+ mux_chr_read,
+ mux_chr_event,
+ chr,
+ context, true);
}
-static void mux_chr_close(struct CharDriverState *chr)
+static void mux_set_focus(Chardev *chr, int focus)
{
- MuxDriver *d = chr->opaque;
+ MuxChardev *d = MUX_CHARDEV(chr);
+
+ assert(focus >= 0);
+ assert(focus < d->mux_cnt);
+
+ if (d->focus != -1) {
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
+ }
- g_free(d);
+ d->focus = focus;
+ chr->be = d->backends[focus];
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
}
-static CharDriverState *qemu_chr_open_mux(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret, Error **errp)
+static void qemu_chr_open_mux(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevMux *mux = backend->u.mux.data;
- CharDriverState *chr, *drv;
- MuxDriver *d;
- ChardevCommon *common = qapi_ChardevMux_base(mux);
+ Chardev *drv;
+ MuxChardev *d = MUX_CHARDEV(chr);
drv = qemu_chr_find(mux->chardev);
if (drv == NULL) {
error_setg(errp, "mux: base chardev %s not found", mux->chardev);
- return NULL;
- }
-
- chr = qemu_chr_alloc(common, errp);
- if (!chr) {
- return NULL;
+ return;
}
- d = g_new0(MuxDriver, 1);
- chr->opaque = d;
- d->drv = drv;
d->focus = -1;
- chr->chr_close = mux_chr_close;
- chr->chr_write = mux_chr_write;
- chr->chr_update_read_handler = mux_chr_update_read_handler;
- 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
*/
- chr->explicit_be_open = muxes_realized ? 0 : 1;
- chr->is_mux = 1;
+ *be_opened = muxes_realized;
+ qemu_chr_fe_init(&d->chr, drv, errp);
+}
- return chr;
+Chardev *qemu_chr_fe_get_driver(CharBackend *be)
+{
+ return be->chr;
+}
+
+bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
+{
+ int tag = 0;
+
+ if (CHARDEV_IS_MUX(s)) {
+ MuxChardev *d = MUX_CHARDEV(s);
+
+ if (d->mux_cnt >= MAX_MUX) {
+ goto unavailable;
+ }
+
+ d->backends[d->mux_cnt] = b;
+ tag = d->mux_cnt++;
+ } else if (s->be) {
+ goto unavailable;
+ } else {
+ s->be = b;
+ }
+
+ b->fe_open = false;
+ b->tag = tag;
+ b->chr = s;
+ return true;
+
+unavailable:
+ error_setg(errp, QERR_DEVICE_IN_USE, s->label);
+ return false;
+}
+
+static bool qemu_chr_is_busy(Chardev *s)
+{
+ if (CHARDEV_IS_MUX(s)) {
+ MuxChardev *d = MUX_CHARDEV(s);
+ return d->mux_cnt >= 0;
+ } else {
+ return s->be != NULL;
+ }
+}
+
+void qemu_chr_fe_deinit(CharBackend *b)
+{
+ assert(b);
+
+ if (b->chr) {
+ qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
+ if (b->chr->be == b) {
+ b->chr->be = NULL;
+ }
+ if (CHARDEV_IS_MUX(b->chr)) {
+ MuxChardev *d = MUX_CHARDEV(b->chr);
+ d->backends[b->tag] = NULL;
+ }
+ b->chr = NULL;
+ }
+}
+
+void qemu_chr_fe_set_handlers(CharBackend *b,
+ IOCanReadHandler *fd_can_read,
+ IOReadHandler *fd_read,
+ IOEventHandler *fd_event,
+ void *opaque,
+ GMainContext *context,
+ bool set_open)
+{
+ Chardev *s;
+ ChardevClass *cc;
+ int fe_open;
+
+ s = b->chr;
+ if (!s) {
+ return;
+ }
+
+ cc = CHARDEV_GET_CLASS(s);
+ if (!opaque && !fd_can_read && !fd_read && !fd_event) {
+ fe_open = 0;
+ remove_fd_in_watch(s);
+ } else {
+ fe_open = 1;
+ }
+ b->chr_can_read = fd_can_read;
+ b->chr_read = fd_read;
+ b->chr_event = fd_event;
+ b->opaque = opaque;
+ if (cc->chr_update_read_handler) {
+ cc->chr_update_read_handler(s, context);
+ }
+
+ if (set_open) {
+ qemu_chr_fe_set_open(b, fe_open);
+ }
+
+ if (fe_open) {
+ qemu_chr_fe_take_focus(b);
+ /* We're connecting to an already opened device, so let's make sure we
+ also get the open event */
+ if (s->be_open) {
+ qemu_chr_be_generic_open(s);
+ }
+ }
+
+ if (CHARDEV_IS_MUX(s)) {
+ mux_chr_set_handlers(s, context);
+ }
}
+void qemu_chr_fe_take_focus(CharBackend *b)
+{
+ if (!b->chr) {
+ return;
+ }
+
+ if (CHARDEV_IS_MUX(b->chr)) {
+ mux_set_focus(b->chr, b->tag);
+ }
+}
typedef struct IOWatchPoll
{
};
/* Can only be used for read */
-static guint io_add_watch_poll(QIOChannel *ioc,
+static guint io_add_watch_poll(Chardev *chr,
+ QIOChannel *ioc,
IOCanReadHandler *fd_can_read,
QIOChannelFunc fd_read,
gpointer user_data,
{
IOWatchPoll *iwp;
int tag;
+ char *name;
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs,
sizeof(IOWatchPoll));
iwp->src = NULL;
iwp->context = context;
+ name = g_strdup_printf("chardev-iowatch-%s", chr->label);
+ g_source_set_name((GSource *)iwp, name);
+ g_free(name);
+
tag = g_source_attach(&iwp->parent, context);
g_source_unref(&iwp->parent);
return tag;
g_source_destroy(&iwp->parent);
}
-static void remove_fd_in_watch(CharDriverState *chr)
+static void remove_fd_in_watch(Chardev *chr)
{
if (chr->fd_in_tag) {
io_remove_watch_poll(chr->fd_in_tag);
return io_channel_send_full(ioc, buf, len, NULL, 0);
}
-
-typedef struct FDCharDriver {
- CharDriverState *chr;
+typedef struct FDChardev {
+ Chardev parent;
+ Chardev *chr;
QIOChannel *ioc_in, *ioc_out;
int max_size;
-} FDCharDriver;
+} FDChardev;
+
+#define TYPE_CHARDEV_FD "chardev-fd"
+#define FD_CHARDEV(obj) OBJECT_CHECK(FDChardev, (obj), TYPE_CHARDEV_FD)
/* Called with chr_write_lock held. */
-static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
- FDCharDriver *s = chr->opaque;
-
+ FDChardev *s = FD_CHARDEV(chr);
+
return io_channel_send(s->ioc_out, buf, len);
}
static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
{
- CharDriverState *chr = opaque;
- FDCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ FDChardev *s = FD_CHARDEV(opaque);
int len;
uint8_t buf[READ_BUF_LEN];
ssize_t ret;
static int fd_chr_read_poll(void *opaque)
{
- CharDriverState *chr = opaque;
- FDCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ FDChardev *s = FD_CHARDEV(opaque);
s->max_size = qemu_chr_be_can_write(chr);
return s->max_size;
}
-static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
{
- FDCharDriver *s = chr->opaque;
+ FDChardev *s = FD_CHARDEV(chr);
return qio_channel_create_watch(s->ioc_out, cond);
}
-static void fd_chr_update_read_handler(CharDriverState *chr,
+static void fd_chr_update_read_handler(Chardev *chr,
GMainContext *context)
{
- FDCharDriver *s = chr->opaque;
+ FDChardev *s = FD_CHARDEV(chr);
remove_fd_in_watch(chr);
if (s->ioc_in) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc_in,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in,
fd_chr_read_poll,
fd_chr_read, chr,
context);
}
}
-static void fd_chr_close(struct CharDriverState *chr)
+static void fd_chr_free(struct Chardev *chr)
{
- FDCharDriver *s = chr->opaque;
+ FDChardev *s = FD_CHARDEV(chr);
remove_fd_in_watch(chr);
if (s->ioc_in) {
object_unref(OBJECT(s->ioc_out));
}
- g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
/* open a character device to a unix fd */
-static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
- ChardevCommon *backend, Error **errp)
+static void qemu_chr_open_fd(Chardev *chr,
+ int fd_in, int fd_out)
{
- CharDriverState *chr;
- FDCharDriver *s;
+ FDChardev *s = FD_CHARDEV(chr);
+ char *name;
- chr = qemu_chr_alloc(backend, errp);
- if (!chr) {
- return NULL;
- }
- s = g_new0(FDCharDriver, 1);
s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
+ name = g_strdup_printf("chardev-file-in-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
+ g_free(name);
s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
+ name = g_strdup_printf("chardev-file-out-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name);
+ g_free(name);
qemu_set_nonblock(fd_out);
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 void char_fd_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->chr_add_watch = fd_chr_add_watch;
+ cc->chr_write = fd_chr_write;
+ cc->chr_update_read_handler = fd_chr_update_read_handler;
+ cc->chr_free = fd_chr_free;
}
-static CharDriverState *qemu_chr_open_pipe(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static const TypeInfo char_fd_type_info = {
+ .name = TYPE_CHARDEV_FD,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(FDChardev),
+ .class_init = char_fd_class_init,
+ .abstract = true,
+};
+
+static void qemu_chr_open_pipe(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevHostdev *opts = backend->u.pipe.data;
int fd_in, fd_out;
char *filename_in;
char *filename_out;
const char *filename = opts->device;
- ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
filename_in = g_strdup_printf("%s.in", filename);
filename_out = g_strdup_printf("%s.out", filename);
TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
if (fd_in < 0) {
error_setg_file_open(errp, errno, filename);
- return NULL;
+ return;
}
}
- return qemu_chr_open_fd(fd_in, fd_out, common, errp);
+ qemu_chr_open_fd(chr, fd_in, fd_out);
}
/* init terminal so that we can grab keys */
static bool stdio_allow_signal;
static bool stdio_echo_state;
-static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo);
+static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo);
static void term_exit(void)
{
qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
}
-static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
+static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo)
{
struct termios tty;
tcsetattr (0, TCSANOW, &tty);
}
-static void qemu_chr_close_stdio(struct CharDriverState *chr)
+static void qemu_chr_free_stdio(struct Chardev *chr)
{
term_exit();
- fd_chr_close(chr);
+ fd_chr_free(chr);
}
-static CharDriverState *qemu_chr_open_stdio(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qemu_chr_open_stdio(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevStdio *opts = backend->u.stdio.data;
- CharDriverState *chr;
struct sigaction act;
- ChardevCommon *common = qapi_ChardevStdio_base(opts);
if (is_daemonized()) {
error_setg(errp, "cannot use stdio with -daemonize");
- return NULL;
+ return;
}
if (stdio_in_use) {
error_setg(errp, "cannot use stdio by multiple character devices");
- return NULL;
+ return;
}
stdio_in_use = true;
act.sa_handler = term_stdio_handler;
sigaction(SIGCONT, &act, NULL);
- chr = qemu_chr_open_fd(0, 1, common, errp);
- if (!chr) {
- return NULL;
- }
- chr->chr_close = qemu_chr_close_stdio;
- chr->chr_set_echo = qemu_chr_set_echo_stdio;
+ qemu_chr_open_fd(chr, 0, 1);
+
if (opts->has_signal) {
stdio_allow_signal = opts->signal;
}
- qemu_chr_fe_set_echo(chr, false);
-
- return chr;
+ qemu_chr_set_echo_stdio(chr, false);
}
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
#define HAVE_CHARDEV_PTY 1
typedef struct {
+ Chardev parent;
QIOChannel *ioc;
int read_bytes;
- /* Protected by the CharDriverState chr_write_lock. */
+ /* Protected by the Chardev chr_write_lock. */
int connected;
guint timer_tag;
guint open_tag;
-} PtyCharDriver;
+} PtyChardev;
+
+#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
-static void pty_chr_update_read_handler_locked(CharDriverState *chr);
-static void pty_chr_state(CharDriverState *chr, int connected);
+static void pty_chr_update_read_handler_locked(Chardev *chr);
+static void pty_chr_state(Chardev *chr, int connected);
static gboolean pty_chr_timer(gpointer opaque)
{
- struct CharDriverState *chr = opaque;
- PtyCharDriver *s = chr->opaque;
+ struct Chardev *chr = CHARDEV(opaque);
+ PtyChardev *s = PTY_CHARDEV(opaque);
qemu_mutex_lock(&chr->chr_write_lock);
s->timer_tag = 0;
}
/* Called with chr_write_lock held. */
-static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
+static void pty_chr_rearm_timer(Chardev *chr, int ms)
{
- PtyCharDriver *s = chr->opaque;
+ PtyChardev *s = PTY_CHARDEV(chr);
+ char *name;
if (s->timer_tag) {
g_source_remove(s->timer_tag);
}
if (ms == 1000) {
+ name = g_strdup_printf("pty-timer-secs-%s", chr->label);
s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
} else {
+ name = g_strdup_printf("pty-timer-ms-%s", chr->label);
s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
}
+ g_source_set_name_by_id(s->timer_tag, name);
+ g_free(name);
}
/* Called with chr_write_lock held. */
-static void pty_chr_update_read_handler_locked(CharDriverState *chr)
+static void pty_chr_update_read_handler_locked(Chardev *chr)
{
- PtyCharDriver *s = chr->opaque;
+ PtyChardev *s = PTY_CHARDEV(chr);
GPollFD pfd;
int rc;
QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
}
}
-static void pty_chr_update_read_handler(CharDriverState *chr,
+static void pty_chr_update_read_handler(Chardev *chr,
GMainContext *context)
{
qemu_mutex_lock(&chr->chr_write_lock);
}
/* Called with chr_write_lock held. */
-static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
- PtyCharDriver *s = chr->opaque;
+ PtyChardev *s = PTY_CHARDEV(chr);
if (!s->connected) {
/* guest sends data, check for (re-)connect */
return io_channel_send(s->ioc, buf, len);
}
-static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
{
- PtyCharDriver *s = chr->opaque;
+ PtyChardev *s = PTY_CHARDEV(chr);
if (!s->connected) {
return NULL;
}
static int pty_chr_read_poll(void *opaque)
{
- CharDriverState *chr = opaque;
- PtyCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ PtyChardev *s = PTY_CHARDEV(opaque);
s->read_bytes = qemu_chr_be_can_write(chr);
return s->read_bytes;
static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
{
- CharDriverState *chr = opaque;
- PtyCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ PtyChardev *s = PTY_CHARDEV(opaque);
gsize len;
uint8_t buf[READ_BUF_LEN];
ssize_t ret;
static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
{
- CharDriverState *chr = opaque;
- PtyCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ PtyChardev *s = PTY_CHARDEV(opaque);
s->open_tag = 0;
qemu_chr_be_generic_open(chr);
}
/* Called with chr_write_lock held. */
-static void pty_chr_state(CharDriverState *chr, int connected)
+static void pty_chr_state(Chardev *chr, int connected)
{
- PtyCharDriver *s = chr->opaque;
+ PtyChardev *s = PTY_CHARDEV(chr);
if (!connected) {
if (s->open_tag) {
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->ioc,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
pty_chr_read_poll,
pty_chr_read,
chr, NULL);
}
}
-static void pty_chr_close(struct CharDriverState *chr)
+static void pty_chr_free(struct Chardev *chr)
{
- PtyCharDriver *s = chr->opaque;
+ PtyChardev *s = PTY_CHARDEV(chr);
qemu_mutex_lock(&chr->chr_write_lock);
pty_chr_state(chr, 0);
s->timer_tag = 0;
}
qemu_mutex_unlock(&chr->chr_write_lock);
- g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_pty(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void char_pty_open(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
- CharDriverState *chr;
- PtyCharDriver *s;
+ PtyChardev *s;
int master_fd, slave_fd;
char pty_name[PATH_MAX];
- ChardevCommon *common = backend->u.pty.data;
+ char *name;
master_fd = qemu_openpty_raw(&slave_fd, pty_name);
if (master_fd < 0) {
error_setg_errno(errp, errno, "Failed to create PTY");
- return NULL;
+ return;
}
close(slave_fd);
qemu_set_nonblock(master_fd);
- chr = qemu_chr_alloc(common, errp);
- if (!chr) {
- close(master_fd);
- return NULL;
- }
-
chr->filename = g_strdup_printf("pty:%s", pty_name);
- ret->pty = g_strdup(pty_name);
- ret->has_pty = true;
-
- fprintf(stderr, "char device redirected to %s (label %s)\n",
- pty_name, id);
-
- s = g_new0(PtyCharDriver, 1);
- 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;
- chr->explicit_be_open = true;
+ error_report("char device redirected to %s (label %s)",
+ pty_name, chr->label);
+ s = PTY_CHARDEV(chr);
s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
+ name = g_strdup_printf("chardev-pty-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
+ g_free(name);
s->timer_tag = 0;
+ *be_opened = false;
+}
- return chr;
+static const CharDriver pty_driver = {
+ .kind = CHARDEV_BACKEND_KIND_PTY,
+};
+
+static void char_pty_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = char_pty_open;
+ cc->chr_write = char_pty_chr_write;
+ cc->chr_update_read_handler = pty_chr_update_read_handler;
+ cc->chr_add_watch = pty_chr_add_watch;
+ cc->chr_free = pty_chr_free;
}
+static const TypeInfo char_pty_type_info = {
+ .name = TYPE_CHARDEV_PTY,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(PtyChardev),
+ .class_init = char_pty_class_init,
+};
+
static void tty_serial_init(int fd, int speed,
int parity, int data_bits, int stop_bits)
{
tcsetattr (fd, TCSANOW, &tty);
}
-static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
+static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
{
- FDCharDriver *s = chr->opaque;
+ FDChardev *s = FD_CHARDEV(chr);
QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
switch(cmd) {
return 0;
}
-static void qemu_chr_close_tty(CharDriverState *chr)
+static void qemu_chr_free_tty(Chardev *chr)
{
- fd_chr_close(chr);
+ fd_chr_free(chr);
}
-
-static CharDriverState *qemu_chr_open_tty_fd(int fd,
- ChardevCommon *backend,
- Error **errp)
-{
- CharDriverState *chr;
-
- tty_serial_init(fd, 115200, 'N', 8, 1);
- chr = qemu_chr_open_fd(fd, fd, backend, errp);
- if (!chr) {
- return NULL;
- }
- chr->chr_ioctl = tty_serial_ioctl;
- chr->chr_close = qemu_chr_close_tty;
- return chr;
-}
-#endif /* __linux__ || __sun__ */
+#endif /* __linux__ || __sun__ */
#if defined(__linux__)
#define HAVE_CHARDEV_PARPORT 1
typedef struct {
+ Chardev parent;
int fd;
int mode;
-} ParallelCharDriver;
+} ParallelChardev;
+
+#define PARALLEL_CHARDEV(obj) \
+ OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
-static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
+static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
{
if (s->mode != mode) {
int m = mode;
return 1;
}
-static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+static int pp_ioctl(Chardev *chr, int cmd, void *arg)
{
- ParallelCharDriver *drv = chr->opaque;
+ ParallelChardev *drv = PARALLEL_CHARDEV(chr);
int fd = drv->fd;
uint8_t b;
return 0;
}
-static void pp_close(CharDriverState *chr)
+static void pp_free(Chardev *chr)
{
- ParallelCharDriver *drv = chr->opaque;
+ ParallelChardev *drv = PARALLEL_CHARDEV(chr);
int fd = drv->fd;
pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
ioctl(fd, PPRELEASE);
close(fd);
- g_free(drv);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_pp_fd(int fd,
- ChardevCommon *backend,
- Error **errp)
+static void qemu_chr_open_pp_fd(Chardev *chr,
+ int fd,
+ bool *be_opened,
+ Error **errp)
{
- CharDriverState *chr;
- ParallelCharDriver *drv;
+ ParallelChardev *drv = PARALLEL_CHARDEV(chr);
if (ioctl(fd, PPCLAIM) < 0) {
error_setg_errno(errp, errno, "not a parallel port");
close(fd);
- return NULL;
- }
-
- chr = qemu_chr_alloc(backend, errp);
- if (!chr) {
- return NULL;
+ return;
}
- drv = g_new0(ParallelCharDriver, 1);
- chr->opaque = drv;
- chr->chr_write = null_chr_write;
- chr->chr_ioctl = pp_ioctl;
- chr->chr_close = pp_close;
-
drv->fd = fd;
drv->mode = IEEE1284_MODE_COMPAT;
-
- return chr;
}
#endif /* __linux__ */
#define HAVE_CHARDEV_PARPORT 1
-static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+typedef struct {
+ Chardev parent;
+ int fd;
+} ParallelChardev;
+
+#define PARALLEL_CHARDEV(obj) \
+ OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
+
+static int pp_ioctl(Chardev *chr, int cmd, void *arg)
{
- int fd = (int)(intptr_t)chr->opaque;
+ ParallelChardev *drv = PARALLEL_CHARDEV(chr);
uint8_t b;
- switch(cmd) {
+ switch (cmd) {
case CHR_IOCTL_PP_READ_DATA:
- if (ioctl(fd, PPIGDATA, &b) < 0)
+ if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
return -ENOTSUP;
+ }
*(uint8_t *)arg = b;
break;
case CHR_IOCTL_PP_WRITE_DATA:
b = *(uint8_t *)arg;
- if (ioctl(fd, PPISDATA, &b) < 0)
+ if (ioctl(drv->fd, PPISDATA, &b) < 0) {
return -ENOTSUP;
+ }
break;
case CHR_IOCTL_PP_READ_CONTROL:
- if (ioctl(fd, PPIGCTRL, &b) < 0)
+ if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
return -ENOTSUP;
+ }
*(uint8_t *)arg = b;
break;
case CHR_IOCTL_PP_WRITE_CONTROL:
b = *(uint8_t *)arg;
- if (ioctl(fd, PPISCTRL, &b) < 0)
+ if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
return -ENOTSUP;
+ }
break;
case CHR_IOCTL_PP_READ_STATUS:
- if (ioctl(fd, PPIGSTATUS, &b) < 0)
+ if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
return -ENOTSUP;
+ }
*(uint8_t *)arg = b;
break;
default:
return 0;
}
-static CharDriverState *qemu_chr_open_pp_fd(int fd,
- ChardevCommon *backend,
- Error **errp)
+static void qemu_chr_open_pp_fd(Chardev *chr,
+ int fd,
+ bool *be_opened,
+ Error **errp)
{
- CharDriverState *chr;
-
- chr = qemu_chr_alloc(backend, errp);
- if (!chr) {
- return NULL;
- }
- chr->opaque = (void *)(intptr_t)fd;
- chr->chr_write = null_chr_write;
- chr->chr_ioctl = pp_ioctl;
- chr->explicit_be_open = true;
- return chr;
+ ParallelChardev *drv = PARALLEL_CHARDEV(chr);
+ drv->fd = fd;
+ *be_opened = false;
}
#endif
#define HAVE_CHARDEV_SERIAL 1
typedef struct {
+ Chardev parent;
int max_size;
HANDLE hcom, hrecv, hsend;
OVERLAPPED orecv;
BOOL fpipe;
DWORD len;
- /* Protected by the CharDriverState chr_write_lock. */
+ /* Protected by the Chardev chr_write_lock. */
OVERLAPPED osend;
-} WinCharState;
+} WinChardev;
+
+#define TYPE_CHARDEV_WIN "chardev-win"
+#define WIN_CHARDEV(obj) OBJECT_CHECK(WinChardev, (obj), TYPE_CHARDEV_WIN)
typedef struct {
+ Chardev parent;
HANDLE hStdIn;
HANDLE hInputReadyEvent;
HANDLE hInputDoneEvent;
HANDLE hInputThread;
uint8_t win_stdio_buf;
-} WinStdioCharState;
+} WinStdioChardev;
+
+#define TYPE_CHARDEV_WIN_STDIO "chardev-win-stdio"
+#define WIN_STDIO_CHARDEV(obj) \
+ OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO)
#define NSENDBUF 2048
#define NRECVBUF 2048
static int win_chr_poll(void *opaque);
static int win_chr_pipe_poll(void *opaque);
-static void win_chr_close(CharDriverState *chr)
+static void win_chr_free(Chardev *chr)
{
- WinCharState *s = chr->opaque;
+ WinChardev *s = WIN_CHARDEV(chr);
if (s->hsend) {
CloseHandle(s->hsend);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp)
+static int win_chr_init(Chardev *chr, const char *filename, Error **errp)
{
- WinCharState *s = chr->opaque;
+ WinChardev *s = WIN_CHARDEV(chr);
COMMCONFIG comcfg;
COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
COMSTAT comstat;
return 0;
fail:
- win_chr_close(chr);
+ win_chr_free(chr);
return -1;
}
/* Called with chr_write_lock held. */
-static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
+static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
{
- WinCharState *s = chr->opaque;
+ WinChardev *s = WIN_CHARDEV(chr);
DWORD len, ret, size, err;
len = len1;
return len1 - len;
}
-static int win_chr_read_poll(CharDriverState *chr)
+static int win_chr_read_poll(Chardev *chr)
{
- WinCharState *s = chr->opaque;
+ WinChardev *s = WIN_CHARDEV(chr);
s->max_size = qemu_chr_be_can_write(chr);
return s->max_size;
}
-static void win_chr_readfile(CharDriverState *chr)
+static void win_chr_readfile(Chardev *chr)
{
- WinCharState *s = chr->opaque;
+ WinChardev *s = WIN_CHARDEV(chr);
+
int ret, err;
uint8_t buf[READ_BUF_LEN];
DWORD size;
}
}
-static void win_chr_read(CharDriverState *chr)
+static void win_chr_read(Chardev *chr)
{
- WinCharState *s = chr->opaque;
+ WinChardev *s = WIN_CHARDEV(chr);
if (s->len > s->max_size)
s->len = s->max_size;
static int win_chr_poll(void *opaque)
{
- CharDriverState *chr = opaque;
- WinCharState *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ WinChardev *s = WIN_CHARDEV(opaque);
COMSTAT status;
DWORD comerr;
return 0;
}
-static CharDriverState *qemu_chr_open_win_path(const char *filename,
- ChardevCommon *backend,
- Error **errp)
-{
- CharDriverState *chr;
- WinCharState *s;
-
- chr = qemu_chr_alloc(backend, errp);
- if (!chr) {
- return NULL;
- }
- s = g_new0(WinCharState, 1);
- chr->opaque = s;
- chr->chr_write = win_chr_write;
- chr->chr_close = win_chr_close;
-
- if (win_chr_init(chr, filename, errp) < 0) {
- g_free(s);
- qemu_chr_free_common(chr);
- return NULL;
- }
- return chr;
-}
-
static int win_chr_pipe_poll(void *opaque)
{
- CharDriverState *chr = opaque;
- WinCharState *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ WinChardev *s = WIN_CHARDEV(opaque);
DWORD size;
PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
return 0;
}
-static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
+static int win_chr_pipe_init(Chardev *chr, const char *filename,
Error **errp)
{
- WinCharState *s = chr->opaque;
+ WinChardev *s = WIN_CHARDEV(chr);
OVERLAPPED ov;
int ret;
DWORD size;
return 0;
fail:
- win_chr_close(chr);
+ win_chr_free(chr);
return -1;
}
-static CharDriverState *qemu_chr_open_pipe(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qemu_chr_open_pipe(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevHostdev *opts = backend->u.pipe.data;
const char *filename = opts->device;
- CharDriverState *chr;
- WinCharState *s;
- ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
- chr = qemu_chr_alloc(common, errp);
- if (!chr) {
- return NULL;
- }
- s = g_new0(WinCharState, 1);
- chr->opaque = s;
- chr->chr_write = win_chr_write;
- chr->chr_close = win_chr_close;
if (win_chr_pipe_init(chr, filename, errp) < 0) {
- g_free(s);
- qemu_chr_free_common(chr);
- return NULL;
+ return;
}
- return chr;
}
-static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out,
- ChardevCommon *backend,
- Error **errp)
+static void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out)
{
- CharDriverState *chr;
- WinCharState *s;
+ WinChardev *s = WIN_CHARDEV(chr);
- chr = qemu_chr_alloc(backend, errp);
- if (!chr) {
- return NULL;
- }
- s = g_new0(WinCharState, 1);
s->hcom = fd_out;
- chr->opaque = s;
- chr->chr_write = win_chr_write;
- return chr;
}
-static CharDriverState *qemu_chr_open_win_con(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void char_win_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->chr_write = win_chr_write;
+ cc->chr_free = win_chr_free;
+}
+
+static const TypeInfo char_win_type_info = {
+ .name = TYPE_CHARDEV_WIN,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(WinChardev),
+ .class_init = char_win_class_init,
+ .abstract = true,
+};
+
+static void qemu_chr_open_win_con(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
+{
+ qemu_chr_open_win_file(chr, GetStdHandle(STD_OUTPUT_HANDLE));
+}
+
+static const CharDriver console_driver = {
+ .kind = CHARDEV_BACKEND_KIND_CONSOLE,
+};
+
+static void char_console_class_init(ObjectClass *oc, void *data)
{
- ChardevCommon *common = backend->u.console.data;
- return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE),
- common, errp);
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qemu_chr_open_win_con;
+ cc->chr_free = NULL;
}
-static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
+static const TypeInfo char_console_type_info = {
+ .name = TYPE_CHARDEV_CONSOLE,
+ .parent = TYPE_CHARDEV_WIN,
+ .class_init = char_console_class_init,
+};
+
+static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
{
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwSize;
static void win_stdio_wait_func(void *opaque)
{
- CharDriverState *chr = opaque;
- WinStdioCharState *stdio = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
INPUT_RECORD buf[4];
int ret;
DWORD dwSize;
static DWORD WINAPI win_stdio_thread(LPVOID param)
{
- CharDriverState *chr = param;
- WinStdioCharState *stdio = chr->opaque;
+ WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
int ret;
DWORD dwSize;
static void win_stdio_thread_wait_func(void *opaque)
{
- CharDriverState *chr = opaque;
- WinStdioCharState *stdio = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
if (qemu_chr_be_can_write(chr)) {
qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
SetEvent(stdio->hInputDoneEvent);
}
-static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
+static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
{
- WinStdioCharState *stdio = chr->opaque;
+ WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
DWORD dwMode = 0;
GetConsoleMode(stdio->hStdIn, &dwMode);
}
}
-static void win_stdio_close(CharDriverState *chr)
+static void win_stdio_free(Chardev *chr)
{
- WinStdioCharState *stdio = chr->opaque;
+ WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
CloseHandle(stdio->hInputReadyEvent);
if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
TerminateThread(stdio->hInputThread, 0);
}
-
- g_free(chr->opaque);
- g_free(chr);
}
-static CharDriverState *qemu_chr_open_stdio(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static const TypeInfo char_win_stdio_type_info = {
+ .name = TYPE_CHARDEV_WIN_STDIO,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(WinStdioChardev),
+ .abstract = true,
+};
+
+static void qemu_chr_open_stdio(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
- CharDriverState *chr;
- WinStdioCharState *stdio;
+ WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
DWORD dwMode;
int is_console = 0;
- ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data);
-
- chr = qemu_chr_alloc(common, errp);
- if (!chr) {
- return NULL;
- }
- stdio = g_new0(WinStdioCharState, 1);
stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
error_setg(errp, "cannot open stdio: invalid handle");
- return NULL;
+ return;
}
is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
- chr->opaque = stdio;
- chr->chr_write = win_stdio_write;
- chr->chr_close = win_stdio_close;
-
if (is_console) {
if (qemu_add_wait_object(stdio->hStdIn,
win_stdio_wait_func, chr)) {
SetConsoleMode(stdio->hStdIn, dwMode);
- chr->chr_set_echo = qemu_chr_set_echo_win_stdio;
- qemu_chr_fe_set_echo(chr, false);
+ qemu_chr_set_echo_win_stdio(chr, false);
- return chr;
+ return;
err3:
qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
CloseHandle(stdio->hInputDoneEvent);
err1:
qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
- return NULL;
}
#endif /* !_WIN32 */
-
/***********************************************************/
/* UDP Net console */
typedef struct {
+ Chardev parent;
QIOChannel *ioc;
uint8_t buf[READ_BUF_LEN];
int bufcnt;
int bufptr;
int max_size;
-} NetCharDriver;
+} UdpChardev;
+
+#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP)
/* Called with chr_write_lock held. */
-static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
- NetCharDriver *s = chr->opaque;
+ UdpChardev *s = UDP_CHARDEV(chr);
return qio_channel_write(
s->ioc, (const char *)buf, len, NULL);
static int udp_chr_read_poll(void *opaque)
{
- CharDriverState *chr = opaque;
- NetCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ UdpChardev *s = UDP_CHARDEV(opaque);
s->max_size = qemu_chr_be_can_write(chr);
static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
{
- CharDriverState *chr = opaque;
- NetCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ UdpChardev *s = UDP_CHARDEV(opaque);
ssize_t ret;
if (s->max_size == 0) {
return TRUE;
}
-static void udp_chr_update_read_handler(CharDriverState *chr,
+static void udp_chr_update_read_handler(Chardev *chr,
GMainContext *context)
{
- NetCharDriver *s = chr->opaque;
+ UdpChardev *s = UDP_CHARDEV(chr);
remove_fd_in_watch(chr);
if (s->ioc) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
udp_chr_read_poll,
udp_chr_read, chr,
context);
}
}
-static void udp_chr_close(CharDriverState *chr)
+static void udp_chr_free(Chardev *chr)
{
- NetCharDriver *s = chr->opaque;
+ UdpChardev *s = UDP_CHARDEV(chr);
remove_fd_in_watch(chr);
if (s->ioc) {
object_unref(OBJECT(s->ioc));
}
- g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
- ChardevCommon *backend,
- Error **errp)
-{
- CharDriverState *chr = NULL;
- NetCharDriver *s = NULL;
-
- chr = qemu_chr_alloc(backend, errp);
- if (!chr) {
- return NULL;
- }
- s = g_new0(NetCharDriver, 1);
-
- s->ioc = QIO_CHANNEL(sioc);
- s->bufcnt = 0;
- s->bufptr = 0;
- chr->opaque = s;
- chr->chr_write = udp_chr_write;
- chr->chr_update_read_handler = udp_chr_update_read_handler;
- chr->chr_close = udp_chr_close;
- /* be isn't opened until we get a connection */
- chr->explicit_be_open = true;
- return chr;
-}
-
/***********************************************************/
/* TCP Net console */
typedef struct {
+ Chardev parent;
QIOChannel *ioc; /* Client I/O channel */
QIOChannelSocket *sioc; /* Client master channel */
QIOChannelSocket *listen_ioc;
guint reconnect_timer;
int64_t reconnect_time;
bool connect_err_reported;
-} TCPCharDriver;
+} SocketChardev;
+
+#define SOCKET_CHARDEV(obj) \
+ OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
static gboolean socket_reconnect_timeout(gpointer opaque);
-static void qemu_chr_socket_restart_timer(CharDriverState *chr)
+static void qemu_chr_socket_restart_timer(Chardev *chr)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+ char *name;
+
assert(s->connected == 0);
s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
socket_reconnect_timeout, chr);
+ name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label);
+ g_source_set_name_by_id(s->reconnect_timer, name);
+ g_free(name);
}
-static void check_report_connect_error(CharDriverState *chr,
+static void check_report_connect_error(Chardev *chr,
Error *err)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
if (!s->connect_err_reported) {
error_report("Unable to connect character device %s: %s",
void *opaque);
/* Called with chr_write_lock held. */
-static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+
if (s->connected) {
int ret = io_channel_send_full(s->ioc, buf, len,
s->write_msgfds,
static int tcp_chr_read_poll(void *opaque)
{
- CharDriverState *chr = opaque;
- TCPCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ SocketChardev *s = SOCKET_CHARDEV(opaque);
if (!s->connected)
return 0;
s->max_size = qemu_chr_be_can_write(chr);
#define IAC 255
#define IAC_BREAK 243
-static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
- TCPCharDriver *s,
+static void tcp_chr_process_IAC_bytes(Chardev *chr,
+ SocketChardev *s,
uint8_t *buf, int *size)
{
/* Handle any telnet client's basic IAC options to satisfy char by
*size = j;
}
-static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
+static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+
int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
assert(num <= TCP_MAX_FDS);
return to_copy;
}
-static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
+static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
/* clear old pending fd array */
g_free(s->write_msgfds);
return 0;
}
-static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
+static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
struct iovec iov = { .iov_base = buf, .iov_len = len };
int ret;
size_t i;
return ret;
}
-static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
return qio_channel_create_watch(s->ioc, cond);
}
-static void tcp_chr_free_connection(CharDriverState *chr)
+static void tcp_chr_free_connection(Chardev *chr)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
int i;
if (!s->connected) {
s->connected = 0;
}
-static void tcp_chr_disconnect(CharDriverState *chr)
+static void tcp_chr_disconnect(Chardev *chr)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
if (!s->connected) {
return;
static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
{
- CharDriverState *chr = opaque;
- TCPCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ SocketChardev *s = SOCKET_CHARDEV(opaque);
uint8_t buf[READ_BUF_LEN];
int len, size;
return TRUE;
}
-static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
+static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
int size;
if (!s->connected) {
static void tcp_chr_connect(void *opaque)
{
- CharDriverState *chr = opaque;
- TCPCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ SocketChardev *s = SOCKET_CHARDEV(opaque);
g_free(chr->filename);
chr->filename = sockaddr_to_str(
s->connected = 1;
if (s->ioc) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
tcp_chr_read_poll,
tcp_chr_read,
chr, NULL);
qemu_chr_be_generic_open(chr);
}
-static void tcp_chr_update_read_handler(CharDriverState *chr,
+static void tcp_chr_update_read_handler(Chardev *chr,
GMainContext *context)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
if (!s->connected) {
return;
remove_fd_in_watch(chr);
if (s->ioc) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
tcp_chr_read_poll,
tcp_chr_read, chr,
context);
}
typedef struct {
- CharDriverState *chr;
+ Chardev *chr;
char buf[12];
size_t buflen;
} TCPCharDriverTelnetInit;
return TRUE;
}
-static void tcp_chr_telnet_init(CharDriverState *chr)
+static void tcp_chr_telnet_init(Chardev *chr)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
TCPCharDriverTelnetInit *init =
g_new0(TCPCharDriverTelnetInit, 1);
size_t n = 0;
}
-static void tcp_chr_tls_handshake(Object *source,
- Error *err,
+static void tcp_chr_tls_handshake(QIOTask *task,
gpointer user_data)
{
- CharDriverState *chr = user_data;
- TCPCharDriver *s = chr->opaque;
+ Chardev *chr = user_data;
+ SocketChardev *s = user_data;
- if (err) {
+ if (qio_task_propagate_error(task, NULL)) {
tcp_chr_disconnect(chr);
} else {
if (s->do_telnetopt) {
}
-static void tcp_chr_tls_init(CharDriverState *chr)
+static void tcp_chr_tls_init(Chardev *chr)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
QIOChannelTLS *tioc;
Error *err = NULL;
+ gchar *name;
if (s->is_listen) {
tioc = qio_channel_tls_new_server(
tcp_chr_disconnect(chr);
return;
}
+ name = g_strdup_printf("chardev-tls-%s-%s",
+ s->is_listen ? "server" : "client",
+ chr->label);
+ qio_channel_set_name(QIO_CHANNEL(tioc), name);
+ g_free(name);
object_unref(OBJECT(s->ioc));
s->ioc = QIO_CHANNEL(tioc);
}
-static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
+static void tcp_chr_set_client_ioc_name(Chardev *chr,
+ QIOChannelSocket *sioc)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+ char *name;
+ name = g_strdup_printf("chardev-tcp-%s-%s",
+ s->is_listen ? "server" : "client",
+ chr->label);
+ qio_channel_set_name(QIO_CHANNEL(sioc), name);
+ g_free(name);
+
+}
+
+static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
+{
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+
if (s->ioc != NULL) {
return -1;
}
}
-static int tcp_chr_add_client(CharDriverState *chr, int fd)
+static int tcp_chr_add_client(Chardev *chr, int fd)
{
int ret;
QIOChannelSocket *sioc;
if (!sioc) {
return -1;
}
+ tcp_chr_set_client_ioc_name(chr, sioc);
ret = tcp_chr_new_client(chr, sioc);
object_unref(OBJECT(sioc));
return ret;
GIOCondition cond,
void *opaque)
{
- CharDriverState *chr = opaque;
+ Chardev *chr = CHARDEV(opaque);
QIOChannelSocket *sioc;
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
return TRUE;
}
-static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
+static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
QIOChannelSocket *sioc;
/* It can't wait on s->connected, since it is set asynchronously
* in TLS and telnet cases, only wait for an accepted socket */
while (!s->ioc) {
if (s->is_listen) {
- fprintf(stderr, "QEMU waiting for connection on: %s\n",
- chr->filename);
+ error_report("QEMU waiting for connection on: %s",
+ chr->filename);
qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL);
tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr);
qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL);
} else {
sioc = qio_channel_socket_new();
+ tcp_chr_set_client_ioc_name(chr, sioc);
if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) {
object_unref(OBJECT(sioc));
return -1;
return 0;
}
-int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
+static int qemu_chr_wait_connected(Chardev *chr, Error **errp)
{
- if (chr->chr_wait_connected) {
- return chr->chr_wait_connected(chr, errp);
+ ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+
+ if (cc->chr_wait_connected) {
+ return cc->chr_wait_connected(chr, errp);
}
return 0;
}
-static void tcp_chr_close(CharDriverState *chr)
+int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
+{
+ if (!be->chr) {
+ error_setg(errp, "missing associated backend");
+ return -1;
+ }
+
+ return qemu_chr_wait_connected(be->chr, errp);
+}
+
+static void tcp_chr_free(Chardev *chr)
{
- TCPCharDriver *s = chr->opaque;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
tcp_chr_free_connection(chr);
if (s->tls_creds) {
object_unref(OBJECT(s->tls_creds));
}
- g_free(s);
+
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque)
+static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
{
- QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(src);
- CharDriverState *chr = opaque;
- TCPCharDriver *s = chr->opaque;
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
+ Chardev *chr = CHARDEV(opaque);
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+ Error *err = NULL;
- if (err) {
+ if (qio_task_propagate_error(task, &err)) {
check_report_connect_error(chr, err);
- object_unref(src);
- return;
+ error_free(err);
+ goto cleanup;
}
s->connect_err_reported = false;
tcp_chr_new_client(chr, sioc);
+
+ cleanup:
object_unref(OBJECT(sioc));
}
/* Ring buffer chardev */
typedef struct {
+ Chardev parent;
size_t size;
size_t prod;
size_t cons;
uint8_t *cbuf;
-} RingBufCharDriver;
+} RingBufChardev;
+
+#define RINGBUF_CHARDEV(obj) \
+ OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF)
-static size_t ringbuf_count(const CharDriverState *chr)
+static size_t ringbuf_count(const Chardev *chr)
{
- const RingBufCharDriver *d = chr->opaque;
+ const RingBufChardev *d = RINGBUF_CHARDEV(chr);
return d->prod - d->cons;
}
/* Called with chr_write_lock held. */
-static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
- RingBufCharDriver *d = chr->opaque;
+ RingBufChardev *d = RINGBUF_CHARDEV(chr);
int i;
if (!buf || (len < 0)) {
}
}
- return 0;
+ return len;
}
-static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
+static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
{
- RingBufCharDriver *d = chr->opaque;
+ RingBufChardev *d = RINGBUF_CHARDEV(chr);
int i;
qemu_mutex_lock(&chr->chr_write_lock);
return i;
}
-static void ringbuf_chr_close(struct CharDriverState *chr)
+static void ringbuf_chr_free(struct Chardev *chr)
{
- RingBufCharDriver *d = chr->opaque;
+ RingBufChardev *d = RINGBUF_CHARDEV(chr);
g_free(d->cbuf);
- g_free(d);
- chr->opaque = NULL;
}
-static CharDriverState *qemu_chr_open_ringbuf(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qemu_chr_open_ringbuf(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevRingbuf *opts = backend->u.ringbuf.data;
- ChardevCommon *common = qapi_ChardevRingbuf_base(opts);
- CharDriverState *chr;
- RingBufCharDriver *d;
-
- chr = qemu_chr_alloc(common, errp);
- if (!chr) {
- return NULL;
- }
- d = g_malloc(sizeof(*d));
+ RingBufChardev *d = RINGBUF_CHARDEV(chr);
d->size = opts->has_size ? opts->size : 65536;
/* The size must be power of 2 */
if (d->size & (d->size - 1)) {
error_setg(errp, "size of ringbuf chardev must be power of two");
- goto fail;
+ return;
}
d->prod = 0;
d->cons = 0;
d->cbuf = g_malloc0(d->size);
-
- chr->opaque = d;
- chr->chr_write = ringbuf_chr_write;
- chr->chr_close = ringbuf_chr_close;
-
- return chr;
-
-fail:
- g_free(d);
- qemu_chr_free_common(chr);
- return NULL;
-}
-
-bool chr_is_ringbuf(const CharDriverState *chr)
-{
- return chr->chr_write == ringbuf_chr_write;
}
void qmp_ringbuf_write(const char *device, const char *data,
bool has_format, enum DataFormat format,
Error **errp)
{
- CharDriverState *chr;
+ Chardev *chr;
const uint8_t *write_data;
int ret;
gsize write_count;
return;
}
- if (!chr_is_ringbuf(chr)) {
+ if (!CHARDEV_IS_RINGBUF(chr)) {
error_setg(errp,"%s is not a ringbuf device", device);
return;
}
bool has_format, enum DataFormat format,
Error **errp)
{
- CharDriverState *chr;
+ Chardev *chr;
uint8_t *read_data;
size_t count;
char *data;
return NULL;
}
- if (!chr_is_ringbuf(chr)) {
+ if (!CHARDEV_IS_RINGBUF(chr)) {
error_setg(errp,"%s is not a ringbuf device", device);
return NULL;
}
stdio->signal = qemu_opt_get_bool(opts, "signal", true);
}
+static const CharDriver stdio_driver = {
+ .kind = CHARDEV_BACKEND_KIND_STDIO,
+ .parse = qemu_chr_parse_stdio,
+};
+
+static void char_stdio_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qemu_chr_open_stdio;
+#ifdef _WIN32
+ cc->chr_write = win_stdio_write;
+ cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
+ cc->chr_free = win_stdio_free;
+#else
+ cc->chr_set_echo = qemu_chr_set_echo_stdio;
+ cc->chr_free = qemu_chr_free_stdio;
+#endif
+}
+
+static const TypeInfo char_stdio_type_info = {
+ .name = TYPE_CHARDEV_STDIO,
+#ifdef _WIN32
+ .parent = TYPE_CHARDEV_WIN_STDIO,
+#else
+ .parent = TYPE_CHARDEV_FD,
+#endif
+ .class_init = char_stdio_class_init,
+};
+
#ifdef HAVE_CHARDEV_SERIAL
static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
dev->device = g_strdup(device);
}
+static const CharDriver pipe_driver = {
+ .kind = CHARDEV_BACKEND_KIND_PIPE,
+ .parse = qemu_chr_parse_pipe,
+};
+
+static void char_pipe_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qemu_chr_open_pipe;
+}
+
+static const TypeInfo char_pipe_type_info = {
+ .name = TYPE_CHARDEV_PIPE,
+#ifdef _WIN32
+ .parent = TYPE_CHARDEV_WIN,
+#else
+ .parent = TYPE_CHARDEV_FD,
+#endif
+ .class_init = char_pipe_class_init,
+};
+
static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
}
}
+static const CharDriver ringbuf_driver = {
+ .kind = CHARDEV_BACKEND_KIND_RINGBUF,
+ .parse = qemu_chr_parse_ringbuf,
+};
+
+static void char_ringbuf_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qemu_chr_open_ringbuf;
+ cc->chr_write = ringbuf_chr_write;
+ cc->chr_free = ringbuf_chr_free;
+}
+
+static const TypeInfo char_ringbuf_type_info = {
+ .name = TYPE_CHARDEV_RINGBUF,
+ .parent = TYPE_CHARDEV,
+ .class_init = char_ringbuf_class_init,
+ .instance_size = sizeof(RingBufChardev),
+};
+
+/* Bug-compatibility: */
+static const CharDriver memory_driver = {
+ .kind = CHARDEV_BACKEND_KIND_MEMORY,
+ .parse = qemu_chr_parse_ringbuf,
+};
+
+static const TypeInfo char_memory_type_info = {
+ .name = TYPE_CHARDEV_MEMORY,
+ .parent = TYPE_CHARDEV_RINGBUF,
+};
+
static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
mux->chardev = g_strdup(chardev);
}
+static const CharDriver mux_driver = {
+ .kind = CHARDEV_BACKEND_KIND_MUX,
+ .parse = qemu_chr_parse_mux,
+};
+
+static void char_mux_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qemu_chr_open_mux;
+ cc->chr_free = mux_chr_free;
+ cc->chr_write = mux_chr_write;
+ cc->chr_accept_input = mux_chr_accept_input;
+ cc->chr_add_watch = mux_chr_add_watch;
+}
+
+static const TypeInfo char_mux_type_info = {
+ .name = TYPE_CHARDEV_MUX,
+ .parent = TYPE_CHARDEV,
+ .class_init = char_mux_class_init,
+ .instance_size = sizeof(MuxChardev),
+};
+
static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
}
}
-typedef struct CharDriver {
- const char *name;
- ChardevBackendKind kind;
- void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
- CharDriverState *(*create)(const char *id, ChardevBackend *backend,
- ChardevReturn *ret, Error **errp);
-} CharDriver;
-
-static GSList *backends;
+static const CharDriver *backends[CHARDEV_BACKEND_KIND__MAX];
-void register_char_driver(const char *name, ChardevBackendKind kind,
- void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp),
- CharDriverState *(*create)(const char *id, ChardevBackend *backend,
- ChardevReturn *ret, Error **errp))
+void register_char_driver(const CharDriver *driver)
{
- CharDriver *s;
-
- s = g_malloc0(sizeof(*s));
- s->name = g_strdup(name);
- s->kind = kind;
- s->parse = parse;
- s->create = create;
-
- backends = g_slist_append(backends, s);
+ backends[driver->kind] = driver;
}
-CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
- void (*init)(struct CharDriverState *s),
- Error **errp)
+Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
+ Error **errp)
{
Error *local_err = NULL;
- CharDriver *cd;
- CharDriverState *chr;
- GSList *i;
+ const CharDriver *cd = NULL;
+ Chardev *chr;
+ int i;
ChardevReturn *ret = NULL;
ChardevBackend *backend;
+ const char *name = qemu_opt_get(opts, "backend");
const char *id = qemu_opts_id(opts);
char *bid = NULL;
- if (qemu_opt_get(opts, "backend") == NULL) {
+ if (name == NULL) {
error_setg(errp, "chardev: \"%s\" missing backend",
qemu_opts_id(opts));
goto err;
}
- if (is_help_option(qemu_opt_get(opts, "backend"))) {
- fprintf(stderr, "Available chardev backend types:\n");
- for (i = backends; i; i = i->next) {
- cd = i->data;
- fprintf(stderr, "%s\n", cd->name);
+ if (is_help_option(name)) {
+ GString *str = g_string_new("");
+ for (i = 0; i < ARRAY_SIZE(backends); i++) {
+ cd = backends[i];
+ if (cd) {
+ g_string_append_printf(str, "\n%s", ChardevBackendKind_lookup[cd->kind]);
+ if (cd->alias) {
+ g_string_append_printf(str, "\n%s", cd->alias);
+ }
+ }
}
- exit(!is_help_option(qemu_opt_get(opts, "backend")));
+
+ error_report("Available chardev backend types: %s", str->str);
+ g_string_free(str, true);
+ exit(0);
}
if (id == NULL) {
goto err;
}
- for (i = backends; i; i = i->next) {
- cd = i->data;
-
- if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
+ for (i = 0; i < ARRAY_SIZE(backends); i++) {
+ cd = backends[i];
+ if (!cd) {
+ continue;
+ }
+ if (g_strcmp0(ChardevBackendKind_lookup[cd->kind], name) == 0 ||
+ g_strcmp0(cd->alias, name) == 0) {
break;
}
}
- if (i == NULL) {
- error_setg(errp, "chardev: backend \"%s\" not found",
- qemu_opt_get(opts, "backend"));
+ if (i == ARRAY_SIZE(backends)) {
+ error_setg(errp, "chardev: backend \"%s\" not found", name);
goto err;
}
}
chr = qemu_chr_find(id);
- chr->opts = opts;
qapi_out:
qapi_free_ChardevBackend(backend);
return chr;
err:
- qemu_opts_del(opts);
return NULL;
}
-CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
- void (*init)(struct CharDriverState *s))
+Chardev *qemu_chr_new_noreplay(const char *label, const char *filename)
{
const char *p;
- CharDriverState *chr;
+ Chardev *chr;
QemuOpts *opts;
Error *err = NULL;
if (!opts)
return NULL;
- chr = qemu_chr_new_from_opts(opts, init, &err);
+ chr = qemu_chr_new_from_opts(opts, &err);
if (err) {
error_report_err(err);
}
if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
- qemu_chr_fe_claim_no_fail(chr);
monitor_init(chr, MONITOR_USE_READLINE);
}
+ qemu_opts_del(opts);
return chr;
}
-CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
+Chardev *qemu_chr_new(const char *label, const char *filename)
{
- CharDriverState *chr;
- chr = qemu_chr_new_noreplay(label, filename, init);
+ Chardev *chr;
+ chr = qemu_chr_new_noreplay(label, filename);
if (chr) {
- chr->replay = replay_mode != REPLAY_MODE_NONE;
- if (chr->replay && chr->chr_ioctl) {
- fprintf(stderr,
- "Replay: ioctl is not supported for serial devices yet\n");
+ if (replay_mode != REPLAY_MODE_NONE) {
+ qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+ }
+ if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) {
+ error_report("Replay: ioctl is not supported "
+ "for serial devices yet");
}
replay_register_char_driver(chr);
}
return chr;
}
-void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
+void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
{
- if (chr->chr_set_echo) {
- chr->chr_set_echo(chr, echo);
+ Chardev *chr = be->chr;
+
+ if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
+ CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
}
}
-void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open)
+void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
{
- if (chr->fe_open == fe_open) {
+ Chardev *chr = be->chr;
+
+ if (!chr) {
return;
}
- chr->fe_open = fe_open;
- if (chr->chr_set_fe_open) {
- chr->chr_set_fe_open(chr, fe_open);
- }
-}
-void qemu_chr_fe_event(struct CharDriverState *chr, int event)
-{
- if (chr->chr_fe_event) {
- chr->chr_fe_event(chr, event);
+ if (be->fe_open == fe_open) {
+ return;
+ }
+ be->fe_open = fe_open;
+ if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
+ CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
}
}
-guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
+guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
GIOFunc func, void *user_data)
{
+ Chardev *s = be->chr;
GSource *src;
guint tag;
- if (s->chr_add_watch == NULL) {
+ if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
return 0;
}
- src = s->chr_add_watch(s, cond);
+ src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
if (!src) {
return 0;
}
return tag;
}
-int qemu_chr_fe_claim(CharDriverState *s)
-{
- if (s->avail_connections < 1) {
- return -1;
- }
- s->avail_connections--;
- return 0;
-}
-
-void qemu_chr_fe_claim_no_fail(CharDriverState *s)
-{
- if (qemu_chr_fe_claim(s) != 0) {
- fprintf(stderr, "%s: error chardev \"%s\" already used\n",
- __func__, s->label);
- exit(1);
- }
-}
-
-void qemu_chr_fe_release(CharDriverState *s)
+void qemu_chr_fe_disconnect(CharBackend *be)
{
- s->avail_connections++;
-}
+ Chardev *chr = be->chr;
-void qemu_chr_disconnect(CharDriverState *chr)
-{
- if (chr->chr_disconnect) {
- chr->chr_disconnect(chr);
+ if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
+ CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
}
}
-static void qemu_chr_free_common(CharDriverState *chr)
+void qemu_chr_free(Chardev *chr)
{
- g_free(chr->filename);
- g_free(chr->label);
- qemu_opts_del(chr->opts);
- if (chr->logfd != -1) {
- close(chr->logfd);
+ if (CHARDEV_GET_CLASS(chr)->chr_free) {
+ CHARDEV_GET_CLASS(chr)->chr_free(chr);
}
- qemu_mutex_destroy(&chr->chr_write_lock);
- g_free(chr);
-}
-
-void qemu_chr_free(CharDriverState *chr)
-{
- if (chr->chr_close) {
- chr->chr_close(chr);
- }
- qemu_chr_free_common(chr);
+ object_unref(OBJECT(chr));
}
-void qemu_chr_delete(CharDriverState *chr)
+void qemu_chr_delete(Chardev *chr)
{
QTAILQ_REMOVE(&chardevs, chr, next);
qemu_chr_free(chr);
ChardevInfoList *qmp_query_chardev(Error **errp)
{
ChardevInfoList *chr_list = NULL;
- CharDriverState *chr;
+ Chardev *chr;
QTAILQ_FOREACH(chr, &chardevs, next) {
ChardevInfoList *info = g_malloc0(sizeof(*info));
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->value->frontend_open = chr->be && chr->be->fe_open;
info->next = chr_list;
chr_list = info;
return chr_list;
}
+static ChardevBackendInfoList *
+qmp_prepend_backend(ChardevBackendInfoList *list, const char *name)
+{
+ ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
+ info->value = g_malloc0(sizeof(*info->value));
+ info->value->name = g_strdup(name);
+ info->next = list;
+ return info;
+}
+
ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
{
ChardevBackendInfoList *backend_list = NULL;
- CharDriver *c = NULL;
- GSList *i = NULL;
+ const CharDriver *c;
+ int i;
- for (i = backends; i; i = i->next) {
- ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
- c = i->data;
- info->value = g_malloc0(sizeof(*info->value));
- info->value->name = g_strdup(c->name);
+ for (i = 0; i < ARRAY_SIZE(backends); i++) {
+ c = backends[i];
+ if (!c) {
+ continue;
+ }
- info->next = backend_list;
- backend_list = info;
+ backend_list = qmp_prepend_backend(backend_list,
+ ChardevBackendKind_lookup[c->kind]);
+ if (c->alias) {
+ backend_list = qmp_prepend_backend(backend_list, c->alias);
+ }
}
return backend_list;
}
-CharDriverState *qemu_chr_find(const char *name)
+Chardev *qemu_chr_find(const char *name)
{
- CharDriverState *chr;
+ Chardev *chr;
QTAILQ_FOREACH(chr, &chardevs, next) {
if (strcmp(chr->label, name) != 0)
#ifdef _WIN32
-static CharDriverState *qmp_chardev_open_file(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qmp_chardev_open_file(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevFile *file = backend->u.file.data;
- ChardevCommon *common = qapi_ChardevFile_base(file);
HANDLE out;
DWORD accessmode;
DWORD flags;
if (file->has_in) {
error_setg(errp, "input file not supported");
- return NULL;
+ return;
}
if (file->has_append && file->append) {
FILE_ATTRIBUTE_NORMAL, NULL);
if (out == INVALID_HANDLE_VALUE) {
error_setg(errp, "open %s failed", file->out);
- return NULL;
+ return;
}
- return qemu_chr_open_win_file(out, common, errp);
+
+ qemu_chr_open_win_file(chr, out);
}
-static CharDriverState *qmp_chardev_open_serial(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qmp_chardev_open_serial(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevHostdev *serial = backend->u.serial.data;
- ChardevCommon *common = qapi_ChardevHostdev_base(serial);
- return qemu_chr_open_win_path(serial->device, common, errp);
+
+ win_chr_init(chr, serial->device, errp);
}
#else /* WIN32 */
return fd;
}
-static CharDriverState *qmp_chardev_open_file(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qmp_chardev_open_file(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevFile *file = backend->u.file.data;
- ChardevCommon *common = qapi_ChardevFile_base(file);
int flags, in = -1, out;
flags = O_WRONLY | O_CREAT | O_BINARY;
out = qmp_chardev_open_file_source(file->out, flags, errp);
if (out < 0) {
- return NULL;
+ return;
}
if (file->has_in) {
in = qmp_chardev_open_file_source(file->in, flags, errp);
if (in < 0) {
qemu_close(out);
- return NULL;
+ return;
}
}
- return qemu_chr_open_fd(in, out, common, errp);
+ qemu_chr_open_fd(chr, in, out);
}
#ifdef HAVE_CHARDEV_SERIAL
-static CharDriverState *qmp_chardev_open_serial(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qmp_chardev_open_serial(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevHostdev *serial = backend->u.serial.data;
- ChardevCommon *common = qapi_ChardevHostdev_base(serial);
int fd;
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
if (fd < 0) {
- return NULL;
+ return;
}
qemu_set_nonblock(fd);
- return qemu_chr_open_tty_fd(fd, common, errp);
+ tty_serial_init(fd, 115200, 'N', 8, 1);
+
+ qemu_chr_open_fd(chr, fd, fd);
}
#endif
#ifdef HAVE_CHARDEV_PARPORT
-static CharDriverState *qmp_chardev_open_parallel(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qmp_chardev_open_parallel(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevHostdev *parallel = backend->u.parallel.data;
- ChardevCommon *common = qapi_ChardevHostdev_base(parallel);
int fd;
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
if (fd < 0) {
- return NULL;
+ return;
}
- return qemu_chr_open_pp_fd(fd, common, errp);
+ qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
+}
+
+static const CharDriver parallel_driver = {
+ .kind = CHARDEV_BACKEND_KIND_PARALLEL,
+ .alias = "parport",
+ .parse = qemu_chr_parse_parallel,
+};
+
+static void char_parallel_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qmp_chardev_open_parallel;
+#if defined(__linux__)
+ cc->chr_write = null_chr_write;
+ cc->chr_ioctl = pp_ioctl;
+ cc->chr_free = pp_free;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ /* FIXME: no chr_free */
+ cc->chr_write = null_chr_write;
+ cc->chr_ioctl = pp_ioctl;
+#endif
}
+
+static const TypeInfo char_parallel_type_info = {
+ .name = TYPE_CHARDEV_PARALLEL,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(ParallelChardev),
+ .class_init = char_parallel_class_init,
+};
#endif
#endif /* WIN32 */
+static const CharDriver file_driver = {
+ .kind = CHARDEV_BACKEND_KIND_FILE,
+ .parse = qemu_chr_parse_file_out,
+};
+
+static void char_file_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qmp_chardev_open_file;
+#ifdef _WIN32
+ /* FIXME: no chr_free */
+ cc->chr_free = NULL;
+#endif
+}
+
+static const TypeInfo char_file_type_info = {
+ .name = TYPE_CHARDEV_FILE,
+#ifdef _WIN32
+ .parent = TYPE_CHARDEV_WIN,
+#else
+ .parent = TYPE_CHARDEV_FD,
+#endif
+ .class_init = char_file_class_init,
+};
+
+#ifdef HAVE_CHARDEV_SERIAL
+
+static const CharDriver serial_driver = {
+ .kind = CHARDEV_BACKEND_KIND_SERIAL,
+ .alias = "tty",
+ .parse = qemu_chr_parse_serial,
+};
+
+static void char_serial_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qmp_chardev_open_serial;
+#ifndef _WIN32
+ cc->chr_ioctl = tty_serial_ioctl;
+ cc->chr_free = qemu_chr_free_tty;
+#endif
+}
+
+static const TypeInfo char_serial_type_info = {
+ .name = TYPE_CHARDEV_SERIAL,
+#ifdef _WIN32
+ .parent = TYPE_CHARDEV_WIN,
+#else
+ .parent = TYPE_CHARDEV_FD,
+#endif
+ .class_init = char_serial_class_init,
+};
+#endif
+
static gboolean socket_reconnect_timeout(gpointer opaque)
{
- CharDriverState *chr = opaque;
- TCPCharDriver *s = chr->opaque;
+ Chardev *chr = CHARDEV(opaque);
+ SocketChardev *s = SOCKET_CHARDEV(opaque);
QIOChannelSocket *sioc;
s->reconnect_timer = 0;
}
sioc = qio_channel_socket_new();
+ tcp_chr_set_client_ioc_name(chr, sioc);
qio_channel_socket_connect_async(sioc, s->addr,
qemu_chr_socket_connected,
chr, NULL);
return false;
}
-static CharDriverState *qmp_chardev_open_socket(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static void qmp_chardev_open_socket(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
- CharDriverState *chr;
- TCPCharDriver *s;
+ SocketChardev *s = SOCKET_CHARDEV(chr);
ChardevSocket *sock = backend->u.socket.data;
SocketAddress *addr = sock->addr;
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
bool is_telnet = sock->has_telnet ? sock->telnet : false;
bool is_waitconnect = sock->has_wait ? sock->wait : false;
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
- ChardevCommon *common = qapi_ChardevSocket_base(sock);
QIOChannelSocket *sioc = NULL;
- chr = qemu_chr_alloc(common, errp);
- if (!chr) {
- return NULL;
- }
- s = g_new0(TCPCharDriver, 1);
-
s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
s->is_listen = is_listen;
s->is_telnet = is_telnet;
s->addr = QAPI_CLONE(SocketAddress, sock->addr);
- chr->opaque = s;
- chr->chr_wait_connected = tcp_chr_wait_connected;
- chr->chr_write = tcp_chr_write;
- chr->chr_sync_read = tcp_chr_sync_read;
- chr->chr_close = tcp_chr_close;
- chr->chr_disconnect = tcp_chr_disconnect;
- chr->get_msgfds = tcp_get_msgfds;
- chr->set_msgfds = tcp_set_msgfds;
- chr->chr_add_client = tcp_chr_add_client;
- chr->chr_add_watch = tcp_chr_add_watch;
- chr->chr_update_read_handler = tcp_chr_update_read_handler;
+ qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE);
+ if (s->is_unix) {
+ qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
+ }
+
/* be isn't opened until we get a connection */
- chr->explicit_be_open = true;
+ *be_opened = false;
chr->filename = SocketAddress_to_str("disconnected:",
addr, is_listen, is_telnet);
if (s->reconnect_time) {
sioc = qio_channel_socket_new();
+ tcp_chr_set_client_ioc_name(chr, sioc);
qio_channel_socket_connect_async(sioc, s->addr,
qemu_chr_socket_connected,
chr, NULL);
} else {
if (s->is_listen) {
+ char *name;
sioc = qio_channel_socket_new();
+
+ name = g_strdup_printf("chardev-tcp-listener-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(sioc), name);
+ g_free(name);
+
if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
goto error;
}
}
}
- return chr;
+ return;
- error:
+error:
if (sioc) {
object_unref(OBJECT(sioc));
}
if (s->tls_creds) {
object_unref(OBJECT(s->tls_creds));
}
- g_free(s);
- qemu_chr_free_common(chr);
- return NULL;
}
-static CharDriverState *qmp_chardev_open_udp(const char *id,
- ChardevBackend *backend,
- ChardevReturn *ret,
- Error **errp)
+static const CharDriver socket_driver = {
+ .kind = CHARDEV_BACKEND_KIND_SOCKET,
+ .parse = qemu_chr_parse_socket,
+};
+
+static void char_socket_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qmp_chardev_open_socket;
+ cc->chr_wait_connected = tcp_chr_wait_connected;
+ cc->chr_write = tcp_chr_write;
+ cc->chr_sync_read = tcp_chr_sync_read;
+ cc->chr_disconnect = tcp_chr_disconnect;
+ cc->get_msgfds = tcp_get_msgfds;
+ cc->set_msgfds = tcp_set_msgfds;
+ cc->chr_add_client = tcp_chr_add_client;
+ cc->chr_add_watch = tcp_chr_add_watch;
+ cc->chr_update_read_handler = tcp_chr_update_read_handler;
+ cc->chr_free = tcp_chr_free;
+}
+
+static const TypeInfo char_socket_type_info = {
+ .name = TYPE_CHARDEV_SOCKET,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(SocketChardev),
+ .class_init = char_socket_class_init,
+};
+
+static void qmp_chardev_open_udp(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
{
ChardevUdp *udp = backend->u.udp.data;
- ChardevCommon *common = qapi_ChardevUdp_base(udp);
QIOChannelSocket *sioc = qio_channel_socket_new();
+ char *name;
+ UdpChardev *s = UDP_CHARDEV(chr);
if (qio_channel_socket_dgram_sync(sioc,
udp->local, udp->remote,
errp) < 0) {
object_unref(OBJECT(sioc));
+ return;
+ }
+
+ name = g_strdup_printf("chardev-udp-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(sioc), name);
+ g_free(name);
+
+ s->ioc = QIO_CHANNEL(sioc);
+ /* be isn't opened until we get a connection */
+ *be_opened = false;
+}
+
+static const CharDriver udp_driver = {
+ .kind = CHARDEV_BACKEND_KIND_UDP,
+ .parse = qemu_chr_parse_udp,
+};
+
+static void char_udp_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = qmp_chardev_open_udp;
+ cc->chr_write = udp_chr_write;
+ cc->chr_update_read_handler = udp_chr_update_read_handler;
+ cc->chr_free = udp_chr_free;
+}
+
+static const TypeInfo char_udp_type_info = {
+ .name = TYPE_CHARDEV_UDP,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(UdpChardev),
+ .class_init = char_udp_class_init,
+};
+
+bool qemu_chr_has_feature(Chardev *chr,
+ CharDriverFeature feature)
+{
+ return test_bit(feature, chr->features);
+}
+
+void qemu_chr_set_feature(Chardev *chr,
+ CharDriverFeature feature)
+{
+ return set_bit(feature, chr->features);
+}
+
+static const ChardevClass *char_get_class(const char *driver, Error **errp)
+{
+ ObjectClass *oc;
+ const ChardevClass *cc;
+ char *typename = g_strdup_printf("chardev-%s", driver);
+
+ oc = object_class_by_name(typename);
+ g_free(typename);
+
+ if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) {
+ error_setg(errp, "'%s' is not a valid char driver name", driver);
return NULL;
}
- return qemu_chr_open_udp(sioc, common, errp);
+
+ if (object_class_is_abstract(oc)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+ "abstract device type");
+ return NULL;
+ }
+
+ cc = CHARDEV_CLASS(oc);
+ if (cc->internal) {
+ error_setg(errp, "'%s' is not a valid char driver name", driver);
+ return NULL;
+ }
+
+ return cc;
+}
+
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+ ChardevBackend *backend, Error **errp)
+{
+ Chardev *chr = NULL;
+ Error *local_err = NULL;
+ bool be_opened = true;
+
+ assert(g_str_has_prefix(typename, "chardev-"));
+
+ chr = CHARDEV(object_new(typename));
+ chr->label = g_strdup(id);
+
+ qemu_char_open(chr, backend, &be_opened, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ object_unref(OBJECT(chr));
+ return NULL;
+ }
+
+ if (!chr->filename) {
+ chr->filename = g_strdup(typename + 8);
+ }
+ if (be_opened) {
+ qemu_chr_be_event(chr, CHR_EVENT_OPENED);
+ }
+
+ return chr;
}
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
Error **errp)
{
- ChardevReturn *ret = g_new0(ChardevReturn, 1);
- CharDriverState *chr = NULL;
- Error *local_err = NULL;
- GSList *i;
- CharDriver *cd;
+ const ChardevClass *cc;
+ ChardevReturn *ret;
+ Chardev *chr;
chr = qemu_chr_find(id);
if (chr) {
error_setg(errp, "Chardev '%s' already exists", id);
- g_free(ret);
return NULL;
}
- for (i = backends; i; i = i->next) {
- cd = i->data;
-
- if (cd->kind == backend->type) {
- chr = cd->create(id, backend, ret, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto out_error;
- }
- break;
- }
+ cc = char_get_class(ChardevBackendKind_lookup[backend->type], errp);
+ if (!cc) {
+ return NULL;
}
- if (chr == NULL) {
- assert(!i);
- error_setg(errp, "chardev backend not available");
- goto out_error;
+ chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
+ backend, errp);
+ if (!chr) {
+ return NULL;
}
- chr->label = g_strdup(id);
- chr->avail_connections =
- (backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
- if (!chr->filename) {
- chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
- }
- if (!chr->explicit_be_open) {
- qemu_chr_be_event(chr, CHR_EVENT_OPENED);
+ ret = g_new0(ChardevReturn, 1);
+ if (CHARDEV_IS_PTY(chr)) {
+ ret->pty = g_strdup(chr->filename + 4);
+ ret->has_pty = true;
}
+
QTAILQ_INSERT_TAIL(&chardevs, chr, next);
return ret;
-
-out_error:
- g_free(ret);
- return NULL;
}
void qmp_chardev_remove(const char *id, Error **errp)
{
- CharDriverState *chr;
+ Chardev *chr;
chr = qemu_chr_find(id);
if (chr == NULL) {
error_setg(errp, "Chardev '%s' not found", id);
return;
}
- if (chr->chr_can_read || chr->chr_read ||
- chr->chr_event || chr->handler_opaque) {
+ if (qemu_chr_is_busy(chr)) {
error_setg(errp, "Chardev '%s' is busy", id);
return;
}
- if (chr->replay) {
+ if (qemu_chr_replay(chr)) {
error_setg(errp,
"Chardev '%s' cannot be unplugged in record/replay mode", id);
return;
void qemu_chr_cleanup(void)
{
- CharDriverState *chr, *tmp;
+ Chardev *chr, *tmp;
QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
qemu_chr_delete(chr);
static void register_types(void)
{
- register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL,
- qemu_chr_open_null);
- register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET,
- qemu_chr_parse_socket, qmp_chardev_open_socket);
- register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
- qmp_chardev_open_udp);
- register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
- qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
- register_char_driver("file", CHARDEV_BACKEND_KIND_FILE,
- qemu_chr_parse_file_out, qmp_chardev_open_file);
- register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO,
- qemu_chr_parse_stdio, qemu_chr_open_stdio);
-#if defined HAVE_CHARDEV_SERIAL
- register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL,
- qemu_chr_parse_serial, qmp_chardev_open_serial);
- register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL,
- qemu_chr_parse_serial, qmp_chardev_open_serial);
+ static const struct {
+ const CharDriver *driver;
+ const TypeInfo *type;
+ } chardevs[] = {
+ { &null_driver, &char_null_type_info },
+ { &socket_driver, &char_socket_type_info },
+ { &udp_driver, &char_udp_type_info },
+ { &ringbuf_driver, &char_ringbuf_type_info },
+ { &file_driver, &char_file_type_info },
+ { &stdio_driver, &char_stdio_type_info },
+#ifdef HAVE_CHARDEV_SERIAL
+ { &serial_driver, &char_serial_type_info },
#endif
#ifdef HAVE_CHARDEV_PARPORT
- register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
- qemu_chr_parse_parallel, qmp_chardev_open_parallel);
- register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL,
- qemu_chr_parse_parallel, qmp_chardev_open_parallel);
+ { ¶llel_driver, &char_parallel_type_info },
#endif
#ifdef HAVE_CHARDEV_PTY
- register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL,
- qemu_chr_open_pty);
+ { &pty_driver, &char_pty_type_info },
#endif
#ifdef _WIN32
- register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
- qemu_chr_open_win_con);
+ { &console_driver, &char_console_type_info },
+#endif
+ { &pipe_driver, &char_pipe_type_info },
+ { &mux_driver, &char_mux_type_info },
+ { &memory_driver, &char_memory_type_info }
+ };
+ int i;
+
+ type_register_static(&char_type_info);
+#ifndef _WIN32
+ type_register_static(&char_fd_type_info);
+#else
+ type_register_static(&char_win_type_info);
+ type_register_static(&char_win_stdio_type_info);
#endif
- register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE,
- qemu_chr_parse_pipe, qemu_chr_open_pipe);
- register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
- qemu_chr_open_mux);
- /* Bug-compatibility: */
- register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
- qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
+ for (i = 0; i < ARRAY_SIZE(chardevs); i++) {
+ type_register_static(chardevs[i].type);
+ register_char_driver(chardevs[i].driver);
+ }
+
/* this must be done after machine init, since we register FEs with muxes
* as part of realize functions like serial_isa_realizefn when -nographic
* is specified