⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ehci-sched.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 4 页
字号:
	 */	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 + -