📄 ehci-sched.c
字号:
// qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */); /* high bandwidth, or otherwise part of every microframe */ if ((period = qh->period) == 0) period = 1; for (i = qh->start; i < ehci->periodic_size; i += period) periodic_unlink (ehci, i, qh); /* update per-qh bandwidth for usbfs */ ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period ? ((qh->usecs + qh->c_usecs) / qh->period) : (qh->usecs * 8); dev_dbg (&qh->dev->dev, "unlink qh%d-%04x/%p start %d [%d/%d us]\n", qh->period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; qh_put (qh); /* maybe turn off periodic schedule */ ehci->periodic_sched--; if (!ehci->periodic_sched) (void) disable_periodic (ehci);}static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh){ unsigned wait; qh_unlink_periodic (ehci, qh); /* simple/paranoid: always delay, expecting the HC needs to read * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and * expect khubd to clean up after any CSPLITs we won't issue. * active high speed queues may need bigger delays... */ if (list_empty (&qh->qtd_list) || (__constant_cpu_to_le32 (QH_CMASK) & qh->hw_info2) != 0) wait = 2; else wait = 55; /* worst case: 3 * 1024 */ udelay (wait); qh->qh_state = QH_STATE_IDLE; qh->hw_next = EHCI_LIST_END; wmb ();}/*-------------------------------------------------------------------------*/static int check_period ( struct ehci_hcd *ehci, unsigned frame, unsigned uframe, unsigned period, unsigned usecs) { int claimed; /* 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" */ usecs = 100 - usecs; /* we "know" 2 and 4 uframe intervals were rejected; so * for period 0, check _every_ microframe in the schedule. */ if (unlikely (period == 0)) { do { for (uframe = 0; uframe < 7; uframe++) { claimed = periodic_usecs (ehci, frame, uframe); if (claimed > usecs) return 0; } } while ((frame += 1) < ehci->periodic_size); /* just check the specified uframe, at that period */ } else { do { claimed = periodic_usecs (ehci, frame, uframe); if (claimed > usecs) return 0; } while ((frame += period) < ehci->periodic_size); } // success! return 1;}static int check_intr_schedule ( struct ehci_hcd *ehci, unsigned frame, unsigned uframe, const struct ehci_qh *qh, __le32 *c_maskp){ int retval = -ENOSPC; u8 mask = 0; if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ goto done; if (!check_period (ehci, frame, uframe, qh->period, qh->usecs)) goto done; if (!qh->c_usecs) { retval = 0; *c_maskp = 0; goto done; }#ifdef CONFIG_USB_EHCI_TT_NEWSCHED if (tt_available (ehci, qh->period, qh->dev, frame, uframe, qh->tt_usecs)) { unsigned i; /* TODO : this may need FSTN for SSPLIT in uframe 5. */ for (i=uframe+1; i<8 && i<uframe+4; i++) if (!check_period (ehci, frame, i, qh->period, qh->c_usecs)) goto done; else mask |= 1 << i; retval = 0; *c_maskp = cpu_to_le32 (mask << 8); }#else /* Make sure this tt's buffer is also available for CSPLITs. * We pessimize a bit; probably the typical full speed case * doesn't need the second CSPLIT. * * NOTE: both SPLIT and CSPLIT could be checked in just * one smart pass... */ 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; }#endifdone: 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, gfp_t 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); if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &ehci_to_hcd(ehci)->flags))) { status = -ESHUTDOWN; goto done; } /* 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 (gfp_t 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; int hs_transfers; 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)); hs_transfers = max (1u, (maxp + 187) / 188); if (is_input) { u32 tmp; addr |= 1 << 31; stream->c_usecs = stream->usecs; stream->usecs = HS_USECS_ISO (1); stream->raw_mask = 1; /* c-mask as specified in USB 2.0 11.18.4 3.c */ tmp = (1 << (hs_transfers + 2)) - 1; stream->raw_mask |= tmp << (8 + 2); } else stream->raw_mask = smask_out [hs_transfers - 1]; 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, gfp_t mem_flags){ struct ehci_iso_sched *iso_sched; int size = sizeof *iso_sched; size += packets * sizeof (struct ehci_iso_packet); iso_sched = kzalloc(size, mem_flags); if (likely (iso_sched != NULL)) { 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -