|
@@ -22,18 +22,42 @@ static void complicated_callback(struct urb *urb);
|
|
/*-------------------------------------------------------------------------*/
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
/* FIXME make these public somewhere; usbdevfs.h? */
|
|
/* FIXME make these public somewhere; usbdevfs.h? */
|
|
-struct usbtest_param {
|
|
|
|
|
|
+
|
|
|
|
+/* Parameter for usbtest driver. */
|
|
|
|
+struct usbtest_param_32 {
|
|
/* inputs */
|
|
/* 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 */
|
|
/* 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)
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
@@ -1030,7 +1054,7 @@ struct ctrl_ctx {
|
|
unsigned pending;
|
|
unsigned pending;
|
|
int status;
|
|
int status;
|
|
struct urb **urb;
|
|
struct urb **urb;
|
|
- struct usbtest_param *param;
|
|
|
|
|
|
+ struct usbtest_param_32 *param;
|
|
int last;
|
|
int last;
|
|
};
|
|
};
|
|
|
|
|
|
@@ -1155,7 +1179,7 @@ error:
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
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 usb_device *udev = testdev_to_usbdev(dev);
|
|
struct urb **urb;
|
|
struct urb **urb;
|
|
@@ -1930,7 +1954,7 @@ static struct urb *iso_alloc_urb(
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
-test_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)
|
|
int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)
|
|
{
|
|
{
|
|
struct transfer_context context;
|
|
struct transfer_context context;
|
|
@@ -2049,81 +2073,20 @@ static int test_unaligned_bulk(
|
|
return retval;
|
|
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, 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.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
|
|
+/* Run tests. */
|
|
static int
|
|
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 usbtest_dev *dev = usb_get_intfdata(intf);
|
|
struct usb_device *udev = testdev_to_usbdev(dev);
|
|
struct usb_device *udev = testdev_to_usbdev(dev);
|
|
- struct usbtest_param *param = buf;
|
|
|
|
- int retval = -EOPNOTSUPP;
|
|
|
|
struct urb *urb;
|
|
struct urb *urb;
|
|
struct scatterlist *sg;
|
|
struct scatterlist *sg;
|
|
struct usb_sg_request req;
|
|
struct usb_sg_request req;
|
|
- struct timeval start;
|
|
|
|
unsigned i;
|
|
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)
|
|
if (param->iterations <= 0)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
-
|
|
|
|
- if (param->sglen > MAX_SGLEN)
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Just a bunch of test cases that every HCD is expected to handle.
|
|
* Just a bunch of test cases that every HCD is expected to handle.
|
|
*
|
|
*
|
|
@@ -2133,7 +2096,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
|
|
* FIXME add more tests! cancel requests, verify the data, control
|
|
* FIXME add more tests! cancel requests, verify the data, control
|
|
* queueing, concurrent read+write threads, and so on.
|
|
* queueing, concurrent read+write threads, and so on.
|
|
*/
|
|
*/
|
|
- do_gettimeofday(&start);
|
|
|
|
switch (param->test_num) {
|
|
switch (param->test_num) {
|
|
|
|
|
|
case 0:
|
|
case 0:
|
|
@@ -2548,13 +2510,116 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
|
|
dev->in_pipe, NULL, 0);
|
|
dev->in_pipe, NULL, 0);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- do_gettimeofday(¶m->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 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)
|
|
|
|
+ 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);
|
|
mutex_unlock(&dev->lock);
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|