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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stddef.h>
29 #include <sys/mdb_modapi.h>
30 #include <mdb/mdb_ks.h>
31 
32 #include <sys/usb/usba.h>
33 #include <sys/ddi_impldefs.h>
34 #include <sys/usb/usba/usba_types.h>
35 #include <sys/usb/usba/usba_impl.h>
36 #include <sys/usb/usba/hcdi_impl.h>
37 #include <sys/file.h>
38 #include <sys/sunndi.h>
39 #include <unistd.h>
40 
41 
42 /*
43  * Prototypes
44  */
45 /* usba.c */
46 extern uintptr_t mdb_usba_get_usba_device(uintptr_t);
47 extern uintptr_t mdb_usba_hcdi_get_hcdi(struct dev_info *);
48 
49 /*
50  * Defines
51  */
52 /* dcmd options */
53 #define	USB_DUMP_VERBOSE	0x01
54 #define	USB_DUMP_ACTIVE_PIPES	0x02
55 
56 /* Hardcoded slop factor designed into debug buf logic */
57 #define	USB_DEBUG_SIZE_EXTRA_ALLOC 8
58 
59 
60 /*
61  * Callback arg struct for find_dip (callback func used in usba_device2devinfo).
62  */
63 typedef struct usba_device2devinfo_data {
64 	uintptr_t	u2d_target_usb_dev_p;	/* one we're looking for */
65 	uintptr_t	*u2d_dip_addr;		/* Where to store result */
66 	boolean_t	u2d_found;		/* Match found */
67 } usba_device2devinfo_cbdata_t;
68 
69 
70 /*
71  * Callback for usba_device2dip.
72  * Callback called from the devinfo_children walk invoked in usba_device2dip.
73  *
74  * For the current dip, get the (potential) pointer to its usba_device_t
75  * struct.
76  * See if this pointer matches the address of the usba_device_t we're looking
77  * for (passed in as usb_dev_p).  If so, stuff its value in u2d_dip_addr,
78  * and terminate the walk.
79  *
80  * - dip_addr is the address in core of the dip currently being processed by the
81  * walk
82  * - local_dip is a pointer to a copy of the struct dev_info in local memory
83  * - cb_data is the addr of the callback arg the walker was invoked with
84  * (passed through transparently from walk invoker).
85  *
86  * Returns:
87  * - WALK_NEXT on success (match not found yet)
88  * - WALK_ERR on errors.
89  * - WALK_DONE is returned, cb_data.found is set to TRUE, and
90  * *cb_data.u2d_dip_addr is set to the matched dip addr if a dip corresponding
91  * to the desired usba_device_t* is found.
92  */
93 /*ARGSUSED*/
94 static int
95 find_dip(uintptr_t dip_addr, const void *local_dip, void *cb_arg)
96 {
97 	uintptr_t			cur_usb_dev;
98 	usba_device2devinfo_cbdata_t	*cb_data =
99 			    (usba_device2devinfo_cbdata_t *)cb_arg;
100 
101 	if ((cur_usb_dev = mdb_usba_get_usba_device(dip_addr)) == NULL) {
102 		/*
103 		 * If there's no corresponding usba_device_t, this dip isn't
104 		 * a usb node.  Might be an sd node.  Ignore it.
105 		 */
106 
107 		return (WALK_NEXT);
108 	}
109 
110 	if (cur_usb_dev == cb_data->u2d_target_usb_dev_p) {
111 		*cb_data->u2d_dip_addr = dip_addr;
112 		cb_data->u2d_found = TRUE;
113 
114 		return (WALK_DONE);
115 	}
116 
117 	return (WALK_NEXT);
118 }
119 
120 
121 /*
122  * Given a usba_device pointer, figure out which dip is associated with it.
123  * Relies on usba_device.usb_root_hub_dip being accurate.
124  *
125  * - usb_dev_addr is a pointer to a usba_device_t in core.
126  * - dip_addr is the address of a uintptr_t to receive the address in core
127  * of the found dip (if any).
128  *
129  * Returns:
130  *  0 on success (no match found)
131  *  1 on success (match found)
132  * -1 on errors.
133  */
134 static int
135 usba_device2dip(uintptr_t usb_dev_addr, uintptr_t *dip_addr)
136 {
137 	usba_device_t			usb_dev;
138 	usba_device2devinfo_cbdata_t	cb_data;
139 
140 	/*
141 	 * Walk all USB children of the root hub devinfo.
142 	 * The callback func looks for a match on the usba_device address.
143 	 */
144 	cb_data.u2d_target_usb_dev_p = usb_dev_addr;
145 	cb_data.u2d_dip_addr = dip_addr;
146 	cb_data.u2d_found = FALSE;
147 
148 	if (mdb_vread(&usb_dev, sizeof (usba_device_t),
149 	    usb_dev_addr) == -1) {
150 		mdb_warn("failed to read usba_device struct");
151 
152 		return (-1);
153 	}
154 
155 	/*
156 	 * Walk devinfo children starting with the root hub node,
157 	 * looking for a match on the usba_device pointer (which is what
158 	 * find_dip does).
159 	 * Result is placed in cb_data.dip_addr.
160 	 */
161 	if (mdb_pwalk("devinfo_children", find_dip, &cb_data,
162 	    (uintptr_t)usb_dev.usb_root_hub_dip) != 0) {
163 		mdb_warn("failed to walk devinfo_children");
164 
165 		return (-1);
166 	}
167 
168 	if (cb_data.u2d_found == TRUE) {
169 
170 		return (1);
171 	}
172 
173 	return (0);
174 }
175 
176 
177 /*
178  * Generic walker usba_list_entry_t walker.
179  * Works for any usba_list_entry_t list.
180  */
181 int
182 usba_list_walk_init(mdb_walk_state_t *wsp)
183 {
184 	/* Must have a start addr.  */
185 	if (wsp->walk_addr == NULL) {
186 		mdb_warn("not a global walk.  Starting address required\n");
187 
188 		return (WALK_ERR);
189 	}
190 
191 	return (WALK_NEXT);
192 }
193 
194 
195 /*
196  * Generic list walker step routine.
197  * NOTE: multiple walkers share this routine.
198  */
199 int
200 usba_list_walk_step(mdb_walk_state_t *wsp)
201 {
202 	int			status;
203 	usba_list_entry_t	list_entry;
204 
205 	if (mdb_vread(&list_entry, sizeof (usba_list_entry_t),
206 	    (uintptr_t)wsp->walk_addr) == -1) {
207 		mdb_warn("failed to read usba_list_entry_t at %p",
208 		    wsp->walk_addr);
209 
210 		return (WALK_ERR);
211 	}
212 
213 	status = wsp->walk_callback(wsp->walk_addr, &list_entry,
214 	    wsp->walk_cbdata);
215 	wsp->walk_addr = (uintptr_t)list_entry.next;
216 
217 	/* Check if we're at the last element */
218 	if (wsp->walk_addr == NULL) {
219 
220 		return (WALK_DONE);
221 	}
222 
223 	return (status);
224 }
225 
226 
227 /*
228  * usb_pipe_handle walker
229  * Given a pointer to a usba_device_t, walk the array of endpoint
230  * pipe_handle lists.
231  * For each list, traverse the list, invoking the callback on each element.
232  *
233  * Note this function takes the address of a usba_device struct (which is
234  * easily obtainable), but actually traverses a sub-portion of the struct
235  * (which address is not so easily obtainable).
236  */
237 int
238 usb_pipe_handle_walk_init(mdb_walk_state_t *wsp)
239 {
240 	if (wsp->walk_addr == NULL) {
241 		mdb_warn("not a global walk; usba_device_t required\n");
242 
243 		return (WALK_ERR);
244 	}
245 
246 	wsp->walk_data = mdb_alloc((sizeof (usba_ph_impl_t)) * USBA_N_ENDPOINTS,
247 					UM_SLEEP | UM_GC);
248 
249 	/*
250 	 * Read the usb_ph_list array into local memory.
251 	 * Set start address to first element/endpoint in usb_pipehandle_list
252 	 */
253 	if (mdb_vread(wsp->walk_data,
254 	    (sizeof (usba_ph_impl_t)) * USBA_N_ENDPOINTS,
255 	    (uintptr_t)((size_t)(wsp->walk_addr) +
256 	    offsetof(usba_device_t, usb_ph_list))) == -1) {
257 		mdb_warn("failed to read usb_pipehandle_list at %p",
258 		    wsp->walk_addr);
259 
260 		return (WALK_ERR);
261 	}
262 
263 	wsp->walk_arg = 0;
264 
265 	return (WALK_NEXT);
266 }
267 
268 
269 int
270 usb_pipe_handle_walk_step(mdb_walk_state_t *wsp)
271 {
272 	int status;
273 	usba_ph_impl_t *impl_list = (usba_ph_impl_t *)(wsp->walk_data);
274 	intptr_t index = (intptr_t)wsp->walk_arg;
275 
276 	/* Find the first valid endpoint, starting from where we left off. */
277 	while ((index < USBA_N_ENDPOINTS) &&
278 	    (impl_list[index].usba_ph_data == NULL)) {
279 		index++;
280 	}
281 
282 	/* No more valid endpoints. */
283 	if (index >= USBA_N_ENDPOINTS) {
284 
285 		return (WALK_DONE);
286 	}
287 
288 	status = wsp->walk_callback((uintptr_t)impl_list[index].usba_ph_data,
289 					wsp->walk_data, wsp->walk_cbdata);
290 
291 	/* Set up to start at next pipe handle next time. */
292 	wsp->walk_arg = (void *)(index + 1);
293 
294 	return (status);
295 }
296 
297 
298 /*
299  * Given the address of a usba_pipe_handle_data_t, dump summary info.
300  */
301 /*ARGSUSED*/
302 int
303 usb_pipe_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
304 {
305 	char			*dir, *type, *state;
306 	usb_ep_descr_t		ept_descr;
307 	usba_pipe_handle_data_t	pipe_handle;
308 	usba_ph_impl_t		ph_impl;
309 
310 	if (!(flags & DCMD_ADDRSPEC)) {
311 
312 		return (DCMD_USAGE);
313 	}
314 
315 	if (mdb_vread(&pipe_handle,
316 	    sizeof (usba_pipe_handle_data_t), addr) == -1) {
317 		mdb_warn("failed to read pipe handle at %p", addr);
318 
319 		return (DCMD_ERR);
320 	}
321 
322 	if (mdb_vread(&ph_impl, sizeof (usba_ph_impl_t),
323 	    (uintptr_t)pipe_handle.p_ph_impl) == -1) {
324 		state = "*******";
325 	} else {
326 		switch (ph_impl.usba_ph_state) {
327 		case USB_PIPE_STATE_CLOSED:
328 			state = "CLOSED ";
329 			break;
330 
331 		case USB_PIPE_STATE_IDLE:
332 			state = "IDLE   ";
333 			break;
334 
335 		case USB_PIPE_STATE_ACTIVE:
336 			state = "ACTIVE ";
337 			break;
338 
339 		case USB_PIPE_STATE_ERROR:
340 			state = "ERROR  ";
341 			break;
342 
343 		case USB_PIPE_STATE_CLOSING:
344 			state = "CLOSING";
345 			break;
346 
347 		default:
348 			state = "ILLEGAL";
349 			break;
350 		}
351 	}
352 
353 	bcopy(&pipe_handle.p_ep, &ept_descr, sizeof (usb_ep_descr_t));
354 
355 	if (DCMD_HDRSPEC(flags)) {
356 		mdb_printf("\n    %<u>%-3s %5s %3s %7s %-?s %-?s %-?s%</u>\n",
357 		    "EP", "TYPE ", "DIR", "STATE  ", "P_HANDLE", "P_POLICY",
358 		    "EP DESCR");
359 	}
360 
361 	dir = ((ept_descr.bEndpointAddress & USB_EP_DIR_MASK) &
362 	    USB_EP_DIR_IN) ? "In " : "Out";
363 	switch (ept_descr.bmAttributes & USB_EP_ATTR_MASK) {
364 	case USB_EP_ATTR_CONTROL:
365 		type = "Cntrl";
366 		break;
367 
368 	case USB_EP_ATTR_ISOCH:
369 		type = "Isoch";
370 		break;
371 
372 	case USB_EP_ATTR_BULK:
373 		type = "Bulk ";
374 		break;
375 
376 	case USB_EP_ATTR_INTR:
377 		type = "Intr ";
378 		break;
379 
380 	default:
381 		type = "*****";
382 		break;
383 	}
384 
385 	mdb_printf("    %3d %5s %3s %7s %-?p %-?p %-?p\n",
386 	    ept_descr.bEndpointAddress & USB_EP_NUM_MASK, type, dir, state,
387 	    addr, addr + offsetof(usba_pipe_handle_data_t, p_policy),
388 	    addr + offsetof(usba_pipe_handle_data_t, p_ep));
389 
390 	return (DCMD_OK);
391 }
392 
393 
394 /*
395  * usba_device walker:
396  *
397  * walks the chain of usba_device structs headed by usba_device_list in usba.c
398  * NOTE: It uses the generic list walk step routine usba_list_walk_step.
399  * No walk_fini routine is needed.
400  */
401 int
402 usba_device_walk_init(mdb_walk_state_t *wsp)
403 {
404 	usba_list_entry_t	list_entry;
405 
406 	if (wsp->walk_addr != NULL) {
407 		mdb_warn(
408 		    "global walk only.  Must be invoked without an address\n");
409 
410 		return (WALK_ERR);
411 	}
412 
413 	if (mdb_readvar(&list_entry, "usba_device_list") == -1) {
414 		mdb_warn("failed to read usba_device_list");
415 
416 		return (WALK_ERR);
417 	}
418 
419 	/* List head is not part of usba_device_t, get first usba_device_t */
420 	wsp->walk_addr = (uintptr_t)list_entry.next;
421 
422 	return (WALK_NEXT);
423 }
424 
425 
426 /*
427  * usba_device dcmd
428  *	Given the address of a usba_device struct, dump summary info
429  *	-v:	Print more (verbose) info
430  *	-p:	Walk/dump all open pipes for this usba_device
431  */
432 /*ARGSUSED*/
433 int
434 usba_device(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
435 {
436 	int		status;
437 	char		pathname[MAXNAMELEN];
438 	char		dname[MODMAXNAMELEN + 1] = "<unatt>"; /* Driver name */
439 	char		drv_statep[MODMAXNAMELEN+ 10];
440 	uint_t		usb_flag  = NULL;
441 	boolean_t	no_driver_attached = FALSE;
442 	uintptr_t	dip_addr;
443 	struct dev_info	devinfo;
444 
445 	if (!(flags & DCMD_ADDRSPEC)) {
446 		/* Global walk */
447 		if (mdb_walk_dcmd("usba_device", "usba_device", argc,
448 		    argv) == -1) {
449 			mdb_warn("failed to walk usba_device");
450 
451 			return (DCMD_ERR);
452 		}
453 
454 		return (DCMD_OK);
455 	}
456 
457 	if (mdb_getopts(argc, argv,
458 	    'p', MDB_OPT_SETBITS, USB_DUMP_ACTIVE_PIPES, &usb_flag,
459 	    'v', MDB_OPT_SETBITS, USB_DUMP_VERBOSE, &usb_flag, NULL) != argc) {
460 
461 		return (DCMD_USAGE);
462 	}
463 
464 	if (usb_flag && !(DCMD_HDRSPEC(flags))) {
465 		mdb_printf("\n");
466 	}
467 
468 	if (DCMD_HDRSPEC(flags)) {
469 		mdb_printf("%<u>%-15s %4s %-?s %-42s%</u>\n",
470 		    "NAME", "INST", "DIP", "PATH                             ");
471 	}
472 
473 	status = usba_device2dip(addr, &dip_addr);
474 	/*
475 	 * -1 = error
476 	 * 0 = no error, no match
477 	 * 1 = no error, match
478 	 */
479 	if (status != 1) {
480 		if (status == -1) {
481 			mdb_warn("error looking for dip for usba_device %p",
482 			    addr);
483 		} else {
484 			mdb_warn("failed to find dip for usba_device %p\n",
485 			    addr);
486 		}
487 		mdb_warn("dip and statep unobtainable\n");
488 
489 		return (DCMD_ERR);
490 	}
491 
492 	/* Figure out what driver (name) is attached to this node. */
493 	(void) mdb_devinfo2driver(dip_addr, (char *)dname, sizeof (dname));
494 
495 	if (mdb_vread(&devinfo, sizeof (struct dev_info),
496 	    dip_addr) == -1) {
497 		mdb_warn("failed to read devinfo");
498 
499 		return (DCMD_ERR);
500 	}
501 
502 	if (!(DDI_CF2(&devinfo))) {
503 		no_driver_attached = TRUE;
504 	}
505 
506 	(void) mdb_ddi_pathname(dip_addr, pathname, sizeof (pathname));
507 	mdb_printf("%-15s %2d   %-?p %s\n", dname, devinfo.devi_instance,
508 	    dip_addr, pathname);
509 
510 	if (usb_flag & USB_DUMP_VERBOSE) {
511 		int		i;
512 		uintptr_t	statep = NULL;
513 		char		*string_descr;
514 		char		**config_cloud, **conf_str_descr;
515 		usb_dev_descr_t	usb_dev_descr;
516 		usba_device_t	usba_device_struct;
517 
518 		if (mdb_vread(&usba_device_struct,
519 		    sizeof (usba_device_t), addr) == -1) {
520 			mdb_warn("failed to read usba_device struct");
521 
522 			return (DCMD_ERR);
523 		}
524 
525 		mdb_printf("    usba_device: %-16p\n\n", (usba_device_t *)addr);
526 
527 		if (mdb_vread(&usb_dev_descr, sizeof (usb_dev_descr),
528 		    (uintptr_t)usba_device_struct.usb_dev_descr) == -1) {
529 			mdb_warn("failed to read usb_dev_descr_t struct");
530 
531 			return (DCMD_ERR);
532 		}
533 
534 		mdb_printf("\n    idVendor: 0x%04x idProduct: 0x%04x "
535 		    "usb_addr: 0x%02x\n", usb_dev_descr.idVendor,
536 		    usb_dev_descr.idProduct, usba_device_struct.usb_addr);
537 
538 		/* Get the string descriptor string into local space. */
539 		string_descr = (char *)mdb_alloc(USB_MAXSTRINGLEN, UM_GC);
540 
541 		if (usba_device_struct.usb_mfg_str == NULL) {
542 			(void) strcpy(string_descr, "<No Manufacturer String>");
543 		} else {
544 			if (mdb_readstr(string_descr, USB_MAXSTRINGLEN,
545 			    (uintptr_t)usba_device_struct.usb_mfg_str) == -1) {
546 				mdb_warn("failed to read manufacturer "
547 				    "string descriptor");
548 				(void) strcpy(string_descr, "???");
549 			}
550 		}
551 		mdb_printf("\n    Manufacturer String:\t%s\n", string_descr);
552 
553 		if (usba_device_struct.usb_product_str == NULL) {
554 			(void) strcpy(string_descr, "<No Product String>");
555 		} else {
556 			if (mdb_readstr(string_descr, USB_MAXSTRINGLEN,
557 			    (uintptr_t)usba_device_struct.usb_product_str) ==
558 			    -1) {
559 				mdb_warn("failed to read product string "
560 				    "descriptor");
561 				(void) strcpy(string_descr, "???");
562 			}
563 		}
564 		mdb_printf("    Product String:\t\t%s\n", string_descr);
565 
566 		if (usba_device_struct.usb_serialno_str == NULL) {
567 			(void) strcpy(string_descr, "<No SerialNumber String>");
568 		} else {
569 			if (mdb_readstr(string_descr, USB_MAXSTRINGLEN,
570 			    (uintptr_t)usba_device_struct.usb_serialno_str) ==
571 			    -1) {
572 				mdb_warn("failed to read serial number string "
573 				    "descriptor");
574 				(void) strcpy(string_descr, "???");
575 			}
576 		}
577 		mdb_printf("    SerialNumber String:\t%s\n", string_descr);
578 
579 		if (no_driver_attached) {
580 			mdb_printf("\n");
581 		} else {
582 			mdb_printf("      state_p: ");
583 
584 			/*
585 			 * Given the dip, find the associated statep. The
586 			 * convention to generate this soft state anchor is:
587 			 *	<driver_name>_statep
588 			 */
589 			(void) mdb_snprintf(drv_statep, sizeof (drv_statep),
590 			    "%s_statep", dname);
591 			if (mdb_devinfo2statep(dip_addr, drv_statep,
592 			    &statep) == -1) {
593 				mdb_warn("failed to find %s state struct for "
594 				    "dip %p", drv_statep, dip_addr);
595 
596 				return (DCMD_ERR);
597 			}
598 			mdb_printf("%-?p\n", statep);
599 		}
600 
601 		config_cloud = (char **)mdb_alloc(sizeof (void *) *
602 		    usba_device_struct.usb_n_cfgs, UM_GC);
603 
604 		conf_str_descr = (char **)mdb_alloc(sizeof (void *) *
605 		    usba_device_struct.usb_n_cfgs, UM_GC);
606 
607 		if ((usba_device_struct.usb_cfg_array) &&
608 		    (usba_device_struct.usb_cfg_str_descr)) {
609 			if ((mdb_vread(config_cloud,  sizeof (void *) *
610 			    usba_device_struct.usb_n_cfgs,
611 			    (uintptr_t)usba_device_struct.usb_cfg_array) ==
612 			    -1) || (mdb_vread(conf_str_descr, sizeof (void *)
613 			    * usba_device_struct.usb_n_cfgs, (uintptr_t)
614 			    usba_device_struct.usb_cfg_str_descr)) == -1) {
615 
616 			    mdb_warn("failed to read config cloud pointers");
617 
618 			} else {
619 
620 				mdb_printf("\n    Device Config Clouds:\n"
621 				    "    Index\tConfig\t\tConfiguration "
622 				    "String\n"
623 				    "    -----\t------\t\t"
624 				    "--------------------\n");
625 
626 				for (i = 0; i < usba_device_struct.usb_n_cfgs;
627 				    i++) {
628 					if (mdb_readstr(string_descr,
629 					    USB_MAXSTRINGLEN,
630 					    (uintptr_t)conf_str_descr[i]) ==
631 					    -1) {
632 						(void) strcpy(string_descr,
633 						    "<No Configuration "
634 						    "String>");
635 					}
636 					mdb_printf("    %4d\t0x%p\t%s\n", i,
637 					    config_cloud[i], string_descr);
638 				}
639 			}
640 		}
641 
642 		mdb_printf("\n    Active configuration index: %d\n",
643 		    usba_device_struct.usb_active_cfg_ndx);
644 	}
645 
646 	if (usb_flag & USB_DUMP_ACTIVE_PIPES) {
647 
648 		if (mdb_pwalk_dcmd("usb_pipe_handle", "usb_pipe_handle",
649 		    0, NULL, addr) == -1) {
650 			mdb_warn("failed to walk usb_pipe_handle");
651 			return (DCMD_ERR);
652 		}
653 	}
654 
655 	return (DCMD_OK);
656 }
657 
658 
659 /*
660  * Dump the contents of the usba_debug_buf, from the oldest to newest,
661  * wrapping around if necessary.
662  */
663 /*ARGSUSED*/
664 int
665 usba_debug_buf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
666 {
667 	char	*debug_buf_addr;	/* addr in core */
668 	char	*local_debug_buf;	/* local copy of buf */
669 	int	debug_buf_size;
670 	char	*term_p;
671 	int	being_cleared;
672 
673 	if (flags & DCMD_ADDRSPEC) {
674 
675 		return (DCMD_USAGE);
676 	}
677 
678 	if (mdb_readvar(&being_cleared, "usba_clear_debug_buf_flag") ==
679 	    -1) {
680 		mdb_warn("failed to read usba_clear_debug_buf_flag");
681 
682 		return (DCMD_ERR);
683 	}
684 	if (being_cleared) {
685 
686 		return (DCMD_OK);
687 	}
688 
689 	if (mdb_readvar(&debug_buf_addr, "usba_debug_buf") == -1) {
690 		mdb_warn("failed to read usba_debug_buf");
691 
692 		return (DCMD_ERR);
693 	}
694 
695 	if (debug_buf_addr == NULL) {
696 		mdb_warn("usba_debug_buf not allocated\n");
697 
698 		return (DCMD_OK);
699 	}
700 
701 
702 	if (mdb_readvar(&debug_buf_size, "usba_debug_buf_size") == -1) {
703 		mdb_warn("failed to read usba_debug_buf_size");
704 
705 		return (DCMD_ERR);
706 	}
707 
708 	debug_buf_size += USB_DEBUG_SIZE_EXTRA_ALLOC;
709 	local_debug_buf = (char *)mdb_alloc(debug_buf_size, UM_SLEEP | UM_GC);
710 
711 	if ((mdb_vread(local_debug_buf, debug_buf_size,
712 	    (uintptr_t)debug_buf_addr)) == -1) {
713 		mdb_warn("failed to read usba_debug_buf at %p",
714 		    local_debug_buf);
715 
716 		return (DCMD_ERR);
717 	}
718 	local_debug_buf[debug_buf_size - 1] = '\0';
719 
720 	if (strlen(local_debug_buf) == NULL) {
721 
722 		return (DCMD_OK);
723 	}
724 
725 	if ((term_p = strstr(local_debug_buf, ">>>>")) == NULL) {
726 		mdb_warn("failed to find terminator \">>>>\"\n");
727 
728 		return (DCMD_ERR);
729 	}
730 
731 	/*
732 	 * Print the chunk of buffer from the terminator to the end.
733 	 * This will print a null string if no wrap has occurred yet.
734 	 */
735 	mdb_printf("%s", term_p+5);	/* after >>>>\0 to end of buf */
736 	mdb_printf("%s\n", local_debug_buf);	/* beg of buf to >>>>\0 */
737 
738 	return (DCMD_OK);
739 }
740 
741 /*ARGSUSED*/
742 int
743 usba_clear_debug_buf(
744 	uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
745 {
746 	int clear = 1;
747 
748 	/* stop the tracing */
749 	if (mdb_writevar((void*)&clear, "usba_clear_debug_buf_flag") == -1) {
750 		mdb_warn("failed to set usba_clear_debug_buf_flag");
751 
752 		return (DCMD_ERR);
753 	}
754 
755 	return (DCMD_OK);
756 }
757 
758 /* prtusb entries */
759 extern int prtusb(uintptr_t, uint_t, int, const mdb_arg_t *);
760 
761 extern void prt_usb_usage(void);
762 
763 /*
764  * MDB module linkage information:
765  *
766  * We declare a list of structures describing our dcmds, and a function
767  * named _mdb_init to return a pointer to our module information.
768  */
769 static const mdb_dcmd_t dcmds[] = {
770 	{ "usb_pipe_handle", ":",
771 	    "print a usb_pipe_handle struct", usb_pipe_handle, NULL},
772 	{ "usba_device", ": [-pv]",
773 	    "print summary info for a usba_device_t struct", usba_device, NULL},
774 	{ "usba_debug_buf", NULL,
775 	    "print usba_debug_buf", usba_debug_buf, NULL},
776 	{ "usba_clear_debug_buf", NULL,
777 	    "clear usba_debug_buf", usba_clear_debug_buf, NULL},
778 	{ "prtusb", ": [-t] [-v] [-i No.]",
779 	    "Print usb tree and descriptors", prtusb, prt_usb_usage},
780 	{ NULL }
781 };
782 
783 static const mdb_walker_t walkers[] = {
784 	/* Generic list walker. */
785 	{ "usba_list_entry", "walk list of usba_list_entry_t structures",
786 	    usba_list_walk_init, usba_list_walk_step, NULL, NULL },
787 	{ "usb_pipe_handle", "walk USB pipe handles, given a usba_device_t ptr",
788 	    usb_pipe_handle_walk_init, usb_pipe_handle_walk_step, NULL, NULL },
789 	{ "usba_device", "walk global list of usba_device_t structures",
790 	    usba_device_walk_init, usba_list_walk_step, NULL, NULL },
791 	{ NULL }
792 };
793 
794 static const mdb_modinfo_t modinfo = {
795 	MDB_API_VERSION, dcmds, walkers
796 };
797 
798 const mdb_modinfo_t *
799 _mdb_init(void)
800 {
801 	return (&modinfo);
802 }
803