summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2017-05-26 20:35:05 +0000
committerRobert Mustacchi <rm@joyent.com>2017-06-29 23:32:23 +0000
commit5732ccacb1877bdbc9366061bb1a43a97c9fde39 (patch)
tree64945e5ab907aecab0a33bd100cb57f1a05f1d7f
parent5ad7f810d0a72624056299b85500cdd7a9e5a916 (diff)
downloadillumos-joyent-5732ccacb1877bdbc9366061bb1a43a97c9fde39.tar.gz
OS-6180 NIC Transceivers should show up in hc topo
OS-6181 Want library to parse SFP i2c information Reviewed by: Ryan Zezeski <ryan.zeseski@joyent.com> Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Approved by: Bryan Cantrill <bryan@joyent.com>
-rw-r--r--exception_lists/copyright1
-rw-r--r--manifest3
-rw-r--r--usr/src/cmd/dlutil/Makefile2
-rw-r--r--usr/src/cmd/dlutil/dltraninfo.c396
-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.c1412
-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/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/Makefile81
-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
62 files changed, 6951 insertions, 184 deletions
diff --git a/exception_lists/copyright b/exception_lists/copyright
index cd371d5701..34a3ddfa44 100644
--- a/exception_lists/copyright
+++ b/exception_lists/copyright
@@ -407,6 +407,7 @@ usr/src/lib/libsmbfs/smb/derparse.[ch]
usr/src/lib/libsmbfs/smb/spnego.c
usr/src/lib/libsmbfs/smb/spnegoparse.[ch]
usr/src/test/util-tests/tests/dis/*/*.out
+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/manifest b/manifest
index a249f0ae14..cec73970ad 100644
--- a/manifest
+++ b/manifest
@@ -4947,6 +4947,7 @@ s usr/lib/amd64/libsecdb.so.1=../../../lib/amd64/libsecdb.so.1
s usr/lib/amd64/libsecdb.so=../../../lib/amd64/libsecdb.so.1
s usr/lib/amd64/libsendfile.so.1=../../../lib/amd64/libsendfile.so.1
s usr/lib/amd64/libsendfile.so=../../../lib/amd64/libsendfile.so.1
+f usr/lib/amd64/libsff.so.1 0755 root bin
f usr/lib/amd64/libshare.so.1 0755 root bin
s usr/lib/amd64/libshare.so=libshare.so.1
f usr/lib/amd64/libshell.so.1 0755 root bin
@@ -5390,6 +5391,7 @@ f usr/lib/fm/topo/plugins/disk.so 0555 root bin
f usr/lib/fm/topo/plugins/fac_prov_ipmi.so 0555 root bin
f usr/lib/fm/topo/plugins/fac_prov_mptsas.so 0555 root bin
f usr/lib/fm/topo/plugins/ipmi.so 0555 root bin
+f usr/lib/fm/topo/plugins/nic.so 0555 root bin
f usr/lib/fm/topo/plugins/ses.so 0555 root bin
f usr/lib/fm/topo/plugins/xfp.so 0555 root bin
d usr/lib/fs 0755 root sys
@@ -6392,6 +6394,7 @@ s usr/lib/libsecdb.so.1=../../lib/libsecdb.so.1
s usr/lib/libsecdb.so=../../lib/libsecdb.so.1
s usr/lib/libsendfile.so.1=../../lib/libsendfile.so.1
s usr/lib/libsendfile.so=../../lib/libsendfile.so.1
+f usr/lib/libsff.so.1 0755 root bin
f usr/lib/libshare.so.1 0755 root bin
s usr/lib/libshare.so=libshare.so.1
f usr/lib/libshell.so.1 0755 root bin
diff --git a/usr/src/cmd/dlutil/Makefile b/usr/src/cmd/dlutil/Makefile
index 49b6933cc7..8644786603 100644
--- a/usr/src/cmd/dlutil/Makefile
+++ b/usr/src/cmd/dlutil/Makefile
@@ -20,7 +20,7 @@ include ../Makefile.cmd
ROOTCMDDIR = $(ROOTLIB)/dl
CFLAGS += $(CCVERBOSE)
-dltraninfo := LDLIBS += -ldladm
+dltraninfo := LDLIBS += -ldladm -lsff -lnvpair
dlled := LDLIBS += -ldladm
dlsend := LDLIBS += -ldlpi -lsocket -lmd
dlrecv := LDLIBS += -ldlpi
diff --git a/usr/src/cmd/dlutil/dltraninfo.c b/usr/src/cmd/dlutil/dltraninfo.c
index 2e6f7ee2e1..af7f8c80c2 100644
--- a/usr/src/cmd/dlutil/dltraninfo.c
+++ b/usr/src/cmd/dlutil/dltraninfo.c
@@ -35,81 +35,18 @@
#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 char **dltran_links;
-static int dltran_nlinks; /* array size */
-static int dltran_clinks; /* current count */
-static boolean_t dltran_tranid_set;
-static int dltran_tranid;
static const char *dltran_progname;
-
-/* ARGSUSED */
-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;
-
- 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));
- return (DLADM_WALK_CONTINUE);
- }
-
- if (dltran_nlinks != NULL) {
- for (i = 0; i < dltran_clinks; i++) {
- if (strcmp(dltran_links[i], name) == 0)
- break;
- }
- if (i == dltran_clinks)
- 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) {
- (void) fprintf(stderr, "failed to get transceiver count "
- "for device %s: %s\n",
- name, strerror(errno));
- return (DLADM_WALK_CONTINUE);
- }
-
-
- count = gt.dgt_tran_id;
- (void) printf("%s: discovered %d transceivers\n", name, count);
- for (i = 0; i < count; i++) {
- if (dltran_tranid_set && i != dltran_tranid)
- continue;
- 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));
- return (DLADM_WALK_CONTINUE);
- }
-
- (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");
- }
-
- return (DLADM_WALK_CONTINUE);
-}
+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
@@ -161,23 +98,19 @@ dltran_dump_page(uint8_t *buf, size_t nbytes, uint_t page)
}
}
-/*
- * We always read 256 bytes even though only the first 128 bytes are sometimes
- * significant on a given page and others are reserved.
- */
static int
dltran_read_page(datalink_id_t link, uint_t tranid, uint_t page, uint8_t *bufp,
- size_t buflen)
+ size_t *buflen)
{
dld_ioc_tranio_t dti;
- bzero(bufp, buflen);
+ 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_nbytes = *buflen;
dti.dti_off = 0;
dti.dti_buf = (uintptr_t)(void *)bufp;
@@ -187,8 +120,7 @@ dltran_read_page(datalink_id_t link, uint_t tranid, uint_t page, uint8_t *bufp,
return (1);
}
- dltran_dump_page(bufp, dti.dti_nbytes, page);
-
+ *buflen = dti.dti_nbytes;
return (0);
}
@@ -217,45 +149,182 @@ dltran_is_8472(uint8_t *buf)
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_read_link(const char *link)
+dltran_dump_transceivers(dladm_handle_t hdl, datalink_id_t linkid, void *arg)
{
dladm_status_t status;
- datalink_id_t linkid;
+ char name[MAXLINKNAMELEN];
dld_ioc_gettran_t gt;
- uint8_t buf[256];
- int ret;
+ uint_t count, i, tranid = UINT_MAX;
+ boolean_t tran_found = B_FALSE;
+ uint_t *tranidp = arg;
- if ((status = dladm_name2info(dltran_hdl, link, &linkid, NULL, NULL,
- NULL)) != DLADM_STATUS_OK) {
- (void) fprintf(stderr, "failed to get link id for link "
- "%s: %s\n", link,
- dladm_status2str(status, dltran_dlerrmsg));
- return (1);
+ 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 = dltran_tranid;
+ gt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN;
- if (ioctl(dladm_dld_fd(dltran_hdl), DLDIOC_GETTRAN, &gt) != 0) {
- (void) fprintf(stderr, "failed to get transceiver information "
- "for %s: %s\n", link, strerror(errno));
- return (1);
+ 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);
}
- if ((ret = dltran_read_page(linkid, dltran_tranid, 0xa0, buf,
- sizeof (buf))) != 0) {
- return (ret);
+ 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 (!dltran_is_8472(buf)) {
- return (0);
+ if (tranid != UINT_MAX && !tran_found) {
+ dltran_errors++;
+ (void) fprintf(stderr, "failed to find transceiver %d on "
+ "link %s\n", tranid, name);
}
- return (dltran_read_page(linkid, dltran_tranid, 0xa2, buf,
- sizeof (buf)));
+ return (DLADM_WALK_CONTINUE);
}
+
static void
dltran_usage(const char *fmt, ...)
{
@@ -268,11 +337,11 @@ dltran_usage(const char *fmt, ...)
va_end(ap);
}
- (void) fprintf(stderr, "Usage: %s [-i id] [-l link]... [-r]\n"
+ (void) fprintf(stderr, "Usage: %s [-x | -v | -w file] [tran]...\n"
"\n"
- "\t-i id specify a transceiver id to operate on\n"
- "\t-l link specify a data link to operate on\n"
- "\t-r read transceiver page\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);
}
@@ -280,46 +349,23 @@ int
main(int argc, char *argv[])
{
int c;
- char *eptr;
- long l;
dladm_status_t status;
- boolean_t do_read = B_FALSE;
+ const char *outfile = NULL;
+ uint_t count = 0;
dltran_progname = basename(argv[0]);
- while ((c = getopt(argc, argv, ":hi:l:r")) != -1) {
+ while ((c = getopt(argc, argv, ":xvw:")) != -1) {
switch (c) {
- case 'i':
- errno = 0;
- l = strtol(optarg, &eptr, 10);
- if (errno != 0 || *eptr != '\0' || l < 0 ||
- l > INT_MAX) {
- (void) fprintf(stderr, "invalid value for -i: "
- "%s\n", optarg);
- return (2);
- }
- dltran_tranid = (int)l;
- dltran_tranid_set = B_TRUE;
+ case 'v':
+ dltran_verbose = B_TRUE;
break;
- case 'l':
- if (dltran_nlinks == dltran_clinks) {
- char **p;
- dltran_nlinks += 8;
-
- p = realloc(dltran_links,
- sizeof (char **) * dltran_nlinks);
- if (p == NULL) {
- (void) fprintf(stderr, "failed to "
- "allocate space for %d links: %s\n",
- dltran_nlinks, strerror(errno));
- return (1);
- }
- dltran_links = p;
- }
- dltran_links[dltran_clinks++] = optarg;
+ case 'x':
+ dltran_hex = B_TRUE;
break;
- case 'r':
- do_read = B_TRUE;
+ case 'w':
+ dltran_write = B_TRUE;
+ outfile = optarg;
break;
case ':':
dltran_usage("option -%c requires an "
@@ -332,25 +378,81 @@ main(int argc, char *argv[])
}
}
- if (do_read && dltran_clinks != 1) {
- (void) fprintf(stderr, "-r requires exactly one link "
- "specified with -l\n");
+ 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 (do_read) {
- return (dltran_read_link(dltran_links[0]));
- }
+ 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;
+ }
- (void) dladm_walk_datalink_id(dltran_dump_transceivers, dltran_hdl,
- NULL, DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE,
- DLADM_OPT_ACTIVE);
+ 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);
+ }
- return (0);
+ (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 8bf8ca4c31..0f6aa5a0bf 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -203,6 +203,7 @@ SUBDIRS += \
libsec \
libsecdb \
libsendfile \
+ libsff \
libshare \
libshell \
libsip \
@@ -459,6 +460,7 @@ HDRSUBDIRS= \
libsasl \
libscf \
libsec \
+ libsff \
libshare \
libshell \
libsip \
@@ -586,7 +588,7 @@ auditd_plugins: libbsm libsecdb libgss libmtmalloc
brand: libzonecfg libmapmalloc libipadm libcmdutils libproc librpcsvc
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
@@ -662,6 +664,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..29efcf6bd9
--- /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 (tn);
+}
+
+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..cd0a1228a6
--- /dev/null
+++ b/usr/src/lib/libsff/common/libsff.c
@@ -0,0 +1,1412 @@
+/*
+ * 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 "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 (buf[start + i] < ' ' || buf[start + i] > '~') {
+ 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/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index 800bd21c91..e40b486a85 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -28,6 +28,7 @@ outputdir = /var/tmp/test_results
[/opt/util-tests/tests/show-overlay-exit]
[/opt/util-tests/tests/vnic-mtu]
[/opt/util-tests/tests/bunyan/bunyan]
+[/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 4ada2a9b4c..f4b7b90daf 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -14,6 +14,6 @@
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
#
-SUBDIRS = dis dladm iconv libnvpair_json printf xargs bunyan mergeq workq
+SUBDIRS = dis dladm iconv libnvpair_json libsff printf xargs bunyan mergeq workq
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..a4134ae739
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libsff/Makefile
@@ -0,0 +1,81 @@
+#
+# 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
+
+PROGS = $(DIFF_PROGS) \
+ libsff_efault \
+ libsff_einval \
+ libsff_strings
+
+SCRIPTS = libsff
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.ctf
+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
+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_O)
+
+
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