Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/usb/misc/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 78 kB image not shown  

Quelle  usbtest.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
#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 <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? */

/* Parameter for usbtest driver. */
struct usbtest_param_32 {
 /* inputs */
 __u32  test_num; /* 0..(TEST_CASES-1) */
 __u32  iterations;
 __u32  length;
 __u32  vary;
 __u32  sglen;

 /* outputs */
 __s32  duration_sec;
 __s32  duration_usec;
};

/*
 * 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)

/*-------------------------------------------------------------------------*/

#define GENERIC  /* let probe() bind using module params */

/* Some devices that can be used for testing will have "real" drivers.
 * Entries for those need to be enabled here by hand, after disabling
 * that "real" driver.
 */

//#define IBOT2 /* grab iBOT2 webcams */
//#define KEYSPAN_19Qi /* grab un-renumerated serial adapter */

/*-------------------------------------------------------------------------*/

struct usbtest_info {
 const char  *name;
 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 */
 unsigned  intr:1;  /* try interrupt in/out */
 int   alt;
};

/* this is accessed only through usbfs ioctl calls.
 * one ioctl to issue a test ... one lock per device.
 * tests create other threads if they need them.
 * urbs and buffers are allocated dynamically,
 * and data generated deterministically.
 */

struct usbtest_dev {
 struct usb_interface *intf;
 struct usbtest_info *info;
 int   in_pipe;
 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
 u8   *buf;
};

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 */

#define ERROR(tdev, fmt, args...) \
 dev_err(&(tdev)->intf->dev , fmt , ## args)
#define WARNING(tdev, fmt, args...) \
 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)
{
 int    tmp;
 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++) {
  unsigned ep;

  in = out = NULL;
  iso_in = iso_out = NULL;
  int_in = int_out = NULL;
  alt = intf->altsetting + tmp;

  if (override_alt >= 0 &&
    override_alt != alt->desc.bAlternateSetting)
   continue;

  /* take the first altsetting with in-bulk + out-bulk;
 * ignore other endpoints and altsettings.
 */

  for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
   struct usb_host_endpoint *e;
   int edi;

   e = alt->endpoint + ep;
   edi = usb_endpoint_dir_in(&e->desc);

   switch (usb_endpoint_type(&e->desc)) {
   case USB_ENDPOINT_XFER_BULK:
    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)
     endpoint_update(edi, &iso_in, &iso_out, e);
    fallthrough;
   default:
    continue;
   }
  }
  if ((in && out)  ||  iso_in || iso_out || int_in || int_out)
   goto found;
 }
 return -EINVAL;

found:
 udev = testdev_to_usbdev(dev);
 dev->info->alt = alt->desc.bAlternateSetting;
 if (alt->desc.bAlternateSetting != 0) {
  tmp = usb_set_interface(udev,
    alt->desc.bInterfaceNumber,
    alt->desc.bAlternateSetting);
  if (tmp < 0)
   return tmp;
 }

 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,
    iso_in->desc.bEndpointAddress
     & USB_ENDPOINT_NUMBER_MASK);
 }

 if (iso_out) {
  dev->iso_out = &iso_out->desc;
  dev->out_iso_pipe = usb_sndisocpipe(udev,
    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;
}

/*-------------------------------------------------------------------------*/

/* 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(urb->context);
}

static struct urb *usbtest_alloc_urb(
 struct usb_device *udev,
 int   pipe,
 unsigned long  bytes,
 unsigned  transfer_flags,
 unsigned  offset,
 u8   bInterval,
 usb_complete_t  complete_fn)
{
 struct urb  *urb;

 urb = usb_alloc_urb(0, GFP_KERNEL);
 if (!urb)
  return urb;

 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;
 urb->transfer_flags = transfer_flags;
 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);
 else
  urb->transfer_buffer = kmalloc(bytes + offset, GFP_KERNEL);

 if (!urb->transfer_buffer) {
  usb_free_urb(urb);
  return NULL;
 }

 /* To test unaligned transfers add an offset and fill the
unused memory with a guard value */

 if (offset) {
  memset(urb->transfer_buffer, GUARD_BYTE, offset);
  urb->transfer_buffer += offset;
  if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
   urb->transfer_dma += offset;
 }

 /* For inbound transfers use guard byte so that test fails if
data not correctly copied */

 memset(urb->transfer_buffer,
   usb_pipein(urb->pipe) ? GUARD_BYTE : 0,
   bytes);
 return urb;
}

static struct urb *simple_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, 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;
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 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;
 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 % maxpacket) % 63);
  break;
 }
}

static inline unsigned long buffer_offset(void *buf)
{
 return (unsigned long)buf & (ARCH_KMALLOC_MINALIGN - 1);
}

static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb)
{
 u8 *buf = urb->transfer_buffer;
 u8 *guard = buf - buffer_offset(buf);
 unsigned i;

 for (i = 0; guard < buf; i++, guard++) {
  if (*guard != GUARD_BYTE) {
   ERROR(tdev, "guard byte[%d] %d (not %d)\n",
    i, *guard, GUARD_BYTE);
   return -EINVAL;
  }
 }
 return 0;
}

static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
{
 unsigned i;
 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)
  return ret;

 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 % maxpacket) % 63;
   break;
  /* always fail unsupported patterns */
  default:
   expected = !*buf;
   break;
  }
  if (*buf == expected)
   continue;
  ERROR(tdev, "buf[%d] = %d (not %d)\n", i, *buf, expected);
  return -EINVAL;
 }
 return 0;
}

static void simple_free_urb(struct urb *urb)
{
 unsigned long offset = buffer_offset(urb->transfer_buffer);

 if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
  usb_free_coherent(
   urb->dev,
   urb->transfer_buffer_length + offset,
   urb->transfer_buffer - offset,
   urb->transfer_dma - offset);
 else
  kfree(urb->transfer_buffer - offset);
 usb_free_urb(urb);
}

static int simple_io(
 struct usbtest_dev *tdev,
 struct urb  *urb,
 int   iterations,
 int   vary,
 int   expected,
 const char  *label
)
{
 struct usb_device *udev = urb->dev;
 int   max = urb->transfer_buffer_length;
 struct completion completion;
 int   retval = 0;
 unsigned long  expire;

 urb->context = &completion;
 while (retval == 0 && iterations-- > 0) {
  init_completion(&completion);
  if (usb_pipeout(urb->pipe)) {
   simple_fill_buf(urb);
   urb->transfer_flags |= URB_ZERO_PACKET;
  }
  retval = usb_submit_urb(urb, GFP_KERNEL);
  if (retval != 0)
   break;

  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);

  if (vary) {
   int len = urb->transfer_buffer_length;

   len += vary;
   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;

 if (expected != retval)
  dev_err(&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.
 * 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_page(&sg[i]))
   continue;
  kfree(sg_virt(&sg[i]));
 }
 kfree(sg);
}

static struct scatterlist *
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);
 if (!sg)
  return NULL;
 sg_init_table(sg, nents);

 for (i = 0; i < nents; i++) {
  char  *buf;
  unsigned j;

  buf = kzalloc(size, GFP_KERNEL);
  if (!buf) {
   free_sglist(sg, i);
   return NULL;
  }

  /* kmalloc pages are always physically contiguous! */
  sg_set_buf(&sg[i], buf, size);

  switch (pattern) {
  case 0:
   /* already zeroed */
   break;
  case 1:
   for (j = 0; j < size; j++)
    *buf++ = (u8) (((j + n_size) % maxpacket) % 63);
   n_size += size;
   break;
  }

  if (vary) {
   size += vary;
   size %= max;
   if (size == 0)
    size = (vary < max) ? vary : max;
  }
 }

 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,
 int   pipe,
 struct usb_sg_request *req,
 struct scatterlist *sg,
 int   nents
)
{
 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,
    (udev->speed == USB_SPEED_HIGH)
     ? (INTERRUPT_RATE << 3)
     : INTERRUPT_RATE,
    sg, nents, 0, GFP_KERNEL);

  if (retval)
   break;
  mod_timer(&timeout.timer, jiffies +
    msecs_to_jiffies(SIMPLE_IO_TIMEOUT));
  usb_sg_wait(req);
  if (!timer_delete_sync(&timeout.timer))
   retval = -ETIMEDOUT;
  else
   retval = req->status;
  timer_destroy_on_stack(&timeout.timer);

  /* FIXME check resulting data pattern */

  /* 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)
  ERROR(tdev, "perform_sglist failed, "
    "iterations left %d, status %d\n",
    iterations, retval);
 return retval;
}


/*-------------------------------------------------------------------------*/

/* unqueued control message testing
 *
 * there's a nice set of device functional requirements in chapter 9 of the
 * usb 2.0 spec, which we can apply to ANY device, even ones that don't use
 * special test firmware.
 *
 * we know the device is configured (or suspended) by the time it's visible
 * through usbfs.  we can't change that, so we won't test enumeration (which
 * worked 'well enough' to get here, this time), power management (ditto),
 * or remote wakeup (which needs human interaction).
 */


static unsigned realworld = 1;
module_param(realworld, uint, 0);
MODULE_PARM_DESC(realworld, "clear to demand stricter spec compliance");

static int get_altsetting(struct usbtest_dev *dev)
{
 struct usb_interface *iface = dev->intf;
 struct usb_device *udev = interface_to_usbdev(iface);
 int   retval;

 retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
   USB_REQ_GET_INTERFACE, USB_DIR_IN|USB_RECIP_INTERFACE,
   0, iface->altsetting[0].desc.bInterfaceNumber,
   dev->buf, 1, USB_CTRL_GET_TIMEOUT);
 switch (retval) {
 case 1:
  return dev->buf[0];
 case 0:
  retval = -ERANGE;
  fallthrough;
 default:
  return retval;
 }
}

static int set_altsetting(struct usbtest_dev *dev, int alternate)
{
 struct usb_interface  *iface = dev->intf;
 struct usb_device  *udev;

 if (alternate < 0 || alternate >= 256)
  return -EINVAL;

 udev = interface_to_usbdev(iface);
 return usb_set_interface(udev,
   iface->altsetting[0].desc.bInterfaceNumber,
   alternate);
}

static int is_good_config(struct usbtest_dev *tdev, int len)
{
 struct usb_config_descriptor *config;

 if (len < (int)sizeof(*config))
  return 0;
 config = (struct usb_config_descriptor *) tdev->buf;

 switch (config->bDescriptorType) {
 case USB_DT_CONFIG:
 case USB_DT_OTHER_SPEED_CONFIG:
  if (config->bLength != 9) {
   ERROR(tdev, "bogus config descriptor length\n");
   return 0;
  }
  /* this bit 'must be 1' but often isn't */
  if (!realworld && !(config->bmAttributes & 0x80)) {
   ERROR(tdev, "high bit of config attributes not set\n");
   return 0;
  }
  if (config->bmAttributes & 0x1f) { /* reserved == 0 */
   ERROR(tdev, "reserved config bits set\n");
   return 0;
  }
  break;
 default:
  return 0;
 }

 if (le16_to_cpu(config->wTotalLength) == len) /* read it all */
  return 1;
 if (le16_to_cpu(config->wTotalLength) >= TBUF_SIZE) /* max partial read */
  return 1;
 ERROR(tdev, "bogus config descriptor read size\n");
 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.
 *
 * this doesn't test how endpoint halts behave or data toggles get set, since
 * we won't do I/O to bulk/interrupt endpoints here (which is how to change
 * halt or toggle).  toggle testing is impractical without support from hcds.
 *
 * this avoids failing devices linux would normally work with, by not testing
 * config/altsetting operations for devices that only support their defaults.
 * such devices rarely support those needless operations.
 *
 * NOTE that since this is a sanity test, it's not examining boundary cases
 * to see if usbcore, hcd, and device all behave right.  such testing would
 * involve varied read sizes and other operation sequences.
 */

static int ch9_postconfig(struct usbtest_dev *dev)
{
 struct usb_interface *iface = dev->intf;
 struct usb_device *udev = interface_to_usbdev(iface);
 int   i, alt, retval;

 /* [9.2.3] if there's more than one altsetting, we need to be able to
 * set and get each one.  mostly trusts the descriptors from usbcore.
 */

 for (i = 0; i < iface->num_altsetting; i++) {

  /* 9.2.3 constrains the range here */
  alt = iface->altsetting[i].desc.bAlternateSetting;
  if (alt < 0 || alt >= iface->num_altsetting) {
   dev_err(&iface->dev,
     "invalid alt [%d].bAltSetting = %d\n",
     i, alt);
  }

  /* [real world] get/set unimplemented if there's only one */
  if (realworld && iface->num_altsetting == 1)
   continue;

  /* [9.4.10] set_interface */
  retval = set_altsetting(dev, alt);
  if (retval) {
   dev_err(&iface->dev, "can't set_interface = %d, %d\n",
     alt, retval);
   return retval;
  }

  /* [9.4.4] get_interface always works */
  retval = get_altsetting(dev);
  if (retval != alt) {
   dev_err(&iface->dev, "get alt should be %d, was %d\n",
     alt, retval);
   return (retval < 0) ? retval : -EDOM;
  }

 }

 /* [real world] get_config unimplemented if there's only one */
 if (!realworld || udev->descriptor.bNumConfigurations != 1) {
  int expected = udev->actconfig->desc.bConfigurationValue;

  /* [9.4.2] get_configuration always works
 * ... although some cheap devices (like one TI Hub I've got)
 * won't return config descriptors except before set_config.
 */

  retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
    USB_REQ_GET_CONFIGURATION,
    USB_DIR_IN | USB_RECIP_DEVICE,
    0, 0, dev->buf, 1, USB_CTRL_GET_TIMEOUT);
  if (retval != 1 || dev->buf[0] != expected) {
   dev_err(&iface->dev, "get config --> %d %d (1 %d)\n",
    retval, dev->buf[0], expected);
   return (retval < 0) ? retval : -EDOM;
  }
 }

 /* 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_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,
    dev->buf, TBUF_SIZE);
  if (!is_good_config(dev, retval)) {
   dev_err(&iface->dev,
     "config [%d] descriptor --> %d\n",
     i, retval);
   return (retval < 0) ? retval : -EDOM;
  }

  /* FIXME cross-checking udev->config[i] to make sure usbcore
 * parsed it right (etc) would be good testing paranoia
 */

 }

 /* and sometimes [9.2.6.6] speed dependent descriptors */
 if (le16_to_cpu(udev->descriptor.bcdUSB) == 0x0200) {
  struct usb_qualifier_descriptor *d = NULL;

  /* device qualifier [9.6.2] */
  retval = usb_get_descriptor(udev,
    USB_DT_DEVICE_QUALIFIER, 0, dev->buf,
    sizeof(struct usb_qualifier_descriptor));
  if (retval == -EPIPE) {
   if (udev->speed == USB_SPEED_HIGH) {
    dev_err(&iface->dev,
      "hs dev qualifier --> %d\n",
      retval);
    return retval;
   }
   /* usb2.0 but not high-speed capable; fine */
  } else if (retval != sizeof(struct usb_qualifier_descriptor)) {
   dev_err(&iface->dev, "dev qualifier --> %d\n", retval);
   return (retval < 0) ? retval : -EDOM;
  } else
   d = (struct usb_qualifier_descriptor *) dev->buf;

  /* might not have [9.6.2] any other-speed configs [9.6.4] */
  if (d) {
   unsigned max = d->bNumConfigurations;
   for (i = 0; i < max; i++) {
    retval = usb_get_descriptor(udev,
     USB_DT_OTHER_SPEED_CONFIG, i,
     dev->buf, TBUF_SIZE);
    if (!is_good_config(dev, retval)) {
     dev_err(&iface->dev,
      "other speed config --> %d\n",
      retval);
     return (retval < 0) ? retval : -EDOM;
    }
   }
  }
 }
 /* FIXME fetch strings from at least the device descriptor */

 /* [9.4.5] get_status always works */
 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;
 }

 /* 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_std_status(udev, USB_RECIP_INTERFACE,
   iface->altsetting[0].desc.bInterfaceNumber, dev->buf);
 if (retval) {
  dev_err(&iface->dev, "get interface status --> %d\n", retval);
  return retval;
 }
 /* FIXME get status for each endpoint in the interface */

 return 0;
}

/*-------------------------------------------------------------------------*/

/* use ch9 requests to test whether:
 *   (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 not like bulk/intr; no halt clearing.
 *   (c) short control reads are reported and handled.
 *   (d) queues are always processed in-order
 */


struct ctrl_ctx {
 spinlock_t  lock;
 struct usbtest_dev *dev;
 struct completion complete;
 unsigned  count;
 unsigned  pending;
 int   status;
 struct urb  **urb;
 struct usbtest_param_32 *param;
 int   last;
};

#define NUM_SUBCASES 16  /* how many test subcases here? */

struct subcase {
 struct usb_ctrlrequest setup;
 int   number;
 int   expected;
};

static void ctrl_complete(struct urb *urb)
{
 struct ctrl_ctx  *ctx = urb->context;
 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_irqsave(&ctx->lock, flags);
 ctx->count--;
 ctx->pending--;

 /* queue must transfer and complete in fifo order, unless
 * usb_unlink_urb() is used to unlink something not at the
 * physical queue head (not tested).
 */

 if (subcase->number > 0) {
  if ((subcase->number - ctx->last) != 1) {
   ERROR(ctx->dev,
    "subcase %d completed out of order, last %d\n",
    subcase->number, ctx->last);
   status = -EDOM;
   ctx->last = subcase->number;
   goto error;
  }
 }
 ctx->last = subcase->number;

 /* succeed or fault in only one way? */
 if (status == subcase->expected)
  status = 0;

 /* async unlink for cleanup? */
 else if (status != -ECONNRESET) {

  /* some faults are allowed, not required */
  if (subcase->expected > 0 && (
     ((status == -subcase->expected /* happened */
      || status == 0))))   /* didn't */
   status = 0;
  /* sometimes more than one fault is allowed */
  else if (subcase->number == 12 && status == -EPIPE)
   status = 0;
  else
   ERROR(ctx->dev, "subtest %d error, status %d\n",
     subcase->number, status);
 }

 /* unexpected status codes mean errors; ideally, in hardware */
 if (status) {
error:
  if (ctx->status == 0) {
   int  i;

   ctx->status = status;
   ERROR(ctx->dev, "control queue %02x.%02x, err %d, "
     "%d left, subcase %d, len %d/%d\n",
     reqp->bRequestType, reqp->bRequest,
     status, ctx->count, subcase->number,
     urb->actual_length,
     urb->transfer_buffer_length);

   /* FIXME this "unlink everything" exit route should
 * be a separate test case.
 */


   /* unlink whatever's still pending */
   for (i = 1; i < ctx->param->sglen; i++) {
    struct urb *u = ctx->urb[
       (i + subcase->number)
       % ctx->param->sglen];

    if (u == urb || !u->dev)
     continue;
    spin_unlock(&ctx->lock);
    status = usb_unlink_urb(u);
    spin_lock(&ctx->lock);
    switch (status) {
    case -EINPROGRESS:
    case -EBUSY:
    case -EIDRM:
     continue;
    default:
     ERROR(ctx->dev, "urb unlink --> %d\n",
       status);
    }
   }
   status = ctx->status;
  }
 }

 /* resubmit if we need to, else mark this as done */
 if ((status == 0) && (ctx->pending < ctx->count)) {
  status = usb_submit_urb(urb, GFP_ATOMIC);
  if (status != 0) {
   ERROR(ctx->dev,
    "can't resubmit ctrl %02x.%02x, err %d\n",
    reqp->bRequestType, reqp->bRequest, status);
   urb->dev = NULL;
  } else
   ctx->pending++;
 } else
  urb->dev = NULL;

 /* signal completion when nothing's queued */
 if (ctx->pending == 0)
  complete(&ctx->complete);
 spin_unlock_irqrestore(&ctx->lock, flags);
}

static int
test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param)
{
 struct usb_device *udev = testdev_to_usbdev(dev);
 struct urb  **urb;
 struct ctrl_ctx  context;
 int   i;

 if (param->sglen == 0 || param->iterations > UINT_MAX / param->sglen)
  return -EOPNOTSUPP;

 spin_lock_init(&context.lock);
 context.dev = dev;
 init_completion(&context.complete);
 context.count = param->sglen * param->iterations;
 context.pending = 0;
 context.status = -ENOMEM;
 context.param = param;
 context.last = -1;

 /* allocate and init the urbs we'll queue.
 * as with bulk/intr sglists, sglen is the queue depth; it also
 * controls which subtests run (more tests than sglen) or rerun.
 */

 urb = kcalloc(param->sglen, sizeof(struct urb *), GFP_KERNEL);
 if (!urb)
  return -ENOMEM;
 for (i = 0; i < param->sglen; i++) {
  int   pipe = usb_rcvctrlpipe(udev, 0);
  unsigned  len;
  struct urb  *u;
  struct usb_ctrlrequest req;
  struct subcase  *reqp;

  /* sign of this variable means:
 *  -: tested code must return this (negative) error code
 *  +: tested code may return this (negative too) error code
 */

  int   expected = 0;

  /* requests here are mostly expected to succeed on any
 * device, but some are chosen to trigger protocol stalls
 * or short reads.
 */

  memset(&req, 0, sizeof(req));
  req.bRequest = USB_REQ_GET_DESCRIPTOR;
  req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE;

  switch (i % NUM_SUBCASES) {
  case 0:  /* get device descriptor */
   req.wValue = cpu_to_le16(USB_DT_DEVICE << 8);
   len = sizeof(struct usb_device_descriptor);
   break;
  case 1:  /* get first config descriptor (only) */
   req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
   len = sizeof(struct usb_config_descriptor);
   break;
  case 2:  /* get altsetting (OFTEN STALLS) */
   req.bRequest = USB_REQ_GET_INTERFACE;
   req.bRequestType = USB_DIR_IN|USB_RECIP_INTERFACE;
   /* index = 0 means first interface */
   len = 1;
   expected = EPIPE;
   break;
  case 3:  /* get interface status */
   req.bRequest = USB_REQ_GET_STATUS;
   req.bRequestType = USB_DIR_IN|USB_RECIP_INTERFACE;
   /* interface 0 */
   len = 2;
   break;
  case 4:  /* get device status */
   req.bRequest = USB_REQ_GET_STATUS;
   req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE;
   len = 2;
   break;
  case 5:  /* get device qualifier (MAY STALL) */
   req.wValue = cpu_to_le16 (USB_DT_DEVICE_QUALIFIER << 8);
   len = sizeof(struct usb_qualifier_descriptor);
   if (udev->speed != USB_SPEED_HIGH)
    expected = EPIPE;
   break;
  case 6:  /* get first config descriptor, plus interface */
   req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
   len = sizeof(struct usb_config_descriptor);
   len += sizeof(struct usb_interface_descriptor);
   break;
  case 7:  /* get interface descriptor (ALWAYS STALLS) */
   req.wValue = cpu_to_le16 (USB_DT_INTERFACE << 8);
   /* interface == 0 */
   len = sizeof(struct usb_interface_descriptor);
   expected = -EPIPE;
   break;
  /* NOTE: two consecutive stalls in the queue here.
 *  that tests fault recovery a bit more aggressively. */

  case 8:  /* clear endpoint halt (MAY STALL) */
   req.bRequest = USB_REQ_CLEAR_FEATURE;
   req.bRequestType = USB_RECIP_ENDPOINT;
   /* wValue 0 == ep halt */
   /* wIndex 0 == ep0 (shouldn't halt!) */
   len = 0;
   pipe = usb_sndctrlpipe(udev, 0);
   expected = EPIPE;
   break;
  case 9:  /* get endpoint status */
   req.bRequest = USB_REQ_GET_STATUS;
   req.bRequestType = USB_DIR_IN|USB_RECIP_ENDPOINT;
   /* endpoint 0 */
   len = 2;
   break;
  case 10: /* trigger short read (EREMOTEIO) */
   req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
   len = 1024;
   expected = -EREMOTEIO;
   break;
  /* NOTE: two consecutive _different_ faults in the queue. */
  case 11: /* get endpoint descriptor (ALWAYS STALLS) */
   req.wValue = cpu_to_le16(USB_DT_ENDPOINT << 8);
   /* endpoint == 0 */
   len = sizeof(struct usb_interface_descriptor);
   expected = EPIPE;
   break;
  /* NOTE: sometimes even a third fault in the queue! */
  case 12: /* get string 0 descriptor (MAY STALL) */
   req.wValue = cpu_to_le16(USB_DT_STRING << 8);
   /* string == 0, for language IDs */
   len = sizeof(struct usb_interface_descriptor);
   /* may succeed when > 4 languages */
   expected = EREMOTEIO; /* or EPIPE, if no strings */
   break;
  case 13: /* short read, resembling case 10 */
   req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
   /* last data packet "should" be DATA1, not DATA0 */
   if (udev->speed == USB_SPEED_SUPER)
    len = 1024 - 512;
   else
    len = 1024 - udev->descriptor.bMaxPacketSize0;
   expected = -EREMOTEIO;
   break;
  case 14: /* short read; try to fill the last packet */
   req.wValue = cpu_to_le16((USB_DT_DEVICE << 8) | 0);
   /* device descriptor size == 18 bytes */
   len = udev->descriptor.bMaxPacketSize0;
   if (udev->speed == USB_SPEED_SUPER)
    len = 512;
   switch (len) {
   case 8:
    len = 24;
    break;
   case 16:
    len = 32;
    break;
   }
   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, 0);
  if (!u)
   goto cleanup;

  reqp = kmalloc(sizeof(*reqp), GFP_KERNEL);
  if (!reqp)
   goto cleanup;
  reqp->setup = req;
  reqp->number = i % NUM_SUBCASES;
  reqp->expected = expected;
  u->setup_packet = (char *) &reqp->setup;

  u->context = &context;
  u->complete = ctrl_complete;
 }

 /* queue the urbs */
 context.urb = urb;
 spin_lock_irq(&context.lock);
 for (i = 0; i < param->sglen; i++) {
  context.status = usb_submit_urb(urb[i], GFP_ATOMIC);
  if (context.status != 0) {
   ERROR(dev, "can't submit urb[%d], status %d\n",
     i, context.status);
   context.count = context.pending;
   break;
  }
  context.pending++;
 }
 spin_unlock_irq(&context.lock);

 /* FIXME  set timer and time out; provide a disconnect hook */

 /* wait for the last one to complete */
 if (context.pending > 0)
  wait_for_completion(&context.complete);

cleanup:
 for (i = 0; i < param->sglen; i++) {
  if (!urb[i])
   continue;
  urb[i]->dev = udev;
  kfree(urb[i]->setup_packet);
  simple_free_urb(urb[i]);
 }
 kfree(urb);
 return context.status;
}
#undef NUM_SUBCASES


/*-------------------------------------------------------------------------*/

static void unlink1_callback(struct urb *urb)
{
 int status = urb->status;

 /* we "know" -EPIPE (stall) never happens */
 if (!status)
  status = usb_submit_urb(urb, GFP_ATOMIC);
 if (status) {
  urb->status = status;
  complete(urb->context);
 }
}

static int unlink1(struct usbtest_dev *dev, int pipe, int size, int async)
{
 struct urb  *urb;
 struct completion completion;
 int   retval = 0;

 init_completion(&completion);
 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.
 *
 * FIXME want additional tests for when endpoint is STALLing
 * due to errors, or is just NAKing requests.
 */

 retval = usb_submit_urb(urb, GFP_KERNEL);
 if (retval != 0) {
  dev_err(&dev->intf->dev, "submit fail %d\n", retval);
  return retval;
 }

 /* unlinking that should always work.  variable delay tests more
 * hcd states and code paths, even with little other system load.
 */

 msleep(jiffies % (2 * INTERRUPT_RATE));
 if (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:
    /* we can't unlink urbs while they're completing
 * or if they've completed, and we haven't
 * resubmitted. "normal" drivers would prevent
 * resubmission, but since we're testing unlink
 * paths, we can't.
 */

    ERROR(dev, "unlink retry\n");
    continue;
   case 0:
   case -EINPROGRESS:
    break;

   default:
    dev_err(&dev->intf->dev,
     "unlink fail %d\n", retval);
    return retval;
   }

   break;
  }
 } else
  usb_kill_urb(urb);

 wait_for_completion(&completion);
 retval = urb->status;
 simple_free_urb(urb);

 if (async)
  return (retval == -ECONNRESET) ? 0 : retval - 1000;
 else
  return (retval == -ENOENT || retval == -EPERM) ?
    0 : retval - 2000;
}

static int unlink_simple(struct usbtest_dev *dev, int pipe, int len)
{
 int   retval = 0;

 /* test sync and async paths */
 retval = unlink1(dev, pipe, len, 1);
 if (!retval)
  retval = unlink1(dev, pipe, len, 0);
 return retval;
}

/*-------------------------------------------------------------------------*/

struct queued_ctx {
 struct completion complete;
 atomic_t  pending;
 unsigned  num;
 int   status;
 struct urb  **urbs;
};

static void unlink_queued_callback(struct urb *urb)
{
 int   status = urb->status;
 struct queued_ctx *ctx = urb->context;

 if (ctx->status)
  goto done;
 if (urb == ctx->urbs[ctx->num - 4] || urb == ctx->urbs[ctx->num - 2]) {
  if (status == -ECONNRESET)
   goto done;
  /* What error should we report if the URB completed normally? */
 }
 if (status != 0)
  ctx->status = status;

 done:
 if (atomic_dec_and_test(&ctx->pending))
  complete(&ctx->complete);
}

static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num,
  unsigned size)
{
 struct queued_ctx ctx;
 struct usb_device *udev = testdev_to_usbdev(dev);
 void   *buf;
 dma_addr_t  buf_dma;
 int   i;
 int   retval = -ENOMEM;

 init_completion(&ctx.complete);
 atomic_set(&ctx.pending, 1); /* One more than the actual value */
 ctx.num = num;
 ctx.status = 0;

 buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &buf_dma);
 if (!buf)
  return retval;
 memset(buf, 0, size);

 /* Allocate and init the urbs we'll queue */
 ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL);
 if (!ctx.urbs)
  goto free_buf;
 for (i = 0; i < num; i++) {
  ctx.urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
  if (!ctx.urbs[i])
   goto free_urbs;
  usb_fill_bulk_urb(ctx.urbs[i], udev, pipe, buf, size,
    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. */
 for (i = 0; i < num; i++) {
  atomic_inc(&ctx.pending);
  retval = usb_submit_urb(ctx.urbs[i], GFP_KERNEL);
  if (retval != 0) {
   dev_err(&dev->intf->dev, "submit urbs[%d] fail %d\n",
     i, retval);
   atomic_dec(&ctx.pending);
   ctx.status = retval;
   break;
  }
 }
 if (i == num) {
  usb_unlink_urb(ctx.urbs[num - 4]);
  usb_unlink_urb(ctx.urbs[num - 2]);
 } else {
  while (--i >= 0)
   usb_unlink_urb(ctx.urbs[i]);
 }

 if (atomic_dec_and_test(&ctx.pending))  /* The extra count */
  complete(&ctx.complete);
 wait_for_completion(&ctx.complete);
 retval = ctx.status;

 free_urbs:
 for (i = 0; i < num; i++)
  usb_free_urb(ctx.urbs[i]);
 kfree(ctx.urbs);
 free_buf:
 usb_free_coherent(udev, size, buf, buf_dma);
 return retval;
}

/*-------------------------------------------------------------------------*/

static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
{
 int retval;
 u16 status;

 /* shouldn't look or act halted */
 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);
  return retval;
 }
 if (status != 0) {
  ERROR(tdev, "ep %02x bogus status: %04x != 0\n", ep, status);
  return -EINVAL;
 }
 retval = simple_io(tdev, urb, 1, 0, 0, __func__);
 if (retval != 0)
  return -EINVAL;
 return 0;
}

static int verify_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
{
 int retval;
 u16 status;

 /* should look and act halted */
 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;
 }
 if (status != 1) {
  ERROR(tdev, "ep %02x bogus status: %04x != 1\n", ep, status);
  return -EINVAL;
 }
 retval = simple_io(tdev, urb, 1, 0, -EPIPE, __func__);
 if (retval != -EPIPE)
  return -EINVAL;
 retval = simple_io(tdev, urb, 1, 0, -EPIPE, "verify_still_halted");
 if (retval != -EPIPE)
  return -EINVAL;
 return 0;
}

static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb)
{
 int retval;

 /* shouldn't look or act halted now */
 retval = verify_not_halted(tdev, 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,
   USB_ENDPOINT_HALT, ep,
   NULL, 0, USB_CTRL_SET_TIMEOUT);
 if (retval < 0) {
  ERROR(tdev, "ep %02x couldn't set halt, %d\n", ep, retval);
  return retval;
 }
 retval = verify_halted(tdev, ep, urb);
 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);
 if (retval < 0) {
  ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval);
  return retval;
 }
 retval = verify_not_halted(tdev, ep, urb);
 if (retval < 0)
  return retval;

 /* NOTE:  could also verify SET_INTERFACE clear halts ... */

 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;
 int   retval = 0;
 struct urb  *urb;
 struct usb_device *udev = testdev_to_usbdev(dev);

 if (udev->speed == USB_SPEED_SUPER)
  urb = simple_alloc_urb(udev, 0, 1024, 0);
 else
  urb = simple_alloc_urb(udev, 0, 512, 0);
 if (urb == NULL)
  return -ENOMEM;

 if (dev->in_pipe) {
  ep = usb_pipeendpoint(dev->in_pipe) | USB_DIR_IN;
  urb->pipe = dev->in_pipe;
  retval = test_halt(dev, 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(dev, ep, urb);
 }
done:
 simple_free_urb(urb);
 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
 * 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 offset)
{
 unsigned  i, j, len;
 int   retval;
 u8   *buf;
 char   *what = "?";
 struct usb_device *udev;

 if (length < 1 || length > 0xffff || vary >= length)
  return -EINVAL;

 buf = kmalloc(length + offset, GFP_KERNEL);
 if (!buf)
  return -ENOMEM;

 buf += offset;
 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] = (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);
  if (retval != len) {
   what = "write";
   if (retval >= 0) {
    ERROR(dev, "ctrl_out, wlen %d (expected %d)\n",
      retval, len);
    retval = -EBADMSG;
   }
   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, USB_CTRL_GET_TIMEOUT);
  if (retval != len) {
   what = "read";
   if (retval >= 0) {
    ERROR(dev, "ctrl_out, rlen %d (expected %d)\n",
      retval, len);
    retval = -EBADMSG;
   }
   break;
  }

  /* fail if we can't verify */
  for (j = 0; j < len; j++) {
   if (buf[j] != (u8)(i + j)) {
    ERROR(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;

  /* [real world] the "zero bytes IN" case isn't really used.
 * hardware can easily trip up in this weird case, since its
 * status stage is IN, not OUT like other ep0in transfers.
 */

  if (len > length)
   len = realworld ? 1 : 0;
 }

 if (retval < 0)
  ERROR(dev, "ctrl_out %s failed, code %d, count %d\n",
   what, retval, i);

 kfree(buf - offset);
 return retval;
}

/*-------------------------------------------------------------------------*/

/* ISO/BULK tests ... mimics common usage
 *  - buffer length is split into N packets (mostly maxpacket sized)
 *  - multi-buffers according to sglen
 */


struct transfer_context {
 unsigned  count;
 unsigned  pending;
 spinlock_t  lock;
 struct completion done;
 int   submit_error;
 unsigned long  errors;
 unsigned long  packet_count;
 struct usbtest_dev *dev;
 bool   is_iso;
};

static void complicated_callback(struct urb *urb)
{
 struct transfer_context *ctx = urb->context;
 unsigned long flags;

 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 += (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)
  ctx->errors++;

 if (urb->status == 0 && ctx->count > (ctx->pending - 1)
   && !ctx->submit_error) {
  int status = usb_submit_urb(urb, GFP_ATOMIC);
  switch (status) {
  case 0:
   goto done;
  default:
   dev_err(&ctx->dev->intf->dev,
     "resubmit err %d\n",
     status);
   fallthrough;
  case -ENODEV:   /* disconnected */
  case -ESHUTDOWN:  /* endpoint disabled */
   ctx->submit_error = 1;
   break;
  }
 }

 ctx->pending--;
 if (ctx->pending == 0) {
  if (ctx->errors)
   dev_err(&ctx->dev->intf->dev,
    "during the test, %lu errors out of %lu\n",
    ctx->errors, ctx->packet_count);
  complete(&ctx->done);
 }
done:
 spin_unlock_irqrestore(&ctx->lock, flags);
}

static struct urb *iso_alloc_urb(
 struct usb_device *udev,
 int   pipe,
 struct usb_endpoint_descriptor *desc,
 long   bytes,
 unsigned offset
)
{
 struct urb  *urb;
 unsigned  i, maxp, packets;

 if (bytes < 0 || !desc)
  return NULL;

 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);
 if (!urb)
  return urb;
 urb->dev = udev;
 urb->pipe = pipe;

 urb->number_of_packets = packets;
 urb->transfer_buffer_length = bytes;
 urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset,
       GFP_KERNEL,
       &urb->transfer_dma);
 if (!urb->transfer_buffer) {
  usb_free_urb(urb);
  return NULL;
 }
 if (offset) {
  memset(urb->transfer_buffer, GUARD_BYTE, offset);
  urb->transfer_buffer += offset;
  urb->transfer_dma += offset;
 }
 /* For inbound transfers use guard byte so that test fails if
data not correctly copied */

 memset(urb->transfer_buffer,
   usb_pipein(urb->pipe) ? GUARD_BYTE : 0,
   bytes);

 for (i = 0; i < packets; i++) {
  /* here, only the last packet will be short */
  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 = complicated_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_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param,
  int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)
{
 struct transfer_context context;
 struct usb_device *udev;
 unsigned  i;
 unsigned long  packets = 0;
 int   status = 0;
 struct urb  **urbs;

 if (!param->sglen || param->iterations > UINT_MAX / param->sglen)
  return -EINVAL;

 if (param->sglen > MAX_SGLEN)
  return -EINVAL;

 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);

 udev = testdev_to_usbdev(dev);

 for (i = 0; i < param->sglen; i++) {
  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;
  }
  packets += urbs[i]->number_of_packets;
  urbs[i]->context = &context;
 }
 packets *= param->iterations;

 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++) {
  ++context.pending;
  status = usb_submit_urb(urbs[i], GFP_ATOMIC);
  if (status < 0) {
   ERROR(dev, "submit iso[%d], error %d\n", i, status);
   if (i == 0) {
    spin_unlock_irq(&context.lock);
    goto fail;
   }

   simple_free_urb(urbs[i]);
   urbs[i] = NULL;
   context.pending--;
   context.submit_error = 1;
   break;
  }
 }
 spin_unlock_irq(&context.lock);

 wait_for_completion(&context.done);

 for (i = 0; i < param->sglen; i++) {
  if (urbs[i])
   simple_free_urb(urbs[i]);
 }
 /*
 * Isochronous transfers are expected to fail sometimes.  As an
 * arbitrary limit, we will report an error if any submissions
 * fail or if the transfer failure rate is > 10%.
 */

 if (status != 0)
  ;
 else if (context.submit_error)
  status = -EACCES;
 else if (context.errors >
   (context.is_iso ? context.packet_count / 10 : 0))
  status = -EIO;

 kfree(urbs);
 return status;

fail:
 for (i = 0; i < param->sglen; i++) {
  if (urbs[i])
   simple_free_urb(urbs[i]);
 }

 kfree(urbs);
 return status;
}

static int test_unaligned_bulk(
 struct usbtest_dev *tdev,
 int pipe,
 unsigned length,
 int iterations,
 unsigned transfer_flags,
 const char *label)
{
 int retval;
 struct urb *urb = usbtest_alloc_urb(testdev_to_usbdev(tdev),
   pipe, length, transfer_flags, 1, 0, simple_callback);

 if (!urb)
  return -ENOMEM;

 retval = simple_io(tdev, urb, iterations, 0, 0, label);
 simple_free_urb(urb);
 return retval;
}

/* Run tests. */
static int
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 urb  *urb;
 struct scatterlist *sg;
 struct usb_sg_request req;
 unsigned  i;
 int retval = -EOPNOTSUPP;

 if (param->iterations <= 0)
  return -EINVAL;
 if (param->sglen > MAX_SGLEN)
  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
 * queueing, concurrent read+write threads, and so on.
 */

 switch (param->test_num) {

 case 0:
  dev_info(&intf->dev, "TEST 0: NOP\n");
  retval = 0;
  break;

 /* Simple non-queued bulk I/O tests */
 case 1:
  if (dev->out_pipe == 0)
   break;
  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, 0);
  if (!urb) {
   retval = -ENOMEM;
   break;
  }
  /* FIRMWARE:  bulk sink (maybe accepts short writes) */
  retval = simple_io(dev, urb, param->iterations, 0, 0, "test1");
  simple_free_urb(urb);
  break;
 case 2:
  if (dev->in_pipe == 0)
   break;
  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, 0);
  if (!urb) {
   retval = -ENOMEM;
   break;
  }
  /* FIRMWARE:  bulk source (maybe generates short writes) */
  retval = simple_io(dev, urb, param->iterations, 0, 0, "test2");
  simple_free_urb(urb);
  break;
 case 3:
  if (dev->out_pipe == 0 || param->vary == 0)
   break;
  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, 0);
  if (!urb) {
   retval = -ENOMEM;
   break;
  }
  /* FIRMWARE:  bulk sink (maybe accepts short writes) */
  retval = simple_io(dev, urb, param->iterations, param->vary,
     0, "test3");
  simple_free_urb(urb);
  break;
 case 4:
  if (dev->in_pipe == 0 || param->vary == 0)
   break;
  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, 0);
  if (!urb) {
   retval = -ENOMEM;
   break;
  }
  /* FIRMWARE:  bulk source (maybe generates short writes) */
  retval = simple_io(dev, urb, param->iterations, param->vary,
     0, "test4");
  simple_free_urb(urb);
  break;

 /* Queued bulk I/O tests */
 case 5:
  if (dev->out_pipe == 0 || param->sglen == 0)
   break;
  dev_info(&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, dev, dev->out_pipe);
  if (!sg) {
   retval = -ENOMEM;
   break;
  }
  /* FIRMWARE:  bulk sink (maybe accepts short writes) */
  retval = perform_sglist(dev, 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;
  dev_info(&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, dev, dev->in_pipe);
  if (!sg) {
   retval = -ENOMEM;
   break;
  }
  /* FIRMWARE:  bulk source (maybe generates short writes) */
  retval = perform_sglist(dev, 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;
  dev_info(&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, dev, dev->out_pipe);
  if (!sg) {
   retval = -ENOMEM;
   break;
  }
  /* FIRMWARE:  bulk sink (maybe accepts short writes) */
  retval = perform_sglist(dev, 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;
  dev_info(&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, dev, dev->in_pipe);
  if (!sg) {
   retval = -ENOMEM;
   break;
  }
  /* FIRMWARE:  bulk source (maybe generates short writes) */
  retval = perform_sglist(dev, param->iterations, dev->in_pipe,
    &req, sg, param->sglen);
  free_sglist(sg, param->sglen);
  break;

 /* non-queued sanity tests for control (chapter 9 subset) */
 case 9:
  retval = 0;
  dev_info(&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)
   dev_err(&intf->dev, "ch9 subset failed, "
     "iterations left %d\n", i);
  break;

 /* queued control messaging */
 case 10:
  retval = 0;
  dev_info(&intf->dev,
    "TEST 10: queue %d control calls, %d times\n",
    param->sglen,
    param->iterations);
  retval = test_ctrl_queue(dev, param);
  break;

 /* simple non-queued unlinks (ring with one urb) */
 case 11:
  if (dev->in_pipe == 0 || !param->length)
   break;
  retval = 0;
  dev_info(&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);
  if (retval)
   dev_err(&intf->dev, "unlink reads failed %d, "
    "iterations left %d\n", retval, i);
  break;
 case 12:
  if (dev->out_pipe == 0 || !param->length)
   break;
  retval = 0;
  dev_info(&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);
  if (retval)
   dev_err(&intf->dev, "unlink writes failed %d, "
    "iterations left %d\n", retval, i);
  break;

 /* ep halt tests */
 case 13:
  if (dev->out_pipe == 0 && dev->in_pipe == 0)
   break;
  retval = 0;
  dev_info(&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)
   ERROR(dev, "halts failed, iterations left %d\n", i);
  break;

 /* control write tests */
 case 14:
  if (!dev->info->ctrl_out)
   break;
  dev_info(&intf->dev, "TEST 14: %d ep0out, %d..%d vary %d\n",
    param->iterations,
    realworld ? 1 : 0, param->length,
    param->vary);
  retval = ctrl_out(dev, param->iterations,
    param->length, param->vary, 0);
  break;

 /* iso write tests */
 case 15:
  if (dev->out_iso_pipe == 0 || param->sglen == 0)
   break;
  dev_info(&intf->dev,
   "TEST 15: write %d iso, %d entries of %d bytes\n",
    param->iterations,
    param->sglen, param->length);
  /* FIRMWARE:  iso sink */
  retval = test_queue(dev, param,
    dev->out_iso_pipe, dev->iso_out, 0);
  break;

 /* iso read tests */
 case 16:
  if (dev->in_iso_pipe == 0 || param->sglen == 0)
   break;
  dev_info(&intf->dev,
   "TEST 16: read %d iso, %d entries of %d bytes\n",
    param->iterations,
    param->sglen, param->length);
  /* FIRMWARE:  iso source */
  retval = test_queue(dev, param,
    dev->in_iso_pipe, dev->iso_in, 0);
  break;

 /* FIXME scatterlist cancel (needs helper thread) */

 /* Tests for bulk I/O using DMA mapping by core and odd address */
 case 17:
  if (dev->out_pipe == 0)
   break;
  dev_info(&intf->dev,
   "TEST 17: write odd addr %d bytes %u times core map\n",
   param->length, param->iterations);

  retval = test_unaligned_bulk(
    dev, dev->out_pipe,
    param->length, param->iterations,
    0, "test17");
  break;

 case 18:
  if (dev->in_pipe == 0)
   break;
  dev_info(&intf->dev,
   "TEST 18: read odd addr %d bytes %u times core map\n",
   param->length, param->iterations);

  retval = test_unaligned_bulk(
    dev, dev->in_pipe,
    param->length, param->iterations,
    0, "test18");
  break;

 /* Tests for bulk I/O using premapped coherent buffer and odd address */
 case 19:
  if (dev->out_pipe == 0)
   break;
  dev_info(&intf->dev,
   "TEST 19: write odd addr %d bytes %u times premapped\n",
   param->length, param->iterations);

  retval = test_unaligned_bulk(
    dev, dev->out_pipe,
    param->length, param->iterations,
    URB_NO_TRANSFER_DMA_MAP, "test19");
  break;

 case 20:
  if (dev->in_pipe == 0)
   break;
  dev_info(&intf->dev,
   "TEST 20: read odd addr %d bytes %u times premapped\n",
   param->length, param->iterations);

  retval = test_unaligned_bulk(
    dev, dev->in_pipe,
    param->length, param->iterations,
    URB_NO_TRANSFER_DMA_MAP, "test20");
  break;

 /* control write tests with unaligned buffer */
 case 21:
  if (!dev->info->ctrl_out)
   break;
  dev_info(&intf->dev,
    "TEST 21: %d ep0out odd addr, %d..%d vary %d\n",
    param->iterations,
    realworld ? 1 : 0, param->length,
    param->vary);
  retval = ctrl_out(dev, param->iterations,
    param->length, param->vary, 1);
  break;

 /* unaligned iso tests */
 case 22:
  if (dev->out_iso_pipe == 0 || param->sglen == 0)
   break;
  dev_info(&intf->dev,
   "TEST 22: write %d iso odd, %d entries of %d bytes\n",
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=94 G=92

¤ Dauer der Verarbeitung: 0.24 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.