📄 ehci-sched.c
字号:
*/ mask = 0x03 << (uframe + qh->gap_uf); *c_maskp = cpu_to_le32 (mask << 8); mask |= 1 << uframe; if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) { if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, qh->period, qh->c_usecs)) goto done; if (!check_period (ehci, frame, uframe + qh->gap_uf, qh->period, qh->c_usecs)) goto done; retval = 0; }done: return retval;}/* "first fit" scheduling policy used the first time through, * or when the previous schedule slot can't be re-used. */static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh){ int status; unsigned uframe; __le32 c_mask; unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ qh_refresh(ehci, qh); qh->hw_next = EHCI_LIST_END; frame = qh->start; /* reuse the previous schedule slots, if we can */ if (frame < qh->period) { uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK); status = check_intr_schedule (ehci, frame, --uframe, qh, &c_mask); } else { uframe = 0; c_mask = 0; status = -ENOSPC; } /* else scan the schedule to find a group of slots such that all * uframes have enough periodic bandwidth available. */ if (status) { /* "normal" case, uframing flexible except with splits */ if (qh->period) { frame = qh->period - 1; do { for (uframe = 0; uframe < 8; uframe++) { status = check_intr_schedule (ehci, frame, uframe, qh, &c_mask); if (status == 0) break; } } while (status && frame--); /* qh->period == 0 means every uframe */ } else { frame = 0; status = check_intr_schedule (ehci, 0, 0, qh, &c_mask); } if (status) goto done; qh->start = frame; /* reset S-frame and (maybe) C-frame masks */ qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK)); qh->hw_info2 |= qh->period ? cpu_to_le32 (1 << uframe) : __constant_cpu_to_le32 (QH_SMASK); qh->hw_info2 |= c_mask; } else ehci_dbg (ehci, "reused qh %p schedule\n", qh); /* stuff into the periodic schedule */ status = qh_link_periodic (ehci, qh);done: return status;}static int intr_submit ( struct ehci_hcd *ehci, struct usb_host_endpoint *ep, struct urb *urb, struct list_head *qtd_list, unsigned mem_flags) { unsigned epnum; unsigned long flags; struct ehci_qh *qh; int status = 0; struct list_head empty; /* get endpoint and transfer/schedule data */ epnum = ep->desc.bEndpointAddress; spin_lock_irqsave (&ehci->lock, flags); /* get qh and force any scheduling errors */ INIT_LIST_HEAD (&empty); qh = qh_append_tds (ehci, urb, &empty, epnum, &ep->hcpriv); if (qh == NULL) { status = -ENOMEM; goto done; } if (qh->qh_state == QH_STATE_IDLE) { if ((status = qh_schedule (ehci, qh)) != 0) goto done; } /* then queue the urb's tds to the qh */ qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv); BUG_ON (qh == NULL); /* ... update usbfs periodic stats */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs++;done: spin_unlock_irqrestore (&ehci->lock, flags); if (status) qtd_list_free (ehci, urb, qtd_list); return status;}/*-------------------------------------------------------------------------*//* ehci_iso_stream ops work with both ITD and SITD */static struct ehci_iso_stream *iso_stream_alloc (unsigned mem_flags){ struct ehci_iso_stream *stream; stream = kzalloc(sizeof *stream, mem_flags); if (likely (stream != NULL)) { INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); stream->next_uframe = -1; stream->refcount = 1; } return stream;}static voidiso_stream_init ( struct ehci_hcd *ehci, struct ehci_iso_stream *stream, struct usb_device *dev, int pipe, unsigned interval){ static const u8 smask_out [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f }; u32 buf1; unsigned epnum, maxp; int is_input; long bandwidth; /* * this might be a "high bandwidth" highspeed endpoint, * as encoded in the ep descriptor's wMaxPacket field */ epnum = usb_pipeendpoint (pipe); is_input = usb_pipein (pipe) ? USB_DIR_IN : 0; maxp = usb_maxpacket(dev, pipe, !is_input); if (is_input) { buf1 = (1 << 11); } else { buf1 = 0; } /* knows about ITD vs SITD */ if (dev->speed == USB_SPEED_HIGH) { unsigned multi = hb_mult(maxp); stream->highspeed = 1; maxp = max_packet(maxp); buf1 |= maxp; maxp *= multi; stream->buf0 = cpu_to_le32 ((epnum << 8) | dev->devnum); stream->buf1 = cpu_to_le32 (buf1); stream->buf2 = cpu_to_le32 (multi); /* usbfs wants to report the average usecs per frame tied up * when transfers on this endpoint are scheduled ... */ stream->usecs = HS_USECS_ISO (maxp); bandwidth = stream->usecs * 8; bandwidth /= 1 << (interval - 1); } else { u32 addr; int think_time; addr = dev->ttport << 24; if (!ehci_is_TDI(ehci) || (dev->tt->hub != ehci_to_hcd(ehci)->self.root_hub)) addr |= dev->tt->hub->devnum << 16; addr |= epnum << 8; addr |= dev->devnum; stream->usecs = HS_USECS_ISO (maxp); think_time = dev->tt ? dev->tt->think_time : 0; stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time ( dev->speed, is_input, 1, maxp)); if (is_input) { u32 tmp; addr |= 1 << 31; stream->c_usecs = stream->usecs; stream->usecs = HS_USECS_ISO (1); stream->raw_mask = 1; /* pessimistic c-mask */ tmp = usb_calc_bus_time (USB_SPEED_FULL, 1, 0, maxp) / (125 * 1000); stream->raw_mask |= 3 << (tmp + 9); } else stream->raw_mask = smask_out [maxp / 188]; bandwidth = stream->usecs + stream->c_usecs; bandwidth /= 1 << (interval + 2); /* stream->splits gets created from raw_mask later */ stream->address = cpu_to_le32 (addr); } stream->bandwidth = bandwidth; stream->udev = dev; stream->bEndpointAddress = is_input | epnum; stream->interval = interval; stream->maxp = maxp;}static voidiso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream){ stream->refcount--; /* free whenever just a dev->ep reference remains. * not like a QH -- no persistent state (toggle, halt) */ if (stream->refcount == 1) { int is_in; // BUG_ON (!list_empty(&stream->td_list)); while (!list_empty (&stream->free_list)) { struct list_head *entry; entry = stream->free_list.next; list_del (entry); /* knows about ITD vs SITD */ if (stream->highspeed) { struct ehci_itd *itd; itd = list_entry (entry, struct ehci_itd, itd_list); dma_pool_free (ehci->itd_pool, itd, itd->itd_dma); } else { struct ehci_sitd *sitd; sitd = list_entry (entry, struct ehci_sitd, sitd_list); dma_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma); } } is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; stream->bEndpointAddress &= 0x0f; stream->ep->hcpriv = NULL; if (stream->rescheduled) { ehci_info (ehci, "ep%d%s-iso rescheduled " "%lu times in %lu seconds\n", stream->bEndpointAddress, is_in ? "in" : "out", stream->rescheduled, ((jiffies - stream->start)/HZ) ); } kfree(stream); }}static inline struct ehci_iso_stream *iso_stream_get (struct ehci_iso_stream *stream){ if (likely (stream != NULL)) stream->refcount++; return stream;}static struct ehci_iso_stream *iso_stream_find (struct ehci_hcd *ehci, struct urb *urb){ unsigned epnum; struct ehci_iso_stream *stream; struct usb_host_endpoint *ep; unsigned long flags; epnum = usb_pipeendpoint (urb->pipe); if (usb_pipein(urb->pipe)) ep = urb->dev->ep_in[epnum]; else ep = urb->dev->ep_out[epnum]; spin_lock_irqsave (&ehci->lock, flags); stream = ep->hcpriv; if (unlikely (stream == NULL)) { stream = iso_stream_alloc(GFP_ATOMIC); if (likely (stream != NULL)) { /* dev->ep owns the initial refcount */ ep->hcpriv = stream; stream->ep = ep; iso_stream_init(ehci, stream, urb->dev, urb->pipe, urb->interval); } /* if dev->ep [epnum] is a QH, info1.maxpacket is nonzero */ } else if (unlikely (stream->hw_info1 != 0)) { ehci_dbg (ehci, "dev %s ep%d%s, not iso??\n", urb->dev->devpath, epnum, usb_pipein(urb->pipe) ? "in" : "out"); stream = NULL; } /* caller guarantees an eventual matching iso_stream_put */ stream = iso_stream_get (stream); spin_unlock_irqrestore (&ehci->lock, flags); return stream;}/*-------------------------------------------------------------------------*//* ehci_iso_sched ops can be ITD-only or SITD-only */static struct ehci_iso_sched *iso_sched_alloc (unsigned packets, unsigned mem_flags){ struct ehci_iso_sched *iso_sched; int size = sizeof *iso_sched; size += packets * sizeof (struct ehci_iso_packet); iso_sched = kmalloc (size, mem_flags); if (likely (iso_sched != NULL)) { memset(iso_sched, 0, size); INIT_LIST_HEAD (&iso_sched->td_list); } return iso_sched;}static inline voiditd_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 voidiso_sched_free ( struct ehci_iso_stream *stream, struct ehci_iso_sched *iso_sched){ if (!iso_sched) return; // caller must hold ehci->lock! list_splice (&iso_sched->td_list, &stream->free_list); kfree (iso_sched);}static intitd_urb_transaction ( struct ehci_iso_stream *stream, struct ehci_hcd *ehci, struct urb *urb, unsigned 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 intitd_slot_ok ( struct ehci_hcd *ehci, u32 mod, u32 uframe, u8 usecs, u32 period){ uframe %= period; do { /* can't commit more than 80% periodic == 100 usec */ if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) > (100 - usecs))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -