David Gwynne(da...@gwynne.id.au) on 2019.04.08 19:33:53 +1000: > this updates the ifconfig part of the diff > > it should have the following improvements: > > - actually applying to -current (thanks hrvoje) > - use vis(3) when printing the strings out (thanks deraadt@) > - make the code less special > - use %.02f for the diag values consistently, and more sane units > (thanks mikeb@) > > mikeb also suggested showing dBm instead of watts for power. this adds > it, but uses log10f, which in turn uses libm. is that ok/worth it?
yes, dBm is what you need to see if the link checks out. Actually printing both would be nice. ethtool has Laser output power : 0.4939 mW / -3.06 dBm but if you dont like that, dBm is preferable. Thanks for doing this! > > ix1 now looks like this: > > dlg@ix ifconfig$ sudo ./obj/ifconfig ix1 sff > ix1: identifier SFP (03) > connector: LC (07) > vendor: FINISAR CORP. > product: FTLX8571D3BCL-FC > revision: A > serial: AQG28W3 > date: 2013-10-19 > temperature: 34.88 C > vcc: 3.36 V > tx-bias: 7.97 mA > tx-power: -2.11 dBm > rx-power: -2.13 dBm average > > the other end (so you can compare): > > eait-78-520-16-1#sh int te0/6 trans > Interface Name : TenGigabitEthernet 0/6 > SFP+ is present > SFP+ 6 Serial Base ID fields > SFP+ 6 Id = 0x03 > SFP+ 6 Ext Id = 0x04 > SFP+ 6 Connector = 0x07 > SFP+ 6 Transceiver Code = 0x10 0x00 0x00 0x00 0x00 0x00 > 0x00 0x00 > SFP+ 6 Encoding = 0x06 > SFP+ 6 BR Nominal = 0x67 > SFP+ 6 Length(SFM) Km = 0x00 > SFP+ 6 Length(SFM) 100m = 0x00 > SFP+ 6 Length(OM3) 10m = 0x1e > SFP+ 6 Length(OM2) 10m = 0x08 > SFP+ 6 Length(OM1) 10m = 0x03 > SFP+ 6 Length(Copper-1m/AOC-1m/OM4-10m) = 0x00 > SFP+ 6 Vendor Rev = A > SFP+ 6 Laser Wavelength = 850 nm > SFP+ 6 CheckCodeBase = 0x9e > SFP+ 6 Serial Extended ID fields > SFP+ 6 Options = 0x00 0x1a > SFP+ 6 BR max = 0 > SFP+ 6 BR min = 0 > SFP+ 6 Vendor SN = AQG2A7A > SFP+ 6 Datecode = 131020 > SFP+ 6 CheckCodeExt = 0xc0 > SFP+ 6 Extended Transceiver Code = 0x00 > > SFP+ 6 Diagnostic Information > =================================== > SFP+ 6 Rx Power measurement type = Average > =================================== > SFP+ 6 Temp High Alarm threshold = 78.000C > SFP+ 6 Voltage High Alarm threshold = 3.700V > SFP+ 6 Bias High Alarm threshold = 13.200mA > SFP+ 6 TX Power High Alarm threshold = 0.0000dBm > SFP+ 6 RX Power High Alarm threshold = 0.0000dBm > SFP+ 6 Temp Low Alarm threshold = -13.000C > SFP+ 6 Voltage Low Alarm threshold = 2.900V > SFP+ 6 Bias Low Alarm threshold = 4.000mA > SFP+ 6 TX Power Low Alarm threshold = -5.9998dBm > SFP+ 6 RX Power Low Alarm threshold = -20.0000dBm > =================================== > SFP+ 6 Temp High Warning threshold = 73.000C > SFP+ 6 Voltage High Warning threshold = 3.600V > SFP+ 6 Bias High Warning threshold = 12.600mA > SFP+ 6 TX Power High Warning threshold = -1.0002dBm > SFP+ 6 RX Power High Warning threshold = -1.0002dBm > SFP+ 6 Temp Low Warning threshold = -8.000C > SFP+ 6 Voltage Low Warning threshold = 3.000V > SFP+ 6 Bias Low Warning threshold = 5.000mA > SFP+ 6 TX Power Low Warning threshold = -5.0004dBm > SFP+ 6 RX Power Low Warning threshold = -18.0134dBm > =================================== > SFP+ 6 Temperature = 48.875C > SFP+ 6 Voltage = 3.338V > SFP+ 6 Tx Bias Current = 8.660mA > SFP+ 6 Tx Power = -2.4260dBm > SFP+ 6 Rx Power = -2.2243dBm > =================================== > SFP+ 6 Data Ready state Bar = False > SFP+ 6 Rx LOS state = False > SFP+ 6 Tx Fault state = False > SFP+ 6 Rate Select state = False > SFP+ 6 RS state = False > SFP+ 6 Tx Disable state = False > =================================== > SFP+ 6 Temperature High Alarm Flag = False > SFP+ 6 Voltage High Alarm Flag = False > SFP+ 6 Tx Bias High Alarm Flag = False > SFP+ 6 Tx Power High Alarm Flag = False > SFP+ 6 Rx Power High Alarm Flag = False > SFP+ 6 Temperature Low Alarm Flag = False > SFP+ 6 Voltage Low Alarm Flag = False > SFP+ 6 Tx Bias Low Alarm Flag = False > SFP+ 6 Tx Power Low Alarm Flag = False > SFP+ 6 Rx Power Low Alarm Flag = False > =================================== > SFP+ 6 Temperature High Warning Flag = False > SFP+ 6 Voltage High Warning Flag = False > SFP+ 6 Tx Bias High Warning Flag = False > SFP+ 6 Tx Power High Warning Flag = False > SFP+ 6 Rx Power High Warning Flag = False > SFP+ 6 Temperature Low Warning Flag = False > SFP+ 6 Voltage Low Warning Flag = False > SFP+ 6 Tx Bias Low Warning Flag = False > SFP+ 6 Tx Power Low Warning Flag = False > SFP+ 6 Rx Power Low Warning Flag = False > > 10GBASE-SR > > > On Mon, Apr 08, 2019 at 02:21:36PM +1000, David Gwynne wrote: > > 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: Makefile > =================================================================== > RCS file: /cvs/src/sbin/ifconfig/Makefile,v > retrieving revision 1.14 > diff -u -p -r1.14 Makefile > --- Makefile 3 May 2016 17:52:33 -0000 1.14 > +++ Makefile 8 Apr 2019 09:23:59 -0000 > @@ -1,10 +1,10 @@ > # $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 > +LDADD= -lutil -lm > DPADD= ${LIBUTIL} > > .include <bsd.prog.mk> > Index: ifconfig.c > =================================================================== > RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v > retrieving revision 1.397 > diff -u -p -r1.397 ifconfig.c > --- ifconfig.c 11 Mar 2019 11:25:48 -0000 1.397 > +++ ifconfig.c 8 Apr 2019 09:23:59 -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 > @@ -589,6 +591,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 }, > @@ -4033,6 +4038,22 @@ unsetpwe3neighbor(const char *val, int d > > if (ioctl(s, SIOCDPWE3NEIGHBOR, &req) == -1) > warn("-pweneighbor"); > +} > + > +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: sff.c > =================================================================== > RCS file: sff.c > diff -N sff.c > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ sff.c 8 Apr 2019 09:23:59 -0000 > @@ -0,0 +1,464 @@ > +/* $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 <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> > + > +#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_VCC_FACTOR 10000.0 > +#define SFF_BIAS_FACTOR 500.0 > +#define SFF_POWER_FACTOR 10000.0 > + > +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 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 { > + char dst[8]; > + vis(dst, d[start], VIS_TAB | VIS_NL, 0); > + printf("%s", dst); > + } 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; > + size_t i; > + > + /* YYMMDD */ > + for (i = 0; i < 6; i++) { > + if (!isdigit(d[i])) { > + if_sff_ascii_print(sff, name, start, start + 5); > + return; > + } > + } > + > + 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 float > +if_sff_power2dbm(uint16_t power) > +{ > + return (10.0 * log10f((float)power / 10000.0)); > +} > + > +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("\tcalibration: external " > + "(WARNING: needs more code)\n"); > + } > + > + printf("\ttemperature: %.02f C\n", > + if_sff_int(&ddm, SFF8472_DDM_TEMP) / SFF_TEMP_FACTOR); > + printf("\tvcc: %.02f V\n", > + if_sff_uint(&ddm, SFF8472_DDM_VCC) / SFF_VCC_FACTOR); > + printf("\ttx-bias: %.02f mA\n", > + if_sff_uint(&ddm, SFF8472_DDM_TX_BIAS) / SFF_BIAS_FACTOR); > + printf("\ttx-power: %.02f dBm\n", > + if_sff_power2dbm(if_sff_uint(&ddm, SFF8472_DDM_TX_POWER))); > + printf("\trx-power: %.02f dBm %s\n", > + if_sff_power2dbm(if_sff_uint(&ddm, SFF8472_DDM_RX_POWER)), > + ISSET(ddm_types, SFF8472_DDM_TYPE_AVG_POWER) ? "average" : "OMA"); > + > + 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 */ >