1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <gelf.h>
  30 
  31 #include <sys/mdb_modapi.h>
  32 #include <mdb/mdb_ks.h>
  33 
  34 #include <sys/usb/usba.h>
  35 #include <sys/usb/usba/usba_types.h>
  36 
  37 #include <sys/usb/hcd/uhci/uhci.h>
  38 #include <sys/usb/hcd/uhci/uhcid.h>
  39 #include <sys/usb/hcd/uhci/uhciutil.h>
  40 
  41 
  42 #define UHCI_TD 0
  43 #define UHCI_QH 1
  44 
  45 
  46 /* Prototypes */
  47 
  48 int     uhci_td(uintptr_t, uint_t, int, const mdb_arg_t *);
  49 int     uhci_qh(uintptr_t, uint_t, int, const mdb_arg_t *);
  50 int     uhci_td_walk_init(mdb_walk_state_t *);
  51 int     uhci_td_walk_step(mdb_walk_state_t *);
  52 int     uhci_qh_walk_init(mdb_walk_state_t *);
  53 int     uhci_qh_walk_step(mdb_walk_state_t *);
  54 
  55 
  56 /*
  57  * Callback for find_uhci_statep (called back from walk "softstate" in
  58  * find_uhci_statep).
  59  *
  60  * - uhci_instancep is the value of the current pointer in the array of soft
  61  * state instance pointers (see i_ddi_soft_state in ddi_impldefs.h)
  62  * - local_ss is a pointer to the copy of the i_ddi_soft_state in local space
  63  * - cb_arg is a pointer to the cb arg (an instance of state_find_data).
  64  *
  65  * For the current uchi_state_t*, see if the td address is in its pool.
  66  *
  67  * Returns WALK_NEXT on success (match not found yet), WALK_ERR on errors.
  68  *
  69  * WALK_DONE is returned, cb_data.found is set to TRUE, and
  70  * *cb_data.fic_uhci_statep is filled in with the contents of the state
  71  * struct in core. This forces the walk to terminate.
  72  */
  73 typedef struct find_instance_struct {
  74         void            *fic_td_qh;     /* td/qh we want uhci instance for */
  75         boolean_t       fic_td_or_qh;   /* which one td_qh points to */
  76         boolean_t       fic_found;
  77         uhci_state_t    *fic_uhci_statep; /* buffer uhci_state's written into */
  78 } find_instance_cb_t;
  79 
  80 /*ARGSUSED*/
  81 static int
  82 find_uhci_instance(uintptr_t uhci_instancep, const void *local_ss, void *cb_arg)
  83 {
  84         int                     td_pool_size, qh_pool_size;
  85         find_instance_cb_t      *cb_data = (find_instance_cb_t *)cb_arg;
  86         uhci_state_t            *uhcip = cb_data->fic_uhci_statep;
  87 
  88 
  89         if (mdb_vread(cb_data->fic_uhci_statep, sizeof (uhci_state_t),
  90             uhci_instancep) == -1) {
  91                 mdb_warn("failed to read uhci_state at %p", uhci_instancep);
  92                 return (-1);
  93         }
  94 
  95         if (mdb_readsym(&td_pool_size, sizeof (int), "uhci_td_pool_size") ==
  96             -1) {
  97                 mdb_warn("failed to read uhci_td_pool_size");
  98                 return (-1);
  99         }
 100 
 101         if (mdb_readsym(&qh_pool_size, sizeof (int), "uhci_qh_pool_size") ==
 102             -1) {
 103                 mdb_warn("failed to read uhci_td_pool_size");
 104                 return (-1);
 105         }
 106 
 107         /*
 108          * See if the addr is within the appropriate pool for this instance.
 109          */
 110         if ((cb_data->fic_td_or_qh == UHCI_TD &&
 111 
 112             ((uhci_td_t *)cb_data->fic_td_qh >= uhcip->uhci_td_pool_addr &&
 113             (uhci_td_t *)cb_data->fic_td_qh <= (uhcip->uhci_td_pool_addr +
 114             td_pool_size - sizeof (uhci_td_t)))) ||
 115 
 116             (cb_data->fic_td_or_qh == UHCI_QH &&
 117 
 118             ((queue_head_t *)cb_data->fic_td_qh >= uhcip->uhci_qh_pool_addr &&
 119             (queue_head_t *)cb_data->fic_td_qh <= (uhcip->uhci_qh_pool_addr +
 120             qh_pool_size - sizeof (queue_head_t))))) {
 121 
 122                 /* td/qh address is within pool for this instance of uhci. */
 123                 cb_data->fic_found = TRUE;
 124                 return (WALK_DONE);
 125         }
 126 
 127         return (WALK_NEXT);
 128 }
 129 
 130 /*
 131  * Figure out which instance of uhci owns a td/qh.
 132  *
 133  * - td_qh: a pointer to a uhci td or qh
 134  * - td_or_qh: a flag indicating which it is (td/qh),
 135  * - uhci_statep, pointer to a uhci_state_t, to be filled in with data from
 136  * the found instance of uhci_state_t.
 137  *
 138  * Only works for Cntl/Interrupt tds/qhs; others are dynamically allocated
 139  * and so cannot be found with this method.
 140  *
 141  * Returns 0 on success (no match found), 1 on success (match found),
 142  * -1 on errors.
 143  */
 144 static int
 145 find_uhci_statep(void *td_qh, boolean_t td_or_qh, uhci_state_t *uhci_statep)
 146 {
 147         find_instance_cb_t      cb_data;
 148         uintptr_t               uhci_ss;
 149 
 150 
 151         if (uhci_statep == NULL) {
 152                 mdb_warn("failed to find uhci statep: "
 153                     "NULL uhci_statep param\n");
 154                 return (-1);
 155         }
 156 
 157         cb_data.fic_td_qh = td_qh;
 158         cb_data.fic_td_or_qh = td_or_qh;
 159         cb_data.fic_found = FALSE;
 160         cb_data.fic_uhci_statep = uhci_statep;
 161 
 162 
 163         if (mdb_readsym(&uhci_ss, sizeof (uhci_statep),
 164             "uhci_statep") == -1) {
 165                 mdb_warn("failed to read uhci_statep");
 166                 return (-1);
 167         }
 168 
 169 
 170         /*
 171          * Walk all instances of uhci.
 172          * The callback func checks if td_qh belongs to a given instance
 173          * of uhci.
 174          */
 175         if (mdb_pwalk("softstate", find_uhci_instance, &cb_data,
 176             uhci_ss) != 0) {
 177                 mdb_warn("failed to walk softstate");
 178                 return (-1);
 179         }
 180 
 181         if (cb_data.fic_found == TRUE) {
 182                 return (1);
 183         }
 184 
 185         return (0);
 186 }
 187 
 188 /*
 189  * Dump a UHCI TD (transaction descriptor);
 190  * or (-d) the chain of TDs starting with the one specified.
 191  */
 192 int
 193 uhci_td(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 194 {
 195         uint_t          depth_flag = FALSE;
 196         uhci_state_t    uhci_state, *uhcip = &uhci_state;
 197         uhci_td_t       td;
 198 
 199 
 200         if (!(flags & DCMD_ADDRSPEC))
 201                 return (DCMD_USAGE);
 202 
 203         if (addr & ~QH_LINK_PTR_MASK) {
 204                 mdb_warn("address must be on a 16-byte boundary.\n");
 205                 return (DCMD_ERR);
 206         }
 207 
 208         if (mdb_getopts(argc, argv,
 209             'd', MDB_OPT_SETBITS, TRUE, &depth_flag,
 210             NULL) != argc) {
 211                 return (DCMD_USAGE);
 212         }
 213 
 214 
 215         if (depth_flag) {
 216                 if (mdb_pwalk_dcmd("uhci_td", "uhci_td", 0, NULL, addr) == -1) {
 217                         mdb_warn("failed to walk 'uhci_td'");
 218                         return (DCMD_ERR);
 219                 }
 220                 return (DCMD_OK);
 221         }
 222 
 223 
 224         if (find_uhci_statep((void *)addr, UHCI_TD, uhcip) != 1) {
 225                 mdb_warn("failed to find uhci_statep");
 226                 return (DCMD_ERR);
 227         }
 228 
 229         if (mdb_vread(&td, sizeof (td), addr) != sizeof (td))  {
 230                 mdb_warn("failed to read td at vaddr %p", addr);
 231                 return (DCMD_ERR);
 232         }
 233 
 234         mdb_printf("\n  UHCI td struct at (vaddr) %08x:\n", addr);
 235 
 236         if (!(td.link_ptr & HC_END_OF_LIST) && td.link_ptr != NULL) {
 237                 mdb_printf("        link_ptr (paddr)    : %-8x        "
 238                     "(vaddr)      : %p\n",
 239                     td.link_ptr,
 240                     /* Note: uhcip needed by TD_VADDR macro */
 241                     TD_VADDR(td.link_ptr & QH_LINK_PTR_MASK));
 242         } else {
 243                 mdb_printf("        link_ptr (paddr)    : %-8x\n",
 244                     td.link_ptr);
 245         }
 246         mdb_printf("        td_dword2           : %08x\n", td.dw2);
 247         mdb_printf("        td_dword3           : %08x\n", td.dw3);
 248         mdb_printf("        buffer_address      : %08x\n", td.buffer_address);
 249         mdb_printf("        qh_td_prev          : %?p        "
 250             "tw_td_next   : %?p\n",
 251             td.qh_td_prev, td.tw_td_next);
 252         mdb_printf("        outst_td_prev        : %?p        "
 253             "outst_td_next : %?p\n",
 254             td.outst_td_prev, td.outst_td_next);
 255         mdb_printf("        tw                  : %?p        "
 256             "flag         : %02x\n", td.tw, td.flag);
 257         mdb_printf("        isoc_next           : %?p        "
 258             "isoc_prev    : %0x\n", td.isoc_next, td.isoc_prev);
 259         mdb_printf("        isoc_pkt_index      : %0x        "
 260             "startingframe: %0x\n", td.isoc_pkt_index, td.starting_frame);
 261 
 262 
 263         if (td.link_ptr == NULL)  {
 264                 mdb_printf("        --> Link pointer = NULL\n");
 265                 return (DCMD_ERR);
 266         } else {
 267 
 268                 /* Inform user if link is to a TD or QH.  */
 269                 if (td.link_ptr & HC_END_OF_LIST)  {
 270                         mdb_printf("        "
 271                             "--> Link pointer invalid (terminate bit set).\n");
 272                 } else {
 273                         if ((td.link_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD)  {
 274                                 mdb_printf("        "
 275                                     "--> Link pointer points to a QH.\n");
 276                         } else {
 277                                 mdb_printf("        "
 278                                     "--> Link pointer points to a TD.\n");
 279                         }
 280                 }
 281         }
 282 
 283         return (DCMD_OK);
 284 }
 285 
 286 /*
 287  * Dump a UHCI QH (queue head).
 288  * -b walk/dump the chian of QHs starting with the one specified.
 289  * -d also dump the chain of TDs starting with the one specified.
 290  */
 291 int
 292 uhci_qh(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 293 {
 294         uint_t          breadth_flag = FALSE, depth_flag = FALSE;
 295         uhci_state_t    uhci_state, *uhcip = &uhci_state;
 296         queue_head_t    qh;
 297 
 298 
 299         if (!(flags & DCMD_ADDRSPEC))
 300                 return (DCMD_USAGE);
 301 
 302         if (addr & ~QH_LINK_PTR_MASK) {
 303                 mdb_warn("address must be on a 16-byte boundary.\n");
 304                 return (DCMD_ERR);
 305         }
 306 
 307         if (mdb_getopts(argc, argv,
 308             'b', MDB_OPT_SETBITS, TRUE, &breadth_flag,
 309             'd', MDB_OPT_SETBITS, TRUE, &depth_flag,
 310             NULL) != argc) {
 311                 return (DCMD_USAGE);
 312         }
 313 
 314 
 315         if (breadth_flag) {
 316                 uint_t          new_argc = 0;
 317                 mdb_arg_t       new_argv[1];
 318 
 319 
 320                 if (depth_flag) {
 321                         new_argc = 1;
 322                         new_argv[0].a_type = MDB_TYPE_STRING;
 323                         new_argv[0].a_un.a_str = "-d";
 324                 }
 325 
 326                 if ((mdb_pwalk_dcmd("uhci_qh", "uhci_qh", new_argc, new_argv,
 327                     addr)) != 0)  {
 328                         mdb_warn("failed to walk 'uhci_qh'");
 329                         return (DCMD_ERR);
 330                 }
 331                 return (DCMD_OK);
 332         }
 333 
 334 
 335         if (find_uhci_statep((void *)addr, UHCI_QH, uhcip) != 1) {
 336                 mdb_warn("failed to find uhci_statep");
 337                 return (DCMD_ERR);
 338         }
 339 
 340 
 341         if (mdb_vread(&qh, sizeof (qh), addr) != sizeof (qh))  {
 342                 mdb_warn("failed to read qh at vaddr %p", addr);
 343                 return (DCMD_ERR);
 344         }
 345 
 346         mdb_printf("\n  UHCI qh struct at (vaddr) %08x:\n", addr);
 347 
 348         if (!(qh.link_ptr & HC_END_OF_LIST) && qh.link_ptr != NULL) {
 349                 mdb_printf("        link_ptr (paddr)    : %08x        "
 350                     "(vaddr)      : %p\n",
 351                     qh.link_ptr,
 352                     /* Note: uhcip needed by QH_VADDR macro */
 353                     QH_VADDR(qh.link_ptr & QH_LINK_PTR_MASK));
 354         } else {
 355                 mdb_printf(
 356                     "        link_ptr (paddr)    : %08x\n",
 357                     qh.link_ptr);
 358         }
 359 
 360         if (!(qh.element_ptr & HC_END_OF_LIST) && qh.element_ptr != NULL) {
 361                 mdb_printf("        element_ptr (paddr) : %08x        "
 362                     "(vaddr)      : %p\n",
 363                     qh.element_ptr,
 364                     /* Note: uhcip needed by TD_VADDR macro */
 365                     TD_VADDR(qh.element_ptr & QH_LINK_PTR_MASK));
 366         } else {
 367                 mdb_printf(
 368                     "        element_ptr (paddr) : %08x\n", qh.element_ptr);
 369         }
 370 
 371         mdb_printf("        node                : %04x            "
 372             "flag         : %04x\n",
 373             qh.node, qh.qh_flag);
 374         mdb_printf("        prev_qh             : %?p        "
 375             "td_tailp     : %?p\n",
 376             qh.prev_qh, qh.td_tailp);
 377         mdb_printf("        bulk_xfer_isoc_info : %?p\n", qh.bulk_xfer_info);
 378 
 379 
 380         if (qh.link_ptr == NULL)  {
 381                 mdb_printf("        --> Link pointer = NULL\n");
 382                 return (DCMD_ERR);
 383         } else {
 384 
 385                 /* Inform user if next link is a TD or QH.  */
 386                 if (qh.link_ptr & HC_END_OF_LIST)  {
 387                         mdb_printf("        "
 388                             "--> Link pointer invalid (terminate bit set).\n");
 389                 } else {
 390                         if ((qh.link_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD)  {
 391                                 mdb_printf("        "
 392                                     "--> Link pointer points to a QH.\n");
 393                         } else {
 394                                 /* Should never happen. */
 395                                 mdb_warn("        "
 396                                     "--> Link pointer points to a TD.\n");
 397                                 return (DCMD_ERR);
 398                         }
 399                 }
 400         }
 401 
 402 
 403         if (qh.element_ptr == NULL)  {
 404                 mdb_printf("        element_ptr = NULL\n");
 405                 return (DCMD_ERR);
 406         } else {
 407 
 408                 /* Inform user if next element is a TD or QH.  */
 409                 if (qh.element_ptr & HC_END_OF_LIST)  {
 410                         mdb_printf("        "
 411                             "-->Element pointer invalid (terminate bit set)."
 412                             "\n");
 413                         return (DCMD_OK);
 414                 } else {
 415                         if ((qh.element_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD) {
 416                                 mdb_printf("        "
 417                                     "--> Element pointer points to a QH.\n");
 418                                 /* Should never happen in UHCI implementation */
 419                                 return (DCMD_ERR);
 420                         } else {
 421                                 mdb_printf("        "
 422                                     "--> Element pointer points to a TD.\n");
 423                         }
 424                 }
 425         }
 426 
 427         /*
 428          * If the user specified the -d (depth) option,
 429          * dump all TDs linked to this TD via the element_ptr.
 430          */
 431         if (depth_flag) {
 432 
 433                 /* Traverse and display all the TDs in the chain */
 434                 if (mdb_pwalk_dcmd("uhci_td", "uhci_td", argc, argv,
 435                     (uintptr_t)(TD_VADDR(qh.element_ptr &
 436                     QH_LINK_PTR_MASK))) == -1) {
 437                         mdb_warn("failed to walk 'uhci_td'");
 438                         return (DCMD_ERR);
 439                 }
 440         }
 441 
 442         return (DCMD_OK);
 443 }
 444 
 445 /*
 446  * Walk a list of UHCI Transaction Descriptors (td's).
 447  * Stop at the end of the list, or if the next element in the list is a
 448  * queue head (qh).
 449  * User must specify the address of the first td to look at.
 450  */
 451 int
 452 uhci_td_walk_init(mdb_walk_state_t *wsp)
 453 {
 454         if (wsp->walk_addr == NULL)  {
 455                 return (DCMD_USAGE);
 456         }
 457 
 458         wsp->walk_data = mdb_alloc(sizeof (uhci_td_t), UM_SLEEP | UM_GC);
 459         wsp->walk_arg = mdb_alloc(sizeof (uhci_state_t), UM_SLEEP | UM_GC);
 460 
 461 
 462         /*
 463          * Read the uhci_state_t for the instance of uhci
 464          * using this td address into buf pointed to by walk_arg.
 465          */
 466         if (find_uhci_statep((void *)wsp->walk_addr, UHCI_TD,
 467             wsp->walk_arg) != 1) {
 468                 mdb_warn("failed to find uhci_statep");
 469                 return (WALK_ERR);
 470         }
 471 
 472         return (WALK_NEXT);
 473 }
 474 
 475 /*
 476  * At each step, read a TD into our private storage, and then invoke
 477  * the callback function.  We terminate when we reach a QH, or
 478  * link_ptr is NULL.
 479  */
 480 int
 481 uhci_td_walk_step(mdb_walk_state_t *wsp)
 482 {
 483         int status;
 484         uhci_state_t    *uhcip = (uhci_state_t *)wsp->walk_arg;
 485 
 486 
 487         if (mdb_vread(wsp->walk_data, sizeof (uhci_td_t), wsp->walk_addr)
 488                 == -1) {
 489                 mdb_warn("failed to read td at %p", wsp->walk_addr);
 490                 return (WALK_DONE);
 491         }
 492 
 493         status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
 494             wsp->walk_cbdata);
 495 
 496         /* Next td. */
 497         wsp->walk_addr = ((uhci_td_t *)wsp->walk_data)->link_ptr;
 498 
 499         /* Check if we're at the last element */
 500         if (wsp->walk_addr == NULL || wsp->walk_addr & HC_END_OF_LIST)
 501                 return (WALK_DONE);
 502 
 503         /* Make sure next element is a TD.  If a QH, stop.  */
 504         if (((((uhci_td_t *)wsp->walk_data)->link_ptr) & HC_QUEUE_HEAD)
 505             == HC_QUEUE_HEAD)  {
 506                 return (WALK_DONE);
 507         }
 508 
 509         /* Strip terminate etc. bits.  */
 510         wsp->walk_addr &= QH_LINK_PTR_MASK; /* there is no TD_LINK_PTR_MASK */
 511 
 512         if (wsp->walk_addr == NULL)
 513                 return (WALK_DONE);
 514 
 515         /*
 516          * Convert link_ptr paddr to vaddr
 517          * Note: uhcip needed by TD_VADDR macro
 518          */
 519         wsp->walk_addr = (uintptr_t)TD_VADDR(wsp->walk_addr);
 520 
 521         return (status);
 522 }
 523 
 524 /*
 525  * Walk a list of UHCI Queue Heads (qh's).
 526  * Stop at the end of the list, or if the next element in the list is a
 527  * Transaction Descriptor (td).
 528  * User must specify the address of the first qh to look at.
 529  */
 530 int
 531 uhci_qh_walk_init(mdb_walk_state_t *wsp)
 532 {
 533         if (wsp->walk_addr == NULL)
 534                 return (DCMD_USAGE);
 535 
 536         wsp->walk_data = mdb_alloc(sizeof (queue_head_t), UM_SLEEP | UM_GC);
 537         wsp->walk_arg = mdb_alloc(sizeof (uhci_state_t), UM_SLEEP | UM_GC);
 538 
 539 
 540         /*
 541          * Read the uhci_state_t for the instance of uhci
 542          * using this td address into buf pointed to by walk_arg.
 543          */
 544         if (find_uhci_statep((void *)wsp->walk_addr, UHCI_QH,
 545             (uhci_state_t *)wsp->walk_arg) != 1) {
 546                 mdb_warn("failed to find uhci_statep");
 547                 return (WALK_ERR);
 548         }
 549 
 550         return (WALK_NEXT);
 551 }
 552 
 553 /*
 554  * At each step, read a QH into our private storage, and then invoke
 555  * the callback function.  We terminate when we reach a QH, or
 556  * link_ptr is NULL.
 557  */
 558 int
 559 uhci_qh_walk_step(mdb_walk_state_t *wsp)
 560 {
 561         int status;
 562         uhci_state_t    *uhcip = (uhci_state_t *)wsp->walk_arg;
 563 
 564 
 565         if (wsp->walk_addr == NULL)  /* Should never occur */
 566                 return (WALK_DONE);
 567 
 568         if (mdb_vread(wsp->walk_data, sizeof (queue_head_t), wsp->walk_addr)
 569             == -1) {
 570                 mdb_warn("failure reading qh at %p", wsp->walk_addr);
 571                 return (WALK_DONE);
 572         }
 573 
 574         status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
 575             wsp->walk_cbdata);
 576 
 577         /* Next QH. */
 578         wsp->walk_addr = ((queue_head_t *)wsp->walk_data)->link_ptr;
 579 
 580 
 581         /* Check if we're at the last element */
 582         if (wsp->walk_addr == NULL || wsp->walk_addr & HC_END_OF_LIST)  {
 583                 return (WALK_DONE);
 584         }
 585 
 586         /* Make sure next element is a QH.  If a TD, stop.  */
 587         if (!  ((((queue_head_t *)wsp->walk_data)->link_ptr) & HC_QUEUE_HEAD)
 588             == HC_QUEUE_HEAD)  {
 589                 return (WALK_DONE);
 590         }
 591 
 592         /* Strip terminate etc. bits.  */
 593         wsp->walk_addr &= QH_LINK_PTR_MASK;
 594 
 595         if (wsp->walk_addr == NULL)
 596                 return (WALK_DONE);
 597 
 598         /*
 599          * Convert link_ptr paddr to vaddr
 600          * Note: uhcip needed by QH_VADDR macro
 601          */
 602         wsp->walk_addr = (uintptr_t)QH_VADDR(wsp->walk_addr);
 603 
 604         return (status);
 605 }
 606 
 607 /*
 608  * MDB module linkage information:
 609  *
 610  * We declare a list of structures describing our dcmds, and a function
 611  * named _mdb_init to return a pointer to our module information.
 612  */
 613 
 614 static const mdb_dcmd_t dcmds[] = {
 615         { "uhci_td", ": [-d]", "print UHCI TD", uhci_td, NULL },
 616         { "uhci_qh", ": [-bd]", "print UHCI QH", uhci_qh, NULL},
 617         { NULL }
 618 };
 619 
 620 
 621 static const mdb_walker_t walkers[] = {
 622         { "uhci_td", "walk list of UHCI TD structures",
 623             uhci_td_walk_init, uhci_td_walk_step, NULL,
 624             NULL },
 625         { "uhci_qh", "walk list of UHCI QH structures",
 626             uhci_qh_walk_init, uhci_qh_walk_step, NULL,
 627             NULL },
 628         { NULL }
 629 };
 630 
 631 static const mdb_modinfo_t modinfo = {
 632         MDB_API_VERSION, dcmds, walkers
 633 };
 634 
 635 
 636 const mdb_modinfo_t *
 637 _mdb_init(void)
 638 {
 639         return (&modinfo);
 640 }