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->disk = NULL;
133
134 if (bd->cfg->disk_path) {
135 bd->disk = malloc(sizeof(lfs_emubd_disk_t));
136 if (!bd->disk) {
137 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
138 return LFS_ERR_NOMEM;
139 }
140 bd->disk->rc = 1;
141 bd->disk->scratch = NULL;
142
143 #ifdef _WIN32
144 bd->disk->fd = open(bd->cfg->disk_path,
145 O_RDWR | O_CREAT | O_BINARY, 0666);
146 #else
147 bd->disk->fd = open(bd->cfg->disk_path,
148 O_RDWR | O_CREAT, 0666);
149 #endif
150 if (bd->disk->fd < 0) {
151 int err = -errno;
152 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
153 return err;
154 }
155
156 // if we're emulating erase values, we can keep a block around in
157 // memory of just the erase state to speed up emulated erases
158 if (bd->cfg->erase_value != -1) {
159 bd->disk->scratch = malloc(bd->cfg->erase_size);
160 if (!bd->disk->scratch) {
161 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
162 return LFS_ERR_NOMEM;
163 }
164 memset(bd->disk->scratch,
165 bd->cfg->erase_value,
166 bd->cfg->erase_size);
167
168 // go ahead and erase all of the disk, otherwise the file will not
169 // match our internal representation
170 for (size_t i = 0; i < bd->cfg->erase_count; i++) {
171 ssize_t res = write(bd->disk->fd,
172 bd->disk->scratch,
173 bd->cfg->erase_size);
174 if (res < 0) {
175 int err = -errno;
176 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
177 return err;
178 }
179 }
180 }
181 }
182
183 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", 0);
184 return 0;
185 }
186
lfs_emubd_destroy(const struct lfs_config * cfg)187 int lfs_emubd_destroy(const struct lfs_config *cfg) {
188 LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg);
189 lfs_emubd_t *bd = cfg->context;
190
191 // decrement reference counts
192 for (lfs_block_t i = 0; i < bd->cfg->erase_count; i++) {
193 lfs_emubd_decblock(bd->blocks[i]);
194 }
195 free(bd->blocks);
196
197 // clean up other resources
198 if (bd->disk) {
199 bd->disk->rc -= 1;
200 if (bd->disk->rc == 0) {
201 close(bd->disk->fd);
202 free(bd->disk->scratch);
203 free(bd->disk);
204 }
205 }
206
207 LFS_EMUBD_TRACE("lfs_emubd_destroy -> %d", 0);
208 return 0;
209 }
210
211
212
213 // block device API
214
lfs_emubd_read(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)215 int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
216 lfs_off_t off, void *buffer, lfs_size_t size) {
217 LFS_EMUBD_TRACE("lfs_emubd_read(%p, "
218 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
219 (void*)cfg, block, off, buffer, size);
220 lfs_emubd_t *bd = cfg->context;
221
222 // check if read is valid
223 LFS_ASSERT(block < bd->cfg->erase_count);
224 LFS_ASSERT(off % bd->cfg->read_size == 0);
225 LFS_ASSERT(size % bd->cfg->read_size == 0);
226 LFS_ASSERT(off+size <= bd->cfg->erase_size);
227
228 // get the block
229 const lfs_emubd_block_t *b = bd->blocks[block];
230 if (b) {
231 // block bad?
232 if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles &&
233 bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) {
234 LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT);
235 return LFS_ERR_CORRUPT;
236 }
237
238 // read data
239 memcpy(buffer, &b->data[off], size);
240 } else {
241 // zero for consistency
242 memset(buffer,
243 (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
244 size);
245 }
246
247 // track reads
248 bd->readed += size;
249 if (bd->cfg->read_sleep) {
250 int err = nanosleep(&(struct timespec){
251 .tv_sec=bd->cfg->read_sleep/1000000000,
252 .tv_nsec=bd->cfg->read_sleep%1000000000},
253 NULL);
254 if (err) {
255 err = -errno;
256 LFS_EMUBD_TRACE("lfs_emubd_read -> %d", err);
257 return err;
258 }
259 }
260
261 LFS_EMUBD_TRACE("lfs_emubd_read -> %d", 0);
262 return 0;
263 }
264
lfs_emubd_prog(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)265 int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
266 lfs_off_t off, const void *buffer, lfs_size_t size) {
267 LFS_EMUBD_TRACE("lfs_emubd_prog(%p, "
268 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
269 (void*)cfg, block, off, buffer, size);
270 lfs_emubd_t *bd = cfg->context;
271
272 // check if write is valid
273 LFS_ASSERT(block < bd->cfg->erase_count);
274 LFS_ASSERT(off % bd->cfg->prog_size == 0);
275 LFS_ASSERT(size % bd->cfg->prog_size == 0);
276 LFS_ASSERT(off+size <= bd->cfg->erase_size);
277
278 // get the block
279 lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
280 if (!b) {
281 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
282 return LFS_ERR_NOMEM;
283 }
284
285 // block bad?
286 if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) {
287 if (bd->cfg->badblock_behavior ==
288 LFS_EMUBD_BADBLOCK_PROGERROR) {
289 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT);
290 return LFS_ERR_CORRUPT;
291 } else if (bd->cfg->badblock_behavior ==
292 LFS_EMUBD_BADBLOCK_PROGNOOP ||
293 bd->cfg->badblock_behavior ==
294 LFS_EMUBD_BADBLOCK_ERASENOOP) {
295 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
296 return 0;
297 }
298 }
299
300 // were we erased properly?
301 if (bd->cfg->erase_value != -1) {
302 for (lfs_off_t i = 0; i < size; i++) {
303 LFS_ASSERT(b->data[off+i] == bd->cfg->erase_value);
304 }
305 }
306
307 // prog data
308 memcpy(&b->data[off], buffer, size);
309
310 // mirror to disk file?
311 if (bd->disk) {
312 off_t res1 = lseek(bd->disk->fd,
313 (off_t)block*bd->cfg->erase_size + (off_t)off,
314 SEEK_SET);
315 if (res1 < 0) {
316 int err = -errno;
317 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
318 return err;
319 }
320
321 ssize_t res2 = write(bd->disk->fd, buffer, size);
322 if (res2 < 0) {
323 int err = -errno;
324 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
325 return err;
326 }
327 }
328
329 // track progs
330 bd->proged += size;
331 if (bd->cfg->prog_sleep) {
332 int err = nanosleep(&(struct timespec){
333 .tv_sec=bd->cfg->prog_sleep/1000000000,
334 .tv_nsec=bd->cfg->prog_sleep%1000000000},
335 NULL);
336 if (err) {
337 err = -errno;
338 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
339 return err;
340 }
341 }
342
343 // lose power?
344 if (bd->power_cycles > 0) {
345 bd->power_cycles -= 1;
346 if (bd->power_cycles == 0) {
347 // simulate power loss
348 bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
349 }
350 }
351
352 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
353 return 0;
354 }
355
lfs_emubd_erase(const struct lfs_config * cfg,lfs_block_t block)356 int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
357 LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
358 (void*)cfg, block, ((lfs_emubd_t*)cfg->context)->cfg->erase_size);
359 lfs_emubd_t *bd = cfg->context;
360
361 // check if erase is valid
362 LFS_ASSERT(block < bd->cfg->erase_count);
363
364 // get the block
365 lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
366 if (!b) {
367 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
368 return LFS_ERR_NOMEM;
369 }
370
371 // block bad?
372 if (bd->cfg->erase_cycles) {
373 if (b->wear >= bd->cfg->erase_cycles) {
374 if (bd->cfg->badblock_behavior ==
375 LFS_EMUBD_BADBLOCK_ERASEERROR) {
376 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT);
377 return LFS_ERR_CORRUPT;
378 } else if (bd->cfg->badblock_behavior ==
379 LFS_EMUBD_BADBLOCK_ERASENOOP) {
380 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
381 return 0;
382 }
383 } else {
384 // mark wear
385 b->wear += 1;
386 }
387 }
388
389 // emulate an erase value?
390 if (bd->cfg->erase_value != -1) {
391 memset(b->data, bd->cfg->erase_value, bd->cfg->erase_size);
392
393 // mirror to disk file?
394 if (bd->disk) {
395 off_t res1 = lseek(bd->disk->fd,
396 (off_t)block*bd->cfg->erase_size,
397 SEEK_SET);
398 if (res1 < 0) {
399 int err = -errno;
400 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
401 return err;
402 }
403
404 ssize_t res2 = write(bd->disk->fd,
405 bd->disk->scratch,
406 bd->cfg->erase_size);
407 if (res2 < 0) {
408 int err = -errno;
409 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
410 return err;
411 }
412 }
413 }
414
415 // track erases
416 bd->erased += bd->cfg->erase_size;
417 if (bd->cfg->erase_sleep) {
418 int err = nanosleep(&(struct timespec){
419 .tv_sec=bd->cfg->erase_sleep/1000000000,
420 .tv_nsec=bd->cfg->erase_sleep%1000000000},
421 NULL);
422 if (err) {
423 err = -errno;
424 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
425 return err;
426 }
427 }
428
429 // lose power?
430 if (bd->power_cycles > 0) {
431 bd->power_cycles -= 1;
432 if (bd->power_cycles == 0) {
433 // simulate power loss
434 bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
435 }
436 }
437
438 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
439 return 0;
440 }
441
lfs_emubd_sync(const struct lfs_config * cfg)442 int lfs_emubd_sync(const struct lfs_config *cfg) {
443 LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg);
444
445 // do nothing
446 (void)cfg;
447
448 LFS_EMUBD_TRACE("lfs_emubd_sync -> %d", 0);
449 return 0;
450 }
451
452 /// Additional extended API for driving test features ///
453
lfs_emubd_rawcrc(const struct lfs_config * cfg,lfs_block_t block,uint32_t * crc)454 static int lfs_emubd_rawcrc(const struct lfs_config *cfg,
455 lfs_block_t block, uint32_t *crc) {
456 lfs_emubd_t *bd = cfg->context;
457
458 // check if crc is valid
459 LFS_ASSERT(block < cfg->block_count);
460
461 // crc the block
462 uint32_t crc_ = 0xffffffff;
463 const lfs_emubd_block_t *b = bd->blocks[block];
464 if (b) {
465 crc_ = lfs_crc(crc_, b->data, cfg->block_size);
466 } else {
467 uint8_t erase_value = (bd->cfg->erase_value != -1)
468 ? bd->cfg->erase_value
469 : 0;
470 for (lfs_size_t i = 0; i < cfg->block_size; i++) {
471 crc_ = lfs_crc(crc_, &erase_value, 1);
472 }
473 }
474 *crc = 0xffffffff ^ crc_;
475
476 return 0;
477 }
478
lfs_emubd_crc(const struct lfs_config * cfg,lfs_block_t block,uint32_t * crc)479 int lfs_emubd_crc(const struct lfs_config *cfg,
480 lfs_block_t block, uint32_t *crc) {
481 LFS_EMUBD_TRACE("lfs_emubd_crc(%p, %"PRIu32", %p)",
482 (void*)cfg, block, crc);
483 int err = lfs_emubd_rawcrc(cfg, block, crc);
484 LFS_EMUBD_TRACE("lfs_emubd_crc -> %d", err);
485 return err;
486 }
487
lfs_emubd_bdcrc(const struct lfs_config * cfg,uint32_t * crc)488 int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc) {
489 LFS_EMUBD_TRACE("lfs_emubd_bdcrc(%p, %p)", (void*)cfg, crc);
490
491 uint32_t crc_ = 0xffffffff;
492 for (lfs_block_t i = 0; i < cfg->block_count; i++) {
493 uint32_t i_crc;
494 int err = lfs_emubd_rawcrc(cfg, i, &i_crc);
495 if (err) {
496 LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", err);
497 return err;
498 }
499
500 crc_ = lfs_crc(crc_, &i_crc, sizeof(uint32_t));
501 }
502 *crc = 0xffffffff ^ crc_;
503
504 LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", 0);
505 return 0;
506 }
507
lfs_emubd_readed(const struct lfs_config * cfg)508 lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg) {
509 LFS_EMUBD_TRACE("lfs_emubd_readed(%p)", (void*)cfg);
510 lfs_emubd_t *bd = cfg->context;
511 LFS_EMUBD_TRACE("lfs_emubd_readed -> %"PRIu64, bd->readed);
512 return bd->readed;
513 }
514
lfs_emubd_proged(const struct lfs_config * cfg)515 lfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg) {
516 LFS_EMUBD_TRACE("lfs_emubd_proged(%p)", (void*)cfg);
517 lfs_emubd_t *bd = cfg->context;
518 LFS_EMUBD_TRACE("lfs_emubd_proged -> %"PRIu64, bd->proged);
519 return bd->proged;
520 }
521
lfs_emubd_erased(const struct lfs_config * cfg)522 lfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg) {
523 LFS_EMUBD_TRACE("lfs_emubd_erased(%p)", (void*)cfg);
524 lfs_emubd_t *bd = cfg->context;
525 LFS_EMUBD_TRACE("lfs_emubd_erased -> %"PRIu64, bd->erased);
526 return bd->erased;
527 }
528
lfs_emubd_setreaded(const struct lfs_config * cfg,lfs_emubd_io_t readed)529 int lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed) {
530 LFS_EMUBD_TRACE("lfs_emubd_setreaded(%p, %"PRIu64")", (void*)cfg, readed);
531 lfs_emubd_t *bd = cfg->context;
532 bd->readed = readed;
533 LFS_EMUBD_TRACE("lfs_emubd_setreaded -> %d", 0);
534 return 0;
535 }
536
lfs_emubd_setproged(const struct lfs_config * cfg,lfs_emubd_io_t proged)537 int lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged) {
538 LFS_EMUBD_TRACE("lfs_emubd_setproged(%p, %"PRIu64")", (void*)cfg, proged);
539 lfs_emubd_t *bd = cfg->context;
540 bd->proged = proged;
541 LFS_EMUBD_TRACE("lfs_emubd_setproged -> %d", 0);
542 return 0;
543 }
544
lfs_emubd_seterased(const struct lfs_config * cfg,lfs_emubd_io_t erased)545 int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased) {
546 LFS_EMUBD_TRACE("lfs_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased);
547 lfs_emubd_t *bd = cfg->context;
548 bd->erased = erased;
549 LFS_EMUBD_TRACE("lfs_emubd_seterased -> %d", 0);
550 return 0;
551 }
552
lfs_emubd_wear(const struct lfs_config * cfg,lfs_block_t block)553 lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
554 lfs_block_t block) {
555 LFS_EMUBD_TRACE("lfs_emubd_wear(%p, %"PRIu32")", (void*)cfg, block);
556 lfs_emubd_t *bd = cfg->context;
557
558 // check if block is valid
559 LFS_ASSERT(block < bd->cfg->erase_count);
560
561 // get the wear
562 lfs_emubd_wear_t wear;
563 const lfs_emubd_block_t *b = bd->blocks[block];
564 if (b) {
565 wear = b->wear;
566 } else {
567 wear = 0;
568 }
569
570 LFS_EMUBD_TRACE("lfs_emubd_wear -> %"PRIi32, wear);
571 return wear;
572 }
573
lfs_emubd_setwear(const struct lfs_config * cfg,lfs_block_t block,lfs_emubd_wear_t wear)574 int lfs_emubd_setwear(const struct lfs_config *cfg,
575 lfs_block_t block, lfs_emubd_wear_t wear) {
576 LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32", %"PRIi32")",
577 (void*)cfg, block, wear);
578 lfs_emubd_t *bd = cfg->context;
579
580 // check if block is valid
581 LFS_ASSERT(block < bd->cfg->erase_count);
582
583 // set the wear
584 lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
585 if (!b) {
586 LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM);
587 return LFS_ERR_NOMEM;
588 }
589 b->wear = wear;
590
591 LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0);
592 return 0;
593 }
594
lfs_emubd_powercycles(const struct lfs_config * cfg)595 lfs_emubd_spowercycles_t lfs_emubd_powercycles(
596 const struct lfs_config *cfg) {
597 LFS_EMUBD_TRACE("lfs_emubd_powercycles(%p)", (void*)cfg);
598 lfs_emubd_t *bd = cfg->context;
599
600 LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %"PRIi32, bd->power_cycles);
601 return bd->power_cycles;
602 }
603
lfs_emubd_setpowercycles(const struct lfs_config * cfg,lfs_emubd_powercycles_t power_cycles)604 int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
605 lfs_emubd_powercycles_t power_cycles) {
606 LFS_EMUBD_TRACE("lfs_emubd_setpowercycles(%p, %"PRIi32")",
607 (void*)cfg, power_cycles);
608 lfs_emubd_t *bd = cfg->context;
609
610 bd->power_cycles = power_cycles;
611
612 LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %d", 0);
613 return 0;
614 }
615
lfs_emubd_copy(const struct lfs_config * cfg,lfs_emubd_t * copy)616 int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
617 LFS_EMUBD_TRACE("lfs_emubd_copy(%p, %p)", (void*)cfg, (void*)copy);
618 lfs_emubd_t *bd = cfg->context;
619
620 // lazily copy over our block array
621 copy->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
622 if (!copy->blocks) {
623 LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM);
624 return LFS_ERR_NOMEM;
625 }
626
627 for (size_t i = 0; i < bd->cfg->erase_count; i++) {
628 copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]);
629 }
630
631 // other state
632 copy->readed = bd->readed;
633 copy->proged = bd->proged;
634 copy->erased = bd->erased;
635 copy->power_cycles = bd->power_cycles;
636 copy->disk = bd->disk;
637 if (copy->disk) {
638 copy->disk->rc += 1;
639 }
640 copy->cfg = bd->cfg;
641
642 LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", 0);
643 return 0;
644 }
645
646