work in progress of ochi + usb bus manager
[newos.git] / kernel / addons / modules / busses / usb / ohci / ohci.c
blobd5598a2fe96356977ef2329a68506e7e79dd3181
1 /*
2 ** Copyright 2003, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
5 #include <kernel/kernel.h>
6 #include <kernel/debug.h>
7 #include <kernel/heap.h>
8 #include <kernel/vm.h>
9 #include <kernel/thread.h>
10 #include <kernel/sem.h>
11 #include <kernel/module.h>
12 #include <kernel/int.h>
14 #include <string.h>
16 #include <kernel/bus/pci/pci.h>
17 #include <kernel/bus/usb/usb.h>
18 #include <kernel/bus/usb/usb_hc.h>
20 #include "ohci.h"
22 #define debug_level_flow 6
23 #define debug_level_error 10
24 #define debug_level_info 10
26 #define DEBUG_MSG_PREFIX "OHCI - "
28 #include <kernel/debug_ext.h>
30 #define phys_to_virt(oi, phys) (((addr_t)(phys) - (oi)->hcca_phys) + (addr_t)(oi)->hcca)
32 static ohci *oi_list = NULL;
34 static ohci_td *allocate_td(ohci *oi)
36 ohci_td *td;
38 // pull a td from the freelist
39 mutex_lock(&oi->td_freelist_lock);
40 td = oi->td_freelist;
41 if(td)
42 oi->td_freelist = td->next;
43 mutex_unlock(&oi->td_freelist_lock);
45 if(!td)
46 return NULL;
48 td->flags = 0;
49 td->curr_buf_ptr = 0;
50 td->next_td = 0;
51 td->buf_end = 0;
53 return td;
56 static void free_td(ohci *oi, ohci_td *td)
58 mutex_lock(&oi->td_freelist_lock);
59 td->next = oi->td_freelist;
60 oi->td_freelist = td;
61 mutex_unlock(&oi->td_freelist_lock);
64 static ohci_ed *create_ed(void)
66 ohci_ed *ed;
68 ed = kmalloc(sizeof(ohci_ed));
69 if(!ed)
70 return NULL;
72 memset(ed, 0, sizeof(ohci_ed));
74 vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)ed, &ed->phys_addr);
76 return ed;
79 static void enqueue_ed(ohci_ed *ed)
81 ohci_queue *queue = ed->queue;
82 ohci *oi = ed->oi;
84 mutex_lock(&oi->hc_list_lock);
86 // stick it in our queue
87 ed->prev_ed = NULL;
88 ed->next_ed = queue->head;
89 if(ed->next_ed != NULL)
90 ed->next_ed->prev_ed = ed;
91 queue->head = ed;
93 // put it in the hardware's queue
94 ed->next = *queue->head_phys;
95 *queue->head_phys = (uint32)ed->phys_addr;
97 mutex_unlock(&oi->hc_list_lock);
100 static ohci_ed *create_endpoint(ohci *oi, usb_endpoint_descriptor *endpoint, int address, int lowspeed)
102 ohci_ed *ed;
103 ohci_td *td;
105 ed = create_ed();
106 if(!ed)
107 return ed;
109 // save a pointer to the original usb endpoint structure
110 ed->usb_ed = endpoint;
111 ed->oi = oi;
113 // figure out what queue it should be in
114 switch(endpoint->attributes & 0x3) {
115 case USB_ENDPOINT_ATTR_CONTROL:
116 ed->queue = &oi->control_queue;
117 break;
118 case USB_ENDPOINT_ATTR_ISO:
119 // not supported
120 break;
121 case USB_ENDPOINT_ATTR_BULK:
122 ed->queue = &oi->bulk_queue;
123 break;
124 case USB_ENDPOINT_ATTR_INT:
125 ed->queue = &oi->interrupt_queue;
126 break;
129 td = allocate_td(oi);
131 // set some hardware flags based on the usb endpoint's data
132 ed->flags = (endpoint->max_packet_size << 16) | ((endpoint->endpoint_address & 0xf) << 7) | (address & 0x7f);
134 if((endpoint->attributes & 0x3) == USB_ENDPOINT_ATTR_CONTROL) {
135 ed->flags |= OHCI_ED_FLAGS_DIR_TD;
136 } else {
137 if(endpoint->endpoint_address & 0x80)
138 ed->flags |= OHCI_ED_FLAGS_DIR_IN;
139 else
140 ed->flags |= OHCI_ED_FLAGS_DIR_OUT;
142 if(lowspeed)
143 ed->flags |= OHCI_ED_FLAGS_SPEED;
145 // stick the null transfer descriptor in our endpoint descriptors list
146 ed->tail_td = td;
147 ed->tail = ed->head = td->phys_addr;
148 ed->prev_ed = ed->next_ed = NULL;
150 // enqueue this descriptor
151 enqueue_ed(ed);
153 return ed;
156 static int ohci_done_list_processor(void *_oi)
158 ohci *oi = (ohci *)_oi;
159 ohci_td *td;
160 ohci_td *done_list;
161 addr_t td_phys;
163 for(;;) {
164 sem_acquire(oi->done_list_sem, 1);
166 if(oi->hcca->done_head != 0)
167 SHOW_FLOW(6, "WDH 0x%x (0x%lx)", oi->hcca->done_head, phys_to_virt(oi, oi->hcca->done_head));
169 done_list = NULL;
171 // pull all of the entries from the done list, put them in another list in reverse order
172 td_phys = oi->hcca->done_head & 0xfffffff0;
173 while(td_phys != 0) {
174 td = (ohci_td *)phys_to_virt(oi, td_phys);
176 td_phys = td->next_td;
178 td->next = done_list;
179 done_list = td;
182 // ack the interrupt
183 oi->hcca->done_head = 0;
184 oi->regs->interrupt_status = INT_WDH;
186 td = done_list;
187 while(done_list) {
188 td = done_list;
189 bool transfer_complete = false;
191 SHOW_FLOW(6, "dealing with %p: CC 0x%x", td, OHCI_TD_FLAGS_CC(td->flags));
193 if(OHCI_TD_FLAGS_CC(td->flags) == OHCI_CC_NoError) {
194 if(td->last_in_transfer) {
195 SHOW_FLOW(6, "transfer %p now finished", td->usb_transfer);
197 td->usb_transfer->status = NO_ERROR;
198 transfer_complete = true;
200 } else {
201 // there was an error, the endpoint is halted
202 td->usb_transfer->status = ERR_GENERAL; // XXX make smarter
203 transfer_complete = true;
205 SHOW_FLOW(6, "stalled transfer, part of usb transfer %p", td->usb_transfer);
207 // walk the head pointer of this endpoint and remove the rest of this transfer's descriptors
208 // the next one after this one should be at the head of the list
209 td_phys = td->ed->head & 0xfffffff0;
210 while(td_phys != 0) {
211 ohci_td *temp_td = (ohci_td *)phys_to_virt(oi, td_phys);
213 SHOW_FLOW(6, "stalled transfer at %p (0x%lx), usb transfer %p", temp_td, td_phys, temp_td->usb_transfer);
215 if(temp_td->usb_transfer != td->usb_transfer)
216 break;
218 // this transfer descriptor is part of the errored transfer
219 td_phys = temp_td->next_td;
220 free_td(oi, temp_td);
222 SHOW_FLOW(6, "setting head to 0x%lx, tail is at 0x%x", td_phys, td->ed->tail);
223 td->ed->head = td_phys; // set the new head pointer
224 oi->regs->command_status = COMMAND_CLF | COMMAND_BLF;
227 if(transfer_complete) {
228 if(td->usb_transfer->callback != NULL)
229 td->usb_transfer->callback(td->usb_transfer);
230 if(td->usb_transfer->completion_sem >= 0)
231 sem_release(td->usb_transfer->completion_sem, 1);
234 done_list = td->next;
235 free_td(oi, td);
239 return 0;
242 static int ohci_interrupt(void *arg)
244 ohci *oi = (ohci *)arg;
245 uint32 int_stat = oi->regs->interrupt_status;
246 int ret = INT_NO_RESCHEDULE;
247 int i;
249 if((int_stat & oi->regs->interrupt_enable) == 0)
250 return INT_NO_RESCHEDULE;
252 SHOW_FLOW(7, "oi %p int 0x%x (%d) enabled 0x%x disabled 0x%x", oi, int_stat, oi->rh_ports, oi->regs->interrupt_enable, oi->regs->interrupt_disable);
254 if(int_stat & INT_SO) {
255 SHOW_FLOW0(8, "\tscheduler overrun");
258 if(int_stat & INT_WDH) {
259 SHOW_FLOW0(8, "\twriteback done head");
260 int_stat &= ~INT_WDH; // dont ack it here
261 sem_release_etc(oi->done_list_sem, 1, SEM_FLAG_NO_RESCHED);
262 ret = INT_RESCHEDULE;
265 if(int_stat & INT_SOF) {
266 SHOW_FLOW0(8, "\tstart of frame");
269 if(int_stat & INT_RD) {
270 SHOW_FLOW0(8, "\tresume detected");
273 if(int_stat & INT_UE) {
274 SHOW_FLOW0(8, "\tunrecoverable error");
277 if(int_stat & INT_FNO) {
278 SHOW_FLOW0(8, "\tframe number overrun");
281 if(int_stat & INT_RHSC) {
282 SHOW_FLOW0(8, "\troot status change");
283 SHOW_FLOW(8, "\trh_status 0x%x", oi->regs->rh_status);
284 for(i = 0; i < oi->rh_ports; i++)
285 SHOW_FLOW(8, "\t\trh_port_status %d: 0x%x", i, oi->regs->rh_port_status[i]);
289 if(int_stat & INT_OC) {
290 SHOW_FLOW0(8, "\townership change");
293 if(int_stat & INT_MIE) {
294 SHOW_FLOW0(8, "\tmaster interrupt enable");
297 oi->regs->interrupt_status = int_stat;
299 return ret;
302 static int ohci_create_endpoint(hc_cookie *cookie, hc_endpoint **hc_endpoint,
303 usb_endpoint_descriptor *usb_endpoint, int address, int lowspeed)
305 ohci *oi = (ohci *)cookie;
307 *hc_endpoint = create_endpoint(oi, usb_endpoint, address, lowspeed);
308 if(*hc_endpoint == NULL)
309 return ERR_NO_MEMORY;
311 return NO_ERROR;
314 static int ohci_destroy_endpoint(hc_cookie *cookie, hc_endpoint *hc_endpoint)
316 // XXX implement
318 return NO_ERROR;
321 static int ohci_enqueue_transfer(hc_cookie *cookie, hc_endpoint *endpoint, usb_hc_transfer *transfer)
323 ohci *oi = (ohci *)cookie;
324 ohci_ed *ed = (ohci_ed *)endpoint;
325 usb_endpoint_descriptor *usb_ed = ed->usb_ed;
327 switch(usb_ed->attributes & 0x3) {
328 case USB_ENDPOINT_ATTR_CONTROL: {
329 ohci_td *td0, *td1, *td2, *td3;
330 int dir;
332 // the direction of the transfer is based off of the first byte in the setup data
333 dir = *((char *)transfer->setup_buf) & 0x80 ? 1 : 0;
335 mutex_lock(&oi->hc_list_lock);
337 SHOW_FLOW(6, "head 0x%x, tail 0x%x", ed->head, ed->tail);
339 // allocate the transfer descriptors needed
340 td0 = ed->tail_td;
341 td2 = allocate_td(oi);
342 if(transfer->data_buf != NULL)
343 td1 = allocate_td(oi);
344 else
345 td1 = td2;
346 td3 = allocate_td(oi); // this will be the new null descriptor
348 // setup descriptor
349 td0->flags = OHCI_TD_FLAGS_CC_NOT | OHCI_TD_FLAGS_DIR_SETUP | OHCI_TD_FLAGS_IRQ_NONE | OHCI_TD_FLAGS_TOGGLE_0;
350 vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)transfer->setup_buf, (addr_t *)&td0->curr_buf_ptr);
351 td0->buf_end = td0->curr_buf_ptr + transfer->setup_len - 1;
352 td0->next_td = td1->phys_addr; // might be td2
353 td0->transfer_head = td0;
354 td0->transfer_next = td1;
355 td0->usb_transfer = transfer;
356 td0->ed = ed;
357 td0->last_in_transfer = false;
359 SHOW_FLOW(6, "td0 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",
360 td0, td0->phys_addr, td0->flags, td0->curr_buf_ptr, td0->buf_end, td0->next_td);
362 if(transfer->data_buf != NULL) {
363 // data descriptor
364 td1->flags = OHCI_TD_FLAGS_CC_NOT | OHCI_TD_FLAGS_TOGGLE_1| OHCI_TD_FLAGS_IRQ_NONE | OHCI_TD_FLAGS_ROUNDING |
365 dir ? OHCI_TD_FLAGS_DIR_IN : OHCI_TD_FLAGS_DIR_OUT;
366 vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)transfer->data_buf, (addr_t *)&td1->curr_buf_ptr);
367 td1->buf_end = td1->curr_buf_ptr + transfer->data_len - 1;
368 td1->next_td = td2->phys_addr;
369 td1->transfer_head = td0;
370 td1->transfer_next = td2;
371 td1->usb_transfer = transfer;
372 td1->ed = ed;
373 td1->last_in_transfer = false;
375 SHOW_FLOW(6, "td1 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",
376 td1, td1->phys_addr, td1->flags, td1->curr_buf_ptr, td1->buf_end, td1->next_td);
379 // ack descriptor
380 td2->flags = OHCI_TD_FLAGS_CC_NOT | OHCI_TD_FLAGS_TOGGLE_1 |
381 dir ? OHCI_TD_FLAGS_DIR_OUT : OHCI_TD_FLAGS_DIR_IN;
382 td2->curr_buf_ptr = 0;
383 td2->buf_end = 0;
384 td2->next_td = td3->phys_addr;
385 td2->transfer_head = td0;
386 td2->transfer_next = NULL;
387 td2->usb_transfer = transfer;
388 td2->ed = ed;
389 td2->last_in_transfer = true;
391 SHOW_FLOW(6, "td2 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",
392 td2, td2->phys_addr, td2->flags, td2->curr_buf_ptr, td2->buf_end, td2->next_td);
394 // next null descriptor is td3, it should be nulled out from allocate_td()
395 td3->ed = NULL;
396 td3->usb_transfer = NULL;
397 td3->transfer_head = td3->transfer_next = NULL;
399 SHOW_FLOW(6, "td3 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",
400 td3, td3->phys_addr, td3->flags, td3->curr_buf_ptr, td3->buf_end, td3->next_td);
402 ed->tail_td = td3;
403 ed->tail = td3->phys_addr;
405 oi->regs->command_status = COMMAND_CLF;
407 mutex_unlock(&oi->hc_list_lock);
408 break;
410 default:
411 SHOW_ERROR(0, "unsupported transfer type %d", ed->usb_ed->attributes & 0x3);
412 return ERR_UNIMPLEMENTED;
415 return NO_ERROR;
418 #if 1
419 static void ohci_test(ohci *oi)
421 static usb_endpoint_descriptor desc = {
422 sizeof(usb_endpoint_descriptor),
423 USB_DESCRIPTOR_ENDPOINT,
424 0, 0, 8, 0
426 static const usb_request GET_DESCRIPTOR = {
427 USB_REQTYPE_DEVICE_IN,
428 USB_REQUEST_GET_DESCRIPTOR,
429 USB_DESCRIPTOR_DEVICE << 8, 0, 8
431 static const usb_request SET_ADDR = {
432 USB_REQTYPE_DEVICE_OUT,
433 USB_REQUEST_SET_ADDRESS,
434 1, 0, 0
436 static uchar buf[512];
437 unsigned char *aligned_buf = (unsigned char *)((((addr_t)buf) & 0xffffffc0) + 0x40);
438 static usb_hc_transfer transfer;
439 ohci_ed *ed0, *ed1;
441 ed0 = create_endpoint(oi, &desc, 0, 0);
443 memcpy(aligned_buf, &GET_DESCRIPTOR, 8);
444 transfer.setup_buf = aligned_buf;
445 transfer.setup_len = 8;
446 transfer.data_buf = aligned_buf;
447 transfer.data_len = 8;
448 transfer.callback = NULL;
449 transfer.completion_sem = sem_create(0, "transaction sem");
451 ohci_enqueue_transfer(oi, ed0, &transfer);
453 sem_acquire_etc(transfer.completion_sem, 1, SEM_FLAG_TIMEOUT, 5000000, NULL);
455 SHOW_FLOW(5, "transaction completed with err 0x%x", transfer.status);
457 usb_device_descriptor *dev = (usb_device_descriptor *)aligned_buf;
459 SHOW_FLOW(5, "device: len %d class 0x%x subclass 0x%x protocol 0x%x",
460 dev->length, dev->device_class, dev->device_subclass, dev->device_protocol);
462 sem_delete(transfer.completion_sem);
464 memcpy(aligned_buf, &SET_ADDR, 8);
465 transfer.setup_buf = aligned_buf;
466 transfer.setup_len = 8;
467 transfer.data_buf = NULL;
468 transfer.data_len = 0;
469 transfer.callback = NULL;
470 transfer.completion_sem = sem_create(0, "transaction sem");
472 ohci_enqueue_transfer(oi, ed0, &transfer);
474 sem_acquire_etc(transfer.completion_sem, 1, SEM_FLAG_TIMEOUT, 5000000, NULL);
475 SHOW_FLOW(5, "transaction completed with err 0x%x", transfer.status);
476 sem_delete(transfer.completion_sem);
478 // new address
479 ed1 = create_endpoint(oi, &desc, 1, 0);
481 memcpy(aligned_buf, &GET_DESCRIPTOR, 8);
482 transfer.setup_buf = aligned_buf;
483 transfer.setup_len = 8;
484 transfer.data_buf = aligned_buf;
485 transfer.data_len = 8;
486 transfer.callback = NULL;
487 transfer.completion_sem = sem_create(0, "transaction sem");
489 ohci_enqueue_transfer(oi, ed1, &transfer);
491 sem_acquire_etc(transfer.completion_sem, 1, SEM_FLAG_TIMEOUT, 5000000, NULL);
493 SHOW_FLOW(5, "transaction completed with err 0x%x", transfer.status);
494 sem_delete(transfer.completion_sem);
497 #endif
499 static ohci *ohci_init_hc(pci_info *pinfo)
501 int i;
502 ohci *oi;
503 ohci_ed *null_ed;
504 ohci_td *null_td;
505 uint32 saved_interval;
506 uint32 largest_packet;
508 oi = kmalloc(sizeof(ohci));
509 if(!oi)
510 goto err;
512 SHOW_INFO(0, "allocated structure at %p", oi);
514 memset(oi, 0, sizeof(*oi));
516 // find the irq
517 oi->irq = pinfo->u.h0.interrupt_line;
519 SHOW_INFO(0, "irq %d", oi->irq);
521 // XXX remove
522 if(oi->irq == 10)
523 goto err;
525 // map the controller's registers
526 oi->reg_region = vm_map_physical_memory(vm_get_kernel_aspace_id(), "ohci regs", (void **)&oi->regs,
527 REGION_ADDR_ANY_ADDRESS, pinfo->u.h0.base_register_sizes[0], LOCK_RW|LOCK_KERNEL,
528 pinfo->u.h0.base_registers[0]);
529 if(oi->reg_region < 0) {
530 SHOW_ERROR0(0, "ohci_init: error creating register region");
531 goto err;
534 SHOW_INFO(0, "regs at 0x%lx, mapped to %p, size 0x%lx", (addr_t)pinfo->u.h0.base_registers[0],
535 oi->regs, pinfo->u.h0.base_register_sizes[0]);
537 // print and check the hardware rev
538 SHOW_INFO(0, "hardware rev %d.%d%s", (oi->regs->revision >> 4) & 0xf, oi->regs->revision & 0xf,
539 oi->regs->revision & 0x100 ? " legacy support" : "");
540 if(((oi->regs->revision >> 4) & 0xf) != 1 || (oi->regs->revision & 0xf) != 0) {
541 SHOW_ERROR0(0, "hardware rev not supported, bailing...");
542 goto err1;
545 // create a region for the hcca memory
546 oi->hcca_region = vm_create_anonymous_region(vm_get_kernel_aspace_id(), "ohci hcca", (void **)&oi->hcca,
547 REGION_ADDR_ANY_ADDRESS, OHCI_HCCA_SIZE, REGION_WIRING_WIRED_CONTIG, LOCK_RW|LOCK_KERNEL);
548 if(oi->hcca_region < 0) {
549 SHOW_ERROR0(0, "ohci_init: error creating hcca region");
550 goto err1;
552 vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)oi->hcca, &oi->hcca_phys);
554 SHOW_INFO(0, "hcca region at %p, physical address 0x%lx", oi->hcca, oi->hcca_phys);
556 // take the hardware back from SMM or anyone else that has had it before
557 if(oi->regs->control & CONTROL_IR) {
558 SHOW_INFO0(1, "SMM has control of the HC");
559 oi->regs->command_status = oi->regs->command_status | COMMAND_OCR;
561 // wait a total of 10ms for the host controller to come back to us
562 for(i = 0; i < 10; i++) {
563 if((oi->regs->control & CONTROL_IR) == 0) {
564 // it was released
565 break;
567 cpu_spin(1000); // 1ms
569 if(i >= 10) {
570 // SMM didn't release it
571 SHOW_ERROR0(0, "SMM will not release control of the HC");
572 goto err2;
576 // create a thread to process the done list for this hc
577 oi->done_list_sem = sem_create(0, "ohci done list sem");
578 oi->done_list_processor = thread_create_kernel_thread("ohci done list processor",
579 &ohci_done_list_processor, oi);
580 thread_set_priority(oi->done_list_processor, THREAD_RT_LOW_PRIORITY);
581 thread_resume_thread(oi->done_list_processor);
583 // set up some queues
584 oi->control_queue.head = NULL;
585 oi->control_queue.head_phys = &oi->regs->control_head_ed;
586 oi->bulk_queue.head = NULL;
587 oi->bulk_queue.head_phys = &oi->regs->bulk_head_ed;
588 oi->interrupt_queue.head = NULL;
589 oi->interrupt_queue.head_phys = &oi->hcca->interrupt_table[0];
590 mutex_init(&oi->hc_list_lock, "ohci list lock");
592 // create a pool of transfer descriptors out of the remainder of the hcca region
594 addr_t ptr;
595 ohci_td *td;
597 ptr = (addr_t)oi->hcca + sizeof(ohci_hcca);
598 oi->td_freelist = NULL;
600 mutex_init(&oi->td_freelist_lock, "ohci td freelist lock");
602 while(ptr < ((addr_t)oi->hcca + OHCI_HCCA_SIZE)) {
603 td = (ohci_td *)ptr;
604 td->phys_addr = oi->hcca_phys + ((addr_t)ptr - (addr_t)oi->hcca);
605 td->next = oi->td_freelist; // add it to the freelist
606 oi->td_freelist = td;
607 ptr += sizeof(ohci_td);
611 // allocate a null transfer descriptor
612 null_td = allocate_td(oi);
614 // create a null endpoint descriptor
615 null_ed = create_ed();
616 null_ed->flags = 0;
617 null_ed->head = null_ed->tail = null_td->phys_addr;
618 null_ed->next = 0;
619 null_ed->next_ed = NULL;
621 // fill in all of the interrupt transfer fields
622 for(i=0; i < 32; i++) {
623 oi->hcca->interrupt_table[i] = null_ed->phys_addr;
626 // install the interrupt handler
627 int_set_io_interrupt_handler(0x20 + oi->irq, &ohci_interrupt, oi);
629 // save the frame interval from the card
630 saved_interval = oi->regs->frame_interval & 0x3fff;
631 // calculate the largest packet size
632 largest_packet = ((saved_interval - 210) * 6) / 7;
633 SHOW_FLOW(1, "largest packet %d", largest_packet);
635 // reset the controller
636 oi->regs->command_status = COMMAND_HCR;
637 for(i = 0; i < 100; i++) {
638 if((oi->regs->control & COMMAND_HCR) == 0) {
639 // it reset
640 break;
642 cpu_spin(10); // 1us
644 if(i >= 100) {
645 SHOW_ERROR0(0, "failed to reset the HC");
646 goto err3;
649 // restore the frame interval register
650 oi->regs->frame_interval = (largest_packet << 16) | saved_interval | 0x80000000;
651 oi->regs->periodic_start = (saved_interval * 9) / 10; // 90% of frame interval
652 oi->regs->ls_threshold = 8*8*8*7/6*2;
653 SHOW_FLOW(1, "ls threshold %d", oi->regs->ls_threshold);
655 oi->hcca->done_head = 0;
656 oi->regs->HCCA = oi->hcca_phys;
657 oi->regs->bulk_head_ed = 0;
658 oi->regs->bulk_current_ed = 0;
659 oi->regs->control_head_ed = 0;
660 oi->regs->control_current_ed = 0;
661 oi->regs->interrupt_disable = 0xffffffff;
662 oi->regs->interrupt_status = 0xffffffff;
664 // start it up
665 oi->regs->control = CONTROL_HCFS_OPERATIONAL | CONTROL_CLE | CONTROL_BLE | CONTROL_PLE;
667 thread_snooze(5000);
669 // reset a couple of the root ports
670 // XXX move this into root hub code
671 oi->rh_ports = (oi->regs->rh_descriptor_a & 0xff);
672 SHOW_FLOW(1, "%d root ports", oi->rh_ports);
674 oi->regs->rh_descriptor_a |= 0x0100;
675 oi->regs->rh_descriptor_b |= 0x60000;
677 for(i=0; i < oi->rh_ports; i++)
678 oi->regs->rh_port_status[i] = 0x100; // set port power
679 thread_snooze(1000);
681 for(i=0; i < oi->rh_ports; i++)
682 oi->regs->rh_port_status[i] = 0x10; // reset port
683 thread_snooze(10000);
685 for(i=0; i < oi->rh_ports; i++)
686 oi->regs->rh_port_status[i] = 0x2; // enable port
687 thread_snooze(10000);
689 // enable all interrupts except Start Of Frame
690 oi->regs->interrupt_enable = INT_MIE | INT_OC | /* INT_RHSC | */ INT_FNO | INT_UE | INT_RD | INT_WDH | INT_SO;
692 // thread_snooze(1000000);
694 // ohci_test(oi);
696 return oi;
698 err3:
699 mutex_destroy(&oi->hc_list_lock);
700 mutex_destroy(&oi->td_freelist_lock);
701 err2:
702 vm_delete_region(vm_get_kernel_aspace_id(), oi->hcca_region);
703 err1:
704 vm_delete_region(vm_get_kernel_aspace_id(), oi->reg_region);
705 err:
706 if(oi)
707 kfree(oi);
708 return NULL;
711 static int ohci_init(int (*hc_init_callback)(void *callback_cookie, void *cookie), void *callback_cookie)
713 int i;
714 pci_module_hooks *pci;
715 pci_info pinfo;
716 ohci *oi;
717 int count = 0;
719 if(module_get(PCI_BUS_MODULE_NAME, 0, (void **)&pci) < 0) {
720 SHOW_ERROR0(0, "ohci_detect: no pci bus found..");
721 return -1;
724 for(i = 0; pci->get_nth_pci_info(i, &pinfo) >= NO_ERROR; i++) {
725 if(pinfo.class_base == OHCI_BASE_CLASS &&
726 pinfo.class_sub == OHCI_SUB_CLASS &&
727 pinfo.class_api == OHCI_INTERFACE) {
729 #if 0
731 int j;
733 for(j=0; j < 6; j++) {
734 dprintf(" %d: base 0x%x size 0x%x\n", j, pinfo.u.h0.base_registers[j], pinfo.u.h0.base_register_sizes[j]);
737 #endif
739 oi = ohci_init_hc(&pinfo);
740 if(oi) {
741 // add it to our list of ohci hcfs
742 oi->next = oi_list;
743 oi_list = oi;
744 count++;
746 // register it with the bus
747 hc_init_callback(callback_cookie, oi);
751 module_put(PCI_BUS_MODULE_NAME);
753 return count;
756 static int
757 ohci_uninit(void *cookie)
759 // XXX finish
760 return ERR_UNIMPLEMENTED;
763 static int
764 ohci_module_init(void)
766 return NO_ERROR;
769 static int
770 ohci_module_uninit(void)
772 return NO_ERROR;
775 static struct usb_hc_module_hooks ohci_hooks = {
776 &ohci_init, // bus initialization
777 &ohci_uninit,
778 &ohci_create_endpoint,
779 &ohci_destroy_endpoint,
780 &ohci_enqueue_transfer,
783 static module_header ohci_module_header = {
784 USB_HC_MODULE_NAME_PREFIX "/ohci/v1",
785 MODULE_CURR_VERSION,
786 0, // dont keep loaded
787 &ohci_hooks,
788 &ohci_module_init,
789 &ohci_module_uninit
792 module_header *modules[] = {
793 &ohci_module_header,
794 NULL