# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.598 -> 1.599 # drivers/usb/uhci.h 1.9 -> 1.10 # drivers/usb/uhci.c 1.29 -> 1.30 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/08/20 greg@kroah.com 1.599 # USB: uhci incorrect bit operations and FSBR timeout fixes. # -------------------------------------------- # diff -Nru a/drivers/usb/uhci.c b/drivers/usb/uhci.c --- a/drivers/usb/uhci.c Wed Aug 21 11:47:11 2002 +++ b/drivers/usb/uhci.c Wed Aug 21 11:47:11 2002 @@ -100,6 +100,11 @@ #define IDLE_TIMEOUT (HZ / 20) /* 50 ms */ #define FSBR_DELAY (HZ / 20) /* 50 ms */ +/* When we timeout an idle transfer for FSBR, we'll switch it over to */ +/* depth first traversal. We'll do it in groups of this number of TD's */ +/* to make sure it doesn't hog all of the bandwidth */ +#define DEPTH_INTERVAL 5 + #define MAX_URB_LOOP 2048 /* Maximum number of linked URB's */ /* @@ -115,12 +120,20 @@ return 0; } +/* + * Technically, updating td->status here is a race, but it's not really a + * problem. The worst that can happen is that we set the IOC bit again + * generating a spurios interrupt. We could fix this by creating another + * QH and leaving the IOC bit always set, but then we would have to play + * games with the FSBR code to make sure we get the correct order in all + * the cases. I don't think it's worth the effort + */ static inline void uhci_set_next_interrupt(struct uhci *uhci) { unsigned long flags; spin_lock_irqsave(&uhci->frame_list_lock, flags); - set_bit(TD_CTRL_IOC_BIT, &uhci->skel_term_td->status); + uhci->skel_term_td->status |= TD_CTRL_IOC; spin_unlock_irqrestore(&uhci->frame_list_lock, flags); } @@ -129,7 +142,7 @@ unsigned long flags; spin_lock_irqsave(&uhci->frame_list_lock, flags); - clear_bit(TD_CTRL_IOC_BIT, &uhci->skel_term_td->status); + uhci->skel_term_td->status &= ~TD_CTRL_IOC; spin_unlock_irqrestore(&uhci->frame_list_lock, flags); } @@ -474,9 +487,9 @@ tmp = tmp->next; if (toggle) - set_bit(TD_TOKEN_TOGGLE, &td->info); + td->info |= TD_TOKEN_TOGGLE; else - clear_bit(TD_TOKEN_TOGGLE, &td->info); + td->info &= ~TD_TOKEN_TOGGLE; toggle ^= 1; } @@ -857,7 +870,7 @@ return -ENOMEM; /* Alternate Data0/1 (start with Data1) */ - destination ^= 1 << TD_TOKEN_TOGGLE; + destination ^= TD_TOKEN_TOGGLE; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | ((pktsze - 1) << 21), @@ -884,7 +897,7 @@ else destination |= USB_PID_OUT; - destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ + destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ status &= ~TD_CTRL_SPD; @@ -953,14 +966,6 @@ tmp = tmp->next; - if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) && - !(td->status & TD_CTRL_ACTIVE)) { - uhci_inc_fsbr(urb->dev->bus->hcpriv, urb); - urbp->fsbr_timeout = 0; - urbp->fsbrtime = jiffies; - clear_bit(TD_CTRL_IOC_BIT, &td->status); - } - status = uhci_status_bits(td->status); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -1097,7 +1102,7 @@ if (!td) return -ENOMEM; - destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); + destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT); destination |= ((urb->transfer_buffer_length - 1) << 21); usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); @@ -1127,14 +1132,6 @@ tmp = tmp->next; - if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) && - !(td->status & TD_CTRL_ACTIVE)) { - uhci_inc_fsbr(urb->dev->bus->hcpriv, urb); - urbp->fsbr_timeout = 0; - urbp->fsbrtime = jiffies; - clear_bit(TD_CTRL_IOC_BIT, &td->status); - } - status = uhci_status_bits(td->status); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -1198,8 +1195,8 @@ td = list_entry(urbp->td_list.next, struct uhci_td, list); td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; - td->info &= ~(1 << TD_TOKEN_TOGGLE); - td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); + td->info &= ~TD_TOKEN_TOGGLE; + td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT); usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); out: @@ -1255,7 +1252,7 @@ uhci_fill_td(td, status, destination | (((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), data); data += pktsze; @@ -1283,7 +1280,7 @@ uhci_fill_td(td, status, destination | (UHCI_NULL_DATA_SIZE << 21) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), data); usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), @@ -1830,11 +1827,18 @@ { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct list_head *head, *tmp; + int count = 0; uhci_dec_fsbr(uhci, urb); urbp->fsbr_timeout = 1; + /* + * Ideally we would want to fix qh->element as well, but it's + * read/write by the HC, so that can introduce a race. It's not + * really worth the hassle + */ + head = &urbp->td_list; tmp = head->next; while (tmp != head) { @@ -1842,10 +1846,15 @@ tmp = tmp->next; - if (td->status & TD_CTRL_ACTIVE) { - set_bit(TD_CTRL_IOC_BIT, &td->status); - break; - } + /* + * Make sure we don't do the last one (since it'll have the + * TERM bit set) as well as we skip every so many TD's to + * make sure it doesn't hog the bandwidth + */ + if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1)) + td->link |= UHCI_PTR_DEPTH; + + count++; } return 0; diff -Nru a/drivers/usb/uhci.h b/drivers/usb/uhci.h --- a/drivers/usb/uhci.h Wed Aug 21 11:47:11 2002 +++ b/drivers/usb/uhci.h Wed Aug 21 11:47:11 2002 @@ -100,7 +100,6 @@ #define TD_CTRL_C_ERR_SHIFT 27 #define TD_CTRL_LS (1 << 26) /* Low Speed Device */ #define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ -#define TD_CTRL_IOC_BIT 24 #define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ #define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ #define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ @@ -120,13 +119,14 @@ /* * for TD : (a.k.a. Token) */ -#define TD_TOKEN_TOGGLE 19 +#define TD_TOKEN_TOGGLE_SHIFT 19 +#define TD_TOKEN_TOGGLE (1 << 19) #define TD_TOKEN_PID_MASK 0xFF #define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */ #define uhci_maxlen(token) ((token) >> 21) #define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */ -#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1) #define uhci_endpoint(token) (((token) >> 15) & 0xf) #define uhci_devaddr(token) (((token) >> 8) & 0x7f) #define uhci_devep(token) (((token) >> 8) & 0x7ff)