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
find_uhci_instance(uintptr_t uhci_instancep,const void * local_ss,void * cb_arg)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
find_uhci_statep(void * td_qh,boolean_t td_or_qh,uhci_state_t * uhci_statep)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
uhci_td(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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 != 0) {
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 == 0)  {
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
uhci_qh(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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 != 0) {
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 != 0) {
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 == 0)  {
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 == 0)  {
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
uhci_td_walk_init(mdb_walk_state_t * wsp)450 uhci_td_walk_init(mdb_walk_state_t *wsp)
451 {
452 	if (wsp->walk_addr == 0)  {
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
uhci_td_walk_step(mdb_walk_state_t * wsp)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 == 0 || 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 == 0)
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
uhci_qh_walk_init(mdb_walk_state_t * wsp)529 uhci_qh_walk_init(mdb_walk_state_t *wsp)
530 {
531 	if (wsp->walk_addr == 0)
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
uhci_qh_walk_step(mdb_walk_state_t * wsp)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 == 0)	/* 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 == 0 || 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 == 0)
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 *
_mdb_init(void)635 _mdb_init(void)
636 {
637 	return (&modinfo);
638 }
639