使用nandwrite的代码,封装为满足我们使用的代码
/* * nandwrite.c * * Copyright (C) 2000 Steven J. Hill ([email protected]) * 2003 Thomas Gleixner ([email protected]) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Overview: * This utility writes a binary image directly to a NAND flash * chip or NAND chips contained in DoC devices. This is the * "inverse operation" of nanddump. * * tglx: Major rewrite to handle bad blocks, write data with or without ECC * write oob data only on request * * Bug/ToDo: */ #define PROGRAM_NAME "nandwrite" #define VERSION "$Revision: 1.32 {1}quot; #define _GNU_SOURCE #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/types.h> #include <getopt.h> #include <asm/types.h> #include "mtd/mtd-user.h" #include "common.h" #include "libmtd.h" #ifdef DEBUG_THIS #define dprintf printf #else #define dprintf #endif // oob layouts to pass into the kernel as default static struct nand_oobinfo none_oobinfo = { .useecc = MTD_NANDECC_OFF, }; static struct nand_oobinfo jffs2_oobinfo = { .useecc = MTD_NANDECC_PLACE, .eccbytes = 6, .eccpos = { 0, 1, 2, 3, 6, 7 } }; static struct nand_oobinfo yaffs_oobinfo = { .useecc = MTD_NANDECC_PLACE, .eccbytes = 6, .eccpos = { 8, 9, 10, 13, 14, 15} }; static struct nand_oobinfo autoplace_oobinfo = { .useecc = MTD_NANDECC_AUTOPLACE }; static void erase_buffer(void *buffer, size_t size) { const uint8_t kEraseByte = 0xff; if (buffer != NULL && size > 0) { memset(buffer, kEraseByte, size); } } typedef struct { bool writeoob; int blockalign; bool pad; bool autoplace; bool noecc; bool forcejffs2; bool forceyaffs; bool noskipbad; const char *img; bool quiet; bool rawoob; bool forcelegacy; bool onlyoob; bool markbad; }nand_other_opt; #define USE_EXAMPLE #ifdef USE_EXAMPLE int main(void) { unsigned long len = 0; unsigned char *buf = NULL; dprintf("ready to nand_flash_write 1\n"); FILE *fp = NULL; fp = fopen("/root/mtd7bin","rb"); if(fp == NULL) { printf("open file error\n"); return -1; } fseek(fp,0,SEEK_END); len = ftell(fp); fseek(fp,0,SEEK_SET); buf = malloc(len); fread(buf,len,1,fp); dprintf("len = %d\n",len); jwtnand_flash_write("/dev/mtd7",(long long)0,len,buf); if(buf != NULL) free(buf); if(fp != NULL) fclose(fp); return 0; } #endif int jwtnand_flash_write(const char *mtd_device,long long jwtoffset,unsigned long len,unsigned char *buf) { nand_other_opt jwtnand_opt; jwtnand_opt.writeoob = false; jwtnand_opt.blockalign = 1; jwtnand_opt.autoplace = false; jwtnand_opt.noecc = false; jwtnand_opt.forcejffs2 = false; jwtnand_opt.forceyaffs = false; jwtnand_opt.noskipbad = false; jwtnand_opt.img = NULL; jwtnand_opt.quiet = false; jwtnand_opt.rawoob = false; jwtnand_opt.pad = true; jwtnand_opt.forcelegacy = false; jwtnand_opt.onlyoob = false; jwtnand_opt.markbad = false; dprintf("offset :0x%x\n",jwtoffset); nand_flash_write(mtd_device,jwtoffset,len,buf,jwtnand_opt); } int nand_flash_write(const char* mtd_device,long long jwtoffset,unsigned long len,unsigned char *buf,nand_other_opt jwtnand_opt) { bool pad; bool writeoob; int blockalign; long long mtdoffset; bool autoplace; bool noecc; bool forcejffs2; bool forceyaffs; bool forcelegacy; char *img; bool quiet; bool noskipbad; bool rawoob; bool onlyoob; bool markbad; int cnt = 0; int fd = -1; int ifd = -1; int imglen = 0, pagelen; bool baderaseblock = false; long long blockstart = -1; struct mtd_dev_info mtd; long long offs; int ret; int oobinfochanged = 0; struct nand_oobinfo old_oobinfo; bool failed = true; // contains all the data read from the file so far for the current eraseblock unsigned char *filebuf = NULL; size_t filebuf_max = 0; size_t filebuf_len = 0; // points to the current page inside filebuf unsigned char *writebuf = NULL; // points to the OOB for the current page in filebuf unsigned char *oobreadbuf = NULL; unsigned char *oobbuf = NULL; libmtd_t mtd_desc; int ebsize_aligned; // jieen nand_other_opt *jwtnand_ptr = &jwtnand_opt; pad = jwtnand_ptr->pad; writeoob = jwtnand_ptr->writeoob; blockalign = jwtnand_ptr->blockalign; mtdoffset = jwtoffset; autoplace = jwtnand_ptr->autoplace; noecc = jwtnand_ptr->noecc; forcejffs2 = jwtnand_ptr->forcejffs2; forceyaffs = jwtnand_ptr->forceyaffs; forcelegacy = jwtnand_ptr->forcelegacy; img = (char *)jwtnand_ptr->img; quiet = jwtnand_ptr->quiet; noskipbad = jwtnand_ptr->noskipbad; rawoob = jwtnand_ptr->rawoob; onlyoob = jwtnand_ptr->onlyoob; markbad = jwtnand_ptr->markbad; dprintf("pad:%d,writeoob:%d,onlyoob:%d img:%d,len:%d\n",pad,writeoob,onlyoob,(img == NULL),len); if (!onlyoob && (pad && writeoob)){ fprintf(stderr, "Can't pad when oob data is present.\n"); return -1;; } /* Open the device */ if ((fd = open(mtd_device, O_RDWR)) == -1) { perror(mtd_device); return -1; } mtd_desc = libmtd_open(); if (!mtd_desc) return errmsg("can't initialize libmtd"); /* Fill in MTD device capability structure */ if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) return errmsg("mtd_get_dev_info failed"); /* * Pretend erasesize is specified number of blocks - to match jffs2 * (virtual) block size * Use this value throughout unless otherwise necessary */ ebsize_aligned = mtd.eb_size * blockalign; if (mtdoffset & (mtd.min_io_size - 1)) { fprintf(stderr, "The start address is not page-aligned !\n" "The pagesize of this NAND Flash is 0x%x.\n", mtd.min_io_size); close(fd); exit(EXIT_FAILURE); } if (autoplace) { /* Read the current oob info */ if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo) != 0) { perror("MEMGETOOBSEL"); close(fd); return -1; } // autoplace ECC ? if (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE) { if (ioctl(fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); return -1; } oobinfochanged = 1; } } if (noecc) { ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW); if (ret == 0) { oobinfochanged = 2; } else { switch (errno) { case ENOTTY: if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo) != 0) { perror("MEMGETOOBSEL"); close(fd); return -1; } if (ioctl(fd, MEMSETOOBSEL, &none_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); return -1; } oobinfochanged = 1; break; default: perror("MTDFILEMODE"); close(fd); return -1; } } } /* * force oob layout for jffs2 or yaffs ? * Legacy support */ if (forcejffs2 || forceyaffs) { struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; if (autoplace) { fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n"); goto restoreoob; } if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) { fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n"); goto restoreoob; } if (mtd.oob_size == 8) { if (forceyaffs) { fprintf(stderr, "YAFSS cannot operate on 256 Byte page size"); goto restoreoob; } /* Adjust number of ecc bytes */ jffs2_oobinfo.eccbytes = 3; } if (ioctl(fd, MEMSETOOBSEL, oobsel) != 0) { perror("MEMSETOOBSEL"); goto restoreoob; } } dprintf("img : %x,mtd.size:%d\n",img,mtd.size); /* Determine if we are reading from standard input or from a file. */ if(img == NULL) { ifd == -1; } else if (strcmp(img, "-") == 0) { ifd = STDIN_FILENO; } else { dprintf("ready to open file %s\n",img); ifd = open(img, O_RDONLY); if(ifd == -1) { fprintf(stderr,"open file %s error\n",img); return -1; } } pagelen = mtd.min_io_size + ((writeoob) ? mtd.oob_size : 0); /* * For the standard input case, the input size is merely an * invariant placeholder and is set to the write page * size. Otherwise, just use the input file size. * * TODO: Add support for the -l,--length=length option (see * previous discussion by Tommi Airikka <[email protected]> at * <http://lists.infradead.org/pipermail/linux-mtd/2008-September/ * 022913.html> */ if(ifd == -1) { imglen = len; } else if (ifd == STDIN_FILENO) { imglen = pagelen; } else { imglen = lseek(ifd, 0, SEEK_END); lseek(ifd, 0, SEEK_SET); } // Check, if file is page-aligned if ((!pad) && ((imglen % pagelen) != 0)) { fprintf(stderr, "Input file is not page-aligned. Use the padding " "option.\n"); goto closeall; } dprintf("mtdoffset = 0x%x ;imglen = %d,pagelen= %d;mtd.size:%d\n\n",mtdoffset,imglen,pagelen,mtd.size); // Check, if length fits into device if (((imglen / pagelen) * mtd.min_io_size) > (mtd.size - mtdoffset)) { fprintf(stderr, "Image %d bytes, NAND page %d bytes, OOB area %d" " bytes, device size %lld bytes\n", imglen, pagelen, mtd.oob_size, mtd.size); perror("Input file does not fit into device"); goto closeall; } /* * Allocate a buffer big enough to contain all the data (OOB included) * for one eraseblock. The order of operations here matters; if ebsize * and pagelen are large enough, then "ebsize_aligned * pagelen" could * overflow a 32-bit data type. */ filebuf_max = ebsize_aligned / mtd.min_io_size * pagelen; filebuf = xmalloc(filebuf_max); erase_buffer(filebuf, filebuf_max); oobbuf = xmalloc(mtd.oob_size); erase_buffer(oobbuf, mtd.oob_size); /* * Get data from input and write to the device while there is * still input to read and we are still within the device * bounds. Note that in the case of standard input, the input * length is simply a quasi-boolean flag whose values are page * length or zero. */ while (((imglen > 0) || (writebuf < (filebuf + filebuf_len))) && (mtdoffset < mtd.size)) { /* * New eraseblock, check for bad block(s) * Stay in the loop to be sure that, if mtdoffset changes because * of a bad block, the next block that will be written to * is also checked. Thus, we avoid errors if the block(s) after the * skipped block(s) is also bad (number of blocks depending on * the blockalign). */ while (blockstart != (mtdoffset & (~ebsize_aligned + 1))) { blockstart = mtdoffset & (~ebsize_aligned + 1); offs = blockstart; // if writebuf == filebuf, we are rewinding so we must not // reset the buffer but just replay it if (writebuf != filebuf) { erase_buffer(filebuf, filebuf_len); filebuf_len = 0; writebuf = filebuf; } baderaseblock = false; if (!quiet) fprintf(stdout, "Writing data to block %lld at offset 0x%llx\n", blockstart / ebsize_aligned, blockstart); /* Check all the blocks in an erase block for bad blocks */ if (noskipbad) continue; do { if ((ret = mtd_is_bad(&mtd, fd, offs / ebsize_aligned)) < 0) { sys_errmsg("%s: MTD get bad block failed", mtd_device); goto closeall; } else if (ret == 1) { baderaseblock = true; if (!quiet) fprintf(stderr, "Bad block at %llx, %u block(s) " "from %llx will be skipped\n", offs, blockalign, blockstart); } if (baderaseblock) { mtdoffset = blockstart + ebsize_aligned; } offs += ebsize_aligned / blockalign; } while (offs < blockstart + ebsize_aligned); } // Read more data from the input if there isn't enough in the buffer if ((writebuf + mtd.min_io_size) > (filebuf + filebuf_len)) { int readlen = mtd.min_io_size; int alreadyread = (filebuf + filebuf_len) - writebuf; int tinycnt = alreadyread; while (tinycnt < readlen) { if(img != NULL) { cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); if (cnt == 0) { // EOF break; } else if (cnt < 0) { perror("File I/O error on input"); goto closeall; } tinycnt += cnt; }else { memcpy(writebuf+tinycnt,buf+tinycnt,pagelen); tinycnt += pagelen; } } /* No padding needed - we are done */ if (tinycnt == 0) { /* * For standard input, set imglen to 0 to signal * the end of the "file". For nonstandard input, * leave it as-is to detect an early EOF. */ if (ifd == STDIN_FILENO) { imglen = 0; } break; } /* Padding */ if (tinycnt < readlen) { if (!pad) { fprintf(stderr, "Unexpected EOF. Expecting at least " "%d more bytes. Use the padding option.\n", readlen - tinycnt); goto closeall; } erase_buffer(writebuf + tinycnt, readlen - tinycnt); } filebuf_len += readlen - alreadyread; if (ifd != STDIN_FILENO) { imglen -= tinycnt - alreadyread; } else if (cnt == 0) { /* No more bytes - we are done after writing the remaining bytes */ imglen = 0; } } if (writeoob) { oobreadbuf = writebuf + mtd.min_io_size; // Read more data for the OOB from the input if there isn't enough in the buffer if ((oobreadbuf + mtd.oob_size) > (filebuf + filebuf_len)) { int readlen = mtd.oob_size; int alreadyread = (filebuf + filebuf_len) - oobreadbuf; int tinycnt = alreadyread; while (tinycnt < readlen) { if(img != NULL) { cnt = read(ifd, oobreadbuf + tinycnt, readlen - tinycnt); if (cnt == 0) { // EOF break; } else if (cnt < 0) { perror("File I/O error on input"); goto closeall; } tinycnt += cnt; }else { memcpy(oobreadbuf+tinycnt,buf+tinycnt,mtd.oob_size); tinycnt += mtd.oob_size; } } if (tinycnt < readlen) { fprintf(stderr, "Unexpected EOF. Expecting at least " "%d more bytes for OOB\n", readlen - tinycnt); goto closeall; } filebuf_len += readlen - alreadyread; if (ifd != STDIN_FILENO) { imglen -= tinycnt - alreadyread; } else if (cnt == 0) { /* No more bytes - we are done after writing the remaining bytes */ imglen = 0; } } if (!noecc) { int i, start, len; int tags_pos = 0; /* * We use autoplacement and have the oobinfo with the autoplacement * information from the kernel available * * Modified to support out of order oobfree segments, * such as the layout used by diskonchip.c */ if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) { for (i = 0; old_oobinfo.oobfree[i][1]; i++) { /* Set the reserved bytes to 0xff */ start = old_oobinfo.oobfree[i][0]; len = old_oobinfo.oobfree[i][1]; if (rawoob) memcpy(oobbuf + start, oobreadbuf + start, len); else memcpy(oobbuf + start, oobreadbuf + tags_pos, len); tags_pos += len; } } else { /* Set at least the ecc byte positions to 0xff */ start = old_oobinfo.eccbytes; len = mtd.oob_size - start; memcpy(oobbuf + start, oobreadbuf + start, len); } } /* Write OOB data first, as ecc will be placed in there */ if (mtd_write_oob(mtd_desc, &mtd, fd, mtdoffset, mtd.oob_size, noecc ? oobreadbuf : oobbuf)) { sys_errmsg("%s: MTD writeoob failure", mtd_device); goto closeall; } } /* Write out the Page data */ if (!onlyoob && mtd_write(&mtd, fd, mtdoffset / mtd.eb_size, mtdoffset % mtd.eb_size, writebuf, mtd.min_io_size)) { int i; if (errno != EIO) { sys_errmsg("%s: MTD write failure", mtd_device); goto closeall; } /* Must rewind to blockstart if we can */ writebuf = filebuf; fprintf(stderr, "Erasing failed write from %#08llx to %#08llx\n", blockstart, blockstart + ebsize_aligned - 1); for (i = blockstart; i < blockstart + ebsize_aligned; i += mtd.eb_size) { if (mtd_erase(mtd_desc, &mtd, fd, mtd.eb_size)) { int errno_tmp = errno; sys_errmsg("%s: MTD Erase failure", mtd_device); if (errno_tmp != EIO) { goto closeall; } } } if (markbad) { fprintf(stderr, "Marking block at %08llx bad\n", mtdoffset & (~mtd.eb_size + 1)); if (mtd_mark_bad(&mtd, fd, mtdoffset / mtd.eb_size)) { sys_errmsg("%s: MTD Mark bad block failure", mtd_device); goto closeall; } } mtdoffset = blockstart + ebsize_aligned; continue; } mtdoffset += mtd.min_io_size; writebuf += pagelen; } failed = false; closeall: if(ifd != -1) { close(ifd); } restoreoob: libmtd_close(mtd_desc); free(filebuf); free(oobbuf); if (oobinfochanged == 1) { if (ioctl(fd, MEMSETOOBSEL, &old_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); return -1; } } close(fd); if (failed) { perror("Data was only partially written due to error\n"); return -1; } /* Return happy */ return 0; }