From xiphmont@gmail.com Thu Sep 28 00:08:44 2006 Message-ID: <806dafc20609280008v18240654v23944e9fc8e7e537@mail.gmail.com> Date: Thu, 28 Sep 2006 03:08:36 -0400 From: "Christopher \"Monty\" Montgomery" To: linux-usb-devel@lists.sourceforge.net Subject: [PATCH 5/15] USB: ehci-hcd: group ehci_iso_sched and ehci_itd code Cc: greg@kroah.com, david-b@pacbell.net, xiphmont@gmail.com Content-Disposition: inline patch 5: first half of untangling intermixed ehci_iso_sched, itd and sitd code; moves/groups scattered code into self-contained sections. Adds kernel-doc entries for functions. This patch should result in no functional difference. Signed-off-by: Christopher "Monty" Montgomery Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 409 ++++++++++++++++++++++++++++-------------- 1 file changed, 282 insertions(+), 127 deletions(-) --- gregkh-2.6.orig/drivers/usb/host/ehci-sched.c +++ gregkh-2.6/drivers/usb/host/ehci-sched.c @@ -969,9 +969,15 @@ iso_sched_free (struct ehci_iso_stream * } /*-------------------------------------------------------------------------*/ +/* ISO stream generic machinery; tracks the ITDs and sITDs associated + with an isochronous endpoint */ -/* ehci_iso_stream ops work with both ITD and SITD */ - +/* iso_stream_alloc - allocate a new iso stream structure; and iso + * stream performs a role similar to QHs for ISO endpoints (both ITD + * and SITD) + * + * @mem_flags: flags for memory allocation + */ static struct ehci_iso_stream * iso_stream_alloc (gfp_t mem_flags) { @@ -987,6 +993,14 @@ iso_stream_alloc (gfp_t mem_flags) return stream; } +/* iso_stream_init - initialize fields of an ehci_iso_stream structure + * + * @ehci: pointer to ehci host controller device structure + * @stream: ehci_iso_stream structure to initialize + * @dev: endpoint usb device + * @pipe: pipe handle + * @interval: bInterval of endpoint; frames for FS/LS, uFrames for HS + */ static void iso_stream_init ( struct ehci_hcd *ehci, @@ -1082,6 +1096,12 @@ iso_stream_init ( stream->maxp = maxp; } +/* iso_stream_put - decrement refcount of passed in ehci_iso_stream + * structure, reaping it if count has fallen to zero. + * + * @ehci: pointer to ehci host controller device structure + * @stream: ehci_iso_stream structure + */ static void iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) { @@ -1144,6 +1164,11 @@ iso_stream_put(struct ehci_hcd *ehci, st } } +/* iso_stream_get - increment refcount of passed in ehci_iso_stream + * structure + * + * @stream: ehci_iso_stream structure + */ static inline struct ehci_iso_stream * iso_stream_get (struct ehci_iso_stream *stream) { @@ -1152,6 +1177,14 @@ iso_stream_get (struct ehci_iso_stream * return stream; } +/* iso_stream_find - returns ehci_iso_stream structure matching the + * passed in urb; if no match is found, a new ehci_iso_stream is + * allocated and initialized to match the urb. This function is thus + * used both for looup and lazy alloc. + * + * @ehci: pointer to ehci host controller device structure + * @urb: usb request block + */ static struct ehci_iso_stream * iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) { @@ -1194,114 +1227,6 @@ iso_stream_find (struct ehci_hcd *ehci, return stream; } -static inline void -itd_sched_init ( - struct ehci_iso_sched *iso_sched, - struct ehci_iso_stream *stream, - struct urb *urb -) -{ - unsigned i; - dma_addr_t dma = urb->transfer_dma; - - /* how many uframes are needed for these transfers */ - iso_sched->span = urb->number_of_packets * stream->interval; - - /* figure out per-uframe itd fields that we'll need later - * when we fit new itds into the schedule. - */ - for (i = 0; i < urb->number_of_packets; i++) { - struct ehci_iso_packet *uframe = &iso_sched->packet [i]; - unsigned length; - dma_addr_t buf; - u32 trans; - - length = urb->iso_frame_desc [i].length; - buf = dma + urb->iso_frame_desc [i].offset; - - trans = EHCI_ISOC_ACTIVE; - trans |= buf & 0x0fff; - if (unlikely (((i + 1) == urb->number_of_packets)) - && !(urb->transfer_flags & URB_NO_INTERRUPT)) - trans |= EHCI_ITD_IOC; - trans |= length << 16; - uframe->transaction = cpu_to_le32 (trans); - - /* might need to cross a buffer page within a uframe */ - uframe->bufp = (buf & ~(u64)0x0fff); - buf += length; - if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff)))) - uframe->cross = 1; - } -} - -static int -itd_urb_transaction ( - struct ehci_iso_stream *stream, - struct ehci_hcd *ehci, - struct urb *urb, - gfp_t mem_flags -) -{ - struct ehci_itd *itd; - dma_addr_t itd_dma; - int i; - unsigned num_itds; - struct ehci_iso_sched *sched; - unsigned long flags; - - sched = iso_sched_alloc (urb->number_of_packets, mem_flags); - if (unlikely (sched == NULL)) - return -ENOMEM; - - itd_sched_init (sched, stream, urb); - - if (urb->interval < 8) - num_itds = 1 + (sched->span + 7) / 8; - else - num_itds = urb->number_of_packets; - - /* allocate/init ITDs */ - spin_lock_irqsave (&ehci->lock, flags); - for (i = 0; i < num_itds; i++) { - - /* free_list.next might be cache-hot ... but maybe - * the HC caches it too. avoid that issue for now. - */ - - /* prefer previously-allocated itds */ - if (likely (!list_empty(&stream->free_list))) { - itd = list_entry (stream->free_list.prev, - struct ehci_itd, itd_list); - list_del (&itd->itd_list); - itd_dma = itd->itd_dma; - } else - itd = NULL; - - if (!itd) { - spin_unlock_irqrestore (&ehci->lock, flags); - itd = dma_pool_alloc (ehci->itd_pool, mem_flags, - &itd_dma); - spin_lock_irqsave (&ehci->lock, flags); - } - - if (unlikely (NULL == itd)) { - iso_sched_free (stream, sched); - spin_unlock_irqrestore (&ehci->lock, flags); - return -ENOMEM; - } - memset (itd, 0, sizeof *itd); - itd->itd_dma = itd_dma; - list_add (&itd->itd_list, &sched->td_list); - } - spin_unlock_irqrestore (&ehci->lock, flags); - - /* temporarily store schedule info in hcpriv */ - urb->hcpriv = sched; - urb->error_count = 0; - return 0; -} - /*-------------------------------------------------------------------------*/ static inline int @@ -1416,12 +1341,19 @@ sitd_slot_ok ( #define SCHEDULE_SLOP 10 /* frames */ -static int -iso_stream_schedule ( +/* iso_stream_schedule - schedules an iso request transaction into the + * periodic schedule, budgeting persistent periodic bandwidth for this + * stream if that has not yet been done. + * + * @ehci: pointer to ehci host controller device structure + * @urb: request + * @stream: persistent iso stream + */ +static int iso_stream_schedule ( struct ehci_hcd *ehci, struct urb *urb, struct ehci_iso_stream *stream -) + ) { u32 now, start, max, period; int status; @@ -1529,6 +1461,139 @@ ready: /*-------------------------------------------------------------------------*/ +/* itd_sched_init - ITD-specific initialization of an ehci_iso_sched + * structure to hold the specifics of the current requested + * transaction during the various steps of budgeting and scheduling. + * + * @iso_sched: structure to initialize + * @stream: iso stream + * @urb: current request + */ +static inline void +itd_sched_init ( + struct ehci_iso_sched *iso_sched, + struct ehci_iso_stream *stream, + struct urb *urb +) +{ + unsigned i; + dma_addr_t dma = urb->transfer_dma; + + /* how many uframes are needed for these transfers */ + iso_sched->span = urb->number_of_packets * stream->interval; + + /* figure out per-uframe itd fields that we'll need later + * when we fit new itds into the schedule. + */ + for (i = 0; i < urb->number_of_packets; i++) { + struct ehci_iso_packet *uframe = &iso_sched->packet [i]; + unsigned length; + dma_addr_t buf; + u32 trans; + + length = urb->iso_frame_desc [i].length; + buf = dma + urb->iso_frame_desc [i].offset; + + trans = EHCI_ISOC_ACTIVE; + trans |= buf & 0x0fff; + if (unlikely (((i + 1) == urb->number_of_packets)) + && !(urb->transfer_flags & URB_NO_INTERRUPT)) + trans |= EHCI_ITD_IOC; + trans |= length << 16; + uframe->transaction = cpu_to_le32 (trans); + + /* might need to cross a buffer page within a uframe */ + uframe->bufp = (buf & ~(u64)0x0fff); + buf += length; + if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff)))) + uframe->cross = 1; + } +} + +/* itd_urb_transaction - sets up memory and relevant structures to + * process current transaction; initializes the iso_sched structure + * and preallocates transfer descriptors + * + * @stream: iso stream + * @ehci: pointer to ehci host controller device structure + * @urb: current request + * mem_flags: flags to use for in-kernel memory allocation + */ +static int +itd_urb_transaction ( + struct ehci_iso_stream *stream, + struct ehci_hcd *ehci, + struct urb *urb, + gfp_t mem_flags +) +{ + struct ehci_itd *itd; + dma_addr_t itd_dma; + int i; + unsigned num_itds; + struct ehci_iso_sched *sched; + unsigned long flags; + + sched = iso_sched_alloc (urb->number_of_packets, mem_flags); + if (unlikely (sched == NULL)) + return -ENOMEM; + + itd_sched_init (sched, stream, urb); + + if (urb->interval < 8) + num_itds = 1 + (sched->span + 7) / 8; + else + num_itds = urb->number_of_packets; + + /* allocate/init ITDs */ + spin_lock_irqsave (&ehci->lock, flags); + for (i = 0; i < num_itds; i++) { + + /* free_list.next might be cache-hot ... but maybe + * the HC caches it too. avoid that issue for now. + */ + + /* prefer previously-allocated itds */ + if (likely (!list_empty(&stream->free_list))) { + itd = list_entry (stream->free_list.prev, + struct ehci_itd, itd_list); + list_del (&itd->itd_list); + itd_dma = itd->itd_dma; + } else + itd = NULL; + + if (!itd) { + spin_unlock_irqrestore (&ehci->lock, flags); + itd = dma_pool_alloc (ehci->itd_pool, mem_flags, + &itd_dma); + spin_lock_irqsave (&ehci->lock, flags); + } + + if (unlikely (NULL == itd)) { + iso_sched_free (stream, sched); + spin_unlock_irqrestore (&ehci->lock, flags); + return -ENOMEM; + } + memset (itd, 0, sizeof *itd); + itd->itd_dma = itd_dma; + list_add (&itd->itd_list, &sched->td_list); + } + spin_unlock_irqrestore (&ehci->lock, flags); + + /* temporarily store schedule info in hcpriv */ + urb->hcpriv = sched; + urb->error_count = 0; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* itd_init - performs master initilization of the ITD fields that are + * drawn from the stream structure. + * + * @stream: iso stream + * @itd: New ITD that is to be added to the shadow/hardware schedule + */ static inline void itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd) { @@ -1546,6 +1611,13 @@ itd_init (struct ehci_iso_stream *stream /* All other fields are filled when scheduling */ } +/* itd_patch - patches additional transfer descriptors into an existing ITD. + * + * @itd: New ITD that is to be added to the shadow/hardware schedule + * @iso_sched: ehci_iso_sched holding transfer descriptors + * @index: packet number to reference within the ehci_iso_sched + * @uframe: uframe in which to schedule specified packet + */ static inline void itd_patch ( struct ehci_itd *itd, @@ -1576,6 +1648,12 @@ itd_patch ( } } +/* itd_link - place one completed itd into the shadow/hardware schedule + * + * @ehci: pointer to ehci host controller device structure + * @frame: shadow/hardware schedule frame + * @itd: ITD to link into schedule + */ static inline void itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) { @@ -1588,7 +1666,14 @@ itd_link (struct ehci_hcd *ehci, unsigne ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD; } -/* fit urb's itds into the selected schedule slot; activate as needed */ +/* itd_link_urb - link urb's ITDs into the hardware schedule acording + * to shadow budget + * + * @ehci: pointer to ehci host controller device structure + * @urb: current USB request + * @mod: size of periodic hardware schedule [in uFrames] + * @stream: stream into which to queue request + */ static int itd_link_urb ( struct ehci_hcd *ehci, @@ -1663,6 +1748,15 @@ itd_link_urb ( #define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) +/* itd_complete - process a single complete ITD that has been removed + * from the periodic schedule and handed to us by scan_periodic. If + * this is the last ITD that needed to be completed for a URB, hand + * the URB back to the upper level driver. + * + * @ehci: pointer to ehci host controller device structure + * @itd: completed ITD + * @regs: ptrace registers + */ static unsigned itd_complete ( struct ehci_hcd *ehci, @@ -1743,8 +1837,13 @@ itd_complete ( return 1; } -/*-------------------------------------------------------------------------*/ - +/* itd_submit - top level submission point for a HS isochronous + * endpoint request. + * + * @ehci: pointer to ehci host controller device structure + * @urb: new USB request + * @mem_flags: flags for memory allocation + */ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags) { @@ -1802,12 +1901,17 @@ done: #ifdef CONFIG_USB_EHCI_SPLIT_ISO /*-------------------------------------------------------------------------*/ +/* Split-ISO transfer machinery; used for FS isoch transfers through the + * TTs in USB 2.0 hubs. */ -/* - * "Split ISO TDs" ... used for USB 1.1 devices going through the - * TTs in USB 2.0 hubs. These need microframe scheduling. +/* sitd_sched_init - sITD-specific initialization of an ehci_iso_sched + * structure to hold the specifics of the current requested + * transaction during the various steps of budgeting and scheduling. + * + * @iso_sched: structure to initialize + * @stream: iso stream + * @urb: current request */ - static inline void sitd_sched_init ( struct ehci_iso_sched *iso_sched, @@ -1856,6 +1960,15 @@ sitd_sched_init ( } } +/* sitd_urb_transaction - sets up memory and relevant structures to + * process current transaction; initializes the iso_sched structure + * and preallocates transfer descriptors + * + * @stream: iso stream + * @ehci: pointer to ehci host controller device structure + * @urb: current request + * @mem_flags: flags to use for in-kernel memory allocation + */ static int sitd_urb_transaction ( struct ehci_iso_stream *stream, @@ -1923,8 +2036,15 @@ sitd_urb_transaction ( return 0; } -/*-------------------------------------------------------------------------*/ - +/* sitd_patch - finishes filling in the hardware fields of a sITD + * schedule prior to it being linked into the hardware/shadow + * schedule. + * + * @stream: stream into which this request will be queued + * @sitd: New sITD that is to be added to the shadow/hardware schedule + * @iso_sched: ehci_iso_sched holding transfer descriptors + * @index: packet number + */ static inline void sitd_patch ( struct ehci_iso_stream *stream, @@ -1953,6 +2073,13 @@ sitd_patch ( sitd->index = index; } +/* sitd_link - fill in and link one sITD into the shadow/hardware + * schedule + * + * @ehci: pointer to ehci host controller device structure + * @frame: shadow/hardware schedule frame + * @sitd: sitd to link + */ static inline void sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) { @@ -1965,7 +2092,14 @@ sitd_link (struct ehci_hcd *ehci, unsign ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD; } -/* fit urb's sitds into the selected schedule slot; activate as needed */ +/* sitd_link_urb - link urb's sITDs into the hardware schedule + * acording to shadow budget + * + * @ehci: pointer to ehci host controller device structure + * @urb: current USB request + * @mod: size of periodic hardware schedule [in uFrames] + * @stream: stream into which to queue request + */ static int sitd_link_urb ( struct ehci_hcd *ehci, @@ -2028,11 +2162,18 @@ sitd_link_urb ( return 0; } -/*-------------------------------------------------------------------------*/ - #define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \ | SITD_STS_XACT | SITD_STS_MMF) +/* sitd_complete - process a single complete sITD that has been removed + * from the periodic schedule and handed to us by scan_periodic. If + * this is the last sITD that needed to be completed for a URB, hand + * the URB back to the upper level driver. + * + * @ehci: pointer to ehci host controller device structure + * @sitd: completed sITD + * @regs: ptrace registers + */ static unsigned sitd_complete ( struct ehci_hcd *ehci, @@ -2102,6 +2243,13 @@ sitd_complete ( } +/* sitd_submit - top level submission point for a FS split isochronous + * endpoint request. + * + * @ehci: pointer to ehci host controller device structure + * @urb: new USB request + * @mem_flags: flags for memory allocation + */ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags) { @@ -2174,8 +2322,15 @@ sitd_complete ( #endif /* USB_EHCI_SPLIT_ISO */ -/*-------------------------------------------------------------------------*/ - +/* scan_periodic - completion worker; called out of interrupt (or + * poll) handler to finish processing of transactions that have been + * completed by the host controller. Also now has the responsibility + * of halting the hardware periodic schedule after a complete pass + * through the schedule with nothing to do. + * + * @ehci: pointer to ehci host controller device structure + * @regs: ptrace registers + */ static void scan_periodic (struct ehci_hcd *ehci) {