ehci-hcd.c
来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,101 行 · 第 1/2 页
C
1,101 行
* * Before this point the HC was idle/ready. After, khubd * and device drivers may start it running. */ udev->speed = USB_SPEED_HIGH; if (hcd_register_root (udev, hcd) != 0) { if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); ehci_reset (ehci); usb_put_dev (udev); retval = -ENODEV; goto done2; } writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */ create_debug_files (ehci); return 0;}/* always called by thread; normally rmmod */static void ehci_stop (struct usb_hcd *hcd){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); u8 rh_ports, port; ehci_dbg (ehci, "stop\n"); /* no more interrupts ... */ if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); if (in_interrupt ()) { /* must not happen!! */ ehci_err (ehci, "stopped in_interrupt!\n"); return; } del_timer_sync (&ehci->watchdog); /* Turn off port power on all root hub ports. */ rh_ports = HCS_N_PORTS (ehci->hcs_params); for (port = 1; port <= rh_ports; port++) { ehci_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, port, NULL, 0); } ehci_reset (ehci); writel (0, &ehci->regs->intr_enable); /* let companion controllers work when we aren't */ writel (0, &ehci->regs->configured_flag); unregister_reboot_notifier (&ehci->reboot_notifier); remove_debug_files (ehci); /* root hub is shut down separately (first, when possible) */ spin_lock_irq (&ehci->lock); if (ehci->async) ehci_work (ehci, NULL); spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci);#ifdef EHCI_STATS ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n", ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, ehci->stats.lost_iaa); ehci_dbg (ehci, "complete %ld unlink %ld\n", ehci->stats.complete, ehci->stats.unlink);#endif dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));}static int ehci_get_frame (struct usb_hcd *hcd){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;}/*-------------------------------------------------------------------------*/#ifdef CONFIG_PM/* suspend/resume, section 4.3 *//* These routines rely on PCI to handle powerdown and wakeup, and * transceivers that don't need any software attention to set up * the right sort of wakeup. */static int ehci_suspend (struct usb_hcd *hcd, u32 state){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); while (time_before (jiffies, ehci->next_statechange)) msleep (100);#ifdef CONFIG_USB_SUSPEND (void) usb_suspend_device (hcd->self.root_hub, state);#else /* FIXME lock root hub */ (void) ehci_hub_suspend (hcd);#endif // save (PCI) FLADJ in case of Vaux power loss return 0;}static int ehci_resume (struct usb_hcd *hcd){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); int retval; // maybe restore (PCI) FLADJ while (time_before (jiffies, ehci->next_statechange)) msleep (100);#ifdef CONFIG_USB_SUSPEND retval = usb_resume_device (hcd->self.root_hub);#else /* FIXME lock root hub */ retval = ehci_hub_resume (hcd);#endif if (retval == 0) hcd->self.controller->power.power_state = 0; return retval;}#endif/*-------------------------------------------------------------------------*//* * ehci_work is called from some interrupts, timers, and so on. * it calls driver completion functions, after dropping ehci->lock. */static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs){ timer_action_done (ehci, TIMER_IO_WATCHDOG); if (ehci->reclaim_ready) end_unlink_async (ehci, regs); scan_async (ehci, regs); if (ehci->next_uframe != -1) scan_periodic (ehci, regs); /* the IO watchdog guards against hardware or driver bugs that * misplace IRQs, and should let us run completely without IRQs. * such lossage has been observed on both VT6202 and VT8235. */ if ((ehci->async->qh_next.ptr != 0) || (ehci->periodic_sched != 0)) timer_action (ehci, TIMER_IO_WATCHDOG);}/*-------------------------------------------------------------------------*/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) && ehci->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 (&ehci->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)) { ehci_err (ehci, "fatal error\n");dead: ehci_reset (ehci); /* 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 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, 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); else return sitd_submit (ehci, urb, mem_flags); }}/* 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; /* if we need to use IAA and it's busy, defer */ if (qh->qh_state == QH_STATE_LINKED && ehci->reclaim && HCD_IS_RUNNING (ehci->hcd.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->hcd.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); break; case PIPE_INTERRUPT: qh = (struct ehci_qh *) urb->hcpriv; if (!qh) break; if (qh->qh_state == QH_STATE_LINKED) { /* messy, can spin or block a microframe ... */ intr_deschedule (ehci, qh, 1); /* qh_state == IDLE */ } qh_completions (ehci, qh, NULL); /* reschedule QH iff another request is queued */ if (!list_empty (&qh->qtd_list) && HCD_IS_RUNNING (ehci->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; } spin_unlock_irqrestore (&ehci->lock, flags); return 0;}/*-------------------------------------------------------------------------*/// bulk qh holds the data togglestatic voidehci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); int epnum; unsigned long flags; struct ehci_qh *qh; /* ASSERT: any requests/urbs are being unlinked */ /* ASSERT: nobody can be submitting urbs for this any more */ epnum = ep & USB_ENDPOINT_NUMBER_MASK; if (epnum != 0 && (ep & USB_DIR_IN)) epnum |= 0x10;rescan: spin_lock_irqsave (&ehci->lock, flags); qh = (struct ehci_qh *) dev->ep [epnum]; 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 (ehci->hcd.state)) qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { 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: /* caller was supposed to have unlinked any requests; * that's not our job. just leak this memory. */ ehci_err (ehci, "qh %p (#%d) state %d%s\n", qh, epnum, qh->qh_state, list_empty (&qh->qtd_list) ? "" : "(has tds)"); break; } dev->ep[epnum] = NULL;done: spin_unlock_irqrestore (&ehci->lock, flags); return;}/*-------------------------------------------------------------------------*/static const struct hc_driver ehci_driver = { .description = hcd_name, /* * 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, /* * 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, .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_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 + =
减小字号Ctrl + -
显示快捷键?