aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtdconcat.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/mtdconcat.c')
-rw-r--r--drivers/mtd/mtdconcat.c169
1 files changed, 164 insertions, 5 deletions
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 9af840364a74..b7de90845c2d 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -251,6 +251,106 @@ concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
251} 251}
252 252
253static int 253static int
254concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
255 unsigned long count, loff_t to, size_t * retlen,
256 u_char *eccbuf, struct nand_oobinfo *oobsel)
257{
258 struct mtd_concat *concat = CONCAT(mtd);
259 struct kvec *vecs_copy;
260 unsigned long entry_low, entry_high;
261 size_t total_len = 0;
262 int i;
263 int err = -EINVAL;
264
265 if (!(mtd->flags & MTD_WRITEABLE))
266 return -EROFS;
267
268 *retlen = 0;
269
270 /* Calculate total length of data */
271 for (i = 0; i < count; i++)
272 total_len += vecs[i].iov_len;
273
274 /* Do not allow write past end of device */
275 if ((to + total_len) > mtd->size)
276 return -EINVAL;
277
278 /* Check alignment */
279 if (mtd->oobblock > 1)
280 if ((to % mtd->oobblock) || (total_len % mtd->oobblock))
281 return -EINVAL;
282
283 /* make a copy of vecs */
284 vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL);
285 if (!vecs_copy)
286 return -ENOMEM;
287 memcpy(vecs_copy, vecs, sizeof(struct kvec) * count);
288
289 entry_low = 0;
290 for (i = 0; i < concat->num_subdev; i++) {
291 struct mtd_info *subdev = concat->subdev[i];
292 size_t size, wsize, retsize, old_iov_len;
293
294 if (to >= subdev->size) {
295 to -= subdev->size;
296 continue;
297 }
298
299 size = min(total_len, (size_t)(subdev->size - to));
300 wsize = size; /* store for future use */
301
302 entry_high = entry_low;
303 while (entry_high < count) {
304 if (size <= vecs_copy[entry_high].iov_len)
305 break;
306 size -= vecs_copy[entry_high++].iov_len;
307 }
308
309 old_iov_len = vecs_copy[entry_high].iov_len;
310 vecs_copy[entry_high].iov_len = size;
311
312 if (!(subdev->flags & MTD_WRITEABLE))
313 err = -EROFS;
314 else if (eccbuf)
315 err = subdev->writev_ecc(subdev, &vecs_copy[entry_low],
316 entry_high - entry_low + 1, to, &retsize,
317 eccbuf, oobsel);
318 else
319 err = subdev->writev(subdev, &vecs_copy[entry_low],
320 entry_high - entry_low + 1, to, &retsize);
321
322 vecs_copy[entry_high].iov_len = old_iov_len - size;
323 vecs_copy[entry_high].iov_base += size;
324
325 entry_low = entry_high;
326
327 if (err)
328 break;
329
330 *retlen += retsize;
331 total_len -= wsize;
332 if (concat->mtd.type == MTD_NANDFLASH && eccbuf)
333 eccbuf += mtd->oobavail * (wsize / mtd->oobblock);
334
335 if (total_len == 0)
336 break;
337
338 err = -EINVAL;
339 to = 0;
340 }
341
342 kfree(vecs_copy);
343 return err;
344}
345
346static int
347concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
348 unsigned long count, loff_t to, size_t * retlen)
349{
350 return concat_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL);
351}
352
353static int
254concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, 354concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
255 size_t * retlen, u_char * buf) 355 size_t * retlen, u_char * buf)
256{ 356{
@@ -636,6 +736,58 @@ static void concat_resume(struct mtd_info *mtd)
636 } 736 }
637} 737}
638 738
739static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
740{
741 struct mtd_concat *concat = CONCAT(mtd);
742 int i, res = 0;
743
744 if (!concat->subdev[0]->block_isbad)
745 return res;
746
747 if (ofs > mtd->size)
748 return -EINVAL;
749
750 for (i = 0; i < concat->num_subdev; i++) {
751 struct mtd_info *subdev = concat->subdev[i];
752
753 if (ofs >= subdev->size) {
754 ofs -= subdev->size;
755 continue;
756 }
757
758 res = subdev->block_isbad(subdev, ofs);
759 break;
760 }
761
762 return res;
763}
764
765static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
766{
767 struct mtd_concat *concat = CONCAT(mtd);
768 int i, err = -EINVAL;
769
770 if (!concat->subdev[0]->block_markbad)
771 return 0;
772
773 if (ofs > mtd->size)
774 return -EINVAL;
775
776 for (i = 0; i < concat->num_subdev; i++) {
777 struct mtd_info *subdev = concat->subdev[i];
778
779 if (ofs >= subdev->size) {
780 ofs -= subdev->size;
781 continue;
782 }
783
784 err = subdev->block_markbad(subdev, ofs);
785 break;
786 }
787
788 return err;
789}
790
639/* 791/*
640 * This function constructs a virtual MTD device by concatenating 792 * This function constructs a virtual MTD device by concatenating
641 * num_devs MTD devices. A pointer to the new device object is 793 * 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
685 concat->mtd.read_ecc = concat_read_ecc; 837 concat->mtd.read_ecc = concat_read_ecc;
686 if (subdev[0]->write_ecc) 838 if (subdev[0]->write_ecc)
687 concat->mtd.write_ecc = concat_write_ecc; 839 concat->mtd.write_ecc = concat_write_ecc;
840 if (subdev[0]->writev)
841 concat->mtd.writev = concat_writev;
842 if (subdev[0]->writev_ecc)
843 concat->mtd.writev_ecc = concat_writev_ecc;
688 if (subdev[0]->read_oob) 844 if (subdev[0]->read_oob)
689 concat->mtd.read_oob = concat_read_oob; 845 concat->mtd.read_oob = concat_read_oob;
690 if (subdev[0]->write_oob) 846 if (subdev[0]->write_oob)
691 concat->mtd.write_oob = concat_write_oob; 847 concat->mtd.write_oob = concat_write_oob;
848 if (subdev[0]->block_isbad)
849 concat->mtd.block_isbad = concat_block_isbad;
850 if (subdev[0]->block_markbad)
851 concat->mtd.block_markbad = concat_block_markbad;
692 852
693 concat->subdev[0] = subdev[0]; 853 concat->subdev[0] = subdev[0];
694 854
@@ -734,14 +894,13 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
734 894
735 } 895 }
736 896
897 if(concat->mtd.type == MTD_NANDFLASH)
898 memcpy(&concat->mtd.oobinfo, &subdev[0]->oobinfo,
899 sizeof(struct nand_oobinfo));
900
737 concat->num_subdev = num_devs; 901 concat->num_subdev = num_devs;
738 concat->mtd.name = name; 902 concat->mtd.name = name;
739 903
740 /*
741 * NOTE: for now, we do not provide any readv()/writev() methods
742 * because they are messy to implement and they are not
743 * used to a great extent anyway.
744 */
745 concat->mtd.erase = concat_erase; 904 concat->mtd.erase = concat_erase;
746 concat->mtd.read = concat_read; 905 concat->mtd.read = concat_read;
747 concat->mtd.write = concat_write; 906 concat->mtd.write = concat_write;