diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-01-08 12:00:28 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 18:44:35 -0500 |
commit | f3fe239b67424d88104e32076aec902c0642925f (patch) | |
tree | d7b3986dcdcb434111c0570eb2a129b2570e3bfa /drivers/usb | |
parent | f38649fee955c19f4df9b9e7267f87702712d973 (diff) |
UHCI: improved debugging checks for the frame list
This patch (as768) improves the debugging checks for the uhci-hcd
frame list. The number of entries displayed is limited to 10, and the
driver now checks for the correct Skeleton QH link value at the end of
each chain of Isochronous TDs. The code to compute these link values
is now used in two spots, so it is moved into its own separate
subroutine.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/uhci-debug.c | 48 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 51 |
2 files changed, 72 insertions, 27 deletions
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index e345f15b7d87..b40bc1ac9b8c 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c | |||
@@ -347,6 +347,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) | |||
347 | struct uhci_qh *qh; | 347 | struct uhci_qh *qh; |
348 | struct uhci_td *td; | 348 | struct uhci_td *td; |
349 | struct list_head *tmp, *head; | 349 | struct list_head *tmp, *head; |
350 | int nframes, nerrs; | ||
350 | 351 | ||
351 | out += uhci_show_root_hub_state(uhci, out, len - (out - buf)); | 352 | out += uhci_show_root_hub_state(uhci, out, len - (out - buf)); |
352 | out += sprintf(out, "HC status\n"); | 353 | out += sprintf(out, "HC status\n"); |
@@ -355,23 +356,60 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) | |||
355 | return out - buf; | 356 | return out - buf; |
356 | 357 | ||
357 | out += sprintf(out, "Frame List\n"); | 358 | out += sprintf(out, "Frame List\n"); |
359 | nframes = 10; | ||
360 | nerrs = 0; | ||
358 | for (i = 0; i < UHCI_NUMFRAMES; ++i) { | 361 | for (i = 0; i < UHCI_NUMFRAMES; ++i) { |
362 | __le32 link, qh_dma; | ||
363 | |||
364 | j = 0; | ||
359 | td = uhci->frame_cpu[i]; | 365 | td = uhci->frame_cpu[i]; |
366 | link = uhci->frame[i]; | ||
360 | if (!td) | 367 | if (!td) |
361 | continue; | 368 | goto check_link; |
362 | 369 | ||
363 | out += sprintf(out, "- Frame %d\n", i); \ | 370 | if (nframes > 0) { |
364 | if (td->dma_handle != (dma_addr_t)uhci->frame[i]) | 371 | out += sprintf(out, "- Frame %d -> (%08x)\n", |
365 | out += sprintf(out, " frame list does not match td->dma_handle!\n"); | 372 | i, le32_to_cpu(link)); |
373 | j = 1; | ||
374 | } | ||
366 | 375 | ||
367 | head = &td->fl_list; | 376 | head = &td->fl_list; |
368 | tmp = head; | 377 | tmp = head; |
369 | do { | 378 | do { |
370 | td = list_entry(tmp, struct uhci_td, fl_list); | 379 | td = list_entry(tmp, struct uhci_td, fl_list); |
371 | tmp = tmp->next; | 380 | tmp = tmp->next; |
372 | out += uhci_show_td(td, out, len - (out - buf), 4); | 381 | if (cpu_to_le32(td->dma_handle) != link) { |
382 | if (nframes > 0) | ||
383 | out += sprintf(out, " link does " | ||
384 | "not match list entry!\n"); | ||
385 | else | ||
386 | ++nerrs; | ||
387 | } | ||
388 | if (nframes > 0) | ||
389 | out += uhci_show_td(td, out, | ||
390 | len - (out - buf), 4); | ||
391 | link = td->link; | ||
373 | } while (tmp != head); | 392 | } while (tmp != head); |
393 | |||
394 | check_link: | ||
395 | qh_dma = uhci_frame_skel_link(uhci, i); | ||
396 | if (link != qh_dma) { | ||
397 | if (nframes > 0) { | ||
398 | if (!j) { | ||
399 | out += sprintf(out, | ||
400 | "- Frame %d -> (%08x)\n", | ||
401 | i, le32_to_cpu(link)); | ||
402 | j = 1; | ||
403 | } | ||
404 | out += sprintf(out, " link does not match " | ||
405 | "QH (%08x)!\n", le32_to_cpu(qh_dma)); | ||
406 | } else | ||
407 | ++nerrs; | ||
408 | } | ||
409 | nframes -= j; | ||
374 | } | 410 | } |
411 | if (nerrs > 0) | ||
412 | out += sprintf(out, "Skipped %d bad links\n", nerrs); | ||
375 | 413 | ||
376 | out += sprintf(out, "Skeleton QHs\n"); | 414 | out += sprintf(out, "Skeleton QHs\n"); |
377 | 415 | ||
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index e0d4c2358b39..49b9d390b95f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -92,6 +92,34 @@ static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state); | |||
92 | static void wakeup_rh(struct uhci_hcd *uhci); | 92 | static void wakeup_rh(struct uhci_hcd *uhci); |
93 | static void uhci_get_current_frame_number(struct uhci_hcd *uhci); | 93 | static void uhci_get_current_frame_number(struct uhci_hcd *uhci); |
94 | 94 | ||
95 | /* | ||
96 | * Calculate the link pointer DMA value for the first Skeleton QH in a frame. | ||
97 | */ | ||
98 | static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) | ||
99 | { | ||
100 | int skelnum; | ||
101 | |||
102 | /* | ||
103 | * The interrupt queues will be interleaved as evenly as possible. | ||
104 | * There's not much to be done about period-1 interrupts; they have | ||
105 | * to occur in every frame. But we can schedule period-2 interrupts | ||
106 | * in odd-numbered frames, period-4 interrupts in frames congruent | ||
107 | * to 2 (mod 4), and so on. This way each frame only has two | ||
108 | * interrupt QHs, which will help spread out bandwidth utilization. | ||
109 | * | ||
110 | * ffs (Find First bit Set) does exactly what we need: | ||
111 | * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], | ||
112 | * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. | ||
113 | * ffs >= 7 => not on any high-period queue, so use | ||
114 | * skel_int1_qh = skelqh[9]. | ||
115 | * Add in UHCI_NUMFRAMES to insure at least one bit is set. | ||
116 | */ | ||
117 | skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES); | ||
118 | if (skelnum <= 1) | ||
119 | skelnum = 9; | ||
120 | return UHCI_PTR_QH | cpu_to_le32(uhci->skelqh[skelnum]->dma_handle); | ||
121 | } | ||
122 | |||
95 | #include "uhci-debug.c" | 123 | #include "uhci-debug.c" |
96 | #include "uhci-q.c" | 124 | #include "uhci-q.c" |
97 | #include "uhci-hub.c" | 125 | #include "uhci-hub.c" |
@@ -631,32 +659,11 @@ static int uhci_start(struct usb_hcd *hcd) | |||
631 | /* | 659 | /* |
632 | * Fill the frame list: make all entries point to the proper | 660 | * Fill the frame list: make all entries point to the proper |
633 | * interrupt queue. | 661 | * interrupt queue. |
634 | * | ||
635 | * The interrupt queues will be interleaved as evenly as possible. | ||
636 | * There's not much to be done about period-1 interrupts; they have | ||
637 | * to occur in every frame. But we can schedule period-2 interrupts | ||
638 | * in odd-numbered frames, period-4 interrupts in frames congruent | ||
639 | * to 2 (mod 4), and so on. This way each frame only has two | ||
640 | * interrupt QHs, which will help spread out bandwidth utilization. | ||
641 | */ | 662 | */ |
642 | for (i = 0; i < UHCI_NUMFRAMES; i++) { | 663 | for (i = 0; i < UHCI_NUMFRAMES; i++) { |
643 | int irq; | ||
644 | |||
645 | /* | ||
646 | * ffs (Find First bit Set) does exactly what we need: | ||
647 | * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], | ||
648 | * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. | ||
649 | * ffs >= 7 => not on any high-period queue, so use | ||
650 | * skel_int1_qh = skelqh[9]. | ||
651 | * Add UHCI_NUMFRAMES to insure at least one bit is set. | ||
652 | */ | ||
653 | irq = 8 - (int) __ffs(i + UHCI_NUMFRAMES); | ||
654 | if (irq <= 1) | ||
655 | irq = 9; | ||
656 | 664 | ||
657 | /* Only place we don't use the frame list routines */ | 665 | /* Only place we don't use the frame list routines */ |
658 | uhci->frame[i] = UHCI_PTR_QH | | 666 | uhci->frame[i] = uhci_frame_skel_link(uhci, i); |
659 | cpu_to_le32(uhci->skelqh[irq]->dma_handle); | ||
660 | } | 667 | } |
661 | 668 | ||
662 | /* | 669 | /* |