diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/tools/usbtest/Makefile | 40 | ||||
-rw-r--r-- | tools/tools/usbtest/usb_control_ep_test.c | 673 | ||||
-rw-r--r-- | tools/tools/usbtest/usb_modem_test.c | 585 | ||||
-rw-r--r-- | tools/tools/usbtest/usb_msc_test.c | 1290 | ||||
-rw-r--r-- | tools/tools/usbtest/usb_msc_test.h | 120 | ||||
-rw-r--r-- | tools/tools/usbtest/usbtest.c | 809 | ||||
-rw-r--r-- | tools/tools/usbtest/usbtest.h | 62 |
7 files changed, 3579 insertions, 0 deletions
diff --git a/tools/tools/usbtest/Makefile b/tools/tools/usbtest/Makefile new file mode 100644 index 0000000..1cc0c83 --- /dev/null +++ b/tools/tools/usbtest/Makefile @@ -0,0 +1,40 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2013 Hans Petter Selasky. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +PROG= usbtest +MAN= +SRCS+= usbtest.c +SRCS+= usb_msc_test.c +SRCS+= usb_modem_test.c +SRCS+= usb_control_ep_test.c + +LDADD+= -lusb + +WARNS= 3 + +CFLAGS+= -I ${.CURDIR}/../../../sys/dev/usb/gadget + +.include <bsd.prog.mk> diff --git a/tools/tools/usbtest/usb_control_ep_test.c b/tools/tools/usbtest/usb_control_ep_test.c new file mode 100644 index 0000000..d2153c2 --- /dev/null +++ b/tools/tools/usbtest/usb_control_ep_test.c @@ -0,0 +1,673 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007-2010 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <err.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <libusb20.h> +#include <libusb20_desc.h> + +#include <dev/usb/usb_endian.h> +#include <dev/usb/usb.h> +#include <dev/usb/usb_cdc.h> + +#include "usbtest.h" + +static void +set_ctrl_ep_fail(int bus, int dev, int ds_fail, int ss_fail) +{ + int error; + + error = sysctlbyname("hw.usb.ctrl_bus_fail", NULL, NULL, + &bus, sizeof(bus)); + if (error != 0) + goto emissing; + + error = sysctlbyname("hw.usb.ctrl_dev_fail", NULL, NULL, + &dev, sizeof(dev)); + if (error != 0) + goto emissing; + + error = sysctlbyname("hw.usb.ctrl_ds_fail", NULL, NULL, + &ds_fail, sizeof(ds_fail)); + if (error != 0) + goto emissing; + + error = sysctlbyname("hw.usb.ctrl_ss_fail", NULL, NULL, + &ss_fail, sizeof(ss_fail)); + if (error != 0) + goto emissing; + return; + +emissing: + printf("Cannot set USB sysctl, missing USB_REQ_DEBUG option?\n"); +} + +void +usb_control_ep_error_test(uint16_t vid, uint16_t pid) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED req; + struct libusb20_device *pdev; + uint8_t buffer[256]; + int error; + int fail = 0; + int bus; + int dev; + int cfg; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + + bus = libusb20_dev_get_bus_number(pdev); + dev = libusb20_dev_get_address(pdev); + + for (cfg = 0; cfg != 255; cfg++) { + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); + req.bmRequestType = 0x80; /* read */ + req.bRequest = 0x06; /* descriptor */ + req.wValue = 0x0200 | cfg; /* config descriptor */ + req.wIndex = 0; + req.wLength = 255; + + printf("Test #%d.1/3 ...\n", cfg); + + set_ctrl_ep_fail(-1,-1,0,0); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + if (error != 0) { + printf("Last configuration index is: %d\n", cfg - 1); + break; + } + + printf("Test #%d.2/3 ...\n", cfg); + + set_ctrl_ep_fail(bus,dev,1,1); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + + set_ctrl_ep_fail(-1,-1,0,0); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + if (error != 0) { + printf("Cannot fetch descriptor (unexpected)\n"); + fail++; + } + + printf("Test #%d.3/3 ...\n", cfg); + + set_ctrl_ep_fail(bus,dev,0,1); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + + set_ctrl_ep_fail(-1,-1,0,0); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + if (error != 0) { + printf("Cannot fetch descriptor (unexpected)\n"); + fail++; + } + } + + libusb20_dev_close(pdev); + libusb20_dev_free(pdev); + + printf("Test completed detecting %d failures\nDone\n\n", fail); +} + +void +usb_get_string_desc_test(uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + uint32_t x; + uint32_t y; + uint32_t valid; + uint8_t *buf; + int error; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + buf = malloc(256); + if (buf == NULL) { + printf("Cannot allocate memory\n"); + libusb20_dev_free(pdev); + return; + } + valid = 0; + + printf("Starting string descriptor test for " + "VID=0x%04x PID=0x%04x\n", vid, pid); + + for (x = 0; x != 256; x++) { + + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + printf("%d .. ", (int)x); + + fflush(stdout); + + error = libusb20_dev_req_string_simple_sync(pdev, x, buf, 255); + + if (error == 0) { + printf("\nINDEX=%d, STRING='%s' (Default language)\n", (int)x, buf); + fflush(stdout); + } else { + continue; + } + + valid = 0; + + for (y = 0; y != 65536; y++) { + + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + error = libusb20_dev_req_string_sync(pdev, x, y, buf, 256); + if (error == 0) + valid++; + } + + printf("String at INDEX=%d responds to %d " + "languages\n", (int)x, (int)valid); + } + + printf("\nDone\n"); + + free(buf); + + libusb20_dev_free(pdev); +} + +void +usb_port_reset_test(uint16_t vid, uint16_t pid, uint32_t duration) +{ + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + + struct libusb20_device *pdev; + + int error; + int iter; + int errcnt; + + time_t last_sec; + + /* sysctl() - no set config */ + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + libusb20_dev_free(pdev); + printf("Could not open USB device\n"); + return; + } + iter = 0; + + errcnt = 0; + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + while (1) { + + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, ERR=%u\n", + (int)iter, (int)errcnt); + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= duration)) + break; + + if (libusb20_dev_reset(pdev)) { + errcnt++; + usleep(50000); + } + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + iter++; + } + + libusb20_dev_reset(pdev); + + libusb20_dev_free(pdev); +} + +void +usb_set_config_test(uint16_t vid, uint16_t pid, uint32_t duration) +{ + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + int x; + int error; + int failed; + int exp; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + failed = 0; + + printf("Starting set config test for " + "VID=0x%04x PID=0x%04x\n", vid, pid); + + for (x = 255; x > -1; x--) { + + error = libusb20_dev_set_config_index(pdev, x); + if (error == 0) { + if (x == 255) { + printf("Unconfiguring USB device " + "was successful\n"); + } else { + printf("Setting configuration %d " + "was successful\n", x); + } + } else { + failed++; + } + } + + ddesc = libusb20_dev_get_device_desc(pdev); + if (ddesc != NULL) + exp = ddesc->bNumConfigurations + 1; + else + exp = 1; + + printf("\n\n" + "Set configuration summary\n" + "Valid count: %d/%d %s\n" + "Failed count: %d\n", + 256 - failed, exp, + (exp == (256 - failed)) ? "(expected)" : "(unexpected)", + failed); + + libusb20_dev_free(pdev); +} + +void +usb_get_descriptor_test(uint16_t vid, uint16_t pid, uint32_t duration) +{ + struct libusb20_device *pdev; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + libusb20_dev_free(pdev); +} + +void +usb_suspend_resume_test(uint16_t vid, uint16_t pid, uint32_t duration) +{ + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + + struct libusb20_device *pdev; + + time_t last_sec; + + int iter; + int error; + int ptimo; + int errcnt; + int power_old; + + ptimo = 1; /* second(s) */ + + error = sysctlbyname("hw.usb.power_timeout", NULL, NULL, + &ptimo, sizeof(ptimo)); + + if (error != 0) { + printf("WARNING: Could not set power " + "timeout to 1 (error=%d) \n", errno); + } + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + power_old = libusb20_dev_get_power_mode(pdev); + + printf("Starting suspend and resume " + "test for VID=0x%04x PID=0x%04x\n", vid, pid); + + iter = 0; + errcnt = 0; + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + while (1) { + + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, ERR=%u\n", + (int)iter, (int)errcnt); + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= duration)) + break; + + error = libusb20_dev_set_power_mode(pdev, (iter & 1) ? + LIBUSB20_POWER_ON : LIBUSB20_POWER_SAVE); + + if (error) + errcnt++; + + /* wait before switching power mode */ + usleep(4100000 + + (((uint32_t)usb_ts_rand_noise()) % 2000000U)); + + iter++; + } + + /* restore default power mode */ + libusb20_dev_set_power_mode(pdev, power_old); + + libusb20_dev_free(pdev); +} + +void +usb_set_and_clear_stall_test(uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + struct libusb20_transfer *pxfer; + + int iter; + int error; + int errcnt; + int ep; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 1); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + printf("Starting set and clear stall test " + "for VID=0x%04x PID=0x%04x\n", vid, pid); + + iter = 0; + errcnt = 0; + + for (ep = 2; ep != 32; ep++) { + + struct LIBUSB20_CONTROL_SETUP_DECODED setup_set_stall; + struct LIBUSB20_CONTROL_SETUP_DECODED setup_get_status; + + uint8_t epno = ((ep / 2) | ((ep & 1) << 7)); + uint8_t buf[1]; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup_set_stall); + setup_set_stall.bmRequestType = 0x02; /* write endpoint */ + setup_set_stall.bRequest = 0x03; /* set feature */ + setup_set_stall.wValue = 0x00; /* UF_ENDPOINT_HALT */ + setup_set_stall.wIndex = epno; + setup_set_stall.wLength = 0; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup_get_status); + setup_get_status.bmRequestType = 0x82; /* read endpoint */ + setup_get_status.bRequest = 0x00; /* get status */ + setup_get_status.wValue = 0x00; + setup_get_status.wIndex = epno; + setup_get_status.wLength = 1; + + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + pxfer = libusb20_tr_get_pointer(pdev, 0); + + error = libusb20_tr_open(pxfer, 1, 1, epno); + + if (error != 0) { + printf("Endpoint 0x%02x does not exist " + "in current setting. (%s, ignored)\n", + epno, libusb20_strerror(error)); + continue; + } + printf("Stalling endpoint 0x%02x\n", epno); + + /* set stall */ + error = libusb20_dev_request_sync(pdev, + &setup_set_stall, NULL, NULL, 250, 0); + + if (error != 0) { + printf("Endpoint 0x%02x does not allow " + "setting of stall. (%s)\n", + epno, libusb20_strerror(error)); + errcnt++; + } + /* get EP status */ + buf[0] = 0; + error = libusb20_dev_request_sync(pdev, + &setup_get_status, buf, NULL, 250, 0); + + if (error != 0) { + printf("Endpoint 0x%02x does not allow " + "reading status. (%s)\n", + epno, libusb20_strerror(error)); + errcnt++; + } else { + if (!(buf[0] & 1)) { + printf("Endpoint 0x%02x status is " + "not set to stalled\n", epno); + errcnt++; + } + } + + buf[0] = 0; + error = libusb20_tr_bulk_intr_sync(pxfer, buf, 1, NULL, 250); + if (error != LIBUSB20_TRANSFER_STALL) { + printf("Endpoint 0x%02x does not appear to " + "have stalled. Missing stall PID!\n", epno); + errcnt++; + } + printf("Unstalling endpoint 0x%02x\n", epno); + + libusb20_tr_clear_stall_sync(pxfer); + + /* get EP status */ + buf[0] = 0; + error = libusb20_dev_request_sync(pdev, + &setup_get_status, buf, NULL, 250, 0); + + if (error != 0) { + printf("Endpoint 0x%02x does not allow " + "reading status. (%s)\n", + epno, libusb20_strerror(error)); + errcnt++; + } else { + if (buf[0] & 1) { + printf("Endpoint 0x%02x status is " + "still stalled\n", epno); + errcnt++; + } + } + + libusb20_tr_close(pxfer); + iter++; + } + + libusb20_dev_free(pdev); + + printf("\n" + "Test summary\n" + "============\n" + "Endpoints tested: %d\n" + "Errors: %d\n", iter, errcnt); +} + +void +usb_set_alt_interface_test(uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + struct libusb20_config *config; + + int iter; + int error; + int errcnt; + int n; + int m; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + printf("Starting set alternate setting test " + "for VID=0x%04x PID=0x%04x\n", vid, pid); + + config = libusb20_dev_alloc_config(pdev, + libusb20_dev_get_config_index(pdev)); + if (config == NULL) { + printf("Could not get configuration descriptor\n"); + libusb20_dev_free(pdev); + return; + } + iter = 0; + errcnt = 0; + + for (n = 0; n != config->num_interface; n++) { + /* detach kernel driver */ + libusb20_dev_detach_kernel_driver(pdev, n); + + error = libusb20_dev_open(pdev, 0); + if (error) + printf("ERROR could not open device\n"); + + /* Try the alternate settings */ + for (m = 0; m != config->interface[n].num_altsetting; m++) { + + iter++; + + if (libusb20_dev_set_alt_index(pdev, n, m + 1)) { + printf("ERROR on interface %d alt %d\n", n, m + 1); + errcnt++; + } + } + + /* Restore to default */ + + iter++; + + if (libusb20_dev_set_alt_index(pdev, n, 0)) { + printf("ERROR on interface %d alt %d\n", n, 0); + errcnt++; + } + libusb20_dev_close(pdev); + } + + libusb20_dev_free(pdev); + + printf("\n" + "Test summary\n" + "============\n" + "Interfaces tested: %d\n" + "Errors: %d\n", iter, errcnt); +} diff --git a/tools/tools/usbtest/usb_modem_test.c b/tools/tools/usbtest/usb_modem_test.c new file mode 100644 index 0000000..5aec956 --- /dev/null +++ b/tools/tools/usbtest/usb_modem_test.c @@ -0,0 +1,585 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007-2010 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <err.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <libusb20.h> +#include <libusb20_desc.h> + +#include <dev/usb/usb_endian.h> +#include <dev/usb/usb.h> +#include <dev/usb/usb_cdc.h> + +#include "usbtest.h" + +static struct modem { + struct libusb20_transfer *xfer_in; + struct libusb20_transfer *xfer_out; + struct libusb20_device *usb_dev; + + struct bps rx_bytes; + struct bps tx_bytes; + uint32_t c0; + uint32_t c1; + uint32_t out_state; + uint32_t in_last; + uint32_t in_synced; + uint32_t duration; + uint32_t errors; + + uint8_t use_vendor_specific; + uint8_t loop_data; + uint8_t modem_at_mode; + uint8_t data_stress_test; + uint8_t control_ep_test; + uint8_t usb_iface; + uint8_t random_tx_length; + uint8_t random_tx_delay; + +} modem; + +static void +set_defaults(struct modem *p) +{ + memset(p, 0, sizeof(*p)); + + p->data_stress_test = 1; + p->control_ep_test = 1; + p->duration = 60; /* seconds */ +} + +void +do_bps(const char *desc, struct bps *bps, uint32_t len) +{ + bps->bytes += len; +} + +static void +modem_out_state(uint8_t *buf) +{ + if (modem.modem_at_mode) { + switch (modem.out_state & 3) { + case 0: + *buf = 'A'; + break; + case 1: + *buf = 'T'; + break; + case 2: + *buf = '\r'; + break; + default: + *buf = '\n'; + modem.c0++; + break; + } + modem.out_state++; + } else { + *buf = modem.out_state; + modem.out_state++; + modem.out_state %= 255; + } +} + +static void +modem_in_state(uint8_t buf, uint32_t counter) +{ + if ((modem.in_last == 'O') && (buf == 'K')) { + modem.c1++; + modem.in_last = buf; + } else if (buf == modem.in_last) { + modem.c1++; + modem.in_last++; + modem.in_last %= 255; + if (modem.in_synced == 0) { + if (modem.errors < 64) { + printf("Got sync\n"); + } + modem.in_synced = 1; + } + } else { + if (modem.in_synced) { + if (modem.errors < 64) { + printf("Lost sync @ %d, 0x%02x != 0x%02x\n", + counter % 512, buf, modem.in_last); + } + modem.in_synced = 0; + modem.errors++; + } + modem.in_last = buf; + modem.in_last++; + modem.in_last %= 255; + } +} + +static void +modem_write(uint8_t *buf, uint32_t len) +{ + uint32_t n; + + for (n = 0; n != len; n++) { + modem_out_state(buf + n); + } + + do_bps("transmitted", &modem.tx_bytes, len); +} + +static void +modem_read(uint8_t *buf, uint32_t len) +{ + uint32_t n; + + for (n = 0; n != len; n++) { + modem_in_state(buf[n], n); + } + + do_bps("received", &modem.rx_bytes, len); +} + +static void +usb_modem_control_ep_test(struct modem *p, uint32_t duration, uint8_t flag) +{ + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + struct LIBUSB20_CONTROL_SETUP_DECODED setup; + struct usb_cdc_abstract_state ast; + struct usb_cdc_line_state ls; + uint16_t feature = UCDC_ABSTRACT_STATE; + uint16_t state = UCDC_DATA_MULTIPLEXED; + uint8_t iface_no; + uint8_t buf[4]; + int id = 0; + int iter = 0; + + time_t last_sec; + + iface_no = p->usb_iface - 1; + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + printf("\nTest=%d\n", (int)flag); + + while (1) { + + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, COUNT=%u tests/sec ERR=%u\n", + (int)id, + (int)iter, + (int)p->errors); + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + + id++; + + iter = 0; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= duration)) + break; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup); + + if (flag & 1) { + setup.bmRequestType = UT_READ_CLASS_INTERFACE; + setup.bRequest = 0x03; + setup.wValue = 0x0001; + setup.wIndex = iface_no; + setup.wLength = 0x0002; + + if (libusb20_dev_request_sync(p->usb_dev, &setup, buf, NULL, 250, 0)) { + p->errors++; + } + } + if (flag & 2) { + setup.bmRequestType = UT_WRITE_CLASS_INTERFACE; + setup.bRequest = UCDC_SET_COMM_FEATURE; + setup.wValue = feature; + setup.wIndex = iface_no; + setup.wLength = UCDC_ABSTRACT_STATE_LENGTH; + USETW(ast.wState, state); + + if (libusb20_dev_request_sync(p->usb_dev, &setup, &ast, NULL, 250, 0)) { + p->errors++; + } + } + if (flag & 4) { + USETDW(ls.dwDTERate, 115200); + ls.bCharFormat = UCDC_STOP_BIT_1; + ls.bParityType = UCDC_PARITY_NONE; + ls.bDataBits = 8; + + setup.bmRequestType = UT_WRITE_CLASS_INTERFACE; + setup.bRequest = UCDC_SET_LINE_CODING; + setup.wValue = 0; + setup.wIndex = iface_no; + setup.wLength = sizeof(ls); + + if (libusb20_dev_request_sync(p->usb_dev, &setup, &ls, NULL, 250, 0)) { + p->errors++; + } + } + iter++; + } + + printf("\nModem control endpoint test done!\n"); +} + +static void +usb_modem_data_stress_test(struct modem *p, uint32_t duration) +{ + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + + time_t last_sec; + + uint8_t in_pending = 0; + uint8_t in_ready = 0; + uint8_t out_pending = 0; + + uint32_t id = 0; + + uint32_t in_max; + uint32_t out_max; + uint32_t io_max; + + uint8_t *in_buffer = 0; + uint8_t *out_buffer = 0; + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + printf("\n"); + + in_max = libusb20_tr_get_max_total_length(p->xfer_in); + out_max = libusb20_tr_get_max_total_length(p->xfer_out); + + /* get the smallest buffer size and use that */ + io_max = (in_max < out_max) ? in_max : out_max; + + if (in_max != out_max) + printf("WARNING: Buffer sizes are un-equal: %u vs %u\n", in_max, out_max); + + in_buffer = malloc(io_max); + if (in_buffer == NULL) + goto fail; + + out_buffer = malloc(io_max); + if (out_buffer == NULL) + goto fail; + + while (1) { + + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, RX=%u bytes/sec, TX=%u bytes/sec, ERR=%d\n", + (int)id, + (int)p->rx_bytes.bytes, + (int)p->tx_bytes.bytes, + (int)p->errors); + + p->rx_bytes.bytes = 0; + p->tx_bytes.bytes = 0; + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + + id++; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= duration)) + break; + + libusb20_dev_process(p->usb_dev); + + if (!libusb20_tr_pending(p->xfer_in)) { + if (in_pending) { + if (libusb20_tr_get_status(p->xfer_in) == 0) { + modem_read(in_buffer, libusb20_tr_get_length(p->xfer_in, 0)); + } else { + p->errors++; + usleep(10000); + } + in_pending = 0; + in_ready = 1; + } + if (p->loop_data == 0) { + libusb20_tr_setup_bulk(p->xfer_in, in_buffer, io_max, 0); + libusb20_tr_start(p->xfer_in); + in_pending = 1; + in_ready = 0; + } + } + if (!libusb20_tr_pending(p->xfer_out)) { + + uint32_t len; + uint32_t dly; + + if (out_pending) { + if (libusb20_tr_get_status(p->xfer_out) != 0) { + p->errors++; + usleep(10000); + } + } + if (p->random_tx_length) { + len = ((uint32_t)usb_ts_rand_noise()) % ((uint32_t)io_max); + } else { + len = io_max; + } + + if (p->random_tx_delay) { + dly = ((uint32_t)usb_ts_rand_noise()) % 16000U; + } else { + dly = 0; + } + + if (p->loop_data != 0) { + if (in_ready != 0) { + len = libusb20_tr_get_length(p->xfer_in, 0); + memcpy(out_buffer, in_buffer, len); + in_ready = 0; + } else { + len = io_max + 1; + } + if (!libusb20_tr_pending(p->xfer_in)) { + libusb20_tr_setup_bulk(p->xfer_in, in_buffer, io_max, 0); + libusb20_tr_start(p->xfer_in); + in_pending = 1; + } + } else { + modem_write(out_buffer, len); + } + + if (len <= io_max) { + libusb20_tr_setup_bulk(p->xfer_out, out_buffer, len, 0); + + if (dly != 0) + usleep(dly); + + libusb20_tr_start(p->xfer_out); + + out_pending = 1; + } + } + libusb20_dev_wait_process(p->usb_dev, 500); + + if (libusb20_dev_check_connected(p->usb_dev) != 0) { + printf("Device disconnected\n"); + break; + } + } + + libusb20_tr_stop(p->xfer_in); + libusb20_tr_stop(p->xfer_out); + + printf("\nData stress test done!\n"); + +fail: + if (in_buffer) + free(in_buffer); + if (out_buffer) + free(out_buffer); +} + +static void +exec_host_modem_test(struct modem *p, uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + + uint8_t ntest = 0; + uint8_t x; + uint8_t in_ep; + uint8_t out_ep; + uint8_t iface; + + int error; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + + if (p->use_vendor_specific) + find_usb_endpoints(pdev, 255, 255, 255, 0, &iface, &in_ep, &out_ep, 0); + else + find_usb_endpoints(pdev, 2, 2, 1, 0, &iface, &in_ep, &out_ep, 1); + + if ((in_ep == 0) || (out_ep == 0)) { + printf("Could not find USB endpoints\n"); + libusb20_dev_free(pdev); + return; + } + printf("Attaching to: %s @ iface %d\n", + libusb20_dev_get_desc(pdev), iface); + + if (libusb20_dev_open(pdev, 2)) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + if (libusb20_dev_detach_kernel_driver(pdev, iface)) { + printf("WARNING: Could not detach kernel driver\n"); + } + p->xfer_in = libusb20_tr_get_pointer(pdev, 0); + error = libusb20_tr_open(p->xfer_in, 65536 / 4, 1, in_ep); + if (error) { + printf("Could not open USB endpoint %d\n", in_ep); + libusb20_dev_free(pdev); + return; + } + p->xfer_out = libusb20_tr_get_pointer(pdev, 1); + error = libusb20_tr_open(p->xfer_out, 65536 / 4, 1, out_ep); + if (error) { + printf("Could not open USB endpoint %d\n", out_ep); + libusb20_dev_free(pdev); + return; + } + p->usb_dev = pdev; + p->usb_iface = iface; + p->errors = 0; + + if (p->control_ep_test) + ntest += 7; + + if (p->data_stress_test) + ntest += 1; + + if (ntest == 0) { + printf("No tests selected\n"); + } else { + + if (p->control_ep_test) { + for (x = 1; x != 8; x++) { + usb_modem_control_ep_test(p, + (p->duration + ntest - 1) / ntest, x); + } + } + if (p->data_stress_test) { + usb_modem_data_stress_test(p, + (p->duration + ntest - 1) / ntest); + } + } + + printf("\nDone\n"); + + libusb20_dev_free(pdev); +} + +void +show_host_modem_test(uint8_t level, uint16_t vid, uint16_t pid, uint32_t duration) +{ + uint8_t retval; + + set_defaults(&modem); + + modem.duration = duration; + + while (1) { + + retval = usb_ts_show_menu(level, "Modem Test Parameters", + " 1) Execute Data Stress Test: <%s>\n" + " 2) Execute Modem Control Endpoint Test: <%s>\n" + " 3) Use random transmit length: <%s>\n" + " 4) Use random transmit delay: <%s> ms\n" + " 5) Use vendor specific interface: <%s>\n" + "10) Loop data: <%s>\n" + "13) Set test duration: <%d> seconds\n" + "20) Reset parameters\n" + "30) Start test (VID=0x%04x, PID=0x%04x)\n" + "40) Select another device\n" + " x) Return to previous menu \n", + (modem.data_stress_test ? "YES" : "NO"), + (modem.control_ep_test ? "YES" : "NO"), + (modem.random_tx_length ? "YES" : "NO"), + (modem.random_tx_delay ? "16" : "0"), + (modem.use_vendor_specific ? "YES" : "NO"), + (modem.loop_data ? "YES" : "NO"), + (int)(modem.duration), + (int)vid, (int)pid); + + switch (retval) { + case 0: + break; + case 1: + modem.data_stress_test ^= 1; + break; + case 2: + modem.control_ep_test ^= 1; + break; + case 3: + modem.random_tx_length ^= 1; + break; + case 4: + modem.random_tx_delay ^= 1; + break; + case 5: + modem.use_vendor_specific ^= 1; + modem.control_ep_test = 0; + break; + case 10: + modem.loop_data ^= 1; + break; + case 13: + modem.duration = get_integer(); + break; + case 20: + set_defaults(&modem); + break; + case 30: + exec_host_modem_test(&modem, vid, pid); + break; + case 40: + show_host_device_selection(level + 1, &vid, &pid); + break; + default: + return; + } + } +} diff --git a/tools/tools/usbtest/usb_msc_test.c b/tools/tools/usbtest/usb_msc_test.c new file mode 100644 index 0000000..5ece858 --- /dev/null +++ b/tools/tools/usbtest/usb_msc_test.c @@ -0,0 +1,1290 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007-2012 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <err.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <libusb20.h> +#include <libusb20_desc.h> + +#include <dev/usb/usb_endian.h> + +#include "usbtest.h" + +#include "usb_msc_test.h" + +/* Command Block Wrapper */ +typedef struct { + uDWord dCBWSignature; +#define CBWSIGNATURE 0x43425355 + uDWord dCBWTag; + uDWord dCBWDataTransferLength; + uByte bCBWFlags; +#define CBWFLAGS_OUT 0x00 +#define CBWFLAGS_IN 0x80 + uByte bCBWLUN; + uByte bCDBLength; +#define CBWCDBLENGTH 16 + uByte CBWCDB[CBWCDBLENGTH]; +} umass_bbb_cbw_t; + +#define UMASS_BBB_CBW_SIZE 31 + +/* Command Status Wrapper */ +typedef struct { + uDWord dCSWSignature; +#define CSWSIGNATURE 0x53425355 +#define CSWSIGNATURE_IMAGINATION_DBX1 0x43425355 +#define CSWSIGNATURE_OLYMPUS_C1 0x55425355 + uDWord dCSWTag; + uDWord dCSWDataResidue; + uByte bCSWStatus; +#define CSWSTATUS_GOOD 0x0 +#define CSWSTATUS_FAILED 0x1 +#define CSWSTATUS_PHASE 0x2 +} umass_bbb_csw_t; + +#define UMASS_BBB_CSW_SIZE 13 + +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_12 0xa8 +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a +#define SC_WRITE_12 0xaa + +static struct stats { + uint64_t xfer_error; + uint64_t xfer_success; + uint64_t xfer_reset; + uint64_t xfer_rx_bytes; + uint64_t xfer_tx_bytes; + uint64_t data_error; +} stats; + +static uint32_t xfer_current_id; +static uint32_t xfer_wrapper_sig; +static uint32_t block_size = 512; + +static struct libusb20_transfer *xfer_in; +static struct libusb20_transfer *xfer_out; +static struct libusb20_device *usb_pdev; +static uint8_t usb_iface; +static int sense_recurse; + +/* + * SCSI commands sniffed off the wire - LUN maybe needs to be + * adjusted! Refer to "dev/usb/storage/ustorage_fs.c" for more + * information. + */ +static uint8_t mode_sense_6[0x6] = {0x1a, 0, 0x3f, 0, 0x0c}; +static uint8_t read_capacity[0xA] = {0x25,}; +static uint8_t request_sense[0xC] = {0x03, 0, 0, 0, 0x12}; +static uint8_t test_unit_ready[0x6] = {0}; +static uint8_t mode_page_inquiry[0x6] = {0x12, 1, 0x80, 0, 0xff, 0}; +static uint8_t request_invalid[0xC] = {0xEA, 0, 0, 0, 0}; +static uint8_t prevent_removal[0x6] = {0x1E, 0, 0, 0, 1}; +static uint8_t read_toc[0xA] = {0x43, 0x02, 0, 0, 0, 0xAA, 0, 0x0C}; + +#define TIMEOUT_FILTER(x) (x) + +static void usb_request_sense(uint8_t lun); + +static void +do_msc_reset(uint8_t lun) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED setup; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup); + + setup.bmRequestType = LIBUSB20_REQUEST_TYPE_CLASS | + LIBUSB20_RECIPIENT_INTERFACE; + setup.bRequest = 0xFF; /* BBB reset */ + setup.wValue = 0; + setup.wIndex = usb_iface; + setup.wLength = 0; + + if (libusb20_dev_request_sync(usb_pdev, &setup, NULL, NULL, 5000, 0)) { + printf("ERROR: %s\n", __FUNCTION__); + stats.xfer_error++; + } + libusb20_tr_clear_stall_sync(xfer_in); + libusb20_tr_clear_stall_sync(xfer_out); + + stats.xfer_reset++; + + usb_request_sense(lun); +} + +static uint8_t +do_msc_cmd(uint8_t *pcmd, uint8_t cmdlen, void *pdata, uint32_t datalen, + uint8_t isread, uint8_t isshort, uint8_t lun, uint8_t flags) +{ + umass_bbb_cbw_t cbw; + umass_bbb_csw_t csw; + struct libusb20_transfer *xfer_io; + uint32_t actlen; + uint32_t timeout; + int error; + + memset(&cbw, 0, sizeof(cbw)); + + USETDW(cbw.dCBWSignature, xfer_wrapper_sig); + USETDW(cbw.dCBWTag, xfer_current_id); + xfer_current_id++; + USETDW(cbw.dCBWDataTransferLength, datalen); + cbw.bCBWFlags = (isread ? CBWFLAGS_IN : CBWFLAGS_OUT); + cbw.bCBWLUN = lun; + cbw.bCDBLength = cmdlen; + bcopy(pcmd, cbw.CBWCDB, cmdlen); + + actlen = 0; + + timeout = ((datalen + 299999) / 300000) * 1000; + timeout += 5000; + + if ((error = libusb20_tr_bulk_intr_sync(xfer_out, + &cbw, sizeof(cbw), &actlen, TIMEOUT_FILTER(1000)))) { + printf("ERROR: CBW reception: %d\n", error); + do_msc_reset(lun); + return (1); + } + if (actlen != sizeof(cbw)) { + printf("ERROR: CBW length: %d != %d\n", + actlen, (int)sizeof(cbw)); + do_msc_reset(lun); + return (1); + } + if (flags & 1) + datalen /= 2; + + if (datalen != 0) { + xfer_io = isread ? xfer_in : xfer_out; + + if ((error = libusb20_tr_bulk_intr_sync(xfer_io, + pdata, datalen, &actlen, TIMEOUT_FILTER(timeout)))) { + printf("ERROR: Data transfer: %d\n", error); + do_msc_reset(lun); + return (1); + } + if ((actlen != datalen) && (!isshort)) { + printf("ERROR: Short data: %d of %d bytes\n", + actlen, datalen); + do_msc_reset(lun); + return (1); + } + } + actlen = 0; + timeout = 8; + + do { + error = libusb20_tr_bulk_intr_sync(xfer_in, &csw, + sizeof(csw), &actlen, TIMEOUT_FILTER(1000)); + if (error) { + if (error == LIBUSB20_TRANSFER_TIMED_OUT) { + printf("TIMEOUT: Trying to get CSW again. " + "%d tries left.\n", timeout); + } else { + break; + } + } else { + break; + } + } while (--timeout); + + if (error) { + libusb20_tr_clear_stall_sync(xfer_in); + error = libusb20_tr_bulk_intr_sync(xfer_in, &csw, + sizeof(csw), &actlen, TIMEOUT_FILTER(1000)); + if (error) { + libusb20_tr_clear_stall_sync(xfer_in); + printf("ERROR: Could not read CSW: Stalled or " + "timeout (%d).\n", error); + do_msc_reset(lun); + return (1); + } + } + if (UGETDW(csw.dCSWSignature) != CSWSIGNATURE) { + printf("ERROR: Wrong CSW signature\n"); + do_msc_reset(lun); + return (1); + } + if (actlen != sizeof(csw)) { + printf("ERROR: Wrong CSW length: %d != %d\n", + actlen, (int)sizeof(csw)); + do_msc_reset(lun); + return (1); + } + if (csw.bCSWStatus != 0) { + printf("ERROR: CSW status: %d\n", (int)csw.bCSWStatus); + return (1); + } else { + stats.xfer_success++; + return (0); + } +} + +static uint8_t +do_read_10(uint32_t lba, uint32_t len, void *buf, uint8_t lun) +{ + static uint8_t cmd[10]; + uint8_t retval; + + cmd[0] = SC_READ_10; + + len /= block_size; + + cmd[2] = lba >> 24; + cmd[3] = lba >> 16; + cmd[4] = lba >> 8; + cmd[5] = lba >> 0; + + cmd[7] = len >> 8; + cmd[8] = len; + + retval = do_msc_cmd(cmd, 10, buf, len * block_size, 1, 0, lun, 0); + + if (retval) { + printf("ERROR: %s\n", __FUNCTION__); + stats.xfer_error++; + } + return (retval); +} + +static uint8_t +do_write_10(uint32_t lba, uint32_t len, void *buf, uint8_t lun) +{ + static uint8_t cmd[10]; + uint8_t retval; + uint8_t abort; + + cmd[0] = SC_WRITE_10; + + abort = len & 1; + + len /= block_size; + + cmd[2] = lba >> 24; + cmd[3] = lba >> 16; + cmd[4] = lba >> 8; + cmd[5] = lba >> 0; + + cmd[7] = len >> 8; + cmd[8] = len; + + retval = do_msc_cmd(cmd, 10, buf, (len * block_size), 0, 0, lun, abort); + + if (retval) { + printf("ERROR: %s\n", __FUNCTION__); + stats.xfer_error++; + } + return (retval); +} + +static void +do_io_test(struct usb_msc_params *p, uint8_t lun, uint32_t lba_max, + uint8_t *buffer, uint8_t *reference) +{ + uint32_t io_offset; + uint32_t io_size; + uint32_t temp; + uint8_t do_read; + uint8_t retval; + + switch (p->io_mode) { + case USB_MSC_IO_MODE_WRITE_ONLY: + do_read = 0; + break; + case USB_MSC_IO_MODE_READ_WRITE: + do_read = (usb_ts_rand_noise() & 1); + break; + default: + do_read = 1; + break; + } + + switch (p->io_offset) { + case USB_MSC_IO_OFF_RANDOM: + io_offset = usb_ts_rand_noise(); + break; + default: + io_offset = 0; + break; + } + + switch (p->io_delay) { + case USB_MSC_IO_DELAY_RANDOM_10MS: + usleep(((uint32_t)usb_ts_rand_noise()) % 10000U); + break; + case USB_MSC_IO_DELAY_RANDOM_100MS: + usleep(((uint32_t)usb_ts_rand_noise()) % 100000U); + break; + case USB_MSC_IO_DELAY_FIXED_10MS: + usleep(10000); + break; + case USB_MSC_IO_DELAY_FIXED_100MS: + usleep(100000); + break; + default: + break; + } + + switch (p->io_size) { + case USB_MSC_IO_SIZE_RANDOM: + io_size = ((uint32_t)usb_ts_rand_noise()) & 65535U; + break; + case USB_MSC_IO_SIZE_INCREASING: + io_size = (xfer_current_id & 65535U); + break; + case USB_MSC_IO_SIZE_FIXED_1BLK: + io_size = 1; + break; + case USB_MSC_IO_SIZE_FIXED_2BLK: + io_size = 2; + break; + case USB_MSC_IO_SIZE_FIXED_4BLK: + io_size = 4; + break; + case USB_MSC_IO_SIZE_FIXED_8BLK: + io_size = 8; + break; + case USB_MSC_IO_SIZE_FIXED_16BLK: + io_size = 16; + break; + case USB_MSC_IO_SIZE_FIXED_32BLK: + io_size = 32; + break; + case USB_MSC_IO_SIZE_FIXED_64BLK: + io_size = 64; + break; + case USB_MSC_IO_SIZE_FIXED_128BLK: + io_size = 128; + break; + case USB_MSC_IO_SIZE_FIXED_256BLK: + io_size = 256; + break; + case USB_MSC_IO_SIZE_FIXED_512BLK: + io_size = 512; + break; + case USB_MSC_IO_SIZE_FIXED_1024BLK: + io_size = 1024; + break; + default: + io_size = 1; + break; + } + + if (io_size == 0) + io_size = 1; + + io_offset %= lba_max; + + temp = (lba_max - io_offset); + + if (io_size > temp) + io_size = temp; + + if (do_read) { + retval = do_read_10(io_offset, io_size * block_size, + buffer + (io_offset * block_size), lun); + + if (retval == 0) { + if (bcmp(buffer + (io_offset * block_size), + reference + (io_offset * block_size), + io_size * block_size)) { + printf("ERROR: Data comparison failure\n"); + stats.data_error++; + retval = 1; + } + } + stats.xfer_rx_bytes += (io_size * block_size); + + } else { + + retval = do_write_10(io_offset, io_size * block_size, + reference + (io_offset * block_size), lun); + + stats.xfer_tx_bytes += (io_size * block_size); + } + + if ((stats.xfer_error + stats.data_error + + stats.xfer_reset) >= p->max_errors) { + printf("Maximum number of errors exceeded\n"); + p->done = 1; + } +} + +static void +usb_request_sense(uint8_t lun) +{ + uint8_t dummy_buf[255]; + + if (sense_recurse) + return; + + sense_recurse++; + + do_msc_cmd(request_sense, sizeof(request_sense), + dummy_buf, 255, 1, 1, lun, 0); + + sense_recurse--; +} + +static void +usb_msc_test(struct usb_msc_params *p) +{ + struct stats last_stat; + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + uint8_t *buffer = NULL; + uint8_t *reference = NULL; + uint32_t dummy_buf[65536 / 4]; + uint32_t lba_max; + uint32_t x; + uint32_t y; + uint32_t capacity_lba; + uint32_t capacity_bs; + time_t last_sec; + uint8_t lun; + int tries; + + memset(&last_stat, 0, sizeof(last_stat)); + + switch (p->io_lun) { + case USB_MSC_IO_LUN_0: + lun = 0; + break; + case USB_MSC_IO_LUN_1: + lun = 1; + break; + case USB_MSC_IO_LUN_2: + lun = 2; + break; + case USB_MSC_IO_LUN_3: + lun = 3; + break; + default: + lun = 0; + break; + } + + p->done = 0; + + sense_recurse = p->try_sense_on_error ? 0 : 1; + + printf("Resetting device ...\n"); + + do_msc_reset(lun); + + printf("Testing SCSI commands ...\n"); + + if (p->try_all_lun) { + printf("Requesting sense from LUN 0..255 ... "); + for (x = y = 0; x != 256; x++) { + if (do_msc_cmd(mode_sense_6, sizeof(mode_sense_6), + dummy_buf, 255, 1, 1, x, 0)) + y++; + + if (libusb20_dev_check_connected(usb_pdev) != 0) { + printf(" disconnect "); + break; + } + } + printf("Passed=%d, Failed=%d\n", 256 - y, y); + } + do_msc_cmd(mode_sense_6, sizeof(mode_sense_6), + dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), + dummy_buf, 255, 1, 1, lun, 0); + + for (tries = 0; tries != 4; tries++) { + + memset(dummy_buf, 0, sizeof(dummy_buf)); + + if (do_msc_cmd(read_capacity, sizeof(read_capacity), + dummy_buf, 255, 1, 1, lun, 0) != 0) { + printf("Cannot read disk capacity (%u / 4)\n", tries); + if (tries == 3) + return; + usleep(50000); + continue; + } else { + break; + } + } + + capacity_lba = be32toh(dummy_buf[0]); + capacity_bs = be32toh(dummy_buf[1]); + + printf("Disk reports a capacity of LBA=%u and BS=%u\n", + capacity_lba, capacity_bs); + + block_size = capacity_bs; + + if (capacity_bs > 65535) { + printf("Blocksize is too big\n"); + return; + } + if (capacity_bs < 1) { + printf("Blocksize is too small\n"); + return; + } + if (capacity_bs != 512) + printf("INFO: Blocksize is not 512 bytes\n"); + + if (p->try_invalid_scsi_command) { + int status; + + for (tries = 0; tries != 4; tries++) { + + printf("Trying invalid SCSI command: "); + + status = do_msc_cmd(request_invalid, + sizeof(request_invalid), dummy_buf, + 255, 1, 1, lun, 0); + + printf("Result%s as expected\n", status ? "" : " NOT"); + + usleep(50000); + } + } + if (p->try_invalid_wrapper_block) { + int status; + + for (tries = 0; tries != 4; tries++) { + + printf("Trying invalid USB wrapper block signature: "); + + xfer_wrapper_sig = 0x55663322; + + status = do_msc_cmd(read_capacity, + sizeof(read_capacity), dummy_buf, + 255, 1, 1, lun, 0); + + printf("Result%s as expected\n", status ? "" : " NOT"); + + xfer_wrapper_sig = CBWSIGNATURE; + + usleep(50000); + } + } + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(read_capacity, sizeof(read_capacity), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(read_capacity, sizeof(read_capacity), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(mode_page_inquiry, sizeof(mode_page_inquiry), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(mode_page_inquiry, sizeof(mode_page_inquiry), dummy_buf, 255, 1, 1, lun, 0); + + if (do_msc_cmd(prevent_removal, sizeof(prevent_removal), + 0, 0, 1, 1, lun, 0)) { + printf("INFO: Prevent medium removal failed\n"); + } + if (do_msc_cmd(read_toc, sizeof(read_toc), + dummy_buf, 255, 1, 1, lun, 0)) { + printf("INFO: Read Table Of Content failed\n"); + } + if (p->try_last_lba) { + + for (y = 0, x = (1UL << 31); x; x >>= 1) { + if (do_read_10(x | y, block_size, dummy_buf, lun) == 0) + y |= x; + } + + printf("Highest readable LBA: %u (%s), " + "Capacity is %u MBytes\n", y, + (capacity_lba != y) ? "WRONG" : "OK", + (int)((((uint64_t)(y) * (uint64_t)block_size) + + (uint64_t)block_size) / 1000000ULL)); + } else { + + y = capacity_lba; + + printf("Highest readable LBA: %u (not " + "verified), Capacity is %u MBytes\n", y, + (int)((((uint64_t)(y) * (uint64_t)block_size) + + (uint64_t)block_size) / 1000000ULL)); + } + + if (y != 0xFFFFFFFFU) + y++; + + lba_max = y; + + switch (p->io_area) { + case USB_MSC_IO_AREA_1MB: + lba_max = 1024; + break; + case USB_MSC_IO_AREA_16MB: + lba_max = 1024 * 16; + break; + case USB_MSC_IO_AREA_256MB: + lba_max = 1024 * 256; + break; + case USB_MSC_IO_AREA_COMPLETE: + default: + break; + } + + if (lba_max > 65535) + lba_max = 65535; + + printf("Highest testable LBA: %u\n", (int)lba_max); + + buffer = malloc(block_size * lba_max); + if (buffer == NULL) { + printf("ERROR: Could not allocate memory\n"); + goto fail; + } + reference = malloc(block_size * lba_max); + if (reference == NULL) { + printf("ERROR: Could not allocate memory\n"); + goto fail; + } +retry_read_init: + + printf("Setting up initial data pattern, " + "LBA limit = %u ... ", lba_max); + + switch (p->io_mode) { + case USB_MSC_IO_MODE_WRITE_ONCE_READ_ONLY: + case USB_MSC_IO_MODE_WRITE_ONLY: + case USB_MSC_IO_MODE_READ_WRITE: + + switch (p->io_pattern) { + case USB_MSC_IO_PATTERN_FIXED: + for (x = 0; x != (block_size * lba_max); x += 8) { + reference[x + 0] = x >> 24; + reference[x + 1] = x >> 16; + reference[x + 2] = x >> 8; + reference[x + 3] = x >> 0; + reference[x + 4] = 0xFF; + reference[x + 5] = 0x00; + reference[x + 6] = 0xFF; + reference[x + 7] = 0x00; + } + if (do_write_10(0, lba_max * block_size, + reference, lun)) { + printf("FAILED\n"); + lba_max /= 2; + if (lba_max) + goto retry_read_init; + goto fail; + } + printf("SUCCESS\n"); + break; + case USB_MSC_IO_PATTERN_RANDOM: + for (x = 0; x != (block_size * lba_max); x++) { + reference[x] = usb_ts_rand_noise() % 255U; + } + if (do_write_10(0, lba_max * block_size, + reference, lun)) { + printf("FAILED\n"); + lba_max /= 2; + if (lba_max) + goto retry_read_init; + goto fail; + } + printf("SUCCESS\n"); + break; + default: + if (do_read_10(0, lba_max * block_size, + reference, lun)) { + printf("FAILED\n"); + lba_max /= 2; + if (lba_max) + goto retry_read_init; + goto fail; + } + printf("SUCCESS\n"); + break; + } + break; + + default: + if (do_read_10(0, lba_max * block_size, reference, lun)) { + printf("FAILED\n"); + lba_max /= 2; + if (lba_max) + goto retry_read_init; + goto fail; + } + printf("SUCCESS\n"); + break; + } + + + if (p->try_abort_data_write) { + if (do_write_10(0, (2 * block_size) | 1, reference, lun)) + printf("Aborted data write failed (OK)!\n"); + else + printf("Aborted data write did not fail (ERROR)!\n"); + + if (do_read_10(0, (2 * block_size), reference, lun)) + printf("Post-aborted data read failed (ERROR)\n"); + else + printf("Post-aborted data read success (OK)!\n"); + } + printf("Starting test ...\n"); + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + printf("\n"); + + while (1) { + + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, RX=%u bytes/sec, " + "TX=%u bytes/sec, ERR=%u, RST=%u, DERR=%u\n", + (int)xfer_current_id, + (int)(stats.xfer_rx_bytes - + last_stat.xfer_rx_bytes), + (int)(stats.xfer_tx_bytes - + last_stat.xfer_tx_bytes), + (int)(stats.xfer_error), + (int)(stats.xfer_reset), + (int)(stats.data_error)); + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + last_stat = stats; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= p->duration)) + break; + + do_io_test(p, lun, lba_max, buffer, reference); + + if (libusb20_dev_check_connected(usb_pdev) != 0) { + printf("Device disconnected\n"); + break; + } + if (p->done) { + printf("Maximum number of errors exceeded\n"); + break; + } + } + + printf("\nTest done!\n"); + +fail: + if (buffer) + free(buffer); + if (reference) + free(reference); +} + +void +show_host_device_selection(uint8_t level, uint16_t *pvid, uint16_t *ppid) +{ + struct libusb20_backend *pbe; + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + + uint16_t vid[USB_DEVICES_MAX]; + uint16_t pid[USB_DEVICES_MAX]; + + int index; + int sel; + + const char *ptr; + +top: + pbe = libusb20_be_alloc_default(); + pdev = NULL; + index = 0; + + printf("\n[] Select USB device:\n"); + + while ((pdev = libusb20_be_device_foreach(pbe, pdev))) { + + if (libusb20_dev_get_mode(pdev) != LIBUSB20_MODE_HOST) + continue; + + if (index < USB_DEVICES_MAX) { + ddesc = libusb20_dev_get_device_desc(pdev); + ptr = libusb20_dev_get_desc(pdev); + printf("%s%d) %s\n", indent[level], index, ptr); + vid[index] = ddesc->idVendor; + pid[index] = ddesc->idProduct; + index++; + } else { + break; + } + } + + printf("%sr) Refresh device list\n", indent[level]); + printf("%sx) Return to previous menu\n", indent[level]); + + /* release data */ + libusb20_be_free(pbe); + + sel = get_integer(); + + if (sel == -2) + goto top; + + if ((sel < 0) || (sel >= index)) { + *pvid = 0; + *ppid = 0; + return; + } + *pvid = vid[sel]; + *ppid = pid[sel]; +} + +struct libusb20_device * +find_usb_device(uint16_t vid, uint16_t pid) +{ + struct libusb20_backend *pbe = libusb20_be_alloc_default(); + struct libusb20_device *pdev = NULL; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + + while ((pdev = libusb20_be_device_foreach(pbe, pdev))) { + + if (libusb20_dev_get_mode(pdev) != LIBUSB20_MODE_HOST) + continue; + + ddesc = libusb20_dev_get_device_desc(pdev); + + if ((vid == ddesc->idVendor) && + (pid == ddesc->idProduct)) { + libusb20_be_dequeue_device(pbe, pdev); + break; + } + } + + /* release data */ + libusb20_be_free(pbe); + + return (pdev); +} + +void +find_usb_endpoints(struct libusb20_device *pdev, uint8_t class, + uint8_t subclass, uint8_t protocol, uint8_t alt_setting, + uint8_t *pif, uint8_t *in_ep, uint8_t *out_ep, uint8_t next_if) +{ + struct libusb20_config *pcfg; + struct libusb20_interface *iface; + struct libusb20_endpoint *ep; + uint8_t x; + uint8_t y; + uint8_t z; + + *in_ep = 0; + *out_ep = 0; + *pif = 0; + + pcfg = libusb20_dev_alloc_config(pdev, + libusb20_dev_get_config_index(pdev)); + + if (pcfg == NULL) + return; + + for (x = 0; x != pcfg->num_interface; x++) { + + y = alt_setting; + + iface = (pcfg->interface + x); + + if ((iface->desc.bInterfaceClass == class) && + (iface->desc.bInterfaceSubClass == subclass || + subclass == 255) && + (iface->desc.bInterfaceProtocol == protocol || + protocol == 255)) { + + if (next_if) { + x++; + if (x == pcfg->num_interface) + break; + iface = (pcfg->interface + x); + } + *pif = x; + + for (z = 0; z != iface->num_endpoints; z++) { + ep = iface->endpoints + z; + + /* BULK only */ + if ((ep->desc.bmAttributes & 3) != 2) + continue; + + if (ep->desc.bEndpointAddress & 0x80) + *in_ep = ep->desc.bEndpointAddress; + else + *out_ep = ep->desc.bEndpointAddress; + } + break; + } + } + + free(pcfg); +} + +static void +exec_host_msc_test(struct usb_msc_params *p, uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + + uint8_t in_ep; + uint8_t out_ep; + uint8_t iface; + + int error; + + memset(&stats, 0, sizeof(stats)); + + xfer_current_id = 0; + xfer_wrapper_sig = CBWSIGNATURE; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + find_usb_endpoints(pdev, 8, 6, 0x50, 0, &iface, &in_ep, &out_ep, 0); + + if ((in_ep == 0) || (out_ep == 0)) { + printf("Could not find USB endpoints\n"); + libusb20_dev_free(pdev); + return; + } + printf("Attaching to: %s @ iface %d\n", + libusb20_dev_get_desc(pdev), iface); + + if (libusb20_dev_open(pdev, 2)) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + if (libusb20_dev_detach_kernel_driver(pdev, iface)) { + printf("WARNING: Could not detach kernel driver\n"); + } + xfer_in = libusb20_tr_get_pointer(pdev, 0); + error = libusb20_tr_open(xfer_in, 65536, 1, in_ep); + if (error) { + printf("Could not open USB endpoint %d\n", in_ep); + libusb20_dev_free(pdev); + return; + } + xfer_out = libusb20_tr_get_pointer(pdev, 1); + error = libusb20_tr_open(xfer_out, 65536, 1, out_ep); + if (error) { + printf("Could not open USB endpoint %d\n", out_ep); + libusb20_dev_free(pdev); + return; + } + usb_pdev = pdev; + usb_iface = iface; + + usb_msc_test(p); + + libusb20_dev_free(pdev); +} + +static void +set_defaults(struct usb_msc_params *p) +{ + memset(p, 0, sizeof(*p)); + + p->duration = 60; /* seconds */ + p->try_invalid_scsi_command = 1; + p->try_invalid_wrapper_block = 1; + p->try_last_lba = 1; + p->max_errors = -1; +} + +static const char * +get_io_mode(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_mode) { + case USB_MSC_IO_MODE_READ_ONLY: + return ("Read Only"); + case USB_MSC_IO_MODE_WRITE_ONCE_READ_ONLY: + return ("Write Once, Read Only"); + case USB_MSC_IO_MODE_WRITE_ONLY: + return ("Write Only"); + case USB_MSC_IO_MODE_READ_WRITE: + return ("Read and Write"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_pattern(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_pattern) { + case USB_MSC_IO_PATTERN_FIXED: + return ("Fixed"); + case USB_MSC_IO_PATTERN_RANDOM: + return ("Random"); + case USB_MSC_IO_PATTERN_PRESERVE: + return ("Preserve"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_size(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_size) { + case USB_MSC_IO_SIZE_RANDOM: + return ("Random"); + case USB_MSC_IO_SIZE_INCREASING: + return ("Increasing"); + case USB_MSC_IO_SIZE_FIXED_1BLK: + return ("Single block"); + case USB_MSC_IO_SIZE_FIXED_2BLK: + return ("2 blocks"); + case USB_MSC_IO_SIZE_FIXED_4BLK: + return ("4 blocks"); + case USB_MSC_IO_SIZE_FIXED_8BLK: + return ("8 blocks"); + case USB_MSC_IO_SIZE_FIXED_16BLK: + return ("16 blocks"); + case USB_MSC_IO_SIZE_FIXED_32BLK: + return ("32 blocks"); + case USB_MSC_IO_SIZE_FIXED_64BLK: + return ("64 blocks"); + case USB_MSC_IO_SIZE_FIXED_128BLK: + return ("128 blocks"); + case USB_MSC_IO_SIZE_FIXED_256BLK: + return ("256 blocks"); + case USB_MSC_IO_SIZE_FIXED_512BLK: + return ("512 blocks"); + case USB_MSC_IO_SIZE_FIXED_1024BLK: + return ("1024 blocks"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_delay(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_delay) { + case USB_MSC_IO_DELAY_NONE: + return ("None"); + case USB_MSC_IO_DELAY_RANDOM_10MS: + return ("Random 10ms"); + case USB_MSC_IO_DELAY_RANDOM_100MS: + return ("Random 100ms"); + case USB_MSC_IO_DELAY_FIXED_10MS: + return ("Fixed 10ms"); + case USB_MSC_IO_DELAY_FIXED_100MS: + return ("Fixed 100ms"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_offset(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_offset) { + case USB_MSC_IO_OFF_START_OF_DISK: + return ("Start Of Disk"); + case USB_MSC_IO_OFF_RANDOM: + return ("Random Offset"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_area(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_area) { + case USB_MSC_IO_AREA_COMPLETE: + return ("Complete Disk"); + case USB_MSC_IO_AREA_1MB: + return ("First MegaByte"); + case USB_MSC_IO_AREA_16MB: + return ("First 16 MegaBytes"); + case USB_MSC_IO_AREA_256MB: + return ("First 256 MegaBytes"); + default: + return ("Unknown"); + } +} + +void +show_host_msc_test(uint8_t level, uint16_t vid, + uint16_t pid, uint32_t duration) +{ + struct usb_msc_params params; + uint8_t retval; + + set_defaults(¶ms); + + params.duration = duration; + + while (1) { + + retval = usb_ts_show_menu(level, + "Mass Storage Test Parameters", + " 1) Toggle I/O mode: <%s>\n" + " 2) Toggle I/O size: <%s>\n" + " 3) Toggle I/O delay: <%s>\n" + " 4) Toggle I/O offset: <%s>\n" + " 5) Toggle I/O area: <%s>\n" + " 6) Toggle I/O pattern: <%s>\n" + " 7) Toggle try invalid SCSI command: <%s>\n" + " 8) Toggle try invalid wrapper block: <%s>\n" + " 9) Toggle try invalid MaxPacketSize: <%s>\n" + "10) Toggle try last Logical Block Address: <%s>\n" + "11) Toggle I/O lun: <%d>\n" + "12) Set maximum number of errors: <%d>\n" + "13) Set test duration: <%d> seconds\n" + "14) Toggle try aborted write transfer: <%s>\n" + "15) Toggle request sense on error: <%s>\n" + "16) Toggle try all LUN: <%s>\n" + "20) Reset parameters\n" + "30) Start test (VID=0x%04x, PID=0x%04x)\n" + "40) Select another device\n" + " x) Return to previous menu \n", + get_io_mode(¶ms), + get_io_size(¶ms), + get_io_delay(¶ms), + get_io_offset(¶ms), + get_io_area(¶ms), + get_io_pattern(¶ms), + (params.try_invalid_scsi_command ? "YES" : "NO"), + (params.try_invalid_wrapper_block ? "YES" : "NO"), + (params.try_invalid_max_packet_size ? "YES" : "NO"), + (params.try_last_lba ? "YES" : "NO"), + params.io_lun, + (int)params.max_errors, + (int)params.duration, + (params.try_abort_data_write ? "YES" : "NO"), + (params.try_sense_on_error ? "YES" : "NO"), + (params.try_all_lun ? "YES" : "NO"), + vid, pid); + switch (retval) { + case 0: + break; + case 1: + params.io_mode++; + params.io_mode %= USB_MSC_IO_MODE_MAX; + break; + case 2: + params.io_size++; + params.io_size %= USB_MSC_IO_SIZE_MAX; + break; + case 3: + params.io_delay++; + params.io_delay %= USB_MSC_IO_DELAY_MAX; + break; + case 4: + params.io_offset++; + params.io_offset %= USB_MSC_IO_OFF_MAX; + break; + case 5: + params.io_area++; + params.io_area %= USB_MSC_IO_AREA_MAX; + break; + case 6: + params.io_pattern++; + params.io_pattern %= USB_MSC_IO_PATTERN_MAX; + break; + case 7: + params.try_invalid_scsi_command ^= 1; + break; + case 8: + params.try_invalid_wrapper_block ^= 1; + break; + case 9: + params.try_invalid_max_packet_size ^= 1; + break; + case 10: + params.try_last_lba ^= 1; + break; + case 11: + params.io_lun++; + params.io_lun %= USB_MSC_IO_LUN_MAX; + break; + case 12: + params.max_errors = get_integer(); + break; + case 13: + params.duration = get_integer(); + break; + case 14: + params.try_abort_data_write ^= 1; + break; + case 15: + params.try_sense_on_error ^= 1; + break; + case 16: + params.try_all_lun ^= 1; + break; + case 20: + set_defaults(¶ms); + break; + case 30: + exec_host_msc_test(¶ms, vid, pid); + break; + case 40: + show_host_device_selection(level + 1, &vid, &pid); + break; + default: + return; + } + } +} diff --git a/tools/tools/usbtest/usb_msc_test.h b/tools/tools/usbtest/usb_msc_test.h new file mode 100644 index 0000000..3af7b08 --- /dev/null +++ b/tools/tools/usbtest/usb_msc_test.h @@ -0,0 +1,120 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _USB_MSC_TEST_H_ +#define _USB_MSC_TEST_H_ + +enum { + USB_MSC_IO_MODE_READ_ONLY, + USB_MSC_IO_MODE_WRITE_ONCE_READ_ONLY, + USB_MSC_IO_MODE_WRITE_ONLY, + USB_MSC_IO_MODE_READ_WRITE, + USB_MSC_IO_MODE_MAX, +}; + +enum { + USB_MSC_IO_PATTERN_FIXED, + USB_MSC_IO_PATTERN_RANDOM, + USB_MSC_IO_PATTERN_PRESERVE, + USB_MSC_IO_PATTERN_MAX, +}; + +enum { + USB_MSC_IO_SIZE_RANDOM, + USB_MSC_IO_SIZE_INCREASING, + USB_MSC_IO_SIZE_FIXED_1BLK, + USB_MSC_IO_SIZE_FIXED_2BLK, + USB_MSC_IO_SIZE_FIXED_4BLK, + USB_MSC_IO_SIZE_FIXED_8BLK, + USB_MSC_IO_SIZE_FIXED_16BLK, + USB_MSC_IO_SIZE_FIXED_32BLK, + USB_MSC_IO_SIZE_FIXED_64BLK, + USB_MSC_IO_SIZE_FIXED_128BLK, + USB_MSC_IO_SIZE_FIXED_256BLK, + USB_MSC_IO_SIZE_FIXED_512BLK, + USB_MSC_IO_SIZE_FIXED_1024BLK, + USB_MSC_IO_SIZE_MAX, +}; + +enum { + USB_MSC_IO_DELAY_NONE, + USB_MSC_IO_DELAY_RANDOM_10MS, + USB_MSC_IO_DELAY_RANDOM_100MS, + USB_MSC_IO_DELAY_FIXED_10MS, + USB_MSC_IO_DELAY_FIXED_100MS, + USB_MSC_IO_DELAY_MAX, +}; + +enum { + USB_MSC_IO_OFF_START_OF_DISK, + USB_MSC_IO_OFF_RANDOM, + USB_MSC_IO_OFF_MAX, +}; + +enum { + USB_MSC_IO_AREA_COMPLETE, + USB_MSC_IO_AREA_1MB, + USB_MSC_IO_AREA_16MB, + USB_MSC_IO_AREA_256MB, + USB_MSC_IO_AREA_MAX, +}; + +enum { + USB_MSC_IO_LUN_0, + USB_MSC_IO_LUN_1, + USB_MSC_IO_LUN_2, + USB_MSC_IO_LUN_3, + USB_MSC_IO_LUN_MAX, +}; + +struct usb_msc_params { + + uint32_t duration; + uint32_t max_errors; + + /* See "USB_MSC_XXX" enums */ + + uint8_t io_mode; + uint8_t io_size; + uint8_t io_delay; + uint8_t io_offset; + uint8_t io_area; + uint8_t io_pattern; + uint8_t io_lun; + + /* booleans */ + uint8_t try_invalid_scsi_command; + uint8_t try_invalid_wrapper_block; + uint8_t try_invalid_max_packet_size; + uint8_t try_last_lba; + uint8_t try_abort_data_write; + uint8_t try_sense_on_error; + uint8_t try_all_lun; + + uint8_t done; +}; + +#endif /* _USB_MSC_TEST_H_ */ diff --git a/tools/tools/usbtest/usbtest.c b/tools/tools/usbtest/usbtest.c new file mode 100644 index 0000000..725b9ea --- /dev/null +++ b/tools/tools/usbtest/usbtest.c @@ -0,0 +1,809 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdint.h> +#include <err.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> + +#include <sys/types.h> +#include <sys/sysctl.h> + +#include <dev/usb/usb_ioctl.h> + +#include "usbtest.h" + +#include <g_keyboard.h> +#include <g_mouse.h> +#include <g_modem.h> +#include <g_audio.h> + +static uint8_t usb_ts_select[USB_TS_MAX_LEVELS]; + +const char *indent[USB_TS_MAX_LEVELS] = { + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", +}; + +/* a perceptual white noise generator (after HPS' invention) */ + +int32_t +usb_ts_rand_noise(void) +{ + uint32_t temp; + const uint32_t prime = 0xFFFF1D; + static uint32_t noise_rem = 1; + + if (noise_rem & 1) { + noise_rem += prime; + } + noise_rem /= 2; + + temp = noise_rem; + + /* unsigned to signed conversion */ + + temp ^= 0x800000; + if (temp & 0x800000) { + temp |= (-0x800000); + } + return temp; +} + +uint8_t +usb_ts_show_menu(uint8_t level, const char *title, const char *fmt,...) +{ + va_list args; + uint8_t x; + uint8_t retval; + char *pstr; + char buf[16]; + char menu[80 * 20]; + + va_start(args, fmt); + vsnprintf(menu, sizeof(menu), fmt, args); + va_end(args); + + printf("["); + + for (x = 0; x != level; x++) { + if ((x + 1) == level) + printf("%d", usb_ts_select[x]); + else + printf("%d.", usb_ts_select[x]); + } + + printf("] - %s:\n\n", title); + + x = 1; + for (pstr = menu; *pstr; pstr++) { + if (x != 0) { + printf("%s", indent[level]); + x = 0; + } + printf("%c", *pstr); + + if (*pstr == '\n') + x = 1; + } + + printf("\n>"); + + if (fgets(buf, sizeof(buf), stdin) == NULL) + err(1, "Cannot read input"); + + if (buf[0] == 'x') + retval = 255; + else + retval = atoi(buf); + + usb_ts_select[level] = retval; + + return (retval); +} + +void +get_string(char *ptr, int size) +{ + printf("\nEnter string>"); + + if (fgets(ptr, size, stdin) == NULL) + err(1, "Cannot read input"); + + ptr[size - 1] = 0; + + size = strlen(ptr); + + /* strip trailing newline, if any */ + if (size == 0) + return; + else if (ptr[size - 1] == '\n') + ptr[size - 1] = 0; +} + +int +get_integer(void) +{ + char buf[32]; + + printf("\nEnter integer value>"); + + if (fgets(buf, sizeof(buf), stdin) == NULL) + err(1, "Cannot read input"); + + if (strcmp(buf, "x\n") == 0) + return (-1); + if (strcmp(buf, "r\n") == 0) + return (-2); + + return ((int)strtol(buf, 0, 0)); +} + +static void +set_template(int template) +{ + int error; + + error = sysctlbyname("hw.usb.template", NULL, NULL, + &template, sizeof(template)); + + if (error != 0) { + printf("WARNING: Could not set USB template " + "to %d (error=%d)\n", template, errno); + } +} + +static void +show_default_audio_select(uint8_t level) +{ + int error; + int retval; + int mode = 0; + int pattern_interval = 128; + int throughput = 0; + size_t len; + char pattern[G_AUDIO_MAX_STRLEN] = {"0123456789abcdef"}; + + set_template(USB_TEMP_AUDIO); + + while (1) { + + error = sysctlbyname("hw.usb.g_audio.mode", NULL, NULL, + &mode, sizeof(mode)); + + if (error != 0) { + printf("WARNING: Could not set audio mode " + "to %d (error=%d)\n", mode, errno); + } + error = sysctlbyname("hw.usb.g_audio.pattern_interval", NULL, NULL, + &pattern_interval, sizeof(pattern_interval)); + + if (error != 0) { + printf("WARNING: Could not set pattern interval " + "to %d (error=%d)\n", pattern_interval, errno); + } + len = sizeof(throughput); + + error = sysctlbyname("hw.usb.g_audio.throughput", + &throughput, &len, 0, 0); + + if (error != 0) { + printf("WARNING: Could not get throughput " + "(error=%d)\n", errno); + } + error = sysctlbyname("hw.usb.g_audio.pattern", NULL, NULL, + &pattern, strlen(pattern)); + + if (error != 0) { + printf("WARNING: Could not set audio pattern " + "to '%s' (error=%d)\n", pattern, errno); + } + retval = usb_ts_show_menu(level, "Default Audio Settings", + "1) Set Silent mode %s\n" + "2) Set Dump mode %s\n" + "3) Set Loop mode %s\n" + "4) Set Pattern mode %s\n" + "5) Change DTMF pattern: '%s'\n" + "6) Change pattern advance interval: %d ms\n" + "x) Return to previous menu\n" + "s: Ready for enumeration\n" + "t: Throughput: %d bytes/second\n", + (mode == G_AUDIO_MODE_SILENT) ? "(selected)" : "", + (mode == G_AUDIO_MODE_DUMP) ? "(selected)" : "", + (mode == G_AUDIO_MODE_LOOP) ? "(selected)" : "", + (mode == G_AUDIO_MODE_PATTERN) ? "(selected)" : "", + pattern, pattern_interval, throughput); + + switch (retval) { + case 0: + break; + case 1: + mode = G_AUDIO_MODE_SILENT; + break; + case 2: + mode = G_AUDIO_MODE_DUMP; + break; + case 3: + mode = G_AUDIO_MODE_LOOP; + break; + case 4: + mode = G_AUDIO_MODE_PATTERN; + break; + case 5: + get_string(pattern, sizeof(pattern)); + break; + case 6: + pattern_interval = get_integer(); + break; + default: + return; + } + } +} + +static void +show_device_audio_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Audio Device Model", + "1) Generic Audio Device\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_default_audio_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_device_msc_select(uint8_t level) +{ + set_template(USB_TEMP_MSC); +} + +static void +show_device_ethernet_select(uint8_t level) +{ + set_template(USB_TEMP_CDCE); +} + +static void +show_default_keyboard_select(uint8_t level) +{ + int error; + int retval; + int mode = 0; + int interval = 1023; + char pattern[G_KEYBOARD_MAX_STRLEN] = {"abcdefpattern"}; + + set_template(USB_TEMP_KBD); + + while (1) { + + error = sysctlbyname("hw.usb.g_keyboard.mode", NULL, NULL, + &mode, sizeof(mode)); + + if (error != 0) { + printf("WARNING: Could not set keyboard mode " + " to %d (error=%d) \n", mode, errno); + } + error = sysctlbyname("hw.usb.g_keyboard.key_press_interval", NULL, NULL, + &interval, sizeof(interval)); + + if (error != 0) { + printf("WARNING: Could not set key press interval " + "to %d (error=%d)\n", interval, errno); + } + error = sysctlbyname("hw.usb.g_keyboard.key_press_pattern", NULL, NULL, + &pattern, strlen(pattern)); + + if (error != 0) { + printf("WARNING: Could not set key pattern " + "to '%s' (error=%d)\n", pattern, errno); + } + retval = usb_ts_show_menu(level, "Default Keyboard Settings", + "1) Set silent mode %s\n" + "2) Set pattern mode %s\n" + "3) Change pattern: '%s'\n" + "4) Change key press interval: %d ms\n" + "x) Return to previous menu\n" + "s: Ready for enumeration\n", + (mode == G_KEYBOARD_MODE_SILENT) ? "(selected)" : "", + (mode == G_KEYBOARD_MODE_PATTERN) ? "(selected)" : "", + pattern, interval); + + switch (retval) { + case 0: + break; + case 1: + mode = G_KEYBOARD_MODE_SILENT; + break; + case 2: + mode = G_KEYBOARD_MODE_PATTERN; + break; + case 3: + get_string(pattern, sizeof(pattern)); + break; + case 4: + interval = get_integer(); + break; + default: + return; + } + } +} + +static void +show_device_keyboard_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Keyboard Model", + "1) Generic Keyboard \n" + "x) Return to previous menu \n"); + + switch (retval) { + case 0: + break; + case 1: + show_default_keyboard_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_default_mouse_select(uint8_t level) +{ + int error; + int retval; + int mode = 0; + int cursor_interval = 128; + int cursor_radius = 75; + int button_interval = 0; + + set_template(USB_TEMP_MOUSE); + + while (1) { + + error = sysctlbyname("hw.usb.g_mouse.mode", NULL, NULL, + &mode, sizeof(mode)); + + if (error != 0) { + printf("WARNING: Could not set mouse mode " + "to %d (error=%d)\n", mode, errno); + } + error = sysctlbyname("hw.usb.g_mouse.cursor_update_interval", NULL, NULL, + &cursor_interval, sizeof(cursor_interval)); + + if (error != 0) { + printf("WARNING: Could not set cursor update interval " + "to %d (error=%d)\n", cursor_interval, errno); + } + error = sysctlbyname("hw.usb.g_mouse.button_press_interval", NULL, NULL, + &button_interval, sizeof(button_interval)); + + if (error != 0) { + printf("WARNING: Could not set button press interval " + "to %d (error=%d)\n", button_interval, errno); + } + error = sysctlbyname("hw.usb.g_mouse.cursor_radius", NULL, NULL, + &cursor_radius, sizeof(cursor_radius)); + + if (error != 0) { + printf("WARNING: Could not set cursor radius " + "to %d (error=%d)\n", cursor_radius, errno); + } + retval = usb_ts_show_menu(level, "Default Mouse Settings", + "1) Set Silent mode %s\n" + "2) Set Circle mode %s\n" + "3) Set Square mode %s\n" + "4) Set Spiral mode %s\n" + "5) Change cursor radius: %d pixels\n" + "6) Change cursor update interval: %d ms\n" + "7) Change button[0] press interval: %d ms\n" + "x) Return to previous menu\n" + "s: Ready for enumeration\n", + (mode == G_MOUSE_MODE_SILENT) ? "(selected)" : "", + (mode == G_MOUSE_MODE_CIRCLE) ? "(selected)" : "", + (mode == G_MOUSE_MODE_BOX) ? "(selected)" : "", + (mode == G_MOUSE_MODE_SPIRAL) ? "(selected)" : "", + cursor_radius, cursor_interval, button_interval); + + switch (retval) { + case 0: + break; + case 1: + mode = G_MOUSE_MODE_SILENT; + break; + case 2: + mode = G_MOUSE_MODE_CIRCLE; + break; + case 3: + mode = G_MOUSE_MODE_BOX; + break; + case 4: + mode = G_MOUSE_MODE_SPIRAL; + break; + case 5: + cursor_radius = get_integer(); + break; + case 6: + cursor_interval = get_integer(); + break; + case 7: + button_interval = get_integer(); + break; + default: + return; + } + } +} + +static void +show_device_mouse_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Mouse Model", + "1) Generic Mouse\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_default_mouse_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_device_mtp_select(uint8_t level) +{ + set_template(USB_TEMP_MTP); +} + +static void +show_default_modem_select(uint8_t level) +{ + int error; + int retval; + int mode = 0; + int pattern_interval = 128; + int throughput = 0; + size_t len; + char pattern[G_MODEM_MAX_STRLEN] = {"abcdefpattern"}; + + set_template(USB_TEMP_MODEM); + + while (1) { + + error = sysctlbyname("hw.usb.g_modem.mode", NULL, NULL, + &mode, sizeof(mode)); + + if (error != 0) { + printf("WARNING: Could not set modem mode " + "to %d (error=%d)\n", mode, errno); + } + error = sysctlbyname("hw.usb.g_modem.pattern_interval", NULL, NULL, + &pattern_interval, sizeof(pattern_interval)); + + if (error != 0) { + printf("WARNING: Could not set pattern interval " + "to %d (error=%d)\n", pattern_interval, errno); + } + len = sizeof(throughput); + + error = sysctlbyname("hw.usb.g_modem.throughput", + &throughput, &len, 0, 0); + + if (error != 0) { + printf("WARNING: Could not get throughput " + "(error=%d)\n", errno); + } + error = sysctlbyname("hw.usb.g_modem.pattern", NULL, NULL, + &pattern, strlen(pattern)); + + if (error != 0) { + printf("WARNING: Could not set modem pattern " + "to '%s' (error=%d)\n", pattern, errno); + } + retval = usb_ts_show_menu(level, "Default Modem Settings", + "1) Set Silent mode %s\n" + "2) Set Dump mode %s\n" + "3) Set Loop mode %s\n" + "4) Set Pattern mode %s\n" + "5) Change test pattern: '%s'\n" + "6) Change data transmit interval: %d ms\n" + "x) Return to previous menu\n" + "s: Ready for enumeration\n" + "t: Throughput: %d bytes/second\n", + (mode == G_MODEM_MODE_SILENT) ? "(selected)" : "", + (mode == G_MODEM_MODE_DUMP) ? "(selected)" : "", + (mode == G_MODEM_MODE_LOOP) ? "(selected)" : "", + (mode == G_MODEM_MODE_PATTERN) ? "(selected)" : "", + pattern, pattern_interval, throughput); + + switch (retval) { + case 0: + break; + case 1: + mode = G_MODEM_MODE_SILENT; + break; + case 2: + mode = G_MODEM_MODE_DUMP; + break; + case 3: + mode = G_MODEM_MODE_LOOP; + break; + case 4: + mode = G_MODEM_MODE_PATTERN; + break; + case 5: + get_string(pattern, sizeof(pattern)); + break; + case 6: + pattern_interval = get_integer(); + break; + default: + return; + } + } +} + +static void +show_device_modem_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Modem Model", + "1) Generic Modem\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_default_modem_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_device_generic_select(uint8_t level) +{ +} + +static void +show_device_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Device Mode Test Group", + "1) Audio (UAUDIO)\n" + "2) Mass Storage (MSC)\n" + "3) Ethernet (CDCE)\n" + "4) Keyboard Input Device (UKBD)\n" + "5) Mouse Input Device (UMS)\n" + "6) Message Transfer Protocol (MTP)\n" + "7) Modem (CDC)\n" + "8) Generic Endpoint Loopback (GENERIC)\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_device_audio_select(level + 1); + break; + case 2: + show_device_msc_select(level + 1); + break; + case 3: + show_device_ethernet_select(level + 1); + break; + case 4: + show_device_keyboard_select(level + 1); + break; + case 5: + show_device_mouse_select(level + 1); + break; + case 6: + show_device_mtp_select(level + 1); + break; + case 7: + show_device_modem_select(level + 1); + break; + case 8: + show_device_generic_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_host_select(uint8_t level) +{ + int force_fs = 0; + int error; + uint32_t duration = 60; + + uint16_t dev_vid = 0; + uint16_t dev_pid = 0; + uint8_t retval; + + while (1) { + + error = sysctlbyname("hw.usb.ehci.no_hs", NULL, NULL, + &force_fs, sizeof(force_fs)); + + if (error != 0) { + printf("WARNING: Could not set non-FS mode " + "to %d (error=%d)\n", force_fs, errno); + } + retval = usb_ts_show_menu(level, "Select Host Mode Test (via LibUSB)", + " 1) Select USB device (VID=0x%04x, PID=0x%04x)\n" + " 2) Manually enter USB vendor and product ID\n" + " 3) Force FULL speed operation: <%s>\n" + " 4) Mass Storage (UMASS)\n" + " 5) Modem (UMODEM)\n" + "10) Start String Descriptor Test\n" + "11) Start Port Reset Test\n" + "12) Start Set Config Test\n" + "13) Start Get Descriptor Test\n" + "14) Start Suspend and Resume Test\n" + "15) Start Set and Clear Endpoint Stall Test\n" + "16) Start Set Alternate Interface Setting Test\n" + "17) Start Invalid Control Request Test\n" + "30) Duration: <%d> seconds\n" + "x) Return to previous menu\n", + dev_vid, dev_pid, + force_fs ? "YES" : "NO", + (int)duration); + + switch (retval) { + case 0: + break; + case 1: + show_host_device_selection(level + 1, &dev_vid, &dev_pid); + break; + case 2: + dev_vid = get_integer() & 0xFFFF; + dev_pid = get_integer() & 0xFFFF; + break; + case 3: + force_fs ^= 1; + break; + case 4: + show_host_msc_test(level + 1, dev_vid, dev_pid, duration); + break; + case 5: + show_host_modem_test(level + 1, dev_vid, dev_pid, duration); + break; + case 10: + usb_get_string_desc_test(dev_vid, dev_pid); + break; + case 11: + usb_port_reset_test(dev_vid, dev_pid, duration); + break; + case 12: + usb_set_config_test(dev_vid, dev_pid, duration); + break; + case 13: + usb_get_descriptor_test(dev_vid, dev_pid, duration); + break; + case 14: + usb_suspend_resume_test(dev_vid, dev_pid, duration); + break; + case 15: + usb_set_and_clear_stall_test(dev_vid, dev_pid); + break; + case 16: + usb_set_alt_interface_test(dev_vid, dev_pid); + break; + case 17: + usb_control_ep_error_test(dev_vid, dev_pid); + break; + case 30: + duration = get_integer(); + break; + default: + return; + } + } +} + +static void +show_mode_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Computer Mode", + "1) This computer is Running the Device Side\n" + "2) This computer is Running the Host Side\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_device_select(level + 1); + break; + case 2: + show_host_select(level + 1); + break; + default: + return; + } + } +} + +int +main(int argc, char **argv) +{ + show_mode_select(1); + + return (0); +} diff --git a/tools/tools/usbtest/usbtest.h b/tools/tools/usbtest/usbtest.h new file mode 100644 index 0000000..3d6643c --- /dev/null +++ b/tools/tools/usbtest/usbtest.h @@ -0,0 +1,62 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _USBTEST_H_ +#define _USBTEST_H_ + +#define USB_DEVICES_MAX 128 +#define USB_TS_MAX_LEVELS 8 + +struct libusb20_device; + +struct bps { + uint32_t bytes; + time_t time; +}; + +extern void usb_get_string_desc_test(uint16_t, uint16_t); +extern void usb_port_reset_test(uint16_t, uint16_t, uint32_t); +extern void usb_set_config_test(uint16_t, uint16_t, uint32_t); +extern void usb_get_descriptor_test(uint16_t, uint16_t, uint32_t); +extern void usb_control_ep_error_test(uint16_t, uint16_t); +extern void usb_set_and_clear_stall_test(uint16_t, uint16_t); +extern void usb_set_alt_interface_test(uint16_t, uint16_t); + +extern void usb_suspend_resume_test(uint16_t, uint16_t, uint32_t); +extern void do_bps(const char *, struct bps *, uint32_t len); +extern const char *indent[USB_TS_MAX_LEVELS]; +extern void show_host_msc_test(uint8_t, uint16_t, uint16_t, uint32_t); +extern void show_host_modem_test(uint8_t, uint16_t, uint16_t, uint32_t); +extern void show_host_device_selection(uint8_t, uint16_t *, uint16_t *); +extern struct libusb20_device *find_usb_device(uint16_t, uint16_t); +extern void find_usb_endpoints(struct libusb20_device *, uint8_t, uint8_t, + uint8_t, uint8_t, uint8_t *, uint8_t *, uint8_t *, uint8_t); +extern void get_string(char *, int); +extern int get_integer(void); +extern uint8_t usb_ts_show_menu(uint8_t, const char *, const char *,...); +extern int32_t usb_ts_rand_noise(void); + +#endif /* _USBTEST_H_ */ |