static void test_mtd_write_withoob(void **state) { struct libmtd *lib = mock_libmtd_open(); int mock_fd = 4; int eb = 0xE0; int offs = 64; int len = 64; int oob_len = 64; uint8_t mode = 3; off_t seek; char buf[64], oob_data[64]; struct mtd_dev_info mtd; struct mtd_write_req req; memset(buf, 0xAA, len); memset(oob_data, 0xBA, oob_len); memset(&mtd, 0, sizeof(mtd)); memset(&req, 0, sizeof(req)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; mtd.subpage_size = 64; seek = (off_t)eb * mtd.eb_size + offs; req.start = seek; req.len = len; req.ooblen = oob_len; req.usr_data = (uint64_t)(unsigned long)buf; req.usr_oob = (uint64_t)(unsigned long)oob_data; req.mode = mode; expect_ioctl(MEMWRITE, 0, &req); int r = mtd_write(lib, &mtd, mock_fd, eb, offs, buf, len, oob_data, oob_len, mode); assert_int_equal(r, 0); libmtd_close(lib); (void) state; }
static void test_mtd_write_nooob(void **state) { struct libmtd *lib = mock_libmtd_open(); int mock_fd = 4; int eb = 0xE0; int offs = 64; int len = 64; off_t seek; char buf[64]; memset(buf, 0xAA, len); struct mtd_dev_info mtd; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; mtd.subpage_size = 64; seek = (off_t)eb * mtd.eb_size + offs; expect_lseek(seek, SEEK_SET, seek); expect_write(buf, len, len); int r = mtd_write(lib, &mtd, mock_fd, eb, offs, buf, len, NULL, 0, 0); assert_int_equal(r, 0); libmtd_close(lib); (void)state; }
/* this is the same as above but with blocks == 1 and a * different function call. * libmtd is mapping mtd_erase to mtd_erase_multi with 1 block */ static void test_mtd_erase(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_dev_info mtd; struct erase_info_user ei; struct erase_info_user64 ei64; int eb = 0x3C; int blocks = 1; memset(&ei, 0, sizeof(ei)); memset(&ei64, 0, sizeof(ei64)); memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; ei64.start = (uint64_t)eb * mtd.eb_size; ei64.length = (uint64_t)mtd.eb_size * blocks; ei.start = ei64.start; ei.length = ei64.length; /* non offs64 first */ lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; expect_ioctl(MEMERASE, 0, &ei); int r = mtd_erase(lib, &mtd, 4, eb); assert_int_equal(r, 0); lib->offs64_ioctls = OFFS64_IOCTLS_SUPPORTED; expect_ioctl(MEMERASE64, 0, &ei64); r = mtd_erase(lib, &mtd, 4, eb); assert_int_equal(r, 0); libmtd_close(lib); (void) state; }
int main(int argc, char * const argv[]) { int err; libmtd_t libmtd; struct mtd_info mtd_info; err = parse_opt(argc, argv); if (err) return -1; libmtd = libmtd_open(); if (libmtd == NULL) { if (errno == 0) return errmsg("MTD is not present in the system"); return sys_errmsg("cannot open libmtd"); } err = mtd_get_info(libmtd, &mtd_info); if (err) { if (errno == ENODEV) return errmsg("MTD is not present"); return sys_errmsg("cannot get MTD information"); } if (!args.all && args.node) { int mtdn; /* * A character device was specified, translate this to MTD * device number. */ mtdn = translate_dev(libmtd, args.node); if (mtdn < 0) goto out_libmtd; err = print_dev_info(libmtd, &mtd_info, mtdn); } else err = print_general_info(libmtd, &mtd_info, args.all); if (err) goto out_libmtd; libmtd_close(libmtd); return 0; out_libmtd: libmtd_close(libmtd); return -1; }
static void test_mtd_dev_present(void **state) { int ret; libmtd_t lib = mock_libmtd_open(); ret = mtd_dev_present(lib, 0); assert_int_equal(ret, 1); libmtd_close(lib); (void) state; }
static void test_mtd_get_dev_info1(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_dev_info info; int dev_num = 0; memset(&info, 0, sizeof(info)); expect_open(SYSFS_ROOT "/class/mtd/mtd0/dev", O_RDONLY, 0); expect_read_real(50,0); expect_read(1,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/name", O_RDONLY, 0); expect_read_real(128,0); expect_read(1,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/type", O_RDONLY, 4); expect_read(65,0); expect_read(1,0); expect_close(4,0); expect_open(SYSFS_ROOT "/class/mtd/mtd0/erasesize", O_RDONLY, 0); expect_read_real(50, 0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/size", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/writesize", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/subpagesize", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/oobsize", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/oobavail", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/numeraseregions", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/flags", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); int r = mtd_get_dev_info1(lib, dev_num, &info); assert_int_equal(r, 0); /* TODO check values */ libmtd_close(lib); (void)state; }
static void test_mtd_get_info(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_info info; memset(&info, 0, sizeof(info)); int r = mtd_get_info(lib, &info); assert_int_equal(info.sysfs_supported, 1); assert_int_equal(info.highest_mtd_num, 0); assert_int_equal(info.lowest_mtd_num, 0); assert_int_equal(info.mtd_dev_cnt, 1); assert_int_equal(r, 0); libmtd_close(lib); (void)state; }
/* 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 void test_libmtd_open(void **state) { expect_open(SYSFS_ROOT "/class/mtd/mtd0/name", O_RDONLY, 4); expect_close(4,0); struct libmtd *lib = libmtd_open(); assert_non_null(lib); assert_string_equal(lib->sysfs_mtd, SYSFS_ROOT "/class/mtd"); assert_string_equal(lib->mtd, SYSFS_ROOT "/class/mtd/mtd%d"); assert_string_equal(lib->mtd_name, SYSFS_ROOT "/class/mtd/mtd%d/name"); assert_string_equal(lib->mtd_dev, SYSFS_ROOT "/class/mtd/mtd%d/dev"); assert_string_equal(lib->mtd_type, SYSFS_ROOT "/class/mtd/mtd%d/type"); assert_string_equal(lib->mtd_eb_size, SYSFS_ROOT "/class/mtd/mtd%d/erasesize"); assert_string_equal(lib->mtd_size, SYSFS_ROOT "/class/mtd/mtd%d/size"); assert_string_equal(lib->mtd_min_io_size, SYSFS_ROOT "/class/mtd/mtd%d/writesize"); assert_string_equal(lib->mtd_subpage_size, SYSFS_ROOT "/class/mtd/mtd%d/subpagesize"); assert_string_equal(lib->mtd_oob_size, SYSFS_ROOT "/class/mtd/mtd%d/oobsize"); assert_string_equal(lib->mtd_oobavail, SYSFS_ROOT "/class/mtd/mtd%d/oobavail"); assert_string_equal(lib->mtd_region_cnt, SYSFS_ROOT "/class/mtd/mtd%d/numeraseregions"); assert_string_equal(lib->mtd_flags, SYSFS_ROOT "/class/mtd/mtd%d/flags"); libmtd_close(lib); (void) state; }
int main(int argc, char * const argv[]) { int err, verbose; libmtd_t libmtd; struct mtd_info mtd_info; struct mtd_dev_info mtd; libubi_t libubi; struct ubigen_info ui; struct ubi_scan_info *si; libmtd = libmtd_open(); if (!libmtd) return errmsg("MTD subsystem is not present"); err = parse_opt(argc, argv); if (err) goto out_close_mtd; err = mtd_get_info(libmtd, &mtd_info); if (err) { if (errno == ENODEV) errmsg("MTD is not present"); sys_errmsg("cannot get MTD information"); goto out_close_mtd; } err = mtd_get_dev_info(libmtd, args.node, &mtd); if (err) { sys_errmsg("cannot get information about \"%s\"", args.node); goto out_close_mtd; } if (!is_power_of_2(mtd.min_io_size)) { errmsg("min. I/O size is %d, but should be power of 2", mtd.min_io_size); goto out_close; } if (!mtd_info.sysfs_supported) { /* * Linux kernels older than 2.6.30 did not support sysfs * interface, and it is impossible to find out sub-page * size in these kernels. This is why users should * provide -s option. */ if (args.subpage_size == 0) { warnmsg("your MTD system is old and it is impossible " "to detect sub-page size. Use -s to get rid " "of this warning"); normsg("assume sub-page to be %d", mtd.subpage_size); } else { mtd.subpage_size = args.subpage_size; args.manual_subpage = 1; } } else if (args.subpage_size && args.subpage_size != mtd.subpage_size) { mtd.subpage_size = args.subpage_size; args.manual_subpage = 1; } if (args.manual_subpage) { /* Do some sanity check */ if (args.subpage_size > mtd.min_io_size) { errmsg("sub-page cannot be larger than min. I/O unit"); goto out_close; } if (mtd.min_io_size % args.subpage_size) { errmsg("min. I/O unit size should be multiple of " "sub-page size"); goto out_close; } } args.node_fd = open(args.node, O_RDWR); if (args.node_fd == -1) { sys_errmsg("cannot open \"%s\"", args.node); goto out_close_mtd; } /* Validate VID header offset if it was specified */ if (args.vid_hdr_offs != 0) { if (args.vid_hdr_offs % 8) { errmsg("VID header offset has to be multiple of min. I/O unit size"); goto out_close; } if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd.eb_size) { errmsg("bad VID header offset"); goto out_close; } } if (!mtd.writable) { errmsg("mtd%d (%s) is a read-only device", mtd.mtd_num, args.node); goto out_close; } /* Make sure this MTD device is not attached to UBI */ libubi = libubi_open(); if (libubi) { int ubi_dev_num; err = mtd_num2ubi_dev(libubi, mtd.mtd_num, &ubi_dev_num); libubi_close(libubi); if (!err) { errmsg("please, first detach mtd%d (%s) from ubi%d", mtd.mtd_num, args.node, ubi_dev_num); goto out_close; } } if (!args.quiet) { normsg_cont("mtd%d (%s), size ", mtd.mtd_num, mtd.type_str); ubiutils_print_bytes(mtd.size, 1); printf(", %d eraseblocks of ", mtd.eb_cnt); ubiutils_print_bytes(mtd.eb_size, 1); printf(", min. I/O size %d bytes\n", mtd.min_io_size); } if (args.quiet) verbose = 0; else if (args.verbose) verbose = 2; else verbose = 1; err = ubi_scan(&mtd, args.node_fd, &si, verbose); if (err) { errmsg("failed to scan mtd%d (%s)", mtd.mtd_num, args.node); goto out_close; } if (si->good_cnt == 0) { errmsg("all %d eraseblocks are bad", si->bad_cnt); goto out_free; } if (si->good_cnt < 2 && (!args.novtbl || args.image)) { errmsg("too few non-bad eraseblocks (%d) on mtd%d", si->good_cnt, mtd.mtd_num); goto out_free; } if (!args.quiet) { if (si->ok_cnt) normsg("%d eraseblocks have valid erase counter, mean value is %lld", si->ok_cnt, si->mean_ec); if (si->empty_cnt) normsg("%d eraseblocks are supposedly empty", si->empty_cnt); if (si->corrupted_cnt) normsg("%d corrupted erase counters", si->corrupted_cnt); print_bad_eraseblocks(&mtd, si); } if (si->alien_cnt) { if (!args.yes || !args.quiet) warnmsg("%d of %d eraseblocks contain non-ubifs data", si->alien_cnt, si->good_cnt); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } } if (!args.override_ec && si->empty_cnt < si->good_cnt) { int percent = ((double)si->ok_cnt)/si->good_cnt * 100; /* * Make sure the majority of eraseblocks have valid * erase counters. */ if (percent < 50) { if (!args.yes || !args.quiet) warnmsg("only %d of %d eraseblocks have valid erase counter", si->ok_cnt, si->good_cnt); normsg("erase counter 0 will be used for all eraseblocks"); normsg("note, arbitrary erase counter value may be specified using -e option"); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } args.ec = 0; args.override_ec = 1; } else if (percent < 95) { if (!args.yes || !args.quiet) warnmsg("only %d of %d eraseblocks have valid erase counter", si->ok_cnt, si->good_cnt); normsg("mean erase counter %lld will be used for the rest of eraseblock", si->mean_ec); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } args.ec = si->mean_ec; args.override_ec = 1; } } if (!args.quiet && args.override_ec) normsg("use erase counter %lld for all eraseblocks", args.ec); ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size, args.vid_hdr_offs, args.ubi_ver, args.image_seq); if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) { /* * Hmm, what we read from flash and what we calculated using * min. I/O unit size and sub-page size differs. */ if (!args.yes || !args.quiet) { warnmsg("VID header and data offsets on flash are %d and %d, " "which is different to requested offsets %d and %d", si->vid_hdr_offs, si->data_offs, ui.vid_hdr_offs, ui.data_offs); normsg_cont("use new offsets %d and %d? (yes/no) ", ui.vid_hdr_offs, ui.data_offs); } if (args.yes || answer_is_yes()) { if (args.yes && !args.quiet) printf("yes\n"); } else ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, 0, si->vid_hdr_offs, args.ubi_ver, args.image_seq); normsg("use offsets %d and %d", ui.vid_hdr_offs, ui.data_offs); } if (args.image) { err = flash_image(libmtd, &mtd, &ui, si); if (err < 0) goto out_free; err = format(libmtd, &mtd, &ui, si, err, 1); if (err) goto out_free; } else { err = format(libmtd, &mtd, &ui, si, 0, args.novtbl); if (err) goto out_free; } ubi_scan_free(si); close(args.node_fd); libmtd_close(libmtd); return 0; out_free: ubi_scan_free(si); out_close: close(args.node_fd); out_close_mtd: libmtd_close(libmtd); return -1; }
static void fio_exit fio_mtd_unregister(void) { unregister_ioengine(&ioengine); libmtd_close(desc); desc = NULL; }
/** * @brief NAND FLASH的读写测试,把DDR中一段数据写到NAND FLASH中,接着 读到DDR的另一空间,进行比较。每个block单独测试。 * @retval S_OK表示SPI FLASH测试成功. * @retval E_FAIL表示硬件底层操作出错. */ HRESULT CTester::TestFlash() { /* 读写NAND FLASH 的block,进行测试: ① 待测的NAND FLASH容量为512K字节(FLASH总容量为512MB,测试保留区域), 测试时将测试区域划分为4个block,每个block为64K字(128KB); ② 先对被测block进行擦除操作, 然后往block中的每个单元(16位,每个block有64K个16位单元)写入其对应的地址偏移量。 写完一个block后,把block内的每个单元读出来跟写入的数据进行比较。 */ struct mtd_dev_info mtd = { 0,0,0,0,"","",0,0,0,0,0,0,0,0,0 }; libmtd_t mtd_desc; static const char *mtd_device = "/dev/mtd11"; //测试专用分区 int fd = -1; int ret = S_OK; unsigned char write_mode = MTD_OPS_RAW; //const DWORD dwTotalSize = 256*1024*1024; const int pagelen = 2048; //?定值? const DWORD blockSize = pagelen * 64; //const DWORD dwTestblockstart = 0xFD80000; //硬件地址? //const DWORD dwTestAddr = 0xFD80000; const DWORD dwTestblockstart = 0x0; //分区开始 const DWORD dwTestAddr = 0x0; const DWORD dwTestSize = 512*1024;//512KByte //DWORD dwLen = dwTestSize; DWORD dwblockCount = dwTestSize/blockSize; DWORD mtdoffset = 0; //unsigned char writebuf[128 *1024]; //unsigned char readbuf[128 *1024]; unsigned char *writebuf = (unsigned char *)swpa_mem_alloc(128 *1024); if (NULL == writebuf) { SW_TRACE_NORMAL("Err: malloc write buf failed!"); return E_OUTOFMEMORY; } unsigned char *readbuf = (unsigned char *)swpa_mem_alloc(128 *1024); if (NULL == readbuf) { SW_TRACE_NORMAL("Err: malloc read buf failed!"); SAFE_MEM_FREE(writebuf); return E_OUTOFMEMORY; } unsigned short *wr_ptr16 = (unsigned short* )(writebuf); //unsigned short *rd_ptr16 = (unsigned short* )(readbuf); unsigned int i = 0,j=0; //create test data for(i = 0; i < ( 64 * 1024); i++) { wr_ptr16[i] = i; } SW_TRACE_NORMAL("Info: Testing NAND Flash...............\n"); /* Open the device */ if ((fd = open(mtd_device, O_RDWR)) < 0) { SW_TRACE_NORMAL("%s\n", mtd_device); //close(fd); SAFE_MEM_FREE(readbuf); SAFE_MEM_FREE(writebuf); return E_FAIL; } mtd_desc = libmtd_open(); if (!mtd_desc) { SW_TRACE_NORMAL("can't initialize libmtd\n"); //libmtd_close(mtd_desc); close(fd); SAFE_MEM_FREE(readbuf); SAFE_MEM_FREE(writebuf); return E_FAIL; } /* Fill in MTD device capability structure */ if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) { SW_TRACE_NORMAL("mtd_get_dev_info failed"); libmtd_close(mtd_desc); close(fd); SAFE_MEM_FREE(readbuf); SAFE_MEM_FREE(writebuf); return E_FAIL; } SW_TRACE_NORMAL("Info: mtd oobsize %d,subpagesize %d,ebsize %d,miniosize %d\n", mtd.oob_size,mtd.subpage_size,mtd.eb_size,mtd.min_io_size); //erase block //@eb_size: eraseblock size for (i = dwTestblockstart; i < dwTestblockstart + dwTestSize; i += mtd.eb_size) { //skip bad? if (mtd_erase(mtd_desc, &mtd, fd, i / mtd.eb_size)) { SW_TRACE_NORMAL("%s: MTD Erase failure\n", mtd_device); ret = E_FAIL; goto closeall; } } // begin test NAND FLASH write and read. for(i = 0 ; i < dwblockCount; i++) { if (GetForceStopFlag()) { return S_OK; } /* write test data to a block*/ j = 0; mtdoffset = dwTestAddr + blockSize * i; while ( j < blockSize) { //@min_io_size: minimum input/output unit size if( mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, mtdoffset % mtd.eb_size,&writebuf[j],mtd.min_io_size,NULL,0,write_mode) ) { SW_TRACE_NORMAL("%s: MTD write failure\n", mtd_device); ret = E_FAIL; goto closeall; } mtdoffset += mtd.min_io_size; j += pagelen; } /* read test data from a block*/ memset(readbuf, 0x00, blockSize); j = 0; mtdoffset = dwTestAddr + blockSize * i; while ( j < blockSize) { if( mtd_read(&mtd, fd, mtdoffset / mtd.eb_size, mtdoffset % mtd.eb_size, &readbuf[j], mtd.min_io_size)) { SW_TRACE_NORMAL("MTD read failure.\n"); ret = E_FAIL; goto closeall; } mtdoffset += mtd.min_io_size; j += pagelen; } if (GetForceStopFlag()) { return S_OK; } /* cheeck the data */ for(j = 0 ; j < blockSize ; j++) { if( readbuf[j] != writebuf[j] ) { SW_TRACE_NORMAL("data check failure.\n"); ret = E_FAIL; goto closeall; } } } closeall: libmtd_close(mtd_desc); close(fd); SAFE_MEM_FREE(readbuf); SAFE_MEM_FREE(writebuf); swpa_utils_shell("flash_eraseall /dev/mtd11", NULL); if(ret == E_FAIL) { SW_TRACE_NORMAL("Info: NAND Flash Test -- NG!\n"); }else{ SW_TRACE_NORMAL("Info: NAND Flash Test -- OK!\n"); } return ret; }
libmtd_t libmtd_open(void) { struct libmtd *lib; lib = calloc(1, sizeof(struct libmtd)); if (!lib) return NULL; lib->sysfs_mtd = mkpath("/sys", SYSFS_MTD); if (!lib->sysfs_mtd) goto out_error; lib->mtd = mkpath(lib->sysfs_mtd, MTD_NAME_PATT); if (!lib->mtd) goto out_error; lib->mtd_name = mkpath(lib->mtd, MTD_NAME); if (!lib->mtd_name) goto out_error; if (!sysfs_is_supported(lib)) { free(lib->mtd); free(lib->sysfs_mtd); free(lib->mtd_name); lib->mtd_name = lib->mtd = lib->sysfs_mtd = NULL; return lib; } lib->mtd_dev = mkpath(lib->mtd, MTD_DEV); if (!lib->mtd_dev) goto out_error; lib->mtd_type = mkpath(lib->mtd, MTD_TYPE); if (!lib->mtd_type) goto out_error; lib->mtd_eb_size = mkpath(lib->mtd, MTD_EB_SIZE); if (!lib->mtd_eb_size) goto out_error; lib->mtd_size = mkpath(lib->mtd, MTD_SIZE); if (!lib->mtd_size) goto out_error; lib->mtd_min_io_size = mkpath(lib->mtd, MTD_MIN_IO_SIZE); if (!lib->mtd_min_io_size) goto out_error; lib->mtd_subpage_size = mkpath(lib->mtd, MTD_SUBPAGE_SIZE); if (!lib->mtd_subpage_size) goto out_error; lib->mtd_oob_size = mkpath(lib->mtd, MTD_OOB_SIZE); if (!lib->mtd_oob_size) goto out_error; lib->mtd_region_cnt = mkpath(lib->mtd, MTD_REGION_CNT); if (!lib->mtd_region_cnt) goto out_error; lib->mtd_flags = mkpath(lib->mtd, MTD_FLAGS); if (!lib->mtd_flags) goto out_error; lib->sysfs_supported = 1; return lib; out_error: libmtd_close((libmtd_t)lib); return NULL; }
/* * 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; }