?? mtdconcat.c
字號:
/* * MTD device concatenation layer * * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> * * NAND support by Christian Gan <cgan@iders.ca> * * This code is GPL * * $Id: mtdconcat.c,v 1.2 2007/09/21 03:09:24 quy Exp $ */#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/mtd/mtd.h>#include <linux/mtd/concat.h>/* * Our storage structure: * Subdev points to an array of pointers to struct mtd_info objects * which is allocated along with this structure * */struct mtd_concat { struct mtd_info mtd; int num_subdev; struct mtd_info **subdev;};/* * how to calculate the size required for the above structure, * including the pointer array subdev points to: */#define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))/* * Given a pointer to the MTD object in the mtd_concat structure, * we can retrieve the pointer to that structure with this macro. */#define CONCAT(x) ((struct mtd_concat *)(x))/* * MTD methods which look up the relevant subdevice, translate the * effective address and pass through to the subdevice. */static intconcat_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){ struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; int i; *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; err = subdev->read(subdev, from, size, &retsize, buf); if (err) break; *retlen += retsize; len -= size; if (len == 0) break; err = -EINVAL; buf += size; from = 0; } return err;}static intconcat_write(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf){ struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; int i; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; *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 err = subdev->write(subdev, to, size, &retsize, buf); if (err) break; *retlen += retsize; len -= size; if (len == 0) break; err = -EINVAL; buf += size; to = 0; } return err;}static intconcat_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); int err = -EINVAL; int i; *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_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;}static intconcat_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; 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_ecc) err = subdev->write_ecc(subdev, to, 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; to = 0; } return err;}static intconcat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){ struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; int i; *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; if (err) break; *retlen += retsize; len -= size; if (len == 0) break; err = -EINVAL; buf += size; from = 0; } return err;}static intconcat_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); int err = -EINVAL; int i; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; *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; if (err) break; *retlen += retsize; len -= size; if (len == 0) break; err = -EINVAL; buf += size; to = 0; } return err;}static void concat_erase_callback(struct erase_info *instr){ wake_up((wait_queue_head_t *) instr->priv);}static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase){ int err; wait_queue_head_t waitq; DECLARE_WAITQUEUE(wait, current); /* * This code was stol^H^H^H^Hinspired by mtdchar.c */ init_waitqueue_head(&waitq); erase->mtd = mtd; erase->callback = concat_erase_callback; erase->priv = (unsigned long) &waitq; /* * FIXME: Allow INTERRUPTIBLE. Which means * not having the wait_queue head on the stack. */ err = mtd->erase(mtd, erase); if (!err) { set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&waitq, &wait); if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) schedule(); remove_wait_queue(&waitq, &wait); set_current_state(TASK_RUNNING); err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; } return err;}static int concat_erase(struct mtd_info *mtd, struct erase_info *instr){ struct mtd_concat *concat = CONCAT(mtd); struct mtd_info *subdev; int i, err; u_int32_t length, offset = 0; struct erase_info *erase; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; if (instr->addr > concat->mtd.size) return -EINVAL; if (instr->len + instr->addr > concat->mtd.size) return -EINVAL; /* * Check for proper erase block alignment of the to-be-erased area. * It is easier to do this based on the super device's erase * region info rather than looking at each particular sub-device * in turn. */ if (!concat->mtd.numeraseregions) { /* the easy case: device has uniform erase block size */ if (instr->addr & (concat->mtd.erasesize - 1)) return -EINVAL; if (instr->len & (concat->mtd.erasesize - 1)) return -EINVAL; } else { /* device has variable erase size */ struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; /* * Find the erase region where the to-be-erased area begins: */ for (i = 0; i < concat->mtd.numeraseregions && instr->addr >= erase_regions[i].offset; i++) ; --i; /* * Now erase_regions[i] is the region in which the * to-be-erased area begins. Verify that the starting * offset is aligned to this region's erase size: */ if (instr->addr & (erase_regions[i].erasesize - 1)) return -EINVAL; /* * now find the erase region where the to-be-erased area ends: */ for (; i < concat->mtd.numeraseregions && (instr->addr + instr->len) >= erase_regions[i].offset; ++i) ; --i; /* * check if the ending offset is aligned to this region's erase size */ if ((instr->addr + instr->len) & (erase_regions[i].erasesize - 1))
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -