diff options
Diffstat (limited to 'drivers/mtd/mtdconcat.c')
-rw-r--r-- | drivers/mtd/mtdconcat.c | 169 |
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 | ||
253 | static int | 253 | static int |
254 | concat_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 | |||
346 | static int | ||
347 | concat_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 | |||
353 | static int | ||
254 | concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 354 | concat_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 | ||
739 | static 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 | |||
765 | static 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; |