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 */
> 

Reply via email to