uhci_free_td(uhci, td);
}
- urbp->urb->hcpriv = NULL;
kmem_cache_free(uhci_up_cachep, urbp);
}
} else if (qh->period != urb->interval) {
return -EINVAL; /* Can't change the period */
- } else { /* Pick up where the last URB leaves off */
+ } else {
+ /* Find the next unused frame */
if (list_empty(&qh->queue)) {
frame = qh->iso_frame;
} else {
lurb->number_of_packets *
lurb->interval;
}
- if (urb->transfer_flags & URB_ISO_ASAP)
- urb->start_frame = frame;
- else if (urb->start_frame != frame)
- return -EINVAL;
+ if (urb->transfer_flags & URB_ISO_ASAP) {
+ /* Skip some frames if necessary to insure
+ * the start frame is in the future.
+ */
+ uhci_get_current_frame_number(uhci);
+ if (uhci_frame_before_eq(frame, uhci->frame_number)) {
+ frame = uhci->frame_number + 1;
+ frame += ((qh->phase - frame) &
+ (qh->period - 1));
+ }
+ } /* Otherwise pick up where the last URB leaves off */
+ urb->start_frame = frame;
}
/* Make sure we won't have to go too far into the future */
* Finish unlinking an URB and give it back
*/
static void uhci_giveback_urb(struct uhci_hcd *uhci, struct uhci_qh *qh,
- struct urb *urb)
+ struct urb *urb, int status)
__releases(uhci->lock)
__acquires(uhci->lock)
{
* unlinked first. Regardless, don't confuse people with a
* negative length. */
urb->actual_length = max(urb->actual_length, 0);
-
- /* Report erroneous short transfers */
- if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
- urb->actual_length <
- urb->transfer_buffer_length &&
- urb->status == 0))
- urb->status = -EREMOTEIO;
}
/* When giving back the first URB in an Isochronous queue,
usb_hcd_unlink_urb_from_ep(uhci_to_hcd(uhci), urb);
spin_unlock(&uhci->lock);
- usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb);
+ usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb, status);
spin_lock(&uhci->lock);
/* If the queue is now empty, we can unlink the QH and give up its
if (status == -EINPROGRESS)
break;
- spin_lock(&urb->lock);
- if (urb->status == -EINPROGRESS) /* Not dequeued */
- urb->status = status;
- else
- status = ECONNRESET; /* Not -ECONNRESET */
- spin_unlock(&urb->lock);
-
/* Dequeued but completed URBs can't be given back unless
* the QH is stopped or has finished unlinking. */
- if (status == ECONNRESET) {
+ if (urb->unlinked) {
if (QH_FINISHED_UNLINKING(qh))
qh->is_stopped = 1;
else if (!qh->is_stopped)
return;
}
- uhci_giveback_urb(uhci, qh, urb);
+ uhci_giveback_urb(uhci, qh, urb, status);
if (status < 0)
break;
}
restart:
list_for_each_entry(urbp, &qh->queue, node) {
urb = urbp->urb;
- if (urb->status != -EINPROGRESS) {
+ if (urb->unlinked) {
/* Fix up the TD links and save the toggles for
* non-Isochronous queues. For Isochronous queues,
qh->is_stopped = 0;
return;
}
- uhci_giveback_urb(uhci, qh, urb);
+ uhci_giveback_urb(uhci, qh, urb, 0);
goto restart;
}
}