This server can create a large number of dummy usbredir test devices. This is particularly useful in testing the usbredir kernel module. Signed-off-by: Jeremy White <jwhite@xxxxxxxxxxxxxxx> --- Makefile.am | 2 +- configure.ac | 1 + usbredirtestserver/Makefile.am | 5 + usbredirtestserver/usbredirtestserver.c | 762 ++++++++++++++++++++++++++++++++ 4 files changed, 769 insertions(+), 1 deletion(-) create mode 100644 usbredirtestserver/Makefile.am create mode 100644 usbredirtestserver/usbredirtestserver.c diff --git a/Makefile.am b/Makefile.am index 03ef2d0..cdcfde1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS = usbredirparser usbredirhost if ! OS_WIN32 -SUBDIRS += usbredirserver connectkernel usbredirtestclient +SUBDIRS += usbredirserver connectkernel usbredirtestclient usbredirtestserver endif EXTRA_DIST = README.multi-thread usb-redirection-protocol.txt diff --git a/configure.ac b/configure.ac index b3d6598..cc5fcb0 100644 --- a/configure.ac +++ b/configure.ac @@ -66,5 +66,6 @@ usbredirparser/libusbredirparser-0.5.pc usbredirserver/Makefile connectkernel/Makefile usbredirtestclient/Makefile +usbredirtestserver/Makefile ]) AC_OUTPUT diff --git a/usbredirtestserver/Makefile.am b/usbredirtestserver/Makefile.am new file mode 100644 index 0000000..9112b3e --- /dev/null +++ b/usbredirtestserver/Makefile.am @@ -0,0 +1,5 @@ +noinst_PROGRAMS = usbredirtestserver + +usbredirtestserver_SOURCES = usbredirtestserver.c +usbredirtestserver_LDADD = $(top_builddir)/usbredirparser/libusbredirparser.la $(READLINE_LIBS) $(PTHREAD_LIBS) +usbredirtestserver_CFLAGS = -I$(top_srcdir)/usbredirparser diff --git a/usbredirtestserver/usbredirtestserver.c b/usbredirtestserver/usbredirtestserver.c new file mode 100644 index 0000000..d949a12 --- /dev/null +++ b/usbredirtestserver/usbredirtestserver.c @@ -0,0 +1,762 @@ +/* usbredirtestserver.c simple usb network redirection test client (guest). + + Copyright 2010-2011 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede <hdegoede@xxxxxxxxxx> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <getopt.h> +#include <inttypes.h> +#include <unistd.h> +#include <errno.h> +#include <poll.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/time.h> +#include <netdb.h> +#include <netinet/in.h> + +#include "usbredirparser.h" + +/* Macros to go from an endpoint address to an index for our ep array */ +#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) +#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) + +#define TESTSERVER_VERSION "usbredirtestserver " PACKAGE_VERSION + +static void usbredirtestserver_hello(void *priv, struct usb_redir_hello_header *h); +static void usbredirtestserver_reset(void *priv); +static void usbredirtestserver_get_configuration(void *priv, uint64_t id); +static void usbredirtestserver_set_configuration(void *priv, uint64_t id, + struct usb_redir_set_configuration_header *set_configuration); +static void usbredirtestserver_set_alt_setting(void *priv, uint64_t id, + struct usb_redir_set_alt_setting_header *set_alt_setting); +static void usbredirtestserver_get_alt_setting(void *priv, uint64_t id, + struct usb_redir_get_alt_setting_header *get_alt_setting); +static void usbredirtestserver_control_packet(void *priv, uint64_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len); +static void usbredirtestserver_bulk_packet(void *priv, uint64_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len); +static void usbredirtestserver_iso_packet(void *priv, uint64_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len); +static void usbredirtestserver_interrupt_packet(void *priv, uint64_t id, + struct usb_redir_interrupt_packet_header *interrupt_packet, + uint8_t *data, int data_len); + +static int verbose = usbredirparser_info; /* 2 */ +static int running = 1; + +typedef struct { + int id; + int fd; + int cmd_fd; + struct usbredirparser *parser; +} private_info_t ; + +static const struct option longopts[] = { + { "port", required_argument, NULL, 'p' }, + { "verbose", required_argument, NULL, 'v' }, + { "script", required_argument, NULL, 's' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } +}; + +static void usbredirtestserver_log(void *priv, int level, const char *msg) +{ + if (level <= verbose) + fprintf(stderr, "%s\n", msg); +} + +static int usbredirtestserver_read(void *priv, uint8_t *data, int count) +{ + private_info_t *info = (private_info_t *) priv; + + int r = read(info->fd, data, count); + if (r < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + if (r == 0) { /* Server disconnected */ + close(info->fd); + info->fd = -1; + } + return r; +} + +static int usbredirtestserver_write(void *priv, uint8_t *data, int count) +{ + private_info_t *info = (private_info_t *) priv; + + int r = write(info->fd, data, count); + if (r < 0) { + if (errno == EAGAIN) + return 0; + if (errno == EPIPE) { /* Server disconnected */ + close(info->fd); + info->fd = -1; + return 0; + } + return -1; + } + return r; +} + +static void usage(int exit_code, char *argv0) +{ + fprintf(exit_code? stderr:stdout, + "Usage: %s [-p|--port <port>] [-v|--verbose <0-3>] [-s|--script <script-file>] <server>\n", + argv0); + exit(exit_code); +} + +static void usbredirtestserver_cmdline_parse(private_info_t *info, char *buf); + +static void run_main_loop(private_info_t *info) +{ + char buf[1024]; + int pos = 0; + fd_set readfds, writefds; + int n, nfds; + struct timeval tv; + int closed = 0; + + printf("device %d connected\n", info->id); + + if (info->cmd_fd == STDIN_FILENO) + printf("%d> ", info->id); fflush(stdout); + + while (running && info->fd != -1) { + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + if (! closed) + FD_SET(info->cmd_fd, &readfds); + + FD_SET(info->fd, &readfds); + if (usbredirparser_has_data_to_write(info->parser)) { + FD_SET(info->fd, &writefds); + } + nfds = info->fd + 1; + if (info->cmd_fd > info->fd) + nfds = info->cmd_fd + 1; + + tv.tv_sec = 0; + tv.tv_usec = 1000; + n = select(nfds, &readfds, &writefds, NULL, &tv); + if (n == -1) { + if (errno == EINTR) { + continue; + } + perror("select"); + break; + } + + if (FD_ISSET(info->fd, &readfds)) { + if (usbredirparser_do_read(info->parser)) { + break; + } + } + if (FD_ISSET(info->fd, &writefds)) { + if (usbredirparser_do_write(info->parser)) { + break; + } + } + + if (!closed && FD_ISSET(info->cmd_fd, &readfds)) { + char *p; + int rc; + rc = read(info->cmd_fd, buf + pos, sizeof(buf) - pos); +printf("JPW read rc %d\n", rc); + + if (rc == 0) + closed++; + + if (rc < 0) + break; + + pos += rc; + + while (pos > 0) { + p = strchr(buf, '\n'); + if (!p) + p = buf + pos; + if (p) { + *p = '\0'; + usbredirtestserver_cmdline_parse(info, buf); + pos -= (p - buf + 1); + memmove(buf, p + 1, sizeof(buf) - pos); + if (info->cmd_fd == STDIN_FILENO) + printf("%d> ", info->id); fflush(stdout); + } + } + } + + } + if (info->fd != -1) { + close(info->fd); + info->fd = -1; + } + printf("device %d closed\n", info->id); +} + +void run_one_device(int fd, char *script_file, int id) +{ + private_info_t private_info; + struct usbredirparser *parser; + int parser_flags = usbredirparser_fl_usb_host; + uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; + int flags; + + if (script_file) { + private_info.cmd_fd = open(script_file, O_RDONLY); + if (private_info.cmd_fd < 0) { + perror("open script"); + exit(-2); + } + } + else + private_info.cmd_fd = STDIN_FILENO; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + perror("fcntl F_GETFL"); + exit(-1); + } + flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (flags == -1) { + perror("fcntl F_SETFL O_NONBLOCK"); + exit(-1); + } + + parser = usbredirparser_create(); + if (!parser) { + exit(-1); + } + + private_info.fd = fd; + private_info.parser = parser; + private_info.id = id++; + + parser->priv = &private_info; + + parser->log_func = usbredirtestserver_log; + parser->read_func = usbredirtestserver_read; + parser->write_func = usbredirtestserver_write; + parser->hello_func = usbredirtestserver_hello; + parser->reset_func = usbredirtestserver_reset; + parser->control_packet_func = usbredirtestserver_control_packet; + parser->bulk_packet_func = usbredirtestserver_bulk_packet; + parser->iso_packet_func = usbredirtestserver_iso_packet; + parser->interrupt_packet_func = usbredirtestserver_interrupt_packet; + parser->get_configuration_func = usbredirtestserver_get_configuration; + parser->set_configuration_func = usbredirtestserver_set_configuration; + parser->set_alt_setting_func = usbredirtestserver_set_alt_setting; + parser->get_alt_setting_func = usbredirtestserver_get_alt_setting; + +/* + TODO: These functions are what the host program also supports + parser->start_iso_stream_func = usbredirtestserver_start_iso_stream; + parser->stop_iso_stream_func = usbredirtestserver_stop_iso_stream; + parser->start_interrupt_receiving_func = + usbredirtestserver_start_interrupt_receiving; + parser->stop_interrupt_receiving_func = + usbredirtestserver_stop_interrupt_receiving; + parser->alloc_bulk_streams_func = usbredirtestserver_alloc_bulk_streams; + parser->free_bulk_streams_func = usbredirtestserver_free_bulk_streams; + parser->cancel_data_packet_func = usbredirtestserver_cancel_data_packet; + parser->filter_reject_func = usbredirtestserver_filter_reject; + parser->filter_filter_func = usbredirtestserver_filter_filter; + parser->device_disconnect_ack_func = + usbredirtestserver_device_disconnect_ack; + parser->start_bulk_receiving_func = + usbredirtestserver_start_bulk_receiving; + parser->stop_bulk_receiving_func = + usbredirtestserver_stop_bulk_receiving; +*/ + + /* TODO - usbredirserver can do this; not sure if we want to... + if (flags & usbredirhost_fl_write_cb_owns_buffer) { + parser_flags |= usbredirparser_fl_write_cb_owns_buffer; + } */ + + usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); + usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); + usbredirparser_caps_set_cap(caps, usb_redir_cap_device_disconnect_ack); + usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); + usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); + usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length); + usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving); + usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams); + + usbredirparser_init(parser, TESTSERVER_VERSION, caps, USB_REDIR_CAPS_SIZE, + parser_flags); + + run_main_loop(&private_info); + + usbredirparser_destroy(parser); + + exit(!running); +} + + +int main(int argc, char *argv[]) +{ + int o, flags; + char *endptr, *server; + struct addrinfo *r, *res, hints; + struct sigaction act; + int port = 4000; + int server_fd, client_fd; + int on = 1; + struct sockaddr_in serveraddr; + int id = 0; + char *script_file = NULL; + + while ((o = getopt_long(argc, argv, "hp:s:", longopts, NULL)) != -1) { + switch (o) { + case 'p': + port = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + fprintf(stderr, "Inalid value for --port: '%s'\n", optarg); + usage(1, argv[0]); + } + break; + case 'v': + verbose = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + fprintf(stderr, "Inalid value for --verbose: '%s'\n", optarg); + usage(1, argv[0]); + } + break; + case 's': + script_file = strdup(optarg); + if (access(script_file, R_OK)) { + fprintf(stderr, "Cannot read %s\n", script_file); + usage(1, argv[0]); + } + break; + case '?': + case 'h': + usage(o == '?', argv[0]); + break; + } + } + + if (optind != argc) { + fprintf(stderr, "Excess non option arguments\n"); + usage(1, argv[0]); + } + + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd == -1) { + perror("Error creating socket"); + exit(1); + } + + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) { + perror("Error setsockopt(SO_REUSEADDR) failed"); + exit(1); + } + + memset(&serveraddr, 0, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + serveraddr.sin_port = htons(port); + serveraddr.sin_addr.s_addr = INADDR_ANY; + + if (bind(server_fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) { + fprintf(stderr, "Error binding port %d: %s\n", port, strerror(errno)); + exit(1); + } + + if (listen(server_fd, 1)) { + perror("Error listening"); + exit(1); + } + + while (running) { + int status; + fd_set readfds; + int n, nfds; + struct timeval tv; + + FD_ZERO(&readfds); + FD_SET(server_fd, &readfds); + + nfds = server_fd + 1; + + tv.tv_sec = 0; + tv.tv_usec = 1000; + if (select(nfds, &readfds, NULL, NULL, &tv) < 0) { + perror("select"); + break; + } + + if (FD_ISSET(server_fd, &readfds)) { + client_fd = accept(server_fd, NULL, 0); + if (client_fd == -1) { + if (errno == EINTR) { + continue; + } + perror("accept"); + break; + } + + id++; + if (fork() == 0) + run_one_device(client_fd, script_file, id); + else + close(client_fd); + } + + if (waitpid(-1, &status, WNOHANG)) { + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + printf("Child exited abnormally; stopping.\n"); + break; + } + } + } + + + exit(0); +} + +static void usbredirtestserver_cmdline_help(void) +{ + printf("Avaiable commands:\n" + "ctrl <endpoint> <request> <request_type> <value> <index> <length> [data]\n" + "device\n" + "kill\n" + "quit\n" + "help\n"); +} + +static void usbredirtestserver_cmdline_ctrl(private_info_t *info, char *buf) +{ + struct usb_redir_control_packet_header control_packet; + char *arg, *endptr = NULL; + uint8_t *data = NULL; + int data_len; + char *dup = strdup(buf); + + arg = strtok(dup, " \t\n"); + if (arg) { + control_packet.endpoint = strtol(arg, &endptr, 0); + } + if (!arg || *endptr != '\0') { + printf("Missing or invalid endpoint\n"); + goto out; + } + + arg = strtok(NULL, " \t\n"); + if (arg) { + control_packet.request = strtol(arg, &endptr, 0); + } + if (!arg || *endptr != '\0') { + printf("Missing or invalid request\n"); + goto out; + } + + arg = strtok(NULL, " \t\n"); + if (arg) { + control_packet.requesttype = strtol(arg, &endptr, 0); + } + if (!arg || *endptr != '\0') { + printf("Missing or invalid request type\n"); + goto out; + } + + arg = strtok(NULL, " \t\n"); + if (arg) { + control_packet.value = strtol(arg, &endptr, 0); + } + if (!arg || *endptr != '\0') { + printf("Missing or invalid value\n"); + goto out; + } + + arg = strtok(NULL, " \t\n"); + if (arg) { + control_packet.index = strtol(arg, &endptr, 0); + } + if (!arg || *endptr != '\0') { + printf("Missing or invalid index\n"); + goto out; + } + + arg = strtok(NULL, " \t\n"); + if (arg) { + control_packet.length = strtol(arg, &endptr, 0); + } + if (!arg || *endptr != '\0') { + printf("Missing or invalid length\n"); + goto out; + } + + if (!(control_packet.endpoint & 0x80)) { + int i; + + data = malloc(control_packet.length); + if (!data) { + fprintf(stderr, "Out of memory!\n"); + close(info->fd); + info->fd= -1; + goto out; + } + + for (i = 0; i < control_packet.length; i++) { + arg = strtok(NULL, " \t\n"); + if (arg) { + data[i] = strtol(arg, &endptr, 0); + } + if (!arg || *endptr != '\0') { + printf("Missing or invalid data byte(s)\n"); + free(data); + goto out; + } + } + data_len = control_packet.length; + } else { + data_len = 0; + } + usbredirparser_send_control_packet(info->parser, info->id, &control_packet, + data, data_len); + if (data) + free(data); + printf("Sent control packet with id: %u\n", info->id); + info->id++; +out: + free(dup); +} + +static void usbredirtestserver_cmdline_device(private_info_t *info, char *buf) +{ + struct usb_redir_interface_info_header interface_info; + struct usb_redir_ep_info_header ep_info; + struct usb_redir_device_connect_header device_connect; + int i; + + memset(&device_connect, 0, sizeof(device_connect)); + if (7 != sscanf(buf, "%x:%x:%x:%x %x:%x:%x", + &device_connect.speed, + &device_connect.device_class, + &device_connect.device_subclass, + &device_connect.device_protocol, + + &device_connect.vendor_id, + &device_connect.product_id, + &device_connect.device_version_bcd)) { + fprintf(stderr, "Error: incorrect device specification.\n"); + fprintf(stderr, "Provide speed:class:subclass:protocol "); + fprintf(stderr, " vendor:product:bcdver\n"); + fprintf(stderr, "All as hex strings.\n"); + return; + } + + memset(&interface_info, 0, sizeof(interface_info)); + + interface_info.interface_count = 4; + for (i = 0; i < 4; i++) { + interface_info.interface[i] = i; + interface_info.interface_class[i] = device_connect.device_class; + interface_info.interface_subclass[i] = device_connect.device_subclass; + interface_info.interface_protocol[i] = device_connect.device_protocol; + } + + memset(&ep_info, 0, sizeof(ep_info)); + ep_info.type[0] = 0; + ep_info.type[1] = 0 | 0x80; + ep_info.type[2] = 2; + ep_info.type[3] = 2 | 0x80; + for (i = 0; i < 4; i++) { + ep_info.interval[i] = 1; /* TODO */ + ep_info.interface[i] = i; /* TODO */ + ep_info.max_packet_size[i] = 64; + ep_info.max_streams[i] = 0; /* TODO */ + } + + usbredirparser_send_interface_info(info->parser, &interface_info); + usbredirparser_send_ep_info(info->parser, &ep_info); + usbredirparser_send_device_connect(info->parser, &device_connect); + +} + +static void usbredirtestserver_cmdline_parse(private_info_t *info, char *buf) +{ + char *p; + int len; + + /* Compute length of first token */ + for (p = buf; *p && *p != ' ' && *p != '\t'; p++) + ; + len = p - buf; + + /* Point at the next token */ + for (; *p && *p == ' ' || *p == '\t'; p++) + ; + + if (len == 0) + return; + + if (len <= 4) { + if (!memcmp(buf, "help", len)) { + usbredirtestserver_cmdline_help(); + return; + } else if (!memcmp(buf, "quit", len)) { + running = 0; + close(info->fd); + info->fd = -1; + printf("Quit requested\n"); + return; + } else if (!memcmp(buf, "kill", len)) { + close(info->fd); + info->fd = -1; + printf("Terminated this device\n"); + return; + } else if (!memcmp(buf, "ctrl", len)) { + usbredirtestserver_cmdline_ctrl(info, p); + return; + } + } + + if (len <= 6 && !memcmp(buf, "device", len)) { + usbredirtestserver_cmdline_device(info, p); + return; + } + + printf("unknown command: '%s', type 'help' for help\n", buf); +} + +#define TODO_IMPLEMENT printf("Error: %s unimplemented.\n", __FUNCTION__) +static void usbredirtestserver_hello(void *priv, struct usb_redir_hello_header *h) +{ +#define TODO_IMPLEMENT printf("Error: %s unimplemented.\n", __FUNCTION__) + // TODO - issue a connect? +} + + +static void usbredirtestserver_reset(void *priv) +{ + TODO_IMPLEMENT; +} + +static void usbredirtestserver_get_configuration(void *priv, uint64_t id) +{ + private_info_t *info = (private_info_t *) priv; + + printf("get_configuration request %"PRIu64"; sending config 0\n", id); + + struct usb_redir_configuration_status_header status; + + status.status = usb_redir_success; + status.configuration = 0; + + usbredirparser_send_configuration_status(info->parser, id, &status); +} + +static void usbredirtestserver_set_configuration(void *priv, uint64_t id, + struct usb_redir_set_configuration_header *set_config) +{ + private_info_t *info = (private_info_t *) priv; + struct usb_redir_configuration_status_header status; + printf("set_configuration request %"PRIu64"; config %d\n", id, + set_config->configuration); + + status.status = usb_redir_success; + status.configuration = set_config->configuration; + + // TODO - we must send the ep_info and interface_info + // before the status, at least according to the console + //usbredirhost_send_interface_n_ep_info(host); + usbredirparser_send_configuration_status(info->parser, id, &status); +} + +static void usbredirtestserver_set_alt_setting(void *priv, uint64_t id, + struct usb_redir_set_alt_setting_header *set_alt_setting) +{ + private_info_t *info = (private_info_t *) priv; + struct usb_redir_alt_setting_status_header status; + + status.status = usb_redir_success, + status.interface = set_alt_setting->interface; + status.alt = set_alt_setting->alt; /* TODO no clue... */ + + usbredirparser_send_alt_setting_status(info->parser, id, &status); +} + +static void usbredirtestserver_get_alt_setting(void *priv, uint64_t id, + struct usb_redir_get_alt_setting_header *get_alt_setting) +{ + private_info_t *info = (private_info_t *) priv; + struct usb_redir_alt_setting_status_header status; + status.status = usb_redir_success, + status.interface = get_alt_setting->interface; + status.alt = 0; /* TODO no clue... */ + usbredirparser_send_alt_setting_status(info->parser, id, &status); +} + + +static void usbredirtestserver_control_packet(void *priv, uint64_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len) +{ + private_info_t *info = (private_info_t *) priv; + int i; + printf("Control packet id: %"PRIu64", status: %d", id, + control_packet->status); + + if (data_len) { + printf(", data:"); + } + for (i = 0; i < data_len; i++) { + printf(" %02X", (unsigned int)data[i]); + } + printf("\n"); + usbredirparser_free_packet_data(info->parser, data); +} + +static void usbredirtestserver_bulk_packet(void *priv, uint64_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len) +{ + printf("Bulk packet id %"PRIu64"\n", id); +} + +static void usbredirtestserver_iso_packet(void *priv, uint64_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len) +{ + printf("ISO packet id %"PRIu64"\n", id); +} + +static void usbredirtestserver_interrupt_packet(void *priv, uint64_t id, + struct usb_redir_interrupt_packet_header *interrupt_packet, + uint8_t *data, int data_len) +{ + printf("Interrupt packet id %"PRIu64"\n", id); +} -- 2.1.4 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel