// ------------------------------------------------------------------------------------------------ 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); }
// ------------------------------------------------------------------------------------------------ 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); }
/* * 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); }