*
  * The callback (see above) does nothing but freeing up the data by
  * putting the URB. Because the URB is allocated at the head of the
- * struct, the whole space we allocated is kfreed.
- *
- * We'll get an 'aborted transaction' xfer result on DTI, that'll
- * politely ignore because at this point the transaction has been
- * marked as aborted already.
+ * struct, the whole space we allocated is kfreed. *
  */
-static void __wa_xfer_abort(struct wa_xfer *xfer)
+static int __wa_xfer_abort(struct wa_xfer *xfer)
 {
-       int result;
+       int result = -ENOMEM;
        struct device *dev = &xfer->wa->usb_iface->dev;
        struct wa_xfer_abort_buffer *b;
        struct wa_rpipe *rpipe = xfer->ep->hcpriv;
        result = usb_submit_urb(&b->urb, GFP_ATOMIC);
        if (result < 0)
                goto error_submit;
-       return;                         /* callback frees! */
+       return result;                          /* callback frees! */
 
 
 error_submit:
                        xfer, result);
        kfree(b);
 error_kmalloc:
-       return;
+       return result;
 
 }
 
        struct wa_xfer *xfer;
        struct wa_seg *seg;
        struct wa_rpipe *rpipe;
-       unsigned cnt;
+       unsigned cnt, done = 0, xfer_abort_pending;
        unsigned rpipe_ready = 0;
 
        xfer = urb->hcpriv;
                goto out;
        }
        spin_lock_irqsave(&xfer->lock, flags);
+       pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer));
        rpipe = xfer->ep->hcpriv;
        if (rpipe == NULL) {
                pr_debug("%s: xfer id 0x%08X has no RPIPE.  %s",
        if (xfer->seg == NULL)          /* still hasn't reached */
                goto out_unlock;        /* setup(), enqueue_b() completes */
        /* Ok, the xfer is in flight already, it's been setup and submitted.*/
-       __wa_xfer_abort(xfer);
+       xfer_abort_pending = __wa_xfer_abort(xfer) >= 0;
        for (cnt = 0; cnt < xfer->segs; cnt++) {
                seg = xfer->seg[cnt];
+               pr_debug("%s: xfer id 0x%08X#%d status = %d\n",
+                       __func__, wa_xfer_id(xfer), cnt, seg->status);
                switch (seg->status) {
                case WA_SEG_NOTREADY:
                case WA_SEG_READY:
                        WARN_ON(1);
                        break;
                case WA_SEG_DELAYED:
+                       /*
+                        * delete from rpipe delayed list.  If no segments on
+                        * this xfer have been submitted, __wa_xfer_is_done will
+                        * trigger a giveback below.  Otherwise, the submitted
+                        * segments will be completed in the DTI interrupt.
+                        */
                        seg->status = WA_SEG_ABORTED;
                        spin_lock_irqsave(&rpipe->seg_lock, flags2);
                        list_del(&seg->list_node);
                        xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
                        spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
                        break;
-               case WA_SEG_SUBMITTED:
-                       seg->status = WA_SEG_ABORTED;
-                       usb_unlink_urb(&seg->tr_urb);
-                       if (xfer->is_inbound == 0)
-                               usb_unlink_urb(seg->dto_urb);
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
-               case WA_SEG_PENDING:
-                       seg->status = WA_SEG_ABORTED;
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
-               case WA_SEG_DTI_PENDING:
-                       usb_unlink_urb(wa->dti_urb);
-                       seg->status = WA_SEG_ABORTED;
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
                case WA_SEG_DONE:
                case WA_SEG_ERROR:
                case WA_SEG_ABORTED:
                        break;
+                       /*
+                        * In the states below, the HWA device already knows
+                        * about the transfer.  If an abort request was sent,
+                        * allow the HWA to process it and wait for the
+                        * results.  Otherwise, the DTI state and seg completed
+                        * counts can get out of sync.
+                        */
+               case WA_SEG_SUBMITTED:
+               case WA_SEG_PENDING:
+               case WA_SEG_DTI_PENDING:
+                       /*
+                        * Check if the abort was successfully sent.  This could
+                        * be false if the HWA has been removed but we haven't
+                        * gotten the disconnect notification yet.
+                        */
+                       if (!xfer_abort_pending) {
+                               seg->status = WA_SEG_ABORTED;
+                               rpipe_ready = rpipe_avail_inc(rpipe);
+                               xfer->segs_done++;
+                       }
+                       break;
                }
        }
        xfer->result = urb->status;     /* -ENOENT or -ECONNRESET */
-       __wa_xfer_is_done(xfer);
+       done = __wa_xfer_is_done(xfer);
        spin_unlock_irqrestore(&xfer->lock, flags);
-       wa_xfer_completion(xfer);
+       if (done)
+               wa_xfer_completion(xfer);
        if (rpipe_ready)
                wa_xfer_delayed_run(rpipe);
        return 0;
        return errno;
 }
 
+/*
+ * If a last segment flag and/or a transfer result error is encountered,
+ * no other segment transfer results will be returned from the device.
+ * Mark the remaining submitted or pending xfers as completed so that
+ * the xfer will complete cleanly.
+ */
+static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
+               struct wa_seg *incoming_seg)
+{
+       int index;
+       struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+       for (index = incoming_seg->index + 1; index < xfer->segs_submitted;
+               index++) {
+               struct wa_seg *current_seg = xfer->seg[index];
+
+               BUG_ON(current_seg == NULL);
+
+               switch (current_seg->status) {
+               case WA_SEG_SUBMITTED:
+               case WA_SEG_PENDING:
+               case WA_SEG_DTI_PENDING:
+                       rpipe_avail_inc(rpipe);
+               /*
+                * do not increment RPIPE avail for the WA_SEG_DELAYED case
+                * since it has not been submitted to the RPIPE.
+                */
+               case WA_SEG_DELAYED:
+                       xfer->segs_done++;
+                       current_seg->status = incoming_seg->status;
+                       break;
+               case WA_SEG_ABORTED:
+                       break;
+               default:
+                       WARN(1, "%s: xfer 0x%08X#%d. bad seg status = %d\n",
+                               __func__, wa_xfer_id(xfer), index,
+                               current_seg->status);
+                       break;
+               }
+       }
+}
+
 /*
  * Process a xfer result completion message
  *
- * inbound transfers: need to schedule a DTI read
+ * inbound transfers: need to schedule a buf_in_urb read
  *
  * FIXME: this function needs to be broken up in parts
  */
                seg->result = wa_xfer_status_to_errno(usb_status);
                dev_err(dev, "DTI: xfer %p#:%08X:%u failed (0x%02x)\n",
                        xfer, xfer->id, seg->index, usb_status);
+               seg->status = ((usb_status & 0x7F) == WA_XFER_STATUS_ABORTED) ?
+                       WA_SEG_ABORTED : WA_SEG_ERROR;
                goto error_complete;
        }
        /* FIXME: we ignore warnings, tally them for stats */
        wa->buf_in_urb->sg = NULL;
 error_sg_alloc:
        __wa_xfer_abort(xfer);
-error_complete:
        seg->status = WA_SEG_ERROR;
+error_complete:
        xfer->segs_done++;
        rpipe_ready = rpipe_avail_inc(rpipe);
+       wa_complete_remaining_xfer_segs(xfer, seg);
        done = __wa_xfer_is_done(xfer);
        /*
         * queue work item to clear STALL for control endpoints.