source: mainline/uspace/drv/bus/usb/ohci/hc.c@ ddbd088

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ddbd088 was 4db49344, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb: fix wrong design of transfer aborting

Apparently, we didn't do a good job in thinking through the problem.
In older HCs, it was done just wrong - the UHCI implementation commited
a batch that could have been already aborted, and EHCI+OHCI might miss
an interrupt because they commited the batch sooner than they added it
to their checked list.

This commit takes everything from the other end, which is probably the
only right one. Instead of an endpoint having an extra mutex, it
inherits a mutex from the outside. It never locks it though, it just
checks if the mutex is locked and uses it for waiting on condition
variables.

This mutex is supposed to be the one which the HC driver uses for
locking its structures in scheduling. This way, we avoid the ABBA
deadlock completely, while preserving the synchronization on an
endpoint.

The good thing is that this implementation is much easier to extend with
multiple active batches per endpoint.

  • Property mode set to 100644
File size: 17.5 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbohcihc
30 * @{
31 */
32/** @file
33 * @brief OHCI Host controller driver routines
34 */
35
36#include <assert.h>
37#include <async.h>
38#include <errno.h>
39#include <macros.h>
40#include <mem.h>
41#include <stdlib.h>
42#include <str_error.h>
43#include <stddef.h>
44#include <stdint.h>
45
46#include <usb/debug.h>
47#include <usb/host/utility.h>
48#include <usb/usb.h>
49
50#include "ohci_bus.h"
51#include "ohci_batch.h"
52
53#include "hc.h"
54
55#define OHCI_USED_INTERRUPTS \
56 (I_SO | I_WDH | I_UE | I_RHSC)
57
58static const irq_pio_range_t ohci_pio_ranges[] = {
59 {
60 .base = 0,
61 .size = sizeof(ohci_regs_t)
62 }
63};
64
65static const irq_cmd_t ohci_irq_commands[] = {
66 {
67 .cmd = CMD_PIO_READ_32,
68 .dstarg = 1,
69 .addr = NULL
70 },
71 {
72 .cmd = CMD_AND,
73 .srcarg = 1,
74 .dstarg = 2,
75 .value = 0
76 },
77 {
78 .cmd = CMD_PREDICATE,
79 .srcarg = 2,
80 .value = 2
81 },
82 {
83 .cmd = CMD_PIO_WRITE_A_32,
84 .srcarg = 1,
85 .addr = NULL
86 },
87 {
88 .cmd = CMD_ACCEPT
89 }
90};
91
92static int hc_init_transfer_lists(hc_t *instance);
93static int hc_init_memory(hc_t *instance);
94
95/** Generate IRQ code.
96 * @param[out] ranges PIO ranges buffer.
97 * @param[in] ranges_size Size of the ranges buffer (bytes).
98 * @param[out] cmds Commands buffer.
99 * @param[in] cmds_size Size of the commands buffer (bytes).
100 * @param[in] hw_res Device's resources.
101 *
102 * @return Error code.
103 */
104int hc_gen_irq_code(irq_code_t *code, hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
105{
106 assert(code);
107 assert(hw_res);
108
109 if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1)
110 return EINVAL;
111
112 const addr_range_t regs = hw_res->mem_ranges.ranges[0];
113
114 if (RNGSZ(regs) < sizeof(ohci_regs_t))
115 return EOVERFLOW;
116
117 code->ranges = malloc(sizeof(ohci_pio_ranges));
118 if (code->ranges == NULL)
119 return ENOMEM;
120
121 code->cmds = malloc(sizeof(ohci_irq_commands));
122 if (code->cmds == NULL) {
123 free(code->ranges);
124 return ENOMEM;
125 }
126
127 code->rangecount = ARRAY_SIZE(ohci_pio_ranges);
128 code->cmdcount = ARRAY_SIZE(ohci_irq_commands);
129
130 memcpy(code->ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
131 code->ranges[0].base = RNGABS(regs);
132
133 memcpy(code->cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
134 ohci_regs_t *registers = (ohci_regs_t *) RNGABSPTR(regs);
135 code->cmds[0].addr = (void *) &registers->interrupt_status;
136 code->cmds[3].addr = (void *) &registers->interrupt_status;
137 OHCI_WR(code->cmds[1].value, OHCI_USED_INTERRUPTS);
138
139 usb_log_debug("Memory mapped regs at %p (size %zu), IRQ %d.",
140 RNGABSPTR(regs), RNGSZ(regs), hw_res->irqs.irqs[0]);
141
142 return hw_res->irqs.irqs[0];
143}
144
145/** Initialize OHCI hc driver structure
146 *
147 * @param[in] instance Memory place for the structure.
148 * @param[in] regs Device's resources
149 * @param[in] interrupts True if w interrupts should be used
150 * @return Error code
151 */
152int hc_add(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
153{
154 hc_t *instance = hcd_to_hc(hcd);
155 assert(hw_res);
156 if (hw_res->mem_ranges.count != 1 ||
157 hw_res->mem_ranges.ranges[0].size < sizeof(ohci_regs_t))
158 return EINVAL;
159
160 int ret = pio_enable_range(&hw_res->mem_ranges.ranges[0],
161 (void **) &instance->registers);
162 if (ret != EOK) {
163 usb_log_error("Failed to gain access to registers: %s.",
164 str_error(ret));
165 return ret;
166 }
167 usb_log_debug("Device registers at %" PRIx64 " (%zuB) accessible.",
168 hw_res->mem_ranges.ranges[0].address.absolute,
169 hw_res->mem_ranges.ranges[0].size);
170
171 list_initialize(&instance->pending_endpoints);
172 fibril_mutex_initialize(&instance->guard);
173
174 ret = hc_init_memory(instance);
175 if (ret != EOK) {
176 usb_log_error("Failed to create OHCI memory structures: %s.",
177 str_error(ret));
178 // TODO: We should disable pio access here
179 return ret;
180 }
181
182 return EOK;
183}
184
185/** Safely dispose host controller internal structures
186 *
187 * @param[in] instance Host controller structure to use.
188 */
189int hc_gone(hc_device_t *instance)
190{
191 assert(instance);
192 /* TODO: implement*/
193 return ENOTSUP;
194}
195
196void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
197{
198 assert(instance);
199 assert(ep);
200
201 endpoint_list_t *list = &instance->lists[ep->transfer_type];
202 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ep);
203 assert(list);
204 assert(ohci_ep);
205
206 /* Enqueue ep */
207 switch (ep->transfer_type) {
208 case USB_TRANSFER_CONTROL:
209 OHCI_CLR(instance->registers->control, C_CLE);
210 endpoint_list_add_ep(list, ohci_ep);
211 OHCI_WR(instance->registers->control_current, 0);
212 OHCI_SET(instance->registers->control, C_CLE);
213 break;
214 case USB_TRANSFER_BULK:
215 OHCI_CLR(instance->registers->control, C_BLE);
216 endpoint_list_add_ep(list, ohci_ep);
217 OHCI_WR(instance->registers->bulk_current, 0);
218 OHCI_SET(instance->registers->control, C_BLE);
219 break;
220 case USB_TRANSFER_ISOCHRONOUS:
221 case USB_TRANSFER_INTERRUPT:
222 OHCI_CLR(instance->registers->control, C_PLE | C_IE);
223 endpoint_list_add_ep(list, ohci_ep);
224 OHCI_SET(instance->registers->control, C_PLE | C_IE);
225 break;
226 }
227}
228
229void hc_dequeue_endpoint(hc_t *instance, const endpoint_t *ep)
230{
231 assert(instance);
232 assert(ep);
233
234 /* Dequeue ep */
235 endpoint_list_t *list = &instance->lists[ep->transfer_type];
236 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ep);
237
238 assert(list);
239 assert(ohci_ep);
240 switch (ep->transfer_type) {
241 case USB_TRANSFER_CONTROL:
242 OHCI_CLR(instance->registers->control, C_CLE);
243 endpoint_list_remove_ep(list, ohci_ep);
244 OHCI_WR(instance->registers->control_current, 0);
245 OHCI_SET(instance->registers->control, C_CLE);
246 break;
247 case USB_TRANSFER_BULK:
248 OHCI_CLR(instance->registers->control, C_BLE);
249 endpoint_list_remove_ep(list, ohci_ep);
250 OHCI_WR(instance->registers->bulk_current, 0);
251 OHCI_SET(instance->registers->control, C_BLE);
252 break;
253 case USB_TRANSFER_ISOCHRONOUS:
254 case USB_TRANSFER_INTERRUPT:
255 OHCI_CLR(instance->registers->control, C_PLE | C_IE);
256 endpoint_list_remove_ep(list, ohci_ep);
257 OHCI_SET(instance->registers->control, C_PLE | C_IE);
258 break;
259 default:
260 break;
261 }
262}
263
264int ohci_hc_status(bus_t *bus_base, uint32_t *status)
265{
266 assert(bus_base);
267 assert(status);
268
269 ohci_bus_t *bus = (ohci_bus_t *) bus_base;
270 hc_t *hc = bus->hc;
271 assert(hc);
272
273 if (hc->registers){
274 *status = OHCI_RD(hc->registers->interrupt_status);
275 OHCI_WR(hc->registers->interrupt_status, *status);
276 }
277 return EOK;
278}
279
280/** Add USB transfer to the schedule.
281 *
282 * @param[in] hcd HCD driver structure.
283 * @param[in] batch Batch representing the transfer.
284 * @return Error code.
285 */
286int ohci_hc_schedule(usb_transfer_batch_t *batch)
287{
288 assert(batch);
289
290 ohci_bus_t *bus = (ohci_bus_t *) endpoint_get_bus(batch->ep);
291 hc_t *hc = bus->hc;
292 assert(hc);
293
294 /* Check for root hub communication */
295 if (batch->target.address == ohci_rh_get_address(&hc->rh)) {
296 usb_log_debug("OHCI root hub request.");
297 return ohci_rh_schedule(&hc->rh, batch);
298 }
299
300 endpoint_t *ep = batch->ep;
301 ohci_endpoint_t * const ohci_ep = ohci_endpoint_get(ep);
302 ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
303
304 int err;
305 if ((err = ohci_transfer_batch_prepare(ohci_batch)))
306 return err;
307
308 fibril_mutex_lock(&hc->guard);
309 if ((err = endpoint_activate_locked(ep, batch))) {
310 fibril_mutex_unlock(&hc->guard);
311 return err;
312 }
313
314 ohci_transfer_batch_commit(ohci_batch);
315 list_append(&ohci_ep->pending_link, &hc->pending_endpoints);
316 fibril_mutex_unlock(&hc->guard);
317
318 /* Control and bulk schedules need a kick to start working */
319 switch (batch->ep->transfer_type)
320 {
321 case USB_TRANSFER_CONTROL:
322 OHCI_SET(hc->registers->command_status, CS_CLF);
323 break;
324 case USB_TRANSFER_BULK:
325 OHCI_SET(hc->registers->command_status, CS_BLF);
326 break;
327 default:
328 break;
329 }
330
331 return EOK;
332}
333
334/** Interrupt handling routine
335 *
336 * @param[in] hcd HCD driver structure.
337 * @param[in] status Value of the status register at the time of interrupt.
338 */
339void ohci_hc_interrupt(bus_t *bus_base, uint32_t status)
340{
341 assert(bus_base);
342
343 ohci_bus_t *bus = (ohci_bus_t *) bus_base;
344 hc_t *hc = bus->hc;
345 assert(hc);
346
347 status = OHCI_RD(status);
348 assert(hc);
349 if ((status & ~I_SF) == 0) /* ignore sof status */
350 return;
351 usb_log_debug2("OHCI(%p) interrupt: %x.", hc, status);
352 if (status & I_RHSC)
353 ohci_rh_interrupt(&hc->rh);
354
355 if (status & I_WDH) {
356 fibril_mutex_lock(&hc->guard);
357 usb_log_debug2("HCCA: %p-%#" PRIx32 " (%p).", hc->hcca,
358 OHCI_RD(hc->registers->hcca),
359 (void *) addr_to_phys(hc->hcca));
360 usb_log_debug2("Periodic current: %#" PRIx32 ".",
361 OHCI_RD(hc->registers->periodic_current));
362
363 list_foreach_safe(hc->pending_endpoints, current, next) {
364 ohci_endpoint_t *ep
365 = list_get_instance(current, ohci_endpoint_t, pending_link);
366
367 ohci_transfer_batch_t *batch
368 = ohci_transfer_batch_get(ep->base.active_batch);
369 assert(batch);
370
371 if (ohci_transfer_batch_check_completed(batch)) {
372 endpoint_deactivate_locked(&ep->base);
373 list_remove(current);
374 hc_reset_toggles(&batch->base, &ohci_ep_toggle_reset);
375 usb_transfer_batch_finish(&batch->base);
376 }
377 }
378 fibril_mutex_unlock(&hc->guard);
379 }
380
381 if (status & I_UE) {
382 usb_log_fatal("Error like no other!");
383 hc_start(&hc->base);
384 }
385
386}
387
388/** Turn off any (BIOS)driver that might be in control of the device.
389 *
390 * This function implements routines described in chapter 5.1.1.3 of the OHCI
391 * specification (page 40, pdf page 54).
392 *
393 * @param[in] instance OHCI hc driver structure.
394 */
395int hc_gain_control(hc_device_t *hcd)
396{
397 hc_t *instance = hcd_to_hc(hcd);
398
399 usb_log_debug("Requesting OHCI control.");
400 if (OHCI_RD(instance->registers->revision) & R_LEGACY_FLAG) {
401 /* Turn off legacy emulation, it should be enough to zero
402 * the lowest bit, but it caused problems. Thus clear all
403 * except GateA20 (causes restart on some hw).
404 * See page 145 of the specs for details.
405 */
406 volatile uint32_t *ohci_emulation_reg =
407 (uint32_t*)((char*)instance->registers + LEGACY_REGS_OFFSET);
408 usb_log_debug("OHCI legacy register %p: %x.",
409 ohci_emulation_reg, OHCI_RD(*ohci_emulation_reg));
410 /* Zero everything but A20State */
411 // TODO: should we ack interrupts before doing this?
412 OHCI_CLR(*ohci_emulation_reg, ~0x100);
413 usb_log_debug(
414 "OHCI legacy register (should be 0 or 0x100) %p: %x.",
415 ohci_emulation_reg, OHCI_RD(*ohci_emulation_reg));
416 }
417
418 /* Interrupt routing enabled => smm driver is active */
419 if (OHCI_RD(instance->registers->control) & C_IR) {
420 usb_log_debug("SMM driver: request ownership change.");
421 // TODO: should we ack interrupts before doing this?
422 OHCI_SET(instance->registers->command_status, CS_OCR);
423 /* Hope that SMM actually knows its stuff or we can hang here */
424 while (OHCI_RD(instance->registers->control) & C_IR) {
425 async_usleep(1000);
426 }
427 usb_log_info("SMM driver: Ownership taken.");
428 C_HCFS_SET(instance->registers->control, C_HCFS_RESET);
429 async_usleep(50000);
430 return EOK;
431 }
432
433 const unsigned hc_status = C_HCFS_GET(instance->registers->control);
434 /* Interrupt routing disabled && status != USB_RESET => BIOS active */
435 if (hc_status != C_HCFS_RESET) {
436 usb_log_debug("BIOS driver found.");
437 if (hc_status == C_HCFS_OPERATIONAL) {
438 usb_log_info("BIOS driver: HC operational.");
439 return EOK;
440 }
441 /* HC is suspended assert resume for 20ms */
442 C_HCFS_SET(instance->registers->control, C_HCFS_RESUME);
443 async_usleep(20000);
444 usb_log_info("BIOS driver: HC resumed.");
445 return EOK;
446 }
447
448 /* HC is in reset (hw startup) => no other driver
449 * maintain reset for at least the time specified in USB spec (50 ms)*/
450 usb_log_debug("Host controller found in reset state.");
451 async_usleep(50000);
452 return EOK;
453}
454
455/** OHCI hw initialization routine.
456 *
457 * @param[in] instance OHCI hc driver structure.
458 */
459int hc_start(hc_device_t *hcd)
460{
461 hc_t *instance = hcd_to_hc(hcd);
462 ohci_rh_init(&instance->rh, instance->registers, "ohci rh");
463
464 /* OHCI guide page 42 */
465 assert(instance);
466 usb_log_debug2("Started hc initialization routine.");
467
468 /* Save contents of fm_interval register */
469 const uint32_t fm_interval = OHCI_RD(instance->registers->fm_interval);
470 usb_log_debug2("Old value of HcFmInterval: %x.", fm_interval);
471
472 /* Reset hc */
473 usb_log_debug2("HC reset.");
474 size_t time = 0;
475 OHCI_WR(instance->registers->command_status, CS_HCR);
476 while (OHCI_RD(instance->registers->command_status) & CS_HCR) {
477 async_usleep(10);
478 time += 10;
479 }
480 usb_log_debug2("HC reset complete in %zu us.", time);
481
482 /* Restore fm_interval */
483 OHCI_WR(instance->registers->fm_interval, fm_interval);
484 assert((OHCI_RD(instance->registers->command_status) & CS_HCR) == 0);
485
486 /* hc is now in suspend state */
487 usb_log_debug2("HC should be in suspend state(%x).",
488 OHCI_RD(instance->registers->control));
489
490 /* Use HCCA */
491 OHCI_WR(instance->registers->hcca, addr_to_phys(instance->hcca));
492
493 /* Use queues */
494 OHCI_WR(instance->registers->bulk_head,
495 instance->lists[USB_TRANSFER_BULK].list_head_pa);
496 usb_log_debug2("Bulk HEAD set to: %p (%#" PRIx32 ").",
497 instance->lists[USB_TRANSFER_BULK].list_head,
498 instance->lists[USB_TRANSFER_BULK].list_head_pa);
499
500 OHCI_WR(instance->registers->control_head,
501 instance->lists[USB_TRANSFER_CONTROL].list_head_pa);
502 usb_log_debug2("Control HEAD set to: %p (%#" PRIx32 ").",
503 instance->lists[USB_TRANSFER_CONTROL].list_head,
504 instance->lists[USB_TRANSFER_CONTROL].list_head_pa);
505
506 /* Enable queues */
507 OHCI_SET(instance->registers->control, (C_PLE | C_IE | C_CLE | C_BLE));
508 usb_log_debug("Queues enabled(%x).",
509 OHCI_RD(instance->registers->control));
510
511 /* Enable interrupts */
512 if (instance->base.irq_cap >= 0) {
513 OHCI_WR(instance->registers->interrupt_enable,
514 OHCI_USED_INTERRUPTS);
515 usb_log_debug("Enabled interrupts: %x.",
516 OHCI_RD(instance->registers->interrupt_enable));
517 OHCI_WR(instance->registers->interrupt_enable, I_MI);
518 }
519
520 /* Set periodic start to 90% */
521 const uint32_t frame_length =
522 (fm_interval >> FMI_FI_SHIFT) & FMI_FI_MASK;
523 OHCI_WR(instance->registers->periodic_start,
524 ((frame_length / 10) * 9) & PS_MASK << PS_SHIFT);
525 usb_log_debug2("All periodic start set to: %x(%u - 90%% of %d).",
526 OHCI_RD(instance->registers->periodic_start),
527 OHCI_RD(instance->registers->periodic_start), frame_length);
528 C_HCFS_SET(instance->registers->control, C_HCFS_OPERATIONAL);
529 usb_log_debug("OHCI HC up and running (ctl_reg=0x%x).",
530 OHCI_RD(instance->registers->control));
531
532 return EOK;
533}
534
535/**
536 * Setup roothub as a virtual hub.
537 */
538int hc_setup_roothub(hc_device_t *hcd)
539{
540 return hc_setup_virtual_root_hub(hcd, USB_SPEED_FULL);
541}
542
543/** Initialize schedule queues
544 *
545 * @param[in] instance OHCI hc driver structure
546 * @return Error code
547 */
548int hc_init_transfer_lists(hc_t *instance)
549{
550 assert(instance);
551#define SETUP_ENDPOINT_LIST(type) \
552do { \
553 const char *name = usb_str_transfer_type(type); \
554 const int ret = endpoint_list_init(&instance->lists[type], name); \
555 if (ret != EOK) { \
556 usb_log_error("Failed to setup %s endpoint list: %s.", \
557 name, str_error(ret)); \
558 endpoint_list_fini(&instance->lists[USB_TRANSFER_ISOCHRONOUS]);\
559 endpoint_list_fini(&instance->lists[USB_TRANSFER_INTERRUPT]); \
560 endpoint_list_fini(&instance->lists[USB_TRANSFER_CONTROL]); \
561 endpoint_list_fini(&instance->lists[USB_TRANSFER_BULK]); \
562 return ret; \
563 } \
564} while (0)
565
566 SETUP_ENDPOINT_LIST(USB_TRANSFER_ISOCHRONOUS);
567 SETUP_ENDPOINT_LIST(USB_TRANSFER_INTERRUPT);
568 SETUP_ENDPOINT_LIST(USB_TRANSFER_CONTROL);
569 SETUP_ENDPOINT_LIST(USB_TRANSFER_BULK);
570#undef SETUP_ENDPOINT_LIST
571 endpoint_list_set_next(&instance->lists[USB_TRANSFER_INTERRUPT],
572 &instance->lists[USB_TRANSFER_ISOCHRONOUS]);
573
574 return EOK;
575}
576
577/** Initialize memory structures used by the OHCI hcd.
578 *
579 * @param[in] instance OHCI hc driver structure.
580 * @return Error code.
581 */
582int hc_init_memory(hc_t *instance)
583{
584 assert(instance);
585
586 memset(&instance->rh, 0, sizeof(instance->rh));
587 /* Init queues */
588 int ret = hc_init_transfer_lists(instance);
589 if (ret != EOK) {
590 return ret;
591 }
592
593 /*Init HCCA */
594 instance->hcca = hcca_get();
595 if (instance->hcca == NULL)
596 return ENOMEM;
597 usb_log_debug2("OHCI HCCA initialized at %p.", instance->hcca);
598
599 for (unsigned i = 0; i < HCCA_INT_EP_COUNT; ++i) {
600 hcca_set_int_ep(instance->hcca, i,
601 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
602 }
603 usb_log_debug2("Interrupt HEADs set to: %p (%#" PRIx32 ").",
604 instance->lists[USB_TRANSFER_INTERRUPT].list_head,
605 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
606
607 if ((ret = ohci_bus_init(&instance->bus, instance))) {
608 usb_log_error("HC(%p): Failed to setup bus : %s",
609 instance, str_error(ret));
610 return ret;
611 }
612
613 hc_device_setup(&instance->base, (bus_t *) &instance->bus);
614
615 return EOK;
616}
617
618/**
619 * @}
620 */
Note: See TracBrowser for help on using the repository browser.