return out - buf;
}
-static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
+static int uhci_show_qh(struct uhci_hcd *uhci,
+ struct uhci_qh *qh, char *buf, int len, int space)
{
char *out = buf;
int i, nurbs;
if (list_empty(&qh->queue)) {
out += sprintf(out, "%*s queue is empty\n", space, "");
+ if (qh == uhci->skel_async_qh)
+ out += uhci_show_td(uhci->term_td, out,
+ len - (out - buf), 0);
} else {
struct urb_priv *urbp = list_entry(qh->queue.next,
struct urb_priv, node);
struct list_head *tmp, *head;
int nframes, nerrs;
__le32 link;
+ __le32 fsbr_link;
static const char * const qh_names[] = {
"unlink", "iso", "int128", "int64", "int32", "int16",
out += sprintf(out, "Skeleton QHs\n");
+ fsbr_link = 0;
for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
int cnt = 0;
- __le32 fsbr_link = 0;
qh = uhci->skelqh[i];
out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); \
- out += uhci_show_qh(qh, out, len - (out - buf), 4);
+ out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4);
/* Last QH is the Terminating QH, it's different */
if (i == SKEL_TERM) {
if (qh_element(qh) != LINK_TO_TD(uhci->term_td))
out += sprintf(out, " skel_term_qh element is not set to term_td!\n");
- if (link == LINK_TO_QH(uhci->skel_term_qh))
- goto check_qh_link;
- continue;
+ link = fsbr_link;
+ if (!link)
+ link = LINK_TO_QH(uhci->skel_term_qh);
+ goto check_qh_link;
}
head = &qh->node;
qh = list_entry(tmp, struct uhci_qh, node);
tmp = tmp->next;
if (++cnt <= 10)
- out += uhci_show_qh(qh, out,
+ out += uhci_show_qh(uhci, qh, out,
len - (out - buf), 4);
if (!fsbr_link && qh->skel >= SKEL_FSBR)
fsbr_link = LINK_TO_QH(qh);
link = LINK_TO_QH(uhci->skel_async_qh);
else if (!uhci->fsbr_is_on)
;
- else if (fsbr_link)
- link = fsbr_link;
else
link = LINK_TO_QH(uhci->skel_term_qh);
check_qh_link:
static inline void lprintk(char *buf)
{}
-static inline int uhci_show_qh(struct uhci_qh *qh, char *buf,
- int len, int space)
+static inline int uhci_show_qh(struct uhci_hcd *uhci,
+ struct uhci_qh *qh, char *buf, int len, int space)
{
return 0;
}
*/
static void uhci_fsbr_on(struct uhci_hcd *uhci)
{
- struct uhci_qh *fsbr_qh, *lqh, *tqh;
+ struct uhci_qh *lqh;
+ /* The terminating skeleton QH always points back to the first
+ * FSBR QH. Make the last async QH point to the terminating
+ * skeleton QH. */
uhci->fsbr_is_on = 1;
lqh = list_entry(uhci->skel_async_qh->node.prev,
struct uhci_qh, node);
-
- /* Find the first FSBR QH. Linear search through the list is
- * acceptable because normally FSBR gets turned on as soon as
- * one QH needs it. */
- fsbr_qh = NULL;
- list_for_each_entry_reverse(tqh, &uhci->skel_async_qh->node, node) {
- if (tqh->skel < SKEL_FSBR)
- break;
- fsbr_qh = tqh;
- }
-
- /* No FSBR QH means we must insert the terminating skeleton QH */
- if (!fsbr_qh) {
- uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);
- wmb();
- lqh->link = uhci->skel_term_qh->link;
-
- /* Otherwise loop the last QH to the first FSBR QH */
- } else
- lqh->link = LINK_TO_QH(fsbr_qh);
+ lqh->link = LINK_TO_QH(uhci->skel_term_qh);
}
static void uhci_fsbr_off(struct uhci_hcd *uhci)
{
struct uhci_qh *lqh;
+ /* Remove the link from the last async QH to the terminating
+ * skeleton QH. */
uhci->fsbr_is_on = 0;
lqh = list_entry(uhci->skel_async_qh->node.prev,
struct uhci_qh, node);
-
- /* End the async list normally and unlink the terminating QH */
- lqh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM;
+ lqh->link = UHCI_PTR_TERM;
}
static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
*/
static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
- struct uhci_qh *pqh, *lqh;
+ struct uhci_qh *pqh;
__le32 link_to_new_qh;
- __le32 *extra_link = &link_to_new_qh;
/* Find the predecessor QH for our new one and insert it in the list.
* The list of QHs is expected to be short, so linear search won't
break;
}
list_add(&qh->node, &pqh->node);
- qh->link = pqh->link;
-
- link_to_new_qh = LINK_TO_QH(qh);
-
- /* If this is now the first FSBR QH, take special action */
- if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR &&
- qh->skel >= SKEL_FSBR) {
- lqh = list_entry(uhci->skel_async_qh->node.prev,
- struct uhci_qh, node);
-
- /* If the new QH is also the last one, we must unlink
- * the terminating skeleton QH and make the new QH point
- * back to itself. */
- if (qh == lqh) {
- qh->link = link_to_new_qh;
- extra_link = &uhci->skel_term_qh->link;
-
- /* Otherwise the last QH must point to the new QH */
- } else
- extra_link = &lqh->link;
- }
/* Link it into the schedule */
+ qh->link = pqh->link;
wmb();
- *extra_link = pqh->link = link_to_new_qh;
+ link_to_new_qh = LINK_TO_QH(qh);
+ pqh->link = link_to_new_qh;
+
+ /* If this is now the first FSBR QH, link the terminating skeleton
+ * QH to it. */
+ if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
+ uhci->skel_term_qh->link = link_to_new_qh;
}
/*
*/
static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
- struct uhci_qh *pqh, *lqh;
+ struct uhci_qh *pqh;
__le32 link_to_next_qh = qh->link;
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
-
- /* If this is the first FSBQ QH, take special action */
- if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR &&
- qh->skel >= SKEL_FSBR) {
- lqh = list_entry(uhci->skel_async_qh->node.prev,
- struct uhci_qh, node);
-
- /* If this QH is also the last one, we must link in
- * the terminating skeleton QH. */
- if (qh == lqh) {
- link_to_next_qh = LINK_TO_QH(uhci->skel_term_qh);
- uhci->skel_term_qh->link = link_to_next_qh;
- wmb();
- qh->link = link_to_next_qh;
-
- /* Otherwise the last QH must point to the new first FSBR QH */
- } else
- lqh->link = link_to_next_qh;
- }
-
pqh->link = link_to_next_qh;
+
+ /* If this was the old first FSBR QH, link the terminating skeleton
+ * QH to the next (new first FSBR) QH. */
+ if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
+ uhci->skel_term_qh->link = link_to_next_qh;
mb();
}
if (debug > 1 && errbuf) {
/* Print the chain for debugging */
- uhci_show_qh(urbp->qh, errbuf,
+ uhci_show_qh(uhci, urbp->qh, errbuf,
ERRBUF_LEN, 0);
lprintk(errbuf);
}