diff options
Diffstat (limited to 'drivers/usb/misc/usbtest.c')
| -rw-r--r-- | drivers/usb/misc/usbtest.c | 180 |
1 files changed, 150 insertions, 30 deletions
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index eee82ca55b7b..5c92c8d8e283 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -181,7 +182,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) case USB_ENDPOINT_XFER_ISOC: if (dev->info->iso) endpoint_update(edi, &iso_in, &iso_out, e); - /* FALLTHROUGH */ + fallthrough; default: continue; } @@ -202,12 +203,13 @@ found: return tmp; } - if (in) { + 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, @@ -345,6 +347,14 @@ static unsigned get_maxpacket(struct usb_device *udev, int 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; @@ -354,7 +364,7 @@ static void simple_fill_buf(struct urb *urb) switch (pattern) { default: - /* FALLTHROUGH */ + fallthrough; case 0: memset(buf, 0, len); break; @@ -575,11 +585,16 @@ alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe) return sg; } -static void sg_timeout(unsigned long _req) +struct sg_timeout { + struct timer_list timer; + struct usb_sg_request *req; +}; + +static void sg_timeout(struct timer_list *t) { - struct usb_sg_request *req = (struct usb_sg_request *) _req; + struct sg_timeout *timeout = timer_container_of(timeout, t, timer); - usb_sg_cancel(req); + usb_sg_cancel(timeout->req); } static int perform_sglist( @@ -593,9 +608,11 @@ static int perform_sglist( { struct usb_device *udev = testdev_to_usbdev(tdev); int retval = 0; - struct timer_list sg_timer; + struct sg_timeout timeout = { + .req = req, + }; - setup_timer_on_stack(&sg_timer, sg_timeout, (unsigned long) req); + timer_setup_on_stack(&timeout.timer, sg_timeout, 0); while (retval == 0 && iterations-- > 0) { retval = usb_sg_init(req, udev, pipe, @@ -606,13 +623,14 @@ static int perform_sglist( if (retval) break; - mod_timer(&sg_timer, jiffies + + mod_timer(&timeout.timer, jiffies + msecs_to_jiffies(SIMPLE_IO_TIMEOUT)); usb_sg_wait(req); - if (!del_timer_sync(&sg_timer)) + if (!timer_delete_sync(&timeout.timer)) retval = -ETIMEDOUT; else retval = req->status; + timer_destroy_on_stack(&timeout.timer); /* FIXME check resulting data pattern */ @@ -663,7 +681,7 @@ static int get_altsetting(struct usbtest_dev *dev) return dev->buf[0]; case 0: retval = -ERANGE; - /* FALLTHROUGH */ + fallthrough; default: return retval; } @@ -687,7 +705,7 @@ static int is_good_config(struct usbtest_dev *tdev, int len) { struct usb_config_descriptor *config; - if (len < sizeof(*config)) + if (len < (int)sizeof(*config)) return 0; config = (struct usb_config_descriptor *) tdev->buf; @@ -1014,7 +1032,7 @@ static int ch9_postconfig(struct usbtest_dev *dev) /* 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); + 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; @@ -1024,7 +1042,7 @@ static int ch9_postconfig(struct usbtest_dev *dev) * the device's remote wakeup feature ... if we can, test that here */ - retval = usb_get_status(udev, USB_RECIP_INTERFACE, + 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); @@ -1072,11 +1090,12 @@ static void ctrl_complete(struct urb *urb) 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(&ctx->lock); + spin_lock_irqsave(&ctx->lock, flags); ctx->count--; ctx->pending--; @@ -1175,7 +1194,7 @@ error: /* signal completion when nothing's queued */ if (ctx->pending == 0) complete(&ctx->complete); - spin_unlock(&ctx->lock); + spin_unlock_irqrestore(&ctx->lock, flags); } static int @@ -1613,7 +1632,7 @@ static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) u16 status; /* shouldn't look or act halted */ - retval = usb_get_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status); + 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); @@ -1635,7 +1654,7 @@ static int verify_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) u16 status; /* should look and act halted */ - retval = usb_get_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status); + 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); @@ -1700,6 +1719,35 @@ static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb) 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; @@ -1732,6 +1780,33 @@ done: 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 @@ -1851,8 +1926,9 @@ struct transfer_context { static void complicated_callback(struct urb *urb) { struct transfer_context *ctx = urb->context; + unsigned long flags; - spin_lock(&ctx->lock); + spin_lock_irqsave(&ctx->lock, flags); ctx->count--; ctx->packet_count += urb->number_of_packets; @@ -1875,7 +1951,7 @@ static void complicated_callback(struct urb *urb) dev_err(&ctx->dev->intf->dev, "resubmit err %d\n", status); - /* FALLTHROUGH */ + fallthrough; case -ENODEV: /* disconnected */ case -ESHUTDOWN: /* endpoint disabled */ ctx->submit_error = 1; @@ -1892,7 +1968,7 @@ static void complicated_callback(struct urb *urb) complete(&ctx->done); } done: - spin_unlock(&ctx->lock); + spin_unlock_irqrestore(&ctx->lock, flags); } static struct urb *iso_alloc_urb( @@ -1908,8 +1984,13 @@ static struct urb *iso_alloc_urb( if (bytes < 0 || !desc) return NULL; - maxp = 0x7ff & usb_endpoint_maxp(desc); - maxp *= usb_endpoint_maxp_mult(desc); + + 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); @@ -1940,7 +2021,8 @@ static struct urb *iso_alloc_urb( for (i = 0; i < packets; i++) { /* here, only the last packet will be short */ - urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp); + 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; @@ -1962,7 +2044,17 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param, unsigned i; unsigned long packets = 0; int status = 0; - struct urb *urbs[param->sglen]; + 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; @@ -1991,17 +2083,24 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param, 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" : "", + (udev->speed >= USB_SPEED_HIGH) ? "micro" : "", usb_endpoint_maxp(desc), - usb_endpoint_maxp_mult(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), + / ((udev->speed >= USB_SPEED_HIGH) ? 8 : 1), packets); } @@ -2043,6 +2142,8 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param, else if (context.errors > (context.is_iso ? context.packet_count / 10 : 0)) status = -EIO; + + kfree(urbs); return status; fail: @@ -2050,6 +2151,8 @@ fail: if (urbs[i]) simple_free_urb(urbs[i]); } + + kfree(urbs); return status; } @@ -2087,6 +2190,8 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param) 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. * @@ -2509,6 +2614,20 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param) retval = test_queue(dev, param, dev->in_pipe, NULL, 0); break; + /* Test data Toggle/seq_nr clear between bulk out transfers */ + case 29: + if (dev->out_pipe == 0) + break; + retval = 0; + dev_info(&intf->dev, "TEST 29: Clear toggle between bulk writes %d times\n", + param->iterations); + for (i = param->iterations; retval == 0 && i > 0; --i) + retval = toggle_sync_simple(dev); + + if (retval) + ERROR(dev, "toggle sync failed, iterations left %d\n", + i); + break; } return retval; } @@ -2520,7 +2639,7 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param) * 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 + * Don't use these only as stress/load tests. Use them along 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, @@ -2763,6 +2882,7 @@ static void usbtest_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); dev_dbg(&intf->dev, "disconnect\n"); + kfree(dev->buf); kfree(dev); } |
