size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
                         size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                       size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                        size_t *retlen, const u_char *buf);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+                       struct mtd_oob_ops *ops);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+                        struct mtd_oob_ops *ops);
 static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
                         size_t *retlen, const u_char *buf);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
        return 0;
 }
 
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                       size_t * retlen, u_char * buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+                       struct mtd_oob_ops *ops)
 {
        struct DiskOnChip *this = mtd->priv;
        int len256 = 0, ret;
        struct Nand *mychip;
+       uint8_t *buf = ops->oobbuf;
+       size_t len = ops->len;
+
+       BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+       ofs += ops->ooboffs;
 
        mutex_lock(&this->lock);
 
 
        DoC_ReadBuf(this, &buf[len256], len - len256);
 
-       *retlen = len;
+       ops->retlen = len;
        /* Reading the full OOB data drops us off of the end of the page,
          * causing the flash device to go into busy mode, so we need
          * to wait until ready 11.4.1 and Toshiba TC58256FT docs */
 
 }
 
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                        size_t * retlen, const u_char * buf)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+                        struct mtd_oob_ops *ops)
 {
-       struct DiskOnChip *this = mtd->priv;
-       int ret;
+       struct DiskOnChip *this = mtd->priv;
+       int ret;
 
-       mutex_lock(&this->lock);
-       ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf);
+       BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+       mutex_lock(&this->lock);
+       ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len,
+                                  &ops->retlen, ops->oobbuf);
 
-       mutex_unlock(&this->lock);
-       return ret;
+       mutex_unlock(&this->lock);
+       return ret;
 }
 
 static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
 
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
                         size_t *retlen, const u_char *buf, u_char *eccbuf,
                         struct nand_oobinfo *oobsel);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                       size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                        size_t *retlen, const u_char *buf);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+                       struct mtd_oob_ops *ops);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+                        struct mtd_oob_ops *ops);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
 
 static struct mtd_info *docmillist = NULL;
        return ret;
 }
 
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                       size_t *retlen, u_char *buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+                       struct mtd_oob_ops *ops)
 {
 #ifndef USE_MEMCPY
        int i;
        struct DiskOnChip *this = mtd->priv;
        void __iomem *docptr = this->virtadr;
        struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+       uint8_t *buf = ops->oobbuf;
+       size_t len = ops->len;
+
+       BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+       ofs += ops->ooboffs;
 
        /* Find the chip which is to be used and select it */
        if (this->curfloor != mychip->floor) {
 #endif
        buf[len - 1] = ReadDOC(docptr, LastDataRead);
 
-       *retlen = len;
+       ops->retlen = len;
 
        return 0;
 }
 
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                        size_t *retlen, const u_char *buf)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+                        struct mtd_oob_ops *ops)
 {
 #ifndef USE_MEMCPY
        int i;
        struct DiskOnChip *this = mtd->priv;
        void __iomem *docptr = this->virtadr;
        struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+       uint8_t *buf = ops->oobbuf;
+       size_t len = ops->len;
+
+       BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+       ofs += ops->ooboffs;
 
        /* Find the chip which is to be used and select it */
        if (this->curfloor != mychip->floor) {
        if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
                printk("Error programming oob data\n");
                /* FIXME: implement Bad Block Replacement (in nftl.c ??) */
-               *retlen = 0;
+               ops->retlen = 0;
                ret = -EIO;
        }
        dummy = ReadDOC(docptr, LastDataRead);
 
-       *retlen = len;
+       ops->retlen = len;
 
        return ret;
 }
 
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf, u_char *eccbuf,
                struct nand_oobinfo *oobsel);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-               size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-               size_t *retlen, const u_char *buf);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+                       struct mtd_oob_ops *ops);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+                        struct mtd_oob_ops *ops);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
 
 static struct mtd_info *docmilpluslist = NULL;
        return ret;
 }
 
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                       size_t *retlen, u_char *buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+                       struct mtd_oob_ops *ops)
 {
        loff_t fofs, base;
        struct DiskOnChip *this = mtd->priv;
        void __iomem * docptr = this->virtadr;
        struct Nand *mychip = &this->chips[ofs >> this->chipshift];
        size_t i, size, got, want;
+       uint8_t *buf = ops->oobbuf;
+       size_t len = ops->len;
+
+       BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+       ofs += ops->ooboffs;
 
        DoC_CheckASIC(docptr);
 
        /* Disable flash internally */
        WriteDOC(0, docptr, Mplus_FlashSelect);
 
-       *retlen = len;
+       ops->retlen = len;
        return 0;
 }
 
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-                        size_t *retlen, const u_char *buf)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+                        struct mtd_oob_ops *ops)
 {
        volatile char dummy;
        loff_t fofs, base;
        struct Nand *mychip = &this->chips[ofs >> this->chipshift];
        size_t i, size, got, want;
        int ret = 0;
+       uint8_t *buf = ops->oobbuf;
+       size_t len = ops->len;
+
+       BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+       ofs += ops->ooboffs;
 
        DoC_CheckASIC(docptr);
 
                        printk("MTD: Error 0x%x programming oob at 0x%x\n",
                                dummy, (int)ofs);
                        /* FIXME: implement Bad Block Replacement */
-                       *retlen = 0;
+                       ops->retlen = 0;
                        ret = -EIO;
                }
                dummy = ReadDOC(docptr, Mplus_LastDataRead);
        /* Disable flash internally */
        WriteDOC(0, docptr, Mplus_FlashSelect);
 
-       *retlen = len;
+       ops->retlen = len;
        return ret;
 }
 
 
  * Actual INFTL access routines.
  */
 
+/*
+ * Read oob data from flash
+ */
+int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+                  size_t *retlen, uint8_t *buf)
+{
+       struct mtd_oob_ops ops;
+       int res;
+
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooboffs = offs & (mtd->writesize - 1);
+       ops.ooblen = len;
+       ops.oobbuf = buf;
+       ops.datbuf = NULL;
+       ops.len = len;
+
+       res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+       *retlen = ops.retlen;
+       return res;
+}
+
+/*
+ * Write oob data to flash
+ */
+int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+                   size_t *retlen, uint8_t *buf)
+{
+       struct mtd_oob_ops ops;
+       int res;
+
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooboffs = offs & (mtd->writesize - 1);
+       ops.ooblen = len;
+       ops.oobbuf = buf;
+       ops.datbuf = NULL;
+       ops.len = len;
+
+       res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+       *retlen = ops.retlen;
+       return res;
+}
+
+/*
+ * Write data and oob to flash
+ */
+static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
+                      size_t *retlen, uint8_t *buf, uint8_t *oob)
+{
+       struct mtd_oob_ops ops;
+       int res;
+
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooboffs = offs;
+       ops.ooblen = mtd->oobsize;
+       ops.oobbuf = oob;
+       ops.datbuf = buf;
+       ops.len = len;
+
+       res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+       *retlen = ops.retlen;
+       return res;
+}
+
 /*
  * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
  *     This function is used when the give Virtual Unit Chain.
                        if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
                                continue;
 
-                       if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize)
-                                         + (block * SECTORSIZE), 16 , &retlen,
-                                         (char *)&oob) < 0)
+                       if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
+                                          + (block * SECTORSIZE), 16, &retlen,
+                                          (char *)&oob) < 0)
                                status = SECTOR_IGNORE;
                        else
                                status = oob.b.Status | oob.b.Status1;
                memset(&oob, 0xff, sizeof(struct inftl_oob));
                oob.b.Status = oob.b.Status1 = SECTOR_USED;
 
-               nand_write_raw(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
-                              (block * SECTORSIZE), SECTORSIZE, &retlen,
-                              movebuf, (char *)&oob);
+               inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
+                           (block * SECTORSIZE), SECTORSIZE, &retlen,
+                           movebuf, (char *)&oob);
        }
 
        /*
                silly = MAX_LOOPS;
 
                while (thisEUN <= inftl->lastEUN) {
-                       mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) +
-                                     blockofs, 8, &retlen, (char *)&bci);
+                       inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
+                                      blockofs, 8, &retlen, (char *)&bci);
 
                        status = bci.Status | bci.Status1;
                        DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
                nacs = 0;
                thisEUN = inftl->VUtable[thisVUC];
                if (thisEUN != BLOCK_NIL) {
-                       mtd->read_oob(mtd, thisEUN * inftl->EraseSize
-                                     + 8, 8, &retlen, (char *)&oob.u);
+                       inftl_read_oob(mtd, thisEUN * inftl->EraseSize
+                                      + 8, 8, &retlen, (char *)&oob.u);
                        anac = oob.u.a.ANAC + 1;
                        nacs = oob.u.a.NACs + 1;
                }
                oob.u.a.parityPerField = parity;
                oob.u.a.discarded = 0xaa;
 
-               mtd->write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
-                              &retlen, (char *)&oob.u);
+               inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
+                               &retlen, (char *)&oob.u);
 
                /* Also back up header... */
                oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
                oob.u.b.parityPerField = parity;
                oob.u.b.discarded = 0xaa;
 
-               mtd->write_oob(mtd, writeEUN * inftl->EraseSize +
-                              SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
+               inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
+                               SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
 
                inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
                inftl->VUtable[thisVUC] = writeEUN;
                        if (BlockUsed[block] || BlockDeleted[block])
                                continue;
 
-                       if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize)
-                                         + (block * SECTORSIZE), 8 , &retlen,
+                       if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
+                                          + (block * SECTORSIZE), 8 , &retlen,
                                          (char *)&bci) < 0)
                                status = SECTOR_IGNORE;
                        else
                "block=%d)\n", inftl, block);
 
        while (thisEUN < inftl->nb_blocks) {
-               if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) +
-                                 blockofs, 8, &retlen, (char *)&bci) < 0)
+               if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
+                                  blockofs, 8, &retlen, (char *)&bci) < 0)
                        status = SECTOR_IGNORE;
                else
                        status = bci.Status | bci.Status1;
        if (thisEUN != BLOCK_NIL) {
                loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
 
-               if (mtd->read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
+               if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
                        return -EIO;
                bci.Status = bci.Status1 = SECTOR_DELETED;
-               if (mtd->write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
+               if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
                        return -EIO;
                INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
        }
                memset(&oob, 0xff, sizeof(struct inftl_oob));
                oob.b.Status = oob.b.Status1 = SECTOR_USED;
 
-               nand_write_raw(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
-                              blockofs, SECTORSIZE, &retlen, (char *)buffer,
-                              (char *)&oob);
+               inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
+                           blockofs, SECTORSIZE, &retlen, (char *)buffer,
+                           (char *)&oob);
                /*
                 * need to write SECTOR_USED flags since they are not written
                 * in mtd_writeecc
                "buffer=%p)\n", inftl, block, buffer);
 
        while (thisEUN < inftl->nb_blocks) {
-               if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) +
+               if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
                                  blockofs, 8, &retlen, (char *)&bci) < 0)
                        status = SECTOR_IGNORE;
                else
 
 
 char inftlmountrev[]="$Revision: 1.18 $";
 
+extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+                         size_t *retlen, uint8_t *buf);
+extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+                          size_t *retlen, uint8_t *buf);
+
 /*
  * find_boot_record: Find the INFTL Media Header and its Spare copy which
  *     contains the various device information of the INFTL partition and
                }
 
                /* To be safer with BIOS, also use erase mark as discriminant */
-               if ((ret = mtd->read_oob(mtd, block * inftl->EraseSize +
-                                        SECTORSIZE + 8, 8, &retlen,
-                                        (char *)&h1) < 0)) {
+               if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize +
+                                         SECTORSIZE + 8, 8, &retlen,
+                                         (char *)&h1) < 0)) {
                        printk(KERN_WARNING "INFTL: ANAND header found at "
                                "0x%x in mtd%d, but OOB data read failed "
                                "(err %d)\n", block * inftl->EraseSize,
                        return -1;
 
                if (check_oob) {
-                       if(mtd->read_oob(mtd, address, mtd->oobsize,
-                                        &retlen, &buf[SECTORSIZE]) < 0)
+                       if(inftl_read_oob(mtd, address, mtd->oobsize,
+                                         &retlen, &buf[SECTORSIZE]) < 0)
                                return -1;
                        if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
                                return -1;
        uci.Reserved[2] = 0;
        uci.Reserved[3] = 0;
        instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
-       if (mtd->write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0)
+       if (inftl_write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0)
                goto fail;
        return 0;
 fail:
                                break;
                        }
 
-                       if (mtd->read_oob(mtd, block * s->EraseSize + 8,
-                                         8, &retlen, (char *)&h0) < 0 ||
-                           mtd->read_oob(mtd, block * s->EraseSize +
-                                         2 * SECTORSIZE + 8, 8, &retlen,
-                                         (char *)&h1) < 0) {
+                       if (inftl_read_oob(mtd, block * s->EraseSize + 8,
+                                          8, &retlen, (char *)&h0) < 0 ||
+                           inftl_read_oob(mtd, block * s->EraseSize +
+                                          2 * SECTORSIZE + 8, 8, &retlen,
+                                          (char *)&h1) < 0) {
                                /* Should never happen? */
                                do_format_chain++;
                                break;
 
        case MEMWRITEOOB:
        {
                struct mtd_oob_buf buf;
-               void *databuf;
-               ssize_t retlen;
+               struct mtd_oob_ops ops;
 
                if(!(file->f_mode & 2))
                        return -EPERM;
                if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
                        return -EFAULT;
 
-               if (buf.length > 0x4096)
+               if (buf.length > 4096)
                        return -EINVAL;
 
                if (!mtd->write_oob)
                if (ret)
                        return ret;
 
-               databuf = kmalloc(buf.length, GFP_KERNEL);
-               if (!databuf)
+               ops.len = buf.length;
+               ops.ooblen = mtd->oobsize;
+               ops.ooboffs = buf.start & (mtd->oobsize - 1);
+               ops.datbuf = NULL;
+               ops.mode = MTD_OOB_PLACE;
+
+               if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs))
+                       return -EINVAL;
+
+               ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
+               if (!ops.oobbuf)
                        return -ENOMEM;
 
-               if (copy_from_user(databuf, buf.ptr, buf.length)) {
-                       kfree(databuf);
+               if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
+                       kfree(ops.oobbuf);
                        return -EFAULT;
                }
 
-               ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
+               buf.start &= ~(mtd->oobsize - 1);
+               ret = mtd->write_oob(mtd, buf.start, &ops);
 
-               if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t)))
+               if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen,
+                                sizeof(uint32_t)))
                        ret = -EFAULT;
 
-               kfree(databuf);
+               kfree(ops.oobbuf);
                break;
 
        }
        case MEMREADOOB:
        {
                struct mtd_oob_buf buf;
-               void *databuf;
-               ssize_t retlen;
+               struct mtd_oob_ops ops;
 
                if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
                        return -EFAULT;
 
-               if (buf.length > 0x4096)
+               if (buf.length > 4096)
                        return -EINVAL;
 
                if (!mtd->read_oob)
                else
                        ret = access_ok(VERIFY_WRITE, buf.ptr,
                                        buf.length) ? 0 : -EFAULT;
-
                if (ret)
                        return ret;
 
-               databuf = kmalloc(buf.length, GFP_KERNEL);
-               if (!databuf)
+               ops.len = buf.length;
+               ops.ooblen = mtd->oobsize;
+               ops.ooboffs = buf.start & (mtd->oobsize - 1);
+               ops.datbuf = NULL;
+               ops.mode = MTD_OOB_PLACE;
+
+               if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs))
+                       return -EINVAL;
+
+               ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
+               if (!ops.oobbuf)
                        return -ENOMEM;
 
-               ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
+               buf.start &= ~(mtd->oobsize - 1);
+               ret = mtd->read_oob(mtd, buf.start, &ops);
 
-               if (put_user(retlen, (uint32_t __user *)argp))
+               if (put_user(ops.retlen, (uint32_t __user *)argp))
                        ret = -EFAULT;
-               else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
+               else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf,
+                                                   ops.retlen))
                        ret = -EFAULT;
 
-               kfree(databuf);
+               kfree(ops.oobbuf);
                break;
        }
 
 
 }
 
 static int
-concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-               size_t * retlen, u_char * buf)
+concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
 {
        struct mtd_concat *concat = CONCAT(mtd);
-       int err = -EINVAL;
-       int i;
+       struct mtd_oob_ops devops = *ops;
+       int i, err;
 
-       *retlen = 0;
+       ops->retlen = 0;
 
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
-               size_t size, retsize;
 
                if (from >= subdev->size) {
-                       /* Not destined for this subdev */
-                       size = 0;
                        from -= subdev->size;
                        continue;
                }
-               if (from + len > subdev->size)
-                       /* First part goes into this subdev */
-                       size = subdev->size - from;
-               else
-                       /* Entire transaction goes into this subdev */
-                       size = len;
 
-               if (subdev->read_oob)
-                       err = subdev->read_oob(subdev, from, size,
-                                              &retsize, buf);
-               else
-                       err = -EINVAL;
+               /* partial read ? */
+               if (from + devops.len > subdev->size)
+                       devops.len = subdev->size - from;
 
+               err = subdev->read_oob(subdev, from, &devops);
+               ops->retlen += devops.retlen;
                if (err)
-                       break;
+                       return err;
 
-               *retlen += retsize;
-               len -= size;
-               if (len == 0)
-                       break;
+               devops.len = ops->len - ops->retlen;
+               if (!devops.len)
+                       return 0;
+
+               if (devops.datbuf)
+                       devops.datbuf += devops.retlen;
+               if (devops.oobbuf)
+                       devops.oobbuf += devops.ooblen;
 
-               err = -EINVAL;
-               buf += size;
                from = 0;
        }
-       return err;
+       return -EINVAL;
 }
 
 static int
-concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
-                size_t * retlen, const u_char * buf)
+concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
 {
        struct mtd_concat *concat = CONCAT(mtd);
-       int err = -EINVAL;
-       int i;
+       struct mtd_oob_ops devops = *ops;
+       int i, err;
 
        if (!(mtd->flags & MTD_WRITEABLE))
                return -EROFS;
 
-       *retlen = 0;
+       ops->retlen = 0;
 
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
-               size_t size, retsize;
 
                if (to >= subdev->size) {
-                       size = 0;
                        to -= subdev->size;
                        continue;
                }
-               if (to + len > subdev->size)
-                       size = subdev->size - to;
-               else
-                       size = len;
 
-               if (!(subdev->flags & MTD_WRITEABLE))
-                       err = -EROFS;
-               else if (subdev->write_oob)
-                       err = subdev->write_oob(subdev, to, size, &retsize,
-                                               buf);
-               else
-                       err = -EINVAL;
+               /* partial write ? */
+               if (to + devops.len > subdev->size)
+                       devops.len = subdev->size - to;
 
+               err = subdev->write_oob(subdev, to, &devops);
+               ops->retlen += devops.retlen;
                if (err)
-                       break;
+                       return err;
 
-               *retlen += retsize;
-               len -= size;
-               if (len == 0)
-                       break;
+               devops.len = ops->len - ops->retlen;
+               if (!devops.len)
+                       return 0;
 
-               err = -EINVAL;
-               buf += size;
+               if (devops.datbuf)
+                       devops.datbuf += devops.retlen;
+               if (devops.oobbuf)
+                       devops.oobbuf += devops.ooblen;
                to = 0;
        }
-       return err;
+       return -EINVAL;
 }
 
 static void concat_erase_callback(struct erase_info *instr)
 
        part->master->unpoint (part->master, addr, from + part->offset, len);
 }
 
-static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
-                       size_t *retlen, u_char *buf)
+static int part_read_oob(struct mtd_info *mtd, loff_t from,
+                        struct mtd_oob_ops *ops)
 {
        struct mtd_part *part = PART(mtd);
+
        if (from >= mtd->size)
-               len = 0;
-       else if (from + len > mtd->size)
-               len = mtd->size - from;
-       return part->master->read_oob (part->master, from + part->offset,
-                                       len, retlen, buf);
+               return -EINVAL;
+       if (from + ops->len > mtd->size)
+               return -EINVAL;
+       return part->master->read_oob(part->master, from + part->offset, ops);
 }
 
 static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
                                    len, retlen, buf);
 }
 
-static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
-                       size_t *retlen, const u_char *buf)
+static int part_write_oob(struct mtd_info *mtd, loff_t to,
+                        struct mtd_oob_ops *ops)
 {
        struct mtd_part *part = PART(mtd);
+
        if (!(mtd->flags & MTD_WRITEABLE))
                return -EROFS;
+
        if (to >= mtd->size)
-               len = 0;
-       else if (to + len > mtd->size)
-               len = mtd->size - to;
-       return part->master->write_oob (part->master, to + part->offset,
-                                       len, retlen, buf);
+               return -EINVAL;
+       if (to + ops->len > mtd->size)
+               return -EINVAL;
+       return part->master->write_oob(part->master, to + part->offset, ops);
 }
 
 static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
 
                 .length = 38}}
 };
 
-/* This is used for padding purposes in nand_write_oob */
-static uint8_t ffchars[] = {
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-};
-
-static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
-                         size_t *retlen, const uint8_t *buf);
 static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
                           int new_state);
 
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+                            struct mtd_oob_ops *ops);
+
 /*
  * For devices which display every fart in the system on a seperate LED. Is
  * compiled away when LED support is disabled.
 {
        struct nand_chip *chip = mtd->priv;
        uint8_t buf[2] = { 0, 0 };
-       size_t retlen;
        int block;
 
        /* Get block number */
                return nand_update_bbt(mtd, ofs);
 
        /* We write two bytes, so we dont have to mess with 16 bit access */
-       ofs += mtd->oobsize + (chip->badblockpos & ~0x01);
-       return nand_write_oob(mtd, ofs, 2, &retlen, buf);
+       ofs += mtd->oobsize;
+       chip->ops.len = 2;
+       chip->ops.datbuf = NULL;
+       chip->ops.oobbuf = buf;
+       chip->ops.ooboffs = chip->badblockpos & ~0x01;
+
+       return nand_do_write_oob(mtd, ofs, &chip->ops);
 }
 
 /**
        return status;
 }
 
+/**
+ * nand_read_page_raw - [Intern] read raw page data without ecc
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       buffer to store read data
+ */
+static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                             uint8_t *buf)
+{
+       chip->read_buf(mtd, buf, mtd->writesize);
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+       return 0;
+}
+
 /**
  * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
  * @mtd:       mtd info structure
        uint8_t *ecc_code = chip->buffers.ecccode;
        int *eccpos = chip->ecc.layout->eccpos;
 
-       chip->read_buf(mtd, buf, mtd->writesize);
-       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
-       if (chip->ecc.mode == NAND_ECC_NONE)
-               return 0;
+       nand_read_page_raw(mtd, chip, buf);
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 }
 
 /**
- * nand_do_read - [Internal] Read data with ECC
+ * nand_transfer_oob - [Internal] Transfer oob to client buffer
+ * @chip:      nand chip structure
+ * @ops:       oob ops structure
+ */
+static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+                                 struct mtd_oob_ops *ops)
+{
+       size_t len = ops->ooblen;
+
+       switch(ops->mode) {
+
+       case MTD_OOB_PLACE:
+       case MTD_OOB_RAW:
+               memcpy(oob, chip->oob_poi + ops->ooboffs, len);
+               return oob + len;
+
+       case MTD_OOB_AUTO: {
+               struct nand_oobfree *free = chip->ecc.layout->oobfree;
+               size_t bytes;
+
+               for(; free->length && len; free++, len -= bytes) {
+                       bytes = min(len, free->length);
+
+                       memcpy(oob, chip->oob_poi + free->offset, bytes);
+                       oob += bytes;
+               }
+               return oob;
+       }
+       default:
+               BUG();
+       }
+       return NULL;
+}
+
+/**
+ * nand_do_read_ops - [Internal] Read data with ECC
  *
  * @mtd:       MTD device structure
  * @from:      offset to read from
- * @len:       number of bytes to read
- * @retlen:    pointer to variable to store the number of read bytes
- * @buf:       the databuffer to put data
  *
  * Internal function. Called with chip held.
  */
-int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
-                size_t *retlen, uint8_t *buf)
+static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+                           struct mtd_oob_ops *ops)
 {
        int chipnr, page, realpage, col, bytes, aligned;
        struct nand_chip *chip = mtd->priv;
        int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
        int sndcmd = 1;
        int ret = 0;
-       uint32_t readlen = len;
-       uint8_t *bufpoi;
+       uint32_t readlen = ops->len;
+       uint8_t *bufpoi, *oob, *buf;
 
        stats = mtd->ecc_stats;
 
        col = (int)(from & (mtd->writesize - 1));
        chip->oob_poi = chip->buffers.oobrbuf;
 
+       buf = ops->datbuf;
+       oob = ops->oobbuf;
+
        while(1) {
                bytes = min(mtd->writesize - col, readlen);
                aligned = (bytes == mtd->writesize);
 
                /* Is the current page in the buffer ? */
-               if (realpage != chip->pagebuf) {
+               if (realpage != chip->pagebuf || oob) {
                        bufpoi = aligned ? buf : chip->buffers.databuf;
 
                        if (likely(sndcmd)) {
                                memcpy(buf, chip->buffers.databuf + col, bytes);
                        }
 
+                       buf += bytes;
+
+                       if (unlikely(oob)) {
+                               /* Raw mode does data:oob:data:oob */
+                               if (ops->mode != MTD_OOB_RAW)
+                                       oob = nand_transfer_oob(chip, oob, ops);
+                               else
+                                       buf = nand_transfer_oob(chip, buf, ops);
+                       }
+
                        if (!(chip->options & NAND_NO_READRDY)) {
                                /*
                                 * Apply delay or wait for ready/busy pin. Do
                                else
                                        nand_wait_ready(mtd);
                        }
-               } else
+               } else {
                        memcpy(buf, chip->buffers.databuf + col, bytes);
+                       buf += bytes;
+               }
 
-               buf += bytes;
                readlen -= bytes;
 
                if (!readlen)
                        sndcmd = 1;
        }
 
-       *retlen = len - (size_t) readlen;
+       ops->retlen = ops->len - (size_t) readlen;
 
        if (ret)
                return ret;
 static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
                     size_t *retlen, uint8_t *buf)
 {
+       struct nand_chip *chip = mtd->priv;
        int ret;
 
-       *retlen = 0;
        /* Do not allow reads past end of device */
        if ((from + len) > mtd->size)
                return -EINVAL;
        if (!len)
                return 0;
 
-       nand_get_device(mtd->priv, mtd, FL_READING);
+       nand_get_device(chip, mtd, FL_READING);
 
-       ret = nand_do_read(mtd, from, len, retlen, buf);
+       chip->ops.len = len;
+       chip->ops.datbuf = buf;
+       chip->ops.oobbuf = NULL;
+
+       ret = nand_do_read_ops(mtd, from, &chip->ops);
 
        nand_release_device(mtd);
 
+       *retlen = chip->ops.retlen;
        return ret;
 }
 
 /**
- * nand_read_oob - [MTD Interface] NAND read out-of-band
+ * nand_do_read_oob - [Intern] NAND read out-of-band
  * @mtd:       MTD device structure
  * @from:      offset to read from
- * @len:       number of bytes to read
- * @retlen:    pointer to variable to store the number of read bytes
- * @buf:       the databuffer to put data
+ * @ops:       oob operations description structure
  *
  * NAND read out-of-band data from the spare area
  */
-static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-                        size_t *retlen, uint8_t *buf)
+static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+                           struct mtd_oob_ops *ops)
 {
        int col, page, realpage, chipnr, sndcmd = 1;
        struct nand_chip *chip = mtd->priv;
        int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
-       int readlen = len;
+       int direct, bytes, readlen = ops->len;
+       uint8_t *bufpoi, *buf = ops->oobbuf;
 
        DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n",
              (unsigned int)from, (int)len);
 
-       /* Initialize return length value */
-       *retlen = 0;
-
-       /* Do not allow reads past end of device */
-       if ((from + len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
-                     "Attempt read beyond end of device\n");
-               return -EINVAL;
-       }
-
-       nand_get_device(chip, mtd, FL_READING);
-
        chipnr = (int)(from >> chip->chip_shift);
        chip->select_chip(mtd, chipnr);
 
        realpage = (int)(from >> chip->page_shift);
        page = realpage & chip->pagemask;
 
-       /* Mask to get column */
-       col = from & (mtd->oobsize - 1);
+       if (ops->mode != MTD_OOB_AUTO) {
+               col = ops->ooboffs;
+               direct = 1;
+       } else {
+               col = 0;
+               direct = 0;
+       }
 
        while(1) {
-               int bytes = min((int)(mtd->oobsize - col), readlen);
+               bytes = direct ? ops->ooblen : mtd->oobsize;
+               bufpoi = direct ? buf : chip->buffers.oobrbuf;
 
                if (likely(sndcmd)) {
                        chip->cmdfunc(mtd, NAND_CMD_READOOB, col, page);
                        sndcmd = 0;
                }
 
-               chip->read_buf(mtd, buf, bytes);
+               chip->read_buf(mtd, bufpoi, bytes);
 
-               readlen -= bytes;
+               if (unlikely(!direct))
+                       buf = nand_transfer_oob(chip, buf, ops);
+               else
+                       buf += ops->ooblen;
+
+               readlen -= ops->ooblen;
                if (!readlen)
                        break;
 
                                nand_wait_ready(mtd);
                }
 
-               buf += bytes;
-               bytes = mtd->oobsize;
-               col = 0;
-
                /* Increment page address */
                realpage++;
 
                        sndcmd = 1;
        }
 
-       /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
-
-       *retlen = len;
+       ops->retlen = ops->len;
        return 0;
 }
 
 /**
- * nand_read_raw - [GENERIC] Read raw data including oob into buffer
+ * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
  * @mtd:       MTD device structure
- * @buf:       temporary buffer
  * @from:      offset to read from
- * @len:       number of bytes to read
- * @ooblen:    number of oob data bytes to read
+ * @ops:       oob operation description structure
  *
- * Read raw data including oob into buffer
+ * NAND read data and/or out-of-band data
  */
-int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
-                 size_t ooblen)
+static int nand_read_oob(struct mtd_info *mtd, loff_t from,
+                        struct mtd_oob_ops *ops)
 {
+       int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
+                        uint8_t *buf) = NULL;
        struct nand_chip *chip = mtd->priv;
-       int page = (int)(from >> chip->page_shift);
-       int chipnr = (int)(from >> chip->chip_shift);
-       int sndcmd = 1;
-       int cnt = 0;
-       int pagesize = mtd->writesize + mtd->oobsize;
-       int blockcheck;
+       int ret = -ENOTSUPP;
+
+       ops->retlen = 0;
 
        /* Do not allow reads past end of device */
-       if ((from + len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: "
+       if ((from + ops->len) > mtd->size) {
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
                      "Attempt read beyond end of device\n");
                return -EINVAL;
        }
 
-       /* Grab the lock and see if the device is available */
        nand_get_device(chip, mtd, FL_READING);
 
-       chip->select_chip(mtd, chipnr);
-
-       /* Add requested oob length */
-       len += ooblen;
-       blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+       switch(ops->mode) {
+       case MTD_OOB_PLACE:
+       case MTD_OOB_AUTO:
+               break;
 
-       while (len) {
-               if (likely(sndcmd)) {
-                       chip->cmdfunc(mtd, NAND_CMD_READ0, 0,
-                                     page & chip->pagemask);
-                       sndcmd = 0;
-               }
+       case MTD_OOB_RAW:
+               /* Replace the read_page algorithm temporary */
+               read_page = chip->ecc.read_page;
+               chip->ecc.read_page = nand_read_page_raw;
+               break;
 
-               chip->read_buf(mtd, &buf[cnt], pagesize);
+       default:
+               goto out;
+       }
 
-               len -= pagesize;
-               cnt += pagesize;
-               page++;
+       if (!ops->datbuf)
+               ret = nand_do_read_oob(mtd, from, ops);
+       else
+               ret = nand_do_read_ops(mtd, from, ops);
 
-               if (!(chip->options & NAND_NO_READRDY)) {
-                       if (!chip->dev_ready)
-                               udelay(chip->chip_delay);
-                       else
-                               nand_wait_ready(mtd);
-               }
+       if (unlikely(ops->mode == MTD_OOB_RAW))
+               chip->ecc.read_page = read_page;
+ out:
+       nand_release_device(mtd);
+       return ret;
+}
 
-               /*
-                * Check, if the chip supports auto page increment or if we
-                * cross a block boundary.
-                */
-               if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck))
-                       sndcmd = 1;
-       }
 
-       /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
-       return 0;
+/**
+ * nand_write_page_raw - [Intern] raw page write function
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       data buffer
+ */
+static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                               const uint8_t *buf)
+{
+       chip->write_buf(mtd, buf, mtd->writesize);
+       chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
 }
 
 /**
        const uint8_t *p = buf;
        int *eccpos = chip->ecc.layout->eccpos;
 
-       if (chip->ecc.mode != NAND_ECC_NONE) {
-               /* Software ecc calculation */
-               for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
-                       chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+       /* Software ecc calculation */
+       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+               chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
-               for (i = 0; i < chip->ecc.total; i++)
-                       chip->oob_poi[eccpos[i]] = ecc_calc[i];
-       }
+       for (i = 0; i < chip->ecc.total; i++)
+               chip->oob_poi[eccpos[i]] = ecc_calc[i];
 
-       chip->write_buf(mtd, buf, mtd->writesize);
-       chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+       nand_write_page_raw(mtd, chip, buf);
 }
 
 /**
        return 0;
 }
 
+/**
+ * nand_fill_oob - [Internal] Transfer client buffer to oob
+ * @chip:      nand chip structure
+ * @oob:       oob data buffer
+ * @ops:       oob ops structure
+ */
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
+                                 struct mtd_oob_ops *ops)
+{
+       size_t len = ops->ooblen;
+
+       switch(ops->mode) {
+
+       case MTD_OOB_PLACE:
+       case MTD_OOB_RAW:
+               memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+               return oob + len;
+
+       case MTD_OOB_AUTO: {
+               struct nand_oobfree *free = chip->ecc.layout->oobfree;
+               size_t bytes;
+
+               for(; free->length && len; free++, len -= bytes) {
+                       bytes = min(len, free->length);
+                       memcpy(chip->oob_poi + free->offset, oob, bytes);
+                       oob += bytes;
+               }
+               return oob;
+       }
+       default:
+               BUG();
+       }
+       return NULL;
+}
+
 #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
 
 /**
- * nand_write - [MTD Interface] NAND write with ECC
+ * nand_do_write_ops - [Internal] NAND write with ECC
  * @mtd:       MTD device structure
  * @to:                offset to write to
- * @len:       number of bytes to write
- * @retlen:    pointer to variable to store the number of written bytes
- * @buf:       the data to write
+ * @ops:       oob operations description structure
  *
  * NAND write with ECC
  */
-static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
-                         size_t *retlen, const uint8_t *buf)
+static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+                            struct mtd_oob_ops *ops)
 {
        int chipnr, realpage, page, blockmask;
        struct nand_chip *chip = mtd->priv;
-       uint32_t writelen = len;
+       uint32_t writelen = ops->len;
+       uint8_t *oob = ops->oobbuf;
+       uint8_t *buf = ops->datbuf;
        int bytes = mtd->writesize;
-       int ret = -EIO;
+       int ret;
 
-       *retlen = 0;
-
-       /* Do not allow write past end of device */
-       if ((to + len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_write: "
-                     "Attempt to write past end of page\n");
-               return -EINVAL;
-       }
+       ops->retlen = 0;
 
        /* reject writes, which are not page aligned */
-       if (NOTALIGNED(to) || NOTALIGNED(len)) {
+       if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
                printk(KERN_NOTICE "nand_write: "
                       "Attempt to write not page aligned data\n");
                return -EINVAL;
        }
 
-       if (!len)
+       if (!writelen)
                return 0;
 
-       nand_get_device(chip, mtd, FL_WRITING);
-
        /* Check, if it is write protected */
        if (nand_check_wp(mtd))
-               goto out;
+               return -EIO;
 
        chipnr = (int)(to >> chip->chip_shift);
        chip->select_chip(mtd, chipnr);
 
        /* Invalidate the page cache, when we write to the cached page */
        if (to <= (chip->pagebuf << chip->page_shift) &&
-           (chip->pagebuf << chip->page_shift) < (to + len))
+           (chip->pagebuf << chip->page_shift) < (to + ops->len))
                chip->pagebuf = -1;
 
        chip->oob_poi = chip->buffers.oobwbuf;
        while(1) {
                int cached = writelen > bytes && page != blockmask;
 
+               if (unlikely(oob))
+                       oob = nand_fill_oob(chip, oob, ops);
+
                ret = nand_write_page(mtd, chip, buf, page, cached);
                if (ret)
                        break;
                        chip->select_chip(mtd, chipnr);
                }
        }
- out:
-       *retlen = len - writelen;
-       nand_release_device(mtd);
+
+       if (unlikely(oob))
+               memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+       ops->retlen = ops->len - writelen;
        return ret;
 }
 
 /**
- * nand_write_raw - [GENERIC] Write raw data including oob
+ * nand_write - [MTD Interface] NAND write with ECC
  * @mtd:       MTD device structure
- * @buf:       source buffer
  * @to:                offset to write to
  * @len:       number of bytes to write
- * @buf:       source buffer
- * @oob:       oob buffer
+ * @retlen:    pointer to variable to store the number of written bytes
+ * @buf:       the data to write
  *
- * Write raw data including oob
+ * NAND write with ECC
  */
-int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
-                  const uint8_t *buf, uint8_t *oob)
+static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+                         size_t *retlen, const uint8_t *buf)
 {
        struct nand_chip *chip = mtd->priv;
-       int page = (int)(to >> chip->page_shift);
-       int chipnr = (int)(to >> chip->chip_shift);
        int ret;
 
-       *retlen = 0;
-
-       /* Do not allow writes past end of device */
-       if ((to + len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write "
-                     "beyond end of device\n");
+       /* Do not allow reads past end of device */
+       if ((to + len) > mtd->size)
                return -EINVAL;
-       }
+       if (!len)
+               return 0;
 
-       /* Grab the lock and see if the device is available */
-       nand_get_device(chip, mtd, FL_WRITING);
+       nand_get_device(chip, mtd, FL_READING);
 
-       chip->select_chip(mtd, chipnr);
-       chip->oob_poi = oob;
+       chip->ops.len = len;
+       chip->ops.datbuf = (uint8_t *)buf;
+       chip->ops.oobbuf = NULL;
 
-       while (len != *retlen) {
-               ret = nand_write_page(mtd, chip, buf, page, 0);
-               if (ret)
-                       return ret;
-               page++;
-               *retlen += mtd->writesize;
-               buf += mtd->writesize;
-               chip->oob_poi += mtd->oobsize;
-       }
+       ret = nand_do_write_ops(mtd, to, &chip->ops);
 
-       /* Deselect and wake up anyone waiting on the device */
        nand_release_device(mtd);
-       return 0;
+
+       *retlen = chip->ops.retlen;
+       return ret;
 }
-EXPORT_SYMBOL_GPL(nand_write_raw);
 
 /**
- * nand_write_oob - [MTD Interface] NAND write out-of-band
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
  * @mtd:       MTD device structure
  * @to:                offset to write to
- * @len:       number of bytes to write
- * @retlen:    pointer to variable to store the number of written bytes
- * @buf:       the data to write
+ * @ops:       oob operation description structure
  *
  * NAND write out-of-band
  */
-static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
-                         size_t *retlen, const uint8_t *buf)
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+                            struct mtd_oob_ops *ops)
 {
-       int column, page, status, ret = -EIO, chipnr;
+       int chipnr, page, status;
        struct nand_chip *chip = mtd->priv;
 
        DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
-             (unsigned int)to, (int)len);
-
-       /* Initialize return length value */
-       *retlen = 0;
+             (unsigned int)to, (int)ops->len);
 
        /* Do not allow write past end of page */
-       column = to & (mtd->oobsize - 1);
-       if ((column + len) > mtd->oobsize) {
+       if ((ops->ooboffs + ops->len) > mtd->oobsize) {
                DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
                      "Attempt to write past end of page\n");
                return -EINVAL;
        }
 
-       nand_get_device(chip, mtd, FL_WRITING);
-
        chipnr = (int)(to >> chip->chip_shift);
        chip->select_chip(mtd, chipnr);
 
 
        /* Check, if it is write protected */
        if (nand_check_wp(mtd))
-               goto out;
+               return -EROFS;
 
        /* Invalidate the page cache, if we write to the cached page */
        if (page == chip->pagebuf)
                chip->pagebuf = -1;
 
-       if (NAND_MUST_PAD(chip)) {
+       if (ops->mode == MTD_OOB_AUTO || NAND_MUST_PAD(chip)) {
+               chip->oob_poi = chip->buffers.oobwbuf;
+               memset(chip->oob_poi, 0xff, mtd->oobsize);
+               nand_fill_oob(chip, ops->oobbuf, ops);
                chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize,
                              page & chip->pagemask);
-               /* prepad 0xff for partial programming */
-               chip->write_buf(mtd, ffchars, column);
-               /* write data */
-               chip->write_buf(mtd, buf, len);
-               /* postpad 0xff for partial programming */
-               chip->write_buf(mtd, ffchars, mtd->oobsize - (len + column));
+               chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+               memset(chip->oob_poi, 0xff, mtd->oobsize);
        } else {
-               chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + column,
+               chip->cmdfunc(mtd, NAND_CMD_SEQIN,
+                             mtd->writesize + ops->ooboffs,
                              page & chip->pagemask);
-               chip->write_buf(mtd, buf, len);
+               chip->write_buf(mtd, ops->oobbuf, ops->len);
        }
+
        /* Send command to program the OOB data */
        chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
 
        if (status & NAND_STATUS_FAIL) {
                DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
                      "Failed write, page 0x%08x\n", page);
-               ret = -EIO;
-               goto out;
+               return -EIO;
        }
-       *retlen = len;
+       ops->retlen = ops->len;
 
 #ifdef CONFIG_MTD_NAND_VERIFY_WRITE
-       /* Send command to read back the data */
-       chip->cmdfunc(mtd, NAND_CMD_READOOB, column, page & chip->pagemask);
+       if (ops->mode != MTD_OOB_AUTO) {
+               /* Send command to read back the data */
+               chip->cmdfunc(mtd, NAND_CMD_READOOB, ops->ooboffs,
+                             page & chip->pagemask);
 
-       if (chip->verify_buf(mtd, buf, len)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
-                     "Failed write verify, page 0x%08x\n", page);
-               ret = -EIO;
-               goto out;
+               if (chip->verify_buf(mtd, ops->oobbuf, ops->len)) {
+                       DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
+                             "Failed write verify, page 0x%08x\n", page);
+                       return -EIO;
+               }
        }
 #endif
-       ret = 0;
+               return 0;
+}
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd:       MTD device structure
+ * @from:      offset to read from
+ * @ops:       oob operation description structure
+ */
+static int nand_write_oob(struct mtd_info *mtd, loff_t to,
+                         struct mtd_oob_ops *ops)
+{
+       void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
+                         const uint8_t *buf) = NULL;
+       struct nand_chip *chip = mtd->priv;
+       int ret = -ENOTSUPP;
+
+       ops->retlen = 0;
+
+       /* Do not allow writes past end of device */
+       if ((to + ops->len) > mtd->size) {
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+                     "Attempt read beyond end of device\n");
+               return -EINVAL;
+       }
+
+       nand_get_device(chip, mtd, FL_READING);
+
+       switch(ops->mode) {
+       case MTD_OOB_PLACE:
+       case MTD_OOB_AUTO:
+               break;
+
+       case MTD_OOB_RAW:
+               /* Replace the write_page algorithm temporary */
+               write_page = chip->ecc.write_page;
+               chip->ecc.write_page = nand_write_page_raw;
+               break;
+
+       default:
+               goto out;
+       }
+
+       if (!ops->datbuf)
+               ret = nand_do_write_oob(mtd, to, ops);
+       else
+               ret = nand_do_write_ops(mtd, to, ops);
+
+       if (unlikely(ops->mode == MTD_OOB_RAW))
+               chip->ecc.write_page = write_page;
  out:
-       /* Deselect and wake up anyone waiting on the device */
        nand_release_device(mtd);
-
        return ret;
 }
 
        case NAND_ECC_NONE:
                printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
                       "This is not recommended !!\n");
-               chip->ecc.read_page = nand_read_page_swecc;
-               chip->ecc.write_page = nand_write_page_swecc;
+               chip->ecc.read_page = nand_read_page_raw;
+               chip->ecc.write_page = nand_write_page_raw;
                chip->ecc.size = mtd->writesize;
                chip->ecc.bytes = 0;
                break;
 
        return 0;
 }
 
+/*
+ * Scan read raw data from flash
+ */
+static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+                        size_t len)
+{
+       struct mtd_oob_ops ops;
+
+       ops.mode = MTD_OOB_RAW;
+       ops.ooboffs = 0;
+       ops.ooblen = mtd->oobsize;
+       ops.oobbuf = buf;
+       ops.datbuf = buf;
+       ops.len = len;
+
+       return mtd->read_oob(mtd, offs, &ops);
+}
+
+/*
+ * Scan write data with oob to flash
+ */
+static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
+                         uint8_t *buf, uint8_t *oob)
+{
+       struct mtd_oob_ops ops;
+
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooboffs = 0;
+       ops.ooblen = mtd->oobsize;
+       ops.datbuf = buf;
+       ops.oobbuf = oob;
+       ops.len = len;
+
+       return mtd->write_oob(mtd, offs, &ops);
+}
+
 /**
  * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
  * @mtd:       MTD device structure
  * We assume that the bbt bits are in consecutive order.
  *
 */
-static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+                        struct nand_bbt_descr *td, struct nand_bbt_descr *md)
 {
        struct nand_chip *this = mtd->priv;
 
        /* Read the primary version, if available */
        if (td->options & NAND_BBT_VERSION) {
-               nand_read_raw(mtd, buf, td->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize);
+               scan_read_raw(mtd, buf, td->pages[0] << this->page_shift,
+                             mtd->writesize);
                td->version[0] = buf[mtd->writesize + td->veroffs];
-               printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]);
+               printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
+                      td->pages[0], td->version[0]);
        }
 
        /* Read the mirror version, if available */
        if (md && (md->options & NAND_BBT_VERSION)) {
-               nand_read_raw(mtd, buf, md->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize);
+               scan_read_raw(mtd, buf, md->pages[0] << this->page_shift,
+                             mtd->writesize);
                md->version[0] = buf[mtd->writesize + md->veroffs];
-               printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
+               printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
+                      md->pages[0], md->version[0]);
        }
-
        return 1;
 }
 
+/*
+ * Scan a given block full
+ */
+static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+                          loff_t offs, uint8_t *buf, size_t readlen,
+                          int scanlen, int len)
+{
+       int ret, j;
+
+       ret = scan_read_raw(mtd, buf, offs, readlen);
+       if (ret)
+               return ret;
+
+       for (j = 0; j < len; j++, buf += scanlen) {
+               if (check_pattern(buf, scanlen, mtd->writesize, bd))
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * Scan a given block partially
+ */
+static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+                          loff_t offs, uint8_t *buf, int len)
+{
+       struct mtd_oob_ops ops;
+       int j, ret;
+
+       ops.len = mtd->oobsize;
+       ops.ooblen = mtd->oobsize;
+       ops.oobbuf = buf;
+       ops.ooboffs = 0;
+       ops.datbuf = NULL;
+       ops.mode = MTD_OOB_PLACE;
+
+       for (j = 0; j < len; j++) {
+               /*
+                * Read the full oob until read_oob is fixed to
+                * handle single byte reads for 16 bit
+                * buswidth
+                */
+               ret = mtd->read_oob(mtd, offs, &ops);
+               if (ret)
+                       return ret;
+
+               if (check_short_pattern(buf, bd))
+                       return 1;
+
+               offs += mtd->writesize;
+       }
+       return 0;
+}
+
 /**
  * create_bbt - [GENERIC] Create a bad block table by scanning the device
  * @mtd:       MTD device structure
  * Create a bad block table by scanning the device
  * for the given good/bad block identify pattern
  */
-static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+       struct nand_bbt_descr *bd, int chip)
 {
        struct nand_chip *this = mtd->priv;
-       int i, j, numblocks, len, scanlen;
+       int i, numblocks, len, scanlen;
        int startblock;
        loff_t from;
-       size_t readlen, ooblen;
+       size_t readlen;
 
        printk(KERN_INFO "Scanning device for bad blocks\n");
 
 
        if (!(bd->options & NAND_BBT_SCANEMPTY)) {
                /* We need only read few bytes from the OOB area */
-               scanlen = ooblen = 0;
+               scanlen = 0;
                readlen = bd->len;
        } else {
                /* Full page content should be read */
                scanlen = mtd->writesize + mtd->oobsize;
                readlen = len * mtd->writesize;
-               ooblen = len * mtd->oobsize;
        }
 
        if (chip == -1) {
-               /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
-                * makes shifting and masking less painful */
+               /* Note that numblocks is 2 * (real numblocks) here, see i+=2
+                * below as it makes shifting and masking less painful */
                numblocks = mtd->size >> (this->bbt_erase_shift - 1);
                startblock = 0;
                from = 0;
        for (i = startblock; i < numblocks;) {
                int ret;
 
-               if (bd->options & NAND_BBT_SCANEMPTY)
-                       if ((ret = nand_read_raw(mtd, buf, from, readlen, ooblen)))
-                               return ret;
-
-               for (j = 0; j < len; j++) {
-                       if (!(bd->options & NAND_BBT_SCANEMPTY)) {
-                               size_t retlen;
-
-                               /* Read the full oob until read_oob is fixed to
-                                * handle single byte reads for 16 bit buswidth */
-                               ret = mtd->read_oob(mtd, from + j * mtd->writesize, mtd->oobsize, &retlen, buf);
-                               if (ret)
-                                       return ret;
-
-                               if (check_short_pattern(buf, bd)) {
-                                       this->bbt[i >> 3] |= 0x03 << (i & 0x6);
-                                       printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
-                                              i >> 1, (unsigned int)from);
-                                       break;
-                               }
-                       } else {
-                               if (check_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
-                                       this->bbt[i >> 3] |= 0x03 << (i & 0x6);
-                                       printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
-                                              i >> 1, (unsigned int)from);
-                                       break;
-                               }
-                       }
+               if (bd->options & NAND_BBT_SCANALLPAGES)
+                       ret = scan_block_full(mtd, bd, from, buf, readlen,
+                                             scanlen, len);
+               else
+                       ret = scan_block_fast(mtd, bd, from, buf, len);
+
+               if (ret < 0)
+                       return ret;
+
+               if (ret) {
+                       this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+                       printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
+                              i >> 1, (unsigned int)from);
                }
+
                i += 2;
                from += (1 << this->bbt_erase_shift);
        }
        int bits, startblock, block, dir;
        int scanlen = mtd->writesize + mtd->oobsize;
        int bbtblocks;
+       int blocktopage = this->bbt_erase_shift - this->page_shift;
 
        /* Search direction top -> down ? */
        if (td->options & NAND_BBT_LASTBLOCK) {
                td->pages[i] = -1;
                /* Scan the maximum number of blocks */
                for (block = 0; block < td->maxblocks; block++) {
+
                        int actblock = startblock + dir * block;
+                       loff_t offs = actblock << this->bbt_erase_shift;
+
                        /* Read first page */
-                       nand_read_raw(mtd, buf, actblock << this->bbt_erase_shift, mtd->writesize, mtd->oobsize);
+                       scan_read_raw(mtd, buf, offs, mtd->writesize);
                        if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
-                               td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
+                               td->pages[i] = actblock << blocktopage;
                                if (td->options & NAND_BBT_VERSION) {
                                        td->version[i] = buf[mtd->writesize + td->veroffs];
                                }
        int nrchips, bbtoffs, pageoffs, ooboffs;
        uint8_t msk[4];
        uint8_t rcode = td->reserved_block_code;
-       size_t retlen, len = 0, ooblen;
+       size_t retlen, len = 0;
        loff_t to;
+       struct mtd_oob_ops ops;
+
+       ops.ooblen = mtd->oobsize;
+       ops.ooboffs = 0;
+       ops.datbuf = NULL;
+       ops.mode = MTD_OOB_PLACE;
 
        if (!rcode)
                rcode = 0xff;
                                       "bad block table\n");
                        }
                        /* Read oob data */
-                       ooblen = (len >> this->page_shift) * mtd->oobsize;
-                       res = mtd->read_oob(mtd, to + mtd->writesize, ooblen,
-                                           &retlen, &buf[len]);
-                       if (res < 0 || retlen != ooblen)
+                       ops.len = (len >> this->page_shift) * mtd->oobsize;
+                       ops.oobbuf = &buf[len];
+                       res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
+                       if (res < 0 || ops.retlen != ops.len)
                                goto outerr;
 
                        /* Calc the byte offset in the buffer */
                if (res < 0)
                        goto outerr;
 
-               res = nand_write_raw(mtd, to, len, &retlen, buf, &buf[len]);
+               res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
                if (res < 0)
                        goto outerr;
 
 
        kfree(nftl);
 }
 
+/*
+ * Read oob data from flash
+ */
+int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+                 size_t *retlen, uint8_t *buf)
+{
+       struct mtd_oob_ops ops;
+       int res;
+
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooboffs = offs & (mtd->writesize - 1);
+       ops.ooblen = len;
+       ops.oobbuf = buf;
+       ops.datbuf = NULL;
+       ops.len = len;
+
+       res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+       *retlen = ops.retlen;
+       return res;
+}
+
+/*
+ * Write oob data to flash
+ */
+int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+                  size_t *retlen, uint8_t *buf)
+{
+       struct mtd_oob_ops ops;
+       int res;
+
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooboffs = offs & (mtd->writesize - 1);
+       ops.ooblen = len;
+       ops.oobbuf = buf;
+       ops.datbuf = NULL;
+       ops.len = len;
+
+       res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+       *retlen = ops.retlen;
+       return res;
+}
+
+/*
+ * Write data and oob to flash
+ */
+static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
+                     size_t *retlen, uint8_t *buf, uint8_t *oob)
+{
+       struct mtd_oob_ops ops;
+       int res;
+
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooboffs = offs;
+       ops.ooblen = mtd->oobsize;
+       ops.oobbuf = oob;
+       ops.datbuf = buf;
+       ops.len = len;
+
+       res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+       *retlen = ops.retlen;
+       return res;
+}
+
 #ifdef CONFIG_NFTL_RW
 
 /* Actual NFTL access routines */
 
                targetEUN = thisEUN;
                for (block = 0; block < nftl->EraseSize / 512; block ++) {
-                       mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) +
+                       nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
                                      (block * 512), 16 , &retlen,
                                      (char *)&oob);
                        if (block == 2) {
                longer one */
                oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
                oob.u.c.unused = 0xffffffff;
-               mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
+               nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
                               8, &retlen, (char *)&oob.u);
        }
 
                memset(&oob, 0xff, sizeof(struct nftl_oob));
                oob.b.Status = oob.b.Status1 = SECTOR_USED;
 
-               nand_write_raw(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
-                              (block * 512), 512, &retlen, movebuf,
-                              (char *)&oob);
-
+               nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
+                          (block * 512), 512, &retlen, movebuf, (char *)&oob);
        }
 
        /* add the header so that it is now a valid chain */
        oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
 
-       mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
+       nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
                       8, &retlen, (char *)&oob.u);
 
        /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
 
                        lastEUN = writeEUN;
 
-                       mtd->read_oob(mtd,
+                       nftl_read_oob(mtd,
                                      (writeEUN * nftl->EraseSize) + blockofs,
                                      8, &retlen, (char *)&bci);
 
                nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
 
                /* ... and on the flash itself */
-               mtd->read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
+               nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
                              &retlen, (char *)&oob.u);
 
                oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 
-               mtd->write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
+               nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
                               &retlen, (char *)&oob.u);
 
                /* we link the new block to the chain only after the
                        /* Both in our cache... */
                        nftl->ReplUnitTable[lastEUN] = writeEUN;
                        /* ... and on the flash itself */
-                       mtd->read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
+                       nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
                                      8, &retlen, (char *)&oob.u);
 
                        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
                                = cpu_to_le16(writeEUN);
 
-                       mtd->write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
+                       nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
                                       8, &retlen, (char *)&oob.u);
                }
 
        memset(&oob, 0xff, sizeof(struct nftl_oob));
        oob.b.Status = oob.b.Status1 = SECTOR_USED;
 
-       nand_write_raw(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) +
-                      blockofs, 512, &retlen, (char *)buffer,
-                      (char *)&oob);
+       nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
+                  512, &retlen, (char *)buffer, (char *)&oob);
        return 0;
 }
 #endif /* CONFIG_NFTL_RW */
 
        if (thisEUN != BLOCK_NIL) {
                while (thisEUN < nftl->nb_blocks) {
-                       if (mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) +
+                       if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
                                          blockofs, 8, &retlen,
                                          (char *)&bci) < 0)
                                status = SECTOR_IGNORE;
 
 
 char nftlmountrev[]="$Revision: 1.41 $";
 
+extern int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+                        size_t *retlen, uint8_t *buf);
+extern int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+                         size_t *retlen, uint8_t *buf);
+
 /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
  *     various device information of the NFTL partition and Bad Unit Table. Update
  *     the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
                }
 
                /* To be safer with BIOS, also use erase mark as discriminant */
-               if ((ret = mtd->read_oob(mtd, block * nftl->EraseSize +
+               if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize +
                                         SECTORSIZE + 8, 8, &retlen,
                                         (char *)&h1) < 0)) {
                        printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
                        return -1;
 
                if (check_oob) {
-                       if(mtd->read_oob(mtd, address, mtd->oobsize,
+                       if(nftl_read_oob(mtd, address, mtd->oobsize,
                                         &retlen, &buf[SECTORSIZE]) < 0)
                                return -1;
                        if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
        struct mtd_info *mtd = nftl->mbd.mtd;
 
        /* Read the Unit Control Information #1 for Wear-Leveling */
-       if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+       if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
                          8, &retlen, (char *)&uci) < 0)
                goto default_uci1;
 
                        goto fail;
 
                uci.WearInfo = le32_to_cpu(nb_erases);
-               if (mtd->write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
+               if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
                                   8, 8, &retlen, (char *)&uci) < 0)
                        goto fail;
                return 0;
        block = first_block;
        for (;;) {
                for (i = 0; i < sectors_per_block; i++) {
-                       if (mtd->read_oob(mtd,
+                       if (nftl_read_oob(mtd,
                                          block * nftl->EraseSize + i * SECTORSIZE,
                                          8, &retlen, (char *)&bci) < 0)
                                status = SECTOR_IGNORE;
                                        /* sector not free actually : mark it as SECTOR_IGNORE  */
                                        bci.Status = SECTOR_IGNORE;
                                        bci.Status1 = SECTOR_IGNORE;
-                                       mtd->write_oob(mtd, block *
+                                       nftl_write_oob(mtd, block *
                                                       nftl->EraseSize +
                                                       i * SECTORSIZE, 8,
                                                       &retlen, (char *)&bci);
        size_t retlen;
 
        /* check erase mark. */
-       if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+       if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
                          &retlen, (char *)&h1) < 0)
                return -1;
 
                h1.EraseMark = cpu_to_le16(ERASE_MARK);
                h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
                h1.WearInfo = cpu_to_le32(0);
-               if (mtd->write_oob(mtd,
+               if (nftl_write_oob(mtd,
                                   block * nftl->EraseSize + SECTORSIZE + 8, 8,
                                   &retlen, (char *)&h1) < 0)
                        return -1;
                                                SECTORSIZE, 0) != 0)
                                return -1;
 
-                       if (mtd->read_oob(mtd, block * nftl->EraseSize + i,
+                       if (nftl_read_oob(mtd, block * nftl->EraseSize + i,
                                          16, &retlen, buf) < 0)
                                return -1;
                        if (i == SECTORSIZE) {
        struct nftl_uci2 uci;
        size_t retlen;
 
-       if (mtd->read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
+       if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
                          8, &retlen, (char *)&uci) < 0)
                return 0;
 
 
                        for (;;) {
                                /* read the block header. If error, we format the chain */
-                               if (mtd->read_oob(mtd,
+                               if (nftl_read_oob(mtd,
                                                  block * s->EraseSize + 8, 8,
                                                  &retlen, (char *)&h0) < 0 ||
-                                   mtd->read_oob(mtd,
+                                   nftl_read_oob(mtd,
                                                  block * s->EraseSize +
                                                  SECTORSIZE + 8, 8,
                                                  &retlen, (char *)&h1) < 0) {
 
 }
 
 /**
- * onenand_read_oob - [MTD Interface] OneNAND read out-of-band
+ * 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
  *
  * OneNAND read out-of-band data from the spare area
  */
-static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-       size_t *retlen, u_char *buf)
+int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+                       size_t *retlen, u_char *buf)
 {
        struct onenand_chip *this = mtd->priv;
        int read = 0, thislen, column;
        return ret;
 }
 
+/**
+ * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd:       MTD device structure
+ * @from:      offset to read from
+ * @ops:       oob operation description structure
+ */
+static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
+                           struct mtd_oob_ops *ops)
+{
+       BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+       return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->len,
+                                  &ops->retlen, ops->oobbuf);
+}
+
 #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
 /**
  * onenand_verify_oob - [GENERIC] verify the oob contents after a write
 }
 
 /**
- * onenand_write_oob - [MTD Interface] OneNAND write out-of-band
+ * 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
  *
  * OneNAND write out-of-band
  */
-static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
-       size_t *retlen, const u_char *buf)
+static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
+                               size_t *retlen, const u_char *buf)
 {
        struct onenand_chip *this = mtd->priv;
        int column, ret = 0;
        return ret;
 }
 
+/**
+ * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd:       MTD device structure
+ * @from:      offset to read from
+ * @ops:       oob operation description structure
+ */
+static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
+                            struct mtd_oob_ops *ops)
+{
+       BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+       return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->len,
+                                   &ops->retlen, ops->oobbuf);
+}
+
 /**
  * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
  * @param mtd          MTD device structure
 
         /* We write two bytes, so we dont have to mess with 16 bit access */
         ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
-        return mtd->write_oob(mtd, ofs , 2, &retlen, buf);
+        return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
 }
 
 /**
        this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
        this->wait(mtd, FL_OTPING);
 
-       ret = mtd->write_oob(mtd, from, len, retlen, buf);
+       ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
 
        /* Exit OTP access mode */
        this->command(mtd, ONENAND_CMD_RESET, 0, 0);
 
 #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);
+
 /**
  * check_short_pattern - [GENERIC] check if a pattern is in the buffer
  * @param buf          the buffer to search
 
                        /* No need to read pages fully,
                         * just read required OOB bytes */
-                       ret = mtd->read_oob(mtd, from + j * mtd->writesize + bd->offs,
-                                               readlen, &retlen, &buf[0]);
+                       ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
+                                                 readlen, &retlen, &buf[0]);
 
                        if (ret)
                                return ret;
 
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
        /* Write-behind buffer for NAND flash */
        unsigned char *wbuf;
+       unsigned char *oobbuf;
        uint32_t wbuf_ofs;
        uint32_t wbuf_len;
        struct jffs2_inodirty *wbuf_inodes;
 
        return ret;
 }
 
+#define NR_OOB_SCAN_PAGES      4
+
 /*
- *     Check, if the out of band area is empty
+ * Check, if the out of band area is empty
  */
-int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode)
+int jffs2_check_oob_empty(struct jffs2_sb_info *c,
+                         struct jffs2_eraseblock *jeb, int mode)
 {
-       unsigned char *buf;
-       int     ret = 0;
-       int     i,len,page;
-       size_t  retlen;
-       int     oob_size;
-
-       /* allocate a buffer for all oob data in this sector */
-       oob_size = c->mtd->oobsize;
-       len = 4 * oob_size;
-       buf = kmalloc(len, GFP_KERNEL);
-       if (!buf) {
-               printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n");
-               return -ENOMEM;
-       }
-       /*
-        * if mode = 0, we scan for a total empty oob area, else we have
-        * to take care of the cleanmarker in the first page of the block
-       */
-       ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf);
+       int i, page, ret;
+       int oobsize = c->mtd->oobsize;
+       struct mtd_oob_ops ops;
+
+       ops.len = NR_OOB_SCAN_PAGES * oobsize;
+       ops.ooblen = oobsize;
+       ops.oobbuf = c->oobbuf;
+       ops.ooboffs = 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));
-               goto out;
+               D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
+                         "failed %d for block at %08x\n", ret, jeb->offset));
+               return ret;
        }
 
-       if (retlen < len) {
-               D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read "
-                         "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
-               ret = -EIO;
-               goto out;
+       if (ops.retlen < ops.len) {
+               D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
+                         "returned short read (%zd bytes not %d) for block "
+                         "at %08x\n", ops.retlen, ops.len, jeb->offset));
+               return -EIO;
        }
 
        /* Special check for first page */
-       for(i = 0; i < oob_size ; i++) {
+       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)
                        continue;
 
-               if (buf[i] != 0xFF) {
-                       D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
-                                 buf[i], i, jeb->offset));
-                       ret = 1;
-                       goto out;
+               if (ops.oobbuf[i] != 0xFF) {
+                       D2(printk(KERN_DEBUG "Found %02x at %x in OOB for "
+                                 "%08x\n", ops.oobbuf[i], i, jeb->offset));
+                       return 1;
                }
        }
 
        /* we know, we are aligned :) */
-       for (page = oob_size; page < len; page += sizeof(long)) {
-               unsigned long dat = *(unsigned long *)(&buf[page]);
-               if(dat != -1) {
-                       ret = 1;
-                       goto out;
-               }
+       for (page = oobsize; page < ops.len; page += sizeof(long)) {
+               long dat = *(long *)(&ops.oobbuf[page]);
+               if(dat != -1)
+                       return 1;
        }
-
-out:
-       kfree(buf);
-
-       return ret;
+       return 0;
 }
 
 /*
-*      Scan for a valid cleanmarker and for bad blocks
-*      For virtual blocks (concatenated physical blocks) check the cleanmarker
-*      only in the first page of the first physical block, but scan for bad blocks in all
-*      physical blocks
-*/
-int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ * Scan for a valid cleanmarker and for bad blocks
+ */
+int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c,
+                                 struct jffs2_eraseblock *jeb)
 {
        struct jffs2_unknown_node n;
-       unsigned char buf[2 * NAND_MAX_OOBSIZE];
-       unsigned char *p;
-       int ret, i, cnt, retval = 0;
-       size_t retlen, offset;
-       int oob_size;
-
-       offset = jeb->offset;
-       oob_size = c->mtd->oobsize;
-
-       /* Loop through the physical blocks */
-       for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
-               /* 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;
-               }
-               /*
-                  *    We read oob data from page 0 and 1 of the block.
-                  *    page 0 contains cleanmarker and badblock info
-                  *    page 1 contains failure count of this block
-                */
-               ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf);
+       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;
+       }
 
-               if (ret) {
-                       D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
-                       return ret;
-               }
-               if (retlen < (oob_size << 1)) {
-                       D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset));
-                       return -EIO;
-               }
+       ops.len = oobsize;
+       ops.ooblen = oobsize;
+       ops.oobbuf = c->oobbuf;
+       ops.ooboffs = 0;
+       ops.datbuf = NULL;
+       ops.mode = MTD_OOB_PLACE;
 
-               /* Check cleanmarker only on the first physical block */
-               if (!cnt) {
-                       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;
+       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));
+               return ret;
+       }
 
-                       for (i = 0; i < c->fsdata_len; i++) {
-                               if (buf[c->fsdata_pos + i] != p[i]) {
-                                       retval = 1;
-                               }
-                       }
-                       D1(if (retval == 1) {
-                               printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
-                               printk(KERN_WARNING "OOB at %08zx was ", offset);
-                               for (i=0; i < oob_size; i++) {
-                                       printk("%02x ", buf[i]);
-                               }
-                               printk("\n");
-                       })
-               }
-               offset += c->mtd->erasesize;
+       if (ops.retlen < ops.len) {
+               D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): "
+                           "Read OOB return short read (%zd bytes not %d) "
+                           "for block at %08x\n", ops.retlen, ops.len,
+                           jeb->offset));
+               return -EIO;
        }
-       return retval;
+
+       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;
 }
 
-int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
+                                struct jffs2_eraseblock *jeb)
 {
-       struct  jffs2_unknown_node n;
-       int     ret;
-       size_t  retlen;
+       struct jffs2_unknown_node n;
+       int     ret;
+       struct mtd_oob_ops ops;
 
        n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
        n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
        n.totlen = cpu_to_je32(8);
 
-       ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n);
+       ops.len = c->fsdata_len;
+       ops.ooblen = c->fsdata_len;;
+       ops.oobbuf = (uint8_t *)&n;
+       ops.ooboffs = c->fsdata_pos;
+       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));
+               D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
+                         "Write failed for block at %08x: error %d\n",
+                         jeb->offset, ret));
                return ret;
        }
-       if (retlen != c->fsdata_len) {
-               D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
-               return ret;
+       if (ops.retlen != ops.len) {
+               D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
+                         "Short write for block at %08x: %zd not %d\n",
+                         jeb->offset, ops.retlen, ops.len));
+               return -EIO;
        }
        return 0;
 }
        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
 void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
 {
        kfree(c->wbuf);
+       kfree(c->oobbuf);
 }
 
 int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
 
        unsigned long failed;
 };
 
+/*
+ * oob operation modes
+ *
+ * MTD_OOB_PLACE:      oob data are placed at the given offset
+ * MTD_OOB_AUTO:       oob data are automatically placed at the free areas
+ *                     which are defined by the ecclayout
+ * MTD_OOB_RAW:                mode to read raw data+oob in one chunk. The oob data
+ *                     is inserted into the data. Thats a raw image of the
+ *                     flash contents.
+ */
+typedef enum {
+       MTD_OOB_PLACE,
+       MTD_OOB_AUTO,
+       MTD_OOB_RAW,
+} mtd_oob_mode_t;
+
+/**
+ * struct mtd_oob_ops - oob operation operands
+ * @mode:      operation mode
+ *
+ * @len:       number of bytes to write/read. When a data buffer is given
+ *             (datbuf != NULL) this is the number of data bytes. When
+ +             no data buffer is available this is the number of oob bytes.
+ *
+ * @retlen:    number of bytes written/read. When a data buffer is given
+ *             (datbuf != NULL) this is the number of data bytes. When
+ +             no data buffer is available this is the number of oob bytes.
+ *
+ * @ooblen:    number of oob bytes per page
+ * @ooboffs:   offset of oob data in the oob area (only relevant when
+ *             mode = MTD_OOB_PLACE)
+ * @datbuf:    data buffer - if NULL only oob data are read/written
+ * @oobbuf:    oob data buffer
+ */
+struct mtd_oob_ops {
+       mtd_oob_mode_t  mode;
+       size_t          len;
+       size_t          retlen;
+       size_t          ooblen;
+       uint32_t        ooboffs;
+       uint8_t         *datbuf;
+       uint8_t         *oobbuf;
+};
+
 struct mtd_info {
        u_char type;
        u_int32_t flags;
        int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
        int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
 
-       int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-       int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+       int (*read_oob) (struct mtd_info *mtd, loff_t from,
+                        struct mtd_oob_ops *ops);
+       int (*write_oob) (struct mtd_info *mtd, loff_t to,
+                        struct mtd_oob_ops *ops);
 
        /*
         * Methods to access the protection register area, present in some
 
 /* Free resources held by the NAND device */
 extern void nand_release (struct mtd_info *mtd);
 
-/* Read raw data from the device without ECC */
-extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from,
-                         size_t len, size_t ooblen);
-
-
-extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len,
-                         size_t *retlen, const uint8_t *buf, uint8_t *oob);
-
 /* The maximum number of NAND chips in an array */
 #define NAND_MAX_CHIPS         8
 
        struct nand_buffers buffers;
        struct nand_hw_control hwcontrol;
 
+       struct mtd_oob_ops ops;
+
        uint8_t         *bbt;
        struct nand_bbt_descr   *bbt_td;
        struct nand_bbt_descr   *bbt_md;