Example #1
0
static int usb_generic_hid_get_event(ddf_fun_t *fun, uint8_t *buffer,
    size_t size, size_t *act_size, int *event_nr, unsigned int flags)
{
	usb_log_debug2("Generic HID: Get event.\n");

	if (fun == NULL || fun->driver_data == NULL || buffer == NULL
	    || act_size == NULL || event_nr == NULL) {
		usb_log_debug("No function");
		return EINVAL;
	}

	const usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)fun->driver_data;

	if (hid_dev->input_report_size > size) {
		usb_log_debug("input_report_size > size (%zu, %zu)\n",
		    hid_dev->input_report_size, size);
		return EINVAL;	// TODO: other error code
	}

	/*! @todo This should probably be somehow atomic. */
	memcpy(buffer, hid_dev->input_report,
	    hid_dev->input_report_size);
	*act_size = hid_dev->input_report_size;
	*event_nr = usb_hid_report_number(hid_dev);

	usb_log_debug2("OK\n");

	return EOK;
}
Example #2
0
static size_t usb_generic_hid_get_event_length(ddf_fun_t *fun)
{
	usb_log_debug2("Generic HID: Get event length (fun: %p, "
	    "fun->driver_data: %p.\n", fun, fun->driver_data);

	if (fun == NULL || fun->driver_data == NULL) {
		return 0;
	}

	const usb_hid_dev_t *hid_dev = fun->driver_data;

	usb_log_debug2("hid_dev: %p, Max input report size (%zu).\n",
	    hid_dev, hid_dev->max_input_report_size);

	return hid_dev->max_input_report_size;
}
Example #3
0
/** Add endpoint to the list and queue.
 *
 * @param[in] instance List to use.
 * @param[in] endpoint Endpoint to add.
 *
 * The endpoint is added to the end of the list and queue.
 */
void endpoint_list_add_ep(endpoint_list_t *instance, ohci_endpoint_t *ep)
{
	assert(instance);
	assert(ep);
	usb_log_debug2("Queue %s: Adding endpoint(%p).\n", instance->name, ep);

	fibril_mutex_lock(&instance->guard);

	ed_t *last_ed = NULL;
	/* Add to the hardware queue. */
	if (list_empty(&instance->endpoint_list)) {
		/* There are no active EDs */
		last_ed = instance->list_head;
	} else {
		/* There are active EDs, get the last one */
		ohci_endpoint_t *last = list_get_instance(
		    list_last(&instance->endpoint_list), ohci_endpoint_t, link);
		last_ed = last->ed;
	}
	/* Keep link */
	ep->ed->next = last_ed->next;
	/* Make sure ED is written to the memory */
	write_barrier();

	/* Add ed to the hw queue */
	ed_append_ed(last_ed, ep->ed);
	/* Make sure ED is updated */
	write_barrier();

	/* Add to the sw list */
	list_append(&ep->link, &instance->endpoint_list);

	ohci_endpoint_t *first = list_get_instance(
	    list_first(&instance->endpoint_list), ohci_endpoint_t, link);
	usb_log_debug("HCD EP(%p) added to list %s, first is %p(%p).\n",
		ep, instance->name, first, first->ed);
	if (last_ed == instance->list_head) {
		usb_log_debug2("%s head ED(%p-0x%0" PRIx32 "): %x:%x:%x:%x.\n",
		    instance->name, last_ed, instance->list_head_pa,
		    last_ed->status, last_ed->td_tail, last_ed->td_head,
		    last_ed->next);
	}
	fibril_mutex_unlock(&instance->guard);
}
Example #4
0
static void toggle_reset_callback(int retval, void *arg)
{
	assert(arg);
	toggle_t *toggle = arg;
	if (retval == EOK) {
		usb_log_debug2("Reseting toggle on %d:%d.\n",
		    toggle->target.address, toggle->target.endpoint);
		usb_bus_reset_toggle(&toggle->hcd->bus,
		    toggle->target, toggle->target.endpoint == 0);
	}

	toggle->original_callback(retval, toggle->original_data);
}
Example #5
0
static size_t usb_generic_get_report_descriptor_length(ddf_fun_t *fun)
{
	usb_log_debug("Generic HID: Get report descriptor length.\n");

	if (fun == NULL || fun->driver_data == NULL) {
		usb_log_debug("No function");
		return EINVAL;
	}

	const usb_hid_dev_t *hid_dev = fun->driver_data;

	usb_log_debug2("hid_dev->report_desc_size = %zu\n",
	    hid_dev->report_desc_size);

	return hid_dev->report_desc_size;
}
Example #6
0
void usb_generic_hid_deinit(usb_hid_dev_t *hid_dev, void *data)
{
	ddf_fun_t *fun = data;
	if (fun == NULL)
		return;

	if (ddf_fun_unbind(fun) != EOK) {
		usb_log_error("Failed to unbind generic hid fun.\n");
		return;
	}
	usb_log_debug2("%s unbound.\n", fun->name);
	/* We did not allocate this, so leave this alone
	 * the device would take care of it */
	fun->driver_data = NULL;
	ddf_fun_destroy(fun);
}
Example #7
0
/** Initialize a new ddf driver instance for uhci hc and hub.
 *
 * @param[in] device DDF instance of the device to initialize.
 * @return Error code.
 */
int uhci_dev_add(ddf_dev_t *device)
{
	usb_log_debug2("uhci_dev_add() called\n");
	assert(device);

	const int ret = device_setup_uhci(device);
	if (ret != EOK) {
		usb_log_error("Failed to initialize UHCI driver: %s.\n",
		    str_error(ret));
	} else {
		usb_log_info("Controlling new UHCI device '%s'.\n",
		    ddf_dev_get_name(device));
	}

	return ret;
}
Example #8
0
/** Destroy pipes previously created by usb_device_create_pipes.
 *
 * @param[in] usb_dev USB device.
 *
 */
void usb_device_destroy_pipes(usb_device_t *usb_dev)
{
	assert(usb_dev);
	assert(usb_dev->pipes || usb_dev->pipes_count == 0);
	
	/* Destroy the pipes. */
	for (size_t i = 0; i < usb_dev->pipes_count; ++i) {
		usb_log_debug2("Unregistering pipe %zu: %spresent.\n",
		    i, usb_dev->pipes[i].present ? "" : "not ");
		if (usb_dev->pipes[i].present)
			usb_pipe_unregister(&usb_dev->pipes[i].pipe);
	}
	
	free(usb_dev->pipes);
	usb_dev->pipes = NULL;
	usb_dev->pipes_count = 0;
}
Example #9
0
/** Polling function, emulates interrupts.
 *
 * @param[in] arg UHCI hc structure to use.
 * @return EOK (should never return)
 */
int hc_interrupt_emulator(void* arg)
{
	usb_log_debug("Started interrupt emulator.\n");
	hc_t *instance = arg;
	assert(instance);

	while (1) {
		/* Read and clear status register */
		uint16_t status = pio_read_16(&instance->registers->usbsts);
		pio_write_16(&instance->registers->usbsts, status);
		if (status != 0)
			usb_log_debug2("UHCI status: %x.\n", status);
		hc_interrupt(instance, status);
		async_usleep(UHCI_INT_EMULATOR_TIMEOUT);
	}
	return EOK;
}
Example #10
0
/** Initialize transfer list structures.
 *
 * @param[in] instance Memory place to use.
 * @param[in] name Name of the new list.
 * @return Error code
 *
 * Allocates memory for internal ed_t structure.
 */
int endpoint_list_init(endpoint_list_t *instance, const char *name)
{
	assert(instance);
	instance->name = name;
	instance->list_head = malloc32(sizeof(ed_t));
	if (!instance->list_head) {
		usb_log_error("Failed to allocate list head.\n");
		return ENOMEM;
	}
	instance->list_head_pa = addr_to_phys(instance->list_head);
	usb_log_debug2("Transfer list %s setup with ED: %p(0x%0" PRIx32 ")).\n",
	    name, instance->list_head, instance->list_head_pa);

	ed_init(instance->list_head, NULL, NULL);
	list_initialize(&instance->endpoint_list);
	fibril_mutex_initialize(&instance->guard);
	return EOK;
}
Example #11
0
/**
 * Callback for removing a device from the driver.
 *
 * @param dev Structure representing the device.
 * @return Error code.
 */
static int usb_hid_device_gone(usb_device_t *dev)
{
	assert(dev);
	usb_hid_dev_t *hid_dev = usb_device_data_get(dev);
	assert(hid_dev);
	unsigned tries = 100;
	/* Wait for fail. */
	while (hid_dev->running && tries--) {
		async_usleep(100000);
	}
	if (hid_dev->running) {
		usb_log_error("Can't remove hid, still running.\n");
		return EBUSY;
	}

	usb_hid_deinit(hid_dev);
	usb_log_debug2("%s destruction complete.\n", usb_device_get_name(dev));
	return EOK;
}
Example #12
0
/** Initialize a new ddf driver instance of UHCI root hub.
 *
 * @param[in] device DDF instance of the device to initialize.
 * @return Error code.
 */
static int uhci_rh_dev_add(ddf_dev_t *device)
{
	if (!device)
		return EINVAL;

	usb_log_debug2("uhci_rh_dev_add(handle=%" PRIun ")\n",
	    device->handle);

	uintptr_t io_regs = 0;
	size_t io_size = 0;
	uhci_root_hub_t *rh = NULL;
	int ret = EOK;

#define CHECK_RET_FREE_RH_RETURN(ret, message...) \
if (ret != EOK) { \
	usb_log_error(message); \
	if (rh) \
		free(rh); \
	return ret; \
} else (void)0

	ret = hc_get_my_registers(device, &io_regs, &io_size);
	CHECK_RET_FREE_RH_RETURN(ret,
	    "Failed to get registers from HC: %s.\n", str_error(ret));
	usb_log_debug("I/O regs at %p (size %zuB).\n",
	    (void *) io_regs, io_size);

	rh = malloc(sizeof(uhci_root_hub_t));
	ret = (rh == NULL) ? ENOMEM : EOK;
	CHECK_RET_FREE_RH_RETURN(ret,
	    "Failed to allocate rh driver instance.\n");

	ret = uhci_root_hub_init(rh, (void*)io_regs, io_size, device);
	CHECK_RET_FREE_RH_RETURN(ret,
	    "Failed(%d) to initialize rh driver instance: %s.\n",
	    ret, str_error(ret));

	device->driver_data = rh;
	usb_log_info("Controlling root hub '%s' (%" PRIun ").\n",
	    device->name, device->handle);
	return EOK;
}
Example #13
0
static int usb_generic_get_report_descriptor(ddf_fun_t *fun, uint8_t *desc,
    size_t size, size_t *actual_size)
{
	usb_log_debug2("Generic HID: Get report descriptor.\n");

	if (fun == NULL || fun->driver_data == NULL) {
		usb_log_debug("No function");
		return EINVAL;
	}

	const usb_hid_dev_t *hid_dev = fun->driver_data;

	if (hid_dev->report_desc_size > size) {
		return EINVAL;
	}

	memcpy(desc, hid_dev->report_desc, hid_dev->report_desc_size);
	*actual_size = hid_dev->report_desc_size;

	return EOK;
}
Example #14
0
/** Remove endpoint from the list and queue.
 *
 * @param[in] instance List to use.
 * @param[in] endpoint Endpoint to remove.
 */
void endpoint_list_remove_ep(endpoint_list_t *instance, ohci_endpoint_t *ep)
{
	assert(instance);
	assert(instance->list_head);
	assert(ep);
	assert(ep->ed);

	fibril_mutex_lock(&instance->guard);

	usb_log_debug2("Queue %s: removing endpoint(%p).\n", instance->name, ep);

	const char *qpos = NULL;
	ed_t *prev_ed;
	/* Remove from the hardware queue */
	if (list_first(&instance->endpoint_list) == &ep->link) {
		/* I'm the first one here */
		prev_ed = instance->list_head;
		qpos = "FIRST";
	} else {
		ohci_endpoint_t *prev =
		    list_get_instance(ep->link.prev, ohci_endpoint_t, link);
		prev_ed = prev->ed;
		qpos = "NOT FIRST";
	}
	assert(ed_next(prev_ed) == addr_to_phys(ep->ed));
	prev_ed->next = ep->ed->next;
	/* Make sure ED is updated */
	write_barrier();

	usb_log_debug("HCD EP(%p) removed (%s) from %s, next %x.\n",
	    ep, qpos, instance->name, ep->ed->next);

	/* Remove from the endpoint list */
	list_remove(&ep->link);
	fibril_mutex_unlock(&instance->guard);
}
Example #15
0
/** Root Hub driver structure initialization.
 *
 * Reads info registers and prepares descriptors. Sets power mode.
 */
void rh_init(rh_t *instance, ohci_regs_t *regs)
{
	assert(instance);
	assert(regs);

	instance->registers = regs;
	instance->port_count = OHCI_RD(regs->rh_desc_a) & RHDA_NDS_MASK;
	usb_log_debug2("rh_desc_a: %x.\n", OHCI_RD(regs->rh_desc_a));
	if (instance->port_count > 15) {
		usb_log_warning("OHCI specification does not allow more than 15"
		    " ports. Max 15 ports will be used");
		instance->port_count = 15;
	}

	/* Don't forget the hub status bit and round up */
	instance->interrupt_mask_size = 1 + (instance->port_count / 8);
	instance->unfinished_interrupt_transfer = NULL;

#if defined OHCI_POWER_SWITCH_no
	usb_log_debug("OHCI rh: Set power mode to no power switching.\n");
	/* Set port power mode to no power-switching. (always on) */
	OHCI_SET(regs->rh_desc_a, RHDA_NPS_FLAG);

	/* Set to no over-current reporting */
	OHCI_SET(regs->rh_desc_a, RHDA_NOCP_FLAG);

#elif defined OHCI_POWER_SWITCH_ganged
	usb_log_debug("OHCI rh: Set power mode to ganged power switching.\n");
	/* Set port power mode to ganged power-switching. */
	OHCI_CLR(regs->rh_desc_a, RHDA_NPS_FLAG);
	OHCI_CLR(regs->rh_desc_a, RHDA_PSM_FLAG);

	/* Turn off power (hub driver will turn this back on)*/
	OHCI_WR(regs->rh_status, RHS_CLEAR_GLOBAL_POWER);

	/* Set to global over-current */
	OHCI_CLR(regs->rh_desc_a, RHDA_NOCP_FLAG);
	OHCI_CLR(regs->rh_desc_a, RHDA_OCPM_FLAG);
#else
	usb_log_debug("OHCI rh: Set power mode to per-port power switching.\n");
	/* Set port power mode to per port power-switching. */
	OHCI_CLR(regs->rh_desc_a, RHDA_NPS_FLAG);
	OHCI_SET(regs->rh_desc_a, RHDA_PSM_FLAG);

	/* Control all ports by global switch and turn them off */
	OHCI_CLR(regs->rh_desc_b, RHDB_PCC_MASK << RHDB_PCC_SHIFT);
	OHCI_WR(regs->rh_status, RHS_CLEAR_GLOBAL_POWER);

	/* Return control to per port state */
	OHCI_SET(regs->rh_desc_b, RHDB_PCC_MASK << RHDB_PCC_SHIFT);

	/* Set per port over-current */
	OHCI_CLR(regs->rh_desc_a, RHDA_NOCP_FLAG);
	OHCI_SET(regs->rh_desc_a, RHDA_OCPM_FLAG);
#endif

	fibril_mutex_initialize(&instance->guard);
	rh_init_descriptors(instance);

	usb_log_info("Root hub (%zu ports) initialized.\n",
	    instance->port_count);
}
Example #16
0
/** Prepare generic usb_transfer_batch and schedule it.
 * @param hcd Host controller driver.
 * @param fun DDF fun
 * @param target address and endpoint number.
 * @param setup_data Data to use in setup stage (Control communication type)
 * @param in Callback for device to host communication.
 * @param out Callback for host to device communication.
 * @param arg Callback parameter.
 * @param name Communication identifier (for nicer output).
 * @return Error code.
 */
int hcd_send_batch(
    hcd_t *hcd, usb_target_t target, usb_direction_t direction,
    void *data, size_t size, uint64_t setup_data,
    usbhc_iface_transfer_in_callback_t in,
    usbhc_iface_transfer_out_callback_t out, void *arg, const char* name)
{
	assert(hcd);

	endpoint_t *ep = usb_bus_find_ep(&hcd->bus,
	    target.address, target.endpoint, direction);
	if (ep == NULL) {
		usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
		    target.address, target.endpoint, name);
		return ENOENT;
	}

	usb_log_debug2("%s %d:%d %zu(%zu).\n",
	    name, target.address, target.endpoint, size, ep->max_packet_size);

	const size_t bw = bandwidth_count_usb11(
	    ep->speed, ep->transfer_type, size, ep->max_packet_size);
	/* Check if we have enough bandwidth reserved */
	if (ep->bandwidth < bw) {
		usb_log_error("Endpoint(%d:%d) %s needs %zu bw "
		    "but only %zu is reserved.\n",
		    ep->address, ep->endpoint, name, bw, ep->bandwidth);
		return ENOSPC;
	}
	if (!hcd->ops.schedule) {
		usb_log_error("HCD does not implement scheduler.\n");
		return ENOTSUP;
	}

	/* Check for commands that reset toggle bit */
	if (ep->transfer_type == USB_TRANSFER_CONTROL) {
		const int reset_toggle = usb_request_needs_toggle_reset(
		    (usb_device_request_setup_packet_t *) &setup_data);
		if (reset_toggle >= 0) {
			assert(out);
			toggle_t *toggle = malloc(sizeof(toggle_t));
			if (!toggle)
				return ENOMEM;
			toggle->target.address = target.address;
			toggle->target.endpoint = reset_toggle;
			toggle->original_callback = out;
			toggle->original_data = arg;
			toggle->hcd = hcd;

			arg = toggle;
			out = toggle_reset_callback;
		}
	}

	usb_transfer_batch_t *batch = usb_transfer_batch_create(
	    ep, data, size, setup_data, in, out, arg);
	if (!batch) {
		usb_log_error("Failed to create transfer batch.\n");
		return ENOMEM;
	}

	const int ret = hcd->ops.schedule(hcd, batch);
	if (ret != EOK)
		usb_transfer_batch_destroy(batch);

	/* Drop our own reference to ep. */
	endpoint_del_ref(ep);

	return ret;
}
Example #17
0
/**
 * Processes key events.
 *
 * @note This function was copied from AT keyboard driver and modified to suit
 *       USB keyboard.
 *
 * @note Lock keys are not sent to the console, as they are completely handled
 *       in the driver. It may, however, be required later that the driver
 *       sends also these keys to application (otherwise it cannot use those
 *       keys at all).
 *
 * @param hid_dev
 * @param multim_dev
 * @param type Type of the event (press / release). Recognized values:
 *             KEY_PRESS, KEY_RELEASE
 * @param key Key code of the key according to HID Usage Tables.
 */
static void usb_multimedia_push_ev(
    usb_multimedia_t *multim_dev, int type, unsigned int key)
{
	assert(multim_dev != NULL);

	const kbd_event_t ev = {
		.type = type,
		.key = key,
		.mods = 0,
		.c = 0,
	};

	usb_log_debug2(NAME " Sending key %d to the console\n", ev.key);
	if (multim_dev->console_sess == NULL) {
		usb_log_warning(
		    "Connection to console not ready, key discarded.\n");
		return;
	}

	async_exch_t *exch = async_exchange_begin(multim_dev->console_sess);
	if (exch != NULL) {
		async_msg_4(exch, KBDEV_EVENT, ev.type, ev.key, ev.mods, ev.c);
		async_exchange_end(exch);
	} else {
		usb_log_warning("Failed to send multimedia key.\n");
	}
}

int usb_multimedia_init(struct usb_hid_dev *hid_dev, void **data)
{
	if (hid_dev == NULL || hid_dev->usb_dev == NULL) {
		return EINVAL;
	}

	usb_log_debug(NAME " Initializing HID/multimedia structure...\n");

	/* Create the exposed function. */
	ddf_fun_t *fun = ddf_fun_create(
	    hid_dev->usb_dev->ddf_dev, fun_exposed, NAME);
	if (fun == NULL) {
		usb_log_error("Could not create DDF function node.\n");
		return ENOMEM;
	}

	ddf_fun_set_ops(fun, &multimedia_ops);

	usb_multimedia_t *multim_dev =
	    ddf_fun_data_alloc(fun, sizeof(usb_multimedia_t));
	if (multim_dev == NULL) {
		ddf_fun_destroy(fun);
		return ENOMEM;
	}

	multim_dev->console_sess = NULL;

	//todo Autorepeat?

	int rc = ddf_fun_bind(fun);
	if (rc != EOK) {
		usb_log_error("Could not bind DDF function: %s.\n",
		    str_error(rc));
		ddf_fun_destroy(fun);
		return rc;
	}

	usb_log_debug(NAME " function created (handle: %" PRIun ").\n",
	    ddf_fun_get_handle(fun));

	rc = ddf_fun_add_to_category(fun, "keyboard");
	if (rc != EOK) {
		usb_log_error(
		    "Could not add DDF function to category 'keyboard': %s.\n",
		    str_error(rc));
		if (ddf_fun_unbind(fun) != EOK) {
			usb_log_error("Failed to unbind %s, won't destroy.\n",
			    ddf_fun_get_name(fun));
		} else {
			ddf_fun_destroy(fun);
		}
		return rc;
	}

	/* Save the KBD device structure into the HID device structure. */
	*data = fun;

	usb_log_debug(NAME " HID/multimedia structure initialized.\n");
	return EOK;
}

void usb_multimedia_deinit(struct usb_hid_dev *hid_dev, void *data)
{
	ddf_fun_t *fun = data;

	usb_multimedia_t *multim_dev = ddf_fun_data_get(fun);

	/* Hangup session to the console */
	if (multim_dev->console_sess)
		async_hangup(multim_dev->console_sess);
	if (ddf_fun_unbind(fun) != EOK) {
		usb_log_error("Failed to unbind %s, won't destroy.\n",
		    ddf_fun_get_name(fun));
	} else {
		usb_log_debug2("%s unbound.\n", ddf_fun_get_name(fun));
		/* This frees multim_dev too as it was stored in
		 * fun->data */
		ddf_fun_destroy(fun);
	}
}
Example #18
0
/** Initialize a new ddf driver instance for uhci hc and hub.
 *
 * @param[in] device DDF instance of the device to initialize.
 * @return Error code.
 */
static int uhci_dev_add(ddf_dev_t *device)
{
    usb_log_debug2("uhci_dev_add() called\n");
    assert(device);
    return hcd_ddf_add_hc(device, &uhci_hc_driver);
}
Example #19
0
/** Debug function, checks consistency of memory structures.
 *
 * @param[in] arg UHCI structure to use.
 * @return EOK (should never return)
 */
int hc_debug_checker(void *arg)
{
	hc_t *instance = arg;
	assert(instance);

#define QH(queue) \
	instance->transfers_##queue.queue_head

	while (1) {
		const uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
		const uint16_t sts = pio_read_16(&instance->registers->usbsts);
		const uint16_t intr =
		    pio_read_16(&instance->registers->usbintr);

		if (((cmd & UHCI_CMD_RUN_STOP) != 1) || (sts != 0)) {
			usb_log_debug2("Command: %X Status: %X Intr: %x\n",
			    cmd, sts, intr);
		}

		const uintptr_t frame_list =
		    pio_read_32(&instance->registers->flbaseadd) & ~0xfff;
		if (frame_list != addr_to_phys(instance->frame_list)) {
			usb_log_debug("Framelist address: %p vs. %p.\n",
			    (void *) frame_list,
			    (void *) addr_to_phys(instance->frame_list));
		}

		int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;

		uintptr_t expected_pa = instance->frame_list[frnum]
		    & LINK_POINTER_ADDRESS_MASK;
		uintptr_t real_pa = addr_to_phys(QH(interrupt));
		if (expected_pa != real_pa) {
			usb_log_debug("Interrupt QH: %p (frame %d) vs. %p.\n",
			    (void *) expected_pa, frnum, (void *) real_pa);
		}

		expected_pa = QH(interrupt)->next & LINK_POINTER_ADDRESS_MASK;
		real_pa = addr_to_phys(QH(control_slow));
		if (expected_pa != real_pa) {
			usb_log_debug("Control Slow QH: %p vs. %p.\n",
			    (void *) expected_pa, (void *) real_pa);
		}

		expected_pa = QH(control_slow)->next & LINK_POINTER_ADDRESS_MASK;
		real_pa = addr_to_phys(QH(control_full));
		if (expected_pa != real_pa) {
			usb_log_debug("Control Full QH: %p vs. %p.\n",
			    (void *) expected_pa, (void *) real_pa);
		}

		expected_pa = QH(control_full)->next & LINK_POINTER_ADDRESS_MASK;
		real_pa = addr_to_phys(QH(bulk_full));
		if (expected_pa != real_pa ) {
			usb_log_debug("Bulk QH: %p vs. %p.\n",
			    (void *) expected_pa, (void *) real_pa);
		}
		async_usleep(UHCI_DEBUGER_TIMEOUT);
	}
	return EOK;
#undef QH
}