diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-02-04 09:52:30 +0100 |
---|---|---|
committer | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-04-19 22:05:47 +0200 |
commit | 75eb2cec251fda33c9bb716ecc372819abb9278a (patch) | |
tree | 6adaf011cf01e43273cf1b70f82ed12812182fc5 /drivers/mtd/mtdcore.c | |
parent | mtd: nand: sunxi: make cur_off parameter optional in extra oob helpers (diff) | |
download | linux-75eb2cec251fda33c9bb716ecc372819abb9278a.tar.xz linux-75eb2cec251fda33c9bb716ecc372819abb9278a.zip |
mtd: add mtd_ooblayout_xxx() helper functions
In order to make the ecclayout definition completely dynamic we need to
rework the way the OOB layout are defined and iterated.
Create a few mtd_ooblayout_xxx() helpers to ease OOB bytes manipulation
and hide ecclayout internals to their users.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Diffstat (limited to 'drivers/mtd/mtdcore.c')
-rw-r--r-- | drivers/mtd/mtdcore.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index bee180bd11e7..0290c41e44fc 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1016,6 +1016,406 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, } EXPORT_SYMBOL_GPL(mtd_write_oob); +/** + * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section + * @mtd: MTD device structure + * @section: ECC section. Depending on the layout you may have all the ECC + * bytes stored in a single contiguous section, or one section + * per ECC chunk (and sometime several sections for a single ECC + * ECC chunk) + * @oobecc: OOB region struct filled with the appropriate ECC position + * information + * + * This functions return ECC section information in the OOB area. I you want + * to get all the ECC bytes information, then you should call + * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc) +{ + int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; + + memset(oobecc, 0, sizeof(*oobecc)); + + if (!mtd || section < 0) + return -EINVAL; + + if (!mtd->ecclayout) + return -ENOTSUPP; + + /* + * This logic allows us to reuse the ->ecclayout information and + * expose them as ECC regions (as done for the OOB free regions). + * + * TODO: this should be dropped as soon as we get rid of the + * ->ecclayout field. + */ + for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { + eccpos = mtd->ecclayout->eccpos[eccbyte]; + + if (eccbyte < mtd->ecclayout->eccbytes - 1) { + int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; + + if (eccpos + 1 == neccpos) { + length++; + continue; + } + } + + if (section == cursection) + break; + + length = 0; + cursection++; + } + + if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) + return -ERANGE; + + oobecc->length = length + 1; + oobecc->offset = eccpos - length; + + return 0; +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); + +/** + * mtd_ooblayout_free - Get the OOB region definition of a specific free + * section + * @mtd: MTD device structure + * @section: Free section you are interested in. Depending on the layout + * you may have all the free bytes stored in a single contiguous + * section, or one section per ECC chunk plus an extra section + * for the remaining bytes (or other funky layout). + * @oobfree: OOB region struct filled with the appropriate free position + * information + * + * This functions return free bytes position in the OOB area. I you want + * to get all the free bytes information, then you should call + * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree) +{ + memset(oobfree, 0, sizeof(*oobfree)); + + if (!mtd || section < 0) + return -EINVAL; + + if (!mtd->ecclayout) + return -ENOTSUPP; + + if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE) + return -ERANGE; + + oobfree->offset = mtd->ecclayout->oobfree[section].offset; + oobfree->length = mtd->ecclayout->oobfree[section].length; + + return 0; +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_free); + +/** + * mtd_ooblayout_find_region - Find the region attached to a specific byte + * @mtd: mtd info structure + * @byte: the byte we are searching for + * @sectionp: pointer where the section id will be stored + * @oobregion: used to retrieve the ECC position + * @iter: iterator function. Should be either mtd_ooblayout_free or + * mtd_ooblayout_ecc depending on the region type you're searching for + * + * This functions returns the section id and oobregion information of a + * specific byte. For example, say you want to know where the 4th ECC byte is + * stored, you'll use: + * + * mtd_ooblayout_find_region(mtd, 3, §ion, &oobregion, mtd_ooblayout_ecc); + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte, + int *sectionp, struct mtd_oob_region *oobregion, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + int pos = 0, ret, section = 0; + + memset(oobregion, 0, sizeof(*oobregion)); + + while (1) { + ret = iter(mtd, section, oobregion); + if (ret) + return ret; + + if (pos + oobregion->length > byte) + break; + + pos += oobregion->length; + section++; + } + + /* + * Adjust region info to make it start at the beginning at the + * 'start' ECC byte. + */ + oobregion->offset += byte - pos; + oobregion->length -= byte - pos; + *sectionp = section; + + return 0; +} + +/** + * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific + * ECC byte + * @mtd: mtd info structure + * @eccbyte: the byte we are searching for + * @sectionp: pointer where the section id will be stored + * @oobregion: OOB region information + * + * Works like mtd_ooblayout_find_region() except it searches for a specific ECC + * byte. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, + int *section, + struct mtd_oob_region *oobregion) +{ + return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion); + +/** + * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer + * @mtd: mtd info structure + * @buf: destination buffer to store OOB bytes + * @oobbuf: OOB buffer + * @start: first byte to retrieve + * @nbytes: number of bytes to retrieve + * @iter: section iterator + * + * Extract bytes attached to a specific category (ECC or free) + * from the OOB buffer and copy them into buf. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf, + const u8 *oobbuf, int start, int nbytes, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret; + + ret = mtd_ooblayout_find_region(mtd, start, §ion, + &oobregion, iter); + + while (!ret) { + int cnt; + + cnt = oobregion.length > nbytes ? nbytes : oobregion.length; + memcpy(buf, oobbuf + oobregion.offset, cnt); + buf += cnt; + nbytes -= cnt; + + if (!nbytes) + break; + + ret = iter(mtd, ++section, &oobregion); + } + + return ret; +} + +/** + * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer + * @mtd: mtd info structure + * @buf: source buffer to get OOB bytes from + * @oobbuf: OOB buffer + * @start: first OOB byte to set + * @nbytes: number of OOB bytes to set + * @iter: section iterator + * + * Fill the OOB buffer with data provided in buf. The category (ECC or free) + * is selected by passing the appropriate iterator. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf, + u8 *oobbuf, int start, int nbytes, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret; + + ret = mtd_ooblayout_find_region(mtd, start, §ion, + &oobregion, iter); + + while (!ret) { + int cnt; + + cnt = oobregion.length > nbytes ? nbytes : oobregion.length; + memcpy(oobbuf + oobregion.offset, buf, cnt); + buf += cnt; + nbytes -= cnt; + + if (!nbytes) + break; + + ret = iter(mtd, ++section, &oobregion); + } + + return ret; +} + +/** + * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category + * @mtd: mtd info structure + * @iter: category iterator + * + * Count the number of bytes in a given category. + * + * Returns a positive value on success, a negative error code otherwise. + */ +static int mtd_ooblayout_count_bytes(struct mtd_info *mtd, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret, nbytes = 0; + + while (1) { + ret = iter(mtd, section++, &oobregion); + if (ret) { + if (ret == -ERANGE) + ret = nbytes; + break; + } + + nbytes += oobregion.length; + } + + return ret; +} + +/** + * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer + * @mtd: mtd info structure + * @eccbuf: destination buffer to store ECC bytes + * @oobbuf: OOB buffer + * @start: first ECC byte to retrieve + * @nbytes: number of ECC bytes to retrieve + * + * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf, + const u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes); + +/** + * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer + * @mtd: mtd info structure + * @eccbuf: source buffer to get ECC bytes from + * @oobbuf: OOB buffer + * @start: first ECC byte to set + * @nbytes: number of ECC bytes to set + * + * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf, + u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes); + +/** + * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer + * @mtd: mtd info structure + * @databuf: destination buffer to store ECC bytes + * @oobbuf: OOB buffer + * @start: first ECC byte to retrieve + * @nbytes: number of ECC bytes to retrieve + * + * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf, + const u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes, + mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes); + +/** + * mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer + * @mtd: mtd info structure + * @eccbuf: source buffer to get data bytes from + * @oobbuf: OOB buffer + * @start: first ECC byte to set + * @nbytes: number of ECC bytes to set + * + * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, + u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes, + mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes); + +/** + * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB + * @mtd: mtd info structure + * + * Works like mtd_ooblayout_count_bytes(), except it count free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_count_freebytes(struct mtd_info *mtd) +{ + return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes); + +/** + * mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB + * @mtd: mtd info structure + * + * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd) +{ + return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); + /* * Method to access the protection register area, present in some flash * devices. The user data is one time programmable but the factory data is read |