Example #1
0
File: ehci.c Project: MWDD/osdev
// ------------------------------------------------------------------------------------------------
static void ehci_dev_intr(USB_Device* dev, USB_Transfer* t)
{
    EHCI_Controller* hc = (EHCI_Controller*)dev->hc;

    // Determine transfer properties
    uint speed = dev->speed;
    uint addr = dev->addr;
    uint max_size = dev->max_packet_size;
    uint endp = dev->endp.desc.addr & 0xf;

    // Create queue of transfer descriptors
    EHCI_TD* td = ehci_alloc_td(hc);
    if (!td)
    {
        t->success = false;
        t->complete = true;
        return;
    }

    EHCI_TD* head = td;
    EHCI_TD* prev = 0;

    // Data in/out packets
    uint toggle = dev->endp.toggle;
    uint packet_type = USB_PACKET_IN;
    uint packet_size = t->len;

    ehci_td_init(td, prev, toggle, packet_type, packet_size, t->data);

    // Initialize queue head
    EHCI_QH* qh = ehci_alloc_qh(hc);
    ehci_qh_init(qh, t, head, dev->parent, true, speed, addr, endp, max_size);

    // Schedule queue
    ehci_insert_qh(hc, qh);
}
Example #2
0
File: ehci.c Project: MWDD/osdev
// ------------------------------------------------------------------------------------------------
void ehci_init(uint id, PCI_DeviceInfo* info)
{
    if (!(((info->class_code << 8) | info->subclass) == PCI_SERIAL_USB &&
        info->prog_intf == PCI_SERIAL_USB_EHCI))
    {
        return;
    }

    if (sizeof(EHCI_QH) != 128)
    {
        console_print("Unexpected EHCI_QH size: %d\n", sizeof(EHCI_QH));
        return;
    }

    console_print ("Initializing EHCI\n");

    // Base I/O Address
    u32 bar0 = pci_in32(id, PCI_CONFIG_BAR0);
    if (bar0 & 0x1)
    {
        // Only Memory Mapped I/O supported
        return;
    }
    if (bar0 & 0x4)
    {
        // TODO - support 64-bit pointer
        return;
    }

    bar0 &= ~0xf;    // clear low 4 bits

    // Controller initialization
    EHCI_Controller* hc = vm_alloc(sizeof(EHCI_Controller));
    hc->cap_regs = (EHCI_Cap_Regs*)(uintptr_t)bar0;
    hc->op_regs = (EHCI_Op_Regs*)(uintptr_t)(bar0 + hc->cap_regs->cap_length);
    hc->qh_pool = (EHCI_QH*)vm_alloc(sizeof(EHCI_QH) * MAX_QH);
    hc->td_pool = (EHCI_TD*)vm_alloc(sizeof(EHCI_TD) * MAX_TD);

    memset(hc->qh_pool, 0, sizeof(EHCI_QH) * MAX_QH);
    memset(hc->td_pool, 0, sizeof(EHCI_TD) * MAX_TD);

    // Asynchronous queue setup
    EHCI_QH* qh = ehci_alloc_qh(hc);
    qh->qhlp = (u32)(uintptr_t)qh | PTR_QH;
    qh->ch = QH_CH_H;
    qh->caps = (1 << QH_CAP_MULT_SHIFT);
    qh->cur_link = 0;
    qh->next_link = PTR_TERMINATE;
    qh->alt_link = 0;
    qh->token = 0;
    for (uint i = 0; i < 5; ++i)
    {
        qh->buffer[i] = 0;
        qh->ext_buffer[i] = 0;
    }
    qh->transfer = 0;
    qh->qh_link.prev = &qh->qh_link;
    qh->qh_link.next = &qh->qh_link;

    hc->async_qh = qh;

    // Check extended capabilities
    uint eecp = (hc->cap_regs->hcc_params & HCCPARAMS_EECP_MASK) >> HCCPARAMS_EECP_SHIFT;
    if (eecp >= 0x40)
    {
        // Disable BIOS legacy support
        uint legsup = pci_in32(id, eecp + USBLEGSUP);

        if (legsup & USBLEGSUP_HC_BIOS)
        {
            pci_out32(id, eecp + USBLEGSUP, legsup | USBLEGSUP_HC_OS);
            for (;;)
            {
                legsup = pci_in32(id, eecp + USBLEGSUP);
                if (~legsup & USBLEGSUP_HC_BIOS && legsup & USBLEGSUP_HC_OS)
                {
                    break;
                }
            }
        }
    }

    // Disable interrupts
    hc->op_regs->usb_intr = 0;

    // Setup frame list
    hc->op_regs->frame_index = 0;
    hc->op_regs->periodic_list_base = (u32)(uintptr_t)0;
    hc->op_regs->async_list_addr = (u32)(uintptr_t)hc->async_qh;
    hc->op_regs->ctrl_ds_segment = 0;

    // Clear status
    hc->op_regs->usb_sts = 0x3f;

    // Enable controller
    hc->op_regs->usb_cmd = (8 << CMD_ITC_SHIFT) | CMD_ASE | CMD_RS;
    while (hc->op_regs->usb_sts & STS_HCHALTED) // TODO - remove after dynamic port detection
        ;

    // Configure all devices to be managed by the EHCI
    hc->op_regs->config_flag = 1;
    pit_wait(5);    // TODO - remove after dynamic port detection

    // Probe devices
    ehci_probe(hc);

    // Register controller
    USB_Controller* controller = (USB_Controller*)vm_alloc(sizeof(USB_Controller));
    controller->next = g_usb_controller_list;
    controller->hc = hc;
    controller->poll = ehci_controller_poll;

    g_usb_controller_list = controller;
}
Example #3
0
File: ehci.c Project: MWDD/osdev
// ------------------------------------------------------------------------------------------------
static void ehci_dev_control(USB_Device* dev, USB_Transfer* t)
{
    EHCI_Controller* hc = (EHCI_Controller*)dev->hc;
    USB_DevReq* req = t->req;

    // Determine transfer properties
    uint speed = dev->speed;
    uint addr = dev->addr;
    uint max_size = dev->max_packet_size;
    uint type = req->type;
    uint len = req->len;

    // Create queue of transfer descriptors
    EHCI_TD* td = ehci_alloc_td(hc);
    if (!td)
    {
        return;
    }

    EHCI_TD* head = td;
    EHCI_TD* prev = 0;

    // Setup packet
    uint toggle = 0;
    uint packet_type = USB_PACKET_SETUP;
    uint packet_size = sizeof(USB_DevReq);
    ehci_td_init(td, prev, toggle, packet_type, packet_size, req);
    prev = td;

    // Data in/out packets
    packet_type = type & RT_DEV_TO_HOST ? USB_PACKET_IN : USB_PACKET_OUT;

    u8* it = (u8*)t->data;
    u8* end = it + len;
    while (it < end)
    {
        td = ehci_alloc_td(hc);
        if (!td)
        {
            return;
        }

        toggle ^= 1;
        packet_size = end - it;
        if (packet_size > max_size)
        {
            packet_size = max_size;
        }

        ehci_td_init(td, prev, toggle, packet_type, packet_size, it);

        it += packet_size;
        prev = td;
    }

    // Status packet
    td = ehci_alloc_td(hc);
    if (!td)
    {
        return;
    }

    toggle = 1;
    packet_type = type & RT_DEV_TO_HOST ? USB_PACKET_OUT : USB_PACKET_IN;
    ehci_td_init(td, prev, toggle, packet_type, 0, 0);

    // Initialize queue head
    EHCI_QH* qh = ehci_alloc_qh(hc);
    ehci_qh_init(qh, t, head, dev->parent, false, speed, addr, 0, max_size);

    // Wait until queue has been processed
    ehci_insert_qh(hc, qh);
    ehci_qh_wait(hc, qh);
}
Example #4
0
/*
 * ehci_hcdi_pipe_open:
 *
 * Member of HCD Ops structure and called during client specific pipe open
 * Add the pipe to the data structure representing the device and allocate
 * bandwidth for the pipe if it is a interrupt or isochronous endpoint.
 */
int
ehci_hcdi_pipe_open(
	usba_pipe_handle_data_t	*ph,
	usb_flags_t		flags)
{
	ehci_state_t		*ehcip = ehci_obtain_state(
				    ph->p_usba_device->usb_root_hub_dip);
	usb_ep_descr_t		*epdt = &ph->p_ep;
	int			rval, error = USB_SUCCESS;
	int			kmflag = (flags & USB_FLAGS_SLEEP) ?
				KM_SLEEP : KM_NOSLEEP;
	uchar_t			smask = 0;
	uchar_t			cmask = 0;
	uint_t			pnode = 0;
	ehci_pipe_private_t	*pp;

	USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
	    "ehci_hcdi_pipe_open: addr = 0x%x, ep%d",
	    ph->p_usba_device->usb_addr,
	    epdt->bEndpointAddress & USB_EP_NUM_MASK);

	mutex_enter(&ehcip->ehci_int_mutex);
	rval = ehci_state_is_operational(ehcip);
	mutex_exit(&ehcip->ehci_int_mutex);

	if (rval != USB_SUCCESS) {

		return (rval);
	}

	/*
	 * Check and handle root hub pipe open.
	 */
	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {

		mutex_enter(&ehcip->ehci_int_mutex);
		error = ehci_handle_root_hub_pipe_open(ph, flags);
		mutex_exit(&ehcip->ehci_int_mutex);

		return (error);
	}

	/*
	 * Opening of other pipes excluding root hub pipe are
	 * handled below. Check whether pipe is already opened.
	 */
	if (ph->p_hcd_private) {
		USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
		    "ehci_hcdi_pipe_open: Pipe is already opened");

		return (USB_FAILURE);
	}

	/*
	 * A portion of the bandwidth is reserved for the non-periodic
	 * transfers, i.e control and bulk transfers in each of one
	 * millisecond frame period & usually it will be 20% of frame
	 * period. Hence there is no need to check for the available
	 * bandwidth before adding the control or bulk endpoints.
	 *
	 * There is a need to check for the available bandwidth before
	 * adding the periodic transfers, i.e interrupt & isochronous,
	 * since all these periodic transfers are guaranteed transfers.
	 * Usually 80% of the total frame time is reserved for periodic
	 * transfers.
	 */
	if (EHCI_PERIODIC_ENDPOINT(epdt)) {

		mutex_enter(&ehcip->ehci_int_mutex);
		mutex_enter(&ph->p_mutex);

		error = ehci_allocate_bandwidth(ehcip,
		    ph, &pnode, &smask, &cmask);

		if (error != USB_SUCCESS) {

			USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
			    "ehci_hcdi_pipe_open: Bandwidth allocation failed");

			mutex_exit(&ph->p_mutex);
			mutex_exit(&ehcip->ehci_int_mutex);

			return (error);
		}

		mutex_exit(&ph->p_mutex);
		mutex_exit(&ehcip->ehci_int_mutex);
	}

	/* Create the HCD pipe private structure */
	pp = kmem_zalloc(sizeof (ehci_pipe_private_t), kmflag);

	/*
	 * Return failure if ehci pipe private
	 * structure allocation fails.
	 */
	if (pp == NULL) {

		mutex_enter(&ehcip->ehci_int_mutex);

		/* Deallocate bandwidth */
		if (EHCI_PERIODIC_ENDPOINT(epdt)) {

			mutex_enter(&ph->p_mutex);
			ehci_deallocate_bandwidth(ehcip,
			    ph, pnode, smask, cmask);
			mutex_exit(&ph->p_mutex);
		}

		mutex_exit(&ehcip->ehci_int_mutex);

		return (USB_NO_RESOURCES);
	}

	mutex_enter(&ehcip->ehci_int_mutex);

	/* Save periodic nodes */
	pp->pp_pnode = pnode;

	/* Save start and complete split mask values */
	pp->pp_smask = smask;
	pp->pp_cmask = cmask;

	/* Create prototype for xfer completion condition variable */
	cv_init(&pp->pp_xfer_cmpl_cv, NULL, CV_DRIVER, NULL);

	/* Set the state of pipe as idle */
	pp->pp_state = EHCI_PIPE_STATE_IDLE;

	/* Store a pointer to the pipe handle */
	pp->pp_pipe_handle = ph;

	mutex_enter(&ph->p_mutex);

	/* Store the pointer in the pipe handle */
	ph->p_hcd_private = (usb_opaque_t)pp;

	/* Store a copy of the pipe policy */
	bcopy(&ph->p_policy, &pp->pp_policy, sizeof (usb_pipe_policy_t));

	mutex_exit(&ph->p_mutex);

	/* Allocate the host controller endpoint descriptor */
	pp->pp_qh = ehci_alloc_qh(ehcip, ph, NULL);

	/* Initialize the halting flag */
	pp->pp_halt_state = EHCI_HALT_STATE_FREE;

	/* Create prototype for halt completion condition variable */
	cv_init(&pp->pp_halt_cmpl_cv, NULL, CV_DRIVER, NULL);

	/* Isoch does not use QH, so ignore this */
	if ((pp->pp_qh == NULL) && !(EHCI_ISOC_ENDPOINT(epdt))) {
		ASSERT(pp->pp_qh == NULL);

		USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
		    "ehci_hcdi_pipe_open: QH allocation failed");

		mutex_enter(&ph->p_mutex);

		/* Deallocate bandwidth */
		if (EHCI_PERIODIC_ENDPOINT(epdt)) {

			ehci_deallocate_bandwidth(ehcip,
			    ph, pnode, smask, cmask);
		}

		/* Destroy the xfer completion condition variable */
		cv_destroy(&pp->pp_xfer_cmpl_cv);

		/*
		 * Deallocate the hcd private portion
		 * of the pipe handle.
		 */
		kmem_free(ph->p_hcd_private, sizeof (ehci_pipe_private_t));

		/*
		 * Set the private structure in the
		 * pipe handle equal to NULL.
		 */
		ph->p_hcd_private = NULL;

		mutex_exit(&ph->p_mutex);
		mutex_exit(&ehcip->ehci_int_mutex);

		return (USB_NO_RESOURCES);
	}

	/*
	 * Isoch does not use QH so no need to
	 * restore data toggle or insert QH
	 */
	if (!(EHCI_ISOC_ENDPOINT(epdt))) {
		/* Restore the data toggle information */
		ehci_restore_data_toggle(ehcip, ph);
	}

	/*
	 * Insert the endpoint onto the host controller's
	 * appropriate endpoint list. The host controller
	 * will not schedule this endpoint and will not have
	 * any QTD's to process.  It will also update the pipe count.
	 */
	ehci_insert_qh(ehcip, ph);

	USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
	    "ehci_hcdi_pipe_open: ph = 0x%p", (void *)ph);

	ehcip->ehci_open_pipe_count++;

	mutex_exit(&ehcip->ehci_int_mutex);

	return (USB_SUCCESS);
}