From owner-svn-src-head@FreeBSD.ORG Fri Aug 9 20:08:44 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id DCBE24B5; Fri, 9 Aug 2013 20:08:43 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id B9EC02F2C; Fri, 9 Aug 2013 20:08:43 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r79K8haj002705; Fri, 9 Aug 2013 20:08:43 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r79K8hki002701; Fri, 9 Aug 2013 20:08:43 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201308092008.r79K8hki002701@svn.freebsd.org> From: Hans Petter Selasky Date: Fri, 9 Aug 2013 20:08:43 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r254159 - head/tools/tools/usbtest X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 09 Aug 2013 20:08:44 -0000 Author: hselasky Date: Fri Aug 9 20:08:42 2013 New Revision: 254159 URL: http://svnweb.freebsd.org/changeset/base/254159 Log: Initial commit of my USB test code which can exercise connected USB devices and the FreeBSD USB stack itself. This program can be used to test compliance against well established usb.org standards, also called chapter-9 tests. The host platform can act as either USB device or USB host depending on the available hardware. The basic USB communication happens through FreeBSD's own libusb v2, and some sysctls are also used to invoke specific error conditions. This test program can be used to verify correct operation of external USB harddisks under heavy load and various other conditions. The software is driven via a simple command line interface. Main supported USB host classes are "USB mass storage" and "USB modems". Added: head/tools/tools/usbtest/ head/tools/tools/usbtest/Makefile (contents, props changed) head/tools/tools/usbtest/usb_control_ep_test.c (contents, props changed) head/tools/tools/usbtest/usb_modem_test.c (contents, props changed) head/tools/tools/usbtest/usb_msc_test.c (contents, props changed) head/tools/tools/usbtest/usb_msc_test.h (contents, props changed) head/tools/tools/usbtest/usbtest.c (contents, props changed) head/tools/tools/usbtest/usbtest.h (contents, props changed) Added: head/tools/tools/usbtest/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tools/tools/usbtest/Makefile Fri Aug 9 20:08:42 2013 (r254159) @@ -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 Added: head/tools/tools/usbtest/usb_control_ep_test.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tools/tools/usbtest/usb_control_ep_test.c Fri Aug 9 20:08:42 2013 (r254159) @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#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); +} Added: head/tools/tools/usbtest/usb_modem_test.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tools/tools/usbtest/usb_modem_test.c Fri Aug 9 20:08:42 2013 (r254159) @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#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; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***