119ea8026Sopenharmony_ci/*
219ea8026Sopenharmony_ci * Testing block device, wraps filebd and rambd while providing a bunch
319ea8026Sopenharmony_ci * of hooks for testing littlefs in various conditions.
419ea8026Sopenharmony_ci *
519ea8026Sopenharmony_ci * Copyright (c) 2022, The littlefs authors.
619ea8026Sopenharmony_ci * Copyright (c) 2017, Arm Limited. All rights reserved.
719ea8026Sopenharmony_ci * SPDX-License-Identifier: BSD-3-Clause
819ea8026Sopenharmony_ci */
919ea8026Sopenharmony_ci#ifndef LFS_TESTBD_H
1019ea8026Sopenharmony_ci#define LFS_TESTBD_H
1119ea8026Sopenharmony_ci
1219ea8026Sopenharmony_ci#include "lfs.h"
1319ea8026Sopenharmony_ci#include "lfs_util.h"
1419ea8026Sopenharmony_ci#include "bd/lfs_rambd.h"
1519ea8026Sopenharmony_ci#include "bd/lfs_filebd.h"
1619ea8026Sopenharmony_ci
1719ea8026Sopenharmony_ci#ifdef __cplusplus
1819ea8026Sopenharmony_ciextern "C"
1919ea8026Sopenharmony_ci{
2019ea8026Sopenharmony_ci#endif
2119ea8026Sopenharmony_ci
2219ea8026Sopenharmony_ci
2319ea8026Sopenharmony_ci// Block device specific tracing
2419ea8026Sopenharmony_ci#ifdef LFS_TESTBD_YES_TRACE
2519ea8026Sopenharmony_ci#define LFS_TESTBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
2619ea8026Sopenharmony_ci#else
2719ea8026Sopenharmony_ci#define LFS_TESTBD_TRACE(...)
2819ea8026Sopenharmony_ci#endif
2919ea8026Sopenharmony_ci
3019ea8026Sopenharmony_ci// Mode determining how "bad blocks" behave during testing. This simulates
3119ea8026Sopenharmony_ci// some real-world circumstances such as progs not sticking (prog-noop),
3219ea8026Sopenharmony_ci// a readonly disk (erase-noop), and ECC failures (read-error).
3319ea8026Sopenharmony_ci//
3419ea8026Sopenharmony_ci// Not that read-noop is not allowed. Read _must_ return a consistent (but
3519ea8026Sopenharmony_ci// may be arbitrary) value on every read.
3619ea8026Sopenharmony_cienum lfs_testbd_badblock_behavior {
3719ea8026Sopenharmony_ci    LFS_TESTBD_BADBLOCK_PROGERROR,
3819ea8026Sopenharmony_ci    LFS_TESTBD_BADBLOCK_ERASEERROR,
3919ea8026Sopenharmony_ci    LFS_TESTBD_BADBLOCK_READERROR,
4019ea8026Sopenharmony_ci    LFS_TESTBD_BADBLOCK_PROGNOOP,
4119ea8026Sopenharmony_ci    LFS_TESTBD_BADBLOCK_ERASENOOP,
4219ea8026Sopenharmony_ci};
4319ea8026Sopenharmony_ci
4419ea8026Sopenharmony_ci// Type for measuring wear
4519ea8026Sopenharmony_citypedef uint32_t lfs_testbd_wear_t;
4619ea8026Sopenharmony_citypedef int32_t  lfs_testbd_swear_t;
4719ea8026Sopenharmony_ci
4819ea8026Sopenharmony_ci// testbd config, this is required for testing
4919ea8026Sopenharmony_cistruct lfs_testbd_config {
5019ea8026Sopenharmony_ci    // 8-bit erase value to use for simulating erases. -1 does not simulate
5119ea8026Sopenharmony_ci    // erases, which can speed up testing by avoiding all the extra block-device
5219ea8026Sopenharmony_ci    // operations to store the erase value.
5319ea8026Sopenharmony_ci    int32_t erase_value;
5419ea8026Sopenharmony_ci
5519ea8026Sopenharmony_ci    // Number of erase cycles before a block becomes "bad". The exact behavior
5619ea8026Sopenharmony_ci    // of bad blocks is controlled by the badblock_mode.
5719ea8026Sopenharmony_ci    uint32_t erase_cycles;
5819ea8026Sopenharmony_ci
5919ea8026Sopenharmony_ci    // The mode determining how bad blocks fail
6019ea8026Sopenharmony_ci    uint8_t badblock_behavior;
6119ea8026Sopenharmony_ci
6219ea8026Sopenharmony_ci    // Number of write operations (erase/prog) before forcefully killing
6319ea8026Sopenharmony_ci    // the program with exit. Simulates power-loss. 0 disables.
6419ea8026Sopenharmony_ci    uint32_t power_cycles;
6519ea8026Sopenharmony_ci
6619ea8026Sopenharmony_ci    // Optional buffer for RAM block device.
6719ea8026Sopenharmony_ci    void *buffer;
6819ea8026Sopenharmony_ci
6919ea8026Sopenharmony_ci    // Optional buffer for wear
7019ea8026Sopenharmony_ci    void *wear_buffer;
7119ea8026Sopenharmony_ci};
7219ea8026Sopenharmony_ci
7319ea8026Sopenharmony_ci// testbd state
7419ea8026Sopenharmony_citypedef struct lfs_testbd {
7519ea8026Sopenharmony_ci    union {
7619ea8026Sopenharmony_ci        struct {
7719ea8026Sopenharmony_ci            lfs_filebd_t bd;
7819ea8026Sopenharmony_ci            struct lfs_filebd_config cfg;
7919ea8026Sopenharmony_ci        } file;
8019ea8026Sopenharmony_ci        struct {
8119ea8026Sopenharmony_ci            lfs_rambd_t bd;
8219ea8026Sopenharmony_ci            struct lfs_rambd_config cfg;
8319ea8026Sopenharmony_ci        } ram;
8419ea8026Sopenharmony_ci    } u;
8519ea8026Sopenharmony_ci
8619ea8026Sopenharmony_ci    bool persist;
8719ea8026Sopenharmony_ci    uint32_t power_cycles;
8819ea8026Sopenharmony_ci    lfs_testbd_wear_t *wear;
8919ea8026Sopenharmony_ci
9019ea8026Sopenharmony_ci    const struct lfs_testbd_config *cfg;
9119ea8026Sopenharmony_ci} lfs_testbd_t;
9219ea8026Sopenharmony_ci
9319ea8026Sopenharmony_ci
9419ea8026Sopenharmony_ci/// Block device API ///
9519ea8026Sopenharmony_ci
9619ea8026Sopenharmony_ci// Create a test block device using the geometry in lfs_config
9719ea8026Sopenharmony_ci//
9819ea8026Sopenharmony_ci// Note that filebd is used if a path is provided, if path is NULL
9919ea8026Sopenharmony_ci// testbd will use rambd which can be much faster.
10019ea8026Sopenharmony_ciint lfs_testbd_create(const struct lfs_config *cfg, const char *path);
10119ea8026Sopenharmony_ciint lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
10219ea8026Sopenharmony_ci        const struct lfs_testbd_config *bdcfg);
10319ea8026Sopenharmony_ci
10419ea8026Sopenharmony_ci// Clean up memory associated with block device
10519ea8026Sopenharmony_ciint lfs_testbd_destroy(const struct lfs_config *cfg);
10619ea8026Sopenharmony_ci
10719ea8026Sopenharmony_ci// Read a block
10819ea8026Sopenharmony_ciint lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
10919ea8026Sopenharmony_ci        lfs_off_t off, void *buffer, lfs_size_t size);
11019ea8026Sopenharmony_ci
11119ea8026Sopenharmony_ci// Program a block
11219ea8026Sopenharmony_ci//
11319ea8026Sopenharmony_ci// The block must have previously been erased.
11419ea8026Sopenharmony_ciint lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
11519ea8026Sopenharmony_ci        lfs_off_t off, const void *buffer, lfs_size_t size);
11619ea8026Sopenharmony_ci
11719ea8026Sopenharmony_ci// Erase a block
11819ea8026Sopenharmony_ci//
11919ea8026Sopenharmony_ci// A block must be erased before being programmed. The
12019ea8026Sopenharmony_ci// state of an erased block is undefined.
12119ea8026Sopenharmony_ciint lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block);
12219ea8026Sopenharmony_ci
12319ea8026Sopenharmony_ci// Sync the block device
12419ea8026Sopenharmony_ciint lfs_testbd_sync(const struct lfs_config *cfg);
12519ea8026Sopenharmony_ci
12619ea8026Sopenharmony_ci
12719ea8026Sopenharmony_ci/// Additional extended API for driving test features ///
12819ea8026Sopenharmony_ci
12919ea8026Sopenharmony_ci// Get simulated wear on a given block
13019ea8026Sopenharmony_cilfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg,
13119ea8026Sopenharmony_ci        lfs_block_t block);
13219ea8026Sopenharmony_ci
13319ea8026Sopenharmony_ci// Manually set simulated wear on a given block
13419ea8026Sopenharmony_ciint lfs_testbd_setwear(const struct lfs_config *cfg,
13519ea8026Sopenharmony_ci        lfs_block_t block, lfs_testbd_wear_t wear);
13619ea8026Sopenharmony_ci
13719ea8026Sopenharmony_ci
13819ea8026Sopenharmony_ci#ifdef __cplusplus
13919ea8026Sopenharmony_ci} /* extern "C" */
14019ea8026Sopenharmony_ci#endif
14119ea8026Sopenharmony_ci
14219ea8026Sopenharmony_ci#endif
143