summaryrefslogtreecommitdiff
path: root/lib/libmtd.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libmtd.c')
-rw-r--r--lib/libmtd.c86
1 files changed, 75 insertions, 11 deletions
diff --git a/lib/libmtd.c b/lib/libmtd.c
index c34874e..1b16de5 100644
--- a/lib/libmtd.c
+++ b/lib/libmtd.c
@@ -970,7 +970,8 @@ int mtd_torture(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb)
/* Write a pattern and check it */
memset(buf, patterns[i], mtd->eb_size);
- err = mtd_write(mtd, fd, eb, 0, buf, mtd->eb_size);
+ err = mtd_write(desc, mtd, fd, eb, 0, buf, mtd->eb_size, NULL,
+ 0, 0);
if (err)
goto out;
@@ -1070,11 +1071,49 @@ int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
return 0;
}
-int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
- void *buf, int len)
+static int legacy_auto_oob_layout(const struct mtd_dev_info *mtd, int fd,
+ int ooblen, void *oob) {
+ struct nand_oobinfo old_oobinfo;
+ int start, len;
+ uint8_t *tmp_buf;
+
+ /* Read the current oob info */
+ if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo))
+ return sys_errmsg("MEMGETOOBSEL failed");
+
+ tmp_buf = malloc(ooblen);
+ memcpy(tmp_buf, oob, ooblen);
+
+ /*
+ * We use autoplacement and have the oobinfo with the autoplacement
+ * information from the kernel available
+ */
+ if (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
+ int i, tags_pos = 0;
+ 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];
+ memcpy(oob + start, tmp_buf + 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(oob + start, tmp_buf + start, len);
+ }
+
+ return 0;
+}
+
+int mtd_write(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb,
+ int offs, void *data, int len, void *oob, int ooblen,
+ uint8_t mode)
{
int ret;
off_t seek;
+ struct mtd_write_req ops;
ret = mtd_valid_erase_block(mtd, eb);
if (ret)
@@ -1099,16 +1138,41 @@ int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
return -1;
}
- /* Seek to the beginning of the eraseblock */
+ /* Calculate seek address */
seek = (off_t)eb * mtd->eb_size + offs;
- if (lseek(fd, seek, SEEK_SET) != seek)
- return sys_errmsg("cannot seek mtd%d to offset %llu",
- mtd->mtd_num, (unsigned long long)seek);
- ret = write(fd, buf, len);
- if (ret != len)
- return sys_errmsg("cannot write %d bytes to mtd%d (eraseblock %d, offset %d)",
- len, mtd->mtd_num, eb, offs);
+ ops.start = seek;
+ ops.len = len;
+ ops.ooblen = ooblen;
+ ops.usr_data = (uint64_t)(unsigned long)data;
+ ops.usr_oob = (uint64_t)(unsigned long)oob;
+ ops.mode = mode;
+
+ ret = ioctl(fd, MEMWRITE, &ops);
+ if (ret == 0)
+ return 0;
+ else if (errno != ENOTTY)
+ return mtd_ioctl_error(mtd, eb, "MEMWRITE");
+
+ /* Fall back to old methods if necessary */
+ if (oob) {
+ if (mode == MTD_OPS_AUTO_OOB)
+ if (legacy_auto_oob_layout(mtd, fd, ooblen, oob))
+ return -1;
+ if (mtd_write_oob(desc, mtd, fd, seek, ooblen, oob) < 0)
+ return sys_errmsg("cannot write to OOB");
+ }
+ if (data) {
+ /* Seek to the beginning of the eraseblock */
+ if (lseek(fd, seek, SEEK_SET) != seek)
+ return sys_errmsg("cannot seek mtd%d to offset %llu",
+ mtd->mtd_num, (unsigned long long)seek);
+ ret = write(fd, data, len);
+ if (ret != len)
+ return sys_errmsg("cannot write %d bytes to mtd%d "
+ "(eraseblock %d, offset %d)",
+ len, mtd->mtd_num, eb, offs);
+ }
return 0;
}