📄 ohci.c
字号:
td0, td0->phys_addr, td0->flags, td0->curr_buf_ptr, td0->buf_end, td0->next_td); // new tail td1->ed = NULL; td1->usb_transfer = NULL; td1->transfer_head = td1->transfer_next = NULL; SHOW_FLOW(6, "td1 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x", td1, td1->phys_addr, td1->flags, td1->curr_buf_ptr, td1->buf_end, td1->next_td); ed->tail_td = td1; ed->tail = td1->phys_addr; oi->regs->command_status = COMMAND_BLF; mutex_unlock(&oi->hc_list_lock); break; } default: SHOW_ERROR(0, "unsupported transfer type %d", ed->usb_ed->attributes & 0x3); return ERR_UNIMPLEMENTED; } return NO_ERROR;}#if 1static void ohci_test(ohci *oi){ static usb_endpoint_descriptor desc = { sizeof(usb_endpoint_descriptor), USB_DESCRIPTOR_ENDPOINT, 0, 0, 8, 0 }; static const usb_request GET_DESCRIPTOR = { USB_REQTYPE_DEVICE_IN, USB_REQUEST_GET_DESCRIPTOR, USB_DESCRIPTOR_DEVICE << 8, 0, 8 }; static const usb_request SET_ADDR = { USB_REQTYPE_DEVICE_OUT, USB_REQUEST_SET_ADDRESS, 1, 0, 0 }; static uchar buf[512]; unsigned char *aligned_buf = (unsigned char *)((((addr_t)buf) & 0xffffffc0) + 0x40); static usb_hc_transfer transfer; ohci_ed *ed0, *ed1; ed0 = create_endpoint(oi, &desc, 0, 0); memcpy(aligned_buf, &GET_DESCRIPTOR, 8); transfer.setup_buf = aligned_buf; transfer.setup_len = 8; transfer.data_buf = aligned_buf; transfer.data_len = 8; transfer.callback = NULL; transfer.completion_sem = sem_create(0, "transaction sem"); ohci_enqueue_transfer(oi, ed0, &transfer); sem_acquire_etc(transfer.completion_sem, 1, SEM_FLAG_TIMEOUT, 5000000, NULL); SHOW_FLOW(5, "transaction completed with err 0x%x", transfer.status); { usb_device_descriptor *dev = (usb_device_descriptor *)aligned_buf; SHOW_FLOW(5, "device: len %d class 0x%x subclass 0x%x protocol 0x%x", dev->length, dev->device_class, dev->device_subclass, dev->device_protocol); } sem_delete(transfer.completion_sem); memcpy(aligned_buf, &SET_ADDR, 8); transfer.setup_buf = aligned_buf; transfer.setup_len = 8; transfer.data_buf = NULL; transfer.data_len = 0; transfer.callback = NULL; transfer.completion_sem = sem_create(0, "transaction sem"); ohci_enqueue_transfer(oi, ed0, &transfer); sem_acquire_etc(transfer.completion_sem, 1, SEM_FLAG_TIMEOUT, 5000000, NULL); SHOW_FLOW(5, "transaction completed with err 0x%x", transfer.status); sem_delete(transfer.completion_sem); // new address ed1 = create_endpoint(oi, &desc, 1, 0); memcpy(aligned_buf, &GET_DESCRIPTOR, 8); transfer.setup_buf = aligned_buf; transfer.setup_len = 8; transfer.data_buf = aligned_buf; transfer.data_len = 8; transfer.callback = NULL; transfer.completion_sem = sem_create(0, "transaction sem"); ohci_enqueue_transfer(oi, ed1, &transfer); sem_acquire_etc(transfer.completion_sem, 1, SEM_FLAG_TIMEOUT, 5000000, NULL); SHOW_FLOW(5, "transaction completed with err 0x%x", transfer.status); sem_delete(transfer.completion_sem);}#endifstatic ohci *ohci_init_hc(pci_info *pinfo){ int i; ohci *oi; ohci_ed *null_ed; ohci_td *null_td; uint32 saved_interval; uint32 largest_packet; oi = kmalloc(sizeof(ohci)); if(!oi) goto err; SHOW_INFO(0, "allocated structure at %p", oi); memset(oi, 0, sizeof(*oi)); // find the irq oi->irq = pinfo->u.h0.interrupt_line; SHOW_INFO(0, "irq %d", oi->irq); // XXX remove if(oi->irq == 10) goto err; // map the controller's registers oi->reg_region = vm_map_physical_memory(vm_get_kernel_aspace_id(), "ohci regs", (void **)&oi->regs, REGION_ADDR_ANY_ADDRESS, pinfo->u.h0.base_register_sizes[0], LOCK_RW|LOCK_KERNEL, pinfo->u.h0.base_registers[0]); if(oi->reg_region < 0) { SHOW_ERROR0(0, "ohci_init: error creating register region"); goto err; } SHOW_INFO(0, "regs at 0x%lx, mapped to %p, size 0x%lx", (addr_t)pinfo->u.h0.base_registers[0], oi->regs, pinfo->u.h0.base_register_sizes[0]); // print and check the hardware rev SHOW_INFO(0, "hardware rev %d.%d%s", (oi->regs->revision >> 4) & 0xf, oi->regs->revision & 0xf, oi->regs->revision & 0x100 ? " legacy support" : ""); if(((oi->regs->revision >> 4) & 0xf) != 1 || (oi->regs->revision & 0xf) != 0) { SHOW_ERROR0(0, "hardware rev not supported, bailing..."); goto err1; } // create a region for the hcca memory oi->hcca_region = vm_create_anonymous_region(vm_get_kernel_aspace_id(), "ohci hcca", (void **)&oi->hcca, REGION_ADDR_ANY_ADDRESS, OHCI_HCCA_SIZE, REGION_WIRING_WIRED_CONTIG, LOCK_RW|LOCK_KERNEL); if(oi->hcca_region < 0) { SHOW_ERROR0(0, "ohci_init: error creating hcca region"); goto err1; } vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)oi->hcca, &oi->hcca_phys); SHOW_INFO(0, "hcca region at %p, physical address 0x%lx", oi->hcca, oi->hcca_phys); // take the hardware back from SMM or anyone else that has had it before if(oi->regs->control & CONTROL_IR) { SHOW_INFO0(1, "SMM has control of the HC"); oi->regs->command_status = oi->regs->command_status | COMMAND_OCR; // wait a total of 10ms for the host controller to come back to us for(i = 0; i < 10; i++) { if((oi->regs->control & CONTROL_IR) == 0) { // it was released break; } cpu_spin(1000); // 1ms } if(i >= 10) { // SMM didn't release it SHOW_ERROR0(0, "SMM will not release control of the HC"); goto err2; } } // create a thread to process the done list for this hc oi->done_list_sem = sem_create(0, "ohci done list sem"); oi->done_list_processor = thread_create_kernel_thread("ohci done list processor", &ohci_done_list_processor, oi); thread_set_priority(oi->done_list_processor, THREAD_RT_LOW_PRIORITY); thread_resume_thread(oi->done_list_processor); // set up some queues oi->control_queue.head = NULL; oi->control_queue.head_phys = &oi->regs->control_head_ed; oi->bulk_queue.head = NULL; oi->bulk_queue.head_phys = &oi->regs->bulk_head_ed; mutex_init(&oi->hc_list_lock, "ohci list lock"); // create a pool of transfer descriptors out of the remainder of the hcca region { addr_t ptr; ohci_td *td; ptr = (addr_t)oi->hcca + sizeof(ohci_hcca); oi->td_freelist = NULL; mutex_init(&oi->td_freelist_lock, "ohci td freelist lock"); while(ptr < ((addr_t)oi->hcca + OHCI_HCCA_SIZE)) { td = (ohci_td *)ptr; td->phys_addr = oi->hcca_phys + ((addr_t)ptr - (addr_t)oi->hcca); td->next = oi->td_freelist; // add it to the freelist oi->td_freelist = td; ptr += sizeof(ohci_td); } } // allocate a null endpoint and transfer descriptor for the 63 interrupt queues we'll have for(i = 0; i < 63; i++) { // allocate a null transfer descriptor null_td = allocate_td(oi); // create a null endpoint descriptor null_ed = create_ed(); null_ed->flags = 0; null_ed->head = null_ed->tail = null_td->phys_addr; null_ed->next = 0; null_ed->next_ed = NULL; oi->interrupt_queue[i].head = null_ed; oi->interrupt_queue[i].head_phys = &null_ed->next; } // set up the interrupt 'tree' shown on page 10 of the OHCI spec 1.0a for(i=0; i < 2; i++) { oi->interrupt_queue[INT_Q_2MS + i].head->next_ed = oi->interrupt_queue[INT_Q_1MS].head; oi->interrupt_queue[INT_Q_2MS + i].head->next = *oi->interrupt_queue[INT_Q_1MS].head_phys; } for(i=0; i < 4; i++) { oi->interrupt_queue[INT_Q_4MS + i].head->next_ed = oi->interrupt_queue[INT_Q_2MS + i/2].head; oi->interrupt_queue[INT_Q_4MS + i].head->next = *oi->interrupt_queue[INT_Q_2MS + i/2].head_phys; } for(i=0; i < 8; i++) { oi->interrupt_queue[INT_Q_8MS + i].head->next_ed = oi->interrupt_queue[INT_Q_4MS + i/4].head; oi->interrupt_queue[INT_Q_8MS + i].head->next = *oi->interrupt_queue[INT_Q_4MS + i/4].head_phys; } for(i=0; i < 16; i++) { oi->interrupt_queue[INT_Q_16MS + i].head->next_ed = oi->interrupt_queue[INT_Q_8MS + i/8].head; oi->interrupt_queue[INT_Q_16MS + i].head->next = *oi->interrupt_queue[INT_Q_8MS + i/8].head_phys; } for(i=0; i < 32; i++) { oi->interrupt_queue[INT_Q_32MS + i].head->next_ed = oi->interrupt_queue[INT_Q_16MS + i/16].head; oi->interrupt_queue[INT_Q_32MS + i].head->next = *oi->interrupt_queue[INT_Q_16MS + i/16].head_phys; oi->hcca->interrupt_table[i] = oi->interrupt_queue[INT_Q_32MS + i].head->phys_addr; } // install the interrupt handler int_set_io_interrupt_handler(oi->irq, &ohci_interrupt, oi, "ohci"); // save the frame interval from the card saved_interval = oi->regs->frame_interval & 0x3fff; // calculate the largest packet size largest_packet = ((saved_interval - 210) * 6) / 7; SHOW_FLOW(1, "largest packet %d", largest_packet); // reset the controller oi->regs->command_status = COMMAND_HCR; for(i = 0; i < 100; i++) { if((oi->regs->control & COMMAND_HCR) == 0) { // it reset break; } cpu_spin(10); // 1us } if(i >= 100) { SHOW_ERROR0(0, "failed to reset the HC"); goto err3; } // restore the frame interval register oi->regs->frame_interval = (largest_packet << 16) | saved_interval | 0x80000000; oi->regs->periodic_start = (saved_interval * 9) / 10; // 90% of frame interval oi->regs->ls_threshold = 8*8*8*7/6*2; SHOW_FLOW(1, "ls threshold %d", oi->regs->ls_threshold); oi->hcca->done_head = 0; oi->regs->HCCA = oi->hcca_phys; oi->regs->bulk_head_ed = 0; oi->regs->bulk_current_ed = 0; oi->regs->control_head_ed = 0; oi->regs->control_current_ed = 0; oi->regs->interrupt_disable = 0xffffffff; oi->regs->interrupt_status = 0xffffffff; // start it up oi->regs->control = CONTROL_HCFS_OPERATIONAL | CONTROL_CLE | CONTROL_BLE | CONTROL_PLE; thread_snooze(5000); // reset a couple of the root ports // XXX move this into root hub code oi->rh_ports = (oi->regs->rh_descriptor_a & 0xff); SHOW_FLOW(1, "%d root ports", oi->rh_ports); oi->regs->rh_descriptor_a |= 0x0100; oi->regs->rh_descriptor_b |= 0x60000; for(i=0; i < oi->rh_ports; i++) oi->regs->rh_port_status[i] = 0x100; // set port power thread_snooze(1000); for(i=0; i < oi->rh_ports; i++) oi->regs->rh_port_status[i] = 0x10; // reset port thread_snooze(10000); for(i=0; i < oi->rh_ports; i++) oi->regs->rh_port_status[i] = 0x2; // enable port thread_snooze(10000); // enable all interrupts except Start Of Frame oi->regs->interrupt_enable = INT_MIE | INT_OC | /* INT_RHSC | */ INT_FNO | INT_UE | INT_RD | INT_WDH | INT_SO;// thread_snooze(1000000);// ohci_test(oi); return oi;err3: mutex_destroy(&oi->hc_list_lock); mutex_destroy(&oi->td_freelist_lock);err2: vm_delete_region(vm_get_kernel_aspace_id(), oi->hcca_region);err1: vm_delete_region(vm_get_kernel_aspace_id(), oi->reg_region);err: if(oi) kfree(oi); return NULL;}static int ohci_init(int (*hc_init_callback)(void *callback_cookie, void *cookie), void *callback_cookie){ int i; pci_module_hooks *pci; pci_info pinfo; ohci *oi; int count = 0; if(module_get(PCI_BUS_MODULE_NAME, 0, (void **)&pci) < 0) { SHOW_ERROR0(0, "ohci_detect: no pci bus found.."); return -1; } for(i = 0; pci->get_nth_pci_info(i, &pinfo) >= NO_ERROR; i++) { if(pinfo.class_base == OHCI_BASE_CLASS && pinfo.class_sub == OHCI_SUB_CLASS && pinfo.class_api == OHCI_INTERFACE) {#if 0 { int j; for(j=0; j < 6; j++) { dprintf(" %d: base 0x%x size 0x%x\n", j, pinfo.u.h0.base_registers[j], pinfo.u.h0.base_register_sizes[j]); } }#endif oi = ohci_init_hc(&pinfo); if(oi) { // add it to our list of ohci hcfs oi->next = oi_list; oi_list = oi; count++; // register it with the bus hc_init_callback(callback_cookie, oi); } } } module_put(PCI_BUS_MODULE_NAME); return count;}static intohci_uninit(void *cookie){ // XXX finish return ERR_UNIMPLEMENTED;}static intohci_module_init(void){ return NO_ERROR;}static intohci_module_uninit(void){ return NO_ERROR;}static struct usb_hc_module_hooks ohci_hooks = { &ohci_init, // bus initialization &ohci_uninit, &ohci_create_endpoint, &ohci_destroy_endpoint, &ohci_enqueue_transfer,};static module_header ohci_module_header = { USB_HC_MODULE_NAME_PREFIX "/ohci/v1", MODULE_CURR_VERSION, 0, // dont keep loaded &ohci_hooks, &ohci_module_init, &ohci_module_uninit};module_header *modules[] = { &ohci_module_header, NULL};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -