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 <ben@decadent.org.uk>
-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 <ben@decadent.org.uk>
-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 <ben@decadent.org.uk>
----
- 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 <linux/types.h>
- #include <linux/if_ether.h>
- 
--/* 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 <ivecera@redhat.com>
+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 <ivecera@redhat.com>
+---
+ 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 <VenkatKumar.Duvvuru@Emulex.com>
-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 <VenkatKumar.Duvvuru@Emulex.com>
----
- 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 <decot@googlers.com>
+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 <decot@googlers.com>
+[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 <ben@decadent.org.uk>
+
+(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 <stdbool.h>
+ #include <stdio.h>
+ #include <stdint.h>
+ #include <stdlib.h>
+@@ -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 <ben@decadent.org.uk>
-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 <ben@decadent.org.uk>
----
- 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 <vidya@cumulusnetworks.com>
+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 <galp@mellanox.com>
+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 <galp@mellanox.com>
+    Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
+    Cc: Ben Hutchings <bwh@kernel.org>
+    Cc: David Decotigny <decot@googlers.com>
+    Acked-By: David Decotigny <decot@googlers.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+(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 <vidya@cumulusnetworks.com>
+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 <vidya@cumulusnetworks.com>
+Acked-by: Bert Kenward <bkenward@solarflare.com>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+(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 <aurelien@iwi.me> (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 <vidya@cumulusnetworks.com>
++ *   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 <stdio.h>
++#include <math.h>
++#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 <aurelien@iwi.me> (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 <vidya@cumulusnetworks.com>
++ *   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 <stdio.h>
++#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 <math.h>
+ #include <arpa/inet.h>
+ #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 <stdio.h>
+ #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 <vidya@cumulusnetworks.com>
+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 <vidya@cumulusnetworks.com>
+Acked-by: Bert Kenward <bkenward@solarflare.com>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+(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 <aurelien@iwi.me> (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 <vidya@cumulusnetworks.com>
++ *   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 <stdio.h>
++#include <math.h>
++#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 <vidya@cumulusnetworks.com>
++ *
++ * 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 <vidya@cumulusnetworks.com>
+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 <vidya@cumulusnetworks.com>
+Acked-By: David Decotigny <decot@googlers.com>
+Acked-By: David Decotigny <decot@googlers.com>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+(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 <Yuval.Mintz@qlogic.com>
+Date: Mon, 22 Aug 2016 08:50:39 +0300
+Subject: [PATCH 7/7] ethtool: Document new ethtool advertise speeds
+
+Signed-off-by: Yuval Mintz <Yuval.Mintz@qlogic.com>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+(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 <ivecera@redhat.com> - 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 <ivecera@redhat.com> - 2:4.5-2
+- Fixed several memory leaks
+
+* Wed Mar 16 2016 Ivan Vecera <ivecera@redhat.com> - 2:4.5-1
+- Update to the upstream version v4.5
+
 * Fri Oct 31 2014 Ivan Vecera <ivecera@redhat.com> - 2:3.15-2
 - Support for configurable RSS hash key