STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
emmc_log.c
Go to the documentation of this file.
1#include "emmc_log.h"
2
3#include <stddef.h>
4#include <string.h>
5
6/* ---- little-endian helpers ------------------------------------------- */
7
8static void put16(uint8_t* p, uint16_t v) {
9 p[0] = (uint8_t)v;
10 p[1] = (uint8_t)(v >> 8);
11}
12
13static void put32(uint8_t* p, uint32_t v) {
14 p[0] = (uint8_t)v;
15 p[1] = (uint8_t)(v >> 8);
16 p[2] = (uint8_t)(v >> 16);
17 p[3] = (uint8_t)(v >> 24);
18}
19
20static uint16_t get16(const uint8_t* p) {
21 return (uint16_t)(p[0] | (p[1] << 8));
22}
23
24static uint32_t get32(const uint8_t* p) {
25 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) |
26 ((uint32_t)p[3] << 24);
27}
28
29/* ---- CRC-32, four bits at a time (16-entry table, no 1 KB table) ------ */
30
31uint32_t emmc_log_crc32(const void* data, uint32_t len) {
32 static const uint32_t table[16] = {
33 0x00000000UL, 0x1DB71064UL, 0x3B6E20C8UL, 0x26D930ACUL,
34 0x76DC4190UL, 0x6B6B51F4UL, 0x4DB26158UL, 0x5005713CUL,
35 0xEDB88320UL, 0xF00F9344UL, 0xD6D6A3E8UL, 0xCB61B38CUL,
36 0x9B64C2B0UL, 0x86D3D2D4UL, 0xA00AE278UL, 0xBDBDF21CUL};
37 const uint8_t* p = (const uint8_t*)data;
38 uint32_t crc = 0xFFFFFFFFUL;
39 for (uint32_t i = 0; i < len; ++i) {
40 crc ^= p[i];
41 crc = (crc >> 4) ^ table[crc & 0x0FU];
42 crc = (crc >> 4) ^ table[crc & 0x0FU];
43 }
44 return crc ^ 0xFFFFFFFFUL;
45}
46
47/* ---- locking ------------------------------------------------------------ */
48
49static void lock(emmc_log_t* log) {
50 if (log->port.lock) {
51 log->port.lock(log->context);
52 }
53}
54
55static void unlock(emmc_log_t* log) {
56 if (log->port.unlock) {
57 log->port.unlock(log->context);
58 }
59}
60
61/* ---- producer side (caller holds the lock) ----------------------------- */
62
63static uint8_t* block_at(emmc_log_t* log, uint8_t chunk, uint8_t block) {
64 return log->chunks[chunk].bytes + (uint32_t)block * EMMC_BLOCK_SIZE;
65}
66
67static void chunk_complete(emmc_log_t* log) {
69 log->has_fill = false;
70 log->fill_chunk = (uint8_t)((log->fill_chunk + 1U) % log->chunk_count);
71}
72
73static void close_block(emmc_log_t* log) {
74 uint8_t* b = block_at(log, log->fill_chunk, log->fill_block);
75 b[16] = (uint8_t)EMMC_LOG_KIND_DATA;
76 put16(b + 18, log->fill_offset);
77 log->fill_block++;
78 log->fill_offset = 0;
80 chunk_complete(log);
81 }
82}
83
84static void seal(emmc_log_t* log) {
85 if (!log->has_fill) {
86 return;
87 }
88 if (log->fill_offset > 0U) {
89 close_block(log);
90 if (!log->has_fill) {
91 return;
92 }
93 }
94 /* Remaining blocks were zeroed when the chunk was acquired. */
95 while (log->fill_block < EMMC_LOG_CHUNK_BLOCKS) {
96 block_at(log, log->fill_chunk, log->fill_block)[16] =
97 (uint8_t)EMMC_LOG_KIND_PAD;
98 log->stats.pad_blocks++;
99 log->fill_block++;
100 }
101 chunk_complete(log);
102}
103
104static bool acquire(emmc_log_t* log) {
105 uint8_t c = log->fill_chunk;
106 if (log->chunk_state[c] != EMMC_CHUNK_FREE) {
107 return false;
108 }
109 memset(log->chunks[c].bytes, 0, EMMC_LOG_CHUNK_BYTES);
111 log->chunk_arm[c] = log->arm_index;
112 log->has_fill = true;
113 log->fill_block = 0;
114 log->fill_offset = 0;
115 log->fill_started_ms = 0;
116 log->fill_aged = false;
117 return true;
118}
119
120static bool put_record(emmc_log_t* log, uint8_t type, uint32_t t_ms,
121 const void* data, uint8_t len) {
122 uint16_t need = (uint16_t)(EMMC_LOG_RECORD_HEADER_BYTES + len);
123 if (log->has_fill &&
124 (uint32_t)log->fill_offset + need > EMMC_LOG_PAYLOAD_BYTES) {
125 close_block(log);
126 }
127 if (!log->has_fill && !acquire(log)) {
128 return false;
129 }
130 uint8_t* p = block_at(log, log->fill_chunk, log->fill_block) +
132 p[0] = type;
133 p[1] = len;
134 put32(p + 2, t_ms);
135 if (len > 0U) {
136 memcpy(p + EMMC_LOG_RECORD_HEADER_BYTES, data, len);
137 }
138 log->fill_offset = (uint16_t)(log->fill_offset + need);
140 close_block(log);
141 }
142 return true;
143}
144
145/* Count the outcome of a record the log was asked to keep. */
146static bool keep(emmc_log_t* log, uint8_t type, uint32_t t_ms, const void* data,
147 uint8_t len) {
148 if (log->unreported_drops > 0U) {
149 uint8_t n[4];
150 put32(n, log->unreported_drops);
151 if (put_record(log, EMMC_REC_DROPPED, t_ms, n, sizeof n)) {
152 log->stats.records++;
153 log->unreported_drops = 0;
154 }
155 }
156 if (put_record(log, type, t_ms, data, len)) {
157 log->stats.records++;
158 return true;
159 }
160 log->stats.dropped++;
161 log->unreported_drops++;
162 return false;
163}
164
165/* ---- public producer API ---------------------------------------------- */
166
170 c.flush_interval_ms = 1000U;
171 c.read_budget_ms = 100U;
172 c.write_budget_ms = 1000U;
173 c.write_attempts = 3U;
174 return c;
175}
176
178 void* context, const emmc_log_config_t* config,
179 emmc_log_chunk_t* chunks, uint8_t chunk_count,
180 uint8_t* scratch) {
181 if (!log || !port || !port->start || !port->read || !port->write ||
182 !config || !chunks || !scratch || chunk_count < 2U ||
183 chunk_count > EMMC_LOG_MAX_CHUNKS || config->data_start_lba == 0U ||
184 config->flush_interval_ms == 0U || config->read_budget_ms == 0U ||
185 config->write_budget_ms == 0U || config->write_attempts == 0U ||
186 (port->lock == NULL) != (port->unlock == NULL)) {
187 return EMMC_ERR_PARAM;
188 }
189 memset(log, 0, sizeof *log);
190 log->port = *port;
191 log->context = context;
192 log->config = *config;
193 log->chunks = chunks;
194 log->chunk_count = chunk_count;
195 log->scratch = scratch;
197 return EMMC_OK;
198}
199
201 uint32_t now_ms) {
202 bool was = flight_state_logs_to_emmc(from);
203 bool is = flight_state_logs_to_emmc(to);
204 bool ok = true;
205 lock(log);
206 if (!was && is) {
207 if (log->disk == EMMC_DISK_FULL || log->disk == EMMC_DISK_FAULT) {
208 log->recording = false;
209 ok = false;
210 } else {
211 seal(log); /* a session never shares a chunk with another */
212 log->arm_index++;
213 log->recording = true;
214 log->unreported_drops = 0;
215 uint8_t s = (uint8_t)to;
216 (void)keep(log, EMMC_REC_SESSION_START, now_ms, &s, 1U);
217 }
218 } else if (was && is && from != to) {
219 if (log->recording) {
220 uint8_t s[2] = {(uint8_t)from, (uint8_t)to};
221 (void)keep(log, EMMC_REC_STATE, now_ms, s, 2U);
222 }
223 } else if (was && !is) {
224 if (log->recording) {
225 uint8_t s = (uint8_t)to;
226 (void)keep(log, EMMC_REC_SESSION_END, now_ms, &s, 1U);
227 }
228 seal(log);
229 log->recording = false;
230 }
231 unlock(log);
232 return ok;
233}
234
235bool emmc_log_append(emmc_log_t* log, uint8_t type, uint32_t t_ms,
236 const void* data, uint8_t len) {
237 if (len > 0U && !data) {
238 return false;
239 }
240 lock(log);
241 bool ok = log->recording && keep(log, type, t_ms, data, len);
242 unlock(log);
243 return ok;
244}
245
247 lock(log);
248 seal(log);
249 unlock(log);
250}
251
252uint8_t emmc_log_backlog(const emmc_log_t* log) {
253 uint8_t n = 0;
254 for (uint8_t i = 0; i < log->chunk_count; ++i) {
255 n = (uint8_t)(n + (log->chunk_state[i] == EMMC_CHUNK_READY));
256 }
257 return n;
258}
259
260/* ---- writer side --------------------------------------------------------- */
261
262static void stop(emmc_log_t* log, emmc_disk_state_t why) {
263 lock(log);
264 log->disk = why;
265 log->recording = false;
266 seal(log); /* so the writer discards it instead of it filling forever */
267 unlock(log);
268}
269
270static void release_write_chunk(emmc_log_t* log, bool written) {
271 lock(log);
273 log->write_chunk = (uint8_t)((log->write_chunk + 1U) % log->chunk_count);
274 if (written) {
275 log->stats.chunks_written++;
276 } else {
277 log->stats.chunks_lost++;
278 }
279 unlock(log);
280 log->write_tries = 0;
281}
282
283/* Read log block k and say whether it is a valid block of this volume. */
284static emmc_status_t probe(emmc_log_t* log, uint32_t k, bool* valid,
285 uint32_t* session) {
286 const uint8_t* b = log->scratch;
287 emmc_status_t st =
288 log->port.read(log->context, log->config.data_start_lba + k,
289 log->scratch, 1U, log->config.read_budget_ms);
290 if (st != EMMC_OK) {
291 return st;
292 }
293 *valid = get32(b) == EMMC_LOG_BLOCK_MAGIC && get32(b + 4) == log->epoch &&
294 get32(b + 8) == k &&
295 (b[16] == EMMC_LOG_KIND_DATA || b[16] == EMMC_LOG_KIND_PAD) &&
296 get16(b + 18) <= EMMC_LOG_PAYLOAD_BYTES &&
297 get32(b + 508) == emmc_log_crc32(b, 508U);
298 if (session) {
299 *session = get32(b + 12);
300 }
301 return EMMC_OK;
302}
303
304/* First invalid block in [lo, hi), given every block below lo is valid. */
305static emmc_status_t search(emmc_log_t* log, uint32_t lo, uint32_t hi,
306 uint32_t* end) {
307 while (lo < hi) {
308 uint32_t mid = lo + (hi - lo) / 2U;
309 bool valid = false;
310 emmc_status_t st = probe(log, mid, &valid, NULL);
311 if (st != EMMC_OK) {
312 return st;
313 }
314 if (valid) {
315 lo = mid + 1U;
316 } else {
317 hi = mid;
318 }
319 }
320 *end = lo;
321 return EMMC_OK;
322}
323
325 uint8_t* s = log->scratch;
326 uint32_t last = 0;
327 /* An old superblock (other config) or old log blocks name an epoch the
328 * new one must differ from, or stale blocks would read as valid. */
329 if (get32(s) == EMMC_LOG_SUPER_MAGIC &&
330 get32(s + 508) == emmc_log_crc32(s, 508U)) {
331 last = get32(s + 8);
332 }
333 emmc_status_t st = log->port.read(log->context, log->config.data_start_lba,
334 s, 1U, log->config.read_budget_ms);
335 if (st != EMMC_OK) {
336 return st;
337 }
338 if (get32(s) == EMMC_LOG_BLOCK_MAGIC &&
339 get32(s + 508) == emmc_log_crc32(s, 508U) && get32(s + 4) > last) {
340 last = get32(s + 4);
341 }
342 log->epoch = last + 1U == 0U ? 1U : last + 1U;
343 memset(s, 0, EMMC_BLOCK_SIZE);
346 put32(s + 8, log->epoch);
347 put32(s + 12, log->config.data_start_lba);
348 put32(s + 16, log->capacity);
349 put32(s + 508, emmc_log_crc32(s, 508U));
350 return log->port.write(log->context, 0U, s, 1U,
352}
353
355 uint32_t capacity = 0;
356 emmc_status_t st = log->port.start(log->context, &capacity);
357 if (st != EMMC_OK) {
358 return st;
359 }
360 if (capacity <= log->config.data_start_lba + EMMC_LOG_CHUNK_BLOCKS) {
361 return EMMC_ERR_PARAM;
362 }
363 log->capacity = capacity;
364
365 const uint8_t* s = log->scratch;
366 st = log->port.read(log->context, 0U, log->scratch, 1U,
368 if (st != EMMC_OK) {
369 return st;
370 }
371 if (get32(s) == EMMC_LOG_SUPER_MAGIC &&
372 get32(s + 4) == EMMC_LOG_LAYOUT_VERSION && get32(s + 8) != 0U &&
373 get32(s + 12) == log->config.data_start_lba &&
374 get32(s + 16) == capacity &&
375 get32(s + 508) == emmc_log_crc32(s, 508U)) {
376 log->epoch = get32(s + 8);
377 } else if ((st = format(log)) != EMMC_OK) {
378 return st;
379 }
380
381 /* Binary search for the end, then look one chunk past it: a torn
382 * multi-block write may leave a hole with valid blocks after it, and
383 * resuming at the hole would overwrite them. */
384 uint32_t n = capacity - log->config.data_start_lba;
385 uint32_t end = 0;
386 if ((st = search(log, 0U, n, &end)) != EMMC_OK) {
387 return st;
388 }
389 for (;;) {
390 uint32_t found = end;
391 for (uint32_t j = end + 1U; j < n && j <= end + EMMC_LOG_CHUNK_BLOCKS;
392 ++j) {
393 bool valid = false;
394 if ((st = probe(log, j, &valid, NULL)) != EMMC_OK) {
395 return st;
396 }
397 if (valid) {
398 found = j;
399 }
400 }
401 if (found == end) {
402 break;
403 }
404 if ((st = search(log, found + 1U, n, &end)) != EMMC_OK) {
405 return st;
406 }
407 }
408
409 log->base_session = 0;
410 if (end > 0U) {
411 bool valid = false;
412 if ((st = probe(log, end - 1U, &valid, &log->base_session)) !=
413 EMMC_OK) {
414 return st;
415 }
416 }
417 log->head = log->config.data_start_lba + end;
418 lock(log);
419 log->disk = EMMC_DISK_MOUNTED;
420 unlock(log);
421 return EMMC_OK;
422}
423
425 lock(log);
426 if (log->has_fill) {
427 if (!log->fill_aged) {
428 log->fill_aged = true;
429 log->fill_started_ms = now_ms;
430 } else if ((uint32_t)(now_ms - log->fill_started_ms) >=
432 seal(log);
433 }
434 }
435 uint8_t c = log->write_chunk;
436 bool ready = log->chunk_state[c] == EMMC_CHUNK_READY;
437 uint32_t arm = log->chunk_arm[c];
438 emmc_disk_state_t disk = log->disk;
439 unlock(log);
440
441 if (!ready) {
442 return EMMC_OK;
443 }
444 if (disk == EMMC_DISK_UNMOUNTED) {
445 emmc_status_t st = mount(log);
446 if (st != EMMC_OK) {
447 stop(log, EMMC_DISK_FAULT);
448 release_write_chunk(log, false);
449 return st;
450 }
451 } else if (disk != EMMC_DISK_MOUNTED) {
452 release_write_chunk(log, false);
453 return EMMC_ERR_NOT_READY;
454 }
455 if (log->head > log->capacity - EMMC_LOG_CHUNK_BLOCKS) {
456 stop(log, EMMC_DISK_FULL);
457 release_write_chunk(log, false);
458 return EMMC_ERR_NOT_READY;
459 }
460
461 uint32_t first = log->head - log->config.data_start_lba;
462 for (uint8_t i = 0; i < EMMC_LOG_CHUNK_BLOCKS; ++i) {
463 uint8_t* b = block_at(log, c, i);
465 put32(b + 4, log->epoch);
466 put32(b + 8, first + i);
467 put32(b + 12, log->base_session + arm);
468 b[17] = 0;
469 put32(b + 508, emmc_log_crc32(b, 508U));
470 }
471 emmc_status_t st =
472 log->port.write(log->context, log->head, log->chunks[c].bytes,
474 if (st == EMMC_OK) {
476 release_write_chunk(log, true);
477 return EMMC_OK;
478 }
479 log->stats.write_errors++;
480 if (++log->write_tries >= log->config.write_attempts) {
481 stop(log, EMMC_DISK_FAULT);
482 release_write_chunk(log, false);
483 }
484 return st;
485}
flight_state_t
static uint8_t chunk[64]
Definition board_uart9.c:12
static void arm(void)
Definition board_uart9.c:17
static emmc_status_t probe(emmc_log_t *log, uint32_t k, bool *valid, uint32_t *session)
Definition emmc_log.c:284
bool emmc_log_on_state(emmc_log_t *log, flight_state_t from, flight_state_t to, uint32_t now_ms)
Definition emmc_log.c:200
uint32_t emmc_log_crc32(const void *data, uint32_t len)
Definition emmc_log.c:31
emmc_status_t emmc_log_service(emmc_log_t *log, uint32_t now_ms)
Definition emmc_log.c:424
static emmc_status_t format(emmc_log_t *log)
Definition emmc_log.c:324
emmc_status_t emmc_log_init(emmc_log_t *log, const emmc_port_t *port, void *context, const emmc_log_config_t *config, emmc_log_chunk_t *chunks, uint8_t chunk_count, uint8_t *scratch)
Definition emmc_log.c:177
static emmc_status_t search(emmc_log_t *log, uint32_t lo, uint32_t hi, uint32_t *end)
Definition emmc_log.c:305
static void close_block(emmc_log_t *log)
Definition emmc_log.c:73
uint8_t emmc_log_backlog(const emmc_log_t *log)
Definition emmc_log.c:252
bool emmc_log_append(emmc_log_t *log, uint8_t type, uint32_t t_ms, const void *data, uint8_t len)
Definition emmc_log.c:235
static bool put_record(emmc_log_t *log, uint8_t type, uint32_t t_ms, const void *data, uint8_t len)
Definition emmc_log.c:120
static uint32_t get32(const uint8_t *p)
Definition emmc_log.c:24
static uint16_t get16(const uint8_t *p)
Definition emmc_log.c:20
static void put16(uint8_t *p, uint16_t v)
Definition emmc_log.c:8
static void chunk_complete(emmc_log_t *log)
Definition emmc_log.c:67
static emmc_status_t mount(emmc_log_t *log)
Definition emmc_log.c:354
static void stop(emmc_log_t *log, emmc_disk_state_t why)
Definition emmc_log.c:262
static void seal(emmc_log_t *log)
Definition emmc_log.c:84
static void put32(uint8_t *p, uint32_t v)
Definition emmc_log.c:13
static bool acquire(emmc_log_t *log)
Definition emmc_log.c:104
static uint8_t * block_at(emmc_log_t *log, uint8_t chunk, uint8_t block)
Definition emmc_log.c:63
static void release_write_chunk(emmc_log_t *log, bool written)
Definition emmc_log.c:270
void emmc_log_flush(emmc_log_t *log)
Definition emmc_log.c:246
static bool keep(emmc_log_t *log, uint8_t type, uint32_t t_ms, const void *data, uint8_t len)
Definition emmc_log.c:146
static void unlock(emmc_log_t *log)
Definition emmc_log.c:55
emmc_log_config_t emmc_log_default_config(void)
Definition emmc_log.c:167
#define EMMC_LOG_RECORD_HEADER_BYTES
Definition emmc_log.h:61
emmc_disk_state_t
Definition emmc_log.h:92
@ EMMC_DISK_FULL
Definition emmc_log.h:95
@ EMMC_DISK_FAULT
Definition emmc_log.h:96
@ EMMC_DISK_UNMOUNTED
Definition emmc_log.h:93
@ EMMC_DISK_MOUNTED
Definition emmc_log.h:94
#define EMMC_LOG_MAX_CHUNKS
Definition emmc_log.h:57
@ EMMC_CHUNK_FREE
Definition emmc_log.h:109
@ EMMC_CHUNK_READY
Definition emmc_log.h:111
@ EMMC_CHUNK_FILLING
Definition emmc_log.h:110
#define EMMC_LOG_KIND_PAD
Definition emmc_log.h:68
#define EMMC_REC_DROPPED
Definition emmc_log.h:74
#define EMMC_LOG_HEADER_BYTES
Definition emmc_log.h:59
#define EMMC_REC_STATE
Definition emmc_log.h:72
#define EMMC_REC_SESSION_END
Definition emmc_log.h:73
#define EMMC_LOG_BLOCK_MAGIC
Definition emmc_log.h:64
#define EMMC_LOG_CHUNK_BYTES
Definition emmc_log.h:56
#define EMMC_LOG_PAYLOAD_BYTES
Definition emmc_log.h:60
#define EMMC_LOG_CHUNK_BLOCKS
Definition emmc_log.h:55
#define EMMC_LOG_SUPER_MAGIC
Definition emmc_log.h:63
#define EMMC_LOG_DEFAULT_DATA_START
Definition emmc_log.h:78
#define EMMC_LOG_LAYOUT_VERSION
Definition emmc_log.h:65
#define EMMC_LOG_KIND_DATA
Definition emmc_log.h:67
#define EMMC_REC_SESSION_START
Definition emmc_log.h:71
#define EMMC_BLOCK_SIZE
Definition emmc_port.h:20
emmc_status_t
Definition emmc_port.h:22
@ EMMC_ERR_NOT_READY
Definition emmc_port.h:26
@ EMMC_ERR_PARAM
Definition emmc_port.h:27
@ EMMC_OK
Definition emmc_port.h:23
static const emmc_port_t port
Definition emmc_stm32.c:210
std::vector< daq::SensorDataChunkCollection > chunks
Definition main.cpp:55
static bool ok
Definition flight_e2e.c:435
int32_t p
Definition flight_e2e.c:78
bool flight_state_logs_to_emmc(flight_state_t state)
#define NULL
Definition usbd_def.h:44
static const idle_config_t config
Definition idle_task.c:17
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
Definition json.hpp:13878
*brief create JSON pointer *sa reference_tokens end()
static osMutexId_t lock
Definition mode.c:11
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
uint8_t bytes[EMMC_LOG_CHUNK_BYTES]
Definition emmc_log.h:81
uint32_t write_budget_ms
Definition emmc_log.h:88
uint32_t read_budget_ms
Definition emmc_log.h:87
uint8_t write_attempts
Definition emmc_log.h:89
uint32_t flush_interval_ms
Definition emmc_log.h:86
uint32_t data_start_lba
Definition emmc_log.h:85
uint32_t records
Definition emmc_log.h:100
uint32_t chunks_lost
Definition emmc_log.h:103
uint32_t write_errors
Definition emmc_log.h:104
uint32_t chunks_written
Definition emmc_log.h:102
uint32_t pad_blocks
Definition emmc_log.h:105
uint32_t dropped
Definition emmc_log.h:101
uint32_t arm_index
Definition emmc_log.h:125
uint32_t fill_started_ms
Definition emmc_log.h:131
bool recording
Definition emmc_log.h:124
emmc_log_stats_t stats
Definition emmc_log.h:145
uint32_t unreported_drops
Definition emmc_log.h:132
emmc_chunk_state_t chunk_state[EMMC_LOG_MAX_CHUNKS]
Definition emmc_log.h:133
emmc_log_config_t config
Definition emmc_log.h:118
emmc_disk_state_t disk
Definition emmc_log.h:139
uint32_t chunk_arm[EMMC_LOG_MAX_CHUNKS]
Definition emmc_log.h:134
uint32_t capacity
Definition emmc_log.h:140
uint32_t epoch
Definition emmc_log.h:141
bool has_fill
Definition emmc_log.h:126
uint8_t * scratch
Definition emmc_log.h:121
uint8_t chunk_count
Definition emmc_log.h:120
emmc_port_t port
Definition emmc_log.h:116
uint8_t write_chunk
Definition emmc_log.h:137
void * context
Definition emmc_log.h:117
bool fill_aged
Definition emmc_log.h:130
uint8_t fill_block
Definition emmc_log.h:128
uint8_t write_tries
Definition emmc_log.h:138
uint32_t base_session
Definition emmc_log.h:143
uint16_t fill_offset
Definition emmc_log.h:129
uint8_t fill_chunk
Definition emmc_log.h:127
uint32_t head
Definition emmc_log.h:142
emmc_log_chunk_t * chunks
Definition emmc_log.h:119
emmc_status_t(* write)(void *context, uint32_t lba, const void *buffer, uint32_t count, uint32_t budget_ms)
Definition emmc_port.h:47
void(* unlock)(void *context)
Definition emmc_port.h:56
emmc_status_t(* read)(void *context, uint32_t lba, void *buffer, uint32_t count, uint32_t budget_ms)
Definition emmc_port.h:45
void(* lock)(void *context)
Definition emmc_port.h:55
emmc_status_t(* start)(void *context, uint32_t *block_count)
Definition emmc_port.h:37
static uint8_t scratch[BS]