#include <linux/mtd/mtd.h>
#include <linux/mtd/concat.h>
-#include <asm/div64.h>
-
/*
* Our storage structure:
* Subdev points to an array of pointers to struct mtd_info objects
size_t * retlen, u_char * buf)
{
struct mtd_concat *concat = CONCAT(mtd);
- int ret = 0, err;
+ int err = -EINVAL;
int i;
*retlen = 0;
err = subdev->read(subdev, from, size, &retsize, buf);
- /* Save information about bitflips! */
- if (unlikely(err)) {
- if (err == -EBADMSG) {
- mtd->ecc_stats.failed++;
- ret = err;
- } else if (err == -EUCLEAN) {
- mtd->ecc_stats.corrected++;
- /* Do not overwrite -EBADMSG !! */
- if (!ret)
- ret = err;
- } else
- return err;
- }
+ if (err)
+ break;
*retlen += retsize;
len -= size;
if (len == 0)
- return ret;
+ break;
+ err = -EINVAL;
buf += size;
from = 0;
}
- return -EINVAL;
+ return err;
}
static int
}
static int
-concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
- unsigned long count, loff_t to, size_t * retlen)
+concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf, u_char * eccbuf,
+ struct nand_oobinfo *oobsel)
{
struct mtd_concat *concat = CONCAT(mtd);
- struct kvec *vecs_copy;
- unsigned long entry_low, entry_high;
- size_t total_len = 0;
- int i;
int err = -EINVAL;
-
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
+ int i;
*retlen = 0;
- /* Calculate total length of data */
- for (i = 0; i < count; i++)
- total_len += vecs[i].iov_len;
+ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size, retsize;
- /* Do not allow write past end of device */
- if ((to + total_len) > mtd->size)
- return -EINVAL;
+ if (from >= subdev->size) {
+ /* Not destined for this subdev */
+ size = 0;
+ from -= subdev->size;
+ continue;
+ }
- /* Check alignment */
- if (mtd->writesize > 1) {
- loff_t __to = to;
- if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
- return -EINVAL;
+ 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_ecc)
+ err = subdev->read_ecc(subdev, from, size,
+ &retsize, buf, eccbuf, oobsel);
+ else
+ err = -EINVAL;
+
+ if (err)
+ break;
+
+ *retlen += retsize;
+ len -= size;
+ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ buf += size;
+ if (eccbuf) {
+ eccbuf += subdev->oobsize;
+ /* in nand.c at least, eccbufs are
+ tagged with 2 (int)eccstatus'; we
+ must account for these */
+ eccbuf += 2 * (sizeof (int));
+ }
+ from = 0;
}
+ return err;
+}
- /* make a copy of vecs */
- vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL);
- if (!vecs_copy)
- return -ENOMEM;
- memcpy(vecs_copy, vecs, sizeof(struct kvec) * count);
+static int
+concat_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)
+{
+ struct mtd_concat *concat = CONCAT(mtd);
+ int err = -EINVAL;
+ int i;
+
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+
+ *retlen = 0;
- entry_low = 0;
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
- size_t size, wsize, retsize, old_iov_len;
+ size_t size, retsize;
if (to >= subdev->size) {
+ size = 0;
to -= subdev->size;
continue;
}
-
- size = min(total_len, (size_t)(subdev->size - to));
- wsize = size; /* store for future use */
-
- entry_high = entry_low;
- while (entry_high < count) {
- if (size <= vecs_copy[entry_high].iov_len)
- break;
- size -= vecs_copy[entry_high++].iov_len;
- }
-
- old_iov_len = vecs_copy[entry_high].iov_len;
- vecs_copy[entry_high].iov_len = size;
+ if (to + len > subdev->size)
+ size = subdev->size - to;
+ else
+ size = len;
if (!(subdev->flags & MTD_WRITEABLE))
err = -EROFS;
+ else if (subdev->write_ecc)
+ err = subdev->write_ecc(subdev, to, size,
+ &retsize, buf, eccbuf, oobsel);
else
- err = subdev->writev(subdev, &vecs_copy[entry_low],
- entry_high - entry_low + 1, to, &retsize);
-
- vecs_copy[entry_high].iov_len = old_iov_len - size;
- vecs_copy[entry_high].iov_base += size;
-
- entry_low = entry_high;
+ err = -EINVAL;
if (err)
break;
*retlen += retsize;
- total_len -= wsize;
-
- if (total_len == 0)
+ len -= size;
+ if (len == 0)
break;
err = -EINVAL;
+ buf += size;
+ if (eccbuf)
+ eccbuf += subdev->oobsize;
to = 0;
}
-
- kfree(vecs_copy);
return err;
}
static int
-concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
+concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf)
{
struct mtd_concat *concat = CONCAT(mtd);
- struct mtd_oob_ops devops = *ops;
- int i, err, ret = 0;
+ int err = -EINVAL;
+ int i;
- ops->retlen = 0;
+ *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;
- /* partial read ? */
- if (from + devops.len > subdev->size)
- devops.len = subdev->size - from;
-
- err = subdev->read_oob(subdev, from, &devops);
- ops->retlen += devops.retlen;
-
- /* Save information about bitflips! */
- if (unlikely(err)) {
- if (err == -EBADMSG) {
- mtd->ecc_stats.failed++;
- ret = err;
- } else if (err == -EUCLEAN) {
- mtd->ecc_stats.corrected++;
- /* Do not overwrite -EBADMSG !! */
- if (!ret)
- ret = err;
- } else
- return err;
- }
+ if (subdev->read_oob)
+ err = subdev->read_oob(subdev, from, size,
+ &retsize, buf);
+ else
+ err = -EINVAL;
- devops.len = ops->len - ops->retlen;
- if (!devops.len)
- return ret;
+ if (err)
+ break;
- if (devops.datbuf)
- devops.datbuf += devops.retlen;
- if (devops.oobbuf)
- devops.oobbuf += devops.ooblen;
+ *retlen += retsize;
+ len -= size;
+ if (len == 0)
+ break;
+ err = -EINVAL;
+ buf += size;
from = 0;
}
- return -EINVAL;
+ return err;
}
static int
-concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
+concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t * retlen, const u_char * buf)
{
struct mtd_concat *concat = CONCAT(mtd);
- struct mtd_oob_ops devops = *ops;
- int i, err;
+ int err = -EINVAL;
+ int i;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
- ops->retlen = 0;
+ *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;
- /* partial write ? */
- if (to + devops.len > subdev->size)
- devops.len = subdev->size - to;
+ if (!(subdev->flags & MTD_WRITEABLE))
+ err = -EROFS;
+ else if (subdev->write_oob)
+ err = subdev->write_oob(subdev, to, size, &retsize,
+ buf);
+ else
+ err = -EINVAL;
- err = subdev->write_oob(subdev, to, &devops);
- ops->retlen += devops.retlen;
if (err)
- return err;
+ break;
- devops.len = ops->len - ops->retlen;
- if (!devops.len)
- return 0;
+ *retlen += retsize;
+ len -= size;
+ if (len == 0)
+ break;
- if (devops.datbuf)
- devops.datbuf += devops.retlen;
- if (devops.oobbuf)
- devops.oobbuf += devops.ooblen;
+ err = -EINVAL;
+ buf += size;
to = 0;
}
- return -EINVAL;
+ return err;
}
static void concat_erase_callback(struct erase_info *instr)
}
/* must never happen since size limit has been verified above */
- BUG_ON(i >= concat->num_subdev);
+ if (i >= concat->num_subdev)
+ BUG();
/* now do the erase: */
err = 0;
if ((err = concat_dev_erase(subdev, erase))) {
/* sanity check: should never happen since
* block alignment has been checked above */
- BUG_ON(err == -EINVAL);
+ if (err == -EINVAL)
+ BUG();
if (erase->fail_addr != 0xffffffff)
instr->fail_addr = erase->fail_addr + offset;
break;
}
}
-static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct mtd_concat *concat = CONCAT(mtd);
- int i, res = 0;
-
- if (!concat->subdev[0]->block_isbad)
- return res;
-
- if (ofs > mtd->size)
- return -EINVAL;
-
- for (i = 0; i < concat->num_subdev; i++) {
- struct mtd_info *subdev = concat->subdev[i];
-
- if (ofs >= subdev->size) {
- ofs -= subdev->size;
- continue;
- }
-
- res = subdev->block_isbad(subdev, ofs);
- break;
- }
-
- return res;
-}
-
-static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct mtd_concat *concat = CONCAT(mtd);
- int i, err = -EINVAL;
-
- if (!concat->subdev[0]->block_markbad)
- return 0;
-
- if (ofs > mtd->size)
- return -EINVAL;
-
- for (i = 0; i < concat->num_subdev; i++) {
- struct mtd_info *subdev = concat->subdev[i];
-
- if (ofs >= subdev->size) {
- ofs -= subdev->size;
- continue;
- }
-
- err = subdev->block_markbad(subdev, ofs);
- if (!err)
- mtd->ecc_stats.badblocks++;
- break;
- }
-
- return err;
-}
-
/*
* This function constructs a virtual MTD device by concatenating
* num_devs MTD devices. A pointer to the new device object is
concat->mtd.flags = subdev[0]->flags;
concat->mtd.size = subdev[0]->size;
concat->mtd.erasesize = subdev[0]->erasesize;
- concat->mtd.writesize = subdev[0]->writesize;
+ concat->mtd.oobblock = subdev[0]->oobblock;
concat->mtd.oobsize = subdev[0]->oobsize;
concat->mtd.ecctype = subdev[0]->ecctype;
concat->mtd.eccsize = subdev[0]->eccsize;
- if (subdev[0]->writev)
- concat->mtd.writev = concat_writev;
+ if (subdev[0]->read_ecc)
+ concat->mtd.read_ecc = concat_read_ecc;
+ if (subdev[0]->write_ecc)
+ concat->mtd.write_ecc = concat_write_ecc;
if (subdev[0]->read_oob)
concat->mtd.read_oob = concat_read_oob;
if (subdev[0]->write_oob)
concat->mtd.write_oob = concat_write_oob;
- if (subdev[0]->block_isbad)
- concat->mtd.block_isbad = concat_block_isbad;
- if (subdev[0]->block_markbad)
- concat->mtd.block_markbad = concat_block_markbad;
-
- concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
concat->subdev[0] = subdev[0];
subdev[i]->flags & MTD_WRITEABLE;
}
concat->mtd.size += subdev[i]->size;
- concat->mtd.ecc_stats.badblocks +=
- subdev[i]->ecc_stats.badblocks;
- if (concat->mtd.writesize != subdev[i]->writesize ||
+ if (concat->mtd.oobblock != subdev[i]->oobblock ||
concat->mtd.oobsize != subdev[i]->oobsize ||
concat->mtd.ecctype != subdev[i]->ecctype ||
concat->mtd.eccsize != subdev[i]->eccsize ||
+ !concat->mtd.read_ecc != !subdev[i]->read_ecc ||
+ !concat->mtd.write_ecc != !subdev[i]->write_ecc ||
!concat->mtd.read_oob != !subdev[i]->read_oob ||
!concat->mtd.write_oob != !subdev[i]->write_oob) {
kfree(concat);
}
- concat->mtd.ecclayout = subdev[0]->ecclayout;
-
concat->num_subdev = num_devs;
concat->mtd.name = name;
+ /*
+ * NOTE: for now, we do not provide any readv()/writev() methods
+ * because they are messy to implement and they are not
+ * used to a great extent anyway.
+ */
concat->mtd.erase = concat_erase;
concat->mtd.read = concat_read;
concat->mtd.write = concat_write;