mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
mtd: add page addressed operations
Currently read(), write() and erase() all use 32 bit addressing. This is a problem when writing to media > 4 GiB, e.g. SD cards. The current implementation would wrap around after 4 GiB and corrupt data. To avoid this, add functions to the MTD subsystem that allow for page-wise addressing. This is how most of the underling storage drivers and the file-systems above work anyway. In the future we should then deprecate the 32-bit functions if all drivers are converted.
This commit is contained in:
parent
04df2bbd8a
commit
4034f96169
@ -101,6 +101,27 @@ struct mtd_desc {
|
||||
uint32_t addr,
|
||||
uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Read from the Memory Technology Device (MTD) using
|
||||
* pagewise addressing.
|
||||
*
|
||||
* @p offset should not exceed the page size
|
||||
*
|
||||
* @param[in] dev Pointer to the selected driver
|
||||
* @param[out] buff Pointer to the data buffer to store read data
|
||||
* @param[in] page Page number to start reading from
|
||||
* @param[in] offset Byte offset from the start of the page
|
||||
* @param[in] size Number of bytes
|
||||
*
|
||||
* @return number of bytes read on success
|
||||
* @return < 0 value on error
|
||||
*/
|
||||
int (*read_page)(mtd_dev_t *dev,
|
||||
void *buff,
|
||||
uint32_t page,
|
||||
uint32_t offset,
|
||||
uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Write to the Memory Technology Device (MTD)
|
||||
*
|
||||
@ -120,6 +141,27 @@ struct mtd_desc {
|
||||
uint32_t addr,
|
||||
uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Write to the Memory Technology Device (MTD) using
|
||||
* pagewise addressing.
|
||||
*
|
||||
* @p offset should not exceed the page size
|
||||
*
|
||||
* @param[in] dev Pointer to the selected driver
|
||||
* @param[out] buff Pointer to the data to be written
|
||||
* @param[in] page Page number to start writing to
|
||||
* @param[in] offset Byte offset from the start of the page
|
||||
* @param[in] size Number of bytes
|
||||
*
|
||||
* @return bytes written on success
|
||||
* @return < 0 value on error
|
||||
*/
|
||||
int (*write_page)(mtd_dev_t *dev,
|
||||
const void *buff,
|
||||
uint32_t page,
|
||||
uint32_t offset,
|
||||
uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Erase sector(s) over the Memory Technology Device (MTD)
|
||||
*
|
||||
@ -136,6 +178,21 @@ struct mtd_desc {
|
||||
uint32_t addr,
|
||||
uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Erase sector(s) of the Memory Technology Device (MTD)
|
||||
*
|
||||
* @param[in] dev Pointer to the selected driver
|
||||
* @param[in] sector the first sector number to erase
|
||||
|
||||
* @param[in] count Number of sectors to erase
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return < 0 value on error
|
||||
*/
|
||||
int (*erase_sector)(mtd_dev_t *dev,
|
||||
uint32_t sector,
|
||||
uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Control power of Memory Technology Device (MTD)
|
||||
*
|
||||
@ -176,6 +233,29 @@ int mtd_init(mtd_dev_t *mtd);
|
||||
*/
|
||||
int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Read data from a MTD device with pagewise addressing
|
||||
*
|
||||
* The MTD layer will take care of splitting up the transaction into multiple
|
||||
* reads if it is required by the underlying storage media.
|
||||
*
|
||||
* @p offset must be smaller than the page size
|
||||
*
|
||||
* @param mtd the device to read from
|
||||
* @param[out] dest the buffer to fill in
|
||||
* @param[in] page Page number to start reading from
|
||||
* @param[in] offset offset from the start of the page (in bytes)
|
||||
* @param[in] size the number of bytes to read
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return < 0 if an error occurred
|
||||
* @return -ENODEV if @p mtd is not a valid device
|
||||
* @return -ENOTSUP if operation is not supported on @p mtd
|
||||
* @return -EOVERFLOW if @p addr or @p count are not valid, i.e. outside memory
|
||||
* @return -EIO if I/O error occurred
|
||||
*/
|
||||
int mtd_read_page(mtd_dev_t *mtd, void *dest, uint32_t page, uint32_t offset, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Write data to a MTD device
|
||||
*
|
||||
@ -199,6 +279,31 @@ int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count);
|
||||
*/
|
||||
int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Write data to a MTD device with pagewise addressing
|
||||
*
|
||||
* The MTD layer will take care of splitting up the transaction into multiple
|
||||
* writes if it is required by the underlying storage media.
|
||||
*
|
||||
* @p offset must be smaller than the page size
|
||||
*
|
||||
*
|
||||
* @param mtd the device to write to
|
||||
* @param[in] src the buffer to write
|
||||
* @param[in] page Page number to start writing to
|
||||
* @param[in] offset byte offset from the start of the page
|
||||
* @param[in] size the number of bytes to write
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return < 0 if an error occurred
|
||||
* @return -ENODEV if @p mtd is not a valid device
|
||||
* @return -ENOTSUP if operation is not supported on @p mtd
|
||||
* @return -EOVERFLOW if @p addr or @p count are not valid, i.e. outside memory,
|
||||
* @return -EIO if I/O error occurred
|
||||
* @return -EINVAL if parameters are invalid
|
||||
*/
|
||||
int mtd_write_page(mtd_dev_t *mtd, const void *src, uint32_t page, uint32_t offset, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Erase sectors of a MTD device
|
||||
*
|
||||
@ -217,6 +322,22 @@ int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count);
|
||||
*/
|
||||
int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Erase sectors of a MTD device
|
||||
*
|
||||
* @param mtd the device to erase
|
||||
* @param[in] sector the first sector number to erase
|
||||
* @param[in] num the number of sectors to erase
|
||||
*
|
||||
* @return 0 if erase successful
|
||||
* @return < 0 if an error occurred
|
||||
* @return -ENODEV if @p mtd is not a valid device
|
||||
* @return -ENOTSUP if operation is not supported on @p mtd
|
||||
* @return -EOVERFLOW if @p addr or @p sector are not valid, i.e. outside memory
|
||||
* @return -EIO if I/O error occurred
|
||||
*/
|
||||
int mtd_erase_sector(mtd_dev_t *mtd, uint32_t sector, uint32_t num);
|
||||
|
||||
/**
|
||||
* @brief Set power mode on a MTD device
|
||||
*
|
||||
|
@ -18,8 +18,12 @@
|
||||
* @author Vincent Dupont <vincent@otakeys.com>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "bitarithm.h"
|
||||
#include "mtd.h"
|
||||
|
||||
int mtd_init(mtd_dev_t *mtd)
|
||||
@ -42,12 +46,62 @@ int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (mtd->driver->read) {
|
||||
return mtd->driver->read(mtd, dest, addr, count);
|
||||
/* page size is always a power of two */
|
||||
const uint32_t page_shift = bitarithm_msb(mtd->page_size);
|
||||
const uint32_t page_mask = mtd->page_size - 1;
|
||||
|
||||
return mtd_read_page(mtd, dest, addr >> page_shift, addr & page_mask, count);
|
||||
}
|
||||
|
||||
int mtd_read_page(mtd_dev_t *mtd, void *dest, uint32_t page, uint32_t offset,
|
||||
uint32_t count)
|
||||
{
|
||||
if (!mtd || !mtd->driver) {
|
||||
return -ENODEV;
|
||||
}
|
||||
else {
|
||||
return -ENOTSUP;
|
||||
|
||||
if (mtd->driver->read_page == NULL) {
|
||||
/* TODO: remove when all backends implement read_page */
|
||||
if (mtd->driver->read) {
|
||||
return mtd->driver->read(mtd, dest, mtd->page_size * page + offset, count);
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation assumes page size is <= INT_MAX and a power of two. */
|
||||
/* We didn't find hardware yet where this is not true. */
|
||||
assert(mtd->page_size <= INT_MAX);
|
||||
assert(bitarithm_bits_set(mtd->page_size) == 1);
|
||||
|
||||
/* page size is always a power of two */
|
||||
const uint32_t page_shift = bitarithm_msb(mtd->page_size);
|
||||
const uint32_t page_mask = mtd->page_size - 1;
|
||||
|
||||
page += offset >> page_shift;
|
||||
offset = offset & page_mask;
|
||||
|
||||
char *_dst = dest;
|
||||
|
||||
while (count) {
|
||||
int read_bytes = mtd->driver->read_page(mtd, _dst, page, offset, count);
|
||||
|
||||
if (read_bytes < 0) {
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
count -= read_bytes;
|
||||
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
_dst += read_bytes;
|
||||
page += (offset + read_bytes) >> page_shift;
|
||||
offset = (offset + read_bytes) & page_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count)
|
||||
@ -56,12 +110,62 @@ int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (mtd->driver->write) {
|
||||
return mtd->driver->write(mtd, src, addr, count);
|
||||
/* page size is always a power of two */
|
||||
const uint32_t page_shift = bitarithm_msb(mtd->page_size);
|
||||
const uint32_t page_mask = mtd->page_size - 1;
|
||||
|
||||
return mtd_write_page(mtd, src, addr >> page_shift, addr & page_mask, count);
|
||||
}
|
||||
|
||||
int mtd_write_page(mtd_dev_t *mtd, const void *src, uint32_t page, uint32_t offset,
|
||||
uint32_t count)
|
||||
{
|
||||
if (!mtd || !mtd->driver) {
|
||||
return -ENODEV;
|
||||
}
|
||||
else {
|
||||
return -ENOTSUP;
|
||||
|
||||
if (mtd->driver->write_page == NULL) {
|
||||
/* TODO: remove when all backends implement write_page */
|
||||
if (mtd->driver->write) {
|
||||
return mtd->driver->write(mtd, src, mtd->page_size * page + offset, count);
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation assumes page size is <= INT_MAX and a power of two. */
|
||||
/* We didn't find hardware yet where this is not true. */
|
||||
assert(mtd->page_size <= INT_MAX);
|
||||
assert(bitarithm_bits_set(mtd->page_size) == 1);
|
||||
|
||||
/* page size is always a power of two */
|
||||
const uint32_t page_shift = bitarithm_msb(mtd->page_size);
|
||||
const uint32_t page_mask = mtd->page_size - 1;
|
||||
|
||||
page += offset >> page_shift;
|
||||
offset = offset & page_mask;
|
||||
|
||||
const char *_src = src;
|
||||
|
||||
while (count) {
|
||||
int written = mtd->driver->write_page(mtd, _src, page, offset, count);
|
||||
|
||||
if (written < 0) {
|
||||
return written;
|
||||
}
|
||||
|
||||
count -= written;
|
||||
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
_src += written;
|
||||
page += (offset + written) >> page_shift;
|
||||
offset = (offset + written) & page_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count)
|
||||
@ -70,12 +174,42 @@ int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (mtd->driver->erase) {
|
||||
return mtd->driver->erase(mtd, addr, count);
|
||||
uint32_t sector_size = mtd->pages_per_sector * mtd->page_size;
|
||||
|
||||
if (count % sector_size) {
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
else {
|
||||
return -ENOTSUP;
|
||||
|
||||
if (addr % sector_size) {
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
return mtd_erase_sector(mtd, addr / sector_size, count / sector_size);
|
||||
}
|
||||
|
||||
int mtd_erase_sector(mtd_dev_t *mtd, uint32_t sector, uint32_t count)
|
||||
{
|
||||
if (!mtd || !mtd->driver) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (sector >= mtd->sector_count) {
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
if (mtd->driver->erase_sector == NULL) {
|
||||
/* TODO: remove when all backends implement erase_sector */
|
||||
if (mtd->driver->erase) {
|
||||
uint32_t sector_size = mtd->pages_per_sector * mtd->page_size;
|
||||
return mtd->driver->erase(mtd,
|
||||
sector * sector_size,
|
||||
count * sector_size);
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
return mtd->driver->erase_sector(mtd, sector, count);
|
||||
}
|
||||
|
||||
int mtd_power(mtd_dev_t *mtd, enum mtd_power_state power)
|
||||
|
Loading…
Reference in New Issue
Block a user