#include <sys/usb/hcd/openhci/ohcid.h>
#include <sys/disp.h>
#include <sys/strsun.h>
static void *ohci_statep;
int force_ohci_off = 1;
#define OHCI_INSTS 1
int ohci_ed_pool_size = OHCI_ED_POOL_SIZE;
int ohci_td_pool_size = OHCI_TD_POOL_SIZE;
static uchar_t ohci_index[NUM_INTR_ED_LISTS / 2] = {0x0, 0x8, 0x4, 0xc,
0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd,
0x3, 0xb, 0x7, 0xf};
uint_t ohci_errmask = (uint_t)PRINT_MASK_ALL;
uint_t ohci_errlevel = USB_LOG_L2;
uint_t ohci_instance_debug = (uint_t)-1;
boolean_t ohci_enable_msi = B_TRUE;
static int ohci_hcdi_pipe_open(
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags);
static int ohci_hcdi_pipe_close(
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags);
static int ohci_hcdi_pipe_reset(
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags);
static void ohci_hcdi_pipe_reset_data_toggle(
usba_pipe_handle_data_t *ph);
static int ohci_hcdi_pipe_ctrl_xfer(
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags);
static int ohci_hcdi_bulk_transfer_size(
usba_device_t *usba_device,
size_t *size);
static int ohci_hcdi_pipe_bulk_xfer(
usba_pipe_handle_data_t *ph,
usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags);
static int ohci_hcdi_pipe_intr_xfer(
usba_pipe_handle_data_t *ph,
usb_intr_req_t *intr_req,
usb_flags_t usb_flags);
static int ohci_hcdi_pipe_stop_intr_polling(
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags);
static int ohci_hcdi_get_current_frame_number(
usba_device_t *usba_device,
usb_frame_number_t *frame_number);
static int ohci_hcdi_get_max_isoc_pkts(
usba_device_t *usba_device,
uint_t *max_isoc_pkts_per_request);
static int ohci_hcdi_pipe_isoc_xfer(
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_reqp,
usb_flags_t usb_flags);
static int ohci_hcdi_pipe_stop_isoc_polling(
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags);
static void ohci_set_dma_attributes(ohci_state_t *ohcip);
static int ohci_allocate_pools(ohci_state_t *ohcip);
static void ohci_decode_ddi_dma_addr_bind_handle_result(
ohci_state_t *ohcip,
int result);
static int ohci_map_regs(ohci_state_t *ohcip);
static int ohci_register_intrs_and_init_mutex(
ohci_state_t *ohcip);
static int ohci_add_intrs(ohci_state_t *ohcip,
int intr_type);
static int ohci_init_ctlr(ohci_state_t *ohcip);
static int ohci_init_hcca(ohci_state_t *ohcip);
static void ohci_build_interrupt_lattice(
ohci_state_t *ohcip);
static int ohci_take_control(ohci_state_t *ohcip);
static usba_hcdi_ops_t *ohci_alloc_hcdi_ops(
ohci_state_t *ohcip);
static int ohci_cleanup(ohci_state_t *ohcip);
static void ohci_rem_intrs(ohci_state_t *ohcip);
static int ohci_cpr_suspend(ohci_state_t *ohcip);
static int ohci_cpr_resume(ohci_state_t *ohcip);
static int ohci_allocate_bandwidth(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
uint_t *node);
static void ohci_deallocate_bandwidth(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static int ohci_compute_total_bandwidth(
usb_ep_descr_t *endpoint,
usb_port_status_t port_status,
uint_t *bandwidth);
static int ohci_adjust_polling_interval(
ohci_state_t *ohcip,
usb_ep_descr_t *endpoint,
usb_port_status_t port_status);
static uint_t ohci_lattice_height(uint_t interval);
static uint_t ohci_lattice_parent(uint_t node);
static uint_t ohci_leftmost_leaf(uint_t node,
uint_t height);
static uint_t ohci_hcca_intr_index(
uint_t node);
static uint_t ohci_hcca_leaf_index(
uint_t leaf);
static uint_t ohci_pow_2(uint_t x);
static uint_t ohci_log_2(uint_t x);
static uint_t ohci_unpack_endpoint(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static void ohci_insert_ed(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static void ohci_insert_ctrl_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_insert_bulk_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_insert_intr_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_insert_isoc_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_modify_sKip_bit(ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
skip_bit_t action,
usb_flags_t flag);
static void ohci_remove_ed(ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_remove_ctrl_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_remove_bulk_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_remove_periodic_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_insert_ed_on_reclaim_list(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_detach_ed_from_list(
ohci_state_t *ohcip,
ohci_ed_t *ept,
uint_t ept_type);
static ohci_ed_t *ohci_ed_iommu_to_cpu(
ohci_state_t *ohcip,
uintptr_t addr);
static int ohci_initialize_dummy(ohci_state_t *ohcip,
ohci_ed_t *ept);
static ohci_trans_wrapper_t *ohci_allocate_ctrl_resources(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags);
static void ohci_insert_ctrl_req(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp,
ohci_trans_wrapper_t *tw,
usb_flags_t usb_flags);
static ohci_trans_wrapper_t *ohci_allocate_bulk_resources(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags);
static void ohci_insert_bulk_req(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_bulk_req_t *bulk_reqp,
ohci_trans_wrapper_t *tw,
usb_flags_t flags);
static int ohci_start_pipe_polling(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags);
static void ohci_set_periodic_pipe_polling(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static ohci_trans_wrapper_t *ohci_allocate_intr_resources(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_intr_req_t *intr_reqp,
usb_flags_t usb_flags);
static void ohci_insert_intr_req(ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
usb_flags_t flags);
static int ohci_stop_periodic_pipe_polling(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags);
static ohci_trans_wrapper_t *ohci_allocate_isoc_resources(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_reqp,
usb_flags_t usb_flags);
static int ohci_insert_isoc_req(ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
uint_t flags);
static int ohci_insert_hc_td(ohci_state_t *ohcip,
uint_t hctd_ctrl,
uint32_t hctd_dma_offs,
size_t hctd_length,
uint32_t hctd_ctrl_phase,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw);
static ohci_td_t *ohci_allocate_td_from_pool(
ohci_state_t *ohcip);
static void ohci_fill_in_td(ohci_state_t *ohcip,
ohci_td_t *td,
ohci_td_t *new_dummy,
uint_t hctd_ctrl,
uint32_t hctd_dma_offs,
size_t hctd_length,
uint32_t hctd_ctrl_phase,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw);
static void ohci_init_itd(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
uint_t hctd_ctrl,
uint32_t index,
ohci_td_t *td);
static int ohci_insert_td_with_frame_number(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *current_td,
ohci_td_t *dummy_td);
static void ohci_insert_td_on_tw(ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
ohci_td_t *td);
static void ohci_done_list_tds(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static ohci_trans_wrapper_t *ohci_create_transfer_wrapper(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
size_t length,
uint_t usb_flags);
static ohci_trans_wrapper_t *ohci_create_isoc_transfer_wrapper(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
size_t length,
usb_isoc_pkt_descr_t *descr,
ushort_t pkt_count,
size_t td_count,
uint_t usb_flags);
int ohci_allocate_tds_for_tw(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
size_t td_count);
static ohci_trans_wrapper_t *ohci_allocate_tw_resources(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
size_t length,
usb_flags_t usb_flags,
size_t td_count);
static void ohci_free_tw_tds_resources(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw);
static void ohci_start_xfer_timer(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw);
static void ohci_stop_xfer_timer(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
uint_t flag);
static void ohci_xfer_timeout_handler(void *arg);
static void ohci_remove_tw_from_timeout_list(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw);
static void ohci_start_timer(ohci_state_t *ohcip);
static void ohci_free_dma_resources(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static void ohci_free_tw(ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw);
static int ohci_tw_rebind_cookie(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw);
static uint_t ohci_intr(caddr_t arg1,
caddr_t arg2);
static void ohci_handle_missed_intr(
ohci_state_t *ohcip);
static void ohci_handle_ue(ohci_state_t *ohcip);
static void ohci_handle_endpoint_reclaimation(
ohci_state_t *ohcip);
static void ohci_traverse_done_list(
ohci_state_t *ohcip,
ohci_td_t *head_done_list);
static ohci_td_t *ohci_reverse_done_list(
ohci_state_t *ohcip,
ohci_td_t *head_done_list);
static usb_cr_t ohci_parse_error(ohci_state_t *ohcip,
ohci_td_t *td);
static void ohci_parse_isoc_error(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td);
static usb_cr_t ohci_check_for_error(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
uint_t ctrl);
static void ohci_handle_error(
ohci_state_t *ohcip,
ohci_td_t *td,
usb_cr_t error);
static int ohci_cleanup_data_underrun(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td);
static void ohci_handle_normal_td(
ohci_state_t *ohcip,
ohci_td_t *td,
ohci_trans_wrapper_t *tw);
static void ohci_handle_ctrl_td(ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
void *);
static void ohci_handle_bulk_td(ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
void *);
static void ohci_handle_intr_td(ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
void *);
static void ohci_handle_one_xfer_completion(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw);
static void ohci_handle_isoc_td(ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
void *);
static void ohci_sendup_td_message(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
usb_cr_t error);
static int ohci_check_done_head(
ohci_state_t *ohcip,
ohci_td_t *done_head);
static void ohci_cpr_cleanup(
ohci_state_t *ohcip);
static usb_req_attrs_t ohci_get_xfer_attrs(ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw);
static int ohci_allocate_periodic_in_resource(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
usb_flags_t flags);
static int ohci_wait_for_sof(
ohci_state_t *ohcip);
static void ohci_pipe_cleanup(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static void ohci_wait_for_transfers_completion(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_check_for_transfers_completion(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp);
static void ohci_save_data_toggle(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static void ohci_restore_data_toggle(ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph);
static void ohci_deallocate_periodic_in_resource(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw);
static void ohci_do_client_periodic_in_req_callback(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
usb_cr_t completion_reason);
static void ohci_hcdi_callback(
usba_pipe_handle_data_t *ph,
ohci_trans_wrapper_t *tw,
usb_cr_t completion_reason);
static void ohci_create_stats(ohci_state_t *ohcip);
static void ohci_destroy_stats(ohci_state_t *ohcip);
static void ohci_do_byte_stats(
ohci_state_t *ohcip,
size_t len,
uint8_t attr,
uint8_t addr);
static void ohci_do_intrs_stats(
ohci_state_t *ohcip,
int val);
static void ohci_print_op_regs(ohci_state_t *ohcip);
static void ohci_print_ed(ohci_state_t *ohcip,
ohci_ed_t *ed);
static void ohci_print_td(ohci_state_t *ohcip,
ohci_td_t *td);
int usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level);
static int ohci_open(dev_t *devp, int flags, int otyp, cred_t *credp);
static int ohci_close(dev_t dev, int flag, int otyp, cred_t *credp);
static int ohci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp);
static int ohci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int ohci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int ohci_quiesce(dev_info_t *dip);
static int ohci_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
void *arg, void **result);
static struct cb_ops ohci_cb_ops = {
ohci_open,
ohci_close,
nodev,
nodev,
nodev,
nodev,
nodev,
ohci_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_MP
};
static struct dev_ops ohci_ops = {
DEVO_REV,
0,
ohci_info,
nulldev,
nulldev,
ohci_attach,
ohci_detach,
nodev,
&ohci_cb_ops,
&usba_hubdi_busops,
usba_hubdi_root_hub_power,
ohci_quiesce,
};
static struct modldrv modldrv = {
&mod_driverops,
"USB OpenHCI Driver",
&ohci_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&ohci_statep, sizeof (ohci_state_t),
OHCI_INSTS)) != 0) {
return (error);
}
if ((error = mod_install(&modlinkage)) != 0) {
ddi_soft_state_fini(&ohci_statep);
}
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) == 0) {
ddi_soft_state_fini(&ohci_statep);
}
return (error);
}
static int
ohci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
ohci_state_t *ohcip = NULL;
usba_hcdi_register_args_t hcdi_args;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
ohcip = ohci_obtain_state(dip);
return (ohci_cpr_resume(ohcip));
default:
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
if (ddi_soft_state_zalloc(ohci_statep, instance) != 0) {
return (DDI_FAILURE);
}
ohcip = ddi_get_soft_state(ohci_statep, instance);
if (ohcip == NULL) {
return (DDI_FAILURE);
}
ohcip->ohci_flags = OHCI_ATTACH;
ohcip->ohci_log_hdl = usb_alloc_log_hdl(dip, "ohci", &ohci_errlevel,
&ohci_errmask, &ohci_instance_debug, 0);
ohcip->ohci_flags |= OHCI_ZALLOC;
ohcip->ohci_hc_soft_state = OHCI_CTLR_INIT_STATE;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohcip = 0x%p", (void *)ohcip);
ohci_set_dma_attributes(ohcip);
ohcip->ohci_dip = dip;
ohcip->ohci_instance = instance;
ohci_create_stats(ohcip);
if (ohci_allocate_pools(ohcip) != DDI_SUCCESS) {
(void) ohci_cleanup(ohcip);
return (DDI_FAILURE);
}
if (ohci_map_regs(ohcip) != DDI_SUCCESS) {
(void) ohci_cleanup(ohcip);
return (DDI_FAILURE);
}
ohcip->ohci_vendor_id = pci_config_get16(
ohcip->ohci_config_handle, PCI_CONF_VENID);
ohcip->ohci_device_id = pci_config_get16(
ohcip->ohci_config_handle, PCI_CONF_DEVID);
ohcip->ohci_rev_id = pci_config_get8(
ohcip->ohci_config_handle, PCI_CONF_REVID);
if (ohci_register_intrs_and_init_mutex(ohcip) != DDI_SUCCESS) {
(void) ohci_cleanup(ohcip);
return (DDI_FAILURE);
}
mutex_enter(&ohcip->ohci_int_mutex);
if (ohci_init_ctlr(ohcip) != DDI_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
(void) ohci_cleanup(ohcip);
return (DDI_FAILURE);
}
ohcip->ohci_hcdi_ops = ohci_alloc_hcdi_ops(ohcip);
mutex_exit(&ohcip->ohci_int_mutex);
hcdi_args.usba_hcdi_register_version = HCDI_REGISTER_VERSION;
hcdi_args.usba_hcdi_register_dip = dip;
hcdi_args.usba_hcdi_register_ops = ohcip->ohci_hcdi_ops;
hcdi_args.usba_hcdi_register_dma_attr = &ohcip->ohci_dma_attr;
hcdi_args.usba_hcdi_register_iblock_cookie =
(ddi_iblock_cookie_t)(uintptr_t)ohcip->ohci_intr_pri;
if (usba_hcdi_register(&hcdi_args, 0) != DDI_SUCCESS) {
(void) ohci_cleanup(ohcip);
return (DDI_FAILURE);
}
ohcip->ohci_flags |= OHCI_USBAREG;
mutex_enter(&ohcip->ohci_int_mutex);
if ((ohci_init_root_hub(ohcip)) != USB_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
(void) ohci_cleanup(ohcip);
return (DDI_FAILURE);
}
mutex_exit(&ohcip->ohci_int_mutex);
if (ohci_load_root_hub_driver(ohcip) != USB_SUCCESS) {
(void) ohci_cleanup(ohcip);
return (DDI_FAILURE);
}
ohcip->ohci_flags |= OHCI_RHREG;
ddi_report_dev(dip);
mutex_enter(&ohcip->ohci_int_mutex);
ohcip->ohci_flags &= ~OHCI_ATTACH;
ohci_print_op_regs(ohcip);
if (OHCI_IS_RIO(ohcip)) {
(void) pci_report_pmcap(dip, PCI_PM_IDLESPEED, (void *)4000);
}
mutex_exit(&ohcip->ohci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_attach: dip = 0x%p done", (void *)dip);
return (DDI_SUCCESS);
}
int
ohci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
ohci_state_t *ohcip = ohci_obtain_state(dip);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_detach:");
switch (cmd) {
case DDI_DETACH:
return (ohci_cleanup(ohcip));
case DDI_SUSPEND:
return (ohci_cpr_suspend(ohcip));
default:
return (DDI_FAILURE);
}
}
static int
ohci_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
dev_t dev;
ohci_state_t *ohcip;
int instance;
int error = DDI_FAILURE;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
dev = (dev_t)arg;
instance = OHCI_UNIT(dev);
ohcip = ddi_get_soft_state(ohci_statep, instance);
if (ohcip != NULL) {
*result = (void *)ohcip->ohci_dip;
if (*result != NULL) {
error = DDI_SUCCESS;
}
} else {
*result = NULL;
}
break;
case DDI_INFO_DEVT2INSTANCE:
dev = (dev_t)arg;
instance = OHCI_UNIT(dev);
*result = (void *)(uintptr_t)instance;
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
static dev_info_t *
ohci_get_dip(dev_t dev)
{
int instance = OHCI_UNIT(dev);
ohci_state_t *ohcip = ddi_get_soft_state(ohci_statep, instance);
if (ohcip) {
return (ohcip->ohci_dip);
} else {
return (NULL);
}
}
static int
ohci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
dev_info_t *dip = ohci_get_dip(*devp);
return (usba_hubdi_open(dip, devp, flags, otyp, credp));
}
static int
ohci_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
dev_info_t *dip = ohci_get_dip(dev);
return (usba_hubdi_close(dip, dev, flag, otyp, credp));
}
static int
ohci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
dev_info_t *dip = ohci_get_dip(dev);
return (usba_hubdi_ioctl(dip,
dev, cmd, arg, mode, credp, rvalp));
}
static void
ohci_set_dma_attributes(ohci_state_t *ohcip)
{
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_set_dma_attributes:");
ohcip->ohci_dma_attr.dma_attr_version = DMA_ATTR_V0;
ohcip->ohci_dma_attr.dma_attr_addr_lo = 0x00000000ull;
ohcip->ohci_dma_attr.dma_attr_addr_hi = 0xfffffffeull;
ohcip->ohci_dma_attr.dma_attr_count_max = OHCI_DMA_ATTR_COUNT_MAX;
ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_ALIGNMENT;
ohcip->ohci_dma_attr.dma_attr_burstsizes = 0x1;
ohcip->ohci_dma_attr.dma_attr_minxfer = 0x1;
ohcip->ohci_dma_attr.dma_attr_maxxfer = OHCI_DMA_ATTR_MAX_XFER;
ohcip->ohci_dma_attr.dma_attr_seg = 0xffffffffull;
ohcip->ohci_dma_attr.dma_attr_sgllen = 1;
ohcip->ohci_dma_attr.dma_attr_granular = OHCI_DMA_ATTR_GRANULAR;
ohcip->ohci_dma_attr.dma_attr_flags = 0;
}
static int
ohci_allocate_pools(ohci_state_t *ohcip)
{
ddi_device_acc_attr_t dev_attr;
size_t real_length;
int result;
uint_t ccount;
int i;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_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;
ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_TD_ALIGNMENT;
if (ddi_dma_alloc_handle(ohcip->ohci_dip, &ohcip->ohci_dma_attr,
DDI_DMA_SLEEP, 0,
&ohcip->ohci_td_pool_dma_handle) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (ddi_dma_mem_alloc(ohcip->ohci_td_pool_dma_handle,
ohci_td_pool_size * sizeof (ohci_td_t),
&dev_attr,
DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
0,
(caddr_t *)&ohcip->ohci_td_pool_addr,
&real_length,
&ohcip->ohci_td_pool_mem_handle)) {
return (DDI_FAILURE);
}
result = ddi_dma_addr_bind_handle(
ohcip->ohci_td_pool_dma_handle,
NULL,
(caddr_t)ohcip->ohci_td_pool_addr,
real_length,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
NULL,
&ohcip->ohci_td_pool_cookie,
&ccount);
bzero((void *)ohcip->ohci_td_pool_addr,
ohci_td_pool_size * sizeof (ohci_td_t));
if (result == DDI_DMA_MAPPED) {
if (ccount != 1) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_allocate_pools: More than 1 cookie");
return (DDI_FAILURE);
}
} else {
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_allocate_pools: Result = %d", result);
ohci_decode_ddi_dma_addr_bind_handle_result(ohcip, result);
return (DDI_FAILURE);
}
ohcip->ohci_dma_addr_bind_flag |= OHCI_TD_POOL_BOUND;
for (i = 0; i < ohci_td_pool_size; i ++) {
Set_TD(ohcip->ohci_td_pool_addr[i].hctd_state, HC_TD_FREE);
}
ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_ED_ALIGNMENT;
if (ddi_dma_alloc_handle(ohcip->ohci_dip,
&ohcip->ohci_dma_attr,
DDI_DMA_SLEEP,
0,
&ohcip->ohci_ed_pool_dma_handle) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (ddi_dma_mem_alloc(ohcip->ohci_ed_pool_dma_handle,
ohci_ed_pool_size * sizeof (ohci_ed_t),
&dev_attr,
DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
0,
(caddr_t *)&ohcip->ohci_ed_pool_addr,
&real_length,
&ohcip->ohci_ed_pool_mem_handle) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
result = ddi_dma_addr_bind_handle(ohcip->ohci_ed_pool_dma_handle,
NULL,
(caddr_t)ohcip->ohci_ed_pool_addr,
real_length,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
NULL,
&ohcip->ohci_ed_pool_cookie,
&ccount);
bzero((void *)ohcip->ohci_ed_pool_addr,
ohci_ed_pool_size * sizeof (ohci_ed_t));
if (result == DDI_DMA_MAPPED) {
if (ccount != 1) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_allocate_pools: More than 1 cookie");
return (DDI_FAILURE);
}
} else {
ohci_decode_ddi_dma_addr_bind_handle_result(ohcip, result);
return (DDI_FAILURE);
}
ohcip->ohci_dma_addr_bind_flag |= OHCI_ED_POOL_BOUND;
for (i = 0; i < ohci_ed_pool_size; i ++) {
Set_ED(ohcip->ohci_ed_pool_addr[i].hced_state, HC_EPT_FREE);
}
return (DDI_SUCCESS);
}
static void
ohci_decode_ddi_dma_addr_bind_handle_result(
ohci_state_t *ohcip,
int result)
{
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_decode_ddi_dma_addr_bind_handle_result:");
switch (result) {
case DDI_DMA_PARTIAL_MAP:
USB_DPRINTF_L2(PRINT_MASK_ALL, ohcip->ohci_log_hdl,
"Partial transfers not allowed");
break;
case DDI_DMA_INUSE:
USB_DPRINTF_L2(PRINT_MASK_ALL, ohcip->ohci_log_hdl,
"Handle is in use");
break;
case DDI_DMA_NORESOURCES:
USB_DPRINTF_L2(PRINT_MASK_ALL, ohcip->ohci_log_hdl,
"No resources");
break;
case DDI_DMA_NOMAPPING:
USB_DPRINTF_L2(PRINT_MASK_ALL, ohcip->ohci_log_hdl,
"No mapping");
break;
case DDI_DMA_TOOBIG:
USB_DPRINTF_L2(PRINT_MASK_ALL, ohcip->ohci_log_hdl,
"Object is too big");
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ALL, ohcip->ohci_log_hdl,
"Unknown dma error");
}
}
static int
ohci_map_regs(ohci_state_t *ohcip)
{
ddi_device_acc_attr_t attr;
uint16_t cmd_reg;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_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_regs_map_setup(ohcip->ohci_dip, 1,
(caddr_t *)&ohcip->ohci_regsp, 0,
sizeof (ohci_regs_t), &attr,
&ohcip->ohci_regs_handle) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_map_regs: Map setup error");
return (DDI_FAILURE);
}
if (pci_config_setup(ohcip->ohci_dip,
&ohcip->ohci_config_handle) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_map_regs: Config error");
return (DDI_FAILURE);
}
cmd_reg = pci_config_get16(ohcip->ohci_config_handle, PCI_CONF_COMM);
if (!(cmd_reg & PCI_COMM_MAE)) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_map_regs: Memory base address access disabled");
return (DDI_FAILURE);
}
cmd_reg |= (PCI_COMM_MAE | PCI_COMM_ME);
pci_config_put16(ohcip->ohci_config_handle, PCI_CONF_COMM, cmd_reg);
return (DDI_SUCCESS);
}
static int
ohci_is_polled(dev_info_t *dip)
{
int ret;
char *propval;
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0,
"usb-polling", &propval) != DDI_SUCCESS)
return (0);
ret = (strcmp(propval, "true") == 0);
ddi_prop_free(propval);
return (ret);
}
static void
ohci_poll_intr(void *arg)
{
for (;;) {
(void) ohci_intr(arg, NULL);
delay(drv_usectohz(1000));
}
}
static int
ohci_register_intrs_and_init_mutex(ohci_state_t *ohcip)
{
int intr_types;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_register_intrs_and_init_mutex:");
if ((ohcip->ohci_vendor_id == PCI_ULI1575_VENID) &&
(ohcip->ohci_device_id == PCI_ULI1575_DEVID)) {
ohcip->ohci_msi_enabled = B_FALSE;
} else {
ohcip->ohci_msi_enabled = ohci_enable_msi;
}
if (ohci_is_polled(ohcip->ohci_dip)) {
extern pri_t maxclsyspri;
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_register_intrs_and_init_mutex: "
"running in simulated polled mode");
(void) thread_create(NULL, 0, ohci_poll_intr, ohcip, 0, &p0,
TS_RUN, maxclsyspri);
goto skip_intr;
}
if (ddi_intr_get_supported_types(ohcip->ohci_dip,
&intr_types) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_register_intrs_and_init_mutex: "
"ddi_intr_get_supported_types failed");
return (DDI_FAILURE);
}
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_register_intrs_and_init_mutex: "
"supported interrupt types 0x%x", intr_types);
if ((intr_types & DDI_INTR_TYPE_MSI) && ohcip->ohci_msi_enabled) {
if (ohci_add_intrs(ohcip, DDI_INTR_TYPE_MSI)
!= DDI_SUCCESS) {
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_register_intrs_and_init_mutex: MSI "
"registration failed, trying FIXED interrupt \n");
} else {
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_register_intrs_and_init_mutex: "
"Using MSI interrupt type\n");
ohcip->ohci_intr_type = DDI_INTR_TYPE_MSI;
ohcip->ohci_flags |= OHCI_INTR;
}
}
if ((!(ohcip->ohci_flags & OHCI_INTR)) &&
(intr_types & DDI_INTR_TYPE_FIXED)) {
if (ohci_add_intrs(ohcip, DDI_INTR_TYPE_FIXED)
!= DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_register_intrs_and_init_mutex: "
"FIXED interrupt registration failed\n");
return (DDI_FAILURE);
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_register_intrs_and_init_mutex: "
"Using FIXED interrupt type\n");
ohcip->ohci_intr_type = DDI_INTR_TYPE_FIXED;
ohcip->ohci_flags |= OHCI_INTR;
}
skip_intr:
cv_init(&ohcip->ohci_SOF_cv, NULL, CV_DRIVER, NULL);
sema_init(&ohcip->ohci_ocsem, 1, NULL, SEMA_DRIVER, NULL);
return (DDI_SUCCESS);
}
static int
ohci_add_intrs(ohci_state_t *ohcip, int intr_type)
{
int actual, avail, intr_size, count = 0;
int i, flag, ret;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: interrupt type 0x%x", intr_type);
ret = ddi_intr_get_nintrs(ohcip->ohci_dip, intr_type, &count);
if ((ret != DDI_SUCCESS) || (count == 0)) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: ddi_intr_get_nintrs() failure, "
"ret: %d, count: %d", ret, count);
return (DDI_FAILURE);
}
ret = ddi_intr_get_navail(ohcip->ohci_dip, intr_type, &avail);
if ((ret != DDI_SUCCESS) || (avail == 0)) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: ddi_intr_get_navail() failure, "
"ret: %d, count: %d", ret, count);
return (DDI_FAILURE);
}
if (avail < count) {
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: ohci_add_intrs: nintrs () "
"returned %d, navail returned %d\n", count, avail);
}
intr_size = count * sizeof (ddi_intr_handle_t);
ohcip->ohci_htable = kmem_zalloc(intr_size, KM_SLEEP);
flag = (intr_type == DDI_INTR_TYPE_MSI) ?
DDI_INTR_ALLOC_STRICT:DDI_INTR_ALLOC_NORMAL;
ret = ddi_intr_alloc(ohcip->ohci_dip, ohcip->ohci_htable,
intr_type, 0, count, &actual, flag);
if ((ret != DDI_SUCCESS) || (actual == 0)) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: ddi_intr_alloc() failed %d", ret);
kmem_free(ohcip->ohci_htable, intr_size);
return (DDI_FAILURE);
}
if (actual < count) {
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: Requested: %d, Received: %d\n",
count, actual);
for (i = 0; i < actual; i++)
(void) ddi_intr_free(ohcip->ohci_htable[i]);
kmem_free(ohcip->ohci_htable, intr_size);
return (DDI_FAILURE);
}
ohcip->ohci_intr_cnt = actual;
if ((ret = ddi_intr_get_pri(ohcip->ohci_htable[0],
&ohcip->ohci_intr_pri)) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: ddi_intr_get_pri() failed %d", ret);
for (i = 0; i < actual; i++)
(void) ddi_intr_free(ohcip->ohci_htable[i]);
kmem_free(ohcip->ohci_htable, intr_size);
return (DDI_FAILURE);
}
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: Supported Interrupt priority 0x%x",
ohcip->ohci_intr_pri);
if (ohcip->ohci_intr_pri >= ddi_intr_get_hilevel_pri()) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: Hi level interrupt not supported");
for (i = 0; i < actual; i++)
(void) ddi_intr_free(ohcip->ohci_htable[i]);
kmem_free(ohcip->ohci_htable, intr_size);
return (DDI_FAILURE);
}
mutex_init(&ohcip->ohci_int_mutex, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(ohcip->ohci_intr_pri));
for (i = 0; i < actual; i++) {
if ((ret = ddi_intr_add_handler(ohcip->ohci_htable[i],
ohci_intr, (caddr_t)ohcip,
(caddr_t)(uintptr_t)i)) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: ddi_intr_add_handler() "
"failed %d", ret);
for (i = 0; i < actual; i++)
(void) ddi_intr_free(ohcip->ohci_htable[i]);
mutex_destroy(&ohcip->ohci_int_mutex);
kmem_free(ohcip->ohci_htable, intr_size);
return (DDI_FAILURE);
}
}
if ((ret = ddi_intr_get_cap(ohcip->ohci_htable[0],
&ohcip->ohci_intr_cap)) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_add_intrs: ddi_intr_get_cap() failed %d", ret);
for (i = 0; i < actual; i++) {
(void) ddi_intr_remove_handler(ohcip->ohci_htable[i]);
(void) ddi_intr_free(ohcip->ohci_htable[i]);
}
mutex_destroy(&ohcip->ohci_int_mutex);
kmem_free(ohcip->ohci_htable, intr_size);
return (DDI_FAILURE);
}
if (ohcip->ohci_intr_cap & DDI_INTR_FLAG_BLOCK) {
(void) ddi_intr_block_enable(ohcip->ohci_htable,
ohcip->ohci_intr_cnt);
} else {
for (i = 0; i < ohcip->ohci_intr_cnt; i++)
(void) ddi_intr_enable(ohcip->ohci_htable[i]);
}
return (DDI_SUCCESS);
}
static int
ohci_init_ctlr(ohci_state_t *ohcip)
{
int revision, curr_control, max_packet = 0;
clock_t sof_time_wait;
int retry = 0;
int ohci_frame_interval;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_init_ctlr:");
if (ohci_take_control(ohcip) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_ctlr: ohci_take_control failed\n");
return (DDI_FAILURE);
}
Set_OpReg(hcr_cmd_status, HCR_STATUS_RESET);
mutex_exit(&ohcip->ohci_int_mutex);
delay(drv_usectohz(OHCI_RESET_TIMEWAIT));
mutex_enter(&ohcip->ohci_int_mutex);
Set_OpReg(hcr_control, HCR_CONTROL_RESET);
revision = Get_OpReg(hcr_revision);
if ((revision & HCR_REVISION_MASK) != HCR_REVISION_1_0) {
return (DDI_FAILURE);
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_ctlr: Revision verified");
if (ohcip->ohci_hc_soft_state == OHCI_CTLR_INIT_STATE) {
if (ohci_init_hcca(ohcip) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
if (ohcip->ohci_vendor_id == PCI_ULI1575_VENID &&
ohcip->ohci_device_id == PCI_ULI1575_DEVID) {
Set_OpReg(hcr_control, HCR_CONTROL_DEFAULT);
Set_OpReg(hcr_intr_enable, HCR_INT_ENABLE_DEFAULT);
Set_OpReg(hcr_HCCA, HCR_HCCA_DEFAULT);
Set_OpReg(hcr_ctrl_head, HCR_CONTROL_HEAD_ED_DEFAULT);
Set_OpReg(hcr_bulk_head, HCR_BULK_HEAD_ED_DEFAULT);
Set_OpReg(hcr_frame_interval, HCR_FRAME_INTERVAL_DEFAULT);
Set_OpReg(hcr_periodic_strt, HCR_PERIODIC_START_DEFAULT);
}
Set_OpReg(hcr_HCCA, (uint_t)ohcip->ohci_hcca_cookie.dmac_address);
Set_OpReg(hcr_intr_enable, HCR_INTR_SO | HCR_INTR_WDH |
HCR_INTR_RD | HCR_INTR_UE | HCR_INTR_FNO | HCR_INTR_MIE);
Set_OpReg(hcr_periodic_strt,
((PERIODIC_XFER_STARTS * BITS_PER_BYTE) - 1));
ohcip->ohci_frame_interval = Get_OpReg(hcr_frame_interval);
max_packet = ((((ohcip->ohci_frame_interval -
MAX_OVERHEAD) * 6) / 7) << HCR_FRME_FSMPS_SHFT);
Set_OpReg(hcr_frame_interval,
(max_packet | ohcip->ohci_frame_interval));
if (ohcip->ohci_vendor_id == PCI_ULI1575_VENID &&
ohcip->ohci_device_id == PCI_ULI1575_DEVID) {
ohci_frame_interval = Get_OpReg(hcr_frame_interval);
while ((ohci_frame_interval != (max_packet |
ohcip->ohci_frame_interval))) {
if (retry >= 10) {
USB_DPRINTF_L1(PRINT_MASK_ATTA,
ohcip->ohci_log_hdl, "Failed to program"
" Frame Interval Register.");
return (DDI_FAILURE);
}
retry++;
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_ctlr: Failed to program Frame"
" Interval Register, retry=%d", retry);
Set_OpReg(hcr_frame_interval,
(max_packet | ohcip->ohci_frame_interval));
ohci_frame_interval = Get_OpReg(hcr_frame_interval);
}
}
curr_control = Get_OpReg(hcr_control);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_ctlr: curr_control=0x%x", curr_control);
curr_control = (curr_control &
(~HCR_CONTROL_HCFS)) | HCR_CONTROL_OPERAT;
Set_OpReg(hcr_control, curr_control);
ASSERT((Get_OpReg(hcr_control) &
HCR_CONTROL_HCFS) == HCR_CONTROL_OPERAT);
ohcip->ohci_hc_soft_state = OHCI_CTLR_OPERATIONAL_STATE;
sof_time_wait = drv_usectohz(OHCI_MAX_SOF_TIMEWAIT * 1000000);
ohcip->ohci_sof_flag = B_FALSE;
Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
ASSERT(Get_OpReg(hcr_intr_enable) & HCR_INTR_SOF);
(void) cv_reltimedwait(&ohcip->ohci_SOF_cv,
&ohcip->ohci_int_mutex, sof_time_wait, TR_CLOCK_TICK);
if (ohcip->ohci_sof_flag == B_FALSE) {
ohcip->ohci_hc_soft_state = OHCI_CTLR_ERROR_STATE;
USB_DPRINTF_L0(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"No SOF interrupts have been received, this USB OHCI host"
"controller is unusable");
return (DDI_FAILURE);
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_ctlr: SOF's have started");
return (DDI_SUCCESS);
}
static int
ohci_init_hcca(ohci_state_t *ohcip)
{
ddi_device_acc_attr_t dev_attr;
size_t real_length;
uint_t mask, ccount;
int result;
uintptr_t addr;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_init_hcca:");
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;
ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_HCCA_ALIGNMENT;
if (ddi_dma_alloc_handle(ohcip->ohci_dip, &ohcip->ohci_dma_attr,
DDI_DMA_SLEEP,
0,
&ohcip->ohci_hcca_dma_handle)
!= DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (ddi_dma_mem_alloc(ohcip->ohci_hcca_dma_handle,
2 * sizeof (ohci_hcca_t),
&dev_attr,
DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
0,
(caddr_t *)&ohcip->ohci_hccap,
&real_length,
&ohcip->ohci_hcca_mem_handle)) {
return (DDI_FAILURE);
}
bzero((void *)ohcip->ohci_hccap, real_length);
Set_OpReg(hcr_HCCA, 0xFFFFFFFF);
mask = Get_OpReg(hcr_HCCA);
mutex_exit(&ohcip->ohci_int_mutex);
while (mask == 0) {
delay(drv_usectohz(OHCI_TIMEWAIT));
mask = Get_OpReg(hcr_HCCA);
}
mutex_enter(&ohcip->ohci_int_mutex);
ASSERT(mask != 0);
addr = (uintptr_t)ohcip->ohci_hccap;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_hcca: addr=0x%lx, mask=0x%x", addr, mask);
while (addr & (~mask)) {
addr++;
}
ohcip->ohci_hccap = (ohci_hcca_t *)addr;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_hcca: Real length %lu", real_length);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_hcca: virtual hcca 0x%p", (void *)ohcip->ohci_hccap);
result = ddi_dma_addr_bind_handle(ohcip->ohci_hcca_dma_handle,
NULL,
(caddr_t)ohcip->ohci_hccap,
real_length,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP, NULL,
&ohcip->ohci_hcca_cookie,
&ccount);
if (result == DDI_DMA_MAPPED) {
if (ccount != 1) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_hcca: More than 1 cookie");
return (DDI_FAILURE);
}
} else {
ohci_decode_ddi_dma_addr_bind_handle_result(ohcip, result);
return (DDI_FAILURE);
}
ohcip->ohci_dma_addr_bind_flag |= OHCI_HCCA_DMA_BOUND;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_hcca: physical 0x%p",
(void *)(uintptr_t)ohcip->ohci_hcca_cookie.dmac_address);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_hcca: size %lu", ohcip->ohci_hcca_cookie.dmac_size);
ohci_build_interrupt_lattice(ohcip);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_init_hcca: End");
return (DDI_SUCCESS);
}
static void
ohci_build_interrupt_lattice(ohci_state_t *ohcip)
{
ohci_ed_t *list_array = ohcip->ohci_ed_pool_addr;
int half_list = NUM_INTR_ED_LISTS / 2;
ohci_hcca_t *hccap = ohcip->ohci_hccap;
uintptr_t addr;
int i;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_build_interrupt_lattice:");
for (i = 0; i < NUM_STATIC_NODES; i++) {
Set_ED(list_array[i].hced_ctrl, HC_EPT_sKip);
Set_ED(list_array[i].hced_state, HC_EPT_STATIC);
}
for (i = 0; i < half_list - 1; i++) {
addr = ohci_ed_cpu_to_iommu(ohcip, (ohci_ed_t *)&list_array[i]);
Set_ED(list_array[2*i + 1].hced_next, addr);
Set_ED(list_array[2*i + 2].hced_next, addr);
}
for (i = 0; i < half_list; i++) {
addr = ohci_ed_cpu_to_iommu(ohcip,
(ohci_ed_t *)&list_array[half_list - 1 + ohci_index[i]]);
ASSERT(Get_ED(list_array[half_list - 1 +
ohci_index[i]].hced_ctrl));
ASSERT(addr != 0);
Set_HCCA(hccap->HccaIntTble[i], addr);
Set_HCCA(hccap->HccaIntTble[i + half_list], addr);
}
}
static int
ohci_take_control(ohci_state_t *ohcip)
{
#if defined(__x86)
uint32_t hcr_control_val;
uint32_t hcr_cmd_status_val;
int wait;
#endif
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_take_control:");
#if defined(__x86)
hcr_control_val = Get_OpReg(hcr_control);
if ((hcr_control_val & HCR_CONTROL_IR) == 0) {
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_take_control: InterruptRouting off\n");
return (DDI_SUCCESS);
}
hcr_cmd_status_val = Get_OpReg(hcr_cmd_status);
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_take_control: hcr_cmd_status: 0x%x\n",
hcr_cmd_status_val);
hcr_cmd_status_val |= HCR_STATUS_OCR;
Set_OpReg(hcr_cmd_status, hcr_cmd_status_val);
mutex_exit(&ohcip->ohci_int_mutex);
for (wait = 0; wait < 5000; wait++) {
if ((Get_OpReg(hcr_control) & HCR_CONTROL_IR) == 0)
break;
delay(drv_usectohz(1000));
}
mutex_enter(&ohcip->ohci_int_mutex);
if (wait >= 5000) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_take_control: couldn't take control from BIOS\n");
return (DDI_FAILURE);
}
#else
if (Get_OpReg(hcr_control) & HCR_CONTROL_IR) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_take_control: Routing bit set");
return (DDI_FAILURE);
}
#endif
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_take_control: End");
return (DDI_SUCCESS);
}
int
ohci_hcdi_pm_support(dev_info_t *dip)
{
return (USB_SUCCESS);
}
static usba_hcdi_ops_t *
ohci_alloc_hcdi_ops(ohci_state_t *ohcip)
{
usba_hcdi_ops_t *usba_hcdi_ops;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_alloc_hcdi_ops:");
usba_hcdi_ops = usba_alloc_hcdi_ops();
usba_hcdi_ops->usba_hcdi_ops_version = HCDI_OPS_VERSION;
usba_hcdi_ops->usba_hcdi_pm_support = ohci_hcdi_pm_support;
usba_hcdi_ops->usba_hcdi_pipe_open = ohci_hcdi_pipe_open;
usba_hcdi_ops->usba_hcdi_pipe_close = ohci_hcdi_pipe_close;
usba_hcdi_ops->usba_hcdi_pipe_reset = ohci_hcdi_pipe_reset;
usba_hcdi_ops->usba_hcdi_pipe_reset_data_toggle =
ohci_hcdi_pipe_reset_data_toggle;
usba_hcdi_ops->usba_hcdi_pipe_ctrl_xfer = ohci_hcdi_pipe_ctrl_xfer;
usba_hcdi_ops->usba_hcdi_pipe_bulk_xfer = ohci_hcdi_pipe_bulk_xfer;
usba_hcdi_ops->usba_hcdi_pipe_intr_xfer = ohci_hcdi_pipe_intr_xfer;
usba_hcdi_ops->usba_hcdi_pipe_isoc_xfer = ohci_hcdi_pipe_isoc_xfer;
usba_hcdi_ops->usba_hcdi_bulk_transfer_size =
ohci_hcdi_bulk_transfer_size;
usba_hcdi_ops->usba_hcdi_pipe_stop_intr_polling =
ohci_hcdi_pipe_stop_intr_polling;
usba_hcdi_ops->usba_hcdi_pipe_stop_isoc_polling =
ohci_hcdi_pipe_stop_isoc_polling;
usba_hcdi_ops->usba_hcdi_get_current_frame_number =
ohci_hcdi_get_current_frame_number;
usba_hcdi_ops->usba_hcdi_get_max_isoc_pkts =
ohci_hcdi_get_max_isoc_pkts;
usba_hcdi_ops->usba_hcdi_console_input_init =
ohci_hcdi_polled_input_init;
usba_hcdi_ops->usba_hcdi_console_input_enter =
ohci_hcdi_polled_input_enter;
usba_hcdi_ops->usba_hcdi_console_read = ohci_hcdi_polled_read;
usba_hcdi_ops->usba_hcdi_console_input_exit =
ohci_hcdi_polled_input_exit;
usba_hcdi_ops->usba_hcdi_console_input_fini =
ohci_hcdi_polled_input_fini;
usba_hcdi_ops->usba_hcdi_console_output_init =
ohci_hcdi_polled_output_init;
usba_hcdi_ops->usba_hcdi_console_output_enter =
ohci_hcdi_polled_output_enter;
usba_hcdi_ops->usba_hcdi_console_write = ohci_hcdi_polled_write;
usba_hcdi_ops->usba_hcdi_console_output_exit =
ohci_hcdi_polled_output_exit;
usba_hcdi_ops->usba_hcdi_console_output_fini =
ohci_hcdi_polled_output_fini;
return (usba_hcdi_ops);
}
static int
ohci_cleanup(ohci_state_t *ohcip)
{
ohci_trans_wrapper_t *tw;
ohci_pipe_private_t *pp;
ohci_td_t *td;
int i, state, rval;
int flags = ohcip->ohci_flags;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_cleanup:");
if (flags & OHCI_RHREG) {
if (ohci_unload_root_hub_driver(ohcip) != USB_SUCCESS) {
return (DDI_FAILURE);
}
}
if (flags & OHCI_USBAREG) {
usba_hcdi_unregister(ohcip->ohci_dip);
}
if (flags & OHCI_INTR) {
mutex_enter(&ohcip->ohci_int_mutex);
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
HCR_CONTROL_BLE | HCR_CONTROL_PLE | HCR_CONTROL_IE)));
Set_OpReg(hcr_intr_disable,
(HCR_INTR_SO | HCR_INTR_WDH | HCR_INTR_RD | HCR_INTR_UE));
(void) ohci_wait_for_sof(ohcip);
Set_OpReg(hcr_intr_disable, (HCR_INTR_MIE | HCR_INTR_SOF));
Set_OpReg(hcr_control, ((Get_OpReg(hcr_control) &
(~HCR_CONTROL_HCFS)) | HCR_CONTROL_RESET));
mutex_exit(&ohcip->ohci_int_mutex);
delay(drv_usectohz(OHCI_TIMEWAIT));
mutex_enter(&ohcip->ohci_int_mutex);
if (ohcip->ohci_vendor_id == PCI_ULI1575_VENID &&
ohcip->ohci_device_id == PCI_ULI1575_DEVID) {
Set_OpReg(hcr_control, HCR_CONTROL_DEFAULT);
Set_OpReg(hcr_intr_enable, HCR_INT_ENABLE_DEFAULT);
Set_OpReg(hcr_HCCA, HCR_HCCA_DEFAULT);
Set_OpReg(hcr_ctrl_head, HCR_CONTROL_HEAD_ED_DEFAULT);
Set_OpReg(hcr_bulk_head, HCR_BULK_HEAD_ED_DEFAULT);
Set_OpReg(hcr_frame_interval,
HCR_FRAME_INTERVAL_DEFAULT);
Set_OpReg(hcr_periodic_strt,
HCR_PERIODIC_START_DEFAULT);
}
mutex_exit(&ohcip->ohci_int_mutex);
ohci_rem_intrs(ohcip);
}
if (ohcip->ohci_regs_handle) {
Set_OpReg(hcr_cmd_status, HCR_STATUS_RESET);
ddi_regs_map_free(&ohcip->ohci_regs_handle);
}
if (ohcip->ohci_config_handle) {
pci_config_teardown(&ohcip->ohci_config_handle);
}
if (ohcip->ohci_td_pool_addr && ohcip->ohci_td_pool_mem_handle) {
for (i = 0; i < ohci_td_pool_size; i ++) {
td = &ohcip->ohci_td_pool_addr[i];
state = Get_TD(ohcip->ohci_td_pool_addr[i].hctd_state);
if ((state != HC_TD_FREE) && (state != HC_TD_DUMMY) &&
(td->hctd_trans_wrapper)) {
mutex_enter(&ohcip->ohci_int_mutex);
tw = (ohci_trans_wrapper_t *)
OHCI_LOOKUP_ID((uint32_t)
Get_TD(td->hctd_trans_wrapper));
pp = tw->tw_pipe_private;
ohci_stop_xfer_timer(ohcip, tw,
OHCI_REMOVE_XFER_ALWAYS);
ohci_deallocate_tw_resources(ohcip, pp, tw);
mutex_exit(&ohcip->ohci_int_mutex);
}
}
if ((ohcip->ohci_dma_addr_bind_flag &
OHCI_TD_POOL_BOUND) == OHCI_TD_POOL_BOUND) {
rval = ddi_dma_unbind_handle(
ohcip->ohci_td_pool_dma_handle);
ASSERT(rval == DDI_SUCCESS);
}
ddi_dma_mem_free(&ohcip->ohci_td_pool_mem_handle);
}
if (ohcip->ohci_td_pool_dma_handle) {
ddi_dma_free_handle(&ohcip->ohci_td_pool_dma_handle);
}
if (ohcip->ohci_ed_pool_addr && ohcip->ohci_ed_pool_mem_handle) {
if ((ohcip->ohci_dma_addr_bind_flag &
OHCI_ED_POOL_BOUND) == OHCI_ED_POOL_BOUND) {
rval = ddi_dma_unbind_handle(
ohcip->ohci_ed_pool_dma_handle);
ASSERT(rval == DDI_SUCCESS);
}
ddi_dma_mem_free(&ohcip->ohci_ed_pool_mem_handle);
}
if (ohcip->ohci_ed_pool_dma_handle) {
ddi_dma_free_handle(&ohcip->ohci_ed_pool_dma_handle);
}
if (ohcip->ohci_hccap && ohcip->ohci_hcca_mem_handle) {
if ((ohcip->ohci_dma_addr_bind_flag &
OHCI_HCCA_DMA_BOUND) == OHCI_HCCA_DMA_BOUND) {
rval = ddi_dma_unbind_handle(
ohcip->ohci_hcca_dma_handle);
ASSERT(rval == DDI_SUCCESS);
}
ddi_dma_mem_free(&ohcip->ohci_hcca_mem_handle);
}
if (ohcip->ohci_hcca_dma_handle) {
ddi_dma_free_handle(&ohcip->ohci_hcca_dma_handle);
}
if (flags & OHCI_INTR) {
mutex_destroy(&ohcip->ohci_int_mutex);
cv_destroy(&ohcip->ohci_SOF_cv);
sema_destroy(&ohcip->ohci_ocsem);
}
ohci_destroy_stats(ohcip);
if (ohcip->ohci_hcdi_ops) {
usba_free_hcdi_ops(ohcip->ohci_hcdi_ops);
}
if (flags & OHCI_ZALLOC) {
usb_free_log_hdl(ohcip->ohci_log_hdl);
ddi_prop_remove_all(ohcip->ohci_dip);
ddi_soft_state_free(ohci_statep,
ddi_get_instance(ohcip->ohci_dip));
}
return (DDI_SUCCESS);
}
static void
ohci_rem_intrs(ohci_state_t *ohcip)
{
int i;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_rem_intrs: interrupt type 0x%x", ohcip->ohci_intr_type);
if (ohcip->ohci_intr_cap & DDI_INTR_FLAG_BLOCK) {
(void) ddi_intr_block_disable(ohcip->ohci_htable,
ohcip->ohci_intr_cnt);
} else {
for (i = 0; i < ohcip->ohci_intr_cnt; i++) {
(void) ddi_intr_disable(ohcip->ohci_htable[i]);
}
}
for (i = 0; i < ohcip->ohci_intr_cnt; i++) {
(void) ddi_intr_remove_handler(ohcip->ohci_htable[i]);
(void) ddi_intr_free(ohcip->ohci_htable[i]);
}
kmem_free(ohcip->ohci_htable,
ohcip->ohci_intr_cnt * sizeof (ddi_intr_handle_t));
}
static int
ohci_cpr_suspend(ohci_state_t *ohcip)
{
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_suspend:");
if (usba_hubdi_detach(ohcip->ohci_dip, DDI_SUSPEND) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
mutex_enter(&ohcip->ohci_int_mutex);
if (ohcip->ohci_open_pipe_count > 1) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_suspend: fails as open pipe count = %d",
ohcip->ohci_open_pipe_count);
mutex_exit(&ohcip->ohci_int_mutex);
return (DDI_FAILURE);
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_suspend: Disable HC ED list processing");
Set_OpReg(hcr_control, (Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
HCR_CONTROL_BLE | HCR_CONTROL_PLE | HCR_CONTROL_IE)));
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_suspend: Disable HC interrupts");
Set_OpReg(hcr_intr_disable, ~(HCR_INTR_MIE|HCR_INTR_SOF));
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_suspend: Wait for the next SOF");
if (ohci_wait_for_sof(ohcip) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_suspend: ohci host controller suspend failed");
mutex_exit(&ohcip->ohci_int_mutex);
return (DDI_FAILURE);
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_suspend: Disable Master interrupt");
Set_OpReg(hcr_intr_disable, HCR_INTR_MIE);
if (ohcip->ohci_polled_kbd_count == 0 || force_ohci_off != 0) {
Set_OpReg(hcr_control, HCR_CONTROL_SUSPD);
}
ohcip->ohci_hc_soft_state = OHCI_CTLR_SUSPEND_STATE;
mutex_exit(&ohcip->ohci_int_mutex);
return (DDI_SUCCESS);
}
static int
ohci_cpr_resume(ohci_state_t *ohcip)
{
mutex_enter(&ohcip->ohci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_resume: Restart the controller");
ohci_cpr_cleanup(ohcip);
if (ohci_init_ctlr(ohcip) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"ohci_cpr_resume: ohci host controller resume failed ");
mutex_exit(&ohcip->ohci_int_mutex);
return (DDI_FAILURE);
}
mutex_exit(&ohcip->ohci_int_mutex);
if (usba_hubdi_attach(ohcip->ohci_dip, DDI_RESUME) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
ohci_hcdi_pipe_open(
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
usb_ep_descr_t *epdt = &ph->p_ep;
int rval, error = USB_SUCCESS;
int kmflag = (flags & USB_FLAGS_SLEEP) ?
KM_SLEEP : KM_NOSLEEP;
uint_t node = 0;
ohci_pipe_private_t *pp;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_open: addr = 0x%x, ep%d",
ph->p_usba_device->usb_addr,
epdt->bEndpointAddress & USB_EP_NUM_MASK);
sema_p(&ohcip->ohci_ocsem);
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
mutex_exit(&ohcip->ohci_int_mutex);
if (rval != USB_SUCCESS) {
sema_v(&ohcip->ohci_ocsem);
return (rval);
}
if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
mutex_enter(&ohcip->ohci_int_mutex);
error = ohci_handle_root_hub_pipe_open(ph, flags);
mutex_exit(&ohcip->ohci_int_mutex);
sema_v(&ohcip->ohci_ocsem);
return (error);
}
if (ph->p_hcd_private) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_open: Pipe is already opened");
sema_v(&ohcip->ohci_ocsem);
return (USB_FAILURE);
}
if (OHCI_PERIODIC_ENDPOINT(epdt)) {
mutex_enter(&ohcip->ohci_int_mutex);
mutex_enter(&ph->p_mutex);
error = ohci_allocate_bandwidth(ohcip, ph, &node);
if (error != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_open: Bandwidth allocation failed");
mutex_exit(&ph->p_mutex);
mutex_exit(&ohcip->ohci_int_mutex);
sema_v(&ohcip->ohci_ocsem);
return (error);
}
mutex_exit(&ph->p_mutex);
mutex_exit(&ohcip->ohci_int_mutex);
}
pp = kmem_zalloc(sizeof (ohci_pipe_private_t), kmflag);
if (pp == NULL) {
mutex_enter(&ohcip->ohci_int_mutex);
if (OHCI_PERIODIC_ENDPOINT(epdt)) {
mutex_enter(&ph->p_mutex);
ohci_deallocate_bandwidth(ohcip, ph);
mutex_exit(&ph->p_mutex);
}
mutex_exit(&ohcip->ohci_int_mutex);
sema_v(&ohcip->ohci_ocsem);
return (USB_NO_RESOURCES);
}
mutex_enter(&ohcip->ohci_int_mutex);
pp->pp_node = node;
cv_init(&pp->pp_xfer_cmpl_cv, NULL, CV_DRIVER, NULL);
pp->pp_state = OHCI_PIPE_STATE_IDLE;
pp->pp_pipe_handle = ph;
mutex_enter(&ph->p_mutex);
ph->p_hcd_private = (usb_opaque_t)pp;
bcopy(&ph->p_policy, &pp->pp_policy, sizeof (usb_pipe_policy_t));
mutex_exit(&ph->p_mutex);
pp->pp_ept = ohci_alloc_hc_ed(ohcip, ph);
if (pp->pp_ept == NULL) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_open: ED allocation failed");
mutex_enter(&ph->p_mutex);
if (OHCI_PERIODIC_ENDPOINT(epdt)) {
ohci_deallocate_bandwidth(ohcip, ph);
}
cv_destroy(&pp->pp_xfer_cmpl_cv);
kmem_free(ph->p_hcd_private, sizeof (ohci_pipe_private_t));
ph->p_hcd_private = NULL;
mutex_exit(&ph->p_mutex);
mutex_exit(&ohcip->ohci_int_mutex);
sema_v(&ohcip->ohci_ocsem);
return (USB_NO_RESOURCES);
}
ohci_restore_data_toggle(ohcip, ph);
ohci_insert_ed(ohcip, ph);
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_open: ph = 0x%p", (void *)ph);
ohcip->ohci_open_pipe_count++;
mutex_exit(&ohcip->ohci_int_mutex);
sema_v(&ohcip->ohci_ocsem);
return (USB_SUCCESS);
}
static int
ohci_hcdi_pipe_close(
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_close: addr = 0x%x, ep%d",
ph->p_usba_device->usb_addr,
eptd->bEndpointAddress & USB_EP_NUM_MASK);
sema_p(&ohcip->ohci_ocsem);
if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
mutex_enter(&ohcip->ohci_int_mutex);
error = ohci_handle_root_hub_pipe_close(ph);
mutex_exit(&ohcip->ohci_int_mutex);
sema_v(&ohcip->ohci_ocsem);
return (error);
}
ASSERT(ph->p_hcd_private != NULL);
mutex_enter(&ohcip->ohci_int_mutex);
pp->pp_state = OHCI_PIPE_STATE_CLOSE;
ohci_pipe_cleanup(ohcip, ph);
ohci_remove_ed(ohcip, pp);
if (OHCI_PERIODIC_ENDPOINT(eptd)) {
mutex_enter(&ph->p_mutex);
ohci_deallocate_bandwidth(ohcip, ph);
mutex_exit(&ph->p_mutex);
}
mutex_enter(&ph->p_mutex);
cv_destroy(&pp->pp_xfer_cmpl_cv);
kmem_free(ph->p_hcd_private, sizeof (ohci_pipe_private_t));
ph->p_hcd_private = NULL;
mutex_exit(&ph->p_mutex);
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_close: ph = 0x%p", (void *)ph);
ohcip->ohci_open_pipe_count--;
mutex_exit(&ohcip->ohci_int_mutex);
sema_v(&ohcip->ohci_ocsem);
return (error);
}
static int
ohci_hcdi_pipe_reset(
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_reset: ph = 0x%p ", (void *)ph);
if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
error = ohci_handle_root_hub_pipe_reset(ph, usb_flags);
return (error);
}
mutex_enter(&ohcip->ohci_int_mutex);
pp->pp_state = OHCI_PIPE_STATE_RESET;
ohci_pipe_cleanup(ohcip, ph);
mutex_exit(&ohcip->ohci_int_mutex);
return (error);
}
void
ohci_hcdi_pipe_reset_data_toggle(
usba_pipe_handle_data_t *ph)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_reset_data_toggle:");
mutex_enter(&ohcip->ohci_int_mutex);
mutex_enter(&ph->p_mutex);
usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
DATA0);
mutex_exit(&ph->p_mutex);
Set_ED(pp->pp_ept->hced_headp,
Get_ED(pp->pp_ept->hced_headp) & (~HC_EPT_Carry));
mutex_exit(&ohcip->ohci_int_mutex);
}
static int
ohci_hcdi_pipe_ctrl_xfer(
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
int rval;
int error = USB_SUCCESS;
ohci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_ctrl_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
(void *)ph, (void *)ctrl_reqp, usb_flags);
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
mutex_exit(&ohcip->ohci_int_mutex);
if (rval != USB_SUCCESS) {
return (rval);
}
if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
error = ohci_handle_root_hub_request(ohcip, ph, ctrl_reqp);
return (error);
}
mutex_enter(&ohcip->ohci_int_mutex);
if (pp->pp_state == OHCI_PIPE_STATE_ERROR) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_ctrl_xfer:"
"Pipe is in error state, need pipe reset to continue");
mutex_exit(&ohcip->ohci_int_mutex);
return (USB_FAILURE);
}
if ((tw = ohci_allocate_ctrl_resources(ohcip, pp, ctrl_reqp,
usb_flags)) == NULL) {
error = USB_NO_RESOURCES;
} else {
ohci_insert_ctrl_req(ohcip, ph, ctrl_reqp, tw, usb_flags);
}
mutex_exit(&ohcip->ohci_int_mutex);
return (error);
}
static int
ohci_hcdi_bulk_transfer_size(
usba_device_t *usba_device,
size_t *size)
{
ohci_state_t *ohcip = ohci_obtain_state(
usba_device->usb_root_hub_dip);
int rval;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_bulk_transfer_size:");
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
mutex_exit(&ohcip->ohci_int_mutex);
if (rval != USB_SUCCESS) {
return (rval);
}
*size = OHCI_MAX_BULK_XFER_SIZE;
return (USB_SUCCESS);
}
static int
ohci_hcdi_pipe_bulk_xfer(
usba_pipe_handle_data_t *ph,
usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
int rval, error = USB_SUCCESS;
ohci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_bulk_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
(void *)ph, (void *)bulk_reqp, usb_flags);
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
return (rval);
}
if (pp->pp_state == OHCI_PIPE_STATE_ERROR) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_bulk_xfer:"
"Pipe is in error state, need pipe reset to continue");
mutex_exit(&ohcip->ohci_int_mutex);
return (USB_FAILURE);
}
if ((tw = ohci_allocate_bulk_resources(ohcip, pp, bulk_reqp,
usb_flags)) == NULL) {
error = USB_NO_RESOURCES;
} else {
ohci_insert_bulk_req(ohcip, ph, bulk_reqp, tw, usb_flags);
}
mutex_exit(&ohcip->ohci_int_mutex);
return (error);
}
static int
ohci_hcdi_pipe_intr_xfer(
usba_pipe_handle_data_t *ph,
usb_intr_req_t *intr_reqp,
usb_flags_t usb_flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
int pipe_dir, rval, error = USB_SUCCESS;
ohci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_intr_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
(void *)ph, (void *)intr_reqp, usb_flags);
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
return (rval);
}
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
if (pipe_dir == USB_EP_DIR_IN) {
error = ohci_start_periodic_pipe_polling(ohcip, ph,
(usb_opaque_t)intr_reqp, usb_flags);
} else {
if ((tw = ohci_allocate_intr_resources(ohcip, ph,
intr_reqp, usb_flags)) == NULL) {
error = USB_NO_RESOURCES;
} else {
ohci_insert_intr_req(ohcip,
(ohci_pipe_private_t *)ph->p_hcd_private,
tw, usb_flags);
}
}
mutex_exit(&ohcip->ohci_int_mutex);
return (error);
}
static int
ohci_hcdi_pipe_stop_intr_polling(
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_stop_intr_polling: ph = 0x%p fl = 0x%x",
(void *)ph, flags);
mutex_enter(&ohcip->ohci_int_mutex);
error = ohci_stop_periodic_pipe_polling(ohcip, ph, flags);
mutex_exit(&ohcip->ohci_int_mutex);
return (error);
}
static int
ohci_hcdi_get_current_frame_number(
usba_device_t *usba_device,
usb_frame_number_t *frame_number)
{
ohci_state_t *ohcip = ohci_obtain_state(
usba_device->usb_root_hub_dip);
int rval;
ohcip = ohci_obtain_state(usba_device->usb_root_hub_dip);
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
return (rval);
}
*frame_number = ohci_get_current_frame_number(ohcip);
mutex_exit(&ohcip->ohci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_get_current_frame_number:"
"Current frame number 0x%llx", (unsigned long long)(*frame_number));
return (rval);
}
static int
ohci_hcdi_get_max_isoc_pkts(
usba_device_t *usba_device,
uint_t *max_isoc_pkts_per_request)
{
ohci_state_t *ohcip = ohci_obtain_state(
usba_device->usb_root_hub_dip);
int rval;
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
mutex_exit(&ohcip->ohci_int_mutex);
if (rval != USB_SUCCESS) {
return (rval);
}
*max_isoc_pkts_per_request = OHCI_MAX_ISOC_PKTS_PER_XFER;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_get_max_isoc_pkts: maximum isochronous"
"packets per usb isochronous request = 0x%x",
*max_isoc_pkts_per_request);
return (rval);
}
static int
ohci_hcdi_pipe_isoc_xfer(
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_reqp,
usb_flags_t usb_flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
int error = USB_SUCCESS;
int pipe_dir, rval;
ohci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_isoc_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
(void *)ph, (void *)isoc_reqp, usb_flags);
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
return (rval);
}
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_isoc_xfer: isoc_reqp = 0x%p, uf = 0x%x",
(void *)isoc_reqp, usb_flags);
if (pipe_dir == USB_EP_DIR_IN) {
error = ohci_start_periodic_pipe_polling(ohcip, ph,
(usb_opaque_t)isoc_reqp, usb_flags);
} else {
if ((tw = ohci_allocate_isoc_resources(ohcip, ph,
isoc_reqp, usb_flags)) == NULL) {
error = USB_NO_RESOURCES;
} else {
error = ohci_insert_isoc_req(ohcip,
(ohci_pipe_private_t *)ph->p_hcd_private,
tw, usb_flags);
}
}
mutex_exit(&ohcip->ohci_int_mutex);
return (error);
}
static int
ohci_hcdi_pipe_stop_isoc_polling(
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
int rval, error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
"ohci_hcdi_pipe_stop_isoc_polling: ph = 0x%p fl = 0x%x",
(void *)ph, flags);
mutex_enter(&ohcip->ohci_int_mutex);
rval = ohci_state_is_operational(ohcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ohcip->ohci_int_mutex);
return (rval);
}
error = ohci_stop_periodic_pipe_polling(ohcip, ph, flags);
mutex_exit(&ohcip->ohci_int_mutex);
return (error);
}
static int
ohci_allocate_bandwidth(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
uint_t *node)
{
int interval, error, i;
uint_t min, min_index, height;
uint_t leftmost, list, bandwidth;
usb_ep_descr_t *endpoint = &ph->p_ep;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
mutex_enter(&ph->p_usba_device->usb_mutex);
error = ohci_compute_total_bandwidth(
endpoint, ph->p_usba_device->usb_port_status, &bandwidth);
mutex_exit(&ph->p_usba_device->usb_mutex);
if (error != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_BW, ohcip->ohci_log_hdl,
"ohci_allocate_bandwidth: Periodic endpoint with "
"zero endpoint maximum packet size is not supported");
return (USB_NOT_SUPPORTED);
}
if ((ohcip->ohci_periodic_minimum_bandwidth + bandwidth) >
(MAX_PERIODIC_BANDWIDTH)) {
USB_DPRINTF_L2(PRINT_MASK_BW, ohcip->ohci_log_hdl,
"ohci_allocate_bandwidth: Reached maximum "
"bandwidth value and cannot allocate bandwidth "
"for a given periodic endpoint");
return (USB_NO_BANDWIDTH);
}
mutex_enter(&ph->p_usba_device->usb_mutex);
interval = ohci_adjust_polling_interval(ohcip,
endpoint, ph->p_usba_device->usb_port_status);
mutex_exit(&ph->p_usba_device->usb_mutex);
if (interval == USB_FAILURE) {
return (USB_FAILURE);
}
USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
"The new interval is %d", interval);
min_index = 0;
min = ohcip->ohci_periodic_bandwidth[0];
for (i = 1; i < NUM_INTR_ED_LISTS; i++) {
if (ohcip->ohci_periodic_bandwidth[i] < min) {
min_index = i;
min = ohcip->ohci_periodic_bandwidth[i];
}
}
USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
"The leaf %d for minimal bandwidth %d", min_index, min);
min_index = min_index + NUM_INTR_ED_LISTS - 1;
height = ohci_lattice_height(interval);
USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
"The height is %d", height);
*node = min_index;
for (i = 0; i < height; i++) {
*node = ohci_lattice_parent(*node);
}
USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
"Real node is %d", *node);
leftmost = ohci_leftmost_leaf(*node, height);
USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
"Leftmost %d", leftmost);
for (i = 0; i < (NUM_INTR_ED_LISTS/interval); i++) {
list = ohci_hcca_leaf_index(leftmost + i);
if ((ohcip->ohci_periodic_bandwidth[list] +
bandwidth) > MAX_PERIODIC_BANDWIDTH) {
USB_DPRINTF_L2(PRINT_MASK_BW, ohcip->ohci_log_hdl,
"ohci_allocate_bandwidth: Reached maximum "
"bandwidth value and cannot allocate bandwidth "
"for periodic endpoint");
return (USB_NO_BANDWIDTH);
}
}
for (i = 0; i < (NUM_INTR_ED_LISTS/interval); i++) {
list = ohci_hcca_leaf_index(leftmost + i);
ohcip->ohci_periodic_bandwidth[list] += bandwidth;
}
min_index = 0;
min = ohcip->ohci_periodic_bandwidth[0];
for (i = 1; i < NUM_INTR_ED_LISTS; i++) {
if (ohcip->ohci_periodic_bandwidth[i] < min) {
min_index = i;
min = ohcip->ohci_periodic_bandwidth[i];
}
}
ohcip->ohci_periodic_minimum_bandwidth = min;
return (USB_SUCCESS);
}
static void
ohci_deallocate_bandwidth(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
uint_t min, node, bandwidth;
uint_t height, leftmost, list;
int i, interval;
usb_ep_descr_t *endpoint = &ph->p_ep;
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
mutex_enter(&ph->p_usba_device->usb_mutex);
(void) ohci_compute_total_bandwidth(
endpoint, ph->p_usba_device->usb_port_status, &bandwidth);
mutex_exit(&ph->p_usba_device->usb_mutex);
node = pp->pp_node;
mutex_enter(&ph->p_usba_device->usb_mutex);
interval = ohci_adjust_polling_interval(ohcip,
endpoint, ph->p_usba_device->usb_port_status);
mutex_exit(&ph->p_usba_device->usb_mutex);
height = ohci_lattice_height(interval);
leftmost = ohci_leftmost_leaf(node, height);
for (i = 0; i < (NUM_INTR_ED_LISTS/interval); i++) {
list = ohci_hcca_leaf_index(leftmost + i);
ohcip->ohci_periodic_bandwidth[list] -= bandwidth;
}
min = ohcip->ohci_periodic_bandwidth[0];
for (i = 1; i < NUM_INTR_ED_LISTS; i++) {
if (ohcip->ohci_periodic_bandwidth[i] < min) {
min = ohcip->ohci_periodic_bandwidth[i];
}
}
ohcip->ohci_periodic_minimum_bandwidth = min;
}
static int
ohci_compute_total_bandwidth(
usb_ep_descr_t *endpoint,
usb_port_status_t port_status,
uint_t *bandwidth)
{
ushort_t maxpacketsize = endpoint->wMaxPacketSize;
if (maxpacketsize == 0) {
return (USB_NOT_SUPPORTED);
}
*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 ((endpoint->bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) {
*bandwidth += FS_NON_ISOC_PROTO_OVERHEAD;
} else {
if ((endpoint->bEndpointAddress &
USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
*bandwidth += FS_ISOC_INPUT_PROTO_OVERHEAD;
} else {
*bandwidth += FS_ISOC_OUTPUT_PROTO_OVERHEAD;
}
}
}
return (USB_SUCCESS);
}
static int
ohci_adjust_polling_interval(
ohci_state_t *ohcip,
usb_ep_descr_t *endpoint,
usb_port_status_t port_status)
{
uint_t interval;
int i = 0;
interval = endpoint->bInterval;
if ((interval < MIN_POLL_INTERVAL) ||
(interval > MAX_POLL_INTERVAL)) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_adjust_polling_interval: "
"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_BW, ohcip->ohci_log_hdl,
"ohci_adjust_polling_interval: "
"Low speed endpoint's poll interval of %d ms "
"is below threshold. Rounding up to %d ms",
interval, MIN_LOW_SPEED_POLL_INTERVAL);
interval = MIN_LOW_SPEED_POLL_INTERVAL;
}
if (interval > NUM_INTR_ED_LISTS) {
interval = NUM_INTR_ED_LISTS;
}
while ((ohci_pow_2(i)) <= interval) {
i++;
}
return (ohci_pow_2((i - 1)));
}
static uint_t
ohci_lattice_height(uint_t interval)
{
return (TREE_HEIGHT - (ohci_log_2(interval)));
}
static uint_t
ohci_lattice_parent(uint_t node)
{
if ((node % 2) == 0) {
return ((node/2) - 1);
} else {
return ((node + 1)/2 - 1);
}
}
static uint_t
ohci_leftmost_leaf(
uint_t node,
uint_t height)
{
return ((ohci_pow_2(height) * (node + 1)) - NUM_INTR_ED_LISTS);
}
static uint_t
ohci_hcca_intr_index(uint_t node)
{
node = node - NUM_STATIC_NODES;
if ((node % 2) == 0) {
return (ohci_index[node / 2]);
} else {
return (ohci_index[node / 2] + (NUM_INTR_ED_LISTS / 2));
}
}
static uint_t
ohci_hcca_leaf_index(uint_t leaf)
{
if ((leaf % 2) == 0) {
return (ohci_index[leaf / 2]);
} else {
return (ohci_index[leaf / 2] + (NUM_INTR_ED_LISTS / 2));
}
}
static uint_t
ohci_pow_2(uint_t x)
{
if (x == 0) {
return (1);
} else {
return (2 << (x - 1));
}
}
static uint_t
ohci_log_2(uint_t x)
{
int i = 0;
while (x != 1) {
x = x >> 1;
i++;
}
return (i);
}
ohci_ed_t *
ohci_alloc_hc_ed(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
int i, state;
ohci_ed_t *hc_ed;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_alloc_hc_ed: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
for (i = NUM_STATIC_NODES; i < ohci_ed_pool_size; i ++) {
state = Get_ED(ohcip->ohci_ed_pool_addr[i].hced_state);
if (state == HC_EPT_FREE) {
break;
}
}
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_alloc_hc_ed: Allocated %d", i);
if (i == ohci_ed_pool_size) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_alloc_hc_ed: ED exhausted");
return (NULL);
} else {
hc_ed = &ohcip->ohci_ed_pool_addr[i];
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_alloc_hc_ed: Allocated address 0x%p", (void *)hc_ed);
ohci_print_ed(ohcip, hc_ed);
if (ph) {
if ((ohci_initialize_dummy(ohcip,
hc_ed)) == USB_NO_RESOURCES) {
bzero((void *)hc_ed, sizeof (ohci_ed_t));
Set_ED(hc_ed->hced_state, HC_EPT_FREE);
return (NULL);
}
Set_ED(hc_ed->hced_prev, 0);
Set_ED(hc_ed->hced_next, 0);
Set_ED(hc_ed->hced_state, HC_EPT_ACTIVE);
Set_ED(hc_ed->hced_ctrl,
ohci_unpack_endpoint(ohcip, ph));
} else {
Set_ED(hc_ed->hced_ctrl, HC_EPT_sKip);
Set_ED(hc_ed->hced_state, HC_EPT_STATIC);
}
return (hc_ed);
}
}
static uint_t
ohci_unpack_endpoint(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
usb_ep_descr_t *endpoint = &ph->p_ep;
uint_t maxpacketsize, addr, ctrl = 0;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_unpack_endpoint:");
ctrl = ph->p_usba_device->usb_addr;
addr = endpoint->bEndpointAddress;
ctrl = ctrl | ((addr & USB_EP_NUM_MASK) << HC_EPT_EP_SHFT);
if ((endpoint->bmAttributes &
USB_EP_ATTR_MASK) != USB_EP_ATTR_CONTROL) {
if (addr & USB_EP_DIR_MASK) {
ctrl = ctrl | HC_EPT_DF_IN;
} else {
ctrl = ctrl | HC_EPT_DF_OUT;
}
}
mutex_enter(&ph->p_usba_device->usb_mutex);
if (ph->p_usba_device->usb_port_status == USBA_LOW_SPEED_DEV) {
ctrl = ctrl | HC_EPT_Speed;
}
mutex_exit(&ph->p_usba_device->usb_mutex);
if ((endpoint->bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH) {
ctrl = ctrl | HC_EPT_Format;
}
maxpacketsize = endpoint->wMaxPacketSize;
maxpacketsize = maxpacketsize << HC_EPT_MAXPKTSZ;
ctrl = ctrl | (maxpacketsize & HC_EPT_MPS);
return (ctrl);
}
static void
ohci_insert_ed(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_ed:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
switch (ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
ohci_insert_ctrl_ed(ohcip, pp);
break;
case USB_EP_ATTR_BULK:
ohci_insert_bulk_ed(ohcip, pp);
break;
case USB_EP_ATTR_INTR:
ohci_insert_intr_ed(ohcip, pp);
break;
case USB_EP_ATTR_ISOCH:
ohci_insert_isoc_ed(ohcip, pp);
break;
}
}
static void
ohci_insert_ctrl_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_ed_t *ept = pp->pp_ept;
ohci_ed_t *prev_ept;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_ctrl_ed:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (Get_OpReg(hcr_ctrl_head)) {
prev_ept = ohci_ed_iommu_to_cpu(ohcip,
Get_OpReg(hcr_ctrl_head));
Set_ED(prev_ept->hced_prev, ohci_ed_cpu_to_iommu(ohcip, ept));
}
Set_ED(ept->hced_next, Get_OpReg(hcr_ctrl_head));
Set_OpReg(hcr_ctrl_head, ohci_ed_cpu_to_iommu(ohcip, ept));
if (!ohcip->ohci_open_ctrl_pipe_count) {
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) | HCR_CONTROL_CLE));
}
ohcip->ohci_open_ctrl_pipe_count++;
}
static void
ohci_insert_bulk_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_ed_t *ept = pp->pp_ept;
ohci_ed_t *prev_ept;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_bulk_ed:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (Get_OpReg(hcr_bulk_head)) {
prev_ept = ohci_ed_iommu_to_cpu(ohcip,
Get_OpReg(hcr_bulk_head));
Set_ED(prev_ept->hced_prev, ohci_ed_cpu_to_iommu(ohcip, ept));
}
Set_ED(ept->hced_next, Get_OpReg(hcr_bulk_head));
Set_OpReg(hcr_bulk_head, ohci_ed_cpu_to_iommu(ohcip, ept));
if (!ohcip->ohci_open_bulk_pipe_count) {
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) | HCR_CONTROL_BLE));
}
ohcip->ohci_open_bulk_pipe_count++;
}
static void
ohci_insert_intr_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_ed_t *ept = pp->pp_ept;
ohci_ed_t *next_lattice_ept, *lattice_ept;
uint_t node;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_intr_ed:");
node = pp->pp_node;
if (node >= NUM_STATIC_NODES) {
node = ohci_hcca_intr_index(node);
next_lattice_ept = ohci_ed_iommu_to_cpu(ohcip,
Get_HCCA(ohcip->ohci_hccap->HccaIntTble[node]));
Set_ED(ept->hced_next,
ohci_ed_cpu_to_iommu(ohcip, next_lattice_ept));
Set_HCCA(ohcip->ohci_hccap->HccaIntTble[node],
ohci_ed_cpu_to_iommu(ohcip, ept));
Set_ED(ept->hced_prev, 0);
if (Get_ED(next_lattice_ept->hced_state) != HC_EPT_STATIC) {
Set_ED(next_lattice_ept->hced_prev,
ohci_ed_cpu_to_iommu(ohcip, ept));
}
} else {
lattice_ept = &ohcip->ohci_ed_pool_addr[node];
next_lattice_ept = ohci_ed_iommu_to_cpu(
ohcip, Get_ED(lattice_ept->hced_next));
Set_ED(ept->hced_next, Get_ED(lattice_ept->hced_next));
Set_ED(lattice_ept->hced_next,
ohci_ed_cpu_to_iommu(ohcip, ept));
Set_ED(ept->hced_prev,
ohci_ed_cpu_to_iommu(ohcip, lattice_ept));
if ((next_lattice_ept) &&
(Get_ED(next_lattice_ept->hced_state) != HC_EPT_STATIC)) {
Set_ED(next_lattice_ept->hced_prev,
ohci_ed_cpu_to_iommu(ohcip, ept));
}
}
if (!ohcip->ohci_open_periodic_pipe_count) {
ASSERT(!ohcip->ohci_open_isoch_pipe_count);
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) | HCR_CONTROL_PLE));
}
ohcip->ohci_open_periodic_pipe_count++;
}
static void
ohci_insert_isoc_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_ed_t *next_lattice_ept, *lattice_ept;
ohci_ed_t *ept = pp->pp_ept;
uint_t node;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_isoc_ed:");
node = pp->pp_node;
ASSERT(node == 0);
lattice_ept = &ohcip->ohci_ed_pool_addr[node];
next_lattice_ept = ohci_ed_iommu_to_cpu(
ohcip, Get_ED(lattice_ept->hced_next));
while (next_lattice_ept) {
lattice_ept = next_lattice_ept;
next_lattice_ept = ohci_ed_iommu_to_cpu(
ohcip, Get_ED(lattice_ept->hced_next));
}
Set_ED(ept->hced_next, 0);
Set_ED(ept->hced_prev, ohci_ed_cpu_to_iommu(ohcip, lattice_ept));
Set_ED(lattice_ept->hced_next, ohci_ed_cpu_to_iommu(ohcip, ept));
if (!ohcip->ohci_open_isoch_pipe_count) {
Set_OpReg(hcr_control, (Get_OpReg(hcr_control) |
HCR_CONTROL_PLE | HCR_CONTROL_IE));
}
ohcip->ohci_open_periodic_pipe_count++;
ohcip->ohci_open_isoch_pipe_count++;
}
static void
ohci_modify_sKip_bit(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
skip_bit_t action,
usb_flags_t flag)
{
ohci_ed_t *ept = pp->pp_ept;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_modify_sKip_bit: action = 0x%x flag = 0x%x",
action, flag);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (action == CLEAR_sKip) {
Set_ED(ept->hced_ctrl, (Get_ED(ept->hced_ctrl) & ~HC_EPT_sKip));
} else {
if (flag & OHCI_FLAGS_DMA_SYNC) {
Sync_ED_TD_Pool(ohcip);
}
if ((Get_ED(ept->hced_headp) & HC_EPT_Halt) ||
(Get_ED(ept->hced_ctrl) & HC_EPT_sKip)) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_modify_sKip_bit: "
"Halt or Skip bit is already set");
} else {
Set_ED(ept->hced_ctrl,
(Get_ED(ept->hced_ctrl) | HC_EPT_sKip));
if (flag & OHCI_FLAGS_SLEEP) {
(void) ohci_wait_for_sof(ohcip);
if (flag & OHCI_FLAGS_DMA_SYNC) {
Sync_ED_TD_Pool(ohcip);
}
}
}
}
}
static void
ohci_remove_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
uchar_t attributes;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_remove_ed:");
attributes = pp->pp_pipe_handle->p_ep.bmAttributes & USB_EP_ATTR_MASK;
switch (attributes) {
case USB_EP_ATTR_CONTROL:
ohci_remove_ctrl_ed(ohcip, pp);
break;
case USB_EP_ATTR_BULK:
ohci_remove_bulk_ed(ohcip, pp);
break;
case USB_EP_ATTR_INTR:
case USB_EP_ATTR_ISOCH:
ohci_remove_periodic_ed(ohcip, pp);
break;
}
}
static void
ohci_remove_ctrl_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_ed_t *ept = pp->pp_ept;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_remove_ctrl_ed:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(!(Get_OpReg(hcr_control) & HCR_CONTROL_CLE));
ohcip->ohci_open_ctrl_pipe_count--;
ohci_detach_ed_from_list(ohcip, ept, USB_EP_ATTR_CONTROL);
if (Get_ED(ept->hced_next)) {
Set_OpReg(hcr_ctrl_curr, Get_ED(ept->hced_next));
} else {
Set_OpReg(hcr_ctrl_curr, Get_OpReg(hcr_ctrl_head));
}
if (ohcip->ohci_open_ctrl_pipe_count) {
ASSERT(Get_OpReg(hcr_ctrl_head));
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) | HCR_CONTROL_CLE));
}
ohci_insert_ed_on_reclaim_list(ohcip, pp);
}
static void
ohci_remove_bulk_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_ed_t *ept = pp->pp_ept;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_remove_bulk_ed:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(!(Get_OpReg(hcr_control) & HCR_CONTROL_BLE));
ohcip->ohci_open_bulk_pipe_count--;
ohci_detach_ed_from_list(ohcip, ept, USB_EP_ATTR_BULK);
if (Get_ED(ept->hced_next)) {
Set_OpReg(hcr_bulk_curr, Get_ED(ept->hced_next));
} else {
Set_OpReg(hcr_bulk_curr, Get_OpReg(hcr_bulk_head));
}
if (ohcip->ohci_open_bulk_pipe_count) {
ASSERT(Get_OpReg(hcr_bulk_head));
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) | HCR_CONTROL_BLE));
}
ohci_insert_ed_on_reclaim_list(ohcip, pp);
}
static void
ohci_remove_periodic_ed(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_ed_t *ept = pp->pp_ept;
uint_t ept_type;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_remove_periodic_ed:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT((Get_ED(ept->hced_tailp) & HC_EPT_TD_TAIL) ==
(Get_ED(ept->hced_headp) & HC_EPT_TD_HEAD));
ohcip->ohci_open_periodic_pipe_count--;
ept_type = pp->pp_pipe_handle->
p_ep.bmAttributes & USB_EP_ATTR_MASK;
if (ept_type == USB_EP_ATTR_ISOCH) {
ohcip->ohci_open_isoch_pipe_count--;
}
Set_ED(ept->hced_node, pp->pp_node);
ohci_detach_ed_from_list(ohcip, ept, ept_type);
if (!ohcip->ohci_open_isoch_pipe_count) {
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) & ~(HCR_CONTROL_IE)));
}
if (!ohcip->ohci_open_periodic_pipe_count) {
ASSERT(!ohcip->ohci_open_isoch_pipe_count);
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) & ~(HCR_CONTROL_PLE)));
}
ohci_insert_ed_on_reclaim_list(ohcip, pp);
}
static void
ohci_detach_ed_from_list(
ohci_state_t *ohcip,
ohci_ed_t *ept,
uint_t ept_type)
{
ohci_ed_t *prev_ept;
ohci_ed_t *next_ept;
uint_t node;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_detach_ed_from_list:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
prev_ept = ohci_ed_iommu_to_cpu(ohcip, Get_ED(ept->hced_prev));
next_ept = ohci_ed_iommu_to_cpu(ohcip, Get_ED(ept->hced_next));
if (prev_ept == NULL) {
if (next_ept) {
switch (ept_type) {
case USB_EP_ATTR_CONTROL:
Set_OpReg(hcr_ctrl_head,
Get_ED(ept->hced_next));
Set_ED(next_ept->hced_prev, 0);
break;
case USB_EP_ATTR_BULK:
Set_OpReg(hcr_bulk_head,
Get_ED(ept->hced_next));
Set_ED(next_ept->hced_prev, 0);
break;
case USB_EP_ATTR_INTR:
ASSERT(Get_ED(ept->hced_node) >=
NUM_STATIC_NODES);
node = ohci_hcca_intr_index(
Get_ED(ept->hced_node));
Set_HCCA(ohcip->ohci_hccap->
HccaIntTble[node], Get_ED(ept->hced_next));
if (Get_ED(next_ept->hced_state) !=
HC_EPT_STATIC) {
Set_ED(next_ept->hced_prev, 0);
}
break;
case USB_EP_ATTR_ISOCH:
default:
break;
}
} else {
switch (ept_type) {
case USB_EP_ATTR_CONTROL:
Set_OpReg(hcr_ctrl_head, 0);
break;
case USB_EP_ATTR_BULK:
Set_OpReg(hcr_bulk_head, 0);
break;
case USB_EP_ATTR_INTR:
case USB_EP_ATTR_ISOCH:
default:
break;
}
}
} else {
Set_ED(prev_ept->hced_next, Get_ED(ept->hced_next));
if ((next_ept) &&
(Get_ED(next_ept->hced_state) != HC_EPT_STATIC)) {
Set_ED(next_ept->hced_prev, Get_ED(ept->hced_prev));
}
}
}
static void
ohci_insert_ed_on_reclaim_list(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_ed_t *ept = pp->pp_ept;
ohci_ed_t *next_ept, *prev_ept;
usb_frame_number_t frame_number;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
frame_number =
ohci_get_current_frame_number(ohcip) + MAX_SOF_WAIT_COUNT;
Set_ED(ept->hced_reclaim_frame,
((uint32_t)(OHCI_GET_ID((void *)(uintptr_t)frame_number))));
if (ohcip->ohci_reclaim_list) {
next_ept = ohcip->ohci_reclaim_list;
while (next_ept) {
prev_ept = next_ept;
next_ept = ohci_ed_iommu_to_cpu(ohcip,
Get_ED(next_ept->hced_reclaim_next));
}
Set_ED(prev_ept->hced_reclaim_next,
ohci_ed_cpu_to_iommu(ohcip, ept));
} else {
ohcip->ohci_reclaim_list = ept;
}
ASSERT(Get_ED(ept->hced_reclaim_next) == 0);
Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
}
void
ohci_deallocate_ed(
ohci_state_t *ohcip,
ohci_ed_t *old_ed)
{
ohci_td_t *dummy_td;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_deallocate_ed:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
dummy_td = ohci_td_iommu_to_cpu(ohcip, Get_ED(old_ed->hced_headp));
if (dummy_td) {
ASSERT(Get_TD(dummy_td->hctd_state) == HC_TD_DUMMY);
ohci_deallocate_td(ohcip, dummy_td);
}
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_deallocate_ed: Deallocated 0x%p", (void *)old_ed);
bzero((void *)old_ed, sizeof (ohci_ed_t));
Set_ED(old_ed->hced_state, HC_EPT_FREE);
}
uint32_t
ohci_ed_cpu_to_iommu(
ohci_state_t *ohcip,
ohci_ed_t *addr)
{
uint32_t ed;
ed = (uint32_t)ohcip->ohci_ed_pool_cookie.dmac_address +
(uint32_t)((uintptr_t)addr - (uintptr_t)(ohcip->ohci_ed_pool_addr));
ASSERT(ed >= ohcip->ohci_ed_pool_cookie.dmac_address);
ASSERT(ed <= ohcip->ohci_ed_pool_cookie.dmac_address +
sizeof (ohci_ed_t) * ohci_ed_pool_size);
return (ed);
}
static ohci_ed_t *
ohci_ed_iommu_to_cpu(
ohci_state_t *ohcip,
uintptr_t addr)
{
ohci_ed_t *ed;
if (addr == 0)
return (NULL);
ed = (ohci_ed_t *)((uintptr_t)
(addr - ohcip->ohci_ed_pool_cookie.dmac_address) +
(uintptr_t)ohcip->ohci_ed_pool_addr);
ASSERT(ed >= ohcip->ohci_ed_pool_addr);
ASSERT((uintptr_t)ed <= (uintptr_t)ohcip->ohci_ed_pool_addr +
(uintptr_t)(sizeof (ohci_ed_t) * ohci_ed_pool_size));
return (ed);
}
static int
ohci_initialize_dummy(
ohci_state_t *ohcip,
ohci_ed_t *ept)
{
ohci_td_t *dummy;
dummy = ohci_allocate_td_from_pool(ohcip);
if (dummy == NULL) {
return (USB_NO_RESOURCES);
}
Set_ED(ept->hced_headp, (ohci_td_cpu_to_iommu(ohcip, dummy)));
Set_ED(ept->hced_tailp, (ohci_td_cpu_to_iommu(ohcip, dummy)));
return (USB_SUCCESS);
}
static ohci_trans_wrapper_t *
ohci_allocate_ctrl_resources(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags)
{
size_t td_count = 2;
size_t ctrl_buf_size;
ohci_trans_wrapper_t *tw;
if (ctrl_reqp->ctrl_wLength) {
td_count++;
}
if (ctrl_reqp->ctrl_wLength) {
ctrl_buf_size = OHCI_MAX_TD_BUF_SIZE +
ctrl_reqp->ctrl_wLength;
} else {
ctrl_buf_size = SETUP_SIZE;
}
tw = ohci_allocate_tw_resources(ohcip, pp, ctrl_buf_size,
usb_flags, td_count);
return (tw);
}
static void
ohci_insert_ctrl_req(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp,
ohci_trans_wrapper_t *tw,
usb_flags_t usb_flags)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
uchar_t bmRequestType = ctrl_reqp->ctrl_bmRequestType;
uchar_t bRequest = ctrl_reqp->ctrl_bRequest;
uint16_t wValue = ctrl_reqp->ctrl_wValue;
uint16_t wIndex = ctrl_reqp->ctrl_wIndex;
uint16_t wLength = ctrl_reqp->ctrl_wLength;
mblk_t *data = ctrl_reqp->ctrl_data;
uint32_t ctrl = 0;
int sdata;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_ctrl_req:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
tw->tw_curr_xfer_reqp = (usb_opaque_t)ctrl_reqp;
tw->tw_timeout = ctrl_reqp->ctrl_timeout ?
ctrl_reqp->ctrl_timeout : OHCI_DEFAULT_XFER_TIMEOUT;
tw->tw_handle_td = ohci_handle_ctrl_td;
tw->tw_handle_callback_value = NULL;
sdata = (bmRequestType << 24) | (bRequest << 16) |
(((wValue >> 8) | (wValue << 8)) & 0x0000FFFF);
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_create_setup_pkt: sdata = 0x%x", sdata);
ddi_put32(tw->tw_accesshandle, (uint_t *)tw->tw_buf, sdata);
sdata = (uint32_t)(((((wIndex >> 8) |
(wIndex << 8)) << 16) & 0xFFFF0000) |
(((wLength >> 8) | (wLength << 8)) & 0x0000FFFF));
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_setup_pkt: sdata = 0x%x", sdata);
ddi_put32(tw->tw_accesshandle,
(uint_t *)((uintptr_t)tw->tw_buf + sizeof (uint_t)), sdata);
ctrl = HC_TD_SETUP|HC_TD_MS_DT|HC_TD_DT_0|HC_TD_6I;
(void) ohci_insert_hc_td(ohcip, ctrl, 0, SETUP_SIZE,
OHCI_CTRL_SETUP_PHASE, pp, tw);
USB_DPRINTF_L3(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"Create_setup: pp 0x%p", (void *)pp);
if (wLength != 0) {
if (bmRequestType & USB_DEV_REQ_DEV_TO_HOST) {
tw->tw_direction = HC_TD_IN;
} else {
tw->tw_direction = HC_TD_OUT;
ddi_rep_put8(tw->tw_accesshandle, data->b_rptr,
(uint8_t *)(tw->tw_buf + OHCI_MAX_TD_BUF_SIZE),
wLength, DDI_DEV_AUTOINCR);
}
ctrl = (ctrl_reqp->ctrl_attributes & USB_ATTRS_SHORT_XFER_OK) ?
HC_TD_R : 0;
if (tw->tw_direction == HC_TD_IN) {
ctrl = ctrl|HC_TD_IN|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_6I;
} else {
ctrl = ctrl|HC_TD_OUT|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_6I;
}
(void) ohci_insert_hc_td(ohcip, ctrl, OHCI_MAX_TD_BUF_SIZE,
wLength, OHCI_CTRL_DATA_PHASE, pp, tw);
if (tw->tw_direction == HC_TD_IN) {
ctrl = HC_TD_OUT|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_1I;
} else {
ctrl = HC_TD_IN|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_1I;
}
} else {
ctrl = HC_TD_IN|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_1I;
}
(void) ohci_insert_hc_td(ohcip, ctrl, 0,
0, OHCI_CTRL_STATUS_PHASE, pp, tw);
Set_OpReg(hcr_cmd_status, HCR_STATUS_CLF);
ohci_start_xfer_timer(ohcip, pp, tw);
}
static ohci_trans_wrapper_t *
ohci_allocate_bulk_resources(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags)
{
size_t td_count = 0;
ohci_trans_wrapper_t *tw;
if (bulk_reqp->bulk_len > OHCI_MAX_BULK_XFER_SIZE) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_bulk_resources: Bulk request size 0x%x is "
"more than 0x%x", bulk_reqp->bulk_len,
OHCI_MAX_BULK_XFER_SIZE);
return (NULL);
}
td_count = bulk_reqp->bulk_len / OHCI_MAX_TD_XFER_SIZE;
if (bulk_reqp->bulk_len % OHCI_MAX_TD_XFER_SIZE ||
bulk_reqp->bulk_len == 0) {
td_count++;
}
tw = ohci_allocate_tw_resources(ohcip, pp, bulk_reqp->bulk_len,
usb_flags, td_count);
return (tw);
}
static void
ohci_insert_bulk_req(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_bulk_req_t *bulk_reqp,
ohci_trans_wrapper_t *tw,
usb_flags_t flags)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
uint_t bulk_pkt_size, count;
size_t residue = 0, len = 0;
uint32_t ctrl = 0;
int pipe_dir;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_bulk_req: bulk_reqp = 0x%p flags = 0x%x",
(void *)bulk_reqp, flags);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
bulk_pkt_size = min(bulk_reqp->bulk_len, OHCI_MAX_TD_XFER_SIZE);
if (bulk_pkt_size)
residue = tw->tw_length % bulk_pkt_size;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_bulk_req: bulk_pkt_size = %d", bulk_pkt_size);
tw->tw_curr_xfer_reqp = (usb_opaque_t)bulk_reqp;
tw->tw_timeout = bulk_reqp->bulk_timeout;
tw->tw_handle_td = ohci_handle_bulk_td;
tw->tw_handle_callback_value = NULL;
tw->tw_direction =
(pipe_dir == USB_EP_DIR_OUT) ? HC_TD_OUT : HC_TD_IN;
if (tw->tw_direction == HC_TD_OUT && bulk_reqp->bulk_len) {
ASSERT(bulk_reqp->bulk_data != NULL);
ddi_rep_put8(tw->tw_accesshandle,
bulk_reqp->bulk_data->b_rptr, (uint8_t *)tw->tw_buf,
bulk_reqp->bulk_len, DDI_DEV_AUTOINCR);
}
ctrl = tw->tw_direction|HC_TD_DT_0|HC_TD_6I;
for (count = 0; count < tw->tw_num_tds; count++) {
if (count == (tw->tw_num_tds - 1)) {
ctrl = ((ctrl & ~HC_TD_DI) | HC_TD_1I);
if (residue) {
bulk_pkt_size = (uint_t)residue;
}
if (bulk_reqp->bulk_attributes &
USB_ATTRS_SHORT_XFER_OK) {
ctrl |= HC_TD_R;
}
}
(void) ohci_insert_hc_td(ohcip, ctrl, len,
bulk_pkt_size, 0, pp, tw);
len = len + bulk_pkt_size;
}
Set_OpReg(hcr_cmd_status, HCR_STATUS_BLF);
ohci_start_xfer_timer(ohcip, pp, tw);
}
int
ohci_start_periodic_pipe_polling(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_opaque_t periodic_in_reqp,
usb_flags_t flags)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_periodic_pipe_polling: ep%d",
ph->p_ep.bEndpointAddress & USB_EP_NUM_MASK);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_INTR)) {
error = ohci_handle_root_hub_pipe_start_intr_polling(ph,
(usb_intr_req_t *)periodic_in_reqp, flags);
return (error);
}
switch (pp->pp_state) {
case OHCI_PIPE_STATE_IDLE:
pp->pp_client_periodic_in_reqp = periodic_in_reqp;
error = ohci_start_pipe_polling(ohcip, ph, flags);
if (error != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_periodic_pipe_polling: "
"Start polling failed");
pp->pp_client_periodic_in_reqp = NULL;
return (error);
}
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_start_periodic_pipe_polling: PP = 0x%p", (void *)pp);
ASSERT((pp->pp_tw_head != NULL) && (pp->pp_tw_tail != NULL));
break;
case OHCI_PIPE_STATE_ACTIVE:
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_periodic_pipe_polling: "
"Polling is already in progress");
error = USB_FAILURE;
break;
case OHCI_PIPE_STATE_ERROR:
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_periodic_pipe_polling: "
"Pipe is halted and perform reset before restart polling");
error = USB_FAILURE;
break;
default:
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_periodic_pipe_polling: Undefined state");
error = USB_FAILURE;
break;
}
return (error);
}
static int
ohci_start_pipe_polling(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
ohci_trans_wrapper_t *tw_list, *tw;
int i, total_tws;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_pipe_polling:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (pp->pp_max_periodic_req_cnt == 0) {
ohci_set_periodic_pipe_polling(ohcip, ph);
}
ASSERT(pp->pp_max_periodic_req_cnt != 0);
tw_list = NULL;
total_tws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt;
for (i = 0; i < total_tws; i++) {
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_INTR:
tw = ohci_allocate_intr_resources(
ohcip, ph, NULL, flags);
break;
case USB_EP_ATTR_ISOCH:
tw = ohci_allocate_isoc_resources(
ohcip, ph, NULL, flags);
break;
}
if (tw == NULL) {
error = USB_NO_RESOURCES;
tw = tw_list;
while (tw != NULL) {
tw_list = tw->tw_next;
ohci_deallocate_periodic_in_resource(
ohcip, pp, tw);
ohci_deallocate_tw_resources(ohcip, pp, tw);
tw = tw_list;
}
return (error);
} else {
if (tw_list == NULL) {
tw_list = tw;
}
}
}
i = 0;
while (pp->pp_cur_periodic_req_cnt < pp->pp_max_periodic_req_cnt) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_pipe_polling: max = %d curr = %d tw = %p:",
pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt,
(void *)tw_list);
tw = tw_list;
tw_list = tw->tw_next;
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_INTR:
ohci_insert_intr_req(ohcip, pp, tw, flags);
break;
case USB_EP_ATTR_ISOCH:
error = ohci_insert_isoc_req(ohcip, pp, tw, flags);
break;
}
if (error == USB_SUCCESS) {
pp->pp_cur_periodic_req_cnt++;
} else {
tw = tw_list;
while (tw != NULL) {
tw_list = tw->tw_next;
ohci_deallocate_periodic_in_resource(
ohcip, pp, tw);
ohci_deallocate_tw_resources(ohcip, pp, tw);
tw = tw_list;
}
if (i != 0) {
error = USB_SUCCESS;
}
break;
}
i++;
}
return (error);
}
static void
ohci_set_periodic_pipe_polling(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *endpoint = &ph->p_ep;
uchar_t ep_attr = endpoint->bmAttributes;
uint_t interval;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_set_periodic_pipe_polling:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
pp->pp_cur_periodic_req_cnt = 0;
if (((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) &&
(pp->pp_client_periodic_in_reqp)) {
usb_intr_req_t *intr_reqp =
(usb_intr_req_t *)pp->pp_client_periodic_in_reqp;
if (intr_reqp->intr_attributes &
USB_ATTRS_ONE_XFER) {
pp->pp_max_periodic_req_cnt = INTR_XMS_REQS;
return;
}
}
mutex_enter(&ph->p_usba_device->usb_mutex);
interval = ohci_adjust_polling_interval(ohcip, endpoint,
ph->p_usba_device->usb_port_status);
mutex_exit(&ph->p_usba_device->usb_mutex);
switch (interval) {
case INTR_1MS_POLL:
pp->pp_max_periodic_req_cnt = INTR_1MS_REQS;
break;
case INTR_2MS_POLL:
pp->pp_max_periodic_req_cnt = INTR_2MS_REQS;
break;
default:
pp->pp_max_periodic_req_cnt = INTR_XMS_REQS;
break;
}
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_set_periodic_pipe_polling: Max periodic requests = %d",
pp->pp_max_periodic_req_cnt);
}
static ohci_trans_wrapper_t *
ohci_allocate_intr_resources(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_intr_req_t *intr_reqp,
usb_flags_t flags)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
int pipe_dir;
size_t td_count = 1;
size_t tw_length;
ohci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_intr_resources:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
if (intr_reqp) {
tw_length = intr_reqp->intr_len;
} else {
ASSERT(pipe_dir == USB_EP_DIR_IN);
tw_length = (pp->pp_client_periodic_in_reqp) ?
(((usb_intr_req_t *)pp->
pp_client_periodic_in_reqp)->intr_len) :
ph->p_ep.wMaxPacketSize;
}
if (tw_length > OHCI_MAX_TD_XFER_SIZE) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_intr_resources: Intr request size 0x%lx is "
"more than 0x%x", tw_length, OHCI_MAX_TD_XFER_SIZE);
return (NULL);
}
if ((tw = ohci_allocate_tw_resources(ohcip, pp, tw_length,
flags, td_count)) == NULL) {
return (NULL);
}
if (pipe_dir == USB_EP_DIR_IN) {
if (ohci_allocate_periodic_in_resource(ohcip, pp, tw, flags) !=
USB_SUCCESS) {
ohci_deallocate_tw_resources(ohcip, pp, tw);
return (NULL);
}
tw->tw_direction = HC_TD_IN;
} else {
if (tw_length) {
ASSERT(intr_reqp->intr_data != NULL);
ddi_rep_put8(tw->tw_accesshandle,
intr_reqp->intr_data->b_rptr, (uint8_t *)tw->tw_buf,
intr_reqp->intr_len, DDI_DEV_AUTOINCR);
}
tw->tw_curr_xfer_reqp = (usb_opaque_t)intr_reqp;
tw->tw_direction = HC_TD_OUT;
}
if (intr_reqp) {
tw->tw_timeout = intr_reqp->intr_timeout;
}
tw->tw_handle_td = ohci_handle_intr_td;
tw->tw_handle_callback_value = NULL;
return (tw);
}
static void
ohci_insert_intr_req(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
usb_flags_t flags)
{
usb_intr_req_t *curr_intr_reqp = NULL;
uint_t ctrl = 0;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(tw->tw_curr_xfer_reqp != NULL);
curr_intr_reqp = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
ctrl = tw->tw_direction | HC_TD_DT_0 | HC_TD_1I;
if (curr_intr_reqp->intr_attributes & USB_ATTRS_SHORT_XFER_OK) {
ctrl |= HC_TD_R;
}
(void) ohci_insert_hc_td(ohcip, ctrl, 0, tw->tw_length, 0, pp, tw);
ohci_start_xfer_timer(ohcip, pp, tw);
}
static int
ohci_stop_periodic_pipe_polling(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_stop_periodic_pipe_polling: Flags = 0x%x", flags);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_INTR)) {
ohci_handle_root_hub_pipe_stop_intr_polling(
ph, flags);
return (USB_SUCCESS);
}
if (pp->pp_state != OHCI_PIPE_STATE_ACTIVE) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_stop_periodic_pipe_polling: Polling already stopped");
return (USB_SUCCESS);
}
pp->pp_state = OHCI_PIPE_STATE_STOP_POLLING;
ohci_pipe_cleanup(ohcip, ph);
return (USB_SUCCESS);
}
static ohci_trans_wrapper_t *
ohci_allocate_isoc_resources(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_reqp,
usb_flags_t flags)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
int pipe_dir;
uint_t max_pkt_size = ph->p_ep.wMaxPacketSize;
uint_t max_isoc_xfer_size;
usb_isoc_pkt_descr_t *isoc_pkt_descr, *start_isoc_pkt_descr;
ushort_t isoc_pkt_count;
size_t count, td_count;
size_t tw_length;
size_t isoc_pkts_length;
ohci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_isoc_resources: flags = ox%x", flags);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (pp->pp_state == OHCI_PIPE_STATE_ERROR) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_isoc_resources:"
"Pipe is in error state, need pipe reset to continue");
return (NULL);
}
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
max_isoc_xfer_size = OHCI_MAX_ISOC_PKTS_PER_XFER * max_pkt_size;
if (isoc_reqp) {
isoc_pkt_descr = isoc_reqp->isoc_pkt_descr;
isoc_pkt_count = isoc_reqp->isoc_pkts_count;
isoc_pkts_length = isoc_reqp->isoc_pkts_length;
} else {
isoc_pkt_descr = ((usb_isoc_req_t *)
pp->pp_client_periodic_in_reqp)->isoc_pkt_descr;
isoc_pkt_count = ((usb_isoc_req_t *)
pp->pp_client_periodic_in_reqp)->isoc_pkts_count;
isoc_pkts_length = ((usb_isoc_req_t *)
pp->pp_client_periodic_in_reqp)->isoc_pkts_length;
}
start_isoc_pkt_descr = isoc_pkt_descr;
if (pipe_dir == USB_EP_DIR_IN) {
for (count = 0, tw_length = 0;
count < isoc_pkt_count; count++) {
tw_length += isoc_pkt_descr->isoc_pkt_length;
isoc_pkt_descr++;
}
if ((isoc_pkts_length) && (isoc_pkts_length != tw_length)) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_isoc_resources: "
"isoc_pkts_length 0x%lx is not equal to the sum of "
"all pkt lengths 0x%lx in an isoc request",
isoc_pkts_length, tw_length);
return (NULL);
}
} else {
ASSERT(isoc_reqp != NULL);
tw_length = MBLKL(isoc_reqp->isoc_data);
}
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_isoc_resources: length = 0x%lx", tw_length);
if (tw_length > max_isoc_xfer_size) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_isoc_resources: Maximum isoc request"
"size 0x%x Given isoc request size 0x%lx",
max_isoc_xfer_size, tw_length);
return (NULL);
}
td_count = isoc_pkt_count / OHCI_ISOC_PKTS_PER_TD;
if (isoc_pkt_count % OHCI_ISOC_PKTS_PER_TD) {
td_count++;
}
tw = ohci_create_isoc_transfer_wrapper(ohcip, pp, tw_length,
start_isoc_pkt_descr, isoc_pkt_count, td_count, flags);
if (tw == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_create_isoc_transfer_wrapper: "
"Unable to allocate TW");
return (NULL);
}
if (ohci_allocate_tds_for_tw(ohcip, tw, td_count) ==
USB_SUCCESS) {
tw->tw_num_tds = (uint_t)td_count;
} else {
ohci_deallocate_tw_resources(ohcip, pp, tw);
return (NULL);
}
if (pipe_dir == USB_EP_DIR_IN) {
if (ohci_allocate_periodic_in_resource(ohcip, pp, tw, flags) !=
USB_SUCCESS) {
ohci_deallocate_tw_resources(ohcip, pp, tw);
return (NULL);
}
tw->tw_direction = HC_TD_IN;
} else {
if (tw->tw_length) {
uchar_t *p;
int i;
ASSERT(isoc_reqp->isoc_data != NULL);
p = isoc_reqp->isoc_data->b_rptr;
for (i = 0; i < td_count; i++) {
ddi_rep_put8(
tw->tw_isoc_bufs[i].mem_handle, p,
(uint8_t *)tw->tw_isoc_bufs[i].buf_addr,
tw->tw_isoc_bufs[i].length,
DDI_DEV_AUTOINCR);
p += tw->tw_isoc_bufs[i].length;
}
}
tw->tw_curr_xfer_reqp = (usb_opaque_t)isoc_reqp;
tw->tw_direction = HC_TD_OUT;
}
tw->tw_handle_td = ohci_handle_isoc_td;
tw->tw_handle_callback_value = NULL;
return (tw);
}
static int
ohci_insert_isoc_req(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
uint_t flags)
{
size_t curr_isoc_xfer_offset, curr_isoc_xfer_len;
uint_t isoc_pkts, residue, count;
uint_t i, ctrl, frame_count;
uint_t error = USB_SUCCESS;
usb_isoc_req_t *curr_isoc_reqp;
usb_isoc_pkt_descr_t *curr_isoc_pkt_descr;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_isoc_req: flags = 0x%x", flags);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
curr_isoc_reqp = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
curr_isoc_pkt_descr = curr_isoc_reqp->isoc_pkt_descr;
ASSERT(curr_isoc_reqp != NULL);
ASSERT(curr_isoc_reqp->isoc_pkt_descr != NULL);
tw->tw_curr_isoc_pktp = curr_isoc_reqp->isoc_pkt_descr;
for (count = 0, curr_isoc_xfer_offset = 0,
isoc_pkts = 0; count < tw->tw_num_tds; count++) {
residue = curr_isoc_reqp->isoc_pkts_count - isoc_pkts;
if ((count == (tw->tw_num_tds - 1)) &&
(residue < OHCI_ISOC_PKTS_PER_TD)) {
frame_count = residue;
} else {
frame_count = OHCI_ISOC_PKTS_PER_TD;
}
curr_isoc_pkt_descr = tw->tw_curr_isoc_pktp;
for (i = 0, curr_isoc_xfer_len = 0;
i < frame_count; i++, curr_isoc_pkt_descr++) {
curr_isoc_xfer_len +=
curr_isoc_pkt_descr->isoc_pkt_length;
}
if (count == (tw->tw_num_tds - 1)) {
ctrl = ((((frame_count - 1) << HC_ITD_FC_SHIFT) &
HC_ITD_FC) | HC_TD_DT_0 | HC_TD_0I);
} else {
ctrl = ((((frame_count - 1) << HC_ITD_FC_SHIFT) &
HC_ITD_FC) | HC_TD_DT_0 | HC_TD_6I);
}
if ((error = ohci_insert_hc_td(ohcip, ctrl, count,
curr_isoc_xfer_len, 0, pp, tw)) !=
USB_SUCCESS) {
tw->tw_num_tds = count;
tw->tw_length = curr_isoc_xfer_offset;
break;
}
isoc_pkts += frame_count;
tw->tw_curr_isoc_pktp += frame_count;
curr_isoc_xfer_offset += curr_isoc_xfer_len;
}
if (error != USB_SUCCESS) {
if (tw->tw_direction == USB_EP_DIR_IN) {
ohci_deallocate_periodic_in_resource(ohcip, pp, tw);
}
if (tw->tw_direction == USB_EP_DIR_IN || count == 0) {
ohci_deallocate_tw_resources(ohcip, pp, tw);
if (pp->pp_cur_periodic_req_cnt) {
pp->pp_state = OHCI_PIPE_STATE_STOP_POLLING;
pp->pp_error = error;
} else {
pp->pp_state = OHCI_PIPE_STATE_IDLE;
}
}
} else {
tw->tw_curr_isoc_pktp = curr_isoc_reqp->isoc_pkt_descr;
pp->pp_flag &= ~OHCI_ISOC_XFER_CONTINUE;
}
return (error);
}
static int
ohci_insert_hc_td(
ohci_state_t *ohcip,
uint_t hctd_ctrl,
uint32_t hctd_dma_offs,
size_t hctd_length,
uint32_t hctd_ctrl_phase,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw)
{
ohci_td_t *new_dummy;
ohci_td_t *cpu_current_dummy;
ohci_ed_t *ept = pp->pp_ept;
int error;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
new_dummy = tw->tw_hctd_free_list;
ASSERT(new_dummy != NULL);
tw->tw_hctd_free_list = ohci_td_iommu_to_cpu(ohcip,
Get_TD(new_dummy->hctd_tw_next_td));
Set_TD(new_dummy->hctd_tw_next_td, NULL);
cpu_current_dummy = (ohci_td_t *)
(ohci_td_iommu_to_cpu(ohcip, Get_ED(ept->hced_tailp)));
ohci_fill_in_td(ohcip, cpu_current_dummy, new_dummy,
hctd_ctrl, hctd_dma_offs, hctd_length, hctd_ctrl_phase, pp, tw);
if ((pp->pp_pipe_handle->p_ep.bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH) {
error = ohci_insert_td_with_frame_number(
ohcip, pp, tw, cpu_current_dummy, new_dummy);
if (error != USB_SUCCESS) {
bzero((char *)cpu_current_dummy, sizeof (ohci_td_t));
Set_TD(cpu_current_dummy->hctd_state, HC_TD_DUMMY);
bzero((char *)new_dummy, sizeof (ohci_td_t));
Set_TD(new_dummy->hctd_state, HC_TD_DUMMY);
if (tw->tw_hctd_free_list != NULL) {
Set_TD(new_dummy->hctd_tw_next_td,
ohci_td_cpu_to_iommu(ohcip,
tw->tw_hctd_free_list));
}
tw->tw_hctd_free_list = new_dummy;
return (error);
}
} else {
Set_ED(ept->hced_tailp,
(ohci_td_cpu_to_iommu(ohcip, new_dummy)));
}
ohci_insert_td_on_tw(ohcip, tw, cpu_current_dummy);
return (USB_SUCCESS);
}
static ohci_td_t *
ohci_allocate_td_from_pool(ohci_state_t *ohcip)
{
int i, state;
ohci_td_t *td;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
for (i = 0; i < ohci_td_pool_size; i ++) {
state = Get_TD(ohcip->ohci_td_pool_addr[i].hctd_state);
if (state == HC_TD_FREE) {
break;
}
}
if (i >= ohci_td_pool_size) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_allocate_td_from_pool: TD exhausted");
return (NULL);
}
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_allocate_td_from_pool: Allocated %d", i);
td = &ohcip->ohci_td_pool_addr[i];
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_td_from_pool: td 0x%p", (void *)td);
Set_TD(td->hctd_state, HC_TD_DUMMY);
return (td);
}
static void
ohci_fill_in_td(
ohci_state_t *ohcip,
ohci_td_t *td,
ohci_td_t *new_dummy,
uint_t hctd_ctrl,
uint32_t hctd_dma_offs,
size_t hctd_length,
uint32_t hctd_ctrl_phase,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw)
{
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_fill_in_td: td 0x%p bufoffs 0x%x len 0x%lx",
(void *)td, hctd_dma_offs, hctd_length);
ASSERT(Get_TD(td->hctd_state) == HC_TD_DUMMY);
Set_TD(td->hctd_state, HC_TD_ACTIVE);
if ((pp->pp_pipe_handle->p_ep.bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH) {
ohci_init_itd(ohcip, tw, hctd_ctrl, hctd_dma_offs, td);
} else {
Set_TD(td->hctd_ctrl, (hctd_ctrl | HC_TD_CC_NA));
ohci_init_td(ohcip, tw, hctd_dma_offs, hctd_length, td);
}
Set_TD(td->hctd_next_td, (ohci_td_cpu_to_iommu(ohcip, new_dummy)));
if (hctd_ctrl_phase) {
Set_TD(td->hctd_ctrl_phase, hctd_ctrl_phase);
}
ohci_print_td(ohcip, td);
ASSERT(tw != NULL);
ASSERT(tw->tw_id != 0);
Set_TD(td->hctd_trans_wrapper, tw->tw_id);
Set_TD(td->hctd_tw_next_td, NULL);
}
void
ohci_init_td(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
uint32_t hctd_dma_offs,
size_t hctd_length,
ohci_td_t *td)
{
uint32_t page_addr, start_addr = 0, end_addr = 0;
size_t buf_len = hctd_length;
int rem_len, i;
ASSERT(buf_len == 0 || hctd_dma_offs >= tw->tw_dma_offs);
Set_TD(td->hctd_xfer_offs, hctd_dma_offs);
Set_TD(td->hctd_xfer_len, buf_len);
for (i = 0; (i < 2) && (buf_len > 0); i++) {
if ((tw->tw_dma_offs + tw->tw_cookie.dmac_size) <=
hctd_dma_offs) {
tw->tw_dma_offs += tw->tw_cookie.dmac_size;
ddi_dma_nextcookie(tw->tw_dmahandle, &tw->tw_cookie);
tw->tw_cookie_idx++;
ASSERT(tw->tw_cookie_idx < tw->tw_ncookies);
}
ASSERT((tw->tw_dma_offs + tw->tw_cookie.dmac_size) >
hctd_dma_offs);
rem_len = (tw->tw_dma_offs + tw->tw_cookie.dmac_size) -
hctd_dma_offs;
page_addr = (hctd_dma_offs - tw->tw_dma_offs) +
tw->tw_cookie.dmac_address;
ASSERT((page_addr % OHCI_4K_ALIGN) == 0);
if (i == 0) {
start_addr = page_addr;
}
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_init_td: page_addr 0x%x dmac_size "
"0x%lx idx %d", page_addr, tw->tw_cookie.dmac_size,
tw->tw_cookie_idx);
if (buf_len <= OHCI_MAX_TD_BUF_SIZE) {
ASSERT(buf_len <= rem_len);
end_addr = page_addr + buf_len - 1;
buf_len = 0;
break;
} else {
ASSERT(rem_len >= OHCI_MAX_TD_BUF_SIZE);
buf_len -= OHCI_MAX_TD_BUF_SIZE;
hctd_dma_offs += OHCI_MAX_TD_BUF_SIZE;
}
}
ASSERT(buf_len == 0);
Set_TD(td->hctd_cbp, start_addr);
Set_TD(td->hctd_buf_end, end_addr);
}
static void
ohci_init_itd(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
uint_t hctd_ctrl,
uint32_t index,
ohci_td_t *td)
{
uint32_t start_addr, end_addr, offset, offset_addr;
ohci_isoc_buf_t *bufp;
size_t buf_len;
uint_t buf, fc, toggle, flag;
usb_isoc_pkt_descr_t *temp_pkt_descr;
int i;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_init_itd: ctrl = 0x%x", hctd_ctrl);
Set_TD(td->hctd_ctrl, (hctd_ctrl | HC_TD_CC_NA));
bufp = &tw->tw_isoc_bufs[index];
Set_TD(td->hctd_xfer_offs, index);
Set_TD(td->hctd_xfer_len, bufp->length);
start_addr = bufp->cookie.dmac_address;
ASSERT((start_addr % OHCI_4K_ALIGN) == 0);
buf_len = bufp->length;
if (bufp->ncookies == OHCI_DMA_ATTR_TD_SGLLEN) {
buf_len = bufp->length - bufp->cookie.dmac_size;
ddi_dma_nextcookie(bufp->dma_handle, &bufp->cookie);
}
end_addr = bufp->cookie.dmac_address + buf_len - 1;
Set_TD(td->hctd_cbp, ((uint32_t)start_addr & HC_ITD_PAGE_MASK));
Set_TD(td->hctd_buf_end, end_addr);
fc = (hctd_ctrl & HC_ITD_FC) >> HC_ITD_FC_SHIFT;
toggle = 0;
buf = start_addr;
temp_pkt_descr = tw->tw_curr_isoc_pktp;
for (i = 0; i <= fc; i++) {
offset_addr = (uint32_t)((buf &
HC_ITD_OFFSET_ADDR) | (HC_ITD_OFFSET_CC));
flag = ((start_addr &
HC_ITD_PAGE_MASK) ^ (buf & HC_ITD_PAGE_MASK));
if (flag) {
offset_addr |= HC_ITD_4KBOUNDARY_CROSS;
}
if (toggle) {
offset = (uint32_t)((offset_addr <<
HC_ITD_OFFSET_SHIFT) & HC_ITD_ODD_OFFSET);
Set_TD(td->hctd_offsets[i / 2],
Get_TD(td->hctd_offsets[i / 2]) | offset);
toggle = 0;
} else {
offset = (uint32_t)(offset_addr & HC_ITD_EVEN_OFFSET);
Set_TD(td->hctd_offsets[i / 2],
Get_TD(td->hctd_offsets[i / 2]) | offset);
toggle = 1;
}
buf = (uint32_t)(buf + temp_pkt_descr->isoc_pkt_length);
temp_pkt_descr++;
}
}
static int
ohci_insert_td_with_frame_number(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *current_td,
ohci_td_t *dummy_td)
{
usb_isoc_req_t *isoc_reqp =
(usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
usb_frame_number_t current_frame_number, start_frame_number;
uint_t ddic, ctrl, isoc_pkts;
ohci_ed_t *ept = pp->pp_ept;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_td_with_frame_number:"
"isoc flags 0x%x", isoc_reqp->isoc_attributes);
isoc_pkts = ((Get_TD(current_td->hctd_ctrl) &
HC_ITD_FC) >> HC_ITD_FC_SHIFT) + 1;
ddic = ddi_enter_critical();
current_frame_number = ohci_get_current_frame_number(ohcip);
switch (isoc_reqp->isoc_attributes &
(USB_ATTRS_ISOC_START_FRAME | USB_ATTRS_ISOC_XFER_ASAP)) {
case USB_ATTRS_ISOC_START_FRAME:
if (pp->pp_flag & OHCI_ISOC_XFER_CONTINUE) {
start_frame_number = pp->pp_next_frame_number;
} else {
if ((isoc_reqp->isoc_frame_no == 0) ||
((isoc_reqp->isoc_frame_no +
isoc_reqp->isoc_pkts_count) <
current_frame_number)) {
ddi_exit_critical(ddic);
USB_DPRINTF_L2(PRINT_MASK_LISTS,
ohcip->ohci_log_hdl,
"ohci_insert_td_with_frame_number:"
"Invalid starting frame number");
return (USB_INVALID_START_FRAME);
}
start_frame_number = isoc_reqp->isoc_frame_no;
pp->pp_next_frame_number = 0;
}
break;
case USB_ATTRS_ISOC_XFER_ASAP:
if ((pp->pp_next_frame_number) &&
(pp->pp_next_frame_number > current_frame_number)) {
start_frame_number = pp->pp_next_frame_number;
} else {
start_frame_number =
current_frame_number + OHCI_FRAME_OFFSET;
}
if (!(pp->pp_flag & OHCI_ISOC_XFER_CONTINUE)) {
isoc_reqp->isoc_frame_no = start_frame_number;
}
break;
default:
ddi_exit_critical(ddic);
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_td_with_frame_number: Either starting "
"frame number or ASAP flags are not set, attrs = 0x%x",
isoc_reqp->isoc_attributes);
return (USB_NO_FRAME_NUMBER);
}
ctrl = Get_TD(current_td->hctd_ctrl) & (~(HC_ITD_SF));
Set_TD(current_td->hctd_ctrl, ctrl | (start_frame_number & HC_ITD_SF));
Set_ED(ept->hced_tailp, (ohci_td_cpu_to_iommu(ohcip, dummy_td)));
ddi_exit_critical(ddic);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_insert_td_with_frame_number:"
"current frame number 0x%llx start frame number 0x%llx",
(unsigned long long)current_frame_number,
(unsigned long long)start_frame_number);
pp->pp_next_frame_number = start_frame_number + isoc_pkts;
pp->pp_flag |= OHCI_ISOC_XFER_CONTINUE;
return (USB_SUCCESS);
}
static void
ohci_insert_td_on_tw(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
ohci_td_t *td)
{
Set_TD(td->hctd_tw_next_td, NULL);
if (tw->tw_hctd_head == NULL) {
ASSERT(tw->tw_hctd_tail == NULL);
tw->tw_hctd_head = td;
tw->tw_hctd_tail = td;
} else {
ohci_td_t *dummy = (ohci_td_t *)tw->tw_hctd_tail;
ASSERT(dummy != NULL);
ASSERT(dummy != td);
ASSERT(Get_TD(td->hctd_state) != HC_TD_DUMMY);
Set_TD(dummy->hctd_tw_next_td,
ohci_td_cpu_to_iommu(ohcip, td));
tw->tw_hctd_tail = td;
ASSERT(Get_TD(td->hctd_tw_next_td) == 0);
}
}
void
ohci_traverse_tds(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
ohci_trans_wrapper_t *tw;
ohci_ed_t *ept;
ohci_pipe_private_t *pp;
uint32_t addr;
ohci_td_t *tailp, *headp, *next;
pp = (ohci_pipe_private_t *)ph->p_hcd_private;
ept = pp->pp_ept;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_traverse_tds: ph = 0x%p ept = 0x%p",
(void *)ph, (void *)ept);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
addr = Get_ED(ept->hced_headp) & (uint32_t)HC_EPT_TD_HEAD;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_traverse_tds: addr (head) = 0x%x", addr);
headp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip, addr));
addr = Get_ED(ept->hced_tailp) & (uint32_t)HC_EPT_TD_TAIL;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_traverse_tds: addr (tail) = 0x%x", addr);
tailp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip, addr));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_traverse_tds: cpu head = 0x%p cpu tail = 0x%p",
(void *)headp, (void *)tailp);
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_traverse_tds: iommu head = 0x%x iommu tail = 0x%x",
ohci_td_cpu_to_iommu(ohcip, headp),
ohci_td_cpu_to_iommu(ohcip, tailp));
while (headp != tailp) {
next = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
(Get_TD(headp->hctd_next_td) & HC_EPT_TD_TAIL)));
tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
(uint32_t)Get_TD(headp->hctd_trans_wrapper));
ohci_stop_xfer_timer(ohcip, tw, OHCI_REMOVE_XFER_ALWAYS);
ohci_deallocate_td(ohcip, headp);
headp = next;
}
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_traverse_tds: head = 0x%p tail = 0x%p",
(void *)headp, (void *)tailp);
Set_ED(ept->hced_headp, (ohci_td_cpu_to_iommu(ohcip, headp)));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_traverse_tds: new head = 0x%x",
(ohci_td_cpu_to_iommu(ohcip, headp)));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_traverse_tds: tailp = 0x%x headp = 0x%x",
(Get_ED(ept->hced_tailp) & HC_EPT_TD_TAIL),
(Get_ED(ept->hced_headp) & HC_EPT_TD_HEAD));
ASSERT((Get_ED(ept->hced_tailp) & HC_EPT_TD_TAIL) ==
(Get_ED(ept->hced_headp) & HC_EPT_TD_HEAD));
}
static void
ohci_done_list_tds(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
ohci_trans_wrapper_t *head_tw = pp->pp_tw_head;
ohci_trans_wrapper_t *next_tw;
ohci_td_t *head_td, *next_td;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_done_list_tds:");
next_tw = head_tw;
while (next_tw) {
head_td = (ohci_td_t *)next_tw->tw_hctd_head;
next_td = head_td;
if (head_td) {
while (next_td) {
Set_TD(next_td->hctd_state, HC_TD_RECLAIM);
Set_TD(next_td->hctd_trans_wrapper, NULL);
next_td = ohci_td_iommu_to_cpu(ohcip,
Get_TD(next_td->hctd_tw_next_td));
}
}
ohci_stop_xfer_timer(ohcip, next_tw, OHCI_REMOVE_XFER_ALWAYS);
next_tw = next_tw->tw_next;
}
}
void
ohci_unlink_td_from_tw(
ohci_state_t *ohcip,
ohci_td_t *old_td,
ohci_trans_wrapper_t *tw)
{
ohci_td_t *next, *head, *tail;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_unlink_td_from_tw: ohcip = 0x%p, old_td = 0x%p, tw = 0x%p",
(void *)ohcip, (void *)old_td, (void *)tw);
if (old_td == NULL || tw == NULL) {
return;
}
head = tw->tw_hctd_head;
tail = tw->tw_hctd_tail;
if (head == NULL) {
return;
}
if (old_td == head) {
if (old_td == tail) {
tw->tw_hctd_head = NULL;
tw->tw_hctd_tail = NULL;
} else {
tw->tw_hctd_head = ohci_td_iommu_to_cpu(ohcip,
Get_TD(head->hctd_tw_next_td));
}
return;
}
next = ohci_td_iommu_to_cpu(ohcip, Get_TD(head->hctd_tw_next_td));
while (next && (old_td != next)) {
head = next;
next = ohci_td_iommu_to_cpu(ohcip,
Get_TD(next->hctd_tw_next_td));
}
if (old_td == next) {
Set_TD(head->hctd_tw_next_td, Get_TD(next->hctd_tw_next_td));
if (old_td == tail) {
tw->tw_hctd_tail = head;
}
}
}
void
ohci_deallocate_td(
ohci_state_t *ohcip,
ohci_td_t *old_td)
{
ohci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_deallocate_td: old_td = 0x%p", (void *)old_td);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if ((Get_TD(old_td->hctd_state) == HC_TD_DUMMY) ||
(Get_TD(old_td->hctd_state) == HC_TD_RECLAIM)) {
tw = (ohci_trans_wrapper_t *)((uintptr_t)
Get_TD(old_td->hctd_trans_wrapper));
ASSERT(tw == NULL);
} else {
tw = (ohci_trans_wrapper_t *)
OHCI_LOOKUP_ID((uint32_t)
Get_TD(old_td->hctd_trans_wrapper));
ASSERT(tw != NULL);
}
if ((Get_TD(old_td->hctd_state) != HC_TD_RECLAIM) && tw) {
ohci_unlink_td_from_tw(ohcip, old_td, tw);
}
bzero((void *)old_td, sizeof (ohci_td_t));
Set_TD(old_td->hctd_state, HC_TD_FREE);
USB_DPRINTF_L3(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_deallocate_td: td 0x%p", (void *)old_td);
}
uint32_t
ohci_td_cpu_to_iommu(
ohci_state_t *ohcip,
ohci_td_t *addr)
{
uint32_t td;
td = (uint32_t)ohcip->ohci_td_pool_cookie.dmac_address +
(uint32_t)((uintptr_t)addr - (uintptr_t)(ohcip->ohci_td_pool_addr));
ASSERT((ohcip->ohci_td_pool_cookie.dmac_address +
(uint32_t) (sizeof (ohci_td_t) *
(addr - ohcip->ohci_td_pool_addr))) ==
(ohcip->ohci_td_pool_cookie.dmac_address +
(uint32_t)((uintptr_t)addr - (uintptr_t)
(ohcip->ohci_td_pool_addr))));
ASSERT(td >= ohcip->ohci_td_pool_cookie.dmac_address);
ASSERT(td <= ohcip->ohci_td_pool_cookie.dmac_address +
sizeof (ohci_td_t) * ohci_td_pool_size);
return (td);
}
ohci_td_t *
ohci_td_iommu_to_cpu(
ohci_state_t *ohcip,
uintptr_t addr)
{
ohci_td_t *td;
if (addr == 0)
return (NULL);
td = (ohci_td_t *)((uintptr_t)
(addr - ohcip->ohci_td_pool_cookie.dmac_address) +
(uintptr_t)ohcip->ohci_td_pool_addr);
ASSERT(td >= ohcip->ohci_td_pool_addr);
ASSERT((uintptr_t)td <= (uintptr_t)ohcip->ohci_td_pool_addr +
(uintptr_t)(sizeof (ohci_td_t) * ohci_td_pool_size));
return (td);
}
int
ohci_allocate_tds_for_tw(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
size_t td_count)
{
ohci_td_t *td;
uint32_t td_addr;
int i;
int error = USB_SUCCESS;
for (i = 0; i < td_count; i++) {
td = ohci_allocate_td_from_pool(ohcip);
if (td == NULL) {
error = USB_NO_RESOURCES;
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_tds_for_tw: "
"Unable to allocate %lu TDs",
td_count);
break;
}
if (tw->tw_hctd_free_list != NULL) {
td_addr = ohci_td_cpu_to_iommu(ohcip,
tw->tw_hctd_free_list);
Set_TD(td->hctd_tw_next_td, td_addr);
}
tw->tw_hctd_free_list = td;
}
return (error);
}
static ohci_trans_wrapper_t *
ohci_allocate_tw_resources(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
size_t tw_length,
usb_flags_t usb_flags,
size_t td_count)
{
ohci_trans_wrapper_t *tw;
tw = ohci_create_transfer_wrapper(ohcip, pp, tw_length, usb_flags);
if (tw == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_tw_resources: Unable to allocate TW");
} else {
if (ohci_allocate_tds_for_tw(ohcip, tw, td_count) ==
USB_SUCCESS) {
tw->tw_num_tds = (uint_t)td_count;
} else {
ohci_deallocate_tw_resources(ohcip, pp, tw);
tw = NULL;
}
}
return (tw);
}
static void
ohci_free_tw_tds_resources(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw)
{
ohci_td_t *td;
ohci_td_t *temp_td;
td = tw->tw_hctd_free_list;
while (td != NULL) {
temp_td = ohci_td_iommu_to_cpu(ohcip,
Get_TD(td->hctd_tw_next_td));
ohci_deallocate_td(ohcip, td);
td = temp_td;
}
tw->tw_hctd_free_list = NULL;
}
static ohci_trans_wrapper_t *
ohci_create_transfer_wrapper(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
size_t length,
uint_t usb_flags)
{
ddi_device_acc_attr_t dev_attr;
int result;
size_t real_length;
ohci_trans_wrapper_t *tw;
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_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_transfer_wrapper: length = 0x%lx flags = 0x%x",
length, usb_flags);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if ((ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_ISOCH) {
return (NULL);
}
kmem_flag = KM_NOSLEEP;
dmamem_wait = DDI_DMA_DONTWAIT;
tw = kmem_zalloc(sizeof (ohci_trans_wrapper_t), kmem_flag);
if (tw == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_transfer_wrapper: kmem_zalloc failed");
return (NULL);
}
if (length == 0) {
goto dmadone;
}
bcopy(&ohcip->ohci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
dma_attr.dma_attr_sgllen = OHCI_DMA_ATTR_TW_SGLLEN;
dma_attr.dma_attr_align = OHCI_DMA_ATTR_ALIGNMENT;
result = ddi_dma_alloc_handle(ohcip->ohci_dip,
&dma_attr, dmamem_wait, 0, &tw->tw_dmahandle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_transfer_wrapper: Alloc handle failed");
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (NULL);
}
dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
result = ddi_dma_mem_alloc(tw->tw_dmahandle, length,
&dev_attr, DDI_DMA_CONSISTENT, dmamem_wait, NULL,
(caddr_t *)&tw->tw_buf, &real_length, &tw->tw_accesshandle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_transfer_wrapper: dma_mem_alloc fail");
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (NULL);
}
ASSERT(real_length >= length);
result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
(caddr_t)tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
dmamem_wait, NULL, &tw->tw_cookie, &tw->tw_ncookies);
if (result != DDI_DMA_MAPPED) {
ohci_decode_ddi_dma_addr_bind_handle_result(ohcip, result);
ddi_dma_mem_free(&tw->tw_accesshandle);
ddi_dma_free_handle(&tw->tw_dmahandle);
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (NULL);
}
tw->tw_cookie_idx = 0;
tw->tw_dma_offs = 0;
dmadone:
if (pp->pp_tw_head == NULL) {
pp->pp_tw_head = tw;
pp->pp_tw_tail = tw;
} else {
pp->pp_tw_tail->tw_next = tw;
pp->pp_tw_tail = tw;
}
tw->tw_length = length;
tw->tw_pipe_private = pp;
tw->tw_flags = usb_flags;
tw->tw_id = OHCI_GET_ID((void *)tw);
ASSERT(tw->tw_id != 0);
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
(void *)tw, tw->tw_ncookies);
return (tw);
}
static ohci_trans_wrapper_t *
ohci_create_isoc_transfer_wrapper(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
size_t length,
usb_isoc_pkt_descr_t *descr,
ushort_t pkt_count,
size_t td_count,
uint_t usb_flags)
{
ddi_device_acc_attr_t dev_attr;
int result;
size_t real_length, xfer_size;
uint_t ccount;
ohci_trans_wrapper_t *tw;
ddi_dma_attr_t dma_attr;
int kmem_flag;
uint_t i, j, frame_count, residue;
int (*dmamem_wait)(caddr_t);
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_isoc_pkt_descr_t *isoc_pkt_descr = descr;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_isoc_transfer_wrapper: length = 0x%lx flags = 0x%x",
length, usb_flags);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if ((ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) !=
USB_EP_ATTR_ISOCH) {
return (NULL);
}
if (servicing_interrupt()) {
kmem_flag = KM_NOSLEEP;
dmamem_wait = DDI_DMA_DONTWAIT;
} else {
kmem_flag = KM_SLEEP;
dmamem_wait = DDI_DMA_SLEEP;
}
tw = kmem_zalloc(sizeof (ohci_trans_wrapper_t), kmem_flag);
if (tw == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_transfer_wrapper: kmem_zalloc failed");
return (NULL);
}
tw->tw_isoc_strtlen = sizeof (ohci_isoc_buf_t) * td_count;
if ((tw->tw_isoc_bufs = kmem_zalloc(tw->tw_isoc_strtlen,
kmem_flag)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_create_isoc_transfer_wrapper: kmem_alloc "
"isoc buffer failed");
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (NULL);
}
bcopy(&ohcip->ohci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
dma_attr.dma_attr_sgllen = OHCI_DMA_ATTR_TD_SGLLEN;
dma_attr.dma_attr_align = OHCI_DMA_ATTR_ALIGNMENT;
dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
residue = pkt_count % OHCI_ISOC_PKTS_PER_TD;
for (i = 0; i < td_count; i++) {
tw->tw_isoc_bufs[i].index = i;
if ((i == (td_count - 1)) && (residue != 0)) {
frame_count = residue;
} else {
frame_count = OHCI_ISOC_PKTS_PER_TD;
}
result = ddi_dma_alloc_handle(ohcip->ohci_dip, &dma_attr,
dmamem_wait, 0, &tw->tw_isoc_bufs[i].dma_handle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_isoc_transfer_wrapper: "
"Alloc handle failed");
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, tw->tw_isoc_strtlen);
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (NULL);
}
for (xfer_size = 0, j = 0; j < frame_count; j++) {
ASSERT(isoc_pkt_descr != NULL);
xfer_size += isoc_pkt_descr->isoc_pkt_length;
isoc_pkt_descr++;
}
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);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_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, tw->tw_isoc_strtlen);
kmem_free(tw, sizeof (ohci_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 <= OHCI_DMA_ATTR_TD_SGLLEN)) {
tw->tw_isoc_bufs[i].length = xfer_size;
tw->tw_isoc_bufs[i].ncookies = ccount;
continue;
} else {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_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, tw->tw_isoc_strtlen);
kmem_free(tw, sizeof (ohci_trans_wrapper_t));
return (NULL);
}
}
if (pp->pp_tw_head == NULL) {
pp->pp_tw_head = tw;
pp->pp_tw_tail = tw;
} else {
pp->pp_tw_tail->tw_next = tw;
pp->pp_tw_tail = tw;
}
tw->tw_length = length;
tw->tw_ncookies = (uint_t)td_count;
tw->tw_pipe_private = pp;
tw->tw_flags = usb_flags;
tw->tw_id = OHCI_GET_ID((void *)tw);
ASSERT(tw->tw_id != 0);
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_create_isoc_transfer_wrapper: tw = 0x%p", (void *)tw);
return (tw);
}
static void
ohci_start_xfer_timer(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw)
{
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_xfer_timer: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (tw->tw_timeout) {
tw->tw_timeout++;
if (ohcip->ohci_timeout_list) {
tw->tw_timeout_next = ohcip->ohci_timeout_list;
}
ohcip->ohci_timeout_list = tw;
ohci_start_timer(ohcip);
}
}
void
ohci_stop_xfer_timer(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw,
uint_t flag)
{
timeout_id_t timer_id;
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_stop_xfer_timer: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (ohcip->ohci_timeout_list == NULL) {
return;
}
switch (flag) {
case OHCI_REMOVE_XFER_IFLAST:
if (tw->tw_hctd_head != tw->tw_hctd_tail) {
break;
}
case OHCI_REMOVE_XFER_ALWAYS:
ohci_remove_tw_from_timeout_list(ohcip, tw);
if ((ohcip->ohci_timeout_list == NULL) &&
(ohcip->ohci_timer_id)) {
timer_id = ohcip->ohci_timer_id;
ohcip->ohci_timer_id = 0;
mutex_exit(&ohcip->ohci_int_mutex);
(void) untimeout(timer_id);
mutex_enter(&ohcip->ohci_int_mutex);
}
break;
default:
break;
}
}
static void
ohci_xfer_timeout_handler(void *arg)
{
ohci_state_t *ohcip = (ohci_state_t *)arg;
ohci_trans_wrapper_t *exp_xfer_list_head = NULL;
ohci_trans_wrapper_t *exp_xfer_list_tail = NULL;
ohci_trans_wrapper_t *tw, *next;
ohci_td_t *td;
usb_flags_t flags;
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_xfer_timeout_handler: ohcip = 0x%p", (void *)ohcip);
mutex_enter(&ohcip->ohci_int_mutex);
flags = OHCI_FLAGS_NOSLEEP | OHCI_FLAGS_DMA_SYNC;
if (ohcip->ohci_timer_id) {
ohcip->ohci_timer_id = 0;
} else {
mutex_exit(&ohcip->ohci_int_mutex);
return;
}
tw = ohcip->ohci_timeout_list;
while (tw) {
next = tw->tw_timeout_next;
tw->tw_timeout--;
if (tw->tw_timeout == 1) {
ohci_modify_sKip_bit(ohcip,
tw->tw_pipe_private, SET_sKip, flags);
flags &= ~OHCI_FLAGS_DMA_SYNC;
}
if (tw->tw_timeout == 0) {
ohci_remove_tw_from_timeout_list(ohcip, tw);
if (exp_xfer_list_head) {
exp_xfer_list_tail->tw_timeout_next = tw;
} else {
exp_xfer_list_head = tw;
}
exp_xfer_list_tail = tw;
tw->tw_timeout_next = NULL;
}
tw = next;
}
tw = exp_xfer_list_head;
if (tw && (flags & OHCI_FLAGS_DMA_SYNC)) {
Sync_ED_TD_Pool(ohcip);
}
while (tw) {
next = tw->tw_timeout_next;
td = tw->tw_hctd_head;
while (td) {
Set_TD(td->hctd_state, HC_TD_TIMEOUT);
td = ohci_td_iommu_to_cpu(ohcip,
Get_TD(td->hctd_tw_next_td));
}
ohci_handle_error(ohcip, tw->tw_hctd_head, USB_CR_TIMEOUT);
tw = next;
}
ohci_start_timer(ohcip);
mutex_exit(&ohcip->ohci_int_mutex);
}
static void
ohci_remove_tw_from_timeout_list(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw)
{
ohci_trans_wrapper_t *prev, *next;
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_remove_tw_from_timeout_list: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (ohcip->ohci_timeout_list == tw) {
ohcip->ohci_timeout_list = tw->tw_timeout_next;
} else {
prev = ohcip->ohci_timeout_list;
next = prev->tw_timeout_next;
while (next && (next != tw)) {
prev = next;
next = next->tw_timeout_next;
}
if (next == tw) {
prev->tw_timeout_next = next->tw_timeout_next;
}
}
tw->tw_timeout_next = NULL;
}
static void
ohci_start_timer(ohci_state_t *ohcip)
{
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_start_timer: ohcip = 0x%p", (void *)ohcip);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if ((!ohcip->ohci_timer_id) && (ohcip->ohci_timeout_list)) {
ohcip->ohci_timer_id = timeout(ohci_xfer_timeout_handler,
(void *)ohcip, drv_usectohz(1000000));
}
}
void
ohci_deallocate_tw_resources(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw)
{
ohci_trans_wrapper_t *prev, *next;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_deallocate_tw_resources: tw = 0x%p", (void *)tw);
if (tw->tw_hctd_head) {
ASSERT(tw->tw_hctd_tail != NULL);
return;
}
ASSERT(tw->tw_hctd_tail == NULL);
ohci_free_tw_tds_resources(ohcip, tw);
if (pp->pp_tw_head == tw) {
if (pp->pp_tw_tail == tw) {
pp->pp_tw_head = NULL;
pp->pp_tw_tail = NULL;
} else {
pp->pp_tw_head = tw->tw_next;
}
} else {
prev = pp->pp_tw_head;
next = prev->tw_next;
while (next && (next != tw)) {
prev = next;
next = next->tw_next;
}
if (next == tw) {
prev->tw_next = next->tw_next;
if (pp->pp_tw_tail == tw) {
pp->pp_tw_tail = prev;
}
}
}
ohci_free_tw(ohcip, tw);
}
static void
ohci_free_dma_resources(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
ohci_trans_wrapper_t *head_tw = pp->pp_tw_head;
ohci_trans_wrapper_t *next_tw, *tw;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_free_dma_resources: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
next_tw = head_tw;
while (next_tw) {
tw = next_tw;
next_tw = tw->tw_next;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_free_dma_resources: Free TW = 0x%p", (void *)tw);
ohci_free_tw(ohcip, tw);
}
pp->pp_tw_head = NULL;
pp->pp_tw_tail = NULL;
}
static void
ohci_free_tw(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw)
{
int rval, i;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
"ohci_free_tw: tw = 0x%p", (void *)tw);
ASSERT(tw != NULL);
ASSERT(tw->tw_id != 0);
OHCI_FREE_ID((uint32_t)tw->tw_id);
if (tw->tw_isoc_strtlen > 0) {
ASSERT(tw->tw_isoc_bufs != NULL);
for (i = 0; i < tw->tw_ncookies; i++) {
if (tw->tw_isoc_bufs[i].ncookies > 0) {
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) {
if (tw->tw_ncookies > 0) {
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 (ohci_trans_wrapper_t));
}
static uint_t
ohci_intr(caddr_t arg1, caddr_t arg2)
{
ohci_state_t *ohcip = (ohci_state_t *)arg1;
uint_t intr;
ohci_td_t *done_head = NULL;
ohci_save_intr_sts_t *ohci_intr_sts = &ohcip->ohci_save_intr_sts;
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Interrupt occurred, arg1 0x%p arg2 0x%p",
(void *)arg1, (void *)arg2);
mutex_enter(&ohcip->ohci_int_mutex);
if (ohcip->ohci_hc_soft_state == OHCI_CTLR_SUSPEND_STATE) {
mutex_exit(&ohcip->ohci_int_mutex);
return (DDI_INTR_UNCLAIMED);
}
ohci_intr_sts->ohci_intr_flag = OHCI_INTR_HANDLING;
Set_OpReg(hcr_intr_disable, HCR_INTR_MIE);
ohci_handle_missed_intr(ohcip);
intr = ohci_intr_sts->ohci_curr_intr_sts =
(Get_OpReg(hcr_intr_status) & Get_OpReg(hcr_intr_enable));
if (ohcip->ohci_hccap) {
Sync_HCCA(ohcip);
done_head = ohci_intr_sts->ohci_curr_done_lst =
(ohci_td_t *)(uintptr_t)
(Get_HCCA(ohcip->ohci_hccap->HccaDoneHead) &
HCCA_DONE_HEAD_MASK);
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Done head! 0x%p", (void *)done_head);
}
ohci_do_intrs_stats(ohcip, intr);
if (ohci_check_done_head(ohcip, done_head) == USB_SUCCESS) {
intr |= HCR_INTR_WDH;
} else {
intr &= ~HCR_INTR_WDH;
}
if (!intr) {
ohci_intr_sts->ohci_intr_flag &= ~OHCI_INTR_HANDLING;
Set_OpReg(hcr_intr_enable, HCR_INTR_MIE);
mutex_exit(&ohcip->ohci_int_mutex);
return (DDI_INTR_UNCLAIMED);
}
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"Interrupt status 0x%x", intr);
if (intr & HCR_INTR_FNO) {
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Frame Number Overflow");
ohci_handle_frame_number_overflow(ohcip);
}
if (intr & HCR_INTR_SOF) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Start of Frame");
ohcip->ohci_sof_flag = B_TRUE;
Set_OpReg(hcr_intr_disable, HCR_INTR_SOF);
cv_broadcast(&ohcip->ohci_SOF_cv);
}
if (intr & HCR_INTR_SO) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Schedule overrun");
ohcip->ohci_so_error++;
}
if ((intr & HCR_INTR_WDH) && (done_head)) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Done Head");
if (done_head == (ohci_td_t *)(uintptr_t)
(Get_HCCA(ohcip->ohci_hccap->HccaDoneHead) &
HCCA_DONE_HEAD_MASK)) {
Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, 0);
} else {
intr &= ~HCR_INTR_WDH;
}
ohci_intr_sts->ohci_curr_done_lst = NULL;
ohci_traverse_done_list(ohcip, done_head);
}
if (ohcip->ohci_reclaim_list) {
ohci_handle_endpoint_reclaimation(ohcip);
}
if (intr & HCR_INTR_RD) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Resume Detected");
}
if (intr & HCR_INTR_RHSC) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Root hub status change");
}
if (intr & HCR_INTR_OC) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Change ownership");
}
if (intr & HCR_INTR_UE) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_intr: Unrecoverable error");
ohci_handle_ue(ohcip);
}
Set_OpReg(hcr_intr_status, intr);
ohci_intr_sts->ohci_curr_intr_sts = 0;
ohci_intr_sts->ohci_intr_flag &= ~OHCI_INTR_HANDLING;
Set_OpReg(hcr_intr_enable, HCR_INTR_MIE);
(void) Get_OpReg(hcr_intr_status);
mutex_exit(&ohcip->ohci_int_mutex);
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"Interrupt handling completed");
return (DDI_INTR_CLAIMED);
}
static int
ohci_check_done_head(ohci_state_t *ohcip, ohci_td_t *done_head)
{
uintptr_t lower, upper, headp;
lower = ohcip->ohci_td_pool_cookie.dmac_address;
upper = lower + ohcip->ohci_td_pool_cookie.dmac_size;
headp = (uintptr_t)done_head;
if (headp && !(headp & ~HCCA_DONE_HEAD_MASK) &&
(headp >= lower) && (headp < upper)) {
return (USB_SUCCESS);
} else {
return (USB_FAILURE);
}
}
static void
ohci_handle_missed_intr(ohci_state_t *ohcip)
{
ohci_save_intr_sts_t *ohci_intr_sts =
&ohcip->ohci_save_intr_sts;
ohci_td_t *done_head;
uint_t intr;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (!ohci_intr_sts->ohci_missed_intr_sts) {
return;
}
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_missed_intr: Handle ohci missed interrupts");
ohci_intr_sts->ohci_intr_flag |= OHCI_INTR_CRITICAL;
ohci_intr_sts->ohci_critical_intr_sts |=
ohci_intr_sts->ohci_missed_intr_sts;
if (ohci_intr_sts->ohci_missed_done_lst) {
ohci_intr_sts->ohci_critical_done_lst =
ohci_intr_sts->ohci_missed_done_lst;
}
ohci_intr_sts->ohci_missed_intr_sts = 0;
ohci_intr_sts->ohci_missed_done_lst = NULL;
ohci_intr_sts->ohci_intr_flag &= ~OHCI_INTR_CRITICAL;
intr = ohci_intr_sts->ohci_critical_intr_sts;
done_head = ohci_intr_sts->ohci_critical_done_lst;
if (intr & HCR_INTR_SOF) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_missed_intr: Start of Frame");
cv_broadcast(&ohcip->ohci_SOF_cv);
}
if ((intr & HCR_INTR_WDH) && (done_head)) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_missed_intr: Done Head");
ohci_intr_sts->ohci_critical_done_lst = NULL;
ohci_traverse_done_list(ohcip, done_head);
}
ohci_intr_sts->ohci_critical_intr_sts = 0;
}
static void
ohci_handle_ue(ohci_state_t *ohcip)
{
usb_frame_number_t before_frame_number, after_frame_number;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_ue: Handling of UE interrupt");
before_frame_number = ohci_get_current_frame_number(ohcip);
drv_usecwait(OHCI_TIMEWAIT);
after_frame_number = ohci_get_current_frame_number(ohcip);
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_ue: Before Frm No 0x%llx After Frm No 0x%llx",
(unsigned long long)before_frame_number,
(unsigned long long)after_frame_number);
if (after_frame_number > before_frame_number) {
Set_OpReg(hcr_intr_disable, HCR_INTR_UE);
return;
}
if ((ohci_do_soft_reset(ohcip)) != USB_SUCCESS) {
USB_DPRINTF_L0(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"Unrecoverable USB Hardware Error");
Set_OpReg(hcr_intr_disable, HCR_INTR_UE);
ohcip->ohci_hc_soft_state = OHCI_CTLR_ERROR_STATE;
}
}
void
ohci_handle_frame_number_overflow(ohci_state_t *ohcip)
{
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_frame_number_overflow:");
ohcip->ohci_fno += (0x10000 -
(((Get_HCCA(ohcip->ohci_hccap->HccaFrameNo) &
0xFFFF) ^ ohcip->ohci_fno) & 0x8000));
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_frame_number_overflow:"
"Frame Number Higher Part 0x%llx\n",
(unsigned long long)(ohcip->ohci_fno));
}
static void
ohci_handle_endpoint_reclaimation(ohci_state_t *ohcip)
{
usb_frame_number_t current_frame_number;
usb_frame_number_t endpoint_frame_number;
ohci_ed_t *reclaim_ed;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_endpoint_reclaimation:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
current_frame_number = ohci_get_current_frame_number(ohcip);
while (ohcip->ohci_reclaim_list) {
reclaim_ed = ohcip->ohci_reclaim_list;
endpoint_frame_number = (usb_frame_number_t)(uintptr_t)
(OHCI_LOOKUP_ID(Get_ED(reclaim_ed->hced_reclaim_frame)));
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_endpoint_reclaimation:"
"current frame number 0x%llx endpoint frame number 0x%llx",
(unsigned long long)current_frame_number,
(unsigned long long)endpoint_frame_number);
if (endpoint_frame_number > current_frame_number) {
break;
}
ohcip->ohci_reclaim_list = ohci_ed_iommu_to_cpu(ohcip,
Get_ED(reclaim_ed->hced_reclaim_next));
OHCI_FREE_ID((uint32_t)Get_ED(reclaim_ed->hced_reclaim_frame));
ohci_deallocate_ed(ohcip, reclaim_ed);
}
}
static void
ohci_traverse_done_list(
ohci_state_t *ohcip,
ohci_td_t *head_done_list)
{
uint_t state;
ohci_td_t *td, *old_td;
usb_cr_t error;
ohci_trans_wrapper_t *tw = NULL;
ohci_pipe_private_t *pp = NULL;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_traverse_done_list:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
Sync_ED_TD_Pool(ohcip);
td = ohci_reverse_done_list(ohcip, head_done_list);
while (td) {
state = Get_TD(td->hctd_state);
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_traverse_done_list:\n\t"
"td = 0x%p state = 0x%x", (void *)td, state);
if (state != HC_TD_RECLAIM) {
tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
(uint32_t)Get_TD(td->hctd_trans_wrapper));
ASSERT(tw != NULL);
pp = tw->tw_pipe_private;
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_traverse_done_list: PP = 0x%p TW = 0x%p",
(void *)pp, (void *)tw);
}
if ((state != HC_TD_RECLAIM) && (state != HC_TD_TIMEOUT)) {
error = ohci_parse_error(ohcip, td);
if (error == USB_CR_OK) {
ohci_handle_normal_td(ohcip, td, tw);
} else {
ohci_handle_error(ohcip, td, error);
}
} else {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_traverse_done_list: TD State = %d", state);
}
old_td = td;
td = ohci_td_iommu_to_cpu(ohcip, Get_TD(td->hctd_next_td));
ohci_deallocate_td(ohcip, old_td);
if (state != HC_TD_RECLAIM) {
ASSERT(tw != NULL);
ohci_deallocate_tw_resources(ohcip, pp, tw);
}
}
}
static ohci_td_t *
ohci_reverse_done_list(
ohci_state_t *ohcip,
ohci_td_t *head_done_list)
{
ohci_td_t *cpu_new_tail, *cpu_new_head, *cpu_save;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_reverse_done_list:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(head_done_list != NULL);
cpu_new_tail = cpu_new_head =
ohci_td_iommu_to_cpu(ohcip, (uintptr_t)head_done_list);
if (Get_TD(cpu_new_head->hctd_next_td) == 0) {
return (cpu_new_head);
}
cpu_new_head = (ohci_td_t *)
ohci_td_iommu_to_cpu(ohcip, Get_TD(cpu_new_head->hctd_next_td));
Set_TD(cpu_new_tail->hctd_next_td, NULL);
cpu_save = (ohci_td_t *)
ohci_td_iommu_to_cpu(ohcip, Get_TD(cpu_new_head->hctd_next_td));
while (cpu_save) {
Set_TD(cpu_new_head->hctd_next_td,
ohci_td_cpu_to_iommu(ohcip, cpu_new_tail));
cpu_new_tail = cpu_new_head;
cpu_new_head = cpu_save;
cpu_save = (ohci_td_t *)
ohci_td_iommu_to_cpu(ohcip,
Get_TD(cpu_new_head->hctd_next_td));
}
Set_TD(cpu_new_head->hctd_next_td,
ohci_td_cpu_to_iommu(ohcip, cpu_new_tail));
return (cpu_new_head);
}
static usb_cr_t
ohci_parse_error(
ohci_state_t *ohcip,
ohci_td_t *td)
{
uint_t ctrl;
usb_ep_descr_t *eptd;
ohci_trans_wrapper_t *tw;
ohci_pipe_private_t *pp;
uint_t flag;
usb_cr_t error;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_parse_error:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(td != NULL);
tw = (ohci_trans_wrapper_t *)
OHCI_LOOKUP_ID((uint32_t)Get_TD(td->hctd_trans_wrapper));
ASSERT(tw != NULL);
pp = tw->tw_pipe_private;
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_parse_error: PP 0x%p TW 0x%p", (void *)pp, (void *)tw);
eptd = &pp->pp_pipe_handle->p_ep;
ctrl = (uint_t)Get_TD(td->hctd_ctrl) & (uint32_t)HC_TD_CC;
if ((error = ohci_check_for_error(ohcip, pp, tw, td, ctrl)) !=
USB_CR_OK) {
flag = OHCI_REMOVE_XFER_ALWAYS;
} else {
flag = OHCI_REMOVE_XFER_IFLAST;
}
ohci_stop_xfer_timer(ohcip, tw, flag);
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_ISOCH) {
ohci_parse_isoc_error(ohcip, pp, tw, td);
error = USB_CR_OK;
}
return (error);
}
void
ohci_parse_isoc_error(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td)
{
usb_isoc_req_t *isoc_reqp;
usb_isoc_pkt_descr_t *isoc_pkt_descr;
uint_t toggle = 0, fc, ctrl, psw;
int i;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_parse_isoc_error: td 0x%p", (void *)td);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
fc = ((uint_t)Get_TD(td->hctd_ctrl) &
HC_ITD_FC) >> HC_ITD_FC_SHIFT;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_parse_isoc_error: frame count %d", fc);
isoc_reqp = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
isoc_pkt_descr = isoc_reqp->isoc_pkt_descr;
isoc_pkt_descr += tw->tw_pkt_idx;
for (i = 0; i <= fc; i++) {
psw = Get_TD(td->hctd_offsets[i / 2]);
if (toggle) {
ctrl = psw & HC_ITD_ODD_OFFSET;
toggle = 0;
} else {
ctrl = (psw & HC_ITD_EVEN_OFFSET) <<
HC_ITD_OFFSET_SHIFT;
toggle = 1;
}
isoc_pkt_descr->isoc_pkt_actual_length =
(ctrl >> HC_ITD_OFFSET_SHIFT) & HC_ITD_OFFSET_ADDR;
ctrl = (uint_t)(ctrl & (uint32_t)HC_TD_CC);
isoc_pkt_descr->isoc_pkt_status =
ohci_check_for_error(ohcip, pp, tw, td, ctrl);
if (isoc_pkt_descr->isoc_pkt_status) {
isoc_reqp->isoc_error_count++;
}
isoc_pkt_descr++;
}
tw->tw_pkt_idx = tw->tw_pkt_idx + fc + 1;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_parse_isoc_error: tw_pkt_idx %d", tw->tw_pkt_idx);
}
static usb_cr_t
ohci_check_for_error(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
uint_t ctrl)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
uchar_t ep_attrs = ph->p_ep.bmAttributes;
usb_cr_t error = USB_CR_OK;
usb_req_attrs_t xfer_attrs;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: td = 0x%p ctrl = 0x%x",
(void *)td, ctrl);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
switch (ctrl) {
case HC_TD_CC_NO_E:
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: No Error");
error = USB_CR_OK;
break;
case HC_TD_CC_CRC:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: CRC error");
error = USB_CR_CRC;
break;
case HC_TD_CC_BS:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Bit stuffing");
error = USB_CR_BITSTUFFING;
break;
case HC_TD_CC_DTM:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Data Toggle Mismatch");
error = USB_CR_DATA_TOGGLE_MM;
break;
case HC_TD_CC_STALL:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Stall");
error = USB_CR_STALL;
break;
case HC_TD_CC_DNR:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Device not responding");
error = USB_CR_DEV_NOT_RESP;
break;
case HC_TD_CC_PCF:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: PID check failure");
error = USB_CR_PID_CHECKFAILURE;
break;
case HC_TD_CC_UPID:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Unexpected PID");
error = USB_CR_UNEXP_PID;
break;
case HC_TD_CC_DO:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Data overrrun");
error = USB_CR_DATA_OVERRUN;
break;
case HC_TD_CC_DU:
xfer_attrs = ohci_get_xfer_attrs(ohcip, pp, tw);
if (xfer_attrs & USB_ATTRS_SHORT_XFER_OK) {
error = USB_CR_OK;
if ((ep_attrs & USB_EP_ATTR_MASK) !=
USB_EP_ATTR_ISOCH) {
if (ohci_cleanup_data_underrun(ohcip, pp, tw,
td) == USB_SUCCESS) {
Set_ED(pp->pp_ept->hced_headp,
(Get_ED(pp->pp_ept->hced_headp) &
~HC_EPT_Halt));
} else {
error = USB_CR_UNSPECIFIED_ERR;
}
}
} else {
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Data underrun");
error = USB_CR_DATA_UNDERRUN;
}
break;
case HC_TD_CC_BO:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Buffer overrun");
error = USB_CR_BUFFER_OVERRUN;
break;
case HC_TD_CC_BU:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Buffer underrun");
error = USB_CR_BUFFER_UNDERRUN;
break;
case HC_TD_CC_NA:
default:
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Not accessed");
error = USB_CR_NOT_ACCESSED;
break;
}
if (error) {
uint_t hced_ctrl = Get_ED(pp->pp_ept->hced_ctrl);
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_check_for_error: Error %d Device address %d "
"Endpoint number %d", error, (hced_ctrl & HC_EPT_FUNC),
((hced_ctrl & HC_EPT_EP) >> HC_EPT_EP_SHFT));
}
return (error);
}
static void
ohci_handle_error(
ohci_state_t *ohcip,
ohci_td_t *td,
usb_cr_t error)
{
ohci_trans_wrapper_t *tw;
usba_pipe_handle_data_t *ph;
ohci_pipe_private_t *pp;
mblk_t *mp = NULL;
size_t length = 0;
uchar_t attributes;
usb_intr_req_t *curr_intr_reqp;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_error: error = 0x%x", error);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(td != NULL);
ohci_print_td(ohcip, td);
tw = (ohci_trans_wrapper_t *)
OHCI_LOOKUP_ID((uint32_t)Get_TD(td->hctd_trans_wrapper));
ASSERT(tw != NULL);
pp = tw->tw_pipe_private;
ph = tw->tw_pipe_private->pp_pipe_handle;
attributes = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
if (tw->tw_direction == HC_TD_IN) {
switch (attributes) {
case USB_EP_ATTR_CONTROL:
if (((ph->p_ep.bmAttributes &
USB_EP_ATTR_MASK) ==
USB_EP_ATTR_CONTROL) &&
(Get_TD(td->hctd_ctrl_phase) ==
OHCI_CTRL_SETUP_PHASE)) {
break;
}
case USB_EP_ATTR_BULK:
ohci_sendup_td_message(ohcip, pp, tw, td, error);
return;
case USB_EP_ATTR_INTR:
curr_intr_reqp =
(usb_intr_req_t *)tw->tw_curr_xfer_reqp;
if (curr_intr_reqp->intr_attributes &
USB_ATTRS_ONE_XFER) {
ohci_handle_one_xfer_completion(ohcip, tw);
}
pp->pp_cur_periodic_req_cnt--;
break;
case USB_EP_ATTR_ISOCH:
default:
break;
}
} else {
switch (attributes) {
case USB_EP_ATTR_BULK:
case USB_EP_ATTR_INTR:
if (Get_TD(td->hctd_cbp)) {
usb_opaque_t xfer_reqp = tw->tw_curr_xfer_reqp;
size_t residue;
residue = ohci_get_td_residue(ohcip, td);
length = Get_TD(td->hctd_xfer_offs) +
Get_TD(td->hctd_xfer_len) - residue;
USB_DPRINTF_L2(PRINT_MASK_INTR,
ohcip->ohci_log_hdl,
"ohci_handle_error: requested data %lu "
"sent data %lu", tw->tw_length, length);
if (attributes == USB_EP_ATTR_BULK) {
mp = (mblk_t *)((usb_bulk_req_t *)
(xfer_reqp))->bulk_data;
} else {
mp = (mblk_t *)((usb_intr_req_t *)
(xfer_reqp))->intr_data;
}
mp->b_rptr = mp->b_rptr + length;
}
break;
default:
break;
}
}
ohci_hcdi_callback(ph, tw, error);
ohci_check_for_transfers_completion(ohcip, pp);
}
static int
ohci_cleanup_data_underrun(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td)
{
ohci_td_t *next_td;
ohci_td_t *last_td;
ohci_td_t *temp_td;
uint32_t last_td_addr;
uint_t hced_head;
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_cleanup_data_underrun: td 0x%p, tw 0x%p",
(void *)td, (void *)tw);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(tw->tw_hctd_head == td);
last_td = tw->tw_hctd_tail;
if (td == last_td) {
return (USB_SUCCESS);
}
hced_head = Get_ED(pp->pp_ept->hced_headp);
if (!(hced_head & HC_EPT_Halt)) {
uint_t hced_ctrl = Get_ED(pp->pp_ept->hced_ctrl);
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_cleanup_data_underrun: Unable to clean up a short "
"xfer error. Client might send/receive irrelevant data."
" Device address %d Endpoint number %d",
(hced_ctrl & HC_EPT_FUNC),
((hced_ctrl & HC_EPT_EP) >> HC_EPT_EP_SHFT));
Set_ED(pp->pp_ept->hced_headp, hced_head | HC_EPT_Halt);
return (USB_FAILURE);
}
last_td_addr = Get_TD(last_td->hctd_next_td);
Set_ED(pp->pp_ept->hced_headp,
(last_td_addr & HC_EPT_TD_HEAD) |
(hced_head & ~HC_EPT_TD_HEAD));
tw->tw_hctd_tail = td;
next_td = (ohci_td_t *)ohci_td_iommu_to_cpu(ohcip,
Get_TD(td->hctd_tw_next_td));
Set_TD(td->hctd_tw_next_td, NULL);
while (next_td != NULL) {
tw->tw_num_tds--;
Set_TD(next_td->hctd_trans_wrapper, NULL);
Set_TD(next_td->hctd_state, HC_TD_RECLAIM);
temp_td = next_td;
next_td = (ohci_td_t *)ohci_td_iommu_to_cpu(ohcip,
Get_TD(next_td->hctd_tw_next_td));
ohci_deallocate_td(ohcip, temp_td);
}
ASSERT(tw->tw_num_tds == 1);
return (USB_SUCCESS);
}
static void
ohci_handle_normal_td(
ohci_state_t *ohcip,
ohci_td_t *td,
ohci_trans_wrapper_t *tw)
{
ohci_pipe_private_t *pp;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_normal_td:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(tw != NULL);
pp = tw->tw_pipe_private;
(*tw->tw_handle_td)(ohcip, pp, tw,
td, tw->tw_handle_callback_value);
ohci_check_for_transfers_completion(ohcip, pp);
}
static void
ohci_handle_ctrl_td(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
void *tw_handle_callback_value)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_ctrl_td: pp = 0x%p tw = 0x%p td = 0x%p state = 0x%x",
(void *)pp, (void *)tw, (void *)td, Get_TD(td->hctd_ctrl_phase));
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
tw->tw_num_tds--;
switch (Get_TD(td->hctd_ctrl_phase)) {
case OHCI_CTRL_SETUP_PHASE:
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"Setup complete: pp 0x%p td 0x%p", (void *)pp, (void *)td);
break;
case OHCI_CTRL_DATA_PHASE:
if (Get_TD(td->hctd_cbp)) {
size_t length, residue;
residue = ohci_get_td_residue(ohcip, td);
length = Get_TD(td->hctd_xfer_offs) +
Get_TD(td->hctd_xfer_len) - residue;
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_ctrl_qtd: requested data %lu "
"received data %lu", tw->tw_length, length);
tw->tw_length = length;
}
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"Data complete: pp 0x%p td 0x%p",
(void *)pp, (void *)td);
break;
case OHCI_CTRL_STATUS_PHASE:
if ((tw->tw_length != 0) &&
(tw->tw_direction == HC_TD_IN)) {
ohci_sendup_td_message(ohcip,
pp, tw, td, USB_CR_OK);
} else {
ohci_do_byte_stats(ohcip,
tw->tw_length - OHCI_MAX_TD_BUF_SIZE,
ph->p_ep.bmAttributes,
ph->p_ep.bEndpointAddress);
ohci_hcdi_callback(ph, tw, USB_CR_OK);
}
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"Status complete: pp 0x%p td 0x%p", (void *)pp, (void *)td);
break;
}
}
static void
ohci_handle_bulk_td(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
void *tw_handle_callback_value)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_ep_descr_t *eptd = &ph->p_ep;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_bulk_td:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (--tw->tw_num_tds != 0) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_bulk_td: Number of TDs %d", tw->tw_num_tds);
return;
}
if ((eptd->bEndpointAddress &
USB_EP_DIR_MASK) == USB_EP_DIR_OUT) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_bulk_td: Bulk out pipe");
ohci_do_byte_stats(ohcip, tw->tw_length,
eptd->bmAttributes, eptd->bEndpointAddress);
ohci_hcdi_callback(ph, tw, USB_CR_OK);
return;
}
ohci_sendup_td_message(ohcip, pp, tw, td, USB_CR_OK);
}
static void
ohci_handle_intr_td(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
void *tw_handle_callback_value)
{
usb_intr_req_t *curr_intr_reqp =
(usb_intr_req_t *)tw->tw_curr_xfer_reqp;
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_ep_descr_t *eptd = &ph->p_ep;
usb_req_attrs_t attrs;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_intr_td: pp=0x%p tw=0x%p td=0x%p"
"intr_reqp=0%p data=0x%p", (void *)pp, (void *)tw, (void *)td,
(void *)curr_intr_reqp, (void *)curr_intr_reqp->intr_data);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
attrs = curr_intr_reqp->intr_attributes;
if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_OUT) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_intr_td: Intr out pipe, intr_reqp=0x%p,"
"data=0x%p", (void *)curr_intr_reqp,
(void *)curr_intr_reqp->intr_data);
ohci_do_byte_stats(ohcip, tw->tw_length,
eptd->bmAttributes, eptd->bEndpointAddress);
ohci_hcdi_callback(ph, tw, USB_CR_OK);
return;
}
pp->pp_cur_periodic_req_cnt--;
if (attrs & USB_ATTRS_ONE_XFER) {
ohci_handle_one_xfer_completion(ohcip, tw);
}
ohci_sendup_td_message(ohcip, pp, tw, td, USB_CR_OK);
if (pp->pp_state != OHCI_PIPE_STATE_ACTIVE) {
return;
}
if ((error = ohci_allocate_periodic_in_resource(ohcip, pp, tw, 0)) ==
USB_SUCCESS) {
curr_intr_reqp = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
ASSERT(curr_intr_reqp != NULL);
tw->tw_num_tds = 1;
if (ohci_tw_rebind_cookie(ohcip, pp, tw) != USB_SUCCESS) {
ohci_deallocate_periodic_in_resource(ohcip, pp, tw);
error = USB_FAILURE;
} else if (ohci_allocate_tds_for_tw(ohcip, tw,
tw->tw_num_tds) != USB_SUCCESS) {
ohci_deallocate_periodic_in_resource(ohcip, pp, tw);
error = USB_FAILURE;
}
}
if (error != USB_SUCCESS) {
pp->pp_state = OHCI_PIPE_STATE_STOP_POLLING;
pp->pp_error = USB_CR_NO_RESOURCES;
} else {
ohci_insert_intr_req(ohcip, pp, tw, 0);
pp->pp_cur_periodic_req_cnt++;
ASSERT(pp->pp_cur_periodic_req_cnt ==
pp->pp_max_periodic_req_cnt);
}
}
static void
ohci_handle_one_xfer_completion(
ohci_state_t *ohcip,
ohci_trans_wrapper_t *tw)
{
usba_pipe_handle_data_t *ph = tw->tw_pipe_private->pp_pipe_handle;
ohci_pipe_private_t *pp = tw->tw_pipe_private;
usb_intr_req_t *curr_intr_reqp =
(usb_intr_req_t *)tw->tw_curr_xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_one_xfer_completion: tw = 0x%p", (void *)tw);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(curr_intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER);
pp->pp_state = OHCI_PIPE_STATE_IDLE;
((usb_intr_req_t *)(pp->pp_client_periodic_in_reqp))->
intr_data = ((usb_intr_req_t *)
(tw->tw_curr_xfer_reqp))->intr_data;
((usb_intr_req_t *)tw->tw_curr_xfer_reqp)->intr_data = NULL;
usb_free_intr_req((usb_intr_req_t *)tw-> tw_curr_xfer_reqp);
mutex_enter(&ph->p_mutex);
ph->p_req_count--;
mutex_exit(&ph->p_mutex);
tw->tw_curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
pp->pp_client_periodic_in_reqp = NULL;
}
static void
ohci_handle_isoc_td(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
void *tw_handle_callback_value)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_ep_descr_t *eptd = &ph->p_ep;
usb_isoc_req_t *curr_isoc_reqp =
(usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_isoc_td: pp=0x%p tw=0x%p td=0x%p"
"isoc_reqp=0%p data=0x%p", (void *)pp, (void *)tw, (void *)td,
(void *)curr_isoc_reqp, (void *)curr_isoc_reqp->isoc_data);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (--tw->tw_num_tds != 0) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_isoc_td: Number of TDs %d", tw->tw_num_tds);
return;
}
if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_OUT) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_handle_isoc_td: Isoc out pipe, isoc_reqp=0x%p,"
"data=0x%p", (void *)curr_isoc_reqp,
(void *)curr_isoc_reqp->isoc_data);
ohci_do_byte_stats(ohcip, tw->tw_length,
eptd->bmAttributes, eptd->bEndpointAddress);
ohci_hcdi_callback(ph, tw, USB_CR_OK);
return;
}
pp->pp_cur_periodic_req_cnt--;
ohci_sendup_td_message(ohcip, pp, tw, td, USB_CR_OK);
if (pp->pp_state != OHCI_PIPE_STATE_ACTIVE) {
return;
}
if ((error = ohci_allocate_periodic_in_resource(ohcip, pp, tw, 0)) ==
USB_SUCCESS) {
curr_isoc_reqp = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
ASSERT(curr_isoc_reqp != NULL);
tw->tw_num_tds =
curr_isoc_reqp->isoc_pkts_count / OHCI_ISOC_PKTS_PER_TD;
if (curr_isoc_reqp->isoc_pkts_count % OHCI_ISOC_PKTS_PER_TD) {
tw->tw_num_tds++;
}
if (ohci_tw_rebind_cookie(ohcip, pp, tw) != USB_SUCCESS) {
ohci_deallocate_periodic_in_resource(ohcip, pp, tw);
error = USB_FAILURE;
} else if (ohci_allocate_tds_for_tw(ohcip, tw,
tw->tw_num_tds) != USB_SUCCESS) {
ohci_deallocate_periodic_in_resource(ohcip, pp, tw);
error = USB_FAILURE;
}
}
if (error != USB_SUCCESS ||
ohci_insert_isoc_req(ohcip, pp, tw, 0) != USB_SUCCESS) {
pp->pp_state = OHCI_PIPE_STATE_STOP_POLLING;
pp->pp_error = USB_CR_NO_RESOURCES;
} else {
pp->pp_cur_periodic_req_cnt++;
ASSERT(pp->pp_cur_periodic_req_cnt ==
pp->pp_max_periodic_req_cnt);
}
}
static int
ohci_tw_rebind_cookie(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw)
{
usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
int rval, i;
uint_t ccount;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_tw_rebind_cookie:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH) {
ASSERT(tw->tw_num_tds == tw->tw_ncookies);
for (i = 0; i < tw->tw_num_tds; i++) {
if (tw->tw_isoc_bufs[i].ncookies == 1) {
continue;
}
rval = ddi_dma_unbind_handle(
tw->tw_isoc_bufs[i].dma_handle);
ASSERT(rval == USB_SUCCESS);
tw->tw_isoc_bufs[i].ncookies = 0;
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"rebind dma_handle %d", i);
rval = ddi_dma_addr_bind_handle(
tw->tw_isoc_bufs[i].dma_handle, NULL,
(caddr_t)tw->tw_isoc_bufs[i].buf_addr,
tw->tw_isoc_bufs[i].length,
DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
DDI_DMA_DONTWAIT, NULL,
&tw->tw_isoc_bufs[i].cookie, &ccount);
if ((rval == DDI_DMA_MAPPED) &&
(ccount <= OHCI_DMA_ATTR_TD_SGLLEN)) {
tw->tw_isoc_bufs[i].ncookies = ccount;
} else {
return (USB_NO_RESOURCES);
}
}
} else {
if (tw->tw_cookie_idx != 0) {
rval = ddi_dma_unbind_handle(tw->tw_dmahandle);
ASSERT(rval == DDI_SUCCESS);
tw->tw_ncookies = 0;
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"rebind dma_handle");
rval = ddi_dma_addr_bind_handle(
tw->tw_dmahandle, NULL,
(caddr_t)tw->tw_buf, tw->tw_length,
DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
DDI_DMA_DONTWAIT, NULL,
&tw->tw_cookie, &ccount);
if (rval == DDI_DMA_MAPPED) {
tw->tw_ncookies = ccount;
tw->tw_dma_offs = 0;
tw->tw_cookie_idx = 0;
} else {
return (USB_NO_RESOURCES);
}
}
}
return (USB_SUCCESS);
}
static void
ohci_sendup_td_message(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
ohci_td_t *td,
usb_cr_t error)
{
usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
size_t length = 0, skip_len = 0, residue;
mblk_t *mp;
uchar_t *buf;
usb_opaque_t curr_xfer_reqp = tw->tw_curr_xfer_reqp;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_sendup_td_message:");
ASSERT(tw != NULL);
length = tw->tw_length;
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
if (((usb_ctrl_req_t *)curr_xfer_reqp)->ctrl_wLength) {
tw->tw_length = length = length - OHCI_MAX_TD_BUF_SIZE;
} else {
tw->tw_length = length = length - SETUP_SIZE;
}
skip_len = OHCI_MAX_TD_BUF_SIZE;
if (Get_TD(td->hctd_ctrl_phase) != OHCI_CTRL_DATA_PHASE) {
break;
}
case USB_EP_ATTR_BULK:
case USB_EP_ATTR_INTR:
if (error == USB_CR_DATA_OVERRUN) {
break;
}
if (Get_TD(td->hctd_cbp)) {
residue = ohci_get_td_residue(ohcip, td);
length = Get_TD(td->hctd_xfer_offs) +
Get_TD(td->hctd_xfer_len) - residue - skip_len;
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_sendup_qtd_message: requested data %lu "
"received data %lu", tw->tw_length, length);
}
break;
case USB_EP_ATTR_ISOCH:
default:
break;
}
buf = (uchar_t *)tw->tw_buf + skip_len;
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_sendup_qtd_message: length %lu error %d", length, error);
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
mp = ((usb_ctrl_req_t *)curr_xfer_reqp)->ctrl_data;
break;
case USB_EP_ATTR_BULK:
mp = ((usb_bulk_req_t *)curr_xfer_reqp)->bulk_data;
break;
case USB_EP_ATTR_INTR:
mp = ((usb_intr_req_t *)curr_xfer_reqp)->intr_data;
break;
case USB_EP_ATTR_ISOCH:
mp = ((usb_isoc_req_t *)curr_xfer_reqp)->isoc_data;
break;
}
ASSERT(mp != NULL);
if (length) {
int i;
uchar_t *p = mp->b_rptr;
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_CONTROL) {
ohci_do_byte_stats(ohcip, length,
eptd->bmAttributes, USB_EP_DIR_IN);
} else {
ohci_do_byte_stats(ohcip, length,
eptd->bmAttributes, eptd->bEndpointAddress);
}
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_ISOCH) {
for (i = 0; i < tw->tw_ncookies; i++) {
Sync_IO_Buffer(
tw->tw_isoc_bufs[i].dma_handle,
tw->tw_isoc_bufs[i].length);
ddi_rep_get8(tw->tw_isoc_bufs[i].mem_handle,
p, (uint8_t *)tw->tw_isoc_bufs[i].buf_addr,
tw->tw_isoc_bufs[i].length,
DDI_DEV_AUTOINCR);
p += tw->tw_isoc_bufs[i].length;
}
tw->tw_pkt_idx = 0;
} else {
Sync_IO_Buffer(tw->tw_dmahandle, (skip_len + length));
ddi_rep_get8(tw->tw_accesshandle,
mp->b_rptr, buf, length, DDI_DEV_AUTOINCR);
}
mp->b_wptr = mp->b_wptr + length;
} else {
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_sendup_td_message: Zero length packet");
}
ohci_hcdi_callback(ph, tw, error);
}
size_t
ohci_get_td_residue(
ohci_state_t *ohcip,
ohci_td_t *td)
{
uint32_t buf_addr, end_addr;
size_t residue;
buf_addr = Get_TD(td->hctd_cbp);
end_addr = Get_TD(td->hctd_buf_end);
if ((buf_addr & 0xfffff000) ==
(end_addr & 0xfffff000)) {
residue = end_addr - buf_addr + 1;
} else {
residue = OHCI_MAX_TD_BUF_SIZE -
(buf_addr & 0x00000fff) +
(end_addr & 0x00000fff) + 1;
}
return (residue);
}
ohci_state_t *
ohci_obtain_state(dev_info_t *dip)
{
int instance = ddi_get_instance(dip);
ohci_state_t *state = ddi_get_soft_state(
ohci_statep, instance);
ASSERT(state != NULL);
return (state);
}
int
ohci_state_is_operational(ohci_state_t *ohcip)
{
int val;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
switch (ohcip->ohci_hc_soft_state) {
case OHCI_CTLR_INIT_STATE:
case OHCI_CTLR_SUSPEND_STATE:
val = USB_FAILURE;
break;
case OHCI_CTLR_OPERATIONAL_STATE:
val = USB_SUCCESS;
break;
case OHCI_CTLR_ERROR_STATE:
val = USB_HC_HARDWARE_ERROR;
break;
default:
val = USB_FAILURE;
break;
}
return (val);
}
int
ohci_do_soft_reset(ohci_state_t *ohcip)
{
usb_frame_number_t before_frame_number, after_frame_number;
timeout_id_t xfer_timer_id, rh_timer_id;
ohci_regs_t *ohci_save_regs;
ohci_td_t *done_head;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ohcip->ohci_hc_error++;
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_do_soft_reset:"
"Reset ohci host controller 0x%x", ohcip->ohci_hc_error);
ohci_save_regs = (ohci_regs_t *)
kmem_zalloc(sizeof (ohci_regs_t), KM_NOSLEEP);
if (ohci_save_regs == NULL) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_do_soft_reset: kmem_zalloc failed");
return (USB_FAILURE);
}
ohci_save_regs->hcr_control = Get_OpReg(hcr_control);
ohci_save_regs->hcr_cmd_status = Get_OpReg(hcr_cmd_status);
ohci_save_regs->hcr_intr_enable = Get_OpReg(hcr_intr_enable);
ohci_save_regs->hcr_periodic_strt = Get_OpReg(hcr_periodic_strt);
ohci_save_regs->hcr_frame_interval = Get_OpReg(hcr_frame_interval);
ohci_save_regs->hcr_HCCA = Get_OpReg(hcr_HCCA);
ohci_save_regs->hcr_bulk_head = Get_OpReg(hcr_bulk_head);
ohci_save_regs->hcr_ctrl_head = Get_OpReg(hcr_ctrl_head);
USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_do_soft_reset: Save reg = 0x%p", (void *)ohci_save_regs);
Set_OpReg(hcr_control, (Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
HCR_CONTROL_BLE | HCR_CONTROL_PLE | HCR_CONTROL_IE)));
Set_OpReg(hcr_intr_disable, HCR_INTR_SO |
HCR_INTR_WDH | HCR_INTR_RD | HCR_INTR_UE |
HCR_INTR_FNO | HCR_INTR_SOF | HCR_INTR_MIE);
drv_usecwait(OHCI_TIMEWAIT);
rh_timer_id = ohcip->ohci_root_hub.rh_intr_pipe_timer_id;
if (rh_timer_id) {
ohcip->ohci_root_hub.rh_intr_pipe_timer_id = 0;
ohcip->ohci_root_hub.rh_intr_pipe_state =
OHCI_PIPE_STATE_IDLE;
mutex_exit(&ohcip->ohci_int_mutex);
(void) untimeout(rh_timer_id);
mutex_enter(&ohcip->ohci_int_mutex);
}
xfer_timer_id = ohcip->ohci_timer_id;
if (xfer_timer_id) {
ohcip->ohci_timer_id = 0;
mutex_exit(&ohcip->ohci_int_mutex);
(void) untimeout(xfer_timer_id);
mutex_enter(&ohcip->ohci_int_mutex);
}
done_head = (ohci_td_t *)(uintptr_t)
(Get_HCCA(ohcip->ohci_hccap->HccaDoneHead) & HCCA_DONE_HEAD_MASK);
if (ohci_check_done_head(ohcip, done_head) == USB_SUCCESS) {
Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, 0);
ohci_traverse_done_list(ohcip, done_head);
}
done_head = (ohci_td_t *)(uintptr_t)
(Get_OpReg(hcr_done_head) & HCCA_DONE_HEAD_MASK);
if (ohci_check_done_head(ohcip, done_head) == USB_SUCCESS) {
ohci_traverse_done_list(ohcip, done_head);
}
Set_OpReg(hcr_cmd_status, HCR_STATUS_RESET);
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_do_soft_reset: Reset in progress");
drv_usecwait(OHCI_RESET_TIMEWAIT);
Set_HCCA(ohcip->ohci_hccap->HccaFrameNo, 0x00000000);
Set_OpReg(hcr_periodic_strt, (uint32_t)
ohci_save_regs->hcr_periodic_strt);
Set_OpReg(hcr_frame_interval, (uint32_t)
ohci_save_regs->hcr_frame_interval);
Set_OpReg(hcr_done_head, 0x0);
Set_OpReg(hcr_bulk_curr, 0x0);
Set_OpReg(hcr_bulk_head, (uint32_t)
ohci_save_regs->hcr_bulk_head);
Set_OpReg(hcr_ctrl_curr, 0x0);
Set_OpReg(hcr_ctrl_head, (uint32_t)
ohci_save_regs->hcr_ctrl_head);
Set_OpReg(hcr_periodic_curr, 0x0);
Set_OpReg(hcr_HCCA, (uint32_t)
ohci_save_regs->hcr_HCCA);
Set_OpReg(hcr_intr_status, 0x0);
Set_OpReg(hcr_intr_enable,
HCR_INTR_SO | HCR_INTR_WDH | HCR_INTR_RD | HCR_INTR_UE |
HCR_INTR_FNO | HCR_INTR_SOF | HCR_INTR_MIE);
Set_OpReg(hcr_cmd_status, (HCR_STATUS_CLF | HCR_STATUS_BLF));
Set_OpReg(hcr_control, (uint32_t)
(ohci_save_regs->hcr_control & (~HCR_CONTROL_HCFS)));
kmem_free((void *) ohci_save_regs, sizeof (ohci_regs_t));
Set_OpReg(hcr_control, ((Get_OpReg(hcr_control) &
(~HCR_CONTROL_HCFS)) | HCR_CONTROL_RESUME));
drv_usecwait(OHCI_RESUME_TIMEWAIT);
Set_OpReg(hcr_control, ((Get_OpReg(hcr_control) &
(~HCR_CONTROL_HCFS)) | HCR_CONTROL_OPERAT));
drv_usecwait(OHCI_TIMEWAIT);
before_frame_number = ohci_get_current_frame_number(ohcip);
drv_usecwait(OHCI_TIMEWAIT);
after_frame_number = ohci_get_current_frame_number(ohcip);
USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_do_soft_reset: Before Frm No 0x%llx After Frm No 0x%llx",
(unsigned long long)before_frame_number,
(unsigned long long)after_frame_number);
if (after_frame_number <= before_frame_number) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
"ohci_do_soft_reset: Soft reset failed");
return (USB_FAILURE);
}
if (rh_timer_id) {
ohcip->ohci_root_hub.rh_intr_pipe_timer_id =
timeout(ohci_handle_root_hub_status_change,
(void *)ohcip, drv_usectohz(OHCI_RH_POLL_TIME));
ohcip->ohci_root_hub.
rh_intr_pipe_state = OHCI_PIPE_STATE_ACTIVE;
}
if (xfer_timer_id) {
ohcip->ohci_timer_id = timeout(ohci_xfer_timeout_handler,
(void *)ohcip, drv_usectohz(1000000));
}
return (USB_SUCCESS);
}
usb_frame_number_t
ohci_get_current_frame_number(ohci_state_t *ohcip)
{
usb_frame_number_t usb_frame_number;
usb_frame_number_t ohci_fno, frame_number;
ohci_save_intr_sts_t *ohci_intr_sts =
&ohcip->ohci_save_intr_sts;
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (!(ohci_intr_sts->ohci_intr_flag &
OHCI_INTR_HANDLING)) {
Sync_HCCA(ohcip);
}
ohci_fno = ohcip->ohci_fno;
frame_number = Get_HCCA(ohcip->ohci_hccap->HccaFrameNo);
usb_frame_number = ((frame_number & 0x7FFF) | ohci_fno) +
(((frame_number & 0xFFFF) ^ ohci_fno) & 0x8000);
return (usb_frame_number);
}
static void
ohci_cpr_cleanup(ohci_state_t *ohcip)
{
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ohcip->ohci_fno = 0;
ohcip->ohci_so_error = 0;
Set_HCCA(ohcip->ohci_hccap->HccaFrameNo, 0x00000000);
}
static usb_req_attrs_t
ohci_get_xfer_attrs(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw)
{
usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
usb_req_attrs_t attrs = 0;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_get_xfer_attrs:");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
attrs = ((usb_ctrl_req_t *)
tw->tw_curr_xfer_reqp)->ctrl_attributes;
break;
case USB_EP_ATTR_BULK:
attrs = ((usb_bulk_req_t *)
tw->tw_curr_xfer_reqp)->bulk_attributes;
break;
case USB_EP_ATTR_INTR:
attrs = ((usb_intr_req_t *)
tw->tw_curr_xfer_reqp)->intr_attributes;
break;
case USB_EP_ATTR_ISOCH:
attrs = ((usb_isoc_req_t *)
tw->tw_curr_xfer_reqp)->isoc_attributes;
break;
}
return (attrs);
}
static int
ohci_allocate_periodic_in_resource(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw,
usb_flags_t flags)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
uchar_t ep_attr = ph->p_ep.bmAttributes;
usb_intr_req_t *curr_intr_reqp;
usb_isoc_req_t *curr_isoc_reqp;
usb_opaque_t client_periodic_in_reqp;
size_t length = 0;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_periodic_in_resource:"
"pp = 0x%p tw = 0x%p flags = 0x%x", (void *)pp, (void *)tw, flags);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
ASSERT(tw->tw_curr_xfer_reqp == NULL);
client_periodic_in_reqp = pp->pp_client_periodic_in_reqp;
if ((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) {
if (client_periodic_in_reqp) {
length = ((usb_intr_req_t *)
client_periodic_in_reqp)->intr_len;
curr_intr_reqp = usba_hcdi_dup_intr_req(
ph->p_dip, (usb_intr_req_t *)
client_periodic_in_reqp, length, flags);
} else {
curr_intr_reqp = usb_alloc_intr_req(
ph->p_dip, length, flags);
}
if (curr_intr_reqp == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_allocate_periodic_in_resource: Interrupt "
"request structure allocation failed");
return (USB_NO_RESOURCES);
}
if (client_periodic_in_reqp == NULL) {
curr_intr_reqp->
intr_attributes = USB_ATTRS_SHORT_XFER_OK;
curr_intr_reqp->
intr_len = ph->p_ep.wMaxPacketSize;
} else {
tw->tw_timeout = (curr_intr_reqp->intr_attributes &
USB_ATTRS_ONE_XFER) ?
curr_intr_reqp->intr_timeout: 0;
}
tw->tw_curr_xfer_reqp = (usb_opaque_t)curr_intr_reqp;
tw->tw_length = curr_intr_reqp->intr_len;
} else {
ASSERT(client_periodic_in_reqp != NULL);
curr_isoc_reqp = usba_hcdi_dup_isoc_req(ph->p_dip,
(usb_isoc_req_t *)client_periodic_in_reqp, flags);
if (curr_isoc_reqp == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_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);
pp->pp_state = OHCI_PIPE_STATE_ACTIVE;
return (USB_SUCCESS);
}
static int
ohci_wait_for_sof(ohci_state_t *ohcip)
{
usb_frame_number_t before_frame_number, after_frame_number;
clock_t sof_time_wait;
int rval, sof_wait_count;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_wait_for_sof");
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
rval = ohci_state_is_operational(ohcip);
if (rval != USB_SUCCESS) {
return (rval);
}
sof_time_wait = drv_usectohz(OHCI_MAX_SOF_TIMEWAIT * 1000000);
sof_wait_count = 0;
before_frame_number = ohci_get_current_frame_number(ohcip);
while (sof_wait_count < MAX_SOF_WAIT_COUNT) {
Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
ASSERT(Get_OpReg(hcr_intr_enable) & HCR_INTR_SOF);
rval = cv_reltimedwait(&ohcip->ohci_SOF_cv,
&ohcip->ohci_int_mutex, sof_time_wait, TR_CLOCK_TICK);
after_frame_number = ohci_get_current_frame_number(ohcip);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_wait_for_sof: before 0x%llx, after 0x%llx",
(unsigned long long)before_frame_number,
(unsigned long long)after_frame_number);
if ((rval == -1) &&
(after_frame_number <= before_frame_number)) {
if ((ohci_do_soft_reset(ohcip)) != USB_SUCCESS) {
USB_DPRINTF_L0(PRINT_MASK_LISTS,
ohcip->ohci_log_hdl, "No SOF interrupts");
ohcip->ohci_hc_soft_state =
OHCI_CTLR_ERROR_STATE;
return (USB_FAILURE);
}
after_frame_number = before_frame_number =
ohci_get_current_frame_number(ohcip);
}
ASSERT(after_frame_number >= before_frame_number);
before_frame_number = after_frame_number;
sof_wait_count++;
}
return (USB_SUCCESS);
}
static void
ohci_pipe_cleanup(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
usb_cr_t completion_reason;
uint_t pipe_state = pp->pp_state;
uint_t bit = 0;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_pipe_cleanup: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
switch (pipe_state) {
case OHCI_PIPE_STATE_CLOSE:
if (OHCI_NON_PERIODIC_ENDPOINT(eptd)) {
bit = ((eptd->bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_CONTROL) ?
HCR_CONTROL_CLE: HCR_CONTROL_BLE;
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) & ~(bit)));
(void) ohci_wait_for_sof(ohcip);
break;
}
case OHCI_PIPE_STATE_RESET:
case OHCI_PIPE_STATE_STOP_POLLING:
ohci_modify_sKip_bit(ohcip, pp, SET_sKip,
OHCI_FLAGS_SLEEP | OHCI_FLAGS_DMA_SYNC);
break;
default:
return;
}
ohci_wait_for_transfers_completion(ohcip, pp);
ohci_save_data_toggle(ohcip, ph);
ohci_traverse_tds(ohcip, ph);
ohci_done_list_tds(ohcip, ph);
ohci_handle_outstanding_requests(ohcip, pp);
ohci_free_dma_resources(ohcip, ph);
switch (pipe_state) {
case OHCI_PIPE_STATE_CLOSE:
completion_reason = USB_CR_PIPE_CLOSING;
break;
case OHCI_PIPE_STATE_RESET:
case OHCI_PIPE_STATE_STOP_POLLING:
completion_reason = (pipe_state ==
OHCI_PIPE_STATE_RESET) ?
USB_CR_PIPE_RESET: USB_CR_STOPPED_POLLING;
ohci_restore_data_toggle(ohcip, ph);
ohci_modify_sKip_bit(ohcip, pp,
CLEAR_sKip, OHCI_FLAGS_NOSLEEP);
pp->pp_state = OHCI_PIPE_STATE_IDLE;
break;
}
ASSERT((Get_ED(pp->pp_ept->hced_tailp) & HC_EPT_TD_TAIL) ==
(Get_ED(pp->pp_ept->hced_headp) & HC_EPT_TD_HEAD));
ASSERT((pp->pp_tw_head == NULL) && (pp->pp_tw_tail == NULL));
if ((OHCI_PERIODIC_ENDPOINT(eptd)) &&
((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) ==
USB_EP_DIR_IN)) {
ohci_do_client_periodic_in_req_callback(
ohcip, pp, completion_reason);
}
}
static void
ohci_wait_for_transfers_completion(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
ohci_trans_wrapper_t *head_tw = pp->pp_tw_head;
ohci_trans_wrapper_t *next_tw;
ohci_td_t *tailp, *headp, *nextp;
ohci_td_t *head_td, *next_td;
ohci_ed_t *ept = pp->pp_ept;
int rval;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_wait_for_transfers_completion: pp = 0x%p", (void *)pp);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
headp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
Get_ED(ept->hced_headp) & (uint32_t)HC_EPT_TD_HEAD));
tailp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
Get_ED(ept->hced_tailp) & (uint32_t)HC_EPT_TD_TAIL));
rval = ohci_state_is_operational(ohcip);
if (rval != USB_SUCCESS) {
return;
}
pp->pp_count_done_tds = 0;
next_tw = head_tw;
while (next_tw) {
head_td = (ohci_td_t *)next_tw->tw_hctd_head;
next_td = head_td;
if (head_td) {
while (next_td) {
nextp = headp;
while (nextp != tailp) {
if (nextp == next_td) {
break;
}
nextp = (ohci_td_t *)
(ohci_td_iommu_to_cpu(ohcip,
(Get_TD(nextp->hctd_next_td) &
HC_EPT_TD_TAIL)));
}
if (nextp == tailp) {
pp->pp_count_done_tds++;
}
next_td = ohci_td_iommu_to_cpu(ohcip,
Get_TD(next_td->hctd_tw_next_td));
}
}
next_tw = next_tw->tw_next;
}
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_wait_for_transfers_completion: count_done_tds = 0x%x",
pp->pp_count_done_tds);
if (!pp->pp_count_done_tds) {
return;
}
(void) cv_reltimedwait(&pp->pp_xfer_cmpl_cv, &ohcip->ohci_int_mutex,
drv_usectohz(OHCI_XFER_CMPL_TIMEWAIT * 1000000), TR_CLOCK_TICK);
if (pp->pp_count_done_tds) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_wait_for_transfers_completion: No transfers "
"completion confirmation received for 0x%x requests",
pp->pp_count_done_tds);
}
}
static void
ohci_check_for_transfers_completion(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_check_for_transfers_completion: pp = 0x%p", (void *)pp);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if ((pp->pp_state == OHCI_PIPE_STATE_STOP_POLLING) &&
(pp->pp_error == USB_CR_NO_RESOURCES) &&
(pp->pp_cur_periodic_req_cnt == 0)) {
pp->pp_error = 0;
ohci_do_client_periodic_in_req_callback(
ohcip, pp, USB_CR_NO_RESOURCES);
}
if (pp->pp_count_done_tds) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_check_for_transfers_completion:"
"count_done_tds = 0x%x", pp->pp_count_done_tds);
pp->pp_count_done_tds--;
if (!pp->pp_count_done_tds) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_check_for_transfers_completion:"
"Sent transfers completion event pp = 0x%p",
(void *)pp);
cv_signal(&pp->pp_xfer_cmpl_cv);
}
}
}
static void
ohci_save_data_toggle(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
uint_t data_toggle;
usb_cr_t error = pp->pp_error;
ohci_ed_t *ed = pp->pp_ept;
ohci_td_t *headp, *tailp;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_save_data_toggle: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
pp->pp_error = USB_CR_OK;
if (((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_CONTROL) || ((eptd->bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH)) {
return;
}
headp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
Get_ED(ed->hced_headp) & (uint32_t)HC_EPT_TD_HEAD));
tailp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
Get_ED(ed->hced_tailp) & (uint32_t)HC_EPT_TD_TAIL));
if ((Get_ED(ed->hced_headp) & HC_EPT_Halt) || (headp == tailp)) {
data_toggle = (Get_ED(ed->hced_headp) &
HC_EPT_Carry)? DATA1:DATA0;
} else {
if (Get_TD(headp->hctd_ctrl) & HC_TD_MS_DT) {
data_toggle = (Get_TD(headp->hctd_ctrl) &
HC_TD_DT_1) ? DATA1:DATA0;
} else {
data_toggle = (Get_ED(ed->hced_headp) &
HC_EPT_Carry)? DATA1:DATA0;
}
}
if (error == USB_CR_STALL) {
data_toggle = DATA0;
}
mutex_enter(&ph->p_mutex);
usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
data_toggle);
mutex_exit(&ph->p_mutex);
}
static void
ohci_restore_data_toggle(
ohci_state_t *ohcip,
usba_pipe_handle_data_t *ph)
{
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
uint_t data_toggle = 0;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_restore_data_toggle: ph = 0x%p", (void *)ph);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_CONTROL) || ((eptd->bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH)) {
return;
}
mutex_enter(&ph->p_mutex);
data_toggle = usba_hcdi_get_data_toggle(ph->p_usba_device,
ph->p_ep.bEndpointAddress);
usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
0);
mutex_exit(&ph->p_mutex);
if (data_toggle) {
Set_ED(pp->pp_ept->hced_headp,
Get_ED(pp->pp_ept->hced_headp) | HC_EPT_Carry);
} else {
Set_ED(pp->pp_ept->hced_headp,
Get_ED(pp->pp_ept->hced_headp) & (~HC_EPT_Carry));
}
}
void
ohci_handle_outstanding_requests(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_ep_descr_t *eptd = &ph->p_ep;
ohci_trans_wrapper_t *curr_tw;
ohci_trans_wrapper_t *next_tw;
usb_opaque_t curr_xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_handle_outstanding_requests: pp = 0x%p", (void *)pp);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
next_tw = pp->pp_tw_head;
while (next_tw) {
curr_tw = next_tw;
next_tw = curr_tw->tw_next;
curr_xfer_reqp = curr_tw->tw_curr_xfer_reqp;
if (curr_xfer_reqp) {
if ((OHCI_PERIODIC_ENDPOINT(eptd)) &&
(curr_tw->tw_direction == HC_TD_IN)) {
pp->pp_cur_periodic_req_cnt--;
ohci_deallocate_periodic_in_resource(
ohcip, pp, curr_tw);
} else {
ohci_hcdi_callback(ph,
curr_tw, USB_CR_FLUSHED);
}
}
}
}
static void
ohci_deallocate_periodic_in_resource(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
ohci_trans_wrapper_t *tw)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
uchar_t ep_attr = ph->p_ep.bmAttributes;
usb_opaque_t curr_xfer_reqp;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_deallocate_periodic_in_resource: "
"pp = 0x%p tw = 0x%p", (void *)pp, (void *)tw);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
curr_xfer_reqp = tw->tw_curr_xfer_reqp;
if (curr_xfer_reqp) {
tw->tw_curr_xfer_reqp = NULL;
tw->tw_curr_isoc_pktp = NULL;
mutex_enter(&ph->p_mutex);
ph->p_req_count--;
mutex_exit(&ph->p_mutex);
switch (ep_attr & USB_EP_ATTR_MASK) {
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;
}
}
}
static void
ohci_do_client_periodic_in_req_callback(
ohci_state_t *ohcip,
ohci_pipe_private_t *pp,
usb_cr_t completion_reason)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_do_client_periodic_in_req_callback: "
"pp = 0x%p cc = 0x%x", (void *)pp, completion_reason);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
if (pp->pp_client_periodic_in_reqp) {
ASSERT(pp->pp_cur_periodic_req_cnt == 0);
ohci_hcdi_callback(ph, NULL, completion_reason);
}
}
static void
ohci_hcdi_callback(
usba_pipe_handle_data_t *ph,
ohci_trans_wrapper_t *tw,
usb_cr_t completion_reason)
{
ohci_state_t *ohcip = ohci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
uchar_t attributes = ph->p_ep.bmAttributes &
USB_EP_ATTR_MASK;
ohci_pipe_private_t *pp = (ohci_pipe_private_t *)ph->p_hcd_private;
usb_opaque_t curr_xfer_reqp;
uint_t pipe_state = 0;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_hcdi_callback: ph = 0x%p, tw = 0x%p, cr = 0x%x",
(void *)ph, (void *)tw, completion_reason);
ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
switch (completion_reason) {
case USB_CR_OK:
pipe_state = pp->pp_state;
break;
case USB_CR_NO_RESOURCES:
case USB_CR_NOT_SUPPORTED:
case USB_CR_STOPPED_POLLING:
case USB_CR_PIPE_RESET:
pipe_state = OHCI_PIPE_STATE_IDLE;
break;
case USB_CR_PIPE_CLOSING:
break;
default:
if (attributes != USB_EP_ATTR_ISOCH) {
pipe_state = OHCI_PIPE_STATE_ERROR;
pp->pp_error = completion_reason;
}
break;
}
pp->pp_state = pipe_state;
if (tw && tw->tw_curr_xfer_reqp) {
curr_xfer_reqp = tw->tw_curr_xfer_reqp;
tw->tw_curr_xfer_reqp = NULL;
tw->tw_curr_isoc_pktp = 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(&ohcip->ohci_int_mutex);
usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
mutex_enter(&ohcip->ohci_int_mutex);
}
static void
ohci_create_stats(ohci_state_t *ohcip)
{
char kstatname[KSTAT_STRLEN];
const char *dname = ddi_driver_name(ohcip->ohci_dip);
char *usbtypes[USB_N_COUNT_KSTATS] =
{"ctrl", "isoch", "bulk", "intr"};
uint_t instance = ohcip->ohci_instance;
ohci_intrs_stats_t *isp;
int i;
if (OHCI_INTRS_STATS(ohcip) == NULL) {
(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,intrs",
dname, instance);
OHCI_INTRS_STATS(ohcip) = kstat_create("usba", instance,
kstatname, "usb_interrupts", KSTAT_TYPE_NAMED,
sizeof (ohci_intrs_stats_t) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT);
if (OHCI_INTRS_STATS(ohcip)) {
isp = OHCI_INTRS_STATS_DATA(ohcip);
kstat_named_init(&isp->ohci_hcr_intr_total,
"Interrupts Total", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_not_claimed,
"Not Claimed", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_so,
"Schedule Overruns", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_wdh,
"Writeback Done Head", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_sof,
"Start Of Frame", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_rd,
"Resume Detected", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_ue,
"Unrecoverable Error", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_fno,
"Frame No. Overflow", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_rhsc,
"Root Hub Status Change", KSTAT_DATA_UINT64);
kstat_named_init(&isp->ohci_hcr_intr_oc,
"Change In Ownership", KSTAT_DATA_UINT64);
OHCI_INTRS_STATS(ohcip)->ks_private = ohcip;
OHCI_INTRS_STATS(ohcip)->ks_update = nulldev;
kstat_install(OHCI_INTRS_STATS(ohcip));
}
}
if (OHCI_TOTAL_STATS(ohcip) == NULL) {
(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,total",
dname, instance);
OHCI_TOTAL_STATS(ohcip) = kstat_create("usba", instance,
kstatname, "usb_byte_count", KSTAT_TYPE_IO, 1,
KSTAT_FLAG_PERSISTENT);
if (OHCI_TOTAL_STATS(ohcip)) {
kstat_install(OHCI_TOTAL_STATS(ohcip));
}
}
for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
if (ohcip->ohci_count_stats[i] == NULL) {
(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,%s",
dname, instance, usbtypes[i]);
ohcip->ohci_count_stats[i] = kstat_create("usba",
instance, kstatname, "usb_byte_count",
KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
if (ohcip->ohci_count_stats[i]) {
kstat_install(ohcip->ohci_count_stats[i]);
}
}
}
}
static void
ohci_destroy_stats(ohci_state_t *ohcip)
{
int i;
if (OHCI_INTRS_STATS(ohcip)) {
kstat_delete(OHCI_INTRS_STATS(ohcip));
OHCI_INTRS_STATS(ohcip) = NULL;
}
if (OHCI_TOTAL_STATS(ohcip)) {
kstat_delete(OHCI_TOTAL_STATS(ohcip));
OHCI_TOTAL_STATS(ohcip) = NULL;
}
for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
if (ohcip->ohci_count_stats[i]) {
kstat_delete(ohcip->ohci_count_stats[i]);
ohcip->ohci_count_stats[i] = NULL;
}
}
}
static void
ohci_do_intrs_stats(
ohci_state_t *ohcip,
int val)
{
if (OHCI_INTRS_STATS(ohcip)) {
OHCI_INTRS_STATS_DATA(ohcip)->ohci_hcr_intr_total.value.ui64++;
switch (val) {
case HCR_INTR_SO:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_so.value.ui64++;
break;
case HCR_INTR_WDH:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_wdh.value.ui64++;
break;
case HCR_INTR_SOF:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_sof.value.ui64++;
break;
case HCR_INTR_RD:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_rd.value.ui64++;
break;
case HCR_INTR_UE:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_ue.value.ui64++;
break;
case HCR_INTR_FNO:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_fno.value.ui64++;
break;
case HCR_INTR_RHSC:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_rhsc.value.ui64++;
break;
case HCR_INTR_OC:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_oc.value.ui64++;
break;
default:
OHCI_INTRS_STATS_DATA(ohcip)->
ohci_hcr_intr_not_claimed.value.ui64++;
break;
}
}
}
static void
ohci_do_byte_stats(ohci_state_t *ohcip, 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;
if (dir == USB_EP_DIR_IN) {
OHCI_TOTAL_STATS_DATA(ohcip)->reads++;
OHCI_TOTAL_STATS_DATA(ohcip)->nread += len;
switch (type) {
case USB_EP_ATTR_CONTROL:
OHCI_CTRL_STATS(ohcip)->reads++;
OHCI_CTRL_STATS(ohcip)->nread += len;
break;
case USB_EP_ATTR_BULK:
OHCI_BULK_STATS(ohcip)->reads++;
OHCI_BULK_STATS(ohcip)->nread += len;
break;
case USB_EP_ATTR_INTR:
OHCI_INTR_STATS(ohcip)->reads++;
OHCI_INTR_STATS(ohcip)->nread += len;
break;
case USB_EP_ATTR_ISOCH:
OHCI_ISOC_STATS(ohcip)->reads++;
OHCI_ISOC_STATS(ohcip)->nread += len;
break;
}
} else if (dir == USB_EP_DIR_OUT) {
OHCI_TOTAL_STATS_DATA(ohcip)->writes++;
OHCI_TOTAL_STATS_DATA(ohcip)->nwritten += len;
switch (type) {
case USB_EP_ATTR_CONTROL:
OHCI_CTRL_STATS(ohcip)->writes++;
OHCI_CTRL_STATS(ohcip)->nwritten += len;
break;
case USB_EP_ATTR_BULK:
OHCI_BULK_STATS(ohcip)->writes++;
OHCI_BULK_STATS(ohcip)->nwritten += len;
break;
case USB_EP_ATTR_INTR:
OHCI_INTR_STATS(ohcip)->writes++;
OHCI_INTR_STATS(ohcip)->nwritten += len;
break;
case USB_EP_ATTR_ISOCH:
OHCI_ISOC_STATS(ohcip)->writes++;
OHCI_ISOC_STATS(ohcip)->nwritten += len;
break;
}
}
}
static void
ohci_print_op_regs(ohci_state_t *ohcip)
{
uint_t i;
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\n\tOHCI%d Operational Registers\n",
ddi_get_instance(ohcip->ohci_dip));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_revision: 0x%x \t\thcr_control: 0x%x",
Get_OpReg(hcr_revision), Get_OpReg(hcr_control));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_cmd_status: 0x%x \t\thcr_intr_enable: 0x%x",
Get_OpReg(hcr_cmd_status), Get_OpReg(hcr_intr_enable));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_intr_disable: 0x%x \thcr_HCCA: 0x%x",
Get_OpReg(hcr_intr_disable), Get_OpReg(hcr_HCCA));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_periodic_curr: 0x%x \t\thcr_ctrl_head: 0x%x",
Get_OpReg(hcr_periodic_curr), Get_OpReg(hcr_ctrl_head));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_ctrl_curr: 0x%x \t\thcr_bulk_head: 0x%x",
Get_OpReg(hcr_ctrl_curr), Get_OpReg(hcr_bulk_head));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_bulk_curr: 0x%x \t\thcr_done_head: 0x%x",
Get_OpReg(hcr_bulk_curr), Get_OpReg(hcr_done_head));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_frame_interval: 0x%x "
"\thcr_frame_remaining: 0x%x", Get_OpReg(hcr_frame_interval),
Get_OpReg(hcr_frame_remaining));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_frame_number: 0x%x \thcr_periodic_strt: 0x%x",
Get_OpReg(hcr_frame_number), Get_OpReg(hcr_periodic_strt));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_transfer_ls: 0x%x \t\thcr_rh_descriptorA: 0x%x",
Get_OpReg(hcr_transfer_ls), Get_OpReg(hcr_rh_descriptorA));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_rh_descriptorB: 0x%x \thcr_rh_status: 0x%x",
Get_OpReg(hcr_rh_descriptorB), Get_OpReg(hcr_rh_status));
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\tRoot hub port status");
for (i = 0; i < (Get_OpReg(hcr_rh_descriptorA) & HCR_RHA_NDP); i++) {
USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
"\thcr_rh_portstatus 0x%x: 0x%x ", i,
Get_OpReg(hcr_rh_portstatus[i]));
}
}
static void
ohci_print_ed(
ohci_state_t *ohcip,
ohci_ed_t *ed)
{
uint_t ctrl = Get_ED(ed->hced_ctrl);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_print_ed: ed = 0x%p", (void *)ed);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\thced_ctrl: 0x%x %s", ctrl,
((Get_ED(ed->hced_headp) & HC_EPT_Halt) ? "halted": ""));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\ttoggle carry: 0x%x", Get_ED(ed->hced_headp) & HC_EPT_Carry);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tctrl: 0x%x", Get_ED(ed->hced_ctrl));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\ttailp: 0x%x", Get_ED(ed->hced_tailp));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\theadp: 0x%x", Get_ED(ed->hced_headp));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tnext: 0x%x", Get_ED(ed->hced_next));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tprev: 0x%x", Get_ED(ed->hced_prev));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tnode: 0x%x", Get_ED(ed->hced_node));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\treclaim_next: 0x%x", Get_ED(ed->hced_reclaim_next));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\treclaim_frame: 0x%x", Get_ED(ed->hced_reclaim_frame));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tstate: 0x%x", Get_ED(ed->hced_state));
}
static void
ohci_print_td(
ohci_state_t *ohcip,
ohci_td_t *td)
{
uint_t i;
uint_t ctrl = Get_TD(td->hctd_ctrl);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"ohci_print_td: td = 0x%p", (void *)td);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tPID: 0x%x ", ctrl & HC_TD_PID);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tDelay Intr: 0x%x ", ctrl & HC_TD_DI);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tData Toggle: 0x%x ", ctrl & HC_TD_DT);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tError Count: 0x%x ", ctrl & HC_TD_EC);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tctrl: 0x%x ", Get_TD(td->hctd_ctrl));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tcbp: 0x%x ", Get_TD(td->hctd_cbp));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tnext_td: 0x%x ", Get_TD(td->hctd_next_td));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tbuf_end: 0x%x ", Get_TD(td->hctd_buf_end));
for (i = 0; i < 4; i++) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\toffset[%d]: 0x%x ", i, Get_TD(td->hctd_offsets[i]));
}
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\ttrans_wrapper: 0x%x ", Get_TD(td->hctd_trans_wrapper));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tstate: 0x%x ", Get_TD(td->hctd_state));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\ttw_next_td: 0x%x ", Get_TD(td->hctd_tw_next_td));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
"\tctrl_phase: 0x%x ", Get_TD(td->hctd_ctrl_phase));
}
#ifdef __sparc
int
ohci_quiesce(dev_info_t *dip)
{
return (ddi_quiesce_not_supported(dip));
}
#else
int
ohci_quiesce(dev_info_t *dip)
{
ohci_state_t *ohcip = ohci_obtain_state(dip);
if (ohcip == NULL)
return (DDI_FAILURE);
#ifndef lint
_NOTE(NO_COMPETING_THREADS_NOW);
#endif
if (ohcip->ohci_flags & OHCI_INTR) {
Set_OpReg(hcr_control,
(Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
HCR_CONTROL_BLE | HCR_CONTROL_PLE | HCR_CONTROL_IE)));
Set_OpReg(hcr_intr_disable,
(HCR_INTR_SO | HCR_INTR_WDH | HCR_INTR_RD | HCR_INTR_UE));
Set_OpReg(hcr_intr_disable, (HCR_INTR_MIE | HCR_INTR_SOF));
Set_OpReg(hcr_control, ((Get_OpReg(hcr_control) &
(~HCR_CONTROL_HCFS)) | HCR_CONTROL_RESET));
if (ohcip->ohci_vendor_id == PCI_ULI1575_VENID &&
ohcip->ohci_device_id == PCI_ULI1575_DEVID) {
Set_OpReg(hcr_control, HCR_CONTROL_DEFAULT);
Set_OpReg(hcr_intr_enable, HCR_INT_ENABLE_DEFAULT);
Set_OpReg(hcr_HCCA, HCR_HCCA_DEFAULT);
Set_OpReg(hcr_ctrl_head, HCR_CONTROL_HEAD_ED_DEFAULT);
Set_OpReg(hcr_bulk_head, HCR_BULK_HEAD_ED_DEFAULT);
Set_OpReg(hcr_frame_interval,
HCR_FRAME_INTERVAL_DEFAULT);
Set_OpReg(hcr_periodic_strt,
HCR_PERIODIC_START_DEFAULT);
}
ohcip->ohci_hc_soft_state = OHCI_CTLR_SUSPEND_STATE;
}
if (ohcip->ohci_regs_handle) {
Set_OpReg(hcr_cmd_status, HCR_STATUS_RESET);
}
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return (DDI_SUCCESS);
}
#endif