# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#                  ChangeSet    1.679   -> 1.680  
#       drivers/usb/misc/Config.help    1.6     -> 1.7    
#       drivers/usb/misc/Makefile       1.9     -> 1.10   
#       drivers/usb/misc/Config.in      1.3     -> 1.4    
#       drivers/usb/Makefile    1.36    -> 1.37   
#                      (new)            -> 1.1     drivers/usb/misc/usbtest.c
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/10/02      [EMAIL PROTECTED]     1.680
# [PATCH] USB: framework for testing usbcore
# 
# USB test driver
# --------------------------------------------
#
diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile
--- a/drivers/usb/Makefile      Wed Oct  2 22:40:20 2002
+++ b/drivers/usb/Makefile      Wed Oct  2 22:40:20 2002
@@ -60,6 +60,7 @@
 obj-$(CONFIG_USB_LCD)          += misc/
 obj-$(CONFIG_USB_RIO500)       += misc/
 obj-$(CONFIG_USB_SPEEDTOUCH)   += misc/
+obj-$(CONFIG_USB_TEST)         += misc/
 obj-$(CONFIG_USB_TIGL)         += misc/
 obj-$(CONFIG_USB_USS720)       += misc/
 
diff -Nru a/drivers/usb/misc/Config.help b/drivers/usb/misc/Config.help
--- a/drivers/usb/misc/Config.help      Wed Oct  2 22:40:20 2002
+++ b/drivers/usb/misc/Config.help      Wed Oct  2 22:40:20 2002
@@ -112,6 +112,12 @@
   For more information, see Johan Verrept's webpages at
   <http://linux-usb.sourceforge.net/SpeedTouch/>.
 
+CONFIG_USB_TEST
+
+  This driver is for testing host controller software.  It is used
+  with specialized device firmware for regression and stress testing,
+  to help prevent problems from cropping up with 'real" drivers.
+
 CONFIG_USB_LCD
   Say Y here if you want to connect an USBLCD to your computer's
   USB port. The USBLCD is a small USB interface board for
diff -Nru a/drivers/usb/misc/Config.in b/drivers/usb/misc/Config.in
--- a/drivers/usb/misc/Config.in        Wed Oct  2 22:40:20 2002
+++ b/drivers/usb/misc/Config.in        Wed Oct  2 22:40:20 2002
@@ -9,3 +9,4 @@
 dep_tristate '  Tieman Voyager USB Braille display support (EXPERIMENTAL)' 
CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL
 dep_tristate '  USB LCD driver support' CONFIG_USB_LCD $CONFIG_USB
 dep_tristate '  Alcatel Speedtouch ADSL USB Modem' CONFIG_USB_SPEEDTOUCH $CONFIG_USB 
$CONFIG_ATM
+dep_tristate '  USB testing driver (DEVELOPMENT)' CONFIG_USB_TEST 
+$CONFIG_USB_DEVICEFS $CONFIG_EXPERIMENTAL
diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
--- a/drivers/usb/misc/Makefile Wed Oct  2 22:40:20 2002
+++ b/drivers/usb/misc/Makefile Wed Oct  2 22:40:20 2002
@@ -11,6 +11,7 @@
 obj-$(CONFIG_USB_LCD)          += usblcd.o
 obj-$(CONFIG_USB_RIO500)       += rio500.o
 obj-$(CONFIG_USB_SPEEDTOUCH)   += speedtouch.o atmsar.o
+obj-$(CONFIG_USB_TEST)         += usbtest.o
 obj-$(CONFIG_USB_TIGL)         += tiglusb.o
 obj-$(CONFIG_USB_USS720)       += uss720.o
 
diff -Nru a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/misc/usbtest.c        Wed Oct  2 22:40:20 2002
@@ -0,0 +1,570 @@
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+//#include <linux/time.h>
+#include <asm/scatterlist.h>
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+#   define DEBUG
+#endif
+#include <linux/usb.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+// FIXME make these public somewhere; usbdevfs.h?
+//
+struct usbtest_param {
+       // inputs
+       unsigned                test_num;       /* 0..(TEST_CASES-1) */
+       int                     iterations;
+       int                     length;
+       int                     vary;
+       int                     sglen;
+
+       // outputs
+       struct timeval          duration;
+};
+#define USBTEST_REQUEST        _IOWR('U', 100, struct usbtest_param)
+
+/*-------------------------------------------------------------------------*/
+
+/* this is accessed only through usbfs ioctl calls.
+ * one ioctl to issue a test ... no locking needed!!!
+ * tests create other threads if they need them.
+ * urbs and buffers are allocated dynamically,
+ * and data generated deterministically.
+ *
+ * there's a minor complication on disconnect(), since
+ * usbfs.disconnect() waits till our ioctl completes.
+ */
+struct usbtest_dev {
+       struct usb_interface    *intf;
+       struct testdev_info     *info;
+       char                    id [32];
+       int                     in_pipe;
+       int                     out_pipe;
+};
+
+static struct usb_device *testdev_to_usbdev (struct usbtest_dev *test)
+{
+       return interface_to_usbdev (test->intf);
+}
+
+/* set up all urbs so they can be used with either bulk or interrupt */
+#define        INTERRUPT_RATE          1       /* msec/transfer */
+
+/*-------------------------------------------------------------------------*/
+
+/* Support for testing basic non-queued I/O streams.
+ *
+ * These just package urbs as requests that can be easily canceled.
+ * Each urb's data buffer is dynamically allocated; callers can fill
+ * them with non-zero test data (or test for it) when appropriate.
+ */
+
+static void simple_callback (struct urb *urb)
+{
+       complete ((struct completion *) urb->context);
+}
+
+static struct urb *simple_alloc_urb (
+       struct usb_device       *udev,
+       int                     pipe,
+       long                    bytes
+)
+{
+       struct urb              *urb;
+
+       if (bytes < 0)
+               return 0;
+       urb = usb_alloc_urb (0, SLAB_KERNEL);
+       if (!urb)
+               return urb;
+       usb_fill_bulk_urb (urb, udev, pipe, 0, bytes, simple_callback, 0);
+       urb->interval = (udev->speed == USB_SPEED_HIGH)
+                       ? (INTERRUPT_RATE << 3)
+                       : INTERRUPT_RATE,
+       urb->transfer_flags = URB_NO_DMA_MAP;
+       urb->transfer_buffer = usb_buffer_alloc (udev, bytes, SLAB_KERNEL,
+                       &urb->transfer_dma);
+       if (!urb->transfer_buffer) {
+               usb_free_urb (urb);
+               urb = 0;
+       } else
+               memset (urb->transfer_buffer, 0, bytes);
+       return urb;
+}
+
+static void simple_free_urb (struct urb *urb)
+{
+       usb_buffer_free (urb->dev, urb->transfer_buffer_length,
+                       urb->transfer_buffer, urb->transfer_dma);
+       usb_free_urb (urb);
+}
+
+static int simple_io (
+       struct urb              *urb,
+       int                     iterations,
+       int                     vary
+)
+{
+       struct usb_device       *udev = urb->dev;
+       int                     max = urb->transfer_buffer_length;
+       struct completion       completion;
+       int                     retval = 0;
+
+       urb->context = &completion;
+       while (iterations-- > 0 && retval == 0) {
+               init_completion (&completion);
+               if ((retval = usb_submit_urb (urb, SLAB_KERNEL)) != 0)
+                       break;
+
+               /* NOTE:  no timeouts; can't be broken out of by interrupt */
+               wait_for_completion (&completion);
+               retval = urb->status;
+               urb->dev = udev;
+
+               if (vary) {
+                       int     len = urb->transfer_buffer_length;
+
+                       len += max;
+                       len %= max;
+                       if (len == 0)
+                               len = (vary < max) ? vary : max;
+                       urb->transfer_buffer_length = len;
+               }
+
+               /* FIXME if endpoint halted, clear halt (and log) */
+       }
+       urb->transfer_buffer_length = max;
+
+       // FIXME for unlink or fault handling tests, don't report
+       // failure if retval is as we expected ...
+       if (retval)
+               dbg ("simple_io failed, iterations left %d, status %d",
+                               iterations, retval);
+       return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We use scatterlist primitives to test queued I/O.
+ * Yes, this also tests the scatterlist primitives.
+ */
+
+static void free_sglist (struct scatterlist *sg, int nents)
+{
+       unsigned                i;
+       
+       if (!sg)
+               return;
+       for (i = 0; i < nents; i++) {
+               if (!sg [i].page)
+                       continue;
+               kfree (page_address (sg [i].page) + sg [i].offset);
+       }
+       kfree (sg);
+}
+
+static struct scatterlist *
+alloc_sglist (int nents, int max, int vary)
+{
+       struct scatterlist      *sg;
+       unsigned                i;
+       unsigned                size = max;
+
+       sg = kmalloc (nents * sizeof *sg, SLAB_KERNEL);
+       if (!sg)
+               return 0;
+       memset (sg, 0, nents * sizeof *sg);
+
+       for (i = 0; i < nents; i++) {
+               char            *buf;
+
+               buf = kmalloc (size, SLAB_KERNEL);
+               if (!buf) {
+                       free_sglist (sg, i);
+                       return 0;
+               }
+               memset (buf, 0, size);
+
+               /* kmalloc pages are always physically contiguous! */
+               sg [i].page = virt_to_page (buf);
+               sg [i].offset = ((unsigned) buf) & ~PAGE_MASK;
+               sg [i].length = size;
+
+               if (vary) {
+                       size += vary;
+                       size %= max;
+                       if (size == 0)
+                               size = (vary < max) ? vary : max;
+               }
+       }
+
+       return sg;
+}
+
+static int perform_sglist (
+       struct usb_device       *udev,
+       unsigned                iterations,
+       int                     pipe,
+       struct usb_sg_request   *req,
+       struct scatterlist      *sg,
+       int                     nents
+)
+{
+       int                     retval = 0;
+
+       while (retval == 0 && iterations-- > 0) {
+               retval = usb_sg_init (req, udev, pipe,
+                               (udev->speed == USB_SPEED_HIGH)
+                                       ? (INTERRUPT_RATE << 3)
+                                       : INTERRUPT_RATE,
+                               sg, nents, 0, SLAB_KERNEL);
+               
+               if (retval)
+                       break;
+               usb_sg_wait (req);
+               retval = req->status;
+
+               /* FIXME if endpoint halted, clear halt (and log) */
+       }
+
+       // FIXME for unlink or fault handling tests, don't report
+       // failure if retval is as we expected ...
+
+       if (retval)
+               dbg ("perform_sglist failed, iterations left %d, status %d",
+                               iterations, 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.
+ */
+
+static int usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf)
+{
+       struct usbtest_dev      *dev = dev_get_drvdata (&intf->dev);
+       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;
+
+       // FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is.
+
+       if (code != USBTEST_REQUEST)
+               return -EOPNOTSUPP;
+
+       if (param->iterations <= 0 || param->length < 0
+                       || param->sglen < 0 || param->vary < 0)
+               return -EINVAL;
+
+       /*
+        * Just a bunch of test cases that every HCD is expected to handle.
+        *
+        * Some may need specific firmware, though it'd be good to have
+        * one firmware image to handle all the test cases.
+        *
+        * FIXME add more tests!  cancel requests, verify the data, control
+        * requests, and so on.
+        */
+       do_gettimeofday (&start);
+       switch (param->test_num) {
+
+       case 0:
+               dbg ("%s TEST 0:  NOP", dev->id);
+               retval = 0;
+               break;
+
+       /* Simple non-queued bulk I/O tests */
+       case 1:
+               if (dev->out_pipe == 0)
+                       break;
+               dbg ("%s TEST 1:  write %d bytes %u times", dev->id,
+                               param->length, param->iterations);
+               urb = simple_alloc_urb (udev, dev->out_pipe, param->length);
+               if (!urb) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               // FIRMWARE:  bulk sink (maybe accepts short writes)
+               retval = simple_io (urb, param->iterations, 0);
+               simple_free_urb (urb);
+               break;
+       case 2:
+               if (dev->in_pipe == 0)
+                       break;
+               dbg ("%s TEST 2:  read %d bytes %u times", dev->id,
+                               param->length, param->iterations);
+               urb = simple_alloc_urb (udev, dev->in_pipe, param->length);
+               if (!urb) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               // FIRMWARE:  bulk source (maybe generates short writes)
+               retval = simple_io (urb, param->iterations, 0);
+               simple_free_urb (urb);
+               break;
+       case 3:
+               if (dev->out_pipe == 0 || param->vary == 0)
+                       break;
+               dbg ("%s TEST 3:  write/%d 0..%d bytes %u times", dev->id,
+                               param->vary, param->length, param->iterations);
+               urb = simple_alloc_urb (udev, dev->out_pipe, param->length);
+               if (!urb) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               // FIRMWARE:  bulk sink (maybe accepts short writes)
+               retval = simple_io (urb, param->iterations, param->vary);
+               simple_free_urb (urb);
+               break;
+       case 4:
+               if (dev->in_pipe == 0 || param->vary == 0)
+                       break;
+               dbg ("%s TEST 3:  read/%d 0..%d bytes %u times", dev->id,
+                               param->vary, param->length, param->iterations);
+               urb = simple_alloc_urb (udev, dev->out_pipe, param->length);
+               if (!urb) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               // FIRMWARE:  bulk source (maybe generates short writes)
+               retval = simple_io (urb, param->iterations, param->vary);
+               simple_free_urb (urb);
+               break;
+
+       /* Queued bulk I/O tests */
+       case 5:
+               if (dev->out_pipe == 0 || param->sglen == 0)
+                       break;
+               dbg ("%s TEST 5:  write %d sglists, %d entries of %d bytes",
+                               dev->id, param->iterations,
+                               param->sglen, param->length);
+               sg = alloc_sglist (param->sglen, param->length, 0);
+               if (!sg) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               // FIRMWARE:  bulk sink (maybe accepts short writes)
+               retval = perform_sglist (udev, param->iterations, dev->out_pipe,
+                               &req, sg, param->sglen);
+               free_sglist (sg, param->sglen);
+               break;
+
+       case 6:
+               if (dev->in_pipe == 0 || param->sglen == 0)
+                       break;
+               dbg ("%s TEST 6:  read %d sglists, %d entries of %d bytes",
+                               dev->id, param->iterations,
+                               param->sglen, param->length);
+               sg = alloc_sglist (param->sglen, param->length, 0);
+               if (!sg) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               // FIRMWARE:  bulk source (maybe generates short writes)
+               retval = perform_sglist (udev, param->iterations, dev->in_pipe,
+                               &req, sg, param->sglen);
+               free_sglist (sg, param->sglen);
+               break;
+       case 7:
+               if (dev->out_pipe == 0 || param->sglen == 0 || param->vary == 0)
+                       break;
+               dbg ("%s TEST 7:  write/%d %d sglists, %d entries 0..%d bytes",
+                               dev->id, param->vary, param->iterations,
+                               param->sglen, param->length);
+               sg = alloc_sglist (param->sglen, param->length, param->vary);
+               if (!sg) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               // FIRMWARE:  bulk sink (maybe accepts short writes)
+               retval = perform_sglist (udev, param->iterations, dev->out_pipe,
+                               &req, sg, param->sglen);
+               free_sglist (sg, param->sglen);
+               break;
+       case 8:
+               if (dev->in_pipe == 0 || param->sglen == 0 || param->vary == 0)
+                       break;
+               dbg ("%s TEST 8:  read/%d %d sglists, %d entries 0..%d bytes",
+                               dev->id, param->vary, param->iterations,
+                               param->sglen, param->length);
+               sg = alloc_sglist (param->sglen, param->length, param->vary);
+               if (!sg) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               // FIRMWARE:  bulk source (maybe generates short writes)
+               retval = perform_sglist (udev, param->iterations, dev->in_pipe,
+                               &req, sg, param->sglen);
+               free_sglist (sg, param->sglen);
+               break;
+
+       /* test cases for the unlink/cancel codepaths need a thread to
+        * usb_unlink_urb() or usg_sg_cancel(), and a way to check if
+        * the urb/sg_request was properly canceled.
+        *
+        * for the unlink-queued cases, the usb_sg_*() code uses/tests
+        * the "streamed" cleanup mode, not the "packet" one
+        */
+
+       }
+       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;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* most programmable USB devices can be given firmware that will support the
+ * test cases above.  one basic question is which endpoints to use for
+ * testing; endpoint numbers are not always firmware-selectable.
+ *
+ * for now, the driver_info in the device_id table entry just encodes the
+ * endpoint info for a pair of bulk-capable endpoints, which we can use
+ * for some interrupt transfer tests too.  later this could get fancier.
+ */
+#define EP_PAIR(in,out) (((in)<<4)|(out))
+
+static int force_interrupt = 0;
+MODULE_PARM (force_interrupt, "i");
+MODULE_PARM_DESC (force_interrupt, "0 = test bulk (default), else interrupt");
+
+static int
+usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct usb_device       *udev;
+       struct usbtest_dev      *dev;
+       unsigned long           driver_info = id->driver_info;
+
+       udev = interface_to_usbdev (intf);
+
+       dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+       memset (dev, 0, sizeof *dev);
+       snprintf (dev->id, sizeof dev->id, "%s-%s",
+                       udev->bus->bus_name, udev->devpath);
+       dev->intf = intf;
+
+       /* NOTE this doesn't yet test the handful of difference that are
+        * visible with high speed devices:  bigger maxpacket (1K) and
+        * "high bandwidth" modes (up to 3 packets/uframe).
+        */
+       if (force_interrupt || udev->speed == USB_SPEED_LOW) {
+               if (driver_info & 0xf0)
+                       dev->in_pipe = usb_rcvintpipe (udev,
+                               (driver_info >> 4) & 0x0f);
+               if (driver_info & 0x0f)
+                       dev->out_pipe = usb_sndintpipe (udev,
+                               driver_info & 0x0f);
+
+#if 1
+               // FIXME disabling this until we finally get rid of
+               // interrupt "automagic" resubmission
+               dbg ("%s:  no interrupt transfers for now", dev->id);
+               kfree (dev);
+               return -ENODEV;
+#endif
+       } else {
+               if (driver_info & 0xf0)
+                       dev->in_pipe = usb_rcvbulkpipe (udev,
+                               (driver_info >> 4) & 0x0f);
+               if (driver_info & 0x0f)
+                       dev->out_pipe = usb_sndbulkpipe (udev,
+                               driver_info & 0x0f);
+       }
+
+       dev_set_drvdata (&intf->dev, dev);
+       info ("bound to %s ...%s%s", dev->id,
+                       dev->out_pipe ? " writes" : "",
+                       dev->in_pipe ? " reads" : "");
+       return 0;
+}
+
+static void usbtest_disconnect (struct usb_interface *intf)
+{
+       struct usbtest_dev      *dev = dev_get_drvdata (&intf->dev);
+
+       dev_set_drvdata (&intf->dev, 0);
+       info ("unbound %s", dev->id);
+       kfree (intf->private_data);
+}
+
+/* Basic testing only needs a device that can source or sink bulk traffic.
+ */
+static struct usb_device_id id_table [] = {
+
+       /* EZ-USB FX2 "bulksrc" or "bulkloop" firmware from Cypress
+        * reads disabled on this one, my version has some problem there
+        */
+       { USB_DEVICE (0x0547, 0x1002),
+               .driver_info = EP_PAIR (0, 2),
+               },
+#if 1
+       // this does not coexist with a real iBOT2 driver!
+       // it makes a nice source of high speed bulk-in data
+       { USB_DEVICE (0x0b62, 0x0059),
+               .driver_info = EP_PAIR (2, 0),
+               },
+#endif
+
+       /* can that old "usbstress-0.3" firmware be used with this? */
+
+       { }
+};
+MODULE_DEVICE_TABLE (usb, id_table);
+
+static struct usb_driver usbtest_driver = {
+       .owner =        THIS_MODULE,
+       .name =         "usbtest",
+       .id_table =     id_table,
+       .probe =        usbtest_probe,
+       .ioctl =        usbtest_ioctl,
+       .disconnect =   usbtest_disconnect,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init usbtest_init (void)
+{
+       return usb_register (&usbtest_driver);
+}
+module_init (usbtest_init);
+
+static void __exit usbtest_exit (void)
+{
+       usb_deregister (&usbtest_driver);
+}
+module_exit (usbtest_exit);
+
+MODULE_DESCRIPTION ("USB HCD Testing Driver");
+MODULE_LICENSE ("GPL");
+


-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to