#define USBA_FRAMEWORK
#include <sys/usb/usba.h>
#include <sys/usb/usba/usba_impl.h>
#include <sys/usb/usba/usba_private.h>
#include <sys/usb/usba/hcdi_impl.h>
#include <sys/usb/hubd/hub.h>
#include <sys/usb/usba/usbai_register_impl.h>
int usb_log_descr_tree(usb_client_dev_data_t *, usb_log_handle_t,
uint_t, uint_t);
usb_log_handle_t usbai_reg_log_handle;
uint_t usbai_register_errlevel = USB_LOG_L2;
uint_t usbai_register_dump_errlevel = USB_LOG_L2;
uint_t usbai_register_errmask = (uint_t)-1;
static int usba_build_descr_tree(dev_info_t *, usba_device_t *,
usb_client_dev_data_t *);
static void usba_process_cfg_descr(usba_reg_state_t *);
static int usba_process_if_descr(usba_reg_state_t *, boolean_t *);
static int usba_process_ep_descr(usba_reg_state_t *);
static int usba_process_ss_ep_comp_descr(usba_reg_state_t *);
static int usba_process_cv_descr(usba_reg_state_t *);
static int usba_set_parse_values(dev_info_t *dip, usba_device_t *usba_device,
usba_reg_state_t *state);
static void* usba_kmem_realloc(void *, int, int);
static void usba_augment_array(void **, uint_t, uint_t);
static void usba_make_alts_sparse(usb_alt_if_data_t **, uint_t *);
static void usba_order_tree(usba_reg_state_t *);
static void usba_free_if_array(usb_if_data_t *, uint_t);
static void usba_free_ep_array(usb_ep_data_t *, uint_t);
static void usba_free_cv_array(usb_cvs_data_t *, uint_t);
static int usba_dump_descr_tree(dev_info_t *, usb_client_dev_data_t *,
usb_log_handle_t, uint_t, uint_t);
static void usba_dump_if(usb_if_data_t *, usb_log_handle_t,
uint_t, uint_t, char *);
static void usba_dump_ep(uint_t, usb_ep_data_t *, usb_log_handle_t, uint_t,
uint_t, char *);
static void usba_dump_cv(usb_cvs_data_t *, usb_log_handle_t, uint_t, uint_t,
char *, int);
static void usba_dump_bin(uint8_t *, int, int, usb_log_handle_t,
uint_t, uint_t, char *, int);
void
usba_usbai_register_initialization()
{
usbai_reg_log_handle = usb_alloc_log_hdl(NULL, "usbreg",
&usbai_register_errlevel,
&usbai_register_errmask, NULL,
0);
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_usbai_register_initialization");
}
void
usba_usbai_register_destroy()
{
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_usbai_register destroy");
usb_free_log_hdl(usbai_reg_log_handle);
}
int
usb_client_attach(dev_info_t *dip, uint_t version, usb_flags_t flags)
{
int rval;
usba_device_t *usba_device;
if (dip == NULL) {
return (USB_INVALID_ARGS);
}
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_client attach:");
usba_device = usba_get_usba_device(dip);
if ((version !=
USBA_MAKE_VER(USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER)) &&
((USBA_GET_MAJOR(version) != USBA_MAJOR_VER) ||
(USBA_GET_MINOR(version) > USBA_MINOR_VER))) {
USB_DPRINTF_L1(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"Incorrect USB driver version for %s%d: found: %d.%d, "
"expecting %d.%d",
ddi_driver_name(dip), ddi_get_instance(dip),
USBA_GET_MAJOR(version), USBA_GET_MINOR(version),
USBA_MAJOR_VER, USBA_MINOR_VER);
return (USB_INVALID_VERSION);
}
if (version == USBA_MAKE_VER(USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER)) {
USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"Accepting legacy USB driver version %d.%d for %s%d",
USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER,
ddi_driver_name(dip), ddi_get_instance(dip));
}
rval = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "driver-major",
USBA_GET_MAJOR(version));
if (rval != DDI_PROP_SUCCESS) {
return (USB_FAILURE);
}
rval = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "driver-minor",
USBA_GET_MINOR(version));
if (rval != DDI_PROP_SUCCESS) {
return (USB_FAILURE);
}
mutex_enter(&usba_device->usb_mutex);
if (strcmp(ddi_driver_name(dip), "usb_mid") != 0) {
usba_device->usb_client_flags[usba_get_ifno(dip)] |=
USBA_CLIENT_FLAG_ATTACH;
usba_device->usb_client_attach_list->dip = dip;
}
mutex_exit(&usba_device->usb_mutex);
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_client attach: done");
return (USB_SUCCESS);
}
void
usb_client_detach(dev_info_t *dip, usb_client_dev_data_t *reg)
{
usba_device_t *usba_device = usba_get_usba_device(dip);
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_client_detach:");
if (dip) {
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"Unregistering usb client %s%d: reg=0x%p",
ddi_driver_name(dip), ddi_get_instance(dip), (void *)reg);
usb_free_dev_data(dip, reg);
mutex_enter(&usba_device->usb_mutex);
if (strcmp(ddi_driver_name(dip), "usb_mid") != 0) {
usba_device->usb_client_flags[usba_get_ifno(dip)] &=
~USBA_CLIENT_FLAG_ATTACH;
}
mutex_exit(&usba_device->usb_mutex);
}
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_client_detach done");
}
int
usb_register_client(dev_info_t *dip, uint_t version,
usb_client_dev_data_t **reg, usb_reg_parse_lvl_t parse_level,
usb_flags_t flags)
{
int rval = usb_client_attach(dip, version, flags);
if (rval == USB_SUCCESS) {
rval = usb_get_dev_data(dip, reg, parse_level, flags);
if (rval != USB_SUCCESS) {
usb_client_detach(dip, NULL);
}
}
return (rval);
}
void
usb_unregister_client(dev_info_t *dip, usb_client_dev_data_t *reg)
{
usb_client_detach(dip, reg);
}
int
usb_get_dev_data(dev_info_t *dip,
usb_client_dev_data_t **reg, usb_reg_parse_lvl_t parse_level,
usb_flags_t flags)
{
usb_client_dev_data_t *usb_reg = NULL;
char *tmpbuf = NULL;
usba_device_t *usba_device;
int rval = USB_SUCCESS;
if ((dip == NULL) || (reg == NULL)) {
return (USB_INVALID_ARGS);
}
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_get_dev_data: %s%d",
ddi_driver_name(dip), ddi_get_instance(dip));
*reg = NULL;
if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"driver-major", -1) == -1) {
return (USB_INVALID_VERSION);
}
if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"driver-minor", -1) == -1) {
return (USB_INVALID_VERSION);
}
usb_reg = kmem_zalloc(sizeof (usb_client_dev_data_t), KM_SLEEP);
usba_device = usba_get_usba_device(dip);
usb_reg->dev_descr = usba_device->usb_dev_descr;
usb_reg->dev_default_ph = usba_get_dflt_pipe_handle(dip);
if (usb_reg->dev_default_ph == NULL) {
kmem_free(usb_reg, sizeof (usb_client_dev_data_t));
return (USB_FAILURE);
}
usb_reg->dev_iblock_cookie = usba_hcdi_get_hcdi(
usba_device->usb_root_hub_dip)->hcdi_soft_iblock_cookie;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"cookie = 0x%p", (void *)usb_reg->dev_iblock_cookie);
tmpbuf = (char *)kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
if (usba_device->usb_mfg_str != NULL) {
usb_reg->dev_mfg = kmem_zalloc(
strlen(usba_device->usb_mfg_str) + 1, KM_SLEEP);
(void) strcpy(usb_reg->dev_mfg, usba_device->usb_mfg_str);
}
if (usba_device->usb_product_str != NULL) {
usb_reg->dev_product = kmem_zalloc(
strlen(usba_device->usb_product_str) + 1,
KM_SLEEP);
(void) strcpy(usb_reg->dev_product,
usba_device->usb_product_str);
}
if (usba_device->usb_serialno_str != NULL) {
usb_reg->dev_serial = kmem_zalloc(
strlen(usba_device->usb_serialno_str) + 1,
KM_SLEEP);
(void) strcpy(usb_reg->dev_serial,
usba_device->usb_serialno_str);
}
if ((usb_reg->dev_parse_level = parse_level) == USB_PARSE_LVL_NONE) {
rval = USB_SUCCESS;
} else if ((rval = usba_build_descr_tree(dip, usba_device, usb_reg)) !=
USB_SUCCESS) {
usb_unregister_client(dip, usb_reg);
usb_reg = NULL;
} else {
if (usb_reg->dev_n_cfg == 1) {
usb_reg->dev_curr_cfg = &usb_reg->dev_cfg[0];
} else {
mutex_enter(&usba_device->usb_mutex);
usb_reg->dev_curr_cfg =
&usb_reg->dev_cfg[usba_device->usb_active_cfg_ndx];
mutex_exit(&usba_device->usb_mutex);
ASSERT(usb_reg->dev_curr_cfg != NULL);
ASSERT(usb_reg->dev_curr_cfg->cfg_descr.bLength ==
USB_CFG_DESCR_SIZE);
}
usb_reg->dev_curr_if = usba_get_ifno(dip);
#ifdef DEBUG
(void) usb_log_descr_tree(usb_reg, usbai_reg_log_handle,
usbai_register_dump_errlevel, (uint_t)-1);
#endif
if ((usb_reg->dev_curr_cfg->cfg_n_if <= usb_reg->dev_curr_if) ||
(usb_reg->dev_curr_cfg->cfg_descr.bLength == 0) ||
(usb_reg->dev_curr_cfg->cfg_if[usb_reg->dev_curr_if].
if_n_alt == 0)) {
USB_DPRINTF_L2(DPRINT_MASK_ALL, usbai_reg_log_handle,
"usb_get_dev_data: dev_curr_cfg or "
"dev_curr_if have no descriptors");
usb_unregister_client(dip, usb_reg);
usb_reg = NULL;
rval = USB_FAILURE;
}
}
*reg = usb_reg;
kmem_free(tmpbuf, USB_MAXSTRINGLEN);
if (rval == USB_SUCCESS) {
usb_client_dev_data_list_t *entry = kmem_zalloc(
sizeof (*entry), KM_SLEEP);
mutex_enter(&usba_device->usb_mutex);
usba_device->usb_client_flags[usba_get_ifno(dip)] |=
USBA_CLIENT_FLAG_DEV_DATA;
entry->cddl_dip = dip;
entry->cddl_dev_data = usb_reg;
entry->cddl_ifno = usba_get_ifno(dip);
entry->cddl_next =
usba_device->usb_client_dev_data_list.cddl_next;
if (entry->cddl_next) {
entry->cddl_next->cddl_prev = entry;
}
entry->cddl_prev = &usba_device->usb_client_dev_data_list;
usba_device->usb_client_dev_data_list.cddl_next = entry;
mutex_exit(&usba_device->usb_mutex);
}
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_get_dev_data rval=%d", rval);
return (rval);
}
void
usb_free_dev_data(dev_info_t *dip, usb_client_dev_data_t *reg)
{
if (dip == NULL) {
return;
}
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_free_dev_data %s%d: reg=0x%p",
ddi_driver_name(dip), ddi_get_instance(dip), (void *)reg);
if (reg != NULL) {
usba_device_t *usba_device = usba_get_usba_device(dip);
usb_client_dev_data_list_t *next, *prev, *entry;
int matches = 0;
if (reg->dev_serial != NULL) {
kmem_free((char *)reg->dev_serial,
strlen((char *)reg->dev_serial) + 1);
}
if (reg->dev_product != NULL) {
kmem_free((char *)reg->dev_product,
strlen((char *)reg->dev_product) + 1);
}
if (reg->dev_mfg != NULL) {
kmem_free((char *)reg->dev_mfg,
strlen((char *)reg->dev_mfg) + 1);
}
if (reg->dev_cfg != NULL) {
usb_free_descr_tree(dip, reg);
}
mutex_enter(&usba_device->usb_mutex);
prev = &usba_device->usb_client_dev_data_list;
entry = usba_device->usb_client_dev_data_list.cddl_next;
while (entry) {
next = entry->cddl_next;
if ((dip == entry->cddl_dip) &&
(reg == entry->cddl_dev_data)) {
prev->cddl_next = entry->cddl_next;
if (entry->cddl_next) {
entry->cddl_next->cddl_prev = prev;
}
kmem_free(entry, sizeof (*entry));
} else {
if (usba_get_ifno(dip) == entry->cddl_ifno) {
matches++;
}
prev = entry;
}
entry = next;
}
USB_DPRINTF_L3(DPRINT_MASK_REGISTER,
usbai_reg_log_handle,
"usb_free_dev_data: next=0x%p flags[%d]=0x%x",
(void *)usba_device->usb_client_dev_data_list.cddl_next,
usba_get_ifno(dip),
usba_device->usb_client_flags[usba_get_ifno(dip)]);
if (matches == 0) {
usba_device->
usb_client_flags[usba_get_ifno(dip)] &=
~USBA_CLIENT_FLAG_DEV_DATA;
}
mutex_exit(&usba_device->usb_mutex);
kmem_free(reg, sizeof (usb_client_dev_data_t));
}
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_free_dev_data done");
}
static int
usba_build_descr_tree(dev_info_t *dip, usba_device_t *usba_device,
usb_client_dev_data_t *usb_reg)
{
usba_reg_state_t state;
int cfg_len_so_far = 0;
uint8_t *last_byte;
uint_t this_cfg_ndx;
uint_t high_cfg_bound;
uint_t low_cfg_bound;
boolean_t process_this_if_tree = B_FALSE;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_build_descr_tree starting");
bzero(&state, sizeof (usba_reg_state_t));
state.dip = dip;
state.st_dev_parse_level = usb_reg->dev_parse_level;
if (usba_set_parse_values(dip, usba_device, &state) != USB_SUCCESS) {
return (USB_INVALID_ARGS);
}
usb_reg->dev_parse_level = state.st_dev_parse_level;
if (usb_reg->dev_parse_level == USB_PARSE_LVL_ALL) {
usb_reg->dev_n_cfg = usba_device->usb_n_cfgs;
low_cfg_bound = 0;
high_cfg_bound = usba_device->usb_n_cfgs;
} else {
usb_reg->dev_n_cfg = 1;
mutex_enter(&usba_device->usb_mutex);
low_cfg_bound = usba_device->usb_active_cfg_ndx;
high_cfg_bound = usba_device->usb_active_cfg_ndx + 1;
mutex_exit(&usba_device->usb_mutex);
}
usb_reg->dev_cfg = state.st_dev_cfg = kmem_zalloc(
(usb_reg->dev_n_cfg * sizeof (usb_cfg_data_t)),
KM_SLEEP);
state.st_dev_n_cfg = 0;
for (this_cfg_ndx = low_cfg_bound; this_cfg_ndx < high_cfg_bound;
this_cfg_ndx++) {
state.st_curr_raw_descr =
usba_device->usb_cfg_array[this_cfg_ndx];
ASSERT(state.st_curr_raw_descr != NULL);
last_byte = NULL;
state.st_curr_cfg = NULL;
state.st_curr_if = NULL;
state.st_curr_alt = NULL;
state.st_curr_ep = NULL;
do {
state.st_curr_raw_descr_len =
state.st_curr_raw_descr[0];
state.st_curr_raw_descr_type =
state.st_curr_raw_descr[1];
if ((last_byte == NULL) &&
(state.st_curr_raw_descr_type !=
USB_DESCR_TYPE_CFG)) {
return (USB_FAILURE);
}
if (cfg_len_so_far > state.st_total_cfg_length) {
USB_DPRINTF_L2(DPRINT_MASK_ALL,
usbai_reg_log_handle,
"usba_build_descr_tree: Configuration (%d) "
"larger than wTotalLength (%d).",
cfg_len_so_far, state.st_total_cfg_length);
return (USB_FAILURE);
}
USB_DPRINTF_L3(DPRINT_MASK_REGISTER,
usbai_reg_log_handle,
"usba_build_descr_tree: Process type %d descr "
"(addr=0x%p)", state.st_curr_raw_descr_type,
(void *)state.st_curr_raw_descr);
switch (state.st_curr_raw_descr_type) {
case USB_DESCR_TYPE_CFG:
cfg_len_so_far = 0;
process_this_if_tree = B_FALSE;
state.st_curr_cfg_str = usba_device->
usb_cfg_str_descr[this_cfg_ndx];
usba_process_cfg_descr(&state);
state.st_last_processed_descr_type =
USB_DESCR_TYPE_CFG;
last_byte = state.st_curr_raw_descr +
(state.st_total_cfg_length *
sizeof (uchar_t));
break;
case USB_DESCR_TYPE_IF:
if (usba_process_if_descr(&state,
&process_this_if_tree) != USB_SUCCESS) {
return (USB_FAILURE);
}
state.st_last_processed_descr_type =
USB_DESCR_TYPE_IF;
break;
case USB_DESCR_TYPE_EP:
if (process_this_if_tree) {
if (usba_process_ep_descr(&state) !=
USB_SUCCESS) {
return (USB_FAILURE);
}
state.st_last_processed_descr_type =
USB_DESCR_TYPE_EP;
}
break;
case USB_DESCR_TYPE_SS_EP_COMP:
if (state.st_last_processed_descr_type ==
USB_DESCR_TYPE_EP) {
if (usba_process_ss_ep_comp_descr(
&state) != USB_SUCCESS) {
return (USB_FAILURE);
}
state.st_last_processed_descr_type =
USB_DESCR_TYPE_SS_EP_COMP;
break;
}
break;
case USB_DESCR_TYPE_STRING:
USB_DPRINTF_L2(DPRINT_MASK_ALL,
usbai_reg_log_handle,
"usb_get_dev_data: "
"Found unexpected str descr at addr 0x%p",
(void *)state.st_curr_raw_descr);
break;
default:
if ((state.st_last_processed_descr_type ==
USB_DESCR_TYPE_CFG) ||
(process_this_if_tree == B_TRUE)) {
if (usba_process_cv_descr(&state) !=
USB_SUCCESS) {
return (USB_FAILURE);
}
}
}
state.st_curr_raw_descr += state.st_curr_raw_descr_len;
cfg_len_so_far += state.st_curr_raw_descr_len;
} while (state.st_curr_raw_descr < last_byte);
}
usba_order_tree(&state);
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_build_descr_tree done");
return (USB_SUCCESS);
}
static void
usba_process_cfg_descr(usba_reg_state_t *state)
{
usb_cfg_data_t *curr_cfg;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_cfg_descr starting");
curr_cfg = state->st_curr_cfg =
&state->st_dev_cfg[state->st_dev_n_cfg++];
(void) usb_parse_data("2cs5c",
state->st_curr_raw_descr, state->st_curr_raw_descr_len,
&curr_cfg->cfg_descr,
sizeof (usb_cfg_descr_t));
state->st_total_cfg_length = curr_cfg->cfg_descr.wTotalLength;
if (state->st_curr_cfg_str != NULL) {
curr_cfg->cfg_strsize = strlen(state->st_curr_cfg_str) + 1;
curr_cfg->cfg_str = kmem_zalloc(curr_cfg->cfg_strsize,
KM_SLEEP);
(void) strcpy(curr_cfg->cfg_str, state->st_curr_cfg_str);
}
curr_cfg->cfg_n_if = curr_cfg->cfg_descr.bNumInterfaces;
curr_cfg->cfg_if = kmem_zalloc((curr_cfg->cfg_n_if *
sizeof (usb_if_data_t)), KM_SLEEP);
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_cfg_descr done");
}
static int
usba_process_if_descr(usba_reg_state_t *state, boolean_t *requested_if)
{
char *string;
usb_if_descr_t *new_if_descr;
usba_device_t *usba_device = usba_get_usba_device(state->dip);
int is_root_hub = (usba_device->usb_addr == ROOT_HUB_ADDR);
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_if_descr starting");
if (state->st_curr_cfg == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_if_descr found interface after no config.");
return (USB_FAILURE);
}
new_if_descr = kmem_zalloc(sizeof (usb_if_descr_t), KM_SLEEP);
(void) usb_parse_data("9c", state->st_curr_raw_descr,
state->st_curr_raw_descr_len,
new_if_descr, sizeof (usb_if_descr_t));
if (new_if_descr->bInterfaceNumber >= state->st_curr_cfg->cfg_n_if) {
USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_if_descr: bInterfaceNumber=%d is not "
"a valid one", new_if_descr->bInterfaceNumber);
kmem_free(new_if_descr, sizeof (usb_if_descr_t));
*requested_if = B_FALSE;
return (USB_SUCCESS);
}
*requested_if = B_TRUE;
if ((state->st_if_to_build != new_if_descr->bInterfaceNumber) &&
(state->st_if_to_build != USBA_ALL)) {
*requested_if = B_FALSE;
} else {
usb_alt_if_data_t *alt_array;
uint_t alt_index;
state->st_curr_if =
&state->st_curr_cfg->cfg_if[new_if_descr->bInterfaceNumber];
alt_index = state->st_curr_if->if_n_alt;
alt_array = state->st_curr_if->if_alt;
usba_augment_array((void **)(&alt_array), alt_index,
sizeof (usb_alt_if_data_t));
state->st_curr_alt = &alt_array[alt_index];
bcopy(new_if_descr, &(alt_array[alt_index++].altif_descr),
sizeof (usb_if_descr_t));
state->st_curr_if->if_alt = alt_array;
state->st_curr_if->if_n_alt = alt_index;
string = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
if (!is_root_hub) {
(void) usb_get_string_descr(state->dip, USB_LANG_ID,
state->st_curr_alt->altif_descr.iInterface,
string, USB_MAXSTRINGLEN);
}
if (string[0] == '\0') {
(void) strcpy(string, "<none>");
}
state->st_curr_alt->altif_strsize = strlen(string) + 1;
state->st_curr_alt->altif_str = kmem_zalloc(
state->st_curr_alt->altif_strsize, KM_SLEEP);
(void) strcpy(state->st_curr_alt->altif_str, string);
kmem_free(string, USB_MAXSTRINGLEN);
}
kmem_free(new_if_descr, sizeof (usb_if_descr_t));
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_if_descr done");
return (USB_SUCCESS);
}
static int
usba_process_ep_descr(usba_reg_state_t *state)
{
usb_alt_if_data_t *curr_alt = state->st_curr_alt;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_ep_descr starting");
if (state->st_curr_alt == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_ep_descr: no requested alt before endpt.");
return (USB_FAILURE);
}
usba_augment_array((void **)(&curr_alt->altif_ep),
curr_alt->altif_n_ep, sizeof (usb_ep_data_t));
state->st_curr_ep = &curr_alt->altif_ep[curr_alt->altif_n_ep++];
(void) usb_parse_data("4csc", state->st_curr_raw_descr,
state->st_curr_raw_descr_len,
&state->st_curr_ep->ep_descr, sizeof (usb_ep_descr_t));
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_ep_descr done");
return (USB_SUCCESS);
}
static int
usba_process_ss_ep_comp_descr(usba_reg_state_t *state)
{
if (state->st_curr_ep == NULL)
return (USB_FAILURE);
(void) usb_parse_data("4cs", state->st_curr_raw_descr,
state->st_curr_raw_descr_len,
&state->st_curr_ep->ep_ss_comp,
sizeof (usb_ep_ss_comp_descr_t));
state->st_curr_ep->ep_ss_valid = B_TRUE;
return (USB_SUCCESS);
}
static int
usba_process_cv_descr(usba_reg_state_t *state)
{
usb_cvs_data_t *curr_cv_descr;
usb_cvs_data_t **cvs_ptr = NULL;
uint_t *n_cvs_ptr;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_cv_descr starting. Processing c/v for descr type %d",
state->st_last_processed_descr_type);
switch (state->st_last_processed_descr_type) {
case USB_DESCR_TYPE_CFG:
n_cvs_ptr = &state->st_curr_cfg->cfg_n_cvs;
cvs_ptr = &state->st_curr_cfg->cfg_cvs;
break;
case USB_DESCR_TYPE_IF:
n_cvs_ptr = &state->st_curr_alt->altif_n_cvs;
cvs_ptr = &state->st_curr_alt->altif_cvs;
break;
case USB_DESCR_TYPE_EP:
case USB_DESCR_TYPE_SS_EP_COMP:
n_cvs_ptr = &state->st_curr_ep->ep_n_cvs;
cvs_ptr = &state->st_curr_ep->ep_cvs;
break;
default:
USB_DPRINTF_L2(DPRINT_MASK_ALL, usbai_reg_log_handle,
"usba_process_cv_descr: Type of last descriptor unknown. ");
return (USB_FAILURE);
}
usba_augment_array((void **)cvs_ptr, *n_cvs_ptr,
sizeof (usb_cvs_data_t));
curr_cv_descr = &(*cvs_ptr)[(*n_cvs_ptr)++];
curr_cv_descr->cvs_buf =
kmem_zalloc(state->st_curr_raw_descr_len, KM_SLEEP);
curr_cv_descr->cvs_buf_len = state->st_curr_raw_descr_len;
bcopy(state->st_curr_raw_descr, curr_cv_descr->cvs_buf,
state->st_curr_raw_descr_len);
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_process_cv_descr done");
return (USB_SUCCESS);
}
static int
usba_set_parse_values(dev_info_t *dip, usba_device_t *usba_device,
usba_reg_state_t *state)
{
mutex_enter(&usba_device->usb_mutex);
state->st_cfg_to_build = usba_device->usb_active_cfg_ndx;
mutex_exit(&usba_device->usb_mutex);
if (state->st_cfg_to_build == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
state->st_cfg_to_build = USBA_ALL;
}
state->st_if_to_build = usb_get_if_number(dip);
switch (state->st_dev_parse_level) {
case USB_PARSE_LVL_ALL:
state->st_cfg_to_build = USBA_ALL;
state->st_if_to_build = USBA_ALL;
break;
case USB_PARSE_LVL_CFG:
state->st_if_to_build = USBA_ALL;
break;
case USB_PARSE_LVL_IF:
if (state->st_if_to_build < 0) {
state->st_if_to_build = USBA_ALL;
}
break;
default:
return (USB_INVALID_ARGS);
}
if ((state->st_if_to_build == USBA_ALL) &&
(state->st_dev_parse_level == USB_PARSE_LVL_IF)) {
state->st_dev_parse_level = USB_PARSE_LVL_CFG;
}
if ((state->st_cfg_to_build == USBA_ALL) &&
(state->st_dev_parse_level == USB_PARSE_LVL_CFG)) {
state->st_dev_parse_level = USB_PARSE_LVL_ALL;
}
return (USB_SUCCESS);
}
static void*
usba_kmem_realloc(void* old_mem, int old_size, int new_size)
{
void *new_mem = NULL;
if (new_size > 0) {
new_mem = kmem_zalloc(new_size, KM_SLEEP);
if (old_size > 0) {
bcopy(old_mem, new_mem,
min(old_size, new_size));
}
}
if (old_size > 0) {
kmem_free(old_mem, old_size);
}
return (new_mem);
}
static void
usba_augment_array(void **addr, uint_t n_elements, uint_t element_size)
{
*addr = usba_kmem_realloc(*addr, (n_elements * element_size),
((n_elements + 1) * element_size));
}
static void
usba_make_alts_sparse(usb_alt_if_data_t **array, uint_t *n_elements)
{
uint_t n_orig_elements = *n_elements;
uint8_t smallest_value;
uint8_t largest_value;
uint8_t curr_value;
uint_t in_order = 0;
usb_alt_if_data_t *orig_addr;
usb_alt_if_data_t *repl_array;
uint_t n_repl_elements;
uint_t i;
if ((array == NULL) || (n_orig_elements == 0)) {
return;
}
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"make_sparse: array=0x%p, n_orig_elements=%d",
(void *)array, n_orig_elements);
orig_addr = *array;
curr_value = orig_addr[0].altif_descr.bAlternateSetting;
smallest_value = largest_value = curr_value;
for (i = 1; i < n_orig_elements; i++) {
curr_value = orig_addr[i].altif_descr.bAlternateSetting;
if (curr_value < smallest_value) {
smallest_value = curr_value;
} else if (curr_value > largest_value) {
in_order++;
largest_value = curr_value;
}
}
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"make_sparse: largest=%d, smallest=%d, "
"order=%d",
largest_value, smallest_value, in_order);
n_repl_elements = largest_value + 1;
if ((n_repl_elements == n_orig_elements) &&
((in_order + 1) == n_orig_elements)) {
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"No holes");
return;
}
repl_array = kmem_zalloc(
(n_repl_elements * sizeof (usb_alt_if_data_t)), KM_SLEEP);
for (i = 0; i < n_orig_elements; i++) {
curr_value = orig_addr[i].altif_descr.bAlternateSetting;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"move %lu bytes (key %d) from 0x%p to 0x%p",
(unsigned long)sizeof (usb_alt_if_data_t), curr_value,
(void *)&orig_addr[i], (void *)&repl_array[curr_value]);
bcopy((char *)&orig_addr[i], (char *)&repl_array[curr_value],
sizeof (usb_alt_if_data_t));
}
kmem_free(*array, sizeof (usb_alt_if_data_t) * n_orig_elements);
*array = repl_array;
*n_elements = n_repl_elements;
}
static void
usba_order_tree(usba_reg_state_t *state)
{
usb_cfg_data_t *this_cfg;
usb_if_data_t *this_if;
uint_t n_cfgs = state->st_dev_n_cfg;
uint_t cfg;
uint_t which_if;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usba_order_tree:");
for (cfg = 0; cfg < n_cfgs; cfg++) {
this_cfg = &state->st_dev_cfg[cfg];
for (which_if = 0; which_if < this_cfg->cfg_n_if; which_if++) {
this_if = this_cfg->cfg_if;
usba_make_alts_sparse(&this_if->if_alt,
&this_if->if_n_alt);
}
}
}
void
usb_free_descr_tree(dev_info_t *dip, usb_client_dev_data_t *dev_data)
{
usb_cfg_data_t *cfg_array;
int n_cfgs;
int cfg;
if ((dip == NULL) || (dev_data == NULL)) {
return;
}
cfg_array = dev_data->dev_cfg;
n_cfgs = dev_data->dev_n_cfg;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_free_descr_tree starting for %s%d",
ddi_driver_name(dip), ddi_get_instance(dip));
for (cfg = 0; cfg < n_cfgs; cfg++) {
if (cfg_array[cfg].cfg_if) {
usba_free_if_array(cfg_array[cfg].cfg_if,
cfg_array[cfg].cfg_n_if);
}
if (cfg_array[cfg].cfg_cvs) {
usba_free_cv_array(cfg_array[cfg].cfg_cvs,
cfg_array[cfg].cfg_n_cvs);
}
if (cfg_array[cfg].cfg_str) {
kmem_free(cfg_array[cfg].cfg_str,
cfg_array[cfg].cfg_strsize);
}
}
if (cfg_array) {
kmem_free(cfg_array, (sizeof (usb_cfg_data_t) * n_cfgs));
}
dev_data->dev_parse_level = USB_PARSE_LVL_NONE;
dev_data->dev_n_cfg = 0;
dev_data->dev_cfg = NULL;
dev_data->dev_curr_cfg = NULL;
USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
"usb_free_descr_tree done");
}
static void
usba_free_if_array(usb_if_data_t *if_array, uint_t n_ifs)
{
uint_t which_if;
uint_t which_alt;
uint_t n_alts;
usb_alt_if_data_t *altif;
for (which_if = 0; which_if < n_ifs; which_if++) {
n_alts = if_array[which_if].if_n_alt;
for (which_alt = 0; which_alt < n_alts; which_alt++) {
altif = &if_array[which_if].if_alt[which_alt];
usba_free_ep_array(altif->altif_ep, altif->altif_n_ep);
usba_free_cv_array(altif->altif_cvs,
altif->altif_n_cvs);
kmem_free(altif->altif_str, altif->altif_strsize);
}
kmem_free(if_array[which_if].if_alt,
(sizeof (usb_alt_if_data_t) * n_alts));
}
kmem_free(if_array, (sizeof (usb_if_data_t) * n_ifs));
}
static void
usba_free_ep_array(usb_ep_data_t *ep_array, uint_t n_eps)
{
uint_t ep;
for (ep = 0; ep < n_eps; ep++) {
usba_free_cv_array(ep_array[ep].ep_cvs, ep_array[ep].ep_n_cvs);
}
kmem_free(ep_array, (sizeof (usb_ep_data_t) * n_eps));
}
static void
usba_free_cv_array(usb_cvs_data_t *cv_array, uint_t n_cvs)
{
uint_t cv_node;
for (cv_node = 0; cv_node < n_cvs; cv_node++) {
kmem_free(cv_array[cv_node].cvs_buf,
cv_array[cv_node].cvs_buf_len);
}
kmem_free(cv_array, (sizeof (usb_cvs_data_t) * n_cvs));
}
int
usb_log_descr_tree(usb_client_dev_data_t *dev_data,
usb_log_handle_t log_handle, uint_t level, uint_t mask)
{
return (usba_dump_descr_tree(NULL, dev_data, log_handle, level, mask));
}
int
usb_print_descr_tree(dev_info_t *dip, usb_client_dev_data_t *dev_data)
{
return (usba_dump_descr_tree(dip, dev_data, NULL, 0, 0));
}
static int
usba_dump_descr_tree(dev_info_t *dip, usb_client_dev_data_t *usb_reg,
usb_log_handle_t log_handle, uint_t level, uint_t mask)
{
usb_log_handle_t dump_handle;
uint_t dump_level;
uint_t dump_mask;
int which_config;
int which_if;
int which_cv;
usb_cfg_data_t *config;
usb_cfg_descr_t *config_descr;
char *string;
char *name_string = NULL;
int name_string_size = 0;
if ((usb_reg == NULL) || ((log_handle == NULL) && (dip == NULL))) {
return (USB_INVALID_ARGS);
}
if (servicing_interrupt()) {
return (USB_INVALID_CONTEXT);
}
string = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
if (log_handle != NULL) {
dump_level = level;
dump_mask = mask;
dump_handle = log_handle;
} else {
dump_level = USB_LOG_L1;
dump_mask = DPRINT_MASK_ALL;
(void) snprintf(string, USB_MAXSTRINGLEN,
"Port%d", usb_get_addr(dip));
name_string_size = strlen(string) + 1;
name_string = kmem_zalloc(name_string_size, KM_SLEEP);
(void) strcpy(name_string, string);
dump_handle = usb_alloc_log_hdl(NULL, name_string,
&dump_level, &dump_mask, NULL,
USB_FLAGS_SLEEP);
}
(void) usb_log(dump_handle, dump_level, dump_mask,
"USB descriptor tree for %s %s",
(usb_reg->dev_mfg != NULL ? usb_reg->dev_mfg : ""),
(usb_reg->dev_product != NULL ? usb_reg->dev_product : ""));
if (usb_reg->dev_n_cfg == 0) {
(void) usb_log(dump_handle, dump_level, dump_mask,
"No descriptor tree present");
} else {
(void) usb_log(dump_handle, dump_level, dump_mask,
"highest configuration found=%d", usb_reg->dev_n_cfg - 1);
}
for (which_config = 0; which_config < usb_reg->dev_n_cfg;
which_config++) {
config = &usb_reg->dev_cfg[which_config];
config_descr = &config->cfg_descr;
if (config_descr->bLength == 0) {
continue;
}
if (dump_level == USB_LOG_L0) {
(void) usb_log(dump_handle, dump_level, dump_mask, " ");
}
(void) usb_log(dump_handle, dump_level, dump_mask,
"Configuration #%d (Addr= 0x%p)", which_config,
(void *)config);
(void) usb_log(dump_handle, dump_level, dump_mask,
"String descr=%s", config->cfg_str);
(void) usb_log(dump_handle, dump_level, dump_mask,
"config descr: len=%d tp=%d totLen=%d numIf=%d "
"cfgVal=%d att=0x%x pwr=%d",
config_descr->bLength, config_descr->bDescriptorType,
config_descr->wTotalLength, config_descr->bNumInterfaces,
config_descr->bConfigurationValue,
config_descr->bmAttributes, config_descr->bMaxPower);
if ((config->cfg_n_if > 0) || (config->cfg_n_cvs > 0)) {
(void) usb_log(dump_handle, dump_level, dump_mask,
"usb_cfg_data_t shows max if=%d "
"and %d cv descr(s).",
config->cfg_n_if - 1, config->cfg_n_cvs);
}
for (which_if = 0; which_if < config->cfg_n_if;
which_if++) {
if (dump_level == USB_LOG_L0) {
(void) usb_log(dump_handle, dump_level,
dump_mask, " ");
}
(void) usb_log(dump_handle, dump_level, dump_mask,
" interface #%d (0x%p)",
which_if, (void *)&config->cfg_if[which_if]);
usba_dump_if(&config->cfg_if[which_if],
dump_handle, dump_level, dump_mask, string);
}
for (which_cv = 0; which_cv < config->cfg_n_cvs; which_cv++) {
(void) usb_log(dump_handle, dump_level, dump_mask,
" config cv descriptor %d (Address=0x%p)",
which_cv, (void *)&config->cfg_cvs[which_cv]);
usba_dump_cv(&config->cfg_cvs[which_cv],
dump_handle, dump_level, dump_mask, string, 4);
}
}
(void) usb_log(dump_handle, dump_level, dump_mask,
"Returning dev_curr_cfg:0x%p, dev_curr_if:%d",
(void *)usb_reg->dev_curr_cfg, usb_reg->dev_curr_if);
if (log_handle == NULL) {
usb_free_log_hdl(dump_handle);
}
if (name_string != NULL) {
kmem_free(name_string, name_string_size);
}
kmem_free(string, USB_MAXSTRINGLEN);
return (USB_SUCCESS);
}
static void
usba_dump_if(usb_if_data_t *which_if, usb_log_handle_t dump_handle,
uint_t dump_level, uint_t dump_mask, char *string)
{
int which_alt;
usb_alt_if_data_t *alt;
usb_if_descr_t *if_descr;
int which_ep;
int which_cv;
for (which_alt = 0; which_alt < which_if->if_n_alt; which_alt++) {
alt = &which_if->if_alt[which_alt];
if_descr = &alt->altif_descr;
if (if_descr->bLength == 0) {
continue;
}
if (dump_level == USB_LOG_L0) {
(void) usb_log(dump_handle, dump_level, dump_mask, " ");
}
(void) usb_log(dump_handle, dump_level, dump_mask,
"\tAlt #%d (0x%p)", which_alt, (void *)alt);
(void) usb_log(dump_handle, dump_level, dump_mask,
"\tString descr=%s", alt->altif_str);
(void) usb_log(dump_handle, dump_level, dump_mask,
"\tif descr: len=%d type=%d if=%d alt=%d n_ept=%d "
"cls=%d sub=%d proto=%d",
if_descr->bLength,
if_descr->bDescriptorType, if_descr->bInterfaceNumber,
if_descr->bAlternateSetting, if_descr->bNumEndpoints,
if_descr->bInterfaceClass, if_descr->bInterfaceSubClass,
if_descr->bInterfaceProtocol);
if ((alt->altif_n_ep > 0) || (alt->altif_n_cvs > 0)) {
(void) usb_log(dump_handle, dump_level, dump_mask,
"\tusb_alt_if_data_t shows max ep=%d "
"and %d cv descr(s).",
alt->altif_n_ep - 1, alt->altif_n_cvs);
}
for (which_ep = 0; which_ep < alt->altif_n_ep;
which_ep++) {
if (alt->altif_ep[which_ep].ep_descr.bLength == 0) {
continue;
}
if (dump_level == USB_LOG_L0) {
(void) usb_log(dump_handle, dump_level,
dump_mask, " ");
}
usba_dump_ep(which_ep, &alt->altif_ep[which_ep],
dump_handle, dump_level, dump_mask, string);
}
for (which_cv = 0; which_cv < alt->altif_n_cvs; which_cv++) {
if (dump_level == USB_LOG_L0) {
(void) usb_log(dump_handle, dump_level,
dump_mask, " ");
}
(void) usb_log(dump_handle, dump_level, dump_mask,
"\talt cv descriptor #%d (0x%p), size=%d",
which_cv, (void *)&alt->altif_cvs[which_cv],
alt->altif_cvs[which_cv].cvs_buf_len);
usba_dump_cv(&alt->altif_cvs[which_cv],
dump_handle, dump_level, dump_mask, string, 2);
}
}
}
static void
usba_dump_ep(uint_t which_ep, usb_ep_data_t *ep, usb_log_handle_t dump_handle,
uint_t dump_level, uint_t dump_mask, char *string)
{
int which_cv;
usb_ep_descr_t *ep_descr = &ep->ep_descr;
(void) usb_log(dump_handle, dump_level, dump_mask,
"\t endpoint[%d], epaddr=0x%x (0x%p)", which_ep,
ep_descr->bEndpointAddress, (void *)ep);
(void) usb_log(dump_handle, dump_level, dump_mask,
"\t len=%d type=%d attr=0x%x pktsize=%d interval=%d",
ep_descr->bLength, ep_descr->bDescriptorType,
ep_descr->bmAttributes, ep_descr->wMaxPacketSize,
ep_descr->bInterval);
if (ep->ep_n_cvs > 0) {
(void) usb_log(dump_handle, dump_level, dump_mask,
"\t usb_ep_data_t shows %d cv descr(s)", ep->ep_n_cvs);
}
for (which_cv = 0; which_cv < ep->ep_n_cvs; which_cv++) {
if (dump_level == USB_LOG_L0) {
(void) usb_log(dump_handle, dump_level,
dump_mask, " ");
}
(void) usb_log(dump_handle, dump_level, dump_mask,
"\t endpoint cv descriptor %d (0x%p), size=%d",
which_cv, (void *)&ep->ep_cvs[which_cv],
ep->ep_cvs[which_cv].cvs_buf_len);
usba_dump_cv(&ep->ep_cvs[which_cv],
dump_handle, dump_level, dump_mask, string, 3);
}
}
static void
usba_dump_cv(usb_cvs_data_t *cv_node, usb_log_handle_t dump_handle,
uint_t dump_level, uint_t dump_mask, char *string, int indent)
{
if (cv_node) {
usba_dump_bin(cv_node->cvs_buf, cv_node->cvs_buf_len, indent,
dump_handle, dump_level, dump_mask, string,
USB_MAXSTRINGLEN);
}
}
static void
usba_dump_bin(uint8_t *data, int max_bytes, int indent,
usb_log_handle_t dump_handle, uint_t dump_level, uint_t dump_mask,
char *buffer, int bufferlen)
{
int i;
int bufoffset = 0;
int nexthere;
if ((indent * SPACES_PER_INDENT) >
(bufferlen - (BINDUMP_BYTES_PER_LINE * 3))) {
(void) usb_log(dump_handle, dump_level, dump_mask,
"Offset to usb_dump_bin must be %d or less. "
"Setting to 0.\n",
(bufferlen - (BINDUMP_BYTES_PER_LINE * 3)));
indent = 0;
}
for (i = 0; i < indent/2; i++) {
buffer[bufoffset] = '\t';
bufoffset++;
}
if (indent % 2) {
(void) strcpy(&buffer[bufoffset], INDENT_SPACE_STR);
bufoffset += SPACES_PER_INDENT;
}
i = 0;
nexthere = bufoffset;
while (i < max_bytes) {
(void) sprintf(&buffer[nexthere], "%2x ", *data++);
nexthere += 3;
i++;
if (!(i % BINDUMP_BYTES_PER_LINE)) {
buffer[nexthere] = '\0';
(void) usb_log(dump_handle, dump_level, dump_mask,
buffer);
nexthere = bufoffset;
}
}
if (nexthere > bufoffset) {
buffer[nexthere] = '\0';
(void) usb_log(dump_handle, dump_level, dump_mask, buffer);
}
}
int
usb_ep_xdescr_fill(uint_t version, dev_info_t *dip, usb_ep_data_t *ep_data,
usb_ep_xdescr_t *ep_xdescr)
{
if (version != USB_EP_XDESCR_VERSION_ONE) {
return (USB_INVALID_ARGS);
}
if (dip == NULL || ep_data == NULL || ep_xdescr == NULL) {
return (USB_INVALID_ARGS);
}
bzero(ep_xdescr, sizeof (usb_ep_xdescr_t));
ep_xdescr->uex_version = version;
ep_xdescr->uex_ep = ep_data->ep_descr;
if (ep_data->ep_ss_valid == B_TRUE) {
ep_xdescr->uex_flags |= USB_EP_XFLAGS_SS_COMP;
ep_xdescr->uex_ep_ss = ep_data->ep_ss_comp;
}
return (USB_SUCCESS);
}