| 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 |