summaryrefslogtreecommitdiffstats
path: root/auto-virtserial-guest.c
diff options
context:
space:
mode:
Diffstat (limited to 'auto-virtserial-guest.c')
-rw-r--r--auto-virtserial-guest.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/auto-virtserial-guest.c b/auto-virtserial-guest.c
new file mode 100644
index 0000000..a5c0f57
--- /dev/null
+++ b/auto-virtserial-guest.c
@@ -0,0 +1,231 @@
+/*
+ * auto-virtserial-guest: Exercise various options for virtio-serial ports
+ *
+ * This program accepts commands from the controlling program on the
+ * host. It then spawns tests on the other virtio-serial ports as per
+ * the host's orders.
+ *
+ * Most of these tests have been described in the test-virtserial.c file
+ *
+ * Some information on the virtio serial ports is available at
+ * http://www.linux-kvm.org/page/VMchannel_Requirements
+ * https://fedoraproject.org/wiki/Features/VirtioSerial
+ *
+ * Copyright (C) 2009, Red Hat, Inc.
+ *
+ * Author(s):
+ * Amit Shah <amit.shah@redhat.com>
+ *
+ * Licensed under the GNU General Public License v2. See the file COPYING
+ * for more details.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "virtserial.h"
+
+#define CONTROL_PORT "/dev/vcon1"
+
+/* The fd to work with for read / write requests. Set by the open message */
+static int fd;
+/* The length to read / write. Set by the length message. Unset at close */
+static int length;
+
+static char *get_port_dev(unsigned int nr)
+{
+ char *buf;
+ buf = malloc(strlen("/dev/vcon12") + 1);
+ if (!buf)
+ return NULL;
+ sprintf(buf, "/dev/vcon%u", nr);
+ return buf;
+}
+
+static int open_port(int nr)
+{
+ char *buf;
+
+ buf = get_port_dev(nr);
+ if (!buf)
+ return -ENOMEM;
+ fd = open(get_port_dev(nr), O_RDWR);
+ if (fd == -1)
+ return -errno;
+ return 0;
+}
+
+static int poll_port(int timeout)
+{
+ struct pollfd pollfds[1];
+ int ret;
+
+ pollfds[0].fd = fd;
+ pollfds[0].events = POLLIN | POLLOUT;
+ ret = poll(pollfds, 1, timeout);
+ if (ret == -1)
+ return -errno;
+ if (ret == 0)
+ return ret;
+ return pollfds[0].revents;
+}
+
+static int read_port(int nr)
+{
+ char *buf;
+ int ret;
+
+ if (!length) {
+ /*
+ * Host just wants to read something. In this case, read
+ * a default length of 1024 bytes.
+ */
+ length = 1024;
+ }
+ buf = malloc(length);
+ if (!buf)
+ return -ENOMEM;
+ ret = read(fd, buf, length);
+ free(buf);
+ if (ret == -1)
+ ret = -errno;
+
+ return ret;
+}
+
+static int write_port(int nr)
+{
+ char *buf;
+ int ret;
+
+ if (!length)
+ return 0;
+
+ buf = malloc(length);
+ if (!buf)
+ return -ENOMEM;
+ ret = write(fd, buf, length);
+ free(buf);
+ if (ret == -1)
+ ret = -errno;
+
+ return ret;
+}
+
+static int close_port(int nr)
+{
+ close(fd);
+ length = 0;
+ return 0;
+}
+
+static int set_port_nonblocking(int val)
+{
+ int ret, flags;
+
+ flags = 0;
+ if (val)
+ flags = O_NONBLOCK;
+ ret = fcntl(fd, F_SETFL, flags);
+ if (ret == -1)
+ return -errno;
+ return 0;
+}
+
+static void send_report(int cfd, int ret)
+{
+ struct guest_packet gpkt;
+
+ gpkt.key = KEY_RESULT;
+ gpkt.value = ret;
+ write(cfd, &gpkt, sizeof(gpkt));
+}
+
+int main(int argc, char *argv[])
+{
+ struct guest_packet gpkt;
+ struct pollfd pollfd[1];
+ int ret, cfd;
+
+ ret = access(CONTROL_PORT, R_OK|W_OK);
+ if (ret == -1)
+ error(errno, errno, "No control port found %s", CONTROL_PORT);
+
+back_to_open:
+ cfd = open(CONTROL_PORT, O_RDWR);
+ if (cfd == -1)
+ error(errno, errno, "open control port %s", CONTROL_PORT);
+
+ gpkt.key = KEY_STATUS_OK;
+ gpkt.value = 1;
+ ret = write(cfd, &gpkt, sizeof(gpkt));
+ if (ret == -1)
+ error(errno, errno, "write control port");
+
+ pollfd[0].fd = cfd;
+ pollfd[0].events = POLLIN;
+
+ while (1) {
+ ret = poll(pollfd, 1, -1);
+ if (ret == -1)
+ error(errno, errno, "poll");
+
+ if (!(pollfd[0].revents & POLLIN))
+ continue;
+
+ ret = read(cfd, &gpkt, sizeof(gpkt));
+ if (ret < sizeof(gpkt)) {
+ /*
+ * Out of sync with host. Close port and start over.
+ * For us to get back in sync with host, this port
+ * has to have buffer cachin disabled
+ */
+ close(cfd);
+ goto back_to_open;
+ }
+ switch(gpkt.key) {
+ /* case KEY_STATUS_OK: */
+ /* gpkt.key = KEY_STATUS_OK; */
+ /* gpkt.value = 1; */
+ /* write(cfd, &gpkt, sizeof(gpkt)); */
+ /* break; */
+ case KEY_OPEN:
+ ret = open_port(gpkt.value);
+ send_report(cfd, ret);
+ break;
+ case KEY_CLOSE:
+ ret = close_port(gpkt.value);
+ send_report(cfd, ret);
+ break;
+ case KEY_READ:
+ ret = read_port(gpkt.value);
+ send_report(cfd, ret);
+ break;
+ case KEY_NONBLOCK:
+ ret = set_port_nonblocking(gpkt.value);
+ send_report(cfd, ret);
+ break;
+ case KEY_LENGTH:
+ length = gpkt.value;
+ send_report(cfd, 0);
+ break;
+ case KEY_WRITE:
+ ret = write_port(gpkt.value);
+ send_report(cfd, ret);
+ break;
+ case KEY_POLL:
+ ret = poll_port(gpkt.value);
+ send_report(cfd, ret);
+ break;
+ }
+ }
+ return 0;
+}