#include <sys/usb/hcd/uhci/uhcid.h>
#include <sys/usb/hcd/uhci/uhciutil.h>
#include <sys/usb/hcd/uhci/uhcipolled.h>
#include <sys/disp.h>
extern uint_t uhci_td_pool_size;
extern uint_t uhci_qh_pool_size;
extern ushort_t uhci_tree_bottom_nodes[];
extern void *uhci_statep;
static void uhci_build_interrupt_lattice(uhci_state_t *uhcip);
static int uhci_init_frame_lst_table(dev_info_t *dip, uhci_state_t *uhcip);
static uint_t uhci_lattice_height(uint_t bandwidth);
static uint_t uhci_lattice_parent(uint_t node);
static uint_t uhci_leftmost_leaf(uint_t node, uint_t height);
static uint_t uhci_compute_total_bandwidth(usb_ep_descr_t *endpoint,
usb_port_status_t port_status);
static int uhci_bandwidth_adjust(uhci_state_t *uhcip,
usb_ep_descr_t *endpoint, usb_port_status_t port_status);
static uhci_td_t *uhci_allocate_td_from_pool(uhci_state_t *uhcip);
static void uhci_fill_in_td(uhci_state_t *uhcip,
uhci_td_t *td, uhci_td_t *current_dummy,
uint32_t buffer_offset, size_t length,
uhci_pipe_private_t *pp, uchar_t PID,
usb_req_attrs_t attrs, uhci_trans_wrapper_t *tw);
static uint32_t uhci_get_tw_paddr_by_offs(uhci_state_t *uhcip,
uint32_t buffer_offset, size_t length,
uhci_trans_wrapper_t *tw);
static uhci_trans_wrapper_t *uhci_create_transfer_wrapper(
uhci_state_t *uhcip, uhci_pipe_private_t *pp,
size_t length, usb_flags_t usb_flags);
static uhci_trans_wrapper_t *uhci_create_isoc_transfer_wrapper(
uhci_state_t *uhcip, uhci_pipe_private_t *pp,
usb_isoc_req_t *req, size_t length,
usb_flags_t usb_flags);
static int uhci_create_setup_pkt(uhci_state_t *uhcip,
uhci_pipe_private_t *pp, uhci_trans_wrapper_t *tw);
static void uhci_insert_ctrl_qh(uhci_state_t *uhcip,
uhci_pipe_private_t *pp);
static void uhci_remove_ctrl_qh(uhci_state_t *uhcip,
uhci_pipe_private_t *pp);
static void uhci_insert_intr_qh(uhci_state_t *uhcip,
uhci_pipe_private_t *pp);
static void uhci_remove_intr_qh(uhci_state_t *uhcip,
uhci_pipe_private_t *pp);
static void uhci_remove_bulk_qh(uhci_state_t *uhcip,
uhci_pipe_private_t *pp);
static void uhci_insert_bulk_qh(uhci_state_t *uhcip,
uhci_pipe_private_t *pp);
static void uhci_handle_bulk_td_errors(uhci_state_t *uhcip, uhci_td_t *td);
static int uhci_alloc_memory_for_tds(uhci_state_t *uhcip, uint_t num_tds,
uhci_bulk_isoc_xfer_t *info);
static int uhci_alloc_bulk_isoc_tds(uhci_state_t *uhcip, uint_t num_tds,
uhci_bulk_isoc_xfer_t *info);
static void uhci_get_isoc_td_by_index(uhci_state_t *uhcip,
uhci_bulk_isoc_xfer_t *info, uint_t index,
uhci_td_t **tdpp, uhci_bulk_isoc_td_pool_t **td_pool_pp);
static void uhci_get_bulk_td_by_paddr(uhci_state_t *uhcip,
uhci_bulk_isoc_xfer_t *info, uint32_t paddr,
uhci_bulk_isoc_td_pool_t **td_pool_pp);
static int uhci_handle_isoc_receive(uhci_state_t *uhcip,
uhci_pipe_private_t *pp, uhci_trans_wrapper_t *tw);
static void uhci_delete_isoc_td(uhci_state_t *uhcip,
uhci_td_t *td);
#ifdef DEBUG
static void uhci_print_td(uhci_state_t *uhcip, uhci_td_t *td);
static void uhci_print_qh(uhci_state_t *uhcip, queue_head_t *qh);
#endif
static void
uhci_build_interrupt_lattice(uhci_state_t *uhcip)
{
int half_list = NUM_INTR_QH_LISTS / 2;
uint16_t i, j, k;
uhci_td_t *sof_td, *isoc_td;
uintptr_t addr;
queue_head_t *list_array = uhcip->uhci_qh_pool_addr;
queue_head_t *tmp_qh;
frame_lst_table_t *frame_lst_tablep =
uhcip->uhci_frame_lst_tablep;
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_build_interrupt_lattice:");
for (i = 0; i < NUM_INTR_QH_LISTS; i++) {
SetQH32(uhcip, list_array[i].link_ptr, HC_END_OF_LIST);
SetQH32(uhcip, list_array[i].element_ptr, HC_END_OF_LIST);
list_array[i].qh_flag = QUEUE_HEAD_FLAG_STATIC;
list_array[i].node = i;
}
for (i = 0; i < half_list - 1; i++) {
addr = QH_PADDR(&list_array[i]) | HC_QUEUE_HEAD;
SetQH32(uhcip, list_array[2*i + 1].link_ptr, addr);
SetQH32(uhcip, list_array[2*i + 2].link_ptr, addr);
}
for (i = 0, j = 0; i < pow_2(TREE_HEIGHT); i++) {
addr = QH_PADDR(&list_array[half_list + i - 1]);
for (k = 0; k < pow_2(VIRTUAL_TREE_HEIGHT); k++) {
SetFL32(uhcip,
frame_lst_tablep[uhci_tree_bottom_nodes[j++]],
addr | HC_QUEUE_HEAD);
}
}
uhcip->uhci_ctrl_xfers_q_head = uhci_alloc_queue_head(uhcip);
tmp_qh = uhcip->uhci_ctrl_xfers_q_tail = uhcip->uhci_ctrl_xfers_q_head;
SetQH32(uhcip, list_array[0].link_ptr,
(QH_PADDR(tmp_qh) | HC_QUEUE_HEAD));
uhcip->uhci_bulk_xfers_q_head = uhci_alloc_queue_head(uhcip);
uhcip->uhci_bulk_xfers_q_tail = uhcip->uhci_bulk_xfers_q_head;
SetQH32(uhcip, tmp_qh->link_ptr,
(QH_PADDR(uhcip->uhci_bulk_xfers_q_head)|HC_QUEUE_HEAD));
SetQH32(uhcip, uhcip->uhci_bulk_xfers_q_head->link_ptr, HC_END_OF_LIST);
sof_td = uhci_allocate_td_from_pool(uhcip);
SetQH32(uhcip, list_array[0].element_ptr,
TD_PADDR(sof_td) | HC_TD_HEAD);
SetTD32(uhcip, sof_td->link_ptr, HC_END_OF_LIST);
uhcip->uhci_sof_td = sof_td;
isoc_td = uhci_allocate_td_from_pool(uhcip);
SetTD32(uhcip, isoc_td->link_ptr, HC_END_OF_LIST);
uhcip->uhci_isoc_td = isoc_td;
uhcip->uhci_isoc_qh = uhci_alloc_queue_head(uhcip);
SetQH32(uhcip, uhcip->uhci_isoc_qh->link_ptr,
GetFL32(uhcip, uhcip->uhci_frame_lst_tablep[MAX_FRAME_NUM]));
SetQH32(uhcip, uhcip->uhci_isoc_qh->element_ptr, TD_PADDR(isoc_td));
SetFL32(uhcip, uhcip->uhci_frame_lst_tablep[MAX_FRAME_NUM],
QH_PADDR(uhcip->uhci_isoc_qh) | HC_QUEUE_HEAD);
}
int
uhci_allocate_pools(uhci_state_t *uhcip)
{
dev_info_t *dip = uhcip->uhci_dip;
size_t real_length;
int i, result;
uint_t ccount;
ddi_device_acc_attr_t dev_attr;
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_allocate_pools:");
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 (ddi_dma_alloc_handle(dip, &uhcip->uhci_dma_attr, DDI_DMA_SLEEP, 0,
&uhcip->uhci_td_pool_dma_handle) != DDI_SUCCESS) {
return (USB_FAILURE);
}
if (ddi_dma_mem_alloc(uhcip->uhci_td_pool_dma_handle,
uhci_td_pool_size * sizeof (uhci_td_t),
&dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
(caddr_t *)&uhcip->uhci_td_pool_addr, &real_length,
&uhcip->uhci_td_pool_mem_handle)) {
return (USB_FAILURE);
}
result = ddi_dma_addr_bind_handle(uhcip->uhci_td_pool_dma_handle,
NULL, (caddr_t)uhcip->uhci_td_pool_addr, real_length,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
NULL, &uhcip->uhci_td_pool_cookie, &ccount);
bzero((void *)uhcip->uhci_td_pool_addr,
uhci_td_pool_size * sizeof (uhci_td_t));
if (result == DDI_DMA_MAPPED) {
if (ccount != 1) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_allocate_pools: More than 1 cookie");
return (USB_FAILURE);
}
} else {
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_allocate_pools: Result = %d", result);
uhci_decode_ddi_dma_addr_bind_handle_result(uhcip, result);
return (USB_FAILURE);
}
uhcip->uhci_dma_addr_bind_flag |= UHCI_TD_POOL_BOUND;
for (i = 0; i < uhci_td_pool_size; i++) {
uhcip->uhci_td_pool_addr[i].flag = TD_FLAG_FREE;
}
if (ddi_dma_alloc_handle(dip, &uhcip->uhci_dma_attr, DDI_DMA_SLEEP,
0, &uhcip->uhci_qh_pool_dma_handle) != DDI_SUCCESS) {
return (USB_FAILURE);
}
if (ddi_dma_mem_alloc(uhcip->uhci_qh_pool_dma_handle,
uhci_qh_pool_size * sizeof (queue_head_t),
&dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
(caddr_t *)&uhcip->uhci_qh_pool_addr, &real_length,
&uhcip->uhci_qh_pool_mem_handle) != DDI_SUCCESS) {
return (USB_FAILURE);
}
result = ddi_dma_addr_bind_handle(uhcip->uhci_qh_pool_dma_handle,
NULL, (caddr_t)uhcip->uhci_qh_pool_addr, real_length,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
&uhcip->uhci_qh_pool_cookie, &ccount);
if (result == DDI_DMA_MAPPED) {
if (ccount != 1) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_allocate_pools: More than 1 cookie");
return (USB_FAILURE);
}
} else {
uhci_decode_ddi_dma_addr_bind_handle_result(uhcip, result);
return (USB_FAILURE);
}
uhcip->uhci_dma_addr_bind_flag |= UHCI_QH_POOL_BOUND;
bzero((void *)uhcip->uhci_qh_pool_addr,
uhci_qh_pool_size * sizeof (queue_head_t));
for (i = 0; i < uhci_qh_pool_size; i ++) {
uhcip->uhci_qh_pool_addr[i].qh_flag = QUEUE_HEAD_FLAG_FREE;
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_allocate_pools: Completed");
return (USB_SUCCESS);
}
void
uhci_free_pools(uhci_state_t *uhcip)
{
int i, flag, rval;
uhci_td_t *td;
uhci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_free_pools:");
if (uhcip->uhci_td_pool_addr && uhcip->uhci_td_pool_mem_handle) {
for (i = 0; i < uhci_td_pool_size; i ++) {
td = &uhcip->uhci_td_pool_addr[i];
flag = uhcip->uhci_td_pool_addr[i].flag;
if ((flag != TD_FLAG_FREE) &&
(flag != TD_FLAG_DUMMY) && (td->tw != NULL)) {
tw = td->tw;
uhci_free_tw(uhcip, tw);
}
}
if (uhcip->uhci_dma_addr_bind_flag & UHCI_TD_POOL_BOUND) {
rval = ddi_dma_unbind_handle(
uhcip->uhci_td_pool_dma_handle);
ASSERT(rval == DDI_SUCCESS);
}
ddi_dma_mem_free(&uhcip->uhci_td_pool_mem_handle);
}
if (uhcip->uhci_td_pool_dma_handle) {
ddi_dma_free_handle(&uhcip->uhci_td_pool_dma_handle);
}
if (uhcip->uhci_qh_pool_addr && uhcip->uhci_qh_pool_mem_handle) {
if (uhcip->uhci_dma_addr_bind_flag & UHCI_QH_POOL_BOUND) {
rval = ddi_dma_unbind_handle(
uhcip->uhci_qh_pool_dma_handle);
ASSERT(rval == DDI_SUCCESS);
}
ddi_dma_mem_free(&uhcip->uhci_qh_pool_mem_handle);
}
if (uhcip->uhci_qh_pool_dma_handle) {
ddi_dma_free_handle(&uhcip->uhci_qh_pool_dma_handle);
}
if (uhcip->uhci_frame_lst_tablep && uhcip->uhci_flt_mem_handle) {
if (uhcip->uhci_dma_addr_bind_flag & UHCI_FLA_POOL_BOUND) {
rval = ddi_dma_unbind_handle(
uhcip->uhci_flt_dma_handle);
ASSERT(rval == DDI_SUCCESS);
}
ddi_dma_mem_free(&uhcip->uhci_flt_mem_handle);
}
if (uhcip->uhci_flt_dma_handle) {
ddi_dma_free_handle(&uhcip->uhci_flt_dma_handle);
}
}
void
uhci_decode_ddi_dma_addr_bind_handle_result(uhci_state_t *uhcip, int result)
{
char *msg;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_decode_ddi_dma_addr_bind_handle_result:");
switch (result) {
case DDI_DMA_PARTIAL_MAP:
msg = "Partial transfers not allowed";
break;
case DDI_DMA_INUSE:
msg = "Handle is in use";
break;
case DDI_DMA_NORESOURCES:
msg = "No resources";
break;
case DDI_DMA_NOMAPPING:
msg = "No mapping";
break;
case DDI_DMA_TOOBIG:
msg = "Object is too big";
break;
default:
msg = "Unknown dma error";
}
USB_DPRINTF_L4(PRINT_MASK_ALL, uhcip->uhci_log_hdl, "%s", msg);
}
int
uhci_init_ctlr(uhci_state_t *uhcip)
{
dev_info_t *dip = uhcip->uhci_dip;
uint_t cmd_reg;
uint_t frame_base_addr;
mutex_enter(&uhcip->uhci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl, "uhci_init_ctlr:");
pci_config_put16(uhcip->uhci_config_handle,
LEGACYMODE_REG_OFFSET, LEGACYMODE_REG_INIT_VALUE);
Set_OpReg16(USBINTR, DISABLE_ALL_INTRS);
cmd_reg = Get_OpReg16(USBCMD);
cmd_reg &= (~USBCMD_REG_HC_RUN);
Set_OpReg16(USBCMD, cmd_reg);
Set_OpReg16(USBCMD, USBCMD_REG_GBL_RESET);
mutex_exit(&uhcip->uhci_int_mutex);
delay(drv_usectohz(UHCI_RESET_DELAY));
mutex_enter(&uhcip->uhci_int_mutex);
Set_OpReg16(USBCMD, 0);
Set_OpReg16(FRNUM, 0);
if (uhcip->uhci_hc_soft_state == UHCI_CTLR_INIT_STATE) {
if (uhci_init_frame_lst_table(dip, uhcip) != USB_SUCCESS) {
mutex_exit(&uhcip->uhci_int_mutex);
return (USB_FAILURE);
}
}
uhcip->uhci_frame_interval = Get_OpReg8(SOFMOD);
frame_base_addr = uhcip->uhci_flt_cookie.dmac_address;
Set_OpReg32(FRBASEADD, frame_base_addr);
cmd_reg = Get_OpReg16(USBCMD);
cmd_reg |= (USBCMD_REG_HC_RUN | USBCMD_REG_MAXPKT_64 |
USBCMD_REG_CONFIG_FLAG);
Set_OpReg16(USBCMD, cmd_reg);
cmd_reg = Get_OpReg16(USBCMD);
if (!(cmd_reg & (USBCMD_REG_HC_RUN | USBCMD_REG_MAXPKT_64 |
USBCMD_REG_CONFIG_FLAG))) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_init_ctlr: Controller initialization failed");
mutex_exit(&uhcip->uhci_int_mutex);
return (USB_FAILURE);
}
SetTD_ioc(uhcip, uhcip->uhci_isoc_td, 1);
uhcip->uhci_hc_soft_state = UHCI_CTLR_OPERATIONAL_STATE;
mutex_exit(&uhcip->uhci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_init_ctlr: Completed");
return (USB_SUCCESS);
}
void
uhci_uninit_ctlr(uhci_state_t *uhcip)
{
if (uhcip->uhci_regs_handle) {
Set_OpReg16(USBINTR, DISABLE_ALL_INTRS);
Set_OpReg16(USBCMD, 0);
mutex_exit(&uhcip->uhci_int_mutex);
delay(drv_usectohz(UHCI_TIMEWAIT));
mutex_enter(&uhcip->uhci_int_mutex);
}
}
int
uhci_map_regs(uhci_state_t *uhcip)
{
dev_info_t *dip = uhcip->uhci_dip;
int index;
uint32_t regs_prop_len;
int32_t *regs_list;
uint16_t command_reg;
ddi_device_acc_attr_t attr;
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl, "uhci_map_regs:");
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, uhcip->uhci_dip,
DDI_PROP_DONTPASS, "reg", ®s_list, ®s_prop_len) !=
DDI_PROP_SUCCESS) {
return (USB_FAILURE);
}
for (index = 0; index * 5 < regs_prop_len; index++) {
if (regs_list[index * 5] & UHCI_PROP_MASK) {
break;
}
}
ddi_prop_free(regs_list);
if (index * 5 >= regs_prop_len) {
return (USB_FAILURE);
}
if (ddi_regs_map_setup(dip, index, (caddr_t *)&uhcip->uhci_regsp,
0, sizeof (hc_regs_t), &attr, &uhcip->uhci_regs_handle) !=
DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"ddi_regs_map_setup: failed");
return (USB_FAILURE);
}
if (pci_config_setup(dip, &uhcip->uhci_config_handle) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_map_regs: Config error");
return (USB_FAILURE);
}
command_reg = pci_config_get16(uhcip->uhci_config_handle,
PCI_CONF_COMM);
if (!(command_reg & (PCI_COMM_MAE | PCI_COMM_ME))) {
USB_DPRINTF_L3(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_map_regs: No MAE/ME");
}
command_reg |= PCI_COMM_MAE | PCI_COMM_ME;
pci_config_put16(uhcip->uhci_config_handle, PCI_CONF_COMM, command_reg);
if (!(command_reg & PCI_COMM_IO)) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"I/O Base address access disabled");
return (USB_FAILURE);
}
uhcip->uhci_iobase = (pci_config_get16(uhcip->uhci_config_handle,
PCI_CONF_IOBASE) & PCI_CONF_IOBASE_MASK);
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_map_regs: Completed");
return (USB_SUCCESS);
}
void
uhci_unmap_regs(uhci_state_t *uhcip)
{
if (uhcip->uhci_regs_handle) {
Set_OpReg16(USBCMD, USBCMD_REG_GBL_RESET);
ddi_regs_map_free(&uhcip->uhci_regs_handle);
}
if (uhcip->uhci_config_handle) {
pci_config_teardown(&uhcip->uhci_config_handle);
}
}
void
uhci_set_dma_attributes(uhci_state_t *uhcip)
{
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_set_dma_attributes:");
uhcip->uhci_dma_attr.dma_attr_version = DMA_ATTR_V0;
uhcip->uhci_dma_attr.dma_attr_addr_lo = 0x00000000ull;
uhcip->uhci_dma_attr.dma_attr_addr_hi = 0xfffffff0ull;
uhcip->uhci_dma_attr.dma_attr_count_max = 0xffffffull;
uhcip->uhci_dma_attr.dma_attr_align = 0x10;
uhcip->uhci_dma_attr.dma_attr_burstsizes = 0x1;
uhcip->uhci_dma_attr.dma_attr_minxfer = 0x1;
uhcip->uhci_dma_attr.dma_attr_maxxfer = 0xffffffull;
uhcip->uhci_dma_attr.dma_attr_seg = 0xffffffffull;
uhcip->uhci_dma_attr.dma_attr_sgllen = 1;
uhcip->uhci_dma_attr.dma_attr_granular = 1;
uhcip->uhci_dma_attr.dma_attr_flags = 0;
}
uint_t
pow_2(uint_t x)
{
return ((x == 0) ? 1 : (1 << x));
}
uint_t
log_2(uint_t x)
{
int ret_val = 0;
while (x != 1) {
ret_val++;
x = x >> 1;
}
return (ret_val);
}
uhci_state_t *
uhci_obtain_state(dev_info_t *dip)
{
int instance = ddi_get_instance(dip);
uhci_state_t *state = ddi_get_soft_state(uhci_statep, instance);
ASSERT(state != NULL);
return (state);
}
usba_hcdi_ops_t *
uhci_alloc_hcdi_ops(uhci_state_t *uhcip)
{
usba_hcdi_ops_t *hcdi_ops;
USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
"uhci_alloc_hcdi_ops:");
hcdi_ops = usba_alloc_hcdi_ops();
hcdi_ops->usba_hcdi_ops_version = HCDI_OPS_VERSION_1;
hcdi_ops->usba_hcdi_pipe_open = uhci_hcdi_pipe_open;
hcdi_ops->usba_hcdi_pipe_close = uhci_hcdi_pipe_close;
hcdi_ops->usba_hcdi_pipe_reset = uhci_hcdi_pipe_reset;
hcdi_ops->usba_hcdi_pipe_reset_data_toggle =
uhci_hcdi_pipe_reset_data_toggle;
hcdi_ops->usba_hcdi_pipe_ctrl_xfer = uhci_hcdi_pipe_ctrl_xfer;
hcdi_ops->usba_hcdi_pipe_bulk_xfer = uhci_hcdi_pipe_bulk_xfer;
hcdi_ops->usba_hcdi_pipe_intr_xfer = uhci_hcdi_pipe_intr_xfer;
hcdi_ops->usba_hcdi_pipe_isoc_xfer = uhci_hcdi_pipe_isoc_xfer;
hcdi_ops->usba_hcdi_bulk_transfer_size = uhci_hcdi_bulk_transfer_size;
hcdi_ops->usba_hcdi_pipe_stop_intr_polling =
uhci_hcdi_pipe_stop_intr_polling;
hcdi_ops->usba_hcdi_pipe_stop_isoc_polling =
uhci_hcdi_pipe_stop_isoc_polling;
hcdi_ops->usba_hcdi_get_current_frame_number =
uhci_hcdi_get_current_frame_number;
hcdi_ops->usba_hcdi_get_max_isoc_pkts = uhci_hcdi_get_max_isoc_pkts;
hcdi_ops->usba_hcdi_console_input_init = uhci_hcdi_polled_input_init;
hcdi_ops->usba_hcdi_console_input_enter = uhci_hcdi_polled_input_enter;
hcdi_ops->usba_hcdi_console_read = uhci_hcdi_polled_read;
hcdi_ops->usba_hcdi_console_input_exit = uhci_hcdi_polled_input_exit;
hcdi_ops->usba_hcdi_console_input_fini = uhci_hcdi_polled_input_fini;
hcdi_ops->usba_hcdi_console_output_init = uhci_hcdi_polled_output_init;
hcdi_ops->usba_hcdi_console_output_enter =
uhci_hcdi_polled_output_enter;
hcdi_ops->usba_hcdi_console_write = uhci_hcdi_polled_write;
hcdi_ops->usba_hcdi_console_output_exit = uhci_hcdi_polled_output_exit;
hcdi_ops->usba_hcdi_console_output_fini = uhci_hcdi_polled_output_fini;
return (hcdi_ops);
}
static int
uhci_init_frame_lst_table(dev_info_t *dip, uhci_state_t *uhcip)
{
int result;
uint_t ccount;
size_t real_length;
ddi_device_acc_attr_t dev_attr;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_init_frame_lst_table:");
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;
uhcip->uhci_dma_attr.dma_attr_align = 0x1000;
if (ddi_dma_alloc_handle(dip, &uhcip->uhci_dma_attr, DDI_DMA_SLEEP,
0, &uhcip->uhci_flt_dma_handle) != DDI_SUCCESS) {
return (USB_FAILURE);
}
uhcip->uhci_dma_attr.dma_attr_align = 0x10;
if (ddi_dma_mem_alloc(uhcip->uhci_flt_dma_handle,
SIZE_OF_FRAME_LST_TABLE, &dev_attr, DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP, 0, (caddr_t *)&uhcip->uhci_frame_lst_tablep,
&real_length, &uhcip->uhci_flt_mem_handle)) {
return (USB_FAILURE);
}
result = ddi_dma_addr_bind_handle(uhcip->uhci_flt_dma_handle,
NULL, (caddr_t)uhcip->uhci_frame_lst_tablep, real_length,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
&uhcip->uhci_flt_cookie, &ccount);
if (result == DDI_DMA_MAPPED) {
if (ccount != 1) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_init_frame_list_table: More than 1 cookie");
return (USB_FAILURE);
}
} else {
uhci_decode_ddi_dma_addr_bind_handle_result(uhcip, result);
return (USB_FAILURE);
}
uhcip->uhci_dma_addr_bind_flag |= UHCI_FLA_POOL_BOUND;
bzero((void *)uhcip->uhci_frame_lst_tablep, real_length);
uhci_build_interrupt_lattice(uhcip);
return (USB_SUCCESS);
}
queue_head_t *
uhci_alloc_queue_head(uhci_state_t *uhcip)
{
int index;
uhci_td_t *dummy_td;
queue_head_t *queue_head;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_alloc_queue_head");
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if ((dummy_td = uhci_allocate_td_from_pool(uhcip)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_alloc_queue_head: allocate td from pool failed");
return (NULL);
}
for (index = NUM_STATIC_NODES; index < uhci_qh_pool_size; index++) {
if (uhcip->uhci_qh_pool_addr[index].qh_flag ==
QUEUE_HEAD_FLAG_FREE) {
break;
}
}
USB_DPRINTF_L3(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_alloc_queue_head: Allocated %d", index);
if (index == uhci_qh_pool_size) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_alloc_queue_head: All QH exhausted");
dummy_td->flag = TD_FLAG_FREE;
return (NULL);
}
queue_head = &uhcip->uhci_qh_pool_addr[index];
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_alloc_queue_head: Allocated address 0x%p",
(void *)queue_head);
bzero((void *)queue_head, sizeof (queue_head_t));
SetQH32(uhcip, queue_head->link_ptr, HC_END_OF_LIST);
SetQH32(uhcip, queue_head->element_ptr, HC_END_OF_LIST);
queue_head->prev_qh = NULL;
queue_head->qh_flag = QUEUE_HEAD_FLAG_BUSY;
bzero((char *)dummy_td, sizeof (uhci_td_t));
queue_head->td_tailp = dummy_td;
SetQH32(uhcip, queue_head->element_ptr, TD_PADDR(dummy_td));
return (queue_head);
}
int
uhci_allocate_bandwidth(
uhci_state_t *uhcip,
usba_pipe_handle_data_t *pipe_handle,
uint_t *node)
{
int bandwidth;
uint_t min, min_index;
uint_t i;
uint_t height;
uint_t leftmost;
uint_t length;
uint32_t paddr;
queue_head_t *tmp_qh;
usb_ep_descr_t *endpoint = &pipe_handle->p_ep;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
mutex_enter(&pipe_handle->p_usba_device->usb_mutex);
length = uhci_compute_total_bandwidth(endpoint,
pipe_handle->p_usba_device->usb_port_status);
mutex_exit(&pipe_handle->p_usba_device->usb_mutex);
if ((length + uhcip->uhci_bandwidth_intr_min +
uhcip->uhci_bandwidth_isoch_sum) > (MAX_PERIODIC_BANDWIDTH)) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_allocate_bandwidth: "
"Reached maximum bandwidth value and cannot allocate "
"bandwidth for a given Interrupt/Isoch endpoint");
return (USB_NO_BANDWIDTH);
}
if (UHCI_XFER_TYPE(endpoint) == USB_EP_ATTR_ISOCH) {
uhcip->uhci_bandwidth_isoch_sum += length;
return (USB_SUCCESS);
}
mutex_enter(&pipe_handle->p_usba_device->usb_mutex);
bandwidth = uhci_bandwidth_adjust(uhcip, endpoint,
pipe_handle->p_usba_device->usb_port_status);
mutex_exit(&pipe_handle->p_usba_device->usb_mutex);
if (bandwidth == USB_FAILURE) {
return (USB_FAILURE);
}
USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
"The new bandwidth is %d", bandwidth);
min_index = 0;
min = uhcip->uhci_bandwidth[0];
for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
if (uhcip->uhci_bandwidth[i] < min) {
min_index = i;
min = uhcip->uhci_bandwidth[i];
}
}
USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
"The leaf with minimal bandwidth %d, "
"The smallest bandwidth %d", min_index, min);
height = uhci_lattice_height(bandwidth);
USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
"The height is %d", height);
*node = uhci_tree_bottom_nodes[min_index];
if (uhcip->uhci_isoc_q_tailp[*node]) {
paddr = (uhcip->uhci_isoc_q_tailp[*node]->link_ptr &
FRAME_LST_PTR_MASK);
} else {
paddr = (uhcip->uhci_frame_lst_tablep[*node] &
FRAME_LST_PTR_MASK);
}
tmp_qh = QH_VADDR(paddr);
*node = tmp_qh->node;
for (i = 0; i < height; i++) {
*node = uhci_lattice_parent(*node);
}
USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
"The real node is %d", *node);
leftmost = uhci_leftmost_leaf(*node, height);
USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
"Leftmost %d", leftmost);
for (i = leftmost; i < leftmost +
(NUM_FRAME_LST_ENTRIES/bandwidth); i ++) {
if ((length + uhcip->uhci_bandwidth_isoch_sum +
uhcip->uhci_bandwidth[i]) > MAX_PERIODIC_BANDWIDTH) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_allocate_bandwidth: "
"Reached maximum bandwidth value and cannot "
"allocate bandwidth for Interrupt endpoint");
return (USB_NO_BANDWIDTH);
}
}
for (i = leftmost; i < leftmost +
(NUM_FRAME_LST_ENTRIES/bandwidth); i ++) {
uhcip->uhci_bandwidth[i] += length;
}
min_index = 0;
min = uhcip->uhci_bandwidth[0];
for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
if (uhcip->uhci_bandwidth[i] < min) {
min_index = i;
min = uhcip->uhci_bandwidth[i];
}
}
uhcip->uhci_bandwidth_intr_min = min;
return (USB_SUCCESS);
}
void
uhci_deallocate_bandwidth(uhci_state_t *uhcip,
usba_pipe_handle_data_t *pipe_handle)
{
uint_t bandwidth;
uint_t height;
uint_t leftmost;
uint_t i;
uint_t min;
usb_ep_descr_t *endpoint = &pipe_handle->p_ep;
uint_t node, length;
uhci_pipe_private_t *pp =
(uhci_pipe_private_t *)pipe_handle->p_hcd_private;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
mutex_enter(&pipe_handle->p_usba_device->usb_mutex);
length = uhci_compute_total_bandwidth(endpoint,
pipe_handle->p_usba_device->usb_port_status);
mutex_exit(&pipe_handle->p_usba_device->usb_mutex);
if (UHCI_XFER_TYPE(endpoint) == USB_EP_ATTR_ISOCH) {
uhcip->uhci_bandwidth_isoch_sum -= length;
return;
}
node = pp->pp_node;
mutex_enter(&pipe_handle->p_usba_device->usb_mutex);
bandwidth = uhci_bandwidth_adjust(uhcip, endpoint,
pipe_handle->p_usba_device->usb_port_status);
mutex_exit(&pipe_handle->p_usba_device->usb_mutex);
height = uhci_lattice_height(bandwidth);
leftmost = uhci_leftmost_leaf(node, height);
for (i = leftmost; i < leftmost + (NUM_FRAME_LST_ENTRIES/bandwidth);
i ++) {
uhcip->uhci_bandwidth[i] -= length;
}
min = uhcip->uhci_bandwidth[0];
for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
if (uhcip->uhci_bandwidth[i] < min) {
min = uhcip->uhci_bandwidth[i];
}
}
uhcip->uhci_bandwidth_intr_min = min;
}
static uint_t
uhci_compute_total_bandwidth(usb_ep_descr_t *endpoint,
usb_port_status_t port_status)
{
uint_t bandwidth;
ushort_t MaxPacketSize = endpoint->wMaxPacketSize;
bandwidth = HOST_CONTROLLER_DELAY;
MaxPacketSize = (ushort_t)((MaxPacketSize * 7) / 6);
if (port_status == USBA_LOW_SPEED_DEV) {
bandwidth += (LOW_SPEED_PROTO_OVERHEAD +
HUB_LOW_SPEED_PROTO_OVERHEAD +
(LOW_SPEED_CLOCK * MaxPacketSize));
} else {
bandwidth += MaxPacketSize;
if (UHCI_XFER_TYPE(endpoint) == USB_EP_ATTR_INTR) {
bandwidth += FS_NON_ISOC_PROTO_OVERHEAD;
} else {
if (UHCI_XFER_DIR(endpoint) == USB_EP_DIR_IN) {
bandwidth += FS_ISOC_INPUT_PROTO_OVERHEAD;
} else {
bandwidth += FS_ISOC_OUTPUT_PROTO_OVERHEAD;
}
}
}
return (bandwidth);
}
static int
uhci_bandwidth_adjust(
uhci_state_t *uhcip,
usb_ep_descr_t *endpoint,
usb_port_status_t port_status)
{
int i = 0;
uint_t interval;
interval = endpoint->bInterval;
if ((interval < MIN_POLL_INTERVAL) || (interval > MAX_POLL_INTERVAL)) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_bandwidth_adjust: Endpoint's poll interval must be "
"between %d and %d ms", MIN_POLL_INTERVAL,
MAX_POLL_INTERVAL);
return (USB_FAILURE);
}
if ((port_status == USBA_LOW_SPEED_DEV) &&
(interval < MIN_LOW_SPEED_POLL_INTERVAL)) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_bandwidth_adjust: Low speed endpoint's poll interval "
"must be >= %d ms, adjusted",
MIN_LOW_SPEED_POLL_INTERVAL);
interval = MIN_LOW_SPEED_POLL_INTERVAL;
}
if (interval > 32) {
interval = 32;
}
while ((pow_2(i)) <= interval) {
i++;
}
return (pow_2((i - 1)));
}
static uint_t
uhci_lattice_height(uint_t bandwidth)
{
return (TREE_HEIGHT - (log_2(bandwidth)));
}
static uint_t
uhci_lattice_parent(uint_t node)
{
return (((node % 2) == 0) ? ((node/2) - 1) : (node/2));
}
static uint_t
uhci_leftmost_leaf(uint_t node, uint_t height)
{
node = pow_2(height + VIRTUAL_TREE_HEIGHT) * (node+1) -
NUM_FRAME_LST_ENTRIES;
return (node);
}
void
uhci_insert_qh(uhci_state_t *uhcip, usba_pipe_handle_data_t *ph)
{
uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_qh:");
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
switch (UHCI_XFER_TYPE(&ph->p_ep)) {
case USB_EP_ATTR_CONTROL:
uhci_insert_ctrl_qh(uhcip, pp);
break;
case USB_EP_ATTR_BULK:
uhci_insert_bulk_qh(uhcip, pp);
break;
case USB_EP_ATTR_INTR:
uhci_insert_intr_qh(uhcip, pp);
break;
case USB_EP_ATTR_ISOCH:
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_insert_qh: Illegal request");
break;
}
}
static void
uhci_insert_ctrl_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
{
queue_head_t *qh = pp->pp_qh;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_ctrl_qh:");
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if (uhcip->uhci_ctrl_xfers_q_head == uhcip->uhci_ctrl_xfers_q_tail) {
uhcip->uhci_ctrl_xfers_q_head->prev_qh = UHCI_INVALID_PTR;
}
SetQH32(uhcip, qh->link_ptr,
GetQH32(uhcip, uhcip->uhci_ctrl_xfers_q_tail->link_ptr));
qh->prev_qh = uhcip->uhci_ctrl_xfers_q_tail;
SetQH32(uhcip, uhcip->uhci_ctrl_xfers_q_tail->link_ptr,
QH_PADDR(qh) | HC_QUEUE_HEAD);
uhcip->uhci_ctrl_xfers_q_tail = qh;
}
static void
uhci_insert_bulk_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
{
queue_head_t *qh = pp->pp_qh;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_bulk_qh:");
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if (uhcip->uhci_bulk_xfers_q_head == uhcip->uhci_bulk_xfers_q_tail) {
uhcip->uhci_bulk_xfers_q_head->prev_qh = UHCI_INVALID_PTR;
} else if (uhcip->uhci_bulk_xfers_q_head->link_ptr ==
uhcip->uhci_bulk_xfers_q_tail->link_ptr) {
qh->link_ptr = uhcip->uhci_bulk_xfers_q_tail->link_ptr;
}
qh->prev_qh = uhcip->uhci_bulk_xfers_q_tail;
SetQH32(uhcip, uhcip->uhci_bulk_xfers_q_tail->link_ptr,
QH_PADDR(qh) | HC_QUEUE_HEAD);
uhcip->uhci_bulk_xfers_q_tail = qh;
}
static void
uhci_insert_intr_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
{
uint_t node = pp->pp_node;
queue_head_t *qh = pp->pp_qh;
queue_head_t *next_lattice_qh, *lattice_qh;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_intr_qh:");
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
lattice_qh = &uhcip->uhci_qh_pool_addr[node];
next_lattice_qh =
QH_VADDR(GetQH32(uhcip, lattice_qh->link_ptr) & QH_LINK_PTR_MASK);
next_lattice_qh->prev_qh = qh;
qh->link_ptr = lattice_qh->link_ptr;
qh->prev_qh = lattice_qh;
SetQH32(uhcip, lattice_qh->link_ptr, QH_PADDR(qh) | HC_QUEUE_HEAD);
pp->pp_data_toggle = 0;
}
int
uhci_insert_intr_td(
uhci_state_t *uhcip,
usba_pipe_handle_data_t *ph,
usb_intr_req_t *req,
usb_flags_t flags)
{
int error, pipe_dir;
uint_t length, mps;
uint32_t buf_offs;
uhci_td_t *tmp_td;
usb_intr_req_t *intr_reqp;
uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
uhci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_intr_td: req: 0x%p", (void *)req);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
pipe_dir = UHCI_XFER_DIR(&ph->p_ep);
if (req) {
length = req->intr_len;
} else {
ASSERT(pipe_dir == USB_EP_DIR_IN);
length = (pp->pp_client_periodic_in_reqp) ?
(((usb_intr_req_t *)pp->
pp_client_periodic_in_reqp)->intr_len) :
ph->p_ep.wMaxPacketSize;
}
if (length > UHCI_MAX_TD_XFER_SIZE) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_intr_td: Intr request size 0x%x is "
"more than 0x%x", length, UHCI_MAX_TD_XFER_SIZE);
return (USB_INVALID_REQUEST);
}
USB_DPRINTF_L3(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_intr_td: length: 0x%x", length);
if ((tw = uhci_create_transfer_wrapper(uhcip, pp, length, flags)) ==
NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_intr_td: TW allocation failed");
return (USB_NO_RESOURCES);
}
tw->tw_handle_td = uhci_handle_intr_td;
tw->tw_handle_callback_value = NULL;
tw->tw_direction = (pipe_dir == USB_EP_DIR_OUT) ?
PID_OUT : PID_IN;
tw->tw_curr_xfer_reqp = (usb_opaque_t)req;
if (tw->tw_direction == PID_IN) {
if ((error = uhci_allocate_periodic_in_resource(uhcip,
pp, tw, flags)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_intr_td: Interrupt request structure "
"allocation failed");
uhci_deallocate_tw(uhcip, pp, tw);
return (error);
}
}
intr_reqp = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
ASSERT(tw->tw_curr_xfer_reqp != NULL);
tw->tw_timeout_cnt = (intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER) ?
intr_reqp->intr_timeout : 0;
if (tw->tw_direction == PID_IN) {
error = uhci_insert_hc_td(uhcip, 0,
length, pp, tw, PID_IN, intr_reqp->intr_attributes);
if (error != USB_SUCCESS) {
uhci_deallocate_periodic_in_resource(uhcip, pp, tw);
uhci_deallocate_tw(uhcip, pp, tw);
return (USB_NO_RESOURCES);
}
tw->tw_bytes_xfered = 0;
return (USB_SUCCESS);
}
if (req->intr_len) {
ASSERT(req->intr_data != NULL);
ddi_rep_put8(tw->tw_accesshandle, req->intr_data->b_rptr,
(uint8_t *)tw->tw_buf, req->intr_len, DDI_DEV_AUTOINCR);
}
tw->tw_claim = UHCI_INTR_HDLR_CLAIMED;
mps = ph->p_ep.wMaxPacketSize;
buf_offs = 0;
while (length > 0) {
error = uhci_insert_hc_td(uhcip, buf_offs,
(length > mps) ? mps : length,
pp, tw, PID_OUT,
intr_reqp->intr_attributes);
if (error != USB_SUCCESS) {
break;
}
if (length <= mps) {
length = 0;
} else {
buf_offs += mps;
length -= mps;
}
}
if (error != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_insert_intr_td: allocate td failed, free resource");
while (tw->tw_hctd_head != NULL) {
uhci_delete_td(uhcip, tw->tw_hctd_head);
}
tw->tw_claim = UHCI_NOT_CLAIMED;
uhci_deallocate_tw(uhcip, pp, tw);
return (error);
}
tmp_td = tw->tw_hctd_head;
while (tmp_td != NULL) {
SetTD_status(uhcip, tmp_td, UHCI_TD_ACTIVE);
tmp_td = tmp_td->tw_td_next;
}
tw->tw_bytes_xfered = 0;
tw->tw_claim = UHCI_NOT_CLAIMED;
return (error);
}
static uhci_trans_wrapper_t *
uhci_create_transfer_wrapper(
uhci_state_t *uhcip,
uhci_pipe_private_t *pp,
size_t length,
usb_flags_t usb_flags)
{
size_t real_length;
uhci_trans_wrapper_t *tw;
ddi_device_acc_attr_t dev_attr;
ddi_dma_attr_t dma_attr;
int kmem_flag;
int (*dmamem_wait)(caddr_t);
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_transfer_wrapper: length = 0x%lx flags = 0x%x",
length, usb_flags);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if (UHCI_XFER_TYPE(&ph->p_ep) == USB_EP_ATTR_ISOCH) {
return (NULL);
}
kmem_flag = KM_NOSLEEP;
dmamem_wait = DDI_DMA_DONTWAIT;
if ((tw = kmem_zalloc(sizeof (uhci_trans_wrapper_t), kmem_flag)) ==
NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_transfer_wrapper: kmem_alloc failed");
return (NULL);
}
if (length == 0) {
goto dmadone;
}
bcopy(&uhcip->uhci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
dma_attr.dma_attr_sgllen = UHCI_DMA_ATTR_SGLLEN;
dma_attr.dma_attr_align = UHCI_DMA_ATTR_ALIGN;
tw->tw_length = length;
if (ddi_dma_alloc_handle(uhcip->uhci_dip, &dma_attr, dmamem_wait,
0, &tw->tw_dmahandle) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_transfer_wrapper: Alloc handle failed");
kmem_free(tw, sizeof (uhci_trans_wrapper_t));
return (NULL);
}
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 (ddi_dma_mem_alloc(tw->tw_dmahandle, tw->tw_length, &dev_attr,
DDI_DMA_CONSISTENT, dmamem_wait, NULL, (caddr_t *)&tw->tw_buf,
&real_length, &tw->tw_accesshandle) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_transfer_wrapper: dma_mem_alloc fail");
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (uhci_trans_wrapper_t));
return (NULL);
}
ASSERT(real_length >= length);
if (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) !=
DDI_DMA_MAPPED) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_transfer_wrapper: Bind handle failed");
ddi_dma_mem_free(&tw->tw_accesshandle);
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (uhci_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;
ASSERT(tw->tw_next == NULL);
}
tw->tw_pipe_private = pp;
tw->tw_flags = usb_flags;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
(void *)tw, tw->tw_ncookies);
return (tw);
}
int
uhci_insert_hc_td(
uhci_state_t *uhcip,
uint32_t buffer_offset,
size_t hcgtd_length,
uhci_pipe_private_t *pp,
uhci_trans_wrapper_t *tw,
uchar_t PID,
usb_req_attrs_t attrs)
{
uhci_td_t *td, *current_dummy;
queue_head_t *qh = pp->pp_qh;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if ((td = uhci_allocate_td_from_pool(uhcip)) == NULL) {
return (USB_NO_RESOURCES);
}
current_dummy = qh->td_tailp;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_insert_hc_td: td %p, attrs = 0x%x", (void *)td, attrs);
uhci_fill_in_td(uhcip, td, current_dummy, buffer_offset,
hcgtd_length, pp, PID, attrs, tw);
if ((tw->tw_handle_td != uhci_handle_intr_td) || (PID != PID_OUT)) {
SetTD_status(uhcip, current_dummy, UHCI_TD_ACTIVE);
}
if (tw->tw_hctd_head == NULL) {
ASSERT(tw->tw_hctd_tail == NULL);
tw->tw_hctd_head = current_dummy;
tw->tw_hctd_tail = current_dummy;
} else {
tw->tw_hctd_tail->tw_td_next = current_dummy;
tw->tw_hctd_tail = current_dummy;
}
current_dummy->outst_td_next = NULL;
current_dummy->outst_td_prev = uhcip->uhci_outst_tds_tail;
if (uhcip->uhci_outst_tds_head == NULL) {
uhcip->uhci_outst_tds_head = current_dummy;
} else {
uhcip->uhci_outst_tds_tail->outst_td_next = current_dummy;
}
uhcip->uhci_outst_tds_tail = current_dummy;
current_dummy->tw = tw;
return (USB_SUCCESS);
}
static void
uhci_fill_in_td(
uhci_state_t *uhcip,
uhci_td_t *td,
uhci_td_t *current_dummy,
uint32_t buffer_offset,
size_t length,
uhci_pipe_private_t *pp,
uchar_t PID,
usb_req_attrs_t attrs,
uhci_trans_wrapper_t *tw)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
uint32_t buf_addr;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_fill_in_td: td 0x%p buf_offs 0x%x len 0x%lx "
"attrs 0x%x", (void *)td, buffer_offset, length, attrs);
if (UHCI_XFER_TYPE(&ph->p_ep) == USB_EP_ATTR_ISOCH) {
return;
}
ASSERT(length <= UHCI_MAX_TD_XFER_SIZE);
bzero((char *)td, sizeof (uhci_td_t));
SetTD32(uhcip, current_dummy->link_ptr, TD_PADDR(td));
if (attrs & USB_ATTRS_SHORT_XFER_OK) {
SetTD_spd(uhcip, current_dummy, 1);
}
mutex_enter(&ph->p_usba_device->usb_mutex);
if (ph->p_usba_device->usb_port_status == USBA_LOW_SPEED_DEV) {
SetTD_ls(uhcip, current_dummy, LOW_SPEED_DEVICE);
}
SetTD_c_err(uhcip, current_dummy, UHCI_MAX_ERR_COUNT);
SetTD_mlen(uhcip, current_dummy,
(length == 0) ? ZERO_LENGTH : (length - 1));
SetTD_dtogg(uhcip, current_dummy, pp->pp_data_toggle);
ADJ_DATA_TOGGLE(pp);
SetTD_devaddr(uhcip, current_dummy, ph->p_usba_device->usb_addr);
SetTD_endpt(uhcip, current_dummy,
ph->p_ep.bEndpointAddress & END_POINT_ADDRESS_MASK);
SetTD_PID(uhcip, current_dummy, PID);
SetTD_ioc(uhcip, current_dummy, INTERRUPT_ON_COMPLETION);
buf_addr = uhci_get_tw_paddr_by_offs(uhcip, buffer_offset, length, tw);
SetTD32(uhcip, current_dummy->buffer_address, buf_addr);
td->qh_td_prev = current_dummy;
current_dummy->qh_td_prev = NULL;
pp->pp_qh->td_tailp = td;
mutex_exit(&ph->p_usba_device->usb_mutex);
}
static uint32_t
uhci_get_tw_paddr_by_offs(
uhci_state_t *uhcip,
uint32_t buffer_offset,
size_t length,
uhci_trans_wrapper_t *tw)
{
uint32_t buf_addr;
int rem_len;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_get_tw_paddr_by_offs: buf_offs 0x%x len 0x%lx",
buffer_offset, length);
ASSERT(length == 0 || buffer_offset >= tw->tw_dma_offs);
if (length == 0) {
buf_addr = 0;
return (buf_addr);
}
while ((tw->tw_dma_offs + tw->tw_cookie.dmac_size) <=
buffer_offset) {
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) -
buffer_offset;
ASSERT(length <= rem_len);
buf_addr = (buffer_offset - tw->tw_dma_offs) +
tw->tw_cookie.dmac_address;
USB_DPRINTF_L3(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_get_tw_paddr_by_offs: dmac_addr 0x%x dmac_size "
"0x%lx idx %d", buf_addr, tw->tw_cookie.dmac_size,
tw->tw_cookie_idx);
return (buf_addr);
}
void
uhci_modify_td_active_bits(
uhci_state_t *uhcip,
uhci_pipe_private_t *pp)
{
uhci_td_t *td_head;
usb_ep_descr_t *ept = &pp->pp_pipe_handle->p_ep;
uhci_trans_wrapper_t *tw_head = pp->pp_tw_head;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_modify_td_active_bits: tw head %p", (void *)tw_head);
while (tw_head != NULL) {
tw_head->tw_claim = UHCI_MODIFY_TD_BITS_CLAIMED;
td_head = tw_head->tw_hctd_head;
while (td_head) {
if (UHCI_XFER_TYPE(ept) == USB_EP_ATTR_ISOCH) {
SetTD_status(uhcip, td_head,
GetTD_status(uhcip, td_head) & TD_INACTIVE);
} else {
SetTD32(uhcip, td_head->link_ptr,
GetTD32(uhcip, td_head->link_ptr) |
HC_END_OF_LIST);
}
td_head = td_head->tw_td_next;
}
tw_head = tw_head->tw_next;
}
}
int
uhci_insert_ctrl_td(
uhci_state_t *uhcip,
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp,
usb_flags_t flags)
{
uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
uhci_trans_wrapper_t *tw;
size_t ctrl_buf_size;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_ctrl_td: timeout: 0x%x", ctrl_reqp->ctrl_timeout);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if (ctrl_reqp->ctrl_wLength) {
ctrl_buf_size = UHCI_CTRL_EPT_MAX_SIZE +
ctrl_reqp->ctrl_wLength;
} else {
ctrl_buf_size = SETUP_SIZE;
}
if ((tw = uhci_create_transfer_wrapper(uhcip, pp,
ctrl_buf_size, flags)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_ctrl_td: TW allocation failed");
return (USB_NO_RESOURCES);
}
pp->pp_data_toggle = 0;
tw->tw_curr_xfer_reqp = (usb_opaque_t)ctrl_reqp;
tw->tw_bytes_xfered = 0;
tw->tw_bytes_pending = ctrl_reqp->ctrl_wLength;
tw->tw_timeout_cnt = max(UHCI_CTRL_TIMEOUT, ctrl_reqp->ctrl_timeout);
tw->tw_handle_td = uhci_handle_ctrl_td;
tw->tw_handle_callback_value = NULL;
if ((uhci_create_setup_pkt(uhcip, pp, tw)) != USB_SUCCESS) {
tw->tw_ctrl_state = 0;
uhci_deallocate_tw(uhcip, pp, tw);
return (USB_NO_RESOURCES);
}
tw->tw_ctrl_state = SETUP;
return (USB_SUCCESS);
}
static int
uhci_create_setup_pkt(
uhci_state_t *uhcip,
uhci_pipe_private_t *pp,
uhci_trans_wrapper_t *tw)
{
int sdata;
usb_ctrl_req_t *req = (usb_ctrl_req_t *)tw->tw_curr_xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_create_setup_pkt: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
req->ctrl_bmRequestType, req->ctrl_bRequest, req->ctrl_wValue,
req->ctrl_wIndex, req->ctrl_wLength, (void *)req->ctrl_data);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
ASSERT(tw != NULL);
sdata = (req->ctrl_bmRequestType | (req->ctrl_bRequest << 8) |
(req->ctrl_wValue << 16));
ddi_put32(tw->tw_accesshandle, (uint_t *)tw->tw_buf, sdata);
sdata = (uint32_t)(req->ctrl_wIndex | (req->ctrl_wLength << 16));
ddi_put32(tw->tw_accesshandle,
(uint_t *)(tw->tw_buf + sizeof (uint_t)), sdata);
if ((uhci_insert_hc_td(uhcip, 0, SETUP_SIZE,
pp, tw, PID_SETUP, req->ctrl_attributes)) != USB_SUCCESS) {
return (USB_NO_RESOURCES);
}
USB_DPRINTF_L3(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"Create_setup: pp = 0x%p, attrs = 0x%x", (void *)pp,
req->ctrl_attributes);
if (req->ctrl_wLength != 0) {
if (req->ctrl_bmRequestType & USB_DEV_REQ_DEV_TO_HOST) {
tw->tw_direction = PID_IN;
} else {
tw->tw_direction = PID_OUT;
ddi_rep_put8(tw->tw_accesshandle,
req->ctrl_data->b_rptr,
(uint8_t *)(tw->tw_buf + UHCI_CTRL_EPT_MAX_SIZE),
req->ctrl_wLength,
DDI_DEV_AUTOINCR);
}
}
return (USB_SUCCESS);
}
void
uhci_create_stats(uhci_state_t *uhcip)
{
int i;
char kstatname[KSTAT_STRLEN];
char *usbtypes[USB_N_COUNT_KSTATS] =
{"ctrl", "isoch", "bulk", "intr"};
uint_t instance = uhcip->uhci_instance;
const char *dname = ddi_driver_name(uhcip->uhci_dip);
uhci_intrs_stats_t *isp;
if (UHCI_INTRS_STATS(uhcip) == NULL) {
(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,intrs",
dname, instance);
UHCI_INTRS_STATS(uhcip) = kstat_create("usba", instance,
kstatname, "usb_interrupts", KSTAT_TYPE_NAMED,
sizeof (uhci_intrs_stats_t) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT);
if (UHCI_INTRS_STATS(uhcip) != NULL) {
isp = UHCI_INTRS_STATS_DATA(uhcip);
kstat_named_init(&isp->uhci_intrs_hc_halted,
"HC Halted", KSTAT_DATA_UINT64);
kstat_named_init(&isp->uhci_intrs_hc_process_err,
"HC Process Errors", KSTAT_DATA_UINT64);
kstat_named_init(&isp->uhci_intrs_host_sys_err,
"Host Sys Errors", KSTAT_DATA_UINT64);
kstat_named_init(&isp->uhci_intrs_resume_detected,
"Resume Detected", KSTAT_DATA_UINT64);
kstat_named_init(&isp->uhci_intrs_usb_err_intr,
"USB Error", KSTAT_DATA_UINT64);
kstat_named_init(&isp->uhci_intrs_usb_intr,
"USB Interrupts", KSTAT_DATA_UINT64);
kstat_named_init(&isp->uhci_intrs_total,
"Total Interrupts", KSTAT_DATA_UINT64);
kstat_named_init(&isp->uhci_intrs_not_claimed,
"Not Claimed", KSTAT_DATA_UINT64);
UHCI_INTRS_STATS(uhcip)->ks_private = uhcip;
UHCI_INTRS_STATS(uhcip)->ks_update = nulldev;
kstat_install(UHCI_INTRS_STATS(uhcip));
}
}
if (UHCI_TOTAL_STATS(uhcip) == NULL) {
(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,total",
dname, instance);
UHCI_TOTAL_STATS(uhcip) = kstat_create("usba", instance,
kstatname, "usb_byte_count", KSTAT_TYPE_IO, 1,
KSTAT_FLAG_PERSISTENT);
if (UHCI_TOTAL_STATS(uhcip) != NULL) {
kstat_install(UHCI_TOTAL_STATS(uhcip));
}
}
for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
if (uhcip->uhci_count_stats[i] == NULL) {
(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,%s",
dname, instance, usbtypes[i]);
uhcip->uhci_count_stats[i] = kstat_create("usba",
instance, kstatname, "usb_byte_count",
KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
if (uhcip->uhci_count_stats[i] != NULL) {
kstat_install(uhcip->uhci_count_stats[i]);
}
}
}
}
void
uhci_destroy_stats(uhci_state_t *uhcip)
{
int i;
if (UHCI_INTRS_STATS(uhcip)) {
kstat_delete(UHCI_INTRS_STATS(uhcip));
UHCI_INTRS_STATS(uhcip) = NULL;
}
if (UHCI_TOTAL_STATS(uhcip)) {
kstat_delete(UHCI_TOTAL_STATS(uhcip));
UHCI_TOTAL_STATS(uhcip) = NULL;
}
for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
if (uhcip->uhci_count_stats[i]) {
kstat_delete(uhcip->uhci_count_stats[i]);
uhcip->uhci_count_stats[i] = NULL;
}
}
}
void
uhci_do_intrs_stats(uhci_state_t *uhcip, int val)
{
if (UHCI_INTRS_STATS(uhcip) == NULL) {
return;
}
UHCI_INTRS_STATS_DATA(uhcip)->uhci_intrs_total.value.ui64++;
switch (val) {
case USBSTS_REG_HC_HALTED:
UHCI_INTRS_STATS_DATA(uhcip)->uhci_intrs_hc_halted.value.ui64++;
break;
case USBSTS_REG_HC_PROCESS_ERR:
UHCI_INTRS_STATS_DATA(uhcip)->
uhci_intrs_hc_process_err.value.ui64++;
break;
case USBSTS_REG_HOST_SYS_ERR:
UHCI_INTRS_STATS_DATA(uhcip)->
uhci_intrs_host_sys_err.value.ui64++;
break;
case USBSTS_REG_RESUME_DETECT:
UHCI_INTRS_STATS_DATA(uhcip)->
uhci_intrs_resume_detected.value.ui64++;
break;
case USBSTS_REG_USB_ERR_INTR:
UHCI_INTRS_STATS_DATA(uhcip)->
uhci_intrs_usb_err_intr.value.ui64++;
break;
case USBSTS_REG_USB_INTR:
UHCI_INTRS_STATS_DATA(uhcip)->uhci_intrs_usb_intr.value.ui64++;
break;
default:
UHCI_INTRS_STATS_DATA(uhcip)->
uhci_intrs_not_claimed.value.ui64++;
break;
}
}
void
uhci_do_byte_stats(uhci_state_t *uhcip, size_t len, uint8_t attr, uint8_t addr)
{
uint8_t type = attr & USB_EP_ATTR_MASK;
uint8_t dir = addr & USB_EP_DIR_MASK;
switch (dir) {
case USB_EP_DIR_IN:
UHCI_TOTAL_STATS_DATA(uhcip)->reads++;
UHCI_TOTAL_STATS_DATA(uhcip)->nread += len;
switch (type) {
case USB_EP_ATTR_CONTROL:
UHCI_CTRL_STATS(uhcip)->reads++;
UHCI_CTRL_STATS(uhcip)->nread += len;
break;
case USB_EP_ATTR_BULK:
UHCI_BULK_STATS(uhcip)->reads++;
UHCI_BULK_STATS(uhcip)->nread += len;
break;
case USB_EP_ATTR_INTR:
UHCI_INTR_STATS(uhcip)->reads++;
UHCI_INTR_STATS(uhcip)->nread += len;
break;
case USB_EP_ATTR_ISOCH:
UHCI_ISOC_STATS(uhcip)->reads++;
UHCI_ISOC_STATS(uhcip)->nread += len;
break;
}
break;
case USB_EP_DIR_OUT:
UHCI_TOTAL_STATS_DATA(uhcip)->writes++;
UHCI_TOTAL_STATS_DATA(uhcip)->nwritten += len;
switch (type) {
case USB_EP_ATTR_CONTROL:
UHCI_CTRL_STATS(uhcip)->writes++;
UHCI_CTRL_STATS(uhcip)->nwritten += len;
break;
case USB_EP_ATTR_BULK:
UHCI_BULK_STATS(uhcip)->writes++;
UHCI_BULK_STATS(uhcip)->nwritten += len;
break;
case USB_EP_ATTR_INTR:
UHCI_INTR_STATS(uhcip)->writes++;
UHCI_INTR_STATS(uhcip)->nwritten += len;
break;
case USB_EP_ATTR_ISOCH:
UHCI_ISOC_STATS(uhcip)->writes++;
UHCI_ISOC_STATS(uhcip)->nwritten += len;
break;
}
break;
}
}
void
uhci_free_tw(uhci_state_t *uhcip, uhci_trans_wrapper_t *tw)
{
int rval, i;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl, "uhci_free_tw:");
ASSERT(tw != NULL);
if (tw->tw_isoc_strtlen > 0) {
ASSERT(tw->tw_isoc_bufs != NULL);
for (i = 0; i < tw->tw_ncookies; i++) {
rval = ddi_dma_unbind_handle(
tw->tw_isoc_bufs[i].dma_handle);
ASSERT(rval == USB_SUCCESS);
ddi_dma_mem_free(&tw->tw_isoc_bufs[i].mem_handle);
ddi_dma_free_handle(&tw->tw_isoc_bufs[i].dma_handle);
}
kmem_free(tw->tw_isoc_bufs, tw->tw_isoc_strtlen);
} else 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);
}
kmem_free(tw, sizeof (uhci_trans_wrapper_t));
}
void
uhci_deallocate_tw(uhci_state_t *uhcip,
uhci_pipe_private_t *pp, uhci_trans_wrapper_t *tw)
{
uhci_trans_wrapper_t *head;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_deallocate_tw:");
if (tw->tw_hctd_head != NULL) {
ASSERT(tw->tw_hctd_tail != NULL);
return;
}
ASSERT(tw->tw_hctd_tail == NULL);
ASSERT(pp->pp_tw_head != NULL);
head = pp->pp_tw_head;
if (head == tw) {
pp->pp_tw_head = head->tw_next;
if (pp->pp_tw_head == NULL) {
pp->pp_tw_tail = NULL;
}
} else {
while (head->tw_next != tw)
head = head->tw_next;
head->tw_next = tw->tw_next;
if (tw->tw_next == NULL) {
pp->pp_tw_tail = head;
}
}
uhci_free_tw(uhcip, tw);
}
void
uhci_delete_td(uhci_state_t *uhcip, uhci_td_t *td)
{
uhci_td_t *tmp_td;
uhci_trans_wrapper_t *tw = td->tw;
if ((td->outst_td_next == NULL) && (td->outst_td_prev == NULL)) {
uhcip->uhci_outst_tds_head = NULL;
uhcip->uhci_outst_tds_tail = NULL;
} else if (td->outst_td_next == NULL) {
td->outst_td_prev->outst_td_next = NULL;
uhcip->uhci_outst_tds_tail = td->outst_td_prev;
} else if (td->outst_td_prev == NULL) {
td->outst_td_next->outst_td_prev = NULL;
uhcip->uhci_outst_tds_head = td->outst_td_next;
} else {
td->outst_td_prev->outst_td_next = td->outst_td_next;
td->outst_td_next->outst_td_prev = td->outst_td_prev;
}
tmp_td = tw->tw_hctd_head;
if (tmp_td != td) {
while (tmp_td->tw_td_next != td) {
tmp_td = tmp_td->tw_td_next;
}
ASSERT(tmp_td);
tmp_td->tw_td_next = td->tw_td_next;
if (td->tw_td_next == NULL) {
tw->tw_hctd_tail = tmp_td;
}
} else {
tw->tw_hctd_head = tw->tw_hctd_head->tw_td_next;
if (tw->tw_hctd_head == NULL) {
tw->tw_hctd_tail = NULL;
}
}
td->flag = TD_FLAG_FREE;
}
void
uhci_remove_tds_tws(
uhci_state_t *uhcip,
usba_pipe_handle_data_t *ph)
{
usb_opaque_t curr_reqp;
uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *ept = &pp->pp_pipe_handle->p_ep;
uhci_trans_wrapper_t *tw_tmp;
uhci_trans_wrapper_t *tw_head = pp->pp_tw_head;
while (tw_head != NULL) {
tw_tmp = tw_head;
tw_head = tw_head->tw_next;
curr_reqp = tw_tmp->tw_curr_xfer_reqp;
if (curr_reqp) {
if ((tw_tmp->tw_direction == PID_IN) &&
(UHCI_XFER_TYPE(ept) == USB_EP_ATTR_INTR)) {
uhci_deallocate_periodic_in_resource(uhcip,
pp, tw_tmp);
} else {
uhci_hcdi_callback(uhcip, pp,
pp->pp_pipe_handle, tw_tmp, USB_CR_FLUSHED);
}
}
if (tw_tmp->tw_claim != UHCI_MODIFY_TD_BITS_CLAIMED) {
continue;
}
while (tw_tmp->tw_hctd_head != NULL) {
uhci_delete_td(uhcip, tw_tmp->tw_hctd_head);
}
uhci_deallocate_tw(uhcip, pp, tw_tmp);
}
}
void
uhci_remove_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
{
uhci_td_t *dummy_td;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_remove_qh:");
dummy_td = pp->pp_qh->td_tailp;
dummy_td->flag = TD_FLAG_FREE;
switch (UHCI_XFER_TYPE(&pp->pp_pipe_handle->p_ep)) {
case USB_EP_ATTR_CONTROL:
uhci_remove_ctrl_qh(uhcip, pp);
break;
case USB_EP_ATTR_BULK:
uhci_remove_bulk_qh(uhcip, pp);
break;
case USB_EP_ATTR_INTR:
uhci_remove_intr_qh(uhcip, pp);
break;
}
}
static void
uhci_remove_intr_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
{
queue_head_t *qh = pp->pp_qh;
queue_head_t *next_lattice_qh =
QH_VADDR(GetQH32(uhcip, qh->link_ptr) & QH_LINK_PTR_MASK);
qh->prev_qh->link_ptr = qh->link_ptr;
next_lattice_qh->prev_qh = qh->prev_qh;
qh->qh_flag = QUEUE_HEAD_FLAG_FREE;
}
static void
uhci_remove_bulk_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
{
queue_head_t *qh = pp->pp_qh;
queue_head_t *next_lattice_qh;
uint32_t paddr;
paddr = (GetQH32(uhcip, qh->link_ptr) & QH_LINK_PTR_MASK);
next_lattice_qh = (qh == uhcip->uhci_bulk_xfers_q_tail) ?
0 : QH_VADDR(paddr);
if ((qh == uhcip->uhci_bulk_xfers_q_tail) &&
(qh->prev_qh == uhcip->uhci_bulk_xfers_q_head)) {
SetQH32(uhcip, qh->prev_qh->link_ptr, HC_END_OF_LIST);
} else {
qh->prev_qh->link_ptr = qh->link_ptr;
}
if (next_lattice_qh == NULL) {
uhcip->uhci_bulk_xfers_q_tail = qh->prev_qh;
} else {
next_lattice_qh->prev_qh = qh->prev_qh;
}
qh->qh_flag = QUEUE_HEAD_FLAG_FREE;
}
static void
uhci_remove_ctrl_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
{
queue_head_t *qh = pp->pp_qh;
queue_head_t *next_lattice_qh =
QH_VADDR(GetQH32(uhcip, qh->link_ptr) & QH_LINK_PTR_MASK);
qh->prev_qh->link_ptr = qh->link_ptr;
if (next_lattice_qh->prev_qh != NULL) {
next_lattice_qh->prev_qh = qh->prev_qh;
} else {
uhcip->uhci_ctrl_xfers_q_tail = qh->prev_qh;
}
qh->qh_flag = QUEUE_HEAD_FLAG_FREE;
}
static uhci_td_t *
uhci_allocate_td_from_pool(uhci_state_t *uhcip)
{
int index;
uhci_td_t *td;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
for (index = 0; index < uhci_td_pool_size; index ++) {
if (uhcip->uhci_td_pool_addr[index].flag == TD_FLAG_FREE) {
break;
}
}
if (index == uhci_td_pool_size) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_allocate_td_from_pool: TD exhausted");
return (NULL);
}
USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
"uhci_allocate_td_from_pool: Allocated %d", index);
td = &uhcip->uhci_td_pool_addr[index];
td->flag = TD_FLAG_DUMMY;
td->qh_td_prev = NULL;
return (td);
}
int
uhci_insert_bulk_td(
uhci_state_t *uhcip,
usba_pipe_handle_data_t *ph,
usb_bulk_req_t *req,
usb_flags_t flags)
{
size_t length;
uint_t mps;
uint_t num_bulk_tds, i, j;
uint32_t buf_offs;
uhci_td_t *bulk_td_ptr;
uhci_td_t *current_dummy, *tmp_td;
uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
uhci_trans_wrapper_t *tw;
uhci_bulk_isoc_xfer_t *bulk_xfer_info;
uhci_bulk_isoc_td_pool_t *td_pool_ptr;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_bulk_td: req: 0x%p, flags = 0x%x", (void *)req, flags);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if ((tw = uhci_create_transfer_wrapper(uhcip, pp, req->bulk_len,
flags)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_bulk_td: TW allocation failed");
return (USB_NO_RESOURCES);
}
tw->tw_bytes_xfered = 0;
tw->tw_bytes_pending = req->bulk_len;
tw->tw_handle_td = uhci_handle_bulk_td;
tw->tw_handle_callback_value = (usb_opaque_t)req->bulk_data;
tw->tw_timeout_cnt = req->bulk_timeout;
tw->tw_data = req->bulk_data;
tw->tw_curr_xfer_reqp = (usb_opaque_t)req;
tw->tw_direction = (UHCI_XFER_DIR(&ph->p_ep) == USB_EP_DIR_OUT) ?
PID_OUT : PID_IN;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_bulk_td: direction: 0x%x", tw->tw_direction);
if (tw->tw_direction == PID_OUT) {
if (req->bulk_len) {
ASSERT(req->bulk_data != NULL);
ddi_rep_put8(tw->tw_accesshandle,
req->bulk_data->b_rptr,
(uint8_t *)tw->tw_buf,
req->bulk_len, DDI_DEV_AUTOINCR);
}
}
length = mps = pp->pp_pipe_handle->p_ep.wMaxPacketSize;
if ((tw->tw_bytes_pending / mps) >= MAX_NUM_BULK_TDS_PER_XFER) {
num_bulk_tds = MAX_NUM_BULK_TDS_PER_XFER;
} else {
num_bulk_tds = (tw->tw_bytes_pending / mps);
if (tw->tw_bytes_pending % mps || tw->tw_bytes_pending == 0) {
num_bulk_tds++;
length = (tw->tw_bytes_pending % mps);
}
}
if ((bulk_xfer_info = kmem_zalloc(
sizeof (uhci_bulk_isoc_xfer_t), KM_NOSLEEP)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_bulk_td: kmem_zalloc failed");
uhci_deallocate_tw(uhcip, pp, tw);
return (USB_FAILURE);
}
if (uhci_alloc_bulk_isoc_tds(uhcip, num_bulk_tds, bulk_xfer_info) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_insert_bulk_td: alloc_bulk_isoc_tds failed");
kmem_free(bulk_xfer_info, sizeof (uhci_bulk_isoc_xfer_t));
uhci_deallocate_tw(uhcip, pp, tw);
return (USB_FAILURE);
}
td_pool_ptr = &bulk_xfer_info->td_pools[0];
bulk_td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
bulk_td_ptr[0].qh_td_prev = NULL;
current_dummy = pp->pp_qh->td_tailp;
buf_offs = 0;
pp->pp_qh->bulk_xfer_info = bulk_xfer_info;
for (i = 0; i < bulk_xfer_info->num_pools; i++) {
for (j = 0; j < (td_pool_ptr->num_tds - 1); j++) {
uhci_fill_in_bulk_isoc_td(uhcip, &bulk_td_ptr[j],
&bulk_td_ptr[j+1], BULKTD_PADDR(td_pool_ptr,
&bulk_td_ptr[j+1]), ph, buf_offs, mps, tw);
buf_offs += mps;
}
if (i == (bulk_xfer_info->num_pools - 1)) {
uhci_fill_in_bulk_isoc_td(uhcip, &bulk_td_ptr[j],
current_dummy, TD_PADDR(current_dummy),
ph, buf_offs, length, tw);
} else {
tmp_td = &bulk_td_ptr[j];
td_pool_ptr = &bulk_xfer_info->td_pools[i + 1];
bulk_td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
uhci_fill_in_bulk_isoc_td(uhcip, tmp_td,
&bulk_td_ptr[0], BULKTD_PADDR(td_pool_ptr,
&bulk_td_ptr[0]), ph, buf_offs, mps, tw);
buf_offs += mps;
}
}
bulk_xfer_info->num_tds = (ushort_t)num_bulk_tds;
if (uhcip->uhci_pending_bulk_cmds++ == 0) {
uhcip->uhci_bulk_xfers_q_tail->link_ptr =
uhcip->uhci_bulk_xfers_q_head->link_ptr;
USB_DPRINTF_L3(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_insert_bulk_td: count = %d no tds %d",
uhcip->uhci_pending_bulk_cmds, num_bulk_tds);
}
SetQH32(uhcip, pp->pp_qh->element_ptr,
bulk_xfer_info->td_pools[0].cookie.dmac_address);
return (USB_SUCCESS);
}
void
uhci_fill_in_bulk_isoc_td(uhci_state_t *uhcip, uhci_td_t *current_td,
uhci_td_t *next_td,
uint32_t next_td_paddr,
usba_pipe_handle_data_t *ph,
uint_t offset,
uint_t length,
uhci_trans_wrapper_t *tw)
{
uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *ept = &pp->pp_pipe_handle->p_ep;
uint32_t buf_addr;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_fill_in_bulk_isoc_td: tw 0x%p offs 0x%x length 0x%x",
(void *)tw, offset, length);
bzero((char *)current_td, sizeof (uhci_td_t));
SetTD32(uhcip, current_td->link_ptr, next_td_paddr | HC_DEPTH_FIRST);
switch (UHCI_XFER_TYPE(ept)) {
case USB_EP_ATTR_ISOCH:
if (((usb_isoc_req_t *)tw->tw_curr_xfer_reqp)->isoc_attributes
& USB_ATTRS_SHORT_XFER_OK) {
SetTD_spd(uhcip, current_td, 1);
}
break;
case USB_EP_ATTR_BULK:
if (((usb_bulk_req_t *)tw->tw_curr_xfer_reqp)->bulk_attributes
& USB_ATTRS_SHORT_XFER_OK) {
SetTD_spd(uhcip, current_td, 1);
}
break;
}
mutex_enter(&ph->p_usba_device->usb_mutex);
SetTD_c_err(uhcip, current_td, UHCI_MAX_ERR_COUNT);
SetTD_status(uhcip, current_td, UHCI_TD_ACTIVE);
SetTD_ioc(uhcip, current_td, INTERRUPT_ON_COMPLETION);
SetTD_mlen(uhcip, current_td,
(length == 0) ? ZERO_LENGTH : (length - 1));
SetTD_dtogg(uhcip, current_td, pp->pp_data_toggle);
SetTD_devaddr(uhcip, current_td, ph->p_usba_device->usb_addr);
SetTD_endpt(uhcip, current_td, ph->p_ep.bEndpointAddress &
END_POINT_ADDRESS_MASK);
SetTD_PID(uhcip, current_td, tw->tw_direction);
switch (UHCI_XFER_TYPE(ept)) {
case USB_EP_ATTR_ISOCH:
buf_addr = tw->tw_isoc_bufs[offset].cookie.dmac_address;
break;
case USB_EP_ATTR_BULK:
buf_addr = uhci_get_tw_paddr_by_offs(uhcip, offset,
length, tw);
break;
}
SetTD32(uhcip, current_td->buffer_address, buf_addr);
if (UHCI_XFER_TYPE(ept) == USB_EP_ATTR_ISOCH) {
pp->pp_data_toggle = 0;
SetTD_iso(uhcip, current_td, 1);
} else {
ADJ_DATA_TOGGLE(pp);
next_td->qh_td_prev = current_td;
pp->pp_qh->td_tailp = next_td;
}
current_td->outst_td_next = NULL;
current_td->outst_td_prev = uhcip->uhci_outst_tds_tail;
if (uhcip->uhci_outst_tds_head == NULL) {
uhcip->uhci_outst_tds_head = current_td;
} else {
uhcip->uhci_outst_tds_tail->outst_td_next = current_td;
}
uhcip->uhci_outst_tds_tail = current_td;
current_td->tw = tw;
if (tw->tw_hctd_head == NULL) {
ASSERT(tw->tw_hctd_tail == NULL);
tw->tw_hctd_head = current_td;
tw->tw_hctd_tail = current_td;
} else {
tw->tw_hctd_tail->tw_td_next = current_td;
tw->tw_hctd_tail = current_td;
}
mutex_exit(&ph->p_usba_device->usb_mutex);
}
static int
uhci_alloc_bulk_isoc_tds(
uhci_state_t *uhcip,
uint_t num_tds,
uhci_bulk_isoc_xfer_t *info)
{
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_alloc_bulk_isoc_tds: num_tds: 0x%x info: 0x%p",
num_tds, (void *)info);
info->num_pools = 1;
if (uhci_alloc_memory_for_tds(uhcip, num_tds, info) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"alloc_memory_for_tds failed: num_tds %d num_pools %d",
num_tds, info->num_pools);
info->num_pools = num_tds / UHCI_MAX_TD_NUM_PER_POOL;
if (num_tds % UHCI_MAX_TD_NUM_PER_POOL) {
info->num_pools++;
}
if (uhci_alloc_memory_for_tds(uhcip, num_tds, info) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"alloc_memory_for_tds failed: num_tds %d "
"num_pools %d", num_tds, info->num_pools);
return (USB_NO_RESOURCES);
}
}
return (USB_SUCCESS);
}
static int
uhci_alloc_memory_for_tds(
uhci_state_t *uhcip,
uint_t num_tds,
uhci_bulk_isoc_xfer_t *info)
{
int result, i, j, err;
size_t real_length;
uint_t ccount, num;
ddi_device_acc_attr_t dev_attr;
uhci_bulk_isoc_td_pool_t *td_pool_ptr1, *td_pool_ptr2;
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_alloc_memory_for_tds: num_tds: 0x%x info: 0x%p "
"num_pools: %u", num_tds, (void *)info, info->num_pools);
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 ((info->td_pools = kmem_zalloc(
(sizeof (uhci_bulk_isoc_td_pool_t) * info->num_pools),
KM_SLEEP)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_alloc_memory_for_tds: alloc td_pools failed");
return (USB_FAILURE);
}
for (i = 0; i < info->num_pools; i++) {
if (info->num_pools == 1) {
num = num_tds;
} else if (i < (info->num_pools - 1)) {
num = UHCI_MAX_TD_NUM_PER_POOL;
} else {
num = (num_tds % UHCI_MAX_TD_NUM_PER_POOL);
}
td_pool_ptr1 = &info->td_pools[i];
if (ddi_dma_alloc_handle(uhcip->uhci_dip,
&uhcip->uhci_dma_attr, DDI_DMA_SLEEP, 0,
&td_pool_ptr1->dma_handle) != DDI_SUCCESS) {
for (j = 0; j < i; j++) {
td_pool_ptr2 = &info->td_pools[j];
result = ddi_dma_unbind_handle(
td_pool_ptr2->dma_handle);
ASSERT(result == DDI_SUCCESS);
ddi_dma_mem_free(&td_pool_ptr2->mem_handle);
ddi_dma_free_handle(&td_pool_ptr2->dma_handle);
}
kmem_free(info->td_pools,
(sizeof (uhci_bulk_isoc_td_pool_t) *
info->num_pools));
return (USB_FAILURE);
}
if (ddi_dma_mem_alloc(td_pool_ptr1->dma_handle,
num * sizeof (uhci_td_t), &dev_attr,
DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
&td_pool_ptr1->pool_addr, &real_length,
&td_pool_ptr1->mem_handle) != DDI_SUCCESS) {
ddi_dma_free_handle(&td_pool_ptr1->dma_handle);
for (j = 0; j < i; j++) {
td_pool_ptr2 = &info->td_pools[j];
result = ddi_dma_unbind_handle(
td_pool_ptr2->dma_handle);
ASSERT(result == DDI_SUCCESS);
ddi_dma_mem_free(&td_pool_ptr2->mem_handle);
ddi_dma_free_handle(&td_pool_ptr2->dma_handle);
}
kmem_free(info->td_pools,
(sizeof (uhci_bulk_isoc_td_pool_t) *
info->num_pools));
return (USB_FAILURE);
}
result = ddi_dma_addr_bind_handle(td_pool_ptr1->dma_handle,
NULL, (caddr_t)td_pool_ptr1->pool_addr, real_length,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
&td_pool_ptr1->cookie, &ccount);
err = USB_SUCCESS;
if (result != DDI_DMA_MAPPED) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_allocate_memory_for_tds: Result = %d",
result);
uhci_decode_ddi_dma_addr_bind_handle_result(uhcip,
result);
err = USB_FAILURE;
}
if ((result == DDI_DMA_MAPPED) && (ccount != 1)) {
USB_DPRINTF_L2(PRINT_MASK_ATTA,
uhcip->uhci_log_hdl,
"uhci_allocate_memory_for_tds: "
"More than 1 cookie");
result = ddi_dma_unbind_handle(
td_pool_ptr1->dma_handle);
ASSERT(result == DDI_SUCCESS);
err = USB_FAILURE;
}
if (err == USB_FAILURE) {
ddi_dma_mem_free(&td_pool_ptr1->mem_handle);
ddi_dma_free_handle(&td_pool_ptr1->dma_handle);
for (j = 0; j < i; j++) {
td_pool_ptr2 = &info->td_pools[j];
result = ddi_dma_unbind_handle(
td_pool_ptr2->dma_handle);
ASSERT(result == DDI_SUCCESS);
ddi_dma_mem_free(&td_pool_ptr2->mem_handle);
ddi_dma_free_handle(&td_pool_ptr2->dma_handle);
}
kmem_free(info->td_pools,
(sizeof (uhci_bulk_isoc_td_pool_t) *
info->num_pools));
return (USB_FAILURE);
}
bzero((void *)td_pool_ptr1->pool_addr,
num * sizeof (uhci_td_t));
td_pool_ptr1->num_tds = (ushort_t)num;
}
return (USB_SUCCESS);
}
void
uhci_handle_bulk_td(uhci_state_t *uhcip, uhci_td_t *td)
{
uint_t num_bulk_tds, index, td_count, j;
usb_cr_t error;
uint_t length, bytes_xfered;
ushort_t MaxPacketSize;
uint32_t buf_offs, paddr;
uhci_td_t *bulk_td_ptr, *current_dummy, *td_head;
uhci_td_t *tmp_td;
queue_head_t *qh, *next_qh;
uhci_trans_wrapper_t *tw = td->tw;
uhci_pipe_private_t *pp = tw->tw_pipe_private;
uhci_bulk_isoc_xfer_t *bulk_xfer_info;
uhci_bulk_isoc_td_pool_t *td_pool_ptr;
usba_pipe_handle_data_t *ph;
USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_handle_bulk_td: td = 0x%p tw = 0x%p", (void *)td, (void *)tw);
bytes_xfered = ZERO_LENGTH;
if (GetTD_status(uhcip, td) & TD_STATUS_MASK) {
uhci_handle_bulk_td_errors(uhcip, td);
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_handle_bulk_td: error; data toggle: 0x%x",
pp->pp_data_toggle);
return;
}
bytes_xfered = GetTD_alen(uhcip, td);
if (bytes_xfered != ZERO_LENGTH) {
tw->tw_bytes_pending -= (bytes_xfered + 1);
tw->tw_bytes_xfered += (bytes_xfered + 1);
}
bulk_xfer_info = pp->pp_qh->bulk_xfer_info;
ph = tw->tw_pipe_private->pp_pipe_handle;
if (bytes_xfered != GetTD_mlen(uhcip, td)) {
bulk_xfer_info->num_tds = 1;
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_handle_bulk_td: Data underrun occured");
pp->pp_data_toggle = GetTD_dtogg(uhcip, td) == 0 ? 1 : 0;
}
USB_DPRINTF_L3(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_handle_bulk_td: completed TD data toggle: 0x%x",
GetTD_dtogg(uhcip, td));
if (--bulk_xfer_info->num_tds == 0) {
uhci_delete_td(uhcip, td);
if ((tw->tw_bytes_pending) &&
(GetTD_mlen(uhcip, td) - GetTD_alen(uhcip, td) == 0)) {
MaxPacketSize = pp->pp_pipe_handle->p_ep.wMaxPacketSize;
length = MaxPacketSize;
qh = pp->pp_qh;
paddr = GetQH32(uhcip, qh->link_ptr) & QH_LINK_PTR_MASK;
if (GetQH32(uhcip, qh->link_ptr) !=
GetQH32(uhcip,
uhcip->uhci_bulk_xfers_q_head->link_ptr)) {
next_qh = QH_VADDR(paddr);
SetQH32(uhcip, qh->prev_qh->link_ptr,
paddr|(0x2));
next_qh->prev_qh = qh->prev_qh;
SetQH32(uhcip, qh->link_ptr,
GetQH32(uhcip,
uhcip->uhci_bulk_xfers_q_head->link_ptr));
qh->prev_qh = uhcip->uhci_bulk_xfers_q_tail;
SetQH32(uhcip,
uhcip->uhci_bulk_xfers_q_tail->link_ptr,
QH_PADDR(qh) | 0x2);
uhcip->uhci_bulk_xfers_q_tail = qh;
}
if ((tw->tw_bytes_pending / MaxPacketSize) >=
MAX_NUM_BULK_TDS_PER_XFER) {
num_bulk_tds = MAX_NUM_BULK_TDS_PER_XFER;
} else {
num_bulk_tds =
(tw->tw_bytes_pending / MaxPacketSize);
if (tw->tw_bytes_pending % MaxPacketSize) {
num_bulk_tds++;
length = (tw->tw_bytes_pending %
MaxPacketSize);
}
}
current_dummy = pp->pp_qh->td_tailp;
td_pool_ptr = &bulk_xfer_info->td_pools[0];
bulk_td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
buf_offs = tw->tw_bytes_xfered;
td_count = num_bulk_tds;
index = 0;
while (td_count > 0) {
for (j = 0;
(j < (td_pool_ptr->num_tds - 1)) &&
(td_count > 1); j++, td_count--) {
uhci_fill_in_bulk_isoc_td(uhcip,
&bulk_td_ptr[j], &bulk_td_ptr[j+1],
BULKTD_PADDR(td_pool_ptr,
&bulk_td_ptr[j+1]), ph, buf_offs,
MaxPacketSize, tw);
buf_offs += MaxPacketSize;
}
if (td_count == 1) {
uhci_fill_in_bulk_isoc_td(uhcip,
&bulk_td_ptr[j], current_dummy,
TD_PADDR(current_dummy), ph,
buf_offs, length, tw);
break;
} else {
tmp_td = &bulk_td_ptr[j];
ASSERT(index <
(bulk_xfer_info->num_pools - 1));
td_pool_ptr = &bulk_xfer_info->
td_pools[index + 1];
bulk_td_ptr = (uhci_td_t *)
td_pool_ptr->pool_addr;
uhci_fill_in_bulk_isoc_td(uhcip,
tmp_td, &bulk_td_ptr[0],
BULKTD_PADDR(td_pool_ptr,
&bulk_td_ptr[0]), ph, buf_offs,
MaxPacketSize, tw);
buf_offs += MaxPacketSize;
td_count--;
index++;
}
}
pp->pp_qh->bulk_xfer_info = bulk_xfer_info;
bulk_xfer_info->num_tds = (ushort_t)num_bulk_tds;
SetQH32(uhcip, pp->pp_qh->element_ptr,
bulk_xfer_info->td_pools[0].cookie.dmac_address);
} else {
usba_pipe_handle_data_t *usb_pp = pp->pp_pipe_handle;
pp->pp_qh->bulk_xfer_info = NULL;
if (tw->tw_bytes_pending) {
SetQH32(uhcip, pp->pp_qh->element_ptr,
TD_PADDR(pp->pp_qh->td_tailp));
td_head = tw->tw_hctd_head;
while (td_head != NULL) {
uhci_delete_td(uhcip, td_head);
td_head = tw->tw_hctd_head;
}
}
if (tw->tw_direction == PID_IN) {
usb_req_attrs_t attrs = ((usb_bulk_req_t *)
tw->tw_curr_xfer_reqp)->bulk_attributes;
error = USB_CR_OK;
if (tw->tw_bytes_pending &&
(!(attrs & USB_ATTRS_SHORT_XFER_OK))) {
error = USB_CR_DATA_UNDERRUN;
}
uhci_sendup_td_message(uhcip, error, tw);
} else {
uhci_do_byte_stats(uhcip, tw->tw_length,
usb_pp->p_ep.bmAttributes,
usb_pp->p_ep.bEndpointAddress);
if (tw->tw_bytes_pending) {
tw->tw_data->b_rptr +=
tw->tw_bytes_xfered;
USB_DPRINTF_L2(PRINT_MASK_ATTA,
uhcip->uhci_log_hdl,
"uhci_handle_bulk_td: "
"data underrun occurred");
uhci_hcdi_callback(uhcip, pp,
tw->tw_pipe_private->pp_pipe_handle,
tw, USB_CR_DATA_UNDERRUN);
} else {
uhci_hcdi_callback(uhcip, pp,
tw->tw_pipe_private->pp_pipe_handle,
tw, USB_CR_OK);
}
}
uhci_deallocate_tw(uhcip, pp, tw);
for (j = 0; j < bulk_xfer_info->num_pools; j++) {
td_pool_ptr = &bulk_xfer_info->td_pools[j];
(void) ddi_dma_unbind_handle(
td_pool_ptr->dma_handle);
ddi_dma_mem_free(&td_pool_ptr->mem_handle);
ddi_dma_free_handle(&td_pool_ptr->dma_handle);
}
kmem_free(bulk_xfer_info->td_pools,
(sizeof (uhci_bulk_isoc_td_pool_t) *
bulk_xfer_info->num_pools));
kmem_free(bulk_xfer_info,
sizeof (uhci_bulk_isoc_xfer_t));
if (--uhcip->uhci_pending_bulk_cmds == 0) {
uhcip->uhci_bulk_xfers_q_tail->link_ptr =
HC_END_OF_LIST;
USB_DPRINTF_L3(PRINT_MASK_ATTA,
uhcip->uhci_log_hdl,
"uhci_handle_bulk_td: count = %d",
uhcip->uhci_pending_bulk_cmds);
}
}
} else {
uhci_delete_td(uhcip, td);
}
}
void
uhci_handle_bulk_td_errors(uhci_state_t *uhcip, uhci_td_t *td)
{
usb_cr_t usb_err;
uint32_t paddr_tail, element_ptr, paddr;
uhci_td_t *next_td;
uhci_pipe_private_t *pp;
uhci_trans_wrapper_t *tw = td->tw;
usba_pipe_handle_data_t *ph;
uhci_bulk_isoc_td_pool_t *td_pool_ptr = NULL;
USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_handle_bulk_td_errors: td = %p", (void *)td);
#ifdef DEBUG
uhci_print_td(uhcip, td);
#endif
tw = td->tw;
ph = tw->tw_pipe_private->pp_pipe_handle;
pp = (uhci_pipe_private_t *)ph->p_hcd_private;
element_ptr = GetQH32(uhcip, pp->pp_qh->element_ptr) &
QH_ELEMENT_PTR_MASK;
paddr_tail = TD_PADDR(pp->pp_qh->td_tailp);
if (element_ptr != paddr_tail) {
paddr = (element_ptr & QH_ELEMENT_PTR_MASK);
uhci_get_bulk_td_by_paddr(uhcip, pp->pp_qh->bulk_xfer_info,
paddr, &td_pool_ptr);
next_td = BULKTD_VADDR(td_pool_ptr, paddr);
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_handle_bulk_td_errors: next td = %p",
(void *)next_td);
usb_err = uhci_parse_td_error(uhcip, pp, next_td);
} else {
usb_err = uhci_parse_td_error(uhcip, pp, td);
}
SetQH32(uhcip, pp->pp_qh->element_ptr, TD_PADDR(pp->pp_qh->td_tailp));
if ((tw->tw_direction == PID_OUT) && tw->tw_data) {
tw->tw_data->b_rptr += tw->tw_bytes_xfered;
}
uhci_remove_bulk_tds_tws(uhcip, tw->tw_pipe_private, UHCI_IN_ERROR);
if (--uhcip->uhci_pending_bulk_cmds == 0) {
uhcip->uhci_bulk_xfers_q_tail->link_ptr = HC_END_OF_LIST;
USB_DPRINTF_L3(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"uhci_handle_bulk_td_errors: count = %d",
uhcip->uhci_pending_bulk_cmds);
}
uhci_hcdi_callback(uhcip, pp, ph, tw, usb_err);
uhci_deallocate_tw(uhcip, pp, tw);
}
static void
uhci_get_bulk_td_by_paddr(
uhci_state_t *uhcip,
uhci_bulk_isoc_xfer_t *info,
uint32_t paddr,
uhci_bulk_isoc_td_pool_t **td_pool_pp)
{
uint_t i = 0;
while (i < info->num_pools) {
*td_pool_pp = &info->td_pools[i];
if (((*td_pool_pp)->cookie.dmac_address <= paddr) &&
(((*td_pool_pp)->cookie.dmac_address +
(*td_pool_pp)->cookie.dmac_size) > paddr)) {
break;
}
i++;
}
ASSERT(i < info->num_pools);
}
void
uhci_remove_bulk_tds_tws(
uhci_state_t *uhcip,
uhci_pipe_private_t *pp,
int what)
{
uint_t rval, i;
uhci_td_t *head;
uhci_td_t *head_next;
usb_opaque_t curr_reqp;
uhci_bulk_isoc_xfer_t *info;
uhci_bulk_isoc_td_pool_t *td_pool_ptr;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if ((info = pp->pp_qh->bulk_xfer_info) == NULL) {
return;
}
head = uhcip->uhci_outst_tds_head;
while (head) {
uhci_trans_wrapper_t *tw_tmp = head->tw;
head_next = head->outst_td_next;
if (pp->pp_qh == tw_tmp->tw_pipe_private->pp_qh) {
curr_reqp = tw_tmp->tw_curr_xfer_reqp;
if (curr_reqp &&
((what == UHCI_IN_CLOSE) ||
(what == UHCI_IN_RESET))) {
uhci_hcdi_callback(uhcip, pp,
pp->pp_pipe_handle,
tw_tmp, USB_CR_FLUSHED);
}
uhci_delete_td(uhcip, head);
if (what == UHCI_IN_CLOSE || what == UHCI_IN_RESET) {
ASSERT(info->num_tds > 0);
if (--info->num_tds == 0) {
uhci_deallocate_tw(uhcip, pp, tw_tmp);
if (--uhcip->uhci_pending_bulk_cmds
== 0) {
uhcip->uhci_bulk_xfers_q_tail->
link_ptr = HC_END_OF_LIST;
USB_DPRINTF_L3(PRINT_MASK_ATTA,
uhcip->uhci_log_hdl,
"uhci_remove_bulk_tds_tws:"
" count = %d",
uhcip->
uhci_pending_bulk_cmds);
}
}
}
}
head = head_next;
}
if (what == UHCI_IN_CLOSE || what == UHCI_IN_RESET) {
ASSERT(info->num_tds == 0);
}
for (i = 0; i < info->num_pools; i++) {
td_pool_ptr = &info->td_pools[i];
rval = ddi_dma_unbind_handle(td_pool_ptr->dma_handle);
ASSERT(rval == DDI_SUCCESS);
ddi_dma_mem_free(&td_pool_ptr->mem_handle);
ddi_dma_free_handle(&td_pool_ptr->dma_handle);
}
kmem_free(info->td_pools, (sizeof (uhci_bulk_isoc_td_pool_t) *
info->num_pools));
kmem_free(info, sizeof (uhci_bulk_isoc_xfer_t));
pp->pp_qh->bulk_xfer_info = NULL;
}
void
uhci_save_data_toggle(uhci_pipe_private_t *pp)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
mutex_enter(&ph->p_mutex);
usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
pp->pp_data_toggle);
mutex_exit(&ph->p_mutex);
}
static uhci_trans_wrapper_t *
uhci_create_isoc_transfer_wrapper(
uhci_state_t *uhcip,
uhci_pipe_private_t *pp,
usb_isoc_req_t *req,
size_t length,
usb_flags_t usb_flags)
{
int result;
size_t real_length, strtlen, xfer_size;
uhci_trans_wrapper_t *tw;
ddi_device_acc_attr_t dev_attr;
ddi_dma_attr_t dma_attr;
int kmem_flag;
int (*dmamem_wait)(caddr_t);
uint_t i, j, ccount;
usb_isoc_req_t *tmp_req = req;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if (UHCI_XFER_TYPE(&pp->pp_pipe_handle->p_ep) != USB_EP_ATTR_ISOCH) {
return (NULL);
}
if ((req == NULL) && (UHCI_XFER_DIR(&pp->pp_pipe_handle->p_ep) ==
USB_EP_DIR_IN)) {
tmp_req = (usb_isoc_req_t *)pp->pp_client_periodic_in_reqp;
}
if (tmp_req == NULL) {
return (NULL);
}
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_isoc_transfer_wrapper: length = 0x%lx flags = 0x%x",
length, usb_flags);
if (servicing_interrupt()) {
kmem_flag = KM_NOSLEEP;
dmamem_wait = DDI_DMA_DONTWAIT;
} else {
kmem_flag = KM_SLEEP;
dmamem_wait = DDI_DMA_SLEEP;
}
if ((tw = kmem_zalloc(sizeof (uhci_trans_wrapper_t), kmem_flag)) ==
NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_isoc_transfer_wrapper: kmem_alloc failed");
return (NULL);
}
strtlen = sizeof (uhci_isoc_buf_t) * tmp_req->isoc_pkts_count;
if ((tw->tw_isoc_bufs = kmem_zalloc(strtlen, kmem_flag)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_isoc_transfer_wrapper: kmem_alloc "
"isoc buffer failed");
kmem_free(tw, sizeof (uhci_trans_wrapper_t));
return (NULL);
}
bcopy(&uhcip->uhci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
dma_attr.dma_attr_sgllen = 1;
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;
tw->tw_length = length;
for (i = 0; i < tmp_req->isoc_pkts_count; i++) {
tw->tw_isoc_bufs[i].index = (ushort_t)i;
if ((result = ddi_dma_alloc_handle(uhcip->uhci_dip, &dma_attr,
dmamem_wait, 0, &tw->tw_isoc_bufs[i].dma_handle)) !=
DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_isoc_transfer_wrapper: "
"Alloc handle %d failed", i);
for (j = 0; j < i; j++) {
result = ddi_dma_unbind_handle(
tw->tw_isoc_bufs[j].dma_handle);
ASSERT(result == USB_SUCCESS);
ddi_dma_mem_free(&tw->tw_isoc_bufs[j].
mem_handle);
ddi_dma_free_handle(&tw->tw_isoc_bufs[j].
dma_handle);
}
kmem_free(tw->tw_isoc_bufs, strtlen);
kmem_free(tw, sizeof (uhci_trans_wrapper_t));
return (NULL);
}
xfer_size = tmp_req->isoc_pkt_descr[i].isoc_pkt_length;
if ((result = ddi_dma_mem_alloc(tw->tw_isoc_bufs[i].dma_handle,
xfer_size, &dev_attr, DDI_DMA_CONSISTENT, dmamem_wait,
NULL, (caddr_t *)&tw->tw_isoc_bufs[i].buf_addr,
&real_length, &tw->tw_isoc_bufs[i].mem_handle)) !=
DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_isoc_transfer_wrapper: "
"dma_mem_alloc %d fail", i);
ddi_dma_free_handle(&tw->tw_isoc_bufs[i].dma_handle);
for (j = 0; j < i; j++) {
result = ddi_dma_unbind_handle(
tw->tw_isoc_bufs[j].dma_handle);
ASSERT(result == USB_SUCCESS);
ddi_dma_mem_free(&tw->tw_isoc_bufs[j].
mem_handle);
ddi_dma_free_handle(&tw->tw_isoc_bufs[j].
dma_handle);
}
kmem_free(tw->tw_isoc_bufs, strtlen);
kmem_free(tw, sizeof (uhci_trans_wrapper_t));
return (NULL);
}
ASSERT(real_length >= xfer_size);
result = ddi_dma_addr_bind_handle(
tw->tw_isoc_bufs[i].dma_handle, NULL,
(caddr_t)tw->tw_isoc_bufs[i].buf_addr, real_length,
DDI_DMA_RDWR|DDI_DMA_CONSISTENT, dmamem_wait, NULL,
&tw->tw_isoc_bufs[i].cookie, &ccount);
if ((result == DDI_DMA_MAPPED) && (ccount == 1)) {
tw->tw_isoc_bufs[i].length = xfer_size;
continue;
} else {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_isoc_transfer_wrapper: "
"Bind handle %d failed", i);
if (result == DDI_DMA_MAPPED) {
result = ddi_dma_unbind_handle(
tw->tw_isoc_bufs[i].dma_handle);
ASSERT(result == USB_SUCCESS);
}
ddi_dma_mem_free(&tw->tw_isoc_bufs[i].mem_handle);
ddi_dma_free_handle(&tw->tw_isoc_bufs[i].dma_handle);
for (j = 0; j < i; j++) {
result = ddi_dma_unbind_handle(
tw->tw_isoc_bufs[j].dma_handle);
ASSERT(result == USB_SUCCESS);
ddi_dma_mem_free(&tw->tw_isoc_bufs[j].
mem_handle);
ddi_dma_free_handle(&tw->tw_isoc_bufs[j].
dma_handle);
}
kmem_free(tw->tw_isoc_bufs, strtlen);
kmem_free(tw, sizeof (uhci_trans_wrapper_t));
return (NULL);
}
}
tw->tw_ncookies = tmp_req->isoc_pkts_count;
tw->tw_isoc_strtlen = strtlen;
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;
ASSERT(tw->tw_next == NULL);
}
tw->tw_pipe_private = pp;
tw->tw_flags = usb_flags;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_create_isoc_transfer_wrapper: tw = 0x%p, ncookies = %u",
(void *)tw, tw->tw_ncookies);
return (tw);
}
int
uhci_insert_isoc_td(
uhci_state_t *uhcip,
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_req,
size_t length,
usb_flags_t flags)
{
int rval = USB_SUCCESS;
int error;
uint_t ddic;
uint32_t i, j, index;
uint32_t bytes_to_xfer;
uint32_t expired_frames = 0;
usb_frame_number_t start_frame, end_frame, current_frame;
uhci_td_t *td_ptr;
uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
uhci_trans_wrapper_t *tw;
uhci_bulk_isoc_xfer_t *isoc_xfer_info;
uhci_bulk_isoc_td_pool_t *td_pool_ptr;
USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_insert_isoc_td: ph = 0x%p isoc req = %p length = %lu",
(void *)ph, (void *)isoc_req, length);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if ((tw = uhci_create_isoc_transfer_wrapper(uhcip, pp, isoc_req,
length, flags)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_insert_isoc_td: TW allocation failed");
return (USB_NO_RESOURCES);
}
tw->tw_curr_xfer_reqp = (usb_opaque_t)isoc_req;
tw->tw_handle_td = uhci_handle_isoc_td;
tw->tw_handle_callback_value = NULL;
tw->tw_direction = (UHCI_XFER_DIR(&ph->p_ep) == USB_EP_DIR_OUT) ?
PID_OUT : PID_IN;
if ((tw->tw_direction == PID_OUT) && length) {
uchar_t *p;
ASSERT(isoc_req->isoc_data != NULL);
p = isoc_req->isoc_data->b_rptr;
for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
ddi_rep_put8(tw->tw_isoc_bufs[i].mem_handle,
p, (uint8_t *)tw->tw_isoc_bufs[i].buf_addr,
isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
DDI_DEV_AUTOINCR);
p += isoc_req->isoc_pkt_descr[i].isoc_pkt_length;
}
}
if (tw->tw_direction == PID_IN) {
if ((rval = uhci_allocate_periodic_in_resource(uhcip, pp, tw,
flags)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_insert_isoc_td: isoc_req_t alloc failed");
uhci_deallocate_tw(uhcip, pp, tw);
return (rval);
}
isoc_req = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
}
tw->tw_isoc_req = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
isoc_xfer_info = (uhci_bulk_isoc_xfer_t *)&tw->tw_xfer_info;
isoc_xfer_info->num_tds = isoc_req->isoc_pkts_count;
if ((rval = uhci_alloc_bulk_isoc_tds(uhcip, isoc_req->isoc_pkts_count,
isoc_xfer_info)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_alloc_bulk_isoc_td: Memory allocation failure");
if (tw->tw_direction == PID_IN) {
uhci_deallocate_periodic_in_resource(uhcip, pp, tw);
}
uhci_deallocate_tw(uhcip, pp, tw);
return (rval);
}
td_pool_ptr = &isoc_xfer_info->td_pools[0];
td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
index = 0;
USB_DPRINTF_L3(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_insert_isoc_td : isoc pkts %d", isoc_req->isoc_pkts_count);
for (i = 0; i < isoc_xfer_info->num_pools; i++) {
for (j = 0; j < td_pool_ptr->num_tds; j++) {
bytes_to_xfer =
isoc_req->isoc_pkt_descr[index].isoc_pkt_length;
uhci_fill_in_bulk_isoc_td(uhcip, &td_ptr[j],
(uhci_td_t *)NULL, HC_END_OF_LIST, ph, index,
bytes_to_xfer, tw);
td_ptr[j].isoc_pkt_index = (ushort_t)index;
index++;
}
if (i < (isoc_xfer_info->num_pools - 1)) {
td_pool_ptr = &isoc_xfer_info->td_pools[i + 1];
td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
}
}
ddic = ddi_enter_critical();
current_frame = uhci_get_sw_frame_number(uhcip);
if (isoc_req->isoc_attributes & USB_ATTRS_ISOC_START_FRAME) {
start_frame = isoc_req->isoc_frame_no;
end_frame = start_frame + isoc_req->isoc_pkts_count;
if ((end_frame - current_frame) < UHCI_MAX_ISOC_FRAMES) {
if (current_frame > start_frame) {
if ((current_frame + FRNUM_OFFSET) <
end_frame) {
expired_frames = current_frame +
FRNUM_OFFSET - start_frame;
start_frame = current_frame +
FRNUM_OFFSET;
} else {
rval = USB_INVALID_START_FRAME;
}
}
} else {
rval = USB_INVALID_START_FRAME;
}
} else if (isoc_req->isoc_attributes & USB_ATTRS_ISOC_XFER_ASAP) {
start_frame = pp->pp_frame_num;
if (start_frame == INVALID_FRNUM) {
start_frame = current_frame + FRNUM_OFFSET;
} else if (current_frame > start_frame) {
start_frame = current_frame + FRNUM_OFFSET;
}
end_frame = start_frame + isoc_req->isoc_pkts_count;
isoc_req->isoc_frame_no = start_frame;
}
if (rval != USB_SUCCESS) {
ddi_exit_critical(ddic);
USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_insert_isoc_td: Invalid starting frame number");
if (tw->tw_direction == PID_IN) {
uhci_deallocate_periodic_in_resource(uhcip, pp, tw);
}
while (tw->tw_hctd_head) {
uhci_delete_td(uhcip, tw->tw_hctd_head);
}
for (i = 0; i < isoc_xfer_info->num_pools; i++) {
td_pool_ptr = &isoc_xfer_info->td_pools[i];
error = ddi_dma_unbind_handle(td_pool_ptr->dma_handle);
ASSERT(error == DDI_SUCCESS);
ddi_dma_mem_free(&td_pool_ptr->mem_handle);
ddi_dma_free_handle(&td_pool_ptr->dma_handle);
}
kmem_free(isoc_xfer_info->td_pools,
(sizeof (uhci_bulk_isoc_td_pool_t) *
isoc_xfer_info->num_pools));
uhci_deallocate_tw(uhcip, pp, tw);
return (rval);
}
for (i = 0; i < expired_frames; i++) {
isoc_req->isoc_pkt_descr[i].isoc_pkt_status =
USB_CR_NOT_ACCESSED;
isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length =
isoc_req->isoc_pkt_descr[i].isoc_pkt_length;
uhci_get_isoc_td_by_index(uhcip, isoc_xfer_info, i,
&td_ptr, &td_pool_ptr);
uhci_delete_td(uhcip, td_ptr);
--isoc_xfer_info->num_tds;
}
start_frame = (start_frame & 0x3ff);
for (; i < isoc_req->isoc_pkts_count; i++) {
uhci_get_isoc_td_by_index(uhcip, isoc_xfer_info, i,
&td_ptr, &td_pool_ptr);
if (uhcip->uhci_isoc_q_tailp[start_frame]) {
td_ptr->isoc_prev =
uhcip->uhci_isoc_q_tailp[start_frame];
td_ptr->isoc_next = NULL;
td_ptr->link_ptr =
uhcip->uhci_isoc_q_tailp[start_frame]->link_ptr;
uhcip->uhci_isoc_q_tailp[start_frame]->isoc_next =
td_ptr;
SetTD32(uhcip,
uhcip->uhci_isoc_q_tailp[start_frame]->link_ptr,
ISOCTD_PADDR(td_pool_ptr, td_ptr));
uhcip->uhci_isoc_q_tailp[start_frame] = td_ptr;
} else {
uhcip->uhci_isoc_q_tailp[start_frame] = td_ptr;
td_ptr->isoc_next = NULL;
td_ptr->isoc_prev = NULL;
SetTD32(uhcip, td_ptr->link_ptr,
GetFL32(uhcip,
uhcip->uhci_frame_lst_tablep[start_frame]));
SetFL32(uhcip,
uhcip->uhci_frame_lst_tablep[start_frame],
ISOCTD_PADDR(td_pool_ptr, td_ptr));
}
td_ptr->starting_frame = (uint_t)start_frame;
if (++start_frame == NUM_FRAME_LST_ENTRIES)
start_frame = 0;
}
ddi_exit_critical(ddic);
pp->pp_frame_num = end_frame;
USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_insert_isoc_td: current frame number 0x%llx, pipe frame num"
" 0x%llx", (unsigned long long)current_frame,
(unsigned long long)(pp->pp_frame_num));
return (rval);
}
static void
uhci_get_isoc_td_by_index(
uhci_state_t *uhcip,
uhci_bulk_isoc_xfer_t *info,
uint_t index,
uhci_td_t **tdpp,
uhci_bulk_isoc_td_pool_t **td_pool_pp)
{
uint_t i = 0, j = 0;
uhci_td_t *td_ptr;
while (j < info->num_pools) {
if ((i + info->td_pools[j].num_tds) <= index) {
i += info->td_pools[j].num_tds;
j++;
} else {
i = index - i;
break;
}
}
ASSERT(j < info->num_pools);
*td_pool_pp = &info->td_pools[j];
td_ptr = (uhci_td_t *)((*td_pool_pp)->pool_addr);
*tdpp = &td_ptr[i];
}
void
uhci_handle_isoc_td(uhci_state_t *uhcip, uhci_td_t *td)
{
uint_t rval, i;
uint32_t pkt_index = td->isoc_pkt_index;
usb_cr_t cr;
uhci_trans_wrapper_t *tw = td->tw;
usb_isoc_req_t *isoc_req = (usb_isoc_req_t *)tw->tw_isoc_req;
uhci_pipe_private_t *pp = tw->tw_pipe_private;
uhci_bulk_isoc_xfer_t *isoc_xfer_info = &tw->tw_xfer_info;
usba_pipe_handle_data_t *usb_pp;
uhci_bulk_isoc_td_pool_t *td_pool_ptr;
USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_handle_isoc_td: td = 0x%p, pp = 0x%p, tw = 0x%p, req = 0x%p, "
"index = %x", (void *)td, (void *)pp, (void *)tw, (void *)isoc_req,
pkt_index);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
usb_pp = pp->pp_pipe_handle;
cr = USB_CR_OK;
if (GetTD_status(uhcip, td) & TD_STATUS_MASK) {
USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_handle_isoc_td: Error Occurred: TD Status = %x",
GetTD_status(uhcip, td));
isoc_req->isoc_error_count++;
}
if (isoc_req != NULL) {
isoc_req->isoc_pkt_descr[pkt_index].isoc_pkt_status = cr;
isoc_req->isoc_pkt_descr[pkt_index].isoc_pkt_actual_length =
(GetTD_alen(uhcip, td) == ZERO_LENGTH) ? 0 :
GetTD_alen(uhcip, td) + 1;
}
uhci_delete_isoc_td(uhcip, td);
if (--isoc_xfer_info->num_tds != 0) {
USB_DPRINTF_L3(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_handle_isoc_td: Number of TDs %d",
isoc_xfer_info->num_tds);
return;
}
tw->tw_claim = UHCI_INTR_HDLR_CLAIMED;
if (tw->tw_direction == PID_IN) {
uhci_sendup_td_message(uhcip, cr, tw);
if ((uhci_handle_isoc_receive(uhcip, pp, tw)) != USB_SUCCESS) {
USB_DPRINTF_L3(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_handle_isoc_td: Drop message");
}
} else {
uhci_do_byte_stats(uhcip, tw->tw_length,
usb_pp->p_ep.bmAttributes, usb_pp->p_ep.bEndpointAddress);
uhci_hcdi_callback(uhcip, pp, usb_pp, tw, USB_CR_OK);
}
for (i = 0; i < isoc_xfer_info->num_pools; i++) {
td_pool_ptr = &isoc_xfer_info->td_pools[i];
rval = ddi_dma_unbind_handle(td_pool_ptr->dma_handle);
ASSERT(rval == DDI_SUCCESS);
ddi_dma_mem_free(&td_pool_ptr->mem_handle);
ddi_dma_free_handle(&td_pool_ptr->dma_handle);
}
kmem_free(isoc_xfer_info->td_pools,
(sizeof (uhci_bulk_isoc_td_pool_t) *
isoc_xfer_info->num_pools));
uhci_deallocate_tw(uhcip, pp, tw);
}
static int
uhci_handle_isoc_receive(
uhci_state_t *uhcip,
uhci_pipe_private_t *pp,
uhci_trans_wrapper_t *tw)
{
USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_handle_isoc_receive: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if (uhci_start_isoc_receive_polling(uhcip,
pp->pp_pipe_handle, (usb_isoc_req_t *)tw->tw_curr_xfer_reqp,
0) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_handle_isoc_receive: receive polling failed");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static void
uhci_delete_isoc_td(uhci_state_t *uhcip, uhci_td_t *td)
{
uint32_t starting_frame = td->starting_frame;
if ((td->isoc_next == NULL) && (td->isoc_prev == NULL)) {
SetFL32(uhcip, uhcip->uhci_frame_lst_tablep[starting_frame],
GetTD32(uhcip, td->link_ptr));
uhcip->uhci_isoc_q_tailp[starting_frame] = 0;
} else if (td->isoc_next == NULL) {
td->isoc_prev->link_ptr = td->link_ptr;
td->isoc_prev->isoc_next = NULL;
uhcip->uhci_isoc_q_tailp[starting_frame] = td->isoc_prev;
} else if (td->isoc_prev == NULL) {
td->isoc_next->isoc_prev = NULL;
SetFL32(uhcip, uhcip->uhci_frame_lst_tablep[starting_frame],
GetTD32(uhcip, td->link_ptr));
} else {
td->isoc_prev->isoc_next = td->isoc_next;
td->isoc_next->isoc_prev = td->isoc_prev;
td->isoc_prev->link_ptr = td->link_ptr;
}
uhci_delete_td(uhcip, td);
}
int
uhci_start_isoc_receive_polling(
uhci_state_t *uhcip,
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_req,
usb_flags_t usb_flags)
{
int ii, error;
size_t max_isoc_xfer_size, length, isoc_pkts_length;
ushort_t isoc_pkt_count;
uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
usb_isoc_pkt_descr_t *isoc_pkt_descr;
USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_start_isoc_receive_polling: usb_flags = %x", usb_flags);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
max_isoc_xfer_size = ph->p_ep.wMaxPacketSize * UHCI_MAX_ISOC_PKTS;
if (isoc_req) {
isoc_pkt_descr = isoc_req->isoc_pkt_descr;
isoc_pkt_count = isoc_req->isoc_pkts_count;
isoc_pkts_length = isoc_req->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;
}
for (ii = 0, length = 0; ii < isoc_pkt_count; ii++) {
length += isoc_pkt_descr->isoc_pkt_length;
isoc_pkt_descr++;
}
if ((isoc_pkts_length) && (isoc_pkts_length != length)) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_start_isoc_receive_polling: isoc_pkts_length 0x%lx "
"is not equal to the sum of all pkt lengths 0x%lx in "
"an isoc request", isoc_pkts_length, length);
return (USB_FAILURE);
}
if (length > max_isoc_xfer_size) {
USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_start_isoc_receive_polling: "
"Max isoc request size = %lx, Given isoc req size = %lx",
max_isoc_xfer_size, length);
return (USB_FAILURE);
}
error = uhci_insert_isoc_td(uhcip, ph, isoc_req, length, usb_flags);
return (error);
}
void
uhci_remove_isoc_tds_tws(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
{
uint_t rval, i;
uhci_td_t *tmp_td, *td_head;
usb_isoc_req_t *isoc_req;
uhci_trans_wrapper_t *tmp_tw, *tw_head;
uhci_bulk_isoc_xfer_t *isoc_xfer_info;
uhci_bulk_isoc_td_pool_t *td_pool_ptr;
USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
"uhci_remove_isoc_tds_tws: pp = %p", (void *)pp);
tw_head = pp->pp_tw_head;
while (tw_head) {
tmp_tw = tw_head;
tw_head = tw_head->tw_next;
td_head = tmp_tw->tw_hctd_head;
if (tmp_tw->tw_direction == PID_IN) {
uhci_deallocate_periodic_in_resource(uhcip, pp,
tmp_tw);
} else if (tmp_tw->tw_direction == PID_OUT) {
uhci_hcdi_callback(uhcip, pp, pp->pp_pipe_handle,
tmp_tw, USB_CR_FLUSHED);
}
while (td_head) {
tmp_td = td_head;
td_head = td_head->tw_td_next;
uhci_delete_isoc_td(uhcip, tmp_td);
}
isoc_req = (usb_isoc_req_t *)tmp_tw->tw_isoc_req;
if (isoc_req) {
usb_free_isoc_req(isoc_req);
}
ASSERT(tmp_tw->tw_hctd_head == NULL);
if (tmp_tw->tw_xfer_info.td_pools) {
isoc_xfer_info =
(uhci_bulk_isoc_xfer_t *)&tmp_tw->tw_xfer_info;
for (i = 0; i < isoc_xfer_info->num_pools; i++) {
td_pool_ptr = &isoc_xfer_info->td_pools[i];
rval = ddi_dma_unbind_handle(
td_pool_ptr->dma_handle);
ASSERT(rval == DDI_SUCCESS);
ddi_dma_mem_free(&td_pool_ptr->mem_handle);
ddi_dma_free_handle(&td_pool_ptr->dma_handle);
}
kmem_free(isoc_xfer_info->td_pools,
(sizeof (uhci_bulk_isoc_td_pool_t) *
isoc_xfer_info->num_pools));
}
uhci_deallocate_tw(uhcip, pp, tmp_tw);
}
}
void
uhci_isoc_update_sw_frame_number(uhci_state_t *uhcip)
{
(void) uhci_get_sw_frame_number(uhcip);
}
uint64_t
uhci_get_sw_frame_number(uhci_state_t *uhcip)
{
uint64_t sw_frnum, hw_frnum, current_frnum;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
sw_frnum = uhcip->uhci_sw_frnum;
hw_frnum = Get_OpReg16(FRNUM);
if ((sw_frnum & UHCI_BIT_10_MASK) == (hw_frnum & UHCI_BIT_10_MASK)) {
current_frnum = ((sw_frnum & (SW_FRNUM_MASK)) | hw_frnum);
} else {
sw_frnum >>= UHCI_SIZE_OF_HW_FRNUM - 1;
current_frnum =
((++sw_frnum << (UHCI_SIZE_OF_HW_FRNUM - 1)) | hw_frnum);
}
uhcip->uhci_sw_frnum = current_frnum;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_get_sw_frame_number: sw=%lld hd=%lld",
(unsigned long long)(uhcip->uhci_sw_frnum),
(unsigned long long)hw_frnum);
return (current_frnum);
}
void
uhci_cmd_timeout_hdlr(void *arg)
{
uint_t flag = B_FALSE;
uhci_td_t *head, *tmp_td;
uhci_state_t *uhcip = (uhci_state_t *)arg;
uhci_pipe_private_t *pp;
mutex_enter(&uhcip->uhci_int_mutex);
head = uhcip->uhci_outst_tds_head;
while (head) {
if (head->tw->tw_timeout_cnt == 0) {
head = head->outst_td_next;
continue;
}
if (!(head->tw->tw_flags & TW_TIMEOUT_FLAG)) {
head->tw->tw_flags |= TW_TIMEOUT_FLAG;
--head->tw->tw_timeout_cnt;
}
if ((head->tw->tw_timeout_cnt == 0) &&
(head->tw->tw_handle_td != uhci_handle_isoc_td)) {
USB_DPRINTF_L3(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
"Command timed out: td = %p", (void *)head);
head->tw->tw_claim = UHCI_TIMEOUT_HDLR_CLAIMED;
if (GetTD_status(uhcip, head) & UHCI_TD_ACTIVE) {
SetTD32(uhcip, head->link_ptr,
GetTD32(uhcip, head->link_ptr) |
HC_END_OF_LIST);
pp = head->tw->tw_pipe_private;
SetQH32(uhcip, pp->pp_qh->element_ptr,
GetQH32(uhcip, pp->pp_qh->element_ptr) |
HC_END_OF_LIST);
}
flag = B_TRUE;
}
head = head->outst_td_next;
}
if (flag) {
(void) uhci_wait_for_sof(uhcip);
}
head = uhcip->uhci_outst_tds_head;
while (head) {
if (head->tw->tw_flags & TW_TIMEOUT_FLAG) {
head->tw->tw_flags &= ~TW_TIMEOUT_FLAG;
}
if (head->tw->tw_claim == UHCI_TIMEOUT_HDLR_CLAIMED) {
head->tw->tw_claim = UHCI_NOT_CLAIMED;
tmp_td = head->tw->tw_hctd_head;
while (tmp_td) {
SetTD_status(uhcip, tmp_td,
UHCI_TD_CRC_TIMEOUT);
tmp_td = tmp_td->tw_td_next;
}
}
head = head->outst_td_next;
}
if (uhcip->uhci_polled_flag == UHCI_POLLED_FLAG_TRUE) {
uhci_process_submitted_td_queue(uhcip);
uhcip->uhci_polled_flag = UHCI_POLLED_FLAG_FALSE;
} else if (flag) {
uhci_process_submitted_td_queue(uhcip);
}
if (uhcip->uhci_cmd_timeout_id) {
uhcip->uhci_cmd_timeout_id = timeout(uhci_cmd_timeout_hdlr,
(void *)uhcip, UHCI_ONE_SECOND);
}
mutex_exit(&uhcip->uhci_int_mutex);
}
int
uhci_wait_for_sof(uhci_state_t *uhcip)
{
int n, error;
ushort_t cmd_reg;
usb_frame_number_t before_frame_number, after_frame_number;
clock_t rval;
USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
"uhci_wait_for_sof: uhcip = %p", (void *)uhcip);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
error = uhci_state_is_operational(uhcip);
if (error != USB_SUCCESS) {
return (error);
}
before_frame_number = uhci_get_sw_frame_number(uhcip);
for (n = 0; n < MAX_SOF_WAIT_COUNT; n++) {
SetTD_ioc(uhcip, uhcip->uhci_sof_td, 1);
uhcip->uhci_cv_signal = B_TRUE;
rval = cv_reltimedwait(&uhcip->uhci_cv_SOF,
&uhcip->uhci_int_mutex, UHCI_ONE_SECOND, TR_CLOCK_TICK);
after_frame_number = uhci_get_sw_frame_number(uhcip);
if ((rval == -1) &&
(after_frame_number <= before_frame_number)) {
cmd_reg = Get_OpReg16(USBCMD);
Set_OpReg16(USBCMD, (cmd_reg | USBCMD_REG_HC_RUN));
Set_OpReg16(USBINTR, ENABLE_ALL_INTRS);
after_frame_number = uhci_get_sw_frame_number(uhcip);
}
before_frame_number = after_frame_number;
}
SetTD_ioc(uhcip, uhcip->uhci_sof_td, 0);
return (uhcip->uhci_cv_signal ? USB_FAILURE : USB_SUCCESS);
}
int
uhci_allocate_periodic_in_resource(
uhci_state_t *uhcip,
uhci_pipe_private_t *pp,
uhci_trans_wrapper_t *tw,
usb_flags_t flags)
{
size_t length = 0;
usb_opaque_t client_periodic_in_reqp;
usb_intr_req_t *cur_intr_req;
usb_isoc_req_t *curr_isoc_reqp;
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
"uhci_allocate_periodic_in_resource:\n\t"
"ph = 0x%p, pp = 0x%p, tw = 0x%p, flags = 0x%x",
(void *)ph, (void *)pp, (void *)tw, flags);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if (tw->tw_curr_xfer_reqp) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
"uhci_allocate_periodic_in_resource: Interrupt "
"request structure already exists: "
"allocation failed");
return (USB_SUCCESS);
}
client_periodic_in_reqp = pp->pp_client_periodic_in_reqp;
if (UHCI_XFER_TYPE(&ph->p_ep) == USB_EP_ATTR_INTR) {
length = ((usb_intr_req_t *)client_periodic_in_reqp)->
intr_len;
cur_intr_req = usba_hcdi_dup_intr_req(ph->p_dip,
(usb_intr_req_t *)client_periodic_in_reqp, length, flags);
if (cur_intr_req == NULL) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
"uhci_allocate_periodic_in_resource: Interrupt "
"request structure allocation failed");
return (USB_NO_RESOURCES);
}
tw->tw_timeout_cnt = (cur_intr_req->intr_attributes &
USB_ATTRS_ONE_XFER) ? cur_intr_req->intr_timeout: 0;
tw->tw_curr_xfer_reqp = (usb_opaque_t)cur_intr_req;
tw->tw_length = cur_intr_req->intr_len;
} else {
ASSERT(client_periodic_in_reqp != NULL);
if ((curr_isoc_reqp = usba_hcdi_dup_isoc_req(ph->p_dip,
(usb_isoc_req_t *)client_periodic_in_reqp, flags)) ==
NULL) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
"uhci_allocate_periodic_in_resource: Isochronous "
"request structure allocation failed");
return (USB_NO_RESOURCES);
}
tw->tw_curr_xfer_reqp =
(usb_opaque_t)pp->pp_client_periodic_in_reqp;
pp->pp_client_periodic_in_reqp = (usb_opaque_t)curr_isoc_reqp;
}
mutex_enter(&ph->p_mutex);
ph->p_req_count++;
mutex_exit(&ph->p_mutex);
return (USB_SUCCESS);
}
void
uhci_deallocate_periodic_in_resource(
uhci_state_t *uhcip,
uhci_pipe_private_t *pp,
uhci_trans_wrapper_t *tw)
{
usb_opaque_t curr_xfer_reqp;
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
"uhci_deallocate_periodic_in_resource: "
"pp = 0x%p tw = 0x%p", (void *)pp, (void *)tw);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
curr_xfer_reqp = tw->tw_curr_xfer_reqp;
if (curr_xfer_reqp) {
tw->tw_curr_xfer_reqp = NULL;
tw->tw_isoc_req = NULL;
mutex_enter(&ph->p_mutex);
ph->p_req_count--;
mutex_exit(&ph->p_mutex);
switch (UHCI_XFER_TYPE(&ph->p_ep)) {
case USB_EP_ATTR_INTR:
usb_free_intr_req((usb_intr_req_t *)curr_xfer_reqp);
break;
case USB_EP_ATTR_ISOCH:
usb_free_isoc_req((usb_isoc_req_t *)curr_xfer_reqp);
break;
}
}
}
void
uhci_hcdi_callback(uhci_state_t *uhcip, uhci_pipe_private_t *pp,
usba_pipe_handle_data_t *ph, uhci_trans_wrapper_t *tw, usb_cr_t cr)
{
usb_opaque_t curr_xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
"uhci_hcdi_callback: ph = 0x%p, tw = 0x%p, cr = 0x%x",
(void *)ph, (void *)tw, cr);
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
if (tw && tw->tw_curr_xfer_reqp) {
curr_xfer_reqp = tw->tw_curr_xfer_reqp;
tw->tw_curr_xfer_reqp = NULL;
tw->tw_isoc_req = 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(&uhcip->uhci_int_mutex);
usba_hcdi_cb(ph, curr_xfer_reqp, cr);
mutex_enter(&uhcip->uhci_int_mutex);
}
int
uhci_state_is_operational(uhci_state_t *uhcip)
{
int val;
ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
switch (uhcip->uhci_hc_soft_state) {
case UHCI_CTLR_INIT_STATE:
case UHCI_CTLR_SUSPEND_STATE:
val = USB_FAILURE;
break;
case UHCI_CTLR_OPERATIONAL_STATE:
val = USB_SUCCESS;
break;
case UHCI_CTLR_ERROR_STATE:
val = USB_HC_HARDWARE_ERROR;
break;
default:
val = USB_FAILURE;
break;
}
return (val);
}
#ifdef DEBUG
static void
uhci_print_td(uhci_state_t *uhcip, uhci_td_t *td)
{
uint_t *ptr = (uint_t *)td;
#ifndef lint
_NOTE(NO_COMPETING_THREADS_NOW);
#endif
USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
"\tDWORD 1 0x%x\t DWORD 2 0x%x", ptr[0], ptr[1]);
USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
"\tDWORD 3 0x%x\t DWORD 4 0x%x", ptr[2], ptr[3]);
USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
"\tBytes xfered = %d", td->tw->tw_bytes_xfered);
USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
"\tBytes Pending = %d", td->tw->tw_bytes_pending);
USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
"Queue Head Details:");
uhci_print_qh(uhcip, td->tw->tw_pipe_private->pp_qh);
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
}
static void
uhci_print_qh(uhci_state_t *uhcip, queue_head_t *qh)
{
uint_t *ptr = (uint_t *)qh;
USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
"\tLink Ptr = %x Element Ptr = %x", ptr[0], ptr[1]);
}
#endif