aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/devio.c78
-rw-r--r--include/linux/usbdevice_fs.h1
2 files changed, 78 insertions, 1 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 71514be8b71..181f78c8410 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,
diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h
index 00ceebeb9e5..b2a7d8ba6ee 100644
--- a/include/linux/usbdevice_fs.h
+++ b/include/linux/usbdevice_fs.h
@@ -77,6 +77,7 @@ struct usbdevfs_connectinfo {
77 77
78#define USBDEVFS_URB_SHORT_NOT_OK 0x01 78#define USBDEVFS_URB_SHORT_NOT_OK 0x01
79#define USBDEVFS_URB_ISO_ASAP 0x02 79#define USBDEVFS_URB_ISO_ASAP 0x02
80#define USBDEVFS_URB_BULK_CONTINUATION 0x04
80#define USBDEVFS_URB_NO_FSBR 0x20 81#define USBDEVFS_URB_NO_FSBR 0x20
81#define USBDEVFS_URB_ZERO_PACKET 0x40 82#define USBDEVFS_URB_ZERO_PACKET 0x40
82#define USBDEVFS_URB_NO_INTERRUPT 0x80 83#define USBDEVFS_URB_NO_INTERRUPT 0x80