#include <sys/usb/hcd/ehci/ehcid.h>
#include <sys/usb/hcd/ehci/ehci_xfer.h>
#include <sys/usb/hcd/ehci/ehci_util.h>
void ehci_handle_ue(ehci_state_t *ehcip);
void ehci_handle_frame_list_rollover(
ehci_state_t *ehcip);
void ehci_handle_endpoint_reclaimation(
ehci_state_t *ehcip);
void ehci_traverse_active_qtd_list(
ehci_state_t *ehcip);
static ehci_qtd_t *ehci_create_done_qtd_list(
ehci_state_t *ehcip);
static usb_cr_t ehci_parse_error(
ehci_state_t *ehcip,
ehci_qtd_t *qtd);
usb_cr_t ehci_check_for_error(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
uint_t ctrl);
static usb_cr_t ehci_check_for_short_xfer(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd);
void ehci_handle_error(
ehci_state_t *ehcip,
ehci_qtd_t *qtd,
usb_cr_t error);
static void ehci_cleanup_data_underrun(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd);
static void ehci_handle_normal_qtd(
ehci_state_t *ehcip,
ehci_qtd_t *qtd,
ehci_trans_wrapper_t *tw);
void ehci_handle_ctrl_qtd(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
void *);
void ehci_handle_bulk_qtd(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
void *);
void ehci_handle_intr_qtd(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
void *);
static void ehci_handle_one_xfer_completion(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw);
static void ehci_sendup_qtd_message(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
usb_cr_t error);
void
ehci_handle_ue(ehci_state_t *ehcip)
{
usb_frame_number_t before_frame_number, after_frame_number;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_ue: Handling of UE interrupt");
before_frame_number = ehci_get_current_frame_number(ehcip);
drv_usecwait(EHCI_TIMEWAIT);
after_frame_number = ehci_get_current_frame_number(ehcip);
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_ue: Before Frame Number 0x%llx "
"After Frame Number 0x%llx",
(unsigned long long)before_frame_number,
(unsigned long long)after_frame_number);
if (after_frame_number > before_frame_number) {
Set_OpReg(ehci_interrupt, (Get_OpReg(ehci_interrupt) &
~EHCI_INTR_HOST_SYSTEM_ERROR));
return;
}
if ((ehci_do_soft_reset(ehcip)) != USB_SUCCESS) {
USB_DPRINTF_L0(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"Unrecoverable USB Hardware Error");
Set_OpReg(ehci_interrupt, (Get_OpReg(ehci_interrupt) &
~EHCI_INTR_HOST_SYSTEM_ERROR));
Set_OpReg(ehci_config_flag, EHCI_CONFIG_FLAG_CLASSIC);
ehcip->ehci_hc_soft_state = EHCI_CTLR_ERROR_STATE;
}
}
void
ehci_handle_frame_list_rollover(ehci_state_t *ehcip)
{
ehcip->ehci_fno += (0x4000 -
(((Get_OpReg(ehci_frame_index) & 0x3FFF) ^
ehcip->ehci_fno) & 0x2000));
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_frame_list_rollover:"
"Frame Number Higher Part 0x%llx\n",
(unsigned long long)(ehcip->ehci_fno));
}
void
ehci_handle_endpoint_reclaimation(ehci_state_t *ehcip)
{
usb_frame_number_t current_frame_number;
usb_frame_number_t endpoint_frame_number;
ehci_qh_t *reclaim_qh;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_endpoint_reclamation:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
current_frame_number = ehci_get_current_frame_number(ehcip);
while (ehcip->ehci_reclaim_list) {
reclaim_qh = ehcip->ehci_reclaim_list;
endpoint_frame_number = (usb_frame_number_t)(uintptr_t)
(EHCI_LOOKUP_ID(Get_QH(reclaim_qh->qh_reclaim_frame)));
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_endpoint_reclamation:"
"current frame number 0x%llx endpoint frame number 0x%llx",
(unsigned long long)current_frame_number,
(unsigned long long)endpoint_frame_number);
if (endpoint_frame_number > current_frame_number) {
break;
}
ehcip->ehci_reclaim_list = ehci_qh_iommu_to_cpu(
ehcip, Get_QH(reclaim_qh->qh_reclaim_next));
EHCI_FREE_ID((uint32_t)Get_QH(reclaim_qh->qh_reclaim_frame));
ehci_deallocate_qh(ehcip, reclaim_qh);
}
}
void
ehci_traverse_active_qtd_list(
ehci_state_t *ehcip)
{
uint_t state;
ehci_qtd_t *curr_qtd = NULL;
ehci_qtd_t *next_qtd = NULL;
usb_cr_t error;
ehci_trans_wrapper_t *tw = NULL;
ehci_pipe_private_t *pp = NULL;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_traverse_active_qtd_list:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
Sync_QH_QTD_Pool(ehcip);
curr_qtd = ehci_create_done_qtd_list(ehcip);
while (curr_qtd) {
next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(curr_qtd->qtd_active_qtd_next));
state = Get_QTD(curr_qtd->qtd_state);
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_traverse_active_qtd_list:\n\t"
"curr_qtd = 0x%p state = 0x%x", (void *)curr_qtd, state);
tw = (ehci_trans_wrapper_t *)EHCI_LOOKUP_ID(
(uint32_t)Get_QTD(curr_qtd->qtd_trans_wrapper));
ASSERT(tw != NULL);
pp = tw->tw_pipe_private;
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_traverse_active_qtd_list: "
"PP = 0x%p TW = 0x%p", (void *)pp, (void *)tw);
if (state != EHCI_QTD_RECLAIM) {
error = ehci_parse_error(ehcip, curr_qtd);
if (error == USB_CR_OK) {
ehci_handle_normal_qtd(ehcip, curr_qtd, tw);
} else {
ehci_handle_error(ehcip, curr_qtd, error);
}
} else {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_traverse_active_qtd_list: "
"QTD State = %d", state);
}
ehci_deallocate_qtd(ehcip, curr_qtd);
ehci_deallocate_tw(ehcip, pp, tw);
curr_qtd = next_qtd;
}
}
ehci_qtd_t *
ehci_create_done_qtd_list(
ehci_state_t *ehcip)
{
ehci_qtd_t *curr_qtd = NULL, *next_qtd = NULL;
ehci_qtd_t *done_qtd_list = NULL, *last_done_qtd = NULL;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_create_done_qtd_list:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
curr_qtd = ehcip->ehci_active_qtd_list;
while (curr_qtd) {
next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(curr_qtd->qtd_active_qtd_next));
if (!(Get_QTD(curr_qtd->qtd_ctrl) &
EHCI_QTD_CTRL_ACTIVE_XACT)) {
ehci_remove_qtd_from_active_qtd_list(ehcip, curr_qtd);
Set_QTD(curr_qtd->qtd_active_qtd_next, 0);
if (last_done_qtd != NULL) {
Set_QTD(last_done_qtd->qtd_active_qtd_next,
ehci_qtd_cpu_to_iommu(ehcip, curr_qtd));
} else {
done_qtd_list = curr_qtd;
}
last_done_qtd = curr_qtd;
}
curr_qtd = next_qtd;
}
return (done_qtd_list);
}
static usb_cr_t
ehci_parse_error(
ehci_state_t *ehcip,
ehci_qtd_t *qtd)
{
uint_t ctrl;
ehci_trans_wrapper_t *tw;
ehci_pipe_private_t *pp;
uint_t flag;
usb_cr_t error;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_parse_error:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(qtd != NULL);
tw = (ehci_trans_wrapper_t *)
EHCI_LOOKUP_ID((uint32_t)Get_QTD(qtd->qtd_trans_wrapper));
ASSERT(tw != NULL);
pp = tw->tw_pipe_private;
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_parse_error: PP 0x%p TW 0x%p", (void *)pp, (void *)tw);
ctrl = (uint_t)Get_QTD(qtd->qtd_ctrl);
if ((error = ehci_check_for_error(ehcip, pp, tw, qtd, ctrl)) !=
USB_CR_OK) {
flag = EHCI_REMOVE_XFER_ALWAYS;
} else {
flag = EHCI_REMOVE_XFER_IFLAST;
}
ehci_stop_xfer_timer(ehcip, tw, flag);
return (error);
}
usb_cr_t
ehci_check_for_error(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
uint_t ctrl)
{
usb_cr_t error = USB_CR_OK;
uint_t status, speed, mask;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: qtd = 0x%p ctrl = 0x%x",
(void *)qtd, ctrl);
speed = Get_QH(pp->pp_qh->qh_ctrl) & EHCI_QH_CTRL_ED_SPEED;
mask = (speed == EHCI_QH_CTRL_ED_HIGH_SPEED)?
EHCI_QTD_CTRL_HS_XACT_STATUS : EHCI_QTD_CTRL_NON_HS_XACT_STATUS;
status = ctrl & EHCI_QTD_CTRL_XACT_STATUS & ~EHCI_QTD_CTRL_HALTED_XACT;
switch (status & mask) {
case EHCI_QTD_CTRL_NO_ERROR:
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: No Error");
error = USB_CR_OK;
break;
case EHCI_QTD_CTRL_ACTIVE_XACT:
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Not accessed");
error = USB_CR_NOT_ACCESSED;
break;
case EHCI_QTD_CTRL_HALTED_XACT:
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Halted");
error = USB_CR_STALL;
break;
case EHCI_QTD_CTRL_BABBLE_DETECTED:
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Babble Detected");
error = USB_CR_DATA_OVERRUN;
break;
case EHCI_QTD_CTRL_XACT_ERROR:
if (ctrl & EHCI_QTD_CTRL_HALTED_XACT && (ctrl &
EHCI_QTD_CTRL_ERR_COUNT_MASK) == 0) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Transaction Error");
error = USB_CR_DEV_NOT_RESP;
} else {
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: No Error");
error = USB_CR_OK;
}
break;
case EHCI_QTD_CTRL_DATA_BUFFER_ERROR:
if (!(ctrl & EHCI_QTD_CTRL_HALTED_XACT)) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Data buffer overrun or "
"underrun ignored");
error = USB_CR_OK;
break;
}
if (tw->tw_direction == EHCI_QTD_CTRL_IN_PID) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Buffer Overrun");
error = USB_CR_BUFFER_OVERRUN;
} else {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Buffer Underrun");
error = USB_CR_BUFFER_UNDERRUN;
}
break;
case EHCI_QTD_CTRL_MISSED_uFRAME:
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Missed uFrame");
error = USB_CR_NOT_ACCESSED;
break;
case EHCI_QTD_CTRL_PRD_SPLIT_XACT_ERR:
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Periodic split-transaction "
"receives an error handshake");
error = USB_CR_UNSPECIFIED_ERR;
break;
default:
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Unspecified Error");
error = USB_CR_UNSPECIFIED_ERR;
break;
}
if ((ctrl & EHCI_QTD_CTRL_HALTED_XACT) && (error == USB_CR_OK)) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Halted");
error = USB_CR_STALL;
}
if (error == USB_CR_OK) {
error = ehci_check_for_short_xfer(ehcip, pp, tw, qtd);
}
if (error) {
uint_t qh_ctrl = Get_QH(pp->pp_qh->qh_ctrl);
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_error: Error %d Device address %d "
"Endpoint number %d", error,
(qh_ctrl & EHCI_QH_CTRL_DEVICE_ADDRESS),
((qh_ctrl & EHCI_QH_CTRL_ED_NUMBER) >>
EHCI_QH_CTRL_ED_NUMBER_SHIFT));
}
return (error);
}
static usb_cr_t
ehci_check_for_short_xfer(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd)
{
usb_cr_t error = USB_CR_OK;
usb_ep_descr_t *eptd;
uchar_t attributes;
uint32_t residue = 0;
usb_req_attrs_t xfer_attrs;
size_t length;
mblk_t *mp = NULL;
usb_opaque_t xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_short_xfer:");
if (pp->pp_flag == EHCI_POLLED_MODE_FLAG) {
return (error);
}
eptd = &pp->pp_pipe_handle->p_ep;
attributes = eptd->bmAttributes & USB_EP_ATTR_MASK;
switch (attributes) {
case USB_EP_ATTR_CONTROL:
if (Get_QTD(qtd->qtd_ctrl_phase) !=
EHCI_CTRL_DATA_PHASE) {
break;
}
case USB_EP_ATTR_BULK:
case USB_EP_ATTR_INTR:
residue = (Get_QTD(qtd->qtd_ctrl) &
EHCI_QTD_CTRL_BYTES_TO_XFER) >>
EHCI_QTD_CTRL_BYTES_TO_XFER_SHIFT;
break;
case USB_EP_ATTR_ISOCH:
break;
}
if (residue) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_short_xfer: residue=%d direction=0x%x",
residue, tw->tw_direction);
length = Get_QTD(qtd->qtd_xfer_offs) +
Get_QTD(qtd->qtd_xfer_len) - residue;
if (tw->tw_direction == EHCI_QTD_CTRL_IN_PID) {
xfer_attrs = ehci_get_xfer_attrs(ehcip, pp, tw);
if (xfer_attrs & USB_ATTRS_SHORT_XFER_OK) {
ehci_cleanup_data_underrun(ehcip, tw, qtd);
} else {
Set_QH(pp->pp_qh->qh_status,
((Get_QH(pp->pp_qh->qh_status) &
~EHCI_QH_STS_ACTIVE) |
EHCI_QH_STS_HALTED));
error = USB_CR_DATA_UNDERRUN;
}
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_short_xfer: requested data=%lu "
"received data=%lu", tw->tw_length, length);
switch (attributes) {
case USB_EP_ATTR_CONTROL:
case USB_EP_ATTR_BULK:
case USB_EP_ATTR_INTR:
tw->tw_length = length;
break;
case USB_EP_ATTR_ISOCH:
default:
break;
}
} else {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_short_xfer: requested data=%lu "
"sent data=%lu", tw->tw_length, length);
xfer_reqp = tw->tw_curr_xfer_reqp;
switch (attributes) {
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_BULK:
mp = (mblk_t *)((usb_bulk_req_t *)
(xfer_reqp))->bulk_data;
mp->b_rptr = mp->b_rptr + length;
break;
case USB_EP_ATTR_INTR:
mp = (mblk_t *)((usb_intr_req_t *)
(xfer_reqp))->intr_data;
mp->b_rptr = mp->b_rptr + length;
break;
case USB_EP_ATTR_ISOCH:
default:
break;
}
}
}
return (error);
}
void
ehci_handle_error(
ehci_state_t *ehcip,
ehci_qtd_t *qtd,
usb_cr_t error)
{
ehci_trans_wrapper_t *tw;
usba_pipe_handle_data_t *ph;
ehci_pipe_private_t *pp;
ehci_qtd_t *tw_qtd = qtd;
uchar_t attributes;
usb_intr_req_t *curr_intr_reqp;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_error: error = 0x%x", error);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(qtd != NULL);
ehci_print_qtd(ehcip, qtd);
tw = (ehci_trans_wrapper_t *)
EHCI_LOOKUP_ID((uint32_t)Get_QTD(qtd->qtd_trans_wrapper));
ASSERT(tw != NULL);
pp = tw->tw_pipe_private;
ph = tw->tw_pipe_private->pp_pipe_handle;
attributes = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
while (tw_qtd) {
Set_QTD(tw_qtd->qtd_state, EHCI_QTD_RECLAIM);
tw_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(tw_qtd->qtd_tw_next_qtd));
}
if (tw->tw_direction == EHCI_QTD_CTRL_IN_PID) {
switch (attributes) {
case USB_EP_ATTR_CONTROL:
if (((ph->p_ep.bmAttributes &
USB_EP_ATTR_MASK) ==
USB_EP_ATTR_CONTROL) &&
(Get_QTD(qtd->qtd_ctrl_phase) ==
EHCI_CTRL_SETUP_PHASE)) {
break;
}
case USB_EP_ATTR_BULK:
ehci_sendup_qtd_message(ehcip, pp, tw, qtd, error);
return;
case USB_EP_ATTR_INTR:
curr_intr_reqp =
(usb_intr_req_t *)tw->tw_curr_xfer_reqp;
if (curr_intr_reqp->intr_attributes &
USB_ATTRS_ONE_XFER) {
ehci_handle_one_xfer_completion(ehcip, tw);
}
pp->pp_cur_periodic_req_cnt--;
break;
case USB_EP_ATTR_ISOCH:
break;
}
}
ehci_hcdi_callback(ph, tw, error);
ehci_check_for_transfers_completion(ehcip, pp);
}
static void
ehci_cleanup_data_underrun(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd)
{
ehci_qtd_t *next_qtd, *temp_qtd;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_cleanup_data_underrun: qtd=0x%p, tw=0x%p",
(void *)qtd, (void *)tw);
if ((tw->tw_alt_qtd == NULL) || (qtd == tw->tw_qtd_tail)) {
return;
}
next_qtd = (ehci_qtd_t *)ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(qtd->qtd_tw_next_qtd));
while (next_qtd) {
tw->tw_num_qtds--;
ehci_remove_qtd_from_active_qtd_list(ehcip, next_qtd);
temp_qtd = next_qtd;
next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
Get_QTD(next_qtd->qtd_tw_next_qtd));
ehci_deallocate_qtd(ehcip, temp_qtd);
}
ASSERT(tw->tw_num_qtds == 1);
}
static void
ehci_handle_normal_qtd(
ehci_state_t *ehcip,
ehci_qtd_t *qtd,
ehci_trans_wrapper_t *tw)
{
ehci_pipe_private_t *pp;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_normal_qtd:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(tw != NULL);
pp = tw->tw_pipe_private;
(*tw->tw_handle_qtd)(ehcip, pp, tw,
qtd, tw->tw_handle_callback_value);
ehci_check_for_transfers_completion(ehcip, pp);
}
void
ehci_handle_ctrl_qtd(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
void *tw_handle_callback_value)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_ctrl_qtd: pp = 0x%p tw = 0x%p qtd = 0x%p state = 0x%x",
(void *)pp, (void *)tw, (void *)qtd, Get_QTD(qtd->qtd_ctrl_phase));
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
tw->tw_num_qtds--;
switch (Get_QTD(qtd->qtd_ctrl_phase)) {
case EHCI_CTRL_SETUP_PHASE:
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"Setup complete: pp 0x%p qtd 0x%p",
(void *)pp, (void *)qtd);
break;
case EHCI_CTRL_DATA_PHASE:
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"Data complete: pp 0x%p qtd 0x%p",
(void *)pp, (void *)qtd);
break;
case EHCI_CTRL_STATUS_PHASE:
if (tw->tw_num_qtds != 0) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"Status complete, but the transfer is not done: "
"tw 0x%p, qtd 0x%p, tw_num_qtd 0x%d",
(void *)tw, (void *)qtd, tw->tw_num_qtds);
ehci_print_qh(ehcip, pp->pp_qh);
ehci_print_qtd(ehcip, qtd);
break;
}
if ((tw->tw_length) &&
(tw->tw_direction == EHCI_QTD_CTRL_IN_PID)) {
ehci_sendup_qtd_message(ehcip,
pp, tw, qtd, USB_CR_OK);
} else {
ehci_hcdi_callback(ph, tw, USB_CR_OK);
}
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"Status complete: pp 0x%p qtd 0x%p",
(void *)pp, (void *)qtd);
break;
}
}
void
ehci_handle_bulk_qtd(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
void *tw_handle_callback_value)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_ep_descr_t *eptd = &ph->p_ep;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_bulk_qtd:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (--tw->tw_num_qtds != 0) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_bulk_qtd: Number of QTDs %d", tw->tw_num_qtds);
return;
}
if ((eptd->bEndpointAddress &
USB_EP_DIR_MASK) == USB_EP_DIR_OUT) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_bulk_qtd: Bulk out pipe");
ehci_hcdi_callback(ph, tw, USB_CR_OK);
return;
}
ehci_sendup_qtd_message(ehcip, pp, tw, qtd, USB_CR_OK);
}
void
ehci_handle_intr_qtd(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
void *tw_handle_callback_value)
{
usb_intr_req_t *curr_intr_reqp =
(usb_intr_req_t *)tw->tw_curr_xfer_reqp;
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_ep_descr_t *eptd = &ph->p_ep;
usb_req_attrs_t attrs;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_intr_qtd:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
attrs = curr_intr_reqp->intr_attributes;
if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_OUT) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_intr_qtd: Intr out pipe, intr_reqp=0x%p,"
"data=0x%p", (void *)curr_intr_reqp,
(void *)curr_intr_reqp->intr_data);
ehci_hcdi_callback(ph, tw, USB_CR_OK);
return;
}
pp->pp_cur_periodic_req_cnt--;
if (attrs & USB_ATTRS_ONE_XFER) {
ehci_handle_one_xfer_completion(ehcip, tw);
}
ehci_sendup_qtd_message(ehcip, pp, tw, qtd, USB_CR_OK);
if ((pp->pp_state != EHCI_PIPE_STATE_ACTIVE) ||
(ehci_state_is_operational(ehcip) != USB_SUCCESS)) {
return;
}
if ((error = ehci_allocate_intr_in_resource(ehcip, pp, tw, 0)) ==
USB_SUCCESS) {
curr_intr_reqp = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
ASSERT(curr_intr_reqp != NULL);
tw->tw_num_qtds = 1;
if (ehci_allocate_tds_for_tw(ehcip, pp, tw, tw->tw_num_qtds) !=
USB_SUCCESS) {
ehci_deallocate_intr_in_resource(ehcip, pp, tw);
error = USB_FAILURE;
}
}
if (error != USB_SUCCESS) {
pp->pp_state = EHCI_PIPE_STATE_STOP_POLLING;
pp->pp_error = USB_CR_NO_RESOURCES;
} else {
ehci_insert_intr_req(ehcip, pp, tw, 0);
pp->pp_cur_periodic_req_cnt++;
ASSERT(pp->pp_cur_periodic_req_cnt ==
pp->pp_max_periodic_req_cnt);
}
}
static void
ehci_handle_one_xfer_completion(
ehci_state_t *ehcip,
ehci_trans_wrapper_t *tw)
{
usba_pipe_handle_data_t *ph = tw->tw_pipe_private->pp_pipe_handle;
ehci_pipe_private_t *pp = tw->tw_pipe_private;
usb_intr_req_t *curr_intr_reqp =
(usb_intr_req_t *)tw->tw_curr_xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_handle_one_xfer_completion: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(curr_intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER);
pp->pp_state = EHCI_PIPE_STATE_IDLE;
((usb_intr_req_t *)(pp->pp_client_periodic_in_reqp))->
intr_data = ((usb_intr_req_t *)
(tw->tw_curr_xfer_reqp))->intr_data;
((usb_intr_req_t *)tw->tw_curr_xfer_reqp)->intr_data = NULL;
usb_free_intr_req((usb_intr_req_t *)tw-> tw_curr_xfer_reqp);
mutex_enter(&ph->p_mutex);
ph->p_req_count--;
mutex_exit(&ph->p_mutex);
tw->tw_curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
pp->pp_client_periodic_in_reqp = NULL;
}
static void
ehci_sendup_qtd_message(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_trans_wrapper_t *tw,
ehci_qtd_t *qtd,
usb_cr_t error)
{
usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_opaque_t curr_xfer_reqp = tw->tw_curr_xfer_reqp;
size_t skip_len = 0;
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_qtd_message:");
ASSERT(tw != NULL);
length = tw->tw_length;
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_CONTROL) {
if (((usb_ctrl_req_t *)curr_xfer_reqp)->ctrl_wLength)
length = length - EHCI_MAX_QTD_BUF_SIZE;
else
length = length - SETUP_SIZE;
skip_len = EHCI_MAX_QTD_BUF_SIZE;
}
buf = (uchar_t *)tw->tw_buf + skip_len;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_sendup_qtd_message: length %ld error %d", length, error);
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
mp = ((usb_ctrl_req_t *)curr_xfer_reqp)->ctrl_data;
break;
case USB_EP_ATTR_BULK:
mp = ((usb_bulk_req_t *)curr_xfer_reqp)->bulk_data;
break;
case USB_EP_ATTR_INTR:
mp = ((usb_intr_req_t *)curr_xfer_reqp)->intr_data;
break;
case USB_EP_ATTR_ISOCH:
mp = NULL;
break;
}
ASSERT(mp != NULL);
if (length) {
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_CONTROL) {
ehci_do_byte_stats(ehcip, length,
eptd->bmAttributes, USB_EP_DIR_IN);
} else {
ehci_do_byte_stats(ehcip, length,
eptd->bmAttributes, eptd->bEndpointAddress);
}
Sync_IO_Buffer(tw->tw_dmahandle, (skip_len + length));
bcopy(buf, mp->b_rptr, length);
mp->b_wptr = mp->b_wptr + length;
} else {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_sendup_qtd_message: Zero length packet");
}
ehci_hcdi_callback(ph, tw, error);
}