#include <stddef.h>
#include <sys/mdb_modapi.h>
#include <mdb/mdb_ks.h>
#include <sys/usb/usba.h>
#include <sys/ddi_impldefs.h>
#include <sys/usb/usba/usba_types.h>
#include <sys/usb/usba/usba_impl.h>
#include <sys/usb/usba/hcdi_impl.h>
#include <sys/usb/hubd/hub.h>
#include <sys/usb/hubd/hubdvar.h>
#include <sys/file.h>
#include <sys/sunndi.h>
#include <unistd.h>
extern uintptr_t mdb_usba_get_usba_device(uintptr_t);
extern uintptr_t mdb_usba_hcdi_get_hcdi(struct dev_info *);
#define USB_DUMP_VERBOSE 0x01
#define USB_DUMP_ACTIVE_PIPES 0x02
#define USB_DEBUG_SIZE_EXTRA_ALLOC 8
typedef struct usba_device2devinfo_data {
uintptr_t u2d_target_usb_dev_p;
uintptr_t *u2d_dip_addr;
boolean_t u2d_found;
} usba_device2devinfo_cbdata_t;
static int
find_dip(uintptr_t dip_addr, const void *local_dip, void *cb_arg)
{
uintptr_t cur_usb_dev;
usba_device2devinfo_cbdata_t *cb_data =
(usba_device2devinfo_cbdata_t *)cb_arg;
if ((cur_usb_dev = mdb_usba_get_usba_device(dip_addr)) == 0) {
return (WALK_NEXT);
}
if (cur_usb_dev == cb_data->u2d_target_usb_dev_p) {
*cb_data->u2d_dip_addr = dip_addr;
cb_data->u2d_found = TRUE;
return (WALK_DONE);
}
return (WALK_NEXT);
}
static int
usba_device2dip(uintptr_t usb_dev_addr, uintptr_t *dip_addr)
{
usba_device_t usb_dev;
usba_device2devinfo_cbdata_t cb_data;
cb_data.u2d_target_usb_dev_p = usb_dev_addr;
cb_data.u2d_dip_addr = dip_addr;
cb_data.u2d_found = FALSE;
if (mdb_vread(&usb_dev, sizeof (usba_device_t),
usb_dev_addr) == -1) {
mdb_warn("failed to read usba_device struct");
return (-1);
}
if (mdb_pwalk("devinfo_children", find_dip, &cb_data,
(uintptr_t)usb_dev.usb_root_hub_dip) != 0) {
mdb_warn("failed to walk devinfo_children");
return (-1);
}
if (cb_data.u2d_found == TRUE) {
return (1);
}
return (0);
}
int
usba_list_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr == 0) {
mdb_warn("not a global walk. Starting address required\n");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
usba_list_walk_step(mdb_walk_state_t *wsp)
{
int status;
usba_list_entry_t list_entry;
if (mdb_vread(&list_entry, sizeof (usba_list_entry_t),
(uintptr_t)wsp->walk_addr) == -1) {
mdb_warn("failed to read usba_list_entry_t at %p",
wsp->walk_addr);
return (WALK_ERR);
}
status = wsp->walk_callback(wsp->walk_addr, &list_entry,
wsp->walk_cbdata);
wsp->walk_addr = (uintptr_t)list_entry.next;
if (wsp->walk_addr == 0) {
return (WALK_DONE);
}
return (status);
}
int
usb_pipe_handle_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr == 0) {
mdb_warn("not a global walk; usba_device_t required\n");
return (WALK_ERR);
}
wsp->walk_data = mdb_alloc((sizeof (usba_ph_impl_t)) * USBA_N_ENDPOINTS,
UM_SLEEP | UM_GC);
if (mdb_vread(wsp->walk_data,
(sizeof (usba_ph_impl_t)) * USBA_N_ENDPOINTS,
(uintptr_t)((size_t)(wsp->walk_addr) +
offsetof(usba_device_t, usb_ph_list))) == -1) {
mdb_warn("failed to read usb_pipehandle_list at %p",
wsp->walk_addr);
return (WALK_ERR);
}
wsp->walk_arg = 0;
return (WALK_NEXT);
}
int
usb_pipe_handle_walk_step(mdb_walk_state_t *wsp)
{
int status;
usba_ph_impl_t *impl_list = (usba_ph_impl_t *)(wsp->walk_data);
intptr_t index = (intptr_t)wsp->walk_arg;
while ((index < USBA_N_ENDPOINTS) &&
(impl_list[index].usba_ph_data == NULL)) {
index++;
}
if (index >= USBA_N_ENDPOINTS) {
return (WALK_DONE);
}
status = wsp->walk_callback((uintptr_t)impl_list[index].usba_ph_data,
wsp->walk_data, wsp->walk_cbdata);
wsp->walk_arg = (void *)(index + 1);
return (status);
}
int
usb_pipe_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
char *dir, *type, *state;
usb_ep_descr_t ept_descr;
usba_pipe_handle_data_t pipe_handle;
usba_ph_impl_t ph_impl;
if (!(flags & DCMD_ADDRSPEC)) {
return (DCMD_USAGE);
}
if (mdb_vread(&pipe_handle,
sizeof (usba_pipe_handle_data_t), addr) == -1) {
mdb_warn("failed to read pipe handle at %p", addr);
return (DCMD_ERR);
}
if (mdb_vread(&ph_impl, sizeof (usba_ph_impl_t),
(uintptr_t)pipe_handle.p_ph_impl) == -1) {
state = "*******";
} else {
switch (ph_impl.usba_ph_state) {
case USB_PIPE_STATE_CLOSED:
state = "CLOSED ";
break;
case USB_PIPE_STATE_IDLE:
state = "IDLE ";
break;
case USB_PIPE_STATE_ACTIVE:
state = "ACTIVE ";
break;
case USB_PIPE_STATE_ERROR:
state = "ERROR ";
break;
case USB_PIPE_STATE_CLOSING:
state = "CLOSING";
break;
default:
state = "ILLEGAL";
break;
}
}
bcopy(&pipe_handle.p_ep, &ept_descr, sizeof (usb_ep_descr_t));
if (DCMD_HDRSPEC(flags)) {
mdb_printf("\n %<u>%-3s %5s %3s %7s %-?s %-?s %-?s%</u>\n",
"EP", "TYPE ", "DIR", "STATE ", "P_HANDLE", "P_POLICY",
"EP DESCR");
}
dir = ((ept_descr.bEndpointAddress & USB_EP_DIR_MASK) &
USB_EP_DIR_IN) ? "In " : "Out";
switch (ept_descr.bmAttributes & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_CONTROL:
type = "Cntrl";
break;
case USB_EP_ATTR_ISOCH:
type = "Isoch";
break;
case USB_EP_ATTR_BULK:
type = "Bulk ";
break;
case USB_EP_ATTR_INTR:
type = "Intr ";
break;
default:
type = "*****";
break;
}
mdb_printf(" %3d %5s %3s %7s %-?p %-?p %-?p\n",
ept_descr.bEndpointAddress & USB_EP_NUM_MASK, type, dir, state,
addr, addr + offsetof(usba_pipe_handle_data_t, p_policy),
addr + offsetof(usba_pipe_handle_data_t, p_ep));
return (DCMD_OK);
}
int
usba_device_walk_init(mdb_walk_state_t *wsp)
{
usba_list_entry_t list_entry;
if (wsp->walk_addr != 0) {
mdb_warn(
"global walk only. Must be invoked without an address\n");
return (WALK_ERR);
}
if (mdb_readvar(&list_entry, "usba_device_list") == -1) {
mdb_warn("failed to read usba_device_list");
return (WALK_ERR);
}
wsp->walk_addr = (uintptr_t)list_entry.next;
return (WALK_NEXT);
}
int
usba_hubd_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr != 0) {
mdb_warn("hubd only supports global walks.\n");
return (WALK_ERR);
}
if (mdb_layered_walk("usba_device", wsp) == -1) {
mdb_warn("couldn't walk 'usba_device'");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
usba_hubd_walk_step(mdb_walk_state_t *wsp)
{
usba_device_t ud;
hubd_t hubd;
struct dev_info dev_info;
uintptr_t state_addr;
if (mdb_vread(&ud, sizeof (ud), wsp->walk_addr) != sizeof (ud)) {
mdb_warn("failed to read usba_device_t at %p", wsp->walk_addr);
return (WALK_ERR);
}
if (ud.usb_root_hubd != NULL) {
if (mdb_vread(&hubd, sizeof (hubd),
(uintptr_t)ud.usb_root_hubd) != sizeof (hubd)) {
mdb_warn("failed to read hubd at %p", ud.usb_root_hubd);
return (WALK_ERR);
}
return (wsp->walk_callback((uintptr_t)ud.usb_root_hubd, &hubd,
wsp->walk_cbdata));
}
if (ud.usb_hubdi == NULL)
return (WALK_NEXT);
if (mdb_vread(&dev_info, sizeof (struct dev_info),
(uintptr_t)ud.usb_dip) != sizeof (struct dev_info)) {
mdb_warn("failed to read dev_info_t for device %p at %p",
wsp->walk_addr, ud.usb_dip);
return (WALK_ERR);
}
if (mdb_get_soft_state_byname("hubd_statep", dev_info.devi_instance,
&state_addr, &hubd, sizeof (hubd)) == -1) {
mdb_warn("failed to read hubd soft state for instance %d from "
"usb device %p", dev_info.devi_instance, wsp->walk_addr);
return (WALK_ERR);
}
return (wsp->walk_callback(state_addr, &hubd, wsp->walk_cbdata));
}
int
usba_device(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int status;
char pathname[MAXNAMELEN];
char dname[MODMAXNAMELEN] = "<unatt>";
char drv_statep[MODMAXNAMELEN+ 10];
uint_t usb_flag = 0;
boolean_t no_driver_attached = FALSE;
uintptr_t dip_addr;
struct dev_info devinfo;
if (!(flags & DCMD_ADDRSPEC)) {
if (mdb_walk_dcmd("usba_device", "usba_device", argc,
argv) == -1) {
mdb_warn("failed to walk usba_device");
return (DCMD_ERR);
}
return (DCMD_OK);
}
if (mdb_getopts(argc, argv,
'p', MDB_OPT_SETBITS, USB_DUMP_ACTIVE_PIPES, &usb_flag,
'v', MDB_OPT_SETBITS, USB_DUMP_VERBOSE, &usb_flag, NULL) != argc) {
return (DCMD_USAGE);
}
if (usb_flag && !(DCMD_HDRSPEC(flags))) {
mdb_printf("\n");
}
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%-15s %4s %-?s %-42s%</u>\n",
"NAME", "INST", "DIP", "PATH ");
}
status = usba_device2dip(addr, &dip_addr);
if (status != 1) {
if (status == -1) {
mdb_warn("error looking for dip for usba_device %p",
addr);
} else {
mdb_warn("failed to find dip for usba_device %p\n",
addr);
}
mdb_warn("dip and statep unobtainable\n");
return (DCMD_ERR);
}
(void) mdb_devinfo2driver(dip_addr, (char *)dname, sizeof (dname));
if (mdb_vread(&devinfo, sizeof (struct dev_info),
dip_addr) == -1) {
mdb_warn("failed to read devinfo");
return (DCMD_ERR);
}
if (!(DDI_CF2(&devinfo))) {
no_driver_attached = TRUE;
}
(void) mdb_ddi_pathname(dip_addr, pathname, sizeof (pathname));
mdb_printf("%-15s %2d %-?p %s\n", dname, devinfo.devi_instance,
dip_addr, pathname);
if (usb_flag & USB_DUMP_VERBOSE) {
int i;
uintptr_t statep = 0;
char *string_descr;
char **config_cloud, **conf_str_descr;
usb_dev_descr_t usb_dev_descr;
usba_device_t usba_device_struct;
if (mdb_vread(&usba_device_struct,
sizeof (usba_device_t), addr) == -1) {
mdb_warn("failed to read usba_device struct");
return (DCMD_ERR);
}
mdb_printf(" usba_device: %-16p\n\n", (usba_device_t *)addr);
if (mdb_vread(&usb_dev_descr, sizeof (usb_dev_descr),
(uintptr_t)usba_device_struct.usb_dev_descr) == -1) {
mdb_warn("failed to read usb_dev_descr_t struct");
return (DCMD_ERR);
}
mdb_printf("\n idVendor: 0x%04x idProduct: 0x%04x "
"usb_addr: 0x%02x\n", usb_dev_descr.idVendor,
usb_dev_descr.idProduct, usba_device_struct.usb_addr);
string_descr = (char *)mdb_alloc(USB_MAXSTRINGLEN, UM_GC);
if (usba_device_struct.usb_mfg_str == NULL) {
(void) strcpy(string_descr, "<No Manufacturer String>");
} else {
if (mdb_readstr(string_descr, USB_MAXSTRINGLEN,
(uintptr_t)usba_device_struct.usb_mfg_str) == -1) {
mdb_warn("failed to read manufacturer "
"string descriptor");
(void) strcpy(string_descr, "???");
}
}
mdb_printf("\n Manufacturer String:\t%s\n", string_descr);
if (usba_device_struct.usb_product_str == NULL) {
(void) strcpy(string_descr, "<No Product String>");
} else {
if (mdb_readstr(string_descr, USB_MAXSTRINGLEN,
(uintptr_t)usba_device_struct.usb_product_str) ==
-1) {
mdb_warn("failed to read product string "
"descriptor");
(void) strcpy(string_descr, "???");
}
}
mdb_printf(" Product String:\t\t%s\n", string_descr);
if (usba_device_struct.usb_serialno_str == NULL) {
(void) strcpy(string_descr, "<No SerialNumber String>");
} else {
if (mdb_readstr(string_descr, USB_MAXSTRINGLEN,
(uintptr_t)usba_device_struct.usb_serialno_str) ==
-1) {
mdb_warn("failed to read serial number string "
"descriptor");
(void) strcpy(string_descr, "???");
}
}
mdb_printf(" SerialNumber String:\t%s\n", string_descr);
if (no_driver_attached) {
mdb_printf("\n");
} else {
mdb_printf(" state_p: ");
(void) mdb_snprintf(drv_statep, sizeof (drv_statep),
"%s_statep", dname);
if (mdb_devinfo2statep(dip_addr, drv_statep,
&statep) == -1) {
mdb_warn("failed to find %s state struct for "
"dip %p", drv_statep, dip_addr);
return (DCMD_ERR);
}
mdb_printf("%-?p\n", statep);
}
config_cloud = (char **)mdb_alloc(sizeof (void *) *
usba_device_struct.usb_n_cfgs, UM_GC);
conf_str_descr = (char **)mdb_alloc(sizeof (void *) *
usba_device_struct.usb_n_cfgs, UM_GC);
if ((usba_device_struct.usb_cfg_array) &&
(usba_device_struct.usb_cfg_str_descr)) {
if ((mdb_vread(config_cloud, sizeof (void *) *
usba_device_struct.usb_n_cfgs,
(uintptr_t)usba_device_struct.usb_cfg_array) ==
-1) || (mdb_vread(conf_str_descr, sizeof (void *)
* usba_device_struct.usb_n_cfgs, (uintptr_t)
usba_device_struct.usb_cfg_str_descr)) == -1) {
mdb_warn("failed to read config cloud "
"pointers");
} else {
mdb_printf("\n Device Config Clouds:\n"
" Index\tConfig\t\tConfiguration "
"String\n"
" -----\t------\t\t"
"--------------------\n");
for (i = 0; i < usba_device_struct.usb_n_cfgs;
i++) {
if (mdb_readstr(string_descr,
USB_MAXSTRINGLEN,
(uintptr_t)conf_str_descr[i]) ==
-1) {
(void) strcpy(string_descr,
"<No Configuration "
"String>");
}
mdb_printf(" %4d\t0x%p\t%s\n", i,
config_cloud[i], string_descr);
}
}
}
mdb_printf("\n Active configuration index: %d\n",
usba_device_struct.usb_active_cfg_ndx);
}
if (usb_flag & USB_DUMP_ACTIVE_PIPES) {
if (mdb_pwalk_dcmd("usb_pipe_handle", "usb_pipe_handle",
0, NULL, addr) == -1) {
mdb_warn("failed to walk usb_pipe_handle");
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
int
usba_debug_buf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
char *debug_buf_addr;
char *local_debug_buf;
int debug_buf_size;
char *term_p;
int being_cleared;
if (flags & DCMD_ADDRSPEC) {
return (DCMD_USAGE);
}
if (mdb_readvar(&being_cleared, "usba_clear_debug_buf_flag") ==
-1) {
mdb_warn("failed to read usba_clear_debug_buf_flag");
return (DCMD_ERR);
}
if (being_cleared) {
return (DCMD_OK);
}
if (mdb_readvar(&debug_buf_addr, "usba_debug_buf") == -1) {
mdb_warn("failed to read usba_debug_buf");
return (DCMD_ERR);
}
if (debug_buf_addr == NULL) {
mdb_warn("usba_debug_buf not allocated\n");
return (DCMD_OK);
}
if (mdb_readvar(&debug_buf_size, "usba_debug_buf_size") == -1) {
mdb_warn("failed to read usba_debug_buf_size");
return (DCMD_ERR);
}
debug_buf_size += USB_DEBUG_SIZE_EXTRA_ALLOC;
local_debug_buf = (char *)mdb_alloc(debug_buf_size, UM_SLEEP | UM_GC);
if ((mdb_vread(local_debug_buf, debug_buf_size,
(uintptr_t)debug_buf_addr)) == -1) {
mdb_warn("failed to read usba_debug_buf at %p",
local_debug_buf);
return (DCMD_ERR);
}
local_debug_buf[debug_buf_size - 1] = '\0';
if (strlen(local_debug_buf) == 0) {
return (DCMD_OK);
}
if ((term_p = strstr(local_debug_buf, ">>>>")) == NULL) {
mdb_warn("failed to find terminator \">>>>\"\n");
return (DCMD_ERR);
}
mdb_printf("%s", term_p+5);
mdb_printf("%s\n", local_debug_buf);
return (DCMD_OK);
}
int
usba_clear_debug_buf(
uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int clear = 1;
if (mdb_writevar((void*)&clear, "usba_clear_debug_buf_flag") == -1) {
mdb_warn("failed to set usba_clear_debug_buf_flag");
return (DCMD_ERR);
}
return (DCMD_OK);
}
extern int prtusb(uintptr_t, uint_t, int, const mdb_arg_t *);
extern void prt_usb_usage(void);
static const mdb_dcmd_t dcmds[] = {
{ "usb_pipe_handle", ":",
"print a usb_pipe_handle struct", usb_pipe_handle, NULL},
{ "usba_device", ": [-pv]",
"print summary info for a usba_device_t struct", usba_device, NULL},
{ "usba_debug_buf", NULL,
"print usba_debug_buf", usba_debug_buf, NULL},
{ "usba_clear_debug_buf", NULL,
"clear usba_debug_buf", usba_clear_debug_buf, NULL},
{ "prtusb", "?[-t] [-v] [-i index]",
"print trees and descriptors for usba_device_t",
prtusb, prt_usb_usage},
{ NULL }
};
static const mdb_walker_t walkers[] = {
{ "usba_list_entry", "walk list of usba_list_entry_t structures",
usba_list_walk_init, usba_list_walk_step, NULL, NULL },
{ "usb_pipe_handle", "walk USB pipe handles, given a usba_device_t ptr",
usb_pipe_handle_walk_init, usb_pipe_handle_walk_step, NULL, NULL },
{ "usba_device", "walk global list of usba_device_t structures",
usba_device_walk_init, usba_list_walk_step, NULL, NULL },
{ "hubd", "walk hubd instances", usba_hubd_walk_init,
usba_hubd_walk_step, NULL, NULL },
{ NULL }
};
static const mdb_modinfo_t modinfo = {
MDB_API_VERSION, dcmds, walkers
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}