aboutsummaryrefslogtreecommitdiff
path: root/vl.c
diff options
context:
space:
mode:
Diffstat (limited to 'vl.c')
-rw-r--r--vl.c149
1 files changed, 145 insertions, 4 deletions
diff --git a/vl.c b/vl.c
index a60fbae..bb92776 100644
--- a/vl.c
+++ b/vl.c
@@ -2464,21 +2464,162 @@ void cfmakeraw (struct termios *termios_p)
#endif
#if defined(__linux__) || defined(__sun__)
+
+typedef struct {
+ int fd;
+ int connected;
+ int polling;
+ int read_bytes;
+ QEMUTimer *timer;
+} PtyCharDriver;
+
+static void pty_chr_update_read_handler(CharDriverState *chr);
+static void pty_chr_state(CharDriverState *chr, int connected);
+
+static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ if (!s->connected) {
+ /* guest sends data, check for (re-)connect */
+ pty_chr_update_read_handler(chr);
+ return 0;
+ }
+ return unix_write(s->fd, buf, len);
+}
+
+static int pty_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ s->read_bytes = qemu_chr_can_read(chr);
+ return s->read_bytes;
+}
+
+static void pty_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+ int size, len;
+ uint8_t buf[1024];
+
+ len = sizeof(buf);
+ if (len > s->read_bytes)
+ len = s->read_bytes;
+ if (len == 0)
+ return;
+ size = read(s->fd, buf, len);
+ if ((size == -1 && errno == EIO) ||
+ (size == 0)) {
+ pty_chr_state(chr, 0);
+ return;
+ }
+ if (size > 0) {
+ pty_chr_state(chr, 1);
+ qemu_chr_read(chr, buf, size);
+ }
+}
+
+static void pty_chr_update_read_handler(CharDriverState *chr)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ qemu_set_fd_handler2(s->fd, pty_chr_read_poll,
+ pty_chr_read, NULL, chr);
+ s->polling = 1;
+ /*
+ * Short timeout here: just need wait long enougth that qemu makes
+ * it through the poll loop once. When reconnected we want a
+ * short timeout so we notice it almost instantly. Otherwise
+ * read() gives us -EIO instantly, making pty_chr_state() reset the
+ * timeout to the normal (much longer) poll interval before the
+ * timer triggers.
+ */
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10);
+}
+
+static void pty_chr_state(CharDriverState *chr, int connected)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ if (!connected) {
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ s->connected = 0;
+ s->polling = 0;
+ /* (re-)connect poll interval for idle guests: once per second.
+ * We check more frequently in case the guests sends data to
+ * the virtual device linked to our pty. */
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
+ } else {
+ if (!s->connected)
+ qemu_chr_reset(chr);
+ s->connected = 1;
+ }
+}
+
+void pty_chr_timer(void *opaque)
+{
+ struct CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ if (s->connected)
+ return;
+ if (s->polling) {
+ /* If we arrive here without polling being cleared due
+ * read returning -EIO, then we are (re-)connected */
+ pty_chr_state(chr, 1);
+ return;
+ }
+
+ /* Next poll ... */
+ pty_chr_update_read_handler(chr);
+}
+
+static void pty_chr_close(struct CharDriverState *chr)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ close(s->fd);
+ qemu_free(s);
+}
+
static CharDriverState *qemu_chr_open_pty(void)
{
+ CharDriverState *chr;
+ PtyCharDriver *s;
struct termios tty;
- int master_fd, slave_fd;
+ int slave_fd;
- if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) < 0) {
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(PtyCharDriver));
+ if (!s) {
+ qemu_free(chr);
+ return NULL;
+ }
+
+ if (openpty(&s->fd, &slave_fd, NULL, NULL, NULL) < 0) {
return NULL;
}
/* Set raw attributes on the pty. */
cfmakeraw(&tty);
tcsetattr(slave_fd, TCSAFLUSH, &tty);
+ close(slave_fd);
+
+ fprintf(stderr, "char device redirected to %s\n", ptsname(s->fd));
- fprintf(stderr, "char device redirected to %s\n", ptsname(master_fd));
- return qemu_chr_open_fd(master_fd, master_fd);
+ 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;
+
+ s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr);
+
+ return chr;
}
static void tty_serial_init(int fd, int speed,