lfs_emubd.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. /*
  2. * Emulating block device, wraps filebd and rambd while providing a bunch
  3. * of hooks for testing littlefs in various conditions.
  4. *
  5. * Copyright (c) 2022, The littlefs authors.
  6. * Copyright (c) 2017, Arm Limited. All rights reserved.
  7. * SPDX-License-Identifier: BSD-3-Clause
  8. */
  9. #ifndef _POSIX_C_SOURCE
  10. #define _POSIX_C_SOURCE 199309L
  11. #endif
  12. #include "bd/lfs_emubd.h"
  13. #include <stdlib.h>
  14. #include <fcntl.h>
  15. #include <unistd.h>
  16. #include <errno.h>
  17. #include <time.h>
  18. #ifdef _WIN32
  19. #include <windows.h>
  20. #endif
  21. // access to lazily-allocated/copy-on-write blocks
  22. //
  23. // Note we can only modify a block if we have exclusive access to it (rc == 1)
  24. //
  25. static lfs_emubd_block_t *lfs_emubd_incblock(lfs_emubd_block_t *block) {
  26. if (block) {
  27. block->rc += 1;
  28. }
  29. return block;
  30. }
  31. static void lfs_emubd_decblock(lfs_emubd_block_t *block) {
  32. if (block) {
  33. block->rc -= 1;
  34. if (block->rc == 0) {
  35. free(block);
  36. }
  37. }
  38. }
  39. static lfs_emubd_block_t *lfs_emubd_mutblock(
  40. const struct lfs_config *cfg,
  41. lfs_emubd_block_t **block) {
  42. lfs_emubd_block_t *block_ = *block;
  43. if (block_ && block_->rc == 1) {
  44. // rc == 1? can modify
  45. return block_;
  46. } else if (block_) {
  47. // rc > 1? need to create a copy
  48. lfs_emubd_block_t *nblock = malloc(
  49. sizeof(lfs_emubd_block_t) + cfg->block_size);
  50. if (!nblock) {
  51. return NULL;
  52. }
  53. memcpy(nblock, block_,
  54. sizeof(lfs_emubd_block_t) + cfg->block_size);
  55. nblock->rc = 1;
  56. lfs_emubd_decblock(block_);
  57. *block = nblock;
  58. return nblock;
  59. } else {
  60. // no block? need to allocate
  61. lfs_emubd_block_t *nblock = malloc(
  62. sizeof(lfs_emubd_block_t) + cfg->block_size);
  63. if (!nblock) {
  64. return NULL;
  65. }
  66. nblock->rc = 1;
  67. nblock->wear = 0;
  68. // zero for consistency
  69. lfs_emubd_t *bd = cfg->context;
  70. memset(nblock->data,
  71. (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
  72. cfg->block_size);
  73. *block = nblock;
  74. return nblock;
  75. }
  76. }
  77. // emubd create/destroy
  78. int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path,
  79. const struct lfs_emubd_config *bdcfg) {
  80. LFS_EMUBD_TRACE("lfs_emubd_createcfg(%p {.context=%p, "
  81. ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
  82. ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
  83. ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
  84. "\"%s\", "
  85. "%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
  86. ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
  87. ".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, "
  88. ".powerloss_data=%p, .track_branches=%d})",
  89. (void*)cfg, cfg->context,
  90. (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
  91. (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
  92. cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
  93. path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles,
  94. bdcfg->badblock_behavior, bdcfg->power_cycles,
  95. bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
  96. bdcfg->powerloss_data, bdcfg->track_branches);
  97. lfs_emubd_t *bd = cfg->context;
  98. bd->cfg = bdcfg;
  99. // allocate our block array, all blocks start as uninitialized
  100. bd->blocks = malloc(cfg->block_count * sizeof(lfs_emubd_block_t*));
  101. if (!bd->blocks) {
  102. LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
  103. return LFS_ERR_NOMEM;
  104. }
  105. memset(bd->blocks, 0, cfg->block_count * sizeof(lfs_emubd_block_t*));
  106. // setup testing things
  107. bd->readed = 0;
  108. bd->proged = 0;
  109. bd->erased = 0;
  110. bd->power_cycles = bd->cfg->power_cycles;
  111. bd->disk = NULL;
  112. if (bd->cfg->disk_path) {
  113. bd->disk = malloc(sizeof(lfs_emubd_disk_t));
  114. if (!bd->disk) {
  115. LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
  116. return LFS_ERR_NOMEM;
  117. }
  118. bd->disk->rc = 1;
  119. bd->disk->scratch = NULL;
  120. #ifdef _WIN32
  121. bd->disk->fd = open(bd->cfg->disk_path,
  122. O_RDWR | O_CREAT | O_BINARY, 0666);
  123. #else
  124. bd->disk->fd = open(bd->cfg->disk_path,
  125. O_RDWR | O_CREAT, 0666);
  126. #endif
  127. if (bd->disk->fd < 0) {
  128. int err = -errno;
  129. LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
  130. return err;
  131. }
  132. // if we're emulating erase values, we can keep a block around in
  133. // memory of just the erase state to speed up emulated erases
  134. if (bd->cfg->erase_value != -1) {
  135. bd->disk->scratch = malloc(cfg->block_size);
  136. if (!bd->disk->scratch) {
  137. LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
  138. return LFS_ERR_NOMEM;
  139. }
  140. memset(bd->disk->scratch,
  141. bd->cfg->erase_value,
  142. cfg->block_size);
  143. // go ahead and erase all of the disk, otherwise the file will not
  144. // match our internal representation
  145. for (size_t i = 0; i < cfg->block_count; i++) {
  146. ssize_t res = write(bd->disk->fd,
  147. bd->disk->scratch,
  148. cfg->block_size);
  149. if (res < 0) {
  150. int err = -errno;
  151. LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
  152. return err;
  153. }
  154. }
  155. }
  156. }
  157. LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", 0);
  158. return 0;
  159. }
  160. int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
  161. LFS_EMUBD_TRACE("lfs_emubd_create(%p {.context=%p, "
  162. ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
  163. ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
  164. ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
  165. "\"%s\")",
  166. (void*)cfg, cfg->context,
  167. (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
  168. (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
  169. cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
  170. path);
  171. static const struct lfs_emubd_config defaults = {.erase_value=-1};
  172. int err = lfs_emubd_createcfg(cfg, path, &defaults);
  173. LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
  174. return err;
  175. }
  176. int lfs_emubd_destroy(const struct lfs_config *cfg) {
  177. LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg);
  178. lfs_emubd_t *bd = cfg->context;
  179. // decrement reference counts
  180. for (lfs_block_t i = 0; i < cfg->block_count; i++) {
  181. lfs_emubd_decblock(bd->blocks[i]);
  182. }
  183. free(bd->blocks);
  184. // clean up other resources
  185. if (bd->disk) {
  186. bd->disk->rc -= 1;
  187. if (bd->disk->rc == 0) {
  188. close(bd->disk->fd);
  189. free(bd->disk->scratch);
  190. free(bd->disk);
  191. }
  192. }
  193. LFS_EMUBD_TRACE("lfs_emubd_destroy -> %d", 0);
  194. return 0;
  195. }
  196. // block device API
  197. int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
  198. lfs_off_t off, void *buffer, lfs_size_t size) {
  199. LFS_EMUBD_TRACE("lfs_emubd_read(%p, "
  200. "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
  201. (void*)cfg, block, off, buffer, size);
  202. lfs_emubd_t *bd = cfg->context;
  203. // check if read is valid
  204. LFS_ASSERT(block < cfg->block_count);
  205. LFS_ASSERT(off % cfg->read_size == 0);
  206. LFS_ASSERT(size % cfg->read_size == 0);
  207. LFS_ASSERT(off+size <= cfg->block_size);
  208. // get the block
  209. const lfs_emubd_block_t *b = bd->blocks[block];
  210. if (b) {
  211. // block bad?
  212. if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles &&
  213. bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) {
  214. LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT);
  215. return LFS_ERR_CORRUPT;
  216. }
  217. // read data
  218. memcpy(buffer, &b->data[off], size);
  219. } else {
  220. // zero for consistency
  221. memset(buffer,
  222. (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
  223. size);
  224. }
  225. // track reads
  226. bd->readed += size;
  227. if (bd->cfg->read_sleep) {
  228. int err = nanosleep(&(struct timespec){
  229. .tv_sec=bd->cfg->read_sleep/1000000000,
  230. .tv_nsec=bd->cfg->read_sleep%1000000000},
  231. NULL);
  232. if (err) {
  233. err = -errno;
  234. LFS_EMUBD_TRACE("lfs_emubd_read -> %d", err);
  235. return err;
  236. }
  237. }
  238. LFS_EMUBD_TRACE("lfs_emubd_read -> %d", 0);
  239. return 0;
  240. }
  241. int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
  242. lfs_off_t off, const void *buffer, lfs_size_t size) {
  243. LFS_EMUBD_TRACE("lfs_emubd_prog(%p, "
  244. "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
  245. (void*)cfg, block, off, buffer, size);
  246. lfs_emubd_t *bd = cfg->context;
  247. // check if write is valid
  248. LFS_ASSERT(block < cfg->block_count);
  249. LFS_ASSERT(off % cfg->prog_size == 0);
  250. LFS_ASSERT(size % cfg->prog_size == 0);
  251. LFS_ASSERT(off+size <= cfg->block_size);
  252. // get the block
  253. lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
  254. if (!b) {
  255. LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
  256. return LFS_ERR_NOMEM;
  257. }
  258. // block bad?
  259. if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) {
  260. if (bd->cfg->badblock_behavior ==
  261. LFS_EMUBD_BADBLOCK_PROGERROR) {
  262. LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT);
  263. return LFS_ERR_CORRUPT;
  264. } else if (bd->cfg->badblock_behavior ==
  265. LFS_EMUBD_BADBLOCK_PROGNOOP ||
  266. bd->cfg->badblock_behavior ==
  267. LFS_EMUBD_BADBLOCK_ERASENOOP) {
  268. LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
  269. return 0;
  270. }
  271. }
  272. // were we erased properly?
  273. if (bd->cfg->erase_value != -1) {
  274. for (lfs_off_t i = 0; i < size; i++) {
  275. LFS_ASSERT(b->data[off+i] == bd->cfg->erase_value);
  276. }
  277. }
  278. // prog data
  279. memcpy(&b->data[off], buffer, size);
  280. // mirror to disk file?
  281. if (bd->disk) {
  282. off_t res1 = lseek(bd->disk->fd,
  283. (off_t)block*cfg->block_size + (off_t)off,
  284. SEEK_SET);
  285. if (res1 < 0) {
  286. int err = -errno;
  287. LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
  288. return err;
  289. }
  290. ssize_t res2 = write(bd->disk->fd, buffer, size);
  291. if (res2 < 0) {
  292. int err = -errno;
  293. LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
  294. return err;
  295. }
  296. }
  297. // track progs
  298. bd->proged += size;
  299. if (bd->cfg->prog_sleep) {
  300. int err = nanosleep(&(struct timespec){
  301. .tv_sec=bd->cfg->prog_sleep/1000000000,
  302. .tv_nsec=bd->cfg->prog_sleep%1000000000},
  303. NULL);
  304. if (err) {
  305. err = -errno;
  306. LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
  307. return err;
  308. }
  309. }
  310. // lose power?
  311. if (bd->power_cycles > 0) {
  312. bd->power_cycles -= 1;
  313. if (bd->power_cycles == 0) {
  314. // simulate power loss
  315. bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
  316. }
  317. }
  318. LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
  319. return 0;
  320. }
  321. int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
  322. LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
  323. (void*)cfg, block, cfg->block_size);
  324. lfs_emubd_t *bd = cfg->context;
  325. // check if erase is valid
  326. LFS_ASSERT(block < cfg->block_count);
  327. // get the block
  328. lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
  329. if (!b) {
  330. LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
  331. return LFS_ERR_NOMEM;
  332. }
  333. // block bad?
  334. if (bd->cfg->erase_cycles) {
  335. if (b->wear >= bd->cfg->erase_cycles) {
  336. if (bd->cfg->badblock_behavior ==
  337. LFS_EMUBD_BADBLOCK_ERASEERROR) {
  338. LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT);
  339. return LFS_ERR_CORRUPT;
  340. } else if (bd->cfg->badblock_behavior ==
  341. LFS_EMUBD_BADBLOCK_ERASENOOP) {
  342. LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
  343. return 0;
  344. }
  345. } else {
  346. // mark wear
  347. b->wear += 1;
  348. }
  349. }
  350. // emulate an erase value?
  351. if (bd->cfg->erase_value != -1) {
  352. memset(b->data, bd->cfg->erase_value, cfg->block_size);
  353. // mirror to disk file?
  354. if (bd->disk) {
  355. off_t res1 = lseek(bd->disk->fd,
  356. (off_t)block*cfg->block_size,
  357. SEEK_SET);
  358. if (res1 < 0) {
  359. int err = -errno;
  360. LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
  361. return err;
  362. }
  363. ssize_t res2 = write(bd->disk->fd,
  364. bd->disk->scratch,
  365. cfg->block_size);
  366. if (res2 < 0) {
  367. int err = -errno;
  368. LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
  369. return err;
  370. }
  371. }
  372. }
  373. // track erases
  374. bd->erased += cfg->block_size;
  375. if (bd->cfg->erase_sleep) {
  376. int err = nanosleep(&(struct timespec){
  377. .tv_sec=bd->cfg->erase_sleep/1000000000,
  378. .tv_nsec=bd->cfg->erase_sleep%1000000000},
  379. NULL);
  380. if (err) {
  381. err = -errno;
  382. LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
  383. return err;
  384. }
  385. }
  386. // lose power?
  387. if (bd->power_cycles > 0) {
  388. bd->power_cycles -= 1;
  389. if (bd->power_cycles == 0) {
  390. // simulate power loss
  391. bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
  392. }
  393. }
  394. LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
  395. return 0;
  396. }
  397. int lfs_emubd_sync(const struct lfs_config *cfg) {
  398. LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg);
  399. // do nothing
  400. (void)cfg;
  401. LFS_EMUBD_TRACE("lfs_emubd_sync -> %d", 0);
  402. return 0;
  403. }
  404. /// Additional extended API for driving test features ///
  405. static int lfs_emubd_rawcrc(const struct lfs_config *cfg,
  406. lfs_block_t block, uint32_t *crc) {
  407. lfs_emubd_t *bd = cfg->context;
  408. // check if crc is valid
  409. LFS_ASSERT(block < cfg->block_count);
  410. // crc the block
  411. uint32_t crc_ = 0xffffffff;
  412. const lfs_emubd_block_t *b = bd->blocks[block];
  413. if (b) {
  414. crc_ = lfs_crc(crc_, b->data, cfg->block_size);
  415. } else {
  416. uint8_t erase_value = (bd->cfg->erase_value != -1)
  417. ? bd->cfg->erase_value
  418. : 0;
  419. for (lfs_size_t i = 0; i < cfg->block_size; i++) {
  420. crc_ = lfs_crc(crc_, &erase_value, 1);
  421. }
  422. }
  423. *crc = 0xffffffff ^ crc_;
  424. return 0;
  425. }
  426. int lfs_emubd_crc(const struct lfs_config *cfg,
  427. lfs_block_t block, uint32_t *crc) {
  428. LFS_EMUBD_TRACE("lfs_emubd_crc(%p, %"PRIu32", %p)",
  429. (void*)cfg, block, crc);
  430. int err = lfs_emubd_rawcrc(cfg, block, crc);
  431. LFS_EMUBD_TRACE("lfs_emubd_crc -> %d", err);
  432. return err;
  433. }
  434. int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc) {
  435. LFS_EMUBD_TRACE("lfs_emubd_bdcrc(%p, %p)", (void*)cfg, crc);
  436. uint32_t crc_ = 0xffffffff;
  437. for (lfs_block_t i = 0; i < cfg->block_count; i++) {
  438. uint32_t i_crc;
  439. int err = lfs_emubd_rawcrc(cfg, i, &i_crc);
  440. if (err) {
  441. LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", err);
  442. return err;
  443. }
  444. crc_ = lfs_crc(crc_, &i_crc, sizeof(uint32_t));
  445. }
  446. *crc = 0xffffffff ^ crc_;
  447. LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", 0);
  448. return 0;
  449. }
  450. lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg) {
  451. LFS_EMUBD_TRACE("lfs_emubd_readed(%p)", (void*)cfg);
  452. lfs_emubd_t *bd = cfg->context;
  453. LFS_EMUBD_TRACE("lfs_emubd_readed -> %"PRIu64, bd->readed);
  454. return bd->readed;
  455. }
  456. lfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg) {
  457. LFS_EMUBD_TRACE("lfs_emubd_proged(%p)", (void*)cfg);
  458. lfs_emubd_t *bd = cfg->context;
  459. LFS_EMUBD_TRACE("lfs_emubd_proged -> %"PRIu64, bd->proged);
  460. return bd->proged;
  461. }
  462. lfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg) {
  463. LFS_EMUBD_TRACE("lfs_emubd_erased(%p)", (void*)cfg);
  464. lfs_emubd_t *bd = cfg->context;
  465. LFS_EMUBD_TRACE("lfs_emubd_erased -> %"PRIu64, bd->erased);
  466. return bd->erased;
  467. }
  468. int lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed) {
  469. LFS_EMUBD_TRACE("lfs_emubd_setreaded(%p, %"PRIu64")", (void*)cfg, readed);
  470. lfs_emubd_t *bd = cfg->context;
  471. bd->readed = readed;
  472. LFS_EMUBD_TRACE("lfs_emubd_setreaded -> %d", 0);
  473. return 0;
  474. }
  475. int lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged) {
  476. LFS_EMUBD_TRACE("lfs_emubd_setproged(%p, %"PRIu64")", (void*)cfg, proged);
  477. lfs_emubd_t *bd = cfg->context;
  478. bd->proged = proged;
  479. LFS_EMUBD_TRACE("lfs_emubd_setproged -> %d", 0);
  480. return 0;
  481. }
  482. int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased) {
  483. LFS_EMUBD_TRACE("lfs_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased);
  484. lfs_emubd_t *bd = cfg->context;
  485. bd->erased = erased;
  486. LFS_EMUBD_TRACE("lfs_emubd_seterased -> %d", 0);
  487. return 0;
  488. }
  489. lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
  490. lfs_block_t block) {
  491. LFS_EMUBD_TRACE("lfs_emubd_wear(%p, %"PRIu32")", (void*)cfg, block);
  492. lfs_emubd_t *bd = cfg->context;
  493. // check if block is valid
  494. LFS_ASSERT(block < cfg->block_count);
  495. // get the wear
  496. lfs_emubd_wear_t wear;
  497. const lfs_emubd_block_t *b = bd->blocks[block];
  498. if (b) {
  499. wear = b->wear;
  500. } else {
  501. wear = 0;
  502. }
  503. LFS_EMUBD_TRACE("lfs_emubd_wear -> %"PRIi32, wear);
  504. return wear;
  505. }
  506. int lfs_emubd_setwear(const struct lfs_config *cfg,
  507. lfs_block_t block, lfs_emubd_wear_t wear) {
  508. LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32", %"PRIi32")",
  509. (void*)cfg, block, wear);
  510. lfs_emubd_t *bd = cfg->context;
  511. // check if block is valid
  512. LFS_ASSERT(block < cfg->block_count);
  513. // set the wear
  514. lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
  515. if (!b) {
  516. LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM);
  517. return LFS_ERR_NOMEM;
  518. }
  519. b->wear = wear;
  520. LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0);
  521. return 0;
  522. }
  523. lfs_emubd_spowercycles_t lfs_emubd_powercycles(
  524. const struct lfs_config *cfg) {
  525. LFS_EMUBD_TRACE("lfs_emubd_powercycles(%p)", (void*)cfg);
  526. lfs_emubd_t *bd = cfg->context;
  527. LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %"PRIi32, bd->power_cycles);
  528. return bd->power_cycles;
  529. }
  530. int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
  531. lfs_emubd_powercycles_t power_cycles) {
  532. LFS_EMUBD_TRACE("lfs_emubd_setpowercycles(%p, %"PRIi32")",
  533. (void*)cfg, power_cycles);
  534. lfs_emubd_t *bd = cfg->context;
  535. bd->power_cycles = power_cycles;
  536. LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %d", 0);
  537. return 0;
  538. }
  539. int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
  540. LFS_EMUBD_TRACE("lfs_emubd_copy(%p, %p)", (void*)cfg, (void*)copy);
  541. lfs_emubd_t *bd = cfg->context;
  542. // lazily copy over our block array
  543. copy->blocks = malloc(cfg->block_count * sizeof(lfs_emubd_block_t*));
  544. if (!copy->blocks) {
  545. LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM);
  546. return LFS_ERR_NOMEM;
  547. }
  548. for (size_t i = 0; i < cfg->block_count; i++) {
  549. copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]);
  550. }
  551. // other state
  552. copy->readed = bd->readed;
  553. copy->proged = bd->proged;
  554. copy->erased = bd->erased;
  555. copy->power_cycles = bd->power_cycles;
  556. copy->disk = bd->disk;
  557. if (copy->disk) {
  558. copy->disk->rc += 1;
  559. }
  560. copy->cfg = bd->cfg;
  561. LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", 0);
  562. return 0;
  563. }