GCC Code Coverage Report


Directory: ./
File: firmware/controllers/storage.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 100.0% 8 0 8
Functions: 100.0% 1 0 1
Branches: 91.7% 11 0 12
Decisions: -% 0 - 0

Line Branch Decision Exec Source
1 /**
2 * @file storage.cpp
3 * @brief adapter for unify access to MFS and legacy 'internal flash' storage drivers
4 *
5 * @date Jan 4, 2025
6 * @author Andrey Gusakov
7 */
8
9 #include "pch.h"
10
11 #include "storage.h"
12
13 #if EFI_CONFIGURATION_STORAGE
14 #include "mpu_util.h"
15 #endif
16
17 /* If any setting storage is exist or we are in unit test */
18 #if EFI_CONFIGURATION_STORAGE || defined(EFI_UNIT_TEST)
19 6 bool storageAllowWriteID(StorageItemId id)
20 {
21 #if (EFI_STORAGE_INT_FLASH == TRUE) || defined(EFI_UNIT_TEST)
22
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
6 if ((id == EFI_SETTINGS_RECORD_ID) ||
23 (id == EFI_SETTINGS_BACKUP_RECORD_ID)) {
24 // special case, settings can be stored in internal flash
25
26 // writing internal flash can cause cpu freeze
27 // check if HW support flash writing while executing
28
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
4 if (mcuCanFlashWhileRunning()) {
29 1 return true;
30 }
31
32 #if EFI_SHAFT_POSITION_INPUT
33 // MCU does not support write while executing, check if engine is stopped
34
6/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 time.
3 if (engine->triggerCentral.directSelfStimulation || engine->rpmCalculator.isStopped()) {
35 2 return true;
36 } else {
37 // rusEfi usually runs on hardware which halts execution while writing to internal flash, so we
38 // postpone writes to until engine is stopped. Writes in case of self-stimulation are fine.
39 1 return false;
40 }
41 #endif // EFI_SHAFT_POSITION_INPUT
42 }
43 #endif // EFI_STORAGE_INT_FLASH
44
45 // TODO: we expect every other ID to be stored in external flash...
46 2 return true;
47 }
48 #endif // EFI_CONFIGURATION_STORAGE || defined(EFI_UNIT_TEST)
49
50 /* If any setting storage is exist */
51 #if EFI_CONFIGURATION_STORAGE
52
53 #if EFI_STORAGE_INT_FLASH == TRUE
54 #include "storage_flash.h"
55 #endif
56
57 #if EFI_STORAGE_MFS == TRUE
58 #include "storage_mfs.h"
59 #endif
60
61 #if EFI_STORAGE_SD == TRUE
62 #include "storage_sd.h"
63 #endif
64
65 #define STORAGE_MANAGER_POLL_INTERVAL_MS 100
66
67 static constexpr size_t storagesCount = STORAGE_TOTAL;
68
69 static SettingStorageBase *storages[storagesCount];
70
71 chibios_rt::Mailbox<msg_t, 16> storageManagerMb;
72
73 #define MSG_CMD_WRITE (0)
74 // force settings save independently of mcuCanFlashWhileRunning()
75 #define MSG_CMD_WRITE_NOW (1)
76 #define MSG_CMD_READ (2)
77 #define MSG_CMD_PING (3)
78 #define MSG_CMD_REG (4)
79 #define MSG_CMD_UNREG (5)
80
81 #define MSG_ID_MASK 0x1f
82
83 static bool storageWriteID(uint32_t id) {
84 // Do the actual flash write operation for given ID
85 if (id == EFI_SETTINGS_RECORD_ID) {
86 return writeToFlashNowImpl();
87 } else if (id == EFI_LTFT_RECORD_ID) {
88 engine->module<LongTermFuelTrim>()->store();
89 return true;
90 } else {
91 efiPrintf("Requested to write unknown record id %ld", id);
92 // to clear pending bit
93 return true;
94 }
95 return true;
96 }
97
98 static bool storageReadID(uint32_t id) {
99 // Do actual read and call consumers callback
100
101 if (id == EFI_LTFT_RECORD_ID) {
102 engine->module<LongTermFuelTrim>()->load();
103 return true;
104 } else {
105 efiPrintf("Requested to read unknown record id %ld", id);
106 // to clear pending bit
107 return true;
108 }
109 }
110
111 static const char *storageTypeToName(StorageType type) {
112 switch (type) {
113 case STORAGE_INT_FLASH:
114 return "INT_FLASH";
115 case STORAGE_MFS_INT_FLASH:
116 return "MFS_INT_FLASH";
117 case STORAGE_MFS_EXT_FLASH:
118 return "MFS_EXT_FLASH";
119 case STORAGE_SD_CARD:
120 return "SD_CARD";
121 default:
122 break;
123 }
124
125 return "UNK";
126 }
127
128 #define for_all_storages SettingStorageBase* storage = nullptr; \
129 for (size_t i = 0; i < storagesCount; i++) \
130 if ((storage = storages[i]) != nullptr)
131
132 static bool storageIsIdAvailableForId(StorageItemId id) {
133 for_all_storages {
134 if ((storage->isReady()) && (storage->isIdSupported(id))) {
135 return true;
136 }
137 }
138
139 return false;
140 }
141
142 StorageStatus storageWrite(StorageItemId id, const uint8_t *ptr, size_t size) {
143 bool success = false;
144 StorageStatus status = StorageStatus::NotSupported;
145
146 for_all_storages {
147 if ((!storage->isReady()) || (!storage->isIdSupported(id))) {
148 continue;
149 }
150
151 status = storage->store(id, ptr, size);
152 if (status == StorageStatus::Ok) {
153 success = true;
154 }
155 }
156
157 return (success ? StorageStatus::Ok : status);
158 }
159
160 StorageStatus storageRead(StorageItemId id, uint8_t *ptr, size_t size) {
161 bool success = false;
162 StorageStatus status = StorageStatus::NotSupported;
163
164 for_all_storages {
165 if ((!storage->isReady()) || (!storage->isIdSupported(id))) {
166 continue;
167 }
168
169 status = storage->read(id, ptr, size);
170 if (status == StorageStatus::Ok) {
171 success = true;
172 }
173 }
174
175 return (success ? StorageStatus::Ok : status);
176 }
177
178 static bool storageManagerSendCmd(uint32_t cmd, uint32_t arg)
179 {
180 msg_t msg = ((cmd & 0xff) << 24) | (arg & 0x00ffffff);
181 // Note: we are not sure about caller context and using universal (but not optimal) chSysGetStatusAndLockX()
182 chibios_rt::CriticalSectionLocker csl;
183 return (storageManagerMb.postI(msg) == MSG_OK);
184 }
185
186 bool storageRequestWriteID(StorageItemId id, bool forced) {
187 return storageManagerSendCmd(forced ? MSG_CMD_WRITE_NOW : MSG_CMD_WRITE, (uint32_t)id);
188 }
189
190 bool storageReqestReadID(StorageItemId id) {
191 return storageManagerSendCmd(MSG_CMD_READ, (uint32_t)id);
192 }
193
194 bool storageRegisterStorage(StorageType type, SettingStorageBase *storage) {
195 if (type >= STORAGE_TOTAL) {
196 return false;
197 }
198
199 if (storageIsStorageRegistered(type)) {
200 /* already registered */
201 efiPrintf("Trying to register already exist storage %s", storageTypeToName(type));
202 return false;
203 }
204
205 storages[type] = storage;
206 efiPrintf("Storage %s registered", storageTypeToName(type));
207
208 storageManagerSendCmd(MSG_CMD_PING, 0);
209
210 return true;
211 }
212
213 bool storageUnregisterStorage(StorageType type) {
214 if (type >= STORAGE_TOTAL) {
215 return false;
216 }
217
218 if (!storageIsStorageRegistered(type)) {
219 /* already unregistered */
220 efiPrintf("Trying to unregister non-exist storage %s", storageTypeToName(type));
221 return false;
222 }
223
224 storages[type] = nullptr;
225 efiPrintf("Storage %s unregistered", storageTypeToName(type));
226
227 return true;
228 }
229
230 bool storageIsStorageRegistered(StorageType type) {
231 if (type >= STORAGE_TOTAL) {
232 return false;
233 }
234
235 return (storages[type] != nullptr);
236 }
237
238 bool storagRequestRegisterStorage(StorageType id)
239 {
240 return storageManagerSendCmd(MSG_CMD_REG, (uint32_t)id);
241 }
242
243 bool storagRequestUnregisterStorage(StorageType id)
244 {
245 return storageManagerSendCmd(MSG_CMD_UNREG, (uint32_t)id);
246 }
247
248 static uint32_t pendingWrites = 0;
249 static uint32_t pendingReads = 0;
250
251 #if (EFI_STORAGE_MFS == TRUE) || (EFI_STORAGE_SD == TRUE)
252 /* in case of MFS or SD card we need more stack */
253 static THD_WORKING_AREA(storageManagerThreadStack, 3 * UTILITY_THREAD_STACK_SIZE);
254 #else
255 static THD_WORKING_AREA(storageManagerThreadStack, UTILITY_THREAD_STACK_SIZE);
256 #endif
257
258 static void storageManagerThread(void*) {
259 chRegSetThreadName("storage manger");
260
261 while (true) {
262 msg_t ret;
263 msg_t msg;
264
265 // Wait for a request to come in or timeout
266 ret = storageManagerMb.fetch(&msg, TIME_MS2I(STORAGE_MANAGER_POLL_INTERVAL_MS));
267 if (ret == MSG_OK) {
268 uint8_t cmd = (msg >> 24) & 0xff;
269 uint32_t id = msg & MSG_ID_MASK;
270
271 switch (cmd) {
272 case MSG_CMD_READ:
273 pendingReads |= BIT(id);
274 break;
275 case MSG_CMD_WRITE:
276 pendingWrites |= BIT(id);
277 break;
278 case MSG_CMD_WRITE_NOW:
279 pendingWrites |= BIT(id);
280 // skip storageAllowWriteID() check
281 if (storageWriteID(id)) {
282 pendingWrites &= ~BIT(id);
283 }
284 break;
285 case MSG_CMD_PING:
286 /* nop */
287 break;
288 case MSG_CMD_REG:
289 #if EFI_STORAGE_INT_FLASH == TRUE
290 if ((StorageType)id == STORAGE_INT_FLASH) {
291 initStorageFlash();
292 }
293 #endif // EFI_STORAGE_INT_FLASH
294 #if EFI_STORAGE_MFS == TRUE
295 if ((StorageType)id == STORAGE_MFS_EXT_FLASH) {
296 initStorageMfs();
297 }
298 #endif // EFI_STORAGE_MFS
299 #if EFI_STORAGE_SD == TRUE
300 if ((StorageType)id == STORAGE_SD_CARD) {
301 initStorageSD();
302 }
303 #endif // EFI_STORAGE_SD
304 break;
305 case MSG_CMD_UNREG:
306 storageUnregisterStorage((StorageType)id);
307 break;
308 default:
309 /* ignore */
310 break;
311 }
312 }
313
314 // check if we can read some of pending IDs...
315 for (size_t i = 0; (i < EFI_STORAGE_TOTAL_ITEMS) && pendingReads; i++) {
316 if ((pendingReads & BIT(i)) == 0) {
317 continue;
318 }
319
320 StorageItemId id = (StorageItemId)i;
321 if (!storageIsIdAvailableForId(id)) {
322 continue;
323 }
324
325 if (storageReadID(id)) {
326 pendingReads &= ~BIT(id);
327 }
328 }
329
330 // check if we can write some of pendind IDs...
331 for (size_t i = 0; (i < EFI_STORAGE_TOTAL_ITEMS) && pendingWrites; i++) {
332 if ((pendingWrites & BIT(i)) == 0) {
333 continue;
334 }
335
336 StorageItemId id = (StorageItemId)i;
337 if (!storageIsIdAvailableForId(id)) {
338 continue;
339 }
340
341 if (!storageAllowWriteID(id)) {
342 continue;
343 }
344
345 if (storageWriteID(id)) {
346 pendingWrites &= ~BIT(id);
347 }
348 }
349 }
350 }
351
352 /* misc helpers */
353 bool getNeedToWriteConfiguration() {
354 return (pendingWrites & BIT(EFI_SETTINGS_RECORD_ID)) != 0;
355 }
356
357 void initStorage() {
358 bool settingsStorageReady = false;
359 // may be unused
360 (void)settingsStorageReady;
361
362 #if EFI_STORAGE_INT_FLASH == TRUE
363 settingsStorageReady = initStorageFlash();
364 #endif // EFI_STORAGE_INT_FLASH
365
366 #if EFI_STORAGE_MFS == TRUE
367 if (settingsStorageReady) {
368 // Skip MFS if internal storage is used for persistentState
369 // Init of MFS may take significant time, lets postpone it until storage manager thread
370 storagRequestRegisterStorage(STORAGE_MFS_EXT_FLASH);
371 } else {
372 // Set long timeout to watchdog as this code is called before any thread is started
373 // and no one is feeding watchdog
374 startWatchdog(WATCHDOG_MFS_START_TIMEOUT_MS);
375
376 initStorageMfs();
377
378 // restart the watchdog with the default timeout
379 startWatchdog();
380 }
381 #endif // EFI_STORAGE_MFS
382
383 chThdCreateStatic(storageManagerThreadStack, sizeof(storageManagerThreadStack), PRIO_STORAGE_MANAGER, storageManagerThread, nullptr);
384 }
385
386 #endif // EFI_CONFIGURATION_STORAGE
387