Пример #1
0
/** Initialize UHCI hc memory structures.
 *
 * @param[in] instance UHCI structure to use.
 * @return Error code
 * @note Should be called only once on any structure.
 *
 * Structures:
 *  - transfer lists (queue heads need to be accessible by the hw)
 *  - frame list page (needs to be one UHCI hw accessible 4K page)
 */
int hc_init_mem_structures(hc_t *instance)
{
	assert(instance);

	/* Init USB frame list page */
	instance->frame_list = get_page();
	if (!instance->frame_list) {
		return ENOMEM;
	}
	usb_log_debug("Initialized frame list at %p.\n", instance->frame_list);

	/* Init transfer lists */
	int ret = hc_init_transfer_lists(instance);
	if (ret != EOK) {
		usb_log_error("Failed to initialize transfer lists.\n");
		return_page(instance->frame_list);
		return ENOMEM;
	}
	usb_log_debug("Initialized transfer lists.\n");


	/* Set all frames to point to the first queue head */
	const uint32_t queue = LINK_POINTER_QH(
	        addr_to_phys(instance->transfers_interrupt.queue_head));

	for (unsigned i = 0; i < UHCI_FRAME_LIST_COUNT; ++i) {
		instance->frame_list[i] = queue;
	}

	return EOK;
}
Пример #2
0
/**
 * Initialize EHCI TD.
 * @param instance TD structure to initialize.
 * @param next Next TD in ED list.
 * @param direction Used to determine PID, BOTH means setup PID.
 * @param buffer Pointer to the first byte of transferred data.
 * @param size Size of the buffer.
 * @param toggle Toggle bit value, use 0/1 to set explicitly,
 *        any other value means that ED toggle will be used.
 */
void td_init(td_t *instance, const td_t *next,
    usb_direction_t direction, const void *buffer, size_t size, int toggle,
    bool ioc)
{
	assert(instance);
	memset(instance, 0, sizeof(td_t));
	/* Set PID and Total size */
	assert((size & TD_STATUS_TOTAL_MASK) == size);
	EHCI_MEM32_WR(instance->status,
	    ((dir[direction] & TD_STATUS_PID_MASK) << TD_STATUS_PID_SHIFT) |
	    ((size & TD_STATUS_TOTAL_MASK) << TD_STATUS_TOTAL_SHIFT) |
	    (ioc ? TD_STATUS_IOC_FLAG : 0) );

	if (toggle == 0 || toggle == 1) {
		EHCI_MEM32_SET(instance->status,
		    toggle ? TD_STATUS_TOGGLE_FLAG : 0);
	}

	if (buffer != NULL) {
		assert(size != 0);
		for (unsigned i = 0; (i < ARRAY_SIZE(instance->buffer_pointer))
		    && size; ++i) {
			const uintptr_t page =
			    (addr_to_phys(buffer) & TD_BUFFER_POINTER_MASK);
			const size_t offset =
			    ((uintptr_t)buffer & TD_BUFFER_POINTER_OFFSET_MASK);
			assert(offset == 0 || i == 0);
			size -= min((4096 - offset), size);
			buffer += min((4096 - offset), size);
			EHCI_MEM32_WR(instance->buffer_pointer[i],
			    page | offset);
		}
	}

	EHCI_MEM32_WR(instance->next, next ?
	    LINK_POINTER_TD(addr_to_phys(next)) : LINK_POINTER_TERM);

	EHCI_MEM32_WR(instance->alternate, LINK_POINTER_TERM);
	EHCI_MEM32_SET(instance->status, TD_STATUS_ACTIVE_FLAG);
	write_barrier();
}
Пример #3
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;
}
Пример #4
0
/**
 * Initialize ED.
 *
 * @param instance ED structure to initialize.
 * @param ep Driver endpoint to use.
 * @param td TD to put in the list.
 *
 * If @param ep is NULL, dummy ED is initialized with only skip flag set.
 */
void ed_init(ed_t *instance, const endpoint_t *ep, const td_t *td)
{
	assert(instance);
	memset(instance, 0, sizeof(*instance));

	if (ep == NULL) {
		/* Mark as dead, used for dummy EDs at the beginning of
		 * endpoint lists. */
		OHCI_MEM32_WR(instance->status, ED_STATUS_K_FLAG);
		return;
	}
	/* Non-dummy ED must have corresponding EP and TD assigned */
	assert(td);
	assert(ep);
	assert(ep->direction < ARRAY_SIZE(dir));

	/* Status: address, endpoint nr, direction mask and max packet size. */
	OHCI_MEM32_WR(instance->status,
	    ((ep->address & ED_STATUS_FA_MASK) << ED_STATUS_FA_SHIFT)
	    | ((ep->endpoint & ED_STATUS_EN_MASK) << ED_STATUS_EN_SHIFT)
	    | ((dir[ep->direction] & ED_STATUS_D_MASK) << ED_STATUS_D_SHIFT)
	    | ((ep->max_packet_size & ED_STATUS_MPS_MASK)
	        << ED_STATUS_MPS_SHIFT));

	/* Low speed flag */
	if (ep->speed == USB_SPEED_LOW)
		OHCI_MEM32_SET(instance->status, ED_STATUS_S_FLAG);

	/* Isochronous format flag */
	// TODO: We need iTD instead of TD for iso transfers
	if (ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)
		OHCI_MEM32_SET(instance->status, ED_STATUS_F_FLAG);

	/* Set TD to the list */
	const uintptr_t pa = addr_to_phys(td);
	OHCI_MEM32_WR(instance->td_head, pa & ED_TDHEAD_PTR_MASK);
	OHCI_MEM32_WR(instance->td_tail, pa & ED_TDTAIL_PTR_MASK);

	/* Set toggle bit */
	if (ep->toggle)
		OHCI_MEM32_SET(instance->td_head, ED_TDHEAD_TOGGLE_CARRY);

}
Пример #5
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);
}
Пример #6
0
/** Initialize UHCI hc hw resources.
 *
 * @param[in] instance UHCI structure to use.
 * For magic values see UHCI Design Guide
 */
void hc_init_hw(const hc_t *instance)
{
	assert(instance);
	uhci_regs_t *registers = instance->registers;

	/* Reset everything, who knows what touched it before us */
	pio_write_16(&registers->usbcmd, UHCI_CMD_GLOBAL_RESET);
	async_usleep(50000); /* 50ms according to USB spec(root hub reset) */
	pio_write_16(&registers->usbcmd, 0);

	/* Reset hc, all states and counters. Hope that hw is not broken */
	pio_write_16(&registers->usbcmd, UHCI_CMD_HCRESET);
	do { async_usleep(10); }
	while ((pio_read_16(&registers->usbcmd) & UHCI_CMD_HCRESET) != 0);

	/* Set frame to exactly 1ms */
	pio_write_8(&registers->sofmod, 64);

	/* Set frame list pointer */
	const uint32_t pa = addr_to_phys(instance->frame_list);
	pio_write_32(&registers->flbaseadd, pa);

	if (instance->hw_interrupts) {
		/* Enable all interrupts, but resume interrupt */
		pio_write_16(&instance->registers->usbintr,
		    UHCI_INTR_ALLOW_INTERRUPTS);
	}

	const uint16_t cmd = pio_read_16(&registers->usbcmd);
	if (cmd != 0)
		usb_log_warning("Previous command value: %x.\n", cmd);

	/* Start the hc with large(64B) packet FSBR */
	pio_write_16(&registers->usbcmd,
	    UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET | UHCI_CMD_CONFIGURE);
}
Пример #7
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
}