diff options
| author | Robert Mustacchi <rm@joyent.com> | 2016-03-28 04:43:35 +0000 |
|---|---|---|
| committer | Robert Mustacchi <rm@joyent.com> | 2016-12-09 00:17:55 +0000 |
| commit | 1cb3ba547f23532c62f715007a21832205f7fdf7 (patch) | |
| tree | a3891916372f655da53eb47142019aa2a3f6775a /usr/src/cmd/xhci | |
| parent | 83390469eaf76687ae434504ed3e41fdbe4ae3b6 (diff) | |
| download | illumos-joyent-1cb3ba547f23532c62f715007a21832205f7fdf7.tar.gz | |
OS-1151 need support for USB v3.0
OS-5778 want usb_pipe_xopen(9F)
OS-5776 usbai burst macros for endpoint descriptor are wrong
OS-5367 usba_hcdi_register() should fail if driver is using private data
OS-5368 failing to load the usba root hub module destroys driver parent private data
OS-5775 want ::hubd walker
OS-5774 ::prtusb should include version
OS-5777 usb_*_request(9S) manual pages should match structure names
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Bryan Cantrill <bryan@joyent.com>
Approved by: Dave Pacheco <dap@joyent.com>
Diffstat (limited to 'usr/src/cmd/xhci')
| -rw-r--r-- | usr/src/cmd/xhci/Makefile | 43 | ||||
| -rw-r--r-- | usr/src/cmd/xhci/xhci_portsc.c | 373 |
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); +} |
