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

📄 ehci-sched.c

📁 linux2.4.20下的针对三星公司的s3c2410的usb模块驱动代码
💻 C
📖 第 1 页 / 共 3 页
字号:
			/* QH will run once each period, starting there  */			urb->start_frame = frame;			status = 0;			/* set S-frame mask */			qh->hw_info2 |= cpu_to_le32 (1 << uframe);			// dbg_qh ("Schedule INTR qh", ehci, qh);			/* stuff into the periodic schedule */			qh->qh_state = QH_STATE_LINKED;			vdbg ("qh %p usecs %d period %d starting %d.%d",				qh, qh->usecs, period, frame, uframe);			do {				if (unlikely (ehci->pshadow [frame].ptr != 0)) {// FIXME -- just link toward the end, before any qh with a shorter period,// AND handle it already being (implicitly) linked into this frame// AS WELL AS updating the check_period() logic					BUG ();				} else {					ehci->pshadow [frame].qh = qh_get (qh);					ehci->periodic [frame] =						QH_NEXT (qh->qh_dma);				}				wmb ();				frame += period;			} while (frame < ehci->periodic_size);			/* update bandwidth utilization records (for usbfs) */			usb_claim_bandwidth (urb->dev, urb, usecs/period, 0);			/* maybe enable periodic schedule processing */			if (!ehci->periodic_urbs++)				enable_periodic (ehci);			break;		} while (frame);	}	spin_unlock_irqrestore (&ehci->lock, flags);done:	if (status)		qtd_list_free (ehci, urb, qtd_list);	return status;}static unsigned longintr_complete (	struct ehci_hcd	*ehci,	unsigned	frame,	struct ehci_qh	*qh,	unsigned long	flags		/* caller owns ehci->lock ... */) {	struct ehci_qtd	*qtd;	struct urb	*urb;	int		unlinking;	/* nothing to report? */	if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))			!= 0))		return flags;	if (unlikely (list_empty (&qh->qtd_list))) {		dbg ("intr qh %p no TDs?", qh);		return flags;	}		qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list);	urb = qtd->urb;	unlinking = (urb->status == -ENOENT) || (urb->status == -ECONNRESET);	/* call any completions, after patching for reactivation */	spin_unlock_irqrestore (&ehci->lock, flags);	/* NOTE:  currently restricted to one qtd per qh! */	if (qh_completions (ehci, qh, 0) == 0)		urb = 0;	spin_lock_irqsave (&ehci->lock, flags);	/* never reactivate requests that were unlinked ... */	if (likely (urb != 0)) {		if (unlinking				|| urb->status == -ECONNRESET				|| urb->status == -ENOENT				// || (urb->dev == null)				|| ehci->hcd.state == USB_STATE_HALT)			urb = 0;		// FIXME look at all those unlink cases ... we always		// need exactly one completion that reports unlink.		// the one above might not have been it!	}	/* normally reactivate */	if (likely (urb != 0)) {		if (usb_pipeout (urb->pipe))			pci_dma_sync_single (ehci->hcd.pdev,				qtd->buf_dma,				urb->transfer_buffer_length,				PCI_DMA_TODEVICE);		urb->status = -EINPROGRESS;		urb->actual_length = 0;		/* patch qh and restart */		qh_update (qh, qtd);	}	return flags;}/*-------------------------------------------------------------------------*/static voiditd_free_list (struct ehci_hcd *ehci, struct urb *urb){	struct ehci_itd *first_itd = urb->hcpriv;	pci_unmap_single (ehci->hcd.pdev,		first_itd->buf_dma, urb->transfer_buffer_length,		usb_pipein (urb->pipe)		    ? PCI_DMA_FROMDEVICE		    : PCI_DMA_TODEVICE);	while (!list_empty (&first_itd->itd_list)) {		struct ehci_itd	*itd;		itd = list_entry (			first_itd->itd_list.next,			struct ehci_itd, itd_list);		list_del (&itd->itd_list);		pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);	}	pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma);	urb->hcpriv = 0;}static intitd_fill (	struct ehci_hcd	*ehci,	struct ehci_itd	*itd,	struct urb	*urb,	unsigned	index,		// urb->iso_frame_desc [index]	dma_addr_t	dma		// mapped transfer buffer) {	u64		temp;	u32		buf1;	unsigned	i, epnum, maxp, multi;	unsigned	length;	itd->hw_next = EHCI_LIST_END;	itd->urb = urb;	itd->index = index;	/* tell itd about its transfer buffer, max 2 pages */	length = urb->iso_frame_desc [index].length;	dma += urb->iso_frame_desc [index].offset;	temp = dma & ~0x0fff;	for (i = 0; i < 2; i++) {		itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);		itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));		temp += 0x1000;	}	itd->buf_dma = dma;	/*	 * this might be a "high bandwidth" highspeed endpoint,	 * as encoded in the ep descriptor's maxpacket field	 */	epnum = usb_pipeendpoint (urb->pipe);	if (usb_pipein (urb->pipe)) {		maxp = urb->dev->epmaxpacketin [epnum];		buf1 = (1 << 11);	} else {		maxp = urb->dev->epmaxpacketout [epnum];		buf1 = 0;	}	buf1 |= (maxp & 0x03ff);	multi = 1;	multi += (maxp >> 11) & 0x03;	maxp &= 0x03ff;	maxp *= multi;	/* transfer can't fit in any uframe? */ 	if (length < 0 || maxp < length) {		dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)",			length, maxp, urb, index,			urb->iso_frame_desc [index].length);		return -ENOSPC;	}	itd->usecs = HS_USECS_ISO (length);	/* "plus" info in low order bits of buffer pointers */	itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);	itd->hw_bufp [1] |= cpu_to_le32 (buf1);	itd->hw_bufp [2] |= cpu_to_le32 (multi);	/* figure hw_transaction[] value (it's scheduled later) */	itd->transaction = EHCI_ISOC_ACTIVE;	itd->transaction |= dma & 0x0fff;		/* offset; buffer=0 */	if ((index + 1) == urb->number_of_packets)		itd->transaction |= EHCI_ITD_IOC; 	/* end-of-urb irq */	itd->transaction |= length << 16;	cpu_to_le32s (&itd->transaction);	return 0;}static intitd_urb_transaction (	struct ehci_hcd		*ehci,	struct urb		*urb,	int			mem_flags) {	int			frame_index;	struct ehci_itd		*first_itd, *itd;	int			status;	dma_addr_t		buf_dma, itd_dma;	/* set up one dma mapping for this urb */	buf_dma = pci_map_single (ehci->hcd.pdev,		urb->transfer_buffer, urb->transfer_buffer_length,		usb_pipein (urb->pipe)		    ? PCI_DMA_FROMDEVICE		    : PCI_DMA_TODEVICE);	if (buf_dma == 0)		return -ENOMEM;	/* allocate/init ITDs */	for (frame_index = 0, first_itd = 0;			frame_index < urb->number_of_packets;			frame_index++) {		itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma);		if (!itd) {			status = -ENOMEM;			goto fail;		}		memset (itd, 0, sizeof *itd);		itd->itd_dma = itd_dma;		status = itd_fill (ehci, itd, urb, frame_index, buf_dma);		if (status != 0)			goto fail;		if (first_itd)			list_add_tail (&itd->itd_list,					&first_itd->itd_list);		else {			INIT_LIST_HEAD (&itd->itd_list);			urb->hcpriv = first_itd = itd;		}	}	urb->error_count = 0;	return 0;fail:	if (urb->hcpriv)		itd_free_list (ehci, urb);	return status;}/*-------------------------------------------------------------------------*/static inline voiditd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd){	/* always prepend ITD/SITD ... only QH tree is order-sensitive */	itd->itd_next = ehci->pshadow [frame];	itd->hw_next = ehci->periodic [frame];	ehci->pshadow [frame].itd = itd;	ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;}/* * return zero on success, else -errno * - start holds first uframe to start scheduling into * - max is the first uframe it's NOT (!) OK to start scheduling into * math to be done modulo "mod" (ehci->periodic_size << 3) */static int get_iso_range (	struct ehci_hcd		*ehci,	struct urb		*urb,	unsigned		*start,	unsigned		*max,	unsigned		mod) {	struct list_head	*lh;	struct hcd_dev		*dev = urb->dev->hcpriv;	int			last = -1;	unsigned		now, span, end;	span = urb->interval * urb->number_of_packets;	/* first see if we know when the next transfer SHOULD happen */	list_for_each (lh, &dev->urb_list) {		struct urb	*u;		struct ehci_itd	*itd;		unsigned	s;		u = list_entry (lh, struct urb, urb_list);		if (u == urb || u->pipe != urb->pipe)			continue;		if (u->interval != urb->interval) {	/* must not change! */ 			dbg ("urb %p interval %d ... != %p interval %d",				u, u->interval, urb, urb->interval);			return -EINVAL;		}				/* URB for this endpoint... covers through when?  */		itd = urb->hcpriv;		s = itd->uframe + u->interval * u->number_of_packets;		if (last < 0)			last = s;		else {			/*			 * So far we can only queue two ISO URBs...			 *			 * FIXME do interval math, figure out whether			 * this URB is "before" or not ... also, handle			 * the case where the URB might have completed,			 * but hasn't yet been processed.			 */			dbg ("NYET: queue >2 URBs per ISO endpoint");			return -EDOM;		}	}	/* calculate the legal range [start,max) */	now = readl (&ehci->regs->frame_index) + 1;	/* next uframe */	if (!ehci->periodic_urbs)		now += 8;				/* startup delay */	now %= mod;	end = now + mod;	if (last < 0) {		*start = now + ehci->i_thresh + /* paranoia */ 1;		*max = end - span;		if (*max < *start + 1)			*max = *start + 1;	} else {		*start = last % mod;		*max = (last + 1) % mod;	}	/* explicit start frame? */	if (!(urb->transfer_flags & USB_ISO_ASAP)) {		unsigned	temp;		/* sanity check: must be in range */		urb->start_frame %= ehci->periodic_size;		temp = urb->start_frame << 3;		if (temp < *start)			temp += mod;		if (temp > *max)			return -EDOM;		/* use that explicit start frame */		*start = urb->start_frame << 3;		temp += 8;		if (temp < *max)			*max = temp;	}	// FIXME minimize wraparound to "now" ... insist max+span	// (and start+span) remains a few frames short of "end"	*max %= ehci->periodic_size;	if ((*start + span) < end)		return 0;	return -EFBIG;}static intitd_schedule (struct ehci_hcd *ehci, struct urb *urb){	unsigned	start, max, i;	int		status;	unsigned	mod = ehci->periodic_size << 3;	for (i = 0; i < urb->number_of_packets; i++) {		urb->iso_frame_desc [i].status = -EINPROGRESS;		urb->iso_frame_desc [i].actual_length = 0;	}	if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0)		return status;	do {		unsigned	uframe;		unsigned	usecs;		struct ehci_itd	*itd;		/* check schedule: enough space? */		itd = urb->hcpriv;		uframe = start;		for (i = 0, uframe = start;				i < urb->number_of_packets;				i++, uframe += urb->interval) {			uframe %= mod;			/* can't commit more than 80% periodic == 100 usec */			if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)					> (100 - itd->usecs)) {				itd = 0;				break;			}			itd = list_entry (itd->itd_list.next,				struct ehci_itd, itd_list);		}		if (!itd)			continue;				/* that's where we'll schedule this! */		itd = urb->hcpriv;		urb->start_frame = start >> 3;		vdbg ("ISO urb %p (%d packets period %d) starting %d.%d",			urb, urb->number_of_packets, urb->interval,			urb->start_frame, start & 0x7);		for (i = 0, uframe = start, usecs = 0;				i < urb->number_of_packets;				i++, uframe += urb->interval) {			uframe %= mod;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -