summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2017-09-21 17:35:16 +0000
committerRobert Mustacchi <rm@joyent.com>2017-11-14 23:29:06 +0000
commit45d3dd981abb9025d8ac994cf4cc8ce8cb1a9480 (patch)
treec8e69d104ed167a879e84b023dc05727e833a976
parent4213c2d8f7d24a0383e863621115570a68fb016c (diff)
downloadillumos-joyent-45d3dd981abb9025d8ac994cf4cc8ce8cb1a9480.tar.gz
8699 Want NIC transceiver visibility
8700 ixgbe MAC_CAPAB_TRANSCEIVER support 8701 i40e MAC_CAPAB_TRANSCEIVER support Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Ryan Zezeski <ryan.zeseski@joyent.com> Reviewed by: Yuri Pankov <yuripv@gmx.com> Reviewed by: Toomas Soome <tsoome@me.com> Approved by: Richard Lowe <richlowe@richlowe.net>
-rw-r--r--exception_lists/copyright1
-rw-r--r--exception_lists/packaging12
-rw-r--r--usr/src/cmd/Makefile3
-rw-r--r--usr/src/cmd/dlutil/Makefile42
-rw-r--r--usr/src/cmd/dlutil/dltraninfo.c458
-rw-r--r--usr/src/lib/Makefile5
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/hc.c5
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_hc.h18
-rw-r--r--usr/src/lib/fm/topo/modules/Makefile.plugin5
-rw-r--r--usr/src/lib/fm/topo/modules/common/Makefile2
-rw-r--r--usr/src/lib/fm/topo/modules/common/nic/Makefile26
-rw-r--r--usr/src/lib/fm/topo/modules/common/nic/topo_nic.c241
-rw-r--r--usr/src/lib/fm/topo/modules/common/nic/topo_nic.h34
-rw-r--r--usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c72
-rw-r--r--usr/src/lib/fm/topo/modules/common/shared/topo_port.c130
-rw-r--r--usr/src/lib/fm/topo/modules/common/shared/topo_port.h36
-rw-r--r--usr/src/lib/fm/topo/modules/common/shared/topo_transceiver.c185
-rw-r--r--usr/src/lib/fm/topo/modules/common/shared/topo_transceiver.h37
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile4
-rw-r--r--usr/src/lib/fm/topo/modules/sun4/pcibus/Makefile.pci3
-rw-r--r--usr/src/lib/libsff/Makefile44
-rw-r--r--usr/src/lib/libsff/Makefile.com34
-rw-r--r--usr/src/lib/libsff/amd64/Makefile19
-rw-r--r--usr/src/lib/libsff/common/libsff.c1414
-rw-r--r--usr/src/lib/libsff/common/libsff.h101
-rw-r--r--usr/src/lib/libsff/common/llib-lsff19
-rw-r--r--usr/src/lib/libsff/common/mapfile-vers37
-rw-r--r--usr/src/lib/libsff/common/sff.h221
-rw-r--r--usr/src/lib/libsff/i386/Makefile18
-rw-r--r--usr/src/lib/libsff/sparc/Makefile18
-rw-r--r--usr/src/man/man9e/Makefile6
-rw-r--r--usr/src/man/man9e/mac_capab_transceiver.9e407
-rw-r--r--usr/src/man/man9f/Makefile7
-rw-r--r--usr/src/man/man9f/mac_transceiver_info.9f95
-rw-r--r--usr/src/pkg/manifests/service-fault-management.mf1
-rw-r--r--usr/src/pkg/manifests/system-kernel.man9e.inc3
-rw-r--r--usr/src/pkg/manifests/system-kernel.man9f.inc5
-rw-r--r--usr/src/pkg/manifests/system-library.mf2
-rw-r--r--usr/src/pkg/manifests/system-network.mf2
-rw-r--r--usr/src/pkg/manifests/system-test-utiltest.mf31
-rw-r--r--usr/src/test/util-tests/runfiles/default.run1
-rw-r--r--usr/src/test/util-tests/tests/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/libsff/Makefile88
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff.ksh61
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8472.c61
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8472.out255
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8636_diag.c61
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8636_diag.out2
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8636_extspec.c62
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8636_extspec.out255
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8636_tech.c79
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8636_tech.out96
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8636_temp.c61
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_8636_temp.out255
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_br.c133
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_br.out8
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_compliance.c124
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_compliance.out130
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_conn.c89
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_conn.out514
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_efault.c54
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_einval.c88
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_enc.c89
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_enc.out514
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_ident.c60
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_ident.out255
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_lengths.c133
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_lengths.out15
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_opts.c98
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_opts.out50
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_strings.c133
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_wave.c177
-rw-r--r--usr/src/test/util-tests/tests/libsff/libsff_wave.out11
-rw-r--r--usr/src/uts/common/Makefile.files5
-rw-r--r--usr/src/uts/common/io/dld/dld_drv.c102
-rw-r--r--usr/src/uts/common/io/i40e/i40e_gld.c41
-rw-r--r--usr/src/uts/common/io/ixgbe/ixgbe_gld.c15
-rw-r--r--usr/src/uts/common/io/ixgbe/ixgbe_main.c3
-rw-r--r--usr/src/uts/common/io/ixgbe/ixgbe_sw.h4
-rw-r--r--usr/src/uts/common/io/ixgbe/ixgbe_transceiver.c169
-rw-r--r--usr/src/uts/common/io/mac/mac.c104
-rw-r--r--usr/src/uts/common/io/mac/mac_provider.c16
-rw-r--r--usr/src/uts/common/sys/dld.h24
-rw-r--r--usr/src/uts/common/sys/mac_impl.h20
-rw-r--r--usr/src/uts/common/sys/mac_provider.h24
85 files changed, 8300 insertions, 44 deletions
diff --git a/exception_lists/copyright b/exception_lists/copyright
index 2657794328..e07700d831 100644
--- a/exception_lists/copyright
+++ b/exception_lists/copyright
@@ -385,6 +385,7 @@ usr/src/lib/libsmbfs/smb/spnegoparse.[ch]
usr/src/test/util-tests/tests/dis/*/*.out
usr/src/test/util-tests/tests/grep_xpg4/files/gout*
usr/src/test/util-tests/tests/grep_xpg4/files/test*
+usr/src/test/util-tests/tests/libsff/*.out
usr/src/test/zfs-tests/tests/functional/history/*Z
usr/src/test/zfs-tests/tests/functional/history/*txt
usr/src/tools/btxld/btx.h
diff --git a/exception_lists/packaging b/exception_lists/packaging
index 24fa387e57..69bb86b174 100644
--- a/exception_lists/packaging
+++ b/exception_lists/packaging
@@ -1000,3 +1000,15 @@ usr/lib/sparcv9/libficl-sys.so sparc
usr/lib/llib-lficl-sys
usr/lib/llib-lficl-sys.ln
usr/lib/libficl-sys.so
+
+#
+# libsff is private
+#
+usr/include/libsff.h
+usr/lib/amd64/libsff.so i386
+usr/lib/amd64/llib-lsff.ln i386
+usr/lib/sparcv9/libsff.so sparc
+usr/lib/sparcv9/llib-lsff.ln sparc
+usr/lib/libsff.so
+usr/lib/llib-lsff
+usr/lib/llib-lsff.ln
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 399c2db93f..0903d63b7b 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -21,7 +21,7 @@
#
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2016 Joyent, Inc.
+# Copyright (c) 2017, Joyent, Inc.
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright (c) 2013 DEY Storage Systems, Inc. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
@@ -131,6 +131,7 @@ COMMON_SUBDIRS= \
dispadmin \
dladm \
dlstat \
+ dlutil \
dmesg \
dodatadm \
dtrace \
diff --git a/usr/src/cmd/dlutil/Makefile b/usr/src/cmd/dlutil/Makefile
new file mode 100644
index 0000000000..b037908c60
--- /dev/null
+++ b/usr/src/cmd/dlutil/Makefile
@@ -0,0 +1,42 @@
+#
+# 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) 2017, Joyent, Inc.
+#
+
+PROG= dltraninfo
+LINTPROGS= $(PROG:%=%.ln)
+
+include ../Makefile.cmd
+
+ROOTCMDDIR = $(ROOTLIB)/dl
+CFLAGS += $(CCVERBOSE)
+
+dltraninfo := LDLIBS += -ldladm -lsff -lnvpair
+dltraninfo.ln := LDLIBS += -ldladm -lsff -lnvpair
+
+ROOTLIBDLFILES = $(PROG:%=$(ROOTLIB)/dl/%)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+%.ln: %.c
+ $(LINT.c) $< $(LDLIBS)
+
+lint: $(LINTPROGS)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/dlutil/dltraninfo.c b/usr/src/cmd/dlutil/dltraninfo.c
new file mode 100644
index 0000000000..af7f8c80c2
--- /dev/null
+++ b/usr/src/cmd/dlutil/dltraninfo.c
@@ -0,0 +1,458 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Private utility to dump transceiver information for each physical datalink.
+ * Something like this should eventually be a part of dladm or similar.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <libgen.h>
+
+#include <libdladm.h>
+#include <libdllink.h>
+#include <sys/dld.h>
+#include <sys/dld_ioc.h>
+#include <sys/dls_mgmt.h>
+#include <libsff.h>
+
+#define DLTRAN_KIND_LEN 64
+
+static dladm_handle_t dltran_hdl;
+static char dltran_dlerrmsg[DLADM_STRSIZE];
+static const char *dltran_progname;
+static boolean_t dltran_verbose;
+static boolean_t dltran_hex;
+static boolean_t dltran_write;
+static int dltran_outfd;
+static int dltran_errors;
+
+/*
+ * This routine basically assumes that we'll have 16 byte aligned output to
+ * print out the human readable output.
+ */
+static void
+dltran_dump_page(uint8_t *buf, size_t nbytes, uint_t page)
+{
+ size_t i;
+ static boolean_t first = B_TRUE;
+
+ if (first) {
+ (void) printf("page %*s 0", 4, "");
+ for (i = 1; i < 16; i++) {
+ if (i % 4 == 0 && i % 16 != 0) {
+ (void) printf(" ");
+ }
+
+ (void) printf("%2x", i);
+ }
+ (void) printf(" v123456789abcdef\n");
+ first = B_FALSE;
+ }
+ for (i = 0; i < nbytes; i++) {
+
+ if (i % 16 == 0) {
+ (void) printf("0x%02x %04x: ", page, i);
+ }
+
+ if (i % 4 == 0 && i % 16 != 0) {
+ (void) printf(" ");
+ }
+
+
+ (void) printf("%02x", buf[i]);
+
+ if (i % 16 == 15) {
+ int j;
+ (void) printf(" ");
+ for (j = i - (i % 16); j <= i; j++) {
+ if (!isprint(buf[j])) {
+ (void) printf(".");
+ } else {
+ (void) printf("%c", buf[j]);
+ }
+ }
+ (void) printf("\n");
+ }
+ }
+}
+
+static int
+dltran_read_page(datalink_id_t link, uint_t tranid, uint_t page, uint8_t *bufp,
+ size_t *buflen)
+{
+ dld_ioc_tranio_t dti;
+
+ bzero(bufp, *buflen);
+ bzero(&dti, sizeof (dti));
+
+ dti.dti_linkid = link;
+ dti.dti_tran_id = tranid;
+ dti.dti_page = page;
+ dti.dti_nbytes = *buflen;
+ dti.dti_off = 0;
+ dti.dti_buf = (uintptr_t)(void *)bufp;
+
+ if (ioctl(dladm_dld_fd(dltran_hdl), DLDIOC_READTRAN, &dti) != 0) {
+ (void) fprintf(stderr, "failed to read transceiver page "
+ "0x%2x: %s\n", page, strerror(errno));
+ return (1);
+ }
+
+ *buflen = dti.dti_nbytes;
+ return (0);
+}
+
+static boolean_t
+dltran_is_8472(uint8_t *buf)
+{
+ switch (buf[0]) {
+ case 0xc:
+ case 0xd:
+ case 0x11:
+ /*
+ * Catch cases that refer explicitly to QSFP and newer.
+ */
+ return (B_FALSE);
+ default:
+ break;
+ }
+
+ /*
+ * Check the byte that indicates compliance with SFF 8472. Use this to
+ * know if we can read page 0xa2 or not.
+ */
+ if (buf[94] == 0)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+static void
+dltran_hex_dump(datalink_id_t linkid, uint_t tranid)
+{
+ uint8_t buf[256];
+ size_t buflen = sizeof (buf);
+
+ if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
+ dltran_errors++;
+ return;
+ }
+
+ dltran_dump_page(buf, buflen, 0xa0);
+
+ if (!dltran_is_8472(buf)) {
+ return;
+ }
+
+ buflen = sizeof (buf);
+ if (dltran_read_page(linkid, tranid, 0xa2, buf, &buflen) != 0) {
+ dltran_errors++;
+ return;
+ }
+
+ dltran_dump_page(buf, buflen, 0xa2);
+}
+
+static void
+dltran_write_page(datalink_id_t linkid, uint_t tranid)
+{
+ uint8_t buf[256];
+ size_t buflen = sizeof (buf);
+ off_t off;
+
+ if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
+ dltran_errors++;
+ return;
+ }
+
+ off = 0;
+ while (buflen > 0) {
+ ssize_t ret;
+
+ ret = write(dltran_outfd, buf + off, buflen);
+ if (ret == -1) {
+ (void) fprintf(stderr, "failed to write data "
+ "to output file: %s\n", strerror(errno));
+ dltran_errors++;
+ return;
+ }
+
+ off += ret;
+ buflen -= ret;
+ }
+}
+
+static void
+dltran_verbose_dump(datalink_id_t linkid, uint_t tranid)
+{
+ uint8_t buf[256];
+ size_t buflen = sizeof (buf);
+ int ret;
+ nvlist_t *nvl;
+
+ if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
+ dltran_errors++;
+ return;
+ }
+
+ ret = libsff_parse(buf, buflen, 0xa0, &nvl);
+ if (ret == 0) {
+ dump_nvlist(nvl, 8);
+ nvlist_free(nvl);
+ } else {
+ fprintf(stderr, "failed to parse sfp data: %s\n",
+ strerror(ret));
+ dltran_errors++;
+ }
+}
+
+static int
+dltran_dump_transceivers(dladm_handle_t hdl, datalink_id_t linkid, void *arg)
+{
+ dladm_status_t status;
+ char name[MAXLINKNAMELEN];
+ dld_ioc_gettran_t gt;
+ uint_t count, i, tranid = UINT_MAX;
+ boolean_t tran_found = B_FALSE;
+ uint_t *tranidp = arg;
+
+ if (tranidp != NULL)
+ tranid = *tranidp;
+
+ if ((status = dladm_datalink_id2info(hdl, linkid, NULL, NULL, NULL,
+ name, sizeof (name))) != DLADM_STATUS_OK) {
+ (void) fprintf(stderr, "failed to get datalink name for link "
+ "%d: %s", linkid, dladm_status2str(status,
+ dltran_dlerrmsg));
+ dltran_errors++;
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ bzero(&gt, sizeof (gt));
+ gt.dgt_linkid = linkid;
+ gt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN;
+
+ if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, &gt) != 0) {
+ if (errno != ENOTSUP) {
+ (void) fprintf(stderr, "failed to get transceiver "
+ "count for device %s: %s\n",
+ name, strerror(errno));
+ dltran_errors++;
+ }
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ count = gt.dgt_tran_id;
+ (void) printf("%s: discovered %d transceiver%s\n", name, count,
+ count > 1 ? "s" : "");
+ for (i = 0; i < count; i++) {
+ if (tranid != UINT_MAX && i != tranid)
+ continue;
+ if (tranid != UINT_MAX)
+ tran_found = B_TRUE;
+ bzero(&gt, sizeof (gt));
+ gt.dgt_linkid = linkid;
+ gt.dgt_tran_id = i;
+
+ if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, &gt) != 0) {
+ (void) fprintf(stderr, "failed to get tran info for "
+ "%s: %s\n", name, strerror(errno));
+ dltran_errors++;
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ if (dltran_hex && !gt.dgt_present)
+ continue;
+ if (!dltran_hex && !dltran_write) {
+ (void) printf("\ttransceiver %d present: %s\n", i,
+ gt.dgt_present ? "yes" : "no");
+ if (!gt.dgt_present)
+ continue;
+ (void) printf("\ttransceiver %d usable: %s\n", i,
+ gt.dgt_usable ? "yes" : "no");
+ }
+
+ if (dltran_verbose) {
+ dltran_verbose_dump(linkid, i);
+ }
+
+ if (dltran_write) {
+ if (!gt.dgt_present) {
+ (void) fprintf(stderr, "warning: no "
+ "transceiver present in port %d, not "
+ "writing\n", i);
+ dltran_errors++;
+ continue;
+ }
+ dltran_write_page(linkid, i);
+ }
+
+ if (dltran_hex) {
+ printf("transceiver %d data:\n", i);
+ dltran_hex_dump(linkid, i);
+ }
+ }
+
+ if (tranid != UINT_MAX && !tran_found) {
+ dltran_errors++;
+ (void) fprintf(stderr, "failed to find transceiver %d on "
+ "link %s\n", tranid, name);
+ }
+
+ return (DLADM_WALK_CONTINUE);
+}
+
+
+static void
+dltran_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", dltran_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-x | -v | -w file] [tran]...\n"
+ "\n"
+ "\t-v display all transceiver information\n"
+ "\t-w write transceiver data page 0xa0 to file\n"
+ "\t-x dump raw hexadecimal for transceiver\n",
+ dltran_progname);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ dladm_status_t status;
+ const char *outfile = NULL;
+ uint_t count = 0;
+
+ dltran_progname = basename(argv[0]);
+
+ while ((c = getopt(argc, argv, ":xvw:")) != -1) {
+ switch (c) {
+ case 'v':
+ dltran_verbose = B_TRUE;
+ break;
+ case 'x':
+ dltran_hex = B_TRUE;
+ break;
+ case 'w':
+ dltran_write = B_TRUE;
+ outfile = optarg;
+ break;
+ case ':':
+ dltran_usage("option -%c requires an "
+ "operand\n", optopt);
+ return (2);
+ case '?':
+ default:
+ dltran_usage("unknown option: -%c\n", optopt);
+ return (2);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (dltran_verbose)
+ count++;
+ if (dltran_hex)
+ count++;
+ if (dltran_write)
+ count++;
+ if (count > 1) {
+ (void) fprintf(stderr, "only one of -v, -w, and -x may be "
+ "specified\n");
+ return (2);
+ }
+
+ if (dltran_write) {
+ if ((dltran_outfd = open(outfile, O_RDWR | O_TRUNC | O_CREAT,
+ 0644)) < 0) {
+ (void) fprintf(stderr, "failed to open output file "
+ "%s: %s\n", outfile, strerror(errno));
+ return (1);
+ }
+ }
+
+ if ((status = dladm_open(&dltran_hdl)) != DLADM_STATUS_OK) {
+ (void) fprintf(stderr, "failed to open /dev/dld: %s\n",
+ dladm_status2str(status, dltran_dlerrmsg));
+ return (1);
+ }
+
+ if (argc == 0) {
+ (void) dladm_walk_datalink_id(dltran_dump_transceivers,
+ dltran_hdl, NULL, DATALINK_CLASS_PHYS,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ } else {
+ int i;
+ char *c;
+
+ for (i = 0; i < argc; i++) {
+ uint_t tran;
+ uint_t *tranidp = NULL;
+ datalink_id_t linkid;
+
+ if ((c = strrchr(argv[i], '/')) != NULL) {
+ unsigned long u;
+ char *eptr;
+
+ c++;
+ errno = 0;
+ u = strtoul(c, &eptr, 10);
+ if (errno != 0 || *eptr != '\0' ||
+ u >= UINT_MAX) {
+ (void) fprintf(stderr, "failed to "
+ "parse link/transceiver: %s\n",
+ argv[i]);
+ return (1);
+ }
+ c--;
+ *c = '\0';
+ tran = (uint_t)u;
+ tranidp = &tran;
+ }
+
+ if ((status = dladm_name2info(dltran_hdl, argv[i],
+ &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ (void) fprintf(stderr, "failed to get link "
+ "id for link %s: %s\n", argv[i],
+ dladm_status2str(status, dltran_dlerrmsg));
+ return (1);
+ }
+
+ (void) dltran_dump_transceivers(dltran_hdl, linkid,
+ tranidp);
+ }
+ }
+
+ return (dltran_errors != 0 ? 1 : 0);
+}
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index d72fdba6b3..c23483ba27 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -199,6 +199,7 @@ SUBDIRS += \
libsec \
libsecdb \
libsendfile \
+ libsff \
libshare \
libshell \
libsip \
@@ -449,6 +450,7 @@ HDRSUBDIRS= \
libsasl \
libscf \
libsec \
+ libsff \
libshare \
libshell \
libsip \
@@ -575,7 +577,7 @@ auditd_plugins: libbsm libsecdb libgss libmtmalloc
brand: libzonecfg libmapmalloc
cfgadm_plugins: libdevice libdevinfo libhotplug librcm hbaapi libkstat libscf
fm: libexacct libipmi libzfs scsi libdevinfo libdevid libcfgadm \
- libcontract libsysevent ../cmd/sgs/libelf
+ libcontract libsysevent ../cmd/sgs/libelf libdladm
$(SPARC_BLD)fm: libpri
gss_mechs/mech_dh: libgss
gss_mechs/mech_dummy: libgss
@@ -648,6 +650,7 @@ libsasl: libgss pkcs11
libsaveargs: libdisasm
libscf: libuutil libgen libsmbios
libsec: libavl libidmap
+libsff: libnvpair
libshare: libscf libzfs libuuid libfsmgt libsecdb libumem libsmbfs
libshell: libast libcmd libdll libsecdb
libsip: libmd5
diff --git a/usr/src/lib/fm/topo/libtopo/common/hc.c b/usr/src/lib/fm/topo/libtopo/common/hc.c
index 59b9866285..df718d6490 100644
--- a/usr/src/lib/fm/topo/libtopo/common/hc.c
+++ b/usr/src/lib/fm/topo/libtopo/common/hc.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#include <stdio.h>
@@ -179,6 +180,7 @@ static const hcc_t hc_canon[] = {
{ PCIEX_ROOT, TOPO_STABILITY_PRIVATE },
{ PCIEX_SWUP, TOPO_STABILITY_PRIVATE },
{ PCIEX_SWDWN, TOPO_STABILITY_PRIVATE },
+ { PORT, TOPO_STABILITY_PRIVATE },
{ POWERBOARD, TOPO_STABILITY_PRIVATE },
{ POWERMODULE, TOPO_STABILITY_PRIVATE },
{ PSU, TOPO_STABILITY_PRIVATE },
@@ -194,6 +196,7 @@ static const hcc_t hc_canon[] = {
{ STRAND, TOPO_STABILITY_PRIVATE },
{ SUBCHASSIS, TOPO_STABILITY_PRIVATE },
{ SYSTEMBOARD, TOPO_STABILITY_PRIVATE },
+ { TRANSCEIVER, TOPO_STABILITY_PRIVATE },
{ XAUI, TOPO_STABILITY_PRIVATE },
{ XFP, TOPO_STABILITY_PRIVATE }
};
@@ -799,7 +802,7 @@ make_hc_pairs(topo_mod_t *mod, char *fmri, int *num)
int
make_hc_auth(topo_mod_t *mod, char *fmri, char **serial, char **part,
-char **rev, nvlist_t **auth)
+ char **rev, nvlist_t **auth)
{
char *starti, *startn, *endi, *copy;
char *aname = NULL, *aid = NULL, *fs;
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 7b29adad69..9de7a86736 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#ifndef _TOPO_HC_H
@@ -76,6 +76,7 @@ extern "C" {
#define PCIEX_ROOT "pciexrc"
#define PCIEX_SWUP "pciexswu"
#define PCIEX_SWDWN "pciexswd"
+#define PORT "port"
#define POWERBOARD "powerboard"
#define POWERMODULE "powermodule"
#define PSU "psu"
@@ -90,6 +91,7 @@ extern "C" {
#define SP "sp"
#define SUBCHASSIS "subchassis"
#define SYSTEMBOARD "systemboard"
+#define TRANSCEIVER "transceiver"
#define XAUI "xaui"
#define XFP "xfp"
@@ -161,6 +163,20 @@ extern "C" {
#define TOPO_PROP_SAS_PHY_MASK "phy-mask"
#define TOPO_PROP_SAS_CONNECTOR_TYPE "sas-connector-type"
+#define TOPO_PGROUP_PORT "port"
+#define TOPO_PROP_PORT_TYPE "type"
+#define TOPO_PROP_PORT_TYPE_SFF "sff"
+
+#define TOPO_PGROUP_TRANSCEIVER "transceiver"
+#define TOPO_PROP_TRANSCEIVER_TYPE "type"
+#define TOPO_PROP_TRANSCEIVER_USABLE "usable"
+
+#define TOPO_PGROUP_SFF_TRANSCEIVER "sff-transceiver"
+#define TOPO_PORT_SFF_TRANSCEIVER_VENDOR "vendor"
+#define TOPO_PORT_SFF_TRANSCEIVER_PN "part-number"
+#define TOPO_PORT_SFF_TRANSCEIVER_REV "revision"
+#define TOPO_PORT_SFF_TRANSCEIVER_SN "serial-number"
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/fm/topo/modules/Makefile.plugin b/usr/src/lib/fm/topo/modules/Makefile.plugin
index 7eef744f46..13d90b8a27 100644
--- a/usr/src/lib/fm/topo/modules/Makefile.plugin
+++ b/usr/src/lib/fm/topo/modules/Makefile.plugin
@@ -56,7 +56,7 @@ plat_ROOTCONF = $(PLATFORMS:%=$(ROOT)/usr/platform/%/lib/fm/topo/plugins/$(CONF)
ROOTCONF = $($(CLASS)_ROOTCONF)
LINTFLAGS = -msux
-LINTFILES = $(SRCS:%.c=%.ln)
+LINTFILES = $(MODULESRCS:%.c=%.ln) $(SHAREDSRCS:%.c=%.ln)
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
@@ -105,6 +105,9 @@ clobber: clean
%.ln: ../../common/$(MODULE)/%.c
$(LINT.c) -c $<
+%.ln: ../../common/$(SHAREDMODULE)/%.c
+ $(LINT.c) -c $<
+
%.ln: %.c
$(LINT.c) -c $<
diff --git a/usr/src/lib/fm/topo/modules/common/Makefile b/usr/src/lib/fm/topo/modules/common/Makefile
index fa38496755..d725016aef 100644
--- a/usr/src/lib/fm/topo/modules/common/Makefile
+++ b/usr/src/lib/fm/topo/modules/common/Makefile
@@ -22,6 +22,7 @@
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright (c) 2017, Joyent, Inc.
#
SUBDIRS = \
@@ -29,6 +30,7 @@ SUBDIRS = \
fac_prov_ipmi \
fac_prov_mptsas \
ipmi \
+ nic \
ses \
xfp
diff --git a/usr/src/lib/fm/topo/modules/common/nic/Makefile b/usr/src/lib/fm/topo/modules/common/nic/Makefile
new file mode 100644
index 0000000000..084b49dcd1
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/common/nic/Makefile
@@ -0,0 +1,26 @@
+#
+# 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) 2017, Joyent, Inc.
+#
+
+MODULE = nic
+CLASS = common
+SHAREDMODULE = shared
+
+MODULESRCS = topo_nic.c
+SHAREDSRCS = topo_port.c topo_transceiver.c
+
+include ../../Makefile.plugin
+
+CPPFLAGS += -I../shared
+LDLIBS += -ldevinfo -ldladm -lsff
diff --git a/usr/src/lib/fm/topo/modules/common/nic/topo_nic.c b/usr/src/lib/fm/topo/modules/common/nic/topo_nic.c
new file mode 100644
index 0000000000..51c37142c5
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/common/nic/topo_nic.c
@@ -0,0 +1,241 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * This module covers enumerating properties of physical NICs. At this time, as
+ * various devices are discovered that may relate to various networking gear, we
+ * will attempt to enumerate ports and transceivers under them, if requested.
+ */
+
+#include <strings.h>
+#include <libdevinfo.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libsff.h>
+#include <unistd.h>
+#include <sys/dld_ioc.h>
+#include <sys/dld.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_transceiver.h>
+
+#include "topo_nic.h"
+
+/*
+ * Create an instance of a transceiver with the specified id. We must create
+ * both its port and the transceiver node.
+ */
+static int
+nic_create_transceiver(topo_mod_t *mod, tnode_t *pnode, dladm_handle_t handle,
+ datalink_id_t linkid, uint_t tranid)
+{
+ int ret;
+ tnode_t *port;
+ dld_ioc_gettran_t dgt;
+ dld_ioc_tranio_t dti;
+ uint8_t buf[256];
+ char ouibuf[16];
+ char *vendor = NULL, *part = NULL, *rev = NULL, *serial = NULL;
+ nvlist_t *nvl = NULL;
+
+ if ((ret = port_create_sff(mod, pnode, tranid, &port)) != 0)
+ return (ret);
+
+ bzero(&dgt, sizeof (dgt));
+ dgt.dgt_linkid = linkid;
+ dgt.dgt_tran_id = tranid;
+
+ if (ioctl(dladm_dld_fd(handle), DLDIOC_GETTRAN, &dgt) != 0) {
+ if (errno == ENOTSUP)
+ return (0);
+ return (-1);
+ }
+
+ if (dgt.dgt_present == 0)
+ return (0);
+
+ bzero(&dti, sizeof (dti));
+ dti.dti_linkid = linkid;
+ dti.dti_tran_id = tranid;
+ dti.dti_page = 0xa0;
+ dti.dti_nbytes = sizeof (buf);
+ dti.dti_buf = (uintptr_t)buf;
+
+ if (ioctl(dladm_dld_fd(handle), DLDIOC_READTRAN, &dti) == 0) {
+ uchar_t *oui;
+ uint_t nbyte;
+
+ if (libsff_parse(buf, dti.dti_nbytes, dti.dti_page,
+ &nvl) == 0) {
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_VENDOR,
+ &vendor)) != 0 && nvlist_lookup_byte_array(nvl,
+ LIBSFF_KEY_OUI, &oui, &nbyte) == 0 && nbyte == 3) {
+ if (snprintf(ouibuf, sizeof (ouibuf),
+ "%02x:%02x:%02x", oui[0], oui[1], oui[2]) <
+ sizeof (ouibuf)) {
+ vendor = ouibuf;
+ }
+ } else if (ret != 0) {
+ vendor = NULL;
+ }
+
+ if (nvlist_lookup_string(nvl, LIBSFF_KEY_PART,
+ &part) != 0) {
+ part = NULL;
+ }
+
+ if (nvlist_lookup_string(nvl, LIBSFF_KEY_REVISION,
+ &rev) != 0) {
+ rev = NULL;
+ }
+
+ if (nvlist_lookup_string(nvl, LIBSFF_KEY_SERIAL,
+ &serial) != 0) {
+ serial = NULL;
+ }
+ }
+ }
+
+ if (transceiver_range_create(mod, port, 0, 0) != 0) {
+ nvlist_free(nvl);
+ return (-1);
+ }
+
+ if (transceiver_create_sff(mod, port, 0, dgt.dgt_usable, vendor, part,
+ rev, serial, NULL) != 0) {
+ nvlist_free(nvl);
+ return (-1);
+ }
+
+ nvlist_free(nvl);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+nic_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
+ topo_instance_t min, topo_instance_t max, void *modarg, void *data)
+{
+ di_node_t din = data;
+ datalink_id_t linkid;
+ dladm_handle_t handle;
+ dld_ioc_gettran_t dgt;
+ uint_t ntrans, i;
+ char dname[MAXNAMELEN];
+
+ if (strcmp(name, NIC) != 0) {
+ topo_mod_dprintf(mod, "nic_enum: asked to enumerate unknown "
+ "component: %s\n", name);
+ return (-1);
+ }
+
+ if (din == NULL) {
+ topo_mod_dprintf(mod, "nic_enum: missing data argument\n");
+ return (-1);
+ }
+
+ if ((handle = topo_mod_getspecific(mod)) == NULL) {
+ topo_mod_dprintf(mod, "nic_enum: failed to get nic module "
+ "specific data\n");
+ return (-1);
+ }
+
+ if (snprintf(dname, sizeof (dname), "%s%d", di_driver_name(din),
+ di_instance(din)) >= sizeof (dname)) {
+ topo_mod_dprintf(mod, "nic_enum: device name overflowed "
+ "internal buffer\n");
+ return (-1);
+ }
+
+ if (dladm_dev2linkid(handle, dname, &linkid) != DLADM_STATUS_OK)
+ return (-1);
+
+ bzero(&dgt, sizeof (dgt));
+ dgt.dgt_linkid = linkid;
+ dgt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN;
+
+ if (ioctl(dladm_dld_fd(handle), DLDIOC_GETTRAN, &dgt) != 0) {
+ if (errno == ENOTSUP)
+ return (0);
+ return (-1);
+ }
+
+ ntrans = dgt.dgt_tran_id;
+ if (ntrans == 0)
+ return (0);
+
+ if (port_range_create(mod, pnode, 0, ntrans - 1) != 0)
+ return (-1);
+
+ for (i = 0; i < ntrans; i++) {
+ if (nic_create_transceiver(mod, pnode, handle, linkid, i) != 0)
+ return (-1);
+ }
+
+ return (0);
+}
+
+static const topo_modops_t nic_ops = {
+ nic_enum, NULL
+};
+
+static topo_modinfo_t nic_mod = {
+ NIC, FM_FMRI_SCHEME_HC, NIC_VERSION, &nic_ops
+};
+
+int
+_topo_init(topo_mod_t *mod, topo_version_t version)
+{
+ dladm_handle_t handle;
+
+ if (getenv("TOPONICDEBUG") != NULL)
+ topo_mod_setdebug(mod);
+
+ topo_mod_dprintf(mod, "_mod_init: "
+ "initializing %s enumerator\n", NIC);
+
+ if (version != NIC_VERSION) {
+ return (-1);
+ }
+
+ if (dladm_open(&handle) != 0)
+ return (-1);
+
+ if (topo_mod_register(mod, &nic_mod, TOPO_VERSION) != 0) {
+ dladm_close(handle);
+ return (-1);
+ }
+
+ topo_mod_setspecific(mod, handle);
+
+ return (0);
+}
+
+void
+_topo_fini(topo_mod_t *mod)
+{
+ dladm_handle_t handle;
+
+ if ((handle = topo_mod_getspecific(mod)) == NULL)
+ return;
+
+ dladm_close(handle);
+ topo_mod_setspecific(mod, NULL);
+}
diff --git a/usr/src/lib/fm/topo/modules/common/nic/topo_nic.h b/usr/src/lib/fm/topo/modules/common/nic/topo_nic.h
new file mode 100644
index 0000000000..ba661542c4
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/common/nic/topo_nic.h
@@ -0,0 +1,34 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+#ifndef _TOPO_NIC_H
+#define _TOPO_NIC_H
+
+/*
+ * Common NIC module header file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NIC "nic"
+#define NIC_VERSION 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TOPO_NIC_H */
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 1f3ba4478e..36dc26aa8d 100644
--- a/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c
+++ b/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#include <sys/fm/protocol.h>
@@ -43,6 +44,7 @@
#include <did.h>
#include <did_props.h>
#include <util.h>
+#include <topo_nic.h>
extern txprop_t Bus_common_props[];
extern txprop_t Dev_common_props[];
@@ -489,40 +491,52 @@ declare_dev_and_fn(topo_mod_t *mod, tnode_t *bus, tnode_t **dev, di_node_t din,
*/
else if (class == PCI_CLASS_NET &&
di_uintprop_get(mod, din, DI_VENDIDPROP, &vid) >= 0 &&
- di_uintprop_get(mod, din, DI_DEVIDPROP, &did) >= 0) {
- if (vid == SUN_VENDOR_ID && did == NEPTUNE_DEVICE_ID) {
- /*
- * Is this an adapter card? Check the bus's physlot
- */
- dp = did_find(mod, topo_node_getspecific(bus));
- if (did_physlot(dp) >= 0) {
- topo_mod_dprintf(mod, "Found Neptune slot\n");
- (void) topo_mod_enummap(mod, fn,
- "xfp", FM_FMRI_SCHEME_HC);
+ di_uintprop_get(mod, din, DI_DEVIDPROP, &did) >= 0 &&
+ vid == SUN_VENDOR_ID && did == NEPTUNE_DEVICE_ID) {
+ /*
+ * Is this an adapter card? Check the bus's physlot
+ */
+ dp = did_find(mod, topo_node_getspecific(bus));
+ if (did_physlot(dp) >= 0) {
+ topo_mod_dprintf(mod, "Found Neptune slot\n");
+ (void) topo_mod_enummap(mod, fn,
+ "xfp", FM_FMRI_SCHEME_HC);
+ } else {
+ topo_mod_dprintf(mod, "Found Neptune ASIC\n");
+ if (topo_mod_load(mod, XAUI, TOPO_VERSION) == NULL) {
+ topo_mod_dprintf(mod, "pcibus enum "
+ "could not load xaui enum\n");
+ (void) topo_mod_seterrno(mod,
+ EMOD_PARTIAL_ENUM);
+ return;
} else {
- topo_mod_dprintf(mod, "Found Neptune ASIC\n");
- if (topo_mod_load(mod, XAUI, TOPO_VERSION) ==
- NULL) {
- topo_mod_dprintf(mod, "pcibus enum "
- "could not load xaui enum\n");
- (void) topo_mod_seterrno(mod,
- EMOD_PARTIAL_ENUM);
+ if (topo_node_range_create(mod, fn,
+ XAUI, 0, 1) < 0) {
+ topo_mod_dprintf(mod,
+ "child_range_add for "
+ "XAUI failed: %s\n",
+ topo_strerror(
+ topo_mod_errno(mod)));
return;
- } else {
- if (topo_node_range_create(mod, fn,
- XAUI, 0, 1) < 0) {
- topo_mod_dprintf(mod,
- "child_range_add for "
- "XAUI failed: %s\n",
- topo_strerror(
- topo_mod_errno(mod)));
- return;
- }
- (void) topo_mod_enumerate(mod, fn,
- XAUI, XAUI, fnno, fnno, fn);
}
+ (void) topo_mod_enumerate(mod, fn,
+ XAUI, XAUI, fnno, fnno, fn);
}
}
+ } else if (class == PCI_CLASS_NET) {
+ /*
+ * Ask the nic module if there are any nodes that need to be
+ * enumerated under this device. This might include things like
+ * transceivers or some day, LEDs.
+ */
+ if (topo_mod_load(mod, NIC, NIC_VERSION) == NULL) {
+ topo_mod_dprintf(mod, "pcibus enum could not load "
+ "nic enum\n");
+ (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
+ return;
+ }
+
+ (void) topo_mod_enumerate(mod, fn, NIC, NIC, 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
new file mode 100644
index 0000000000..8f0ba8690e
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/common/shared/topo_port.c
@@ -0,0 +1,130 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+#include <sys/fm/protocol.h>
+#include <fm/topo_mod.h>
+#include <fm/topo_list.h>
+#include <fm/topo_method.h>
+
+#include <topo_port.h>
+
+/*
+ * Common routines to create port entries in the topology tree.
+ */
+
+static const topo_pgroup_info_t port_pgroup = {
+ TOPO_PGROUP_PORT,
+ TOPO_STABILITY_PRIVATE,
+ TOPO_STABILITY_PRIVATE,
+ 1
+};
+
+int
+port_range_create(topo_mod_t *mod, tnode_t *pnode, topo_instance_t min,
+ topo_instance_t max)
+{
+ return (topo_node_range_create(mod, pnode, PORT, min, max));
+}
+
+/*
+ * Create a port node, specifying the type of port it is. This will create the
+ * common port property group and populate it. The caller will need to populate
+ * the port-specific property group as needed.
+ */
+static tnode_t *
+port_create_common(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
+ const char *type)
+{
+ int err;
+ tnode_t *tn = NULL;
+ nvlist_t *fmri = NULL, *auth = NULL, *presource = NULL;
+
+ if (type == NULL) {
+ topo_mod_dprintf(mod, "port_create_common missing type "
+ "argument\n");
+ goto error;
+ }
+
+ if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
+ topo_mod_dprintf(mod, "topo_mod_auth() failed: %s\n",
+ topo_mod_errmsg(mod));
+ goto error;
+ }
+
+ if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, PORT,
+ inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
+ topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s\n",
+ topo_mod_errmsg(mod));
+ goto error;
+ }
+
+ if ((tn = topo_node_bind(mod, pnode, PORT, inst, fmri)) == NULL) {
+ topo_mod_dprintf(mod, "topo_node_bind() failed: %s\n",
+ topo_mod_errmsg(mod));
+ goto error;
+ }
+
+ /*
+ * The FRU is always set to the FMRI of the parent device for a port.
+ */
+ if (topo_node_resource(pnode, &presource, &err) != 0) {
+ topo_mod_dprintf(mod, "topo_node_resource() failed: %s\n",
+ topo_strerror(err));
+ goto error;
+ }
+
+ if (topo_node_fru_set(tn, presource, 0, &err) != 0) {
+ topo_mod_dprintf(mod, "topo_node_fru_set() failed: %s\n",
+ topo_strerror(err));
+ goto error;
+ }
+
+ if (topo_pgroup_create(tn, &port_pgroup, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to create property group %s: "
+ "%s\n", TOPO_PGROUP_PORT, topo_strerror(err));
+ goto error;
+ }
+
+ if (topo_prop_set_string(tn, TOPO_PGROUP_PORT, TOPO_PROP_PORT_TYPE,
+ TOPO_PROP_IMMUTABLE, type, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set %s property: %s\n",
+ TOPO_PROP_PORT_TYPE, topo_strerror(err));
+ goto error;
+ }
+
+ nvlist_free(fmri);
+ nvlist_free(auth);
+ nvlist_free(presource);
+ return (tn);
+error:
+ topo_node_unbind(tn);
+ nvlist_free(fmri);
+ nvlist_free(auth);
+ nvlist_free(presource);
+ return (NULL);
+}
+
+int
+port_create_sff(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_SFF);
+ 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
new file mode 100644
index 0000000000..78ace0b0bb
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/common/shared/topo_port.h
@@ -0,0 +1,36 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+#ifndef _TOPO_PORT_H
+#define _TOPO_PORT_H
+
+/*
+ * Routines to manage and create ports.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+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 **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TOPO_PORT_H */
diff --git a/usr/src/lib/fm/topo/modules/common/shared/topo_transceiver.c b/usr/src/lib/fm/topo/modules/common/shared/topo_transceiver.c
new file mode 100644
index 0000000000..25c4276dab
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/common/shared/topo_transceiver.c
@@ -0,0 +1,185 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+#include <sys/fm/protocol.h>
+#include <fm/topo_mod.h>
+#include <fm/topo_list.h>
+#include <fm/topo_method.h>
+
+/*
+ * Common routines to create transceiver entries in the topology tree.
+ */
+
+static const topo_pgroup_info_t transceiver_pgroup = {
+ TOPO_PGROUP_TRANSCEIVER,
+ TOPO_STABILITY_PRIVATE,
+ TOPO_STABILITY_PRIVATE,
+ 1
+};
+
+static const topo_pgroup_info_t sff_transceiver_pgroup = {
+ TOPO_PGROUP_SFF_TRANSCEIVER,
+ TOPO_STABILITY_PRIVATE,
+ TOPO_STABILITY_PRIVATE,
+ 1
+};
+
+int
+transceiver_range_create(topo_mod_t *mod, tnode_t *pnode, topo_instance_t min,
+ topo_instance_t max)
+{
+ return (topo_node_range_create(mod, pnode, TRANSCEIVER, min, max));
+}
+
+static tnode_t *
+transceiver_create_common(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
+ const char *type, boolean_t usable, const char *part, const char *rev,
+ const char *serial)
+{
+ int err;
+ tnode_t *tn = NULL;
+ nvlist_t *fmri = NULL, *auth = NULL;
+
+ if (type == NULL) {
+ topo_mod_dprintf(mod, "transceiver_create_common missing type "
+ "argument");
+ goto error;
+ }
+
+ if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
+ topo_mod_dprintf(mod, "topo_mod_auth() failed: %s\n",
+ topo_mod_errmsg(mod));
+ goto error;
+ }
+
+ if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
+ TRANSCEIVER, inst, NULL, auth, part, rev, serial)) == NULL) {
+ topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s\n",
+ topo_mod_errmsg(mod));
+ goto error;
+ }
+
+ if ((tn = topo_node_bind(mod, pnode, TRANSCEIVER, inst, fmri)) ==
+ NULL) {
+ topo_mod_dprintf(mod, "topo_node_bind() failed: %s\n",
+ topo_mod_errmsg(mod));
+ goto error;
+ }
+
+ /*
+ * The FRU for a transceiver is always itself.
+ */
+ if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
+ topo_mod_dprintf(mod, "topo_node_fru_set() failed: %s\n",
+ topo_strerror(err));
+ goto error;
+ }
+
+ if (topo_pgroup_create(tn, &transceiver_pgroup, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to create property group %s: "
+ "%s\n", TOPO_PGROUP_TRANSCEIVER, topo_strerror(err));
+ goto error;
+ }
+
+ if (topo_prop_set_string(tn, TOPO_PGROUP_TRANSCEIVER,
+ TOPO_PROP_TRANSCEIVER_TYPE, TOPO_PROP_IMMUTABLE, type,
+ &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set %s property: %s\n",
+ TOPO_PROP_TRANSCEIVER_TYPE, topo_strerror(err));
+ goto error;
+ }
+
+ if (topo_prop_set_string(tn, TOPO_PGROUP_TRANSCEIVER,
+ TOPO_PROP_TRANSCEIVER_USABLE, TOPO_PROP_IMMUTABLE,
+ usable ? "true" : "false", &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set %s property: %s\n",
+ TOPO_PROP_TRANSCEIVER_USABLE, topo_strerror(err));
+ goto error;
+ }
+
+ nvlist_free(fmri);
+ nvlist_free(auth);
+ return (tn);
+
+error:
+ topo_node_unbind(tn);
+ nvlist_free(fmri);
+ nvlist_free(auth);
+ return (NULL);
+}
+
+int
+transceiver_create_sff(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
+ boolean_t useable, const char *vendor, const char *part, const char *rev,
+ const char *serial, tnode_t **nodep)
+{
+ int err;
+ tnode_t *tn = NULL;
+
+ if ((tn = transceiver_create_common(mod, pnode, inst,
+ TOPO_PROP_PORT_TYPE_SFF, useable, part, rev, serial)) == NULL) {
+ return (-1);
+ }
+
+ /*
+ * Always create the SFF property group, even if we can't fill in any
+ * properties.
+ */
+ if (topo_pgroup_create(tn, &sff_transceiver_pgroup, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to create property group %s: "
+ "%s\n", TOPO_PGROUP_SFF_TRANSCEIVER, topo_strerror(err));
+ goto error;
+ }
+
+ if (vendor != NULL && topo_prop_set_string(tn,
+ TOPO_PGROUP_SFF_TRANSCEIVER, TOPO_PORT_SFF_TRANSCEIVER_VENDOR,
+ TOPO_PROP_IMMUTABLE, vendor, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set %s property: %s\n",
+ TOPO_PORT_SFF_TRANSCEIVER_VENDOR, topo_strerror(err));
+ goto error;
+ }
+
+ if (part != NULL && topo_prop_set_string(tn,
+ TOPO_PGROUP_SFF_TRANSCEIVER, TOPO_PORT_SFF_TRANSCEIVER_PN,
+ TOPO_PROP_IMMUTABLE, part, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set %s property: %s\n",
+ TOPO_PORT_SFF_TRANSCEIVER_PN, topo_strerror(err));
+ goto error;
+ }
+
+ if (rev != NULL && topo_prop_set_string(tn,
+ TOPO_PGROUP_SFF_TRANSCEIVER, TOPO_PORT_SFF_TRANSCEIVER_REV,
+ TOPO_PROP_IMMUTABLE, rev, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set %s property: %s\n",
+ TOPO_PORT_SFF_TRANSCEIVER_REV, topo_strerror(err));
+ goto error;
+ }
+
+ if (serial != NULL && topo_prop_set_string(tn,
+ TOPO_PGROUP_SFF_TRANSCEIVER, TOPO_PORT_SFF_TRANSCEIVER_SN,
+ TOPO_PROP_IMMUTABLE, serial, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set %s property: %s\n",
+ TOPO_PORT_SFF_TRANSCEIVER_SN, topo_strerror(err));
+ goto error;
+ }
+
+ if (nodep != NULL)
+ *nodep = tn;
+ return (0);
+
+error:
+ topo_node_unbind(tn);
+ return (-1);
+}
diff --git a/usr/src/lib/fm/topo/modules/common/shared/topo_transceiver.h b/usr/src/lib/fm/topo/modules/common/shared/topo_transceiver.h
new file mode 100644
index 0000000000..f371598739
--- /dev/null
+++ b/usr/src/lib/fm/topo/modules/common/shared/topo_transceiver.h
@@ -0,0 +1,37 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+#ifndef _TOPO_TRANSCEIVER_H
+#define _TOPO_TRANSCEIVER_H
+
+/*
+ * Routines to manage and create ports.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int transceiver_range_create(topo_mod_t *, tnode_t *, topo_instance_t,
+ topo_instance_t);
+extern int transceiver_create_sff(topo_mod_t *, tnode_t *, topo_instance_t,
+ boolean_t, const char *, const char *, const char *, const char *,
+ tnode_t **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TOPO_TRANSCEIVER_H */
diff --git a/usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile b/usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile
index 1b34ed3510..b41606985a 100644
--- a/usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile
+++ b/usr/src/lib/fm/topo/modules/i86pc/pcibus/Makefile
@@ -21,6 +21,7 @@
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, Joyent, Inc.
#
MODULE = pcibus
@@ -28,6 +29,7 @@ ARCH = i86pc
CLASS = arch
UTILDIR = ../../common/pcibus
HBDIR = ../../common/hostbridge
+NICDIR = ../../common/nic
UTILSRCS = did.c did_hash.c did_props.c util.c
PCISRCS = pcibus.c pcibus_labels.c pcibus_hba.c
@@ -37,4 +39,4 @@ include ../../Makefile.plugin
LDLIBS += -ldevinfo -lsmbios
-CPPFLAGS += -I$(UTILDIR) -I$(HBDIR)
+CPPFLAGS += -I$(UTILDIR) -I$(HBDIR) -I $(NICDIR)
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 277e868277..82ecb535c2 100644
--- a/usr/src/lib/fm/topo/modules/sun4/pcibus/Makefile.pci
+++ b/usr/src/lib/fm/topo/modules/sun4/pcibus/Makefile.pci
@@ -27,6 +27,7 @@ MODULE = pcibus
CLASS = arch
SUN4DIR = ../../sun4/$(MODULE)
UTILDIR = ../../common/pcibus
+NICDIR = ../../common/nic
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
@@ -36,7 +37,7 @@ MODULESRCS = $(PCISRCS) $(UTILSRCS) pci_$(ARCH).c
include ../../Makefile.plugin
LDLIBS += -ldevinfo -lsmbios
-CPPFLAGS += -I$(SUN4DIR) -I$(UTILDIR) -I$(HBDIR)
+CPPFLAGS += -I$(SUN4DIR) -I$(UTILDIR) -I$(HBDIR) -I$(NICDIR)
%.o: $(SUN4DIR)/%.c
$(COMPILE.c) -o $@ $<
diff --git a/usr/src/lib/libsff/Makefile b/usr/src/lib/libsff/Makefile
new file mode 100644
index 0000000000..39c13c30bf
--- /dev/null
+++ b/usr/src/lib/libsff/Makefile
@@ -0,0 +1,44 @@
+#
+# 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) 2017 Joyent, Inc.
+#
+
+include ../Makefile.lib
+
+HDRS = libsff.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber lint: $(SUBDIRS)
+
+install: $(SUBDIRS) $(VARPD_MAPFILES) install_h
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libsff/Makefile.com b/usr/src/lib/libsff/Makefile.com
new file mode 100644
index 0000000000..dab4bfbca3
--- /dev/null
+++ b/usr/src/lib/libsff/Makefile.com
@@ -0,0 +1,34 @@
+#
+# 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) 2017 Joyent, Inc. All rights reserved.
+#
+
+LIBRARY = libsff.a
+VERS = .1
+OBJECTS = libsff.o
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lnvpair
+CPPFLAGS += -I../common
+
+SRCDIR = ../common
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libsff/amd64/Makefile b/usr/src/lib/libsff/amd64/Makefile
new file mode 100644
index 0000000000..4d3cfa1f81
--- /dev/null
+++ b/usr/src/lib/libsff/amd64/Makefile
@@ -0,0 +1,19 @@
+#
+# 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) 2017 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libsff/common/libsff.c b/usr/src/lib/libsff/common/libsff.c
new file mode 100644
index 0000000000..43ca69c75d
--- /dev/null
+++ b/usr/src/lib/libsff/common/libsff.c
@@ -0,0 +1,1414 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Parse raw SFF data into an nvlist that can be processed by users, providing
+ * them with what can be printable strings. At the moment, we handle the
+ * majority of parsing page 0xa0 based on SFF 8472 (thus covering INF-8074 and
+ * friends) and SFF 8636 (thus covering SFF-8436 and friends). Interfaces that
+ * parse data into logical structures may be useful to add when considering
+ * monitoring data in page 0xa2.
+ *
+ * When parsing, we try to make sure that the user has supplied, or at least
+ * thinks they have supplied, a buffer of sufficient length. The general design
+ * is that we require the buffer to be large enough to cover all of the offsets
+ * that we care about. If the buffer isn't this large, then we leave it be.
+ *
+ * This library is private and subject to change at any time.
+ */
+
+#include <assert.h>
+#include <strings.h>
+#include <libsff.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "sff.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Maximum size of a string buffer while parsing.
+ */
+#define SFP_STRBUF 128
+
+/*
+ * Minimum length of the buffer we require to parse the SFP data.
+ */
+#define SFP_MIN_LEN_8472 96
+#define SFP_MIN_LEN_8636 224
+
+/*
+ * This table is derived from SFF 8024 Section 4.1, Table 4-1.
+ */
+static const char *sff_8024_id_strs[SFF_8024_NIDS] = {
+ "Unknown or Unspecified",
+ "GBIC",
+ "Module/connector soldered to motherboard",
+ "SFP/SFP+/SFP28",
+ "300 pin XBI",
+ "XENPAK",
+ "XFP",
+ "XFF",
+ "XFP-E",
+ "XPAK",
+ "X2",
+ "DWDM-SFP/SFP+ (not using SFF-8472)",
+ "QSFP",
+ "QSFP+ or later",
+ "CXP or later",
+ "Shielded Mini Multilane HD 4X",
+ "Shielded Mini Multilane HD 8X",
+ "QSFP28 or later",
+ "CXP2 (aka CXP28) or later",
+ "CDFP (Style 1/Style2)",
+ "Shielded Mini Multilane HD 4X Fanout Cable",
+ "Shielded Mini Multilane HD 8X Fanout Cable",
+ "CDFP (Style 3)",
+ "microQSFP"
+};
+
+/*
+ * The set of values used for the encoding depends on whether we're a basic SFP
+ * device or not. The values are inconsistent between SFP and QSFP based
+ * devices.
+ *
+ * This table is derived from SFF 8024 r3.9 Table 4-2.
+ */
+#define SFF_8024_NENCS 9
+static const char *sff_8024_enc_sfp[] = {
+ "Unspecified",
+ "8B/10B",
+ "4B/5B",
+ "NRZ",
+ "Manchester",
+ "SONET Scrambled",
+ "64B/66B",
+ "256B/257B",
+ "PAM4"
+};
+
+static const char *sff_8024_enc_qsfp[] = {
+ "Unspecified",
+ "8B/10B",
+ "4B/5B",
+ "NRZ",
+ "SONET Scrambled",
+ "64B/66B",
+ "Manchester",
+ "256B/257B",
+ "PAM4"
+};
+
+/*
+ * This table is derived from SFF 8024 r3.9 Section 4.4.
+ */
+#define SFF_8024_EXT_SPEC_NENTRIES 27
+static const char *sff_8024_ext_spec[] = {
+ "Unspecified",
+ "100G AOC or 25GAUI C2M AOC",
+ "100GBASE-SR4 or 25GBASE-SR",
+ "100GBASE-LR4 or 25GBASE-LR",
+ "100GBASE-ER4 or 25GBASE-ER",
+ "100GBASE-SR10",
+ "100G CWDM4",
+ "100G PSM4 Parallel SMF",
+ "100G ACC or 25GAUI C2M ACC",
+ "Obsolete",
+ "Reserved",
+ "100GBASE-CR4 or 25GBASE-CR CA-L",
+ "25GBASE-CR CA-S",
+ "25GBASE-CR CA-N",
+ "Reserved",
+ "Reserved",
+ "40GBASE-ER4",
+ "4 x 10GBASE-SR",
+ "40G PSM4 Parallel SMF",
+ "G959.1 profile P1I1-2D1",
+ "G959.1 profile P1S1-2D2",
+ "G959.1 profile P1L1-2D2",
+ "10GBASE-T with SFI electrical interface",
+ "100G CLR4",
+ "100G AOC or 25GAUI C2M AOC",
+ "100G ACC or 25GAUI C2M ACC",
+ "100GE-DWDM2"
+};
+
+typedef struct sff_pair {
+ uint_t sp_val;
+ const char *sp_name;
+} sff_pair_t;
+
+/*
+ * This table is derived from SFF 8024 r3.9 Section 4.3.
+ */
+static sff_pair_t sff_8024_connectors[] = {
+ { 0x00, "Unknown" },
+ { 0x01, "SC (Subscriber Connector)" },
+ { 0x02, "Fibre Channel Style 1 copper connector" },
+ { 0x03, "Fibre Channel Style 2 copper connector" },
+ { 0x04, "BNC/TNC (Bayonet/Threaded Neill-Concelman)" },
+ { 0x05, "Fibre Channel coax headers" },
+ { 0x06, "Fiber Jack" },
+ { 0x07, "LC (Lucent Connector)" },
+ { 0x08, "MT-RJ (Mechanical Transfer - Registered Jack)" },
+ { 0x09, "MU (Multiple Optical)" },
+ { 0x0A, "SG" },
+ { 0x0B, "Optical Pigtail" },
+ { 0x0C, "MPO 1x12 (Multifiber Parallel Optic)" },
+ { 0x0D, "MPO 2x16" },
+ { 0x20, "HSSDC II (High Speed Serial Data Connector)" },
+ { 0x21, "Copper pigtail" },
+ { 0x22, "RJ45 (Registered Jack)" },
+ { 0x23, "No separable connector" },
+ { 0x24, "MXC 2x16" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3.
+ */
+#define SFF_8472_COMP_10GETH_MASK 0xf0
+static sff_pair_t sff_8472_comp_10geth[] = {
+ { 0x80, "10G Base-ER" },
+ { 0x40, "10G Base-LRM" },
+ { 0x20, "10G Base-LR" },
+ { 0x10, "10G Base-SR" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3.
+ */
+#define SFF_8472_COMP_IB_MASK 0x0f
+static sff_pair_t sff_8472_comp_ib[] = {
+ { 0x08, "1X SX" },
+ { 0x04, "1X LX" },
+ { 0x02, "1X Copper Active" },
+ { 0x01, "1X Copper Passive" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3.
+ */
+#define SFF_8472_COMP_ESCON_MASK 0xc0
+static sff_pair_t sff_8472_comp_escon[] = {
+ { 0x80, "ESCON MMF, 1310nm LED" },
+ { 0x40, "ESCON SMF, 1310nm Laser" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3. These values come from both
+ * bytes 4 and 5. We treat this as a uint16_t with the low byte as byte 4 and
+ * the high byte as byte 5.
+ */
+#define SFF_8472_COMP_SOCON_MASK 0x773f
+static sff_pair_t sff_8472_comp_sonet[] = {
+ { 0x20, "OC-192, short reach" },
+ { 0x10, "SONET reach specifier bit 1" },
+ { 0x08, "ONET reach specifier bit 2" },
+ { 0x04, "OC-48, long reach" },
+ { 0x02, "OC-48, intermediate reach" },
+ { 0x01, "OC-48, short reach" },
+ /* 0x8000 is unallocated */
+ { 0x4000, "OC-12, single mode, long reach" },
+ { 0x2000, "OC-12, single mode, inter. reach" },
+ { 0x1000, "OC-12, short reach" },
+ /* 0x800 is unallocted */
+ { 0x0400, "OC-3, single mode, long reach" },
+ { 0x0200, "OC-3, single mode, inter. reach" },
+ { 0x0100, "OC-3, short reach" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3.
+ */
+#define SFF_8472_COMP_ETH_MASK 0xff
+static sff_pair_t sff_8472_comp_eth[] = {
+ { 0x80, "BASE-PX" },
+ { 0x40, "BASE-BX10" },
+ { 0x20, "100BASE-FX" },
+ { 0x10, "100BASE-LX/LX10" },
+ { 0x08, "1000BASE-T" },
+ { 0x04, "1000BASE-CX" },
+ { 0x02, "1000BASE-LX" },
+ { 0x01, "1000BASE-SX" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3.
+ */
+#define SFF_8472_COMP_FCLEN_MASK 0xf8
+static sff_pair_t sff_8472_comp_fclen[] = {
+ { 0x80, "very long distance (V)" },
+ { 0x40, "short distance (S)" },
+ { 0x20, "intermeddiate distance (I)" },
+ { 0x10, "long distance (L)" },
+ { 0x08, "medium distance (M)" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3. These values come from both
+ * bytes 7 and 8. We treat this as a uint16_t with the low byte as byte 7 and
+ * the high byte as byte 8.
+ */
+#define SFF_8472_COMP_TECH_MASK 0xf007
+static sff_pair_t sff_8472_comp_tech[] = {
+ { 0x4, "Shortwave laser, linear Rx (SA)" },
+ { 0x2, "Longwave laser (LC)" },
+ { 0x1, "Electrical inter-enclosure (EL)" },
+ { 0x8000, "Electrical intra-enclosure (EL)" },
+ { 0x4000, "Shortwave laser w/o OFC (SN)" },
+ { 0x2000, "Shortwave laser with OFC (SL)" },
+ { 0x1000, "Longwave laser (LL)" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3.
+ */
+#define SFF_8472_COMP_CABLE_MASK 0x0c
+#define SFF_8472_COMP_CABLE_ACTIVE 0x08
+#define SFF_8472_COMP_CABLE_PASSIVE 0x04
+static sff_pair_t sff_8472_comp_cable[] = {
+ { 0x08, "Active Cable" },
+ { 0x04, "Passive Cable" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3.
+ */
+#define SFF_8472_COMP_MEDIA_MASK 0xfd
+static sff_pair_t sff_8472_comp_media[] = {
+ { 0x80, "Twin Axial Pair (TW)" },
+ { 0x40, "Twisted Pair (TP)" },
+ { 0x20, "Miniature Coax (MI)" },
+ { 0x10, "Video Coax (TV)" },
+ { 0x08, "Multimode, 62.5um (M6)" },
+ { 0x04, "Multimode, 50um (M5, M5E)" },
+ /* 0x02 is Unallocated */
+ { 0x01, "Single Mode (SM)" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 5-3.
+ */
+#define SFF_8472_COMP_SPEED_MASK 0xfd
+static sff_pair_t sff_8472_comp_speed[] = {
+ { 0x80, "1200 MBytes/sec" },
+ { 0x40, "800 MBytes/sec" },
+ { 0x20, "1600 MBytes/sec" },
+ { 0x10, "400 MBytes/sec" },
+ { 0x08, "3200 MBytes/sec" },
+ { 0x04, "200 MBytes/sec" },
+ /* 0x02 is Unallocated */
+ { 0x01, "100 MBytes/sec" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 8-1.
+ * Note, only byte 60 is allocated at this time.
+ */
+#define SFF_8472_PCABLE_COMP_MASK 0x3f
+static sff_pair_t sff_8472_pcable_comp[] = {
+ { 0x20, "Reserved for SFF-8461" },
+ { 0x10, "Reserved for SFF-8461" },
+ { 0x08, "Reserved for SFF-8461" },
+ { 0x04, "Reserved for SFF-8461" },
+ { 0x02, "Compliant to FC-PI-4 Appendix H" },
+ { 0x01, "Compliant to SFF-8431 Appendix E" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 8-2.
+ * Note, only byte 60 is allocated at this time.
+ */
+#define SFF_8472_ACABLE_COMP_MASK 0xf
+static sff_pair_t sff_8472_acable_comp[] = {
+ { 0x08, "Compliant to FC-PI-4 Limiting" },
+ { 0x04, "Compliant to SFF-8431 Limiting" },
+ { 0x02, "Compliant to FC-PI-4 Appendix H" },
+ { 0x01, "Compliant to SFF-8431 Appendix" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 8-3.
+ * Note that we combined byte 64 and 65. Byte 64 is the upper bit.
+ */
+#define SFF_8472_OPTION_MASK 0x3ffe
+static sff_pair_t sff_8472_options[] = {
+ { 0x2000, "Power Level 3 Requirement"},
+ { 0x1000, "Paging Implemented"},
+ { 0x0800, "Retimer or CDR implemented"},
+ { 0x0400, "Cooled Transceiver Implemented"},
+ { 0x0200, "Power Level 2 Requirement"},
+ { 0x0100, "Linear Receiver Output Implemented"},
+ { 0x0080, "Receiver decision threshold implemented"},
+ { 0x0040, "Tunable transmitter"},
+ { 0x0020, "RATE_SELECT implemented"},
+ { 0x0010, "TX_DISABLE implemented"},
+ { 0x0008, "TX_FAULT implemented"},
+ { 0x0004, "Rx_LOS inverted"},
+ { 0x0002, "Rx_LOS implemented"},
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 8-6.
+ */
+#define SFF_8472_EXTOPT_MASK 0xfe
+static sff_pair_t sff_8472_extopts[] = {
+ { 0x80, "Alarm/Warning flags implemented" },
+ { 0x40, "Soft TX_DISABLE implemented" },
+ { 0x20, "Soft TX_FAULT implemented" },
+ { 0x10, "Soft RX_LOS implemented" },
+ { 0x08, "Soft RATE_SELECT implemented" },
+ { 0x04, "Application Select implemented" },
+ { 0x02, "Soft Rate Select Control Implemented" },
+ { 0x01, "" },
+};
+
+/*
+ * This is derived from SFF 8472 r12.2 Table 8-8.
+ */
+#define SFF_8472_8472_COMP_NENTRIES 9
+static const char *sff_8472_8472_comp[] = {
+ "Not compliant",
+ "Rev 9.3",
+ "Rev 9.5",
+ "Rev 10.2",
+ "Rev 10.4",
+ "Rev 11.0",
+ "Rev 11.3",
+ "Rev 11.4",
+ "Rev 12.0"
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-17.
+ */
+#define SFF_8636_COMP_10GETH_MASK 0x7f
+static sff_pair_t sff_8636_comp_10geth[] = {
+ { 0x40, "10GBASE-LRM" },
+ { 0x20, "10GBASE-LR" },
+ { 0x10, "10GBASE-SR" },
+ { 0x08, "40GBASE-CR4" },
+ { 0x04, "40GBASE-SR4" },
+ { 0x02, "40GBASE-LR4" },
+ { 0x01, "40G Active Cable (XLPPI)" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-17.
+ */
+#define SFF_8636_COMP_SONET_MASK 0x07
+static sff_pair_t sff_8636_comp_sonet[] = {
+ { 0x04, "OC 48, long reach" },
+ { 0x02, "OC 48, intermediate reach" },
+ { 0x01, "OC 48 short reach" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-17.
+ */
+#define SFF_8636_COMP_SAS_MASK 0xf0
+static sff_pair_t sff_8636_comp_sas[] = {
+ { 0x80, "SAS 24.0 Gb/s" },
+ { 0x40, "SAS 12.0 Gb/s" },
+ { 0x20, "SAS 6.0 Gb/s" },
+ { 0x10, "SAS 3.0 Gb/s" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-17.
+ */
+#define SFF_8636_COMP_ETH_MASK 0x0f
+static sff_pair_t sff_8636_comp_eth[] = {
+ { 0x08, "1000BASE-T" },
+ { 0x04, "1000BASE-CX" },
+ { 0x02, "1000BASE-LX" },
+ { 0x01, "1000BASE-SX" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-17.
+ */
+#define SFF_8636_COMP_FCLEN_MASK 0xf8
+static sff_pair_t sff_8636_comp_fclen[] = {
+ { 0x80, "very long distance (V)" },
+ { 0x40, "short distance (S)" },
+ { 0x20, "intermeddiate distance (I)" },
+ { 0x10, "long distance (L)" },
+ { 0x08, "medium distance (M)" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-17.
+ */
+#define SFF_8636_COMP_TECH_MASK 0xf003
+static sff_pair_t sff_8636_comp_tech[] = {
+ { 0x2, "Longwave laser (LC)" },
+ { 0x1, "Electrical inter-enclosure (EL)" },
+ { 0x8000, "Electrical intra-enclosure (EL)" },
+ { 0x4000, "Shortwave laser w/o OFC (SN)" },
+ { 0x2000, "Shortwave laser with OFC (SL)" },
+ { 0x1000, "Longwave laser (LL)" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-17.
+ */
+#define SFF_8636_COMP_MEDIA_MASK 0xff
+static sff_pair_t sff_8636_comp_media[] = {
+ { 0x80, "Twin Axial Pair (TW)" },
+ { 0x40, "Twisted Pair (TP)" },
+ { 0x20, "Miniature Coax (MI)" },
+ { 0x10, "Video Coax (TV)" },
+ { 0x08, "Multimode, 62.5um (M6)" },
+ { 0x04, "Multimode, 50m (M5)" },
+ { 0x02, "Multimode, 50um (OM3)" },
+ { 0x01, "Single Mode (SM)" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-17.
+ */
+#define SFF_8636_COMP_SPEED_MASK 0xfd
+static sff_pair_t sff_8636_comp_speed[] = {
+ { 0x80, "1200 MBytes/sec" },
+ { 0x40, "800 MBytes/sec" },
+ { 0x20, "1600 MBytes/sec" },
+ { 0x10, "400 MBytes/sec" },
+ { 0x08, "3200 MBytes/sec" },
+ { 0x04, "200 MBytes/sec" },
+ { 0x01, "100 MBytes/sec" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-20.
+ */
+static const char *sff_8636_trans_tech[] = {
+ "850 nm VCSEL",
+ "1310 nm VCSEL",
+ "1550 nm VCSEL",
+ "1310 nm FP",
+ "1310 nm DFB",
+ "1550 nm DFB",
+ "1310 nm EML",
+ "1550 nm EML",
+ "Other / Undefined",
+ "1490 nm DFB",
+ "Copper cable unequalized",
+ "Copper cable passive equalized",
+ "Copper cable, near and far end limiting active equalizers",
+ "Copper cable, far end limiting active equalizers",
+ "Copper cable, near end limiting active equalizers",
+ "Copper cable, linear active equalizers"
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-21.
+ */
+#define SFF_8636_EXTMOD_CODES 0x1f
+static sff_pair_t sff_8636_extmod_codes[] = {
+ { 0x10, "EDR" },
+ { 0x08, "FDR" },
+ { 0x04, "QDR" },
+ { 0x02, "DDR" },
+ { 0x01, "SDR" },
+ { 0x00, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-22. This combines bytes 193-195.
+ * We treat byte 193 as the most significant.
+ */
+#define SFF_8636_OPTION_MASK 0x0ffffe
+static sff_pair_t sff_8636_options[] = {
+ { 0x080000, "TX Input Equalization Auto Adaptive Capable" },
+ { 0x040000, "TX Input Equalization Fixed Programmable" },
+ { 0x020000, "RX Output Emphasis Fixed Programmable Settings" },
+ { 0x010000, "RX Output Amplitude Fixed Programmable Settings" },
+ { 0x008000, "TX CDR On/Off Control implemented" },
+ { 0x004000, "RX CDR On/Off Control implemented" },
+ { 0x002000, "Tx CDR Loss of Lock Flag implemented" },
+ { 0x001000, "Rx CDR Loss of Lock Flag implemented" },
+ { 0x000800, "Rx Squelch Disable implemented" },
+ { 0x000400, "Rx Output Disable capable" },
+ { 0x000200, "Tx Squelch Disable implemented" },
+ { 0x000100, "Tx Squelch implemented" },
+ { 0x000080, "Memory page 02h provided" },
+ { 0x000040, "Memory page 01h provided" },
+ { 0x000020, "Rate Select implemented" },
+ { 0x000010, "Tx_DISABLE implemented" },
+ { 0x000008, "Tx_FAULT implemented" },
+ { 0x000004, "Tx Squelch for Pave" },
+ { 0x000002, "Tx Loss of Signal implemented" },
+ { 0x0, NULL }
+};
+
+/*
+ * This is derived from SFF 8636 r2.7 Table 6-25.
+ */
+#define SFF_8636_ENHANCED_OPTIONS_MASK 0x1c
+static sff_pair_t sff_8636_eopt[] = {
+ { 0x10, "Initialization Complete Flag Implemented" },
+ { 0x08, "Extended Rate Selection Supported" },
+ { 0x04, "Application Select Table Supported" },
+ { 0x0, NULL }
+};
+
+static const char *
+sff_pair_find(uint_t val, sff_pair_t *pairs)
+{
+ while (pairs->sp_name != NULL) {
+ if (val == pairs->sp_val)
+ return (pairs->sp_name);
+ pairs++;
+ }
+
+ return (NULL);
+}
+
+static int
+sff_parse_id(uint8_t id, nvlist_t *nvl)
+{
+ const char *val;
+
+ if (id >= SFF_8024_VENDOR) {
+ val = "Vendor Specific";
+ } else if (id >= SFF_8024_NIDS) {
+ val = "Reserved";
+ } else {
+ val = sff_8024_id_strs[id];
+ }
+
+ return (nvlist_add_string(nvl, LIBSFF_KEY_IDENTIFIER, val));
+}
+
+static int
+sff_add_unit_string(uint64_t val, uint64_t factor, const char *unit,
+ nvlist_t *nvl, const char *key)
+{
+ char str[SFP_STRBUF];
+
+ val *= factor;
+ (void) snprintf(str, sizeof (str), "%" PRIu64 " %s", val, unit);
+ return (nvlist_add_string(nvl, key, str));
+}
+
+static int
+sff_parse_connector(uint8_t con, nvlist_t *nvl)
+{
+ const char *val;
+
+ if (con >= 0x80) {
+ val = "Vendor Specific";
+ } else {
+ if ((val = sff_pair_find(con, sff_8024_connectors)) == NULL)
+ val = "Reserved";
+ }
+
+ return (nvlist_add_string(nvl, LIBSFF_KEY_CONNECTOR, val));
+}
+
+/*
+ * Many of the values in the specifications are bitfields of which one or more
+ * bits may be set. We represent that as an array of strings. One entry will be
+ * added for each set bit that's found in pairs.
+ */
+static int
+sff_gather_bitfield(uint32_t value, const char *name, sff_pair_t *pairs,
+ nvlist_t *nvl)
+{
+ uint32_t i;
+ const char *vals[32];
+ uint_t count;
+
+ count = 0;
+ for (i = 0; i < 32; i++) {
+ uint32_t bit;
+ const char *str;
+
+ bit = 1 << i;
+ if ((bit & value) == 0)
+ continue;
+
+ str = sff_pair_find(bit, pairs);
+ if (str != NULL) {
+ vals[count++] = str;
+ }
+ }
+
+ if (count == 0)
+ return (0);
+
+ /*
+ * The nvlist routines don't touch the array, so we end up lying about
+ * the type of data so that we can avoid a rash of additional
+ * allocations and strdups.
+ */
+ return (nvlist_add_string_array(nvl, name, (char **)vals, count));
+}
+
+static int
+sff_parse_compliance(const uint8_t *buf, nvlist_t *nvl)
+{
+ int ret;
+ uint16_t v;
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_10GE] &
+ SFF_8472_COMP_10GETH_MASK, LIBSFF_KEY_COMPLIANCE_10GBE,
+ sff_8472_comp_10geth, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_IB] &
+ SFF_8472_COMP_IB_MASK, LIBSFF_KEY_COMPLIANCE_IB,
+ sff_8472_comp_ib, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_ESCON] &
+ SFF_8472_COMP_ESCON_MASK, LIBSFF_KEY_COMPLIANCE_ESCON,
+ sff_8472_comp_escon, nvl)) != 0)
+ return (ret);
+
+ v = buf[SFF_8472_COMPLIANCE_SONET_LOW] |
+ (buf[SFF_8472_COMPLIANCE_SONET_HIGH] << 8);
+ if ((ret = sff_gather_bitfield(v & SFF_8472_COMP_SOCON_MASK,
+ LIBSFF_KEY_COMPLIANCE_SONET, sff_8472_comp_sonet, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_ETHERNET] &
+ SFF_8472_COMP_ETH_MASK, LIBSFF_KEY_COMPLIANCE_GBE,
+ sff_8472_comp_eth, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_FCLEN] &
+ SFF_8472_COMP_FCLEN_MASK, LIBSFF_KEY_COMPLIANCE_FC_LEN,
+ sff_8472_comp_fclen, nvl)) != 0)
+ return (ret);
+
+ v = buf[SFF_8472_COMPLIANCE_FC_LOW] |
+ (buf[SFF_8472_COMPLIANCE_FC_HIGH] << 8);
+ if ((ret = sff_gather_bitfield(v & SFF_8472_COMP_TECH_MASK,
+ LIBSFF_KEY_COMPLIANCE_FC_TECH, sff_8472_comp_tech, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_SFP] &
+ SFF_8472_COMP_CABLE_MASK, LIBSFF_KEY_COMPLIANCE_SFP,
+ sff_8472_comp_cable, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_FC_MEDIA] &
+ SFF_8472_COMP_MEDIA_MASK, LIBSFF_KEY_COMPLIANCE_FC_MEDIA,
+ sff_8472_comp_media, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_FC_SPEED] &
+ SFF_8472_COMP_SPEED_MASK, LIBSFF_KEY_COMPLIANCE_FC_SPEED,
+ sff_8472_comp_speed, nvl)) != 0)
+ return (ret);
+
+ return (0);
+}
+
+static int
+sff_parse_encoding(uint8_t val, nvlist_t *nvl, boolean_t sfp)
+{
+ const char *str;
+ if (val >= SFF_8024_NENCS) {
+ str = "Reserved";
+ } else if (sfp) {
+ str = sff_8024_enc_sfp[val];
+ } else {
+ str = sff_8024_enc_qsfp[val];
+ }
+
+ return (nvlist_add_string(nvl, LIBSFF_KEY_ENCODING, str));
+}
+
+static int
+sff_parse_br(const uint8_t *buf, nvlist_t *nvl)
+{
+ if (buf[SFF_8472_BR_NOMINAL] == 0xff) {
+ int ret;
+ if ((ret = sff_add_unit_string(buf[SFF_8472_BR_MAX],
+ SFF_8472_BR_MAX_FACTOR, "MBd", nvl,
+ LIBSFF_KEY_BR_MAX)) != 0)
+ return (ret);
+ return (sff_add_unit_string(buf[SFF_8472_BR_MIN],
+ SFF_8472_BR_MIN_FACTOR, "MBd", nvl, LIBSFF_KEY_BR_MIN));
+ } else {
+ return (sff_add_unit_string(buf[SFF_8472_BR_NOMINAL],
+ SFF_8472_BR_NOMINAL_FACTOR, "MBd", nvl,
+ LIBSFF_KEY_BR_NOMINAL));
+ }
+}
+
+static int
+sff_parse_lengths(const uint8_t *buf, nvlist_t *nvl)
+{
+ int ret;
+
+ if (buf[SFF_8472_LENGTH_SMF_KM] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_SMF_KM],
+ SFF_8472_LENGTH_SMF_KM_FACTOR, "km", nvl,
+ LIBSFF_KEY_LENGTH_SMF_KM)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8472_LENGTH_SMF] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_SMF],
+ SFF_8472_LENGTH_SMF_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_SMF)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8472_LENGTH_50UM] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_50UM],
+ SFF_8472_LENGTH_50UM_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_OM2)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8472_LENGTH_62UM] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_62UM],
+ SFF_8472_LENGTH_62UM_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_OM1)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8472_LENGTH_COPPER] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_COPPER],
+ SFF_8472_LENGTH_COPPER_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_COPPER)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8472_LENGTH_OM3] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_OM3],
+ SFF_8472_LENGTH_OM3_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_OM3)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+/*
+ * Strings in the SFF specification are written into fixed sized buffers. The
+ * strings are padded to the right with spaces (ASCII 0x20) and there is no NUL
+ * character like in a standard C string. While the string is padded with
+ * spaces, spaces may appear in the middle of the string and should not be
+ * confused as padding.
+ */
+static int
+sff_parse_string(const uint8_t *buf, uint_t start, uint_t len,
+ const char *field, nvlist_t *nvl)
+{
+ uint_t i;
+ char strbuf[SFP_STRBUF];
+
+ assert(len < sizeof (strbuf));
+ strbuf[0] = '\0';
+ while (len > 0) {
+ if (buf[start + len - 1] != ' ')
+ break;
+ len--;
+ }
+ if (len == 0)
+ return (0);
+
+ /*
+ * This is supposed to be 7-bit printable ASCII. If we find any
+ * characters that aren't, don't include this string.
+ */
+ for (i = 0; i < len; i++) {
+ if (isascii(buf[start + i]) == 0 ||
+ isprint(buf[start + i]) == 0) {
+ return (0);
+ }
+ }
+ bcopy(&buf[start], strbuf, len);
+ strbuf[len] = '\0';
+
+ return (nvlist_add_string(nvl, field, strbuf));
+}
+
+static int
+sff_parse_optical(const uint8_t *buf, nvlist_t *nvl)
+{
+ /*
+ * The value in byte 8 determines whether we interpret this as
+ * describing aspects of a copper device or if it describes the
+ * wavelength.
+ */
+ if (buf[SFF_8472_COMPLIANCE_SFP] & SFF_8472_COMP_CABLE_PASSIVE) {
+ return (sff_gather_bitfield(buf[SFF_8472_PASSIVE_SPEC] &
+ SFF_8472_PCABLE_COMP_MASK, LIBSFF_KEY_COMPLIANCE_PASSIVE,
+ sff_8472_pcable_comp, nvl));
+ } else if (buf[SFF_8472_COMPLIANCE_SFP] & SFF_8472_COMP_CABLE_ACTIVE) {
+ return (sff_gather_bitfield(buf[SFF_8472_ACTIVE_SPEC] &
+ SFF_8472_ACABLE_COMP_MASK, LIBSFF_KEY_COMPLIANCE_ACTIVE,
+ sff_8472_acable_comp, nvl));
+
+ } else {
+ uint16_t val = (buf[SFF_8472_WAVELENGTH_HI] << 8) |
+ buf[SFF_8472_WAVELENGTH_LOW];
+
+ return (sff_add_unit_string(val, SFF_8472_WAVELENGTH_FACTOR,
+ "nm", nvl, LIBSFF_KEY_WAVELENGTH));
+ }
+}
+
+static int
+sff_parse_options(const uint8_t *buf, nvlist_t *nvl)
+{
+ uint16_t val;
+
+ val = (buf[SFF_8472_OPTIONS_HI] << 8) | buf[SFF_8472_OPTIONS_LOW];
+ return (sff_gather_bitfield(val & SFF_8472_OPTION_MASK,
+ LIBSFF_KEY_OPTIONS, sff_8472_options, nvl));
+}
+
+static int
+sff_parse_8472_comp(uint8_t val, nvlist_t *nvl)
+{
+ const char *str;
+
+ if (val >= SFF_8472_8472_COMP_NENTRIES) {
+ str = "Unallocated";
+ } else {
+ str = sff_8472_8472_comp[val];
+ }
+
+ return (nvlist_add_string(nvl, LIBSFF_KEY_COMPLIANCE_8472, str));
+}
+
+/*
+ * Parse an SFP that is either based on INF 8074 or SFF 8472. These are GBIC,
+ * SFP, SFP+, and SFP28 based devices.
+ *
+ * The SFP parsing into an nvlist_t is incomplete. At the moment we're not
+ * parsing the following pieces from SFF 8472 page 0xa0:
+ *
+ * o Rate Selection Logic
+ * o Diagnostic Monitoring Type
+ */
+static int
+sff_parse_sfp(const uint8_t *buf, nvlist_t *nvl)
+{
+ int ret;
+
+ if ((ret = sff_parse_id(buf[SFF_8472_IDENTIFIER], nvl)) != 0)
+ return (ret);
+
+ /*
+ * The extended identifier is derived from SFF 8472, Table 5-2. It
+ * generally is just the value 4. The other values are not well defined.
+ */
+ if ((ret = nvlist_add_uint8(nvl, LIBSFF_KEY_8472_EXT_IDENTIFIER,
+ buf[SFF_8472_EXT_IDENTIFER])) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_connector(buf[SFF_8472_CONNECTOR], nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_compliance(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_encoding(buf[SFF_8472_ENCODING], nvl,
+ B_TRUE)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_br(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_lengths(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8472_VENDOR, SFF_8472_VENDOR_LEN,
+ LIBSFF_KEY_VENDOR, nvl)) != 0)
+ return (ret);
+
+ if ((ret = nvlist_add_byte_array(nvl, LIBSFF_KEY_OUI,
+ (uchar_t *)&buf[SFF_8472_OUI], SFF_8472_OUI_LEN)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8472_VENDOR_PN,
+ SFF_8472_VENDOR_PN_LEN, LIBSFF_KEY_PART, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8472_VENDOR_REV,
+ SFF_8472_VENDOR_REV_LEN, LIBSFF_KEY_REVISION, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_optical(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_options(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8472_VENDOR_SN,
+ SFF_8472_VENDOR_SN_LEN, LIBSFF_KEY_SERIAL, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8472_DATE_CODE,
+ SFF_8472_DATE_CODE_LEN, LIBSFF_KEY_DATECODE, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8472_ENHANCED_OPTIONS] &
+ SFF_8472_EXTOPT_MASK, LIBSFF_KEY_EXTENDED_OPTIONS,
+ sff_8472_extopts, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_8472_comp(buf[SFF_8472_SFF_8472_COMPLIANCE],
+ nvl)) != 0)
+ return (ret);
+
+ return (0);
+}
+
+static int
+sff_qsfp_parse_compliance(const uint8_t *buf, nvlist_t *nvl)
+{
+ int ret;
+ uint16_t fc_val;
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_10GBEP] &
+ SFF_8636_COMP_10GETH_MASK, LIBSFF_KEY_COMPLIANCE_10GBE,
+ sff_8636_comp_10geth, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_SONET] &
+ SFF_8636_COMP_SONET_MASK, LIBSFF_KEY_COMPLIANCE_SONET,
+ sff_8636_comp_sonet, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_SAS] &
+ SFF_8636_COMP_SAS_MASK, LIBSFF_KEY_COMPLIANCE_SAS,
+ sff_8636_comp_sas, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_ETHERNET] &
+ SFF_8636_COMP_ETH_MASK, LIBSFF_KEY_COMPLIANCE_GBE,
+ sff_8636_comp_eth, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_FCLEN] &
+ SFF_8636_COMP_FCLEN_MASK, LIBSFF_KEY_COMPLIANCE_FC_LEN,
+ sff_8636_comp_fclen, nvl)) != 0)
+ return (ret);
+
+ fc_val = buf[SFF_8636_COMPLIANCE_FC_LOW] |
+ (buf[SFF_8636_COMPLIANCE_FC_HIGH] << 8);
+ if ((ret = sff_gather_bitfield(fc_val & SFF_8636_COMP_TECH_MASK,
+ LIBSFF_KEY_COMPLIANCE_FC_TECH, sff_8636_comp_tech, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_FC_MEDIA] &
+ SFF_8636_COMP_MEDIA_MASK, LIBSFF_KEY_COMPLIANCE_FC_MEDIA,
+ sff_8636_comp_media, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_FC_SPEED] &
+ SFF_8636_COMP_SPEED_MASK, LIBSFF_KEY_COMPLIANCE_FC_SPEED,
+ sff_8636_comp_speed, nvl)) != 0)
+ return (ret);
+
+ return (0);
+}
+
+static int
+sff_qsfp_parse_br(const uint8_t *buf, nvlist_t *nvl)
+{
+ if (buf[SFF_8636_BR_NOMINAL] == 0xff) {
+ return (sff_add_unit_string(buf[SFF_8636_BR_NOMINAL_EXT],
+ SFF_8636_BR_NOMINAL_EXT_FACTOR, "Mbps", nvl,
+ LIBSFF_KEY_BR_NOMINAL));
+ } else {
+ return (sff_add_unit_string(buf[SFF_8636_BR_NOMINAL],
+ SFF_8636_BR_NOMINAL_FACTOR, "Mbps", nvl,
+ LIBSFF_KEY_BR_NOMINAL));
+ }
+}
+
+static int
+sff_qsfp_parse_lengths(const uint8_t *buf, nvlist_t *nvl)
+{
+ int ret;
+
+ if (buf[SFF_8636_LENGTH_SMF] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_SMF],
+ SFF_8636_LENGTH_SMF_FACTOR, "km", nvl,
+ LIBSFF_KEY_LENGTH_SMF_KM)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8636_LENGTH_OM3] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_OM3],
+ SFF_8636_LENGTH_OM3_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_OM3)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8636_LENGTH_OM2] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_OM2],
+ SFF_8636_LENGTH_OM2_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_OM2)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8636_LENGTH_OM1] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_OM1],
+ SFF_8636_LENGTH_OM1_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_OM1)) != 0)
+ return (ret);
+ }
+
+ if (buf[SFF_8636_LENGTH_COPPER] != 0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_COPPER],
+ SFF_8636_LENGTH_COPPER_FACTOR, "m", nvl,
+ LIBSFF_KEY_LENGTH_COPPER)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+static int
+sff_qsfp_parse_tech(uint8_t val, nvlist_t *nvl)
+{
+ const char *strs[5];
+
+ strs[0] = sff_8636_trans_tech[(val & 0xf0) >> 4];
+ if (val & 0x08) {
+ strs[1] = "Active Wavelength Control";
+ } else {
+ strs[1] = "No Wavelength Control";
+ }
+
+ if (val & 0x04) {
+ strs[2] = "Cooled Transmitter";
+ } else {
+ strs[2] = "Uncooled Transmitter";
+ }
+
+ if (val & 0x02) {
+ strs[3] = "APD Detector";
+ } else {
+ strs[3] = "Pin Detector";
+ }
+
+ if (val & 0x01) {
+ strs[4] = "Transmitter Tunable";
+ } else {
+ strs[4] = "Transmitter Not Tunable";
+ }
+
+ /*
+ * The nvlist routines don't touch the array, so we end up lying about
+ * the type of data so that we can avoid a rash of additional
+ * allocations and strdups.
+ */
+ return (nvlist_add_string_array(nvl, LIBSFF_KEY_TRAN_TECH,
+ (char **)strs, 5));
+}
+
+static int
+sff_qsfp_parse_copperwave(const uint8_t *buf, nvlist_t *nvl)
+{
+ int ret;
+
+ /*
+ * The values that we get depend on whether or not we are a copper
+ * device or not. We can determine this based on the identification
+ * information in the device technology field.
+ */
+ if ((buf[SFF_8636_DEVICE_TECH] & 0xf0) >= 0xa0) {
+ if ((ret = sff_add_unit_string(buf[SFF_8636_ATTENUATE_2G], 1,
+ "dB", nvl, LIBSFF_KEY_ATTENUATE_2G)) != 0)
+ return (ret);
+ if ((ret = sff_add_unit_string(buf[SFF_8636_ATTENUATE_5G], 1,
+ "dB", nvl, LIBSFF_KEY_ATTENUATE_5G)) != 0)
+ return (ret);
+ if ((ret = sff_add_unit_string(buf[SFF_8636_ATTENUATE_7G], 1,
+ "dB", nvl, LIBSFF_KEY_ATTENUATE_7G)) != 0)
+ return (ret);
+ if ((ret = sff_add_unit_string(buf[SFF_8636_ATTENUATE_12G], 1,
+ "dB", nvl, LIBSFF_KEY_ATTENUATE_12G)) != 0)
+ return (ret);
+ } else {
+ uint16_t val;
+ double d;
+ char strbuf[SFP_STRBUF];
+
+ /*
+ * Because we need to divide the units here into doubles, we
+ * can't use the standard unit routine.
+ */
+ val = (buf[SFF_8636_WAVELENGTH_NOMINAL_HI] << 8) |
+ buf[SFF_8636_WAVELENGTH_NOMINAL_LOW];
+ if (val != 0) {
+ d = val / 20.0;
+ (void) snprintf(strbuf, sizeof (strbuf), "%.3lf nm", d);
+ if ((ret = nvlist_add_string(nvl, LIBSFF_KEY_WAVELENGTH,
+ strbuf)) != 0)
+ return (ret);
+ }
+
+ val = (buf[SFF_8636_WAVELENGTH_TOLERANCE_HI] << 8) |
+ buf[SFF_8636_WAVELENGTH_TOLERANCE_LOW];
+ if (val != 0) {
+ d = val / 20.0;
+ (void) snprintf(strbuf, sizeof (strbuf), "%.3lf nm", d);
+ if ((ret = nvlist_add_string(nvl,
+ LIBSFF_KEY_WAVE_TOLERANCE, strbuf)) != 0)
+ return (ret);
+ }
+ }
+
+ return (0);
+}
+
+static int
+sff_qsfp_parse_casetemp(uint8_t val, nvlist_t *nvl)
+{
+ /*
+ * The default temperature per SFF 8636 r2.7 6.3.21 'Maximum Case
+ * Temperature' is 70 C. If the value is zero, we're supposed to assume
+ * it's the default.
+ */
+ if (val == 0)
+ val = 70;
+
+ return (sff_add_unit_string(val, 1, "C", nvl,
+ LIBSFF_KEY_MAX_CASE_TEMP));
+}
+
+static int
+sff_qsfp_parse_extcomp(uint8_t val, nvlist_t *nvl)
+{
+ const char *str;
+
+ if (val >= SFF_8024_EXT_SPEC_NENTRIES) {
+ str = "Reserved";
+ } else {
+ str = sff_8024_ext_spec[val];
+ }
+
+ return (nvlist_add_string(nvl, LIBSFF_KEY_EXT_SPEC, str));
+}
+
+static int
+sff_qsfp_parse_options(const uint8_t *buf, nvlist_t *nvl)
+{
+ uint_t val;
+
+ val = (buf[SFF_8636_OPTIONS_HI] << 16) |
+ (buf[SFF_8636_OPTIONS_MID] << 8) | buf[SFF_8636_OPTIONS_LOW];
+
+ return (sff_gather_bitfield(val & SFF_8636_OPTION_MASK,
+ LIBSFF_KEY_OPTIONS, sff_8636_options, nvl));
+}
+
+static int
+sff_qsfp_parse_diag(uint8_t val, nvlist_t *nvl)
+{
+ const char *buf[2];
+ uint_t count = 1;
+
+ if (val & 0x08) {
+ buf[0] = "Received power measurements: Average Power";
+ } else {
+ buf[0] = "Received power measurements: OMA";
+ }
+
+ if (val & 0x04) {
+ count++;
+ buf[1] = "Transmitter power measurement";
+ }
+
+ /*
+ * The nvlist routines don't touch the array, so we end up lying about
+ * the type of data so that we can avoid a rash of additional
+ * allocations and strdups.
+ */
+ return (nvlist_add_string_array(nvl, LIBSFF_KEY_DIAG_MONITOR,
+ (char **)buf, count));
+}
+
+/*
+ * Parse a QSFP family device that is based on SFF-8436 / SFF-8636. Note that we
+ * ignore the lower half of page 0xa0 at this time and instead focus on the
+ * upper half of page 0xa0 which has identification information.
+ *
+ * For the moment we're not parsing the following fields:
+ *
+ * o Extended Identifier (byte 129)
+ * o Extended Rate Select Compliance (byte 141)
+ */
+static int
+sff_parse_qsfp(const uint8_t *buf, nvlist_t *nvl)
+{
+ int ret;
+
+ if ((ret = sff_parse_id(buf[SFF_8636_IDENTIFIER], nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_connector(buf[SFF_8636_CONNECTOR], nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_compliance(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_encoding(buf[SFF_8636_ENCODING], nvl,
+ B_FALSE)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_br(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_lengths(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_tech(buf[SFF_8636_DEVICE_TECH], nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8636_VENDOR, SFF_8636_VENDOR_LEN,
+ LIBSFF_KEY_VENDOR, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_EXTENDED_MODULE] &
+ SFF_8636_EXTMOD_CODES, LIBSFF_KEY_EXT_MOD_CODES,
+ sff_8636_extmod_codes, nvl)) != 0)
+ return (ret);
+
+ if ((ret = nvlist_add_byte_array(nvl, LIBSFF_KEY_OUI,
+ (uchar_t *)&buf[SFF_8636_OUI], SFF_8636_OUI_LEN)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8636_VENDOR_PN,
+ SFF_8636_VENDOR_PN_LEN, LIBSFF_KEY_PART, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8636_VENDOR_REV,
+ SFF_8636_VENDOR_REV_LEN, LIBSFF_KEY_REVISION, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_copperwave(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_casetemp(buf[SFF_8636_MAX_CASE_TEMP],
+ nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_extcomp(buf[SFF_8636_LINK_CODES], nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_options(buf, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8636_VENDOR_SN,
+ SFF_8636_VENDOR_SN_LEN, LIBSFF_KEY_SERIAL, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_parse_string(buf, SFF_8636_DATE_CODE,
+ SFF_8636_DATE_CODE_LEN, LIBSFF_KEY_DATECODE, nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_qsfp_parse_diag(buf[SFF_8636_DIAG_MONITORING],
+ nvl)) != 0)
+ return (ret);
+
+ if ((ret = sff_gather_bitfield(buf[SFF_8636_ENHANCED_OPTIONS] &
+ SFF_8636_ENHANCED_OPTIONS_MASK, LIBSFF_KEY_ENHANCED_OPTIONS,
+ sff_8636_eopt, nvl)) != 0)
+ return (ret);
+
+ return (0);
+}
+
+int
+libsff_parse(const uint8_t *buf, size_t len, uint_t page, nvlist_t **nvpp)
+{
+ int ret;
+ nvlist_t *nvp = NULL;
+ uint8_t ubuf[256];
+
+ /*
+ * At the moment, we only support page a0.
+ */
+ if (page != 0xa0 || buf == NULL || len == 0 || nvpp == NULL)
+ return (EINVAL);
+
+ *nvpp = NULL;
+
+ /*
+ * Make sure that the library has been given valid data to parse.
+ */
+ if (uucopy(buf, ubuf, MIN(sizeof (ubuf), len)) != 0)
+ return (errno);
+
+ if ((ret = nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0)) != 0)
+ return (ret);
+
+ switch (buf[0]) {
+ case SFF_8024_ID_QSFP:
+ case SFF_8024_ID_QSFP_PLUS:
+ case SFF_8024_ID_QSFP28:
+ /*
+ * For QSFP based products, identification information is spread
+ * across both the top and bottom half of page 0xa0.
+ */
+ if (len < SFP_MIN_LEN_8636) {
+ ret = EINVAL;
+ break;
+ }
+ ret = sff_parse_qsfp(ubuf, nvp);
+ break;
+ default:
+ if (len < SFP_MIN_LEN_8472) {
+ ret = EINVAL;
+ break;
+ }
+ ret = sff_parse_sfp(ubuf, nvp);
+ break;
+ }
+
+ if (ret != 0) {
+ nvlist_free(nvp);
+ } else {
+ *nvpp = nvp;
+ }
+ return (ret);
+}
diff --git a/usr/src/lib/libsff/common/libsff.h b/usr/src/lib/libsff/common/libsff.h
new file mode 100644
index 0000000000..04812e478f
--- /dev/null
+++ b/usr/src/lib/libsff/common/libsff.h
@@ -0,0 +1,101 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+#ifndef _LIBSFF_H
+#define _LIBSFF_H
+
+/*
+ * Parse SFF structures and values and return an nvlist_t of keys. This library
+ * is private and subject to change and break compat at any time.
+ */
+
+#include <libnvpair.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int libsff_parse(const uint8_t *, size_t, uint_t, nvlist_t **);
+
+/*
+ * Supported Keys in the resulting nvlist. Not every key will be present in
+ * every SFF compatible device.
+ */
+#define LIBSFF_KEY_IDENTIFIER "Identifier" /* String */
+#define LIBSFF_KEY_CONNECTOR "Connector" /* String */
+#define LIBSFF_KEY_ENCODING "Encoding" /* String */
+#define LIBSFF_KEY_VENDOR "Vendor" /* String */
+#define LIBSFF_KEY_OUI "OUI" /* Byte Array [3] */
+#define LIBSFF_KEY_PART "Part Number" /* String */
+#define LIBSFF_KEY_REVISION "Revision" /* String */
+#define LIBSFF_KEY_SERIAL "Serial Number" /* String */
+#define LIBSFF_KEY_DATECODE "Date Code" /* String */
+#define LIBSFF_KEY_BR_NOMINAL "BR, nominal" /* String */
+#define LIBSFF_KEY_BR_MAX "BR, maximum" /* String */
+#define LIBSFF_KEY_BR_MIN "BR, minimum" /* String */
+#define LIBSFF_KEY_LENGTH_SMF_KM "Length SMF (km)" /* String */
+#define LIBSFF_KEY_LENGTH_SMF "Length SMF (m)" /* String */
+#define LIBSFF_KEY_LENGTH_OM2 "Length 50um OM2" /* String */
+#define LIBSFF_KEY_LENGTH_OM1 "Length 62.5um OM1" /* String */
+#define LIBSFF_KEY_LENGTH_COPPER "Length Copper" /* String */
+#define LIBSFF_KEY_LENGTH_OM3 "Length OM3" /* String */
+#define LIBSFF_KEY_WAVELENGTH "Laser Wavelength" /* String */
+#define LIBSFF_KEY_WAVE_TOLERANCE "Wavelength Tolerance" /* String */
+#define LIBSFF_KEY_OPTIONS "Options" /* String Array */
+#define LIBSFF_KEY_COMPLIANCE_8472 "8472 Compliance" /* String */
+#define LIBSFF_KEY_EXTENDED_OPTIONS "Extended Options" /* String Array */
+#define LIBSFF_KEY_ENHANCED_OPTIONS "Enhanced Options" /* String Array */
+#define LIBSFF_KEY_EXT_MOD_CODES "Extended Module Codes" /* String Array */
+#define LIBSFF_KEY_DIAG_MONITOR "Diagnostic Monitoring" /* String */
+#define LIBSFF_KEY_EXT_SPEC "Extended Specification" /* String */
+#define LIBSFF_KEY_MAX_CASE_TEMP "Maximum Case Temperature" /* String */
+#define LIBSFF_KEY_ATTENUATE_2G "Cable Attenuation at 2.5 GHz" /* String */
+#define LIBSFF_KEY_ATTENUATE_5G "Cable Attenuation at 5.0 GHz" /* String */
+#define LIBSFF_KEY_ATTENUATE_7G "Cable Attenuation at 7.0 GHz" /* String */
+#define LIBSFF_KEY_ATTENUATE_12G "Cable Attenuation at 12.9 GHz" /* String */
+#define LIBSFF_KEY_TRAN_TECH "Transmitter Technology" /* String */
+
+/*
+ * Note, different revisions of the SFF standard have different compliance
+ * values available. We try to use a common set of compliance keys when
+ * possible, even if the values will be different. All entries here are String
+ * Arrays.
+ */
+#define LIBSFF_KEY_COMPLIANCE_10GBE "10G+ Ethernet Compliance Codes"
+#define LIBSFF_KEY_COMPLIANCE_IB "Infiniband Compliance Codes"
+#define LIBSFF_KEY_COMPLIANCE_ESCON "ESCON Compliance Codes"
+#define LIBSFF_KEY_COMPLIANCE_SONET "SONET Compliance Codes"
+#define LIBSFF_KEY_COMPLIANCE_GBE "Ethernet Compliance Codes"
+#define LIBSFF_KEY_COMPLIANCE_FC_LEN "Fibre Channel Link Lengths"
+#define LIBSFF_KEY_COMPLIANCE_FC_TECH "Fibre Channel Technology"
+#define LIBSFF_KEY_COMPLIANCE_SFP "SFP+ Cable Technology"
+#define LIBSFF_KEY_COMPLIANCE_FC_MEDIA "Fibre Channel Transmission Media"
+#define LIBSFF_KEY_COMPLIANCE_FC_SPEED "Fibre Channel Speed"
+#define LIBSFF_KEY_COMPLIANCE_SAS "SAS Compliance Codes"
+#define LIBSFF_KEY_COMPLIANCE_ACTIVE "Active Cable Specification Compliance"
+#define LIBSFF_KEY_COMPLIANCE_PASSIVE "Passive Cable Specification Compliance"
+
+
+/*
+ * The following keys have meaning that varies based on the standard.
+ */
+#define LIBSFF_KEY_8472_EXT_IDENTIFIER "Extended Identifier" /* uint8_t */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSFF_H */
diff --git a/usr/src/lib/libsff/common/llib-lsff b/usr/src/lib/libsff/common/llib-lsff
new file mode 100644
index 0000000000..1636a7e1b0
--- /dev/null
+++ b/usr/src/lib/libsff/common/llib-lsff
@@ -0,0 +1,19 @@
+/*
+ * 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) 2017 Joyent, Inc.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <libsfp.h>
diff --git a/usr/src/lib/libsff/common/mapfile-vers b/usr/src/lib/libsff/common/mapfile-vers
new file mode 100644
index 0000000000..7e48256f37
--- /dev/null
+++ b/usr/src/lib/libsff/common/mapfile-vers
@@ -0,0 +1,37 @@
+#
+# 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) 2017, Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate {
+ global:
+ libsff_parse;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libsff/common/sff.h b/usr/src/lib/libsff/common/sff.h
new file mode 100644
index 0000000000..d3b64e7fba
--- /dev/null
+++ b/usr/src/lib/libsff/common/sff.h
@@ -0,0 +1,221 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+#ifndef _SFF_H
+#define _SFF_H
+
+/*
+ * Definitions internal to libsfp for various SFF versions. This generally
+ * contains offsets for each byte and its purpose. The meaning of the values are
+ * not generally found in this header.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This table is derived from SFF 8024 Section 4.1, Table 4-1.
+ */
+typedef enum sff_8024_id {
+ SFF_8024_ID_UNKNOWN = 0x00,
+ SFF_8024_ID_GBIC = 0x01,
+ SFF_8024_ID_SOLDERED = 0x02,
+ SFF_8024_ID_SFP = 0x03, /* SFP, SFP+, SFP28 */
+ SFF_8024_ID_XBI = 0x04,
+ SFF_8024_ID_XENPAK = 0x05,
+ SFF_8024_ID_XFP = 0x06,
+ SFF_8024_ID_XFF = 0x07,
+ SFF_8024_ID_XFP_E = 0x08,
+ SFF_8024_ID_XPAK = 0x09,
+ SFF_8024_ID_X2 = 0x0A,
+ SFF_8024_ID_DWDM_SFP = 0x0B,
+ SFF_8024_ID_QSFP = 0x0C,
+ SFF_8024_ID_QSFP_PLUS = 0x0D,
+ SFF_8024_ID_CXP = 0x0E,
+ SFF_8024_ID_SMMHD4X = 0x0F,
+ SFF_8024_ID_SMMHD8X = 0x10,
+ SFF_8024_ID_QSFP28 = 0x11,
+ SFF_8024_ID_CXP2 = 0x12,
+ SFF_8024_ID_CDFP = 0x13,
+ SFF_8024_ID_SMMHD4XF = 0x14,
+ SFF_8024_ID_SMMHD8XF = 0x15,
+ SFF_8024_ID_CDFP3 = 0x16,
+ SFF_8024_ID_MICROQSFP = 0x17,
+ SFF_8024_NIDS = 0x18,
+ SFF_8024_VENDOR = 0x80
+} sff_8024_id_t;
+
+
+/*
+ * Byte offsets for SFF-8472. Note that most of this applies to INF-8074.
+ * Generally speaking, SFF-8472 is a backwards compatible evolution of INF-8074.
+ */
+#define SFF_8472_IDENTIFIER 0
+#define SFF_8472_EXT_IDENTIFER 1
+#define SFF_8472_CONNECTOR 2
+
+/*
+ * Note that several constants overlap here as the offset is used for multiple
+ * purposes.
+ */
+#define SFF_8472_COMPLIANCE_10GE 3
+#define SFF_8472_COMPLIANCE_IB 3
+#define SFF_8472_COMPLIANCE_ESCON 4
+#define SFF_8472_COMPLIANCE_SONET_LOW 4
+#define SFF_8472_COMPLIANCE_SONET_HIGH 5
+#define SFF_8472_COMPLIANCE_ETHERNET 6
+#define SFF_8472_COMPLIANCE_FCLEN 7
+#define SFF_8472_COMPLIANCE_FC_LOW 7
+#define SFF_8472_COMPLIANCE_FC_HIGH 8
+#define SFF_8472_COMPLIANCE_SFP 8
+#define SFF_8472_COMPLIANCE_FC_MEDIA 9
+#define SFF_8472_COMPLIANCE_FC_SPEED 10
+
+#define SFF_8472_ENCODING 11
+#define SFF_8472_BR_NOMINAL 12
+#define SFF_8472_RATE_IDENTIFIER 13
+#define SFF_8472_LENGTH_SMF_KM 14
+#define SFF_8472_LENGTH_SMF 15
+#define SFF_8472_LENGTH_50UM 16
+#define SFF_8472_LENGTH_62UM 17
+#define SFF_8472_LENGTH_COPPER 18
+#define SFF_8472_LENGTH_OM3 19
+
+#define SFF_8472_VENDOR 20
+#define SFF_8472_VENDOR_LEN 16
+#define SFF_8472_TRANSCEIVER 36
+#define SFF_8472_OUI 37
+#define SFF_8472_OUI_LEN 3
+#define SFF_8472_VENDOR_PN 40
+#define SFF_8472_VENDOR_PN_LEN 16
+#define SFF_8472_VENDOR_REV 56
+#define SFF_8472_VENDOR_REV_LEN 4
+
+#define SFF_8472_PASSIVE_SPEC 60
+#define SFF_8472_ACTIVE_SPEC 60
+#define SFF_8472_WAVELENGTH_HI 60
+#define SFF_8472_WAVELENGTH_LOW 61
+
+#define SFF_8472_CC_BASE 63
+
+#define SFF_8472_OPTIONS_HI 64
+#define SFF_8472_OPTIONS_LOW 65
+#define SFF_8472_BR_MAX 66
+#define SFF_8472_BR_MIN 67
+#define SFF_8472_VENDOR_SN 68
+#define SFF_8472_VENDOR_SN_LEN 16
+#define SFF_8472_DATE_CODE 84
+#define SFF_8472_DATE_CODE_LEN 8
+#define SFF_8472_DIAG_MONITORING 92
+#define SFF_8472_ENHANCED_OPTIONS 93
+#define SFF_8472_SFF_8472_COMPLIANCE 94
+
+#define SFF_8472_CC_EXT 95
+#define SFF_8472_VENDOR_SPECIFIC 96
+#define SFF_8472_RESERVED 128
+
+/*
+ * These values are factors by which we should multiple or divide various units.
+ */
+#define SFF_8472_BR_NOMINAL_FACTOR 100
+#define SFF_8472_BR_MAX_FACTOR 250
+#define SFF_8472_BR_MIN_FACTOR 250
+#define SFF_8472_LENGTH_SMF_KM_FACTOR 1
+#define SFF_8472_LENGTH_SMF_FACTOR 100
+#define SFF_8472_LENGTH_50UM_FACTOR 10
+#define SFF_8472_LENGTH_62UM_FACTOR 10
+#define SFF_8472_LENGTH_COPPER_FACTOR 1
+#define SFF_8472_LENGTH_OM3_FACTOR 10
+#define SFF_8472_WAVELENGTH_FACTOR 1
+
+
+/*
+ * SFF 8636 related constants
+ */
+#define SFF_8636_IDENTIFIER 0
+#define SFF_8636_EXT_IDENTIFIER 129
+#define SFF_8636_CONNECTOR 130
+
+#define SFF_8636_COMPLIANCE_10GBEP 131
+#define SFF_8636_COMPLIANCE_SONET 132
+#define SFF_8636_COMPLIANCE_SAS 133
+#define SFF_8636_COMPLIANCE_ETHERNET 134
+#define SFF_8636_COMPLIANCE_FCLEN 135
+#define SFF_8636_COMPLIANCE_FC_LOW 135
+#define SFF_8636_COMPLIANCE_FC_HIGH 136
+#define SFF_8636_COMPLIANCE_FC_MEDIA 137
+#define SFF_8636_COMPLIANCE_FC_SPEED 138
+
+#define SFF_8636_ENCODING 139
+#define SFF_8636_BR_NOMINAL 140
+#define SFF_8636_BR_EXT_RATE_SELECT 141
+#define SFF_8636_LENGTH_SMF 142
+#define SFF_8636_LENGTH_OM3 143
+#define SFF_8636_LENGTH_OM2 144
+#define SFF_8636_LENGTH_OM1 145
+#define SFF_8636_LENGTH_COPPER 146
+#define SFF_8636_DEVICE_TECH 147
+#define SFF_8636_VENDOR 148
+#define SFF_8636_VENDOR_LEN 16
+#define SFF_8636_EXTENDED_MODULE 164
+#define SFF_8636_OUI 165
+#define SFF_8636_OUI_LEN 3
+#define SFF_8636_VENDOR_PN 168
+#define SFF_8636_VENDOR_PN_LEN 16
+#define SFF_8636_VENDOR_REV 184
+#define SFF_8636_VENDOR_REV_LEN 2
+
+#define SFF_8636_ATTENUATE_2G 186
+#define SFF_8636_ATTENUATE_5G 187
+#define SFF_8636_ATTENUATE_7G 188
+#define SFF_8636_ATTENUATE_12G 189
+#define SFF_8636_WAVELENGTH_NOMINAL_HI 186
+#define SFF_8636_WAVELENGTH_NOMINAL_LOW 187
+#define SFF_8636_WAVELENGTH_TOLERANCE_HI 188
+#define SFF_8636_WAVELENGTH_TOLERANCE_LOW 189
+#define SFF_8636_MAX_CASE_TEMP 190
+#define SFF_8636_CC_BASE 191
+
+#define SFF_8636_LINK_CODES 192
+#define SFF_8636_OPTIONS_HI 193
+#define SFF_8636_OPTIONS_MID 194
+#define SFF_8636_OPTIONS_LOW 195
+#define SFF_8636_VENDOR_SN 196
+#define SFF_8636_VENDOR_SN_LEN 16
+#define SFF_8636_DATE_CODE 212
+#define SFF_8636_DATE_CODE_LEN 8
+#define SFF_8636_DIAG_MONITORING 220
+#define SFF_8636_ENHANCED_OPTIONS 221
+#define SFF_8636_BR_NOMINAL_EXT 222
+#define SFF_8636_CC_EXT 223
+#define SFF_866_VENDOR_SPECIFIC 224
+
+/*
+ * SFF 8636 multiplication factors
+ */
+#define SFF_8636_BR_NOMINAL_FACTOR 100
+#define SFF_8636_BR_NOMINAL_EXT_FACTOR 250
+#define SFF_8636_LENGTH_SMF_FACTOR 1
+#define SFF_8636_LENGTH_OM3_FACTOR 2
+#define SFF_8636_LENGTH_OM2_FACTOR 1
+#define SFF_8636_LENGTH_OM1_FACTOR 1
+#define SFF_8636_LENGTH_COPPER_FACTOR 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFF_H */
diff --git a/usr/src/lib/libsff/i386/Makefile b/usr/src/lib/libsff/i386/Makefile
new file mode 100644
index 0000000000..0a22fa4dc3
--- /dev/null
+++ b/usr/src/lib/libsff/i386/Makefile
@@ -0,0 +1,18 @@
+#
+# 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) 2017 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libsff/sparc/Makefile b/usr/src/lib/libsff/sparc/Makefile
new file mode 100644
index 0000000000..0a22fa4dc3
--- /dev/null
+++ b/usr/src/lib/libsff/sparc/Makefile
@@ -0,0 +1,18 @@
+#
+# 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) 2017 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/man/man9e/Makefile b/usr/src/man/man9e/Makefile
index 2d32bfbe34..0c29674b11 100644
--- a/usr/src/man/man9e/Makefile
+++ b/usr/src/man/man9e/Makefile
@@ -42,6 +42,7 @@ MANFILES= Intro.9e \
ks_snapshot.9e \
ks_update.9e \
mac.9e \
+ mac_capab_transceiver.9e \
mc_getcapab.9e \
mc_getprop.9e \
mc_getstat.9e \
@@ -111,6 +112,8 @@ MANLINKS= _info.9e \
MAC.9e \
mc_close.9e \
mc_stop.9e \
+ mct_info.9e \
+ mct_read.9e \
intro.9e \
tran_destroy_pkt.9e \
tran_pkt_constructor.9e \
@@ -148,6 +151,9 @@ gldm_stop.9e := LINKSRC = gld.9e
mc_close.9e := LINKSRC = mc_open.9e
mc_stop.9e := LINKSRC = mc_start.9e
+mct_info.9e := LINKSRC = mac_capab_transceiver.9e
+mct_read.9e := LINKSRC = mac_capab_transceiver.9e
+
tran_setcap.9e := LINKSRC = tran_getcap.9e
tran_destroy_pkt.9e := LINKSRC = tran_init_pkt.9e
diff --git a/usr/src/man/man9e/mac_capab_transceiver.9e b/usr/src/man/man9e/mac_capab_transceiver.9e
new file mode 100644
index 0000000000..78649f0e32
--- /dev/null
+++ b/usr/src/man/man9e/mac_capab_transceiver.9e
@@ -0,0 +1,407 @@
+.\"
+.\" 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) 2017, Joyent, Inc.
+.\"
+.Dd Mar 24, 2017
+.Dt MAC_CAPAB_TRANSCEIVER 9E
+.Os
+.Sh NAME
+.Nm mac_capab_transciever ,
+.Nm mct_info ,
+.Nm mct_read
+.Nd MAC capability for networking transceivers
+.Sh SYNOPSIS
+.In sys/mac_provider.h
+.Vt typedef struct mac_capab_transceiver mac_capab_transceiver_t;
+.Ft int
+.Fo "mct_info"
+.Fa "void *driver"
+.Fa "uint_t id"
+.Fa "mac_transceiver_info_t *infop"
+.Fc
+.Ft int
+.Fo mct_read
+.Fa "void *driver"
+.Fa "uint_t id"
+.Fa "uint_t page"
+.Fa "void *buf"
+.Fa "size_t nbytes"
+.Fa "off_t offset"
+.Fa "size_t *nread"
+.Fc
+.Sh INTERFACE LEVEL
+.Sy Volatile -
+This interface is still evolving in illumos.
+API and ABI stability is
+not guaranteed.
+.Sh PARAMETERS
+.Bl -tag -width Fa
+.It Fa driver
+A pointer to the driver's private data that was passed in via the
+.Sy m_pdata
+member of the
+.Xr mac_register 9S
+structure to the
+.Xr mac_register 9F
+function.
+.It Fa id
+An integer value indicating which transceiver is being inquired about.
+.It Fa infop
+An opaque structure which is used to set information about the
+transceiver.
+.It Fa page
+A value that indicates which page from the i2c bus is being requested.
+.It Fa buf
+A pointer to which data should be written to when reading from the
+device.
+.It Fa nbytes
+A value indicating the number of bytes being asked to read into
+.Fa buf .
+.It Fa offset
+A value indicating the offset into the page to start reading data.
+.It Fa nread
+A value to be updated by the driver with the number of successfully read
+bytes.
+.El
+.Sh DESCRIPTION
+The
+.Sy MAC_CAPAB_TRANSCEIVER
+capability allows for GLDv3 networking device drivers to provide
+information to the system about their transceiver.
+Implementing this capability is optional.
+For more information on how to handle capabilities and how to indicate
+that a capability is not supported, see
+.Xr mc_getcapab 9E .
+.Pp
+This capability should be implemented if the device in question supports
+a Small Form Factor (SFF) transceiver.
+These are more commonly known by names such as SFP, SFP+, SFP28, QSFP+,
+and QSFP28.
+This interface does not apply to traditional copper Ethernet phys.
+These transceivers provide standardized information over the i2c bus at
+specific pages.
+.Ss Supported Standards
+.Bl -tag -width Sy
+.It Sy INF-8074
+The
+.Sy INF-8084
+standard was the original multiple source agreement (MSA) for SFP
+devices.
+It proposed the original series of management pages at i2c page 0xa0.
+This page contained up to 512 bytes, however, only the first
+96 bytes are standardized.
+Bytes 97 to 127 are reserved for the vendor.
+The remaining bytes are reserved by the specification.
+The management page was subsequently adopted by SFP+ devices.
+.It Sy SFF-8472
+The
+.Sy SFF-8472
+standard extended the original SFP MSA.
+This standard added a second i2c page at 0xa2, while maintaining the
+original page at 0xa0.
+The page at 0xa0 is now explicitly 256 bytes.
+The page at 0xa2 is also 256 bytes.
+This standard was also adopted for all SFP28 parts, which are commonly
+used in transceivers for 25 Gb/s Ethernet.
+.It Sy SFF-8436
+The
+.Sy SFF-8436
+standard was developed for QSFP+ transceivers, which involve the
+bonding of 4 SFP+ links.
+QSFP+ is commonly used in the transceivers for 40 Gb/s Ethernet.
+This standard uses i2c page 0xa0 for read-only identification purposes.
+The lower half of the page is used for control, while the upper 128
+bytes is similar to the
+.Sy INF-8084
+and
+.Sy SFF-8472
+standards.
+.It Sy SFF-8636
+The
+.Sy SFF-8636
+standard is a common management standard which is shared between both
+SAS and QSFP+ 28 Gb/s transceivers.
+The latter transceiver is commonly found in 100 Gb/s Ethernet.
+The transceiver's memory map is similar to that found in the
+.Sy SFF-8436
+specification.
+The identification information is found in the upper 128
+bytes of page 0xa0, while the lower part of the page is used for
+control, among other purposes.
+.El
+.Pp
+The following table summarizes the above information.
+.Bl -column "Sy SFF-8636" "1 Gb/s, 10 Gb/s, 25 Gb/s" "256 bytes" "0xa0, 0xa2" -offset indent
+.Em "Standard" Ta Em Speeds Ta Em Size Ta Em i2c pages
+.It INF-8074 Ta 1 Gb/s, 10 Gb/s Ta 128 bytes Ta 0xa0
+.It SFF-8472 Ta 1 Gb/s, 10 Gb/s, 25 GB/s Ta 512 bytes Ta 0xa0, 0xa2
+.It SFF-8436 Ta 40 Gb/s Ta 256 bytes Ta 0xa0
+.It SFF-8636 Ta 100 Gb/s Ta 256 bytes Ta 0xa0
+.El
+.Ss MAC Capability Structure
+When the device driver's
+.Xr mc_getcapab 9E
+function entry point is called with the capability requested set to
+.Sy MAC_CAPAB_TRANSCEIVER ,
+then the value of the capability structure is the following structure:
+.Bd -literal -offset indent
+typedef struct mac_capab_transceiver {
+ uint_t mct_flags;
+ uint_t mct_ntransceiveres;
+ int (*mct_info)(void *driver, uint_t id,
+ mac_transceiver_info_t *infop),
+ int (*mct_read)(void *driver, uint_t id, uint_t page,
+ void *buf, size_t nbytes, off_t offset,
+ size_t *nread)
+} mac_capab_transceiver_t;
+.Ed
+.Pp
+If the device driver supports the
+.Sy MAC_CAPAB_TRANSCEIVER
+capability, it should fill in this structure, based on the following
+rules:
+.Bl -tag -width Sy
+.It Sy mct_flags
+The
+.Vt mct_flags
+member is used to negotiate extensions with the driver.
+MAC will set the value of
+.Vt mct_flags
+to include all of the currently known extensions.
+The driver should intersect this list with the set that they actually
+support.
+At this time, no such features are defined and the driver should set the
+member to
+.Sy 0 .
+.It Sy mct_ntransceivers
+The value of
+.Sy mct_ntransceivers
+indicates that the number of transceivers present in the device.
+For most devices, it is expected that this value will be set to one.
+However, some devices do support multiple transceivers and PHYs that
+show up behind a single logical MAC.
+.Pp
+It is expected that this value will not change across the lifetime of
+the device being attached.
+It is important to remember that this represents the total possible
+number of transceivers in the device, not how many are currently present
+and powered on.
+.Pp
+The number of transceivers will influence the
+.Fa id
+argument used in the
+.Fn mct_info
+and
+.Fn mct_read
+entry points.
+The transceiver IDs will start at zero and go to the value of
+.Fa mct_ntransceivers - 1 .
+It is up to the driver to keep the mapping between actual transceivers
+and the transceiver identifiers consistent.
+.It Sy mct_info
+The
+.Fn mct_info
+entry point is used to set basic information about the transceiver.
+This entry point is
+.Em required .
+If the device driver cannot implement this entry point, then it should
+not indicate that it supports the capability.
+.Pp
+The
+.Fn mct_info
+entry point should fill in information about the transceiver with an
+identifier of
+.Fa id .
+See the description above of
+.Sy mct_ntransceivers
+for more information on how the IDs are determined.
+.Pp
+The driver should then proceed to fill in basic information by calling
+the functions described in the section
+.Sx Information Functions .
+After successfully calling all of the functions, the driver should
+return
+.Sy 0 .
+Othewrise, it should return the appropriate error number.
+For a full list of error numbers, see
+.Xr Intro 2 .
+Common values are:
+.Bl -tag -width Er -offset width
+.It Er EINVAL
+The transceiver identifier
+.Fa id
+was invalid.
+.It Er ENOTSUP
+This instance of the devices does not support a transceiver.
+For example, a device which sometimes has copper PHYs and therefore this
+instance does not have any PHYs.
+.It Er EIO
+An error occurred while trying to read device registers.
+For example, an FM-aware device had an error.
+.El
+.It Sy mct_read
+The
+.Fn mct_read
+function is used to read information from a transceiver's i2c bus.
+The
+.Fn mct_read
+entry point is an
+.Em optional
+entry point.
+.Pp
+The transceiver should first check the value of
+.Fa id ,
+which indicates which transceiver information is being requested.
+See the description above of
+.Sy mct_ntransceivers
+for more information on how the IDs are determined.
+.Pp
+The driver should try to read up to
+.Fa nbytes
+of data from the i2c bus at page
+.Fa page .
+The driver should start reading at offset
+.Fa offset .
+Finally, it should update the value in
+.Fa nread
+with the number of bytes written to the buffer
+.Fa buf .
+.Pp
+If for some reason the driver cannot read all of the requested bytes,
+that is acceptable.
+Instead it should perform a short read.
+This may occur because the transceiver does not allow reads at a
+requested region or the region is shorter than is common for most
+devices.
+.Pp
+Upon successful completion, the driver should ensure that
+.Fa nread
+has been updated and then return
+.Sy 0 .
+Otherwise, the driver should return the appropriate error number.
+For
+a full list of error numbers, see
+.Xr Intro 2 .
+Common values are:
+.Bl -tag -width Er -offset width
+.It Er EINVAL
+The value of
+.Fa id
+represented an invalid transceiver identifier.
+The transceiver i2c page
+.Fa page
+is not valid for this type of device.
+The value of
+.Fa offset
+is beyond the range supported for this
+.Fa page .
+.It Er EIO
+An error occurred while trying to read the device i2c pages.
+.El
+.El
+.Ss Transceiver Information Functions
+The
+.Fn mct_info
+entry point is the primary required entry point for a device driver
+which supports this capability.
+The information structure is opaque to the device driver.
+Instead, a series of informational functions is
+available to the device driver to call on the transceiver.
+The device drivers should try to call and fill in as many of these as
+possible.
+There are two different properties that a driver can set:
+.Bl -enum -offset indent
+.It
+Whether the transceiver is present.
+.It
+Whether the transceiver is usable.
+.El
+.Pp
+To set whether or not the transceiver is present, the driver should call
+.Xr mac_transceiver_info_set_present 9F .
+This is used to indicate whether the transceiver is plugged in or not.
+If the transceiver is a part of the NIC, then this function should
+always be called with the value set to
+.Dv B_TRUE .
+.Pp
+Finally, the driver has the ability to provide information about whether
+or not the transceiver is usable or not.
+A transceiver may be present, but not usable, if the hardware and
+firmware support a limited number of transceivers.
+To set this information, the driver should call
+.Xr mac_transceiver_info_set_usable 9F .
+If the transceiver is not present, then the driver should not call this
+function.
+.Ss Opaque Transceivers
+Some devices abstract the nature of the transceiver and do not allow
+direct access to the transceiver.
+In this case, if the device driver still has access to enough
+information to know if the transceiver is at least present, then it
+should still implement the
+.Fn mct_info
+entry point.
+.Ss Locking and Data Access
+Calls to get information about the transceivers may come at the same
+time as general I/O requests to the device to send or receive data.
+The driver should make sure that reading data from the i2c bus of the
+transceiver does not interfere with the device's functionality in this
+regard.
+Different locks should be used.
+.Pp
+On some devices, reading from the transceiver's i2c bus might cause a
+disruption of service to the device.
+For example, on some devices a phy reset may be required or come about
+as a side effect of trying to read the device.
+If any kind of disruption would be caused, then the driver
+must not implement the
+.Ft mct_read
+entry point.
+.Sh CONTEXT
+The various callback functions will be called from
+.Sy kernel
+context.
+These functions will never be called from
+.Sy interrupt
+context.
+.Sh SEE ALSO
+.Xr Intro 2 ,
+.Xr mac 9E ,
+.Xr mc_getcapab 9E ,
+.Xr mac_register 9F ,
+.Xr mac_transceiver_info_set_present 9F ,
+.Xr mac_transceiver_info_set_usable 9F ,
+.Xr mac_register 9S
+.Rs
+.%N INF-8074i
+.%T SFP (Small Formfactor Pluggable) Interface
+.%Q SFF Committee
+.%O Revision 1.0
+.%D May 12, 2001
+.Re
+.Rs
+.%N SFF-8472
+.%T Diagnostic Monitoring Interface for Optical Transceivers
+.%O Revision 12.2
+.%D November 21, 2014
+.Re
+.Rs
+.%N SFF-8436
+.%T QSFP+ 10 Gbs 4X PLUGGABLE TRANSCEIVER
+.%O Revision 4.8
+.%D October 31, 2013
+.Re
+.Rs
+.%N SFF-8636
+.%T Management Interface for Cabled Environments
+.%O Revision 2.7
+.%D January 26, 2016
+.Re
diff --git a/usr/src/man/man9f/Makefile b/usr/src/man/man9f/Makefile
index 30c1d87148..dcea9314f9 100644
--- a/usr/src/man/man9f/Makefile
+++ b/usr/src/man/man9f/Makefile
@@ -332,6 +332,7 @@ MANFILES= ASSERT.9f \
mac_prop_info.9f \
mac_register.9f \
mac_rx.9f \
+ mac_transceiver_info.9f \
mac_tx_update.9f \
makecom.9f \
makedevice.9f \
@@ -999,6 +1000,8 @@ MANLINKS= AVL_NEXT.9f \
mac_prop_info_set_default_uint8.9f \
mac_prop_info_set_perm.9f \
mac_prop_info_set_range_uint32.9f \
+ mac_transceiver_info_set_present.9f \
+ mac_transceiver_info_set_usable.9f \
mac_unregister.9f \
makecom_g0.9f \
makecom_g0_s.9f \
@@ -1828,6 +1831,10 @@ mac_prop_info_set_default_uint32.9f := LINKSRC = mac_prop_info.9f
mac_prop_info_set_default_uint64.9f := LINKSRC = mac_prop_info.9f
mac_prop_info_set_perm.9f := LINKSRC = mac_prop_info.9f
mac_prop_info_set_range_uint32.9f := LINKSRC = mac_prop_info.9f
+
+mac_transceiver_info_set_present.9f := LINKSRC = mac_transceiver_info.9f
+mac_transceiver_info_set_usable.9f := LINKSRC = mac_transceiver_info.9f
+
mac_unregister.9f := LINKSRC = mac_register.9f
makecom_g0.9f := LINKSRC = makecom.9f
diff --git a/usr/src/man/man9f/mac_transceiver_info.9f b/usr/src/man/man9f/mac_transceiver_info.9f
new file mode 100644
index 0000000000..9b04ab3a9d
--- /dev/null
+++ b/usr/src/man/man9f/mac_transceiver_info.9f
@@ -0,0 +1,95 @@
+.\"
+.\" 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) 2017, Joyent, Inc.
+.\"
+.Dd Feb 21, 2017
+.Dt MAC_TRANSCEIVER_INFO 9F
+.Os
+.Sh NAME
+.Nm mac_transceiver_info ,
+.Nm mac_transceiver_info_set_present ,
+.Nm mac_transceiver_info_set_usable
+.Nd set MAC transceiver property information
+.Sh SYNOPSIS
+.In sys/mac_provider.h
+.Ft void
+.Fo mac_transceiver_info_set_present
+.Fa "mac_transceiver_info_t *infop"
+.Fa "boolean_t present"
+.Fc
+.Ft void
+.Fo mac_transceiver_info_set_usable
+.Fa "mac_transceiver_info_t *infop"
+.Fa "boolean_t usable"
+.Fc
+.Sh INTERFACE LEVEL
+.Sy Volatile -
+This interface is still evolving in illumos.
+API and ABI stability is
+not guaranteed.
+.Sh PARAMETERS
+.Bl -tag -width Fa
+.It Fa infop
+A pointer to an opaque structure obtained as an argument to the
+.Xr mct_info 9E
+entry point.
+.It Fa present
+A boolean that indicates whether the transceiver is present.
+.It Fa usable
+A boolean that indicates whether the transceiver is usable.
+.El
+.Sh DESCRIPTION
+The
+.Fn mac_transceiver_set_present
+and
+.Fn mac_transceiver_set_usable
+functions are used to set information about a transceiver as part of the
+.Xr mct_info 9E
+entry point to obtain information about a MAC transceiver.
+For more information and background, see the
+.Sy Transceiver Information Functions
+section of
+.Xr mac_capab_transceiver 9E .
+.Pp
+The
+.Fn mct_transceiver_set_present
+function sets whether or not the transceiver is present and plugged into
+the system.
+If the transceiver is not plugged in, then the function
+should be called with
+.Fa present set to
+.Dv B_FALSE ,
+otehrwise it should use
+.Dv B_TRUE .
+.Pp
+The
+.Fn mct_transceiver_set_usable
+function determines whether or not the device can use the transceiver.
+If the device cannot use the transceiver, then it should call the
+function with
+.Fa usable
+set to
+.Dv B_FALSE .
+Otherwise, it should use
+.Dv B_TRUE .
+If the transceiver is not present, then this function should not be
+called.
+.Sh CONTEXT
+These functions should be called in response to handling the
+.Fn mct_info 9E
+entry point for transceivers in
+.Sy kernel
+context.
+.Sh SEE ALSO
+.Xr mac 9E ,
+.Xr mac_capab_transceiver 9E ,
+.Xr mct_info 9E
diff --git a/usr/src/pkg/manifests/service-fault-management.mf b/usr/src/pkg/manifests/service-fault-management.mf
index c2dd3890ee..e83432f48f 100644
--- a/usr/src/pkg/manifests/service-fault-management.mf
+++ b/usr/src/pkg/manifests/service-fault-management.mf
@@ -569,6 +569,7 @@ file path=usr/lib/fm/topo/plugins/disk.so mode=0555
file path=usr/lib/fm/topo/plugins/fac_prov_ipmi.so mode=0555
file path=usr/lib/fm/topo/plugins/fac_prov_mptsas.so mode=0555
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/xfp.so mode=0555
#
diff --git a/usr/src/pkg/manifests/system-kernel.man9e.inc b/usr/src/pkg/manifests/system-kernel.man9e.inc
index a3e8bc6788..b45d3cbabe 100644
--- a/usr/src/pkg/manifests/system-kernel.man9e.inc
+++ b/usr/src/pkg/manifests/system-kernel.man9e.inc
@@ -37,6 +37,7 @@ file path=usr/share/man/man9e/ioctl.9e
file path=usr/share/man/man9e/ks_snapshot.9e
file path=usr/share/man/man9e/ks_update.9e
file path=usr/share/man/man9e/mac.9e
+file path=usr/share/man/man9e/mac_capab_transceiver.9e
file path=usr/share/man/man9e/mc_getcapab.9e
file path=usr/share/man/man9e/mc_getprop.9e
file path=usr/share/man/man9e/mc_getstat.9e
@@ -94,6 +95,8 @@ link path=usr/share/man/man9e/gldv3.9e target=mac.9e
link path=usr/share/man/man9e/intro.9e target=Intro.9e
link path=usr/share/man/man9e/mc_close.9e target=mc_open.9e
link path=usr/share/man/man9e/mc_stop.9e target=mc_start.9e
+link path=usr/share/man/man9e/mct_info.9e target=mac_capab_transceiver.9e
+link path=usr/share/man/man9e/mct_read.9e target=mac_capab_transceiver.9e
link path=usr/share/man/man9e/tran_destroy_pkt.9e target=tran_init_pkt.9e
link path=usr/share/man/man9e/tran_pkt_constructor.9e target=tran_setup_pkt.9e
link path=usr/share/man/man9e/tran_pkt_destructor.9e target=tran_setup_pkt.9e
diff --git a/usr/src/pkg/manifests/system-kernel.man9f.inc b/usr/src/pkg/manifests/system-kernel.man9f.inc
index 8f82c38cec..31b6644aa9 100644
--- a/usr/src/pkg/manifests/system-kernel.man9f.inc
+++ b/usr/src/pkg/manifests/system-kernel.man9f.inc
@@ -327,6 +327,7 @@ file path=usr/share/man/man9f/mac_maxsdu_update.9f
file path=usr/share/man/man9f/mac_prop_info.9f
file path=usr/share/man/man9f/mac_register.9f
file path=usr/share/man/man9f/mac_rx.9f
+file path=usr/share/man/man9f/mac_transceiver_info.9f
file path=usr/share/man/man9f/mac_tx_update.9f
file path=usr/share/man/man9f/makecom.9f
file path=usr/share/man/man9f/makedevice.9f
@@ -1015,6 +1016,10 @@ link path=usr/share/man/man9f/mac_prop_info_set_perm.9f \
target=mac_prop_info.9f
link path=usr/share/man/man9f/mac_prop_info_set_range_uint32.9f \
target=mac_prop_info.9f
+link path=usr/share/man/man9f/mac_transceiver_info_set_present.9f \
+ target=mac_transceiver_info.9f
+link path=usr/share/man/man9f/mac_transceiver_info_set_usable.9f \
+ target=mac_transceiver_info.9f
link path=usr/share/man/man9f/mac_unregister.9f target=mac_register.9f
link path=usr/share/man/man9f/makecom_g0.9f target=makecom.9f
link path=usr/share/man/man9f/makecom_g0_s.9f target=makecom.9f
diff --git a/usr/src/pkg/manifests/system-library.mf b/usr/src/pkg/manifests/system-library.mf
index 87b6465d5e..523abb44d7 100644
--- a/usr/src/pkg/manifests/system-library.mf
+++ b/usr/src/pkg/manifests/system-library.mf
@@ -375,6 +375,7 @@ file path=usr/lib/$(ARCH64)/libreparse.so.1
$(i386_ONLY)file path=usr/lib/$(ARCH64)/libsaveargs.so.1
file path=usr/lib/$(ARCH64)/libsched.so.1
file path=usr/lib/$(ARCH64)/libsctp.so.1
+file path=usr/lib/$(ARCH64)/libsff.so.1
file path=usr/lib/$(ARCH64)/libshell.so.1
file path=usr/lib/$(ARCH64)/libsip.so.1
file path=usr/lib/$(ARCH64)/libsldap.so.1
@@ -450,6 +451,7 @@ file path=usr/lib/libraidcfg.so.1
file path=usr/lib/libreparse.so.1
file path=usr/lib/libsched.so.1
file path=usr/lib/libsctp.so.1
+file path=usr/lib/libsff.so.1
file path=usr/lib/libshell.so.1
file path=usr/lib/libsip.so.1
file path=usr/lib/libsldap.so.1
diff --git a/usr/src/pkg/manifests/system-network.mf b/usr/src/pkg/manifests/system-network.mf
index bcc96e4b34..fd44515945 100644
--- a/usr/src/pkg/manifests/system-network.mf
+++ b/usr/src/pkg/manifests/system-network.mf
@@ -43,6 +43,7 @@ dir path=etc/nwam group=netadm owner=netadm
dir path=etc/nwam/loc group=netadm owner=netadm
dir path=etc/nwam/loc/NoNet group=netadm owner=netadm
dir path=sbin group=sys
+dir path=usr/lib/dl
dir path=usr/share/man
dir path=usr/share/man/man1m
file path=etc/default/dhcpagent group=sys \
@@ -82,6 +83,7 @@ file path=sbin/dlstat mode=0555
file path=sbin/flowadm mode=0555
file path=sbin/flowstat mode=0555
file path=sbin/ipadm mode=0555
+file path=usr/lib/dl/dltraninfo mode=0555
file path=usr/share/man/man1m/dladm.1m
file path=usr/share/man/man1m/flowadm.1m
file path=usr/share/man/man1m/ipadm.1m
diff --git a/usr/src/pkg/manifests/system-test-utiltest.mf b/usr/src/pkg/manifests/system-test-utiltest.mf
index 045ba608b0..1bf15858bd 100644
--- a/usr/src/pkg/manifests/system-test-utiltest.mf
+++ b/usr/src/pkg/manifests/system-test-utiltest.mf
@@ -31,6 +31,7 @@ dir path=opt/util-tests/tests/dis/i386
dir path=opt/util-tests/tests/dis/sparc
dir path=opt/util-tests/tests/files
dir path=opt/util-tests/tests/libnvpair_json
+dir path=opt/util-tests/tests/libsff
file path=opt/util-tests/README mode=0444
file path=opt/util-tests/bin/print_json mode=0555
file path=opt/util-tests/bin/utiltest mode=0555
@@ -237,6 +238,36 @@ file path=opt/util-tests/tests/libnvpair_json/json_05_strings mode=0555
file path=opt/util-tests/tests/libnvpair_json/json_06_nested mode=0555
file path=opt/util-tests/tests/libnvpair_json/json_07_nested_arrays mode=0555
file path=opt/util-tests/tests/libnvpair_json/json_common mode=0555
+file path=opt/util-tests/tests/libsff/libsff mode=0555
+file path=opt/util-tests/tests/libsff/libsff_8472 mode=0555
+file path=opt/util-tests/tests/libsff/libsff_8472.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_8636_diag mode=0555
+file path=opt/util-tests/tests/libsff/libsff_8636_diag.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_8636_extspec mode=0555
+file path=opt/util-tests/tests/libsff/libsff_8636_extspec.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_8636_tech mode=0555
+file path=opt/util-tests/tests/libsff/libsff_8636_tech.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_8636_temp mode=0555
+file path=opt/util-tests/tests/libsff/libsff_8636_temp.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_br mode=0555
+file path=opt/util-tests/tests/libsff/libsff_br.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_compliance mode=0555
+file path=opt/util-tests/tests/libsff/libsff_compliance.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_conn mode=0555
+file path=opt/util-tests/tests/libsff/libsff_conn.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_efault mode=0555
+file path=opt/util-tests/tests/libsff/libsff_einval mode=0555
+file path=opt/util-tests/tests/libsff/libsff_enc mode=0555
+file path=opt/util-tests/tests/libsff/libsff_enc.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_ident mode=0555
+file path=opt/util-tests/tests/libsff/libsff_ident.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_lengths mode=0555
+file path=opt/util-tests/tests/libsff/libsff_lengths.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_opts mode=0555
+file path=opt/util-tests/tests/libsff/libsff_opts.out mode=0444
+file path=opt/util-tests/tests/libsff/libsff_strings mode=0555
+file path=opt/util-tests/tests/libsff/libsff_wave mode=0555
+file path=opt/util-tests/tests/libsff/libsff_wave.out mode=0444
file path=opt/util-tests/tests/printf_test mode=0555
file path=opt/util-tests/tests/set-linkprop mode=0555
file path=opt/util-tests/tests/xargs_test mode=0555
diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index 76a66454c0..97d5736a20 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -27,6 +27,7 @@ outputdir = /var/tmp/test_results
[/opt/util-tests/tests/printf_test]
[/opt/util-tests/tests/allowed-ips]
[/opt/util-tests/tests/set-linkprop]
+[/opt/util-tests/tests/libsff/libsff]
[/opt/util-tests/tests/xargs_test]
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index 3d82685ca1..58e303b03f 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -15,6 +15,6 @@
# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
-SUBDIRS = dis dladm iconv libnvpair_json printf xargs grep_xpg4
+SUBDIRS = dis dladm iconv libnvpair_json libsff printf xargs grep_xpg4
include $(SRC)/test/Makefile.com
diff --git a/usr/src/test/util-tests/tests/libsff/Makefile b/usr/src/test/util-tests/tests/libsff/Makefile
new file mode 100644
index 0000000000..99a3053259
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/Makefile
@@ -0,0 +1,88 @@
+#
+# 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) 2017, Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/util-tests
+TESTDIR = $(ROOTOPTPKG)/tests/libsff
+
+DIFF_PROGS = \
+ libsff_8472 \
+ libsff_8636_diag \
+ libsff_8636_extspec \
+ libsff_8636_tech \
+ libsff_8636_temp \
+ libsff_br \
+ libsff_conn \
+ libsff_compliance \
+ libsff_enc \
+ libsff_ident \
+ libsff_lengths \
+ libsff_opts \
+ libsff_wave
+
+ERR_PROGS = \
+ libsff_efault \
+ libsff_einval
+
+PROGS = $(DIFF_PROGS) \
+ $(ERR_PROGS) \
+ libsff_strings
+
+SCRIPTS = libsff
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+CMDS = $(PROGS:%=$(TESTDIR)/%) $(SCRIPTS:%=$(TESTDIR)/%)
+OUTFILES = $(DIFF_PROGS:%=$(TESTDIR)/%.out)
+$(CMDS) := FILEMODE = 0555
+$(OUTFILES) := FILEMODE = 0444
+
+CPPFLAGS += -I$(SRC)/lib/libsff/common
+
+#
+# Different tests require different sets of libraries. If we try and use
+# the same set, we'll get guidance errors from ld.
+#
+$(ERR_PROGS) := LDLIBS += -lsff
+$(DIFF_PROGS) := LDLIBS += -lsff -lnvpair
+libsff_strings := LDLIBS += -lsff -lnvpair
+
+all: $(PROGS)
+
+install: all $(CMDS) $(OUTFILES)
+
+lint: lint_SRCS
+
+clobber: clean
+ -$(RM) $(PROGS)
+
+clean:
+
+$(CMDS): $(TESTDIR) $(PROG)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
+
+$(TESTDIR)/%: %.ksh
+ $(INS.rename)
+
+%: %.c
+ $(LINK.c) -o $@ $< $(LDLIBS)
+ $(POST_PROCESS)
diff --git a/usr/src/test/util-tests/tests/libsff/libsff.ksh b/usr/src/test/util-tests/tests/libsff/libsff.ksh
new file mode 100644
index 0000000000..d9974bc10c
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff.ksh
@@ -0,0 +1,61 @@
+#! /usr/bin/ksh
+#
+#
+# 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) 2017, Joyent, Inc.
+#
+
+#
+# Run all of the various libsff tests.
+#
+
+unalias -a
+sff_arg0=$(basename $0)
+sff_origwd=
+sff_root=
+sff_tests="8472 br compliance conn enc ident lengths opts strings wave"
+sff_tests="$sff_tests 8636_diag 8636_extspec 8636_tech 8636_temp einval efault"
+sff_outfile="/tmp/$sff_arg0.out.$$"
+
+fatal()
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "TEST FAILED: $sff_arg0: $msg" >&2
+ rm -f $sff_outfile
+ exit 1
+}
+
+sff_origwd=$PWD
+cd $(dirname $0) || fatal "failed to cd to test root"
+sff_root=$PWD
+cd $dt_origwd || fatal "failed to return to original dir"
+
+for t in $sff_tests; do
+ difffile=
+ testfile=$sff_root/libsff_$t
+
+ if ! $testfile > $sff_outfile; then
+ fatal "failed to run $testfile"
+ fi
+
+ if [[ -f $testfile.out ]]; then
+ if ! diff $testfile.out $sff_outfile >/dev/null; then
+ fatal "$t results differ from expected values"
+ fi
+ fi
+ printf "TEST PASSED: libsff_%s\n" $t
+done
+
+rm -f $sff_outfile || fatal "failed to remove output file"
+exit 0
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8472.c b/usr/src/test/util-tests/tests/libsff/libsff_8472.c
new file mode 100644
index 0000000000..5649faabec
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8472.c
@@ -0,0 +1,61 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print all SFF 8472 Compliance levels
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ uint_t i;
+ uint8_t buf[256];
+
+ bzero(buf, sizeof (buf));
+ for (i = 0; i < UINT8_MAX; i++) {
+ int ret;
+ nvlist_t *nvl;
+ char *val;
+
+ buf[SFF_8472_SFF_8472_COMPLIANCE] = i;
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP 8472 "
+ "Compliance value %d: %s\n", i, strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_COMPLIANCE_8472,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find key %s with "
+ "value %d: %s", LIBSFF_KEY_COMPLIANCE_8472, i,
+ strerror(ret));
+ }
+
+ (void) puts(val);
+ nvlist_free(nvl);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8472.out b/usr/src/test/util-tests/tests/libsff/libsff_8472.out
new file mode 100644
index 0000000000..f3a878f20a
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8472.out
@@ -0,0 +1,255 @@
+Not compliant
+Rev 9.3
+Rev 9.5
+Rev 10.2
+Rev 10.4
+Rev 11.0
+Rev 11.3
+Rev 11.4
+Rev 12.0
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
+Unallocated
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8636_diag.c b/usr/src/test/util-tests/tests/libsff/libsff_8636_diag.c
new file mode 100644
index 0000000000..bf84ea9f39
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8636_diag.c
@@ -0,0 +1,61 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print all SFF 8636 diagnostic monitoring
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ int ret;
+ uint8_t buf[256];
+ char **vals;
+ uint_t count, i;
+ nvlist_t *nvl;
+
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ buf[SFF_8636_DIAG_MONITORING] = 0xff;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP diagnostics: "
+ "%s\n", strerror(errno));
+ }
+
+ if ((ret = nvlist_lookup_string_array(nvl, LIBSFF_KEY_DIAG_MONITOR,
+ &vals, &count)) != 0) {
+ errx(1, "TEST FAILED: failed to find key %s: %s ",
+ LIBSFF_KEY_EXT_SPEC, strerror(ret));
+ }
+
+ for (i = 0; i < count; i++) {
+ (void) printf("%d\t%s\n", i, vals[i]);
+ }
+
+ nvlist_free(nvl);
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8636_diag.out b/usr/src/test/util-tests/tests/libsff/libsff_8636_diag.out
new file mode 100644
index 0000000000..b7e47c349e
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8636_diag.out
@@ -0,0 +1,2 @@
+0 Received power measurements: Average Power
+1 Transmitter power measurement
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8636_extspec.c b/usr/src/test/util-tests/tests/libsff/libsff_8636_extspec.c
new file mode 100644
index 0000000000..03d58658f6
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8636_extspec.c
@@ -0,0 +1,62 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print all SFF 8636 / 8024 extended specification values
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ uint_t i;
+ uint8_t buf[256];
+
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ for (i = 0; i < UINT8_MAX; i++) {
+ int ret;
+ nvlist_t *nvl;
+ char *val;
+
+ buf[SFF_8636_LINK_CODES] = i;
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP link code "
+ "%d: %s\n", i, strerror(errno));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_EXT_SPEC,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find key %s with "
+ "value %d: %s", LIBSFF_KEY_EXT_SPEC, i,
+ strerror(ret));
+ }
+
+ (void) puts(val);
+ nvlist_free(nvl);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8636_extspec.out b/usr/src/test/util-tests/tests/libsff/libsff_8636_extspec.out
new file mode 100644
index 0000000000..1c55ceb022
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8636_extspec.out
@@ -0,0 +1,255 @@
+Unspecified
+100G AOC or 25GAUI C2M AOC
+100GBASE-SR4 or 25GBASE-SR
+100GBASE-LR4 or 25GBASE-LR
+100GBASE-ER4 or 25GBASE-ER
+100GBASE-SR10
+100G CWDM4
+100G PSM4 Parallel SMF
+100G ACC or 25GAUI C2M ACC
+Obsolete
+Reserved
+100GBASE-CR4 or 25GBASE-CR CA-L
+25GBASE-CR CA-S
+25GBASE-CR CA-N
+Reserved
+Reserved
+40GBASE-ER4
+4 x 10GBASE-SR
+40G PSM4 Parallel SMF
+G959.1 profile P1I1-2D1
+G959.1 profile P1S1-2D2
+G959.1 profile P1L1-2D2
+10GBASE-T with SFI electrical interface
+100G CLR4
+100G AOC or 25GAUI C2M AOC
+100G ACC or 25GAUI C2M ACC
+100GE-DWDM2
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8636_tech.c b/usr/src/test/util-tests/tests/libsff/libsff_8636_tech.c
new file mode 100644
index 0000000000..21f02cab8c
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8636_tech.c
@@ -0,0 +1,79 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print SFF 8636 device tech values.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+static void
+lst_print_array(nvlist_t *nvl, const char *key)
+{
+ int ret;
+ uint_t i, count;
+ char **vals;
+
+ if ((ret = nvlist_lookup_string_array(nvl, key, &vals, &count)) != 0) {
+ errx(1, "TEST FAILED failed to find key %s: %s\n", key,
+ strerror(ret));
+ }
+
+ (void) puts(key);
+ for (i = 0; i < count; i++) {
+ (void) printf("\t%d\t%s\n", i, vals[i]);
+ }
+}
+
+int
+main(void)
+{
+ uint_t i;
+ uint8_t buf[256];
+
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+
+ /*
+ * The upper four bits of this value are used as a 4-bit identifier. The
+ * lower four bits are used as options.
+ */
+ for (i = 0; i < 16; i++) {
+ int ret;
+ nvlist_t *nvl;
+
+ buf[SFF_8636_DEVICE_TECH] = i << 4;
+ buf[SFF_8636_DEVICE_TECH] |= (i % 16);
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP device tech "
+ "%d: %s\n", i, strerror(errno));
+ }
+
+ lst_print_array(nvl, LIBSFF_KEY_TRAN_TECH);
+ nvlist_free(nvl);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8636_tech.out b/usr/src/test/util-tests/tests/libsff/libsff_8636_tech.out
new file mode 100644
index 0000000000..c8652f21fb
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8636_tech.out
@@ -0,0 +1,96 @@
+Transmitter Technology
+ 0 850 nm VCSEL
+ 1 No Wavelength Control
+ 2 Uncooled Transmitter
+ 3 Pin Detector
+ 4 Transmitter Not Tunable
+Transmitter Technology
+ 0 1310 nm VCSEL
+ 1 No Wavelength Control
+ 2 Uncooled Transmitter
+ 3 Pin Detector
+ 4 Transmitter Tunable
+Transmitter Technology
+ 0 1550 nm VCSEL
+ 1 No Wavelength Control
+ 2 Uncooled Transmitter
+ 3 APD Detector
+ 4 Transmitter Not Tunable
+Transmitter Technology
+ 0 1310 nm FP
+ 1 No Wavelength Control
+ 2 Uncooled Transmitter
+ 3 APD Detector
+ 4 Transmitter Tunable
+Transmitter Technology
+ 0 1310 nm DFB
+ 1 No Wavelength Control
+ 2 Cooled Transmitter
+ 3 Pin Detector
+ 4 Transmitter Not Tunable
+Transmitter Technology
+ 0 1550 nm DFB
+ 1 No Wavelength Control
+ 2 Cooled Transmitter
+ 3 Pin Detector
+ 4 Transmitter Tunable
+Transmitter Technology
+ 0 1310 nm EML
+ 1 No Wavelength Control
+ 2 Cooled Transmitter
+ 3 APD Detector
+ 4 Transmitter Not Tunable
+Transmitter Technology
+ 0 1550 nm EML
+ 1 No Wavelength Control
+ 2 Cooled Transmitter
+ 3 APD Detector
+ 4 Transmitter Tunable
+Transmitter Technology
+ 0 Other / Undefined
+ 1 Active Wavelength Control
+ 2 Uncooled Transmitter
+ 3 Pin Detector
+ 4 Transmitter Not Tunable
+Transmitter Technology
+ 0 1490 nm DFB
+ 1 Active Wavelength Control
+ 2 Uncooled Transmitter
+ 3 Pin Detector
+ 4 Transmitter Tunable
+Transmitter Technology
+ 0 Copper cable unequalized
+ 1 Active Wavelength Control
+ 2 Uncooled Transmitter
+ 3 APD Detector
+ 4 Transmitter Not Tunable
+Transmitter Technology
+ 0 Copper cable passive equalized
+ 1 Active Wavelength Control
+ 2 Uncooled Transmitter
+ 3 APD Detector
+ 4 Transmitter Tunable
+Transmitter Technology
+ 0 Copper cable, near and far end limiting active equalizers
+ 1 Active Wavelength Control
+ 2 Cooled Transmitter
+ 3 Pin Detector
+ 4 Transmitter Not Tunable
+Transmitter Technology
+ 0 Copper cable, far end limiting active equalizers
+ 1 Active Wavelength Control
+ 2 Cooled Transmitter
+ 3 Pin Detector
+ 4 Transmitter Tunable
+Transmitter Technology
+ 0 Copper cable, near end limiting active equalizers
+ 1 Active Wavelength Control
+ 2 Cooled Transmitter
+ 3 APD Detector
+ 4 Transmitter Not Tunable
+Transmitter Technology
+ 0 Copper cable, linear active equalizers
+ 1 Active Wavelength Control
+ 2 Cooled Transmitter
+ 3 APD Detector
+ 4 Transmitter Tunable
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8636_temp.c b/usr/src/test/util-tests/tests/libsff/libsff_8636_temp.c
new file mode 100644
index 0000000000..5e3ba70cd4
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8636_temp.c
@@ -0,0 +1,61 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print all case temperature values. Remember that 0 is special.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ uint_t i;
+ uint8_t buf[256];
+
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ for (i = 0; i < UINT8_MAX; i++) {
+ int ret;
+ nvlist_t *nvl;
+ char *val;
+
+ buf[SFF_8636_MAX_CASE_TEMP] = i;
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP connector "
+ "%d: %s\n", i, strerror(errno));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_MAX_CASE_TEMP,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find connector when "
+ "parsing key %d: %s\n", i, strerror(errno));
+ }
+
+ (void) puts(val);
+ nvlist_free(nvl);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_8636_temp.out b/usr/src/test/util-tests/tests/libsff/libsff_8636_temp.out
new file mode 100644
index 0000000000..f4838c433b
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_8636_temp.out
@@ -0,0 +1,255 @@
+70 C
+1 C
+2 C
+3 C
+4 C
+5 C
+6 C
+7 C
+8 C
+9 C
+10 C
+11 C
+12 C
+13 C
+14 C
+15 C
+16 C
+17 C
+18 C
+19 C
+20 C
+21 C
+22 C
+23 C
+24 C
+25 C
+26 C
+27 C
+28 C
+29 C
+30 C
+31 C
+32 C
+33 C
+34 C
+35 C
+36 C
+37 C
+38 C
+39 C
+40 C
+41 C
+42 C
+43 C
+44 C
+45 C
+46 C
+47 C
+48 C
+49 C
+50 C
+51 C
+52 C
+53 C
+54 C
+55 C
+56 C
+57 C
+58 C
+59 C
+60 C
+61 C
+62 C
+63 C
+64 C
+65 C
+66 C
+67 C
+68 C
+69 C
+70 C
+71 C
+72 C
+73 C
+74 C
+75 C
+76 C
+77 C
+78 C
+79 C
+80 C
+81 C
+82 C
+83 C
+84 C
+85 C
+86 C
+87 C
+88 C
+89 C
+90 C
+91 C
+92 C
+93 C
+94 C
+95 C
+96 C
+97 C
+98 C
+99 C
+100 C
+101 C
+102 C
+103 C
+104 C
+105 C
+106 C
+107 C
+108 C
+109 C
+110 C
+111 C
+112 C
+113 C
+114 C
+115 C
+116 C
+117 C
+118 C
+119 C
+120 C
+121 C
+122 C
+123 C
+124 C
+125 C
+126 C
+127 C
+128 C
+129 C
+130 C
+131 C
+132 C
+133 C
+134 C
+135 C
+136 C
+137 C
+138 C
+139 C
+140 C
+141 C
+142 C
+143 C
+144 C
+145 C
+146 C
+147 C
+148 C
+149 C
+150 C
+151 C
+152 C
+153 C
+154 C
+155 C
+156 C
+157 C
+158 C
+159 C
+160 C
+161 C
+162 C
+163 C
+164 C
+165 C
+166 C
+167 C
+168 C
+169 C
+170 C
+171 C
+172 C
+173 C
+174 C
+175 C
+176 C
+177 C
+178 C
+179 C
+180 C
+181 C
+182 C
+183 C
+184 C
+185 C
+186 C
+187 C
+188 C
+189 C
+190 C
+191 C
+192 C
+193 C
+194 C
+195 C
+196 C
+197 C
+198 C
+199 C
+200 C
+201 C
+202 C
+203 C
+204 C
+205 C
+206 C
+207 C
+208 C
+209 C
+210 C
+211 C
+212 C
+213 C
+214 C
+215 C
+216 C
+217 C
+218 C
+219 C
+220 C
+221 C
+222 C
+223 C
+224 C
+225 C
+226 C
+227 C
+228 C
+229 C
+230 C
+231 C
+232 C
+233 C
+234 C
+235 C
+236 C
+237 C
+238 C
+239 C
+240 C
+241 C
+242 C
+243 C
+244 C
+245 C
+246 C
+247 C
+248 C
+249 C
+250 C
+251 C
+252 C
+253 C
+254 C
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_br.c b/usr/src/test/util-tests/tests/libsff/libsff_br.c
new file mode 100644
index 0000000000..f0d3f25db2
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_br.c
@@ -0,0 +1,133 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print and tests SFF BR values.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ int ret;
+ uint8_t buf[256];
+ nvlist_t *nvl;
+ char *val;
+
+ /*
+ * SFF 8472 has two different modes of printing the bit rate. It has a
+ * nominal bit rate and then if 0xff is in that field it has a max and
+ * min.
+ */
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_BR_NOMINAL] = 0x42;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP compliance "
+ "values: %s\n", strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_BR_NOMINAL, &val)) !=
+ 0) {
+ errx(1, "TEST FAILED: failed to find %s: %s when "
+ "parsing key %d: %s\n", LIBSFF_KEY_BR_NOMINAL,
+ strerror(ret));
+ }
+ (void) printf("nominal: %s\n", val);
+
+ /*
+ * Make sure min, max are missing.
+ */
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_BR_MIN, &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value for key "
+ "%s: %d\n", LIBSFF_KEY_BR_MIN, ret);
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_BR_MAX, &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value for key "
+ "%s: %d\n", LIBSFF_KEY_BR_MAX, ret);
+ }
+ nvlist_free(nvl);
+
+ /*
+ * Now the opposite.
+ */
+ buf[SFF_8472_BR_NOMINAL] = 0xff;
+ buf[SFF_8472_BR_MAX] = 0x50;
+ buf[SFF_8472_BR_MIN] = 0x10;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP compliance "
+ "values: %s\n", strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_BR_MAX, &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find %s: %s when "
+ "parsing key %d: %s\n", LIBSFF_KEY_BR_MAX,
+ strerror(ret));
+ }
+ (void) printf("max: %s\n", val);
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_BR_MIN, &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find %s: %s when "
+ "parsing key %d: %s\n", LIBSFF_KEY_BR_MIN,
+ strerror(ret));
+ }
+ (void) printf("min: %s\n", val);
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_BR_NOMINAL, &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value for key "
+ "%s: %d\n", LIBSFF_KEY_BR_NOMINAL, ret);
+ }
+ nvlist_free(nvl);
+
+ /*
+ * Now for QSFP+
+ */
+ (void) puts("\n\nQSFP\n");
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ buf[SFF_8636_BR_NOMINAL] = 0x42;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP BR "
+ "values: %s\n", strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_BR_NOMINAL,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find %s: %s when "
+ "parsing key %d: %s\n", LIBSFF_KEY_BR_NOMINAL,
+ strerror(ret));
+ }
+ (void) printf("nominal: %s\n", val);
+
+ nvlist_free(nvl);
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_br.out b/usr/src/test/util-tests/tests/libsff/libsff_br.out
new file mode 100644
index 0000000000..3973fb58aa
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_br.out
@@ -0,0 +1,8 @@
+nominal: 6600 MBd
+max: 20000 MBd
+min: 4000 MBd
+
+
+QSFP
+
+nominal: 6600 Mbps
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_compliance.c b/usr/src/test/util-tests/tests/libsff/libsff_compliance.c
new file mode 100644
index 0000000000..9aeb611228
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_compliance.c
@@ -0,0 +1,124 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print and tests SFF compliance values.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+static void
+lsc_print_array(nvlist_t *nvl, const char *key)
+{
+ int ret;
+ uint_t i, count;
+ char **vals;
+
+ if ((ret = nvlist_lookup_string_array(nvl, key, &vals, &count)) != 0) {
+ errx(1, "TEST FAILED failed to find key %s: %s\n", key,
+ strerror(ret));
+ }
+
+ (void) puts(key);
+ for (i = 0; i < count; i++) {
+ (void) printf("\t%d\t%s\n", i, vals[i]);
+ }
+}
+
+int
+main(void)
+{
+ int ret;
+ uint8_t buf[256];
+ nvlist_t *nvl;
+
+ /*
+ * Set every shared bit for compliance then print them all out. Note we
+ * include reserved bits so that way if someone ends up adding something
+ * to one of the reserved fields, we end up printing it.
+ */
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_COMPLIANCE_10GE] = 0xff;
+ buf[SFF_8472_COMPLIANCE_SONET_LOW] = 0xff;
+ buf[SFF_8472_COMPLIANCE_SONET_HIGH] = 0xff;
+ buf[SFF_8472_COMPLIANCE_ETHERNET] = 0xff;
+ buf[SFF_8472_COMPLIANCE_FC_LOW] = 0xff;
+ buf[SFF_8472_COMPLIANCE_FC_HIGH] = 0xff;
+ buf[SFF_8472_COMPLIANCE_FC_MEDIA] = 0xff;
+ buf[SFF_8472_COMPLIANCE_FC_SPEED] = 0xff;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP compliance "
+ "values: %s\n", strerror(ret));
+ }
+
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_10GBE);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_IB);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_ESCON);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_SONET);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_GBE);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_FC_LEN);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_FC_TECH);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_SFP);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_FC_MEDIA);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_FC_SPEED);
+
+ nvlist_free(nvl);
+
+ /*
+ * Now for QSFP+
+ */
+ (void) puts("\n\nQSFP\n");
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ buf[SFF_8636_COMPLIANCE_10GBEP] = 0xff;
+ buf[SFF_8636_COMPLIANCE_SONET] = 0xff;
+ buf[SFF_8636_COMPLIANCE_SAS] = 0xff;
+ buf[SFF_8636_COMPLIANCE_ETHERNET] = 0xff;
+ buf[SFF_8636_COMPLIANCE_FCLEN] = 0xff;
+ buf[SFF_8636_COMPLIANCE_FC_LOW] = 0xff;
+ buf[SFF_8636_COMPLIANCE_FC_HIGH] = 0xff;
+ buf[SFF_8636_COMPLIANCE_FC_MEDIA] = 0xff;
+ buf[SFF_8636_COMPLIANCE_FC_SPEED] = 0xff;
+ buf[SFF_8636_EXTENDED_MODULE] = 0xff;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP compliance "
+ "values: %s\n", strerror(ret));
+ }
+
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_10GBE);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_SONET);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_SAS);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_GBE);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_FC_LEN);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_FC_TECH);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_FC_MEDIA);
+ lsc_print_array(nvl, LIBSFF_KEY_COMPLIANCE_FC_SPEED);
+ lsc_print_array(nvl, LIBSFF_KEY_EXT_MOD_CODES);
+
+ nvlist_free(nvl);
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_compliance.out b/usr/src/test/util-tests/tests/libsff/libsff_compliance.out
new file mode 100644
index 0000000000..6f6ec66c44
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_compliance.out
@@ -0,0 +1,130 @@
+10G+ Ethernet Compliance Codes
+ 0 10G Base-SR
+ 1 10G Base-LR
+ 2 10G Base-LRM
+ 3 10G Base-ER
+Infiniband Compliance Codes
+ 0 1X Copper Passive
+ 1 1X Copper Active
+ 2 1X LX
+ 3 1X SX
+ESCON Compliance Codes
+ 0 ESCON SMF, 1310nm Laser
+ 1 ESCON MMF, 1310nm LED
+SONET Compliance Codes
+ 0 OC-48, short reach
+ 1 OC-48, intermediate reach
+ 2 OC-48, long reach
+ 3 ONET reach specifier bit 2
+ 4 SONET reach specifier bit 1
+ 5 OC-192, short reach
+ 6 OC-3, short reach
+ 7 OC-3, single mode, inter. reach
+ 8 OC-3, single mode, long reach
+ 9 OC-12, short reach
+ 10 OC-12, single mode, inter. reach
+ 11 OC-12, single mode, long reach
+Ethernet Compliance Codes
+ 0 1000BASE-SX
+ 1 1000BASE-LX
+ 2 1000BASE-CX
+ 3 1000BASE-T
+ 4 100BASE-LX/LX10
+ 5 100BASE-FX
+ 6 BASE-BX10
+ 7 BASE-PX
+Fibre Channel Link Lengths
+ 0 medium distance (M)
+ 1 long distance (L)
+ 2 intermeddiate distance (I)
+ 3 short distance (S)
+ 4 very long distance (V)
+Fibre Channel Technology
+ 0 Electrical inter-enclosure (EL)
+ 1 Longwave laser (LC)
+ 2 Shortwave laser, linear Rx (SA)
+ 3 Longwave laser (LL)
+ 4 Shortwave laser with OFC (SL)
+ 5 Shortwave laser w/o OFC (SN)
+ 6 Electrical intra-enclosure (EL)
+SFP+ Cable Technology
+ 0 Passive Cable
+ 1 Active Cable
+Fibre Channel Transmission Media
+ 0 Single Mode (SM)
+ 1 Multimode, 50um (M5, M5E)
+ 2 Multimode, 62.5um (M6)
+ 3 Video Coax (TV)
+ 4 Miniature Coax (MI)
+ 5 Twisted Pair (TP)
+ 6 Twin Axial Pair (TW)
+Fibre Channel Speed
+ 0 100 MBytes/sec
+ 1 200 MBytes/sec
+ 2 3200 MBytes/sec
+ 3 400 MBytes/sec
+ 4 1600 MBytes/sec
+ 5 800 MBytes/sec
+ 6 1200 MBytes/sec
+
+
+QSFP
+
+10G+ Ethernet Compliance Codes
+ 0 40G Active Cable (XLPPI)
+ 1 40GBASE-LR4
+ 2 40GBASE-SR4
+ 3 40GBASE-CR4
+ 4 10GBASE-SR
+ 5 10GBASE-LR
+ 6 10GBASE-LRM
+SONET Compliance Codes
+ 0 OC 48 short reach
+ 1 OC 48, intermediate reach
+ 2 OC 48, long reach
+SAS Compliance Codes
+ 0 SAS 3.0 Gb/s
+ 1 SAS 6.0 Gb/s
+ 2 SAS 12.0 Gb/s
+ 3 SAS 24.0 Gb/s
+Ethernet Compliance Codes
+ 0 1000BASE-SX
+ 1 1000BASE-LX
+ 2 1000BASE-CX
+ 3 1000BASE-T
+Fibre Channel Link Lengths
+ 0 medium distance (M)
+ 1 long distance (L)
+ 2 intermeddiate distance (I)
+ 3 short distance (S)
+ 4 very long distance (V)
+Fibre Channel Technology
+ 0 Electrical inter-enclosure (EL)
+ 1 Longwave laser (LC)
+ 2 Longwave laser (LL)
+ 3 Shortwave laser with OFC (SL)
+ 4 Shortwave laser w/o OFC (SN)
+ 5 Electrical intra-enclosure (EL)
+Fibre Channel Transmission Media
+ 0 Single Mode (SM)
+ 1 Multimode, 50um (OM3)
+ 2 Multimode, 50m (M5)
+ 3 Multimode, 62.5um (M6)
+ 4 Video Coax (TV)
+ 5 Miniature Coax (MI)
+ 6 Twisted Pair (TP)
+ 7 Twin Axial Pair (TW)
+Fibre Channel Speed
+ 0 100 MBytes/sec
+ 1 200 MBytes/sec
+ 2 3200 MBytes/sec
+ 3 400 MBytes/sec
+ 4 1600 MBytes/sec
+ 5 800 MBytes/sec
+ 6 1200 MBytes/sec
+Extended Module Codes
+ 0 SDR
+ 1 DDR
+ 2 QDR
+ 3 FDR
+ 4 EDR
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_conn.c b/usr/src/test/util-tests/tests/libsff/libsff_conn.c
new file mode 100644
index 0000000000..778eaf1105
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_conn.c
@@ -0,0 +1,89 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print all SFF Connector values
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ uint_t i;
+ uint8_t buf[256];
+
+ bzero(buf, sizeof (buf));
+ for (i = 0; i < UINT8_MAX; i++) {
+ int ret;
+ nvlist_t *nvl;
+ char *val;
+
+ buf[SFF_8472_CONNECTOR] = i;
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP connector "
+ "%d: %s\n", i, strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_CONNECTOR,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find key %s with "
+ "value %d: %s", LIBSFF_KEY_CONNECTOR, i,
+ strerror(ret));
+ }
+
+ (void) puts(val);
+ nvlist_free(nvl);
+ }
+
+ /*
+ * Now for QSFP+
+ */
+ (void) puts("\n\nQSFP\n");
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ for (i = 0; i < UINT8_MAX; i++) {
+ int ret;
+ nvlist_t *nvl;
+ char *val;
+
+ buf[SFF_8636_CONNECTOR] = i;
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP connector "
+ "%d: %s\n", i, strerror(errno));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_CONNECTOR,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find key %s with "
+ "value %d: %s", LIBSFF_KEY_CONNECTOR, i,
+ strerror(ret));
+ }
+
+ (void) puts(val);
+ nvlist_free(nvl);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_conn.out b/usr/src/test/util-tests/tests/libsff/libsff_conn.out
new file mode 100644
index 0000000000..68dfb0e4d5
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_conn.out
@@ -0,0 +1,514 @@
+Unknown
+SC (Subscriber Connector)
+Fibre Channel Style 1 copper connector
+Fibre Channel Style 2 copper connector
+BNC/TNC (Bayonet/Threaded Neill-Concelman)
+Fibre Channel coax headers
+Fiber Jack
+LC (Lucent Connector)
+MT-RJ (Mechanical Transfer - Registered Jack)
+MU (Multiple Optical)
+SG
+Optical Pigtail
+MPO 1x12 (Multifiber Parallel Optic)
+MPO 2x16
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+HSSDC II (High Speed Serial Data Connector)
+Copper pigtail
+RJ45 (Registered Jack)
+No separable connector
+MXC 2x16
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+
+
+QSFP
+
+Unknown
+SC (Subscriber Connector)
+Fibre Channel Style 1 copper connector
+Fibre Channel Style 2 copper connector
+BNC/TNC (Bayonet/Threaded Neill-Concelman)
+Fibre Channel coax headers
+Fiber Jack
+LC (Lucent Connector)
+MT-RJ (Mechanical Transfer - Registered Jack)
+MU (Multiple Optical)
+SG
+Optical Pigtail
+MPO 1x12 (Multifiber Parallel Optic)
+MPO 2x16
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+HSSDC II (High Speed Serial Data Connector)
+Copper pigtail
+RJ45 (Registered Jack)
+No separable connector
+MXC 2x16
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_efault.c b/usr/src/test/util-tests/tests/libsff/libsff_efault.c
new file mode 100644
index 0000000000..7f57529d81
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_efault.c
@@ -0,0 +1,54 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Test various error cases all of which should return EFAULT.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int
+main(void)
+{
+ int ret;
+ void *addr;
+ nvlist_t *nvl;
+ size_t len = getpagesize();
+
+ /*
+ * Get an unreadable page
+ */
+ if ((addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE | MAP_ANON, -1,
+ 0)) == MAP_FAILED) {
+ err(1, "TEST FAILED: failed to mmap private page");
+ }
+
+ if (mprotect(addr, len, PROT_NONE) != 0) {
+ err(1, "TEST FAILED: failed to protect private page");
+ }
+
+ if ((ret = libsff_parse(addr, 128, 0xa0, &nvl)) != EFAULT) {
+ errx(1, "TEST FAILED: failed to return EFAULT on bad"
+ "data buffer\n");
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_einval.c b/usr/src/test/util-tests/tests/libsff/libsff_einval.c
new file mode 100644
index 0000000000..ac835f95e7
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_einval.c
@@ -0,0 +1,88 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Test various error cases all of which should return EINVAL.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+#include "sff.h"
+
+int
+main(void)
+{
+ int ret;
+ uint8_t buf[256];
+ nvlist_t *nvl;
+
+ bzero(buf, sizeof (buf));
+ if ((ret = libsff_parse(NULL, sizeof (buf), 0xa0, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on NULL buffer");
+ }
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, NULL)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on NULL nvl");
+ }
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa1, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad page");
+ }
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad page");
+ }
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xff, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad page");
+ }
+
+ if ((ret = libsff_parse(buf, 0, 0xa0, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad 8476 "
+ "size");
+ }
+
+ if ((ret = libsff_parse(buf, 50, 0xa0, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad 8476 "
+ "size");
+ }
+
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ if ((ret = libsff_parse(buf, 0, 0xa0, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad 8476 "
+ "size");
+ }
+
+ if ((ret = libsff_parse(buf, 50, 0xa0, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad 8476 "
+ "size");
+ }
+
+ if ((ret = libsff_parse(buf, 96, 0xa0, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad 8635 "
+ "size");
+ }
+
+ if ((ret = libsff_parse(buf, 128, 0xa0, &nvl)) != EINVAL) {
+ errx(1, "TEST FAILED: failed to return EINVAL on bad 8635 "
+ "size");
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_enc.c b/usr/src/test/util-tests/tests/libsff/libsff_enc.c
new file mode 100644
index 0000000000..77ac12b2ed
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_enc.c
@@ -0,0 +1,89 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print all SFF Encoding values
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ uint_t i;
+ uint8_t buf[256];
+
+ bzero(buf, sizeof (buf));
+ for (i = 0; i < UINT8_MAX; i++) {
+ int ret;
+ nvlist_t *nvl;
+ char *val;
+
+ buf[SFF_8472_ENCODING] = i;
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP connector "
+ "%d: %s\n", i, strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_ENCODING,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find key %s with "
+ "value %d: %s", LIBSFF_KEY_ENCODING, i,
+ strerror(ret));
+ }
+
+ (void) puts(val);
+ nvlist_free(nvl);
+ }
+
+ /*
+ * Now for QSFP+
+ */
+ (void) puts("\n\nQSFP\n");
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ for (i = 0; i < UINT8_MAX; i++) {
+ int ret;
+ nvlist_t *nvl;
+ char *val;
+
+ buf[SFF_8636_ENCODING] = i;
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP connector "
+ "%d: %s\n", i, strerror(errno));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_ENCODING,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find key %s with "
+ "value %d: %s", LIBSFF_KEY_ENCODING, i,
+ strerror(ret));
+ }
+
+ (void) puts(val);
+ nvlist_free(nvl);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_enc.out b/usr/src/test/util-tests/tests/libsff/libsff_enc.out
new file mode 100644
index 0000000000..b31a565e4e
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_enc.out
@@ -0,0 +1,514 @@
+Unspecified
+8B/10B
+4B/5B
+NRZ
+Manchester
+SONET Scrambled
+64B/66B
+256B/257B
+PAM4
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+
+
+QSFP
+
+Unspecified
+8B/10B
+4B/5B
+NRZ
+SONET Scrambled
+64B/66B
+Manchester
+256B/257B
+PAM4
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_ident.c b/usr/src/test/util-tests/tests/libsff/libsff_ident.c
new file mode 100644
index 0000000000..0065a6d0e0
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_ident.c
@@ -0,0 +1,60 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print all SFF Identifier values
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ uint_t i;
+ uint8_t buf[256];
+
+ bzero(buf, sizeof (buf));
+ for (i = 0; i < UINT8_MAX; i++) {
+ int ret;
+ nvlist_t *nvl;
+ char *val;
+
+ buf[SFF_8472_IDENTIFIER] = i;
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse identifier "
+ "%d: %s\n", i, strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_IDENTIFIER,
+ &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find key %s with "
+ "value %d: %s", LIBSFF_KEY_IDENTIFIER, i,
+ strerror(ret));
+ }
+
+ (void) puts(val);
+ nvlist_free(nvl);
+ }
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_ident.out b/usr/src/test/util-tests/tests/libsff/libsff_ident.out
new file mode 100644
index 0000000000..360c5966f8
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_ident.out
@@ -0,0 +1,255 @@
+Unknown or Unspecified
+GBIC
+Module/connector soldered to motherboard
+SFP/SFP+/SFP28
+300 pin XBI
+XENPAK
+XFP
+XFF
+XFP-E
+XPAK
+X2
+DWDM-SFP/SFP+ (not using SFF-8472)
+QSFP
+QSFP+ or later
+CXP or later
+Shielded Mini Multilane HD 4X
+Shielded Mini Multilane HD 8X
+QSFP28 or later
+CXP2 (aka CXP28) or later
+CDFP (Style 1/Style2)
+Shielded Mini Multilane HD 4X Fanout Cable
+Shielded Mini Multilane HD 8X Fanout Cable
+CDFP (Style 3)
+microQSFP
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Reserved
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
+Vendor Specific
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_lengths.c b/usr/src/test/util-tests/tests/libsff/libsff_lengths.c
new file mode 100644
index 0000000000..f77b29a397
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_lengths.c
@@ -0,0 +1,133 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print and tests SFF length values.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ int ret;
+ uint8_t buf[256];
+ nvlist_t *nvl;
+ char *val;
+ uint_t i;
+ const char *lengths_8472[] = { LIBSFF_KEY_LENGTH_SMF_KM,
+ LIBSFF_KEY_LENGTH_SMF, LIBSFF_KEY_LENGTH_OM2, LIBSFF_KEY_LENGTH_OM1,
+ LIBSFF_KEY_LENGTH_COPPER, LIBSFF_KEY_LENGTH_OM3, NULL };
+ const char *lengths_8636[] = { LIBSFF_KEY_LENGTH_SMF_KM,
+ LIBSFF_KEY_LENGTH_OM2, LIBSFF_KEY_LENGTH_OM1,
+ LIBSFF_KEY_LENGTH_COPPER, LIBSFF_KEY_LENGTH_OM3, NULL };
+
+ /*
+ * Make sure if lengths are zero that they don't show up.
+ */
+ bzero(buf, sizeof (buf));
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP length "
+ "values: %s\n", strerror(ret));
+ }
+
+ for (i = 0; lengths_8472[i] != NULL; i++) {
+ if ((ret = nvlist_lookup_string(nvl, lengths_8472[i], &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value "
+ "for key %s: %d\n", lengths_8472[i], ret);
+ }
+ }
+
+ nvlist_free(nvl);
+
+ buf[SFF_8472_LENGTH_SMF_KM] = 0x23;
+ buf[SFF_8472_LENGTH_SMF] = 0x24;
+ buf[SFF_8472_LENGTH_50UM] = 0x25;
+ buf[SFF_8472_LENGTH_62UM] = 0x26;
+ buf[SFF_8472_LENGTH_COPPER] = 0x27;
+ buf[SFF_8472_LENGTH_OM3] = 0x28;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP length "
+ "values: %s\n", strerror(ret));
+ }
+
+ for (i = 0; lengths_8472[i] != NULL; i++) {
+ if ((ret = nvlist_lookup_string(nvl, lengths_8472[i], &val)) !=
+ 0) {
+ errx(1, "TEST FALIED: failed to find length for key "
+ "%s: %d\n", lengths_8472[i], ret);
+ }
+ (void) printf("%s: %s\n", lengths_8472[i], val);
+ }
+
+ nvlist_free(nvl);
+
+ /*
+ * Now for QSFP+
+ */
+ (void) puts("\n\nQSFP\n");
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP length "
+ "values: %s\n", strerror(ret));
+ }
+
+ for (i = 0; lengths_8472[i] != NULL; i++) {
+ if ((ret = nvlist_lookup_string(nvl, lengths_8472[i], &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value "
+ "for key %s: %d\n", lengths_8472[i], ret);
+ }
+ }
+
+ nvlist_free(nvl);
+
+ buf[SFF_8636_LENGTH_SMF] = 0x23;
+ buf[SFF_8636_LENGTH_OM3] = 0x24;
+ buf[SFF_8636_LENGTH_OM2] = 0x25;
+ buf[SFF_8636_LENGTH_OM1] = 0x26;
+ buf[SFF_8636_LENGTH_COPPER] = 0x27;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP length "
+ "values: %s\n", strerror(ret));
+ }
+
+ for (i = 0; lengths_8636[i] != NULL; i++) {
+ if ((ret = nvlist_lookup_string(nvl, lengths_8636[i], &val)) !=
+ 0) {
+ errx(1, "TEST FALIED: failed to find length for key "
+ "%s: %d\n", lengths_8472[i], ret);
+ }
+ (void) printf("%s: %s\n", lengths_8636[i], val);
+ }
+
+ nvlist_free(nvl);
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_lengths.out b/usr/src/test/util-tests/tests/libsff/libsff_lengths.out
new file mode 100644
index 0000000000..9be9d76804
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_lengths.out
@@ -0,0 +1,15 @@
+Length SMF (km): 35 km
+Length SMF (m): 3600 m
+Length 50um OM2: 370 m
+Length 62.5um OM1: 380 m
+Length Copper: 39 m
+Length OM3: 400 m
+
+
+QSFP
+
+Length SMF (km): 35 km
+Length 50um OM2: 37 m
+Length 62.5um OM1: 38 m
+Length Copper: 39 m
+Length OM3: 72 m
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_opts.c b/usr/src/test/util-tests/tests/libsff/libsff_opts.c
new file mode 100644
index 0000000000..e91365dc6a
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_opts.c
@@ -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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print and tests SFF options values.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+static void
+lso_print_array(nvlist_t *nvl, const char *key)
+{
+ int ret;
+ uint_t i, count;
+ char **vals;
+
+ if ((ret = nvlist_lookup_string_array(nvl, key, &vals, &count)) != 0) {
+ errx(1, "TEST FAILED failed to find key %s: %s\n", key,
+ strerror(ret));
+ }
+
+ (void) puts(key);
+ for (i = 0; i < count; i++) {
+ (void) printf("\t%d\t%s\n", i, vals[i]);
+ }
+}
+
+int
+main(void)
+{
+ int ret;
+ uint8_t buf[256];
+ nvlist_t *nvl;
+
+ /*
+ * Set every shared bit for options then print them all out. Note we
+ * include reserved bits so that way if someone ends up adding something
+ * to one of the reserved fields, we end up printing it.
+ */
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_OPTIONS_HI] = 0xff;
+ buf[SFF_8472_OPTIONS_LOW] = 0xff;
+ buf[SFF_8472_ENHANCED_OPTIONS] = 0xff;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP options "
+ "values: %s\n", strerror(ret));
+ }
+
+ lso_print_array(nvl, LIBSFF_KEY_OPTIONS);
+ lso_print_array(nvl, LIBSFF_KEY_EXTENDED_OPTIONS);
+
+ nvlist_free(nvl);
+
+ /*
+ * Now for QSFP+
+ */
+ (void) puts("\n\nQSFP\n");
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ buf[SFF_8636_OPTIONS_HI] = 0xff;
+ buf[SFF_8636_OPTIONS_MID] = 0xff;
+ buf[SFF_8636_OPTIONS_LOW] = 0xff;
+ buf[SFF_8636_ENHANCED_OPTIONS] = 0xff;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP options "
+ "values: %s\n", strerror(ret));
+ }
+
+ lso_print_array(nvl, LIBSFF_KEY_OPTIONS);
+ lso_print_array(nvl, LIBSFF_KEY_ENHANCED_OPTIONS);
+
+ nvlist_free(nvl);
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_opts.out b/usr/src/test/util-tests/tests/libsff/libsff_opts.out
new file mode 100644
index 0000000000..870ea735ac
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_opts.out
@@ -0,0 +1,50 @@
+Options
+ 0 Rx_LOS implemented
+ 1 Rx_LOS inverted
+ 2 TX_FAULT implemented
+ 3 TX_DISABLE implemented
+ 4 RATE_SELECT implemented
+ 5 Tunable transmitter
+ 6 Receiver decision threshold implemented
+ 7 Linear Receiver Output Implemented
+ 8 Power Level 2 Requirement
+ 9 Cooled Transceiver Implemented
+ 10 Retimer or CDR implemented
+ 11 Paging Implemented
+ 12 Power Level 3 Requirement
+Extended Options
+ 0 Soft Rate Select Control Implemented
+ 1 Application Select implemented
+ 2 Soft RATE_SELECT implemented
+ 3 Soft RX_LOS implemented
+ 4 Soft TX_FAULT implemented
+ 5 Soft TX_DISABLE implemented
+ 6 Alarm/Warning flags implemented
+
+
+QSFP
+
+Options
+ 0 Tx Loss of Signal implemented
+ 1 Tx Squelch for Pave
+ 2 Tx_FAULT implemented
+ 3 Tx_DISABLE implemented
+ 4 Rate Select implemented
+ 5 Memory page 01h provided
+ 6 Memory page 02h provided
+ 7 Tx Squelch implemented
+ 8 Tx Squelch Disable implemented
+ 9 Rx Output Disable capable
+ 10 Rx Squelch Disable implemented
+ 11 Rx CDR Loss of Lock Flag implemented
+ 12 Tx CDR Loss of Lock Flag implemented
+ 13 RX CDR On/Off Control implemented
+ 14 TX CDR On/Off Control implemented
+ 15 RX Output Amplitude Fixed Programmable Settings
+ 16 RX Output Emphasis Fixed Programmable Settings
+ 17 TX Input Equalization Fixed Programmable
+ 18 TX Input Equalization Auto Adaptive Capable
+Enhanced Options
+ 0 Application Select Table Supported
+ 1 Extended Rate Selection Supported
+ 2 Initialization Complete Flag Implemented
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_strings.c b/usr/src/test/util-tests/tests/libsff/libsff_strings.c
new file mode 100644
index 0000000000..a9d5a2252f
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_strings.c
@@ -0,0 +1,133 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Test our ability to parse SFF string values which are space encoded. As this
+ * is shared between the SFP and QSFP logic, we end up only testing the SFP
+ * based data.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff. Strings are
+ * described as having spaces at the end of them. We mostly want to make sure
+ * that if we have strings without spaces that we parse them sanely as well as
+ * test what happens with embedded spaces and NUL characters.
+ */
+#include "sff.h"
+
+typedef struct {
+ uint8_t lss_bytes[16];
+ const char *lss_parsed;
+} lsfs_string_pair_t;
+
+static const lsfs_string_pair_t lsfs_bad_vals[] = {
+ /* All NULs */
+ { { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' },
+ "" },
+ /* Embedded NULs */
+ { { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+ '\0', 'a', 'a', 'a', 'a', 'a', 'a', 'a' },
+ "" },
+ /* Non-ASCII */
+ { { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+ 156, 'a', 'a', 'a', 'a', 'a', 'a', 'a' },
+ "" },
+ /* All padding */
+ { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },
+ "" }
+};
+#define NBAD (sizeof (lsfs_bad_vals) / sizeof (lsfs_string_pair_t))
+
+static const lsfs_string_pair_t lsfs_good_vals[] = {
+ /* Basic Name */
+ { { 'f', 'i', 'n', 'g', 'o', 'l', 'f', 'i',
+ 'n', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },
+ "fingolfin" },
+ /* Non-padding Space */
+ { { 'G', 'l', 'o', 'b', 'e', 'x', ' ', 'C',
+ 'o', 'r', 'p', ' ', ' ', ' ', ' ', ' ' },
+ "Globex Corp" },
+ /* 1-character name to catch off by one */
+ { { '~', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },
+ "~" },
+ /* Use all characters */
+ { { '!', '!', '!', '!', '!', '!', '!', '!',
+ '!', '!', '!', '!', '!', '!', '!', '!' },
+ "!!!!!!!!!!!!!!!!" }
+};
+#define NGOOD (sizeof (lsfs_good_vals) / sizeof (lsfs_string_pair_t))
+
+int
+main(void)
+{
+ int ret, i;
+ uint8_t buf[256];
+ nvlist_t *nvl;
+ char *val;
+
+ for (i = 0; i < NBAD; i++) {
+ bzero(buf, sizeof (buf));
+ bcopy(lsfs_bad_vals[i].lss_bytes, &buf[SFF_8472_VENDOR],
+ SFF_8472_VENDOR_LEN);
+
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP bad string "
+ "case %d: %s\n", i, strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_VENDOR,
+ &val)) != ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value "
+ "for %s: %d\n", LIBSFF_KEY_VENDOR, ret);
+ }
+ nvlist_free(nvl);
+ }
+
+ for (i = 0; i < NGOOD; i++) {
+ bzero(buf, sizeof (buf));
+ bcopy(lsfs_good_vals[i].lss_bytes, &buf[SFF_8472_VENDOR],
+ SFF_8472_VENDOR_LEN);
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP good string "
+ "case %d: %s\n", i, strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_VENDOR,
+ &val)) != 0) {
+ errx(1, "TEST FALIED: failed to find expected key "
+ "%s: %d", LIBSFF_KEY_VENDOR, ret);
+ }
+
+ if (strcmp(val, lsfs_good_vals[i].lss_parsed) != 0) {
+ errx(1, "TEST FAILED: expected string %s, found %s\n",
+ lsfs_good_vals[i].lss_parsed, val);
+ }
+
+ nvlist_free(nvl);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_wave.c b/usr/src/test/util-tests/tests/libsff/libsff_wave.c
new file mode 100644
index 0000000000..08b3637e06
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_wave.c
@@ -0,0 +1,177 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Print and tests SFF Wavelength values. Note that in both SFF 8472 and SFF
+ * 8636 the wavelength values also double for various copper complaince values.
+ * We check both forms here. Note that the copper compliance in SFF 8472 is
+ * currently tested in libsff_compliance.c. SFF 8636's Copper Attenuation values
+ * are tested here.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <err.h>
+#include <libsff.h>
+
+/*
+ * Pick up private sff header file with offsets from lib/libsff.
+ */
+#include "sff.h"
+
+int
+main(void)
+{
+ int ret, i;
+ uint8_t buf[256];
+ nvlist_t *nvl;
+ char *val;
+ char *attenuate[] = { LIBSFF_KEY_ATTENUATE_2G, LIBSFF_KEY_ATTENUATE_5G,
+ LIBSFF_KEY_ATTENUATE_7G, LIBSFF_KEY_ATTENUATE_12G, NULL };
+ char *wave[] = { LIBSFF_KEY_WAVELENGTH, LIBSFF_KEY_WAVE_TOLERANCE,
+ NULL };
+
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_WAVELENGTH_HI] = 0x12;
+ buf[SFF_8472_WAVELENGTH_LOW] = 0x34;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP wavelength "
+ "values: %s\n", strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_WAVELENGTH, &val)) !=
+ 0) {
+ errx(1, "TEST FAILED: failed to find %s: %s when "
+ "parsing key %d: %s\n", LIBSFF_KEY_WAVELENGTH,
+ strerror(ret));
+ }
+ (void) printf("%s: %s\n", LIBSFF_KEY_WAVELENGTH, val);
+ nvlist_free(nvl);
+
+ /*
+ * Make sure wavelength is missing if we specify a copper compliance.
+ */
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_COMPLIANCE_SFP] = 0x08;
+ buf[SFF_8472_WAVELENGTH_HI] = 0x12;
+ buf[SFF_8472_WAVELENGTH_LOW] = 0x34;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP wavelength "
+ "values: %s\n", strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_WAVELENGTH, &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value for key "
+ "%s: %d\n", LIBSFF_KEY_WAVELENGTH, ret);
+ }
+
+ nvlist_free(nvl);
+
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_COMPLIANCE_SFP] = 0x04;
+ buf[SFF_8472_WAVELENGTH_HI] = 0x12;
+ buf[SFF_8472_WAVELENGTH_LOW] = 0x34;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse SFP wavelength "
+ "values: %s\n", strerror(ret));
+ }
+
+ if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_WAVELENGTH, &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value for key "
+ "%s: %d\n", LIBSFF_KEY_WAVELENGTH, ret);
+ }
+
+ nvlist_free(nvl);
+
+ /*
+ * Now for QSFP+
+ */
+ (void) puts("\n\nQSFP\n");
+
+ /* First copper */
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+ buf[SFF_8636_DEVICE_TECH] = 0xa0;
+
+ buf[SFF_8636_ATTENUATE_2G] = 0x42;
+ buf[SFF_8636_ATTENUATE_5G] = 0x43;
+ buf[SFF_8636_ATTENUATE_7G] = 0x44;
+ buf[SFF_8636_ATTENUATE_12G] = 0x45;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP BR "
+ "values: %s\n", strerror(ret));
+ }
+
+ for (i = 0; attenuate[i] != NULL; i++) {
+ if ((ret = nvlist_lookup_string(nvl, attenuate[i], &val)) !=
+ 0) {
+ errx(1, "TEST FAILED: failed to find %s: %s when "
+ "parsing key %d: %s\n", attenuate[i],
+ strerror(ret));
+ }
+ (void) printf("%s: %s\n", attenuate[i], val);
+ }
+
+ for (i = 0; wave[i] != NULL; i++) {
+ if ((ret = nvlist_lookup_string(nvl, wave[i], &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value "
+ "for key %s: %d\n", attenuate[i], ret);
+ }
+
+ }
+ nvlist_free(nvl);
+
+ /* Now normal wavelengths */
+ bzero(buf, sizeof (buf));
+ buf[SFF_8472_IDENTIFIER] = SFF_8024_ID_QSFP;
+
+ buf[SFF_8636_WAVELENGTH_NOMINAL_HI] = 0x12;
+ buf[SFF_8636_WAVELENGTH_NOMINAL_LOW] = 0x34;
+ buf[SFF_8636_WAVELENGTH_TOLERANCE_HI] = 0x56;
+ buf[SFF_8636_WAVELENGTH_TOLERANCE_LOW] = 0x78;
+
+ if ((ret = libsff_parse(buf, sizeof (buf), 0xa0, &nvl)) != 0) {
+ errx(1, "TEST FAILED: failed to parse QSFP Wavelength "
+ "values: %s\n", strerror(ret));
+ }
+
+ for (i = 0; wave[i] != NULL; i++) {
+ if ((ret = nvlist_lookup_string(nvl, wave[i], &val)) != 0) {
+ errx(1, "TEST FAILED: failed to find %s: %s when "
+ "parsing key %d: %s\n", wave[i], strerror(ret));
+ }
+ (void) printf("%s: %s\n", wave[i], val);
+ }
+
+ for (i = 0; attenuate[i] != NULL; i++) {
+ if ((ret = nvlist_lookup_string(nvl, attenuate[i], &val)) !=
+ ENOENT) {
+ errx(1, "TEST FALIED: found unexpected return value "
+ "for key %s: %d\n", attenuate[i], ret);
+ }
+
+ }
+ nvlist_free(nvl);
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/libsff/libsff_wave.out b/usr/src/test/util-tests/tests/libsff/libsff_wave.out
new file mode 100644
index 0000000000..190e296478
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/libsff_wave.out
@@ -0,0 +1,11 @@
+Laser Wavelength: 4660 nm
+
+
+QSFP
+
+Cable Attenuation at 2.5 GHz: 66 dB
+Cable Attenuation at 5.0 GHz: 67 dB
+Cable Attenuation at 7.0 GHz: 68 dB
+Cable Attenuation at 12.9 GHz: 69 dB
+Laser Wavelength: 233.000 nm
+Wavelength Tolerance: 1106.800 nm
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index e386a974a6..949a115588 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -25,7 +25,7 @@
# Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
# Copyright 2016 Garrett D'Amore <garrett@damore.org>
-# Copyright 2016 Joyent, Inc.
+# Copyright (c) 2017, Joyent, Inc.
# Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
#
@@ -2029,7 +2029,8 @@ IPRB_OBJS = iprb.o
# illumos-written source files
IXGBE_OBJS = ixgbe_buf.o ixgbe_debug.o ixgbe_gld.o ixgbe_log.o ixgbe_main.o \
- ixgbe_osdep.o ixgbe_rx.o ixgbe_stat.o ixgbe_tx.o
+ ixgbe_osdep.o ixgbe_rx.o ixgbe_stat.o ixgbe_transceiver.o \
+ ixgbe_tx.o
# Intel-written source files
IXGBE_INTC_OBJS = ixgbe_82598.o ixgbe_82599.o ixgbe_api.o ixgbe_common.o \
diff --git a/usr/src/uts/common/io/dld/dld_drv.c b/usr/src/uts/common/io/dld/dld_drv.c
index 40cbe86170..a2ca4cfb96 100644
--- a/usr/src/uts/common/io/dld/dld_drv.c
+++ b/usr/src/uts/common/io/dld/dld_drv.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
/*
@@ -1309,6 +1310,103 @@ drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred,
return (0);
}
+/* ARGSUSED */
+static int
+drv_ioc_gettran(void *karg, intptr_t arg, int mode, cred_t *cred,
+ int *rvalp)
+{
+ int ret = 0;
+ mac_perim_handle_t mph = NULL;
+ dls_dl_handle_t dlh = NULL;
+ dls_link_t *dlp = NULL;
+ dld_ioc_gettran_t *dgt = karg;
+
+ if ((ret = mac_perim_enter_by_linkid(dgt->dgt_linkid, &mph)) != 0)
+ goto done;
+
+ if ((ret = dls_devnet_hold_link(dgt->dgt_linkid, &dlh, &dlp)) != 0)
+ goto done;
+
+ /*
+ * Make sure that this link belongs to the zone.
+ */
+ if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (dgt->dgt_tran_id == DLDIOC_GETTRAN_GETNTRAN) {
+ ret = mac_transceiver_count(dlp->dl_mh, &dgt->dgt_tran_id);
+ } else {
+ ret = mac_transceiver_info(dlp->dl_mh, dgt->dgt_tran_id,
+ &dgt->dgt_present, &dgt->dgt_usable);
+ }
+
+done:
+ if (dlh != NULL && dlp != NULL) {
+ dls_devnet_rele_link(dlh, dlp);
+ }
+
+ if (mph != NULL) {
+ mac_perim_exit(mph);
+ }
+
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+drv_ioc_readtran(void *karg, intptr_t arg, int mode, cred_t *cred,
+ int *rvalp)
+{
+ int ret = 0;
+ mac_perim_handle_t mph = NULL;
+ dls_dl_handle_t dlh = NULL;
+ dls_link_t *dlp = NULL;
+ dld_ioc_tranio_t *dti = karg;
+ uint8_t buf[256];
+ size_t nr;
+
+ /*
+ * Be strict for the moment
+ */
+ if (dti->dti_nbytes != 256 || dti->dti_off != 0)
+ return (EINVAL);
+
+ if ((ret = mac_perim_enter_by_linkid(dti->dti_linkid, &mph)) != 0)
+ goto done;
+
+ if ((ret = dls_devnet_hold_link(dti->dti_linkid, &dlh, &dlp)) != 0)
+ goto done;
+
+ /*
+ * Make sure that this link belongs to the zone.
+ */
+ if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ bzero(buf, sizeof (buf));
+ if ((ret = mac_transceiver_read(dlp->dl_mh, dti->dti_tran_id,
+ dti->dti_page, buf, dti->dti_nbytes, dti->dti_off, &nr)) == 0) {
+ dti->dti_nbytes = nr;
+ ret = ddi_copyout(buf, (void *)(uintptr_t)dti->dti_buf,
+ sizeof (buf), mode);
+ }
+
+done:
+ if (dlh != NULL && dlp != NULL) {
+ dls_devnet_rele_link(dlh, dlp);
+ }
+
+ if (mph != NULL) {
+ mac_perim_exit(mph);
+ }
+
+ return (ret);
+}
+
/*
* Note that ioctls that modify links have a NULL di_priv_func(), as
* privileges can only be checked after we know the class of the link being
@@ -1348,6 +1446,10 @@ static dld_ioc_info_t drv_ioc_list[] = {
drv_ioc_getprop, NULL},
{DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t),
drv_ioc_hwgrpget, NULL},
+ {DLDIOC_GETTRAN, DLDCOPYINOUT, sizeof (dld_ioc_gettran_t),
+ drv_ioc_gettran, NULL },
+ {DLDIOC_READTRAN, DLDCOPYINOUT, sizeof (dld_ioc_tranio_t),
+ drv_ioc_readtran, NULL }
};
typedef struct dld_ioc_modentry {
diff --git a/usr/src/uts/common/io/i40e/i40e_gld.c b/usr/src/uts/common/io/i40e/i40e_gld.c
index 28b4387594..ea0718dfe7 100644
--- a/usr/src/uts/common/io/i40e/i40e_gld.c
+++ b/usr/src/uts/common/io/i40e/i40e_gld.c
@@ -572,11 +572,38 @@ i40e_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
infop->mgi_count = i40e->i40e_num_trqpairs;
}
+static int
+i40e_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
+{
+ boolean_t present, usable;
+ i40e_t *i40e = arg;
+
+ if (id != 0 || infop == NULL)
+ return (EINVAL);
+
+ mutex_enter(&i40e->i40e_general_lock);
+ present = !!(i40e->i40e_hw_space.phy.link_info.link_info &
+ I40E_AQ_MEDIA_AVAILABLE);
+ if (present) {
+ usable = !!(i40e->i40e_hw_space.phy.link_info.an_info &
+ I40E_AQ_QUALIFIED_MODULE);
+ } else {
+ usable = B_FALSE;
+ }
+ mutex_exit(&i40e->i40e_general_lock);
+
+ mac_transceiver_info_set_usable(infop, usable);
+ mac_transceiver_info_set_present(infop, present);
+
+ return (0);
+}
+
static boolean_t
i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
{
i40e_t *i40e = arg;
mac_capab_rings_t *cap_rings;
+ mac_capab_transceiver_t *mct;
switch (cap) {
case MAC_CAPAB_HCKSUM: {
@@ -619,6 +646,20 @@ i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
return (B_FALSE);
}
break;
+ case MAC_CAPAB_TRANSCEIVER:
+ mct = cap_data;
+
+ /*
+ * Firmware doesn't have a great way of telling us in advance
+ * whether we'd expect a SFF transceiver. As such, we always
+ * advertise the support for this capability.
+ */
+ mct->mct_flags = 0;
+ mct->mct_ntransceivers = 1;
+ mct->mct_info = i40e_transceiver_info;
+ mct->mct_read = NULL;
+
+ return (B_TRUE);
default:
return (B_FALSE);
}
diff --git a/usr/src/uts/common/io/ixgbe/ixgbe_gld.c b/usr/src/uts/common/io/ixgbe/ixgbe_gld.c
index e00202c1f2..9dff2fe2db 100644
--- a/usr/src/uts/common/io/ixgbe/ixgbe_gld.c
+++ b/usr/src/uts/common/io/ixgbe/ixgbe_gld.c
@@ -27,6 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#include "ixgbe_sw.h"
@@ -280,6 +281,20 @@ ixgbe_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
}
break;
}
+ case MAC_CAPAB_TRANSCEIVER: {
+ mac_capab_transceiver_t *mct = cap_data;
+
+ /*
+ * Rather than try and guess based on the media type whether or
+ * not we have a transceiver we can read, we instead will let
+ * the actual function calls figure that out for us.
+ */
+ mct->mct_flags = 0;
+ mct->mct_ntransceivers = 1;
+ mct->mct_info = ixgbe_transceiver_info;
+ mct->mct_read = ixgbe_transceiver_read;
+ return (B_TRUE);
+ }
default:
return (B_FALSE);
}
diff --git a/usr/src/uts/common/io/ixgbe/ixgbe_main.c b/usr/src/uts/common/io/ixgbe/ixgbe_main.c
index 18f5333e48..7388cbef98 100644
--- a/usr/src/uts/common/io/ixgbe/ixgbe_main.c
+++ b/usr/src/uts/common/io/ixgbe/ixgbe_main.c
@@ -25,7 +25,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2017, Joyent, Inc.
+ * Copyright (c) 2017, Joyent, Inc.
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2013 Saso Kiselkov. All rights reserved.
* Copyright (c) 2013 OSN Online Service Nuernberg GmbH. All rights reserved.
@@ -3747,6 +3747,7 @@ ixgbe_sfp_check(void *arg)
struct ixgbe_hw *hw = &ixgbe->hw;
mutex_enter(&ixgbe->gen_lock);
+ (void) hw->phy.ops.identify_sfp(hw);
if (eicr & IXGBE_EICR_GPI_SDP1_BY_MAC(hw)) {
/* clear the interrupt */
IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1_BY_MAC(hw));
diff --git a/usr/src/uts/common/io/ixgbe/ixgbe_sw.h b/usr/src/uts/common/io/ixgbe/ixgbe_sw.h
index 6cd03d571d..0a40284416 100644
--- a/usr/src/uts/common/io/ixgbe/ixgbe_sw.h
+++ b/usr/src/uts/common/io/ixgbe/ixgbe_sw.h
@@ -857,6 +857,10 @@ void ixgbe_fill_group(void *arg, mac_ring_type_t, const int,
int ixgbe_rx_ring_intr_enable(mac_intr_handle_t);
int ixgbe_rx_ring_intr_disable(mac_intr_handle_t);
+int ixgbe_transceiver_info(void *, uint_t, mac_transceiver_info_t *);
+int ixgbe_transceiver_read(void *, uint_t, uint_t, void *, size_t, off_t,
+ size_t *);
+
/*
* Function prototypes in ixgbe_gld.c
*/
diff --git a/usr/src/uts/common/io/ixgbe/ixgbe_transceiver.c b/usr/src/uts/common/io/ixgbe/ixgbe_transceiver.c
new file mode 100644
index 0000000000..131b43d418
--- /dev/null
+++ b/usr/src/uts/common/io/ixgbe/ixgbe_transceiver.c
@@ -0,0 +1,169 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * Routines to get access to the phy and transceiver that require routines and
+ * definitions that aren't part of the common ixgbe API.
+ */
+
+#include "ixgbe_sw.h"
+#include "ixgbe_phy.h"
+
+static int
+ixgbe_transceiver_is_8472(ixgbe_t *ixgbe, boolean_t *valp)
+{
+ int32_t ret;
+ uint8_t rev, swap;
+ struct ixgbe_hw *hw = &ixgbe->hw;
+
+ ASSERT(MUTEX_HELD(&ixgbe->gen_lock));
+ if (hw->phy.ops.read_i2c_eeprom == NULL)
+ return (ENOTSUP);
+
+ ret = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_SFF_8472_COMP, &rev);
+ if (ret != 0)
+ return (EIO);
+
+ ret = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_SFF_8472_SWAP, &swap);
+ if (ret != 0)
+ return (EIO);
+
+ if (swap & IXGBE_SFF_ADDRESSING_MODE) {
+ ixgbe_log(ixgbe, "transceiver requires unsupported address "
+ "change for page 0xa2. Access will only be allowed to "
+ "page 0xa0.");
+ }
+
+ if (rev == IXGBE_SFF_SFF_8472_UNSUP ||
+ (swap & IXGBE_SFF_ADDRESSING_MODE)) {
+ *valp = B_FALSE;
+ } else {
+ *valp = B_TRUE;
+ }
+
+ return (0);
+}
+
+/*
+ * Note, we presume that the mac perimeter is held during these calls. As such,
+ * we rely on that for guaranteeing that only one thread is calling the i2c
+ * routines at any time.
+ */
+int
+ixgbe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
+{
+ ixgbe_t *ixgbe = arg;
+ struct ixgbe_hw *hw = &ixgbe->hw;
+ boolean_t present, usable;
+
+ if (id != 0 || infop == NULL)
+ return (EINVAL);
+
+ mutex_enter(&ixgbe->gen_lock);
+ if (ixgbe_get_media_type(&ixgbe->hw) == ixgbe_media_type_copper) {
+ mutex_exit(&ixgbe->gen_lock);
+ return (ENOTSUP);
+ }
+
+ /*
+ * Make sure we have the latest sfp information. This is especially
+ * important if the SFP is removed as that doesn't trigger interrupts in
+ * our current configuration.
+ */
+ (void) hw->phy.ops.identify_sfp(hw);
+ if (hw->phy.type == ixgbe_phy_none ||
+ (hw->phy.type == ixgbe_phy_unknown &&
+ hw->phy.sfp_type == ixgbe_sfp_type_not_present)) {
+ present = B_FALSE;
+ usable = B_FALSE;
+ } else {
+ present = B_TRUE;
+ usable = hw->phy.type != ixgbe_phy_sfp_unsupported;
+ }
+
+ mutex_exit(&ixgbe->gen_lock);
+
+ mac_transceiver_info_set_present(infop, present);
+ mac_transceiver_info_set_usable(infop, usable);
+
+ return (0);
+}
+
+/*
+ * Note, we presume that the mac perimeter is held during these calls. As such,
+ * we rely on that for guaranteeing that only one thread is calling the i2c
+ * routines at any time.
+ */
+int
+ixgbe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp,
+ size_t nbytes, off_t offset, size_t *nread)
+{
+ ixgbe_t *ixgbe = arg;
+ struct ixgbe_hw *hw = &ixgbe->hw;
+ uint8_t *buf = bp;
+ size_t i;
+ boolean_t is8472;
+
+ if (id != 0 || buf == NULL || nbytes == 0 || nread == NULL ||
+ (page != 0xa0 && page != 0xa2) || offset < 0)
+ return (EINVAL);
+
+ /*
+ * Both supported pages have a length of 256 bytes, ensure nothing asks
+ * us to go beyond that.
+ */
+ if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
+ return (EINVAL);
+ }
+
+ mutex_enter(&ixgbe->gen_lock);
+ if (ixgbe_get_media_type(&ixgbe->hw) == ixgbe_media_type_copper) {
+ mutex_exit(&ixgbe->gen_lock);
+ return (ENOTSUP);
+ }
+
+ if (hw->phy.ops.read_i2c_eeprom == NULL) {
+ mutex_exit(&ixgbe->gen_lock);
+ return (ENOTSUP);
+ }
+
+ if (ixgbe_transceiver_is_8472(ixgbe, &is8472) != 0) {
+ mutex_exit(&ixgbe->gen_lock);
+ return (EIO);
+ }
+
+ if (!is8472 && page == 0xa2) {
+ mutex_exit(&ixgbe->gen_lock);
+ return (EINVAL);
+ }
+
+ for (i = 0; i < nbytes; i++, offset++, buf++) {
+ int32_t ret;
+
+ if (page == 0xa0) {
+ ret = hw->phy.ops.read_i2c_eeprom(hw, offset, buf);
+ } else {
+ ret = hw->phy.ops.read_i2c_sff8472(hw, offset, buf);
+ }
+ if (ret != 0) {
+ mutex_exit(&ixgbe->gen_lock);
+ return (EIO);
+ }
+ }
+ mutex_exit(&ixgbe->gen_lock);
+ *nread = i;
+
+ return (0);
+}
diff --git a/usr/src/uts/common/io/mac/mac.c b/usr/src/uts/common/io/mac/mac.c
index c608110379..e9518233f2 100644
--- a/usr/src/uts/common/io/mac/mac.c
+++ b/usr/src/uts/common/io/mac/mac.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2016 Joyent, Inc.
+ * Copyright (c) 2017, Joyent, Inc.
* Copyright 2015 Garrett D'Amore <garrett@damore.org>
*/
@@ -8047,3 +8047,105 @@ mac_check_primary_relocation(mac_client_impl_t *mcip, boolean_t rxhw)
}
return (mcip);
}
+
+void
+mac_transceiver_init(mac_impl_t *mip)
+{
+ if (mac_capab_get((mac_handle_t)mip, MAC_CAPAB_TRANSCEIVER,
+ &mip->mi_transceiver)) {
+ /*
+ * The driver set a flag that we don't know about. In this case,
+ * we need to warn about that case and ignore this capability.
+ */
+ if (mip->mi_transceiver.mct_flags != 0) {
+ dev_err(mip->mi_dip, CE_WARN, "driver set transceiver "
+ "flags to invalid value: 0x%x, ignoring "
+ "capability", mip->mi_transceiver.mct_flags);
+ bzero(&mip->mi_transceiver,
+ sizeof (mac_capab_transceiver_t));
+ }
+ } else {
+ bzero(&mip->mi_transceiver,
+ sizeof (mac_capab_transceiver_t));
+ }
+}
+
+int
+mac_transceiver_count(mac_handle_t mh, uint_t *countp)
+{
+ mac_impl_t *mip = (mac_impl_t *)mh;
+
+ ASSERT(MAC_PERIM_HELD(mh));
+
+ if (mip->mi_transceiver.mct_ntransceivers == 0)
+ return (ENOTSUP);
+
+ *countp = mip->mi_transceiver.mct_ntransceivers;
+ return (0);
+}
+
+int
+mac_transceiver_info(mac_handle_t mh, uint_t tranid, boolean_t *present,
+ boolean_t *usable)
+{
+ int ret;
+ mac_transceiver_info_t info;
+
+ mac_impl_t *mip = (mac_impl_t *)mh;
+
+ ASSERT(MAC_PERIM_HELD(mh));
+
+ if (mip->mi_transceiver.mct_info == NULL ||
+ mip->mi_transceiver.mct_ntransceivers == 0)
+ return (ENOTSUP);
+
+ if (tranid >= mip->mi_transceiver.mct_ntransceivers)
+ return (EINVAL);
+
+ bzero(&info, sizeof (mac_transceiver_info_t));
+ if ((ret = mip->mi_transceiver.mct_info(mip->mi_driver, tranid,
+ &info)) != 0) {
+ return (ret);
+ }
+
+ *present = info.mti_present;
+ *usable = info.mti_usable;
+ return (0);
+}
+
+int
+mac_transceiver_read(mac_handle_t mh, uint_t tranid, uint_t page, void *buf,
+ size_t nbytes, off_t offset, size_t *nread)
+{
+ int ret;
+ size_t nr;
+ mac_impl_t *mip = (mac_impl_t *)mh;
+
+ ASSERT(MAC_PERIM_HELD(mh));
+
+ if (mip->mi_transceiver.mct_read == NULL)
+ return (ENOTSUP);
+
+ if (tranid >= mip->mi_transceiver.mct_ntransceivers)
+ return (EINVAL);
+
+ /*
+ * All supported pages today are 256 bytes wide. Make sure offset +
+ * nbytes never exceeds that.
+ */
+ if (offset < 0 || offset >= 256 || nbytes > 256 ||
+ offset + nbytes > 256)
+ return (EINVAL);
+
+ if (nread == NULL)
+ nread = &nr;
+ ret = mip->mi_transceiver.mct_read(mip->mi_driver, tranid, page, buf,
+ nbytes, offset, nread);
+ if (ret == 0 && *nread > nbytes) {
+ dev_err(mip->mi_dip, CE_PANIC, "driver wrote %lu bytes into "
+ "%lu byte sized buffer, possible memory corruption",
+ *nread, nbytes);
+ }
+
+ return (ret);
+}
diff --git a/usr/src/uts/common/io/mac/mac_provider.c b/usr/src/uts/common/io/mac/mac_provider.c
index f5ff559a9d..e2590ca641 100644
--- a/usr/src/uts/common/io/mac/mac_provider.c
+++ b/usr/src/uts/common/io/mac/mac_provider.c
@@ -353,6 +353,8 @@ mac_register(mac_register_t *mregp, mac_handle_t *mhp)
mac_addr_factory_init(mip);
+ mac_transceiver_init(mip);
+
/*
* Enforce the virtrualization level registered.
*/
@@ -1516,3 +1518,17 @@ mac_lso_get(mblk_t *mp, uint32_t *mss, uint32_t *flags)
*mss = (uint32_t)DB_LSOMSS(mp);
}
}
+
+void
+mac_transceiver_info_set_present(mac_transceiver_info_t *infop,
+ boolean_t present)
+{
+ infop->mti_present = present;
+}
+
+void
+mac_transceiver_info_set_usable(mac_transceiver_info_t *infop,
+ boolean_t usable)
+{
+ infop->mti_usable = usable;
+}
diff --git a/usr/src/uts/common/sys/dld.h b/usr/src/uts/common/sys/dld.h
index fb2a0749d3..50febacbd8 100644
--- a/usr/src/uts/common/sys/dld.h
+++ b/usr/src/uts/common/sys/dld.h
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#ifndef _SYS_DLD_H
@@ -29,7 +30,7 @@
/*
* Data-Link Driver ioctl interfaces.
*
- * Note that the datastructures defined here define an ioctl interface
+ * Note that the data structures defined here define an ioctl interface
* that is shared betwen user and kernel space. The dld driver thus
* assumes that the structures have identical layout and size when
* compiled in either IPL32 or LP64.
@@ -314,6 +315,27 @@ typedef struct dld_hwgrpinfo {
char dhi_clnts[MAXCLIENTNAMELEN];
} dld_hwgrpinfo_t;
+#define DLDIOC_GETTRAN DLDIOC(0x1e)
+
+#define DLDIOC_GETTRAN_GETNTRAN UINT32_MAX
+
+typedef struct dld_ioc_gettran {
+ datalink_id_t dgt_linkid;
+ uint_t dgt_tran_id;
+ boolean_t dgt_present;
+ boolean_t dgt_usable;
+} dld_ioc_gettran_t;
+
+#define DLDIOC_READTRAN DLDIOC(0x1f)
+typedef struct dld_ioc_tranio {
+ datalink_id_t dti_linkid;
+ uint_t dti_tran_id;
+ uint_t dti_page;
+ uint_t dti_nbytes;
+ uint_t dti_off;
+ uint64_t dti_buf;
+} dld_ioc_tranio_t;
+
#if _LONG_LONG_ALIGNMENT == 8 && _LONG_LONG_ALIGNMENT_32 == 4
#pragma pack()
#endif
diff --git a/usr/src/uts/common/sys/mac_impl.h b/usr/src/uts/common/sys/mac_impl.h
index 3a9403e747..292550db62 100644
--- a/usr/src/uts/common/sys/mac_impl.h
+++ b/usr/src/uts/common/sys/mac_impl.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#ifndef _SYS_MAC_IMPL_H
@@ -476,6 +476,11 @@ struct mac_impl_s {
mac_ring_handle_t mi_default_tx_ring;
/*
+ * Transceiver capabilities. SL protected.
+ */
+ mac_capab_transceiver_t mi_transceiver;
+
+ /*
* MAC address list. SL protected.
*/
mac_address_t *mi_addresses;
@@ -910,6 +915,19 @@ extern mac_bridge_rx_t mac_bridge_rx_cb;
extern mac_bridge_ref_t mac_bridge_ref_cb;
extern mac_bridge_ls_t mac_bridge_ls_cb;
+/*
+ * MAC Transceiver related functions
+ */
+struct mac_transceiver_info {
+ boolean_t mti_present;
+ boolean_t mti_usable;
+};
+
+extern void mac_transceiver_init(mac_impl_t *);
+extern int mac_transceiver_count(mac_handle_t, uint_t *);
+extern int mac_transceiver_info(mac_handle_t, uint_t, boolean_t *, boolean_t *);
+extern int mac_transceiver_read(mac_handle_t, uint_t, uint_t, void *, size_t,
+ off_t, size_t *);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/sys/mac_provider.h b/usr/src/uts/common/sys/mac_provider.h
index 9f7f2a1a73..fb3d74c90f 100644
--- a/usr/src/uts/common/sys/mac_provider.h
+++ b/usr/src/uts/common/sys/mac_provider.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#ifndef _SYS_MAC_PROVIDER_H
@@ -106,7 +107,8 @@ typedef enum {
MAC_CAPAB_NO_NATIVEVLAN = 0x00080000, /* boolean only, no data */
MAC_CAPAB_NO_ZCOPY = 0x00100000, /* boolean only, no data */
MAC_CAPAB_LEGACY = 0x00200000, /* data is mac_capab_legacy_t */
- MAC_CAPAB_VRRP = 0x00400000 /* data is mac_capab_vrrp_t */
+ MAC_CAPAB_VRRP = 0x00400000, /* data is mac_capab_vrrp_t */
+ MAC_CAPAB_TRANSCEIVER = 0x01000000 /* mac_capab_transciever_t */
} mac_capab_t;
/*
@@ -432,6 +434,19 @@ typedef struct mac_capab_vrrp_s {
} mac_capab_vrrp_t;
/*
+ * Transceiver capability
+ */
+typedef struct mac_transceiver_info mac_transceiver_info_t;
+
+typedef struct mac_capab_transceiver {
+ uint_t mct_flags;
+ uint_t mct_ntransceivers;
+ int (*mct_info)(void *, uint_t, mac_transceiver_info_t *);
+ int (*mct_read)(void *, uint_t, uint_t, void *, size_t, off_t,
+ size_t *);
+} mac_capab_transceiver_t;
+
+/*
* MAC registration interface
*/
typedef struct mac_register_s {
@@ -541,6 +556,13 @@ extern void mac_hcksum_set(mblk_t *, uint32_t, uint32_t,
extern void mac_lso_get(mblk_t *, uint32_t *, uint32_t *);
+extern void mac_transceiver_info_set_present(
+ mac_transceiver_info_t *,
+ boolean_t);
+extern void mac_transceiver_info_set_usable(
+ mac_transceiver_info_t *,
+ boolean_t);
+
#endif /* _KERNEL */
#ifdef __cplusplus