usbai.c revision cf1b9f3de4ee9881f8ce4002861c99c4c52e3904
/*
* 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"
/*
* USBA: Solaris USB Architecture support
*
* all functions exposed to client drivers have prefix usb_ while all USBA
* internal functions or functions exposed to HCD or hubd only have prefix
* usba_
*
* support
*/
#define USBA_FRAMEWORK
/*
* print buffer protected by mutex for debug stuff. the mutex also
* ensures serializing debug messages
*/
static kmutex_t usba_print_mutex;
static char usba_print_buf[USBA_PRINT_BUF_LEN];
/*
* debug stuff
*/
#define USBA_DEBUG_SIZE_EXTRA_ALLOC 8
#ifdef DEBUG
#define USBA_DEBUG_BUF_SIZE \
(0x40000 - USBA_DEBUG_SIZE_EXTRA_ALLOC)
#else
#define USBA_DEBUG_BUF_SIZE \
(0x4000 - USBA_DEBUG_SIZE_EXTRA_ALLOC)
#endif /* DEBUG */
#define USBA_POWER_STR_SIZE 40
int usba_suppress_dprintf; /* Suppress debug printing */
int usba_clear_debug_buf_flag; /* clear debug buf */
int usba_timestamp_dprintf = 0; /* get time stamps in trace */
int usba_debug_chatty; /* L1 msg on console */
static char *usba_buf_sptr, *usba_buf_eptr;
/*
* Set to 1 to enable PM.
*/
int usb_force_enable_pm = 0;
/* USBA framework initializations */
void
{
&usbai_errmask, NULL, 0);
"usba_usbai_initialization");
}
/* USBA framework destroys */
void
{
"usba_usbai_destroy");
if (usba_debug_buf) {
}
}
/*
* debug, log, and console message handling
*/
{
} else {
}
#ifdef __lock_lint
instance_filter, 0, flags);
#endif
return ((usb_log_handle_t)hdl);
}
/*ARGSUSED*/
{
instance_filter, flags));
}
void
{
if (handle) {
}
}
void
{
if (handle) {
}
}
static void
{
if (usba_debug_buf) {
}
}
#ifdef DEBUG
char *
{
int count;
char *r = NULL;
if (usba_debug_buf) {
count = 0;
r = usba_buf_sptr;
if (*r == '\n') {
count++;
}
r--;
}
}
return (r);
}
#endif /* DEBUG */
__KVPRINTFLIKE(4);
static void
{
int instance;
char driver_name[USBA_DRVNAME_LEN];
char *msg_ptr;
if (usba_suppress_dprintf) {
return;
}
*driver_name = '\0';
/*
* Check if we have a valid buf size?
* Suppress logging to usb_buffer if so.
*/
if (usba_debug_buf_size <= 0) {
usba_buffer_dprintf = 0;
}
/*
* if there is label and dip, use <driver name><instance>:
* otherwise just use the label
*/
if (dip) {
}
} else if (usba_timestamp_dprintf) {
usba_last_timestamp = t;
if (dip) {
"+%lld->%p: %s%d: ", elapsed,
} else {
"+%lld->%p: %s: ", elapsed,
}
} else {
if (dip) {
} else {
"%s:\t", label);
}
}
/*
* stuff the message in the debug buf
*/
if (usba_buffer_dprintf) {
if (usba_debug_buf == NULL) {
KM_SLEEP);
} else if (usba_clear_debug_buf_flag) {
}
/*
* overwrite >>>> that might be over the end of the
* the buffer
*/
} else {
usba_buf_sptr += len;
}
/* add marker */
}
/*
* L4-L2 message may go to the log buf if not logged in usba_debug_buf
* L1 messages will go to the log buf in non-debug kernels and
* to console and log buf in debug kernels if usba_debug_chatty
* has been set
* L0 messages are warnings and will go to console and log buf and
* include the pathname, if available
*/
switch (level) {
case USB_LOG_L4:
case USB_LOG_L3:
case USB_LOG_L2:
if (!usba_buffer_dprintf) {
}
break;
case USB_LOG_L1:
if (dip) {
if (pathname) {
"%s (%s): %s" : "?%s (%s): %s",
} else {
}
} else {
}
break;
case USB_LOG_L0:
/* Strip the "\n" added earlier */
}
}
if (dip) {
if (pathname) {
} else {
}
} else {
}
break;
}
}
int
__KVPRINTFLIKE(4);
/* When usba10_calls.c goes away, this function can be made static again. */
int
{
char *label;
/* if there is no handle, use usba as label */
return (USB_SUCCESS);
}
/* look up the filters and set defaults */
if (hdl->lh_errlevel) {
} else {
hdl_errlevel = 0;
}
} else {
}
if (hdl->lh_instance_filter) {
} else {
}
/* if threshold is lower or mask doesn't match, we are done */
return (USB_FAILURE);
}
/*
* if we have a dip, and it is not a warning, check
* the instance number
*/
return (USB_FAILURE);
}
}
return (USB_SUCCESS);
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
int
{
int rval;
return (rval);
}
/*
* Provide a default configuration power descriptor
*/
18, /* bLength */
USBA_DESCR_TYPE_CFG_PWR_1_1, /* bDescriptorType */
0, /* SelfPowerConsumedD0_l */
0, /* SelfPowerConsumedD0_h */
0, /* bPowerSummaryId */
0, /* bBusPowerSavingD1 */
0, /* bSelfPowerSavingD1 */
0, /* bBusPowerSavingD2 */
0, /* bSelfPowerSavingD2 */
100, /* bBusPowerSavingD3 */
100, /* bSelfPowerSavingD3 */
0, /* TransitionTimeFromD1 */
0, /* TransitionTimeFromD2 */
10, /* TransitionTimeFromD3 1 Second */
};
/*
* Provide a default interface power descriptor
*/
15, /* bLength */
USBA_DESCR_TYPE_IF_PWR_1_1, /* bDescriptorType */
8, /* bmCapabilitiesFlags */
0, /* bBusPowerSavingD1 */
0, /* bSelfPowerSavingD1 */
0, /* bBusPowerSavingD2 */
0, /* bSelfPowerSavingD2 */
100, /* bBusPowerSavingD3 */
100, /* bSelfPowerSavingD3 */
0, /* TransitionTimeFromD1 */
0, /* TransitionTimeFromD2 */
10, /* TransitionTimeFromD3 1 Second */
};
static void
usba_async_req_raise_power(void *arg)
{
int rval;
/*
* To eliminate race condition between the call to power entry
* point and our call to raise power level, we first mark the
* component busy and later idle
*/
/* We are done with pmrq. Free it now */
}
/* usb function to perform async pm_request_power_change */
int
{
if (flags & USB_FLAGS_SLEEP) {
}
NULL) {
return (USB_FAILURE);
}
USB_SUCCESS) {
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static void
usba_async_req_lower_power(void *arg)
{
int rval;
/*
* To eliminate race condition between the call to power entry
* point and our call to lower power level, we call idle component
* to push ahead the PM timestamp
*/
}
/* usb function to perform async pm_request_power_change */
int
{
if (flags & USB_FLAGS_SLEEP) {
}
NULL) {
return (USB_FAILURE);
}
USB_SUCCESS) {
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/* function to see if pm is enabled for this device */
/*ARGSUSED*/
int
{
switch (usb_force_enable_pm) {
case -1:
/* no PM at all */
return (USB_FAILURE);
case 1:
/* PM on all platforms, regardless of hcd */
return (USB_SUCCESS);
case 0:
default:
break;
}
if (usba_device) {
int rval;
if (root_hub_dip == NULL) {
return (USB_FAILURE);
}
if (rval != USB_SUCCESS) {
"%s%d: no PM enabled for this device",
}
return (rval);
}
}
return (USB_FAILURE);
}
/*
* usba_handle_device_remote_wakeup:
* or interface
*/
static int
{
int rval;
"usba_handle_device_remote_wakeup: dip = 0x%p", dip);
/* get the default pipe */
/* do we own the device? */
if (usb_owns_device(dip)) {
} else {
}
bmRequest, /* bmRequest */
bRequest, /* bRequest */
USB_DEV_REMOTE_WAKEUP, /* wValue */
wIndex, /* wIndex */
0, /* wLength */
NULL, 0,
"Set/ClearFeature (RemoteWakep) failed: "
"rval = %d, cmd = %d, cr = 0x%x cb = 0x%x",
}
return (rval);
}
void
{
"pm-want-child-notification?");
}
/*
* usb_handle_remote_wakeup:
* remote wake up in the device depending upon the command
*/
int
{
int rval;
/* Obtain the raw configuration descriptor */
/* get configuration descriptor, must succeed */
/*
* If the device supports remote wakeup, and PM is enabled,
* we enable remote wakeup in the device
*/
} else {
rval = USB_FAILURE;
}
return (rval);
}
/*
* usb_create_pm_components:
* map descriptor into pm properties
*/
int
{
int n_prop = 0;
char *drvname;
char str[USBA_POWER_STR_SIZE];
char *pm_comp[USBA_N_PMCOMP];
return (USB_FAILURE);
}
/* Obtain the raw configuration descriptor */
/* get configuration descriptor, must succceed */
*pwr_states = 0;
/*
* Now start creating the pm-components strings
*/
/*
* if the device is bus powered we look at the bBusPowerSavingDx
* fields else we look at bSelfPowerSavingDx fields.
* OS and USB power states are numerically reversed,
*
* Here is the mapping :-
* OS State USB State
* 0 D3 (minimal or no power)
* 1 D2
* 2 D1
* 3 D0 (Full power)
*
* if we own the whole device, we look at the config pwr descr
* else at the interface pwr descr.
*/
if (usb_owns_device(dip)) {
/* Parse the configuration power descriptor */
if (rval != USBA_CFG_PWR_DESCR_SIZE) {
"usb_create_pm_components: "
"usb_parse_cfg_pwr_descr returns length of %d, "
return (USB_FAILURE);
}
if (cfg_attrib & USB_CFG_ATTR_SELFPWR) {
} else {
}
} else {
/* Parse the interface power descriptor */
0, /* XXXX alt interface index */
if (rval != USBA_IF_PWR_DESCR_SIZE) {
"usb_create_pm_components: "
"usb_parse_if_pwr_descr "
"returns length of %d, "
return (USB_FAILURE);
}
if (cfg_attrib & USB_CFG_ATTR_SELFPWR) {
} else {
}
}
/* walk thru levels and create prop level=name strings */
"%d=USB D%d State",
KM_SLEEP);
}
}
"usb_create_pm_components: pwr_states: %x", *pwr_states);
/* now create the actual components */
if (rval == DDI_PROP_SUCCESS) {
rval = USB_SUCCESS;
} else {
rval = USB_FAILURE;
}
/* display & delete properties */
"usb_create_pm_components: The properties are:");
for (i = 0; i < n_prop; i++) {
"\t%s", pm_comp[i]);
}
return (rval);
}
/*
* Generic Functions to set the power level of any usb device
*
* Since OS and USB power states are numerically reverse,
* Here is the mapping :-
* OS State USB State
* 0 D3 (minimal or no power)
* 1 D2
* 2 D1
* 3 D0 (Full power)
*/
/* set device power level to 0 (full power) */
/*ARGSUSED*/
int
{
"usb_set_device_pwrlvl0 : Not Yet Implemented");
return (USB_SUCCESS);
}
/* set device power level to 1 */
/*ARGSUSED*/
int
{
"usb_set_device_pwrlvl1 : Not Yet Implemented");
return (USB_SUCCESS);
}
/* set device power level to 2 */
/*ARGSUSED*/
int
{
"usb_set_device_pwrlvl2 : Not Yet Implemented");
return (USB_SUCCESS);
}
/* set device power level to 3 */
/*ARGSUSED*/
int
{
"usb_set_device_pwrlvl3 : Not Yet Implemented");
return (USB_SUCCESS);
}
/*
* USB event management
*/
/*
* usb_register_hotplug_cbs:
* Register to get callbacks for hotplug events
*/
/*ARGSUSED*/
int
int (*disconnect_event_handler)(dev_info_t *),
int (*reconnect_event_handler)(dev_info_t *))
{
(reconnect_event_handler == NULL)) {
"usb_register_hotplug_cbs: Bad argument(s)");
return (USB_FAILURE);
}
"usb_register_hotplug_cbs: entry");
/*
* The event list searches by ddi_get_eventcookie calls below, go
* through hubd and so do not apply to host controllers.
*/
"usb_register_hotplug_cbs: get rm cookie failed");
goto fail;
}
}
"usb_register_hotplug_cbs: add disconnect handler failed");
goto fail;
}
"usb_register_hotplug_cbs: get ins cookie failed");
goto fail;
}
}
"usb_register_hotplug_cbs: add reconnect handler failed");
goto fail;
}
return (USB_SUCCESS);
fail:
return (USB_FAILURE);
}
/*
* usb_unregister_hotplug_cbs:
* Unregister hotplug callbacks
*/
/*ARGSUSED*/
void
{
}
/*
* usb_register_event_cbs:
* Register to get callbacks for USB events
*/
/*ARGSUSED*/
int
{
return (USB_FAILURE);
}
/*
* The event list searches by ddi_get_eventcookie calls below, go
* through hubd and so do not apply to host controllers.
*/
goto fail;
}
}
goto fail;
}
}
goto fail;
}
}
goto fail;
}
}
goto fail;
}
}
goto fail;
}
}
goto fail;
}
}
goto fail;
}
}
return (USB_SUCCESS);
fail:
return (USB_FAILURE);
}
/*
* usb_unregister_event_cbs:
* Unregister all event callbacks
*/
/*ARGSUSED*/
void
{
}
}
}
}
}