OneNAND: Add support for auto-placement of out-of-band data
PATCH 1/1 OneNAND: Add support for auto-placement of out-of-band data
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND. Note that MTD_OOB_RAW is still not supported.
---drivers/mtd/onenand/onenand_base.c | 144 ++++++++++++++++++++++++++++++++---
1 files changed, 131 insertions(+), 13 deletions(-)
drivers/mtd/onenand/onenand_base.c +0 -0
@@ -787,17 +787,61 @@ static int onenand_read(struct mtd_info }
/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + *thislen;
+ int lastgap = 0, len = 0;
+ uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (readend > mtd->oobsize)
+ readend = mtd->oobsize;
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
+ readcol, readend - readcol);
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+ return 0;
+}
+
+/**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data
+ * @param mode operation mode
*
* OneNAND read out-of-band data from the spare area
*/
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
int read = 0, thislen, column;
@@ -832,7 +876,10 @@ int onenand_do_read_oob(struct mtd_info
ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, &thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);@@ -871,10 +918,18 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -1060,17 +1115,58 @@ static int onenand_write(struct mtd_info }
/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static void onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + *thislen;
+ int lastgap = 0, len = 0;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (writeend > mtd->oobsize)
+ writeend = mtd->oobsize;
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+}
+
+/**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
int column, ret = 0;
@@ -1103,7 +1199,10 @@ static int onenand_do_write_oob(struct m
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(this->page_buf, 0xff, mtd->oobsize);- memcpy(this->page_buf + column, buf, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, this->page_buf, buf, column, &thislen);
+ else
+ memcpy(this->page_buf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); @@ -1116,7 +1215,7 @@ static int onenand_do_write_oob(struct m goto out; }
- ret = onenand_verify_oob(mtd, buf, to, thislen);
+ ret = onenand_verify_oob(mtd, this->page_buf, to, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
goto out;@@ -1149,10 +1248,18 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
@@ -1318,7 +1425,7 @@ static int onenand_default_block_markbad
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+ return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
}
/**
@@ -1612,7 +1719,7 @@ static int do_otp_lock(struct mtd_info *
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING);
- ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+ ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2020,6 +2127,7 @@ static void onenand_resume(struct mtd_in
*/
int onenand_scan(struct mtd_info *mtd, int maxchips) {
+ int i; struct onenand_chip *this = mtd->priv; if (!this->read_word) @@ -2091,6 +2199,16 @@ int onenand_scan(struct mtd_info *mtd, i } this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ this->ecclayout->oobavail = 0;
+ for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ this->ecclayout->oobavail +=
+ this->ecclayout->oobfree[i].length;
+
mtd->ecclayout = this->ecclayout; /* Fill in remaining MTD driver data */
Signature · 1 line
1.4.3
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND. Note that MTD_OOB_RAW is still not supported.
---drivers/mtd/onenand/onenand_base.c | 144 ++++++++++++++++++++++++++++++++---drivers/mtd/onenand/onenand_bbt.c | 4 +-
2 files changed, 133 insertions(+), 15 deletions(-)
drivers/mtd/onenand/onenand_base.c +0 -0
@@ -787,17 +787,61 @@ static int onenand_read(struct mtd_info }
/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + *thislen;
+ int lastgap = 0, len = 0;
+ uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (readend > mtd->oobsize)
+ readend = mtd->oobsize;
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
+ readcol, readend - readcol);
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+ return 0;
+}
+
+/**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data
+ * @param mode operation mode
*
* OneNAND read out-of-band data from the spare area
*/
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
int read = 0, thislen, column;
@@ -832,7 +876,10 @@ int onenand_do_read_oob(struct mtd_info
ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, &thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);@@ -871,10 +918,18 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -1060,17 +1115,58 @@ static int onenand_write(struct mtd_info }
/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static void onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + *thislen;
+ int lastgap = 0, len = 0;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (writeend > mtd->oobsize)
+ writeend = mtd->oobsize;
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+}
+
+/**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
int column, ret = 0;
@@ -1103,7 +1199,10 @@ static int onenand_do_write_oob(struct m
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(this->page_buf, 0xff, mtd->oobsize);- memcpy(this->page_buf + column, buf, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, this->page_buf, buf, column, &thislen);
+ else
+ memcpy(this->page_buf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); @@ -1116,7 +1215,7 @@ static int onenand_do_write_oob(struct m goto out; }
- ret = onenand_verify_oob(mtd, buf, to, thislen);
+ ret = onenand_verify_oob(mtd, this->page_buf, to, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
goto out;@@ -1149,10 +1248,18 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
@@ -1318,7 +1425,7 @@ static int onenand_default_block_markbad
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+ return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
}
/**
@@ -1612,7 +1719,7 @@ static int do_otp_lock(struct mtd_info *
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING);
- ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+ ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2020,6 +2127,7 @@ static void onenand_resume(struct mtd_in
*/
int onenand_scan(struct mtd_info *mtd, int maxchips) {
+ int i; struct onenand_chip *this = mtd->priv; if (!this->read_word) @@ -2091,6 +2199,16 @@ int onenand_scan(struct mtd_info *mtd, i } this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ this->ecclayout->oobavail = 0;
+ for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ this->ecclayout->oobavail +=
+ this->ecclayout->oobfree[i].length;
+
mtd->ecclayout = this->ecclayout; /* Fill in remaining MTD driver data */
drivers/mtd/onenand/onenand_bbt.c +0 -0
@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h>
#include <linux/mtd/compatmac.h>
extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode);
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m
/* No need to read pages fully, * just read required OOB bytes */ ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
- readlen, &retlen, &buf[0]);
+ readlen, &retlen, &buf[0], MTD_OOB_PLACE);
/* If it is a initial bad block, just ignore it */ if (ret && !(ret & ONENAND_CTRL_LOAD))
Signature · 1 line
1.4.3
RE: [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data
Hi Adiran
Quoted 4 lines
Enable the use of oob operation mode
MTD_OOB_AUTOwith OneNAND. Note thatMTD_OOB_RAWis still not supported.
It's cool. however how can we test this one?
Just modify JFFS2 oob mode from OOB_PLACE to OOB_AUTO?
Thank you,
Kyungmin Park
Re: [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data
Quoted 1 line · ext Kyungmin Park
It's cool. however how can we test this one?
I have a test module, that I will post.
However I just noticed lots of bugs so please wait for a corrected version of the patch.
Re: [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data
Quoted 1 line · ext Adrian Hunter
However I just noticed lots of bugs so please wait for a corrected version of the patch.
Corrected patch below
Quoted 1 line
From
9a1a24325059ba00a64faeabac8a1f0da9febe6fMon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Mon, 29 Jan 2007 17:28:54 +0200
Subject: [MTD] OneNAND: Add support for auto-placement of out-of-band data
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND. Note that MTD_OOB_RAW is still not supported.
---drivers/mtd/onenand/onenand_base.c | 211 ++++++++++++++++++++++++++++++------drivers/mtd/onenand/onenand_bbt.c | 4 +-
2 files changed, 178 insertions(+), 37 deletions(-)
drivers/mtd/onenand/onenand_base.c +0 -0
@@ -787,20 +787,65 @@ static int onenand_read(struct mtd_info }
/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + *thislen;
+ int lastgap = 0, len = 0;
+ uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (readend > mtd->oobsize)
+ readend = mtd->oobsize;
+ if (readcol < readend)
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
+ readcol, readend - readcol);
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+ return 0;
+}
+
+/**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data
+ * @param mode operation mode
*
* OneNAND read out-of-band data from the spare area
*/
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
- int read = 0, thislen, column;
+ int read = 0, thislen, column, oobsize;
int ret = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
@@ -808,21 +853,33 @@ int onenand_do_read_oob(struct mtd_info
/* Initialize return length value */
*retlen = 0;+ if (mode == MTD_OOB_AUTO)
+ oobsize = this->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = from & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n");
+ return -EINVAL;
+ }
+
/* Do not allow reads past end of device */
- if (unlikely((from + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n");
+ if (unlikely(from >= mtd->size ||
+ len > ((mtd->size >> this->page_shift) -
+ (from >> this->page_shift)) * oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n");
return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING);
- column = from & (mtd->oobsize - 1);
-
while (read < len) {
cond_resched();- thislen = mtd->oobsize - column;
+ thislen = oobsize - column; thislen = min_t(int, thislen, len); this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); @@ -832,7 +889,10 @@ int onenand_do_read_oob(struct mtd_info ret = this->wait(mtd, FL_READING); /* First copy data and check return value for ECC handling */
- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, &thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);@@ -871,10 +931,18 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -883,14 +951,12 @@ #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
* @param mtd MTD device structure
* @param buf the databuffer to verify
* @param to offset to read from
- * @param len number of bytes to read and compare
*
*/
-static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) +static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
{
struct onenand_chip *this = mtd->priv;
- char *readp = this->page_buf;
- int column = to & (mtd->oobsize - 1);
+ char *readp = this->page_buf + mtd->writesize;
int status, i;
this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@@ -899,9 +965,8 @@ static int onenand_verify_oob(struct mtd
if (status)
return status;- this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len);
-
- for(i = 0; i < len; i++)
+ this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize);
+ for(i = 0; i < mtd->oobsize; i++)
if (buf[i] != 0xFF && buf[i] != readp[i]) return -EBADMSG;
@@ -1060,20 +1125,61 @@ static int onenand_write(struct mtd_info }
/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static void onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + *thislen;
+ int lastgap = 0, len = 0;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (writeend > mtd->oobsize)
+ writeend = mtd->oobsize;
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+}
+
+/**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
- int column, ret = 0;
+ int column, ret = 0, oobsize;
int written = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -1081,9 +1187,23 @@ static int onenand_do_write_oob(struct m
/* Initialize retlen, in case of early exit */
*retlen = 0;- /* Do not allow writes past end of device */
- if (unlikely((to + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n");
+ if (mode == MTD_OOB_AUTO)
+ oobsize = this->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ len > ((mtd->size >> this->page_shift) -
+ (to >> this->page_shift)) * oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n");
return -EINVAL; }
@@ -1092,18 +1212,19 @@ static int onenand_do_write_oob(struct m
/* Loop until all data write */
while (written < len) {- int thislen = min_t(int, mtd->oobsize, len - written);
+ int thislen = min_t(int, oobsize, len - written);
cond_resched();
- column = to & (mtd->oobsize - 1);
-
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); /* We send data to spare ram with oobsize * to prevent byte access */ memset(this->page_buf, 0xff, mtd->oobsize);
- memcpy(this->page_buf + column, buf, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, this->page_buf, buf, column, &thislen);
+ else
+ memcpy(this->page_buf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1112,11 +1233,11 @@ static int onenand_do_write_oob(struct m
ret = this->wait(mtd, FL_WRITING);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret);
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret);
goto out;
}- ret = onenand_verify_oob(mtd, buf, to, thislen);
+ ret = onenand_verify_oob(mtd, this->page_buf, to);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
goto out;
@@ -1127,8 +1248,9 @@ static int onenand_do_write_oob(struct m
if (written == len)
break;- to += thislen;
+ to += mtd->writesize; buf += thislen; + column = 0; }
out:
@@ -1149,10 +1271,18 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
@@ -1318,7 +1448,7 @@ static int onenand_default_block_markbad
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+ return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
}
/**
@@ -1612,7 +1742,7 @@ static int do_otp_lock(struct mtd_info *
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING);
- ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+ ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2020,6 +2150,7 @@ static void onenand_resume(struct mtd_in
*/
int onenand_scan(struct mtd_info *mtd, int maxchips) {
+ int i; struct onenand_chip *this = mtd->priv; if (!this->read_word) @@ -2091,6 +2222,16 @@ int onenand_scan(struct mtd_info *mtd, i } this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ this->ecclayout->oobavail = 0;
+ for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ this->ecclayout->oobavail +=
+ this->ecclayout->oobfree[i].length;
+
mtd->ecclayout = this->ecclayout; /* Fill in remaining MTD driver data */
drivers/mtd/onenand/onenand_bbt.c +0 -0
@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h>
#include <linux/mtd/compatmac.h>
extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode);
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m
/* No need to read pages fully, * just read required OOB bytes */ ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
- readlen, &retlen, &buf[0]);
+ readlen, &retlen, &buf[0], MTD_OOB_PLACE);
/* If it is a initial bad block, just ignore it */ if (ret && !(ret & ONENAND_CTRL_LOAD))
Signature · 1 line
1.4.3
RE: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data
Hi,
Now I tested it and it works well,
It looks good except some minor error condition handling and type mismatch. (see below)
Another interest things are nand is not passed in this test program. :)
/ # cat /proc/mtd
dev: size erasesize name
mtd6: 00800000 00002000 "NAND simulator partition" / # insmod /mtd-utils/oobtest.ko dev=6 Using /mtd-utils/oobtest.ko
============================================================================
====
oobtest: dev = 6
oobtest: oob available per page = 8
oobtest: Test 1 of 5
oobtest: erasing
oobtest: erased 0
oobtest: erased 256
oobtest: erased 512
oobtest: erased 768
oobtest: erased 1024
oobtest: writing
oobtest: written 0
oobtest: written 256
oobtest: written 512
oobtest: written 768
oobtest: written 1024
oobtest: verifying
oobtest: verified 0
oobtest: verified 256
oobtest: verified 512
oobtest: verified 768
oobtest: verified 1024
oobtest: Test 2 of 5
oobtest: erasing
oobtest: erased 0
oobtest: erased 256
oobtest: erased 512
oobtest: erased 768
oobtest: erased 1024
oobtest: writing
oobtest: error: writeoob failed at 0x00000000
oobtest: error -22 occurred
============================================================================
====
Thank you,
Kyungmin Park
Quoted 19 lines
(quoted diff) +15
-0@@ -787,20 +787,65 @@ static int onenand_read(struct mtd_info } /** + * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer + * @param mtd MTD device structure + * @param buf destination address + * @param column oob offset to read from + * @param thislen oob length to read + */ +static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, + int *thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int readcol = column; + int readend = column + *thislen; + int lastgap = 0, len = 0; + uint8_t *oob_buf = this->page_buf + mtd->writesize;
Review static int onenand_read(struct mtd_info } · line 814
Quoted 10 lines
(quoted diff) +10
-0+ + for (free = this->ecclayout->oobfree; free->length; ++free) { + if (readcol >= lastgap) + readcol += free->offset - lastgap; + if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + if (readend > mtd->oobsize) + readend = mtd->oobsize;
When this condition occurs?
Review static int onenand_read(struct mtd_info } · line 818
Quoted 4 lines
(quoted diff) +3
-0+ if (readcol < readend) + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol, + readcol, readend - readcol);
ditto, what happend If readcol is grater than readend ?
Review static int onenand_read(struct mtd_info } · line 830
Quoted 12 lines
(quoted diff) +12
-0+ for (free = this->ecclayout->oobfree; free->length; ++free) { + int free_end = free->offset + free->length; + if (free->offset < readend && free_end > readcol) { + int st = max_t(int,free->offset,readcol); + int ed = min_t(int,free_end,readend); + int n = ed - st; + memcpy(buf, oob_buf + st, n); + len += n; + buf += n; + } + } + *thislen = len;
Why do we re-assign thislen value. In test program. it never changed.
Review static int onenand_read(struct mtd_info } · line 839
Quoted 11 lines
(quoted diff) +5
-2+ return 0; +} + static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - BUG_ON(ops->mode != MTD_OOB_PLACE); - + switch (ops->mode) + {
=> swich (ops->mode) {
Quoted 18 lines
(quoted diff) +10
-1+ case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + case MTD_OOB_RAW: + return -EINVAL; /* Not implemented yet */ + default: + return -EINVAL; + } return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); } +static void onenand_fill_auto_oob(struct mtd_info *mtd,
u_char*oob_buf,(quoted diff) +1
-0+ const u_char *buf, int
Quoted 1 line
column, int *thislen) {
Use int return type for compatability.
Quoted 5 lines
(quoted diff) +5
-0+ struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int writecol = column; + int writeend = column + *thislen; + int lastgap = 0, len = 0;
Review
Quoted 10 lines
(quoted diff) +10
-0+ + for (free = this->ecclayout->oobfree; free->length; ++free) { + if (writecol >= lastgap) + writecol += free->offset - lastgap; + if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + if (writeend > mtd->oobsize) + writeend = mtd->oobsize;
It is also don't occurs if we need it use 'unlikely' macro
Review
Quoted 12 lines
(quoted diff) +12
-0+ for (free = this->ecclayout->oobfree; free->length; ++free) { + int free_end = free->offset + free->length; + if (free->offset < writeend && free_end > writecol) { + int st = max_t(int,free->offset,writecol); + int ed = min_t(int,free_end,writeend); + int n = ed - st; + memcpy(oob_buf + st, buf, n); + len += n; + buf += n; + } + } + *thislen = len;
add return 0;
Quoted 215 lines
(quoted diff) +3
-0+} + +/** * onenand_do_write_oob - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the numberof written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(structmtd_info*mtd,loff_t
to,size_tlen,(quoted diff) +1
-1- size_t *retlen, const u_char *buf) + size_t *retlen, const u_char*buf,
mtd_oob_mode_tmode)
{(quoted diff) +1
-1struct onenand_chip *this = mtd->priv; - int column, ret = 0; + int column, ret = 0, oobsize; int written = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to =0x%08x, len = %i\n", (unsigned int) to, (int) len); @@ -1081,9 +1187,23 @@ static int onenand_do_write_oob(struct m
(quoted diff) +0
-3/* Initialize retlen, in case of early exit */ *retlen = 0; - /* Do not allow writes past end of device */ - if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob:Attempt write to past end of device\n");
(quoted diff) +9
-0+ if (mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob:Attempted to start write outside oob\n");
(quoted diff) +8
-0+ return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + len > ((mtd->size >> this->page_shift) - + (to >> this->page_shift)) * oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob:Attempted to write past
(quoted diff) +6
-4+end of device\n"); return -EINVAL; }@@ -1092,18 +1212,19 @@ static int onenand_do_write_oob(struct m /* Loop until all data write */ while (written < len) { - int thislen = min_t(int, mtd->oobsize, len - written); + int thislen = min_t(int, oobsize, len - written); cond_resched(); - column = to & (mtd->oobsize - 1); - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); /* We send data to spare ram with oobsize * to prevent byte access */ memset(this->page_buf, 0xff, mtd->oobsize); - memcpy(this->page_buf + column, buf, thislen); + if (mode == MTD_OOB_AUTO) + onenand_fill_auto_oob(mtd, this->page_buf, buf, column, &thislen); + else + memcpy(this->page_buf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM,
this->page_buf, 0,mtd->oobsize);this->command(mtd,
ONENAND_CMD_PROGOOB, to,mtd->oobsize); @@ -1112,11 +1233,11 @@ static int onenand_do_write_oob(struct m(quoted diff) +0
-1ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0,"
onenand_write_oob: write filaed %d\n", ret);(quoted diff) +1
-0+ DEBUG(MTD_DEBUG_LEVEL0,"
onenand_write_oob: write failed %d\n",(quoted diff) +2
-1+ret); goto out; } - ret = onenand_verify_oob(mtd, buf, to, thislen); + ret = onenand_verify_oob(mtd, this->page_buf, to); if (ret) { DEBUG(MTD_DEBUG_LEVEL0,"
onenand_write_oob: verify failed %d\n", ret);(quoted diff) +13
-4goto out;@@ -1127,8 +1248,9 @@ static int onenand_do_write_oob(struct m if (written == len) break; - to += thislen; + to += mtd->writesize; buf += thislen; + column = 0; } out:@@ -1149,10 +1271,18 @@ out: static int onenand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - BUG_ON(ops->mode != MTD_OOB_PLACE); - + switch (ops->mode) + { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + case MTD_OOB_RAW: + return -EINVAL; /* Not implemented yet */ + default: + return -EINVAL; + } return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); }/**
(quoted diff) +0
-0@@ -1318,7 +1448,7 @@ static int onenand_default_block_markbad /* We write two bytes, so we dont have to mess with16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);(quoted diff) +16
-2- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); + return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, + MTD_OOB_PLACE); } /**@@ -1612,7 +1742,7 @@ static int do_otp_lock(struct mtd_info * this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_do_write_oob(mtd, from, len, retlen, buf); + ret = onenand_do_write_oob(mtd, from, len, retlen, buf, +MTD_OOB_PLACE); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2020,6 +2150,7 @@ static void onenand_resume(struct mtd_in */ int onenand_scan(struct mtd_info *mtd, int maxchips) { + int i; struct onenand_chip *this = mtd->priv; if (!this->read_word)@@ -2091,6 +2222,16 @@ int onenand_scan(struct mtd_info *mtd, i } this->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + this->ecclayout->oobavail = 0; + for (i = 0; this->ecclayout->oobfree[i].length; i++) + this->ecclayout->oobavail += + this->ecclayout->oobfree[i].length; + mtd->ecclayout = this->ecclayout; /* Fill in remaining MTD driver data */ diff --git
a/drivers/mtd/onenand/onenand_bbt.cb/drivers/mtd/onenand/onenand_bbt.c
index aa46b7f..acea9a1 100644drivers/mtd/onenand/onenand_bbt.c +0
-0@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h> #include<
linux/mtd/compatmac.h>extern int onenand_do_read_oob(struct
mtd_info*mtd,loff_t
from,size_tlen,drivers/mtd/onenand/onenand_bbt.c +1
-1- size_t *retlen, u_char *buf); + size_t *retlen, u_char *buf,
mtd_oob_mode_tmode);/**
*check_short_pattern- [GENERIC] check if a pattern is in
the buffer @@ -91,7 +91,7 @@ static int create_bbt(structmtd_info*mdrivers/mtd/onenand/onenand_bbt.c +0
-0/* No need to read pages fully, * just read required OOB bytes */ ret = onenand_do_read_oob(mtd, from + j*
mtd->writesize+bd->offs,drivers/mtd/onenand/onenand_bbt.c +0
-1- readlen,&retlen, &buf[0]);
drivers/mtd/onenand/onenand_bbt.c +1
-0+ readlen,&retlen, &buf[0],
MTD_OOB_PLACE);/* If it is a initial bad block, just
ignore it */
if (ret && !(ret &ONENAND_CTRL_LOAD))
--
1.4.3
Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data
Quoted 1 line · ext Kyungmin Park
It looks good except some minor error condition handling and type mismatch.
Yes the original version of the patch did not have enough validation. When I added the validation, those conditions could not happen anymore, so I have now taken them out.
I have made the changes. The new patch is below.
Quoted 1 line
Another interest things are nand is not passed in this test program. :)
Test 2 does unaligned writes and reads of odd sizes. Presumably nand won't accept that.
Quoted 1 line
From
b0a39f5eb3151fad2605f96b8032828586162bfdMon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Wed, 31 Jan 2007 10:24:43 +0200
Subject: [MTD] OneNAND: Add support for auto-placement of out-of-band data
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND. Note that MTD_OOB_RAW is still not supported.
--- drivers/mtd/onenand/onenand_base.c | 204 +++++++++++++++++++++++++++++------ drivers/mtd/onenand/onenand_bbt.c | 4 +- 2 files changed, 171 insertions(+), 37 deletions(-)
drivers/mtd/onenand/onenand_base.c +169 -35
@@ -787,20 +787,60 @@ static int onenand_read(struct mtd_info }/** +* onenand_transfer_auto_oob - [Internal] oob auto-placement transfer +* @param mtd MTD device structure +* @param buf destination address +* @param column oob offset to read from +* @param thislen oob length to read +*/ +static int onenand_transfer_auto_oob (struct mtd_info *mtd,uint8_t *buf,int column, +int thislen) +{ +struct onenand_chip *this = mtd->priv; +struct nand_oobfree *free; +int readcol = column; +int readend = column + thislen; +int lastgap =0 ; +uint8_t *oob_buf = this->page_buf + mtd->writesize; + +for (free = this->ecclayout->oobfree; free->length; ++free) { +if (readcol >= lastgap) + readcol += free->offset - lastgap; +if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + this->read_bufferram (mtd,ONENAND_SPARERAM , oob_buf + readcol, + readcol, readend - readcol); +for (free = this->ecclayout->oobfree; free->length; ++free) { +int free_end = free->offset + free->length; +if (free->offset < readend && free_end > readcol) { +int st =max_t (int,free->offset,readcol); +int ed =min_t (int,free_end,readend); +int n = ed - st; +memcpy (buf, oob_buf + st, n); + buf += n; + } + } +return 0 ; +} + +/** * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data +* @param mode operation mode * * OneNAND read out-of-band data from the spare area */ int onenand_do_read_oob (struct mtd_info *mtd,loff_t from,size_t len, -size_t *retlen,u_char *buf) +size_t *retlen,u_char *buf,mtd_oob_mode_t mode) {struct onenand_chip *this = mtd->priv; -int read =0 , thislen, column; +int read =0 , thislen, column, oobsize;int ret =0 ;DEBUG (MTD_DEBUG_LEVEL3 ,"onenand_read_oob: from = 0x%08x, len = %i\n" , (unsigned int ) from, (int ) len);
@@ -808,21 +848,33 @@ int onenand_do_read_oob(struct mtd_info/* Initialize return length value */ *retlen =0 ; +if (mode ==MTD_OOB_AUTO ) + oobsize = this->ecclayout->oobavail; +else + oobsize = mtd->oobsize; + + column = from & (mtd->oobsize -1 ); + +if (unlikely (column >= oobsize)) { +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_read_oob: Attempted to start read outside oob\n" ); +return -EINVAL ; + } +/* Do not allow reads past end of device */ -if (unlikely ((from + len) > mtd->size)) { -DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_read_oob: Attempt read beyond end of device\n" ); +if (unlikely (from >= mtd->size || + len > ((mtd->size >> this->page_shift) - + (from >> this->page_shift)) * oobsize)) { +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_read_oob: Attempted to read beyond end of device\n" );return -EINVAL ; }/* Grab the lock and see if the device is available */ onenand_get_device (mtd,FL_READING ); - column = from & (mtd->oobsize -1 ); -while (read < len) {cond_resched (); - thislen = mtd->oobsize - column; + thislen = oobsize - column; thislen =min_t (int, thislen, len); this->command (mtd,ONENAND_CMD_READOOB , from, mtd->oobsize);
@@ -832,7 +884,10 @@ int onenand_do_read_oob(struct mtd_info ret = this->wait (mtd,FL_READING );/* First copy data and check return value for ECC handling */ - this->read_bufferram (mtd,ONENAND_SPARERAM , buf, column, thislen); +if (mode ==MTD_OOB_AUTO ) +onenand_transfer_auto_oob (mtd, buf, column, thislen); +else + this->read_bufferram (mtd,ONENAND_SPARERAM , buf, column, thislen);if (ret) {DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_read_oob: read failed = 0x%x\n" , ret);
@@ -871,10 +926,18 @@ out:static int onenand_read_oob (struct mtd_info *mtd,loff_t from,struct mtd_oob_ops *ops) { -BUG_ON (ops->mode !=MTD_OOB_PLACE ); - +switch (ops->mode) + { +case MTD_OOB_PLACE : +case MTD_OOB_AUTO : +break ; +case MTD_OOB_RAW : +return -EINVAL ;/* Not implemented yet */ +default : +return -EINVAL ; + }return onenand_do_read_oob (mtd, from + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); }#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -883,14 +946,12 @@ #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE* @param mtd MTD device structure * @param buf the databuffer to verify * @param to offset to read from -* @param len number of bytes to read and compare * */ -static int onenand_verify_oob (struct mtd_info *mtd,const u_char *buf,loff_t to,int len) +static int onenand_verify_oob (struct mtd_info *mtd,const u_char *buf,loff_t to) {struct onenand_chip *this = mtd->priv; -char *readp = this->page_buf; -int column = to & (mtd->oobsize -1 ); +char *readp = this->page_buf + mtd->writesize;int status, i; this->command (mtd,ONENAND_CMD_READOOB , to, mtd->oobsize);
@@ -899,9 +960,8 @@ static int onenand_verify_oob(struct mtdif (status)return status; - this->read_bufferram (mtd,ONENAND_SPARERAM , readp, column, len); - -for (i =0 ; i < len; i++) + this->read_bufferram (mtd,ONENAND_SPARERAM , readp,0 , mtd->oobsize); +for (i =0 ; i < mtd->oobsize; i++)if (buf[i] !=0xFF && buf[i] != readp[i])return -EBADMSG ;
@@ -1060,20 +1120,59 @@ static int onenand_write(struct mtd_info }/** +* onenand_fill_auto_oob - [Internal] oob auto-placement transfer +* @param mtd MTD device structure +* @param oob_buf oob buffer +* @param buf source address +* @param column oob offset to write to +* @param thislen oob length to write +*/ +static int onenand_fill_auto_oob (struct mtd_info *mtd,u_char *oob_buf, +const u_char *buf,int column,int thislen) +{ +struct onenand_chip *this = mtd->priv; +struct nand_oobfree *free; +int writecol = column; +int writeend = column + thislen; +int lastgap =0 ; + +for (free = this->ecclayout->oobfree; free->length; ++free) { +if (writecol >= lastgap) + writecol += free->offset - lastgap; +if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + writeend = mtd->oobsize; +for (free = this->ecclayout->oobfree; free->length; ++free) { +int free_end = free->offset + free->length; +if (free->offset < writeend && free_end > writecol) { +int st =max_t (int,free->offset,writecol); +int ed =min_t (int,free_end,writeend); +int n = ed - st; +memcpy (oob_buf + st, buf, n); + buf += n; + } + } +return 0 ; +} + +/** * onenand_do_write_oob - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write +* @param mode operation mode * * OneNAND write out-of-band */ static int onenand_do_write_oob (struct mtd_info *mtd,loff_t to,size_t len, -size_t *retlen,const u_char *buf) +size_t *retlen,const u_char *buf,mtd_oob_mode_t mode) {struct onenand_chip *this = mtd->priv; -int column, ret =0 ; +int column, ret =0 , oobsize;int written =0 ;DEBUG (MTD_DEBUG_LEVEL3 ,"onenand_write_oob: to = 0x%08x, len = %i\n" , (unsigned int ) to, (int ) len);
@@ -1081,9 +1180,23 @@ static int onenand_do_write_oob(struct m/* Initialize retlen, in case of early exit */ *retlen =0 ; -/* Do not allow writes past end of device */ -if (unlikely ((to + len) > mtd->size)) { -DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: Attempt write to past end of device\n" ); +if (mode ==MTD_OOB_AUTO ) + oobsize = this->ecclayout->oobavail; +else + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize -1 ); + +if (unlikely (column >= oobsize)) { +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: Attempted to start write outside oob\n" ); +return -EINVAL ; + } + +/* Do not allow reads past end of device */ +if (unlikely (to >= mtd->size || + len > ((mtd->size >> this->page_shift) - + (to >> this->page_shift)) * oobsize)) { +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: Attempted to write past end of device\n" );return -EINVAL ; }
@@ -1092,18 +1205,19 @@ static int onenand_do_write_oob(struct m/* Loop until all data write */ while (written < len) { -int thislen =min_t (int, mtd->oobsize, len - written); +int thislen =min_t (int, oobsize, len - written);cond_resched (); - column = to & (mtd->oobsize -1 ); - this->command (mtd,ONENAND_CMD_BUFFERRAM , to, mtd->oobsize);/* We send data to spare ram with oobsize * to prevent byte access */ memset (this->page_buf,0xff , mtd->oobsize); -memcpy (this->page_buf + column, buf, thislen); +if (mode ==MTD_OOB_AUTO ) +onenand_fill_auto_oob (mtd, this->page_buf, buf, column, thislen); +else +memcpy (this->page_buf + column, buf, thislen); this->write_bufferram (mtd,ONENAND_SPARERAM , this->page_buf,0 , mtd->oobsize); this->command (mtd,ONENAND_CMD_PROGOOB , to, mtd->oobsize);
@@ -1112,11 +1226,11 @@ static int onenand_do_write_oob(struct m ret = this->wait (mtd,FL_WRITING );if (ret) { -DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: write filaed %d\n" , ret); +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: write failed %d\n" , ret); goto out; } - ret =onenand_verify_oob (mtd, buf, to, thislen); + ret =onenand_verify_oob (mtd, this->page_buf, to);if (ret) {DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: verify failed %d\n" , ret); goto out;
@@ -1127,8 +1241,9 @@ static int onenand_do_write_oob(struct mif (written == len)break ; - to += thislen; + to += mtd->writesize; buf += thislen; + column =0 ; } out:
@@ -1149,10 +1264,18 @@ out:static int onenand_write_oob (struct mtd_info *mtd,loff_t to,struct mtd_oob_ops *ops) { -BUG_ON (ops->mode !=MTD_OOB_PLACE ); - +switch (ops->mode) + { +case MTD_OOB_PLACE : +case MTD_OOB_AUTO : +break ; +case MTD_OOB_RAW : +return -EINVAL ;/* Not implemented yet */ +default : +return -EINVAL ; + }return onenand_do_write_oob (mtd, to + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); }/**
@@ -1318,7 +1441,7 @@ static int onenand_default_block_markbad/* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01 ); -return onenand_do_write_oob (mtd, ofs ,2 , &retlen, buf); +return onenand_do_write_oob (mtd, ofs ,2 , &retlen, buf,MTD_OOB_PLACE ); }/**
@@ -1612,7 +1735,7 @@ static int do_otp_lock(struct mtd_info * this->command (mtd,ONENAND_CMD_OTP_ACCESS ,0 ,0 ); this->wait (mtd,FL_OTPING ); - ret =onenand_do_write_oob (mtd, from, len, retlen, buf); + ret =onenand_do_write_oob (mtd, from, len, retlen, buf,MTD_OOB_PLACE );/* Exit OTP access mode */ this->command (mtd,ONENAND_CMD_RESET ,0 ,0 );
@@ -2020,6 +2143,7 @@ static void onenand_resume(struct mtd_in*/ int onenand_scan (struct mtd_info *mtd,int maxchips) { +int i;struct onenand_chip *this = mtd->priv;if (!this->read_word)
@@ -2091,6 +2215,16 @@ int onenand_scan(struct mtd_info *mtd, i } this->subpagesize = mtd->writesize >> mtd->subpage_sft; + +/* +* The number of bytes available for a client to place data into +* the out of band area +*/ + this->ecclayout->oobavail =0 ; +for (i =0 ; this->ecclayout->oobfree[i].length; i++) + this->ecclayout->oobavail += + this->ecclayout->oobfree[i].length; + mtd->ecclayout = this->ecclayout;/* Fill in remaining MTD driver data */
drivers/mtd/onenand/onenand_bbt.c +2 -2
@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h>#include <linux/mtd/compatmac.h> extern int onenand_do_read_oob (struct mtd_info *mtd,loff_t from,size_t len, -size_t *retlen,u_char *buf); +size_t *retlen,u_char *buf,mtd_oob_mode_t mode);/** * check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m/* No need to read pages fully, * just read required OOB bytes */ ret =onenand_do_read_oob (mtd, from + j * mtd->writesize + bd->offs, - readlen, &retlen, &buf[0 ]); + readlen, &retlen, &buf[0 ],MTD_OOB_PLACE );/* If it is a initial bad block, just ignore it */ if (ret && !(ret &ONENAND_CTRL_LOAD ))
Signature · 1 line
1.4.3
Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data
In reply to Kyungmin ParkJust noticed another bug. Here is better version.
Quoted 1 line
From
f93c480e9ccd6a72abbc3fed1ab71dc4244b0229Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Wed, 31 Jan 2007 17:19:28 +0200
Subject: [MTD] OneNAND: Add support for auto-placement of out-of-band data
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND. Note that MTD_OOB_RAW is still not supported.
--- drivers/mtd/onenand/onenand_base.c | 204 +++++++++++++++++++++++++++++------ drivers/mtd/onenand/onenand_bbt.c | 4 +- 2 files changed, 171 insertions(+), 37 deletions(-)
drivers/mtd/onenand/onenand_base.c +169 -35
@@ -787,20 +787,60 @@ static int onenand_read(struct mtd_info }/** +* onenand_transfer_auto_oob - [Internal] oob auto-placement transfer +* @param mtd MTD device structure +* @param buf destination address +* @param column oob offset to read from +* @param thislen oob length to read +*/ +static int onenand_transfer_auto_oob (struct mtd_info *mtd,uint8_t *buf,int column, +int thislen) +{ +struct onenand_chip *this = mtd->priv; +struct nand_oobfree *free; +int readcol = column; +int readend = column + thislen; +int lastgap =0 ; +uint8_t *oob_buf = this->page_buf + mtd->writesize; + +for (free = this->ecclayout->oobfree; free->length; ++free) { +if (readcol >= lastgap) + readcol += free->offset - lastgap; +if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + this->read_bufferram (mtd,ONENAND_SPARERAM , oob_buf + readcol, + readcol, readend - readcol); +for (free = this->ecclayout->oobfree; free->length; ++free) { +int free_end = free->offset + free->length; +if (free->offset < readend && free_end > readcol) { +int st =max_t (int,free->offset,readcol); +int ed =min_t (int,free_end,readend); +int n = ed - st; +memcpy (buf, oob_buf + st, n); + buf += n; + } + } +return 0 ; +} + +/** * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data +* @param mode operation mode * * OneNAND read out-of-band data from the spare area */ int onenand_do_read_oob (struct mtd_info *mtd,loff_t from,size_t len, -size_t *retlen,u_char *buf) +size_t *retlen,u_char *buf,mtd_oob_mode_t mode) {struct onenand_chip *this = mtd->priv; -int read =0 , thislen, column; +int read =0 , thislen, column, oobsize;int ret =0 ;DEBUG (MTD_DEBUG_LEVEL3 ,"onenand_read_oob: from = 0x%08x, len = %i\n" , (unsigned int ) from, (int ) len);
@@ -808,21 +848,33 @@ int onenand_do_read_oob(struct mtd_info/* Initialize return length value */ *retlen =0 ; +if (mode ==MTD_OOB_AUTO ) + oobsize = this->ecclayout->oobavail; +else + oobsize = mtd->oobsize; + + column = from & (mtd->oobsize -1 ); + +if (unlikely (column >= oobsize)) { +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_read_oob: Attempted to start read outside oob\n" ); +return -EINVAL ; + } +/* Do not allow reads past end of device */ -if (unlikely ((from + len) > mtd->size)) { -DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_read_oob: Attempt read beyond end of device\n" ); +if (unlikely (from >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (from >> this->page_shift)) * oobsize)) { +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_read_oob: Attempted to read beyond end of device\n" );return -EINVAL ; }/* Grab the lock and see if the device is available */ onenand_get_device (mtd,FL_READING ); - column = from & (mtd->oobsize -1 ); -while (read < len) {cond_resched (); - thislen = mtd->oobsize - column; + thislen = oobsize - column; thislen =min_t (int, thislen, len); this->command (mtd,ONENAND_CMD_READOOB , from, mtd->oobsize);
@@ -832,7 +884,10 @@ int onenand_do_read_oob(struct mtd_info ret = this->wait (mtd,FL_READING );/* First copy data and check return value for ECC handling */ - this->read_bufferram (mtd,ONENAND_SPARERAM , buf, column, thislen); +if (mode ==MTD_OOB_AUTO ) +onenand_transfer_auto_oob (mtd, buf, column, thislen); +else + this->read_bufferram (mtd,ONENAND_SPARERAM , buf, column, thislen);if (ret) {DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_read_oob: read failed = 0x%x\n" , ret);
@@ -871,10 +926,18 @@ out:static int onenand_read_oob (struct mtd_info *mtd,loff_t from,struct mtd_oob_ops *ops) { -BUG_ON (ops->mode !=MTD_OOB_PLACE ); - +switch (ops->mode) + { +case MTD_OOB_PLACE : +case MTD_OOB_AUTO : +break ; +case MTD_OOB_RAW : +return -EINVAL ;/* Not implemented yet */ +default : +return -EINVAL ; + }return onenand_do_read_oob (mtd, from + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); }#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -883,14 +946,12 @@ #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE* @param mtd MTD device structure * @param buf the databuffer to verify * @param to offset to read from -* @param len number of bytes to read and compare * */ -static int onenand_verify_oob (struct mtd_info *mtd,const u_char *buf,loff_t to,int len) +static int onenand_verify_oob (struct mtd_info *mtd,const u_char *buf,loff_t to) {struct onenand_chip *this = mtd->priv; -char *readp = this->page_buf; -int column = to & (mtd->oobsize -1 ); +char *readp = this->page_buf + mtd->writesize;int status, i; this->command (mtd,ONENAND_CMD_READOOB , to, mtd->oobsize);
@@ -899,9 +960,8 @@ static int onenand_verify_oob(struct mtdif (status)return status; - this->read_bufferram (mtd,ONENAND_SPARERAM , readp, column, len); - -for (i =0 ; i < len; i++) + this->read_bufferram (mtd,ONENAND_SPARERAM , readp,0 , mtd->oobsize); +for (i =0 ; i < mtd->oobsize; i++)if (buf[i] !=0xFF && buf[i] != readp[i])return -EBADMSG ;
@@ -1060,20 +1120,59 @@ static int onenand_write(struct mtd_info }/** +* onenand_fill_auto_oob - [Internal] oob auto-placement transfer +* @param mtd MTD device structure +* @param oob_buf oob buffer +* @param buf source address +* @param column oob offset to write to +* @param thislen oob length to write +*/ +static int onenand_fill_auto_oob (struct mtd_info *mtd,u_char *oob_buf, +const u_char *buf,int column,int thislen) +{ +struct onenand_chip *this = mtd->priv; +struct nand_oobfree *free; +int writecol = column; +int writeend = column + thislen; +int lastgap =0 ; + +for (free = this->ecclayout->oobfree; free->length; ++free) { +if (writecol >= lastgap) + writecol += free->offset - lastgap; +if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + writeend = mtd->oobsize; +for (free = this->ecclayout->oobfree; free->length; ++free) { +int free_end = free->offset + free->length; +if (free->offset < writeend && free_end > writecol) { +int st =max_t (int,free->offset,writecol); +int ed =min_t (int,free_end,writeend); +int n = ed - st; +memcpy (oob_buf + st, buf, n); + buf += n; + } + } +return 0 ; +} + +/** * onenand_do_write_oob - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write +* @param mode operation mode * * OneNAND write out-of-band */ static int onenand_do_write_oob (struct mtd_info *mtd,loff_t to,size_t len, -size_t *retlen,const u_char *buf) +size_t *retlen,const u_char *buf,mtd_oob_mode_t mode) {struct onenand_chip *this = mtd->priv; -int column, ret =0 ; +int column, ret =0 , oobsize;int written =0 ;DEBUG (MTD_DEBUG_LEVEL3 ,"onenand_write_oob: to = 0x%08x, len = %i\n" , (unsigned int ) to, (int ) len);
@@ -1081,9 +1180,23 @@ static int onenand_do_write_oob(struct m/* Initialize retlen, in case of early exit */ *retlen =0 ; -/* Do not allow writes past end of device */ -if (unlikely ((to + len) > mtd->size)) { -DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: Attempt write to past end of device\n" ); +if (mode ==MTD_OOB_AUTO ) + oobsize = this->ecclayout->oobavail; +else + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize -1 ); + +if (unlikely (column >= oobsize)) { +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: Attempted to start write outside oob\n" ); +return -EINVAL ; + } + +/* Do not allow reads past end of device */ +if (unlikely (to >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (to >> this->page_shift)) * oobsize)) { +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: Attempted to write past end of device\n" );return -EINVAL ; }
@@ -1092,18 +1205,19 @@ static int onenand_do_write_oob(struct m/* Loop until all data write */ while (written < len) { -int thislen =min_t (int, mtd->oobsize, len - written); +int thislen =min_t (int, oobsize, len - written);cond_resched (); - column = to & (mtd->oobsize -1 ); - this->command (mtd,ONENAND_CMD_BUFFERRAM , to, mtd->oobsize);/* We send data to spare ram with oobsize * to prevent byte access */ memset (this->page_buf,0xff , mtd->oobsize); -memcpy (this->page_buf + column, buf, thislen); +if (mode ==MTD_OOB_AUTO ) +onenand_fill_auto_oob (mtd, this->page_buf, buf, column, thislen); +else +memcpy (this->page_buf + column, buf, thislen); this->write_bufferram (mtd,ONENAND_SPARERAM , this->page_buf,0 , mtd->oobsize); this->command (mtd,ONENAND_CMD_PROGOOB , to, mtd->oobsize);
@@ -1112,11 +1226,11 @@ static int onenand_do_write_oob(struct m ret = this->wait (mtd,FL_WRITING );if (ret) { -DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: write filaed %d\n" , ret); +DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: write failed %d\n" , ret); goto out; } - ret =onenand_verify_oob (mtd, buf, to, thislen); + ret =onenand_verify_oob (mtd, this->page_buf, to);if (ret) {DEBUG (MTD_DEBUG_LEVEL0 ,"onenand_write_oob: verify failed %d\n" , ret); goto out;
@@ -1127,8 +1241,9 @@ static int onenand_do_write_oob(struct mif (written == len)break ; - to += thislen; + to += mtd->writesize; buf += thislen; + column =0 ; } out:
@@ -1149,10 +1264,18 @@ out:static int onenand_write_oob (struct mtd_info *mtd,loff_t to,struct mtd_oob_ops *ops) { -BUG_ON (ops->mode !=MTD_OOB_PLACE ); - +switch (ops->mode) + { +case MTD_OOB_PLACE : +case MTD_OOB_AUTO : +break ; +case MTD_OOB_RAW : +return -EINVAL ;/* Not implemented yet */ +default : +return -EINVAL ; + }return onenand_do_write_oob (mtd, to + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); }/**
@@ -1318,7 +1441,7 @@ static int onenand_default_block_markbad/* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01 ); -return onenand_do_write_oob (mtd, ofs ,2 , &retlen, buf); +return onenand_do_write_oob (mtd, ofs ,2 , &retlen, buf,MTD_OOB_PLACE ); }/**
@@ -1612,7 +1735,7 @@ static int do_otp_lock(struct mtd_info * this->command (mtd,ONENAND_CMD_OTP_ACCESS ,0 ,0 ); this->wait (mtd,FL_OTPING ); - ret =onenand_do_write_oob (mtd, from, len, retlen, buf); + ret =onenand_do_write_oob (mtd, from, len, retlen, buf,MTD_OOB_PLACE );/* Exit OTP access mode */ this->command (mtd,ONENAND_CMD_RESET ,0 ,0 );
@@ -2019,6 +2142,7 @@ static void onenand_resume(struct mtd_in*/ int onenand_scan (struct mtd_info *mtd,int maxchips) { +int i;struct onenand_chip *this = mtd->priv;if (!this->read_word)
@@ -2090,6 +2214,16 @@ int onenand_scan(struct mtd_info *mtd, i } this->subpagesize = mtd->writesize >> mtd->subpage_sft; + +/* +* The number of bytes available for a client to place data into +* the out of band area +*/ + this->ecclayout->oobavail =0 ; +for (i =0 ; this->ecclayout->oobfree[i].length; i++) + this->ecclayout->oobavail += + this->ecclayout->oobfree[i].length; + mtd->ecclayout = this->ecclayout;/* Fill in remaining MTD driver data */
drivers/mtd/onenand/onenand_bbt.c +2 -2
@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h>#include <linux/mtd/compatmac.h> extern int onenand_do_read_oob (struct mtd_info *mtd,loff_t from,size_t len, -size_t *retlen,u_char *buf); +size_t *retlen,u_char *buf,mtd_oob_mode_t mode);/** * check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m/* No need to read pages fully, * just read required OOB bytes */ ret =onenand_do_read_oob (mtd, from + j * mtd->writesize + bd->offs, - readlen, &retlen, &buf[0 ]); + readlen, &retlen, &buf[0 ],MTD_OOB_PLACE );/* If it is a initial bad block, just ignore it */ if (ret && !(ret &ONENAND_CTRL_LOAD ))
Signature · 1 line
1.4.3
RE: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata
I'll push it.
But with JFFS2 patch, there's one bug (see below)
I'l fix it and commit it.
Thank you,
Kyungmin Park
Quoted 9 lines
+static int onenand_fill_auto_oob(struct
mtd_info*mtd,u_char*oob_buf,(quoted diff) +1
-0+ const u_char *buf, intcolumn, int thislen) {
drivers/mtd/onenand/onenand_base.c +5
-0+struct onenand_chip *this = mtd->priv; +struct nand_oobfree *free; +int writecol = column; +int writeend = column + thislen; +int lastgap =0 ;
drivers/mtd/onenand/onenand_base.c static int onenand_write(struct mtd_info
Quoted 10 lines
drivers/mtd/onenand/onenand_base.c +9
-0+ +for (free = this->ecclayout->oobfree; free->length; ++free) { +if (writecol >= lastgap) + writecol += free->offset - lastgap; +if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + writeend = mtd->oobsize;
We don't re-assign the 'writeend'. It cause the block is not clean. Since JFFS2 send only 12 bytes but onenand writes until oobsize, actually 20 bytes with zero.
So this statement will be removed.
Quoted 36 lines
drivers/mtd/onenand/onenand_base.c +11
-1+for (free = this->ecclayout->oobfree; free->length; ++free) { +int free_end = free->offset + free->length; +if (free->offset < writeend && free_end > writecol) { +int st =max_t (int,free->offset,writecol); +int ed =min_t (int,free_end,writeend); +int n = ed - st; +memcpy (oob_buf + st, buf, n); + buf += n; + } + } +return 0 ; -----Original Message-----From: [email protected] [mailto:[email protected]] On Behalf Of Adrian Hunter
Sent: Thursday, February 01, 2007 12:42 AM
To: [email protected]
Subject: Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddataJust noticed another bug. Here is better version.
Quoted 1 line
From
f93c480e9ccd6a72abbc3fed1ab71dc4244b0229Mon Sep 1700:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Wed, 31 Jan 2007 17:19:28 +0200
Subject: [MTD] OneNAND: Add support for auto-placement of out-of-band dataEnable the use of oob operation mode
MTD_OOB_AUTOwith OneNAND. Note thatMTD_OOB_RAWis still not supported.Signed-off-by Adrian Hunter <[email protected]>---
Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata
Quoted 6 lines · ext Kyungmin Park
drivers/mtd/onenand/onenand_base.c static int onenand_write(struct mtd_info · line 1146
Quoted 1 line
drivers/mtd/onenand/onenand_base.c +1
-0+ writeend = mtd->oobsize;We don't re-assign the 'writeend'. It cause the block is not clean. Since JFFS2 send only 12 bytes but onenand writes until oobsize, actually 20 bytes with zero.
So this statement will be removed.
Yes, that was a typo. The line should have been deleted when the previous line was deleted.
Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata
Quoted 9 lines · ext Adrian Hunter
Quoted 5 lines · ext Kyungmin Park
Review
Quoted 1 line
(quoted diff) +1
-0+ writeend = mtd->oobsize;We don't re-assign the 'writeend'. It cause the block is not clean. Since JFFS2 send only 12 bytes but onenand writes until oobsize, actually 20 bytes with zero.
So this statement will be removed.Yes, that was a typo. The line should have been deleted when the previous line was deleted.
The statement still seems to be in the onenand tree. Here is a patch to remove it.
Quoted 1 line
From
397419987d744ed971c74cfabae2eeac705f2fe0Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Thu, 1 Feb 2007 09:59:41 +0200
Subject: [MTD] OneNAND: Remove line of code that was meant to be deleted
Iterations of the patch to add oob auto-placement support to OneNAND left a line of code that was meant to have been deleted.
---drivers/mtd/onenand/onenand_base.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
drivers/mtd/onenand/onenand_base.c +0 -0
@@ -1143,7 +1143,6 @@ static int onenand_fill_auto_oob(struct
writeend += free->offset - lastgap;
lastgap = free->offset + free->length;
}
- writeend = mtd->oobsize;
for (free = this->ecclayout->oobfree; free->length; ++free) {
int free_end = free->offset + free->length;
if (free->offset < writeend && free_end > writecol) {Signature · 1 line
1.4.3
RE: [PATCH] [MTD] OneNAND: Add support forauto-placementofout-of-banddata
How about the second mail about onenand_transfer_auto_oob
I will commit it both
Thank you,
Kyungmin Park
Quoted 40 lines
Quoted 3 lines
From
397419987d744ed971c74cfabae2eeac705f2fe0Mon Sep 1700:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Thu, 1 Feb 2007 09:59:41 +0200
Subject: [MTD] OneNAND: Remove line of code that was meant to be deletedIterations of the patch to add oob auto-placement support to OneNAND left a line of code that was meant to have been deleted.
Signed-off-by Adrian Hunter <[email protected]>--- drivers/mtd/onenand/onenand_base.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)drivers/mtd/onenand/onenand_base.c +0
-2@@ -1143,7 +1143,6 @@ static int onenand_fill_auto_oob(struct writeend += free->offset - lastgap; lastgap = free->offset + free->length; } - writeend = mtd->oobsize;for (free = this->ecclayout->oobfree; free->length; ++free) {int free_end = free->offset + free->length;if (free->offset < writeend && free_end > writecol) { --1.4.3
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
Re: [PATCH] [MTD] OneNAND: Add support forauto-placementofout-of-banddata
Quoted 1 line · ext Kyungmin Park
How about the second mail about
onenand_transfer_auto_oob
Sorry, I thought you had done it already.
I will leave it to you.
RE: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata
In reply to Adrian HunterQuoted 18 lines
/**
+ *onenand_transfer_auto_oob- [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(structmtd_info*mtd,uint8_t*buf, int column,drivers/mtd/onenand/onenand_base.c +8
-0+ int thislen) +{ +struct onenand_chip *this = mtd->priv; +struct nand_oobfree *free; +int readcol = column; +int readend = column + thislen; +int lastgap =0 ; +uint8_t *oob_buf = this->page_buf + mtd->writesize;
drivers/mtd/onenand/onenand_base.c static int onenand_read(struct mtd_info · line 814
Quoted 10 lines
drivers/mtd/onenand/onenand_base.c +10
-0+ +for (free = this->ecclayout->oobfree; free->length; ++free) { +if (readcol >= lastgap) + readcol += free->offset - lastgap; +if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + this->read_bufferram (mtd,ONENAND_SPARERAM , oob_buf + readcol, + readcol, readend - readcol);
How about to read full oobsize? since 32 bytes memcpy is optimized. Now there are two cases.
case 1: readcol = 2, readend = 36, so read length is 34
case 2: readcol = 2, readend = 64, so read length is 62
Buf if we change to colume = 0, length = 64. we only do 2 loop in memcpy.
this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
The contents of buffer are handled correctly (see below)
Quoted 13 lines
drivers/mtd/onenand/onenand_base.c +13
-0+for (free = this->ecclayout->oobfree; free->length; ++free) { +int free_end = free->offset + free->length; +if (free->offset < readend && free_end > readcol) { +int st =max_t (int,free->offset,readcol); +int ed =min_t (int,free_end,readend); +int n = ed - st; +memcpy (buf, oob_buf + st, n); + buf += n; + } + } +return 0 ; +} +
Re: [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data
In reply to Adrian HunterQuoted 4 lines · ext Adrian Hunter
Quoted 1 line · ext Kyungmin Park
It's cool. however how can we test this one?
I have a test module, that I will post.
Here it is
/*
* oobtest.c
*
* Test oob read and write on MTD device.
*
* Copyright (C) 2005-2006 Nokia Corporation
*
* Authors: Artem Bityutskiy <[email protected]>
* Adrian Hunter <[email protected]>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/mtd/mtd.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#define PRINT_PREF KERN_CRIT "oobtest: "
/* Uncomment this if you have old MTD sources */
/* #define writesize oobblock */
static int dev = 4;module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use");
static struct mtd_info *mtd;
static unsigned char *readbuf = 0;
static unsigned char *writebuf = 0;
static int pgsize;
static int bufsize;
static int ebcnt;
static int pgcnt;
static int errcnt = 0;
static int use_offset;
static int use_len;
static int use_len_max;
static int vary_offset;
static unsigned long next = 1;
static int simple_rand(void)
{
next = next * 1103515245 + 12345;
return ((unsigned) (next / 65536) % 32768);
}static void simple_srand(unsigned long seed)
{
next = seed;
}static inline void set_random_data(unsigned char *buf,size_t len)
{
size_t i;
for (i = 0; i < len; ++i)
buf[i] = simple_rand();
}static inline int erase_eraseblock(int ebnum)
{
int err;
struct erase_info ei;
loff_t addr = ebnum * mtd->erasesize;
memset(&ei, 0, sizeof(struct erase_info));
ei.mtd = mtd;
ei.addr = addr;
ei.len = mtd->erasesize;
err = mtd->erase(mtd, &ei);
if (unlikely(err)) {
printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
return err;
}
if (unlikely(ei.state == MTD_ERASE_FAILED)) {
printk(PRINT_PREF "some erase error occurred at EB %d\n", ebnum);
return -EIO;
}
return 0;
}static void do_vary_offset(void)
{
use_len -= 1;
if (use_len < 1) {
use_offset += 1;
if (use_offset >= use_len_max)
use_offset = 0;
use_len = use_len_max - use_offset;
}
}static inline 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 += pgsize) {
set_random_data(writebuf, use_len);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = use_len;
ops.oobretlen = 0;
ops.ooboffs = use_offset;
ops.datbuf = 0;
ops.oobbuf = writebuf;
err = mtd->write_oob(mtd, addr, &ops);
if (unlikely(err || ops.oobretlen != use_len)) {
printk(PRINT_PREF "error: writeoob failed at 0x%08x\n", (unsigned) 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 inline int write_eraseblock_in_one_go(int ebnum)
{
struct mtd_oob_ops ops;
int err = 0;
loff_t addr = ebnum * mtd->erasesize;
size_t len = mtd->ecclayout->oobavail * pgcnt;
set_random_data(writebuf, len);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = len;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = writebuf;
err = mtd->write_oob(mtd, addr, &ops);
if (unlikely(err || ops.oobretlen != len)) {
printk(PRINT_PREF "error: writeoob failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
return err ? err : -1;
}
return err;
}static inline int verify_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 += pgsize) {
set_random_data(writebuf, use_len);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = use_len;
ops.oobretlen = 0;
ops.ooboffs = use_offset;
ops.datbuf = 0;
ops.oobbuf = readbuf;
err = mtd->read_oob(mtd, addr, &ops);
if (unlikely(err || ops.oobretlen != use_len)) {
printk(PRINT_PREF "error: readoob failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
return err ? err : -1;
}
if (unlikely(memcmp(readbuf, writebuf, use_len))) {
printk(PRINT_PREF "error: verify failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
if (errcnt > 1000) {
printk(PRINT_PREF "error: too many errors\n");
return -1;
}
}
if (vary_offset)
do_vary_offset();
}
return err;
}static inline int verify_eraseblock_in_one_go(int ebnum)
{
struct mtd_oob_ops ops;
int err = 0;
loff_t addr = ebnum * mtd->erasesize;
size_t len = mtd->ecclayout->oobavail * pgcnt;
set_random_data(writebuf, len);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = len;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = readbuf;
err = mtd->read_oob(mtd, addr, &ops);
if (unlikely(err || ops.oobretlen != len)) {
printk(PRINT_PREF "error: readoob failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
return err ? err : -1;
}
if (unlikely(memcmp(readbuf, writebuf, len))) {
printk(PRINT_PREF "error: verify failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
if (errcnt > 1000) {
printk(PRINT_PREF "error: too many errors\n");
return -1;
}
}
return err;
}static int __init oobtest_init(void)
{
int err = 0;
u_int32_t i;
struct mtd_oob_ops ops;
loff_t addr = 0;
printk("\n");
printk("=========================================================="
"===============================\n");
printk("oobtest: dev = %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->writesize == 1) {
printk(PRINT_PREF "warning: this test was written for NAND."
"Assume page size is 512 bytes.\n");
pgsize = 512;
} else
pgsize = mtd->writesize;
printk(PRINT_PREF "oob available per page = %u\n",(unsigned) mtd->ecclayout->oobavail);
err = -ENOMEM;
bufsize = mtd->erasesize;
readbuf = kmalloc(bufsize, GFP_KERNEL);
if (!readbuf) {
printk(PRINT_PREF "error: cannot allocate memory\n");
goto out;
}
writebuf = kmalloc(bufsize, GFP_KERNEL);
if (!writebuf) {
printk(PRINT_PREF "error: cannot allocate memory\n");
goto out;
}
ebcnt = mtd->size / mtd->erasesize;
pgcnt = mtd->erasesize / pgsize;
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");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Write all eraseblocks */
simple_srand(1);
printk(PRINT_PREF "writing\n");
for (i = 0; i < ebcnt; ++i) {
err = write_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "written %u\n", i);
cond_resched();
}
printk(PRINT_PREF "written %u\n", i);
/* Check all eraseblocks */
simple_srand(1);
printk(PRINT_PREF "verifying\n");
for (i = 0; i < ebcnt; ++i) {
err = verify_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "verified %u\n", i);
cond_resched();
}
printk(PRINT_PREF "verified %u\n", i);
/* Second test: write all oob, a block at a time, read it back and verify */
printk(PRINT_PREF "Test 2 of 5\n");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Write all eraseblocks */
simple_srand(3);
printk(PRINT_PREF "writing\n");
for (i = 0; i < ebcnt; ++i) {
err = write_eraseblock_in_one_go(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "written %u\n", i);
cond_resched();
}
printk(PRINT_PREF "written %u\n", i);
/* Check all eraseblocks */
simple_srand(3);
printk(PRINT_PREF "verifying\n");
for (i = 0; i < ebcnt; ++i) {
err = verify_eraseblock_in_one_go(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "verified %u\n", i);
cond_resched();
}
printk(PRINT_PREF "verified %u\n", i);
/* Third test: write oob at varying offsets and lengths,
read it back and verify */
printk(PRINT_PREF "Test 3 of 5\n");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Write all eraseblocks */
use_offset = 0;
use_len = mtd->ecclayout->oobavail;
use_len_max = mtd->ecclayout->oobavail;
vary_offset = 1;
simple_srand(5);
printk(PRINT_PREF "writing\n");
for (i = 0; i < ebcnt; ++i) {
err = write_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "written %u\n", i);
cond_resched();
}
printk(PRINT_PREF "written %u\n", i);
/* Check all eraseblocks */
use_offset = 0;
use_len = mtd->ecclayout->oobavail;
use_len_max = mtd->ecclayout->oobavail;
vary_offset = 1;
simple_srand(5);
printk(PRINT_PREF "verifying\n");
for (i = 0; i < ebcnt; ++i) {
err = verify_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "verified %u\n", i);
cond_resched();
}
printk(PRINT_PREF "verified %u\n", i);
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");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Attempt to write off end of oob */
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = 1;
ops.oobretlen = 0;
ops.ooboffs = mtd->ecclayout->oobavail;
ops.datbuf = 0;
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, 0, &ops);
if (unlikely(err)) {
printk(PRINT_PREF "Error occurred as expected\n");
err = 0;
} else {
printk(PRINT_PREF "error: started write past end of oob\n");
errcnt += 1;
}
/* Attempt to read off end of oob */
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = 1;
ops.oobretlen = 0;
ops.ooboffs = mtd->ecclayout->oobavail;
ops.datbuf = 0;
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, 0, &ops);
if (unlikely(err)) {
printk(PRINT_PREF "Error occurred as expected\n");
err = 0;
} else {
printk(PRINT_PREF "error: started read past end of oob\n");
errcnt += 1;
}
/* Write oob across chip boundary */
simple_srand(7);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = writebuf;
err = mtd->write_oob(mtd, addr, &ops);
if (unlikely(err))
goto out;
/* Read oob across chip boundary */
simple_srand(7);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = readbuf;
err = mtd->read_oob(mtd, addr, &ops);
if (unlikely(err))
goto out;
/* Attempt to write off end of device */
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail + 1;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
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 (unlikely(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_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail + 1;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
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 (unlikely(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 */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Write all eraseblocks */
simple_srand(11);
printk(PRINT_PREF "writing\n");
for (i = 0; i < ebcnt - 1; ++i) {
set_random_data(writebuf, mtd->ecclayout->oobavail * 2);
addr = (i + 1) * mtd->erasesize - mtd->writesize;
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = writebuf;
err = mtd->write_oob(mtd, addr, &ops);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "written %u\n", i);
cond_resched();
}
printk(PRINT_PREF "written %u\n", i);
/* Check all eraseblocks */
simple_srand(11);
printk(PRINT_PREF "verifying\n");
for (i = 0; i < ebcnt - 1; ++i) {
set_random_data(writebuf, mtd->ecclayout->oobavail * 2);
addr = (i + 1) * mtd->erasesize - mtd->writesize;
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = readbuf;
err = mtd->read_oob(mtd, addr, &ops);
if (unlikely(err))
goto out;
if (unlikely(memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2))) {
printk(PRINT_PREF "error: verify failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
if (errcnt > 1000) {
printk(PRINT_PREF "error: too many errors\n");
goto out;
}
}
if (i % 256 == 0)
printk(PRINT_PREF "verified %u\n", i);
cond_resched();
}
printk(PRINT_PREF "verified %u\n", i);
printk(PRINT_PREF "oobtest finished with %d errors\n", errcnt);
out:
kfree(writebuf);
kfree(readbuf);
put_mtd_device(mtd);
if (err)
printk(PRINT_PREF "error %d occurred\n", err);
printk("=========================================================="
"===============================\n");
return -1;
}module_init(oobtest_init);
static void __exit oobtest_exit(void)
{
return;
}module_exit(oobtest_exit);
MODULE_DESCRIPTION("Page test module");MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter");MODULE_LICENSE("GPL");