static int write_eraseblock(int ebnum) { int i; struct mtd_oob_ops ops; int err = 0; loff_t addr = (loff_t)ebnum * mtd->erasesize; prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt); for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = use_len; ops.oobretlen = 0; ops.ooboffs = use_offset; ops.datbuf = NULL; ops.oobbuf = writebuf + (use_len_max * i) + use_offset; err = mtd_write_oob(mtd, addr, &ops); if (err || ops.oobretlen != use_len) { pr_err("error: writeoob failed at %#llx\n", (long long)addr); pr_err("error: use_len %d, use_offset %d\n", use_len, use_offset); errcnt += 1; return err ? err : -1; } if (vary_offset) do_vary_offset(); } return err; }
static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_oob_ops ops; struct sm_oob oob; int ret, error = 0; memset(&oob, -1, SM_OOB_SIZE); oob.block_status = 0x0F; /* As long as this function is called on erase block boundaries it will work correctly for 256 byte nand */ ops.mode = MTD_OPS_PLACE_OOB; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; ops.oobbuf = (void *)&oob; ops.datbuf = NULL; ret = mtd_write_oob(mtd, ofs, &ops); if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { printk(KERN_NOTICE "sm_common: can't mark sector at %i as bad\n", (int)ofs); error = -EIO; } else mtd->ecc_stats.badblocks++; return error; }
static int sm_block_markbad(struct nand_chip *chip, loff_t ofs) { struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_oob_ops ops; struct sm_oob oob; int ret; memset(&oob, -1, SM_OOB_SIZE); oob.block_status = 0x0F; /* As long as this function is called on erase block boundaries it will work correctly for 256 byte nand */ ops.mode = MTD_OPS_PLACE_OOB; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; ops.oobbuf = (void *)&oob; ops.datbuf = NULL; ret = mtd_write_oob(mtd, ofs, &ops); if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { pr_notice("sm_common: can't mark sector at %i as bad\n", (int)ofs); return -EIO; } return 0; }
static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, const u8 *data, int data_len, const u8 *oob, int oob_len) { struct mtd_info *mtd = yaffs_dev_to_mtd(dev); loff_t addr; struct mtd_oob_ops ops; int retval; addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; memset(&ops, 0, sizeof(ops)); ops.mode = MTD_OPS_AUTO_OOB; ops.len = (data) ? data_len : 0; ops.ooblen = oob_len; ops.datbuf = (u8 *)data; ops.oobbuf = (u8 *)oob; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) retval = mtd_write_oob(mtd, addr, &ops); #else retval = mtd->write_oob(mtd, addr, &ops); #endif if (retval) { yaffs_trace(YAFFS_TRACE_MTD, "write_oob failed, chunk %d, mtd error %d", nand_chunk, retval); } return retval ? YAFFS_FAIL : YAFFS_OK; }
/* Write one page with oob one time */ int ubi_io_write_oob(const struct ubi_device *ubi, void *databuf, void *oobbuf, int pnum, int offset) { int err; loff_t addr; struct mtd_oob_ops ops; dbg_io("read from PEB %d:%d", pnum, offset); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); ubi_assert(offset >= 0 && offset + ubi->mtd->writesize <= ubi->peb_size); addr = (loff_t)pnum * ubi->peb_size + offset; ops.mode = MTD_OPS_AUTO_OOB; ops.ooblen = ubi->mtd->oobavail; ops.oobbuf = oobbuf; ops.ooboffs = 0; ops.len = ubi->mtd->writesize; ops.datbuf = databuf; ops.retlen = ops.oobretlen = 0; err = mtd_write_oob(ubi->mtd, addr, &ops); if (err) { ubi_err("error %d while writing to addr %lld peb%d:0x%x, written ", err, addr, pnum, offset); dump_stack(); } else ubi_assert(ops.retlen == ops.len); return err; }
int nandmtd_WriteChunkToNAND(struct yaffs_dev *dev, int chunkInNAND, const u8 *data, const struct yaffs_spare *spare) { struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context); struct mtd_oob_ops ops; size_t dummy; int retval = 0; loff_t addr = ((loff_t) chunkInNAND) * dev->data_bytes_per_chunk; u8 spareAsBytes[8]; /* OOB */ if (data && !spare) retval = mtd_write(mtd, addr, dev->data_bytes_per_chunk, &dummy, data); else if (spare) { if (dev->param.use_nand_ecc) { translate_spare2oob(spare, spareAsBytes); ops.mode = MTD_OPS_AUTO_OOB; ops.ooblen = 8; /* temp hack */ } else { ops.mode = MTD_OPS_RAW; ops.ooblen = YAFFS_BYTES_PER_SPARE; } ops.len = data ? dev->data_bytes_per_chunk : ops.ooblen; ops.datbuf = (u8 *)data; ops.ooboffs = 0; ops.oobbuf = spareAsBytes; retval = mtd_write_oob(mtd, addr, &ops); } if (retval == 0) return YAFFS_OK; else return YAFFS_FAIL; }
static int write_eraseblock(int ebnum) { int i; struct mtd_oob_ops ops; int err = 0; loff_t addr = ebnum * mtd->erasesize; for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { set_random_data(writebuf, use_len); ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = use_len; ops.oobretlen = 0; ops.ooboffs = use_offset; ops.datbuf = NULL; ops.oobbuf = writebuf; err = mtd_write_oob(mtd, addr, &ops); if (err || ops.oobretlen != use_len) { printk(PRINT_PREF "error: writeoob failed at %#llx\n", (long long)addr); printk(PRINT_PREF "error: use_len %d, use_offset %d\n", use_len, use_offset); errcnt += 1; return err ? err : -1; } if (vary_offset) do_vary_offset(); } return err; }
static int part_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); if (to >= mtd->size) return -EINVAL; if (ops->datbuf && to + ops->len > mtd->size) return -EINVAL; return mtd_write_oob(part->master, to + part->offset, ops); }
/* Write a chunk (page) of data to NAND. * * Caller always provides ExtendedTags data which are converted to a more * compact (packed) form for storage in NAND. A mini-ECC runs over the * contents of the tags meta-data; used to valid the tags when read. * * - Pack ExtendedTags to packed_tags1 form * - Compute mini-ECC for packed_tags1 * - Write data and packed tags to NAND. * * Note: Due to the use of the packed_tags1 meta-data which does not include * a full sequence number (as found in the larger packed_tags2 form) it is * necessary for Yaffs to re-write a chunk/page (just once) to mark it as * discarded and dirty. This is not ideal: newer NAND parts are supposed * to be written just once. When Yaffs performs this operation, this * function is called with a NULL data pointer -- calling MTD write_oob * without data is valid usage (2.6.17). * * Any underlying MTD error results in YAFFS_FAIL. * Returns YAFFS_OK or YAFFS_FAIL. */ int nandmtd1_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk, const u8 * data, const struct yaffs_ext_tags *etags) { struct mtd_info *mtd = yaffs_dev_to_mtd(dev); int chunk_bytes = dev->data_bytes_per_chunk; loff_t addr = ((loff_t) nand_chunk) * chunk_bytes; struct mtd_oob_ops ops; struct yaffs_packed_tags1 pt1; int retval; /* we assume that packed_tags1 and struct yaffs_tags are compatible */ compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12); compile_time_assertion(sizeof(struct yaffs_tags) == 8); yaffs_pack_tags1(&pt1, etags); yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1); /* When deleting a chunk, the upper layer provides only skeletal * etags, one with is_deleted set. However, we need to update the * tags, not erase them completely. So we use the NAND write property * that only zeroed-bits stick and set tag bytes to all-ones and * zero just the (not) deleted bit. */ #ifndef CONFIG_YAFFS_9BYTE_TAGS if (etags->is_deleted) { memset(&pt1, 0xff, 8); /* clear delete status bit to indicate deleted */ pt1.deleted = 0; } #else ((u8 *) & pt1)[8] = 0xff; if (etags->is_deleted) { memset(&pt1, 0xff, 8); /* zero page_status byte to indicate deleted */ ((u8 *) & pt1)[8] = 0; } #endif memset(&ops, 0, sizeof(ops)); ops.mode = MTD_OPS_AUTO_OOB; ops.len = (data) ? chunk_bytes : 0; ops.ooblen = YTAG1_SIZE; ops.datbuf = (u8 *) data; ops.oobbuf = (u8 *) & pt1; retval = mtd_write_oob(mtd, addr, &ops); if (retval) { yaffs_trace(YAFFS_TRACE_MTD, "write_oob failed, chunk %d, mtd error %d", nand_chunk, retval); } return retval ? YAFFS_FAIL : YAFFS_OK; }
/* NB For use with inband tags.... * We assume that the data buffer is of size total_bytes_per_chunk so that we can also * use it to load the tags. */ int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk, const u8 * data, const struct yaffs_ext_tags *tags) { struct mtd_info *mtd = yaffs_dev_to_mtd(dev); struct mtd_oob_ops ops; int retval = 0; loff_t addr; struct yaffs_packed_tags2 pt; int packed_tags_size = dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); void *packed_tags_ptr = dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_write_chunk_tags chunk %d data %p tags %p", nand_chunk, data, tags); addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; /* For yaffs2 writing there must be both data and tags. * If we're using inband tags, then the tags are stuffed into * the end of the data buffer. */ if (!data || !tags) BUG(); else if (dev->param.inband_tags) { struct yaffs_packed_tags2_tags_only *pt2tp; pt2tp = (struct yaffs_packed_tags2_tags_only *)(data + dev-> data_bytes_per_chunk); yaffs_pack_tags2_tags_only(pt2tp, tags); } else { yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); } ops.mode = MTD_OPS_AUTO_OOB; ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size; ops.len = dev->param.total_bytes_per_chunk; ops.ooboffs = 0; ops.datbuf = (u8 *) data; ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr; retval = mtd_write_oob(mtd, addr, &ops); if (retval == 0) return YAFFS_OK; else return YAFFS_FAIL; }
/* basically the same as above but write calls */ static void test_mtd_write_oob(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_dev_info mtd; struct mtd_oob_buf64 oob64; struct mtd_oob_buf oob; int mock_fd = 4; uint64_t start = 0, length = 64; char buf[64]; memset(buf, 0xCD, 64); memset(&oob, 0, sizeof(oob)); memset(&oob64, 0, sizeof(oob64)); memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; mtd.subpage_size = 64; mtd.oob_size = 128; oob64.start = start; oob64.length = length; oob64.usr_ptr = (uint64_t)(unsigned long)buf; oob.start = oob64.start; oob.length = oob64.length; oob.ptr = buf; lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; expect_ioctl(MEMWRITEOOB, 0, &oob); int r = mtd_write_oob(lib, &mtd, mock_fd, start, length, buf); assert_int_equal(r, 0); lib->offs64_ioctls = OFFS64_IOCTLS_SUPPORTED; expect_ioctl(MEMWRITEOOB64, 0, &oob64); r = mtd_write_oob(lib, &mtd, mock_fd, start, length, buf); assert_int_equal(r, 0); libmtd_close(lib); (void)state; }
static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset) { struct mtd_oob_ops ops; ssize_t ret; ops.mode = MTD_OPS_RAW; ops.ooboffs = 0; ops.datbuf = buf; ops.len = mtd->writesize; ops.oobbuf = buf + mtd->writesize; ops.ooblen = mtd->oobsize; ret = mtd_write_oob(mtd, offset, &ops); return ret; }
/* * Write oob data to flash */ int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, size_t *retlen, uint8_t *buf) { struct mtd_oob_ops ops; int res; ops.mode = MTD_OPS_PLACE_OOB; ops.ooboffs = offs & (mtd->writesize - 1); ops.ooblen = len; ops.oobbuf = buf; ops.datbuf = NULL; res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); *retlen = ops.oobretlen; return res; }
static ssize_t mtdraw_blkwrite(struct mtd_info *mtd, const void *buf, ulong offset) { struct mtd_oob_ops ops; int ret; ops.mode = MTD_OPS_RAW; ops.ooboffs = 0; ops.datbuf = (void *)buf; ops.len = mtd->writesize; ops.oobbuf = (void *)buf + mtd->writesize; ops.ooblen = mtd->oobsize; ret = mtd_write_oob(mtd, offset, &ops); if (!ret) ret = ops.retlen + ops.oobretlen; return ret; }
/* * Write data and oob to flash */ static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len, size_t *retlen, uint8_t *buf, uint8_t *oob) { struct mtd_oob_ops ops; int res; ops.mode = MTD_OPS_PLACE_OOB; ops.ooboffs = offs; ops.ooblen = mtd->oobsize; ops.oobbuf = oob; ops.datbuf = buf; ops.len = len; res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); *retlen = ops.retlen; return res; }
static int concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct mtd_concat *concat = CONCAT(mtd); struct mtd_oob_ops devops = *ops; int i, err; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; ops->retlen = ops->oobretlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; if (to >= subdev->size) { to -= subdev->size; continue; } /* partial write ? */ if (to + devops.len > subdev->size) devops.len = subdev->size - to; err = mtd_write_oob(subdev, to, &devops); ops->retlen += devops.oobretlen; if (err) return err; if (devops.datbuf) { devops.len = ops->len - ops->retlen; if (!devops.len) return 0; devops.datbuf += devops.retlen; } if (devops.oobbuf) { devops.ooblen = ops->ooblen - ops->oobretlen; if (!devops.ooblen) return 0; devops.oobbuf += devops.oobretlen; } to = 0; } return -EINVAL; }
static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, const u8 *data, int data_len, const u8 *oob, int oob_len) { struct mtd_info *mtd = yaffs_dev_to_mtd(dev); loff_t addr; struct mtd_oob_ops ops; int retval; yaffs_trace(YAFFS_TRACE_MTD, "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", dev, nand_chunk, data, data_len, oob, oob_len); if (!data || !data_len) { data = NULL; data_len = 0; } if (!oob || !oob_len) { oob = NULL; oob_len = 0; } addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; memset(&ops, 0, sizeof(ops)); ops.mode = MTD_OPS_AUTO_OOB; ops.len = (data) ? data_len : 0; ops.ooblen = oob_len; ops.datbuf = (u8 *)data; ops.oobbuf = (u8 *)oob; retval = mtd_write_oob(mtd, addr, &ops); if (retval) { yaffs_trace(YAFFS_TRACE_MTD, "write_oob failed, chunk %d, mtd error %d", nand_chunk, retval); } return retval ? YAFFS_FAIL : YAFFS_OK; }
int main(int argc, char *argv[]) { libmtd_t mtd_desc; struct mtd_dev_info mtd; int fd, clmpos = 0, clmlen = 8; unsigned long long start; unsigned int eb, eb_start, eb_cnt; int isNAND; int error = 0; off_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; switch (c) { case 0: switch (option_index) { case 0: display_help(); return 0; case 1: display_version(); return 0; } 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; } } switch (argc - optind) { case 3: mtd_device = argv[optind]; start = simple_strtoull(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"); eb_start = start / mtd.eb_size; 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)); } /* * 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 = (off_t)eb * mtd.eb_size; if (!noskipbad) { int ret = mtd_is_bad(&mtd, fd, eb); if (ret > 0) { verbose(!quiet, "Skipping bad block at %08"PRIxoff_t, 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); } } 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; } } if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { sys_errmsg("%s: MTD Erase failure", mtd_device); continue; } /* format for JFFS2 ? */ if (!jffs2) continue; /* write cleanmarker */ if (isNAND) { if (mtd_write_oob(mtd_desc, &mtd, fd, (uint64_t)offset + clmpos, clmlen, &cleanmarker) != 0) { sys_errmsg("%s: MTD writeoob failure", mtd_device); continue; } } else { if (lseek(fd, 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 %"PRIxoff_t, offset); } show_progress(&mtd, offset, eb, eb_start, eb_cnt); bareverbose(!quiet, "\n"); return 0; }
/* * Main program */ int main(int argc, char * const argv[]) { 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; process_options(argc, argv); if (pad && writeoob) { fprintf(stderr, "Can't pad when oob data is present.\n"); exit(EXIT_FAILURE); } /* Open the device */ if ((fd = open(mtd_device, O_RDWR)) == -1) { perror(mtd_device); exit(EXIT_FAILURE); } 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); exit(EXIT_FAILURE); } // autoplace ECC ? if (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE) { if (ioctl(fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); exit(EXIT_FAILURE); } 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); exit(EXIT_FAILURE); } if (ioctl(fd, MEMSETOOBSEL, &none_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); exit(EXIT_FAILURE); } oobinfochanged = 1; break; default: perror("MTDFILEMODE"); close(fd); exit(EXIT_FAILURE); } } } /* * 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; } } /* Determine if we are reading from standard input or from a file. */ if (strcmp(img, standard_input) == 0) { ifd = STDIN_FILENO; } else { ifd = open(img, O_RDONLY); } if (ifd == -1) { perror(img); goto restoreoob; } 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 <*****@*****.**> at * <http://lists.infradead.org/pipermail/linux-mtd/2008-September/ * 022913.html> */ 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; } // 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) { 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; } /* 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) { 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; } 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 (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: 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); exit(EXIT_FAILURE); } } close(fd); if (failed || ((ifd != STDIN_FILENO) && (imglen > 0)) || (writebuf < (filebuf + filebuf_len))) { perror("Data was only partially written due to error\n"); exit(EXIT_FAILURE); } /* Return happy */ return EXIT_SUCCESS; }
int main (int argc, char *argv[]) { libmtd_t mtd_desc; struct mtd_dev_info mtd; int fd, clmpos = 0, clmlen = 8, eb; int isNAND, bbtest = 1; int error = 0; uint64_t offset = 0; exe_name = argv[0]; for (;;) { int option_index = 0; static const char *short_options = "jq"; static const struct option long_options[] = { {"help", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"jffs2", no_argument, 0, 'j'}, {"quiet", no_argument, 0, 'q'}, {"silent", no_argument, 0, 'q'}, {0, 0, 0, 0}, }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) break; switch (c) { case 0: switch (option_index) { case 0: display_help(); return 0; case 1: display_version(); return 0; } break; case 'q': quiet = 1; break; case 'j': jffs2 = 1; break; case '?': error = 1; break; } } if (optind == argc) { fprintf(stderr, "%s: no MTD device specified\n", exe_name); error = 1; } if (error) { fprintf(stderr, "Try `%s --help' for more information.\n", exe_name); return 1; } mtd_device = argv[optind]; mtd_desc = libmtd_open(); if (mtd_desc == NULL) { fprintf(stderr, "%s: can't initialize libmtd\n", exe_name); return 1; } if ((fd = open(mtd_device, O_RDWR)) < 0) { fprintf(stderr, "%s: %s: %s\n", exe_name, mtd_device, strerror(errno)); return 1; } if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) { fprintf(stderr, "%s: mtd_get_dev_info failed\n", exe_name); return 1; } 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 (struct jffs2_unknown_node)); else { struct nand_oobinfo oobinfo; if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) { fprintf(stderr, "%s: %s: unable to get NAND oobinfo\n", exe_name, mtd_device); return 1; } /* Check for autoplacement */ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { /* Get the position of the free bytes */ if (!oobinfo.oobfree[0][1]) { fprintf (stderr, " Eeep. Autoplacement selected and no empty space in oob\n"); return 1; } 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 (struct jffs2_unknown_node) - 4)); } for (eb = 0; eb < (mtd.size / mtd.eb_size); eb++) { offset = eb * mtd.eb_size; if (bbtest) { int ret = mtd_is_bad(&mtd, fd, eb); if (ret > 0) { if (!quiet) printf ("\nSkipping bad block at 0x%08llx\n", (unsigned long long)offset); continue; } else if (ret < 0) { if (errno == EOPNOTSUPP) { bbtest = 0; if (isNAND) { fprintf(stderr, "%s: %s: Bad block check not available\n", exe_name, mtd_device); return 1; } } else { fprintf(stderr, "\n%s: %s: MTD get bad block failed: %s\n", exe_name, mtd_device, strerror(errno)); return 1; } } } if (!quiet) show_progress(&mtd, offset); if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { fprintf(stderr, "\n%s: %s: MTD Erase failure: %s\n", exe_name, mtd_device, strerror(errno)); continue; } /* format for JFFS2 ? */ if (!jffs2) continue; /* write cleanmarker */ if (isNAND) { if (mtd_write_oob(mtd_desc, &mtd, fd, offset + clmpos, clmlen, &cleanmarker) != 0) { fprintf(stderr, "\n%s: %s: MTD writeoob failure: %s\n", exe_name, mtd_device, strerror(errno)); continue; } } else { if (lseek (fd, (loff_t)offset, SEEK_SET) < 0) { fprintf(stderr, "\n%s: %s: MTD lseek failure: %s\n", exe_name, mtd_device, strerror(errno)); continue; } if (write (fd , &cleanmarker, sizeof (cleanmarker)) != sizeof (cleanmarker)) { fprintf(stderr, "\n%s: %s: MTD write failure: %s\n", exe_name, mtd_device, strerror(errno)); continue; } } if (!quiet) printf (" Cleanmarker written at %llx.", (unsigned long long)offset); } if (!quiet) { show_progress(&mtd, offset); printf("\n"); } return 0; }
static int __init mtd_oobtest_init(void) { int err = 0; unsigned int i; uint64_t tmp; struct mtd_oob_ops ops; loff_t addr = 0, addr0; printk(KERN_INFO "\n"); printk(KERN_INFO "=================================================\n"); if (dev < 0) { printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n"); printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n"); return -EINVAL; } printk(PRINT_PREF "MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); printk(PRINT_PREF "error: cannot get MTD device\n"); return err; } if (mtd->type != MTD_NANDFLASH) { printk(PRINT_PREF "this test requires NAND flash\n"); goto out; } tmp = mtd->size; do_div(tmp, mtd->erasesize); ebcnt = tmp; pgcnt = mtd->erasesize / mtd->writesize; printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " "eraseblock %u, OOB size %u\n", (unsigned long long)mtd->size, mtd->erasesize, mtd->writesize, ebcnt, pgcnt, mtd->oobsize); err = -ENOMEM; readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!readbuf) { printk(PRINT_PREF "error: cannot allocate memory\n"); goto out; } writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!writebuf) { printk(PRINT_PREF "error: cannot allocate memory\n"); goto out; } err = scan_for_bad_eraseblocks(); if (err) goto out; use_offset = 0; use_len = mtd->ecclayout->oobavail; use_len_max = mtd->ecclayout->oobavail; vary_offset = 0; /* First test: write all OOB, read it back and verify */ printk(PRINT_PREF "test 1 of 5\n"); err = erase_whole_device(); if (err) goto out; simple_srand(1); err = write_whole_device(); if (err) goto out; simple_srand(1); err = verify_all_eraseblocks(); if (err) goto out; /* * Second test: write all OOB, a block at a time, read it back and * verify. */ printk(PRINT_PREF "test 2 of 5\n"); err = erase_whole_device(); if (err) goto out; simple_srand(3); err = write_whole_device(); if (err) goto out; /* Check all eraseblocks */ simple_srand(3); printk(PRINT_PREF "verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; err = verify_eraseblock_in_one_go(i); if (err) goto out; if (i % 256 == 0) printk(PRINT_PREF "verified up to eraseblock %u\n", i); cond_resched(); } printk(PRINT_PREF "verified %u eraseblocks\n", i); /* * Third test: write OOB at varying offsets and lengths, read it back * and verify. */ printk(PRINT_PREF "test 3 of 5\n"); err = erase_whole_device(); if (err) goto out; /* Write all eraseblocks */ use_offset = 0; use_len = mtd->ecclayout->oobavail; use_len_max = mtd->ecclayout->oobavail; vary_offset = 1; simple_srand(5); err = write_whole_device(); if (err) goto out; /* Check all eraseblocks */ use_offset = 0; use_len = mtd->ecclayout->oobavail; use_len_max = mtd->ecclayout->oobavail; vary_offset = 1; simple_srand(5); err = verify_all_eraseblocks(); if (err) goto out; use_offset = 0; use_len = mtd->ecclayout->oobavail; use_len_max = mtd->ecclayout->oobavail; vary_offset = 0; /* Fourth test: try to write off end of device */ printk(PRINT_PREF "test 4 of 5\n"); err = erase_whole_device(); if (err) goto out; addr0 = 0; for (i = 0; i < ebcnt && bbt[i]; ++i) addr0 += mtd->erasesize; /* Attempt to write off end of OOB */ ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = 1; ops.oobretlen = 0; ops.ooboffs = mtd->ecclayout->oobavail; ops.datbuf = NULL; ops.oobbuf = writebuf; printk(PRINT_PREF "attempting to start write past end of OOB\n"); printk(PRINT_PREF "an error is expected...\n"); err = mtd_write_oob(mtd, addr0, &ops); if (err) { printk(PRINT_PREF "error occurred as expected\n"); err = 0; } else { printk(PRINT_PREF "error: can write past end of OOB\n"); errcnt += 1; } /* Attempt to read off end of OOB */ ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = 1; ops.oobretlen = 0; ops.ooboffs = mtd->ecclayout->oobavail; ops.datbuf = NULL; ops.oobbuf = readbuf; printk(PRINT_PREF "attempting to start read past end of OOB\n"); printk(PRINT_PREF "an error is expected...\n"); err = mtd_read_oob(mtd, addr0, &ops); if (err) { printk(PRINT_PREF "error occurred as expected\n"); err = 0; } else { printk(PRINT_PREF "error: can read past end of OOB\n"); errcnt += 1; } if (bbt[ebcnt - 1]) printk(PRINT_PREF "skipping end of device tests because last " "block is bad\n"); else { /* Attempt to write off end of device */ ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = mtd->ecclayout->oobavail + 1; ops.oobretlen = 0; ops.ooboffs = 0; ops.datbuf = NULL; ops.oobbuf = writebuf; printk(PRINT_PREF "attempting to write past end of device\n"); printk(PRINT_PREF "an error is expected...\n"); err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); if (err) { printk(PRINT_PREF "error occurred as expected\n"); err = 0; } else { printk(PRINT_PREF "error: wrote past end of device\n"); errcnt += 1; } /* Attempt to read off end of device */ ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = mtd->ecclayout->oobavail + 1; ops.oobretlen = 0; ops.ooboffs = 0; ops.datbuf = NULL; ops.oobbuf = readbuf; printk(PRINT_PREF "attempting to read past end of device\n"); printk(PRINT_PREF "an error is expected...\n"); err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); if (err) { printk(PRINT_PREF "error occurred as expected\n"); err = 0; } else { printk(PRINT_PREF "error: read past end of device\n"); errcnt += 1; } err = erase_eraseblock(ebcnt - 1); if (err) goto out; /* Attempt to write off end of device */ ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = mtd->ecclayout->oobavail; ops.oobretlen = 0; ops.ooboffs = 1; ops.datbuf = NULL; ops.oobbuf = writebuf; printk(PRINT_PREF "attempting to write past end of device\n"); printk(PRINT_PREF "an error is expected...\n"); err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); if (err) { printk(PRINT_PREF "error occurred as expected\n"); err = 0; } else { printk(PRINT_PREF "error: wrote past end of device\n"); errcnt += 1; } /* Attempt to read off end of device */ ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = mtd->ecclayout->oobavail; ops.oobretlen = 0; ops.ooboffs = 1; ops.datbuf = NULL; ops.oobbuf = readbuf; printk(PRINT_PREF "attempting to read past end of device\n"); printk(PRINT_PREF "an error is expected...\n"); err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); if (err) { printk(PRINT_PREF "error occurred as expected\n"); err = 0; } else { printk(PRINT_PREF "error: read past end of device\n"); errcnt += 1; } } /* Fifth test: write / read across block boundaries */ printk(PRINT_PREF "test 5 of 5\n"); /* Erase all eraseblocks */ err = erase_whole_device(); if (err) goto out; /* Write all eraseblocks */ simple_srand(11); printk(PRINT_PREF "writing OOBs of whole device\n"); for (i = 0; i < ebcnt - 1; ++i) { int cnt = 2; int pg; size_t sz = mtd->ecclayout->oobavail; if (bbt[i] || bbt[i + 1]) continue; addr = (i + 1) * mtd->erasesize - mtd->writesize; for (pg = 0; pg < cnt; ++pg) { set_random_data(writebuf, sz); ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = sz; ops.oobretlen = 0; ops.ooboffs = 0; ops.datbuf = NULL; ops.oobbuf = writebuf; err = mtd_write_oob(mtd, addr, &ops); if (err) goto out; if (i % 256 == 0) printk(PRINT_PREF "written up to eraseblock " "%u\n", i); cond_resched(); addr += mtd->writesize; } } printk(PRINT_PREF "written %u eraseblocks\n", i); /* Check all eraseblocks */ simple_srand(11); printk(PRINT_PREF "verifying all eraseblocks\n"); for (i = 0; i < ebcnt - 1; ++i) { if (bbt[i] || bbt[i + 1]) continue; set_random_data(writebuf, mtd->ecclayout->oobavail * 2); addr = (i + 1) * mtd->erasesize - mtd->writesize; ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = mtd->ecclayout->oobavail * 2; ops.oobretlen = 0; ops.ooboffs = 0; ops.datbuf = NULL; ops.oobbuf = readbuf; err = mtd_read_oob(mtd, addr, &ops); if (err) goto out; if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) { printk(PRINT_PREF "error: verify failed at %#llx\n", (long long)addr); errcnt += 1; if (errcnt > 1000) { printk(PRINT_PREF "error: too many errors\n"); goto out; } } if (i % 256 == 0) printk(PRINT_PREF "verified up to eraseblock %u\n", i); cond_resched(); } printk(PRINT_PREF "verified %u eraseblocks\n", i); printk(PRINT_PREF "finished with %d errors\n", errcnt); out: kfree(bbt); kfree(writebuf); kfree(readbuf); put_mtd_device(mtd); if (err) printk(PRINT_PREF "error %d occurred\n", err); printk(KERN_INFO "=================================================\n"); return err; }