netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Optics (SFP) monitoring on ixgbe and igbe
@ 2012-11-07 12:27 Aurélien
  2012-11-07 19:58 ` Ben Hutchings
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien @ 2012-11-07 12:27 UTC (permalink / raw)
  To: netdev

Hello,

I'm looking to propose a patch for optics monitoring on igbe and ixgbe
cards which support SFP and SFP+. I have one of each card. While
searching for previous work done by others on this domain, i found the
following:

https://github.com/jelaas/bifrost-build-x86_64/tree/master/all/kernel-x86_64-3.3.0-rc2-1
(the DOM-* files)

I did not yet test this patch (i'll do in a few days), but just by
looking at the patches, do you think this could get incorporated in
the current state, provided some administrative work is done, or would
it require more architectural rework / documentation / other ?

Could you point me to the next steps required for the functionality to
be merged in a future mainline kernel/ethtool ? Is there a better
place than netdev@ to discuss this matter ?

Thanks,
-- 
Aurélien Guillaume

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-07 12:27 Optics (SFP) monitoring on ixgbe and igbe Aurélien
@ 2012-11-07 19:58 ` Ben Hutchings
  2012-11-08  0:39   ` Aurélien
  0 siblings, 1 reply; 17+ messages in thread
From: Ben Hutchings @ 2012-11-07 19:58 UTC (permalink / raw)
  To: footplus; +Cc: netdev

On Wed, 2012-11-07 at 13:27 +0100, Aurélien wrote:
> Hello,
> 
> I'm looking to propose a patch for optics monitoring on igbe and ixgbe
> cards which support SFP and SFP+. I have one of each card. While
> searching for previous work done by others on this domain, i found the
> following:
> 
> https://github.com/jelaas/bifrost-build-x86_64/tree/master/all/kernel-x86_64-3.3.0-rc2-1
> (the DOM-* files)
> 
> I did not yet test this patch (i'll do in a few days), but just by
> looking at the patches, do you think this could get incorporated in
> the current state, provided some administrative work is done, or would
> it require more architectural rework / documentation / other ?
> 
> Could you point me to the next steps required for the functionality to
> be merged in a future mainline kernel/ethtool ? Is there a better
> place than netdev@ to discuss this matter ?

We just added the ETHTOOL_GMODULEINFO and ETHTOOL_GMODULEEEPROM
interface for this purpose.  The diagnostics obviously aren't really an
EEPROM but I don't think that matters.

These drivers should report a type of ETH_MODULE_SFF_8472 (assuming a
module is present) and expose an 'EEPROM' with the real EEPROM in the
first 256 bytes and the diagnostic registers in the next 256 bytes.

Decoding of the diagnostic registers can be done in ethtool itself.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-07 19:58 ` Ben Hutchings
@ 2012-11-08  0:39   ` Aurélien
  2012-11-09 15:08     ` Ben Hutchings
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien @ 2012-11-08  0:39 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev

On Wed, Nov 7, 2012 at 8:58 PM, Ben Hutchings <bhutchings@solarflare.com> wrote:
> On Wed, 2012-11-07 at 13:27 +0100, Aurélien wrote:
>
> We just added the ETHTOOL_GMODULEINFO and ETHTOOL_GMODULEEEPROM
> interface for this purpose.  The diagnostics obviously aren't really an
> EEPROM but I don't think that matters.
>
> These drivers should report a type of ETH_MODULE_SFF_8472 (assuming a
> module is present) and expose an 'EEPROM' with the real EEPROM in the
> first 256 bytes and the diagnostic registers in the next 256 bytes.
>
> Decoding of the diagnostic registers can be done in ethtool itself.
>

Hi Ben,

Thanks for this reply.

I did not see these interfaces in my searches, but from what I gather
from the code, the missing part is the actual decoding of the data in
ethtool.

As soon as I get my card installed in a test server, I'll reboot on a
kernel issued from the davem/net-next master, and will do some tests
to see if ethtool correctly sees an SFF-8472 compliant module, and has
the correct diagnostic data after the eeprom.

Then I will port the decoding part from the kernel-side patches I to
code in ethtool, and will do some basic tests.

Thanks,
Best regards,
-- 
Aurélien Guillaume

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-08  0:39   ` Aurélien
@ 2012-11-09 15:08     ` Ben Hutchings
  2012-11-09 15:31       ` Aurélien
  0 siblings, 1 reply; 17+ messages in thread
From: Ben Hutchings @ 2012-11-09 15:08 UTC (permalink / raw)
  To: footplus; +Cc: netdev

On Thu, 2012-11-08 at 01:39 +0100, Aurélien wrote:
> On Wed, Nov 7, 2012 at 8:58 PM, Ben Hutchings <bhutchings@solarflare.com> wrote:
> > On Wed, 2012-11-07 at 13:27 +0100, Aurélien wrote:
> >
> > We just added the ETHTOOL_GMODULEINFO and ETHTOOL_GMODULEEEPROM
> > interface for this purpose.  The diagnostics obviously aren't really an
> > EEPROM but I don't think that matters.
> >
> > These drivers should report a type of ETH_MODULE_SFF_8472 (assuming a
> > module is present) and expose an 'EEPROM' with the real EEPROM in the
> > first 256 bytes and the diagnostic registers in the next 256 bytes.
> >
> > Decoding of the diagnostic registers can be done in ethtool itself.
> >
> 
> Hi Ben,
> 
> Thanks for this reply.
> 
> I did not see these interfaces in my searches, but from what I gather
> from the code, the missing part is the actual decoding of the data in
> ethtool.
[...]

No, the driver also needs to implement ethtool_ops::get_module_info and
ethtool_ops::get_module_eeprom.  But those should be quite easy to do.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-09 15:08     ` Ben Hutchings
@ 2012-11-09 15:31       ` Aurélien
  2012-11-15 21:36         ` Aurélien
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien @ 2012-11-09 15:31 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev

On Fri, Nov 9, 2012 at 4:08 PM, Ben Hutchings <bhutchings@solarflare.com> wrote:
>
> No, the driver also needs to implement ethtool_ops::get_module_info and
> ethtool_ops::get_module_eeprom.  But those should be quite easy to do.
>

Indeed. But like you said, it should be easy. I shall receive my cards
on monday, i'll try to do the implementation next week.

Best regards,
-- 
Aurélien Guillaume
- By all means break the rules, and break them beautifully,
deliberately and well. That is one of the ends for which they exist.
-- Robert Bringhurst

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-09 15:31       ` Aurélien
@ 2012-11-15 21:36         ` Aurélien
  2012-11-15 22:46           ` Jeff Kirsher
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien @ 2012-11-15 21:36 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev

[-- Attachment #1: Type: text/plain, Size: 1575 bytes --]

> On Fri, Nov 9, 2012 at 4:08 PM, Ben Hutchings <bhutchings@solarflare.com> wrote:
>>
>> No, the driver also needs to implement ethtool_ops::get_module_info and
>> ethtool_ops::get_module_eeprom.  But those should be quite easy to do.
>>

Hi !

I started to implement these operations in ixgbe.

So far, the result is the attached patch, which applies on dave-m's
net-next @ 1ff05fb7114a6b4118e0f7d89fed2659f7131b0a. It's not yet
finished, and since it is my first peek at network drivers I need some
advice on:

- whether the implementation seems correct for ixgbe and all its
supported MAC/PHY combinations ?
- what would be the best way to manage SFF-8472 A0/A2 bank swapping
mechanism for reading A2h ? (it seems I need to lock the whole -
adress change sequence - read A2h - address change again - operation)
in case it's needed. I may not be able to test that, so I may add an
unsupported return code for now.
- Is the supported PHY selection correct, or should other PHYs be
supported ? What should be the rule ?

I have been able to get correct temperature readings with a patched-up
ethtool, so it seems to work correctly on at least my card (Ethernet
controller [0200]: Intel Corporation 82599EB 10-Gigabit Network
Connection [8086:10fb] (rev 01)).

About ethtool, I was thinking about making a -O option for optical
diagnostics, which would have a readable output. I will make a
function to parse the A2 register contents, so it can be reused in
other daemons/libs (SNMP, etc).

Thanks,
Best regards,
-- 
Aurélien Guillaume

[-- Attachment #2: ixgbe-sff8472.patch --]
[-- Type: application/octet-stream, Size: 11887 bytes --]

diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
index 4253733..385b3c1 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
@@ -1006,14 +1006,15 @@ static s32 ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val)
 }
 
 /**
- *  ixgbe_read_i2c_eeprom_82598 - Reads 8 bit word over I2C interface.
+ *  ixgbe_read_i2c_phy_82598 - Reads 8 bit word over I2C interface.
  *  @hw: pointer to hardware structure
- *  @byte_offset: EEPROM byte offset to read
+ *  @dev_addr: SFF-8079 or SFF-8472 bank address to read.
+ *  @byte_offset: EEPROM or DOM byte offset to read
  *  @eeprom_data: value read
  *
  *  Performs 8 byte read operation to SFP module's EEPROM over I2C interface.
  **/
-static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
+static s32 ixgbe_read_i2c_phy_82598(struct ixgbe_hw *hw, u8 dev_addr, u8 byte_offset,
 				       u8 *eeprom_data)
 {
 	s32 status = 0;
@@ -1028,7 +1029,7 @@ static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
 		 * 0xC30D.  These registers are used to talk to the SFP+
 		 * module's EEPROM through the SDA/SCL (I2C) interface.
 		 */
-		sfp_addr = (IXGBE_I2C_EEPROM_DEV_ADDR << 8) + byte_offset;
+		sfp_addr = (dev_addr << 8) + byte_offset;
 		sfp_addr = (sfp_addr | IXGBE_I2C_EEPROM_READ_MASK);
 		hw->phy.ops.write_reg(hw,
 		                      IXGBE_MDIO_PMA_PMD_SDA_SCL_ADDR,
@@ -1068,6 +1069,37 @@ out:
 }
 
 /**
+ *  ixgbe_read_i2c_eeprom_82598 - Reads 8 bit word over I2C interface.
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: EEPROM byte offset to read
+ *  @eeprom_data: value read
+ *
+ *  Performs 8 byte read operation to SFP module's EEPROM over I2C interface.
+ **/
+static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
+				       u8 *eeprom_data)
+{
+	return ixgbe_read_i2c_phy_82598(hw, IXGBE_I2C_EEPROM_DEV_ADDR, byte_offset, eeprom_data);
+}
+
+/**
+ *  ixgbe_read_i2c_dom_82598 - Reads 8 bit word over I2C interface.
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: DOM byte offset to read
+ *  @eeprom_data: value read
+ *
+ *  Performs 8 byte read operation to SFP module's DOM registers over I2C interface.
+ **/
+static s32 ixgbe_read_i2c_dom_82598(struct ixgbe_hw *hw, u8 byte_offset,
+				       u8 *dom_data)
+{
+	return ixgbe_read_i2c_phy_82598(hw, IXGBE_I2C_EEPROM_DEV_ADDR2, byte_offset, dom_data);
+}
+
+
+
+
+/**
  *  ixgbe_get_supported_physical_layer_82598 - Returns physical layer type
  *  @hw: pointer to hardware structure
  *
@@ -1300,6 +1332,7 @@ static struct ixgbe_phy_operations phy_ops_82598 = {
 	.write_reg		= &ixgbe_write_phy_reg_generic,
 	.setup_link		= &ixgbe_setup_phy_link_generic,
 	.setup_link_speed	= &ixgbe_setup_phy_link_speed_generic,
+	.read_i2c_dom	= &ixgbe_read_i2c_dom_82598,
 	.read_i2c_eeprom	= &ixgbe_read_i2c_eeprom_82598,
 	.check_overtemp   = &ixgbe_tn_check_overtemp,
 };
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index e75f5a4..4eb64a6 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -2253,6 +2253,7 @@ static struct ixgbe_phy_operations phy_ops_82599 = {
 	.setup_link_speed	= &ixgbe_setup_phy_link_speed_generic,
 	.read_i2c_byte		= &ixgbe_read_i2c_byte_generic,
 	.write_i2c_byte		= &ixgbe_write_i2c_byte_generic,
+	.read_i2c_dom   	= &ixgbe_read_i2c_dom_generic,
 	.read_i2c_eeprom	= &ixgbe_read_i2c_eeprom_generic,
 	.write_i2c_eeprom	= &ixgbe_write_i2c_eeprom_generic,
 	.check_overtemp		= &ixgbe_tn_check_overtemp,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index a545728..2bf16c3 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -39,6 +39,7 @@
 #include <linux/uaccess.h>
 
 #include "ixgbe.h"
+#include "ixgbe_phy.h"
 
 
 #define IXGBE_ALL_RAR_ENTRIES 16
@@ -2699,6 +2700,100 @@ static int ixgbe_get_ts_info(struct net_device *dev,
 	return 0;
 }
 
+static int ixgbe_get_module_info(struct net_device *dev,
+				       struct ethtool_modinfo *modinfo)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(dev);
+	struct ixgbe_hw *hw = &adapter->hw;
+	u32 status = IXGBE_ERR_PHY_ADDR_INVALID;
+	u8 sff8472_rev = IXGBE_SFF_SFF_8472_UNSUP;
+
+	/* We do not support the operation when SFP is absent/unsupported */
+	if (hw->phy.sfp_type == ixgbe_sfp_type_not_present
+		|| hw->phy.sfp_type == ixgbe_sfp_type_unknown) {
+		return -EOPNOTSUPP;
+	}
+
+	/* Check whether we support SFF-8472 or not */
+	status = hw->phy.ops.read_i2c_eeprom(hw,
+			    IXGBE_SFF_SFF_8472_COMP,
+			    &sff8472_rev);
+
+	if (status == IXGBE_ERR_SWFW_SYNC ||
+	    status == IXGBE_ERR_I2C ||
+	    status == IXGBE_ERR_SFP_NOT_PRESENT)
+		/* Error occured while reading module */
+		return -EIO;
+
+	if (sff8472_rev == IXGBE_SFF_SFF_8472_UNSUP)
+	{
+		/* We have a SFP, but it does not support DOM. */
+		modinfo->type = ETH_MODULE_SFF_8079;
+		modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
+		return 0;
+	}
+		
+	/* We have a SFP which supports a revision of SFF-8472. */
+	modinfo->type = ETH_MODULE_SFF_8472;
+	modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+	return 0;
+}
+
+static int ixgbe_get_module_eeprom(struct net_device *dev,
+					 struct ethtool_eeprom *ee,
+					 u8 *data)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(dev);
+	struct ixgbe_hw *hw = &adapter->hw;
+	u32 status = IXGBE_ERR_PHY_ADDR_INVALID;
+	u8 databyte = 0xFF;
+	int i = 0;
+
+	/* We do not support the operation when SFP is absent/unsupported */
+	if (hw->phy.sfp_type == ixgbe_sfp_type_not_present
+		|| hw->phy.sfp_type == ixgbe_sfp_type_unknown) {
+		return -EOPNOTSUPP;
+	}
+
+	/* Read the first block, SFF-8079 */
+	for (i = 0; (i < ee->len && i < ETH_MODULE_SFF_8079_LEN); i++)
+	{
+		status = hw->phy.ops.read_i2c_eeprom(hw, i, &databyte);
+		if (status == IXGBE_ERR_SWFW_SYNC ||
+		    status == IXGBE_ERR_I2C ||
+			status == IXGBE_ERR_SFP_NOT_PRESENT)
+			/* Error occured while reading module */
+			return -EIO;
+		data[i] = databyte;
+	}
+
+	/* If the second block is requested, check if SFF-8472 is supported. */
+	if (ee->len > ETH_MODULE_SFF_8079_LEN)
+	{
+		if (data[IXGBE_SFF_SFF_8472_COMP] == IXGBE_SFF_SFF_8472_UNSUP
+			|| !hw->phy.ops.read_i2c_dom)
+			return -EOPNOTSUPP;
+		/* Read the second block, SFF-8472 */
+		for (i = ETH_MODULE_SFF_8079_LEN;
+			 (i < ee->len && i < ETH_MODULE_SFF_8472_LEN); i++)
+		{
+			status = hw->phy.ops.read_i2c_dom(hw, i - ETH_MODULE_SFF_8079_LEN, &databyte);
+			if (status == IXGBE_ERR_SWFW_SYNC ||
+			    status == IXGBE_ERR_I2C ||
+				status == IXGBE_ERR_SFP_NOT_PRESENT)
+				/* Error occured while reading module */
+				return -EIO;
+			data[i] = databyte;
+		}
+	}
+
+	return 0;
+}
+
+
+
+
+
 static const struct ethtool_ops ixgbe_ethtool_ops = {
 	.get_settings           = ixgbe_get_settings,
 	.set_settings           = ixgbe_set_settings,
@@ -2728,6 +2823,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
 	.get_rxnfc		= ixgbe_get_rxnfc,
 	.set_rxnfc		= ixgbe_set_rxnfc,
 	.get_ts_info		= ixgbe_get_ts_info,
+	.get_module_info		= ixgbe_get_module_info,
+	.get_module_eeprom		= ixgbe_get_module_eeprom,
 };
 
 void ixgbe_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
index 71659ed..e9599d9 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -1206,6 +1206,27 @@ s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
 }
 
 /**
+ *  ixgbe_read_i2c_dom_generic - Reads 8 bit DOM word over I2C interface
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: DOM byte offset to read
+ *  @eeprom_data: value read
+ *
+ *  Performs byte read operation to SFP module's DOM info over I2C interface,
+ *  possibly managing the byte swap.
+ **/
+s32 ixgbe_read_i2c_dom_generic(struct ixgbe_hw *hw, u8 byte_offset,
+                                  u8 *dom_data)
+{
+
+	// FIXME: Got to manage the A0/A2 switch according to SFF-8472 (bank swap).
+	
+	return hw->phy.ops.read_i2c_byte(hw, byte_offset,
+	                                 IXGBE_I2C_EEPROM_DEV_ADDR2,
+	                                 dom_data);
+}
+
+
+/**
  *  ixgbe_write_i2c_eeprom_generic - Writes 8 bit EEPROM word over I2C interface
  *  @hw: pointer to hardware structure
  *  @byte_offset: EEPROM byte offset to write
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
index cc18165..1ce0624 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
@@ -30,6 +30,8 @@
 
 #include "ixgbe_type.h"
 #define IXGBE_I2C_EEPROM_DEV_ADDR    0xA0
+#define IXGBE_I2C_EEPROM_DEV_ADDR2   0xA2
+#define IXGBE_I2C_EEPROM_BANK_LEN    0xFF
 
 /* EEPROM byte offsets */
 #define IXGBE_SFF_IDENTIFIER         0x0
@@ -41,6 +43,7 @@
 #define IXGBE_SFF_10GBE_COMP_CODES   0x3
 #define IXGBE_SFF_CABLE_TECHNOLOGY   0x8
 #define IXGBE_SFF_CABLE_SPEC_COMP    0x3C
+#define IXGBE_SFF_SFF_8472_COMP      0x5E
 
 /* Bitmasks */
 #define IXGBE_SFF_DA_PASSIVE_CABLE           0x4
@@ -88,6 +91,14 @@
 #define IXGBE_TN_LASI_STATUS_REG        0x9005
 #define IXGBE_TN_LASI_STATUS_TEMP_ALARM 0x0008
 
+/* SFP+ SFF-8472 Compliance code */
+#define IXGBE_SFF_SFF_8472_UNSUP      0x00
+#define IXGBE_SFF_SFF_8472_REV_9_3    0x01
+#define IXGBE_SFF_SFF_8472_REV_9_5    0x02
+#define IXGBE_SFF_SFF_8472_REV_10_2   0x03
+#define IXGBE_SFF_SFF_8472_REV_10_4   0x04
+#define IXGBE_SFF_SFF_8472_REV_11_0   0x05
+
 s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw);
 s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw);
 s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw);
@@ -126,6 +137,8 @@ s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                  u8 dev_addr, u8 data);
 s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                   u8 *eeprom_data);
+s32 ixgbe_read_i2c_dom_generic(struct ixgbe_hw *hw, u8 byte_offset,
+                                  u8 *dom_data);
 s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                    u8 eeprom_data);
 #endif /* _IXGBE_PHY_H_ */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
index 21915e2..8fa1f15 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
@@ -2882,6 +2882,7 @@ struct ixgbe_phy_operations {
 	s32 (*get_firmware_version)(struct ixgbe_hw *, u16 *);
 	s32 (*read_i2c_byte)(struct ixgbe_hw *, u8, u8, u8 *);
 	s32 (*write_i2c_byte)(struct ixgbe_hw *, u8, u8, u8);
+	s32 (*read_i2c_dom)(struct ixgbe_hw *, u8 , u8 *);
 	s32 (*read_i2c_eeprom)(struct ixgbe_hw *, u8 , u8 *);
 	s32 (*write_i2c_eeprom)(struct ixgbe_hw *, u8, u8);
 	s32 (*check_overtemp)(struct ixgbe_hw *);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
index de4da52..a129aad 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
@@ -879,6 +879,7 @@ static struct ixgbe_phy_operations phy_ops_X540 = {
 	.setup_link_speed       = &ixgbe_setup_phy_link_speed_generic,
 	.read_i2c_byte          = &ixgbe_read_i2c_byte_generic,
 	.write_i2c_byte         = &ixgbe_write_i2c_byte_generic,
+	.read_i2c_dom           = &ixgbe_read_i2c_dom_generic,
 	.read_i2c_eeprom        = &ixgbe_read_i2c_eeprom_generic,
 	.write_i2c_eeprom       = &ixgbe_write_i2c_eeprom_generic,
 	.check_overtemp         = &ixgbe_tn_check_overtemp,

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-15 21:36         ` Aurélien
@ 2012-11-15 22:46           ` Jeff Kirsher
  2012-11-15 23:30             ` Ben Hutchings
  0 siblings, 1 reply; 17+ messages in thread
From: Jeff Kirsher @ 2012-11-15 22:46 UTC (permalink / raw)
  To: footplus; +Cc: Ben Hutchings, netdev

[-- Attachment #1: Type: text/plain, Size: 1818 bytes --]

On 11/15/2012 01:36 PM, Aurélien wrote:
>> On Fri, Nov 9, 2012 at 4:08 PM, Ben Hutchings <bhutchings@solarflare.com> wrote:
>>> No, the driver also needs to implement ethtool_ops::get_module_info and
>>> ethtool_ops::get_module_eeprom.  But those should be quite easy to do.
>>>
> Hi !
>
> I started to implement these operations in ixgbe.
>
> So far, the result is the attached patch, which applies on dave-m's
> net-next @ 1ff05fb7114a6b4118e0f7d89fed2659f7131b0a. It's not yet
> finished, and since it is my first peek at network drivers I need some
> advice on:
>
> - whether the implementation seems correct for ixgbe and all its
> supported MAC/PHY combinations ?
> - what would be the best way to manage SFF-8472 A0/A2 bank swapping
> mechanism for reading A2h ? (it seems I need to lock the whole -
> adress change sequence - read A2h - address change again - operation)
> in case it's needed. I may not be able to test that, so I may add an
> unsupported return code for now.
> - Is the supported PHY selection correct, or should other PHYs be
> supported ? What should be the rule ?
>
> I have been able to get correct temperature readings with a patched-up
> ethtool, so it seems to work correctly on at least my card (Ethernet
> controller [0200]: Intel Corporation 82599EB 10-Gigabit Network
> Connection [8086:10fb] (rev 01)).
>
> About ethtool, I was thinking about making a -O option for optical
> diagnostics, which would have a readable output. I will make a
> function to parse the A2 register contents, so it can be reused in
> other daemons/libs (SNMP, etc).
>
> Thanks,
> Best regards,
Can you please add me <jeffrey.t.kirsher@intel.com> to the CC on future
patches for ixgbe or ixgb, as I will be the one applying the patch to my
queue?

Thanks
Jeff


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-15 22:46           ` Jeff Kirsher
@ 2012-11-15 23:30             ` Ben Hutchings
  2012-11-16  2:23               ` Aurélien
  0 siblings, 1 reply; 17+ messages in thread
From: Ben Hutchings @ 2012-11-15 23:30 UTC (permalink / raw)
  To: footplus; +Cc: netdev, jeffrey.t.kirsher

On Thu, 2012-11-15 at 14:46 -0800, Jeff Kirsher wrote:
> On 11/15/2012 01:36 PM, Aurélien wrote:
> >> On Fri, Nov 9, 2012 at 4:08 PM, Ben Hutchings <bhutchings@solarflare.com> wrote:
> >>> No, the driver also needs to implement ethtool_ops::get_module_info and
> >>> ethtool_ops::get_module_eeprom.  But those should be quite easy to do.
> >>>
> > Hi !
> >
> > I started to implement these operations in ixgbe.
> >
> > So far, the result is the attached patch, which applies on dave-m's
> > net-next @ 1ff05fb7114a6b4118e0f7d89fed2659f7131b0a. It's not yet
> > finished, and since it is my first peek at network drivers I need some
> > advice on:
> >
> > - whether the implementation seems correct for ixgbe and all its
> > supported MAC/PHY combinations ?
> > - what would be the best way to manage SFF-8472 A0/A2 bank swapping
> > mechanism for reading A2h ? (it seems I need to lock the whole -
> > adress change sequence - read A2h - address change again - operation)
> > in case it's needed. I may not be able to test that, so I may add an
> > unsupported return code for now.
> > - Is the supported PHY selection correct, or should other PHYs be
> > supported ? What should be the rule ?
> >
> > I have been able to get correct temperature readings with a patched-up
> > ethtool, so it seems to work correctly on at least my card (Ethernet
> > controller [0200]: Intel Corporation 82599EB 10-Gigabit Network
> > Connection [8086:10fb] (rev 01)).
> >
> > About ethtool, I was thinking about making a -O option for optical
> > diagnostics, which would have a readable output. I will make a
> > function to parse the A2 register contents, so it can be reused in
> > other daemons/libs (SNMP, etc).
> >
> > Thanks,
> > Best regards,
> Can you please add me <jeffrey.t.kirsher@intel.com> to the CC on future
> patches for ixgbe or ixgb, as I will be the one applying the patch to my
> queue?

Yes, Jeff's the one you should be talking to about these drivers.  I
just look after the ethtool utility and API.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-15 23:30             ` Ben Hutchings
@ 2012-11-16  2:23               ` Aurélien
  2012-11-16  6:25                 ` Jeff Kirsher
  2012-11-16 19:38                 ` Ben Hutchings
  0 siblings, 2 replies; 17+ messages in thread
From: Aurélien @ 2012-11-16  2:23 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev, jeffrey.t.kirsher

[-- Attachment #1: Type: text/plain, Size: 3723 bytes --]

On Fri, Nov 16, 2012 at 12:30 AM, Ben Hutchings
<bhutchings@solarflare.com> wrote:
>
> Yes, Jeff's the one you should be talking to about these drivers.  I
> just look after the ethtool utility and API.
>

Ok, so I will discuss the ixgbe patch with Jeff :)

Ben, on the ethtool side, attached is a patch to enable the following
option and output; It's still missing externally calibrated optics
support (my current one is internally calibrated, so that's difficult
to test anything). What do you think ? Is there any other data that
could be interesting to show with -O or -m options ?

I based the text output on Juniper's, which is quite parsable by other
apps, but if someone sees a more appropriate output mode, let me know.

# ./ethtool -h
        ethtool -O|--module-optics DEVNAME	Show module optical diagnostics

# ./ethtool -O eth6
Physical interface: eth6
    Laser bias current                        :  27.378 mA
    Laser output power                        :  0.6980 mW / -1.56 dBm
    Receiver signal average optical power     :  1.0817 mW / 0.34 dBm
    Module temperature                        :  53 degrees C / 128 degrees F
    Module voltage                            :  3.2657 V
    Laser bias current high alarm             :  Off
    Laser bias current low alarm              :  Off
    Laser bias current high warning           :  Off
    Laser bias current low warning            :  Off
    Laser output power high alarm             :  Off
    Laser output power low alarm              :  Off
    Laser output power high warning           :  Off
    Laser output power low warning            :  Off
    Module temperature high alarm             :  Off
    Module temperature low alarm              :  Off
    Module temperature high warning           :  Off
    Module temperature low warning            :  Off
    Module voltage high alarm                 :  Off
    Module voltage low alarm                  :  Off
    Module voltage high warning               :  Off
    Module voltage low warning                :  Off
    Laser rx power high alarm                 :  Off
    Laser rx power low alarm                  :  Off
    Laser rx power high warning               :  Off
    Laser rx power low warning                :  Off
    Laser bias current high alarm threshold   :  100.000 mA
    Laser bias current low alarm threshold    :  2.000 mA
    Laser bias current high warning threshold :  80.000 mA
    Laser bias current low warning threshold  :  4.000 mA
    Laser output power high alarm threshold   :  2.2440 mW / 3.51 dBm
    Laser output power low alarm threshold    :  0.0792 mW / -11.01 dBm
    Laser output power high warning threshold :  1.6830 mW / 2.26 dBm
    Laser output power low warning threshold  :  0.1188 mW / -9.25 dBm
    Module temperature high alarm threshold   :  125 degrees C / 257 degrees F
    Module temperature low alarm threshold    :  -45 degrees C / -49 degrees F
    Module temperature high warning threshold :  115 degrees C / 239 degrees F
    Module temperature low warning threshold  :  -40 degrees C / -40 degrees F
    Module voltage high alarm threshold       :  3.9000 V
    Module voltage low alarm threshold        :  2.7000 V
    Module voltage high warning threshold     :  3.7000 V
    Module voltage low warning threshold      :  2.9000 V
    Laser rx power high alarm threshold       :  2.0000 mW / 3.01 dBm
    Laser rx power low alarm threshold        :  0.0158 mW / -18.01 dBm
    Laser rx power high warning threshold     :  1.5000 mW / 1.76 dBm
    Laser rx power low warning threshold      :  0.0237 mW / -16.25 dBm

Best regards,
-- 
Aurélien Guillaume

[-- Attachment #2: 0001-Implemented-basic-optics-diagnostics-for-SFF-8472-co.patch --]
[-- Type: application/octet-stream, Size: 13855 bytes --]

From f24878834fce1d5464f3175bfe8cad96d9308e16 Mon Sep 17 00:00:00 2001
From: Aurelien Guillaume <aurelien@iwi.me>
Date: Fri, 16 Nov 2012 02:50:00 +0100
Subject: [PATCH]   Implemented basic optics diagnostics for SFF-8472 compliant
   transceivers in ethtool.

---
 Makefile.am  |    2 +-
 configure.ac |    2 +
 ethtool.c    |   45 ++++++++++-
 internal.h   |    3 +
 sfpdiag.c    |  267 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 317 insertions(+), 2 deletions(-)
 create mode 100644 sfpdiag.c

diff --git a/Makefile.am b/Makefile.am
index e33f71f..89a0d1e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,7 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
 		  fec_8xx.c ibm_emac.c ixgb.c ixgbe.c natsemi.c	\
 		  pcnet32.c realtek.c tg3.c marvell.c vioc.c	\
 		  smsc911x.c at76c50x-usb.c sfc.c stmmac.c	\
-		  rxclass.c sfpid.c
+		  rxclass.c sfpid.c sfpdiag.c
 
 TESTS = test-cmdline test-features
 check_PROGRAMS = test-cmdline test-features
diff --git a/configure.ac b/configure.ac
index 0c597c6..b87d5d8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,9 +13,11 @@ AC_PROG_GCC_TRADITIONAL
 AM_PROG_CC_C_O
 
 dnl Checks for libraries.
+AC_CHECK_LIB([m], [log10])
 
 dnl Checks for header files.
 AC_CHECK_HEADERS(sys/ioctl.h)
+AC_CHECK_HEADERS(math.h)
 
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_MSG_CHECKING([whether <linux/types.h> defines big-endian types])
diff --git a/ethtool.c b/ethtool.c
index 3db7fec..e18fc85 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3549,6 +3549,47 @@ static int do_tsinfo(struct cmd_context *ctx)
 	return 0;
 }
 
+static int do_getmoduleoptics(struct cmd_context *ctx)
+{
+	struct ethtool_modinfo modinfo;
+	struct ethtool_eeprom *eeprom;
+	int err;
+
+	modinfo.cmd = ETHTOOL_GMODULEINFO;
+	err = send_ioctl(ctx, &modinfo);
+	if (err < 0) {
+		perror("Cannot get module information");
+		return 1;
+	}
+
+	if (modinfo.type != ETH_MODULE_SFF_8472)
+	{
+		perror("Module is not SFF-8472 (DOM) compliant");
+		return 1;
+	}
+
+	eeprom = calloc(1, sizeof(*eeprom) + modinfo.eeprom_len);
+	if (!eeprom) {
+		perror("Cannot allocate memory for module EEPROM data");
+		return 1;
+	}
+
+	eeprom->cmd = ETHTOOL_GMODULEEEPROM;
+	eeprom->len = modinfo.eeprom_len;
+	eeprom->offset = 0;
+	err = send_ioctl(ctx, eeprom);
+	if (err < 0) {
+		perror("Cannot access module EEPROM");
+		free(eeprom);
+		return 1;
+	}
+
+	printf("Physical interface: %s\n", ctx->devname);
+	sff8472_show_all(eeprom->data);
+	free(eeprom);
+	return 0;
+}
+
 static int do_getmodule(struct cmd_context *ctx)
 {
 	struct ethtool_modinfo modinfo;
@@ -3832,11 +3873,13 @@ static const struct option {
 	{ "--set-priv-flags", 1, do_sprivflags, "Set private flags",
 	  "		FLAG on|off ...\n" },
 	{ "-m|--dump-module-eeprom", 1, do_getmodule,
-	  "Qeuery/Decode Module EEPROM information",
+	  "Query/Decode Module EEPROM information",
 	  "		[ raw on|off ]\n"
 	  "		[ hex on|off ]\n"
 	  "		[ offset N ]\n"
 	  "		[ length N ]\n" },
+	{ "-O|--module-optics", 1, do_getmoduleoptics,
+	  "Show module optical diagnostics" },
 	{ "--show-eee", 1, do_geee, "Show EEE settings"},
 	{ "--set-eee", 1, do_seee, "Set EEE settings",
 	  "		[ eee on|off ]\n"
diff --git a/internal.h b/internal.h
index 4f96fd5..e977a81 100644
--- a/internal.h
+++ b/internal.h
@@ -253,4 +253,7 @@ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
 /* Module EEPROM parsing code */
 void sff8079_show_all(const __u8 *id);
 
+/* Optics diagnostics */
+void sff8472_show_all(const __u8 *id);
+
 #endif /* ETHTOOL_INTERNAL_H__ */
diff --git a/sfpdiag.c b/sfpdiag.c
new file mode 100644
index 0000000..aa7c14c
--- /dev/null
+++ b/sfpdiag.c
@@ -0,0 +1,267 @@
+/*
+ * sfpdiag.c: Implements SFF-8472 optics diagnostics.
+ *
+ * Implemented by Aurelien Guillaume <aurelien@iwi.me>
+ *   based on previous works by Robert Olsson <robert@herjulf.se>
+ *   and SFF-8472 specs by SFF.
+ */
+
+#include <stdio.h>
+#include "internal.h"
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+
+/* EEPROM offsets for DOM */
+#define SFF_A0_DOM                      92
+#define SFF_A0_OPTIONS                  93
+#define SFF_A0_COMP                     94
+
+#define SFF_A0_COMP                     94
+
+/* EEPROM bit values */
+#define SFF_A0_DOM_EXTCAL               (1 << 4)
+#define SFF_A0_DOM_INTCAL               (1 << 5)
+#define SFF_A0_DOM_IMPL                 (1 << 6)
+#define SFF_A0_DOM_PWRT                 (1 << 3)
+
+#define SFF_A0_OPTIONS_AW               (1 << 7)
+
+#define SFF_A2_TEMP                       0x100 + 96
+#define SFF_A2_TEMP_HALRM                 0x100 + 0
+#define SFF_A2_TEMP_LALRM                 0x100 + 2
+#define SFF_A2_TEMP_HWARN                 0x100 + 4
+#define SFF_A2_TEMP_LWARN                 0x100 + 6
+
+#define SFF_A2_VCC                        0x100 + 98
+#define SFF_A2_VCC_HALRM                  0x100 + 8
+#define SFF_A2_VCC_LALRM                  0x100 + 10
+#define SFF_A2_VCC_HWARN                  0x100 + 12
+#define SFF_A2_VCC_LWARN                  0x100 + 14
+
+#define SFF_A2_BIAS                       0x100 + 96
+#define SFF_A2_BIAS_HALRM                 0x100 + 16
+#define SFF_A2_BIAS_LALRM                 0x100 + 18
+#define SFF_A2_BIAS_HWARN                 0x100 + 20
+#define SFF_A2_BIAS_LWARN                 0x100 + 22
+
+#define SFF_A2_TX_PWR                     0x100 + 102
+#define SFF_A2_TX_PWR_HALRM               0x100 + 24
+#define SFF_A2_TX_PWR_LALRM               0x100 + 26
+#define SFF_A2_TX_PWR_HWARN               0x100 + 28
+#define SFF_A2_TX_PWR_LWARN               0x100 + 30
+
+#define SFF_A2_RX_PWR                     0x100 + 104
+#define SFF_A2_RX_PWR_HALRM               0x100 + 32
+#define SFF_A2_RX_PWR_LALRM               0x100 + 34
+#define SFF_A2_RX_PWR_HWARN               0x100 + 36
+#define SFF_A2_RX_PWR_LWARN               0x100 + 38
+
+#define SFF_A2_ALRM_FLG                   0x100 + 112
+#define SFF_A2_WARN_FLG                   0x100 + 116
+
+struct sff8472_diags {
+
+#define MCURR 0
+#define LWARN 1
+#define HWARN 2
+#define LALRM 3
+#define HALRM 4
+
+	/* [5] tables are current, low/high warn, low/high alarm */
+	__u8 supports_dom;      /* Supports DOM */
+	__u8 supports_alarms;   /* Supports alarm/warning thold */
+	__u8 calibrated_int;	/* Is internally calibrated */
+	__u16 bias_cur[5];		/* Measured bias current in 2uA units (cur, l/h warn, l/h alarm) */
+	__u16 tx_power[5];		/* Measured TX Power in 0.1uW units (cur, warn, alarm) */
+	__u16 rx_power[5];		/* Measured RX Power (cur, warn, alarm) */
+	__u8  rx_power_type;    /* 0 = OMA, 1 = Average power */
+	__s16 sfp_temp[5];      /* SFP Temp in 0.1 Celcius (cur, warn, alarm) */
+	__u16 sfp_voltage[5];   /* SFP voltage in 0.1mV units (cur, warn, alarm) */
+
+};
+
+static struct sff8472_aw_flags {
+	const char *str;        /* Human-readable string, null at the end */
+	int offset;             /* A2-relative adress offset */
+	__u8 value;             /* 1-bit mask, alarm is on if offset & value != 0. */
+} sff8472_aw_flags[] =
+{
+	{ "Laser bias current high alarm",   SFF_A2_ALRM_FLG, (1 << 3) },
+	{ "Laser bias current low alarm",    SFF_A2_ALRM_FLG, (1 << 2) },
+	{ "Laser bias current high warning", SFF_A2_WARN_FLG, (1 << 3) },
+	{ "Laser bias current low warning",  SFF_A2_WARN_FLG, (1 << 2) },
+
+	{ "Laser output power high alarm",   SFF_A2_ALRM_FLG, (1 << 1) },
+	{ "Laser output power low alarm",    SFF_A2_ALRM_FLG, (1 << 0) },
+	{ "Laser output power high warning", SFF_A2_WARN_FLG, (1 << 1) },
+	{ "Laser output power low warning",  SFF_A2_WARN_FLG, (1 << 0) },
+
+	{ "Module temperature high alarm",   SFF_A2_ALRM_FLG, (1 << 7) },
+	{ "Module temperature low alarm",    SFF_A2_ALRM_FLG, (1 << 6) },
+	{ "Module temperature high warning", SFF_A2_WARN_FLG, (1 << 7) },
+	{ "Module temperature low warning",  SFF_A2_WARN_FLG, (1 << 6) },
+
+	{ "Module voltage high alarm",   SFF_A2_ALRM_FLG, (1 << 5) },
+	{ "Module voltage low alarm",    SFF_A2_ALRM_FLG, (1 << 4) },
+	{ "Module voltage high warning", SFF_A2_WARN_FLG, (1 << 5) },
+	{ "Module voltage low warning",  SFF_A2_WARN_FLG, (1 << 4) },
+
+	{ "Laser rx power high alarm",   SFF_A2_ALRM_FLG + 1, (1 << 7) },
+	{ "Laser rx power low alarm",    SFF_A2_ALRM_FLG + 1, (1 << 6) },
+	{ "Laser rx power high warning", SFF_A2_WARN_FLG + 1, (1 << 7) },
+	{ "Laser rx power low warning",  SFF_A2_WARN_FLG + 1, (1 << 6) },
+
+	{ NULL, 0, 0 },
+};
+
+#ifdef HAVE_LIBM
+
+static double convert_mw_to_dbm(double mw)
+{
+	return (10.f * log10(mw / 1000.f)) + 30.f;
+}
+
+#endif
+
+/* Externally calibrated SFP calculations */
+#define ECAL(v, s, o) (( ((double) (s>>8)) + (s & 0xFF)) * (double) v + o)	
+
+static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd)
+{
+	sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
+	sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
+	sd->calibrated_int = id[SFF_A0_DOM] & SFF_A0_DOM_INTCAL;
+	sd->rx_power_type = id[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
+
+
+#define OFFSET_TO_U16(offset) (id[(offset)] << 8 | id[(offset) + 1]) 
+
+	sd->bias_cur[MCURR] = OFFSET_TO_U16(SFF_A2_BIAS);
+	sd->bias_cur[HALRM] = OFFSET_TO_U16(SFF_A2_BIAS_HALRM);
+	sd->bias_cur[LALRM] = OFFSET_TO_U16(SFF_A2_BIAS_LALRM);
+	sd->bias_cur[HWARN] = OFFSET_TO_U16(SFF_A2_BIAS_HWARN);
+	sd->bias_cur[LWARN] = OFFSET_TO_U16(SFF_A2_BIAS_LWARN);
+
+	sd->sfp_voltage[MCURR] = OFFSET_TO_U16(SFF_A2_VCC);
+	sd->sfp_voltage[HALRM] = OFFSET_TO_U16(SFF_A2_VCC_HALRM);
+	sd->sfp_voltage[LALRM] = OFFSET_TO_U16(SFF_A2_VCC_LALRM);
+	sd->sfp_voltage[HWARN] = OFFSET_TO_U16(SFF_A2_VCC_HWARN);
+	sd->sfp_voltage[LWARN] = OFFSET_TO_U16(SFF_A2_VCC_LWARN);
+
+	sd->tx_power[MCURR] = OFFSET_TO_U16(SFF_A2_TX_PWR);
+	sd->tx_power[HALRM] = OFFSET_TO_U16(SFF_A2_TX_PWR_HALRM);
+	sd->tx_power[LALRM] = OFFSET_TO_U16(SFF_A2_TX_PWR_LALRM);
+	sd->tx_power[HWARN] = OFFSET_TO_U16(SFF_A2_TX_PWR_HWARN);
+	sd->tx_power[LWARN] = OFFSET_TO_U16(SFF_A2_TX_PWR_LWARN);
+
+	sd->rx_power[MCURR] = OFFSET_TO_U16(SFF_A2_RX_PWR);
+	sd->rx_power[HALRM] = OFFSET_TO_U16(SFF_A2_RX_PWR_HALRM);
+	sd->rx_power[LALRM] = OFFSET_TO_U16(SFF_A2_RX_PWR_LALRM);
+	sd->rx_power[HWARN] = OFFSET_TO_U16(SFF_A2_RX_PWR_HWARN);
+	sd->rx_power[LWARN] = OFFSET_TO_U16(SFF_A2_RX_PWR_LWARN);
+
+	/* Temperature conversions */
+#define OFFSET_TO_TEMP(offset) \
+    ((*(__s8 *)(&id[(offset)])) * 1000 + ((id[(offset) + 1] * 1000) / 256)) / 100;
+	
+	sd->sfp_temp[MCURR] = OFFSET_TO_TEMP(SFF_A2_TEMP);
+	sd->sfp_temp[HALRM] = OFFSET_TO_TEMP(SFF_A2_TEMP_HALRM);
+	sd->sfp_temp[LALRM] = OFFSET_TO_TEMP(SFF_A2_TEMP_LALRM);
+	sd->sfp_temp[HWARN] = OFFSET_TO_TEMP(SFF_A2_TEMP_HWARN);
+	sd->sfp_temp[LWARN] = OFFSET_TO_TEMP(SFF_A2_TEMP_LWARN);
+
+}
+
+void sff8472_show_all(const __u8 *id)
+{
+	struct sff8472_diags sd;
+	char *rx_power_string = NULL;
+	int i;
+
+	sff8472_parse_eeprom(id, &sd);
+
+	if (!sd.supports_dom)
+	{
+		printf("    Optical diagnostics are not supported.\n");
+		return ;
+	}
+
+#define PRINT_BIAS(string, index) \
+	printf("    %-41s :  %.3f mA\n", (string), \
+		   (double)(sd.bias_cur[(index)] / 500.f));
+
+#ifdef HAVE_LIBM
+
+# define PRINT_xX_PWR(string, var, index) \
+	printf("    %-41s :  %.4f mW / %.2f dBm\n", (string), \
+		   (double)((var)[(index)] / 10000.f), \
+		   convert_mw_to_dbm((double)((var)[(index)] / 10000.f)));
+
+#else
+
+# define PRINT_xX_PWR(string, var, index) \
+	printf("    %-41s :  %.4f mW\n", (string), \
+		   (double)((var)[(index)] / 10000.f));
+
+#endif
+
+#define PRINT_TEMP(string, index) \
+	printf("    %-41s :  %.0f degrees C / %.0f degrees F\n", (string), \
+		   (double)(sd.sfp_temp[(index)] / 10.f), \
+		   (double)(sd.sfp_temp[(index)] / 10.f * 1.8f + 32.f));
+
+#define PRINT_VCC(string, index) \
+	printf("    %-41s :  %.4f V\n", (string), \
+		   (double)(sd.sfp_voltage[(index)] / 10000.f));
+
+
+
+
+	PRINT_BIAS("Laser bias current", MCURR);
+	PRINT_xX_PWR("Laser output power", sd.tx_power, MCURR);
+
+	if (!sd.rx_power_type)
+		rx_power_string = "Receiver signal OMA";
+	else
+		rx_power_string = "Receiver signal average optical power";
+
+	PRINT_xX_PWR(rx_power_string, sd.rx_power, MCURR);
+
+	PRINT_TEMP("Module temperature", MCURR);
+	PRINT_VCC("Module voltage", MCURR);
+
+	for (i = 0; sff8472_aw_flags[i].str; ++i)
+	{
+		printf("    %-41s :  %s\n", sff8472_aw_flags[i].str, 
+			   id[sff8472_aw_flags[i].offset] & sff8472_aw_flags[i].value ? "On" : "Off");
+	}	
+
+	PRINT_BIAS("Laser bias current high alarm threshold",   HALRM);
+	PRINT_BIAS("Laser bias current low alarm threshold",    LALRM);
+	PRINT_BIAS("Laser bias current high warning threshold", HWARN);
+	PRINT_BIAS("Laser bias current low warning threshold",  LWARN);
+
+	PRINT_xX_PWR("Laser output power high alarm threshold",   sd.tx_power, HALRM);
+	PRINT_xX_PWR("Laser output power low alarm threshold",    sd.tx_power, LALRM);
+	PRINT_xX_PWR("Laser output power high warning threshold", sd.tx_power, HWARN);
+	PRINT_xX_PWR("Laser output power low warning threshold",  sd.tx_power, LWARN);
+
+	PRINT_TEMP("Module temperature high alarm threshold",   HALRM);
+	PRINT_TEMP("Module temperature low alarm threshold",    LALRM);
+	PRINT_TEMP("Module temperature high warning threshold", HWARN);
+	PRINT_TEMP("Module temperature low warning threshold",  LWARN);
+
+	PRINT_VCC("Module voltage high alarm threshold",   HALRM);
+	PRINT_VCC("Module voltage low alarm threshold",    LALRM);
+	PRINT_VCC("Module voltage high warning threshold", HWARN);
+	PRINT_VCC("Module voltage low warning threshold",  LWARN);
+
+	PRINT_xX_PWR("Laser rx power high alarm threshold",   sd.rx_power, HALRM);
+	PRINT_xX_PWR("Laser rx power low alarm threshold",    sd.rx_power, LALRM);
+	PRINT_xX_PWR("Laser rx power high warning threshold", sd.rx_power, HWARN);
+	PRINT_xX_PWR("Laser rx power low warning threshold",  sd.rx_power, LWARN);
+
+}
+
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-16  2:23               ` Aurélien
@ 2012-11-16  6:25                 ` Jeff Kirsher
  2012-11-16 19:38                 ` Ben Hutchings
  1 sibling, 0 replies; 17+ messages in thread
From: Jeff Kirsher @ 2012-11-16  6:25 UTC (permalink / raw)
  To: footplus; +Cc: Ben Hutchings, netdev, Skidmore, Donald C

[-- Attachment #1: Type: text/plain, Size: 809 bytes --]

On Fri, 2012-11-16 at 03:23 +0100, Aurélien wrote:
> On Fri, Nov 16, 2012 at 12:30 AM, Ben Hutchings
> <bhutchings@solarflare.com> wrote:
> >
> > Yes, Jeff's the one you should be talking to about these drivers.  I
> > just look after the ethtool utility and API.
> >
> 
> Ok, so I will discuss the ixgbe patch with Jeff :)

Aurélien-

As far as your driver changes go and the questions you have, Don
Skidmore (ixgbe maintainer) and I have been discussing your patch and
may have some implementation changes possibly to suggest.  I will talk
with Don more tomorrow about it before I suggest any changes.

As far as your questions regarding ixgbe PHY's, I will have Don respond
your questions.  I have CC'd Don <donald.c.skidmore@intel.com> as well
on this email thread.

Cheers,
Jeff

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-16  2:23               ` Aurélien
  2012-11-16  6:25                 ` Jeff Kirsher
@ 2012-11-16 19:38                 ` Ben Hutchings
  2012-11-18 21:35                   ` Aurélien
  1 sibling, 1 reply; 17+ messages in thread
From: Ben Hutchings @ 2012-11-16 19:38 UTC (permalink / raw)
  To: footplus; +Cc: netdev, jeffrey.t.kirsher

On Fri, 2012-11-16 at 03:23 +0100, Aurélien wrote:
> On Fri, Nov 16, 2012 at 12:30 AM, Ben Hutchings
> <bhutchings@solarflare.com> wrote:
> >
> > Yes, Jeff's the one you should be talking to about these drivers.  I
> > just look after the ethtool utility and API.
> >
> 
> Ok, so I will discuss the ixgbe patch with Jeff :)
> 
> Ben, on the ethtool side, attached is a patch to enable the following
> option and output; It's still missing externally calibrated optics
> support (my current one is internally calibrated, so that's difficult
> to test anything). What do you think ? Is there any other data that
> could be interesting to show with -O or -m options ?
[...]
> --- a/configure.ac
> +++ b/configure.ac
> @@ -13,9 +13,11 @@ AC_PROG_GCC_TRADITIONAL
>  AM_PROG_CC_C_O
>  
>  dnl Checks for libraries.
> +AC_CHECK_LIB([m], [log10])
>  
>  dnl Checks for header files.
>  AC_CHECK_HEADERS(sys/ioctl.h)
> +AC_CHECK_HEADERS(math.h)

This is silly; log10() and <math.h> are part of standard C and -lm is
standard on Unix.  Just use <math.h> and -lm unconditionally.

>  dnl Checks for typedefs, structures, and compiler characteristics.
>  AC_MSG_CHECKING([whether <linux/types.h> defines big-endian types])
> diff --git a/ethtool.c b/ethtool.c
> index 3db7fec..e18fc85 100644
> --- a/ethtool.c
> +++ b/ethtool.c
> @@ -3549,6 +3549,47 @@ static int do_tsinfo(struct cmd_context *ctx)
>         return 0;
>  }
>  
> +static int do_getmoduleoptics(struct cmd_context *ctx)
> +{
> +       struct ethtool_modinfo modinfo;
> +       struct ethtool_eeprom *eeprom;
> +       int err;
> +
> +       modinfo.cmd = ETHTOOL_GMODULEINFO;
> +       err = send_ioctl(ctx, &modinfo);
> +       if (err < 0) {
> +               perror("Cannot get module information");
> +               return 1;
> +       }
> +
> +       if (modinfo.type != ETH_MODULE_SFF_8472)
> +       {
> +               perror("Module is not SFF-8472 (DOM) compliant");
> +               return 1;
> +       }
> +
> +       eeprom = calloc(1, sizeof(*eeprom) + modinfo.eeprom_len);
> +       if (!eeprom) {
> +               perror("Cannot allocate memory for module EEPROM data");
> +               return 1;
> +       }
> +
> +       eeprom->cmd = ETHTOOL_GMODULEEEPROM;
> +       eeprom->len = modinfo.eeprom_len;
> +       eeprom->offset = 0;
> +       err = send_ioctl(ctx, eeprom);
> +       if (err < 0) {
> +               perror("Cannot access module EEPROM");
> +               free(eeprom);
> +               return 1;
> +       }
> +
> +       printf("Physical interface: %s\n", ctx->devname);
> +       sff8472_show_all(eeprom->data);
> +       free(eeprom);
> +       return 0;
> +}

Please merge this with the existing -m option and update the
documentation to say that this covers diagnostics where available.  You
could add a long option alias like --dump-module or --module-info that
covers the two types of information.

>  static int do_getmodule(struct cmd_context *ctx)
>  {
>         struct ethtool_modinfo modinfo;
> @@ -3832,11 +3873,13 @@ static const struct option {
>         { "--set-priv-flags", 1, do_sprivflags, "Set private flags",
>           "             FLAG on|off ...\n" },
>         { "-m|--dump-module-eeprom", 1, do_getmodule,
> -         "Qeuery/Decode Module EEPROM information",
> +         "Query/Decode Module EEPROM information",
>           "             [ raw on|off ]\n"
>           "             [ hex on|off ]\n"
>           "             [ offset N ]\n"
>           "             [ length N ]\n" },
> +       { "-O|--module-optics", 1, do_getmoduleoptics,
> +         "Show module optical diagnostics" },
>         { "--show-eee", 1, do_geee, "Show EEE settings"},
>         { "--set-eee", 1, do_seee, "Set EEE settings",
>           "             [ eee on|off ]\n"
> diff --git a/internal.h b/internal.h
> index 4f96fd5..e977a81 100644
> --- a/internal.h
> +++ b/internal.h
> @@ -253,4 +253,7 @@ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
>  /* Module EEPROM parsing code */
>  void sff8079_show_all(const __u8 *id);
>  
> +/* Optics diagnostics */
> +void sff8472_show_all(const __u8 *id);
> +
>  #endif /* ETHTOOL_INTERNAL_H__ */
> diff --git a/sfpdiag.c b/sfpdiag.c
> new file mode 100644
> index 0000000..aa7c14c
> --- /dev/null
> +++ b/sfpdiag.c
[...]
> +#define SFF_A2_TEMP                       0x100 + 96
> +#define SFF_A2_TEMP_HALRM                 0x100 + 0
[...]
> +#define SFF_A2_ALRM_FLG                   0x100 + 112
> +#define SFF_A2_WARN_FLG                   0x100 + 116

All the above offsets need parentheses around their definitions.

> +struct sff8472_diags {
> +
> +#define MCURR 0
> +#define LWARN 1
> +#define HWARN 2
> +#define LALRM 3
> +#define HALRM 4
> +
> +       /* [5] tables are current, low/high warn, low/high alarm */
> +       __u8 supports_dom;      /* Supports DOM */
> +       __u8 supports_alarms;   /* Supports alarm/warning thold */
> +       __u8 calibrated_int;    /* Is internally calibrated */
> +       __u16 bias_cur[5];              /* Measured bias current in 2uA units (cur, l/h warn, l/h alarm) */
> +       __u16 tx_power[5];              /* Measured TX Power in 0.1uW units (cur, warn, alarm) */
> +       __u16 rx_power[5];              /* Measured RX Power (cur, warn, alarm) */
> +       __u8  rx_power_type;    /* 0 = OMA, 1 = Average power */
> +       __s16 sfp_temp[5];      /* SFP Temp in 0.1 Celcius (cur, warn, alarm) */
> +       __u16 sfp_voltage[5];   /* SFP voltage in 0.1mV units (cur, warn, alarm) */
> +
> +};
> +
> +static struct sff8472_aw_flags {
> +       const char *str;        /* Human-readable string, null at the end */
> +       int offset;             /* A2-relative adress offset */

This is commented as an offset in the A2 'EEPROM' but the offsets
actually used include the 0x100 offset from the start of the
concatenated 'EEPROM'.

> +       __u8 value;             /* 1-bit mask, alarm is on if offset & value != 0. */
> +} sff8472_aw_flags[] =
> +{
> +       { "Laser bias current high alarm",   SFF_A2_ALRM_FLG, (1 << 3) },
> +       { "Laser bias current low alarm",    SFF_A2_ALRM_FLG, (1 << 2) },
> +       { "Laser bias current high warning", SFF_A2_WARN_FLG, (1 << 3) },
> +       { "Laser bias current low warning",  SFF_A2_WARN_FLG, (1 << 2) },
> +
> +       { "Laser output power high alarm",   SFF_A2_ALRM_FLG, (1 << 1) },
> +       { "Laser output power low alarm",    SFF_A2_ALRM_FLG, (1 << 0) },
> +       { "Laser output power high warning", SFF_A2_WARN_FLG, (1 << 1) },
> +       { "Laser output power low warning",  SFF_A2_WARN_FLG, (1 << 0) },
> +
> +       { "Module temperature high alarm",   SFF_A2_ALRM_FLG, (1 << 7) },
> +       { "Module temperature low alarm",    SFF_A2_ALRM_FLG, (1 << 6) },
> +       { "Module temperature high warning", SFF_A2_WARN_FLG, (1 << 7) },
> +       { "Module temperature low warning",  SFF_A2_WARN_FLG, (1 << 6) },
> +
> +       { "Module voltage high alarm",   SFF_A2_ALRM_FLG, (1 << 5) },
> +       { "Module voltage low alarm",    SFF_A2_ALRM_FLG, (1 << 4) },
> +       { "Module voltage high warning", SFF_A2_WARN_FLG, (1 << 5) },
> +       { "Module voltage low warning",  SFF_A2_WARN_FLG, (1 << 4) },
> +
> +       { "Laser rx power high alarm",   SFF_A2_ALRM_FLG + 1, (1 << 7) },
> +       { "Laser rx power low alarm",    SFF_A2_ALRM_FLG + 1, (1 << 6) },
> +       { "Laser rx power high warning", SFF_A2_WARN_FLG + 1, (1 << 7) },
> +       { "Laser rx power low warning",  SFF_A2_WARN_FLG + 1, (1 << 6) },
> +
> +       { NULL, 0, 0 },
> +};
> +
> +#ifdef HAVE_LIBM
> +
> +static double convert_mw_to_dbm(double mw)
> +{
> +       return (10.f * log10(mw / 1000.f)) + 30.f;

Why are all the literals explicitly float and not double?

> +}
> +
> +#endif
> +
> +/* Externally calibrated SFP calculations */
> +#define ECAL(v, s, o) (( ((double) (s>>8)) + (s & 0xFF)) * (double) v + o)     

Please follow kernel coding style for spacing.  checkpatch.pl will show
you what should be changed.

> +static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd)
> +{
> +       sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
> +       sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
> +       sd->calibrated_int = id[SFF_A0_DOM] & SFF_A0_DOM_INTCAL;
> +       sd->rx_power_type = id[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
> +
> +
> +#define OFFSET_TO_U16(offset) (id[(offset)] << 8 | id[(offset) + 1]) 
> +
> +       sd->bias_cur[MCURR] = OFFSET_TO_U16(SFF_A2_BIAS);
> +       sd->bias_cur[HALRM] = OFFSET_TO_U16(SFF_A2_BIAS_HALRM);
> +       sd->bias_cur[LALRM] = OFFSET_TO_U16(SFF_A2_BIAS_LALRM);
> +       sd->bias_cur[HWARN] = OFFSET_TO_U16(SFF_A2_BIAS_HWARN);
> +       sd->bias_cur[LWARN] = OFFSET_TO_U16(SFF_A2_BIAS_LWARN);
> +
> +       sd->sfp_voltage[MCURR] = OFFSET_TO_U16(SFF_A2_VCC);
> +       sd->sfp_voltage[HALRM] = OFFSET_TO_U16(SFF_A2_VCC_HALRM);
> +       sd->sfp_voltage[LALRM] = OFFSET_TO_U16(SFF_A2_VCC_LALRM);
> +       sd->sfp_voltage[HWARN] = OFFSET_TO_U16(SFF_A2_VCC_HWARN);
> +       sd->sfp_voltage[LWARN] = OFFSET_TO_U16(SFF_A2_VCC_LWARN);
> +
> +       sd->tx_power[MCURR] = OFFSET_TO_U16(SFF_A2_TX_PWR);
> +       sd->tx_power[HALRM] = OFFSET_TO_U16(SFF_A2_TX_PWR_HALRM);
> +       sd->tx_power[LALRM] = OFFSET_TO_U16(SFF_A2_TX_PWR_LALRM);
> +       sd->tx_power[HWARN] = OFFSET_TO_U16(SFF_A2_TX_PWR_HWARN);
> +       sd->tx_power[LWARN] = OFFSET_TO_U16(SFF_A2_TX_PWR_LWARN);
> +
> +       sd->rx_power[MCURR] = OFFSET_TO_U16(SFF_A2_RX_PWR);
> +       sd->rx_power[HALRM] = OFFSET_TO_U16(SFF_A2_RX_PWR_HALRM);
> +       sd->rx_power[LALRM] = OFFSET_TO_U16(SFF_A2_RX_PWR_LALRM);
> +       sd->rx_power[HWARN] = OFFSET_TO_U16(SFF_A2_RX_PWR_HWARN);
> +       sd->rx_power[LWARN] = OFFSET_TO_U16(SFF_A2_RX_PWR_LWARN);
> +
> +       /* Temperature conversions */
> +#define OFFSET_TO_TEMP(offset) \
> +    ((*(__s8 *)(&id[(offset)])) * 1000 + ((id[(offset) + 1] * 1000) / 256)) / 100;

This seems awfuly complicated; why not:

#define OFFSET_TO_TEMP(offset) (((s16)OFFSET_TO_U16(offset)) * 10 / 256)

But why round to tenths of a degree here and then round again to whole
degrees celsius/fahrenheit when printing?

[...]
> +#define PRINT_TEMP(string, index) \
> +       printf("    %-41s :  %.0f degrees C / %.0f degrees F\n", (string), \
> +                  (double)(sd.sfp_temp[(index)] / 10.f), \
> +                  (double)(sd.sfp_temp[(index)] / 10.f * 1.8f + 32.f));
[...]

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-16 19:38                 ` Ben Hutchings
@ 2012-11-18 21:35                   ` Aurélien
  2012-11-19  7:27                     ` Robert Olsson
  2012-12-01  4:18                     ` Ben Hutchings
  0 siblings, 2 replies; 17+ messages in thread
From: Aurélien @ 2012-11-18 21:35 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev

[-- Attachment #1: Type: text/plain, Size: 1982 bytes --]

Hi Ben,

I've rewritten things according to your remarks.

On Fri, Nov 16, 2012 at 8:38 PM, Ben Hutchings
<bhutchings@solarflare.com> wrote:
>
> This is silly; log10() and <math.h> are part of standard C and -lm is
> standard on Unix.  Just use <math.h> and -lm unconditionally.

Ok, I wasn't sure.

>
> Please merge this with the existing -m option and update the
> documentation to say that this covers diagnostics where available.  You
> could add a long option alias like --dump-module or --module-info that
> covers the two types of information.

Done that, the current output of -m has been modified so that
everything lines up correctly.
The --module-info option alias has been added.

> All the above offsets need parentheses around their definitions.
[…]
> This is commented as an offset in the A2 'EEPROM' but the offsets
> actually used include the 0x100 offset from the start of the
> concatenated 'EEPROM'.

A new SFF_A2_BASE has been added, and the OFFSET_TO macros are now
using that, so I removed the 0x100 from all the offsets, and they are
now indeed A2-relative.

>
> Why are all the literals explicitly float and not double?
>

It was a keyboard/chair interface problem, now fixed :)

> Please follow kernel coding style for spacing.  checkpatch.pl will show
> you what should be changed.

Ran a checkpatch, and fixed everything that should be fixed.

>
> This seems awfuly complicated; why not:
>
> #define OFFSET_TO_TEMP(offset) (((s16)OFFSET_TO_U16(offset)) * 10 / 256)
>
> But why round to tenths of a degree here and then round again to whole
> degrees celsius/fahrenheit when printing?
>

I did not think a simple cast would work, but it seems to give the
right value. I also implemented externally calibrated optics in this
new version, so I now do the whole formatting in the printing, and
store the raw value in the struct.

It should be better now.

Best regards,
-- 
Aurélien Guillaume

[-- Attachment #2: 0001-Implemented-basic-optics-diagnostics-for-SFF-8472-co.patch --]
[-- Type: application/octet-stream, Size: 21479 bytes --]

From 2b96a1a65e1c24e3c43f479719bd3e3da499656c Mon Sep 17 00:00:00 2001
From: Aurelien Guillaume <aurelien@iwi.me>
Date: Fri, 16 Nov 2012 02:50:00 +0100
Subject: [PATCH]   Implemented basic optics diagnostics for SFF-8472 compliant
   transceivers in ethtool.


Signed-off-by: Aurelien Guillaume <aurelien@iwi.me>
---
 Makefile.am |    2 +-
 ethtool.c   |   17 +++-
 internal.h  |    3 +
 sfpdiag.c   |  364 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sfpid.c     |   35 +++---
 5 files changed, 402 insertions(+), 19 deletions(-)
 create mode 100644 sfpdiag.c

diff --git a/Makefile.am b/Makefile.am
index e33f71f..89a0d1e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,7 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
 		  fec_8xx.c ibm_emac.c ixgb.c ixgbe.c natsemi.c	\
 		  pcnet32.c realtek.c tg3.c marvell.c vioc.c	\
 		  smsc911x.c at76c50x-usb.c sfc.c stmmac.c	\
-		  rxclass.c sfpid.c
+		  rxclass.c sfpid.c sfpdiag.c
 
 TESTS = test-cmdline test-features
 check_PROGRAMS = test-cmdline test-features
diff --git a/ethtool.c b/ethtool.c
index 3db7fec..345c21c 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3604,6 +3604,16 @@ static int do_getmodule(struct cmd_context *ctx)
 		return 1;
 	}
 
+	/*
+	 * SFF-8079 EEPROM layout contains the memory available at A0 address on
+	 * the PHY EEPROM.
+	 * SFF-8472 defines a virtual extension of the EEPROM, where the
+	 * microcontroller on the SFP/SFP+ generates a page at the A2 address,
+	 * which contains data relative to optical diagnostics.
+	 * The current kernel implementation returns a blob, which contains:
+	 *  - ETH_MODULE_SFF_8079 => The A0 page only.
+	 *  - ETH_MODULE_SFF_8472 => The A0 and A2 page concatenated.
+	 */
 	if (geeprom_dump_raw) {
 		fwrite(eeprom->data, 1, eeprom->len, stdout);
 	} else {
@@ -3613,8 +3623,11 @@ static int do_getmodule(struct cmd_context *ctx)
 		} else if (!geeprom_dump_hex) {
 			switch (modinfo.type) {
 			case ETH_MODULE_SFF_8079:
+				sff8079_show_all(eeprom->data);
+				break;
 			case ETH_MODULE_SFF_8472:
 				sff8079_show_all(eeprom->data);
+				sff8472_show_all(eeprom->data);
 				break;
 			default:
 				geeprom_dump_hex = 1;
@@ -3831,8 +3844,8 @@ static const struct option {
 	{ "--show-priv-flags" , 1, do_gprivflags, "Query private flags" },
 	{ "--set-priv-flags", 1, do_sprivflags, "Set private flags",
 	  "		FLAG on|off ...\n" },
-	{ "-m|--dump-module-eeprom", 1, do_getmodule,
-	  "Qeuery/Decode Module EEPROM information",
+	{ "-m|--dump-module-eeprom|--module-info", 1, do_getmodule,
+	  "Query/Decode Module EEPROM information and optical diagnostics if available",
 	  "		[ raw on|off ]\n"
 	  "		[ hex on|off ]\n"
 	  "		[ offset N ]\n"
diff --git a/internal.h b/internal.h
index 4f96fd5..e977a81 100644
--- a/internal.h
+++ b/internal.h
@@ -253,4 +253,7 @@ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
 /* Module EEPROM parsing code */
 void sff8079_show_all(const __u8 *id);
 
+/* Optics diagnostics */
+void sff8472_show_all(const __u8 *id);
+
 #endif /* ETHTOOL_INTERNAL_H__ */
diff --git a/sfpdiag.c b/sfpdiag.c
new file mode 100644
index 0000000..1094bd7
--- /dev/null
+++ b/sfpdiag.c
@@ -0,0 +1,364 @@
+/*
+ * sfpdiag.c: Implements SFF-8472 optics diagnostics.
+ *
+ * Aurelien Guillaume <aurelien@iwi.me> (C) 2012
+ *   This implementation is loosely based on DOM patches
+ *   from Robert Olsson <robert@herjulf.se> (C) 2009
+ *   and SFF-8472 specs (ftp://ftp.seagate.com/pub/sff/SFF-8472.PDF)
+ *   by SFF Committee.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "internal.h"
+
+/* Offsets in decimal, for direct comparison with the SFF specs */
+
+/* A0-based EEPROM offsets for DOM support checks */
+#define SFF_A0_DOM                        92
+#define SFF_A0_OPTIONS                    93
+#define SFF_A0_COMP                       94
+
+/* EEPROM bit values for various registers */
+#define SFF_A0_DOM_EXTCAL                 (1 << 4)
+#define SFF_A0_DOM_INTCAL                 (1 << 5)
+#define SFF_A0_DOM_IMPL                   (1 << 6)
+#define SFF_A0_DOM_PWRT                   (1 << 3)
+
+#define SFF_A0_OPTIONS_AW                 (1 << 7)
+
+/*
+ * See ethtool.c comments about SFF-8472, this is the offset
+ * at which the A2 page is in the EEPROM blob returned by the
+ * kernel.
+ */
+#define SFF_A2_BASE                       0x100
+
+/* A2-based offsets for DOM */
+#define SFF_A2_TEMP                       96
+#define SFF_A2_TEMP_HALRM                 0
+#define SFF_A2_TEMP_LALRM                 2
+#define SFF_A2_TEMP_HWARN                 4
+#define SFF_A2_TEMP_LWARN                 6
+
+#define SFF_A2_VCC                        98
+#define SFF_A2_VCC_HALRM                  8
+#define SFF_A2_VCC_LALRM                  10
+#define SFF_A2_VCC_HWARN                  12
+#define SFF_A2_VCC_LWARN                  14
+
+#define SFF_A2_BIAS                       96
+#define SFF_A2_BIAS_HALRM                 16
+#define SFF_A2_BIAS_LALRM                 18
+#define SFF_A2_BIAS_HWARN                 20
+#define SFF_A2_BIAS_LWARN                 22
+
+#define SFF_A2_TX_PWR                     102
+#define SFF_A2_TX_PWR_HALRM               24
+#define SFF_A2_TX_PWR_LALRM               26
+#define SFF_A2_TX_PWR_HWARN               28
+#define SFF_A2_TX_PWR_LWARN               30
+
+#define SFF_A2_RX_PWR                     104
+#define SFF_A2_RX_PWR_HALRM               32
+#define SFF_A2_RX_PWR_LALRM               34
+#define SFF_A2_RX_PWR_HWARN               36
+#define SFF_A2_RX_PWR_LWARN               38
+
+#define SFF_A2_ALRM_FLG                   112
+#define SFF_A2_WARN_FLG                   116
+
+/* 32-bit little-endian calibration constants */
+#define SFF_A2_CAL_RXPWR4                 56
+#define SFF_A2_CAL_RXPWR3                 60
+#define SFF_A2_CAL_RXPWR2                 64
+#define SFF_A2_CAL_RXPWR1                 68
+#define SFF_A2_CAL_RXPWR0                 72
+
+/* 16-bit little endian calibration constants */
+#define SFF_A2_CAL_TXI_SLP                76
+#define SFF_A2_CAL_TXI_OFF                78
+#define SFF_A2_CAL_TXPWR_SLP              80
+#define SFF_A2_CAL_TXPWR_OFF              82
+#define SFF_A2_CAL_T_SLP                  84
+#define SFF_A2_CAL_T_OFF                  86
+#define SFF_A2_CAL_V_SLP                  88
+#define SFF_A2_CAL_V_OFF			      90
+
+
+struct sff8472_diags {
+
+#define MCURR 0
+#define LWARN 1
+#define HWARN 2
+#define LALRM 3
+#define HALRM 4
+
+	/* [5] tables are current, low/high warn, low/high alarm */
+	__u8 supports_dom;      /* Supports DOM */
+	__u8 supports_alarms;   /* Supports alarm/warning thold */
+	__u8 calibrated_ext;	/* Is externally calibrated */
+	__u16 bias_cur[5];		/* Measured bias current in 2uA units */
+	__u16 tx_power[5];		/* Measured TX Power in 0.1uW units */
+	__u16 rx_power[5];		/* Measured RX Power */
+	__u8  rx_power_type;    /* 0 = OMA, 1 = Average power */
+	__s16 sfp_temp[5];      /* SFP Temp in 16-bit signed 1/256 Celcius */
+	__u16 sfp_voltage[5];   /* SFP voltage in 0.1mV units */
+
+};
+
+static struct sff8472_aw_flags {
+	const char *str;        /* Human-readable string, null at the end */
+	int offset;             /* A2-relative adress offset */
+	__u8 value;             /* Alarm is on if (offset & value) != 0. */
+} sff8472_aw_flags[] = {
+	{ "Laser bias current high alarm",   SFF_A2_ALRM_FLG, (1 << 3) },
+	{ "Laser bias current low alarm",    SFF_A2_ALRM_FLG, (1 << 2) },
+	{ "Laser bias current high warning", SFF_A2_WARN_FLG, (1 << 3) },
+	{ "Laser bias current low warning",  SFF_A2_WARN_FLG, (1 << 2) },
+
+	{ "Laser output power high alarm",   SFF_A2_ALRM_FLG, (1 << 1) },
+	{ "Laser output power low alarm",    SFF_A2_ALRM_FLG, (1 << 0) },
+	{ "Laser output power high warning", SFF_A2_WARN_FLG, (1 << 1) },
+	{ "Laser output power low warning",  SFF_A2_WARN_FLG, (1 << 0) },
+
+	{ "Module temperature high alarm",   SFF_A2_ALRM_FLG, (1 << 7) },
+	{ "Module temperature low alarm",    SFF_A2_ALRM_FLG, (1 << 6) },
+	{ "Module temperature high warning", SFF_A2_WARN_FLG, (1 << 7) },
+	{ "Module temperature low warning",  SFF_A2_WARN_FLG, (1 << 6) },
+
+	{ "Module voltage high alarm",   SFF_A2_ALRM_FLG, (1 << 5) },
+	{ "Module voltage low alarm",    SFF_A2_ALRM_FLG, (1 << 4) },
+	{ "Module voltage high warning", SFF_A2_WARN_FLG, (1 << 5) },
+	{ "Module voltage low warning",  SFF_A2_WARN_FLG, (1 << 4) },
+
+	{ "Laser rx power high alarm",   SFF_A2_ALRM_FLG + 1, (1 << 7) },
+	{ "Laser rx power low alarm",    SFF_A2_ALRM_FLG + 1, (1 << 6) },
+	{ "Laser rx power high warning", SFF_A2_WARN_FLG + 1, (1 << 7) },
+	{ "Laser rx power low warning",  SFF_A2_WARN_FLG + 1, (1 << 6) },
+
+	{ NULL, 0, 0 },
+};
+
+static double convert_mw_to_dbm(double mw)
+{
+	return (10. * log10(mw / 1000.)) + 30.;
+}
+
+
+/* Most common case: 16-bit unsigned integer in a certain unit */
+#define A2_OFFSET_TO_U16(offset) \
+	(id[SFF_A2_BASE + (offset)] << 8 | id[SFF_A2_BASE + (offset) + 1])
+
+/* Calibration slope is a number between 0.0 included and 256.0 excluded. */
+#define A2_OFFSET_TO_SLP(offset) \
+	(id[SFF_A2_BASE + (offset)] + id[SFF_A2_BASE + (offset) + 1] / 256.)
+
+/* Calibration offset is an integer from -32768 to 32767 */
+#define A2_OFFSET_TO_OFF(offset) \
+	((__s16)A2_OFFSET_TO_U16(offset))
+
+/* RXPWR(x) are IEEE-754 floating point numbers in big-endian format */
+#define A2_OFFSET_TO_RXPWRx(offset) \
+	(befloattoh((__u32 *)(id + SFF_A2_BASE + (offset))))
+
+/*
+ * 2-byte internal temperature conversions:
+ * First byte is a signed 8-bit integer, which is the temp decimal part
+ * Second byte are 1/256th of degree, which are added to the dec part.
+ */
+#define A2_OFFSET_TO_TEMP(offset) ((__s16)A2_OFFSET_TO_U16(offset))
+
+
+static void sff8472_dom_parse(const __u8 *id, struct sff8472_diags *sd)
+{
+
+	sd->bias_cur[MCURR] = A2_OFFSET_TO_U16(SFF_A2_BIAS);
+	sd->bias_cur[HALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HALRM);
+	sd->bias_cur[LALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LALRM);
+	sd->bias_cur[HWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HWARN);
+	sd->bias_cur[LWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LWARN);
+
+	sd->sfp_voltage[MCURR] = A2_OFFSET_TO_U16(SFF_A2_VCC);
+	sd->sfp_voltage[HALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_HALRM);
+	sd->sfp_voltage[LALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_LALRM);
+	sd->sfp_voltage[HWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_HWARN);
+	sd->sfp_voltage[LWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_LWARN);
+
+	sd->tx_power[MCURR] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR);
+	sd->tx_power[HALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HALRM);
+	sd->tx_power[LALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LALRM);
+	sd->tx_power[HWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HWARN);
+	sd->tx_power[LWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LWARN);
+
+	sd->rx_power[MCURR] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR);
+	sd->rx_power[HALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HALRM);
+	sd->rx_power[LALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LALRM);
+	sd->rx_power[HWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HWARN);
+	sd->rx_power[LWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LWARN);
+
+	sd->sfp_temp[MCURR] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP);
+	sd->sfp_temp[HALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HALRM);
+	sd->sfp_temp[LALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LALRM);
+	sd->sfp_temp[HWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HWARN);
+	sd->sfp_temp[LWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LWARN);
+
+}
+
+/* Converts to a float from a big-endian 4-byte source buffer. */
+static float befloattoh(const __u32 *source)
+{
+	union {
+		__u32 src;
+		float dst;
+	} converter;
+
+	converter.src = be32toh(*source);
+	return converter.dst;
+}
+
+static void sff8472_calibration(const __u8 *id, struct sff8472_diags *sd)
+{
+	int i;
+	__u16 rx_reading;
+
+	/* Calibration should occur for all values (threshold and current) */
+	for (i = 0; i < sizeof(sd->bias_cur); ++i) {
+		/*
+		 * Apply calibration formula 1 (Temp., Voltage, Bias, Tx Power)
+		 */
+		sd->bias_cur[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXI_SLP);
+		sd->tx_power[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXPWR_SLP);
+		sd->sfp_voltage[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_V_SLP);
+		sd->sfp_temp[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_T_SLP);
+
+		sd->bias_cur[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXI_OFF);
+		sd->tx_power[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXPWR_OFF);
+		sd->sfp_voltage[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_V_OFF);
+		sd->sfp_temp[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_T_OFF);
+
+		/*
+		 * Apply calibration formula 2 (Rx Power only)
+		 */
+		rx_reading = sd->rx_power[i];
+		sd->rx_power[i]    = A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR0);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR1);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR2);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR3);
+	}
+}
+
+static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd)
+{
+	sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
+	sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
+	sd->calibrated_ext = id[SFF_A0_DOM] & SFF_A0_DOM_EXTCAL;
+	sd->rx_power_type = id[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
+
+	sff8472_dom_parse(id, sd);
+
+	/*
+	 * If the SFP is externally calibrated, we need to read calibration data
+	 * and compensate the already stored readings.
+	 */
+	if (sd->calibrated_ext)
+		sff8472_calibration(id, sd);
+}
+
+
+
+void sff8472_show_all(const __u8 *id)
+{
+	struct sff8472_diags sd;
+	char *rx_power_string = NULL;
+	int i;
+
+	sff8472_parse_eeprom(id, &sd);
+
+	if (!sd.supports_dom) {
+		printf("\t%-41s : No\n", "Optical diagnostics support");
+		return ;
+	}
+	printf("\t%-41s : Yes\n", "Optical diagnostics support");
+
+#define PRINT_BIAS(string, index) \
+	printf("\t%-41s : %.3f mA\n", (string), \
+		   (double)(sd.bias_cur[(index)] / 500.));
+
+# define PRINT_xX_PWR(string, var, index) \
+	printf("\t%-41s : %.4f mW / %.2f dBm\n", (string), \
+		   (double)((var)[(index)] / 10000.), \
+		   convert_mw_to_dbm((double)((var)[(index)] / 10000.)));
+
+#define PRINT_TEMP(string, index) \
+	printf("\t%-41s : %.2f degrees C / %.2f degrees F\n", (string), \
+		   (double)(sd.sfp_temp[(index)] / 256.), \
+		   (double)(sd.sfp_temp[(index)] / 256. * 1.8 + 32.));
+
+#define PRINT_VCC(string, index) \
+	printf("\t%-41s : %.4f V\n", (string), \
+		   (double)(sd.sfp_voltage[(index)] / 10000.));
+
+
+	PRINT_BIAS("Laser bias current", MCURR);
+	PRINT_xX_PWR("Laser output power", sd.tx_power, MCURR);
+
+	if (!sd.rx_power_type)
+		rx_power_string = "Receiver signal OMA";
+	else
+		rx_power_string = "Receiver signal average optical power";
+
+	PRINT_xX_PWR(rx_power_string, sd.rx_power, MCURR);
+
+	PRINT_TEMP("Module temperature", MCURR);
+	PRINT_VCC("Module voltage", MCURR);
+
+	printf("\t%-41s : %s\n", "Alarm/warning flags implemented",
+		   (sd.supports_alarms ? "Yes" : "No"));
+	if (sd.supports_alarms) {
+
+		for (i = 0; sff8472_aw_flags[i].str; ++i) {
+			printf("\t%-41s : %s\n", sff8472_aw_flags[i].str,
+				   id[SFF_A2_BASE + sff8472_aw_flags[i].offset]
+				   & sff8472_aw_flags[i].value ? "On" : "Off");
+		}
+
+		PRINT_BIAS("Laser bias current high alarm threshold",   HALRM);
+		PRINT_BIAS("Laser bias current low alarm threshold",    LALRM);
+		PRINT_BIAS("Laser bias current high warning threshold", HWARN);
+		PRINT_BIAS("Laser bias current low warning threshold",  LWARN);
+
+		PRINT_xX_PWR("Laser output power high alarm threshold",
+					 sd.tx_power, HALRM);
+		PRINT_xX_PWR("Laser output power low alarm threshold",
+					 sd.tx_power, LALRM);
+		PRINT_xX_PWR("Laser output power high warning threshold",
+					 sd.tx_power, HWARN);
+		PRINT_xX_PWR("Laser output power low warning threshold",
+					 sd.tx_power, LWARN);
+
+		PRINT_TEMP("Module temperature high alarm threshold",   HALRM);
+		PRINT_TEMP("Module temperature low alarm threshold",    LALRM);
+		PRINT_TEMP("Module temperature high warning threshold", HWARN);
+		PRINT_TEMP("Module temperature low warning threshold",  LWARN);
+
+		PRINT_VCC("Module voltage high alarm threshold",   HALRM);
+		PRINT_VCC("Module voltage low alarm threshold",    LALRM);
+		PRINT_VCC("Module voltage high warning threshold", HWARN);
+		PRINT_VCC("Module voltage low warning threshold",  LWARN);
+
+		PRINT_xX_PWR("Laser rx power high alarm threshold",
+					 sd.rx_power, HALRM);
+		PRINT_xX_PWR("Laser rx power low alarm threshold",
+					 sd.rx_power, LALRM);
+		PRINT_xX_PWR("Laser rx power high warning threshold",
+					 sd.rx_power, HWARN);
+		PRINT_xX_PWR("Laser rx power low warning threshold",
+					 sd.rx_power, LWARN);
+	}
+
+}
+
diff --git a/sfpid.c b/sfpid.c
index a4a671d..2982d0d 100644
--- a/sfpid.c
+++ b/sfpid.c
@@ -12,7 +12,7 @@
 
 static void sff8079_show_identifier(const __u8 *id)
 {
-	printf("\tIdentifier          : 0x%02x", id[0]);
+	printf("\t%-41s : 0x%02x", "Identifier", id[0]);
 	switch (id[0]) {
 	case 0x00:
 		printf(" (no module present, unknown, or unspecified)\n");
@@ -34,7 +34,7 @@ static void sff8079_show_identifier(const __u8 *id)
 
 static void sff8079_show_ext_identifier(const __u8 *id)
 {
-	printf("\tExtended identifier : 0x%02x", id[1]);
+	printf("\t%-41s : 0x%02x", "Extended identifier", id[1]);
 	if (id[1] == 0x00)
 		printf(" (GBIC not specified / not MOD_DEF compliant)\n");
 	else if (id[1] == 0x04)
@@ -47,7 +47,7 @@ static void sff8079_show_ext_identifier(const __u8 *id)
 
 static void sff8079_show_connector(const __u8 *id)
 {
-	printf("\tConnector           : 0x%02x", id[2]);
+	printf("\t%-41s : 0x%02x", "Connector", id[2]);
 	switch (id[2]) {
 	case 0x00:
 		printf(" (unknown or unspecified)\n");
@@ -105,10 +105,12 @@ static void sff8079_show_connector(const __u8 *id)
 
 static void sff8079_show_transceiver(const __u8 *id)
 {
-	static const char *pfx = "\t                    :  =>";
+	static const char *pfx =
+		"\tTransceiver type                          :";
 
-	printf("\tTransceiver codes   : 0x%02x 0x%02x 0x%02x" \
+	printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \
 	       "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+		   "Transceiver codes",
 	       id[3], id[4], id[5], id[6],
 	       id[7], id[8], id[9], id[10]);
 	/* 10G Ethernet Compliance Codes */
@@ -239,7 +241,7 @@ static void sff8079_show_transceiver(const __u8 *id)
 
 static void sff8079_show_encoding(const __u8 *id)
 {
-	printf("\tEncoding            : 0x%02x", id[11]);
+	printf("\t%-41s : 0x%02x", "Encoding", id[11]);
 	switch (id[11]) {
 	case 0x00:
 		printf(" (unspecified)\n");
@@ -270,7 +272,7 @@ static void sff8079_show_encoding(const __u8 *id)
 
 static void sff8079_show_rate_identifier(const __u8 *id)
 {
-	printf("\tRate identifier     : 0x%02x", id[13]);
+	printf("\t%-41s : 0x%02x", "Rate identifier", id[13]);
 	switch (id[13]) {
 	case 0x00:
 		printf(" (unspecified)\n");
@@ -295,14 +297,14 @@ static void sff8079_show_rate_identifier(const __u8 *id)
 
 static void sff8079_show_oui(const __u8 *id)
 {
-	printf("\tVendor OUI          : %02x:%02x:%02x\n",
+	printf("\t%-41s : %02x:%02x:%02x\n", "Vendor OUI",
 	       id[37], id[38], id[39]);
 }
 
 static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 {
 	if (id[8] & (1 << 2)) {
-		printf("\tPassive Cu cmplnce. : 0x%02x", id[60]);
+		printf("\t%-41s : 0x%02x", "Passive copper compliance", id[60]);
 		switch (id[60]) {
 		case 0x00:
 			printf(" (unspecified)");
@@ -316,7 +318,7 @@ static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 		}
 		printf(" [SFF-8472 rev10.4 only]\n");
 	} else if (id[8] & (1 << 3)) {
-		printf("\tActive Cu cmplnce.  : 0x%02x", id[60]);
+		printf("\t%-41s : 0x%02x", "Active copper compliance", id[60]);
 		switch (id[60]) {
 		case 0x00:
 			printf(" (unspecified)");
@@ -333,7 +335,7 @@ static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 		}
 		printf(" [SFF-8472 rev10.4 only]\n");
 	} else {
-		printf("\tLaser wavelength    : %unm\n",
+		printf("\t%-41s : %unm\n", "Laser wavelength",
 		       (id[60] << 8) | id[61]);
 	}
 }
@@ -344,7 +346,7 @@ static void sff8079_show_value_with_unit(const __u8 *id, unsigned int reg,
 {
 	unsigned int val = id[reg];
 
-	printf("\t%-20s: %u%s\n", name, val * mult, unit);
+	printf("\t%-41s : %u%s\n", name, val * mult, unit);
 }
 
 static void sff8079_show_ascii(const __u8 *id, unsigned int first_reg,
@@ -352,7 +354,7 @@ static void sff8079_show_ascii(const __u8 *id, unsigned int first_reg,
 {
 	unsigned int reg, val;
 
-	printf("\t%-20s: ", name);
+	printf("\t%-41s : ", name);
 	for (reg = first_reg; reg <= last_reg; reg++) {
 		val = id[reg];
 		putchar(((val >= 32) && (val <= 126)) ? val : '_');
@@ -368,14 +370,15 @@ void sff8079_show_all(const __u8 *id)
 		sff8079_show_connector(id);
 		sff8079_show_transceiver(id);
 		sff8079_show_encoding(id);
-		sff8079_show_value_with_unit(id, 12, "BR, Nominal", 100, "MBd");
+		sff8079_show_value_with_unit(id, 12,
+				"Nominal signalling rate", 100, "MBd");
 		sff8079_show_rate_identifier(id);
 		sff8079_show_value_with_unit(id, 14,
-					     "Length (SMF,km)", 1, "km");
+				"Length (SMF,km)", 1, "km");
 		sff8079_show_value_with_unit(id, 15, "Length (SMF)", 100, "m");
 		sff8079_show_value_with_unit(id, 16, "Length (50um)", 10, "m");
 		sff8079_show_value_with_unit(id, 17,
-					     "Length (62.5um)", 10, "m");
+				"Length (62.5um)", 10, "m");
 		sff8079_show_value_with_unit(id, 18, "Length (Copper)", 1, "m");
 		sff8079_show_value_with_unit(id, 19, "Length (OM3)", 10, "m");
 		sff8079_show_wavelength_or_copper_compliance(id);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-18 21:35                   ` Aurélien
@ 2012-11-19  7:27                     ` Robert Olsson
  2012-12-01  4:18                     ` Ben Hutchings
  1 sibling, 0 replies; 17+ messages in thread
From: Robert Olsson @ 2012-11-19  7:27 UTC (permalink / raw)
  To: footplus; +Cc: Ben Hutchings, netdev


Hi,
FYI. DOM use in Serengeti Tanzania (Bunda-Nata 60km) on solar driven low-power 
linux atom router @ 20Watt w. igb driver using the older DOM pathes. Very useful 
stuff. Yes get included in the kernel.

NATA:/# ethtool -D eth1
Ext-Calbr: Avr RX-Power: Alarm & Warn: RX_LOS: 	Wavelength: 1550 nm
Alarms, warnings in beginning of line, Ie. AH = Alarm High, WL == Warn Low etc
	Temp:  76.2 C			Thresh: Lo: -50.0/-48.0  Hi:  95.0/110.0 C
	Vcc:  3.27 V			Thresh: Lo:   2.9/3.0    Hi:   3.5/3.6   V
	Tx-Bias:  27.9 mA		Thresh: Lo:   3.0/5.0    Hi:  90.0/100.0 mA
	TX-pwr:   3.8 dBm ( 2.39 mW)	Thresh: Lo:  -5.0/-4.0   Hi:   5.0/6.0   dBm
	RX-pwr: -17.3 dBm ( 0.02 mW) 	Thresh: Lo: -40.0/-37.0  Hi:  -5.0/-3.0  dBm

http://herjulf.se/robert/tanzania-2012/Nata-installation-2.jpg

						--ro

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-11-18 21:35                   ` Aurélien
  2012-11-19  7:27                     ` Robert Olsson
@ 2012-12-01  4:18                     ` Ben Hutchings
  2012-12-02 21:47                       ` Aurélien
  1 sibling, 1 reply; 17+ messages in thread
From: Ben Hutchings @ 2012-12-01  4:18 UTC (permalink / raw)
  To: footplus; +Cc: netdev

On Sun, 2012-11-18 at 22:35 +0100, Aurélien wrote:


> Hi Ben,
> 
> I've rewritten things according to your remarks.
> 
> On Fri, Nov 16, 2012 at 8:38 PM, Ben Hutchings
> <bhutchings@solarflare.com> wrote:
> >
> > This is silly; log10() and <math.h> are part of standard C and -lm is
> > standard on Unix.  Just use <math.h> and -lm unconditionally.
> 
> Ok, I wasn't sure.

This version drops the -lm completely, so it doesn't link.  Maybe you
edited the generated Makefile or Makefile.in?

> > Please merge this with the existing -m option and update the
> > documentation to say that this covers diagnostics where available.  You
> > could add a long option alias like --dump-module or --module-info that
> > covers the two types of information.
> 
> Done that, the current output of -m has been modified so that
> everything lines up correctly.
> The --module-info option alias has been added.

The option alias should be included in the manual page and in a
(trivial) test case in test-cmdline.c.

[...]
> > Please follow kernel coding style for spacing.  checkpatch.pl will show
> > you what should be changed.
> 
> Ran a checkpatch, and fixed everything that should be fixed.

The indentation is still weird, though:

[...]
> --- /dev/null
> +++ b/sfpdiag.c
[...]
> +struct sff8472_diags {
> +
> +#define MCURR 0
> +#define LWARN 1
> +#define HWARN 2
> +#define LALRM 3
> +#define HALRM 4
> +
> +       /* [5] tables are current, low/high warn, low/high alarm */
> +       __u8 supports_dom;      /* Supports DOM */
> +       __u8 supports_alarms;   /* Supports alarm/warning thold */
> +       __u8 calibrated_ext;    /* Is externally calibrated */
> +       __u16 bias_cur[5];              /* Measured bias current in 2uA units */
> +       __u16 tx_power[5];              /* Measured TX Power in 0.1uW units */
> +       __u16 rx_power[5];              /* Measured RX Power */

These comments should be lined up vertically.

[...]
> +/* Converts to a float from a big-endian 4-byte source buffer. */
> +static float befloattoh(const __u32 *source)
> +{
> +       union {
> +               __u32 src;
> +               float dst;
> +       } converter;
> +
> +       converter.src = be32toh(*source);

be32toh() is non-standard and was apparently added to glibc relatively
recently (version 2.9).  Therefore please use the equivalent ntohl()
instead.

[...]
> +static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd)
> +{
> +       sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
> +       sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
> +       sd->calibrated_ext = id[SFF_A0_DOM] & SFF_A0_DOM_EXTCAL;
> +       sd->rx_power_type = id[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
> +
> +       sff8472_dom_parse(id, sd);
> +
> +       /*
> +        * If the SFP is externally calibrated, we need to read calibration data
> +        * and compensate the already stored readings.
> +        */
> +       if (sd->calibrated_ext)
> +               sff8472_calibration(id, sd);
> +}
> +
> +
> +

One line between functions is enough.

> +void sff8472_show_all(const __u8 *id)
> +{
> +       struct sff8472_diags sd;
> +       char *rx_power_string = NULL;
> +       int i;
> +
> +       sff8472_parse_eeprom(id, &sd);
> +
> +       if (!sd.supports_dom) {
> +               printf("\t%-41s : No\n", "Optical diagnostics support");
> +               return ;
> +       }
> +       printf("\t%-41s : Yes\n", "Optical diagnostics support");
> +
> +#define PRINT_BIAS(string, index) \
> +       printf("\t%-41s : %.3f mA\n", (string), \
> +                  (double)(sd.bias_cur[(index)] / 500.));
> +
> +# define PRINT_xX_PWR(string, var, index) \
> +       printf("\t%-41s : %.4f mW / %.2f dBm\n", (string), \
> +                  (double)((var)[(index)] / 10000.), \
> +                  convert_mw_to_dbm((double)((var)[(index)] / 10000.)));
> +
> +#define PRINT_TEMP(string, index) \
> +       printf("\t%-41s : %.2f degrees C / %.2f degrees F\n", (string), \
> +                  (double)(sd.sfp_temp[(index)] / 256.), \
> +                  (double)(sd.sfp_temp[(index)] / 256. * 1.8 + 32.));
> +
> +#define PRINT_VCC(string, index) \
> +       printf("\t%-41s : %.4f V\n", (string), \
> +                  (double)(sd.sfp_voltage[(index)] / 10000.));

Function-like macros generally shouldn't be defined with a trailing
semi-colon, as that will be added at the point of use.

The backslashes should be lined up on the right, and continuation lines
within parentheses should be indented so they begin just to the right of
the opening parenthesis, e.g.:

#define PRINT_VCC(string, index)				\
	printf("\t%-41s : %.4f V\n", (string),			\
	       (double)(sd.sfp_voltage[(index)] / 10000.))

[...]
> +               PRINT_xX_PWR("Laser output power high alarm threshold",
> +                                        sd.tx_power, HALRM);
> +               PRINT_xX_PWR("Laser output power low alarm threshold",
> +                                        sd.tx_power, LALRM);
> +               PRINT_xX_PWR("Laser output power high warning threshold",
> +                                        sd.tx_power, HWARN);
> +               PRINT_xX_PWR("Laser output power low warning threshold",
> +                                        sd.tx_power, LWARN);

The continuation lines are over-indented here.

[...]
> +               PRINT_xX_PWR("Laser rx power high alarm threshold",
> +                                        sd.rx_power, HALRM);
> +               PRINT_xX_PWR("Laser rx power low alarm threshold",
> +                                        sd.rx_power, LALRM);
> +               PRINT_xX_PWR("Laser rx power high warning threshold",
> +                                        sd.rx_power, HWARN);
> +               PRINT_xX_PWR("Laser rx power low warning threshold",
> +                                        sd.rx_power, LWARN);
> +       }

Same here.

> +}
> +
> diff --git a/sfpid.c b/sfpid.c
> index a4a671d..2982d0d 100644
> --- a/sfpid.c
> +++ b/sfpid.c
[...]
>  static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
>  {
>         if (id[8] & (1 << 2)) {
> -               printf("\tPassive Cu cmplnce. : 0x%02x", id[60]);
> +               printf("\t%-41s : 0x%02x", "Passive copper compliance", id[60]);
>                 switch (id[60]) {
>                 case 0x00:
>                         printf(" (unspecified)");
> @@ -316,7 +318,7 @@ static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
>                 }
>                 printf(" [SFF-8472 rev10.4 only]\n");
>         } else if (id[8] & (1 << 3)) {
> -               printf("\tActive Cu cmplnce.  : 0x%02x", id[60]);
> +               printf("\t%-41s : 0x%02x", "Active copper compliance", id[60]);
>                 switch (id[60]) {
>                 case 0x00:
>                         printf(" (unspecified)");

If you want to change these labels, do that in a separate patch.

[...]
> @@ -368,14 +370,15 @@ void sff8079_show_all(const __u8 *id)
>                 sff8079_show_connector(id);
>                 sff8079_show_transceiver(id);
>                 sff8079_show_encoding(id);
> -               sff8079_show_value_with_unit(id, 12, "BR, Nominal", 100, "MBd");
> +               sff8079_show_value_with_unit(id, 12,
> +                               "Nominal signalling rate", 100, "MBd");
>                 sff8079_show_rate_identifier(id);
>                 sff8079_show_value_with_unit(id, 14,
> -                                            "Length (SMF,km)", 1, "km");
> +                               "Length (SMF,km)", 1, "km");
>                 sff8079_show_value_with_unit(id, 15, "Length (SMF)", 100, "m");
>                 sff8079_show_value_with_unit(id, 16, "Length (50um)", 10, "m");
>                 sff8079_show_value_with_unit(id, 17,
> -                                            "Length (62.5um)", 10, "m");
> +                               "Length (62.5um)", 10, "m");
>                 sff8079_show_value_with_unit(id, 18, "Length (Copper)", 1, "m");
>                 sff8079_show_value_with_unit(id, 19, "Length (OM3)", 10, "m");
>                 sff8079_show_wavelength_or_copper_compliance(id);

These changes are unnecessary.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-12-01  4:18                     ` Ben Hutchings
@ 2012-12-02 21:47                       ` Aurélien
  2012-12-02 22:00                         ` Aurélien
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien @ 2012-12-02 21:47 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev

[-- Attachment #1: Type: text/plain, Size: 2554 bytes --]

Hi Ben,

Thanks for your review. Here's a fixed-up version, according to your remarks.

On Sat, Dec 1, 2012 at 5:18 AM, Ben Hutchings <bhutchings@solarflare.com> wrote:
>
> This version drops the -lm completely, so it doesn't link.  Maybe you
> edited the generated Makefile or Makefile.in?

No, I just stupidly forgot to make distclean & autogen after removing
all libm checks. Re-added AC_CHECK_LIB to link with it.

>
> The option alias should be included in the manual page and in a
> (trivial) test case in test-cmdline.c.
>

Included in the man page, along with a modified option description,
and a test equivalent to --dump-module-eeprom, which passes.

>
> The indentation is still weird, though:
>
> [...]
>
> These comments should be lined up vertically.

Yup, I had a mixup between tab/whitespaces, and my Vim config did not
help. Fixed.

>
> be32toh() is non-standard and was apparently added to glibc relatively
> recently (version 2.9).  Therefore please use the equivalent ntohl()
> instead.

Did that, it indeed works just fine. I should have used that from the start.

>
> Function-like macros generally shouldn't be defined with a trailing
> semi-colon, as that will be added at the point of use.
>

That was a copy/paste typo, fixed.

> The backslashes should be lined up on the right, and continuation lines
> within parentheses should be indented so they begin just to the right of
> the opening parenthesis, e.g.:
>
> #define PRINT_VCC(string, index)                                \
>         printf("\t%-41s : %.4f V\n", (string),                  \
>                (double)(sd.sfp_voltage[(index)] / 10000.))
>
> [...]
>> +               PRINT_xX_PWR("Laser output power low warning threshold",
>> +                                        sd.tx_power, LWARN);
>
> The continuation lines are over-indented here.
>

Fixed (was using wrong tabstop width at 4).

>> -               printf("\tActive Cu cmplnce.  : 0x%02x", id[60]);
>> +               printf("\t%-41s : 0x%02x", "Active copper compliance", id[60]);
>
> If you want to change these labels, do that in a separate patch.
>

There's no real need, so I'll leave those alone, just changing the
alignment like for the other labels.

> [...]
>> -                                            "Length (62.5um)", 10, "m");
>> +                               "Length (62.5um)", 10, "m");
>
> These changes are unnecessary.
>

Agreed, removed from the patch.

Best regards,
-- 
Aurélien Guillaume

[-- Attachment #2: 0001-Implemented-basic-optics-diagnostics-for-SFF-8472.patch --]
[-- Type: application/octet-stream, Size: 22638 bytes --]

From 9a6e14770f12aa728751e3c9256968b4fd611290 Mon Sep 17 00:00:00 2001
From: Aurelien Guillaume <aurelien@iwi.me>
Date: Sun, 2 Dec 2012 21:21:01 +0100
Subject: [PATCH] Implemented basic optics diagnostics for SFF-8472


Signed-off-by: Aurelien Guillaume <aurelien@iwi.me>
---
 Makefile.am    |    2 +-
 configure.ac   |    1 +
 ethtool.8.in   |    8 +-
 ethtool.c      |   17 +++-
 internal.h     |    3 +
 sfpdiag.c      |  362 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sfpid.c        |   28 +++--
 test-cmdline.c |    2 +
 8 files changed, 404 insertions(+), 19 deletions(-)
 create mode 100644 sfpdiag.c

diff --git a/Makefile.am b/Makefile.am
index e33f71f..89a0d1e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,7 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
 		  fec_8xx.c ibm_emac.c ixgb.c ixgbe.c natsemi.c	\
 		  pcnet32.c realtek.c tg3.c marvell.c vioc.c	\
 		  smsc911x.c at76c50x-usb.c sfc.c stmmac.c	\
-		  rxclass.c sfpid.c
+		  rxclass.c sfpid.c sfpdiag.c
 
 TESTS = test-cmdline test-features
 check_PROGRAMS = test-cmdline test-features
diff --git a/configure.ac b/configure.ac
index 0c597c6..5806eae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,6 +13,7 @@ AC_PROG_GCC_TRADITIONAL
 AM_PROG_CC_C_O
 
 dnl Checks for libraries.
+AC_CHECK_LIB([m], [log10])
 
 dnl Checks for header files.
 AC_CHECK_HEADERS(sys/ioctl.h)
diff --git a/ethtool.8.in b/ethtool.8.in
index a3c7fbb..e701919 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -312,7 +312,7 @@ ethtool \- query or control network driver and hardware settings
 .BN other
 .BN combined
 .HP
-.B ethtool \-m|\-\-dump\-module\-eeprom
+.B ethtool \-m|\-\-dump\-module\-eeprom|\-\-module\-info
 .I devname
 .B2 raw on off
 .B2 hex on off
@@ -815,8 +815,10 @@ Changes the number of channels used only for other purposes e.g. link interrupts
 .BI combined \ N
 Changes the number of multi-purpose channels.
 .TP
-.B \-m \-\-dump\-module\-eeprom
-Retrieves and if possible decodes the EEPROM from plugin modules, e.g SFP+, QSFP
+.B \-m \-\-dump\-module\-eeprom \-\-module\-info
+Retrieves and if possible decodes the EEPROM from plugin modules, e.g SFP+, QSFP.
+If the driver and module support it, the optical diagnostic information is also
+read and decoded.
 .TP
 .B \-\-show\-priv\-flags
 Queries the specified network device for its private flags.  The
diff --git a/ethtool.c b/ethtool.c
index 3db7fec..345c21c 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3604,6 +3604,16 @@ static int do_getmodule(struct cmd_context *ctx)
 		return 1;
 	}
 
+	/*
+	 * SFF-8079 EEPROM layout contains the memory available at A0 address on
+	 * the PHY EEPROM.
+	 * SFF-8472 defines a virtual extension of the EEPROM, where the
+	 * microcontroller on the SFP/SFP+ generates a page at the A2 address,
+	 * which contains data relative to optical diagnostics.
+	 * The current kernel implementation returns a blob, which contains:
+	 *  - ETH_MODULE_SFF_8079 => The A0 page only.
+	 *  - ETH_MODULE_SFF_8472 => The A0 and A2 page concatenated.
+	 */
 	if (geeprom_dump_raw) {
 		fwrite(eeprom->data, 1, eeprom->len, stdout);
 	} else {
@@ -3613,8 +3623,11 @@ static int do_getmodule(struct cmd_context *ctx)
 		} else if (!geeprom_dump_hex) {
 			switch (modinfo.type) {
 			case ETH_MODULE_SFF_8079:
+				sff8079_show_all(eeprom->data);
+				break;
 			case ETH_MODULE_SFF_8472:
 				sff8079_show_all(eeprom->data);
+				sff8472_show_all(eeprom->data);
 				break;
 			default:
 				geeprom_dump_hex = 1;
@@ -3831,8 +3844,8 @@ static const struct option {
 	{ "--show-priv-flags" , 1, do_gprivflags, "Query private flags" },
 	{ "--set-priv-flags", 1, do_sprivflags, "Set private flags",
 	  "		FLAG on|off ...\n" },
-	{ "-m|--dump-module-eeprom", 1, do_getmodule,
-	  "Qeuery/Decode Module EEPROM information",
+	{ "-m|--dump-module-eeprom|--module-info", 1, do_getmodule,
+	  "Query/Decode Module EEPROM information and optical diagnostics if available",
 	  "		[ raw on|off ]\n"
 	  "		[ hex on|off ]\n"
 	  "		[ offset N ]\n"
diff --git a/internal.h b/internal.h
index 4f96fd5..e977a81 100644
--- a/internal.h
+++ b/internal.h
@@ -253,4 +253,7 @@ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
 /* Module EEPROM parsing code */
 void sff8079_show_all(const __u8 *id);
 
+/* Optics diagnostics */
+void sff8472_show_all(const __u8 *id);
+
 #endif /* ETHTOOL_INTERNAL_H__ */
diff --git a/sfpdiag.c b/sfpdiag.c
new file mode 100644
index 0000000..f67e491
--- /dev/null
+++ b/sfpdiag.c
@@ -0,0 +1,362 @@
+/*
+ * sfpdiag.c: Implements SFF-8472 optics diagnostics.
+ *
+ * Aurelien Guillaume <aurelien@iwi.me> (C) 2012
+ *   This implementation is loosely based on DOM patches
+ *   from Robert Olsson <robert@herjulf.se> (C) 2009
+ *   and SFF-8472 specs (ftp://ftp.seagate.com/pub/sff/SFF-8472.PDF)
+ *   by SFF Committee.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <arpa/inet.h>
+#include "internal.h"
+
+/* Offsets in decimal, for direct comparison with the SFF specs */
+
+/* A0-based EEPROM offsets for DOM support checks */
+#define SFF_A0_DOM                        92
+#define SFF_A0_OPTIONS                    93
+#define SFF_A0_COMP                       94
+
+/* EEPROM bit values for various registers */
+#define SFF_A0_DOM_EXTCAL                 (1 << 4)
+#define SFF_A0_DOM_INTCAL                 (1 << 5)
+#define SFF_A0_DOM_IMPL                   (1 << 6)
+#define SFF_A0_DOM_PWRT                   (1 << 3)
+
+#define SFF_A0_OPTIONS_AW                 (1 << 7)
+
+/*
+ * See ethtool.c comments about SFF-8472, this is the offset
+ * at which the A2 page is in the EEPROM blob returned by the
+ * kernel.
+ */
+#define SFF_A2_BASE                       0x100
+
+/* A2-based offsets for DOM */
+#define SFF_A2_TEMP                       96
+#define SFF_A2_TEMP_HALRM                 0
+#define SFF_A2_TEMP_LALRM                 2
+#define SFF_A2_TEMP_HWARN                 4
+#define SFF_A2_TEMP_LWARN                 6
+
+#define SFF_A2_VCC                        98
+#define SFF_A2_VCC_HALRM                  8
+#define SFF_A2_VCC_LALRM                  10
+#define SFF_A2_VCC_HWARN                  12
+#define SFF_A2_VCC_LWARN                  14
+
+#define SFF_A2_BIAS                       96
+#define SFF_A2_BIAS_HALRM                 16
+#define SFF_A2_BIAS_LALRM                 18
+#define SFF_A2_BIAS_HWARN                 20
+#define SFF_A2_BIAS_LWARN                 22
+
+#define SFF_A2_TX_PWR                     102
+#define SFF_A2_TX_PWR_HALRM               24
+#define SFF_A2_TX_PWR_LALRM               26
+#define SFF_A2_TX_PWR_HWARN               28
+#define SFF_A2_TX_PWR_LWARN               30
+
+#define SFF_A2_RX_PWR                     104
+#define SFF_A2_RX_PWR_HALRM               32
+#define SFF_A2_RX_PWR_LALRM               34
+#define SFF_A2_RX_PWR_HWARN               36
+#define SFF_A2_RX_PWR_LWARN               38
+
+#define SFF_A2_ALRM_FLG                   112
+#define SFF_A2_WARN_FLG                   116
+
+/* 32-bit little-endian calibration constants */
+#define SFF_A2_CAL_RXPWR4                 56
+#define SFF_A2_CAL_RXPWR3                 60
+#define SFF_A2_CAL_RXPWR2                 64
+#define SFF_A2_CAL_RXPWR1                 68
+#define SFF_A2_CAL_RXPWR0                 72
+
+/* 16-bit little endian calibration constants */
+#define SFF_A2_CAL_TXI_SLP                76
+#define SFF_A2_CAL_TXI_OFF                78
+#define SFF_A2_CAL_TXPWR_SLP              80
+#define SFF_A2_CAL_TXPWR_OFF              82
+#define SFF_A2_CAL_T_SLP                  84
+#define SFF_A2_CAL_T_OFF                  86
+#define SFF_A2_CAL_V_SLP                  88
+#define SFF_A2_CAL_V_OFF                  90
+
+
+struct sff8472_diags {
+
+#define MCURR 0
+#define LWARN 1
+#define HWARN 2
+#define LALRM 3
+#define HALRM 4
+
+	/* [5] tables are current, low/high warn, low/high alarm */
+	__u8 supports_dom;      /* Supports DOM */
+	__u8 supports_alarms;   /* Supports alarm/warning thold */
+	__u8 calibrated_ext;    /* Is externally calibrated */
+	__u16 bias_cur[5];      /* Measured bias current in 2uA units */
+	__u16 tx_power[5];      /* Measured TX Power in 0.1uW units */
+	__u16 rx_power[5];      /* Measured RX Power */
+	__u8  rx_power_type;    /* 0 = OMA, 1 = Average power */
+	__s16 sfp_temp[5];      /* SFP Temp in 16-bit signed 1/256 Celcius */
+	__u16 sfp_voltage[5];   /* SFP voltage in 0.1mV units */
+
+};
+
+static struct sff8472_aw_flags {
+	const char *str;        /* Human-readable string, null at the end */
+	int offset;             /* A2-relative adress offset */
+	__u8 value;             /* Alarm is on if (offset & value) != 0. */
+} sff8472_aw_flags[] = {
+	{ "Laser bias current high alarm",   SFF_A2_ALRM_FLG, (1 << 3) },
+	{ "Laser bias current low alarm",    SFF_A2_ALRM_FLG, (1 << 2) },
+	{ "Laser bias current high warning", SFF_A2_WARN_FLG, (1 << 3) },
+	{ "Laser bias current low warning",  SFF_A2_WARN_FLG, (1 << 2) },
+
+	{ "Laser output power high alarm",   SFF_A2_ALRM_FLG, (1 << 1) },
+	{ "Laser output power low alarm",    SFF_A2_ALRM_FLG, (1 << 0) },
+	{ "Laser output power high warning", SFF_A2_WARN_FLG, (1 << 1) },
+	{ "Laser output power low warning",  SFF_A2_WARN_FLG, (1 << 0) },
+
+	{ "Module temperature high alarm",   SFF_A2_ALRM_FLG, (1 << 7) },
+	{ "Module temperature low alarm",    SFF_A2_ALRM_FLG, (1 << 6) },
+	{ "Module temperature high warning", SFF_A2_WARN_FLG, (1 << 7) },
+	{ "Module temperature low warning",  SFF_A2_WARN_FLG, (1 << 6) },
+
+	{ "Module voltage high alarm",   SFF_A2_ALRM_FLG, (1 << 5) },
+	{ "Module voltage low alarm",    SFF_A2_ALRM_FLG, (1 << 4) },
+	{ "Module voltage high warning", SFF_A2_WARN_FLG, (1 << 5) },
+	{ "Module voltage low warning",  SFF_A2_WARN_FLG, (1 << 4) },
+
+	{ "Laser rx power high alarm",   SFF_A2_ALRM_FLG + 1, (1 << 7) },
+	{ "Laser rx power low alarm",    SFF_A2_ALRM_FLG + 1, (1 << 6) },
+	{ "Laser rx power high warning", SFF_A2_WARN_FLG + 1, (1 << 7) },
+	{ "Laser rx power low warning",  SFF_A2_WARN_FLG + 1, (1 << 6) },
+
+	{ NULL, 0, 0 },
+};
+
+static double convert_mw_to_dbm(double mw)
+{
+	return (10. * log10(mw / 1000.)) + 30.;
+}
+
+
+/* Most common case: 16-bit unsigned integer in a certain unit */
+#define A2_OFFSET_TO_U16(offset) \
+	(id[SFF_A2_BASE + (offset)] << 8 | id[SFF_A2_BASE + (offset) + 1])
+
+/* Calibration slope is a number between 0.0 included and 256.0 excluded. */
+#define A2_OFFSET_TO_SLP(offset) \
+	(id[SFF_A2_BASE + (offset)] + id[SFF_A2_BASE + (offset) + 1] / 256.)
+
+/* Calibration offset is an integer from -32768 to 32767 */
+#define A2_OFFSET_TO_OFF(offset) \
+	((__s16)A2_OFFSET_TO_U16(offset))
+
+/* RXPWR(x) are IEEE-754 floating point numbers in big-endian format */
+#define A2_OFFSET_TO_RXPWRx(offset) \
+	(befloattoh((__u32 *)(id + SFF_A2_BASE + (offset))))
+
+/*
+ * 2-byte internal temperature conversions:
+ * First byte is a signed 8-bit integer, which is the temp decimal part
+ * Second byte are 1/256th of degree, which are added to the dec part.
+ */
+#define A2_OFFSET_TO_TEMP(offset) ((__s16)A2_OFFSET_TO_U16(offset))
+
+
+static void sff8472_dom_parse(const __u8 *id, struct sff8472_diags *sd)
+{
+
+	sd->bias_cur[MCURR] = A2_OFFSET_TO_U16(SFF_A2_BIAS);
+	sd->bias_cur[HALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HALRM);
+	sd->bias_cur[LALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LALRM);
+	sd->bias_cur[HWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HWARN);
+	sd->bias_cur[LWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LWARN);
+
+	sd->sfp_voltage[MCURR] = A2_OFFSET_TO_U16(SFF_A2_VCC);
+	sd->sfp_voltage[HALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_HALRM);
+	sd->sfp_voltage[LALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_LALRM);
+	sd->sfp_voltage[HWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_HWARN);
+	sd->sfp_voltage[LWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_LWARN);
+
+	sd->tx_power[MCURR] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR);
+	sd->tx_power[HALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HALRM);
+	sd->tx_power[LALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LALRM);
+	sd->tx_power[HWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HWARN);
+	sd->tx_power[LWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LWARN);
+
+	sd->rx_power[MCURR] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR);
+	sd->rx_power[HALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HALRM);
+	sd->rx_power[LALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LALRM);
+	sd->rx_power[HWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HWARN);
+	sd->rx_power[LWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LWARN);
+
+	sd->sfp_temp[MCURR] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP);
+	sd->sfp_temp[HALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HALRM);
+	sd->sfp_temp[LALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LALRM);
+	sd->sfp_temp[HWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HWARN);
+	sd->sfp_temp[LWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LWARN);
+
+}
+
+/* Converts to a float from a big-endian 4-byte source buffer. */
+static float befloattoh(const __u32 *source)
+{
+	union {
+		__u32 src;
+		float dst;
+	} converter;
+
+	converter.src = ntohl(*source);
+	return converter.dst;
+}
+
+static void sff8472_calibration(const __u8 *id, struct sff8472_diags *sd)
+{
+	int i;
+	__u16 rx_reading;
+
+	/* Calibration should occur for all values (threshold and current) */
+	for (i = 0; i < sizeof(sd->bias_cur); ++i) {
+		/*
+		 * Apply calibration formula 1 (Temp., Voltage, Bias, Tx Power)
+		 */
+		sd->bias_cur[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXI_SLP);
+		sd->tx_power[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXPWR_SLP);
+		sd->sfp_voltage[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_V_SLP);
+		sd->sfp_temp[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_T_SLP);
+
+		sd->bias_cur[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXI_OFF);
+		sd->tx_power[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXPWR_OFF);
+		sd->sfp_voltage[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_V_OFF);
+		sd->sfp_temp[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_T_OFF);
+
+		/*
+		 * Apply calibration formula 2 (Rx Power only)
+		 */
+		rx_reading = sd->rx_power[i];
+		sd->rx_power[i]    = A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR0);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR1);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR2);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR3);
+	}
+}
+
+static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd)
+{
+	sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
+	sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
+	sd->calibrated_ext = id[SFF_A0_DOM] & SFF_A0_DOM_EXTCAL;
+	sd->rx_power_type = id[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
+
+	sff8472_dom_parse(id, sd);
+
+	/*
+	 * If the SFP is externally calibrated, we need to read calibration data
+	 * and compensate the already stored readings.
+	 */
+	if (sd->calibrated_ext)
+		sff8472_calibration(id, sd);
+}
+
+void sff8472_show_all(const __u8 *id)
+{
+	struct sff8472_diags sd;
+	char *rx_power_string = NULL;
+	int i;
+
+	sff8472_parse_eeprom(id, &sd);
+
+	if (!sd.supports_dom) {
+		printf("\t%-41s : No\n", "Optical diagnostics support");
+		return ;
+	}
+	printf("\t%-41s : Yes\n", "Optical diagnostics support");
+
+#define PRINT_BIAS(string, index)                                        \
+	printf("\t%-41s : %.3f mA\n", (string),                          \
+	       (double)(sd.bias_cur[(index)] / 500.))
+
+# define PRINT_xX_PWR(string, var, index)                                \
+	printf("\t%-41s : %.4f mW / %.2f dBm\n", (string),               \
+	       (double)((var)[(index)] / 10000.),                        \
+	       convert_mw_to_dbm((double)((var)[(index)] / 10000.)))
+
+#define PRINT_TEMP(string, index)                                        \
+	printf("\t%-41s : %.2f degrees C / %.2f degrees F\n", (string),  \
+	       (double)(sd.sfp_temp[(index)] / 256.),                    \
+	       (double)(sd.sfp_temp[(index)] / 256. * 1.8 + 32.))
+
+#define PRINT_VCC(string, index)                                         \
+	printf("\t%-41s : %.4f V\n", (string),                           \
+	       (double)(sd.sfp_voltage[(index)] / 10000.))
+
+	PRINT_BIAS("Laser bias current", MCURR);
+	PRINT_xX_PWR("Laser output power", sd.tx_power, MCURR);
+
+	if (!sd.rx_power_type)
+		rx_power_string = "Receiver signal OMA";
+	else
+		rx_power_string = "Receiver signal average optical power";
+
+	PRINT_xX_PWR(rx_power_string, sd.rx_power, MCURR);
+
+	PRINT_TEMP("Module temperature", MCURR);
+	PRINT_VCC("Module voltage", MCURR);
+
+	printf("\t%-41s : %s\n", "Alarm/warning flags implemented",
+	       (sd.supports_alarms ? "Yes" : "No"));
+	if (sd.supports_alarms) {
+
+		for (i = 0; sff8472_aw_flags[i].str; ++i) {
+			printf("\t%-41s : %s\n", sff8472_aw_flags[i].str,
+			       id[SFF_A2_BASE + sff8472_aw_flags[i].offset]
+			       & sff8472_aw_flags[i].value ? "On" : "Off");
+		}
+
+		PRINT_BIAS("Laser bias current high alarm threshold",   HALRM);
+		PRINT_BIAS("Laser bias current low alarm threshold",    LALRM);
+		PRINT_BIAS("Laser bias current high warning threshold", HWARN);
+		PRINT_BIAS("Laser bias current low warning threshold",  LWARN);
+
+		PRINT_xX_PWR("Laser output power high alarm threshold",
+			     sd.tx_power, HALRM);
+		PRINT_xX_PWR("Laser output power low alarm threshold",
+			     sd.tx_power, LALRM);
+		PRINT_xX_PWR("Laser output power high warning threshold",
+			     sd.tx_power, HWARN);
+		PRINT_xX_PWR("Laser output power low warning threshold",
+			     sd.tx_power, LWARN);
+
+		PRINT_TEMP("Module temperature high alarm threshold",   HALRM);
+		PRINT_TEMP("Module temperature low alarm threshold",    LALRM);
+		PRINT_TEMP("Module temperature high warning threshold", HWARN);
+		PRINT_TEMP("Module temperature low warning threshold",  LWARN);
+
+		PRINT_VCC("Module voltage high alarm threshold",   HALRM);
+		PRINT_VCC("Module voltage low alarm threshold",    LALRM);
+		PRINT_VCC("Module voltage high warning threshold", HWARN);
+		PRINT_VCC("Module voltage low warning threshold",  LWARN);
+
+		PRINT_xX_PWR("Laser rx power high alarm threshold",
+			     sd.rx_power, HALRM);
+		PRINT_xX_PWR("Laser rx power low alarm threshold",
+			     sd.rx_power, LALRM);
+		PRINT_xX_PWR("Laser rx power high warning threshold",
+			     sd.rx_power, HWARN);
+		PRINT_xX_PWR("Laser rx power low warning threshold",
+			     sd.rx_power, LWARN);
+	}
+
+}
+
diff --git a/sfpid.c b/sfpid.c
index a4a671d..4f88aa2 100644
--- a/sfpid.c
+++ b/sfpid.c
@@ -12,7 +12,7 @@
 
 static void sff8079_show_identifier(const __u8 *id)
 {
-	printf("\tIdentifier          : 0x%02x", id[0]);
+	printf("\t%-41s : 0x%02x", "Identifier", id[0]);
 	switch (id[0]) {
 	case 0x00:
 		printf(" (no module present, unknown, or unspecified)\n");
@@ -34,7 +34,7 @@ static void sff8079_show_identifier(const __u8 *id)
 
 static void sff8079_show_ext_identifier(const __u8 *id)
 {
-	printf("\tExtended identifier : 0x%02x", id[1]);
+	printf("\t%-41s : 0x%02x", "Extended identifier", id[1]);
 	if (id[1] == 0x00)
 		printf(" (GBIC not specified / not MOD_DEF compliant)\n");
 	else if (id[1] == 0x04)
@@ -47,7 +47,7 @@ static void sff8079_show_ext_identifier(const __u8 *id)
 
 static void sff8079_show_connector(const __u8 *id)
 {
-	printf("\tConnector           : 0x%02x", id[2]);
+	printf("\t%-41s : 0x%02x", "Connector", id[2]);
 	switch (id[2]) {
 	case 0x00:
 		printf(" (unknown or unspecified)\n");
@@ -105,10 +105,12 @@ static void sff8079_show_connector(const __u8 *id)
 
 static void sff8079_show_transceiver(const __u8 *id)
 {
-	static const char *pfx = "\t                    :  =>";
+	static const char *pfx =
+		"\tTransceiver type                          :";
 
-	printf("\tTransceiver codes   : 0x%02x 0x%02x 0x%02x" \
+	printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \
 	       "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+		   "Transceiver codes",
 	       id[3], id[4], id[5], id[6],
 	       id[7], id[8], id[9], id[10]);
 	/* 10G Ethernet Compliance Codes */
@@ -239,7 +241,7 @@ static void sff8079_show_transceiver(const __u8 *id)
 
 static void sff8079_show_encoding(const __u8 *id)
 {
-	printf("\tEncoding            : 0x%02x", id[11]);
+	printf("\t%-41s : 0x%02x", "Encoding", id[11]);
 	switch (id[11]) {
 	case 0x00:
 		printf(" (unspecified)\n");
@@ -270,7 +272,7 @@ static void sff8079_show_encoding(const __u8 *id)
 
 static void sff8079_show_rate_identifier(const __u8 *id)
 {
-	printf("\tRate identifier     : 0x%02x", id[13]);
+	printf("\t%-41s : 0x%02x", "Rate identifier", id[13]);
 	switch (id[13]) {
 	case 0x00:
 		printf(" (unspecified)\n");
@@ -295,14 +297,14 @@ static void sff8079_show_rate_identifier(const __u8 *id)
 
 static void sff8079_show_oui(const __u8 *id)
 {
-	printf("\tVendor OUI          : %02x:%02x:%02x\n",
+	printf("\t%-41s : %02x:%02x:%02x\n", "Vendor OUI",
 	       id[37], id[38], id[39]);
 }
 
 static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 {
 	if (id[8] & (1 << 2)) {
-		printf("\tPassive Cu cmplnce. : 0x%02x", id[60]);
+		printf("\t%-41s : 0x%02x", "Passive Cu cmplnce.", id[60]);
 		switch (id[60]) {
 		case 0x00:
 			printf(" (unspecified)");
@@ -316,7 +318,7 @@ static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 		}
 		printf(" [SFF-8472 rev10.4 only]\n");
 	} else if (id[8] & (1 << 3)) {
-		printf("\tActive Cu cmplnce.  : 0x%02x", id[60]);
+		printf("\t%-41s : 0x%02x", "Active Cu cmplnce.", id[60]);
 		switch (id[60]) {
 		case 0x00:
 			printf(" (unspecified)");
@@ -333,7 +335,7 @@ static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 		}
 		printf(" [SFF-8472 rev10.4 only]\n");
 	} else {
-		printf("\tLaser wavelength    : %unm\n",
+		printf("\t%-41s : %unm\n", "Laser wavelength",
 		       (id[60] << 8) | id[61]);
 	}
 }
@@ -344,7 +346,7 @@ static void sff8079_show_value_with_unit(const __u8 *id, unsigned int reg,
 {
 	unsigned int val = id[reg];
 
-	printf("\t%-20s: %u%s\n", name, val * mult, unit);
+	printf("\t%-41s : %u%s\n", name, val * mult, unit);
 }
 
 static void sff8079_show_ascii(const __u8 *id, unsigned int first_reg,
@@ -352,7 +354,7 @@ static void sff8079_show_ascii(const __u8 *id, unsigned int first_reg,
 {
 	unsigned int reg, val;
 
-	printf("\t%-20s: ", name);
+	printf("\t%-41s : ", name);
 	for (reg = first_reg; reg <= last_reg; reg++) {
 		val = id[reg];
 		putchar(((val >= 32) && (val <= 126)) ? val : '_');
diff --git a/test-cmdline.c b/test-cmdline.c
index 85b4ce0..f1d4555 100644
--- a/test-cmdline.c
+++ b/test-cmdline.c
@@ -213,6 +213,8 @@ static struct test_case {
 	{ 0, "-m devname" },
 	{ 1, "--dump-module-eeprom" },
 	{ 0, "--dump-module-eeprom devname" },
+	{ 1, "--module-info" },
+	{ 0, "--module-info devname" },
 	{ 0, "-m devname raw on" },
 	{ 0, "-m devname raw off" },
 	{ 0, "-m devname hex on" },
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-12-02 21:47                       ` Aurélien
@ 2012-12-02 22:00                         ` Aurélien
  2012-12-03 17:43                           ` Ben Hutchings
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien @ 2012-12-02 22:00 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev

[-- Attachment #1: Type: text/plain, Size: 571 bytes --]

On Sun, Dec 2, 2012 at 10:47 PM, Aurélien <footplus@gmail.com> wrote:
>>
>> This version drops the -lm completely, so it doesn't link.  Maybe you
>> edited the generated Makefile or Makefile.in?
>
> No, I just stupidly forgot to make distclean & autogen after removing
> all libm checks. Re-added AC_CHECK_LIB to link with it.
>

Just after re-reading this, I thought it was silly; I instead edited
Makefile.am and reverted the configure.ac change. Here's a new
full-patch with that fix along with the rest.

Sorry for the noise.
-- 
Aurélien Guillaume

[-- Attachment #2: 0001-Implemented-basic-optics-diagnostics-for-SFF-8472.patch --]
[-- Type: application/octet-stream, Size: 22479 bytes --]

From c6e58988c0c30123f78e0ae83730697bb2b159d0 Mon Sep 17 00:00:00 2001
From: Aurelien Guillaume <aurelien@iwi.me>
Date: Sun, 2 Dec 2012 21:21:01 +0100
Subject: [PATCH] Implemented basic optics diagnostics for SFF-8472


Signed-off-by: Aurelien Guillaume <aurelien@iwi.me>
---
 Makefile.am    |    3 +-
 ethtool.8.in   |    8 +-
 ethtool.c      |   17 +++-
 internal.h     |    3 +
 sfpdiag.c      |  362 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sfpid.c        |   28 +++--
 test-cmdline.c |    2 +
 7 files changed, 404 insertions(+), 19 deletions(-)
 create mode 100644 sfpdiag.c

diff --git a/Makefile.am b/Makefile.am
index e33f71f..ba1faa6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,5 @@
 AM_CFLAGS = -Wall
+LDADD = -lm
 
 man_MANS = ethtool.8
 EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh
@@ -9,7 +10,7 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
 		  fec_8xx.c ibm_emac.c ixgb.c ixgbe.c natsemi.c	\
 		  pcnet32.c realtek.c tg3.c marvell.c vioc.c	\
 		  smsc911x.c at76c50x-usb.c sfc.c stmmac.c	\
-		  rxclass.c sfpid.c
+		  rxclass.c sfpid.c sfpdiag.c
 
 TESTS = test-cmdline test-features
 check_PROGRAMS = test-cmdline test-features
diff --git a/ethtool.8.in b/ethtool.8.in
index a3c7fbb..e701919 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -312,7 +312,7 @@ ethtool \- query or control network driver and hardware settings
 .BN other
 .BN combined
 .HP
-.B ethtool \-m|\-\-dump\-module\-eeprom
+.B ethtool \-m|\-\-dump\-module\-eeprom|\-\-module\-info
 .I devname
 .B2 raw on off
 .B2 hex on off
@@ -815,8 +815,10 @@ Changes the number of channels used only for other purposes e.g. link interrupts
 .BI combined \ N
 Changes the number of multi-purpose channels.
 .TP
-.B \-m \-\-dump\-module\-eeprom
-Retrieves and if possible decodes the EEPROM from plugin modules, e.g SFP+, QSFP
+.B \-m \-\-dump\-module\-eeprom \-\-module\-info
+Retrieves and if possible decodes the EEPROM from plugin modules, e.g SFP+, QSFP.
+If the driver and module support it, the optical diagnostic information is also
+read and decoded.
 .TP
 .B \-\-show\-priv\-flags
 Queries the specified network device for its private flags.  The
diff --git a/ethtool.c b/ethtool.c
index 3db7fec..345c21c 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3604,6 +3604,16 @@ static int do_getmodule(struct cmd_context *ctx)
 		return 1;
 	}
 
+	/*
+	 * SFF-8079 EEPROM layout contains the memory available at A0 address on
+	 * the PHY EEPROM.
+	 * SFF-8472 defines a virtual extension of the EEPROM, where the
+	 * microcontroller on the SFP/SFP+ generates a page at the A2 address,
+	 * which contains data relative to optical diagnostics.
+	 * The current kernel implementation returns a blob, which contains:
+	 *  - ETH_MODULE_SFF_8079 => The A0 page only.
+	 *  - ETH_MODULE_SFF_8472 => The A0 and A2 page concatenated.
+	 */
 	if (geeprom_dump_raw) {
 		fwrite(eeprom->data, 1, eeprom->len, stdout);
 	} else {
@@ -3613,8 +3623,11 @@ static int do_getmodule(struct cmd_context *ctx)
 		} else if (!geeprom_dump_hex) {
 			switch (modinfo.type) {
 			case ETH_MODULE_SFF_8079:
+				sff8079_show_all(eeprom->data);
+				break;
 			case ETH_MODULE_SFF_8472:
 				sff8079_show_all(eeprom->data);
+				sff8472_show_all(eeprom->data);
 				break;
 			default:
 				geeprom_dump_hex = 1;
@@ -3831,8 +3844,8 @@ static const struct option {
 	{ "--show-priv-flags" , 1, do_gprivflags, "Query private flags" },
 	{ "--set-priv-flags", 1, do_sprivflags, "Set private flags",
 	  "		FLAG on|off ...\n" },
-	{ "-m|--dump-module-eeprom", 1, do_getmodule,
-	  "Qeuery/Decode Module EEPROM information",
+	{ "-m|--dump-module-eeprom|--module-info", 1, do_getmodule,
+	  "Query/Decode Module EEPROM information and optical diagnostics if available",
 	  "		[ raw on|off ]\n"
 	  "		[ hex on|off ]\n"
 	  "		[ offset N ]\n"
diff --git a/internal.h b/internal.h
index 4f96fd5..e977a81 100644
--- a/internal.h
+++ b/internal.h
@@ -253,4 +253,7 @@ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
 /* Module EEPROM parsing code */
 void sff8079_show_all(const __u8 *id);
 
+/* Optics diagnostics */
+void sff8472_show_all(const __u8 *id);
+
 #endif /* ETHTOOL_INTERNAL_H__ */
diff --git a/sfpdiag.c b/sfpdiag.c
new file mode 100644
index 0000000..f67e491
--- /dev/null
+++ b/sfpdiag.c
@@ -0,0 +1,362 @@
+/*
+ * sfpdiag.c: Implements SFF-8472 optics diagnostics.
+ *
+ * Aurelien Guillaume <aurelien@iwi.me> (C) 2012
+ *   This implementation is loosely based on DOM patches
+ *   from Robert Olsson <robert@herjulf.se> (C) 2009
+ *   and SFF-8472 specs (ftp://ftp.seagate.com/pub/sff/SFF-8472.PDF)
+ *   by SFF Committee.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <arpa/inet.h>
+#include "internal.h"
+
+/* Offsets in decimal, for direct comparison with the SFF specs */
+
+/* A0-based EEPROM offsets for DOM support checks */
+#define SFF_A0_DOM                        92
+#define SFF_A0_OPTIONS                    93
+#define SFF_A0_COMP                       94
+
+/* EEPROM bit values for various registers */
+#define SFF_A0_DOM_EXTCAL                 (1 << 4)
+#define SFF_A0_DOM_INTCAL                 (1 << 5)
+#define SFF_A0_DOM_IMPL                   (1 << 6)
+#define SFF_A0_DOM_PWRT                   (1 << 3)
+
+#define SFF_A0_OPTIONS_AW                 (1 << 7)
+
+/*
+ * See ethtool.c comments about SFF-8472, this is the offset
+ * at which the A2 page is in the EEPROM blob returned by the
+ * kernel.
+ */
+#define SFF_A2_BASE                       0x100
+
+/* A2-based offsets for DOM */
+#define SFF_A2_TEMP                       96
+#define SFF_A2_TEMP_HALRM                 0
+#define SFF_A2_TEMP_LALRM                 2
+#define SFF_A2_TEMP_HWARN                 4
+#define SFF_A2_TEMP_LWARN                 6
+
+#define SFF_A2_VCC                        98
+#define SFF_A2_VCC_HALRM                  8
+#define SFF_A2_VCC_LALRM                  10
+#define SFF_A2_VCC_HWARN                  12
+#define SFF_A2_VCC_LWARN                  14
+
+#define SFF_A2_BIAS                       96
+#define SFF_A2_BIAS_HALRM                 16
+#define SFF_A2_BIAS_LALRM                 18
+#define SFF_A2_BIAS_HWARN                 20
+#define SFF_A2_BIAS_LWARN                 22
+
+#define SFF_A2_TX_PWR                     102
+#define SFF_A2_TX_PWR_HALRM               24
+#define SFF_A2_TX_PWR_LALRM               26
+#define SFF_A2_TX_PWR_HWARN               28
+#define SFF_A2_TX_PWR_LWARN               30
+
+#define SFF_A2_RX_PWR                     104
+#define SFF_A2_RX_PWR_HALRM               32
+#define SFF_A2_RX_PWR_LALRM               34
+#define SFF_A2_RX_PWR_HWARN               36
+#define SFF_A2_RX_PWR_LWARN               38
+
+#define SFF_A2_ALRM_FLG                   112
+#define SFF_A2_WARN_FLG                   116
+
+/* 32-bit little-endian calibration constants */
+#define SFF_A2_CAL_RXPWR4                 56
+#define SFF_A2_CAL_RXPWR3                 60
+#define SFF_A2_CAL_RXPWR2                 64
+#define SFF_A2_CAL_RXPWR1                 68
+#define SFF_A2_CAL_RXPWR0                 72
+
+/* 16-bit little endian calibration constants */
+#define SFF_A2_CAL_TXI_SLP                76
+#define SFF_A2_CAL_TXI_OFF                78
+#define SFF_A2_CAL_TXPWR_SLP              80
+#define SFF_A2_CAL_TXPWR_OFF              82
+#define SFF_A2_CAL_T_SLP                  84
+#define SFF_A2_CAL_T_OFF                  86
+#define SFF_A2_CAL_V_SLP                  88
+#define SFF_A2_CAL_V_OFF                  90
+
+
+struct sff8472_diags {
+
+#define MCURR 0
+#define LWARN 1
+#define HWARN 2
+#define LALRM 3
+#define HALRM 4
+
+	/* [5] tables are current, low/high warn, low/high alarm */
+	__u8 supports_dom;      /* Supports DOM */
+	__u8 supports_alarms;   /* Supports alarm/warning thold */
+	__u8 calibrated_ext;    /* Is externally calibrated */
+	__u16 bias_cur[5];      /* Measured bias current in 2uA units */
+	__u16 tx_power[5];      /* Measured TX Power in 0.1uW units */
+	__u16 rx_power[5];      /* Measured RX Power */
+	__u8  rx_power_type;    /* 0 = OMA, 1 = Average power */
+	__s16 sfp_temp[5];      /* SFP Temp in 16-bit signed 1/256 Celcius */
+	__u16 sfp_voltage[5];   /* SFP voltage in 0.1mV units */
+
+};
+
+static struct sff8472_aw_flags {
+	const char *str;        /* Human-readable string, null at the end */
+	int offset;             /* A2-relative adress offset */
+	__u8 value;             /* Alarm is on if (offset & value) != 0. */
+} sff8472_aw_flags[] = {
+	{ "Laser bias current high alarm",   SFF_A2_ALRM_FLG, (1 << 3) },
+	{ "Laser bias current low alarm",    SFF_A2_ALRM_FLG, (1 << 2) },
+	{ "Laser bias current high warning", SFF_A2_WARN_FLG, (1 << 3) },
+	{ "Laser bias current low warning",  SFF_A2_WARN_FLG, (1 << 2) },
+
+	{ "Laser output power high alarm",   SFF_A2_ALRM_FLG, (1 << 1) },
+	{ "Laser output power low alarm",    SFF_A2_ALRM_FLG, (1 << 0) },
+	{ "Laser output power high warning", SFF_A2_WARN_FLG, (1 << 1) },
+	{ "Laser output power low warning",  SFF_A2_WARN_FLG, (1 << 0) },
+
+	{ "Module temperature high alarm",   SFF_A2_ALRM_FLG, (1 << 7) },
+	{ "Module temperature low alarm",    SFF_A2_ALRM_FLG, (1 << 6) },
+	{ "Module temperature high warning", SFF_A2_WARN_FLG, (1 << 7) },
+	{ "Module temperature low warning",  SFF_A2_WARN_FLG, (1 << 6) },
+
+	{ "Module voltage high alarm",   SFF_A2_ALRM_FLG, (1 << 5) },
+	{ "Module voltage low alarm",    SFF_A2_ALRM_FLG, (1 << 4) },
+	{ "Module voltage high warning", SFF_A2_WARN_FLG, (1 << 5) },
+	{ "Module voltage low warning",  SFF_A2_WARN_FLG, (1 << 4) },
+
+	{ "Laser rx power high alarm",   SFF_A2_ALRM_FLG + 1, (1 << 7) },
+	{ "Laser rx power low alarm",    SFF_A2_ALRM_FLG + 1, (1 << 6) },
+	{ "Laser rx power high warning", SFF_A2_WARN_FLG + 1, (1 << 7) },
+	{ "Laser rx power low warning",  SFF_A2_WARN_FLG + 1, (1 << 6) },
+
+	{ NULL, 0, 0 },
+};
+
+static double convert_mw_to_dbm(double mw)
+{
+	return (10. * log10(mw / 1000.)) + 30.;
+}
+
+
+/* Most common case: 16-bit unsigned integer in a certain unit */
+#define A2_OFFSET_TO_U16(offset) \
+	(id[SFF_A2_BASE + (offset)] << 8 | id[SFF_A2_BASE + (offset) + 1])
+
+/* Calibration slope is a number between 0.0 included and 256.0 excluded. */
+#define A2_OFFSET_TO_SLP(offset) \
+	(id[SFF_A2_BASE + (offset)] + id[SFF_A2_BASE + (offset) + 1] / 256.)
+
+/* Calibration offset is an integer from -32768 to 32767 */
+#define A2_OFFSET_TO_OFF(offset) \
+	((__s16)A2_OFFSET_TO_U16(offset))
+
+/* RXPWR(x) are IEEE-754 floating point numbers in big-endian format */
+#define A2_OFFSET_TO_RXPWRx(offset) \
+	(befloattoh((__u32 *)(id + SFF_A2_BASE + (offset))))
+
+/*
+ * 2-byte internal temperature conversions:
+ * First byte is a signed 8-bit integer, which is the temp decimal part
+ * Second byte are 1/256th of degree, which are added to the dec part.
+ */
+#define A2_OFFSET_TO_TEMP(offset) ((__s16)A2_OFFSET_TO_U16(offset))
+
+
+static void sff8472_dom_parse(const __u8 *id, struct sff8472_diags *sd)
+{
+
+	sd->bias_cur[MCURR] = A2_OFFSET_TO_U16(SFF_A2_BIAS);
+	sd->bias_cur[HALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HALRM);
+	sd->bias_cur[LALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LALRM);
+	sd->bias_cur[HWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HWARN);
+	sd->bias_cur[LWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LWARN);
+
+	sd->sfp_voltage[MCURR] = A2_OFFSET_TO_U16(SFF_A2_VCC);
+	sd->sfp_voltage[HALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_HALRM);
+	sd->sfp_voltage[LALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_LALRM);
+	sd->sfp_voltage[HWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_HWARN);
+	sd->sfp_voltage[LWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_LWARN);
+
+	sd->tx_power[MCURR] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR);
+	sd->tx_power[HALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HALRM);
+	sd->tx_power[LALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LALRM);
+	sd->tx_power[HWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HWARN);
+	sd->tx_power[LWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LWARN);
+
+	sd->rx_power[MCURR] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR);
+	sd->rx_power[HALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HALRM);
+	sd->rx_power[LALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LALRM);
+	sd->rx_power[HWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HWARN);
+	sd->rx_power[LWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LWARN);
+
+	sd->sfp_temp[MCURR] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP);
+	sd->sfp_temp[HALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HALRM);
+	sd->sfp_temp[LALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LALRM);
+	sd->sfp_temp[HWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HWARN);
+	sd->sfp_temp[LWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LWARN);
+
+}
+
+/* Converts to a float from a big-endian 4-byte source buffer. */
+static float befloattoh(const __u32 *source)
+{
+	union {
+		__u32 src;
+		float dst;
+	} converter;
+
+	converter.src = ntohl(*source);
+	return converter.dst;
+}
+
+static void sff8472_calibration(const __u8 *id, struct sff8472_diags *sd)
+{
+	int i;
+	__u16 rx_reading;
+
+	/* Calibration should occur for all values (threshold and current) */
+	for (i = 0; i < sizeof(sd->bias_cur); ++i) {
+		/*
+		 * Apply calibration formula 1 (Temp., Voltage, Bias, Tx Power)
+		 */
+		sd->bias_cur[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXI_SLP);
+		sd->tx_power[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXPWR_SLP);
+		sd->sfp_voltage[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_V_SLP);
+		sd->sfp_temp[i]    *= A2_OFFSET_TO_SLP(SFF_A2_CAL_T_SLP);
+
+		sd->bias_cur[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXI_OFF);
+		sd->tx_power[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXPWR_OFF);
+		sd->sfp_voltage[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_V_OFF);
+		sd->sfp_temp[i]    += A2_OFFSET_TO_OFF(SFF_A2_CAL_T_OFF);
+
+		/*
+		 * Apply calibration formula 2 (Rx Power only)
+		 */
+		rx_reading = sd->rx_power[i];
+		sd->rx_power[i]    = A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR0);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR1);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR2);
+		sd->rx_power[i]    += rx_reading *
+			A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR3);
+	}
+}
+
+static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd)
+{
+	sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
+	sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
+	sd->calibrated_ext = id[SFF_A0_DOM] & SFF_A0_DOM_EXTCAL;
+	sd->rx_power_type = id[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
+
+	sff8472_dom_parse(id, sd);
+
+	/*
+	 * If the SFP is externally calibrated, we need to read calibration data
+	 * and compensate the already stored readings.
+	 */
+	if (sd->calibrated_ext)
+		sff8472_calibration(id, sd);
+}
+
+void sff8472_show_all(const __u8 *id)
+{
+	struct sff8472_diags sd;
+	char *rx_power_string = NULL;
+	int i;
+
+	sff8472_parse_eeprom(id, &sd);
+
+	if (!sd.supports_dom) {
+		printf("\t%-41s : No\n", "Optical diagnostics support");
+		return ;
+	}
+	printf("\t%-41s : Yes\n", "Optical diagnostics support");
+
+#define PRINT_BIAS(string, index)                                        \
+	printf("\t%-41s : %.3f mA\n", (string),                          \
+	       (double)(sd.bias_cur[(index)] / 500.))
+
+# define PRINT_xX_PWR(string, var, index)                                \
+	printf("\t%-41s : %.4f mW / %.2f dBm\n", (string),               \
+	       (double)((var)[(index)] / 10000.),                        \
+	       convert_mw_to_dbm((double)((var)[(index)] / 10000.)))
+
+#define PRINT_TEMP(string, index)                                        \
+	printf("\t%-41s : %.2f degrees C / %.2f degrees F\n", (string),  \
+	       (double)(sd.sfp_temp[(index)] / 256.),                    \
+	       (double)(sd.sfp_temp[(index)] / 256. * 1.8 + 32.))
+
+#define PRINT_VCC(string, index)                                         \
+	printf("\t%-41s : %.4f V\n", (string),                           \
+	       (double)(sd.sfp_voltage[(index)] / 10000.))
+
+	PRINT_BIAS("Laser bias current", MCURR);
+	PRINT_xX_PWR("Laser output power", sd.tx_power, MCURR);
+
+	if (!sd.rx_power_type)
+		rx_power_string = "Receiver signal OMA";
+	else
+		rx_power_string = "Receiver signal average optical power";
+
+	PRINT_xX_PWR(rx_power_string, sd.rx_power, MCURR);
+
+	PRINT_TEMP("Module temperature", MCURR);
+	PRINT_VCC("Module voltage", MCURR);
+
+	printf("\t%-41s : %s\n", "Alarm/warning flags implemented",
+	       (sd.supports_alarms ? "Yes" : "No"));
+	if (sd.supports_alarms) {
+
+		for (i = 0; sff8472_aw_flags[i].str; ++i) {
+			printf("\t%-41s : %s\n", sff8472_aw_flags[i].str,
+			       id[SFF_A2_BASE + sff8472_aw_flags[i].offset]
+			       & sff8472_aw_flags[i].value ? "On" : "Off");
+		}
+
+		PRINT_BIAS("Laser bias current high alarm threshold",   HALRM);
+		PRINT_BIAS("Laser bias current low alarm threshold",    LALRM);
+		PRINT_BIAS("Laser bias current high warning threshold", HWARN);
+		PRINT_BIAS("Laser bias current low warning threshold",  LWARN);
+
+		PRINT_xX_PWR("Laser output power high alarm threshold",
+			     sd.tx_power, HALRM);
+		PRINT_xX_PWR("Laser output power low alarm threshold",
+			     sd.tx_power, LALRM);
+		PRINT_xX_PWR("Laser output power high warning threshold",
+			     sd.tx_power, HWARN);
+		PRINT_xX_PWR("Laser output power low warning threshold",
+			     sd.tx_power, LWARN);
+
+		PRINT_TEMP("Module temperature high alarm threshold",   HALRM);
+		PRINT_TEMP("Module temperature low alarm threshold",    LALRM);
+		PRINT_TEMP("Module temperature high warning threshold", HWARN);
+		PRINT_TEMP("Module temperature low warning threshold",  LWARN);
+
+		PRINT_VCC("Module voltage high alarm threshold",   HALRM);
+		PRINT_VCC("Module voltage low alarm threshold",    LALRM);
+		PRINT_VCC("Module voltage high warning threshold", HWARN);
+		PRINT_VCC("Module voltage low warning threshold",  LWARN);
+
+		PRINT_xX_PWR("Laser rx power high alarm threshold",
+			     sd.rx_power, HALRM);
+		PRINT_xX_PWR("Laser rx power low alarm threshold",
+			     sd.rx_power, LALRM);
+		PRINT_xX_PWR("Laser rx power high warning threshold",
+			     sd.rx_power, HWARN);
+		PRINT_xX_PWR("Laser rx power low warning threshold",
+			     sd.rx_power, LWARN);
+	}
+
+}
+
diff --git a/sfpid.c b/sfpid.c
index a4a671d..4f88aa2 100644
--- a/sfpid.c
+++ b/sfpid.c
@@ -12,7 +12,7 @@
 
 static void sff8079_show_identifier(const __u8 *id)
 {
-	printf("\tIdentifier          : 0x%02x", id[0]);
+	printf("\t%-41s : 0x%02x", "Identifier", id[0]);
 	switch (id[0]) {
 	case 0x00:
 		printf(" (no module present, unknown, or unspecified)\n");
@@ -34,7 +34,7 @@ static void sff8079_show_identifier(const __u8 *id)
 
 static void sff8079_show_ext_identifier(const __u8 *id)
 {
-	printf("\tExtended identifier : 0x%02x", id[1]);
+	printf("\t%-41s : 0x%02x", "Extended identifier", id[1]);
 	if (id[1] == 0x00)
 		printf(" (GBIC not specified / not MOD_DEF compliant)\n");
 	else if (id[1] == 0x04)
@@ -47,7 +47,7 @@ static void sff8079_show_ext_identifier(const __u8 *id)
 
 static void sff8079_show_connector(const __u8 *id)
 {
-	printf("\tConnector           : 0x%02x", id[2]);
+	printf("\t%-41s : 0x%02x", "Connector", id[2]);
 	switch (id[2]) {
 	case 0x00:
 		printf(" (unknown or unspecified)\n");
@@ -105,10 +105,12 @@ static void sff8079_show_connector(const __u8 *id)
 
 static void sff8079_show_transceiver(const __u8 *id)
 {
-	static const char *pfx = "\t                    :  =>";
+	static const char *pfx =
+		"\tTransceiver type                          :";
 
-	printf("\tTransceiver codes   : 0x%02x 0x%02x 0x%02x" \
+	printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \
 	       "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+		   "Transceiver codes",
 	       id[3], id[4], id[5], id[6],
 	       id[7], id[8], id[9], id[10]);
 	/* 10G Ethernet Compliance Codes */
@@ -239,7 +241,7 @@ static void sff8079_show_transceiver(const __u8 *id)
 
 static void sff8079_show_encoding(const __u8 *id)
 {
-	printf("\tEncoding            : 0x%02x", id[11]);
+	printf("\t%-41s : 0x%02x", "Encoding", id[11]);
 	switch (id[11]) {
 	case 0x00:
 		printf(" (unspecified)\n");
@@ -270,7 +272,7 @@ static void sff8079_show_encoding(const __u8 *id)
 
 static void sff8079_show_rate_identifier(const __u8 *id)
 {
-	printf("\tRate identifier     : 0x%02x", id[13]);
+	printf("\t%-41s : 0x%02x", "Rate identifier", id[13]);
 	switch (id[13]) {
 	case 0x00:
 		printf(" (unspecified)\n");
@@ -295,14 +297,14 @@ static void sff8079_show_rate_identifier(const __u8 *id)
 
 static void sff8079_show_oui(const __u8 *id)
 {
-	printf("\tVendor OUI          : %02x:%02x:%02x\n",
+	printf("\t%-41s : %02x:%02x:%02x\n", "Vendor OUI",
 	       id[37], id[38], id[39]);
 }
 
 static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 {
 	if (id[8] & (1 << 2)) {
-		printf("\tPassive Cu cmplnce. : 0x%02x", id[60]);
+		printf("\t%-41s : 0x%02x", "Passive Cu cmplnce.", id[60]);
 		switch (id[60]) {
 		case 0x00:
 			printf(" (unspecified)");
@@ -316,7 +318,7 @@ static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 		}
 		printf(" [SFF-8472 rev10.4 only]\n");
 	} else if (id[8] & (1 << 3)) {
-		printf("\tActive Cu cmplnce.  : 0x%02x", id[60]);
+		printf("\t%-41s : 0x%02x", "Active Cu cmplnce.", id[60]);
 		switch (id[60]) {
 		case 0x00:
 			printf(" (unspecified)");
@@ -333,7 +335,7 @@ static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
 		}
 		printf(" [SFF-8472 rev10.4 only]\n");
 	} else {
-		printf("\tLaser wavelength    : %unm\n",
+		printf("\t%-41s : %unm\n", "Laser wavelength",
 		       (id[60] << 8) | id[61]);
 	}
 }
@@ -344,7 +346,7 @@ static void sff8079_show_value_with_unit(const __u8 *id, unsigned int reg,
 {
 	unsigned int val = id[reg];
 
-	printf("\t%-20s: %u%s\n", name, val * mult, unit);
+	printf("\t%-41s : %u%s\n", name, val * mult, unit);
 }
 
 static void sff8079_show_ascii(const __u8 *id, unsigned int first_reg,
@@ -352,7 +354,7 @@ static void sff8079_show_ascii(const __u8 *id, unsigned int first_reg,
 {
 	unsigned int reg, val;
 
-	printf("\t%-20s: ", name);
+	printf("\t%-41s : ", name);
 	for (reg = first_reg; reg <= last_reg; reg++) {
 		val = id[reg];
 		putchar(((val >= 32) && (val <= 126)) ? val : '_');
diff --git a/test-cmdline.c b/test-cmdline.c
index 85b4ce0..f1d4555 100644
--- a/test-cmdline.c
+++ b/test-cmdline.c
@@ -213,6 +213,8 @@ static struct test_case {
 	{ 0, "-m devname" },
 	{ 1, "--dump-module-eeprom" },
 	{ 0, "--dump-module-eeprom devname" },
+	{ 1, "--module-info" },
+	{ 0, "--module-info devname" },
 	{ 0, "-m devname raw on" },
 	{ 0, "-m devname raw off" },
 	{ 0, "-m devname hex on" },
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: Optics (SFP) monitoring on ixgbe and igbe
  2012-12-02 22:00                         ` Aurélien
@ 2012-12-03 17:43                           ` Ben Hutchings
  0 siblings, 0 replies; 17+ messages in thread
From: Ben Hutchings @ 2012-12-03 17:43 UTC (permalink / raw)
  To: footplus; +Cc: netdev

On Sun, 2012-12-02 at 23:00 +0100, Aurélien wrote:
> On Sun, Dec 2, 2012 at 10:47 PM, Aurélien <footplus@gmail.com> wrote:
> >>
> >> This version drops the -lm completely, so it doesn't link.  Maybe you
> >> edited the generated Makefile or Makefile.in?
> >
> > No, I just stupidly forgot to make distclean & autogen after removing
> > all libm checks. Re-added AC_CHECK_LIB to link with it.
> >
> 
> Just after re-reading this, I thought it was silly; I instead edited
> Makefile.am and reverted the configure.ac change. Here's a new
> full-patch with that fix along with the rest.
> 
> Sorry for the noise.

Applied, thanks.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2012-12-03 17:43 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-11-07 12:27 Optics (SFP) monitoring on ixgbe and igbe Aurélien
2012-11-07 19:58 ` Ben Hutchings
2012-11-08  0:39   ` Aurélien
2012-11-09 15:08     ` Ben Hutchings
2012-11-09 15:31       ` Aurélien
2012-11-15 21:36         ` Aurélien
2012-11-15 22:46           ` Jeff Kirsher
2012-11-15 23:30             ` Ben Hutchings
2012-11-16  2:23               ` Aurélien
2012-11-16  6:25                 ` Jeff Kirsher
2012-11-16 19:38                 ` Ben Hutchings
2012-11-18 21:35                   ` Aurélien
2012-11-19  7:27                     ` Robert Olsson
2012-12-01  4:18                     ` Ben Hutchings
2012-12-02 21:47                       ` Aurélien
2012-12-02 22:00                         ` Aurélien
2012-12-03 17:43                           ` Ben Hutchings

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).