diff options
Diffstat (limited to 'auto-virtserial-guest.c')
-rw-r--r-- | auto-virtserial-guest.c | 231 |
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; +} |