aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2009-09-01 11:09:56 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-09-23 09:46:39 -0400
commit01c6460f968d7b57fc6f98adb587952628c6e099 (patch)
tree7eb51548d2db9ced665af1a5def7748cad983e34 /drivers/usb
parente6929a9020acbeb04d9a3ad9a88234c15be808fd (diff)
USB: usbfs: add USBDEVFS_URB_BULK_CONTINUATION flag
This patch (as1283) adds a new flag, USBDEVFS_URB_BULK_CONTINUATION, to usbfs. It is intended for userspace libraries such as libusb and openusb. When they have to break up a single usbfs bulk transfer into multiple URBs, they will set the flag on all but the first URB of the series. If an error other than an unlink occurs, the kernel will automatically cancel all the following URBs for the same endpoint and refuse to accept new submissions, until an URB is encountered that is not marked as a BULK_CONTINUATION. Such an URB would indicate the start of a new transfer or the presence of an older library, so the kernel returns to normal operation. This enables libraries to delimit bulk transfers correctly, even in the presence of early termination as indicated by short packets. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/devio.c78
1 files changed, 77 insertions, 1 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 71514be8b715..181f78c84105 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -74,6 +74,7 @@ struct dev_state {
74 void __user *disccontext; 74 void __user *disccontext;
75 unsigned long ifclaimed; 75 unsigned long ifclaimed;
76 u32 secid; 76 u32 secid;
77 u32 disabled_bulk_eps;
77}; 78};
78 79
79struct async { 80struct async {
@@ -88,6 +89,8 @@ struct async {
88 struct urb *urb; 89 struct urb *urb;
89 int status; 90 int status;
90 u32 secid; 91 u32 secid;
92 u8 bulk_addr;
93 u8 bulk_status;
91}; 94};
92 95
93static int usbfs_snoop; 96static int usbfs_snoop;
@@ -343,6 +346,43 @@ static void snoop_urb(struct usb_device *udev,
343 } 346 }
344} 347}
345 348
349#define AS_CONTINUATION 1
350#define AS_UNLINK 2
351
352static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
353__releases(ps->lock)
354__acquires(ps->lock)
355{
356 struct async *as;
357
358 /* Mark all the pending URBs that match bulk_addr, up to but not
359 * including the first one without AS_CONTINUATION. If such an
360 * URB is encountered then a new transfer has already started so
361 * the endpoint doesn't need to be disabled; otherwise it does.
362 */
363 list_for_each_entry(as, &ps->async_pending, asynclist) {
364 if (as->bulk_addr == bulk_addr) {
365 if (as->bulk_status != AS_CONTINUATION)
366 goto rescan;
367 as->bulk_status = AS_UNLINK;
368 as->bulk_addr = 0;
369 }
370 }
371 ps->disabled_bulk_eps |= (1 << bulk_addr);
372
373 /* Now carefully unlink all the marked pending URBs */
374 rescan:
375 list_for_each_entry(as, &ps->async_pending, asynclist) {
376 if (as->bulk_status == AS_UNLINK) {
377 as->bulk_status = 0; /* Only once */
378 spin_unlock(&ps->lock); /* Allow completions */
379 usb_unlink_urb(as->urb);
380 spin_lock(&ps->lock);
381 goto rescan;
382 }
383 }
384}
385
346static void async_completed(struct urb *urb) 386static void async_completed(struct urb *urb)
347{ 387{
348 struct async *as = urb->context; 388 struct async *as = urb->context;
@@ -371,6 +411,9 @@ static void async_completed(struct urb *urb)
371 snoop(&urb->dev->dev, "urb complete\n"); 411 snoop(&urb->dev->dev, "urb complete\n");
372 snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, 412 snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
373 as->status, COMPLETE); 413 as->status, COMPLETE);
414 if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
415 as->status != -ENOENT)
416 cancel_bulk_urbs(ps, as->bulk_addr);
374 spin_unlock(&ps->lock); 417 spin_unlock(&ps->lock);
375 418
376 if (signr) 419 if (signr)
@@ -993,6 +1036,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
993 1036
994 if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | 1037 if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
995 USBDEVFS_URB_SHORT_NOT_OK | 1038 USBDEVFS_URB_SHORT_NOT_OK |
1039 USBDEVFS_URB_BULK_CONTINUATION |
996 USBDEVFS_URB_NO_FSBR | 1040 USBDEVFS_URB_NO_FSBR |
997 USBDEVFS_URB_ZERO_PACKET | 1041 USBDEVFS_URB_ZERO_PACKET |
998 USBDEVFS_URB_NO_INTERRUPT)) 1042 USBDEVFS_URB_NO_INTERRUPT))
@@ -1194,7 +1238,39 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
1194 snoop_urb(ps->dev, as->userurb, as->urb->pipe, 1238 snoop_urb(ps->dev, as->userurb, as->urb->pipe,
1195 as->urb->transfer_buffer_length, 0, SUBMIT); 1239 as->urb->transfer_buffer_length, 0, SUBMIT);
1196 async_newpending(as); 1240 async_newpending(as);
1197 if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) { 1241
1242 if (usb_endpoint_xfer_bulk(&ep->desc)) {
1243 spin_lock_irq(&ps->lock);
1244
1245 /* Not exactly the endpoint address; the direction bit is
1246 * shifted to the 0x10 position so that the value will be
1247 * between 0 and 31.
1248 */
1249 as->bulk_addr = usb_endpoint_num(&ep->desc) |
1250 ((ep->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
1251 >> 3);
1252
1253 /* If this bulk URB is the start of a new transfer, re-enable
1254 * the endpoint. Otherwise mark it as a continuation URB.
1255 */
1256 if (uurb->flags & USBDEVFS_URB_BULK_CONTINUATION)
1257 as->bulk_status = AS_CONTINUATION;
1258 else
1259 ps->disabled_bulk_eps &= ~(1 << as->bulk_addr);
1260
1261 /* Don't accept continuation URBs if the endpoint is
1262 * disabled because of an earlier error.
1263 */
1264 if (ps->disabled_bulk_eps & (1 << as->bulk_addr))
1265 ret = -EREMOTEIO;
1266 else
1267 ret = usb_submit_urb(as->urb, GFP_ATOMIC);
1268 spin_unlock_irq(&ps->lock);
1269 } else {
1270 ret = usb_submit_urb(as->urb, GFP_KERNEL);
1271 }
1272
1273 if (ret) {
1198 dev_printk(KERN_DEBUG, &ps->dev->dev, 1274 dev_printk(KERN_DEBUG, &ps->dev->dev,
1199 "usbfs: usb_submit_urb returned %d\n", ret); 1275 "usbfs: usb_submit_urb returned %d\n", ret);
1200 snoop_urb(ps->dev, as->userurb, as->urb->pipe, 1276 snoop_urb(ps->dev, as->userurb, as->urb->pipe,