Skip to content

Commit

Permalink
[PATCH] USB: ehci-hcd - fix page pointer allocation in itd_patch()
Browse files Browse the repository at this point in the history
The itd_patch() function is responsible for allocating entries in the
buffer page pointer list of the iTD.  Particularly, a new page pointer
is needed every time when buffer data crosses a page boundary.

However, there is a bug in the allocation logic: the function does not
allocate a new entry when the current transaction is the first
transaction in the iTD (as indicated by first!=0).

The consequence is that, when the data of the first transaction begins
somewhere at the end of a page so that it actually does cross the page
boundary, no new page pointer is allocated.  This means that the data
at the end of the first transaction (beyond the page boundary) will be
accessed by the HC using the second page pointer, which is zero.
Furthermore, the first page pointer will be later overwritten by the
page pointers of the other transactions, which will garble it because
the value is or-ed into the iTD field.

All this particular check (for !first) does is cause incorrect
behaviour, so it should be entirely removed (and with it the variable
first that is not used for anything else).

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
David Brownell authored and Greg Kroah-Hartman committed Jun 27, 2005
1 parent e07fefa commit 7707857
Showing 1 changed file with 7 additions and 10 deletions.
17 changes: 7 additions & 10 deletions drivers/usb/host/ehci-sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -637,9 +637,8 @@ iso_stream_alloc (int mem_flags)
{
struct ehci_iso_stream *stream;

stream = kmalloc(sizeof *stream, mem_flags);
stream = kcalloc(1, sizeof *stream, mem_flags);
if (likely (stream != NULL)) {
memset (stream, 0, sizeof(*stream));
INIT_LIST_HEAD(&stream->td_list);
INIT_LIST_HEAD(&stream->free_list);
stream->next_uframe = -1;
Expand Down Expand Up @@ -894,7 +893,7 @@ itd_sched_init (
trans |= length << 16;
uframe->transaction = cpu_to_le32 (trans);

/* might need to cross a buffer page within a td */
/* might need to cross a buffer page within a uframe */
uframe->bufp = (buf & ~(u64)0x0fff);
buf += length;
if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff))))
Expand Down Expand Up @@ -1194,6 +1193,7 @@ itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
{
int i;

/* it's been recently zeroed */
itd->hw_next = EHCI_LIST_END;
itd->hw_bufp [0] = stream->buf0;
itd->hw_bufp [1] = stream->buf1;
Expand All @@ -1210,8 +1210,7 @@ itd_patch (
struct ehci_itd *itd,
struct ehci_iso_sched *iso_sched,
unsigned index,
u16 uframe,
int first
u16 uframe
)
{
struct ehci_iso_packet *uf = &iso_sched->packet [index];
Expand All @@ -1228,7 +1227,7 @@ itd_patch (
itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32));

/* iso_frame_desc[].offset must be strictly increasing */
if (unlikely (!first && uf->cross)) {
if (unlikely (uf->cross)) {
u64 bufp = uf->bufp + 4096;
itd->pg = ++pg;
itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0);
Expand Down Expand Up @@ -1257,7 +1256,7 @@ itd_link_urb (
struct ehci_iso_stream *stream
)
{
int packet, first = 1;
int packet;
unsigned next_uframe, uframe, frame;
struct ehci_iso_sched *iso_sched = urb->hcpriv;
struct ehci_itd *itd;
Expand Down Expand Up @@ -1290,16 +1289,14 @@ itd_link_urb (
list_move_tail (&itd->itd_list, &stream->td_list);
itd->stream = iso_stream_get (stream);
itd->urb = usb_get_urb (urb);
first = 1;
itd_init (stream, itd);
}

uframe = next_uframe & 0x07;
frame = next_uframe >> 3;

itd->usecs [uframe] = stream->usecs;
itd_patch (itd, iso_sched, packet, uframe, first);
first = 0;
itd_patch (itd, iso_sched, packet, uframe);

next_uframe += stream->interval;
stream->depth += stream->interval;
Expand Down

0 comments on commit 7707857

Please sign in to comment.