usb.c revision eae66f1631399d412a02922037e18608ecce2123
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stddef.h>
#include <sys/mdb_modapi.h>
#include <sys/ddi_impldefs.h>
#include <unistd.h>
/*
* Prototypes
*/
/* usba.c */
/*
* Defines
*/
/* dcmd options */
#define USB_DUMP_VERBOSE 0x01
#define USB_DUMP_ACTIVE_PIPES 0x02
/* Hardcoded slop factor designed into debug buf logic */
#define USB_DEBUG_SIZE_EXTRA_ALLOC 8
/*
* Callback arg struct for find_dip (callback func used in usba_device2devinfo).
*/
typedef struct usba_device2devinfo_data {
/*
* Callback for usba_device2dip.
* Callback called from the devinfo_children walk invoked in usba_device2dip.
*
* For the current dip, get the (potential) pointer to its usba_device_t
* struct.
* See if this pointer matches the address of the usba_device_t we're looking
* for (passed in as usb_dev_p). If so, stuff its value in u2d_dip_addr,
* and terminate the walk.
*
* - dip_addr is the address in core of the dip currently being processed by the
* walk
* - local_dip is a pointer to a copy of the struct dev_info in local memory
* - cb_data is the addr of the callback arg the walker was invoked with
* (passed through transparently from walk invoker).
*
* Returns:
* - WALK_NEXT on success (match not found yet)
* - WALK_ERR on errors.
* - WALK_DONE is returned, cb_data.found is set to TRUE, and
* *cb_data.u2d_dip_addr is set to the matched dip addr if a dip corresponding
* to the desired usba_device_t* is found.
*/
/*ARGSUSED*/
static int
{
/*
* If there's no corresponding usba_device_t, this dip isn't
* a usb node. Might be an sd node. Ignore it.
*/
return (WALK_NEXT);
}
return (WALK_DONE);
}
return (WALK_NEXT);
}
/*
* Given a usba_device pointer, figure out which dip is associated with it.
* Relies on usba_device.usb_root_hub_dip being accurate.
*
* - usb_dev_addr is a pointer to a usba_device_t in core.
* - dip_addr is the address of a uintptr_t to receive the address in core
* of the found dip (if any).
*
* Returns:
* 0 on success (no match found)
* 1 on success (match found)
* -1 on errors.
*/
static int
{
/*
* Walk all USB children of the root hub devinfo.
* The callback func looks for a match on the usba_device address.
*/
usb_dev_addr) == -1) {
mdb_warn("failed to read usba_device struct");
return (-1);
}
/*
* Walk devinfo children starting with the root hub node,
* looking for a match on the usba_device pointer (which is what
* find_dip does).
* Result is placed in cb_data.dip_addr.
*/
mdb_warn("failed to walk devinfo_children");
return (-1);
}
return (1);
}
return (0);
}
/*
* Generic walker usba_list_entry_t walker.
* Works for any usba_list_entry_t list.
*/
int
{
/* Must have a start addr. */
mdb_warn("not a global walk. Starting address required\n");
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* Generic list walker step routine.
* NOTE: multiple walkers share this routine.
*/
int
{
int status;
mdb_warn("failed to read usba_list_entry_t at %p",
return (WALK_ERR);
}
wsp->walk_cbdata);
/* Check if we're at the last element */
return (WALK_DONE);
}
return (status);
}
/*
* usb_pipe_handle walker
* Given a pointer to a usba_device_t, walk the array of endpoint
* pipe_handle lists.
* For each list, traverse the list, invoking the callback on each element.
*
* Note this function takes the address of a usba_device struct (which is
* easily obtainable), but actually traverses a sub-portion of the struct
* (which address is not so easily obtainable).
*/
int
{
mdb_warn("not a global walk; usba_device_t required\n");
return (WALK_ERR);
}
/*
* Read the usb_ph_list array into local memory.
*/
(sizeof (usba_ph_impl_t)) * USBA_N_ENDPOINTS,
mdb_warn("failed to read usb_pipehandle_list at %p",
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
int status;
/* Find the first valid endpoint, starting from where we left off. */
while ((index < USBA_N_ENDPOINTS) &&
index++;
}
/* No more valid endpoints. */
if (index >= USBA_N_ENDPOINTS) {
return (WALK_DONE);
}
/* Set up to start at next pipe handle next time. */
return (status);
}
/*
* Given the address of a usba_pipe_handle_data_t, dump summary info.
*/
/*ARGSUSED*/
int
{
if (!(flags & DCMD_ADDRSPEC)) {
return (DCMD_USAGE);
}
if (mdb_vread(&pipe_handle,
return (DCMD_ERR);
}
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;
}
}
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");
}
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",
return (DCMD_OK);
}
/*
* usba_device walker:
*
* walks the chain of usba_device structs headed by usba_device_list in usba.c
* NOTE: It uses the generic list walk step routine usba_list_walk_step.
* No walk_fini routine is needed.
*/
int
{
"global walk only. Must be invoked without an address\n");
return (WALK_ERR);
}
mdb_warn("failed to read usba_device_list");
return (WALK_ERR);
}
/* List head is not part of usba_device_t, get first usba_device_t */
return (WALK_NEXT);
}
/*
* usba_device dcmd
* Given the address of a usba_device struct, dump summary info
* -v: Print more (verbose) info
*/
/*ARGSUSED*/
int
{
int status;
char pathname[MAXNAMELEN];
if (!(flags & DCMD_ADDRSPEC)) {
/* Global walk */
argv) == -1) {
mdb_warn("failed to walk usba_device");
return (DCMD_ERR);
}
return (DCMD_OK);
}
return (DCMD_USAGE);
}
mdb_printf("\n");
}
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%-15s %4s %-?s %-42s%</u>\n",
"NAME", "INST", "DIP", "PATH ");
}
/*
* -1 = error
* 0 = no error, no match
* 1 = no error, match
*/
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);
}
/* Figure out what driver (name) is attached to this node. */
dip_addr) == -1) {
mdb_warn("failed to read devinfo");
return (DCMD_ERR);
}
}
if (usb_flag & USB_DUMP_VERBOSE) {
int i;
char *string_descr;
char **config_cloud, **conf_str_descr;
if (mdb_vread(&usba_device_struct,
mdb_warn("failed to read usba_device struct");
return (DCMD_ERR);
}
mdb_warn("failed to read usb_dev_descr_t struct");
return (DCMD_ERR);
}
mdb_printf("\n idVendor: 0x%04x idProduct: 0x%04x "
/* Get the string descriptor string into local space. */
} else {
mdb_warn("failed to read manufacturer "
"string descriptor");
}
}
} else {
-1) {
mdb_warn("failed to read product string "
"descriptor");
}
}
} else {
-1) {
mdb_warn("failed to read serial number string "
"descriptor");
}
}
if (no_driver_attached) {
mdb_printf("\n");
} else {
mdb_printf(" state_p: ");
/*
* Given the dip, find the associated statep. The
* convention to generate this soft state anchor is:
* <driver_name>_statep
*/
"%s_statep", dname);
&statep) == -1) {
mdb_warn("failed to find %s state struct for "
return (DCMD_ERR);
}
}
config_cloud = (char **)mdb_alloc(sizeof (void *) *
conf_str_descr = (char **)mdb_alloc(sizeof (void *) *
if ((usba_device_struct.usb_cfg_array) &&
if ((mdb_vread(config_cloud, sizeof (void *) *
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,
(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",
}
if (usb_flag & USB_DUMP_ACTIVE_PIPES) {
mdb_warn("failed to walk usb_pipe_handle");
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
/*
* Dump the contents of the usba_debug_buf, from the oldest to newest,
* wrapping around if necessary.
*/
/*ARGSUSED*/
int
{
char *debug_buf_addr; /* addr in core */
char *local_debug_buf; /* local copy of buf */
int debug_buf_size;
char *term_p;
int being_cleared;
if (flags & DCMD_ADDRSPEC) {
return (DCMD_USAGE);
}
-1) {
mdb_warn("failed to read usba_clear_debug_buf_flag");
return (DCMD_ERR);
}
if (being_cleared) {
return (DCMD_OK);
}
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);
}
mdb_warn("failed to read usba_debug_buf_size");
return (DCMD_ERR);
}
mdb_warn("failed to read usba_debug_buf at %p",
return (DCMD_ERR);
}
return (DCMD_OK);
}
mdb_warn("failed to find terminator \">>>>\"\n");
return (DCMD_ERR);
}
/*
* Print the chunk of buffer from the terminator to the end.
* This will print a null string if no wrap has occurred yet.
*/
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
int clear = 1;
/* stop the tracing */
mdb_warn("failed to set usba_clear_debug_buf_flag");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/* prtusb entries */
extern void prt_usb_usage(void);
/*
* MDB module linkage information:
*
* We declare a list of structures describing our dcmds, and a function
* named _mdb_init to return a pointer to our module information.
*/
static const mdb_dcmd_t dcmds[] = {
{ "usb_pipe_handle", ":",
{ "usba_device", ": [-pv]",
{ "usba_debug_buf", NULL,
{ "usba_clear_debug_buf", NULL,
{ "prtusb", "?[-t] [-v] [-i index]",
"print trees and descriptors for usba_device_t",
{ NULL }
};
static const mdb_walker_t walkers[] = {
/* Generic list walker. */
{ "usba_list_entry", "walk list of usba_list_entry_t structures",
{ "usb_pipe_handle", "walk USB pipe handles, given a usba_device_t ptr",
{ "usba_device", "walk global list of usba_device_t structures",
{ NULL }
};
static const mdb_modinfo_t modinfo = {
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}