📄 ehci-hcd.c
字号:
struct ehci_hcd *ehci = hcd_to_ehci (hcd); int ports; int i; dbg ("%s: resume", hcd->bus_name); ports = HCS_N_PORTS (ehci->hcs_params); // FIXME: if controller didn't retain state, // return and let generic code clean it up // test configured_flag ? /* resume HC and each port */// restore pci FLADJ value // khubd and drivers will set HC running, if needed; hcd->state = USB_STATE_READY; // FIXME Philips/Intel/... etc don't really have a "READY" // state ... turn on CMD_RUN too for (i = 0; i < ports; i++) { int temp = readl (&ehci->regs->port_status [i]); if ((temp & PORT_PE) == 0 || (temp & PORT_SUSPEND) != 0) continue;dbg ("%s: resume port %d", hcd->bus_name, i); temp |= PORT_RESUME; writel (temp, &ehci->regs->port_status [i]); readl (&ehci->regs->command); /* unblock posted writes */ wait_ms (20); temp &= ~PORT_RESUME; writel (temp, &ehci->regs->port_status [i]); } readl (&ehci->regs->command); /* unblock posted writes */ return 0;}#endif/*-------------------------------------------------------------------------*//* * tasklet scheduled by some interrupts and other events * calls driver completion functions ... but not in_irq() */static void ehci_tasklet (unsigned long param){ struct ehci_hcd *ehci = (struct ehci_hcd *) param; if (ehci->reclaim_ready) end_unlink_async (ehci); scan_async (ehci); if (ehci->next_uframe != -1) scan_periodic (ehci);}/*-------------------------------------------------------------------------*/static void ehci_irq (struct usb_hcd *hcd){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 status = readl (&ehci->regs->status); int bh; status &= INTR_MASK; if (!status) /* irq sharing? */ return; /* clear (just) interrupts */ writel (status, &ehci->regs->status); readl (&ehci->regs->command); /* unblock posted write */ bh = 0;#ifdef EHCI_VERBOSE_DEBUG /* unrequested/ignored: Port Change Detect, 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)) bh = 1; /* complete the unlinking of some qh [4.15.2.3] */ if (status & STS_IAA) { ehci->reclaim_ready = 1; bh = 1; } /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { err ("%s: fatal error, state %x", hcd->bus_name, hcd->state); ehci_reset (ehci); // generic layer kills/unlinks all urbs // then tasklet cleans up the rest bh = 1; } /* most work doesn't need to be in_irq() */ if (likely (bh == 1)) tasklet_schedule (&ehci->tasklet);}/*-------------------------------------------------------------------------*//* * 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_dev.urb_list * we're queueing TDs onto software and hardware lists * * hcd-specific init for hcpriv hasn't been done yet * * NOTE: EHCI queues control and bulk requests transparently, like OHCI. */static int ehci_urb_enqueue ( struct usb_hcd *hcd, struct urb *urb, int mem_flags) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct list_head qtd_list; urb->transfer_flags &= ~EHCI_STATE_UNLINK; INIT_LIST_HEAD (&qtd_list); switch (usb_pipetype (urb->pipe)) { case PIPE_CONTROL: case PIPE_BULK: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; return submit_async (ehci, urb, &qtd_list, mem_flags); case PIPE_INTERRUPT: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; return intr_submit (ehci, urb, &qtd_list, mem_flags); case PIPE_ISOCHRONOUS: if (urb->dev->speed == USB_SPEED_HIGH) return itd_submit (ehci, urb, mem_flags);#ifdef have_split_iso else return sitd_submit (ehci, urb, mem_flags);#else dbg ("no split iso support yet"); return -ENOSYS;#endif /* have_split_iso */ default: /* can't happen */ return -ENOSYS; }}/* 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 = (struct ehci_qh *) urb->hcpriv; unsigned long flags; dbg ("%s urb_dequeue %p qh state %d", hcd->bus_name, urb, qh->qh_state); switch (usb_pipetype (urb->pipe)) { case PIPE_CONTROL: case PIPE_BULK: spin_lock_irqsave (&ehci->lock, flags); if (ehci->reclaim) {dbg ("dq: reclaim busy, %s", RUN_CONTEXT); if (in_interrupt ()) { spin_unlock_irqrestore (&ehci->lock, flags); return -EAGAIN; } while (qh->qh_state == QH_STATE_LINKED && ehci->reclaim && ehci->hcd.state != USB_STATE_HALT ) { spin_unlock_irqrestore (&ehci->lock, flags);// yeech ... this could spin for up to two frames!dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d", qh->qh_state, ehci->reclaim, ehci->hcd.state); udelay (100); spin_lock_irqsave (&ehci->lock, flags); } } if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh); spin_unlock_irqrestore (&ehci->lock, flags); return 0; case PIPE_INTERRUPT: intr_deschedule (ehci, urb->start_frame, qh, (urb->dev->speed == USB_SPEED_HIGH) ? urb->interval : (urb->interval << 3)); if (ehci->hcd.state == USB_STATE_HALT) urb->status = -ESHUTDOWN; qh_completions (ehci, qh, 1); return 0; case PIPE_ISOCHRONOUS: // itd or sitd ... // wait till next completion, do it then. // completion irqs can wait up to 1024 msec, urb->transfer_flags |= EHCI_STATE_UNLINK; return 0; } return -EINVAL;}/*-------------------------------------------------------------------------*/// bulk qh holds the data togglestatic void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev){ struct hcd_dev *dev = (struct hcd_dev *)udev->hcpriv; struct ehci_hcd *ehci = hcd_to_ehci (hcd); int i; unsigned long flags; /* ASSERT: nobody can be submitting urbs for this any more */ dbg ("%s: free_config devnum %d", hcd->bus_name, udev->devnum); spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < 32; i++) { if (dev->ep [i]) { struct ehci_qh *qh; /* dev->ep never has ITDs or SITDs */ qh = (struct ehci_qh *) dev->ep [i]; vdbg ("free_config, ep 0x%02x qh %p", i, qh); if (!list_empty (&qh->qtd_list)) { dbg ("ep 0x%02x qh %p not empty!", i, qh); BUG (); } dev->ep [i] = 0; /* wait_ms() won't spin here -- we're a thread */ while (qh->qh_state == QH_STATE_LINKED && ehci->reclaim && ehci->hcd.state != USB_STATE_HALT ) { spin_unlock_irqrestore (&ehci->lock, flags); wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); } if (qh->qh_state == QH_STATE_LINKED) { start_unlink_async (ehci, qh); while (qh->qh_state != QH_STATE_IDLE) { spin_unlock_irqrestore (&ehci->lock, flags); wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); } } qh_put (ehci, qh); } } spin_unlock_irqrestore (&ehci->lock, flags);}/*-------------------------------------------------------------------------*/static const char hcd_name [] = "ehci-hcd";static const struct hc_driver ehci_driver = { description: hcd_name, /* * generic hardware linkage */ irq: ehci_irq, flags: HCD_MEMORY | HCD_USB2, /* * basic lifecycle operations */ start: ehci_start,#ifdef CONFIG_PM suspend: ehci_suspend, resume: ehci_resume,#endif stop: ehci_stop, /* * memory lifecycle (except per-request) */ hcd_alloc: ehci_hcd_alloc, hcd_free: ehci_hcd_free, /* * managing i/o requests and associated device resources */ urb_enqueue: ehci_urb_enqueue, urb_dequeue: ehci_urb_dequeue, free_config: ehci_free_config, /* * scheduling support */ get_frame_number: ehci_get_frame, /* * root hub support */ hub_status_data: ehci_hub_status_data, hub_control: ehci_hub_control,};/*-------------------------------------------------------------------------*//* EHCI spec says PCI is required. *//* PCI driver selection metadata; PCI hotplugging uses this */static const struct pci_device_id __devinitdata pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ class: ((PCI_CLASS_SERIAL_USB << 8) | 0x20), class_mask: ~0, driver_data: (unsigned long) &ehci_driver, /* no matter who makes it */ vendor: PCI_ANY_ID, device: PCI_ANY_ID, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID,}, { /* 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};#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESCEXPORT_NO_SYMBOLS;MODULE_DESCRIPTION (DRIVER_INFO);MODULE_AUTHOR (DRIVER_AUTHOR);MODULE_LICENSE ("GPL");static int __init init (void) { dbg (DRIVER_INFO); dbg ("block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd", sizeof (struct ehci_qh), sizeof (struct ehci_qtd), sizeof (struct ehci_itd), sizeof (struct ehci_sitd)); return pci_module_init (&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 + -