907 lines
25 KiB
C
907 lines
25 KiB
C
/* $OpenBSD: sff.c,v 1.23 2019/10/24 18:54:10 bluhm Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2019 David Gwynne <dlg@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 <math.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 <vis.h>
|
|
|
|
#include "ifconfig.h"
|
|
|
|
#ifndef nitems
|
|
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
|
|
#endif
|
|
|
|
#ifndef ISSET
|
|
#define ISSET(_w, _m) ((_w) & (_m))
|
|
#endif
|
|
|
|
#define SFF_THRESH_HI_ALARM 0
|
|
#define SFF_THRESH_LO_ALARM 1
|
|
#define SFF_THRESH_HI_WARN 2
|
|
#define SFF_THRESH_LO_WARN 3
|
|
#define SFF_THRESH_COUNT 4
|
|
|
|
#define SFF_THRESH_REG(_i) ((_i) * 2)
|
|
|
|
struct sff_thresholds {
|
|
float thresholds[SFF_THRESH_COUNT];
|
|
};
|
|
|
|
struct sff_media_map {
|
|
float factor_wavelength;
|
|
int scale_om1;
|
|
int scale_om2;
|
|
int scale_om3;
|
|
uint8_t connector_type;
|
|
uint8_t wavelength;
|
|
uint8_t dist_smf_m;
|
|
uint8_t dist_smf_km;
|
|
uint8_t dist_om1;
|
|
uint8_t dist_om2;
|
|
uint8_t dist_om3;
|
|
uint8_t dist_cu;
|
|
};
|
|
|
|
#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] = "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] = "HD 4X",
|
|
[SFF8024_ID_HD8X] = "HD 8X",
|
|
[SFF8024_ID_QSFP28] = "QSFP28",
|
|
[SFF8024_ID_CXP2] = "CXP2",
|
|
[SFF8024_ID_CDFP] = "CDFP Style 1/2",
|
|
[SFF8024_ID_HD4X_FAN] = "HD 4X Fanout",
|
|
[SFF8024_ID_HD8X_FAN] = "HD 8X Fanout",
|
|
[SFF8024_ID_CDFP3] = "CDFP Style 3",
|
|
[SFF8024_ID_uQSFP] = "microQSFP",
|
|
[SFF8024_ID_QSFP_DD] = "QSFP-DD",
|
|
};
|
|
|
|
static const char *sff8024_con_names[] = {
|
|
[SFF8024_CON_UNKNOWN] = "Unknown",
|
|
[SFF8024_CON_SC] = "SC",
|
|
[SFF8024_CON_FC_1] = "FC Style 1",
|
|
[SFF8024_CON_FC_2] = "FC Style 2",
|
|
[SFF8024_CON_BNC_TNC] = "BNC/TNC",
|
|
[SFF8024_CON_FC_COAX] = "FC coax headers",
|
|
[SFF8024_CON_FJ] = "FJ",
|
|
[SFF8024_CON_LC] = "LC",
|
|
[SFF8024_CON_MT_RJ] = "MT-RJ",
|
|
[SFF8024_CON_MU] = "MU",
|
|
[SFF8024_CON_SG] = "SG",
|
|
[SFF8024_CON_O_PIGTAIL] = "AOC",
|
|
[SFF8024_CON_MPO_1x12] = "MPO 1x12",
|
|
[SFF8024_CON_MPO_2x16] = "MPO 2x16",
|
|
[SFF8024_CON_HSSDC2] = "HSSDC II",
|
|
[SFF8024_CON_Cu_PIGTAIL]
|
|
= "DAC",
|
|
[SFF8024_CON_RJ45] = "RJ45",
|
|
[SFF8024_CON_NO] = "No 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_DIST_SMF_KM 14
|
|
#define SFF8472_DIST_SMF_M 15
|
|
#define SFF8472_DIST_OM2 16
|
|
#define SFF8472_DIST_OM1 17
|
|
#define SFF8472_DIST_CU 18
|
|
#define SFF8472_DIST_OM3 19
|
|
#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_WAVELENGTH 60
|
|
#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 */
|
|
|
|
static const struct sff_media_map sff8472_media_map = {
|
|
.connector_type = SFF8472_CON,
|
|
.wavelength = SFF8472_WAVELENGTH,
|
|
.factor_wavelength = 1.0,
|
|
.dist_smf_m = SFF8472_DIST_SMF_M,
|
|
.dist_smf_km = SFF8472_DIST_SMF_KM,
|
|
.dist_om1 = SFF8472_DIST_OM1,
|
|
.scale_om1 = 10,
|
|
.dist_om2 = SFF8472_DIST_OM2,
|
|
.scale_om2 = 10,
|
|
.dist_om3 = SFF8472_DIST_OM3,
|
|
.scale_om3 = 20,
|
|
.dist_cu = SFF8472_DIST_CU,
|
|
};
|
|
|
|
/*
|
|
* page 0xa2
|
|
*/
|
|
#define SFF8472_AW_TEMP 0
|
|
#define SFF8472_AW_VCC 8
|
|
#define SFF8472_AW_TX_BIAS 16
|
|
#define SFF8472_AW_TX_POWER 24
|
|
#define SFF8472_AW_RX_POWER 32
|
|
#define ALRM_HIGH 0
|
|
#define ALRM_LOW 2
|
|
#define WARN_HIGH 4
|
|
#define WARN_LOW 6
|
|
#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_VCC_FACTOR 10000.0
|
|
#define SFF_BIAS_FACTOR 500.0
|
|
#define SFF_POWER_FACTOR 10000.0
|
|
|
|
/*
|
|
* QSFP is defined by SFF-8436, but the management interface is
|
|
* updated and maintained by SFF-8636.
|
|
*/
|
|
|
|
#define SFF8436_STATUS1 1
|
|
#define SFF8436_STATUS2 2
|
|
#define SFF8436_STATUS2_DNR (1 << 0) /* Data_Not_Ready */
|
|
#define SFF8436_STATUS2_INTL (1 << 1) /* Interrupt output state */
|
|
#define SFF8436_STATUS2_FLAT_MEM (1 << 2) /* Upper memory flat/paged */
|
|
|
|
#define SFF8436_TEMP 22
|
|
#define SFF8436_VCC 26
|
|
|
|
#define SFF8436_CHANNELS 4 /* number of TX and RX channels */
|
|
#define SFF8436_RX_POWER_BASE 34
|
|
#define SFF8436_RX_POWER(_i) (SFF8436_RX_POWER_BASE + ((_i) * 2))
|
|
#define SFF8436_TX_BIAS_BASE 42
|
|
#define SFF8436_TX_BIAS(_i) (SFF8436_TX_BIAS_BASE + ((_i) * 2))
|
|
#define SFF8436_TX_POWER_BASE 50
|
|
#define SFF8436_TX_POWER(_i) (SFF8436_TX_POWER_BASE + ((_i) * 2))
|
|
|
|
/* Upper Page 00h */
|
|
|
|
#define SFF8436_MAXCASETEMP 190 /* C */
|
|
#define SFF8436_MAXCASETEMP_DEFAULT 70 /* if SFF8436_MAXCASETEMP is 0 */
|
|
|
|
/* Upper page 03h */
|
|
|
|
#define SFF8436_AW_TEMP 128
|
|
#define SFF8436_AW_VCC 144
|
|
#define SFF8436_AW_RX_POWER 176
|
|
#define SFF8436_AW_TX_BIAS 184
|
|
#define SFF8436_AW_TX_POWER 192
|
|
|
|
/*
|
|
* XFP stuff is defined by INF-8077.
|
|
*
|
|
* The "Serial ID Memory Map" on page 1 contains the interesting strings
|
|
*/
|
|
|
|
/* SFF-8636 and INF-8077 share a layout for various strings */
|
|
|
|
#define UPPER_CON 130 /* connector type */
|
|
#define UPPER_DIST_SMF 142
|
|
#define UPPER_DIST_OM3 143
|
|
#define UPPER_DIST_OM2 144
|
|
#define UPPER_DIST_OM1 145
|
|
#define UPPER_DIST_CU 146
|
|
#define UPPER_WAVELENGTH 186
|
|
|
|
#define UPPER_VENDOR_START 148
|
|
#define UPPER_VENDOR_END 163
|
|
#define UPPER_PRODUCT_START 168
|
|
#define UPPER_PRODUCT_END 183
|
|
#define UPPER_REVISION_START 184
|
|
#define UPPER_REVISION_END 185
|
|
#define UPPER_SERIAL_START 196
|
|
#define UPPER_SERIAL_END 211
|
|
#define UPPER_DATECODE 212
|
|
#define UPPER_LOT_START 218
|
|
#define UPPER_LOT_END 219
|
|
|
|
static const struct sff_media_map upper_media_map = {
|
|
.connector_type = UPPER_CON,
|
|
.wavelength = UPPER_WAVELENGTH,
|
|
.factor_wavelength = 20.0,
|
|
.dist_smf_m = 0,
|
|
.dist_smf_km = UPPER_DIST_SMF,
|
|
.dist_om1 = UPPER_DIST_OM1,
|
|
.scale_om1 = 1,
|
|
.dist_om2 = UPPER_DIST_OM1,
|
|
.scale_om2 = 1,
|
|
.dist_om3 = UPPER_DIST_OM3,
|
|
.scale_om3 = 2,
|
|
.dist_cu = UPPER_DIST_CU,
|
|
};
|
|
|
|
static void hexdump(const void *, size_t);
|
|
static int if_sff8472(int, const struct if_sffpage *);
|
|
static int if_sff8636(int, const struct if_sffpage *);
|
|
static int if_inf8077(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, 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 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 dump)
|
|
{
|
|
struct if_sffpage pg0;
|
|
int error = 0;
|
|
uint8_t id, ext_id;
|
|
|
|
if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 0);
|
|
if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1) {
|
|
if (errno == ENXIO) {
|
|
/* try 1 for XFP cos myx which can't switch pages... */
|
|
if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 1);
|
|
if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
|
|
return (-1);
|
|
} else
|
|
return (-1);
|
|
}
|
|
|
|
if (dump)
|
|
if_sffpage_dump(&pg0);
|
|
|
|
id = pg0.sff_data[0]; /* SFF8472_ID */
|
|
|
|
printf("\ttransceiver: %s ", sff_id_name(id));
|
|
switch (id) {
|
|
case SFF8024_ID_SFP:
|
|
ext_id = pg0.sff_data[SFF8472_EXT_ID];
|
|
if (ext_id != SFF8472_EXT_ID_2WIRE) {
|
|
printf("extended-id %02xh\n", ext_id);
|
|
break;
|
|
}
|
|
/* FALLTHROUGH */
|
|
case SFF8024_ID_GBIC:
|
|
error = if_sff8472(dump, &pg0);
|
|
break;
|
|
case SFF8024_ID_XFP:
|
|
if (pg0.sff_page != 1) {
|
|
if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 1);
|
|
if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
|
|
return (-1);
|
|
if (dump)
|
|
if_sffpage_dump(&pg0);
|
|
}
|
|
error = if_inf8077(dump, &pg0);
|
|
break;
|
|
case SFF8024_ID_QSFP:
|
|
case SFF8024_ID_QSFP_PLUS:
|
|
case SFF8024_ID_QSFP28:
|
|
error = if_sff8636(dump, &pg0);
|
|
break;
|
|
default:
|
|
printf("\n");
|
|
break;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
if_sff_ascii_print(const struct if_sffpage *sff, const char *name,
|
|
size_t start, size_t end, const char *trailer)
|
|
{
|
|
const uint8_t *d = sff->sff_data;
|
|
int ch;
|
|
|
|
for (;;) {
|
|
ch = d[start];
|
|
if (!isspace(ch) && ch != '\0')
|
|
break;
|
|
|
|
start++;
|
|
if (start == end)
|
|
return;
|
|
}
|
|
|
|
printf("%s", name);
|
|
|
|
for (;;) {
|
|
ch = d[end];
|
|
if (!isspace(ch) && ch != '\0')
|
|
break;
|
|
|
|
end--;
|
|
}
|
|
|
|
do {
|
|
char dst[8];
|
|
vis(dst, d[start], VIS_TAB | VIS_NL, 0);
|
|
printf("%s", dst);
|
|
} while (++start <= end);
|
|
|
|
printf("%s", trailer);
|
|
}
|
|
|
|
static void
|
|
if_sff_date_print(const struct if_sffpage *sff, const char *name,
|
|
size_t start, const char *trailer)
|
|
{
|
|
const uint8_t *d = sff->sff_data + start;
|
|
size_t i;
|
|
|
|
/* YYMMDD */
|
|
for (i = 0; i < 6; i++) {
|
|
if (!isdigit(d[i])) {
|
|
if_sff_ascii_print(sff, name, start,
|
|
start + 5, trailer);
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf("%s20%c%c-%c%c-%c%c%s", name,
|
|
d[0], d[1], d[2], d[3], d[4], d[5], trailer);
|
|
}
|
|
|
|
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 float
|
|
if_sff_power2dbm(const struct if_sffpage *sff, size_t start)
|
|
{
|
|
const uint8_t *d = sff->sff_data + start;
|
|
|
|
int power = d[0] << 8 | d[1];
|
|
return (10.0 * log10f((float)power / 10000.0));
|
|
}
|
|
|
|
static void
|
|
if_sff_printalarm(const char *unit, int range, float actual,
|
|
float alrm_high, float alrm_low, float warn_high, float warn_log)
|
|
{
|
|
printf("%.02f%s", actual, unit);
|
|
if (range == 1)
|
|
printf(" (low %.02f%s, high %.02f%s)", alrm_low,
|
|
unit, alrm_high, unit);
|
|
|
|
if(actual > alrm_high || actual < alrm_low)
|
|
printf(" [ALARM]");
|
|
else if(actual > warn_high || actual < warn_log)
|
|
printf(" [WARNING]");
|
|
}
|
|
|
|
static void
|
|
if_sff_printdist(const char *type, int value, int scale)
|
|
{
|
|
int distance = value * scale;
|
|
|
|
if (value == 0)
|
|
return;
|
|
|
|
if (distance < 10000)
|
|
printf (", %s%u%s", value > 254 ? ">" : "", distance, type);
|
|
else
|
|
printf (", %s%0.1fk%s", value > 254 ? ">" : "",
|
|
distance / 1000.0, type);
|
|
}
|
|
|
|
static void
|
|
if_sff_printmedia(const struct if_sffpage *pg, const struct sff_media_map *m)
|
|
{
|
|
uint8_t con;
|
|
unsigned int wavelength;
|
|
|
|
con = pg->sff_data[m->connector_type];
|
|
printf("%s", sff_con_name(con));
|
|
|
|
wavelength = if_sff_uint(pg, m->wavelength);
|
|
switch (wavelength) {
|
|
case 0x0000:
|
|
/* not known or is unavailable */
|
|
break;
|
|
/* Copper Cable */
|
|
case 0x0100: /* SFF-8431 Appendix E */
|
|
case 0x0400: /* SFF-8431 limiting */
|
|
case 0x0c00: /* SFF-8431 limiting and FC-PI-4 limiting */
|
|
break;
|
|
default:
|
|
printf(", %.f nm", wavelength / m->factor_wavelength);
|
|
}
|
|
|
|
if (m->dist_smf_m != 0 &&
|
|
pg->sff_data[m->dist_smf_m] > 0 &&
|
|
pg->sff_data[m->dist_smf_m] < 255)
|
|
if_sff_printdist("m SMF", pg->sff_data[m->dist_smf_m], 100);
|
|
else
|
|
if_sff_printdist("km SMF", pg->sff_data[m->dist_smf_km], 1);
|
|
if_sff_printdist("m OM1", pg->sff_data[m->dist_om1], m->scale_om1);
|
|
if_sff_printdist("m OM2", pg->sff_data[m->dist_om2], m->scale_om2);
|
|
if_sff_printdist("m OM3", pg->sff_data[m->dist_om3], m->scale_om3);
|
|
if_sff_printdist("m", pg->sff_data[m->dist_cu], 1);
|
|
}
|
|
|
|
static int
|
|
if_sff8472(int dump, const struct if_sffpage *pg0)
|
|
{
|
|
struct if_sffpage ddm;
|
|
uint8_t ddm_types;
|
|
|
|
if_sff_printmedia(pg0, &sff8472_media_map);
|
|
|
|
printf("\n\tmodel: ");
|
|
if_sff_ascii_print(pg0, "",
|
|
SFF8472_VENDOR_START, SFF8472_VENDOR_END, " ");
|
|
if_sff_ascii_print(pg0, "",
|
|
SFF8472_PRODUCT_START, SFF8472_PRODUCT_END, "");
|
|
if_sff_ascii_print(pg0, " rev ",
|
|
SFF8472_REVISION_START, SFF8472_REVISION_END, "");
|
|
|
|
if_sff_ascii_print(pg0, "\n\tserial: ",
|
|
SFF8472_SERIAL_START, SFF8472_SERIAL_END, ", ");
|
|
if_sff_date_print(pg0, "date: ", SFF8472_DATECODE, "\n");
|
|
|
|
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, IFSFF_ADDR_DDM, 0);
|
|
if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&ddm) == -1)
|
|
return (-1);
|
|
|
|
if (dump)
|
|
if_sffpage_dump(&ddm);
|
|
|
|
if (ISSET(ddm_types, SFF8472_DDM_TYPE_CAL_EXT)) {
|
|
printf("\tcalibration: external "
|
|
"(WARNING: needs more code)\n");
|
|
}
|
|
|
|
printf("\tvoltage: ");
|
|
if_sff_printalarm(" V", 0,
|
|
if_sff_uint(&ddm, SFF8472_DDM_VCC) / SFF_VCC_FACTOR,
|
|
if_sff_uint(&ddm, SFF8472_AW_VCC + ALRM_HIGH) / SFF_VCC_FACTOR,
|
|
if_sff_uint(&ddm, SFF8472_AW_VCC + ALRM_LOW) / SFF_VCC_FACTOR,
|
|
if_sff_uint(&ddm, SFF8472_AW_VCC + WARN_HIGH) / SFF_VCC_FACTOR,
|
|
if_sff_uint(&ddm, SFF8472_AW_VCC + WARN_LOW) / SFF_VCC_FACTOR);
|
|
|
|
printf(", bias current: ");
|
|
if_sff_printalarm(" mA", 0,
|
|
if_sff_uint(&ddm, SFF8472_DDM_TX_BIAS) / SFF_BIAS_FACTOR,
|
|
if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + ALRM_HIGH) / SFF_BIAS_FACTOR,
|
|
if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + ALRM_LOW) / SFF_BIAS_FACTOR,
|
|
if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + WARN_HIGH) / SFF_BIAS_FACTOR,
|
|
if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + WARN_LOW) / SFF_BIAS_FACTOR);
|
|
|
|
printf("\n\ttemp: ");
|
|
if_sff_printalarm(" C", 1,
|
|
if_sff_int(&ddm, SFF8472_DDM_TEMP) / SFF_TEMP_FACTOR,
|
|
if_sff_int(&ddm, SFF8472_AW_TEMP + ALRM_HIGH) / SFF_TEMP_FACTOR,
|
|
if_sff_int(&ddm, SFF8472_AW_TEMP + ALRM_LOW) / SFF_TEMP_FACTOR,
|
|
if_sff_int(&ddm, SFF8472_AW_TEMP + WARN_HIGH) / SFF_TEMP_FACTOR,
|
|
if_sff_int(&ddm, SFF8472_AW_TEMP + WARN_LOW) / SFF_TEMP_FACTOR);
|
|
|
|
printf("\n\ttx: ");
|
|
if_sff_printalarm(" dBm", 1,
|
|
if_sff_power2dbm(&ddm, SFF8472_DDM_TX_POWER),
|
|
if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + ALRM_HIGH),
|
|
if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + ALRM_LOW),
|
|
if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + WARN_HIGH),
|
|
if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + WARN_LOW));
|
|
|
|
printf("\n\trx: ");
|
|
if_sff_printalarm(" dBm", 1,
|
|
if_sff_power2dbm(&ddm, SFF8472_DDM_RX_POWER),
|
|
if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + ALRM_HIGH),
|
|
if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + ALRM_LOW),
|
|
if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + WARN_HIGH),
|
|
if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + WARN_LOW));
|
|
|
|
putchar('\n');
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
if_upper_strings(const struct if_sffpage *pg)
|
|
{
|
|
if_sff_printmedia(pg, &upper_media_map);
|
|
|
|
printf("\n\tmodel: ");
|
|
if_sff_ascii_print(pg, "",
|
|
UPPER_VENDOR_START, UPPER_VENDOR_END, " ");
|
|
if_sff_ascii_print(pg, "",
|
|
UPPER_PRODUCT_START, UPPER_PRODUCT_END, "");
|
|
if_sff_ascii_print(pg, " rev ",
|
|
UPPER_REVISION_START, UPPER_REVISION_END, "");
|
|
|
|
if_sff_ascii_print(pg, "\n\tserial: ",
|
|
UPPER_SERIAL_START, UPPER_SERIAL_END, " ");
|
|
if_sff_date_print(pg, "date: ", UPPER_DATECODE, " ");
|
|
if_sff_ascii_print(pg, "lot: ",
|
|
UPPER_LOT_START, UPPER_LOT_END, "");
|
|
|
|
putchar('\n');
|
|
}
|
|
|
|
static int
|
|
if_inf8077(int dump, const struct if_sffpage *pg1)
|
|
{
|
|
if_upper_strings(pg1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
if_sff8636_thresh(int dump, const struct if_sffpage *pg0)
|
|
{
|
|
struct if_sffpage pg3;
|
|
unsigned int i;
|
|
struct sff_thresholds temp, vcc, tx, rx, bias;
|
|
|
|
if_sffpage_init(&pg3, IFSFF_ADDR_EEPROM, 3);
|
|
if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg3) == -1) {
|
|
if (dump)
|
|
warn("%s SIOCGIFSFFPAGE page 3", ifname);
|
|
return (-1);
|
|
}
|
|
|
|
if (dump)
|
|
if_sffpage_dump(&pg3);
|
|
|
|
if (pg3.sff_data[0x7f] != 3) { /* just in case... */
|
|
if (dump) {
|
|
warnx("%s SIOCGIFSFFPAGE: page select unsupported",
|
|
ifname);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
for (i = 0; i < SFF_THRESH_COUNT; i++) {
|
|
temp.thresholds[i] = if_sff_int(&pg3,
|
|
SFF8436_AW_TEMP + SFF_THRESH_REG(i)) / SFF_TEMP_FACTOR;
|
|
|
|
vcc.thresholds[i] = if_sff_uint(&pg3,
|
|
SFF8436_AW_VCC + SFF_THRESH_REG(i)) / SFF_VCC_FACTOR;
|
|
|
|
rx.thresholds[i] = if_sff_power2dbm(&pg3,
|
|
SFF8436_AW_RX_POWER + SFF_THRESH_REG(i));
|
|
|
|
bias.thresholds[i] = if_sff_uint(&pg3,
|
|
SFF8436_AW_TX_BIAS + SFF_THRESH_REG(i)) / SFF_BIAS_FACTOR;
|
|
|
|
tx.thresholds[i] = if_sff_power2dbm(&pg3,
|
|
SFF8436_AW_TX_POWER + SFF_THRESH_REG(i));
|
|
}
|
|
|
|
printf("\ttemp: ");
|
|
if_sff_printalarm(" C", 1,
|
|
if_sff_int(&pg3, SFF8436_TEMP) / SFF_TEMP_FACTOR,
|
|
temp.thresholds[SFF_THRESH_HI_ALARM],
|
|
temp.thresholds[SFF_THRESH_LO_ALARM],
|
|
temp.thresholds[SFF_THRESH_HI_WARN],
|
|
temp.thresholds[SFF_THRESH_LO_WARN]);
|
|
printf("\n");
|
|
|
|
printf("\tvoltage: ");
|
|
if_sff_printalarm(" V", 1,
|
|
if_sff_uint(&pg3, SFF8436_VCC) / SFF_VCC_FACTOR,
|
|
vcc.thresholds[SFF_THRESH_HI_ALARM],
|
|
vcc.thresholds[SFF_THRESH_LO_ALARM],
|
|
vcc.thresholds[SFF_THRESH_HI_WARN],
|
|
vcc.thresholds[SFF_THRESH_LO_WARN]);
|
|
printf("\n");
|
|
|
|
for (i = 0; i < SFF8436_CHANNELS; i++) {
|
|
unsigned int channel = i + 1;
|
|
|
|
printf("\tchannel %u bias current: ", channel);
|
|
if_sff_printalarm(" mA", 1,
|
|
if_sff_uint(&pg3, SFF8436_TX_BIAS(i)) / SFF_BIAS_FACTOR,
|
|
bias.thresholds[SFF_THRESH_HI_ALARM],
|
|
bias.thresholds[SFF_THRESH_LO_ALARM],
|
|
bias.thresholds[SFF_THRESH_HI_WARN],
|
|
bias.thresholds[SFF_THRESH_LO_WARN]);
|
|
printf("\n");
|
|
|
|
printf("\tchannel %u tx: ", channel);
|
|
if_sff_printalarm(" dBm", 1,
|
|
if_sff_power2dbm(&pg3, SFF8436_TX_POWER(i)),
|
|
tx.thresholds[SFF_THRESH_HI_ALARM],
|
|
tx.thresholds[SFF_THRESH_LO_ALARM],
|
|
tx.thresholds[SFF_THRESH_HI_WARN],
|
|
tx.thresholds[SFF_THRESH_LO_WARN]);
|
|
printf("\n");
|
|
|
|
printf("\tchannel %u rx: ", channel);
|
|
if_sff_printalarm(" dBm", 1,
|
|
if_sff_power2dbm(&pg3, SFF8436_RX_POWER(i)),
|
|
rx.thresholds[SFF_THRESH_HI_ALARM],
|
|
rx.thresholds[SFF_THRESH_LO_ALARM],
|
|
rx.thresholds[SFF_THRESH_HI_WARN],
|
|
rx.thresholds[SFF_THRESH_LO_WARN]);
|
|
printf("\n");
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
if_sff8636(int dump, const struct if_sffpage *pg0)
|
|
{
|
|
int16_t temp;
|
|
uint8_t maxcasetemp;
|
|
uint8_t flat;
|
|
unsigned int i;
|
|
|
|
if_upper_strings(pg0);
|
|
|
|
if (pg0->sff_data[SFF8436_STATUS2] & SFF8436_STATUS2_DNR) {
|
|
printf("\tmonitor data not ready\n");
|
|
return (0);
|
|
}
|
|
|
|
maxcasetemp = pg0->sff_data[SFF8436_MAXCASETEMP];
|
|
if (maxcasetemp == 0x00)
|
|
maxcasetemp = SFF8436_MAXCASETEMP_DEFAULT;
|
|
printf("\tmax case temp: %u C\n", maxcasetemp);
|
|
|
|
temp = if_sff_int(pg0, SFF8436_TEMP);
|
|
/* the temp reading look unset, assume the rest will be unset too */
|
|
if ((uint16_t)temp == 0 || (uint16_t)temp == 0xffffU) {
|
|
if (!dump)
|
|
return (0);
|
|
}
|
|
|
|
flat = pg0->sff_data[SFF8436_STATUS2] & SFF8436_STATUS2_FLAT_MEM;
|
|
if (!flat && if_sff8636_thresh(dump, pg0) == 0) {
|
|
if (!dump)
|
|
return (0);
|
|
}
|
|
|
|
printf("\t");
|
|
printf("temp: %.02f%s", temp / SFF_TEMP_FACTOR, " C");
|
|
printf(", ");
|
|
printf("voltage: %.02f%s",
|
|
if_sff_uint(pg0, SFF8436_VCC) / SFF_VCC_FACTOR, " V");
|
|
printf("\n");
|
|
|
|
for (i = 0; i < SFF8436_CHANNELS; i++) {
|
|
printf("\t");
|
|
printf("channel %u: ", i + 1);
|
|
printf("bias current: %.02f mA",
|
|
if_sff_uint(pg0, SFF8436_TX_BIAS(i)) / SFF_BIAS_FACTOR);
|
|
printf(", ");
|
|
printf("rx: %.02f dBm",
|
|
if_sff_power2dbm(pg0, SFF8436_RX_POWER(i)));
|
|
printf(", ");
|
|
printf("tx: %.02f dBm",
|
|
if_sff_power2dbm(pg0, SFF8436_TX_POWER(i)));
|
|
printf("\n");
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
printable(int ch)
|
|
{
|
|
if (ch == '\0')
|
|
return ('_');
|
|
if (!isprint(ch))
|
|
return ('~');
|
|
|
|
return (ch);
|
|
}
|
|
|
|
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 */
|