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 #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(¶m->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(¶m->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(¶m->reg, reg); + htolem32(¶m->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 + * + * 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */