[PATCH/RFC] introducing new functions/ioctls for handling oobavail

Vitaly Wool vwool at ru.mvista.com
Thu Dec 22 07:09:51 EST 2005


Hi,

the patch inlined below allows to get and store free OOB data as a single data chunk.
This is a lot more conservatiev approach than the one that I used for my previous work on the problem. Now it retains full backward-compatibility and just adds the ability to read/write only the free OOB data (that's what flash filesystems usually want to do! :))

Vitaly

Index: drivers/mtd/mtdchar.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/mtdchar.c,v
retrieving revision 1.77
diff -u -r1.77 mtdchar.c
--- drivers/mtd/mtdchar.c	14 Dec 2005 16:41:54 -0000	1.77
+++ drivers/mtd/mtdchar.c	22 Dec 2005 12:03:21 -0000
@@ -557,6 +557,92 @@
 		break;
 	}
 
+	case MEMGETOOBAVAIL:
+	{
+		if (copy_to_user(argp, &(mtd->oobavail), sizeof(mtd->oobavail)))
+			return -EFAULT;
+		break;
+	}
+
+	case MEMWRITEOOBFREE:
+	{
+		struct mtd_oob_buf buf;
+		void *databuf;
+		ssize_t retlen;
+
+		if(!(file->f_mode & 2))
+			return -EPERM;
+
+		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
+			return -EFAULT;
+
+		if (buf.length > 0x4096)
+			return -EINVAL;
+
+		if (!mtd->write_oobfree)
+			ret = -EOPNOTSUPP;
+		else
+			ret = access_ok(VERIFY_READ, buf.ptr,
+					buf.length) ? 0 : EFAULT;
+
+		if (ret)
+			return ret;
+
+		databuf = kmalloc(buf.length, GFP_KERNEL);
+		if (!databuf)
+			return -ENOMEM;
+
+		if (copy_from_user(databuf, buf.ptr, buf.length)) {
+			kfree(databuf);
+			return -EFAULT;
+		}
+
+		ret = (mtd->write_oobfree)(mtd, buf.start, buf.length, &retlen, databuf);
+
+		if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t)))
+			ret = -EFAULT;
+
+		kfree(databuf);
+		break;
+
+	}
+
+	case MEMREADOOBFREE:
+	{
+		struct mtd_oob_buf buf;
+		void *databuf;
+		ssize_t retlen;
+
+		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
+			return -EFAULT;
+
+		if (buf.length > 0x4096)
+			return -EINVAL;
+
+		if (!mtd->read_oobfree)
+			ret = -EOPNOTSUPP;
+		else
+			ret = access_ok(VERIFY_WRITE, buf.ptr,
+					buf.length) ? 0 : -EFAULT;
+
+		if (ret)
+			return ret;
+
+		databuf = kmalloc(buf.length, GFP_KERNEL);
+		if (!databuf)
+			return -ENOMEM;
+
+		ret = (mtd->read_oobfree)(mtd, buf.start, buf.length, &retlen, databuf);
+
+		if (put_user(retlen, (uint32_t __user *)argp))
+			ret = -EFAULT;
+		else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
+			ret = -EFAULT;
+
+		kfree(databuf);
+		break;
+	}
+
 #ifdef CONFIG_MTD_OTP
 	case OTPSELECT:
 	{
Index: drivers/mtd/mtdconcat.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/mtdconcat.c,v
retrieving revision 1.11
diff -u -r1.11 mtdconcat.c
--- drivers/mtd/mtdconcat.c	7 Nov 2005 11:14:20 -0000	1.11
+++ drivers/mtd/mtdconcat.c	22 Dec 2005 12:03:21 -0000
@@ -299,6 +299,54 @@
 }
 
 static int
+concat_read_oobfree(struct mtd_info *mtd, loff_t from, size_t len,
+		    size_t * retlen, u_char * buf)
+{
+	struct mtd_concat *concat = CONCAT(mtd);
+	int err = -EINVAL;
+	int i;
+
+	*retlen = 0;
+
+	for (i = 0; i < concat->num_subdev; i++) {
+		struct mtd_info *subdev = concat->subdev[i];
+		size_t size, retsize;
+
+		if (from >= subdev->size) {
+			/* Not destined for this subdev */
+			size = 0;
+			from -= subdev->size;
+			continue;
+		}
+		if (from + len > subdev->size)
+			/* First part goes into this subdev */
+			size = subdev->size - from;
+		else
+			/* Entire transaction goes into this subdev */
+			size = len;
+
+		if (subdev->read_oobfree)
+			err = subdev->read_oobfree(subdev, from, size,
+					           &retsize, buf);
+		else
+			err = -EINVAL;
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		from = 0;
+	}
+	return err;
+}
+
+static int
 concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
 		 size_t * retlen, const u_char * buf)
 {
@@ -348,6 +396,56 @@
 	return err;
 }
 
+static int
+concat_write_oobfree(struct mtd_info *mtd, loff_t to, size_t len,
+		     size_t * retlen, const u_char * buf)
+{
+	struct mtd_concat *concat = CONCAT(mtd);
+	int err = -EINVAL;
+	int i;
+
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+
+	*retlen = 0;
+
+	for (i = 0; i < concat->num_subdev; i++) {
+		struct mtd_info *subdev = concat->subdev[i];
+		size_t size, retsize;
+
+		if (to >= subdev->size) {
+			size = 0;
+			to -= subdev->size;
+			continue;
+		}
+		if (to + len > subdev->size)
+			size = subdev->size - to;
+		else
+			size = len;
+
+		if (!(subdev->flags & MTD_WRITEABLE))
+			err = -EROFS;
+		else if (subdev->write_oobfree)
+			err = subdev->write_oobfree(subdev, to, size, &retsize,
+						    buf);
+		else
+			err = -EINVAL;
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		to = 0;
+	}
+	return err;
+}
+
 static void concat_erase_callback(struct erase_info *instr)
 {
 	wake_up((wait_queue_head_t *) instr->priv);
@@ -691,6 +789,10 @@
 		concat->mtd.read_oob = concat_read_oob;
 	if (subdev[0]->write_oob)
 		concat->mtd.write_oob = concat_write_oob;
+	if (subdev[0]->read_oobfree)
+		concat->mtd.read_oobfree = concat_read_oobfree;
+	if (subdev[0]->write_oobfree)
+		concat->mtd.write_oobfree = concat_write_oobfree;
 
 	concat->subdev[0] = subdev[0];
 
@@ -726,7 +828,10 @@
 		    !concat->mtd.read_ecc  != !subdev[i]->read_ecc ||
 		    !concat->mtd.write_ecc != !subdev[i]->write_ecc ||
 		    !concat->mtd.read_oob  != !subdev[i]->read_oob ||
-		    !concat->mtd.write_oob != !subdev[i]->write_oob) {
+		    !concat->mtd.write_oob != !subdev[i]->write_oob ||
+		    !concat->mtd.read_oobfree != !subdev[i]->read_oobfree ||
+		    !concat->mtd.write_oobfree != !subdev[i]->write_oobfree ||
+		    ) {
 			kfree(concat);
 			printk("Incompatible OOB or ECC data on \"%s\"\n",
 			       subdev[i]->name);
Index: drivers/mtd/mtdpart.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/mtdpart.c,v
retrieving revision 1.56
diff -u -r1.56 mtdpart.c
--- drivers/mtd/mtdpart.c	29 Nov 2005 16:00:23 -0000	1.56
+++ drivers/mtd/mtdpart.c	22 Dec 2005 12:03:21 -0000
@@ -108,6 +108,18 @@
 					len, retlen, buf);
 }
 
+static int part_read_oobfree (struct mtd_info *mtd, loff_t from, size_t len, 
+			size_t *retlen, u_char *buf)
+{
+	struct mtd_part *part = PART(mtd);
+	if (from >= mtd->size)
+		len = 0;
+	else if (from + len > mtd->size)
+		len = mtd->size - from;
+	return part->master->read_oobfree (part->master, from + part->offset, 
+					len, retlen, buf);
+}
+
 static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
 			size_t *retlen, u_char *buf)
 {
@@ -188,6 +200,20 @@
 					len, retlen, buf);
 }
 
+static int part_write_oobfree (struct mtd_info *mtd, loff_t to, size_t len,
+			size_t *retlen, const u_char *buf)
+{
+	struct mtd_part *part = PART(mtd);
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+	if (to >= mtd->size)
+		len = 0;
+	else if (to + len > mtd->size)
+		len = mtd->size - to;
+	return part->master->write_oobfree (part->master, to + part->offset, 
+					len, retlen, buf);
+}
+
 static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
 			size_t *retlen, u_char *buf)
 {
@@ -424,6 +450,10 @@
 			slave->mtd.read_oob = part_read_oob;
 		if (master->write_oob)
 			slave->mtd.write_oob = part_write_oob;
+		if (master->read_oobfree)
+			slave->mtd.read_oobfree = part_read_oobfree;
+		if (master->write_oobfree)
+			slave->mtd.write_oobfree = part_write_oobfree;
 		if(master->read_user_prot_reg)
 			slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
 		if(master->read_fact_prot_reg)
Index: drivers/mtd/nand/nand_base.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/nand/nand_base.c,v
retrieving revision 1.165
diff -u -r1.165 nand_base.c
--- drivers/mtd/nand/nand_base.c	16 Dec 2005 15:41:31 -0000	1.165
+++ drivers/mtd/nand/nand_base.c	22 Dec 2005 12:03:22 -0000
@@ -918,6 +918,7 @@
 				this->write_buf(mtd, &oob_buf[oidx], len);
 				break;
 			case ITEM_TYPE_OOB:
+			case ITEM_TYPE_OOBFREE:
 				this->enable_hwecc(mtd, NAND_ECC_WRITEOOB);
 				if (this->options & NAND_BUSWIDTH_16) {
 					if (oidx & 1) {
@@ -1292,6 +1293,7 @@
 					eccidx += this->layout[j].length;
 					break;
 				case ITEM_TYPE_OOB:
+				case ITEM_TYPE_OOBFREE:
 					DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d free oob bytes\n", __FUNCTION__, this->layout[j].length);
 					this->enable_hwecc(mtd, NAND_ECC_READOOB);
 					if (this->options & NAND_BUSWIDTH_16) {
@@ -1497,6 +1499,179 @@
 
 				case ITEM_TYPE_ECC:
 				case ITEM_TYPE_OOB:
+				case ITEM_TYPE_OOBFREE:
+					DEBUG (MTD_DEBUG_LEVEL3, "%s: %s bytes read\n", __FUNCTION__, this->layout[j].type == ITEM_TYPE_ECC ? "ecc" : "oob");
+					i = min_t(int, col, this->layout[j].length);
+					if (i) {
+						reallen += i;
+						if (this->options & NAND_BUSWIDTH_16)
+							this->cmdfunc (mtd, NAND_CMD_READ0, reallen & ~1, page);
+						else
+							this->cmdfunc (mtd, NAND_CMD_READ0, reallen, page);
+					}
+					col -= i;
+
+					if (this->layout[j].type == ITEM_TYPE_ECC)
+						this->enable_hwecc(mtd, NAND_ECC_READSYN);
+					else
+						this->enable_hwecc(mtd, NAND_ECC_READOOB);
+					i = min_t(int, len - read, this->layout[j].length - i);
+					if (i) {
+						if (this->options & NAND_BUSWIDTH_16) {
+							if (reallen & 1) {
+								oob_data[0] = cpu_to_le16(this->read_word(mtd)) >> 8;
+								oob_data++; i--; reallen++;
+							}
+							if (i & 1)
+								this->read_buf(mtd, oob_data, i - 1);
+							else
+								this->read_buf(mtd, oob_data, i);
+ 						}
+						else
+							this->read_buf(mtd, oob_data, i);
+						reallen += i;
+					}
+					if (oob_buf + len == oob_data + i) {
+						read += i;
+						goto out;
+	 				}
+					break;
+ 				}
+				read += i;
+				oob_data += i;
+			}
+		}
+out:
+
+		/* Apply delay or wait for ready/busy pin
+		 * Do this before the AUTOINCR check, so no problems
+		 * arise if a chip which does auto increment
+		 * is marked as NOAUTOINCR by the board driver.
+		*/
+		if (!this->dev_ready)
+			udelay (this->chip_delay);
+		else
+			nand_wait_ready(mtd);
+
+		if (read == len)
+			break;
+
+		/* For subsequent reads align to page boundary. */
+		reallen = col = 0;
+		/* Increment page address */
+		realpage++;
+
+		page = realpage & this->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!page) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+		}
+		/* Check, if the chip supports auto page increment
+		 * or if we have hit a block boundary.
+		*/
+		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+			sndcmd = 1;
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+
+	*retlen = read;
+	/*
+	 * Return success
+	 */
+	return 0;
+
+}
+
+/**
+ * nand_read_oobfree - [MTD Interface] NAND read free out-of-band
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ *
+ * NAND read free out-of-band data from the spare area
+ */
+static int nand_read_oobfree (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * oob_buf)
+{
+
+	int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1, reallen = 0;
+	int read = 0;
+	struct nand_chip *this = mtd->priv;
+	u_char *oob_data = oob_buf;
+        int 	eccsteps;
+	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+
+	DEBUG (MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n", __FUNCTION__, (unsigned int) from, (int) len);
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		*retlen = 0;
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd, FL_READING);
+
+	/* Select the NAND device */
+	chipnr = (int)(from >> this->chip_shift);
+	this->select_chip(mtd, chipnr);
+
+	/* First we calculate the starting page */
+	realpage = (int) (from >> this->page_shift);
+	page = realpage & this->pagemask;
+
+	/* Get raw starting column */
+	col = from & (mtd->oobblock - 1);
+
+	if (col > mtd->oobavail) {
+		*retlen = 0;
+		return -EINVAL;
+	}
+
+	end = mtd->oobblock;
+	ecc = this->eccsize;
+
+	/* Loop until all data read */
+	while (read < len) {
+		if (this->eccmode == NAND_ECC_NONE) {
+			int thislen = mtd->oobsize - col;
+			if (sndcmd) {
+				this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
+				col = 0;
+				sndcmd = 0;
+			}
+			thislen = min_t(int, thislen, len);
+			this->read_buf(mtd, &oob_buf[read], thislen);
+			read += thislen;
+		} else {
+			/* Check, if we must send the read command */
+			if (sndcmd) {
+				this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+				sndcmd = 0;
+			}
+
+			eccsteps = this->eccsteps;
+			for (j = 0; this->layout[j].length; j++) {
+				i = 0;
+				switch (this->layout[j].type) {
+				case ITEM_TYPE_DATA:
+				case ITEM_TYPE_ECC:
+				case ITEM_TYPE_OOB:
+					DEBUG (MTD_DEBUG_LEVEL3, "%s: dummy data read\n", __FUNCTION__);
+					reallen += this->layout[j].length;
+					if (this->options & NAND_BUSWIDTH_16)
+						this->cmdfunc (mtd, NAND_CMD_READ0, reallen & ~1, page);
+					else
+						this->cmdfunc (mtd, NAND_CMD_READ0, reallen, page);
+					break;
+
+				case ITEM_TYPE_OOBFREE:
 					DEBUG (MTD_DEBUG_LEVEL3, "%s: %s bytes read\n", __FUNCTION__, this->layout[j].type == ITEM_TYPE_ECC ? "ecc" : "oob");
 					i = min_t(int, col, this->layout[j].length);
 					if (i) {
@@ -1982,6 +2157,7 @@
 
 			case ITEM_TYPE_ECC:
 			case ITEM_TYPE_OOB:
+			case ITEM_TYPE_OOBFREE:
 				if (this->layout[j].type == ITEM_TYPE_ECC)
 					this->enable_hwecc(mtd, NAND_ECC_WRITESYN);
 				else
@@ -2061,6 +2237,178 @@
 }
 
 /**
+ * nand_write_oobfree - [MTD Interface] NAND write free out-of-band
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @len:	number of bytes to write
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
+ *
+ * NAND write free out-of-band area
+ */
+static int nand_write_oobfree (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * oob_buf)
+{
+	int column, page, status, ret = -EIO, chipnr, eccsteps; 
+	struct nand_chip *this = mtd->priv;
+
+	DEBUG (MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", __FUNCTION__, (unsigned int) to, (int) len);
+
+	/* Shift to get page */
+	page = (int) (to >> this->page_shift);
+	chipnr = (int) (to >> this->chip_shift);
+
+	/* Mask to get column */
+	column = to & (mtd->oobsize - 1);
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Do not allow write past end of page */
+	if ((column + len) > mtd->oobavail) {
+		DEBUG (MTD_DEBUG_LEVEL0, "%s: Attempt to write past end of page\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd, FL_WRITING);
+
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Reset the chip. Some chips (like the Toshiba TC5832DC found
+	   in one of my DiskOnChip 2000 test units) will clear the whole
+	   data page too if we don't do this. I have no clue why, but
+	   I seem to have 'fixed' it in the doc2000 driver in
+	   August 1999.  dwmw2. */
+	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd))
+		goto out;
+
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page == this->pagebuf)
+		this->pagebuf = -1;
+
+	if (this->eccmode == NAND_ECC_NONE) {
+		if (NAND_MUST_PAD(this)) {
+			/* Write out desired data */
+			this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
+			/* prepad 0xff for partial programming */
+			this->write_buf(mtd, ffchars, column);
+			/* write data */
+			this->write_buf(mtd, oob_buf, len);
+			/* postpad 0xff for partial programming */
+			this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
+		} else {
+			/* Write out desired data */
+			this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
+			/* write data */
+			this->write_buf(mtd, oob_buf, len);
+		}
+	} else {
+		int i = 0, j = 0;
+		int fflen = 0, old_fflen = 0, ooblen = 0;
+
+		/* Write out desired data */
+		this->cmdfunc (mtd, NAND_CMD_SEQIN, 0, page & this->pagemask);
+
+		eccsteps = this->eccsteps;
+		for (j = 0; this->layout[j].length; j++) {
+			switch (this->layout[j].type) {
+			case ITEM_TYPE_DATA:
+			case ITEM_TYPE_ECC:
+			case ITEM_TYPE_OOB:
+				if (this->options & NAND_COMPLEX_OOB_WRITE) {
+					this->enable_hwecc(mtd, NAND_ECC_WRITE);
+					this->write_buf(mtd, ffchars, this->layout[j].length);
+					fflen += this->layout[j].length;
+				} else {
+					if (old_fflen < fflen) {
+						this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+						status = this->waitfunc (mtd, this, FL_WRITING);
+						if (status & NAND_STATUS_FAIL) {
+							DEBUG (MTD_DEBUG_LEVEL0, "%s: Failed write, page 0x%08x\n", __FUNCTION__, page);
+							ret = -EIO;
+							goto out;
+						}
+					}
+					fflen += this->layout[j].length;
+					if (this->options & NAND_BUSWIDTH_16 && (fflen + ooblen) & 1)
+						this->cmdfunc (mtd, NAND_CMD_SEQIN, fflen + ooblen - 1, page & this->pagemask);
+					else
+						this->cmdfunc (mtd, NAND_CMD_SEQIN, fflen + ooblen, page & this->pagemask);
+					old_fflen = fflen;
+				}
+				break;
+
+			case ITEM_TYPE_OOBFREE:
+				if (this->layout[j].type == ITEM_TYPE_ECC)
+					this->enable_hwecc(mtd, NAND_ECC_WRITESYN);
+				else
+					this->enable_hwecc(mtd, NAND_ECC_WRITEOOB);
+				i = min_t(int, column, this->layout[j].length);
+				if (i) {
+					if (this->options & NAND_BUSWIDTH_16 && i & 1)
+						i--;
+					if (i == 0) {
+						this->write_word(mtd, cpu_to_le16((oob_buf[0] >> 8) || 0xff));
+						i++;
+						ooblen++;
+					} else
+						this->write_buf(mtd, ffchars, i);
+				}
+				column -= i;
+				fflen += i;
+				i = min_t(int, len + column - ooblen, this->layout[j].length - i);
+				if (i) {
+					if (column) {
+						this->write_word(mtd, cpu_to_le16((oob_buf[0] >> 8) || 0xff));
+						i--;
+						ooblen++;
+					}
+					if (i & 1)
+						i--;
+					this->write_buf(mtd, &oob_buf[ooblen], i);
+				}
+				ooblen += i;
+				if (ooblen == len - 1) {
+					this->write_word(mtd, cpu_to_le16(oob_buf[ooblen]) || 0xff00);
+					ooblen += 2;
+				}
+				if (ooblen >= len) {
+					if (NAND_MUST_PAD(this))
+						this->write_buf(mtd, ffchars, mtd->oobsize + mtd->oobblock - fflen - ooblen);
+					goto finish;
+				}
+				break;
+			}
+		}
+	}
+finish:
+	/* Send command to program the OOB data */
+	this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+	status = this->waitfunc (mtd, this, FL_WRITING);
+
+	/* See if device thinks it succeeded */
+	if (status & NAND_STATUS_FAIL) {
+		DEBUG (MTD_DEBUG_LEVEL0, "%s: Failed write, page 0x%08x\n", __FUNCTION__, page);
+		ret = -EIO;
+		goto out;
+	}
+	/* Return happy */
+	*retlen = len;
+
+	ret = 0;
+out:
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+
+	return ret;
+}
+
+/**
  * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc
  * @mtd:	MTD device structure
  * @vecs:	the iovectors to write
@@ -2562,15 +2910,15 @@
 			int len = oob->oobfree[oobcur][1];
 			oobfreesize += this->layout[i].length;
 			oobcur++;
-			if (i > 0 && this->layout[i-1].type == ITEM_TYPE_OOB) {
+			if (i > 0 && this->layout[i-1].type == ITEM_TYPE_OOBFREE) {
 				i--;
 				cur -= this->layout[i].length;
 				this->layout[i].length += len;
-				DEBUG (MTD_DEBUG_LEVEL3, "fill_autooob_layout: oob concatenated, aggregate length %d\n", this->layout[i].length);
+				DEBUG (MTD_DEBUG_LEVEL3, "fill_autooob_layout: oobfree concatenated, aggregate length %d\n", this->layout[i].length);
 			} else {
-				this->layout[i].type = ITEM_TYPE_OOB;
+				this->layout[i].type = ITEM_TYPE_OOBFREE;
 				this->layout[i].length = len;
-				DEBUG (MTD_DEBUG_LEVEL3, "fill_autooob_layout: oob type, length %d\n", this->layout[i].length);
+				DEBUG (MTD_DEBUG_LEVEL3, "fill_autooob_layout: oobfree type, length %d\n", this->layout[i].length);
 			}
 		} else if (oob->eccpos[eccpos] == cur) {
 			int eccpos_cur = eccpos;
@@ -2978,6 +3326,8 @@
 	mtd->write_ecc = nand_write_ecc;
 	mtd->read_oob = nand_read_oob;
 	mtd->write_oob = nand_write_oob;
+	mtd->read_oobfree = nand_read_oobfree;
+	mtd->write_oobfree = nand_write_oobfree;
 	mtd->readv = NULL;
 	mtd->writev = nand_writev;
 	mtd->writev_ecc = nand_writev_ecc;
Index: include/linux/mtd/mtd.h
===================================================================
RCS file: /home/cvs/mtd/include/linux/mtd/mtd.h,v
retrieving revision 1.62
diff -u -r1.62 mtd.h
--- include/linux/mtd/mtd.h	29 Nov 2005 20:01:30 -0000	1.62
+++ include/linux/mtd/mtd.h	22 Dec 2005 12:03:22 -0000
@@ -118,6 +118,9 @@
 	int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
 	int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
 
+	int (*read_oobfree) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+	int (*write_oobfree) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+
 	/*
 	 * Methods to access the protection register area, present in some
 	 * flash devices. The user data is one time programmable but the
Index: include/linux/mtd/nand.h
===================================================================
RCS file: /home/cvs/mtd/include/linux/mtd/nand.h,v
retrieving revision 1.77
diff -u -r1.77 nand.h
--- include/linux/mtd/nand.h	16 Dec 2005 15:41:33 -0000	1.77
+++ include/linux/mtd/nand.h	22 Dec 2005 12:03:22 -0000
@@ -171,6 +171,7 @@
 	enum {
 		ITEM_TYPE_DATA,
 		ITEM_TYPE_OOB,
+		ITEM_TYPE_OOBFREE,
 		ITEM_TYPE_ECC,
 	} type;
 };
Index: include/mtd/mtd-abi.h
===================================================================
RCS file: /home/cvs/mtd/include/mtd/mtd-abi.h,v
retrieving revision 1.13
diff -u -r1.13 mtd-abi.h
--- include/mtd/mtd-abi.h	7 Nov 2005 11:14:56 -0000	1.13
+++ include/mtd/mtd-abi.h	22 Dec 2005 12:03:22 -0000
@@ -110,6 +110,9 @@
 #define OTPGETREGIONCOUNT	_IOW('M', 14, int)
 #define OTPGETREGIONINFO	_IOW('M', 15, struct otp_info)
 #define OTPLOCK		_IOR('M', 16, struct otp_info)
+#define MEMGETOOBAVAIL		_IOR('M', 17, uint32_t)
+#define MEMWRITEOOBFREE         _IOWR('M', 18, struct mtd_oob_buf)
+#define MEMREADOOBFREE          _IOWR('M', 19, struct mtd_oob_buf)
 
 struct nand_oobinfo {
 	uint32_t useecc;





More information about the linux-mtd mailing list