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

📄 ehci-sched.c

📁 host usb 主设备程序 支持sd卡 mouse keyboard 的最单单的驱动程序 gcc编译
💻 C
📖 第 1 页 / 共 4 页
字号:
	//   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 + -