#include <sys/usb/hcd/ehci/ehcid.h>
#include <sys/usb/hcd/ehci/ehci_xfer.h>
#include <sys/usb/hcd/ehci/ehci_intr.h>
#include <sys/usb/hcd/ehci/ehci_util.h>
#include <sys/usb/hcd/ehci/ehci_isoch.h>
void *ehci_statep;
#define EHCI_INSTS 1
uint_t ehci_errmask = (uint_t)PRINT_MASK_ALL;
uint_t ehci_errlevel = USB_LOG_L2;
uint_t ehci_instance_debug = (uint_t)-1;
int force_ehci_off = 1;
uint_t ehci_vt62x2_workaround = EHCI_VIA_WORKAROUNDS;
static int ehci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int ehci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int ehci_reset(dev_info_t *dip, ddi_reset_cmd_t cmd);
static int ehci_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
void *arg, void **result);
static int ehci_open(dev_t *devp, int flags, int otyp, cred_t *credp);
static int ehci_close(dev_t dev, int flag, int otyp, cred_t *credp);
static int ehci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp);
int usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level);
static int ehci_quiesce(dev_info_t *dip);
static struct cb_ops ehci_cb_ops = {
ehci_open,
ehci_close,
nodev,
nodev,
nodev,
nodev,
nodev,
ehci_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_NEW | D_MP | D_HOTPLUG
};
static struct dev_ops ehci_ops = {
DEVO_REV,
0,
ehci_info,
nulldev,
nulldev,
ehci_attach,
ehci_detach,
ehci_reset,
&ehci_cb_ops,
&usba_hubdi_busops,
usba_hubdi_root_hub_power,
ehci_quiesce
};
static struct modldrv modldrv = {
&mod_driverops,
"USB EHCI Driver",
&ehci_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&ehci_statep, sizeof (ehci_state_t),
EHCI_INSTS)) != 0) {
return (error);
}
if ((error = mod_install(&modlinkage)) != 0) {
ddi_soft_state_fini(&ehci_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(&ehci_statep);
}
return (error);
}
static int
ehci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
ehci_state_t *ehcip = NULL;
usba_hcdi_register_args_t hcdi_args;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
ehcip = ehci_obtain_state(dip);
return (ehci_cpr_resume(ehcip));
default:
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
if (ddi_soft_state_zalloc(ehci_statep, instance) != 0) {
return (DDI_FAILURE);
}
ehcip = ddi_get_soft_state(ehci_statep, instance);
if (ehcip == NULL) {
return (DDI_FAILURE);
}
ehcip->ehci_flags = EHCI_ATTACH;
ehcip->ehci_log_hdl = usb_alloc_log_hdl(dip, "ehci", &ehci_errlevel,
&ehci_errmask, &ehci_instance_debug, 0);
ehcip->ehci_flags |= EHCI_ZALLOC;
ehcip->ehci_hc_soft_state = EHCI_CTLR_INIT_STATE;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
"ehcip = 0x%p", (void *)ehcip);
ehcip->ehci_dip = dip;
ehcip->ehci_instance = instance;
if (ehci_map_regs(ehcip) != DDI_SUCCESS) {
(void) ehci_cleanup(ehcip);
return (DDI_FAILURE);
}
ehcip->ehci_vendor_id = pci_config_get16(
ehcip->ehci_config_handle, PCI_CONF_VENID);
ehcip->ehci_device_id = pci_config_get16(
ehcip->ehci_config_handle, PCI_CONF_DEVID);
ehcip->ehci_rev_id = pci_config_get8(
ehcip->ehci_config_handle, PCI_CONF_REVID);
ehci_set_dma_attributes(ehcip);
ehci_create_stats(ehcip);
if (ehci_allocate_pools(ehcip) != DDI_SUCCESS) {
(void) ehci_cleanup(ehcip);
return (DDI_FAILURE);
}
if (ehci_isoc_init(ehcip) != DDI_SUCCESS) {
(void) ehci_cleanup(ehcip);
return (DDI_FAILURE);
}
if (ehci_register_intrs_and_init_mutex(ehcip) != DDI_SUCCESS) {
(void) ehci_cleanup(ehcip);
return (DDI_FAILURE);
}
mutex_enter(&ehcip->ehci_int_mutex);
if (ehci_init_ctlr(ehcip, EHCI_NORMAL_INITIALIZATION) != DDI_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
(void) ehci_cleanup(ehcip);
return (DDI_FAILURE);
}
ehcip->ehci_hcdi_ops = ehci_alloc_hcdi_ops(ehcip);
mutex_exit(&ehcip->ehci_int_mutex);
hcdi_args.usba_hcdi_register_version = HCDI_REGISTER_VERSION;
hcdi_args.usba_hcdi_register_dip = dip;
hcdi_args.usba_hcdi_register_ops = ehcip->ehci_hcdi_ops;
hcdi_args.usba_hcdi_register_dma_attr = &ehcip->ehci_dma_attr;
hcdi_args.usba_hcdi_register_iblock_cookie =
(ddi_iblock_cookie_t)(uintptr_t)ehcip->ehci_intr_pri;
if (usba_hcdi_register(&hcdi_args, 0) != DDI_SUCCESS) {
(void) ehci_cleanup(ehcip);
return (DDI_FAILURE);
}
ehcip->ehci_flags |= EHCI_USBAREG;
mutex_enter(&ehcip->ehci_int_mutex);
if ((ehci_init_root_hub(ehcip)) != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
(void) ehci_cleanup(ehcip);
return (DDI_FAILURE);
}
mutex_exit(&ehcip->ehci_int_mutex);
if (ehci_load_root_hub_driver(ehcip) != USB_SUCCESS) {
(void) ehci_cleanup(ehcip);
return (DDI_FAILURE);
}
ehcip->ehci_flags |= EHCI_RHREG;
ddi_report_dev(dip);
mutex_enter(&ehcip->ehci_int_mutex);
ehcip->ehci_flags &= ~EHCI_ATTACH;
ehci_print_caps(ehcip);
ehci_print_regs(ehcip);
(void) pci_report_pmcap(dip, PCI_PM_IDLESPEED, (void *)4000);
mutex_exit(&ehcip->ehci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
"ehci_attach: dip = 0x%p done", (void *)dip);
return (DDI_SUCCESS);
}
int
ehci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
ehci_state_t *ehcip = ehci_obtain_state(dip);
USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl, "ehci_detach:");
switch (cmd) {
case DDI_DETACH:
return (ehci_cleanup(ehcip));
case DDI_SUSPEND:
return (ehci_cpr_suspend(ehcip));
default:
return (DDI_FAILURE);
}
}
static int
ehci_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
{
#if defined(__sparc)
return (DDI_SUCCESS);
#else
ehci_state_t *ehcip = ehci_obtain_state(dip);
mutex_enter(&ehcip->ehci_int_mutex);
if (!(Get_OpReg(ehci_status) & EHCI_STS_HOST_CTRL_HALTED)) {
Set_OpReg(ehci_command,
Get_OpReg(ehci_command) & ~EHCI_CMD_HOST_CTRL_RUN);
drv_usecwait(EHCI_RESET_TIMEWAIT);
}
Set_OpReg(ehci_command,
Get_OpReg(ehci_command) | EHCI_CMD_HOST_CTRL_RESET);
mutex_exit(&ehcip->ehci_int_mutex);
return (DDI_SUCCESS);
#endif
}
static int
ehci_quiesce(dev_info_t *dip)
{
ehci_state_t *ehcip = ehci_obtain_state(dip);
if (ehcip == NULL)
return (DDI_FAILURE);
#ifndef lint
_NOTE(NO_COMPETING_THREADS_NOW);
#endif
if (!(Get_OpReg(ehci_status) & EHCI_STS_HOST_CTRL_HALTED)) {
Set_OpReg(ehci_command,
Get_OpReg(ehci_command) & ~EHCI_CMD_HOST_CTRL_RUN);
drv_usecwait(EHCI_RESET_TIMEWAIT);
}
Set_OpReg(ehci_command,
Get_OpReg(ehci_command) | EHCI_CMD_HOST_CTRL_RESET);
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return (DDI_SUCCESS);
}
static int
ehci_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
dev_t dev;
ehci_state_t *ehcip;
int instance;
int error = DDI_FAILURE;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
dev = (dev_t)arg;
instance = EHCI_UNIT(dev);
ehcip = ddi_get_soft_state(ehci_statep, instance);
if (ehcip != NULL) {
*result = (void *)ehcip->ehci_dip;
if (*result != NULL) {
error = DDI_SUCCESS;
}
} else {
*result = NULL;
}
break;
case DDI_INFO_DEVT2INSTANCE:
dev = (dev_t)arg;
instance = EHCI_UNIT(dev);
*result = (void *)(uintptr_t)instance;
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
static dev_info_t *
ehci_get_dip(dev_t dev)
{
int instance = EHCI_UNIT(dev);
ehci_state_t *ehcip = ddi_get_soft_state(ehci_statep, instance);
if (ehcip) {
return (ehcip->ehci_dip);
} else {
return (NULL);
}
}
static int
ehci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
dev_info_t *dip = ehci_get_dip(*devp);
return (usba_hubdi_open(dip, devp, flags, otyp, credp));
}
static int
ehci_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
dev_info_t *dip = ehci_get_dip(dev);
return (usba_hubdi_close(dip, dev, flag, otyp, credp));
}
static int
ehci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
dev_info_t *dip = ehci_get_dip(dev);
return (usba_hubdi_ioctl(dip,
dev, cmd, arg, mode, credp, rvalp));
}
uint_t
ehci_intr(caddr_t arg1, caddr_t arg2)
{
uint_t intr;
ehci_state_t *ehcip = (void *)arg1;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_intr: Interrupt occurred, arg1 0x%p arg2 0x%p",
(void *)arg1, (void *)arg2);
mutex_enter(&ehcip->ehci_int_mutex);
if (ehcip->ehci_hc_soft_state == EHCI_CTLR_SUSPEND_STATE) {
mutex_exit(&ehcip->ehci_int_mutex);
return (DDI_INTR_UNCLAIMED);
}
intr = (Get_OpReg(ehci_status) & Get_OpReg(ehci_interrupt));
ehci_do_intrs_stats(ehcip, intr);
if (!intr) {
mutex_exit(&ehcip->ehci_int_mutex);
return (DDI_INTR_UNCLAIMED);
}
Set_OpReg(ehci_status, intr);
if (ehcip->ehci_hc_soft_state == EHCI_CTLR_ERROR_STATE) {
mutex_exit(&ehcip->ehci_int_mutex);
return (DDI_INTR_CLAIMED);
}
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"Interrupt status 0x%x", intr);
if (ehcip->ehci_flags & EHCI_CV_INTR) {
ehcip->ehci_flags &= ~EHCI_CV_INTR;
cv_broadcast(&ehcip->ehci_async_schedule_advance_cv);
}
if (intr & EHCI_INTR_FRAME_LIST_ROLLOVER) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_intr: Frame List Rollover");
ehci_handle_frame_list_rollover(ehcip);
if ((ehcip->ehci_vendor_id == PCI_VENDOR_VIA) &&
(ehci_vt62x2_workaround & EHCI_VIA_LOST_INTERRUPTS)) {
ehcip->ehci_missed_intr_sts |= EHCI_INTR_USB;
}
}
if (intr & EHCI_INTR_ASYNC_ADVANCE) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_intr: Asynchronous Schedule Advance Notification");
Set_OpReg(ehci_interrupt,
(Get_OpReg(ehci_interrupt) & ~EHCI_INTR_ASYNC_ADVANCE));
cv_broadcast(&ehcip->ehci_async_schedule_advance_cv);
}
ehci_traverse_active_isoc_list(ehcip);
if ((intr & EHCI_INTR_USB) || (intr & EHCI_INTR_USB_ERROR) ||
(ehcip->ehci_missed_intr_sts & EHCI_INTR_USB) ||
(ehcip->ehci_missed_intr_sts & EHCI_INTR_USB_ERROR)) {
USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_intr: USB Transaction Completion Notification");
if (ehcip->ehci_missed_intr_sts) {
ehcip->ehci_missed_intr_sts = 0;
}
ehci_traverse_active_qtd_list(ehcip);
}
if (ehcip->ehci_reclaim_list) {
ehci_handle_endpoint_reclaimation(ehcip);
}
if (intr & EHCI_INTR_HOST_SYSTEM_ERROR) {
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_intr: Unrecoverable error");
ehci_handle_ue(ehcip);
}
(void) Get_OpReg(ehci_status);
mutex_exit(&ehcip->ehci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"Interrupt handling completed");
return (DDI_INTR_CLAIMED);
}
int
ehci_hcdi_pipe_open(
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ehci_state_t *ehcip = ehci_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;
uchar_t smask = 0;
uchar_t cmask = 0;
uint_t pnode = 0;
ehci_pipe_private_t *pp;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_open: addr = 0x%x, ep%d",
ph->p_usba_device->usb_addr,
epdt->bEndpointAddress & USB_EP_NUM_MASK);
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
mutex_exit(&ehcip->ehci_int_mutex);
if (rval != USB_SUCCESS) {
return (rval);
}
if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
mutex_enter(&ehcip->ehci_int_mutex);
error = ehci_handle_root_hub_pipe_open(ph, flags);
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
if (ph->p_hcd_private) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_open: Pipe is already opened");
return (USB_FAILURE);
}
if (EHCI_PERIODIC_ENDPOINT(epdt)) {
mutex_enter(&ehcip->ehci_int_mutex);
mutex_enter(&ph->p_mutex);
error = ehci_allocate_bandwidth(ehcip,
ph, &pnode, &smask, &cmask);
if (error != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_open: Bandwidth allocation failed");
mutex_exit(&ph->p_mutex);
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
mutex_exit(&ph->p_mutex);
mutex_exit(&ehcip->ehci_int_mutex);
}
pp = kmem_zalloc(sizeof (ehci_pipe_private_t), kmflag);
if (pp == NULL) {
mutex_enter(&ehcip->ehci_int_mutex);
if (EHCI_PERIODIC_ENDPOINT(epdt)) {
mutex_enter(&ph->p_mutex);
ehci_deallocate_bandwidth(ehcip,
ph, pnode, smask, cmask);
mutex_exit(&ph->p_mutex);
}
mutex_exit(&ehcip->ehci_int_mutex);
return (USB_NO_RESOURCES);
}
mutex_enter(&ehcip->ehci_int_mutex);
pp->pp_pnode = pnode;
pp->pp_smask = smask;
pp->pp_cmask = cmask;
cv_init(&pp->pp_xfer_cmpl_cv, NULL, CV_DRIVER, NULL);
pp->pp_state = EHCI_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_qh = ehci_alloc_qh(ehcip, ph, EHCI_INTERRUPT_MODE_FLAG);
pp->pp_halt_state = EHCI_HALT_STATE_FREE;
cv_init(&pp->pp_halt_cmpl_cv, NULL, CV_DRIVER, NULL);
if ((pp->pp_qh == NULL) && !(EHCI_ISOC_ENDPOINT(epdt))) {
ASSERT(pp->pp_qh == NULL);
USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_open: QH allocation failed");
mutex_enter(&ph->p_mutex);
if (EHCI_PERIODIC_ENDPOINT(epdt)) {
ehci_deallocate_bandwidth(ehcip,
ph, pnode, smask, cmask);
}
cv_destroy(&pp->pp_xfer_cmpl_cv);
kmem_free(ph->p_hcd_private, sizeof (ehci_pipe_private_t));
ph->p_hcd_private = NULL;
mutex_exit(&ph->p_mutex);
mutex_exit(&ehcip->ehci_int_mutex);
return (USB_NO_RESOURCES);
}
if (!(EHCI_ISOC_ENDPOINT(epdt))) {
ehci_restore_data_toggle(ehcip, ph);
}
ehci_insert_qh(ehcip, ph);
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_open: ph = 0x%p", (void *)ph);
ehcip->ehci_open_pipe_count++;
mutex_exit(&ehcip->ehci_int_mutex);
return (USB_SUCCESS);
}
int
ehci_hcdi_pipe_close(
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
usb_ep_descr_t *eptd = &ph->p_ep;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_close: addr = 0x%x, ep%d",
ph->p_usba_device->usb_addr,
eptd->bEndpointAddress & USB_EP_NUM_MASK);
if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
mutex_enter(&ehcip->ehci_int_mutex);
error = ehci_handle_root_hub_pipe_close(ph);
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
ASSERT(ph->p_hcd_private != NULL);
mutex_enter(&ehcip->ehci_int_mutex);
pp->pp_state = EHCI_PIPE_STATE_CLOSE;
ehci_pipe_cleanup(ehcip, ph);
ehci_remove_qh(ehcip, pp, B_TRUE);
if (EHCI_PERIODIC_ENDPOINT(eptd)) {
mutex_enter(&ph->p_mutex);
ehci_deallocate_bandwidth(ehcip, ph, pp->pp_pnode,
pp->pp_smask, pp->pp_cmask);
mutex_exit(&ph->p_mutex);
}
mutex_enter(&ph->p_mutex);
cv_destroy(&pp->pp_xfer_cmpl_cv);
cv_destroy(&pp->pp_halt_cmpl_cv);
kmem_free(ph->p_hcd_private, sizeof (ehci_pipe_private_t));
ph->p_hcd_private = NULL;
mutex_exit(&ph->p_mutex);
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_close: ph = 0x%p", (void *)ph);
ehcip->ehci_open_pipe_count--;
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
int
ehci_hcdi_pipe_reset(
usba_pipe_handle_data_t *ph,
usb_flags_t usb_flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_reset:");
if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
error = ehci_handle_root_hub_pipe_reset(ph, usb_flags);
return (error);
}
mutex_enter(&ehcip->ehci_int_mutex);
pp->pp_state = EHCI_PIPE_STATE_RESET;
ehci_pipe_cleanup(ehcip, ph);
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
void
ehci_hcdi_pipe_reset_data_toggle(
usba_pipe_handle_data_t *ph)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_reset_data_toggle:");
mutex_enter(&ehcip->ehci_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_QH(pp->pp_qh->qh_status,
Get_QH(pp->pp_qh->qh_status) & (~EHCI_QH_STS_DATA_TOGGLE));
mutex_exit(&ehcip->ehci_int_mutex);
}
int
ehci_hcdi_pipe_ctrl_xfer(
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
int rval;
int error = USB_SUCCESS;
ehci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_ctrl_xfer: ph = 0x%p reqp = 0x%p flags = %x",
(void *)ph, (void *)ctrl_reqp, usb_flags);
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
mutex_exit(&ehcip->ehci_int_mutex);
if (rval != USB_SUCCESS) {
return (rval);
}
if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
error = ehci_handle_root_hub_request(ehcip, ph, ctrl_reqp);
return (error);
}
mutex_enter(&ehcip->ehci_int_mutex);
if (pp->pp_state == EHCI_PIPE_STATE_ERROR) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_ctrl_xfer: "
"Pipe is in error state, need pipe reset to continue");
mutex_exit(&ehcip->ehci_int_mutex);
return (USB_FAILURE);
}
if ((tw = ehci_allocate_ctrl_resources(ehcip, pp, ctrl_reqp,
usb_flags)) == NULL) {
error = USB_NO_RESOURCES;
} else {
ehci_insert_ctrl_req(ehcip, ph, ctrl_reqp, tw, usb_flags);
}
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
int
ehci_hcdi_bulk_transfer_size(
usba_device_t *usba_device,
size_t *size)
{
ehci_state_t *ehcip = ehci_obtain_state(
usba_device->usb_root_hub_dip);
int rval;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_bulk_transfer_size:");
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
mutex_exit(&ehcip->ehci_int_mutex);
if (rval != USB_SUCCESS) {
return (rval);
}
if ((ehcip->ehci_vendor_id == PCI_VENDOR_VIA) &&
(ehci_vt62x2_workaround & EHCI_VIA_REDUCED_MAX_BULK_XFER_SIZE)) {
*size = EHCI_VIA_MAX_BULK_XFER_SIZE;
} else {
*size = EHCI_MAX_BULK_XFER_SIZE;
}
return (USB_SUCCESS);
}
int
ehci_hcdi_pipe_bulk_xfer(
usba_pipe_handle_data_t *ph,
usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
int rval, error = USB_SUCCESS;
ehci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_bulk_xfer: ph = 0x%p reqp = 0x%p flags = %x",
(void *)ph, (void *)bulk_reqp, usb_flags);
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return (rval);
}
if (pp->pp_state == EHCI_PIPE_STATE_ERROR) {
USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_bulk_xfer:"
"Pipe is in error state, need pipe reset to continue");
mutex_exit(&ehcip->ehci_int_mutex);
return (USB_FAILURE);
}
if ((tw = ehci_allocate_bulk_resources(ehcip, pp, bulk_reqp,
usb_flags)) == NULL) {
error = USB_NO_RESOURCES;
} else {
ehci_insert_bulk_req(ehcip, ph, bulk_reqp, tw, usb_flags);
}
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
int
ehci_hcdi_pipe_intr_xfer(
usba_pipe_handle_data_t *ph,
usb_intr_req_t *intr_reqp,
usb_flags_t usb_flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
int pipe_dir, rval, error = USB_SUCCESS;
ehci_trans_wrapper_t *tw;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_intr_xfer: ph = 0x%p reqp = 0x%p flags = %x",
(void *)ph, (void *)intr_reqp, usb_flags);
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return (rval);
}
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
if (pipe_dir == USB_EP_DIR_IN) {
error = ehci_start_periodic_pipe_polling(ehcip, ph,
(usb_opaque_t)intr_reqp, usb_flags);
} else {
if ((tw = ehci_allocate_intr_resources(ehcip, ph,
intr_reqp, usb_flags)) == NULL) {
error = USB_NO_RESOURCES;
} else {
ehci_insert_intr_req(ehcip,
(ehci_pipe_private_t *)ph->p_hcd_private,
tw, usb_flags);
}
}
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
int
ehci_hcdi_pipe_stop_intr_polling(
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_stop_intr_polling: ph = 0x%p fl = 0x%x",
(void *)ph, flags);
mutex_enter(&ehcip->ehci_int_mutex);
error = ehci_stop_periodic_pipe_polling(ehcip, ph, flags);
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
int
ehci_hcdi_get_current_frame_number(
usba_device_t *usba_device,
usb_frame_number_t *frame_number)
{
ehci_state_t *ehcip = ehci_obtain_state(
usba_device->usb_root_hub_dip);
int rval;
ehcip = ehci_obtain_state(usba_device->usb_root_hub_dip);
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return (rval);
}
*frame_number = ehci_get_current_frame_number(ehcip);
mutex_exit(&ehcip->ehci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_get_current_frame_number: "
"Current frame number 0x%llx", (unsigned long long)(*frame_number));
return (rval);
}
int
ehci_hcdi_get_max_isoc_pkts(
usba_device_t *usba_device,
uint_t *max_isoc_pkts_per_request)
{
ehci_state_t *ehcip = ehci_obtain_state(
usba_device->usb_root_hub_dip);
int rval;
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
mutex_exit(&ehcip->ehci_int_mutex);
if (rval != USB_SUCCESS) {
return (rval);
}
*max_isoc_pkts_per_request = EHCI_MAX_ISOC_PKTS_PER_XFER;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_get_max_isoc_pkts: maximum isochronous"
"packets per usb isochronous request = 0x%x",
*max_isoc_pkts_per_request);
return (rval);
}
int
ehci_hcdi_pipe_isoc_xfer(
usba_pipe_handle_data_t *ph,
usb_isoc_req_t *isoc_reqp,
usb_flags_t usb_flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
int pipe_dir, rval;
ehci_isoc_xwrapper_t *itw;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_isoc_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
(void *)ph, (void *)isoc_reqp, usb_flags);
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return (rval);
}
pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
if (pipe_dir == USB_EP_DIR_IN) {
rval = ehci_start_periodic_pipe_polling(ehcip, ph,
(usb_opaque_t)isoc_reqp, usb_flags);
} else {
if ((itw = ehci_allocate_isoc_resources(ehcip, ph,
isoc_reqp, usb_flags)) == NULL) {
rval = USB_NO_RESOURCES;
} else {
rval = ehci_insert_isoc_req(ehcip,
(ehci_pipe_private_t *)ph->p_hcd_private,
itw, usb_flags);
}
}
mutex_exit(&ehcip->ehci_int_mutex);
return (rval);
}
int
ehci_hcdi_pipe_stop_isoc_polling(
usba_pipe_handle_data_t *ph,
usb_flags_t flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
int rval;
USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_stop_isoc_polling: ph = 0x%p fl = 0x%x",
(void *)ph, flags);
mutex_enter(&ehcip->ehci_int_mutex);
rval = ehci_state_is_operational(ehcip);
if (rval != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return (rval);
}
rval = ehci_stop_periodic_pipe_polling(ehcip, ph, flags);
mutex_exit(&ehcip->ehci_int_mutex);
return (rval);
}