diff options
author | Mike Frysinger <vapier@gentoo.org> | 2010-09-27 02:50:58 -0400 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2010-09-27 09:54:17 +0300 |
commit | a8801d8665bcc94c63a798e291a03c88f2af0215 (patch) | |
tree | dbb8e24cb34701252d36d70d1b033298f35d38a3 /flash_erase.c | |
parent | 2c413f5232e02d8dd413a93f7f4f5cb8d2420ec6 (diff) |
mtd-utils: unify flash_erase and flash_eraseall
These have overlapping functionality, and while flash_eraseall supports
newer 64bit ioctls, flash_erase does not. So rather than graft support
onto flash_erase, merge the functionality of two into flash_erase so we
only have to support one util from now on.
A simple wrapper is provided to ease old flash_eraseall users into the
new combined flash_erase util.
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Diffstat (limited to 'flash_erase.c')
-rw-r--r-- | flash_erase.c | 418 |
1 files changed, 261 insertions, 157 deletions
diff --git a/flash_erase.c b/flash_erase.c index fdf9918..8929054 100644 --- a/flash_erase.c +++ b/flash_erase.c @@ -1,189 +1,293 @@ -/* - * flash_erase.c -- erase parts of a MTD device - */ +/* flash_erase.c -- erase MTD devices -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <fcntl.h> -#include <time.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/mount.h> -#include <mtd/mtd-user.h> + Copyright (C) 2000 Arcom Control System Ltd + Copyright (C) 2010 Mike Frysinger <vapier@gentoo.org> -int region_erase(int Fd, int start, int count, int unlock, int regcount) -{ - int i, j; - region_info_t * reginfo; - - reginfo = calloc(regcount, sizeof(region_info_t)); - - for(i = 0; i < regcount; i++) - { - reginfo[i].regionindex = i; - if(ioctl(Fd,MEMGETREGIONINFO,&(reginfo[i])) != 0) - return 8; - else - printf("Region %d is at %d of %d sector and with sector " - "size %x\n", i, reginfo[i].offset, reginfo[i].numblocks, - reginfo[i].erasesize); - } + 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. - // We have all the information about the chip we need. + 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. - for(i = 0; i < regcount; i++) - { //Loop through the regions - region_info_t * r = &(reginfo[i]); + 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 02111-1307, USA + */ - if((start >= reginfo[i].offset) && - (start < (r->offset + r->numblocks*r->erasesize))) - break; - } +#define PROGRAM_NAME "flash_erase" +#define VERSION "2" - if(i >= regcount) - { - printf("Starting offset %x not within chip.\n", start); - return 8; - } +#include <inttypes.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <sys/types.h> - //We are now positioned within region i of the chip, so start erasing - //count sectors from there. +#include <common.h> +#include <crc32.h> +#include <libmtd.h> - for(j = 0; (j < count)&&(i < regcount); j++) - { - erase_info_t erase; - region_info_t * r = &(reginfo[i]); +#include <mtd/mtd-user.h> +#include <mtd/jffs2-user.h> - erase.start = start; - erase.length = r->erasesize; +static const char *mtd_device; - if(unlock != 0) - { //Unlock the sector first. - if(ioctl(Fd, MEMUNLOCK, &erase) != 0) - { - perror("\nMTD Unlock failure"); - close(Fd); - return 8; - } - } - printf("\rPerforming Flash Erase of length %u at offset 0x%x", - erase.length, erase.start); - fflush(stdout); - if(ioctl(Fd, MEMERASE, &erase) != 0) - { - perror("\nMTD Erase failure"); - close(Fd); - return 8; - } +static int quiet; /* true -- don't output progress */ +static int jffs2; /* format for jffs2 usage */ +static int noskipbad; /* do not skip bad blocks */ +static int unlock; /* unlock sectors before erasing */ +static struct jffs2_unknown_node cleanmarker; +int target_endian = __BYTE_ORDER; - start += erase.length; - if(start >= (r->offset + r->numblocks*r->erasesize)) - { //We finished region i so move to region i+1 - printf("\nMoving to region %d\n", i+1); - i++; - } - } +static void show_progress(struct mtd_dev_info *mtd, uint64_t start, int eb, + int eb_start, int eb_cnt) +{ + bareverbose(!quiet, "\rErasing %d Kibyte @ %"PRIx64" -- %2i %% complete ", + mtd->eb_size / 1024, start, ((eb - eb_start) * 100) / eb_cnt); + fflush(stdout); +} - printf(" done\n"); +static void display_help (void) +{ + printf("Usage: %s [options] MTD_DEVICE <start block> <block count>\n" + "Erase blocks of the specified MTD device.\n" + "Specify a count of 0 to erase to end of device.\n" + "\n" + " -j, --jffs2 format the device for jffs2\n" + " -N, --noskipbad don't skip bad blocks\n" + " -u, --unlock unlock sectors before erasing\n" + " -q, --quiet display progress messages\n" + " --silent same as --quiet\n" + " --help display this help and exit\n" + " --version output version information and exit\n", + PROGRAM_NAME); +} - return 0; +static void display_version (void) +{ + printf("%1$s version " VERSION "\n" + "\n" + "Copyright (C) 2000 Arcom Control Systems Ltd\n" + "\n" + "%1$s comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of %1$s\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n", + PROGRAM_NAME); } -int non_region_erase(int Fd, int start, int count, int unlock) +int main(int argc, char *argv[]) { - mtd_info_t meminfo; - - if (ioctl(Fd,MEMGETINFO,&meminfo) == 0) - { - erase_info_t erase; - - erase.start = start; - - erase.length = meminfo.erasesize; - - for (; count > 0; count--) { - printf("\rPerforming Flash Erase of length %u at offset 0x%x", - erase.length, erase.start); - fflush(stdout); - - if(unlock != 0) - { - //Unlock the sector first. - printf("\rPerforming Flash unlock at offset 0x%x",erase.start); - if(ioctl(Fd, MEMUNLOCK, &erase) != 0) - { - perror("\nMTD Unlock failure"); - close(Fd); - return 8; - } - } + libmtd_t mtd_desc; + struct mtd_dev_info mtd; + int fd, clmpos = 0, clmlen = 8, eb, eb_start, eb_cnt; + int isNAND; + int error = 0; + uint64_t offset = 0; + + /* + * Process user arguments + */ + for (;;) { + int option_index = 0; + static const char *short_options = "jNqu"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"jffs2", no_argument, 0, 'j'}, + {"noskipbad", no_argument, 0, 'N'}, + {"quiet", no_argument, 0, 'q'}, + {"silent", no_argument, 0, 'q'}, + {"unlock", no_argument, 0, 'u'}, + + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) + break; - if (ioctl(Fd,MEMERASE,&erase) != 0) - { - perror("\nMTD Erase failure"); - close(Fd); - return 8; + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + return 0; + case 1: + display_version(); + return 0; } - erase.start += meminfo.erasesize; + break; + case 'j': + jffs2 = 1; + break; + case 'N': + noskipbad = 1; + break; + case 'q': + quiet = 1; + break; + case 'u': + unlock = 1; + break; + case '?': + error = 1; + break; } - printf(" done\n"); } - return 0; -} - -int main(int argc,char *argv[]) -{ - int regcount; - int Fd; - int start; - int count; - int unlock; - int res = 0; - - if (1 >= argc || !strcmp(argv[1], "-h") || !strcmp (argv[1], "--help") ) { - printf("Usage: flash_erase MTD-device [start] [cnt (# erase blocks)] [lock]\n" - " flash_erase -h | --help\n") ; - return 16 ; + switch (argc - optind) { + case 3: + mtd_device = argv[optind]; + eb_start = simple_strtoul(argv[optind + 1], &error); + eb_cnt = simple_strtoul(argv[optind + 2], &error); + break; + default: + case 0: + errmsg("no MTD device specified"); + case 1: + errmsg("no start erase block specified"); + case 2: + errmsg("no erase block count specified"); + error = 1; + break; + } + if (error) + return errmsg("Try `--help' for more information"); + + /* + * Locate MTD and prepare for erasure + */ + mtd_desc = libmtd_open(); + if (mtd_desc == NULL) + return errmsg("can't initialize libmtd"); + + if ((fd = open(mtd_device, O_RDWR)) < 0) + return sys_errmsg("%s", mtd_device); + + if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) + return errmsg("mtd_get_dev_info failed"); + + isNAND = mtd.type == MTD_NANDFLASH ? 1 : 0; + + if (jffs2) { + cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + if (!isNAND) + cleanmarker.totlen = cpu_to_je32(sizeof(cleanmarker)); + else { + struct nand_oobinfo oobinfo; + + if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) + return sys_errmsg("%s: unable to get NAND oobinfo", mtd_device); + + /* Check for autoplacement */ + if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { + /* Get the position of the free bytes */ + if (!oobinfo.oobfree[0][1]) + return errmsg(" Eeep. Autoplacement selected and no empty space in oob"); + clmpos = oobinfo.oobfree[0][0]; + clmlen = oobinfo.oobfree[0][1]; + if (clmlen > 8) + clmlen = 8; + } else { + /* Legacy mode */ + switch (mtd.oob_size) { + case 8: + clmpos = 6; + clmlen = 2; + break; + case 16: + clmpos = 8; + clmlen = 8; + break; + case 64: + clmpos = 16; + clmlen = 8; + break; + } + } + cleanmarker.totlen = cpu_to_je32(8); + } + cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(cleanmarker) - 4)); } - if (argc > 2) - start = strtol(argv[2], NULL, 0); - else - start = 0; - - if (argc > 3) - count = strtol(argv[3], NULL, 0); - else - count = 1; + /* + * Now do the actual erasing of the MTD device + */ + if (eb_cnt == 0) + eb_cnt = (mtd.size / mtd.eb_size) - eb_start; + + for (eb = eb_start; eb < eb_start + eb_cnt; eb++) { + offset = eb * mtd.eb_size; + + if (!noskipbad) { + int ret = mtd_is_bad(&mtd, fd, eb); + if (ret > 0) { + verbose(!quiet, "Skipping bad block at %08"PRIx64, offset); + continue; + } else if (ret < 0) { + if (errno == EOPNOTSUPP) { + noskipbad = 1; + if (isNAND) + return errmsg("%s: Bad block check not available", mtd_device); + } else + return sys_errmsg("%s: MTD get bad block failed", mtd_device); + } + } - if(argc > 4) - unlock = strtol(argv[4], NULL, 0); - else - unlock = 0; + show_progress(&mtd, offset, eb, eb_start, eb_cnt); + if (unlock) { + if (mtd_unlock(&mtd, fd, eb) != 0) { + sys_errmsg("%s: MTD unlock failure", mtd_device); + continue; + } + } - // Open and size the device - if ((Fd = open(argv[1],O_RDWR)) < 0) - { - fprintf(stderr,"File open error\n"); - return 8; - } + if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { + sys_errmsg("%s: MTD Erase failure", mtd_device); + continue; + } - printf("Erase Total %d Units\n", count); + /* format for JFFS2 ? */ + if (!jffs2) + continue; - if (ioctl(Fd,MEMGETREGIONCOUNT,®count) == 0) - { - if(regcount == 0) - { - res = non_region_erase(Fd, start, count, unlock); - } - else - { - res = region_erase(Fd, start, count, unlock, regcount); + /* write cleanmarker */ + if (isNAND) { + if (mtd_write_oob(mtd_desc, &mtd, fd, offset + clmpos, clmlen, &cleanmarker) != 0) { + sys_errmsg("%s: MTD writeoob failure", mtd_device); + continue; + } + } else { + if (lseek(fd, (loff_t)offset, SEEK_SET) < 0) { + sys_errmsg("%s: MTD lseek failure", mtd_device); + continue; + } + if (write(fd, &cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) { + sys_errmsg("%s: MTD write failure", mtd_device); + continue; + } } + verbose(!quiet, " Cleanmarker written at %"PRIx64, offset); } + offset += mtd.eb_size; + show_progress(&mtd, offset, eb, eb_start, eb_cnt); + bareverbose(!quiet, "\n"); - return res; + return 0; } |