/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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.
*/
/*
* USBA: Solaris USB Architecture support
*
* Utility functions
*/
#define USBA_FRAMEWORK
#include <sys/usb/usba/usba_impl.h>
#include <sys/usb/usba/hcdi_impl.h>
#include <sys/strsun.h>
extern void usba_free_evdata(usba_evdata_t *);
static mblk_t *usba_get_cfg_cloud(dev_info_t *, usb_pipe_handle_t, int);
/* local functions */
static int usba_sync_set_cfg(dev_info_t *, usba_ph_impl_t *,
usba_pipe_async_req_t *, usb_flags_t);
static int usba_sync_set_alt_if(dev_info_t *, usba_ph_impl_t *,
usba_pipe_async_req_t *, usb_flags_t);
static int usba_sync_clear_feature(dev_info_t *, usba_ph_impl_t *,
usba_pipe_async_req_t *, usb_flags_t);
/*
* Wrapper functions returning parsed standard descriptors without
* getting the config cloud first but by just providing the dip.
*
* The client can easily retrieve the device and config descriptor from
* the usb registration and no separate functions are provided
*
* These functions return failure if the full descriptor can not be
* retrieved. These functions will not access the device.
* The caller must allocate the buffer.
*/
/*
* usb_get_if_descr:
* Function to get the cooked interface descriptor
* This function will not access the device.
*
* Arguments:
* dip - pointer to devinfo of the client
* if_index - interface index
* alt_setting - alt interface setting
* descr - pointer to user allocated interface descr
*
* Return Values:
* USB_SUCCESS - descriptor is valid
* USB_FAILURE - full descriptor could not be retrieved
* USB_* - refer to usbai.h
*/
int
usb_get_if_descr(dev_info_t *dip,
uint_t if_index,
uint_t alt_setting,
usb_if_descr_t *descr)
{
uchar_t *usb_cfg; /* buf for config descriptor */
size_t size, cfg_length;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_if_descr: %s, index=0x%x, alt#=0x%x",
ddi_node_name(dip), if_index, alt_setting);
if ((dip == NULL) || (descr == NULL)) {
return (USB_INVALID_ARGS);
}
usb_cfg = usb_get_raw_cfg_data(dip, &cfg_length);
size = usb_parse_if_descr(usb_cfg, cfg_length,
if_index, /* interface index */
alt_setting, /* alt interface index */
descr,
USB_IF_DESCR_SIZE);
if (size != USB_IF_DESCR_SIZE) {
USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
"parsing interface: size (%lu) != USB_IF_DESCR_SIZE (%d)",
size, USB_IF_DESCR_SIZE);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* usb_get_ep_descr:
* Function to get the cooked endpoint descriptor
* This function will not access the device.
*
* Arguments:
* dip - pointer to devinfo of the client
* if_index - interface index
* alt_setting - alternate interface setting
* endpoint_index - endpoint index
* descr - pointer to user allocated interface descr
*
* Return Values:
* USB_SUCCESS - descriptor is valid
* USB_FAILURE - full descriptor could not be retrieved
* USB_* - refer to usbai.h
*/
int
usb_get_ep_descr(dev_info_t *dip,
uint_t if_index,
uint_t alt_setting,
uint_t endpoint_index,
usb_ep_descr_t *descr)
{
uchar_t *usb_cfg; /* buf for config descriptor */
size_t size, cfg_length;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_ep_descr: %s, index=0x%x, alt#=0x%x",
ddi_node_name(dip), if_index, alt_setting);
if ((dip == NULL) || (descr == NULL)) {
return (USB_INVALID_ARGS);
}
usb_cfg = usb_get_raw_cfg_data(dip, &cfg_length);
size = usb_parse_ep_descr(usb_cfg, cfg_length,
if_index, /* interface index */
alt_setting, /* alt interface index */
endpoint_index, /* ep index */
descr, USB_EP_DESCR_SIZE);
if (size != USB_EP_DESCR_SIZE) {
USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
"parsing endpoint: size (%lu) != USB_EP_DESCR_SIZE (%d)",
size, USB_EP_DESCR_SIZE);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* usb_lookup_ep_data:
* usb_get_ep_data (deprecated):
* Function to get specific endpoint descriptor data
* This function will not access the device.
*
* Arguments:
* dip - pointer to dev info
* usb_client_dev_data_t - pointer to registration data
* interface - requested interface
* alternate - requested alternate
* skip - how many to skip
* type - endpoint type
* direction - endpoint direction or USB_DIR_DONT_CARE
*
* Return Values:
* NULL or an endpoint descriptor pointer
*/
usb_ep_data_t *
usb_lookup_ep_data(dev_info_t *dip,
usb_client_dev_data_t *dev_datap,
uint_t interface,
uint_t alternate,
uint_t skip,
uint_t type,
uint_t dir)
{
usb_alt_if_data_t *altif_data;
int i;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_lookup_ep_data: "
"if=%d alt=%d skip=%d type=%d dir=%d",
interface, alternate, skip, type, dir);
if ((dip == NULL) || (dev_datap == NULL)) {
return (NULL);
}
altif_data = &dev_datap->dev_curr_cfg->
cfg_if[interface].if_alt[alternate];
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"altif=0x%p n_ep=%d", (void *)altif_data, altif_data->altif_n_ep);
for (i = 0; i < altif_data->altif_n_ep; i++) {
usb_ep_descr_t *ept = &altif_data->altif_ep[i].ep_descr;
uint8_t ept_type = ept->bmAttributes & USB_EP_ATTR_MASK;
uint8_t ept_dir = ept->bEndpointAddress & USB_EP_DIR_MASK;
if (ept->bLength == 0) {
continue;
}
if ((ept_type == type) &&
((type == USB_EP_ATTR_CONTROL) || (dir == ept_dir))) {
if (skip-- == 0) {
USB_DPRINTF_L4(DPRINT_MASK_USBA,
usbai_log_handle,
"usb_get_ep_data: data=0x%p",
(void *)&altif_data->altif_ep[i]);
return (&altif_data->altif_ep[i]);
}
}
}
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_ep_data: returning NULL");
return (NULL);
}
/*ARGSUSED*/
usb_ep_data_t *
usb_get_ep_data(dev_info_t *dip,
usb_client_dev_data_t *dev_datap,
uint_t interface,
uint_t alternate,
uint_t type,
uint_t dir)
{
return (usb_lookup_ep_data(dip, dev_datap, interface,
alternate, 0, type, dir));
}
/*
* usb_get_string_descr:
* Function to read the string descriptor
* This function will access the device and block.
*
* Arguments:
* dip - pointer to devinfo of the client
* langid - LANGID to read different LOCALEs
* index - index to the string
* buf - user provided buffer for string descriptor
* buflen - user provided length of the buffer
*
* Return Values:
* USB_SUCCESS - descriptor is valid
* USB_FAILURE - full descriptor could not be retrieved
* USB_* - refer to usbai.h
*/
int
usb_get_string_descr(dev_info_t *dip,
uint16_t langid,
uint8_t index,
char *buf,
size_t buflen)
{
mblk_t *data = NULL;
uint16_t length;
int rval;
usb_cr_t completion_reason;
size_t len;
usb_cb_flags_t cb_flags;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_string_descr: %s, langid=0x%x index=0x%x",
ddi_node_name(dip), langid, index);
if ((dip == NULL) || (buf == NULL) || (buflen == 0) || (index == 0)) {
return (USB_INVALID_ARGS);
}
/*
* determine the length of the descriptor
*/
rval = usb_pipe_sync_ctrl_xfer(dip,
usba_get_dflt_pipe_handle(dip),
USB_DEV_REQ_DEV_TO_HOST,
USB_REQ_GET_DESCR,
USB_DESCR_TYPE_STRING << 8 | index & 0xff,
langid,
4,
&data, USB_ATTRS_SHORT_XFER_OK,
&completion_reason,
&cb_flags, USB_FLAGS_SLEEP);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d cr=%d", rval, completion_reason);
goto done;
}
if (MBLKL(data) == 0) {
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"0 bytes received");
goto done;
}
ASSERT(data);
length = *(data->b_rptr);
freemsg(data);
data = NULL;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d, cr=%d, length=%d", rval, completion_reason, length);
/*
* if length is zero the next control request may fail.
* the HCD may not support a zero length control request
* and return an mblk_t which is NULL along with rval
* being USB_SUCCESS and "cr" being USB_CR_OK
*/
if (length < 2) {
rval = USB_FAILURE;
goto done;
}
rval = usb_pipe_sync_ctrl_xfer(dip,
usba_get_dflt_pipe_handle(dip),
USB_DEV_REQ_DEV_TO_HOST,
USB_REQ_GET_DESCR,
USB_DESCR_TYPE_STRING << 8 | index & 0xff,
langid,
length,
&data, USB_ATTRS_SHORT_XFER_OK,
&completion_reason,
&cb_flags, USB_FLAGS_SLEEP);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d, cb_flags=%d, cr=%d", rval, cb_flags, completion_reason);
if ((data == NULL) || (rval != USB_SUCCESS)) {
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"failed to get string descriptor (rval=%d cr=%d)",
rval, completion_reason);
goto done;
}
if ((length = MBLKL(data)) != 0) {
len = usba_ascii_string_descr(data->b_rptr, length, buf,
buflen);
USB_DPRINTF_L4(DPRINT_MASK_USBA,
usbai_log_handle, "buf=%s buflen=%lu", buf, len);
ASSERT(len <= buflen);
} else {
rval = USB_FAILURE;
}
done:
freemsg(data);
return (rval);
}
/*
* usb_get_dev_descr:
* utility function to get device descriptor from usba_device
*
* Arguments:
* dip - pointer to devinfo of the client
*
* Return Values:
* usb_dev_descr - device descriptor or NULL
*/
usb_dev_descr_t *
usb_get_dev_descr(dev_info_t *dip)
{
usba_device_t *usba_device;
usb_dev_descr_t *usb_dev_descr = NULL;
if (dip) {
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_dev_descr: %s", ddi_node_name(dip));
usba_device = usba_get_usba_device(dip);
mutex_enter(&usba_device->usb_mutex);
usb_dev_descr = usba_device->usb_dev_descr;
mutex_exit(&usba_device->usb_mutex);
}
return (usb_dev_descr);
}
/*
* usb_get_raw_cfg_data:
* utility function to get raw config descriptor from usba_device
*
* Arguments:
* dip - pointer to devinfo of the client
* length - pointer to copy the cfg length
*
* Return Values:
* usb_cfg - raw config descriptor
*/
uchar_t *
usb_get_raw_cfg_data(dev_info_t *dip, size_t *length)
{
usba_device_t *usba_device;
uchar_t *usb_cfg;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_raw_cfg_data: %s", ddi_node_name(dip));
if ((dip == NULL) || (length == NULL)) {
return (NULL);
}
usba_device = usba_get_usba_device(dip);
mutex_enter(&usba_device->usb_mutex);
usb_cfg = usba_device->usb_cfg;
*length = usba_device->usb_cfg_length;
mutex_exit(&usba_device->usb_mutex);
return (usb_cfg);
}
/*
* usb_get_addr:
* utility function to return current usb address, mostly
* for debugging purposes
*
* Arguments:
* dip - pointer to devinfo of the client
*
* Return Values:
* address - USB Device Address
*/
int
usb_get_addr(dev_info_t *dip)
{
int address = 0;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_addr: %s", ddi_node_name(dip));
if (dip) {
usba_device_t *usba_device = usba_get_usba_device(dip);
mutex_enter(&usba_device->usb_mutex);
address = usba_device->usb_addr;
mutex_exit(&usba_device->usb_mutex);
}
return (address);
}
/*
* usb_set_cfg():
* set configuration, use with caution (issues USB_REQ_SET_CONFIG)
* Changing configuration will fail if pipes are still open or when
* invoked from a driver bound to an interface on a composite device.
*
* This function will access the device and block
*
* Arguments:
* dip - pointer to devinfo of the client
* cfg_index - config index
* cfg_value - config value to be set
* flags - USB_FLAGS_SLEEP:
* wait for completion
* cb - if USB_FLAGS_SLEEP has not been specified
* this callback function will be called on
* completion. This callback may be NULL
* and no notification of completion will then
* be provided.
* cb_arg - 2nd argument to callback function.
*
* Return Values:
* USB_SUCCESS: - new configuration was set
* USB_FAILURE: - new configuration could not be set
* USB_BUSY: - some pipes were open or there were children
* USB_* - refer to usbai.h
*/
int
usb_set_cfg(dev_info_t *dip,
uint_t cfg_index,
usb_flags_t usb_flags,
void (*cb)(
usb_pipe_handle_t ph,
usb_opaque_t arg,
int rval,
usb_cb_flags_t flags),
usb_opaque_t cb_arg)
{
usb_pipe_handle_t ph;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_set_cfg: %s%d, cfg_index = 0x%x, uf = 0x%x",
ddi_driver_name(dip), ddi_get_instance(dip), cfg_index,
usb_flags);
if (dip == NULL) {
return (USB_INVALID_ARGS);
}
if ((usb_flags & USB_FLAGS_SLEEP) && servicing_interrupt()) {
return (USB_INVALID_CONTEXT);
}
if (!usb_owns_device(dip)) {
return (USB_INVALID_PERM);
}
ph = usba_get_dflt_pipe_handle(dip);
if (usba_hold_ph_data(ph) == NULL) {
return (USB_INVALID_PIPE);
}
return (usba_pipe_setup_func_call(dip,
usba_sync_set_cfg, (usba_ph_impl_t *)ph,
(usb_opaque_t)((uintptr_t)cfg_index), usb_flags, cb, cb_arg));
}
static int
usba_sync_set_cfg(dev_info_t *dip,
usba_ph_impl_t *ph_impl,
usba_pipe_async_req_t *request,
usb_flags_t flags)
{
int rval;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
usba_device_t *usba_device;
int i, ph_open_cnt;
uint_t cfg_index = (uint_t)((uintptr_t)(request->arg));
size_t size;
usb_cfg_descr_t confdescr;
dev_info_t *pdip;
usba_device = usba_get_usba_device(dip);
/*
* default pipe is still open
* all other pipes should be closed
*/
for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) {
if (usba_device->usb_ph_list[i].usba_ph_data) {
ph_open_cnt++;
break;
}
}
if (ph_open_cnt || ddi_get_child(dip)) {
usba_release_ph_data(ph_impl);
return (USB_BUSY);
}
/*
* check if the configuration meets the
* power budget requirement
*/
if (usba_is_root_hub(dip)) {
/*
* root hub should never be multi-configured.
* the code is here just to ensure
*/
usba_release_ph_data(ph_impl);
return (USB_FAILURE);
}
pdip = ddi_get_parent(dip);
/*
* increase the power budget value back to the unconfigured
* state to eliminate the influence of the old configuration
* before checking the new configuration; but remember to
* make a decrement before leaving this routine to restore
* the power consumption state of the device no matter it
* is in the new or old configuration
*/
usba_hubdi_incr_power_budget(pdip, usba_device);
if ((usba_hubdi_check_power_budget(pdip, usba_device,
cfg_index)) != USB_SUCCESS) {
usba_hubdi_decr_power_budget(pdip, usba_device);
usba_release_ph_data(ph_impl);
return (USB_FAILURE);
}
size = usb_parse_cfg_descr(usba_device->usb_cfg_array[cfg_index],
USB_CFG_DESCR_SIZE, &confdescr, USB_CFG_DESCR_SIZE);
/* hubdi should ensure that this descriptor is correct */
ASSERT(size == USB_CFG_DESCR_SIZE);
/* set the configuration */
rval = usb_pipe_sync_ctrl_xfer(dip, (usb_pipe_handle_t)ph_impl,
USB_DEV_REQ_HOST_TO_DEV,
USB_REQ_SET_CFG,
confdescr.bConfigurationValue,
0,
0,
NULL, 0,
&completion_reason,
&cb_flags, flags | USBA_FLAGS_PRIVILEGED | USB_FLAGS_SLEEP);
if (rval == USB_SUCCESS) {
mutex_enter(&usba_device->usb_mutex);
usba_device->usb_cfg_value = confdescr.bConfigurationValue;
usba_device->usb_active_cfg_ndx = cfg_index;
usba_device->usb_cfg = usba_device->usb_cfg_array[cfg_index];
usba_device->usb_cfg_length = confdescr.wTotalLength;
mutex_exit(&usba_device->usb_mutex);
/* update the configuration property */
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"configuration#", usba_device->usb_cfg_value);
}
/*
* usba_device->usb_cfg always stores current configuration
* descriptor no matter SET_CFG request succeeded or not,
* so usba_hubdi_decr_power_budget can be done regardless
* of rval above
*/
usba_hubdi_decr_power_budget(pdip, usba_device);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d, cb_flags=%d, cr=%d", rval, cb_flags, completion_reason);
usba_release_ph_data(ph_impl);
return (rval);
}
/*
* usb_get_cfg():
* get configuration value
*
* Arguments:
* dip - pointer to devinfo of the client
* cfg_value - current config value
* flags - none, always blocks
*
* Return Values:
* USB_SUCCESS: - config value was retrieved
* USB_FAILURE: - config value could not be retrieved
* USB_* - refer to usbai.h
*/
int
usb_get_cfg(dev_info_t *dip,
uint_t *cfgval,
usb_flags_t flags)
{
int rval;
usb_cr_t completion_reason;
mblk_t *data = NULL;
usb_cb_flags_t cb_flags;
usb_pipe_handle_t ph;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_cfg: %s uf = 0x%x", ddi_node_name(dip), flags);
if ((cfgval == NULL) || (dip == NULL)) {
return (USB_INVALID_ARGS);
}
ph = usba_get_dflt_pipe_handle(dip);
/*
* get the cfg value
*/
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_RCPT_DEV,
USB_REQ_GET_CFG,
0,
0,
1, /* returns one byte of data */
&data, 0,
&completion_reason,
&cb_flags, flags);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d cb_flags=%d cr=%d", rval, cb_flags, completion_reason);
if ((rval == USB_SUCCESS) && data &&
(MBLKL(data) == 1)) {
*cfgval = *(data->b_rptr);
} else {
*cfgval = 1;
if (rval == USB_SUCCESS) {
rval = USB_FAILURE;
}
}
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_cfg: %s cfgval=%d", ddi_node_name(dip), *cfgval);
freemsg(data);
return (rval);
}
/*
* usb_get_current_cfgidx:
* get current current config index
*/
uint_t
usb_get_current_cfgidx(dev_info_t *dip)
{
usba_device_t *usba_device = usba_get_usba_device(dip);
uint_t ndx;
mutex_enter(&usba_device->usb_mutex);
ndx = usba_device->usb_active_cfg_ndx;
mutex_exit(&usba_device->usb_mutex);
return (ndx);
}
/*
* usb_get_if_number:
* get usb interface number of current OS device node.
*
* Arguments:
* dip - pointer to devinfo of the client
*
* Return Values:
* USB_COMBINED_NODE if the driver is responsible for the entire
* device and this dip doesn't correspond to a device node.
* USB_DEVICE_NODE if the driver is responsible for the entire device
* and this dip corresponds to a device node.
* interface number: otherwise.
*/
int
usb_get_if_number(dev_info_t *dip)
{
int interface_num;
usba_device_t *usba_device = usba_get_usba_device(dip);
usb_dev_descr_t *usb_dev_descr;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_if_number: dip = 0x%p", (void *)dip);
/* not quite right but we can't return a negative return value */
if (dip == NULL) {
return (0);
}
if (usba_device) {
usb_dev_descr = usba_device->usb_dev_descr;
} else {
return (0);
}
interface_num = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "interface", USB_COMBINED_NODE);
if (interface_num == USB_COMBINED_NODE) {
if (!(((usb_dev_descr->bDeviceClass == USB_CLASS_HUB) ||
(usb_dev_descr->bDeviceClass == 0)) &&
(usba_device->usb_n_cfgs == 1) &&
(usba_device->usb_n_ifs == 1))) {
interface_num = USB_DEVICE_NODE;
}
}
return (interface_num);
}
boolean_t
usb_owns_device(dev_info_t *dip)
{
int interface_num = usb_get_if_number(dip);
return (interface_num < 0 ? B_TRUE : B_FALSE);
}
/* check whether the interface is in this interface association */
boolean_t
usba_check_if_in_ia(dev_info_t *dip, int n_if)
{
int first_if, if_count;
first_if = usb_get_if_number(dip);
if_count = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "interface-count", -1);
if_count += first_if;
return ((n_if >= first_if && n_if < if_count) ? B_TRUE : B_FALSE);
}
uint8_t
usba_get_ifno(dev_info_t *dip)
{
int interface_num = usb_get_if_number(dip);
return (uint8_t)(interface_num < 0 ? 0 : interface_num);
}
/*
* usb_set_alt_if:
* set the alternate interface number. Issues USB_REQ_SET_IF
* This function will access the device
*
* Arguments:
* dip - pointer to devinfo of the client
* if_number - interface number
* alt_number - alternate interface number
* flags - USB_FLAGS_SLEEP:
* wait for completion
* cb - if USB_FLAGS_SLEEP has not been specified
* this callback function will be called on
* completion. This callback may be NULL
* and no notification of completion will then
* be provided.
* cb_arg - 2nd argument to callback function.
*
*
* return values:
* USB_SUCCESS - alternate was set
* USB_FAILURE - alternate could not be set because pipes
* were still open or some access error occurred
* USB_* - refer to usbai.h
*
* Note:
* we can't easily check if all pipes to endpoints for this interface
* are closed since we don't have a map of which endpoints belong
* to which interface. If we had this map, we would need to update
* this on each alternative or configuration switch
*/
int
usb_set_alt_if(dev_info_t *dip,
uint_t interface,
uint_t alt_number,
usb_flags_t usb_flags,
void (*cb)(
usb_pipe_handle_t ph,
usb_opaque_t arg,
int rval,
usb_cb_flags_t flags),
usb_opaque_t cb_arg)
{
usb_pipe_handle_t ph;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_set_alt_if: %s%d, if = %d alt = %d, uf = 0x%x",
ddi_driver_name(dip), ddi_get_instance(dip),
interface, alt_number, usb_flags);
if (dip == NULL) {
return (USB_INVALID_ARGS);
}
if ((usb_flags & USB_FLAGS_SLEEP) && servicing_interrupt()) {
return (USB_INVALID_CONTEXT);
}
ph = usba_get_dflt_pipe_handle(dip);
if (usba_hold_ph_data(ph) == NULL) {
return (USB_INVALID_PIPE);
}
return (usba_pipe_setup_func_call(dip,
usba_sync_set_alt_if, (usba_ph_impl_t *)ph,
(usb_opaque_t)((uintptr_t)((interface << 8) | alt_number)),
usb_flags, cb, cb_arg));
}
static int
usba_sync_set_alt_if(dev_info_t *dip,
usba_ph_impl_t *ph_impl,
usba_pipe_async_req_t *request,
usb_flags_t flags)
{
int rval;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
usb_opaque_t arg = request->arg;
int interface = ((uintptr_t)arg >> 8) & 0xff;
int alt_number = (uintptr_t)arg & 0xff;
usba_pipe_handle_data_t *ph_data = usba_get_ph_data(
(usb_pipe_handle_t)ph_impl);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_set_alt_if: %s, interface#=0x%x, alt#=0x%x, "
"uf=0x%x", ddi_node_name(dip), interface,
alt_number, flags);
/* if we don't own the device, we must own the interface or ia */
if (!usb_owns_device(dip) && !usba_check_if_in_ia(dip, interface) &&
(interface != usb_get_if_number(dip))) {
usba_release_ph_data(ph_data->p_ph_impl);
return (USB_INVALID_PERM);
}
/* set the alternate setting */
rval = usb_pipe_sync_ctrl_xfer(dip, usba_get_dflt_pipe_handle(dip),
USB_DEV_REQ_HOST_TO_DEV | USB_DEV_REQ_RCPT_IF,
USB_REQ_SET_IF,
alt_number,
interface,
0,
NULL, 0,
&completion_reason,
&cb_flags, flags | USB_FLAGS_SLEEP);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d, cb_flags=%d, cr=%d", rval, cb_flags, completion_reason);
usba_release_ph_data(ph_data->p_ph_impl);
return (rval);
}
/*
* usb_get_alt_if:
* get the alternate interface number. Issues USB_REQ_GET_IF
* This function will access the device and block
*
* Arguments:
* dip - pointer to devinfo of the client
* if_number - interface number
* alt_number - alternate interface number
* flags - none but USB_FLAGS_SLEEP may be passed
*
* return values:
* USB_SUCCESS: alternate was set
* USB_FAILURE: alternate could not be set because pipes
* were still open or some access error occurred
*/
int
usb_get_alt_if(dev_info_t *dip,
uint_t if_number,
uint_t *alt_number,
usb_flags_t flags)
{
int rval;
usb_cr_t completion_reason;
mblk_t *data = NULL;
usb_cb_flags_t cb_flags;
usb_pipe_handle_t ph;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_alt_if: %s, interface# = 0x%x, altp = 0x%p, "
"uf = 0x%x", ddi_node_name(dip), if_number,
(void *)alt_number, flags);
if ((alt_number == NULL) || (dip == NULL)) {
return (USB_INVALID_ARGS);
}
ph = usba_get_dflt_pipe_handle(dip);
/*
* get the alternate setting
*/
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_RCPT_IF,
USB_REQ_GET_IF,
0,
if_number,
1, /* returns one byte of data */
&data, 0,
&completion_reason,
&cb_flags, flags);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d cb_flags=%d cr=%d", rval, cb_flags, completion_reason);
if ((rval == USB_SUCCESS) && data &&
(MBLKL(data) == 1)) {
*alt_number = *(data->b_rptr);
} else {
*alt_number = 0;
if (rval == USB_SUCCESS) {
rval = USB_FAILURE;
}
}
freemsg(data);
return (rval);
}
/*
* usba_get_cfg_cloud:
* Get descriptor cloud for a given configuration.
*
* Arguments:
* dip - pointer to devinfo of the client
* default_ph - default pipe handle
* cfg - which configuration to retrieve raw cloud of
*
* Returns:
* on success: mblock containing the raw data. Caller must free.
* on failure: NULL
*/
static mblk_t *
usba_get_cfg_cloud(dev_info_t *dip, usb_pipe_handle_t default_ph, int cfg)
{
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
usb_cfg_descr_t cfg_descr;
mblk_t *pdata = NULL;
if (usb_pipe_sync_ctrl_xfer(dip, default_ph,
USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
USB_REQ_GET_DESCR,
USB_DESCR_TYPE_SETUP_CFG | cfg,
0,
USB_CFG_DESCR_SIZE,
&pdata,
0,
&completion_reason,
&cb_flags,
0) != USB_SUCCESS) {
freemsg(pdata);
return (NULL);
}
(void) usb_parse_cfg_descr(pdata->b_rptr,
MBLKL(pdata), &cfg_descr, USB_CFG_DESCR_SIZE);
freemsg(pdata);
pdata = NULL;
if (usb_pipe_sync_ctrl_xfer(dip, default_ph,
USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
USB_REQ_GET_DESCR,
USB_DESCR_TYPE_SETUP_CFG | cfg,
0,
cfg_descr.wTotalLength,
&pdata,
0,
&completion_reason,
&cb_flags,
0) != USB_SUCCESS) {
freemsg(pdata);
return (NULL);
}
return (pdata);
}
/*
* usb_check_same_device:
* Check if the device connected to the port is the same as
* the previous device that was in the port. The previous device is
* represented by the dip on record for the port. Print a message
* if the device is different. If device_string arg is not NULL, it is
* included in the message. Can block.
*
* Arguments:
* dip - pointer to devinfo of the client
* log_handle - handle to which messages are logged
* log_level - one of USB_LOG_*
* log_mask - logging mask
* check_mask - one mask containing things to check:
* USB_CHK_BASIC: empty mask;
* these checks are always done.
* USB_CHK_VIDPID:
* check vid, pid only.
* USB_CHK_SERIAL: check match on device
* serial number.
* USB_CHK_CFG: check all raw config
* clouds for a match.
* NOTE: descr length and content always checked
* device_string - Device string to appear in error message
*
* return values:
* USB_SUCCESS: same device
* USB_INVALID_VERSION not same device
* USB_FAILURE: Failure processing request
* USB_INVALID_ARG: dip is invalid
*/
int
usb_check_same_device(dev_info_t *dip, usb_log_handle_t log_handle,
int log_level, int log_mask, uint_t check_mask, char *device_string)
{
usb_dev_descr_t usb_dev_descr;
usba_device_t *usba_device;
mblk_t *pdata = NULL;
uint16_t length;
int rval;
char *buf;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
boolean_t match = B_TRUE;
usb_pipe_handle_t def_ph;
if (dip == NULL) {
return (USB_INVALID_ARGS);
}
usba_device = usba_get_usba_device(dip);
length = usba_device->usb_dev_descr->bLength;
def_ph = usba_get_dflt_pipe_handle(dip);
ASSERT(def_ph);
/* get the "new" device descriptor */
rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
USB_DEV_REQ_DEV_TO_HOST |
USB_DEV_REQ_TYPE_STANDARD,
USB_REQ_GET_DESCR, /* bRequest */
USB_DESCR_TYPE_SETUP_DEV, /* wValue */
0, /* wIndex */
length, /* wLength */
&pdata, 0,
&completion_reason,
&cb_flags, USB_FLAGS_SLEEP);
if (rval != USB_SUCCESS) {
if (!((completion_reason == USB_CR_DATA_OVERRUN) && (pdata))) {
USB_DPRINTF_L3(DPRINT_MASK_USBA, usbai_log_handle,
"getting device descriptor failed (%d)", rval);
freemsg(pdata);
return (USB_FAILURE);
}
}
ASSERT(pdata != NULL);
(void) usb_parse_dev_descr(pdata->b_rptr,
MBLKL(pdata), &usb_dev_descr,
sizeof (usb_dev_descr_t));
freemsg(pdata);
pdata = NULL;
/* Always check the device descriptor length. */
if (usb_dev_descr.bLength != length) {
match = B_FALSE;
}
if ((match == B_TRUE) && (check_mask & USB_CHK_VIDPID)) {
match = (usba_device->usb_dev_descr->idVendor ==
usb_dev_descr.idVendor) &&
(usba_device->usb_dev_descr->idProduct ==
usb_dev_descr.idProduct);
} else if (bcmp((char *)usba_device->usb_dev_descr,
(char *)&usb_dev_descr, length) != 0) {
match = B_FALSE;
}
/* if requested & this device has a serial number check and compare */
if ((match == B_TRUE) && ((check_mask & USB_CHK_SERIAL) != 0) &&
(usba_device->usb_serialno_str != NULL)) {
buf = kmem_alloc(USB_MAXSTRINGLEN, KM_SLEEP);
if (usb_get_string_descr(dip, USB_LANG_ID,
usb_dev_descr.iSerialNumber, buf,
USB_MAXSTRINGLEN) == USB_SUCCESS) {
match =
(strcmp(buf, usba_device->usb_serialno_str) == 0);
}
kmem_free(buf, USB_MAXSTRINGLEN);
}
if ((match == B_TRUE) && (check_mask & USB_CHK_CFG)) {
uint8_t num_cfgs = usb_dev_descr.bNumConfigurations;
uint8_t cfg;
mblk_t *cloud;
for (cfg = 0; cfg < num_cfgs; cfg++) {
cloud = usba_get_cfg_cloud(dip, def_ph, cfg);
if (cloud == NULL) {
USB_DPRINTF_L3(DPRINT_MASK_USBA,
usbai_log_handle,
"Could not retrieve config cloud for "
"comparison");
break;
}
if (bcmp((char *)cloud->b_rptr,
usba_device->usb_cfg_array[cfg],
MBLKL(cloud)) != 0) {
freemsg(cloud);
break;
}
freemsg(cloud);
}
if (cfg != num_cfgs) {
match = B_FALSE;
}
}
if (match == B_FALSE) {
boolean_t allocated_here = (device_string == NULL);
if (allocated_here) {
device_string =
kmem_zalloc(USB_MAXSTRINGLEN, USB_FLAGS_SLEEP);
(void) usba_get_mfg_prod_sn_str(dip, device_string,
USB_MAXSTRINGLEN);
}
if (device_string[0] != '\0') {
(void) usb_log(log_handle, log_level, log_mask,
"Cannot access %s. Please reconnect.",
device_string);
} else {
(void) usb_log(log_handle, log_level, log_mask,
"Device is not identical to the "
"previous one this port.\n"
"Please disconnect and reconnect");
}
if (allocated_here) {
kmem_free(device_string, USB_MAXSTRINGLEN);
}
return (USB_INVALID_VERSION);
}
return (USB_SUCCESS);
}
/*
* usb_pipe_get_state:
* Return the state of the pipe
*
* Arguments:
* pipe_handle - pipe_handle pointer
* pipe_state - pointer to copy pipe state to
* flags:
* not used other than to check context
*
* Return Values:
* USB_SUCCESS - port state returned
* USB_* - refer to usbai.h
*/
int
usb_pipe_get_state(usb_pipe_handle_t pipe_handle,
usb_pipe_state_t *pipe_state,
usb_flags_t usb_flags)
{
usba_pipe_handle_data_t *ph_data = usba_hold_ph_data(pipe_handle);
USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
"usb_pipe_get_state: ph_data=0x%p uf=0x%x", (void *)ph_data,
usb_flags);
if (pipe_state == NULL) {
if (ph_data) {
usba_release_ph_data(ph_data->p_ph_impl);
}
return (USB_INVALID_ARGS);
}
if (ph_data == NULL) {
*pipe_state = USB_PIPE_STATE_CLOSED;
return (USB_SUCCESS);
}
mutex_enter(&ph_data->p_mutex);
*pipe_state = usba_get_ph_state(ph_data);
mutex_exit(&ph_data->p_mutex);
usba_release_ph_data(ph_data->p_ph_impl);
return (USB_SUCCESS);
}
/*
* usba_pipe_get_policy:
* Return a pipe's policy
*
* Arguments:
* pipe_handle - pipe_handle pointer
*
* Return Values:
* On success: the pipe's policy
* On failure: NULL
*/
usb_pipe_policy_t
*usba_pipe_get_policy(usb_pipe_handle_t pipe_handle)
{
usb_pipe_policy_t *pp = NULL;
usba_pipe_handle_data_t *ph_data = usba_hold_ph_data(pipe_handle);
if (ph_data) {
pp = &ph_data->p_policy;
usba_release_ph_data(ph_data->p_ph_impl);
}
return (pp);
}
/*
* usb_ep_num:
* Return the endpoint number for a given pipe handle
*
* Arguments:
* pipe_handle - pipe_handle pointer
*
* Return Values:
* endpoint number
*/
int
usb_ep_num(usb_pipe_handle_t pipe_handle)
{
usba_pipe_handle_data_t *ph_data = usba_hold_ph_data(pipe_handle);
int ep_num;
if (ph_data == NULL) {
return (USB_INVALID_PIPE);
}
mutex_enter(&ph_data->p_mutex);
ep_num = ph_data->p_ep.bEndpointAddress & USB_EP_NUM_MASK;
mutex_exit(&ph_data->p_mutex);
usba_release_ph_data(ph_data->p_ph_impl);
return (ep_num);
}
/*
* usb_get_status
* Issues USB_REQ_GET_STATUS to device/endpoint/interface
* and report in "status" arg.
*
* status reported for a "device" is
* RemoteWakeup enabled
* SelfPowered device?
*
* status reported for an "interface" is NONE.
* status reported for an "endpoint" is
* HALT set (device STALLED?)
*
* Arguments:
* dip - pointer to devinfo of the client
* ph - pipe handle
* type - bmRequestType to be used
* what - 0 for device, otherwise interface or ep number
* status - user supplied pointer for storing the status
* flags - USB_FLAGS_SLEEP (mandatory)
*
* Return Values:
* valid usb_status_t or USB_FAILURE
*/
int
usb_get_status(dev_info_t *dip,
usb_pipe_handle_t ph,
uint_t type, /* bmRequestType */
uint_t what, /* 0, interface, ept number */
uint16_t *status,
usb_flags_t flags)
{
int rval;
usb_cr_t completion_reason;
mblk_t *data = NULL;
usb_cb_flags_t cb_flags;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_get_status: type = 0x%x, what = 0x%x, uf = 0x%x",
type, what, flags);
if ((status == NULL) || (dip == NULL)) {
return (USB_INVALID_ARGS);
}
if (ph == NULL) {
return (USB_INVALID_PIPE);
}
type |= USB_DEV_REQ_DEV_TO_HOST;
/* get the status */
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
type,
USB_REQ_GET_STATUS,
0,
what,
USB_GET_STATUS_LEN, /* status is fixed 2 bytes long */
&data, 0,
&completion_reason, &cb_flags, flags);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d, cb_flags=%d, cr=%d", rval, cb_flags, completion_reason);
if ((rval == USB_SUCCESS) && data &&
(MBLKL(data) == USB_GET_STATUS_LEN)) {
*status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
} else {
*status = 0;
if (rval == USB_SUCCESS) {
rval = USB_FAILURE;
}
}
freemsg(data);
return (rval);
}
/*
* usb_clear_feature:
* Issue USB_REQ_CLEAR_FEATURE to endpoint/device/interface
*
* Arguments:
* dip - pointer to devinfo of the client
* ph - pipe handle pointer
* type - bmRequestType to be used
* feature - feature to be cleared
* what - 0 for device, otherwise interface or ep number
* flags - none (but will sleep)
*
* Return Values:
* USB_SUCCESS - on doing a successful clear feature
* USB_FAILURE - on failure
* USB_* - refer to usbai.h
*/
int
usb_clear_feature(dev_info_t *dip,
usb_pipe_handle_t ph,
uint_t type, /* bmRequestType */
uint_t feature,
uint_t what, /* 0, interface, ept number */
usb_flags_t flags)
{
int rval;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_clear_feature: type = 0x%x, feature = 0x%x, what = 0x%x "
"uf = 0x%x", type, feature, what, flags);
if (dip == NULL) {
return (USB_INVALID_ARGS);
}
if (ph == NULL) {
return (USB_INVALID_PIPE);
}
/* issue Clear feature */
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
type,
USB_REQ_CLEAR_FEATURE,
feature,
what,
0,
NULL, 0,
&completion_reason,
&cb_flags, flags | USB_FLAGS_SLEEP);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"rval=%d, cb_flags=%d, cr=%d", rval, cb_flags, completion_reason);
return (rval);
}
/*
* usb_clr_feature:
* Issue USB_REQ_CLEAR_FEATURE to endpoint/device/interface
*
* Arguments:
* dip - pointer to devinfo of the client
* type - bmRequestType to be used
* feature - feature to be cleared
* what - 0 for device, otherwise interface or ep number
* flags - USB_FLAGS_SLEEP:
* wait for completion
* cb - if USB_FLAGS_SLEEP has not been specified
* this callback function will be called on
* completion. This callback may be NULL
* and no notification of completion will then
* be provided.
* cb_arg - 2nd argument to callback function.
*
*
* Return Values:
* USB_SUCCESS - on doing a successful clear feature
* USB_FAILURE - on failure
* USB_* - refer to usbai.h
*/
int
usb_clr_feature(
dev_info_t *dip,
uint_t type, /* bmRequestType */
uint_t feature,
uint_t what, /* 0, interface, ept number */
usb_flags_t flags,
void (*cb)(
usb_pipe_handle_t ph,
usb_opaque_t arg,
int rval,
usb_cb_flags_t flags),
usb_opaque_t cb_arg)
{
usb_pipe_handle_t ph;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_clr_feature: type = 0x%x, feature = 0x%x, what = 0x%x "
"uf = 0x%x", type, feature, what, flags);
if (dip == NULL) {
return (USB_INVALID_ARGS);
}
if ((flags & USB_FLAGS_SLEEP) && servicing_interrupt()) {
return (USB_INVALID_CONTEXT);
}
ph = usba_get_dflt_pipe_handle(dip);
if (usba_hold_ph_data(ph) == NULL) {
return (USB_INVALID_PIPE);
}
return (usba_pipe_setup_func_call(dip,
usba_sync_clear_feature, (usba_ph_impl_t *)ph,
(usb_opaque_t)((uintptr_t)((type << 16 | feature << 8 | what))),
flags, cb, cb_arg));
}
static int
usba_sync_clear_feature(dev_info_t *dip,
usba_ph_impl_t *ph_impl,
usba_pipe_async_req_t *req,
usb_flags_t usb_flags)
{
uint_t n = (uint_t)((uintptr_t)(req->arg));
uint_t type = ((uint_t)n >> 16) & 0xff;
uint_t feature = ((uint_t)n >> 8) & 0xff;
uint_t what = (uint_t)n & 0xff;
int rval;
usba_device_t *usba_device;
usba_pipe_handle_data_t *ph_data;
usba_ph_impl_t *ph_im;
uchar_t ep_index;
usb_ep_descr_t *eptd;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_sync_clear_feature: "
"dip=0x%p ph=0x%p type=0x%x feature=0x%x what=0x%x fl=0x%x",
(void *)dip, (void *)ph_impl, type, feature, what, usb_flags);
rval = usb_clear_feature(dip, (usb_pipe_handle_t)ph_impl, type,
feature, what, usb_flags);
/*
* Reset data toggle to DATA0 for bulk and interrupt endpoint.
* Data toggle synchronization is not supported for isochronous
* transfer.Halt feature is not supported by control endpoint.
*
* From USB2.0 specification:
* 1.Section 5.8.5 Bulk Transfer Data Sequences
* Removal of the halt condition is achieved via software intervention
* through a separate control pipe. This recovery will reset the data
* toggle bit to DATA0 for the endpoint on both the host and the device.
*
* 2.Section 5.7.5 Interrupt Transfer Data Sequences
* Removal of the halt condition is achieved via software intervention
* through a separate control pipe. This recovery will reset the data
* toggle bit to DATA0 for the endpoint on both the host and the device.
*
* 3.Section 9.4.5
* If the condition causing a halt has been removed, clearing the Halt
* feature via a ClearFeature(ENDPOINT_HALT) request results in the
* endpoint no longer returning a STALL. For endpoints using data
* toggle, regardless of whether an endpoint has the Halt feature set, a
* ClearFeature(ENDPOINT_HALT) request always results in the data toggle
* being reinitialized to DATA0.
*
*/
if (rval == USB_SUCCESS && feature == 0) {
usba_device = usba_get_usba_device(dip);
ep_index = usb_get_ep_index((uint8_t)what);
ph_im = &usba_device->usb_ph_list[ep_index];
ph_data = usba_get_ph_data((usb_pipe_handle_t)ph_im);
eptd = &ph_data->p_ep;
if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
USB_EP_ATTR_BULK || (eptd->bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR)
usba_device->usb_hcdi_ops->
usba_hcdi_pipe_reset_data_toggle(ph_data);
}
usba_release_ph_data(ph_impl);
return (rval);
}
/*
* usb_async_req:
* function used to dispatch a request to the taskq
*
* Arguments:
* dip - pointer to devinfo node
* func - pointer to function issued by taskq
* flag - USB_FLAGS_SLEEP mostly
*
* Return Values:
* USB_SUCCESS - on doing a successful taskq invocation
* USB_FAILURE - on failure
* USB_* - refer to usbai.h
*/
int
usb_async_req(dev_info_t *dip,
void (*func)(void *),
void *arg,
usb_flags_t flag)
{
int tq_flag;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_async_req: dip=0x%p func=0x%p, arg=0x%p flag=0x%x",
(void *)dip, (void *)func, arg, flag);
if ((dip == NULL) || (func == NULL)) {
return (USB_INVALID_ARGS);
}
tq_flag = (flag & USB_FLAGS_SLEEP) ? TQ_SLEEP : TQ_NOSLEEP;
if (flag & USB_FLAGS_NOQUEUE) {
tq_flag |= TQ_NOQUEUE;
}
if (!taskq_dispatch(system_taskq, func, (void *)arg,
tq_flag)) {
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"usb_async_req: failure");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* usba_async_ph_req:
* function used to dispatch a request to the ph taskq
*
* Arguments:
* ph_data - pointer to pipe handle data
* func - pointer to function issued by taskq
* flag - USB_FLAGS_SLEEP or USB_FLAGS_NOSLEEP
*
* Return Values:
* USB_SUCCESS - on doing a successful taskq invocation
* USB_FAILURE - on failure
* USB_* - refer to usbai.h
*
* Note:
* If the caller specified USB_FLAGS_NOSLEEP, it must be
* capable of reliably recovering from a failure return
*/
int
usba_async_ph_req(usba_pipe_handle_data_t *ph_data,
void (*func)(void *),
void *arg,
usb_flags_t flag)
{
int tq_flag;
taskq_t *taskq;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usba_async_ph_req: ph_data=0x%p func=0x%p, arg=0x%p flag=0x%x",
(void *)ph_data, (void *)func, arg, flag);
if (func == NULL) {
return (USB_INVALID_ARGS);
}
tq_flag = (flag & USB_FLAGS_SLEEP) ? TQ_SLEEP : TQ_NOSLEEP;
if (ph_data && ph_data->p_taskq) {
taskq = ph_data->p_taskq;
} else {
taskq = system_taskq;
tq_flag |= TQ_NOQUEUE;
}
if (!taskq_dispatch(taskq, func, (void *)arg, tq_flag)) {
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"usba_async_ph_req: failure");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* utility functions to display CR, CB, return values
*/
typedef struct conv_table {
int what;
const char *name;
} conv_table_t;
static const char *
usba_get_name(conv_table_t *conv_table, int value)
{
int i;
for (i = 0; conv_table[i].name != NULL; i++) {
if (conv_table[i].what == value) {
return (conv_table[i].name);
}
}
return ("unknown");
}
static conv_table_t cr_table[] = {
{ USB_CR_OK, "<no errors detected>" },
{ USB_CR_CRC, "<crc error detected>" },
{ USB_CR_BITSTUFFING, "<Bit stuffing violation>" },
{ USB_CR_DATA_TOGGLE_MM, "<Data toggle PID did not match>" },
{ USB_CR_STALL, "<Endpoint returned stall PID>" },
{ USB_CR_DEV_NOT_RESP, "<Device not responding>" },
{ USB_CR_PID_CHECKFAILURE, "<Check bits on PID failed>" },
{ USB_CR_UNEXP_PID, "<Receive PID was not valid>" },
{ USB_CR_DATA_OVERRUN, "<Data size exceeded>" },
{ USB_CR_DATA_UNDERRUN, "<Less data recieved than requested>" },
{ USB_CR_BUFFER_OVERRUN, "<Memory write can't keep up>" },
{ USB_CR_BUFFER_UNDERRUN, "<Buffer underrun>" },
{ USB_CR_TIMEOUT, "<Command timed out>" },
{ USB_CR_NOT_ACCESSED, "<Not accessed by hardware>" },
{ USB_CR_NO_RESOURCES, "<No resources>" },
{ USB_CR_UNSPECIFIED_ERR, "<Unspecified usba or hcd error>" },
{ USB_CR_STOPPED_POLLING, "<Intr/ISOC IN polling stopped>" },
{ USB_CR_PIPE_CLOSING, "<Intr/ISOC IN pipe being closed>" },
{ USB_CR_PIPE_RESET, "<Intr/ISOC IN pipe reset>" },
{ USB_CR_NOT_SUPPORTED, "<Command not supported>" },
{ USB_CR_FLUSHED, "<Req was flushed>" },
{ USB_CR_HC_HARDWARE_ERR, "<USB host controller error>" },
{ 0, NULL }
};
const char *
usb_str_cr(usb_cr_t cr)
{
return (usba_get_name(cr_table, cr));
}
static conv_table_t cb_flags_table[] = {
{ USB_CB_NO_INFO, "<callback processed>" },
{ USB_CB_STALL_CLEARED, "<stall cleared>" },
{ USB_CB_FUNCTIONAL_STALL, "<functional stall>" },
{ USB_CB_PROTOCOL_STALL, "<protocol stall>" },
{ USB_CB_RESET_PIPE, "<pipe reset>" },
{ USB_CB_ASYNC_REQ_FAILED, "<thread could not be started>" },
{ USB_CB_NO_RESOURCES, "<no resources>" },
{ USB_CB_SUBMIT_FAILED, "<submit failed>" },
{ USB_CB_INTR_CONTEXT, "<Callback executing in interrupt context>" },
{ 0, NULL }
};
/*ARGSUSED*/
char *
usb_str_cb_flags(usb_cb_flags_t cb_flags, char *buffer, size_t length)
{
int i;
buffer[0] = '\0';
if (cb_flags == USB_CB_NO_INFO) {
(void) strncpy(buffer, cb_flags_table[0].name, length);
} else {
for (i = 0; cb_flags_table[i].name != NULL; i++) {
if (cb_flags & cb_flags_table[i].what) {
(void) strncpy(&buffer[strlen(buffer)],
cb_flags_table[0].name,
length - strlen(buffer) - 1);
}
}
}
return (buffer);
}
static conv_table_t pipe_state_table[] = {
{ USB_PIPE_STATE_CLOSED, "<closed>" },
{ USB_PIPE_STATE_IDLE, "<idle>" },
{ USB_PIPE_STATE_ACTIVE, "<active>" },
{ USB_PIPE_STATE_ERROR, "<error>" },
{ USB_PIPE_STATE_CLOSING, "<closing>" },
{ 0, NULL }
};
const char *
usb_str_pipe_state(usb_pipe_state_t state)
{
return (usba_get_name(pipe_state_table, state));
}
static conv_table_t dev_state[] = {
{ USB_DEV_ONLINE, "<online>" },
{ USB_DEV_DISCONNECTED, "<disconnected>" },
{ USB_DEV_SUSPENDED, "<suspended>" },
{ USB_DEV_PWRED_DOWN, "<powered down>" },
{ 0, NULL }
};
const char *
usb_str_dev_state(int state)
{
return (usba_get_name(dev_state, state));
}
static conv_table_t rval_table[] = {
{ USB_SUCCESS, "<success>" },
{ USB_FAILURE, "<failure>" },
{ USB_NO_RESOURCES, "<no resources>" },
{ USB_NO_BANDWIDTH, "<no bandwidth>" },
{ USB_NOT_SUPPORTED, "<not supported>" },
{ USB_PIPE_ERROR, "<pipe error>" },
{ USB_INVALID_PIPE, "<invalid pipe>" },
{ USB_NO_FRAME_NUMBER, "<no frame number>" },
{ USB_INVALID_START_FRAME, "<invalid frame>" },
{ USB_HC_HARDWARE_ERROR, "<hw error>" },
{ USB_INVALID_REQUEST, "<invalid request>" },
{ USB_INVALID_CONTEXT, "<invalid context>" },
{ USB_INVALID_VERSION, "<invalid version>" },
{ USB_INVALID_ARGS, "<invalid args>" },
{ USB_INVALID_PERM, "<invalid perms>" },
{ USB_BUSY, "<busy>" },
{ 0, NULL }
};
const char *
usb_str_rval(int rval)
{
return (usba_get_name(rval_table, rval));
}
/*
* function to convert USB return values to close errno
*/
static struct usb_rval2errno_entry {
int rval;
int Errno;
} usb_rval2errno_table[] = {
{ USB_SUCCESS, 0 },
{ USB_FAILURE, EIO },
{ USB_NO_RESOURCES, ENOMEM },
{ USB_NO_BANDWIDTH, EAGAIN },
{ USB_NOT_SUPPORTED, ENOTSUP },
{ USB_PIPE_ERROR, EIO },
{ USB_INVALID_PIPE, EINVAL },
{ USB_NO_FRAME_NUMBER, EINVAL },
{ USB_INVALID_START_FRAME, EINVAL },
{ USB_HC_HARDWARE_ERROR, EIO },
{ USB_INVALID_REQUEST, EINVAL },
{ USB_INVALID_CONTEXT, EINVAL },
{ USB_INVALID_VERSION, EINVAL },
{ USB_INVALID_ARGS, EINVAL },
{ USB_INVALID_PERM, EACCES },
{ USB_BUSY, EBUSY },
};
#define USB_RVAL2ERRNO_TABLE_SIZE (sizeof (usb_rval2errno_table) / \
sizeof (struct usb_rval2errno_entry))
int
usb_rval2errno(int rval)
{
int i;
for (i = 0; i < USB_RVAL2ERRNO_TABLE_SIZE; i++) {
if (usb_rval2errno_table[i].rval == rval) {
return (usb_rval2errno_table[i].Errno);
}
}
return (EIO);
}
/*
* serialization
*/
usb_serialization_t
usb_init_serialization(
dev_info_t *dip,
uint_t flag)
{
usba_serialization_impl_t *impl_tokenp = kmem_zalloc(
sizeof (usba_serialization_impl_t), KM_SLEEP);
usba_device_t *usba_device;
ddi_iblock_cookie_t cookie = NULL;
if (dip) {
usba_device = usba_get_usba_device(dip);
cookie = usba_hcdi_get_hcdi(
usba_device->usb_root_hub_dip)->hcdi_iblock_cookie;
}
impl_tokenp->s_dip = dip;
impl_tokenp->s_flag = flag;
mutex_init(&impl_tokenp->s_mutex, NULL, MUTEX_DRIVER, cookie);
cv_init(&impl_tokenp->s_cv, NULL, CV_DRIVER, NULL);
return ((usb_serialization_t)impl_tokenp);
}
void
usb_fini_serialization(
usb_serialization_t tokenp)
{
usba_serialization_impl_t *impl_tokenp;
if (tokenp) {
impl_tokenp = (usba_serialization_impl_t *)tokenp;
ASSERT(impl_tokenp->s_count == 0);
cv_destroy(&impl_tokenp->s_cv);
mutex_destroy(&impl_tokenp->s_mutex);
kmem_free(impl_tokenp, sizeof (usba_serialization_impl_t));
}
}
/*
* usb_serialize_access() permits single threaded access.
*
* If tokenp is initialized with USB_INIT_SER_CHECK_SAME_THREAD,
* it is reentrant with respect to thread. The thread must
* hold and release the same number of times.
*
* If tokenp is initialized without USB_INIT_SER_CHECK_SAME_THREAD,
* it is not reentrant by the same thread. It is something like
* a semaphore.
*/
int
usb_serialize_access(
usb_serialization_t tokenp, uint_t how_to_wait, uint_t delta_timeout)
{
int rval = 1; /* Must be initialized > 0 */
clock_t abs_timeout;
usba_serialization_impl_t *impl_tokenp;
impl_tokenp = (usba_serialization_impl_t *)tokenp;
/*
* Convert delta timeout in ms to absolute timeout in ticks, if used.
*/
if ((how_to_wait == USB_TIMEDWAIT) ||
(how_to_wait == USB_TIMEDWAIT_SIG)) {
/* Convert timeout arg (in ms) to hz */
abs_timeout = ddi_get_lbolt() +
drv_usectohz(delta_timeout * 1000);
}
/* Get mutex after calc abs time, to count time waiting for mutex. */
mutex_enter(&impl_tokenp->s_mutex);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_serialize_access: tok=0x%p dip=0x%p cnt=%d thr=0x%p, "
"flg=0x%x, abs_tmo=0x%lx",
(void *)impl_tokenp, (void *)impl_tokenp->s_dip,
impl_tokenp->s_count, (void *)impl_tokenp->s_thread,
how_to_wait, abs_timeout);
if ((impl_tokenp->s_flag & USB_INIT_SER_CHECK_SAME_THREAD) == 0 ||
impl_tokenp->s_thread != curthread) {
/*
* There are three ways to break out of the loop:
* 1) Condition met (s_count == 0) - higher prio test
* 2) kill(2) signal received (rval == 0)
* 3) timeout occurred (rval == -1)
* If condition met, whether or not signal or timeout occurred
* take access. If condition not met, check other exit means.
*/
while (impl_tokenp->s_count != 0) {
/* cv_timedwait* returns -1 on timeout. */
/* cv_wait*_sig returns 0 on (kill(2)) signal. */
if (rval <= 0) {
mutex_exit(&impl_tokenp->s_mutex);
USB_DPRINTF_L4(DPRINT_MASK_USBA,
usbai_log_handle,
"usb_serialize_access: "
"tok=0x%p exit due to %s",
(void *)impl_tokenp,
((rval == 0) ? "signal" : "timeout"));
return (rval);
}
switch (how_to_wait) {
default:
how_to_wait = USB_WAIT;
/* FALLTHROUGH */
case USB_WAIT:
cv_wait(&impl_tokenp->s_cv,
&impl_tokenp->s_mutex);
break;
case USB_WAIT_SIG:
rval = cv_wait_sig(&impl_tokenp->s_cv,
&impl_tokenp->s_mutex);
break;
case USB_TIMEDWAIT:
rval = cv_timedwait(&impl_tokenp->s_cv,
&impl_tokenp->s_mutex, abs_timeout);
break;
case USB_TIMEDWAIT_SIG:
rval = cv_timedwait_sig(&impl_tokenp->s_cv,
&impl_tokenp->s_mutex, abs_timeout);
break;
}
}
impl_tokenp->s_thread = curthread;
}
impl_tokenp->s_count++;
ASSERT(!(impl_tokenp->s_count > 1 &&
(impl_tokenp->s_flag & USB_INIT_SER_CHECK_SAME_THREAD) == 0));
mutex_exit(&impl_tokenp->s_mutex);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_serialize_access exit: tok=0x%p thr=0x%p", (void *)impl_tokenp,
(void *)curthread);
return (1);
}
/*ARGSUSED*/
int
usb_try_serialize_access(
usb_serialization_t tokenp, uint_t flag)
{
usba_serialization_impl_t *impl_tokenp =
(usba_serialization_impl_t *)tokenp;
mutex_enter(&impl_tokenp->s_mutex);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_try_serialize_access: tok=0x%p dip=0x%p cnt=%d thr=0x%p",
(void *)impl_tokenp, (void *)impl_tokenp->s_dip,
impl_tokenp->s_count, (void *)curthread);
/*
* If lock is not taken (s_count is 0), take it.
* If lock is already taken, the thread is owner and lock
* is reentrant, take it.
* Otherwise, fail the access.
*/
if (!impl_tokenp->s_count || ((impl_tokenp->s_thread == curthread) &&
(impl_tokenp->s_flag & USB_INIT_SER_CHECK_SAME_THREAD))) {
impl_tokenp->s_thread = curthread;
impl_tokenp->s_count++;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_try_serialize_access success: tok=0x%p",
(void *)impl_tokenp);
mutex_exit(&impl_tokenp->s_mutex);
return (USB_SUCCESS);
}
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_try_serialize_access failed: "
"tok=0x%p dip=0x%p cnt=%d thr=0x%p",
(void *)impl_tokenp, (void *)impl_tokenp->s_dip,
impl_tokenp->s_count, (void *)impl_tokenp->s_thread);
mutex_exit(&impl_tokenp->s_mutex);
return (USB_FAILURE);
}
void
usb_release_access(
usb_serialization_t tokenp)
{
usba_serialization_impl_t *impl_tokenp =
(usba_serialization_impl_t *)tokenp;
mutex_enter(&impl_tokenp->s_mutex);
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_release_access: tok=0x%p dip=0x%p count=%d thr=0x%p",
(void *)impl_tokenp, (void *)impl_tokenp->s_dip,
impl_tokenp->s_count, (void *)curthread);
ASSERT(impl_tokenp->s_count > 0);
if (impl_tokenp->s_flag & USB_INIT_SER_CHECK_SAME_THREAD) {
if (impl_tokenp->s_thread != curthread) {
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"usb_release_access: release from wrong thread");
}
ASSERT(impl_tokenp->s_thread == curthread);
}
if (--impl_tokenp->s_count == 0) {
impl_tokenp->s_thread = NULL;
cv_broadcast(&impl_tokenp->s_cv);
}
mutex_exit(&impl_tokenp->s_mutex);
}
/*
* usb_fail_checkpoint:
* fail checkpoint as driver/device could not be quiesced
*/
/*ARGSUSED*/
void
usb_fail_checkpoint(dev_info_t *dip, usb_flags_t flags)
{
usba_device_t *usba_device = usba_get_usba_device(dip);
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"usb_fail_checkpoint: %s%d", ddi_driver_name(dip),
ddi_get_instance(dip));
mutex_enter(&usba_device->usb_mutex);
usba_device->usb_no_cpr++;
mutex_exit(&usba_device->usb_mutex);
}
_NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", datab))
/*
* usba_mk_mctl:
* create a USB style M_CTL message, given an iocblk and a buffer
* returns mblk_t * on success, NULL on failure
*/
mblk_t *
usba_mk_mctl(struct iocblk mctlmsg, void *buf, size_t len)
{
mblk_t *bp1, *bp2;
if ((bp1 = allocb(sizeof (struct iocblk), BPRI_HI)) != NULL) {
/* LINTED E_BAD_PTR_CAST_ALIGN */
*((struct iocblk *)bp1->b_datap->db_base) = mctlmsg;
bp1->b_datap->db_type = M_CTL;
bp1->b_wptr += sizeof (struct iocblk);
if (buf != NULL) {
if ((bp2 = allocb(len, BPRI_HI)) != NULL) {
bp1->b_cont = bp2;
bcopy(buf, bp2->b_datap->db_base, len);
bp2->b_wptr += len;
} else {
freemsg(bp1);
bp1 = NULL;
}
}
}
return (bp1);
}
#ifdef ALLOCB_TEST
#undef allocb
mblk_t *
usba_test_allocb(size_t size, uint_t pri)
{
if (ddi_get_lbolt() & 0x1) {
return (NULL);
} else {
return (allocb(size, pri));
}
}
#endif
/*
* usb common power management for usb_mid, usb_ia and maybe other simple
* drivers.
*/
/*
* functions to handle power transition for OS levels 0 -> 3
*/
static int
usb_common_pwrlvl0(dev_info_t *dip, uint8_t *pm, int *dev_state)
{
int rval;
switch (*dev_state) {
case USB_DEV_ONLINE:
/* Issue USB D3 command to the device here */
rval = usb_set_device_pwrlvl3(dip);
ASSERT(rval == USB_SUCCESS);
*dev_state = USB_DEV_PWRED_DOWN;
*pm = USB_DEV_OS_PWR_OFF;
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
/* allow a disconnected/cpr'ed device to go to low pwr */
return (USB_SUCCESS);
case USB_DEV_PWRED_DOWN:
default:
return (USB_FAILURE);
}
}
/* ARGSUSED */
static int
usb_common_pwrlvl1(dev_info_t *dip, uint8_t *pm, int *dev_state)
{
int rval;
/* Issue USB D2 command to the device here */
rval = usb_set_device_pwrlvl2(dip);
ASSERT(rval == USB_SUCCESS);
return (USB_FAILURE);
}
/* ARGSUSED */
static int
usb_common_pwrlvl2(dev_info_t *dip, uint8_t *pm, int *dev_state)
{
int rval;
/* Issue USB D1 command to the device here */
rval = usb_set_device_pwrlvl1(dip);
ASSERT(rval == USB_SUCCESS);
return (USB_FAILURE);
}
static int
usb_common_pwrlvl3(dev_info_t *dip, uint8_t *pm, int *dev_state)
{
int rval;
switch (*dev_state) {
case USB_DEV_PWRED_DOWN:
/* Issue USB D0 command to the device here */
rval = usb_set_device_pwrlvl0(dip);
ASSERT(rval == USB_SUCCESS);
*dev_state = USB_DEV_ONLINE;
*pm = USB_DEV_OS_FULL_PWR;
/* FALLTHRU */
case USB_DEV_ONLINE:
/* we are already in full power */
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
/* allow a disconnected/cpr'ed device to go to low power */
return (USB_SUCCESS);
default:
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"usb_common_pwrlvl3: Illegal state (%s)",
usb_str_dev_state(*dev_state));
return (USB_FAILURE);
}
}
/* power management */
int
usba_common_power(dev_info_t *dip, uint8_t *pm, int *dev_state, int level)
{
int rval = DDI_FAILURE;
switch (level) {
case USB_DEV_OS_PWR_OFF:
rval = usb_common_pwrlvl0(dip, pm, dev_state);
break;
case USB_DEV_OS_PWR_1:
rval = usb_common_pwrlvl1(dip, pm, dev_state);
break;
case USB_DEV_OS_PWR_2:
rval = usb_common_pwrlvl2(dip, pm, dev_state);
break;
case USB_DEV_OS_FULL_PWR:
rval = usb_common_pwrlvl3(dip, pm, dev_state);
break;
}
return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
}
/*
* register and unregister for events from our parent for usb_mid and usb_ia
* and maybe other nexus driver.
*
* Note: The cookie fields in usba_device structure is not used. They are
* used/shared by children.
*/
void
usba_common_register_events(dev_info_t *dip, uint_t if_num,
void (*event_cb)(dev_info_t *, ddi_eventcookie_t, void *, void *))
{
int rval;
usba_evdata_t *evdata;
ddi_eventcookie_t cookie;
USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
"usb_common_register_events:");
evdata = usba_get_evdata(dip);
/* get event cookie, discard level and icookie for now */
rval = ddi_get_eventcookie(dip, DDI_DEVI_REMOVE_EVENT,
&cookie);
if (rval == DDI_SUCCESS) {
rval = ddi_add_event_handler(dip,
cookie, event_cb, NULL, &evdata->ev_rm_cb_id);
if (rval != DDI_SUCCESS) {
goto fail;
}
}
rval = ddi_get_eventcookie(dip, DDI_DEVI_INSERT_EVENT,
&cookie);
if (rval == DDI_SUCCESS) {
rval = ddi_add_event_handler(dip, cookie, event_cb,
NULL, &evdata->ev_ins_cb_id);
if (rval != DDI_SUCCESS) {
goto fail;
}
}
rval = ddi_get_eventcookie(dip, USBA_PRE_SUSPEND_EVENT, &cookie);
if (rval == DDI_SUCCESS) {
rval = ddi_add_event_handler(dip,
cookie, event_cb, NULL, &evdata->ev_suspend_cb_id);
if (rval != DDI_SUCCESS) {
goto fail;
}
}
rval = ddi_get_eventcookie(dip, USBA_POST_RESUME_EVENT, &cookie);
if (rval == DDI_SUCCESS) {
rval = ddi_add_event_handler(dip, cookie, event_cb, NULL,
&evdata->ev_resume_cb_id);
if (rval != DDI_SUCCESS) {
goto fail;
}
}
return;
fail:
usba_common_unregister_events(dip, if_num);
}
void
usba_common_unregister_events(dev_info_t *dip, uint_t if_num)
{
usba_evdata_t *evdata;
usba_device_t *usba_device = usba_get_usba_device(dip);
int i;
evdata = usba_get_evdata(dip);
if (evdata->ev_rm_cb_id != NULL) {
(void) ddi_remove_event_handler(evdata->ev_rm_cb_id);
evdata->ev_rm_cb_id = NULL;
}
if (evdata->ev_ins_cb_id != NULL) {
(void) ddi_remove_event_handler(evdata->ev_ins_cb_id);
evdata->ev_ins_cb_id = NULL;
}
if (evdata->ev_suspend_cb_id != NULL) {
(void) ddi_remove_event_handler(evdata->ev_suspend_cb_id);
evdata->ev_suspend_cb_id = NULL;
}
if (evdata->ev_resume_cb_id != NULL) {
(void) ddi_remove_event_handler(evdata->ev_resume_cb_id);
evdata->ev_resume_cb_id = NULL;
}
/* clear event data for children, required for cfgmadm unconfigure */
mutex_enter(&usba_device->usb_mutex);
if (usb_owns_device(dip)) {
usba_free_evdata(usba_device->usb_evdata);
usba_device->usb_evdata = NULL;
usba_device->rm_cookie = NULL;
usba_device->ins_cookie = NULL;
usba_device->suspend_cookie = NULL;
usba_device->resume_cookie = NULL;
} else {
for (i = 0; i < if_num; i++) {
usba_device->usb_client_flags[usba_get_ifno(dip) + i]
&= ~USBA_CLIENT_FLAG_EV_CBS;
}
}
mutex_exit(&usba_device->usb_mutex);
}