#include <sys/usb/hcd/ehci/ehcid.h>
#include <sys/usb/hcd/ehci/ehci_util.h>
#include <sys/usb/usba/usba_types.h>
static int ehci_handle_set_clear_port_feature(
ehci_state_t *ehcip,
uchar_t bRequest,
uint16_t wValue,
uint16_t port);
static void ehci_handle_port_power(
ehci_state_t *ehcip,
uint16_t port,
uint_t on);
static void ehci_handle_port_enable(
ehci_state_t *ehcip,
uint16_t port,
uint_t on);
static void ehci_handle_clrchng_port_enable(
ehci_state_t *ehcip,
uint16_t port);
static void ehci_handle_port_suspend(
ehci_state_t *ehcip,
uint16_t port,
uint_t on);
static void ehci_handle_clrchng_port_suspend(
ehci_state_t *ehcip,
uint16_t port);
static void ehci_handle_port_reset(
ehci_state_t *ehcip,
uint16_t port);
static void ehci_root_hub_reset_occured(
ehci_state_t *ehcip);
static void ehci_handle_complete_port_reset(
ehci_state_t *ehcip,
uint16_t port);
static void ehci_handle_clear_port_connection(
ehci_state_t *ehcip,
uint16_t port);
static void ehci_handle_clrchng_port_over_current(
ehci_state_t *ehcip,
uint16_t port);
static void ehci_handle_get_port_status(
ehci_state_t *ehcip,
uint16_t port);
static void ehci_handle_get_hub_descriptor(
ehci_state_t *ehcip);
static void ehci_handle_get_hub_status(
ehci_state_t *ehcip);
static void ehci_handle_get_device_status(
ehci_state_t *ehcip);
static uint_t ehci_get_root_hub_port_status(
ehci_state_t *ehcip,
uint16_t port);
static int ehci_is_port_owner(
ehci_state_t *ehcip,
uint16_t port);
static int ehci_root_hub_allocate_intr_pipe_resource(
ehci_state_t *ehcip,
usb_flags_t flags);
static void ehci_root_hub_intr_pipe_cleanup(
ehci_state_t *ehcip,
usb_cr_t completion_reason);
static void ehci_handle_root_hub_status_change(void *arg);
static void ehci_root_hub_hcdi_callback(
usba_pipe_handle_data_t *ph,
usb_cr_t completion_reason);
int
ehci_init_root_hub(ehci_state_t *ehcip)
{
usb_hub_descr_t *root_hub_descr =
&ehcip->ehci_root_hub.rh_descr;
uint_t i, length, port_state;
uint32_t capability;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_init_root_hub:");
capability = Get_Cap(ehci_hcs_params);
root_hub_descr->bDescriptorType = ROOT_HUB_DESCRIPTOR_TYPE;
if ((capability & EHCI_HCS_NUM_PORTS) > EHCI_MAX_RH_PORTS) {
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_init_root_hub: Invalid no of root hub ports 0x%x",
capability & EHCI_HCS_NUM_PORTS);
return (USB_FAILURE);
}
root_hub_descr->bNbrPorts = capability & EHCI_HCS_NUM_PORTS;
length = root_hub_descr->bNbrPorts / 8;
if (length) {
root_hub_descr->bDescLength = 7 + (2 * (length + 1));
} else {
root_hub_descr->bDescLength = ROOT_HUB_DESCRIPTOR_LENGTH;
}
ehcip->ehci_root_hub.rh_companion_controllers = (capability &
EHCI_HCS_NUM_COMP_CTRLS) >> EHCI_HCS_NUM_COMP_CTRL_SHIFT;
if (capability & EHCI_HCS_PORT_POWER_CONTROL) {
root_hub_descr-> wHubCharacteristics =
HUB_CHARS_INDIVIDUAL_PORT_POWER;
root_hub_descr->wHubCharacteristics |=
HUB_CHARS_INDIV_OVER_CURRENT;
port_state = POWERED_OFF;
} else {
root_hub_descr->
wHubCharacteristics = HUB_CHARS_NO_POWER_SWITCHING;
root_hub_descr->wHubCharacteristics |=
HUB_CHARS_NO_OVER_CURRENT;
port_state = DISCONNECTED;
}
if (capability & EHCI_HCS_PORT_INDICATOR) {
root_hub_descr->wHubCharacteristics |= HUB_CHARS_PORT_INDICATOR;
}
root_hub_descr->bPwrOn2PwrGood = 2;
USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"Power on to power good %d", root_hub_descr->bPwrOn2PwrGood);
root_hub_descr->DeviceRemovable = 0;
root_hub_descr->PortPwrCtrlMask = 0;
for (i = 0; i < root_hub_descr->bNbrPorts; i++) {
ehcip->ehci_root_hub.rh_port_state[i] = port_state;
ehcip->ehci_root_hub.rh_port_status[i] = 0;
}
return (USB_SUCCESS);
}
static usb_dev_descr_t ehci_root_hub_device_descriptor = {
0x12,
0x01,
0x200,
0x09,
0x00,
0x01,
0x40,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01
};
static uchar_t ehci_root_hub_config_descriptor[] = {
0x09,
0x02,
0x19, 0x00,
0x01,
0x01,
0x00,
0x40,
0x00,
0x09,
0x04,
0x00,
0x00,
0x01,
0x09,
0x01,
0x00,
0x00,
0x07,
0x05,
0x81,
0x03,
0x01, 0x00,
0xff
};
int
ehci_load_root_hub_driver(ehci_state_t *ehcip)
{
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_load_root_hub_driver:");
return (usba_hubdi_bind_root_hub(ehcip->ehci_dip,
ehci_root_hub_config_descriptor,
sizeof (ehci_root_hub_config_descriptor),
&ehci_root_hub_device_descriptor));
}
int
ehci_unload_root_hub_driver(ehci_state_t *ehcip)
{
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_unload_root_hub_driver:");
return (usba_hubdi_unbind_root_hub(ehcip->ehci_dip));
}
int
ehci_handle_root_hub_pipe_open(
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);
usb_ep_descr_t *eptd = &ph->p_ep;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_open: Root hub pipe open");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
ehcip->ehci_root_hub.rh_ctrl_pipe_handle = ph;
ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_IDLE;
ehcip->ehci_root_hub.rh_curr_ctrl_reqp = NULL;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_open: Root hub control "
"pipe open succeeded");
break;
case USB_EP_ATTR_INTR:
ehcip->ehci_root_hub.rh_intr_pipe_handle = ph;
ehcip->ehci_root_hub.rh_intr_pipe_state = EHCI_PIPE_STATE_IDLE;
ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_open: Root hub interrupt "
"pipe open succeeded");
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_open: Root hub pipe open"
"failed");
return (USB_FAILURE);
}
ehcip->ehci_open_pipe_count++;
return (USB_SUCCESS);
}
int
ehci_handle_root_hub_pipe_close(usba_pipe_handle_data_t *ph)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
usb_ep_descr_t *eptd = &ph->p_ep;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_close: Root hub pipe close");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
ASSERT(ehcip->ehci_root_hub.
rh_ctrl_pipe_state != EHCI_PIPE_STATE_CLOSE);
ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_CLOSE;
ehcip->ehci_root_hub.rh_ctrl_pipe_handle = NULL;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_close: "
"Root hub control pipe close succeeded");
break;
case USB_EP_ATTR_INTR:
ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
ASSERT(ehcip->ehci_root_hub.
rh_intr_pipe_state != EHCI_PIPE_STATE_CLOSE);
ehcip->ehci_root_hub.rh_intr_pipe_state = EHCI_PIPE_STATE_CLOSE;
ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_PIPE_CLOSING);
ehcip->ehci_root_hub.rh_intr_pipe_handle = NULL;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_close: "
"Root hub interrupt pipe close succeeded");
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_close: "
"Root hub pipe close failed");
return (USB_FAILURE);
}
ehcip->ehci_open_pipe_count--;
return (USB_SUCCESS);
}
int
ehci_handle_root_hub_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);
usb_ep_descr_t *eptd = &ph->p_ep;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_reset: Root hub pipe reset");
mutex_enter(&ehcip->ehci_int_mutex);
switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_IDLE;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_reset: Pipe reset"
"for the root hub control pipe successful");
break;
case USB_EP_ATTR_INTR:
ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
if ((ehcip->ehci_root_hub.rh_client_intr_reqp) &&
(ehcip->ehci_root_hub.rh_intr_pipe_state !=
EHCI_PIPE_STATE_IDLE)) {
ehcip->ehci_root_hub.
rh_intr_pipe_state = EHCI_PIPE_STATE_RESET;
ehci_root_hub_intr_pipe_cleanup(
ehcip, USB_CR_PIPE_RESET);
}
ASSERT(ehcip->ehci_root_hub.
rh_intr_pipe_state == EHCI_PIPE_STATE_IDLE);
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_reset: "
"Pipe reset for root hub interrupt pipe successful");
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_reset: "
"Root hub pipe reset failed");
error = USB_FAILURE;
break;
}
mutex_exit(&ehcip->ehci_int_mutex);
return (error);
}
int
ehci_handle_root_hub_request(
ehci_state_t *ehcip,
usba_pipe_handle_data_t *ph,
usb_ctrl_req_t *ctrl_reqp)
{
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;
uint16_t port = wIndex - 1;
usb_cr_t completion_reason;
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_request: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
bmRequestType, bRequest, wValue, wIndex, wLength, (void *)data);
mutex_enter(&ehcip->ehci_int_mutex);
if (ehcip->ehci_root_hub.
rh_ctrl_pipe_state != EHCI_PIPE_STATE_IDLE) {
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_request: Pipe is not idle");
mutex_exit(&ehcip->ehci_int_mutex);
return (USB_FAILURE);
}
ehcip->ehci_root_hub.rh_curr_ctrl_reqp = ctrl_reqp;
ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_ACTIVE;
mutex_exit(&ehcip->ehci_int_mutex);
switch (bmRequestType) {
case HUB_GET_DEVICE_STATUS_TYPE:
ehci_handle_get_device_status(ehcip);
break;
case HUB_HANDLE_PORT_FEATURE_TYPE:
error = ehci_handle_set_clear_port_feature(ehcip,
bRequest, wValue, port);
break;
case HUB_GET_PORT_STATUS_TYPE:
ehci_handle_get_port_status(ehcip, port);
break;
case HUB_CLASS_REQ_TYPE:
switch (bRequest) {
case USB_REQ_GET_STATUS:
ehci_handle_get_hub_status(ehcip);
break;
case USB_REQ_GET_DESCR:
ehci_handle_get_hub_descriptor(ehcip);
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_request:"
"Unsupported request 0x%x", bRequest);
error = USB_FAILURE;
break;
}
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_request: "
"Unsupported request 0x%x", bmRequestType);
error = USB_FAILURE;
break;
}
completion_reason = (error) ? USB_CR_NOT_SUPPORTED : USB_CR_OK;
mutex_enter(&ehcip->ehci_int_mutex);
ehci_root_hub_hcdi_callback(ph, completion_reason);
mutex_exit(&ehcip->ehci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_request: error = %d", error);
return (USB_SUCCESS);
}
static int
ehci_handle_set_clear_port_feature(
ehci_state_t *ehcip,
uchar_t bRequest,
uint16_t wValue,
uint16_t port)
{
int error = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_set_clear_port_feature: 0x%x 0x%x 0x%x",
bRequest, wValue, port);
switch (bRequest) {
case USB_REQ_SET_FEATURE:
switch (wValue) {
case CFS_PORT_ENABLE:
ehci_handle_port_enable(ehcip, port, 1);
break;
case CFS_PORT_SUSPEND:
ehci_handle_port_suspend(ehcip, port, 1);
break;
case CFS_PORT_RESET:
ehci_handle_port_reset(ehcip, port);
break;
case CFS_PORT_POWER:
ehci_handle_port_power(ehcip, port, 1);
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_set_clear_port_feature: "
"Unsupported request 0x%x 0x%x", bRequest, wValue);
error = USB_FAILURE;
break;
}
break;
case USB_REQ_CLEAR_FEATURE:
switch (wValue) {
case CFS_PORT_ENABLE:
ehci_handle_port_enable(ehcip, port, 0);
break;
case CFS_C_PORT_ENABLE:
ehci_handle_clrchng_port_enable(ehcip, port);
break;
case CFS_PORT_SUSPEND:
ehci_handle_port_suspend(ehcip, port, 0);
break;
case CFS_C_PORT_SUSPEND:
ehci_handle_clrchng_port_suspend(ehcip, port);
break;
case CFS_C_PORT_RESET:
ehci_handle_complete_port_reset(ehcip, port);
break;
case CFS_PORT_POWER:
ehci_handle_port_power(ehcip, port, 0);
break;
case CFS_C_PORT_CONNECTION:
ehci_handle_clear_port_connection(ehcip, port);
break;
case CFS_C_PORT_OVER_CURRENT:
ehci_handle_clrchng_port_over_current(ehcip, port);
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_set_clear_port_feature: "
"Unsupported request 0x%x 0x%x", bRequest, wValue);
error = USB_FAILURE;
break;
}
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_set_clear_port_feature: "
"Unsupported request 0x%x 0x%x", bRequest, wValue);
error = USB_FAILURE;
break;
}
return (error);
}
static void
ehci_handle_port_power(
ehci_state_t *ehcip,
uint16_t port,
uint_t on)
{
uint_t port_status;
ehci_root_hub_t *rh;
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
rh = &ehcip->ehci_root_hub;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_port_power: port = 0x%x status = 0x%x on = %d",
port, port_status, on);
if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
if (on) {
if (!(port_status & EHCI_RH_PORT_POWER)) {
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_POWER);
}
rh->rh_port_status[port] = 0;
rh->rh_port_state[port] = DISCONNECTED;
} else {
if (port_status & EHCI_RH_PORT_POWER) {
Set_OpReg(ehci_rh_port_status[port],
port_status & ~EHCI_RH_PORT_POWER);
}
rh->rh_port_status[port] = 0;
rh->rh_port_state[port] = POWERED_OFF;
}
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_port_power done: port = 0x%x status = 0x%x on = %d",
port, Get_OpReg(ehci_rh_port_status[port]), on);
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_port_enable(
ehci_state_t *ehcip,
uint16_t port,
uint_t on)
{
uint_t port_status;
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_port_enable: port = 0x%x, status = 0x%x",
port, port_status);
if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
if (on) {
if (!(port_status & EHCI_RH_PORT_ENABLE)) {
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_ENABLE);
}
} else {
if (port_status & EHCI_RH_PORT_ENABLE) {
Set_OpReg(ehci_rh_port_status[port],
port_status & ~EHCI_RH_PORT_ENABLE);
}
}
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_clrchng_port_enable(
ehci_state_t *ehcip,
uint16_t port)
{
uint_t port_status;
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_port_enable: port = 0x%x, status = 0x%x",
port, port_status);
if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_ENABLE_CHANGE);
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_port_suspend(
ehci_state_t *ehcip,
uint16_t port,
uint_t on)
{
uint_t port_status;
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_port_suspend: port = 0x%x, status = 0x%x",
port, port_status);
if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
if (on) {
if ((port_status & EHCI_RH_PORT_ENABLE) &&
(!(port_status & EHCI_RH_PORT_SUSPEND))) {
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_SUSPEND);
mutex_exit(&ehcip->ehci_int_mutex);
delay(drv_usectohz(EHCI_PORT_SUSPEND_TIMEWAIT));
return;
}
} else {
if (port_status & EHCI_RH_PORT_SUSPEND) {
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_RESUME);
}
}
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_clrchng_port_suspend(
ehci_state_t *ehcip,
uint16_t port)
{
uint_t port_status;
int i;
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_clrchng_port_suspend: port = 0x%x, "
"status = 0x%x", port, port_status);
if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
if (!(port_status & EHCI_RH_PORT_RESUME)) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
mutex_exit(&ehcip->ehci_int_mutex);
delay(drv_usectohz(EHCI_PORT_RESUME_TIMEWAIT));
mutex_enter(&ehcip->ehci_int_mutex);
Set_OpReg(ehci_rh_port_status[port],
port_status & ~EHCI_RH_PORT_RESUME);
mutex_exit(&ehcip->ehci_int_mutex);
for (i = 0; i < EHCI_PORT_RESUME_RETRY_MAX; i++) {
delay(drv_usectohz(EHCI_PORT_RESUME_COMP_TIMEWAIT));
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
mutex_exit(&ehcip->ehci_int_mutex);
if (!(port_status & EHCI_RH_PORT_RESUME)) {
break;
}
}
}
static void
ehci_handle_port_reset(
ehci_state_t *ehcip,
uint16_t port)
{
ehci_root_hub_t *rh;
uint_t port_status;
int i;
mutex_enter(&ehcip->ehci_int_mutex);
rh = &ehcip->ehci_root_hub;
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_port_reset: port = 0x%x status = 0x%x",
port, port_status);
if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
if (port_status & EHCI_RH_PORT_LOW_SPEED) {
if (rh->rh_companion_controllers) {
USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_port_reset: Low speed device "
"and handover this port to Companion controller");
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_OWNER_CLASSIC);
} else {
USB_DPRINTF_L1(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"Low speed device is not supported");
}
} else {
Set_OpReg(ehci_rh_port_status[port],
((port_status | EHCI_RH_PORT_RESET) &
~EHCI_RH_PORT_ENABLE));
mutex_exit(&ehcip->ehci_int_mutex);
delay(drv_usectohz(EHCI_PORT_RESET_TIMEWAIT));
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
Set_OpReg(ehci_rh_port_status[port],
(port_status & ~EHCI_RH_PORT_RESET));
mutex_exit(&ehcip->ehci_int_mutex);
for (i = 0; i < EHCI_PORT_RESET_RETRY_MAX; i++) {
delay(drv_usectohz(EHCI_PORT_RESET_COMP_TIMEWAIT));
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
mutex_exit(&ehcip->ehci_int_mutex);
if (!(port_status & EHCI_RH_PORT_RESET)) {
break;
}
}
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
if (!(port_status & EHCI_RH_PORT_ENABLE)) {
if (rh->rh_companion_controllers) {
USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl,
"ehci_handle_port_reset: Full speed device "
"and handover this port to Companion host "
"controller");
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_OWNER_CLASSIC);
} else {
USB_DPRINTF_L1(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl,
"Full speed device is not supported");
}
} else {
USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_port_reset: High speed device ");
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
Set_OpReg(ehci_rh_port_status[port], port_status &
~(EHCI_RH_PORT_OVER_CURENT_ENABLE |
EHCI_RH_PORT_DISCONNECT_ENABLE |
EHCI_RH_PORT_CONNECT_ENABLE));
ehcip->ehci_root_hub.
rh_intr_pending_status |= (1 << port);
if (ehcip->ehci_root_hub.
rh_intr_pipe_state == EHCI_PIPE_STATE_ACTIVE) {
ehci_root_hub_reset_occured(ehcip);
}
}
}
mutex_exit(&ehcip->ehci_int_mutex);
}
void
ehci_root_hub_reset_occured(
ehci_state_t *ehcip)
{
usb_intr_req_t *curr_intr_reqp =
ehcip->ehci_root_hub.rh_curr_intr_reqp;
usb_port_mask_t port_mask;
usba_pipe_handle_data_t *ph;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_root_hub_reset_occured: curr_intr_reqp = 0x%p data = 0x%p",
(void *)curr_intr_reqp, (void *)curr_intr_reqp->intr_data);
ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
port_mask = ehcip->ehci_root_hub.rh_intr_pending_status << 1;
do {
*curr_intr_reqp->intr_data->b_wptr++ = (uchar_t)port_mask;
port_mask >>= 8;
} while (port_mask != 0);
ehci_root_hub_hcdi_callback(ph, USB_CR_OK);
ehcip->ehci_root_hub.rh_intr_pending_status = 0;
if ((ehci_root_hub_allocate_intr_pipe_resource(
ehcip, 0)) != USB_SUCCESS) {
ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_NO_RESOURCES);
}
}
static void
ehci_handle_complete_port_reset(
ehci_state_t *ehcip,
uint16_t port)
{
uint_t port_status;
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_complete_port_reset: port = 0x%x status = 0x%x",
port, port_status);
if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
if (port_status & EHCI_RH_PORT_RESET) {
Set_OpReg(ehci_rh_port_status[port],
port_status & ~EHCI_RH_PORT_RESET);
}
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_clear_port_connection(
ehci_state_t *ehcip,
uint16_t port)
{
uint_t port_status;
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_clear_port_connection: port = 0x%x"
"status = 0x%x", port, port_status);
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_CONNECT_STS_CHANGE);
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_clrchng_port_over_current(
ehci_state_t *ehcip,
uint16_t port)
{
uint_t port_status;
mutex_enter(&ehcip->ehci_int_mutex);
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_clrchng_port_over_current: port = 0x%x"
"status = 0x%x", port, port_status);
Set_OpReg(ehci_rh_port_status[port],
port_status | EHCI_RH_PORT_OVER_CURR_CHANGE);
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_get_port_status(
ehci_state_t *ehcip,
uint16_t port)
{
usb_ctrl_req_t *ctrl_reqp;
mblk_t *message;
uint_t new_port_status = 0;
uint_t change_status = 0;
uint_t port_status;
mutex_enter(&ehcip->ehci_int_mutex);
ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
port_status = ehci_get_root_hub_port_status(ehcip, port);
new_port_status = port_status & PORT_STATUS_MASK;
change_status = (port_status >> 16) & PORT_CHANGE_MASK_2X;
ehcip->ehci_root_hub.rh_port_status[port] = new_port_status;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_get_port_status: port = %d new status = 0x%x"
"change = 0x%x", port, new_port_status, change_status);
message = ctrl_reqp->ctrl_data;
ASSERT(message != NULL);
*message->b_wptr++ = (uchar_t)new_port_status;
*message->b_wptr++ = (uchar_t)(new_port_status >> 8);
*message->b_wptr++ = (uchar_t)change_status;
*message->b_wptr++ = (uchar_t)(change_status >> 8);
ctrl_reqp->ctrl_data = message;
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_get_hub_descriptor(
ehci_state_t *ehcip)
{
usb_ctrl_req_t *ctrl_reqp;
mblk_t *message;
usb_hub_descr_t *root_hub_descr;
size_t length;
uchar_t raw_descr[ROOT_HUB_DESCRIPTOR_LENGTH];
mutex_enter(&ehcip->ehci_int_mutex);
ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
root_hub_descr = &ehcip->ehci_root_hub.rh_descr;
length = ctrl_reqp->ctrl_wLength;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_get_hub_descriptor: Ctrl Req = 0x%p",
(void *)ctrl_reqp);
message = ctrl_reqp->ctrl_data;
ASSERT(message != NULL);
bzero(&raw_descr, ROOT_HUB_DESCRIPTOR_LENGTH);
raw_descr[0] = root_hub_descr->bDescLength;
raw_descr[1] = root_hub_descr->bDescriptorType;
raw_descr[2] = root_hub_descr->bNbrPorts;
raw_descr[3] = root_hub_descr->wHubCharacteristics & 0x00FF;
raw_descr[4] = (root_hub_descr->wHubCharacteristics & 0xFF00) >> 8;
raw_descr[5] = root_hub_descr->bPwrOn2PwrGood;
raw_descr[6] = root_hub_descr->bHubContrCurrent;
raw_descr[7] = root_hub_descr->DeviceRemovable;
raw_descr[8] = root_hub_descr->PortPwrCtrlMask;
bcopy(raw_descr, message->b_wptr, length);
message->b_wptr += length;
ctrl_reqp->ctrl_data = message;
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_get_hub_status(
ehci_state_t *ehcip)
{
usb_ctrl_req_t *ctrl_reqp;
mblk_t *message;
uint_t new_root_hub_status;
mutex_enter(&ehcip->ehci_int_mutex);
ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
new_root_hub_status = 0;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_get_hub_status: new root hub status = 0x%x",
new_root_hub_status);
message = ctrl_reqp->ctrl_data;
ASSERT(message != NULL);
*message->b_wptr++ = (uchar_t)new_root_hub_status;
*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 8);
*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 16);
*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 24);
ctrl_reqp->ctrl_data = message;
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_handle_get_device_status(
ehci_state_t *ehcip)
{
usb_ctrl_req_t *ctrl_reqp;
mblk_t *message;
uint16_t dev_status;
mutex_enter(&ehcip->ehci_int_mutex);
ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
dev_status = USB_DEV_SLF_PWRD_STATUS;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_get_device_status: device status = 0x%x",
dev_status);
message = ctrl_reqp->ctrl_data;
ASSERT(message != NULL);
*message->b_wptr++ = (uchar_t)dev_status;
*message->b_wptr++ = (uchar_t)(dev_status >> 8);
ctrl_reqp->ctrl_data = message;
mutex_exit(&ehcip->ehci_int_mutex);
}
int
ehci_handle_root_hub_pipe_start_intr_polling(
usba_pipe_handle_data_t *ph,
usb_intr_req_t *client_intr_reqp,
usb_flags_t flags)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
usb_ep_descr_t *eptd = &ph->p_ep;
int error = USB_SUCCESS;
uint_t pipe_state;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_start_intr_polling: "
"Root hub pipe start polling");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
ASSERT((client_intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER) == 0);
pipe_state = ehcip->ehci_root_hub.rh_intr_pipe_state;
switch (pipe_state) {
case EHCI_PIPE_STATE_IDLE:
ASSERT(ehcip->ehci_root_hub.rh_intr_pipe_timer_id == 0);
ASSERT(ehcip->ehci_root_hub.rh_client_intr_reqp == NULL);
ehcip->ehci_root_hub.rh_client_intr_reqp = client_intr_reqp;
error = ehci_root_hub_allocate_intr_pipe_resource(ehcip, flags);
if (error != USB_SUCCESS) {
ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_start_intr_polling: "
"No Resources");
return (error);
}
if (ehcip->ehci_root_hub.rh_intr_pending_status) {
ehci_root_hub_reset_occured(ehcip);
}
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_start_intr_polling: "
"Start polling for root hub successful");
break;
case EHCI_PIPE_STATE_ACTIVE:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_start_intr_polling: "
"Polling for root hub is already in progress");
break;
default:
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_start_intr_polling: "
"Pipe is in error state 0x%x", pipe_state);
error = USB_FAILURE;
break;
}
return (error);
}
void
ehci_handle_root_hub_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);
usb_ep_descr_t *eptd = &ph->p_ep;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_pipe_stop_intr_polling: "
"Root hub pipe stop polling");
ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
if (ehcip->ehci_root_hub.rh_intr_pipe_state ==
EHCI_PIPE_STATE_ACTIVE) {
ehcip->ehci_root_hub.rh_intr_pipe_state =
EHCI_PIPE_STATE_STOP_POLLING;
ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_STOPPED_POLLING);
ASSERT(ehcip->ehci_root_hub.
rh_intr_pipe_state == EHCI_PIPE_STATE_IDLE);
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_hcdi_pipe_stop_intr_polling: Stop polling for root"
"hub successful");
} else {
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl, "ehci_hcdi_pipe_stop_intr_polling: "
"Polling for root hub is already stopped");
}
}
static uint_t
ehci_get_root_hub_port_status(
ehci_state_t *ehcip,
uint16_t port)
{
uint_t new_port_status = 0;
uint_t change_status = 0;
uint_t port_status;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
port_status = Get_OpReg(ehci_rh_port_status[port]);
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_get_root_hub_port_status: port %d "
"port status = 0x%x", port, port_status);
if ((port_status & EHCI_RH_PORT_OWNER) == EHCI_RH_PORT_OWNER_EHCI) {
if (port_status & EHCI_RH_PORT_ENABLE_CHANGE) {
change_status |= PORT_CHANGE_PESC;
}
if (port_status & EHCI_RH_PORT_RESUME) {
change_status |= PORT_CHANGE_PSSC;
}
if (port_status & EHCI_RH_PORT_OVER_CURR_CHANGE) {
change_status |= PORT_CHANGE_OCIC;
}
if (port_status & EHCI_RH_PORT_CONNECT_STATUS) {
new_port_status |= PORT_STATUS_CCS;
}
if (port_status & EHCI_RH_PORT_ENABLE) {
new_port_status |=
(PORT_STATUS_PES | PORT_STATUS_HSDA);
}
if (port_status & EHCI_RH_PORT_SUSPEND) {
new_port_status |= PORT_STATUS_PSS;
}
if (port_status & EHCI_RH_PORT_OVER_CURR_ACTIVE) {
new_port_status |= PORT_STATUS_POCI;
}
if (port_status & EHCI_RH_PORT_RESET) {
new_port_status |= PORT_STATUS_PRS;
}
if (port_status & EHCI_RH_PORT_INDICATOR) {
new_port_status |= PORT_STATUS_PIC;
}
}
if (port_status & EHCI_RH_PORT_CONNECT_STS_CHANGE) {
change_status |= PORT_CHANGE_CSC;
}
if (port_status & EHCI_RH_PORT_POWER) {
new_port_status |= PORT_STATUS_PPS;
}
if ((!(port_status & EHCI_RH_PORT_ENABLE)) &&
(port_status & EHCI_RH_PORT_LOW_SPEED)) {
new_port_status |= PORT_STATUS_LSDA;
}
port_status = ((change_status << 16) | new_port_status);
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_get_root_hub_port_status: port = %d new status = 0x%x "
"change status = 0x%x complete port status 0x%x", port,
new_port_status, change_status, port_status);
return (port_status);
}
static int
ehci_is_port_owner(
ehci_state_t *ehcip,
uint16_t port)
{
uint_t port_status;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
port_status = Get_OpReg(ehci_rh_port_status[port]) &
~EHCI_RH_PORT_CLEAR_MASK;
if ((port_status & EHCI_RH_PORT_OWNER) == EHCI_RH_PORT_OWNER_CLASSIC) {
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_set_clear_port_feature: "
"Port %d is owned by classic host controller", port);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static int
ehci_root_hub_allocate_intr_pipe_resource(
ehci_state_t *ehcip,
usb_flags_t flags)
{
usba_pipe_handle_data_t *ph;
size_t length;
usb_intr_req_t *curr_intr_reqp;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_root_hub_allocate_intr_pipe_resource");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
if (curr_intr_reqp == NULL) {
ASSERT(ehcip->ehci_root_hub.rh_client_intr_reqp);
length = ehcip->ehci_root_hub.
rh_client_intr_reqp->intr_len;
curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
ehcip->ehci_root_hub.rh_client_intr_reqp,
length, flags);
if (curr_intr_reqp == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_root_hub_allocate_intr_pipe_resource:"
"Interrupt request structure allocation failed");
return (USB_NO_RESOURCES);
}
ehcip->ehci_root_hub.rh_curr_intr_reqp = curr_intr_reqp;
mutex_enter(&ph->p_mutex);
ph->p_req_count++;
mutex_exit(&ph->p_mutex);
}
if (ehcip->ehci_root_hub.rh_intr_pipe_timer_id == 0) {
ehcip->ehci_root_hub.rh_intr_pipe_timer_id =
timeout(ehci_handle_root_hub_status_change,
(void *)ehcip, drv_usectohz(EHCI_RH_POLL_TIME));
ehcip->ehci_root_hub.
rh_intr_pipe_state = EHCI_PIPE_STATE_ACTIVE;
}
return (USB_SUCCESS);
}
static void
ehci_root_hub_intr_pipe_cleanup(
ehci_state_t *ehcip,
usb_cr_t completion_reason)
{
usb_intr_req_t *curr_intr_reqp;
usb_opaque_t client_intr_reqp;
timeout_id_t timer_id;
usba_pipe_handle_data_t *ph;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_root_hub_intr_pipe_cleanup");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
timer_id = ehcip->ehci_root_hub.rh_intr_pipe_timer_id;
if (timer_id) {
ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
mutex_exit(&ehcip->ehci_int_mutex);
(void) untimeout(timer_id);
mutex_enter(&ehcip->ehci_int_mutex);
}
curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
if (curr_intr_reqp) {
ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
usb_free_intr_req(curr_intr_reqp);
mutex_enter(&ph->p_mutex);
ph->p_req_count--;
mutex_exit(&ph->p_mutex);
}
client_intr_reqp = (usb_opaque_t)
ehcip->ehci_root_hub.rh_client_intr_reqp;
if (client_intr_reqp) {
ehci_root_hub_hcdi_callback(ph, completion_reason);
}
}
static void
ehci_handle_root_hub_status_change(void *arg)
{
ehci_state_t *ehcip = (ehci_state_t *)arg;
usb_hub_descr_t *root_hub_descr =
&ehcip->ehci_root_hub.rh_descr;
usb_intr_req_t *curr_intr_reqp;
usb_port_mask_t port_mask = 0;
uint_t new_port_status;
uint_t change_status;
uint_t port_status;
mblk_t *message;
size_t length;
usb_ep_descr_t *eptd;
usba_pipe_handle_data_t *ph;
int i;
mutex_enter(&ehcip->ehci_int_mutex);
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_status_change: state = %d",
ehcip->ehci_root_hub.rh_intr_pipe_state);
#if defined(__x86)
if (ehcip->ehci_polled_root_hub_count < 16) {
if (Get_OpReg(ehci_config_flag) == 0) {
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl,
"ehci_handle_root_hub_status_change:"
" EHCI have been reset");
if (ehci_init_ctlr(ehcip, EHCI_REINITIALIZATION) !=
DDI_SUCCESS) {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
}
ehcip->ehci_polled_root_hub_count++;
}
#endif
curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
if (ehcip->ehci_root_hub.rh_intr_pipe_timer_id) {
if ((ehci_state_is_operational(ehcip)) != USB_SUCCESS) {
ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
ehci_root_hub_intr_pipe_cleanup(
ehcip, USB_CR_HC_HARDWARE_ERR);
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
} else {
mutex_exit(&ehcip->ehci_int_mutex);
return;
}
eptd = &ehcip->ehci_root_hub.rh_intr_pipe_handle->p_ep;
for (i = 0; i < root_hub_descr->bNbrPorts; i++) {
port_status = ehci_get_root_hub_port_status(ehcip, i);
new_port_status = port_status & PORT_STATUS_MASK;
change_status = (port_status >> 16) & PORT_CHANGE_MASK_2X;
if (change_status) {
if (change_status & PORT_CHANGE_CSC) {
if (new_port_status & PORT_STATUS_CCS) {
ehcip->ehci_root_hub.
rh_port_state[i] = DISABLED;
USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl,
"Port %d connected", i+1);
} else {
ehcip->ehci_root_hub.
rh_port_state[i] = DISCONNECTED;
USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl,
"Port %d disconnected", i+1);
}
}
if (change_status & PORT_CHANGE_PESC) {
if (new_port_status & PORT_STATUS_PES) {
ehcip->ehci_root_hub.
rh_port_state[i] = ENABLED;
USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl,
"Port %d enabled", i+1);
} else {
ehcip->ehci_root_hub.
rh_port_state[i] = DISABLED;
USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl,
"Port %d disabled", i+1);
}
}
port_mask |= 1 << (i + 1);
ehcip->ehci_root_hub.
rh_port_status[i] = new_port_status;
}
}
if (ph && port_mask && curr_intr_reqp) {
length = eptd->wMaxPacketSize;
ASSERT(length != 0);
message = curr_intr_reqp->intr_data;
ASSERT(message != NULL);
do {
if (message->b_wptr >= message->b_datap->db_lim) {
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB,
ehcip->ehci_log_hdl,
"ehci_handle_root_hub_status_change"
"mblk data overflow.");
break;
}
*message->b_wptr++ = (uchar_t)port_mask;
port_mask >>= 8;
} while (port_mask != 0);
ehci_root_hub_hcdi_callback(ph, USB_CR_OK);
}
ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
if (ehcip->ehci_root_hub.rh_intr_pipe_state ==
EHCI_PIPE_STATE_ACTIVE) {
if ((ehci_root_hub_allocate_intr_pipe_resource(
ehcip, 0)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_handle_root_hub_status_change: No Resources");
ehci_root_hub_intr_pipe_cleanup(
ehcip, USB_CR_NO_RESOURCES);
}
}
mutex_exit(&ehcip->ehci_int_mutex);
}
static void
ehci_root_hub_hcdi_callback(
usba_pipe_handle_data_t *ph,
usb_cr_t completion_reason)
{
ehci_state_t *ehcip = ehci_obtain_state(
ph->p_usba_device->usb_root_hub_dip);
uchar_t attributes = ph->p_ep.bmAttributes &
USB_EP_ATTR_MASK;
usb_opaque_t curr_xfer_reqp;
uint_t pipe_state = 0;
USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
"ehci_root_hub_hcdi_callback: ph = 0x%p, cr = 0x%x",
(void *)ph, completion_reason);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
switch (completion_reason) {
case USB_CR_OK:
switch (attributes) {
case USB_EP_ATTR_CONTROL:
pipe_state = EHCI_PIPE_STATE_IDLE;
break;
case USB_EP_ATTR_INTR:
pipe_state = ehcip->ehci_root_hub.
rh_intr_pipe_state;
break;
}
break;
case USB_CR_NO_RESOURCES:
case USB_CR_NOT_SUPPORTED:
case USB_CR_STOPPED_POLLING:
case USB_CR_PIPE_RESET:
case USB_CR_HC_HARDWARE_ERR:
pipe_state = EHCI_PIPE_STATE_IDLE;
break;
case USB_CR_PIPE_CLOSING:
break;
default:
pipe_state = EHCI_PIPE_STATE_ERROR;
break;
}
switch (attributes) {
case USB_EP_ATTR_CONTROL:
curr_xfer_reqp = (usb_opaque_t)
ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
ehcip->ehci_root_hub.rh_curr_ctrl_reqp = NULL;
ehcip->ehci_root_hub.rh_ctrl_pipe_state = pipe_state;
break;
case USB_EP_ATTR_INTR:
if (ehcip->ehci_root_hub.rh_curr_intr_reqp) {
curr_xfer_reqp = (usb_opaque_t)ehcip->
ehci_root_hub.rh_curr_intr_reqp;
ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
} else {
curr_xfer_reqp = (usb_opaque_t)
ehcip->ehci_root_hub.rh_client_intr_reqp;
ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
}
ehcip->ehci_root_hub.rh_intr_pipe_state = pipe_state;
break;
}
ASSERT(curr_xfer_reqp != NULL);
mutex_exit(&ehcip->ehci_int_mutex);
usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
mutex_enter(&ehcip->ehci_int_mutex);
}