/********************************************************************* * @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 compactPage * * @brief Compacts the page specified. * * @param srcPg - Valid NV page to erase. * * @return none */ static void compactPage( uint8 srcPg ) { uint16 dstOff = pgOff[pgRes-OSAL_NV_PAGE_BEG]; uint16 srcOff = OSAL_NV_ZEROED_ID; osalNvHdr_t hdr; // Mark page as being in process of compaction. writeWordH( srcPg, OSAL_NV_PG_XFER, (uint8*)(&srcOff) ); srcOff = OSAL_NV_PAGE_HDR_SIZE; do { uint16 sz; HalFlashRead(srcPg, srcOff, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE); if ( hdr.id == OSAL_NV_ERASED_ID ) { break; } srcOff += OSAL_NV_HDR_SIZE; if ( (srcOff + hdr.len) > OSAL_NV_PAGE_FREE ) { break; } sz = OSAL_NV_DATA_SIZE( hdr.len ); if ( hdr.id != OSAL_NV_ZEROED_ID ) { if ( hdr.chk == calcChkF( srcPg, srcOff, hdr.len ) ) { setItem( srcPg, srcOff, eNvXfer ); writeBuf( pgRes, dstOff, OSAL_NV_HDR_SIZE, (byte *)(&hdr) ); dstOff += OSAL_NV_HDR_SIZE; xferBuf( srcPg, srcOff, pgRes, dstOff, sz ); dstOff += sz; } setItem( srcPg, srcOff, eNvZero ); // Mark old location as invalid. } srcOff += sz; } while ( TRUE ); pgOff[pgRes-OSAL_NV_PAGE_BEG] = dstOff; /* In order to recover from a page compaction that is interrupted, * the logic in osal_nv_init() depends upon the following order: * 1. Compacted page is erased. * 2. State of the target of compaction is changed ePgActive to ePgInUse. */ erasePage( srcPg ); // Mark the reserve page as being in use. setPageUse( pgRes, TRUE ); // Set the reserve page to be the newly erased page. pgRes = srcPg; }
/********************************************************************* * @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; }