ChangeSet 1.1557.49.23, 2004/02/18 13:17:29-08:00, david-b@pacbell.net [PATCH] USB: ehci-hcd, fullspeed iso data structures (2/3) [USB] ehci, rename some iso data structures Rename some data and functions used by EHCI to manage ISO transactions, since they currently assume only high speed transfers. Much of the same infrastructure will be used for full speed ISO, with split transactions. drivers/usb/host/ehci-sched.c | 136 +++++++++++++++++++++--------------------- drivers/usb/host/ehci.h | 25 ++++--- 2 files changed, 84 insertions(+), 77 deletions(-) diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c --- a/drivers/usb/host/ehci-sched.c Thu Feb 19 17:21:12 2004 +++ b/drivers/usb/host/ehci-sched.c Thu Feb 19 17:21:12 2004 @@ -491,7 +491,9 @@ /*-------------------------------------------------------------------------*/ -static inline struct ehci_iso_stream * +/* ehci_iso_stream ops work with both ITD and SITD */ + +static struct ehci_iso_stream * iso_stream_alloc (int mem_flags) { struct ehci_iso_stream *stream; @@ -499,15 +501,15 @@ stream = kmalloc(sizeof *stream, mem_flags); if (likely (stream != 0)) { memset (stream, 0, sizeof(*stream)); - INIT_LIST_HEAD(&stream->itd_list); - INIT_LIST_HEAD(&stream->free_itd_list); + INIT_LIST_HEAD(&stream->td_list); + INIT_LIST_HEAD(&stream->free_list); stream->next_uframe = -1; stream->refcount = 1; } return stream; } -static inline void +static void iso_stream_init ( struct ehci_iso_stream *stream, struct usb_device *dev, @@ -534,12 +536,14 @@ buf1 = 0; } + stream->highspeed = 1; + multi = hb_mult(maxp); maxp = max_packet(maxp); buf1 |= maxp; maxp *= multi; - stream->dev = (struct hcd_dev *)dev->hcpriv; + stream->udev = dev; stream->bEndpointAddress = is_input | epnum; stream->interval = interval; @@ -567,14 +571,15 @@ * not like a QH -- no persistent state (toggle, halt) */ if (stream->refcount == 1) { - int is_in; + int is_in; + struct hcd_dev *dev = stream->udev->hcpriv; - // BUG_ON (!list_empty(&stream->itd_list)); + // BUG_ON (!list_empty(&stream->td_list)); - while (!list_empty (&stream->free_itd_list)) { + while (!list_empty (&stream->free_list)) { struct ehci_itd *itd; - itd = list_entry (stream->free_itd_list.next, + itd = list_entry (stream->free_list.next, struct ehci_itd, itd_list); list_del (&itd->itd_list); dma_pool_free (ehci->itd_pool, itd, itd->itd_dma); @@ -582,7 +587,7 @@ is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; stream->bEndpointAddress &= 0x0f; - stream->dev->ep [is_in + stream->bEndpointAddress] = 0; + dev->ep [is_in + stream->bEndpointAddress] = 0; if (stream->rescheduled) { ehci_info (ehci, "ep%d%s-iso rescheduled " @@ -648,24 +653,26 @@ /*-------------------------------------------------------------------------*/ -static inline struct ehci_itd_sched * -itd_sched_alloc (unsigned packets, int mem_flags) +/* ehci_iso_sched ops can be shared, ITD-only, or SITD-only */ + +static struct ehci_iso_sched * +iso_sched_alloc (unsigned packets, int mem_flags) { - struct ehci_itd_sched *itd_sched; - int size = sizeof *itd_sched; + struct ehci_iso_sched *iso_sched; + int size = sizeof *iso_sched; - size += packets * sizeof (struct ehci_iso_uframe); - itd_sched = kmalloc (size, mem_flags); - if (likely (itd_sched != 0)) { - memset(itd_sched, 0, size); - INIT_LIST_HEAD (&itd_sched->itd_list); + size += packets * sizeof (struct ehci_iso_packet); + iso_sched = kmalloc (size, mem_flags); + if (likely (iso_sched != 0)) { + memset(iso_sched, 0, size); + INIT_LIST_HEAD (&iso_sched->td_list); } - return itd_sched; + return iso_sched; } -static int +static inline void itd_sched_init ( - struct ehci_itd_sched *itd_sched, + struct ehci_iso_sched *iso_sched, struct ehci_iso_stream *stream, struct urb *urb ) @@ -674,13 +681,13 @@ dma_addr_t dma = urb->transfer_dma; /* how many uframes are needed for these transfers */ - itd_sched->span = urb->number_of_packets * stream->interval; + 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_uframe *uframe = &itd_sched->packet [i]; + struct ehci_iso_packet *uframe = &iso_sched->packet [i]; unsigned length; dma_addr_t buf; u32 trans; @@ -702,17 +709,19 @@ if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff)))) uframe->cross = 1; } - return 0; } static void -itd_sched_free ( +iso_sched_free ( struct ehci_iso_stream *stream, - struct ehci_itd_sched *itd_sched + struct ehci_iso_sched *iso_sched ) { - list_splice (&itd_sched->itd_list, &stream->free_itd_list); - kfree (itd_sched); + if (!iso_sched) + return; + // caller must hold ehci->lock! + list_splice (&iso_sched->td_list, &stream->free_list); + kfree (iso_sched); } static int @@ -724,37 +733,32 @@ ) { struct ehci_itd *itd; - int status; dma_addr_t itd_dma; int i; unsigned num_itds; - struct ehci_itd_sched *itd_sched; + struct ehci_iso_sched *sched; - itd_sched = itd_sched_alloc (urb->number_of_packets, mem_flags); - if (unlikely (itd_sched == 0)) + sched = iso_sched_alloc (urb->number_of_packets, mem_flags); + if (unlikely (sched == 0)) return -ENOMEM; - status = itd_sched_init (itd_sched, stream, urb); - if (unlikely (status != 0)) { - itd_sched_free (stream, itd_sched); - return status; - } + itd_sched_init (sched, stream, urb); if (urb->interval < 8) - num_itds = 1 + (itd_sched->span + 7) / 8; + num_itds = 1 + (sched->span + 7) / 8; else num_itds = urb->number_of_packets; /* allocate/init ITDs */ for (i = 0; i < num_itds; i++) { - /* free_itd_list.next might be cache-hot ... but maybe + /* 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_itd_list))) { - itd = list_entry (stream->free_itd_list.prev, + 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; @@ -763,16 +767,16 @@ &itd_dma); if (unlikely (0 == itd)) { - itd_sched_free (stream, itd_sched); + iso_sched_free (stream, sched); return -ENOMEM; } memset (itd, 0, sizeof *itd); itd->itd_dma = itd_dma; - list_add (&itd->itd_list, &itd_sched->itd_list); + list_add (&itd->itd_list, &sched->td_list); } /* temporarily store schedule info in hcpriv */ - urb->hcpriv = itd_sched; + urb->hcpriv = sched; urb->error_count = 0; return 0; } @@ -800,9 +804,9 @@ u32 now, start, end, max; int status; unsigned mod = ehci->periodic_size << 3; - struct ehci_itd_sched *itd_sched = urb->hcpriv; + struct ehci_iso_sched *sched = urb->hcpriv; - if (unlikely (itd_sched->span > (mod - 8 * SCHEDULE_SLOP))) { + if (unlikely (sched->span > (mod - 8 * SCHEDULE_SLOP))) { ehci_dbg (ehci, "iso request %p too long\n", urb); status = -EFBIG; goto fail; @@ -812,13 +816,13 @@ /* when's the last uframe this urb could start? */ max = now + mod; - max -= itd_sched->span; + max -= sched->span; max -= 8 * SCHEDULE_SLOP; /* typical case: reuse current schedule. stream is still active, * and no gaps from host falling behind (irq delays etc) */ - if (likely (!list_empty (&stream->itd_list))) { + if (likely (!list_empty (&stream->td_list))) { start = stream->next_uframe; if (start < now) @@ -852,7 +856,7 @@ max = start + urb->interval; /* hack: account for itds already scheduled to this endpoint */ - if (unlikely (list_empty (&stream->itd_list))) + if (unlikely (list_empty (&stream->td_list))) end = max; /* within [start..max] find a uframe slot with enough bandwidth */ @@ -880,7 +884,7 @@ /* (re)schedule it here if there's enough bandwidth */ if (enough_space) { start %= mod; - if (unlikely (!list_empty (&stream->itd_list))) { + if (unlikely (!list_empty (&stream->td_list))) { /* host fell behind ... maybe irq latencies * delayed this request queue for too long. */ @@ -902,12 +906,12 @@ /* no room in the schedule */ ehci_dbg (ehci, "iso %ssched full %p (now %d end %d max %d)\n", - list_empty (&stream->itd_list) ? "" : "re", + list_empty (&stream->td_list) ? "" : "re", urb, now, end, max); status = -ENOSPC; fail: - itd_sched_free (stream, itd_sched); + iso_sched_free (stream, sched); urb->hcpriv = 0; return status; @@ -937,13 +941,13 @@ static inline void itd_patch ( struct ehci_itd *itd, - struct ehci_itd_sched *itd_sched, + struct ehci_iso_sched *iso_sched, unsigned index, u16 uframe, int first ) { - struct ehci_iso_uframe *uf = &itd_sched->packet [index]; + struct ehci_iso_packet *uf = &iso_sched->packet [index]; unsigned pg = itd->pg; // BUG_ON (pg == 6 && uf->cross); @@ -988,12 +992,12 @@ { int packet, first = 1; unsigned next_uframe, uframe, frame; - struct ehci_itd_sched *itd_sched = urb->hcpriv; + struct ehci_iso_sched *iso_sched = urb->hcpriv; struct ehci_itd *itd; next_uframe = stream->next_uframe % mod; - if (unlikely (list_empty(&stream->itd_list))) { + if (unlikely (list_empty(&stream->td_list))) { hcd_to_bus (&ehci->hcd)->bandwidth_allocated += stream->bandwidth; ehci_vdbg (ehci, @@ -1010,13 +1014,13 @@ for (packet = 0, itd = 0; packet < urb->number_of_packets; ) { if (itd == 0) { /* ASSERT: we have all necessary itds */ - // BUG_ON (list_empty (&itd_sched->itd_list)); + // BUG_ON (list_empty (&iso_sched->td_list)); /* ASSERT: no itds for this endpoint in this uframe */ - itd = list_entry (itd_sched->itd_list.next, + itd = list_entry (iso_sched->td_list.next, struct ehci_itd, itd_list); - list_move_tail (&itd->itd_list, &stream->itd_list); + list_move_tail (&itd->itd_list, &stream->td_list); itd->stream = iso_stream_get (stream); itd->urb = usb_get_urb (urb); first = 1; @@ -1027,7 +1031,7 @@ frame = next_uframe >> 3; itd->usecs [uframe] = stream->usecs; - itd_patch (itd, itd_sched, packet, uframe, first); + itd_patch (itd, iso_sched, packet, uframe, first); first = 0; next_uframe += stream->interval; @@ -1044,7 +1048,7 @@ stream->next_uframe = next_uframe; /* don't need that schedule data any more */ - itd_sched_free (stream, itd_sched); + iso_sched_free (stream, iso_sched); urb->hcpriv = 0; if (unlikely (!ehci->periodic_sched++)) @@ -1102,7 +1106,7 @@ usb_put_urb (urb); itd->urb = 0; itd->stream = 0; - list_move (&itd->itd_list, &stream->free_itd_list); + list_move (&itd->itd_list, &stream->free_list); iso_stream_put (ehci, stream); /* handle completion now? */ @@ -1110,7 +1114,7 @@ return 0; /* ASSERT: it's really the last itd for this urb - list_for_each_entry (itd, &stream->itd_list, itd_list) + list_for_each_entry (itd, &stream->td_list, itd_list) BUG_ON (itd->urb == urb); */ @@ -1125,7 +1129,7 @@ (void) disable_periodic (ehci); hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs--; - if (unlikely (list_empty (&stream->itd_list))) { + if (unlikely (list_empty (&stream->td_list))) { hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= stream->bandwidth; ehci_vdbg (ehci, @@ -1278,7 +1282,7 @@ rmb (); for (uf = uframes; uf < 8; uf++) { if (0 == (q.itd->hw_transaction [uf] - & ISO_ACTIVE)) + & ITD_ACTIVE)) continue; q_p = &q.itd->itd_next; hw_p = &q.itd->hw_next; diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h Thu Feb 19 17:21:12 2004 +++ b/drivers/usb/host/ehci.h Thu Feb 19 17:21:12 2004 @@ -386,22 +386,24 @@ /*-------------------------------------------------------------------------*/ -/* description of one iso highspeed transaction (up to 3 KB data) */ -struct ehci_iso_uframe { +/* description of one iso transaction (up to 3 KB data if highspeed) */ +struct ehci_iso_packet { /* These will be copied to iTD when scheduling */ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ u32 transaction; /* itd->hw_transaction[i] |= */ u8 cross; /* buf crosses pages */ + /* for full speed OUT splits */ + u16 buf1; }; -/* temporary schedule data for highspeed packets from iso urbs - * each packet is one uframe's usb transactions, in some itd, +/* temporary schedule data for packets from iso urbs (both speeds) + * each packet is one logical usb transaction to the device (not TT), * beginning at stream->next_uframe */ -struct ehci_itd_sched { - struct list_head itd_list; +struct ehci_iso_sched { + struct list_head td_list; unsigned span; - struct ehci_iso_uframe packet [0]; + struct ehci_iso_packet packet [0]; }; /* @@ -415,9 +417,10 @@ u32 refcount; u8 bEndpointAddress; - struct list_head itd_list; /* queued itds */ - struct list_head free_itd_list; /* list of unused itds */ - struct hcd_dev *dev; + u8 highspeed; + struct list_head td_list; /* queued itds/sitds */ + struct list_head free_list; /* list of unused itds/sitds */ + struct usb_device *udev; /* output of (re)scheduling */ unsigned long start; /* jiffies */ @@ -460,7 +463,7 @@ #define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) #define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */ -#define ISO_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE) +#define ITD_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE) u32 hw_bufp [7]; /* see EHCI 3.3.3 */ u32 hw_bufp_hi [7]; /* Appendix B */