hwahc.c revision ff0e937b36dcde1a47ff7b00aa76a491c0dc07a8
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* The Data Transfer Interface driver for Host Wire Adapter device
*
* HWA device has two interfaces, one is the data transfer interface,
* another is the radio control interface. This driver (hwahc) is only
* for data transfer interface support, but it depends on the radio
* control interface driver (hwarc) to work. That means the hwarc
* driver must be loaded while the hwahc is working. This is now
* ensured by holding hwarc open until hwahc detaches or powers down.
*
* The data transfer interface has three endpoints besides the default
* control endpoint which is shared between the two interfaces. The
* three endpoints are:
*
* - notification endpoint (intr in type, for asynchronous event
* notifications and transfer status notifications)
*
* - data transfer OUT endpoint (bulk out type, for sending transfer
* requests and transfer data from the host to the HWA device)
*
* - data transfer IN endpoint (bulk in type, for returning transfer
* status and transfer data from the HWA device to the host)
*
* The HWA device is a USB 2.0 device, so it supports the standard USB
* requests defined in chapter 9 of USB 2.0 specification as other USB
* client devices. But its most important functionality is to work as
* a wireless USB host. This means the hwahc driver needs to supply
* host controller functionalities, which include children hotplug
* support and data transfer support to children device endpoints.
*
* So hwahc driver is implemented as a nexus driver and it follows the
* event mechanism in existing USBA framework to support children
* hotplug events.
*
* The hwahc driver works as the root-hub on wireless USB bus. And it
*
* remote pipe (rpipe) mechanism. The rpipe descriptor on the HWA defines
* the attributes of a wireless USB transfer, such as the transfer type,
* the target device address, the target endpoint address and the max
* packet size. And the transfer requests through data transfer OUT
* endpoint will take a certain rpipe as the transfer target, thus
* fulfills the data transfer across buses. Refer to chapter 8 of WUSB
* 1.0 specification for details of this.
*/
#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER 0
void *hwahc_statep;
/* number of instances */
#define HWAHC_INSTS 1
/* default value for set number DNTS slots request */
#define HWAHC_DEFAULT_DNTS_SLOT_NUM 4
/* debug support */
/* bus config debug flag */
/*
* Use the default GTK for the whole life of HWA driver.
* Not so compatible with WUSB spec.
*/
extern usb_log_handle_t whcdi_log_handle;
/*
* Function Prototypes
*/
/* driver operations (dev_ops) entry points */
static int hwahc_power(dev_info_t *, int, int);
/* bus_ops entry points */
void *, void *);
char *, ddi_eventcookie_t *);
static int hwahc_busop_add_eventcall(
void (*)(dev_info_t *, ddi_eventcookie_t, void *, void *),
void *, ddi_callback_id_t *);
void *, dev_info_t **);
void *);
/* hotplug and power management supporting functions */
static int hwahc_cpr_suspend(dev_info_t *);
static int hwahc_cpr_resume(dev_info_t *);
static void hwahc_destroy_pm_components(hwahc_state_t *);
static void hwahc_pm_busy_component(hwahc_state_t *);
static void hwahc_pm_idle_component(hwahc_state_t *);
static int hwahc_pwrlvl0(hwahc_state_t *);
static int hwahc_pwrlvl1(hwahc_state_t *);
static int hwahc_pwrlvl2(hwahc_state_t *);
static int hwahc_pwrlvl3(hwahc_state_t *);
static int hwahc_hc_channel_suspend(hwahc_state_t *);
/* hardware initialization and deinitialization functions */
static int hwahc_parse_security_data(wusb_secrt_data_t *,
usb_cfg_data_t *);
static void hwahc_print_secrt_data(hwahc_state_t *);
static int hwahc_hub_attach(hwahc_state_t *);
static int hwahc_hub_detach(hwahc_state_t *);
static int hwahc_hc_initial_start(hwahc_state_t *);
static int hwahc_hc_final_stop(hwahc_state_t *);
static int hwahc_wa_start(hwahc_state_t *);
static void hwahc_wa_stop(hwahc_state_t *);
static int hwahc_hc_channel_start(hwahc_state_t *);
static int hwahc_hc_channel_stop(hwahc_state_t *);
static void hwahc_hc_data_init(hwahc_state_t *);
static void hwahc_hc_data_fini(hwahc_state_t *);
/* ioctl support */
cred_t *, int *);
cred_t *, int *);
/* callbacks registered to USBA */
static int hwahc_cleanup_child(dev_info_t *);
/* data transfer and notification handling */
static void hwahc_stop_result_thread(hwahc_state_t *);
static void hwahc_result_thread(void *);
static void hwahc_notif_thread(void *);
static void hwahc_drain_notif_queue(hwahc_state_t *);
static void hwahc_trust_timeout_handler(void *arg);
/* hwa specific requests */
/* helper functions */
static struct cb_ops hwahc_cb_ops = {
hwahc_open, /* Open */
hwahc_close, /* Close */
nodev, /* Strategy */
nodev, /* Print */
nodev, /* Dump */
nodev, /* Read */
nodev, /* Write */
hwahc_ioctl, /* Ioctl */
nodev, /* Devmap */
nodev, /* Mmap */
nodev, /* Segmap */
nochpoll, /* Poll */
ddi_prop_op, /* cb_prop_op */
NULL, /* Streamtab */
D_MP /* Driver compatibility flag */
};
static struct bus_ops hwahc_busops = {
nullbusmap, /* bus_map */
NULL, /* bus_get_intrspec */
NULL, /* bus_add_intrspec */
NULL, /* bus_remove_intrspec */
NULL, /* bus_map_fault */
ddi_dma_map, /* bus_dma_map */
ddi_dma_mctl, /* bus_dma_ctl */
hwahc_bus_ctl, /* bus_ctl */
ddi_bus_prop_op, /* bus_prop_op */
hwahc_busop_get_eventcookie, /* bus_get_eventcookie */
hwahc_busop_add_eventcall, /* bus_add_eventcall */
hwahc_busop_remove_eventcall, /* bus_remove_eventcall */
NULL, /* bus_post_event */
NULL, /* bus_intr_ctl */
hwahc_bus_config, /* bus_config */
hwahc_bus_unconfig, /* bus_unconfig */
NULL, /* bus_fm_init */
NULL, /* bus_fm_fini */
NULL, /* bus_fm_access_enter */
NULL, /* bus_fm_access_exit */
NULL, /* bus_power */
};
DEVO_REV, /* Devo_rev */
0, /* Refcnt */
hwahc_info, /* Info */
nulldev, /* Identify */
nulldev, /* Probe */
hwahc_attach, /* Attach */
hwahc_detach, /* Detach */
nodev, /* Reset */
&hwahc_cb_ops, /* Driver operations */
&hwahc_busops, /* Bus operations */
hwahc_power, /* Power */
ddi_quiesce_not_needed, /* devo_quiesce */
};
static struct modldrv hwahc_modldrv = {
"WUSB hwa-hc driver",
};
static struct modlinkage modlinkage = {
};
/* events from parent */
static usb_event_t hwahc_events = {
};
/*
* events support for children
* A map tween USBA_EVENTs and DDI_EVENTs.
*/
static ndi_event_definition_t hwahc_ndi_event_defs[] = {
};
#define HWAHC_N_NDI_EVENTS \
(sizeof (hwahc_ndi_event_defs) / sizeof (ndi_event_definition_t))
static ndi_event_set_t hwahc_ndi_events = {
/* transfer callbacks */
static wusb_wa_cb_t hwahc_cbs = {
};
/*
* Module-wide initialization routine.
*/
int
_init(void)
{
int rval;
HWAHC_INSTS)) != 0) {
return (rval);
}
}
return (rval);
}
/*
* Module-wide tear-down routine.
*/
int
_fini(void)
{
int rval;
/* Release per module resources */
}
return (rval);
}
int
{
}
/*
* hwahc_info:
* Get minor number, instance number, etc.
*/
/*ARGSUSED*/
static int
{
int error = DDI_FAILURE;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_SUCCESS;
}
} else {
}
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
/*
* hwahc_attach:
* Attach or resume.
*
* For attach, initialize state and device, including:
* state variables, locks, device node,
* resource initialization, event registration,
* device registration with system
* power management, hotplugging
* For resume, restore device and state
*/
static int
{
struct usb_cfg_data *cfg_data;
int rval;
char *pathname;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
"hwahc_attach: failed");
return (DDI_FAILURE);
}
/*
* Allocate soft state information.
*/
if (rval != DDI_SUCCESS) {
"hwahc_attach: cannot allocate soft state for instance %d",
instance);
return (USB_FAILURE);
}
"hwahc_attach: get soft state failed for instance %d",
instance);
return (USB_FAILURE);
}
/* initialize hc state */
/* register with USBA as client driver */
"hwahc_attach: client attach failed");
goto fail;
}
USB_SUCCESS) {
"hwahc_attach: cannot get dev_data");
goto fail;
}
/* initialize mutex and cv */
/* initialize data transfer function related structure */
"hwahc_attach: init wa data failed");
goto fail;
}
/* parse the security descrs from the configuration descr cloud */
USB_SUCCESS) {
"hwahc_attach: parse security descrs failed");
goto fail;
}
/* now create components to power manage this device */
/*
* Event definition and registration
*
* allocate a new NDI event handle as a nexus driver
*/
/*
* bind our NDI events with the event handle,
* i.e. Define the events set we're to support as a nexus driver.
*
* These events will be used by bus_ops functions to register callbacks.
*/
NDI_SLEEP)) {
"hwahc_attach: binding event set failed");
goto fail;
}
/*
* Register USB events to USBA(the parent) to get callbacks as a
* child of (root) hub
*/
"hwahc_attach: register_events failed");
goto fail;
}
/* create minor nodes */
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
"hwahc_attach: cannot create minor node");
goto fail;
}
/* register this hc instance with usba HCD interface */
/* use parent dma attr here */
"hwahc_attach: usba_hcdi_register failed");
goto fail;
}
/* create hub minor node and register to usba HUBD interface */
"hwahc_attach: hub attach failed");
goto fail;
}
/* intialize WUSB host function related structure */
/* can be combined with wusb_wa_data_init() */
goto fail;
}
/* report this dev */
goto fail;
}
return (DDI_SUCCESS);
fail:
/* log this message to usba_debug_buf */
if (hwahcp) {
if (rval != USB_SUCCESS) {
"failure to complete cleanup after attach failure");
}
}
return (DDI_FAILURE);
}
/*
* hwahc_detach:
* detach or suspend driver instance
*
* Note: in detach, only contention threads is from pm and disconnnect.
*/
static int
{
int rval = DDI_FAILURE;
"hwahc_detach: cmd = %d", cmd);
switch (cmd) {
case DDI_DETACH:
"offline uwb device for dip: 0x%p", (void *)dip);
/* offline the hwarc interface */
(void) uwb_dev_offline(dip);
if (hwahcp) {
}
break;
case DDI_SUSPEND:
break;
default:
break;
}
}
/*
* hwahc_cleanup:
* clean up on attach failure or detach
*/
static int
{
"hwahc_cleanup: start");
goto done;
}
/*
* deallocate events, if events are still registered
* (ie. children still attached) then we have to fail the detach
*/
if (hwahcp->hwahc_ndi_event_hdl &&
"hwahc_cleanup: ndi_event_free_hdl failed");
return (USB_FAILURE);
}
/* unregister events */
}
/* unregister the instance with usba HCD interface */
}
/* stop the hw if it is enabled */
(void) hwahc_hc_final_stop(hwahcp);
}
/* can be combined with wusb_wa_data_fini() */
}
/* deinitialize the WUSB host function related structure */
}
/* destroy power management components */
}
/* unregister the instance from usba HUBD interface */
return (USB_FAILURE);
}
}
if (hwahcp->hwahc_hcdi_ops) {
}
/* free security descrs */
sizeof (usb_encryption_descr_t) *
}
/* deinitialize data transfer function related structure */
}
/* remove all the minor nodes */
}
/* destroy mutex and cv */
done:
/* unregister the client driver from usba */
"hwahc_cleanup: end");
/* remove all properties created */
/* free the soft state information */
return (USB_SUCCESS);
}
/*ARGSUSED*/
static int
{
return (ENXIO);
}
"hwahc_open: start");
/* exclusive open */
return (EBUSY);
}
return (EIO);
}
/* raise to full power and keep it until close */
"hwahc_open: end");
return (0);
}
/*ARGSUSED*/
static int
{
return (ENXIO);
}
"hwahc_close: start");
if (hwahcp->hwahc_open_count == 0) {
"hwahc_close: already closed");
return (EINVAL);
}
"hwahc_close: end");
return (0);
}
/* retrieve port number from devctl data */
static usb_port_t
{
/* Get which port to operate on. */
"hwahc_get_port_num: port lookup failed");
port = 0;
}
"hwahc_get_port_num: hwahcp=0x%p, port=%d", (void *)hwahcp,
port);
return ((usb_port_t)port);
}
/* return the child dip on a certain port */
static dev_info_t *
{
/* check port range to prevent an illegal number */
return (NULL);
}
return (child_dip);
}
/*
* hwahc_cfgadm_state:
*
* child_dip list child_state cfgadm_state
* -------------- ---------- ------------
* != NULL connected configured or
* unconfigured
* != NULL not connected disconnect but
* NULL connected logically disconnected
* NULL not connected empty
*/
static uint_t
{
return (HWAHC_CFGADM_INVALID);
}
if (dev_info) {
if (child_dip &&
!i_ddi_devi_attached(child_dip))) {
} else if (!child_dip) {
} else {
}
if (child_dip) {
} else {
}
} else {
if (child_dip) {
} else {
}
}
} else {
}
"hwahc_cfgadm_state: hwahcp=0x%p, port=%d state=0x%x",
return (state);
}
/* cfgadm ioctl support, now only implements list function */
/* ARGSUSED */
static int
{
usb_port_t port = 0;
int rv = 0;
char *msg;
/* read devctl ioctl data */
if ((cmd != DEVCTL_AP_CONTROL) &&
return (EFAULT);
}
switch (cmd) {
case DEVCTL_AP_DISCONNECT:
case DEVCTL_AP_UNCONFIGURE:
case DEVCTL_AP_CONFIGURE:
"hwahc_cfgadm_ioctl: dev already gone");
if (dcp) {
}
return (EIO);
}
/* FALLTHROUGH */
case DEVCTL_AP_GETSTATE:
"hwahc_cfgadm_ioctl: bad port");
if (dcp) {
}
return (EINVAL);
}
break;
case DEVCTL_AP_CONTROL:
break;
default:
if (dcp) {
}
return (ENOTTY);
}
/* should not happen, just in case */
if (dcp) {
}
return (EIO);
}
switch (cmd) {
case DEVCTL_AP_DISCONNECT:
/* TODO: not supported now */
break;
case DEVCTL_AP_UNCONFIGURE:
/* TODO: not supported now */
break;
case DEVCTL_AP_CONFIGURE:
/* TODO: not supported now */
break;
case DEVCTL_AP_GETSTATE:
/* port previously 'disconnected' by cfgadm */
break;
break;
case HWAHC_CFGADM_CONFIGURED:
break;
break;
case HWAHC_CFGADM_EMPTY:
default:
break;
}
ap_state.ap_error_code = 0;
ap_state.ap_in_transition = 0;
"DEVCTL_AP_GETSTATE: "
"ostate=0x%x, rstate=0x%x, condition=0x%x",
/* copy the return-AP-state information to the user space */
}
break;
case DEVCTL_AP_CONTROL:
{
/*
* Generic devctl for hardware-specific functionality.
* For list of sub-commands see hubd_impl.h
*/
/* copy user ioctl data in first */
#ifdef _MULTI_DATAMODEL
break;
}
} else
#endif /* _MULTI_DATAMODEL */
mode) != 0) {
break;
}
"DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
/*
* returns a 32-bit number.
*/
break;
}
case USB_DESCR_TYPE_DEV:
msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
/* uint32 so this works 32/64 */
"%s: get_size copyout failed", msg);
break;
}
} else { /* send out the actual descr */
/* check child_dip */
break;
}
"%s: bufsize passed (%d) != sizeof "
"usba_device_descr_t (%d)", msg,
break;
}
if (ddi_copyout((void *)dev_descrp,
"%s: copyout failed.", msg);
break;
}
}
break;
case USB_DESCR_TYPE_CFG:
{
if ((child_dip =
break;
}
msg = "DEVCTL_AP_CONTROL: GET_CONFIG_DESC";
"%s: get_size copyout failed", msg);
break;
}
} else { /* send out the actual descr */
"%s: bufsize passed (%d) != size "
"of cfg_descr (%d)", msg,
break;
}
if (ddi_copyout((void *)cfg_descr,
"%s: copyout failed.", msg);
break;
}
}
break;
}
case USB_DESCR_TYPE_STRING:
{
char *str;
msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
/* recheck */
if ((child_dip =
break;
}
case HUBD_MFG_STR:
break;
case HUBD_PRODUCT_STR:
break;
case HUBD_SERIALNO_STR:
break;
case HUBD_CFG_DESCR_STR:
break;
default:
"%s: Invalid string request", msg);
break;
} /* end of switch */
if (rv != 0) {
break;
}
"%s: copyout of size failed.", msg);
break;
}
} else {
if (size == 0) {
"%s: String is NULL", msg);
break;
}
"%s: string buf size wrong", msg);
break;
}
"%s: copyout failed.", msg);
break;
}
}
break;
}
case HUBD_GET_CFGADM_NAME:
{
const char *name;
/* recheck */
if ((child_dip =
break;
}
name = "unsupported";
}
msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
if (ddi_copyout((void *)&name_len,
"%s: copyout of size failed", msg);
break;
}
} else {
"%s: string buf length wrong", msg);
break;
}
"%s: copyout failed.", msg);
break;
}
}
break;
}
/*
* Return the config index for the currently-configured
* configuration.
*/
case HUBD_GET_CURRENT_CONFIG:
{
msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
/*
* Return the config index for the configuration
* currently in use.
* Recheck if child_dip exists
*/
if ((child_dip =
break;
}
if (ddi_copyout((void *)&size,
"%s: copyout of size failed.", msg);
break;
}
} else {
"%s: buffer size wrong", msg);
break;
}
if (ddi_copyout((void *)&config_index,
"%s: copyout failed", msg);
}
}
break;
}
case HUBD_GET_DEVICE_PATH:
{
char *path;
msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
/* Recheck if child_dip exists */
if ((child_dip =
break;
}
/* ddi_pathname doesn't supply /devices, so we do. */
if (ddi_copyout((void *)&size,
"%s: copyout of size failed.", msg);
}
} else {
"%s: buffer wrong size.", msg);
} else if (ddi_copyout((void *)path,
"%s: copyout failed.", msg);
}
}
break;
}
case HUBD_REFRESH_DEVDB:
msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
}
break;
default:
} /* end switch */
break;
}
default:
}
if (dcp) {
}
return (rv);
}
/* update CHID for the hc driver, return 0 on success */
static int
{
/* same as the old CHID, return success */
return (0);
}
/*
* stop hw from working before updating CHID
* this may not be necessary but so far we don't know
* other ways to do it safely
*/
/* use final_stop to fully stop the hwa */
return (EIO);
}
/* restart the host */
return (EIO);
}
return (0);
}
/* hc is stopped or partially stopped, simply update */
return (0);
}
/*
* wusbadm ioctl support
*/
/* ARGSUSED */
static int
{
int rv = 0;
"hwahc_wusb_ioctl: user must have SYS_DEVICE privilege,"
"cmd=%x", cmd);
return (EPERM);
}
switch (cmd) {
case WUSB_HC_GET_DSTATE: /* Get device state: wusbadm list */
{
usb_port_t port = 0;
mode) != 0) {
break;
}
} else {
/* cdid not found */
}
"hwahc_wusb_ioctl: hc_data=%p, port = %d, state=%d",
/* Get the bind device node name of this child */
}
"WUSB_HC_GET_DSTATE: copyout failed");
}
break;
}
case WUSB_HC_GET_MAC_ADDR: /* Get host MAC addr */
{
/*
* get UWB 48-bit mac address
* Section 8.6.2.2.
*/
USB_SUCCESS) {
"WUSB_HC_GET_MAC_ADDR: get mac failed");
break;
}
6, mode) != 0) {
"WUSB_HC_GET_MAC_ADDR: copyout failed");
}
break;
}
case WUSB_HC_ADD_CC:
{
/*
* add a new device CC to host's list: wusbadm associate
* Or, the application can pass in a fake CC with only CHID set
* to set the host's CHID.
*/
break;
}
/* update CHID only when cc list is empty */
break;
}
} else {
/* fail if the CHID in the new CC does not match */
16) != 0) {
break;
}
}
break;
}
case WUSB_HC_REM_CC:
{
mode) != 0) {
break;
}
/* check if the CHID in the CC matches */
break;
}
/* if the device is connected, disconnect it first */
/*
* clean up host side state, device not
* really disconnected. But user can safely remove
* the device now.
*/
}
break;
}
case WUSB_HC_SET_CHANNEL: /* for debug purpose */
{
"WUSB_HC_SET_CHANNEL ioctl: same as existing");
break;
}
/* beacon is already started, stop it first */
"WUSB_HC_SET_CHANNEL ioctl: "
"stop beacon failed");
break;
}
/* update channel number */
/* restart beacon on the new channel */
channel) != USB_SUCCESS) {
"WUSB_HC_SET_CHANNEL ioctl: "
"restart beacon failed");
}
break;
}
/* beacon is not started, simply update channel number */
break;
}
case WUSB_HC_START:
{
int flag;
"WUSB_HC_START ioctl: already started");
break;
}
/*
* now we start hc only when the cc list is not NULL
* this limitation may be removed if we support
* numeric association, but CHID needs to be set
* in advance for the hc to work
*/
"WUSB_HC_START ioctl: cc list not inited");
break;
}
/* cannot be both */
"WUSB_HC_START ioctl: flag cannot coexist");
break;
}
/*
* init Mac layer 16-bit dev addr. it is important for
* authentication. It'd be better to let UWB provide
* this address.
*/
/* set UWB 16-bit dev address */
dev_addr) != USB_SUCCESS) {
"WUSB_HC_START ioctl: set dev addr failed");
break;
}
/* verify the dev addr is set correctly */
&dev_addr) != USB_SUCCESS) {
"WUSB_HC_START ioctl: get dev addr failed");
break;
}
"host dev addr = 0x%x", dev_addr);
}
/* start functions of wusb host */
if ((flag & WUSB_HC_INITIAL_START) &&
}
} else if ((flag & WUSB_HC_CHANNEL_START) &&
}
} else {
"WUSB_HC_START ioctl: unknown flag (%d) or "
}
break;
}
case WUSB_HC_STOP:
{
int flag;
/* cannot be both */
"WUSB_HC_STOP ioctl: flag cannot coexist");
break;
}
if (flag & WUSB_HC_FINAL_STOP) {
}
} else if (flag & WUSB_HC_CHANNEL_STOP) {
}
} else {
/* must be one of the STOP flag */
"WUSB_HC_STOP ioctl: invalid flag = %d", flag);
}
/* REM_ALL_CC flag is optional */
if (hc_data->hc_cc_list) {
}
}
break;
}
case WUSB_HC_GET_HSTATE:
{
int state;
} else {
switch (hwahcp->hwahc_hw_state) {
case HWAHC_HW_STOPPED:
break;
case HWAHC_HW_STARTED:
break;
case HWAHC_HW_CH_STOPPED:
/*
* app can mark the hwa as disabled
* for this state
*/
break;
}
}
sizeof (int), mode) != 0) {
"WUSB_HC_GET_HSTATE: copyout failed");
}
break;
}
default:
"hwahc_ioctl: unsupported command");
}
return (rv);
}
static int
{
int rval;
return (ENXIO);
}
"hwahc_ioctl: cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
/* for cfgadm cmd support */
} else {
/* for wusbadm cmd support */
}
return (rval);
}
/* return the port number corresponding the child dip */
static usb_port_t
{
break;
}
}
return (port);
}
/*
*/
static void
struct attachspec *as)
{
/* we don't need additional process for post-attach now */
"hwahc_post_attach: rdip = 0x%p result = %d", (void *) rdip,
}
static void
struct detachspec *as)
{
/* we don't need additional process for post-detach now */
"hwahc_post_detach: rdip = 0x%p result = %d", (void *) rdip,
}
/*
* bus ctl support.
* To support different operations, such as a PreAttach preparation,
* PostAttach operations. HWA only process the interested operations.
* Other general ones are processed by usba_bus_ctl().
*/
static int
void *arg,
void *result)
{
struct attachspec *as;
struct detachspec *ds;
return (DDI_FAILURE);
}
"hwahc_bus_ctl:\n\t"
"dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
switch (op) {
case DDI_CTLOPS_ATTACH:
case DDI_PRE :
/* nothing to do basically */
"DDI_PRE DDI_CTLOPS_ATTACH");
break;
case DDI_POST :
(struct attachspec *)arg);
break;
}
break;
case DDI_CTLOPS_DETACH:
case DDI_PRE :
/* nothing to do basically */
"DDI_PRE DDI_CTLOPS_DETACH");
break;
case DDI_POST :
(struct detachspec *)arg);
break;
}
break;
case DDI_CTLOPS_REPORTDEV: /* the workhorse behind ddi_report_dev */
{
if (usb_owns_device(rdip)) {
(void) snprintf(compat_name,
sizeof (compat_name),
"usb%x,%x",
} else if (usba_owns_ia(rdip)) {
(void) snprintf(compat_name,
sizeof (compat_name),
"usbia%x,%x.config%x.%x",
} else {
(void) snprintf(compat_name,
sizeof (compat_name),
"usbif%x,%x.config%x.%x",
}
"?USB %x.%x %s (%s) operating wirelessly with "
"HWA device: "
"%s@%s, %s%d at bus address %d\n",
"interface"))),
if (name[0] != '\0') {
}
break;
}
default:
/* pass to usba to handle */
}
return (DDI_SUCCESS);
}
/*
* bus enumeration entry points
* Configures the named device(BUS_CONFIG_ONE) or all devices under
* the nexus(BUS_CONFIG_ALL). Drives devinfo state to DS_READY,i.e.device
* is fully operational.
*
* This operation is driven from devfs(reading /devices), devctl, libdevinfo;
* or from within the kernel to attach a boot device or layered underlying
* driver.
*/
static int
{
return (NDI_FAILURE);
}
"hwahc_bus_config: op=%d", op);
if (hwahc_bus_config_debug) {
flag |= NDI_DEVI_DEBUG;
}
return (rval);
}
/*
* Unconfigures the named device or all devices under the nexus. The
* devinfo state is not DS_READY anymore.
* This operations is driven by modunload, devctl or DR branch removal or
* rem_drv(1M).
*/
static int
void *arg)
{
return (NDI_FAILURE);
}
"hwahc_bus_unconfig: op=%d", op);
if (hwahc_bus_config_debug) {
flag |= NDI_DEVI_DEBUG;
}
flag |= NDI_DEVI_REMOVE;
}
/* serialize access */
/* unconfig children, detach them */
/* logically zap children's list */
}
/* fill in what's left */
if (usba_device == NULL) {
continue;
}
}
/* physically zap the children we didn't find */
"hwahc_bus_unconfig: physically zap port %d", port);
/* zap the dip and usba_device structure as well */
/* dip freed in usba_destroy_child_devi */
/* free hc_dev_infos[port] */
continue;
}
/* stop the device's trust timer before deallocate it */
sizeof (usb_encryption_descr_t) *
}
if (dev_info->wdev_uwb_descr) {
sizeof (usb_uwb_cap_descr_t));
}
}
}
"hwahc_bus_unconfig: rval=%d", rval);
return (rval);
}
/*
* busctl event support
*
* Called by ndi_busop_get_eventcookie(). Return a event cookie
* associated with one event name.
* The eventname should be the one we defined in hwahc_ndi_event_defs
*/
static int
char *eventname,
{
return (NDI_FAILURE);
}
"hwahc_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
"(dip=%s%d, rdip=%s%d)",
/* return event cookie, iblock cookie, and level */
}
/*
* Add event handler for a given event cookie
*/
static int
void *bus_impldata),
{
return (NDI_FAILURE);
}
"hwahc_busop_add_eventcall: dip=0x%p, rdip=0x%p "
"cookie=0x%p, cb=0x%p, arg=0x%p",
"(dip=%s%d, rdip=%s%d, event=%s)",
/* Set flag on children registering events */
break;
break;
default:
break;
}
/* add callback to our event set */
}
/*
* Remove a callback previously added by bus_add_eventcall()
*/
static int
{
return (NDI_FAILURE);
}
"hwahc_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
(void *)id->ndi_evtcb_cookie);
"(dip=%s%d, rdip=%s%d, event=%s)",
id->ndi_evtcb_cookie));
/* remove event registration from our event set */
}
/*
* hwahc_post_event
* post event to a single child on the port depending on the type, i.e.
* to invoke the child's registered callback.
*/
static void
{
int rval;
"hwahc_post_event: port=%d event=%s", port,
/*
* Hotplug daemon may be attaching a driver that may be registering
* event callbacks. So it already has got the device tree lock and
* event handle mutex. So to prevent a deadlock while posting events,
* we grab and release the locks in the same order.
*/
switch (type) {
/* stop this device's timer to prevent its further process */
/* Clear the registered event flag */
/*
* Mark the dip for deletion only after the driver has
* seen the disconnect event to prevent cleanup thread
* from stepping in between.
*/
#ifndef __lock_lint
#endif
break;
/*
* persistent pipe close for this event is taken care by the
* caller after verfying that all children can suspend
*/
break;
/*
* Check if this child has missed the disconnect event before
* it registered for event callbacks
*/
/* clear the flag and post disconnect event */
(void) ndi_event_do_callback(
}
/*
* Mark the dip as reinserted to prevent cleanup thread
* from stepping in.
*/
#ifndef __lock_lint
#endif
if (rval != USB_SUCCESS) {
"failed to reopen all pipes on reconnect");
}
/*
* We might see a connect event only if hotplug thread for
* disconnect event don't run in time.
* Set the flag again, so we don't miss posting a
* disconnect event.
*/
break;
/*
* Check if this child has missed the pre-suspend event before
* it registered for event callbacks
*/
/* clear the flag and post pre_suspend event */
(void) ndi_event_do_callback(
}
usba_device->usb_no_cpr = 0;
/*
* Since the pipe has already been opened by whub
* at DDI_RESUME time, there is no need for a
* persistent pipe open
*/
/*
* Set the flag again, so we don't miss posting a
* pre-suspend event. This enforces a tighter
* dev_state model.
*/
break;
}
}
/*
* hwahc_run_callbacks:
* Send an event to all children
*/
static void
{
"hwahc_run_callbacks:");
}
}
}
/*
* hwahc_disconnect_event_cb:
* Called when hwa device hotplug-removed.
* Close pipes
* Post event to child
* Set state to DISCONNECTED
*/
static int
{
int circ;
return (USB_FAILURE);
}
"hwahc_disconnect_event_cb: dip = 0x%p", (void *)dip);
"hwahc_disconnect_event_cb: devstate= %d hw-state=%d",
switch (hwahcp->hwahc_dev_state) {
case USB_DEV_ONLINE:
case USB_DEV_PWRED_DOWN:
}
/* FALLTHROUGH */
case USB_DEV_SUSPENDED:
/* remain in this state */
break;
case USB_DEV_DISCONNECTED:
"hwahc_disconnect_event_cb: already disconnected");
break;
default:
"hwahc_disconnect_event_cb: illegal devstate=%d",
break;
}
return (USB_SUCCESS);
}
/*
* hwahc_reconnect_event_cb:
* Called with device hotplug-inserted
* Restore state
*/
static int
{
int circ;
return (USB_FAILURE);
}
"hwahc_reconnect_event_cb: dip = 0x%p", (void *)dip);
return (USB_SUCCESS);
}
/*
* hwahc_pre_suspend_event_cb:
* Called before HWA device suspend
*/
static int
{
int circ;
return (USB_FAILURE);
}
"hwahc_pre_suspend_event_cb: dip = 0x%p", (void *)dip);
"hwahc_pre_suspend_event_cb: start, hw state = %d, softstate = %d",
/* keep PM out till we see a cpr resume */
(void) hwahc_pm_busy_component(hwahcp);
/*
* rc driver is always suspended first, that fails the hc suspend.
* need to suspend hc before rc is suspended, so move the suspend
* operations here
*/
"hwahc_pre_suspend_event_cb: dev_state = %d",
return (USB_SUCCESS);
}
/*
* notify children the host is going to stop
*/
(void) hwahc_hc_channel_suspend(hwahcp);
}
/* stop the hc from functioning */
}
"hwahc_pre_suspend_event_cb: end, devstate=%d "
"hwstate=%d softstate = %d",
return (USB_SUCCESS);
}
/*
* hwahc_post_resume_event_cb:
* Call after HWA device resume
*/
static int
{
int circ;
return (USB_FAILURE);
}
"hwahc_post_resume_event_cb: dip = 0x%p", (void *)dip);
"hwahc_post_resume_event_cb: start, hw state = %d, softstate = %d",
/* need to place hc restore here to make sure rc has resumed */
/* enable PM */
(void) hwahc_pm_idle_component(hwahcp);
return (USB_SUCCESS);
}
/*
* hwahc_restore_device_state:
* Called during hotplug-reconnect and resume.
* re-enable power management
* Verify the device is the same as before the disconnect/suspend.
* Restore device state
* Thaw any IO which was frozen.
* Quiesce device. (Other routines will activate if thawed IO.)
* Set device online.
* Leave device disconnected if there are problems.
*/
static void
{
int rval;
int old_hw_state;
"hwahc_restore_device_state: dip = 0x%p", (void *)dip);
/* raise power */
/*
* Check if we are talking to the same device
* Some host controllers may see all devices disconnected
* when they just resume. This may be a cause of not
* finding the same device.
*
* Some HWA devices need to download firmware when it is
* powered on. Before the firmware is downloaded, the device
* will look differently.
*/
USB_SUCCESS) {
"hwahc_restore_device_state: not the same device");
/* change the device state from suspended to disconnected */
return;
}
"hwahc_restore_device_state: Hwahc has been reconnected but"
" data may have been lost");
/* reinitialize the hw */
/* no need to start hc */
"hwahc_restore_device_state: stopped hwa");
return;
}
if (rval != USB_SUCCESS) {
"hwahc_restore_device_state: set cluster id fails");
goto err;
}
if (rval != USB_SUCCESS) {
"hwahc_restore_device_state: start hc fails");
goto err;
}
}
if (rval != USB_SUCCESS) {
"hwahc_restore_device_state: set num dnts fails");
goto err;
}
/* set default GTK */
if (rval != USB_SUCCESS) {
"hwahc_restore_device_state: set gtk fails");
goto err;
}
if (rval != USB_SUCCESS) {
"hwahc_restore_device_state: enable wa fails");
goto err;
}
/*
* This is a workaround, sometimes the ioctl and reconnect will
* happen at the sametime, so the ioctl will start nep which makes
* the below sart nep fail. Need more work to do to avoid such
* issues
*/
if (rval != USB_SUCCESS) {
"hwahc_restore_device_state: start notifep fails rval =%d",
rval);
goto err;
}
/* Handle transfer results on bulk-in ep */
if (rval != USB_SUCCESS) {
"hwahc_restore_device_state: start result thread fails");
goto err;
}
/* if the device had remote wakeup earlier, enable it again */
}
return;
err:
}
/*
* hwahc_cpr_suspend:
* Clean up device.
* Wait for any IO to finish, then close pipes.
* Quiesce device.
* due to the dependency on hwarc, the actual suspend operations are
* moved to hwahc_pre_suspend_event_cb function.
*/
static int
{
return (USB_FAILURE);
}
"hwahc_cpr_suspend: start");
/* Don't suspend if the device is open. */
if (hwahcp->hwahc_open_count > 0) {
"hwahc_cpr_suspend: Device is open, cannot suspend");
return (USB_FAILURE);
}
/* raise power */
switch (hwahcp->hwahc_dev_state) {
case USB_DEV_ONLINE:
/* real suspend operations put in pre_suspend function */
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_PWRED_DOWN:
break;
case USB_DEV_SUSPENDED:
default:
"hwahc_cpr_suspend: illegal dev state=%d",
break;
}
"hwahc_cpr_suspend: end");
return (USB_SUCCESS);
}
/*
* hwahc_cpr_resume:
*
* hwahc_restore_device_state marks success by putting device back online
*/
static int
{
return (USB_FAILURE);
}
"hwahc_cpr_resume: hw state = %d, softstate = %d",
/*
* rc is always resumed after hc. restoring hc before rc would fail.
* move the restoring operations to hwahc_post_resume_event_cb.
*/
return (USB_SUCCESS);
}
/*
* hwahc_create_pm_components:
* Create power managements components
*/
static void
{
"hwahc_create_pm_components: Begin");
/* Allocate the state structure */
hwahcpm->hwahc_pm_capabilities = 0;
"hwahc_create_pm_components: created PM components");
}
/* make device busy till end of attach */
} else {
"hwahc_create_pm_components: failed");
}
"hwahc_create_pm_components: End");
}
/*
* hwahc_destroy_pm_components:
* Shut down and destroy power management and remote wakeup functionality
*/
static void
{
"hwahc_destroy_pm_components: Begin");
int rval;
if ((rval = usb_handle_remote_wakeup(
USB_SUCCESS) {
"hwahc_destroy_pm_components: "
"Error disabling rmt wakeup: rval = %d",
rval);
}
} else {
}
/*
* Since remote wakeup is disabled now,
* no one can raise power and get to device
* once power is lowered here.
*/
}
}
"hwahc_destroy_pm_components: End");
}
/* mark component busy */
static void
{
"hwahc_pm_busy_component: %d",
DDI_SUCCESS) {
"hwahc_pm_busy_component failed: %d",
}
}
}
/* mark component idle */
static void
{
DDI_SUCCESS) {
"hwahc_pm_idle_component: %d",
}
}
}
/*
* hwahc_power :
* Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
* usb_req_raise_power and usb_req_lower_power.
*/
/* ARGSUSED */
static int
{
int rval = USB_FAILURE;
return (DDI_FAILURE);
}
"hwahc_power: dip = 0x%p", (void *)dip);
goto done;
}
/* Check if we are transitioning to a legal power level */
"hwahc_power: illegal power level = %d "
goto done;
}
switch (level) {
case USB_DEV_OS_PWR_OFF :
break;
case USB_DEV_OS_PWR_1:
break;
case USB_DEV_OS_PWR_2:
break;
case USB_DEV_OS_FULL_PWR :
break;
}
done:
}
/*
* hwahc_pwrlvl0:
* Functions to handle power transition for OS levels 0 -> 3
* OS 0 <--> USB D3, no or minimal power
*/
static int
{
int rval;
switch (hwahcp->hwahc_dev_state) {
case USB_DEV_ONLINE:
/* Deny the powerdown request if the device is busy */
"hwahc_pwrlvl0: hwahc_pm is busy");
return (USB_FAILURE);
}
/*
* only when final_stop gets called, we allow the system
* to do PM on us. At this moment, we don't need to do
* more operations other than those in final_stop.
*/
/* Issue USB D3 command to the device here */
break;
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
case USB_DEV_PWRED_DOWN:
default:
break;
}
return (USB_SUCCESS);
}
/*
* hwahc_pwrlvl1:
* Functions to handle power transition to OS levels -> 2
* OS level 1 <--> D2
*/
static int
{
int rval;
"hwahc_pwrlvl1:");
/* Issue USB D2 command to the device here */
return (USB_FAILURE);
}
/*
* hwahc_pwrlvl2:
* Functions to handle power transition to OS levels -> 1
* OS leve 2 <--> D1
*/
static int
{
int rval;
"hwahc_pwrlvl2:");
/* Issue USB D1 command to the device here */
return (USB_FAILURE);
}
/*
* hwahc_pwrlvl3:
* Functions to handle power transition to OS level -> 0
* OS level 3 <--> D0 (full power)
*/
static int
{
switch (hwahcp->hwahc_dev_state) {
case USB_DEV_PWRED_DOWN:
/* Issue USB D0 command to the device here */
/*
* Due to our current PM policy, it's not possible
* for hwa to be in USB_DEV_PWRED_DOWN between
* initial_start and final_stop. If it's PWRED_DOWN,
* it should not start. We don't need to resume
* soft or hardware state in this case.
*/
/* no need to start hc */
return (USB_SUCCESS);
}
/* FALLTHRU */
case USB_DEV_ONLINE:
/* we are already in full power */
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
/*
* PM framework tries to put you in full power
* during system shutdown. If we are disconnected
* return success. Also, we should not change state
* when we are disconnected or suspended or about to
* transition to that state
*/
return (USB_SUCCESS);
default:
"hwahc_pwrlvl3: illegal dev_state=%d",
return (USB_FAILURE);
}
}
/*
* Host power management: stop channel
* See Section 4.16.2.1 for details
*/
static int
{
int rval;
"hwahc_hc_channel_suspend:");
/* no need to suspend if host hw was not started */
"hwahc_hc_channel_suspend: hw already stopped");
return (USB_SUCCESS);
}
"hwahc_hc_channel_suspend: already suspended");
return (USB_SUCCESS);
}
/* suspend host, refer to WUSB 1.0 spec 8.5.3.14 */
if (rval != USB_SUCCESS) {
"hwahc_hc_channel_suspend: wusb channel stop fails");
return (rval);
}
return (USB_SUCCESS);
}
/*
* Parse security descriptors, see T.8-43
* put result in secrt_data
*/
static int
{
int i, j;
return (USB_INVALID_ARGS);
}
continue;
}
(void *)&secrt_data->secrt_descr,
if (count != USB_SECURITY_DESCR_SIZE) {
return (USB_FAILURE);
} else {
len = sizeof (usb_encryption_descr_t) *
KM_SLEEP);
for (j = 0; j < secrt_data->secrt_n_encry;
j++) {
cvs_data =
return (USB_FAILURE);
}
/* Table 7-34 */
(void *)&secrt_data->
if (count !=
return (USB_FAILURE);
}
}
return (USB_SUCCESS);
}
}
}
return (USB_FAILURE);
}
/* initialize wusb_hc_data_t structure */
static void
{
sizeof (wusb_ie_header_t *)), KM_SLEEP);
/* initialize frequently used IE */
/* register callbacks */
/* HWA HC operation functions */
}
/* deinitialize wusb_hc_data_t structure */
static void
{
int i;
#ifdef DEBUG
#endif
if (hc_data->hc_mmcie_list) {
/* Free all recorded IEs except statically allocated IEs */
for (i = 0; i < hc_data->hc_num_mmcies; i++) {
if ((hdr->bIEIdentifier !=
}
}
}
}
if (hc_data->hc_cluster_id) {
}
if (hc_data->hc_cc_list) {
}
#ifdef DEBUG
}
#endif
}
/* fully start the HWA hw */
static int
{
int rval;
uint8_t cluster_id = 0;
"hwahc_hc_initial_start:");
"hwahc_hc_initial_start: invalid dev state = %d",
return (USB_INVALID_REQUEST);
}
"hwahc_hc_initial_start: invalid hw state");
return (USB_INVALID_REQUEST);
}
/*
* start beacon of radio layer
* We're not sure if previouse channel is occupied or not. So, let
* UWB allocates a free channel for this hwa. Then we can start
* beacon.
*/
"wusb_hc_initial_start: channel = %d",
return (USB_FAILURE);
}
"wusb_hc_initial_start: start uwb beacon failed");
return (rval);
}
/* reset wire adapter */
"hwahc_hc_initial_start: reset wa fails");
goto err;
}
/* reuse the old cluster id or assign one */
} else {
if (cluster_id == 0) {
"hwahc_hc_initial_start: cannot get cluster id");
goto err;
}
}
/* set cluster id for the wusb channel */
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: set cluster id %d fails",
goto err;
}
/* UWB should be responsible for assigning stream index */
stream_idx = 1;
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: set stream idx %d fails",
goto err;
}
/* set dnts slot */
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: set num dnts fails");
goto err;
}
/* set host info IE */
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: add hostinfo ie fails");
goto err;
}
/* reserve MAS slots for the host, need a way to assign */
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: set wusb mas fails");
goto err;
}
/* record the available MAS slots */
/* set default GTK, need a way to dynamically compute it */
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: set gtk fails");
goto err;
}
/* enable wire adapter */
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: enable wa fails");
goto err;
}
/* Start Notification endpoint */
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: start notification ep fails");
goto err;
}
/*
* Handle transfer results on bulk-in ep
* The bulk-in ep needs to be polled no matter the completion
* notification is received or not to avoid miss result.
*/
if (rval != USB_SUCCESS) {
"hwahc_hc_initial_start: start result thread fails, "
"rval = %d", rval);
goto err;
}
"hwahc_hc_initial_start: start result thread success");
/* Don't do PM on an active beacon hwa until explicitly stopped */
return (USB_SUCCESS);
err:
if (cluster_id != 0) {
}
return (rval);
}
/* entirely stop the HWA from working */
static int
{
"hwahc_hc_final_stop:");
"hwahc_hc_final_stop: already stopped");
return (USB_SUCCESS);
}
"hwahc_hc_final_stop: invalid dev state = %d",
return (USB_INVALID_REQUEST);
}
/* might have been powered down before detaching */
/* notify children the host is going to stop */
(void) hwahc_hc_channel_suspend(hwahcp);
/* release mutex here to avoid deadlock with exc_cb */
/* stop notification endpoint */
/* stop bulk-in ept from listening result */
/* drain the device notifications */
/* disable wire adapter */
/* stop beaconing. Not necessary to unreserve mas */
/* Manually remove all connected children */
/* delete all the children */
}
/*
* we make it busy at hwahc_hc_initial_start(). This idle operation
* is to match that busy operation.
*/
"hwahc_hc_final_stop: pm_busy=%d",
}
}
return (USB_SUCCESS);
}
/*
* init WUSB channel, this is only part of the full hw start operations
* including setting wusb channel stream idx, wusb MAS slots reservation
* and adding host info IE
*/
static int
{
int rval;
"hwahc_hc_channel_start:");
"hwahc_hc_channel_start: invalid dev_state = %d",
return (USB_INVALID_REQUEST);
}
"hwahc_hc_channel_start: invalid hw state");
return (USB_INVALID_REQUEST);
}
/* set stream idx */
stream_idx = 1;
if (rval != USB_SUCCESS) {
"hwahc_hc_channel_start: set stream idx %d fails",
return (rval);
}
/* reserve MAS slots for the host. Should be allocated by UWB */
if (rval != USB_SUCCESS) {
"hwahc_hc_channel_start: set wusb mas fails");
return (rval);
}
/* set host info IE */
if (rval != USB_SUCCESS) {
"hwahc_hc_channel_start: add hostinfo ie fails");
return (rval);
}
/* do not PM this device, once we're ready to accept DN */
return (USB_SUCCESS);
}
/*
* stop WUSB channel, this only stops part of the hw function
* it mainly unreserve the MAS slots and remove the host info IE
*/
static int
{
int rval;
"hwahc_hc_channel_stop:");
"hwahc_hc_channel_stop: invalid dev state %d",
return (USB_INVALID_REQUEST);
}
"hwahc_hc_channel_stop: already partially stopped");
return (USB_SUCCESS);
}
"hwahc_hc_channel_stop: already stopped, invalid state");
return (USB_INVALID_REQUEST);
}
/* send host disconect IE so that the children know to disconnect */
if (rval != USB_SUCCESS) {
"hwahc_hc_channel_stop: send host disconnect ie fails");
return (rval);
}
/* remove host info IE */
/* unset stream idx */
stream_idx = 0;
if (rval != USB_SUCCESS) {
"hwahc_hc_channel_stop: set stream idx 0 fails");
return (rval);
}
/* unreserve MAS slots */
if (rval != USB_SUCCESS) {
"hwahc_hc_channel_stop: set null wusb mas fails");
return (rval);
}
/* Channel is stopped, can be PM'ed */
return (USB_SUCCESS);
}
/* initialize data transfer related resources */
static int
{
int rval;
"hwahc_wa_start:");
/* get all rpipe descrs */
"hwahc_wa_start: get rpipe descrs fails, rval=%d", rval);
return (rval);
}
/* open all data transfer epts */
USB_SUCCESS) {
"hwahc_wa_start: open pipes fails, rval=%d", rval);
return (rval);
}
/* init notification list */
return (USB_SUCCESS);
}
/* deinitialize data transfer related resources */
static void
{
"hwahc_wa_stop:");
}
/*
* HUBD related initialization
* To mimic standard hub attach process to create a fake "root hub"
* for HWA
*/
static int
{
int i;
int rval;
"hwahc_hub_attach:");
"wire-adapter") != NDI_SUCCESS) {
return (USB_FAILURE);
}
/* allocate hubd structure */
&hubd_errmask, &hubd_instance_debug, 0);
USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
"cannot get dev_data");
goto fail;
}
/* init hubd mutex */
/* register the instance to usba HUBDI */
if (rval != USB_SUCCESS) {
"usba_hubdi_register failed");
goto fail;
}
KM_SLEEP);
/* create cfgadm minor nodes */
char ap_name[HUBD_APID_NAMELEN];
hubd->h_ancestry_str, i);
"ap_name=%s", ap_name);
(instance << HWAHC_MINOR_INSTANCE_SHIFT) | i,
DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
"cannot create attachment point node (%d)",
instance);
goto fail;
}
}
/* create hubd minor node */
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
"cannot create devctl minor node (%d)", instance);
goto fail;
}
"usb-port-count", i) != DDI_PROP_SUCCESS) {
"usb-port-count update failed");
}
return (USB_SUCCESS);
fail:
"fail to cleanup after hub attach failure");
}
return (USB_FAILURE);
}
/* HUBD related deinitialization */
static int
{
"hwahc_hub_detach:");
goto done;
}
/* remove minor nodes */
}
/* unregister with usba HUBDI */
(void) usba_hubdi_unregister(dip);
}
}
if (hubd->h_ancestry_str) {
}
done:
if (hubd->h_dev_data) {
/* unregister client from usba */
}
return (USB_SUCCESS);
}
/* print security descrs */
static void
{
int i;
"The Host Wire Adapter security descriptor:");
"bLength = 0x%x\t\t bDescriptorType = 0x%x",
"wTotalLength = 0x%x\t bNumEncryptionTypes = 0x%x",
for (i = 0; i < secrt_data->secrt_n_encry; i++) {
"The Host Wire Adapter encryption descriptor %d:", i + 1);
"bLength = 0x%x\t\t bDescriptorType = 0x%x",
"bEncryptionType = 0x%x\t bEncryptionValue = 0x%x",
"bAuthKeyIndex = 0x%x",
}
}
/* drain device notifications */
static void
{
int i;
"hwahc_drain_notif_queue: started");
/* kick off a notif thread to drain the queue */
(void *)hwahcp, 0) != USB_SUCCESS) {
"hwahc_drain_notif_queue: no notif thread started");
} else {
}
}
for (i = 0; i < HWAHC_NOTIF_DRAIN_TIMEOUT; i++) {
/* loop until the queue is completed or it timeouts */
0)) {
break;
}
}
/* cleanup the queue if not completed */
}
"hwahc_drain_notif_queue: ended");
}
/* normal callback for notification ept */
static void
{
"hwahc_intr_cb: ph = 0x%p reqp = 0x%p", (void *)ph,
(void *)reqp);
return;
}
/* handle the notification */
}
/*
* See Section 8.3.3.3 for Transfer Notification format and
* Section 8.5.4 for HWA specific notifications.
* Three kinds of Notifications:
* - Transfer Completion
* - DN Received
* - BPST ADJ
*/
/* handle the notification according to notification type */
static void
{
int len;
uint8_t *p;
return;
}
"hwahc_handle_notif: data len = %d", len);
/*
* according to WUSB 1.0/8.1.2, multiple notifications might be sent
* at a time, need to parse one by one
*/
while (len > 0) {
if (len < 2) {
"hwahc_handle_notif: short packet len = %d",
len);
break;
}
hdr = (wa_notif_header_t *)p;
"hwahc_handle_notif: length not match, "
"hdr length = %d, actual length = %d",
break;
}
switch (hdr->bNotifyType) {
case WA_NOTIF_TYPE_TRANSFER:
{
/* deal with transfer completion notification */
break;
}
{
/* deal with device notification */
break;
}
case HWA_NOTIF_TYPE_BPST_ADJ:
"hwahc_handle_notif: received BPST adjust "
"notification, bAdjustment = %d", p[2]);
break;
default:
"hwahc_handle_notif: unknown notification 0x%x",
hdr->bNotifyType);
break;
}
}
}
/*
* start listening on bulk-in ept for transfer result
*
* Dispatches a task to read the BULK IN endpoint to get the result of
* last request. usb_async_req() will have system_taskq to process the tasks.
*/
int
{
"hwahc_start_result_thread:");
if (hwahcp->hwahc_result_thread_id != 0) {
"hwahc_start_result_thread: already started");
return (USB_SUCCESS);
}
return (USB_INVALID_PIPE);
}
"hwahc_start_result_thread: open pipe failed");
return (USB_FAILURE);
}
}
/* kick off an asynchronous thread to handle transfer result */
(void *)hwahcp, 0) != USB_SUCCESS) {
"hwahc_start_result_thread: failed to start result thread");
return (USB_FAILURE);
}
/* pipe state is active while the result thread is on */
return (USB_SUCCESS);
}
/* stop the bulk-in ept from listening */
static void
{
"hwahc_stop_result_thread:");
if (hwahcp->hwahc_result_thread_id == 0) {
"hwahc_stop_result_thread: already stopped");
return;
}
"hwahc_stop_result_thread: invalid pipe state");
return;
}
"hwahc_stop_result_thread: reset hwa bulk-in pipe");
/*
* have to close pipe here to fail the bulk-in transfer
* that never timeouts
*/
"hwahc_stop_result_thread: close hwa bulk-in pipe");
while (hwahcp->hwahc_result_thread_id != 0) {
/* wait the result thread to exit */
}
}
/*
* keep listening for transfer result by setting timeout to 0 while the
* bulk-in pipe is active
* the thread would be stopped by closing bulk-in pipe or encountering
* transaction error, eg, hot-removal of hwa device
*/
static void
hwahc_result_thread(void *arg)
{
int rval;
"hwahc_result_thread: started, thread_id=0x%p",
(void *)hwahcp->hwahc_result_thread_id);
/* keep polling the bulk IN endpoint to get the result */
retry++;
"hwahc_result_thread: get xfer result failed, "
/* retry 3 times upon failure */
if (retry >= 3) {
break;
}
}
}
hwahcp->hwahc_result_thread_id = 0;
/* signal to the thread requesting stopping if any */
"hwahc_result_thread: ended");
}
/*
* nothing to do here, just check if the ept number in the transfer
* completion notification is valid
* the actual handling of transfer result is performed by the result thread
*/
static void
{
"hwahc_handle_xfer_result: result on ept %d", ept);
/* the result should be on the bulk-in ept */
"hwahc_handle_xfer_result: ept number not match");
return;
}
}
/*
* Section 8.5.4.2.
* Copy the DN Notification and add it to the instance's global
* nofication list. If the worker thread is not started yet, start
* it.
*/
static void
{
"hwahc_handle_dn_notif: notif = 0x%p", (void *)dn_notif);
/* queue the new notification to the list */
/* handle the notification queue with an asynchronous thread */
if (hwahcp->hwahc_notif_thread_id == 0) {
(void *)hwahcp, 0) != USB_SUCCESS) {
"hwahc_handle_dn_notif: no notif thread started");
return;
}
}
}
/* handle the notifications in the notification queue in sequence */
static void
hwahc_notif_thread(void *arg)
{
"hwahc_notif_thread: started, thread_id=0x%p",
(void *)hwahcp->hwahc_notif_thread_id);
/*
* first in first out, only one notification will be handled
* at a time, so it assures no racing in attach or detach
*/
if ((nlist =
continue;
}
}
hwahcp->hwahc_notif_thread_id = 0;
"hwahc_notif_thread: ended");
}
/* Set the child device's active bit to 1 */
static void
{
int i;
"hwahc_set_device_active:device(%p) updated ",
(void *)dev_info);
break;
}
}
}
/*
* handle a specific device notification
* assuming the raw data in HWA DN_RECEIVED notification pkt includes
* no more than one dn pkt
*/
static void
{
uint8_t *p;
int circ;
return;
}
/*
* WUSB Errata 06.12 specifies that the raw data in the DN_RECEIVED
* notification must not include the WUSB header, but only the bType
* and Notification specific data
*/
if (len == 0) {
"hwahc_handle_dn: no raw data");
return;
}
dntype = *p;
/* update the device's status bit, no matter what the DN is */
switch (dntype) {
case WUSB_DN_CONNECT:
/* DN_Connect */
break;
case WUSB_DN_DISCONNECT:
/* DN_Disconnect */
p, len);
break;
case WUSB_DN_ALIVE:
/* We only send KeepAlive IE to one device at a comment */
if (dn_notif->bSourceDeviceAddr ==
}
break;
case WUSB_DN_EPRDY:
case WUSB_DN_MASAVAILCHANGED:
case WUSB_DN_REMOTEWAKEUP:
case WUSB_DN_SLEEP:
default:
"hwahc_handle_dn: dn type 0x%x not supported yet",
dntype);
break;
}
}
/* exceptional callback for notification ept */
/* ARGSUSED */
static void
{
"hwahc_intr_exc_cb: receive intr exception cb, cr=%d",
switch (reqp->intr_completion_reason) {
case USB_CR_PIPE_RESET:
/* only restart nep after autoclearing */
}
break;
case USB_CR_DEV_NOT_RESP:
case USB_CR_STOPPED_POLLING:
case USB_CR_PIPE_CLOSING:
case USB_CR_UNSPECIFIED_ERR:
/* never restart nep on these conditions */
default:
/* for all others, wait for the autoclearing PIPE_RESET cb */
break;
}
}
/*
* callback function called by WA to resubmit a periodic request for
* interrupt polling or isochronous transfer.
*/
static int
{
int rval;
"hwahc_pipe_submit_periodic_req: hwahcp=0x%p, pp=0x%p,"
/* pipe error or pipe closing, don't resubmit any more */
"hwahc_pipe_submit_periodic_req: pipe not active = %d",
return (USB_PIPE_ERROR);
}
/* re-submit the original request */
return (rval);
}
/* call HCD callback for completion handling */
static void
{
return;
}
"hwahc_rpipe_xfer_cb: ph = 0x%p, wr = 0x%p cr = 0x%x",
switch (cr) {
case USB_CR_OK:
break;
case USB_CR_NOT_SUPPORTED:
case USB_CR_NO_RESOURCES:
case USB_CR_PIPE_RESET:
case USB_CR_STOPPED_POLLING:
break;
case USB_CR_PIPE_CLOSING:
break;
default:
break;
}
} else { /* periodic pipe cleanup */
/* the original request is cleared and returned to client */
}
"hwahc_rpipe_xfer_cb: call usba_hcdi_cb for req= 0x%p",
(void *)req);
}
/* post disconnect event to child on a certain port */
static void
{
int circ;
return;
}
/* if the child driver remains attached */
if (i_ddi_devi_attached(child_dip)) {
}
}
}
/* post reconect event to child on a certain port */
static void
{
int circ;
return;
}
}
}
/*
* Device TrustTimeout timer operations:
* hwahc_start_trust_timer: start the trust timer for a newly connected device
* hwahc_trust_timeout_handler: timer handler
* hwahc_stop_trust_timer: stop a device's trust timer
*/
static void
{
if (hwahc_enable_trust_timeout == 0) {
return;
}
}
}
/* timeout handler for device TrustTimeout. See section 4.14 */
static void
hwahc_trust_timeout_handler(void *arg)
{
dev->wdev_trust_timer = 0;
/* device is active during the past period. Restart the timer */
} else {
/* send a KeepAlive IE to query the device */
break;
}
/* retry 3 times if fail to send KeepAlive IE */
}
if (dev->wdev_active == 0) {
/* still no activity! Delete this device */
&port)) {
/* the device comes to the end of its life */
return;
}
}
}
/* active or we received DN during query */
}
/* stop a child device's trust timeout handler */
void
{
if (hwahc_enable_trust_timeout == 0) {
return;
}
}
}
/* configure child device and attach child on a certain port */
static int
{
int rval;
int user_conf_index;
return (USB_INVALID_ARGS);
}
/* exclude other threads */
/* Created in whcdi.c before authed */
/*
* HWA maintains the address space as a separate bus and
* will not occupy parent's address space
*/
if (address < 0x80) {
"hwahc_create_child: reconnecting, address = %d",
address);
} else {
/* SetAddress(0) */
USB_REQ_SET_ADDRESS, /* bRequest */
0, /* wValue */
0, /* wIndex */
0, /* wLength */
NULL, 0,
char buffer[64];
"setting address failed (cr=%s cb_flags=%s "
rval);
goto done;
}
"set address 0 done");
/* need to be called each time dev addr is changed */
port)) != USB_SUCCESS) {
"update device info failed, rval = %d", rval);
goto done;
}
/* new ph is stored in usba_device */
USB_SUCCESS) {
"usb_pipe_open failed (%d)", rval);
goto done;
}
/* provide at least 2ms time for address change, 7.3.1.3 */
/* start normal enumeration process */
/*
* wusb bus address has 1:1 relationship with port number
* and wusb bus address starts from 2, so as to follow
* the convention that USB bus address 1 is reserved for
* host controller device. As such, only 126 WUSB devices
* are supported on a WUSB host
*/
if (address >= 0x80) {
"hwahc_create_child: address for port %d exceeds "
"0x80", port);
rval = USB_FAILURE;
goto done;
}
/* Set the address of the device */
USB_REQ_SET_ADDRESS, /* bRequest */
address, /* wValue */
0, /* wIndex */
0, /* wLength */
NULL, 0,
char buffer[64];
"setting address failed (cr=%s cb_flags=%s "
rval);
goto done;
}
"set address 0x%x done", address);
port)) != USB_SUCCESS) {
"update device info failed, rval = %d", rval);
goto done;
}
/* new ph is stored in usba_device */
USB_SUCCESS) {
"usb_pipe_open failed (%d)", rval);
goto done;
}
/* provide at least 2ms time for address change, 7.3.1.3 */
}
/* get device descriptor ignoring device reconnection */
USB_REQ_GET_DESCR, /* bRequest */
USB_DESCR_TYPE_SETUP_DEV, /* wValue */
0, /* wIndex */
512, /* wLength */
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
if (pdata) {
}
"hwahc_create_child: get device descriptor failed "
goto done;
}
sizeof (usb_dev_descr_t));
if (size < USB_DEV_DESCR_SIZE) {
"hwahc_create_child: get device descriptor size = %lu "
rval = USB_FAILURE;
goto done;
}
sizeof (usb_dev_descr_t));
if (usb_dev_descr.bNumConfigurations == 0) {
"device descriptor:\n\t"
"l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
"protocol=0x%x maxpktsize=0x%x "
"Vid=0x%x Pid=0x%x rel=0x%x\n\t"
"Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
rval = USB_FAILURE;
goto done;
}
/* get the device string descriptor(s) */
/* retrieve config cloud for all configurations */
if (rval != USB_SUCCESS) {
"failed to get configuration descriptor(s)");
goto done;
}
/* get the preferred configuration for this device */
/* Check if the user selected configuration index is in range */
(user_conf_index < 0)) {
"Configuration index for device idVendor=%d "
"idProduct=%d is=%d, and is out of range[0..%d]",
/* treat this as user didn't specify configuration */
}
if (child_ud->usb_preferred_driver) {
/*
* It is the job of the "preferred driver" to put the
* device in the desired configuration. Till then
* put the device in config index 0.
*/
/* h_ignore_pwr_budget = TRUE, not care the power */
goto done;
}
/*
* Assign the dip before onlining to avoid race
* with busctl
*/
(void) usba_bind_driver(child_dip);
} else {
/*
* loop through all the configurations to see if we
* can find a driver for any one config. If not, set
* the device in config_index 0
*/
rval = USB_FAILURE;
for (config_index = 0;
/*
* Assign the dip before onlining to avoid race
* with busctl
*/
if (rval == USB_SUCCESS) {
/* always succeed for WUSB device */
child_ud, config_index)) !=
USB_SUCCESS) {
rval = USB_FAILURE;
goto done;
}
}
}
if (rval != USB_SUCCESS) {
child_ud, 0)) != USB_SUCCESS) {
goto done;
}
}
} /* end else loop all configs */
} else {
rval = USB_FAILURE;
goto done;
}
/*
* Assign the dip before onlining to avoid race
* with busctl
*/
(void) usba_bind_driver(child_dip);
rval = USB_SUCCESS;
}
/* workaround for non response after ctrl write */
USB_SUCCESS) {
"usb_pipe_open failed (%d)", rval);
goto done;
}
done:
if (rval == USB_SUCCESS) {
(void) ndi_devi_online(child_dip, 0);
"hwahc_create_child: create timer for child %p",
(void *)dev_info);
}
return (rval);
}
/* offline child on a certain port */
static int
{
return (USB_INVALID_ARGS);
}
"hwahc_destroy_child: scheduling cleanup");
/* schedule cleanup thread */
return (USB_SUCCESS);
}
/*
* called by cleanup thread to offline child and cleanup child resources
* Child's callback functions have been called before calling this routine.
* dip - hwahc's dip
*/
static int
{
return (USB_INVALID_ARGS);
}
continue;
}
/*
* child's callback has been called and its dip has been
* marked REMOVED. Do further cleanup in hwa driver for
* this child.
*/
}
return (USB_SUCCESS);
}
/* offline child and cleanup child resources */
static int
{
int rval;
return (USB_INVALID_ARGS);
}
return (USB_SUCCESS);
}
"hwahc_delete_child: port=%d, dip=0x%p usba_device=0x%p",
if (usba_device) {
}
/* remove this child's dip. If it's <DS_INITIALIZED, free it */
/*
* if the child was still < DS_INITIALIZED
* then our bus_unconfig was not called and
* we have to zap the child here
*/
if (ud) {
ud->usb_ref_count = 0;
}
/* free the child's wusb_dev_info data */
if (dev_info) {
if (dev_info->
sizeof (usb_encryption_descr_t) *
}
if (dev_info->wdev_uwb_descr) {
sizeof (usb_uwb_cap_descr_t));
}
}
}
}
}
return (rval);
}
/*
* Set encryption type for WUSB host, refer to WUSB 1.0/8.5.3.6
* index = port number - 1
*/
int
{
"hwahc_set_dev_encrypt: device index = %d", index);
if (type == USB_ENC_TYPE_UNSECURE) {
value = 0;
} else if (type == USB_ENC_TYPE_CCM_1) {
if (secrt_data == NULL) {
return (USB_INVALID_ARGS);
}
if (value == -1) {
"hwahc_set_dev_encrypt: cannot find ccm "
"encryption type");
return (USB_FAILURE);
}
"hwahc_set_dev_encrypt: ccm encryption value is %d",
value);
} else {
"hwahc_set_dev_encrypt: unsupported encryption type %d",
type);
return (USB_INVALID_ARGS);
}
"bmRequestType=0x%x, bRequest=0x%x, wValue=0x%x, wIndex=0x%x",
}