📄 ehci-hcd.c
字号:
static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 status; int bh; spin_lock (&ehci->lock); status = readl (&ehci->regs->status); /* e.g. cardbus physical eject */ if (status == ~(u32) 0) { ehci_dbg (ehci, "device removed\n"); goto dead; } status &= INTR_MASK; if (!status) { /* irq sharing? */ spin_unlock(&ehci->lock); return IRQ_NONE; } /* clear (just) interrupts */ writel (status, &ehci->regs->status); readl (&ehci->regs->command); /* unblock posted write */ bh = 0;#ifdef EHCI_VERBOSE_DEBUG /* unrequested/ignored: Frame List Rollover */ dbg_status (ehci, "irq", status);#endif /* INT, ERR, and IAA interrupt rates can be throttled */ /* normal [4.15.1.2] or error [4.15.1.1] completion */ if (likely ((status & (STS_INT|STS_ERR)) != 0)) { if (likely ((status & STS_ERR) == 0)) COUNT (ehci->stats.normal); else COUNT (ehci->stats.error); bh = 1; } /* complete the unlinking of some qh [4.15.2.3] */ if (status & STS_IAA) { COUNT (ehci->stats.reclaim); ehci->reclaim_ready = 1; bh = 1; } /* remote wakeup [4.3.1] */ if ((status & STS_PCD) && hcd->remote_wakeup) { unsigned i = HCS_N_PORTS (ehci->hcs_params); /* resume root hub? */ status = readl (&ehci->regs->command); if (!(status & CMD_RUN)) writel (status | CMD_RUN, &ehci->regs->command); while (i--) { status = readl (&ehci->regs->port_status [i]); if (status & PORT_OWNER) continue; if (!(status & PORT_RESUME) || ehci->reset_done [i] != 0) continue; /* start 20 msec resume signaling from this port, * and make khubd collect PORT_STAT_C_SUSPEND to * stop that signaling. */ ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); mod_timer (&hcd->rh_timer, ehci->reset_done [i] + 1); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); } } /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { /* bogus "fatal" IRQs appear on some chips... why? */ status = readl (&ehci->regs->status); dbg_cmd (ehci, "fatal", readl (&ehci->regs->command)); dbg_status (ehci, "fatal", status); if (status & STS_HALT) { ehci_err (ehci, "fatal error\n");dead: ehci_reset (ehci); writel (0, &ehci->regs->configured_flag); /* generic layer kills/unlinks all urbs, then * uses ehci_stop to clean up the rest */ bh = 1; } } if (bh) ehci_work (ehci, regs); spin_unlock (&ehci->lock); return IRQ_HANDLED;}/*-------------------------------------------------------------------------*//* * non-error returns are a promise to giveback() the urb later * we drop ownership so next owner (or urb unlink) can get it * * urb + dev is in hcd.self.controller.urb_list * we're queueing TDs onto software and hardware lists * * hcd-specific init for hcpriv hasn't been done yet * * NOTE: control, bulk, and interrupt share the same code to append TDs * to a (possibly active) QH, and the same QH scanning code. */static int ehci_urb_enqueue ( struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct urb *urb, int mem_flags) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct list_head qtd_list; INIT_LIST_HEAD (&qtd_list); switch (usb_pipetype (urb->pipe)) { // case PIPE_CONTROL: // case PIPE_BULK: default: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; return submit_async (ehci, ep, urb, &qtd_list, mem_flags); case PIPE_INTERRUPT: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; return intr_submit (ehci, ep, urb, &qtd_list, mem_flags); case PIPE_ISOCHRONOUS: if (urb->dev->speed == USB_SPEED_HIGH) return itd_submit (ehci, urb, mem_flags); else return sitd_submit (ehci, urb, mem_flags); }}static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh){ /* if we need to use IAA and it's busy, defer */ if (qh->qh_state == QH_STATE_LINKED && ehci->reclaim && HCD_IS_RUNNING (ehci_to_hcd(ehci)->state)) { struct ehci_qh *last; for (last = ehci->reclaim; last->reclaim; last = last->reclaim) continue; qh->qh_state = QH_STATE_UNLINK_WAIT; last->reclaim = qh; /* bypass IAA if the hc can't care */ } else if (!HCD_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) end_unlink_async (ehci, NULL); /* something else might have unlinked the qh by now */ if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh);}/* remove from hardware lists * completions normally happen asynchronously */static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_qh *qh; unsigned long flags; spin_lock_irqsave (&ehci->lock, flags); switch (usb_pipetype (urb->pipe)) { // case PIPE_CONTROL: // case PIPE_BULK: default: qh = (struct ehci_qh *) urb->hcpriv; if (!qh) break; unlink_async (ehci, qh); break; case PIPE_INTERRUPT: qh = (struct ehci_qh *) urb->hcpriv; if (!qh) break; switch (qh->qh_state) { case QH_STATE_LINKED: intr_deschedule (ehci, qh); /* FALL THROUGH */ case QH_STATE_IDLE: qh_completions (ehci, qh, NULL); break; default: ehci_dbg (ehci, "bogus qh %p state %d\n", qh, qh->qh_state); goto done; } /* reschedule QH iff another request is queued */ if (!list_empty (&qh->qtd_list) && HCD_IS_RUNNING (hcd->state)) { int status; status = qh_schedule (ehci, qh); spin_unlock_irqrestore (&ehci->lock, flags); if (status != 0) { // shouldn't happen often, but ... // FIXME kill those tds' urbs err ("can't reschedule qh %p, err %d", qh, status); } return status; } break; case PIPE_ISOCHRONOUS: // itd or sitd ... // wait till next completion, do it then. // completion irqs can wait up to 1024 msec, break; }done: spin_unlock_irqrestore (&ehci->lock, flags); return 0;}/*-------------------------------------------------------------------------*/// bulk qh holds the data togglestatic voidehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); unsigned long flags; struct ehci_qh *qh, *tmp; /* ASSERT: any requests/urbs are being unlinked */ /* ASSERT: nobody can be submitting urbs for this any more */rescan: spin_lock_irqsave (&ehci->lock, flags); qh = ep->hcpriv; if (!qh) goto done; /* endpoints can be iso streams. for now, we don't * accelerate iso completions ... so spin a while. */ if (qh->hw_info1 == 0) { ehci_vdbg (ehci, "iso delay\n"); goto idle_timeout; } if (!HCD_IS_RUNNING (hcd->state)) qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: for (tmp = ehci->async->qh_next.qh; tmp && tmp != qh; tmp = tmp->qh_next.qh) continue; /* periodic qh self-unlinks on empty */ if (!tmp) goto nogood; unlink_async (ehci, qh); /* FALL THROUGH */ case QH_STATE_UNLINK: /* wait for hw to finish? */idle_timeout: spin_unlock_irqrestore (&ehci->lock, flags); set_current_state (TASK_UNINTERRUPTIBLE); schedule_timeout (1); goto rescan; case QH_STATE_IDLE: /* fully unlinked */ if (list_empty (&qh->qtd_list)) { qh_put (qh); break; } /* else FALL THROUGH */ default:nogood: /* caller was supposed to have unlinked any requests; * that's not our job. just leak this memory. */ ehci_err (ehci, "qh %p (#%02x) state %d%s\n", qh, ep->desc.bEndpointAddress, qh->qh_state, list_empty (&qh->qtd_list) ? "" : "(has tds)"); break; } ep->hcpriv = NULL;done: spin_unlock_irqrestore (&ehci->lock, flags); return;}/*-------------------------------------------------------------------------*/static const struct hc_driver ehci_driver = { .description = hcd_name, .product_desc = "EHCI Host Controller", .hcd_priv_size = sizeof(struct ehci_hcd), /* * generic hardware linkage */ .irq = ehci_irq, .flags = HCD_MEMORY | HCD_USB2, /* * basic lifecycle operations */ .reset = ehci_hc_reset, .start = ehci_start,#ifdef CONFIG_PM .suspend = ehci_suspend, .resume = ehci_resume,#endif .stop = ehci_stop, /* * managing i/o requests and associated device resources */ .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, /* * scheduling support */ .get_frame_number = ehci_get_frame, /* * root hub support */ .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, .hub_suspend = ehci_hub_suspend, .hub_resume = ehci_hub_resume,};/*-------------------------------------------------------------------------*//* EHCI 1.0 doesn't require PCI */#ifdef CONFIG_PCI/* PCI driver selection metadata; PCI hotplugging uses this */static const struct pci_device_id pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0), .driver_data = (unsigned long) &ehci_driver, }, { /* end: all zeroes */ }};MODULE_DEVICE_TABLE (pci, pci_ids);/* pci driver glue; this is a "new style" PCI driver module */static struct pci_driver ehci_pci_driver = { .name = (char *) hcd_name, .id_table = pci_ids, .probe = usb_hcd_pci_probe, .remove = usb_hcd_pci_remove,#ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, .resume = usb_hcd_pci_resume,#endif};#endif /* PCI */#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESCMODULE_DESCRIPTION (DRIVER_INFO);MODULE_AUTHOR (DRIVER_AUTHOR);MODULE_LICENSE ("GPL");static int __init init (void) { if (usb_disabled()) return -ENODEV; pr_debug ("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n", hcd_name, sizeof (struct ehci_qh), sizeof (struct ehci_qtd), sizeof (struct ehci_itd), sizeof (struct ehci_sitd)); return pci_register_driver (&ehci_pci_driver);}module_init (init);static void __exit cleanup (void) { pci_unregister_driver (&ehci_pci_driver);}module_exit (cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -