📄 usb-ohci.c
字号:
default: status = TD_CC_STALL; } break; case RH_GET_DESCRIPTOR | RH_CLASS: *(__u8 *) (data_buf+1) = 0x29; *(__u32 *) (data_buf+2) = cpu_to_le32 (readl (&ohci->regs->roothub.a)); *(__u8 *) data_buf = (*(__u8 *) (data_buf + 2) / 8) * 2 + 9; /* length of descriptor */ len = min (leni, min(*(__u8 *) data_buf, wLength)); *(__u8 *) (data_buf+6) = 0; /* Root Hub needs no current from bus */ if (*(__u8 *) (data_buf+2) < 8) { /* less than 8 Ports */ *(__u8 *) (data_buf+7) = readl (&ohci->regs->roothub.b) & 0xff; *(__u8 *) (data_buf+8) = (readl (&ohci->regs->roothub.b) & 0xff0000) >> 16; } else { *(__u32 *) (data_buf+7) = cpu_to_le32 (readl(&ohci->regs->roothub.b)); } OK (len); case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); default: status = TD_CC_STALL; } dbg("USB HC roothubstat1: %x", readl ( &(ohci->regs->roothub.portstatus[0]) )); dbg("USB HC roothubstat2: %x", readl ( &(ohci->regs->roothub.portstatus[1]) )); len = min(len, leni); 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 if (urb->complete) urb->complete (urb); return 0;}/*-------------------------------------------------------------------------*/static int rh_unlink_urb (urb_t * urb){ ohci_t * ohci = urb->dev->bus->hcpriv; ohci->rh.send = 0; del_timer (&ohci->rh.rh_int_timer); return 0;} /*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*//* reset the HC not the BUS */static void hc_reset (ohci_t * ohci){ int timeout = 30; int smm_timeout = 50; /* 0,5 sec */ if (readl (&ohci->regs->control) & 0x100) { /* SMM owns the HC */ writel (0x08, &ohci->regs->cmdstatus); /* request ownership */ dbg("USB HC TakeOver from SMM"); while (readl (&ohci->regs->control) & 0x100) { wait_ms (10); if (--smm_timeout == 0) { err("USB HC TakeOver failed!"); break; } } } writel ((1 << 31), &ohci->regs->intrdisable); /* Disable HC interrupts */ dbg("USB HC reset_hc: %x ;", readl (&ohci->regs->control)); /* this seems to be needed for the lucent controller on powerbooks.. */ writel (0, &ohci->regs->control); /* Move USB to reset state */ writel (1, &ohci->regs->cmdstatus); /* HC Reset */ while ((readl (&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */ if (--timeout == 0) { err("USB HC reset timed out!"); return; } udelay (1); } }/*-------------------------------------------------------------------------*//* Start an OHCI controller, set the BUS operational * enable interrupts * connect the virtual root hub */static int hc_start (ohci_t * ohci){ unsigned int mask; unsigned int fminterval; struct usb_device * usb_dev; struct ohci_device * dev; /* 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); /* Choose the interrupts we care about now, others later on demand */ mask = OHCI_INTR_MIE | OHCI_INTR_WDH | OHCI_INTR_SO; writel (ohci->hc_control = 0xBF, &ohci->regs->control); /* USB Operational */ writel (mask, &ohci->regs->intrenable); writel (mask, &ohci->regs->intrstatus);#ifdef OHCI_USE_NPS writel ((readl(&ohci->regs->roothub.a) | 0x200) & ~0x100, &ohci->regs->roothub.a); writel (0x10000, &ohci->regs->roothub.status); mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe);#endif /* OHCI_USE_NPS */ /* connect the virtual root hub */ usb_dev = usb_alloc_dev (NULL, ohci->bus); if (!usb_dev) return -1; 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); return -1; } 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_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); }/*-------------------------------------------------------------------------*//* allocate OHCI */static ohci_t * hc_alloc_ohci (void * mem_base){ int i; ohci_t * ohci; struct usb_bus * bus; ohci = (ohci_t *) __get_free_pages (GFP_KERNEL, 1); if (!ohci) return NULL; memset (ohci, 0, sizeof (ohci_t)); ohci->irq = -1; ohci->regs = mem_base; /* for load ballancing of 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; /* end of control and bulk lists */ ohci->ed_isotail = NULL; ohci->ed_controltail = NULL; ohci->ed_bulktail = NULL; bus = usb_alloc_bus (&sohci_device_operations); if (!bus) { free_pages ((unsigned long) ohci, 1); 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"); /* disconnect all devices */ if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub); hc_reset (ohci); writel (OHCI_USB_RESET, &ohci->regs->control); wait_ms (10); if (ohci->irq >= 0) { free_irq (ohci->irq, ohci); ohci->irq = -1; } usb_deregister_bus (ohci->bus); usb_free_bus (ohci->bus); /* unmap the IO address space */ iounmap (ohci->regs); free_pages ((unsigned long) ohci, 1); }/*-------------------------------------------------------------------------*//* Increment the module usage count, start the control thread and * return success. */ static int hc_found_ohci (int irq, void * mem_base){ ohci_t * ohci; dbg("USB HC found: irq= %d membase= %lx", irq, (unsigned long) mem_base); ohci = hc_alloc_ohci (mem_base); if (!ohci) { return -ENOMEM; } INIT_LIST_HEAD (&ohci->ohci_hcd_list); list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); hc_reset (ohci); 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-usb", ohci) == 0) { ohci->irq = irq; hc_start (ohci); return 0; } err("request interrupt %d failed", irq); hc_release_ohci (ohci); return -EBUSY;}/*-------------------------------------------------------------------------*/ static int hc_start_ohci (struct pci_dev * dev){#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) unsigned long mem_base = dev->resource[0].start;#else unsigned long mem_base = dev->base_address[0]; if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) return -ENODEV; mem_base &= PCI_BASE_ADDRESS_MEM_MASK;#endif pci_set_master (dev); mem_base = (unsigned long) ioremap_nocache (mem_base, 4096); if (!mem_base) { err("Error mapping OHCI memory"); return -EFAULT; } return hc_found_ohci (dev->irq, (void *) mem_base);} /*-------------------------------------------------------------------------*/#ifdef CONFIG_PMAC_PBOOK/* On Powerbooks, put the controller into suspend mode when going * to sleep, and do a resume when waking up. */static int ohci_sleep_notify (struct pmu_sleep_notifier * self, int when){ struct list_head * ohci_l; ohci_t * ohci; for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list); switch (when) { case PBOOK_SLEEP_NOW: disable_irq (ohci->irq); writel (ohci->hc_control = OHCI_USB_SUSPEND, &ohci->regs->control); wait_ms (10); break; case PBOOK_WAKE: writel (ohci->hc_control = OHCI_USB_RESUME, &ohci->regs->control); wait_ms (20); writel (ohci->hc_control = 0xBF, &ohci->regs->control); enable_irq (ohci->irq); break; } } return PBOOK_SLEEP_OK;}static struct pmu_sleep_notifier ohci_sleep_notifier = { ohci_sleep_notify, SLEEP_LEVEL_MISC,};#endif /* CONFIG_PMAC_PBOOK *//*-------------------------------------------------------------------------*/ #ifdef CONFIG_APMstatic int handle_apm_event (apm_event_t event) { static int down = 0; ohci_t * ohci; struct list_head * ohci_l; switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: if (down) { dbg("received extra suspend event"); break; } for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list); dbg("USB-Bus suspend: %p", ohci); writel (ohci->hc_control = 0xFF, &ohci->regs->control); } wait_ms (10); down = 1; break; case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: if (!down) { dbg("received bogus resume event"); break; } for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { ohci = list_entry(ohci_l, ohci_t, ohci_hcd_list); dbg("USB-Bus resume: %p", ohci); writel (ohci->hc_control = 0x7F, &ohci->regs->control); } wait_ms (20); for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list); writel (ohci->hc_control = 0xBF, &ohci->regs->control); } down = 0; break; } return 0;}#endif/*-------------------------------------------------------------------------*/#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310 int ohci_hcd_init (void) { int ret = -ENODEV; struct pci_dev * dev = NULL; while ((dev = pci_find_class (PCI_CLASS_SERIAL_USB_OHCI, dev))) { if (hc_start_ohci(dev) >= 0) ret = 0; } #ifdef CONFIG_APM apm_register_callback (&handle_apm_event);#endif#ifdef CONFIG_PMAC_PBOOK pmu_register_sleep_notifier (&ohci_sleep_notifier);#endif return ret;}/*-------------------------------------------------------------------------*/#ifdef MODULEint init_module (void) { return ohci_hcd_init ();}/*-------------------------------------------------------------------------*/void cleanup_module (void) { ohci_t * ohci; #ifdef CONFIG_APM apm_unregister_callback (&handle_apm_event);#endif#ifdef CONFIG_PMAC_PBOOK pmu_unregister_sleep_notifier (&ohci_sleep_notifier);#endif while (!list_empty (&ohci_hcd_list)) { ohci = list_entry (ohci_hcd_list.next, ohci_t, ohci_hcd_list); list_del (&ohci->ohci_hcd_list); INIT_LIST_HEAD (&ohci->ohci_hcd_list); hc_release_ohci (ohci); } }#endif //MODULE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -