2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright 2016 Joyent, Inc.
17 * This is a private utility that combines a number of minor debugging routines
27 #include <sys/types.h>
30 #include <libdevinfo.h>
31 #include <sys/usb/hcd/xhci/xhci_ioctl.h>
32 #include <sys/usb/hcd/xhci/xhcireg.h>
34 static char *xp_devpath
= NULL
;
36 static const char *xp_path
;
37 static const char *xp_state
= NULL
;
38 static uint32_t xp_port
;
39 static boolean_t xp_verbose
= B_FALSE
;
40 static boolean_t xp_clear
= B_FALSE
;
41 static boolean_t xp_list
= B_FALSE
;
42 extern const char *__progname
;
45 xp_usage(const char *format
, ...)
50 va_start(alist
, format
);
51 vwarnx(format
, alist
);
55 (void) fprintf(stderr
, "usage: %s [-l] [-v] [-c] [-d path] [-p port] "
56 "[-s state]\n", __progname
);
60 static const char *xp_pls_strings
[] = {
81 xp_dump_verbose(uint32_t portsc
)
83 if (portsc
& XHCI_PS_CCS
)
84 (void) printf("\t\t\tCCS\n");
85 if (portsc
& XHCI_PS_PED
)
86 (void) printf("\t\t\tPED\n");
87 if (portsc
& XHCI_PS_OCA
)
88 (void) printf("\t\t\tOCA\n");
89 if (portsc
& XHCI_PS_PR
)
90 (void) printf("\t\t\tPR\n");
91 if (portsc
& XHCI_PS_PP
) {
92 (void) printf("\t\t\tPLS: %s (%d)\n",
93 xp_pls_strings
[XHCI_PS_PLS_GET(portsc
)],
94 XHCI_PS_PLS_GET(portsc
));
95 (void) printf("\t\t\tPP\n");
97 (void) printf("\t\t\tPLS: undefined (No PP)\n");
100 if (XHCI_PS_SPEED_GET(portsc
) != 0) {
101 (void) printf("\t\t\tPort Speed: ");
102 switch (XHCI_PS_SPEED_GET(portsc
)) {
104 (void) printf("Undefined ");
106 case XHCI_SPEED_FULL
:
107 (void) printf("Full ");
110 (void) printf("Low ");
112 case XHCI_SPEED_HIGH
:
113 (void) printf("High ");
115 case XHCI_SPEED_SUPER
:
116 (void) printf("Super ");
119 (void) printf("Unknown ");
122 (void) printf("(%d)\n", XHCI_PS_SPEED_GET(portsc
));
124 if (XHCI_PS_PIC_GET(portsc
) != 0)
125 (void) printf("\t\t\tPIC: %d\n", XHCI_PS_PIC_GET(portsc
));
127 if (portsc
& XHCI_PS_LWS
)
128 (void) printf("\t\t\tLWS\n");
129 if (portsc
& XHCI_PS_CSC
)
130 (void) printf("\t\t\tCSC\n");
131 if (portsc
& XHCI_PS_PEC
)
132 (void) printf("\t\t\tPEC\n");
133 if (portsc
& XHCI_PS_WRC
)
134 (void) printf("\t\t\tWRC\n");
135 if (portsc
& XHCI_PS_OCC
)
136 (void) printf("\t\t\tOCC\n");
137 if (portsc
& XHCI_PS_PRC
)
138 (void) printf("\t\t\tPRC\n");
139 if (portsc
& XHCI_PS_PLC
)
140 (void) printf("\t\t\tPLC\n");
141 if (portsc
& XHCI_PS_CEC
)
142 (void) printf("\t\t\tCEC\n");
143 if (portsc
& XHCI_PS_CAS
)
144 (void) printf("\t\t\tCAS\n");
145 if (portsc
& XHCI_PS_WCE
)
146 (void) printf("\t\t\tWCE\n");
147 if (portsc
& XHCI_PS_WDE
)
148 (void) printf("\t\t\tWDE\n");
149 if (portsc
& XHCI_PS_WOE
)
150 (void) printf("\t\t\tWOE\n");
151 if (portsc
& XHCI_PS_DR
)
152 (void) printf("\t\t\tDR\n");
153 if (portsc
& XHCI_PS_WPR
)
154 (void) printf("\t\t\tWPR\n");
158 xp_dump(const char *path
)
161 xhci_ioctl_portsc_t xhi
= { 0 };
163 fd
= open(path
, O_RDWR
);
165 err(EXIT_FAILURE
, "failed to open %s", path
);
168 if (ioctl(fd
, XHCI_IOCTL_PORTSC
, &xhi
) != 0)
169 err(EXIT_FAILURE
, "failed to get port status");
173 for (i
= 1; i
<= xhi
.xhi_nports
; i
++) {
174 if (xp_port
!= 0 && i
!= xp_port
)
177 (void) printf("port %2d:\t0x%08x\n", i
, xhi
.xhi_portsc
[i
]);
178 if (xp_verbose
== B_TRUE
)
179 xp_dump_verbose(xhi
.xhi_portsc
[i
]);
184 xp_set_pls(const char *path
, uint32_t port
, const char *state
)
187 xhci_ioctl_setpls_t xis
;
189 fd
= open(path
, O_RDWR
);
191 err(EXIT_FAILURE
, "failed to open %s", path
);
195 for (i
= 0; xp_pls_strings
[i
] != NULL
; i
++) {
196 if (strcasecmp(state
, xp_pls_strings
[i
]) == 0)
200 if (xp_pls_strings
[i
] == NULL
) {
201 errx(EXIT_FAILURE
, "unknown state string: %s\n", state
);
205 (void) printf("setting port %d with pls %d\n", port
, xis
.xis_pls
);
207 if (ioctl(fd
, XHCI_IOCTL_SETPLS
, &xis
) != 0)
208 err(EXIT_FAILURE
, "failed to set port status");
214 xp_clear_change(const char *path
, uint32_t port
)
217 xhci_ioctl_clear_t xic
;
219 fd
= open(path
, O_RDWR
);
221 err(EXIT_FAILURE
, "failed to open %s", path
);
225 (void) printf("clearing change bits on port %d\n", port
);
226 if (ioctl(fd
, XHCI_IOCTL_CLEAR
, &xic
) != 0)
227 err(EXIT_FAILURE
, "failed to set port status");
234 xp_devinfo_cb(di_node_t node
, void *arg
)
238 boolean_t
*do_print
= arg
;
240 drv
= di_driver_name(node
);
242 return (DI_WALK_CONTINUE
);
243 if (strcmp(drv
, "xhci") != 0)
244 return (DI_WALK_CONTINUE
);
247 * We have an instance of the xhci driver. We need to find the minor
248 * node for the hubd instance. These are all usually greater than
249 * HUBD_IS_ROOT_HUB. However, to avoid hardcoding that here, we instead
250 * rely on the fact that the minor node for the actual device has a
251 * :hubd as the intance.
253 minor
= DI_MINOR_NIL
;
254 while ((minor
= di_minor_next(node
, minor
)) != DI_MINOR_NIL
) {
257 mname
= di_minor_name(minor
);
260 if (strcmp(mname
, "hubd") != 0)
262 path
= di_devfs_minor_path(minor
);
263 if (*do_print
== B_TRUE
) {
264 (void) printf("/devices%s\n", path
);
265 di_devfs_path_free(path
);
268 if (xp_devpath
== NULL
)
271 di_devfs_path_free(path
);
275 return (DI_WALK_PRUNECHILD
);
279 * We need to find all minor nodes of instances of the xhci driver whose name is
283 xp_find_devs(boolean_t print
)
287 if ((root
= di_init("/", DINFOCPYALL
)) == DI_NODE_NIL
) {
288 err(EXIT_FAILURE
, "failed to initialize devices tree");
291 if (di_walk_node(root
, DI_WALK_CLDFIRST
, &print
, xp_devinfo_cb
) != 0)
292 err(EXIT_FAILURE
, "failed to walk devices tree");
296 main(int argc
, char *argv
[])
299 char devpath
[PATH_MAX
];
301 while ((c
= getopt(argc
, argv
, ":d:vlcp:s:")) != -1) {
316 xp_port
= atoi(optarg
);
317 if (xp_port
< 1 || xp_port
> XHCI_PORTSC_NPORTS
)
318 return (xp_usage("invalid port for -p: %d\n",
325 return (xp_usage("-%c requires an operand\n", optopt
));
327 return (xp_usage("unknown option: -%c\n", optopt
));
333 if (xp_list
== B_TRUE
&& (xp_path
!= NULL
|| xp_clear
== B_TRUE
||
334 xp_port
> 0 || xp_state
!= NULL
)) {
335 return (xp_usage("-l cannot be used with other options\n"));
338 if (xp_list
== B_TRUE
) {
339 xp_find_devs(B_TRUE
);
343 if (xp_path
== NULL
) {
344 xp_find_devs(B_FALSE
);
345 if (xp_npaths
== 0) {
346 errx(EXIT_FAILURE
, "no xhci devices found");
347 } else if (xp_npaths
> 1) {
348 errx(EXIT_FAILURE
, "more than one xhci device found, "
349 "please specify device with -d, use -l to list");
351 if (snprintf(devpath
, sizeof (devpath
), "/devices/%s",
352 xp_devpath
) >= sizeof (devpath
))
353 errx(EXIT_FAILURE
, "xhci path found at %s overflows "
354 "internal device path");
355 di_devfs_path_free(xp_devpath
);
360 if (xp_clear
== B_TRUE
&& xp_state
!= NULL
) {
361 return (xp_usage("-c and -s can't be used together\n"));
364 if (xp_state
!= NULL
) {
365 xp_set_pls(xp_path
, xp_port
, xp_state
);
366 } else if (xp_clear
== B_TRUE
) {
367 xp_clear_change(xp_path
, xp_port
);