summaryrefslogtreecommitdiff
path: root/usr/src/cmd/dlutil/dltraninfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/dlutil/dltraninfo.c')
-rw-r--r--usr/src/cmd/dlutil/dltraninfo.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/usr/src/cmd/dlutil/dltraninfo.c b/usr/src/cmd/dlutil/dltraninfo.c
new file mode 100644
index 0000000000..af7f8c80c2
--- /dev/null
+++ b/usr/src/cmd/dlutil/dltraninfo.c
@@ -0,0 +1,458 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2017, Joyent, Inc.
+ */
+
+/*
+ * Private utility to dump transceiver information for each physical datalink.
+ * Something like this should eventually be a part of dladm or similar.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <libgen.h>
+
+#include <libdladm.h>
+#include <libdllink.h>
+#include <sys/dld.h>
+#include <sys/dld_ioc.h>
+#include <sys/dls_mgmt.h>
+#include <libsff.h>
+
+#define DLTRAN_KIND_LEN 64
+
+static dladm_handle_t dltran_hdl;
+static char dltran_dlerrmsg[DLADM_STRSIZE];
+static const char *dltran_progname;
+static boolean_t dltran_verbose;
+static boolean_t dltran_hex;
+static boolean_t dltran_write;
+static int dltran_outfd;
+static int dltran_errors;
+
+/*
+ * This routine basically assumes that we'll have 16 byte aligned output to
+ * print out the human readable output.
+ */
+static void
+dltran_dump_page(uint8_t *buf, size_t nbytes, uint_t page)
+{
+ size_t i;
+ static boolean_t first = B_TRUE;
+
+ if (first) {
+ (void) printf("page %*s 0", 4, "");
+ for (i = 1; i < 16; i++) {
+ if (i % 4 == 0 && i % 16 != 0) {
+ (void) printf(" ");
+ }
+
+ (void) printf("%2x", i);
+ }
+ (void) printf(" v123456789abcdef\n");
+ first = B_FALSE;
+ }
+ for (i = 0; i < nbytes; i++) {
+
+ if (i % 16 == 0) {
+ (void) printf("0x%02x %04x: ", page, i);
+ }
+
+ if (i % 4 == 0 && i % 16 != 0) {
+ (void) printf(" ");
+ }
+
+
+ (void) printf("%02x", buf[i]);
+
+ if (i % 16 == 15) {
+ int j;
+ (void) printf(" ");
+ for (j = i - (i % 16); j <= i; j++) {
+ if (!isprint(buf[j])) {
+ (void) printf(".");
+ } else {
+ (void) printf("%c", buf[j]);
+ }
+ }
+ (void) printf("\n");
+ }
+ }
+}
+
+static int
+dltran_read_page(datalink_id_t link, uint_t tranid, uint_t page, uint8_t *bufp,
+ size_t *buflen)
+{
+ dld_ioc_tranio_t dti;
+
+ bzero(bufp, *buflen);
+ bzero(&dti, sizeof (dti));
+
+ dti.dti_linkid = link;
+ dti.dti_tran_id = tranid;
+ dti.dti_page = page;
+ dti.dti_nbytes = *buflen;
+ dti.dti_off = 0;
+ dti.dti_buf = (uintptr_t)(void *)bufp;
+
+ if (ioctl(dladm_dld_fd(dltran_hdl), DLDIOC_READTRAN, &dti) != 0) {
+ (void) fprintf(stderr, "failed to read transceiver page "
+ "0x%2x: %s\n", page, strerror(errno));
+ return (1);
+ }
+
+ *buflen = dti.dti_nbytes;
+ return (0);
+}
+
+static boolean_t
+dltran_is_8472(uint8_t *buf)
+{
+ switch (buf[0]) {
+ case 0xc:
+ case 0xd:
+ case 0x11:
+ /*
+ * Catch cases that refer explicitly to QSFP and newer.
+ */
+ return (B_FALSE);
+ default:
+ break;
+ }
+
+ /*
+ * Check the byte that indicates compliance with SFF 8472. Use this to
+ * know if we can read page 0xa2 or not.
+ */
+ if (buf[94] == 0)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+static void
+dltran_hex_dump(datalink_id_t linkid, uint_t tranid)
+{
+ uint8_t buf[256];
+ size_t buflen = sizeof (buf);
+
+ if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
+ dltran_errors++;
+ return;
+ }
+
+ dltran_dump_page(buf, buflen, 0xa0);
+
+ if (!dltran_is_8472(buf)) {
+ return;
+ }
+
+ buflen = sizeof (buf);
+ if (dltran_read_page(linkid, tranid, 0xa2, buf, &buflen) != 0) {
+ dltran_errors++;
+ return;
+ }
+
+ dltran_dump_page(buf, buflen, 0xa2);
+}
+
+static void
+dltran_write_page(datalink_id_t linkid, uint_t tranid)
+{
+ uint8_t buf[256];
+ size_t buflen = sizeof (buf);
+ off_t off;
+
+ if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
+ dltran_errors++;
+ return;
+ }
+
+ off = 0;
+ while (buflen > 0) {
+ ssize_t ret;
+
+ ret = write(dltran_outfd, buf + off, buflen);
+ if (ret == -1) {
+ (void) fprintf(stderr, "failed to write data "
+ "to output file: %s\n", strerror(errno));
+ dltran_errors++;
+ return;
+ }
+
+ off += ret;
+ buflen -= ret;
+ }
+}
+
+static void
+dltran_verbose_dump(datalink_id_t linkid, uint_t tranid)
+{
+ uint8_t buf[256];
+ size_t buflen = sizeof (buf);
+ int ret;
+ nvlist_t *nvl;
+
+ if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
+ dltran_errors++;
+ return;
+ }
+
+ ret = libsff_parse(buf, buflen, 0xa0, &nvl);
+ if (ret == 0) {
+ dump_nvlist(nvl, 8);
+ nvlist_free(nvl);
+ } else {
+ fprintf(stderr, "failed to parse sfp data: %s\n",
+ strerror(ret));
+ dltran_errors++;
+ }
+}
+
+static int
+dltran_dump_transceivers(dladm_handle_t hdl, datalink_id_t linkid, void *arg)
+{
+ dladm_status_t status;
+ char name[MAXLINKNAMELEN];
+ dld_ioc_gettran_t gt;
+ uint_t count, i, tranid = UINT_MAX;
+ boolean_t tran_found = B_FALSE;
+ uint_t *tranidp = arg;
+
+ if (tranidp != NULL)
+ tranid = *tranidp;
+
+ if ((status = dladm_datalink_id2info(hdl, linkid, NULL, NULL, NULL,
+ name, sizeof (name))) != DLADM_STATUS_OK) {
+ (void) fprintf(stderr, "failed to get datalink name for link "
+ "%d: %s", linkid, dladm_status2str(status,
+ dltran_dlerrmsg));
+ dltran_errors++;
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ bzero(&gt, sizeof (gt));
+ gt.dgt_linkid = linkid;
+ gt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN;
+
+ if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, &gt) != 0) {
+ if (errno != ENOTSUP) {
+ (void) fprintf(stderr, "failed to get transceiver "
+ "count for device %s: %s\n",
+ name, strerror(errno));
+ dltran_errors++;
+ }
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ count = gt.dgt_tran_id;
+ (void) printf("%s: discovered %d transceiver%s\n", name, count,
+ count > 1 ? "s" : "");
+ for (i = 0; i < count; i++) {
+ if (tranid != UINT_MAX && i != tranid)
+ continue;
+ if (tranid != UINT_MAX)
+ tran_found = B_TRUE;
+ bzero(&gt, sizeof (gt));
+ gt.dgt_linkid = linkid;
+ gt.dgt_tran_id = i;
+
+ if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, &gt) != 0) {
+ (void) fprintf(stderr, "failed to get tran info for "
+ "%s: %s\n", name, strerror(errno));
+ dltran_errors++;
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ if (dltran_hex && !gt.dgt_present)
+ continue;
+ if (!dltran_hex && !dltran_write) {
+ (void) printf("\ttransceiver %d present: %s\n", i,
+ gt.dgt_present ? "yes" : "no");
+ if (!gt.dgt_present)
+ continue;
+ (void) printf("\ttransceiver %d usable: %s\n", i,
+ gt.dgt_usable ? "yes" : "no");
+ }
+
+ if (dltran_verbose) {
+ dltran_verbose_dump(linkid, i);
+ }
+
+ if (dltran_write) {
+ if (!gt.dgt_present) {
+ (void) fprintf(stderr, "warning: no "
+ "transceiver present in port %d, not "
+ "writing\n", i);
+ dltran_errors++;
+ continue;
+ }
+ dltran_write_page(linkid, i);
+ }
+
+ if (dltran_hex) {
+ printf("transceiver %d data:\n", i);
+ dltran_hex_dump(linkid, i);
+ }
+ }
+
+ if (tranid != UINT_MAX && !tran_found) {
+ dltran_errors++;
+ (void) fprintf(stderr, "failed to find transceiver %d on "
+ "link %s\n", tranid, name);
+ }
+
+ return (DLADM_WALK_CONTINUE);
+}
+
+
+static void
+dltran_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", dltran_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-x | -v | -w file] [tran]...\n"
+ "\n"
+ "\t-v display all transceiver information\n"
+ "\t-w write transceiver data page 0xa0 to file\n"
+ "\t-x dump raw hexadecimal for transceiver\n",
+ dltran_progname);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ dladm_status_t status;
+ const char *outfile = NULL;
+ uint_t count = 0;
+
+ dltran_progname = basename(argv[0]);
+
+ while ((c = getopt(argc, argv, ":xvw:")) != -1) {
+ switch (c) {
+ case 'v':
+ dltran_verbose = B_TRUE;
+ break;
+ case 'x':
+ dltran_hex = B_TRUE;
+ break;
+ case 'w':
+ dltran_write = B_TRUE;
+ outfile = optarg;
+ break;
+ case ':':
+ dltran_usage("option -%c requires an "
+ "operand\n", optopt);
+ return (2);
+ case '?':
+ default:
+ dltran_usage("unknown option: -%c\n", optopt);
+ return (2);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (dltran_verbose)
+ count++;
+ if (dltran_hex)
+ count++;
+ if (dltran_write)
+ count++;
+ if (count > 1) {
+ (void) fprintf(stderr, "only one of -v, -w, and -x may be "
+ "specified\n");
+ return (2);
+ }
+
+ if (dltran_write) {
+ if ((dltran_outfd = open(outfile, O_RDWR | O_TRUNC | O_CREAT,
+ 0644)) < 0) {
+ (void) fprintf(stderr, "failed to open output file "
+ "%s: %s\n", outfile, strerror(errno));
+ return (1);
+ }
+ }
+
+ if ((status = dladm_open(&dltran_hdl)) != DLADM_STATUS_OK) {
+ (void) fprintf(stderr, "failed to open /dev/dld: %s\n",
+ dladm_status2str(status, dltran_dlerrmsg));
+ return (1);
+ }
+
+ if (argc == 0) {
+ (void) dladm_walk_datalink_id(dltran_dump_transceivers,
+ dltran_hdl, NULL, DATALINK_CLASS_PHYS,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ } else {
+ int i;
+ char *c;
+
+ for (i = 0; i < argc; i++) {
+ uint_t tran;
+ uint_t *tranidp = NULL;
+ datalink_id_t linkid;
+
+ if ((c = strrchr(argv[i], '/')) != NULL) {
+ unsigned long u;
+ char *eptr;
+
+ c++;
+ errno = 0;
+ u = strtoul(c, &eptr, 10);
+ if (errno != 0 || *eptr != '\0' ||
+ u >= UINT_MAX) {
+ (void) fprintf(stderr, "failed to "
+ "parse link/transceiver: %s\n",
+ argv[i]);
+ return (1);
+ }
+ c--;
+ *c = '\0';
+ tran = (uint_t)u;
+ tranidp = &tran;
+ }
+
+ if ((status = dladm_name2info(dltran_hdl, argv[i],
+ &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ (void) fprintf(stderr, "failed to get link "
+ "id for link %s: %s\n", argv[i],
+ dladm_status2str(status, dltran_dlerrmsg));
+ return (1);
+ }
+
+ (void) dltran_dump_transceivers(dltran_hdl, linkid,
+ tranidp);
+ }
+ }
+
+ return (dltran_errors != 0 ? 1 : 0);
+}