#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);
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);
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;
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,
alt_setting,
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);
}
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;
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,
alt_setting,
endpoint_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_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);
}
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));
}
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);
}
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 < 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_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);
}
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);
}
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);
}
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);
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);
}
if (usba_is_root_hub(dip)) {
usba_release_ph_data(ph_impl);
return (USB_FAILURE);
}
pdip = ddi_get_parent(dip);
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);
ASSERT(size == USB_CFG_DESCR_SIZE);
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);
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"configuration#", usba_device->usb_cfg_value);
}
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);
}
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);
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,
&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);
}
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);
}
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);
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);
}
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);
}
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 (!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);
}
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);
}
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);
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,
&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);
}
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);
}
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);
rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
USB_DEV_REQ_DEV_TO_HOST |
USB_DEV_REQ_TYPE_STANDARD,
USB_REQ_GET_DESCR,
USB_DESCR_TYPE_SETUP_DEV,
0,
length,
&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;
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 ((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);
}
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);
}
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);
}
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);
}
int
usb_get_status(dev_info_t *dip,
usb_pipe_handle_t ph,
uint_t type,
uint_t what,
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;
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
type,
USB_REQ_GET_STATUS,
0,
what,
USB_GET_STATUS_LEN,
&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);
}
int
usb_clear_feature(dev_info_t *dip,
usb_pipe_handle_t ph,
uint_t type,
uint_t feature,
uint_t what,
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);
}
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);
}
int
usb_clr_feature(
dev_info_t *dip,
uint_t type,
uint_t feature,
uint_t what,
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);
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);
}
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, arg,
tq_flag) == TASKQID_INVALID) {
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"usb_async_req: failure");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
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, arg, tq_flag) == TASKQID_INVALID) {
USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
"usba_async_ph_req: failure");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
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 }
};
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));
}
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);
}
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));
}
}
int
usb_serialize_access(
usb_serialization_t tokenp, uint_t how_to_wait, uint_t delta_timeout)
{
int rval = 1;
clock_t abs_timeout = 0;
usba_serialization_impl_t *impl_tokenp;
impl_tokenp = (usba_serialization_impl_t *)tokenp;
if ((how_to_wait == USB_TIMEDWAIT) ||
(how_to_wait == USB_TIMEDWAIT_SIG)) {
abs_timeout = ddi_get_lbolt() +
drv_usectohz(delta_timeout * 1000);
}
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) {
while (impl_tokenp->s_count != 0) {
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;
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);
}
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 (!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);
}
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))
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) {
*((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
static int
usb_common_pwrlvl0(dev_info_t *dip, uint8_t *pm, int *dev_state)
{
int rval;
switch (*dev_state) {
case USB_DEV_ONLINE:
rval = usb_set_device_pwrlvl3(dip);
ASSERT(rval == USB_SUCCESS);
*dev_state = USB_DEV_PWRED_DOWN;
*pm = USB_DEV_OS_PWR_OFF;
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
return (USB_SUCCESS);
case USB_DEV_PWRED_DOWN:
default:
return (USB_FAILURE);
}
}
static int
usb_common_pwrlvl1(dev_info_t *dip, uint8_t *pm, int *dev_state)
{
int rval;
rval = usb_set_device_pwrlvl2(dip);
ASSERT(rval == USB_SUCCESS);
return (USB_FAILURE);
}
static int
usb_common_pwrlvl2(dev_info_t *dip, uint8_t *pm, int *dev_state)
{
int rval;
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:
rval = usb_set_device_pwrlvl0(dip);
ASSERT(rval == USB_SUCCESS);
*dev_state = USB_DEV_ONLINE;
*pm = USB_DEV_OS_FULL_PWR;
case USB_DEV_ONLINE:
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
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);
}
}
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);
}
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);
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;
}
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);
}