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