aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/wusbcore/wa-xfer.c
diff options
context:
space:
mode:
authorThomas Pugliese <thomas.pugliese@gmail.com>2014-02-28 15:31:56 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-02-28 19:13:09 -0500
commitacfadcea2adaa52048c6b3c8a3c75105a5540707 (patch)
treed846a9a1e898e0d0f4d2bdfe08bc5e5abe8ccf38 /drivers/usb/wusbcore/wa-xfer.c
parent618836cc3478aec3dc9a60488bfd43ca93a322bd (diff)
usb: wusbcore: fix stranded URB after HWA unplug
This patch adds error checking to the abort request callback to forcibly clean up the dequeued transfers if the abort request failed. The wa_complete_remaining_xfer_segs was modified so that it could be used in this situation as well. This fixes a stranded URB/PNP hang when the HWA is unplugged while playing audio to a wireless audio device. Signed-off-by: Thomas Pugliese <thomas.pugliese@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/wusbcore/wa-xfer.c')
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c56
1 files changed, 51 insertions, 5 deletions
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 3ca055555105..cb061915f051 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -167,6 +167,8 @@ struct wa_xfer {
167 167
168static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer, 168static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer,
169 struct wa_seg *seg, int curr_iso_frame); 169 struct wa_seg *seg, int curr_iso_frame);
170static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
171 int starting_index, enum wa_seg_status status);
170 172
171static inline void wa_xfer_init(struct wa_xfer *xfer) 173static inline void wa_xfer_init(struct wa_xfer *xfer)
172{ 174{
@@ -416,12 +418,51 @@ out:
416 418
417struct wa_xfer_abort_buffer { 419struct wa_xfer_abort_buffer {
418 struct urb urb; 420 struct urb urb;
421 struct wahc *wa;
419 struct wa_xfer_abort cmd; 422 struct wa_xfer_abort cmd;
420}; 423};
421 424
422static void __wa_xfer_abort_cb(struct urb *urb) 425static void __wa_xfer_abort_cb(struct urb *urb)
423{ 426{
424 struct wa_xfer_abort_buffer *b = urb->context; 427 struct wa_xfer_abort_buffer *b = urb->context;
428 struct wahc *wa = b->wa;
429
430 /*
431 * If the abort request URB failed, then the HWA did not get the abort
432 * command. Forcibly clean up the xfer without waiting for a Transfer
433 * Result from the HWA.
434 */
435 if (urb->status < 0) {
436 struct wa_xfer *xfer;
437 struct device *dev = &wa->usb_iface->dev;
438
439 xfer = wa_xfer_get_by_id(wa, le32_to_cpu(b->cmd.dwTransferID));
440 dev_err(dev, "%s: Transfer Abort request failed. result: %d\n",
441 __func__, urb->status);
442 if (xfer) {
443 unsigned long flags;
444 int done;
445 struct wa_rpipe *rpipe = xfer->ep->hcpriv;
446
447 dev_err(dev, "%s: cleaning up xfer %p ID 0x%08X.\n",
448 __func__, xfer, wa_xfer_id(xfer));
449 spin_lock_irqsave(&xfer->lock, flags);
450 /* mark all segs as aborted. */
451 wa_complete_remaining_xfer_segs(xfer, 0,
452 WA_SEG_ABORTED);
453 done = __wa_xfer_is_done(xfer);
454 spin_unlock_irqrestore(&xfer->lock, flags);
455 if (done)
456 wa_xfer_completion(xfer);
457 wa_xfer_delayed_run(rpipe);
458 wa_xfer_put(xfer);
459 } else {
460 dev_err(dev, "%s: xfer ID 0x%08X already gone.\n",
461 __func__, le32_to_cpu(b->cmd.dwTransferID));
462 }
463 }
464
465 wa_put(wa); /* taken in __wa_xfer_abort */
425 usb_put_urb(&b->urb); 466 usb_put_urb(&b->urb);
426} 467}
427 468
@@ -449,6 +490,7 @@ static int __wa_xfer_abort(struct wa_xfer *xfer)
449 b->cmd.bRequestType = WA_XFER_ABORT; 490 b->cmd.bRequestType = WA_XFER_ABORT;
450 b->cmd.wRPipe = rpipe->descr.wRPipeIndex; 491 b->cmd.wRPipe = rpipe->descr.wRPipeIndex;
451 b->cmd.dwTransferID = wa_xfer_id_le32(xfer); 492 b->cmd.dwTransferID = wa_xfer_id_le32(xfer);
493 b->wa = wa_get(xfer->wa);
452 494
453 usb_init_urb(&b->urb); 495 usb_init_urb(&b->urb);
454 usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev, 496 usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev,
@@ -462,6 +504,7 @@ static int __wa_xfer_abort(struct wa_xfer *xfer)
462 504
463 505
464error_submit: 506error_submit:
507 wa_put(xfer->wa);
465 if (printk_ratelimit()) 508 if (printk_ratelimit())
466 dev_err(dev, "xfer %p: Can't submit abort request: %d\n", 509 dev_err(dev, "xfer %p: Can't submit abort request: %d\n",
467 xfer, result); 510 xfer, result);
@@ -2036,15 +2079,17 @@ static int wa_xfer_status_to_errno(u8 status)
2036 * no other segment transfer results will be returned from the device. 2079 * no other segment transfer results will be returned from the device.
2037 * Mark the remaining submitted or pending xfers as completed so that 2080 * Mark the remaining submitted or pending xfers as completed so that
2038 * the xfer will complete cleanly. 2081 * the xfer will complete cleanly.
2082 *
2083 * xfer->lock must be held
2084 *
2039 */ 2085 */
2040static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, 2086static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
2041 struct wa_seg *incoming_seg, enum wa_seg_status status) 2087 int starting_index, enum wa_seg_status status)
2042{ 2088{
2043 int index; 2089 int index;
2044 struct wa_rpipe *rpipe = xfer->ep->hcpriv; 2090 struct wa_rpipe *rpipe = xfer->ep->hcpriv;
2045 2091
2046 for (index = incoming_seg->index + 1; index < xfer->segs_submitted; 2092 for (index = starting_index; index < xfer->segs_submitted; index++) {
2047 index++) {
2048 struct wa_seg *current_seg = xfer->seg[index]; 2093 struct wa_seg *current_seg = xfer->seg[index];
2049 2094
2050 BUG_ON(current_seg == NULL); 2095 BUG_ON(current_seg == NULL);
@@ -2202,7 +2247,8 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer,
2202 * transfers with data or below for no data, the xfer will complete. 2247 * transfers with data or below for no data, the xfer will complete.
2203 */ 2248 */
2204 if (xfer_result->bTransferSegment & 0x80) 2249 if (xfer_result->bTransferSegment & 0x80)
2205 wa_complete_remaining_xfer_segs(xfer, seg, WA_SEG_DONE); 2250 wa_complete_remaining_xfer_segs(xfer, seg->index + 1,
2251 WA_SEG_DONE);
2206 if (usb_pipeisoc(xfer->urb->pipe) 2252 if (usb_pipeisoc(xfer->urb->pipe)
2207 && (le32_to_cpu(xfer_result->dwNumOfPackets) > 0)) { 2253 && (le32_to_cpu(xfer_result->dwNumOfPackets) > 0)) {
2208 /* set up WA state to read the isoc packet status next. */ 2254 /* set up WA state to read the isoc packet status next. */
@@ -2253,7 +2299,7 @@ error_buf_in_populate:
2253error_complete: 2299error_complete:
2254 xfer->segs_done++; 2300 xfer->segs_done++;
2255 rpipe_ready = rpipe_avail_inc(rpipe); 2301 rpipe_ready = rpipe_avail_inc(rpipe);
2256 wa_complete_remaining_xfer_segs(xfer, seg, seg->status); 2302 wa_complete_remaining_xfer_segs(xfer, seg->index + 1, seg->status);
2257 done = __wa_xfer_is_done(xfer); 2303 done = __wa_xfer_is_done(xfer);
2258 /* 2304 /*
2259 * queue work item to clear STALL for control endpoints. 2305 * queue work item to clear STALL for control endpoints.