diff --git a/.ethtool.metadata b/.ethtool.metadata index 8d4c891..7b43eeb 100644 --- a/.ethtool.metadata +++ b/.ethtool.metadata @@ -1 +1 @@ -cb01cb38c18474e21c3741aec9b58030b26ba489 SOURCES/ethtool-3.15.tar.xz +cd71638cf3033dc83422b48a8c5e8831e6e05a84 SOURCES/ethtool-4.5.tar.xz diff --git a/.gitignore b/.gitignore index dfb92ea..135bc2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/ethtool-3.15.tar.xz +SOURCES/ethtool-4.5.tar.xz diff --git a/SOURCES/0001-ethtool-copy.h-sync-with-net.patch b/SOURCES/0001-ethtool-copy.h-sync-with-net.patch deleted file mode 100644 index 8b015cb..0000000 --- a/SOURCES/0001-ethtool-copy.h-sync-with-net.patch +++ /dev/null @@ -1,680 +0,0 @@ -From e63362618ed0b54fe8341c700cf5fe695a24b258 Mon Sep 17 00:00:00 2001 -From: Ben Hutchings -Date: Sun, 21 Sep 2014 21:40:46 +0100 -Subject: [PATCH 1/4] ethtool-copy.h: sync with net - -This covers kernel changes up to: - -commit 38c891a49dec43dbb1575cc40d10dbd49c4961ab -Author: Ben Hutchings -Date: Thu May 15 01:07:16 2014 +0100 - - ethtool: Improve explanation of the two arrays following struct ethtool_rxfh - -Signed-off-by: Ben Hutchings ---- - ethtool-copy.h | 478 +++++++++++++++++++++++++++++++++++++++++++-------------- - 1 file changed, 363 insertions(+), 115 deletions(-) - -diff --git a/ethtool-copy.h b/ethtool-copy.h -index b5515c2..61b78fc 100644 ---- a/ethtool-copy.h -+++ b/ethtool-copy.h -@@ -16,37 +16,97 @@ - #include - #include - --/* This should work for both 32 and 64 bit userland. */ -+/* All structures exposed to userland should be defined such that they -+ * have the same layout for 32-bit and 64-bit userland. -+ */ -+ -+/** -+ * struct ethtool_cmd - link control and status -+ * @cmd: Command number = %ETHTOOL_GSET or %ETHTOOL_SSET -+ * @supported: Bitmask of %SUPPORTED_* flags for the link modes, -+ * physical connectors and other link features for which the -+ * interface supports autonegotiation or auto-detection. -+ * Read-only. -+ * @advertising: Bitmask of %ADVERTISED_* flags for the link modes, -+ * physical connectors and other link features that are -+ * advertised through autonegotiation or enabled for -+ * auto-detection. -+ * @speed: Low bits of the speed -+ * @duplex: Duplex mode; one of %DUPLEX_* -+ * @port: Physical connector type; one of %PORT_* -+ * @phy_address: MDIO address of PHY (transceiver); 0 or 255 if not -+ * applicable. For clause 45 PHYs this is the PRTAD. -+ * @transceiver: Historically used to distinguish different possible -+ * PHY types, but not in a consistent way. Deprecated. -+ * @autoneg: Enable/disable autonegotiation and auto-detection; -+ * either %AUTONEG_DISABLE or %AUTONEG_ENABLE -+ * @mdio_support: Bitmask of %ETH_MDIO_SUPPORTS_* flags for the MDIO -+ * protocols supported by the interface; 0 if unknown. -+ * Read-only. -+ * @maxtxpkt: Historically used to report TX IRQ coalescing; now -+ * obsoleted by &struct ethtool_coalesce. Read-only; deprecated. -+ * @maxrxpkt: Historically used to report RX IRQ coalescing; now -+ * obsoleted by &struct ethtool_coalesce. Read-only; deprecated. -+ * @speed_hi: High bits of the speed -+ * @eth_tp_mdix: Ethernet twisted-pair MDI(-X) status; one of -+ * %ETH_TP_MDI_*. If the status is unknown or not applicable, the -+ * value will be %ETH_TP_MDI_INVALID. Read-only. -+ * @eth_tp_mdix_ctrl: Ethernet twisted pair MDI(-X) control; one of -+ * %ETH_TP_MDI_*. If MDI(-X) control is not implemented, reads -+ * yield %ETH_TP_MDI_INVALID and writes may be ignored or rejected. -+ * When written successfully, the link should be renegotiated if -+ * necessary. -+ * @lp_advertising: Bitmask of %ADVERTISED_* flags for the link modes -+ * and other link features that the link partner advertised -+ * through autonegotiation; 0 if unknown or not applicable. -+ * Read-only. -+ * -+ * The link speed in Mbps is split between @speed and @speed_hi. Use -+ * the ethtool_cmd_speed() and ethtool_cmd_speed_set() functions to -+ * access it. -+ * -+ * If autonegotiation is disabled, the speed and @duplex represent the -+ * fixed link mode and are writable if the driver supports multiple -+ * link modes. If it is enabled then they are read-only; if the link -+ * is up they represent the negotiated link mode; if the link is down, -+ * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and -+ * @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode. -+ * -+ * Some hardware interfaces may have multiple PHYs and/or physical -+ * connectors fitted or do not allow the driver to detect which are -+ * fitted. For these interfaces @port and/or @phy_address may be -+ * writable, possibly dependent on @autoneg being %AUTONEG_DISABLE. -+ * Otherwise, attempts to write different values may be ignored or -+ * rejected. -+ * -+ * Users should assume that all fields not marked read-only are -+ * writable and subject to validation by the driver. They should use -+ * %ETHTOOL_GSET to get the current values before making specific -+ * changes and then applying them with %ETHTOOL_SSET. -+ * -+ * Drivers that implement set_settings() should validate all fields -+ * other than @cmd that are not described as read-only or deprecated, -+ * and must ignore all fields described as read-only. -+ * -+ * Deprecated fields should be ignored by both users and drivers. -+ */ - struct ethtool_cmd { - __u32 cmd; -- __u32 supported; /* Features this interface supports */ -- __u32 advertising; /* Features this interface advertises */ -- __u16 speed; /* The forced speed (lower bits) in -- * Mbps. Please use -- * ethtool_cmd_speed()/_set() to -- * access it */ -- __u8 duplex; /* Duplex, half or full */ -- __u8 port; /* Which connector port */ -- __u8 phy_address; /* MDIO PHY address (PRTAD for clause 45). -- * May be read-only or read-write -- * depending on the driver. -- */ -- __u8 transceiver; /* Which transceiver to use */ -- __u8 autoneg; /* Enable or disable autonegotiation */ -- __u8 mdio_support; /* MDIO protocols supported. Read-only. -- * Not set by all drivers. -- */ -- __u32 maxtxpkt; /* Tx pkts before generating tx int */ -- __u32 maxrxpkt; /* Rx pkts before generating rx int */ -- __u16 speed_hi; /* The forced speed (upper -- * bits) in Mbps. Please use -- * ethtool_cmd_speed()/_set() to -- * access it */ -- __u8 eth_tp_mdix; /* twisted pair MDI-X status */ -- __u8 eth_tp_mdix_ctrl; /* twisted pair MDI-X control, when set, -- * link should be renegotiated if necessary -- */ -- __u32 lp_advertising; /* Features the link partner advertises */ -+ __u32 supported; -+ __u32 advertising; -+ __u16 speed; -+ __u8 duplex; -+ __u8 port; -+ __u8 phy_address; -+ __u8 transceiver; -+ __u8 autoneg; -+ __u8 mdio_support; -+ __u32 maxtxpkt; -+ __u32 maxrxpkt; -+ __u16 speed_hi; -+ __u8 eth_tp_mdix; -+ __u8 eth_tp_mdix_ctrl; -+ __u32 lp_advertising; - __u32 reserved[2]; - }; - -@@ -79,37 +139,68 @@ static __inline__ __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) - - #define ETHTOOL_FWVERS_LEN 32 - #define ETHTOOL_BUSINFO_LEN 32 --/* these strings are set to whatever the driver author decides... */ -+ -+/** -+ * struct ethtool_drvinfo - general driver and device information -+ * @cmd: Command number = %ETHTOOL_GDRVINFO -+ * @driver: Driver short name. This should normally match the name -+ * in its bus driver structure (e.g. pci_driver::name). Must -+ * not be an empty string. -+ * @version: Driver version string; may be an empty string -+ * @fw_version: Firmware version string; may be an empty string -+ * @bus_info: Device bus address. This should match the dev_name() -+ * string for the underlying bus device, if there is one. May be -+ * an empty string. -+ * @n_priv_flags: Number of flags valid for %ETHTOOL_GPFLAGS and -+ * %ETHTOOL_SPFLAGS commands; also the number of strings in the -+ * %ETH_SS_PRIV_FLAGS set -+ * @n_stats: Number of u64 statistics returned by the %ETHTOOL_GSTATS -+ * command; also the number of strings in the %ETH_SS_STATS set -+ * @testinfo_len: Number of results returned by the %ETHTOOL_TEST -+ * command; also the number of strings in the %ETH_SS_TEST set -+ * @eedump_len: Size of EEPROM accessible through the %ETHTOOL_GEEPROM -+ * and %ETHTOOL_SEEPROM commands, in bytes -+ * @regdump_len: Size of register dump returned by the %ETHTOOL_GREGS -+ * command, in bytes -+ * -+ * Users can use the %ETHTOOL_GSSET_INFO command to get the number of -+ * strings in any string set (from Linux 2.6.34). -+ * -+ * Drivers should set at most @driver, @version, @fw_version and -+ * @bus_info in their get_drvinfo() implementation. The ethtool -+ * core fills in the other fields using other driver operations. -+ */ - struct ethtool_drvinfo { - __u32 cmd; -- char driver[32]; /* driver short name, "tulip", "eepro100" */ -- char version[32]; /* driver version string */ -- char fw_version[ETHTOOL_FWVERS_LEN]; /* firmware version string */ -- char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ -- /* For PCI devices, use pci_name(pci_dev). */ -+ char driver[32]; -+ char version[32]; -+ char fw_version[ETHTOOL_FWVERS_LEN]; -+ char bus_info[ETHTOOL_BUSINFO_LEN]; - char reserved1[32]; - char reserved2[12]; -- /* -- * Some struct members below are filled in -- * using ops->get_sset_count(). Obtaining -- * this info from ethtool_drvinfo is now -- * deprecated; Use ETHTOOL_GSSET_INFO -- * instead. -- */ -- __u32 n_priv_flags; /* number of flags valid in ETHTOOL_GPFLAGS */ -- __u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ -+ __u32 n_priv_flags; -+ __u32 n_stats; - __u32 testinfo_len; -- __u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ -- __u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ -+ __u32 eedump_len; -+ __u32 regdump_len; - }; - - #define SOPASS_MAX 6 --/* wake-on-lan settings */ -+ -+/** -+ * struct ethtool_wolinfo - Wake-On-Lan configuration -+ * @cmd: Command number = %ETHTOOL_GWOL or %ETHTOOL_SWOL -+ * @supported: Bitmask of %WAKE_* flags for supported Wake-On-Lan modes. -+ * Read-only. -+ * @wolopts: Bitmask of %WAKE_* flags for enabled Wake-On-Lan modes. -+ * @sopass: SecureOn(tm) password; meaningful only if %WAKE_MAGICSECURE -+ * is set in @wolopts. -+ */ - struct ethtool_wolinfo { - __u32 cmd; - __u32 supported; - __u32 wolopts; -- __u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */ -+ __u8 sopass[SOPASS_MAX]; - }; - - /* for passing single values */ -@@ -118,20 +209,51 @@ struct ethtool_value { - __u32 data; - }; - --/* for passing big chunks of data */ -+/** -+ * struct ethtool_regs - hardware register dump -+ * @cmd: Command number = %ETHTOOL_GREGS -+ * @version: Dump format version. This is driver-specific and may -+ * distinguish different chips/revisions. Drivers must use new -+ * version numbers whenever the dump format changes in an -+ * incompatible way. -+ * @len: On entry, the real length of @data. On return, the number of -+ * bytes used. -+ * @data: Buffer for the register dump -+ * -+ * Users should use %ETHTOOL_GDRVINFO to find the maximum length of -+ * a register dump for the interface. They must allocate the buffer -+ * immediately following this structure. -+ */ - struct ethtool_regs { - __u32 cmd; -- __u32 version; /* driver-specific, indicates different chips/revs */ -- __u32 len; /* bytes */ -+ __u32 version; -+ __u32 len; - __u8 data[0]; - }; - --/* for passing EEPROM chunks */ -+/** -+ * struct ethtool_eeprom - EEPROM dump -+ * @cmd: Command number = %ETHTOOL_GEEPROM, %ETHTOOL_GMODULEEEPROM or -+ * %ETHTOOL_SEEPROM -+ * @magic: A 'magic cookie' value to guard against accidental changes. -+ * The value passed in to %ETHTOOL_SEEPROM must match the value -+ * returned by %ETHTOOL_GEEPROM for the same device. This is -+ * unused when @cmd is %ETHTOOL_GMODULEEEPROM. -+ * @offset: Offset within the EEPROM to begin reading/writing, in bytes -+ * @len: On entry, number of bytes to read/write. On successful -+ * return, number of bytes actually read/written. In case of -+ * error, this may indicate at what point the error occurred. -+ * @data: Buffer to read/write from -+ * -+ * Users may use %ETHTOOL_GDRVINFO or %ETHTOOL_GMODULEINFO to find -+ * the length of an on-board or module EEPROM, respectively. They -+ * must allocate the buffer immediately following this structure. -+ */ - struct ethtool_eeprom { - __u32 cmd; - __u32 magic; -- __u32 offset; /* in bytes */ -- __u32 len; /* in bytes */ -+ __u32 offset; -+ __u32 len; - __u8 data[0]; - }; - -@@ -229,17 +351,18 @@ struct ethtool_modinfo { - * @rate_sample_interval: How often to do adaptive coalescing packet rate - * sampling, measured in seconds. Must not be zero. - * -- * Each pair of (usecs, max_frames) fields specifies this exit -- * condition for interrupt coalescing: -+ * Each pair of (usecs, max_frames) fields specifies that interrupts -+ * should be coalesced until - * (usecs > 0 && time_since_first_completion >= usecs) || - * (max_frames > 0 && completed_frames >= max_frames) -+ * - * It is illegal to set both usecs and max_frames to zero as this - * would cause interrupts to never be generated. To disable - * coalescing, set usecs = 0 and max_frames = 1. - * - * Some implementations ignore the value of max_frames and use the -- * condition: -- * time_since_first_completion >= usecs -+ * condition time_since_first_completion >= usecs -+ * - * This is deprecated. Drivers for hardware that does not support - * counting completions should validate that max_frames == !rx_usecs. - * -@@ -279,22 +402,37 @@ struct ethtool_coalesce { - __u32 rate_sample_interval; - }; - --/* for configuring RX/TX ring parameters */ -+/** -+ * struct ethtool_ringparam - RX/TX ring parameters -+ * @cmd: Command number = %ETHTOOL_GRINGPARAM or %ETHTOOL_SRINGPARAM -+ * @rx_max_pending: Maximum supported number of pending entries per -+ * RX ring. Read-only. -+ * @rx_mini_max_pending: Maximum supported number of pending entries -+ * per RX mini ring. Read-only. -+ * @rx_jumbo_max_pending: Maximum supported number of pending entries -+ * per RX jumbo ring. Read-only. -+ * @tx_max_pending: Maximum supported number of pending entries per -+ * TX ring. Read-only. -+ * @rx_pending: Current maximum number of pending entries per RX ring -+ * @rx_mini_pending: Current maximum number of pending entries per RX -+ * mini ring -+ * @rx_jumbo_pending: Current maximum number of pending entries per RX -+ * jumbo ring -+ * @tx_pending: Current maximum supported number of pending entries -+ * per TX ring -+ * -+ * If the interface does not have separate RX mini and/or jumbo rings, -+ * @rx_mini_max_pending and/or @rx_jumbo_max_pending will be 0. -+ * -+ * There may also be driver-dependent minimum values for the number -+ * of entries per ring. -+ */ - struct ethtool_ringparam { -- __u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ -- -- /* Read only attributes. These indicate the maximum number -- * of pending RX/TX ring entries the driver will allow the -- * user to set. -- */ -+ __u32 cmd; - __u32 rx_max_pending; - __u32 rx_mini_max_pending; - __u32 rx_jumbo_max_pending; - __u32 tx_max_pending; -- -- /* Values changeable by the user. The valid values are -- * in the range 1 to the "*_max_pending" counterpart above. -- */ - __u32 rx_pending; - __u32 rx_mini_pending; - __u32 rx_jumbo_pending; -@@ -329,51 +467,96 @@ struct ethtool_channels { - __u32 combined_count; - }; - --/* for configuring link flow control parameters */ -+/** -+ * struct ethtool_pauseparam - Ethernet pause (flow control) parameters -+ * @cmd: Command number = %ETHTOOL_GPAUSEPARAM or %ETHTOOL_SPAUSEPARAM -+ * @autoneg: Flag to enable autonegotiation of pause frame use -+ * @rx_pause: Flag to enable reception of pause frames -+ * @tx_pause: Flag to enable transmission of pause frames -+ * -+ * Drivers should reject a non-zero setting of @autoneg when -+ * autoneogotiation is disabled (or not supported) for the link. -+ * -+ * If the link is autonegotiated, drivers should use -+ * mii_advertise_flowctrl() or similar code to set the advertised -+ * pause frame capabilities based on the @rx_pause and @tx_pause flags, -+ * even if @autoneg is zero. They should also allow the advertised -+ * pause frame capabilities to be controlled directly through the -+ * advertising field of &struct ethtool_cmd. -+ * -+ * If @autoneg is non-zero, the MAC is configured to send and/or -+ * receive pause frames according to the result of autonegotiation. -+ * Otherwise, it is configured directly based on the @rx_pause and -+ * @tx_pause flags. -+ */ - struct ethtool_pauseparam { -- __u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ -- -- /* If the link is being auto-negotiated (via ethtool_cmd.autoneg -- * being true) the user may set 'autoneg' here non-zero to have the -- * pause parameters be auto-negotiated too. In such a case, the -- * {rx,tx}_pause values below determine what capabilities are -- * advertised. -- * -- * If 'autoneg' is zero or the link is not being auto-negotiated, -- * then {rx,tx}_pause force the driver to use/not-use pause -- * flow control. -- */ -+ __u32 cmd; - __u32 autoneg; - __u32 rx_pause; - __u32 tx_pause; - }; - - #define ETH_GSTRING_LEN 32 -+ -+/** -+ * enum ethtool_stringset - string set ID -+ * @ETH_SS_TEST: Self-test result names, for use with %ETHTOOL_TEST -+ * @ETH_SS_STATS: Statistic names, for use with %ETHTOOL_GSTATS -+ * @ETH_SS_PRIV_FLAGS: Driver private flag names, for use with -+ * %ETHTOOL_GPFLAGS and %ETHTOOL_SPFLAGS -+ * @ETH_SS_NTUPLE_FILTERS: Previously used with %ETHTOOL_GRXNTUPLE; -+ * now deprecated -+ * @ETH_SS_FEATURES: Device feature names -+ */ - enum ethtool_stringset { - ETH_SS_TEST = 0, - ETH_SS_STATS, - ETH_SS_PRIV_FLAGS, -- ETH_SS_NTUPLE_FILTERS, /* Do not use, GRXNTUPLE is now deprecated */ -+ ETH_SS_NTUPLE_FILTERS, - ETH_SS_FEATURES, - }; - --/* for passing string sets for data tagging */ -+/** -+ * struct ethtool_gstrings - string set for data tagging -+ * @cmd: Command number = %ETHTOOL_GSTRINGS -+ * @string_set: String set ID; one of &enum ethtool_stringset -+ * @len: On return, the number of strings in the string set -+ * @data: Buffer for strings. Each string is null-padded to a size of -+ * %ETH_GSTRING_LEN. -+ * -+ * Users must use %ETHTOOL_GSSET_INFO to find the number of strings in -+ * the string set. They must allocate a buffer of the appropriate -+ * size immediately following this structure. -+ */ - struct ethtool_gstrings { -- __u32 cmd; /* ETHTOOL_GSTRINGS */ -- __u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ -- __u32 len; /* number of strings in the string set */ -+ __u32 cmd; -+ __u32 string_set; -+ __u32 len; - __u8 data[0]; - }; - -+/** -+ * struct ethtool_sset_info - string set information -+ * @cmd: Command number = %ETHTOOL_GSSET_INFO -+ * @sset_mask: On entry, a bitmask of string sets to query, with bits -+ * numbered according to &enum ethtool_stringset. On return, a -+ * bitmask of those string sets queried that are supported. -+ * @data: Buffer for string set sizes. On return, this contains the -+ * size of each string set that was queried and supported, in -+ * order of ID. -+ * -+ * Example: The user passes in @sset_mask = 0x7 (sets 0, 1, 2) and on -+ * return @sset_mask == 0x6 (sets 1, 2). Then @data[0] contains the -+ * size of set 1 and @data[1] contains the size of set 2. -+ * -+ * Users must allocate a buffer of the appropriate size (4 * number of -+ * sets queried) immediately following this structure. -+ */ - struct ethtool_sset_info { -- __u32 cmd; /* ETHTOOL_GSSET_INFO */ -+ __u32 cmd; - __u32 reserved; -- __u64 sset_mask; /* input: each bit selects an sset to query */ -- /* output: each bit a returned sset */ -- __u32 data[0]; /* ETH_SS_xxx count, in order, based on bits -- in sset_mask. One bit implies one -- __u32, two bits implies two -- __u32's, etc. */ -+ __u64 sset_mask; -+ __u32 data[0]; - }; - - /** -@@ -393,24 +576,58 @@ enum ethtool_test_flags { - ETH_TEST_FL_EXTERNAL_LB_DONE = (1 << 3), - }; - --/* for requesting NIC test and getting results*/ -+/** -+ * struct ethtool_test - device self-test invocation -+ * @cmd: Command number = %ETHTOOL_TEST -+ * @flags: A bitmask of flags from &enum ethtool_test_flags. Some -+ * flags may be set by the user on entry; others may be set by -+ * the driver on return. -+ * @len: On return, the number of test results -+ * @data: Array of test results -+ * -+ * Users must use %ETHTOOL_GSSET_INFO or %ETHTOOL_GDRVINFO to find the -+ * number of test results that will be returned. They must allocate a -+ * buffer of the appropriate size (8 * number of results) immediately -+ * following this structure. -+ */ - struct ethtool_test { -- __u32 cmd; /* ETHTOOL_TEST */ -- __u32 flags; /* ETH_TEST_FL_xxx */ -+ __u32 cmd; -+ __u32 flags; - __u32 reserved; -- __u32 len; /* result length, in number of u64 elements */ -+ __u32 len; - __u64 data[0]; - }; - --/* for dumping NIC-specific statistics */ -+/** -+ * struct ethtool_stats - device-specific statistics -+ * @cmd: Command number = %ETHTOOL_GSTATS -+ * @n_stats: On return, the number of statistics -+ * @data: Array of statistics -+ * -+ * Users must use %ETHTOOL_GSSET_INFO or %ETHTOOL_GDRVINFO to find the -+ * number of statistics that will be returned. They must allocate a -+ * buffer of the appropriate size (8 * number of statistics) -+ * immediately following this structure. -+ */ - struct ethtool_stats { -- __u32 cmd; /* ETHTOOL_GSTATS */ -- __u32 n_stats; /* number of u64's being returned */ -+ __u32 cmd; -+ __u32 n_stats; - __u64 data[0]; - }; - -+/** -+ * struct ethtool_perm_addr - permanent hardware address -+ * @cmd: Command number = %ETHTOOL_GPERMADDR -+ * @size: On entry, the size of the buffer. On return, the size of the -+ * address. The command fails if the buffer is too small. -+ * @data: Buffer for the address -+ * -+ * Users must allocate the buffer immediately following this structure. -+ * A buffer size of %MAX_ADDR_LEN should be sufficient for any address -+ * type. -+ */ - struct ethtool_perm_addr { -- __u32 cmd; /* ETHTOOL_GPERMADDR */ -+ __u32 cmd; - __u32 size; - __u8 data[0]; - }; -@@ -593,7 +810,7 @@ struct ethtool_rx_flow_spec { - * %ETHTOOL_SRXCLSRLINS may add the rule at any suitable unused - * location, and may remove a rule at a later location (lower - * priority) that matches exactly the same set of flows. The special -- * values are: %RX_CLS_LOC_ANY, selecting any location; -+ * values are %RX_CLS_LOC_ANY, selecting any location; - * %RX_CLS_LOC_FIRST, selecting the first suitable location (maximum - * priority); and %RX_CLS_LOC_LAST, selecting the last suitable - * location (minimum priority). Additional special values may be -@@ -630,6 +847,38 @@ struct ethtool_rxfh_indir { - }; - - /** -+ * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key. -+ * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH -+ * @rss_context: RSS context identifier. -+ * @indir_size: On entry, the array size of the user buffer for the -+ * indirection table, which may be zero, or (for %ETHTOOL_SRSSH), -+ * %ETH_RXFH_INDIR_NO_CHANGE. On return from %ETHTOOL_GRSSH, -+ * the array size of the hardware indirection table. -+ * @key_size: On entry, the array size of the user buffer for the hash key, -+ * which may be zero. On return from %ETHTOOL_GRSSH, the size of the -+ * hardware hash key. -+ * @rsvd: Reserved for future extensions. -+ * @rss_config: RX ring/queue index for each hash value i.e., indirection table -+ * of @indir_size __u32 elements, followed by hash key of @key_size -+ * bytes. -+ * -+ * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the -+ * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of -+ * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested -+ * and a @indir_size of zero means the indir table should be reset to default -+ * values. -+ */ -+struct ethtool_rxfh { -+ __u32 cmd; -+ __u32 rss_context; -+ __u32 indir_size; -+ __u32 key_size; -+ __u32 rsvd[2]; -+ __u32 rss_config[0]; -+}; -+#define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff -+ -+/** - * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter - * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW - * @h_u: Flow field values to match (dependent on @flow_type) -@@ -704,9 +953,6 @@ struct ethtool_flash { - * for %ETHTOOL_GET_DUMP_FLAG command - * @data: data collected for get dump data operation - */ -- --#define ETH_FW_DUMP_DISABLE 0 -- - struct ethtool_dump { - __u32 cmd; - __u32 version; -@@ -715,6 +961,8 @@ struct ethtool_dump { - __u8 data[0]; - }; - -+#define ETH_FW_DUMP_DISABLE 0 -+ - /* for returning and changing feature sets */ - - /** -@@ -734,8 +982,9 @@ struct ethtool_get_features_block { - /** - * struct ethtool_gfeatures - command to get state of device's features - * @cmd: command number = %ETHTOOL_GFEATURES -- * @size: in: number of elements in the features[] array; -- * out: number of elements in features[] needed to hold all features -+ * @size: On entry, the number of elements in the features[] array; -+ * on return, the number of elements in features[] needed to hold -+ * all features - * @features: state of features - */ - struct ethtool_gfeatures { -@@ -901,11 +1150,13 @@ enum ethtool_sfeatures_retval_bits { - #define ETHTOOL_GEEE 0x00000044 /* Get EEE settings */ - #define ETHTOOL_SEEE 0x00000045 /* Set EEE settings */ - -+#define ETHTOOL_GRSSH 0x00000046 /* Get RX flow hash configuration */ -+#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */ -+ - /* compatibility with older code */ - #define SPARC_ETH_GSET ETHTOOL_GSET - #define SPARC_ETH_SSET ETHTOOL_SSET - --/* Indicates what features are supported by the interface. */ - #define SUPPORTED_10baseT_Half (1 << 0) - #define SUPPORTED_10baseT_Full (1 << 1) - #define SUPPORTED_100baseT_Half (1 << 2) -@@ -934,7 +1185,6 @@ enum ethtool_sfeatures_retval_bits { - #define SUPPORTED_40000baseSR4_Full (1 << 25) - #define SUPPORTED_40000baseLR4_Full (1 << 26) - --/* Indicates what features are advertised by the interface. */ - #define ADVERTISED_10baseT_Half (1 << 0) - #define ADVERTISED_10baseT_Full (1 << 1) - #define ADVERTISED_100baseT_Half (1 << 2) -@@ -993,15 +1243,13 @@ enum ethtool_sfeatures_retval_bits { - #define PORT_OTHER 0xff - - /* Which transceiver to use. */ --#define XCVR_INTERNAL 0x00 --#define XCVR_EXTERNAL 0x01 -+#define XCVR_INTERNAL 0x00 /* PHY and MAC are in the same package */ -+#define XCVR_EXTERNAL 0x01 /* PHY and MAC are in different packages */ - #define XCVR_DUMMY1 0x02 - #define XCVR_DUMMY2 0x03 - #define XCVR_DUMMY3 0x04 - --/* Enable or disable autonegotiation. If this is set to enable, -- * the forced link modes above are completely ignored. -- */ -+/* Enable or disable autonegotiation. */ - #define AUTONEG_DISABLE 0x00 - #define AUTONEG_ENABLE 0x01 - --- -1.8.3.1 - diff --git a/SOURCES/0001-ethtool.c-fix-memory-leaks.patch b/SOURCES/0001-ethtool.c-fix-memory-leaks.patch new file mode 100644 index 0000000..b592b31 --- /dev/null +++ b/SOURCES/0001-ethtool.c-fix-memory-leaks.patch @@ -0,0 +1,223 @@ +From 266d945d9c6432fc5746fa46e3e3ace2518600db Mon Sep 17 00:00:00 2001 +From: Ivan Vecera +Date: Fri, 18 Mar 2016 12:50:43 +0100 +Subject: [PATCH] ethtool.c: fix memory leaks + +Memory allocated at several places is not appropriately freed. + +Signed-off-by: Ivan Vecera +--- + ethtool.c | 60 +++++++++++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 45 insertions(+), 15 deletions(-) + +diff --git a/ethtool.c b/ethtool.c +index 0cd0d4f..ca0bf28 100644 +--- a/ethtool.c ++++ b/ethtool.c +@@ -2065,10 +2065,14 @@ static int do_gfeatures(struct cmd_context *ctx) + features = get_features(ctx, defs); + if (!features) { + fprintf(stdout, "no feature info available\n"); ++ free(defs); + return 1; + } + + dump_features(defs, features, NULL); ++ ++ free(features); ++ free(defs); + return 0; + } + +@@ -2078,11 +2082,11 @@ static int do_sfeatures(struct cmd_context *ctx) + int any_changed = 0, any_mismatch = 0; + u32 off_flags_wanted = 0; + u32 off_flags_mask = 0; +- struct ethtool_sfeatures *efeatures; ++ struct ethtool_sfeatures *efeatures = NULL; + struct cmdline_info *cmdline_features; +- struct feature_state *old_state, *new_state; ++ struct feature_state *old_state = NULL, *new_state = NULL; + struct ethtool_value eval; +- int err; ++ int err, retval = 1; + int i, j; + + defs = get_feature_defs(ctx); +@@ -2096,7 +2100,7 @@ static int do_sfeatures(struct cmd_context *ctx) + sizeof(efeatures->features[0])); + if (!efeatures) { + perror("Cannot parse arguments"); +- return 1; ++ goto finish; + } + efeatures->cmd = ETHTOOL_SFEATURES; + efeatures->size = FEATURE_BITS_TO_BLOCKS(defs->n_features); +@@ -2114,7 +2118,7 @@ static int do_sfeatures(struct cmd_context *ctx) + sizeof(cmdline_features[0])); + if (!cmdline_features) { + perror("Cannot parse arguments"); +- return 1; ++ goto finish; + } + for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) + flag_to_cmdline_info(off_flag_def[i].short_name, +@@ -2133,12 +2137,13 @@ static int do_sfeatures(struct cmd_context *ctx) + + if (!any_changed) { + fprintf(stdout, "no features changed\n"); +- return 0; ++ retval = 0; ++ goto finish; + } + + old_state = get_features(ctx, defs); + if (!old_state) +- return 1; ++ goto finish; + + if (efeatures) { + /* For each offload that the user specified, update any +@@ -2182,7 +2187,7 @@ static int do_sfeatures(struct cmd_context *ctx) + err = send_ioctl(ctx, efeatures); + if (err < 0) { + perror("Cannot set device feature settings"); +- return 1; ++ goto finish; + } + } else { + for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) { +@@ -2197,7 +2202,7 @@ static int do_sfeatures(struct cmd_context *ctx) + fprintf(stderr, + "Cannot set device %s settings: %m\n", + off_flag_def[i].long_name); +- return 1; ++ goto finish; + } + } + } +@@ -2211,7 +2216,8 @@ static int do_sfeatures(struct cmd_context *ctx) + err = send_ioctl(ctx, &eval); + if (err) { + perror("Cannot set device flag settings"); +- return 92; ++ retval = 92; ++ goto finish; + } + } + } +@@ -2219,7 +2225,7 @@ static int do_sfeatures(struct cmd_context *ctx) + /* Compare new state with requested state */ + new_state = get_features(ctx, defs); + if (!new_state) +- return 1; ++ goto finish; + any_changed = new_state->off_flags != old_state->off_flags; + any_mismatch = (new_state->off_flags != + ((old_state->off_flags & ~off_flags_mask) | +@@ -2238,13 +2244,19 @@ static int do_sfeatures(struct cmd_context *ctx) + if (!any_changed) { + fprintf(stderr, + "Could not change any device features\n"); +- return 1; ++ goto finish; + } + printf("Actual changes:\n"); + dump_features(defs, new_state, old_state); + } + +- return 0; ++ retval = 0; ++finish: ++ free(new_state); ++ free(old_state); ++ free(efeatures); ++ free(defs); ++ return retval; + } + + static int do_gset(struct cmd_context *ctx) +@@ -2705,8 +2717,18 @@ static int do_gregs(struct cmd_context *ctx) + return 75; + } + +- regs = realloc(regs, sizeof(*regs) + st.st_size); +- regs->len = st.st_size; ++ if (regs->len != st.st_size) { ++ struct ethtool_regs *new_regs; ++ new_regs = realloc(regs, sizeof(*regs) + st.st_size); ++ if (!new_regs) { ++ perror("Cannot allocate memory for register " ++ "dump"); ++ free(regs); ++ return 73; ++ } ++ regs = new_regs; ++ regs->len = st.st_size; ++ } + nread = fread(regs->data, regs->len, 1, f); + fclose(f); + if (nread != 1) { +@@ -3775,6 +3797,7 @@ static int do_gprivflags(struct cmd_context *ctx) + } + if (strings->len == 0) { + fprintf(stderr, "No private flags defined\n"); ++ free(strings); + return 1; + } + if (strings->len > 32) { +@@ -3786,6 +3809,7 @@ static int do_gprivflags(struct cmd_context *ctx) + flags.cmd = ETHTOOL_GPFLAGS; + if (send_ioctl(ctx, &flags)) { + perror("Cannot get private flags"); ++ free(strings); + return 1; + } + +@@ -3804,6 +3828,7 @@ static int do_gprivflags(struct cmd_context *ctx) + (const char *)strings->data + i * ETH_GSTRING_LEN, + (flags.data & (1U << i)) ? "on" : "off"); + ++ free(strings); + return 0; + } + +@@ -3825,6 +3850,7 @@ static int do_sprivflags(struct cmd_context *ctx) + } + if (strings->len == 0) { + fprintf(stderr, "No private flags defined\n"); ++ free(strings); + return 1; + } + if (strings->len > 32) { +@@ -3836,6 +3862,7 @@ static int do_sprivflags(struct cmd_context *ctx) + cmdline = calloc(strings->len, sizeof(*cmdline)); + if (!cmdline) { + perror("Cannot parse arguments"); ++ free(strings); + return 1; + } + for (i = 0; i < strings->len; i++) { +@@ -3852,6 +3879,7 @@ static int do_sprivflags(struct cmd_context *ctx) + flags.cmd = ETHTOOL_GPFLAGS; + if (send_ioctl(ctx, &flags)) { + perror("Cannot get private flags"); ++ free(strings); + return 1; + } + +@@ -3859,9 +3887,11 @@ static int do_sprivflags(struct cmd_context *ctx) + flags.data = (flags.data & ~seen_flags) | wanted_flags; + if (send_ioctl(ctx, &flags)) { + perror("Cannot set private flags"); ++ free(strings); + return 1; + } + ++ free(strings); + return 0; + } + +-- +2.7.3 + diff --git a/SOURCES/0002-Support-for-configurable-RSS-hash-key.patch b/SOURCES/0002-Support-for-configurable-RSS-hash-key.patch deleted file mode 100644 index c69a995..0000000 --- a/SOURCES/0002-Support-for-configurable-RSS-hash-key.patch +++ /dev/null @@ -1,575 +0,0 @@ -From 86c0326c06b2de611f438a453fae51512747e831 Mon Sep 17 00:00:00 2001 -From: Venkat Duvvuru -Date: Tue, 22 Jul 2014 17:51:07 +0530 -Subject: [PATCH 2/4] ethtool: Support for configurable RSS hash key - -This ethtool patch will primarily implement the parser for the options provided -by the user for get and set rxfh before invoking the ioctl. -This patch also has -1. Ethtool man page changes which describes the Usage of - get and set rxfh options. -2. Test cases for get and set rxfh in test-cmdline.c - -Signed-off-by: Venkat Duvvuru ---- - ethtool.8.in | 18 ++- - ethtool.c | 393 ++++++++++++++++++++++++++++++++++++++++++++++++--------- - test-cmdline.c | 11 ++ - 3 files changed, 354 insertions(+), 68 deletions(-) - -diff --git a/ethtool.8.in b/ethtool.8.in -index bb394cc..c1e6e09 100644 ---- a/ethtool.8.in -+++ b/ethtool.8.in -@@ -286,11 +286,12 @@ ethtool \- query or control network driver and hardware settings - .B ethtool \-T|\-\-show\-time\-stamping - .I devname - .HP --.B ethtool \-x|\-\-show\-rxfh\-indir -+.B ethtool \-x|\-\-show\-rxfh\-indir|\-\-show\-rxfh - .I devname - .HP --.B ethtool \-X|\-\-set\-rxfh\-indir -+.B ethtool \-X|\-\-set\-rxfh\-indir|\-\-rxfh - .I devname -+.RB [ hkey \ \*(MA:\...] - .RB [\ equal - .IR N \ | - .BI weight\ W0 -@@ -784,11 +785,16 @@ Sets the dump flag for the device. - Show the device's time stamping capabilities and associated PTP - hardware clock. - .TP --.B \-x \-\-show\-rxfh\-indir --Retrieves the receive flow hash indirection table. -+.B \-x \-\-show\-rxfh\-indir \-\-show\-rxfh -+Retrieves the receive flow hash indirection table and/or RSS hash key. - .TP --.B \-X \-\-set\-rxfh\-indir --Configures the receive flow hash indirection table. -+.B \-X \-\-set\-rxfh\-indir \-\-rxfh -+Configures the receive flow hash indirection table and/or RSS hash key. -+.TP -+.BI hkey -+Sets RSS hash key of the specified network device. RSS hash key should be of device supported length. -+Hash key format must be in xx:yy:zz:aa:bb:cc format meaning both the nibbles of a byte should be mentioned -+even if a nibble is zero. - .TP - .BI equal\ N - Sets the receive flow hash indirection table to spread flows evenly -diff --git a/ethtool.c b/ethtool.c -index 19b8b0c..bf583f3 100644 ---- a/ethtool.c -+++ b/ethtool.c -@@ -878,6 +878,74 @@ static char *unparse_rxfhashopts(u64 opts) - return buf; - } - -+static int convert_string_to_hashkey(char *rss_hkey, u32 key_size, -+ const char *rss_hkey_string) -+{ -+ u32 i = 0; -+ int hex_byte, len; -+ -+ do { -+ if (i > (key_size - 1)) { -+ fprintf(stderr, -+ "Key is too long for device (%u > %u)\n", -+ i + 1, key_size); -+ goto err; -+ } -+ -+ if (sscanf(rss_hkey_string, "%2x%n", &hex_byte, &len) < 1 || -+ len != 2) { -+ fprintf(stderr, "Invalid RSS hash key format\n"); -+ goto err; -+ } -+ -+ rss_hkey[i++] = hex_byte; -+ rss_hkey_string += 2; -+ -+ if (*rss_hkey_string == ':') { -+ rss_hkey_string++; -+ } else if (*rss_hkey_string != '\0') { -+ fprintf(stderr, "Invalid RSS hash key format\n"); -+ goto err; -+ } -+ -+ } while (*rss_hkey_string); -+ -+ if (i != key_size) { -+ fprintf(stderr, "Key is too short for device (%u < %u)\n", -+ i, key_size); -+ goto err; -+ } -+ -+ return 0; -+err: -+ return 2; -+} -+ -+static int parse_hkey(char **rss_hkey, u32 key_size, -+ const char *rss_hkey_string) -+{ -+ if (!key_size) { -+ fprintf(stderr, -+ "Cannot set RX flow hash configuration:\n" -+ " Hash key setting not supported\n"); -+ return 1; -+ } -+ -+ *rss_hkey = malloc(key_size); -+ if (!(*rss_hkey)) { -+ perror("Cannot allocate memory for RSS hash key"); -+ return 1; -+ } -+ -+ if (convert_string_to_hashkey(*rss_hkey, key_size, -+ rss_hkey_string)) { -+ free(*rss_hkey); -+ *rss_hkey = NULL; -+ return 2; -+ } -+ return 0; -+} -+ - static const struct { - const char *name; - int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs); -@@ -3042,92 +3110,141 @@ static int do_grxclass(struct cmd_context *ctx) - return err ? 1 : 0; - } - --static int do_grxfhindir(struct cmd_context *ctx) -+static void print_indir_table(struct cmd_context *ctx, -+ struct ethtool_rxnfc *ring_count, -+ u32 indir_size, u32 *indir) - { -- struct ethtool_rxnfc ring_count; -- struct ethtool_rxfh_indir indir_head; -- struct ethtool_rxfh_indir *indir; - u32 i; -- int err; - -- ring_count.cmd = ETHTOOL_GRXRINGS; -- err = send_ioctl(ctx, &ring_count); -- if (err < 0) { -- perror("Cannot get RX ring count"); -- return 102; -+ printf("RX flow hash indirection table for %s with %llu RX ring(s):\n", -+ ctx->devname, ring_count->data); -+ -+ if (!indir_size) -+ printf("Operation not supported\n"); -+ -+ for (i = 0; i < indir_size; i++) { -+ if (i % 8 == 0) -+ printf("%5u: ", i); -+ printf(" %5u", indir[i]); -+ if (i % 8 == 7) -+ fputc('\n', stdout); - } -+} -+ -+static int do_grxfhindir(struct cmd_context *ctx, -+ struct ethtool_rxnfc *ring_count) -+{ -+ struct ethtool_rxfh_indir indir_head; -+ struct ethtool_rxfh_indir *indir; -+ int err; - - indir_head.cmd = ETHTOOL_GRXFHINDIR; - indir_head.size = 0; - err = send_ioctl(ctx, &indir_head); - if (err < 0) { - perror("Cannot get RX flow hash indirection table size"); -- return 103; -+ return 1; - } - - indir = malloc(sizeof(*indir) + - indir_head.size * sizeof(*indir->ring_index)); -+ if (!indir) { -+ perror("Cannot allocate memory for indirection table"); -+ return 1; -+ } -+ - indir->cmd = ETHTOOL_GRXFHINDIR; - indir->size = indir_head.size; - err = send_ioctl(ctx, indir); - if (err < 0) { - perror("Cannot get RX flow hash indirection table"); -- return 103; -+ free(indir); -+ return 1; - } - -- printf("RX flow hash indirection table for %s with %llu RX ring(s):\n", -- ctx->devname, ring_count.data); -- for (i = 0; i < indir->size; i++) { -- if (i % 8 == 0) -- printf("%5u: ", i); -- printf(" %5u", indir->ring_index[i]); -- if (i % 8 == 7) -- fputc('\n', stdout); -- } -+ print_indir_table(ctx, ring_count, indir->size, indir->ring_index); -+ -+ free(indir); - return 0; - } - --static int do_srxfhindir(struct cmd_context *ctx) -+static int do_grxfh(struct cmd_context *ctx) - { -- int rxfhindir_equal = 0; -- char **rxfhindir_weight = NULL; -- struct ethtool_rxfh_indir indir_head; -- struct ethtool_rxfh_indir *indir; -- u32 i; -+ struct ethtool_rxfh rss_head = {0}; -+ struct ethtool_rxnfc ring_count; -+ struct ethtool_rxfh *rss; -+ u32 i, indir_bytes; -+ char *hkey; - int err; - -- if (ctx->argc < 2) -- exit_bad_args(); -- if (!strcmp(ctx->argp[0], "equal")) { -- if (ctx->argc != 2) -- exit_bad_args(); -- rxfhindir_equal = get_int_range(ctx->argp[1], 0, 1, INT_MAX); -- } else if (!strcmp(ctx->argp[0], "weight")) { -- rxfhindir_weight = ctx->argp + 1; -- } else { -- exit_bad_args(); -+ ring_count.cmd = ETHTOOL_GRXRINGS; -+ err = send_ioctl(ctx, &ring_count); -+ if (err < 0) { -+ perror("Cannot get RX ring count"); -+ return 1; - } - -- indir_head.cmd = ETHTOOL_GRXFHINDIR; -- indir_head.size = 0; -- err = send_ioctl(ctx, &indir_head); -+ rss_head.cmd = ETHTOOL_GRSSH; -+ err = send_ioctl(ctx, &rss_head); -+ if (err < 0 && errno == EOPNOTSUPP) { -+ return do_grxfhindir(ctx, &ring_count); -+ } else if (err < 0) { -+ perror("Cannot get RX flow hash indir size and/or key size"); -+ return 1; -+ } -+ -+ rss = calloc(1, sizeof(*rss) + -+ rss_head.indir_size * sizeof(rss_head.rss_config[0]) + -+ rss_head.key_size); -+ if (!rss) { -+ perror("Cannot allocate memory for RX flow hash config"); -+ return 1; -+ } -+ -+ rss->cmd = ETHTOOL_GRSSH; -+ rss->indir_size = rss_head.indir_size; -+ rss->key_size = rss_head.key_size; -+ err = send_ioctl(ctx, rss); - if (err < 0) { -- perror("Cannot get RX flow hash indirection table size"); -- return 104; -+ perror("Cannot get RX flow hash configuration"); -+ free(rss); -+ return 1; - } - -- indir = malloc(sizeof(*indir) + -- indir_head.size * sizeof(*indir->ring_index)); -- indir->cmd = ETHTOOL_SRXFHINDIR; -- indir->size = indir_head.size; -+ print_indir_table(ctx, &ring_count, rss->indir_size, rss->rss_config); -+ -+ indir_bytes = rss->indir_size * sizeof(rss->rss_config[0]); -+ hkey = ((char *)rss->rss_config + indir_bytes); - -+ printf("RSS hash key:\n"); -+ if (!rss->key_size) -+ printf("Operation not supported\n"); -+ -+ for (i = 0; i < rss->key_size; i++) { -+ if (i == (rss->key_size - 1)) -+ printf("%02x\n", (u8) hkey[i]); -+ else -+ printf("%02x:", (u8) hkey[i]); -+ } -+ -+ free(rss); -+ return 0; -+} -+ -+static int fill_indir_table(u32 *indir_size, u32 *indir, int rxfhindir_equal, -+ char **rxfhindir_weight, u32 num_weights) -+{ -+ u32 i; -+ /* -+ * "*indir_size == 0" ==> reset indir to default -+ */ - if (rxfhindir_equal) { -- for (i = 0; i < indir->size; i++) -- indir->ring_index[i] = i % rxfhindir_equal; -- } else { -+ for (i = 0; i < *indir_size; i++) -+ indir[i] = i % rxfhindir_equal; -+ } else if (rxfhindir_weight) { - u32 j, weight, sum = 0, partial = 0; - -- for (j = 0; rxfhindir_weight[j]; j++) { -+ for (j = 0; j < num_weights; j++) { - weight = get_u32(rxfhindir_weight[j], 0); - sum += weight; - } -@@ -3135,36 +3252,187 @@ static int do_srxfhindir(struct cmd_context *ctx) - if (sum == 0) { - fprintf(stderr, - "At least one weight must be non-zero\n"); -- exit(1); -+ return 2; - } - -- if (sum > indir->size) { -+ if (sum > *indir_size) { - fprintf(stderr, - "Total weight exceeds the size of the " - "indirection table\n"); -- exit(1); -+ return 2; - } - - j = -1; -- for (i = 0; i < indir->size; i++) { -- while (i >= indir->size * partial / sum) { -+ for (i = 0; i < *indir_size; i++) { -+ while (i >= (*indir_size) * partial / sum) { - j += 1; - weight = get_u32(rxfhindir_weight[j], 0); - partial += weight; - } -- indir->ring_index[i] = j; -+ indir[i] = j; - } -+ } else { -+ *indir_size = ETH_RXFH_INDIR_NO_CHANGE; -+ } -+ -+ return 0; -+} -+ -+static int do_srxfhindir(struct cmd_context *ctx, int rxfhindir_equal, -+ char **rxfhindir_weight, u32 num_weights) -+{ -+ struct ethtool_rxfh_indir indir_head; -+ struct ethtool_rxfh_indir *indir; -+ int err; -+ -+ indir_head.cmd = ETHTOOL_GRXFHINDIR; -+ indir_head.size = 0; -+ err = send_ioctl(ctx, &indir_head); -+ if (err < 0) { -+ perror("Cannot get RX flow hash indirection table size"); -+ return 1; -+ } -+ -+ indir = malloc(sizeof(*indir) + -+ indir_head.size * sizeof(*indir->ring_index)); -+ -+ if (!indir) { -+ perror("Cannot allocate memory for indirection table"); -+ return 1; -+ } -+ -+ indir->cmd = ETHTOOL_SRXFHINDIR; -+ indir->size = indir_head.size; -+ -+ if (fill_indir_table(&indir->size, indir->ring_index, rxfhindir_equal, -+ rxfhindir_weight, num_weights)) { -+ free(indir); -+ return 1; - } - - err = send_ioctl(ctx, indir); - if (err < 0) { - perror("Cannot set RX flow hash indirection table"); -- return 105; -+ free(indir); -+ return 1; - } - -+ free(indir); - return 0; - } - -+static int do_srxfh(struct cmd_context *ctx) -+{ -+ struct ethtool_rxfh rss_head = {0}; -+ struct ethtool_rxfh *rss; -+ struct ethtool_rxnfc ring_count; -+ int rxfhindir_equal = 0; -+ char **rxfhindir_weight = NULL; -+ char *rxfhindir_key = NULL; -+ char *hkey = NULL; -+ int err = 0; -+ u32 arg_num = 0, indir_bytes = 0; -+ u32 entry_size = sizeof(rss_head.rss_config[0]); -+ u32 num_weights = 0; -+ -+ if (ctx->argc < 2) -+ exit_bad_args(); -+ -+ while (arg_num < ctx->argc) { -+ if (!strcmp(ctx->argp[arg_num], "equal")) { -+ ++arg_num; -+ rxfhindir_equal = get_int_range(ctx->argp[arg_num], -+ 0, 1, INT_MAX); -+ ++arg_num; -+ } else if (!strcmp(ctx->argp[arg_num], "weight")) { -+ ++arg_num; -+ rxfhindir_weight = ctx->argp + arg_num; -+ while (arg_num < ctx->argc && -+ isdigit((unsigned char)ctx->argp[arg_num][0])) { -+ ++arg_num; -+ ++num_weights; -+ } -+ if (!num_weights) -+ exit_bad_args(); -+ } else if (!strcmp(ctx->argp[arg_num], "hkey")) { -+ ++arg_num; -+ rxfhindir_key = ctx->argp[arg_num]; -+ if (!rxfhindir_key) -+ exit_bad_args(); -+ ++arg_num; -+ } else { -+ exit_bad_args(); -+ } -+ } -+ -+ if (rxfhindir_equal && rxfhindir_weight) { -+ fprintf(stderr, -+ "Equal and weight options are mutually exclusive\n"); -+ return 1; -+ } -+ -+ ring_count.cmd = ETHTOOL_GRXRINGS; -+ err = send_ioctl(ctx, &ring_count); -+ if (err < 0) { -+ perror("Cannot get RX ring count"); -+ return 1; -+ } -+ -+ rss_head.cmd = ETHTOOL_GRSSH; -+ err = send_ioctl(ctx, &rss_head); -+ if (err < 0 && errno == EOPNOTSUPP && !rxfhindir_key) { -+ return do_srxfhindir(ctx, rxfhindir_equal, rxfhindir_weight, -+ num_weights); -+ } else if (err < 0) { -+ perror("Cannot get RX flow hash indir size and key size"); -+ return 1; -+ } -+ -+ if (rxfhindir_key) { -+ err = parse_hkey(&hkey, rss_head.key_size, -+ rxfhindir_key); -+ if (err) -+ return err; -+ } -+ -+ if (rxfhindir_equal || rxfhindir_weight) -+ indir_bytes = rss_head.indir_size * entry_size; -+ -+ rss = calloc(1, sizeof(*rss) + indir_bytes + rss_head.key_size); -+ if (!rss) { -+ perror("Cannot allocate memory for RX flow hash config"); -+ return 1; -+ } -+ rss->cmd = ETHTOOL_SRSSH; -+ rss->indir_size = rss_head.indir_size; -+ rss->key_size = rss_head.key_size; -+ -+ if (fill_indir_table(&rss->indir_size, rss->rss_config, rxfhindir_equal, -+ rxfhindir_weight, num_weights)) { -+ err = 1; -+ goto free; -+ } -+ -+ if (hkey) -+ memcpy((char *)rss->rss_config + indir_bytes, -+ hkey, rss->key_size); -+ else -+ rss->key_size = 0; -+ -+ err = send_ioctl(ctx, rss); -+ if (err < 0) { -+ perror("Cannot set RX flow hash configuration"); -+ err = 1; -+ } -+ -+free: -+ if (hkey) -+ free(hkey); -+ -+ free(rss); -+ return err; -+} -+ - static int do_flash(struct cmd_context *ctx) - { - char *flash_file; -@@ -3842,11 +4110,12 @@ static const struct option { - " delete %d\n" }, - { "-T|--show-time-stamping", 1, do_tsinfo, - "Show time stamping capabilities" }, -- { "-x|--show-rxfh-indir", 1, do_grxfhindir, -- "Show Rx flow hash indirection" }, -- { "-X|--set-rxfh-indir", 1, do_srxfhindir, -- "Set Rx flow hash indirection", -- " equal N | weight W0 W1 ...\n" }, -+ { "-x|--show-rxfh-indir|--show-rxfh", 1, do_grxfh, -+ "Show Rx flow hash indirection and/or hash key" }, -+ { "-X|--set-rxfh-indir|--rxfh", 1, do_srxfh, -+ "Set Rx flow hash indirection and/or hash key", -+ " [ equal N | weight W0 W1 ... ]\n" -+ " [ hkey %x:%x:%x:%x:%x:.... ]\n" }, - { "-f|--flash", 1, do_flash, - "Flash firmware image from the specified file to a region on the device", - " FILENAME [ REGION-NUMBER-TO-FLASH ]\n" }, -diff --git a/test-cmdline.c b/test-cmdline.c -index f1d4555..be41a30 100644 ---- a/test-cmdline.c -+++ b/test-cmdline.c -@@ -173,6 +173,7 @@ static struct test_case { - { 1, "-T" }, - { 0, "-x devname" }, - { 0, "--show-rxfh-indir devname" }, -+ { 0, "--show-rxfh devname" }, - { 1, "-x" }, - /* Argument parsing for -X is specialised */ - { 0, "-X devname equal 2" }, -@@ -181,6 +182,16 @@ static struct test_case { - { 1, "--set-rxfh-indir devname equal foo" }, - { 1, "-X devname equal" }, - { 0, "--set-rxfh-indir devname weight 1 2 3 4" }, -+ { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" }, -+ { 0, "-X devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" }, -+ { 1, "--rxfh devname hkey foo" }, -+ { 1, "-X devname hkey foo" }, -+ { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee weight 1 2 3 4" }, -+ { 0, "-X devname weight 1 2 3 4 hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" }, -+ { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee equal 2" }, -+ { 0, "-X devname equal 2 hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" }, -+ { 1, "--rxfh devname weight 1 2 3 4 equal 8" }, -+ { 1, "-X devname weight 1 2 3 4 equal 8" }, - { 1, "-X devname foo" }, - { 1, "-X" }, - { 0, "-P devname" }, --- -1.8.3.1 - diff --git a/SOURCES/0002-ethtool.c-add-support-for-ETHTOOL_xLINKSETTINGS-ioctl.patch b/SOURCES/0002-ethtool.c-add-support-for-ETHTOOL_xLINKSETTINGS-ioctl.patch new file mode 100644 index 0000000..10392c4 --- /dev/null +++ b/SOURCES/0002-ethtool.c-add-support-for-ETHTOOL_xLINKSETTINGS-ioctl.patch @@ -0,0 +1,1072 @@ +From 00a31b21a767be682a27d631a7c9df2a41c41e06 Mon Sep 17 00:00:00 2001 +From: David Decotigny +Date: Fri, 25 Mar 2016 09:21:01 -0700 +Subject: [PATCH 2/7] ethtool.c: add support for ETHTOOL_xLINKSETTINGS ioctls + +More info with kernel commit 8d3f2806f8fb ("Merge branch +'ethtool-ksettings'"). + +Note: The new features implemented in this patch depend on kernel +commit 793cf87de9d1 ("Set cmd field in ETHTOOL_GLINKSETTINGS response to +wrong nwords"). + +Signed-off-by: David Decotigny +[bwh: Fix style: + - Remove '_' and '__' prefixes from newly defined names + - Add space around multiplication operator + - Remove space before semi-colon] +Signed-off-by: Ben Hutchings + +(cherry picked from commit 33133abf3b777a2e0730f826817c8e05b3616150) +--- + ethtool.c | 680 +++++++++++++++++++++++++++++++++++++++++++-------------- + internal.h | 66 ++++++ + test-cmdline.c | 13 ++ + 3 files changed, 601 insertions(+), 158 deletions(-) + +diff --git a/ethtool.c b/ethtool.c +index ca0bf28..796d0b5 100644 +--- a/ethtool.c ++++ b/ethtool.c +@@ -47,42 +47,6 @@ + #define MAX_ADDR_LEN 32 + #endif + +-#define ALL_ADVERTISED_MODES \ +- (ADVERTISED_10baseT_Half | \ +- ADVERTISED_10baseT_Full | \ +- ADVERTISED_100baseT_Half | \ +- ADVERTISED_100baseT_Full | \ +- ADVERTISED_1000baseT_Half | \ +- ADVERTISED_1000baseT_Full | \ +- ADVERTISED_1000baseKX_Full| \ +- ADVERTISED_2500baseX_Full | \ +- ADVERTISED_10000baseT_Full | \ +- ADVERTISED_10000baseKX4_Full | \ +- ADVERTISED_10000baseKR_Full | \ +- ADVERTISED_10000baseR_FEC | \ +- ADVERTISED_20000baseMLD2_Full | \ +- ADVERTISED_20000baseKR2_Full | \ +- ADVERTISED_40000baseKR4_Full | \ +- ADVERTISED_40000baseCR4_Full | \ +- ADVERTISED_40000baseSR4_Full | \ +- ADVERTISED_40000baseLR4_Full | \ +- ADVERTISED_56000baseKR4_Full | \ +- ADVERTISED_56000baseCR4_Full | \ +- ADVERTISED_56000baseSR4_Full | \ +- ADVERTISED_56000baseLR4_Full) +- +-#define ALL_ADVERTISED_FLAGS \ +- (ADVERTISED_Autoneg | \ +- ADVERTISED_TP | \ +- ADVERTISED_AUI | \ +- ADVERTISED_MII | \ +- ADVERTISED_FIBRE | \ +- ADVERTISED_BNC | \ +- ADVERTISED_Pause | \ +- ADVERTISED_Asym_Pause | \ +- ADVERTISED_Backplane | \ +- ALL_ADVERTISED_MODES) +- + #ifndef HAVE_NETIF_MSG + enum { + NETIF_MSG_DRV = 0x0001, +@@ -293,6 +257,43 @@ static void get_mac_addr(char *src, unsigned char *dest) + } + } + ++static int parse_hex_u32_bitmap(const char *s, ++ unsigned int nbits, u32 *result) ++{ ++ const unsigned int nwords = __KERNEL_DIV_ROUND_UP(nbits, 32); ++ size_t slen = strlen(s); ++ size_t i; ++ ++ /* ignore optional '0x' prefix */ ++ if ((slen > 2) && (strncasecmp(s, "0x", 2) == 0)) { ++ slen -= 2; ++ s += 2; ++ } ++ ++ if (slen > 8 * nwords) /* up to 2 digits per byte */ ++ return -1; ++ ++ memset(result, 0, 4 * nwords); ++ for (i = 0; i < slen; ++i) { ++ const unsigned int shift = (slen - 1 - i) * 4; ++ u32 *dest = &result[shift / 32]; ++ u32 nibble; ++ ++ if ('a' <= s[i] && s[i] <= 'f') ++ nibble = 0xa + (s[i] - 'a'); ++ else if ('A' <= s[i] && s[i] <= 'F') ++ nibble = 0xa + (s[i] - 'A'); ++ else if ('0' <= s[i] && s[i] <= '9') ++ nibble = (s[i] - '0'); ++ else ++ return -1; ++ ++ *dest |= (nibble << (shift % 32)); ++ } ++ ++ return 0; ++} ++ + static void parse_generic_cmdline(struct cmd_context *ctx, + int *changed, + struct cmdline_info *info, +@@ -472,64 +473,157 @@ static int do_version(struct cmd_context *ctx) + return 0; + } + +-static void dump_link_caps(const char *prefix, const char *an_prefix, u32 mask, +- int link_mode_only); ++/* link mode routines */ + +-static void dump_supported(struct ethtool_cmd *ep) ++static ETHTOOL_DECLARE_LINK_MODE_MASK(all_advertised_modes); ++static ETHTOOL_DECLARE_LINK_MODE_MASK(all_advertised_flags); ++ ++static void init_global_link_mode_masks(void) + { +- u32 mask = ep->supported; ++ static const enum ethtool_link_mode_bit_indices ++ all_advertised_modes_bits[] = { ++ ETHTOOL_LINK_MODE_10baseT_Half_BIT, ++ ETHTOOL_LINK_MODE_10baseT_Full_BIT, ++ ETHTOOL_LINK_MODE_100baseT_Half_BIT, ++ ETHTOOL_LINK_MODE_100baseT_Full_BIT, ++ ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ++ ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, ++ ETHTOOL_LINK_MODE_2500baseX_Full_BIT, ++ ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ++ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, ++ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, ++ ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, ++ ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT, ++ ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT, ++ ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, ++ ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, ++ ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, ++ ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, ++ ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, ++ ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, ++ ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, ++ ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, ++ }; ++ static const enum ethtool_link_mode_bit_indices ++ additional_advertised_flags_bits[] = { ++ ETHTOOL_LINK_MODE_Autoneg_BIT, ++ ETHTOOL_LINK_MODE_TP_BIT, ++ ETHTOOL_LINK_MODE_AUI_BIT, ++ ETHTOOL_LINK_MODE_MII_BIT, ++ ETHTOOL_LINK_MODE_FIBRE_BIT, ++ ETHTOOL_LINK_MODE_BNC_BIT, ++ ETHTOOL_LINK_MODE_Pause_BIT, ++ ETHTOOL_LINK_MODE_Asym_Pause_BIT, ++ ETHTOOL_LINK_MODE_Backplane_BIT, ++ }; ++ unsigned int i; + ++ ethtool_link_mode_zero(all_advertised_modes); ++ ethtool_link_mode_zero(all_advertised_flags); ++ for (i = 0; i < ARRAY_SIZE(all_advertised_modes_bits); ++i) { ++ ethtool_link_mode_set_bit(all_advertised_modes_bits[i], ++ all_advertised_modes); ++ ethtool_link_mode_set_bit(all_advertised_modes_bits[i], ++ all_advertised_flags); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(additional_advertised_flags_bits); ++i) { ++ ethtool_link_mode_set_bit( ++ additional_advertised_flags_bits[i], ++ all_advertised_flags); ++ } ++} ++ ++static void dump_link_caps(const char *prefix, const char *an_prefix, ++ const u32 *mask, int link_mode_only); ++ ++static void dump_supported(const struct ethtool_link_usettings *link_usettings) ++{ + fprintf(stdout, " Supported ports: [ "); +- if (mask & SUPPORTED_TP) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_TP_BIT, ++ link_usettings->link_modes.supported)) + fprintf(stdout, "TP "); +- if (mask & SUPPORTED_AUI) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_AUI_BIT, ++ link_usettings->link_modes.supported)) + fprintf(stdout, "AUI "); +- if (mask & SUPPORTED_BNC) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_BNC_BIT, ++ link_usettings->link_modes.supported)) + fprintf(stdout, "BNC "); +- if (mask & SUPPORTED_MII) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_MII_BIT, ++ link_usettings->link_modes.supported)) + fprintf(stdout, "MII "); +- if (mask & SUPPORTED_FIBRE) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_FIBRE_BIT, ++ link_usettings->link_modes.supported)) + fprintf(stdout, "FIBRE "); +- if (mask & SUPPORTED_Backplane) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_Backplane_BIT, ++ link_usettings->link_modes.supported)) + fprintf(stdout, "Backplane "); + fprintf(stdout, "]\n"); + +- dump_link_caps("Supported", "Supports", mask, 0); ++ dump_link_caps("Supported", "Supports", ++ link_usettings->link_modes.supported, 0); + } + + /* Print link capability flags (supported, advertised or lp_advertised). + * Assumes that the corresponding SUPPORTED and ADVERTISED flags are equal. + */ +-static void +-dump_link_caps(const char *prefix, const char *an_prefix, u32 mask, +- int link_mode_only) ++static void dump_link_caps(const char *prefix, const char *an_prefix, ++ const u32 *mask, int link_mode_only) + { + static const struct { + int same_line; /* print on same line as previous */ +- u32 value; ++ unsigned int bit_index; + const char *name; + } mode_defs[] = { +- { 0, ADVERTISED_10baseT_Half, "10baseT/Half" }, +- { 1, ADVERTISED_10baseT_Full, "10baseT/Full" }, +- { 0, ADVERTISED_100baseT_Half, "100baseT/Half" }, +- { 1, ADVERTISED_100baseT_Full, "100baseT/Full" }, +- { 0, ADVERTISED_1000baseT_Half, "1000baseT/Half" }, +- { 1, ADVERTISED_1000baseT_Full, "1000baseT/Full" }, +- { 0, ADVERTISED_1000baseKX_Full, "1000baseKX/Full" }, +- { 0, ADVERTISED_2500baseX_Full, "2500baseX/Full" }, +- { 0, ADVERTISED_10000baseT_Full, "10000baseT/Full" }, +- { 0, ADVERTISED_10000baseKX4_Full, "10000baseKX4/Full" }, +- { 0, ADVERTISED_10000baseKR_Full, "10000baseKR/Full" }, +- { 0, ADVERTISED_20000baseMLD2_Full, "20000baseMLD2/Full" }, +- { 0, ADVERTISED_20000baseKR2_Full, "20000baseKR2/Full" }, +- { 0, ADVERTISED_40000baseKR4_Full, "40000baseKR4/Full" }, +- { 0, ADVERTISED_40000baseCR4_Full, "40000baseCR4/Full" }, +- { 0, ADVERTISED_40000baseSR4_Full, "40000baseSR4/Full" }, +- { 0, ADVERTISED_40000baseLR4_Full, "40000baseLR4/Full" }, +- { 0, ADVERTISED_56000baseKR4_Full, "56000baseKR4/Full" }, +- { 0, ADVERTISED_56000baseCR4_Full, "56000baseCR4/Full" }, +- { 0, ADVERTISED_56000baseSR4_Full, "56000baseSR4/Full" }, +- { 0, ADVERTISED_56000baseLR4_Full, "56000baseLR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_10baseT_Half_BIT, ++ "10baseT/Half" }, ++ { 1, ETHTOOL_LINK_MODE_10baseT_Full_BIT, ++ "10baseT/Full" }, ++ { 0, ETHTOOL_LINK_MODE_100baseT_Half_BIT, ++ "100baseT/Half" }, ++ { 1, ETHTOOL_LINK_MODE_100baseT_Full_BIT, ++ "100baseT/Full" }, ++ { 0, ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ++ "1000baseT/Half" }, ++ { 1, ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ "1000baseT/Full" }, ++ { 0, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, ++ "1000baseKX/Full" }, ++ { 0, ETHTOOL_LINK_MODE_2500baseX_Full_BIT, ++ "2500baseX/Full" }, ++ { 0, ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ++ "10000baseT/Full" }, ++ { 0, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, ++ "10000baseKX4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, ++ "10000baseKR/Full" }, ++ { 0, ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT, ++ "20000baseMLD2/Full" }, ++ { 0, ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT, ++ "20000baseKR2/Full" }, ++ { 0, ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, ++ "40000baseKR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, ++ "40000baseCR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, ++ "40000baseSR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, ++ "40000baseLR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, ++ "56000baseKR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, ++ "56000baseCR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, ++ "56000baseSR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, ++ "56000baseLR4/Full" }, + }; + int indent; + int did1, new_line_pend, i; +@@ -546,7 +640,8 @@ dump_link_caps(const char *prefix, const char *an_prefix, u32 mask, + for (i = 0; i < ARRAY_SIZE(mode_defs); i++) { + if (did1 && !mode_defs[i].same_line) + new_line_pend = 1; +- if (mask & mode_defs[i].value) { ++ if (ethtool_link_mode_test_bit(mode_defs[i].bit_index, ++ mask)) { + if (new_line_pend) { + fprintf(stdout, "\n"); + fprintf(stdout, " %*s", indent, ""); +@@ -562,46 +657,52 @@ dump_link_caps(const char *prefix, const char *an_prefix, u32 mask, + + if (!link_mode_only) { + fprintf(stdout, " %s pause frame use: ", prefix); +- if (mask & ADVERTISED_Pause) { ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_Pause_BIT, mask)) { + fprintf(stdout, "Symmetric"); +- if (mask & ADVERTISED_Asym_Pause) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask)) + fprintf(stdout, " Receive-only"); + fprintf(stdout, "\n"); + } else { +- if (mask & ADVERTISED_Asym_Pause) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask)) + fprintf(stdout, "Transmit-only\n"); + else + fprintf(stdout, "No\n"); + } + + fprintf(stdout, " %s auto-negotiation: ", an_prefix); +- if (mask & ADVERTISED_Autoneg) ++ if (ethtool_link_mode_test_bit( ++ ETHTOOL_LINK_MODE_Autoneg_BIT, mask)) + fprintf(stdout, "Yes\n"); + else + fprintf(stdout, "No\n"); + } + } + +-static int dump_ecmd(struct ethtool_cmd *ep) ++static int ++dump_link_usettings(const struct ethtool_link_usettings *link_usettings) + { +- u32 speed; +- +- dump_supported(ep); +- dump_link_caps("Advertised", "Advertised", ep->advertising, 0); +- if (ep->lp_advertising) ++ dump_supported(link_usettings); ++ dump_link_caps("Advertised", "Advertised", ++ link_usettings->link_modes.advertising, 0); ++ if (!ethtool_link_mode_is_empty( ++ link_usettings->link_modes.lp_advertising)) + dump_link_caps("Link partner advertised", +- "Link partner advertised", ep->lp_advertising, +- 0); ++ "Link partner advertised", ++ link_usettings->link_modes.lp_advertising, 0); + + fprintf(stdout, " Speed: "); +- speed = ethtool_cmd_speed(ep); +- if (speed == 0 || speed == (u16)(-1) || speed == (u32)(-1)) ++ if (link_usettings->base.speed == 0 ++ || link_usettings->base.speed == (u16)(-1) ++ || link_usettings->base.speed == (u32)(-1)) + fprintf(stdout, "Unknown!\n"); + else +- fprintf(stdout, "%uMb/s\n", speed); ++ fprintf(stdout, "%uMb/s\n", link_usettings->base.speed); + + fprintf(stdout, " Duplex: "); +- switch (ep->duplex) { ++ switch (link_usettings->base.duplex) { + case DUPLEX_HALF: + fprintf(stdout, "Half\n"); + break; +@@ -609,12 +710,12 @@ static int dump_ecmd(struct ethtool_cmd *ep) + fprintf(stdout, "Full\n"); + break; + default: +- fprintf(stdout, "Unknown! (%i)\n", ep->duplex); ++ fprintf(stdout, "Unknown! (%i)\n", link_usettings->base.duplex); + break; + }; + + fprintf(stdout, " Port: "); +- switch (ep->port) { ++ switch (link_usettings->base.port) { + case PORT_TP: + fprintf(stdout, "Twisted Pair\n"); + break; +@@ -640,13 +741,13 @@ static int dump_ecmd(struct ethtool_cmd *ep) + fprintf(stdout, "Other\n"); + break; + default: +- fprintf(stdout, "Unknown! (%i)\n", ep->port); ++ fprintf(stdout, "Unknown! (%i)\n", link_usettings->base.port); + break; + }; + +- fprintf(stdout, " PHYAD: %d\n", ep->phy_address); ++ fprintf(stdout, " PHYAD: %d\n", link_usettings->base.phy_address); + fprintf(stdout, " Transceiver: "); +- switch (ep->transceiver) { ++ switch (link_usettings->deprecated.transceiver) { + case XCVR_INTERNAL: + fprintf(stdout, "internal\n"); + break; +@@ -659,17 +760,18 @@ static int dump_ecmd(struct ethtool_cmd *ep) + }; + + fprintf(stdout, " Auto-negotiation: %s\n", +- (ep->autoneg == AUTONEG_DISABLE) ? ++ (link_usettings->base.autoneg == AUTONEG_DISABLE) ? + "off" : "on"); + +- if (ep->port == PORT_TP) { ++ if (link_usettings->base.port == PORT_TP) { + fprintf(stdout, " MDI-X: "); +- if (ep->eth_tp_mdix_ctrl == ETH_TP_MDI) { ++ if (link_usettings->base.eth_tp_mdix_ctrl == ETH_TP_MDI) { + fprintf(stdout, "off (forced)\n"); +- } else if (ep->eth_tp_mdix_ctrl == ETH_TP_MDI_X) { ++ } else if (link_usettings->base.eth_tp_mdix_ctrl ++ == ETH_TP_MDI_X) { + fprintf(stdout, "on (forced)\n"); + } else { +- switch (ep->eth_tp_mdix) { ++ switch (link_usettings->base.eth_tp_mdix) { + case ETH_TP_MDI: + fprintf(stdout, "off"); + break; +@@ -680,7 +782,8 @@ static int dump_ecmd(struct ethtool_cmd *ep) + fprintf(stdout, "Unknown"); + break; + } +- if (ep->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) ++ if (link_usettings->base.eth_tp_mdix_ctrl ++ == ETH_TP_MDI_AUTO) + fprintf(stdout, " (auto)"); + fprintf(stdout, "\n"); + } +@@ -1368,6 +1471,7 @@ static int dump_rxfhash(int fhash, u64 val) + + static void dump_eeecmd(struct ethtool_eee *ep) + { ++ ETHTOOL_DECLARE_LINK_MODE_MASK(link_mode); + + fprintf(stdout, " EEE status: "); + if (!ep->supported) { +@@ -1389,9 +1493,16 @@ static void dump_eeecmd(struct ethtool_eee *ep) + else + fprintf(stdout, " disabled\n"); + +- dump_link_caps("Supported EEE", "", ep->supported, 1); +- dump_link_caps("Advertised EEE", "", ep->advertised, 1); +- dump_link_caps("Link partner advertised EEE", "", ep->lp_advertised, 1); ++ ethtool_link_mode_zero(link_mode); ++ ++ link_mode[0] = ep->supported; ++ dump_link_caps("Supported EEE", "", link_mode, 1); ++ ++ link_mode[0] = ep->advertised; ++ dump_link_caps("Advertised EEE", "", link_mode, 1); ++ ++ link_mode[0] = ep->lp_advertised; ++ dump_link_caps("Link partner advertised EEE", "", link_mode, 1); + } + + #define N_SOTS 7 +@@ -2259,10 +2370,220 @@ finish: + return retval; + } + +-static int do_gset(struct cmd_context *ctx) ++static struct ethtool_link_usettings * ++do_ioctl_glinksettings(struct cmd_context *ctx) ++{ ++ int err; ++ struct { ++ struct ethtool_link_settings req; ++ __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; ++ } ecmd; ++ struct ethtool_link_usettings *link_usettings; ++ unsigned int u32_offs; ++ ++ /* Handshake with kernel to determine number of words for link ++ * mode bitmaps. When requested number of bitmap words is not ++ * the one expected by kernel, the latter returns the integer ++ * opposite of what it is expecting. We request length 0 below ++ * (aka. invalid bitmap length) to get this info. ++ */ ++ memset(&ecmd, 0, sizeof(ecmd)); ++ ecmd.req.cmd = ETHTOOL_GLINKSETTINGS; ++ err = send_ioctl(ctx, &ecmd); ++ if (err < 0) ++ return NULL; ++ ++ /* see above: we expect a strictly negative value from kernel. ++ */ ++ if (ecmd.req.link_mode_masks_nwords >= 0 ++ || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) ++ return NULL; ++ ++ /* got the real ecmd.req.link_mode_masks_nwords, ++ * now send the real request ++ */ ++ ecmd.req.cmd = ETHTOOL_GLINKSETTINGS; ++ ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; ++ err = send_ioctl(ctx, &ecmd); ++ if (err < 0) ++ return NULL; ++ ++ if (ecmd.req.link_mode_masks_nwords <= 0 ++ || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) ++ return NULL; ++ ++ /* Convert to usettings struct */ ++ link_usettings = calloc(1, sizeof(*link_usettings)); ++ if (link_usettings == NULL) ++ return NULL; ++ ++ /* keep transceiver 0 */ ++ memcpy(&link_usettings->base, &ecmd.req, sizeof(link_usettings->base)); ++ ++ /* copy link mode bitmaps */ ++ u32_offs = 0; ++ memcpy(link_usettings->link_modes.supported, ++ &ecmd.link_mode_data[u32_offs], ++ 4 * ecmd.req.link_mode_masks_nwords); ++ ++ u32_offs += ecmd.req.link_mode_masks_nwords; ++ memcpy(link_usettings->link_modes.advertising, ++ &ecmd.link_mode_data[u32_offs], ++ 4 * ecmd.req.link_mode_masks_nwords); ++ ++ u32_offs += ecmd.req.link_mode_masks_nwords; ++ memcpy(link_usettings->link_modes.lp_advertising, ++ &ecmd.link_mode_data[u32_offs], ++ 4 * ecmd.req.link_mode_masks_nwords); ++ ++ return link_usettings; ++} ++ ++static int ++do_ioctl_slinksettings(struct cmd_context *ctx, ++ const struct ethtool_link_usettings *link_usettings) ++{ ++ struct { ++ struct ethtool_link_settings req; ++ __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; ++ } ecmd; ++ unsigned int u32_offs; ++ ++ /* refuse to send ETHTOOL_SLINKSETTINGS ioctl if ++ * link_usettings was retrieved with ETHTOOL_GSET ++ */ ++ if (link_usettings->base.cmd != ETHTOOL_GLINKSETTINGS) ++ return -1; ++ ++ /* refuse to send ETHTOOL_SLINKSETTINGS ioctl if deprecated fields ++ * were set ++ */ ++ if (link_usettings->deprecated.transceiver) ++ return -1; ++ ++ if (link_usettings->base.link_mode_masks_nwords <= 0) ++ return -1; ++ ++ memcpy(&ecmd.req, &link_usettings->base, sizeof(ecmd.req)); ++ ecmd.req.cmd = ETHTOOL_SLINKSETTINGS; ++ ++ /* copy link mode bitmaps */ ++ u32_offs = 0; ++ memcpy(&ecmd.link_mode_data[u32_offs], ++ link_usettings->link_modes.supported, ++ 4 * ecmd.req.link_mode_masks_nwords); ++ ++ u32_offs += ecmd.req.link_mode_masks_nwords; ++ memcpy(&ecmd.link_mode_data[u32_offs], ++ link_usettings->link_modes.advertising, ++ 4 * ecmd.req.link_mode_masks_nwords); ++ ++ u32_offs += ecmd.req.link_mode_masks_nwords; ++ memcpy(&ecmd.link_mode_data[u32_offs], ++ link_usettings->link_modes.lp_advertising, ++ 4 * ecmd.req.link_mode_masks_nwords); ++ ++ return send_ioctl(ctx, &ecmd); ++} ++ ++static struct ethtool_link_usettings * ++do_ioctl_gset(struct cmd_context *ctx) + { + int err; + struct ethtool_cmd ecmd; ++ struct ethtool_link_usettings *link_usettings; ++ ++ memset(&ecmd, 0, sizeof(ecmd)); ++ ecmd.cmd = ETHTOOL_GSET; ++ err = send_ioctl(ctx, &ecmd); ++ if (err < 0) ++ return NULL; ++ ++ link_usettings = calloc(1, sizeof(*link_usettings)); ++ if (link_usettings == NULL) ++ return NULL; ++ ++ /* remember that ETHTOOL_GSET was used */ ++ link_usettings->base.cmd = ETHTOOL_GSET; ++ ++ link_usettings->base.link_mode_masks_nwords = 1; ++ link_usettings->link_modes.supported[0] = ecmd.supported; ++ link_usettings->link_modes.advertising[0] = ecmd.advertising; ++ link_usettings->link_modes.lp_advertising[0] = ecmd.lp_advertising; ++ link_usettings->base.speed = ethtool_cmd_speed(&ecmd); ++ link_usettings->base.duplex = ecmd.duplex; ++ link_usettings->base.port = ecmd.port; ++ link_usettings->base.phy_address = ecmd.phy_address; ++ link_usettings->deprecated.transceiver = ecmd.transceiver; ++ link_usettings->base.autoneg = ecmd.autoneg; ++ link_usettings->base.mdio_support = ecmd.mdio_support; ++ /* ignored (fully deprecated): maxrxpkt, maxtxpkt */ ++ link_usettings->base.eth_tp_mdix = ecmd.eth_tp_mdix; ++ link_usettings->base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl; ++ ++ return link_usettings; ++} ++ ++static bool ethtool_link_mode_is_backward_compatible(const u32 *mask) ++{ ++ unsigned int i; ++ ++ for (i = 1; i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32; ++i) ++ if (mask[i]) ++ return false; ++ ++ return true; ++} ++ ++static int ++do_ioctl_sset(struct cmd_context *ctx, ++ const struct ethtool_link_usettings *link_usettings) ++{ ++ struct ethtool_cmd ecmd; ++ ++ /* refuse to send ETHTOOL_SSET ioctl if link_usettings was ++ * retrieved with ETHTOOL_GLINKSETTINGS ++ */ ++ if (link_usettings->base.cmd != ETHTOOL_GSET) ++ return -1; ++ ++ if (link_usettings->base.link_mode_masks_nwords <= 0) ++ return -1; ++ ++ /* refuse to sset if any bit > 31 is set */ ++ if (!ethtool_link_mode_is_backward_compatible( ++ link_usettings->link_modes.supported)) ++ return -1; ++ if (!ethtool_link_mode_is_backward_compatible( ++ link_usettings->link_modes.advertising)) ++ return -1; ++ if (!ethtool_link_mode_is_backward_compatible( ++ link_usettings->link_modes.lp_advertising)) ++ return -1; ++ ++ memset(&ecmd, 0, sizeof(ecmd)); ++ ecmd.cmd = ETHTOOL_SSET; ++ ++ ecmd.supported = link_usettings->link_modes.supported[0]; ++ ecmd.advertising = link_usettings->link_modes.advertising[0]; ++ ecmd.lp_advertising = link_usettings->link_modes.lp_advertising[0]; ++ ethtool_cmd_speed_set(&ecmd, link_usettings->base.speed); ++ ecmd.duplex = link_usettings->base.duplex; ++ ecmd.port = link_usettings->base.port; ++ ecmd.phy_address = link_usettings->base.phy_address; ++ ecmd.transceiver = link_usettings->deprecated.transceiver; ++ ecmd.autoneg = link_usettings->base.autoneg; ++ ecmd.mdio_support = link_usettings->base.mdio_support; ++ /* ignored (fully deprecated): maxrxpkt, maxtxpkt */ ++ ecmd.eth_tp_mdix = link_usettings->base.eth_tp_mdix; ++ ecmd.eth_tp_mdix_ctrl = link_usettings->base.eth_tp_mdix_ctrl; ++ return send_ioctl(ctx, &ecmd); ++} ++ ++static int do_gset(struct cmd_context *ctx) ++{ ++ int err; ++ struct ethtool_link_usettings *link_usettings; + struct ethtool_wolinfo wolinfo; + struct ethtool_value edata; + int allfail = 1; +@@ -2272,10 +2593,12 @@ static int do_gset(struct cmd_context *ctx) + + fprintf(stdout, "Settings for %s:\n", ctx->devname); + +- ecmd.cmd = ETHTOOL_GSET; +- err = send_ioctl(ctx, &ecmd); +- if (err == 0) { +- err = dump_ecmd(&ecmd); ++ link_usettings = do_ioctl_glinksettings(ctx); ++ if (link_usettings == NULL) ++ link_usettings = do_ioctl_gset(ctx); ++ if (link_usettings != NULL) { ++ err = dump_link_usettings(link_usettings); ++ free(link_usettings); + if (err) + return err; + allfail = 0; +@@ -2334,8 +2657,10 @@ static int do_sset(struct cmd_context *ctx) + int autoneg_wanted = -1; + int phyad_wanted = -1; + int xcvr_wanted = -1; +- int full_advertising_wanted = -1; +- int advertising_wanted = -1; ++ u32 *full_advertising_wanted = NULL; ++ u32 *advertising_wanted = NULL; ++ ETHTOOL_DECLARE_LINK_MODE_MASK(mask_full_advertising_wanted); ++ ETHTOOL_DECLARE_LINK_MODE_MASK(mask_advertising_wanted); + int gset_changed = 0; /* did anything in GSET change? */ + u32 wol_wanted = 0; + int wol_change = 0; +@@ -2349,7 +2674,7 @@ static int do_sset(struct cmd_context *ctx) + int argc = ctx->argc; + char **argp = ctx->argp; + int i; +- int err; ++ int err = 0; + + for (i = 0; i < ARRAY_SIZE(flags_msglvl); i++) + flag_to_cmdline_info(flags_msglvl[i].name, +@@ -2423,7 +2748,12 @@ static int do_sset(struct cmd_context *ctx) + i += 1; + if (i >= argc) + exit_bad_args(); +- full_advertising_wanted = get_int(argp[i], 16); ++ if (parse_hex_u32_bitmap( ++ argp[i], ++ ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS, ++ mask_full_advertising_wanted)) ++ exit_bad_args(); ++ full_advertising_wanted = mask_full_advertising_wanted; + } else if (!strcmp(argp[i], "phyad")) { + gset_changed = 1; + i += 1; +@@ -2480,75 +2810,89 @@ static int do_sset(struct cmd_context *ctx) + } + } + +- if (full_advertising_wanted < 0) { ++ if (full_advertising_wanted == NULL) { + /* User didn't supply a full advertisement bitfield: + * construct one from the specified speed and duplex. + */ ++ int adv_bit = -1; ++ + if (speed_wanted == SPEED_10 && duplex_wanted == DUPLEX_HALF) +- advertising_wanted = ADVERTISED_10baseT_Half; ++ adv_bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT; + else if (speed_wanted == SPEED_10 && + duplex_wanted == DUPLEX_FULL) +- advertising_wanted = ADVERTISED_10baseT_Full; ++ adv_bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT; + else if (speed_wanted == SPEED_100 && + duplex_wanted == DUPLEX_HALF) +- advertising_wanted = ADVERTISED_100baseT_Half; ++ adv_bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT; + else if (speed_wanted == SPEED_100 && + duplex_wanted == DUPLEX_FULL) +- advertising_wanted = ADVERTISED_100baseT_Full; ++ adv_bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT; + else if (speed_wanted == SPEED_1000 && + duplex_wanted == DUPLEX_HALF) +- advertising_wanted = ADVERTISED_1000baseT_Half; ++ adv_bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT; + else if (speed_wanted == SPEED_1000 && + duplex_wanted == DUPLEX_FULL) +- advertising_wanted = ADVERTISED_1000baseT_Full; ++ adv_bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT; + else if (speed_wanted == SPEED_2500 && + duplex_wanted == DUPLEX_FULL) +- advertising_wanted = ADVERTISED_2500baseX_Full; ++ adv_bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT; + else if (speed_wanted == SPEED_10000 && + duplex_wanted == DUPLEX_FULL) +- advertising_wanted = ADVERTISED_10000baseT_Full; +- else +- /* auto negotiate without forcing, +- * all supported speed will be assigned below +- */ +- advertising_wanted = 0; ++ adv_bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT; ++ ++ if (adv_bit >= 0) { ++ advertising_wanted = mask_advertising_wanted; ++ ethtool_link_mode_zero(advertising_wanted); ++ ethtool_link_mode_set_bit( ++ adv_bit, advertising_wanted); ++ } ++ /* otherwise: auto negotiate without forcing, ++ * all supported speed will be assigned below ++ */ + } + + if (gset_changed) { +- struct ethtool_cmd ecmd; ++ struct ethtool_link_usettings *link_usettings; + +- ecmd.cmd = ETHTOOL_GSET; +- err = send_ioctl(ctx, &ecmd); +- if (err < 0) { ++ link_usettings = do_ioctl_glinksettings(ctx); ++ if (link_usettings == NULL) ++ link_usettings = do_ioctl_gset(ctx); ++ if (link_usettings == NULL) { + perror("Cannot get current device settings"); ++ err = -1; + } else { + /* Change everything the user specified. */ + if (speed_wanted != -1) +- ethtool_cmd_speed_set(&ecmd, speed_wanted); ++ link_usettings->base.speed = speed_wanted; + if (duplex_wanted != -1) +- ecmd.duplex = duplex_wanted; ++ link_usettings->base.duplex = duplex_wanted; + if (port_wanted != -1) +- ecmd.port = port_wanted; ++ link_usettings->base.port = port_wanted; + if (mdix_wanted != -1) { + /* check driver supports MDI-X */ +- if (ecmd.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID) +- ecmd.eth_tp_mdix_ctrl = mdix_wanted; ++ if (link_usettings->base.eth_tp_mdix_ctrl ++ != ETH_TP_MDI_INVALID) ++ link_usettings->base.eth_tp_mdix_ctrl ++ = mdix_wanted; + else +- fprintf(stderr, "setting MDI not supported\n"); ++ fprintf(stderr, ++ "setting MDI not supported\n"); + } + if (autoneg_wanted != -1) +- ecmd.autoneg = autoneg_wanted; ++ link_usettings->base.autoneg = autoneg_wanted; + if (phyad_wanted != -1) +- ecmd.phy_address = phyad_wanted; ++ link_usettings->base.phy_address = phyad_wanted; + if (xcvr_wanted != -1) +- ecmd.transceiver = xcvr_wanted; ++ link_usettings->deprecated.transceiver ++ = xcvr_wanted; + /* XXX If the user specified speed or duplex + * then we should mask the advertised modes + * accordingly. For now, warn that we aren't + * doing that. + */ + if ((speed_wanted != -1 || duplex_wanted != -1) && +- ecmd.autoneg && advertising_wanted == 0) { ++ link_usettings->base.autoneg && ++ advertising_wanted == NULL) { + fprintf(stderr, "Cannot advertise"); + if (speed_wanted >= 0) + fprintf(stderr, " speed %d", +@@ -2560,37 +2904,55 @@ static int do_sset(struct cmd_context *ctx) + fprintf(stderr, "\n"); + } + if (autoneg_wanted == AUTONEG_ENABLE && +- advertising_wanted == 0) { ++ advertising_wanted == NULL) { ++ unsigned int i; ++ + /* Auto negotiation enabled, but with + * unspecified speed and duplex: enable all + * supported speeds and duplexes. + */ +- ecmd.advertising = +- (ecmd.advertising & +- ~ALL_ADVERTISED_MODES) | +- (ALL_ADVERTISED_MODES & ecmd.supported); ++ ethtool_link_mode_for_each_u32(i) { ++ u32 sup = link_usettings->link_modes.supported[i]; ++ u32 *adv = link_usettings->link_modes.advertising + i; ++ ++ *adv = ((*adv & ~all_advertised_modes[i]) ++ | (sup & all_advertised_modes[i])); ++ } + + /* If driver supports unknown flags, we cannot + * be sure that we enable all link modes. + */ +- if ((ecmd.supported & ALL_ADVERTISED_FLAGS) != +- ecmd.supported) { +- fprintf(stderr, "Driver supports one " +- "or more unknown flags\n"); ++ ethtool_link_mode_for_each_u32(i) { ++ u32 sup = link_usettings->link_modes.supported[i]; ++ ++ if ((sup & all_advertised_flags[i]) != sup) { ++ fprintf(stderr, "Driver supports one or more unknown flags\n"); ++ break; ++ } + } +- } else if (advertising_wanted > 0) { ++ } else if (advertising_wanted != NULL) { ++ unsigned int i; ++ + /* Enable all requested modes */ +- ecmd.advertising = +- (ecmd.advertising & +- ~ALL_ADVERTISED_MODES) | +- advertising_wanted; +- } else if (full_advertising_wanted > 0) { +- ecmd.advertising = full_advertising_wanted; ++ ethtool_link_mode_for_each_u32(i) { ++ u32 *adv = link_usettings->link_modes.advertising + i; ++ ++ *adv = ((*adv & ~all_advertised_modes[i]) ++ | advertising_wanted[i]); ++ } ++ } else if (full_advertising_wanted != NULL) { ++ ethtool_link_mode_copy( ++ link_usettings->link_modes.advertising, ++ full_advertising_wanted); + } + + /* Try to perform the update. */ +- ecmd.cmd = ETHTOOL_SSET; +- err = send_ioctl(ctx, &ecmd); ++ if (link_usettings->base.cmd == ETHTOOL_GLINKSETTINGS) ++ err = do_ioctl_slinksettings(ctx, ++ link_usettings); ++ else ++ err = do_ioctl_sset(ctx, link_usettings); ++ free(link_usettings); + if (err < 0) + perror("Cannot set new settings"); + } +@@ -4260,6 +4622,8 @@ int main(int argc, char **argp) + struct cmd_context ctx; + int k; + ++ init_global_link_mode_masks(); ++ + /* Skip command name */ + argp++; + argc--; +diff --git a/internal.h b/internal.h +index e98f532..ef27ab2 100644 +--- a/internal.h ++++ b/internal.h +@@ -12,6 +12,7 @@ + #ifdef HAVE_CONFIG_H + #include "ethtool-config.h" + #endif ++#include + #include + #include + #include +@@ -122,6 +123,71 @@ static inline int test_bit(unsigned int nr, const unsigned long *addr) + ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE | \ + ETH_FLAG_RXHASH) + ++/* internal API for link mode bitmap interaction with kernel. */ ++ ++#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 \ ++ (SCHAR_MAX) ++#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS \ ++ (32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32) ++#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES \ ++ (4 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32) ++#define ETHTOOL_DECLARE_LINK_MODE_MASK(name) \ ++ u32 name[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32] ++ ++struct ethtool_link_usettings { ++ struct { ++ __u8 transceiver; ++ } deprecated; ++ struct ethtool_link_settings base; ++ struct { ++ ETHTOOL_DECLARE_LINK_MODE_MASK(supported); ++ ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); ++ ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising); ++ } link_modes; ++}; ++ ++#define ethtool_link_mode_for_each_u32(index) \ ++ for ((index) = 0; \ ++ (index) < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32; \ ++ ++(index)) ++ ++static inline void ethtool_link_mode_zero(u32 *dst) ++{ ++ memset(dst, 0, ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES); ++} ++ ++static inline bool ethtool_link_mode_is_empty(const u32 *mask) ++{ ++ unsigned int i; ++ ++ ethtool_link_mode_for_each_u32(i) { ++ if (mask[i] != 0) ++ return false; ++ } ++ ++ return true; ++} ++ ++static inline void ethtool_link_mode_copy(u32 *dst, const u32 *src) ++{ ++ memcpy(dst, src, ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES); ++} ++ ++static inline int ethtool_link_mode_test_bit(unsigned int nr, const u32 *mask) ++{ ++ if (nr >= ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS) ++ return !!0; ++ return !!(mask[nr / 32] & (1 << (nr % 32))); ++} ++ ++static inline int ethtool_link_mode_set_bit(unsigned int nr, u32 *mask) ++{ ++ if (nr >= ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS) ++ return -1; ++ mask[nr / 32] |= (1 << (nr % 32)); ++ return 0; ++} ++ + /* Context for sub-commands */ + struct cmd_context { + const char *devname; /* net device name */ +diff --git a/test-cmdline.c b/test-cmdline.c +index 2fd7cbb..a94edea 100644 +--- a/test-cmdline.c ++++ b/test-cmdline.c +@@ -37,7 +37,20 @@ static struct test_case { + { 1, "--change devname autoneg foo" }, + { 1, "-s devname autoneg" }, + { 0, "--change devname advertise 0x1" }, ++ { 0, "--change devname advertise 0xf" }, ++ { 0, "--change devname advertise 0Xf" }, ++ { 0, "--change devname advertise 1" }, ++ { 0, "--change devname advertise f" }, ++ { 0, "--change devname advertise 01" }, ++ { 0, "--change devname advertise 0f" }, ++ { 0, "--change devname advertise 0xfffffffffffffffffffffffffffffffff" }, ++ { 0, "--change devname advertise fffffffffffffffffffffffffffffffff" }, ++ { 0, "--change devname advertise 0x0000fffffffffffffffffffffffffffff" }, ++ { 0, "--change devname advertise 0000fffffffffffffffffffffffffffff" }, ++ { 1, "-s devname advertise" }, ++ { 1, "-s devname advertise 0x" }, + { 1, "-s devname advertise foo" }, ++ { 1, "-s devname advertise 0xfoo" }, + { 1, "--change devname advertise" }, + { 0, "-s devname phyad 1" }, + { 1, "--change devname phyad foo" }, +-- +1.8.3.1 + diff --git a/SOURCES/0003-Disable-test-cases-for-rxfh-hash-key-pa.patch b/SOURCES/0003-Disable-test-cases-for-rxfh-hash-key-pa.patch deleted file mode 100644 index 7665712..0000000 --- a/SOURCES/0003-Disable-test-cases-for-rxfh-hash-key-pa.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 14ef76c155ad0d646f5c2d0fdfa31edacd4024c6 Mon Sep 17 00:00:00 2001 -From: Ben Hutchings -Date: Sun, 21 Sep 2014 23:40:29 +0100 -Subject: [PATCH 3/4] test-cmdline: Disable test cases for --rxfh hash key - parsing - -test-cmdline does not mock the result of send_ioctl() but just treats -a call to send_ioctl() as indicating successful parsing. Any parse -failure after this point cannot be tested for. Currently, the hash -key string passed to --rxfh isn't parsed until after the device's key -size is known, so we can't test it tis way. - -Signed-off-by: Ben Hutchings ---- - test-cmdline.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/test-cmdline.c b/test-cmdline.c -index be41a30..2fd7cbb 100644 ---- a/test-cmdline.c -+++ b/test-cmdline.c -@@ -184,8 +184,14 @@ static struct test_case { - { 0, "--set-rxfh-indir devname weight 1 2 3 4" }, - { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" }, - { 0, "-X devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" }, -+#if 0 -+ /* XXX These won't fail as expected because we don't parse the -+ * hash key until after the first send_ioctl(). That needs to -+ * be changed before we enable them. -+ */ - { 1, "--rxfh devname hkey foo" }, - { 1, "-X devname hkey foo" }, -+#endif - { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee weight 1 2 3 4" }, - { 0, "-X devname weight 1 2 3 4 hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" }, - { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee equal 2" }, --- -1.8.3.1 - diff --git a/SOURCES/0003-ethtool-copy.h-sync-with-net.patch b/SOURCES/0003-ethtool-copy.h-sync-with-net.patch new file mode 100644 index 0000000..b4fb20b --- /dev/null +++ b/SOURCES/0003-ethtool-copy.h-sync-with-net.patch @@ -0,0 +1,74 @@ +From c3a3768ddec648ddc15402b8054ee1b4d3b2f336 Mon Sep 17 00:00:00 2001 +From: Vidya Sagar Ravipati +Date: Tue, 23 Aug 2016 06:30:30 -0700 +Subject: [PATCH 3/7] ethtool-copy.h:sync with net + +This covers kernel changes upto: + +commit 89da45b8b5b2187734a11038b8593714f964ffd1 +Author: Gal Pressman +Date: Thu Jun 23 17:02:43 2016 +0300 + + ethtool: Add 50G baseSR2 link mode + + Add ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT bit. + + Signed-off-by: Gal Pressman + Signed-off-by: Saeed Mahameed + Cc: Ben Hutchings + Cc: David Decotigny + Acked-By: David Decotigny + Signed-off-by: David S. Miller + +Signed-off-by: John W. Linville +(cherry picked from commit c431e762fadca8648d78d0e7e1842e1a0c9ac309) +--- + ethtool-copy.h | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/ethtool-copy.h b/ethtool-copy.h +index 7c581ea..246f7b0 100644 +--- a/ethtool-copy.h ++++ b/ethtool-copy.h +@@ -1351,6 +1351,16 @@ enum ethtool_link_mode_bit_indices { + ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT = 28, + ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT = 29, + ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT = 30, ++ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT = 31, ++ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT = 32, ++ ETHTOOL_LINK_MODE_25000baseSR_Full_BIT = 33, ++ ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT = 34, ++ ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT = 35, ++ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT = 36, ++ ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT = 37, ++ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT = 38, ++ ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT = 39, ++ ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT = 40, + + /* Last allowed bit for __ETHTOOL_LINK_MODE_LEGACY_MASK is bit + * 31. Please do NOT define any SUPPORTED_* or ADVERTISED_* +@@ -1359,7 +1369,7 @@ enum ethtool_link_mode_bit_indices { + */ + + __ETHTOOL_LINK_MODE_LAST +- = ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, ++ = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, + }; + + #define __ETHTOOL_LINK_MODE_LEGACY_MASK(base_name) \ +@@ -1646,9 +1656,9 @@ enum ethtool_reset_flags { + * %ETHTOOL_GLINKSETTINGS: on entry, number of words passed by user + * (>= 0); on return, if handshake in progress, negative if + * request size unsupported by kernel: absolute value indicates +- * kernel recommended size and cmd field is 0, as well as all the +- * other fields; otherwise (handshake completed), strictly +- * positive to indicate size used by kernel and cmd field is ++ * kernel expected size and all the other fields but cmd ++ * are 0; otherwise (handshake completed), strictly positive ++ * to indicate size used by kernel and cmd field stays + * %ETHTOOL_GLINKSETTINGS, all other fields populated by driver. For + * %ETHTOOL_SLINKSETTINGS: must be valid on entry, ie. a positive + * value returned previously by %ETHTOOL_GLINKSETTINGS, otherwise +-- +1.8.3.1 + diff --git a/SOURCES/0004-ethtool-Reorganizing-SFF-8024-fields-for-SFP-QSFP.patch b/SOURCES/0004-ethtool-Reorganizing-SFF-8024-fields-for-SFP-QSFP.patch new file mode 100644 index 0000000..3e6de4d --- /dev/null +++ b/SOURCES/0004-ethtool-Reorganizing-SFF-8024-fields-for-SFP-QSFP.patch @@ -0,0 +1,872 @@ +From 0173e95eb07cef9f189299a040e11f31de15f638 Mon Sep 17 00:00:00 2001 +From: Vidya Sagar Ravipati +Date: Tue, 23 Aug 2016 06:30:31 -0700 +Subject: [PATCH 4/7] ethtool:Reorganizing SFF-8024 fields for SFP/QSFP + +This patch provides following support +a) Reorganized fields based out of SFF-8024 fields i.e. Identifier/ + Encoding/Connector types which are common across SFP/SFP+ (SFF-8472) + and QSFP+/QSFP28 (SFF-8436/SFF-8636) modules into sff-common files. +b) Moving the common functions for SFP+ and QSFP+ DOM into sff-common + files + +Standards for SFF-8024 +a) SFF-8024 Rev 4.0 dated May 31, 2016 + +Signed-off-by: Vidya Sagar Ravipati +Acked-by: Bert Kenward +Signed-off-by: John W. Linville +(cherry picked from commit 7a4c42258e3038ff943c1e27c74b4bca2d3190a0) +--- + Makefile.am | 3 +- + sff-common.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + sff-common.h | 189 +++++++++++++++++++++++++++++++++++++ + sfpdiag.c | 105 +++------------------ + sfpid.c | 103 +------------------- + 5 files changed, 511 insertions(+), 193 deletions(-) + create mode 100644 sff-common.c + create mode 100644 sff-common.h + +diff --git a/Makefile.am b/Makefile.am +index 6814bc9..3c9f387 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -13,7 +13,8 @@ ethtool_SOURCES += \ + 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 \ +- sfpid.c sfpdiag.c ixgbevf.c tse.c vmxnet3.c ++ sff-common.c sff-common.h sfpid.c sfpdiag.c \ ++ ixgbevf.c tse.c vmxnet3.c + endif + + TESTS = test-cmdline test-features +diff --git a/sff-common.c b/sff-common.c +new file mode 100644 +index 0000000..7700cbe +--- /dev/null ++++ b/sff-common.c +@@ -0,0 +1,304 @@ ++/* ++ * sff-common.c: Implements SFF-8024 Rev 4.0 i.e. Specifcation ++ * of pluggable I/O configuration ++ * ++ * Common utilities across SFF-8436/8636 and SFF-8472/8079 ++ * are defined in this file ++ * ++ * Copyright 2010 Solarflare Communications Inc. ++ * Aurelien Guillaume (C) 2012 ++ * Copyright (C) 2014 Cumulus networks Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Freeoftware Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Vidya Sagar Ravipati ++ * This implementation is loosely based on current SFP parser ++ * and SFF-8024 Rev 4.0 spec (ftp://ftp.seagate.com/pub/sff/SFF-8024.PDF) ++ * by SFF Committee. ++ */ ++ ++#include ++#include ++#include "sff-common.h" ++ ++double convert_mw_to_dbm(double mw) ++{ ++ return (10. * log10(mw / 1000.)) + 30.; ++} ++ ++void sff_show_value_with_unit(const __u8 *id, unsigned int reg, ++ const char *name, unsigned int mult, ++ const char *unit) ++{ ++ unsigned int val = id[reg]; ++ ++ printf("\t%-41s : %u%s\n", name, val * mult, unit); ++} ++ ++void sff_show_ascii(const __u8 *id, unsigned int first_reg, ++ unsigned int last_reg, const char *name) ++{ ++ unsigned int reg, val; ++ ++ printf("\t%-41s : ", name); ++ while (first_reg <= last_reg && id[last_reg] == ' ') ++ last_reg--; ++ for (reg = first_reg; reg <= last_reg; reg++) { ++ val = id[reg]; ++ putchar(((val >= 32) && (val <= 126)) ? val : '_'); ++ } ++ printf("\n"); ++} ++ ++void sff8024_show_oui(const __u8 *id, int id_offset) ++{ ++ printf("\t%-41s : %02x:%02x:%02x\n", "Vendor OUI", ++ id[id_offset], id[(id_offset) + 1], ++ id[(id_offset) + 2]); ++} ++ ++void sff8024_show_identifier(const __u8 *id, int id_offset) ++{ ++ printf("\t%-41s : 0x%02x", "Identifier", id[id_offset]); ++ switch (id[id_offset]) { ++ case SFF8024_ID_UNKNOWN: ++ printf(" (no module present, unknown, or unspecified)\n"); ++ break; ++ case SFF8024_ID_GBIC: ++ printf(" (GBIC)\n"); ++ break; ++ case SFF8024_ID_SOLDERED_MODULE: ++ printf(" (module soldered to motherboard)\n"); ++ break; ++ case SFF8024_ID_SFP: ++ printf(" (SFP)\n"); ++ break; ++ case SFF8024_ID_300_PIN_XBI: ++ printf(" (300 pin XBI)\n"); ++ break; ++ case SFF8024_ID_XENPAK: ++ printf(" (XENPAK)\n"); ++ break; ++ case SFF8024_ID_XFP: ++ printf(" (XFP)\n"); ++ break; ++ case SFF8024_ID_XFF: ++ printf(" (XFF)\n"); ++ break; ++ case SFF8024_ID_XFP_E: ++ printf(" (XFP-E)\n"); ++ break; ++ case SFF8024_ID_XPAK: ++ printf(" (XPAK)\n"); ++ break; ++ case SFF8024_ID_X2: ++ printf(" (X2)\n"); ++ break; ++ case SFF8024_ID_DWDM_SFP: ++ printf(" (DWDM-SFP)\n"); ++ break; ++ case SFF8024_ID_QSFP: ++ printf(" (QSFP)\n"); ++ break; ++ case SFF8024_ID_QSFP_PLUS: ++ printf(" (QSFP+)\n"); ++ break; ++ case SFF8024_ID_CXP: ++ printf(" (CXP)\n"); ++ break; ++ case SFF8024_ID_HD4X: ++ printf(" (Shielded Mini Multilane HD 4X)\n"); ++ break; ++ case SFF8024_ID_HD8X: ++ printf(" (Shielded Mini Multilane HD 8X)\n"); ++ break; ++ case SFF8024_ID_QSFP28: ++ printf(" (QSFP28)\n"); ++ break; ++ case SFF8024_ID_CXP2: ++ printf(" (CXP2/CXP28)\n"); ++ break; ++ case SFF8024_ID_CDFP: ++ printf(" (CDFP Style 1/Style 2)\n"); ++ break; ++ case SFF8024_ID_HD4X_FANOUT: ++ printf(" (Shielded Mini Multilane HD 4X Fanout Cable)\n"); ++ break; ++ case SFF8024_ID_HD8X_FANOUT: ++ printf(" (Shielded Mini Multilane HD 8X Fanout Cable)\n"); ++ break; ++ case SFF8024_ID_CDFP_S3: ++ printf(" (CDFP Style 3)\n"); ++ break; ++ case SFF8024_ID_MICRO_QSFP: ++ printf(" (microQSFP)\n"); ++ break; ++ default: ++ printf(" (reserved or unknown)\n"); ++ break; ++ } ++} ++ ++void sff8024_show_connector(const __u8 *id, int ctor_offset) ++{ ++ printf("\t%-41s : 0x%02x", "Connector", id[ctor_offset]); ++ switch (id[ctor_offset]) { ++ case SFF8024_CTOR_UNKNOWN: ++ printf(" (unknown or unspecified)\n"); ++ break; ++ case SFF8024_CTOR_SC: ++ printf(" (SC)\n"); ++ break; ++ case SFF8024_CTOR_FC_STYLE_1: ++ printf(" (Fibre Channel Style 1 copper)\n"); ++ break; ++ case SFF8024_CTOR_FC_STYLE_2: ++ printf(" (Fibre Channel Style 2 copper)\n"); ++ break; ++ case SFF8024_CTOR_BNC_TNC: ++ printf(" (BNC/TNC)\n"); ++ break; ++ case SFF8024_CTOR_FC_COAX: ++ printf(" (Fibre Channel coaxial headers)\n"); ++ break; ++ case SFF8024_CTOR_FIBER_JACK: ++ printf(" (FibreJack)\n"); ++ break; ++ case SFF8024_CTOR_LC: ++ printf(" (LC)\n"); ++ break; ++ case SFF8024_CTOR_MT_RJ: ++ printf(" (MT-RJ)\n"); ++ break; ++ case SFF8024_CTOR_MU: ++ printf(" (MU)\n"); ++ break; ++ case SFF8024_CTOR_SG: ++ printf(" (SG)\n"); ++ break; ++ case SFF8024_CTOR_OPT_PT: ++ printf(" (Optical pigtail)\n"); ++ break; ++ case SFF8024_CTOR_MPO: ++ printf(" (MPO Parallel Optic)\n"); ++ break; ++ case SFF8024_CTOR_MPO_2: ++ printf(" (MPO Parallel Optic - 2x16)\n"); ++ break; ++ case SFF8024_CTOR_HSDC_II: ++ printf(" (HSSDC II)\n"); ++ break; ++ case SFF8024_CTOR_COPPER_PT: ++ printf(" (Copper pigtail)\n"); ++ break; ++ case SFF8024_CTOR_RJ45: ++ printf(" (RJ45)\n"); ++ break; ++ case SFF8024_CTOR_NO_SEPARABLE: ++ printf(" (No separable connector)\n"); ++ break; ++ case SFF8024_CTOR_MXC_2x16: ++ printf(" (MXC 2x16)\n"); ++ break; ++ default: ++ printf(" (reserved or unknown)\n"); ++ break; ++ } ++} ++ ++void sff8024_show_encoding(const __u8 *id, int encoding_offset, int sff_type) ++{ ++ printf("\t%-41s : 0x%02x", "Encoding", id[encoding_offset]); ++ switch (id[encoding_offset]) { ++ case SFF8024_ENCODING_UNSPEC: ++ printf(" (unspecified)\n"); ++ break; ++ case SFF8024_ENCODING_8B10B: ++ printf(" (8B/10B)\n"); ++ break; ++ case SFF8024_ENCODING_4B5B: ++ printf(" (4B/5B)\n"); ++ break; ++ case SFF8024_ENCODING_NRZ: ++ printf(" (NRZ)\n"); ++ break; ++ case SFF8024_ENCODING_4h: ++ if (sff_type == ETH_MODULE_SFF_8472) ++ printf(" (Manchester)\n"); ++ else if (sff_type == ETH_MODULE_SFF_8636) ++ printf(" (SONET Scrambled)\n"); ++ break; ++ case SFF8024_ENCODING_5h: ++ if (sff_type == ETH_MODULE_SFF_8472) ++ printf(" (SONET Scrambled)\n"); ++ else if (sff_type == ETH_MODULE_SFF_8636) ++ printf(" (64B/66B)\n"); ++ break; ++ case SFF8024_ENCODING_6h: ++ if (sff_type == ETH_MODULE_SFF_8472) ++ printf(" (64B/66B)\n"); ++ else if (sff_type == ETH_MODULE_SFF_8636) ++ printf(" (Manchester)\n"); ++ break; ++ case SFF8024_ENCODING_256B: ++ printf(" ((256B/257B (transcoded FEC-enabled data))\n"); ++ break; ++ case SFF8024_ENCODING_PAM4: ++ printf(" (PAM4)\n"); ++ break; ++ default: ++ printf(" (reserved or unknown)\n"); ++ break; ++ } ++} ++ ++void sff_show_thresholds(struct sff_diags sd) ++{ ++ PRINT_BIAS("Laser bias current high alarm threshold", ++ sd.bias_cur[HALRM]); ++ PRINT_BIAS("Laser bias current low alarm threshold", ++ sd.bias_cur[LALRM]); ++ PRINT_BIAS("Laser bias current high warning threshold", ++ sd.bias_cur[HWARN]); ++ PRINT_BIAS("Laser bias current low warning threshold", ++ sd.bias_cur[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", ++ sd.sfp_temp[HALRM]); ++ PRINT_TEMP("Module temperature low alarm threshold", ++ sd.sfp_temp[LALRM]); ++ PRINT_TEMP("Module temperature high warning threshold", ++ sd.sfp_temp[HWARN]); ++ PRINT_TEMP("Module temperature low warning threshold", ++ sd.sfp_temp[LWARN]); ++ ++ PRINT_VCC("Module voltage high alarm threshold", ++ sd.sfp_voltage[HALRM]); ++ PRINT_VCC("Module voltage low alarm threshold", ++ sd.sfp_voltage[LALRM]); ++ PRINT_VCC("Module voltage high warning threshold", ++ sd.sfp_voltage[HWARN]); ++ PRINT_VCC("Module voltage low warning threshold", ++ sd.sfp_voltage[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/sff-common.h b/sff-common.h +new file mode 100644 +index 0000000..5562b4d +--- /dev/null ++++ b/sff-common.h +@@ -0,0 +1,189 @@ ++/* ++ * sff-common.h: Implements SFF-8024 Rev 4.0 i.e. Specifcation ++ * of pluggable I/O configuration ++ * ++ * Common utilities across SFF-8436/8636 and SFF-8472/8079 ++ * are defined in this file ++ * ++ * Copyright 2010 Solarflare Communications Inc. ++ * Aurelien Guillaume (C) 2012 ++ * Copyright (C) 2014 Cumulus networks Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Freeoftware Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Vidya Sagar Ravipati ++ * This implementation is loosely based on current SFP parser ++ * and SFF-8024 specs (ftp://ftp.seagate.com/pub/sff/SFF-8024.PDF) ++ * by SFF Committee. ++ */ ++ ++#ifndef SFF_COMMON_H__ ++#define SFF_COMMON_H__ ++ ++#include ++#include "internal.h" ++ ++#define SFF8024_ID_OFFSET 0x00 ++#define SFF8024_ID_UNKNOWN 0x00 ++#define SFF8024_ID_GBIC 0x01 ++#define SFF8024_ID_SOLDERED_MODULE 0x02 ++#define SFF8024_ID_SFP 0x03 ++#define SFF8024_ID_300_PIN_XBI 0x04 ++#define SFF8024_ID_XENPAK 0x05 ++#define SFF8024_ID_XFP 0x06 ++#define SFF8024_ID_XFF 0x07 ++#define SFF8024_ID_XFP_E 0x08 ++#define SFF8024_ID_XPAK 0x09 ++#define SFF8024_ID_X2 0x0A ++#define SFF8024_ID_DWDM_SFP 0x0B ++#define SFF8024_ID_QSFP 0x0C ++#define SFF8024_ID_QSFP_PLUS 0x0D ++#define SFF8024_ID_CXP 0x0E ++#define SFF8024_ID_HD4X 0x0F ++#define SFF8024_ID_HD8X 0x10 ++#define SFF8024_ID_QSFP28 0x11 ++#define SFF8024_ID_CXP2 0x12 ++#define SFF8024_ID_CDFP 0x13 ++#define SFF8024_ID_HD4X_FANOUT 0x14 ++#define SFF8024_ID_HD8X_FANOUT 0x15 ++#define SFF8024_ID_CDFP_S3 0x16 ++#define SFF8024_ID_MICRO_QSFP 0x17 ++#define SFF8024_ID_LAST SFF8024_ID_MICRO_QSFP ++#define SFF8024_ID_UNALLOCATED_LAST 0x7F ++#define SFF8024_ID_VENDOR_START 0x80 ++#define SFF8024_ID_VENDOR_LAST 0xFF ++ ++#define SFF8024_CTOR_UNKNOWN 0x00 ++#define SFF8024_CTOR_SC 0x01 ++#define SFF8024_CTOR_FC_STYLE_1 0x02 ++#define SFF8024_CTOR_FC_STYLE_2 0x03 ++#define SFF8024_CTOR_BNC_TNC 0x04 ++#define SFF8024_CTOR_FC_COAX 0x05 ++#define SFF8024_CTOR_FIBER_JACK 0x06 ++#define SFF8024_CTOR_LC 0x07 ++#define SFF8024_CTOR_MT_RJ 0x08 ++#define SFF8024_CTOR_MU 0x09 ++#define SFF8024_CTOR_SG 0x0A ++#define SFF8024_CTOR_OPT_PT 0x0B ++#define SFF8024_CTOR_MPO 0x0C ++#define SFF8024_CTOR_MPO_2 0x0D ++/* 0E-1Fh --- Reserved */ ++#define SFF8024_CTOR_HSDC_II 0x20 ++#define SFF8024_CTOR_COPPER_PT 0x21 ++#define SFF8024_CTOR_RJ45 0x22 ++#define SFF8024_CTOR_NO_SEPARABLE 0x23 ++#define SFF8024_CTOR_MXC_2x16 0x24 ++#define SFF8024_CTOR_LAST SFF8024_CTOR_MXC_2x16 ++#define SFF8024_CTOR_UNALLOCATED_LAST 0x7F ++#define SFF8024_CTOR_VENDOR_START 0x80 ++#define SFF8024_CTOR_VENDOR_LAST 0xFF ++ ++/* ENCODING Values */ ++#define SFF8024_ENCODING_UNSPEC 0x00 ++#define SFF8024_ENCODING_8B10B 0x01 ++#define SFF8024_ENCODING_4B5B 0x02 ++#define SFF8024_ENCODING_NRZ 0x03 ++/* ++ * Value: 04h ++ * SFF-8472 - Manchester ++ * SFF-8436/8636 - SONET Scrambled ++ */ ++#define SFF8024_ENCODING_4h 0x04 ++/* ++ * Value: 05h ++ * SFF-8472 - SONET Scrambled ++ * SFF-8436/8636 - 64B/66B ++ */ ++#define SFF8024_ENCODING_5h 0x05 ++/* ++ * Value: 06h ++ * SFF-8472 - 64B/66B ++ * SFF-8436/8636 - Manchester ++ */ ++#define SFF8024_ENCODING_6h 0x06 ++#define SFF8024_ENCODING_256B 0x07 ++#define SFF8024_ENCODING_PAM4 0x08 ++ ++/* Most common case: 16-bit unsigned integer in a certain unit */ ++#define OFFSET_TO_U16(offset) \ ++ (id[offset] << 8 | id[(offset) + 1]) ++ ++# define PRINT_xX_PWR(string, var) \ ++ printf("\t%-41s : %.4f mW / %.2f dBm\n", (string), \ ++ (double)((var) / 10000.), \ ++ convert_mw_to_dbm((double)((var) / 10000.))) ++ ++#define PRINT_BIAS(string, bias_cur) \ ++ printf("\t%-41s : %.3f mA\n", (string), \ ++ (double)(bias_cur / 500.)) ++ ++#define PRINT_TEMP(string, temp) \ ++ printf("\t%-41s : %.2f degrees C / %.2f degrees F\n", \ ++ (string), (double)(temp / 256.), \ ++ (double)(temp / 256. * 1.8 + 32.)) ++ ++#define PRINT_VCC(string, sfp_voltage) \ ++ printf("\t%-41s : %.4f V\n", (string), \ ++ (double)(sfp_voltage / 10000.)) ++ ++# define PRINT_xX_THRESH_PWR(string, var, index) \ ++ PRINT_xX_PWR(string, (var)[(index)]) ++ ++/* Channel Monitoring Fields */ ++struct sff_channel_diags { ++ __u16 bias_cur; /* Measured bias current in 2uA units */ ++ __u16 rx_power; /* Measured RX Power */ ++ __u16 tx_power; /* Measured TX Power */ ++}; ++ ++/* Module Monitoring Fields */ ++struct sff_diags { ++ ++#define MAX_CHANNEL_NUM 4 ++#define LWARN 0 ++#define HWARN 1 ++#define LALRM 2 ++#define HALRM 3 ++#define MCURR 4 ++ ++ /* Supports DOM */ ++ __u8 supports_dom; ++ /* Supports alarm/warning thold */ ++ __u8 supports_alarms; ++ /* RX Power: 0 = OMA, 1 = Average power */ ++ __u8 rx_power_type; ++ /* TX Power: 0 = Not supported, 1 = Average power */ ++ __u8 tx_power_type; ++ ++ __u8 calibrated_ext; /* Is externally calibrated */ ++ /* [5] tables are low/high warn, low/high alarm, current */ ++ /* SFP voltage in 0.1mV units */ ++ __u16 sfp_voltage[5]; ++ /* SFP Temp in 16-bit signed 1/256 Celcius */ ++ __s16 sfp_temp[5]; ++ /* Measured bias current in 2uA units */ ++ __u16 bias_cur[5]; ++ /* Measured TX Power */ ++ __u16 tx_power[5]; ++ /* Measured RX Power */ ++ __u16 rx_power[5]; ++ struct sff_channel_diags scd[MAX_CHANNEL_NUM]; ++}; ++ ++double convert_mw_to_dbm(double mw); ++void sff_show_value_with_unit(const __u8 *id, unsigned int reg, ++ const char *name, unsigned int mult, ++ const char *unit); ++void sff_show_ascii(const __u8 *id, unsigned int first_reg, ++ unsigned int last_reg, const char *name); ++void sff_show_thresholds(struct sff_diags sd); ++ ++void sff8024_show_oui(const __u8 *id, int id_offset); ++void sff8024_show_identifier(const __u8 *id, int id_offset); ++void sff8024_show_connector(const __u8 *id, int ctor_offset); ++void sff8024_show_encoding(const __u8 *id, int encoding_offset, int sff_type); ++ ++#endif /* SFF_COMMON_H__ */ +diff --git a/sfpdiag.c b/sfpdiag.c +index a3dbc9b..32e4cd8 100644 +--- a/sfpdiag.c ++++ b/sfpdiag.c +@@ -12,6 +12,7 @@ + #include + #include + #include "internal.h" ++#include "sff-common.h" + + /* Offsets in decimal, for direct comparison with the SFF specs */ + +@@ -86,28 +87,6 @@ + #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 Celsius */ +- __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 address offset */ +@@ -141,12 +120,6 @@ static struct sff8472_aw_flags { + { 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]) +@@ -170,10 +143,8 @@ static double convert_mw_to_dbm(double mw) + */ + #define A2_OFFSET_TO_TEMP(offset) ((__s16)A2_OFFSET_TO_U16(offset)) + +- +-static void sff8472_dom_parse(const __u8 *id, struct sff8472_diags *sd) ++static void sff8472_dom_parse(const __u8 *id, struct sff_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); +@@ -203,7 +174,6 @@ static void sff8472_dom_parse(const __u8 *id, struct sff8472_diags *sd) + 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. */ +@@ -218,7 +188,7 @@ static float befloattoh(const __u32 *source) + return converter.dst; + } + +-static void sff8472_calibration(const __u8 *id, struct sff8472_diags *sd) ++static void sff8472_calibration(const __u8 *id, struct sff_diags *sd) + { + int i; + __u16 rx_reading; +@@ -252,7 +222,7 @@ static void sff8472_calibration(const __u8 *id, struct sff8472_diags *sd) + } + } + +-static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd) ++static void sff8472_parse_eeprom(const __u8 *id, struct sff_diags *sd) + { + sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL; + sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW; +@@ -271,7 +241,7 @@ static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd) + + void sff8472_show_all(const __u8 *id) + { +- struct sff8472_diags sd; ++ struct sff_diags sd; + char *rx_power_string = NULL; + int i; + +@@ -279,40 +249,22 @@ void sff8472_show_all(const __u8 *id) + + if (!sd.supports_dom) { + printf("\t%-41s : No\n", "Optical diagnostics support"); +- return ; ++ 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); ++ PRINT_BIAS("Laser bias current", sd.bias_cur[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_xX_PWR(rx_power_string, sd.rx_power[MCURR]); + +- PRINT_TEMP("Module temperature", MCURR); +- PRINT_VCC("Module voltage", MCURR); ++ PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]); ++ PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]); + + printf("\t%-41s : %s\n", "Alarm/warning flags implemented", + (sd.supports_alarms ? "Yes" : "No")); +@@ -323,40 +275,7 @@ void sff8472_show_all(const __u8 *id) + 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); ++ sff_show_thresholds(sd); + } +- + } + +diff --git a/sfpid.c b/sfpid.c +index 0b5cd62..fd6415c 100644 +--- a/sfpid.c ++++ b/sfpid.c +@@ -9,27 +9,11 @@ + + #include + #include "internal.h" ++#include "sff-common.h" + + static void sff8079_show_identifier(const __u8 *id) + { +- printf("\t%-41s : 0x%02x", "Identifier", id[0]); +- switch (id[0]) { +- case 0x00: +- printf(" (no module present, unknown, or unspecified)\n"); +- break; +- case 0x01: +- printf(" (GBIC)\n"); +- break; +- case 0x02: +- printf(" (module soldered to motherboard)\n"); +- break; +- case 0x03: +- printf(" (SFP)\n"); +- break; +- default: +- printf(" (reserved or unknown)\n"); +- break; +- } ++ sff8024_show_identifier(id, 0); + } + + static void sff8079_show_ext_identifier(const __u8 *id) +@@ -47,60 +31,7 @@ static void sff8079_show_ext_identifier(const __u8 *id) + + static void sff8079_show_connector(const __u8 *id) + { +- printf("\t%-41s : 0x%02x", "Connector", id[2]); +- switch (id[2]) { +- case 0x00: +- printf(" (unknown or unspecified)\n"); +- break; +- case 0x01: +- printf(" (SC)\n"); +- break; +- case 0x02: +- printf(" (Fibre Channel Style 1 copper)\n"); +- break; +- case 0x03: +- printf(" (Fibre Channel Style 2 copper)\n"); +- break; +- case 0x04: +- printf(" (BNC/TNC)\n"); +- break; +- case 0x05: +- printf(" (Fibre Channel coaxial headers)\n"); +- break; +- case 0x06: +- printf(" (FibreJack)\n"); +- break; +- case 0x07: +- printf(" (LC)\n"); +- break; +- case 0x08: +- printf(" (MT-RJ)\n"); +- break; +- case 0x09: +- printf(" (MU)\n"); +- break; +- case 0x0a: +- printf(" (SG)\n"); +- break; +- case 0x0b: +- printf(" (Optical pigtail)\n"); +- break; +- case 0x0c: +- printf(" (MPO Parallel Optic)\n"); +- break; +- case 0x20: +- printf(" (HSSDC II)\n"); +- break; +- case 0x21: +- printf(" (Copper pigtail)\n"); +- break; +- case 0x22: +- printf(" (RJ45)\n"); +- break; +- default: +- printf(" (reserved or unknown)\n"); +- break; +- } ++ sff8024_show_connector(id, 2); + } + + static void sff8079_show_transceiver(const __u8 *id) +@@ -241,33 +172,7 @@ static void sff8079_show_transceiver(const __u8 *id) + + static void sff8079_show_encoding(const __u8 *id) + { +- printf("\t%-41s : 0x%02x", "Encoding", id[11]); +- switch (id[11]) { +- case 0x00: +- printf(" (unspecified)\n"); +- break; +- case 0x01: +- printf(" (8B/10B)\n"); +- break; +- case 0x02: +- printf(" (4B/5B)\n"); +- break; +- case 0x03: +- printf(" (NRZ)\n"); +- break; +- case 0x04: +- printf(" (Manchester)\n"); +- break; +- case 0x05: +- printf(" (SONET Scrambled)\n"); +- break; +- case 0x06: +- printf(" (64B/66B)\n"); +- break; +- default: +- printf(" (reserved or unknown)\n"); +- break; +- } ++ sff8024_show_encoding(id, 11, ETH_MODULE_SFF_8472); + } + + static void sff8079_show_rate_identifier(const __u8 *id) +-- +1.8.3.1 + diff --git a/SOURCES/0005-ethtool-QSFP-Plus-QSFP28-Diagnostics-Information-Sup.patch b/SOURCES/0005-ethtool-QSFP-Plus-QSFP28-Diagnostics-Information-Sup.patch new file mode 100644 index 0000000..a893134 --- /dev/null +++ b/SOURCES/0005-ethtool-QSFP-Plus-QSFP28-Diagnostics-Information-Sup.patch @@ -0,0 +1,1466 @@ +From 3cfd8329790e382c88c670e4f9e7014f1cbd4ad6 Mon Sep 17 00:00:00 2001 +From: Vidya Sagar Ravipati +Date: Tue, 23 Aug 2016 06:30:32 -0700 +Subject: [PATCH 5/7] ethtool:QSFP Plus/QSFP28 Diagnostics Information Support + +This patch provides following support +a) Support for diagnostics information for QSFP Plus/QSFP28 modules + based on SFF-8436/SFF-8636 + +Standards for QSFP+/QSFP28 +a) QSFP+/QSFP28 - SFF 8636 Rev 2.7 dated January 26,2016 +b) SFF-8024 Rev 4.0 dated May 31, 2016 + +Signed-off-by: Vidya Sagar Ravipati +Acked-by: Bert Kenward +Signed-off-by: John W. Linville +(cherry picked from commit a5e73bb05ee4de0a1c06b117e0ee3ea4cba83561) +--- + Makefile.am | 2 +- + ethtool.c | 5 + + internal.h | 3 + + qsfp.c | 788 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + qsfp.h | 595 +++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 1392 insertions(+), 1 deletion(-) + create mode 100644 qsfp.c + create mode 100644 qsfp.h + +diff --git a/Makefile.am b/Makefile.am +index 3c9f387..de2db2e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -14,7 +14,7 @@ ethtool_SOURCES += \ + pcnet32.c realtek.c tg3.c marvell.c vioc.c \ + smsc911x.c at76c50x-usb.c sfc.c stmmac.c \ + sff-common.c sff-common.h sfpid.c sfpdiag.c \ +- ixgbevf.c tse.c vmxnet3.c ++ ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h + endif + + TESTS = test-cmdline test-features +diff --git a/ethtool.c b/ethtool.c +index 796d0b5..ffb3573 100644 +--- a/ethtool.c ++++ b/ethtool.c +@@ -4355,6 +4355,11 @@ static int do_getmodule(struct cmd_context *ctx) + sff8079_show_all(eeprom->data); + sff8472_show_all(eeprom->data); + break; ++ case ETH_MODULE_SFF_8436: ++ case ETH_MODULE_SFF_8636: ++ sff8636_show_all(eeprom->data, ++ modinfo.eeprom_len); ++ break; + #endif + default: + geeprom_dump_hex = 1; +diff --git a/internal.h b/internal.h +index ef27ab2..3c08b74 100644 +--- a/internal.h ++++ b/internal.h +@@ -345,4 +345,7 @@ void sff8079_show_all(const __u8 *id); + /* Optics diagnostics */ + void sff8472_show_all(const __u8 *id); + ++/* QSFP Optics diagnostics */ ++void sff8636_show_all(const __u8 *id, __u32 eeprom_len); ++ + #endif /* ETHTOOL_INTERNAL_H__ */ +diff --git a/qsfp.c b/qsfp.c +new file mode 100644 +index 0000000..213802e +--- /dev/null ++++ b/qsfp.c +@@ -0,0 +1,788 @@ ++/* ++ * qsfp.c: Implements SFF-8636 based QSFP+/QSFP28 Diagnostics Memory map. ++ * ++ * Copyright 2010 Solarflare Communications Inc. ++ * Aurelien Guillaume (C) 2012 ++ * Copyright (C) 2014 Cumulus networks Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Freeoftware Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Vidya Ravipati ++ * This implementation is loosely based on current SFP parser ++ * and SFF-8636 spec Rev 2.7 (ftp://ftp.seagate.com/pub/sff/SFF-8636.PDF) ++ * by SFF Committee. ++ */ ++ ++/* ++ * Description: ++ * a) The register/memory layout is up to 5 128 byte pages defined by ++ * a "pages valid" register and switched via a "page select" ++ * register. Memory of 256 bytes can be memory mapped at a time ++ * according to SFF 8636. ++ * b) SFF 8636 based 640 bytes memory layout is presented for parser ++ * ++ * SFF 8636 based QSFP Memory Map ++ * ++ * 2-Wire Serial Address: 1010000x ++ * ++ * Lower Page 00h (128 bytes) ++ * ====================== ++ * | | ++ * |Page Select Byte(127)| ++ * ====================== ++ * | ++ * V ++ * ---------------------------------------- ++ * | | | | ++ * V V V V ++ * ---------- ---------- --------- ------------ ++ * | Upper | | Upper | | Upper | | Upper | ++ * | Page 00h | | Page 01h | | Page 02h | | Page 03h | ++ * | | |(Optional)| |(Optional)| | (Optional) | ++ * | | | | | | | | ++ * | | | | | | | | ++ * | ID | | AST | | User | | For | ++ * | Fields | | Table | | EEPROM | | Cable | ++ * | | | | | Data | | Assemblies | ++ * | | | | | | | | ++ * | | | | | | | | ++ * ----------- ----------- ---------- -------------- ++ * ++ * ++ **/ ++#include ++#include ++#include "internal.h" ++#include "sff-common.h" ++#include "qsfp.h" ++ ++#define MAX_DESC_SIZE 42 ++ ++static struct sff8636_aw_flags { ++ const char *str; /* Human-readable string, null at the end */ ++ int offset; /* A2-relative address offset */ ++ __u8 value; /* Alarm is on if (offset & value) != 0. */ ++} sff8636_aw_flags[] = { ++ { "Laser bias current high alarm (Chan 1)", ++ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_HALARM) }, ++ { "Laser bias current low alarm (Chan 1)", ++ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_LALARM) }, ++ { "Laser bias current high warning (Chan 1)", ++ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_HWARN) }, ++ { "Laser bias current low warning (Chan 1)", ++ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_LWARN) }, ++ ++ { "Laser bias current high alarm (Chan 2)", ++ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_HALARM) }, ++ { "Laser bias current low alarm (Chan 2)", ++ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_LALARM) }, ++ { "Laser bias current high warning (Chan 2)", ++ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_HWARN) }, ++ { "Laser bias current low warning (Chan 2)", ++ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_LWARN) }, ++ ++ { "Laser bias current high alarm (Chan 3)", ++ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_HALARM) }, ++ { "Laser bias current low alarm (Chan 3)", ++ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_LALARM) }, ++ { "Laser bias current high warning (Chan 3)", ++ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_HWARN) }, ++ { "Laser bias current low warning (Chan 3)", ++ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_LWARN) }, ++ ++ { "Laser bias current high alarm (Chan 4)", ++ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_HALARM) }, ++ { "Laser bias current low alarm (Chan 4)", ++ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_LALARM) }, ++ { "Laser bias current high warning (Chan 4)", ++ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_HWARN) }, ++ { "Laser bias current low warning (Chan 4)", ++ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_LWARN) }, ++ ++ { "Module temperature high alarm", ++ SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_HALARM_STATUS) }, ++ { "Module temperature low alarm", ++ SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_LALARM_STATUS) }, ++ { "Module temperature high warning", ++ SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_HWARN_STATUS) }, ++ { "Module temperature low warning", ++ SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_LWARN_STATUS) }, ++ ++ { "Module voltage high alarm", ++ SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_HALARM_STATUS) }, ++ { "Module voltage low alarm", ++ SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_LALARM_STATUS) }, ++ { "Module voltage high warning", ++ SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_HWARN_STATUS) }, ++ { "Module voltage low warning", ++ SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_LWARN_STATUS) }, ++ ++ { "Laser tx power high alarm (Channel 1)", ++ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_HALARM) }, ++ { "Laser tx power low alarm (Channel 1)", ++ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_LALARM) }, ++ { "Laser tx power high warning (Channel 1)", ++ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_HWARN) }, ++ { "Laser tx power low warning (Channel 1)", ++ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_LWARN) }, ++ ++ { "Laser tx power high alarm (Channel 2)", ++ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_HALARM) }, ++ { "Laser tx power low alarm (Channel 2)", ++ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_LALARM) }, ++ { "Laser tx power high warning (Channel 2)", ++ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_HWARN) }, ++ { "Laser tx power low warning (Channel 2)", ++ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_LWARN) }, ++ ++ { "Laser tx power high alarm (Channel 3)", ++ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_HALARM) }, ++ { "Laser tx power low alarm (Channel 3)", ++ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_LALARM) }, ++ { "Laser tx power high warning (Channel 3)", ++ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_HWARN) }, ++ { "Laser tx power low warning (Channel 3)", ++ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_LWARN) }, ++ ++ { "Laser tx power high alarm (Channel 4)", ++ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_HALARM) }, ++ { "Laser tx power low alarm (Channel 4)", ++ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_LALARM) }, ++ { "Laser tx power high warning (Channel 4)", ++ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_HWARN) }, ++ { "Laser tx power low warning (Channel 4)", ++ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_LWARN) }, ++ ++ { "Laser rx power high alarm (Channel 1)", ++ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_HALARM) }, ++ { "Laser rx power low alarm (Channel 1)", ++ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_LALARM) }, ++ { "Laser rx power high warning (Channel 1)", ++ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_HWARN) }, ++ { "Laser rx power low warning (Channel 1)", ++ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_LWARN) }, ++ ++ { "Laser rx power high alarm (Channel 2)", ++ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_HALARM) }, ++ { "Laser rx power low alarm (Channel 2)", ++ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_LALARM) }, ++ { "Laser rx power high warning (Channel 2)", ++ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_HWARN) }, ++ { "Laser rx power low warning (Channel 2)", ++ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_LWARN) }, ++ ++ { "Laser rx power high alarm (Channel 3)", ++ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_HALARM) }, ++ { "Laser rx power low alarm (Channel 3)", ++ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_LALARM) }, ++ { "Laser rx power high warning (Channel 3)", ++ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_HWARN) }, ++ { "Laser rx power low warning (Channel 3)", ++ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_LWARN) }, ++ ++ { "Laser rx power high alarm (Channel 4)", ++ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_HALARM) }, ++ { "Laser rx power low alarm (Channel 4)", ++ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_LALARM) }, ++ { "Laser rx power high warning (Channel 4)", ++ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_HWARN) }, ++ { "Laser rx power low warning (Channel 4)", ++ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_LWARN) }, ++ ++ { NULL, 0, 0 }, ++}; ++ ++static void sff8636_show_identifier(const __u8 *id) ++{ ++ sff8024_show_identifier(id, SFF8636_ID_OFFSET); ++} ++ ++static void sff8636_show_ext_identifier(const __u8 *id) ++{ ++ printf("\t%-41s : 0x%02x\n", "Extended identifier", ++ id[SFF8636_EXT_ID_OFFSET]); ++ ++ static const char *pfx = ++ "\tExtended identifier description :"; ++ ++ switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_PWR_CLASS_MASK) { ++ case SFF8636_EXT_ID_PWR_CLASS_1: ++ printf("%s 1.5W max. Power consumption\n", pfx); ++ break; ++ case SFF8636_EXT_ID_PWR_CLASS_2: ++ printf("%s 2.0W max. Power consumption\n", pfx); ++ break; ++ case SFF8636_EXT_ID_PWR_CLASS_3: ++ printf("%s 2.5W max. Power consumption\n", pfx); ++ break; ++ case SFF8636_EXT_ID_PWR_CLASS_4: ++ printf("%s 3.5W max. Power consumption\n", pfx); ++ break; ++ } ++ ++ if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK) ++ printf("%s CDR present in TX,", pfx); ++ else ++ printf("%s No CDR in TX,", pfx); ++ ++ if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK) ++ printf(" CDR present in RX\n"); ++ else ++ printf(" No CDR in RX\n"); ++ ++ switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_EPWR_CLASS_MASK) { ++ case SFF8636_EXT_ID_PWR_CLASS_LEGACY: ++ printf("%s", pfx); ++ break; ++ case SFF8636_EXT_ID_PWR_CLASS_5: ++ printf("%s 4.0W max. Power consumption,", pfx); ++ break; ++ case SFF8636_EXT_ID_PWR_CLASS_6: ++ printf("%s 4.5W max. Power consumption, ", pfx); ++ break; ++ case SFF8636_EXT_ID_PWR_CLASS_7: ++ printf("%s 5.0W max. Power consumption, ", pfx); ++ break; ++ } ++ if (id[SFF8636_PWR_MODE_OFFSET] & SFF8636_HIGH_PWR_ENABLE) ++ printf(" High Power Class (> 3.5 W) enabled\n"); ++ else ++ printf(" High Power Class (> 3.5 W) not enabled\n"); ++} ++ ++static void sff8636_show_connector(const __u8 *id) ++{ ++ sff8024_show_connector(id, SFF8636_CTOR_OFFSET); ++} ++ ++static void sff8636_show_transceiver(const __u8 *id) ++{ ++ static const char *pfx = ++ "\tTransceiver type :"; ++ ++ printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", ++ "Transceiver codes", ++ id[SFF8636_ETHERNET_COMP_OFFSET], ++ id[SFF8636_SONET_COMP_OFFSET], ++ id[SFF8636_SAS_COMP_OFFSET], ++ id[SFF8636_GIGE_COMP_OFFSET], ++ id[SFF8636_FC_LEN_OFFSET], ++ id[SFF8636_FC_TECH_OFFSET], ++ id[SFF8636_FC_TRANS_MEDIA_OFFSET], ++ id[SFF8636_FC_SPEED_OFFSET]); ++ ++ /* 10G/40G Ethernet Compliance Codes */ ++ if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LRM) ++ printf("%s 10G Ethernet: 10G Base-LRM\n", pfx); ++ if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LR) ++ printf("%s 10G Ethernet: 10G Base-LR\n", pfx); ++ if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_SR) ++ printf("%s 10G Ethernet: 10G Base-SR\n", pfx); ++ if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_CR4) ++ printf("%s 40G Ethernet: 40G Base-CR4\n", pfx); ++ if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_SR4) ++ printf("%s 40G Ethernet: 40G Base-SR4\n", pfx); ++ if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_LR4) ++ printf("%s 40G Ethernet: 40G Base-LR4\n", pfx); ++ if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_ACTIVE) ++ printf("%s 40G Ethernet: 40G Active Cable (XLPPI)\n", pfx); ++ /* Extended Specification Compliance Codes from SFF-8024 */ ++ if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_RSRVD) { ++ switch (id[SFF8636_OPTION_1_OFFSET]) { ++ case SFF8636_ETHERNET_UNSPECIFIED: ++ printf("%s (reserved or unknown)\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_AOC: ++ printf("%s 100G Ethernet: 100G AOC or 25GAUI C2M AOC with worst BER of 5x10^(-5)\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_100G_SR4: ++ printf("%s 100G Ethernet: 100G Base-SR4 or 25GBase-SR\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_100G_LR4: ++ printf("%s 100G Ethernet: 100G Base-LR4\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_ER4: ++ printf("%s 100G Ethernet: 100G Base-ER4\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_SR10: ++ printf("%s 100G Ethernet: 100G Base-SR10\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_CWDM4_FEC: ++ printf("%s 100G Ethernet: 100G CWDM4 MSA with FEC\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_PSM4: ++ printf("%s 100G Ethernet: 100G PSM4 Parallel SMF\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_ACC: ++ printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 5x10^(-5)\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_100G_CWDM4_NO_FEC: ++ printf("%s 100G Ethernet: 100G CWDM4 MSA without FEC\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_RSVD1: ++ printf("%s (reserved or unknown)\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_CR4: ++ printf("%s 100G Ethernet: 100G Base-CR4 or 25G Base-CR CA-L\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_25G_CR_CA_S: ++ printf("%s 25G Ethernet: 25G Base-CR CA-S\n", pfx); ++ break; ++ case SFF8636_ETHERNET_25G_CR_CA_N: ++ printf("%s 25G Ethernet: 25G Base-CR CA-N\n", pfx); ++ break; ++ case SFF8636_ETHERNET_40G_ER4: ++ printf("%s 40G Ethernet: 40G Base-ER4\n", pfx); ++ break; ++ case SFF8636_ETHERNET_4X10_SR: ++ printf("%s 4x10G Ethernet: 10G Base-SR\n", pfx); ++ break; ++ case SFF8636_ETHERNET_40G_PSM4: ++ printf("%s 40G Ethernet: 40G PSM4 Parallel SMF\n", pfx); ++ break; ++ case SFF8636_ETHERNET_G959_P1I1_2D1: ++ printf("%s Ethernet: G959.1 profile P1I1-2D1 (10709 MBd, 2km, 1310nm SM)\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_G959_P1S1_2D2: ++ printf("%s Ethernet: G959.1 profile P1S1-2D2 (10709 MBd, 40km, 1550nm SM)\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_G959_P1L1_2D2: ++ printf("%s Ethernet: G959.1 profile P1L1-2D2 (10709 MBd, 80km, 1550nm SM)\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_10GT_SFI: ++ printf("%s 10G Ethernet: 10G Base-T with SFI electrical interface\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_100G_CLR4: ++ printf("%s 100G Ethernet: 100G CLR4\n", pfx); ++ break; ++ case SFF8636_ETHERNET_100G_AOC2: ++ printf("%s 100G Ethernet: 100G AOC or 25GAUI C2M AOC with worst BER of 10^(-12)\n", ++ pfx); ++ break; ++ case SFF8636_ETHERNET_100G_ACC2: ++ printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n", ++ pfx); ++ break; ++ default: ++ printf("%s (reserved or unknown)\n", pfx); ++ break; ++ } ++ } ++ ++ /* SONET Compliance Codes */ ++ if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_40G_OTN)) ++ printf("%s 40G OTN (OTU3B/OTU3C)\n", pfx); ++ if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR)) ++ printf("%s SONET: OC-48, long reach\n", pfx); ++ if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR)) ++ printf("%s SONET: OC-48, intermediate reach\n", pfx); ++ if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR)) ++ printf("%s SONET: OC-48, short reach\n", pfx); ++ ++ /* SAS/SATA Compliance Codes */ ++ if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G)) ++ printf("%s SAS 6.0G\n", pfx); ++ if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G)) ++ printf("%s SAS 3.0G\n", pfx); ++ ++ /* Ethernet Compliance Codes */ ++ if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T) ++ printf("%s Ethernet: 1000BASE-T\n", pfx); ++ if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX) ++ printf("%s Ethernet: 1000BASE-CX\n", pfx); ++ if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX) ++ printf("%s Ethernet: 1000BASE-LX\n", pfx); ++ if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX) ++ printf("%s Ethernet: 1000BASE-SX\n", pfx); ++ ++ /* Fibre Channel link length */ ++ if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG) ++ printf("%s FC: very long distance (V)\n", pfx); ++ if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT) ++ printf("%s FC: short distance (S)\n", pfx); ++ if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT) ++ printf("%s FC: intermediate distance (I)\n", pfx); ++ if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG) ++ printf("%s FC: long distance (L)\n", pfx); ++ if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED) ++ printf("%s FC: medium distance (M)\n", pfx); ++ ++ /* Fibre Channel transmitter technology */ ++ if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC) ++ printf("%s FC: Longwave laser (LC)\n", pfx); ++ if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER) ++ printf("%s FC: Electrical inter-enclosure (EL)\n", pfx); ++ if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA) ++ printf("%s FC: Electrical intra-enclosure (EL)\n", pfx); ++ if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_WO_OFC) ++ printf("%s FC: Shortwave laser w/o OFC (SN)\n", pfx); ++ if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC) ++ printf("%s FC: Shortwave laser with OFC (SL)\n", pfx); ++ if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL) ++ printf("%s FC: Longwave laser (LL)\n", pfx); ++ ++ /* Fibre Channel transmission media */ ++ if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TW) ++ printf("%s FC: Twin Axial Pair (TW)\n", pfx); ++ if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TP) ++ printf("%s FC: Twisted Pair (TP)\n", pfx); ++ if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_MI) ++ printf("%s FC: Miniature Coax (MI)\n", pfx); ++ if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TV) ++ printf("%s FC: Video Coax (TV)\n", pfx); ++ if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M6) ++ printf("%s FC: Multimode, 62.5m (M6)\n", pfx); ++ if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M5) ++ printf("%s FC: Multimode, 50m (M5)\n", pfx); ++ if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_OM3) ++ printf("%s FC: Multimode, 50um (OM3)\n", pfx); ++ if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_SM) ++ printf("%s FC: Single Mode (SM)\n", pfx); ++ ++ /* Fibre Channel speed */ ++ if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS) ++ printf("%s FC: 1200 MBytes/sec\n", pfx); ++ if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS) ++ printf("%s FC: 800 MBytes/sec\n", pfx); ++ if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS) ++ printf("%s FC: 1600 MBytes/sec\n", pfx); ++ if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS) ++ printf("%s FC: 400 MBytes/sec\n", pfx); ++ if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS) ++ printf("%s FC: 200 MBytes/sec\n", pfx); ++ if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS) ++ printf("%s FC: 100 MBytes/sec\n", pfx); ++} ++ ++static void sff8636_show_encoding(const __u8 *id) ++{ ++ sff8024_show_encoding(id, SFF8636_ENCODING_OFFSET, ETH_MODULE_SFF_8636); ++} ++ ++static void sff8636_show_rate_identifier(const __u8 *id) ++{ ++ /* TODO: Need to fix rate select logic */ ++ printf("\t%-41s : 0x%02x\n", "Rate identifier", ++ id[SFF8636_EXT_RS_OFFSET]); ++} ++ ++static void sff8636_show_oui(const __u8 *id) ++{ ++ sff8024_show_oui(id, SFF8636_VENDOR_OUI_OFFSET); ++} ++ ++static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id) ++{ ++ printf("\t%-41s : 0x%02x", "Transmitter technology", ++ (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK)); ++ ++ switch (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK) { ++ case SFF8636_TRANS_850_VCSEL: ++ printf(" (850 nm VCSEL)\n"); ++ break; ++ case SFF8636_TRANS_1310_VCSEL: ++ printf(" (1310 nm VCSEL)\n"); ++ break; ++ case SFF8636_TRANS_1550_VCSEL: ++ printf(" (1550 nm VCSEL)\n"); ++ break; ++ case SFF8636_TRANS_1310_FP: ++ printf(" (1310 nm FP)\n"); ++ break; ++ case SFF8636_TRANS_1310_DFB: ++ printf(" (1310 nm DFB)\n"); ++ break; ++ case SFF8636_TRANS_1550_DFB: ++ printf(" (1550 nm DFB)\n"); ++ break; ++ case SFF8636_TRANS_1310_EML: ++ printf(" (1310 nm EML)\n"); ++ break; ++ case SFF8636_TRANS_1550_EML: ++ printf(" (1550 nm EML)\n"); ++ break; ++ case SFF8636_TRANS_OTHERS: ++ printf(" (Others/Undefined)\n"); ++ break; ++ case SFF8636_TRANS_1490_DFB: ++ printf(" (1490 nm DFB)\n"); ++ break; ++ case SFF8636_TRANS_COPPER_PAS_UNEQUAL: ++ printf(" (Copper cable unequalized)\n"); ++ break; ++ case SFF8636_TRANS_COPPER_PAS_EQUAL: ++ printf(" (Copper cable passive equalized)\n"); ++ break; ++ case SFF8636_TRANS_COPPER_LNR_FAR_EQUAL: ++ printf(" (Copper cable, near and far end limiting active equalizers)\n"); ++ break; ++ case SFF8636_TRANS_COPPER_FAR_EQUAL: ++ printf(" (Copper cable, far end limiting active equalizers)\n"); ++ break; ++ case SFF8636_TRANS_COPPER_NEAR_EQUAL: ++ printf(" (Copper cable, near end limiting active equalizers)\n"); ++ break; ++ case SFF8636_TRANS_COPPER_LNR_EQUAL: ++ printf(" (Copper cable, linear active equalizers)\n"); ++ break; ++ } ++ ++ if ((id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK) ++ >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) { ++ printf("\t%-41s : %udb\n", "Attenuation at 2.5GHz", ++ id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]); ++ printf("\t%-41s : %udb\n", "Attenuation at 5.0GHz", ++ id[SFF8636_WAVELEN_LOW_BYTE_OFFSET]); ++ printf("\t%-41s : %udb\n", "Attenuation at 7.0GHz", ++ id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]); ++ printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz", ++ id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]); ++ } else { ++ printf("\t%-41s : %.3lfnm\n", "Laser wavelength", ++ (((id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) | ++ id[SFF8636_WAVELEN_LOW_BYTE_OFFSET])*0.05)); ++ printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance", ++ (((id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) | ++ id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET])*0.005)); ++ } ++} ++ ++static void sff8636_show_revision_compliance(const __u8 *id) ++{ ++ static const char *pfx = ++ "\tRevision Compliance :"; ++ ++ switch (id[SFF8636_REV_COMPLIANCE_OFFSET]) { ++ case SFF8636_REV_UNSPECIFIED: ++ printf("%s Revision not specified\n", pfx); ++ break; ++ case SFF8636_REV_8436_48: ++ printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx); ++ break; ++ case SFF8636_REV_8436_8636: ++ printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx); ++ break; ++ case SFF8636_REV_8636_13: ++ printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx); ++ break; ++ case SFF8636_REV_8636_14: ++ printf("%s SFF-8636 Rev 1.4\n", pfx); ++ break; ++ case SFF8636_REV_8636_15: ++ printf("%s SFF-8636 Rev 1.5\n", pfx); ++ break; ++ case SFF8636_REV_8636_20: ++ printf("%s SFF-8636 Rev 2.0\n", pfx); ++ break; ++ case SFF8636_REV_8636_27: ++ printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx); ++ break; ++ default: ++ printf("%s Unallocated\n", pfx); ++ break; ++ } ++} ++ ++/* ++ * 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 SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset)) ++ ++static void sff8636_dom_parse(const __u8 *id, struct sff_diags *sd) ++{ ++ int i = 0; ++ ++ /* Monitoring Thresholds for Alarms and Warnings */ ++ sd->sfp_voltage[MCURR] = OFFSET_TO_U16(SFF8636_VCC_CURR); ++ sd->sfp_voltage[HALRM] = OFFSET_TO_U16(SFF8636_VCC_HALRM); ++ sd->sfp_voltage[LALRM] = OFFSET_TO_U16(SFF8636_VCC_LALRM); ++ sd->sfp_voltage[HWARN] = OFFSET_TO_U16(SFF8636_VCC_HWARN); ++ sd->sfp_voltage[LWARN] = OFFSET_TO_U16(SFF8636_VCC_LWARN); ++ ++ sd->sfp_temp[MCURR] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_CURR); ++ sd->sfp_temp[HALRM] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_HALRM); ++ sd->sfp_temp[LALRM] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_LALRM); ++ sd->sfp_temp[HWARN] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_HWARN); ++ sd->sfp_temp[LWARN] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_LWARN); ++ ++ sd->bias_cur[HALRM] = OFFSET_TO_U16(SFF8636_TX_BIAS_HALRM); ++ sd->bias_cur[LALRM] = OFFSET_TO_U16(SFF8636_TX_BIAS_LALRM); ++ sd->bias_cur[HWARN] = OFFSET_TO_U16(SFF8636_TX_BIAS_HWARN); ++ sd->bias_cur[LWARN] = OFFSET_TO_U16(SFF8636_TX_BIAS_LWARN); ++ ++ sd->tx_power[HALRM] = OFFSET_TO_U16(SFF8636_TX_PWR_HALRM); ++ sd->tx_power[LALRM] = OFFSET_TO_U16(SFF8636_TX_PWR_LALRM); ++ sd->tx_power[HWARN] = OFFSET_TO_U16(SFF8636_TX_PWR_HWARN); ++ sd->tx_power[LWARN] = OFFSET_TO_U16(SFF8636_TX_PWR_LWARN); ++ ++ sd->rx_power[HALRM] = OFFSET_TO_U16(SFF8636_RX_PWR_HALRM); ++ sd->rx_power[LALRM] = OFFSET_TO_U16(SFF8636_RX_PWR_LALRM); ++ sd->rx_power[HWARN] = OFFSET_TO_U16(SFF8636_RX_PWR_HWARN); ++ sd->rx_power[LWARN] = OFFSET_TO_U16(SFF8636_RX_PWR_LWARN); ++ ++ ++ /* Channel Specific Data */ ++ for (i = 0; i < MAX_CHANNEL_NUM; i++) { ++ u8 rx_power_offset, tx_bias_offset; ++ u8 tx_power_offset; ++ ++ switch (i) { ++ case 0: ++ rx_power_offset = SFF8636_RX_PWR_1_OFFSET; ++ tx_power_offset = SFF8636_TX_PWR_1_OFFSET; ++ tx_bias_offset = SFF8636_TX_BIAS_1_OFFSET; ++ break; ++ case 1: ++ rx_power_offset = SFF8636_RX_PWR_2_OFFSET; ++ tx_power_offset = SFF8636_TX_PWR_2_OFFSET; ++ tx_bias_offset = SFF8636_TX_BIAS_2_OFFSET; ++ break; ++ case 2: ++ rx_power_offset = SFF8636_RX_PWR_3_OFFSET; ++ tx_power_offset = SFF8636_TX_PWR_3_OFFSET; ++ tx_bias_offset = SFF8636_TX_BIAS_3_OFFSET; ++ break; ++ case 3: ++ rx_power_offset = SFF8636_RX_PWR_4_OFFSET; ++ tx_power_offset = SFF8636_TX_PWR_4_OFFSET; ++ tx_bias_offset = SFF8636_TX_BIAS_4_OFFSET; ++ break; ++ default: ++ printf(" Invalid channel: %d\n", i); ++ break; ++ } ++ sd->scd[i].bias_cur = OFFSET_TO_U16(tx_bias_offset); ++ sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset); ++ sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset); ++ } ++ ++} ++ ++static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len) ++{ ++ struct sff_diags sd; ++ char *rx_power_string = NULL; ++ char power_string[MAX_DESC_SIZE]; ++ int i; ++ ++ /* ++ * There is no clear identifier to signify the existence of ++ * optical diagnostics similar to SFF-8472. So checking existence ++ * of page 3, will provide the gurantee for existence of alarms ++ * and thresholds ++ * If pagging support exists, then supports_alarms is marked as 1 ++ */ ++ ++ if (eeprom_len == ETH_MODULE_SFF_8636_MAX_LEN) { ++ if (!(id[SFF8636_STATUS_2_OFFSET] & ++ SFF8636_STATUS_PAGE_3_PRESENT)) { ++ sd.supports_alarms = 1; ++ } ++ } ++ ++ sd.rx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] & ++ SFF8636_RX_PWR_TYPE_MASK; ++ sd.tx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] & ++ SFF8636_RX_PWR_TYPE_MASK; ++ ++ sff8636_dom_parse(id, &sd); ++ ++ PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]); ++ PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]); ++ ++ /* ++ * SFF-8636/8436 spec is not clear whether RX power/ TX bias ++ * current fields are supported or not. A valid temperature ++ * reading is used as existence for TX/RX power. ++ */ ++ if ((sd.sfp_temp[MCURR] == 0x0) || (sd.sfp_temp[MCURR] == 0xFFFF)) ++ return; ++ ++ printf("\t%-41s : %s\n", "Alarm/warning flags implemented", ++ (sd.supports_alarms ? "Yes" : "No")); ++ ++ for (i = 0; i < MAX_CHANNEL_NUM; i++) { ++ snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)", ++ "Laser tx bias current", i+1); ++ PRINT_BIAS(power_string, sd.scd[i].bias_cur); ++ } ++ ++ for (i = 0; i < MAX_CHANNEL_NUM; i++) { ++ snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)", ++ "Transmit avg optical power", i+1); ++ PRINT_xX_PWR(power_string, sd.scd[i].tx_power); ++ } ++ ++ if (!sd.rx_power_type) ++ rx_power_string = "Receiver signal OMA"; ++ else ++ rx_power_string = "Rcvr signal avg optical power"; ++ ++ for (i = 0; i < MAX_CHANNEL_NUM; i++) { ++ snprintf(power_string, MAX_DESC_SIZE, "%s(Channel %d)", ++ rx_power_string, i+1); ++ PRINT_xX_PWR(power_string, sd.scd[i].rx_power); ++ } ++ ++ if (sd.supports_alarms) { ++ for (i = 0; sff8636_aw_flags[i].str; ++i) { ++ printf("\t%-41s : %s\n", sff8636_aw_flags[i].str, ++ id[sff8636_aw_flags[i].offset] ++ & sff8636_aw_flags[i].value ? "On" : "Off"); ++ } ++ ++ sff_show_thresholds(sd); ++ } ++ ++} ++void sff8636_show_all(const __u8 *id, __u32 eeprom_len) ++{ ++ sff8636_show_identifier(id); ++ if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) || ++ (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) || ++ (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) { ++ sff8636_show_ext_identifier(id); ++ sff8636_show_connector(id); ++ sff8636_show_transceiver(id); ++ sff8636_show_encoding(id); ++ sff_show_value_with_unit(id, SFF8636_BR_NOMINAL_OFFSET, ++ "BR, Nominal", 100, "Mbps"); ++ sff8636_show_rate_identifier(id); ++ sff_show_value_with_unit(id, SFF8636_SM_LEN_OFFSET, ++ "Length (SMF,km)", 1, "km"); ++ sff_show_value_with_unit(id, SFF8636_OM3_LEN_OFFSET, ++ "Length (OM3 50um)", 2, "m"); ++ sff_show_value_with_unit(id, SFF8636_OM2_LEN_OFFSET, ++ "Length (OM2 50um)", 1, "m"); ++ sff_show_value_with_unit(id, SFF8636_OM1_LEN_OFFSET, ++ "Length (OM1 62.5um)", 1, "m"); ++ sff_show_value_with_unit(id, SFF8636_CBL_LEN_OFFSET, ++ "Length (Copper or Active cable)", 1, "m"); ++ sff8636_show_wavelength_or_copper_compliance(id); ++ sff_show_ascii(id, SFF8636_VENDOR_NAME_START_OFFSET, ++ SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name"); ++ sff8636_show_oui(id); ++ sff_show_ascii(id, SFF8636_VENDOR_PN_START_OFFSET, ++ SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN"); ++ sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET, ++ SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev"); ++ sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET, ++ SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN"); ++ sff8636_show_revision_compliance(id); ++ sff8636_show_dom(id, eeprom_len); ++ } ++} +diff --git a/qsfp.h b/qsfp.h +new file mode 100644 +index 0000000..553ca5f +--- /dev/null ++++ b/qsfp.h +@@ -0,0 +1,595 @@ ++/* ++ * SFF 8636 standards based QSFP EEPROM Field Definitions ++ * ++ * Vidya Ravipati ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++ ++#ifndef QSFP_H__ ++#define QSFP_H__ ++ ++/*------------------------------------------------------------------------------ ++ * ++ * QSFP EEPROM data structures ++ * ++ * register info from SFF-8636 Rev 2.7 ++ */ ++ ++/*------------------------------------------------------------------------------ ++ * ++ * Lower Memory Page 00h ++ * Measurement, Diagnostic and Control Functions ++ * ++ */ ++/* Identifier - 0 */ ++/* Values are defined under SFF8024_ID_OFFSET */ ++#define SFF8636_ID_OFFSET 0x00 ++ ++#define SFF8636_REV_COMPLIANCE_OFFSET 0x01 ++#define SFF8636_REV_UNSPECIFIED 0x00 ++#define SFF8636_REV_8436_48 0x01 ++#define SFF8636_REV_8436_8636 0x02 ++#define SFF8636_REV_8636_13 0x03 ++#define SFF8636_REV_8636_14 0x04 ++#define SFF8636_REV_8636_15 0x05 ++#define SFF8636_REV_8636_20 0x06 ++#define SFF8636_REV_8636_27 0x07 ++ ++#define SFF8636_STATUS_2_OFFSET 0x02 ++/* Flat Memory:0- Paging, 1- Page 0 only */ ++#define SFF8636_STATUS_PAGE_3_PRESENT (1 << 2) ++#define SFF8636_STATUS_INTL_OUTPUT (1 << 1) ++#define SFF8636_STATUS_DATA_NOT_READY (1 << 0) ++ ++/* Channel Status Interrupt Flags - 3-5 */ ++#define SFF8636_LOS_AW_OFFSET 0x03 ++#define SFF8636_TX4_LOS_AW (1 << 7) ++#define SFF8636_TX3_LOS_AW (1 << 6) ++#define SFF8636_TX2_LOS_AW (1 << 5) ++#define SFF8636_TX1_LOS_AW (1 << 4) ++#define SFF8636_RX4_LOS_AW (1 << 3) ++#define SFF8636_RX3_LOS_AW (1 << 2) ++#define SFF8636_RX2_LOS_AW (1 << 1) ++#define SFF8636_RX1_LOS_AW (1 << 0) ++ ++#define SFF8636_FAULT_AW_OFFSET 0x04 ++#define SFF8636_TX4_FAULT_AW (1 << 3) ++#define SFF8636_TX3_FAULT_AW (1 << 2) ++#define SFF8636_TX2_FAULT_AW (1 << 1) ++#define SFF8636_TX1_FAULT_AW (1 << 0) ++ ++/* Module Monitor Interrupt Flags - 6-8 */ ++#define SFF8636_TEMP_AW_OFFSET 0x06 ++#define SFF8636_TEMP_HALARM_STATUS (1 << 7) ++#define SFF8636_TEMP_LALARM_STATUS (1 << 6) ++#define SFF8636_TEMP_HWARN_STATUS (1 << 5) ++#define SFF8636_TEMP_LWARN_STATUS (1 << 4) ++ ++#define SFF8636_VCC_AW_OFFSET 0x07 ++#define SFF8636_VCC_HALARM_STATUS (1 << 7) ++#define SFF8636_VCC_LALARM_STATUS (1 << 6) ++#define SFF8636_VCC_HWARN_STATUS (1 << 5) ++#define SFF8636_VCC_LWARN_STATUS (1 << 4) ++ ++/* Channel Monitor Interrupt Flags - 9-21 */ ++#define SFF8636_RX_PWR_12_AW_OFFSET 0x09 ++#define SFF8636_RX_PWR_1_HALARM (1 << 7) ++#define SFF8636_RX_PWR_1_LALARM (1 << 6) ++#define SFF8636_RX_PWR_1_HWARN (1 << 5) ++#define SFF8636_RX_PWR_1_LWARN (1 << 4) ++#define SFF8636_RX_PWR_2_HALARM (1 << 3) ++#define SFF8636_RX_PWR_2_LALARM (1 << 2) ++#define SFF8636_RX_PWR_2_HWARN (1 << 1) ++#define SFF8636_RX_PWR_2_LWARN (1 << 0) ++ ++#define SFF8636_RX_PWR_34_AW_OFFSET 0x0A ++#define SFF8636_RX_PWR_3_HALARM (1 << 7) ++#define SFF8636_RX_PWR_3_LALARM (1 << 6) ++#define SFF8636_RX_PWR_3_HWARN (1 << 5) ++#define SFF8636_RX_PWR_3_LWARN (1 << 4) ++#define SFF8636_RX_PWR_4_HALARM (1 << 3) ++#define SFF8636_RX_PWR_4_LALARM (1 << 2) ++#define SFF8636_RX_PWR_4_HWARN (1 << 1) ++#define SFF8636_RX_PWR_4_LWARN (1 << 0) ++ ++#define SFF8636_TX_BIAS_12_AW_OFFSET 0x0B ++#define SFF8636_TX_BIAS_1_HALARM (1 << 7) ++#define SFF8636_TX_BIAS_1_LALARM (1 << 6) ++#define SFF8636_TX_BIAS_1_HWARN (1 << 5) ++#define SFF8636_TX_BIAS_1_LWARN (1 << 4) ++#define SFF8636_TX_BIAS_2_HALARM (1 << 3) ++#define SFF8636_TX_BIAS_2_LALARM (1 << 2) ++#define SFF8636_TX_BIAS_2_HWARN (1 << 1) ++#define SFF8636_TX_BIAS_2_LWARN (1 << 0) ++ ++#define SFF8636_TX_BIAS_34_AW_OFFSET 0xC ++#define SFF8636_TX_BIAS_3_HALARM (1 << 7) ++#define SFF8636_TX_BIAS_3_LALARM (1 << 6) ++#define SFF8636_TX_BIAS_3_HWARN (1 << 5) ++#define SFF8636_TX_BIAS_3_LWARN (1 << 4) ++#define SFF8636_TX_BIAS_4_HALARM (1 << 3) ++#define SFF8636_TX_BIAS_4_LALARM (1 << 2) ++#define SFF8636_TX_BIAS_4_HWARN (1 << 1) ++#define SFF8636_TX_BIAS_4_LWARN (1 << 0) ++ ++#define SFF8636_TX_PWR_12_AW_OFFSET 0x0D ++#define SFF8636_TX_PWR_1_HALARM (1 << 7) ++#define SFF8636_TX_PWR_1_LALARM (1 << 6) ++#define SFF8636_TX_PWR_1_HWARN (1 << 5) ++#define SFF8636_TX_PWR_1_LWARN (1 << 4) ++#define SFF8636_TX_PWR_2_HALARM (1 << 3) ++#define SFF8636_TX_PWR_2_LALARM (1 << 2) ++#define SFF8636_TX_PWR_2_HWARN (1 << 1) ++#define SFF8636_TX_PWR_2_LWARN (1 << 0) ++ ++#define SFF8636_TX_PWR_34_AW_OFFSET 0x0E ++#define SFF8636_TX_PWR_3_HALARM (1 << 7) ++#define SFF8636_TX_PWR_3_LALARM (1 << 6) ++#define SFF8636_TX_PWR_3_HWARN (1 << 5) ++#define SFF8636_TX_PWR_3_LWARN (1 << 4) ++#define SFF8636_TX_PWR_4_HALARM (1 << 3) ++#define SFF8636_TX_PWR_4_LALARM (1 << 2) ++#define SFF8636_TX_PWR_4_HWARN (1 << 1) ++#define SFF8636_TX_PWR_4_LWARN (1 << 0) ++ ++/* Module Monitoring Values - 22-33 */ ++#define SFF8636_TEMP_CURR 0x16 ++#define SFF8636_TEMP_MSB_OFFSET 0x16 ++#define SFF8636_TEMP_LSB_OFFSET 0x17 ++ ++#define SFF8636_VCC_CURR 0x1A ++#define SFF8636_VCC_MSB_OFFSET 0x1A ++#define SFF8636_VCC_LSB_OFFSET 0x1B ++ ++/* Channel Monitoring Values - 34-81 */ ++#define SFF8636_RX_PWR_1_OFFSET 0x22 ++#define SFF8636_RX_PWR_2_OFFSET 0x24 ++#define SFF8636_RX_PWR_3_OFFSET 0x26 ++#define SFF8636_RX_PWR_4_OFFSET 0x28 ++ ++#define SFF8636_TX_BIAS_1_OFFSET 0x2A ++#define SFF8636_TX_BIAS_2_OFFSET 0x2C ++#define SFF8636_TX_BIAS_3_OFFSET 0x2E ++#define SFF8636_TX_BIAS_4_OFFSET 0x30 ++ ++#define SFF8636_TX_PWR_1_OFFSET 0x32 ++#define SFF8636_TX_PWR_2_OFFSET 0x34 ++#define SFF8636_TX_PWR_3_OFFSET 0x36 ++#define SFF8636_TX_PWR_4_OFFSET 0x38 ++ ++/* Control Bytes - 86 - 99 */ ++#define SFF8636_TX_DISABLE_OFFSET 0x56 ++#define SFF8636_TX_DISABLE_4 (1 << 3) ++#define SFF8636_TX_DISABLE_3 (1 << 2) ++#define SFF8636_TX_DISABLE_2 (1 << 1) ++#define SFF8636_TX_DISABLE_1 (1 << 0) ++ ++#define SFF8636_RX_RATE_SELECT_OFFSET 0x57 ++#define SFF8636_RX_RATE_SELECT_4_MASK (3 << 6) ++#define SFF8636_RX_RATE_SELECT_3_MASK (3 << 4) ++#define SFF8636_RX_RATE_SELECT_2_MASK (3 << 2) ++#define SFF8636_RX_RATE_SELECT_1_MASK (3 << 0) ++ ++#define SFF8636_TX_RATE_SELECT_OFFSET 0x58 ++#define SFF8636_TX_RATE_SELECT_4_MASK (3 << 6) ++#define SFF8636_TX_RATE_SELECT_3_MASK (3 << 4) ++#define SFF8636_TX_RATE_SELECT_2_MASK (3 << 2) ++#define SFF8636_TX_RATE_SELECT_1_MASK (3 << 0) ++ ++#define SFF8636_RX_APP_SELECT_4_OFFSET 0x58 ++#define SFF8636_RX_APP_SELECT_3_OFFSET 0x59 ++#define SFF8636_RX_APP_SELECT_2_OFFSET 0x5A ++#define SFF8636_RX_APP_SELECT_1_OFFSET 0x5B ++ ++#define SFF8636_PWR_MODE_OFFSET 0x5D ++#define SFF8636_HIGH_PWR_ENABLE (1 << 2) ++#define SFF8636_LOW_PWR_MODE (1 << 1) ++#define SFF8636_PWR_OVERRIDE (1 << 0) ++ ++#define SFF8636_TX_APP_SELECT_4_OFFSET 0x5E ++#define SFF8636_TX_APP_SELECT_3_OFFSET 0x5F ++#define SFF8636_TX_APP_SELECT_2_OFFSET 0x60 ++#define SFF8636_TX_APP_SELECT_1_OFFSET 0x61 ++ ++#define SFF8636_LOS_MASK_OFFSET 0x64 ++#define SFF8636_TX_LOS_4_MASK (1 << 7) ++#define SFF8636_TX_LOS_3_MASK (1 << 6) ++#define SFF8636_TX_LOS_2_MASK (1 << 5) ++#define SFF8636_TX_LOS_1_MASK (1 << 4) ++#define SFF8636_RX_LOS_4_MASK (1 << 3) ++#define SFF8636_RX_LOS_3_MASK (1 << 2) ++#define SFF8636_RX_LOS_2_MASK (1 << 1) ++#define SFF8636_RX_LOS_1_MASK (1 << 0) ++ ++#define SFF8636_FAULT_MASK_OFFSET 0x65 ++#define SFF8636_TX_FAULT_1_MASK (1 << 3) ++#define SFF8636_TX_FAULT_2_MASK (1 << 2) ++#define SFF8636_TX_FAULT_3_MASK (1 << 1) ++#define SFF8636_TX_FAULT_4_MASK (1 << 0) ++ ++#define SFF8636_TEMP_MASK_OFFSET 0x67 ++#define SFF8636_TEMP_HALARM_MASK (1 << 7) ++#define SFF8636_TEMP_LALARM_MASK (1 << 6) ++#define SFF8636_TEMP_HWARN_MASK (1 << 5) ++#define SFF8636_TEMP_LWARN_MASK (1 << 4) ++ ++#define SFF8636_VCC_MASK_OFFSET 0x68 ++#define SFF8636_VCC_HALARM_MASK (1 << 7) ++#define SFF8636_VCC_LALARM_MASK (1 << 6) ++#define SFF8636_VCC_HWARN_MASK (1 << 5) ++#define SFF8636_VCC_LWARN_MASK (1 << 4) ++ ++/*------------------------------------------------------------------------------ ++ * ++ * Upper Memory Page 00h ++ * Serial ID - Base ID, Extended ID and Vendor Specific ID fields ++ * ++ */ ++/* Identifier - 128 */ ++/* Identifier values same as Lower Memory Page 00h */ ++#define SFF8636_UPPER_PAGE_0_ID_OFFSET 0x80 ++ ++/* Extended Identifier - 128 */ ++#define SFF8636_EXT_ID_OFFSET 0x81 ++#define SFF8636_EXT_ID_PWR_CLASS_MASK 0xC0 ++#define SFF8636_EXT_ID_PWR_CLASS_1 (0 << 6) ++#define SFF8636_EXT_ID_PWR_CLASS_2 (1 << 6) ++#define SFF8636_EXT_ID_PWR_CLASS_3 (2 << 6) ++#define SFF8636_EXT_ID_PWR_CLASS_4 (3 << 6) ++#define SFF8636_EXT_ID_CLIE_MASK 0x10 ++#define SFF8636_EXT_ID_CLIEI_CODE_PRESENT (1 << 4) ++#define SFF8636_EXT_ID_CDR_TX_MASK 0x08 ++#define SFF8636_EXT_ID_CDR_TX_PRESENT (1 << 3) ++#define SFF8636_EXT_ID_CDR_RX_MASK 0x04 ++#define SFF8636_EXT_ID_CDR_RX_PRESENT (1 << 2) ++#define SFF8636_EXT_ID_EPWR_CLASS_MASK 0x03 ++#define SFF8636_EXT_ID_PWR_CLASS_LEGACY 0 ++#define SFF8636_EXT_ID_PWR_CLASS_5 1 ++#define SFF8636_EXT_ID_PWR_CLASS_6 2 ++#define SFF8636_EXT_ID_PWR_CLASS_7 3 ++ ++/* Connector Values offset - 130 */ ++/* Values are defined under SFF8024_CTOR */ ++#define SFF8636_CTOR_OFFSET 0x82 ++#define SFF8636_CTOR_UNKNOWN 0x00 ++#define SFF8636_CTOR_SC 0x01 ++#define SFF8636_CTOR_FC_STYLE_1 0x02 ++#define SFF8636_CTOR_FC_STYLE_2 0x03 ++#define SFF8636_CTOR_BNC_TNC 0x04 ++#define SFF8636_CTOR_FC_COAX 0x05 ++#define SFF8636_CTOR_FIBER_JACK 0x06 ++#define SFF8636_CTOR_LC 0x07 ++#define SFF8636_CTOR_MT_RJ 0x08 ++#define SFF8636_CTOR_MU 0x09 ++#define SFF8636_CTOR_SG 0x0A ++#define SFF8636_CTOR_OPT_PT 0x0B ++#define SFF8636_CTOR_MPO 0x0C ++/* 0D-1Fh --- Reserved */ ++#define SFF8636_CTOR_HSDC_II 0x20 ++#define SFF8636_CTOR_COPPER_PT 0x21 ++#define SFF8636_CTOR_RJ45 0x22 ++#define SFF8636_CTOR_NO_SEPARABLE 0x23 ++#define SFF8636_CTOR_MXC_2X16 0x24 ++ ++/* Specification Compliance - 131-138 */ ++/* Ethernet Compliance Codes - 131 */ ++#define SFF8636_ETHERNET_COMP_OFFSET 0x83 ++#define SFF8636_ETHERNET_RSRVD (1 << 7) ++#define SFF8636_ETHERNET_10G_LRM (1 << 6) ++#define SFF8636_ETHERNET_10G_LR (1 << 5) ++#define SFF8636_ETHERNET_10G_SR (1 << 4) ++#define SFF8636_ETHERNET_40G_CR4 (1 << 3) ++#define SFF8636_ETHERNET_40G_SR4 (1 << 2) ++#define SFF8636_ETHERNET_40G_LR4 (1 << 1) ++#define SFF8636_ETHERNET_40G_ACTIVE (1 << 0) ++ ++/* SONET Compliance Codes - 132 */ ++#define SFF8636_SONET_COMP_OFFSET 0x84 ++#define SFF8636_SONET_40G_OTN (1 << 3) ++#define SFF8636_SONET_OC48_LR (1 << 2) ++#define SFF8636_SONET_OC48_IR (1 << 1) ++#define SFF8636_SONET_OC48_SR (1 << 0) ++ ++/* SAS/SATA Complaince Codes - 133 */ ++#define SFF8636_SAS_COMP_OFFSET 0x85 ++#define SFF8636_SAS_12G (1 << 6) ++#define SFF8636_SAS_6G (1 << 5) ++#define SFF8636_SAS_3G (1 << 4) ++ ++/* Gigabit Ethernet Compliance Codes - 134 */ ++#define SFF8636_GIGE_COMP_OFFSET 0x86 ++#define SFF8636_GIGE_1000_BASE_T (1 << 3) ++#define SFF8636_GIGE_1000_BASE_CX (1 << 2) ++#define SFF8636_GIGE_1000_BASE_LX (1 << 1) ++#define SFF8636_GIGE_1000_BASE_SX (1 << 0) ++ ++/* Fibre Channel Link length/Transmitter Tech. - 135,136 */ ++#define SFF8636_FC_LEN_OFFSET 0x87 ++#define SFF8636_FC_LEN_VERY_LONG (1 << 7) ++#define SFF8636_FC_LEN_SHORT (1 << 6) ++#define SFF8636_FC_LEN_INT (1 << 5) ++#define SFF8636_FC_LEN_LONG (1 << 4) ++#define SFF8636_FC_LEN_MED (1 << 3) ++#define SFF8636_FC_TECH_LONG_LC (1 << 1) ++#define SFF8636_FC_TECH_ELEC_INTER (1 << 0) ++ ++#define SFF8636_FC_TECH_OFFSET 0x88 ++#define SFF8636_FC_TECH_ELEC_INTRA (1 << 7) ++#define SFF8636_FC_TECH_SHORT_WO_OFC (1 << 6) ++#define SFF8636_FC_TECH_SHORT_W_OFC (1 << 5) ++#define SFF8636_FC_TECH_LONG_LL (1 << 4) ++ ++/* Fibre Channel Transmitter Media - 137 */ ++#define SFF8636_FC_TRANS_MEDIA_OFFSET 0x89 ++/* Twin Axial Pair */ ++#define SFF8636_FC_TRANS_MEDIA_TW (1 << 7) ++/* Shielded Twisted Pair */ ++#define SFF8636_FC_TRANS_MEDIA_TP (1 << 6) ++/* Miniature Coax */ ++#define SFF8636_FC_TRANS_MEDIA_MI (1 << 5) ++/* Video Coax */ ++#define SFF8636_FC_TRANS_MEDIA_TV (1 << 4) ++/* Multi-mode 62.5m */ ++#define SFF8636_FC_TRANS_MEDIA_M6 (1 << 3) ++/* Multi-mode 50m */ ++#define SFF8636_FC_TRANS_MEDIA_M5 (1 << 2) ++/* Multi-mode 50um */ ++#define SFF8636_FC_TRANS_MEDIA_OM3 (1 << 1) ++/* Single Mode */ ++#define SFF8636_FC_TRANS_MEDIA_SM (1 << 0) ++ ++/* Fibre Channel Speed - 138 */ ++#define SFF8636_FC_SPEED_OFFSET 0x8A ++#define SFF8636_FC_SPEED_1200_MBPS (1 << 7) ++#define SFF8636_FC_SPEED_800_MBPS (1 << 6) ++#define SFF8636_FC_SPEED_1600_MBPS (1 << 5) ++#define SFF8636_FC_SPEED_400_MBPS (1 << 4) ++#define SFF8636_FC_SPEED_200_MBPS (1 << 2) ++#define SFF8636_FC_SPEED_100_MBPS (1 << 0) ++ ++/* Encoding - 139 */ ++/* Values are defined under SFF8024_ENCODING */ ++#define SFF8636_ENCODING_OFFSET 0x8B ++#define SFF8636_ENCODING_MANCHESTER 0x06 ++#define SFF8636_ENCODING_64B66B 0x05 ++#define SFF8636_ENCODING_SONET 0x04 ++#define SFF8636_ENCODING_NRZ 0x03 ++#define SFF8636_ENCODING_4B5B 0x02 ++#define SFF8636_ENCODING_8B10B 0x01 ++#define SFF8636_ENCODING_UNSPEC 0x00 ++ ++/* BR, Nominal - 140 */ ++#define SFF8636_BR_NOMINAL_OFFSET 0x8C ++ ++/* Extended RateSelect - 141 */ ++#define SFF8636_EXT_RS_OFFSET 0x8D ++#define SFF8636_EXT_RS_V1 (1 << 0) ++ ++/* Length (Standard SM Fiber)-km - 142 */ ++#define SFF8636_SM_LEN_OFFSET 0x8E ++ ++/* Length (OM3)-Unit 2m - 143 */ ++#define SFF8636_OM3_LEN_OFFSET 0x8F ++ ++/* Length (OM2)-Unit 1m - 144 */ ++#define SFF8636_OM2_LEN_OFFSET 0x90 ++ ++/* Length (OM1)-Unit 1m - 145 */ ++#define SFF8636_OM1_LEN_OFFSET 0x91 ++ ++/* Cable Assembly Length -Unit 1m - 146 */ ++#define SFF8636_CBL_LEN_OFFSET 0x92 ++ ++/* Device Technology - 147 */ ++#define SFF8636_DEVICE_TECH_OFFSET 0x93 ++/* Transmitter Technology */ ++#define SFF8636_TRANS_TECH_MASK 0xF0 ++/* Copper cable, linear active equalizers */ ++#define SFF8636_TRANS_COPPER_LNR_EQUAL (15 << 4) ++/* Copper cable, near end limiting active equalizers */ ++#define SFF8636_TRANS_COPPER_NEAR_EQUAL (14 << 4) ++/* Copper cable, far end limiting active equalizers */ ++#define SFF8636_TRANS_COPPER_FAR_EQUAL (13 << 4) ++/* Copper cable, near & far end limiting active equalizers */ ++#define SFF8636_TRANS_COPPER_LNR_FAR_EQUAL (12 << 4) ++/* Copper cable, passive equalized */ ++#define SFF8636_TRANS_COPPER_PAS_EQUAL (11 << 4) ++/* Copper cable, unequalized */ ++#define SFF8636_TRANS_COPPER_PAS_UNEQUAL (10 << 4) ++/* 1490 nm DFB */ ++#define SFF8636_TRANS_1490_DFB (9 << 4) ++/* Others */ ++#define SFF8636_TRANS_OTHERS (8 << 4) ++/* 1550 nm EML */ ++#define SFF8636_TRANS_1550_EML (7 << 4) ++/* 1310 nm EML */ ++#define SFF8636_TRANS_1310_EML (6 << 4) ++/* 1550 nm DFB */ ++#define SFF8636_TRANS_1550_DFB (5 << 4) ++/* 1310 nm DFB */ ++#define SFF8636_TRANS_1310_DFB (4 << 4) ++/* 1310 nm FP */ ++#define SFF8636_TRANS_1310_FP (3 << 4) ++/* 1550 nm VCSEL */ ++#define SFF8636_TRANS_1550_VCSEL (2 << 4) ++/* 1310 nm VCSEL */ ++#define SFF8636_TRANS_1310_VCSEL (1 << 4) ++/* 850 nm VCSEL */ ++#define SFF8636_TRANS_850_VCSEL (0 << 4) ++ ++ /* Active/No wavelength control */ ++#define SFF8636_DEV_TECH_ACTIVE_WAVE_LEN (1 << 3) ++/* Cooled transmitter */ ++#define SFF8636_DEV_TECH_COOL_TRANS (1 << 2) ++/* APD/Pin Detector */ ++#define SFF8636_DEV_TECH_APD_DETECTOR (1 << 1) ++/* Transmitter tunable */ ++#define SFF8636_DEV_TECH_TUNABLE (1 << 0) ++ ++/* Vendor Name - 148-163 */ ++#define SFF8636_VENDOR_NAME_START_OFFSET 0x94 ++#define SFF8636_VENDOR_NAME_END_OFFSET 0xA3 ++ ++/* Extended Module Codes - 164 */ ++#define SFF8636_EXT_MOD_CODE_OFFSET 0xA4 ++#define SFF8636_EXT_MOD_INFINIBAND_EDR (1 << 4) ++#define SFF8636_EXT_MOD_INFINIBAND_FDR (1 << 3) ++#define SFF8636_EXT_MOD_INFINIBAND_QDR (1 << 2) ++#define SFF8636_EXT_MOD_INFINIBAND_DDR (1 << 1) ++#define SFF8636_EXT_MOD_INFINIBAND_SDR (1 << 0) ++ ++/* Vendor OUI - 165-167 */ ++#define SFF8636_VENDOR_OUI_OFFSET 0xA5 ++#define SFF8636_VENDOR_OUI_LEN 3 ++ ++/* Vendor OUI - 165-167 */ ++#define SFF8636_VENDOR_PN_START_OFFSET 0xA8 ++#define SFF8636_VENDOR_PN_END_OFFSET 0xB7 ++ ++/* Vendor Revision - 184-185 */ ++#define SFF8636_VENDOR_REV_START_OFFSET 0xB8 ++#define SFF8636_VENDOR_REV_END_OFFSET 0xB9 ++ ++/* Wavelength - 186-187 */ ++#define SFF8636_WAVELEN_HIGH_BYTE_OFFSET 0xBA ++#define SFF8636_WAVELEN_LOW_BYTE_OFFSET 0xBB ++ ++/* Wavelength Tolerance- 188-189 */ ++#define SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET 0xBC ++#define SFF8636_WAVE_TOL_LOW_BYTE_OFFSET 0xBD ++ ++/* Max case temp - Other than 70 C - 190 */ ++#define SFF8636_MAXCASE_TEMP_OFFSET 0xBE ++ ++/* CC_BASE - 191 */ ++#define SFF8636_CC_BASE_OFFSET 0xBF ++ ++/* Option Values - 192-195 */ ++#define SFF8636_OPTION_1_OFFSET 0xC0 ++#define SFF8636_ETHERNET_UNSPECIFIED 0x00 ++#define SFF8636_ETHERNET_100G_AOC 0x01 ++#define SFF8636_ETHERNET_100G_SR4 0x02 ++#define SFF8636_ETHERNET_100G_LR4 0x03 ++#define SFF8636_ETHERNET_100G_ER4 0x04 ++#define SFF8636_ETHERNET_100G_SR10 0x05 ++#define SFF8636_ETHERNET_100G_CWDM4_FEC 0x06 ++#define SFF8636_ETHERNET_100G_PSM4 0x07 ++#define SFF8636_ETHERNET_100G_ACC 0x08 ++#define SFF8636_ETHERNET_100G_CWDM4_NO_FEC 0x09 ++#define SFF8636_ETHERNET_100G_RSVD1 0x0A ++#define SFF8636_ETHERNET_100G_CR4 0x0B ++#define SFF8636_ETHERNET_25G_CR_CA_S 0x0C ++#define SFF8636_ETHERNET_25G_CR_CA_N 0x0D ++#define SFF8636_ETHERNET_40G_ER4 0x10 ++#define SFF8636_ETHERNET_4X10_SR 0x11 ++#define SFF8636_ETHERNET_40G_PSM4 0x12 ++#define SFF8636_ETHERNET_G959_P1I1_2D1 0x13 ++#define SFF8636_ETHERNET_G959_P1S1_2D2 0x14 ++#define SFF8636_ETHERNET_G959_P1L1_2D2 0x15 ++#define SFF8636_ETHERNET_10GT_SFI 0x16 ++#define SFF8636_ETHERNET_100G_CLR4 0x17 ++#define SFF8636_ETHERNET_100G_AOC2 0x18 ++#define SFF8636_ETHERNET_100G_ACC2 0x19 ++ ++#define SFF8636_OPTION_2_OFFSET 0xC1 ++/* Rx output amplitude */ ++#define SFF8636_O2_RX_OUTPUT_AMP (1 << 0) ++#define SFF8636_OPTION_3_OFFSET 0xC2 ++/* Rx Squelch Disable */ ++#define SFF8636_O3_RX_SQL_DSBL (1 << 3) ++/* Rx Output Disable capable */ ++#define SFF8636_O3_RX_OUTPUT_DSBL (1 << 2) ++/* Tx Squelch Disable */ ++#define SFF8636_O3_TX_SQL_DSBL (1 << 1) ++/* Tx Squelch Impl */ ++#define SFF8636_O3_TX_SQL_IMPL (1 << 0) ++#define SFF8636_OPTION_4_OFFSET 0xC3 ++/* Memory Page 02 present */ ++#define SFF8636_O4_PAGE_02_PRESENT (1 << 7) ++/* Memory Page 01 present */ ++#define SFF8636_O4_PAGE_01_PRESENT (1 << 6) ++/* Rate Select implemented */ ++#define SFF8636_O4_RATE_SELECT (1 << 5) ++/* Tx_DISABLE implemented */ ++#define SFF8636_O4_TX_DISABLE (1 << 4) ++/* Tx_FAULT implemented */ ++#define SFF8636_O4_TX_FAULT (1 << 3) ++/* Tx Squelch implemented */ ++#define SFF8636_O4_TX_SQUELCH (1 << 2) ++/* Tx Loss of Signal */ ++#define SFF8636_O4_TX_LOS (1 << 1) ++ ++/* Vendor SN - 196-211 */ ++#define SFF8636_VENDOR_SN_START_OFFSET 0xC4 ++#define SFF8636_VENDOR_SN_END_OFFSET 0xD3 ++ ++/* Vendor Date - 212-219 */ ++#define SFF8636_DATE_YEAR_OFFSET 0xD4 ++#define SFF8636_DATE_YEAR_LEN 2 ++#define SFF8636_DATE_MONTH_OFFSET 0xD6 ++#define SFF8636_DATE_MONTH_LEN 2 ++#define SFF8636_DATE_DAY_OFFSET 0xD8 ++#define SFF8636_DATE_DAY_LEN 2 ++ ++/* Diagnostic Monitoring Type - 220 */ ++#define SFF8636_DIAG_TYPE_OFFSET 0xDC ++#define SFF8636_RX_PWR_TYPE_MASK 0x8 ++#define SFF8636_RX_PWR_TYPE_AVG_PWR (1 << 3) ++#define SFF8636_RX_PWR_TYPE_OMA (0 << 3) ++#define SFF8636_TX_PWR_TYPE_MASK 0x4 ++#define SFF8636_TX_PWR_TYPE_AVG_PWR (1 << 2) ++ ++/* Enhanced Options - 221 */ ++#define SFF8636_ENH_OPTIONS_OFFSET 0xDD ++#define SFF8636_RATE_SELECT_EXT_SUPPORT (1 << 3) ++#define SFF8636_RATE_SELECT_APP_TABLE_SUPPORT (1 << 2) ++ ++/* Check code - 223 */ ++#define SFF8636_CC_EXT_OFFSET 0xDF ++#define SFF8636_CC_EXT_LEN 1 ++ ++/*------------------------------------------------------------------------------ ++ * ++ * Upper Memory Page 03h ++ * Contains module thresholds, channel thresholds and masks, ++ * and optional channel controls ++ * ++ * Offset - Page Num(3) * PageSize(0x80) + Page offset ++ */ ++ ++/* Module Thresholds (48 Bytes) 128-175 */ ++/* MSB at low address, LSB at high address */ ++#define SFF8636_TEMP_HALRM 0x200 ++#define SFF8636_TEMP_LALRM 0x202 ++#define SFF8636_TEMP_HWARN 0x204 ++#define SFF8636_TEMP_LWARN 0x206 ++ ++#define SFF8636_VCC_HALRM 0x210 ++#define SFF8636_VCC_LALRM 0x212 ++#define SFF8636_VCC_HWARN 0x214 ++#define SFF8636_VCC_LWARN 0x216 ++ ++#define SFF8636_RX_PWR_HALRM 0x230 ++#define SFF8636_RX_PWR_LALRM 0x232 ++#define SFF8636_RX_PWR_HWARN 0x234 ++#define SFF8636_RX_PWR_LWARN 0x236 ++ ++#define SFF8636_TX_BIAS_HALRM 0x238 ++#define SFF8636_TX_BIAS_LALRM 0x23A ++#define SFF8636_TX_BIAS_HWARN 0x23C ++#define SFF8636_TX_BIAS_LWARN 0x23E ++ ++#define SFF8636_TX_PWR_HALRM 0x240 ++#define SFF8636_TX_PWR_LALRM 0x242 ++#define SFF8636_TX_PWR_HWARN 0x244 ++#define SFF8636_TX_PWR_LWARN 0x246 ++ ++#define ETH_MODULE_SFF_8636_MAX_LEN 640 ++#define ETH_MODULE_SFF_8436_MAX_LEN 640 ++ ++#endif /* QSFP_H__ */ +-- +1.8.3.1 + diff --git a/SOURCES/0006-ethtool-Enhancing-link-mode-bits-to-support-25G-50G-.patch b/SOURCES/0006-ethtool-Enhancing-link-mode-bits-to-support-25G-50G-.patch new file mode 100644 index 0000000..9bd389a --- /dev/null +++ b/SOURCES/0006-ethtool-Enhancing-link-mode-bits-to-support-25G-50G-.patch @@ -0,0 +1,68 @@ +From d1387987c21dbd4a41c1c5c3b6780b16545b19fc Mon Sep 17 00:00:00 2001 +From: Vidya Sagar Ravipati +Date: Tue, 23 Aug 2016 06:30:33 -0700 +Subject: [PATCH 6/7] ethtool: Enhancing link mode bits to support 25G/50G/100G + +Enhancing link mode bits to support 25G/50G/100G +for supported and advertised speed mode bits + +Signed-off-by: Vidya Sagar Ravipati +Acked-By: David Decotigny +Acked-By: David Decotigny +Signed-off-by: John W. Linville +(cherry picked from commit ac2f96d5f281a67ab8796badfa40f2da14033aba) +--- + ethtool.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/ethtool.c b/ethtool.c +index ffb3573..3687ab0 100644 +--- a/ethtool.c ++++ b/ethtool.c +@@ -504,6 +504,16 @@ static void init_global_link_mode_masks(void) + ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, + ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, + ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, ++ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, ++ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, ++ ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, ++ ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, ++ ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, ++ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, ++ ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, ++ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, ++ ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, ++ ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, + }; + static const enum ethtool_link_mode_bit_indices + additional_advertised_flags_bits[] = { +@@ -624,6 +634,26 @@ static void dump_link_caps(const char *prefix, const char *an_prefix, + "56000baseSR4/Full" }, + { 0, ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, + "56000baseLR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, ++ "25000baseCR/Full" }, ++ { 0, ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, ++ "25000baseKR/Full" }, ++ { 0, ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, ++ "25000baseSR/Full" }, ++ { 0, ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, ++ "50000baseCR2/Full" }, ++ { 0, ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, ++ "50000baseKR2/Full" }, ++ { 0, ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, ++ "100000baseKR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, ++ "100000baseSR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, ++ "100000baseCR4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, ++ "100000baseLR4_ER4/Full" }, ++ { 0, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, ++ "50000baseSR2/Full" }, + }; + int indent; + int did1, new_line_pend, i; +-- +1.8.3.1 + diff --git a/SOURCES/0007-ethtool-Document-new-ethtool-advertise-speeds.patch b/SOURCES/0007-ethtool-Document-new-ethtool-advertise-speeds.patch new file mode 100644 index 0000000..b98e4f3 --- /dev/null +++ b/SOURCES/0007-ethtool-Document-new-ethtool-advertise-speeds.patch @@ -0,0 +1,40 @@ +From 17e87eace95b8742252707d6719124af6e0f60c0 Mon Sep 17 00:00:00 2001 +From: Yuval Mintz +Date: Mon, 22 Aug 2016 08:50:39 +0300 +Subject: [PATCH 7/7] ethtool: Document new ethtool advertise speeds + +Signed-off-by: Yuval Mintz +Signed-off-by: John W. Linville +(cherry picked from commit 215d157b130c0a4af9bd4122fb69bc9908a22bca) +--- + ethtool.8.in | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/ethtool.8.in b/ethtool.8.in +index 009711d..6eec78d 100644 +--- a/ethtool.8.in ++++ b/ethtool.8.in +@@ -580,10 +580,20 @@ lB l lB. + 0x80000 10000baseKR Full + 0x200000 20000baseMLD2 Full (not supported by IEEE standards) + 0x400000 20000baseKR2 Full (not supported by IEEE standards) ++0x80000000 25000baseCR Full ++0x100000000 25000baseKR Full ++0x200000000 25000baseSR Full + 0x800000 40000baseKR4 Full + 0x1000000 40000baseCR4 Full + 0x2000000 40000baseSR4 Full + 0x4000000 40000baseLR4 Full ++0x400000000 50000baseCR2 Full ++0x800000000 50000baseKR2 Full ++0x10000000000 50000baseSR2 Full ++0x1000000000 100000baseKR4 Full ++0x2000000000 100000baseSR4 Full ++0x4000000000 100000baseCR4 Full ++0x8000000000 100000baseLR4_ER4 Full + .TE + .TP + .BI phyad \ N +-- +1.8.3.1 + diff --git a/SPECS/ethtool.spec b/SPECS/ethtool.spec index 8ddf0b5..31d5a78 100644 --- a/SPECS/ethtool.spec +++ b/SPECS/ethtool.spec @@ -1,7 +1,7 @@ Name: ethtool Epoch: 2 -Version: 3.15 -Release: 2%{?dist} +Version: 4.5 +Release: 3%{?dist} Summary: Settings tool for Ethernet NICs License: GPLv2 @@ -21,9 +21,13 @@ URL: http://ftp.kernel.org/pub/software/network/%{name}/ Source0: http://ftp.kernel.org/pub/software/network/%{name}/%{name}-%{version}.tar.xz BuildRequires: automake, autoconf Conflicts: filesystem < 3 -Patch0: 0001-ethtool-copy.h-sync-with-net.patch -Patch1: 0002-Support-for-configurable-RSS-hash-key.patch -Patch2: 0003-Disable-test-cases-for-rxfh-hash-key-pa.patch +Patch0: 0001-ethtool.c-fix-memory-leaks.patch +Patch1: 0002-ethtool.c-add-support-for-ETHTOOL_xLINKSETTINGS-ioctl.patch +Patch2: 0003-ethtool-copy.h-sync-with-net.patch +Patch3: 0004-ethtool-Reorganizing-SFF-8024-fields-for-SFP-QSFP.patch +Patch4: 0005-ethtool-QSFP-Plus-QSFP28-Diagnostics-Information-Sup.patch +Patch5: 0006-ethtool-Enhancing-link-mode-bits-to-support-25G-50G-.patch +Patch6: 0007-ethtool-Document-new-ethtool-advertise-speeds.patch %description This utility allows querying and changing settings such as speed, @@ -35,6 +39,10 @@ network devices, especially of Ethernet devices. %patch0 -p1 %patch1 -p1 %patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 # Only needed when using upstream git # aclocal @@ -43,6 +51,8 @@ network devices, especially of Ethernet devices. # autoconf %build +aclocal +automake --gnu --add-missing --copy %configure make %{?_smp_mflags} @@ -55,6 +65,17 @@ make DESTDIR=%{buildroot} INSTALL='install -p' install %{_mandir}/man8/%{name}.8* %changelog +* Tue Aug 16 2016 Ivan Vecera - 2:4.5-3 +- Added support for new ETHTOOL_xLINKSETTINGS API +- Added support for diagnostics information for QSFP Plus/QSFP28 modules +- Added support for 25G/50G/100G supported and advertised speed modes + +* Fri Mar 18 2016 Ivan Vecera - 2:4.5-2 +- Fixed several memory leaks + +* Wed Mar 16 2016 Ivan Vecera - 2:4.5-1 +- Update to the upstream version v4.5 + * Fri Oct 31 2014 Ivan Vecera - 2:3.15-2 - Support for configurable RSS hash key