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 |