libs
This commit is contained in:
797
lib_littlefs/lib_littlefs/api/lfs.h
Normal file
797
lib_littlefs/lib_littlefs/api/lfs.h
Normal file
@@ -0,0 +1,797 @@
|
||||
/*
|
||||
* The little filesystem
|
||||
*
|
||||
* Copyright (c) 2022, The littlefs authors.
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef LFS_H
|
||||
#define LFS_H
|
||||
|
||||
#include "lfs_util.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
/// Version info ///
|
||||
|
||||
// Software library version
|
||||
// Major (top-nibble), incremented on backwards incompatible changes
|
||||
// Minor (bottom-nibble), incremented on feature additions
|
||||
#define LFS_VERSION 0x0002000a
|
||||
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
|
||||
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
|
||||
|
||||
// Version of On-disk data structures
|
||||
// Major (top-nibble), incremented on backwards incompatible changes
|
||||
// Minor (bottom-nibble), incremented on feature additions
|
||||
#define LFS_DISK_VERSION 0x00020001
|
||||
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
|
||||
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
|
||||
|
||||
|
||||
/// Definitions ///
|
||||
|
||||
// Type definitions
|
||||
typedef uint32_t lfs_size_t;
|
||||
typedef uint32_t lfs_off_t;
|
||||
|
||||
typedef int32_t lfs_ssize_t;
|
||||
typedef int32_t lfs_soff_t;
|
||||
|
||||
typedef uint32_t lfs_block_t;
|
||||
|
||||
// Maximum name size in bytes, may be redefined to reduce the size of the
|
||||
// info struct. Limited to <= 1022. Stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
#ifndef LFS_NAME_MAX
|
||||
#define LFS_NAME_MAX 255
|
||||
#endif
|
||||
|
||||
// Maximum size of a file in bytes, may be redefined to limit to support other
|
||||
// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
#ifndef LFS_FILE_MAX
|
||||
#define LFS_FILE_MAX 2147483647
|
||||
#endif
|
||||
|
||||
// Maximum size of custom attributes in bytes, may be redefined, but there is
|
||||
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. Stored
|
||||
// in superblock and must be respected by other littlefs drivers.
|
||||
#ifndef LFS_ATTR_MAX
|
||||
#define LFS_ATTR_MAX 1022
|
||||
#endif
|
||||
|
||||
// Possible error codes, these are negative to allow
|
||||
// valid positive return values
|
||||
enum lfs_error {
|
||||
LFS_ERR_OK = 0, // No error
|
||||
LFS_ERR_IO = -5, // Error during device operation
|
||||
LFS_ERR_CORRUPT = -84, // Corrupted
|
||||
LFS_ERR_NOENT = -2, // No directory entry
|
||||
LFS_ERR_EXIST = -17, // Entry already exists
|
||||
LFS_ERR_NOTDIR = -20, // Entry is not a dir
|
||||
LFS_ERR_ISDIR = -21, // Entry is a dir
|
||||
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
|
||||
LFS_ERR_BADF = -9, // Bad file number
|
||||
LFS_ERR_FBIG = -27, // File too large
|
||||
LFS_ERR_INVAL = -22, // Invalid parameter
|
||||
LFS_ERR_NOSPC = -28, // No space left on device
|
||||
LFS_ERR_NOMEM = -12, // No more memory available
|
||||
LFS_ERR_NOATTR = -61, // No data/attr available
|
||||
LFS_ERR_NAMETOOLONG = -36, // File name too long
|
||||
};
|
||||
|
||||
// File types
|
||||
enum lfs_type {
|
||||
// file types
|
||||
LFS_TYPE_REG = 0x001,
|
||||
LFS_TYPE_DIR = 0x002,
|
||||
|
||||
// internally used types
|
||||
LFS_TYPE_SPLICE = 0x400,
|
||||
LFS_TYPE_NAME = 0x000,
|
||||
LFS_TYPE_STRUCT = 0x200,
|
||||
LFS_TYPE_USERATTR = 0x300,
|
||||
LFS_TYPE_FROM = 0x100,
|
||||
LFS_TYPE_TAIL = 0x600,
|
||||
LFS_TYPE_GLOBALS = 0x700,
|
||||
LFS_TYPE_CRC = 0x500,
|
||||
|
||||
// internally used type specializations
|
||||
LFS_TYPE_CREATE = 0x401,
|
||||
LFS_TYPE_DELETE = 0x4ff,
|
||||
LFS_TYPE_SUPERBLOCK = 0x0ff,
|
||||
LFS_TYPE_DIRSTRUCT = 0x200,
|
||||
LFS_TYPE_CTZSTRUCT = 0x202,
|
||||
LFS_TYPE_INLINESTRUCT = 0x201,
|
||||
LFS_TYPE_SOFTTAIL = 0x600,
|
||||
LFS_TYPE_HARDTAIL = 0x601,
|
||||
LFS_TYPE_MOVESTATE = 0x7ff,
|
||||
LFS_TYPE_CCRC = 0x500,
|
||||
LFS_TYPE_FCRC = 0x5ff,
|
||||
|
||||
// internal chip sources
|
||||
LFS_FROM_NOOP = 0x000,
|
||||
LFS_FROM_MOVE = 0x101,
|
||||
LFS_FROM_USERATTRS = 0x102,
|
||||
};
|
||||
|
||||
// File open flags
|
||||
enum lfs_open_flags {
|
||||
// open flags
|
||||
LFS_O_RDONLY = 1, // Open a file as read only
|
||||
#ifndef LFS_READONLY
|
||||
LFS_O_WRONLY = 2, // Open a file as write only
|
||||
LFS_O_RDWR = 3, // Open a file as read and write
|
||||
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
|
||||
LFS_O_EXCL = 0x0200, // Fail if a file already exists
|
||||
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
|
||||
LFS_O_APPEND = 0x0800, // Move to end of file on every write
|
||||
#endif
|
||||
|
||||
// internally used flags
|
||||
#ifndef LFS_READONLY
|
||||
LFS_F_DIRTY = 0x010000, // File does not match storage
|
||||
LFS_F_WRITING = 0x020000, // File has been written since last flush
|
||||
#endif
|
||||
LFS_F_READING = 0x040000, // File has been read since last flush
|
||||
#ifndef LFS_READONLY
|
||||
LFS_F_ERRED = 0x080000, // An error occurred during write
|
||||
#endif
|
||||
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
|
||||
};
|
||||
|
||||
// File seek flags
|
||||
enum lfs_whence_flags {
|
||||
LFS_SEEK_SET = 0, // Seek relative to an absolute position
|
||||
LFS_SEEK_CUR = 1, // Seek relative to the current file position
|
||||
LFS_SEEK_END = 2, // Seek relative to the end of the file
|
||||
};
|
||||
|
||||
|
||||
// Configuration provided during initialization of the littlefs
|
||||
struct lfs_config {
|
||||
// Opaque user provided context that can be used to pass
|
||||
// information to the block device operations
|
||||
void *context;
|
||||
|
||||
// Read a region in a block. Negative error codes are propagated
|
||||
// to the user.
|
||||
int (*read)(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size);
|
||||
|
||||
// Program a region in a block. The block must have previously
|
||||
// been erased. Negative error codes are propagated to the user.
|
||||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
||||
int (*prog)(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size);
|
||||
|
||||
// Erase a block. A block must be erased before being programmed.
|
||||
// The state of an erased block is undefined. Negative error codes
|
||||
// are propagated to the user.
|
||||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
||||
int (*erase)(const struct lfs_config *c, lfs_block_t block);
|
||||
|
||||
// Sync the state of the underlying block device. Negative error codes
|
||||
// are propagated to the user.
|
||||
int (*sync)(const struct lfs_config *c);
|
||||
|
||||
#ifdef LFS_THREADSAFE
|
||||
// Lock the underlying block device. Negative error codes
|
||||
// are propagated to the user.
|
||||
int (*lock)(const struct lfs_config *c);
|
||||
|
||||
// Unlock the underlying block device. Negative error codes
|
||||
// are propagated to the user.
|
||||
int (*unlock)(const struct lfs_config *c);
|
||||
#endif
|
||||
|
||||
// Minimum size of a block read in bytes. All read operations will be a
|
||||
// multiple of this value.
|
||||
lfs_size_t read_size;
|
||||
|
||||
// Minimum size of a block program in bytes. All program operations will be
|
||||
// a multiple of this value.
|
||||
lfs_size_t prog_size;
|
||||
|
||||
// Size of an erasable block in bytes. This does not impact ram consumption
|
||||
// and may be larger than the physical erase size. However, non-inlined
|
||||
// files take up at minimum one block. Must be a multiple of the read and
|
||||
// program sizes.
|
||||
lfs_size_t block_size;
|
||||
|
||||
// Number of erasable blocks on the device. Defaults to block_count stored
|
||||
// on disk when zero.
|
||||
lfs_size_t block_count;
|
||||
|
||||
// Number of erase cycles before littlefs evicts metadata logs and moves
|
||||
// the metadata to another block. Suggested values are in the
|
||||
// range 100-1000, with large values having better performance at the cost
|
||||
// of less consistent wear distribution.
|
||||
//
|
||||
// Set to -1 to disable block-level wear-leveling.
|
||||
int32_t block_cycles;
|
||||
|
||||
// Size of block caches in bytes. Each cache buffers a portion of a block in
|
||||
// RAM. The littlefs needs a read cache, a program cache, and one additional
|
||||
// cache per file. Larger caches can improve performance by storing more
|
||||
// data and reducing the number of disk accesses. Must be a multiple of the
|
||||
// read and program sizes, and a factor of the block size.
|
||||
lfs_size_t cache_size;
|
||||
|
||||
// Size of the lookahead buffer in bytes. A larger lookahead buffer
|
||||
// increases the number of blocks found during an allocation pass. The
|
||||
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
|
||||
// can track 8 blocks.
|
||||
lfs_size_t lookahead_size;
|
||||
|
||||
// Threshold for metadata compaction during lfs_fs_gc in bytes. Metadata
|
||||
// pairs that exceed this threshold will be compacted during lfs_fs_gc.
|
||||
// Defaults to ~88% block_size when zero, though the default may change
|
||||
// in the future.
|
||||
//
|
||||
// Note this only affects lfs_fs_gc. Normal compactions still only occur
|
||||
// when full.
|
||||
//
|
||||
// Set to -1 to disable metadata compaction during lfs_fs_gc.
|
||||
lfs_size_t compact_thresh;
|
||||
|
||||
// Optional statically allocated read buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *read_buffer;
|
||||
|
||||
// Optional statically allocated program buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *prog_buffer;
|
||||
|
||||
// Optional statically allocated lookahead buffer. Must be lookahead_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *lookahead_buffer;
|
||||
|
||||
// Optional upper limit on length of file names in bytes. No downside for
|
||||
// larger names except the size of the info struct which is controlled by
|
||||
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX or name_max stored on
|
||||
// disk when zero.
|
||||
lfs_size_t name_max;
|
||||
|
||||
// Optional upper limit on files in bytes. No downside for larger files
|
||||
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX or file_max stored
|
||||
// on disk when zero.
|
||||
lfs_size_t file_max;
|
||||
|
||||
// Optional upper limit on custom attributes in bytes. No downside for
|
||||
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
|
||||
// LFS_ATTR_MAX or attr_max stored on disk when zero.
|
||||
lfs_size_t attr_max;
|
||||
|
||||
// Optional upper limit on total space given to metadata pairs in bytes. On
|
||||
// devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB)
|
||||
// can help bound the metadata compaction time. Must be <= block_size.
|
||||
// Defaults to block_size when zero.
|
||||
lfs_size_t metadata_max;
|
||||
|
||||
// Optional upper limit on inlined files in bytes. Inlined files live in
|
||||
// metadata and decrease storage requirements, but may be limited to
|
||||
// improve metadata-related performance. Must be <= cache_size, <=
|
||||
// attr_max, and <= block_size/8. Defaults to the largest possible
|
||||
// inline_max when zero.
|
||||
//
|
||||
// Set to -1 to disable inlined files.
|
||||
lfs_size_t inline_max;
|
||||
|
||||
#ifdef LFS_MULTIVERSION
|
||||
// On-disk version to use when writing in the form of 16-bit major version
|
||||
// + 16-bit minor version. This limiting metadata to what is supported by
|
||||
// older minor versions. Note that some features will be lost. Defaults to
|
||||
// to the most recent minor version when zero.
|
||||
uint32_t disk_version;
|
||||
#endif
|
||||
};
|
||||
|
||||
// File info structure
|
||||
struct lfs_info {
|
||||
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
|
||||
uint8_t type;
|
||||
|
||||
// Size of the file, only valid for REG files. Limited to 32-bits.
|
||||
lfs_size_t size;
|
||||
|
||||
// Name of the file stored as a null-terminated string. Limited to
|
||||
// LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to
|
||||
// reduce RAM. LFS_NAME_MAX is stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
char name[LFS_NAME_MAX+1];
|
||||
};
|
||||
|
||||
// Filesystem info structure
|
||||
struct lfs_fsinfo {
|
||||
// On-disk version.
|
||||
uint32_t disk_version;
|
||||
|
||||
// Size of a logical block in bytes.
|
||||
lfs_size_t block_size;
|
||||
|
||||
// Number of logical blocks in filesystem.
|
||||
lfs_size_t block_count;
|
||||
|
||||
// Upper limit on the length of file names in bytes.
|
||||
lfs_size_t name_max;
|
||||
|
||||
// Upper limit on the size of files in bytes.
|
||||
lfs_size_t file_max;
|
||||
|
||||
// Upper limit on the size of custom attributes in bytes.
|
||||
lfs_size_t attr_max;
|
||||
};
|
||||
|
||||
// Custom attribute structure, used to describe custom attributes
|
||||
// committed atomically during file writes.
|
||||
struct lfs_attr {
|
||||
// 8-bit type of attribute, provided by user and used to
|
||||
// identify the attribute
|
||||
uint8_t type;
|
||||
|
||||
// Pointer to buffer containing the attribute
|
||||
void *buffer;
|
||||
|
||||
// Size of attribute in bytes, limited to LFS_ATTR_MAX
|
||||
lfs_size_t size;
|
||||
};
|
||||
|
||||
// Optional configuration provided during lfs_file_opencfg
|
||||
struct lfs_file_config {
|
||||
// Optional statically allocated file buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *buffer;
|
||||
|
||||
// Optional list of custom attributes related to the file. If the file
|
||||
// is opened with read access, these attributes will be read from disk
|
||||
// during the open call. If the file is opened with write access, the
|
||||
// attributes will be written to disk every file sync or close. This
|
||||
// write occurs atomically with update to the file's contents.
|
||||
//
|
||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
||||
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller
|
||||
// than the buffer, it will be padded with zeros. If the stored attribute
|
||||
// is larger, then it will be silently truncated. If the attribute is not
|
||||
// found, it will be created implicitly.
|
||||
struct lfs_attr *attrs;
|
||||
|
||||
// Number of custom attributes in the list
|
||||
lfs_size_t attr_count;
|
||||
};
|
||||
|
||||
|
||||
/// internal littlefs data structures ///
|
||||
typedef struct lfs_cache {
|
||||
lfs_block_t block;
|
||||
lfs_off_t off;
|
||||
lfs_size_t size;
|
||||
uint8_t *buffer;
|
||||
} lfs_cache_t;
|
||||
|
||||
typedef struct lfs_mdir {
|
||||
lfs_block_t pair[2];
|
||||
uint32_t rev;
|
||||
lfs_off_t off;
|
||||
uint32_t etag;
|
||||
uint16_t count;
|
||||
bool erased;
|
||||
bool split;
|
||||
lfs_block_t tail[2];
|
||||
} lfs_mdir_t;
|
||||
|
||||
// littlefs directory type
|
||||
typedef struct lfs_dir {
|
||||
struct lfs_dir *next;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
lfs_mdir_t m;
|
||||
|
||||
lfs_off_t pos;
|
||||
lfs_block_t head[2];
|
||||
} lfs_dir_t;
|
||||
|
||||
// littlefs file type
|
||||
typedef struct lfs_file {
|
||||
struct lfs_file *next;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
lfs_mdir_t m;
|
||||
|
||||
struct lfs_ctz {
|
||||
lfs_block_t head;
|
||||
lfs_size_t size;
|
||||
} ctz;
|
||||
|
||||
uint32_t flags;
|
||||
lfs_off_t pos;
|
||||
lfs_block_t block;
|
||||
lfs_off_t off;
|
||||
lfs_cache_t cache;
|
||||
|
||||
const struct lfs_file_config *cfg;
|
||||
} lfs_file_t;
|
||||
|
||||
typedef struct lfs_superblock {
|
||||
uint32_t version;
|
||||
lfs_size_t block_size;
|
||||
lfs_size_t block_count;
|
||||
lfs_size_t name_max;
|
||||
lfs_size_t file_max;
|
||||
lfs_size_t attr_max;
|
||||
} lfs_superblock_t;
|
||||
|
||||
typedef struct lfs_gstate {
|
||||
uint32_t tag;
|
||||
lfs_block_t pair[2];
|
||||
} lfs_gstate_t;
|
||||
|
||||
// The littlefs filesystem type
|
||||
typedef struct lfs {
|
||||
lfs_cache_t rcache;
|
||||
lfs_cache_t pcache;
|
||||
|
||||
lfs_block_t root[2];
|
||||
struct lfs_mlist {
|
||||
struct lfs_mlist *next;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
lfs_mdir_t m;
|
||||
} *mlist;
|
||||
uint32_t seed;
|
||||
|
||||
lfs_gstate_t gstate;
|
||||
lfs_gstate_t gdisk;
|
||||
lfs_gstate_t gdelta;
|
||||
|
||||
struct lfs_lookahead {
|
||||
lfs_block_t start;
|
||||
lfs_block_t size;
|
||||
lfs_block_t next;
|
||||
lfs_block_t ckpoint;
|
||||
uint8_t *buffer;
|
||||
} lookahead;
|
||||
|
||||
const struct lfs_config *cfg;
|
||||
lfs_size_t block_count;
|
||||
lfs_size_t name_max;
|
||||
lfs_size_t file_max;
|
||||
lfs_size_t attr_max;
|
||||
lfs_size_t inline_max;
|
||||
|
||||
#ifdef LFS_MIGRATE
|
||||
struct lfs1 *lfs1;
|
||||
#endif
|
||||
} lfs_t;
|
||||
|
||||
|
||||
/// Filesystem functions ///
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Format a block device with the littlefs
|
||||
//
|
||||
// Requires a littlefs object and config struct. This clobbers the littlefs
|
||||
// object, and does not leave the filesystem mounted. The config struct must
|
||||
// be zeroed for defaults and backwards compatibility.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
|
||||
#endif
|
||||
|
||||
// Mounts a littlefs
|
||||
//
|
||||
// Requires a littlefs object and config struct. Multiple filesystems
|
||||
// may be mounted simultaneously with multiple littlefs objects. Both
|
||||
// lfs and config must be allocated while mounted. The config struct must
|
||||
// be zeroed for defaults and backwards compatibility.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
|
||||
|
||||
// Unmounts a littlefs
|
||||
//
|
||||
// Does nothing besides releasing any allocated resources.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_unmount(lfs_t *lfs);
|
||||
|
||||
/// General operations ///
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Removes a file or directory
|
||||
//
|
||||
// If removing a directory, the directory must be empty.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_remove(lfs_t *lfs, const char *path);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Rename or move a file or directory
|
||||
//
|
||||
// If the destination exists, it must match the source in type.
|
||||
// If the destination is a directory, the directory must be empty.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
|
||||
#endif
|
||||
|
||||
// Find info about a file or directory
|
||||
//
|
||||
// Fills out the info structure, based on the specified file or directory.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
|
||||
|
||||
// Get a custom attribute
|
||||
//
|
||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
||||
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than
|
||||
// the buffer, it will be padded with zeros. If the stored attribute is larger,
|
||||
// then it will be silently truncated. If no attribute is found, the error
|
||||
// LFS_ERR_NOATTR is returned and the buffer is filled with zeros.
|
||||
//
|
||||
// Returns the size of the attribute, or a negative error code on failure.
|
||||
// Note, the returned size is the size of the attribute on disk, irrespective
|
||||
// of the size of the buffer. This can be used to dynamically allocate a buffer
|
||||
// or check for existence.
|
||||
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
|
||||
uint8_t type, void *buffer, lfs_size_t size);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Set custom attributes
|
||||
//
|
||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
||||
// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be
|
||||
// implicitly created.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_setattr(lfs_t *lfs, const char *path,
|
||||
uint8_t type, const void *buffer, lfs_size_t size);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Removes a custom attribute
|
||||
//
|
||||
// If an attribute is not found, nothing happens.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type);
|
||||
#endif
|
||||
|
||||
|
||||
/// File operations ///
|
||||
|
||||
#ifndef LFS_NO_MALLOC
|
||||
// Open a file
|
||||
//
|
||||
// The mode that the file is opened in is determined by the flags, which
|
||||
// are values from the enum lfs_open_flags that are bitwise-ored together.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
|
||||
const char *path, int flags);
|
||||
|
||||
// if LFS_NO_MALLOC is defined, lfs_file_open() will fail with LFS_ERR_NOMEM
|
||||
// thus use lfs_file_opencfg() with config.buffer set.
|
||||
#endif
|
||||
|
||||
// Open a file with extra configuration
|
||||
//
|
||||
// The mode that the file is opened in is determined by the flags, which
|
||||
// are values from the enum lfs_open_flags that are bitwise-ored together.
|
||||
//
|
||||
// The config struct provides additional config options per file as described
|
||||
// above. The config struct must remain allocated while the file is open, and
|
||||
// the config struct must be zeroed for defaults and backwards compatibility.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
|
||||
const char *path, int flags,
|
||||
const struct lfs_file_config *config);
|
||||
|
||||
// Close a file
|
||||
//
|
||||
// Any pending writes are written out to storage as though
|
||||
// sync had been called and releases any allocated resources.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Synchronize a file on storage
|
||||
//
|
||||
// Any pending writes are written out to storage.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Read data from file
|
||||
//
|
||||
// Takes a buffer and size indicating where to store the read data.
|
||||
// Returns the number of bytes read, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
||||
void *buffer, lfs_size_t size);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Write data to file
|
||||
//
|
||||
// Takes a buffer and size indicating the data to write. The file will not
|
||||
// actually be updated on the storage until either sync or close is called.
|
||||
//
|
||||
// Returns the number of bytes written, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
|
||||
const void *buffer, lfs_size_t size);
|
||||
#endif
|
||||
|
||||
// Change the position of the file
|
||||
//
|
||||
// The change in position is determined by the offset and whence flag.
|
||||
// Returns the new position of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
|
||||
lfs_soff_t off, int whence);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Truncates the size of the file to the specified size
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size);
|
||||
#endif
|
||||
|
||||
// Return the position of the file
|
||||
//
|
||||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
|
||||
// Returns the position of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Change the position of the file to the beginning of the file
|
||||
//
|
||||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET)
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Return the size of the file
|
||||
//
|
||||
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
|
||||
// Returns the size of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
|
||||
/// Directory operations ///
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Create a directory
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_mkdir(lfs_t *lfs, const char *path);
|
||||
#endif
|
||||
|
||||
// Open a directory
|
||||
//
|
||||
// Once open a directory can be used with read to iterate over files.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);
|
||||
|
||||
// Close a directory
|
||||
//
|
||||
// Releases any allocated resources.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
// Read an entry in the directory
|
||||
//
|
||||
// Fills out the info structure, based on the specified file or directory.
|
||||
// Returns a positive value on success, 0 at the end of directory,
|
||||
// or a negative error code on failure.
|
||||
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);
|
||||
|
||||
// Change the position of the directory
|
||||
//
|
||||
// The new off must be a value previous returned from tell and specifies
|
||||
// an absolute offset in the directory seek.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
|
||||
|
||||
// Return the position of the directory
|
||||
//
|
||||
// The returned offset is only meant to be consumed by seek and may not make
|
||||
// sense, but does indicate the current position in the directory iteration.
|
||||
//
|
||||
// Returns the position of the directory, or a negative error code on failure.
|
||||
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
// Change the position of the directory to the beginning of the directory
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
|
||||
/// Filesystem-level filesystem operations
|
||||
|
||||
// Find on-disk info about the filesystem
|
||||
//
|
||||
// Fills out the fsinfo structure based on the filesystem found on-disk.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo);
|
||||
|
||||
// Finds the current size of the filesystem
|
||||
//
|
||||
// Note: Result is best effort. If files share COW structures, the returned
|
||||
// size may be larger than the filesystem actually is.
|
||||
//
|
||||
// Returns the number of allocated blocks, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
|
||||
|
||||
// Traverse through all blocks in use by the filesystem
|
||||
//
|
||||
// The provided callback will be called with each block address that is
|
||||
// currently in use by the filesystem. This can be used to determine which
|
||||
// blocks are in use or how much of the storage is available.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Attempt to make the filesystem consistent and ready for writing
|
||||
//
|
||||
// Calling this function is not required, consistency will be implicitly
|
||||
// enforced on the first operation that writes to the filesystem, but this
|
||||
// function allows the work to be performed earlier and without other
|
||||
// filesystem changes.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_mkconsistent(lfs_t *lfs);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Attempt any janitorial work
|
||||
//
|
||||
// This currently:
|
||||
// 1. Calls mkconsistent if not already consistent
|
||||
// 2. Compacts metadata > compact_thresh
|
||||
// 3. Populates the block allocator
|
||||
//
|
||||
// Though additional janitorial work may be added in the future.
|
||||
//
|
||||
// Calling this function is not required, but may allow the offloading of
|
||||
// expensive janitorial work to a less time-critical code path.
|
||||
//
|
||||
// Returns a negative error code on failure. Accomplishing nothing is not
|
||||
// an error.
|
||||
int lfs_fs_gc(lfs_t *lfs);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Grows the filesystem to a new size, updating the superblock with the new
|
||||
// block count.
|
||||
//
|
||||
// Note: This is irreversible.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
#ifdef LFS_MIGRATE
|
||||
// Attempts to migrate a previous version of littlefs
|
||||
//
|
||||
// Behaves similarly to the lfs_format function. Attempts to mount
|
||||
// the previous version of littlefs and update the filesystem so it can be
|
||||
// mounted with the current version of littlefs.
|
||||
//
|
||||
// Requires a littlefs object and config struct. This clobbers the littlefs
|
||||
// object, and does not leave the filesystem mounted. The config struct must
|
||||
// be zeroed for defaults and backwards compatibility.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
275
lib_littlefs/lib_littlefs/api/lfs_util.h
Normal file
275
lib_littlefs/lib_littlefs/api/lfs_util.h
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* lfs utility functions
|
||||
*
|
||||
* Copyright (c) 2022, The littlefs authors.
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef LFS_UTIL_H
|
||||
#define LFS_UTIL_H
|
||||
|
||||
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
|
||||
#define LFS_STRINGIZE2(x) #x
|
||||
|
||||
// Users can override lfs_util.h with their own configuration by defining
|
||||
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
|
||||
//
|
||||
// If LFS_CONFIG is used, none of the default utils will be emitted and must be
|
||||
// provided by the config file. To start, I would suggest copying lfs_util.h
|
||||
// and modifying as needed.
|
||||
#ifdef LFS_CONFIG
|
||||
#include LFS_STRINGIZE(LFS_CONFIG)
|
||||
#else
|
||||
|
||||
// Alternatively, users can provide a header file which defines
|
||||
// macros and other things consumed by littlefs.
|
||||
//
|
||||
// For example, provide my_defines.h, which contains
|
||||
// something like:
|
||||
//
|
||||
// #include <stddef.h>
|
||||
// extern void *my_malloc(size_t sz);
|
||||
// #define LFS_MALLOC(sz) my_malloc(sz)
|
||||
//
|
||||
// And build littlefs with the header by defining LFS_DEFINES.
|
||||
// (-DLFS_DEFINES=my_defines.h)
|
||||
|
||||
#ifdef LFS_DEFINES
|
||||
#include LFS_STRINGIZE(LFS_DEFINES)
|
||||
#endif
|
||||
|
||||
// System includes
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifndef LFS_NO_MALLOC
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#if !defined(LFS_NO_DEBUG) || \
|
||||
!defined(LFS_NO_WARN) || \
|
||||
!defined(LFS_NO_ERROR) || \
|
||||
defined(LFS_YES_TRACE)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
// Macros, may be replaced by system specific wrappers. Arguments to these
|
||||
// macros must not have side-effects as the macros can be removed for a smaller
|
||||
// code footprint
|
||||
|
||||
// Logging functions
|
||||
#ifndef LFS_TRACE
|
||||
#ifdef LFS_YES_TRACE
|
||||
#define LFS_TRACE_(fmt, ...) \
|
||||
printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
|
||||
#else
|
||||
#define LFS_TRACE(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LFS_DEBUG
|
||||
#ifndef LFS_NO_DEBUG
|
||||
#define LFS_DEBUG_(fmt, ...) \
|
||||
printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "")
|
||||
#else
|
||||
#define LFS_DEBUG(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LFS_WARN
|
||||
#ifndef LFS_NO_WARN
|
||||
#define LFS_WARN_(fmt, ...) \
|
||||
printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "")
|
||||
#else
|
||||
#define LFS_WARN(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LFS_ERROR
|
||||
#ifndef LFS_NO_ERROR
|
||||
#define LFS_ERROR_(fmt, ...) \
|
||||
printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "")
|
||||
#else
|
||||
#define LFS_ERROR(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Runtime assertions
|
||||
#ifndef LFS_ASSERT
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#define LFS_ASSERT(test) assert(test)
|
||||
#else
|
||||
#define LFS_ASSERT(test)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// Builtin functions, these may be replaced by more efficient
|
||||
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
|
||||
// expensive basic C implementation for debugging purposes
|
||||
|
||||
// Min/max functions for unsigned 32-bit numbers
|
||||
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
// Align to nearest multiple of a size
|
||||
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
|
||||
return a - (a % alignment);
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
|
||||
return lfs_aligndown(a + alignment-1, alignment);
|
||||
}
|
||||
|
||||
// Find the smallest power of 2 greater than or equal to a
|
||||
static inline uint32_t lfs_npw2(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
||||
return 32 - __builtin_clz(a-1);
|
||||
#else
|
||||
uint32_t r = 0;
|
||||
uint32_t s;
|
||||
a -= 1;
|
||||
s = (a > 0xffff) << 4; a >>= s; r |= s;
|
||||
s = (a > 0xff ) << 3; a >>= s; r |= s;
|
||||
s = (a > 0xf ) << 2; a >>= s; r |= s;
|
||||
s = (a > 0x3 ) << 1; a >>= s; r |= s;
|
||||
return (r | (a >> 1)) + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Count the number of trailing binary zeros in a
|
||||
// lfs_ctz(0) may be undefined
|
||||
static inline uint32_t lfs_ctz(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
|
||||
return __builtin_ctz(a);
|
||||
#else
|
||||
return lfs_npw2((a & -a) + 1) - 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Count the number of binary ones in a
|
||||
static inline uint32_t lfs_popc(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
||||
return __builtin_popcount(a);
|
||||
#else
|
||||
a = a - ((a >> 1) & 0x55555555);
|
||||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
||||
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Find the sequence comparison of a and b, this is the distance
|
||||
// between a and b ignoring overflow
|
||||
static inline int lfs_scmp(uint32_t a, uint32_t b) {
|
||||
return (int)(unsigned)(a - b);
|
||||
}
|
||||
|
||||
// Convert between 32-bit little-endian and native order
|
||||
static inline uint32_t lfs_fromle32(uint32_t a) {
|
||||
#if (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
return a;
|
||||
#elif !defined(LFS_NO_INTRINSICS) && ( \
|
||||
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
return __builtin_bswap32(a);
|
||||
#else
|
||||
return (((uint8_t*)&a)[0] << 0) |
|
||||
(((uint8_t*)&a)[1] << 8) |
|
||||
(((uint8_t*)&a)[2] << 16) |
|
||||
(((uint8_t*)&a)[3] << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_tole32(uint32_t a) {
|
||||
return lfs_fromle32(a);
|
||||
}
|
||||
|
||||
// Convert between 32-bit big-endian and native order
|
||||
static inline uint32_t lfs_frombe32(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && ( \
|
||||
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
||||
return __builtin_bswap32(a);
|
||||
#elif (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
return a;
|
||||
#else
|
||||
return (((uint8_t*)&a)[0] << 24) |
|
||||
(((uint8_t*)&a)[1] << 16) |
|
||||
(((uint8_t*)&a)[2] << 8) |
|
||||
(((uint8_t*)&a)[3] << 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_tobe32(uint32_t a) {
|
||||
return lfs_frombe32(a);
|
||||
}
|
||||
|
||||
// Calculate CRC-32 with polynomial = 0x04c11db7
|
||||
#ifdef LFS_CRC
|
||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
||||
return LFS_CRC(crc, buffer, size)
|
||||
}
|
||||
#else
|
||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
|
||||
#endif
|
||||
|
||||
// Allocate memory, only used if buffers are not provided to littlefs
|
||||
//
|
||||
// littlefs current has no alignment requirements, as it only allocates
|
||||
// byte-level buffers.
|
||||
static inline void *lfs_malloc(size_t size) {
|
||||
#if defined(LFS_MALLOC)
|
||||
return LFS_MALLOC(size);
|
||||
#elif !defined(LFS_NO_MALLOC)
|
||||
return malloc(size);
|
||||
#else
|
||||
(void)size;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Deallocate memory, only used if buffers are not provided to littlefs
|
||||
static inline void lfs_free(void *p) {
|
||||
#if defined(LFS_FREE)
|
||||
LFS_FREE(p);
|
||||
#elif !defined(LFS_NO_MALLOC)
|
||||
if (!p) {
|
||||
free(p);
|
||||
}
|
||||
#else
|
||||
(void)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
10
lib_littlefs/lib_littlefs/lib_build_info.cmake
Normal file
10
lib_littlefs/lib_littlefs/lib_build_info.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
set(LIB_NAME lib_littlefs)
|
||||
set(LIB_VERSION 0.0.1)
|
||||
set(LIB_INCLUDES api)
|
||||
set(LIB_DEPENDENT_MODULES "lib_logging(3.2.0)")
|
||||
|
||||
set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm)
|
||||
list(APPEND LIB_COMPILER_FLAGS -DXASSERT_ENABLE_ASSERTIONS=0
|
||||
-DXASSERT_ENABLE_DEBUG=0
|
||||
-DXASSERT_ENABLE_LINE_NUMBERS=0)
|
||||
XMOS_REGISTER_MODULE()
|
||||
6555
lib_littlefs/lib_littlefs/src/lfs.c
Normal file
6555
lib_littlefs/lib_littlefs/src/lfs.c
Normal file
File diff suppressed because it is too large
Load Diff
37
lib_littlefs/lib_littlefs/src/lfs_util.c
Normal file
37
lib_littlefs/lib_littlefs/src/lfs_util.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* lfs util functions
|
||||
*
|
||||
* Copyright (c) 2022, The littlefs authors.
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include "lfs_util.h"
|
||||
|
||||
// Only compile if user does not provide custom config
|
||||
#ifndef LFS_CONFIG
|
||||
|
||||
|
||||
// If user provides their own CRC impl we don't need this
|
||||
#ifndef LFS_CRC
|
||||
// Software CRC implementation with small lookup table
|
||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
||||
static const uint32_t rtable[16] = {
|
||||
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
|
||||
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
|
||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
|
||||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
|
||||
};
|
||||
|
||||
const uint8_t *data = buffer;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];
|
||||
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
30
lib_qspi_flash/lib_qspi_flash/CMakeLists.txt
Normal file
30
lib_qspi_flash/lib_qspi_flash/CMakeLists.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
if((${CMAKE_SYSTEM_NAME} STREQUAL XCORE_XS3A) OR (${CMAKE_SYSTEM_NAME} STREQUAL XCORE_XS2A))
|
||||
## Create library target
|
||||
add_library(framework_rtos_drivers_qspi_io INTERFACE)
|
||||
target_sources(framework_rtos_drivers_qspi_io
|
||||
INTERFACE
|
||||
src/rtos_qspi_flash.c
|
||||
src/rtos_qspi_flash_rpc.c
|
||||
)
|
||||
target_include_directories(framework_rtos_drivers_qspi_io
|
||||
INTERFACE
|
||||
api
|
||||
)
|
||||
target_link_libraries(framework_rtos_drivers_qspi_io
|
||||
INTERFACE
|
||||
lib_qspi_fast_read
|
||||
rtos::osal
|
||||
)
|
||||
target_compile_options(framework_rtos_drivers_qspi_io
|
||||
INTERFACE
|
||||
-lquadflash
|
||||
)
|
||||
target_link_options(framework_rtos_drivers_qspi_io
|
||||
INTERFACE
|
||||
-lquadflash
|
||||
)
|
||||
|
||||
## Create an alias
|
||||
add_library(rtos::drivers::qspi_io ALIAS framework_rtos_drivers_qspi_io)
|
||||
endif()
|
||||
394
lib_qspi_flash/lib_qspi_flash/api/rtos_qspi_flash.h
Normal file
394
lib_qspi_flash/lib_qspi_flash/api/rtos_qspi_flash.h
Normal file
@@ -0,0 +1,394 @@
|
||||
// Copyright 2020-2023 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
#ifndef RTOS_QSPI_FLASH_H_
|
||||
#define RTOS_QSPI_FLASH_H_
|
||||
|
||||
/**
|
||||
* \addtogroup rtos_qspi_flash_driver rtos_qspi_flash_driver
|
||||
*
|
||||
* The public API for using the RTOS QSPI flash driver.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <quadflash.h>
|
||||
#include <quadflashlib.h>
|
||||
#include "xs1.h"
|
||||
#include "xcore/clock.h"
|
||||
#include "xcore/port.h"
|
||||
|
||||
|
||||
#define RTOS_QSPI_FLASH_READ_CHUNK_SIZE (24*1024)
|
||||
|
||||
/**
|
||||
* Typedef to the RTOS QSPI flash driver instance struct.
|
||||
*/
|
||||
typedef struct rtos_qspi_flash_struct rtos_qspi_flash_t;
|
||||
|
||||
/**
|
||||
* Struct representing an RTOS QSPI flash driver instance.
|
||||
*
|
||||
* The members in this struct should not be accessed directly.
|
||||
*/
|
||||
struct rtos_qspi_flash_struct {
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_read_fptr_grp")))
|
||||
void (*read)(rtos_qspi_flash_t *, uint8_t *, unsigned, size_t);
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_read_mode_fptr_grp")))
|
||||
void (*read_mode)(rtos_qspi_flash_t *, uint8_t *, unsigned, size_t, qspi_fast_flash_read_transfer_mode_t);
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_write_fptr_grp")))
|
||||
void (*write)(rtos_qspi_flash_t *, const uint8_t *, unsigned, size_t);
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_erase_fptr_grp")))
|
||||
void (*erase)(rtos_qspi_flash_t *, unsigned, size_t);
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_lock_fptr_grp")))
|
||||
void (*lock)(rtos_qspi_flash_t *);
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_unlock_fptr_grp")))
|
||||
void (*unlock)(rtos_qspi_flash_t *);
|
||||
|
||||
fl_QSPIPorts qspi_ports;
|
||||
fl_QuadDeviceSpec qspi_spec;
|
||||
size_t flash_size;
|
||||
unsigned calibration_valid;
|
||||
unsigned last_op;
|
||||
|
||||
volatile int spinlock;
|
||||
volatile int ll_req_flag;
|
||||
};
|
||||
|
||||
/**
|
||||
* \addtogroup rtos_qspi_flash_driver_core rtos_qspi_flash_driver_core
|
||||
*
|
||||
* The core functions for using an RTOS QSPI flash driver instance after
|
||||
* it has been initialized and started. These functions may be used
|
||||
* by both the host and any client tiles that RPC has been enabled for.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains a lock for exclusive access to the QSPI flash. This allows
|
||||
* a thread to perform a sequence of operations (such as read, modify, erase,
|
||||
* write) without the risk of another thread issuing a command in the middle of
|
||||
* the sequence and corrupting the data in the flash.
|
||||
*
|
||||
* If only a single atomic operation needs to be performed, such as a read, it
|
||||
* is not necessary to call this to obtain the lock first. Each individual operation
|
||||
* obtains and releases the lock automatically so that they cannot run while another
|
||||
* thread has the lock.
|
||||
*
|
||||
* The lock MUST be released when it is no longer needed by calling
|
||||
* rtos_qspi_flash_unlock().
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to lock.
|
||||
*/
|
||||
inline void rtos_qspi_flash_lock(
|
||||
rtos_qspi_flash_t *ctx)
|
||||
{
|
||||
ctx->lock(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases a lock for exclusive access to the QSPI flash. The lock
|
||||
* must have already been obtained by calling rtos_qspi_flash_lock().
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to unlock.
|
||||
*/
|
||||
inline void rtos_qspi_flash_unlock(
|
||||
rtos_qspi_flash_t *ctx)
|
||||
{
|
||||
ctx->unlock(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* This reads data from the flash in quad I/O mode. All four lines are
|
||||
* used to send the address and to read the data.
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to use.
|
||||
* \param data Pointer to the buffer to save the read data to.
|
||||
* \param address The byte address in the flash to begin reading at.
|
||||
* Only bits 23:0 contain the address. Bits 31:24 are
|
||||
* ignored.
|
||||
* \param len The number of bytes to read and save to \p data.
|
||||
*/
|
||||
inline void rtos_qspi_flash_read(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
uint8_t *data,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
ctx->read(ctx, data, address, len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is a lower level version of rtos_qspi_flash_read() that is safe
|
||||
* to call from within ISRs. If a task currently own the flash lock, or
|
||||
* if another core is actively doing a read with this function, then the
|
||||
* read will not be performed and an error returned. It is up to the
|
||||
* application to determine what it should do in this situation and to
|
||||
* avoid a potential deadlock.
|
||||
*
|
||||
* This function may only be called on the same tile as the underlying
|
||||
* peripheral.
|
||||
*
|
||||
* This function uses the lib_quadflash API to perform the read. It is up
|
||||
* to the application to ensure that XCORE resources are properly configured.
|
||||
*
|
||||
* \note It is not possible to call this from a task that currently owns
|
||||
* the flash lock taken with rtos_qspi_flash_lock(). In general it is not
|
||||
* advisable to call this from an RTOS task unless the small amount of
|
||||
* overhead time that is introduced by rtos_qspi_flash_read() is unacceptable.
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to use.
|
||||
* \param data Pointer to the buffer to save the read data to.
|
||||
* \param address The byte address in the flash to begin reading at.
|
||||
* Only bits 23:0 contain the address. Bits 31:24 are
|
||||
* ignored.
|
||||
* \param len The number of bytes to read and save to \p data.
|
||||
*
|
||||
* \retval 0 if the flash was available and the read operation was performed.
|
||||
* \retval -1 if the flash was unavailable and the read could not be performed.
|
||||
*/
|
||||
int rtos_qspi_flash_read_ll(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
uint8_t *data,
|
||||
unsigned address,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* This writes data to the QSPI flash. The standard page program command
|
||||
* is sent and only SIO0 (MOSI) is used to send the address and data.
|
||||
*
|
||||
* The driver handles sending the write enable command, as well as waiting for
|
||||
* the write to complete.
|
||||
*
|
||||
* This function may return before the write operation is complete, as the actual
|
||||
* write operation is queued and executed by a thread created by the driver.
|
||||
*
|
||||
* \note this function does NOT erase the flash first. Erase operations must be
|
||||
* explicitly requested by the application.
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to use.
|
||||
* \param data Pointer to the data to write to the flash.
|
||||
* \param address The byte address in the flash to begin writing at.
|
||||
* Only bits 23:0 contain the address. The byte in bits 31:24 is
|
||||
* not sent.
|
||||
* \param len The number of bytes to write to the flash.
|
||||
*/
|
||||
inline void rtos_qspi_flash_write(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
const uint8_t *data,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
ctx->write(ctx, data, address, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* This erases data from the QSPI flash. If the address range to erase
|
||||
* spans multiple sectors, then all of these sectors will be erased by issuing
|
||||
* multiple erase commands.
|
||||
*
|
||||
* The driver handles sending the write enable command, as well as waiting for
|
||||
* the write to complete.
|
||||
*
|
||||
* This function may return before the write operation is complete, as the actual
|
||||
* erase operation is queued and executed by a thread created by the driver.
|
||||
*
|
||||
* \note The smallest amount of data that can be erased is a 4k sector.
|
||||
* This means that data outside the address range specified by \p address
|
||||
* and \p len will be erased if the address range does not both begin and
|
||||
* end at 4k sector boundaries.
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to use.
|
||||
* \param address The byte address to begin erasing. This does not need to begin
|
||||
* at a sector boundary, but if it does not, note that the entire
|
||||
* sector that contains this address will still be erased.
|
||||
* \param len The minimum number of bytes to erase. If \p address + \p len - 1
|
||||
* does not correspond to the last address within a sector, note that
|
||||
* the entire sector that contains this address will still be erased.
|
||||
*/
|
||||
inline void rtos_qspi_flash_erase(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
ctx->erase(ctx, address, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets the size in bytes of the flash chip.
|
||||
*
|
||||
* \param A pointer to the QSPI flash driver instance to query.
|
||||
*
|
||||
* \returns the size in bytes of the flash chip.
|
||||
*/
|
||||
inline size_t rtos_qspi_flash_size_get(
|
||||
rtos_qspi_flash_t *qspi_flash_ctx)
|
||||
{
|
||||
return qspi_flash_ctx->flash_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets the size in bytes of each page in the flash chip.
|
||||
*
|
||||
* \param A pointer to the QSPI flash driver instance to query.
|
||||
*
|
||||
* \returns the size in bytes of the flash page.
|
||||
*/
|
||||
inline size_t rtos_qspi_flash_page_size_get(
|
||||
rtos_qspi_flash_t *qspi_flash_ctx)
|
||||
{
|
||||
return qspi_flash_ctx->qspi_spec.pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets the number of pages in the flash chip.
|
||||
*
|
||||
* \param A pointer to the QSPI flash driver instance to query.
|
||||
*
|
||||
* \returns the number of pages in the flash chip.
|
||||
*/
|
||||
inline size_t rtos_qspi_flash_page_count_get(
|
||||
rtos_qspi_flash_t *qspi_flash_ctx)
|
||||
{
|
||||
return qspi_flash_ctx->qspi_spec.numPages;
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets the sector size of the flash chip
|
||||
*
|
||||
* \param A pointer to the QSPI flash driver instance to query.
|
||||
*
|
||||
* \returns the size in bytes of the smallest sector
|
||||
*/
|
||||
inline size_t rtos_qspi_flash_sector_size_get(
|
||||
rtos_qspi_flash_t *qspi_flash_ctx)
|
||||
{
|
||||
return qspi_flash_ctx->qspi_spec.sectorEraseSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the calibration valid.
|
||||
*
|
||||
* \param A pointer to the QSPI flash driver instance to query.
|
||||
*
|
||||
* \returns 1 if calibration was successful
|
||||
* 0 otherwise
|
||||
*/
|
||||
inline unsigned rtos_qspi_flash_calibration_valid_get(
|
||||
rtos_qspi_flash_t *qspi_flash_ctx)
|
||||
{
|
||||
return qspi_flash_ctx->calibration_valid;
|
||||
}
|
||||
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* Starts an RTOS QSPI flash driver instance. This must only be called by the tile that
|
||||
* owns the driver instance. It may be called either before or after starting
|
||||
* the RTOS, but must be called before any of the core QSPI flash driver functions are
|
||||
* called with this instance.
|
||||
*
|
||||
* rtos_qspi_flash_init() must be called on this QSPI flash driver instance prior to calling this.
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to start.
|
||||
* \param priority The priority of the task that gets created by the driver to
|
||||
* handle the QSPI flash interface.
|
||||
*/
|
||||
void rtos_qspi_flash_start(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
unsigned priority);
|
||||
|
||||
/**
|
||||
* Sets the core affinity for a RTOS QSPI flash driver instance.
|
||||
* This must only be called by the tile that owns the driver instance.
|
||||
* It may be called either before or after starting the RTOS, and should
|
||||
* be called before any of the core QSPI flash driver functions are
|
||||
* called with this instance.
|
||||
*
|
||||
* Since interrupts are disabled during the QSPI transaction on the op thread, a
|
||||
* core mask is provided to allow users to avoid collisions with application ISRs.
|
||||
*
|
||||
* rtos_qspi_flash_start() must be called on this QSPI flash driver instance prior to calling this.
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to start.
|
||||
* \param op_core_mask A bitmask representing the cores on which the QSPI I/O thread
|
||||
* created by the driver is allowed to run. Bit 0 is core 0, bit 1 is core 1,
|
||||
* etc.
|
||||
*/
|
||||
void rtos_qspi_flash_op_core_affinity_set(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
uint32_t op_core_mask);
|
||||
|
||||
/**
|
||||
* Initializes an RTOS QSPI flash driver instance.
|
||||
* This must only be called by the tile that owns the driver instance. It may be
|
||||
* called either before or after starting the RTOS, but must be called before calling
|
||||
* rtos_qspi_flash_start() or any of the core QSPI flash driver functions with this instance.
|
||||
*
|
||||
* This function will initialize a flash driver using lib_quadflash for
|
||||
* all operations.
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to initialize.
|
||||
* \param clock_block The clock block to use for the qspi_io interface.
|
||||
* \param cs_port The chip select port. MUST be a 1-bit port.
|
||||
* \param sclk_port The SCLK port. MUST be a 1-bit port.
|
||||
* \param sio_port The SIO port. MUST be a 4-bit port.
|
||||
* \param spec A pointer to the flash part specification.
|
||||
* This may be set to NULL to use the XTC default
|
||||
*/
|
||||
void rtos_qspi_flash_init(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
xclock_t clock_block,
|
||||
port_t cs_port,
|
||||
port_t sclk_port,
|
||||
port_t sio_port,
|
||||
fl_QuadDeviceSpec *spec);
|
||||
|
||||
/**
|
||||
* Initializes an RTOS QSPI flash driver instance.
|
||||
* This must only be called by the tile that owns the driver instance. It may be
|
||||
* called either before or after starting the RTOS, but must be called before calling
|
||||
* rtos_qspi_flash_start() or any of the core QSPI flash driver functions with this instance.
|
||||
*
|
||||
* This function will initialize a flash driver using lib_quadflash for
|
||||
* erase and writes, and lib_qspi_fast_read for reads. If calibration
|
||||
* fails the driver will enable lib_quadflash for reads and allow the
|
||||
* application to decide what to do about the failed calibration. The
|
||||
* status of the calibration can be checked at runtime by calling
|
||||
* rtos_qspi_flash_calibration_valid_get().
|
||||
*
|
||||
* \param ctx A pointer to the QSPI flash driver instance to initialize.
|
||||
* \param clock_block The clock block to use for the qspi_io interface.
|
||||
* \param cs_port The chip select port. MUST be a 1-bit port.
|
||||
* \param sclk_port The SCLK port. MUST be a 1-bit port.
|
||||
* \param sio_port The SIO port. MUST be a 4-bit port.
|
||||
* \param spec A pointer to the flash part specification.
|
||||
* This may be set to NULL to use the XTC default
|
||||
* \param read_mode The transfer mode to use for port reads.
|
||||
* Invalid values will default to qspi_fast_flash_read_transfer_raw
|
||||
* \param read_divide The divisor to use for QSPI SCLK.
|
||||
* \param calibration_pattern_addr The address of the default calibration pattern.
|
||||
* This driver requires the default calibration pattern
|
||||
* supplied with lib_qspi_fast_read and does not support
|
||||
* custom patterns.
|
||||
*/
|
||||
void rtos_qspi_flash_fast_read_init(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
xclock_t clock_block,
|
||||
port_t cs_port,
|
||||
port_t sclk_port,
|
||||
port_t sio_port,
|
||||
fl_QuadDeviceSpec *spec,
|
||||
uint8_t read_divide,
|
||||
uint32_t calibration_pattern_addr);
|
||||
|
||||
/**@}*/
|
||||
|
||||
#endif /* RTOS_QSPI_FLASH_H_ */
|
||||
10
lib_qspi_flash/lib_qspi_flash/lib_build_info.cmake
Normal file
10
lib_qspi_flash/lib_qspi_flash/lib_build_info.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
set(LIB_NAME lib_qspi_flash)
|
||||
set(LIB_VERSION 0.0.1)
|
||||
set(LIB_INCLUDES api)
|
||||
set(LIB_DEPENDENT_MODULES "lib_logging(3.2.0)")
|
||||
|
||||
set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm)
|
||||
list(APPEND LIB_COMPILER_FLAGS -DXASSERT_ENABLE_ASSERTIONS=0
|
||||
-DXASSERT_ENABLE_DEBUG=0
|
||||
-DXASSERT_ENABLE_LINE_NUMBERS=0)
|
||||
XMOS_REGISTER_MODULE()
|
||||
399
lib_qspi_flash/lib_qspi_flash/src/rtos_qspi_flash.c
Normal file
399
lib_qspi_flash/lib_qspi_flash/src/rtos_qspi_flash.c
Normal file
@@ -0,0 +1,399 @@
|
||||
// Copyright 2020-2023 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
#define DEBUG_UNIT RTOS_QSPI_FLASH
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <xcore/assert.h>
|
||||
#include <xcore/lock.h>
|
||||
|
||||
#include "rtos_qspi_flash.h"
|
||||
|
||||
#ifndef QSPI_FL_RETRY_DELAY_TICKS
|
||||
#define QSPI_FL_RETRY_DELAY_TICKS 1000
|
||||
#endif
|
||||
|
||||
#ifndef QSPI_FL_RETRY_ATTEMPT_CNT
|
||||
#define QSPI_FL_RETRY_ATTEMPT_CNT 5
|
||||
#endif
|
||||
|
||||
/* TODO, these will be removed once moved to the public API */
|
||||
#define ERASE_CHIP 0xC7
|
||||
|
||||
extern void fl_int_read(
|
||||
unsigned char cmd,
|
||||
unsigned int address,
|
||||
unsigned char * destination,
|
||||
unsigned int num_bytes);
|
||||
extern void fl_int_write(
|
||||
unsigned char cmd,
|
||||
unsigned int pageAddress,
|
||||
const unsigned char data[],
|
||||
unsigned int num_bytes);
|
||||
extern void fl_int_sendSingleByteCommand(unsigned char cmd);
|
||||
extern void fl_int_eraseSector(
|
||||
unsigned char cmd,
|
||||
unsigned int sectorAddress);
|
||||
/* end TODO */
|
||||
|
||||
/* Library only supports 4096 sector size*/
|
||||
#define QSPI_ERASE_TYPE_SIZE_LOG2 12
|
||||
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define FLASH_OP_NONE 0
|
||||
#define FLASH_OP_READ 1
|
||||
#define FLASH_OP_WRITE 2
|
||||
#define FLASH_OP_ERASE 3
|
||||
#define FLASH_OP_READ_FAST_RAW 4
|
||||
#define FLASH_OP_READ_FAST_NIBBLE_SWAP 5
|
||||
#define FLASH_OP_LL_SETUP 6
|
||||
|
||||
extern unsigned __libc_hwlock;
|
||||
|
||||
typedef struct {
|
||||
int op;
|
||||
uint8_t *data;
|
||||
unsigned address;
|
||||
size_t len;
|
||||
unsigned priority;
|
||||
} qspi_flash_op_req_t;
|
||||
|
||||
/*
|
||||
* Returns true if the spinlock is
|
||||
* acquired, false if not available.
|
||||
* NOT recursive.
|
||||
*/
|
||||
static bool spinlock_get(volatile int *lock)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
lock_acquire(__libc_hwlock);
|
||||
{
|
||||
if (*lock == 0) {
|
||||
*lock = 1;
|
||||
ret = true;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
lock_release(__libc_hwlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Releases the lock. It MUST be owned
|
||||
* by the caller.
|
||||
*/
|
||||
static void spinlock_release(volatile int *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static void rtos_qspi_flash_fl_connect_with_retry(
|
||||
rtos_qspi_flash_t *ctx)
|
||||
{
|
||||
if (fl_connectToDevice(&ctx->qspi_ports, &ctx->qspi_spec, 1) == 0) {
|
||||
return;
|
||||
} else {
|
||||
int cnt = 0;
|
||||
while (fl_connectToDevice(&ctx->qspi_ports, &ctx->qspi_spec, 1) != 0) {
|
||||
delay_ticks(QSPI_FL_RETRY_DELAY_TICKS);
|
||||
if (cnt++ >= QSPI_FL_RETRY_ATTEMPT_CNT) {
|
||||
xassert(0); /* fl_connectToDevice failed too many times */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rtos_qspi_flash_read_ll(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
uint8_t *data,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
uint32_t irq_mask;
|
||||
bool lock_acquired;
|
||||
|
||||
// printf("Asked to ll read %d bytes at address 0x%08x\n", len, address);
|
||||
|
||||
lock_acquired = spinlock_get(&ctx->spinlock);
|
||||
|
||||
while (lock_acquired && len > 0) {
|
||||
|
||||
size_t read_len = MIN(len, RTOS_QSPI_FLASH_READ_CHUNK_SIZE);
|
||||
|
||||
/*
|
||||
* Cap the address at the size of the flash.
|
||||
* This ensures the correction below will work if
|
||||
* address is outside the flash's address space.
|
||||
*/
|
||||
if (address >= ctx->flash_size) {
|
||||
address = ctx->flash_size;
|
||||
}
|
||||
|
||||
if (address + read_len > ctx->flash_size) {
|
||||
int original_len = read_len;
|
||||
|
||||
/* Don't read past the end of the flash */
|
||||
read_len = ctx->flash_size - address;
|
||||
|
||||
/* Return all 0xFF bytes for addresses beyond the end of the flash */
|
||||
memset(&data[read_len], 0xFF, original_len - read_len);
|
||||
}
|
||||
|
||||
// printf("Read %d bytes from flash at address 0x%x\n", read_len, address);
|
||||
|
||||
fl_int_read(ctx->qspi_spec.readCommand, address, data, read_len);
|
||||
|
||||
len -= read_len;
|
||||
data += read_len;
|
||||
address += read_len;
|
||||
}
|
||||
|
||||
if (lock_acquired) {
|
||||
spinlock_release(&ctx->spinlock);
|
||||
}
|
||||
|
||||
return lock_acquired ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
static void read_op(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
uint8_t *data,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
// printf("Asked to read %d bytes at address 0x%08x\n", len, address);
|
||||
|
||||
while (len > 0) {
|
||||
|
||||
size_t read_len = MIN(len, RTOS_QSPI_FLASH_READ_CHUNK_SIZE);
|
||||
|
||||
/*
|
||||
* Cap the address at the size of the flash.
|
||||
* This ensures the correction below will work if
|
||||
* address is outside the flash's address space.
|
||||
*/
|
||||
if (address >= ctx->flash_size) {
|
||||
address = ctx->flash_size;
|
||||
}
|
||||
|
||||
if (address + read_len > ctx->flash_size) {
|
||||
int original_len = read_len;
|
||||
|
||||
/* Don't read past the end of the flash */
|
||||
read_len = ctx->flash_size - address;
|
||||
|
||||
/* Return all 0xFF bytes for addresses beyond the end of the flash */
|
||||
memset(&data[read_len], 0xFF, original_len - read_len);
|
||||
}
|
||||
|
||||
// printf("Read %d bytes from flash at address 0x%x\n", read_len, address);
|
||||
|
||||
fl_int_read(ctx->qspi_spec.readCommand, address, data, read_len);
|
||||
|
||||
len -= read_len;
|
||||
data += read_len;
|
||||
address += read_len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void while_busy(void)
|
||||
{
|
||||
bool busy;
|
||||
|
||||
do {
|
||||
busy = fl_getBusyStatus();
|
||||
} while (busy);
|
||||
}
|
||||
|
||||
static void write_op(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
const uint8_t *data,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
size_t bytes_left_to_write = len;
|
||||
unsigned address_to_write = address;
|
||||
const uint8_t *write_buf = data;
|
||||
|
||||
// printf("Asked to write %d bytes at address 0x%08x\n", bytes_left_to_write, address_to_write);
|
||||
|
||||
while (bytes_left_to_write > 0) {
|
||||
/* compute the maximum number of bytes that can be written to the current page. */
|
||||
size_t max_bytes_to_write = fl_getPageSize() - (address_to_write & (fl_getPageSize() - 1));
|
||||
size_t bytes_to_write = bytes_left_to_write <= max_bytes_to_write ? bytes_left_to_write : max_bytes_to_write;
|
||||
|
||||
if (address_to_write >= ctx->flash_size) {
|
||||
break; /* do not write past the end of the flash */
|
||||
}
|
||||
|
||||
// printf("Write %d bytes from flash at address 0x%x\n", bytes_to_write, address_to_write);
|
||||
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeEnableCommand);
|
||||
fl_int_write(ctx->qspi_spec.programPageCommand, address_to_write, write_buf, bytes_to_write);
|
||||
while_busy();
|
||||
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeDisableCommand);
|
||||
|
||||
bytes_left_to_write -= bytes_to_write;
|
||||
write_buf += bytes_to_write;
|
||||
address_to_write += bytes_to_write;
|
||||
}
|
||||
}
|
||||
|
||||
#define SECTORS_TO_BYTES(s, ss_log2) ((s) << (ss_log2))
|
||||
#define BYTES_TO_SECTORS(b, ss_log2) (((b) + (1 << ss_log2) - 1) >> (ss_log2))
|
||||
|
||||
#define SECTOR_TO_BYTE_ADDRESS(s, ss_log2) SECTORS_TO_BYTES(s, ss_log2)
|
||||
#define BYTE_TO_SECTOR_ADDRESS(b, ss_log2) ((b) >> (ss_log2))
|
||||
|
||||
static void erase_op(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
size_t bytes_left_to_erase = len;
|
||||
unsigned address_to_erase = address;
|
||||
|
||||
// printf("Asked to erase %d bytes at address 0x%08x\n", bytes_left_to_erase, address_to_erase);
|
||||
|
||||
if (address_to_erase == 0 && bytes_left_to_erase >= ctx->flash_size) {
|
||||
/* Use chip erase when being asked to erase the entire address range */
|
||||
// printf("Erasing entire chip\n");
|
||||
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeEnableCommand);
|
||||
fl_int_sendSingleByteCommand(ERASE_CHIP);
|
||||
while_busy();
|
||||
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeDisableCommand);
|
||||
} else {
|
||||
if (SECTOR_TO_BYTE_ADDRESS(BYTE_TO_SECTOR_ADDRESS(address_to_erase, QSPI_ERASE_TYPE_SIZE_LOG2), QSPI_ERASE_TYPE_SIZE_LOG2) != address_to_erase) {
|
||||
/*
|
||||
* If the provided starting erase address does not begin on the smallest
|
||||
* sector boundary, then update the starting address and number of bytes
|
||||
* to erase so that it does.
|
||||
*/
|
||||
unsigned sector_address;
|
||||
sector_address = BYTE_TO_SECTOR_ADDRESS(address_to_erase, QSPI_ERASE_TYPE_SIZE_LOG2);
|
||||
bytes_left_to_erase += address_to_erase - SECTOR_TO_BYTE_ADDRESS(sector_address, QSPI_ERASE_TYPE_SIZE_LOG2);
|
||||
address_to_erase = SECTOR_TO_BYTE_ADDRESS(sector_address, QSPI_ERASE_TYPE_SIZE_LOG2);
|
||||
// printf("adjusted starting erase address to %d\n", address_to_erase);
|
||||
}
|
||||
|
||||
while (bytes_left_to_erase > 0) {
|
||||
int erase_length;
|
||||
int erase_length_log2 = QSPI_ERASE_TYPE_SIZE_LOG2;
|
||||
|
||||
if (address_to_erase >= ctx->flash_size) {
|
||||
break; /* do not erase past the end of the flash */
|
||||
}
|
||||
|
||||
erase_length = 1 << erase_length_log2;
|
||||
|
||||
xassert(address_to_erase == SECTOR_TO_BYTE_ADDRESS(BYTE_TO_SECTOR_ADDRESS(address_to_erase, erase_length_log2), erase_length_log2));
|
||||
|
||||
// printf("Erasing %d bytes (%d) at byte address %d, sector %d\n", erase_length, bytes_left_to_erase, address_to_erase, BYTE_TO_SECTOR_ADDRESS(address_to_erase, erase_length_log2));
|
||||
|
||||
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeEnableCommand);
|
||||
fl_int_eraseSector(ctx->qspi_spec.sectorEraseCommand, address_to_erase);
|
||||
while_busy();
|
||||
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeDisableCommand);
|
||||
|
||||
address_to_erase += erase_length;
|
||||
bytes_left_to_erase -= erase_length < bytes_left_to_erase ? erase_length : bytes_left_to_erase;
|
||||
}
|
||||
}
|
||||
|
||||
// printf("Erasing complete\n");
|
||||
}
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_read_fptr_grp")))
|
||||
static void qspi_flash_local_read(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
uint8_t *data,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
qspi_flash_op_req_t op = {
|
||||
.op = FLASH_OP_READ,
|
||||
.data = data,
|
||||
.address = address,
|
||||
.len = len
|
||||
};
|
||||
read_op(ctx, op.data, op.address, op.len);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_write_fptr_grp")))
|
||||
static void qspi_flash_local_write(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
const uint8_t *data,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
qspi_flash_op_req_t op = {
|
||||
.op = FLASH_OP_WRITE,
|
||||
.address = address,
|
||||
.len = len
|
||||
};
|
||||
|
||||
write_op(ctx, data, op.address, op.len);
|
||||
}
|
||||
|
||||
__attribute__((fptrgroup("rtos_qspi_flash_erase_fptr_grp")))
|
||||
static void qspi_flash_local_erase(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
unsigned address,
|
||||
size_t len)
|
||||
{
|
||||
qspi_flash_op_req_t op = {
|
||||
.op = FLASH_OP_ERASE,
|
||||
.address = address,
|
||||
.len = len
|
||||
};
|
||||
erase_op(ctx, op.address, op.len);
|
||||
}
|
||||
|
||||
void rtos_qspi_flash_init(
|
||||
rtos_qspi_flash_t *ctx,
|
||||
xclock_t clock_block,
|
||||
port_t cs_port,
|
||||
port_t sclk_port,
|
||||
port_t sio_port,
|
||||
fl_QuadDeviceSpec *spec)
|
||||
{
|
||||
ctx->qspi_ports.qspiCS = cs_port;
|
||||
ctx->qspi_ports.qspiSCLK = sclk_port;
|
||||
ctx->qspi_ports.qspiSIO = sio_port;
|
||||
ctx->qspi_ports.qspiClkblk = clock_block;
|
||||
|
||||
fl_QuadDeviceSpec default_spec = FL_QUADDEVICE_DEFAULT;
|
||||
|
||||
if (spec == NULL) {
|
||||
memcpy(&ctx->qspi_spec, &default_spec, sizeof(fl_QuadDeviceSpec));
|
||||
} else {
|
||||
memcpy(&ctx->qspi_spec, spec, sizeof(fl_QuadDeviceSpec));
|
||||
}
|
||||
|
||||
xassert(fl_connectToDevice(&ctx->qspi_ports, &ctx->qspi_spec, 1) == 0);
|
||||
/* Copy the spec back in case one was provided which has params populated by sfdp */
|
||||
xassert(fl_copySpec(&ctx->qspi_spec) == 0);
|
||||
|
||||
ctx->flash_size = fl_getFlashSize();
|
||||
|
||||
/* Driver currently only supports 4096 sector size */
|
||||
xassert(rtos_qspi_flash_sector_size_get(ctx) == (1 << QSPI_ERASE_TYPE_SIZE_LOG2));
|
||||
|
||||
/* Enable quad flash */
|
||||
xassert(fl_quadEnable() == 0);
|
||||
|
||||
ctx->calibration_valid = 0;
|
||||
ctx->last_op = FLASH_OP_NONE;
|
||||
ctx->read = qspi_flash_local_read;
|
||||
ctx->write = qspi_flash_local_write;
|
||||
ctx->erase = qspi_flash_local_erase;
|
||||
}
|
||||
Reference in New Issue
Block a user