|
@@ -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
|