this adds support to ifconfig for reading info from transceivers.

it looks like this:

dlg@ix ifconfig$ sudo ./obj/ifconfig ix0 transceiver
ix0: identifier SFP (03)
        connector: Copper Pigtail (21)
        vendor: Amphenol
        product: 616740001
        revision: B
        serial: CN0V250M36J0T86
        date: 2013-07-04
dlg@ix ifconfig$ sudo ./obj/ifconfig ix1 transceiver 
ix1: identifier SFP (03)
        connector: LC (07)
        vendor: FINISAR CORP.
        product: FTLX8571D3BCL-FC
        revision: A
        serial: AQG28W3
        date: 2013-10-19
        temperature: 34.60 C
        vcc: 3.3553 V
        tx-bias: 7986.0 uA
        tx-power: 0.6128 mW
        rx-power: 0.6153 mW average
dlg@ix ifconfig$ sudo ./obj/ifconfig ixl0 transceiver
ixl0: identifier QSFP+ (0d)

this is all specified by the SFF (small formfactor) group in SNIA, but
it is a lot of disparate documentation to get into your head. the top
level summary is that sfp modules have an i2c bus wired up to them, and
answer reads at device address 0xa0. there is a 256 byte page at that
address with information like the type of module, and depending on the
type you can find the manufacturer, product name, serial number, and so
on.

a later spec added support a "digital diagnostics monitoring" (DDM)
or "digital optical monitoring" (DOM) capability where there's live
status/diag information available at i2c address 0xa2. again, it's a 256
byte page, but the values change all the time based on what the module
is doing. this is where the temperature and laser power stuff is.

ive implemented basic support for the above, which is specific to
some sfp shaped modules (so sfp+ and sfp28 too) and gbics. devices
report whether they support the diag page, so it only fetches and
parses that if page 0 on 0xa0 says it can. there are different specs
for the other types of modules, in particular qsfp and related
modules have a very different layout. however, they still use the
same device addresses and pages, it's just that the contents of the
page vary. support for qsfp will be forthcoming if this goes ahead.
dumping more info generally will happen as time and interest permits
too.

i've only implemented the kernel backend for this on ix and ixl. ixl
support is patchy because it relies on a command that only exists in
high API versions (like 1.7). ix seems pretty consistent. other nics can
grow support as time and hw availability permites. i don't have an em(4)
with optics, so that might be hard for me to do myself, but i tried to
make the kernel side as easy as possible so people should have a good
chance at figuring it out.

do those power units sound plausible or are the factors off?

this was originally requested by rachel roch on misc@ in "Viewing SFP
diagnostic data in OpenBSD ?"

thoughts? 

Index: sys/sys/sockio.h
===================================================================
RCS file: /cvs/src/sys/sys/sockio.h,v
retrieving revision 1.80
diff -u -p -r1.80 sockio.h
--- sys/sys/sockio.h    26 Feb 2019 03:19:11 -0000      1.80
+++ sys/sys/sockio.h    8 Apr 2019 02:05:36 -0000
@@ -68,6 +68,7 @@
 /* 53 and 54 used to be SIOC[SG]IFMEDIA with a 32 bit media word */
 #define        SIOCSIFMEDIA    _IOWR('i', 55, struct ifreq)    /* set net 
media */
 #define        SIOCGIFMEDIA    _IOWR('i', 56, struct ifmediareq) /* get net 
media */
+#define        SIOCGIFSFFPAGE  _IOWR('i', 57, struct if_sffpage) /* get SFF 
page */
 
 #define        SIOCDIFPHYADDR   _IOW('i', 73, struct ifreq)    /* delete gif 
addrs */
 #define        SIOCSLIFPHYADDR  _IOW('i', 74, struct if_laddrreq) /* set gif 
addrs */
Index: sys/net/if.c
===================================================================
RCS file: /cvs/src/sys/net/if.c,v
retrieving revision 1.573
diff -u -p -r1.573 if.c
--- sys/net/if.c        1 Mar 2019 04:47:32 -0000       1.573
+++ sys/net/if.c        8 Apr 2019 02:05:36 -0000
@@ -144,6 +144,8 @@ int if_detached_ioctl(struct ifnet *, u_
 
 int    ifioctl_get(u_long, caddr_t);
 int    ifconf(caddr_t);
+static int
+       if_sffpage_check(const caddr_t);
 
 int    if_getgroup(caddr_t, struct ifnet *);
 int    if_getgroupmembers(caddr_t);
@@ -2143,6 +2172,19 @@ ifioctl(struct socket *so, u_long cmd, c
                NET_UNLOCK();
                break;
 
+       case SIOCGIFSFFPAGE:
+               error = suser(p);
+               if (error != 0)
+                       break;
+
+               error = if_sffpage_check(data);
+               if (error != 0)
+                       break;
+
+               /* don't take NET_LOCK because i2c reads take a long time */
+               error = ((*ifp->if_ioctl)(ifp, cmd, data));
+               break;
+
        case SIOCSETKALIVE:
        case SIOCDIFPHYADDR:
        case SIOCSLIFPHYADDR:
@@ -2304,6 +2346,22 @@ ifioctl_get(u_long cmd, caddr_t data)
        return (error);
 }
 
+static int
+if_sffpage_check(const caddr_t data)
+{
+       const struct if_sffpage *sff = (const struct if_sffpage *)data;
+
+       switch (sff->sff_addr) {
+       case IFSFF_ADDR_EEPROM:
+       case IFSFF_ADDR_DDM:
+               break;
+       default:
+               return (EINVAL);
+       }
+
+       return (0);
+}
+
 /*
  * Return interface configuration
  * of system.  List may be used
Index: sys/net/if.h
===================================================================
RCS file: /cvs/src/sys/net/if.h,v
retrieving revision 1.199
diff -u -p -r1.199 if.h
--- sys/net/if.h        23 Jan 2019 08:23:18 -0000      1.199
+++ sys/net/if.h        8 Apr 2019 02:05:36 -0000
@@ -501,6 +501,20 @@ struct if_parent {
        char            ifp_parent[IFNAMSIZ];
 };
 
+/* SIOCGIFSFFPAGE */
+
+#define IFSFF_ADDR_EEPROM      0xa0
+#define IFSFF_ADDR_DDM         0xa2
+
+#define IFSFF_DATA_LEN         256
+
+struct if_sffpage {
+       char             sff_ifname[IFNAMSIZ];  /* u -> k */
+       uint8_t          sff_addr;              /* u -> k */
+       uint8_t          sff_page;              /* u -> k */
+       uint8_t          sff_data[256];         /* k -> u */
+};
+
 #include <net/if_arp.h>
 
 #ifdef _KERNEL
Index: sys/dev/pci/if_ix.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_ix.c,v
retrieving revision 1.156
diff -u -p -r1.156 if_ix.c
--- sys/dev/pci/if_ix.c 1 Mar 2019 06:15:59 -0000       1.156
+++ sys/dev/pci/if_ix.c 8 Apr 2019 02:05:36 -0000
@@ -96,6 +96,7 @@ int   ixgbe_detach(struct device *, int);
 void   ixgbe_start(struct ifqueue *);
 int    ixgbe_ioctl(struct ifnet *, u_long, caddr_t);
 int    ixgbe_rxrinfo(struct ix_softc *, struct if_rxrinfo *);
+int    ixgbe_get_sffpage(struct ix_softc *, struct if_sffpage *);
 void   ixgbe_watchdog(struct ifnet *);
 void   ixgbe_init(void *);
 void   ixgbe_stop(void *);
@@ -225,6 +226,8 @@ ixgbe_attach(struct device *parent, stru
        sc->osdep.os_sc = sc;
        sc->osdep.os_pa = *pa;
 
+       rw_init(&sc->sfflock, "ixsff");
+
        /* Set up the timer callout */
        timeout_set(&sc->timer, ixgbe_local_timer, sc);
        timeout_set(&sc->rx_refill, ixgbe_rxrefill, sc);
@@ -498,6 +506,15 @@ ixgbe_ioctl(struct ifnet * ifp, u_long c
                error = ixgbe_rxrinfo(sc, (struct if_rxrinfo *)ifr->ifr_data);
                break;
 
+       case SIOCGIFSFFPAGE:
+               error = rw_enter(&sc->sfflock, RW_WRITE|RW_INTR);
+               if (error != 0)
+                       break;
+
+               error = ixgbe_get_sffpage(sc, (struct if_sffpage *)data);
+               rw_exit(&sc->sfflock);
+               break;
+
        default:
                error = ether_ioctl(ifp, &sc->arpcom, command, data);
        }
@@ -516,6 +533,50 @@ ixgbe_ioctl(struct ifnet * ifp, u_long c
 }
 
 int
+ixgbe_get_sffpage(struct ix_softc *sc, struct if_sffpage *sff)
+{
+       struct ixgbe_hw *hw = &sc->hw;
+       uint32_t swfw_mask = hw->phy.phy_semaphore_mask;
+       uint8_t page;
+       size_t i;
+       int error = EIO;
+
+       if (hw->phy.type == ixgbe_phy_fw)
+               return (ENODEV);
+
+       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
+               return (EBUSY); /* XXX */
+
+       if (sff->sff_addr == IFSFF_ADDR_EEPROM) {
+               if (hw->phy.ops.read_i2c_byte_unlocked(hw, 127,
+                   IFSFF_ADDR_EEPROM, &page))
+                       goto error;
+               if (page != sff->sff_page &&
+                   hw->phy.ops.write_i2c_byte_unlocked(hw, 127,
+                   IFSFF_ADDR_EEPROM, sff->sff_page))
+                       goto error;
+       }
+
+       for (i = 0; i < sizeof(sff->sff_data); i++) {
+               if (hw->phy.ops.read_i2c_byte_unlocked(hw, i,
+                   sff->sff_addr, &sff->sff_data[i]))
+                       goto error;
+       }
+
+       if (sff->sff_addr == IFSFF_ADDR_EEPROM) {
+               if (page != sff->sff_page &&
+                   hw->phy.ops.write_i2c_byte_unlocked(hw, 127,
+                   IFSFF_ADDR_EEPROM, page))
+                       goto error;
+       }
+
+       error = 0;
+error:
+       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+       return (error);
+}
+
+int
 ixgbe_rxrinfo(struct ix_softc *sc, struct if_rxrinfo *ifri)
 {
        struct if_rxring_info *ifr, ifr1;
Index: sys/dev/pci/ixgbe_type.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/ixgbe_type.h,v
retrieving revision 1.31
diff -u -p -r1.31 ixgbe_type.h
--- sys/dev/pci/ixgbe_type.h    18 Nov 2016 14:16:10 -0000      1.31
+++ sys/dev/pci/ixgbe_type.h    8 Apr 2019 02:05:36 -0000
@@ -3142,7 +3142,9 @@ enum ixgbe_phy_type {
        ixgbe_phy_aq,
        ixgbe_phy_x550em_kr,
        ixgbe_phy_x550em_kx4,
+       ixgbe_phy_x550em_xfi,
        ixgbe_phy_x550em_ext_t,
+       ixgbe_phy_ext_1g_t,
        ixgbe_phy_cu_unknown,
        ixgbe_phy_qt,
        ixgbe_phy_xaui,
@@ -3160,6 +3162,8 @@ enum ixgbe_phy_type {
        ixgbe_phy_qsfp_intel,
        ixgbe_phy_qsfp_unknown,
        ixgbe_phy_sfp_unsupported, /*Enforce bit set with unsupported module*/
+       ixgbe_phy_sgmii,
+       ixgbe_phy_fw,
        ixgbe_phy_generic
 };
 
Index: sys/dev/pci/if_ixl.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_ixl.c,v
retrieving revision 1.34
diff -u -p -r1.34 if_ixl.c
--- sys/dev/pci/if_ixl.c        1 Apr 2019 03:01:14 -0000       1.34
+++ sys/dev/pci/if_ixl.c        8 Apr 2019 02:05:36 -0000
@@ -170,6 +170,8 @@ struct ixl_aq_desc {
 #define IXL_AQ_OP_PHY_RESTART_AN       0x0605
 #define IXL_AQ_OP_PHY_LINK_STATUS      0x0607
 #define IXL_AQ_OP_PHY_SET_EVENT_MASK   0x0613
+#define IXL_AQ_OP_PHY_SET_REGISTER     0x0628
+#define IXL_AQ_OP_PHY_GET_REGISTER     0x0629
 #define IXL_AQ_OP_LLDP_GET_MIB         0x0a00
 #define IXL_AQ_OP_LLDP_MIB_CHG_EV      0x0a01
 #define IXL_AQ_OP_LLDP_ADD_TLV         0x0a02
@@ -596,6 +598,18 @@ struct ixl_aq_veb_reply {
 #define IXL_AQ_PHY_REPORT_QUAL         (1 << 0)
 #define IXL_AQ_PHY_REPORT_INIT         (1 << 1)
 
+struct ixl_aq_phy_reg_access {
+       uint8_t         phy_iface;
+#define IXL_AQ_PHY_IF_INTERNAL         0
+#define IXL_AQ_PHY_IF_EXTERNAL         1
+#define IXL_AQ_PHY_IF_MODULE           2
+       uint8_t         dev_addr;
+       uint16_t        _reserved1;
+       uint32_t        reg;
+       uint32_t        val;
+       uint32_t        _reserved2;
+} __packed __aligned(16);
+
 /* RESTART_AN param[0] */
 #define IXL_AQ_PHY_RESTART_AN          (1 << 1)
 #define IXL_AQ_PHY_LINK_ENABLE         (1 << 2)
@@ -1124,6 +1138,8 @@ struct ixl_softc {
 
        struct rwlock            sc_cfg_lock;
        unsigned int             sc_dead;
+
+       struct rwlock            sc_sff_lock;
 };
 #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
 
@@ -1171,6 +1187,12 @@ static void      ixl_arq(void *);
 static void    ixl_hmc_pack(void *, const void *,
                    const struct ixl_hmc_pack *, unsigned int);
 
+static int     ixl_get_sffpage(struct ixl_softc *, struct if_sffpage *);
+static int     ixl_sff_get_byte(struct ixl_softc *, uint8_t, uint32_t,
+                   uint8_t *);
+static int     ixl_sff_set_byte(struct ixl_softc *, uint8_t, uint32_t,
+                   uint8_t);
+
 static int     ixl_match(struct device *, void *, void *);
 static void    ixl_attach(struct device *, struct device *, void *);
 
@@ -1347,6 +1369,8 @@ ixl_aq_dva(struct ixl_aq_desc *iaq, bus_
 #define HTOLE16(_x)    (_x)
 #endif
 
+static struct rwlock ixl_sff_lock = RWLOCK_INITIALIZER("ixlsff");
+
 static const struct pci_matchid ixl_devices[] = {
 #ifdef notyet
        { PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_XL710_VF },
@@ -1771,6 +1795,15 @@ ixl_ioctl(struct ifnet *ifp, u_long cmd,
                }
                break;
 
+       case SIOCGIFSFFPAGE:
+               error = rw_enter(&ixl_sff_lock, RW_WRITE|RW_INTR);
+               if (error != 0)
+                       break;
+
+               error = ixl_get_sffpage(sc, (struct if_sffpage *)data);
+               rw_exit(&ixl_sff_lock);
+               break;
+
        default:
                error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
                break;
@@ -3427,6 +3460,115 @@ ixl_get_link_status(struct ixl_softc *sc
        }
 
        sc->sc_ac.ac_if.if_link_state = ixl_set_link_status(sc, &iaq);
+
+       return (0);
+}
+
+static int
+ixl_get_sffpage(struct ixl_softc *sc, struct if_sffpage *sff)
+{
+       uint8_t page;
+       size_t i;
+       int error;
+
+       if (sff->sff_addr == IFSFF_ADDR_EEPROM) {
+               error = ixl_sff_get_byte(sc, IFSFF_ADDR_EEPROM, 127, &page);
+               if (error != 0)
+                       return (error);
+               if (page != sff->sff_page) {
+                       error = ixl_sff_set_byte(sc, IFSFF_ADDR_EEPROM, 127,
+                           sff->sff_page);
+                       if (error != 0)
+                               return (error);
+               }
+       }
+
+       for (i = 0; i < sizeof(sff->sff_data); i++) {
+               error = ixl_sff_get_byte(sc, sff->sff_addr, i,
+                   &sff->sff_data[i]);
+               if (error != 0)
+                       return (error);
+       }
+
+       if (sff->sff_addr == IFSFF_ADDR_EEPROM) {
+               if (page != sff->sff_page) {
+                       error = ixl_sff_set_byte(sc, IFSFF_ADDR_EEPROM, 127,
+                           page);
+                       if (error != 0)
+                               return (error);
+               }
+       }
+
+       return (0);
+}
+
+static int
+ixl_sff_get_byte(struct ixl_softc *sc, uint8_t dev, uint32_t reg, uint8_t *p)
+{
+       struct ixl_atq iatq;
+       struct ixl_aq_desc *iaq;
+       struct ixl_aq_phy_reg_access *param;
+
+       memset(&iatq, 0, sizeof(iatq));
+       iaq = &iatq.iatq_desc;
+       iaq->iaq_opcode = htole16(IXL_AQ_OP_PHY_GET_REGISTER);
+       param = (struct ixl_aq_phy_reg_access *)iaq->iaq_param;
+       param->phy_iface = IXL_AQ_PHY_IF_MODULE;
+       param->dev_addr = dev;
+       htolem32(&param->reg, reg);
+
+       ixl_atq_exec(sc, &iatq, "ixlsffget");
+
+       switch (iaq->iaq_retval) {
+       case htole16(IXL_AQ_RC_OK):
+               break;
+       case htole16(IXL_AQ_RC_EBUSY):
+               return (EBUSY);
+       case htole16(IXL_AQ_RC_ESRCH):
+               return (ENODEV);
+       case htole16(IXL_AQ_RC_EIO):
+       case htole16(IXL_AQ_RC_EINVAL):
+       default:
+               printf("%s: %u\n", __func__, lemtoh16(&iaq->iaq_retval));
+               return (EIO);
+       }
+
+       *p = lemtoh32(&param->val);
+
+       return (0);
+}
+
+
+static int
+ixl_sff_set_byte(struct ixl_softc *sc, uint8_t dev, uint32_t reg, uint8_t v)
+{
+       struct ixl_atq iatq;
+       struct ixl_aq_desc *iaq;
+       struct ixl_aq_phy_reg_access *param;
+
+       memset(&iatq, 0, sizeof(iatq));
+       iaq = &iatq.iatq_desc;
+       iaq->iaq_opcode = htole16(IXL_AQ_OP_PHY_SET_REGISTER);
+       param = (struct ixl_aq_phy_reg_access *)iaq->iaq_param;
+       param->phy_iface = IXL_AQ_PHY_IF_MODULE;
+       param->dev_addr = dev;
+       htolem32(&param->reg, reg);
+       htolem32(&param->val, v);
+
+       ixl_atq_exec(sc, &iatq, "ixlsffset");
+
+       switch (iaq->iaq_retval) {
+       case htole16(IXL_AQ_RC_OK):
+               break;
+       case htole16(IXL_AQ_RC_EBUSY):
+               return (EBUSY);
+       case htole16(IXL_AQ_RC_ESRCH):
+               return (ENODEV);
+       case htole16(IXL_AQ_RC_EIO):
+       case htole16(IXL_AQ_RC_EINVAL):
+       default:
+               return (EIO);
+       }
 
        return (0);
 }
Index: sbin/ifconfig/Makefile
===================================================================
RCS file: /cvs/src/sbin/ifconfig/Makefile,v
retrieving revision 1.14
diff -u -p -r1.14 Makefile
--- sbin/ifconfig/Makefile      3 May 2016 17:52:33 -0000       1.14
+++ sbin/ifconfig/Makefile      8 Apr 2019 02:05:36 -0000
@@ -1,7 +1,7 @@
 #      $OpenBSD: Makefile,v 1.14 2016/05/03 17:52:33 jca Exp $
 
 PROG=  ifconfig
-SRCS=  ifconfig.c brconfig.c
+SRCS=  ifconfig.c brconfig.c sff.c
 MAN=   ifconfig.8
 
 LDADD= -lutil
Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.394
diff -u -p -r1.394 ifconfig.c
--- sbin/ifconfig/ifconfig.c    20 Feb 2019 19:17:17 -0000      1.394
+++ sbin/ifconfig/ifconfig.c    8 Apr 2019 02:05:36 -0000
@@ -340,6 +340,8 @@ void        umb_setclass(const char *, int);
 void   umb_roaming(const char *, int);
 void   utf16_to_char(uint16_t *, int, char *, size_t);
 int    char_to_utf16(const char *, uint16_t *, size_t);
+void   transceiver(const char *, int);
+void   transceiverdump(const char *, int);
 #else
 void   setignore(const char *, int);
 #endif
@@ -587,6 +589,9 @@ const struct        cmd {
        { "datapath",   NEXTARG,        0,              switch_datapathid },
        { "portno",     NEXTARG2,       0,              NULL, switch_portno },
        { "addlocal",   NEXTARG,        0,              addlocal },
+       { "transceiver", 0,             0,              transceiver },
+       { "sff",        0,              0,              transceiver },
+       { "sffdump",    0,              0,              transceiverdump },
 #else /* SMALL */
        { "powersave",  NEXTARG0,       0,              setignore },
        { "priority",   NEXTARG,        0,              setignore },
@@ -4003,6 +4008,22 @@ setmpwcontrolword(const char *value, int
                imrsave.imr_flags |= IMR_FLAG_CONTROLWORD;
        else
                imrsave.imr_flags &= ~IMR_FLAG_CONTROLWORD;
+}
+
+int    if_sff_info(int, const char *, int);
+
+void
+transceiver(const char *value, int d)
+{
+       if (if_sff_info(s, name, 0) == -1)
+               err(1, "%s %s", name, __func__);
+}
+
+void
+transceiverdump(const char *value, int d)
+{
+       if (if_sff_info(s, name, 1) == -1)
+               err(1, "%s transceiver", name);
 }
 #endif /* SMALL */
 
Index: sbin/ifconfig/sff.c
===================================================================
RCS file: sbin/ifconfig/sff.c
diff -N sbin/ifconfig/sff.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sbin/ifconfig/sff.c 8 Apr 2019 02:05:36 -0000
@@ -0,0 +1,451 @@
+/*     $OpenBSD$ */
+
+/*
+ * Copyright (c) David Gwynne <d...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SMALL
+
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <util.h>
+
+#ifndef nitems
+#define nitems(_a)     (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#ifndef ISSET
+#define ISSET(_w, _m)  ((_w) & (_m))
+#endif
+
+#define SFF8024_ID_UNKNOWN     0x00
+#define SFF8024_ID_GBIC                0x01
+#define SFF8024_ID_MOBO                0x02 /* Module/connector soldered to 
mobo */
+                                    /* using SFF-8472 */
+#define SFF8024_ID_SFP         0x03 /* SFP/SFP+/SFP28 */
+#define SFF8024_ID_300PIN_XBI  0x04 /* 300 pin XBI */
+#define SFF8024_ID_XENPAK      0x05
+#define SFF8024_ID_XFP         0x06
+#define SFF8024_ID_XFF         0x07
+#define SFF8024_ID_XFPE                0x08 /* XFP-E */
+#define SFF8024_ID_XPAK                0x09
+#define SFF8024_ID_X2          0x0a
+#define SFF8024_ID_DWDM_SFP    0x0b /* DWDM-SFP/SFP+ */
+                                    /* not using SFF-8472 */
+#define SFF8024_ID_QSFP                0x0c
+#define SFF8024_ID_QSFP_PLUS   0x0d /* or later */
+                                    /* using SFF-8436/8665/8685 et al */
+#define SFF8024_ID_CXP         0x0e /* or later */
+#define SFF8024_ID_HD4X                0x0f /* shielded mini multilane HD 4X */
+#define SFF8024_ID_HD8X                0x10 /* shielded mini multilane HD 8X */
+#define SFF8024_ID_QSFP28      0x11 /* or later */
+                                    /* using SFF-8665 et al */
+#define SFF8024_ID_CXP2                0x12 /* aka CXP28, or later */
+#define SFF8024_ID_CDFP                0x13 /* style 1/style 2 */
+#define SFF8024_ID_HD4X_FAN    0x14 /* shielded mini multilane HD 4X fanout */ 
+#define SFF8024_ID_HD8X_FAN    0x15 /* shielded mini multilane HD 8X fanout */ 
+#define SFF8024_ID_CDFP3       0x16 /* style 3 */
+#define SFF8024_ID_uQSFP       0x17 /* microQSFP */
+#define SFF8024_ID_QSFP_DD     0x18 /* QSFP-DD double density 8x */
+                                    /* INF-8628 */
+#define SFF8024_ID_RESERVED    0x7f /* up to here is reserved */
+                                    /* 0x80 to 0xff is vendor specific */
+
+#define SFF8024_ID_IS_RESERVED(_id)    ((_id) <= SFF8024_ID_RESERVED)
+#define SFF8024_ID_IS_VENDOR(_id)      ((_id) > SFF8024_ID_RESERVED)
+
+#define SFF8024_CON_UNKNOWN    0x00
+#define SFF8024_CON_SC         0x01 /* Subscriber Connector */
+#define SFF8024_CON_FC_1       0x02 /* Fibre Channel Style 1 copper */
+#define SFF8024_CON_FC_2       0x03 /* Fibre Channel Style 2 copper */
+#define SFF8024_CON_BNC_TNC    0x04 /* BNC/TNC */
+#define SFF8024_CON_FC_COAX    0x05 /* Fibre Channel coax headers */
+#define SFF8024_CON_FJ         0x06 /* Fibre Jack */
+#define SFF8024_CON_LC         0x07 /* Lucent Connector */
+#define SFF8024_CON_MT_RJ      0x08 /* Mechanical Transfer - Registered Jack */
+#define SFF8024_CON_MU         0x09 /* Multiple Optical */
+#define SFF8024_CON_SG         0x0a
+#define SFF8024_CON_O_PIGTAIL  0x0b /* Optical Pigtail */
+#define SFF8024_CON_MPO_1x12   0x0c /* Multifiber Parallel Optic 1x12 */
+#define SFF8024_CON_MPO_2x16   0x0e /* Multifiber Parallel Optic 2x16 */
+#define SFF8024_CON_HSSDC2     0x20 /* High Speed Serial Data Connector */
+#define SFF8024_CON_Cu_PIGTAIL 0x21 /* Copper Pigtail */
+#define SFF8024_CON_RJ45       0x22
+#define SFF8024_CON_NO         0x23 /* No separable connector */
+#define SFF8024_CON_MXC_2x16   0x24
+#define SFF8024_CON_RESERVED   0x7f /* up to here is reserved */
+                                    /* 0x80 to 0xff is vendor specific */
+
+#define SFF8024_CON_IS_RESERVED(_id)   ((_id) <= SFF8024_CON_RESERVED)
+#define SFF8024_CON_IS_VENDOR(_id)     ((_id) > SFF8024_CON_RESERVED)
+
+static const char *sff8024_id_names[] = {
+       [SFF8024_ID_UNKNOWN]    = "Unknown",
+       [SFF8024_ID_GBIC]       = "GBIC",
+       [SFF8024_ID_SFP]        = "SFP",
+       [SFF8024_ID_300PIN_XBI] = "300 pin XBI",
+       [SFF8024_ID_XENPAK]     = "XENPAK",
+       [SFF8024_ID_XFP]        = "XFP",
+       [SFF8024_ID_XFF]        = "XFF",
+       [SFF8024_ID_XFPE]       = "XFPE",
+       [SFF8024_ID_XPAK]       = "XPAK",
+       [SFF8024_ID_X2]         = "X2",
+       [SFF8024_ID_DWDM_SFP]   = "DWDM-SFP",
+       [SFF8024_ID_QSFP]       = "QSFP",
+       [SFF8024_ID_QSFP_PLUS]  = "QSFP+",
+       [SFF8024_ID_CXP]        = "CXP",
+       [SFF8024_ID_HD4X]       = "Shielded Mini Multilane HD 4X",
+       [SFF8024_ID_HD8X]       = "Shielded Mini Multilane HD 8X",
+       [SFF8024_ID_QSFP28]     = "QSFP28",
+       [SFF8024_ID_CXP2]       = "CXP2",
+       [SFF8024_ID_CDFP]       = "CDFP Style 1/2",
+       [SFF8024_ID_HD4X_FAN]   = "Shielded Mini Multilane HD 4X Fanout Cable",
+       [SFF8024_ID_HD8X_FAN]   = "Shielded Mini Multilane HD 8X Fanout Cable",
+       [SFF8024_ID_CDFP3]      = "CDFP Style 3",
+       [SFF8024_ID_uQSFP]      = "microQSFP",
+       [SFF8024_ID_QSFP_DD]    = "QSFP Double-Density",
+};
+
+static const char *sff8024_con_names[] = {
+       [SFF8024_CON_UNKNOWN]   = "Unknown",
+       [SFF8024_CON_SC]        = "SC",
+       [SFF8024_CON_FC_1]      = "Fibre Channel style 1",
+       [SFF8024_CON_FC_2]      = "Fibre Channel style 2",
+       [SFF8024_CON_BNC_TNC]   = "BNC/TNC",
+       [SFF8024_CON_FC_COAX]   = "Fibre Channel coax headers",
+       [SFF8024_CON_FJ]        = "Fiber Jack",
+       [SFF8024_CON_LC]        = "LC",
+       [SFF8024_CON_MT_RJ]     = "MT-RJ",
+       [SFF8024_CON_MU]        = "MU",
+       [SFF8024_CON_SG]        = "SG",
+       [SFF8024_CON_O_PIGTAIL] = "Optical Pigtail",
+       [SFF8024_CON_MPO_1x12]  = "MPO 2x16",
+       [SFF8024_CON_MPO_2x16]  = "MPO 1x12",
+       [SFF8024_CON_HSSDC2]    = "HSSDC II",
+       [SFF8024_CON_Cu_PIGTAIL]
+                               = "Copper Pigtail",
+       [SFF8024_CON_RJ45]      = "RJ45",
+       [SFF8024_CON_NO]        = "No separable connector",
+       [SFF8024_CON_MXC_2x16]  = "MXC 2x16",
+};
+
+#define SFF8472_ID                     0 /* SFF8027 for identifier values */
+#define SFF8472_EXT_ID                 1
+#define SFF8472_EXT_ID_UNSPECIFIED             0x00
+#define SFF8472_EXT_ID_MOD_DEF_1               0x01
+#define SFF8472_EXT_ID_MOD_DEF_2               0x02
+#define SFF8472_EXT_ID_MOD_DEF_3               0x03
+#define SFF8472_EXT_ID_2WIRE                   0x04
+#define SFF8472_EXT_ID_MOD_DEF_5               0x05
+#define SFF8472_EXT_ID_MOD_DEF_6               0x06
+#define SFF8472_EXT_ID_MOD_DEF_7               0x07
+#define SFF8472_CON                    2 /* SFF8027 for connector values */
+#define SFF8472_VENDOR_START           20
+#define SFF8472_VENDOR_END             35
+#define SFF8472_PRODUCT_START          40
+#define SFF8472_PRODUCT_END            55
+#define SFF8472_REVISION_START         56
+#define SFF8472_REVISION_END           59
+#define SFF8472_SERIAL_START           68
+#define SFF8472_SERIAL_END             83
+#define SFF8472_DATECODE               84
+#define SFF8472_DDM_TYPE               92
+#define SFF8472_DDM_TYPE_AVG_POWER             (1U << 3)
+#define SFF8472_DDM_TYPE_CAL_EXT               (1U << 4)
+#define SFF8472_DDM_TYPE_CAL_INT               (1U << 5)
+#define SFF8472_DDM_TYPE_IMPL                  (1U << 6)
+#define SFF8472_COMPLIANCE             94
+#define SFF8472_COMPLIANCE_NONE                        0x00
+#define SFF8472_COMPLIANCE_9_3                 0x01 /* SFF-8472 Rev 9.3 */
+#define SFF8472_COMPLIANCE_9_5                 0x02 /* SFF-8472 Rev 9.5 */
+#define SFF8472_COMPLIANCE_10_2                        0x03 /* SFF-8472 Rev 
10.2 */
+#define SFF8472_COMPLIANCE_10_4                        0x04 /* SFF-8472 Rev 
10.4 */
+#define SFF8472_COMPLIANCE_11_0                        0x05 /* SFF-8472 Rev 
11.0 */
+#define SFF8472_COMPLIANCE_11_3                        0x06 /* SFF-8472 Rev 
11.3 */
+#define SFF8472_COMPLIANCE_11_4                        0x07 /* SFF-8472 Rev 
11.4 */
+#define SFF8472_COMPLIANCE_12_3                        0x08 /* SFF-8472 Rev 
12.3 */
+
+/*
+ * page 0xa2
+ */
+#define SFF8472_DDM_TEMP               96
+#define SFF8472_DDM_VCC                        98
+#define SFF8472_DDM_TX_BIAS            100
+#define SFF8472_DDM_TX_POWER           102
+#define SFF8472_DDM_RX_POWER           104
+#define SFF8472_DDM_LASER              106 /* laser temp/wavelength */
+                                           /* optional */
+#define SFF8472_DDM_TEC                        108 /* Measured TEC current */
+                                           /* optional */
+
+#define SFF_TEMP_FACTOR                256.0
+#define SFF_TEMP_FMT           "%.02f C"
+#define SFF_VCC_FACTOR         10000.0
+#define SFF_VCC_FMT            "%.04f V"
+#define SFF_BIAS_FACTOR                0.5
+#define SFF_BIAS_FMT           "%.01f uA"
+#define SFF_POWER_FACTOR       10000.0
+#define SFF_POWER_FMT          "%.04f mW"
+
+static void    hexdump(const void *, size_t);
+static int     if_sff8472(int, const char *, int, const struct if_sffpage *);
+
+static const char *
+sff_id_name(uint8_t id)
+{
+       const char *name = NULL;
+
+       if (id < nitems(sff8024_id_names)) {
+               name = sff8024_id_names[id];
+               if (name != NULL)
+                       return (name);
+       }
+
+       if (SFF8024_ID_IS_VENDOR(id))
+               return ("Vendor Specific");
+
+       return ("Reserved");
+}
+
+static const char *
+sff_con_name(uint8_t id)
+{
+       const char *name = NULL;
+
+       if (id < nitems(sff8024_con_names)) {
+               name = sff8024_con_names[id];
+               if (name != NULL)
+                       return (name);
+       }
+
+       if (SFF8024_CON_IS_VENDOR(id))
+               return ("Vendor Specific");
+
+       return ("Reserved");
+}
+
+static void
+if_sffpage_init(struct if_sffpage *sff, const char *ifname,
+    uint8_t addr, uint8_t page)
+{
+       memset(sff, 0, sizeof(*sff));
+
+       if (strlcpy(sff->sff_ifname, ifname, sizeof(sff->sff_ifname)) >=
+           sizeof(sff->sff_ifname))
+               errx(1, "interface name too long");
+
+       sff->sff_addr = addr;
+       sff->sff_page = page;
+}
+
+static void
+if_sffpage_dump(const char *ifname, const struct if_sffpage *sff)
+{
+       printf("%s: addr %02x", ifname, sff->sff_addr);
+       if (sff->sff_addr == IFSFF_ADDR_EEPROM)
+               printf(" page %u", sff->sff_page);
+       putchar('\n');
+       hexdump(sff->sff_data, sizeof(sff->sff_data));
+}
+
+int
+if_sff_info(int s, const char *ifname, int dump)
+{
+       struct if_sffpage pg0;
+       int error = 0;
+       uint8_t id, ext_id;
+
+       if_sffpage_init(&pg0, ifname, IFSFF_ADDR_EEPROM, 0);
+
+       if (ioctl(s, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
+               return (-1);
+
+       if (dump)
+               if_sffpage_dump(ifname, &pg0);
+
+       id = pg0.sff_data[0]; /* SFF8472_ID */
+
+       printf("%s: identifier %s (%02x)\n", ifname, sff_id_name(id), id);
+       switch (id) {
+       case SFF8024_ID_SFP:
+               ext_id = pg0.sff_data[SFF8472_EXT_ID];
+               if (ext_id != SFF8472_EXT_ID_2WIRE) {
+                       printf("\textended-id: %02xh\n", ext_id);
+                       break;
+               }
+               /* FALLTHROUGH */
+       case SFF8024_ID_GBIC:
+               error = if_sff8472(s, ifname, dump, &pg0);
+               break;
+       }
+
+       return (error);
+}
+
+static int
+printable(int ch)
+{
+       if (ch == '\0')
+               return ('_');
+       if (!isprint(ch))
+               return ('~');
+
+       return (ch);
+}
+
+static void
+if_sff_ascii_print(const struct if_sffpage *sff, const char *name,
+    size_t start, size_t end)
+{
+       const uint8_t *d = sff->sff_data;
+       int ch;
+
+       printf("\t%s: ", name);
+
+       for (;;) {
+               ch = d[start];
+               if (!isspace(ch) && ch != '\0')
+                       break;
+
+               start++;
+               if (start == end) {
+                       printf("(unknown)\n");
+                       return;
+               }
+       }
+
+       for (;;) {
+               int ch = d[end];
+               if (!isspace(ch) && ch != '\0')
+                       break;
+
+               end--;
+       }
+
+       do {
+               putchar(printable(d[start]));
+       } while (++start <= end);
+       putchar('\n');
+}
+
+static void
+if_sff_date_print(const struct if_sffpage *sff, const char *name,
+    size_t start)
+{
+       const uint8_t *d = sff->sff_data + start;
+
+       /* YYMMDD */
+       printf("\t%s: 20%c%c-%c%c-%c%c\n", name,
+           d[0], d[1], d[2], d[3], d[4], d[5]);
+}
+
+static int16_t
+if_sff_int(const struct if_sffpage *sff, size_t start)
+{
+       const uint8_t *d = sff->sff_data + start;
+
+       return (d[0] << 8 | d[1]);
+}
+
+static uint16_t
+if_sff_uint(const struct if_sffpage *sff, size_t start)
+{
+       const uint8_t *d = sff->sff_data + start;
+
+       return (d[0] << 8 | d[1]);
+}
+
+static int
+if_sff8472(int s, const char *ifname, int dump, const struct if_sffpage *pg0)
+{
+       struct if_sffpage ddm;
+       uint8_t con, ddm_types;
+
+       con = pg0->sff_data[SFF8472_CON];
+       printf("\tconnector: %s (%02x)\n", sff_con_name(con), con);
+
+       if_sff_ascii_print(pg0, "vendor",
+           SFF8472_VENDOR_START, SFF8472_VENDOR_END);
+       if_sff_ascii_print(pg0, "product",
+           SFF8472_PRODUCT_START, SFF8472_PRODUCT_END);
+       if_sff_ascii_print(pg0, "revision",
+           SFF8472_REVISION_START, SFF8472_REVISION_END);
+       if_sff_ascii_print(pg0, "serial",
+           SFF8472_SERIAL_START, SFF8472_SERIAL_END);
+       if_sff_date_print(pg0, "date", SFF8472_DATECODE);
+
+       ddm_types = pg0->sff_data[SFF8472_DDM_TYPE];
+       if (pg0->sff_data[SFF8472_COMPLIANCE] == SFF8472_COMPLIANCE_NONE ||
+           !ISSET(ddm_types, SFF8472_DDM_TYPE_IMPL))
+               return (0);
+
+       if_sffpage_init(&ddm, ifname, IFSFF_ADDR_DDM, 0);
+       if (ioctl(s, SIOCGIFSFFPAGE, (caddr_t)&ddm) == -1)
+               return (-1);
+
+       if (dump)
+               if_sffpage_dump(ifname, &ddm);
+
+       if (ISSET(ddm_types, SFF8472_DDM_TYPE_CAL_EXT)) {
+               printf("\t" "calibration: external "
+                   "(WARNING: needs more code)\n");
+       }
+
+       printf("\t" "temperature: " SFF_TEMP_FMT "\n",
+           if_sff_int(&ddm, SFF8472_DDM_TEMP) / SFF_TEMP_FACTOR);
+       printf("\t" "vcc: " SFF_VCC_FMT "\n",
+           if_sff_uint(&ddm, SFF8472_DDM_VCC) / SFF_VCC_FACTOR);
+       printf("\t" "tx-bias: " SFF_BIAS_FMT "\n",
+           if_sff_uint(&ddm, SFF8472_DDM_TX_BIAS) / SFF_BIAS_FACTOR);
+       printf("\t" "tx-power: " SFF_POWER_FMT "\n",
+           if_sff_uint(&ddm, SFF8472_DDM_TX_POWER) / SFF_POWER_FACTOR);
+       printf("\t" "rx-power: " SFF_POWER_FMT " %s\n",
+           if_sff_uint(&ddm, SFF8472_DDM_RX_POWER) / SFF_POWER_FACTOR,
+           ISSET(ddm_types, SFF8472_DDM_TYPE_AVG_POWER) ? "average" : "OMA");
+
+       return (0);
+}
+
+static void
+hexdump(const void *d, size_t datalen)
+{
+       const uint8_t *data = d;
+       int i, j = 0;
+
+       for (i = 0; i < datalen; i += j) {
+               printf("% 4d: ", i);
+               for (j = 0; j < 16 && i+j < datalen; j++)
+                       printf("%02x ", data[i + j]);
+               while (j++ < 16)
+                       printf("   ");
+               printf("|");
+               for (j = 0; j < 16 && i+j < datalen; j++)
+                       putchar(printable(data[i + j]));
+               printf("|\n");
+       }
+}
+
+#endif /* SMALL */

Reply via email to