#include <gelf.h>
#include <sys/mdb_modapi.h>
#include <mdb/mdb_ks.h>
#include <sys/usb/usba.h>
#include <sys/usb/usba/usba_types.h>
#include <sys/usb/hcd/uhci/uhci.h>
#include <sys/usb/hcd/uhci/uhcid.h>
#include <sys/usb/hcd/uhci/uhciutil.h>
#define UHCI_TD 0
#define UHCI_QH 1
int uhci_td(uintptr_t, uint_t, int, const mdb_arg_t *);
int uhci_qh(uintptr_t, uint_t, int, const mdb_arg_t *);
int uhci_td_walk_init(mdb_walk_state_t *);
int uhci_td_walk_step(mdb_walk_state_t *);
int uhci_qh_walk_init(mdb_walk_state_t *);
int uhci_qh_walk_step(mdb_walk_state_t *);
typedef struct find_instance_struct {
void *fic_td_qh;
boolean_t fic_td_or_qh;
boolean_t fic_found;
uhci_state_t *fic_uhci_statep;
} find_instance_cb_t;
static int
find_uhci_instance(uintptr_t uhci_instancep, const void *local_ss, void *cb_arg)
{
int td_pool_size, qh_pool_size;
find_instance_cb_t *cb_data = (find_instance_cb_t *)cb_arg;
uhci_state_t *uhcip = cb_data->fic_uhci_statep;
if (mdb_vread(cb_data->fic_uhci_statep, sizeof (uhci_state_t),
uhci_instancep) == -1) {
mdb_warn("failed to read uhci_state at %p", uhci_instancep);
return (-1);
}
if (mdb_readsym(&td_pool_size, sizeof (int), "uhci_td_pool_size") ==
-1) {
mdb_warn("failed to read uhci_td_pool_size");
return (-1);
}
if (mdb_readsym(&qh_pool_size, sizeof (int), "uhci_qh_pool_size") ==
-1) {
mdb_warn("failed to read uhci_td_pool_size");
return (-1);
}
if ((cb_data->fic_td_or_qh == UHCI_TD &&
((uhci_td_t *)cb_data->fic_td_qh >= uhcip->uhci_td_pool_addr &&
(uhci_td_t *)cb_data->fic_td_qh <= (uhcip->uhci_td_pool_addr +
td_pool_size - sizeof (uhci_td_t)))) ||
(cb_data->fic_td_or_qh == UHCI_QH &&
((queue_head_t *)cb_data->fic_td_qh >= uhcip->uhci_qh_pool_addr &&
(queue_head_t *)cb_data->fic_td_qh <= (uhcip->uhci_qh_pool_addr +
qh_pool_size - sizeof (queue_head_t))))) {
cb_data->fic_found = TRUE;
return (WALK_DONE);
}
return (WALK_NEXT);
}
static int
find_uhci_statep(void *td_qh, boolean_t td_or_qh, uhci_state_t *uhci_statep)
{
find_instance_cb_t cb_data;
uintptr_t uhci_ss;
if (uhci_statep == NULL) {
mdb_warn("failed to find uhci statep: "
"NULL uhci_statep param\n");
return (-1);
}
cb_data.fic_td_qh = td_qh;
cb_data.fic_td_or_qh = td_or_qh;
cb_data.fic_found = FALSE;
cb_data.fic_uhci_statep = uhci_statep;
if (mdb_readsym(&uhci_ss, sizeof (uhci_statep),
"uhci_statep") == -1) {
mdb_warn("failed to read uhci_statep");
return (-1);
}
if (mdb_pwalk("softstate", find_uhci_instance, &cb_data,
uhci_ss) != 0) {
mdb_warn("failed to walk softstate");
return (-1);
}
if (cb_data.fic_found == TRUE) {
return (1);
}
return (0);
}
int
uhci_td(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t depth_flag = FALSE;
uhci_state_t uhci_state, *uhcip = &uhci_state;
uhci_td_t td;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
if (addr & ~QH_LINK_PTR_MASK) {
mdb_warn("address must be on a 16-byte boundary.\n");
return (DCMD_ERR);
}
if (mdb_getopts(argc, argv,
'd', MDB_OPT_SETBITS, TRUE, &depth_flag,
NULL) != argc) {
return (DCMD_USAGE);
}
if (depth_flag) {
if (mdb_pwalk_dcmd("uhci_td", "uhci_td", 0, NULL, addr) == -1) {
mdb_warn("failed to walk 'uhci_td'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
if (find_uhci_statep((void *)addr, UHCI_TD, uhcip) != 1) {
mdb_warn("failed to find uhci_statep");
return (DCMD_ERR);
}
if (mdb_vread(&td, sizeof (td), addr) != sizeof (td)) {
mdb_warn("failed to read td at vaddr %p", addr);
return (DCMD_ERR);
}
mdb_printf("\n UHCI td struct at (vaddr) %08x:\n", addr);
if (!(td.link_ptr & HC_END_OF_LIST) && td.link_ptr != 0) {
mdb_printf(" link_ptr (paddr) : %-8x "
"(vaddr) : %p\n",
td.link_ptr,
TD_VADDR(td.link_ptr & QH_LINK_PTR_MASK));
} else {
mdb_printf(" link_ptr (paddr) : %-8x\n",
td.link_ptr);
}
mdb_printf(" td_dword2 : %08x\n", td.dw2);
mdb_printf(" td_dword3 : %08x\n", td.dw3);
mdb_printf(" buffer_address : %08x\n", td.buffer_address);
mdb_printf(" qh_td_prev : %?p "
"tw_td_next : %?p\n",
td.qh_td_prev, td.tw_td_next);
mdb_printf(" outst_td_prev : %?p "
"outst_td_next : %?p\n",
td.outst_td_prev, td.outst_td_next);
mdb_printf(" tw : %?p "
"flag : %02x\n", td.tw, td.flag);
mdb_printf(" isoc_next : %?p "
"isoc_prev : %0x\n", td.isoc_next, td.isoc_prev);
mdb_printf(" isoc_pkt_index : %0x "
"startingframe: %0x\n", td.isoc_pkt_index, td.starting_frame);
if (td.link_ptr == 0) {
mdb_printf(" --> Link pointer = NULL\n");
return (DCMD_ERR);
} else {
if (td.link_ptr & HC_END_OF_LIST) {
mdb_printf(" "
"--> Link pointer invalid (terminate bit set).\n");
} else {
if ((td.link_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD) {
mdb_printf(" "
"--> Link pointer points to a QH.\n");
} else {
mdb_printf(" "
"--> Link pointer points to a TD.\n");
}
}
}
return (DCMD_OK);
}
int
uhci_qh(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t breadth_flag = FALSE, depth_flag = FALSE;
uhci_state_t uhci_state, *uhcip = &uhci_state;
queue_head_t qh;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
if (addr & ~QH_LINK_PTR_MASK) {
mdb_warn("address must be on a 16-byte boundary.\n");
return (DCMD_ERR);
}
if (mdb_getopts(argc, argv,
'b', MDB_OPT_SETBITS, TRUE, &breadth_flag,
'd', MDB_OPT_SETBITS, TRUE, &depth_flag,
NULL) != argc) {
return (DCMD_USAGE);
}
if (breadth_flag) {
uint_t new_argc = 0;
mdb_arg_t new_argv[1];
if (depth_flag) {
new_argc = 1;
new_argv[0].a_type = MDB_TYPE_STRING;
new_argv[0].a_un.a_str = "-d";
}
if ((mdb_pwalk_dcmd("uhci_qh", "uhci_qh", new_argc, new_argv,
addr)) != 0) {
mdb_warn("failed to walk 'uhci_qh'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
if (find_uhci_statep((void *)addr, UHCI_QH, uhcip) != 1) {
mdb_warn("failed to find uhci_statep");
return (DCMD_ERR);
}
if (mdb_vread(&qh, sizeof (qh), addr) != sizeof (qh)) {
mdb_warn("failed to read qh at vaddr %p", addr);
return (DCMD_ERR);
}
mdb_printf("\n UHCI qh struct at (vaddr) %08x:\n", addr);
if (!(qh.link_ptr & HC_END_OF_LIST) && qh.link_ptr != 0) {
mdb_printf(" link_ptr (paddr) : %08x "
"(vaddr) : %p\n",
qh.link_ptr,
QH_VADDR(qh.link_ptr & QH_LINK_PTR_MASK));
} else {
mdb_printf(
" link_ptr (paddr) : %08x\n",
qh.link_ptr);
}
if (!(qh.element_ptr & HC_END_OF_LIST) && qh.element_ptr != 0) {
mdb_printf(" element_ptr (paddr) : %08x "
"(vaddr) : %p\n",
qh.element_ptr,
TD_VADDR(qh.element_ptr & QH_LINK_PTR_MASK));
} else {
mdb_printf(
" element_ptr (paddr) : %08x\n", qh.element_ptr);
}
mdb_printf(" node : %04x "
"flag : %04x\n",
qh.node, qh.qh_flag);
mdb_printf(" prev_qh : %?p "
"td_tailp : %?p\n",
qh.prev_qh, qh.td_tailp);
mdb_printf(" bulk_xfer_isoc_info : %?p\n", qh.bulk_xfer_info);
if (qh.link_ptr == 0) {
mdb_printf(" --> Link pointer = NULL\n");
return (DCMD_ERR);
} else {
if (qh.link_ptr & HC_END_OF_LIST) {
mdb_printf(" "
"--> Link pointer invalid (terminate bit set).\n");
} else {
if ((qh.link_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD) {
mdb_printf(" "
"--> Link pointer points to a QH.\n");
} else {
mdb_warn(" "
"--> Link pointer points to a TD.\n");
return (DCMD_ERR);
}
}
}
if (qh.element_ptr == 0) {
mdb_printf(" element_ptr = NULL\n");
return (DCMD_ERR);
} else {
if (qh.element_ptr & HC_END_OF_LIST) {
mdb_printf(" "
"-->Element pointer invalid (terminate bit set)."
"\n");
return (DCMD_OK);
} else {
if ((qh.element_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD) {
mdb_printf(" "
"--> Element pointer points to a QH.\n");
return (DCMD_ERR);
} else {
mdb_printf(" "
"--> Element pointer points to a TD.\n");
}
}
}
if (depth_flag) {
if (mdb_pwalk_dcmd("uhci_td", "uhci_td", argc, argv,
(uintptr_t)(TD_VADDR(qh.element_ptr &
QH_LINK_PTR_MASK))) == -1) {
mdb_warn("failed to walk 'uhci_td'");
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
int
uhci_td_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr == 0) {
return (DCMD_USAGE);
}
wsp->walk_data = mdb_alloc(sizeof (uhci_td_t), UM_SLEEP | UM_GC);
wsp->walk_arg = mdb_alloc(sizeof (uhci_state_t), UM_SLEEP | UM_GC);
if (find_uhci_statep((void *)wsp->walk_addr, UHCI_TD,
wsp->walk_arg) != 1) {
mdb_warn("failed to find uhci_statep");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
uhci_td_walk_step(mdb_walk_state_t *wsp)
{
int status;
uhci_state_t *uhcip = (uhci_state_t *)wsp->walk_arg;
if (mdb_vread(wsp->walk_data, sizeof (uhci_td_t), wsp->walk_addr)
== -1) {
mdb_warn("failed to read td at %p", wsp->walk_addr);
return (WALK_DONE);
}
status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
wsp->walk_cbdata);
wsp->walk_addr = ((uhci_td_t *)wsp->walk_data)->link_ptr;
if (wsp->walk_addr == 0 || wsp->walk_addr & HC_END_OF_LIST)
return (WALK_DONE);
if (((((uhci_td_t *)wsp->walk_data)->link_ptr) & HC_QUEUE_HEAD)
== HC_QUEUE_HEAD) {
return (WALK_DONE);
}
wsp->walk_addr &= QH_LINK_PTR_MASK;
if (wsp->walk_addr == 0)
return (WALK_DONE);
wsp->walk_addr = (uintptr_t)TD_VADDR(wsp->walk_addr);
return (status);
}
int
uhci_qh_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr == 0)
return (DCMD_USAGE);
wsp->walk_data = mdb_alloc(sizeof (queue_head_t), UM_SLEEP | UM_GC);
wsp->walk_arg = mdb_alloc(sizeof (uhci_state_t), UM_SLEEP | UM_GC);
if (find_uhci_statep((void *)wsp->walk_addr, UHCI_QH,
(uhci_state_t *)wsp->walk_arg) != 1) {
mdb_warn("failed to find uhci_statep");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
uhci_qh_walk_step(mdb_walk_state_t *wsp)
{
int status;
uhci_state_t *uhcip = (uhci_state_t *)wsp->walk_arg;
if (wsp->walk_addr == 0)
return (WALK_DONE);
if (mdb_vread(wsp->walk_data, sizeof (queue_head_t), wsp->walk_addr)
== -1) {
mdb_warn("failure reading qh at %p", wsp->walk_addr);
return (WALK_DONE);
}
status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
wsp->walk_cbdata);
wsp->walk_addr = ((queue_head_t *)wsp->walk_data)->link_ptr;
if (wsp->walk_addr == 0 || wsp->walk_addr & HC_END_OF_LIST) {
return (WALK_DONE);
}
if (((((queue_head_t *)wsp->walk_data)->link_ptr) & HC_QUEUE_HEAD)
!= HC_QUEUE_HEAD) {
return (WALK_DONE);
}
wsp->walk_addr &= QH_LINK_PTR_MASK;
if (wsp->walk_addr == 0)
return (WALK_DONE);
wsp->walk_addr = (uintptr_t)QH_VADDR(wsp->walk_addr);
return (status);
}
static const mdb_dcmd_t dcmds[] = {
{ "uhci_td", ": [-d]", "print UHCI TD", uhci_td, NULL },
{ "uhci_qh", ": [-bd]", "print UHCI QH", uhci_qh, NULL},
{ NULL }
};
static const mdb_walker_t walkers[] = {
{ "uhci_td", "walk list of UHCI TD structures",
uhci_td_walk_init, uhci_td_walk_step, NULL,
NULL },
{ "uhci_qh", "walk list of UHCI QH structures",
uhci_qh_walk_init, uhci_qh_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);
}