source: mainline/uspace/drv/bus/usb/uhci/hc.c@ 629255a

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

usb2_bus: no longer be a bus

As the number of implemented functions got to 3, it's not so beneficial
to inherit usb2 bus to get the functionality. Overall, four trampolines
needed to be added, which is an acceptable number.

Now, the usb2_bus has become a usb2_bus_helper, to be used as
a companion to the common bus.

This is mostly a preparation to remove the runtime binding of the bus
methods.

  • Property mode set to 100644
File size: 18.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 drvusbuhcihc
30 * @{
31 */
32/** @file
33 * @brief UHCI Host controller driver routines
34 */
35
36#include <adt/list.h>
37#include <assert.h>
38#include <async.h>
39#include <ddi.h>
40#include <device/hw_res_parsed.h>
41#include <fibril.h>
42#include <errno.h>
43#include <macros.h>
44#include <mem.h>
45#include <stdlib.h>
46#include <stdint.h>
47#include <str_error.h>
48
49#include <usb/debug.h>
50#include <usb/usb.h>
51#include <usb/host/utils/malloc32.h>
52#include <usb/host/bandwidth.h>
53#include <usb/host/utility.h>
54
55#include "uhci_batch.h"
56#include "transfer_list.h"
57#include "hc.h"
58
59#define UHCI_INTR_ALLOW_INTERRUPTS \
60 (UHCI_INTR_CRC | UHCI_INTR_COMPLETE | UHCI_INTR_SHORT_PACKET)
61#define UHCI_STATUS_USED_INTERRUPTS \
62 (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)
63
64static const irq_pio_range_t uhci_irq_pio_ranges[] = {
65 {
66 .base = 0,
67 .size = sizeof(uhci_regs_t)
68 }
69};
70
71static const irq_cmd_t uhci_irq_commands[] = {
72 {
73 .cmd = CMD_PIO_READ_16,
74 .dstarg = 1,
75 .addr = NULL
76 },
77 {
78 .cmd = CMD_AND,
79 .srcarg = 1,
80 .dstarg = 2,
81 .value = UHCI_STATUS_USED_INTERRUPTS | UHCI_STATUS_NM_INTERRUPTS
82 },
83 {
84 .cmd = CMD_PREDICATE,
85 .srcarg = 2,
86 .value = 2
87 },
88 {
89 .cmd = CMD_PIO_WRITE_A_16,
90 .srcarg = 1,
91 .addr = NULL
92 },
93 {
94 .cmd = CMD_ACCEPT
95 }
96};
97
98static void hc_init_hw(const hc_t *instance);
99static int hc_init_mem_structures(hc_t *instance);
100static int hc_init_transfer_lists(hc_t *instance);
101
102static int hc_debug_checker(void *arg);
103
104
105/** Generate IRQ code.
106 * @param[out] code IRQ code structure.
107 * @param[in] hw_res Device's resources.
108 *
109 * @return Error code.
110 */
111int hc_gen_irq_code(irq_code_t *code, hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
112{
113 assert(code);
114 assert(hw_res);
115
116 if (hw_res->irqs.count != 1 || hw_res->io_ranges.count != 1)
117 return EINVAL;
118 const addr_range_t regs = hw_res->io_ranges.ranges[0];
119
120 if (RNGSZ(regs) < sizeof(uhci_regs_t))
121 return EOVERFLOW;
122
123 code->ranges = malloc(sizeof(uhci_irq_pio_ranges));
124 if (code->ranges == NULL)
125 return ENOMEM;
126
127 code->cmds = malloc(sizeof(uhci_irq_commands));
128 if (code->cmds == NULL) {
129 free(code->ranges);
130 return ENOMEM;
131 }
132
133 code->rangecount = ARRAY_SIZE(uhci_irq_pio_ranges);
134 code->cmdcount = ARRAY_SIZE(uhci_irq_commands);
135
136 memcpy(code->ranges, uhci_irq_pio_ranges, sizeof(uhci_irq_pio_ranges));
137 code->ranges[0].base = RNGABS(regs);
138
139 memcpy(code->cmds, uhci_irq_commands, sizeof(uhci_irq_commands));
140 uhci_regs_t *registers = (uhci_regs_t *) RNGABSPTR(regs);
141 code->cmds[0].addr = (void*)&registers->usbsts;
142 code->cmds[3].addr = (void*)&registers->usbsts;
143
144 usb_log_debug("I/O regs at %p (size %zu), IRQ %d.",
145 RNGABSPTR(regs), RNGSZ(regs), hw_res->irqs.irqs[0]);
146
147 return hw_res->irqs.irqs[0];
148}
149
150/** Take action based on the interrupt cause.
151 *
152 * @param[in] hcd HCD structure to use.
153 * @param[in] status Value of the status register at the time of interrupt.
154 *
155 * Interrupt might indicate:
156 * - transaction completed, either by triggering IOC, SPD, or an error
157 * - some kind of device error
158 * - resume from suspend state (not implemented)
159 */
160static void hc_interrupt(bus_t *bus, uint32_t status)
161{
162 hc_t *instance = bus_to_hc(bus);
163
164 /* Lower 2 bits are transaction error and transaction complete */
165 if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
166 transfer_list_check_finished(&instance->transfers_interrupt);
167 transfer_list_check_finished(&instance->transfers_control_slow);
168 transfer_list_check_finished(&instance->transfers_control_full);
169 transfer_list_check_finished(&instance->transfers_bulk_full);
170 }
171
172 /* Resume interrupts are not supported */
173 if (status & UHCI_STATUS_RESUME) {
174 usb_log_error("Resume interrupt!");
175 }
176
177 /* Bits 4 and 5 indicate hc error */
178 if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) {
179 usb_log_error("UHCI hardware failure!.");
180 ++instance->hw_failures;
181 transfer_list_abort_all(&instance->transfers_interrupt);
182 transfer_list_abort_all(&instance->transfers_control_slow);
183 transfer_list_abort_all(&instance->transfers_control_full);
184 transfer_list_abort_all(&instance->transfers_bulk_full);
185
186 if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) {
187 /* reinitialize hw, this triggers virtual disconnect*/
188 hc_init_hw(instance);
189 } else {
190 usb_log_fatal("Too many UHCI hardware failures!.");
191 hc_gone(&instance->base);
192 }
193 }
194}
195
196/** Initialize UHCI hc driver structure
197 *
198 * @param[in] instance Memory place to initialize.
199 * @param[in] regs Range of device's I/O control registers.
200 * @param[in] interrupts True if hw interrupts should be used.
201 * @return Error code.
202 * @note Should be called only once on any structure.
203 *
204 * Initializes memory structures, starts up hw, and launches debugger and
205 * interrupt fibrils.
206 */
207int hc_add(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
208{
209 hc_t *instance = hcd_to_hc(hcd);
210 assert(hw_res);
211 if (hw_res->io_ranges.count != 1 ||
212 hw_res->io_ranges.ranges[0].size < sizeof(uhci_regs_t))
213 return EINVAL;
214
215 instance->hw_failures = 0;
216
217 /* allow access to hc control registers */
218 int ret = pio_enable_range(&hw_res->io_ranges.ranges[0],
219 (void **) &instance->registers);
220 if (ret != EOK) {
221 usb_log_error("Failed to gain access to registers: %s.",
222 str_error(ret));
223 return ret;
224 }
225
226 usb_log_debug("Device registers at %" PRIx64 " (%zuB) accessible.",
227 hw_res->io_ranges.ranges[0].address.absolute,
228 hw_res->io_ranges.ranges[0].size);
229
230 ret = hc_init_mem_structures(instance);
231 if (ret != EOK) {
232 usb_log_error("Failed to init UHCI memory structures: %s.",
233 str_error(ret));
234 // TODO: we should disable pio here
235 return ret;
236 }
237
238 return EOK;
239}
240
241int hc_start(hc_device_t *hcd)
242{
243 hc_t *instance = hcd_to_hc(hcd);
244 hc_init_hw(instance);
245 (void)hc_debug_checker;
246
247 return uhci_rh_init(&instance->rh, instance->registers->ports, "uhci");
248}
249
250int hc_setup_roothub(hc_device_t *hcd)
251{
252 return hc_setup_virtual_root_hub(hcd, USB_SPEED_FULL);
253}
254
255/** Safely dispose host controller internal structures
256 *
257 * @param[in] instance Host controller structure to use.
258 */
259int hc_gone(hc_device_t *instance)
260{
261 assert(instance);
262 //TODO Implement
263 return ENOTSUP;
264}
265
266/** Initialize UHCI hc hw resources.
267 *
268 * @param[in] instance UHCI structure to use.
269 * For magic values see UHCI Design Guide
270 */
271void hc_init_hw(const hc_t *instance)
272{
273 assert(instance);
274 uhci_regs_t *registers = instance->registers;
275
276 /* Reset everything, who knows what touched it before us */
277 pio_write_16(&registers->usbcmd, UHCI_CMD_GLOBAL_RESET);
278 async_usleep(50000); /* 50ms according to USB spec(root hub reset) */
279 pio_write_16(&registers->usbcmd, 0);
280
281 /* Reset hc, all states and counters. Hope that hw is not broken */
282 pio_write_16(&registers->usbcmd, UHCI_CMD_HCRESET);
283 do { async_usleep(10); }
284 while ((pio_read_16(&registers->usbcmd) & UHCI_CMD_HCRESET) != 0);
285
286 /* Set frame to exactly 1ms */
287 pio_write_8(&registers->sofmod, 64);
288
289 /* Set frame list pointer */
290 const uint32_t pa = addr_to_phys(instance->frame_list);
291 pio_write_32(&registers->flbaseadd, pa);
292
293 if (instance->base.irq_cap >= 0) {
294 /* Enable all interrupts, but resume interrupt */
295 pio_write_16(&instance->registers->usbintr,
296 UHCI_INTR_ALLOW_INTERRUPTS);
297 }
298
299 const uint16_t cmd = pio_read_16(&registers->usbcmd);
300 if (cmd != 0)
301 usb_log_warning("Previous command value: %x.", cmd);
302
303 /* Start the hc with large(64B) packet FSBR */
304 pio_write_16(&registers->usbcmd,
305 UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET | UHCI_CMD_CONFIGURE);
306}
307
308static usb_transfer_batch_t *create_transfer_batch(endpoint_t *ep)
309{
310 uhci_transfer_batch_t *batch = uhci_transfer_batch_create(ep);
311 return &batch->base;
312}
313
314static void destroy_transfer_batch(usb_transfer_batch_t *batch)
315{
316 uhci_transfer_batch_destroy(uhci_transfer_batch_get(batch));
317}
318
319static endpoint_t *endpoint_create(device_t *device, const usb_endpoint_descriptors_t *desc)
320{
321 endpoint_t *ep = calloc(1, sizeof(uhci_endpoint_t));
322 if (ep)
323 endpoint_init(ep, device, desc);
324 return ep;
325}
326
327static int endpoint_register(endpoint_t *ep)
328{
329 hc_t * const hc = bus_to_hc(endpoint_get_bus(ep));
330
331 const int err = usb2_bus_endpoint_register(&hc->bus_helper, ep);
332 if (err)
333 return err;
334
335 transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
336 if (!list)
337 /*
338 * We don't support this combination (e.g. isochronous). Do not
339 * fail early, because that would block any device with these
340 * endpoints from connecting. Instead, make sure these transfers
341 * are denied soon enough with ENOTSUP not to fail on asserts.
342 */
343 return EOK;
344
345 endpoint_set_online(ep, &list->guard);
346 return EOK;
347}
348
349static void endpoint_unregister(endpoint_t *ep)
350{
351 hc_t * const hc = bus_to_hc(endpoint_get_bus(ep));
352 usb2_bus_endpoint_unregister(&hc->bus_helper, ep);
353
354 // Check for the roothub, as it does not schedule into lists
355 if (ep->device->address == uhci_rh_get_address(&hc->rh)) {
356 // FIXME: We shall check the roothub for active transfer. But
357 // as it is polling, there is no way to make it stop doing so.
358 // Return after rewriting uhci rh.
359 return;
360 }
361
362 transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
363 if (!list)
364 /*
365 * We don't support this combination (e.g. isochronous),
366 * so no transfer can be active.
367 */
368 return;
369
370 fibril_mutex_lock(&list->guard);
371
372 endpoint_set_offline_locked(ep);
373 /* From now on, no other transfer will be scheduled. */
374
375 if (!ep->active_batch) {
376 fibril_mutex_unlock(&list->guard);
377 return;
378 }
379
380 /* First, offer the batch a short chance to be finished. */
381 endpoint_wait_timeout_locked(ep, 10000);
382
383 if (!ep->active_batch) {
384 fibril_mutex_unlock(&list->guard);
385 return;
386 }
387
388 uhci_transfer_batch_t * const batch =
389 uhci_transfer_batch_get(ep->active_batch);
390
391 /* Remove the batch from the schedule to stop it from being finished. */
392 endpoint_deactivate_locked(ep);
393 transfer_list_remove_batch(list, batch);
394
395 fibril_mutex_unlock(&list->guard);
396
397 /*
398 * We removed the batch from software schedule only, it's still possible
399 * that HC has it in its caches. Better wait a while before we release
400 * the buffers.
401 */
402 async_usleep(20000);
403 batch->base.error = EINTR;
404 batch->base.transferred_size = 0;
405 usb_transfer_batch_finish(&batch->base);
406}
407
408static int device_enumerate(device_t *dev)
409{
410 hc_t * const hc = bus_to_hc(dev->bus);
411 return usb2_bus_device_enumerate(&hc->bus_helper, dev);
412}
413
414static int hc_status(bus_t *, uint32_t *);
415static int hc_schedule(usb_transfer_batch_t *);
416
417static const bus_ops_t uhci_bus_ops = {
418 .interrupt = hc_interrupt,
419 .status = hc_status,
420
421 .device_enumerate = device_enumerate,
422
423 .endpoint_create = endpoint_create,
424 .endpoint_register = endpoint_register,
425 .endpoint_unregister = endpoint_unregister,
426
427 .batch_create = create_transfer_batch,
428 .batch_schedule = hc_schedule,
429 .batch_destroy = destroy_transfer_batch,
430};
431
432/** Initialize UHCI hc memory structures.
433 *
434 * @param[in] instance UHCI structure to use.
435 * @return Error code
436 * @note Should be called only once on any structure.
437 *
438 * Structures:
439 * - transfer lists (queue heads need to be accessible by the hw)
440 * - frame list page (needs to be one UHCI hw accessible 4K page)
441 */
442int hc_init_mem_structures(hc_t *instance)
443{
444 assert(instance);
445
446 usb2_bus_helper_init(&instance->bus_helper, &bandwidth_accounting_usb11);
447
448 bus_init(&instance->bus, sizeof(device_t));
449 instance->bus.ops = &uhci_bus_ops;
450
451 hc_device_setup(&instance->base, &instance->bus);
452
453 /* Init USB frame list page */
454 instance->frame_list = get_page();
455 if (!instance->frame_list) {
456 return ENOMEM;
457 }
458 usb_log_debug("Initialized frame list at %p.", instance->frame_list);
459
460 /* Init transfer lists */
461 int ret = hc_init_transfer_lists(instance);
462 if (ret != EOK) {
463 usb_log_error("Failed to initialize transfer lists.");
464 return_page(instance->frame_list);
465 return ENOMEM;
466 }
467 list_initialize(&instance->pending_endpoints);
468 usb_log_debug("Initialized transfer lists.");
469
470
471 /* Set all frames to point to the first queue head */
472 const uint32_t queue = LINK_POINTER_QH(
473 addr_to_phys(instance->transfers_interrupt.queue_head));
474
475 for (unsigned i = 0; i < UHCI_FRAME_LIST_COUNT; ++i) {
476 instance->frame_list[i] = queue;
477 }
478
479 return EOK;
480}
481
482/** Initialize UHCI hc transfer lists.
483 *
484 * @param[in] instance UHCI structure to use.
485 * @return Error code
486 * @note Should be called only once on any structure.
487 *
488 * Initializes transfer lists and sets them in one chain to support proper
489 * USB scheduling. Sets pointer table for quick access.
490 */
491int hc_init_transfer_lists(hc_t *instance)
492{
493 assert(instance);
494#define SETUP_TRANSFER_LIST(type, name) \
495do { \
496 int ret = transfer_list_init(&instance->transfers_##type, name); \
497 if (ret != EOK) { \
498 usb_log_error("Failed to setup %s transfer list: %s.", \
499 name, str_error(ret)); \
500 transfer_list_fini(&instance->transfers_bulk_full); \
501 transfer_list_fini(&instance->transfers_control_full); \
502 transfer_list_fini(&instance->transfers_control_slow); \
503 transfer_list_fini(&instance->transfers_interrupt); \
504 return ret; \
505 } \
506} while (0)
507
508 SETUP_TRANSFER_LIST(bulk_full, "BULK FULL");
509 SETUP_TRANSFER_LIST(control_full, "CONTROL FULL");
510 SETUP_TRANSFER_LIST(control_slow, "CONTROL LOW");
511 SETUP_TRANSFER_LIST(interrupt, "INTERRUPT");
512#undef SETUP_TRANSFER_LIST
513 /* Connect lists into one schedule */
514 transfer_list_set_next(&instance->transfers_control_full,
515 &instance->transfers_bulk_full);
516 transfer_list_set_next(&instance->transfers_control_slow,
517 &instance->transfers_control_full);
518 transfer_list_set_next(&instance->transfers_interrupt,
519 &instance->transfers_control_slow);
520
521 /*FSBR, This feature is not needed (adds no benefit) and is supposedly
522 * buggy on certain hw, enable at your own risk. */
523#ifdef FSBR
524 transfer_list_set_next(&instance->transfers_bulk_full,
525 &instance->transfers_control_full);
526#endif
527
528 /* Assign pointers to be used during scheduling */
529 instance->transfers[USB_SPEED_FULL][USB_TRANSFER_INTERRUPT] =
530 &instance->transfers_interrupt;
531 instance->transfers[USB_SPEED_LOW][USB_TRANSFER_INTERRUPT] =
532 &instance->transfers_interrupt;
533 instance->transfers[USB_SPEED_FULL][USB_TRANSFER_CONTROL] =
534 &instance->transfers_control_full;
535 instance->transfers[USB_SPEED_LOW][USB_TRANSFER_CONTROL] =
536 &instance->transfers_control_slow;
537 instance->transfers[USB_SPEED_FULL][USB_TRANSFER_BULK] =
538 &instance->transfers_bulk_full;
539
540 return EOK;
541}
542
543static int hc_status(bus_t *bus, uint32_t *status)
544{
545 hc_t *instance = bus_to_hc(bus);
546 assert(status);
547
548 *status = 0;
549 if (instance->registers) {
550 uint16_t s = pio_read_16(&instance->registers->usbsts);
551 pio_write_16(&instance->registers->usbsts, s);
552 *status = s;
553 }
554 return EOK;
555}
556
557/**
558 * Schedule batch for execution.
559 *
560 * @param[in] instance UHCI structure to use.
561 * @param[in] batch Transfer batch to schedule.
562 * @return Error code
563 */
564static int hc_schedule(usb_transfer_batch_t *batch)
565{
566 uhci_transfer_batch_t *uhci_batch = uhci_transfer_batch_get(batch);
567 endpoint_t *ep = batch->ep;
568 hc_t *hc = bus_to_hc(endpoint_get_bus(ep));
569
570 if (batch->target.address == uhci_rh_get_address(&hc->rh))
571 return uhci_rh_schedule(&hc->rh, batch);
572
573 transfer_list_t * const list =
574 hc->transfers[ep->device->speed][ep->transfer_type];
575
576 if (!list)
577 return ENOTSUP;
578
579 int err;
580 if ((err = uhci_transfer_batch_prepare(uhci_batch)))
581 return err;
582
583 return transfer_list_add_batch(list, uhci_batch);
584}
585
586/** Debug function, checks consistency of memory structures.
587 *
588 * @param[in] arg UHCI structure to use.
589 * @return EOK (should never return)
590 */
591int hc_debug_checker(void *arg)
592{
593 hc_t *instance = arg;
594 assert(instance);
595
596#define QH(queue) \
597 instance->transfers_##queue.queue_head
598
599 while (1) {
600 const uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
601 const uint16_t sts = pio_read_16(&instance->registers->usbsts);
602 const uint16_t intr =
603 pio_read_16(&instance->registers->usbintr);
604
605 if (((cmd & UHCI_CMD_RUN_STOP) != 1) || (sts != 0)) {
606 usb_log_debug2("Command: %X Status: %X Intr: %x",
607 cmd, sts, intr);
608 }
609
610 const uintptr_t frame_list =
611 pio_read_32(&instance->registers->flbaseadd) & ~0xfff;
612 if (frame_list != addr_to_phys(instance->frame_list)) {
613 usb_log_debug("Framelist address: %p vs. %p.",
614 (void *) frame_list,
615 (void *) addr_to_phys(instance->frame_list));
616 }
617
618 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
619
620 uintptr_t expected_pa = instance->frame_list[frnum]
621 & LINK_POINTER_ADDRESS_MASK;
622 uintptr_t real_pa = addr_to_phys(QH(interrupt));
623 if (expected_pa != real_pa) {
624 usb_log_debug("Interrupt QH: %p (frame %d) vs. %p.",
625 (void *) expected_pa, frnum, (void *) real_pa);
626 }
627
628 expected_pa = QH(interrupt)->next & LINK_POINTER_ADDRESS_MASK;
629 real_pa = addr_to_phys(QH(control_slow));
630 if (expected_pa != real_pa) {
631 usb_log_debug("Control Slow QH: %p vs. %p.",
632 (void *) expected_pa, (void *) real_pa);
633 }
634
635 expected_pa = QH(control_slow)->next & LINK_POINTER_ADDRESS_MASK;
636 real_pa = addr_to_phys(QH(control_full));
637 if (expected_pa != real_pa) {
638 usb_log_debug("Control Full QH: %p vs. %p.",
639 (void *) expected_pa, (void *) real_pa);
640 }
641
642 expected_pa = QH(control_full)->next & LINK_POINTER_ADDRESS_MASK;
643 real_pa = addr_to_phys(QH(bulk_full));
644 if (expected_pa != real_pa ) {
645 usb_log_debug("Bulk QH: %p vs. %p.",
646 (void *) expected_pa, (void *) real_pa);
647 }
648 async_usleep(UHCI_DEBUGER_TIMEOUT);
649 }
650 return EOK;
651#undef QH
652}
653/**
654 * @}
655 */
Note: See TracBrowser for help on using the repository browser.