# 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.588 -> 1.588.1.1 # drivers/usb/host/ehci-dbg.c 1.3 -> 1.4 # drivers/usb/host/ehci.h 1.3 -> 1.4 # drivers/usb/host/ehci-sched.c 1.12 -> 1.13 # drivers/usb/core/hcd.h 1.7 -> 1.8 # drivers/usb/core/hcd.c 1.20 -> 1.21 # drivers/usb/host/ehci-hcd.c 1.17 -> 1.18 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/05/26 david-b@pacbell.net 1.588.1.1 # [PATCH] ehci split interrupt transactions # # This patch lets more devices hook up to USB 2.0 hubs, stuff # like keyboards, mice, hubs that hasn't worked yet: # # - schedules full/low speed interrupt transactions # - tracks CSPLIT bandwidth for full/low speed interrupt # transactions # - moves some bus bandwidth calculation out of the EHCI code # - makes the bandwidth calculation primitive public, and # adds kerneldoc for it # # It still takes a scheduling shortcut, placing at most one # interrupt transaction in a frame (vs potentially over 100), # but it should do for now. # -------------------------------------------- # diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c Tue May 28 23:49:09 2002 +++ b/drivers/usb/core/hcd.c Tue May 28 23:49:09 2002 @@ -745,12 +745,18 @@ /*-------------------------------------------------------------------------*/ -/* - * usb_calc_bus_time: +/** + * usb_calc_bus_time: approximate periodic transaction time in nanoseconds + * @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH} + * @is_input: true iff the transaction sends data to the host + * @is_isoc: true for isochronous transactions, false for interrupt ones + * @bytecount: how many bytes in the transaction. + * * Returns approximate bus time in nanoseconds for a periodic transaction. - * See USB 2.0 spec section 5.11.3 + * See USB 2.0 spec section 5.11.3; only periodic transfers need to be + * scheduled in software, this function is only used for such scheduling. */ -static long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) +long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) { unsigned long tmp; @@ -772,14 +778,18 @@ return (9107L + BW_HOST_DELAY + tmp); } case USB_SPEED_HIGH: /* ISOC or INTR */ - // FIXME merge from EHCI code; caller will need to handle - // each part of a split separately. - return 0; + // FIXME adjust for input vs output + if (isoc) + tmp = HS_USECS (bytecount); + else + tmp = HS_USECS_ISO (bytecount); + return tmp; default: dbg ("bogus device speed!"); return -1; } } +EXPORT_SYMBOL (usb_calc_bus_time); /* * usb_check_bandwidth(): diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h --- a/drivers/usb/core/hcd.h Tue May 28 23:49:09 2002 +++ b/drivers/usb/core/hcd.h Tue May 28 23:49:09 2002 @@ -263,6 +263,22 @@ extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); +/* + * Ceiling microseconds (typical) for that many bytes at high speed + * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed + * to preallocate bandwidth) + */ +#define USB2_HOST_DELAY 5 /* nsec, guess */ +#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \ + + ((2083UL * (3167 + BitTime (bytes)))/1000) \ + + USB2_HOST_DELAY) +#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \ + + ((2083UL * (3167 + BitTime (bytes)))/1000) \ + + USB2_HOST_DELAY) + +extern long usb_calc_bus_time (int speed, int is_input, + int isoc, int bytecount); + /*-------------------------------------------------------------------------*/ extern struct usb_bus *usb_alloc_bus (struct usb_operations *); diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c --- a/drivers/usb/host/ehci-dbg.c Tue May 28 23:49:09 2002 +++ b/drivers/usb/host/ehci-dbg.c Tue May 28 23:49:09 2002 @@ -102,8 +102,8 @@ #ifdef DEBUG -#if 0 -static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) +static void __attribute__((__unused__)) +dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) { dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label, qh, qh->hw_info1, qh->hw_info2, @@ -117,15 +117,13 @@ qh->hw_buf [4]); } } -#endif static const char *const fls_strings [] = { "1024", "512", "256", "??" }; #else -#if 0 -static inline void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {} -#endif +static inline void __attribute__((__unused__)) +dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {} #endif /* DEBUG */ /* functions have the "wrong" filename when they're output... */ diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Tue May 28 23:49:09 2002 +++ b/drivers/usb/host/ehci-hcd.c Tue May 28 23:49:09 2002 @@ -65,6 +65,7 @@ * * HISTORY: * + * 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts * 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other * missing pieces: enabling 64bit dma, handoff from BIOS/SMM. * 2002-05-07 Some error path cleanups to report better errors; wmb(); @@ -82,7 +83,7 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "2002-May-11" +#define DRIVER_VERSION "2002-May-24" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" @@ -622,7 +623,10 @@ return 0; case PIPE_INTERRUPT: - intr_deschedule (ehci, urb->start_frame, qh, urb->interval); + intr_deschedule (ehci, urb->start_frame, qh, + (urb->dev->speed == USB_SPEED_HIGH) + ? urb->interval + : (urb->interval << 3)); if (ehci->hcd.state == USB_STATE_HALT) urb->status = -ESHUTDOWN; qh_completions (ehci, qh, 1); diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c --- a/drivers/usb/host/ehci-sched.c Tue May 28 23:49:09 2002 +++ b/drivers/usb/host/ehci-sched.c Tue May 28 23:49:09 2002 @@ -33,19 +33,6 @@ * or with "USB On The Go" additions to USB 2.0 ...) */ -/* - * Ceiling microseconds (typical) for that many bytes at high speed - * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed - * to preallocate bandwidth) - */ -#define EHCI_HOST_DELAY 5 /* nsec, guess */ -#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \ - + ((2083UL * (3167 + BitTime (bytes)))/1000) \ - + EHCI_HOST_DELAY) -#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \ - + ((2083UL * (3167 + BitTime (bytes)))/1000) \ - + EHCI_HOST_DELAY) - static int ehci_get_frame (struct usb_hcd *hcd); /*-------------------------------------------------------------------------*/ @@ -124,6 +111,9 @@ /* is it in the S-mask? */ if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe)) usecs += q->qh->usecs; + /* ... or C-mask? */ + if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe))) + usecs += q->qh->c_usecs; q = &q->qh->qh_next; break; case Q_TYPE_FSTN: @@ -273,6 +263,12 @@ unsigned period, unsigned usecs ) { + /* complete split running into next frame? + * given FSTN support, we could sometimes check... + */ + if (uframe >= 8) + return 0; + /* * 80% periodic == 100 usec/uframe available * convert "usecs we need" to "max already claimed" @@ -284,6 +280,8 @@ // FIXME delete when intr_submit handles non-empty queues // this gives us a one intr/frame limit (vs N/uframe) +// ... and also lets us avoid tracking split transactions +// that might collide at a given TT/hub. if (ehci->pshadow [frame].ptr) return 0; @@ -305,20 +303,54 @@ int mem_flags ) { unsigned epnum, period; - unsigned short usecs; + unsigned short usecs, c_usecs, gap_uf; unsigned long flags; struct ehci_qh *qh; struct hcd_dev *dev; + int is_input; int status = 0; - /* get endpoint and transfer data */ + /* get endpoint and transfer/schedule data */ epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) + is_input = usb_pipein (urb->pipe); + if (is_input) epnum |= 0x10; - if (urb->dev->speed != USB_SPEED_HIGH) { - dbg ("no intr/tt scheduling yet"); - status = -ENOSYS; - goto done; + + /* + * HS interrupt transfers are simple -- only one microframe. FS/LS + * interrupt transfers involve a SPLIT in one microframe and CSPLIT + * sometime later. We need to know how much time each will be + * needed in each microframe and, for FS/LS, how many microframes + * separate the two in the best case. + */ + usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, + urb->transfer_buffer_length); + if (urb->dev->speed == USB_SPEED_HIGH) { + gap_uf = 0; + c_usecs = 0; + + /* FIXME handle HS periods of less than 1 frame. */ + period = urb->interval >> 3; + if (period < 1) { + dbg ("intr period %d uframes, NYET!", urb->interval); + status = -EINVAL; + goto done; + } + } else { + /* gap is a function of full/low speed transfer times */ + gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, is_input, 0, + urb->transfer_buffer_length) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { // SPLIT, gap, CSPLIT+DATA + c_usecs = usecs + HS_USECS (0); + usecs = HS_USECS (1); + } else { // SPLIT+DATA, gap, CSPLIT + usecs = usecs + HS_USECS (1); + c_usecs = HS_USECS (0); + } + + period = urb->interval; } /* @@ -339,16 +371,6 @@ goto done; } - usecs = HS_USECS (urb->transfer_buffer_length); - - /* FIXME handle HS periods of less than 1 frame. */ - period = urb->interval >> 3; - if (period < 1) { - dbg ("intr period %d uframes, NYET!", urb->interval); - status = -EINVAL; - goto done; - } - spin_lock_irqsave (&ehci->lock, flags); /* get the qh (must be empty and idle) */ @@ -392,6 +414,7 @@ qh->hw_next = EHCI_LIST_END; qh->usecs = usecs; + qh->c_usecs = c_usecs; urb->hcpriv = qh_get (qh); status = -ENOSPC; @@ -399,18 +422,47 @@ /* pick a set of schedule slots, link the QH into them */ do { int uframe; + u32 c_mask = 0; /* pick a set of slots such that all uframes have * enough periodic bandwidth available. - * - * FIXME for TT splits, need uframes for start and end. - * FSTNs can put end into next frame (uframes 0 or 1). */ frame--; for (uframe = 0; uframe < 8; uframe++) { if (check_period (ehci, frame, uframe, - period, usecs) != 0) - break; + period, usecs) == 0) + continue; + + /* If this is a split transaction, check the + * bandwidth available for the completion + * too. check both best and worst case gaps: + * worst case is SPLIT near uframe end, and + * CSPLIT near start ... best is vice versa. + * Difference can be almost two uframe times. + * + * FIXME don't even bother unless we know + * this TT is idle in that uframe ... right + * now we know only one interrupt transfer + * will be scheduled per frame, so we don't + * need to update/check TT state when we + * schedule a split (QH, SITD, or FSTN). + * + * FIXME ehci 0.96 and above can use FSTNs + */ + if (!c_usecs) + break; + if (check_period (ehci, frame, + uframe + gap_uf, + period, c_usecs) == 0) + continue; + if (check_period (ehci, frame, + uframe + gap_uf + 1, + period, c_usecs) == 0) + continue; + + c_mask = 0x03 << (8 + uframe + gap_uf); + c_mask = cpu_to_le32 (c_mask); + break; } if (uframe == 8) continue; @@ -419,13 +471,14 @@ urb->start_frame = frame; status = 0; - /* set S-frame mask */ - qh->hw_info2 |= cpu_to_le32 (1 << uframe); + /* reset S-frame and (maybe) C-frame masks */ + qh->hw_info2 &= ~0xffff; + qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; // dbg_qh ("Schedule INTR qh", ehci, qh); /* stuff into the periodic schedule */ qh->qh_state = QH_STATE_LINKED; - vdbg ("qh %p usecs %d period %d starting %d.%d", + vdbg ("qh %p usecs %d period %d.0 starting %d.%d", qh, qh->usecs, period, frame, uframe); do { if (unlikely (ehci->pshadow [frame].ptr != 0)) { @@ -443,7 +496,8 @@ } while (frame < ehci->periodic_size); /* update bandwidth utilization records (for usbfs) */ - usb_claim_bandwidth (urb->dev, urb, usecs/period, 0); + usb_claim_bandwidth (urb->dev, urb, + (usecs + c_usecs) / period, 0); /* maybe enable periodic schedule processing */ if (!ehci->periodic_urbs++) @@ -557,6 +611,7 @@ u32 buf1; unsigned i, epnum, maxp, multi; unsigned length; + int is_input; itd->hw_next = EHCI_LIST_END; itd->urb = urb; @@ -578,7 +633,8 @@ * as encoded in the ep descriptor's maxpacket field */ epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) { + is_input = usb_pipein (urb->pipe); + if (is_input) { maxp = urb->dev->epmaxpacketin [epnum]; buf1 = (1 << 11); } else { @@ -598,7 +654,7 @@ urb->iso_frame_desc [index].length); return -ENOSPC; } - itd->usecs = HS_USECS_ISO (length); + itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length); /* "plus" info in low order bits of buffer pointers */ itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); @@ -919,17 +975,9 @@ return flags; /* - * For now, always give the urb back to the driver ... expect it - * to submit a new urb (or resubmit this), and to have another - * already queued when un-interrupted transfers are needed. - * No, that's not what OHCI or UHCI are now doing. - * - * FIXME Revisit the ISO URB model. It's cleaner not to have all - * the special case magic, but it'd be faster to reuse existing - * ITD/DMA setup and schedule state. Easy to dma_sync/complete(), - * then either reschedule or, if unlinking, free and giveback(). - * But we can't overcommit like the full and low speed HCs do, and - * there's no clean way to report an error when rescheduling... + * Always give the urb back to the driver ... expect it to submit + * a new urb (or resubmit this), and to have another already queued + * when un-interrupted transfers are needed. * * NOTE that for now we don't accelerate ISO unlinks; they just * happen according to the current schedule. Means a delay of diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h Tue May 28 23:49:09 2002 +++ b/drivers/usb/host/ehci.h Tue May 28 23:49:09 2002 @@ -288,14 +288,11 @@ atomic_t refcount; unsigned short usecs; /* intr bandwidth */ + unsigned short c_usecs; /* ... split completion bw */ short qh_state; #define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */ - -#ifdef EHCI_SOFT_RETRIES - int retries; -#endif } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ @@ -360,6 +357,9 @@ union ehci_shadow sitd_next; /* ptr to periodic q entry */ struct urb *urb; dma_addr_t buf_dma; /* buffer address */ + + unsigned short usecs; /* start bandwidth */ + unsigned short c_usecs; /* completion bandwidth */ } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/