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

📄 ehci-q-fehci.c

📁 WiFi IP-Cam solution. FIC8120 platform VIA VT6656 USB Module driver source code.
💻 C
📖 第 1 页 / 共 3 页
字号:

		if (!(cmd & CMD_ASE)) {
			/* in case a clear of CMD_ASE didn't take yet */
			(void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
			cmd |= CMD_ASE | CMD_RUN;
			writel (cmd, &ehci->regs->command);
			ehci->hcd.state = USB_STATE_RUNNING;
			/* posted write need not be known to HC yet ... */
		}
	}

	qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT);

	/* splice right after start */
	qh->qh_next = head->qh_next;
	qh->hw_next = head->hw_next;
	wmb ();

	head->qh_next.qh = qh;
	head->hw_next = dma;

	qh->qh_state = QH_STATE_LINKED;
	/* qtd completions reported later by interrupt */

	ehci->async_idle = 0;
}

/*-------------------------------------------------------------------------*/

/*
 * For control/bulk/interrupt, return QH with these TDs appended.
 * Allocates and initializes the QH if necessary.
 * Returns null if it can't allocate a QH it needs to.
 * If the QH has TDs (urbs) already, that's great.
 */
static struct ehci_qh *qh_append_tds (
	struct ehci_hcd		*ehci,
	struct urb		*urb,
	struct list_head	*qtd_list,
	int			epnum,
	void			**ptr
)
{
	struct ehci_qh		*qh = 0;

	qh = (struct ehci_qh *) *ptr;
	if (unlikely (qh == 0)) {
		/* can't sleep here, we have ehci->lock... */
		qh = qh_make (ehci, urb, SLAB_ATOMIC);
		*ptr = qh;
	}
	if (likely (qh != 0)) {
		struct ehci_qtd	*qtd;

		if (unlikely (list_empty (qtd_list)))
			qtd = 0;
		else
			qtd = list_entry (qtd_list->next, struct ehci_qtd,
					qtd_list);

		/* control qh may need patching after enumeration */
		if (unlikely (epnum == 0)) {
			/* set_address changes the address */
			if (le32_to_cpu (qh->hw_info1 & 0x7f) == 0)
				qh->hw_info1 |= cpu_to_le32 (
						usb_pipedevice (urb->pipe));

			/* for full speed, ep0 maxpacket can grow */
			else if (!(qh->hw_info1 & cpu_to_le32 (0x3 << 12))) {
				u32	info, max;

				info = le32_to_cpu (qh->hw_info1);
				max = urb->dev->descriptor.bMaxPacketSize0;
				if (max > (0x07ff & (info >> 16))) {
					info &= ~(0x07ff << 16);
					info |= max << 16;
					qh->hw_info1 = cpu_to_le32 (info);
				}
			}
		}

		/* FIXME:  changing config or interface setting is not
		 * supported yet.  preferred fix is for usbcore to tell
		 * us to clear out each endpoint's state, but...
		 */

		/* usb_clear_halt() means qh data toggle gets reset */
		if (unlikely (!usb_gettoggle (urb->dev,
					(epnum & 0x0f), !(epnum & 0x10)))
				&& !usb_pipecontrol (urb->pipe)) {
			/* "never happens": drivers do stall cleanup right */
			if (qh->qh_state != QH_STATE_IDLE
					&& !list_empty (&qh->qtd_list)
					&& qh->qh_state != QH_STATE_COMPLETING)
				ehci_warn (ehci, "clear toggle dev%d "
						"ep%d%s: not idle\n",
						usb_pipedevice (urb->pipe),
						epnum & 0x0f,
						usb_pipein (urb->pipe)
							? "in" : "out");
			/* else we know this overlay write is safe */
			clear_toggle (urb->dev,
				epnum & 0x0f, !(epnum & 0x10), qh);
		}

		/* just one way to queue requests: swap with the dummy qtd.
		 * only hc or qh_completions() usually modify the overlay.
		 */
		if (likely (qtd != 0)) {
			struct ehci_qtd		*dummy;
			dma_addr_t		dma;
			u32			token;

			/* to avoid racing the HC, use the dummy td instead of
			 * the first td of our list (becomes new dummy).  both
			 * tds stay deactivated until we're done, when the
			 * HC is allowed to fetch the old dummy (4.10.2).
			 */
			token = qtd->hw_token;
			qtd->hw_token = 0;
			wmb ();
			dummy = qh->dummy;

			dma = dummy->qtd_dma;
			*dummy = *qtd;
			dummy->qtd_dma = dma;

			list_del (&qtd->qtd_list);
			list_add (&dummy->qtd_list, qtd_list);
			__list_splice (qtd_list, qh->qtd_list.prev);

			ehci_qtd_init (qtd, qtd->qtd_dma);
			qh->dummy = qtd;

			/* hc must see the new dummy at list end */
			dma = qtd->qtd_dma;
			qtd = list_entry (qh->qtd_list.prev,
					struct ehci_qtd, qtd_list);
			qtd->hw_next = QTD_NEXT (dma);

			/* let the hc process these next qtds */
			wmb ();
			dummy->hw_token = token;

			urb->hcpriv = qh_get (qh);
		}
	}
	return qh;
}

/*-------------------------------------------------------------------------*/

static int
submit_async (
	struct ehci_hcd		*ehci,
	struct urb		*urb,
	struct list_head	*qtd_list,
	int			mem_flags
) {
	struct ehci_qtd		*qtd;
	struct hcd_dev		*dev;
	int			epnum;
	unsigned long		flags;
	struct ehci_qh		*qh = 0;

	qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
	dev = (struct hcd_dev *)urb->dev->hcpriv;
	epnum = usb_pipeendpoint (urb->pipe);
	if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe))
		epnum |= 0x10;

	vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
		hcd_to_bus (&ehci->hcd)->bus_name,
		urb, urb->transfer_buffer_length,
		epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
		qtd, dev ? dev->ep [epnum] : (void *)~0);

	spin_lock_irqsave (&ehci->lock, flags);
	qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);

//Add Einsn@VIA

  #ifdef CONFIG_FARADAY_FEHCI
  // The control endpoint's maximum packet size will be modified after fist "Get_Descriptor" request.
  // There the maxp of qH should be updated here.
  if ( usb_pipecontrol(urb->pipe) )
  {
    //maxp = (usb_maxpacket (urb->dev, urb->pipe, !(usb_pipein (urb->pipe)))<<16);
    qh->hw_info1 = cpu_to_le32((le32_to_cpu(qh->hw_info1)&(~(((1<<11)-1)<<16)))|(usb_maxpacket (urb->dev, urb->pipe, !(usb_pipein (urb->pipe)))<<16));
  }
  #endif

//End Add

	/* Control/bulk operations through TTs don't need scheduling,
	 * the HC and TT handle it when the TT has a buffer ready.
	 */
	if (likely (qh != 0)) {
		if (likely (qh->qh_state == QH_STATE_IDLE))
			qh_link_async (ehci, qh_get (qh));
	}
	spin_unlock_irqrestore (&ehci->lock, flags);
	if (unlikely (qh == 0)) {
		qtd_list_free (ehci, urb, qtd_list);
		return -ENOMEM;
	}
	return 0;
}

/*-------------------------------------------------------------------------*/

/* the async qh for the qtds being reclaimed are now unlinked from the HC */

static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);

static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
{
	struct ehci_qh		*qh = ehci->reclaim;
	struct ehci_qh		*next;

	del_timer (&ehci->watchdog);

	qh->hw_next = cpu_to_le32 (qh->qh_dma);
	qh->qh_state = QH_STATE_IDLE;
	qh->qh_next.qh = 0;
	qh_put (ehci, qh);			// refcount from reclaim 
	ehci->reclaim = 0;
	ehci->reclaim_ready = 0;

	/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
	next = qh->reclaim;
	qh->reclaim = 0;

	qh_completions (ehci, qh, regs);

	if (!list_empty (&qh->qtd_list)
			&& HCD_IS_RUNNING (ehci->hcd.state))
		qh_link_async (ehci, qh);
	else {
		qh_put (ehci, qh);		// refcount from async list

		/* it's not free to turn the async schedule on/off; leave it
		 * active but idle for a while once it empties.
		 */
		if (HCD_IS_RUNNING (ehci->hcd.state)
				&& ehci->async->qh_next.qh == 0
				&& !timer_pending (&ehci->watchdog)) {
			ehci->async_idle = 1;
			mod_timer (&ehci->watchdog,
					jiffies + EHCI_ASYNC_JIFFIES);
		}
	}

	if (next)
		start_unlink_async (ehci, next);
}

/* makes sure the async qh will become idle */
/* caller must own ehci->lock */

static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
	int		cmd = readl (&ehci->regs->command);
	struct ehci_qh	*prev;

#ifdef DEBUG
	if (ehci->reclaim
			|| (qh->qh_state != QH_STATE_LINKED
				&& qh->qh_state != QH_STATE_UNLINK_WAIT)
#ifdef CONFIG_SMP
// this macro lies except on SMP compiles
			|| !spin_is_locked (&ehci->lock)
#endif
			)
		BUG ();
#endif

	/* stop async schedule right now? */
	if (unlikely (qh == ehci->async)) {
		/* can't get here without STS_ASS set */
		if (ehci->hcd.state != USB_STATE_HALT) {
			writel (cmd & ~CMD_ASE, &ehci->regs->command);
			wmb ();
			// handshake later, if we need to
		}
		return;
	} 

	qh->qh_state = QH_STATE_UNLINK;
	ehci->reclaim = qh = qh_get (qh);

	prev = ehci->async;
	while (prev->qh_next.qh != qh)
		prev = prev->qh_next.qh;

	prev->hw_next = qh->hw_next;
	prev->qh_next = qh->qh_next;
	wmb ();

	if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
		/* if (unlikely (qh->reclaim != 0))
		 * 	this will recurse, probably not much
		 */
		end_unlink_async (ehci, NULL);
		return;
	}

	ehci->reclaim_ready = 0;
	cmd |= CMD_IAAD;
	writel (cmd, &ehci->regs->command);
	/* posted write need not be known to HC yet ... */

	mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES);
}

/*-------------------------------------------------------------------------*/

static void
scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
{
	struct ehci_qh		*qh;
	int			unlink_delay = 0;

	if (!++(ehci->stamp))
		ehci->stamp++;
rescan:
	qh = ehci->async->qh_next.qh;
	if (likely (qh != 0)) {
		do {
			/* clean any finished work for this qh */
			if (!list_empty (&qh->qtd_list)
					&& qh->stamp != ehci->stamp) {
				int temp;

				/* unlinks could happen here; completion
				 * reporting drops the lock.  rescan using
				 * the latest schedule, but don't rescan
				 * qhs we already finished (no looping).
				 */
				qh = qh_get (qh);
				qh->stamp = ehci->stamp;
				temp = qh_completions (ehci, qh, regs);
				qh_put (ehci, qh);
				if (temp != 0) {
					goto rescan;
				}
			}

			/* unlink idle entries, reducing HC PCI usage as well
			 * as HCD schedule-scanning costs.  delay for any qh
			 * we just scanned, there's a not-unusual case that it
			 * doesn't stay idle for long.
			 * (plus, avoids some kind of re-activation race.)
			 */
			if (list_empty (&qh->qtd_list)) {
				if (qh->stamp == ehci->stamp)
					unlink_delay = 1;
				else if (!ehci->reclaim) {
					start_unlink_async (ehci, qh);
					unlink_delay = 0;
				}
			}

			qh = qh->qh_next.qh;
		} while (qh);
	}

	if (unlink_delay && !timer_pending (&ehci->watchdog))
		mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES/2);
}

⌨️ 快捷键说明

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