diff options
author | Robert Mustacchi <rm@joyent.com> | 2019-01-15 21:35:50 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2019-01-28 06:30:32 +0000 |
commit | 672fc84a1840ce8ef60fc752e9ea374723d1135a (patch) | |
tree | c5930c6d028555e52fb42b74fff23794b7f11231 | |
parent | 31b6814e52e5d603c18d8d2143eb2a8a660249c8 (diff) | |
download | illumos-joyent-672fc84a1840ce8ef60fc752e9ea374723d1135a.tar.gz |
10242 Project Tiresias: USB topology
10243 topo_node_label_set() should take a const char* instead of char*
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Rob Johnston <rob.johnston@joyent.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
40 files changed, 3752 insertions, 141 deletions
diff --git a/usr/src/cmd/diskinfo/diskinfo.c b/usr/src/cmd/diskinfo/diskinfo.c index 182cd46a63..36376827fc 100644 --- a/usr/src/cmd/diskinfo/diskinfo.c +++ b/usr/src/cmd/diskinfo/diskinfo.c @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2013 Joyent Inc., All rights reserved. + * Copyright (c) 2018 Joyent Inc., All rights reserved. */ #include <sys/types.h> @@ -137,6 +137,7 @@ disk_walker(topo_hdl_t *hp, tnode_t *np, void *arg) pnp = topo_node_parent(np); ppnp = topo_node_parent(pnp); + pp->dp_chassis = topo_node_instance(ppnp); if (strcmp(topo_node_name(pnp), BAY) == 0) { if (topo_node_facility(hp, pnp, TOPO_FAC_TYPE_INDICATOR, TOPO_FAC_TYPE_ANY, &fl, &err) == 0) { @@ -177,9 +178,19 @@ disk_walker(topo_hdl_t *hp, tnode_t *np, void *arg) } pp->dp_slot = topo_node_instance(pnp); - } + } else if (strcmp(topo_node_name(pnp), USB_DEVICE) == 0) { + if (topo_prop_get_string(pnp, TOPO_PGROUP_PROTOCOL, + TOPO_PROP_LABEL, &slotname, &err) == 0) { + pp->dp_slotname = slotname; + } - pp->dp_chassis = topo_node_instance(ppnp); + /* + * Override dp_chassis for USB devices since they show up + * everywhere in the name space and may not be under a logical + * bay. + */ + pp->dp_chassis = -1; + } return (TOPO_WALK_TERMINATE); } @@ -327,9 +338,12 @@ enumerate_disks(di_opts_t *opts) if (opts->di_parseable) { (void) snprintf(slotname, sizeof (slotname), "%d,%d", phys.dp_chassis, phys.dp_slot); - } else if (phys.dp_slotname != NULL) { + } else if (phys.dp_slotname != NULL && phys.dp_chassis != -1) { (void) snprintf(slotname, sizeof (slotname), "[%d] %s", phys.dp_chassis, phys.dp_slotname); + } else if (phys.dp_slotname != NULL) { + (void) snprintf(slotname, sizeof (slotname), + "%s", phys.dp_slotname); } else { slotname[0] = '-'; slotname[1] = '\0'; diff --git a/usr/src/lib/fm/topo/libtopo/common/hc.c b/usr/src/lib/fm/topo/libtopo/common/hc.c index 26d7b2a642..8b6b57aeb1 100644 --- a/usr/src/lib/fm/topo/libtopo/common/hc.c +++ b/usr/src/lib/fm/topo/libtopo/common/hc.c @@ -198,6 +198,7 @@ static const hcc_t hc_canon[] = { { SUBCHASSIS, TOPO_STABILITY_PRIVATE }, { SYSTEMBOARD, TOPO_STABILITY_PRIVATE }, { TRANSCEIVER, TOPO_STABILITY_PRIVATE }, + { USB_DEVICE, TOPO_STABILITY_PRIVATE }, { XAUI, TOPO_STABILITY_PRIVATE }, { XFP, TOPO_STABILITY_PRIVATE } }; diff --git a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers index 9446179dec..5bafce8002 100644 --- a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers +++ b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers @@ -100,6 +100,7 @@ SYMBOL_VERSION SUNWprivate { topo_mod_enummap; topo_mod_errmsg; topo_mod_errno; + topo_mod_file_search; topo_mod_free; topo_mod_getspecific; topo_mod_hcfmri; diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h index e68417ca42..bfa8d5ba62 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h @@ -93,6 +93,7 @@ extern "C" { #define SUBCHASSIS "subchassis" #define SYSTEMBOARD "systemboard" #define TRANSCEIVER "transceiver" +#define USB_DEVICE "usb-device" #define XAUI "xaui" #define XFP "xfp" @@ -171,6 +172,7 @@ extern "C" { #define TOPO_PGROUP_PORT "port" #define TOPO_PROP_PORT_TYPE "type" #define TOPO_PROP_PORT_TYPE_SFF "sff" +#define TOPO_PROP_PORT_TYPE_USB "usb" #define TOPO_PGROUP_TRANSCEIVER "transceiver" #define TOPO_PROP_TRANSCEIVER_TYPE "type" @@ -182,6 +184,29 @@ extern "C" { #define TOPO_PORT_SFF_TRANSCEIVER_REV "revision" #define TOPO_PORT_SFF_TRANSCEIVER_SN "serial-number" +#define TOPO_PGROUP_USB_PORT "usb-port" +#define TOPO_PROP_USB_PORT_LPORTS "logical-ports" +#define TOPO_PROP_USB_PORT_VERSIONS "usb-versions" +#define TOPO_PROP_USB_PORT_TYPE "port-type" +#define TOPO_PROP_USB_PORT_ATTRIBUTES "port-attributes" +#define TOPO_PROP_USB_PORT_A_VISIBLE "user-visible" +#define TOPO_PROP_USB_PORT_A_CONNECTED "port-connected" +#define TOPO_PROP_USB_PORT_A_DISCONNECTED "port-disconnected" +#define TOPO_PROP_USB_PORT_A_EXTERNAL "external-port" +#define TOPO_PROP_USB_PORT_A_INTERNAL "internal-port" +#define TOPO_PROP_USB_PORT_NATTRS 5 + +#define TOPO_PGROUP_USB_PROPS "usb-properties" +#define TOPO_PGROUP_USB_PROPS_VID "usb-vendor-id" +#define TOPO_PGROUP_USB_PROPS_PID "usb-product-id" +#define TOPO_PGROUP_USB_PROPS_REV "usb-revision-id" +#define TOPO_PGROUP_USB_PROPS_VNAME "usb-vendor-name" +#define TOPO_PGROUP_USB_PROPS_PNAME "usb-product-name" +#define TOPO_PGROUP_USB_PROPS_SN "usb-serialno" +#define TOPO_PGROUP_USB_PROPS_VERSION "usb-version" +#define TOPO_PGROUP_USB_PROPS_SPEED "usb-speed" +#define TOPO_PGROUP_USB_PROPS_PORT "usb-port" + /* * These properties will exist on nodes enumerated by the ipmi module. They * are consumed by the fac_prov_ipmi module diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c index 72d40475f8..281ab28a28 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c @@ -96,6 +96,9 @@ #include <sys/utsname.h> #include <sys/smbios.h> #include <sys/fm/protocol.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <topo_alloc.h> #include <topo_error.h> @@ -918,3 +921,20 @@ topo_mod_clean_str(topo_mod_t *mod, const char *str) return (topo_cleanup_auth_str(mod->tm_hdl, str)); } + +int +topo_mod_file_search(topo_mod_t *mod, const char *file, int oflags) +{ + int ret; + char *path; + topo_hdl_t *thp = mod->tm_hdl; + + path = topo_search_path(mod, thp->th_rootdir, file); + if (path == NULL) { + return (-1); + } + + ret = open(path, oflags); + topo_mod_strfree(mod, path); + return (ret); +} diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h index 482ba27511..e384ff83cf 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h @@ -245,7 +245,7 @@ extern void topo_node_setspecific(tnode_t *, void *); extern void *topo_node_getspecific(tnode_t *); extern int topo_node_asru_set(tnode_t *node, nvlist_t *, int, int *); extern int topo_node_fru_set(tnode_t *node, nvlist_t *, int, int *); -extern int topo_node_label_set(tnode_t *node, char *, int *); +extern int topo_node_label_set(tnode_t *node, const char *, int *); #define TOPO_ASRU_COMPUTE 0x0001 /* Compute ASRU dynamically */ #define TOPO_FRU_COMPUTE 0x0002 /* Compute FRU dynamically */ @@ -292,6 +292,11 @@ typedef enum topo_mod_errno { extern int topo_mod_seterrno(topo_mod_t *, int); +/* + * Function used by a module to try and open a file based on FM's search path. + */ +extern int topo_mod_file_search(topo_mod_t *, const char *file, int oflags); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_protocol.c b/usr/src/lib/fm/topo/libtopo/common/topo_protocol.c index bc7eef55f4..3a0e4811df 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_protocol.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_protocol.c @@ -21,6 +21,7 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2018, Joyent, Inc. */ #include <assert.h> @@ -152,11 +153,11 @@ topo_node_fru_set(tnode_t *node, nvlist_t *fru, int flag, int *err) } int -topo_node_label_set(tnode_t *node, char *label, int *err) +topo_node_label_set(tnode_t *node, const char *label, int *err) { /* - * Inherit FRU property from our parent if * not specified + * Inherit label property from our parent if * not specified */ if (label == NULL) { if (topo_prop_inherit(node, TOPO_PGROUP_PROTOCOL, diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/Makefile b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/Makefile index 15401c3abb..1a25f9a1ea 100644 --- a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/Makefile +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/Makefile @@ -36,13 +36,18 @@ JOYENT_PLATFORMS = \ Joyent-Compute-Platform-3101-hc-topology.xml \ Joyent-Compute-Platform-3102-hc-topology.xml +JOYENT_USBMAPS = \ + Joyent-Compute-Platform-3101-usb.usbtopo \ + Joyent-Compute-Platform-3102-usb.usbtopo ROOTJOYENTMAPS = $(JOYENT_PLATFORMS:%=$(arch_ROOTTOPOROOT)/%) +ROOTJOYENTUSB = $(JOYENT_USBMAPS:%=$(arch_ROOTTOPOROOT)/%) TOPOFILE = \ SSG-2028R-ACR24L-hc-topology.xml \ SSG-2028R-ACR24L-chassis-hc-topology.xml \ SSG-2028R-ACR24L-disk-hc-topology.xml \ - SSG-2028R-ACR24L-slot-hc-topology.xml + SSG-2028R-ACR24L-slot-hc-topology.xml \ + SSG-2028R-ACR24L-usb.usbtopo SRCDIR = ../SMCI,SSG-2028R-ACR24L @@ -52,7 +57,7 @@ CLOBBERFILES += $(ROOTJOYENTMAPS) SSG-2028R-ACR24L-disk-hc-topology.xml include ../Makefile.map -install: $(ROOTJOYENTMAPS) +install: $(ROOTJOYENTMAPS) $(ROOTJOYENTUSB) # # Note, the Joyent Compute Platform 310x is based on the SuperMicro @@ -63,3 +68,6 @@ $(ROOTJOYENTMAPS): SSG-2028R-ACR24L-hc-topology.xml $(RM) $@ $(SYMLINK) ./$? $@ +$(ROOTJOYENTUSB): SSG-2028R-ACR24L-usb.usbtopo + $(RM) $@ + $(SYMLINK) ./$? $@ diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-hc-topology.xml b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-hc-topology.xml index 8406db227d..3fb53266f9 100644 --- a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-hc-topology.xml +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-hc-topology.xml @@ -72,6 +72,9 @@ Copyright (c) 2018, Joyent, Inc. <enum-method name='smbios' version='1' /> <propmap name='SSG-2028R-ACR24L-slot' /> </range> + <range name='usb-mobo' min='0' max='256'> + <enum-method name='usb' version='1' /> + </range> </dependents> </range> @@ -90,6 +93,9 @@ Copyright (c) 2018, Joyent, Inc. <range name='bay' min='0' max='23'> <propmap name='SSG-2028R-ACR24L-disk' /> </range> + <range name='usb-chassis' min='0' max='256'> + <enum-method name='usb' version='1' /> + </range> </dependents> diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-usb.usbtopo b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-usb.usbtopo new file mode 100644 index 0000000000..8cc0b80014 --- /dev/null +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-usb.usbtopo @@ -0,0 +1,98 @@ +# +# 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 (c) 2018, Joyent, Inc. +# + +# +# This file describes the USB topology for the SuperMicro +# SSG-2028P-ACR24L product. For more information on the format see +# topo_usb_file.c. +# + +# +# Older revisions of this system had issues with the ACPI tables that +# caused the ACPI PLD data to incorrectly match ports. As such, drive +# all port matching rules from this. We'll explicitly label the ports as +# well. +# +disable-acpi-match +enable-metadata-match +port + label + Rear Upper Left USB + chassis + external + port-type + 0x0 + acpi-path + \_SB_.PCI0.XHCI.RHUB.HS02 + acpi-path + \_SB_.PCI0.EHC1.HUBN.PR01.PR12 +end-port + +port + label + Rear Lower Left USB + chassis + external + port-type + 0x0 + acpi-path + \_SB_.PCI0.XHCI.RHUB.HS01 + acpi-path + \_SB_.PCI0.EHC1.HUBN.PR01.PR11 +end-port + +port + label + Rear Upper Right USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PCI0.XHCI.RHUB.HS12 + acpi-path + \_SB_.PCI0.XHCI.RHUB.SSP1 + acpi-path + \_SB_.PCI0.EHC2.HUBN.PR01.PR16 +end-port + +port + label + Rear Lower Right USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PCI0.XHCI.RHUB.HS11 + acpi-path + \_SB_.PCI0.XHCI.RHUB.SSP2 + acpi-path + \_SB_.PCI0.EHC2.HUBN.PR01.PR15 +end-port + +port + label + Internal USB + internal + port-type + 0x3 + acpi-path + \_SB_.PCI0.XHCI.RHUB.HS07 + acpi-path + \_SB_.PCI0.XHCI.RHUB.SSP4 + acpi-path + \_SB_.PCI0.EHC2.HUBN.PR01.PR13 +end-port diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/Makefile b/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/Makefile index 0cd1c11a20..fd5412c263 100644 --- a/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/Makefile +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/Makefile @@ -23,13 +23,17 @@ DTDFILE = topology.dtd.1 JOYENT_PLATFORMS = \ Joyent-M12G5-hc-topology.xml +JOYENT_USBMAPS = \ + Joyent-M12G5-usb.usbtopo ROOTJOYENTMAPS = $(JOYENT_PLATFORMS:%=$(arch_ROOTTOPOROOT)/%) +ROOTJOYENTUSB = $(JOYENT_USBMAPS:%=$(arch_ROOTTOPOROOT)/%) TOPOFILE = \ SSG-2029P-ACR24L-hc-topology.xml \ SSG-2029P-ACR24L-chassis-hc-topology.xml \ SSG-2029P-ACR24L-disk-hc-topology.xml \ - SSG-2029P-ACR24L-slot-hc-topology.xml + SSG-2029P-ACR24L-slot-hc-topology.xml \ + SSG-2029P-ACR24L-usb.usbtopo SRCDIR = ../SMCI,SSG-2029P-ACR24L @@ -39,7 +43,7 @@ CLOBBERFILES += SSG-2029P-ACR24L-disk-hc-topology.xml include ../Makefile.map -install: $(ROOTJOYENTMAPS) +install: $(ROOTJOYENTMAPS) $(ROOTJOYENTUSB) # # Note, the Joyent M12G5 is based on the SuperMicro SSG-2029P-ACR24L. @@ -49,3 +53,7 @@ install: $(ROOTJOYENTMAPS) $(ROOTJOYENTMAPS): SSG-2029P-ACR24L-hc-topology.xml $(RM) $@ $(SYMLINK) ./$? $@ + +$(ROOTJOYENTUSB): SSG-2029P-ACR24L-usb.usbtopo + $(RM) $@ + $(SYMLINK) ./$? $@ diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-hc-topology.xml b/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-hc-topology.xml index 68182bfef3..8d13633d4c 100644 --- a/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-hc-topology.xml +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-hc-topology.xml @@ -70,6 +70,9 @@ <enum-method name='smbios' version='1' /> <propmap name='SSG-2029P-ACR24L-slot' /> </range> + <range name='usb-mobo' min='0' max='256'> + <enum-method name='usb' version='1' /> + </range> </dependents> </range> @@ -88,6 +91,9 @@ <range name='bay' min='0' max='25'> <propmap name='SSG-2029P-ACR24L-disk' /> </range> + <range name='usb-chassis' min='0' max='256'> + <enum-method name='usb' version='1' /> + </range> </dependents> diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-usb.usbtopo b/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-usb.usbtopo new file mode 100644 index 0000000000..f63a7369a9 --- /dev/null +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-usb.usbtopo @@ -0,0 +1,85 @@ +# +# 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 (c) 2018, Joyent, Inc. +# + +# +# This file describes the USB topology for the SuperMicro +# SSG-2029P-ACR24L product. For more information on the format see +# topo_usb_file.c. +# + +enable-acpi-match +port + label + Rear Upper Left USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS02 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS02 +end-port + +port + label + Rear Lower Left USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS01 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS01 +end-port + +port + label + Rear Upper Right USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS04 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS04 +end-port + +port + label + Rear Lower Right USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS03 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS03 +end-port + +port + label + Internal USB + internal + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS10 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS07 +end-port diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/Makefile b/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/Makefile index eeaf0c9045..b5c98659bf 100644 --- a/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/Makefile +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/Makefile @@ -23,12 +23,17 @@ DTDFILE = topology.dtd.1 JOYENT_PLATFORMS = \ Joyent-S10G5-hc-topology.xml +JOYENT_USBMAPS = \ + Joyent-S10G5-usb.usbtopo + ROOTJOYENTMAPS = $(JOYENT_PLATFORMS:%=$(arch_ROOTTOPOROOT)/%) +ROOTJOYENTUSB = $(JOYENT_USBMAPS:%=$(arch_ROOTTOPOROOT)/%) TOPOFILE = \ SSG-6049P-E1CR36L-hc-topology.xml \ SSG-6049P-E1CR36L-chassis-hc-topology.xml \ - SSG-6049P-E1CR36L-slot-hc-topology.xml + SSG-6049P-E1CR36L-slot-hc-topology.xml \ + SSG-6049P-E1CR36L-usb.usbtopo SRCDIR = ../SMCI,SSG-6049P-E1CR36L @@ -36,7 +41,7 @@ PLATFORM = SSG-6049P-E1CR36L include ../Makefile.map -install: $(ROOTJOYENTMAPS) +install: $(ROOTJOYENTMAPS) $(ROOTJOYENTUSB) # # Note, the Joyent S10G5 is based on the SuperMicro SSG-6049P-E1CR36L. # Because of that, the topo map used here will work for all such @@ -45,3 +50,7 @@ install: $(ROOTJOYENTMAPS) $(ROOTJOYENTMAPS): SSG-6049P-E1CR36L-hc-topology.xml $(RM) $@ $(SYMLINK) ./$? $@ + +$(ROOTJOYENTUSB): SSG-6049P-E1CR36L-usb.usbtopo + $(RM) $@ + $(SYMLINK) ./$? $@ diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-hc-topology.xml b/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-hc-topology.xml index f3f7311249..b64672502e 100644 --- a/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-hc-topology.xml +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-hc-topology.xml @@ -61,6 +61,9 @@ <enum-method name='smbios' version='1' /> <propmap name='SSG-6049P-E1CR36L-slot' /> </range> + <range name='usb-mobo' min='0' max='256'> + <enum-method name='usb' version='1' /> + </range> </dependents> </range> @@ -76,7 +79,9 @@ <range name='fan' min='0' max='7'> <enum-method name='ipmi' version='1' /> </range> - + <range name='usb-chassis' min='0' max='256'> + <enum-method name='usb' version='1' /> + </range> </dependents> </range> diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-usb.usbtopo b/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-usb.usbtopo new file mode 100644 index 0000000000..1e50d0f021 --- /dev/null +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-usb.usbtopo @@ -0,0 +1,85 @@ +# +# 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 (c) 2018, Joyent, Inc. +# + +# +# This file describes the USB topology for the SuperMicro +# SSG-6049P-E1CR36L product. For more information on the format see +# topo_usb_file.c. +# + +enable-acpi-match +port + label + Rear Upper Left USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS02 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS02 +end-port + +port + label + Rear Lower Left USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS01 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS01 +end-port + +port + label + Rear Upper Right USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS04 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS04 +end-port + +port + label + Rear Lower Right USB + chassis + external + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS03 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS03 +end-port + +port + label + Internal USB + internal + port-type + 0x3 + acpi-path + \_SB_.PC00.XHCI.RHUB.HS10 + acpi-path + \_SB_.PC00.XHCI.RHUB.SS07 +end-port diff --git a/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml b/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml index b878f9599c..dc37ecfed4 100644 --- a/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml +++ b/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml @@ -83,6 +83,9 @@ Copyright (c) 2018, Joyent, Inc. All rights reserved. <range name='hostbridge' min='0' max='254'> <enum-method name='hostbridge' version='1' /> </range> + <range name='usb-mobo' min='0' max='100'> + <enum-method name='usb' version='1' /> + </range> </dependents> </range> @@ -177,6 +180,9 @@ Copyright (c) 2018, Joyent, Inc. All rights reserved. <range name='bay' min='0' max='1024'> <enum-method name='ses' version='1' /> </range> + <range name='usb-chassis' min='0' max='1024'> + <enum-method name='usb' version='1' /> + </range> </set> </dependents> diff --git a/usr/src/lib/fm/topo/modules/common/Makefile b/usr/src/lib/fm/topo/modules/common/Makefile index f7f47f5e06..275ff96f9a 100644 --- a/usr/src/lib/fm/topo/modules/common/Makefile +++ b/usr/src/lib/fm/topo/modules/common/Makefile @@ -33,6 +33,7 @@ SUBDIRS = \ nic \ ses \ smbios \ + usb \ xfp ses: disk diff --git a/usr/src/lib/fm/topo/modules/common/disk/disk.c b/usr/src/lib/fm/topo/modules/common/disk/disk.c index ec53198906..c16fa28aae 100644 --- a/usr/src/lib/fm/topo/modules/common/disk/disk.c +++ b/usr/src/lib/fm/topo/modules/common/disk/disk.c @@ -22,7 +22,7 @@ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2018, Joyent, Inc. */ #include <strings.h> @@ -78,9 +78,8 @@ disk_enum(topo_mod_t *mod, tnode_t *baynode, const char *name, topo_instance_t min, topo_instance_t max, void *arg, void *notused) { - char *device, *driver; + char *device, *driver, *pname; int err; - nvlist_t *fmri; topo_list_t *dlistp = topo_mod_getspecific(mod); if (strcmp(name, DISK) != 0) { @@ -89,19 +88,28 @@ disk_enum(topo_mod_t *mod, tnode_t *baynode, return (-1); } - /* set the parent fru */ - if (topo_node_resource(baynode, &fmri, &err) != 0) { - topo_mod_dprintf(mod, "disk_enum: " - "topo_node_resource error %s\n", topo_strerror(err)); - return (-1); - } - if (topo_node_fru_set(baynode, fmri, 0, &err) != 0) { - topo_mod_dprintf(mod, "disk_enum: " - "topo_node_fru error %s\n", topo_strerror(err)); + /* + * Historically we've always set the parent FRU on nodes; however, it's + * not clear why. Certain node types like USB don't want this, so we + * only do this if the parent is actually a bay. + */ + pname = topo_node_name(baynode); + if (strcmp(pname, BAY) == 0) { + nvlist_t *fmri; + if (topo_node_resource(baynode, &fmri, &err) != 0) { + topo_mod_dprintf(mod, "disk_enum: " + "topo_node_resource error %s\n", + topo_strerror(err)); + return (-1); + } + if (topo_node_fru_set(baynode, fmri, 0, &err) != 0) { + topo_mod_dprintf(mod, "disk_enum: " + "topo_node_fru error %s\n", topo_strerror(err)); + nvlist_free(fmri); + return (-1); + } nvlist_free(fmri); - return (-1); } - nvlist_free(fmri); /* * For internal storage, first check to see if we need to diff --git a/usr/src/lib/fm/topo/modules/common/disk/disk_common.c b/usr/src/lib/fm/topo/modules/common/disk/disk_common.c index e126a190e0..b1ddbd06e9 100644 --- a/usr/src/lib/fm/topo/modules/common/disk/disk_common.c +++ b/usr/src/lib/fm/topo/modules/common/disk/disk_common.c @@ -149,11 +149,12 @@ disk_set_props(topo_mod_t *mod, tnode_t *parent, /* pull the label property down from our parent 'bay' node */ if (topo_node_label(parent, &label, &err) != 0) { - topo_mod_dprintf(mod, "disk_set_props: " - "label error %s\n", topo_strerror(err)); - goto error; - } - if (topo_node_label_set(dtn, label, &err) != 0) { + if (err != ETOPO_PROP_NOENT) { + topo_mod_dprintf(mod, "disk_set_props: " + "label error %s\n", topo_strerror(err)); + goto error; + } + } else if (topo_node_label_set(dtn, label, &err) != 0) { topo_mod_dprintf(mod, "disk_set_props: " "label_set error %s\n", topo_strerror(err)); goto error; @@ -1006,7 +1007,25 @@ dev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp) INQUIRY_SERIAL_NO, &s) > 0) { if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL) goto error; + } else { + /* + * Many USB disk devices don't emulate serial inquiry number + * because their serial number can be longer than the standard + * SCSI length. If we didn't get an inquiry serial number, fill + * one in this way. + */ + di_node_t parent; + + if ((parent = di_parent_node(node)) != DI_NODE_NIL && + di_prop_lookup_strings(DDI_DEV_T_ANY, parent, + "usb-serialno", &s) > 0) { + if ((dnode->ddn_serial = disk_trim_whitespace(mod, + s)) == NULL) { + goto error; + } + } } + if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, "device-nblocks", &nblocksp) > 0) { nblocks = (uint64_t)*nblocksp; diff --git a/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c b/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c index 36dc26aa8d..1417440b2f 100644 --- a/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c +++ b/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2017, Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. */ #include <sys/fm/protocol.h> @@ -45,6 +45,7 @@ #include <did_props.h> #include <util.h> #include <topo_nic.h> +#include <topo_usb.h> extern txprop_t Bus_common_props[]; extern txprop_t Dev_common_props[]; @@ -537,6 +538,19 @@ declare_dev_and_fn(topo_mod_t *mod, tnode_t *bus, tnode_t **dev, di_node_t din, } (void) topo_mod_enumerate(mod, fn, NIC, NIC, 0, 0, din); + } else if (class == PCI_CLASS_SERIALBUS && subclass == PCI_SERIAL_USB) { + /* + * If we encounter a USB controller, make sure to enumerate all + * of its USB ports. + */ + if (topo_mod_load(mod, USB, USB_VERSION) == NULL) { + topo_mod_dprintf(mod, "pcibus enum could not load " + "usb enum\n"); + (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); + return; + } + + (void) topo_mod_enumerate(mod, fn, USB, USB_PCI, 0, 0, din); } else if (class == PCI_CLASS_MASS) { di_node_t cn; int niports = 0; diff --git a/usr/src/lib/fm/topo/modules/common/shared/topo_port.c b/usr/src/lib/fm/topo/modules/common/shared/topo_port.c index 8f0ba8690e..8b6f1c726c 100644 --- a/usr/src/lib/fm/topo/modules/common/shared/topo_port.c +++ b/usr/src/lib/fm/topo/modules/common/shared/topo_port.c @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2017, Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. */ #include <sys/fm/protocol.h> @@ -128,3 +128,16 @@ port_create_sff(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst, *nodep = tn; return (0); } + +int +port_create_usb(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst, + tnode_t **nodep) +{ + tnode_t *tn; + + tn = port_create_common(mod, pnode, inst, TOPO_PROP_PORT_TYPE_USB); + if (tn == NULL) + return (-1); + *nodep = tn; + return (0); +} diff --git a/usr/src/lib/fm/topo/modules/common/shared/topo_port.h b/usr/src/lib/fm/topo/modules/common/shared/topo_port.h index 78ace0b0bb..df85ff84fd 100644 --- a/usr/src/lib/fm/topo/modules/common/shared/topo_port.h +++ b/usr/src/lib/fm/topo/modules/common/shared/topo_port.h @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2017, Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. */ #ifndef _TOPO_PORT_H @@ -28,6 +28,8 @@ extern int port_range_create(topo_mod_t *, tnode_t *, topo_instance_t, topo_instance_t); extern int port_create_sff(topo_mod_t *, tnode_t *, topo_instance_t, tnode_t **); +extern int port_create_usb(topo_mod_t *, tnode_t *, topo_instance_t, + tnode_t **); #ifdef __cplusplus } diff --git a/usr/src/lib/fm/topo/modules/common/usb/Makefile b/usr/src/lib/fm/topo/modules/common/usb/Makefile new file mode 100644 index 0000000000..eff5fb2604 --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/usb/Makefile @@ -0,0 +1,28 @@ +# +# 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 (c) 2018, Joyent, Inc. +# + +MODULE = usb +CLASS = common +SHAREDMODULE = shared + + +MODULESRCS = topo_usb.c topo_usb_acpi.o topo_usb_metadata.o +SHAREDSRCS = topo_port.c + +include ../../Makefile.plugin + +CPPFLAGS += -I../shared -I$(SRC)/uts/intel/sys/acpi -DACPI_APPLICATION +CPPFLAGS += -D_POSIX_PTHREAD_SEMANTICS +LDLIBS += -ldevinfo diff --git a/usr/src/lib/fm/topo/modules/common/usb/topo_usb.c b/usr/src/lib/fm/topo/modules/common/usb/topo_usb.c new file mode 100644 index 0000000000..5a5c526c51 --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/usb/topo_usb.c @@ -0,0 +1,2012 @@ +/* + * 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 (c) 2018, Joyent, Inc. + */ + +/* + * The purpose of this module is to build topology information for USB devices. + * USB devices are more complicated to build topology information for, as there + * are multiple sources of information needed to correctly understand the + * topology, and the way they present themselves is not always straightforward. + * + * We enumerate two different types of devices: + * + * o USB ports + * o USB devices + * + * A USB port represents a logical port, while a USB device represents an actual + * device that's been plugged in. If a device is a hub, then we'll enumerate + * that device as well. + * + * Now, some basics. There are several different USB controllers that exist in + * the system. Some are part of the chipset, while others may be present via + * add-on cards. The system interfaces initially with USB devices through a host + * controller. Prior to USB 3.0/xhci, a single controller only supported a + * single protocol. With USB 3.0, it is possible for a port to share wiring with + * both USB 2.0 devices and USB 3.0 devices. However, to the host controller + * this appears as two different logical ports. + * + * To make matters worse, during the transition to USB 3, the ports that were + * controlled could be routed to and from a USB 2 controller to a USB 3 + * controller. This means that there are a lot of ways for ports to overlap. + * + * In the first case, controllers define a way to perform this mapping by + * leveraging ACPI information. Of course, this only helps us if the platform + * provides ACPI information, which it may not. When we do know that two ports + * are actually the same port, either because of ACPI or because of a + * product-specific mapping file, then we'll use that to say two ports are the + * same. Otherwise, we'll enumerate them as two separate logical ports. + * + * To perform the actual enumeration, the first time we're asked to enumerate a + * node, we go through and put together an entire picture of all of the USB + * devices in the system. This is done so we can make sure to enumerate devices + * under specific devices. The actual topology is determined in a few different + * passes. + * + * Before we walk any trees, we look to see if we have a topo USB metadata file + * and if present, load it. However, we do not apply any information from it. + * + * The first pass uses the devinfo tree to determine all of the USB controllers + * and devices that are in the system. We use properties in the devices tree to + * identify whether items are a root hub. When a root hub is found, we walk all + * of its children and make a note of all of the logical ports under it. + * + * Next, we walk the information provided by ACPI to try and reduplicate + * information about the ports on the system. If the USB topology metadata tells + * us that we should not skip ACPI, then we use it. This is done by walking the + * /devices/fw tree, looking for USB nodes and then linking them to their + * corresponding entries found from the first devinfo walk. + * + * Finally, we go back and apply metadata to ports that match. + * + * + * To logically keep track of all of this, we have several different structures: + * + * topo_usb_controller_t - Represents a physical controller. + * topo_usb_port_t - Represents a physical port. This is a synthetic + * construct that we put together based on ACPI + * information. + * topo_usb_lport_t - Represents a logical port. This is what the OS + * actually detects and sees. Each logical port + * belongs to a corresponding topo_usb_port_t. + * topo_usb_t - Represents the overall topology enumeration state. + * + * + * This topo module is invoked at three different points by the surrounding code + * and logic. Specifically: + * + * * Dynamically by the pcibus enumerator when we encounter PCI add on cards + * which are present in a physical slot. Traditional chipset devices are not + * considered a part of this. + * + * * Statically under the motherboard. All ports that don't belong to a PCI + * device are assumed to belong under the motherboard, unless a + * platform-specific topology map maps them under the chassis. + * + * * Statically under the chassis. Ports are only placed under the chassis if + * a platform-specific topology file indicates that the port is a part of + * the chassis. + */ + +#include <libdevinfo.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <sys/debug.h> +#include <unistd.h> + +#include <sys/fm/protocol.h> +#include <fm/topo_mod.h> +#include <fm/topo_list.h> +#include <fm/topo_method.h> + +#include <topo_port.h> + +#include "topo_usb.h" +#include "topo_usb_int.h" + +typedef enum topo_usb_type { + TOPO_USB_PCI, + TOPO_USB_MOBO, + TOPO_USB_CHASSIS +} topo_usb_type_t; + +typedef enum topo_usb_cdrv { + TOPO_USB_D_UNKNOWN, + TOPO_USB_D_UHCI, + TOPO_USB_D_OHCI, + TOPO_USB_D_EHCI, + TOPO_USB_D_XHCI +} topo_usb_cdrv_t; + +typedef enum topo_usb_protocol { + TOPO_USB_P_UNKNOWN, + TOPO_USB_P_1x, + TOPO_USB_P_20, + TOPO_USB_P_30, + TOPO_USB_P_31 +} topo_usb_protocol_t; + +typedef enum topo_usb_port_connected { + TOPO_USB_C_UNKNOWN, + TOPO_USB_C_DISCONNECTED, + TOPO_USB_C_CONNECTED +} topo_usb_port_connected_t; + +typedef struct topo_usb_port { + topo_list_t tup_link; + uint_t tup_nlports; + topo_list_t tup_lports; + boolean_t tup_pld_valid; + acpi_pld_info_t tup_pld; + uint_t tup_port_type; + topo_usb_port_connected_t tup_port_connected; + topo_usb_meta_port_t *tup_meta; +} topo_usb_port_t; + +typedef struct topo_usb_lport { + topo_list_t tul_link; + uint_t tul_portno; + topo_usb_protocol_t tul_protocol; + di_node_t tul_device; + di_node_t tul_acpi_device; + topo_usb_port_t *tul_port; + uint_t tul_nhubd_ports; + uint_t tul_nports; + topo_list_t tul_ports; + char tul_name[PATH_MAX]; + const char *tul_acpi_name; +} topo_usb_lport_t; + +typedef struct topo_usb_controller { + topo_list_t tuc_link; + di_node_t tuc_devinfo; + char *tuc_path; + char *tuc_acpi_path; + char tuc_name[PATH_MAX]; + topo_usb_cdrv_t tuc_driver; + /* + * Number of actual ports we've created (some of the logical ports are + * deduped). + */ + uint_t tuc_nports; + topo_list_t tuc_ports; + /* + * Total number of logical ports we expect to exist on this controller. + * This may be greater than the number of actual ports we've created + * under it because some physical ports represent more than one logical + * port (xhci with USB2/3). + */ + uint_t tuc_nhubd_ports; + /* + * Keep track of port number and offset information. This is only done + * for xhci. + */ + uint_t tuc_nusb20; + uint_t tuc_fusb20; + uint_t tuc_nusb30; + uint_t tuc_fusb30; + uint_t tuc_nusb31; + uint_t tuc_fusb31; + boolean_t tuc_enumed; +} topo_usb_controller_t; + +typedef struct topo_usb { + topo_list_t tu_controllers; + boolean_t tu_enum_done; + di_node_t tu_devinfo; + topo_list_t tu_metadata; + topo_usb_meta_flags_t tu_meta_flags; + topo_list_t tu_chassis_ports; + uint_t tu_nchassis_ports; +} topo_usb_t; + +typedef struct topo_usb_devcfg_arg { + topo_usb_t *tda_usb; + topo_mod_t *tda_mod; + boolean_t tda_fatal; +} topo_usb_devcfg_arg_t; + +static const topo_pgroup_info_t topo_usb_port_pgroup = { + TOPO_PGROUP_USB_PORT, + TOPO_STABILITY_PRIVATE, + TOPO_STABILITY_PRIVATE, + 1 +}; + +static const topo_pgroup_info_t topo_io_pgroup = { + TOPO_PGROUP_IO, + TOPO_STABILITY_PRIVATE, + TOPO_STABILITY_PRIVATE, + 1 +}; + +static const topo_pgroup_info_t topo_binding_pgroup = { + TOPO_PGROUP_BINDING, + TOPO_STABILITY_PRIVATE, + TOPO_STABILITY_PRIVATE, + 1 +}; + +static const topo_pgroup_info_t topo_usb_props_pgroup = { + TOPO_PGROUP_USB_PROPS, + TOPO_STABILITY_PRIVATE, + TOPO_STABILITY_PRIVATE, + 1 +}; + +/* Required forwards */ +static int topo_usb_enum_device(topo_mod_t *, tnode_t *, topo_usb_port_t *); + +/* + * Defines the maximum number of USB ports that can exist. Ports are basically + * defined by a uint8_t, meaning that we can go up to UINT8_MAX inclusively. + */ +#define USB_TOPO_PORT_MAX 256 + +/* + * Default value to indicate that a USB port has no valid type. + */ +#define USB_TOPO_PORT_TYPE_DEFAULT 0xff + +/* + * These come from the ACPI 6.2 / Table 9-290 UPC Return Package Values. + */ +static const char * +topo_usb_port_type_to_string(int type) +{ + switch (type) { + case 0x00: + return ("Type A connector"); + case 0x01: + return ("Mini-AB connector"); + case 0x02: + return ("ExpressCard"); + case 0x03: + return ("USB 3 Standard-A connector"); + case 0x04: + return ("USB 3 Standard-B connector"); + case 0x05: + return ("USB 3 Micro-B connector"); + case 0x06: + return ("USB 3 Micro-AB connector"); + case 0x07: + return ("USB 3 Power-B connector"); + case 0x08: + return ("Type C connector - USB2-only"); + case 0x09: + return ("Type C connector - USB2 and SS with Switch"); + case 0x0A: + return ("Type C connector - USB2 and SS without Switch"); + /* 0x0B->0xFE are reserved. Treat them like 0xFF */ + case 0xFF: + default: + return ("Unknown"); + } +} + +/* + * Searches the list of ports at a given layer (not recursively) for the + * specific port id. + */ +static topo_usb_lport_t * +topo_usb_lport_find(topo_list_t *plist, uint_t logid) +{ + topo_usb_port_t *p; + + for (p = topo_list_next(plist); p != NULL; p = topo_list_next(p)) { + topo_usb_lport_t *l; + + for (l = topo_list_next(&p->tup_lports); l != NULL; + l = topo_list_next(l)) { + if (l->tul_portno == logid) + return (l); + } + } + return (NULL); +} + +/* + * Create an instance of a controller and seed the basic information. + */ +static topo_usb_controller_t * +topo_usb_controller_create(topo_mod_t *mod, topo_usb_t *usb, di_node_t node) +{ + int *pcount, inst; + char *drvname, *acpi; + topo_usb_controller_t *c; + + /* + * If we can't get the port count or the driver, then this node is + * uninteresting. + */ + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-port-count", + &pcount) != 1) { + return (NULL); + } + + if ((drvname = di_driver_name(node)) == NULL || + (inst = di_instance(node) == -1)) + return (NULL); + + if ((c = topo_mod_zalloc(mod, sizeof (topo_usb_controller_t))) == + NULL || *pcount <= 0) { + return (NULL); + } + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "acpi-namespace", + &acpi) == 1) { + c->tuc_acpi_path = acpi; + } + + c->tuc_nhubd_ports = (uint_t)*pcount; + c->tuc_devinfo = node; + c->tuc_path = di_devfs_path(node); + (void) snprintf(c->tuc_name, sizeof (c->tuc_name), "%s%d", drvname, + inst); + if (strcmp(drvname, "xhci") == 0) { + int *p; + + c->tuc_driver = TOPO_USB_D_XHCI; + + /* + * Grab the properties that we need so we can better do a port + * speed mapping. + */ + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "usb2.0-port-count", &p) == 1 && *p > 0) { + c->tuc_nusb20 = (uint_t)*p; + } + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "usb2.0-first-port", &p) == 1 && *p > 0) { + c->tuc_fusb20 = (uint_t)*p; + } + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "usb3.0-port-count", &p) == 1 && *p > 0) { + c->tuc_nusb30 = (uint_t)*p; + } + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "usb3.0-first-port", &p) == 1 && *p > 0) { + c->tuc_fusb30 = (uint_t)*p; + } + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "usb3.1-port-count", &p) == 1 && *p > 0) { + c->tuc_nusb31 = (uint_t)*p; + } + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "usb3.1-first-port", &p) == 1 && *p > 0) { + c->tuc_fusb31 = (uint_t)*p; + } + } else if (strcmp(drvname, "ehci") == 0) { + c->tuc_driver = TOPO_USB_D_EHCI; + } else if (strcmp(drvname, "uhci") == 0) { + c->tuc_driver = TOPO_USB_D_UHCI; + } else if (strcmp(drvname, "ohci") == 0) { + c->tuc_driver = TOPO_USB_D_OHCI; + } else { + c->tuc_driver = TOPO_USB_D_UNKNOWN; + } + topo_list_append(&usb->tu_controllers, c); + topo_mod_dprintf(mod, "created new USB controller at %s", c->tuc_path); + + return (c); +} + +/* + * Process this port and any others that might exist. + */ +static boolean_t +topo_usb_gather_acpi_port(topo_mod_t *mod, topo_usb_t *usb, topo_list_t *plist, + uint_t *nports, topo_usb_controller_t *tuc, di_node_t portinfo) +{ + int64_t *portno; + uchar_t *loc; + int loclen, *type; + char *acpi; + acpi_pld_info_t pld; + boolean_t pld_valid = B_FALSE; + topo_usb_port_t *port = NULL; + topo_usb_lport_t *lport; + di_node_t child; + + /* + * Get the port's address, it's a required value. Because this is coming + * from firmware, we cannot trust the port's value to be correct. + */ + if (di_prop_lookup_int64(DDI_DEV_T_ANY, portinfo, "acpi-address", + &portno) != 1 || *portno < 1 || *portno >= USB_TOPO_PORT_MAX) { + return (B_FALSE); + } + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, portinfo, "acpi-namespace", + &acpi) != 1) { + return (B_FALSE); + } + + /* + * Check to see if we have any ACPI location information. If we do, we + * can decode it. + */ + if ((loclen = di_prop_lookup_bytes(DDI_DEV_T_ANY, portinfo, + "acpi-physical-location", &loc)) >= ACPI_PLD_REV1_BUFFER_SIZE && + usbtopo_decode_pld(loc, loclen, &pld)) { + pld_valid = B_TRUE; + } + + /* + * Find the corresponding lport. If this node doesn't happen to match + * something we've enumerated from the hub. Warn about that fact and + * consider this bad data. + */ + lport = topo_usb_lport_find(plist, (uint_t)*portno); + if (lport == NULL) { + topo_mod_dprintf(mod, "failed to find physical usb port for " + "%s/%u", acpi, (uint_t)*portno); + return (B_TRUE); + } + + if (lport->tul_acpi_device != DI_NODE_NIL) { + topo_mod_dprintf(mod, "logical port already bound to %s, not " + "binding to %s", lport->tul_acpi_name, acpi); + return (B_FALSE); + } + + lport->tul_acpi_device = portinfo; + lport->tul_acpi_name = acpi; + port = lport->tul_port; + + if (pld_valid) { + port->tup_pld_valid = B_TRUE; + port->tup_pld = pld; + } + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, portinfo, "usb-port-type", + &type) == 1 && *type >= 0) { + port->tup_port_type = *type; + } else { + port->tup_port_type = USB_TOPO_PORT_TYPE_DEFAULT; + } + + if (di_prop_find(DDI_DEV_T_ANY, portinfo, + "usb-port-connectable") != DI_PROP_NIL) { + port->tup_port_connected = TOPO_USB_C_CONNECTED; + } else { + port->tup_port_connected = TOPO_USB_C_DISCONNECTED; + } + + for (child = di_child_node(portinfo); child != NULL; + child = di_sibling_node(child)) { + const char *pname; + + pname = di_node_name(child); + if (pname == NULL || strcmp(pname, "port") != 0) { + continue; + } + + if (!topo_usb_gather_acpi_port(mod, usb, &lport->tul_ports, + &lport->tul_nports, tuc, child)) { + return (B_FALSE); + } + } + + topo_mod_dprintf(mod, "discovered %u ACPI usb child ports", + lport->tul_nports); + + return (B_TRUE); +} + +/* + * First, bootstrap all of our information by reading the ACPI information + * exposed in the devinfo tree. All of the nodes we care about will be under + * /fw/sb@XX/usbrootub@YYY/port@ZZZ + */ +static boolean_t +topo_usb_gather_acpi(topo_mod_t *mod, topo_usb_t *usb) +{ + di_node_t fwroot, sbnode; + + /* + * If we can't find the /fw node, that's fine. We may not have any ACPI + * information on the system. + */ + fwroot = di_lookup_node(usb->tu_devinfo, "/fw"); + if (fwroot == DI_NODE_NIL) + return (B_TRUE); + + for (sbnode = di_child_node(fwroot); sbnode != DI_NODE_NIL; + sbnode = di_sibling_node(sbnode)) { + const char *sbname; + di_node_t hub; + + sbname = di_node_name(sbnode); + if (sbname == NULL || strcmp(sbname, "sb") != 0) { + continue; + } + + for (hub = di_child_node(sbnode); hub != DI_NODE_NIL; + hub = di_sibling_node(hub)) { + const char *hubname; + char *acpi; + topo_usb_controller_t *tuc; + di_node_t port; + + hubname = di_node_name(hub); + if (hubname == NULL || + strcmp(hubname, "usbroothub") != 0) { + continue; + } + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, hub, + "acpi-controller-name", &acpi) != 1) { + continue; + } + + for (tuc = topo_list_next(&usb->tu_controllers); + tuc != NULL; + tuc = topo_list_next(tuc)) { + if (tuc->tuc_acpi_path != NULL && + strcmp(acpi, tuc->tuc_acpi_path) == 0) + break; + } + + if (tuc == NULL) { + topo_mod_dprintf(mod, "failed to find USB " + "controller for ACPI path %s", acpi); + continue; + } + + for (port = di_child_node(hub); port != NULL; + port = di_sibling_node(port)) { + const char *pname; + + pname = di_node_name(port); + if (pname == NULL || + strcmp(pname, "port") != 0) { + continue; + } + + if (!topo_usb_gather_acpi_port(mod, usb, + &tuc->tuc_ports, &tuc->tuc_nports, tuc, + port)) { + return (B_FALSE); + } + } + + topo_mod_dprintf(mod, "found ACPI usb controller %s " + "with %d top-level ports", tuc->tuc_path, + tuc->tuc_nports); + } + } + + return (B_TRUE); +} + +static topo_usb_port_t * +topo_usb_port_create(topo_mod_t *mod, uint_t portno, const char *parent, + char sep) +{ + topo_usb_lport_t *l; + topo_usb_port_t *p; + + if ((l = topo_mod_zalloc(mod, sizeof (topo_usb_lport_t))) == NULL) { + return (NULL); + } + l->tul_portno = portno; + if (snprintf(l->tul_name, sizeof (l->tul_name), "%s%c%u", parent, sep, + portno) >= sizeof (l->tul_name)) { + topo_mod_free(mod, l, sizeof (topo_usb_lport_t)); + return (NULL); + } + + if ((p = topo_mod_zalloc(mod, sizeof (topo_usb_port_t))) == NULL) { + topo_mod_free(mod, l, sizeof (topo_usb_lport_t)); + return (NULL); + } + l->tul_port = p; + p->tup_port_type = USB_TOPO_PORT_TYPE_DEFAULT; + topo_list_append(&p->tup_lports, l); + p->tup_nlports++; + + return (p); +} + +/* + * Set the protocol of a port that belongs to a root hub. + */ +static void +topo_usb_set_rhub_port_protocol(topo_mod_t *mod, topo_usb_controller_t *tuc, + topo_usb_lport_t *lport) +{ + switch (tuc->tuc_driver) { + case TOPO_USB_D_XHCI: + break; + case TOPO_USB_D_UHCI: + case TOPO_USB_D_OHCI: + lport->tul_protocol = TOPO_USB_P_1x; + return; + case TOPO_USB_D_EHCI: + lport->tul_protocol = TOPO_USB_P_20; + return; + case TOPO_USB_D_UNKNOWN: + default: + lport->tul_protocol = TOPO_USB_P_UNKNOWN; + return; + } + + /* + * The xHCI controller can support multiple different, protocols. It + * communicates this information to us via devinfo properties. It's + * possible that a port that is within max ports is not within the range + * here. If that's the case, we'll set it to unknown. + */ + if (lport->tul_portno >= tuc->tuc_fusb20 && + lport->tul_portno < tuc->tuc_fusb20 + tuc->tuc_nusb20) { + lport->tul_protocol = TOPO_USB_P_20; + } else if (lport->tul_portno >= tuc->tuc_fusb30 && + lport->tul_portno < tuc->tuc_fusb30 + tuc->tuc_nusb30) { + lport->tul_protocol = TOPO_USB_P_30; + } else if (lport->tul_portno >= tuc->tuc_fusb31 && + lport->tul_portno < tuc->tuc_fusb31 + tuc->tuc_nusb31) { + lport->tul_protocol = TOPO_USB_P_31; + } else { + lport->tul_protocol = TOPO_USB_P_UNKNOWN; + } +} + +/* + * We've found a node on the list. Attempt to find its corresponding port. If we + * find a hub, then we will descend further down this part of the tree. + */ +static int +topo_usb_gather_devcfg_port(topo_mod_t *mod, topo_usb_controller_t *c, + topo_list_t *plist, di_node_t node) +{ + int *vend, *reg, *nports; + topo_usb_lport_t *l; + char *drvname; + + /* + * Look for the presence of the usb-vendor-id property to determine + * whether or not this is a usb device node. usba always adds this + * to the devices that it enumerates. + */ + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-vendor-id", + &vend) != 1) { + topo_mod_dprintf(mod, "failed to find usb-vendor-id property " + "for child"); + return (0); + } + + /* + * For usb-devices, the reg property is one entry long and it has the + * logical port that the controller sees. + */ + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", ®) != 1 || + *reg <= 0) { + topo_mod_dprintf(mod, "got bad \"reg\" property"); + return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM)); + } + + if ((l = topo_usb_lport_find(plist, (uint_t)*reg)) == NULL) { + topo_mod_dprintf(mod, "failed to find topo_usb_lport_t for " + "port %d", *reg); + return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); + } + + l->tul_device = node; + + /* + * Check to see if we have a hub and if so, process it. + */ + if ((drvname = di_driver_name(node)) != NULL && + strcmp(drvname, "hubd") == 0 && + di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-port-count", + &nports) == 1 && *nports >= 1) { + di_node_t child; + + /* + * First go through and try and discover and create all the + * logical ports that exist. It is possible that these ports + * already exist and that we have ACPI information about them. + * This would happen when a root port is connected into a set of + * hubs that are built-in. + */ + l->tul_nhubd_ports = (uint_t)*nports; + for (uint_t i = 1; i <= l->tul_nhubd_ports; i++) { + topo_usb_lport_t *clport; + topo_usb_port_t *cport; + + if ((cport = topo_usb_port_create(mod, i, l->tul_name, + '.')) == NULL) { + return (topo_mod_seterrno(mod, EMOD_NOMEM)); + } + + clport = topo_list_next(&cport->tup_lports); + topo_list_append(&l->tul_ports, cport); + l->tul_nports++; + + clport->tul_protocol = l->tul_protocol; + } + + /* + * Now go through and discover its children. + */ + for (child = di_child_node(node); child != NULL; + child = di_sibling_node(child)) { + int ret; + + if ((ret = topo_usb_gather_devcfg_port(mod, c, + &l->tul_ports, child)) != 0) { + return (-1); + } + } + } + + return (0); +} + +static int +topo_usb_gather_devcfg_cb(di_node_t node, void *arg) +{ + uint_t i; + topo_usb_controller_t *tuc; + di_prop_t prop = DI_PROP_NIL; + boolean_t rh = B_FALSE, pc = B_FALSE; + topo_usb_devcfg_arg_t *tda = arg; + topo_usb_t *usb = tda->tda_usb; + topo_mod_t *mod = tda->tda_mod; + di_node_t child; + + while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) { + const char *name = di_prop_name(prop); + int *ports; + + if (strcmp(name, "root-hub") == 0 && + di_prop_type(prop) == DI_PROP_TYPE_BOOLEAN) { + rh = B_TRUE; + } else if (strcmp(name, "usb-port-count") == 0 && + di_prop_ints(prop, &ports) == 1 && *ports > 0 && + *ports < USB_TOPO_PORT_MAX) { + pc = B_TRUE; + } + } + + if (!rh || !pc) + return (DI_WALK_CONTINUE); + + if ((tuc = topo_usb_controller_create(mod, usb, node)) == NULL) { + tda->tda_fatal = B_TRUE; + return (DI_WALK_TERMINATE); + } + + /* + * Check to make sure that every logical port exists at this level and + * that we have its speed information filled in. If it does not exist, + * create it. + */ + for (i = 1; i <= tuc->tuc_nhubd_ports; i++) { + topo_usb_lport_t *l; + topo_usb_port_t *p; + + topo_mod_dprintf(mod, "attempting to discover lport %u on " + "controller %s", i, tuc->tuc_path); + + if ((p = topo_usb_port_create(mod, i, tuc->tuc_name, '@')) == + NULL) { + topo_mod_dprintf(mod, "failed to create " + "port %u", i); + tda->tda_fatal = B_TRUE; + return (DI_WALK_TERMINATE); + } + + topo_list_append(&tuc->tuc_ports, p); + tuc->tuc_nports++; + l = topo_list_next(&p->tup_lports); + + topo_usb_set_rhub_port_protocol(mod, tuc, l); + } + + for (child = di_child_node(tuc->tuc_devinfo); child != NULL; + child = di_sibling_node(child)) { + int ret; + + if ((ret = topo_usb_gather_devcfg_port(mod, tuc, + &tuc->tuc_ports, child)) != 0) { + tda->tda_fatal = B_TRUE; + return (DI_WALK_TERMINATE); + } + } + + return (DI_WALK_PRUNECHILD); +} + +/* + * To find all the controllers in the system, look for device nodes that have + * the 'root-hub' property and also a valid usb-port-count property. + */ +static boolean_t +topo_usb_gather_devcfg(topo_mod_t *mod, topo_usb_t *usb) +{ + topo_usb_devcfg_arg_t tda; + + tda.tda_usb = usb; + tda.tda_mod = mod; + tda.tda_fatal = B_FALSE; + + (void) di_walk_node(usb->tu_devinfo, DI_WALK_CLDFIRST, + &tda, topo_usb_gather_devcfg_cb); + + return (!tda.tda_fatal); +} + +/* + * For more information on the matching logic here, see xHCI r1.1 / Appendix D - + * Port to Connector Mapping. + */ +static boolean_t +topo_usb_acpi_pld_match(const acpi_pld_info_t *l, const acpi_pld_info_t *r) +{ + if (l->Panel == r->Panel && + l->VerticalPosition == r->VerticalPosition && + l->HorizontalPosition == r->HorizontalPosition && + l->Shape == r->Shape && + l->GroupOrientation == r->GroupOrientation && + l->GroupPosition == r->GroupPosition && + l->GroupToken == r->GroupToken) { + return (B_TRUE); + } + + return (B_FALSE); +} + +typedef boolean_t (*topo_usb_port_match_f)(topo_usb_port_t *, void *); + +static topo_usb_port_t * +topo_usb_port_match_lport(topo_usb_lport_t *lport, boolean_t remove, + topo_usb_port_match_f func, void *arg) +{ + topo_usb_port_t *p; + + for (p = topo_list_next(&lport->tul_ports); p != NULL; + p = topo_list_next(p)) { + topo_usb_lport_t *l; + topo_usb_port_t *ret; + + if (func(p, arg)) { + if (remove) { + topo_list_delete(&lport->tul_ports, p); + lport->tul_nports--; + } + + return (p); + } + + for (l = topo_list_next(&p->tup_lports); l != NULL; + l = topo_list_next(l)) { + if ((ret = topo_usb_port_match_lport(l, + remove, func, arg)) != NULL) { + return (ret); + } + } + } + + return (NULL); +} + +static topo_usb_port_t * +topo_usb_port_match_controller(topo_usb_controller_t *c, boolean_t remove, + topo_usb_port_match_f func, void *arg) +{ + topo_usb_port_t *p; + + for (p = topo_list_next(&c->tuc_ports); p != NULL; + p = topo_list_next(p)) { + topo_usb_lport_t *l; + topo_usb_port_t *ret; + + if (func(p, arg)) { + if (remove) { + topo_list_delete(&c->tuc_ports, p); + c->tuc_nports--; + } + + return (p); + } + + for (l = topo_list_next(&p->tup_lports); l != NULL; + l = topo_list_next(l)) { + if ((ret = topo_usb_port_match_lport(l, + remove, func, arg)) != NULL) { + return (ret); + } + } + } + + return (NULL); +} + +static topo_usb_port_t * +topo_usb_port_match(topo_usb_t *usb, boolean_t remove, + topo_usb_port_match_f func, void *arg) +{ + topo_usb_controller_t *c; + + for (c = topo_list_next(&usb->tu_controllers); c != NULL; + c = topo_list_next(c)) { + topo_usb_port_t *p; + + if ((p = topo_usb_port_match_controller(c, remove, func, + arg)) != NULL) + return (p); + } + return (NULL); +} + +/* + * Merge all of the local ports and information in source, to sink. + */ +static void +topo_usb_port_merge(topo_usb_port_t *sink, topo_usb_port_t *source) +{ + topo_usb_lport_t *l; + + while ((l = topo_list_next(&source->tup_lports)) != NULL) { + topo_list_delete(&source->tup_lports, l); + source->tup_nlports--; + topo_list_append(&sink->tup_lports, l); + sink->tup_nlports++; + } + + if (sink->tup_port_type == USB_TOPO_PORT_TYPE_DEFAULT) { + sink->tup_port_type = source->tup_port_type; + } + + if (sink->tup_port_connected == TOPO_USB_C_UNKNOWN) { + sink->tup_port_connected = source->tup_port_connected; + } +} + +static boolean_t +topo_usb_acpi_port_match(topo_usb_port_t *port, void *arg) +{ + topo_usb_port_t *target = arg; + + return (port != target && port->tup_pld_valid && + topo_usb_acpi_pld_match(&port->tup_pld, &target->tup_pld)); +} + +/* + * Ports on an xhci controller can match up. If we've been told that we should + * do so, attempt to perform that match. We only try to find matches in the top + * level ports of an xhci controller as that's what's most common on systems, + * though we'll search all the descendants. + */ +static void +topo_usb_acpi_match(topo_mod_t *mod, topo_usb_controller_t *tuc) +{ + topo_usb_port_t *p; + + for (p = topo_list_next(&tuc->tuc_ports); p != NULL; + p = topo_list_next(p)) { + topo_usb_port_t *match; + + if ((match = topo_usb_port_match_controller(tuc, B_TRUE, + topo_usb_acpi_port_match, p)) != NULL) { + VERIFY3P(p, !=, match); + topo_usb_port_merge(p, match); + topo_mod_free(mod, match, sizeof (topo_usb_port_t)); + } + } +} + +static boolean_t +topo_usb_metadata_match(topo_usb_port_t *port, void *arg) +{ + topo_usb_meta_port_path_t *path = arg; + topo_usb_lport_t *l; + + if (path->tmpp_type != TOPO_USB_T_ACPI) + return (B_FALSE); + + for (l = topo_list_next(&port->tup_lports); l != NULL; + l = topo_list_next(l)) { + if (l->tul_acpi_name != NULL && strcmp(path->tmpp_path, + l->tul_acpi_name) == 0) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +/* + * We've found metadata describing the USB ports. We need to now go through and + * try to match that data up to actual nodes. + */ +static void +topo_usb_apply_metadata(topo_mod_t *mod, topo_usb_t *usb) +{ + topo_usb_meta_port_t *m; + + for (m = topo_list_next(&usb->tu_metadata); m != NULL; + m = topo_list_next(m)) { + topo_usb_port_t *p, *sink = NULL; + topo_usb_meta_port_path_t *path; + boolean_t remove = B_FALSE; + + /* + * If this is a chassis node, we'll remove the port and move it + * to the chassis. + */ + if (m->tmp_flags & TOPO_USB_F_CHASSIS) { + remove = B_TRUE; + } + + for (path = topo_list_next(&m->tmp_paths); path != NULL; + path = topo_list_next(path)) { + topo_mod_dprintf(mod, "considering metadata path %s", + path->tmpp_path); + if ((p = topo_usb_port_match(usb, remove, + topo_usb_metadata_match, path)) == NULL) + continue; + topo_mod_dprintf(mod, "matched path to a logical port"); + p->tup_meta = m; + + /* + * Check if we can move this to the Chassis. We should + * always do this on the first port in a group. However, + * if it's a match candidate, then it will have already + * been appended. + */ + if ((m->tmp_flags & TOPO_USB_F_CHASSIS) != 0 && + sink == NULL) { + topo_list_append(&usb->tu_chassis_ports, p); + usb->tu_nchassis_ports++; + } + + if ((usb->tu_meta_flags & TOPO_USB_M_METADATA_MATCH) != + 0) { + if (sink == NULL) { + sink = p; + remove = B_TRUE; + } else { + VERIFY3P(p, !=, sink); + topo_usb_port_merge(sink, p); + topo_mod_free(mod, p, + sizeof (topo_usb_port_t)); + } + continue; + } + + break; + } + + } +} + +static int +topo_usb_gather(topo_mod_t *mod, topo_usb_t *usb, tnode_t *pnode) +{ + int ret; + + if ((ret = topo_usb_load_metadata(mod, pnode, &usb->tu_metadata, + &usb->tu_meta_flags)) != 0) { + topo_mod_dprintf(mod, "failed to read usb metadata"); + return (-1); + } + topo_mod_dprintf(mod, "loaded metadata flags: %d", usb->tu_meta_flags); + + if (!topo_usb_gather_devcfg(mod, usb)) { + topo_mod_dprintf(mod, "encountered fatal error while " + "gathering physical data"); + return (-1); + } + + if ((usb->tu_meta_flags & TOPO_USB_M_NO_ACPI) == 0 && + !topo_usb_gather_acpi(mod, usb)) { + topo_mod_dprintf(mod, "encountered fatal error while " + "gathering ACPI data"); + return (-1); + } + + if ((usb->tu_meta_flags & TOPO_USB_M_ACPI_MATCH) != 0) { + topo_usb_controller_t *c; + + for (c = topo_list_next(&usb->tu_controllers); c != NULL; + c = topo_list_next(c)) { + if (c->tuc_driver == TOPO_USB_D_XHCI) { + topo_usb_acpi_match(mod, c); + } + } + } + + topo_usb_apply_metadata(mod, usb); + + return (0); +} + +static int +topo_usb_port_properties(topo_mod_t *mod, tnode_t *tn, topo_usb_port_t *port) +{ + int err; + char **strs = NULL; + uint_t i; + topo_usb_lport_t *l; + char *label; + const char *ptype; + size_t strlen; + + strlen = sizeof (char *) * MAX(port->tup_nlports, + TOPO_PROP_USB_PORT_NATTRS); + if ((strs = topo_mod_zalloc(mod, strlen)) == NULL) { + return (-1); + } + + label = NULL; + if (port->tup_meta != NULL) { + label = port->tup_meta->tmp_label; + } + + if (port->tup_meta != NULL && port->tup_meta->tmp_port_type != + USB_TOPO_PORT_TYPE_DEFAULT) { + ptype = + topo_usb_port_type_to_string(port->tup_meta->tmp_port_type); + } else { + ptype = topo_usb_port_type_to_string(port->tup_port_type); + } + + if (topo_pgroup_create(tn, &topo_usb_port_pgroup, &err) != 0) { + topo_mod_dprintf(mod, "failed to create property group %s: " + "%s\n", TOPO_PGROUP_USB_PORT, topo_strerror(err)); + goto error; + + } + + if (label != NULL && topo_node_label_set(tn, label, &err) != 0) { + topo_mod_dprintf(mod, "failed to set label on port: %s", + topo_strerror(err)); + goto error; + } + + if (ptype != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PORT, + TOPO_PROP_USB_PORT_TYPE, TOPO_PROP_IMMUTABLE, ptype, &err) != 0) { + topo_mod_dprintf(mod, "failed to set %s property: %s", + TOPO_PROP_USB_PORT_TYPE, topo_strerror(err)); + goto error; + } + + for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL; + l = topo_list_next(l)) { + char *vers; + int j; + + switch (l->tul_protocol) { + case TOPO_USB_P_1x: + vers = "1.x"; + break; + case TOPO_USB_P_20: + vers = "2.0"; + break; + case TOPO_USB_P_30: + vers = "3.0"; + break; + case TOPO_USB_P_31: + vers = "3.1"; + break; + default: + continue; + } + + /* + * Make sure we don't already have this string. This can happen + * when we have an ehci port and xhci support that both provide + * USB 2.0 service. + */ + for (j = 0; j < i; j++) { + if (strcmp(strs[j], vers) == 0) + break; + } + + if (j < i) + continue; + strs[i++] = vers; + } + + if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT, + TOPO_PROP_USB_PORT_VERSIONS, TOPO_PROP_IMMUTABLE, + (const char **)strs, i, &err) != 0) { + topo_mod_dprintf(mod, "failed to set %s property: %s", + TOPO_PROP_USB_PORT_VERSIONS, topo_strerror(err)); + goto error; + } + + i = 0; + if (port->tup_pld_valid && port->tup_pld.UserVisible != 0 && + port->tup_port_connected == TOPO_USB_C_CONNECTED) { + strs[i++] = TOPO_PROP_USB_PORT_A_VISIBLE; + } else if (port->tup_port_connected == TOPO_USB_C_CONNECTED) { + strs[i++] = TOPO_PROP_USB_PORT_A_CONNECTED; + } else if (port->tup_port_connected == TOPO_USB_C_DISCONNECTED) { + strs[i++] = TOPO_PROP_USB_PORT_A_DISCONNECTED; + } + + if (port->tup_meta != NULL) { + if (port->tup_meta->tmp_flags & TOPO_USB_F_INTERNAL) { + strs[i++] = TOPO_PROP_USB_PORT_A_INTERNAL; + } + + if (port->tup_meta->tmp_flags & TOPO_USB_F_EXTERNAL) { + strs[i++] = TOPO_PROP_USB_PORT_A_EXTERNAL; + } + } + + if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT, + TOPO_PROP_USB_PORT_ATTRIBUTES, TOPO_PROP_IMMUTABLE, + (const char **)strs, i, &err) != 0) { + topo_mod_dprintf(mod, "failed to set %s property: %s", + TOPO_PROP_USB_PORT_VERSIONS, topo_strerror(err)); + goto error; + } + + for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL; + l = topo_list_next(l)) { + strs[i++] = l->tul_name; + } + + if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT, + TOPO_PROP_USB_PORT_LPORTS, TOPO_PROP_IMMUTABLE, + (const char **)strs, i, &err) != 0) { + topo_mod_dprintf(mod, "failed to set %s propert: %s", + TOPO_PROP_USB_PORT_LPORTS, topo_strerror(err)); + goto error; + } + + err = 0; +error: + if (strs != NULL) { + topo_mod_free(mod, strs, strlen); + } + + if (err != 0) { + return (topo_mod_seterrno(mod, err)); + } + + return (err); +} + +/* + * Create a disk node under the scsa2usb node. When we have an scsa2usb node, + * we'll have a child devinfo which is a disk. To successfully enumerate this, + * we need to find the child node (which should be our only direct descendent) + * and get its devfs path. From there we can construct a 'binding' property + * group with the 'occupantpath' property that points to the module. At that + * point we can invoke the disk enumerator. + */ +static int +topo_usb_enum_scsa2usb(topo_mod_t *mod, tnode_t *tn, topo_usb_lport_t *lport) +{ + int ret; + di_node_t child; + char *devfs = NULL; + topo_instance_t min = 0, max = 0; + + if ((child = di_child_node(lport->tul_device)) == DI_NODE_NIL || + strcmp("disk", di_node_name(child)) != 0) { + return (0); + } + + if ((devfs = di_devfs_path(child)) == NULL) { + topo_mod_dprintf(mod, "failed to get USB disk child device " + "devfs path"); + return (topo_mod_seterrno(mod, EMOD_NOMEM)); + } + + if (topo_mod_load(mod, DISK, TOPO_VERSION) == NULL) { + topo_mod_dprintf(mod, "failed to load disk module: %s", + topo_mod_errmsg(mod)); + goto error; + } + + if (topo_pgroup_create(tn, &topo_binding_pgroup, &ret) != 0) { + topo_mod_dprintf(mod, "failed to create \"binding\" " + "property group: %s", topo_strerror(ret)); + goto error; + } + + if (topo_prop_set_string(tn, TOPO_PGROUP_BINDING, + TOPO_BINDING_OCCUPANT, TOPO_PROP_IMMUTABLE, devfs, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_IO_MODULE, topo_strerror(ret)); + goto error; + } + di_devfs_path_free(devfs); + devfs = NULL; + + if (topo_node_range_create(mod, tn, DISK, min, max) != 0) { + topo_mod_dprintf(mod, "failed to create disk node range %s: %s", + devfs, topo_mod_errmsg(mod)); + goto error; + } + + if (topo_mod_enumerate(mod, tn, DISK, DISK, min, max, NULL) != 0) { + topo_mod_dprintf(mod, "failed to create disk node %s: %s", + devfs, topo_mod_errmsg(mod)); + goto error; + } + + return (0); + +error: + di_devfs_path_free(devfs); + return (-1); +} + +static int +topo_usb_enum_port_children(topo_mod_t *mod, tnode_t *pn, + topo_usb_lport_t *plport) +{ + int ret; + topo_usb_port_t *port; + topo_instance_t min = 0, i; + + if ((ret = port_range_create(mod, pn, min, plport->tul_nports)) != 0) { + topo_mod_dprintf(mod, "failed to create port range [%u, %u) " + "for child hub", 0, plport->tul_nports); + return (ret); + } + + for (i = 0, port = topo_list_next(&plport->tul_ports); port != NULL; + port = topo_list_next(port)) { + tnode_t *tn; + if ((ret = port_create_usb(mod, pn, i, &tn)) != 0) + return (ret); + + if ((ret = topo_usb_port_properties(mod, tn, port)) != 0) { + return (ret); + } + + if ((ret = topo_usb_enum_device(mod, tn, port)) != 0) + return (ret); + + i++; + } + + return (0); +} + +/* + * Enumerate the requested device. Depending on the driver associated with it + * (if any), we may have to create child nodes. + */ +static int +topo_usb_enum_lport(topo_mod_t *mod, tnode_t *pn, topo_usb_port_t *port, + topo_usb_lport_t *lport, topo_instance_t topo_inst) +{ + int ret, inst; + int *vendid = NULL, *prodid = NULL, *revid = NULL, *release = NULL; + char *vend = NULL, *prod = NULL, *serial = NULL, *speed = NULL; + char *driver, *devfs; + char revbuf[32], relbuf[32]; + tnode_t *tn = NULL; + di_prop_t prop = DI_PROP_NIL; + nvlist_t *auth = NULL, *fmri = NULL, *modnvl = NULL; + + /* + * Look up the information we'll need to create the usb-properties. We + * do this first because this information is often part of the FMRI. + */ + for (prop = di_prop_next(lport->tul_device, DI_PROP_NIL); + prop != DI_PROP_NIL; prop = di_prop_next(lport->tul_device, prop)) { + const char *pname = di_prop_name(prop); + + if (strcmp(pname, "usb-vendor-id") == 0) { + if (di_prop_ints(prop, &vendid) != 1) + vendid = NULL; + } else if (strcmp(pname, "usb-product-id") == 0) { + if (di_prop_ints(prop, &prodid) != 1) + prodid = NULL; + } else if (strcmp(pname, "usb-revision-id") == 0) { + if (di_prop_ints(prop, &revid) != 1) { + revid = NULL; + } else { + (void) snprintf(revbuf, sizeof (revbuf), "%x", + *revid); + } + } else if (strcmp(pname, "usb-release") == 0) { + if (di_prop_ints(prop, &release) != 1) { + release = NULL; + } else { + (void) snprintf(relbuf, sizeof (relbuf), + "%x.%x", *release >> 8, + (*release >> 4) & 0xf); + } + } else if (strcmp(pname, "usb-vendor-name") == 0) { + if (di_prop_strings(prop, &vend) != 1) + vend = NULL; + } else if (strcmp(pname, "usb-product-name") == 0) { + if (di_prop_strings(prop, &prod) != 1) + prod = NULL; + } else if (strcmp(pname, "usb-serialno") == 0) { + if (di_prop_strings(prop, &serial) != 1) + serial = NULL; + } else if (strcmp(pname, "full-speed") == 0) { + speed = "full-speed"; + } else if (strcmp(pname, "low-speed") == 0) { + speed = "low-speed"; + } else if (strcmp(pname, "high-speed") == 0) { + speed = "high-speed"; + } else if (strcmp(pname, "super-speed") == 0) { + speed = "super-speed"; + } + } + + driver = di_driver_name(lport->tul_device); + inst = di_instance(lport->tul_device); + devfs = di_devfs_path(lport->tul_device); + + if ((auth = topo_mod_auth(mod, pn)) == NULL) { + topo_mod_dprintf(mod, "failed to get authority for USB device: " + "%s", topo_mod_errmsg(mod)); + goto error; + } + + if ((fmri = topo_mod_hcfmri(mod, pn, FM_HC_SCHEME_VERSION, USB_DEVICE, + topo_inst, NULL, auth, prod, revbuf, serial)) == NULL) { + topo_mod_dprintf(mod, "failed to generate fmri for USB " + "device %s: %s", di_devfs_path(lport->tul_device), + topo_mod_errmsg(mod)); + goto error; + } + + if ((tn = topo_node_bind(mod, pn, USB_DEVICE, topo_inst, fmri)) == + NULL) { + topo_mod_dprintf(mod, "failed to bind USB device node: %s", + topo_mod_errmsg(mod)); + goto error; + } + + /* + * In general, we expect a USB device to be its own FRU. There are some + * exceptions to this, for example, a built-in hub. However, it's hard + * for us to generally know. It may be nice to allow the platform to + * override this in the future. + */ + if (topo_node_fru_set(tn, fmri, 0, &ret) != 0) { + topo_mod_dprintf(mod, "failed to set FRU: %s", + topo_strerror(ret)); + (void) topo_mod_seterrno(mod, ret); + goto error; + } + + /* + * Inherit the label from the port on the device. This is intended to + * only go a single way. + */ + if (port->tup_meta != NULL && port->tup_meta->tmp_label != NULL && + topo_node_label_set(tn, port->tup_meta->tmp_label, &ret) != 0) { + topo_mod_dprintf(mod, "failed to set label on device: %s", + topo_strerror(ret)); + goto error; + } + + /* + * USB-properties + */ + if (topo_pgroup_create(tn, &topo_usb_props_pgroup, &ret) != 0) { + topo_mod_dprintf(mod, "failed to create \"usb-properties\" " + "property group: %s", topo_strerror(ret)); + goto error; + } + + if (topo_prop_set_uint32(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_PORT, TOPO_PROP_IMMUTABLE, lport->tul_portno, + &ret) != 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_PORT, topo_strerror(ret)); + goto error; + } + + if (vendid != NULL && topo_prop_set_int32(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_VID, TOPO_PROP_IMMUTABLE, *vendid, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_VID, topo_strerror(ret)); + goto error; + } + + if (prodid != NULL && topo_prop_set_int32(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_PID, TOPO_PROP_IMMUTABLE, *prodid, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_PID, topo_strerror(ret)); + goto error; + } + + if (revid != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_REV, TOPO_PROP_IMMUTABLE, revbuf, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_REV, topo_strerror(ret)); + goto error; + } + + if (release != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_VERSION, TOPO_PROP_IMMUTABLE, relbuf, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_VERSION, topo_strerror(ret)); + goto error; + } + + if (vend != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_VNAME, TOPO_PROP_IMMUTABLE, vend, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_VNAME, topo_strerror(ret)); + goto error; + } + + if (prod != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_PNAME, TOPO_PROP_IMMUTABLE, prod, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_PNAME, topo_strerror(ret)); + goto error; + } + + if (serial != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_SN, TOPO_PROP_IMMUTABLE, serial, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_SN, topo_strerror(ret)); + goto error; + } + + if (speed != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS, + TOPO_PGROUP_USB_PROPS_SPEED, TOPO_PROP_IMMUTABLE, speed, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_PGROUP_USB_PROPS_SPEED, topo_strerror(ret)); + goto error; + } + + /* + * I/O pgroup + */ + if (topo_pgroup_create(tn, &topo_io_pgroup, &ret) != 0) { + topo_mod_dprintf(mod, "failed to create \"io\" " + "property group: %s", topo_strerror(ret)); + goto error; + } + + if (driver != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO, + TOPO_IO_DRIVER, TOPO_PROP_IMMUTABLE, driver, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_IO_DRIVER, topo_strerror(ret)); + goto error; + } + + if (inst != -1 && topo_prop_set_uint32(tn, TOPO_PGROUP_IO, + TOPO_IO_INSTANCE, TOPO_PROP_IMMUTABLE, inst, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_IO_INSTANCE, topo_strerror(ret)); + goto error; + } + + if (devfs != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO, + TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, devfs, &ret) != + 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_IO_DEV_PATH, topo_strerror(ret)); + goto error; + } + + if (driver != NULL && (modnvl = topo_mod_modfmri(mod, + FM_MOD_SCHEME_VERSION, driver)) != NULL && + topo_prop_set_fmri(tn, TOPO_PGROUP_IO, TOPO_IO_MODULE, + TOPO_PROP_IMMUTABLE, modnvl, &ret) != 0) { + topo_mod_dprintf(mod, "failed to create property %s: %s", + TOPO_IO_MODULE, topo_strerror(ret)); + goto error; + } + + /* + * Check the drivers to determine special behavior that we should do. + * The following are cases that we want to handle: + * + * o Creating disk nodes for scsa2usb devices + * o Creating children ports and searching them for hubd + */ + if (driver != NULL && strcmp(driver, "scsa2usb") == 0) { + if ((ret = topo_usb_enum_scsa2usb(mod, tn, lport)) != 0) + goto error; + } + + if (lport->tul_nports > 0 && driver != NULL && + strcmp(driver, "hubd") == 0) { + if ((ret = topo_usb_enum_port_children(mod, tn, lport)) != 0) + goto error; + } + + di_devfs_path_free(devfs); + nvlist_free(fmri); + nvlist_free(auth); + nvlist_free(modnvl); + return (0); + +error: + topo_node_unbind(tn); + di_devfs_path_free(devfs); + nvlist_free(fmri); + nvlist_free(auth); + nvlist_free(modnvl); + return (-1); +} + +static int +topo_usb_enum_device(topo_mod_t *mod, tnode_t *pn, topo_usb_port_t *port) +{ + int ret; + topo_instance_t i, max; + topo_usb_lport_t *l; + + max = 0; + for (l = topo_list_next(&port->tup_lports); l != NULL; + l = topo_list_next(l)) { + if (l->tul_device != DI_NODE_NIL) + max++; + } + + if (max == 0) { + return (0); + } + + if ((ret = topo_node_range_create(mod, pn, USB_DEVICE, 0, max - 1)) != + 0) { + return (-1); + } + + for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL; + l = topo_list_next(l)) { + if (l->tul_device != DI_NODE_NIL) { + topo_mod_dprintf(mod, "enumerating device on lport " + "%u, log inst %d", l->tul_portno, i); + if ((ret = topo_usb_enum_lport(mod, pn, port, l, + i)) != 0) { + return (ret); + } + i++; + } + } + + return (0); +} + +static int +topo_usb_enum_controller(topo_mod_t *mod, tnode_t *pnode, + topo_usb_controller_t *c, topo_instance_t base) +{ + int ret; + topo_usb_port_t *port; + + if (c->tuc_enumed) + return (0); + + c->tuc_enumed = B_TRUE; + if (c->tuc_nports == 0) + return (0); + + for (port = topo_list_next(&c->tuc_ports); port != NULL; + port = topo_list_next(port)) { + tnode_t *tn; + if ((ret = port_create_usb(mod, pnode, base, &tn)) != 0) + return (ret); + + if ((ret = topo_usb_port_properties(mod, tn, port)) != 0) { + return (ret); + } + + if ((ret = topo_usb_enum_device(mod, tn, port)) != 0) + return (ret); + + base++; + } + + return (0); +} + +static int +topo_usb_enum_mobo(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb) +{ + int ret; + topo_usb_controller_t *c; + topo_instance_t inst = 0; + + /* + * First count the number of ports, so we can create the right range. + * Then go back and actually create things. Some of the ports here may + * be actually on the chassis, that's OK, we don't mind over counting + * here. + */ + for (c = topo_list_next(&usb->tu_controllers); c != NULL; + c = topo_list_next(c)) { + inst += c->tuc_nports; + } + + if ((ret = port_range_create(mod, pnode, 0, inst)) != 0) { + topo_mod_dprintf(mod, "failed to create port range [%u, %u) " + "for mobo", 0, inst); + return (ret); + } + + inst = 0; + for (c = topo_list_next(&usb->tu_controllers); c != NULL; + c = topo_list_next(c)) { + if (c->tuc_enumed) + continue; + if ((ret = topo_usb_enum_controller(mod, pnode, c, inst)) != + 0) { + return (ret); + } + inst += c->tuc_nports; + } + + return (0); +} + +static int +topo_usb_enum_pci(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb, + di_node_t din) +{ + int ret; + topo_usb_controller_t *c; + topo_instance_t min = 0; + + for (c = topo_list_next(&usb->tu_controllers); c != NULL; + c = topo_list_next(c)) { + if (din == c->tuc_devinfo) { + break; + } + } + + if (c == NULL) { + return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM)); + } + + if ((ret = port_range_create(mod, pnode, min, c->tuc_nports)) != 0) { + topo_mod_dprintf(mod, "failed to create port range [%u, %u) " + "for controller %s", min, c->tuc_nports, c->tuc_path); + return (ret); + } + + return (topo_usb_enum_controller(mod, pnode, c, min)); +} + +static int +topo_usb_enum_chassis(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb) +{ + int ret; + topo_usb_port_t *p; + topo_instance_t base = 0; + + if (usb->tu_nchassis_ports == 0) + return (0); + + if ((ret = port_range_create(mod, pnode, 0, usb->tu_nchassis_ports)) != + 0) { + topo_mod_dprintf(mod, "failed to create port range [%u, %u) " + "for mobo", 0, usb); + return (ret); + } + + for (p = topo_list_next(&usb->tu_chassis_ports); p != NULL; + p = topo_list_next(p)) { + tnode_t *tn; + if ((ret = port_create_usb(mod, pnode, base, &tn)) != 0) + return (ret); + + if ((ret = topo_usb_port_properties(mod, tn, p)) != 0) { + return (ret); + } + + if ((ret = topo_usb_enum_device(mod, tn, p)) != 0) + return (ret); + + base++; + } + + return (0); +} + +/* ARGSUSED */ +static int +topo_usb_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, + topo_instance_t min, topo_instance_t max, void *modarg, void *data) +{ + topo_usb_t *usb; + topo_usb_type_t type; + + if (strcmp(name, USB_PCI) == 0) { + type = TOPO_USB_PCI; + } else if (strcmp(name, USB_MOBO) == 0) { + type = TOPO_USB_MOBO; + } else if (strcmp(name, USB_CHASSIS) == 0) { + type = TOPO_USB_CHASSIS; + } else { + topo_mod_dprintf(mod, "usb_enum: asked to enumerate unknown " + "component: %s\n", name); + return (-1); + } + + if (type == TOPO_USB_PCI && data == NULL) { + topo_mod_dprintf(mod, "usb_enum: missing argument to " + "PCI controller enum"); + return (-1); + } else if (type != TOPO_USB_PCI && data != NULL) { + topo_mod_dprintf(mod, "extraneous argument to non-controller " + "enum %s", name); + return (-1); + } + + if ((usb = topo_mod_getspecific(mod)) == NULL) { + return (-1); + } + + if (!usb->tu_enum_done) { + if (topo_usb_gather(mod, usb, pnode) != 0) + return (-1); + usb->tu_enum_done = B_TRUE; + } + + /* + * Now that we've built up the topo nodes, enumerate the specific nodes + * based on the requested type. + */ + if (type == TOPO_USB_PCI) { + return (topo_usb_enum_pci(mod, pnode, usb, data)); + } else if (type == TOPO_USB_MOBO) { + return (topo_usb_enum_mobo(mod, pnode, usb)); + } else if (type == TOPO_USB_CHASSIS) { + return (topo_usb_enum_chassis(mod, pnode, usb)); + } + + return (0); +} + +static const topo_modops_t usb_ops = { + topo_usb_enum, NULL +}; + +static topo_modinfo_t usb_mod = { + USB, FM_FMRI_SCHEME_HC, USB_VERSION, &usb_ops +}; + +static void +topo_usb_port_free(topo_mod_t *mod, topo_usb_port_t *p) +{ + topo_usb_lport_t *lport; + + while ((lport = topo_list_next(&p->tup_lports)) != NULL) { + topo_usb_port_t *child; + + topo_list_delete(&p->tup_lports, lport); + while ((child = topo_list_next(&lport->tul_ports)) != NULL) { + topo_list_delete(&lport->tul_ports, child); + topo_usb_port_free(mod, child); + } + topo_mod_free(mod, lport, sizeof (topo_usb_lport_t)); + } + + topo_mod_free(mod, p, sizeof (topo_usb_port_t)); +} + +static void +topo_usb_free(topo_mod_t *mod, topo_usb_t *usb) +{ + topo_usb_controller_t *c; + topo_usb_port_t *p; + + if (usb == NULL) + return; + + while ((p = topo_list_next(&usb->tu_chassis_ports)) != NULL) { + topo_list_delete(&usb->tu_chassis_ports, p); + topo_usb_port_free(mod, p); + } + + while ((c = topo_list_next(&usb->tu_controllers)) != NULL) { + + topo_list_delete(&usb->tu_controllers, c); + di_devfs_path_free(c->tuc_path); + + while ((p = topo_list_next(&c->tuc_ports)) != NULL) { + topo_list_delete(&c->tuc_ports, p); + topo_usb_port_free(mod, p); + } + topo_mod_free(mod, c, sizeof (topo_usb_controller_t)); + } + + topo_usb_free_metadata(mod, &usb->tu_metadata); + + /* + * The devinfo handle came from fm, don't do anything ourselevs. + */ + usb->tu_devinfo = DI_NODE_NIL; + + topo_mod_free(mod, usb, sizeof (topo_usb_t)); +} + +static topo_usb_t * +topo_usb_alloc(topo_mod_t *mod) +{ + topo_usb_t *usb = NULL; + + if ((usb = topo_mod_zalloc(mod, sizeof (topo_usb_t))) == NULL) { + goto free; + } + + if ((usb->tu_devinfo = topo_mod_devinfo(mod)) == DI_NODE_NIL) { + goto free; + } + + return (usb); + +free: + topo_usb_free(mod, usb); + return (NULL); +} + +int +_topo_init(topo_mod_t *mod, topo_version_t version) +{ + topo_usb_t *usb; + + if (getenv("TOPOUSBDEBUG") != NULL) + topo_mod_setdebug(mod); + + topo_mod_dprintf(mod, "_mod_init: initializing %s enumerator\n", USB); + + if (version != USB_VERSION) { + return (-1); + } + + if ((usb = topo_usb_alloc(mod)) == NULL) { + return (-1); + } + + if (topo_mod_register(mod, &usb_mod, TOPO_VERSION) != 0) { + topo_usb_free(mod, usb); + return (-1); + } + + topo_mod_setspecific(mod, usb); + + return (0); +} + +void +_topo_fini(topo_mod_t *mod) +{ + topo_usb_free(mod, topo_mod_getspecific(mod)); + topo_mod_setspecific(mod, NULL); +} diff --git a/usr/src/lib/fm/topo/modules/common/usb/topo_usb.h b/usr/src/lib/fm/topo/modules/common/usb/topo_usb.h new file mode 100644 index 0000000000..89271836bc --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/usb/topo_usb.h @@ -0,0 +1,40 @@ +/* + * 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 (c) 2018, Joyent, Inc. + */ + +#ifndef _TOPO_USB_H +#define _TOPO_USB_H + +#include <libdevinfo.h> + +/* + * Common USB module header file. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define USB "usb" +#define USB_VERSION 1 + +#define USB_PCI "usb-pci" +#define USB_MOBO "usb-mobo" +#define USB_CHASSIS "usb-chassis" + +#ifdef __cplusplus +} +#endif + +#endif /* _TOPO_USB_H */ diff --git a/usr/src/lib/fm/topo/modules/common/usb/topo_usb_acpi.c b/usr/src/lib/fm/topo/modules/common/usb/topo_usb_acpi.c new file mode 100644 index 0000000000..b8e169a1cf --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/usb/topo_usb_acpi.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (c) 2018, Joyent, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * This allows us to embed the decoding of the _PLD data structure method. While + * this is normally part of the broader ACPI code dump, it has been pulled out + * to simplify the management and required set of ACPI tools. The _PLD is the + * physical location of the device. It is a series of fields that is + * theoretically supposed to tell you information about where a given device is, + * its shape, color, etc. This data is only as good as the firwmare that + * provides it. This is defined in the ACPI specification in section 6.1.8 (ACPI + * 6.2). + */ + +#include <strings.h> +#include "topo_usb_int.h" + +boolean_t +usbtopo_decode_pld(uint8_t *buf, size_t len, ACPI_PLD_INFO *infop) +{ + uint32_t *buf32p; + size_t i; + + if (buf == NULL || len < ACPI_PLD_REV1_BUFFER_SIZE) + return (B_FALSE); + + /* + * Look through the array of bytes that we have. We've found some cases + * where we have buffers that are basically all zeros aside from the + * revision, which is revision one. + */ + for (i = 1; i < len; i++) { + if (buf[i] != 0) + break; + } + if (i == len && buf[0] == 0x01) + return (B_FALSE); + + buf32p = (uint32_t *)buf; + bzero(infop, sizeof (*infop)); + + /* First 32-bit DWord */ + infop->Revision = ACPI_PLD_GET_REVISION(&buf32p[0]); + infop->IgnoreColor = ACPI_PLD_GET_IGNORE_COLOR(&buf32p[0]); + infop->Red = ACPI_PLD_GET_RED(&buf32p[0]); + infop->Green = ACPI_PLD_GET_GREEN(&buf32p[0]); + infop->Blue = ACPI_PLD_GET_BLUE(&buf32p[0]); + + /* Second 32-bit DWord */ + infop->Width = ACPI_PLD_GET_WIDTH(&buf32p[1]); + infop->Height = ACPI_PLD_GET_HEIGHT(&buf32p[1]); + + /* Third 32-bit DWord */ + infop->UserVisible = ACPI_PLD_GET_USER_VISIBLE(&buf32p[2]); + infop->Dock = ACPI_PLD_GET_DOCK(&buf32p[2]); + infop->Lid = ACPI_PLD_GET_LID(&buf32p[2]); + infop->Panel = ACPI_PLD_GET_PANEL(&buf32p[2]); + infop->VerticalPosition = ACPI_PLD_GET_VERTICAL(&buf32p[2]); + infop->HorizontalPosition = ACPI_PLD_GET_HORIZONTAL(&buf32p[2]); + infop->Shape = ACPI_PLD_GET_SHAPE(&buf32p[2]); + infop->GroupOrientation = ACPI_PLD_GET_ORIENTATION(&buf32p[2]); + infop->GroupToken = ACPI_PLD_GET_TOKEN(&buf32p[2]); + infop->GroupPosition = ACPI_PLD_GET_POSITION(&buf32p[2]); + infop->Bay = ACPI_PLD_GET_BAY(&buf32p[2]); + + /* Fourth 32-bit DWord */ + infop->Ejectable = ACPI_PLD_GET_EJECTABLE(&buf32p[3]); + infop->OspmEjectRequired = ACPI_PLD_GET_OSPM_EJECT(&buf32p[3]); + infop->CabinetNumber = ACPI_PLD_GET_CABINET(&buf32p[3]); + infop->CardCageNumber = ACPI_PLD_GET_CARD_CAGE(&buf32p[3]); + infop->Reference = ACPI_PLD_GET_REFERENCE(&buf32p[3]); + infop->Rotation = ACPI_PLD_GET_ROTATION(&buf32p[3]); + infop->Order = ACPI_PLD_GET_ORDER(&buf32p[3]); + + if (len >= ACPI_PLD_REV2_BUFFER_SIZE && infop->Revision >= 2) { + /* Fifth 32-bit DWord (Revision 2 of _PLD) */ + + infop->VerticalOffset = ACPI_PLD_GET_VERT_OFFSET(&buf32p[4]); + infop->HorizontalOffset = ACPI_PLD_GET_HORIZ_OFFSET(&buf32p[4]); + } + + return (B_TRUE); +} diff --git a/usr/src/lib/fm/topo/modules/common/usb/topo_usb_int.h b/usr/src/lib/fm/topo/modules/common/usb/topo_usb_int.h new file mode 100644 index 0000000000..20e397f496 --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/usb/topo_usb_int.h @@ -0,0 +1,74 @@ +/* + * 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 (c) 2018, Joyent, Inc. + */ + +#ifndef _TOPO_USB_INT_H +#define _TOPO_USB_INT_H + +#include <stdint.h> +#include <sys/types.h> +#include <fm/topo_list.h> +#include <fm/topo_mod.h> +#include <sys/fm/protocol.h> + +#include "acpi.h" +#include "accommon.h" +#include "acbuffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum topo_usb_path_type { + TOPO_USB_T_ACPI +} topo_usb_path_type_t; + +typedef struct topo_usb_meta_port_path { + topo_list_t tmpp_link; + topo_usb_path_type_t tmpp_type; + char *tmpp_path; +} topo_usb_meta_port_path_t; + +typedef enum topo_usb_meta_port_flags { + TOPO_USB_F_INTERNAL = 1 << 0, + TOPO_USB_F_EXTERNAL = 1 << 1, + TOPO_USB_F_CHASSIS = 1 << 2, +} topo_usb_meta_port_flags_t; + +typedef struct topo_usb_meta_port { + topo_list_t tmp_link; + topo_usb_meta_port_flags_t tmp_flags; + uint_t tmp_port_type; + char *tmp_label; + topo_list_t tmp_paths; +} topo_usb_meta_port_t; + +typedef enum topo_usb_meta_flags { + TOPO_USB_M_ACPI_MATCH = 1 << 0, + TOPO_USB_M_NO_ACPI = 1 << 1, + TOPO_USB_M_METADATA_MATCH = 1 << 2 +} topo_usb_meta_flags_t; + +extern int topo_usb_load_metadata(topo_mod_t *, tnode_t *, topo_list_t *, + topo_usb_meta_flags_t *); +extern void topo_usb_free_metadata(topo_mod_t *, topo_list_t *); + +typedef ACPI_PLD_INFO acpi_pld_info_t; +extern boolean_t usbtopo_decode_pld(uint8_t *, size_t, acpi_pld_info_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _TOPO_USB_INT_H */ diff --git a/usr/src/lib/fm/topo/modules/common/usb/topo_usb_metadata.c b/usr/src/lib/fm/topo/modules/common/usb/topo_usb_metadata.c new file mode 100644 index 0000000000..8e53aecbe5 --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/usb/topo_usb_metadata.c @@ -0,0 +1,471 @@ +/* + * 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 (c) 2018, Joyent, Inc. + */ + +/* + * This module parses the private file format used for describing + * platform-specific USB overrides. + * + * FILE FORMAT + * ----------- + * + * A USB topology file contains a series of lines which are separated by new + * lines. Leading and trailing whitespace on a line are ignored and empty lines + * are ignored as well. The '#' character is used as a comment character. There + * are a series of keywords that are supported which are used to indicate + * different control aspects. These keywords are all treated in a + * case-insensitive fashion. There are both top-level keywords and keywords that + * are only accepted within the context of a scope. + * + * Top-level keywords + * ------------------ + * + * The following keywords are accepted, but must not be found inside a nested + * scope: + * + * 'disable-acpi' Disables the use of ACPI for this platform. This + * includes getting information about the port's + * type and visibility. This implies + * 'disable-acpi-match'. + * + * 'disable-acpi-match' Disables the act of trying to match ports based + * on ACPI. + * + * + * 'enable-acpi-match' Explicitly enables ACPI port matching on the + * platform based on ACPI. + * + * 'enable-metadata-match' Enables port matching based on metadata. This is + * most commonly used on platforms that have ehci + * and xhci controllers that share ports. + * + * 'port' Begins a port stanza that describes a single + * physical port. This stanza will continue until + * the 'end-port' keyword is encountered. + * + * Port Keywords + * ------------- + * + * Some port keywords take arguments and others do not. When an argument exists, + * will occur on the subsequent line. Ports have a series of directives that + * describe metadata as well as directives that describe how to determine the + * port. + * + * 'label' Indicates that the next line contains the + * human-readable label for the port. + * + * 'chassis' Indicates that this port is part of the chassis + * and should not be enumerated elsewhere. + * + * 'external' Indicates that this port is externally visible. + * + * 'internal' Indicates that this port is internal to the + * chassis and cannot be accessed without opening + * the chassis. + * + * 'port-type' Indicates that the next line contains a number + * which corresponds to the type of the port. The + * port numbers are based on the ACPI table and + * may be in either base 10 or hexadecimal. + * + * 'acpi-path' Indicates that the next line contains an ACPI + * based name that matches the port. + * + * 'end-port' Closes the port-clause. + */ + +#include <libnvpair.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <fm/topo_list.h> +#include <fm/topo_mod.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <libnvpair.h> +#include <sys/debug.h> +#include <ctype.h> +#include <unistd.h> + +#include "topo_usb.h" +#include "topo_usb_int.h" + +/* + * Maximum number of characters we expect to encounter in a line. + */ +#define TOPO_USB_META_LINE_MAX 1000 + +/* + * This constant is the default set of flags that we'd like to apply when there + * is no configuration file present to determine the desired behavior. If one is + * present, we always defer to what it asks for. + * + * It's a difficult decision to enable ACPI by default or not. Unfortunately, + * we've encountered some systems where the ACPI information is wrong. However, + * we've encountered a larger number where it is correct. When it's correct, + * this greatly simplifies some of the work that we have to do. Our default + * disposition at the moment is to opt to decide its correct as that ends up + * giving us much better information. + */ +#define USB_TOPO_META_DEFAULT_FLAGS TOPO_USB_M_ACPI_MATCH + +typedef enum { + TOPO_USB_P_START, + TOPO_USB_P_PORT, + TOPO_USB_P_LABEL, + TOPO_USB_P_PORT_TYPE, + TOPO_USB_P_ACPI_PATH +} topo_usb_parse_state_t; + +typedef struct topo_usb_parse { + topo_usb_parse_state_t tp_state; + topo_list_t *tp_ports; + topo_usb_meta_port_t *tp_cport; + topo_usb_meta_flags_t tp_flags; +} topo_usb_parse_t; + +/* + * Read the next line in the file with content. Trim trailing and leading + * whitespace and trim comments out. If this results in an empty line, read the + * next. Returns zero if we hit EOF. Otherwise, returns one if data, or negative + * one if an error occurred. + */ +static int +topo_usb_getline(topo_mod_t *mod, char *buf, size_t len, FILE *f, char **first) +{ + while (fgets(buf, len, f) != NULL) { + char *c; + size_t i; + + if ((c = strrchr(buf, '\n')) == NULL) { + topo_mod_dprintf(mod, "failed to find new line in " + "metadata file"); + return (-1); + } + + while (isspace(*c) != 0 && c >= buf) { + *c = '\0'; + c--; + continue; + } + + if ((c = strchr(buf, '#')) != 0) { + *c = '\0'; + } + + for (i = 0; buf[i] != '\0'; i++) { + if (isspace(buf[i]) == 0) + break; + } + + if (buf[i] == '\0') + continue; + *first = &buf[i]; + return (1); + } + + return (0); +} + +static boolean_t +topo_usb_parse_start(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line) +{ + topo_usb_meta_port_t *port; + + VERIFY3S(parse->tp_state, ==, TOPO_USB_P_START); + VERIFY3P(parse->tp_cport, ==, NULL); + + if (strcasecmp(line, "disable-acpi") == 0) { + parse->tp_flags |= TOPO_USB_M_NO_ACPI; + parse->tp_flags &= ~TOPO_USB_M_ACPI_MATCH; + return (B_TRUE); + } else if (strcasecmp(line, "disable-acpi-match") == 0) { + parse->tp_flags &= ~TOPO_USB_M_ACPI_MATCH; + return (B_TRUE); + } else if (strcasecmp(line, "enable-acpi-match") == 0) { + parse->tp_flags |= TOPO_USB_M_ACPI_MATCH; + return (B_TRUE); + } else if (strcasecmp(line, "enable-metadata-match") == 0) { + parse->tp_flags |= TOPO_USB_M_METADATA_MATCH; + return (B_TRUE); + } else if (strcasecmp(line, "port") != 0) { + topo_mod_dprintf(mod, "expected 'port', encountered %s", + line); + return (B_FALSE); + } + + if ((port = topo_mod_zalloc(mod, sizeof (topo_usb_meta_port_t))) == + NULL) { + topo_mod_dprintf(mod, "failed to allocate metadata port"); + return (B_FALSE); + } + port->tmp_port_type = 0xff; + + parse->tp_cport = port; + parse->tp_state = TOPO_USB_P_PORT; + return (B_TRUE); +} + +static boolean_t +topo_usb_parse_port(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line) +{ + VERIFY3S(parse->tp_state, ==, TOPO_USB_P_PORT); + VERIFY3P(parse->tp_cport, !=, NULL); + + if (strcasecmp(line, "label") == 0) { + parse->tp_state = TOPO_USB_P_LABEL; + } else if (strcasecmp(line, "chassis") == 0) { + parse->tp_cport->tmp_flags |= TOPO_USB_F_CHASSIS; + } else if (strcasecmp(line, "external") == 0) { + parse->tp_cport->tmp_flags |= TOPO_USB_F_EXTERNAL; + } else if (strcasecmp(line, "internal") == 0) { + parse->tp_cport->tmp_flags |= TOPO_USB_F_INTERNAL; + } else if (strcasecmp(line, "port-type") == 0) { + parse->tp_state = TOPO_USB_P_PORT_TYPE; + } else if (strcasecmp(line, "acpi-path") == 0) { + parse->tp_state = TOPO_USB_P_ACPI_PATH; + } else if (strcasecmp(line, "end-port") == 0) { + topo_list_append(parse->tp_ports, parse->tp_cport); + parse->tp_cport = NULL; + parse->tp_state = TOPO_USB_P_START; + } else { + topo_mod_dprintf(mod, "illegal directive in port block: %s", + line); + return (B_FALSE); + } + + return (B_TRUE); +} + +static boolean_t +topo_usb_parse_label(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line) +{ + size_t i, len; + + VERIFY3S(parse->tp_state, ==, TOPO_USB_P_LABEL); + + len = strlen(line); + for (i = 0; i < len; i++) { + if (isascii(line[i]) == 0 || isprint(line[i]) == 0) { + topo_mod_dprintf(mod, "label character %llu is " + "invalid: 0x%x", i, line[i]); + return (B_FALSE); + } + } + + if (parse->tp_cport->tmp_label != NULL) { + topo_mod_strfree(mod, parse->tp_cport->tmp_label); + } + + if ((parse->tp_cport->tmp_label = topo_mod_strdup(mod, line)) == NULL) { + topo_mod_dprintf(mod, "failed to duplicate label for port"); + return (B_FALSE); + } + + parse->tp_state = TOPO_USB_P_PORT; + + return (B_TRUE); +} + +static boolean_t +topo_usb_parse_port_type(topo_mod_t *mod, topo_usb_parse_t *parse, + const char *line) +{ + unsigned long val; + char *eptr; + + VERIFY3S(parse->tp_state, ==, TOPO_USB_P_PORT_TYPE); + + errno = 0; + val = strtoul(line, &eptr, 0); + if (errno != 0 || *eptr != '\0' || val > UINT_MAX) { + topo_mod_dprintf(mod, "encountered bad value for port-type " + "line: %s", line); + return (B_FALSE); + } + + parse->tp_cport->tmp_port_type = (uint_t)val; + + parse->tp_state = TOPO_USB_P_PORT; + return (B_TRUE); +} + +static boolean_t +topo_usb_parse_path(topo_mod_t *mod, topo_usb_parse_t *parse, + topo_usb_path_type_t ptype, const char *line) +{ + char *fspath; + topo_usb_meta_port_path_t *path; + + VERIFY(parse->tp_state == TOPO_USB_P_ACPI_PATH); + VERIFY3P(parse->tp_cport, !=, NULL); + + if ((fspath = topo_mod_strdup(mod, line)) == NULL) { + topo_mod_dprintf(mod, "failed to duplicate path"); + return (B_FALSE); + } + + if ((path = topo_mod_zalloc(mod, sizeof (topo_usb_meta_port_path_t))) == + NULL) { + topo_mod_dprintf(mod, "failed to allocate meta port path " + "structure"); + topo_mod_strfree(mod, fspath); + return (B_FALSE); + } + + path->tmpp_type = ptype; + path->tmpp_path = fspath; + + topo_list_append(&parse->tp_cport->tmp_paths, path); + + parse->tp_state = TOPO_USB_P_PORT; + return (B_TRUE); +} + + +void +topo_usb_free_metadata(topo_mod_t *mod, topo_list_t *metadata) +{ + topo_usb_meta_port_t *mp; + + while ((mp = topo_list_next(metadata)) != NULL) { + topo_usb_meta_port_path_t *path; + + while ((path = topo_list_next((&mp->tmp_paths))) != NULL) { + topo_list_delete(&mp->tmp_paths, path); + topo_mod_strfree(mod, path->tmpp_path); + topo_mod_free(mod, path, + sizeof (topo_usb_meta_port_path_t)); + } + + topo_list_delete(metadata, mp); + topo_mod_strfree(mod, mp->tmp_label); + topo_mod_free(mod, mp, sizeof (topo_usb_meta_port_t)); + } +} + +int +topo_usb_load_metadata(topo_mod_t *mod, tnode_t *pnode, topo_list_t *list, + topo_usb_meta_flags_t *flagsp) +{ + int fd; + FILE *f = NULL; + char buf[TOPO_USB_META_LINE_MAX], *first, *prod; + int ret; + topo_usb_parse_t parse; + char pbuf[PATH_MAX]; + + *flagsp = USB_TOPO_META_DEFAULT_FLAGS; + + /* + * If no product string, just leave it as is and don't attempt to get + * metadata. + */ + if ((topo_prop_get_string(pnode, FM_FMRI_AUTHORITY, + FM_FMRI_AUTH_PRODUCT, &prod, &ret)) != 0) { + topo_mod_dprintf(mod, "skipping metadata load: failed to get " + "auth"); + return (0); + } + + if (snprintf(pbuf, sizeof (pbuf), "maps/%s-usb.usbtopo", prod) >= + sizeof (pbuf)) { + topo_mod_dprintf(mod, "skipping metadata load: product name " + "too long"); + topo_mod_strfree(mod, prod); + return (0); + } + topo_mod_strfree(mod, prod); + + if ((fd = topo_mod_file_search(mod, pbuf, O_RDONLY)) < 0) { + topo_mod_dprintf(mod, "skipping metadata load: couldn't find " + "%s", pbuf); + return (0); + } + + + if ((f = fdopen(fd, "r")) == NULL) { + topo_mod_dprintf(mod, "failed to fdopen metadata file %s: %s", + pbuf, strerror(errno)); + VERIFY0(close(fd)); + ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); + goto err; + } + + bzero(&parse, sizeof (parse)); + parse.tp_ports = list; + parse.tp_state = TOPO_USB_P_START; + + while ((ret = topo_usb_getline(mod, buf, sizeof (buf), f, &first)) != + 0) { + if (ret == -1) { + ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); + goto err; + } + + switch (parse.tp_state) { + case TOPO_USB_P_START: + if (!topo_usb_parse_start(mod, &parse, first)) { + ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); + goto err; + } + break; + case TOPO_USB_P_PORT: + if (!topo_usb_parse_port(mod, &parse, first)) { + ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); + goto err; + } + break; + case TOPO_USB_P_LABEL: + if (!topo_usb_parse_label(mod, &parse, first)) { + ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); + goto err; + } + break; + case TOPO_USB_P_PORT_TYPE: + if (!topo_usb_parse_port_type(mod, &parse, first)) { + ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); + goto err; + } + break; + + case TOPO_USB_P_ACPI_PATH: + if (!topo_usb_parse_path(mod, &parse, TOPO_USB_T_ACPI, + first)) { + ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); + goto err; + } + break; + } + } + + if (parse.tp_state != TOPO_USB_P_START) { + topo_mod_dprintf(mod, "metadata file didn't end in correct " + "state, failing"); + ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); + goto err; + } + + topo_mod_dprintf(mod, "successfully loaded metadata %s", pbuf); + VERIFY0(fclose(f)); + *flagsp = parse.tp_flags; + return (0); + +err: + if (f != NULL) + VERIFY0(fclose(f)); + topo_usb_free_metadata(mod, list); + return (ret); +} diff --git a/usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile b/usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile index 1a5c04ec39..f40bd2dfbc 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile +++ b/usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile @@ -30,6 +30,7 @@ CLASS = arch UTILDIR = ../../common/pcibus HBDIR = ../../common/hostbridge NICDIR = ../../common/nic +USBDIR = ../../common/usb UTILSRCS = did.c did_hash.c did_props.c util.c PCISRCS = pcibus.c pcibus_labels.c pcibus_hba.c @@ -39,4 +40,4 @@ include ../../Makefile.plugin LDLIBS += -ldevinfo -lsmbios -lpcidb -CPPFLAGS += -I$(UTILDIR) -I$(HBDIR) -I $(NICDIR) +CPPFLAGS += -I$(UTILDIR) -I$(HBDIR) -I$(NICDIR) -I$(USBDIR) diff --git a/usr/src/lib/fm/topo/modules/i86pc/x86pi/Makefile b/usr/src/lib/fm/topo/modules/i86pc/x86pi/Makefile index 4ac5dc7e1c..15cfd1a5ad 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/x86pi/Makefile +++ b/usr/src/lib/fm/topo/modules/i86pc/x86pi/Makefile @@ -34,6 +34,7 @@ TOPODIR = ../../../libtopo/common UTILDIR = ../../common/pcibus BRDIR = ../../common/hostbridge +USBDIR = ../../common/usb UTILSRCS = did.c did_hash.c did_props.c X86PISRCS = x86pi.c x86pi_bay.c x86pi_bboard.c x86pi_chassis.c \ x86pi_generic.c x86pi_hostbridge.c x86pi_subr.c @@ -44,7 +45,7 @@ include ../../Makefile.plugin LDLIBS += -lsmbios -ldevinfo -luutil -lpcidb CPPFLAGS += -I. -I$(ROOT)/usr/platform/i86pc/include -I$(TOPODIR) -CPPFLAGS += -I$(UTILDIR) -I$(BRDIR) +CPPFLAGS += -I$(UTILDIR) -I$(BRDIR) -I$(USBDIR) %.o: $(UTILDIR)/%.c $(COMPILE.c) -o $@ $< diff --git a/usr/src/lib/fm/topo/modules/sun4/pcibus/Makefile.pci b/usr/src/lib/fm/topo/modules/sun4/pcibus/Makefile.pci index 620a2c388d..0600e71993 100644 --- a/usr/src/lib/fm/topo/modules/sun4/pcibus/Makefile.pci +++ b/usr/src/lib/fm/topo/modules/sun4/pcibus/Makefile.pci @@ -29,6 +29,7 @@ CLASS = arch SUN4DIR = ../../sun4/$(MODULE) UTILDIR = ../../common/pcibus NICDIR = ../../common/nic +USBDIR = ../../common/usb HBDIR = ../../common/hostbridge UTILSRCS = did.c did_hash.c did_props.c util.c PCISRCS = pcibus.c pcibus_labels.c pci_sun4.c pcibus_hba.c @@ -38,7 +39,7 @@ MODULESRCS = $(PCISRCS) $(UTILSRCS) pci_$(ARCH).c include ../../Makefile.plugin LDLIBS += -ldevinfo -lsmbios -lpcidb -CPPFLAGS += -I$(SUN4DIR) -I$(UTILDIR) -I$(HBDIR) -I$(NICDIR) +CPPFLAGS += -I$(SUN4DIR) -I$(UTILDIR) -I$(HBDIR) -I$(NICDIR) -I$(USBDIR) %.o: $(SUN4DIR)/%.c $(COMPILE.c) -o $@ $< diff --git a/usr/src/pkg/manifests/service-fault-management.mf b/usr/src/pkg/manifests/service-fault-management.mf index 10fee761bf..d651843032 100644 --- a/usr/src/pkg/manifests/service-fault-management.mf +++ b/usr/src/pkg/manifests/service-fault-management.mf @@ -572,6 +572,7 @@ file path=usr/lib/fm/topo/plugins/ipmi.so mode=0555 file path=usr/lib/fm/topo/plugins/nic.so mode=0555 file path=usr/lib/fm/topo/plugins/ses.so mode=0555 file path=usr/lib/fm/topo/plugins/smbios.so mode=0555 +file path=usr/lib/fm/topo/plugins/usb.so mode=0555 file path=usr/lib/fm/topo/plugins/xfp.so mode=0555 # # Dictionaries, whether they are hardware-specific or not, are @@ -767,6 +768,9 @@ $(i386_ONLY)file \ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-slot-hc-topology.xml \ mode=0444 $(i386_ONLY)file \ + path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-usb.usbtopo \ + mode=0444 +$(i386_ONLY)file \ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-chassis-hc-topology.xml \ mode=0444 $(i386_ONLY)file \ @@ -779,6 +783,9 @@ $(i386_ONLY)file \ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-slot-hc-topology.xml \ mode=0444 $(i386_ONLY)file \ + path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-usb.usbtopo \ + mode=0444 +$(i386_ONLY)file \ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-6049P-E1CR36L-chassis-hc-topology.xml \ mode=0444 $(i386_ONLY)file \ @@ -788,6 +795,9 @@ $(i386_ONLY)file \ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-6049P-E1CR36L-slot-hc-topology.xml \ mode=0444 $(i386_ONLY)file \ + path=usr/platform/i86pc/lib/fm/topo/maps/SSG-6049P-E1CR36L-usb.usbtopo \ + mode=0444 +$(i386_ONLY)file \ path=usr/platform/i86pc/lib/fm/topo/maps/Sun-Fire-X4200-M2-disk-hc-topology.xml \ mode=0444 $(i386_ONLY)file \ @@ -1021,21 +1031,34 @@ $(sparc_ONLY)link path=usr/platform/SUNW,T5140/lib/fm/fmd/plugins/etm.so \ target=../../../../../sun4v/lib/fm/fmd/plugins/etm.so $(sparc_ONLY)link path=usr/platform/SUNW,USBRDT-5240/lib/fm/fmd/plugins/etm.so \ target=../../../../../sun4v/lib/fm/fmd/plugins/etm.so +$(i386_ONLY)link \ + path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3101-hc-topology.xml \ + target=SSG-2028R-ACR24L-hc-topology.xml # # symlinks for paticular topo maps # + $(i386_ONLY)link \ - path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3101-hc-topology.xml \ - target=SSG-2028R-ACR24L-hc-topology.xml + path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3101-usb.usbtopo \ + target=SSG-2028R-ACR24L-usb.usbtopo $(i386_ONLY)link \ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3102-hc-topology.xml \ target=SSG-2028R-ACR24L-hc-topology.xml $(i386_ONLY)link \ + path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3102-usb.usbtopo \ + target=SSG-2028R-ACR24L-usb.usbtopo +$(i386_ONLY)link \ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3302-hc-topology.xml \ target=Joyent-Compute-Platform-3301-hc-topology.xml $(i386_ONLY)link \ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-M12G5-hc-topology.xml \ target=SSG-2029P-ACR24L-hc-topology.xml $(i386_ONLY)link \ + path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-M12G5-usb.usbtopo \ + target=SSG-2029P-ACR24L-usb.usbtopo +$(i386_ONLY)link \ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-S10G5-hc-topology.xml \ target=SSG-6049P-E1CR36L-hc-topology.xml +$(i386_ONLY)link \ + path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-S10G5-usb.usbtopo \ + target=SSG-6049P-E1CR36L-usb.usbtopo diff --git a/usr/src/uts/common/io/usb/hcd/xhci/xhci.c b/usr/src/uts/common/io/usb/hcd/xhci/xhci.c index 84bd1a13c9..eabdd4fd9e 100644 --- a/usr/src/uts/common/io/usb/hcd/xhci/xhci.c +++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci.c @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2017, Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. */ /* @@ -86,39 +86,39 @@ * * There are four different kinds of endpoints: * - * BULK These transfers are large transfers of data to or from - * a device. The most common use for bulk transfers is for - * mass storage devices. Though they are often also used by - * network devices and more. Bulk endpoints do not have an - * explicit time component to them. They are always used - * for one-shot transfers. - * - * CONTROL These transfers are used to manipulate devices - * themselves and are used for USB protocol level - * operations (whether device-specific, class-specific, or - * generic across all of USB). Unlike other transfers, - * control transfers are always bi-directional and use - * different kinds of transfers. - * - * INTERRUPT Interrupt transfers are used for small transfers that - * happen infrequently, but need reasonable latency. A good - * example of interrupt transfers is to receive input from - * a USB keyboard. Interrupt-IN transfers are generally - * polled. Meaning that a client (device driver) opens up - * an interrupt-IN pipe to poll on it, and receives - * periodic updates whenever there is information - * available. However, Interrupt transfers can be used - * as one-shot transfers both going IN and OUT. - * - * ISOCHRONOUS These transfers are things that happen once per - * time-interval at a very regular rate. A good example of - * these transfers are for audio and video. A device may - * describe an interval as 10ms at which point it will read - * or write the next batch of data every 10ms and transform - * it for the user. There are no one-shot Isochronous-IN - * transfers. There are one-shot Isochronous-OUT transfers, - * but these are used by device drivers to always provide - * the system with sufficient data. + * BULK These transfers are large transfers of data to or from + * a device. The most common use for bulk transfers is for + * mass storage devices. Though they are often also used by + * network devices and more. Bulk endpoints do not have an + * explicit time component to them. They are always used + * for one-shot transfers. + * + * CONTROL These transfers are used to manipulate devices + * themselves and are used for USB protocol level + * operations (whether device-specific, class-specific, or + * generic across all of USB). Unlike other transfers, + * control transfers are always bi-directional and use + * different kinds of transfers. + * + * INTERRUPT Interrupt transfers are used for small transfers that + * happen infrequently, but need reasonable latency. A good + * example of interrupt transfers is to receive input from + * a USB keyboard. Interrupt-IN transfers are generally + * polled. Meaning that a client (device driver) opens up + * an interrupt-IN pipe to poll on it, and receives + * periodic updates whenever there is information + * available. However, Interrupt transfers can be used + * as one-shot transfers both going IN and OUT. + * + * ISOCHRONOUS These transfers are things that happen once per + * time-interval at a very regular rate. A good example of + * these transfers are for audio and video. A device may + * describe an interval as 10ms at which point it will read + * or write the next batch of data every 10ms and transform + * it for the user. There are no one-shot Isochronous-IN + * transfers. There are one-shot Isochronous-OUT transfers, + * but these are used by device drivers to always provide + * the system with sufficient data. * * To find out information about the endpoints, USB devices have a series of * descriptors that cover different aspects of the device. For example, there @@ -220,67 +220,67 @@ * purpose of each of these files. * * xhci_command.c: This file contains the logic to issue commands to the - * controller as well as the actual functions that the - * other parts of the driver use to cause those commands. + * controller as well as the actual functions that the + * other parts of the driver use to cause those commands. * * xhci_context.c: This file manages various data structures used by the - * controller to manage the controller's and device's - * context data structures. See more in the xHCI Overview - * and General Design for more information. + * controller to manage the controller's and device's + * context data structures. See more in the xHCI Overview + * and General Design for more information. * * xhci_dma.c: This manages the allocation of DMA memory and DMA - * attributes for controller, whether memory is for a - * transfer or something else. This file also deals with - * all the logic of getting data in and out of DMA buffers. + * attributes for controller, whether memory is for a + * transfer or something else. This file also deals with + * all the logic of getting data in and out of DMA buffers. * * xhci_endpoint.c: This manages all of the logic of handling endpoints or - * pipes. It deals with endpoint configuration, I/O - * scheduling, timeouts, and callbacks to USBA. + * pipes. It deals with endpoint configuration, I/O + * scheduling, timeouts, and callbacks to USBA. * * xhci_event.c: This manages callbacks from the hardware to the driver. - * This covers command completion notifications and I/O - * notifications. + * This covers command completion notifications and I/O + * notifications. * * xhci_hub.c: This manages the virtual root-hub. It basically - * implements and translates all of the USB level requests - * into xhci specific implements. It also contains the - * functions to register this hub with USBA. + * implements and translates all of the USB level requests + * into xhci specific implements. It also contains the + * functions to register this hub with USBA. * * xhci_intr.c: This manages the underlying interrupt allocation, - * interrupt moderation, and interrupt routines. + * interrupt moderation, and interrupt routines. * * xhci_quirks.c: This manages information about buggy hardware that's - * been collected and experienced primarily from other - * systems. + * been collected and experienced primarily from other + * systems. * * xhci_ring.c: This manages the abstraction of a ring in xhci, which is - * the primary of communication between the driver and the - * hardware, whether for the controller or a device. + * the primary of communication between the driver and the + * hardware, whether for the controller or a device. * * xhci_usba.c: This implements all of the HCDI functions required by - * USBA. This is the main entry point that drivers and the - * kernel frameworks will reach to start any operation. - * Many functions here will end up in the command and - * endpoint code. + * USBA. This is the main entry point that drivers and the + * kernel frameworks will reach to start any operation. + * Many functions here will end up in the command and + * endpoint code. * * xhci.c: This provides the main kernel DDI interfaces and - * performs device initialization. + * performs device initialization. * * xhci.h: This is the primary header file which defines - * illumos-specific data structures and constants to manage - * the system. + * illumos-specific data structures and constants to manage + * the system. * * xhcireg.h: This header file defines all of the register offsets, - * masks, and related macros. It also contains all of the - * constants that are used in various structures as defined - * by the specification, such as command offsets, etc. + * masks, and related macros. It also contains all of the + * constants that are used in various structures as defined + * by the specification, such as command offsets, etc. * * xhci_ioctl.h: This contains a few private ioctls that are used by a - * private debugging command. These are private. + * private debugging command. These are private. * * cmd/xhci/xhci_portsc: This is a private utility that can be useful for - * debugging xhci state. It is the only consumer of - * xhci_ioctl.h and the private ioctls. + * debugging xhci state. It is the only consumer of + * xhci_ioctl.h and the private ioctls. * * ---------------------------------- * xHCI Overview and Structure Layout @@ -1444,12 +1444,15 @@ xhci_find_ext_cap(xhci_t *xhcip, uint32_t id, uint32_t init, uint32_t *outp) static boolean_t xhci_port_count(xhci_t *xhcip) { - uint_t nusb2 = 0, nusb3 = 0; + uint_t nusb2 = 0, fusb2 = 0; + uint_t nusb30 = 0, fusb30 = 0; + uint_t nusb31 = 0, fusb31 = 0; uint32_t off = UINT32_MAX; while (xhci_find_ext_cap(xhcip, XHCI_ID_PROTOCOLS, off, &off) == B_TRUE) { uint32_t rvers, rport; + uint8_t maj, min, count, first; /* * See xHCI 1.1 / 7.2 for the format of this. The first uint32_t @@ -1466,23 +1469,48 @@ xhci_port_count(xhci_t *xhcip) return (B_FALSE); } - rvers = XHCI_XECP_PROT_MAJOR(rvers); - rport = XHCI_XECP_PROT_PCOUNT(rport); - - if (rvers == 3) { - nusb3 += rport; - } else if (rvers <= 2) { - nusb2 += rport; + maj = XHCI_XECP_PROT_MAJOR(rvers); + min = XHCI_XECP_PROT_MINOR(rvers); + count = XHCI_XECP_PROT_PCOUNT(rport); + first = XHCI_XECP_PROT_FPORT(rport); + + if (maj == 3 && min == 1) { + nusb31 = count; + fusb31 = first; + } else if (maj == 3 && min == 0) { + nusb30 = count; + fusb30 = first; + } else if (maj <= 2) { + nusb2 = count; + fusb2 = first; } else { xhci_error(xhcip, "encountered port capabilities with " "unknown major USB version: %d\n", rvers); } } - (void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip, - "usb2-capable-ports", nusb2); - (void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip, - "usb3-capable-ports", nusb3); + /* + * These properties are used by FMA and the USB topo module. + */ + if (nusb2 > 0) { + (void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip, + "usb2.0-port-count", nusb2); + (void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip, + "usb2.0-first-port", fusb2); + } + if (nusb30 > 0) { + (void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip, + "usb3.0-port-count", nusb30); + (void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip, + "usb3.0-first-port", fusb30); + } + + if (nusb31 > 0) { + (void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip, + "usb3.1-port-count", nusb30); + (void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip, + "usb3.1-first-port", fusb31); + } return (B_TRUE); } @@ -2183,7 +2211,7 @@ static struct dev_ops xhci_dev_ops = { &xhci_cb_ops, /* devo_cb_ops */ &usba_hubdi_busops, /* devo_bus_ops */ usba_hubdi_root_hub_power, /* devo_power */ - ddi_quiesce_not_supported /* devo_quiesce */ + ddi_quiesce_not_supported /* devo_quiesce */ }; static struct modldrv xhci_modldrv = { diff --git a/usr/src/uts/common/sys/usb/hcd/xhci/xhcireg.h b/usr/src/uts/common/sys/usb/hcd/xhci/xhcireg.h index f521428852..e8d2b44ac4 100644 --- a/usr/src/uts/common/sys/usb/hcd/xhci/xhcireg.h +++ b/usr/src/uts/common/sys/usb/hcd/xhci/xhcireg.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2014 Martin Pieuchot. All rights reserved. * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. - * Copyright (c) 2017, Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -286,9 +286,10 @@ extern "C" { /* * xHCI Supported Protocol Capability. See xHCI 1.1 / 7.2. */ -#define XHCI_XECP_PROT_MAJOR(x) ((x >> 24) & 0xff) -#define XHCI_XECP_PROT_MINOR(x) ((x >> 16) & 0xff) -#define XHCI_XECP_PROT_PCOUNT(x) ((x >> 8) & 0xff) +#define XHCI_XECP_PROT_MAJOR(x) (((x) >> 24) & 0xff) +#define XHCI_XECP_PROT_MINOR(x) (((x) >> 16) & 0xff) +#define XHCI_XECP_PROT_PCOUNT(x) (((x) >> 8) & 0xff) +#define XHCI_XECP_PROT_FPORT(x) ((x) & 0xff) /* * xHCI Slot Context definitions - xHCI 1.1 / 6.2.2. @@ -397,7 +398,7 @@ extern "C" { /* * TRB flags that are used between different different TRB types. */ -#define XHCI_TRB_CYCLE (1 << 0) /* Enqueue point of xfer ring */ +#define XHCI_TRB_CYCLE (1 << 0) /* Enqueue point of xfer ring */ #define XHCI_TRB_ENT (1 << 1) /* Evaluate next TRB */ #define XHCI_TRB_LINKSEG XHCI_TRB_ENT /* Link to next segment */ #define XHCI_TRB_ISP (1 << 2) /* Interrupt on short packet */ @@ -460,7 +461,7 @@ extern "C" { #define XHCI_CMD_RESET_DEV (17 << 10) #define XHCI_CMD_FEVENT (18 << 10) #define XHCI_CMD_NEG_BW (19 << 10) -#define XHCI_CMD_SET_LT (20 << 10) +#define XHCI_CMD_SET_LT (20 << 10) #define XHCI_CMD_GET_BW (21 << 10) #define XHCI_CMD_FHEADER (22 << 10) #define XHCI_CMD_NOOP (23 << 10) @@ -498,25 +499,25 @@ extern "C" { #define XHCI_CODE_RING_OVERRUN 15 /* Empty ring when receiving isoc */ #define XHCI_CODE_VF_RING_FULL 16 /* VF's event ring is full */ #define XHCI_CODE_PARAMETER 17 /* Context parameter is invalid */ -#define XHCI_CODE_BW_OVERRUN 18 /* TD exceeds the bandwidth */ +#define XHCI_CODE_BW_OVERRUN 18 /* TD exceeds the bandwidth */ #define XHCI_CODE_CONTEXT_STATE 19 /* Transition from illegal ctx state */ #define XHCI_CODE_NO_PING_RESP 20 /* Unable to complete periodic xfer */ #define XHCI_CODE_EV_RING_FULL 21 /* Unable to post an evt to the ring */ #define XHCI_CODE_INCOMPAT_DEV 22 /* Device cannot be accessed */ #define XHCI_CODE_MISSED_SRV 23 /* Unable to service isoc EP in ESIT */ -#define XHCI_CODE_CMD_RING_STOP 24 /* Command Stop (CS) requested */ -#define XHCI_CODE_CMD_ABORTED 25 /* Command Abort (CA) operation */ -#define XHCI_CODE_XFER_STOPPED 26 /* xfer terminated by a stop endpoint */ -#define XHCI_CODE_XFER_STOPINV 27 /* TRB transfer length invalid */ -#define XHCI_CODE_XFER_STOPSHORT 28 /* Stopped before end of TD */ +#define XHCI_CODE_CMD_RING_STOP 24 /* Command Stop (CS) requested */ +#define XHCI_CODE_CMD_ABORTED 25 /* Command Abort (CA) operation */ +#define XHCI_CODE_XFER_STOPPED 26 /* xfer terminated by a stop endpoint */ +#define XHCI_CODE_XFER_STOPINV 27 /* TRB transfer length invalid */ +#define XHCI_CODE_XFER_STOPSHORT 28 /* Stopped before end of TD */ #define XHCI_CODE_MELAT 29 /* Max Exit Latency too large */ #define XHCI_CODE_RESERVED 30 #define XHCI_CODE_ISOC_OVERRUN 31 /* IN data buffer < Max ESIT Payload */ -#define XHCI_CODE_EVENT_LOST 32 /* Internal overrun - impl. specific */ -#define XHCI_CODE_UNDEFINED 33 /* Fatal error - impl. specific */ -#define XHCI_CODE_INVALID_SID 34 /* Invalid stream ID received */ -#define XHCI_CODE_SEC_BW 35 /* Cannot alloc secondary BW Domain */ -#define XHCI_CODE_SPLITERR 36 /* USB2 split transaction */ +#define XHCI_CODE_EVENT_LOST 32 /* Internal overrun - impl. specific */ +#define XHCI_CODE_UNDEFINED 33 /* Fatal error - impl. specific */ +#define XHCI_CODE_INVALID_SID 34 /* Invalid stream ID received */ +#define XHCI_CODE_SEC_BW 35 /* Cannot alloc secondary BW Domain */ +#define XHCI_CODE_SPLITERR 36 /* USB2 split transaction */ #ifdef __cplusplus } diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files index dbacb20029..2df0d1ea51 100644 --- a/usr/src/uts/i86pc/Makefile.files +++ b/usr/src/uts/i86pc/Makefile.files @@ -224,6 +224,7 @@ ACPIDEV_OBJS += acpidev_drv.o \ acpidev_memory.o \ acpidev_pci.o \ acpidev_resource.o \ + acpidev_usbport.o \ acpidev_util.o DRMACH_ACPI_OBJS += drmach_acpi.o dr_util.o drmach_err.o @@ -254,7 +255,7 @@ INC_PATH += -I$(UTSBASE)/i86xpv -I$(UTSBASE)/common/xen # since only C headers are included when #defined(__lint) is true. # -ASSYM_DEPS += \ +ASSYM_DEPS += \ copy.o \ desctbls_asm.o \ ddi_i86_asm.o \ diff --git a/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_device.c b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_device.c index cf65cf4084..0557a1a311 100644 --- a/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_device.c +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_device.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009-2010, Intel Corporation. * All rights reserved. + * Copyright (c) 2018, Joyent, Inc. */ #include <sys/types.h> @@ -31,10 +32,13 @@ #include <sys/acpica.h> #include <sys/acpidev.h> #include <sys/acpidev_impl.h> +#include <sys/pci.h> static ACPI_STATUS acpidev_device_probe(acpidev_walk_info_t *infop); static acpidev_filter_result_t acpidev_device_filter(acpidev_walk_info_t *infop, char *devname, int maxlen); +static acpidev_filter_result_t acpidev_device_filter_usb(acpidev_walk_info_t *, + ACPI_HANDLE, acpidev_filter_rule_t *, char *, int); static ACPI_STATUS acpidev_device_init(acpidev_walk_info_t *infop); static uint32_t acpidev_device_unitaddr = 0; @@ -88,10 +92,20 @@ static acpidev_filter_rule_t acpidev_device_filters[] = { NULL, NULL, }, + { /* Scan a device attempting to find a USB node */ + acpidev_device_filter_usb, + 0, + ACPIDEV_FILTER_SCAN, + &acpidev_class_list_usbport, + 2, + INT_MAX, + NULL, + NULL + }, { /* Scan other device objects not directly under ACPI root */ NULL, 0, - ACPIDEV_FILTER_SKIP, + ACPIDEV_FILTER_SCAN, &acpidev_class_list_device, 2, INT_MAX, @@ -151,6 +165,57 @@ acpidev_device_probe(acpidev_walk_info_t *infop) return (rc); } +/* + * Attempt to determine which devices here correspond to an HCI for a USB + * controller. + */ +static acpidev_filter_result_t +acpidev_device_filter_usb(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, + acpidev_filter_rule_t *afrp, char *devname, int len) +{ + dev_info_t *dip; + char **compat; + uint_t ncompat, i; + + if (infop->awi_op_type != ACPIDEV_OP_BOOT_REPROBE) + return (ACPIDEV_FILTER_SKIP); + + /* + * If we don't find a dip that matches this one, then let's not worry + * about it. This means that it may not be a device we care about in any + * way. + */ + if (ACPI_FAILURE(acpica_get_devinfo(hdl, &dip))) { + return (ACPIDEV_FILTER_SKIP); + } + + /* + * To determine if this is a PCI USB class controller, we grab its + * compatible array and look for an instance of pciclass,0c03 or + * pciexclass,0c03. The class code 0c03 is used to indicate a USB + * controller. + */ + if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "compatible", &compat, &ncompat) != DDI_SUCCESS) { + return (ACPIDEV_FILTER_SKIP); + } + + for (i = 0; i < ncompat; i++) { + if (strcmp(compat[i], "pciclass,0c03") == 0 || + strcmp(compat[i], "pciexclass,0c03") == 0) { + ddi_prop_free(compat); + /* + * We've found a PCI based USB controller. Switch to the + * USB specific parser. + */ + return (ACPIDEV_FILTER_SCAN); + } + } + + ddi_prop_free(compat); + return (ACPIDEV_FILTER_SKIP); +} + static acpidev_filter_result_t acpidev_device_filter(acpidev_walk_info_t *infop, char *devname, int maxlen) { diff --git a/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_drv.c b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_drv.c index c6b69a4bbb..1668a4fcad 100644 --- a/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_drv.c +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_drv.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009-2010, Intel Corporation. * All rights reserved. + * Copyright (c) 2018, Joyent, Inc. */ /* @@ -160,6 +161,9 @@ acpidev_class_list_fini(void) { acpidev_unload_plat_modules(); + (void) acpidev_unregister_class(&acpidev_class_list_usbport, + &acpidev_class_usbport); + if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { (void) acpidev_unregister_class(&acpidev_class_list_scope, &acpidev_class_pci); @@ -303,6 +307,13 @@ acpidev_class_list_init(uint64_t *fp) *fp |= ACPI_DEVCFG_PCI; } + /* Check support of USB port enumeration */ + if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_usbport, + &acpidev_class_usbport, B_TRUE))) { + goto error_usbport; + } + + /* Check blacklist and load platform specific modules. */ rc = acpidev_load_plat_modules(); if (ACPI_FAILURE(rc)) { @@ -318,6 +329,11 @@ error_plat: (void) acpidev_unregister_class(&acpidev_class_list_scope, &acpidev_class_pci); } + +error_usbport: + (void) acpidev_unregister_class(&acpidev_class_list_usbport, + &acpidev_class_usbport); + error_scope_pci: if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { (void) acpidev_unregister_class(&acpidev_class_list_device, diff --git a/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_usbport.c b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_usbport.c new file mode 100644 index 0000000000..f9139034dc --- /dev/null +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_usbport.c @@ -0,0 +1,281 @@ +/* + * 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 (c) 2018, Joyent, Inc. + */ + +/* + * ACPI driver that enumerates and creates USB nodes. + */ + +#include <sys/types.h> +#include <sys/atomic.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_impl.h> + +/* + * List of class drivers which will be called in order when handling + * children of ACPI USB objects. + */ +acpidev_class_list_t *acpidev_class_list_usbport = NULL; + +static acpidev_filter_result_t acpidev_usbport_filter_cb(acpidev_walk_info_t *, + ACPI_HANDLE, acpidev_filter_rule_t *, char *, int); + +static acpidev_filter_rule_t acpidev_usbport_filters[] = { + { + acpidev_usbport_filter_cb, + 0, + ACPIDEV_FILTER_SCAN, + &acpidev_class_list_usbport, + 3, + INT_MAX, + NULL, + NULL + } +}; + +/* + * We've been passed something by the general device scanner. This means that we + * were able to determine that the parent was a valid PCI device with a USB + * class code. + */ +static ACPI_STATUS +acpidev_usbport_probe(acpidev_walk_info_t *infop) +{ + ACPI_STATUS ret; + int flags; + + if (infop->awi_info->Type != ACPI_TYPE_DEVICE) { + return (AE_OK); + } + + flags = ACPIDEV_PROCESS_FLAG_SCAN; + switch (infop->awi_op_type) { + case ACPIDEV_OP_BOOT_PROBE: + case ACPIDEV_OP_BOOT_REPROBE: + flags |= ACPIDEV_PROCESS_FLAG_CREATE; + break; + case ACPIDEV_OP_HOTPLUG_PROBE: + flags |= ACPIDEV_PROCESS_FLAG_CREATE | + ACPIDEV_PROCESS_FLAG_SYNCSTATUS | + ACPIDEV_PROCESS_FLAG_HOLDBRANCH; + break; + default: + return (AE_BAD_PARAMETER); + } + + if (infop->awi_parent == NULL) { + return (AE_BAD_PARAMETER); + } + + /* + * Inherit our parents value. + */ + if (infop->awi_parent->awi_scratchpad[AWI_SCRATCH_USBPORT] != 0) { + infop->awi_scratchpad[AWI_SCRATCH_USBPORT] = + infop->awi_parent->awi_scratchpad[AWI_SCRATCH_USBPORT]; + } else { + infop->awi_scratchpad[AWI_SCRATCH_USBPORT] = infop->awi_level; + } + + ret = acpidev_process_object(infop, flags); + if (ACPI_FAILURE(ret) && ret != AE_NOT_EXIST && + ret != AE_ALREADY_EXISTS) { + cmn_err(CE_WARN, "!failed to process USB object %s: %d", + infop->awi_name, ret); + } else { + ret = AE_OK; + } + + return (ret); +} + +static acpidev_filter_result_t +acpidev_usbport_filter_cb(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, + acpidev_filter_rule_t *afrp, char *devname, int len) +{ + ACPI_BUFFER buf; + + if (infop->awi_info->Type != ACPI_TYPE_DEVICE) { + return (ACPIDEV_FILTER_SKIP); + } + + /* + * Make sure we can get the _ADR method for this as a reasonable case of + * determining whether or not this is something that we care about. + */ + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_FAILURE(AcpiEvaluateObject(hdl, "_ADR", NULL, &buf))) { + return (ACPIDEV_FILTER_SKIP); + } + AcpiOsFree(buf.Pointer); + + if (infop->awi_level == infop->awi_scratchpad[AWI_SCRATCH_USBPORT]) { + (void) snprintf(devname, len, "usbroothub"); + } else { + (void) snprintf(devname, len, "port"); + } + + return (ACPIDEV_FILTER_DEFAULT); +} + +static acpidev_filter_result_t +acpidev_usbport_filter(acpidev_walk_info_t *infop, char *devname, int maxlen) +{ + acpidev_filter_result_t res; + + ASSERT(infop != NULL); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE || + infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE || + infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + res = acpidev_filter_device(infop, infop->awi_hdl, + ACPIDEV_ARRAY_PARAM(acpidev_usbport_filters), + devname, maxlen); + } else { + ACPIDEV_DEBUG(CE_WARN, "!acpidev: unknown operation type %u " + "in acpidev_device_filter().", infop->awi_op_type); + res = ACPIDEV_FILTER_FAILED; + } + + return (res); +} + +static ACPI_STATUS +acpidev_usbport_init(acpidev_walk_info_t *infop) +{ + char *name; + ACPI_BUFFER buf; + char acpi_strbuf[128]; + + char *compatible[] = { + ACPIDEV_TYPE_USBPORT, + ACPIDEV_TYPE_VIRTNEX + }; + + if (ACPI_FAILURE(acpidev_set_compatible(infop, + ACPIDEV_ARRAY_PARAM(compatible)))) { + return (AE_ERROR); + } + + /* + * Set the port's unit address to the last component of the ACPI path + * for it. This needs to be unique for a given set of parents and + * children. Because the hubs all usually have names that aren't unique + * we end up using the name of the parent for the top level device. + * + * For children, just use their acpi port address. + */ + if (infop->awi_parent->awi_scratchpad[AWI_SCRATCH_USBPORT] == 0) { + name = strrchr(infop->awi_parent->awi_name, '.'); + if (name != NULL) + name = name + 1; + + /* + * Also, add the parents name as a property so when user land is + * trying to marry up USB devices with the root controller, it + * can. + */ + if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip, + "acpi-controller-name", infop->awi_parent->awi_name) != + DDI_PROP_SUCCESS) { + return (AE_ERROR); + } + } else { + ACPI_OBJECT *obj; + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_FAILURE(AcpiEvaluateObject(infop->awi_hdl, "_ADR", + NULL, &buf))) { + return (AE_ERROR); + } + + obj = (ACPI_OBJECT *)buf.Pointer; + if (obj->Type != ACPI_TYPE_INTEGER) { + AcpiOsFree(buf.Pointer); + return (AE_ERROR); + } + if (ndi_prop_update_int64(DDI_DEV_T_NONE, infop->awi_dip, + "acpi-address", (int64_t)obj->Integer.Value) != + DDI_PROP_SUCCESS) { + AcpiOsFree(buf.Pointer); + return (AE_ERROR); + } + (void) snprintf(acpi_strbuf, sizeof (acpi_strbuf), "%lu", + obj->Integer.Value); + name = acpi_strbuf; + AcpiOsFree(buf.Pointer); + } + + + if (ACPI_FAILURE(acpidev_set_unitaddr(infop, NULL, 0, + name))) { + return (AE_ERROR); + } + + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_SUCCESS(AcpiEvaluateObject(infop->awi_hdl, "_PLD", NULL, + &buf))) { + ACPI_OBJECT *obj = (ACPI_OBJECT *)buf.Pointer; + + if (obj->Type == ACPI_TYPE_PACKAGE && obj->Package.Count >= 1 && + obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER && + obj->Package.Elements[0].Buffer.Length >= + ACPI_PLD_REV1_BUFFER_SIZE) { + (void) ndi_prop_update_byte_array(DDI_DEV_T_NONE, + infop->awi_dip, "acpi-physical-location", + obj->Package.Elements[0].Buffer.Pointer, + obj->Package.Elements[0].Buffer.Length); + } + AcpiOsFree(buf.Pointer); + } + + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_SUCCESS(AcpiEvaluateObject(infop->awi_hdl, "_UPC", NULL, + &buf))) { + ACPI_OBJECT *obj = (ACPI_OBJECT *)buf.Pointer; + + if (obj->Type == ACPI_TYPE_PACKAGE && obj->Package.Count >= 4 && + obj->Package.Elements[0].Type == ACPI_TYPE_INTEGER && + obj->Package.Elements[1].Type == ACPI_TYPE_INTEGER) { + if (obj->Package.Elements[0].Integer.Value != 0) { + (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, + infop->awi_dip, "usb-port-connectable"); + } + + (void) ndi_prop_update_int(DDI_DEV_T_NONE, + infop->awi_dip, "usb-port-type", + (int)obj->Package.Elements[1].Integer.Value); + } + AcpiOsFree(buf.Pointer); + } + + return (AE_OK); +} + +acpidev_class_t acpidev_class_usbport = { + 0, /* adc_refcnt */ + ACPIDEV_CLASS_REV1, /* adc_version */ + ACPIDEV_CLASS_ID_USB, /* adc_class_id */ + "ACPI USBPORT", /* adc_class_name */ + ACPIDEV_TYPE_USBPORT, /* adc_dev_type */ + NULL, /* adc_private */ + NULL, /* adc_pre_probe */ + NULL, /* adc_post_probe */ + acpidev_usbport_probe, /* adc_probe */ + acpidev_usbport_filter, /* adc_filter */ + acpidev_usbport_init, /* adc_init */ + NULL, /* adc_fini */ +}; diff --git a/usr/src/uts/i86pc/sys/acpidev.h b/usr/src/uts/i86pc/sys/acpidev.h index a3bd54d4e3..1e16a6cbdd 100644 --- a/usr/src/uts/i86pc/sys/acpidev.h +++ b/usr/src/uts/i86pc/sys/acpidev.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2009-2010, Intel Corporation. * All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2018, Joyent, Inc. All rights reserved. */ #ifndef _SYS_ACPIDEV_H @@ -82,6 +82,7 @@ extern "C" { #define ACPIDEV_TYPE_MEMORY "acpimemory" #define ACPIDEV_TYPE_PCI "pci" #define ACPIDEV_TYPE_PCIEX "pciex" +#define ACPIDEV_TYPE_USBPORT "acpiusbport" /* Device property names for ACPI objects. */ #define ACPIDEV_PROP_NAME_UNIT_ADDR "unit-address" @@ -116,6 +117,7 @@ typedef enum acpidev_class_id { ACPIDEV_CLASS_ID_MEMORY = 6, ACPIDEV_CLASS_ID_PCI = 7, ACPIDEV_CLASS_ID_PCIEX = 8, + ACPIDEV_CLASS_ID_USB = 9, ACPIDEV_CLASS_ID_MAX } acpidev_class_id_t; @@ -244,6 +246,12 @@ struct acpidev_walk_info { intptr_t awi_scratchpad[4]; }; +/* + * Scratchpad entries used by drivers. Note the CPU driver uses entries 0 and 1 + * and do not currently have a #define. + */ +#define AWI_SCRATCH_USBPORT 2 + /* Disable creating device nodes for ACPI objects. */ #define ACPIDEV_WI_DISABLE_CREATE 0x1 /* Device node has already been created for an ACPI object. */ @@ -334,6 +342,7 @@ extern acpidev_class_t acpidev_class_container; extern acpidev_class_t acpidev_class_cpu; extern acpidev_class_t acpidev_class_memory; extern acpidev_class_t acpidev_class_pci; +extern acpidev_class_t acpidev_class_usbport; /* * Class driver lists. @@ -343,6 +352,7 @@ extern acpidev_class_list_t *acpidev_class_list_scope; extern acpidev_class_list_t *acpidev_class_list_device; extern acpidev_class_list_t *acpidev_class_list_cpu; extern acpidev_class_list_t *acpidev_class_list_memory; +extern acpidev_class_list_t *acpidev_class_list_usbport; /* * Register a device class driver onto a driver list. All class drivers on the |