diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/misc/usbtest.c | 120 |
1 files changed, 118 insertions, 2 deletions
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 388cc128072..58a5685fb7d 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 | ||
1198 | struct queued_ctx { | ||
1199 | struct completion complete; | ||
1200 | atomic_t pending; | ||
1201 | unsigned num; | ||
1202 | int status; | ||
1203 | struct urb **urbs; | ||
1204 | }; | ||
1205 | |||
1206 | static 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 | |||
1226 | static 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 | |||
1198 | static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) | 1296 | static 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(¶m->duration); | 2184 | do_gettimeofday(¶m->duration); |
2069 | param->duration.tv_sec -= start.tv_sec; | 2185 | param->duration.tv_sec -= start.tv_sec; |