/* Keep track if the char device is open */
switch (event) {
case CHR_EVENT_OPENED:
- s->opened = 1;
+ s->be_open = 1;
break;
case CHR_EVENT_CLOSED:
- s->opened = 0;
+ s->be_open = 0;
break;
}
s->chr_event(s->handler_opaque, event);
}
-static gboolean qemu_chr_generic_open_bh(gpointer opaque)
+static gboolean qemu_chr_be_generic_open_bh(gpointer opaque)
{
CharDriverState *s = opaque;
qemu_chr_be_event(s, CHR_EVENT_OPENED);
return FALSE;
}
-void qemu_chr_generic_open(CharDriverState *s)
+void qemu_chr_be_generic_open(CharDriverState *s)
{
if (s->idle_tag == 0) {
- s->idle_tag = g_idle_add(qemu_chr_generic_open_bh, s);
+ s->idle_tag = g_idle_add(qemu_chr_be_generic_open_bh, s);
}
}
return s->chr_write(s, buf, len);
}
+int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
+{
+ int offset = 0;
+ int res;
+
+ while (offset < len) {
+ do {
+ res = s->chr_write(s, buf + offset, len - offset);
+ if (res == -1 && errno == EAGAIN) {
+ g_usleep(100);
+ }
+ } while (res == -1 && errno == EAGAIN);
+
+ if (res == 0) {
+ break;
+ }
+
+ if (res < 0) {
+ return res;
+ }
+
+ offset += res;
+ }
+
+ return offset;
+}
+
int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
{
if (!s->chr_ioctl)
IOEventHandler *fd_event,
void *opaque)
{
+ int fe_open;
+
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
/* chr driver being released. */
++s->avail_connections;
+ fe_open = 0;
+ } else {
+ fe_open = 1;
}
s->chr_can_read = fd_can_read;
s->chr_read = fd_read;
if (s->chr_update_read_handler)
s->chr_update_read_handler(s);
+ if (!s->explicit_fe_open) {
+ if (fe_open) {
+ qemu_chr_fe_open(s);
+ } else {
+ qemu_chr_fe_close(s);
+ }
+ }
+
/* We're connecting to an already opened device, so let's make sure we
also get the open event */
- if (s->opened) {
- qemu_chr_generic_open(s);
+ if (s->be_open) {
+ qemu_chr_be_generic_open(s);
}
}
chr->chr_guest_close = NULL;
/* Muxes are always open on creation */
- qemu_chr_generic_open(chr);
+ qemu_chr_be_generic_open(chr);
return chr;
}
chr->chr_update_read_handler = fd_chr_update_read_handler;
chr->chr_close = fd_chr_close;
- qemu_chr_generic_open(chr);
+ qemu_chr_be_generic_open(chr);
return chr;
}
-static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
{
int fd_in, fd_out;
char filename_in[256], filename_out[256];
- const char *filename = qemu_opt_get(opts, "path");
+ const char *filename = opts->device;
if (filename == NULL) {
fprintf(stderr, "chardev: pipe: no filename given\n");
pty_chr_rearm_timer(chr, 1000);
} else {
if (!s->connected)
- qemu_chr_generic_open(chr);
+ qemu_chr_be_generic_open(chr);
s->connected = 1;
}
}
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_pty(const char *id,
+ ChardevReturn *ret)
{
CharDriverState *chr;
PtyCharDriver *s;
struct termios tty;
- const char *label;
- int master_fd, slave_fd, len;
+ int master_fd, slave_fd;
#if defined(__OpenBSD__) || defined(__DragonFly__)
char pty_name[PATH_MAX];
#define q_ptsname(x) pty_name
chr = g_malloc0(sizeof(CharDriverState));
- len = strlen(q_ptsname(master_fd)) + 5;
- chr->filename = g_malloc(len);
- snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd));
- qemu_opt_set(opts, "path", q_ptsname(master_fd));
+ chr->filename = g_strdup_printf("pty:%s", q_ptsname(master_fd));
+ ret->pty = g_strdup(q_ptsname(master_fd));
+ ret->has_pty = true;
- label = qemu_opts_id(opts);
- fprintf(stderr, "char device redirected to %s%s%s%s\n",
- q_ptsname(master_fd),
- label ? " (label " : "",
- label ? label : "",
- label ? ")" : "");
+ fprintf(stderr, "char device redirected to %s (label %s)\n",
+ q_ptsname(master_fd), id);
s = g_malloc0(sizeof(PtyCharDriver));
chr->opaque = s;
chr->chr_close = pp_close;
chr->opaque = drv;
- qemu_chr_generic_open(chr);
+ qemu_chr_be_generic_open(chr);
return chr;
}
g_free(chr);
return NULL;
}
- qemu_chr_generic_open(chr);
+ qemu_chr_be_generic_open(chr);
return chr;
}
}
-static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
{
- const char *filename = qemu_opt_get(opts, "path");
+ const char *filename = opts->device;
CharDriverState *chr;
WinCharState *s;
g_free(chr);
return NULL;
}
- qemu_chr_generic_open(chr);
+ qemu_chr_be_generic_open(chr);
return chr;
}
s->hcom = fd_out;
chr->opaque = s;
chr->chr_write = win_chr_write;
- qemu_chr_generic_open(chr);
+ qemu_chr_be_generic_open(chr);
return chr;
}
-static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_win_con(void)
{
return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
}
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_udp_fd(int fd)
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
- Error *local_err = NULL;
- int fd = -1;
chr = g_malloc0(sizeof(CharDriverState));
s = g_malloc0(sizeof(NetCharDriver));
- fd = inet_dgram_opts(opts, &local_err);
- if (fd < 0) {
- goto return_err;
- }
-
s->fd = fd;
s->chan = io_channel_from_socket(s->fd);
s->bufcnt = 0;
chr->chr_update_read_handler = udp_chr_update_read_handler;
chr->chr_close = udp_chr_close;
return chr;
+}
-return_err:
- if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
- }
- g_free(chr);
- g_free(s);
- if (fd >= 0) {
- closesocket(fd);
+static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
+{
+ Error *local_err = NULL;
+ int fd = -1;
+
+ fd = inet_dgram_opts(opts, &local_err);
+ if (fd < 0) {
+ return NULL;
}
- return NULL;
+ return qemu_chr_open_udp_fd(fd);
}
/***********************************************************/
if (s->chan) {
s->tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, tcp_chr_read, chr);
}
- qemu_chr_generic_open(chr);
+ qemu_chr_be_generic_open(chr);
}
#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
s->do_nodelay = do_nodelay;
getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host),
serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);
- snprintf(chr->filename, 256, "%s:%s:%s%s%s%s",
+ snprintf(chr->filename, 256, "%s:%s%s%s:%s%s",
is_telnet ? "telnet" : "tcp",
left, host, right, serv,
is_listen ? ",server" : "");
chr->opaque = NULL;
}
-static CharDriverState *qemu_chr_open_ringbuf(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts,
+ Error **errp)
{
CharDriverState *chr;
RingBufCharDriver *d;
chr = g_malloc0(sizeof(CharDriverState));
d = g_malloc(sizeof(*d));
- d->size = qemu_opt_get_size(opts, "size", 0);
- if (d->size == 0) {
- d->size = 65536;
- }
+ d->size = opts->has_size ? opts->size : 65536;
/* The size must be power of 2 */
if (d->size & (d->size - 1)) {
- error_report("size of ringbuf device must be power of two");
+ error_setg(errp, "size of ringbuf chardev must be power of two");
goto fail;
}
return NULL;
}
-#ifdef HAVE_CHARDEV_PARPORT
-
-static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
-{
- const char *filename = qemu_opt_get(opts, "path");
- int fd;
-
- fd = qemu_open(filename, O_RDWR);
- if (fd < 0) {
- return NULL;
- }
- return qemu_chr_open_pp_fd(fd);
-}
-
-#endif
-
static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
backend->serial->device = g_strdup(device);
}
+static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ const char *device = qemu_opt_get(opts, "path");
+
+ if (device == NULL) {
+ error_setg(errp, "chardev: parallel: no device path given");
+ return;
+ }
+ backend->parallel = g_new0(ChardevHostdev, 1);
+ backend->parallel->device = g_strdup(device);
+}
+
+static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ const char *device = qemu_opt_get(opts, "path");
+
+ if (device == NULL) {
+ error_setg(errp, "chardev: pipe: no device path given");
+ return;
+ }
+ backend->pipe = g_new0(ChardevHostdev, 1);
+ backend->pipe->device = g_strdup(device);
+}
+
+static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
+{
+ int val;
+
+ backend->memory = g_new0(ChardevRingbuf, 1);
+
+ val = qemu_opt_get_number(opts, "size", 0);
+ if (val != 0) {
+ backend->memory->has_size = true;
+ backend->memory->size = val;
+ }
+}
+
typedef struct CharDriver {
const char *name;
/* old, pre qapi */
void qemu_chr_fe_open(struct CharDriverState *chr)
{
+ if (chr->fe_open) {
+ return;
+ }
+ chr->fe_open = 1;
if (chr->chr_guest_open) {
chr->chr_guest_open(chr);
}
void qemu_chr_fe_close(struct CharDriverState *chr)
{
+ if (!chr->fe_open) {
+ return;
+ }
+ chr->fe_open = 0;
if (chr->chr_guest_close) {
chr->chr_guest_close(chr);
}
}
-guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
- GIOFunc func, void *user_data)
+int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
+ GIOFunc func, void *user_data)
{
GSource *src;
guint tag;
is_telnet, is_waitconnect, errp);
}
+static CharDriverState *qmp_chardev_open_dgram(ChardevDgram *dgram,
+ Error **errp)
+{
+ int fd;
+
+ fd = socket_dgram(dgram->remote, dgram->local, errp);
+ if (error_is_set(errp)) {
+ return NULL;
+ }
+ return qemu_chr_open_udp_fd(fd);
+}
+
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
Error **errp)
{
case CHARDEV_BACKEND_KIND_PARALLEL:
chr = qmp_chardev_open_parallel(backend->parallel, errp);
break;
+ case CHARDEV_BACKEND_KIND_PIPE:
+ chr = qemu_chr_open_pipe(backend->pipe);
+ break;
case CHARDEV_BACKEND_KIND_SOCKET:
chr = qmp_chardev_open_socket(backend->socket, errp);
break;
+ case CHARDEV_BACKEND_KIND_DGRAM:
+ chr = qmp_chardev_open_dgram(backend->dgram, errp);
+ break;
#ifdef HAVE_CHARDEV_TTY
case CHARDEV_BACKEND_KIND_PTY:
- {
- /* qemu_chr_open_pty sets "path" in opts */
- QemuOpts *opts;
- opts = qemu_opts_create_nofail(qemu_find_opts("chardev"));
- chr = qemu_chr_open_pty(opts);
- ret->pty = g_strdup(qemu_opt_get(opts, "path"));
- ret->has_pty = true;
- qemu_opts_del(opts);
+ chr = qemu_chr_open_pty(id, ret);
break;
- }
#endif
case CHARDEV_BACKEND_KIND_NULL:
chr = qemu_chr_open_null();
case CHARDEV_BACKEND_KIND_STDIO:
chr = qemu_chr_open_stdio(backend->stdio);
break;
+#ifdef _WIN32
+ case CHARDEV_BACKEND_KIND_CONSOLE:
+ chr = qemu_chr_open_win_con();
+ break;
+#endif
+#ifdef CONFIG_SPICE
+ case CHARDEV_BACKEND_KIND_SPICEVMC:
+ chr = qemu_chr_open_spice_vmc(backend->spicevmc->type);
+ break;
+ case CHARDEV_BACKEND_KIND_SPICEPORT:
+ chr = qemu_chr_open_spice_port(backend->spiceport->fqdn);
+ break;
+#endif
+ case CHARDEV_BACKEND_KIND_VC:
+ chr = vc_init(backend->vc);
+ break;
+ case CHARDEV_BACKEND_KIND_MEMORY:
+ chr = qemu_chr_open_ringbuf(backend->memory, errp);
+ break;
default:
error_setg(errp, "unknown chardev backend (%d)", backend->kind);
break;
register_char_driver_qapi("null", CHARDEV_BACKEND_KIND_NULL, NULL);
register_char_driver("socket", qemu_chr_open_socket);
register_char_driver("udp", qemu_chr_open_udp);
- register_char_driver("memory", qemu_chr_open_ringbuf);
+ register_char_driver_qapi("memory", CHARDEV_BACKEND_KIND_MEMORY,
+ qemu_chr_parse_ringbuf);
register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE,
qemu_chr_parse_file_out);
register_char_driver_qapi("stdio", CHARDEV_BACKEND_KIND_STDIO,
qemu_chr_parse_serial);
register_char_driver_qapi("tty", CHARDEV_BACKEND_KIND_SERIAL,
qemu_chr_parse_serial);
-#ifdef _WIN32
- register_char_driver("pipe", qemu_chr_open_win_pipe);
- register_char_driver("console", qemu_chr_open_win_con);
-#else
- register_char_driver("pipe", qemu_chr_open_pipe);
-#endif
-#ifdef HAVE_CHARDEV_TTY
- register_char_driver("pty", qemu_chr_open_pty);
-#endif
-#ifdef HAVE_CHARDEV_PARPORT
- register_char_driver("parallel", qemu_chr_open_pp);
- register_char_driver("parport", qemu_chr_open_pp);
-#endif
+ register_char_driver_qapi("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
+ qemu_chr_parse_parallel);
+ register_char_driver_qapi("parport", CHARDEV_BACKEND_KIND_PARALLEL,
+ qemu_chr_parse_parallel);
+ register_char_driver_qapi("pty", CHARDEV_BACKEND_KIND_PTY, NULL);
+ register_char_driver_qapi("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL);
+ register_char_driver_qapi("pipe", CHARDEV_BACKEND_KIND_PIPE,
+ qemu_chr_parse_pipe);
}
type_init(register_types);