📄 usbtest.c
字号:
* 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_dbg (&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_dbg (&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_dbg (&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_dbg (&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_dbg (&iface->dev, "dev descriptor --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } /* 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->buf, retval)) { dev_dbg (&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_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)) { dev_dbg (&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->buf, retval)) { dev_dbg (&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_status (udev, USB_RECIP_DEVICE, 0, dev->buf); if (retval != 2) { dev_dbg (&iface->dev, "get dev status --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } // FIXME configuration.bmAttributes says if we could try to set/clear // the device's remote wakeup feature ... if we can, test that here retval = usb_get_status (udev, USB_RECIP_INTERFACE, iface->altsetting [0].desc.bInterfaceNumber, dev->buf); if (retval != 2) { dev_dbg (&iface->dev, "get interface status --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } // 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 *param; int last;};#define NUM_SUBCASES 15 /* how many test subcases here? */struct subcase { struct usb_ctrlrequest setup; int number; int expected;};static void ctrl_complete (struct urb *urb, struct pt_regs *regs){ struct ctrl_ctx *ctx = urb->context; struct usb_ctrlrequest *reqp; struct subcase *subcase; int status = urb->status; reqp = (struct usb_ctrlrequest *)urb->setup_packet; subcase = container_of (reqp, struct subcase, setup); spin_lock (&ctx->lock); 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) { dbg ("subcase %d completed out of order, last %d", 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 && ( ((urb->status == -subcase->expected /* happened */ || urb->status == 0)))) /* didn't */ status = 0; /* sometimes more than one fault is allowed */ else if (subcase->number == 12 && status == -EPIPE) status = 0; else dbg ("subtest %d error, status %d", subcase->number, status); } /* unexpected status codes mean errors; ideally, in hardware */ if (status) {error: if (ctx->status == 0) { int i; ctx->status = status; info ("control queue %02x.%02x, err %d, %d left", reqp->bRequestType, reqp->bRequest, status, ctx->count); /* 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; status = usb_unlink_urb (u); switch (status) { case -EINPROGRESS: case -EBUSY: case -EIDRM: continue; default: dbg ("urb unlink --> %d", status); } } status = ctx->status; } } /* resubmit if we need to, else mark this as done */ if ((status == 0) && (ctx->pending < ctx->count)) { if ((status = usb_submit_urb (urb, SLAB_ATOMIC)) != 0) { dbg ("can't resubmit ctrl %02x.%02x, err %d", 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 (&ctx->lock);}static inttest_ctrl_queue (struct usbtest_dev *dev, struct usbtest_param *param){ struct usb_device *udev = testdev_to_usbdev (dev); struct urb **urb; struct ctrl_ctx context; int i; 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 = kmalloc (param->sglen * sizeof (struct urb *), SLAB_KERNEL); if (!urb) return -ENOMEM; memset (urb, 0, param->sglen * sizeof (struct urb *)); 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; 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 (USUALLY STALLS) 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 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; switch (len) { case 8: len = 24; break; case 16: len = 32; break; } expected = -EREMOTEIO; break; default: err ("bogus number of ctrl queue testcases!"); context.status = -EINVAL; goto cleanup; } req.wLength = cpu_to_le16 (len); urb [i] = u = simple_alloc_urb (udev, pipe, len); if (!u) goto cleanup; reqp = usb_buffer_alloc (udev, sizeof *reqp, SLAB_KERNEL, &u->setup_dma); if (!reqp) goto cleanup; reqp->setup = req; reqp->number = i % NUM_SUBCASES; reqp->expected = expected; u->setup_packet = (char *) &reqp->setup; u->transfer_flags |= URB_NO_SETUP_DMA_MAP; 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], SLAB_ATOMIC); if (context.status != 0) { dbg ("can't submit urb[%d], status %d", 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; if (urb [i]->setup_packet) usb_buffer_free (udev, sizeof (struct usb_ctrlrequest), urb [i]->setup_packet, urb [i]->setup_dma); simple_free_urb (urb [i]); } kfree (urb); return context.status;}#undef NUM_SUBCASES/*-------------------------------------------------------------------------*/static void unlink1_callback (struct urb *urb, struct pt_regs *regs){ int status = urb->status; // we "know" -EPIPE (stall) never happens if (!status) status = usb_submit_urb (urb, SLAB_ATOMIC); if (status) { urb->status = status; complete ((struct completion *) 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); if (!urb) return -ENOMEM; urb->context = &completion; urb->complete = unlink1_callback; /* 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. */ if ((retval = usb_submit_urb (urb, SLAB_KERNEL)) != 0) { dev_dbg (&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) {retry: retval = usb_unlink_urb (urb); if (retval == -EBUSY || retval == -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. */ dev_dbg (&dev->intf->dev, "unlink retry\n"); goto retry; } } else usb_kill_urb (urb); if (!(retval == 0 || retval == -EINPROGRESS)) { dev_dbg (&dev->intf->dev, "unlink fail %d\n", retval); return retval; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -