urbp->fsbr = 1;
}
-static void uhci_qh_wants_fsbr(struct uhci_hcd *uhci, struct uhci_qh *qh)
+static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
{
- struct urb_priv *urbp =
- list_entry(qh->queue.next, struct urb_priv, node);
-
if (urbp->fsbr) {
- uhci->fsbr_jiffies = jiffies;
+ uhci->fsbr_is_wanted = 1;
if (!uhci->fsbr_is_on)
uhci_fsbr_on(uhci);
+ else if (uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 0;
+ del_timer(&uhci->fsbr_timer);
+ }
+ }
+}
+
+static void uhci_fsbr_timeout(unsigned long _uhci)
+{
+ struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci;
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ if (uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 0;
+ uhci_fsbr_off(uhci);
}
+ spin_unlock_irqrestore(&uhci->lock, flags);
}
td->frame = -1;
}
+static inline void uhci_remove_tds_from_frame(struct uhci_hcd *uhci,
+ unsigned int framenum)
+{
+ struct uhci_td *ftd, *ltd;
+
+ framenum &= (UHCI_NUMFRAMES - 1);
+
+ ftd = uhci->frame_cpu[framenum];
+ if (ftd) {
+ ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+ uhci->frame[framenum] = ltd->link;
+ uhci->frame_cpu[framenum] = NULL;
+
+ while (!list_empty(&ftd->fl_list))
+ list_del_init(ftd->fl_list.prev);
+ }
+}
+
/*
* Remove all the TDs for an Isochronous URB from the frame list
*/
if (qh->type == USB_ENDPOINT_XFER_ISOC) {
ret = (uhci->frame_number + uhci->is_stopped !=
qh->unlink_frame);
- return ret;
+ goto done;
}
/* If the URB isn't first on its queue, adjust the link pointer
td = list_entry(urbp->td_list.prev, struct uhci_td,
list);
ptd->link = td->link;
- return ret;
+ goto done;
}
/* If the QH element pointer is UHCI_PTR_TERM then then currently
* executing URB has already been unlinked, so this one isn't it. */
if (qh_element(qh) == UHCI_PTR_TERM)
- return ret;
+ goto done;
qh->element = UHCI_PTR_TERM;
/* Control pipes have to worry about toggles */
if (qh->type == USB_ENDPOINT_XFER_CONTROL)
- return ret;
+ goto done;
/* Save the next toggle value */
WARN_ON(list_empty(&urbp->td_list));
td = list_entry(urbp->td_list.next, struct uhci_td, list);
qh->needs_fixup = 1;
qh->initial_toggle = uhci_toggle(td_token(td));
+
+done:
return ret;
}
return -ENOSR;
if (status & TD_CTRL_STALLED) /* Stalled */
return -EPIPE;
- WARN_ON(status & TD_CTRL_ACTIVE); /* Active */
return 0;
}
uhci_packetout(td_token(td)));
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
/* Some debugging code */
- dev_dbg(uhci_dev(uhci),
+ dev_dbg(&urb->dev->dev,
"%s: failed with status %x\n",
__FUNCTION__, status);
return -EFBIG;
/* Check the period and figure out the starting frame number */
- uhci_get_current_frame_number(uhci);
if (qh->period == 0) {
if (urb->transfer_flags & URB_ISO_ASAP) {
+ uhci_get_current_frame_number(uhci);
urb->start_frame = uhci->frame_number + 10;
} else {
- i = urb->start_frame - uhci->frame_number;
+ i = urb->start_frame - uhci->last_iso_frame;
if (i <= 0 || i >= UHCI_NUMFRAMES)
return -EINVAL;
}
} else { /* Pick up where the last URB leaves off */
if (list_empty(&qh->queue)) {
- frame = uhci->frame_number + 10;
+ frame = qh->iso_frame;
} else {
struct urb *lurb;
}
if (urb->transfer_flags & URB_ISO_ASAP)
urb->start_frame = frame;
- /* FIXME: Sanity check */
+ else if (urb->start_frame != frame)
+ return -EINVAL;
}
/* Make sure we won't have to go too far into the future */
- if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES,
+ if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES,
urb->start_frame + urb->number_of_packets *
urb->interval))
return -EFBIG;
frame = urb->start_frame;
list_for_each_entry(td, &urbp->td_list, list) {
uhci_insert_td_in_frame_list(uhci, td, frame);
- frame += urb->interval;
+ frame += qh->period;
+ }
+
+ if (list_empty(&qh->queue)) {
+ qh->iso_packet_desc = &urb->iso_frame_desc[0];
+ qh->iso_frame = urb->start_frame;
+ qh->iso_status = 0;
}
return 0;
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
- struct uhci_td *td;
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- int status;
- int i, ret = 0;
-
- urb->actual_length = urb->error_count = 0;
+ struct uhci_td *td, *tmp;
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_qh *qh = urbp->qh;
- i = 0;
- list_for_each_entry(td, &urbp->td_list, list) {
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ unsigned int ctrlstat;
+ int status;
int actlength;
- unsigned int ctrlstat = td_status(td);
- if (ctrlstat & TD_CTRL_ACTIVE)
+ if (uhci_frame_before_eq(uhci->cur_iso_frame, qh->iso_frame))
return -EINPROGRESS;
- actlength = uhci_actual_length(ctrlstat);
- urb->iso_frame_desc[i].actual_length = actlength;
- urb->actual_length += actlength;
+ uhci_remove_tds_from_frame(uhci, qh->iso_frame);
+
+ ctrlstat = td_status(td);
+ if (ctrlstat & TD_CTRL_ACTIVE) {
+ status = -EXDEV; /* TD was added too late? */
+ } else {
+ status = uhci_map_status(uhci_status_bits(ctrlstat),
+ usb_pipeout(urb->pipe));
+ actlength = uhci_actual_length(ctrlstat);
+
+ urb->actual_length += actlength;
+ qh->iso_packet_desc->actual_length = actlength;
+ qh->iso_packet_desc->status = status;
+ }
- status = uhci_map_status(uhci_status_bits(ctrlstat),
- usb_pipeout(urb->pipe));
- urb->iso_frame_desc[i].status = status;
if (status) {
urb->error_count++;
- ret = status;
+ qh->iso_status = status;
}
- i++;
+ uhci_remove_td_from_urbp(td);
+ uhci_free_td(uhci, td);
+ qh->iso_frame += qh->period;
+ ++qh->iso_packet_desc;
}
-
- return ret;
+ return qh->iso_status;
}
static int uhci_urb_enqueue(struct usb_hcd *hcd,
}
break;
case USB_ENDPOINT_XFER_ISOC:
+ urb->error_count = 0;
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0) {
ret = bustime;
* queue isn't stopped. */
if (qh->queue.next == &urbp->node && !qh->is_stopped) {
uhci_activate_qh(uhci, qh);
- uhci_qh_wants_fsbr(uhci, qh);
+ uhci_urbp_wants_fsbr(uhci, urbp);
}
goto done;
{
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
- /* Isochronous TDs get unlinked directly from the frame list */
- if (qh->type == USB_ENDPOINT_XFER_ISOC)
- uhci_unlink_isochronous_tds(uhci, urb);
+ /* When giving back the first URB in an Isochronous queue,
+ * reinitialize the QH's iso-related members for the next URB. */
+ if (qh->type == USB_ENDPOINT_XFER_ISOC &&
+ urbp->node.prev == &qh->queue &&
+ urbp->node.next != &qh->queue) {
+ struct urb *nurb = list_entry(urbp->node.next,
+ struct urb_priv, node)->urb;
+
+ qh->iso_packet_desc = &nurb->iso_frame_desc[0];
+ qh->iso_frame = nurb->start_frame;
+ qh->iso_status = 0;
+ }
/* Take the URB off the QH's queue. If the queue is now empty,
* this is a perfect time for a toggle fixup. */
unsigned status;
if (qh->type == USB_ENDPOINT_XFER_ISOC)
- return ret;
+ goto done;
/* Treat an UNLINKING queue as though it hasn't advanced.
* This is okay because reactivation will treat it as though
/* We're okay, the queue has advanced */
qh->wait_expired = 0;
qh->advance_jiffies = jiffies;
- return ret;
+ goto done;
}
ret = 0;
}
/* The queue hasn't advanced; check for timeout */
- if (!qh->wait_expired && time_after(jiffies,
- qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
+ if (qh->wait_expired)
+ goto done;
+
+ if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
/* Detect the Intel bug and work around it */
if (qh->post_td && qh_element(qh) ==
cpu_to_le32(qh->post_td->dma_handle)) {
qh->element = qh->post_td->link;
qh->advance_jiffies = jiffies;
- return 1;
+ ret = 1;
+ goto done;
}
qh->wait_expired = 1;
* starts moving again. */
if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))
uhci_unlink_qh(uhci, qh);
+
+ } else {
+ /* Unmoving but not-yet-expired queues keep FSBR alive */
+ if (urbp)
+ uhci_urbp_wants_fsbr(uhci, urbp);
}
+
+done:
return ret;
}
uhci->scan_in_progress = 1;
rescan:
uhci->need_rescan = 0;
+ uhci->fsbr_is_wanted = 0;
uhci_clear_next_interrupt(uhci);
uhci_get_current_frame_number(uhci);
+ uhci->cur_iso_frame = uhci->frame_number;
/* Go through all the QH queues and process the URBs in each one */
for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {
if (uhci_advance_check(uhci, qh)) {
uhci_scan_qh(uhci, qh, regs);
- if (qh->state == QH_STATE_ACTIVE)
- uhci_qh_wants_fsbr(uhci, qh);
+ if (qh->state == QH_STATE_ACTIVE) {
+ uhci_urbp_wants_fsbr(uhci,
+ list_entry(qh->queue.next, struct urb_priv, node));
+ }
}
}
}
+ uhci->last_iso_frame = uhci->cur_iso_frame;
if (uhci->need_rescan)
goto rescan;
uhci->scan_in_progress = 0;
- if (uhci->fsbr_is_on && time_after(jiffies,
- uhci->fsbr_jiffies + FSBR_OFF_DELAY))
- uhci_fsbr_off(uhci);
+ if (uhci->fsbr_is_on && !uhci->fsbr_is_wanted &&
+ !uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 1;
+ mod_timer(&uhci->fsbr_timer, jiffies + FSBR_OFF_DELAY);
+ }
if (list_empty(&uhci->skel_unlink_qh->node))
uhci_clear_next_interrupt(uhci);