2 ** Copyright 2003, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
5 #include <kernel/kernel.h>
6 #include <kernel/debug.h>
7 #include <kernel/heap.h>
9 #include <kernel/thread.h>
10 #include <kernel/sem.h>
11 #include <kernel/module.h>
12 #include <kernel/int.h>
16 #include <kernel/bus/pci/pci.h>
17 #include <kernel/bus/usb/usb.h>
18 #include <kernel/bus/usb/usb_hc.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
)
38 // pull a td from the freelist
39 mutex_lock(&oi
->td_freelist_lock
);
42 oi
->td_freelist
= td
->next
;
43 mutex_unlock(&oi
->td_freelist_lock
);
56 static void free_td(ohci
*oi
, ohci_td
*td
)
58 mutex_lock(&oi
->td_freelist_lock
);
59 td
->next
= oi
->td_freelist
;
61 mutex_unlock(&oi
->td_freelist_lock
);
64 static ohci_ed
*create_ed(void)
68 ed
= kmalloc(sizeof(ohci_ed
));
72 memset(ed
, 0, sizeof(ohci_ed
));
74 vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t
)ed
, &ed
->phys_addr
);
79 static void enqueue_ed(ohci_ed
*ed
)
81 ohci_queue
*queue
= ed
->queue
;
84 mutex_lock(&oi
->hc_list_lock
);
86 // stick it in our queue
88 ed
->next_ed
= queue
->head
;
89 if(ed
->next_ed
!= NULL
)
90 ed
->next_ed
->prev_ed
= 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
)
109 // save a pointer to the original usb endpoint structure
110 ed
->usb_ed
= endpoint
;
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
;
118 case USB_ENDPOINT_ATTR_ISO
:
121 case USB_ENDPOINT_ATTR_BULK
:
122 ed
->queue
= &oi
->bulk_queue
;
124 case USB_ENDPOINT_ATTR_INT
:
125 ed
->queue
= &oi
->interrupt_queue
;
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
;
137 if(endpoint
->endpoint_address
& 0x80)
138 ed
->flags
|= OHCI_ED_FLAGS_DIR_IN
;
140 ed
->flags
|= OHCI_ED_FLAGS_DIR_OUT
;
143 ed
->flags
|= OHCI_ED_FLAGS_SPEED
;
145 // stick the null transfer descriptor in our endpoint descriptors list
147 ed
->tail
= ed
->head
= td
->phys_addr
;
148 ed
->prev_ed
= ed
->next_ed
= NULL
;
150 // enqueue this descriptor
156 static int ohci_done_list_processor(void *_oi
)
158 ohci
*oi
= (ohci
*)_oi
;
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
));
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
;
183 oi
->hcca
->done_head
= 0;
184 oi
->regs
->interrupt_status
= INT_WDH
;
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;
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
)
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
;
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
;
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
;
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
;
314 static int ohci_destroy_endpoint(hc_cookie
*cookie
, hc_endpoint
*hc_endpoint
)
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
;
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
341 td2
= allocate_td(oi
);
342 if(transfer
->data_buf
!= NULL
)
343 td1
= allocate_td(oi
);
346 td3
= allocate_td(oi
); // this will be the new null 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
;
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
) {
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
;
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
);
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;
384 td2
->next_td
= td3
->phys_addr
;
385 td2
->transfer_head
= td0
;
386 td2
->transfer_next
= NULL
;
387 td2
->usb_transfer
= transfer
;
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()
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
);
403 ed
->tail
= td3
->phys_addr
;
405 oi
->regs
->command_status
= COMMAND_CLF
;
407 mutex_unlock(&oi
->hc_list_lock
);
411 SHOW_ERROR(0, "unsupported transfer type %d", ed
->usb_ed
->attributes
& 0x3);
412 return ERR_UNIMPLEMENTED
;
419 static void ohci_test(ohci
*oi
)
421 static usb_endpoint_descriptor desc
= {
422 sizeof(usb_endpoint_descriptor
),
423 USB_DESCRIPTOR_ENDPOINT
,
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
,
436 static uchar buf
[512];
437 unsigned char *aligned_buf
= (unsigned char *)((((addr_t
)buf
) & 0xffffffc0) + 0x40);
438 static usb_hc_transfer transfer
;
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
);
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
);
499 static ohci
*ohci_init_hc(pci_info
*pinfo
)
505 uint32 saved_interval
;
506 uint32 largest_packet
;
508 oi
= kmalloc(sizeof(ohci
));
512 SHOW_INFO(0, "allocated structure at %p", oi
);
514 memset(oi
, 0, sizeof(*oi
));
517 oi
->irq
= pinfo
->u
.h0
.interrupt_line
;
519 SHOW_INFO(0, "irq %d", oi
->irq
);
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");
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...");
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");
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) {
567 cpu_spin(1000); // 1ms
570 // SMM didn't release it
571 SHOW_ERROR0(0, "SMM will not release control of the HC");
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
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
)) {
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();
617 null_ed
->head
= null_ed
->tail
= null_td
->phys_addr
;
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) {
645 SHOW_ERROR0(0, "failed to reset the HC");
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;
665 oi
->regs
->control
= CONTROL_HCFS_OPERATIONAL
| CONTROL_CLE
| CONTROL_BLE
| CONTROL_PLE
;
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
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);
699 mutex_destroy(&oi
->hc_list_lock
);
700 mutex_destroy(&oi
->td_freelist_lock
);
702 vm_delete_region(vm_get_kernel_aspace_id(), oi
->hcca_region
);
704 vm_delete_region(vm_get_kernel_aspace_id(), oi
->reg_region
);
711 static int ohci_init(int (*hc_init_callback
)(void *callback_cookie
, void *cookie
), void *callback_cookie
)
714 pci_module_hooks
*pci
;
719 if(module_get(PCI_BUS_MODULE_NAME
, 0, (void **)&pci
) < 0) {
720 SHOW_ERROR0(0, "ohci_detect: no pci bus found..");
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
) {
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
]);
739 oi
= ohci_init_hc(&pinfo
);
741 // add it to our list of ohci hcfs
746 // register it with the bus
747 hc_init_callback(callback_cookie
, oi
);
751 module_put(PCI_BUS_MODULE_NAME
);
757 ohci_uninit(void *cookie
)
760 return ERR_UNIMPLEMENTED
;
764 ohci_module_init(void)
770 ohci_module_uninit(void)
775 static struct usb_hc_module_hooks ohci_hooks
= {
776 &ohci_init
, // bus initialization
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",
786 0, // dont keep loaded
792 module_header
*modules
[] = {