mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Changed callbacks to take user-provided context directly
This is a style change to make littlefs's callbacks consistent with most callback declarations found in C. That is, taking in a user-provided `void*`. Previously, these callbacks took a pointer to the config struct itself, which indirectly contained a user provided context, and this gets the job done, but taking in a callback with a `void*` is arguably more expected, has a better chance of integrating with C++/OS-specific code, and is more likely to be optimized out by a clever compiler. --- As a part of these changes, the geometry for the test bds needed to be moved into bd specific configuration objects. This is a good change as it also allows for testing situations where littlefs's geometry does not match the underlying bd.
This commit is contained in:
		
							
								
								
									
										107
									
								
								bd/lfs_filebd.c
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								bd/lfs_filebd.c
									
									
									
									
									
								
							| @@ -10,21 +10,18 @@ | ||||
| #include <unistd.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| int lfs_filebd_createcfg(const struct lfs_cfg *cfg, const char *path, | ||||
|         const struct lfs_filebd_cfg *bdcfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
| int lfs_filebd_createcfg(lfs_filebd_t *bd, const char *path, | ||||
|         const struct lfs_filebd_cfg *cfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p, \"%s\", %p {" | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
|                 "\"%s\", " | ||||
|                 "%p {.erase_value=%"PRId32"})", | ||||
|             (void*)cfg, cfg->context, | ||||
|             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||
|             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||
|             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||
|             path, (void*)bdcfg, bdcfg->erase_value); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     bd->cfg = bdcfg; | ||||
|                 ".erase_size=%"PRIu32", .erase_count=%"PRIu32", " | ||||
|                 ".erase_value=%"PRId32"})", | ||||
|             (void*)bd, path, (void*)cfg, | ||||
|             cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count, | ||||
|             cfg->erase_value); | ||||
|  | ||||
|     // copy over config | ||||
|     bd->cfg = *cfg; | ||||
|  | ||||
|     // open file | ||||
|     bd->fd = open(path, O_RDWR | O_CREAT, 0666); | ||||
| @@ -38,26 +35,8 @@ int lfs_filebd_createcfg(const struct lfs_cfg *cfg, const char *path, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_create(const struct lfs_cfg *cfg, const char *path) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
|                 "\"%s\")", | ||||
|             (void*)cfg, cfg->context, | ||||
|             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||
|             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||
|             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||
|             path); | ||||
|     static const struct lfs_filebd_cfg defaults = {.erase_value=-1}; | ||||
|     int err = lfs_filebd_createcfg(cfg, path, &defaults); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_destroy(const struct lfs_cfg *cfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
| int lfs_filebd_destroy(lfs_filebd_t *bd) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)bd); | ||||
|     int err = close(bd->fd); | ||||
|     if (err < 0) { | ||||
|         err = -errno; | ||||
| @@ -68,26 +47,25 @@ int lfs_filebd_destroy(const struct lfs_cfg *cfg) { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_read(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_filebd_read(lfs_filebd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|             (void*)bd, block, off, buffer, size); | ||||
|  | ||||
|     // check if read is valid | ||||
|     LFS_ASSERT(off  % cfg->read_size == 0); | ||||
|     LFS_ASSERT(size % cfg->read_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(off  % bd->cfg.read_size == 0); | ||||
|     LFS_ASSERT(size % bd->cfg.read_size == 0); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // zero for reproducability (in case file is truncated) | ||||
|     if (bd->cfg->erase_value != -1) { | ||||
|         memset(buffer, bd->cfg->erase_value, size); | ||||
|     if (bd->cfg.erase_value != -1) { | ||||
|         memset(buffer, bd->cfg.erase_value, size); | ||||
|     } | ||||
|  | ||||
|     // read | ||||
|     off_t res1 = lseek(bd->fd, | ||||
|             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|             (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET); | ||||
|     if (res1 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); | ||||
| @@ -105,21 +83,21 @@ int lfs_filebd_read(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_filebd_prog(lfs_filebd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_prog(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)bd, block, off, buffer, size); | ||||
|  | ||||
|     // check if write is valid | ||||
|     LFS_ASSERT(off  % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(size % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(off  % bd->cfg.prog_size == 0); | ||||
|     LFS_ASSERT(size % bd->cfg.prog_size == 0); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // check that data was erased? only needed for testing | ||||
|     if (bd->cfg->erase_value != -1) { | ||||
|     if (bd->cfg.erase_value != -1) { | ||||
|         off_t res1 = lseek(bd->fd, | ||||
|                 (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|                 (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET); | ||||
|         if (res1 < 0) { | ||||
|             int err = -errno; | ||||
|             LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
| @@ -135,13 +113,13 @@ int lfs_filebd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             LFS_ASSERT(c == bd->cfg->erase_value); | ||||
|             LFS_ASSERT(c == bd->cfg.erase_value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // program data | ||||
|     off_t res1 = lseek(bd->fd, | ||||
|             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|             (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET); | ||||
|     if (res1 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
| @@ -159,24 +137,23 @@ int lfs_filebd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
| int lfs_filebd_erase(lfs_filebd_t *bd, lfs_block_t block) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)bd, block); | ||||
|  | ||||
|     // check if erase is valid | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // erase, only needed for testing | ||||
|     if (bd->cfg->erase_value != -1) { | ||||
|         off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET); | ||||
|     if (bd->cfg.erase_value != -1) { | ||||
|         off_t res1 = lseek(bd->fd, (off_t)block*bd->cfg.erase_size, SEEK_SET); | ||||
|         if (res1 < 0) { | ||||
|             int err = -errno; | ||||
|             LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         for (lfs_off_t i = 0; i < cfg->block_size; i++) { | ||||
|             ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1); | ||||
|         for (lfs_off_t i = 0; i < bd->cfg.erase_size; i++) { | ||||
|             ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg.erase_value}, 1); | ||||
|             if (res2 < 0) { | ||||
|                 int err = -errno; | ||||
|                 LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); | ||||
| @@ -189,10 +166,10 @@ int lfs_filebd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_filebd_sync(const struct lfs_cfg *cfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg); | ||||
| int lfs_filebd_sync(lfs_filebd_t *bd) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)bd); | ||||
|  | ||||
|     // file sync | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     int err = fsync(bd->fd); | ||||
|     if (err) { | ||||
|         err = -errno; | ||||
|   | ||||
| @@ -10,8 +10,7 @@ | ||||
| #include "lfs.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| { | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| @@ -24,6 +23,20 @@ extern "C" | ||||
|  | ||||
| // filebd config (optional) | ||||
| struct lfs_filebd_cfg { | ||||
|     // Minimum size of block read. All read operations must be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t read_size; | ||||
|  | ||||
|     // Minimum size of block program. All program operations must be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t prog_size; | ||||
|  | ||||
|     // Size of an erasable block. | ||||
|     lfs_size_t erase_size; | ||||
|  | ||||
|     // Number of erasable blocks on the device. | ||||
|     lfs_size_t erase_count; | ||||
|  | ||||
|     // 8-bit erase value to use for simulating erases. -1 does not simulate | ||||
|     // erases, which can speed up testing by avoiding all the extra block-device | ||||
|     // operations to store the erase value. | ||||
| @@ -33,40 +46,39 @@ struct lfs_filebd_cfg { | ||||
| // filebd state | ||||
| typedef struct lfs_filebd { | ||||
|     int fd; | ||||
|     const struct lfs_filebd_cfg *cfg; | ||||
|     struct lfs_filebd_cfg cfg; | ||||
| } lfs_filebd_t; | ||||
|  | ||||
|  | ||||
| // Create a file block device using the geometry in lfs_cfg | ||||
| int lfs_filebd_create(const struct lfs_cfg *cfg, const char *path); | ||||
| int lfs_filebd_createcfg(const struct lfs_cfg *cfg, const char *path, | ||||
|         const struct lfs_filebd_cfg *bdcfg); | ||||
| // Create a file block device using the geometry in lfs_filebd_cfg | ||||
| int lfs_filebd_createcfg(lfs_filebd_t *bd, const char *path, | ||||
|         const struct lfs_filebd_cfg *cfg); | ||||
|  | ||||
| // Clean up memory associated with block device | ||||
| int lfs_filebd_destroy(const struct lfs_cfg *cfg); | ||||
| int lfs_filebd_destroy(lfs_filebd_t *bd); | ||||
|  | ||||
| // Read a block | ||||
| int lfs_filebd_read(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_filebd_read(lfs_filebd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size); | ||||
|  | ||||
| // Program a block | ||||
| // | ||||
| // The block must have previously been erased. | ||||
| int lfs_filebd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_filebd_prog(lfs_filebd_t *bd, 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. | ||||
| int lfs_filebd_erase(const struct lfs_cfg *cfg, lfs_block_t block); | ||||
| int lfs_filebd_erase(lfs_filebd_t *bd, lfs_block_t block); | ||||
|  | ||||
| // Sync the block device | ||||
| int lfs_filebd_sync(const struct lfs_cfg *cfg); | ||||
| int lfs_filebd_sync(lfs_filebd_t *bd); | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										109
									
								
								bd/lfs_rambd.c
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								bd/lfs_rambd.c
									
									
									
									
									
								
							| @@ -6,26 +6,24 @@ | ||||
|  */ | ||||
| #include "bd/lfs_rambd.h" | ||||
|  | ||||
| int lfs_rambd_createcfg(const struct lfs_cfg *cfg, | ||||
|         const struct lfs_rambd_cfg *bdcfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_createcfg(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
| int lfs_rambd_createcfg(lfs_rambd_t *bd, | ||||
|         const struct lfs_rambd_cfg *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_filebd_createcfg(%p, %p {" | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
|                 "%p {.erase_value=%"PRId32", .buffer=%p})", | ||||
|             (void*)cfg, cfg->context, | ||||
|             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||
|             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||
|             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||
|             (void*)bdcfg, bdcfg->erase_value, bdcfg->buffer); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|     bd->cfg = bdcfg; | ||||
|                 ".erase_size=%"PRIu32", .erase_count=%"PRIu32", " | ||||
|                 ".erase_value=%"PRId32", .buffer=%p})", | ||||
|             (void*)bd, (void*)cfg, | ||||
|             cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count, | ||||
|             cfg->erase_value, cfg->buffer); | ||||
|  | ||||
|     // copy over config | ||||
|     bd->cfg = *cfg; | ||||
|  | ||||
|     // allocate buffer? | ||||
|     if (bd->cfg->buffer) { | ||||
|         bd->buffer = bd->cfg->buffer; | ||||
|     if (bd->cfg.buffer) { | ||||
|         bd->buffer = bd->cfg.buffer; | ||||
|     } else { | ||||
|         bd->buffer = lfs_malloc(cfg->block_size * cfg->block_count); | ||||
|         bd->buffer = lfs_malloc(bd->cfg.erase_size * bd->cfg.erase_count); | ||||
|         if (!bd->buffer) { | ||||
|             LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|             return LFS_ERR_NOMEM; | ||||
| @@ -33,108 +31,89 @@ int lfs_rambd_createcfg(const struct lfs_cfg *cfg, | ||||
|     } | ||||
|  | ||||
|     // zero for reproducability? | ||||
|     if (bd->cfg->erase_value != -1) { | ||||
|         memset(bd->buffer, bd->cfg->erase_value, | ||||
|                 cfg->block_size * cfg->block_count); | ||||
|     if (bd->cfg.erase_value != -1) { | ||||
|         memset(bd->buffer, bd->cfg.erase_value, | ||||
|                 bd->cfg.erase_size * bd->cfg.erase_count); | ||||
|     } | ||||
|  | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_create(const struct lfs_cfg *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_create(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"})", | ||||
|             (void*)cfg, cfg->context, | ||||
|             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||
|             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||
|             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count); | ||||
|     static const struct lfs_rambd_cfg defaults = {.erase_value=-1}; | ||||
|     int err = lfs_rambd_createcfg(cfg, &defaults); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_destroy(const struct lfs_cfg *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)cfg); | ||||
| int lfs_rambd_destroy(lfs_rambd_t *bd) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)bd); | ||||
|     // clean up memory | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|     if (!bd->cfg->buffer) { | ||||
|     if (!bd->cfg.buffer) { | ||||
|         lfs_free(bd->buffer); | ||||
|     } | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_read(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_rambd_read(lfs_rambd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|             (void*)bd, block, off, buffer, size); | ||||
|  | ||||
|     // check if read is valid | ||||
|     LFS_ASSERT(off  % cfg->read_size == 0); | ||||
|     LFS_ASSERT(size % cfg->read_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(off  % bd->cfg.read_size == 0); | ||||
|     LFS_ASSERT(size % bd->cfg.read_size == 0); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // read data | ||||
|     memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size); | ||||
|     memcpy(buffer, &bd->buffer[block*bd->cfg.erase_size + off], size); | ||||
|  | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_rambd_prog(lfs_rambd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_prog(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|             (void*)bd, block, off, buffer, size); | ||||
|  | ||||
|     // check if write is valid | ||||
|     LFS_ASSERT(off  % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(size % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(off  % bd->cfg.prog_size == 0); | ||||
|     LFS_ASSERT(size % bd->cfg.prog_size == 0); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // check that data was erased? only needed for testing | ||||
|     if (bd->cfg->erase_value != -1) { | ||||
|     if (bd->cfg.erase_value != -1) { | ||||
|         for (lfs_off_t i = 0; i < size; i++) { | ||||
|             LFS_ASSERT(bd->buffer[block*cfg->block_size + off + i] == | ||||
|                     bd->cfg->erase_value); | ||||
|             LFS_ASSERT(bd->buffer[block*bd->cfg.erase_size + off + i] == | ||||
|                     bd->cfg.erase_value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // program data | ||||
|     memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size); | ||||
|     memcpy(&bd->buffer[block*bd->cfg.erase_size + off], buffer, size); | ||||
|  | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
| int lfs_rambd_erase(lfs_rambd_t *bd, lfs_block_t block) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)bd, block); | ||||
|  | ||||
|     // check if erase is valid | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // erase, only needed for testing | ||||
|     if (bd->cfg->erase_value != -1) { | ||||
|         memset(&bd->buffer[block*cfg->block_size], | ||||
|                 bd->cfg->erase_value, cfg->block_size); | ||||
|     if (bd->cfg.erase_value != -1) { | ||||
|         memset(&bd->buffer[block*bd->cfg.erase_size], | ||||
|                 bd->cfg.erase_value, bd->cfg.erase_size); | ||||
|     } | ||||
|  | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_rambd_sync(const struct lfs_cfg *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)cfg); | ||||
| int lfs_rambd_sync(lfs_rambd_t *bd) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)bd); | ||||
|     // sync does nothing because we aren't backed by anything real | ||||
|     (void)cfg; | ||||
|     (void)bd; | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -10,8 +10,7 @@ | ||||
| #include "lfs.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| { | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| @@ -24,6 +23,20 @@ extern "C" | ||||
|  | ||||
| // rambd config (optional) | ||||
| struct lfs_rambd_cfg { | ||||
|     // Minimum size of block read. All read operations must be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t read_size; | ||||
|  | ||||
|     // Minimum size of block program. All program operations must be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t prog_size; | ||||
|  | ||||
|     // Size of an erasable block. | ||||
|     lfs_size_t erase_size; | ||||
|  | ||||
|     // Number of erasable blocks on the device. | ||||
|     lfs_size_t erase_count; | ||||
|  | ||||
|     // 8-bit erase value to simulate erasing with. -1 indicates no erase | ||||
|     // occurs, which is still a valid block device | ||||
|     int32_t erase_value; | ||||
| @@ -35,40 +48,39 @@ struct lfs_rambd_cfg { | ||||
| // rambd state | ||||
| typedef struct lfs_rambd { | ||||
|     uint8_t *buffer; | ||||
|     const struct lfs_rambd_cfg *cfg; | ||||
|     struct lfs_rambd_cfg cfg; | ||||
| } lfs_rambd_t; | ||||
|  | ||||
|  | ||||
| // Create a RAM block device using the geometry in lfs_cfg | ||||
| int lfs_rambd_create(const struct lfs_cfg *cfg); | ||||
| int lfs_rambd_createcfg(const struct lfs_cfg *cfg, | ||||
|         const struct lfs_rambd_cfg *bdcfg); | ||||
| int lfs_rambd_createcfg(lfs_rambd_t *bd, | ||||
|         const struct lfs_rambd_cfg *cfg); | ||||
|  | ||||
| // Clean up memory associated with block device | ||||
| int lfs_rambd_destroy(const struct lfs_cfg *cfg); | ||||
| int lfs_rambd_destroy(lfs_rambd_t *bd); | ||||
|  | ||||
| // Read a block | ||||
| int lfs_rambd_read(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_rambd_read(lfs_rambd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size); | ||||
|  | ||||
| // Program a block | ||||
| // | ||||
| // The block must have previously been erased. | ||||
| int lfs_rambd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_rambd_prog(lfs_rambd_t *bd, 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. | ||||
| int lfs_rambd_erase(const struct lfs_cfg *cfg, lfs_block_t block); | ||||
| int lfs_rambd_erase(lfs_rambd_t *bd, lfs_block_t block); | ||||
|  | ||||
| // Sync the block device | ||||
| int lfs_rambd_sync(const struct lfs_cfg *cfg); | ||||
| int lfs_rambd_sync(lfs_rambd_t *bd); | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										204
									
								
								bd/lfs_testbd.c
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								bd/lfs_testbd.c
									
									
									
									
									
								
							| @@ -10,185 +10,164 @@ | ||||
| #include <stdlib.h> | ||||
|  | ||||
|  | ||||
| int lfs_testbd_createcfg(const struct lfs_cfg *cfg, const char *path, | ||||
|         const struct lfs_testbd_cfg *bdcfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
| int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path, | ||||
|         const struct lfs_testbd_cfg *cfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p, \"%s\", %p {" | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
|                 "\"%s\", " | ||||
|                 "%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", " | ||||
|                 ".erase_size=%"PRIu32", .erase_count=%"PRIu32", " | ||||
|                 ".erase_value=%"PRId32", .erase_cycles=%"PRIu32", " | ||||
|                 ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", " | ||||
|                 ".buffer=%p, .wear_buffer=%p})", | ||||
|             (void*)cfg, cfg->context, | ||||
|             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||
|             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||
|             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||
|             path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles, | ||||
|             bdcfg->badblock_behavior, bdcfg->power_cycles, | ||||
|             bdcfg->buffer, bdcfg->wear_buffer); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     bd->cfg = bdcfg; | ||||
|             (void*)bd, path, (void*)cfg, | ||||
|             cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count, | ||||
|             cfg->erase_value, cfg->erase_cycles, | ||||
|             cfg->badblock_behavior, cfg->power_cycles, | ||||
|             cfg->buffer, cfg->wear_buffer); | ||||
|  | ||||
|     // copy over config | ||||
|     bd->cfg = *cfg; | ||||
|  | ||||
|     // setup testing things | ||||
|     bd->persist = path; | ||||
|     bd->power_cycles = bd->cfg->power_cycles; | ||||
|     bd->power_cycles = bd->cfg.power_cycles; | ||||
|  | ||||
|     if (bd->cfg->erase_cycles) { | ||||
|         if (bd->cfg->wear_buffer) { | ||||
|             bd->wear = bd->cfg->wear_buffer; | ||||
|     if (bd->cfg.erase_cycles) { | ||||
|         if (bd->cfg.wear_buffer) { | ||||
|             bd->wear = bd->cfg.wear_buffer; | ||||
|         } else { | ||||
|             bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->block_count); | ||||
|             bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->erase_count); | ||||
|             if (!bd->wear) { | ||||
|                 LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|                 return LFS_ERR_NOMEM; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * cfg->block_count); | ||||
|         memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * bd->cfg.erase_count); | ||||
|     } | ||||
|  | ||||
|     // create underlying block device | ||||
|     if (bd->persist) { | ||||
|         bd->u.file.cfg = (struct lfs_filebd_cfg){ | ||||
|             .erase_value = bd->cfg->erase_value, | ||||
|         }; | ||||
|         int err = lfs_filebd_createcfg(cfg, path, &bd->u.file.cfg); | ||||
|         int err = lfs_filebd_createcfg(&bd->impl.filebd, path, | ||||
|                 &(struct lfs_filebd_cfg){ | ||||
|                     .read_size=bd->cfg.read_size, | ||||
|                     .prog_size=bd->cfg.prog_size, | ||||
|                     .erase_size=bd->cfg.erase_size, | ||||
|                     .erase_count=bd->cfg.erase_count, | ||||
|                     .erase_value=bd->cfg.erase_value}); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } else { | ||||
|         bd->u.ram.cfg = (struct lfs_rambd_cfg){ | ||||
|             .erase_value = bd->cfg->erase_value, | ||||
|             .buffer = bd->cfg->buffer, | ||||
|         }; | ||||
|         int err = lfs_rambd_createcfg(cfg, &bd->u.ram.cfg); | ||||
|         int err = lfs_rambd_createcfg(&bd->impl.rambd, | ||||
|                 &(struct lfs_rambd_cfg){ | ||||
|                     .read_size=bd->cfg.read_size, | ||||
|                     .prog_size=bd->cfg.prog_size, | ||||
|                     .erase_size=bd->cfg.erase_size, | ||||
|                     .erase_count=bd->cfg.erase_count, | ||||
|                     .erase_value=bd->cfg.erase_value, | ||||
|                     .buffer=bd->cfg.buffer}); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int lfs_testbd_create(const struct lfs_cfg *cfg, const char *path) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_create(%p {.context=%p, " | ||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||
|                 "\"%s\")", | ||||
|             (void*)cfg, cfg->context, | ||||
|             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||
|             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||
|             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||
|             path); | ||||
|     static const struct lfs_testbd_cfg defaults = {.erase_value=-1}; | ||||
|     int err = lfs_testbd_createcfg(cfg, path, &defaults); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_destroy(const struct lfs_cfg *cfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) { | ||||
| int lfs_testbd_destroy(lfs_testbd_t *bd) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)bd); | ||||
|     if (bd->cfg.erase_cycles && !bd->cfg.wear_buffer) { | ||||
|         lfs_free(bd->wear); | ||||
|     } | ||||
|  | ||||
|     if (bd->persist) { | ||||
|         int err = lfs_filebd_destroy(cfg); | ||||
|         int err = lfs_filebd_destroy(&bd->impl.filebd); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } else { | ||||
|         int err = lfs_rambd_destroy(cfg); | ||||
|         int err = lfs_rambd_destroy(&bd->impl.rambd); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Internal mapping to block devices /// | ||||
| static int lfs_testbd_rawread(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| static int lfs_testbd_rawread(lfs_testbd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->persist) { | ||||
|         return lfs_filebd_read(cfg, block, off, buffer, size); | ||||
|         return lfs_filebd_read(&bd->impl.filebd, block, off, buffer, size); | ||||
|     } else { | ||||
|         return lfs_rambd_read(cfg, block, off, buffer, size); | ||||
|         return lfs_rambd_read(&bd->impl.rambd, block, off, buffer, size); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_testbd_rawprog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| static int lfs_testbd_rawprog(lfs_testbd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->persist) { | ||||
|         return lfs_filebd_prog(cfg, block, off, buffer, size); | ||||
|         return lfs_filebd_prog(&bd->impl.filebd, block, off, buffer, size); | ||||
|     } else { | ||||
|         return lfs_rambd_prog(cfg, block, off, buffer, size); | ||||
|         return lfs_rambd_prog(&bd->impl.rambd, block, off, buffer, size); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_testbd_rawerase(const struct lfs_cfg *cfg, | ||||
| static int lfs_testbd_rawerase(lfs_testbd_t *bd, | ||||
|         lfs_block_t block) { | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->persist) { | ||||
|         return lfs_filebd_erase(cfg, block); | ||||
|         return lfs_filebd_erase(&bd->impl.filebd, block); | ||||
|     } else { | ||||
|         return lfs_rambd_erase(cfg, block); | ||||
|         return lfs_rambd_erase(&bd->impl.rambd, block); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_testbd_rawsync(const struct lfs_cfg *cfg) { | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
| static int lfs_testbd_rawsync(lfs_testbd_t *bd) { | ||||
|     if (bd->persist) { | ||||
|         return lfs_filebd_sync(cfg); | ||||
|         return lfs_filebd_sync(&bd->impl.filebd); | ||||
|     } else { | ||||
|         return lfs_rambd_sync(cfg); | ||||
|         return lfs_rambd_sync(&bd->impl.rambd); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// block device API /// | ||||
| int lfs_testbd_read(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_testbd_read(lfs_testbd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_read(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|             (void*)bd, block, off, buffer, size); | ||||
|  | ||||
|     // check if read is valid | ||||
|     LFS_ASSERT(off  % cfg->read_size == 0); | ||||
|     LFS_ASSERT(size % cfg->read_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(off  % bd->cfg.read_size == 0); | ||||
|     LFS_ASSERT(size % bd->cfg.read_size == 0); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // block bad? | ||||
|     if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles && | ||||
|             bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) { | ||||
|     if (bd->cfg.erase_cycles && bd->wear[block] >= bd->cfg.erase_cycles && | ||||
|             bd->cfg.badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) { | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); | ||||
|         return LFS_ERR_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     // read | ||||
|     int err = lfs_testbd_rawread(cfg, block, off, buffer, size); | ||||
|     int err = lfs_testbd_rawread(bd, block, off, buffer, size); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_testbd_prog(lfs_testbd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|             (void*)bd, block, off, buffer, size); | ||||
|  | ||||
|     // check if write is valid | ||||
|     LFS_ASSERT(off  % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(size % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(off  % bd->cfg.prog_size == 0); | ||||
|     LFS_ASSERT(size % bd->cfg.prog_size == 0); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // block bad? | ||||
|     if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) { | ||||
|         if (bd->cfg->badblock_behavior == | ||||
|     if (bd->cfg.erase_cycles && bd->wear[block] >= bd->cfg.erase_cycles) { | ||||
|         if (bd->cfg.badblock_behavior == | ||||
|                 LFS_TESTBD_BADBLOCK_PROGERROR) { | ||||
|             LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT); | ||||
|             return LFS_ERR_CORRUPT; | ||||
|         } else if (bd->cfg->badblock_behavior == | ||||
|         } else if (bd->cfg.badblock_behavior == | ||||
|                 LFS_TESTBD_BADBLOCK_PROGNOOP || | ||||
|                 bd->cfg->badblock_behavior == | ||||
|                 bd->cfg.badblock_behavior == | ||||
|                 LFS_TESTBD_BADBLOCK_ERASENOOP) { | ||||
|             LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|             return 0; | ||||
| @@ -196,7 +175,7 @@ int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
|     } | ||||
|  | ||||
|     // prog | ||||
|     int err = lfs_testbd_rawprog(cfg, block, off, buffer, size); | ||||
|     int err = lfs_testbd_rawprog(bd, block, off, buffer, size); | ||||
|     if (err) { | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err); | ||||
|         return err; | ||||
| @@ -207,7 +186,7 @@ int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
|         bd->power_cycles -= 1; | ||||
|         if (bd->power_cycles == 0) { | ||||
|             // sync to make sure we persist the last changes | ||||
|             assert(lfs_testbd_rawsync(cfg) == 0); | ||||
|             assert(lfs_testbd_rawsync(bd) == 0); | ||||
|             // simulate power loss | ||||
|             exit(33); | ||||
|         } | ||||
| @@ -217,21 +196,20 @@ int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
| int lfs_testbd_erase(lfs_testbd_t *bd, lfs_block_t block) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)bd, block); | ||||
|  | ||||
|     // check if erase is valid | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     // block bad? | ||||
|     if (bd->cfg->erase_cycles) { | ||||
|         if (bd->wear[block] >= bd->cfg->erase_cycles) { | ||||
|             if (bd->cfg->badblock_behavior == | ||||
|     if (bd->cfg.erase_cycles) { | ||||
|         if (bd->wear[block] >= bd->cfg.erase_cycles) { | ||||
|             if (bd->cfg.badblock_behavior == | ||||
|                     LFS_TESTBD_BADBLOCK_ERASEERROR) { | ||||
|                 LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); | ||||
|                 return LFS_ERR_CORRUPT; | ||||
|             } else if (bd->cfg->badblock_behavior == | ||||
|             } else if (bd->cfg.badblock_behavior == | ||||
|                     LFS_TESTBD_BADBLOCK_ERASENOOP) { | ||||
|                 LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0); | ||||
|                 return 0; | ||||
| @@ -243,7 +221,7 @@ int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { | ||||
|     } | ||||
|  | ||||
|     // erase | ||||
|     int err = lfs_testbd_rawerase(cfg, block); | ||||
|     int err = lfs_testbd_rawerase(bd, block); | ||||
|     if (err) { | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err); | ||||
|         return err; | ||||
| @@ -254,7 +232,7 @@ int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { | ||||
|         bd->power_cycles -= 1; | ||||
|         if (bd->power_cycles == 0) { | ||||
|             // sync to make sure we persist the last changes | ||||
|             assert(lfs_testbd_rawsync(cfg) == 0); | ||||
|             assert(lfs_testbd_rawsync(bd) == 0); | ||||
|             // simulate power loss | ||||
|             exit(33); | ||||
|         } | ||||
| @@ -264,36 +242,34 @@ int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_sync(const struct lfs_cfg *cfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg); | ||||
|     int err = lfs_testbd_rawsync(cfg); | ||||
| int lfs_testbd_sync(lfs_testbd_t *bd) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)bd); | ||||
|     int err = lfs_testbd_rawsync(bd); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
|  | ||||
| /// simulated wear operations /// | ||||
| lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_cfg *cfg, | ||||
| lfs_testbd_swear_t lfs_testbd_getwear(lfs_testbd_t *bd, | ||||
|         lfs_block_t block) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)bd, block); | ||||
|  | ||||
|     // check if block is valid | ||||
|     LFS_ASSERT(bd->cfg->erase_cycles); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(bd->cfg.erase_cycles); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); | ||||
|     return bd->wear[block]; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_setwear(const struct lfs_cfg *cfg, | ||||
| int lfs_testbd_setwear(lfs_testbd_t *bd, | ||||
|         lfs_block_t block, lfs_testbd_wear_t wear) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)bd, block); | ||||
|  | ||||
|     // check if block is valid | ||||
|     LFS_ASSERT(bd->cfg->erase_cycles); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS_ASSERT(bd->cfg.erase_cycles); | ||||
|     LFS_ASSERT(block < bd->cfg.erase_count); | ||||
|  | ||||
|     bd->wear[block] = wear; | ||||
|  | ||||
|   | ||||
| @@ -13,8 +13,7 @@ | ||||
| #include "bd/lfs_filebd.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| { | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| @@ -45,6 +44,20 @@ typedef int32_t  lfs_testbd_swear_t; | ||||
|  | ||||
| // testbd config, this is required for testing | ||||
| struct lfs_testbd_cfg { | ||||
|     // Minimum size of block read. All read operations must be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t read_size; | ||||
|  | ||||
|     // Minimum size of block program. All program operations must be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t prog_size; | ||||
|  | ||||
|     // Size of an erasable block. | ||||
|     lfs_size_t erase_size; | ||||
|  | ||||
|     // Number of erasable blocks on the device. | ||||
|     lfs_size_t erase_count; | ||||
|  | ||||
|     // 8-bit erase value to use for simulating erases. -1 does not simulate | ||||
|     // erases, which can speed up testing by avoiding all the extra block-device | ||||
|     // operations to store the erase value. | ||||
| @@ -71,21 +84,15 @@ struct lfs_testbd_cfg { | ||||
| // testbd state | ||||
| typedef struct lfs_testbd { | ||||
|     union { | ||||
|         struct { | ||||
|             lfs_filebd_t bd; | ||||
|             struct lfs_filebd_cfg cfg; | ||||
|         } file; | ||||
|         struct { | ||||
|             lfs_rambd_t bd; | ||||
|             struct lfs_rambd_cfg cfg; | ||||
|         } ram; | ||||
|     } u; | ||||
|         lfs_filebd_t filebd; | ||||
|         lfs_rambd_t rambd; | ||||
|     } impl; | ||||
|  | ||||
|     bool persist; | ||||
|     uint32_t power_cycles; | ||||
|     lfs_testbd_wear_t *wear; | ||||
|  | ||||
|     const struct lfs_testbd_cfg *cfg; | ||||
|     struct lfs_testbd_cfg cfg; | ||||
| } lfs_testbd_t; | ||||
|  | ||||
|  | ||||
| @@ -95,46 +102,45 @@ typedef struct lfs_testbd { | ||||
| // | ||||
| // Note that filebd is used if a path is provided, if path is NULL | ||||
| // testbd will use rambd which can be much faster. | ||||
| int lfs_testbd_create(const struct lfs_cfg *cfg, const char *path); | ||||
| int lfs_testbd_createcfg(const struct lfs_cfg *cfg, const char *path, | ||||
|         const struct lfs_testbd_cfg *bdcfg); | ||||
| int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path, | ||||
|         const struct lfs_testbd_cfg *cfg); | ||||
|  | ||||
| // Clean up memory associated with block device | ||||
| int lfs_testbd_destroy(const struct lfs_cfg *cfg); | ||||
| int lfs_testbd_destroy(lfs_testbd_t *bd); | ||||
|  | ||||
| // Read a block | ||||
| int lfs_testbd_read(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_testbd_read(lfs_testbd_t *bd, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size); | ||||
|  | ||||
| // Program a block | ||||
| // | ||||
| // The block must have previously been erased. | ||||
| int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block, | ||||
| int lfs_testbd_prog(lfs_testbd_t *bd, 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. | ||||
| int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block); | ||||
| int lfs_testbd_erase(lfs_testbd_t *bd, lfs_block_t block); | ||||
|  | ||||
| // Sync the block device | ||||
| int lfs_testbd_sync(const struct lfs_cfg *cfg); | ||||
| int lfs_testbd_sync(lfs_testbd_t *bd); | ||||
|  | ||||
|  | ||||
| /// Additional extended API for driving test features /// | ||||
|  | ||||
| // Get simulated wear on a given block | ||||
| lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_cfg *cfg, | ||||
| lfs_testbd_swear_t lfs_testbd_getwear(lfs_testbd_t *bd, | ||||
|         lfs_block_t block); | ||||
|  | ||||
| // Manually set simulated wear on a given block | ||||
| int lfs_testbd_setwear(const struct lfs_cfg *cfg, | ||||
| int lfs_testbd_setwear(lfs_testbd_t *bd, | ||||
|         lfs_block_t block, lfs_testbd_wear_t wear); | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user