staticint
concat_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf)
{ struct mtd_concat *concat = CONCAT(mtd); int ret = 0, err; int i;
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;
/* Save information about bitflips! */ if (unlikely(err)) { if (mtd_is_eccerr(err)) {
mtd->ecc_stats.failed++;
ret = err;
} elseif (mtd_is_bitflip(err)) {
mtd->ecc_stats.corrected++; /* Do not overwrite -EBADMSG !! */ if (!ret)
ret = err;
} else return err;
}
*retlen += retsize;
len -= size; if (len == 0) return ret;
buf += size;
from = 0;
} return -EINVAL;
}
staticint
concat_panic_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; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize;
if (to >= subdev->size) {
to -= subdev->size; continue;
} if (to + len > subdev->size)
size = subdev->size - to; else
size = len;
err = mtd_panic_write(subdev, to, size, &retsize, buf); if (err == -EOPNOTSUPP) {
printk(KERN_ERR "mtdconcat: Cannot write from panic without panic_write\n"); return err;
} if (err) break;
*retlen += retsize;
len -= size; if (len == 0) break;
/* Save information about bitflips! */ if (unlikely(err)) { if (mtd_is_eccerr(err)) {
mtd->ecc_stats.failed++;
ret = err;
} elseif (mtd_is_bitflip(err)) {
mtd->ecc_stats.corrected++; /* Do not overwrite -EBADMSG !! */ if (!ret)
ret = err;
} else return err;
}
if (devops.datbuf) {
devops.len = ops->len - ops->retlen; if (!devops.len) return ret;
devops.datbuf += devops.retlen;
} if (devops.oobbuf) {
devops.ooblen = ops->ooblen - ops->oobretlen; if (!devops.ooblen) return ret;
devops.oobbuf += ops->oobretlen;
}
from = 0;
} return -EINVAL;
}
staticint
concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
{ struct mtd_concat *concat = CONCAT(mtd); struct mtd_oob_ops devops = *ops; int i, err;
if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS;
ops->retlen = ops->oobretlen = 0;
for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i];
if (to >= subdev->size) {
to -= subdev->size; continue;
}
/* *Checkforpropereraseblockalignmentoftheto-be-erasedarea. *Itiseasiertodothisbasedonthesuperdevice'serase *regioninforatherthanlookingateachparticularsub-device *inturn.
*/ 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;
/* *Findtheeraseregionwheretheto-be-erasedareabegins:
*/ for (i = 0; i < concat->mtd.numeraseregions &&
instr->addr >= erase_regions[i].offset; i++) ;
--i;
/* *Nowerase_regions[i]istheregioninwhichthe *to-be-erasedareabegins.Verifythatthestarting *offsetisalignedtothisregion'serasesize:
*/ if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1)) return -EINVAL;
/* *nowfindtheeraseregionwheretheto-be-erasedareaends:
*/ for (; i < concat->mtd.numeraseregions &&
(instr->addr + instr->len) >= erase_regions[i].offset;
++i) ;
--i; /* *checkiftheendingoffsetisalignedtothisregion'serasesize
*/ if (i < 0 || ((instr->addr + instr->len) &
(erase_regions[i].erasesize - 1))) return -EINVAL;
}
/* make a local copy of instr to avoid modifying the caller's struct */
erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
if (!erase) return -ENOMEM;
*erase = *instr;
length = instr->len;
/* *findthesubdevicewheretheto-be-erasedareabegins,adjust *startingoffsettoberelativetothesubdevicestart
*/ for (i = 0; i < concat->num_subdev; i++) {
subdev = concat->subdev[i]; if (subdev->size <= erase->addr) {
erase->addr -= subdev->size;
offset += subdev->size;
} else { break;
}
}
/* must never happen since size limit has been verified above */
BUG_ON(i >= concat->num_subdev);
/* now do the erase: */
err = 0; for (; length > 0; i++) { /* loop for all subdevices affected by this request */
subdev = concat->subdev[i]; /* get current subdevice */
for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i];
mtd_resume(subdev);
}
}
staticint concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
{ struct mtd_concat *concat = CONCAT(mtd); int i, res = 0;
if (!mtd_can_have_bb(concat->subdev[0])) return res;
for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i];
if (ofs >= subdev->size) {
ofs -= subdev->size; continue;
}
res = mtd_block_isbad(subdev, ofs); break;
}
return res;
}
staticint concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
{ struct mtd_concat *concat = CONCAT(mtd); int i, err = -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 = mtd_block_markbad(subdev, ofs); if (!err)
mtd->ecc_stats.badblocks++; break;
}
return err;
}
/* *ThisfunctionconstructsavirtualMTDdevicebyconcatenating *num_devsMTDdevices.Apointertothenewdeviceobjectis *storedto*new_devuponsuccess.Thisfunctiondoes_not_ *registeranydevices:thisisthecaller'sresponsibility.
*/ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */ constchar *name)
{ /* name for the new device */ int i;
size_t size; struct mtd_concat *concat; struct mtd_info *subdev_master = NULL;
uint32_t max_erasesize, curr_erasesize; int num_erase_region; int max_writebufsize = 0;
printk(KERN_NOTICE "Concatenating MTD devices:\n"); for (i = 0; i < num_devs; i++)
printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
printk(KERN_NOTICE "into device \"%s\"\n", name);
for (i = 0; i < num_devs; i++) if (max_writebufsize < subdev[i]->writebufsize)
max_writebufsize = subdev[i]->writebufsize;
concat->mtd.writebufsize = max_writebufsize;
/* *Combinetheeraseblocksizeinfoofthesubdevices: * *first,walkthemapofthenewdeviceandseehow *manychangesinerasesizewehave
*/
max_erasesize = curr_erasesize = subdev[0]->erasesize;
num_erase_region = 1; for (i = 0; i < num_devs; i++) { if (subdev[i]->numeraseregions == 0) { /* current subdevice has uniform erase size */ if (subdev[i]->erasesize != curr_erasesize) { /* if it differs from the last subdevice's erase size, count it */
++num_erase_region;
curr_erasesize = subdev[i]->erasesize; if (curr_erasesize > max_erasesize)
max_erasesize = curr_erasesize;
}
} else { /* current subdevice has variable erase size */ int j; for (j = 0; j < subdev[i]->numeraseregions; j++) {
/* walk the list of erase regions, count any changes */ if (subdev[i]->eraseregions[j].erasesize !=
curr_erasesize) {
++num_erase_region;
curr_erasesize =
subdev[i]->eraseregions[j].
erasesize; if (curr_erasesize > max_erasesize)
max_erasesize = curr_erasesize;
}
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.