#include <sys/usb/hcd/ehci/ehcid.h>
#include <sys/usb/hcd/ehci/ehci_xfer.h>
#include <sys/usb/hcd/ehci/ehci_util.h>
#include <sys/usb/hcd/ehci/ehci_isoch.h>
#include <sys/usb/hcd/ehci/ehci_isoch_util.h>
#include <sys/strsun.h>
int ehci_isoc_init(
ehci_state_t *ehcip);
void ehci_isoc_cleanup(
ehci_state_t *ehcip);
void ehci_isoc_pipe_cleanup(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph);
static void ehci_wait_for_isoc_completion(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
ehci_isoc_xwrapper_t *ehci_allocate_isoc_resources(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_reqp,
usb_flags_t usb_flags);
int ehci_insert_isoc_req(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
usb_flags_t usb_flags);
static int ehci_insert_itd_req(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
usb_flags_t usb_flags);
static int ehci_insert_sitd_req(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
usb_flags_t usb_flags);
static void ehci_remove_isoc_itds(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
static void ehci_mark_reclaim_isoc(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp);
static void ehci_reclaim_isoc(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd,
ehci_pipe_private_t *pp);
int ehci_start_isoc_polling(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags);
void ehci_traverse_active_isoc_list(
ehci_state_t *ehcip);
static void ehci_handle_isoc(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd);
static void ehci_handle_itd(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd,
void *tw_handle_callback_value);
static void ehci_sendup_itd_message(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *td,
usb_cr_t error);
void ehci_hcdi_isoc_callback(
usba_pipe_handle_data_t *ph,
ehci_isoc_xwrapper_t *itw,
usb_cr_t completion_reason);
int
ehci_isoc_init(
ehci_state_t *ehcip)
{
return (ehci_allocate_isoc_pools(ehcip));
}
void
ehci_isoc_cleanup(
ehci_state_t *ehcip)
{
ehci_isoc_xwrapper_t *itw;
ehci_pipe_private_t *pp;
ehci_itd_t *itd;
int i, ctrl, rval;
if (ehcip->ehci_itd_pool_addr && ehcip->ehci_itd_pool_mem_handle) {
for (i = 0; i < ehci_get_itd_pool_size(); i ++) {
itd = &ehcip->ehci_itd_pool_addr[i];
ctrl = Get_ITD(ehcip->
ehci_itd_pool_addr[i].itd_state);
if ((ctrl != EHCI_ITD_FREE) &&
(ctrl != EHCI_ITD_DUMMY) &&
(itd->itd_trans_wrapper)) {
mutex_enter(&ehcip->ehci_int_mutex);
itw = (ehci_isoc_xwrapper_t *)
EHCI_LOOKUP_ID((uint32_t)
Get_ITD(itd->itd_trans_wrapper));
pp = itw->itw_pipe_private;
ehci_deallocate_itd(ehcip, itw, itd);
ehci_deallocate_itw(ehcip, pp, itw);
mutex_exit(&ehcip->ehci_int_mutex);
}
}
if ((ehcip->ehci_dma_addr_bind_flag &
EHCI_ITD_POOL_BOUND) == EHCI_ITD_POOL_BOUND) {
rval = ddi_dma_unbind_handle(
ehcip->ehci_itd_pool_dma_handle);
ASSERT(rval == DDI_SUCCESS);
}
ddi_dma_mem_free(&ehcip->ehci_itd_pool_mem_handle);
}
if (ehcip->ehci_itd_pool_dma_handle) {
ddi_dma_free_handle(&ehcip->ehci_itd_pool_dma_handle);
}
}
void ehci_isoc_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_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_isoc_pipe_cleanup: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ehci_mark_reclaim_isoc(ehcip, pp);
ehci_wait_for_isoc_completion(ehcip, pp);
ehci_remove_isoc_itds(ehcip, pp);
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;
pp->pp_state = EHCI_PIPE_STATE_IDLE;
break;
}
if ((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_isoc_completion(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (pp->pp_itw_head == NULL) {
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_itw_head) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_wait_for_isoc_completion: "
"No transfers completion confirmation received");
}
}
ehci_isoc_xwrapper_t *
ehci_allocate_isoc_resources(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_reqp,
usb_flags_t usb_flags)
{
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
int pipe_dir, i;
uint_t max_ep_pkt_size, max_isoc_xfer_size;
usb_isoc_pkt_descr_t *isoc_pkt_descr;
size_t isoc_pkt_count, isoc_pkts_length;
size_t itw_xfer_size = 0;
ehci_isoc_xwrapper_t *itw;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_resources: flags = 0x%x", usb_flags);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (pp->pp_state == EHCI_PIPE_STATE_ERROR) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_resources:"
"Pipe is in error state, need pipe reset to continue");
return (NULL);
}
max_ep_pkt_size = (ph->p_ep.wMaxPacketSize &
EHCI_ITD_CTRL_MAX_PACKET_MASK) *
CalculateITDMultiField(ph->p_ep.wMaxPacketSize);
max_isoc_xfer_size = EHCI_MAX_ISOC_PKTS_PER_XFER * max_ep_pkt_size;
if (isoc_reqp) {
isoc_pkt_descr = isoc_reqp->isoc_pkt_descr;
isoc_pkt_count = isoc_reqp->isoc_pkts_count;
isoc_pkts_length = isoc_reqp->isoc_pkts_length;
} else {
isoc_pkt_descr = ((usb_isoc_req_t *)
pp->pp_client_periodic_in_reqp)->isoc_pkt_descr;
isoc_pkt_count = ((usb_isoc_req_t *)
pp->pp_client_periodic_in_reqp)->isoc_pkts_count;
isoc_pkts_length = ((usb_isoc_req_t *)
pp->pp_client_periodic_in_reqp)->isoc_pkts_length;
}
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
if (pipe_dir == USB_EP_DIR_IN) {
for (i = 0; i < isoc_pkt_count; i++) {
if (isoc_pkt_descr->isoc_pkt_length > 3072) {
return (NULL);
}
itw_xfer_size += isoc_pkt_descr->isoc_pkt_length;
isoc_pkt_descr++;
}
if ((isoc_pkts_length) &&
(isoc_pkts_length != itw_xfer_size)) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_resources: "
"isoc_pkts_length 0x%lx is not equal to the sum of "
"all pkt lengths 0x%lx in an isoc request",
isoc_pkts_length, itw_xfer_size);
return (NULL);
}
} else {
ASSERT(isoc_reqp != NULL);
itw_xfer_size = MBLKL(isoc_reqp->isoc_data);
}
if (itw_xfer_size > max_isoc_xfer_size) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_resources: Maximum isoc request "
"size 0x%x Given isoc request size 0x%lx",
max_isoc_xfer_size, itw_xfer_size);
return (NULL);
}
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_resources: length = 0x%lx", itw_xfer_size);
if ((itw = ehci_allocate_itw_resources(ehcip, pp, itw_xfer_size,
usb_flags, isoc_pkt_count)) == NULL) {
return (NULL);
}
itw->itw_handle_callback_value = NULL;
if (pipe_dir == USB_EP_DIR_IN) {
if (ehci_allocate_isoc_in_resource(ehcip, pp, itw, usb_flags) !=
USB_SUCCESS) {
ehci_deallocate_itw(ehcip, pp, itw);
return (NULL);
}
} else {
if (itw->itw_length) {
ASSERT(isoc_reqp->isoc_data != NULL);
bcopy(isoc_reqp->isoc_data->b_rptr,
itw->itw_buf, itw->itw_length);
Sync_IO_Buffer_for_device(itw->itw_dmahandle,
itw->itw_length);
}
itw->itw_curr_xfer_reqp = isoc_reqp;
}
return (itw);
}
int
ehci_insert_isoc_req(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
usb_flags_t usb_flags)
{
int error;
ehci_itd_t *new_itd, *temp_itd;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_isoc_req: flags = 0x%x port status = 0x%x",
usb_flags, itw->itw_port_status);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(itw->itw_curr_xfer_reqp != NULL);
ASSERT(itw->itw_curr_xfer_reqp->isoc_pkt_descr != NULL);
itw->itw_curr_isoc_pktp = itw->itw_curr_xfer_reqp->isoc_pkt_descr;
if (itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
error = ehci_insert_itd_req(ehcip, pp, itw, usb_flags);
} else {
error = ehci_insert_sitd_req(ehcip, pp, itw, usb_flags);
}
error = ehci_insert_isoc_to_pfl(ehcip, pp, itw);
if (error != USB_SUCCESS) {
new_itd = itw->itw_itd_head;
while (new_itd) {
temp_itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(new_itd->itd_itw_next_itd));
ehci_deallocate_itd(ehcip, itw, new_itd);
new_itd = temp_itd;
}
if ((itw->itw_direction == USB_EP_DIR_IN)) {
ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
if (pp->pp_cur_periodic_req_cnt) {
pp->pp_state =
EHCI_PIPE_STATE_STOP_POLLING;
pp->pp_error = error;
} else {
pp->pp_state = EHCI_PIPE_STATE_IDLE;
}
return (error);
}
itw->itw_num_itds = 0;
itw->itw_length = 0;
}
itw->itw_curr_isoc_pktp = itw->itw_curr_xfer_reqp->isoc_pkt_descr;
pp->pp_flag &= ~EHCI_ISOC_XFER_CONTINUE;
return (error);
}
static int
ehci_insert_itd_req(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
usb_flags_t usb_flags)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_isoc_req_t *curr_isoc_reqp;
usb_isoc_pkt_descr_t *curr_isoc_pkt_descr;
size_t curr_isoc_xfer_offset;
size_t isoc_pkt_length;
uint_t count, xactcount;
uint32_t xact_status;
uint32_t page, pageselected;
uint32_t buf[EHCI_ITD_BUFFER_LIST_SIZE];
uint16_t index = 0;
uint16_t multi = 0;
ehci_itd_t *new_itd;
curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
page = itw->itw_cookie.dmac_address;
ASSERT((page % EHCI_4K_ALIGN) == 0);
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_insert_itd_req: itw_curr_xfer_reqp = 0x%p page = 0x%x,"
" pagesize = 0x%lx", (void *)itw->itw_curr_xfer_reqp, page,
itw->itw_cookie.dmac_size);
count = 0;
curr_isoc_xfer_offset = 0;
while (count < curr_isoc_reqp->isoc_pkts_count) {
new_itd = itw->itw_itd_free_list;
ASSERT(new_itd != NULL);
itw->itw_itd_free_list = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(new_itd->itd_link_ptr));
Set_ITD(new_itd->itd_link_ptr, 0);
bzero(buf, EHCI_ITD_BUFFER_LIST_SIZE * sizeof (uint32_t));
multi = CalculateITDMultiField(ph->p_ep.wMaxPacketSize);
if (multi > EHCI_ITD_CTRL_MULTI_MASK) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_insert_itd_req: Wrong multi value.");
return (USB_FAILURE);
}
for (xactcount = 0, pageselected = 0;
xactcount < EHCI_ITD_CTRL_LIST_SIZE; xactcount++) {
curr_isoc_pkt_descr = itw->itw_curr_isoc_pktp;
isoc_pkt_length =
curr_isoc_pkt_descr->isoc_pkt_length;
curr_isoc_pkt_descr->isoc_pkt_actual_length
= (ushort_t)isoc_pkt_length;
xact_status = 0;
if (pageselected < EHCI_ITD_BUFFER_LIST_SIZE) {
buf[pageselected] |= page;
} else {
USB_DPRINTF_L2(PRINT_MASK_INTR,
ehcip->ehci_log_hdl,
"ehci_insert_itd_req: "
"Error in buffer pointer.");
return (USB_FAILURE);
}
xact_status = (uint32_t)curr_isoc_xfer_offset;
xact_status |= (pageselected << 12);
xact_status |= isoc_pkt_length << 16;
xact_status |= EHCI_ITD_XFER_ACTIVE;
if (count == (curr_isoc_reqp->isoc_pkts_count - 1)) {
xact_status |= EHCI_ITD_XFER_IOC_ON;
}
USB_DPRINTF_L3(PRINT_MASK_INTR,
ehcip->ehci_log_hdl,
"ehci_insert_itd_req: count = 0x%x multi = %d"
"status = 0x%x page = 0x%x index = %d "
"pageselected = %d isoc_pkt_length = 0x%lx",
xactcount, multi, xact_status, page,
index, pageselected, isoc_pkt_length);
Set_ITD_BODY(new_itd, xactcount, xact_status);
itw->itw_curr_isoc_pktp++;
Set_ITD_INDEX(new_itd, xactcount, index++);
curr_isoc_xfer_offset += isoc_pkt_length;
if (curr_isoc_xfer_offset >= EHCI_4K_ALIGN) {
pageselected ++;
page += EHCI_4K_ALIGN;
curr_isoc_xfer_offset -= EHCI_4K_ALIGN;
}
count ++;
if (count >= curr_isoc_reqp->isoc_pkts_count) {
break;
}
}
buf[0] |= (itw->itw_endpoint_num << 8);
buf[0] |= itw->itw_device_addr;
buf[1] |= ph->p_ep.wMaxPacketSize &
EHCI_ITD_CTRL_MAX_PACKET_MASK;
if (itw->itw_direction == USB_EP_DIR_IN) {
buf[1] |= EHCI_ITD_CTRL_DIR_IN;
}
buf[2] |= multi;
Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER0, buf[0]);
Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER1, buf[1]);
Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER2, buf[2]);
Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER3, buf[3]);
Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER4, buf[4]);
Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER5, buf[5]);
Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER6, buf[6]);
Set_ITD(new_itd->itd_state, EHCI_ITD_ACTIVE);
ehci_print_itd(ehcip, new_itd);
ehci_insert_itd_on_itw(ehcip, itw, new_itd);
}
return (USB_SUCCESS);
}
static int
ehci_insert_sitd_req(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
usb_flags_t usb_flags)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_isoc_req_t *curr_isoc_reqp;
usb_isoc_pkt_descr_t *curr_isoc_pkt_descr;
size_t curr_isoc_xfer_offset;
size_t isoc_pkt_length;
uint_t count;
uint32_t ctrl, uframe_sched, xfer_state;
uint32_t page0, page1, prev_sitd;
uint32_t ssplit_count;
ehci_itd_t *new_sitd;
curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
ctrl = 0;
if (itw->itw_direction == USB_EP_DIR_IN) {
ctrl |= EHCI_SITD_CTRL_DIR_IN;
} else {
ctrl |= EHCI_SITD_CTRL_DIR_OUT;
}
ctrl |= (itw->itw_hub_port << EHCI_SITD_CTRL_PORT_SHIFT) &
EHCI_SITD_CTRL_PORT_MASK;
ctrl |= (itw->itw_hub_addr << EHCI_SITD_CTRL_HUB_SHIFT) &
EHCI_SITD_CTRL_HUB_MASK;
ctrl |= (itw->itw_endpoint_num << EHCI_SITD_CTRL_END_PT_SHIFT) &
EHCI_SITD_CTRL_END_PT_MASK;
ctrl |= (itw->itw_device_addr << EHCI_SITD_CTRL_DEVICE_SHIFT) &
EHCI_SITD_CTRL_DEVICE_MASK;
uframe_sched = 0;
uframe_sched |= (pp->pp_smask << EHCI_SITD_UFRAME_SMASK_SHIFT) &
EHCI_SITD_UFRAME_SMASK_MASK;
uframe_sched |= (pp->pp_cmask << EHCI_SITD_UFRAME_CMASK_SHIFT) &
EHCI_SITD_UFRAME_CMASK_MASK;
page0 = itw->itw_cookie.dmac_address;
page1 = 0;
prev_sitd = EHCI_ITD_LINK_PTR_INVALID;
itw->itw_num_itds = curr_isoc_reqp->isoc_pkts_count;
for (count = 0, curr_isoc_xfer_offset = 0;
count < itw->itw_num_itds; count++) {
curr_isoc_pkt_descr = itw->itw_curr_isoc_pktp;
isoc_pkt_length = curr_isoc_pkt_descr->isoc_pkt_length;
curr_isoc_pkt_descr->isoc_pkt_actual_length =
(ushort_t)isoc_pkt_length;
xfer_state = 0;
if (itw->itw_direction == USB_EP_DIR_IN) {
xfer_state |= (ph->p_ep.wMaxPacketSize <<
EHCI_SITD_XFER_TOTAL_SHIFT) &
EHCI_SITD_XFER_TOTAL_MASK;
} else {
xfer_state |= (isoc_pkt_length <<
EHCI_SITD_XFER_TOTAL_SHIFT) &
EHCI_SITD_XFER_TOTAL_MASK;
}
xfer_state |= EHCI_SITD_XFER_ACTIVE;
if (count == (itw->itw_num_itds - 1)) {
xfer_state |= EHCI_SITD_XFER_IOC_ON;
}
ssplit_count = isoc_pkt_length / MAX_UFRAME_SITD_XFER;
if (isoc_pkt_length % MAX_UFRAME_SITD_XFER) {
ssplit_count++;
}
page1 = (ssplit_count & EHCI_SITD_XFER_TCOUNT_MASK) <<
EHCI_SITD_XFER_TCOUNT_SHIFT;
if (ssplit_count > 1) {
page1 |= EHCI_SITD_XFER_TP_BEGIN;
} else {
page1 |= EHCI_SITD_XFER_TP_ALL;
}
new_sitd = itw->itw_itd_free_list;
ASSERT(new_sitd != NULL);
itw->itw_itd_free_list = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(new_sitd->itd_link_ptr));
Set_ITD(new_sitd->itd_link_ptr, 0);
Set_ITD_BODY(new_sitd, EHCI_SITD_CTRL, ctrl);
Set_ITD_BODY(new_sitd, EHCI_SITD_UFRAME_SCHED, uframe_sched);
Set_ITD_BODY(new_sitd, EHCI_SITD_XFER_STATE, xfer_state);
Set_ITD_BODY(new_sitd, EHCI_SITD_BUFFER0,
page0 + curr_isoc_xfer_offset);
Set_ITD_BODY(new_sitd, EHCI_SITD_BUFFER1, page1);
Set_ITD_BODY(new_sitd, EHCI_SITD_PREV_SITD, prev_sitd);
Set_ITD(new_sitd->itd_state, EHCI_ITD_ACTIVE);
ehci_insert_itd_on_itw(ehcip, itw, new_sitd);
itw->itw_curr_isoc_pktp++;
curr_isoc_xfer_offset += isoc_pkt_length;
}
return (USB_SUCCESS);
}
static void
ehci_remove_isoc_itds(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
ehci_isoc_xwrapper_t *curr_itw, *next_itw;
ehci_itd_t *curr_itd, *next_itd;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_remove_isoc_itds: pp = 0x%p", (void *)pp);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
curr_itw = pp->pp_itw_head;
while (curr_itw) {
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_remove_isoc_itds: itw = 0x%p num itds = %d",
(void *)curr_itw, curr_itw->itw_num_itds);
next_itw = curr_itw->itw_next;
curr_itd = curr_itw->itw_itd_head;
while (curr_itd) {
next_itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(curr_itd->itd_itw_next_itd));
ehci_reclaim_isoc(ehcip, curr_itw, curr_itd, pp);
curr_itd = next_itd;
}
ehci_deallocate_itw(ehcip, pp, curr_itw);
curr_itw = next_itw;
}
}
static void
ehci_mark_reclaim_isoc(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp)
{
usb_frame_number_t current_frame_number;
ehci_isoc_xwrapper_t *curr_itw, *next_itw;
ehci_itd_t *curr_itd, *next_itd;
uint_t ctrl;
uint_t isActive;
int i;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_mark_reclaim_isoc: pp = 0x%p", (void *)pp);
if (pp->pp_itw_head == NULL) {
return;
}
current_frame_number = ehci_get_current_frame_number(ehcip);
curr_itw = pp->pp_itw_head;
while (curr_itw) {
next_itw = curr_itw->itw_next;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_mark_reclaim_isoc: itw = 0x%p num itds = %d",
(void *)curr_itw, curr_itw->itw_num_itds);
curr_itd = curr_itw->itw_itd_head;
while (curr_itd) {
next_itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(curr_itd->itd_itw_next_itd));
if (curr_itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
for (i = 0; i < EHCI_ITD_CTRL_LIST_SIZE; i++) {
ctrl = Get_ITD_BODY(curr_itd,
EHCI_ITD_CTRL0 + i);
isActive = ctrl & EHCI_ITD_XFER_ACTIVE;
if (isActive) {
ctrl &= ~EHCI_ITD_XFER_ACTIVE;
Set_ITD_BODY(curr_itd,
EHCI_ITD_CTRL0 + i,
ctrl);
break;
}
}
} else {
ctrl = Get_ITD_BODY(curr_itd,
EHCI_SITD_XFER_STATE);
isActive = ctrl & EHCI_SITD_XFER_ACTIVE;
if (isActive) {
ctrl &= ~EHCI_SITD_XFER_ACTIVE;
Set_ITD_BODY(curr_itd,
EHCI_SITD_XFER_STATE,
ctrl);
}
}
if (isActive) {
Set_ITD(curr_itd->itd_state, EHCI_ITD_RECLAIM);
Set_ITD_FRAME(curr_itd->itd_reclaim_number,
current_frame_number);
ehci_remove_isoc_from_pfl(ehcip, curr_itd);
}
curr_itd = next_itd;
}
curr_itw = next_itw;
}
}
static void
ehci_reclaim_isoc(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd,
ehci_pipe_private_t *pp)
{
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_reclaim_isoc: itd = 0x%p", (void *)itd);
if ((--itw->itw_num_itds == 0) && (itw->itw_curr_xfer_reqp)) {
if (itw->itw_direction == USB_EP_DIR_IN) {
pp->pp_cur_periodic_req_cnt--;
ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
} else {
ehci_hcdi_isoc_callback(pp->pp_pipe_handle, itw,
USB_CR_FLUSHED);
}
}
ehci_deallocate_itd(ehcip, itw, itd);
}
int
ehci_start_isoc_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_isoc_xwrapper_t *itw_list, *itw;
int i, total_itws;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_start_isoc_polling:");
itw_list = NULL;
total_itws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt;
for (i = 0; i < total_itws; i += 1) {
itw = ehci_allocate_isoc_resources(ehcip, ph, NULL, flags);
if (itw == NULL) {
error = USB_NO_RESOURCES;
itw = itw_list;
while (itw != NULL) {
itw_list = itw->itw_next;
ehci_deallocate_isoc_in_resource(
ehcip, pp, itw);
ehci_deallocate_itw(ehcip, pp, itw);
itw = itw_list;
}
return (error);
} else {
if (itw_list == NULL) {
itw_list = itw;
}
}
}
i = 0;
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_isoc_polling: max = %d curr = %d itw = %p:",
pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt,
(void *)itw_list);
itw = itw_list;
itw_list = itw->itw_next;
error = ehci_insert_isoc_req(ehcip, pp, itw, flags);
if (error == USB_SUCCESS) {
pp->pp_cur_periodic_req_cnt++;
} else {
itw = itw_list;
while (itw != NULL) {
itw_list = itw->itw_next;
ehci_deallocate_isoc_in_resource(
ehcip, pp, itw);
ehci_deallocate_itw(ehcip, pp, itw);
itw = itw_list;
}
if (i != 0) {
error = USB_SUCCESS;
}
break;
}
i++;
}
return (error);
}
void
ehci_traverse_active_isoc_list(
ehci_state_t *ehcip)
{
ehci_isoc_xwrapper_t *curr_itw;
ehci_itd_t *curr_itd, *next_itd;
uint_t state;
ehci_pipe_private_t *pp;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_traverse_active_isoc_list:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
Sync_ITD_Pool(ehcip);
curr_itd = ehci_create_done_itd_list(ehcip);
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_traverse_active_isoc_list: current itd = 0x%p",
(void *)curr_itd);
while (curr_itd) {
next_itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(curr_itd->itd_next_active_itd));
curr_itw = (ehci_isoc_xwrapper_t *)EHCI_LOOKUP_ID(
(uint32_t)Get_ITD(curr_itd->itd_trans_wrapper));
pp = curr_itw->itw_pipe_private;
if (curr_itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
ehci_print_itd(ehcip, curr_itd);
} else {
ehci_print_sitd(ehcip, curr_itd);
}
state = Get_ITD(curr_itd->itd_state);
if (state == EHCI_ITD_ACTIVE) {
ehci_parse_isoc_error(ehcip, curr_itw, curr_itd);
ehci_handle_isoc(ehcip, curr_itw, curr_itd);
} else {
ASSERT(state == EHCI_ITD_RECLAIM);
ehci_reclaim_isoc(ehcip, curr_itw, curr_itd, pp);
}
ehci_deallocate_itw(ehcip, pp, curr_itw);
if (pp->pp_itw_head == NULL) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_traverse_active_isoc_list: "
"Sent transfers completion event pp = 0x%p",
(void *)pp);
cv_signal(&pp->pp_xfer_cmpl_cv);
}
curr_itd = next_itd;
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_traverse_active_isoc_list: state = 0x%x "
"pp = 0x%p itw = 0x%p itd = 0x%p next_itd = 0x%p",
state, (void *)pp, (void *)curr_itw, (void *)curr_itd,
(void *)next_itd);
}
}
static void
ehci_handle_isoc(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd)
{
ehci_pipe_private_t *pp;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_isoc:");
pp = itw->itw_pipe_private;
ehci_handle_itd(ehcip, pp, itw, itd, itw->itw_handle_callback_value);
}
static void
ehci_handle_itd(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd,
void *tw_handle_callback_value)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_isoc_req_t *curr_isoc_reqp =
(usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
int error = USB_SUCCESS;
int i, index;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_itd: pp=0x%p itw=0x%p itd=0x%p "
"isoc_reqp=0%p data=0x%p", (void *)pp, (void *)itw, (void *)itd,
(void *)curr_isoc_reqp, (void *)curr_isoc_reqp->isoc_data);
if (itw->itw_port_status == USBA_HIGH_SPEED_DEV &&
curr_isoc_reqp != NULL) {
for (i = 0; i < EHCI_ITD_CTRL_LIST_SIZE; i++) {
index = Get_ITD_INDEX(itd, i);
if (index == EHCI_ITD_UNUSED_INDEX) {
continue;
}
curr_isoc_reqp->
isoc_pkt_descr[index].isoc_pkt_actual_length =
(Get_ITD_BODY(itd, i) & EHCI_ITD_XFER_LENGTH) >> 16;
}
}
if (--itw->itw_num_itds != 0) {
ehci_deallocate_itd(ehcip, itw, itd);
return;
}
if (itw->itw_direction == USB_EP_DIR_OUT) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_itd: Isoc out pipe, isoc_reqp=0x%p, data=0x%p",
(void *)curr_isoc_reqp, (void *)curr_isoc_reqp->isoc_data);
ehci_hcdi_isoc_callback(ph, itw, USB_CR_OK);
ehci_deallocate_itd(ehcip, itw, itd);
return;
}
pp->pp_cur_periodic_req_cnt--;
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_itd: pp_cur_periodic_req_cnt = 0x%x ",
pp->pp_cur_periodic_req_cnt);
ehci_sendup_itd_message(ehcip, pp, itw, itd, USB_CR_OK);
ehci_deallocate_itd(ehcip, itw, itd);
if (pp->pp_state != EHCI_PIPE_STATE_ACTIVE) {
return;
}
if ((error = ehci_allocate_isoc_in_resource(ehcip, pp, itw, 0)) ==
USB_SUCCESS) {
curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
ASSERT(curr_isoc_reqp != NULL);
itw->itw_num_itds = ehci_calc_num_itds(itw,
curr_isoc_reqp->isoc_pkts_count);
if (ehci_allocate_itds_for_itw(ehcip, itw, itw->itw_num_itds) !=
USB_SUCCESS) {
ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
itw->itw_num_itds = 0;
error = USB_FAILURE;
}
}
if ((error != USB_SUCCESS) ||
(ehci_insert_isoc_req(ehcip, pp, itw, 0) != USB_SUCCESS)) {
pp->pp_state = EHCI_PIPE_STATE_STOP_POLLING;
pp->pp_error = USB_CR_NO_RESOURCES;
} else {
pp->pp_cur_periodic_req_cnt++;
ASSERT(pp->pp_cur_periodic_req_cnt ==
pp->pp_max_periodic_req_cnt);
}
}
static void
ehci_sendup_itd_message(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *td,
usb_cr_t error)
{
usb_isoc_req_t *isoc_reqp = itw->itw_curr_xfer_reqp;
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
size_t length;
uchar_t *buf;
mblk_t *mp;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_sendup_itd_message:");
ASSERT(itw != NULL);
length = itw->itw_length;
buf = (uchar_t *)itw->itw_buf;
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_sendup_itd_message: length %ld error %d", length, error);
mp = isoc_reqp->isoc_data;
ASSERT(mp != NULL);
if (length) {
Sync_IO_Buffer(itw->itw_dmahandle, length);
ddi_rep_get8(itw->itw_accesshandle,
mp->b_rptr, buf, length, DDI_DEV_AUTOINCR);
mp->b_wptr = mp->b_wptr + length;
} else {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_sendup_itd_message: Zero length packet");
}
ehci_hcdi_isoc_callback(ph, itw, error);
}
void
ehci_hcdi_isoc_callback(
usba_pipe_handle_data_t *ph,
ehci_isoc_xwrapper_t *itw,
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_isoc_callback: ph = 0x%p, itw = 0x%p, cr = 0x%x",
(void *)ph, (void *)itw, 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;
}
pp->pp_state = pipe_state;
if (itw && itw->itw_curr_xfer_reqp) {
curr_xfer_reqp = (usb_opaque_t)itw->itw_curr_xfer_reqp;
itw->itw_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);
}