#include <sys/usb/hcd/openhci/ohcid.h>
#include <sys/usb/hcd/openhci/ohci_polled.h>
static int ohci_polled_init(
usba_pipe_handle_data_t *ph,
ohci_state_t *ohcip,
usb_console_info_impl_t *console_input_info);
static int ohci_polled_fini(ohci_polled_t *ohci_polledp);
static void ohci_polled_save_state(ohci_polled_t *ohci_polledp);
static void ohci_polled_stop_processing(
ohci_polled_t *ohci_polledp);
static void ohci_polled_restore_state(ohci_polled_t *ohci_polledp);
static void ohci_polled_start_processing(
ohci_polled_t *ohci_polledp);
static ohci_td_t *ohci_polled_pickup_done_list(
ohci_polled_t *ohci_polledp,
ohci_td_t *done_head);
static int ohci_polled_check_done_list(
ohci_polled_t *ohci_polledp);
static void ohci_polled_create_input_list(
ohci_polled_t *ohci_polledp,
ohci_td_t *head_done_list);
static int ohci_polled_process_input_list(
ohci_polled_t *ohci_polledp);
static int ohci_polled_handle_normal_td(
ohci_polled_t *ohci_polledp,
ohci_td_t *td);
static void ohci_polled_insert_td(ohci_state_t *ohcip,
ohci_td_t *td);
static void ohci_polled_fill_in_td(ohci_state_t *ohcip,
ohci_td_t *td,
ohci_td_t *new_dummy,
uint_t hctd_ctrl,
uint32_t hctd_iommu_cbp,
size_t hctd_length,
ohci_trans_wrapper_t *tw);
static void ohci_polled_insert_td_on_tw(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
ohci_td_t *td);
static void ohci_polled_handle_frame_number_overflow(
ohci_state_t *ohcip);
static void ohci_polled_finish_interrupt(
ohci_state_t *ohcip,
uint_t intr);
static void ohci_polled_insert_bulk_td(
ohci_polled_t *ohci_polledp);
static int ohci_polled_create_tw(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags);
static int ohci_polled_insert_hc_td(
ohci_state_t *ohcip,
uint_t hctd_ctrl,
uint32_t hctd_dma_offs,
size_t hctd_length,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw);
int
ohci_hcdi_polled_input_init(
usba_pipe_handle_data_t *ph,
uchar_t **polled_buf,
usb_console_info_impl_t *console_input_info)
{
ohci_polled_t *ohci_polledp;
ohci_state_t *ohcip;
int pipe_attr, ret;
ohcip = ohci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
mutex_enter(&ohcip->ohci_int_mutex);
ret = ohci_polled_init(ph, ohcip, console_input_info);
if (ret != USB_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
return (ret);
}
ohci_polledp = (ohci_polled_t *)console_input_info->uci_private;
ohci_polledp->ohci_polled_flags |= POLLED_INPUT_MODE;
ohcip->ohci_polled_kbd_count ++;
ohci_polledp->ohci_polled_buf =
(uchar_t *)kmem_zalloc(POLLED_RAW_BUF_SIZE, KM_SLEEP);
*polled_buf = ohci_polledp->ohci_polled_buf;
pipe_attr = ohci_polledp->ohci_polled_input_pipe_handle->
p_ep.bmAttributes & USB_EP_ATTR_MASK;
if (pipe_attr == USB_EP_ATTR_BULK) {
ohci_polled_insert_bulk_td(ohci_polledp);
}
if (ddi_prop_exists(DDI_DEV_T_ANY, ohcip->ohci_dip,
DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
ohci_polledp->ohci_polled_no_sync_flag = B_TRUE;
}
mutex_exit(&ohcip->ohci_int_mutex);
return (USB_SUCCESS);
}
int
ohci_hcdi_polled_input_fini(usb_console_info_impl_t *info)
{
ohci_polled_t *ohci_polledp;
ohci_state_t *ohcip;
int ret;
ohci_polledp = (ohci_polled_t *)info->uci_private;
ohcip = ohci_polledp->ohci_polled_ohcip;
mutex_enter(&ohcip->ohci_int_mutex);
ohci_polledp->ohci_polled_flags &= ~POLLED_INPUT_MODE;
ohcip->ohci_polled_kbd_count --;
kmem_free(ohci_polledp->ohci_polled_buf, POLLED_RAW_BUF_SIZE);
ret = ohci_polled_fini(ohci_polledp);
mutex_exit(&ohcip->ohci_int_mutex);
return (ret);
}
int
ohci_hcdi_polled_input_enter(usb_console_info_impl_t *info)
{
ohci_polled_t *ohci_polledp;
ohci_polledp = (ohci_polled_t *)info->uci_private;
ohci_polledp->ohci_polled_entry++;
if (ohci_polledp->ohci_polled_entry > 1) {
return (USB_SUCCESS);
}
ohci_polled_save_state(ohci_polledp);
ohci_polledp->ohci_polled_flags |= POLLED_INPUT_MODE_INUSE;
return (USB_SUCCESS);
}
int
ohci_hcdi_polled_input_exit(usb_console_info_impl_t *info)
{
ohci_polled_t *ohci_polledp;
ohci_polledp = (ohci_polled_t *)info->uci_private;
ohci_polledp->ohci_polled_entry--;
if (ohci_polledp->ohci_polled_entry > 0)
return (USB_SUCCESS);
ohci_polledp->ohci_polled_flags &= ~POLLED_INPUT_MODE_INUSE;
ohci_polled_restore_state(ohci_polledp);
return (USB_SUCCESS);
}
int
ohci_hcdi_polled_read(
usb_console_info_impl_t *info,
uint_t *num_characters)
{
ohci_state_t *ohcip;
ohci_polled_t *ohci_polledp;
uint_t intr;
ohci_polledp = (ohci_polled_t *)info->uci_private;
ohcip = ohci_polledp->ohci_polled_ohcip;
#ifndef lint
_NOTE(NO_COMPETING_THREADS_NOW);
#endif
*num_characters = 0;
intr = (Get_OpReg(hcr_intr_status) & Get_OpReg(hcr_intr_enable));
if (intr & HCR_INTR_FNO) {
ohci_handle_frame_number_overflow(ohcip);
ohci_polled_finish_interrupt(ohcip, HCR_INTR_FNO);
}
if (ohci_polled_check_done_list(ohci_polledp) == USB_SUCCESS) {
*num_characters =
ohci_polled_process_input_list(ohci_polledp);
}
if (intr & HCR_INTR_WDH) {
ohci_polled_finish_interrupt(ohcip, HCR_INTR_WDH);
}
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return (USB_SUCCESS);
}
int
ohci_hcdi_polled_output_init(
usba_pipe_handle_data_t *ph,
usb_console_info_impl_t *console_output_info)
{
ohci_polled_t *ohci_polledp;
ohci_state_t *ohcip;
int ret;
ohcip = ohci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
mutex_enter(&ohcip->ohci_int_mutex);
ret = ohci_polled_init(ph, ohcip, console_output_info);
if (ret != USB_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
return (ret);
}
ohci_polledp = (ohci_polled_t *)console_output_info->uci_private;
ohci_polledp->ohci_polled_flags |= POLLED_OUTPUT_MODE;
if (ddi_prop_exists(DDI_DEV_T_ANY, ohcip->ohci_dip,
DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
ohci_polledp->ohci_polled_no_sync_flag = B_TRUE;
}
mutex_exit(&ohcip->ohci_int_mutex);
return (USB_SUCCESS);
}
int
ohci_hcdi_polled_output_fini(usb_console_info_impl_t *info)
{
ohci_polled_t *ohci_polledp;
ohci_state_t *ohcip;
int ret;
ohci_polledp = (ohci_polled_t *)info->uci_private;
ohcip = ohci_polledp->ohci_polled_ohcip;
mutex_enter(&ohcip->ohci_int_mutex);
ohci_polledp->ohci_polled_flags &= ~POLLED_OUTPUT_MODE;
ret = ohci_polled_fini(ohci_polledp);
info->uci_private = NULL;
mutex_exit(&ohcip->ohci_int_mutex);
return (ret);
}
int
ohci_hcdi_polled_output_enter(usb_console_info_impl_t *info)
{
return (USB_SUCCESS);
}
int
ohci_hcdi_polled_output_exit(usb_console_info_impl_t *info)
{
return (USB_SUCCESS);
}
int
ohci_hcdi_polled_write(usb_console_info_impl_t *info, uchar_t *buf,
uint_t num_characters, uint_t *num_characters_written)
{
ohci_state_t *ohcip;
ohci_polled_t *ohci_polledp;
ohci_trans_wrapper_t *tw;
ohci_pipe_private_t *pp;
usba_pipe_handle_data_t *ph;
uint32_t ctrl;
uint_t intr, bulk_pkg_size;
int i;
#ifndef lint
_NOTE(NO_COMPETING_THREADS_NOW);
#endif
ohci_polledp = (ohci_polled_t *)info->uci_private;
ohcip = ohci_polledp->ohci_polled_ohcip;
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) & (~HCR_CONTROL_PLE)));
for (i = ohcip->ohci_polled_enter_count; i < NUM_INTR_ED_LISTS;
i = i + MIN_LOW_SPEED_POLL_INTERVAL) {
Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
ohci_ed_cpu_to_iommu(ohcip,
ohci_polledp->ohci_polled_ed));
}
ph = ohci_polledp->ohci_polled_input_pipe_handle;
pp = (ohci_pipe_private_t *)ph->p_hcd_private;
tw = pp->pp_tw_head;
ASSERT(tw != NULL);
if (tw->tw_hctd_free_list == NULL) {
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return (USB_SUCCESS);
}
if (num_characters > POLLED_RAW_BUF_SIZE) {
cmn_err(CE_NOTE, "polled write size %d bigger than %d",
num_characters, POLLED_RAW_BUF_SIZE);
num_characters = POLLED_RAW_BUF_SIZE;
}
tw->tw_length = num_characters;
ddi_rep_put8(tw->tw_accesshandle,
buf, (uint8_t *)tw->tw_buf,
tw->tw_length, DDI_DEV_AUTOINCR);
Sync_IO_Buffer_for_device(tw->tw_dmahandle, tw->tw_length);
ctrl = tw->tw_direction | HC_TD_DT_0|HC_TD_1I | HC_TD_R;
bulk_pkg_size = min(tw->tw_length, OHCI_MAX_TD_XFER_SIZE);
(void) ohci_polled_insert_hc_td(ohcip, ctrl, 0, bulk_pkg_size, pp, tw);
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) | HCR_CONTROL_PLE));
for (;;) {
intr = Get_OpReg(hcr_intr_status);
if (intr & HCR_INTR_FNO) {
ohci_handle_frame_number_overflow(ohcip);
ohci_polled_finish_interrupt(ohcip, HCR_INTR_FNO);
}
if (intr & HCR_INTR_WDH) {
if (ohci_polled_check_done_list(ohci_polledp) ==
USB_SUCCESS) {
*num_characters_written =
ohci_polled_process_input_list(
ohci_polledp);
break;
}
}
Set_OpReg(hcr_intr_status, intr);
(void) Get_OpReg(hcr_intr_status);
}
for (i = ohcip->ohci_polled_enter_count; i < NUM_INTR_ED_LISTS;
i = i + MIN_LOW_SPEED_POLL_INTERVAL) {
Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
ohci_ed_cpu_to_iommu(ohcip,
ohci_polledp->ohci_polled_dummy_ed));
}
Set_OpReg(hcr_intr_status, intr);
(void) Get_OpReg(hcr_intr_status);
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return (USB_SUCCESS);
}
static int
ohci_polled_init(
usba_pipe_handle_data_t *ph,
ohci_state_t *ohcip,
usb_console_info_impl_t *console_info)
{
ohci_polled_t *ohci_polledp;
ohci_pipe_private_t *pp;
int pipe_attr;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (console_info->uci_private) {
return (USB_SUCCESS);
}
ohci_polledp = (ohci_polled_t *)
kmem_zalloc(sizeof (ohci_polled_t), KM_SLEEP);
console_info->uci_private = (usb_console_info_private_t)ohci_polledp;
ohci_polledp->ohci_polled_ohcip = ohcip;
mutex_enter(&ph->p_mutex);
ohci_polledp->ohci_polled_usb_dev = ph->p_usba_device;
ohci_polledp->ohci_polled_ep_addr = ph->p_ep.bEndpointAddress;
mutex_exit(&ph->p_mutex);
ohci_polledp->ohci_polled_input_pipe_handle =
kmem_zalloc(sizeof (usba_pipe_handle_data_t), KM_SLEEP);
bcopy((void *)ph,
(void *)ohci_polledp->ohci_polled_input_pipe_handle,
sizeof (usba_pipe_handle_data_t));
mutex_init(&ohci_polledp->ohci_polled_input_pipe_handle->p_mutex,
NULL, MUTEX_DRIVER, DDI_INTR_PRI(ohcip->ohci_intr_pri));
pp = (ohci_pipe_private_t *)
kmem_zalloc(sizeof (ohci_pipe_private_t), KM_SLEEP);
mutex_enter(&ohci_polledp->ohci_polled_input_pipe_handle->p_mutex);
ohci_polledp->ohci_polled_input_pipe_handle->
p_hcd_private = (usb_opaque_t)pp;
mutex_exit(&ohci_polledp->ohci_polled_input_pipe_handle->p_mutex);
mutex_enter(&ph->p_mutex);
bcopy(&ph->p_policy, &pp->pp_policy, sizeof (usb_pipe_policy_t));
mutex_exit(&ph->p_mutex);
pp->pp_pipe_handle = ohci_polledp->ohci_polled_input_pipe_handle;
ohci_polledp->ohci_polled_dummy_ed = ohci_alloc_hc_ed(ohcip, NULL);
if (ohci_polledp->ohci_polled_dummy_ed == NULL) {
return (USB_NO_RESOURCES);
}
ohci_polledp->ohci_polled_ed = ohci_alloc_hc_ed(ohcip,
ohci_polledp->ohci_polled_input_pipe_handle);
if (ohci_polledp->ohci_polled_ed == NULL) {
return (USB_NO_RESOURCES);
}
pp->pp_state = OHCI_PIPE_STATE_IDLE;
pp->pp_ept = ohci_polledp->ohci_polled_ed;
pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
switch (pipe_attr) {
case USB_EP_ATTR_INTR:
mutex_enter(&ph->p_mutex);
ph->p_spec_flag |= USBA_PH_FLAG_USE_SOFT_INTR;
mutex_exit(&ph->p_mutex);
if ((ohci_start_periodic_pipe_polling(ohcip,
ohci_polledp->ohci_polled_input_pipe_handle,
NULL, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
return (USB_NO_RESOURCES);
}
break;
case USB_EP_ATTR_BULK:
if ((ohci_polled_create_tw(ohcip,
ohci_polledp->ohci_polled_input_pipe_handle,
USB_FLAGS_SLEEP)) != USB_SUCCESS) {
return (USB_NO_RESOURCES);
}
break;
default:
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static int
ohci_polled_fini(ohci_polled_t *ohci_polledp)
{
ohci_state_t *ohcip = ohci_polledp->ohci_polled_ohcip;
ohci_pipe_private_t *pp;
ohci_td_t *curr_td, *next_td;
ohci_trans_wrapper_t *curr_tw, *next_tw;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (ohci_polledp->ohci_polled_flags & POLLED_INPUT_MODE) {
return (USB_SUCCESS);
}
pp = (ohci_pipe_private_t *)
ohci_polledp->ohci_polled_input_pipe_handle->p_hcd_private;
ohci_handle_outstanding_requests(ohcip, pp);
ohci_traverse_tds(ohcip, pp->pp_pipe_handle);
next_tw = pp->pp_tw_head;
while (next_tw) {
next_td = (ohci_td_t *)next_tw->tw_hctd_head;
while (next_td) {
curr_td = next_td;
next_td = ohci_td_iommu_to_cpu(ohcip,
Get_TD(next_td->hctd_tw_next_td));
ohci_deallocate_td(ohcip, curr_td);
}
curr_tw = next_tw;
next_tw = curr_tw->tw_next;
ohci_deallocate_tw_resources(ohcip, pp, curr_tw);
}
if (ohci_polledp->ohci_polled_dummy_ed) {
ohci_deallocate_ed(ohcip, ohci_polledp->ohci_polled_dummy_ed);
}
if (ohci_polledp->ohci_polled_ed) {
ohci_deallocate_ed(ohcip, ohci_polledp->ohci_polled_ed);
}
mutex_destroy(&ohci_polledp->ohci_polled_input_pipe_handle->p_mutex);
kmem_free(pp, sizeof (ohci_pipe_private_t));
kmem_free(ohci_polledp->ohci_polled_input_pipe_handle,
sizeof (usba_pipe_handle_data_t));
ohci_polledp->ohci_polled_input_pipe_handle = NULL;
kmem_free(ohci_polledp, sizeof (ohci_polled_t));
return (USB_SUCCESS);
}
static void
ohci_polled_save_state(ohci_polled_t *ohci_polledp)
{
ohci_state_t *ohcip;
int i;
uint_t polled_toggle;
uint_t real_toggle;
ohci_pipe_private_t *pp = NULL;
ohci_pipe_private_t *polled_pp;
usba_pipe_handle_data_t *ph;
uint8_t ep_addr;
ohci_save_intr_sts_t *ohci_intr_sts;
ohci_regs_t *ohci_polled_regsp;
ohci_td_t *td, *prev_td;
ohci_td_t *done_head, **done_list;
#ifndef lint
_NOTE(NO_COMPETING_THREADS_NOW);
#endif
if (ohci_polledp->ohci_polled_flags & POLLED_INPUT_MODE_INUSE) {
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return;
}
ohcip = ohci_polledp->ohci_polled_ohcip;
if (++ ohcip->ohci_polled_enter_count > MAX_NUM_FOR_KEYBOARD) {
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return;
}
ep_addr = ohci_polledp->ohci_polled_ep_addr;
ph = usba_hcdi_get_ph_data(ohci_polledp->ohci_polled_usb_dev, ep_addr);
ohci_intr_sts = &ohcip->ohci_save_intr_sts;
ohci_polled_regsp = &ohcip->ohci_polled_save_regs;
if (ohcip->ohci_polled_enter_count == 1) {
Set_OpReg(hcr_intr_disable, HCR_INTR_MIE);
bzero((void *)ohci_polled_regsp, sizeof (ohci_regs_t));
ohci_polled_regsp->hcr_control = Get_OpReg(hcr_control);
ohci_polled_regsp->hcr_cmd_status = Get_OpReg(hcr_cmd_status);
ohci_polled_regsp->hcr_intr_enable = Get_OpReg(hcr_intr_enable);
ohci_polled_regsp->hcr_HCCA = Get_OpReg(hcr_HCCA);
ohci_polled_regsp->hcr_done_head = Get_OpReg(hcr_done_head);
ohci_polled_regsp->hcr_bulk_head = Get_OpReg(hcr_bulk_head);
ohci_polled_regsp->hcr_ctrl_head = Get_OpReg(hcr_ctrl_head);
if ((ohci_intr_sts->ohci_intr_flag & OHCI_INTR_CRITICAL) &&
(ohci_intr_sts->ohci_critical_intr_sts != 0)) {
ohci_intr_sts->ohci_critical_intr_sts |=
((Get_OpReg(hcr_intr_status) &
Get_OpReg(hcr_intr_enable)) & HCR_INTR_SOF);
} else {
ohci_intr_sts->ohci_missed_intr_sts |=
((Get_OpReg(hcr_intr_status) &
Get_OpReg(hcr_intr_enable)) & HCR_INTR_SOF);
}
ohci_polled_stop_processing(ohci_polledp);
ohci_polled_handle_frame_number_overflow(ohcip);
done_head = (ohci_td_t *)(uintptr_t)(Get_HCCA(
ohcip->ohci_hccap->HccaDoneHead) & HCCA_DONE_HEAD_MASK);
if ((done_head) &&
(done_head != ohci_intr_sts->ohci_curr_done_lst)) {
if ((ohci_intr_sts->ohci_intr_flag &
OHCI_INTR_CRITICAL) &&
((ohci_intr_sts->ohci_critical_done_lst) ||
(ohci_intr_sts->ohci_missed_done_lst == NULL))) {
done_list =
&ohci_intr_sts->ohci_critical_done_lst;
ohci_intr_sts->ohci_critical_intr_sts |=
HCR_INTR_WDH;
} else {
done_list =
&ohci_intr_sts->ohci_missed_done_lst;
ohci_intr_sts->ohci_missed_intr_sts |=
HCR_INTR_WDH;
}
if (*done_list) {
td = (ohci_td_t *)
ohci_td_iommu_to_cpu(ohcip,
(uintptr_t)done_head);
while (td) {
prev_td = td;
td = ohci_td_iommu_to_cpu(ohcip,
Get_TD(td->hctd_next_td));
}
Set_TD(prev_td->hctd_next_td, *done_list);
*done_list = done_head;
} else {
*done_list = (ohci_td_t *)done_head;
}
}
ohci_polled_regsp->hcr_done_head = Get_OpReg(hcr_done_head);
Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, 0);
Set_OpReg(hcr_done_head, 0);
Set_OpReg(hcr_intr_status, HCR_INTR_WDH);
for (i = 0; i < NUM_INTR_ED_LISTS; i++) {
ohcip->ohci_polled_save_IntTble[i] =
(ohci_ed_t *)(uintptr_t)Get_HCCA(
ohcip->ohci_hccap->HccaIntTble[i]);
}
for (i = 0; i < NUM_INTR_ED_LISTS; i++) {
Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
ohci_ed_cpu_to_iommu(ohcip,
ohci_polledp->ohci_polled_dummy_ed));
}
}
polled_pp = (ohci_pipe_private_t *)
ohci_polledp->ohci_polled_input_pipe_handle->p_hcd_private;
polled_toggle = (Get_ED(polled_pp->pp_ept->hced_headp) &
HC_EPT_Carry) ? DATA1:DATA0;
if (ph) {
pp = (ohci_pipe_private_t *)ph->p_hcd_private;
real_toggle = (Get_ED(pp->pp_ept->hced_headp) &
HC_EPT_Carry) ? DATA1:DATA0;
} else {
real_toggle = usba_hcdi_get_data_toggle(
ohci_polledp->ohci_polled_usb_dev, ep_addr);
}
if (polled_toggle != real_toggle) {
if (real_toggle == DATA0) {
Set_ED(polled_pp->pp_ept->hced_headp,
Get_ED(polled_pp->pp_ept->hced_headp) &
~HC_EPT_Carry);
} else {
Set_ED(polled_pp->pp_ept->hced_headp,
Get_ED(polled_pp->pp_ept->hced_headp) |
HC_EPT_Carry);
}
}
if (polled_pp->pp_ept->hced_headp & HC_EPT_Halt) {
Set_ED(polled_pp->pp_ept->hced_headp,
(Get_ED(polled_pp->pp_ept->hced_headp) & ~HC_EPT_Halt));
}
for (i = (ohcip->ohci_polled_enter_count -1); i < NUM_INTR_ED_LISTS;
i = i + MIN_LOW_SPEED_POLL_INTERVAL) {
Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
ohci_ed_cpu_to_iommu(ohcip,
ohci_polledp->ohci_polled_ed));
}
if (ohcip->ohci_polled_enter_count == 1) {
Set_OpReg(hcr_periodic_curr, (uint32_t)0x0);
Set_OpReg(hcr_intr_enable, HCR_INTR_WDH);
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) | HCR_CONTROL_PLE));
}
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
}
static void
ohci_polled_stop_processing(ohci_polled_t *ohci_polledp)
{
ohci_state_t *ohcip;
uint_t count;
ohci_regs_t *ohci_polled_regsp;
ohcip = ohci_polledp->ohci_polled_ohcip;
ohci_polled_regsp = &ohcip->ohci_polled_save_regs;
Set_OpReg(hcr_control,
(ohci_polled_regsp->hcr_control) & ~(HCR_CONTROL_CLE|
HCR_CONTROL_PLE| HCR_CONTROL_BLE|HCR_CONTROL_IE));
Set_OpReg(hcr_intr_status, HCR_INTR_SOF);
Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
for (count = 0; count <= DONE_QUEUE_INTR_COUNTER; count++) {
while (!((Get_OpReg(hcr_intr_status)) & HCR_INTR_SOF)) {
continue;
}
ohci_polled_finish_interrupt(ohcip, HCR_INTR_SOF);
}
Set_OpReg(hcr_intr_disable, HCR_INTR_SOF);
}
static void
ohci_polled_restore_state(ohci_polled_t *ohci_polledp)
{
ohci_state_t *ohcip;
int i;
uint_t polled_toggle;
uint_t real_toggle;
ohci_pipe_private_t *pp = NULL;
ohci_pipe_private_t *polled_pp;
ohci_td_t *td;
ohci_td_t *next_td;
uint_t count;
ohci_save_intr_sts_t *ohci_intr_sts;
ohci_regs_t *ohci_polled_regsp;
uint32_t mask;
usba_pipe_handle_data_t *ph;
uint8_t ep_addr;
#ifndef lint
_NOTE(NO_COMPETING_THREADS_NOW);
#endif
if (ohci_polledp->ohci_polled_flags & POLLED_INPUT_MODE_INUSE) {
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return;
}
ohcip = ohci_polledp->ohci_polled_ohcip;
ohci_intr_sts = &ohcip->ohci_save_intr_sts;
ohci_polled_regsp = &ohcip->ohci_polled_save_regs;
ohcip->ohci_polled_enter_count --;
ep_addr = ohci_polledp->ohci_polled_ep_addr;
ph = usba_hcdi_get_ph_data(ohci_polledp->ohci_polled_usb_dev, ep_addr);
if (Get_OpReg(hcr_control) & HCR_CONTROL_PLE) {
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) & ~HCR_CONTROL_PLE));
}
if (ohcip->ohci_polled_enter_count == 0) {
Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
for (count = 0; count <= DONE_QUEUE_INTR_COUNTER; count++) {
while (!((Get_OpReg(hcr_intr_status)) & HCR_INTR_SOF)) {
continue;
}
ohci_polled_finish_interrupt(ohcip, HCR_INTR_SOF);
}
ohci_polled_handle_frame_number_overflow(ohcip);
td = ohci_td_iommu_to_cpu(ohcip,
(uintptr_t)Get_OpReg(hcr_done_head));
while (td) {
next_td = ohci_td_iommu_to_cpu(ohcip,
Get_TD(td->hctd_next_td));
ohci_polled_insert_td(ohcip, td);
td = next_td;
}
td = ohci_td_iommu_to_cpu(ohcip, (Get_HCCA(
ohcip->ohci_hccap->HccaDoneHead) & HCCA_DONE_HEAD_MASK));
while (td) {
next_td = ohci_td_iommu_to_cpu(ohcip,
Get_TD(td->hctd_next_td));
ohci_polled_insert_td(ohcip, td);
td = next_td;
}
Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, 0);
Set_OpReg(hcr_done_head,
(uint32_t)ohci_polled_regsp->hcr_done_head);
Set_OpReg(hcr_intr_status, (HCR_INTR_WDH | HCR_INTR_SOF));
}
polled_pp = (ohci_pipe_private_t *)
ohci_polledp->ohci_polled_input_pipe_handle->p_hcd_private;
polled_toggle = (Get_ED(polled_pp->pp_ept->hced_headp) &
HC_EPT_Carry) ? DATA1:DATA0;
if (ph) {
pp = (ohci_pipe_private_t *)ph->p_hcd_private;
real_toggle = (Get_ED(pp->pp_ept->hced_headp) &
HC_EPT_Carry) ? DATA1:DATA0;
if (polled_toggle != real_toggle) {
if (polled_toggle == DATA0) {
Set_ED(pp->pp_ept->hced_headp,
Get_ED(pp->pp_ept->hced_headp) &
~HC_EPT_Carry);
} else {
Set_ED(pp->pp_ept->hced_headp,
Get_ED(pp->pp_ept->hced_headp) |
HC_EPT_Carry);
}
}
} else {
usba_hcdi_set_data_toggle(ohci_polledp->ohci_polled_usb_dev,
ep_addr, polled_toggle);
}
if (ohcip->ohci_polled_enter_count == 0) {
for (i = 0; i < NUM_INTR_ED_LISTS; i++) {
Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
(uintptr_t)ohcip->ohci_polled_save_IntTble[i]);
}
Set_OpReg(hcr_periodic_curr, (uint32_t)0x0);
ohci_polled_start_processing(ohci_polledp);
mask = (uint32_t)ohci_polled_regsp->hcr_intr_enable &
(HCR_INTR_SOF | HCR_INTR_WDH);
if (ohci_intr_sts->ohci_intr_flag & OHCI_INTR_HANDLING) {
Set_OpReg(hcr_intr_enable, mask);
} else {
Set_OpReg(hcr_intr_enable, mask | HCR_INTR_MIE);
}
}
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
}
static void
ohci_polled_start_processing(ohci_polled_t *ohci_polledp)
{
ohci_state_t *ohcip;
uint32_t control;
uint32_t mask;
ohci_regs_t *ohci_polled_regsp;
ohcip = ohci_polledp->ohci_polled_ohcip;
ohci_polled_regsp = &ohcip->ohci_polled_save_regs;
mask = ((uint32_t)ohci_polled_regsp->hcr_control) & (HCR_CONTROL_CLE |
HCR_CONTROL_PLE | HCR_CONTROL_BLE | HCR_CONTROL_IE);
control = Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
HCR_CONTROL_PLE | HCR_CONTROL_BLE | HCR_CONTROL_IE);
Set_OpReg(hcr_control, (control | mask));
}
static int
ohci_polled_check_done_list(ohci_polled_t *ohci_polledp)
{
ohci_state_t *ohcip = ohci_polledp->ohci_polled_ohcip;
ohci_td_t *done_head, *done_list;
if (ohci_polledp->ohci_polled_no_sync_flag == B_FALSE) {
Sync_HCCA(ohcip);
}
done_head = (ohci_td_t *)(uintptr_t)
(Get_HCCA(ohcip->ohci_hccap->HccaDoneHead) & HCCA_DONE_HEAD_MASK);
if (done_head == NULL) {
if (ohcip->ohci_polled_done_list) {
done_head = ohcip->ohci_polled_done_list;
ohcip->ohci_polled_done_list = NULL;
} else {
return (USB_FAILURE);
}
} else {
Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, 0);
}
if (ohci_polledp->ohci_polled_no_sync_flag == B_FALSE) {
Sync_ED_TD_Pool(ohcip);
}
done_list = ohci_polled_pickup_done_list(ohci_polledp, done_head);
if (done_list == NULL) {
return (USB_FAILURE);
}
ohci_polled_create_input_list(ohci_polledp, done_list);
return (USB_SUCCESS);
}
static ohci_td_t *
ohci_polled_pickup_done_list(
ohci_polled_t *ohci_polledp,
ohci_td_t *done_head)
{
ohci_state_t *ohcip = ohci_polledp->ohci_polled_ohcip;
ohci_td_t *create_head = NULL, *current_td, *td;
ohci_trans_wrapper_t *tw;
ohci_pipe_private_t *pp;
current_td = (ohci_td_t *)
ohci_td_iommu_to_cpu(ohcip, (uintptr_t)done_head);
while (current_td) {
td = (ohci_td_t *)ohci_td_iommu_to_cpu(ohcip,
Get_TD(current_td->hctd_next_td));
Set_TD(current_td->hctd_next_td, NULL);
tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
(uint32_t)Get_TD(current_td->hctd_trans_wrapper));
pp = tw->tw_pipe_private;
if (pp->pp_pipe_handle ==
ohci_polledp->ohci_polled_input_pipe_handle) {
if (create_head == NULL) {
create_head = current_td;
} else {
Set_TD(current_td->hctd_next_td,
ohci_td_cpu_to_iommu(ohcip, create_head));
create_head = current_td;
}
} else {
if (ohcip->ohci_polled_done_list == NULL) {
ohcip->ohci_polled_done_list = (ohci_td_t *)
(uintptr_t)ohci_td_cpu_to_iommu(ohcip,
current_td);
} else {
Set_TD(current_td->hctd_next_td,
ohcip->ohci_polled_done_list);
ohcip->ohci_polled_done_list = (ohci_td_t *)
(uintptr_t)ohci_td_cpu_to_iommu(ohcip,
current_td);
}
}
current_td = td;
}
return (create_head);
}
static void
ohci_polled_create_input_list(
ohci_polled_t *ohci_polledp,
ohci_td_t *head_done_list)
{
ohci_state_t *ohcip = ohci_polledp->ohci_polled_ohcip;
ohci_td_t *cpu_save, *td;
ASSERT(head_done_list != NULL);
td = (ohci_td_t *)head_done_list;
while (td) {
cpu_save = ohci_td_iommu_to_cpu(ohcip,
Get_TD(td->hctd_next_td));
Set_TD(td->hctd_next_td, NULL);
if (ohci_polledp->ohci_polled_input_done_head == NULL) {
ohci_polledp->ohci_polled_input_done_head = td;
} else {
Set_TD(ohci_polledp->
ohci_polled_input_done_tail->hctd_next_td,
ohci_td_cpu_to_iommu(ohcip, td));
}
ohci_polledp->ohci_polled_input_done_tail = td;
td = cpu_save;
}
}
static int
ohci_polled_process_input_list(ohci_polled_t *ohci_polledp)
{
ohci_state_t *ohcip = ohci_polledp->ohci_polled_ohcip;
ohci_td_t *td, *next_td;
uint_t ctrl;
uint_t num_characters;
ohci_trans_wrapper_t *tw;
ohci_pipe_private_t *pp;
int pipe_dir;
td = ohci_polledp->ohci_polled_input_done_head;
ohci_polledp->ohci_polled_input_done_head = NULL;
num_characters = 0;
while (td) {
next_td = (ohci_td_t *)
ohci_td_iommu_to_cpu(ohcip, Get_TD(td->hctd_next_td));
ctrl = (uint_t)Get_TD(td->hctd_ctrl) & (uint32_t)HC_TD_CC;
if (ctrl != HC_TD_CC_NO_E) {
tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
(uint32_t)Get_TD(td->hctd_trans_wrapper));
pp = tw->tw_pipe_private;
Set_ED(pp->pp_ept->hced_headp,
(Get_ED(pp->pp_ept->hced_headp) & ~HC_EPT_Halt));
}
tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
(uint32_t)Get_TD(td->hctd_trans_wrapper));
pipe_dir = tw->tw_pipe_private->pp_pipe_handle->
p_ep.bEndpointAddress & USB_EP_DIR_MASK;
switch (pipe_dir) {
case USB_EP_DIR_IN:
num_characters +=
ohci_polled_handle_normal_td(ohci_polledp,
td);
ohci_polled_insert_td(ohcip, td);
break;
case USB_EP_DIR_OUT:
ASSERT((ohci_td_t *)tw->tw_hctd_head == td);
tw->tw_hctd_head = (ohci_td_t *)
ohci_td_iommu_to_cpu(ohcip,
Get_TD(td->hctd_tw_next_td));
Set_TD(td->hctd_state, HC_TD_DUMMY);
if (tw->tw_hctd_head == NULL) {
tw->tw_hctd_tail = NULL;
}
if (tw->tw_hctd_free_list != NULL) {
uint32_t td_addr;
td_addr = ohci_td_cpu_to_iommu(ohcip,
tw->tw_hctd_free_list);
Set_TD(td->hctd_tw_next_td, td_addr);
tw->tw_hctd_free_list = td;
} else {
tw->tw_hctd_free_list = td;
Set_TD(td->hctd_tw_next_td, NULL);
}
break;
}
td = next_td;
}
return (num_characters);
}
static int
ohci_polled_handle_normal_td(
ohci_polled_t *ohci_polledp,
ohci_td_t *td)
{
ohci_state_t *ohcip = ohci_polledp->ohci_polled_ohcip;
uchar_t *buf;
ohci_trans_wrapper_t *tw;
size_t length, residue;
tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID((uint32_t)
Get_TD(td->hctd_trans_wrapper));
ASSERT(tw != NULL);
buf = (uchar_t *)tw->tw_buf;
length = tw->tw_length;
if (Get_TD(td->hctd_cbp)) {
residue = ohci_get_td_residue(ohcip, td);
length = Get_TD(td->hctd_xfer_offs) +
Get_TD(td->hctd_xfer_len) - residue;
}
if (ohci_polledp->ohci_polled_no_sync_flag == B_FALSE) {
Sync_IO_Buffer(tw->tw_dmahandle, length);
}
ddi_rep_get8(tw->tw_accesshandle,
(uint8_t *)ohci_polledp->ohci_polled_buf,
(uint8_t *)buf, length, DDI_DEV_AUTOINCR);
return ((int)length);
}
static void
ohci_polled_insert_td(
ohci_state_t *ohcip,
ohci_td_t *td)
{
ohci_pipe_private_t *pp;
ohci_ed_t *ept;
uint_t td_control;
ohci_trans_wrapper_t *tw;
ohci_td_t *cpu_current_dummy;
usb_intr_req_t *intr_req;
usba_pipe_handle_data_t *ph;
int pipe_attr;
tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
(uint32_t)Get_TD(td->hctd_trans_wrapper));
ASSERT((tw->tw_cookie_idx == 0) && (tw->tw_dma_offs == 0));
ASSERT((ohci_td_t *)tw->tw_hctd_head == td);
tw->tw_hctd_head = (ohci_td_t *)
ohci_td_iommu_to_cpu(ohcip, Get_TD(td->hctd_tw_next_td));
if (tw->tw_hctd_head == NULL) {
tw->tw_hctd_tail = NULL;
}
bzero((char *)td, sizeof (ohci_td_t));
Set_TD(td->hctd_state, HC_TD_DUMMY);
pp = tw->tw_pipe_private;
ph = pp->pp_pipe_handle;
ept = pp->pp_ept;
pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
switch (pipe_attr) {
case USB_EP_ATTR_INTR:
intr_req = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
if (intr_req->intr_attributes & USB_ATTRS_SHORT_XFER_OK) {
td_control = HC_TD_IN|HC_TD_1I|HC_TD_R;
} else {
td_control = HC_TD_IN|HC_TD_1I;
}
break;
case USB_EP_ATTR_BULK:
td_control = tw->tw_direction|HC_TD_DT_0|HC_TD_1I|HC_TD_R;
break;
}
cpu_current_dummy = (ohci_td_t *)
(ohci_td_iommu_to_cpu(ohcip, Get_ED(ept->hced_tailp)));
ohci_polled_fill_in_td(ohcip, cpu_current_dummy, td,
td_control, 0, tw->tw_length, tw);
ohci_polled_insert_td_on_tw(ohcip, tw, cpu_current_dummy);
Set_ED(ept->hced_tailp, (ohci_td_cpu_to_iommu(ohcip, td)));
}
static void
ohci_polled_fill_in_td(
ohci_state_t *ohcip,
ohci_td_t *td,
ohci_td_t *new_dummy,
uint_t hctd_ctrl,
uint32_t hctd_dma_offs,
size_t hctd_length,
ohci_trans_wrapper_t *tw)
{
ASSERT(Get_TD(td->hctd_state) == HC_TD_DUMMY);
bzero((char *)td, sizeof (ohci_td_t));
Set_TD(td->hctd_ctrl, (hctd_ctrl | HC_TD_CC_NA));
ohci_init_td(ohcip, tw, hctd_dma_offs, hctd_length, td);
Set_TD(td->hctd_next_td, (ohci_td_cpu_to_iommu(ohcip, new_dummy)));
Set_TD(td->hctd_trans_wrapper, (uint32_t)tw->tw_id);
Set_TD(td->hctd_tw_next_td, NULL);
}
static void
ohci_polled_insert_td_on_tw(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
ohci_td_t *td)
{
Set_TD(td->hctd_tw_next_td, NULL);
if (tw->tw_hctd_head == NULL) {
ASSERT(tw->tw_hctd_tail == NULL);
tw->tw_hctd_head = td;
tw->tw_hctd_tail = td;
} else {
ohci_td_t *dummy = (ohci_td_t *)tw->tw_hctd_tail;
ASSERT(dummy != NULL);
ASSERT(dummy != td);
ASSERT(Get_TD(td->hctd_state) == HC_TD_DUMMY);
Set_TD(dummy->hctd_tw_next_td, ohci_td_cpu_to_iommu(ohcip, td));
tw->tw_hctd_tail = td;
ASSERT(Get_TD(td->hctd_tw_next_td) == 0);
}
}
static void
ohci_polled_handle_frame_number_overflow(ohci_state_t *ohcip)
{
uint_t intr;
intr = (Get_OpReg(hcr_intr_status) & Get_OpReg(hcr_intr_enable));
if (intr & HCR_INTR_FNO) {
ohci_handle_frame_number_overflow(ohcip);
ohci_polled_finish_interrupt(ohcip, HCR_INTR_FNO);
}
}
static void
ohci_polled_finish_interrupt(
ohci_state_t *ohcip,
uint_t intr)
{
Set_OpReg(hcr_intr_status, intr);
(void) Get_OpReg(hcr_intr_status);
}
static void
ohci_polled_insert_bulk_td(
ohci_polled_t *ohci_polledp)
{
ohci_state_t *ohcip;
ohci_trans_wrapper_t *tw;
ohci_pipe_private_t *pp;
usba_pipe_handle_data_t *ph;
uint32_t ctrl;
uint_t bulk_pkg_size;
ohcip = ohci_polledp->ohci_polled_ohcip;
ph = ohci_polledp->ohci_polled_input_pipe_handle;
pp = (ohci_pipe_private_t *)ph->p_hcd_private;
tw = pp->pp_tw_head;
ASSERT(tw != NULL);
ctrl = tw->tw_direction | HC_TD_DT_0 | HC_TD_1I | HC_TD_R;
bulk_pkg_size = min(POLLED_RAW_BUF_SIZE, OHCI_MAX_TD_XFER_SIZE);
(void) ohci_polled_insert_hc_td(ohcip, ctrl, 0, bulk_pkg_size, pp, tw);
}
static int
ohci_polled_create_tw(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags)
{
uint_t ccount;
ohci_trans_wrapper_t *tw;
ddi_device_acc_attr_t dev_attr;
ddi_dma_attr_t dma_attr;
ohci_pipe_private_t *pp;
int result, pipe_dir, td_count;
size_t real_length;
pp = (ohci_pipe_private_t *)ph->p_hcd_private;
td_count = (POLLED_RAW_BUF_SIZE - 1) / OHCI_MAX_TD_XFER_SIZE + 1;
if ((tw = kmem_zalloc(sizeof (ohci_trans_wrapper_t),
KM_NOSLEEP)) == NULL) {
return (USB_FAILURE);
}
bcopy(&ohcip->ohci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
dma_attr.dma_attr_sgllen = OHCI_DMA_ATTR_TW_SGLLEN;
dma_attr.dma_attr_align = OHCI_DMA_ATTR_ALIGNMENT;
if ((result = ddi_dma_alloc_handle(ohcip->ohci_dip,
&dma_attr, DDI_DMA_DONTWAIT, 0, &tw->tw_dmahandle)) !=
DDI_SUCCESS) {
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (USB_FAILURE);
}
dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
if ((result = ddi_dma_mem_alloc(tw->tw_dmahandle, POLLED_RAW_BUF_SIZE,
&dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
&tw->tw_buf, &real_length, &tw->tw_accesshandle)) !=
DDI_SUCCESS) {
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (USB_FAILURE);
}
if ((result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
DDI_DMA_DONTWAIT, NULL, &tw->tw_cookie, &ccount)) !=
DDI_DMA_MAPPED) {
ddi_dma_mem_free(&tw->tw_accesshandle);
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (USB_FAILURE);
}
if (ccount != 1) {
result = ddi_dma_unbind_handle(tw->tw_dmahandle);
ASSERT(result == DDI_SUCCESS);
ddi_dma_mem_free(&tw->tw_accesshandle);
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (USB_FAILURE);
}
if (ohci_allocate_tds_for_tw(ohcip, tw, td_count) == USB_SUCCESS) {
tw->tw_num_tds = td_count;
} else {
ohci_deallocate_tw_resources(ohcip, pp, tw);
return (USB_FAILURE);
}
tw->tw_cookie_idx = 0;
tw->tw_dma_offs = 0;
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 = POLLED_RAW_BUF_SIZE;
tw->tw_pipe_private = pp;
tw->tw_flags = usb_flags;
tw->tw_id = OHCI_GET_ID((void *)tw);
ASSERT(tw->tw_id != 0);
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
tw->tw_direction = (pipe_dir == USB_EP_DIR_IN) ? HC_TD_IN : HC_TD_OUT;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
(void *)tw, tw->tw_ncookies);
return (USB_SUCCESS);
}
int
ohci_polled_insert_hc_td(
ohci_state_t *ohcip,
uint_t hctd_ctrl,
uint32_t hctd_dma_offs,
size_t hctd_length,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw)
{
ohci_td_t *new_dummy;
ohci_td_t *cpu_current_dummy;
ohci_ed_t *ept = pp->pp_ept;
new_dummy = tw->tw_hctd_free_list;
ASSERT(new_dummy != NULL);
tw->tw_hctd_free_list = ohci_td_iommu_to_cpu(ohcip,
Get_TD(new_dummy->hctd_tw_next_td));
Set_TD(new_dummy->hctd_tw_next_td, NULL);
cpu_current_dummy = (ohci_td_t *)
(ohci_td_iommu_to_cpu(ohcip, Get_ED(ept->hced_tailp)));
ohci_polled_fill_in_td(ohcip, cpu_current_dummy, new_dummy,
hctd_ctrl, hctd_dma_offs, hctd_length, tw);
Set_ED(ept->hced_tailp,
(ohci_td_cpu_to_iommu(ohcip, new_dummy)));
ohci_polled_insert_td_on_tw(ohcip, tw, cpu_current_dummy);
return (USB_SUCCESS);
}