summaryrefslogtreecommitdiff
path: root/usr/src/cmd/xhci
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/xhci')
-rw-r--r--usr/src/cmd/xhci/Makefile43
-rw-r--r--usr/src/cmd/xhci/xhci_portsc.c373
2 files changed, 416 insertions, 0 deletions
diff --git a/usr/src/cmd/xhci/Makefile b/usr/src/cmd/xhci/Makefile
new file mode 100644
index 0000000000..b0282ca5f9
--- /dev/null
+++ b/usr/src/cmd/xhci/Makefile
@@ -0,0 +1,43 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Joyent, Inc.
+#
+
+PROG= xhci_portsc
+
+ROOTLIBXHCI = $(ROOTLIB)/xhci
+ROOTLIBXHCIPROG = $(PROG:%=$(ROOTLIBXHCI)/%)
+
+include ../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(SRC)/uts/common/
+LDLIBS += -ldevinfo
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTLIBXHCIPROG)
+
+clean:
+
+lint: lint_PROG
+
+$(ROOTLIBXHCI):
+ $(INS.dir)
+
+$(ROOTLIBXHCI)/%: % $(ROOTLIBXHCI)
+ $(INS.file)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/xhci/xhci_portsc.c b/usr/src/cmd/xhci/xhci_portsc.c
new file mode 100644
index 0000000000..a656f76950
--- /dev/null
+++ b/usr/src/cmd/xhci/xhci_portsc.c
@@ -0,0 +1,373 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * This is a private utility that combines a number of minor debugging routines
+ * for xhci.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libdevinfo.h>
+#include <sys/usb/hcd/xhci/xhci_ioctl.h>
+#include <sys/usb/hcd/xhci/xhcireg.h>
+
+static char *xp_devpath = NULL;
+static int xp_npaths;
+static const char *xp_path;
+static const char *xp_state = NULL;
+static uint32_t xp_port;
+static boolean_t xp_verbose = B_FALSE;
+static boolean_t xp_clear = B_FALSE;
+static boolean_t xp_list = B_FALSE;
+extern const char *__progname;
+
+static int
+xp_usage(const char *format, ...)
+{
+ if (format != NULL) {
+ va_list alist;
+
+ va_start(alist, format);
+ vwarnx(format, alist);
+ va_end(alist);
+ }
+
+ (void) fprintf(stderr, "usage: %s [-l] [-v] [-c] [-d path] [-p port] "
+ "[-s state]\n", __progname);
+ return (2);
+}
+
+static const char *xp_pls_strings[] = {
+ "U0",
+ "U1",
+ "U2",
+ "U3 (suspended)",
+ "Disabled",
+ "RxDetect",
+ "Inactive",
+ "Polling",
+ "Recovery",
+ "Hot Reset",
+ "Compliance Mode",
+ "Test Mode",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Resume",
+ NULL
+};
+
+static void
+xp_dump_verbose(uint32_t portsc)
+{
+ if (portsc & XHCI_PS_CCS)
+ (void) printf("\t\t\tCCS\n");
+ if (portsc & XHCI_PS_PED)
+ (void) printf("\t\t\tPED\n");
+ if (portsc & XHCI_PS_OCA)
+ (void) printf("\t\t\tOCA\n");
+ if (portsc & XHCI_PS_PR)
+ (void) printf("\t\t\tPR\n");
+ if (portsc & XHCI_PS_PP) {
+ (void) printf("\t\t\tPLS: %s (%d)\n",
+ xp_pls_strings[XHCI_PS_PLS_GET(portsc)],
+ XHCI_PS_PLS_GET(portsc));
+ (void) printf("\t\t\tPP\n");
+ } else {
+ (void) printf("\t\t\tPLS: undefined (No PP)\n");
+ }
+
+ if (XHCI_PS_SPEED_GET(portsc) != 0) {
+ (void) printf("\t\t\tPort Speed: ");
+ switch (XHCI_PS_SPEED_GET(portsc)) {
+ case 0:
+ (void) printf("Undefined ");
+ break;
+ case XHCI_SPEED_FULL:
+ (void) printf("Full ");
+ break;
+ case XHCI_SPEED_LOW:
+ (void) printf("Low ");
+ break;
+ case XHCI_SPEED_HIGH:
+ (void) printf("High ");
+ break;
+ case XHCI_SPEED_SUPER:
+ (void) printf("Super ");
+ break;
+ default:
+ (void) printf("Unknown ");
+ break;
+ }
+ (void) printf("(%d)\n", XHCI_PS_SPEED_GET(portsc));
+ }
+ if (XHCI_PS_PIC_GET(portsc) != 0)
+ (void) printf("\t\t\tPIC: %d\n", XHCI_PS_PIC_GET(portsc));
+
+ if (portsc & XHCI_PS_LWS)
+ (void) printf("\t\t\tLWS\n");
+ if (portsc & XHCI_PS_CSC)
+ (void) printf("\t\t\tCSC\n");
+ if (portsc & XHCI_PS_PEC)
+ (void) printf("\t\t\tPEC\n");
+ if (portsc & XHCI_PS_WRC)
+ (void) printf("\t\t\tWRC\n");
+ if (portsc & XHCI_PS_OCC)
+ (void) printf("\t\t\tOCC\n");
+ if (portsc & XHCI_PS_PRC)
+ (void) printf("\t\t\tPRC\n");
+ if (portsc & XHCI_PS_PLC)
+ (void) printf("\t\t\tPLC\n");
+ if (portsc & XHCI_PS_CEC)
+ (void) printf("\t\t\tCEC\n");
+ if (portsc & XHCI_PS_CAS)
+ (void) printf("\t\t\tCAS\n");
+ if (portsc & XHCI_PS_WCE)
+ (void) printf("\t\t\tWCE\n");
+ if (portsc & XHCI_PS_WDE)
+ (void) printf("\t\t\tWDE\n");
+ if (portsc & XHCI_PS_WOE)
+ (void) printf("\t\t\tWOE\n");
+ if (portsc & XHCI_PS_DR)
+ (void) printf("\t\t\tDR\n");
+ if (portsc & XHCI_PS_WPR)
+ (void) printf("\t\t\tWPR\n");
+}
+
+static void
+xp_dump(const char *path)
+{
+ int fd, i;
+ xhci_ioctl_portsc_t xhi = { 0 };
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "failed to open %s", path);
+ }
+
+ if (ioctl(fd, XHCI_IOCTL_PORTSC, &xhi) != 0)
+ err(EXIT_FAILURE, "failed to get port status");
+
+ (void) close(fd);
+
+ for (i = 1; i <= xhi.xhi_nports; i++) {
+ if (xp_port != 0 && i != xp_port)
+ continue;
+
+ (void) printf("port %2d:\t0x%08x\n", i, xhi.xhi_portsc[i]);
+ if (xp_verbose == B_TRUE)
+ xp_dump_verbose(xhi.xhi_portsc[i]);
+ }
+}
+
+static void
+xp_set_pls(const char *path, uint32_t port, const char *state)
+{
+ int fd, i;
+ xhci_ioctl_setpls_t xis;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "failed to open %s", path);
+ }
+
+ xis.xis_port = port;
+ for (i = 0; xp_pls_strings[i] != NULL; i++) {
+ if (strcasecmp(state, xp_pls_strings[i]) == 0)
+ break;
+ }
+
+ if (xp_pls_strings[i] == NULL) {
+ errx(EXIT_FAILURE, "unknown state string: %s\n", state);
+ }
+
+ xis.xis_pls = i;
+ (void) printf("setting port %d with pls %d\n", port, xis.xis_pls);
+
+ if (ioctl(fd, XHCI_IOCTL_SETPLS, &xis) != 0)
+ err(EXIT_FAILURE, "failed to set port status");
+
+ (void) close(fd);
+}
+
+static void
+xp_clear_change(const char *path, uint32_t port)
+{
+ int fd;
+ xhci_ioctl_clear_t xic;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "failed to open %s", path);
+ }
+
+ xic.xic_port = port;
+ (void) printf("clearing change bits on port %d\n", port);
+ if (ioctl(fd, XHCI_IOCTL_CLEAR, &xic) != 0)
+ err(EXIT_FAILURE, "failed to set port status");
+
+ (void) close(fd);
+}
+
+/* ARGSUSED */
+static int
+xp_devinfo_cb(di_node_t node, void *arg)
+{
+ char *drv;
+ di_minor_t minor;
+ boolean_t *do_print = arg;
+
+ drv = di_driver_name(node);
+ if (drv == NULL)
+ return (DI_WALK_CONTINUE);
+ if (strcmp(drv, "xhci") != 0)
+ return (DI_WALK_CONTINUE);
+
+ /*
+ * We have an instance of the xhci driver. We need to find the minor
+ * node for the hubd instance. These are all usually greater than
+ * HUBD_IS_ROOT_HUB. However, to avoid hardcoding that here, we instead
+ * rely on the fact that the minor node for the actual device has a
+ * :hubd as the intance.
+ */
+ minor = DI_MINOR_NIL;
+ while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
+ char *mname, *path;
+
+ mname = di_minor_name(minor);
+ if (mname == NULL)
+ continue;
+ if (strcmp(mname, "hubd") != 0)
+ continue;
+ path = di_devfs_minor_path(minor);
+ if (*do_print == B_TRUE) {
+ (void) printf("/devices%s\n", path);
+ di_devfs_path_free(path);
+ } else {
+ xp_npaths++;
+ if (xp_devpath == NULL)
+ xp_devpath = path;
+ else
+ di_devfs_path_free(path);
+ }
+ }
+
+ return (DI_WALK_PRUNECHILD);
+}
+
+/*
+ * We need to find all minor nodes of instances of the xhci driver whose name is
+ * 'hubd'.
+ */
+static void
+xp_find_devs(boolean_t print)
+{
+ di_node_t root;
+
+ if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
+ err(EXIT_FAILURE, "failed to initialize devices tree");
+ }
+
+ if (di_walk_node(root, DI_WALK_CLDFIRST, &print, xp_devinfo_cb) != 0)
+ err(EXIT_FAILURE, "failed to walk devices tree");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ char devpath[PATH_MAX];
+
+ while ((c = getopt(argc, argv, ":d:vlcp:s:")) != -1) {
+ switch (c) {
+ case 'c':
+ xp_clear = B_TRUE;
+ break;
+ case 'd':
+ xp_path = optarg;
+ break;
+ case 'l':
+ xp_list = B_TRUE;
+ break;
+ case 'v':
+ xp_verbose = B_TRUE;
+ break;
+ case 'p':
+ xp_port = atoi(optarg);
+ if (xp_port < 1 || xp_port > XHCI_PORTSC_NPORTS)
+ return (xp_usage("invalid port for -p: %d\n",
+ optarg));
+ break;
+ case 's':
+ xp_state = optarg;
+ break;
+ case ':':
+ return (xp_usage("-%c requires an operand\n", optopt));
+ case '?':
+ return (xp_usage("unknown option: -%c\n", optopt));
+ default:
+ abort();
+ }
+ }
+
+ if (xp_list == B_TRUE && (xp_path != NULL || xp_clear == B_TRUE ||
+ xp_port > 0 || xp_state != NULL)) {
+ return (xp_usage("-l cannot be used with other options\n"));
+ }
+
+ if (xp_list == B_TRUE) {
+ xp_find_devs(B_TRUE);
+ return (0);
+ }
+
+ if (xp_path == NULL) {
+ xp_find_devs(B_FALSE);
+ if (xp_npaths == 0) {
+ errx(EXIT_FAILURE, "no xhci devices found");
+ } else if (xp_npaths > 1) {
+ errx(EXIT_FAILURE, "more than one xhci device found, "
+ "please specify device with -d, use -l to list");
+ }
+ if (snprintf(devpath, sizeof (devpath), "/devices/%s",
+ xp_devpath) >= sizeof (devpath))
+ errx(EXIT_FAILURE, "xhci path found at %s overflows "
+ "internal device path");
+ di_devfs_path_free(xp_devpath);
+ xp_devpath = NULL;
+ xp_path = devpath;
+ }
+
+ if (xp_clear == B_TRUE && xp_state != NULL) {
+ return (xp_usage("-c and -s can't be used together\n"));
+ }
+
+ if (xp_state != NULL) {
+ xp_set_pls(xp_path, xp_port, xp_state);
+ } else if (xp_clear == B_TRUE) {
+ xp_clear_change(xp_path, xp_port);
+ } else {
+ xp_dump(xp_path);
+ }
+
+ return (0);
+}