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 }
641