Message ID | SN1PR02MB13896AB22521C993B24F90A283DF0@SN1PR02MB1389.namprd02.prod.outlook.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Ping... Hi Chris, Could you please review the patch? Thanks, Alex > -----Original Message----- > From: Avi Shchislowski > Sent: Tuesday, February 02, 2016 6:12 PM > To: Chris Ball; Ulf Hansson > Cc: linux-mmc@vger.kernel.org; Alex Lemberg; Yaniv Agman > Subject: [PATCH 1/1] mmc_utils: add ffu support > > Adding support for field firmware update over multiple command ioctl. > As multiple command ioctl is supported only from kernel 4.4, this patch > should be used against kernel 4.4 and above. > > Known issues: > - There is no support for Multiple Block write commands (CMD25) in existing > IOCTL implementation > - In case MODE_OPERATION_CODES field is not supported by the device > manual reset of the device/platform is required. > The reset issue discussed in another email thread - " [RFC 0/6] mmc: Field > Firmware Update" > > Signed-off-by: Yaniv Agman <yaniv.agman@sandisk.com> > Signed-off-by: Avi Shchislowski <avi.shchislowski@sandisk.com> > > diff --git a/mmc.c b/mmc.c > index a13d9ae..b1a8b3a 100644 > --- a/mmc.c > +++ b/mmc.c > @@ -13,6 +13,9 @@ > * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > * Boston, MA 021110-1307, USA. > * > + * Modified Jan. 2016 by SanDisk Corp. > + * Copyright (c) 2016 SanDisk Corp. > + * > * (This code is based on btrfs-progs/btrfs.c.) > */ > > @@ -175,6 +178,11 @@ static struct Command commands[] = { > "NOTE! The cache is an optional feature on devices >= > eMMC4.5.", > NULL > }, > + { do_ffu, -2, > + "ffu", "<image name> <device>\n" > + "Run Field Firmware Update with <image name> on > <device>.\n", > + NULL > + }, > { 0, 0, 0, 0 } > }; > > diff --git a/mmc.h b/mmc.h > index b7063fb..9de8bcc 100644 > --- a/mmc.h > +++ b/mmc.h > @@ -12,6 +12,9 @@ > * License along with this program; if not, write to the > * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > * Boston, MA 021110-1307, USA. > + * > + * Modified Jan. 2016 by SanDisk Corp. > + * Copyright (c) 2016 SanDisk Corp. > */ > > #include <asm-generic/int-ll64.h> > @@ -34,6 +37,7 @@ > #define R1_SWITCH_ERROR (1 << 7) /* sx, c */ > #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value > */ > #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 */ > > /* > @@ -42,6 +46,17 @@ > #define EXT_CSD_S_CMD_SET 504 > #define EXT_CSD_HPI_FEATURE 503 > #define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ > +#define EXT_CSD_SUPPORTED_MODES 493 /* RO */ > +#define EXT_CSD_FFU_FEATURES 492 /* RO */ > +#define EXT_CSD_FFU_ARG_3 490 /* RO */ > +#define EXT_CSD_FFU_ARG_2 489 /* RO */ > +#define EXT_CSD_FFU_ARG_1 488 /* RO */ > +#define EXT_CSD_FFU_ARG_0 487 /* RO */ > +#define EXT_CSD_NUM_OF_FW_SEC_PROG_3 305 /* RO */ > +#define EXT_CSD_NUM_OF_FW_SEC_PROG_2 304 /* RO */ > +#define EXT_CSD_NUM_OF_FW_SEC_PROG_1 303 /* RO */ > +#define EXT_CSD_NUM_OF_FW_SEC_PROG_0 302 /* RO */ > +#define EXT_CSD_FIRMWARE_VERSION 254 /* RO */ > #define EXT_CSD_CACHE_SIZE_3 252 > #define EXT_CSD_CACHE_SIZE_2 251 > #define EXT_CSD_CACHE_SIZE_1 250 > @@ -58,6 +73,7 @@ > #define EXT_CSD_BOOT_BUS_CONDITIONS 177 > #define EXT_CSD_ERASE_GROUP_DEF 175 > #define EXT_CSD_BOOT_WP 173 > +#define EXT_CSD_FW_CONFIG 169 /* R/W */ > #define EXT_CSD_WR_REL_SET 167 > #define EXT_CSD_WR_REL_PARAM 166 > #define EXT_CSD_SANITIZE_START 165 > @@ -94,6 +110,9 @@ > #define EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_1 53 > #define EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 52 > #define EXT_CSD_CACHE_CTRL 33 > +#define EXT_CSD_MODE_CONFIG 30 > +#define EXT_CSD_MODE_OPERATION_CODES 29 /* W */ > +#define EXT_CSD_FFU_STATUS 26 /* R */ > > /* > * WR_REL_PARAM field definitions > @@ -109,6 +128,11 @@ > /* > * EXT_CSD field definitions > */ > +#define EXT_CSD_FFU_INSTALL (0x01) > +#define EXT_CSD_FFU_MODE (0x01) > +#define EXT_CSD_NORMAL_MODE (0x00) > +#define EXT_CSD_FFU (1<<0) > +#define EXT_CSD_UPDATE_DISABLE (1<<0) > #define EXT_CSD_HPI_SUPP (1<<0) > #define EXT_CSD_HPI_IMPL (1<<1) > #define EXT_CSD_CMD_SET_NORMAL (1<<0) > diff --git a/mmc_cmds.c b/mmc_cmds.c > index 77446b4..dec9b9e 100644 > --- a/mmc_cmds.c > +++ b/mmc_cmds.c > @@ -12,6 +12,9 @@ > * License along with this program; if not, write to the > * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > * Boston, MA 021110-1307, USA. > + * > + * Modified Jan. 2016 by SanDisk Corp. > + * Copyright (c) 2016 SanDisk Corp. > */ > > #include <stdio.h> > @@ -1017,6 +1020,9 @@ int do_read_extcsd(int nargs, char **argv) > ext_csd_rev = ext_csd[EXT_CSD_REV]; > > switch (ext_csd_rev) { > + case 8: > + str = "5.1"; > + break; > case 7: > str = "5.0"; > break; > @@ -1430,6 +1436,10 @@ int do_read_extcsd(int nargs, char **argv) > /*Reserved [31:0] */ > } > > + if (ext_csd_rev >= 7) { > + printf("eMMC Firmware Version: %s\n", > + (char*)&ext_csd[EXT_CSD_FIRMWARE_VERSION]); > + } > out_free: > return ret; > } > @@ -2032,3 +2042,218 @@ int do_cache_dis(int nargs, char **argv) { > return do_cache_ctrl(0, nargs, argv); > } > + > +int do_ffu(int nargs, char **argv) > +{ > + int dev_fd, img_fd; > + int sect_done = 0, retry = 3, ret = -EINVAL; > + unsigned int sect_size; > + __u8 ext_csd[512]; > + __u8 *buf; > + __u32 arg; > + off_t fw_size; > + ssize_t chunk_size; > + char *device; > + struct mmc_ioc_multi_cmd *multi_cmd; > + > + CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> > \n", > + exit(1)); > + > + device = argv[2]; > + dev_fd = open(device, O_RDWR); > + if (dev_fd < 0) { > + perror("device open failed"); > + exit(1); > + } > + img_fd = open(argv[1], O_RDONLY); > + if (img_fd < 0) { > + perror("image open failed"); > + close(dev_fd); > + exit(1); > + } > + > + buf = malloc(512); > + multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) + > + 3 * sizeof(struct mmc_ioc_cmd)); > + if (!buf || !multi_cmd) { > + perror("failed to allocate memory"); > + goto out; > + } > + > + ret = read_extcsd(dev_fd, ext_csd); > + if (ret) { > + fprintf(stderr, "Could not read EXT_CSD from %s\n", device); > + goto out; > + } > + > + if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) { > + fprintf(stderr, > + "The FFU feature is only available on devices >= " > + "MMC 5.0, not supported in %s\n", device); > + goto out; > + } > + > + if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) { > + fprintf(stderr, "FFU is not supported in %s\n", device); > + goto out; > + } > + > + if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) { > + fprintf(stderr, "Firmware update was disabled in %s\n", > device); > + goto out; > + } > + > + fw_size = lseek(img_fd, 0, SEEK_END); > + > + if (fw_size == 0) { > + fprintf(stderr, "Firmware image is empty"); > + goto out; > + } > + > + sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : > 4096; > + if (fw_size % sect_size) { > + fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", > (intmax_t)fw_size); > + goto out; > + } > + > + /* set CMD ARG */ > + arg = ext_csd[EXT_CSD_FFU_ARG_0] | > + ext_csd[EXT_CSD_FFU_ARG_1] << 8 | > + ext_csd[EXT_CSD_FFU_ARG_2] << 16 | > + ext_csd[EXT_CSD_FFU_ARG_3] << 24; > + > + /* prepare multi_cmd to be sent */ > + multi_cmd->num_of_cmds = 3; > + > + /* put device into ffu mode */ > + multi_cmd->cmds[0].opcode = MMC_SWITCH; > + multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << > 24) | > + (EXT_CSD_MODE_CONFIG << 16) | > + (EXT_CSD_FFU_MODE << 8) | > + EXT_CSD_CMD_SET_NORMAL; > + multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | > MMC_CMD_AC; > + multi_cmd->cmds[0].write_flag = 1; > + > + /* send image chunk */ > + multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK; > + multi_cmd->cmds[1].blksz = sect_size; > + multi_cmd->cmds[1].blocks = 1; > + multi_cmd->cmds[1].arg = arg; > + multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | > MMC_CMD_ADTC; > + multi_cmd->cmds[1].write_flag = 1; > + mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf); > + > + /* return device into normal mode */ > + multi_cmd->cmds[2].opcode = MMC_SWITCH; > + multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << > 24) | > + (EXT_CSD_MODE_CONFIG << 16) | > + (EXT_CSD_NORMAL_MODE << 8) | > + EXT_CSD_CMD_SET_NORMAL; > + multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | > MMC_CMD_AC; > + multi_cmd->cmds[2].write_flag = 1; > + > +do_retry: > + /* read firmware chunk */ > + lseek(img_fd, 0, SEEK_SET); > + chunk_size = read(img_fd, buf, 512); > + > + while (chunk_size > 0) { > + /* send ioctl with multi-cmd */ > + ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd); > + > + if (ret) { > + perror("Multi-cmd ioctl"); > + /* In case multi-cmd ioctl failed before exiting from > ffu mode */ > + ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd- > >cmds[2]); > + goto out; > + } > + > + ret = read_extcsd(dev_fd, ext_csd); > + if (ret) { > + fprintf(stderr, "Could not read EXT_CSD from %s\n", > device); > + goto out; > + } > + > + /* Test if we need to restart the download */ > + sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] > | > + > ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 | > + > ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 | > + > ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24; > + /* By spec, host should re-start download from the first > sector if sect_done is 0 */ > + if (sect_done == 0) { > + if (retry > 0) { > + retry--; > + fprintf(stderr, "Programming failed. > Retrying... (%d)\n", retry); > + goto do_retry; > + } > + fprintf(stderr, "Programming failed! Aborting...\n"); > + goto out; > + } else { > + fprintf(stderr, "Programmed %d/%jd bytes\r", > sect_done * sect_size, (intmax_t)fw_size); > + } > + > + /* read the next firmware chunk (if any) */ > + chunk_size = read(img_fd, buf, 512); > + } > + > + if ((sect_done * sect_size) == fw_size) { > + fprintf(stderr, "Programmed %jd/%jd bytes\n", > (intmax_t)fw_size, (intmax_t)fw_size); > + fprintf(stderr, "Programming finished with status %d \n", ret); > + } > + else { > + fprintf(stderr, "FW size and number of sectors written > mismatch. Status return %d\n", ret); > + goto out; > + } > + > + /* check mode operation for ffu install*/ > + if (!ext_csd[EXT_CSD_FFU_FEATURES]) { > + fprintf(stderr, "Please reboot to complete firmware > installation on %s\n", device); > + } else { > + fprintf(stderr, "Installing firmware on %s...\n", device); > + /* Re-enter ffu mode and install the firmware */ > + multi_cmd->num_of_cmds = 2; > + > + /* set ext_csd to install mode */ > + multi_cmd->cmds[1].opcode = MMC_SWITCH; > + multi_cmd->cmds[1].blksz = 0; > + multi_cmd->cmds[1].blocks = 0; > + multi_cmd->cmds[1].arg = > (MMC_SWITCH_MODE_WRITE_BYTE << 24) | > + (EXT_CSD_MODE_OPERATION_CODES << 16) > | > + (EXT_CSD_FFU_INSTALL << 8) | > + EXT_CSD_CMD_SET_NORMAL; > + multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | > MMC_RSP_R1B | MMC_CMD_AC; > + multi_cmd->cmds[1].write_flag = 1; > + > + /* send ioctl with multi-cmd */ > + ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd); > + > + if (ret) { > + perror("Multi-cmd ioctl failed setting install mode"); > + /* In case multi-cmd ioctl failed before exiting from > ffu mode */ > + ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd- > >cmds[2]); > + goto out; > + } > + > + ret = read_extcsd(dev_fd, ext_csd); > + if (ret) { > + fprintf(stderr, "Could not read EXT_CSD from %s\n", > device); > + goto out; > + } > + > + /* return status */ > + ret = ext_csd[EXT_CSD_FFU_STATUS]; > + if (ret) { > + fprintf(stderr, "%s: error %d during FFU install:\n", > device, ret); > + goto out; > + } else { > + fprintf(stderr, "FFU finished successfully\n"); > + } > + } > + > +out: > + free(buf); > + free(multi_cmd); > + close(img_fd); > + close(dev_fd); > + return ret; > +} > diff --git a/mmc_cmds.h b/mmc_cmds.h > index 75d8f8c..f645c38 100644 > --- a/mmc_cmds.h > +++ b/mmc_cmds.h > @@ -12,6 +12,9 @@ > * License along with this program; if not, write to the > * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > * Boston, MA 021110-1307, USA. > + * > + * Modified Jan. 2016 by SanDisk Corp. > + * Copyright (c) 2016 SanDisk Corp. > */ > > /* mmc_cmds.c */ > @@ -36,3 +39,4 @@ int do_rpmb_read_block(int nargs, char **argv); int > do_rpmb_write_block(int nargs, char **argv); int do_cache_en(int nargs, > char **argv); int do_cache_dis(int nargs, char **argv); > +int do_ffu(int nargs, char **argv); > -- > 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/mmc.c b/mmc.c index a13d9ae..b1a8b3a 100644 --- a/mmc.c +++ b/mmc.c @@ -13,6 +13,9 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. * + * Modified Jan. 2016 by SanDisk Corp. + * Copyright (c) 2016 SanDisk Corp. + * * (This code is based on btrfs-progs/btrfs.c.) */ @@ -175,6 +178,11 @@ static struct Command commands[] = { "NOTE! The cache is an optional feature on devices >= eMMC4.5.", NULL }, + { do_ffu, -2, + "ffu", "<image name> <device>\n" + "Run Field Firmware Update with <image name> on <device>.\n", + NULL + }, { 0, 0, 0, 0 } }; diff --git a/mmc.h b/mmc.h index b7063fb..9de8bcc 100644 --- a/mmc.h +++ b/mmc.h @@ -12,6 +12,9 @@ * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. + * + * Modified Jan. 2016 by SanDisk Corp. + * Copyright (c) 2016 SanDisk Corp. */ #include <asm-generic/int-ll64.h> @@ -34,6 +37,7 @@ #define R1_SWITCH_ERROR (1 << 7) /* sx, c */ #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ #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 */ /* @@ -42,6 +46,17 @@ #define EXT_CSD_S_CMD_SET 504 #define EXT_CSD_HPI_FEATURE 503 #define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ +#define EXT_CSD_SUPPORTED_MODES 493 /* RO */ +#define EXT_CSD_FFU_FEATURES 492 /* RO */ +#define EXT_CSD_FFU_ARG_3 490 /* RO */ +#define EXT_CSD_FFU_ARG_2 489 /* RO */ +#define EXT_CSD_FFU_ARG_1 488 /* RO */ +#define EXT_CSD_FFU_ARG_0 487 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_3 305 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_2 304 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_1 303 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_0 302 /* RO */ +#define EXT_CSD_FIRMWARE_VERSION 254 /* RO */ #define EXT_CSD_CACHE_SIZE_3 252 #define EXT_CSD_CACHE_SIZE_2 251 #define EXT_CSD_CACHE_SIZE_1 250 @@ -58,6 +73,7 @@ #define EXT_CSD_BOOT_BUS_CONDITIONS 177 #define EXT_CSD_ERASE_GROUP_DEF 175 #define EXT_CSD_BOOT_WP 173 +#define EXT_CSD_FW_CONFIG 169 /* R/W */ #define EXT_CSD_WR_REL_SET 167 #define EXT_CSD_WR_REL_PARAM 166 #define EXT_CSD_SANITIZE_START 165 @@ -94,6 +110,9 @@ #define EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_1 53 #define EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 52 #define EXT_CSD_CACHE_CTRL 33 +#define EXT_CSD_MODE_CONFIG 30 +#define EXT_CSD_MODE_OPERATION_CODES 29 /* W */ +#define EXT_CSD_FFU_STATUS 26 /* R */ /* * WR_REL_PARAM field definitions @@ -109,6 +128,11 @@ /* * EXT_CSD field definitions */ +#define EXT_CSD_FFU_INSTALL (0x01) +#define EXT_CSD_FFU_MODE (0x01) +#define EXT_CSD_NORMAL_MODE (0x00) +#define EXT_CSD_FFU (1<<0) +#define EXT_CSD_UPDATE_DISABLE (1<<0) #define EXT_CSD_HPI_SUPP (1<<0) #define EXT_CSD_HPI_IMPL (1<<1) #define EXT_CSD_CMD_SET_NORMAL (1<<0) diff --git a/mmc_cmds.c b/mmc_cmds.c index 77446b4..dec9b9e 100644 --- a/mmc_cmds.c +++ b/mmc_cmds.c @@ -12,6 +12,9 @@ * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. + * + * Modified Jan. 2016 by SanDisk Corp. + * Copyright (c) 2016 SanDisk Corp. */ #include <stdio.h> @@ -1017,6 +1020,9 @@ int do_read_extcsd(int nargs, char **argv) ext_csd_rev = ext_csd[EXT_CSD_REV]; switch (ext_csd_rev) { + case 8: + str = "5.1"; + break; case 7: str = "5.0"; break; @@ -1430,6 +1436,10 @@ int do_read_extcsd(int nargs, char **argv) /*Reserved [31:0] */ } + if (ext_csd_rev >= 7) { + printf("eMMC Firmware Version: %s\n", + (char*)&ext_csd[EXT_CSD_FIRMWARE_VERSION]); + } out_free: return ret; } @@ -2032,3 +2042,218 @@ int do_cache_dis(int nargs, char **argv) { return do_cache_ctrl(0, nargs, argv); } + +int do_ffu(int nargs, char **argv) +{ + int dev_fd, img_fd; + int sect_done = 0, retry = 3, ret = -EINVAL; + unsigned int sect_size; + __u8 ext_csd[512]; + __u8 *buf; + __u32 arg; + off_t fw_size; + ssize_t chunk_size; + char *device; + struct mmc_ioc_multi_cmd *multi_cmd; + + CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> \n", + exit(1)); + + device = argv[2]; + dev_fd = open(device, O_RDWR); + if (dev_fd < 0) { + perror("device open failed"); + exit(1); + } + img_fd = open(argv[1], O_RDONLY); + if (img_fd < 0) { + perror("image open failed"); + close(dev_fd); + exit(1); + } + + buf = malloc(512); + multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) + + 3 * sizeof(struct mmc_ioc_cmd)); + if (!buf || !multi_cmd) { + perror("failed to allocate memory"); + goto out; + } + + ret = read_extcsd(dev_fd, ext_csd); + if (ret) { + fprintf(stderr, "Could not read EXT_CSD from %s\n", device); + goto out; + } + + if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) { + fprintf(stderr, + "The FFU feature is only available on devices >= " + "MMC 5.0, not supported in %s\n", device); + goto out; + } + + if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) { + fprintf(stderr, "FFU is not supported in %s\n", device); + goto out; + } + + if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) { + fprintf(stderr, "Firmware update was disabled in %s\n", device); + goto out; + } + + fw_size = lseek(img_fd, 0, SEEK_END); + + if (fw_size == 0) { + fprintf(stderr, "Firmware image is empty"); + goto out; + } + + sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096; + if (fw_size % sect_size) { + fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", (intmax_t)fw_size); + goto out; + } + + /* set CMD ARG */ + arg = ext_csd[EXT_CSD_FFU_ARG_0] | + ext_csd[EXT_CSD_FFU_ARG_1] << 8 | + ext_csd[EXT_CSD_FFU_ARG_2] << 16 | + ext_csd[EXT_CSD_FFU_ARG_3] << 24; + + /* prepare multi_cmd to be sent */ + multi_cmd->num_of_cmds = 3; + + /* put device into ffu mode */ + multi_cmd->cmds[0].opcode = MMC_SWITCH; + multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_MODE_CONFIG << 16) | + (EXT_CSD_FFU_MODE << 8) | + EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + multi_cmd->cmds[0].write_flag = 1; + + /* send image chunk */ + multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK; + multi_cmd->cmds[1].blksz = sect_size; + multi_cmd->cmds[1].blocks = 1; + multi_cmd->cmds[1].arg = arg; + multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + multi_cmd->cmds[1].write_flag = 1; + mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf); + + /* return device into normal mode */ + multi_cmd->cmds[2].opcode = MMC_SWITCH; + multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_MODE_CONFIG << 16) | + (EXT_CSD_NORMAL_MODE << 8) | + EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + multi_cmd->cmds[2].write_flag = 1; + +do_retry: + /* read firmware chunk */ + lseek(img_fd, 0, SEEK_SET); + chunk_size = read(img_fd, buf, 512); + + while (chunk_size > 0) { + /* send ioctl with multi-cmd */ + ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd); + + if (ret) { + perror("Multi-cmd ioctl"); + /* In case multi-cmd ioctl failed before exiting from ffu mode */ + ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]); + goto out; + } + + ret = read_extcsd(dev_fd, ext_csd); + if (ret) { + fprintf(stderr, "Could not read EXT_CSD from %s\n", device); + goto out; + } + + /* Test if we need to restart the download */ + sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] | + ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 | + ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 | + ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24; + /* By spec, host should re-start download from the first sector if sect_done is 0 */ + if (sect_done == 0) { + if (retry > 0) { + retry--; + fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry); + goto do_retry; + } + fprintf(stderr, "Programming failed! Aborting...\n"); + goto out; + } else { + fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size); + } + + /* read the next firmware chunk (if any) */ + chunk_size = read(img_fd, buf, 512); + } + + if ((sect_done * sect_size) == fw_size) { + fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size); + fprintf(stderr, "Programming finished with status %d \n", ret); + } + else { + fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret); + goto out; + } + + /* check mode operation for ffu install*/ + if (!ext_csd[EXT_CSD_FFU_FEATURES]) { + fprintf(stderr, "Please reboot to complete firmware installation on %s\n", device); + } else { + fprintf(stderr, "Installing firmware on %s...\n", device); + /* Re-enter ffu mode and install the firmware */ + multi_cmd->num_of_cmds = 2; + + /* set ext_csd to install mode */ + multi_cmd->cmds[1].opcode = MMC_SWITCH; + multi_cmd->cmds[1].blksz = 0; + multi_cmd->cmds[1].blocks = 0; + multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_MODE_OPERATION_CODES << 16) | + (EXT_CSD_FFU_INSTALL << 8) | + EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + multi_cmd->cmds[1].write_flag = 1; + + /* send ioctl with multi-cmd */ + ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd); + + if (ret) { + perror("Multi-cmd ioctl failed setting install mode"); + /* In case multi-cmd ioctl failed before exiting from ffu mode */ + ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]); + goto out; + } + + ret = read_extcsd(dev_fd, ext_csd); + if (ret) { + fprintf(stderr, "Could not read EXT_CSD from %s\n", device); + goto out; + } + + /* return status */ + ret = ext_csd[EXT_CSD_FFU_STATUS]; + if (ret) { + fprintf(stderr, "%s: error %d during FFU install:\n", device, ret); + goto out; + } else { + fprintf(stderr, "FFU finished successfully\n"); + } + } + +out: + free(buf); + free(multi_cmd); + close(img_fd); + close(dev_fd); + return ret; +} diff --git a/mmc_cmds.h b/mmc_cmds.h index 75d8f8c..f645c38 100644 --- a/mmc_cmds.h +++ b/mmc_cmds.h @@ -12,6 +12,9 @@ * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. + * + * Modified Jan. 2016 by SanDisk Corp. + * Copyright (c) 2016 SanDisk Corp. */ /* mmc_cmds.c */ @@ -36,3 +39,4 @@ int do_rpmb_read_block(int nargs, char **argv); int do_rpmb_write_block(int nargs, char **argv); int do_cache_en(int nargs, char **argv); int do_cache_dis(int nargs, char **argv); +int do_ffu(int nargs, char **argv);