📄 usbtest.c
字号:
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;}/*-------------------------------------------------------------------------*/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, USB_ENDPOINT_HALT, ep, NULL, 0, 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 == NULL) 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 < 1 || 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, USB_CTRL_SET_TIMEOUT); if (retval != len) { what = "write"; if (retval >= 0) { INFO(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) { INFO(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)) { 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; /* [real world] the "zero bytes IN" case isn't really used. * hardware can easily trip up in this wierd case, since its * status stage is IN, not OUT like other ep0in transfers. */ if (len > length) len = realworld ? 1 : 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); }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 NULL; maxp = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); maxp *= 1 + (0x3 & (le16_to_cpu(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 NULL; } 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 inttest_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" : "", le16_to_cpu(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) { spin_unlock_irq (&context.lock); 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 * test. So discovery is simplified, and we have no device naming issues. * * Don't use these only as stress/load tests. Use them along with with * other USB bus activity: plugging, unplugging, mousing, mp3 playback, * video capture, and so on. Run different tests at different times, in * different sequences. Nothing here should interact with other devices, * except indirectly by consuming USB bandwidth and CPU resources for test * threads and request completion. But the only way to know that for sure * is to test when HC queues are in use by many devices. */static intusbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf){ struct usbtest_dev *dev = usb_get_intfdata (intf); struct usb_device *udev = testdev_to_usbdev (dev); struct usbtest_param *param = buf; int retval = -EOPNOTSUPP; struct urb *urb; struct scatterlist *sg; struct usb_sg_request req; struct timeval start; unsigned i; // FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. if (code != USBTEST_REQUEST) return -EOPNOTSUPP; if (param->iterations <= 0 || param->length < 0 || param->sglen < 0 || param->vary < 0) return -EINVAL; if (down_interruptible (&dev->sem)) return -ERESTARTSYS; if (intf->dev.power.power_state.event != PM_EVENT_ON) { up (&dev->sem); return -EHOSTUNREACH; } /* some devices, like ez-usb default devices, need a non-default * altsetting to have any active endpoints. some tests change * altsettings; force a default so most tests don't need to check. */ if (dev->info->alt >= 0) { int res; if (intf->altsetting->desc.bInterfaceNumber) { up (&dev->sem); return -ENODEV; } res = set_altsetting (dev, dev->info->alt); if (res) { dev_err (&intf->dev, "set altsetting to %d failed, %d\n", dev->info->alt, res); up (&dev->sem); return res; } } /* * 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. */ do_gettimeofday (&start); switch (param->test_num) { case 0: dev_dbg (&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_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) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) retval = simple_io (urb, param->iterations, 0, 0, "test1"); simple_free_urb (urb); break; case 2: if (dev->in_pipe == 0) break; 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) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) 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; 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) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) 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; 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -