#include <sys/usb/hcd/ehci/ehcid.h>
#include <sys/usb/hcd/ehci/ehci_intr.h>
#include <sys/usb/hcd/ehci/ehci_util.h>
#include <sys/usb/hcd/ehci/ehci_isoch.h>
extern int ehci_qh_pool_size;
extern int ehci_qtd_pool_size;
ehci_qh_t *ehci_alloc_qh(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
uint_t flag);
static void ehci_unpack_endpoint(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
ehci_qh_t *qh);
void ehci_insert_qh(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph);
static void ehci_insert_async_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
static void ehci_insert_intr_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
static void ehci_modify_qh_status_bit(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
halt_bit_t action);
static void ehci_halt_hs_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_qh_t *qh);
static void ehci_halt_fls_ctrl_and_bulk_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_qh_t *qh);
static void ehci_clear_tt_buffer(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
ehci_qh_t *qh);
static void ehci_halt_fls_intr_qh(
ehci_state_t *ehcip,
ehci_qh_t *qh);
void ehci_remove_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
boolean_t reclaim);
static void ehci_remove_async_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
boolean_t reclaim);
static void ehci_remove_intr_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
boolean_t reclaim);
static void ehci_insert_qh_on_reclaim_list(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
void ehci_deallocate_qh(
ehci_state_t *ehcip,
ehci_qh_t *old_qh);
uint32_t ehci_qh_cpu_to_iommu(
ehci_state_t *ehcip,
ehci_qh_t *addr);
ehci_qh_t *ehci_qh_iommu_to_cpu(
ehci_state_t *ehcip,
uintptr_t addr);
static int ehci_initialize_dummy(
ehci_state_t *ehcip,
ehci_qh_t *qh);
ehci_trans_wrapper_t *ehci_allocate_ctrl_resources(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags);
void ehci_insert_ctrl_req(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp,
ehci_trans_wrapper_t *tw,
usb_flags_t usb_flags);
ehci_trans_wrapper_t *ehci_allocate_bulk_resources(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags);
void ehci_insert_bulk_req(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_bulk_req_t *bulk_reqp,
ehci_trans_wrapper_t *tw,
usb_flags_t flags);
int ehci_start_periodic_pipe_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_opaque_t periodic_in_reqp,
usb_flags_t flags);
static int ehci_start_pipe_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags);
static int ehci_start_intr_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags);
static void ehci_set_periodic_pipe_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph);
ehci_trans_wrapper_t *ehci_allocate_intr_resources(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_intr_req_t *intr_reqp,
usb_flags_t usb_flags);
void ehci_insert_intr_req(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
usb_flags_t flags);
int ehci_stop_periodic_pipe_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags);
int ehci_insert_qtd(
ehci_state_t *ehcip,
uint32_t qtd_ctrl,
size_t qtd_dma_offs,
size_t qtd_length,
uint32_t qtd_ctrl_phase,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw);
static ehci_qtd_t *ehci_allocate_qtd_from_pool(
ehci_state_t *ehcip);
static void ehci_fill_in_qtd(
ehci_state_t *ehcip,
ehci_qtd_t *qtd,
uint32_t qtd_ctrl,
size_t qtd_dma_offs,
size_t qtd_length,
uint32_t qtd_ctrl_phase,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw);
static void ehci_insert_qtd_on_tw(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd);
static void ehci_insert_qtd_into_active_qtd_list(
ehci_state_t *ehcip,
ehci_qtd_t *curr_qtd);
void ehci_remove_qtd_from_active_qtd_list(
ehci_state_t *ehcip,
ehci_qtd_t *curr_qtd);
static void ehci_traverse_qtds(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph);
void ehci_deallocate_qtd(
ehci_state_t *ehcip,
ehci_qtd_t *old_qtd);
uint32_t ehci_qtd_cpu_to_iommu(
ehci_state_t *ehcip,
ehci_qtd_t *addr);
ehci_qtd_t *ehci_qtd_iommu_to_cpu(
ehci_state_t *ehcip,
uintptr_t addr);
static ehci_trans_wrapper_t *ehci_create_transfer_wrapper(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
size_t length,
uint_t usb_flags);
int ehci_allocate_tds_for_tw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
size_t qtd_count);
static ehci_trans_wrapper_t *ehci_allocate_tw_resources(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
size_t length,
usb_flags_t usb_flags,
size_t td_count);
static void ehci_free_tw_td_resources(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw);
static void ehci_start_xfer_timer(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw);
void ehci_stop_xfer_timer(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw,
uint_t flag);
static void ehci_xfer_timeout_handler(void *arg);
static void ehci_remove_tw_from_timeout_list(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw);
static void ehci_start_timer(ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
void ehci_deallocate_tw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw);
void ehci_free_dma_resources(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph);
static void ehci_free_tw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw);
int ehci_allocate_intr_in_resource(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
usb_flags_t flags);
void ehci_pipe_cleanup(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph);
static void ehci_wait_for_transfers_completion(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
void ehci_check_for_transfers_completion(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
static void ehci_save_data_toggle(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph);
void ehci_restore_data_toggle(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph);
void ehci_handle_outstanding_requests(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
void ehci_deallocate_intr_in_resource(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw);
void ehci_do_client_periodic_in_req_callback(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
usb_cr_t completion_reason);
void ehci_hcdi_callback(
usba_pipe_handle_data_t *ph,
ehci_trans_wrapper_t *tw,
usb_cr_t completion_reason);
ehci_qh_t *
ehci_alloc_qh(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
uint_t flag)
{
int i, state;
ehci_qh_t *qh;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_alloc_qh: ph = 0x%p flag = 0x%x", (void *)ph, flag);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (ph) {
if (EHCI_ISOC_ENDPOINT((&ph->p_ep))) {
return (NULL);
}
}
for (i = EHCI_NUM_STATIC_NODES; i < ehci_qh_pool_size; i ++) {
state = Get_QH(ehcip->ehci_qh_pool_addr[i].qh_state);
if (state == EHCI_QH_FREE) {
break;
}
}
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_alloc_qh: Allocated %d", i);
if (i == ehci_qh_pool_size) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_alloc_qh: QH exhausted");
return (NULL);
} else {
qh = &ehcip->ehci_qh_pool_addr[i];
bzero((void *)qh, sizeof (ehci_qh_t));
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_alloc_qh: Allocated address 0x%p", (void *)qh);
if (flag == EHCI_POLLED_MODE_FLAG) {
Set_QH(qh->qh_link_ptr, EHCI_QH_LINK_PTR_VALID);
Set_QH(qh->qh_ctrl, EHCI_QH_CTRL_ED_INACTIVATE);
}
if (ph) {
if ((ehci_initialize_dummy(ehcip,
qh)) == USB_NO_RESOURCES) {
Set_QH(qh->qh_state, EHCI_QH_FREE);
return (NULL);
}
ehci_unpack_endpoint(ehcip, ph, qh);
Set_QH(qh->qh_curr_qtd, 0);
Set_QH(qh->qh_alt_next_qtd,
EHCI_QH_ALT_NEXT_QTD_PTR_VALID);
Set_QH(qh->qh_state, EHCI_QH_ACTIVE);
} else {
Set_QH(qh->qh_status, EHCI_QH_STS_HALTED);
Set_QH(qh->qh_state, EHCI_QH_STATIC);
}
ehci_print_qh(ehcip, qh);
return (qh);
}
}
static void
ehci_unpack_endpoint(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
ehci_qh_t *qh)
{
usb_ep_descr_t *endpoint = &ph->p_ep;
uint_t maxpacketsize, addr, xactions;
uint_t ctrl = 0, status = 0, split_ctrl = 0;
usb_port_status_t usb_port_status;
usba_device_t *usba_device = ph->p_usba_device;
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_unpack_endpoint:");
mutex_enter(&usba_device->usb_mutex);
ctrl = usba_device->usb_addr;
usb_port_status = usba_device->usb_port_status;
mutex_exit(&usba_device->usb_mutex);
addr = endpoint->bEndpointAddress;
ctrl |= ((addr & USB_EP_NUM_MASK) << EHCI_QH_CTRL_ED_NUMBER_SHIFT);
switch (usb_port_status) {
case USBA_LOW_SPEED_DEV:
ctrl |= EHCI_QH_CTRL_ED_LOW_SPEED;
break;
case USBA_FULL_SPEED_DEV:
ctrl |= EHCI_QH_CTRL_ED_FULL_SPEED;
break;
case USBA_HIGH_SPEED_DEV:
ctrl |= EHCI_QH_CTRL_ED_HIGH_SPEED;
break;
}
switch (endpoint->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
ctrl |= EHCI_QH_CTRL_DATA_TOGGLE;
if (usb_port_status != USBA_HIGH_SPEED_DEV) {
ctrl |= EHCI_QH_CTRL_CONTROL_ED_FLAG;
}
case USB_EP_ATTR_BULK:
ctrl |= EHCI_QH_CTRL_MAX_NC;
if (usb_port_status == USBA_HIGH_SPEED_DEV) {
status = EHCI_QH_STS_DO_PING;
}
break;
case USB_EP_ATTR_INTR:
split_ctrl = (pp->pp_smask & EHCI_QH_SPLIT_CTRL_INTR_MASK);
if (usb_port_status != USBA_HIGH_SPEED_DEV) {
split_ctrl |= ((pp->pp_cmask <<
EHCI_QH_SPLIT_CTRL_COMP_SHIFT) &
EHCI_QH_SPLIT_CTRL_COMP_MASK);
}
break;
}
xactions = (endpoint->wMaxPacketSize &
USB_EP_MAX_XACTS_MASK) >> USB_EP_MAX_XACTS_SHIFT;
switch (xactions) {
case 0:
split_ctrl |= EHCI_QH_SPLIT_CTRL_1_XACTS;
break;
case 1:
split_ctrl |= EHCI_QH_SPLIT_CTRL_2_XACTS;
break;
case 2:
split_ctrl |= EHCI_QH_SPLIT_CTRL_3_XACTS;
break;
default:
split_ctrl |= EHCI_QH_SPLIT_CTRL_1_XACTS;
break;
}
if (usb_port_status != USBA_HIGH_SPEED_DEV) {
mutex_enter(&usba_device->usb_mutex);
split_ctrl |= ((usba_device->usb_hs_hub_addr
<< EHCI_QH_SPLIT_CTRL_HUB_ADDR_SHIFT) &
EHCI_QH_SPLIT_CTRL_HUB_ADDR);
split_ctrl |= ((usba_device->usb_hs_hub_port
<< EHCI_QH_SPLIT_CTRL_HUB_PORT_SHIFT) &
EHCI_QH_SPLIT_CTRL_HUB_PORT);
mutex_exit(&usba_device->usb_mutex);
status = EHCI_QH_STS_DO_START_SPLIT;
}
maxpacketsize = endpoint->wMaxPacketSize & USB_EP_MAX_PKTSZ_MASK;
maxpacketsize = maxpacketsize << EHCI_QH_CTRL_MAXPKTSZ_SHIFT;
ctrl |= (maxpacketsize & EHCI_QH_CTRL_MAXPKTSZ);
Set_QH(qh->qh_ctrl, ctrl);
Set_QH(qh->qh_split_ctrl, split_ctrl);
Set_QH(qh->qh_status, status);
}
void
ehci_insert_qh(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_qh: qh=0x%p", (void *)pp->pp_qh);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
switch (ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
case USB_EP_ATTR_BULK:
ehci_insert_async_qh(ehcip, pp);
ehcip->ehci_open_async_count++;
break;
case USB_EP_ATTR_INTR:
ehci_insert_intr_qh(ehcip, pp);
ehcip->ehci_open_periodic_count++;
break;
case USB_EP_ATTR_ISOCH:
ehcip->ehci_open_periodic_count++;
break;
}
}
static void
ehci_insert_async_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
ehci_qh_t *qh = pp->pp_qh;
ehci_qh_t *async_head_qh;
ehci_qh_t *next_qh;
uintptr_t qh_addr;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_async_qh:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == 0);
qh_addr = ehci_qh_cpu_to_iommu(ehcip, qh);
async_head_qh = ehcip->ehci_head_of_async_sched_list;
if (async_head_qh == NULL) {
Set_QH(qh->qh_ctrl,
(Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_RECLAIM_HEAD));
Set_QH(qh->qh_link_ptr, qh_addr | EHCI_QH_LINK_REF_QH);
Set_QH(qh->qh_prev, qh_addr);
ehcip->ehci_head_of_async_sched_list = qh;
Set_OpReg(ehci_async_list_addr, qh_addr);
if ((ehcip->ehci_vendor_id == PCI_VENDOR_ULi_M1575) &&
(ehcip->ehci_device_id == PCI_DEVICE_ULi_M1575) &&
(qh_addr != Get_OpReg(ehci_async_list_addr))) {
int retry = 0;
Set_OpRegRetry(ehci_async_list_addr, qh_addr, retry);
if (retry >= EHCI_MAX_RETRY)
cmn_err(CE_PANIC, "ehci_insert_async_qh:"
" ASYNCLISTADDR write failed.");
USB_DPRINTF_L2(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
"ehci_insert_async_qh: ASYNCLISTADDR "
"write failed, retry=%d", retry);
}
} else {
ASSERT(Get_QH(async_head_qh->qh_ctrl) &
EHCI_QH_CTRL_RECLAIM_HEAD);
Set_QH(qh->qh_ctrl,
(Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_RECLAIM_HEAD));
next_qh = ehci_qh_iommu_to_cpu(ehcip,
Get_QH(async_head_qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
Set_QH(qh->qh_link_ptr,
Get_QH(async_head_qh->qh_link_ptr) | EHCI_QH_LINK_REF_QH);
Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, async_head_qh));
Set_QH(next_qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, qh));
Set_QH(async_head_qh->qh_link_ptr,
qh_addr | EHCI_QH_LINK_REF_QH);
}
}
static void
ehci_insert_intr_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
ehci_qh_t *qh = pp->pp_qh;
ehci_qh_t *next_lattice_qh, *lattice_qh;
uint_t hnode;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_intr_qh:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == 0);
hnode = pp->pp_pnode;
lattice_qh = &ehcip->ehci_qh_pool_addr[hnode];
next_lattice_qh = ehci_qh_iommu_to_cpu(
ehcip, (Get_QH(lattice_qh->qh_link_ptr) & EHCI_QH_LINK_PTR));
Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, lattice_qh));
if (next_lattice_qh) {
Set_QH(qh->qh_link_ptr, Get_QH(lattice_qh->qh_link_ptr));
if (Get_QH(next_lattice_qh->qh_state) != EHCI_QH_STATIC) {
Set_QH(next_lattice_qh->qh_prev,
ehci_qh_cpu_to_iommu(ehcip, qh));
}
} else {
Set_QH(qh->qh_link_ptr,
(Get_QH(lattice_qh->qh_link_ptr) | EHCI_QH_LINK_PTR_VALID));
}
Set_QH(lattice_qh->qh_link_ptr,
(ehci_qh_cpu_to_iommu(ehcip, qh) | EHCI_QH_LINK_REF_QH));
}
static void
ehci_modify_qh_status_bit(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
halt_bit_t action)
{
ehci_qh_t *qh = pp->pp_qh;
uint_t smask, eps, split_intr_qh;
uint_t status;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_modify_qh_status_bit: action=0x%x qh=0x%p",
action, (void *)qh);
ehci_print_qh(ehcip, qh);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
while (pp->pp_halt_state & EHCI_HALT_STATE_HALTING) {
cv_wait(&pp->pp_halt_cmpl_cv,
&ehcip->ehci_int_mutex);
}
Sync_QH_QTD_Pool(ehcip);
if (action == CLEAR_HALT) {
Set_QH(qh->qh_ctrl,
(Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_ED_INACTIVATE));
Set_QH(qh->qh_status,
Get_QH(qh->qh_status) & ~(EHCI_QH_STS_XACT_STATUS));
goto success;
}
status = Get_QH(qh->qh_status);
if (!(status & EHCI_QH_STS_HALTED)) {
pp->pp_halt_state |= EHCI_HALT_STATE_HALTING;
smask = Get_QH(qh->qh_split_ctrl) &
EHCI_QH_SPLIT_CTRL_INTR_MASK;
eps = Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_ED_SPEED;
split_intr_qh = ((smask != 0) &&
(eps != EHCI_QH_CTRL_ED_HIGH_SPEED));
if (eps == EHCI_QH_CTRL_ED_HIGH_SPEED) {
ehci_halt_hs_qh(ehcip, pp, qh);
} else {
if (split_intr_qh) {
ehci_halt_fls_intr_qh(ehcip, qh);
} else {
ehci_halt_fls_ctrl_and_bulk_qh(ehcip, pp, qh);
}
}
pp->pp_halt_state &= ~EHCI_HALT_STATE_HALTING;
}
Sync_QH_QTD_Pool(ehcip);
ehci_print_qh(ehcip, qh);
status = Get_QH(qh->qh_status);
if (!(status & EHCI_QH_STS_HALTED)) {
USB_DPRINTF_L1(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_modify_qh_status_bit: Failed to halt qh=0x%p",
(void *)qh);
ehci_print_qh(ehcip, qh);
ehcip->ehci_hc_soft_state = EHCI_CTLR_ERROR_STATE;
ASSERT(status & EHCI_QH_STS_HALTED);
}
success:
cv_signal(&pp->pp_halt_cmpl_cv);
}
static void
ehci_halt_hs_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_qh_t *qh)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_halt_hs_qh:");
ehci_remove_qh(ehcip, pp, B_FALSE);
ehci_toggle_scheduler_on_pipe(ehcip);
(void) ehci_wait_for_sof(ehcip);
Sync_QH_QTD_Pool(ehcip);
Set_QH(qh->qh_status,
((Get_QH(qh->qh_status) &
~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
ehci_insert_qh(ehcip, ph);
ehci_toggle_scheduler_on_pipe(ehcip);
}
static void
ehci_halt_fls_ctrl_and_bulk_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_qh_t *qh)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
uint_t status, split_status, bytes_left;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_halt_fls_ctrl_and_bulk_qh:");
ehci_remove_qh(ehcip, pp, B_FALSE);
ehci_toggle_scheduler_on_pipe(ehcip);
(void) ehci_wait_for_sof(ehcip);
Sync_QH_QTD_Pool(ehcip);
Set_QH(qh->qh_status,
((Get_QH(qh->qh_status) &
~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
status = Get_QH(qh->qh_status);
split_status = status & EHCI_QH_STS_SPLIT_XSTATE;
bytes_left = status & EHCI_QH_STS_BYTES_TO_XFER;
if ((split_status == EHCI_QH_STS_DO_COMPLETE_SPLIT) &&
(bytes_left != 0)) {
ehci_clear_tt_buffer(ehcip, ph, qh);
}
ehci_insert_qh(ehcip, ph);
ehci_toggle_scheduler_on_pipe(ehcip);
}
static void
ehci_clear_tt_buffer(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
ehci_qh_t *qh)
{
usba_device_t *usba_device;
usba_device_t *hub_usba_device;
usb_pipe_handle_t hub_def_ph;
usb_ep_descr_t *eptd;
uchar_t attributes;
uint16_t wValue;
usb_ctrl_setup_t setup;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
int retry;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_clear_tt_buffer: ");
usba_device = ph->p_usba_device;
eptd = &ph->p_ep;
attributes = eptd->bmAttributes & USB_EP_ATTR_MASK;
wValue = 0;
if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
wValue |= 0x8000;
}
wValue |= attributes << 11;
wValue |= (Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_DEVICE_ADDRESS) << 4;
wValue |= (Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_ED_HIGH_SPEED) >>
EHCI_QH_CTRL_ED_NUMBER_SHIFT;
mutex_exit(&ehcip->ehci_int_mutex);
setup.bmRequestType = EHCI_CLEAR_TT_BUFFER_REQTYPE;
setup.bRequest = EHCI_CLEAR_TT_BUFFER_BREQ;
setup.wValue = wValue;
setup.wIndex = 1;
setup.wLength = 0;
setup.attrs = USB_ATTRS_NONE;
mutex_enter(&usba_device->usb_mutex);
hub_usba_device = usba_device->usb_hs_hub_usba_dev;
mutex_exit(&usba_device->usb_mutex);
mutex_enter(&hub_usba_device->usb_mutex);
hub_def_ph = (usb_pipe_handle_t)&hub_usba_device->usb_ph_list[0];
mutex_exit(&hub_usba_device->usb_mutex);
for (retry = 0; retry < 3; retry++) {
if (usb_pipe_ctrl_xfer_wait(
hub_def_ph,
&setup,
NULL,
&completion_reason, &cb_flags, 0) == USB_SUCCESS) {
break;
}
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_clear_tt_buffer: Failed to clear tt buffer,"
"retry = %d, cr = %d, cb_flags = 0x%x\n",
retry, completion_reason, cb_flags);
}
if (retry >= 3) {
char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
dev_info_t *dip = hub_usba_device->usb_dip;
USB_DPRINTF_L0(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"Error recovery failure: Please hotplug the 2.0 hub at"
"%s", ddi_pathname(dip, path));
kmem_free(path, MAXPATHLEN);
}
mutex_enter(&ehcip->ehci_int_mutex);
}
static void
ehci_halt_fls_intr_qh(
ehci_state_t *ehcip,
ehci_qh_t *qh)
{
usb_frame_number_t starting_frame;
usb_frame_number_t frames_past;
uint_t status, i;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_halt_fls_intr_qh:");
Set_QH(qh->qh_ctrl,
(Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_ED_INACTIVATE));
starting_frame = ehci_get_current_frame_number(ehcip);
Sync_QH_QTD_Pool(ehcip);
frames_past = 0;
status = Get_QH(qh->qh_status) & EHCI_QTD_CTRL_ACTIVE_XACT;
while ((frames_past <= (EHCI_NUM_INTR_QH_LISTS + 2)) &&
(status != 0)) {
(void) ehci_wait_for_sof(ehcip);
Sync_QH_QTD_Pool(ehcip);
status = Get_QH(qh->qh_status) & EHCI_QTD_CTRL_ACTIVE_XACT;
frames_past = ehci_get_current_frame_number(ehcip) -
starting_frame;
}
Sync_QH_QTD_Pool(ehcip);
status = Get_QH(qh->qh_status);
for (i = 0; i < EHCI_NUM_INTR_QH_LISTS; i++) {
Set_QH(qh->qh_status,
((Get_QH(qh->qh_status) &
~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
Sync_QH_QTD_Pool(ehcip);
(void) ehci_wait_for_sof(ehcip);
Sync_QH_QTD_Pool(ehcip);
if (Get_QH(qh->qh_status) & EHCI_QH_STS_HALTED) {
break;
}
}
Sync_QH_QTD_Pool(ehcip);
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_halt_fls_intr_qh: qh=0x%p frames past=%llu,"
" status=0x%x, 0x%x", (void *)qh,
(unsigned long long)(ehci_get_current_frame_number(ehcip) -
starting_frame), status, Get_QH(qh->qh_status));
}
void
ehci_remove_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
boolean_t reclaim)
{
uchar_t attributes;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_remove_qh: qh=0x%p", (void *)pp->pp_qh);
attributes = pp->pp_pipe_handle->p_ep.bmAttributes & USB_EP_ATTR_MASK;
switch (attributes) {
case USB_EP_ATTR_CONTROL:
case USB_EP_ATTR_BULK:
ehci_remove_async_qh(ehcip, pp, reclaim);
ehcip->ehci_open_async_count--;
break;
case USB_EP_ATTR_INTR:
ehci_remove_intr_qh(ehcip, pp, reclaim);
ehcip->ehci_open_periodic_count--;
break;
case USB_EP_ATTR_ISOCH:
ehcip->ehci_open_periodic_count--;
break;
}
}
static void
ehci_remove_async_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
boolean_t reclaim)
{
ehci_qh_t *qh = pp->pp_qh;
ehci_qh_t *prev_qh, *next_qh;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_remove_async_qh:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
prev_qh = ehci_qh_iommu_to_cpu(ehcip,
Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR);
next_qh = ehci_qh_iommu_to_cpu(ehcip,
Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
ASSERT(prev_qh != NULL);
if (qh == next_qh) {
ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
ehcip->ehci_head_of_async_sched_list = NULL;
ASSERT(ehcip->ehci_open_async_count == 1);
} else {
if (ehcip->ehci_head_of_async_sched_list == qh) {
ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
ehcip->ehci_head_of_async_sched_list = next_qh;
Set_QH(next_qh->qh_ctrl,
Get_QH(next_qh->qh_ctrl) |
EHCI_QH_CTRL_RECLAIM_HEAD);
}
Set_QH(prev_qh->qh_link_ptr, Get_QH(qh->qh_link_ptr));
Set_QH(next_qh->qh_prev, Get_QH(qh->qh_prev));
}
Set_QH(qh->qh_prev, 0);
if (reclaim) {
ehci_insert_qh_on_reclaim_list(ehcip, pp);
}
}
static void
ehci_remove_intr_qh(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
boolean_t reclaim)
{
ehci_qh_t *qh = pp->pp_qh;
ehci_qh_t *prev_qh, *next_qh;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_remove_intr_qh:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
prev_qh = ehci_qh_iommu_to_cpu(ehcip, Get_QH(qh->qh_prev));
next_qh = ehci_qh_iommu_to_cpu(ehcip,
Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
ASSERT(prev_qh != NULL);
if (next_qh) {
Set_QH(prev_qh->qh_link_ptr, Get_QH(qh->qh_link_ptr));
if (Get_QH(next_qh->qh_state) != EHCI_QH_STATIC) {
Set_QH(next_qh->qh_prev, Get_QH(qh->qh_prev));
}
} else {
Set_QH(prev_qh->qh_link_ptr,
(Get_QH(qh->qh_link_ptr) | EHCI_QH_LINK_PTR_VALID));
}
Set_QH(qh->qh_prev, 0);
if (reclaim) {
ehci_insert_qh_on_reclaim_list(ehcip, pp);
}
}
static void
ehci_insert_qh_on_reclaim_list(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
ehci_qh_t *qh = pp->pp_qh;
ehci_qh_t *next_qh, *prev_qh;
usb_frame_number_t frame_number;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
frame_number =
ehci_get_current_frame_number(ehcip) + MAX_SOF_WAIT_COUNT;
Set_QH(qh->qh_reclaim_frame,
((uint32_t)(EHCI_GET_ID((void *)(uintptr_t)frame_number))));
if (ehcip->ehci_reclaim_list) {
next_qh = ehcip->ehci_reclaim_list;
while (next_qh) {
prev_qh = next_qh;
next_qh = ehci_qh_iommu_to_cpu(ehcip,
Get_QH(next_qh->qh_reclaim_next));
}
Set_QH(prev_qh->qh_reclaim_next,
ehci_qh_cpu_to_iommu(ehcip, qh));
} else {
ehcip->ehci_reclaim_list = qh;
}
ASSERT(Get_QH(qh->qh_reclaim_next) == 0);
}
void
ehci_deallocate_qh(
ehci_state_t *ehcip,
ehci_qh_t *old_qh)
{
ehci_qtd_t *first_dummy_qtd, *second_dummy_qtd;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_deallocate_qh:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
first_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
(Get_QH(old_qh->qh_next_qtd) & EHCI_QH_NEXT_QTD_PTR));
if (first_dummy_qtd) {
ASSERT(Get_QTD(first_dummy_qtd->qtd_state) == EHCI_QTD_DUMMY);
second_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(first_dummy_qtd->qtd_next_qtd));
if (second_dummy_qtd) {
ASSERT(Get_QTD(second_dummy_qtd->qtd_state) ==
EHCI_QTD_DUMMY);
ehci_deallocate_qtd(ehcip, second_dummy_qtd);
}
ehci_deallocate_qtd(ehcip, first_dummy_qtd);
}
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_deallocate_qh: Deallocated 0x%p", (void *)old_qh);
Set_QH(old_qh->qh_state, EHCI_QH_FREE);
}
uint32_t
ehci_qh_cpu_to_iommu(
ehci_state_t *ehcip,
ehci_qh_t *addr)
{
uint32_t qh;
qh = (uint32_t)ehcip->ehci_qh_pool_cookie.dmac_address +
(uint32_t)((uintptr_t)addr - (uintptr_t)(ehcip->ehci_qh_pool_addr));
ASSERT(qh >= ehcip->ehci_qh_pool_cookie.dmac_address);
ASSERT(qh <= ehcip->ehci_qh_pool_cookie.dmac_address +
sizeof (ehci_qh_t) * ehci_qh_pool_size);
return (qh);
}
ehci_qh_t *
ehci_qh_iommu_to_cpu(
ehci_state_t *ehcip,
uintptr_t addr)
{
ehci_qh_t *qh;
if (addr == 0)
return (NULL);
qh = (ehci_qh_t *)((uintptr_t)
(addr - ehcip->ehci_qh_pool_cookie.dmac_address) +
(uintptr_t)ehcip->ehci_qh_pool_addr);
ASSERT(qh >= ehcip->ehci_qh_pool_addr);
ASSERT((uintptr_t)qh <= (uintptr_t)ehcip->ehci_qh_pool_addr +
(uintptr_t)(sizeof (ehci_qh_t) * ehci_qh_pool_size));
return (qh);
}
static int
ehci_initialize_dummy(
ehci_state_t *ehcip,
ehci_qh_t *qh)
{
ehci_qtd_t *first_dummy_qtd, *second_dummy_qtd;
first_dummy_qtd = ehci_allocate_qtd_from_pool(ehcip);
if (first_dummy_qtd == NULL) {
return (USB_NO_RESOURCES);
}
second_dummy_qtd = ehci_allocate_qtd_from_pool(ehcip);
if (second_dummy_qtd == NULL) {
ehci_deallocate_qtd(ehcip, first_dummy_qtd);
return (USB_NO_RESOURCES);
}
Set_QH(qh->qh_next_qtd, ehci_qtd_cpu_to_iommu(ehcip,
first_dummy_qtd) & EHCI_QH_NEXT_QTD_PTR);
Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, first_dummy_qtd));
Set_QTD(first_dummy_qtd->qtd_next_qtd,
ehci_qtd_cpu_to_iommu(ehcip, second_dummy_qtd));
return (USB_SUCCESS);
}
ehci_trans_wrapper_t *
ehci_allocate_ctrl_resources(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags)
{
size_t qtd_count = 2;
size_t ctrl_buf_size;
ehci_trans_wrapper_t *tw;
if (ctrl_reqp->ctrl_wLength) {
qtd_count += 1;
}
if (ctrl_reqp->ctrl_wLength) {
ctrl_buf_size = EHCI_MAX_QTD_BUF_SIZE +
ctrl_reqp->ctrl_wLength;
} else {
ctrl_buf_size = SETUP_SIZE;
}
tw = ehci_allocate_tw_resources(ehcip, pp, ctrl_buf_size,
usb_flags, qtd_count);
return (tw);
}
void
ehci_insert_ctrl_req(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp,
ehci_trans_wrapper_t *tw,
usb_flags_t usb_flags)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
uchar_t bmRequestType = ctrl_reqp->ctrl_bmRequestType;
uchar_t bRequest = ctrl_reqp->ctrl_bRequest;
uint16_t wValue = ctrl_reqp->ctrl_wValue;
uint16_t wIndex = ctrl_reqp->ctrl_wIndex;
uint16_t wLength = ctrl_reqp->ctrl_wLength;
mblk_t *data = ctrl_reqp->ctrl_data;
uint32_t ctrl = 0;
uint8_t setup_packet[8];
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_ctrl_req:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
tw->tw_curr_xfer_reqp = (usb_opaque_t)ctrl_reqp;
tw->tw_timeout = ctrl_reqp->ctrl_timeout ?
ctrl_reqp->ctrl_timeout : EHCI_DEFAULT_XFER_TIMEOUT;
tw->tw_handle_qtd = ehci_handle_ctrl_qtd;
tw->tw_handle_callback_value = NULL;
setup_packet[0] = bmRequestType;
setup_packet[1] = bRequest;
setup_packet[2] = (uint8_t)wValue;
setup_packet[3] = wValue >> 8;
setup_packet[4] = (uint8_t)wIndex;
setup_packet[5] = wIndex >> 8;
setup_packet[6] = (uint8_t)wLength;
setup_packet[7] = wLength >> 8;
bcopy(setup_packet, tw->tw_buf, SETUP_SIZE);
Sync_IO_Buffer_for_device(tw->tw_dmahandle, SETUP_SIZE);
ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_0 | EHCI_QTD_CTRL_SETUP_PID);
(void) ehci_insert_qtd(ehcip, ctrl, 0, SETUP_SIZE,
EHCI_CTRL_SETUP_PHASE, pp, tw);
USB_DPRINTF_L3(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_insert_ctrl_req: pp 0x%p", (void *)pp);
if (wLength != 0) {
if (bmRequestType & USB_DEV_REQ_DEV_TO_HOST) {
tw->tw_direction = EHCI_QTD_CTRL_IN_PID;
} else {
tw->tw_direction = EHCI_QTD_CTRL_OUT_PID;
bcopy(data->b_rptr, tw->tw_buf + EHCI_MAX_QTD_BUF_SIZE,
wLength);
Sync_IO_Buffer_for_device(tw->tw_dmahandle,
wLength + EHCI_MAX_QTD_BUF_SIZE);
}
ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1 | tw->tw_direction);
(void) ehci_insert_qtd(ehcip, ctrl, EHCI_MAX_QTD_BUF_SIZE,
tw->tw_length - EHCI_MAX_QTD_BUF_SIZE,
EHCI_CTRL_DATA_PHASE, pp, tw);
if (tw->tw_direction == EHCI_QTD_CTRL_IN_PID) {
ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1|
EHCI_QTD_CTRL_OUT_PID |
EHCI_QTD_CTRL_INTR_ON_COMPLETE);
} else {
ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1|
EHCI_QTD_CTRL_IN_PID |
EHCI_QTD_CTRL_INTR_ON_COMPLETE);
}
} else {
ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1 |
EHCI_QTD_CTRL_IN_PID |
EHCI_QTD_CTRL_INTR_ON_COMPLETE);
}
(void) ehci_insert_qtd(ehcip, ctrl, 0, 0,
EHCI_CTRL_STATUS_PHASE, pp, tw);
ehci_start_xfer_timer(ehcip, pp, tw);
}
ehci_trans_wrapper_t *
ehci_allocate_bulk_resources(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags)
{
size_t qtd_count = 0;
ehci_trans_wrapper_t *tw;
if (bulk_reqp->bulk_len > EHCI_MAX_BULK_XFER_SIZE) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_bulk_resources: Bulk request size 0x%x is "
"more than 0x%x", bulk_reqp->bulk_len,
EHCI_MAX_BULK_XFER_SIZE);
return (NULL);
}
qtd_count = bulk_reqp->bulk_len / EHCI_MAX_QTD_XFER_SIZE;
if (bulk_reqp->bulk_len % EHCI_MAX_QTD_XFER_SIZE ||
bulk_reqp->bulk_len == 0) {
qtd_count += 1;
}
tw = ehci_allocate_tw_resources(ehcip, pp, bulk_reqp->bulk_len,
usb_flags, qtd_count);
return (tw);
}
void
ehci_insert_bulk_req(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_bulk_req_t *bulk_reqp,
ehci_trans_wrapper_t *tw,
usb_flags_t flags)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
uint_t bulk_pkt_size, count;
size_t residue = 0, len = 0;
uint32_t ctrl = 0;
int pipe_dir;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_bulk_req: bulk_reqp = 0x%p flags = 0x%x",
(void *)bulk_reqp, flags);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
bulk_pkt_size = min(bulk_reqp->bulk_len, EHCI_MAX_QTD_XFER_SIZE);
if (bulk_pkt_size) {
residue = tw->tw_length % bulk_pkt_size;
}
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_bulk_req: bulk_pkt_size = %d", bulk_pkt_size);
tw->tw_curr_xfer_reqp = (usb_opaque_t)bulk_reqp;
tw->tw_timeout = bulk_reqp->bulk_timeout;
tw->tw_handle_qtd = ehci_handle_bulk_qtd;
tw->tw_handle_callback_value = NULL;
tw->tw_direction = (pipe_dir == USB_EP_DIR_OUT) ?
EHCI_QTD_CTRL_OUT_PID : EHCI_QTD_CTRL_IN_PID;
if (tw->tw_direction == EHCI_QTD_CTRL_OUT_PID) {
if (bulk_reqp->bulk_len) {
ASSERT(bulk_reqp->bulk_data != NULL);
bcopy(bulk_reqp->bulk_data->b_rptr, tw->tw_buf,
bulk_reqp->bulk_len);
Sync_IO_Buffer_for_device(tw->tw_dmahandle,
bulk_reqp->bulk_len);
}
}
ctrl = tw->tw_direction;
for (count = 0; count < tw->tw_num_qtds; count++) {
if (count == (tw->tw_num_qtds - 1)) {
ctrl |= EHCI_QTD_CTRL_INTR_ON_COMPLETE;
if (residue) {
bulk_pkt_size = (uint_t)residue;
}
}
(void) ehci_insert_qtd(ehcip, ctrl, len, bulk_pkt_size,
0, pp, tw);
len = len + bulk_pkt_size;
}
ehci_start_xfer_timer(ehcip, pp, tw);
}
int
ehci_start_periodic_pipe_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_opaque_t periodic_in_reqp,
usb_flags_t flags)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_start_periodic_pipe_polling: ep%d",
ph->p_ep.bEndpointAddress & USB_EP_NUM_MASK);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_INTR)) {
error = ehci_handle_root_hub_pipe_start_intr_polling(ph,
(usb_intr_req_t *)periodic_in_reqp, flags);
return (error);
}
switch (pp->pp_state) {
case EHCI_PIPE_STATE_IDLE:
pp->pp_client_periodic_in_reqp = periodic_in_reqp;
error = ehci_start_pipe_polling(ehcip, ph, flags);
if (error != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_INTR,
ehcip->ehci_log_hdl,
"ehci_start_periodic_pipe_polling: "
"Start polling failed");
pp->pp_client_periodic_in_reqp = NULL;
return (error);
}
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_start_periodic_pipe_polling: PP = 0x%p", (void *)pp);
#ifdef DEBUG
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_INTR:
ASSERT((pp->pp_tw_head != NULL) &&
(pp->pp_tw_tail != NULL));
break;
case USB_EP_ATTR_ISOCH:
ASSERT((pp->pp_itw_head != NULL) &&
(pp->pp_itw_tail != NULL));
break;
}
#endif
break;
case EHCI_PIPE_STATE_ACTIVE:
USB_DPRINTF_L2(PRINT_MASK_INTR,
ehcip->ehci_log_hdl,
"ehci_start_periodic_pipe_polling: "
"Polling is already in progress");
error = USB_FAILURE;
break;
case EHCI_PIPE_STATE_ERROR:
USB_DPRINTF_L2(PRINT_MASK_INTR,
ehcip->ehci_log_hdl,
"ehci_start_periodic_pipe_polling: "
"Pipe is halted and perform reset"
"before restart polling");
error = USB_FAILURE;
break;
default:
USB_DPRINTF_L2(PRINT_MASK_INTR,
ehcip->ehci_log_hdl,
"ehci_start_periodic_pipe_polling: "
"Undefined state");
error = USB_FAILURE;
break;
}
return (error);
}
static int
ehci_start_pipe_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
int error = USB_FAILURE;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_start_pipe_polling:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (pp->pp_max_periodic_req_cnt == 0) {
ehci_set_periodic_pipe_polling(ehcip, ph);
}
ASSERT(pp->pp_max_periodic_req_cnt != 0);
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_INTR:
error = ehci_start_intr_polling(ehcip, ph, flags);
break;
case USB_EP_ATTR_ISOCH:
error = ehci_start_isoc_polling(ehcip, ph, flags);
break;
}
return (error);
}
static int
ehci_start_intr_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
ehci_trans_wrapper_t *tw_list, *tw;
int i, total_tws;
int error = USB_SUCCESS;
tw_list = NULL;
total_tws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt;
for (i = 0; i < total_tws; i += 1) {
tw = ehci_allocate_intr_resources(ehcip, ph, NULL, flags);
if (tw == NULL) {
error = USB_NO_RESOURCES;
tw = tw_list;
while (tw != NULL) {
tw_list = tw->tw_next;
ehci_deallocate_intr_in_resource(
ehcip, pp, tw);
ehci_deallocate_tw(ehcip, pp, tw);
tw = tw_list;
}
return (error);
} else {
if (tw_list == NULL) {
tw_list = tw;
}
}
}
while (pp->pp_cur_periodic_req_cnt < pp->pp_max_periodic_req_cnt) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_start_pipe_polling: max = %d curr = %d tw = %p:",
pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt,
(void *)tw_list);
tw = tw_list;
tw_list = tw->tw_next;
ehci_insert_intr_req(ehcip, pp, tw, flags);
pp->pp_cur_periodic_req_cnt++;
}
return (error);
}
static void
ehci_set_periodic_pipe_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *endpoint = &ph->p_ep;
uchar_t ep_attr = endpoint->bmAttributes;
uint_t interval;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_set_periodic_pipe_polling:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
pp->pp_cur_periodic_req_cnt = 0;
if (((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) &&
(pp->pp_client_periodic_in_reqp)) {
usb_intr_req_t *intr_reqp = (usb_intr_req_t *)
pp->pp_client_periodic_in_reqp;
if (intr_reqp->intr_attributes &
USB_ATTRS_ONE_XFER) {
pp->pp_max_periodic_req_cnt = EHCI_INTR_XMS_REQS;
return;
}
}
mutex_enter(&ph->p_usba_device->usb_mutex);
interval = ehci_adjust_polling_interval(ehcip, endpoint,
ph->p_usba_device->usb_port_status);
mutex_exit(&ph->p_usba_device->usb_mutex);
switch (interval) {
case EHCI_INTR_1MS_POLL:
pp->pp_max_periodic_req_cnt = EHCI_INTR_1MS_REQS;
break;
case EHCI_INTR_2MS_POLL:
pp->pp_max_periodic_req_cnt = EHCI_INTR_2MS_REQS;
break;
default:
pp->pp_max_periodic_req_cnt = EHCI_INTR_XMS_REQS;
break;
}
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_set_periodic_pipe_polling: Max periodic requests = %d",
pp->pp_max_periodic_req_cnt);
}
ehci_trans_wrapper_t *
ehci_allocate_intr_resources(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_intr_req_t *intr_reqp,
usb_flags_t flags)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
int pipe_dir;
size_t qtd_count = 1;
size_t tw_length;
ehci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_intr_resources:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
if (intr_reqp) {
tw_length = intr_reqp->intr_len;
} else {
ASSERT(pipe_dir == USB_EP_DIR_IN);
tw_length = (pp->pp_client_periodic_in_reqp) ?
(((usb_intr_req_t *)pp->
pp_client_periodic_in_reqp)->intr_len) :
ph->p_ep.wMaxPacketSize;
}
if (tw_length > EHCI_MAX_QTD_XFER_SIZE) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_intr_resources: Intr request size 0x%lx is "
"more than 0x%x", tw_length, EHCI_MAX_QTD_XFER_SIZE);
return (NULL);
}
if ((tw = ehci_allocate_tw_resources(ehcip, pp, tw_length, flags,
qtd_count)) == NULL) {
return (NULL);
}
if (pipe_dir == USB_EP_DIR_IN) {
if (ehci_allocate_intr_in_resource(ehcip, pp, tw, flags) !=
USB_SUCCESS) {
ehci_deallocate_tw(ehcip, pp, tw);
}
tw->tw_direction = EHCI_QTD_CTRL_IN_PID;
} else {
if (tw_length) {
ASSERT(intr_reqp->intr_data != NULL);
bcopy(intr_reqp->intr_data->b_rptr, tw->tw_buf,
intr_reqp->intr_len);
Sync_IO_Buffer_for_device(tw->tw_dmahandle,
intr_reqp->intr_len);
}
tw->tw_curr_xfer_reqp = (usb_opaque_t)intr_reqp;
tw->tw_direction = EHCI_QTD_CTRL_OUT_PID;
}
if (intr_reqp) {
tw->tw_timeout = intr_reqp->intr_timeout;
}
tw->tw_handle_qtd = ehci_handle_intr_qtd;
tw->tw_handle_callback_value = NULL;
return (tw);
}
void
ehci_insert_intr_req(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
usb_flags_t flags)
{
uint_t ctrl = 0;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(tw->tw_curr_xfer_reqp != NULL);
ctrl = (tw->tw_direction | EHCI_QTD_CTRL_INTR_ON_COMPLETE);
(void) ehci_insert_qtd(ehcip, ctrl, 0, tw->tw_length, 0, pp, tw);
ehci_start_xfer_timer(ehcip, pp, tw);
}
int
ehci_stop_periodic_pipe_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_stop_periodic_pipe_polling: Flags = 0x%x", flags);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_INTR)) {
ehci_handle_root_hub_pipe_stop_intr_polling(ph, flags);
return (USB_SUCCESS);
}
if (pp->pp_state != EHCI_PIPE_STATE_ACTIVE) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_stop_periodic_pipe_polling: "
"Polling already stopped");
return (USB_SUCCESS);
}
pp->pp_state = EHCI_PIPE_STATE_STOP_POLLING;
ehci_pipe_cleanup(ehcip, ph);
return (USB_SUCCESS);
}
int
ehci_insert_qtd(
ehci_state_t *ehcip,
uint32_t qtd_ctrl,
size_t qtd_dma_offs,
size_t qtd_length,
uint32_t qtd_ctrl_phase,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw)
{
ehci_qtd_t *curr_dummy_qtd, *next_dummy_qtd;
ehci_qtd_t *new_dummy_qtd;
ehci_qh_t *qh = pp->pp_qh;
int error = USB_SUCCESS;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
new_dummy_qtd = tw->tw_qtd_free_list;
ASSERT(new_dummy_qtd != NULL);
tw->tw_qtd_free_list = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(new_dummy_qtd->qtd_tw_next_qtd));
Set_QTD(new_dummy_qtd->qtd_tw_next_qtd, 0);
curr_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QH(qh->qh_dummy_qtd));
next_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(curr_dummy_qtd->qtd_next_qtd));
Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, next_dummy_qtd));
Set_QTD(next_dummy_qtd->qtd_next_qtd,
ehci_qtd_cpu_to_iommu(ehcip, new_dummy_qtd));
ehci_fill_in_qtd(ehcip, curr_dummy_qtd, qtd_ctrl,
qtd_dma_offs, qtd_length, qtd_ctrl_phase, pp, tw);
ehci_insert_qtd_on_tw(ehcip, tw, curr_dummy_qtd);
if (pp->pp_flag != EHCI_POLLED_MODE_FLAG) {
ehci_insert_qtd_into_active_qtd_list(ehcip, curr_dummy_qtd);
}
ehci_print_qh(ehcip, qh);
ehci_print_qtd(ehcip, curr_dummy_qtd);
return (error);
}
static ehci_qtd_t *
ehci_allocate_qtd_from_pool(ehci_state_t *ehcip)
{
int i, ctrl;
ehci_qtd_t *qtd;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
for (i = 0; i < ehci_qtd_pool_size; i ++) {
ctrl = Get_QTD(ehcip->ehci_qtd_pool_addr[i].qtd_state);
if (ctrl == EHCI_QTD_FREE) {
break;
}
}
if (i >= ehci_qtd_pool_size) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_allocate_qtd_from_pool: QTD exhausted");
return (NULL);
}
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_allocate_qtd_from_pool: Allocated %d", i);
qtd = &ehcip->ehci_qtd_pool_addr[i];
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_qtd_from_pool: qtd 0x%p", (void *)qtd);
Set_QTD(qtd->qtd_state, EHCI_QTD_DUMMY);
Set_QTD(qtd->qtd_ctrl, EHCI_QTD_CTRL_HALTED_XACT);
Set_QTD(qtd->qtd_next_qtd, EHCI_QTD_NEXT_QTD_PTR_VALID);
Set_QTD(qtd->qtd_alt_next_qtd, EHCI_QTD_ALT_NEXT_QTD_PTR_VALID);
return (qtd);
}
static void
ehci_fill_in_qtd(
ehci_state_t *ehcip,
ehci_qtd_t *qtd,
uint32_t qtd_ctrl,
size_t qtd_dma_offs,
size_t qtd_length,
uint32_t qtd_ctrl_phase,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw)
{
uint32_t buf_addr;
size_t buf_len = qtd_length;
uint32_t ctrl = qtd_ctrl;
uint_t i = 0;
int rem_len;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_fill_in_qtd: qtd 0x%p ctrl 0x%x bufoffs 0x%lx "
"len 0x%lx", (void *)qtd, qtd_ctrl, qtd_dma_offs, qtd_length);
ASSERT(Get_QTD(qtd->qtd_state) == EHCI_QTD_DUMMY);
Set_QTD(qtd->qtd_state, EHCI_QTD_ACTIVE);
ctrl |= (((qtd_length << EHCI_QTD_CTRL_BYTES_TO_XFER_SHIFT)
& EHCI_QTD_CTRL_BYTES_TO_XFER) | EHCI_QTD_CTRL_MAX_ERR_COUNTS);
ASSERT(buf_len == 0 || qtd_dma_offs >= tw->tw_dma_offs);
Set_QTD(qtd->qtd_xfer_offs, qtd_dma_offs);
Set_QTD(qtd->qtd_xfer_len, buf_len);
while (buf_len) {
while ((tw->tw_dma_offs + tw->tw_cookie.dmac_size) <=
qtd_dma_offs) {
tw->tw_dma_offs += tw->tw_cookie.dmac_size;
ddi_dma_nextcookie(tw->tw_dmahandle, &tw->tw_cookie);
tw->tw_cookie_idx++;
ASSERT(tw->tw_cookie_idx < tw->tw_ncookies);
}
rem_len = (tw->tw_dma_offs + tw->tw_cookie.dmac_size) -
qtd_dma_offs;
buf_addr = (qtd_dma_offs - tw->tw_dma_offs) +
tw->tw_cookie.dmac_address;
ASSERT((buf_addr % EHCI_4K_ALIGN) == 0);
Set_QTD(qtd->qtd_buf[i], buf_addr);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_fill_in_qtd: dmac_addr 0x%x dmac_size "
"0x%lx idx %d", buf_addr, tw->tw_cookie.dmac_size,
tw->tw_cookie_idx);
if (buf_len <= EHCI_MAX_QTD_BUF_SIZE) {
ASSERT(buf_len <= rem_len);
break;
} else {
ASSERT(rem_len >= EHCI_MAX_QTD_BUF_SIZE);
buf_len -= EHCI_MAX_QTD_BUF_SIZE;
qtd_dma_offs += EHCI_MAX_QTD_BUF_SIZE;
}
i++;
}
if (tw->tw_alt_qtd != NULL) {
Set_QTD(qtd->qtd_alt_next_qtd,
(ehci_qtd_cpu_to_iommu(ehcip, tw->tw_alt_qtd) &
EHCI_QTD_ALT_NEXT_QTD_PTR));
}
Set_QTD(qtd->qtd_ctrl, (ctrl | EHCI_QTD_CTRL_ACTIVE_XACT));
if (qtd_ctrl_phase) {
Set_QTD(qtd->qtd_ctrl_phase, qtd_ctrl_phase);
}
ASSERT(tw != NULL);
ASSERT(tw->tw_id != 0);
Set_QTD(qtd->qtd_trans_wrapper, (uint32_t)tw->tw_id);
}
static void
ehci_insert_qtd_on_tw(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd)
{
Set_QTD(qtd->qtd_tw_next_qtd, 0);
if (tw->tw_qtd_head == NULL) {
ASSERT(tw->tw_qtd_tail == NULL);
tw->tw_qtd_head = qtd;
tw->tw_qtd_tail = qtd;
} else {
ehci_qtd_t *dummy = (ehci_qtd_t *)tw->tw_qtd_tail;
ASSERT(dummy != NULL);
ASSERT(dummy != qtd);
ASSERT(Get_QTD(qtd->qtd_state) != EHCI_QTD_DUMMY);
Set_QTD(dummy->qtd_tw_next_qtd,
ehci_qtd_cpu_to_iommu(ehcip, qtd));
tw->tw_qtd_tail = qtd;
ASSERT(Get_QTD(qtd->qtd_tw_next_qtd) == 0);
}
}
static void
ehci_insert_qtd_into_active_qtd_list(
ehci_state_t *ehcip,
ehci_qtd_t *qtd)
{
ehci_qtd_t *curr_qtd, *next_qtd;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
curr_qtd = ehcip->ehci_active_qtd_list;
if (curr_qtd) {
next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(curr_qtd->qtd_active_qtd_next));
while (next_qtd) {
curr_qtd = next_qtd;
next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(curr_qtd->qtd_active_qtd_next));
}
Set_QTD(qtd->qtd_active_qtd_prev,
ehci_qtd_cpu_to_iommu(ehcip, curr_qtd));
Set_QTD(curr_qtd->qtd_active_qtd_next,
ehci_qtd_cpu_to_iommu(ehcip, qtd));
} else {
ehcip->ehci_active_qtd_list = qtd;
Set_QTD(qtd->qtd_active_qtd_next, 0);
Set_QTD(qtd->qtd_active_qtd_prev, 0);
}
}
void
ehci_remove_qtd_from_active_qtd_list(
ehci_state_t *ehcip,
ehci_qtd_t *qtd)
{
ehci_qtd_t *curr_qtd, *prev_qtd, *next_qtd;
ASSERT(qtd != NULL);
curr_qtd = ehcip->ehci_active_qtd_list;
while ((curr_qtd) && (curr_qtd != qtd)) {
curr_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(curr_qtd->qtd_active_qtd_next));
}
if ((curr_qtd) && (curr_qtd == qtd)) {
prev_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(curr_qtd->qtd_active_qtd_prev));
next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(curr_qtd->qtd_active_qtd_next));
if (prev_qtd) {
Set_QTD(prev_qtd->qtd_active_qtd_next,
Get_QTD(curr_qtd->qtd_active_qtd_next));
} else {
ehcip->ehci_active_qtd_list = next_qtd;
}
if (next_qtd) {
Set_QTD(next_qtd->qtd_active_qtd_prev,
Get_QTD(curr_qtd->qtd_active_qtd_prev));
}
} else {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_remove_qtd_from_active_qtd_list: "
"Unable to find QTD in active_qtd_list");
}
}
static void
ehci_traverse_qtds(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
ehci_trans_wrapper_t *next_tw;
ehci_qtd_t *qtd;
ehci_qtd_t *next_qtd;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_traverse_qtds:");
next_tw = pp->pp_tw_head;
while (next_tw) {
ehci_stop_xfer_timer(ehcip, next_tw, EHCI_REMOVE_XFER_ALWAYS);
qtd = (ehci_qtd_t *)next_tw->tw_qtd_head;
while (qtd) {
ehci_remove_qtd_from_active_qtd_list(ehcip, qtd);
next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(qtd->qtd_tw_next_qtd));
ehci_deallocate_qtd(ehcip, qtd);
qtd = next_qtd;
}
next_tw = next_tw->tw_next;
}
Set_QH(pp->pp_qh->qh_curr_qtd, (uint32_t)0x00000000);
Set_QH(pp->pp_qh->qh_next_qtd, Get_QH(pp->pp_qh->qh_dummy_qtd));
}
void
ehci_deallocate_qtd(
ehci_state_t *ehcip,
ehci_qtd_t *old_qtd)
{
ehci_trans_wrapper_t *tw = NULL;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_deallocate_qtd: old_qtd = 0x%p", (void *)old_qtd);
if (Get_QTD(old_qtd->qtd_state) != EHCI_QTD_DUMMY) {
tw = (ehci_trans_wrapper_t *)
EHCI_LOOKUP_ID((uint32_t)
Get_QTD(old_qtd->qtd_trans_wrapper));
ASSERT(tw != NULL);
}
if (tw) {
ehci_qtd_t *qtd, *next_qtd;
qtd = tw->tw_qtd_head;
if (old_qtd != qtd) {
next_qtd = ehci_qtd_iommu_to_cpu(
ehcip, Get_QTD(qtd->qtd_tw_next_qtd));
while (next_qtd != old_qtd) {
qtd = next_qtd;
next_qtd = ehci_qtd_iommu_to_cpu(
ehcip, Get_QTD(qtd->qtd_tw_next_qtd));
}
Set_QTD(qtd->qtd_tw_next_qtd, old_qtd->qtd_tw_next_qtd);
if (qtd->qtd_tw_next_qtd == 0) {
tw->tw_qtd_tail = qtd;
}
} else {
tw->tw_qtd_head = ehci_qtd_iommu_to_cpu(
ehcip, Get_QTD(old_qtd->qtd_tw_next_qtd));
if (tw->tw_qtd_head == NULL) {
tw->tw_qtd_tail = NULL;
}
}
}
bzero((void *)old_qtd, sizeof (ehci_qtd_t));
Set_QTD(old_qtd->qtd_state, EHCI_QTD_FREE);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"Dealloc_qtd: qtd 0x%p", (void *)old_qtd);
}
uint32_t
ehci_qtd_cpu_to_iommu(
ehci_state_t *ehcip,
ehci_qtd_t *addr)
{
uint32_t td;
td = (uint32_t)ehcip->ehci_qtd_pool_cookie.dmac_address +
(uint32_t)((uintptr_t)addr -
(uintptr_t)(ehcip->ehci_qtd_pool_addr));
ASSERT((ehcip->ehci_qtd_pool_cookie.dmac_address +
(uint32_t) (sizeof (ehci_qtd_t) *
(addr - ehcip->ehci_qtd_pool_addr))) ==
(ehcip->ehci_qtd_pool_cookie.dmac_address +
(uint32_t)((uintptr_t)addr - (uintptr_t)
(ehcip->ehci_qtd_pool_addr))));
ASSERT(td >= ehcip->ehci_qtd_pool_cookie.dmac_address);
ASSERT(td <= ehcip->ehci_qtd_pool_cookie.dmac_address +
sizeof (ehci_qtd_t) * ehci_qtd_pool_size);
return (td);
}
ehci_qtd_t *
ehci_qtd_iommu_to_cpu(
ehci_state_t *ehcip,
uintptr_t addr)
{
ehci_qtd_t *qtd;
if (addr == 0)
return (NULL);
qtd = (ehci_qtd_t *)((uintptr_t)
(addr - ehcip->ehci_qtd_pool_cookie.dmac_address) +
(uintptr_t)ehcip->ehci_qtd_pool_addr);
ASSERT(qtd >= ehcip->ehci_qtd_pool_addr);
ASSERT((uintptr_t)qtd <= (uintptr_t)ehcip->ehci_qtd_pool_addr +
(uintptr_t)(sizeof (ehci_qtd_t) * ehci_qtd_pool_size));
return (qtd);
}
int
ehci_allocate_tds_for_tw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
size_t qtd_count)
{
usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
uchar_t attributes;
ehci_qtd_t *qtd;
uint32_t qtd_addr;
int i;
int error = USB_SUCCESS;
attributes = eptd->bmAttributes & USB_EP_ATTR_MASK;
for (i = 0; i < qtd_count; i += 1) {
qtd = ehci_allocate_qtd_from_pool(ehcip);
if (qtd == NULL) {
error = USB_NO_RESOURCES;
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_qtds_for_tw: "
"Unable to allocate %lu QTDs",
qtd_count);
break;
}
if (i > 0) {
qtd_addr = ehci_qtd_cpu_to_iommu(ehcip,
tw->tw_qtd_free_list);
Set_QTD(qtd->qtd_tw_next_qtd, qtd_addr);
}
tw->tw_qtd_free_list = qtd;
if ((i == 1) && (attributes == USB_EP_ATTR_BULK)) {
tw->tw_alt_qtd = qtd;
}
}
return (error);
}
static ehci_trans_wrapper_t *
ehci_allocate_tw_resources(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
size_t tw_length,
usb_flags_t usb_flags,
size_t qtd_count)
{
ehci_trans_wrapper_t *tw;
tw = ehci_create_transfer_wrapper(ehcip, pp, tw_length, usb_flags);
if (tw == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_tw_resources: Unable to allocate TW");
} else {
if (ehci_allocate_tds_for_tw(ehcip, pp, tw, qtd_count) ==
USB_SUCCESS) {
tw->tw_num_qtds = (uint_t)qtd_count;
} else {
ehci_deallocate_tw(ehcip, pp, tw);
tw = NULL;
}
}
return (tw);
}
static void
ehci_free_tw_td_resources(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw)
{
ehci_qtd_t *qtd = NULL;
ehci_qtd_t *temp_qtd = NULL;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_free_tw_td_resources: tw = 0x%p", (void *)tw);
qtd = tw->tw_qtd_free_list;
while (qtd != NULL) {
temp_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(qtd->qtd_tw_next_qtd));
ehci_deallocate_qtd(ehcip, qtd);
qtd = temp_qtd;
}
tw->tw_qtd_free_list = NULL;
}
static ehci_trans_wrapper_t *
ehci_create_transfer_wrapper(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
size_t length,
uint_t usb_flags)
{
ddi_device_acc_attr_t dev_attr;
ddi_dma_attr_t dma_attr;
int result;
size_t real_length;
ehci_trans_wrapper_t *tw;
int kmem_flag;
int (*dmamem_wait)(caddr_t);
usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_create_transfer_wrapper: length = 0x%lx flags = 0x%x",
length, usb_flags);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
kmem_flag = KM_NOSLEEP;
dmamem_wait = DDI_DMA_DONTWAIT;
tw = kmem_zalloc(sizeof (ehci_trans_wrapper_t), kmem_flag);
if (tw == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_create_transfer_wrapper: kmem_zalloc failed");
return (NULL);
}
if (length == 0) {
goto dmadone;
}
bcopy(&ehcip->ehci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
dma_attr.dma_attr_sgllen = EHCI_DMA_ATTR_TW_SGLLEN;
dma_attr.dma_attr_align = EHCI_DMA_ATTR_ALIGNMENT;
result = ddi_dma_alloc_handle(ehcip->ehci_dip,
&dma_attr, dmamem_wait, 0, &tw->tw_dmahandle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_create_transfer_wrapper: Alloc handle failed");
kmem_free(tw, sizeof (ehci_trans_wrapper_t));
return (NULL);
}
dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
result = ddi_dma_mem_alloc(tw->tw_dmahandle, length,
&dev_attr, DDI_DMA_CONSISTENT, dmamem_wait, NULL,
(caddr_t *)&tw->tw_buf, &real_length, &tw->tw_accesshandle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_create_transfer_wrapper: dma_mem_alloc fail");
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (ehci_trans_wrapper_t));
return (NULL);
}
ASSERT(real_length >= length);
result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
(caddr_t)tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
dmamem_wait, NULL, &tw->tw_cookie, &tw->tw_ncookies);
if (result != DDI_DMA_MAPPED) {
ehci_decode_ddi_dma_addr_bind_handle_result(ehcip, result);
ddi_dma_mem_free(&tw->tw_accesshandle);
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (ehci_trans_wrapper_t));
return (NULL);
}
tw->tw_cookie_idx = 0;
tw->tw_dma_offs = 0;
dmadone:
if (pp->pp_tw_head == NULL) {
pp->pp_tw_head = tw;
pp->pp_tw_tail = tw;
} else {
pp->pp_tw_tail->tw_next = tw;
pp->pp_tw_tail = tw;
}
tw->tw_length = length;
tw->tw_pipe_private = pp;
tw->tw_flags = usb_flags;
tw->tw_id = EHCI_GET_ID((void *)tw);
ASSERT(tw->tw_id != 0);
if (EHCI_INTR_ENDPOINT(eptd)) {
ehcip->ehci_periodic_req_count++;
} else {
ehcip->ehci_async_req_count++;
}
ehci_toggle_scheduler(ehcip);
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
(void *)tw, tw->tw_ncookies);
return (tw);
}
static void
ehci_start_xfer_timer(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw)
{
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_start_xfer_timer: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (tw->tw_timeout) {
if (pp->pp_timeout_list) {
tw->tw_timeout_next = pp->pp_timeout_list;
}
pp->pp_timeout_list = tw;
ehci_start_timer(ehcip, pp);
}
}
void
ehci_stop_xfer_timer(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw,
uint_t flag)
{
ehci_pipe_private_t *pp;
timeout_id_t timer_id;
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_stop_xfer_timer: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
pp = tw->tw_pipe_private;
if (pp->pp_timeout_list == NULL) {
return;
}
switch (flag) {
case EHCI_REMOVE_XFER_IFLAST:
if (tw->tw_qtd_head != tw->tw_qtd_tail) {
break;
}
case EHCI_REMOVE_XFER_ALWAYS:
ehci_remove_tw_from_timeout_list(ehcip, tw);
if ((pp->pp_timeout_list == NULL) &&
(pp->pp_timer_id)) {
timer_id = pp->pp_timer_id;
pp->pp_timer_id = 0;
mutex_exit(&ehcip->ehci_int_mutex);
(void) untimeout(timer_id);
mutex_enter(&ehcip->ehci_int_mutex);
}
break;
default:
break;
}
}
static void
ehci_xfer_timeout_handler(void *arg)
{
usba_pipe_handle_data_t *ph = (usba_pipe_handle_data_t *)arg;
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
ehci_trans_wrapper_t *tw, *next;
ehci_trans_wrapper_t *expire_xfer_list = NULL;
ehci_qtd_t *qtd;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_xfer_timeout_handler: ehcip = 0x%p, ph = 0x%p",
(void *)ehcip, (void *)ph);
mutex_enter(&ehcip->ehci_int_mutex);
if (pp->pp_timer_id != 0) {
pp->pp_timer_id = 0;
} else {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
tw = pp->pp_timeout_list;
while (tw) {
next = tw->tw_timeout_next;
tw->tw_timeout--;
if (tw->tw_timeout <= 0) {
ehci_remove_tw_from_timeout_list(ehcip, tw);
qtd = tw->tw_qtd_head;
while (qtd) {
ehci_remove_qtd_from_active_qtd_list(
ehcip, qtd);
qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(qtd->qtd_tw_next_qtd));
}
tw->tw_timeout_next = expire_xfer_list;
expire_xfer_list = tw;
}
tw = next;
}
ehci_start_timer(ehcip, pp);
tw = expire_xfer_list;
while (tw) {
next = tw->tw_timeout_next;
ehci_handle_error(ehcip, tw->tw_qtd_head, USB_CR_TIMEOUT);
tw = next;
}
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_remove_tw_from_timeout_list(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw)
{
ehci_pipe_private_t *pp;
ehci_trans_wrapper_t *prev, *next;
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_remove_tw_from_timeout_list: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
pp = tw->tw_pipe_private;
if (pp->pp_timeout_list) {
if (pp->pp_timeout_list == tw) {
pp->pp_timeout_list = tw->tw_timeout_next;
tw->tw_timeout_next = NULL;
} else {
prev = pp->pp_timeout_list;
next = prev->tw_timeout_next;
while (next && (next != tw)) {
prev = next;
next = next->tw_timeout_next;
}
if (next == tw) {
prev->tw_timeout_next =
next->tw_timeout_next;
tw->tw_timeout_next = NULL;
}
}
}
}
static void
ehci_start_timer(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_start_timer: ehcip = 0x%p, pp = 0x%p",
(void *)ehcip, (void *)pp);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if ((!pp->pp_timer_id) && (pp->pp_timeout_list)) {
pp->pp_timer_id = timeout(ehci_xfer_timeout_handler,
(void *)(pp->pp_pipe_handle), drv_usectohz(1000000));
}
}
void
ehci_deallocate_tw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw)
{
ehci_trans_wrapper_t *prev, *next;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_deallocate_tw: tw = 0x%p", (void *)tw);
if (tw->tw_qtd_head) {
ASSERT(tw->tw_qtd_tail != NULL);
return;
}
ASSERT(tw->tw_qtd_tail == NULL);
ehci_free_tw_td_resources(ehcip, tw);
if (pp->pp_tw_head == tw) {
if (pp->pp_tw_tail == tw) {
pp->pp_tw_head = NULL;
pp->pp_tw_tail = NULL;
} else {
pp->pp_tw_head = tw->tw_next;
}
} else {
prev = pp->pp_tw_head;
next = prev->tw_next;
while (next && (next != tw)) {
prev = next;
next = next->tw_next;
}
if (next == tw) {
prev->tw_next = next->tw_next;
if (pp->pp_tw_tail == tw) {
pp->pp_tw_tail = prev;
}
}
}
ehci_remove_tw_from_timeout_list(ehcip, tw);
ehci_free_tw(ehcip, pp, tw);
}
void
ehci_free_dma_resources(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
ehci_trans_wrapper_t *head_tw = pp->pp_tw_head;
ehci_trans_wrapper_t *next_tw, *tw;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_free_dma_resources: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
next_tw = head_tw;
while (next_tw) {
tw = next_tw;
next_tw = tw->tw_next;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_free_dma_resources: Free TW = 0x%p", (void *)tw);
ehci_free_tw(ehcip, pp, tw);
}
pp->pp_tw_head = NULL;
pp->pp_tw_tail = NULL;
}
static void
ehci_free_tw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw)
{
int rval;
usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_free_tw: tw = 0x%p", (void *)tw);
ASSERT(tw != NULL);
ASSERT(tw->tw_id != 0);
EHCI_FREE_ID((uint32_t)tw->tw_id);
if (tw->tw_dmahandle != NULL) {
rval = ddi_dma_unbind_handle(tw->tw_dmahandle);
ASSERT(rval == DDI_SUCCESS);
ddi_dma_mem_free(&tw->tw_accesshandle);
ddi_dma_free_handle(&tw->tw_dmahandle);
}
if (EHCI_INTR_ENDPOINT(eptd)) {
ehcip->ehci_periodic_req_count--;
} else {
ehcip->ehci_async_req_count--;
}
ehci_toggle_scheduler(ehcip);
kmem_free(tw, sizeof (ehci_trans_wrapper_t));
}
int
ehci_allocate_intr_in_resource(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
usb_flags_t flags)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_intr_req_t *curr_intr_reqp;
usb_opaque_t client_periodic_in_reqp;
size_t length = 0;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_intr_in_resource:"
"pp = 0x%p tw = 0x%p flags = 0x%x", (void *)pp, (void *)tw, flags);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(tw->tw_curr_xfer_reqp == NULL);
client_periodic_in_reqp = pp->pp_client_periodic_in_reqp;
if (client_periodic_in_reqp) {
length = ((usb_intr_req_t *)
client_periodic_in_reqp)->intr_len;
curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
(usb_intr_req_t *)client_periodic_in_reqp, length, flags);
} else {
curr_intr_reqp = usb_alloc_intr_req(ph->p_dip, length, flags);
}
if (curr_intr_reqp == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_intr_in_resource: Interrupt"
"request structure allocation failed");
return (USB_NO_RESOURCES);
}
if (client_periodic_in_reqp == NULL) {
curr_intr_reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK;
curr_intr_reqp->intr_len = ph->p_ep.wMaxPacketSize;
} else {
tw->tw_timeout = (curr_intr_reqp->intr_attributes &
USB_ATTRS_ONE_XFER) ? curr_intr_reqp->intr_timeout: 0;
}
tw->tw_curr_xfer_reqp = (usb_opaque_t)curr_intr_reqp;
tw->tw_length = curr_intr_reqp->intr_len;
mutex_enter(&ph->p_mutex);
ph->p_req_count++;
mutex_exit(&ph->p_mutex);
pp->pp_state = EHCI_PIPE_STATE_ACTIVE;
return (USB_SUCCESS);
}
void
ehci_pipe_cleanup(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
uint_t pipe_state = pp->pp_state;
usb_cr_t completion_reason;
usb_ep_descr_t *eptd = &ph->p_ep;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_pipe_cleanup: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (EHCI_ISOC_ENDPOINT(eptd)) {
ehci_isoc_pipe_cleanup(ehcip, ph);
return;
}
ASSERT(!servicing_interrupt());
ehci_modify_qh_status_bit(ehcip, pp, SET_HALT);
ehci_wait_for_transfers_completion(ehcip, pp);
ehci_save_data_toggle(ehcip, ph);
ehci_traverse_qtds(ehcip, ph);
ASSERT(pp->pp_timer_id == 0);
ehci_handle_outstanding_requests(ehcip, pp);
ehci_free_dma_resources(ehcip, ph);
switch (pipe_state) {
case EHCI_PIPE_STATE_CLOSE:
completion_reason = USB_CR_PIPE_CLOSING;
break;
case EHCI_PIPE_STATE_RESET:
case EHCI_PIPE_STATE_STOP_POLLING:
completion_reason = (pipe_state ==
EHCI_PIPE_STATE_RESET) ?
USB_CR_PIPE_RESET: USB_CR_STOPPED_POLLING;
ehci_restore_data_toggle(ehcip, ph);
ehci_modify_qh_status_bit(ehcip, pp, CLEAR_HALT);
pp->pp_state = EHCI_PIPE_STATE_IDLE;
break;
}
if ((EHCI_PERIODIC_ENDPOINT(eptd)) &&
((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) ==
USB_EP_DIR_IN)) {
ehci_do_client_periodic_in_req_callback(
ehcip, pp, completion_reason);
}
}
static void
ehci_wait_for_transfers_completion(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
ehci_trans_wrapper_t *next_tw = pp->pp_tw_head;
ehci_qtd_t *qtd;
USB_DPRINTF_L4(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_wait_for_transfers_completion: pp = 0x%p", (void *)pp);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if ((ehci_state_is_operational(ehcip)) != USB_SUCCESS) {
return;
}
pp->pp_count_done_qtds = 0;
while (next_tw) {
qtd = (ehci_qtd_t *)next_tw->tw_qtd_head;
while (qtd) {
if (!(Get_QTD(qtd->qtd_ctrl) &
EHCI_QTD_CTRL_ACTIVE_XACT)) {
pp->pp_count_done_qtds++;
}
qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(qtd->qtd_tw_next_qtd));
}
next_tw = next_tw->tw_next;
}
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_wait_for_transfers_completion: count_done_qtds = 0x%x",
pp->pp_count_done_qtds);
if (!pp->pp_count_done_qtds) {
return;
}
(void) cv_reltimedwait(&pp->pp_xfer_cmpl_cv, &ehcip->ehci_int_mutex,
drv_usectohz(EHCI_XFER_CMPL_TIMEWAIT * 1000000), TR_CLOCK_TICK);
if (pp->pp_count_done_qtds) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_wait_for_transfers_completion:"
"No transfers completion confirmation received");
}
}
void
ehci_check_for_transfers_completion(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
USB_DPRINTF_L4(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_check_for_transfers_completion: pp = 0x%p", (void *)pp);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if ((pp->pp_state == EHCI_PIPE_STATE_STOP_POLLING) &&
(pp->pp_error == USB_CR_NO_RESOURCES) &&
(pp->pp_cur_periodic_req_cnt == 0)) {
pp->pp_error = 0;
ehci_do_client_periodic_in_req_callback(
ehcip, pp, USB_CR_NO_RESOURCES);
}
if (pp->pp_count_done_qtds) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_check_for_transfers_completion:"
"count_done_qtds = 0x%x", pp->pp_count_done_qtds);
pp->pp_count_done_qtds--;
if (!pp->pp_count_done_qtds) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_check_for_transfers_completion:"
"Sent transfers completion event pp = 0x%p",
(void *)pp);
cv_signal(&pp->pp_xfer_cmpl_cv);
}
}
}
static void
ehci_save_data_toggle(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
uint_t data_toggle;
usb_cr_t error = pp->pp_error;
ehci_qh_t *qh = pp->pp_qh;
USB_DPRINTF_L4(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_save_data_toggle: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
pp->pp_error = USB_CR_OK;
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_CONTROL) {
return;
}
data_toggle = (Get_QH(qh->qh_status) &
EHCI_QH_STS_DATA_TOGGLE)? DATA1:DATA0;
if (error == USB_CR_STALL) {
data_toggle = DATA0;
}
mutex_enter(&ph->p_mutex);
usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
data_toggle);
mutex_exit(&ph->p_mutex);
}
void
ehci_restore_data_toggle(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
uint_t data_toggle = 0;
USB_DPRINTF_L4(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_restore_data_toggle: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_CONTROL) {
return;
}
mutex_enter(&ph->p_mutex);
data_toggle = usba_hcdi_get_data_toggle(ph->p_usba_device,
ph->p_ep.bEndpointAddress);
usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
0);
mutex_exit(&ph->p_mutex);
if (data_toggle) {
Set_QH(pp->pp_qh->qh_status,
Get_QH(pp->pp_qh->qh_status) | EHCI_QH_STS_DATA_TOGGLE);
} else {
Set_QH(pp->pp_qh->qh_status,
Get_QH(pp->pp_qh->qh_status) & (~EHCI_QH_STS_DATA_TOGGLE));
}
}
void
ehci_handle_outstanding_requests(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_ep_descr_t *eptd = &ph->p_ep;
ehci_trans_wrapper_t *curr_tw;
ehci_trans_wrapper_t *next_tw;
usb_opaque_t curr_xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_handle_outstanding_requests: pp = 0x%p", (void *)pp);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
next_tw = pp->pp_tw_head;
while (next_tw) {
curr_tw = next_tw;
next_tw = curr_tw->tw_next;
curr_xfer_reqp = curr_tw->tw_curr_xfer_reqp;
if (curr_xfer_reqp) {
if ((EHCI_PERIODIC_ENDPOINT(eptd)) &&
(curr_tw->tw_direction == EHCI_QTD_CTRL_IN_PID)) {
pp->pp_cur_periodic_req_cnt--;
ehci_deallocate_intr_in_resource(
ehcip, pp, curr_tw);
} else {
ehci_hcdi_callback(ph, curr_tw, USB_CR_FLUSHED);
}
}
}
}
void
ehci_deallocate_intr_in_resource(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
uchar_t ep_attr = ph->p_ep.bmAttributes;
usb_opaque_t curr_xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_deallocate_intr_in_resource: "
"pp = 0x%p tw = 0x%p", (void *)pp, (void *)tw);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR);
curr_xfer_reqp = tw->tw_curr_xfer_reqp;
if (curr_xfer_reqp) {
tw->tw_curr_xfer_reqp = NULL;
mutex_enter(&ph->p_mutex);
ph->p_req_count--;
mutex_exit(&ph->p_mutex);
usb_free_intr_req((usb_intr_req_t *)curr_xfer_reqp);
pp->pp_state = EHCI_PIPE_STATE_IDLE;
}
}
void
ehci_do_client_periodic_in_req_callback(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
usb_cr_t completion_reason)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_ep_descr_t *eptd = &ph->p_ep;
USB_DPRINTF_L4(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_do_client_periodic_in_req_callback: "
"pp = 0x%p cc = 0x%x", (void *)pp, completion_reason);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (pp->pp_client_periodic_in_reqp) {
ASSERT(pp->pp_cur_periodic_req_cnt == 0);
if (EHCI_ISOC_ENDPOINT(eptd)) {
ehci_hcdi_isoc_callback(ph, NULL, completion_reason);
} else {
ehci_hcdi_callback(ph, NULL, completion_reason);
}
}
}
void
ehci_hcdi_callback(
usba_pipe_handle_data_t *ph,
ehci_trans_wrapper_t *tw,
usb_cr_t completion_reason)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
usb_opaque_t curr_xfer_reqp;
uint_t pipe_state = 0;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_callback: ph = 0x%p, tw = 0x%p, cr = 0x%x",
(void *)ph, (void *)tw, completion_reason);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
switch (completion_reason) {
case USB_CR_OK:
pipe_state = pp->pp_state;
break;
case USB_CR_NO_RESOURCES:
case USB_CR_NOT_SUPPORTED:
case USB_CR_PIPE_RESET:
case USB_CR_STOPPED_POLLING:
pipe_state = EHCI_PIPE_STATE_IDLE;
break;
case USB_CR_PIPE_CLOSING:
break;
default:
pipe_state = EHCI_PIPE_STATE_ERROR;
pp->pp_error = completion_reason;
break;
}
pp->pp_state = pipe_state;
if (tw && tw->tw_curr_xfer_reqp) {
curr_xfer_reqp = tw->tw_curr_xfer_reqp;
tw->tw_curr_xfer_reqp = NULL;
} else {
ASSERT(pp->pp_client_periodic_in_reqp != NULL);
curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
pp->pp_client_periodic_in_reqp = NULL;
}
ASSERT(curr_xfer_reqp != NULL);
mutex_exit(&ehcip->ehci_int_mutex);
usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
mutex_enter(&ehcip->ehci_int_mutex);
}