fds.c 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164
  1. /**
  2. * Copyright (c) 2015 - 2018, 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 = NRF_UICR->NRFFW[0];
  1310. uint32_t const page_sz = NRF_FICR->CODEPAGESIZE;
  1311. #ifndef NRF52810_XXAA
  1312. uint32_t const code_sz = NRF_FICR->CODESIZE;
  1313. #else
  1314. // Number of flash pages, necessary to emulate the NRF52810 on NRF52832.
  1315. uint32_t const code_sz = 48;
  1316. #endif
  1317. return (bootloader_addr != 0xFFFFFFFF) ? bootloader_addr : (code_sz * page_sz);
  1318. }
  1319. static void flash_bounds_set(void)
  1320. {
  1321. uint32_t flash_size = (FDS_PHY_PAGES * FDS_PHY_PAGE_SIZE * sizeof(uint32_t));
  1322. m_fs.end_addr = flash_end_addr();
  1323. m_fs.start_addr = m_fs.end_addr - flash_size;
  1324. }
  1325. static ret_code_t flash_subsystem_init(void)
  1326. {
  1327. flash_bounds_set();
  1328. #if (FDS_BACKEND == NRF_FSTORAGE_SD)
  1329. return nrf_fstorage_init(&m_fs, &nrf_fstorage_sd, NULL);
  1330. #elif (FDS_BACKEND == NRF_FSTORAGE_NVMC)
  1331. return nrf_fstorage_init(&m_fs, &nrf_fstorage_nvmc, NULL);
  1332. #else
  1333. #error Invalid FDS_BACKEND.
  1334. #endif
  1335. }
  1336. static void queue_init(void)
  1337. {
  1338. (void) NRF_ATFIFO_INIT(m_queue);
  1339. }
  1340. ret_code_t fds_init(void)
  1341. {
  1342. ret_code_t ret;
  1343. fds_evt_t const evt_success =
  1344. {
  1345. .id = FDS_EVT_INIT,
  1346. .result = FDS_SUCCESS,
  1347. };
  1348. if (m_flags.initialized)
  1349. {
  1350. // No initialization is necessary. Notify the application immediately.
  1351. event_send(&evt_success);
  1352. return FDS_SUCCESS;
  1353. }
  1354. if (nrf_atomic_flag_set_fetch(&m_flags.initializing))
  1355. {
  1356. // If we were already initializing, return.
  1357. return FDS_SUCCESS;
  1358. }
  1359. // Otherwise, the flag is set and we proceed to initialization.
  1360. ret = flash_subsystem_init();
  1361. if (ret != NRF_SUCCESS)
  1362. {
  1363. return ret;
  1364. }
  1365. queue_init();
  1366. // Initialize the page structure (m_pages), and determine which
  1367. // initialization steps are required given the current state of the filesystem.
  1368. fds_init_opts_t init_opts = pages_init();
  1369. switch (init_opts)
  1370. {
  1371. case NO_PAGES:
  1372. case NO_SWAP:
  1373. return FDS_ERR_NO_PAGES;
  1374. case ALREADY_INSTALLED:
  1375. {
  1376. // No initialization is necessary. Notify the application immediately.
  1377. m_flags.initialized = true;
  1378. m_flags.initializing = false;
  1379. event_send(&evt_success);
  1380. return FDS_SUCCESS;
  1381. }
  1382. default:
  1383. break;
  1384. }
  1385. // A write operation is necessary to initialize the fileystem.
  1386. nrf_atfifo_item_put_t iput_ctx;
  1387. fds_op_t * p_op = queue_buf_get(&iput_ctx);
  1388. if (p_op == NULL)
  1389. {
  1390. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1391. }
  1392. p_op->op_code = FDS_OP_INIT;
  1393. switch (init_opts)
  1394. {
  1395. case FRESH_INSTALL:
  1396. case TAG_SWAP:
  1397. p_op->init.step = FDS_OP_INIT_TAG_SWAP;
  1398. break;
  1399. case PROMOTE_SWAP:
  1400. case PROMOTE_SWAP_INST:
  1401. p_op->init.step = FDS_OP_INIT_PROMOTE_SWAP;
  1402. break;
  1403. case DISCARD_SWAP:
  1404. p_op->init.step = FDS_OP_INIT_ERASE_SWAP;
  1405. break;
  1406. case TAG_DATA:
  1407. case TAG_DATA_INST:
  1408. p_op->init.step = FDS_OP_INIT_TAG_DATA;
  1409. break;
  1410. default:
  1411. // Should not happen.
  1412. break;
  1413. }
  1414. queue_buf_store(&iput_ctx);
  1415. queue_start();
  1416. return FDS_SUCCESS;
  1417. }
  1418. ret_code_t fds_record_open(fds_record_desc_t * const p_desc,
  1419. fds_flash_record_t * const p_flash_rec)
  1420. {
  1421. uint16_t page;
  1422. if ((p_desc == NULL) || (p_flash_rec == NULL))
  1423. {
  1424. return FDS_ERR_NULL_ARG;
  1425. }
  1426. // Find the record if necessary.
  1427. if (record_find_by_desc(p_desc, &page))
  1428. {
  1429. fds_header_t const * const p_header = (fds_header_t*)p_desc->p_record;
  1430. #if (FDS_CRC_CHECK_ON_READ)
  1431. if (!crc_verify_success(p_header->crc16,
  1432. p_header->length_words,
  1433. p_desc->p_record))
  1434. {
  1435. return FDS_ERR_CRC_CHECK_FAILED;
  1436. }
  1437. #endif
  1438. (void) nrf_atomic_u32_add(&m_pages[page].records_open, 1);
  1439. // Initialize p_flash_rec.
  1440. p_flash_rec->p_header = p_header;
  1441. p_flash_rec->p_data = (p_desc->p_record + FDS_HEADER_SIZE);
  1442. // Set the record as open in the descriptor.
  1443. p_desc->record_is_open = true;
  1444. return FDS_SUCCESS;
  1445. }
  1446. // The record could not be found.
  1447. // It either never existed or it has been deleted.
  1448. return FDS_ERR_NOT_FOUND;
  1449. }
  1450. ret_code_t fds_record_close(fds_record_desc_t * const p_desc)
  1451. {
  1452. ret_code_t ret;
  1453. uint16_t page;
  1454. if (p_desc == NULL)
  1455. {
  1456. return FDS_ERR_NULL_ARG;
  1457. }
  1458. if (record_find_by_desc((fds_record_desc_t*)p_desc, &page))
  1459. {
  1460. CRITICAL_SECTION_ENTER();
  1461. if ((m_pages[page].records_open > 0) && (p_desc->record_is_open))
  1462. {
  1463. m_pages[page].records_open--;
  1464. p_desc->record_is_open = false;
  1465. ret = FDS_SUCCESS;
  1466. }
  1467. else
  1468. {
  1469. ret = FDS_ERR_NO_OPEN_RECORDS;
  1470. }
  1471. CRITICAL_SECTION_EXIT();
  1472. }
  1473. else
  1474. {
  1475. ret = FDS_ERR_NOT_FOUND;
  1476. }
  1477. return ret;
  1478. }
  1479. ret_code_t fds_reserve(fds_reserve_token_t * const p_tok, uint16_t length_words)
  1480. {
  1481. ret_code_t ret;
  1482. uint16_t page;
  1483. if (!m_flags.initialized)
  1484. {
  1485. return FDS_ERR_NOT_INITIALIZED;
  1486. }
  1487. if (p_tok == NULL)
  1488. {
  1489. return FDS_ERR_NULL_ARG;
  1490. }
  1491. ret = write_space_reserve(length_words, &page);
  1492. if (ret == FDS_SUCCESS)
  1493. {
  1494. p_tok->page = page;
  1495. p_tok->length_words = length_words;
  1496. }
  1497. return ret;
  1498. }
  1499. ret_code_t fds_reserve_cancel(fds_reserve_token_t * const p_tok)
  1500. {
  1501. ret_code_t ret;
  1502. if (!m_flags.initialized)
  1503. {
  1504. return FDS_ERR_NOT_INITIALIZED;
  1505. }
  1506. if (p_tok == NULL)
  1507. {
  1508. return FDS_ERR_NULL_ARG;
  1509. }
  1510. if (p_tok->page > FDS_DATA_PAGES)
  1511. {
  1512. // The page does not exist. This shouldn't happen.
  1513. return FDS_ERR_INVALID_ARG;
  1514. }
  1515. fds_page_t const * const p_page = &m_pages[p_tok->page];
  1516. CRITICAL_SECTION_ENTER();
  1517. if ((FDS_HEADER_SIZE + p_tok->length_words) <= p_page->words_reserved)
  1518. {
  1519. // Free reserved space.
  1520. write_space_free(p_tok->length_words, p_tok->page);
  1521. // Clean the token.
  1522. p_tok->page = 0;
  1523. p_tok->length_words = 0;
  1524. ret = FDS_SUCCESS;
  1525. }
  1526. else
  1527. {
  1528. // We are trying to cancel a reservation of more words than how many are
  1529. // currently reserved on the page. Clearly, this shouldn't happen.
  1530. ret = FDS_ERR_INVALID_ARG;
  1531. }
  1532. CRITICAL_SECTION_EXIT();
  1533. return ret;
  1534. }
  1535. ret_code_t fds_record_write(fds_record_desc_t * const p_desc,
  1536. fds_record_t const * const p_record)
  1537. {
  1538. return write_enqueue(p_desc, p_record, NULL, FDS_OP_WRITE);
  1539. }
  1540. ret_code_t fds_record_write_reserved(fds_record_desc_t * const p_desc,
  1541. fds_record_t const * const p_record,
  1542. fds_reserve_token_t const * const p_tok)
  1543. {
  1544. // A NULL token is not allowed when writing to a reserved space.
  1545. if (p_tok == NULL)
  1546. {
  1547. return FDS_ERR_NULL_ARG;
  1548. }
  1549. return write_enqueue(p_desc, p_record, p_tok, FDS_OP_WRITE);
  1550. }
  1551. ret_code_t fds_record_update(fds_record_desc_t * const p_desc,
  1552. fds_record_t const * const p_record)
  1553. {
  1554. // A NULL descriptor is not allowed when updating a record.
  1555. if (p_desc == NULL)
  1556. {
  1557. return FDS_ERR_NULL_ARG;
  1558. }
  1559. return write_enqueue(p_desc, p_record, NULL, FDS_OP_UPDATE);
  1560. }
  1561. ret_code_t fds_record_delete(fds_record_desc_t * const p_desc)
  1562. {
  1563. fds_op_t * p_op;
  1564. nrf_atfifo_item_put_t iput_ctx;
  1565. if (!m_flags.initialized)
  1566. {
  1567. return FDS_ERR_NOT_INITIALIZED;
  1568. }
  1569. if (p_desc == NULL)
  1570. {
  1571. return FDS_ERR_NULL_ARG;
  1572. }
  1573. p_op = queue_buf_get(&iput_ctx);
  1574. if (p_op == NULL)
  1575. {
  1576. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1577. }
  1578. p_op->op_code = FDS_OP_DEL_RECORD;
  1579. p_op->del.step = FDS_OP_DEL_RECORD_FLAG_DIRTY;
  1580. p_op->del.record_to_delete = p_desc->record_id;
  1581. queue_buf_store(&iput_ctx);
  1582. queue_start();
  1583. return FDS_SUCCESS;
  1584. }
  1585. ret_code_t fds_file_delete(uint16_t file_id)
  1586. {
  1587. fds_op_t * p_op;
  1588. nrf_atfifo_item_put_t iput_ctx;
  1589. if (!m_flags.initialized)
  1590. {
  1591. return FDS_ERR_NOT_INITIALIZED;
  1592. }
  1593. if (file_id == FDS_FILE_ID_INVALID)
  1594. {
  1595. return FDS_ERR_INVALID_ARG;
  1596. }
  1597. p_op = queue_buf_get(&iput_ctx);
  1598. if (p_op == NULL)
  1599. {
  1600. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1601. }
  1602. p_op->op_code = FDS_OP_DEL_FILE;
  1603. p_op->del.step = FDS_OP_DEL_FILE_FLAG_DIRTY;
  1604. p_op->del.file_id = file_id;
  1605. queue_buf_store(&iput_ctx);
  1606. queue_start();
  1607. return FDS_SUCCESS;
  1608. }
  1609. ret_code_t fds_gc(void)
  1610. {
  1611. fds_op_t * p_op;
  1612. nrf_atfifo_item_put_t iput_ctx;
  1613. if (!m_flags.initialized)
  1614. {
  1615. return FDS_ERR_NOT_INITIALIZED;
  1616. }
  1617. p_op = queue_buf_get(&iput_ctx);
  1618. if (p_op == NULL)
  1619. {
  1620. return FDS_ERR_NO_SPACE_IN_QUEUES;
  1621. }
  1622. p_op->op_code = FDS_OP_GC;
  1623. queue_buf_store(&iput_ctx);
  1624. if (m_gc.state != GC_BEGIN)
  1625. {
  1626. // Resume GC by retrying the last step.
  1627. m_gc.resume = true;
  1628. }
  1629. queue_start();
  1630. return FDS_SUCCESS;
  1631. }
  1632. ret_code_t fds_record_iterate(fds_record_desc_t * const p_desc,
  1633. fds_find_token_t * const p_token)
  1634. {
  1635. return record_find(NULL, NULL, p_desc, p_token);
  1636. }
  1637. ret_code_t fds_record_find(uint16_t file_id,
  1638. uint16_t record_key,
  1639. fds_record_desc_t * const p_desc,
  1640. fds_find_token_t * const p_token)
  1641. {
  1642. return record_find(&file_id, &record_key, p_desc, p_token);
  1643. }
  1644. ret_code_t fds_record_find_by_key(uint16_t record_key,
  1645. fds_record_desc_t * const p_desc,
  1646. fds_find_token_t * const p_token)
  1647. {
  1648. return record_find(NULL, &record_key, p_desc, p_token);
  1649. }
  1650. ret_code_t fds_record_find_in_file(uint16_t file_id,
  1651. fds_record_desc_t * const p_desc,
  1652. fds_find_token_t * const p_token)
  1653. {
  1654. return record_find(&file_id, NULL, p_desc, p_token);
  1655. }
  1656. ret_code_t fds_descriptor_from_rec_id(fds_record_desc_t * const p_desc,
  1657. uint32_t record_id)
  1658. {
  1659. if (p_desc == NULL)
  1660. {
  1661. return FDS_ERR_NULL_ARG;
  1662. }
  1663. // Zero the descriptor and set the record_id field.
  1664. memset(p_desc, 0x00, sizeof(fds_record_desc_t));
  1665. p_desc->record_id = record_id;
  1666. return FDS_SUCCESS;
  1667. }
  1668. ret_code_t fds_record_id_from_desc(fds_record_desc_t const * const p_desc,
  1669. uint32_t * const p_record_id)
  1670. {
  1671. if ((p_desc == NULL) || (p_record_id == NULL))
  1672. {
  1673. return FDS_ERR_NULL_ARG;
  1674. }
  1675. *p_record_id = p_desc->record_id;
  1676. return FDS_SUCCESS;
  1677. }
  1678. ret_code_t fds_stat(fds_stat_t * const p_stat)
  1679. {
  1680. uint16_t const words_in_page = FDS_PAGE_SIZE;
  1681. // The largest number of free contiguous words on any page.
  1682. uint16_t contig_words = 0;
  1683. if (!m_flags.initialized)
  1684. {
  1685. return FDS_ERR_NOT_INITIALIZED;
  1686. }
  1687. if (p_stat == NULL)
  1688. {
  1689. return FDS_ERR_NULL_ARG;
  1690. }
  1691. memset(p_stat, 0x00, sizeof(fds_stat_t));
  1692. p_stat->pages_available = FDS_VIRTUAL_PAGES;
  1693. for (uint16_t page = 0; page < FDS_DATA_PAGES; page++)
  1694. {
  1695. uint16_t const words_used = m_pages[page].write_offset + m_pages[page].words_reserved;
  1696. if (page_identify(m_pages[page].p_addr) == FDS_PAGE_UNDEFINED)
  1697. {
  1698. p_stat->pages_available--;
  1699. }
  1700. p_stat->open_records += m_pages[page].records_open;
  1701. p_stat->words_reserved += m_pages[page].words_reserved;
  1702. p_stat->words_used += words_used;
  1703. contig_words = (words_in_page - words_used);
  1704. if (contig_words > p_stat->largest_contig)
  1705. {
  1706. p_stat->largest_contig = contig_words;
  1707. }
  1708. records_stat(page,
  1709. &p_stat->valid_records,
  1710. &p_stat->dirty_records,
  1711. &p_stat->freeable_words,
  1712. &p_stat->corruption);
  1713. }
  1714. return FDS_SUCCESS;
  1715. }
  1716. #endif //NRF_MODULE_ENABLED(FDS)