diff mbox

mtdchar : writing image with oob and NOP=1

Message ID 4C598244.9060901@parrot.com
State New, archived
Headers show

Commit Message

Matthieu CASTET Aug. 4, 2010, 3:07 p.m. UTC
Matthieu CASTET a écrit :
> Hi,
> 
> We need to write our bootloader with a special ecc (use by cpu rom).
> 
> For that we use MTD_MODE_RAW, write and MEMWRITEOOB. This work fine, but 
> from my understanding this will do a first page program with oob set to 
> 0xff and then program again oob.
> 
> But some recent nand support only 1 program per page (NOP=1).
> How can we program nand with NOP=1 with a custom ecc ?
> 
>  From what I see in mtdchar either datbuf is set (and oobbuf is null) or 
> oobbuf is set (and datbuf is null).
> 
> So this mean we need to add an ioctl to do that ?
> 
For info here what we did for our 2.6.27 kernel.

Matthieu
diff mbox

Patch

diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index e00d424..7621449 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -547,6 +547,84 @@  static int mtd_ioctl(struct inode *inode, struct file *file,
 		break;
 
 	}
+	case MEMWRITEPAGE:
+	{
+		struct mtd_page_buf buf;
+		struct mtd_oob_ops ops;
+		struct mtd_page_buf __user *user_buf = argp;
+	        uint32_t retlen;
+
+		if(!(file->f_mode & 2))
+			return -EPERM;
+
+		if (copy_from_user(&buf, argp, sizeof(struct mtd_page_buf)))
+			return -EFAULT;
+
+		if (buf.length != mtd->writesize || buf.oob_length != mtd->oobsize)
+			return -EINVAL;
+
+		if (!mtd->write_oob)
+			return -EOPNOTSUPP;
+
+		ret = access_ok(VERIFY_READ, buf.ptr,
+					buf.length) ? 0 : EFAULT;
+		if (ret)
+			return ret;
+
+		ret = access_ok(VERIFY_READ, buf.oob_ptr,
+					buf.oob_length) ? 0 : EFAULT;
+		if (ret)
+			return ret;
+
+		ops.ooblen = mtd->oobsize;
+		ops.ooboffs = 0;
+		ops.len = mtd->writesize;
+		ops.mode = MTD_OOB_RAW;
+
+		if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
+			return -EINVAL;
+
+		ops.datbuf = kmalloc(buf.length, GFP_KERNEL);
+		if (!ops.datbuf)
+			return -ENOMEM;
+
+		if (copy_from_user(ops.datbuf, buf.ptr, buf.length)) {
+			kfree(ops.datbuf);
+			return -EFAULT;
+		}
+
+		ops.oobbuf = kmalloc(buf.oob_length, GFP_KERNEL);
+		if (!ops.oobbuf) {
+			kfree(ops.datbuf);
+			return -ENOMEM;
+		}
+
+		if (copy_from_user(ops.oobbuf, buf.oob_ptr, buf.oob_length)) {
+			kfree(ops.datbuf);
+			kfree(ops.oobbuf);
+			return -EFAULT;
+		}
+
+		ret = mtd->write_oob(mtd, buf.start, &ops);
+
+		if (!ret) {
+			retlen = ops.retlen;
+			if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length)))
+				ret = -EFAULT;
+		}
+
+		if (!ret) {
+			retlen = ops.oobretlen;
+			if (copy_to_user(&user_buf->oob_length, &retlen, sizeof(buf.oob_length)))
+				ret = -EFAULT;
+		}
+
+
+		kfree(ops.datbuf);
+		kfree(ops.oobbuf);
+		break;
+
+	}
 
 	case MEMREADOOB:
 	{
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h
index c6c61cd..3d9e3bc 100644
--- a/include/mtd/mtd-abi.h
+++ b/include/mtd/mtd-abi.h
@@ -16,6 +16,13 @@  struct mtd_oob_buf {
 	unsigned char __user *ptr;
 };
 
+struct mtd_page_buf {
+	uint32_t start;
+	uint32_t length;
+	unsigned char __user *ptr;
+	uint32_t oob_length;
+	unsigned char __user *oob_ptr;
+};
 #define MTD_ABSENT		0
 #define MTD_RAM			1
 #define MTD_ROM			2
@@ -93,6 +100,7 @@  struct otp_info {
 #define ECCGETLAYOUT		_IOR('M', 17, struct nand_ecclayout)
 #define ECCGETSTATS		_IOR('M', 18, struct mtd_ecc_stats)
 #define MTDFILEMODE		_IO('M', 19)
+#define MEMWRITEPAGE		_IOWR('M', 30, struct mtd_page_buf)
 
 /*
  * Obsolete legacy interface. Keep it in order not to break userspace