This adds device, interface, and endpoint commands, along with fixing the ctrl command and adding a new expect ctrl command. Put all together and we can simulate the attachment of a HP Scanjet 4370. Signed-off-by: Jeremy White <jwhite@xxxxxxxxxxxxxxx> --- usbredirtestserver/usbredirtestserver.c | 423 ++++++++++++++++++++------------ 1 file changed, 268 insertions(+), 155 deletions(-) diff --git a/usbredirtestserver/usbredirtestserver.c b/usbredirtestserver/usbredirtestserver.c index e878b42..4937fba 100644 --- a/usbredirtestserver/usbredirtestserver.c +++ b/usbredirtestserver/usbredirtestserver.c @@ -74,11 +74,21 @@ static int verbose = usbredirparser_info; /* 2 */ static int running = 1; typedef struct { + struct usb_redir_control_packet_header ctrl; + unsigned char *data; + int data_len; +} expect_ctrl_t; + +typedef struct { int id; int fd; int cmd_fd; struct usbredirparser *parser; -} private_info_t ; + struct usb_redir_interface_info_header interface_info; + struct usb_redir_ep_info_header ep_info; + struct usb_redir_device_connect_header device_connect; + expect_ctrl_t *expect_ctrl; +} private_info_t; static const struct option longopts[] = { { "port", required_argument, NULL, 'p' }, @@ -139,6 +149,44 @@ static void usage(int exit_code, char *argv0) static void usbredirtestserver_cmdline_parse(private_info_t *info, char *buf); +static int read_cmd(private_info_t *info, char *buf, int buf_size, int *pos) +{ + char *p; + int rc; + + if (info->cmd_fd != -1 && *pos < buf_size) { + memset(buf + *pos, 0, buf_size - *pos); + rc = read(info->cmd_fd, buf + *pos, buf_size - *pos); + if (rc == 0) { + close(info->cmd_fd); + info->cmd_fd = -1; + } + + if (rc < 0) + return rc; + + *pos += rc; + } + + while (*pos > 0 && ! info->expect_ctrl) { + p = strchr(buf, '\n'); + if (!p && info->cmd_fd == -1) + p = buf + strlen(buf); + + if (p) { + *p = '\0'; + usbredirtestserver_cmdline_parse(info, buf); + *pos -= (p - buf + 1); + memmove(buf, p + 1, *pos); + *(buf + *pos) = 0; + if (info->cmd_fd == STDIN_FILENO) + printf("%d> ", info->id); fflush(stdout); + } + } + + return 0; +} + static void run_main_loop(private_info_t *info) { char buf[1024]; @@ -146,7 +194,6 @@ static void run_main_loop(private_info_t *info) fd_set readfds, writefds; int n, nfds; struct timeval tv; - int closed = 0; printf("device %d connected\n", info->id); @@ -157,7 +204,7 @@ static void run_main_loop(private_info_t *info) FD_ZERO(&readfds); FD_ZERO(&writefds); - if (! closed) + if (info->cmd_fd != -1) FD_SET(info->cmd_fd, &readfds); FD_SET(info->fd, &readfds); @@ -179,6 +226,12 @@ static void run_main_loop(private_info_t *info) break; } + if ( (info->cmd_fd != -1 && FD_ISSET(info->cmd_fd, &readfds)) || + pos > 0) { + if (read_cmd(info, buf, sizeof(buf), &pos)) + break; + } + if (FD_ISSET(info->fd, &readfds)) { if (usbredirparser_do_read(info->parser)) { break; @@ -190,35 +243,6 @@ static void run_main_loop(private_info_t *info) } } - 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); @@ -235,6 +259,8 @@ void run_one_device(int fd, char *script_file, int id) uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; int flags; + memset(&private_info, 0, sizeof(private_info)); + if (script_file) { private_info.cmd_fd = open(script_file, O_RDONLY); if (private_info.cmd_fd < 0) { @@ -302,7 +328,7 @@ void run_one_device(int fd, char *script_file, int id) usbredirtestserver_stop_bulk_receiving; */ - /* TODO - usbredirserver can do this; not sure if we want to... + /* 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; } */ @@ -338,7 +364,7 @@ int main(int argc, char *argv[]) int id = 0; char *script_file = NULL; - while ((o = getopt_long(argc, argv, "hp:s:", longopts, NULL)) != -1) { + while ((o = getopt_long(argc, argv, "hp:s:v:", longopts, NULL)) != -1) { switch (o) { case 'p': port = strtol(optarg, &endptr, 10); @@ -434,12 +460,6 @@ int main(int argc, char *argv[]) close(client_fd); } - if (waitpid(-1, &status, WNOHANG)) { - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - printf("Child exited abnormally; stopping.\n"); - break; - } - } } @@ -449,168 +469,198 @@ int main(int argc, char *argv[]) static void usbredirtestserver_cmdline_help(void) { printf("Avaiable commands:\n" - "ctrl <endpoint> <request> <request_type> <value> <index> <length> [data]\n" - "device\n" + "interface n <val>:<class>:<subclass>:<protocol>\n" + "endpoint n <type>:<interval>:<interface>:<max_packet_size>:<max_streams>\n" + "device <speed>:<class>:<subclass>:<protocol>:<vendor>:<product>:<bcd>\n" + "ctrl <endpoint>:<request>:<request_type>:<value>:<index>:<length> [data]\n" + "expect ctrl ...\n" "kill\n" "quit\n" "help\n"); } -static void usbredirtestserver_cmdline_ctrl(private_info_t *info, char *buf) +static int parse_ctrl(char *buf, struct usb_redir_control_packet_header *ctrl, + unsigned char **data, int *data_len) { - 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; - } + int pos1 = 0; + int pos2 = 0; + int i; - 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; + memset(ctrl, 0, sizeof(*ctrl)); + *data_len = 0; + *data = NULL; + + if (7 != sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hx:%hx:%hx %n", + &ctrl->endpoint, + &ctrl->request, + &ctrl->requesttype, + &ctrl->status, + &ctrl->value, + &ctrl->index, + &ctrl->length, + &pos1)) { + fprintf(stderr, "Error scanning '%s'\n", buf); + return -1; } - 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; + *data = malloc(strlen(buf + pos1) + 1); + if (!*data) { + fprintf(stderr, "Out of memory allocating %d!\n", strlen(buf + pos1) + 1); + return -2; } + memset(*data, 0, strlen(buf + pos1) + 1); - arg = strtok(NULL, " \t\n"); - if (arg) { - control_packet.index = strtol(arg, &endptr, 0); + for (i = 0; i < ctrl->length; i++) { + if (sscanf(buf + pos1, "%hhx %n", (*data) + i, &pos2) != 1) { + break; + } + pos1 += pos2; } - if (!arg || *endptr != '\0') { - printf("Missing or invalid index\n"); - goto out; + *data_len = i; + if (i == 0) { + free(*data); + *data = NULL; } - 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; - } + return 0; +} - if (!(control_packet.endpoint & 0x80)) { - int i; +static void usbredirtestserver_cmdline_ctrl(private_info_t *info, char *buf) +{ + unsigned char *data; + int data_len; - data = malloc(control_packet.length); - if (!data) { - fprintf(stderr, "Out of memory!\n"); - close(info->fd); - info->fd= -1; - goto out; - } + struct usb_redir_control_packet_header control_packet; - 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; + if (parse_ctrl(buf, &control_packet, &data, &data_len)) { + close(info->fd); + info->fd = -1; + return; } + 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 expect_ctrl(private_info_t *info, char *buf) +{ + if (info->expect_ctrl) { + fprintf(stderr, "Warning: discarding previous expect_ctrl\n"); + if (info->expect_ctrl->data) + free(info->expect_ctrl->data); + free(info->expect_ctrl); + } + + info->expect_ctrl = malloc(sizeof(*info->expect_ctrl)); + if (parse_ctrl(buf, &info->expect_ctrl->ctrl, + &info->expect_ctrl->data, &info->expect_ctrl->data_len) < 0) { + free(info->expect_ctrl); + info->expect_ctrl = NULL; + } +} + +static void usbredirtestserver_cmdline_expect(private_info_t *info, char *buf) +{ + if (strlen(buf) >= 5 && memcmp(buf, "ctrl ", 5) == 0) + expect_ctrl(info, buf + 5); + + else + fprintf(stderr, "Error: we can only expect ctrl at the moment.\n"); } 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, "%hhx:%hhx:%hhx:%hhx %hx:%hx:%hx", - &device_connect.speed, - &device_connect.device_class, - &device_connect.device_subclass, - &device_connect.device_protocol, + memset(&info->device_connect, 0, sizeof(info->device_connect)); + if (7 != sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hx:%hx:%hx", + &info->device_connect.speed, + &info->device_connect.device_class, + &info->device_connect.device_subclass, + &info->device_connect.device_protocol, - &device_connect.vendor_id, - &device_connect.product_id, - &device_connect.device_version_bcd)) { + &info->device_connect.vendor_id, + &info->device_connect.product_id, + &info->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, "Provide speed:class:subclass:protocol: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; + usbredirparser_send_interface_info(info->parser, &info->interface_info); + usbredirparser_send_ep_info(info->parser, &info->ep_info); + usbredirparser_send_device_connect(info->parser, &info->device_connect); +} + +static void usbredirtestserver_cmdline_endpoint(private_info_t *info, char *buf) +{ + int i; + uint8_t type; + uint8_t interval; + uint8_t interface; + uint16_t max_packet_size; + uint32_t max_streams; + + memset(&info->device_connect, 0, sizeof(info->device_connect)); + if (6 != sscanf(buf, "%d %hhx:%hhx:%hhx:%hx:%x", + &i, &type, &interval, &interface, &max_packet_size, &max_streams)) { + fprintf(stderr, "Error: incorrect endpoint specification.\n"); + fprintf(stderr, "Provide type:interval:interface:max_packet_size:max_streams"); + fprintf(stderr, "All as hex strings.\n"); + return; } - 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 */ + if (i >= 0 && i < 32) { + info->ep_info.type[i] = type; + info->ep_info.interval[i] = interval; + info->ep_info.interface[i] = interface; + info->ep_info.max_packet_size[i] = max_packet_size; + info->ep_info.max_streams[i] = max_streams; } +} - 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_interface(private_info_t *info, char *buf) +{ + int i; + uint8_t interface; + uint8_t class; + uint8_t subclass; + uint8_t protocol; + + memset(&info->device_connect, 0, sizeof(info->device_connect)); + if (5 != sscanf(buf, "%d %hhx:%hhx:%hhx:%hhx", + &i, &interface, &class, &subclass, &protocol)) { + fprintf(stderr, "Error: incorrect interface specification.\n"); + fprintf(stderr, "Provide value:class:subclass:protocol"); + fprintf(stderr, "All as hex strings.\n"); + return; + } + if (i >= 0 && i < 32) { + if (info->interface_info.interface_count < (i + 1)) + info->interface_info.interface_count = i + 1; + info->interface_info.interface[i] = interface; + info->interface_info.interface_class[i] = class; + info->interface_info.interface_subclass[i] = subclass; + info->interface_info.interface_protocol[i] = protocol; + } } + static void usbredirtestserver_cmdline_parse(private_info_t *info, char *buf) { char *p; int len; + if (strlen(buf) == 0 || buf[0] == '#') + return; + /* Compute length of first token */ for (p = buf; *p && *p != ' ' && *p != '\t'; p++) ; @@ -649,6 +699,21 @@ static void usbredirtestserver_cmdline_parse(private_info_t *info, char *buf) return; } + if (len <= 6 && !memcmp(buf, "expect", len)) { + usbredirtestserver_cmdline_expect(info, p); + return; + } + + if (len <= 8 && !memcmp(buf, "endpoint", len)) { + usbredirtestserver_cmdline_endpoint(info, p); + return; + } + + if (len <= 9 && !memcmp(buf, "interface", len)) { + usbredirtestserver_cmdline_interface(info, p); + return; + } + printf("unknown command: '%s', type 'help' for help\n", buf); } @@ -721,14 +786,58 @@ static void usbredirtestserver_get_alt_setting(void *priv, uint64_t id, } +static void check_expect_ctrl(private_info_t *info, + struct usb_redir_control_packet_header *ctrl, + uint8_t *data, int data_len) +{ + int i; + + if (ctrl->endpoint != info->expect_ctrl->ctrl.endpoint || + ctrl->request != info->expect_ctrl->ctrl.request || + ctrl->requesttype != info->expect_ctrl->ctrl.requesttype || + ctrl->status != info->expect_ctrl->ctrl.status || + ctrl->value != info->expect_ctrl->ctrl.value || + ctrl->index != info->expect_ctrl->ctrl.index || + ctrl->length != info->expect_ctrl->ctrl.length) { + fprintf(stderr, "Error: incoming control does not match expected.\n"); + return; + } + + if (data_len != info->expect_ctrl->data_len) { + fprintf(stderr, "Error: incoming control data_len %d does not match expected %d.\n", + data_len, info->expect_ctrl->data_len); + return; + } + + for (i = 0; i < data_len; i++) { + if (data[i] != info->expect_ctrl->data[i]) { + fprintf(stderr, "Error: incoming data[%d] value %x does not match expected %x.\n", + i, data[i], info->expect_ctrl->data[i]); + return; + } + } + + if (info->expect_ctrl->data) + free(info->expect_ctrl->data); + free(info->expect_ctrl); + info->expect_ctrl = NULL; +} + 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); + printf("Control packet id: %"PRIu64", status: %d - %x:%x:%x:%x:%x:%x:%x", + id, control_packet->status, + control_packet->endpoint, + control_packet->request, + control_packet->requesttype, + control_packet->status, + control_packet->value, + control_packet->index, + control_packet->length); if (data_len) { printf(", data:"); @@ -737,6 +846,10 @@ static void usbredirtestserver_control_packet(void *priv, uint64_t id, printf(" %02X", (unsigned int)data[i]); } printf("\n"); + + if (info->expect_ctrl) + check_expect_ctrl(info, control_packet, data, data_len); + usbredirparser_free_packet_data(info->parser, data); } -- 2.1.4 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel