📄 usb-ohci.c
字号:
case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); default: dbg ("unsupported root hub command"); status = TD_CC_STALL; } #ifdef DEBUG // ohci_dump_roothub (ohci, 0);#endif len = min(len, leni); if (data != data_buf) memcpy (data, data_buf, len); urb->actual_length = len; urb->status = cc_to_error [status]; #ifdef DEBUG urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));#endif urb->hcpriv = NULL; usb_dec_dev_use (usb_dev); urb->dev = NULL; if (urb->complete) urb->complete (urb); return 0;}/*-------------------------------------------------------------------------*/static int rh_unlink_urb (urb_t * urb){ ohci_t * ohci = urb->dev->bus->hcpriv; if (ohci->rh.urb == urb) { ohci->rh.send = 0; del_timer (&ohci->rh.rh_int_timer); ohci->rh.urb = NULL; urb->hcpriv = NULL; usb_dec_dev_use(urb->dev); urb->dev = NULL; if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; if (urb->complete) urb->complete (urb); } else urb->status = -ENOENT; } return 0;} /*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*//* reset the HC and BUS */static int hc_reset (ohci_t * ohci){ int timeout = 30; int smm_timeout = 50; /* 0,5 sec */ if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ dbg("USB HC TakeOver from SMM"); while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { wait_ms (10); if (--smm_timeout == 0) { err("USB HC TakeOver failed!"); return -1; } } } /* Disable HC interrupts */ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;", ohci->ohci_dev->slot_name, readl (&ohci->regs->control)); /* Reset USB (needed by some controllers) */ writel (0, &ohci->regs->control); /* HC Reset requires max 10 ms delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { if (--timeout == 0) { err("USB HC reset timed out!"); return -1; } udelay (1); } return 0;}/*-------------------------------------------------------------------------*//* Start an OHCI controller, set the BUS operational * enable interrupts * connect the virtual root hub */static int hc_start (ohci_t * ohci){ __u32 mask; unsigned int fminterval; struct usb_device * usb_dev; struct ohci_device * dev; ohci->disabled = 1; /* Tell the controller where the control and bulk lists are * The lists are empty now. */ writel (0, &ohci->regs->ed_controlhead); writel (0, &ohci->regs->ed_bulkhead); writel (virt_to_bus (&ohci->hcca), &ohci->regs->hcca); /* a reset clears this */ fminterval = 0x2edf; writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); fminterval |= ((((fminterval - 210) * 6) / 7) << 16); writel (fminterval, &ohci->regs->fminterval); writel (0x628, &ohci->regs->lsthresh); /* start controller operations */ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; ohci->disabled = 0; writel (ohci->hc_control, &ohci->regs->control); /* Choose the interrupts we care about now, others later on demand */ mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; writel (mask, &ohci->regs->intrenable); writel (mask, &ohci->regs->intrstatus);#ifdef OHCI_USE_NPS writel ((readl(&ohci->regs->roothub.a) | RH_A_NPS) & ~RH_A_PSM, &ohci->regs->roothub.a); writel (RH_HS_LPSC, &ohci->regs->roothub.status);#endif /* OHCI_USE_NPS */ // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe); /* connect the virtual root hub */ ohci->rh.devnum = 0; usb_dev = usb_alloc_dev (NULL, ohci->bus); if (!usb_dev) { ohci->disabled = 1; return -ENOMEM; } dev = usb_to_ohci (usb_dev); ohci->bus->root_hub = usb_dev; usb_connect (usb_dev); if (usb_new_device (usb_dev) != 0) { usb_free_dev (usb_dev); ohci->disabled = 1; return -ENODEV; } return 0;}/*-------------------------------------------------------------------------*//* an interrupt happens */static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r){ ohci_t * ohci = __ohci; struct ohci_regs * regs = ohci->regs; int ints; if ((ohci->hcca.done_head != 0) && !(le32_to_cpup (&ohci->hcca.done_head) & 0x01)) { ints = OHCI_INTR_WDH; } else if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable))) == 0) { return; } // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no)); if (ints & OHCI_INTR_UE) { ohci->disabled++; err ("OHCI Unrecoverable Error, controller usb-%s disabled", ohci->ohci_dev->slot_name); // e.g. due to PCI Master/Target Abort#ifdef DEBUG ohci_dump (ohci, 1);#else // FIXME: be optimistic, hope that bug won't repeat often. // Make some non-interrupt context restart the controller. // Count and limit the retries though; either hardware or // software errors can go forever...#endif } if (ints & OHCI_INTR_WDH) { writel (OHCI_INTR_WDH, ®s->intrdisable); dl_done_list (ohci, dl_reverse_done_list (ohci)); writel (OHCI_INTR_WDH, ®s->intrenable); } if (ints & OHCI_INTR_SO) { dbg("USB Schedule overrun"); writel (OHCI_INTR_SO, ®s->intrenable); } if (ints & OHCI_INTR_SF) { unsigned int frame = le16_to_cpu (ohci->hcca.frame_no) & 1; writel (OHCI_INTR_SF, ®s->intrdisable); if (ohci->ed_rm_list[!frame] != NULL) { dl_del_list (ohci, !frame); } if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, ®s->intrenable); } writel (ints, ®s->intrstatus); writel (OHCI_INTR_MIE, ®s->intrenable); /* FIXME: check URB timeouts */}/*-------------------------------------------------------------------------*//* allocate OHCI */static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base){ ohci_t * ohci; struct usb_bus * bus; ohci = (ohci_t *) kmalloc (sizeof *ohci, GFP_KERNEL); if (!ohci) return NULL; memset (ohci, 0, sizeof (ohci_t)); ohci->disabled = 1; ohci->irq = -1; ohci->regs = mem_base; ohci->ohci_dev = dev; dev->driver_data = ohci; INIT_LIST_HEAD (&ohci->ohci_hcd_list); list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); bus = usb_alloc_bus (&sohci_device_operations); if (!bus) { kfree (ohci); return NULL; } ohci->bus = bus; bus->hcpriv = (void *) ohci; return ohci;} /*-------------------------------------------------------------------------*//* De-allocate all resources.. */static void hc_release_ohci (ohci_t * ohci){ dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name); /* disconnect all devices */ if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub); if (!ohci->disabled) hc_reset (ohci); if (ohci->irq >= 0) { free_irq (ohci->irq, ohci); ohci->irq = -1; } ohci->ohci_dev->driver_data = 0; usb_deregister_bus (ohci->bus); usb_free_bus (ohci->bus); list_del (&ohci->ohci_hcd_list); INIT_LIST_HEAD (&ohci->ohci_hcd_list); /* unmap the IO address space */ iounmap (ohci->regs); kfree (ohci);}/*-------------------------------------------------------------------------*//* Increment the module usage count, start the control thread and * return success. */static struct pci_driver ohci_pci_driver; static int __devinithc_found_ohci (struct pci_dev *dev, int irq, void * mem_base){ ohci_t * ohci; u8 latency, limit; char buf[8], *bufp = buf;#ifndef __sparc__ sprintf(buf, "%d", irq);#else bufp = __irq_itoa(irq);#endif printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n", (unsigned long) mem_base, bufp); printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); ohci = hc_alloc_ohci (dev, mem_base); if (!ohci) { return -ENOMEM; } /* bad pci latencies can contribute to overruns */ pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); if (latency) { pci_read_config_byte (dev, PCI_MAX_LAT, &limit); if (limit && limit < latency) { dbg ("PCI latency reduced to max %d", limit); pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit); ohci->pci_latency = limit; } else { /* it might already have been reduced */ ohci->pci_latency = latency; } } if (hc_reset (ohci) < 0) { hc_release_ohci (ohci); return -ENODEV; } /* FIXME this is a second HC reset; why?? */ writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); wait_ms (10); usb_register_bus (ohci->bus); if (request_irq (irq, hc_interrupt, SA_SHIRQ, ohci_pci_driver.name, ohci) != 0) { err ("request interrupt %s failed", bufp); hc_release_ohci (ohci); return -EBUSY; } ohci->irq = irq; if (hc_start (ohci) < 0) { err ("can't start usb-%s", dev->slot_name); hc_release_ohci (ohci); return -EBUSY; }#ifdef DEBUG ohci_dump (ohci, 1);#endif return 0;}/*-------------------------------------------------------------------------*/#ifdef CONFIG_PM/* controller died; cleanup debris, then restart *//* must not be called from interrupt context */static void hc_restart (ohci_t *ohci){ int temp; int i; if (ohci->pci_latency) pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); ohci->disabled = 1; if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub); /* empty the interrupt branches */ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->hcca.int_table[i] = 0; /* no EDs to remove */ ohci->ed_rm_list [0] = NULL; ohci->ed_rm_list [1] = NULL; /* empty control and bulk lists */ ohci->ed_isotail = NULL; ohci->ed_controltail = NULL; ohci->ed_bulktail = NULL; if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp); } else dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);}#endif /* CONFIG_PM *//*-------------------------------------------------------------------------*//* configured so that an OHCI device is always provided *//* always called with process context; sleeping is OK */static int __devinitohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id){ unsigned long mem_resource, mem_len; void *mem_base; if (pci_enable_device(dev) < 0) return -ENODEV; /* we read its hardware registers as memory */ mem_resource = pci_resource_start(dev, 0); mem_len = pci_resource_len(dev, 0); if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) { dbg ("controller already in use"); return -EBUSY; } mem_base = ioremap_nocache (mem_resource, mem_len); if (!mem_base) { err("Error mapping OHCI memory"); return -EFAULT; } /* controller writes into our memory */ pci_set_master (dev); return hc_found_ohci (dev, dev->irq, mem_base);} /*-------------------------------------------------------------------------*//* may be called from interrupt context [interface spec] *//* may be called without controller present *//* may be called with controller, bus, and devices active */static void __devexitohci_pci_remove (struct pci_dev *dev){ ohci_t *ohci = (ohci_t *) dev->driver_data; dbg ("remove %s controller usb-%s%s%s", hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), dev->slot_name, ohci->disabled ? " (disabled)" : "", in_interrupt () ? " in interrupt" : "" );#ifdef DEBUG ohci_dump (ohci, 1);#endif /* don't wake up sleeping controllers, or block in interrupt context */ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER || in_interrupt ()) { dbg ("controller being disabled"); ohci->disabled = 1; } /* on return, USB will always be reset (if present) */ if (ohci->disabled) writel (ohci->hc_control = OHCI_USB_RES
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -