ChangeSet 1.1500.8.25, 2004/02/05 16:34:35-08:00, [EMAIL PROTECTED]

[PATCH] USB: usbtest updates

[USB] usbtest, add more tests; minor fixes

More tests for host and gadget controller drivers:

    - test 13, bulk endpoint halts
    - test 14, control OUT transfers
    - test 15, iso OUT testing
    - test 16, iso IN testing

Other improvements:
    - Many tests now support data pattern testing submodes.
    - Use the standard usb_set_interface() call, not its own
      version ... one more API call covered!
    - Converted to use driver model style diagnostics.

And minor fixes.


 drivers/usb/misc/usbtest.c |  779 ++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 664 insertions(+), 115 deletions(-)


diff -Nru a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
--- a/drivers/usb/misc/usbtest.c        Mon Feb  9 14:38:00 2004
+++ b/drivers/usb/misc/usbtest.c        Mon Feb  9 14:38:00 2004
@@ -1,15 +1,16 @@
 #include <linux/config.h>
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+#   define DEBUG
+#endif
 #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/moduleparam.h>
 #include <asm/scatterlist.h>
 
-#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
-#   define DEBUG
-#endif
 #include <linux/usb.h>
 
 
@@ -20,10 +21,10 @@
 struct usbtest_param {
        // inputs
        unsigned                test_num;       /* 0..(TEST_CASES-1) */
-       int                     iterations;
-       int                     length;
-       int                     vary;
-       int                     sglen;
+       unsigned                iterations;
+       unsigned                length;
+       unsigned                vary;
+       unsigned                sglen;
 
        // outputs
        struct timeval          duration;
@@ -48,6 +49,8 @@
        u8                      ep_in;          /* bulk/intr source */
        u8                      ep_out;         /* bulk/intr sink */
        unsigned                autoconf : 1;
+       unsigned                ctrl_out : 1;
+       unsigned                iso : 1;        /* try iso in/out */
        int                     alt;
 };
 
@@ -60,9 +63,11 @@
 struct usbtest_dev {
        struct usb_interface    *intf;
        struct usbtest_info     *info;
-       char                    id [32];
        int                     in_pipe;
        int                     out_pipe;
+       int                     in_iso_pipe;
+       int                     out_iso_pipe;
+       struct usb_endpoint_descriptor  *iso_in, *iso_out;
        struct semaphore        sem;
 
 #define TBUF_SIZE      256
@@ -77,6 +82,31 @@
 /* set up all urbs so they can be used with either bulk or interrupt */
 #define        INTERRUPT_RATE          1       /* msec/transfer */
 
+#define xprintk(tdev,level,fmt,args...) \
+       dev_printk(level ,  &(tdev)->intf->dev ,  fmt ,  ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+       xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+       do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(dev,fmt,args...) \
+       do { } while (0)
+#endif /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+       xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+       xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+       xprintk(dev , KERN_INFO , fmt , ## args)
+
 /*-------------------------------------------------------------------------*/
 
 static int
@@ -85,12 +115,14 @@
        int                             tmp;
        struct usb_host_interface       *alt;
        struct usb_host_endpoint        *in, *out;
+       struct usb_host_endpoint        *iso_in, *iso_out;
        struct usb_device               *udev;
 
        for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
                unsigned        ep;
 
                in = out = 0;
+               iso_in = iso_out = 0;
                alt = intf->altsetting + tmp;
 
                /* take the first altsetting with in-bulk + out-bulk;
@@ -100,8 +132,16 @@
                        struct usb_host_endpoint        *e;
 
                        e = alt->endpoint + ep;
-                       if (e->desc.bmAttributes != USB_ENDPOINT_XFER_BULK)
+                       switch (e->desc.bmAttributes) {
+                       case USB_ENDPOINT_XFER_BULK:
+                               break;
+                       case USB_ENDPOINT_XFER_ISOC:
+                               if (dev->info->iso)
+                                       goto try_iso;
+                               // FALLTHROUGH
+                       default:
                                continue;
+                       }
                        if (e->desc.bEndpointAddress & USB_DIR_IN) {
                                if (!in)
                                        in = e;
@@ -111,6 +151,17 @@
                        }
                        if (in && out)
                                goto found;
+                       continue;
+try_iso:
+                       if (e->desc.bEndpointAddress & USB_DIR_IN) {
+                               if (!iso_in)
+                                       iso_in = e;
+                       } else {
+                               if (!iso_out)
+                                       iso_out = e;
+                       }
+                       if (iso_in && iso_out)
+                               goto found;
                }
        }
        return -EINVAL;
@@ -125,10 +176,21 @@
                        return tmp;
        }
 
-       dev->in_pipe = usb_rcvbulkpipe (udev,
+       if (in) {
+               dev->in_pipe = usb_rcvbulkpipe (udev,
                        in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
-       dev->out_pipe = usb_sndbulkpipe (udev,
+               dev->out_pipe = usb_sndbulkpipe (udev,
                        out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+       } else if (iso_in) {
+               dev->iso_in = &iso_in->desc;
+               dev->in_iso_pipe = usb_rcvisocpipe (udev,
+                               iso_in->desc.bEndpointAddress
+                                       & USB_ENDPOINT_NUMBER_MASK);
+               dev->iso_out = &iso_out->desc;
+               dev->out_iso_pipe = usb_sndisocpipe (udev,
+                               iso_out->desc.bEndpointAddress
+                                       & USB_ENDPOINT_NUMBER_MASK);
+       }
        return 0;
 }
 
@@ -176,6 +238,63 @@
        return urb;
 }
 
+static unsigned pattern = 0;
+module_param (pattern, uint, S_IRUGO);
+// MODULE_PARM_DESC (pattern, "i/o pattern (0 == zeroes)");
+
+static inline void simple_fill_buf (struct urb *urb)
+{
+       unsigned        i;
+       u8              *buf = urb->transfer_buffer;
+       unsigned        len = urb->transfer_buffer_length;
+
+       switch (pattern) {
+       default:
+               // FALLTHROUGH
+       case 0:
+               memset (buf, 0, len);
+               break;
+       case 1:                 /* mod63 */
+               for (i = 0; i < len; i++)
+                       *buf++ = (u8) (i % 63);
+               break;
+       }
+}
+
+static inline int simple_check_buf (struct urb *urb)
+{
+       unsigned        i;
+       u8              expected;
+       u8              *buf = urb->transfer_buffer;
+       unsigned        len = urb->actual_length;
+
+       for (i = 0; i < len; i++, buf++) {
+               switch (pattern) {
+               /* all-zeroes has no synchronization issues */
+               case 0:
+                       expected = 0;
+                       break;
+               /* mod63 stays in sync with short-terminated transfers,
+                * or otherwise when host and gadget agree on how large
+                * each usb transfer request should be.  resync is done
+                * with set_interface or set_config.
+                */
+               case 1:                 /* mod63 */
+                       expected = i % 63;
+                       break;
+               /* always fail unsupported patterns */
+               default:
+                       expected = !*buf;
+                       break;
+               }
+               if (*buf == expected)
+                       continue;
+               dbg ("buf[%d] = %d (not %d)", i, *buf, expected);
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static void simple_free_urb (struct urb *urb)
 {
        usb_buffer_free (urb->dev, urb->transfer_buffer_length,
@@ -186,7 +305,9 @@
 static int simple_io (
        struct urb              *urb,
        int                     iterations,
-       int                     vary
+       int                     vary,
+       int                     expected,
+       const char              *label
 )
 {
        struct usb_device       *udev = urb->dev;
@@ -197,6 +318,8 @@
        urb->context = &completion;
        while (retval == 0 && iterations-- > 0) {
                init_completion (&completion);
+               if (usb_pipeout (urb->pipe))
+                       simple_fill_buf (urb);
                if ((retval = usb_submit_urb (urb, SLAB_KERNEL)) != 0)
                        break;
 
@@ -204,6 +327,8 @@
                wait_for_completion (&completion);
                retval = urb->status;
                urb->dev = udev;
+               if (retval == 0 && usb_pipein (urb->pipe))
+                       retval = simple_check_buf (urb);
 
                if (vary) {
                        int     len = urb->transfer_buffer_length;
@@ -219,14 +344,14 @@
        }
        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);
+       if (expected != retval)
+               dev_dbg (&udev->dev,
+                       "%s failed, iterations left %d, status %d (not %d)\n",
+                               label, iterations, retval, expected);
        return retval;
 }
 
+
 /*-------------------------------------------------------------------------*/
 
 /* We use scatterlist primitives to test queued I/O.
@@ -360,54 +485,18 @@
        }
 }
 
-/* this is usb_set_interface(), with no 'only one altsetting' case */
 static int set_altsetting (struct usbtest_dev *dev, int alternate)
 {
        struct usb_interface            *iface = dev->intf;
        struct usb_device               *udev;
-       struct usb_host_interface       *iface_as;
-       int                             i, ret;
 
        if (alternate < 0 || alternate >= iface->num_altsetting)
                return -EINVAL;
 
        udev = interface_to_usbdev (iface);
-       if ((ret = usb_control_msg (udev, usb_sndctrlpipe (udev, 0),
-                       USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
-                       alternate,
-                       iface->altsetting->desc.bInterfaceNumber,
-                       NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)
-               return ret;
-
-       // FIXME usbcore should be more like this:
-       // - remove that special casing in usbcore.
-       // - fix usbcore signature to take interface
-
-       /* prevent requests using previous endpoint settings */
-       iface_as = iface->altsetting + iface->act_altsetting;
-       for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
-               u8      ep = iface_as->endpoint [i].desc.bEndpointAddress;
-               int     out = !(ep & USB_DIR_IN);
-
-               ep &= USB_ENDPOINT_NUMBER_MASK;
-               (out ? udev->epmaxpacketout : udev->epmaxpacketin ) [ep] = 0;
-               // FIXME want hcd hook here, "forget this endpoint"
-       }
-       iface->act_altsetting = alternate;
-
-       /* reset toggles and maxpacket for all endpoints affected */
-       iface_as = iface->altsetting + iface->act_altsetting;
-       for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
-               u8      ep = iface_as->endpoint [i].desc.bEndpointAddress;
-               int     out = !(ep & USB_DIR_IN);
-
-               ep &= USB_ENDPOINT_NUMBER_MASK;
-               usb_settoggle (udev, ep, out, 0);
-               (out ? udev->epmaxpacketout : udev->epmaxpacketin ) [ep]
-                       = iface_as->endpoint [i].desc.wMaxPacketSize;
-       }
-
-       return 0;
+       return usb_set_interface (udev,
+                       iface->altsetting [0].desc.bInterfaceNumber,
+                       alternate);
 }
 
 static int is_good_config (char *buf, int len)
@@ -421,15 +510,19 @@
        switch (config->bDescriptorType) {
        case USB_DT_CONFIG:
        case USB_DT_OTHER_SPEED_CONFIG:
-               if (config->bLength != 9)
+               if (config->bLength != 9) {
+                       dbg ("bogus config descriptor length");
                        return 0;
+               }
                /* this bit 'must be 1' but often isn't */
                if (!realworld && !(config->bmAttributes & 0x80)) {
                        dbg ("high bit of config attributes not set");
                        return 0;
                }
-               if (config->bmAttributes & 0x1f)        /* reserved == 0 */
+               if (config->bmAttributes & 0x1f) {      /* reserved == 0 */
+                       dbg ("reserved config bits set");
                        return 0;
+               }
                break;
        default:
                return 0;
@@ -438,7 +531,10 @@
        le16_to_cpus (&config->wTotalLength);
        if (config->wTotalLength == len)                /* read it all */
                return 1;
-       return config->wTotalLength >= TBUF_SIZE;       /* max partial read */
+       if (config->wTotalLength >= TBUF_SIZE)          /* max partial read */
+               return 1;
+       dbg ("bogus config descriptor read size");
+       return 0;
 }
 
 /* sanity test for standard requests working with usb_control_mesg() and some
@@ -471,8 +567,9 @@
                 * they're ordered meaningfully in this array
                 */
                if (iface->altsetting [i].desc.bAlternateSetting != i) {
-                       dbg ("%s, invalid alt [%d].bAltSetting = %d",
-                                       dev->id, i, 
+                       dev_dbg (&iface->dev,
+                                       "invalid alt [%d].bAltSetting = %d\n",
+                                       i, 
                                        iface->altsetting [i].desc
                                                .bAlternateSetting);
                        return -EDOM;
@@ -485,16 +582,16 @@
                /* [9.4.10] set_interface */
                retval = set_altsetting (dev, i);
                if (retval) {
-                       dbg ("%s can't set_interface = %d, %d",
-                                       dev->id, i, retval);
+                       dev_dbg (&iface->dev, "can't set_interface = %d, %d\n",
+                                       i, retval);
                        return retval;
                }
 
                /* [9.4.4] get_interface always works */
                retval = get_altsetting (dev);
                if (retval != i) {
-                       dbg ("%s get alt should be %d, was %d",
-                                       dev->id, i, retval);
+                       dev_dbg (&iface->dev, "get alt should be %d, was %d\n",
+                                       i, retval);
                        return (retval < 0) ? retval : -EDOM;
                }
 
@@ -513,7 +610,8 @@
                                USB_DIR_IN | USB_RECIP_DEVICE,
                                0, 0, dev->buf, 1, HZ * USB_CTRL_GET_TIMEOUT);
                if (retval != 1 || dev->buf [0] != expected) {
-                       dbg ("%s get config --> %d (%d)", dev->id, retval,
+                       dev_dbg (&iface->dev,
+                               "get config --> %d (%d)\n", retval,
                                expected);
                        return (retval < 0) ? retval : -EDOM;
                }
@@ -523,7 +621,7 @@
        retval = usb_get_descriptor (udev, USB_DT_DEVICE, 0,
                        dev->buf, sizeof udev->descriptor);
        if (retval != sizeof udev->descriptor) {
-               dbg ("%s dev descriptor --> %d", dev->id, retval);
+               dev_dbg (&iface->dev, "dev descriptor --> %d\n", retval);
                return (retval < 0) ? retval : -EDOM;
        }
 
@@ -532,8 +630,9 @@
                retval = usb_get_descriptor (udev, USB_DT_CONFIG, i,
                                dev->buf, TBUF_SIZE);
                if (!is_good_config (dev->buf, retval)) {
-                       dbg ("%s config [%d] descriptor --> %d",
-                                       dev->id, i, retval);
+                       dev_dbg (&iface->dev,
+                                       "config [%d] descriptor --> %d\n",
+                                       i, retval);
                        return (retval < 0) ? retval : -EDOM;
                }
 
@@ -551,13 +650,14 @@
                                sizeof (struct usb_qualifier_descriptor));
                if (retval == -EPIPE) {
                        if (udev->speed == USB_SPEED_HIGH) {
-                               dbg ("%s hs dev qualifier --> %d",
-                                               dev->id, retval);
+                               dev_dbg (&iface->dev,
+                                               "hs dev qualifier --> %d\n",
+                                               retval);
                                return (retval < 0) ? retval : -EDOM;
                        }
                        /* usb2.0 but not high-speed capable; fine */
                } else if (retval != sizeof (struct usb_qualifier_descriptor)) {
-                       dbg ("%s dev qualifier --> %d", dev->id, retval);
+                       dev_dbg (&iface->dev, "dev qualifier --> %d\n", retval);
                        return (retval < 0) ? retval : -EDOM;
                } else
                        d = (struct usb_qualifier_descriptor *) dev->buf;
@@ -570,8 +670,9 @@
                                        USB_DT_OTHER_SPEED_CONFIG, i,
                                        dev->buf, TBUF_SIZE);
                                if (!is_good_config (dev->buf, retval)) {
-                                       dbg ("%s other speed config --> %d",
-                                                       dev->id, retval);
+                                       dev_dbg (&iface->dev,
+                                               "other speed config --> %d\n",
+                                               retval);
                                        return (retval < 0) ? retval : -EDOM;
                                }
                        }
@@ -582,7 +683,7 @@
        /* [9.4.5] get_status always works */
        retval = usb_get_status (udev, USB_RECIP_DEVICE, 0, dev->buf);
        if (retval != 2) {
-               dbg ("%s get dev status --> %d", dev->id, retval);
+               dev_dbg (&iface->dev, "get dev status --> %d\n", retval);
                return (retval < 0) ? retval : -EDOM;
        }
 
@@ -592,7 +693,7 @@
        retval = usb_get_status (udev, USB_RECIP_INTERFACE,
                        iface->altsetting [0].desc.bInterfaceNumber, dev->buf);
        if (retval != 2) {
-               dbg ("%s get interface status --> %d", dev->id, retval);
+               dev_dbg (&iface->dev, "get interface status --> %d\n", retval);
                return (retval < 0) ? retval : -EDOM;
        }
        // FIXME get status for each endpoint in the interface
@@ -606,7 +707,7 @@
  *   (a) queues work for control, keeping N subtests queued and
  *       active (auto-resubmit) for M loops through the queue.
  *   (b) protocol stalls (control-only) will autorecover.
- *       it's quite not like bulk/intr; no halt clearing.
+ *       it's not like bulk/intr; no halt clearing.
  *   (c) short control reads are reported and handled.
  *   (d) queues are always processed in-order
  */
@@ -654,6 +755,7 @@
                        dbg ("subcase %d completed out of order, last %d",
                                        subcase->number, ctx->last);
                        status = -EDOM;
+                       ctx->last = subcase->number;
                        goto error;
                }
        }
@@ -995,6 +1097,368 @@
 
 /*-------------------------------------------------------------------------*/
 
+static int verify_not_halted (int ep, struct urb *urb)
+{
+       int     retval;
+       u16     status;
+
+       /* shouldn't look or act halted */
+       retval = usb_get_status (urb->dev, USB_RECIP_ENDPOINT, ep, &status);
+       if (retval < 0) {
+               dbg ("ep %02x couldn't get no-halt status, %d", ep, retval);
+               return retval;
+       }
+       if (status != 0) {
+               dbg ("ep %02x bogus status: %04x != 0", ep, status);
+               return -EINVAL;
+       }
+       retval = simple_io (urb, 1, 0, 0, __FUNCTION__);
+       if (retval != 0)
+               return -EINVAL;
+       return 0;
+}
+
+static int verify_halted (int ep, struct urb *urb)
+{
+       int     retval;
+       u16     status;
+
+       /* should look and act halted */
+       retval = usb_get_status (urb->dev, USB_RECIP_ENDPOINT, ep, &status);
+       if (retval < 0) {
+               dbg ("ep %02x couldn't get halt status, %d", ep, retval);
+               return retval;
+       }
+       if (status != 1) {
+               dbg ("ep %02x bogus status: %04x != 1", ep, status);
+               return -EINVAL;
+       }
+       retval = simple_io (urb, 1, 0, -EPIPE, __FUNCTION__);
+       if (retval != -EPIPE)
+               return -EINVAL;
+       retval = simple_io (urb, 1, 0, -EPIPE, "verify_still_halted");
+       if (retval != -EPIPE)
+               return -EINVAL;
+       return 0;
+}
+
+static int test_halt (int ep, struct urb *urb)
+{
+       int     retval;
+
+       /* shouldn't look or act halted now */
+       retval = verify_not_halted (ep, urb);
+       if (retval < 0)
+               return retval;
+
+       /* set halt (protocol test only), verify it worked */
+       retval = usb_control_msg (urb->dev, usb_sndctrlpipe (urb->dev, 0),
+                       USB_REQ_SET_FEATURE, USB_RECIP_ENDPOINT, 0, ep,
+                       NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
+       if (retval < 0) {
+               dbg ("ep %02x couldn't set halt, %d", ep, retval);
+               return retval;
+       }
+       retval = verify_halted (ep, urb);
+       if (retval < 0)
+               return retval;
+
+       /* clear halt (tests API + protocol), verify it worked */
+       retval = usb_clear_halt (urb->dev, urb->pipe);
+       if (retval < 0) {
+               dbg ("ep %02x couldn't clear halt, %d", ep, retval);
+               return retval;
+       }
+       retval = verify_not_halted (ep, urb);
+       if (retval < 0)
+               return retval;
+
+       /* NOTE:  could also verify SET_INTERFACE clear halts ... */
+
+       return 0;
+}
+
+static int halt_simple (struct usbtest_dev *dev)
+{
+       int             ep;
+       int             retval = 0;
+       struct urb      *urb;
+
+       urb = simple_alloc_urb (testdev_to_usbdev (dev), 0, 512);
+       if (urb == 0)
+               return -ENOMEM;
+
+       if (dev->in_pipe) {
+               ep = usb_pipeendpoint (dev->in_pipe) | USB_DIR_IN;
+               urb->pipe = dev->in_pipe;
+               retval = test_halt (ep, urb);
+               if (retval < 0)
+                       goto done;
+       }
+
+       if (dev->out_pipe) {
+               ep = usb_pipeendpoint (dev->out_pipe);
+               urb->pipe = dev->out_pipe;
+               retval = test_halt (ep, urb);
+       }
+done:
+       simple_free_urb (urb);
+       return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Control OUT tests use the vendor control requests from Intel's
+ * USB 2.0 compliance test device:  write a buffer, read it back.
+ *
+ * Intel's spec only _requires_ that it work for one packet, which
+ * is pretty weak.   Some HCDs place limits here; most devices will
+ * need to be able to handle more than one OUT data packet.  We'll
+ * try whatever we're told to try.
+ */
+static int ctrl_out (struct usbtest_dev *dev,
+               unsigned count, unsigned length, unsigned vary)
+{
+       unsigned                i, j, len, retval;
+       u8                      *buf;
+       char                    *what = "?";
+       struct usb_device       *udev;
+       
+       if (length > 0xffff || vary >= length)
+               return -EINVAL;
+
+       buf = kmalloc(length, SLAB_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       udev = testdev_to_usbdev (dev);
+       len = length;
+       retval = 0;
+
+       /* NOTE:  hardware might well act differently if we pushed it
+        * with lots back-to-back queued requests.
+        */
+       for (i = 0; i < count; i++) {
+               /* write patterned data */
+               for (j = 0; j < len; j++)
+                       buf [j] = i + j;
+               retval = usb_control_msg (udev, usb_sndctrlpipe (udev,0),
+                               0x5b, USB_DIR_OUT|USB_TYPE_VENDOR,
+                               0, 0, buf, len, HZ * USB_CTRL_SET_TIMEOUT);
+               if (retval != len) {
+                       what = "write";
+                       break;
+               }
+
+               /* read it back -- assuming nothing intervened!!  */
+               retval = usb_control_msg (udev, usb_rcvctrlpipe (udev,0),
+                               0x5c, USB_DIR_IN|USB_TYPE_VENDOR,
+                               0, 0, buf, len, HZ * USB_CTRL_GET_TIMEOUT);
+               if (retval != len) {
+                       what = "read";
+                       break;
+               }
+
+               /* fail if we can't verify */
+               for (j = 0; j < len; j++) {
+                       if (buf [j] != (u8) (i + j)) {
+                               INFO (dev, "ctrl_out, byte %d is %d not %d\n",
+                                       j, buf [j], (u8) i + j);
+                               retval = -EBADMSG;
+                               break;
+                       }
+               }
+               if (retval < 0) {
+                       what = "verify";
+                       break;
+               }
+
+               len += vary;
+               if (len > length)
+                       len = 0;
+       }
+
+       if (retval < 0)
+               INFO (dev, "ctrl_out %s failed, code %d, count %d\n",
+                       what, retval, i);
+
+       kfree (buf);
+       return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ISO tests ... mimics common usage
+ *  - buffer length is split into N packets (mostly maxpacket sized)
+ *  - multi-buffers according to sglen
+ */
+
+struct iso_context {
+       unsigned                count;
+       unsigned                pending;
+       spinlock_t              lock;
+       struct completion       done;
+       unsigned long           errors;
+       struct usbtest_dev      *dev;
+};
+
+static void iso_callback (struct urb *urb, struct pt_regs *regs)
+{
+       struct iso_context      *ctx = urb->context;
+
+       spin_lock(&ctx->lock);
+       ctx->count--;
+
+       if (urb->error_count > 0)
+               ctx->errors += urb->error_count;
+
+       if (urb->status == 0 && ctx->count > (ctx->pending - 1)) {
+               int status = usb_submit_urb (urb, GFP_ATOMIC);
+               switch (status) {
+               case 0:
+                       goto done;
+               default:
+                       dev_dbg (&ctx->dev->intf->dev,
+                                       "iso resubmit err %d\n",
+                                       status);
+                       /* FALLTHROUGH */
+               case -ENODEV:                   /* disconnected */
+                       break;
+               }
+       }
+       simple_free_urb (urb);
+
+       ctx->pending--;
+       if (ctx->pending == 0) {
+               if (ctx->errors)
+                       dev_dbg (&ctx->dev->intf->dev,
+                               "iso test, %lu errors\n",
+                               ctx->errors);
+               complete (&ctx->done);
+       } else
+done:
+       spin_unlock(&ctx->lock);
+}
+
+static struct urb *iso_alloc_urb (
+       struct usb_device       *udev,
+       int                     pipe,
+       struct usb_endpoint_descriptor  *desc,
+       long                    bytes
+)
+{
+       struct urb              *urb;
+       unsigned                i, maxp, packets;
+
+       if (bytes < 0 || !desc)
+               return 0;
+       maxp = 0x7ff & desc->wMaxPacketSize;
+       maxp *= 1 + (0x3 & (desc->wMaxPacketSize >> 11));
+       packets = (bytes + maxp - 1) / maxp;
+
+       urb = usb_alloc_urb (packets, SLAB_KERNEL);
+       if (!urb)
+               return urb;
+       urb->dev = udev;
+       urb->pipe = pipe;
+
+       urb->number_of_packets = packets;
+       urb->transfer_buffer_length = bytes;
+       urb->transfer_buffer = usb_buffer_alloc (udev, bytes, SLAB_KERNEL,
+                       &urb->transfer_dma);
+       if (!urb->transfer_buffer) {
+               usb_free_urb (urb);
+               return 0;
+       }
+       memset (urb->transfer_buffer, 0, bytes);
+       for (i = 0; i < packets; i++) {
+               /* here, only the last packet will be short */
+               urb->iso_frame_desc[i].length = min ((unsigned) bytes, maxp);
+               bytes -= urb->iso_frame_desc[i].length;
+
+               urb->iso_frame_desc[i].offset = maxp * i;
+       }
+
+       urb->complete = iso_callback;
+       // urb->context = SET BY CALLER
+       urb->interval = 1 << (desc->bInterval - 1);
+       urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+       return urb;
+}
+
+static int
+test_iso_queue (struct usbtest_dev *dev, struct usbtest_param *param,
+               int pipe, struct usb_endpoint_descriptor *desc)
+{
+       struct iso_context      context;
+       struct usb_device       *udev;
+       unsigned                i;
+       unsigned long           packets = 0;
+       int                     status;
+       struct urb              *urbs[10];      /* FIXME no limit */
+
+       if (param->sglen > 10)
+               return -EDOM;
+
+       context.count = param->iterations * param->sglen;
+       context.pending = param->sglen;
+       context.errors = 0;
+       context.dev = dev;
+       init_completion (&context.done);
+       spin_lock_init (&context.lock);
+
+       memset (urbs, 0, sizeof urbs);
+       udev = testdev_to_usbdev (dev);
+       dev_dbg (&dev->intf->dev,
+               "... iso period %d %sframes, wMaxPacket %04x\n",
+               1 << (desc->bInterval - 1),
+               (udev->speed == USB_SPEED_HIGH) ? "micro" : "",
+               desc->wMaxPacketSize);
+
+       for (i = 0; i < param->sglen; i++) {
+               urbs [i] = iso_alloc_urb (udev, pipe, desc,
+                               param->length);
+               if (!urbs [i]) {
+                       status = -ENOMEM;
+                       goto fail;
+               }
+               packets += urbs[i]->number_of_packets;
+               urbs [i]->context = &context;
+       }
+       packets *= param->iterations;
+       dev_dbg (&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++) {
+               status = usb_submit_urb (urbs [i], SLAB_ATOMIC);
+               if (status < 0) {
+                       ERROR (dev, "submit iso[%d], error %d\n", i, status);
+                       if (i == 0)
+                               goto fail;
+
+                       simple_free_urb (urbs [i]);
+                       context.pending--;
+               }
+       }
+       spin_unlock_irq (&context.lock);
+
+       wait_for_completion (&context.done);
+       return 0;
+
+fail:
+       for (i = 0; i < param->sglen; i++) {
+               if (urbs [i])
+                       simple_free_urb (urbs [i]);
+       }
+       return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
 /* 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
@@ -1047,8 +1511,9 @@
                }
                res = set_altsetting (dev, dev->info->alt);
                if (res) {
-                       err ("%s: set altsetting to %d failed, %d",
-                                       dev->id, dev->info->alt, res);
+                       dev_err (&intf->dev,
+                                       "set altsetting to %d failed, %d\n",
+                                       dev->info->alt, res);
                        up (&dev->sem);
                        return res;
                }
@@ -1067,7 +1532,7 @@
        switch (param->test_num) {
 
        case 0:
-               dbg ("%s TEST 0:  NOP", dev->id);
+               dev_dbg (&intf->dev, "TEST 0:  NOP\n");
                retval = 0;
                break;
 
@@ -1075,7 +1540,8 @@
        case 1:
                if (dev->out_pipe == 0)
                        break;
-               dbg ("%s TEST 1:  write %d bytes %u times", dev->id,
+               dev_dbg (&intf->dev,
+                               "TEST 1:  write %d bytes %u times\n",
                                param->length, param->iterations);
                urb = simple_alloc_urb (udev, dev->out_pipe, param->length);
                if (!urb) {
@@ -1083,13 +1549,14 @@
                        break;
                }
                // FIRMWARE:  bulk sink (maybe accepts short writes)
-               retval = simple_io (urb, param->iterations, 0);
+               retval = simple_io (urb, param->iterations, 0, 0, "test1");
                simple_free_urb (urb);
                break;
        case 2:
                if (dev->in_pipe == 0)
                        break;
-               dbg ("%s TEST 2:  read %d bytes %u times", dev->id,
+               dev_dbg (&intf->dev,
+                               "TEST 2:  read %d bytes %u times\n",
                                param->length, param->iterations);
                urb = simple_alloc_urb (udev, dev->in_pipe, param->length);
                if (!urb) {
@@ -1097,13 +1564,14 @@
                        break;
                }
                // FIRMWARE:  bulk source (maybe generates short writes)
-               retval = simple_io (urb, param->iterations, 0);
+               retval = simple_io (urb, param->iterations, 0, 0, "test2");
                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,
+               dev_dbg (&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);
                if (!urb) {
@@ -1111,13 +1579,15 @@
                        break;
                }
                // FIRMWARE:  bulk sink (maybe accepts short writes)
-               retval = simple_io (urb, param->iterations, param->vary);
+               retval = simple_io (urb, param->iterations, param->vary,
+                                       0, "test3");
                simple_free_urb (urb);
                break;
        case 4:
                if (dev->in_pipe == 0 || param->vary == 0)
                        break;
-               dbg ("%s TEST 4:  read/%d 0..%d bytes %u times", dev->id,
+               dev_dbg (&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);
                if (!urb) {
@@ -1125,7 +1595,8 @@
                        break;
                }
                // FIRMWARE:  bulk source (maybe generates short writes)
-               retval = simple_io (urb, param->iterations, param->vary);
+               retval = simple_io (urb, param->iterations, param->vary,
+                                       0, "test4");
                simple_free_urb (urb);
                break;
 
@@ -1133,8 +1604,9 @@
        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,
+               dev_dbg (&intf->dev,
+                       "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);
                if (!sg) {
@@ -1150,8 +1622,9 @@
        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,
+               dev_dbg (&intf->dev,
+                       "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);
                if (!sg) {
@@ -1166,8 +1639,9 @@
        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,
+               dev_dbg (&intf->dev,
+                       "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);
                if (!sg) {
@@ -1182,8 +1656,9 @@
        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,
+               dev_dbg (&intf->dev,
+                       "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);
                if (!sg) {
@@ -1199,8 +1674,9 @@
        /* non-queued sanity tests for control (chapter 9 subset) */
        case 9:
                retval = 0;
-               dbg ("%s TEST 9:  ch9 (subset) control tests, %d times",
-                               dev->id, param->iterations);
+               dev_dbg (&intf->dev,
+                       "TEST 9:  ch9 (subset) control tests, %d times\n",
+                               param->iterations);
                for (i = param->iterations; retval == 0 && i--; /* NOP */)
                        retval = ch9_postconfig (dev);
                if (retval)
@@ -1212,8 +1688,9 @@
                if (param->sglen == 0)
                        break;
                retval = 0;
-               dbg ("%s TEST 10:  queue %d control calls, %d times",
-                               dev->id, param->sglen,
+               dev_dbg (&intf->dev,
+                               "TEST 10:  queue %d control calls, %d times\n",
+                               param->sglen,
                                param->iterations);
                retval = test_ctrl_queue (dev, param);
                break;
@@ -1223,10 +1700,11 @@
                if (dev->in_pipe == 0 || !param->length)
                        break;
                retval = 0;
-               dbg ("%s TEST 11:  unlink %d reads of %d",
-                               dev->id, param->iterations, param->length);
+               dev_dbg (&intf->dev, "TEST 11:  unlink %d reads of %d\n",
+                               param->iterations, param->length);
                for (i = param->iterations; retval == 0 && i--; /* NOP */)
-                       retval = unlink_simple (dev, dev->in_pipe, param->length);
+                       retval = unlink_simple (dev, dev->in_pipe,
+                                               param->length);
                if (retval)
                        dbg ("unlink reads failed, iterations left %d", i);
                break;
@@ -1234,14 +1712,65 @@
                if (dev->out_pipe == 0 || !param->length)
                        break;
                retval = 0;
-               dbg ("%s TEST 12:  unlink %d writes of %d",
-                               dev->id, param->iterations, param->length);
+               dev_dbg (&intf->dev, "TEST 12:  unlink %d writes of %d\n",
+                               param->iterations, param->length);
                for (i = param->iterations; retval == 0 && i--; /* NOP */)
-                       retval = unlink_simple (dev, dev->out_pipe, param->length);
+                       retval = unlink_simple (dev, dev->out_pipe,
+                                               param->length);
                if (retval)
                        dbg ("unlink writes failed, iterations left %d", i);
                break;
 
+       /* ep halt tests */
+       case 13:
+               if (dev->out_pipe == 0 && dev->in_pipe == 0)
+                       break;
+               retval = 0;
+               dev_dbg (&intf->dev, "TEST 13:  set/clear %d halts\n",
+                               param->iterations);
+               for (i = param->iterations; retval == 0 && i--; /* NOP */)
+                       retval = halt_simple (dev);
+               
+               if (retval)
+                       DBG (dev, "halts failed, iterations left %d\n", i);
+               break;
+
+       /* control write tests */
+       case 14:
+               if (!dev->info->ctrl_out)
+                       break;
+               dev_dbg (&intf->dev, "TEST 14:  %d ep0out, 0..%d vary %d\n",
+                               param->iterations, param->length, param->vary);
+               retval = ctrl_out (dev, param->iterations, 
+                               param->length, param->vary);
+               break;
+
+       /* iso write tests */
+       case 15:
+               if (dev->out_iso_pipe == 0 || param->sglen == 0)
+                       break;
+               dev_dbg (&intf->dev, 
+                       "TEST 15:  write %d iso, %d entries of %d bytes\n",
+                               param->iterations,
+                               param->sglen, param->length);
+               // FIRMWARE:  iso sink
+               retval = test_iso_queue (dev, param,
+                               dev->out_iso_pipe, dev->iso_out);
+               break;
+
+       /* iso read tests */
+       case 16:
+               if (dev->in_iso_pipe == 0 || param->sglen == 0)
+                       break;
+               dev_dbg (&intf->dev,
+                       "TEST 16:  read %d iso, %d entries of %d bytes\n",
+                               param->iterations,
+                               param->sglen, param->length);
+               // FIRMWARE:  iso source
+               retval = test_iso_queue (dev, param,
+                               dev->in_iso_pipe, dev->iso_in);
+               break;
+
        // FIXME unlink from queue (ring with N urbs)
 
        // FIXME scatterlist cancel (needs helper thread)
@@ -1262,7 +1791,7 @@
 
 static int force_interrupt = 0;
 MODULE_PARM (force_interrupt, "i");
-MODULE_PARM_DESC (force_interrupt, "0 = test bulk (default), else interrupt");
+MODULE_PARM_DESC (force_interrupt, "0 = test default; else interrupt");
 
 #ifdef GENERIC
 static int vendor;
@@ -1281,6 +1810,7 @@
        struct usbtest_dev      *dev;
        struct usbtest_info     *info;
        char                    *rtest, *wtest;
+       char                    *irtest, *iwtest;
 
        udev = interface_to_usbdev (intf);
 
@@ -1306,10 +1836,6 @@
        dev->info = info;
        init_MUTEX (&dev->sem);
 
-       /* use the same kind of id the hid driver shows */
-       snprintf (dev->id, sizeof dev->id, "%s-%s:%d",
-                       udev->bus->bus_name, udev->devpath,
-                       intf->altsetting [0].desc.bInterfaceNumber);
        dev->intf = intf;
 
        /* cacheline-aligned scratch for i/o */
@@ -1323,6 +1849,7 @@
         * "high bandwidth" modes (up to 3 packets/uframe).
         */
        rtest = wtest = "";
+       irtest = iwtest = "";
        if (force_interrupt || udev->speed == USB_SPEED_LOW) {
                if (info->ep_in) {
                        dev->in_pipe = usb_rcvintpipe (udev, info->ep_in);
@@ -1341,6 +1868,7 @@
                                dbg ("couldn't get endpoints, %d\n", status);
                                return status;
                        }
+                       /* may find bulk or ISO pipes */
                } else {
                        if (info->ep_in)
                                dev->in_pipe = usb_rcvbulkpipe (udev,
@@ -1353,18 +1881,26 @@
                        rtest = " bulk-in";
                if (dev->out_pipe)
                        wtest = " bulk-out";
+               if (dev->in_iso_pipe)
+                       irtest = " iso-in";
+               if (dev->out_iso_pipe)
+                       iwtest = " iso-out";
        }
 
        usb_set_intfdata (intf, dev);
-       info ("%s at %s ... %s speed {control%s%s} tests",
-                       info->name, dev->id,
+       dev_info (&intf->dev, "%s\n", info->name);
+       dev_info (&intf->dev, "%s speed {control%s%s%s%s%s} tests%s\n",
                        ({ char *tmp;
                        switch (udev->speed) {
                        case USB_SPEED_LOW: tmp = "low"; break;
                        case USB_SPEED_FULL: tmp = "full"; break;
                        case USB_SPEED_HIGH: tmp = "high"; break;
                        default: tmp = "unknown"; break;
-                       }; tmp; }), rtest, wtest);
+                       }; tmp; }),
+                       info->ctrl_out ? " in/out" : "",
+                       rtest, wtest,
+                       irtest, iwtest,
+                       info->alt >= 0 ? " (+alt)" : "");
        return 0;
 }
 
@@ -1375,7 +1911,7 @@
        down (&dev->sem);
 
        usb_set_intfdata (intf, NULL);
-       info ("unbound %s", dev->id);
+       dev_dbg (&intf->dev, "disconnect\n");
        kfree (dev);
 }
 
@@ -1423,6 +1959,7 @@
 static struct usbtest_info gz_info = {
        .name           = "Linux gadget zero",
        .autoconf       = 1,
+       .ctrl_out       = 1,
        .alt            = 0,
 };
 
@@ -1432,6 +1969,13 @@
        .alt            = -1,
 };
 
+static struct usbtest_info um2_info = {
+       .name           = "Linux user mode ISO test driver",
+       .autoconf       = 1,
+       .iso            = 1,
+       .alt            = -1,
+};
+
 #ifdef IBOT2
 /* this is a nice source of high speed bulk data;
  * uses an FX2, with firmware provided in the device
@@ -1500,6 +2044,11 @@
        /* so does a user-mode variant */
        { USB_DEVICE (0x0525, 0xa4a4),
                .driver_info = (unsigned long) &um_info,
+               },
+
+       /* ... and a user-mode variant that talks iso */
+       { USB_DEVICE (0x0525, 0xa4a3),
+               .driver_info = (unsigned long) &um2_info,
                },
 
 #ifdef KEYSPAN_19Qi



-------------------------------------------------------
The SF.Net email is sponsored by EclipseCon 2004
Premiere Conference on Open Tools Development and Integration
See the breadth of Eclipse activity. February 3-5 in Anaheim, CA.
http://www.eclipsecon.org/osdn
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to