📄 ohci.c
字号:
printk("Bad port # passed to ohci_reset_port\n"); port = MAX_ROOT_PORTS-1; } writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); /* Reset */ /* * Get the time required for a root hub port to reset and wait * it out (adding 1ms for good measure). */ ms = (readl(&ohci->regs->roothub.a) >> 24) * 2 + 1; wait_ms(ms); /* check port status to see that the reset completed */ status = readl(&ohci->regs->roothub.portstatus[port]); if (status & PORT_PRS) { /* reset failed, try harder? */ printk("usb-ohci: port %d reset failed, retrying\n", port); writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); wait_ms(50); } /* TODO we might need to re-enable the port here or is that * done elsewhere? */} /* ohci_reset_port *//* * This gets called if the connect status on the root hub changes. */static void ohci_connect_change(struct ohci * ohci, int port){ struct usb_device *usb_dev; struct ohci_device *dev; /* memory I/O address of the port status register */ void *portaddr = &ohci->regs->roothub.portstatus[port]; int portstatus; /* * Because of the status change we have to forget * everything we think we know about the device * on this root hub port. It may have changed. */ usb_disconnect(ohci->root_hub->usb->children + port); portstatus = readl(portaddr); /* disable the port if nothing is connected */ if (!(portstatus & PORT_CCS)) { writel(PORT_CCS, portaddr); return; } /* * Allocate a device for the new thingy that's been attached */ usb_dev = ohci_usb_allocate(ohci->root_hub->usb); dev = usb_dev->hcpriv; dev->ohci = ohci; usb_connect(dev->usb); /* link it into the bus's device tree */ ohci->root_hub->usb->children[port] = usb_dev; wait_ms(200); /* wait for powerup; XXX is this needed? */ ohci_reset_port(ohci, port); /* Get information on speed by using LSD */ usb_dev->slow = readl(portaddr) & PORT_LSDA ? 1 : 0; /* * Do generic USB device tree processing on the new device. */ usb_new_device(usb_dev);} /* ohci_connect_change() *//* * This gets called when the root hub configuration * has changed. Just go through each port, seeing if * there is something interesting happening. */static void ohci_check_configuration(struct ohci *ohci){ int num = 0; int maxport = readl(&ohci->regs->roothub) & 0xff; do { if (readl(ohci->regs->roothub.portstatus[num]) & PORT_CSC) ohci_connect_change(ohci, num); } while (++num < maxport);} /* ohci_check_configuration() *//* * Get annoyed at the controller for bothering us. */static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r){ struct ohci *ohci = __ohci; struct ohci_regs *regs = ohci->regs; struct ohci_hcca *hcca = ohci->root_hub->hcca; __u32 donehead = hcca->donehead; /* * Check the interrupt status register if needed */ if (!donehead || (donehead & 1)) { __u32 intrstatus = readl(®s->intrstatus); /* * XXX eek! printk's in an interrupt handler. shoot me! */ if (intrstatus & OHCI_INTR_SO) { printk(KERN_DEBUG "usb-ohci: scheduling overrun\n"); } if (intrstatus & OHCI_INTR_RD) { printk(KERN_DEBUG "usb-ohci: resume detected\n"); } if (intrstatus & OHCI_INTR_UE) { printk(KERN_DEBUG "usb-ohci: unrecoverable error\n"); } if (intrstatus & OHCI_INTR_OC) { printk(KERN_DEBUG "usb-ohci: ownership change?\n"); } if (intrstatus & OHCI_INTR_RHSC) { /* TODO Process events on the root hub */ } } /* * Process the done list */ if (donehead &= ~0x1) { /* * TODO See which TD's completed.. */ } /* Re-enable done queue interrupts and reset the donehead */ hcca->donehead = 0; writel(OHCI_INTR_WDH, ®s->intrenable); } /* ohci_interrupt() *//* * Allocate the resources required for running an OHCI controller. * Host controller interrupts must not be running while calling this * function or the penguins will get angry. * * The mem_base parameter must be the usable -virtual- address of the * host controller's memory mapped I/O registers. */static struct ohci *alloc_ohci(void* mem_base){ int i; struct ohci *ohci; struct usb_bus *bus; struct ohci_device *dev; struct usb_device *usb; ohci = kmalloc(sizeof(*ohci), GFP_KERNEL); if (!ohci) return NULL; memset(ohci, 0, sizeof(*ohci)); ohci->irq = -1; ohci->regs = mem_base; INIT_LIST_HEAD(&ohci->interrupt_list); bus = kmalloc(sizeof(*bus), GFP_KERNEL); if (!bus) return NULL; memset(bus, 0, sizeof(*bus)); ohci->bus = bus; bus->hcpriv = ohci; bus->op = &ohci_device_operations; /* * Here we allocate our own root hub and TDs as well as the * OHCI host controller communications area. The HCCA is just * a nice pool of memory with pointers to endpoint descriptors * for the different interrupts. */ usb = ohci_usb_allocate(NULL); if (!usb) return NULL; dev = ohci->root_hub = usb_to_ohci(usb); usb->bus = bus; /* Initialize the root hub */ memset(dev, 0, sizeof(*dev)); dev->ohci = ohci; /* link back to the controller */ /* * Allocate the Host Controller Communications Area */ dev->hcca = (struct ohci_hcca *) kmalloc(sizeof(*dev->hcca), GFP_KERNEL); /* Tell the controller where the HCCA is */ writel(virt_to_bus(dev->hcca), &ohci->regs->hcca); /* Get the number of ports on the root hub */ usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff; if (usb->maxchild > MAX_ROOT_PORTS) { printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); usb->maxchild = MAX_ROOT_PORTS; } if (usb->maxchild < 1) { printk("usb-ohci: Less than one root hub port? Impossible!\n"); usb->maxchild = 1; } printk("usb-ohci: %d root hub ports found\n", usb->maxchild); printk("alloc_ohci() controller\n"); show_ohci_status(ohci); printk("alloc_ohci() root_hub device\n"); show_ohci_device(dev); /* * Initialize the ED polling "tree" (for simplicity's sake in * this driver many nodes in the tree will be identical) */ dev->ed[ED_INT_32].next_ed = virt_to_bus(&dev->ed[ED_INT_16]); dev->ed[ED_INT_16].next_ed = virt_to_bus(&dev->ed[ED_INT_8]); dev->ed[ED_INT_8].next_ed = virt_to_bus(&dev->ed[ED_INT_4]); dev->ed[ED_INT_4].next_ed = virt_to_bus(&dev->ed[ED_INT_2]); dev->ed[ED_INT_2].next_ed = virt_to_bus(&dev->ed[ED_INT_1]); /* * Initialize the polling table to call interrupts at the * intended intervals. */ for (i = 0; i < NUM_INTS; i++) { if (i == 0) dev->hcca->int_table[i] = virt_to_bus(&dev->ed[ED_INT_32]); else if (i & 1) dev->hcca->int_table[i] = virt_to_bus(&dev->ed[ED_INT_16]); else if (i & 2) dev->hcca->int_table[i] = virt_to_bus(&dev->ed[ED_INT_8]); else if (i & 4) dev->hcca->int_table[i] = virt_to_bus(&dev->ed[ED_INT_4]); else if (i & 8) dev->hcca->int_table[i] = virt_to_bus(&dev->ed[ED_INT_2]); else if (i & 16) dev->hcca->int_table[i] = virt_to_bus(&dev->ed[ED_INT_1]); } /* * Tell the controller where the control and bulk lists are */ writel(virt_to_bus(&dev->ed[ED_CONTROL]), &ohci->regs->ed_controlhead); writel(virt_to_bus(&dev->ed[ED_BULK]), &ohci->regs->ed_bulkhead); return ohci;} /* alloc_ohci() *//* * De-allocate all resoueces.. */static void release_ohci(struct ohci *ohci){ if (ohci->irq >= 0) { free_irq(ohci->irq, ohci); ohci->irq = -1; } if (ohci->root_hub) { /* ensure that HC is stopped before releasing the HCCA */ writel(OHCI_USB_SUSPEND, &ohci->regs->control); free_pages((unsigned int) ohci->root_hub->hcca, 1); free_pages((unsigned int) ohci->root_hub, 1); ohci->root_hub->hcca = NULL; ohci->root_hub = NULL; } /* unmap the IO address space */ iounmap(ohci->regs); /* If the ohci itself were dynamic we'd free it here */} /* release_ohci() *//* * USB OHCI control thread */static int ohci_control_thread(void * __ohci){ struct ohci *ohci = (struct ohci *)__ohci; /* * I'm unfamiliar with the SMP kernel locking.. where should * this be released? -greg */ lock_kernel(); /* * This thread doesn't need any user-level access, * so get rid of all of our resources.. */ printk("ohci_control_thread at %p\n", &ohci_control_thread); exit_mm(current); exit_files(current); exit_fs(current); strcpy(current->comm, "ohci-control"); /* * Damn the torpedoes, full speed ahead */ start_hc(ohci); do { interruptible_sleep_on(&ohci_configure);#ifdef CONFIG_APM if (apm_resume) { apm_resume = 0; start_hc(ohci); continue; }#endif ohci_check_configuration(ohci); } while (!signal_pending(current)); reset_hc(ohci); release_ohci(ohci); MOD_DEC_USE_COUNT; return 0;} /* ohci_control_thread() */#ifdef CONFIG_APMstatic int handle_apm_event(apm_event_t event){ static int down = 0; switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: if (down) { printk(KERN_DEBUG "usb-ohci: received extra suspend event\n"); break; } down = 1; break; case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: if (!down) { printk(KERN_DEBUG "usb-ohci: received bogus resume event\n"); break; } down = 0; if (waitqueue_active(&ohci_configure)) { apm_resume = 1; wake_up(&ohci_configure); } break; } return 0;}#endif/* ... *//* * Increment the module usage count, start the control thread and * return success if the controller is good. */static int found_ohci(int irq, void* mem_base){ int retval; struct ohci *ohci; /* Allocate the running OHCI structures */ ohci = alloc_ohci(mem_base); if (!ohci) return -ENOMEM; printk("usb-ohci: alloc_ohci() = %p\n", ohci); reset_hc(ohci); retval = -EBUSY; if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) { int pid; MOD_INC_USE_COUNT; ohci->irq = irq; /* fork off the handler */ pid = kernel_thread(ohci_control_thread, ohci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); if (pid >= 0) return 0; MOD_DEC_USE_COUNT; retval = pid; } release_ohci(ohci); return retval;} /* found_ohci() *//* * If this controller is for real, map the IO memory and proceed */static int init_ohci(struct pci_dev *dev){ unsigned int mem_base = dev->base_address[0]; printk("usb-ohci: mem_base is %p\n", (void*)mem_base); /* If its OHCI, its memory */ if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) return -ENODEV; /* Get the memory address and map it for IO */ mem_base &= PCI_BASE_ADDRESS_MEM_MASK; /* * FIXME ioremap_nocache isn't implemented on all CPUs (such * as the Alpha) [?] What should I use instead... * * The iounmap() is done on in release_ohci. */ mem_base = (unsigned int) ioremap_nocache(mem_base, 4096); if (!mem_base) { printk("Error mapping OHCI memory\n"); return -EFAULT; } return found_ohci(dev->irq, (void *) mem_base);} /* init_ohci() *//* TODO this should be named following Linux convention and go in pci.h */#define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010)/* * Search the PCI bus for an OHCI USB controller and set it up * * If anyone wants multiple controllers this will need to be * updated.. Right now, it just picks the first one it finds. */int init_module(void){ int retval; struct pci_dev *dev = NULL; /*u8 type;*/ if (sizeof(struct ohci_device) > 4096) { printk("usb-ohci: struct ohci_device to large\n"); return -ENODEV; } retval = -ENODEV; for (;;) { /* Find an OHCI USB controller */ dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev); if (!dev) break; /* Verify that its OpenHCI by checking for MMIO */ /* pci_read_config_byte(dev, PCI_CLASS_PROG, &type); if (!type) continue; */ /* Ok, set it up */ retval = init_ohci(dev); if (retval < 0) continue; /* TODO check module params here to determine what to load *//* usb_mouse_init(); *//* usb_kbd_init(); hub_init(); */#ifdef CONFIG_APM apm_register_callback(&handle_apm_event);#endif return 0; /* no error */ } return retval;} /* init_module() *//* * Clean up when unloading the module */void cleanup_module(void){#ifdef CONFIG_APM apm_unregister_callback(&handle_apm_event);#endif}/* vim:sw=8 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -