Refactored NVM data structures and functions

nvm-refactor
Silvano Seva 2024-04-03 21:11:12 +02:00
rodzic d43dd57031
commit d18bcbc59f
3 zmienionych plików z 188 dodań i 155 usunięć

Wyświetl plik

@ -26,15 +26,60 @@
#include <errno.h>
/**
* Perform a byte-aligned read operation on an NVM area.
* Perform a byte-aligned read operation on a nonvolatile memory.
*
* @param area: pointer to the NVM are descriptor.
* @param address: start address for the read operation.
* @param dev: NVM device number.
* @param part: partition number, -1 for direct device access.
* @param address: offset for the read operation.
* @param data: pointer to a buffer where to store the data read.
* @param len: number of bytes to read.
* @return zero on success, a negative error code otherwise.
*/
int nvmArea_read(const struct nvmArea *area, uint32_t address, void *data, size_t len);
int nvm_read(const uint32_t dev, const int part, uint32_t offset, void *data,
size_t len);
/**
* Perform a write operation on a nonvolatile memory.
*
* @param dev: NVM device number.
* @param part: partition number, -1 for direct device access.
* @param offset: offset for the write operation.
* @param data: pointer to a buffer containing the data to write.
* @param len: number of bytes to write.
* @return zero on success, a negative error code otherwise.
*/
int nvm_write(const uint32_t dev, const int part, uint32_t offset, const void *data,
size_t len);
/**
* Perform an erase operation on a nonvolatile memory. Acceptable offset and
* size depend on characteristics of the underlying device.
*
* @param dev: NVM device number.
* @param part: partition number, -1 for direct device access.
* @param offset: offset for the erase operation.
* @param size: size of the area to be erased.
* @return zero on success, a negative error code otherwise.
*/
int nvm_erase(const uint32_t dev, const int part, uint32_t offset, size_t size);
/**
* Perform a byte-aligned read operation on an NVM area.
*
* @param area: pointer to the NVM are descriptor.
* @param offset: offset for the read operation.
* @param data: pointer to a buffer where to store the data read.
* @param len: number of bytes to read.
* @return zero on success, a negative error code otherwise.
*/
static inline int nvm_devRead(const struct nvmDevice *dev, uint32_t offset,
void *data, size_t len)
{
if((offset + len) > dev->size)
return -EINVAL;
return dev->ops->read(dev, offset, data, len);
}
/**
* Perform a byte-aligned write operation on an NVM area. If the underlying
@ -42,98 +87,47 @@ int nvmArea_read(const struct nvmArea *area, uint32_t address, void *data, size_
* the write.
*
* @param area: pointer to the NVM are descriptor.
* @param address: start address for the write operation.
* @param offset: offset for the write operation.
* @param data: pointer to a buffer containing the data to write.
* @param len: number of bytes to write.
* @return zero on success, a negative error code otherwise.
*/
int nvmArea_write(const struct nvmArea *area, uint32_t address, const void *data,
size_t len);
static inline int nvm_devWrite(const struct nvmDevice *dev, uint32_t offset,
const void *data, size_t len)
{
if(dev->ops->write == NULL)
return -ENOTSUP;
if((offset + len) > dev->size)
return -EINVAL;
return dev->ops->write(dev, offset, data, len);
}
/**
* Perform an erase operation on an NVM area. Acceptable start address and size
* depend on the NVM device the area belongs to.
* Perform an erase operation on an NVM area. Acceptable offset and size depend
* on the NVM device the area belongs to.
*
* @param area: pointer to the NVM are descriptor.
* @param address: start address for the erase operation.
* @param offset: offset for the erase operation.
* @param size: size of the area to be erased.
* @return zero on success, a negative error code otherwise.
*/
int nvmArea_erase(const struct nvmArea *area, uint32_t address, size_t size);
int nvm_devErase(const struct nvmDevice *dev, uint32_t offset, size_t size);
/**
* Get the parameters of the NVM device associated to a memory area.
* Sync device cache and state to its underlying hardware.
* If the device does not support sync this function pointer is set to NULL.
*
* @param area: pointer to the NVM are descriptor.
* @return pointer to the device parameters' data structure.
* @param dev: pointer to NVM device descriptor.
* @return 0 on success, negative errno code on fail.
*/
static inline const struct nvmParams *nvmArea_params(const struct nvmArea *area)
static inline int nvm_devSync(const struct nvmDevice *dev)
{
const struct nvmDevice *dev = area->dev;
if(dev->ops->sync == NULL)
return -ENOTSUP;
return dev->api->params(dev);
}
/**
* Perform a byte-aligned read operation on an NVM area partition.
*
* @param area: pointer to the NVM are descriptor.
* @param pNum: partition number.
* @param address: start address for the read operation.
* @param data: pointer to a buffer where to store the data read.
* @param len: number of bytes to read.
* @return zero on success, a negative error code otherwise.
*/
static inline int nvmArea_readPartition(const struct nvmArea *area,
const uint32_t pNum, uint32_t offset,
void *data, size_t len)
{
const struct nvmPartition *partition = &(area->partitions[pNum]);
const size_t startAddr = area->startAddr + partition->offset + offset;
return nvmArea_read(area, startAddr, data, len);
}
/**
* Perform a byte-aligned write operation on an NVM area partition. If the
* underlying device requires state syncing, a sync operation is performed at
* the end of the write.
*
* @param area: pointer to the NVM are descriptor.
* @param pNum: partition number.
* @param address: start address for the write operation.
* @param data: pointer to a buffer containing the data to write.
* @param len: number of bytes to write.
* @return zero on success, a negative error code otherwise.
*/
static inline int nvmArea_writePartition(const struct nvmArea *area,
const uint32_t pNum, uint32_t offset,
const void *data, size_t len)
{
const struct nvmPartition *partition = &(area->partitions[pNum]);
const size_t startAddr = area->startAddr + partition->offset + offset;
return nvmArea_write(area, startAddr, data, len);
}
/**
* Perform an erase operation on an NVM area partition. Acceptable start address
* and size depend on the NVM device the area belongs to.
*
* @param area: pointer to the NVM are descriptor.
* @param pNum: partition number.
* @param address: start address for the erase operation.
* @param size: size of the area to be erased.
* @return zero on success, a negative error code otherwise.
*/
static inline int nvmArea_erasePartition(const struct nvmArea *area,
const uint32_t pNum, uint32_t offset,
size_t size)
{
const struct nvmPartition *partition = &(area->partitions[pNum]);
const size_t startAddr = area->startAddr + partition->offset + offset;
return nvmArea_erase(area, startAddr, size);
return dev->ops->sync(dev);
}
#endif

Wyświetl plik

@ -41,23 +41,33 @@ enum nvmType
};
/**
* Nonvolatile memory device parameters. The content of this data structure is
* filled by the device driver and then kept constant.
* Enumeration field for nonvolatile memory properties.
*/
struct nvmParams
enum nvmProperties
{
size_t write_size; ///< Minimum write size (write unit)
size_t erase_size; ///< Minimum erase size (erase unit)
size_t erase_cycles; ///< Maximum allowed erase cycles of a block
uint8_t type; ///< Device type
NVM_WRITE = 0x100, ///< Device allows write access
NVM_BITWRITE = 0x200, ///< Device allows to change the value of single bits
NVM_ERASE = 0x400, ///< Device memory needs to be erased before writing
};
/**
* Nonvolatile memory device information block. The content of this data structure
* is defined by the device driver and remains constant.
*/
struct nvmInfo
{
size_t write_size; ///< Minimum write size (write unit)
size_t erase_size; ///< Minimum erase size (erase unit)
size_t erase_cycles; ///< Maximum allowed erase cycles of a block
uint32_t device_info; ///< Device type and flags
};
struct nvmDevice;
/**
* Standard API for nonvolatile memory driver.
* Nonvolatile memory device driver.
*/
struct nvmApi
struct nvmOps
{
/**
* Read data from nonvolatile memory device.
@ -104,29 +114,18 @@ struct nvmApi
* @return 0 on success, negative errno code on fail.
*/
int (*sync)(const struct nvmDevice *dev);
/**
* Get device parameters.
*
* @param dev: pointer to NVM device descriptor.
* @return pointer to the device parameters' data structure.
*/
const struct nvmParams *(*params)(const struct nvmDevice *dev);
};
/**
* Nonvolatile memory device driver.
*/
struct nvmDevice
{
const struct nvmApi *api; ///< Driver API
const void *config; ///< Driver configuration data
void *const priv; ///< Driver runtime data
const struct nvmOps *ops; ///< Device operations
const struct nvmInfo *info; ///< Device info
const size_t size; ///< Device size
};
/**
* Data structure representing a partition of a NVM area. The offset of the
* partition is referred to the beginning of the area itself.
* Data structure representing a partition of a nonvolatile memory. The offset
* of the partition is referred to the beginning of the memory area.
*/
struct nvmPartition
{
@ -135,17 +134,16 @@ struct nvmPartition
};
/**
* Nonvolatile memory area descriptor. This data structure contains all the data
* Nonvolatile memory descriptor. This data structure contains all the data
* relative to an area of nonvolatile memory with a fixed size, managed by a
* given device and with zero or more partition.
*/
struct nvmArea
struct nvmDescriptor
{
const char *name; ///< Area name
const struct nvmDevice *dev; ///< Device driver to manage the area
const size_t startAddr; ///< Start address of the area from the beginning of the device
const size_t size; ///< Size of the area, in bytes
const struct nvmPartition *partitions; ///< List of partitions
const char *name; ///< Name
const struct nvmDevice *dev; ///< Associated device driver
const size_t partNum; ///< Number of partitions
const struct nvmPartition *partitions; ///< Partion table
};
@ -160,12 +158,13 @@ void nvm_init();
void nvm_terminate();
/**
* Get a list of the available nonvolatile memory areas of the device.
* Obtain the descriptor of a given nonvolatile memory.
*
* @param list: pointer where to store the pointer to the list head.
* @return number of elements in the list.
* @param index: index of the nonvolatile memory.
* @return a pointer to the memory descriptor or NULL if the requested descriptor
* does not exist.
*/
size_t nvm_getMemoryAreas(const struct nvmArea **list);
const struct nvmDescriptor *nvm_getDesc(const size_t index);
/**
* Load calibration data from nonvolatile memory.

Wyświetl plik

@ -19,6 +19,7 @@
***************************************************************************/
#include <interfaces/nvmem.h>
#include <nvmem_access.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
@ -26,60 +27,99 @@
/**
* \internal
* Check if a read/write/erase operation is within the bounds of a given NVM
* area.
* Compute the absolute offset from the beginning of an NVM device, given the
* device descriptor and the partition number. If the partition number is set
* to -1, the offset is considered from the beginning of the device.
* This function performs also a bound check to guarantee that the following
* operation stays within the limits of the partition (if any).
*
* @param area: pointer to the NVM area descriptor
* @param addr: start address of the read/write/erase operation
* @param len: size of the read/write/erase operation
* @return true if the operation is within the NVM area bounds
* @param nvm: pointer to NVM descriptor.
* @param part: partition number.
* @param offset: pointer to the offset.
* @param len: lenght of the read/write/erase operation.
* @return a negative error code or zero.
*/
static inline bool checkBounds(const struct nvmArea *area, uint32_t addr, size_t len)
static inline int getAbsOffset(const struct nvmDescriptor *nvm, const int part,
uint32_t *offset, size_t len)
{
return (addr >= area->startAddr)
&& ((addr + len) < (area->startAddr + area->size));
}
const struct nvmPartition *np;
// Offset is relative to a partition
if(part >= 0)
{
if((size_t) part >= nvm->partNum)
return -EINVAL;
int nvmArea_read(const struct nvmArea *area, uint32_t address, void *data, size_t len)
{
const struct nvmDevice *dev = area->dev;
np = &nvm->partitions[part];
if(checkBounds(area, address, len) == false)
return -EINVAL;
if((*offset + len) > np->size)
return -EINVAL;
return dev->api->read(dev, address, data, len);
}
int nvmArea_write(const struct nvmArea *area, uint32_t address, void *data, size_t len)
{
const struct nvmDevice *dev = area->dev;
if(checkBounds(area, address, len) == false)
return -EINVAL;
if(dev->api->write == NULL)
return -ENOTSUP;
int ret = dev->api->write(dev, address, data, len);
if(ret < 0)
return ret;
if(dev->api->sync != NULL)
dev->api->sync(dev);
*offset += np->offset;
}
return 0;
}
int nvmArea_erase(const struct nvmArea *area, uint32_t address, size_t size)
{
const struct nvmDevice *dev = area->dev;
if(checkBounds(area, address, size) == false)
int nvm_read(const uint32_t dev, const int part, uint32_t offset, void *data,
size_t len)
{
const struct nvmDescriptor *nvm = nvm_getDesc(dev);
if(nvm == NULL)
return -EINVAL;
if(dev->api->erase == NULL)
int ret = getAbsOffset(nvm, part, &offset, len);
if(ret < 0)
return ret;
return nvm_devRead(nvm->dev, offset, data, len);
}
int nvm_write(const uint32_t dev, const int part, uint32_t offset, const void *data,
size_t len)
{
const struct nvmDescriptor *nvm = nvm_getDesc(dev);
if(nvm == NULL)
return -EINVAL;
int ret = getAbsOffset(nvm, part, &offset, len);
if(ret < 0)
return ret;
return nvm_devWrite(nvm->dev, offset, data, len);
}
int nvm_erase(const uint32_t dev, const int part, uint32_t offset, size_t size)
{
const struct nvmDescriptor *nvm = nvm_getDesc(dev);
if(nvm == NULL)
return -EINVAL;
int ret = getAbsOffset(nvm, part, &offset, size);
if(ret < 0)
return ret;
return nvm_devErase(nvm->dev, offset, size);
}
int nvm_devErase(const struct nvmDevice *dev, uint32_t offset, size_t size)
{
// Erase operation is allowed
if(dev->ops->erase == NULL)
return -ENOTSUP;
return dev->api->erase(dev, address, size);
// Out-of-bounds check
if((offset + size) > dev->size)
return -EINVAL;
// Start offset must be aligned to the erase size
if((offset % dev->info->erase_size) == 0)
return -EINVAL;
// Total size must be aligned to the erase size
if((size % dev->info->erase_size) == 0)
return -EINVAL;
return dev->ops->erase(dev, offset, size);
}