📄 ehci-q-fehci.c
字号:
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 + -