Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
[pandora-kernel.git] / drivers / usb / misc / usbtest.c
index ff9a01f..bb10846 100644 (file)
@@ -268,9 +268,9 @@ static inline void simple_fill_buf(struct urb *urb)
        }
 }
 
-static inline unsigned buffer_offset(void *buf)
+static inline unsigned long buffer_offset(void *buf)
 {
-       return (unsigned)buf & (ARCH_KMALLOC_MINALIGN - 1);
+       return (unsigned long)buf & (ARCH_KMALLOC_MINALIGN - 1);
 }
 
 static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb)
@@ -329,7 +329,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
 
 static void simple_free_urb(struct urb *urb)
 {
-       unsigned offset = buffer_offset(urb->transfer_buffer);
+       unsigned long offset = buffer_offset(urb->transfer_buffer);
 
        if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
                usb_free_coherent(
@@ -1030,6 +1030,8 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
                        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;
@@ -1195,6 +1197,104 @@ static int unlink_simple(struct usbtest_dev *dev, int pipe, int len)
 
 /*-------------------------------------------------------------------------*/
 
+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;
+       }
+
+       /* 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;
@@ -1970,8 +2070,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
                                dev->in_iso_pipe, dev->iso_in, 0);
                break;
 
-       /* FIXME unlink from queue (ring with N urbs) */
-
        /* FIXME scatterlist cancel (needs helper thread) */
 
        /* Tests for bulk I/O using DMA mapping by core and odd address */
@@ -2064,6 +2162,26 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
                                dev->in_iso_pipe, dev->iso_in, 1);
                break;
 
+       /* unlink URBs from a bulk-OUT queue */
+       case 24:
+               if (dev->out_pipe == 0 || !param->length || param->sglen < 4)
+                       break;
+               retval = 0;
+               dev_info(&intf->dev, "TEST 17:  unlink from %d queues of "
+                               "%d %d-byte writes\n",
+                               param->iterations, param->sglen, param->length);
+               for (i = param->iterations; retval == 0 && i > 0; --i) {
+                       retval = unlink_queued(dev, dev->out_pipe,
+                                               param->sglen, param->length);
+                       if (retval) {
+                               dev_err(&intf->dev,
+                                       "unlink queued writes failed %d, "
+                                       "iterations left %d\n", retval, i);
+                               break;
+                       }
+               }
+               break;
+
        }
        do_gettimeofday(&param->duration);
        param->duration.tv_sec -= start.tv_sec;
@@ -2192,6 +2310,9 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
                        case USB_SPEED_HIGH:
                                tmp = "high";
                                break;
+                       case USB_SPEED_SUPER:
+                               tmp = "super";
+                               break;
                        default:
                                tmp = "unknown";
                                break;