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
10 #ifndef _POSIX_C_SOURCE
11 #define _POSIX_C_SOURCE 199309L
12 #endif
13
14 #include "bd/lfs_emubd.h"
15
16 #include <stdlib.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <time.h>
21
22 #ifdef _WIN32
23 #include <windows.h>
24 #endif
25
26
27 // access to lazily-allocated/copy-on-write blocks
28 //
29 // Note we can only modify a block if we have exclusive access to it (rc == 1)
30 //
31
lfs_emubd_incblock(lfs_emubd_block_t * block)32 static lfs_emubd_block_t *lfs_emubd_incblock(lfs_emubd_block_t *block) {
33 if (block) {
34 block->rc += 1;
35 }
36 return block;
37 }
38
lfs_emubd_decblock(lfs_emubd_block_t * block)39 static void lfs_emubd_decblock(lfs_emubd_block_t *block) {
40 if (block) {
41 block->rc -= 1;
42 if (block->rc == 0) {
43 free(block);
44 }
45 }
46 }
47
lfs_emubd_mutblock(const struct lfs_config * cfg,lfs_emubd_block_t ** block)48 static lfs_emubd_block_t *lfs_emubd_mutblock(
49 const struct lfs_config *cfg,
50 lfs_emubd_block_t **block) {
51 lfs_emubd_t *bd = cfg->context;
52 lfs_emubd_block_t *block_ = *block;
53 if (block_ && block_->rc == 1) {
54 // rc == 1? can modify
55 return block_;
56
57 } else if (block_) {
58 // rc > 1? need to create a copy
59 lfs_emubd_block_t *nblock = malloc(
60 sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
61 if (!nblock) {
62 return NULL;
63 }
64
65 memcpy(nblock, block_,
66 sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
67 nblock->rc = 1;
68
69 lfs_emubd_decblock(block_);
70 *block = nblock;
71 return nblock;
72
73 } else {
74 // no block? need to allocate
75 lfs_emubd_block_t *nblock = malloc(
76 sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
77 if (!nblock) {
78 return NULL;
79 }
80
81 nblock->rc = 1;
82 nblock->wear = 0;
83
84 // zero for consistency
85 memset(nblock->data,
86 (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
87 bd->cfg->erase_size);
88
89 *block = nblock;
90 return nblock;
91 }
92 }
93
94
95 // emubd create/destroy
96
lfs_emubd_create(const struct lfs_config * cfg,const struct lfs_emubd_config * bdcfg)97 int lfs_emubd_create(const struct lfs_config *cfg,
98 const struct lfs_emubd_config *bdcfg) {
99 LFS_EMUBD_TRACE("lfs_emubd_create(%p {.context=%p, "
100 ".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
101 "%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
102 ".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
103 ".erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
104 ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
105 ".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, "
106 ".powerloss_data=%p, .track_branches=%d})",
107 (void*)cfg, cfg->context,
108 (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
109 (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
110 (void*)bdcfg,
111 bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
112 bdcfg->erase_count, bdcfg->erase_value, bdcfg->erase_cycles,
113 bdcfg->badblock_behavior, bdcfg->power_cycles,
114 bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
115 bdcfg->powerloss_data, bdcfg->track_branches);
116 lfs_emubd_t *bd = cfg->context;
117 bd->cfg = bdcfg;
118
119 // allocate our block array, all blocks start as uninitialized
120 bd->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
121 if (!bd->blocks) {
122 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
123 return LFS_ERR_NOMEM;
124 }
125 memset(bd->blocks, 0, bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
126
127 // setup testing things
128 bd->readed = 0;
129 bd->proged = 0;
130 bd->erased = 0;
131 bd->power_cycles = bd->cfg->power_cycles;
132 bd->ooo_block = -1;
133 bd->ooo_data = NULL;
134 bd->disk = NULL;
135
136 if (bd->cfg->disk_path) {
137 bd->disk = malloc(sizeof(lfs_emubd_disk_t));
138 if (!bd->disk) {
139 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
140 return LFS_ERR_NOMEM;
141 }
142 bd->disk->rc = 1;
143 bd->disk->scratch = NULL;
144
145 #ifdef _WIN32
146 bd->disk->fd = open(bd->cfg->disk_path,
147 O_RDWR | O_CREAT | O_BINARY, 0666);
148 #else
149 bd->disk->fd = open(bd->cfg->disk_path,
150 O_RDWR | O_CREAT, 0666);
151 #endif
152 if (bd->disk->fd < 0) {
153 int err = -errno;
154 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
155 return err;
156 }
157
158 // if we're emulating erase values, we can keep a block around in
159 // memory of just the erase state to speed up emulated erases
160 if (bd->cfg->erase_value != -1) {
161 bd->disk->scratch = malloc(bd->cfg->erase_size);
162 if (!bd->disk->scratch) {
163 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
164 return LFS_ERR_NOMEM;
165 }
166 memset(bd->disk->scratch,
167 bd->cfg->erase_value,
168 bd->cfg->erase_size);
169
170 // go ahead and erase all of the disk, otherwise the file will not
171 // match our internal representation
172 for (size_t i = 0; i < bd->cfg->erase_count; i++) {
173 ssize_t res = write(bd->disk->fd,
174 bd->disk->scratch,
175 bd->cfg->erase_size);
176 if (res < 0) {
177 int err = -errno;
178 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
179 return err;
180 }
181 }
182 }
183 }
184
185 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", 0);
186 return 0;
187 }
188
lfs_emubd_destroy(const struct lfs_config * cfg)189 int lfs_emubd_destroy(const struct lfs_config *cfg) {
190 LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg);
191 lfs_emubd_t *bd = cfg->context;
192
193 // decrement reference counts
194 for (lfs_block_t i = 0; i < bd->cfg->erase_count; i++) {
195 lfs_emubd_decblock(bd->blocks[i]);
196 }
197 free(bd->blocks);
198
199 // clean up other resources
200 lfs_emubd_decblock(bd->ooo_data);
201 if (bd->disk) {
202 bd->disk->rc -= 1;
203 if (bd->disk->rc == 0) {
204 close(bd->disk->fd);
205 free(bd->disk->scratch);
206 free(bd->disk);
207 }
208 }
209
210 LFS_EMUBD_TRACE("lfs_emubd_destroy -> %d", 0);
211 return 0;
212 }
213
214
215 // powerloss hook
lfs_emubd_powerloss(const struct lfs_config * cfg)216 static int lfs_emubd_powerloss(const struct lfs_config *cfg) {
217 lfs_emubd_t *bd = cfg->context;
218
219 // emulate out-of-order writes?
220 lfs_emubd_block_t *ooo_data = NULL;
221 if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
222 && bd->ooo_block != -1) {
223 // since writes between syncs are allowed to be out-of-order, it
224 // shouldn't hurt to restore the first write on powerloss, right?
225 ooo_data = bd->blocks[bd->ooo_block];
226 bd->blocks[bd->ooo_block] = lfs_emubd_incblock(bd->ooo_data);
227
228 // mirror to disk file?
229 if (bd->disk
230 && (bd->blocks[bd->ooo_block]
231 || bd->cfg->erase_value != -1)) {
232 off_t res1 = lseek(bd->disk->fd,
233 (off_t)bd->ooo_block*bd->cfg->erase_size,
234 SEEK_SET);
235 if (res1 < 0) {
236 return -errno;
237 }
238
239 ssize_t res2 = write(bd->disk->fd,
240 (bd->blocks[bd->ooo_block])
241 ? bd->blocks[bd->ooo_block]->data
242 : bd->disk->scratch,
243 bd->cfg->erase_size);
244 if (res2 < 0) {
245 return -errno;
246 }
247 }
248 }
249
250 // simulate power loss
251 bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
252
253 // if we continue, undo out-of-order write emulation
254 if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
255 && bd->ooo_block != -1) {
256 lfs_emubd_decblock(bd->blocks[bd->ooo_block]);
257 bd->blocks[bd->ooo_block] = ooo_data;
258
259 // mirror to disk file?
260 if (bd->disk
261 && (bd->blocks[bd->ooo_block]
262 || bd->cfg->erase_value != -1)) {
263 off_t res1 = lseek(bd->disk->fd,
264 (off_t)bd->ooo_block*bd->cfg->erase_size,
265 SEEK_SET);
266 if (res1 < 0) {
267 return -errno;
268 }
269
270 ssize_t res2 = write(bd->disk->fd,
271 (bd->blocks[bd->ooo_block])
272 ? bd->blocks[bd->ooo_block]->data
273 : bd->disk->scratch,
274 bd->cfg->erase_size);
275 if (res2 < 0) {
276 return -errno;
277 }
278 }
279 }
280
281 return 0;
282 }
283
284
285 // block device API
286
lfs_emubd_read(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)287 int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
288 lfs_off_t off, void *buffer, lfs_size_t size) {
289 LFS_EMUBD_TRACE("lfs_emubd_read(%p, "
290 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
291 (void*)cfg, block, off, buffer, size);
292 lfs_emubd_t *bd = cfg->context;
293
294 // check if read is valid
295 LFS_ASSERT(block < bd->cfg->erase_count);
296 LFS_ASSERT(off % bd->cfg->read_size == 0);
297 LFS_ASSERT(size % bd->cfg->read_size == 0);
298 LFS_ASSERT(off+size <= bd->cfg->erase_size);
299
300 // get the block
301 const lfs_emubd_block_t *b = bd->blocks[block];
302 if (b) {
303 // block bad?
304 if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles &&
305 bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) {
306 LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT);
307 return LFS_ERR_CORRUPT;
308 }
309
310 // read data
311 memcpy(buffer, &b->data[off], size);
312 } else {
313 // zero for consistency
314 memset(buffer,
315 (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
316 size);
317 }
318
319 // track reads
320 bd->readed += size;
321 if (bd->cfg->read_sleep) {
322 int err = nanosleep(&(struct timespec){
323 .tv_sec=bd->cfg->read_sleep/1000000000,
324 .tv_nsec=bd->cfg->read_sleep%1000000000},
325 NULL);
326 if (err) {
327 err = -errno;
328 LFS_EMUBD_TRACE("lfs_emubd_read -> %d", err);
329 return err;
330 }
331 }
332
333 LFS_EMUBD_TRACE("lfs_emubd_read -> %d", 0);
334 return 0;
335 }
336
lfs_emubd_prog(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)337 int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
338 lfs_off_t off, const void *buffer, lfs_size_t size) {
339 LFS_EMUBD_TRACE("lfs_emubd_prog(%p, "
340 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
341 (void*)cfg, block, off, buffer, size);
342 lfs_emubd_t *bd = cfg->context;
343
344 // check if write is valid
345 LFS_ASSERT(block < bd->cfg->erase_count);
346 LFS_ASSERT(off % bd->cfg->prog_size == 0);
347 LFS_ASSERT(size % bd->cfg->prog_size == 0);
348 LFS_ASSERT(off+size <= bd->cfg->erase_size);
349
350 // get the block
351 lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
352 if (!b) {
353 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
354 return LFS_ERR_NOMEM;
355 }
356
357 // block bad?
358 if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) {
359 if (bd->cfg->badblock_behavior ==
360 LFS_EMUBD_BADBLOCK_PROGERROR) {
361 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT);
362 return LFS_ERR_CORRUPT;
363 } else if (bd->cfg->badblock_behavior ==
364 LFS_EMUBD_BADBLOCK_PROGNOOP ||
365 bd->cfg->badblock_behavior ==
366 LFS_EMUBD_BADBLOCK_ERASENOOP) {
367 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
368 return 0;
369 }
370 }
371
372 // were we erased properly?
373 if (bd->cfg->erase_value != -1) {
374 for (lfs_off_t i = 0; i < size; i++) {
375 LFS_ASSERT(b->data[off+i] == bd->cfg->erase_value);
376 }
377 }
378
379 // prog data
380 memcpy(&b->data[off], buffer, size);
381
382 // mirror to disk file?
383 if (bd->disk) {
384 off_t res1 = lseek(bd->disk->fd,
385 (off_t)block*bd->cfg->erase_size + (off_t)off,
386 SEEK_SET);
387 if (res1 < 0) {
388 int err = -errno;
389 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
390 return err;
391 }
392
393 ssize_t res2 = write(bd->disk->fd, buffer, size);
394 if (res2 < 0) {
395 int err = -errno;
396 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
397 return err;
398 }
399 }
400
401 // track progs
402 bd->proged += size;
403 if (bd->cfg->prog_sleep) {
404 int err = nanosleep(&(struct timespec){
405 .tv_sec=bd->cfg->prog_sleep/1000000000,
406 .tv_nsec=bd->cfg->prog_sleep%1000000000},
407 NULL);
408 if (err) {
409 err = -errno;
410 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
411 return err;
412 }
413 }
414
415 // lose power?
416 if (bd->power_cycles > 0) {
417 bd->power_cycles -= 1;
418 if (bd->power_cycles == 0) {
419 int err = lfs_emubd_powerloss(cfg);
420 if (err) {
421 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
422 return err;
423 }
424 }
425 }
426
427 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
428 return 0;
429 }
430
lfs_emubd_erase(const struct lfs_config * cfg,lfs_block_t block)431 int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
432 LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
433 (void*)cfg, block, ((lfs_emubd_t*)cfg->context)->cfg->erase_size);
434 lfs_emubd_t *bd = cfg->context;
435
436 // check if erase is valid
437 LFS_ASSERT(block < bd->cfg->erase_count);
438
439 // emulate out-of-order writes? save first write
440 if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
441 && bd->ooo_block == -1) {
442 bd->ooo_block = block;
443 bd->ooo_data = lfs_emubd_incblock(bd->blocks[block]);
444 }
445
446 // get the block
447 lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
448 if (!b) {
449 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_NOMEM);
450 return LFS_ERR_NOMEM;
451 }
452
453 // block bad?
454 if (bd->cfg->erase_cycles) {
455 if (b->wear >= bd->cfg->erase_cycles) {
456 if (bd->cfg->badblock_behavior ==
457 LFS_EMUBD_BADBLOCK_ERASEERROR) {
458 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT);
459 return LFS_ERR_CORRUPT;
460 } else if (bd->cfg->badblock_behavior ==
461 LFS_EMUBD_BADBLOCK_ERASENOOP) {
462 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
463 return 0;
464 }
465 } else {
466 // mark wear
467 b->wear += 1;
468 }
469 }
470
471 // emulate an erase value?
472 if (bd->cfg->erase_value != -1) {
473 memset(b->data, bd->cfg->erase_value, bd->cfg->erase_size);
474
475 // mirror to disk file?
476 if (bd->disk) {
477 off_t res1 = lseek(bd->disk->fd,
478 (off_t)block*bd->cfg->erase_size,
479 SEEK_SET);
480 if (res1 < 0) {
481 int err = -errno;
482 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
483 return err;
484 }
485
486 ssize_t res2 = write(bd->disk->fd,
487 bd->disk->scratch,
488 bd->cfg->erase_size);
489 if (res2 < 0) {
490 int err = -errno;
491 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
492 return err;
493 }
494 }
495 }
496
497 // track erases
498 bd->erased += bd->cfg->erase_size;
499 if (bd->cfg->erase_sleep) {
500 int err = nanosleep(&(struct timespec){
501 .tv_sec=bd->cfg->erase_sleep/1000000000,
502 .tv_nsec=bd->cfg->erase_sleep%1000000000},
503 NULL);
504 if (err) {
505 err = -errno;
506 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
507 return err;
508 }
509 }
510
511 // lose power?
512 if (bd->power_cycles > 0) {
513 bd->power_cycles -= 1;
514 if (bd->power_cycles == 0) {
515 int err = lfs_emubd_powerloss(cfg);
516 if (err) {
517 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
518 return err;
519 }
520 }
521 }
522
523 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
524 return 0;
525 }
526
lfs_emubd_sync(const struct lfs_config * cfg)527 int lfs_emubd_sync(const struct lfs_config *cfg) {
528 LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg);
529 lfs_emubd_t *bd = cfg->context;
530
531 // emulate out-of-order writes? reset first write, writes
532 // cannot be out-of-order across sync
533 if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) {
534 lfs_emubd_decblock(bd->ooo_data);
535 bd->ooo_block = -1;
536 bd->ooo_data = NULL;
537 }
538
539 LFS_EMUBD_TRACE("lfs_emubd_sync -> %d", 0);
540 return 0;
541 }
542
543
544 /// Additional extended API for driving test features ///
545
lfs_emubd_crc_(const struct lfs_config * cfg,lfs_block_t block,uint32_t * crc)546 static int lfs_emubd_crc_(const struct lfs_config *cfg,
547 lfs_block_t block, uint32_t *crc) {
548 lfs_emubd_t *bd = cfg->context;
549
550 // check if crc is valid
551 LFS_ASSERT(block < cfg->block_count);
552
553 // crc the block
554 uint32_t crc_ = 0xffffffff;
555 const lfs_emubd_block_t *b = bd->blocks[block];
556 if (b) {
557 crc_ = lfs_crc(crc_, b->data, cfg->block_size);
558 } else {
559 uint8_t erase_value = (bd->cfg->erase_value != -1)
560 ? bd->cfg->erase_value
561 : 0;
562 for (lfs_size_t i = 0; i < cfg->block_size; i++) {
563 crc_ = lfs_crc(crc_, &erase_value, 1);
564 }
565 }
566 *crc = 0xffffffff ^ crc_;
567
568 return 0;
569 }
570
lfs_emubd_crc(const struct lfs_config * cfg,lfs_block_t block,uint32_t * crc)571 int lfs_emubd_crc(const struct lfs_config *cfg,
572 lfs_block_t block, uint32_t *crc) {
573 LFS_EMUBD_TRACE("lfs_emubd_crc(%p, %"PRIu32", %p)",
574 (void*)cfg, block, crc);
575 int err = lfs_emubd_crc_(cfg, block, crc);
576 LFS_EMUBD_TRACE("lfs_emubd_crc -> %d", err);
577 return err;
578 }
579
lfs_emubd_bdcrc(const struct lfs_config * cfg,uint32_t * crc)580 int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc) {
581 LFS_EMUBD_TRACE("lfs_emubd_bdcrc(%p, %p)", (void*)cfg, crc);
582
583 uint32_t crc_ = 0xffffffff;
584 for (lfs_block_t i = 0; i < cfg->block_count; i++) {
585 uint32_t i_crc;
586 int err = lfs_emubd_crc_(cfg, i, &i_crc);
587 if (err) {
588 LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", err);
589 return err;
590 }
591
592 crc_ = lfs_crc(crc_, &i_crc, sizeof(uint32_t));
593 }
594 *crc = 0xffffffff ^ crc_;
595
596 LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", 0);
597 return 0;
598 }
599
lfs_emubd_readed(const struct lfs_config * cfg)600 lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg) {
601 LFS_EMUBD_TRACE("lfs_emubd_readed(%p)", (void*)cfg);
602 lfs_emubd_t *bd = cfg->context;
603 LFS_EMUBD_TRACE("lfs_emubd_readed -> %"PRIu64, bd->readed);
604 return bd->readed;
605 }
606
lfs_emubd_proged(const struct lfs_config * cfg)607 lfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg) {
608 LFS_EMUBD_TRACE("lfs_emubd_proged(%p)", (void*)cfg);
609 lfs_emubd_t *bd = cfg->context;
610 LFS_EMUBD_TRACE("lfs_emubd_proged -> %"PRIu64, bd->proged);
611 return bd->proged;
612 }
613
lfs_emubd_erased(const struct lfs_config * cfg)614 lfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg) {
615 LFS_EMUBD_TRACE("lfs_emubd_erased(%p)", (void*)cfg);
616 lfs_emubd_t *bd = cfg->context;
617 LFS_EMUBD_TRACE("lfs_emubd_erased -> %"PRIu64, bd->erased);
618 return bd->erased;
619 }
620
lfs_emubd_setreaded(const struct lfs_config * cfg,lfs_emubd_io_t readed)621 int lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed) {
622 LFS_EMUBD_TRACE("lfs_emubd_setreaded(%p, %"PRIu64")", (void*)cfg, readed);
623 lfs_emubd_t *bd = cfg->context;
624 bd->readed = readed;
625 LFS_EMUBD_TRACE("lfs_emubd_setreaded -> %d", 0);
626 return 0;
627 }
628
lfs_emubd_setproged(const struct lfs_config * cfg,lfs_emubd_io_t proged)629 int lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged) {
630 LFS_EMUBD_TRACE("lfs_emubd_setproged(%p, %"PRIu64")", (void*)cfg, proged);
631 lfs_emubd_t *bd = cfg->context;
632 bd->proged = proged;
633 LFS_EMUBD_TRACE("lfs_emubd_setproged -> %d", 0);
634 return 0;
635 }
636
lfs_emubd_seterased(const struct lfs_config * cfg,lfs_emubd_io_t erased)637 int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased) {
638 LFS_EMUBD_TRACE("lfs_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased);
639 lfs_emubd_t *bd = cfg->context;
640 bd->erased = erased;
641 LFS_EMUBD_TRACE("lfs_emubd_seterased -> %d", 0);
642 return 0;
643 }
644
lfs_emubd_wear(const struct lfs_config * cfg,lfs_block_t block)645 lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
646 lfs_block_t block) {
647 LFS_EMUBD_TRACE("lfs_emubd_wear(%p, %"PRIu32")", (void*)cfg, block);
648 lfs_emubd_t *bd = cfg->context;
649
650 // check if block is valid
651 LFS_ASSERT(block < bd->cfg->erase_count);
652
653 // get the wear
654 lfs_emubd_wear_t wear;
655 const lfs_emubd_block_t *b = bd->blocks[block];
656 if (b) {
657 wear = b->wear;
658 } else {
659 wear = 0;
660 }
661
662 LFS_EMUBD_TRACE("lfs_emubd_wear -> %"PRIi32, wear);
663 return wear;
664 }
665
lfs_emubd_setwear(const struct lfs_config * cfg,lfs_block_t block,lfs_emubd_wear_t wear)666 int lfs_emubd_setwear(const struct lfs_config *cfg,
667 lfs_block_t block, lfs_emubd_wear_t wear) {
668 LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32", %"PRIi32")",
669 (void*)cfg, block, wear);
670 lfs_emubd_t *bd = cfg->context;
671
672 // check if block is valid
673 LFS_ASSERT(block < bd->cfg->erase_count);
674
675 // set the wear
676 lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
677 if (!b) {
678 LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM);
679 return LFS_ERR_NOMEM;
680 }
681 b->wear = wear;
682
683 LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0);
684 return 0;
685 }
686
lfs_emubd_powercycles(const struct lfs_config * cfg)687 lfs_emubd_spowercycles_t lfs_emubd_powercycles(
688 const struct lfs_config *cfg) {
689 LFS_EMUBD_TRACE("lfs_emubd_powercycles(%p)", (void*)cfg);
690 lfs_emubd_t *bd = cfg->context;
691
692 LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %"PRIi32, bd->power_cycles);
693 return bd->power_cycles;
694 }
695
lfs_emubd_setpowercycles(const struct lfs_config * cfg,lfs_emubd_powercycles_t power_cycles)696 int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
697 lfs_emubd_powercycles_t power_cycles) {
698 LFS_EMUBD_TRACE("lfs_emubd_setpowercycles(%p, %"PRIi32")",
699 (void*)cfg, power_cycles);
700 lfs_emubd_t *bd = cfg->context;
701
702 bd->power_cycles = power_cycles;
703
704 LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %d", 0);
705 return 0;
706 }
707
lfs_emubd_copy(const struct lfs_config * cfg,lfs_emubd_t * copy)708 int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
709 LFS_EMUBD_TRACE("lfs_emubd_copy(%p, %p)", (void*)cfg, (void*)copy);
710 lfs_emubd_t *bd = cfg->context;
711
712 // lazily copy over our block array
713 copy->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
714 if (!copy->blocks) {
715 LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM);
716 return LFS_ERR_NOMEM;
717 }
718
719 for (size_t i = 0; i < bd->cfg->erase_count; i++) {
720 copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]);
721 }
722
723 // other state
724 copy->readed = bd->readed;
725 copy->proged = bd->proged;
726 copy->erased = bd->erased;
727 copy->power_cycles = bd->power_cycles;
728 copy->ooo_block = bd->ooo_block;
729 copy->ooo_data = lfs_emubd_incblock(bd->ooo_data);
730 copy->disk = bd->disk;
731 if (copy->disk) {
732 copy->disk->rc += 1;
733 }
734 copy->cfg = bd->cfg;
735
736 LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", 0);
737 return 0;
738 }
739
740