|
@@ -957,43 +957,48 @@ exit:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-#define NR_OOB_SCAN_PAGES 4
|
|
|
+#define NR_OOB_SCAN_PAGES 4
|
|
|
+
|
|
|
+/* For historical reasons we use only 12 bytes for OOB clean marker */
|
|
|
+#define OOB_CM_SIZE 12
|
|
|
+
|
|
|
+static const struct jffs2_unknown_node oob_cleanmarker =
|
|
|
+{
|
|
|
+ .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
|
|
|
+ .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
|
|
|
+ .totlen = cpu_to_je32(8)
|
|
|
+};
|
|
|
|
|
|
/*
|
|
|
- * Check, if the out of band area is empty
|
|
|
+ * Check, if the out of band area is empty. This function knows about the clean
|
|
|
+ * marker and if it is present in OOB, treats the OOB as empty anyway.
|
|
|
*/
|
|
|
int jffs2_check_oob_empty(struct jffs2_sb_info *c,
|
|
|
struct jffs2_eraseblock *jeb, int mode)
|
|
|
{
|
|
|
- int i, page, ret;
|
|
|
- int oobsize = c->mtd->oobsize;
|
|
|
+ int i, ret;
|
|
|
+ int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
|
|
|
struct mtd_oob_ops ops;
|
|
|
|
|
|
- ops.ooblen = NR_OOB_SCAN_PAGES * oobsize;
|
|
|
+ ops.mode = MTD_OOB_AUTO;
|
|
|
+ ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
|
|
|
ops.oobbuf = c->oobbuf;
|
|
|
- ops.ooboffs = 0;
|
|
|
+ ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
|
|
|
ops.datbuf = NULL;
|
|
|
- ops.mode = MTD_OOB_PLACE;
|
|
|
|
|
|
ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
|
|
|
- if (ret) {
|
|
|
- D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
|
|
|
- "failed %d for block at %08x\n", ret, jeb->offset));
|
|
|
+ if (ret || ops.oobretlen != ops.ooblen) {
|
|
|
+ printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
|
|
|
+ " bytes, read %zd bytes, error %d\n",
|
|
|
+ jeb->offset, ops.ooblen, ops.oobretlen, ret);
|
|
|
+ if (!ret)
|
|
|
+ ret = -EIO;
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- if (ops.oobretlen < ops.ooblen) {
|
|
|
- D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
|
|
|
- "returned short read (%zd bytes not %d) for block "
|
|
|
- "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset));
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
-
|
|
|
- /* Special check for first page */
|
|
|
- for(i = 0; i < oobsize ; i++) {
|
|
|
- /* Yeah, we know about the cleanmarker. */
|
|
|
- if (mode && i >= c->fsdata_pos &&
|
|
|
- i < c->fsdata_pos + c->fsdata_len)
|
|
|
+ for(i = 0; i < ops.ooblen; i++) {
|
|
|
+ if (mode && i < cmlen)
|
|
|
+ /* Yeah, we know about the cleanmarker */
|
|
|
continue;
|
|
|
|
|
|
if (ops.oobbuf[i] != 0xFF) {
|
|
@@ -1003,111 +1008,63 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* we know, we are aligned :) */
|
|
|
- for (page = oobsize; page < ops.ooblen; page += sizeof(long)) {
|
|
|
- long dat = *(long *)(&ops.oobbuf[page]);
|
|
|
- if(dat != -1)
|
|
|
- return 1;
|
|
|
- }
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Scan for a valid cleanmarker and for bad blocks
|
|
|
+ * Check for a valid cleanmarker.
|
|
|
+ * Returns: 0 if a valid cleanmarker was found
|
|
|
+ * 1 if no cleanmarker was found
|
|
|
+ * negative error code if an error occurred
|
|
|
*/
|
|
|
-int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c,
|
|
|
- struct jffs2_eraseblock *jeb)
|
|
|
+int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
|
|
|
+ struct jffs2_eraseblock *jeb)
|
|
|
{
|
|
|
- struct jffs2_unknown_node n;
|
|
|
struct mtd_oob_ops ops;
|
|
|
- int oobsize = c->mtd->oobsize;
|
|
|
- unsigned char *p,*b;
|
|
|
- int i, ret;
|
|
|
- size_t offset = jeb->offset;
|
|
|
-
|
|
|
- /* Check first if the block is bad. */
|
|
|
- if (c->mtd->block_isbad(c->mtd, offset)) {
|
|
|
- D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()"
|
|
|
- ": Bad block at %08x\n", jeb->offset));
|
|
|
- return 2;
|
|
|
- }
|
|
|
+ int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
|
|
|
|
|
|
- ops.ooblen = oobsize;
|
|
|
+ ops.mode = MTD_OOB_AUTO;
|
|
|
+ ops.ooblen = cmlen;
|
|
|
ops.oobbuf = c->oobbuf;
|
|
|
- ops.ooboffs = 0;
|
|
|
+ ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
|
|
|
ops.datbuf = NULL;
|
|
|
- ops.mode = MTD_OOB_PLACE;
|
|
|
|
|
|
- ret = c->mtd->read_oob(c->mtd, offset, &ops);
|
|
|
- if (ret) {
|
|
|
- D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): "
|
|
|
- "Read OOB failed %d for block at %08x\n",
|
|
|
- ret, jeb->offset));
|
|
|
+ ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
|
|
|
+ if (ret || ops.oobretlen != ops.ooblen) {
|
|
|
+ printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
|
|
|
+ " bytes, read %zd bytes, error %d\n",
|
|
|
+ jeb->offset, ops.ooblen, ops.oobretlen, ret);
|
|
|
+ if (!ret)
|
|
|
+ ret = -EIO;
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- if (ops.oobretlen < ops.ooblen) {
|
|
|
- D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): "
|
|
|
- "Read OOB return short read (%zd bytes not %d) "
|
|
|
- "for block at %08x\n", ops.oobretlen, ops.ooblen,
|
|
|
- jeb->offset));
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
-
|
|
|
- n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
|
|
|
- n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
|
|
|
- n.totlen = cpu_to_je32 (8);
|
|
|
- p = (unsigned char *) &n;
|
|
|
- b = c->oobbuf + c->fsdata_pos;
|
|
|
-
|
|
|
- for (i = c->fsdata_len; i; i--) {
|
|
|
- if (*b++ != *p++)
|
|
|
- ret = 1;
|
|
|
- }
|
|
|
-
|
|
|
- D1(if (ret == 1) {
|
|
|
- printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): "
|
|
|
- "Cleanmarker node not detected in block at %08x\n",
|
|
|
- offset);
|
|
|
- printk(KERN_WARNING "OOB at %08zx was ", offset);
|
|
|
- for (i=0; i < oobsize; i++)
|
|
|
- printk("%02x ", c->oobbuf[i]);
|
|
|
- printk("\n");
|
|
|
- });
|
|
|
- return ret;
|
|
|
+ return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen);
|
|
|
}
|
|
|
|
|
|
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
|
|
|
struct jffs2_eraseblock *jeb)
|
|
|
{
|
|
|
- struct jffs2_unknown_node n;
|
|
|
- int ret;
|
|
|
+ int ret;
|
|
|
struct mtd_oob_ops ops;
|
|
|
+ int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
|
|
|
|
|
|
- n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
|
|
- n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
|
|
|
- n.totlen = cpu_to_je32(8);
|
|
|
-
|
|
|
- ops.ooblen = c->fsdata_len;
|
|
|
- ops.oobbuf = (uint8_t *)&n;
|
|
|
- ops.ooboffs = c->fsdata_pos;
|
|
|
+ ops.mode = MTD_OOB_AUTO;
|
|
|
+ ops.ooblen = cmlen;
|
|
|
+ ops.oobbuf = (uint8_t *)&oob_cleanmarker;
|
|
|
+ ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
|
|
|
ops.datbuf = NULL;
|
|
|
- ops.mode = MTD_OOB_PLACE;
|
|
|
|
|
|
ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops);
|
|
|
-
|
|
|
- if (ret) {
|
|
|
- D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
|
|
|
- "Write failed for block at %08x: error %d\n",
|
|
|
- jeb->offset, ret));
|
|
|
+ if (ret || ops.oobretlen != ops.ooblen) {
|
|
|
+ printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd"
|
|
|
+ " bytes, read %zd bytes, error %d\n",
|
|
|
+ jeb->offset, ops.ooblen, ops.oobretlen, ret);
|
|
|
+ if (!ret)
|
|
|
+ ret = -EIO;
|
|
|
return ret;
|
|
|
}
|
|
|
- if (ops.oobretlen != ops.ooblen) {
|
|
|
- D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
|
|
|
- "Short write for block at %08x: %zd not %d\n",
|
|
|
- jeb->offset, ops.oobretlen, ops.ooblen));
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1140,41 +1097,24 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
-static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
|
|
|
+int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
|
|
|
{
|
|
|
struct nand_ecclayout *oinfo = c->mtd->ecclayout;
|
|
|
|
|
|
- /* Do this only, if we have an oob buffer */
|
|
|
if (!c->mtd->oobsize)
|
|
|
return 0;
|
|
|
|
|
|
/* Cleanmarker is out-of-band, so inline size zero */
|
|
|
c->cleanmarker_size = 0;
|
|
|
|
|
|
- /* Should we use autoplacement ? */
|
|
|
- if (!oinfo) {
|
|
|
- D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
|
|
|
+ if (!oinfo || oinfo->oobavail == 0) {
|
|
|
+ printk(KERN_ERR "inconsistent device description\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
|
|
|
- /* Get the position of the free bytes */
|
|
|
- if (!oinfo->oobfree[0].length) {
|
|
|
- printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep."
|
|
|
- " Autoplacement selected and no empty space in oob\n");
|
|
|
- return -ENOSPC;
|
|
|
- }
|
|
|
- c->fsdata_pos = oinfo->oobfree[0].offset;
|
|
|
- c->fsdata_len = oinfo->oobfree[0].length;
|
|
|
- if (c->fsdata_len > 8)
|
|
|
- c->fsdata_len = 8;
|
|
|
+ D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n"));
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
|
|
|
-{
|
|
|
- int res;
|
|
|
+ c->oobavail = oinfo->oobavail;
|
|
|
|
|
|
/* Initialise write buffer */
|
|
|
init_rwsem(&c->wbuf_sem);
|
|
@@ -1185,22 +1125,13 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
|
|
|
if (!c->wbuf)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL);
|
|
|
- if (!c->oobbuf)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- res = jffs2_nand_set_oobinfo(c);
|
|
|
-
|
|
|
-#ifdef BREAKME
|
|
|
- if (!brokenbuf)
|
|
|
- brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
|
|
|
- if (!brokenbuf) {
|
|
|
+ c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL);
|
|
|
+ if (!c->oobbuf) {
|
|
|
kfree(c->wbuf);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
- memset(brokenbuf, 0xdb, c->wbuf_pagesize);
|
|
|
-#endif
|
|
|
- return res;
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
|