summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2019-01-15 21:35:50 +0000
committerRobert Mustacchi <rm@joyent.com>2019-01-28 06:30:32 +0000
commit672fc84a1840ce8ef60fc752e9ea374723d1135a (patch)
treec5930c6d028555e52fb42b74fff23794b7f11231
parent31b6814e52e5d603c18d8d2143eb2a8a660249c8 (diff)
downloadillumos-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>
-rw-r--r--usr/src/cmd/diskinfo/diskinfo.c22
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/hc.c1
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/mapfile-vers1
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_hc.h25
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.c20
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.h7
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_protocol.c5
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/Makefile12
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-hc-topology.xml6
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-usb.usbtopo98
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/Makefile12
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-hc-topology.xml6
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-2029P-ACR24L/SSG-2029P-ACR24L-usb.usbtopo85
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/Makefile13
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-hc-topology.xml7
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-6049P-E1CR36L/SSG-6049P-E1CR36L-usb.usbtopo85
-rw-r--r--usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml6
-rw-r--r--usr/src/lib/fm/topo/modules/common/Makefile1
-rw-r--r--usr/src/lib/fm/topo/modules/common/disk/disk.c36
-rw-r--r--usr/src/lib/fm/topo/modules/common/disk/disk_common.c29
-rw-r--r--usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c16
-rw-r--r--usr/src/lib/fm/topo/modules/common/shared/topo_port.c15
-rw-r--r--usr/src/lib/fm/topo/modules/common/shared/topo_port.h4
-rw-r--r--usr/src/lib/fm/topo/modules/common/usb/Makefile28
-rw-r--r--usr/src/lib/fm/topo/modules/common/usb/topo_usb.c2012
-rw-r--r--usr/src/lib/fm/topo/modules/common/usb/topo_usb.h40
-rw-r--r--usr/src/lib/fm/topo/modules/common/usb/topo_usb_acpi.c118
-rw-r--r--usr/src/lib/fm/topo/modules/common/usb/topo_usb_int.h74
-rw-r--r--usr/src/lib/fm/topo/modules/common/usb/topo_usb_metadata.c471
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile3
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/x86pi/Makefile3
-rw-r--r--usr/src/lib/fm/topo/modules/sun4/pcibus/Makefile.pci3
-rw-r--r--usr/src/pkg/manifests/service-fault-management.mf27
-rw-r--r--usr/src/uts/common/io/usb/hcd/xhci/xhci.c188
-rw-r--r--usr/src/uts/common/sys/usb/hcd/xhci/xhcireg.h35
-rw-r--r--usr/src/uts/i86pc/Makefile.files3
-rw-r--r--usr/src/uts/i86pc/io/acpi/acpidev/acpidev_device.c67
-rw-r--r--usr/src/uts/i86pc/io/acpi/acpidev/acpidev_drv.c16
-rw-r--r--usr/src/uts/i86pc/io/acpi/acpidev/acpidev_usbport.c281
-rw-r--r--usr/src/uts/i86pc/sys/acpidev.h12
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", &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