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