From e8d32937d9f2022c31871ef357a4883f78da1b7f Mon Sep 17 00:00:00 2001 From: Alexander Belyakov Date: Wed, 17 May 2006 19:11:16 +0400 Subject: MTD: mtdconcat NAND/Sibley support (rev.2) There is a second revision of "mtdconcat NAND/Sibley" patch. I hope the patch will not get damaged as I'm posting it from gmail account, thanks to Jorn. The patch adds previously missing concat_writev(), concat_writev_ecc(), concat_block_isbad(), concat_block_markbad() functions to make concatenation layer compatible with Sibley and NAND chips. Patch has been cleared from whitespaces, fixed some lines of code as requested. Also I have added code for alignment check that should support Jorn's "writesize" patch. Signed-off-by: Alexander Belyakov Signed-off-by: David Woodhouse --- drivers/mtd/mtdconcat.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 9af840364a74..da4c9c190bb3 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -250,6 +250,106 @@ concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, return err; } +static int +concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen, + 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; + + *retlen = 0; + + /* Calculate total length of data */ + for (i = 0; i < count; i++) + total_len += vecs[i].iov_len; + + /* Do not allow write past end of device */ + if ((to + total_len) > mtd->size) + return -EINVAL; + + /* Check alignment */ + if (mtd->writesize > 1) + if ((to % mtd->writesize) || (total_len % mtd->writesize)) + return -EINVAL; + + /* 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); + + 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; + + if (to >= subdev->size) { + 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 (!(subdev->flags & MTD_WRITEABLE)) + err = -EROFS; + else if (eccbuf) + err = subdev->writev_ecc(subdev, &vecs_copy[entry_low], + entry_high - entry_low + 1, to, &retsize, + 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; + + if (err) + break; + + *retlen += retsize; + total_len -= wsize; + if (concat->mtd.type == MTD_NANDFLASH && eccbuf) + eccbuf += mtd->oobavail * (wsize / mtd->oobblock); + + if (total_len == 0) + break; + + err = -EINVAL; + to = 0; + } + + kfree(vecs_copy); + return err; +} + +static int +concat_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen) +{ + return concat_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL); +} + static int concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) @@ -636,6 +736,58 @@ static void concat_resume(struct mtd_info *mtd) } } +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); + break; + } + + return err; +} + /* * This function constructs a virtual MTD device by concatenating * num_devs MTD devices. A pointer to the new device object is @@ -685,10 +837,18 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.read_ecc = concat_read_ecc; if (subdev[0]->write_ecc) concat->mtd.write_ecc = concat_write_ecc; + if (subdev[0]->writev) + concat->mtd.writev = concat_writev; + if (subdev[0]->writev_ecc) + concat->mtd.writev_ecc = concat_writev_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->subdev[0] = subdev[0]; @@ -734,14 +894,13 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c } + if(concat->mtd.type == MTD_NANDFLASH) + memcpy(&concat->mtd.oobinfo, &subdev[0]->oobinfo, + sizeof(struct nand_oobinfo)); + 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; -- cgit v1.2.3