From 9efed0a996f30e1f2d2d33611b209fee6faabc2c Mon Sep 17 00:00:00 2001
From: Ajit Khaparde <ajitk@serverengines.com>
Date: Thu, 3 Sep 2009 08:33:08 +0530
Subject: [PATCH] ethtool: Add option to flash firmware image from specified file, to a device.

This patch adds a new "-f" option to the ethtool utility to flash a
firmware image specified by a file, to a network device.  The filename
is passed to the network driver which will flash the image on the
chip using the request_firmware path.

The region "on the chip" to be flashed can be specified by an option.
It is up to the device driver to enumerate the region number passed
by ethtool, to the region to be flashed.

The default behavior is to flash all the regions on the chip.

Usage:
ethtool -f <interface name> <filename of firmware image>

ethtool -f <interface name> <filename of firmware image> [ REGION-NUMBER-TO-FLASH ]

Signed-off-by: Ajit Khaparde <ajitk@serverengines.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
---
 ethtool-copy.h |   13 +++++++++++++
 ethtool.8      |   14 ++++++++++++++
 ethtool.c      |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 81 insertions(+), 1 deletion(-)

Index: ethtool-6/ethtool-copy.h
===================================================================
--- ethtool-6.orig/ethtool-copy.h
+++ ethtool-6/ethtool-copy.h
@@ -291,6 +291,18 @@ struct ethtool_rxnfc {
 	__u64		data;
 };
 
+#define ETHTOOL_FLASH_MAX_FILENAME	128
+enum ethtool_flash_op_type {
+	ETHTOOL_FLASH_ALL_REGIONS	= 0,
+};
+
+/* for passing firmware flashing related parameters */
+struct ethtool_flash {
+	__u32	cmd;
+	__u32	region;
+	char	data[ETHTOOL_FLASH_MAX_FILENAME];
+};
+
 /* CMDs currently supported */
 #define ETHTOOL_GSET		0x00000001 /* Get settings. */
 #define ETHTOOL_SSET		0x00000002 /* Set settings. */
@@ -336,6 +348,7 @@ struct ethtool_rxnfc {
 
 #define	ETHTOOL_GRXFH		0x00000029 /* Get RX flow hash configuration */
 #define	ETHTOOL_SRXFH		0x0000002a /* Set RX flow hash configuration */
+#define	ETHTOOL_FLASHDEV	0x00000033 /* Flash firmware to device */
 
 /* compatibility with older code */
 #define SPARC_ETH_GSET		ETHTOOL_GSET
Index: ethtool-6/ethtool.8
===================================================================
--- ethtool-6.orig/ethtool.8
+++ ethtool-6/ethtool.8
@@ -188,6 +188,10 @@ ethtool \- Display or change ethernet ca
 .RB [ sopass \ \*(MA]
 .RB [ msglvl
 .IR N ]
+.B ethtool \-f|\-\-flash
+.I ethX
+.RI FILE
+.RI [ N ]
 .SH DESCRIPTION
 .BI ethtool
 is used for querying settings of an ethernet device and changing them.
@@ -413,6 +417,16 @@ bytes in ethernet MAC hex format (\*(MA)
 .TP
 .BI msglvl \ N
 Sets the driver message level. Meanings differ per driver.
+.B \-f \-\-flash \ FILE
+Flash firmware image from the specified file to a region on the adapter.
+By default this will flash all the regions on the adapter.
+.TP
+.B N
+A number to identify flash region where the image should be flashed.
+Default region is 0 which denotes all regions in the flash.
+.PD
+.RE
+.TP
 .SH BUGS
 Not supported (in part or whole) on all ethernet drivers.
 .SH AUTHOR
Index: ethtool-6/ethtool.c
===================================================================
--- ethtool-6.orig/ethtool.c
+++ ethtool-6/ethtool.c
@@ -69,6 +69,7 @@ static int do_scoalesce(int fd, struct i
 static int do_goffload(int fd, struct ifreq *ifr);
 static int do_soffload(int fd, struct ifreq *ifr);
 static int do_gstats(int fd, struct ifreq *ifr);
+static int do_flash(int fd, struct ifreq *ifr);
 
 static enum {
 	MODE_HELP = -1,
@@ -90,6 +91,7 @@ static enum {
 	MODE_GOFFLOAD,
 	MODE_SOFFLOAD,
 	MODE_GSTATS,
+	MODE_FLASHDEV,
 } mode = MODE_GSET;
 
 static struct option {
@@ -172,6 +174,9 @@ static struct option {
     { "-t", "--test", MODE_TEST, "Execute adapter self test",
                 "               [ online | offline ]\n" },
     { "-S", "--statistics", MODE_GSTATS, "Show adapter statistics" },
+    { "-f", "--flash", MODE_FLASHDEV, "FILENAME " "Flash firmware image "
+    		"from the specified file to a region on the device",
+		"               [ REGION-NUMBER-TO-FLASH ]\n" },
     { "-h", "--help", MODE_HELP, "Show this help" },
     {}
 };
@@ -269,6 +274,9 @@ static int seeprom_changed = 0;
 static int seeprom_magic = 0;
 static int seeprom_offset = -1;
 static int seeprom_value = 0;
+static char *flash_file = NULL;
+static int flash = -1;
+static int flash_region = -1;
 static enum {
 	ONLINE=0,
 	OFFLINE,
@@ -434,7 +442,8 @@ static void parse_cmdline(int argc, char
 			    (mode == MODE_GOFFLOAD) ||
 			    (mode == MODE_SOFFLOAD) ||
 			    (mode == MODE_GSTATS) ||
-			    (mode == MODE_PHYS_ID)) {
+			    (mode == MODE_PHYS_ID) ||
+			    (mode == MODE_FLASHDEV)) {
 				devname = argp[i];
 				break;
 			}
@@ -454,6 +463,10 @@ static void parse_cmdline(int argc, char
 				if (phys_id_time < 0)
 					show_usage(1);
 				break;
+			} else if (mode == MODE_FLASHDEV) {
+				flash_file = argp[i];
+				flash = 1;
+				break;
 			}
 			/* fallthrough */
 		default:
@@ -513,6 +526,12 @@ static void parse_cmdline(int argc, char
 				i = argc;
 				break;
 			}
+			if (mode == MODE_FLASHDEV) {
+				flash_region = strtol(argp[i], NULL, 0);
+				if ((flash_region < 0))
+					show_usage(1);
+				break;
+			}
 			if (mode != MODE_SSET)
 				show_usage(1);
 			if (!strcmp(argp[i], "speed")) {
@@ -1285,6 +1304,8 @@ static int doit(void)
 		return do_soffload(fd, &ifr);
 	} else if (mode == MODE_GSTATS) {
 		return do_gstats(fd, &ifr);
+	} else if (mode == MODE_FLASHDEV) {
+		return do_flash(fd, &ifr);
 	}
 
 	return 69;
@@ -2116,6 +2137,38 @@ static int do_gstats(int fd, struct ifre
 	return 0;
 }
 
+static int do_flash(int fd, struct ifreq *ifr)
+{
+	struct ethtool_flash efl;
+	int err;
+
+	if (flash < 0) {
+		fprintf(stdout, "Missing filename argument\n");
+		show_usage(1);
+		return 98;
+	}
+
+	if (strlen(flash_file) > ETHTOOL_FLASH_MAX_FILENAME - 1) {
+		fprintf(stdout, "Filename too long\n");
+		return 99;
+	}
+
+	efl.cmd = ETHTOOL_FLASHDEV;
+	strcpy(efl.data, flash_file);
+
+	if (flash_region < 0)
+		efl.region = ETHTOOL_FLASH_ALL_REGIONS;
+	else
+		efl.region = flash_region;
+
+	ifr->ifr_data = (caddr_t)&efl;
+	err = ioctl(fd, SIOCETHTOOL, ifr);
+	if (err < 0)
+		perror("Flashing failed");
+
+	return err;
+}
+
 int main(int argc, char **argp, char **envp)
 {
 	parse_cmdline(argc, argp);
