fds.c 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169
  1. /**
  2. * Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. #include "sdk_common.h"
  41. #if NRF_MODULE_ENABLED(FDS)
  42. #include "fds.h"
  43. #include "fds_internal_defs.h"
  44. #include <stdint.h>
  45. #include <string.h>
  46. #include <stdbool.h>
  47. #include "nrf_error.h"
  48. #include "nrf_atomic.h"
  49. #include "nrf_atfifo.h"
  50. #include "nrf_fstorage.h"
  51. #if (FDS_BACKEND == NRF_FSTORAGE_SD)
  52. #include "nrf_fstorage_sd.h"
  53. #elif (FDS_BACKEND == NRF_FSTORAGE_NVMC)
  54. #include "nrf_fstorage_nvmc.h"
  55. #else
  56. #error Invalid FDS backend.
  57. #endif
  58. #if (FDS_CRC_CHECK_ON_READ)
  59. #include "crc16.h"
  60. #endif
  61. static void fs_event_handler(nrf_fstorage_evt_t * evt);
  62. NRF_FSTORAGE_DEF(nrf_fstorage_t m_fs) =
  63. {
  64. // The flash area boundaries are set in fds_init().
  65. .evt_handler = fs_event_handler,
  66. };
  67. // Internal status flags.
  68. static struct
  69. {
  70. bool volatile initialized;
  71. nrf_atomic_flag_t initializing;
  72. } m_flags;
  73. // The number of queued operations.
  74. // Incremented by queue_start() and decremented by queue_has_next().
  75. static nrf_atomic_u32_t m_queued_op_cnt;
  76. // The number of registered users and their callback functions.
  77. static nrf_atomic_u32_t m_users;
  78. static fds_cb_t m_cb_table[FDS_MAX_USERS];
  79. // The latest (largest) record ID written so far.
  80. static nrf_atomic_u32_t m_latest_rec_id;
  81. // Queue of fds operations.
  82. NRF_ATFIFO_DEF(m_queue, fds_op_t, FDS_OP_QUEUE_SIZE);
  83. // Structures used to hold informations about virtual pages.
  84. static fds_page_t m_pages[FDS_DATA_PAGES];
  85. static fds_swap_page_t m_swap_page;
  86. // Garbage collection data.
  87. static fds_gc_data_t m_gc;
  88. static void event_send(fds_evt_t const * const p_evt)
  89. {
  90. for (uint32_t user = 0; user < FDS_MAX_USERS; user++)
  91. {
  92. if (m_cb_table[user] != NULL)
  93. {
  94. m_cb_table[user](p_evt);
  95. }
  96. }
  97. }
  98. static void event_prepare(fds_op_t const * const p_op, fds_evt_t * const p_evt)
  99. {
  100. switch (p_op->op_code)
  101. {
  102. case FDS_OP_INIT:
  103. p_evt->id = FDS_EVT_INIT;
  104. break;
  105. case FDS_OP_WRITE:
  106. p_evt->id = FDS_EVT_WRITE;
  107. p_evt->write.file_id = p_op->write.header.file_id;
  108. p_evt->write.record_key = p_op->write.header.record_key;
  109. p_evt->write.record_id = p_op->write.header.record_id;
  110. p_evt->write.is_record_updated = 0;
  111. break;
  112. case FDS_OP_UPDATE:
  113. p_evt->id = FDS_EVT_UPDATE;
  114. p_evt->write.file_id = p_op->write.header.file_id;
  115. p_evt->write.record_key = p_op->write.header.record_key;
  116. p_evt->write.record_id = p_op->write.header.record_id;
  117. p_evt->write.is_record_updated = (p_op->write.step == FDS_OP_WRITE_DONE);
  118. break;
  119. case FDS_OP_DEL_RECORD:
  120. p_evt->id = FDS_EVT_DEL_RECORD;
  121. p_evt->del.file_id = p_op->del.file_id;
  122. p_evt->del.record_key = p_op->del.record_key;
  123. p_evt->del.record_id = p_op->del.record_to_delete;
  124. break;
  125. case FDS_OP_DEL_FILE:
  126. p_evt->id = FDS_EVT_DEL_FILE;
  127. p_evt->del.file_id = p_op->del.file_id;
  128. p_evt->del.record_key = FDS_RECORD_KEY_DIRTY;
  129. p_evt->del.record_id = 0;
  130. break;
  131. case FDS_OP_GC:
  132. p_evt->id = FDS_EVT_GC;
  133. break;
  134. default:
  135. // Should not happen.
  136. break;
  137. }
  138. }
  139. static bool header_has_next(fds_header_t const * p_hdr, uint32_t const * p_page_end)
  140. {
  141. uint32_t const * const p_hdr32 = (uint32_t*)p_hdr;
  142. return ( ( p_hdr32 < p_page_end)
  143. && (*p_hdr32 != FDS_ERASED_WORD)); // Check last to be on the safe side (dereference)
  144. }
  145. // Jump to the next header.
  146. static fds_header_t const * header_jump(fds_header_t const * const p_hdr)
  147. {
  148. return (fds_header_t*)((uint32_t*)p_hdr + FDS_HEADER_SIZE + p_hdr->length_words);
  149. }
  150. static fds_header_status_t header_check(fds_header_t const * p_hdr, uint32_t const * p_page_end)
  151. {
  152. if (((uint32_t*)header_jump(p_hdr) > p_page_end))
  153. {
  154. // The length field would jump across the page boundary.
  155. // FDS won't allow writing such a header, therefore it has been corrupted.
  156. return FDS_HEADER_CORRUPT;
  157. }
  158. if ( (p_hdr->file_id == FDS_FILE_ID_INVALID)
  159. || (p_hdr->record_key == FDS_RECORD_KEY_DIRTY))
  160. {
  161. return FDS_HEADER_DIRTY;
  162. }
  163. return FDS_HEADER_VALID;
  164. }
  165. static bool address_is_valid(uint32_t const * const p_addr)
  166. {
  167. return ((p_addr != NULL) &&
  168. (p_addr >= (uint32_t*)m_fs.start_addr) &&
  169. (p_addr <= (uint32_t*)m_fs.end_addr) &&
  170. (is_word_aligned(p_addr)));
  171. }
  172. // Reads a page tag, and determines if the page is used to store data or as swap.
  173. static fds_page_type_t page_identify(uint32_t const * const p_page_addr)
  174. {
  175. if ( (p_page_addr == NULL) // Should never happen.
  176. || (p_page_addr[FDS_PAGE_TAG_WORD_0] != FDS_PAGE_TAG_MAGIC))
  177. {
  178. return FDS_PAGE_UNDEFINED;
  179. }
  180. switch (p_page_addr[FDS_PAGE_TAG_WORD_1])
  181. {
  182. case FDS_PAGE_TAG_SWAP:
  183. return FDS_PAGE_SWAP;
  184. case FDS_PAGE_TAG_DATA:
  185. return FDS_PAGE_DATA;
  186. default:
  187. return FDS_PAGE_UNDEFINED;
  188. }
  189. }
  190. static bool page_is_erased(uint32_t const * const p_page_addr)
  191. {
  192. for (uint32_t i = 0; i < FDS_PAGE_SIZE; i++)
  193. {
  194. if (*(p_page_addr + i) != FDS_ERASED_WORD)
  195. {
  196. return false;
  197. }
  198. }
  199. return true;
  200. }
  201. // NOTE: Must be called from within a critical section.
  202. static bool page_has_space(uint16_t page, uint16_t length_words)
  203. {
  204. length_words += m_pages[page].write_offset;
  205. length_words += m_pages[page].words_reserved;
  206. return (length_words < FDS_PAGE_SIZE);
  207. }
  208. // Given a pointer to a record, find the index of the page on which it is stored.
  209. // Returns FDS_SUCCESS if the page is found, FDS_ERR_NOT_FOUND otherwise.
  210. static ret_code_t page_from_record(uint16_t * const p_page, uint32_t const * const p_rec)
  211. {
  212. ret_code_t ret = FDS_ERR_NOT_FOUND;
  213. CRITICAL_SECTION_ENTER();
  214. for (uint16_t i = 0; i < FDS_DATA_PAGES; i++)
  215. {
  216. if ((p_rec > m_pages[i].p_addr) &&
  217. (p_rec < m_pages[i].p_addr + FDS_PAGE_SIZE))
  218. {
  219. ret = FDS_SUCCESS;
  220. *p_page = i;
  221. break;
  222. }
  223. }
  224. CRITICAL_SECTION_EXIT();
  225. return ret;
  226. }
  227. // Scan a page to determine how many words have been written to it.
  228. // This information is used to set the page write offset during initialization.
  229. // Additionally, this function updates the latest record ID as it proceeds.
  230. // If an invalid record header is found, the can_gc argument is set to true.
  231. static void page_scan(uint32_t const * p_addr,
  232. uint16_t * const words_written,
  233. bool * const can_gc)
  234. {
  235. uint32_t const * const p_page_end = p_addr + FDS_PAGE_SIZE;
  236. p_addr += FDS_PAGE_TAG_SIZE;
  237. *words_written = FDS_PAGE_TAG_SIZE;
  238. fds_header_t const * p_header = (fds_header_t*)p_addr;
  239. while (header_has_next(p_header, p_page_end))
  240. {
  241. fds_header_status_t hdr = header_check(p_header, p_page_end);
  242. if (hdr == FDS_HEADER_VALID)
  243. {
  244. // Update the latest (largest) record ID.
  245. if (p_header->record_id > m_latest_rec_id)
  246. {
  247. m_latest_rec_id = p_header->record_id;
  248. }
  249. }
  250. else
  251. {
  252. if (can_gc != NULL)
  253. {
  254. *can_gc = true;
  255. }
  256. if (hdr == FDS_HEADER_CORRUPT)
  257. {
  258. // It could happen that a record has a corrupt header which would set a
  259. // wrong offset for this page. In such cases, update this value to its maximum,
  260. // to ensure that no new records will be written to this page and to enable
  261. // correct statistics reporting by fds_stat().
  262. *words_written = FDS_PAGE_SIZE;
  263. // We can't continue to scan this page.
  264. return;
  265. }
  266. }
  267. *words_written += (FDS_HEADER_SIZE + p_header->length_words);
  268. p_header = header_jump(p_header);
  269. }
  270. }
  271. static void page_offsets_update(fds_page_t * const p_page, fds_op_t const * p_op)
  272. {
  273. // If the first part of the header has been written correctly, update the offset as normal.
  274. // Even if the record has not been written completely, fds is still able to continue normal
  275. // operation. Incomplete records will be deleted the next time garbage collection is run.
  276. // If we failed at the very beginning of the write operation, restore the offset
  277. // to the previous value so that no holes will be left in the flash.
  278. if (p_op->write.step > FDS_OP_WRITE_RECORD_ID)
  279. {
  280. p_page->write_offset += (FDS_HEADER_SIZE + p_op->write.header.length_words);
  281. }
  282. p_page->words_reserved -= (FDS_HEADER_SIZE + p_op->write.header.length_words);
  283. }
  284. // Tags a page as swap, i.e., reserved for GC.
  285. static ret_code_t page_tag_write_swap(void)
  286. {
  287. // The tag needs to be statically allocated since it is not buffered by fstorage.
  288. static uint32_t const page_tag_swap[] = {FDS_PAGE_TAG_MAGIC, FDS_PAGE_TAG_SWAP};
  289. return nrf_fstorage_write(&m_fs, (uint32_t)m_swap_page.p_addr, page_tag_swap, FDS_PAGE_TAG_SIZE * sizeof(uint32_t), NULL);
  290. }
  291. // Tags a page as data, i.e, ready for storage.
  292. static ret_code_t page_tag_write_data(uint32_t const * const p_page_addr)
  293. {
  294. // The tag needs to be statically allocated since it is not buffered by fstorage.
  295. static uint32_t const page_tag_data[] = {FDS_PAGE_TAG_MAGIC, FDS_PAGE_TAG_DATA};
  296. return nrf_fstorage_write(&m_fs, (uint32_t)p_page_addr, page_tag_data, FDS_PAGE_TAG_SIZE * sizeof(uint32_t), NULL);
  297. }
  298. // Reserve space on a page.
  299. // NOTE: this function takes into the account the space required for the record header.
  300. static ret_code_t write_space_reserve(uint16_t length_words, uint16_t * p_page)
  301. {
  302. bool space_reserved = false;
  303. uint16_t const total_len_words = length_words + FDS_HEADER_SIZE;
  304. if (total_len_words >= FDS_PAGE_SIZE - FDS_PAGE_TAG_SIZE)
  305. {
  306. return FDS_ERR_RECORD_TOO_LARGE;
  307. }
  308. CRITICAL_SECTION_ENTER();
  309. for (uint16_t page = 0; page < FDS_DATA_PAGES; page++)
  310. {
  311. if ((m_pages[page].page_type == FDS_PAGE_DATA) &&
  312. (page_has_space(page, total_len_words)))
  313. {
  314. space_reserved = true;
  315. *p_page = page;
  316. m_pages[page].words_reserved += total_len_words;
  317. break;
  318. }
  319. }
  320. CRITICAL_SECTION_EXIT();
  321. return (space_reserved) ? FDS_SUCCESS : FDS_ERR_NO_SPACE_IN_FLASH;
  322. }
  323. // Undo a write_space_reserve() call.
  324. // NOTE: Must be called within a critical section.
  325. static void write_space_free(uint16_t length_words, uint16_t page)
  326. {
  327. m_pages[page].words_reserved -= (length_words + FDS_HEADER_SIZE);
  328. }
  329. static uint32_t record_id_new(void)
  330. {
  331. return nrf_atomic_u32_add(&m_latest_rec_id, 1);
  332. }
  333. // Given a page and a record, find the next valid record on that page.
  334. // If p_record is NULL, search from the beginning of the page,
  335. // otherwise, resume searching from p_record.
  336. // Return true if a record is found, false otherwise.
  337. // If no record is found, p_record is unchanged.
  338. static bool record_find_next(uint16_t page, uint32_t const ** p_record)
  339. {
  340. uint32_t const * p_page_end = (m_pages[page].p_addr + FDS_PAGE_SIZE);
  341. // If this is the first call on this page, start searching from its beginning.
  342. // Otherwise, jump to the next record.
  343. fds_header_t const * p_header = (fds_header_t*)(*p_record);
  344. if (p_header != NULL)
  345. {
  346. p_header = header_jump(p_header);
  347. }
  348. else
  349. {
  350. p_header = (fds_header_t*)(m_pages[page].p_addr + FDS_PAGE_TAG_SIZE);
  351. }
  352. // Read records from the page until:
  353. // - a valid record is found or
  354. // - the last record on a page is found
  355. while (header_has_next(p_header, p_page_end))
  356. {
  357. switch (header_check(p_header, p_page_end))
  358. {
  359. case FDS_HEADER_VALID:
  360. *p_record = (uint32_t*)p_header;
  361. return true;
  362. case FDS_HEADER_DIRTY:
  363. p_header = header_jump(p_header);
  364. break;
  365. case FDS_HEADER_CORRUPT:
  366. // We can't reliably jump over this record.
  367. // There is nothing more we can do on this page.
  368. return false;
  369. }
  370. }
  371. // No more valid records on this page.
  372. return false;
  373. }
  374. // Find a record given its descriptor and retrive the page in which the record is stored.
  375. // NOTE: Do not pass NULL as an argument for p_page.
  376. static bool record_find_by_desc(fds_record_desc_t * const p_desc, uint16_t * const p_page)
  377. {
  378. // If the gc_run_count field in the descriptor matches our counter, then the record has
  379. // not been moved. If the address is valid, and the record ID matches, there is no need
  380. // to find the record again. Only lookup the page in which the record is stored.
  381. if ((address_is_valid(p_desc->p_record)) &&
  382. (p_desc->gc_run_count == m_gc.run_count) &&
  383. (p_desc->record_id == ((fds_header_t*)p_desc->p_record)->record_id))
  384. {
  385. return (page_from_record(p_page, p_desc->p_record) == FDS_SUCCESS);
  386. }
  387. // Otherwise, find the record in flash.
  388. for (*p_page = 0; *p_page < FDS_DATA_PAGES; (*p_page)++)
  389. {
  390. // Set p_record to NULL to make record_find_next() search from the beginning of the page.
  391. uint32_t const * p_record = NULL;
  392. while (record_find_next(*p_page, &p_record))
  393. {
  394. fds_header_t const * const p_header = (fds_header_t*)p_record;
  395. if (p_header->record_id == p_desc->record_id)
  396. {
  397. p_desc->p_record = p_record;
  398. p_desc->gc_run_count = m_gc.run_count;
  399. return true;
  400. }
  401. }
  402. }
  403. return false;
  404. }
  405. // Search for a record and return its descriptor.
  406. // If p_file_id is NULL, only the record key will be used for matching.
  407. // If p_record_key is NULL, only the file ID will be used for matching.
  408. // If both are NULL, it will iterate through all records.
  409. static ret_code_t record_find(uint16_t const * p_file_id,
  410. uint16_t const * p_record_key,
  411. fds_record_desc_t * p_desc,
  412. fds_find_token_t * p_token)
  413. {
  414. if (!m_flags.initialized)
  415. {
  416. return FDS_ERR_NOT_INITIALIZED;
  417. }
  418. if (p_desc == NULL || p_token == NULL)
  419. {
  420. return FDS_ERR_NULL_ARG;
  421. }
  422. // Begin (or resume) searching for a record.
  423. for (; p_token->page < FDS_DATA_PAGES; p_token->page++)
  424. {
  425. if (m_pages[p_token->page].page_type != FDS_PAGE_DATA)
  426. {
  427. // It might be that the page is FDS_PAGE_UNDEFINED.
  428. // Skip this page.
  429. continue;
  430. }
  431. while (record_find_next(p_token->page, &p_token->p_addr))
  432. {
  433. fds_header_t const * p_header = (fds_header_t*)p_token->p_addr;
  434. // A valid record was found, check its header for a match.
  435. if ((p_file_id != NULL) &&
  436. (p_header->file_id != *p_file_id))
  437. {
  438. continue;
  439. }
  440. if ((p_record_key != NULL) &&
  441. (p_header->record_key != *p_record_key))
  442. {
  443. continue;
  444. }
  445. // Record found; update the descriptor.
  446. p_desc->record_id = p_header->record_id;
  447. p_desc->p_record = p_token->p_addr;
  448. p_desc->gc_run_count = m_gc.run_count;
  449. return FDS_SUCCESS;
  450. }
  451. // We have scanned an entire page. Set the address in the token to NULL
  452. // so that it will be updated in the next iteration.
  453. p_token->p_addr = NULL;
  454. }
  455. return FDS_ERR_NOT_FOUND;
  456. }
  457. // Retrieve statistics about dirty records on a page.
  458. static void records_stat(uint16_t page,
  459. uint16_t * p_valid_records,
  460. uint16_t * p_dirty_records,
  461. uint16_t * p_freeable_words,
  462. bool * p_corruption)
  463. {
  464. fds_header_t const * p_header = (fds_header_t*)(m_pages[page].p_addr + FDS_PAGE_TAG_SIZE);
  465. uint32_t const * const p_page_end = (m_pages[page].p_addr + FDS_PAGE_SIZE);
  466. while (header_has_next(p_header, p_page_end))
  467. {
  468. switch (header_check(p_header, p_page_end))
  469. {
  470. case FDS_HEADER_DIRTY:
  471. *p_dirty_records += 1;
  472. *p_freeable_words += FDS_HEADER_SIZE + p_header->length_words;
  473. p_header = header_jump(p_header);
  474. break;
  475. case FDS_HEADER_VALID:
  476. *p_valid_records += 1;
  477. p_header = header_jump(p_header);
  478. break;
  479. case FDS_HEADER_CORRUPT:
  480. {
  481. *p_dirty_records += 1;
  482. *p_freeable_words += (p_page_end - (uint32_t*)p_header);
  483. *p_corruption = true;
  484. // We can't continue on this page.
  485. return;
  486. }
  487. default:
  488. break;
  489. }
  490. }
  491. }
  492. // Get a buffer on the queue of operations.
  493. static fds_op_t * queue_buf_get(nrf_atfifo_item_put_t * p_iput_ctx)
  494. {
  495. fds_op_t * const p_op = (fds_op_t*) nrf_atfifo_item_alloc(m_queue, p_iput_ctx);
  496. memset(p_op, 0x00, sizeof(fds_op_t));
  497. return p_op;
  498. }
  499. // Commit a buffer to the queue of operations.
  500. static void queue_buf_store(nrf_atfifo_item_put_t * p_iput_ctx)
  501. {
  502. (void) nrf_atfifo_item_put(m_queue, p_iput_ctx);
  503. }
  504. // Load the next operation from the queue.
  505. static fds_op_t * queue_load(nrf_atfifo_item_get_t * p_iget_ctx)
  506. {
  507. return (fds_op_t*) nrf_atfifo_item_get(m_queue, p_iget_ctx);
  508. }
  509. // Free the currently loaded operation.
  510. static void queue_free(nrf_atfifo_item_get_t * p_iget_ctx)
  511. {
  512. // Free the current queue element.
  513. (void) nrf_atfifo_item_free(m_queue, p_iget_ctx);
  514. }
  515. static bool queue_has_next(void)
  516. {
  517. // Decrement the number of queued operations.
  518. ASSERT(m_queued_op_cnt != 0);
  519. return nrf_atomic_u32_sub(&m_queued_op_cnt, 1);
  520. }
  521. // This function is called during initialization to setup the page structure (m_pages) and
  522. // provide additional information regarding eventual further initialization steps.
  523. static fds_init_opts_t pages_init(void)
  524. {
  525. uint32_t ret = NO_PAGES;
  526. uint16_t page = 0;
  527. uint16_t total_pages_available = FDS_VIRTUAL_PAGES;
  528. bool swap_set_but_not_found = false;
  529. for (uint16_t i = 0; i < FDS_VIRTUAL_PAGES; i++)
  530. {
  531. uint32_t const * const p_page_addr = (uint32_t*)m_fs.start_addr + (i * FDS_PAGE_SIZE);
  532. fds_page_type_t const page_type = page_identify(p_page_addr);
  533. switch (page_type)
  534. {
  535. case FDS_PAGE_UNDEFINED:
  536. {
  537. if (page_is_erased(p_page_addr))
  538. {
  539. if (m_swap_page.p_addr != NULL)
  540. {
  541. // If a swap page is already set, flag the page as erased (in m_pages)
  542. // and try to tag it as data (in flash) later on during initialization.
  543. m_pages[page].page_type = FDS_PAGE_ERASED;
  544. m_pages[page].p_addr = p_page_addr;
  545. m_pages[page].write_offset = FDS_PAGE_TAG_SIZE;
  546. // This is a candidate for a potential new swap page, in case the
  547. // current swap is going to be promoted to complete a GC instance.
  548. m_gc.cur_page = page;
  549. page++;
  550. }
  551. else
  552. {
  553. // If there is no swap page yet, use this one.
  554. m_swap_page.p_addr = p_page_addr;
  555. m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
  556. swap_set_but_not_found = true;
  557. }
  558. ret |= PAGE_ERASED;
  559. }
  560. else
  561. {
  562. // The page contains non-FDS data.
  563. // Do not initialize or use this page.
  564. total_pages_available--;
  565. m_pages[page].p_addr = p_page_addr;
  566. m_pages[page].page_type = FDS_PAGE_UNDEFINED;
  567. page++;
  568. }
  569. } break;
  570. case FDS_PAGE_DATA:
  571. {
  572. m_pages[page].page_type = FDS_PAGE_DATA;
  573. m_pages[page].p_addr = p_page_addr;
  574. // Scan the page to compute its write offset and determine whether or not the page
  575. // can be garbage collected. Additionally, update the latest kwown record ID.
  576. page_scan(p_page_addr, &m_pages[page].write_offset, &m_pages[page].can_gc);
  577. ret |= PAGE_DATA;
  578. page++;
  579. } break;
  580. case FDS_PAGE_SWAP:
  581. {
  582. if (swap_set_but_not_found)
  583. {
  584. m_pages[page].page_type = FDS_PAGE_ERASED;
  585. m_pages[page].p_addr = m_swap_page.p_addr;
  586. m_pages[page].write_offset = FDS_PAGE_TAG_SIZE;
  587. page++;
  588. }
  589. m_swap_page.p_addr = p_page_addr;
  590. // If the swap is promoted, this offset should be kept, otherwise,
  591. // it should be set to FDS_PAGE_TAG_SIZE.
  592. page_scan(p_page_addr, &m_swap_page.write_offset, NULL);
  593. ret |= (m_swap_page.write_offset == FDS_PAGE_TAG_SIZE) ?
  594. PAGE_SWAP_CLEAN : PAGE_SWAP_DIRTY;
  595. } break;
  596. default:
  597. // Shouldn't happen.
  598. break;
  599. }
  600. }
  601. if (total_pages_available < 2)
  602. {
  603. ret &= NO_PAGES;
  604. }
  605. return (fds_init_opts_t)ret;
  606. }
  607. // Write the first part of a record header (the key and length).
  608. static ret_code_t record_header_write_begin(fds_op_t * const p_op, uint32_t * const p_addr)
  609. {
  610. ret_code_t ret;
  611. // Write the record ID next.
  612. p_op->write.step = FDS_OP_WRITE_RECORD_ID;
  613. ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_TL),
  614. &p_op->write.header.record_key, FDS_HEADER_SIZE_TL * sizeof(uint32_t), NULL);
  615. return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY;
  616. }
  617. static ret_code_t record_header_write_id(fds_op_t * const p_op, uint32_t * const p_addr)
  618. {
  619. ret_code_t ret;
  620. // If this record has no data, write the last part of the header directly.
  621. // Otherwise, write the record data next.
  622. p_op->write.step = (p_op->write.p_data != NULL) ?
  623. FDS_OP_WRITE_DATA : FDS_OP_WRITE_HEADER_FINALIZE;
  624. ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_ID),
  625. &p_op->write.header.record_id, FDS_HEADER_SIZE_ID * sizeof(uint32_t), NULL);
  626. return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY;
  627. }
  628. static ret_code_t record_header_write_finalize(fds_op_t * const p_op, uint32_t * const p_addr)
  629. {
  630. ret_code_t ret;
  631. // If this is a simple write operation, then this is the last step.
  632. // If this is an update instead, delete the old record next.
  633. p_op->write.step = (p_op->op_code == FDS_OP_UPDATE) ?
  634. FDS_OP_WRITE_FLAG_DIRTY : FDS_OP_WRITE_DONE;
  635. ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_IC),
  636. &p_op->write.header.file_id, FDS_HEADER_SIZE_IC * sizeof(uint32_t), NULL);
  637. return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY;
  638. }
  639. static ret_code_t record_header_flag_dirty(uint32_t * const p_record, uint16_t page_to_gc)
  640. {
  641. // Used to flag a record as dirty, i.e. ready for garbage collection.
  642. // Must be statically allocated since it will be written to flash.
  643. __ALIGN(4) static uint32_t const dirty_header = {0xFFFF0000};
  644. // Flag the record as dirty.
  645. ret_code_t ret;
  646. ret = nrf_fstorage_write(&m_fs, (uint32_t)p_record,
  647. &dirty_header, FDS_HEADER_SIZE_TL * sizeof(uint32_t), NULL);
  648. if (ret != NRF_SUCCESS)
  649. {
  650. return FDS_ERR_BUSY;
  651. }
  652. m_pages[page_to_gc].can_gc = true;
  653. return FDS_SUCCESS;
  654. }
  655. static ret_code_t record_find_and_delete(fds_op_t * const p_op)
  656. {
  657. ret_code_t ret;
  658. uint16_t page;
  659. fds_record_desc_t desc = {0};
  660. desc.record_id = p_op->del.record_to_delete;
  661. if (record_find_by_desc(&desc, &page))
  662. {
  663. fds_header_t const * const p_header = (fds_header_t const *)desc.p_record;
  664. // Copy the record key and file ID, so that they can be returned in the event.
  665. // In case this function is run as part of an update, there is no need to copy
  666. // the file ID and record key since they are present in the header stored
  667. // in the queue element.
  668. p_op->del.file_id = p_header->file_id;
  669. p_op->del.record_key = p_header->record_key;
  670. // Flag the record as dirty.
  671. ret = record_header_flag_dirty((uint32_t*)desc.p_record, page);
  672. }
  673. else
  674. {
  675. // The record never existed, or it has already been deleted.
  676. ret = FDS_ERR_NOT_FOUND;
  677. }
  678. return ret;
  679. }
  680. // Finds a record within a file and flags it as dirty.
  681. static ret_code_t file_find_and_delete(fds_op_t * const p_op)
  682. {
  683. ret_code_t ret;
  684. fds_record_desc_t desc;
  685. // This token must persist across calls.
  686. static fds_find_token_t tok = {0};
  687. // Pass NULL to ignore the record key.
  688. ret = record_find(&p_op->del.file_id, NULL, &desc, &tok);
  689. if (ret == FDS_SUCCESS)
  690. {
  691. // A record was found: flag it as dirty.
  692. ret = record_header_flag_dirty((uint32_t*)desc.p_record, tok.page);
  693. }
  694. else // FDS_ERR_NOT_FOUND
  695. {
  696. // No more records were found. Zero the token, so that it can be reused.
  697. memset(&tok, 0x00, sizeof(fds_find_token_t));
  698. }
  699. return ret;
  700. }
  701. // Writes record data to flash.
  702. static ret_code_t record_write_data(fds_op_t * const p_op, uint32_t * const p_addr)
  703. {
  704. ret_code_t ret;
  705. p_op->write.step = FDS_OP_WRITE_HEADER_FINALIZE;
  706. ret = nrf_fstorage_write(&m_fs, (uint32_t)(p_addr + FDS_OFFSET_DATA),
  707. p_op->write.p_data, p_op->write.header.length_words * sizeof(uint32_t), NULL);
  708. return (ret == NRF_SUCCESS) ? FDS_SUCCESS : FDS_ERR_BUSY;
  709. }
  710. #if (FDS_CRC_CHECK_ON_READ)
  711. static bool crc_verify_success(uint16_t crc, uint16_t len_words, uint32_t const * const p_data)
  712. {
  713. uint16_t computed_crc;
  714. // The CRC is computed on the entire record, except the CRC field itself.
  715. // The record header is 12 bytes, out of these we have to skip bytes 6 to 8 where the
  716. // CRC itself is stored. Then we compute the CRC for the rest of the record, from byte 8 of
  717. // the header (where the record ID begins) to the end of the record data.
  718. computed_crc = crc16_compute((uint8_t const *)p_data, 6, NULL);
  719. computed_crc = crc16_compute((uint8_t const *)p_data + 8,
  720. (FDS_HEADER_SIZE_ID + len_words) * sizeof(uint32_t),
  721. &computed_crc);
  722. return (computed_crc == crc);
  723. }
  724. #endif
  725. static void gc_init(void)
  726. {
  727. m_gc.run_count++;
  728. m_gc.cur_page = 0;
  729. m_gc.resume = false;
  730. // Setup which pages to GC. Defer checking for open records and the can_gc flag,
  731. // as other operations might change those while GC is running.
  732. for (uint16_t i = 0; i < FDS_DATA_PAGES; i++)
  733. {
  734. m_gc.do_gc_page[i] = (m_pages[i].page_type == FDS_PAGE_DATA);
  735. }
  736. }
  737. // Obtain the next page to be garbage collected.
  738. // Returns true if there are pages left to garbage collect, returns false otherwise.
  739. static bool gc_page_next(uint16_t * const p_next_page)
  740. {
  741. bool ret = false;
  742. for (uint16_t i = 0; i < FDS_DATA_PAGES; i++)
  743. {
  744. if (m_gc.do_gc_page[i])
  745. {
  746. // Do not attempt to GC this page again.
  747. m_gc.do_gc_page[i] = false;
  748. // Only GC pages with no open records and with some records which have been deleted.
  749. if ((m_pages[i].records_open == 0) && (m_pages[i].can_gc == true))
  750. {
  751. *p_next_page = i;
  752. ret = true;
  753. break;
  754. }
  755. }
  756. }
  757. return ret;
  758. }
  759. static ret_code_t gc_swap_erase(void)
  760. {
  761. m_gc.state = GC_DISCARD_SWAP;
  762. m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
  763. return nrf_fstorage_erase(&m_fs, (uint32_t)m_swap_page.p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL);
  764. }
  765. // Erase the page being garbage collected, or erase the swap in case there are any open
  766. // records on the page being garbage collected.
  767. static ret_code_t gc_page_erase(void)
  768. {
  769. uint32_t ret;
  770. uint16_t const gc = m_gc.cur_page;
  771. if (m_pages[gc].records_open == 0)
  772. {
  773. m_gc.state = GC_ERASE_PAGE;
  774. ret = nrf_fstorage_erase(&m_fs, (uint32_t)m_pages[gc].p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL);
  775. }
  776. else
  777. {
  778. // If there are open records, stop garbage collection on this page.
  779. // Discard the swap and try to garbage collect another page.
  780. ret = gc_swap_erase();
  781. }
  782. return ret;
  783. }
  784. // Copy the current record to swap.
  785. static ret_code_t gc_record_copy(void)
  786. {
  787. fds_header_t const * const p_header = (fds_header_t*)m_gc.p_record_src;
  788. uint32_t const * const p_dest = m_swap_page.p_addr + m_swap_page.write_offset;
  789. uint16_t const record_len = FDS_HEADER_SIZE + p_header->length_words;
  790. m_gc.state = GC_COPY_RECORD;
  791. // Copy the record to swap; it is guaranteed to fit in the destination page,
  792. // so there is no need to check its size. This will either succeed or timeout.
  793. return nrf_fstorage_write(&m_fs, (uint32_t)p_dest, m_gc.p_record_src,
  794. record_len * sizeof(uint32_t),
  795. NULL);
  796. }
  797. static ret_code_t gc_record_find_next(void)
  798. {
  799. ret_code_t ret;
  800. // Find the next valid record to copy.
  801. if (record_find_next(m_gc.cur_page, &m_gc.p_record_src))
  802. {
  803. ret = gc_record_copy();
  804. }
  805. else
  806. {
  807. // No more records left to copy on this page; swap pages.
  808. ret = gc_page_erase();
  809. }
  810. return ret;
  811. }
  812. // Promote the swap by tagging it as a data page.
  813. static ret_code_t gc_swap_promote(void)
  814. {
  815. m_gc.state = GC_PROMOTE_SWAP;
  816. return page_tag_write_data(m_pages[m_gc.cur_page].p_addr);
  817. }
  818. // Tag the page just garbage collected as swap.
  819. static ret_code_t gc_tag_new_swap(void)
  820. {
  821. m_gc.state = GC_TAG_NEW_SWAP;
  822. m_gc.p_record_src = NULL;
  823. return page_tag_write_swap();
  824. }
  825. static ret_code_t gc_next_page(void)
  826. {
  827. if (!gc_page_next(&m_gc.cur_page))
  828. {
  829. // No pages left to GC; GC has terminated. Reset the state.
  830. m_gc.state = GC_BEGIN;
  831. m_gc.cur_page = 0;
  832. m_gc.p_record_src = NULL;
  833. return FDS_OP_COMPLETED;
  834. }
  835. return gc_record_find_next();
  836. }
  837. // Update the swap page offeset after a record has been successfully copied to it.
  838. static void gc_update_swap_offset(void)
  839. {
  840. fds_header_t const * const p_header = (fds_header_t*)m_gc.p_record_src;
  841. uint16_t const record_len = FDS_HEADER_SIZE + p_header->length_words;
  842. m_swap_page.write_offset += record_len;
  843. }
  844. static void gc_swap_pages(void)
  845. {
  846. // The page being garbage collected will be the new swap page,
  847. // and the current swap will be used as a data page (promoted).
  848. uint32_t const * const p_addr = m_swap_page.p_addr;
  849. m_swap_page.p_addr = m_pages[m_gc.cur_page].p_addr;
  850. m_pages[m_gc.cur_page].p_addr = p_addr;
  851. // Keep the offset for this page, but reset it for the swap.
  852. m_pages[m_gc.cur_page].write_offset = m_swap_page.write_offset;
  853. m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
  854. }
  855. static void gc_state_advance(void)
  856. {
  857. switch (m_gc.state)
  858. {
  859. case GC_BEGIN:
  860. gc_init();
  861. m_gc.state = GC_NEXT_PAGE;
  862. break;
  863. // A record was successfully copied.
  864. case GC_COPY_RECORD:
  865. gc_update_swap_offset();
  866. m_gc.state = GC_FIND_NEXT_RECORD;
  867. break;
  868. // A page was successfully erased. Prepare to promote the swap.
  869. case GC_ERASE_PAGE:
  870. gc_swap_pages();
  871. m_gc.state = GC_PROMOTE_SWAP;
  872. break;
  873. // Swap was discarded because the page being GC'ed had open records.
  874. case GC_DISCARD_SWAP:
  875. // Swap was successfully promoted.
  876. case GC_PROMOTE_SWAP:
  877. // Prepare to tag the page just GC'ed as swap.
  878. m_gc.state = GC_TAG_NEW_SWAP;
  879. break;
  880. case GC_TAG_NEW_SWAP:
  881. m_gc.state = GC_NEXT_PAGE;
  882. break;
  883. default:
  884. // Should not happen.
  885. break;
  886. }
  887. }
  888. // Initialize the filesystem.
  889. static ret_code_t init_execute(uint32_t prev_ret, fds_op_t * const p_op)
  890. {
  891. ret_code_t ret = FDS_ERR_INTERNAL;
  892. if (prev_ret != NRF_SUCCESS)
  893. {
  894. // A previous operation has timed out.
  895. m_flags.initializing = false;
  896. return FDS_ERR_OPERATION_TIMEOUT;
  897. }
  898. switch (p_op->init.step)
  899. {
  900. case FDS_OP_INIT_TAG_SWAP:
  901. {
  902. // The page write offset was determined previously by pages_init().
  903. p_op->init.step = FDS_OP_INIT_TAG_DATA;
  904. ret = page_tag_write_swap();
  905. } break;
  906. case FDS_OP_INIT_TAG_DATA:
  907. {
  908. // Tag remaining erased pages as data.
  909. bool write_reqd = false;
  910. for (uint16_t i = 0; i < FDS_DATA_PAGES; i++)
  911. {
  912. if (m_pages[i].page_type == FDS_PAGE_ERASED)
  913. {
  914. m_pages[i].page_type = FDS_PAGE_DATA;
  915. write_reqd = true;
  916. ret = page_tag_write_data(m_pages[i].p_addr);
  917. break;
  918. }
  919. }
  920. if (!write_reqd)
  921. {
  922. m_flags.initialized = true;
  923. m_flags.initializing = false;
  924. return FDS_OP_COMPLETED;
  925. }
  926. } break;
  927. case FDS_OP_INIT_ERASE_SWAP:
  928. {
  929. // If the swap is going to be discarded then reset its write_offset.
  930. p_op->init.step = FDS_OP_INIT_TAG_SWAP;
  931. m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
  932. ret = nrf_fstorage_erase(&m_fs, (uint32_t)m_swap_page.p_addr, FDS_PHY_PAGES_IN_VPAGE, NULL);
  933. } break;
  934. case FDS_OP_INIT_PROMOTE_SWAP:
  935. {
  936. p_op->init.step = FDS_OP_INIT_TAG_SWAP;
  937. // When promoting the swap, keep the write_offset set by pages_init().
  938. ret = page_tag_write_data(m_swap_page.p_addr);
  939. uint16_t const gc = m_gc.cur_page;
  940. uint32_t const * const p_old_swap = m_swap_page.p_addr;
  941. // Execute the swap.
  942. m_swap_page.p_addr = m_pages[gc].p_addr;
  943. m_pages[gc].p_addr = p_old_swap;
  944. // Copy the offset from the swap to the new page.
  945. m_pages[gc].write_offset = m_swap_page.write_offset;
  946. m_swap_page.write_offset = FDS_PAGE_TAG_SIZE;
  947. m_pages[gc].page_type = FDS_PAGE_DATA;
  948. } break;
  949. default:
  950. // Should not happen.
  951. break;
  952. }
  953. if (ret != FDS_SUCCESS)
  954. {
  955. // fstorage queue was full.
  956. m_flags.initializing = false;
  957. return FDS_ERR_BUSY;
  958. }
  959. return FDS_OP_EXECUTING;
  960. }
  961. // Executes write and update operations.
  962. static ret_code_t write_execute(uint32_t prev_ret, fds_op_t * const p_op)
  963. {
  964. ret_code_t ret;
  965. uint32_t * p_write_addr;
  966. fds_page_t * const p_page = &m_pages[p_op->write.page];
  967. // This must persist across calls.
  968. static fds_record_desc_t desc = {0};
  969. // When a record is updated, this variable will hold the page where the old
  970. // copy was stored. This will be used to set the can_gc flag when the header is
  971. // invalidated (FDS_OP_WRITE_FLAG_DIRTY).
  972. static uint16_t page;
  973. if (prev_ret != NRF_SUCCESS)
  974. {
  975. // The previous operation has timed out, update offsets.
  976. page_offsets_update(p_page, p_op);
  977. return FDS_ERR_OPERATION_TIMEOUT;
  978. }
  979. // Compute the address where to write data.
  980. p_write_addr = (uint32_t*)(p_page->p_addr + p_page->write_offset);
  981. // Execute the current step of the operation, and set one to be executed next.
  982. switch (p_op->write.step)
  983. {
  984. case FDS_OP_WRITE_FIND_RECORD:
  985. {
  986. // The first step of updating a record constists of locating the copy to be deleted.
  987. // If the old copy couldn't be found for any reason then the update should fail.
  988. // This prevents duplicates when queuing multiple updates of the same record.
  989. desc.p_record = NULL;
  990. desc.record_id = p_op->write.record_to_delete;
  991. if (!record_find_by_desc(&desc, &page))
  992. {
  993. return FDS_ERR_NOT_FOUND;
  994. }
  995. // Setting the step is redundant since we are falling through.
  996. }
  997. // Fallthrough to FDS_OP_WRITE_HEADER_BEGIN.
  998. case FDS_OP_WRITE_HEADER_BEGIN:
  999. ret = record_header_write_begin(p_op, p_write_addr);
  1000. break;
  1001. case FDS_OP_WRITE_RECORD_ID:
  1002. ret = record_header_write_id(p_op, p_write_addr);
  1003. break;
  1004. case FDS_OP_WRITE_DATA:
  1005. ret = record_write_data(p_op, p_write_addr);
  1006. break;
  1007. case FDS_OP_WRITE_HEADER_FINALIZE:
  1008. ret = record_header_write_finalize(p_op, p_write_addr);
  1009. break;
  1010. case FDS_OP_WRITE_FLAG_DIRTY:
  1011. p_op->write.step = FDS_OP_WRITE_DONE;
  1012. ret = record_header_flag_dirty((uint32_t*)desc.p_record, page);
  1013. break;
  1014. case FDS_OP_WRITE_DONE:
  1015. ret = FDS_OP_COMPLETED;
  1016. #if (FDS_CRC_CHECK_ON_WRITE)
  1017. if (!crc_verify_success(p_op->write.header.crc16,
  1018. p_op->write.header.length_words,
  1019. p_write_addr))
  1020. {
  1021. ret = FDS_ERR_CRC_CHECK_FAILED;
  1022. }
  1023. #endif
  1024. break;
  1025. default:
  1026. ret = FDS_ERR_INTERNAL;
  1027. break;
  1028. }
  1029. // An operation has either completed or failed. It may have failed because fstorage
  1030. // ran out of memory, or because the user tried to delete a record which did not exist.
  1031. if (ret != FDS_OP_EXECUTING)
  1032. {
  1033. // There won't be another callback for this operation, so update the page offset now.
  1034. page_offsets_update(p_page, p_op);
  1035. }
  1036. return ret;
  1037. }
  1038. static ret_code_t delete_execute(uint32_t prev_ret, fds_op_t * const p_op)
  1039. {
  1040. ret_code_t ret;
  1041. if (prev_ret != NRF_SUCCESS)
  1042. {
  1043. return FDS_ERR_OPERATION_TIMEOUT;
  1044. }
  1045. switch (p_op->del.step)
  1046. {
  1047. case FDS_OP_DEL_RECORD_FLAG_DIRTY:
  1048. p_op->del.step = FDS_OP_DEL_DONE;
  1049. ret = record_find_and_delete(p_op);
  1050. break;
  1051. case FDS_OP_DEL_FILE_FLAG_DIRTY:
  1052. ret = file_find_and_delete(p_op);
  1053. if (ret == FDS_ERR_NOT_FOUND)
  1054. {
  1055. // No more records could be found.
  1056. // There won't be another callback for this operation, so return now.
  1057. ret = FDS_OP_COMPLETED;
  1058. }
  1059. break;
  1060. case FDS_OP_DEL_DONE:
  1061. ret = FDS_OP_COMPLETED;
  1062. break;
  1063. default:
  1064. ret = FDS_ERR_INTERNAL;
  1065. break;
  1066. }
  1067. return ret;
  1068. }
  1069. static ret_code_t gc_execute(uint32_t prev_ret)
  1070. {
  1071. ret_code_t ret;
  1072. if (prev_ret != NRF_SUCCESS)
  1073. {
  1074. return FDS_ERR_OPERATION_TIMEOUT;
  1075. }
  1076. if (m_gc.resume)
  1077. {
  1078. m_gc.resume = false;
  1079. }
  1080. else
  1081. {
  1082. gc_state_advance();
  1083. }
  1084. switch (m_gc.state)
  1085. {
  1086. case GC_NEXT_PAGE:
  1087. ret = gc_next_page();
  1088. break;
  1089. case GC_FIND_NEXT_RECORD:
  1090. ret = gc_record_find_next();
  1091. break;
  1092. case GC_COPY_RECORD:
  1093. ret = gc_record_copy();
  1094. break;
  1095. case GC_ERASE_PAGE:
  1096. ret = gc_page_erase();
  1097. break;
  1098. case GC_PROMOTE_SWAP:
  1099. ret = gc_swap_promote();
  1100. break;
  1101. case GC_TAG_NEW_SWAP:
  1102. ret = gc_tag_new_swap();
  1103. break;
  1104. default:
  1105. // Should not happen.
  1106. ret = FDS_ERR_INTERNAL;
  1107. break;
  1108. }
  1109. // Either FDS_OP_EXECUTING, FDS_OP_COMPLETED, FDS_ERR_BUSY or FDS_ERR_INTERNAL.
  1110. return ret;
  1111. }
  1112. static void queue_process(ret_code_t result)
  1113. {
  1114. static fds_op_t * m_p_cur_op; // Current fds operation.
  1115. static nrf_atfifo_item_get_t m_iget_ctx; // Queue context for the current operation.
  1116. while (true)
  1117. {
  1118. if (m_p_cur_op == NULL)
  1119. {
  1120. // Load the next from the queue if no operation is being executed.
  1121. m_p_cur_op = queue_load(&m_iget_ctx);
  1122. }
  1123. /* We can reach here in three ways:
  1124. * from queue_start(): something was just queued
  1125. * from the fstorage event handler: an operation is being executed
  1126. * looping: we only loop if there are operations still in the queue
  1127. *
  1128. * In all these three cases, m_p_cur_op != NULL.
  1129. */
  1130. ASSERT(m_p_cur_op != NULL);
  1131. switch (m_p_cur_op->op_code)
  1132. {
  1133. case FDS_OP_INIT:
  1134. result = init_execute(result, m_p_cur_op);
  1135. break;
  1136. case FDS_OP_WRITE:
  1137. case FDS_OP_UPDATE:
  1138. result = write_execute(result, m_p_cur_op);
  1139. break;
  1140. case FDS_OP_DEL_RECORD:
  1141. case FDS_OP_DEL_FILE:
  1142. result = delete_execute(result, m_p_cur_op);
  1143. break;
  1144. case FDS_OP_GC:
  1145. result = gc_execute(result);
  1146. break;
  1147. default:
  1148. result = FDS_ERR_INTERNAL;
  1149. break;
  1150. }
  1151. if (result == FDS_OP_EXECUTING)
  1152. {
  1153. // The operation has not completed yet. Wait for the next system event.
  1154. break;
  1155. }
  1156. // The operation has completed (either successfully or with an error).
  1157. // - send an event to the user
  1158. // - free the operation buffer
  1159. // - execute any other queued operations
  1160. fds_evt_t evt =
  1161. {
  1162. // The operation might have failed for one of the following reasons:
  1163. // FDS_ERR_BUSY - flash subsystem can't accept the operation
  1164. // FDS_ERR_OPERATION_TIMEOUT - flash subsystem timed out
  1165. // FDS_ERR_CRC_CHECK_FAILED - a CRC check failed
  1166. // FDS_ERR_NOT_FOUND - no record found (delete/update)
  1167. .result = (result == FDS_OP_COMPLETED) ? FDS_SUCCESS : result,
  1168. };
  1169. event_prepare(m_p_cur_op, &evt);
  1170. event_send(&evt);
  1171. // Zero the pointer to the current operation so that this function
  1172. // will fetch a new one from the queue next time it is run.
  1173. m_p_cur_op = NULL;
  1174. // The result of the operation must be reset upon re-entering the loop to ensure
  1175. // the next operation won't be affected by eventual errors in previous operations.
  1176. result = NRF_SUCCESS;
  1177. // Free the queue element used by the current operation.
  1178. queue_free(&m_iget_ctx);
  1179. if (!queue_has_next())
  1180. {
  1181. // No more elements left. Nothing to do.
  1182. break;
  1183. }
  1184. }
  1185. }
  1186. static void queue_start(void)
  1187. {
  1188. if (!nrf_atomic_u32_fetch_add(&m_queued_op_cnt, 1))
  1189. {
  1190. queue_process(NRF_SUCCESS);
  1191. }
  1192. }
  1193. static void fs_event_handler(nrf_fstorage_evt_t * p_evt)
  1194. {
  1195. queue_process(p_evt->result);
  1196. }
  1197. // Enqueues write and update operations.
  1198. static ret_code_t write_enqueue(fds_record_desc_t * const p_desc,
  1199. fds_record_t const * const p_record,
  1200. fds_reserve_token_t const * const p_tok,
  1201. fds_op_code_t op_code)
  1202. {
  1203. ret_code_t ret;
  1204. uint16_t page;
  1205. uint16_t crc = 0;
  1206. uint16_t length_words = 0;
  1207. fds_op_t * p_op;
  1208. nrf_atfifo_item_put_t iput_ctx;
  1209. if (!m_flags.initialized)
  1210. {
  1211. return FDS_ERR_NOT_INITIALIZED;
  1212. }
  1213. if (p_record == NULL)
  1214. {
  1215. return FDS_ERR_NULL_ARG;
  1216. }
  1217. if ((p_record->file_id == FDS_FILE_ID_INVALID) ||
  1218. (p_record->key == FDS_RECORD_KEY_DIRTY))
  1219. {
  1220. return FDS_ERR_INVALID_ARG;
  1221. }
  1222. if (!is_word_aligned(p_record->data.p_data))
  1223. {
  1224. return FDS_ERR_UNALIGNED_ADDR;
  1225. }
  1226. // No space was previously reserved in flash for this operation.
  1227. if (p_tok == NULL)
  1228. {
  1229. // Find a page where to write data.
  1230. length_words = p_record->data.length_words;
  1231. ret = write_space_reserve(length_words, &page);
  1232. if (ret != FDS_SUCCESS)
  1233. {
  1234. // There is either not enough space in flash (FDS_ERR_NO_SPACE_IN_FLASH) or
  1235. // the record exceeds the size of virtual page (FDS_ERR_RECORD_TOO_LARGE).
  1236. return ret;
  1237. }
  1238. }
  1239. else
  1240. {
  1241. page = p_tok->page;
  1242. length_words = p_tok->length_words;
  1243. }
  1244. // Get a buffer on the queue of operations.
  1245. p_op = queue_buf_get(&iput_ctx);
  1246. if (p_op == NULL)
  1247. {
  1248. CRITICAL_SECTION_ENTER();
  1249. write_space_free(length_words, page);
  1250. CRITICAL_SECTION_EXIT();
  1251. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1252. }
  1253. // Initialize the operation.
  1254. p_op->op_code = op_code;
  1255. p_op->write.step = FDS_OP_WRITE_HEADER_BEGIN;
  1256. p_op->write.page = page;
  1257. p_op->write.p_data = p_record->data.p_data;
  1258. p_op->write.header.record_id = record_id_new();
  1259. p_op->write.header.file_id = p_record->file_id;
  1260. p_op->write.header.record_key = p_record->key;
  1261. p_op->write.header.length_words = length_words;
  1262. if (op_code == FDS_OP_UPDATE)
  1263. {
  1264. p_op->write.step = FDS_OP_WRITE_FIND_RECORD;
  1265. // Save the record ID of the record to be updated.
  1266. p_op->write.record_to_delete = p_desc->record_id;
  1267. }
  1268. #if (FDS_CRC_CHECK_ON_READ)
  1269. // First, compute the CRC for the first 6 bytes of the header which contain the
  1270. // record key, length and file ID, then, compute the CRC of the record ID (4 bytes).
  1271. crc = crc16_compute((uint8_t*)&p_op->write.header, 6, NULL);
  1272. crc = crc16_compute((uint8_t*)&p_op->write.header.record_id, 4, &crc);
  1273. // Compute the CRC for the record data.
  1274. crc = crc16_compute((uint8_t*)p_record->data.p_data,
  1275. p_record->data.length_words * sizeof(uint32_t), &crc);
  1276. #endif
  1277. p_op->write.header.crc16 = crc;
  1278. queue_buf_store(&iput_ctx);
  1279. // Initialize the record descriptor, if provided.
  1280. if (p_desc != NULL)
  1281. {
  1282. p_desc->p_record = NULL;
  1283. // Don't invoke record_id_new() again !
  1284. p_desc->record_id = p_op->write.header.record_id;
  1285. p_desc->record_is_open = false;
  1286. p_desc->gc_run_count = m_gc.run_count;
  1287. }
  1288. // Start processing the queue, if necessary.
  1289. queue_start();
  1290. return FDS_SUCCESS;
  1291. }
  1292. ret_code_t fds_register(fds_cb_t cb)
  1293. {
  1294. ret_code_t ret;
  1295. if (m_users == FDS_MAX_USERS)
  1296. {
  1297. ret = FDS_ERR_USER_LIMIT_REACHED;
  1298. }
  1299. else
  1300. {
  1301. m_cb_table[m_users] = cb;
  1302. (void) nrf_atomic_u32_add(&m_users, 1);
  1303. ret = FDS_SUCCESS;
  1304. }
  1305. return ret;
  1306. }
  1307. static uint32_t flash_end_addr(void)
  1308. {
  1309. uint32_t const bootloader_addr = BOOTLOADER_ADDRESS;
  1310. uint32_t const page_sz = NRF_FICR->CODEPAGESIZE;
  1311. #if defined(NRF52810_XXAA) || defined(NRF52811_XXAA)
  1312. // Hardcode the number of flash pages, necessary for SoC emulation.
  1313. // nRF52810 on nRF52832 and
  1314. // nRF52811 on nRF52840
  1315. uint32_t const code_sz = 48;
  1316. #else
  1317. uint32_t const code_sz = NRF_FICR->CODESIZE;
  1318. #endif
  1319. uint32_t end_addr = (bootloader_addr != 0xFFFFFFFF) ? bootloader_addr : (code_sz * page_sz);
  1320. return end_addr - (FDS_PHY_PAGES_RESERVED * FDS_PHY_PAGE_SIZE * sizeof(uint32_t));
  1321. }
  1322. static void flash_bounds_set(void)
  1323. {
  1324. uint32_t flash_size = (FDS_PHY_PAGES * FDS_PHY_PAGE_SIZE * sizeof(uint32_t));
  1325. m_fs.end_addr = flash_end_addr();
  1326. m_fs.start_addr = m_fs.end_addr - flash_size;
  1327. }
  1328. static ret_code_t flash_subsystem_init(void)
  1329. {
  1330. flash_bounds_set();
  1331. #if (FDS_BACKEND == NRF_FSTORAGE_SD)
  1332. return nrf_fstorage_init(&m_fs, &nrf_fstorage_sd, NULL);
  1333. #elif (FDS_BACKEND == NRF_FSTORAGE_NVMC)
  1334. return nrf_fstorage_init(&m_fs, &nrf_fstorage_nvmc, NULL);
  1335. #else
  1336. #error Invalid FDS_BACKEND.
  1337. #endif
  1338. }
  1339. static void queue_init(void)
  1340. {
  1341. (void) NRF_ATFIFO_INIT(m_queue);
  1342. }
  1343. ret_code_t fds_init(void)
  1344. {
  1345. ret_code_t ret;
  1346. fds_evt_t const evt_success =
  1347. {
  1348. .id = FDS_EVT_INIT,
  1349. .result = FDS_SUCCESS,
  1350. };
  1351. if (m_flags.initialized)
  1352. {
  1353. // No initialization is necessary. Notify the application immediately.
  1354. event_send(&evt_success);
  1355. return FDS_SUCCESS;
  1356. }
  1357. if (nrf_atomic_flag_set_fetch(&m_flags.initializing))
  1358. {
  1359. // If we were already initializing, return.
  1360. return FDS_SUCCESS;
  1361. }
  1362. // Otherwise, the flag is set and we proceed to initialization.
  1363. ret = flash_subsystem_init();
  1364. if (ret != NRF_SUCCESS)
  1365. {
  1366. return ret;
  1367. }
  1368. queue_init();
  1369. // Initialize the page structure (m_pages), and determine which
  1370. // initialization steps are required given the current state of the filesystem.
  1371. fds_init_opts_t init_opts = pages_init();
  1372. switch (init_opts)
  1373. {
  1374. case NO_PAGES:
  1375. case NO_SWAP:
  1376. return FDS_ERR_NO_PAGES;
  1377. case ALREADY_INSTALLED:
  1378. {
  1379. // No initialization is necessary. Notify the application immediately.
  1380. m_flags.initialized = true;
  1381. m_flags.initializing = false;
  1382. event_send(&evt_success);
  1383. return FDS_SUCCESS;
  1384. }
  1385. default:
  1386. break;
  1387. }
  1388. // A write operation is necessary to initialize the fileystem.
  1389. nrf_atfifo_item_put_t iput_ctx;
  1390. fds_op_t * p_op = queue_buf_get(&iput_ctx);
  1391. if (p_op == NULL)
  1392. {
  1393. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1394. }
  1395. p_op->op_code = FDS_OP_INIT;
  1396. switch (init_opts)
  1397. {
  1398. case FRESH_INSTALL:
  1399. case TAG_SWAP:
  1400. p_op->init.step = FDS_OP_INIT_TAG_SWAP;
  1401. break;
  1402. case PROMOTE_SWAP:
  1403. case PROMOTE_SWAP_INST:
  1404. p_op->init.step = FDS_OP_INIT_PROMOTE_SWAP;
  1405. break;
  1406. case DISCARD_SWAP:
  1407. p_op->init.step = FDS_OP_INIT_ERASE_SWAP;
  1408. break;
  1409. case TAG_DATA:
  1410. case TAG_DATA_INST:
  1411. p_op->init.step = FDS_OP_INIT_TAG_DATA;
  1412. break;
  1413. default:
  1414. // Should not happen.
  1415. break;
  1416. }
  1417. queue_buf_store(&iput_ctx);
  1418. queue_start();
  1419. return FDS_SUCCESS;
  1420. }
  1421. ret_code_t fds_record_open(fds_record_desc_t * const p_desc,
  1422. fds_flash_record_t * const p_flash_rec)
  1423. {
  1424. uint16_t page;
  1425. if ((p_desc == NULL) || (p_flash_rec == NULL))
  1426. {
  1427. return FDS_ERR_NULL_ARG;
  1428. }
  1429. // Find the record if necessary.
  1430. if (record_find_by_desc(p_desc, &page))
  1431. {
  1432. fds_header_t const * const p_header = (fds_header_t*)p_desc->p_record;
  1433. #if (FDS_CRC_CHECK_ON_READ)
  1434. if (!crc_verify_success(p_header->crc16,
  1435. p_header->length_words,
  1436. p_desc->p_record))
  1437. {
  1438. return FDS_ERR_CRC_CHECK_FAILED;
  1439. }
  1440. #endif
  1441. (void) nrf_atomic_u32_add(&m_pages[page].records_open, 1);
  1442. // Initialize p_flash_rec.
  1443. p_flash_rec->p_header = p_header;
  1444. p_flash_rec->p_data = (p_desc->p_record + FDS_HEADER_SIZE);
  1445. // Set the record as open in the descriptor.
  1446. p_desc->record_is_open = true;
  1447. return FDS_SUCCESS;
  1448. }
  1449. // The record could not be found.
  1450. // It either never existed or it has been deleted.
  1451. return FDS_ERR_NOT_FOUND;
  1452. }
  1453. ret_code_t fds_record_close(fds_record_desc_t * const p_desc)
  1454. {
  1455. ret_code_t ret;
  1456. uint16_t page;
  1457. if (p_desc == NULL)
  1458. {
  1459. return FDS_ERR_NULL_ARG;
  1460. }
  1461. if (record_find_by_desc((fds_record_desc_t*)p_desc, &page))
  1462. {
  1463. CRITICAL_SECTION_ENTER();
  1464. if ((m_pages[page].records_open > 0) && (p_desc->record_is_open))
  1465. {
  1466. m_pages[page].records_open--;
  1467. p_desc->record_is_open = false;
  1468. ret = FDS_SUCCESS;
  1469. }
  1470. else
  1471. {
  1472. ret = FDS_ERR_NO_OPEN_RECORDS;
  1473. }
  1474. CRITICAL_SECTION_EXIT();
  1475. }
  1476. else
  1477. {
  1478. ret = FDS_ERR_NOT_FOUND;
  1479. }
  1480. return ret;
  1481. }
  1482. ret_code_t fds_reserve(fds_reserve_token_t * const p_tok, uint16_t length_words)
  1483. {
  1484. ret_code_t ret;
  1485. uint16_t page;
  1486. if (!m_flags.initialized)
  1487. {
  1488. return FDS_ERR_NOT_INITIALIZED;
  1489. }
  1490. if (p_tok == NULL)
  1491. {
  1492. return FDS_ERR_NULL_ARG;
  1493. }
  1494. ret = write_space_reserve(length_words, &page);
  1495. if (ret == FDS_SUCCESS)
  1496. {
  1497. p_tok->page = page;
  1498. p_tok->length_words = length_words;
  1499. }
  1500. return ret;
  1501. }
  1502. ret_code_t fds_reserve_cancel(fds_reserve_token_t * const p_tok)
  1503. {
  1504. ret_code_t ret;
  1505. if (!m_flags.initialized)
  1506. {
  1507. return FDS_ERR_NOT_INITIALIZED;
  1508. }
  1509. if (p_tok == NULL)
  1510. {
  1511. return FDS_ERR_NULL_ARG;
  1512. }
  1513. if (p_tok->page > FDS_DATA_PAGES)
  1514. {
  1515. // The page does not exist. This shouldn't happen.
  1516. return FDS_ERR_INVALID_ARG;
  1517. }
  1518. fds_page_t const * const p_page = &m_pages[p_tok->page];
  1519. CRITICAL_SECTION_ENTER();
  1520. if ((FDS_HEADER_SIZE + p_tok->length_words) <= p_page->words_reserved)
  1521. {
  1522. // Free reserved space.
  1523. write_space_free(p_tok->length_words, p_tok->page);
  1524. // Clean the token.
  1525. p_tok->page = 0;
  1526. p_tok->length_words = 0;
  1527. ret = FDS_SUCCESS;
  1528. }
  1529. else
  1530. {
  1531. // We are trying to cancel a reservation of more words than how many are
  1532. // currently reserved on the page. Clearly, this shouldn't happen.
  1533. ret = FDS_ERR_INVALID_ARG;
  1534. }
  1535. CRITICAL_SECTION_EXIT();
  1536. return ret;
  1537. }
  1538. ret_code_t fds_record_write(fds_record_desc_t * const p_desc,
  1539. fds_record_t const * const p_record)
  1540. {
  1541. return write_enqueue(p_desc, p_record, NULL, FDS_OP_WRITE);
  1542. }
  1543. ret_code_t fds_record_write_reserved(fds_record_desc_t * const p_desc,
  1544. fds_record_t const * const p_record,
  1545. fds_reserve_token_t const * const p_tok)
  1546. {
  1547. // A NULL token is not allowed when writing to a reserved space.
  1548. if (p_tok == NULL)
  1549. {
  1550. return FDS_ERR_NULL_ARG;
  1551. }
  1552. return write_enqueue(p_desc, p_record, p_tok, FDS_OP_WRITE);
  1553. }
  1554. ret_code_t fds_record_update(fds_record_desc_t * const p_desc,
  1555. fds_record_t const * const p_record)
  1556. {
  1557. // A NULL descriptor is not allowed when updating a record.
  1558. if (p_desc == NULL)
  1559. {
  1560. return FDS_ERR_NULL_ARG;
  1561. }
  1562. return write_enqueue(p_desc, p_record, NULL, FDS_OP_UPDATE);
  1563. }
  1564. ret_code_t fds_record_delete(fds_record_desc_t * const p_desc)
  1565. {
  1566. fds_op_t * p_op;
  1567. nrf_atfifo_item_put_t iput_ctx;
  1568. if (!m_flags.initialized)
  1569. {
  1570. return FDS_ERR_NOT_INITIALIZED;
  1571. }
  1572. if (p_desc == NULL)
  1573. {
  1574. return FDS_ERR_NULL_ARG;
  1575. }
  1576. p_op = queue_buf_get(&iput_ctx);
  1577. if (p_op == NULL)
  1578. {
  1579. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1580. }
  1581. p_op->op_code = FDS_OP_DEL_RECORD;
  1582. p_op->del.step = FDS_OP_DEL_RECORD_FLAG_DIRTY;
  1583. p_op->del.record_to_delete = p_desc->record_id;
  1584. queue_buf_store(&iput_ctx);
  1585. queue_start();
  1586. return FDS_SUCCESS;
  1587. }
  1588. ret_code_t fds_file_delete(uint16_t file_id)
  1589. {
  1590. fds_op_t * p_op;
  1591. nrf_atfifo_item_put_t iput_ctx;
  1592. if (!m_flags.initialized)
  1593. {
  1594. return FDS_ERR_NOT_INITIALIZED;
  1595. }
  1596. if (file_id == FDS_FILE_ID_INVALID)
  1597. {
  1598. return FDS_ERR_INVALID_ARG;
  1599. }
  1600. p_op = queue_buf_get(&iput_ctx);
  1601. if (p_op == NULL)
  1602. {
  1603. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1604. }
  1605. p_op->op_code = FDS_OP_DEL_FILE;
  1606. p_op->del.step = FDS_OP_DEL_FILE_FLAG_DIRTY;
  1607. p_op->del.file_id = file_id;
  1608. queue_buf_store(&iput_ctx);
  1609. queue_start();
  1610. return FDS_SUCCESS;
  1611. }
  1612. ret_code_t fds_gc(void)
  1613. {
  1614. fds_op_t * p_op;
  1615. nrf_atfifo_item_put_t iput_ctx;
  1616. if (!m_flags.initialized)
  1617. {
  1618. return FDS_ERR_NOT_INITIALIZED;
  1619. }
  1620. p_op = queue_buf_get(&iput_ctx);
  1621. if (p_op == NULL)
  1622. {
  1623. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1624. }
  1625. p_op->op_code = FDS_OP_GC;
  1626. queue_buf_store(&iput_ctx);
  1627. if (m_gc.state != GC_BEGIN)
  1628. {
  1629. // Resume GC by retrying the last step.
  1630. m_gc.resume = true;
  1631. }
  1632. queue_start();
  1633. return FDS_SUCCESS;
  1634. }
  1635. ret_code_t fds_record_iterate(fds_record_desc_t * const p_desc,
  1636. fds_find_token_t * const p_token)
  1637. {
  1638. return record_find(NULL, NULL, p_desc, p_token);
  1639. }
  1640. ret_code_t fds_record_find(uint16_t file_id,
  1641. uint16_t record_key,
  1642. fds_record_desc_t * const p_desc,
  1643. fds_find_token_t * const p_token)
  1644. {
  1645. return record_find(&file_id, &record_key, p_desc, p_token);
  1646. }
  1647. ret_code_t fds_record_find_by_key(uint16_t record_key,
  1648. fds_record_desc_t * const p_desc,
  1649. fds_find_token_t * const p_token)
  1650. {
  1651. return record_find(NULL, &record_key, p_desc, p_token);
  1652. }
  1653. ret_code_t fds_record_find_in_file(uint16_t file_id,
  1654. fds_record_desc_t * const p_desc,
  1655. fds_find_token_t * const p_token)
  1656. {
  1657. return record_find(&file_id, NULL, p_desc, p_token);
  1658. }
  1659. ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * const p_desc,
  1660. uint32_t record_id)
  1661. {
  1662. if (p_desc == NULL)
  1663. {
  1664. return FDS_ERR_NULL_ARG;
  1665. }
  1666. // Zero the descriptor and set the record_id field.
  1667. memset(p_desc, 0x00, sizeof(fds_record_desc_t));
  1668. p_desc->record_id = record_id;
  1669. return FDS_SUCCESS;
  1670. }
  1671. ret_code_t fds_record_id_from_desc(fds_record_desc_t const * const p_desc,
  1672. uint32_t * const p_record_id)
  1673. {
  1674. if ((p_desc == NULL) || (p_record_id == NULL))
  1675. {
  1676. return FDS_ERR_NULL_ARG;
  1677. }
  1678. *p_record_id = p_desc->record_id;
  1679. return FDS_SUCCESS;
  1680. }
  1681. ret_code_t fds_stat(fds_stat_t * const p_stat)
  1682. {
  1683. uint16_t const words_in_page = FDS_PAGE_SIZE;
  1684. // The largest number of free contiguous words on any page.
  1685. uint16_t contig_words = 0;
  1686. if (!m_flags.initialized)
  1687. {
  1688. return FDS_ERR_NOT_INITIALIZED;
  1689. }
  1690. if (p_stat == NULL)
  1691. {
  1692. return FDS_ERR_NULL_ARG;
  1693. }
  1694. memset(p_stat, 0x00, sizeof(fds_stat_t));
  1695. p_stat->pages_available = FDS_VIRTUAL_PAGES;
  1696. for (uint16_t page = 0; page < FDS_DATA_PAGES; page++)
  1697. {
  1698. uint16_t const words_used = m_pages[page].write_offset + m_pages[page].words_reserved;
  1699. if (page_identify(m_pages[page].p_addr) == FDS_PAGE_UNDEFINED)
  1700. {
  1701. p_stat->pages_available--;
  1702. }
  1703. p_stat->open_records += m_pages[page].records_open;
  1704. p_stat->words_reserved += m_pages[page].words_reserved;
  1705. p_stat->words_used += words_used;
  1706. contig_words = (words_in_page - words_used);
  1707. if (contig_words > p_stat->largest_contig)
  1708. {
  1709. p_stat->largest_contig = contig_words;
  1710. }
  1711. records_stat(page,
  1712. &p_stat->valid_records,
  1713. &p_stat->dirty_records,
  1714. &p_stat->freeable_words,
  1715. &p_stat->corruption);
  1716. }
  1717. return FDS_SUCCESS;
  1718. }
  1719. #endif //NRF_MODULE_ENABLED(FDS)