uhcihub.c revision 35f36846429327ed1512f8098c6a6b337055d875
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Universal Serial BUS Host Controller Driver (UHCI)
*
* The UHCI driver is a driver which interfaces to the Universal
* Serial Bus Architecture (USBA) and the Host Controller (HC). The interface to
* the Host Controller is defined by the Universal Host Controller Interface.
* This file contains the code for root hub related functions.
*/
/*
* Function Prototypes
*/
static int uhci_handle_set_clear_port_feature(
static void uhci_handle_port_power(
static void uhci_handle_port_suspend(
static void uhci_handle_port_enable_disable(
static void uhci_handle_port_reset(
static void uhci_handle_complete_port_reset(
static void uhci_handle_clear_port_connection(
static void uhci_handle_get_port_status(
static void uhci_handle_get_hub_descriptor(
static void uhci_handle_get_hub_status(
static void uhci_handle_get_device_status(
static uint_t uhci_get_port_status(
static void uhci_rh_hcdi_callback(
/*
* root hub device descriptor
*/
static usb_dev_descr_t uhci_rh_dev_descr = {
0x12, /* Length */
1, /* Type */
0x110, /* BCD - v1.1 */
9, /* Class */
0, /* Sub class */
0, /* Protocol */
8, /* Max pkt size */
0, /* Vendor */
0, /* Product id */
0, /* Device release */
0, /* Manufacturer */
0, /* Product */
0, /* Sn */
1 /* No of configs */
};
/*
* root hub config descriptor
*/
static uchar_t uhci_rh_config_descr[] = {
/* config descriptor */
0x09, /* bLength */
0x02, /* bDescriptorType, Configuration */
0x19, 0x00, /* wTotalLength */
0x01, /* bNumInterfaces */
0x01, /* bConfigurationValue */
0x00, /* iConfiguration */
0x40, /* bmAttributes */
0x00, /* MaxPower */
/* interface descriptor */
0x09, /* bLength */
0x04, /* bDescriptorType, Interface */
0x00, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x01, /* bNumEndpoints */
0x09, /* bInterfaceClass */
0x01, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00, /* iInterface */
/* endpoint descriptor */
0x07, /* bLength */
0x05, /* bDescriptorType, Endpoint */
0x81, /* bEndpointAddress */
0x03, /* bmAttributes */
0x01, 0x00, /* wMaxPacketSize, 1 + (OHCI_MAX_RH_PORTS / 8) */
0x20 /* bInterval */
};
/*
* uhci_init_root_hub:
* Initialize the root hub
*/
int
{
int i, length;
"uhci_init_root_hub:");
/*
* Build the hub descriptor
*/
if (length) {
} else {
}
/* Determine the Power Switching Mode */
/* Indicate if the device is removable */
/* Fill in the port power control mask */
}
/* Finally load the root hub driver */
sizeof (uhci_rh_config_descr), &uhci_rh_dev_descr));
}
/*
* uhci_handle_root_hub_request:
* Intercept a root hub request.
* Handle the root hub request through the registers
*/
int
{
int error = USB_SUCCESS;
"uhci_handle_root_hub_request: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
switch (req->ctrl_bmRequestType) {
break;
break;
case HUB_GET_PORT_STATUS_TYPE:
break;
case HUB_CLASS_REQ_TYPE:
switch (req->ctrl_bRequest) {
case USB_REQ_GET_DESCR:
break;
case USB_REQ_GET_STATUS:
break;
default:
"uhci_handle_root_hub_request: Unsupported "
break;
}
break;
default:
"uhci_handle_root_hub_request: Unsupported request 0x%x",
break;
}
"uhci_handle_root_hub_request: error = %d", error);
return (USB_SUCCESS);
}
/*
* uhci_handle_set_clear_port_feature:
*/
static int
{
int error = USB_SUCCESS;
"uhci_handle_set_clear_port_feature: 0x%x 0x%x 0x%x",
switch (bRequest) {
case USB_REQ_SET_FEATURE:
switch (wValue) {
case CFS_PORT_ENABLE:
break;
case CFS_PORT_SUSPEND:
break;
case CFS_PORT_RESET:
break;
case CFS_PORT_POWER:
break;
default:
"uhci_handle_set_clear_port_feature: "
error = USB_FAILURE;
break;
}
break;
case USB_REQ_CLEAR_FEATURE:
switch (wValue) {
case CFS_PORT_ENABLE:
break;
case CFS_C_PORT_ENABLE:
break;
case CFS_PORT_SUSPEND:
break;
case CFS_C_PORT_RESET:
break;
case CFS_PORT_POWER:
break;
case CFS_C_PORT_CONNECTION:
break;
default:
"uhci_handle_set_clear_port_feature: "
error = USB_FAILURE;
break;
}
break;
default:
"uhci_handle_set_clear_port_feature: "
error = USB_FAILURE;
}
return (error);
}
/*
* uhci_handle_port_suspend:
*/
static void
{
"uhci_handle_port_suspend: port=%d on=%d",
if (on) {
/* See if the port suspend is already on */
if (!(port_status & HCR_PORT_SUSPEND)) {
/* suspend the port */
(port_status | HCR_PORT_SUSPEND));
}
} else {
/* See if the port suspend is already off */
if ((port_status & HCR_PORT_SUSPEND)) {
/* resume the port */
(port_status & ~HCR_PORT_SUSPEND));
}
}
}
/*
* uhci_handle_port_power:
* Turn on a root hub port. NOTE: Driver does not have any control
* over the power status.
*/
/* ARGSUSED */
static void
{
"uhci_handle_port_power: nothing to do");
}
/*
* uhci_handle_port_enable_disable:
* Handle port enable request.
*/
static void
{
"uhci_handle_port_enable: port = 0x%x, status = 0x%x",
port, port_status);
if (action == UHCI_ENABLE_PORT) {
/* See if the port enable is already on */
if (!(port_status & HCR_PORT_ENABLE)) {
/* Enable the port */
(port_status | HCR_PORT_ENABLE));
}
} else if (action == UHCI_DISABLE_PORT) {
/* See if the port enable is already off */
if ((port_status & HCR_PORT_ENABLE)) {
/* Disable the port */
(port_status & ~HCR_PORT_ENABLE));
}
} else {
/* Update software port_changes register */
}
}
/*
* uhci_root_hub_reset_occurred:
* Inform the upper layer that reset has occured on the port.
* This is required because the upper layer is expecting an
* event immediately after doing a reset. In case of OHCI
* the HC gets an interrupt for the change in the root hub
* status, but in case of UHCI we don't. So, we send an
* event to the upper layer as soon as we complete the reset
* as long as the root hub pipe is polling.
*/
void
{
"uhci_root_hub_reset_occurred: intr_reqp = 0x%p data = 0x%p",
}
/*
* uhci_handle_port_reset:
* Perform a port reset.
*/
static void
{
"uhci_handle_port_reset: port = 0x%x, status = 0x%x",
port, port_status);
if (!(port_status & HCR_PORT_CCS)) {
"port_status & HCR_PORT_CCS == 0: "
}
/*
* The next function is only called if the interrupt pipe
* is polling and the USBA is ready to receive the
* data. If not, we could panic.
*/
/* make a note that we need to send status back */
} else {
}
}
/*
* uhci_handle_complete_port_reset:
* Perform a port reset change.
*/
static void
{
"uhci_handle_complete_port_reset: port = 0x%x status = 0x%x",
port, port_status);
if (!(port_status & HCR_PORT_CCS)) {
"port_status & HCR_PORT_CCS == 0: "
}
/* Update software port_changes register */
}
/*
* uhci_handle_clear_port_connection:
* Perform a clear port connection.
*/
static void
{
"uhci_handle_clear_port_connection: port = 0x%x status = 0x%x",
port, port_status);
/* Clear CSC bit */
/* Update software port_changes register */
}
/*
* uhci_handle_get_port_status:
* Handle a get port status request.
*/
static void
{
/* Read the current port status and return it */
"uhci_handle_get_port_status:\n\t"
"port%d: old status = 0x%x new status = 0x%x change = 0x%x",
/* Update the status */
}
/*
* uhci_handle_get_hub_descriptor:
*/
static void
{
"uhci_handle_get_hub_descriptor: wLength = 0x%x",
req->ctrl_wLength);
}
/*
* uhci_handle_get_hub_status:
*/
static void
{
"uhci_handle_get_hub_status: wLength = 0x%x",
req->ctrl_wLength);
/*
* A good status is always sent because there is no way that
* the driver can get to know about the status change of the
* over current or power failure of the root hub from the HC.
*/
}
/*
* uhci_handle_get_device_status:
*/
static void
{
"uhci_handle_get_device_status: wLength = 0x%x",
req->ctrl_wLength);
/*
* UHCI doesn't have device status information.
* Simply return what is desired for the request.
*/
}
/*
* uhci_handle_root_hub_status_change:
* This function is called every 32 seconds from the time out handler.
* It checks for the status change of the root hub and its ports.
*/
void
{
uchar_t all_ports_status = 0;
/* reset the timeout id */
uhcip->uhci_timeout_id = 0;
/* Get the current interrupt request pointer */
/* Check each port */
if (change_status & PORT_STATUS_CCS) {
}
"port %d old status 0x%x new status 0x%x change 0x%x\n\t"
}
}
/*
* If needed, allocate new interrupt request. Also
* start the timer for the root hub interrupt polling.
*/
if (uhci_root_hub_allocate_intr_pipe_resource(uhcip, 0) !=
USB_SUCCESS) {
/* Do interrupt pipe cleanup */
}
}
}
static uint_t
{
if (port_status & HCR_PORT_CCS) {
}
if (port_status & HCR_PORT_LSDA) {
}
if (port_status & HCR_PORT_ENABLE) {
}
if (port_status & HCR_PORT_SUSPEND) {
}
if (port_status & HCR_PORT_RESET) {
}
return (new_port_status);
}
/*
* uhci_root_hub_allocate_intr_pipe_resource:
* Allocate interrupt requests and initialize them.
*/
int
{
"uhci_root_hub_allocate_intr_pipe_resource:");
/* Get the interrupt pipe handle */
/* Get the current interrupt request pointer */
/*
* If current interrupt request pointer is null,
* allocate new interrupt request.
*/
if (curr_intr_reqp == NULL) {
"uhci_root_hub_allocate_intr_pipe_resource:"
"Interrupt request structure allocation failed");
return (USB_NO_RESOURCES);
}
ph->p_req_count++;
}
if (uhcip->uhci_timeout_id == 0) {
(void *)uhcip, UHCI_32_MS);
}
return (USB_SUCCESS);
}
/*
* uhci_root_hub_intr_pipe_cleanup:
* Deallocate all interrupt requests and do callback
* the original client interrupt request.
*/
void
{
"uhci_root_hub_intr_pipe_cleanup:");
/* Get the interrupt pipe handle */
/* Get the interrupt timerid */
/* Stop the root hub interrupt timer */
if (timer_id) {
/* Reset the timer id to zero */
uhcip->uhci_timeout_id = 0;
}
/* Reset the current interrupt request pointer */
/* Deallocate uncompleted interrupt request */
if (curr_intr_reqp) {
ph->p_req_count--;
}
/* Callback for original client interrupt request */
if (client_intr_reqp) {
}
}
/*
* uhci_rh_hcdi_callback:
* Convenience wrapper around usba_hcdi_cb() for the root hub.
*/
static void
{
"uhci_rh_hcdi_callback: ph=0x%p cr=0x%x req=0x%p",
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_INTR:
if ((usb_intr_req_t *)req ==
break;
} else if ((usb_intr_req_t *)req ==
break;
}
/*FALLTHRU*/
default:
break;
}
}