Field Firmware Update feature is new for 5.0 spec.
Code written per JESD84-B50.pdf spec available from:
    
http://www.jedec.org/standards-documents/technology-focus-areas/flash-memory-ssds-ufs-emmc/e-mmc

Change-Id: I0a22e9862876421630f53ac27fc0a161a9c70131
Signed-off-by: Grant Grundler <grund...@chromium.org>
---
V3:
- reference FFU_ARG instead of hardcoding 0x0000ffff
- Set MODE_CONFIG[30] to 1 as part of CMD6
- fixed sequence numbers in comments
- fix compile warning.
- Still missing support for MODE_OPERATION_CODES

This version can be cherry-picked into local branch with:
git fetch https://chromium.googlesource.com/chromiumos/third_party/mmc-utils 
refs/changes/71/185471/1 && git cherry-pick FETCH_HEAD

Gwendal Grignou has added eMMC 5.0 support to "mmc extcsd read" and I
don't see that in Chris' mmc-utils tree.  Might want to first grab:
    "Decode EXT_CSD of eMMC 5.0 device"
    git fetch 
https://chromium.googlesource.com/chromiumos/third_party/mmc-utils 
refs/changes/75/184175/5 && git cherry-pick FETCH_HEAD

V2:
- Fix compiler borkage in use of strncmp().
- Use '\0' instead of 0 in char assignment.

V1:
This patch needs to be reviewed and tested by _ANY_ eMMC 5.0 HW vendor.

If you represent an eMMC 5.0 HW vendor and expect your part will be used
in a ChromeOS device, make sure FFU works with mmc-utils.
...

 .gitignore       |   2 +
 Makefile         |   4 +-
 mmc.c            |  31 ++++++--
 mmc.h            |  29 +++++++-
 mmc_cmds-emmc5.c | 140 +++++++++++++++++++++++++++++++++++++
 mmc_cmds.c       | 210 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 mmc_cmds.h       |   6 ++
 7 files changed, 387 insertions(+), 35 deletions(-)
 create mode 100644 mmc_cmds-emmc5.c

diff --git a/.gitignore b/.gitignore
index 5a94d47..1de13cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
 .mmc.o.d
 .mmc_cmds.o.d
+.mmc_cmds-emmc5.o.d
 mmc
 mmc.o
 mmc_cmds.o
+mmc_cmds-emmc5.o
diff --git a/Makefile b/Makefile
index 91cfc35..1cdd97f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 CC ?= gcc
 AM_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2
 CFLAGS ?= -g -O2
-objects = mmc.o mmc_cmds.o
+objects = mmc.o mmc_cmds.o mmc_cmds-emmc5.o
 
 CHECKFLAGS = -Wall -Werror -Wuninitialized -Wundef
 
@@ -44,7 +44,7 @@ clean:
        $(MAKE) -C man clean
 
 install: $(progs) install-man
-       $(INSTALL) -m755 -d $(DESTDIR)$(bindir)
+       $(INSTALL) -m 755 -d $(DESTDIR)$(bindir)
        $(INSTALL) $(progs) $(DESTDIR)$(bindir)
 
 .PHONY: all clean install manpages install-man
diff --git a/mmc.c b/mmc.c
index 926e92f..01a5074 100644
--- a/mmc.c
+++ b/mmc.c
@@ -20,7 +20,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
+#include <linux/types.h>
 
+#include "mmc.h"
 #include "mmc_cmds.h"
 
 #define MMC_VERSION    "0.1"
@@ -37,9 +40,9 @@ struct Command {
                                   if < 0, _minimum_ number of arguments */
        char    *verb;          /* verb */
        char    *help;          /* help lines; from the 2nd line onward they 
-                                   are automatically indented */
-        char    *adv_help;      /* advanced help message; from the 2nd line 
-                                   onward they are automatically indented */
+                                  are automatically indented */
+       char    *adv_help;      /* advanced help message; from the 2nd line
+                                  onward they are automatically indented */
 
        /* the following fields are run-time filled by the program */
        char    **cmds;         /* array of subcommands */
@@ -110,9 +113,24 @@ static struct Command commands[] = {
                "Send Sanitize command to the <device>.\nThis will delete the 
unmapped memory region of the device.",
          NULL
        },
+       { do_firmware_update, -2,
+         "firmware upload", "<firmware_file> " "<device>\n"
+               "Upload/Update firmware on <device> using <firmware_file>.\n",
+         NULL
+       },
        { 0, 0, 0, 0 }
 };
 
+const char *manfid_lookup[0x100] = {
+       [MANFID_PANASONIC] = "Panasonic",
+       [MANFID_KINGSTON] = "Kingston",
+       [MANFID_SANDISK] = "Sandisk",
+       [MANFID_SAMSUNG] = "Samsung",
+       [MANFID_TOSHIBA] = "Toshiba",
+       [MANFID_SANDISK_SEM] = "Sandisk_SEM",
+       [MANFID_KINGSTON_MMC] = "Kingston_MMC"
+};
+
 static char *get_prgname(char *programname)
 {
        char    *np;
@@ -363,11 +381,10 @@ static int parse_args(int argc, char **argv,
                        return -2;
        }
        
-        if (prepare_args( nargs_, args_, prgname, matchcmd )){
-                fprintf(stderr, "ERROR: not enough memory\\n");
+       if (prepare_args( nargs_, args_, prgname, matchcmd )){
+               fprintf(stderr, "ERROR: not enough memory\\n");
                return -20;
-        }
-
+       }
 
        return 1;
 }
diff --git a/mmc.h b/mmc.h
index 9871d62..61c0303 100644
--- a/mmc.h
+++ b/mmc.h
@@ -24,12 +24,25 @@
 #define MMC_BLOCK_MAJOR                        179
 
 /* From kernel linux/mmc/mmc.h */
+#define MMC_GO_IDLE_STATE      0       /* bc                               */
+#define MMC_ALL_SEND_CID       2       /* bcr                          R2  */
 #define MMC_SWITCH             6       /* ac   [31:0] See below        R1b */
+#define MMC_SELECT_CARD                7       /* ac   [31:16] RCA             
R1  */
 #define MMC_SEND_EXT_CSD       8       /* adtc                         R1  */
-#define MMC_SEND_STATUS                13      /* ac   [31:16] RCA        R1  
*/
+#define MMC_SEND_CID           10      /* ac   [31:16] RCA             R2  */
+#define MMC_STOP_TRANSMISSION  12      /* ac                           R1b */
+#define MMC_SEND_STATUS                13      /* ac   [31:16] RCA             
R1  */
+#define MMC_READ_SINGLE_BLOCK  17      /* adtc [31:0] data addr        R1  */
+#define MMC_READ_MULTIPLE_BLOCK        18      /* adtc [31:0] data addr        
R1  */
+#define MMC_WRITE_BLOCK                24      /* adtc [31:0] data addr        
R1  */
+#define MMC_WRITE_MULTIPLE_BLOCK 25    /* adtc                         R1  */
+
+#define MMC_GEN_CMD            56      /* adtc [0] RD/WR               R1  */
+
 #define R1_SWITCH_ERROR   (1 << 7)  /* sx, c */
 #define MMC_SWITCH_MODE_WRITE_BYTE     0x03    /* Set target to value */
 
+
 /*
  * EXT_CSD fields
  */
@@ -125,3 +138,17 @@
 
 #define MMC_RSP_R1     (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 #define MMC_RSP_R1B    
(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
+
+
+/* Manufacturer IDs as shown by /sys/block/mmcblk0/device/manfid */
+#define MANFID_PANASONIC       0x000001
+#define MANFID_KINGSTON                0x000002
+#define MANFID_SANDISK         0x000003
+#define MANFID_SAMSUNG         0x000015
+#define MANFID_TOSHIBA         0x00001d
+#define MANFID_SANDISK_SEM     0x000045        /* seen with "SEM16G" */
+#define MANFID_KINGSTON_MMC    0x000070        /* Seen on embedded device */
+
+#if 0
+const char *manfid_lookup[];
+#endif
diff --git a/mmc_cmds-emmc5.c b/mmc_cmds-emmc5.c
new file mode 100644
index 0000000..155fcad
--- /dev/null
+++ b/mmc_cmds-emmc5.c
@@ -0,0 +1,140 @@
+/*
+ * eMMC 5.0+ Specific tools
+ *
+ * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>    /* for memset() */
+#include <errno.h>
+#include <linux/types.h>
+#include <sys/ioctl.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "mmc.h"
+#include "mmc_cmds.h"
+
+
+/* ext_csd[] offsets specific to FFU */
+#define FFU_STATUS     26
+#define FFU_ARG                487
+#define FFU_FEATURES   492
+#define SUPPORTED_MODES        493
+
+int do_emmc5_fw_update(int fd, __u8 *cid, __u8 *ext_csd,
+                               char *emmc_fw, size_t fwsize)
+{
+       struct mmc_ioc_cmd idata;
+       int retcode = 0;
+       int ret;
+
+       /* 1) Confirm device supports FFU */
+       if ((ext_csd[SUPPORTED_MODES] & 1) == 0) {
+               fprintf(stderr, "eMMC 5.0 FFU not supported\n");
+               return -EINVAL;
+       }
+
+       /* 2) Host send CMD6 to set MODE_CONFIG[30] = 0x01 */
+       memset(&idata, 0, sizeof(idata));
+       idata.opcode = MMC_SWITCH;
+        idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+                       (30 << 16) |    /* index */
+                       (1 << 8) |      /* value */
+                       EXT_CSD_CMD_SET_NORMAL;
+       idata.flags = MMC_RSP_R1B | MMC_CMD_AC;;
+       ret = ioctl(fd, MMC_IOC_CMD, &idata);
+       if (ret) {
+               retcode = ret;
+               printf("ioctl: MMC_SWITCH (eMMC 5.0 FW Update) %m\n");
+               goto abort_update;
+       }
+
+       /* 3) send CMD25 with FFU_ARG */
+       memset(&idata, 0, sizeof(idata));
+       idata.write_flag = 1;
+       idata.data_ptr = (__u64) ((unsigned long) emmc_fw); /* Write this FW */
+       idata.opcode = MMC_WRITE_MULTIPLE_BLOCK;
+       idata.arg = le32toh( *((__u32 *) &ext_csd[FFU_ARG]));
+       idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;;
+       ret = ioctl(fd, MMC_IOC_CMD, &idata);
+       if (ret) {
+               retcode = ret;
+               printf("ioctl:MMC_WRITE_MULTIPLE_BLOCK (eMMC 5.0 FW Update) 
%m\n");
+               goto abort_update;
+       }
+
+       /* 3) check FFU_STATUS[26] */
+       ret = read_extcsd(fd, ext_csd);
+       if (ret) {
+               retcode = ret;
+               fprintf(stderr, "read_extcsd error (%d): %m\n", ret);
+               goto abort_update;
+       }
+
+       switch(ext_csd[FFU_STATUS]) {
+       case 0:  break;
+       case 0x10:
+               fprintf(stderr, "eMMC 5.0 FFU had general error and failed 
(EIO).\n");
+               retcode = -EIO;
+               break;
+       case 0x11:
+               fprintf(stderr, "eMMC 5.0 FFU did not complete (EAGAIN).\n");
+               retcode = -EAGAIN;
+               break;
+       case 0x12:
+               fprintf(stderr, "eMMC 5.0 FFU download failed: checksum "
+                       "mismatch or was interrupted (EINTR).\n");
+               retcode = -EINTR;
+               break;
+       default:
+               fprintf(stderr, "eMMC 5.0 FFU unknown error: %x\n", 
ext_csd[FFU_STATUS]);
+               retcode = -EIO;
+       }
+
+       /* 4) check NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED[305-302] */
+       ret = ext_csd[302] << 0 | (ext_csd[303] << 8) |
+               (ext_csd[304] << 16) | (ext_csd[305] << 24);
+
+       /* convert that gibberish to bytes */
+       ret *= 512;
+       if (ext_csd[61] == 1)
+               ret *= 8;       /* 4K sectors */
+
+       if (ret != fwsize) {
+               fprintf(stderr, "eMMC 5.0 FFU Failed? (fwsize: %zu,"
+                       " PROGRAMMED_SECTORS: %d)\n", fwsize, ret);
+       }
+
+abort_update:
+       /* 4) send CMD0
+        * This should reset the device and force use of the new firmware.
+        */
+       memset(&idata, 0, sizeof(idata));
+       idata.opcode = MMC_GO_IDLE_STATE;
+       ret = ioctl(fd, MMC_IOC_CMD, &idata);
+       if (ret) {
+               retcode = ret;
+               perror("ioctl:MMC_GO_IDLE_STATE (eMMC 5.0 FFU)");
+       }
+
+       return retcode;
+}
diff --git a/mmc_cmds.c b/mmc_cmds.c
index 4b9b12e..6673837 100644
--- a/mmc_cmds.c
+++ b/mmc_cmds.c
@@ -1,3 +1,4 @@
+
 /*
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
@@ -17,9 +18,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
+#include <errno.h>
 #include <dirent.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -30,23 +32,27 @@
 #include "mmc.h"
 #include "mmc_cmds.h"
 
+#define EXT_CSD_SIZE   512
+#define CID_SIZE 16
+
+
 int read_extcsd(int fd, __u8 *ext_csd)
 {
        int ret = 0;
        struct mmc_ioc_cmd idata;
        memset(&idata, 0, sizeof(idata));
-       memset(ext_csd, 0, sizeof(__u8) * 512);
+       memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
        idata.write_flag = 0;
        idata.opcode = MMC_SEND_EXT_CSD;
        idata.arg = 0;
        idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
-       idata.blksz = 512;
+       idata.blksz = EXT_CSD_SIZE;
        idata.blocks = 1;
        mmc_ioc_cmd_set_data(idata, ext_csd);
 
        ret = ioctl(fd, MMC_IOC_CMD, &idata);
        if (ret)
-               perror("ioctl");
+               perror("ioctl SEND_EXT_CSD");
 
        return ret;
 }
@@ -67,7 +73,30 @@ int write_extcsd_value(int fd, __u8 index, __u8 value)
 
        ret = ioctl(fd, MMC_IOC_CMD, &idata);
        if (ret)
-               perror("ioctl");
+               perror("ioctl Write EXT CSD");
+
+       return ret;
+}
+
+int read_cid(int fd, __u8 *cid)
+{
+       int ret = 0;
+       struct mmc_ioc_cmd idata;
+
+       memset(&idata, 0, sizeof(idata));
+       memset(cid, 0, sizeof(__u8) * CID_SIZE);
+
+       idata.write_flag = 0;
+       idata.opcode = MMC_SEND_CID;
+       idata.arg    = 0;
+       idata.flags  = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+       idata.blksz  = CID_SIZE;
+       idata.blocks = 1;
+       mmc_ioc_cmd_set_data(idata, cid);
+
+       ret = ioctl(fd, MMC_IOC_CMD, &idata);
+       if (ret)
+               perror("ioctl SEND_CID");
 
        return ret;
 }
@@ -103,17 +132,11 @@ void print_writeprotect_status(__u8 *ext_csd)
 
                reg = ext_csd[EXT_CSD_BOOT_WP];
                printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
-               printf(" Power ro locking: ");
-               if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
-                       printf("not possible\n");
-               else
-                       printf("possible\n");
+               printf(" Power ro locking: %spossible\n",
+                       (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS) ? "not " : "");
 
-               printf(" Permanent ro locking: ");
-               if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
-                       printf("not possible\n");
-               else
-                       printf("possible\n");
+               printf(" Permanent ro locking: %spossible\n",
+                       (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS) ? "not " : "");
 
                printf(" ro lock status: ");
                if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
@@ -127,7 +150,7 @@ void print_writeprotect_status(__u8 *ext_csd)
 
 int do_writeprotect_get(int nargs, char **argv)
 {
-       __u8 ext_csd[512];
+       __u8 ext_csd[EXT_CSD_SIZE];
        int fd, ret;
        char *device;
 
@@ -155,7 +178,7 @@ int do_writeprotect_get(int nargs, char **argv)
 
 int do_writeprotect_set(int nargs, char **argv)
 {
-       __u8 ext_csd[512], value;
+       __u8 ext_csd[EXT_CSD_SIZE], value;
        int fd, ret;
        char *device;
 
@@ -191,7 +214,7 @@ int do_writeprotect_set(int nargs, char **argv)
 
 int do_disable_512B_emulation(int nargs, char **argv)
 {
-       __u8 ext_csd[512], native_sector_size, data_sector_size, wr_rel_param;
+       __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, 
wr_rel_param;
        int fd, ret;
        char *device;
 
@@ -301,7 +324,7 @@ int do_write_boot_en(int nargs, char **argv)
 
 int do_hwreset(int value, int nargs, char **argv)
 {
-       __u8 ext_csd[512];
+       __u8 ext_csd[EXT_CSD_SIZE];
        int fd, ret;
        char *device;
 
@@ -360,7 +383,7 @@ int do_hwreset_dis(int nargs, char **argv)
 
 int do_write_bkops_en(int nargs, char **argv)
 {
-       __u8 ext_csd[512], value = 0;
+       __u8 ext_csd[EXT_CSD_SIZE], value = 0;
        int fd, ret;
        char *device;
 
@@ -502,7 +525,7 @@ int set_partitioning_setting_completed(int dry_run, const 
char * const device,
 int do_enh_area_set(int nargs, char **argv)
 {
        __u8 value;
-       __u8 ext_csd[512];
+       __u8 ext_csd[EXT_CSD_SIZE];
        int fd, ret;
        char *device;
        int dry_run = 1;
@@ -640,7 +663,7 @@ int do_enh_area_set(int nargs, char **argv)
 int do_write_reliability_set(int nargs, char **argv)
 {
        __u8 value;
-       __u8 ext_csd[512];
+       __u8 ext_csd[EXT_CSD_SIZE];
        int fd, ret;
 
        int dry_run = 1;
@@ -701,7 +724,7 @@ int do_write_reliability_set(int nargs, char **argv)
 
 int do_read_extcsd(int nargs, char **argv)
 {
-       __u8 ext_csd[512], ext_csd_rev, reg;
+       __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
        __u32 regl;
        int fd, ret;
        char *device;
@@ -1218,6 +1241,143 @@ int do_sanitize(int nargs, char **argv)
        }
 
        return ret;
-
 }
 
+int do_firmware_update(int nargs, char **argv)
+{
+       char *emmc_fw;
+       char *device;
+       char *fwfilename;
+       struct stat fwfilestat;
+       size_t fwsize;
+       int ret = 0;
+       int devfd,fwfd;
+       __u8 ext_csd[EXT_CSD_SIZE];
+       __u8 cid[CID_SIZE];
+       __u8 ext_csd_rev;
+
+
+       CHECK(nargs != 4, "Usage: mmc firmware update"
+                       " --I_want_to_destroy_my_drive"
+                       " </path/to/mmcblkX> </path/to/FW.bin>\n",
+                         exit(1));
+
+
+       /* Lesson from hdparm: user must be aware of the risks
+        * Key here is the additional command line flag be UNDOCUMENTED.
+        * User _must_ read and KNOW this is risky at runtime.
+        */
+       if (strncmp(argv[4], "--I_want_to_destroy_my_drive", 28)) {
+               fprintf(stderr,"ERROR: Please specify 
--I_want_to_destroy_my_drive"
+               " as first parameter to firmware update.\n");
+
+               exit(1);
+       }
+
+       emmc_fw = malloc(MMC_IOC_MAX_BYTES);
+       if (!emmc_fw)
+               return -ENOMEM;
+
+       device = argv[5];
+
+       devfd = open(device, O_RDWR);
+       if (devfd < 0) {
+               fprintf(stderr,"ERROR: open %s: %m\n", device); /* %m = errno */
+               goto out_free;
+       }
+
+       /* 1) read device version and attributes */
+       ret = read_extcsd(devfd, ext_csd);
+       if (ret) {
+               fprintf(stderr, "ERROR: Read EXT_CSD from %s: %m\n", device);
+               goto out_dev;
+       }
+
+       ext_csd_rev = ext_csd[192];
+
+       if (ext_csd_rev < 7) {
+               fprintf(stderr, "ERROR: Can not update firmware"
+                       ": %s is pre-emmc 5.0 vintage\n", device);
+               goto out_dev;
+       }
+
+       printf("%s: FW is currently %c%c%c%c%c%c%c%c\n",
+               device,
+               ext_csd[254], ext_csd[255], ext_csd[256], ext_csd[257],
+               ext_csd[258], ext_csd[259], ext_csd[260], ext_csd[261]
+               );
+
+       /* 2) confirm SUPPORTED_MODES has FFU bit set */
+       if (!(ext_csd[493] & 1)) {
+               fprintf(stderr, "ERROR: %s is eMMC 5.0 device"
+                       "but does not support FFU.\n", device);
+               goto out_dev;
+       }
+
+       /* 3) confirm FW updated is NOT disabled on this device */
+       if (ext_csd[169] & 1) {
+               fprintf(stderr, "ERROR: %s is eMMC 5.0 device"
+                       "but FFU is disabled.\n", device);
+               goto out_dev;
+       }
+
+       /* 4) read the device manfid */
+       ret = read_cid(devfd, cid);
+       if (ret) {
+               fprintf(stderr, "ERROR: Read EXT_CSD from %s: %m\n", device);
+               goto out_dev;
+       }
+
+       /* 5) Fetch the FW image */
+       fwfilename = argv[2];
+
+       fwfd = open(fwfilename, O_RDONLY);
+       if (fwfd < 0) {
+               /* %m = errno */
+               fprintf(stderr,"ERROR: open %s: %m", fwfilename);
+               goto out_dev;
+       }
+
+       ret = fstat(fwfd, &fwfilestat);
+       if (ret) {
+               /* %m = errno */
+               fprintf(stderr,"ERROR: fstat %s: %m", fwfilename);
+               goto out_fw;
+       }
+
+       fwsize = fwfilestat.st_size;
+       if (fwsize > MMC_IOC_MAX_BYTES) {
+               fprintf(stderr,"ERROR: %s is > %ld bytes long (max allowed)\n",
+                       fwfilename, MMC_IOC_MAX_BYTES);
+               goto out_fw;
+       }
+
+       ret = read(fwfd, emmc_fw, fwsize);
+       if (ret < fwsize) {
+               fprintf(stderr,"ERROR: did not read all %zu bytes of %s"
+                               "(%d bytes long)\n",
+                       fwsize, fwfilename, ret);
+               goto out_fw;
+       }
+
+       ret = do_emmc5_fw_update(devfd, cid, ext_csd, emmc_fw, fwsize);
+       if (ret) {
+               fprintf(stderr,"ERROR: emmc5_fw_update failed for %s: %d", 
fwfilename, ret);
+               goto out_fw;
+       }
+       cid[13] = '\0'; /* make sure string is NULL terminated */
+
+       printf("%s: FW updated to %8s (0x%2x%2x%2x%2x%2x%2x%2x%2x)\n",
+               device, &(ext_csd[254]),
+               ext_csd[254], ext_csd[255], ext_csd[256], ext_csd[257],
+               ext_csd[258], ext_csd[259], ext_csd[260], ext_csd[261]
+               );
+
+out_fw:
+       close(fwfd);
+out_dev:
+       close(devfd);
+out_free:
+       free(emmc_fw);
+       return ret;
+}
diff --git a/mmc_cmds.h b/mmc_cmds.h
index f06cc10..549b851 100644
--- a/mmc_cmds.h
+++ b/mmc_cmds.h
@@ -28,3 +28,9 @@ int do_sanitize(int nargs, char **argv);
 int do_status_get(int nargs, char **argv);
 int do_enh_area_set(int nargs, char **argv);
 int do_write_reliability_set(int nargs, char **argv);
+int do_firmware_update(int nargs, char **argv);
+
+int read_extcsd(int fd, __u8 *ext_csd);
+int write_extcsd_value(int fd, __u8 index, __u8 value);
+
+int do_emmc5_fw_update(int devfd, __u8 *cid, __u8 *ext_csd, char *emmc_fw, 
size_t fwsize);
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to