summaryrefslogtreecommitdiff
path: root/drivers/usb/misc/usbtest.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/misc/usbtest.c')
-rw-r--r--drivers/usb/misc/usbtest.c919
1 files changed, 727 insertions, 192 deletions
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 8b4ca1cb450a..5c92c8d8e283 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
@@ -7,31 +8,57 @@
#include <linux/moduleparam.h>
#include <linux/scatterlist.h>
#include <linux/mutex.h>
-
+#include <linux/timer.h>
#include <linux/usb.h>
+#define SIMPLE_IO_TIMEOUT 10000 /* in milliseconds */
/*-------------------------------------------------------------------------*/
static int override_alt = -1;
module_param_named(alt, override_alt, int, 0644);
MODULE_PARM_DESC(alt, ">= 0 to override altsetting selection");
+static void complicated_callback(struct urb *urb);
/*-------------------------------------------------------------------------*/
/* FIXME make these public somewhere; usbdevfs.h? */
-struct usbtest_param {
+
+/* Parameter for usbtest driver. */
+struct usbtest_param_32 {
/* inputs */
- unsigned test_num; /* 0..(TEST_CASES-1) */
- unsigned iterations;
- unsigned length;
- unsigned vary;
- unsigned sglen;
+ __u32 test_num; /* 0..(TEST_CASES-1) */
+ __u32 iterations;
+ __u32 length;
+ __u32 vary;
+ __u32 sglen;
/* outputs */
- struct timeval duration;
+ __s32 duration_sec;
+ __s32 duration_usec;
};
-#define USBTEST_REQUEST _IOWR('U', 100, struct usbtest_param)
+
+/*
+ * Compat parameter to the usbtest driver.
+ * This supports older user space binaries compiled with 64 bit compiler.
+ */
+struct usbtest_param_64 {
+ /* inputs */
+ __u32 test_num; /* 0..(TEST_CASES-1) */
+ __u32 iterations;
+ __u32 length;
+ __u32 vary;
+ __u32 sglen;
+
+ /* outputs */
+ __s64 duration_sec;
+ __s64 duration_usec;
+};
+
+/* IOCTL interface to the driver. */
+#define USBTEST_REQUEST_32 _IOWR('U', 100, struct usbtest_param_32)
+/* COMPAT IOCTL interface to the driver. */
+#define USBTEST_REQUEST_64 _IOWR('U', 100, struct usbtest_param_64)
/*-------------------------------------------------------------------------*/
@@ -53,6 +80,7 @@ struct usbtest_info {
unsigned autoconf:1;
unsigned ctrl_out:1;
unsigned iso:1; /* try iso in/out */
+ unsigned intr:1; /* try interrupt in/out */
int alt;
};
@@ -69,7 +97,10 @@ struct usbtest_dev {
int out_pipe;
int in_iso_pipe;
int out_iso_pipe;
+ int in_int_pipe;
+ int out_int_pipe;
struct usb_endpoint_descriptor *iso_in, *iso_out;
+ struct usb_endpoint_descriptor *int_in, *int_out;
struct mutex lock;
#define TBUF_SIZE 256
@@ -90,9 +121,24 @@ static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test)
dev_warn(&(tdev)->intf->dev , fmt , ## args)
#define GUARD_BYTE 0xA5
+#define MAX_SGLEN 128
/*-------------------------------------------------------------------------*/
+static inline void endpoint_update(int edi,
+ struct usb_host_endpoint **in,
+ struct usb_host_endpoint **out,
+ struct usb_host_endpoint *e)
+{
+ if (edi) {
+ if (!*in)
+ *in = e;
+ } else {
+ if (!*out)
+ *out = e;
+ }
+}
+
static int
get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
{
@@ -100,6 +146,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
struct usb_host_interface *alt;
struct usb_host_endpoint *in, *out;
struct usb_host_endpoint *iso_in, *iso_out;
+ struct usb_host_endpoint *int_in, *int_out;
struct usb_device *udev;
for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
@@ -107,6 +154,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
in = out = NULL;
iso_in = iso_out = NULL;
+ int_in = int_out = NULL;
alt = intf->altsetting + tmp;
if (override_alt >= 0 &&
@@ -118,36 +166,28 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
*/
for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
struct usb_host_endpoint *e;
+ int edi;
e = alt->endpoint + ep;
- switch (e->desc.bmAttributes) {
+ edi = usb_endpoint_dir_in(&e->desc);
+
+ switch (usb_endpoint_type(&e->desc)) {
case USB_ENDPOINT_XFER_BULK:
- break;
+ endpoint_update(edi, &in, &out, e);
+ continue;
+ case USB_ENDPOINT_XFER_INT:
+ if (dev->info->intr)
+ endpoint_update(edi, &int_in, &int_out, e);
+ continue;
case USB_ENDPOINT_XFER_ISOC:
if (dev->info->iso)
- goto try_iso;
- /* FALLTHROUGH */
+ endpoint_update(edi, &iso_in, &iso_out, e);
+ fallthrough;
default:
continue;
}
- if (usb_endpoint_dir_in(&e->desc)) {
- if (!in)
- in = e;
- } else {
- if (!out)
- out = e;
- }
- continue;
-try_iso:
- if (usb_endpoint_dir_in(&e->desc)) {
- if (!iso_in)
- iso_in = e;
- } else {
- if (!iso_out)
- iso_out = e;
- }
}
- if ((in && out) || iso_in || iso_out)
+ if ((in && out) || iso_in || iso_out || int_in || int_out)
goto found;
}
return -EINVAL;
@@ -163,12 +203,13 @@ found:
return tmp;
}
- if (in) {
+ if (in)
dev->in_pipe = usb_rcvbulkpipe(udev,
in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ if (out)
dev->out_pipe = usb_sndbulkpipe(udev,
out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
- }
+
if (iso_in) {
dev->iso_in = &iso_in->desc;
dev->in_iso_pipe = usb_rcvisocpipe(udev,
@@ -182,6 +223,20 @@ found:
iso_out->desc.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK);
}
+
+ if (int_in) {
+ dev->int_in = &int_in->desc;
+ dev->in_int_pipe = usb_rcvintpipe(udev,
+ int_in->desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
+ }
+
+ if (int_out) {
+ dev->int_out = &int_out->desc;
+ dev->out_int_pipe = usb_sndintpipe(udev,
+ int_out->desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
+ }
return 0;
}
@@ -204,14 +259,23 @@ static struct urb *usbtest_alloc_urb(
int pipe,
unsigned long bytes,
unsigned transfer_flags,
- unsigned offset)
+ unsigned offset,
+ u8 bInterval,
+ usb_complete_t complete_fn)
{
struct urb *urb;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return urb;
- usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback, NULL);
+
+ if (bInterval)
+ usb_fill_int_urb(urb, udev, pipe, NULL, bytes, complete_fn,
+ NULL, bInterval);
+ else
+ usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, complete_fn,
+ NULL);
+
urb->interval = (udev->speed == USB_SPEED_HIGH)
? (INTERRUPT_RATE << 3)
: INTERRUPT_RATE;
@@ -219,6 +283,9 @@ static struct urb *usbtest_alloc_urb(
if (usb_pipein(pipe))
urb->transfer_flags |= URB_SHORT_NOT_OK;
+ if ((bytes + offset) == 0)
+ return urb;
+
if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset,
GFP_KERNEL, &urb->transfer_dma);
@@ -250,9 +317,21 @@ static struct urb *usbtest_alloc_urb(
static struct urb *simple_alloc_urb(
struct usb_device *udev,
int pipe,
- unsigned long bytes)
+ unsigned long bytes,
+ u8 bInterval)
{
- return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0);
+ return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
+ bInterval, simple_callback);
+}
+
+static struct urb *complicated_alloc_urb(
+ struct usb_device *udev,
+ int pipe,
+ unsigned long bytes,
+ u8 bInterval)
+{
+ return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
+ bInterval, complicated_callback);
}
static unsigned pattern;
@@ -260,21 +339,39 @@ static unsigned mod_pattern;
module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(mod_pattern, "i/o pattern (0 == zeroes)");
-static inline void simple_fill_buf(struct urb *urb)
+static unsigned get_maxpacket(struct usb_device *udev, int pipe)
+{
+ struct usb_host_endpoint *ep;
+
+ ep = usb_pipe_endpoint(udev, pipe);
+ return le16_to_cpup(&ep->desc.wMaxPacketSize);
+}
+
+static int ss_isoc_get_packet_num(struct usb_device *udev, int pipe)
+{
+ struct usb_host_endpoint *ep = usb_pipe_endpoint(udev, pipe);
+
+ return USB_SS_MULT(ep->ss_ep_comp.bmAttributes)
+ * (1 + ep->ss_ep_comp.bMaxBurst);
+}
+
+static void simple_fill_buf(struct urb *urb)
{
unsigned i;
u8 *buf = urb->transfer_buffer;
unsigned len = urb->transfer_buffer_length;
+ unsigned maxpacket;
switch (pattern) {
default:
- /* FALLTHROUGH */
+ fallthrough;
case 0:
memset(buf, 0, len);
break;
case 1: /* mod63 */
+ maxpacket = get_maxpacket(urb->dev, urb->pipe);
for (i = 0; i < len; i++)
- *buf++ = (u8) (i % 63);
+ *buf++ = (u8) ((i % maxpacket) % 63);
break;
}
}
@@ -306,6 +403,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
u8 expected;
u8 *buf = urb->transfer_buffer;
unsigned len = urb->actual_length;
+ unsigned maxpacket = get_maxpacket(urb->dev, urb->pipe);
int ret = check_guard_bytes(tdev, urb);
if (ret)
@@ -323,7 +421,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
* with set_interface or set_config.
*/
case 1: /* mod63 */
- expected = i % 63;
+ expected = (i % maxpacket) % 63;
break;
/* always fail unsupported patterns */
default:
@@ -366,6 +464,7 @@ static int simple_io(
int max = urb->transfer_buffer_length;
struct completion completion;
int retval = 0;
+ unsigned long expire;
urb->context = &completion;
while (retval == 0 && iterations-- > 0) {
@@ -378,9 +477,15 @@ static int simple_io(
if (retval != 0)
break;
- /* NOTE: no timeouts; can't be broken out of by interrupt */
- wait_for_completion(&completion);
- retval = urb->status;
+ expire = msecs_to_jiffies(SIMPLE_IO_TIMEOUT);
+ if (!wait_for_completion_timeout(&completion, expire)) {
+ usb_kill_urb(urb);
+ retval = (urb->status == -ENOENT ?
+ -ETIMEDOUT : urb->status);
+ } else {
+ retval = urb->status;
+ }
+
urb->dev = udev;
if (retval == 0 && usb_pipein(urb->pipe))
retval = simple_check_buf(tdev, urb);
@@ -428,16 +533,19 @@ static void free_sglist(struct scatterlist *sg, int nents)
}
static struct scatterlist *
-alloc_sglist(int nents, int max, int vary)
+alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe)
{
struct scatterlist *sg;
+ unsigned int n_size = 0;
unsigned i;
unsigned size = max;
+ unsigned maxpacket =
+ get_maxpacket(interface_to_usbdev(dev->intf), pipe);
if (max == 0)
return NULL;
- sg = kmalloc_array(nents, sizeof *sg, GFP_KERNEL);
+ sg = kmalloc_array(nents, sizeof(*sg), GFP_KERNEL);
if (!sg)
return NULL;
sg_init_table(sg, nents);
@@ -461,7 +569,8 @@ alloc_sglist(int nents, int max, int vary)
break;
case 1:
for (j = 0; j < size; j++)
- *buf++ = (u8) (j % 63);
+ *buf++ = (u8) (((j + n_size) % maxpacket) % 63);
+ n_size += size;
break;
}
@@ -476,6 +585,18 @@ alloc_sglist(int nents, int max, int vary)
return sg;
}
+struct sg_timeout {
+ struct timer_list timer;
+ struct usb_sg_request *req;
+};
+
+static void sg_timeout(struct timer_list *t)
+{
+ struct sg_timeout *timeout = timer_container_of(timeout, t, timer);
+
+ usb_sg_cancel(timeout->req);
+}
+
static int perform_sglist(
struct usbtest_dev *tdev,
unsigned iterations,
@@ -487,6 +608,11 @@ static int perform_sglist(
{
struct usb_device *udev = testdev_to_usbdev(tdev);
int retval = 0;
+ struct sg_timeout timeout = {
+ .req = req,
+ };
+
+ timer_setup_on_stack(&timeout.timer, sg_timeout, 0);
while (retval == 0 && iterations-- > 0) {
retval = usb_sg_init(req, udev, pipe,
@@ -497,8 +623,14 @@ static int perform_sglist(
if (retval)
break;
+ mod_timer(&timeout.timer, jiffies +
+ msecs_to_jiffies(SIMPLE_IO_TIMEOUT));
usb_sg_wait(req);
- retval = req->status;
+ if (!timer_delete_sync(&timeout.timer))
+ retval = -ETIMEDOUT;
+ else
+ retval = req->status;
+ timer_destroy_on_stack(&timeout.timer);
/* FIXME check resulting data pattern */
@@ -549,7 +681,7 @@ static int get_altsetting(struct usbtest_dev *dev)
return dev->buf[0];
case 0:
retval = -ERANGE;
- /* FALLTHROUGH */
+ fallthrough;
default:
return retval;
}
@@ -573,7 +705,7 @@ static int is_good_config(struct usbtest_dev *tdev, int len)
{
struct usb_config_descriptor *config;
- if (len < sizeof *config)
+ if (len < (int)sizeof(*config))
return 0;
config = (struct usb_config_descriptor *) tdev->buf;
@@ -606,6 +738,76 @@ static int is_good_config(struct usbtest_dev *tdev, int len)
return 0;
}
+static int is_good_ext(struct usbtest_dev *tdev, u8 *buf)
+{
+ struct usb_ext_cap_descriptor *ext;
+ u32 attr;
+
+ ext = (struct usb_ext_cap_descriptor *) buf;
+
+ if (ext->bLength != USB_DT_USB_EXT_CAP_SIZE) {
+ ERROR(tdev, "bogus usb 2.0 extension descriptor length\n");
+ return 0;
+ }
+
+ attr = le32_to_cpu(ext->bmAttributes);
+ /* bits[1:15] is used and others are reserved */
+ if (attr & ~0xfffe) { /* reserved == 0 */
+ ERROR(tdev, "reserved bits set\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int is_good_ss_cap(struct usbtest_dev *tdev, u8 *buf)
+{
+ struct usb_ss_cap_descriptor *ss;
+
+ ss = (struct usb_ss_cap_descriptor *) buf;
+
+ if (ss->bLength != USB_DT_USB_SS_CAP_SIZE) {
+ ERROR(tdev, "bogus superspeed device capability descriptor length\n");
+ return 0;
+ }
+
+ /*
+ * only bit[1] of bmAttributes is used for LTM and others are
+ * reserved
+ */
+ if (ss->bmAttributes & ~0x02) { /* reserved == 0 */
+ ERROR(tdev, "reserved bits set in bmAttributes\n");
+ return 0;
+ }
+
+ /* bits[0:3] of wSpeedSupported is used and others are reserved */
+ if (le16_to_cpu(ss->wSpeedSupported) & ~0x0f) { /* reserved == 0 */
+ ERROR(tdev, "reserved bits set in wSpeedSupported\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int is_good_con_id(struct usbtest_dev *tdev, u8 *buf)
+{
+ struct usb_ss_container_id_descriptor *con_id;
+
+ con_id = (struct usb_ss_container_id_descriptor *) buf;
+
+ if (con_id->bLength != USB_DT_USB_SS_CONTN_ID_SIZE) {
+ ERROR(tdev, "bogus container id descriptor length\n");
+ return 0;
+ }
+
+ if (con_id->bReserved) { /* reserved == 0 */
+ ERROR(tdev, "reserved bits set\n");
+ return 0;
+ }
+
+ return 1;
+}
+
/* sanity test for standard requests working with usb_control_mesg() and some
* of the utility functions which use it.
*
@@ -683,12 +885,96 @@ static int ch9_postconfig(struct usbtest_dev *dev)
/* there's always [9.4.3] a device descriptor [9.6.1] */
retval = usb_get_descriptor(udev, USB_DT_DEVICE, 0,
- dev->buf, sizeof udev->descriptor);
- if (retval != sizeof udev->descriptor) {
+ dev->buf, sizeof(udev->descriptor));
+ if (retval != sizeof(udev->descriptor)) {
dev_err(&iface->dev, "dev descriptor --> %d\n", retval);
return (retval < 0) ? retval : -EDOM;
}
+ /*
+ * there's always [9.4.3] a bos device descriptor [9.6.2] in USB
+ * 3.0 spec
+ */
+ if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0210) {
+ struct usb_bos_descriptor *bos = NULL;
+ struct usb_dev_cap_header *header = NULL;
+ unsigned total, num, length;
+ u8 *buf;
+
+ retval = usb_get_descriptor(udev, USB_DT_BOS, 0, dev->buf,
+ sizeof(*udev->bos->desc));
+ if (retval != sizeof(*udev->bos->desc)) {
+ dev_err(&iface->dev, "bos descriptor --> %d\n", retval);
+ return (retval < 0) ? retval : -EDOM;
+ }
+
+ bos = (struct usb_bos_descriptor *)dev->buf;
+ total = le16_to_cpu(bos->wTotalLength);
+ num = bos->bNumDeviceCaps;
+
+ if (total > TBUF_SIZE)
+ total = TBUF_SIZE;
+
+ /*
+ * get generic device-level capability descriptors [9.6.2]
+ * in USB 3.0 spec
+ */
+ retval = usb_get_descriptor(udev, USB_DT_BOS, 0, dev->buf,
+ total);
+ if (retval != total) {
+ dev_err(&iface->dev, "bos descriptor set --> %d\n",
+ retval);
+ return (retval < 0) ? retval : -EDOM;
+ }
+
+ length = sizeof(*udev->bos->desc);
+ buf = dev->buf;
+ for (i = 0; i < num; i++) {
+ buf += length;
+ if (buf + sizeof(struct usb_dev_cap_header) >
+ dev->buf + total)
+ break;
+
+ header = (struct usb_dev_cap_header *)buf;
+ length = header->bLength;
+
+ if (header->bDescriptorType !=
+ USB_DT_DEVICE_CAPABILITY) {
+ dev_warn(&udev->dev, "not device capability descriptor, skip\n");
+ continue;
+ }
+
+ switch (header->bDevCapabilityType) {
+ case USB_CAP_TYPE_EXT:
+ if (buf + USB_DT_USB_EXT_CAP_SIZE >
+ dev->buf + total ||
+ !is_good_ext(dev, buf)) {
+ dev_err(&iface->dev, "bogus usb 2.0 extension descriptor\n");
+ return -EDOM;
+ }
+ break;
+ case USB_SS_CAP_TYPE:
+ if (buf + USB_DT_USB_SS_CAP_SIZE >
+ dev->buf + total ||
+ !is_good_ss_cap(dev, buf)) {
+ dev_err(&iface->dev, "bogus superspeed device capability descriptor\n");
+ return -EDOM;
+ }
+ break;
+ case CONTAINER_ID_TYPE:
+ if (buf + USB_DT_USB_SS_CONTN_ID_SIZE >
+ dev->buf + total ||
+ !is_good_con_id(dev, buf)) {
+ dev_err(&iface->dev, "bogus container id descriptor\n");
+ return -EDOM;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
/* there's always [9.4.3] at least one config descriptor [9.6.3] */
for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
retval = usb_get_descriptor(udev, USB_DT_CONFIG, i,
@@ -718,7 +1004,7 @@ static int ch9_postconfig(struct usbtest_dev *dev)
dev_err(&iface->dev,
"hs dev qualifier --> %d\n",
retval);
- return (retval < 0) ? retval : -EDOM;
+ return retval;
}
/* usb2.0 but not high-speed capable; fine */
} else if (retval != sizeof(struct usb_qualifier_descriptor)) {
@@ -746,21 +1032,21 @@ static int ch9_postconfig(struct usbtest_dev *dev)
/* FIXME fetch strings from at least the device descriptor */
/* [9.4.5] get_status always works */
- retval = usb_get_status(udev, USB_RECIP_DEVICE, 0, dev->buf);
- if (retval != 2) {
+ retval = usb_get_std_status(udev, USB_RECIP_DEVICE, 0, dev->buf);
+ if (retval) {
dev_err(&iface->dev, "get dev status --> %d\n", retval);
- return (retval < 0) ? retval : -EDOM;
+ return retval;
}
/* FIXME configuration.bmAttributes says if we could try to set/clear
* the device's remote wakeup feature ... if we can, test that here
*/
- retval = usb_get_status(udev, USB_RECIP_INTERFACE,
+ retval = usb_get_std_status(udev, USB_RECIP_INTERFACE,
iface->altsetting[0].desc.bInterfaceNumber, dev->buf);
- if (retval != 2) {
+ if (retval) {
dev_err(&iface->dev, "get interface status --> %d\n", retval);
- return (retval < 0) ? retval : -EDOM;
+ return retval;
}
/* FIXME get status for each endpoint in the interface */
@@ -786,11 +1072,11 @@ struct ctrl_ctx {
unsigned pending;
int status;
struct urb **urb;
- struct usbtest_param *param;
+ struct usbtest_param_32 *param;
int last;
};
-#define NUM_SUBCASES 15 /* how many test subcases here? */
+#define NUM_SUBCASES 16 /* how many test subcases here? */
struct subcase {
struct usb_ctrlrequest setup;
@@ -804,11 +1090,12 @@ static void ctrl_complete(struct urb *urb)
struct usb_ctrlrequest *reqp;
struct subcase *subcase;
int status = urb->status;
+ unsigned long flags;
reqp = (struct usb_ctrlrequest *)urb->setup_packet;
subcase = container_of(reqp, struct subcase, setup);
- spin_lock(&ctx->lock);
+ spin_lock_irqsave(&ctx->lock, flags);
ctx->count--;
ctx->pending--;
@@ -907,11 +1194,11 @@ error:
/* signal completion when nothing's queued */
if (ctx->pending == 0)
complete(&ctx->complete);
- spin_unlock(&ctx->lock);
+ spin_unlock_irqrestore(&ctx->lock, flags);
}
static int
-test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
+test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param)
{
struct usb_device *udev = testdev_to_usbdev(dev);
struct urb **urb;
@@ -954,7 +1241,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
* device, but some are chosen to trigger protocol stalls
* or short reads.
*/
- memset(&req, 0, sizeof req);
+ memset(&req, 0, sizeof(req));
req.bRequest = USB_REQ_GET_DESCRIPTOR;
req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE;
@@ -1064,17 +1351,26 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
}
expected = -EREMOTEIO;
break;
+ case 15:
+ req.wValue = cpu_to_le16(USB_DT_BOS << 8);
+ if (udev->bos)
+ len = le16_to_cpu(udev->bos->desc->wTotalLength);
+ else
+ len = sizeof(struct usb_bos_descriptor);
+ if (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0201)
+ expected = -EPIPE;
+ break;
default:
ERROR(dev, "bogus number of ctrl queue testcases!\n");
context.status = -EINVAL;
goto cleanup;
}
req.wLength = cpu_to_le16(len);
- urb[i] = u = simple_alloc_urb(udev, pipe, len);
+ urb[i] = u = simple_alloc_urb(udev, pipe, len, 0);
if (!u)
goto cleanup;
- reqp = kmalloc(sizeof *reqp, GFP_KERNEL);
+ reqp = kmalloc(sizeof(*reqp), GFP_KERNEL);
if (!reqp)
goto cleanup;
reqp->setup = req;
@@ -1143,12 +1439,17 @@ static int unlink1(struct usbtest_dev *dev, int pipe, int size, int async)
int retval = 0;
init_completion(&completion);
- urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size);
+ urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size, 0);
if (!urb)
return -ENOMEM;
urb->context = &completion;
urb->complete = unlink1_callback;
+ if (usb_pipeout(urb->pipe)) {
+ simple_fill_buf(urb);
+ urb->transfer_flags |= URB_ZERO_PACKET;
+ }
+
/* keep the endpoint busy. there are lots of hc/hcd-internal
* states, and testing should get to all of them over time.
*
@@ -1169,6 +1470,9 @@ static int unlink1(struct usbtest_dev *dev, int pipe, int size, int async)
while (!completion_done(&completion)) {
retval = usb_unlink_urb(urb);
+ if (retval == 0 && usb_pipein(urb->pipe))
+ retval = simple_check_buf(dev, urb);
+
switch (retval) {
case -EBUSY:
case -EIDRM:
@@ -1279,6 +1583,11 @@ static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num,
unlink_queued_callback, &ctx);
ctx.urbs[i]->transfer_dma = buf_dma;
ctx.urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+
+ if (usb_pipeout(ctx.urbs[i]->pipe)) {
+ simple_fill_buf(ctx.urbs[i]);
+ ctx.urbs[i]->transfer_flags |= URB_ZERO_PACKET;
+ }
}
/* Submit all the URBs and then unlink URBs num - 4 and num - 2. */
@@ -1323,7 +1632,7 @@ static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
u16 status;
/* shouldn't look or act halted */
- retval = usb_get_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
+ retval = usb_get_std_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
if (retval < 0) {
ERROR(tdev, "ep %02x couldn't get no-halt status, %d\n",
ep, retval);
@@ -1345,13 +1654,12 @@ static int verify_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
u16 status;
/* should look and act halted */
- retval = usb_get_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
+ retval = usb_get_std_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
if (retval < 0) {
ERROR(tdev, "ep %02x couldn't get halt status, %d\n",
ep, retval);
return retval;
}
- le16_to_cpus(&status);
if (status != 1) {
ERROR(tdev, "ep %02x bogus status: %04x != 1\n", ep, status);
return -EINVAL;
@@ -1384,8 +1692,17 @@ static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb)
return retval;
}
retval = verify_halted(tdev, ep, urb);
- if (retval < 0)
+ if (retval < 0) {
+ int ret;
+
+ /* clear halt anyways, else further tests will fail */
+ ret = usb_clear_halt(urb->dev, urb->pipe);
+ if (ret)
+ ERROR(tdev, "ep %02x couldn't clear halt, %d\n",
+ ep, ret);
+
return retval;
+ }
/* clear halt (tests API + protocol), verify it worked */
retval = usb_clear_halt(urb->dev, urb->pipe);
@@ -1402,6 +1719,35 @@ static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb)
return 0;
}
+static int test_toggle_sync(struct usbtest_dev *tdev, int ep, struct urb *urb)
+{
+ int retval;
+
+ /* clear initial data toggle to DATA0 */
+ retval = usb_clear_halt(urb->dev, urb->pipe);
+ if (retval < 0) {
+ ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval);
+ return retval;
+ }
+
+ /* transfer 3 data packets, should be DATA0, DATA1, DATA0 */
+ retval = simple_io(tdev, urb, 1, 0, 0, __func__);
+ if (retval != 0)
+ return -EINVAL;
+
+ /* clear halt resets device side data toggle, host should react to it */
+ retval = usb_clear_halt(urb->dev, urb->pipe);
+ if (retval < 0) {
+ ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval);
+ return retval;
+ }
+
+ /* host should use DATA0 again after clear halt */
+ retval = simple_io(tdev, urb, 1, 0, 0, __func__);
+
+ return retval;
+}
+
static int halt_simple(struct usbtest_dev *dev)
{
int ep;
@@ -1410,9 +1756,9 @@ static int halt_simple(struct usbtest_dev *dev)
struct usb_device *udev = testdev_to_usbdev(dev);
if (udev->speed == USB_SPEED_SUPER)
- urb = simple_alloc_urb(udev, 0, 1024);
+ urb = simple_alloc_urb(udev, 0, 1024, 0);
else
- urb = simple_alloc_urb(udev, 0, 512);
+ urb = simple_alloc_urb(udev, 0, 512, 0);
if (urb == NULL)
return -ENOMEM;
@@ -1434,6 +1780,33 @@ done:
return retval;
}
+static int toggle_sync_simple(struct usbtest_dev *dev)
+{
+ int ep;
+ int retval = 0;
+ struct urb *urb;
+ struct usb_device *udev = testdev_to_usbdev(dev);
+ unsigned maxp = get_maxpacket(udev, dev->out_pipe);
+
+ /*
+ * Create a URB that causes a transfer of uneven amount of data packets
+ * This way the clear toggle has an impact on the data toggle sequence.
+ * Use 2 maxpacket length packets and one zero packet.
+ */
+ urb = simple_alloc_urb(udev, 0, 2 * maxp, 0);
+ if (urb == NULL)
+ return -ENOMEM;
+
+ urb->transfer_flags |= URB_ZERO_PACKET;
+
+ ep = usb_pipeendpoint(dev->out_pipe);
+ urb->pipe = dev->out_pipe;
+ retval = test_toggle_sync(dev, ep, urb);
+
+ simple_free_urb(urb);
+ return retval;
+}
+
/*-------------------------------------------------------------------------*/
/* Control OUT tests use the vendor control requests from Intel's
@@ -1471,7 +1844,7 @@ static int ctrl_out(struct usbtest_dev *dev,
for (i = 0; i < count; i++) {
/* write patterned data */
for (j = 0; j < len; j++)
- buf[j] = i + j;
+ buf[j] = (u8)(i + j);
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x5b, USB_DIR_OUT|USB_TYPE_VENDOR,
0, 0, buf, len, USB_CTRL_SET_TIMEOUT);
@@ -1501,9 +1874,9 @@ static int ctrl_out(struct usbtest_dev *dev,
/* fail if we can't verify */
for (j = 0; j < len; j++) {
- if (buf[j] != (u8) (i + j)) {
+ if (buf[j] != (u8)(i + j)) {
ERROR(dev, "ctrl_out, byte %d is %d not %d\n",
- j, buf[j], (u8) i + j);
+ j, buf[j], (u8)(i + j));
retval = -EBADMSG;
break;
}
@@ -1533,12 +1906,12 @@ static int ctrl_out(struct usbtest_dev *dev,
/*-------------------------------------------------------------------------*/
-/* ISO tests ... mimics common usage
+/* ISO/BULK tests ... mimics common usage
* - buffer length is split into N packets (mostly maxpacket sized)
* - multi-buffers according to sglen
*/
-struct iso_context {
+struct transfer_context {
unsigned count;
unsigned pending;
spinlock_t lock;
@@ -1547,20 +1920,22 @@ struct iso_context {
unsigned long errors;
unsigned long packet_count;
struct usbtest_dev *dev;
+ bool is_iso;
};
-static void iso_callback(struct urb *urb)
+static void complicated_callback(struct urb *urb)
{
- struct iso_context *ctx = urb->context;
+ struct transfer_context *ctx = urb->context;
+ unsigned long flags;
- spin_lock(&ctx->lock);
+ spin_lock_irqsave(&ctx->lock, flags);
ctx->count--;
ctx->packet_count += urb->number_of_packets;
if (urb->error_count > 0)
ctx->errors += urb->error_count;
else if (urb->status != 0)
- ctx->errors += urb->number_of_packets;
+ ctx->errors += (ctx->is_iso ? urb->number_of_packets : 1);
else if (urb->actual_length != urb->transfer_buffer_length)
ctx->errors++;
else if (check_guard_bytes(ctx->dev, urb) != 0)
@@ -1574,9 +1949,9 @@ static void iso_callback(struct urb *urb)
goto done;
default:
dev_err(&ctx->dev->intf->dev,
- "iso resubmit err %d\n",
+ "resubmit err %d\n",
status);
- /* FALLTHROUGH */
+ fallthrough;
case -ENODEV: /* disconnected */
case -ESHUTDOWN: /* endpoint disabled */
ctx->submit_error = 1;
@@ -1588,12 +1963,12 @@ static void iso_callback(struct urb *urb)
if (ctx->pending == 0) {
if (ctx->errors)
dev_err(&ctx->dev->intf->dev,
- "iso test, %lu errors out of %lu\n",
+ "during the test, %lu errors out of %lu\n",
ctx->errors, ctx->packet_count);
complete(&ctx->done);
}
done:
- spin_unlock(&ctx->lock);
+ spin_unlock_irqrestore(&ctx->lock, flags);
}
static struct urb *iso_alloc_urb(
@@ -1609,8 +1984,13 @@ static struct urb *iso_alloc_urb(
if (bytes < 0 || !desc)
return NULL;
- maxp = 0x7ff & usb_endpoint_maxp(desc);
- maxp *= 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11));
+
+ maxp = usb_endpoint_maxp(desc);
+ if (udev->speed >= USB_SPEED_SUPER)
+ maxp *= ss_isoc_get_packet_num(udev, pipe);
+ else
+ maxp *= usb_endpoint_maxp_mult(desc);
+
packets = DIV_ROUND_UP(bytes, maxp);
urb = usb_alloc_urb(packets, GFP_KERNEL);
@@ -1641,13 +2021,14 @@ static struct urb *iso_alloc_urb(
for (i = 0; i < packets; i++) {
/* here, only the last packet will be short */
- urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp);
+ urb->iso_frame_desc[i].length = min_t(unsigned int,
+ bytes, maxp);
bytes -= urb->iso_frame_desc[i].length;
urb->iso_frame_desc[i].offset = maxp * i;
}
- urb->complete = iso_callback;
+ urb->complete = complicated_callback;
/* urb->context = SET BY CALLER */
urb->interval = 1 << (desc->bInterval - 1);
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
@@ -1655,36 +2036,43 @@ static struct urb *iso_alloc_urb(
}
static int
-test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
+test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param,
int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)
{
- struct iso_context context;
+ struct transfer_context context;
struct usb_device *udev;
unsigned i;
unsigned long packets = 0;
int status = 0;
- struct urb *urbs[10]; /* FIXME no limit */
+ struct urb **urbs;
+
+ if (!param->sglen || param->iterations > UINT_MAX / param->sglen)
+ return -EINVAL;
- if (param->sglen > 10)
- return -EDOM;
+ if (param->sglen > MAX_SGLEN)
+ return -EINVAL;
- memset(&context, 0, sizeof context);
+ urbs = kcalloc(param->sglen, sizeof(*urbs), GFP_KERNEL);
+ if (!urbs)
+ return -ENOMEM;
+
+ memset(&context, 0, sizeof(context));
context.count = param->iterations * param->sglen;
context.dev = dev;
+ context.is_iso = !!desc;
init_completion(&context.done);
spin_lock_init(&context.lock);
- memset(urbs, 0, sizeof urbs);
udev = testdev_to_usbdev(dev);
- dev_info(&dev->intf->dev,
- "... iso period %d %sframes, wMaxPacket %04x\n",
- 1 << (desc->bInterval - 1),
- (udev->speed == USB_SPEED_HIGH) ? "micro" : "",
- usb_endpoint_maxp(desc));
for (i = 0; i < param->sglen; i++) {
- urbs[i] = iso_alloc_urb(udev, pipe, desc,
+ if (context.is_iso)
+ urbs[i] = iso_alloc_urb(udev, pipe, desc,
param->length, offset);
+ else
+ urbs[i] = complicated_alloc_urb(udev, pipe,
+ param->length, 0);
+
if (!urbs[i]) {
status = -ENOMEM;
goto fail;
@@ -1693,11 +2081,28 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
urbs[i]->context = &context;
}
packets *= param->iterations;
- dev_info(&dev->intf->dev,
- "... total %lu msec (%lu packets)\n",
- (packets * (1 << (desc->bInterval - 1)))
- / ((udev->speed == USB_SPEED_HIGH) ? 8 : 1),
- packets);
+
+ if (context.is_iso) {
+ int transaction_num;
+
+ if (udev->speed >= USB_SPEED_SUPER)
+ transaction_num = ss_isoc_get_packet_num(udev, pipe);
+ else
+ transaction_num = usb_endpoint_maxp_mult(desc);
+
+ dev_info(&dev->intf->dev,
+ "iso period %d %sframes, wMaxPacket %d, transactions: %d\n",
+ 1 << (desc->bInterval - 1),
+ (udev->speed >= USB_SPEED_HIGH) ? "micro" : "",
+ usb_endpoint_maxp(desc),
+ transaction_num);
+
+ dev_info(&dev->intf->dev,
+ "total %lu msec (%lu packets)\n",
+ (packets * (1 << (desc->bInterval - 1)))
+ / ((udev->speed >= USB_SPEED_HIGH) ? 8 : 1),
+ packets);
+ }
spin_lock_irq(&context.lock);
for (i = 0; i < param->sglen; i++) {
@@ -1734,8 +2139,11 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
;
else if (context.submit_error)
status = -EACCES;
- else if (context.errors > context.packet_count / 10)
+ else if (context.errors >
+ (context.is_iso ? context.packet_count / 10 : 0))
status = -EIO;
+
+ kfree(urbs);
return status;
fail:
@@ -1743,6 +2151,8 @@ fail:
if (urbs[i])
simple_free_urb(urbs[i]);
}
+
+ kfree(urbs);
return status;
}
@@ -1755,8 +2165,8 @@ static int test_unaligned_bulk(
const char *label)
{
int retval;
- struct urb *urb = usbtest_alloc_urb(
- testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1);
+ struct urb *urb = usbtest_alloc_urb(testdev_to_usbdev(tdev),
+ pipe, length, transfer_flags, 1, 0, simple_callback);
if (!urb)
return -ENOMEM;
@@ -1766,78 +2176,22 @@ static int test_unaligned_bulk(
return retval;
}
-/*-------------------------------------------------------------------------*/
-
-/* We only have this one interface to user space, through usbfs.
- * User mode code can scan usbfs to find N different devices (maybe on
- * different busses) to use when testing, and allocate one thread per
- * test. So discovery is simplified, and we have no device naming issues.
- *
- * Don't use these only as stress/load tests. Use them along with with
- * other USB bus activity: plugging, unplugging, mousing, mp3 playback,
- * video capture, and so on. Run different tests at different times, in
- * different sequences. Nothing here should interact with other devices,
- * except indirectly by consuming USB bandwidth and CPU resources for test
- * threads and request completion. But the only way to know that for sure
- * is to test when HC queues are in use by many devices.
- *
- * WARNING: Because usbfs grabs udev->dev.sem before calling this ioctl(),
- * it locks out usbcore in certain code paths. Notably, if you disconnect
- * the device-under-test, khubd will wait block forever waiting for the
- * ioctl to complete ... so that usb_disconnect() can abort the pending
- * urbs and then call usbtest_disconnect(). To abort a test, you're best
- * off just killing the userspace task and waiting for it to exit.
- */
-
+/* Run tests. */
static int
-usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
+usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param)
{
struct usbtest_dev *dev = usb_get_intfdata(intf);
struct usb_device *udev = testdev_to_usbdev(dev);
- struct usbtest_param *param = buf;
- int retval = -EOPNOTSUPP;
struct urb *urb;
struct scatterlist *sg;
struct usb_sg_request req;
- struct timeval start;
unsigned i;
-
- /* FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. */
-
- pattern = mod_pattern;
-
- if (code != USBTEST_REQUEST)
- return -EOPNOTSUPP;
+ int retval = -EOPNOTSUPP;
if (param->iterations <= 0)
return -EINVAL;
-
- if (mutex_lock_interruptible(&dev->lock))
- return -ERESTARTSYS;
-
- /* FIXME: What if a system sleep starts while a test is running? */
-
- /* some devices, like ez-usb default devices, need a non-default
- * altsetting to have any active endpoints. some tests change
- * altsettings; force a default so most tests don't need to check.
- */
- if (dev->info->alt >= 0) {
- int res;
-
- if (intf->altsetting->desc.bInterfaceNumber) {
- mutex_unlock(&dev->lock);
- return -ENODEV;
- }
- res = set_altsetting(dev, dev->info->alt);
- if (res) {
- dev_err(&intf->dev,
- "set altsetting to %d failed, %d\n",
- dev->info->alt, res);
- mutex_unlock(&dev->lock);
- return res;
- }
- }
-
+ if (param->sglen > MAX_SGLEN)
+ return -EINVAL;
/*
* Just a bunch of test cases that every HCD is expected to handle.
*
@@ -1847,7 +2201,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
* FIXME add more tests! cancel requests, verify the data, control
* queueing, concurrent read+write threads, and so on.
*/
- do_gettimeofday(&start);
switch (param->test_num) {
case 0:
@@ -1862,7 +2215,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
dev_info(&intf->dev,
"TEST 1: write %d bytes %u times\n",
param->length, param->iterations);
- urb = simple_alloc_urb(udev, dev->out_pipe, param->length);
+ urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0);
if (!urb) {
retval = -ENOMEM;
break;
@@ -1877,7 +2230,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
dev_info(&intf->dev,
"TEST 2: read %d bytes %u times\n",
param->length, param->iterations);
- urb = simple_alloc_urb(udev, dev->in_pipe, param->length);
+ urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0);
if (!urb) {
retval = -ENOMEM;
break;
@@ -1892,7 +2245,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
dev_info(&intf->dev,
"TEST 3: write/%d 0..%d bytes %u times\n",
param->vary, param->length, param->iterations);
- urb = simple_alloc_urb(udev, dev->out_pipe, param->length);
+ urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0);
if (!urb) {
retval = -ENOMEM;
break;
@@ -1908,7 +2261,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
dev_info(&intf->dev,
"TEST 4: read/%d 0..%d bytes %u times\n",
param->vary, param->length, param->iterations);
- urb = simple_alloc_urb(udev, dev->in_pipe, param->length);
+ urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0);
if (!urb) {
retval = -ENOMEM;
break;
@@ -1927,7 +2280,8 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
"TEST 5: write %d sglists %d entries of %d bytes\n",
param->iterations,
param->sglen, param->length);
- sg = alloc_sglist(param->sglen, param->length, 0);
+ sg = alloc_sglist(param->sglen, param->length,
+ 0, dev, dev->out_pipe);
if (!sg) {
retval = -ENOMEM;
break;
@@ -1945,7 +2299,8 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
"TEST 6: read %d sglists %d entries of %d bytes\n",
param->iterations,
param->sglen, param->length);
- sg = alloc_sglist(param->sglen, param->length, 0);
+ sg = alloc_sglist(param->sglen, param->length,
+ 0, dev, dev->in_pipe);
if (!sg) {
retval = -ENOMEM;
break;
@@ -1962,7 +2317,8 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
"TEST 7: write/%d %d sglists %d entries 0..%d bytes\n",
param->vary, param->iterations,
param->sglen, param->length);
- sg = alloc_sglist(param->sglen, param->length, param->vary);
+ sg = alloc_sglist(param->sglen, param->length,
+ param->vary, dev, dev->out_pipe);
if (!sg) {
retval = -ENOMEM;
break;
@@ -1979,7 +2335,8 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
"TEST 8: read/%d %d sglists %d entries 0..%d bytes\n",
param->vary, param->iterations,
param->sglen, param->length);
- sg = alloc_sglist(param->sglen, param->length, param->vary);
+ sg = alloc_sglist(param->sglen, param->length,
+ param->vary, dev, dev->in_pipe);
if (!sg) {
retval = -ENOMEM;
break;
@@ -2076,7 +2433,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
param->iterations,
param->sglen, param->length);
/* FIRMWARE: iso sink */
- retval = test_iso_queue(dev, param,
+ retval = test_queue(dev, param,
dev->out_iso_pipe, dev->iso_out, 0);
break;
@@ -2089,7 +2446,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
param->iterations,
param->sglen, param->length);
/* FIRMWARE: iso source */
- retval = test_iso_queue(dev, param,
+ retval = test_queue(dev, param,
dev->in_iso_pipe, dev->iso_in, 0);
break;
@@ -2170,7 +2527,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
"TEST 22: write %d iso odd, %d entries of %d bytes\n",
param->iterations,
param->sglen, param->length);
- retval = test_iso_queue(dev, param,
+ retval = test_queue(dev, param,
dev->out_iso_pipe, dev->iso_out, 1);
break;
@@ -2181,7 +2538,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
"TEST 23: read %d iso odd, %d entries of %d bytes\n",
param->iterations,
param->sglen, param->length);
- retval = test_iso_queue(dev, param,
+ retval = test_queue(dev, param,
dev->in_iso_pipe, dev->iso_in, 1);
break;
@@ -2205,14 +2562,183 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
}
break;
+ /* Simple non-queued interrupt I/O tests */
+ case 25:
+ if (dev->out_int_pipe == 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 25: write %d bytes %u times\n",
+ param->length, param->iterations);
+ urb = simple_alloc_urb(udev, dev->out_int_pipe, param->length,
+ dev->int_out->bInterval);
+ if (!urb) {
+ retval = -ENOMEM;
+ break;
+ }
+ /* FIRMWARE: interrupt sink (maybe accepts short writes) */
+ retval = simple_io(dev, urb, param->iterations, 0, 0, "test25");
+ simple_free_urb(urb);
+ break;
+ case 26:
+ if (dev->in_int_pipe == 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 26: read %d bytes %u times\n",
+ param->length, param->iterations);
+ urb = simple_alloc_urb(udev, dev->in_int_pipe, param->length,
+ dev->int_in->bInterval);
+ if (!urb) {
+ retval = -ENOMEM;
+ break;
+ }
+ /* FIRMWARE: interrupt source (maybe generates short writes) */
+ retval = simple_io(dev, urb, param->iterations, 0, 0, "test26");
+ simple_free_urb(urb);
+ break;
+ case 27:
+ /* We do performance test, so ignore data compare */
+ if (dev->out_pipe == 0 || param->sglen == 0 || pattern != 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 27: bulk write %dMbytes\n", (param->iterations *
+ param->sglen * param->length) / (1024 * 1024));
+ retval = test_queue(dev, param,
+ dev->out_pipe, NULL, 0);
+ break;
+ case 28:
+ if (dev->in_pipe == 0 || param->sglen == 0 || pattern != 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 28: bulk read %dMbytes\n", (param->iterations *
+ param->sglen * param->length) / (1024 * 1024));
+ retval = test_queue(dev, param,
+ dev->in_pipe, NULL, 0);
+ break;
+ /* Test data Toggle/seq_nr clear between bulk out transfers */
+ case 29:
+ if (dev->out_pipe == 0)
+ break;
+ retval = 0;
+ dev_info(&intf->dev, "TEST 29: Clear toggle between bulk writes %d times\n",
+ param->iterations);
+ for (i = param->iterations; retval == 0 && i > 0; --i)
+ retval = toggle_sync_simple(dev);
+
+ if (retval)
+ ERROR(dev, "toggle sync failed, iterations left %d\n",
+ i);
+ break;
}
- do_gettimeofday(&param->duration);
- param->duration.tv_sec -= start.tv_sec;
- param->duration.tv_usec -= start.tv_usec;
- if (param->duration.tv_usec < 0) {
- param->duration.tv_usec += 1000 * 1000;
- param->duration.tv_sec -= 1;
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We only have this one interface to user space, through usbfs.
+ * User mode code can scan usbfs to find N different devices (maybe on
+ * different busses) to use when testing, and allocate one thread per
+ * test. So discovery is simplified, and we have no device naming issues.
+ *
+ * Don't use these only as stress/load tests. Use them along with
+ * other USB bus activity: plugging, unplugging, mousing, mp3 playback,
+ * video capture, and so on. Run different tests at different times, in
+ * different sequences. Nothing here should interact with other devices,
+ * except indirectly by consuming USB bandwidth and CPU resources for test
+ * threads and request completion. But the only way to know that for sure
+ * is to test when HC queues are in use by many devices.
+ *
+ * WARNING: Because usbfs grabs udev->dev.sem before calling this ioctl(),
+ * it locks out usbcore in certain code paths. Notably, if you disconnect
+ * the device-under-test, hub_wq will wait block forever waiting for the
+ * ioctl to complete ... so that usb_disconnect() can abort the pending
+ * urbs and then call usbtest_disconnect(). To abort a test, you're best
+ * off just killing the userspace task and waiting for it to exit.
+ */
+
+static int
+usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
+{
+
+ struct usbtest_dev *dev = usb_get_intfdata(intf);
+ struct usbtest_param_64 *param_64 = buf;
+ struct usbtest_param_32 temp;
+ struct usbtest_param_32 *param_32 = buf;
+ struct timespec64 start;
+ struct timespec64 end;
+ struct timespec64 duration;
+ int retval = -EOPNOTSUPP;
+
+ /* FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. */
+
+ pattern = mod_pattern;
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+
+ /* FIXME: What if a system sleep starts while a test is running? */
+
+ /* some devices, like ez-usb default devices, need a non-default
+ * altsetting to have any active endpoints. some tests change
+ * altsettings; force a default so most tests don't need to check.
+ */
+ if (dev->info->alt >= 0) {
+ if (intf->altsetting->desc.bInterfaceNumber) {
+ retval = -ENODEV;
+ goto free_mutex;
+ }
+ retval = set_altsetting(dev, dev->info->alt);
+ if (retval) {
+ dev_err(&intf->dev,
+ "set altsetting to %d failed, %d\n",
+ dev->info->alt, retval);
+ goto free_mutex;
+ }
}
+
+ switch (code) {
+ case USBTEST_REQUEST_64:
+ temp.test_num = param_64->test_num;
+ temp.iterations = param_64->iterations;
+ temp.length = param_64->length;
+ temp.sglen = param_64->sglen;
+ temp.vary = param_64->vary;
+ param_32 = &temp;
+ break;
+
+ case USBTEST_REQUEST_32:
+ break;
+
+ default:
+ retval = -EOPNOTSUPP;
+ goto free_mutex;
+ }
+
+ ktime_get_ts64(&start);
+
+ retval = usbtest_do_ioctl(intf, param_32);
+ if (retval < 0)
+ goto free_mutex;
+
+ ktime_get_ts64(&end);
+
+ duration = timespec64_sub(end, start);
+
+ temp.duration_sec = duration.tv_sec;
+ temp.duration_usec = duration.tv_nsec/NSEC_PER_USEC;
+
+ switch (code) {
+ case USBTEST_REQUEST_32:
+ param_32->duration_sec = temp.duration_sec;
+ param_32->duration_usec = temp.duration_usec;
+ break;
+
+ case USBTEST_REQUEST_64:
+ param_64->duration_sec = temp.duration_sec;
+ param_64->duration_usec = temp.duration_usec;
+ break;
+ }
+
+free_mutex:
mutex_unlock(&dev->lock);
return retval;
}
@@ -2241,6 +2767,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
struct usbtest_info *info;
char *rtest, *wtest;
char *irtest, *iwtest;
+ char *intrtest, *intwtest;
udev = interface_to_usbdev(intf);
@@ -2281,6 +2808,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
*/
rtest = wtest = "";
irtest = iwtest = "";
+ intrtest = intwtest = "";
if (force_interrupt || udev->speed == USB_SPEED_LOW) {
if (info->ep_in) {
dev->in_pipe = usb_rcvintpipe(udev, info->ep_in);
@@ -2319,15 +2847,20 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
irtest = " iso-in";
if (dev->out_iso_pipe)
iwtest = " iso-out";
+ if (dev->in_int_pipe)
+ intrtest = " int-in";
+ if (dev->out_int_pipe)
+ intwtest = " int-out";
}
usb_set_intfdata(intf, dev);
dev_info(&intf->dev, "%s\n", info->name);
- dev_info(&intf->dev, "%s {control%s%s%s%s%s} tests%s\n",
+ dev_info(&intf->dev, "%s {control%s%s%s%s%s%s%s} tests%s\n",
usb_speed_string(udev->speed),
info->ctrl_out ? " in/out" : "",
rtest, wtest,
irtest, iwtest,
+ intrtest, intwtest,
info->alt >= 0 ? " (+alt)" : "");
return 0;
}
@@ -2349,6 +2882,7 @@ static void usbtest_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
dev_dbg(&intf->dev, "disconnect\n");
+ kfree(dev->buf);
kfree(dev);
}
@@ -2401,6 +2935,7 @@ static struct usbtest_info gz_info = {
.autoconf = 1,
.ctrl_out = 1,
.iso = 1,
+ .intr = 1,
.alt = 0,
};