OSAL_Nv.c 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499
  1. /******************************************************************************
  2. Filename: OSAL_Nv.c
  3. Revised: $Date: 2011-06-05 18:52:21 -0700 (Sun, 05 Jun 2011) $
  4. Revision: $Revision: 26212 $
  5. Description: This module contains the OSAL non-volatile memory functions.
  6. Copyright 2006-2011 Texas Instruments Incorporated. All rights reserved.
  7. IMPORTANT: Your use of this Software is limited to those specific rights
  8. granted under the terms of a software license agreement between the user
  9. who downloaded the software, his/her employer (which must be your employer)
  10. and Texas Instruments Incorporated (the "License"). You may not use this
  11. Software unless you agree to abide by the terms of the License. The License
  12. limits your use, and you acknowledge, that the Software may not be modified,
  13. copied or distributed unless embedded on a Texas Instruments microcontroller
  14. or used solely and exclusively in conjunction with a Texas Instruments radio
  15. frequency transceiver, which is integrated into your product. Other than for
  16. the foregoing purpose, you may not use, reproduce, copy, prepare derivative
  17. works of, modify, distribute, perform, display or sell this Software and/or
  18. its documentation for any purpose.
  19. YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  20. PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
  21. INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
  22. NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  23. TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
  24. NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  25. LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  26. INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  27. OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  28. OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  29. (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
  30. Should you have any questions regarding your right to use this Software,
  31. contact Texas Instruments Incorporated at www.TI.com.
  32. ******************************************************************************/
  33. /******************************************************************************
  34. Notes:
  35. - A trick buried deep in initPage() requires that the MSB of the NV Item Id
  36. is to be reserved for use by this module.
  37. ******************************************************************************/
  38. /*********************************************************************
  39. * INCLUDES
  40. */
  41. #include "hal_adc.h"
  42. #include "hal_flash.h"
  43. #include "hal_types.h"
  44. #include "OSAL_Nv.h"
  45. #include "ZComDef.h"
  46. /*********************************************************************
  47. * CONSTANTS
  48. */
  49. #define OSAL_NV_PAGE_SIZE HAL_FLASH_PAGE_SIZE
  50. #define OSAL_NV_PAGES_USED HAL_NV_PAGE_CNT
  51. #define OSAL_NV_PAGE_BEG HAL_NV_PAGE_BEG
  52. #define OSAL_NV_PAGE_END (OSAL_NV_PAGE_BEG + OSAL_NV_PAGES_USED - 1)
  53. #define OSAL_NV_ACTIVE 0x00
  54. #define OSAL_NV_ERASED 0xFF
  55. #define OSAL_NV_ERASED_ID 0xFFFF
  56. #define OSAL_NV_ZEROED_ID 0x0000
  57. // Reserve MSB of Id to signal a search for the "old" source copy (new write interrupted/failed.)
  58. #define OSAL_NV_SOURCE_ID 0x8000
  59. // In case pages 0-1 are ever used, define a null page value.
  60. #define OSAL_NV_PAGE_NULL 0
  61. // In case item Id 0 is ever used, define a null item value.
  62. #define OSAL_NV_ITEM_NULL 0
  63. #define OSAL_NV_WORD_SIZE HAL_FLASH_WORD_SIZE
  64. #define OSAL_NV_PAGE_HDR_OFFSET 0
  65. #define OSAL_NV_MAX_HOT 3
  66. static const uint16 hotIds[OSAL_NV_MAX_HOT] = {
  67. ZCD_NV_NWKKEY,
  68. ZCD_NV_NWK_ACTIVE_KEY_INFO,
  69. ZCD_NV_NWK_ALTERN_KEY_INFO,
  70. };
  71. /*********************************************************************
  72. * MACROS
  73. */
  74. #define OSAL_NV_CHECK_BUS_VOLTAGE HalAdcCheckVdd(VDD_MIN_NV)
  75. #define OSAL_NV_DATA_SIZE( LEN ) \
  76. (((LEN) >= ((uint16)(65536UL - OSAL_NV_WORD_SIZE))) ? \
  77. ((uint16)(65536UL - OSAL_NV_WORD_SIZE)) : \
  78. ((((LEN) + OSAL_NV_WORD_SIZE - 1) / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE))
  79. #define OSAL_NV_ITEM_SIZE( LEN ) \
  80. (((LEN) >= ((uint16)(65536UL - OSAL_NV_WORD_SIZE - OSAL_NV_HDR_SIZE))) ? \
  81. ((uint16)(65536UL - OSAL_NV_WORD_SIZE)) : \
  82. (((((LEN) + OSAL_NV_WORD_SIZE - 1) / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE) + OSAL_NV_HDR_SIZE))
  83. #define COMPACT_PAGE_CLEANUP( COM_PG ) st ( \
  84. /* In order to recover from a page compaction that is interrupted,\
  85. * the logic in osal_nv_init() depends upon the following order:\
  86. * 1. State of the target of compaction is changed to ePgInUse.\
  87. * 2. Compacted page is erased.\
  88. */\
  89. setPageUse( pgRes, TRUE ); /* Mark the reserve page as being in use. */\
  90. erasePage( (COM_PG) ); \
  91. \
  92. pgRes = (COM_PG); /* Set the reserve page to be the newly erased page. */\
  93. )
  94. /*********************************************************************
  95. * TYPEDEFS
  96. */
  97. typedef struct
  98. {
  99. uint16 id;
  100. uint16 len; // Enforce Flash-WORD size on len.
  101. uint16 chk; // Byte-wise checksum of the 'len' data bytes of the item.
  102. uint16 stat; // Item status.
  103. } osalNvHdr_t;
  104. // Struct member offsets.
  105. #define OSAL_NV_HDR_ID 0
  106. #define OSAL_NV_HDR_LEN 2
  107. #define OSAL_NV_HDR_CHK 4
  108. #define OSAL_NV_HDR_STAT 6
  109. #define OSAL_NV_HDR_ITEM 2 // Length of any item of a header struct.
  110. #define OSAL_NV_HDR_SIZE 8
  111. #define OSAL_NV_HDR_HALF (OSAL_NV_HDR_SIZE / 2)
  112. typedef struct
  113. {
  114. uint16 active;
  115. uint16 inUse;
  116. uint16 xfer;
  117. uint16 spare;
  118. } osalNvPgHdr_t;
  119. // Struct member offsets.
  120. #define OSAL_NV_PG_ACTIVE 0
  121. #define OSAL_NV_PG_INUSE 2
  122. #define OSAL_NV_PG_XFER 4
  123. #define OSAL_NV_PG_SPARE 6
  124. #define OSAL_NV_PAGE_HDR_SIZE 8
  125. #define OSAL_NV_PAGE_HDR_HALF (OSAL_NV_PAGE_HDR_SIZE / 2)
  126. typedef enum
  127. {
  128. eNvXfer,
  129. eNvZero
  130. } eNvHdrEnum;
  131. typedef enum
  132. {
  133. ePgActive,
  134. ePgInUse,
  135. ePgXfer,
  136. ePgSpare
  137. } ePgHdrEnum;
  138. /*********************************************************************
  139. * GLOBAL VARIABLES
  140. */
  141. #ifndef OAD_KEEP_NV_PAGES
  142. // When NV pages are to remain intact during OAD download,
  143. // the image itself should not include NV pages.
  144. #pragma location="ZIGNV_ADDRESS_SPACE"
  145. __no_init uint8 _nvBuf[OSAL_NV_PAGES_USED * OSAL_NV_PAGE_SIZE];
  146. #pragma required=_nvBuf
  147. #endif // OAD_KEEP_NV_PAGES
  148. /*********************************************************************
  149. * LOCAL VARIABLES
  150. */
  151. // Offset into the page of the first available erased space.
  152. static uint16 pgOff[OSAL_NV_PAGES_USED];
  153. // Count of the bytes lost for the zeroed-out items.
  154. static uint16 pgLost[OSAL_NV_PAGES_USED];
  155. static uint8 pgRes; // Page reserved for item compacting transfer.
  156. // Saving ~100 code bytes to move a uint8* parameter/return value from findItem() to a global.
  157. static uint8 findPg;
  158. // NV page and offsets for hot items.
  159. static uint8 hotPg[OSAL_NV_MAX_HOT];
  160. static uint16 hotOff[OSAL_NV_MAX_HOT];
  161. /*********************************************************************
  162. * LOCAL FUNCTIONS
  163. */
  164. static uint8 initNV( void );
  165. static void setPageUse( uint8 pg, uint8 inUse );
  166. static uint16 initPage( uint8 pg, uint16 id, uint8 findDups );
  167. static void erasePage( uint8 pg );
  168. static uint8 compactPage( uint8 srcPg, uint16 skipId );
  169. static uint16 findItem( uint16 id );
  170. static uint8 initItem( uint8 flag, uint16 id, uint16 len, void *buf );
  171. static void setItem( uint8 pg, uint16 offset, eNvHdrEnum stat );
  172. static uint16 setChk( uint8 pg, uint16 offset, uint16 chk );
  173. static uint16 calcChkB( uint16 len, uint8 *buf );
  174. static uint16 calcChkF( uint8 pg, uint16 offset, uint16 len );
  175. static void writeWord( uint8 pg, uint16 offset, uint8 *buf );
  176. static void writeWordH( uint8 pg, uint16 offset, uint8 *buf );
  177. static void writeWordM( uint8 pg, uint16 offset, uint8 *buf, uint16 cnt );
  178. static void writeBuf( uint8 pg, uint16 offset, uint16 len, uint8 *buf );
  179. static void xferBuf( uint8 srcPg, uint16 srcOff, uint8 dstPg, uint16 dstOff, uint16 len );
  180. static uint8 writeItem( uint8 pg, uint16 id, uint16 len, void *buf, uint8 flag );
  181. static uint8 hotItem(uint16 id);
  182. static void hotItemUpdate(uint8 pg, uint16 off, uint16 id);
  183. /*********************************************************************
  184. * @fn initNV
  185. *
  186. * @brief Initialize the NV flash pages.
  187. *
  188. * @param none
  189. *
  190. * @return TRUE
  191. */
  192. static uint8 initNV( void )
  193. {
  194. osalNvPgHdr_t pgHdr;
  195. uint8 oldPg = OSAL_NV_PAGE_NULL;
  196. uint8 findDups = FALSE;
  197. uint8 pg;
  198. pgRes = OSAL_NV_PAGE_NULL;
  199. for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
  200. {
  201. HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_HDR_SIZE);
  202. if ( pgHdr.active == OSAL_NV_ERASED_ID )
  203. {
  204. if ( pgRes == OSAL_NV_PAGE_NULL )
  205. {
  206. pgRes = pg;
  207. }
  208. else
  209. {
  210. setPageUse( pg, TRUE );
  211. }
  212. }
  213. // An Xfer from this page was in progress.
  214. else if ( pgHdr.xfer != OSAL_NV_ERASED_ID )
  215. {
  216. oldPg = pg;
  217. }
  218. }
  219. // If a page compaction was interrupted before the old page was erased.
  220. if ( oldPg != OSAL_NV_PAGE_NULL )
  221. {
  222. /* Interrupted compaction before the target of compaction was put in use;
  223. * so erase the target of compaction and start again.
  224. */
  225. if ( pgRes != OSAL_NV_PAGE_NULL )
  226. {
  227. erasePage( pgRes );
  228. (void)compactPage( oldPg, OSAL_NV_ITEM_NULL );
  229. }
  230. /* Interrupted compaction after the target of compaction was put in use,
  231. * but before the old page was erased; so erase it now and create a new reserve page.
  232. */
  233. else
  234. {
  235. erasePage( oldPg );
  236. pgRes = oldPg;
  237. }
  238. }
  239. else if ( pgRes != OSAL_NV_PAGE_NULL )
  240. {
  241. erasePage( pgRes ); // The last page erase could have been interrupted by a power-cycle.
  242. }
  243. /* else if there is no reserve page, COMPACT_PAGE_CLEANUP() must have succeeded to put the old
  244. * reserve page (i.e. the target of the compacted items) into use but got interrupted by a reset
  245. * while trying to erase the page to be compacted. Such a page should only contain duplicate items
  246. * (i.e. all items will be marked 'Xfer') and thus should have the lost count equal to the page
  247. * size less the page header.
  248. */
  249. for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
  250. {
  251. // Calculate page offset and lost bytes - any "old" item triggers an N^2 re-scan from start.
  252. if ( initPage( pg, OSAL_NV_ITEM_NULL, findDups ) != OSAL_NV_ITEM_NULL )
  253. {
  254. findDups = TRUE;
  255. pg = (OSAL_NV_PAGE_BEG - 1); // Pre-decrement so that loop increment will start over at zero.
  256. continue;
  257. }
  258. }
  259. if (findDups)
  260. {
  261. // Final pass to calculate page lost after invalidating duplicate items.
  262. for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
  263. {
  264. (void)initPage( pg, OSAL_NV_ITEM_NULL, FALSE );
  265. }
  266. }
  267. if ( pgRes == OSAL_NV_PAGE_NULL )
  268. {
  269. uint8 idx, mostLost = 0;
  270. for ( idx = 0; idx < OSAL_NV_PAGES_USED; idx++ )
  271. {
  272. // Is this the page that was compacted?
  273. if (pgLost[idx] == (OSAL_NV_PAGE_SIZE - OSAL_NV_PAGE_HDR_SIZE))
  274. {
  275. mostLost = idx;
  276. break;
  277. }
  278. /* This check is not expected to be necessary because the above test should always succeed
  279. * with an early loop exit.
  280. */
  281. else if (pgLost[idx] > pgLost[mostLost])
  282. {
  283. mostLost = idx;
  284. }
  285. }
  286. pgRes = mostLost + OSAL_NV_PAGE_BEG;
  287. erasePage( pgRes ); // The last page erase had been interrupted by a power-cycle.
  288. }
  289. return TRUE;
  290. }
  291. /*********************************************************************
  292. * @fn setPageUse
  293. *
  294. * @brief Set page header active/inUse state according to 'inUse'.
  295. *
  296. * @param pg - Valid NV page to verify and init.
  297. * @param inUse - Boolean TRUE if inUse, FALSE if only active.
  298. *
  299. * @return none
  300. */
  301. static void setPageUse( uint8 pg, uint8 inUse )
  302. {
  303. osalNvPgHdr_t pgHdr;
  304. pgHdr.active = OSAL_NV_ZEROED_ID;
  305. if ( inUse )
  306. {
  307. pgHdr.inUse = OSAL_NV_ZEROED_ID;
  308. }
  309. else
  310. {
  311. pgHdr.inUse = OSAL_NV_ERASED_ID;
  312. }
  313. writeWord( pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8*)(&pgHdr) );
  314. }
  315. /*********************************************************************
  316. * @fn initPage
  317. *
  318. * @brief Walk the page items; calculate checksums, lost bytes & page offset.
  319. *
  320. * @param pg - Valid NV page to verify and init.
  321. * @param id - Valid NV item Id to use function as a "findItem".
  322. * If set to NULL then just perform the page initialization.
  323. * @param findDups - TRUE on recursive call from initNV() to find and zero-out duplicate items
  324. * left from a write that is interrupted by a reset/power-cycle.
  325. * FALSE otherwise.
  326. *
  327. * @return If 'id' is non-NULL and good checksums are found, return the offset
  328. * of the data corresponding to item Id; else OSAL_NV_ITEM_NULL.
  329. */
  330. static uint16 initPage( uint8 pg, uint16 id, uint8 findDups )
  331. {
  332. uint16 offset = OSAL_NV_PAGE_HDR_SIZE;
  333. uint16 sz, lost = 0;
  334. osalNvHdr_t hdr;
  335. do
  336. {
  337. HalFlashRead(pg, offset, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
  338. if ( hdr.id == OSAL_NV_ERASED_ID )
  339. {
  340. break;
  341. }
  342. // Get the actual size in bytes which is the ceiling(hdr.len)
  343. sz = OSAL_NV_DATA_SIZE( hdr.len );
  344. // A bad 'len' write has blown away the rest of the page.
  345. if (sz > (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE - offset))
  346. {
  347. lost += (OSAL_NV_PAGE_SIZE - offset);
  348. offset = OSAL_NV_PAGE_SIZE;
  349. break;
  350. }
  351. offset += OSAL_NV_HDR_SIZE;
  352. if ( hdr.id != OSAL_NV_ZEROED_ID )
  353. {
  354. /* This trick allows function to do double duty for findItem() without
  355. * compromising its essential functionality at powerup initialization.
  356. */
  357. if ( id != OSAL_NV_ITEM_NULL )
  358. {
  359. /* This trick allows asking to find the old/transferred item in case
  360. * of a successful new item write that gets interrupted before the
  361. * old item can be zeroed out.
  362. */
  363. if ( (id & 0x7fff) == hdr.id )
  364. {
  365. if ( (((id & OSAL_NV_SOURCE_ID) == 0) && (hdr.stat == OSAL_NV_ERASED_ID)) ||
  366. (((id & OSAL_NV_SOURCE_ID) != 0) && (hdr.stat != OSAL_NV_ERASED_ID)) )
  367. {
  368. return offset;
  369. }
  370. }
  371. }
  372. // When invoked from the osal_nv_init(), verify checksums and find & zero any duplicates.
  373. else
  374. {
  375. if ( hdr.chk == calcChkF( pg, offset, hdr.len ) )
  376. {
  377. if ( findDups )
  378. {
  379. if ( hdr.stat == OSAL_NV_ERASED_ID )
  380. {
  381. /* The trick of setting the MSB of the item Id causes the logic
  382. * immediately above to return a valid page only if the header 'stat'
  383. * indicates that it was the older item being transferred.
  384. */
  385. uint16 off = findItem( (hdr.id | OSAL_NV_SOURCE_ID) );
  386. if ( off != OSAL_NV_ITEM_NULL )
  387. {
  388. setItem( findPg, off, eNvZero ); // Mark old duplicate as invalid.
  389. }
  390. }
  391. }
  392. // Any "old" item immediately exits and triggers the N^2 exhaustive initialization.
  393. else if ( hdr.stat != OSAL_NV_ERASED_ID )
  394. {
  395. return OSAL_NV_ERASED_ID;
  396. }
  397. }
  398. else
  399. {
  400. setItem( pg, offset, eNvZero ); // Mark bad checksum as invalid.
  401. lost += (OSAL_NV_HDR_SIZE + sz);
  402. }
  403. }
  404. }
  405. else
  406. {
  407. lost += (OSAL_NV_HDR_SIZE + sz);
  408. }
  409. offset += sz;
  410. } while (offset < (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE));
  411. pgOff[pg - OSAL_NV_PAGE_BEG] = offset;
  412. pgLost[pg - OSAL_NV_PAGE_BEG] = lost;
  413. return OSAL_NV_ITEM_NULL;
  414. }
  415. /*********************************************************************
  416. * @fn erasePage
  417. *
  418. * @brief Erases a page in Flash.
  419. *
  420. * @param pg - Valid NV page to erase.
  421. *
  422. * @return none
  423. */
  424. static void erasePage( uint8 pg )
  425. {
  426. HalFlashErase(pg);
  427. pgOff[pg - OSAL_NV_PAGE_BEG] = OSAL_NV_PAGE_HDR_SIZE;
  428. pgLost[pg - OSAL_NV_PAGE_BEG] = 0;
  429. }
  430. /*********************************************************************
  431. * @fn compactPage
  432. *
  433. * @brief Compacts the page specified.
  434. *
  435. * @param srcPg - Valid NV page to erase.
  436. * @param skipId - Item Id to not compact.
  437. *
  438. * @return TRUE if valid items from 'srcPg' are successully compacted onto the 'pgRes';
  439. * FALSE otherwise.
  440. * Note that on a failure, this could loop, re-erasing the 'pgRes' and re-compacting with
  441. * the risk of infinitely looping on HAL flash failure.
  442. * Worst case scenario: HAL flash starts failing in general, perhaps low Vdd?
  443. * All page compactions will fail which will cause all osal_nv_write() calls to return
  444. * NV_OPER_FAILED.
  445. * Eventually, all pages in use may also be in the state of "pending compaction" where
  446. * the page header member OSAL_NV_PG_XFER is zeroed out.
  447. * During this "HAL flash brown-out", the code will run and OTA should work (until low Vdd
  448. * causes an actual chip brown-out, of course.) Although no new NV items will be created
  449. * or written, the last value written with a return value of SUCCESS can continue to be
  450. * read successfully.
  451. * If eventually HAL flash starts working again, all of the pages marked as
  452. * "pending compaction" may or may not be eventually compacted. But, initNV() will
  453. * deterministically clean-up one page pending compaction per power-cycle
  454. * (if HAL flash is working.) Nevertheless, one erased reserve page will be maintained
  455. * through such a scenario.
  456. */
  457. static uint8 compactPage( uint8 srcPg, uint16 skipId )
  458. {
  459. uint16 srcOff;
  460. uint8 rtrn;
  461. // To minimize code size, only check for a clean page here where it's absolutely required.
  462. for (srcOff = 0; srcOff < OSAL_NV_PAGE_SIZE; srcOff++)
  463. {
  464. HalFlashRead(pgRes, srcOff, &rtrn, 1);
  465. if (rtrn != OSAL_NV_ERASED)
  466. {
  467. erasePage(pgRes);
  468. return FALSE;
  469. }
  470. }
  471. srcOff = OSAL_NV_PAGE_HDR_SIZE;
  472. rtrn = TRUE;
  473. while ( srcOff < (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE ) )
  474. {
  475. osalNvHdr_t hdr;
  476. uint16 sz, dstOff = pgOff[pgRes-OSAL_NV_PAGE_BEG];
  477. HalFlashRead(srcPg, srcOff, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
  478. if ( hdr.id == OSAL_NV_ERASED_ID )
  479. {
  480. break;
  481. }
  482. // Get the actual size in bytes which is the ceiling(hdr.len)
  483. sz = OSAL_NV_DATA_SIZE( hdr.len );
  484. if ( sz > (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE - srcOff) )
  485. {
  486. break;
  487. }
  488. if ( sz > (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE - dstOff) )
  489. {
  490. rtrn = FALSE;
  491. break;
  492. }
  493. srcOff += OSAL_NV_HDR_SIZE;
  494. if ( (hdr.id != OSAL_NV_ZEROED_ID) && (hdr.id != skipId) )
  495. {
  496. if ( hdr.chk == calcChkF( srcPg, srcOff, hdr.len ) )
  497. {
  498. /* Prevent excessive re-writes to item header caused by numerous, rapid, & successive
  499. * OSAL_Nv interruptions caused by resets.
  500. */
  501. if ( hdr.stat == OSAL_NV_ERASED_ID )
  502. {
  503. setItem( srcPg, srcOff, eNvXfer );
  504. }
  505. if ( writeItem( pgRes, hdr.id, hdr.len, NULL, FALSE ) )
  506. {
  507. dstOff += OSAL_NV_HDR_SIZE;
  508. xferBuf( srcPg, srcOff, pgRes, dstOff, sz );
  509. // Calculate and write the new checksum.
  510. if (hdr.chk == calcChkF(pgRes, dstOff, hdr.len))
  511. {
  512. if ( hdr.chk != setChk( pgRes, dstOff, hdr.chk ) )
  513. {
  514. rtrn = FALSE;
  515. break;
  516. }
  517. else
  518. {
  519. hotItemUpdate(pgRes, dstOff, hdr.id);
  520. }
  521. }
  522. else
  523. {
  524. rtrn = FALSE;
  525. break;
  526. }
  527. }
  528. else
  529. {
  530. rtrn = FALSE;
  531. break;
  532. }
  533. }
  534. }
  535. srcOff += sz;
  536. }
  537. if (rtrn == FALSE)
  538. {
  539. erasePage(pgRes);
  540. }
  541. else if (skipId == OSAL_NV_ITEM_NULL)
  542. {
  543. COMPACT_PAGE_CLEANUP(srcPg);
  544. }
  545. // else invoking function must cleanup.
  546. return rtrn;
  547. }
  548. /*********************************************************************
  549. * @fn findItem
  550. *
  551. * @brief Find an item Id in NV and return the page and offset to its data.
  552. *
  553. * @param id - Valid NV item Id.
  554. *
  555. * @return Offset of data corresponding to item Id, if found;
  556. * otherwise OSAL_NV_ITEM_NULL.
  557. *
  558. * The page containing the item, if found;
  559. * otherwise no valid assignment made - left equal to item Id.
  560. *
  561. */
  562. static uint16 findItem( uint16 id )
  563. {
  564. uint16 off;
  565. uint8 pg;
  566. for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
  567. {
  568. if ( (off = initPage( pg, id, FALSE )) != OSAL_NV_ITEM_NULL )
  569. {
  570. findPg = pg;
  571. return off;
  572. }
  573. }
  574. // Now attempt to find the item as the "old" item of a failed/interrupted NV write.
  575. if ( (id & OSAL_NV_SOURCE_ID) == 0 )
  576. {
  577. return findItem( id | OSAL_NV_SOURCE_ID );
  578. }
  579. else
  580. {
  581. findPg = OSAL_NV_PAGE_NULL;
  582. return OSAL_NV_ITEM_NULL;
  583. }
  584. }
  585. /*********************************************************************
  586. * @fn initItem
  587. *
  588. * @brief An NV item is created and initialized with the data passed to the function, if any.
  589. *
  590. * @param flag - TRUE if the 'buf' parameter contains data for the call to writeItem().
  591. * (i.e. if invoked from osal_nv_item_init() ).
  592. * FALSE if writeItem() should just write the header and the 'buf' parameter
  593. * is ok to use as a return value of the page number to be cleaned with
  594. * COMPACT_PAGE_CLEANUP().
  595. * (i.e. if invoked from osal_nv_write() ).
  596. * @param id - Valid NV item Id.
  597. * @param len - Item data length.
  598. * @param *buf - Pointer to item initalization data. Set to NULL if none.
  599. *
  600. * @return The OSAL Nv page number if item write and read back checksums ok;
  601. * OSAL_NV_PAGE_NULL otherwise.
  602. */
  603. static uint8 initItem( uint8 flag, uint16 id, uint16 len, void *buf )
  604. {
  605. uint16 sz = OSAL_NV_ITEM_SIZE( len );
  606. uint8 rtrn = OSAL_NV_PAGE_NULL;
  607. uint8 cnt = OSAL_NV_PAGES_USED;
  608. uint8 pg = pgRes+1; // Set to 1 after the reserve page to even wear across all available pages.
  609. do {
  610. if (pg >= OSAL_NV_PAGE_BEG+OSAL_NV_PAGES_USED)
  611. {
  612. pg = OSAL_NV_PAGE_BEG;
  613. }
  614. if ( pg != pgRes )
  615. {
  616. uint8 idx = pg - OSAL_NV_PAGE_BEG;
  617. if ( sz <= (OSAL_NV_PAGE_SIZE - pgOff[idx] + pgLost[idx]) )
  618. {
  619. break;
  620. }
  621. }
  622. pg++;
  623. } while (--cnt);
  624. if (cnt)
  625. {
  626. // Item fits if an old page is compacted.
  627. if ( sz > (OSAL_NV_PAGE_SIZE - pgOff[pg - OSAL_NV_PAGE_BEG]) )
  628. {
  629. osalNvPgHdr_t pgHdr;
  630. /* Prevent excessive re-writes to page header caused by numerous, rapid, & successive
  631. * OSAL_Nv interruptions caused by resets.
  632. */
  633. HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_PAGE_HDR_SIZE);
  634. if ( pgHdr.xfer == OSAL_NV_ERASED_ID )
  635. {
  636. // Mark the old page as being in process of compaction.
  637. sz = OSAL_NV_ZEROED_ID;
  638. writeWordH( pg, OSAL_NV_PG_XFER, (uint8*)(&sz) );
  639. }
  640. /* First the old page is compacted, then the new item will be the last one written to what
  641. * had been the reserved page.
  642. */
  643. if (compactPage( pg, id ))
  644. {
  645. if ( writeItem( pgRes, id, len, buf, flag ) )
  646. {
  647. rtrn = pgRes;
  648. }
  649. if ( flag == FALSE )
  650. {
  651. /* Overload 'buf' as an OUT parameter to pass back to the calling function
  652. * the old page to be cleaned up.
  653. */
  654. *(uint8 *)buf = pg;
  655. }
  656. else
  657. {
  658. /* Safe to do the compacted page cleanup even if writeItem() above failed because the
  659. * item does not yet exist since this call with flag==TRUE is from osal_nv_item_init().
  660. */
  661. COMPACT_PAGE_CLEANUP( pg );
  662. }
  663. }
  664. }
  665. else
  666. {
  667. if ( writeItem( pg, id, len, buf, flag ) )
  668. {
  669. rtrn = pg;
  670. }
  671. }
  672. }
  673. return rtrn;
  674. }
  675. /*********************************************************************
  676. * @fn setItem
  677. *
  678. * @brief Set an item Id or status to mark its state.
  679. *
  680. * @param pg - Valid NV page.
  681. * @param offset - Valid offset into the page of the item data - the header
  682. * offset is calculated from this.
  683. * @param stat - Valid enum value for the item status.
  684. *
  685. * @return none
  686. */
  687. static void setItem( uint8 pg, uint16 offset, eNvHdrEnum stat )
  688. {
  689. osalNvHdr_t hdr;
  690. offset -= OSAL_NV_HDR_SIZE;
  691. HalFlashRead(pg, offset, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
  692. if ( stat == eNvXfer )
  693. {
  694. hdr.stat = OSAL_NV_ACTIVE;
  695. writeWord( pg, offset+OSAL_NV_HDR_CHK, (uint8*)(&(hdr.chk)) );
  696. }
  697. else // if ( stat == eNvZero )
  698. {
  699. uint16 sz = ((hdr.len + (OSAL_NV_WORD_SIZE-1)) / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE +
  700. OSAL_NV_HDR_SIZE;
  701. hdr.id = 0;
  702. writeWord( pg, offset, (uint8 *)(&hdr) );
  703. pgLost[pg-OSAL_NV_PAGE_BEG] += sz;
  704. }
  705. }
  706. /*********************************************************************
  707. * @fn setChk
  708. *
  709. * @brief Set the item header checksum given the data buffer offset.
  710. *
  711. * @param pg - Valid NV page.
  712. * @param offset - Valid offset into the page of the item data - the header
  713. * offset is calculated from this.
  714. * @param chk - The checksum to set.
  715. *
  716. * @return The checksum read back.
  717. */
  718. static uint16 setChk( uint8 pg, uint16 offset, uint16 chk )
  719. {
  720. offset -= OSAL_NV_WORD_SIZE;
  721. writeWordH( pg, offset, (uint8 *)&chk );
  722. HalFlashRead( pg, offset, (uint8 *)(&chk), sizeof( chk ) );
  723. return chk;
  724. }
  725. /*********************************************************************
  726. * @fn calcChkB
  727. *
  728. * @brief Calculates the data checksum over the 'buf' parameter.
  729. *
  730. * @param len - Byte count of the data to be checksummed.
  731. * @param buf - Data buffer to be checksummed.
  732. *
  733. * @return Calculated checksum of the data bytes.
  734. */
  735. static uint16 calcChkB( uint16 len, uint8 *buf )
  736. {
  737. uint8 fill = len % OSAL_NV_WORD_SIZE;
  738. uint16 chk;
  739. if ( !buf )
  740. {
  741. chk = len * OSAL_NV_ERASED;
  742. }
  743. else
  744. {
  745. chk = 0;
  746. while ( len-- )
  747. {
  748. chk += *buf++;
  749. }
  750. }
  751. // calcChkF() will calculate over OSAL_NV_WORD_SIZE alignment.
  752. if ( fill )
  753. {
  754. chk += (OSAL_NV_WORD_SIZE - fill) * OSAL_NV_ERASED;
  755. }
  756. return chk;
  757. }
  758. /*********************************************************************
  759. * @fn calcChkF
  760. *
  761. * @brief Calculates the data checksum by reading the data bytes from NV.
  762. *
  763. * @param pg - A valid NV Flash page.
  764. * @param offset - A valid offset into the page.
  765. * @param len - Byte count of the data to be checksummed.
  766. *
  767. * @return Calculated checksum of the data bytes.
  768. */
  769. static uint16 calcChkF( uint8 pg, uint16 offset, uint16 len )
  770. {
  771. uint16 chk = 0;
  772. len = (len + (OSAL_NV_WORD_SIZE-1)) / OSAL_NV_WORD_SIZE;
  773. while ( len-- )
  774. {
  775. uint8 cnt, tmp[OSAL_NV_WORD_SIZE];
  776. HalFlashRead(pg, offset, tmp, OSAL_NV_WORD_SIZE);
  777. offset += OSAL_NV_WORD_SIZE;
  778. for ( cnt = 0; cnt < OSAL_NV_WORD_SIZE; cnt++ )
  779. {
  780. chk += tmp[cnt];
  781. }
  782. }
  783. return chk;
  784. }
  785. /*********************************************************************
  786. * @fn writeWord
  787. *
  788. * @brief Writes a Flash-WORD to NV.
  789. *
  790. * @param pg - A valid NV Flash page.
  791. * @param offset - A valid offset into the page.
  792. * @param buf - Pointer to source buffer.
  793. *
  794. * @return none
  795. */
  796. static void writeWord( uint8 pg, uint16 offset, uint8 *buf )
  797. {
  798. offset = (offset / HAL_FLASH_WORD_SIZE) +
  799. ((uint16)pg * (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE));
  800. HalFlashWrite(offset, buf, 1);
  801. }
  802. /*********************************************************************
  803. * @fn writeWordM
  804. *
  805. * @brief Writes multiple Flash-WORDs to NV.
  806. *
  807. * @param pg - A valid NV Flash page.
  808. * @param offset - A valid offset into the page.
  809. * @param buf - Pointer to source buffer.
  810. * @param cnt - Number of 4-byte blocks to write.
  811. *
  812. * @return none
  813. */
  814. static void writeWordM( uint8 pg, uint16 offset, uint8 *buf, uint16 cnt )
  815. {
  816. offset = (offset / HAL_FLASH_WORD_SIZE) +
  817. ((uint16)pg * (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE));
  818. HalFlashWrite(offset, buf, cnt);
  819. }
  820. /*********************************************************************
  821. * @fn writeWordH
  822. *
  823. * @brief Writes the 1st half of a Flash-WORD to NV (filling 2nd half with 0xffff).
  824. *
  825. * @param pg - A valid NV Flash page.
  826. * @param offset - A valid offset into the page.
  827. * @param buf - Pointer to source buffer.
  828. *
  829. * @return none
  830. */
  831. static void writeWordH( uint8 pg, uint16 offset, uint8 *buf )
  832. {
  833. uint8 tmp[4];
  834. tmp[0] = buf[0];
  835. tmp[1] = buf[1];
  836. tmp[2] = OSAL_NV_ERASED;
  837. tmp[3] = OSAL_NV_ERASED;
  838. writeWord( pg, offset, tmp );
  839. }
  840. /*********************************************************************
  841. * @fn writeBuf
  842. *
  843. * @brief Writes a data buffer to NV.
  844. *
  845. * @param dstPg - A valid NV Flash page.
  846. * @param offset - A valid offset into the page.
  847. * @param len - Byte count of the data to write.
  848. * @param buf - The data to write.
  849. *
  850. * @return TRUE if data buf checksum matches read back checksum, else FALSE.
  851. */
  852. static void writeBuf( uint8 dstPg, uint16 dstOff, uint16 len, uint8 *buf )
  853. {
  854. uint8 rem = dstOff % OSAL_NV_WORD_SIZE;
  855. uint8 tmp[OSAL_NV_WORD_SIZE];
  856. if ( rem )
  857. {
  858. dstOff = (dstOff / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE;
  859. HalFlashRead(dstPg, dstOff, tmp, OSAL_NV_WORD_SIZE);
  860. while ( (rem < OSAL_NV_WORD_SIZE) && len )
  861. {
  862. tmp[rem++] = *buf++;
  863. len--;
  864. }
  865. writeWord( dstPg, dstOff, tmp );
  866. dstOff += OSAL_NV_WORD_SIZE;
  867. }
  868. rem = len % OSAL_NV_WORD_SIZE;
  869. len /= OSAL_NV_WORD_SIZE;
  870. if ( len )
  871. {
  872. writeWordM( dstPg, dstOff, buf, len );
  873. dstOff += OSAL_NV_WORD_SIZE * len;
  874. buf += OSAL_NV_WORD_SIZE * len;
  875. }
  876. if ( rem )
  877. {
  878. uint8 idx = 0;
  879. HalFlashRead(dstPg, dstOff, tmp, OSAL_NV_WORD_SIZE);
  880. while ( rem-- )
  881. {
  882. tmp[idx++] = *buf++;
  883. }
  884. writeWord( dstPg, dstOff, tmp );
  885. }
  886. }
  887. /*********************************************************************
  888. * @fn xferBuf
  889. *
  890. * @brief Xfers an NV buffer from one location to another, enforcing OSAL_NV_WORD_SIZE writes.
  891. *
  892. * @return none
  893. */
  894. static void xferBuf( uint8 srcPg, uint16 srcOff, uint8 dstPg, uint16 dstOff, uint16 len )
  895. {
  896. uint8 rem = dstOff % OSAL_NV_WORD_SIZE;
  897. uint8 tmp[OSAL_NV_WORD_SIZE];
  898. if ( rem )
  899. {
  900. dstOff -= rem;
  901. HalFlashRead(dstPg, dstOff, tmp, OSAL_NV_WORD_SIZE);
  902. while ( (rem < OSAL_NV_WORD_SIZE) && len )
  903. {
  904. HalFlashRead(srcPg, srcOff, tmp+rem, 1);
  905. srcOff++;
  906. rem++;
  907. len--;
  908. }
  909. writeWord( dstPg, dstOff, tmp );
  910. dstOff += OSAL_NV_WORD_SIZE;
  911. }
  912. rem = len % OSAL_NV_WORD_SIZE;
  913. len /= OSAL_NV_WORD_SIZE;
  914. while ( len-- )
  915. {
  916. HalFlashRead(srcPg, srcOff, tmp, OSAL_NV_WORD_SIZE);
  917. srcOff += OSAL_NV_WORD_SIZE;
  918. writeWord( dstPg, dstOff, tmp );
  919. dstOff += OSAL_NV_WORD_SIZE;
  920. }
  921. if ( rem )
  922. {
  923. uint8 idx = 0;
  924. HalFlashRead(dstPg, dstOff, tmp, OSAL_NV_WORD_SIZE);
  925. while ( rem-- )
  926. {
  927. HalFlashRead(srcPg, srcOff, tmp+idx, 1);
  928. srcOff++;
  929. idx++;
  930. }
  931. writeWord( dstPg, dstOff, tmp );
  932. }
  933. }
  934. /*********************************************************************
  935. * @fn writeItem
  936. *
  937. * @brief Writes an item header/data combo to the specified NV page.
  938. *
  939. * @param pg - Valid NV Flash page.
  940. * @param id - Valid NV item Id.
  941. * @param len - Byte count of the data to write.
  942. * @param buf - The data to write. If NULL, no data/checksum write.
  943. * @param flag - TRUE if the checksum should be written, FALSE otherwise.
  944. *
  945. * @return TRUE if header/data to write matches header/data read back, else FALSE.
  946. */
  947. static uint8 writeItem( uint8 pg, uint16 id, uint16 len, void *buf, uint8 flag )
  948. {
  949. uint16 offset = pgOff[pg-OSAL_NV_PAGE_BEG];
  950. uint8 rtrn = FALSE;
  951. osalNvHdr_t hdr;
  952. hdr.id = id;
  953. hdr.len = len;
  954. writeWord( pg, offset, (uint8 *)&hdr );
  955. HalFlashRead(pg, offset, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
  956. if ( (hdr.id == id) && (hdr.len == len) )
  957. {
  958. if ( flag )
  959. {
  960. hdr.chk = calcChkB( len, buf );
  961. offset += OSAL_NV_HDR_SIZE;
  962. if ( buf != NULL )
  963. {
  964. writeBuf( pg, offset, len, buf );
  965. }
  966. if ( hdr.chk == calcChkF( pg, offset, len ) )
  967. {
  968. if ( hdr.chk == setChk( pg, offset, hdr.chk ) )
  969. {
  970. hotItemUpdate(pg, offset, hdr.id);
  971. rtrn = TRUE;
  972. }
  973. }
  974. }
  975. else
  976. {
  977. rtrn = TRUE;
  978. }
  979. len = OSAL_NV_ITEM_SIZE( hdr.len );
  980. }
  981. else
  982. {
  983. len = OSAL_NV_ITEM_SIZE( hdr.len );
  984. if (len > (OSAL_NV_PAGE_SIZE - pgOff[pg - OSAL_NV_PAGE_BEG]))
  985. {
  986. len = (OSAL_NV_PAGE_SIZE - pgOff[pg - OSAL_NV_PAGE_BEG]);
  987. }
  988. pgLost[pg - OSAL_NV_PAGE_BEG] += len;
  989. }
  990. pgOff[pg - OSAL_NV_PAGE_BEG] += len;
  991. return rtrn;
  992. }
  993. /*********************************************************************
  994. * @fn hotItem
  995. *
  996. * @brief Look for the parameter 'id' in the hot items array.
  997. *
  998. * @param id - A valid NV item Id.
  999. *
  1000. * @return A valid index into the hot items if the item is hot; OSAL_NV_MAX_HOT if not.
  1001. */
  1002. static uint8 hotItem(uint16 id)
  1003. {
  1004. uint8 hotIdx;
  1005. for (hotIdx = 0; hotIdx < OSAL_NV_MAX_HOT; hotIdx++)
  1006. {
  1007. if (hotIds[hotIdx] == id)
  1008. {
  1009. break;
  1010. }
  1011. }
  1012. return hotIdx;
  1013. }
  1014. /*********************************************************************
  1015. * @fn hotItemUpdate
  1016. *
  1017. * @brief If the parameter 'id' is a hot item, update the corresponding hot item data.
  1018. *
  1019. * @param pg - The new NV page corresponding to the hot item.
  1020. * @param off - The new NV page offset corresponding to the hot item.
  1021. * @param id - A valid NV item Id.
  1022. *
  1023. * @return none
  1024. */
  1025. static void hotItemUpdate(uint8 pg, uint16 off, uint16 id)
  1026. {
  1027. uint8 hotIdx = hotItem(id);
  1028. if (hotIdx < OSAL_NV_MAX_HOT)
  1029. {
  1030. {
  1031. hotPg[hotIdx] = pg;
  1032. hotOff[hotIdx] = off;
  1033. }
  1034. }
  1035. }
  1036. /*********************************************************************
  1037. * @fn osal_nv_init
  1038. *
  1039. * @brief Initialize NV service.
  1040. *
  1041. * @param p - Not used.
  1042. *
  1043. * @return none
  1044. */
  1045. void osal_nv_init( void *p )
  1046. {
  1047. (void)p; // Suppress Lint warning.
  1048. (void)initNV(); // Always returns TRUE after pages have been erased.
  1049. }
  1050. /*********************************************************************
  1051. * @fn osal_nv_item_init
  1052. *
  1053. * @brief If the NV item does not already exist, it is created and
  1054. * initialized with the data passed to the function, if any.
  1055. * This function must be called before calling osal_nv_read() or
  1056. * osal_nv_write().
  1057. *
  1058. * @param id - Valid NV item Id.
  1059. * @param len - Item length.
  1060. * @param *buf - Pointer to item initalization data. Set to NULL if none.
  1061. *
  1062. * @return NV_ITEM_UNINIT - Id did not exist and was created successfully.
  1063. * SUCCESS - Id already existed, no action taken.
  1064. * NV_OPER_FAILED - Failure to find or create Id.
  1065. */
  1066. uint8 osal_nv_item_init( uint16 id, uint16 len, void *buf )
  1067. {
  1068. uint16 offset;
  1069. if ( !OSAL_NV_CHECK_BUS_VOLTAGE )
  1070. {
  1071. return NV_OPER_FAILED;
  1072. }
  1073. else if ((offset = findItem(id)) != OSAL_NV_ITEM_NULL)
  1074. {
  1075. // Re-populate the NV hot item data if the corresponding items are already established.
  1076. hotItemUpdate(findPg, offset, id);
  1077. return SUCCESS;
  1078. }
  1079. else if ( initItem( TRUE, id, len, buf ) != OSAL_NV_PAGE_NULL )
  1080. {
  1081. return NV_ITEM_UNINIT;
  1082. }
  1083. else
  1084. {
  1085. return NV_OPER_FAILED;
  1086. }
  1087. }
  1088. /*********************************************************************
  1089. * @fn osal_nv_item_len
  1090. *
  1091. * @brief Get the data length of the item stored in NV memory.
  1092. *
  1093. * @param id - Valid NV item Id.
  1094. *
  1095. * @return Item length, if found; zero otherwise.
  1096. */
  1097. uint16 osal_nv_item_len( uint16 id )
  1098. {
  1099. osalNvHdr_t hdr;
  1100. uint16 offset;
  1101. uint8 hotIdx;
  1102. if ((hotIdx = hotItem(id)) < OSAL_NV_MAX_HOT)
  1103. {
  1104. findPg = hotPg[hotIdx];
  1105. offset = hotOff[hotIdx];
  1106. }
  1107. else if ((offset = findItem(id)) == OSAL_NV_ITEM_NULL)
  1108. {
  1109. return 0;
  1110. }
  1111. HalFlashRead(findPg, (offset - OSAL_NV_HDR_SIZE), (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
  1112. return hdr.len;
  1113. }
  1114. /*********************************************************************
  1115. * @fn osal_nv_write
  1116. *
  1117. * @brief Write a data item to NV. Function can write an entire item to NV or
  1118. * an element of an item by indexing into the item with an offset.
  1119. *
  1120. * @param id - Valid NV item Id.
  1121. * @param ndx - Index offset into item
  1122. * @param len - Length of data to write.
  1123. * @param *buf - Data to write.
  1124. *
  1125. * @return SUCCESS if successful, NV_ITEM_UNINIT if item did not
  1126. * exist in NV and offset is non-zero, NV_OPER_FAILED if failure.
  1127. */
  1128. uint8 osal_nv_write( uint16 id, uint16 ndx, uint16 len, void *buf )
  1129. {
  1130. uint8 rtrn = SUCCESS;
  1131. if ( !OSAL_NV_CHECK_BUS_VOLTAGE )
  1132. {
  1133. return NV_OPER_FAILED;
  1134. }
  1135. else if ( len != 0 )
  1136. {
  1137. osalNvHdr_t hdr;
  1138. uint16 origOff, srcOff;
  1139. uint16 cnt, chk;
  1140. uint8 *ptr, srcPg;
  1141. origOff = srcOff = findItem( id );
  1142. srcPg = findPg;
  1143. if ( srcOff == OSAL_NV_ITEM_NULL )
  1144. {
  1145. return NV_ITEM_UNINIT;
  1146. }
  1147. HalFlashRead(srcPg, (srcOff - OSAL_NV_HDR_SIZE), (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
  1148. if ( hdr.len < (ndx + len) )
  1149. {
  1150. return NV_OPER_FAILED;
  1151. }
  1152. srcOff += ndx;
  1153. ptr = buf;
  1154. cnt = len;
  1155. chk = 0;
  1156. while ( cnt-- )
  1157. {
  1158. uint8 tmp;
  1159. HalFlashRead(srcPg, srcOff, &tmp, 1);
  1160. if ( tmp != *ptr )
  1161. {
  1162. chk = 1; // Mark that at least one byte is different.
  1163. // Calculate expected checksum after transferring old data and writing new data.
  1164. hdr.chk -= tmp;
  1165. hdr.chk += *ptr;
  1166. }
  1167. srcOff++;
  1168. ptr++;
  1169. }
  1170. if ( chk != 0 ) // If the buffer to write is different in one or more bytes.
  1171. {
  1172. uint8 comPg = OSAL_NV_PAGE_NULL;
  1173. uint8 dstPg = initItem( FALSE, id, hdr.len, &comPg );
  1174. if ( dstPg != OSAL_NV_PAGE_NULL )
  1175. {
  1176. uint16 tmp = OSAL_NV_DATA_SIZE( hdr.len );
  1177. uint16 dstOff = pgOff[dstPg-OSAL_NV_PAGE_BEG] - tmp;
  1178. srcOff = origOff;
  1179. /* Prevent excessive re-writes to item header caused by numerous, rapid, & successive
  1180. * OSAL_Nv interruptions caused by resets.
  1181. */
  1182. if ( hdr.stat == OSAL_NV_ERASED_ID )
  1183. {
  1184. setItem( srcPg, srcOff, eNvXfer );
  1185. }
  1186. xferBuf( srcPg, srcOff, dstPg, dstOff, ndx );
  1187. srcOff += ndx;
  1188. dstOff += ndx;
  1189. writeBuf( dstPg, dstOff, len, buf );
  1190. srcOff += len;
  1191. dstOff += len;
  1192. xferBuf( srcPg, srcOff, dstPg, dstOff, (hdr.len-ndx-len) );
  1193. // Calculate and write the new checksum.
  1194. dstOff = pgOff[dstPg-OSAL_NV_PAGE_BEG] - tmp;
  1195. if ( hdr.chk == calcChkF( dstPg, dstOff, hdr.len ) )
  1196. {
  1197. if ( hdr.chk != setChk( dstPg, dstOff, hdr.chk ) )
  1198. {
  1199. rtrn = NV_OPER_FAILED;
  1200. }
  1201. else
  1202. {
  1203. hotItemUpdate(dstPg, dstOff, hdr.id);
  1204. }
  1205. }
  1206. else
  1207. {
  1208. rtrn = NV_OPER_FAILED;
  1209. }
  1210. }
  1211. else
  1212. {
  1213. rtrn = NV_OPER_FAILED;
  1214. }
  1215. if ( comPg != OSAL_NV_PAGE_NULL )
  1216. {
  1217. /* Even though the page compaction succeeded, if the new item is coming from the compacted
  1218. * page and writing the new value failed, then the compaction must be aborted.
  1219. */
  1220. if ( (srcPg == comPg) && (rtrn == NV_OPER_FAILED) )
  1221. {
  1222. erasePage( pgRes );
  1223. }
  1224. else
  1225. {
  1226. COMPACT_PAGE_CLEANUP( comPg );
  1227. }
  1228. }
  1229. /* Zero of the old item must wait until after compact page cleanup has finished - if the item
  1230. * is zeroed before and cleanup is interrupted by a power-cycle, the new item can be lost.
  1231. */
  1232. if ( (srcPg != comPg) && (rtrn != NV_OPER_FAILED) )
  1233. {
  1234. setItem( srcPg, origOff, eNvZero );
  1235. }
  1236. }
  1237. }
  1238. return rtrn;
  1239. }
  1240. /*********************************************************************
  1241. * @fn osal_nv_read
  1242. *
  1243. * @brief Read data from NV. This function can be used to read an entire item from NV or
  1244. * an element of an item by indexing into the item with an offset.
  1245. * Read data is copied into *buf.
  1246. *
  1247. * @param id - Valid NV item Id.
  1248. * @param ndx - Index offset into item
  1249. * @param len - Length of data to read.
  1250. * @param *buf - Data is read into this buffer.
  1251. *
  1252. * @return SUCCESS if NV data was copied to the parameter 'buf'.
  1253. * Otherwise, NV_OPER_FAILED for failure.
  1254. */
  1255. uint8 osal_nv_read( uint16 id, uint16 ndx, uint16 len, void *buf )
  1256. {
  1257. uint16 offset;
  1258. uint8 hotIdx;
  1259. if ((hotIdx = hotItem(id)) < OSAL_NV_MAX_HOT)
  1260. {
  1261. HalFlashRead(hotPg[hotIdx], hotOff[hotIdx]+ndx, buf, len);
  1262. return SUCCESS;
  1263. }
  1264. if ((offset = findItem(id)) == OSAL_NV_ITEM_NULL)
  1265. {
  1266. return NV_OPER_FAILED;
  1267. }
  1268. else
  1269. {
  1270. HalFlashRead(findPg, offset+ndx, buf, len);
  1271. return SUCCESS;
  1272. }
  1273. }
  1274. /*********************************************************************
  1275. * @fn osal_nv_delete
  1276. *
  1277. * @brief Delete item from NV. This function will fail if the length
  1278. * parameter does not match the length of the item in NV.
  1279. *
  1280. * @param id - Valid NV item Id.
  1281. * @param len - Length of item to delete.
  1282. *
  1283. * @return SUCCESS if item was deleted,
  1284. * NV_ITEM_UNINIT if item did not exist in NV,
  1285. * NV_BAD_ITEM_LEN if length parameter not correct,
  1286. * NV_OPER_FAILED if attempted deletion failed.
  1287. */
  1288. uint8 osal_nv_delete( uint16 id, uint16 len )
  1289. {
  1290. uint16 length;
  1291. uint16 offset;
  1292. offset = findItem( id );
  1293. if ( offset == OSAL_NV_ITEM_NULL )
  1294. {
  1295. // NV item does not exist
  1296. return NV_ITEM_UNINIT;
  1297. }
  1298. length = osal_nv_item_len( id );
  1299. if ( length != len )
  1300. {
  1301. // NV item has different length
  1302. return NV_BAD_ITEM_LEN;
  1303. }
  1304. // Set item header ID to zero to 'delete' the item
  1305. setItem( findPg, offset, eNvZero );
  1306. // Verify that item has been removed
  1307. offset = findItem( id );
  1308. if ( offset != OSAL_NV_ITEM_NULL )
  1309. {
  1310. // Still there
  1311. return NV_OPER_FAILED;
  1312. }
  1313. else
  1314. {
  1315. // Yes, it's gone
  1316. return SUCCESS;
  1317. }
  1318. }
  1319. /*********************************************************************
  1320. */