/********************************************************************* * @fn initItem * * @brief An NV item is created and initialized with the data passed to the function, if any. * * @param id - Valid NV item Id. * @param len - Item data length. * @param *buf - Pointer to item initalization data. Set to NULL if none. * * @return TRUE if item write and read back checksums ok; FALSE otherwise. */ static uint8 initItem( uint8 flag, uint16 id, uint16 len, void *buf ) { uint16 sz = OSAL_NV_ITEM_SIZE( len ); uint8 rtrn = FALSE; uint8 cnt = OSAL_NV_PAGES_USED; uint8 pg = pgRes+1; // Set to 1 after the reserve page to even wear across all available pages. uint8 idx; do { if (pg >= OSAL_NV_PAGE_BEG+OSAL_NV_PAGES_USED) { pg = OSAL_NV_PAGE_BEG; } if ( pg != pgRes ) { idx = pg - OSAL_NV_PAGE_BEG; if ( (pgOff[idx] - pgLost[idx] + sz) <= OSAL_NV_PAGE_FREE ) { break; } } pg++; } while (--cnt); if (cnt) { // Item fits if an old page is compacted. if ( (pgOff[idx] + sz) > OSAL_NV_PAGE_FREE ) { pg = pgRes; } // New item is the first one written to the reserved page, then the old page is compacted. rtrn = writeItem( pg, id, len, buf, flag ); if ( pg == pgRes ) { if ( flag ) { compactPage( OSAL_NV_PAGE_BEG+idx ); } else { *(uint8 *)buf = OSAL_NV_PAGE_BEG+idx; } } } return rtrn; }
/********************************************************************* * @fn initNV * * @brief Initialize the NV flash pages. * * @param none * * @return TRUE if initialization succeeds. FALSE, otherwise. */ static uint8 initNV( void ) { uint32 pgHdr; uint8 xferPg = OSAL_NV_PAGE_NULL; uint8 pg; failF = FALSE; activePg = OSAL_NV_PAGE_NULL; // Pick active page and clean up erased page if necessary for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ ) { HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_PAGE_HDR_SIZE); if ( pgHdr == OSAL_NV_ACTIVE_PAGE_STATE) { if (activePg != OSAL_NV_PAGE_NULL) { // Both pages are active only when power failed during flash erase and // with very low probability. // As it is hard (code size intensive) to figure out which page is the real active page, // and theoretically impossible as well in lowest probability, erase both pages // in this case cleanErasedPage(activePg); cleanErasedPage(pg); activePg = OSAL_NV_PAGE_NULL; } else { activePg = pg; } } else if ( pgHdr == OSAL_NV_XFER_PAGE_STATE) { xferPg = pg; } else { // Erase this page if it is not erased. // This is to ensure that any page that were in the middle of // compacting gets erased. cleanErasedPage(pg); } } if (activePg == OSAL_NV_PAGE_NULL) { if (xferPg == OSAL_NV_PAGE_NULL) { // Both pages are erased. This must be initial state. // Pick one page as active page. setActivePage(OSAL_NV_PAGE_BEG); pgOff = OSAL_NV_PAGE_HDR_SIZE; // If setting active page from a completely erased page failed, // it is not recommended to operate any further. // Other cases, even if non-active page is corrupt, NV module can still read // the active page content and hence this function could return TRUE. return (!failF); } else { // Compacting a page hasn't completed in previous power cycle. // Complete the compacting. activePg = xferPg; findOffset(); compactPage(xferPg); } } else { if (xferPg != OSAL_NV_PAGE_NULL) { // Compacting has completed except for the final step of erasing // the xferPage. erasePage(xferPg); } // find the active page offset to write a new variable location item findOffset(); } return TRUE; }
/********************************************************************* * @fn initItem * * @brief An NV item is created and initialized with the data passed to the function, if any. * * @param flag - TRUE if the 'buf' parameter contains data for the call to writeItem(). * (i.e. if invoked from osal_nv_item_init() ). * FALSE if writeItem() should just write the header and the 'buf' parameter * is ok to use as a return value of the page number to be cleaned with * COMPACT_PAGE_CLEANUP(). * (i.e. if invoked from osal_nv_write() ). * @param id - Valid NV item Id. * @param len - Item data length. * @param *buf - Pointer to item initalization data. Set to NULL if none. * * @return The OSAL Nv page number if item write and read back checksums ok; * OSAL_NV_PAGE_NULL otherwise. */ static uint8 initItem( uint8 flag, uint16 id, uint16 len, void *buf ) { uint16 sz = OSAL_NV_ITEM_SIZE( len ); uint8 rtrn = OSAL_NV_PAGE_NULL; uint8 cnt = OSAL_NV_PAGES_USED; uint8 pg = pgRes+1; // Set to 1 after the reserve page to even wear across all available pages. do { if (pg >= OSAL_NV_PAGE_BEG+OSAL_NV_PAGES_USED) { pg = OSAL_NV_PAGE_BEG; } if ( pg != pgRes ) { uint8 idx = pg - OSAL_NV_PAGE_BEG; if ( sz <= (OSAL_NV_PAGE_SIZE - pgOff[idx] + pgLost[idx]) ) { break; } } pg++; } while (--cnt); if (cnt) { // Item fits if an old page is compacted. if ( sz > (OSAL_NV_PAGE_SIZE - pgOff[pg - OSAL_NV_PAGE_BEG]) ) { osalNvPgHdr_t pgHdr; /* Prevent excessive re-writes to page header caused by numerous, rapid, & successive * OSAL_Nv interruptions caused by resets. */ HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_PAGE_HDR_SIZE); if ( pgHdr.xfer == OSAL_NV_ERASED_ID ) { // Mark the old page as being in process of compaction. sz = OSAL_NV_ZEROED_ID; writeWordH( pg, OSAL_NV_PG_XFER, (uint8*)(&sz) ); } /* First the old page is compacted, then the new item will be the last one written to what * had been the reserved page. */ if (compactPage( pg, id )) { if ( writeItem( pgRes, id, len, buf, flag ) ) { rtrn = pgRes; } if ( flag == FALSE ) { /* Overload 'buf' as an OUT parameter to pass back to the calling function * the old page to be cleaned up. */ *(uint8 *)buf = pg; } else { /* Safe to do the compacted page cleanup even if writeItem() above failed because the * item does not yet exist since this call with flag==TRUE is from osal_nv_item_init(). */ COMPACT_PAGE_CLEANUP( pg ); } } } else { if ( writeItem( pg, id, len, buf, flag ) ) { rtrn = pg; } } } return rtrn; }
/********************************************************************* * @fn initNV * * @brief Initialize the NV flash pages. * * @param none * * @return TRUE */ static uint8 initNV( void ) { osalNvPgHdr_t pgHdr; uint8 oldPg = OSAL_NV_PAGE_NULL; uint8 newPg = OSAL_NV_PAGE_NULL; uint8 findDups = FALSE; uint8 pg; pgRes = OSAL_NV_PAGE_NULL; for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ ) { HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_HDR_SIZE); if ( pgHdr.active == OSAL_NV_ERASED_ID ) { if ( pgRes == OSAL_NV_PAGE_NULL ) { pgRes = pg; } else { setPageUse( pg, TRUE ); } } else // Page is active. { // If the page is not yet in use, it is the tgt of items from an xfer. if ( pgHdr.inUse == OSAL_NV_ERASED_ID ) { newPg = pg; } // An Xfer from this page was in progress. else if ( pgHdr.xfer != OSAL_NV_ERASED_ID ) { oldPg = pg; } } // Calculate page offset and lost bytes - any "old" item triggers an N^2 re-scan from start. if ( initPage( pg, OSAL_NV_ITEM_NULL, findDups ) != OSAL_NV_ITEM_NULL ) { findDups = TRUE; pg = OSAL_NV_PAGE_BEG-1; continue; } } // for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ ) /* First the old page is erased, and then the new page is put into use. * So if a transfer was in progress, the new page will always not yet be * marked as in use, since that is the last step to ending a transfer. */ if ( newPg != OSAL_NV_PAGE_NULL ) { /* If there is already a fallow page reserved, keep it and put the newPg in use. * An unfinished compaction will finish to the new reserve page and the old page * will be erased and reserved. */ if ( pgRes != OSAL_NV_PAGE_NULL ) { setPageUse( newPg, TRUE ); } /* If setting old page to 'xfer' failed or board reset before it was effected, there is no way * to know which page was the 'old page' - so just reset all NV pages to start clean. */ else if ( oldPg != OSAL_NV_PAGE_NULL ) { pgRes = newPg; } /* If a page compaction was interrupted and the page being compacted is not * yet erased, then there may be items remaining to xfer before erasing. */ if ( oldPg != OSAL_NV_PAGE_NULL ) { compactPage( oldPg ); } } /* If no page met the criteria to be the reserve page: * - A compactPage() failed or board reset before doing so. * - Perhaps the user changed which Flash pages are dedicated to NV and downloaded the code * without erasing Flash? */ if ( pgRes == OSAL_NV_PAGE_NULL ) { for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ ) { erasePage( pg ); } initNV(); } return TRUE; }
/********************************************************************* * @fn initNV * * @brief Initialize the NV flash pages. * * @param none * * @return TRUE */ static uint8 initNV( void ) { osalNvPgHdr_t pgHdr; uint8 oldPg = OSAL_NV_PAGE_NULL; uint8 findDups = FALSE; uint8 pg; pgRes = OSAL_NV_PAGE_NULL; for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ ) { HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_HDR_SIZE); if ( pgHdr.active == OSAL_NV_ERASED_ID ) { if ( pgRes == OSAL_NV_PAGE_NULL ) { pgRes = pg; } else { setPageUse( pg, TRUE ); } } // An Xfer from this page was in progress. else if ( pgHdr.xfer != OSAL_NV_ERASED_ID ) { oldPg = pg; } } // If a page compaction was interrupted before the old page was erased. if ( oldPg != OSAL_NV_PAGE_NULL ) { /* Interrupted compaction before the target of compaction was put in use; * so erase the target of compaction and start again. */ if ( pgRes != OSAL_NV_PAGE_NULL ) { erasePage( pgRes ); (void)compactPage( oldPg, OSAL_NV_ITEM_NULL ); } /* Interrupted compaction after the target of compaction was put in use, * but before the old page was erased; so erase it now and create a new reserve page. */ else { erasePage( oldPg ); pgRes = oldPg; } } else if ( pgRes != OSAL_NV_PAGE_NULL ) { erasePage( pgRes ); // The last page erase could have been interrupted by a power-cycle. } /* else if there is no reserve page, COMPACT_PAGE_CLEANUP() must have succeeded to put the old * reserve page (i.e. the target of the compacted items) into use but got interrupted by a reset * while trying to erase the page to be compacted. Such a page should only contain duplicate items * (i.e. all items will be marked 'Xfer') and thus should have the lost count equal to the page * size less the page header. */ for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ ) { // Calculate page offset and lost bytes - any "old" item triggers an N^2 re-scan from start. if ( initPage( pg, OSAL_NV_ITEM_NULL, findDups ) != OSAL_NV_ITEM_NULL ) { findDups = TRUE; pg = (OSAL_NV_PAGE_BEG - 1); // Pre-decrement so that loop increment will start over at zero. continue; } } if (findDups) { // Final pass to calculate page lost after invalidating duplicate items. for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ ) { (void)initPage( pg, OSAL_NV_ITEM_NULL, FALSE ); } } if ( pgRes == OSAL_NV_PAGE_NULL ) { uint8 idx, mostLost = 0; for ( idx = 0; idx < OSAL_NV_PAGES_USED; idx++ ) { // Is this the page that was compacted? if (pgLost[idx] == (OSAL_NV_PAGE_SIZE - OSAL_NV_PAGE_HDR_SIZE)) { mostLost = idx; break; } /* This check is not expected to be necessary because the above test should always succeed * with an early loop exit. */ else if (pgLost[idx] > pgLost[mostLost]) { mostLost = idx; } } pgRes = mostLost + OSAL_NV_PAGE_BEG; erasePage( pgRes ); // The last page erase had been interrupted by a power-cycle. } return TRUE; }