aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/usbtest.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2011-04-14 11:21:04 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-04-29 20:24:38 -0400
commit869410f82cbbb1464772046d87de8d18a916e706 (patch)
treed8fbbf47c08a3db93fc3dd3d67c99044e6e254ba /drivers/usb/misc/usbtest.c
parent1f594b64a4f74ece0b7166ca4db05a71a64bd685 (diff)
USB: add queued-unlinks test case to usbtest driver
This patch (as1452b) adds a new test case to the usbtest driver. Test 24 exercises the unlink-from-queue pathways in the host. It queues a user-specified number of bulk-OUT URBs of user-specified size, unlinks the fourth- and second-from-last URBs in the queue, and then waits to see if all the URBs complete in the expected way (except of course that the unlinked URBs might complete normally, if they weren't unlinked soon enough). This new test has confirmed the existence of a bug in the ehci-hcd driver, to be fixed by a separate patch. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/misc/usbtest.c')
-rw-r--r--drivers/usb/misc/usbtest.c120
1 files changed, 118 insertions, 2 deletions
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 388cc128072a..58a5685fb7d1 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -1195,6 +1195,104 @@ static int unlink_simple(struct usbtest_dev *dev, int pipe, int len)
1195 1195
1196/*-------------------------------------------------------------------------*/ 1196/*-------------------------------------------------------------------------*/
1197 1197
1198struct queued_ctx {
1199 struct completion complete;
1200 atomic_t pending;
1201 unsigned num;
1202 int status;
1203 struct urb **urbs;
1204};
1205
1206static void unlink_queued_callback(struct urb *urb)
1207{
1208 int status = urb->status;
1209 struct queued_ctx *ctx = urb->context;
1210
1211 if (ctx->status)
1212 goto done;
1213 if (urb == ctx->urbs[ctx->num - 4] || urb == ctx->urbs[ctx->num - 2]) {
1214 if (status == -ECONNRESET)
1215 goto done;
1216 /* What error should we report if the URB completed normally? */
1217 }
1218 if (status != 0)
1219 ctx->status = status;
1220
1221 done:
1222 if (atomic_dec_and_test(&ctx->pending))
1223 complete(&ctx->complete);
1224}
1225
1226static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num,
1227 unsigned size)
1228{
1229 struct queued_ctx ctx;
1230 struct usb_device *udev = testdev_to_usbdev(dev);
1231 void *buf;
1232 dma_addr_t buf_dma;
1233 int i;
1234 int retval = -ENOMEM;
1235
1236 init_completion(&ctx.complete);
1237 atomic_set(&ctx.pending, 1); /* One more than the actual value */
1238 ctx.num = num;
1239 ctx.status = 0;
1240
1241 buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &buf_dma);
1242 if (!buf)
1243 return retval;
1244 memset(buf, 0, size);
1245
1246 /* Allocate and init the urbs we'll queue */
1247 ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL);
1248 if (!ctx.urbs)
1249 goto free_buf;
1250 for (i = 0; i < num; i++) {
1251 ctx.urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
1252 if (!ctx.urbs[i])
1253 goto free_urbs;
1254 usb_fill_bulk_urb(ctx.urbs[i], udev, pipe, buf, size,
1255 unlink_queued_callback, &ctx);
1256 ctx.urbs[i]->transfer_dma = buf_dma;
1257 ctx.urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
1258 }
1259
1260 /* Submit all the URBs and then unlink URBs num - 4 and num - 2. */
1261 for (i = 0; i < num; i++) {
1262 atomic_inc(&ctx.pending);
1263 retval = usb_submit_urb(ctx.urbs[i], GFP_KERNEL);
1264 if (retval != 0) {
1265 dev_err(&dev->intf->dev, "submit urbs[%d] fail %d\n",
1266 i, retval);
1267 atomic_dec(&ctx.pending);
1268 ctx.status = retval;
1269 break;
1270 }
1271 }
1272 if (i == num) {
1273 usb_unlink_urb(ctx.urbs[num - 4]);
1274 usb_unlink_urb(ctx.urbs[num - 2]);
1275 } else {
1276 while (--i >= 0)
1277 usb_unlink_urb(ctx.urbs[i]);
1278 }
1279
1280 if (atomic_dec_and_test(&ctx.pending)) /* The extra count */
1281 complete(&ctx.complete);
1282 wait_for_completion(&ctx.complete);
1283 retval = ctx.status;
1284
1285 free_urbs:
1286 for (i = 0; i < num; i++)
1287 usb_free_urb(ctx.urbs[i]);
1288 kfree(ctx.urbs);
1289 free_buf:
1290 usb_free_coherent(udev, size, buf, buf_dma);
1291 return retval;
1292}
1293
1294/*-------------------------------------------------------------------------*/
1295
1198static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) 1296static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
1199{ 1297{
1200 int retval; 1298 int retval;
@@ -1970,8 +2068,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
1970 dev->in_iso_pipe, dev->iso_in, 0); 2068 dev->in_iso_pipe, dev->iso_in, 0);
1971 break; 2069 break;
1972 2070
1973 /* FIXME unlink from queue (ring with N urbs) */
1974
1975 /* FIXME scatterlist cancel (needs helper thread) */ 2071 /* FIXME scatterlist cancel (needs helper thread) */
1976 2072
1977 /* Tests for bulk I/O using DMA mapping by core and odd address */ 2073 /* Tests for bulk I/O using DMA mapping by core and odd address */
@@ -2064,6 +2160,26 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
2064 dev->in_iso_pipe, dev->iso_in, 1); 2160 dev->in_iso_pipe, dev->iso_in, 1);
2065 break; 2161 break;
2066 2162
2163 /* unlink URBs from a bulk-OUT queue */
2164 case 24:
2165 if (dev->out_pipe == 0 || !param->length || param->sglen < 4)
2166 break;
2167 retval = 0;
2168 dev_info(&intf->dev, "TEST 17: unlink from %d queues of "
2169 "%d %d-byte writes\n",
2170 param->iterations, param->sglen, param->length);
2171 for (i = param->iterations; retval == 0 && i > 0; --i) {
2172 retval = unlink_queued(dev, dev->out_pipe,
2173 param->sglen, param->length);
2174 if (retval) {
2175 dev_err(&intf->dev,
2176 "unlink queued writes failed %d, "
2177 "iterations left %d\n", retval, i);
2178 break;
2179 }
2180 }
2181 break;
2182
2067 } 2183 }
2068 do_gettimeofday(&param->duration); 2184 do_gettimeofday(&param->duration);
2069 param->duration.tv_sec -= start.tv_sec; 2185 param->duration.tv_sec -= start.tv_sec;