本文共 11752 字,大约阅读时间需要 39 分钟。
android手机或平板,作为主设备,对usb的加载流程如下,这时外围设备是usb从设备,例如:usb的hub或者u 盘。
软件版本:android 6.0.1
1. 开机后,usb功能正常初始化;这时插入usb的U盘,或者hub;则首先调用如下代码。
static int hcd_pci_runtime_resume(struct device *dev){ int retval; powermac_set_asic(to_pci_dev(dev), 1); retval = resume_common(dev, PM_EVENT_AUTO_RESUME); dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); return retval;}
然后继续调用:
static int resume_common(struct device *dev, int event){ struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); int retval; if (HCD_RH_RUNNING(hcd) || (hcd->shared_hcd && HCD_RH_RUNNING(hcd->shared_hcd))) { dev_dbg(dev, "can't resume, not suspended!\n"); return 0; } retval = pci_enable_device(pci_dev); if (retval < 0) { dev_err(dev, "can't re-enable after resume, %d!\n", retval); return retval; } pci_set_master(pci_dev); if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) { /* * Only EHCI controllers have to wait for their companions. * No locking is needed because PCI controller drivers do not * get unbound during system resume. */ if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME) for_each_companion(pci_dev, hcd, ehci_wait_for_companions); retval = hcd->driver->pci_resume(hcd, event == PM_EVENT_RESTORE); if (retval) { dev_err(dev, "PCI post-resume error %d!\n", retval); if (hcd->shared_hcd) usb_hc_died(hcd->shared_hcd); usb_hc_died(hcd); } } return retval;}在 代码 hcd->driver->pci_resume() 里, 调用函数如下:
static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated){ struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval = 0; /* Due to one HW bug, XHCI will keep generating PME wakeups and fail * to stay in runtime suspended state, so required to clear the internal * PME flag once it is back to D0 as the software workaround */ if (xhci->quirks & XHCI_SPURIOUS_PME) { xhci_intel_clr_internal_pme_flag(xhci); xhci_intel_ssic_port_unused(xhci, 0); } /* The BIOS on systems with the Intel Panther Point chipset may or may * not support xHCI natively. That means that during system resume, it * may switch the ports back to EHCI so that users can use their * keyboard to select a kernel from GRUB after resume from hibernate. * * The BIOS is supposed to remember whether the OS had xHCI ports * enabled before resume, and switch the ports back to xHCI when the * BIOS/OS semaphore is written, but we all know we can't trust BIOS * writers. * * Unconditionally switch the ports back to xHCI after a system resume. * It should not matter whether the EHCI or xHCI controller is * resumed first. It's enough to do the switchover in xHCI because * USB core won't notice anything as the hub driver doesn't start * running again until after all the devices (including both EHCI and * xHCI host controllers) have been resumed. */ if (pdev->vendor == PCI_VENDOR_ID_INTEL) usb_enable_intel_xhci_ports(pdev); retval = xhci_resume(xhci, hibernated); return retval;}然后调用函数,xhci_resume();
int xhci_resume(struct xhci_hcd *xhci, bool hibernated){ u32 command, temp = 0, status; struct usb_hcd *hcd = xhci_to_hcd(xhci); struct usb_hcd *secondary_hcd; int retval = 0; bool comp_timer_running = false; pr_info("wgq[%s-%d] hibernated[%d]\n",__func__,__LINE__,hibernated); /* Wait a bit if either of the roothubs need to settle from the * transition into bus suspend. */ if (time_before(jiffies, xhci->bus_state[0].next_statechange) || time_before(jiffies, xhci->bus_state[1].next_statechange)) msleep(100); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); spin_lock_irq(&xhci->lock); if (xhci->quirks & XHCI_RESET_ON_RESUME) hibernated = true; if (!hibernated) { /* step 1: restore register */ xhci_restore_registers(xhci); /* step 2: initialize command ring buffer */ xhci_set_cmd_ring_deq(xhci); /* step 3: restore state and start state*/ /* step 3: set CRS flag */ command = readl(&xhci->op_regs->command); command |= CMD_CRS; writel(command, &xhci->op_regs->command); if (xhci_handshake(xhci, &xhci->op_regs->status, STS_RESTORE, 0, 10 * 1000)) { xhci_warn(xhci, "WARN: xHC restore state timeout\n"); spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; } temp = readl(&xhci->op_regs->status); } /* If restore operation fails, re-initialize the HC during resume */ if ((temp & STS_SRE) || hibernated) { if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !(xhci_all_ports_seen_u0(xhci))) { del_timer_sync(&xhci->comp_mode_recovery_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "Compliance Mode Recovery Timer deleted!"); } /* Let the USB core know _both_ roothubs lost power. */ usb_root_hub_lost_power(xhci->main_hcd->self.root_hub); usb_root_hub_lost_power(xhci->shared_hcd->self.root_hub); xhci_dbg(xhci, "Stop HCD\n"); xhci_halt(xhci); xhci_reset(xhci); spin_unlock_irq(&xhci->lock); xhci_cleanup_msix(xhci); xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel(temp & ~STS_EINT, &xhci->op_regs->status); temp = readl(&xhci->ir_set->irq_pending); writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); xhci_print_ir_set(xhci, 0); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); xhci_dbg(xhci, "xhci_stop completed - status = %x\n", readl(&xhci->op_regs->status)); /* USB core calls the PCI reinit and start functions twice: * first with the primary HCD, and then with the secondary HCD. * If we don't do the same, the host will never be started. */ if (!usb_hcd_is_primary_hcd(hcd)) secondary_hcd = hcd; else secondary_hcd = xhci->shared_hcd; xhci_dbg(xhci, "Initialize the xhci_hcd\n"); retval = xhci_init(hcd->primary_hcd); if (retval) return retval; comp_timer_running = true; xhci_dbg(xhci, "Start the primary HCD\n"); retval = xhci_run(hcd->primary_hcd); if (!retval) { xhci_dbg(xhci, "Start the secondary HCD\n"); retval = xhci_run(secondary_hcd); } hcd->state = HC_STATE_SUSPENDED; xhci->shared_hcd->state = HC_STATE_SUSPENDED; goto done; } /* step 4: set Run/Stop bit */ command = readl(&xhci->op_regs->command); command |= CMD_RUN; writel(command, &xhci->op_regs->command); xhci_handshake(xhci, &xhci->op_regs->status, STS_HALT, 0, 250 * 1000); xhci_resume_pending_ports(xhci); /* step 5: walk topology and initialize portsc, * portpmsc and portli */ /* this is done in bus_resume */ /* step 6: restart each of the previously * Running endpoints by ringing their doorbells */ spin_unlock_irq(&xhci->lock); done: if (retval == 0) { /* Resume root hubs only when have pending events. */ status = readl(&xhci->op_regs->status); if (status & STS_EINT) { usb_hcd_resume_root_hub(hcd); usb_hcd_resume_root_hub(xhci->shared_hcd); } } /* * If system is subject to the Quirk, Compliance Mode Timer needs to * be re-initialized Always after a system resume. Ports are subject * to suffer the Compliance Mode issue again. It doesn't matter if * ports have entered previously to U0 before system's suspension. */ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running) compliance_mode_recovery_timer_init(xhci); /* Re-enable port polling. */ xhci_dbg(xhci, "%s: starting port polling.\n", __func__); set_bit(HCD_FLAG_POLL_RH, &hcd->flags); usb_hcd_poll_rh_status(hcd); return retval;}在以上的函数中,当执行完下面写寄存器命令后,正常情况下,会触发一个中断函数。见下面。
command = readl(&xhci->op_regs->command); command |= CMD_RUN; writel(command, &xhci->op_regs->command);2。如果正常,则会触发以下中断函数。
irqreturn_t xhci_msi_irq(int irq, void *hcd){ return xhci_irq(hcd);}进而调用以下函数,进行处理;
irqreturn_t xhci_irq(struct usb_hcd *hcd){ struct xhci_hcd *xhci = hcd_to_xhci(hcd); u32 status; u64 temp_64; union xhci_trb *event_ring_deq; dma_addr_t deq; spin_lock(&xhci->lock); /* Check if the xHC generated the interrupt, or the irq is shared */ status = readl(&xhci->op_regs->status); if (status == 0xffffffff) goto hw_died; if (!(status & STS_EINT)) { spin_unlock(&xhci->lock); return IRQ_NONE; } if (status & STS_FATAL) { xhci_warn(xhci, "WARNING: Host System Error\n"); xhci_halt(xhci);hw_died: spin_unlock(&xhci->lock); return -ESHUTDOWN; } /* * Clear the op reg interrupt status first, * so we can receive interrupts from other MSI-X interrupters. * Write 1 to clear the interrupt status. */ status |= STS_EINT; writel(status, &xhci->op_regs->status); /* FIXME when MSI-X is supported and there are multiple vectors */ /* Clear the MSI-X event interrupt status */ if (hcd->irq) { u32 irq_pending; /* Acknowledge the PCI interrupt */ irq_pending = readl(&xhci->ir_set->irq_pending); irq_pending |= IMAN_IP; writel(irq_pending, &xhci->ir_set->irq_pending); } if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " "Shouldn't IRQs be disabled?\n"); /* Clear the event handler busy flag (RW1C); * the event ring should be empty. */ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); spin_unlock(&xhci->lock); return IRQ_HANDLED; } event_ring_deq = xhci->event_ring->dequeue; /* FIXME this should be a delayed service routine * that clears the EHB. */ while (xhci_handle_event(xhci) > 0) {} temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); /* If necessary, update the HW's version of the event ring deq ptr. */ if (event_ring_deq != xhci->event_ring->dequeue) { deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, xhci->event_ring->dequeue); if (deq == 0) xhci_warn(xhci, "WARN something wrong with SW event " "ring dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ temp_64 &= ERST_PTR_MASK; temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); } /* Clear the event handler busy flag (RW1C); event ring is empty. */ temp_64 |= ERST_EHB; xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); spin_unlock(&xhci->lock); return IRQ_HANDLED;}在以上函数中, while(xhci_handle_event(xhci) > 0) {}; 会处理各种信息;
static int xhci_handle_event(struct xhci_hcd *xhci){ union xhci_trb *event; int update_ptrs = 1; int ret; if (!xhci->event_ring || !xhci->event_ring->dequeue) { xhci->error_bitmask |= 1 << 1; return 0; } event = xhci->event_ring->dequeue; /* Does the HC or OS own the TRB? */ if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != xhci->event_ring->cycle_state) { xhci->error_bitmask |= 1 << 2; return 0; } /* * Barrier between reading the TRB_CYCLE (valid) flag above and any * speculative reads of the event's flags/data below. */ rmb(); /* FIXME: Handle more event types. */ switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) { case TRB_TYPE(TRB_COMPLETION): handle_cmd_completion(xhci, &event->event_cmd); break; case TRB_TYPE(TRB_PORT_STATUS): handle_port_status(xhci, event); update_ptrs = 0; break; case TRB_TYPE(TRB_TRANSFER): ret = handle_tx_event(xhci, &event->trans_event); if (ret < 0) xhci->error_bitmask |= 1 << 9; else update_ptrs = 0; break; case TRB_TYPE(TRB_DEV_NOTE): handle_device_notification(xhci, event); break; default: if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >= TRB_TYPE(48)) handle_vendor_event(xhci, event); else xhci->error_bitmask |= 1 << 3; } /* Any of the above functions may drop and re-acquire the lock, so check * to make sure a watchdog timer didn't mark the host as non-responsive. */ if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "xHCI host dying, returning from " "event handler.\n"); return 0; } if (update_ptrs) /* Update SW event ring dequeue pointer */ inc_deq(xhci, xhci->event_ring); /* Are there more items on the event ring? Caller will call us again to * check. */ return 1;}然后可以再根据log跟踪;以上信息仅供参考。
转载地址:http://gjngi.baihongyu.com/