/* $OpenBSD: sff.c,v 1.21 2019/08/30 03:52:21 deraadt Exp $ */ /* * Copyright (c) 2019 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 #include #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 char *, int, const struct if_sffpage *); static int if_sff8636(int, const char *, int, const struct if_sffpage *); static int if_inf8077(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) { if (errno == ENXIO) { /* try 1 for XFP cos myx which can't switch pages... */ if_sffpage_init(&pg0, ifname, IFSFF_ADDR_EEPROM, 1); if (ioctl(s, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1) return (-1); } else return (-1); } if (dump) if_sffpage_dump(ifname, &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(s, ifname, dump, &pg0); break; case SFF8024_ID_XFP: if (pg0.sff_page != 1) { if_sffpage_init(&pg0, ifname, IFSFF_ADDR_EEPROM, 1); if (ioctl(s, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1) return (-1); if (dump) if_sffpage_dump(ifname, &pg0); } error = if_inf8077(s, ifname, dump, &pg0); break; case SFF8024_ID_QSFP: case SFF8024_ID_QSFP_PLUS: case SFF8024_ID_QSFP28: error = if_sff8636(s, ifname, 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 (;;) { int 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, dist; 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 s, const char *ifname, 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, 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("\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 s, const char *ifname, int dump, const struct if_sffpage *pg1) { if_upper_strings(pg1); return (0); } static int if_sff8636_thresh(int s, const char *ifname, 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, ifname, IFSFF_ADDR_EEPROM, 3); if (ioctl(s, SIOCGIFSFFPAGE, (caddr_t)&pg3) == -1) { if (dump) warn("%s SIOCGIFSFFPAGE page 3", ifname); return (-1); } if (dump) if_sffpage_dump(ifname, &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 s, const char *ifname, 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(s, ifname, 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 */