/****************************************************************************** @file nvocmp.c @brief NV driver for CC26x2 devices - On-Chip Multi-Page Flash Memory Group: CMCU, LPC Target Device: cc13xx_cc26xx ****************************************************************************** Copyright (c) 2023-2024, Texas Instruments Incorporated All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Texas Instruments Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************************** *****************************************************************************/ //***************************************************************************** // Design Overview //***************************************************************************** /* This driver implements a non-volatile (NV) memory system that utilizes multi pages (consecutive) of on-chip Flash memory. After initialization, all pages except one are ACTIVE and the remaining one page is available for "compaction" when the ACTIVE pages do not have enough empty space for data write operation. Compaction can occur 'just in time' during a data write operation or 'on demand' by application request. The compaction process is designed to survive a power cycle before it completes. It will resume where it was interrupted and complete the process. This driver makes the following assumptions and uses them to optimize the code and data storage design: (1) Flash memory is addressable at individual, 1-byte resolution so no padding or word-alignment is necessary (2) Flash has limited number of writes per flash 'sector' between erases. To prevent going over this limit, "small" items are written in one operation. Each Flash page has a "page header" which indicates its current state, located at the first byte of the Flash page and "compact header" which indicates its compaction state, located following "page header". The remainder of the Flash page contains NV data items which are packed together following the page header and compact header. Each NV data item has two parts, (1) a data block which is stored first (lower memory address), (2) immediately followed by item header (higher memory address). The item header contains information necessary to traverse the packed data items, as well as, current status of each data item. Obsolete items marked accordingly but a search for the newest instance of an item is sped up by starting the search at the last entry in the page (higher memory address). Each item is unique, addressed using three ID values (system ID, item ID, sub ID). These three values are stored in the header along with a 'signature', a CRC8 value, the length of the data block, and two status bits. The two status bits indicate whether an item is still active and the health or validity of an item. The signature byte is used by the driver to detect the presence of an item, and is the same for all items as well as the page header and compact header. The CRC8 value allows the driver to confirm the integrity of the items during compaction and optionally when an item read operation is requested. The length of the data block is used to jump from one item to the next. If this field is corrupted, the driver is forced to search for items by signature and possibly compute multiple CRC's to confirm it has found a valid item. Note that any corruption event forces a compaction to recover. To reduce further RAM consumption, the user can define NVOCMP_RAM_OPTIMIZATION to enable this feature. Note that for cc23x0 and cc27xx, NVOCMP_RAM_OPTIMIZATION is enabled by default. Alternatively, if this optimization is not needed in a particular application for cc23x0 and cc27xx, the user can define NVOCMP_NO_RAM_OPTIMIZATION to effectively disable this feature for cc23x0 and cc27xx. When RAM optimization is enabled, the user can configure the size of the working buffer by setting NVOCMP_RAM_BUFFER_SIZE, which defaults to 500. */ //***************************************************************************** // Use / Configuration //***************************************************************************** /* Since this is multi page NV driver, the number of NV pages is configurable. NVOCMP_NVPAGES = 1 means 1 page storage and 0 compaction page. NVOCMP_NVPAGES = 2 means 1 page storage and 1 compaction page. NVOCMP_NVPAGES = 3 means 2 pages storage and 1 compaction page. NVOCMP_NVPAGES = 4 means 3 pages storage and 1 compaction page. NVOCMP_NVPAGES = 5 means 4 pages storage and 1 compaction page. NVOCMP_NVPAGES = 6 means 5 pages storage and 1 compaction page. NVOCMP_NVPAGES can be configured from project option. If this flag is not configured, NVOCMP_NVPAGES = 2 will be by default. "nvintf.h" describes the generic NV interface which is used to access NVOCMP after initialization. Initialization is done by passing a function pointer struct to one of NVOCMP pointer loader functions. Once this is done, the pointer struct (which is part of the nvintf interface) should be used to call the nvintf initialization function, which will initialize NVOCMP. At this point NVOCMP is ready and loaded API functions can be called through the pointer structure. Note that some pointers may be NULL depending on which NVOCMP loader function was called. For example NVOCMP_loadApiPtrsMin() loads only the essential functions to reduce code size at link time. A sample code block is shown below: NVINTF_nvFuncts_t nvFps; NVOCMP_loadApiPtrs(&nvFps); nvFps.initNV(NULL); // Do some NV operations nvFps.compactNV(NULL); status = nvFps.readItem(id, 0, len, buf); Note: Each item operation results in a traversal of the page starting at the most recently written item. This makes 'finding' items by 'trying' item IDs in order extremely inefficient. The doNext() API call allows the user to find, read, or delete items in one page traversal. However, this call requires the user to lock access to NV until the operation is complete so it should be used carefully and sparingly. Note: The compile flag NVDEBUG can be passed to enable ASSERT and ALERT macros which provide assert and logging functionality. When this flag is used, a printf function of the form void nvprint(char * str) MUST be provided to link the driver. The function need not be functional, but it must exist. NVDEBUG also exposes driver global variables for debug and testing. Not all user-defines (such as NVDEBUG) are supported when using NVOCMP in a Linux environment. If debugging/logging functionality is required, the "nv-debug" or "nv-rdwr" logging flags can be enabled in the cfg INI file. Configuration: NVOCMP_STATS - Places a protected item with driver stats NVOCMP_CRCONREAD (on:1 off:0) - item crc is checked on read. Disabling this may increase driver speed but safety is reduced. NVOCMP_NVS_INDEX - The index of the NVS_Config structure which describes the flash sector that NVOCMP should use. Default is 0. NVOCMP_RECOVER_FROM_COMPACT_FAILURE - This define needs to be enabled by the customer. It is disabled be default. When enabled, it causes the NV driver to reformat all NV pages when there is an error while collecting valid items for compaction. When disabled and an error occurs while collecting valid items for compaction, NV pages will preserve the original information prior to the start of such operation. ENABLE_SANITY_CHECK - This define needs to be enabled if user needs the NVOCMP_sanityCheckApi() to be available. This function is used to perform a sanity check on the active partition to report if corruption has been detected. NVOCMP_RAM_OPTIMIZATION - Enables RAM optimization. NVOCMP_NO_RAM_OPTIMIZATION - Disables RAM optimization for cc23x0 and cc27xx, as it is enabled by default on this family of devices. NVOCMP_RAM_BUFFER_SIZE - Sets the size for the RAM buffer used when RAM optimization is enabled. Default value is 500. Dependencies: Requires NVS for NV access. Requires TI-RTOS GateMutexPri or POSIX mutex to be enabled in configuration. Requires API's in a crc.h to implement CRC functionality. */ //***************************************************************************** // Includes //***************************************************************************** #include #ifdef NVOCMP_POSIX_MUTEX #include #elif defined(NVOCMP_POSIX_SEM) #include #else #include #endif #include #include #ifndef NV_LINUX #include #include #ifdef NVOCMP_MIN_VDD_FLASH_MV #include #endif #endif #ifdef NV_LINUX #include "nv_linux.h" #endif //***************************************************************************** // Constants and Definitions //***************************************************************************** #define NVINTF_DONOWRAP 0x80 // Do not wrap around NV space #define NVOCMP_FASTCP 1 // Fast Compaction by Skipping All Active Item Pages #define NVOCMP_COMPR 0 // Order Change When Compaction #define NVOCMP_HDRLE 0 // Little Endian Format Item Header #define NVOCMP_FASTOFF 1 // Fast Search Offset #define NVOCMP_FASTITEM 0 // Fast Find Item #ifndef NVOCMP_NWSAMEITEM #define NVOCMP_NWSAMEITEM 0 // Not Write Same Item #endif #ifndef NVOCMP_MIGRATE_ENABLED #define NVOCMP_MIGRATE_DISABLED // Migration from old NVOCTP disabled by default #endif #define NVOCMP_NVONEP 1 // One Page NV #define NVOCMP_NVTWOP 2 // Two Page NV #define NVOCMP_NVSIZE NVOCMP_size #define NVOCMP_ADDPAGE(p, n) (((p) + (n)) % NVOCMP_NVSIZE) #define NVOCMP_INCPAGE(p) NVOCMP_ADDPAGE(p, 1) #define NVOCMP_DECPAGE(p) NVOCMP_ADDPAGE(p, NVOCMP_NVSIZE - 1) // Which NVS_config indice is used to initialize NVS. #ifndef NVOCMP_NVS_INDEX #define NVOCMP_NVS_INDEX 0 #endif // NVOCMP_NVS_INDEX // Maximum ID parameters - must be coordinated with header format #define NVOCMP_MAXSYSID 0x003F // 6 bits #define NVOCMP_MAXITEMID 0x03FF // 10 bits #define NVOCMP_MAXSUBID 0x03FF // 10 bits #define NVOCMP_MAXLEN 0x0FFF // 12 bits #define NVOCMP_INVALIDSUBID 0xFFFF // Invalid Sub Id // Contents of an erased Flash memory locations #define NVOCMP_ERASEDBYTE 0xFF #define NVOCMP_ERASEDWORD 0xFFFFFFFF // Size of byte #define NVOCMP_ONEBYTE 1 // Compressed ID bit spacing #define NVOCMP_CMPSPACE 12 // Invalid NV page - if 0xFF is ever used, change this definition #define NVOCMP_NULLPAGE 0xFF // Block size for Flash-Flash XFER (Bytes) #define NVOCMP_XFERBLKMAX 32 // Size in bytes of biggest item size that will be concatenated // in RAM before write, instead of header/data written separately #define NVOCMP_SMALLITEM 12 #if defined(NVOCMP_STATS) // NV item ID for driver diagnostics static const NVINTF_itemID_t diagId = NVOCMP_NVID_DIAG; #endif // NVOCMP_STATS // CRC options // When not NULL, reads will result in a CRC check before returning #define NVOCMP_CRCONREAD 1 // findItem search types // Find any item, item of spec'd sysid, item of spec'd sys and item id // or find the exact item specified enum { NVOCMP_FINDANY = 0x00, NVOCMP_FINDSYSID, NVOCMP_FINDITMID, NVOCMP_FINDSTRICT, NVOCMP_FINDCONTENT = 0x10 }; #define NVOCMP_FINDLMASK 0x0F #define NVOCMP_FINDHMASK 0xF0 //***************************************************************************** // Macros //***************************************************************************** // Makes an NV Flash address (for 0x2000 page size) #define NVOCMP_FLASHOFFSET(pg, ofs) ((uint32_t) (((pg) << PAGE_SIZE_LSHIFT) + (ofs))) // Optional user provided function is called before writes/erases // Intention is to check for sufficient voltage for operation #ifndef NV_LINUX #define NVOCMP_FLASHACCESS(err) \ { \ if (NVOCMP_voltCheckFptr) \ { \ if (!NVOCMP_voltCheckFptr()) \ { \ err = NVINTF_LOWPOWER; \ } \ } \ } #endif #ifdef NVOCMP_POSIX_MUTEX // Lock driver access via TI-RTOS gatemutex #define NVOCMP_LOCK() pthread_mutex_lock(&NVOCMP_gPosixMutex); // Unlock driver access via TI-RTOS gatemutex and return error code #define NVOCMP_UNLOCK(err) \ { \ pthread_mutex_unlock(&NVOCMP_gPosixMutex); \ return (err); \ } #elif defined(NVOCMP_POSIX_SEM) // Lock driver access via POSIX semaphore #define NVOCMP_LOCK() sem_wait(&NVOCMP_gPosixSem); // Unlock driver access via POSIX semaphore and return error code #define NVOCMP_UNLOCK(err) \ { \ sem_post(&NVOCMP_gPosixSem); \ return (err); \ } #else // Lock driver access via TI-RTOS gatemutex #define NVOCMP_LOCK() int32_t key = GateMutexPri_enter(NVOCMP_gMutexPri); // Unlock driver access via TI-RTOS gatemutex and return error code #define NVOCMP_UNLOCK(err) \ { \ GateMutexPri_leave(NVOCMP_gMutexPri, key); \ return (err); \ } #endif // Generate a compressed NV ID (NOTE: bit31 must be zero) #define NVOCMP_CMPRID(s, i, b) \ ((uint32_t) ((((((s) &NVOCMP_MAXSYSID) << NVOCMP_CMPSPACE) | ((i) &NVOCMP_MAXITEMID)) << NVOCMP_CMPSPACE) | \ ((b) &NVOCMP_MAXSUBID))) // NVOCMP Unit Test Assert Macro/Function #ifdef NVDEBUG extern void nvprint(char * message); extern void Main_assertHandler(uint8_t assertReason); static void NVOCMP_assert(bool cond, char * message, bool fatal) { if (!cond) { nvprint("NVDEBUG: "); nvprint(message); if (fatal) { Main_assertHandler(0); } } } #define NVOCMP_ASSERT(cond, message) NVOCMP_assert((cond), (message), true); #define NVOCMP_ALERT(cond, message) NVOCMP_assert((cond), (message), false); #define NVOCMP_ASSERT1(cond) \ { \ if (!cond) \ while (1) \ ; \ } #else #ifdef NV_LINUX #define NVOCMP_ASSERT1(cond) NVOCMP_ASSERT((cond), "NVOCMP_ASSERT1") #else #define NVOCMP_ASSERT(cond, message) #define NVOCMP_ALERT(cond, message) #define NVOCMP_ASSERT1(cond) \ { \ if (!cond) \ while (1) \ ; \ } #endif #endif // NVDEBUG //***************************************************************************** // Page and Header Definitions //***************************************************************************** #if defined(DeviceFamily_CC13X4) || defined(DeviceFamily_CC26X4) || defined(DeviceFamily_CC26X3) || \ defined(DeviceFamily_CC23X0R5) || defined(DeviceFamily_CC23X0R2) || defined(DeviceFamily_CC27XX) // CC26x4/CC13x4/CC23x0/cc27xx devices flash page size is (1 << 11) or 0x800 #define PAGE_SIZE_LSHIFT 11 #else // CC26x2/CC13x2 devices flash page size is (1 << 13) or 0x2000 #define PAGE_SIZE_LSHIFT 13 #endif #if !defined(FLASH_PAGE_SIZE) #define FLASH_PAGE_SIZE (1 << PAGE_SIZE_LSHIFT) #endif // FLASH_PAGE_SIZE #if !defined(NVOCMP_VERSION) // Version of NV page format (do not use 0xFF) #define NVOCMP_VERSION 0x03 #endif // NVOCMP_VERSION #if !defined(NVOCMP_SIGNATURE) // Page header validation byte (do not use 0xFF) #define NVOCMP_SIGNATURE 0x96 #endif // NVOCMP_SIGNATURE #ifndef NVOCMP_NO_RAM_OPTIMIZATION #if defined(DeviceFamily_CC23X0R5) || defined(DeviceFamily_CC23X0R2) || defined(DeviceFamily_CC27XX) #define NVOCMP_RAM_OPTIMIZATION #endif #endif #ifndef NVOCMP_RAM_OPTIMIZATION // Compact Memory #if !defined(NV_LINUX) && !defined(DeviceFamily_CC13X4) && !defined(DeviceFamily_CC26X4) && !defined(DeviceFamily_CC26X3) && \ !defined(DeviceFamily_CC23X0R5) && !defined(DeviceFamily_CC23X0R2) && !defined(DeviceFamily_CC27XX) #define NVOCMP_GPRAM #endif #ifdef NVOCMP_GPRAM #define RAM_BUFFER_ADDRESS (uint8_t *) GPRAM_BASE #else /* When CC23X0/CC27XX is used, as GPRAM is not supported, * an SRAM buffer of FLASH_PAGE_SIZE length is declared, * as the NVOCMP algorithm relies on it. * Also, when CC13X4 / CC26X3 / CC26X4 is used, * GPRAM cannot be used as it is always mapped to secure * address space, and therefore cannot be used by non-secure * application. A buffer in SRAM is used instead. * */ uint32_t tBuffer[FLASH_PAGE_SIZE >> 2]; #endif #else #ifdef NVOCMP_FASTOFF #undef NVOCMP_FASTOFF #endif #define NVOCMP_FASTOFF 0 #ifdef NVOCMP_FASTITEM #undef NVOCMP_FASTITEM #endif #define NVOCMP_FASTITEM 0 #ifndef NVOCMP_RAM_BUFFER_SIZE #define NVOCMP_RAM_BUFFER_SIZE 500 #endif uint8_t tBuffer[NVOCMP_RAM_BUFFER_SIZE]; #endif // Page header structure typedef struct { uint32_t state : 8; uint32_t cycle : 8; // Rolling page compaction count (0x00, 0xFF not used) uint32_t allActive : 2; uint32_t version : 6; // Version of NV page format uint32_t signature : 8; // Signature for formatted NV page } NVOCMP_pageHdr_t; typedef struct { uint16_t pageOffset; uint8_t page; uint8_t signature; } NVOCMP_compactHdr_t; // Page header size (bytes) #define NVOCMP_PGHDRLEN (sizeof(NVOCMP_pageHdr_t)) #define NVOCMP_COMPACTHDRLEN (sizeof(NVOCMP_compactHdr_t)) // Page header offsets (from 1st byte of page) #define NVOCMP_PGHDROFS 0 #define NVOCMP_PGHDRPST 0 // Page state #define NVOCMP_PGHDRCYC 1 // Cycle count #define NVOCMP_PGHDRVER 2 // Format version #define NVOCMP_PGHDRSIG 3 // Page signature // Compact header offsets #define NVOCMP_COMPMODEOFS 6 // Compact Mode Offset // Number of Compact headers #define NVOCMP_NOCOMPHDR 3 // Page data size, offset into page #define NVOCMP_PGDATAOFS (NVOCMP_PGHDRLEN + NVOCMP_NOCOMPHDR * NVOCMP_COMPACTHDRLEN) #define NVOCMP_PGDATAEND (FLASH_PAGE_SIZE - 1) #define NVOCMP_PGDATALEN (FLASH_PAGE_SIZE - NVOCMP_PGDATAOFS) // Page mode of operation #define NVOCMP_PGNORMAL 0xFF // normal operation #define NVOCMP_PGCDST 0xFE // used as compact destination #define NVOCMP_PGCDONE 0xFC #define NVOCMP_PGCSRC 0xF8 // used as compact source #define NVOCMP_PGMODEBIT 0x04 // NVOCTP header defines #define NVOCTP_PGACTIVE 0xA5 // Current active page #define NVOCTP_PGXFER 0x24 // Active page being compacted #define NVOCTP_PGDATAOFS NVOCMP_PGHDRLEN #define NVOCTP_VERSION 0x02 #define NVOCTP_SIGNATURE 0x96 // Page header state values - transitions change 1 bit in each nybble typedef enum NVOCMP_pageState { NVOCMP_PGNACT = 0xFF, NVOCMP_PGXDST = 0xFE, NVOCMP_PGRDY = 0x7E, NVOCMP_PGACT = 0x7C, NVOCMP_PGFULL = 0x78, NVOCMP_PGXSRC = 0x70, NVOCMP_PGNDEF = 0x00, } NVOCMP_pageState_t; typedef enum NVOCMP_compactStatus { NVOCMP_COMPACT_SUCCESS = 0x00, NVOCMP_COMPACT_SRCDONE = 0x01, NVOCMP_COMPACT_DSTDONE = 0x02, NVOCMP_COMPACT_BOTHDOE = 0x03, NVOCMP_COMPACT_FAILURE = 0x10, } NVOCMP_compactStatus_t; // Page compaction cycle count limits (0x00 and 0xFF not used) #define NVOCMP_MINCYCLE 0x01 // Minimum cycle count (after rollover) #define NVOCMP_MAXCYCLE 0xFE // Maximum cycle count (before rollover) #define NVOCMP_ALLACTIVE 0x3 // All Items are active #define NVOCMP_SOMEINACTIVE 0x0 // Some Items are inactive //***************************************************************************** // Item Header Definitions //***************************************************************************** // Item header structure typedef struct { uint32_t cmpid; // Compressed ID uint16_t subid; // Sub ID uint16_t itemid; // Item ID uint8_t sysid; // System ID uint8_t crc8; // crc byte uint8_t sig; // signature byte uint8_t stats; // Status 'marks' uint16_t hofs; // Header offset uint16_t len; // Data length uint8_t hpage; // Header page } NVOCMP_itemHdr_t; // Length (bytes) of compressed header #define NVOCMP_ITEMHDRLEN 7 // Offset from beginning (low address) of header to fields in the header #define NVOCMP_HDRSIGOFS 6 #define NVOCMP_HDRVLDOFS 5 // Number of bytes in header to include in CRC calculation #define NVOCMP_HDRCRCINC 5 // Compressed item header information <-- Lower Addr Higher Addr--> // Byte: [0] [1] [2] [3] [4] [5] [6] // Item: SSSSSSII IIIIIIII SSSSSSSS SSLLLLLL LLLLLLCC CCCCCCAV SSSSSSSS // LSB of field: ^ ^ ^ ^ ^ // Bit: 0 15 25 37 45 55 // // Bit(s) Bit Field Description // ============================= // 48-55: Signature byte (NVOCMP_SIGNATURE) // 47: valid id mark (0=valid) // 46: active id mark (1=active) // 38-45: CRC8 value // 26-37: data length (0-4095) // 16-25: item sub id (0-1023) // 6-15: nv item id (0-1023) // 0-5: system id (0-63) // Bit47 in compressed header - '1' indicates 'active' NV item // A deleted item is 'inactive' #define NVOCMP_ACTIVEIDBIT 0x2 // Bit46 in compressed header - '0' indicates 'valid' NV item // A corrupted item is 'invalid' #define NVOCMP_VALIDIDBIT 0x1 // This bit is NOT included in the NV item itself but is encoded // the 'stats' field of the itemHdr_t struct when the item is read #define NVOCMP_FOLLOWBIT 0x4 // Index of last item header byte #define NVOCMP_ITEMHDREND (NVOCMP_ITEMHDRLEN - 1) // Compressed item header byte array typedef uint8_t cmpIH_t[NVOCMP_ITEMHDRLEN]; // Item write parameters typedef struct { NVOCMP_itemHdr_t * iHdr; // Ptr to item header uint16_t dOfs; // Source data offset uint16_t bOfs; // Buffer data offset uint16_t len; // Buffer data length uint8_t * pBuf; // Ptr to data buffer } NVOCMP_itemWrp_t; typedef struct { void * cBuf; // Pointer to content to search uint16_t clength; // Length of content to search uint16_t coff; // Offset content to search void * rBuf; // Pointer to content to read uint16_t rlength; // Length content to read } NVOCMP_itemInfo_t; typedef enum NVOCMP_initAction { NVOCMP_NORMAL_INIT = 0, NVOCMP_NORMAL_RESUME, NVOCMP_RECOVER_COMPACT, NVOCMP_RECOVER_ERASE, NVOCMP_FORCE_CLEAN, NVOCMP_NORMAL_MIGRATE, NVOCMP_ERROR_UNKNOWN, } NVOCMP_initAction_t; typedef enum NVOCMP_writeMode { NVOCMP_WRITE = 0, NVOCMP_CREATE, NVOCMP_UPDATE, } NVOCMP_writeMode_t; typedef struct { uint8_t state; // page state uint8_t cycle; // page compaction cycle count. Used to select the 'newest' active page // at device reset, in the very unlikely scenario that both pages are active. uint8_t mode; // compact mode uint8_t allActive; // all items are active or not uint8_t sPage; uint8_t ePage; uint16_t offset; // page offset uint16_t sOffset; uint16_t eOffset; } NVOCMP_pageInfo_t; typedef struct { uint8_t xDstPage; // xdst page uint8_t xSrcSPage; // xsrc start page uint8_t xSrcEPage; // xsrc end page uint8_t xSrcPages; // no of xsrc pages uint16_t xDstOffset; // xdst offset uint16_t xSrcSOffset; // xsrc start offset uint16_t xSrcEOffset; // xsrc end offset } NVOCMP_compactInfo_t; typedef struct { uint8_t nvSize; // no of NV pages uint8_t headPage; // head active page uint8_t tailPage; // transfer destination page uint8_t actPage; // current active page uint8_t xsrcPage; // transfer source page uint8_t forceCompact; // force compaction to happen uint16_t actOffset; // active page offset uint16_t xsrcOffset; // transfer source page offset uint16_t xdstOffset; // transfer destination page offset NVOCMP_compactInfo_t compactInfo; NVOCMP_pageInfo_t pageInfo[NVOCMP_NVPAGES]; } NVOCMP_nvHandle_t; //***************************************************************************** // Local variables //***************************************************************************** #define NVOCMP_NULLOFFSET 0xFFFF #define DEFAULT_COMPACTHDR { NVOCMP_NULLOFFSET, NVOCMP_NULLPAGE, NVOCMP_SIGNATURE }; #define THISPAGEHDR 0 #define XSRCSTARTHDR 1 #define XSRCENDHDR 2 #define NVOCMP_COMPACTHDRLEN (sizeof(NVOCMP_compactHdr_t)) // NVS Objects #ifdef NVDEBUG // Expose these in debug mode NVS_Handle NVOCMP_nvsHandle; NVS_Attrs NVOCMP_nvsAttrs; NVOCMP_nvHandle_t NVOCMP_nvHandle; #else static NVS_Handle NVOCMP_nvsHandle; static NVS_Attrs NVOCMP_nvsAttrs; /* The following variable has been made non-static, so it can be accessed * through other modules through "extern". However, this is a temporary * solution, as this variable is meant to be used only by NVOCMP. * Users of NVOCMP must only access these features through the available * APIs.*/ /*static*/ NVOCMP_nvHandle_t NVOCMP_nvHandle; #endif // NVDEBUG // Flag to indicate that a fatal error occurred while writing to or erasing the // Flash memory. If flag is set, it's unsafe to attempt another write or erase. // This flag locks writes to Flash until the next system reset. static uint8_t NVOCMP_failF = NVINTF_NOTREADY; // Flag to indicate that a non-fatal error occurred while writing to or erasing // Flash memory. With flag set, it's still safe to attempt a write or erase. // This flag is reset by any API calls that cause an erase/write to Flash. static uint8_t NVOCMP_failW; // TI-RTOS gateMutexPri for the NV driver API functions #ifdef NVOCMP_POSIX_MUTEX static pthread_mutex_t NVOCMP_gPosixMutex; #elif defined(NVOCMP_POSIX_SEM) static sem_t NVOCMP_gPosixSem; #else static GateMutexPri_Handle NVOCMP_gMutexPri; #endif // Small NV Item Buffer, for item construction static uint8_t NVOCMP_itemBuffer[NVOCMP_SMALLITEM]; // Function Pointer to an optional user provided voltage check function static bool (*NVOCMP_voltCheckFptr)(void); // Diagnostic counter for bad CRCs #ifdef NVOCMP_STATS static uint16_t NVOCMP_badCRCCount = 0; #endif // NVOCMP_STATS NVOCMP_initAction_t gAction; uint8_t NVOCMP_size; //***************************************************************************** // NV API Function Prototypes //***************************************************************************** static uint8_t NVOCMP_initNvApi(void * param); static uint8_t NVOCMP_compactNvApi(uint16_t min); static uint8_t NVOCMP_createItemApi(NVINTF_itemID_t id, uint32_t len, void * buf); static uint8_t NVOCMP_updateItemApi(NVINTF_itemID_t id, uint32_t len, void * buf); static uint8_t NVOCMP_deleteItemApi(NVINTF_itemID_t id); static uint32_t NVOCMP_getItemLenApi(NVINTF_itemID_t id); static uint8_t NVOCMP_readItemApi(NVINTF_itemID_t id, uint16_t ofs, uint16_t len, void * buf); static uint8_t NVOCMP_readContItemApi(NVINTF_itemID_t id, uint16_t ofs, uint16_t rlen, void * rBuf, uint16_t clen, uint16_t coff, void * cBuf, uint16_t * pSubId); static uint8_t NVOCMP_writeItemApi(NVINTF_itemID_t id, uint16_t len, void * buf); static uint8_t NVOCMP_doNextApi(NVINTF_nvProxy_t * prx); static int32_t NVOCMP_lockNvApi(void); static void NVOCMP_unlockNvApi(int32_t); static bool NVOCMP_expectCompApi(uint16_t len); static uint8_t NVOCMP_eraseNvApi(void); static uint32_t NVOCMP_getFreeNvApi(void); #ifdef ENABLE_SANITY_CHECK static uint32_t NVOCMP_sanityCheckApi(void); #endif //***************************************************************************** // NV Local Function Prototypes //***************************************************************************** static void NVOCMP_initNv(NVOCMP_nvHandle_t * pNvHandle); static uint8_t NVOCMP_scanPage(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageInfo_t * pPageInfo); static int8_t NVOCMP_findItem(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * pHdr, int8_t flag, NVOCMP_itemInfo_t * pInfo); static uint8_t NVOCMP_addItem(NVOCMP_nvHandle_t * pNvHandle, NVOCMP_itemHdr_t * iHdr, uint8_t * pBuf, NVOCMP_writeMode_t wm); static void NVOCMP_writeItem(NVOCMP_nvHandle_t * pNvHandle, NVOCMP_itemHdr_t * pHdr, uint8_t dstPg, uint16_t dstOff, uint8_t * pBuf); static uint8_t NVOCMP_erase(NVOCMP_nvHandle_t * pNvHandle, uint8_t dstPg); static int16_t NVOCMP_compactPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t nBytes); static NVOCMP_compactStatus_t NVOCMP_compact(NVOCMP_nvHandle_t * pNvHandle); static uint8_t NVOCMP_getDstPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t len); static void NVOCMP_changePageState(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageState_t state); static void NVOCMP_setPageState(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageState_t state); static void NVOCMP_setItemInactive(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t iOfs); static uint8_t NVOCMP_readItem(NVOCMP_itemHdr_t * iHdr, uint16_t ofs, uint16_t len, void * pBuf, bool flag); static uint8_t NVOCMP_checkItem(NVINTF_itemID_t * id, uint16_t len, NVOCMP_itemHdr_t * iHdr, uint8_t flag); static inline void NVOCMP_read(uint8_t pg, uint16_t off, uint8_t * pBuf, uint16_t len); static uint8_t NVOCMP_write(uint8_t dstPg, uint16_t off, uint8_t * pBuf, uint16_t len); static void NVOCMP_readHeader(uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * iHdr, bool flag); static void NVOCMP_setCompactHdr(uint8_t dstPg, uint8_t pg, int16_t offset, uint16_t location); static uint16_t NVOCMP_findOffset(uint8_t pg, uint16_t ofs); static uint8_t NVOCMP_doNVCRC(uint8_t pg, uint16_t ofs, uint16_t len, uint8_t crc, bool flag); static uint8_t NVOCMP_doRAMCRC(uint8_t * input, uint16_t len, uint8_t crc); static uint8_t NVOCMP_verifyCRC(uint16_t iOfs, uint16_t len, uint8_t crc, uint8_t pg, bool flag); static uint8_t NVOCMP_readByte(uint8_t pg, uint16_t ofs); static void NVOCMP_writeByte(uint8_t pg, uint16_t ofs, uint8_t bwv); #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) static uint8_t NVOCMP_findDstPage(NVOCMP_nvHandle_t * pNvHandle); static uint8_t NVOCMP_cleanPage(NVOCMP_nvHandle_t * pNvHandle); static uint8_t NVOCMP_findPage(NVOCMP_pageState_t state); static void NVOCMP_getCompactHdr(uint8_t dstPg, uint16_t location, NVOCMP_compactHdr_t * pHdr); #endif #if (NVOCMP_NVPAGES > NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED) static void NVOCMP_migratePage(NVOCMP_nvHandle_t * pNvHandle, uint8_t page); #endif #if ((NVOCMP_NVPAGES > NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) || defined NVOCMP_RAM_OPTIMIZATION static void NVOCMP_copyItem(uint8_t srcPg, uint8_t dstPg, uint16_t sOfs, uint16_t dOfs, uint16_t len); #endif //***************************************************************************** // Load Pointer Functions (These are declared in nvoctp.h) //***************************************************************************** /** * @fn NVOCMP_loadApiPtrs * * @brief Global function to return function pointers for NV driver API that * are supported by this module, NULL for functions not supported. * * @param pfn - pointer to caller's structure of NV function pointers * * @return none */ void NVOCMP_loadApiPtrs(NVINTF_nvFuncts_t * pfn) { // Load caller's structure with pointers to the NV API functions pfn->initNV = &NVOCMP_initNvApi; pfn->compactNV = &NVOCMP_compactNvApi; pfn->createItem = &NVOCMP_createItemApi; pfn->updateItem = &NVOCMP_updateItemApi; pfn->deleteItem = &NVOCMP_deleteItemApi; pfn->readItem = &NVOCMP_readItemApi; pfn->readContItem = &NVOCMP_readContItemApi; pfn->writeItem = &NVOCMP_writeItemApi; pfn->getItemLen = &NVOCMP_getItemLenApi; pfn->lockNV = NULL; pfn->unlockNV = NULL; pfn->doNext = NULL; pfn->expectComp = &NVOCMP_expectCompApi; pfn->eraseNV = &NVOCMP_eraseNvApi; pfn->getFreeNV = &NVOCMP_getFreeNvApi; #ifdef ENABLE_SANITY_CHECK pfn->sanityCheck = &NVOCMP_sanityCheckApi; #endif } /** * @fn NVOCMP_loadApiPtrsMin * * @brief Global function to return function pointers for NV driver API that * are supported by this module, NULL for functions not supported. * This function loads the minimum necessary API functions. * This should allow smaller code size. * * @param pfn - pointer to caller's structure of NV function pointers * * @return none */ void NVOCMP_loadApiPtrsMin(NVINTF_nvFuncts_t * pfn) { // Load caller's structure with pointers to the NV API functions pfn->initNV = &NVOCMP_initNvApi; pfn->compactNV = &NVOCMP_compactNvApi; pfn->createItem = NULL; pfn->updateItem = NULL; pfn->deleteItem = NULL; pfn->readItem = &NVOCMP_readItemApi; pfn->readContItem = NULL; pfn->writeItem = &NVOCMP_writeItemApi; pfn->getItemLen = NULL; pfn->lockNV = NULL; pfn->unlockNV = NULL; pfn->doNext = NULL; pfn->expectComp = &NVOCMP_expectCompApi; pfn->eraseNV = &NVOCMP_eraseNvApi; pfn->getFreeNV = &NVOCMP_getFreeNvApi; #ifdef ENABLE_SANITY_CHECK pfn->sanityCheck = &NVOCMP_sanityCheckApi; #endif } /** * @fn NVOCMP_loadApiPtrsExt * * @brief Global function to return function pointers for NV driver API that * are supported by this module, NULL for functions not supported. * This function also loads the 'extended' API function pointers. * * @param pfn - pointer to caller's structure of NV function pointers * * @return none */ void NVOCMP_loadApiPtrsExt(NVINTF_nvFuncts_t * pfn) { // Load caller's structure with pointers to the NV API functions pfn->initNV = &NVOCMP_initNvApi; pfn->compactNV = &NVOCMP_compactNvApi; pfn->createItem = &NVOCMP_createItemApi; pfn->updateItem = &NVOCMP_updateItemApi; pfn->deleteItem = &NVOCMP_deleteItemApi; pfn->readItem = &NVOCMP_readItemApi; pfn->readContItem = &NVOCMP_readContItemApi; pfn->writeItem = &NVOCMP_writeItemApi; pfn->getItemLen = &NVOCMP_getItemLenApi; pfn->lockNV = &NVOCMP_lockNvApi; pfn->unlockNV = &NVOCMP_unlockNvApi; pfn->doNext = &NVOCMP_doNextApi; pfn->expectComp = &NVOCMP_expectCompApi; pfn->eraseNV = &NVOCMP_eraseNvApi; pfn->getFreeNV = &NVOCMP_getFreeNvApi; #ifdef ENABLE_SANITY_CHECK pfn->sanityCheck = &NVOCMP_sanityCheckApi; #endif } /** * @fn NVOCMP_setCheckVoltage * * @brief Global function to allow user to provide a voltage check function * for the driver to use. If a pointer is provided, the driver will * call the provided function before flash erases and writes. The * provided function should return true when the battery voltage is * sufficient and vice versa. The user can withdraw their function * by passing a NULL pointer to this function. * * @param funcPtr - pointer to a function which returns a bool. * * @return none */ extern void NVOCMP_setCheckVoltage(void * funcPtr) { #ifndef NV_LINUX NVOCMP_voltCheckFptr = (bool (*)()) funcPtr; #else // Do nothing (void) NVOCMP_voltCheckFptr; #endif } #ifdef NVOCMP_MIN_VDD_FLASH_MV /** * @fn NVOCMP_setLowVoltageCb * * @brief Global function to allow user to provide a low voltage callback function * for the driver to use. If a pointer is provided, the driver will * call the provided function when low voltage detected. * * @param funcPtr - pointer to a function. * * @return none */ static lowVoltCbFptr NVOCMP_lowVoltCbFptr = NULL; extern void NVOCMP_setLowVoltageCb(lowVoltCbFptr funcPtr) { #ifndef NV_LINUX NVOCMP_lowVoltCbFptr = (lowVoltCbFptr) funcPtr; #else // Do nothing (void) NVOCMP_lowVoltCbFptr; #endif } /******************************************************************************* * @fn NVOCMP_checkVoltage() * * @brief Checks the caller supplied voltage threshold against the value read * from the CC26xx BATMON register. * * @param none * * @return false if device voltage less than limit, otherwise true ******************************************************************************* */ static bool NVOCMP_checkVoltage(void) { uint32_t voltage = AONBatMonBatteryVoltageGet(); voltage = (voltage * 1000) >> AON_BATMON_BAT_FRAC_W; if (voltage < NVOCMP_MIN_VDD_FLASH_MV) { // Measured device voltage is below threshold if (NVOCMP_lowVoltCbFptr) { NVOCMP_lowVoltCbFptr(voltage); } return (false); } return (true); } #endif #ifdef NVDEBUG void NVOCMP_corruptData(uint8_t pg, uint16_t off, uint16_t len, uint8_t buf) { NVS_write(NVOCMP_nvsHandle, NVOCMP_FLASHOFFSET(pg, off), (uint8_t *) &buf, len, NVS_WRITE_POST_VERIFY); } #endif /****************************************************************************** * @fn NVOCMP_initNvApi * * @brief API function to initialize the specified NV Flash pages * * @param param - pointer to caller's structure of NV init parameters * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_initNvApi(void * param) { NVOCMP_ALERT(false, "NVOCMP Init. Called!") NVOCMP_failW = NVOCMP_failF; if (NVOCMP_failF == NVINTF_NOTREADY) { #ifdef NVOCMP_POSIX_MUTEX pthread_mutexattr_t attr; #elif defined(NVOCMP_POSIX_SEM) #else GateMutexPri_Params gateParams; #endif // Only one init per device reset NVOCMP_failF = NVINTF_SUCCESS; NVOCMP_failW = NVINTF_SUCCESS; // Create a priority gate mutex for the NV driver #ifdef NVOCMP_POSIX_MUTEX if (pthread_mutexattr_init(&attr) != 0) { NVOCMP_failF = NVINTF_FAILURE; return (NVOCMP_failF); } #ifndef NV_LINUX attr.type = PTHREAD_MUTEX_RECURSIVE; #else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); #endif if (pthread_mutex_init(&NVOCMP_gPosixMutex, &attr) != 0) { NVOCMP_failF = NVINTF_FAILURE; return (NVOCMP_failF); } #elif defined(NVOCMP_POSIX_SEM) sem_init(&NVOCMP_gPosixSem, 0 /* ignored */, 1); #else GateMutexPri_Params_init(&gateParams); NVOCMP_gMutexPri = GateMutexPri_create(&gateParams, NULL); #endif memset(&NVOCMP_nvHandle, 0, sizeof(NVOCMP_nvHandle_t)); memset(&NVOCMP_nvHandle.compactInfo, 0xFF, sizeof(NVOCMP_compactInfo_t)); #ifndef NV_LINUX #ifdef NVOCMP_MIN_VDD_FLASH_MV NVOCMP_setCheckVoltage((void *) &NVOCMP_checkVoltage); #endif // Initialize NVS objects NVS_init(); // Use default NVS_Params to open this flash region NVOCMP_nvsHandle = NVS_open(NVOCMP_NVS_INDEX, param); // Get NV hardware attributes NVS_getAttrs(NVOCMP_nvsHandle, &NVOCMP_nvsAttrs); #else NV_LINUX_init(); NVOCMP_nvsHandle = NVS_HANDLE; NVOCMP_nvsAttrs.sectorSize = FLASH_PAGE_SIZE; NVOCMP_nvsAttrs.regionSize = FLASH_PAGE_SIZE * NVOCMP_NVPAGES; #endif NVOCMP_nvHandle.nvSize = NVOCMP_nvsAttrs.regionSize / NVOCMP_nvsAttrs.sectorSize; NVOCMP_nvHandle.nvSize = NVOCMP_nvHandle.nvSize > NVOCMP_NVPAGES ? NVOCMP_NVPAGES : NVOCMP_nvHandle.nvSize; NVOCMP_size = NVOCMP_nvHandle.nvSize; // Confirm NV region has expected characteristics if (FLASH_PAGE_SIZE != NVOCMP_nvsAttrs.sectorSize || (NVOCMP_NVSIZE * FLASH_PAGE_SIZE > NVOCMP_nvsAttrs.regionSize)) { NVOCMP_failF = NVINTF_FAILURE; NVOCMP_EXCEPTION(pg, NVINTF_FAILURE) return (NVOCMP_failF); } // Confirm that the NVS region opened properly if (NVOCMP_nvsHandle == NULL) { NVOCMP_failF = NVINTF_FAILURE; NVOCMP_ASSERT(false, "NVS HANDLE IS NULL") NVOCMP_EXCEPTION(pg, NVINTF_NOTREADY); return (NVOCMP_failF); } // Initialize force compaction to false NVOCMP_nvHandle.forceCompact = 0; // Look for active page and clean up the other if necessary NVOCMP_nvHandle.actPage = NVOCMP_NULLPAGE; NVOCMP_nvHandle.actOffset = FLASH_PAGE_SIZE; NVOCMP_initNv(&NVOCMP_nvHandle); #if defined(NVOCMP_STATS) { uint8_t err; NVOCMP_diag_t diags; // Look for a copy of diagnostic info err = NVOCMP_readItemApi(diagId, 0, sizeof(diags), &diags); if (err == NVINTF_NOTFOUND) { // Assume this is the first time, memset(&diags, 0, sizeof(diags)); // Space available for everything else diags.available = FLASH_PAGE_SIZE - (NVOCMP_nvHandle.actOffset + NVOCMP_ITEMHDRLEN + sizeof(diags)); } // Remember this reset diags.resets += 1; // Create/Update the diagnostic NV item NVOCMP_writeItemApi(diagId, sizeof(diags), &diags); } #endif // NVOCMP_STATS } return (NVOCMP_failW); } /****************************************************************************** * @fn NVOCMP_eraseNvApi * * @brief API function to erase whole NV pages * * @param none * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_eraseNvApi(void) { uint8_t pg; uint8_t err = NVINTF_SUCCESS; // Check voltage if possible NVOCMP_FLASHACCESS(err) if (err) { return (err); } NVOCMP_LOCK(); // Erase All pages before start for (pg = 0; pg < NVOCMP_NVSIZE; pg++) { NVOCMP_failW |= NVOCMP_erase(&NVOCMP_nvHandle, pg); } err = NVOCMP_failW; // initial state, set head page, act page and tail page NVOCMP_nvHandle.headPage = 0; NVOCMP_nvHandle.tailPage = NVOCMP_NVSIZE - 1; NVOCMP_nvHandle.actPage = 0; NVOCMP_nvHandle.actOffset = NVOCMP_nvHandle.pageInfo[NVOCMP_nvHandle.actPage].offset; NVOCMP_changePageState(&NVOCMP_nvHandle, NVOCMP_nvHandle.headPage, NVOCMP_PGRDY); NVOCMP_changePageState(&NVOCMP_nvHandle, NVOCMP_nvHandle.tailPage, NVOCMP_PGXDST); #ifdef NV_LINUX if (err == NVINTF_SUCCESS) { NV_LINUX_save(); } #endif NVOCMP_UNLOCK(err); } /****************************************************************************** * @fn NVOCMP_getFreeNvApi * * @brief API function to get free space in whole NV pages * * @param none * * @return bytes of free space */ static uint32_t NVOCMP_getFreeNvApi(void) { uint8_t pg = NVOCMP_nvHandle.actPage; NVOCMP_pageHdr_t pageHdr; uint32_t freespace = 0; #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) uint16_t nvSearched = 0; for (pg = NVOCMP_nvHandle.actPage; nvSearched < NVOCMP_NVSIZE; pg = NVOCMP_INCPAGE(pg)) { nvSearched++; if (pg == NVOCMP_nvHandle.tailPage) { continue; } #endif NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); if ((pageHdr.state == NVOCMP_PGNACT) || (pageHdr.state == NVOCMP_PGRDY) || (pageHdr.state == NVOCMP_PGACT)) { freespace += (FLASH_PAGE_SIZE - NVOCMP_nvHandle.pageInfo[pg].offset); } #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) } #endif return (freespace); } /****************************************************************************** * @fn NVOCMP_compactNvApi * * @brief API function to force NV active page compaction * * @param minAvail - threshold size of available bytes on Flash page to do * compaction: 0 = always, >0 = minimum remaining bytes * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_compactNvApi(uint16_t minAvail) { uint8_t err = NVINTF_SUCCESS; // Check voltage if possible NVOCMP_FLASHACCESS(err) if (err) { return (err); } // Prevent RTOS thread contention NVOCMP_LOCK(); NVOCMP_ALERT(false, "API Compaction Request.") err = NVOCMP_failF; // Check for a fatal error if (err == NVINTF_SUCCESS) { int16_t left; // Number of bytes left on active page left = FLASH_PAGE_SIZE - NVOCMP_nvHandle.actOffset; // Time to do a compaction? if ((left < minAvail) || (minAvail == 0)) { // Transfer all items to non-ACTIVE page (void) NVOCMP_compactPage(&NVOCMP_nvHandle, 0); // 'failW' indicates compaction status err = NVOCMP_failW; } else { // Indicate "bad" minAvail value err = NVINTF_BADPARAM; } } #ifdef NV_LINUX if (err == NVINTF_SUCCESS) { NV_LINUX_save(); } #endif NVOCMP_UNLOCK(err); } //***************************************************************************** // API Functions - NV Data Items //***************************************************************************** /****************************************************************************** * @fn NVOCMP_createItemApi * * @brief API function to create a new NV item in Flash memory. This function * will return an error if the specified item already exists. * * @param id - NV item type identifier * @param len - length of NV data * @param pBuf - pointer to caller's data buffer (NULL is illegal) * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_createItemApi(NVINTF_itemID_t id, uint32_t len, void * pBuf) { uint8_t err; NVOCMP_itemHdr_t iHdr; // Parameter Sanity Check if (pBuf == NULL || len == 0) { return (NVINTF_BADPARAM); } err = NVOCMP_checkItem(&id, len, &iHdr, NVOCMP_FINDSTRICT); if (err) { return (err); } // Check voltage if possible NVOCMP_FLASHACCESS(err) if (err) { return (err); } // Prevent RTOS thread contention NVOCMP_LOCK(); err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL); if (err == NVINTF_SUCCESS) { err = NVINTF_EXIST; } else if (err == NVINTF_NOTFOUND) { // Create the new item err = NVOCMP_addItem(&NVOCMP_nvHandle, &iHdr, pBuf, NVOCMP_CREATE); if (err != NVINTF_SUCCESS) { NVOCMP_ALERT(false, "createItem failed.") err = NVINTF_FAILURE; } } else { NVOCMP_ALERT(false, "createItem failed.") err = NVINTF_FAILURE; } #ifdef NV_LINUX if (err == NVINTF_SUCCESS) { NV_LINUX_save(); } #endif NVOCMP_UNLOCK(err); } /****************************************************************************** * @fn NVOCMP_updateItemApi * * @brief API function to update an existing NV item in Flash memory. This function * will return an error if the specified item does not exist. * * @param id - NV item type identifier * @param len - length of NV data * @param pBuf - pointer to caller's data buffer (NULL is illegal) * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_updateItemApi(NVINTF_itemID_t id, uint32_t len, void * pBuf) { uint8_t err; NVOCMP_itemHdr_t iHdr; // Parameter Sanity Check if (pBuf == NULL || len == 0) { return (NVINTF_BADPARAM); } err = NVOCMP_checkItem(&id, len, &iHdr, NVOCMP_FINDSTRICT); if (err) { return (err); } // Check voltage if possible NVOCMP_FLASHACCESS(err) if (err) { return (err); } // Prevent RTOS thread contention NVOCMP_LOCK(); err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL); if (err == NVINTF_SUCCESS) { // Create the new item err = NVOCMP_addItem(&NVOCMP_nvHandle, &iHdr, pBuf, NVOCMP_UPDATE); if ((err == NVINTF_SUCCESS) && (iHdr.hofs > 0)) { // Mark old item as inactive NVOCMP_setItemInactive(&NVOCMP_nvHandle, iHdr.hpage, iHdr.hofs); err = NVOCMP_failW; } } else if (err == NVINTF_NOTFOUND) { err = NVINTF_NOTFOUND; } else { NVOCMP_ALERT(false, "updateItem failed.") err = NVINTF_FAILURE; } #ifdef NV_LINUX if (err == NVINTF_SUCCESS) { NV_LINUX_save(); } #endif NVOCMP_UNLOCK(err); } /****************************************************************************** * @fn NVOCMP_deleteItemApi * * @brief API function to delete an existing NV item from Flash memory. Note, * it is inefficient to use this function to delete a range of items. * The doNext call is recommended for that use case. * * @param id - NV item type identifier * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_deleteItemApi(NVINTF_itemID_t id) { uint8_t err; NVOCMP_itemHdr_t iHdr; #if defined(NVOCMP_STATS) if (!memcmp(&id, &diagId, sizeof(NVINTF_itemID_t))) { // Protect NV driver item(s) return (NVINTF_BADSYSID); } #endif // NVOCMP_STATS err = NVOCMP_checkItem(&id, 0, &iHdr, NVOCMP_FINDSTRICT); if (err) { return (err); } // Check voltage if possible NVOCMP_FLASHACCESS(err) if (err) { return (err); } // Prevent RTOS thread contention NVOCMP_LOCK(); err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL); if (!err) { // Mark this item as inactive NVOCMP_setItemInactive(&NVOCMP_nvHandle, iHdr.hpage, iHdr.hofs); // Verify that item has been removed err = (NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL) == NVINTF_NOTFOUND) ? NVOCMP_failW : NVINTF_FAILURE; // If item did get deleted, report 'failW' status NVOCMP_ALERT(err == NVOCMP_failW, "Item delete failed.") } #ifdef NV_LINUX if (err == NVINTF_SUCCESS) { NV_LINUX_save(); } #endif NVOCMP_UNLOCK(err); } /****************************************************************************** * @fn NVOCMP_getItemLenApi * * @brief API function to return the length of an NV data item * * @param id - NV item type identifier * * @return NV item length or 0 if item not found */ static uint32_t NVOCMP_getItemLenApi(NVINTF_itemID_t id) { uint8_t err; uint32_t len = 0; NVOCMP_itemHdr_t iHdr; err = NVOCMP_checkItem(&id, 0, &iHdr, NVOCMP_FINDSTRICT); if (err) { return (len); } // Prevent RTOS thread contention NVOCMP_LOCK(); // If there was any error, report zero length len = (NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL) != NVINTF_SUCCESS) ? 0 : iHdr.len; NVOCMP_UNLOCK(len); } /****************************************************************************** * @fn NVOCMP_readContItemApi * * @brief API function to read data from an NV item by comparing content * * @param id - NV item type identifier * @param ofs - offset into NV data * @param rlen - length of NV data to return (0 is illegal) * @param rBuf - pointer to caller's read data buffer (NULL is illegal) * @param clen - length of NV data to return (0 is illegal) * @param coff - offset of content in data * @param cBuf - pointer to caller's read data buffer (NULL is illegal) * @param pSubId - pointer to store sub Id (NULL is illegal) * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_readContItemApi(NVINTF_itemID_t id, uint16_t ofs, uint16_t rlen, void * rBuf, uint16_t clen, uint16_t coff, void * cBuf, uint16_t * pSubId) { uint8_t err; NVOCMP_itemHdr_t iHdr; NVOCMP_itemInfo_t itemInfo; *pSubId = NVOCMP_INVALIDSUBID; // Parameter Sanity Check if (rBuf == NULL || rlen == 0 || cBuf == NULL || clen == 0 || coff > FLASH_PAGE_SIZE) { return (NVINTF_BADPARAM); } err = NVOCMP_checkItem(&id, rlen, &iHdr, NVOCMP_FINDSTRICT); if (err) { return (err); } // Prevent RTOS thread contention NVOCMP_LOCK(); itemInfo.cBuf = cBuf; itemInfo.clength = clen; itemInfo.coff = coff; itemInfo.rBuf = rBuf; itemInfo.rlength = rlen; err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDITMID | NVOCMP_FINDCONTENT, &itemInfo); if (!err) { *pSubId = iHdr.subid; } NVOCMP_UNLOCK(err); } /****************************************************************************** * @fn NVOCMP_readItemApi * * @brief API function to read data from an NV item * * @param id - NV item type identifier * @param ofs - offset into NV data * @param len - length of NV data to return (0 is illegal) * @param pBuf - pointer to caller's read data buffer (NULL is illegal) * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_readItemApi(NVINTF_itemID_t id, uint16_t ofs, uint16_t len, void * pBuf) { uint8_t err; NVOCMP_itemHdr_t iHdr; // Parameter Sanity Check if (pBuf == NULL || len == 0) { return (NVINTF_BADPARAM); } err = NVOCMP_checkItem(&id, len, &iHdr, NVOCMP_FINDSTRICT); if (err) { return (err); } // Prevent RTOS thread contention NVOCMP_LOCK(); err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL); // Read Item if (!err) { err = NVOCMP_readItem(&iHdr, ofs, len, pBuf, false); } NVOCMP_UNLOCK(err); } /****************************************************************************** * @fn NVOCMP_writeItemApi * * @brief API function to write data to item, creates item if needed. * Note that when writing to an existing item, data is not * checked for redundancy. Data passed to this function will be * written to NV. NOTE: It is not recommended to write items with * SYSID 0 as this is reserved for the driver. NVOCMP will not * delete items with this SYSID. * * @param id - NV item type identifier * @param len - data buffer length to write into NV block (0 is illegal) * @param pBuf - pointer to caller's data buffer to write (NULL is illegal) * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_writeItemApi(NVINTF_itemID_t id, uint16_t len, void * pBuf) { uint8_t err; NVOCMP_itemHdr_t iHdr; // Parameter Sanity Check if (pBuf == NULL || len == 0) { return (NVINTF_BADPARAM); } err = NVOCMP_checkItem(&id, len, &iHdr, NVOCMP_FINDSTRICT); if (err) { return (err); } // Check voltage if possible NVOCMP_FLASHACCESS(err) if (err) { return (err); } // Prevent RTOS thread contention NVOCMP_LOCK(); // Create a new item err = NVOCMP_addItem(&NVOCMP_nvHandle, &iHdr, pBuf, NVOCMP_WRITE); if ((err == NVINTF_SUCCESS) && (iHdr.hofs > 0)) { // Mark old item as inactive NVOCMP_setItemInactive(&NVOCMP_nvHandle, iHdr.hpage, iHdr.hofs); err = NVOCMP_failW; } #ifdef NV_LINUX if (err == NVINTF_SUCCESS) { NV_LINUX_save(); } #endif NVOCMP_UNLOCK(err); } //***************************************************************************** // Extended API Functions //***************************************************************************** /** * @fn NVOCMP_lockNvApi * * @brief Global function to lock the NV priority gate mutex * * @return Key value needed to unlock the gate */ static int32_t NVOCMP_lockNvApi(void) { #ifdef NVOCMP_POSIX_MUTEX return (pthread_mutex_lock(&NVOCMP_gPosixMutex)); #elif defined(NVOCMP_POSIX_SEM) return (sem_wait(&NVOCMP_gPosixSem)); #else return (GateMutexPri_enter(NVOCMP_gMutexPri)); #endif } /** * @fn NVOCMP_unlockNvApi * * @brief Global function to unlock the NV priority gate mutex * * @return none */ static void NVOCMP_unlockNvApi(int32_t key) { #ifdef NVOCMP_POSIX_MUTEX (void) key; pthread_mutex_unlock(&NVOCMP_gPosixMutex); #elif defined(NVOCMP_POSIX_SEM) (void) key; sem_post(&NVOCMP_gPosixSem); #else GateMutexPri_leave(NVOCMP_gMutexPri, key); #endif } /****************************************************************************** * @fn NVOCMP_doNextApi * * @brief API function which allows operations on batches of NV items. This * function provides a faster way of finding, reading, or deleting multiple * NV items. However, the user must first lock access to NV with lockNV() to * ensure consistent results. The user must take care to minimize the time NV * is locked if NV access is shared. User must also remember to unlock NV when * done with unlockNV(). * * Usage Details: * doNext is controlled through the nvProxy item pointed to by prx * User will set flag bit NVINTF_DOSTART and then other flags based on the * desired operation. For example to find all items in system NVINTF_SYS_BLE, * the user would set flag bit NVINTF_DOFIND and set prx->sysid = NVINTF_SYS_BLE. * Then every call to doNextApi() returns with a status code and with the proxy * item populated with the found item if there was one. NVINTF_SUCCESS is * returned on a successful item operation, NVINTF_NOTFOUND is returned when a * matching item is not found, and other error codes can be returned. * Sample Code: * * // Use doNext to delete items of sysid * NVINTF_nvFuncts_t nvFps; * NVINTF_nvProxy_t nvProxy; * NVOCMP_loadApiPtrsExt(&nvFps); * nvFps.initNV(NULL); * nvProxy.sysid = sysid; * nvProxy.flag = NVINTF_DOSTART | NVINTF_DOSYSID | NVINTF_DODELETE; * * key = nvFps.lockNV(); * while(!status) * { * status |= nvFps.doNext(&nvProxy); * } * nvFps.unlockNV(key); * * Notes: * -User changes to the proxy struct will have no effect until a new search is * started by setting NVINTF_DOSTART * -On read operations, the user will supply a buffer and length into the proxy * -Items with system id NVINTF_SYSID_NVDRVR cannot be deleted with this API, * deleteItemApi must be used one an individual item basis * * @param prx - pointer to nvProxy item which contains user inputs * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_doNextApi(NVINTF_nvProxy_t * prx) { static enum { doFind, doRead, doDelete } op = doFind; NVOCMP_itemHdr_t hdr; static uint8_t search; static uint8_t sPage; static int16_t fOfs = FLASH_PAGE_SIZE; static uint16_t bufLen = 0; uint8_t status = NVINTF_SUCCESS; int16_t iOfs = NVOCMP_nvHandle.actOffset; static int16_t initialFindiofs = -1; static uint8_t initialsPage = 0xFF; // Sanitize inputs if (NULL == prx) { return (NVINTF_BADPARAM); } else if (0 == prx->flag) { return (NVINTF_BADPARAM); } // Locks NV NVOCMP_LOCK(); // New search if start flag set if (prx->flag & NVINTF_DOSTART) { // Remove start flag prx->flag &= ~NVINTF_DOSTART; // Start at latest item sPage = NVOCMP_nvHandle.actPage; fOfs = NVOCMP_nvHandle.actOffset; initialFindiofs = -1; initialsPage = 0xFF; // Read in buffer len bufLen = prx->len; // Decode flag if (prx->flag & NVINTF_DOSYSID) { search = NVOCMP_FINDSYSID; } else if (prx->flag & NVINTF_DOITMID) { search = NVOCMP_FINDITMID; } else if (prx->flag & NVINTF_DOANYID) { search = NVOCMP_FINDANY; } if (prx->flag & NVINTF_DOFIND) { op = doFind; } else if (prx->flag & NVINTF_DOREAD) { op = doRead; } else if (prx->flag & NVINTF_DODELETE) { op = doDelete; } } hdr.sysid = prx->sysid; hdr.itemid = prx->itemid; hdr.subid = prx->subid; // Look for item if (!NVOCMP_findItem(&NVOCMP_nvHandle, sPage, fOfs, &hdr, search, NULL)) { iOfs = hdr.hofs; // store its attributes prx->sysid = hdr.sysid; prx->itemid = hdr.itemid; prx->subid = hdr.subid; prx->len = hdr.len; if (prx->flag & NVINTF_DONOWRAP) { if ((initialFindiofs == hdr.hofs) && (initialsPage == hdr.hpage)) { status = NVINTF_NOTFOUND; } if ((initialFindiofs == -1) && (initialsPage == 0xFF)) { initialFindiofs = hdr.hofs; initialsPage = hdr.hpage; } } // start from this item on next findItem() fOfs = iOfs - hdr.len; sPage = hdr.hpage; if (status == NVINTF_SUCCESS) { // Do operation based on flag switch (op) { case doFind: // nothing, we already stored its info break; case doRead: // read item into user supplied buffer if (prx->buffer != NULL && hdr.len <= bufLen) { status = NVOCMP_readItem(&hdr, 0, hdr.len, prx->buffer, false); } break; case doDelete: if (prx->sysid != NVINTF_SYSID_NVDRVR) { NVOCMP_setItemInactive(&NVOCMP_nvHandle, hdr.hpage, iOfs); } break; default: NVOCMP_ALERT(false, "doNext flag is invalid.") status = NVINTF_BADPARAM; } } } else { // No more items match, done. status = NVINTF_NOTFOUND; } // Unlocks NV NVOCMP_UNLOCK(status); } /****************************************************************************** * @fn NVOCMP_expectCompApi * * @brief API function to check if compaction will happen if a data with size = len is written * * @param len - data buffer length to write into NV block * * @return true or false */ static bool NVOCMP_expectCompApi(uint16_t len) { uint8_t dstPg; uint16_t iLen; bool compact = false; if (len) { iLen = NVOCMP_ITEMHDRLEN + len; dstPg = NVOCMP_getDstPage(&NVOCMP_nvHandle, iLen); if (dstPg == NVOCMP_NULLPAGE) { compact = true; } } return (compact); } //***************************************************************************** // Local NV Driver Utility Functions //***************************************************************************** #ifdef NVOCMP_GPRAM /****************************************************************************** * @fn NVOCMP_disableCache * * @brief Local function to disable cache * * @param vm - pointer to mode storage * * @return none */ static void NVOCMP_disableCache(uint32_t * vm) { // Save current cache mode *vm = VIMSModeGet(VIMS_BASE) & VIMS_STAT_MODE_M; // Disable the cache VIMSModeSet(VIMS_BASE, VIMS_MODE_DISABLED); // Wait until it is while (VIMSModeGet(VIMS_BASE) != VIMS_MODE_DISABLED) ; } /****************************************************************************** * @fn NVOCMP_restoreCache * * @brief Local function to disable cache * * @param vm - mode * * @return none */ static void NVOCMP_restoreCache(uint32_t vm) { // Restore cache to previous state VIMSModeSet(VIMS_BASE, vm); } #endif #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_findPage * * @brief Local function to find page with specified state * * @param state - page state to find * * @return page number or NVOCMP_NULLPAGE */ static uint8_t NVOCMP_findPage(NVOCMP_pageState_t state) { uint8_t pg; NVOCMP_pageHdr_t pageHdr; for (pg = 0; pg < NVOCMP_NVSIZE; pg++) { // Get page header NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); if (pageHdr.state == state) { return (pg); } } return (NVOCMP_NULLPAGE); } #endif #if (NVOCMP_NVPAGES > NVOCMP_NVONEP) #if !defined(NVOCMP_MIGRATE_DISABLED) /****************************************************************************** * @fn NVOCMP_migratePage * * @brief Local function to find page with specified state * * @param pNvHandle - pointer to NV handle * @param page - page to convert * * @return none */ static void NVOCMP_migratePage(NVOCMP_nvHandle_t * pNvHandle, uint8_t page) { uint8_t dstPg; uint8_t tmp; uint16_t offset1; uint16_t offset2; NVOCMP_itemHdr_t iHdr; offset1 = pNvHandle->pageInfo[page].offset; offset2 = NVOCTP_PGDATAOFS; if (offset1 - NVOCTP_PGDATAOFS > FLASH_PAGE_SIZE - NVOCMP_PGDATAOFS) { offset2 = offset1; while (offset2 - NVOCTP_PGDATAOFS > FLASH_PAGE_SIZE - NVOCMP_PGDATAOFS) { NVOCMP_findItem(pNvHandle, page, offset2, &iHdr, NVOCMP_FINDANY, NULL); offset2 = iHdr.hofs - iHdr.len; } } if ((pNvHandle->nvSize <= NVOCMP_NVTWOP) && (offset2 != NVOCTP_PGDATAOFS)) { offset1 = offset2; offset2 = NVOCTP_PGDATAOFS; } dstPg = page; // copy from offset2 to top if (offset2 - NVOCTP_PGDATAOFS > 0) { dstPg = NVOCMP_INCPAGE(dstPg); NVOCMP_copyItem(page, dstPg, NVOCTP_PGDATAOFS, NVOCMP_PGDATAOFS, offset2 - NVOCTP_PGDATAOFS); NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGFULL); pNvHandle->pageInfo[dstPg].allActive = NVOCMP_SOMEINACTIVE; pNvHandle->pageInfo[dstPg].offset = NVOCMP_PGDATAOFS + offset2 - NVOCTP_PGDATAOFS; pNvHandle->actPage = dstPg; pNvHandle->actOffset = pNvHandle->pageInfo[dstPg].offset; tmp = NVOCMP_readByte(dstPg, NVOCMP_PGHDRVER); tmp &= ~NVOCMP_ALLACTIVE; NVOCMP_writeByte(dstPg, NVOCMP_PGHDRVER, tmp); } // copy from offset 1 to offset2 if (offset1 - offset2 > 0) { dstPg = NVOCMP_INCPAGE(dstPg); NVOCMP_copyItem(page, dstPg, offset2, NVOCMP_PGDATAOFS, offset1 - offset2); NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGFULL); pNvHandle->pageInfo[dstPg].allActive = NVOCMP_SOMEINACTIVE; pNvHandle->pageInfo[dstPg].offset = NVOCMP_PGDATAOFS + offset1 - offset2; pNvHandle->actPage = dstPg; pNvHandle->actOffset = pNvHandle->pageInfo[dstPg].offset; tmp = NVOCMP_readByte(dstPg, NVOCMP_PGHDRVER); tmp &= ~NVOCMP_ALLACTIVE; NVOCMP_writeByte(dstPg, NVOCMP_PGHDRVER, tmp); } NVOCMP_failW |= NVOCMP_erase(pNvHandle, page); pNvHandle->tailPage = page; pNvHandle->headPage = NVOCMP_INCPAGE(page); NVOCMP_changePageState(pNvHandle, page, NVOCMP_PGXDST); } #endif #endif #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_scanPage * * @brief Local function to scan page to get page information * * @param pNvHandle - pointer to NV handle * @param pg - page to scan * @param pPageInfo - page info pointer * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_scanPage(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageInfo_t * pPageInfo) { uint32_t pageHdr; NVOCMP_compactHdr_t thisHdr; NVOCMP_compactHdr_t startHdr; NVOCMP_compactHdr_t endHdr; NVOCMP_pageHdr_t * pHdr = (NVOCMP_pageHdr_t *) &pageHdr; // Get page header NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) pHdr, NVOCMP_PGHDRLEN); #if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) uint8_t version = (pHdr->version << 2) | (pHdr->allActive); if ((pHdr->signature == NVOCTP_SIGNATURE) && (version == NVOCTP_VERSION)) { pPageInfo->state = pHdr->state; pPageInfo->cycle = pHdr->cycle; pPageInfo->allActive = NVOCMP_SOMEINACTIVE; pPageInfo->mode = NVOCMP_PGNORMAL; pPageInfo->offset = NVOCMP_findOffset(pg, FLASH_PAGE_SIZE); return (NVINTF_SUCCESS); } #endif NVOCMP_getCompactHdr(pg, THISPAGEHDR, &thisHdr); NVOCMP_getCompactHdr(pg, XSRCSTARTHDR, &startHdr); NVOCMP_getCompactHdr(pg, XSRCENDHDR, &endHdr); uint8_t corruptFlag = ((pHdr->state != NVOCMP_PGNACT) && (pHdr->state != NVOCMP_PGXDST) && (pHdr->state != NVOCMP_PGRDY) && (pHdr->state != NVOCMP_PGACT) && (pHdr->state != NVOCMP_PGFULL) && (pHdr->state != NVOCMP_PGXSRC)); if (corruptFlag || (pHdr->version != NVOCMP_VERSION) || (pHdr->signature != NVOCMP_SIGNATURE)) { // NV page and NV driver versions are different NVOCMP_ALERT(false, "Corrupted or Version/Signature mismatch.") NVOCMP_EXCEPTION(pg, NVINTF_BADVERSION); NVOCMP_failW = NVOCMP_erase(pNvHandle, pg); if (NVOCMP_failW == NVINTF_SUCCESS) { // Get page header NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) pHdr, NVOCMP_PGHDRLEN); NVOCMP_getCompactHdr(pg, THISPAGEHDR, &thisHdr); NVOCMP_getCompactHdr(pg, XSRCSTARTHDR, &startHdr); NVOCMP_getCompactHdr(pg, XSRCENDHDR, &endHdr); } else if (NVOCMP_failW == NVINTF_LOWPOWER) { return (NVINTF_LOWPOWER); } else { return (NVINTF_FAILURE); } } pPageInfo->state = pHdr->state; pPageInfo->cycle = pHdr->cycle; pPageInfo->allActive = pHdr->allActive; pPageInfo->mode = thisHdr.page; if ((pPageInfo->state == NVOCMP_PGNACT) || (pPageInfo->state == NVOCMP_PGXDST)) { pPageInfo->offset = NVOCMP_PGDATAOFS; } else if (thisHdr.pageOffset != NVOCMP_NULLOFFSET) { pPageInfo->offset = thisHdr.pageOffset; } else { pPageInfo->offset = NVOCMP_findOffset(pg, FLASH_PAGE_SIZE); } pPageInfo->sPage = startHdr.page; pPageInfo->sOffset = startHdr.pageOffset; pPageInfo->ePage = endHdr.page; pPageInfo->eOffset = endHdr.pageOffset; return (NVINTF_SUCCESS); } #else /****************************************************************************** * @fn NVOCMP_scanPage * * @brief Local function to scan page to get page information * * @param pNvHandle - pointer to NV handle * @param pg - page to scan * @param pPageInfo - page info pointer * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_scanPage(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageInfo_t * pPageInfo) { uint32_t pageHdr; NVOCMP_pageHdr_t * pHdr = (NVOCMP_pageHdr_t *) &pageHdr; // Get page header NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) pHdr, NVOCMP_PGHDRLEN); pPageInfo->state = pHdr->state; pPageInfo->cycle = pHdr->cycle; #if ((NVOCMP_NVPAGES > NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) uint8_t version = (pHdr->version << 2) | (pHdr->allActive); if ((pHdr->signature == NVOCTP_SIGNATURE) && (version == NVOCTP_VERSION)) { pPageInfo->allActive = NVOCMP_SOMEINACTIVE; pPageInfo->mode = NVOCMP_PGNORMAL; pPageInfo->offset = NVOCMP_findOffset(pg, FLASH_PAGE_SIZE); } #endif if ((pHdr->version == NVOCMP_VERSION) || (pHdr->signature == NVOCMP_SIGNATURE)) { pPageInfo->allActive = pHdr->allActive; if ((pPageInfo->state == NVOCMP_PGNACT) || (pPageInfo->state == NVOCMP_PGXDST) || (pPageInfo->state == NVOCMP_PGRDY)) { pPageInfo->offset = NVOCMP_PGDATAOFS; } else { pPageInfo->offset = NVOCMP_findOffset(pg, FLASH_PAGE_SIZE); } } return (NVINTF_SUCCESS); } #endif #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_findDstPage * * @brief Local function to find dst page after power loss * * @param pNvHandle - pointer to NV handle * * @return page */ static uint8_t NVOCMP_findDstPage(NVOCMP_nvHandle_t * pNvHandle) { uint8_t pg; for (pg = 0; pg < NVOCMP_NVSIZE; pg++) { if (pNvHandle->pageInfo[pg].mode == NVOCMP_PGCDST) { return (pg); } } return (NVOCMP_NVSIZE); } #endif #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_initNv * * @brief Local function to init whole NV area * * @param pNvHandle - pointer to NV handle * * @return none */ static void NVOCMP_initNv(NVOCMP_nvHandle_t * pNvHandle) { uint8_t status; uint8_t pg; uint8_t tmpPg; NVOCMP_initAction_t action = NVOCMP_NORMAL_INIT; NVOCMP_pageInfo_t * pPageInfo; uint8_t noPgNact = 0; uint8_t noPgXdst = 0; uint8_t noPgRdy = 0; uint8_t noPgAct = 0; uint8_t noPgFull = 0; uint8_t noPgXsrc = 0; uint8_t noPgNdef = 0; uint16_t pgXdst = NVOCMP_NULLPAGE; uint16_t pgRdy = NVOCMP_NULLPAGE; uint16_t pgAct = NVOCMP_NULLPAGE; uint16_t pgNact = NVOCMP_NULLPAGE; #if !defined(NVOCMP_MIGRATE_DISABLED) uint8_t noPgLeg = 0; uint16_t pgLegAct = NVOCMP_NULLPAGE; uint16_t pgLegXsrc = NVOCMP_NULLPAGE; #endif uint16_t cleanPages = 0; // Scan Pages for (pg = 0; pg < NVOCMP_NVSIZE; pg++) { pPageInfo = &pNvHandle->pageInfo[pg]; status = NVOCMP_scanPage(pNvHandle, pg, pPageInfo); if (status != NVINTF_SUCCESS) { return; } if (pPageInfo->state == NVOCMP_PGNACT) { noPgNact++; pgNact = pg; } else if (pPageInfo->state == NVOCMP_PGXDST) { pgXdst = pg; noPgXdst++; } else if (pPageInfo->state == NVOCMP_PGRDY) { pgRdy = pg; noPgRdy++; } else if (pPageInfo->state == NVOCMP_PGACT) { pgAct = pg; noPgAct++; } else if (pPageInfo->state == NVOCMP_PGFULL) { noPgFull++; } else if (pPageInfo->state == NVOCMP_PGXSRC) { noPgXsrc++; } #if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) else if (pPageInfo->state == NVOCTP_PGACTIVE) { pgLegAct = pg; noPgLeg++; } else if (pPageInfo->state == NVOCTP_PGXFER) { pgLegXsrc = pg; noPgLeg++; } #endif else { noPgNdef++; } } // Decide Action based on Page Informations #if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) if (noPgLeg > 0) { action = NVOCMP_NORMAL_MIGRATE; } else #endif if ((noPgNdef > 0) || (noPgXdst > 1) || (noPgXsrc > 1) || (noPgRdy > 1)) { // This should not happen NVOCMP_ASSERT(false, "Something wrong serious"); action = NVOCMP_FORCE_CLEAN; } else { if (noPgNact == NVOCMP_NVSIZE) { action = NVOCMP_NORMAL_INIT; } else if (noPgXsrc) { // Power lost during compaction in progress if (noPgXdst) { action = NVOCMP_RECOVER_COMPACT; } // Corrupted due to power lost while writing onto XDST page and erased the XDST page else if (noPgNact) { pgXdst = pgNact; action = NVOCMP_RECOVER_COMPACT; } // Power lost after compaction done, but before erasing PGXSRC page else { if (NVOCMP_findDstPage(pNvHandle) < NVOCMP_NVSIZE) { action = NVOCMP_RECOVER_ERASE; } else { action = NVOCMP_ERROR_UNKNOWN; } } } else if (noPgXdst) { action = NVOCMP_NORMAL_RESUME; } else if (noPgAct || noPgFull) { if (NVOCMP_findDstPage(pNvHandle) < NVOCMP_NVSIZE) { action = NVOCMP_RECOVER_ERASE; } else if (noPgNact) { pgXdst = pgNact; NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); action = NVOCMP_NORMAL_RESUME; } else { action = NVOCMP_ERROR_UNKNOWN; } } else { // This case should be considered more NVOCMP_ASSERT(false, "Something wrong serious"); action = NVOCMP_FORCE_CLEAN; } } gAction = action; switch (action) { case NVOCMP_FORCE_CLEAN: // Erase All pages before start for (pg = 0; pg < NVOCMP_NVSIZE; pg++) { NVOCMP_failW |= NVOCMP_erase(pNvHandle, pg); } // init should be followed by force clean case NVOCMP_NORMAL_INIT: // initial state, set head page, act page and tail page pNvHandle->headPage = 0; pNvHandle->tailPage = NVOCMP_NVSIZE - 1; pNvHandle->actPage = 0; pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; NVOCMP_changePageState(pNvHandle, pNvHandle->headPage, NVOCMP_PGRDY); NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); break; case NVOCMP_NORMAL_RESUME: // resume state, set head page, act page and tail page pNvHandle->tailPage = pgXdst; pNvHandle->headPage = NVOCMP_INCPAGE(pgXdst); if (pgAct != NVOCMP_NULLPAGE) { pNvHandle->actPage = pgAct; pNvHandle->actOffset = pNvHandle->pageInfo[pgAct].offset; NVOCMP_itemHdr_t iHdr; int8_t status; if (pNvHandle->actOffset > NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN) { NVOCMP_readHeader(pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN, &iHdr, false); if (iHdr.stats & NVOCMP_FOLLOWBIT) { status = NVOCMP_findItem(pNvHandle, pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN - iHdr.len, &iHdr, NVOCMP_FINDSTRICT, NULL); if ((status == NVINTF_SUCCESS) && (iHdr.hofs > 0)) { NVOCMP_setItemInactive(pNvHandle, iHdr.hpage, iHdr.hofs); } } else { NVOCMP_compactPage(pNvHandle, 0); } } } else if (pgRdy != NVOCMP_NULLPAGE) { pNvHandle->actPage = pgRdy; pNvHandle->actOffset = pNvHandle->pageInfo[pgRdy].offset; } else { pNvHandle->actPage = pNvHandle->headPage; pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; } #ifdef NVOCMP_COMPACT_WHEN_RESUME NVOCMP_compactPage(pNvHandle, 0); #endif break; case NVOCMP_RECOVER_COMPACT: pNvHandle->tailPage = pgXdst; pNvHandle->headPage = NVOCMP_INCPAGE(pgXdst); NVOCMP_failW = NVOCMP_erase(pNvHandle, pgXdst); NVOCMP_changePageState(pNvHandle, pgXdst, NVOCMP_PGXDST); pNvHandle->forceCompact = 1; NVOCMP_compactPage(pNvHandle, 0); break; case NVOCMP_RECOVER_ERASE: pg = NVOCMP_findDstPage(pNvHandle); pNvHandle->compactInfo.xDstPage = pg; pNvHandle->compactInfo.xSrcSPage = pNvHandle->pageInfo[pg].sPage; pNvHandle->compactInfo.xSrcEPage = pNvHandle->pageInfo[pg].ePage; cleanPages = NVOCMP_cleanPage(pNvHandle); pNvHandle->tailPage = NVOCMP_ADDPAGE(pg, cleanPages); pNvHandle->headPage = NVOCMP_INCPAGE(pNvHandle->tailPage); tmpPg = NVOCMP_findPage(NVOCMP_PGACT); if (tmpPg == NVOCMP_NULLPAGE) { tmpPg = NVOCMP_findPage(NVOCMP_PGRDY); if (tmpPg == NVOCMP_NULLPAGE) { tmpPg = NVOCMP_findPage(NVOCMP_PGNACT); if (tmpPg == NVOCMP_NULLPAGE) { tmpPg = NVOCMP_findPage(NVOCMP_PGFULL); if (tmpPg == NVOCMP_NULLPAGE) { tmpPg = pNvHandle->headPage; } } } } pNvHandle->actPage = tmpPg; pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); break; #if !defined(NVOCMP_MIGRATE_DISABLED) case NVOCMP_NORMAL_MIGRATE: if (pgLegAct != NVOCMP_NULLPAGE) { pNvHandle->actPage = pgLegAct; } else { pNvHandle->actPage = pgLegXsrc; } pNvHandle->headPage = pNvHandle->actPage; pNvHandle->tailPage = NVOCMP_DECPAGE(pNvHandle->headPage); pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; NVOCMP_migratePage(pNvHandle, pNvHandle->actPage); NVOCMP_compactPage(pNvHandle, 0); break; #endif case NVOCMP_ERROR_UNKNOWN: /* When this error happens, NV area should be erased to restart. * This while loop is for only debug purpose */ NVOCMP_ASSERT1(0); default: break; } } #endif #if (NVOCMP_NVPAGES == NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_initNv * * @brief Local function to init whole NV area * * @param pNvHandle - pointer to NV handle * * @return none */ static void NVOCMP_initNv(NVOCMP_nvHandle_t * pNvHandle) { uint8_t status, prevactPage; NVOCMP_itemHdr_t iHdr; uint8_t pg; NVOCMP_initAction_t action; NVOCMP_pageInfo_t * pPageInfo; uint16_t pgAct; #if !defined(NVOCMP_MIGRATE_DISABLED) uint8_t noPgLeg = 0; #endif bool compact = false, compaction_occurred = false; // Scan Pages pNvHandle->xsrcPage = NVOCMP_NULLPAGE; pNvHandle->tailPage = NVOCMP_NULLPAGE; for (pg = 0; pg < NVOCMP_NVSIZE; pg++) { pPageInfo = &pNvHandle->pageInfo[pg]; NVOCMP_scanPage(pNvHandle, pg, pPageInfo); #if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) if (pPageInfo->state == NVOCTP_PGACTIVE) { pNvHandle->actPage = pg; noPgLeg++; } else if (pPageInfo->state == NVOCTP_PGXFER) { pNvHandle->xsrcPage = pg; noPgLeg++; } else #endif if ((pPageInfo->state == NVOCMP_PGACT) || (pPageInfo->state == NVOCMP_PGFULL)) { pNvHandle->actPage = pg; } else if (pPageInfo->state == NVOCMP_PGXSRC) { pNvHandle->xsrcPage = pg; } else if (pPageInfo->state == NVOCMP_PGXDST) { pNvHandle->tailPage = pg; } } action = NVOCMP_NORMAL_INIT; // Decide Action based on Page Informations #if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) if (noPgLeg > 0) { action = NVOCMP_NORMAL_MIGRATE; } else { #endif if (pNvHandle->actPage != NVOCMP_NULLPAGE) { action = NVOCMP_NORMAL_RESUME; } else if (pNvHandle->xsrcPage != NVOCMP_NULLPAGE) { pNvHandle->actPage = pNvHandle->xsrcPage; action = NVOCMP_RECOVER_COMPACT; } else if (pNvHandle->tailPage != NVOCMP_NULLPAGE) { pgAct = NVOCMP_INCPAGE(pNvHandle->tailPage); if (pNvHandle->pageInfo[pgAct].offset) { pNvHandle->actPage = pgAct; action = NVOCMP_RECOVER_COMPACT; } else { pNvHandle->tailPage = NVOCMP_NULLPAGE; } } if (pNvHandle->actPage == NVOCMP_NULLPAGE) { pNvHandle->actPage = 0; NVOCMP_failW |= NVOCMP_erase(pNvHandle, 0); NVOCMP_changePageState(pNvHandle, 0, NVOCMP_PGRDY); } if (pNvHandle->tailPage == NVOCMP_NULLPAGE) { pNvHandle->tailPage = NVOCMP_INCPAGE(pNvHandle->actPage); ; NVOCMP_failW |= NVOCMP_erase(pNvHandle, pNvHandle->tailPage); NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); } pNvHandle->headPage = pNvHandle->actPage; pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; #if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) } #endif gAction = action; switch (action) { case NVOCMP_NORMAL_RESUME: // resume state, set head page, act page and tail page do { compaction_occurred = false; if (pNvHandle->actOffset > NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN) { NVOCMP_readHeader(pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN, &iHdr, false); if (iHdr.stats & NVOCMP_FOLLOWBIT) { /* Cache current active page value before search starts */ prevactPage = pNvHandle->actPage; status = NVOCMP_findItem(pNvHandle, pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN - iHdr.len, &iHdr, NVOCMP_FINDSTRICT, NULL); /* If the current active page is different than previous, then * compaction occurred and the search for duplicate must occur * again. * If item is found and compaction did not occur, it is a true * duplicate, so the item can be deleted. */ compaction_occurred = (prevactPage != pNvHandle->actPage); if ((status == NVINTF_SUCCESS) && (iHdr.hofs > 0) && !compaction_occurred) { NVOCMP_setItemInactive(pNvHandle, iHdr.hpage, iHdr.hofs); } } else { pNvHandle->forceCompact = 1; compact = true; } } } while (compaction_occurred); /* Repeat until compaction does not occur while searching */ #ifdef NVOCMP_COMPACT_WHEN_RESUME compact = true; #endif break; #if !defined(NVOCMP_MIGRATE_DISABLED) case NVOCMP_NORMAL_MIGRATE: if (pNvHandle->actPage == NVOCMP_NULLPAGE) { pNvHandle->actPage = pNvHandle->xsrcPage; } pNvHandle->headPage = pNvHandle->actPage; pNvHandle->tailPage = NVOCMP_DECPAGE(pNvHandle->headPage); pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; NVOCMP_failW = NVOCMP_erase(pNvHandle, pNvHandle->tailPage); NVOCMP_migratePage(pNvHandle, pNvHandle->actPage); compact = true; break; #endif case NVOCMP_RECOVER_COMPACT: pNvHandle->forceCompact = 1; compact = true; break; default: break; } if (compact) { NVOCMP_compactPage(pNvHandle, 0); } } #endif #if (NVOCMP_NVPAGES == NVOCMP_NVONEP) /****************************************************************************** * @fn NVOCMP_initNv * * @brief Local function to init whole NV area * * @param pNvHandle - pointer to NV handle * * @return none */ static void NVOCMP_initNv(NVOCMP_nvHandle_t * pNvHandle) { uint8_t status; NVOCMP_itemHdr_t iHdr; NVOCMP_initAction_t action; NVOCMP_pageInfo_t * pPageInfo; bool compact = false; // Scan Pages pNvHandle->xsrcPage = NVOCMP_NULLPAGE; pNvHandle->tailPage = NVOCMP_NULLPAGE; pPageInfo = &pNvHandle->pageInfo[0]; NVOCMP_scanPage(pNvHandle, 0, pPageInfo); if ((pPageInfo->state == NVOCMP_PGACT) || (pPageInfo->state == NVOCMP_PGFULL)) { pNvHandle->actPage = 0; } else if (pPageInfo->state == NVOCMP_PGXSRC) { pNvHandle->xsrcPage = 0; } action = NVOCMP_NORMAL_INIT; // Decide Action based on Page Informations if (pNvHandle->actPage != NVOCMP_NULLPAGE) { action = NVOCMP_NORMAL_RESUME; } else if (pNvHandle->xsrcPage != NVOCMP_NULLPAGE) { pNvHandle->actPage = 0; action = NVOCMP_RECOVER_COMPACT; } if (pNvHandle->actPage == NVOCMP_NULLPAGE) { pNvHandle->actPage = 0; NVOCMP_failW |= NVOCMP_erase(pNvHandle, 0); NVOCMP_changePageState(pNvHandle, 0, NVOCMP_PGRDY); } pNvHandle->tailPage = 0; pNvHandle->headPage = 0; pNvHandle->actOffset = pNvHandle->pageInfo[0].offset; gAction = action; switch (action) { case NVOCMP_NORMAL_RESUME: // resume state, set head page, act page and tail page if (pNvHandle->actOffset > NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN) { NVOCMP_readHeader(pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN, &iHdr, false); if (iHdr.stats & NVOCMP_FOLLOWBIT) { status = NVOCMP_findItem(pNvHandle, pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN - iHdr.len, &iHdr, NVOCMP_FINDSTRICT, NULL); if ((status == NVINTF_SUCCESS) && (iHdr.hofs > 0)) { NVOCMP_setItemInactive(pNvHandle, iHdr.hpage, iHdr.hofs); } } else { pNvHandle->forceCompact = 1; compact = true; } } #ifdef NVOCMP_COMPACT_WHEN_RESUME compact = true; #endif break; case NVOCMP_RECOVER_COMPACT: pNvHandle->forceCompact = 1; compact = true; break; default: break; } if (compact) { NVOCMP_compactPage(pNvHandle, 0); } } #endif /****************************************************************************** * @fn NVOCMP_checkItem * * @brief Local function to check parameters and locate existing item * * @param id - NV item type identifier * @param len - NV item data length * @param pHdr - pointer to header buffer * @param flag - flag for item search * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_checkItem(NVINTF_itemID_t * id, uint16_t len, NVOCMP_itemHdr_t * pHdr, uint8_t flag) { if (len > NVOCMP_MAXLEN) { // Item data is too long NVOCMP_ALERT(false, "Item data too large.") return (NVINTF_BADLENGTH); } if (id->systemID > NVOCMP_MAXSYSID) { // Too large for compressed header NVOCMP_ALERT(false, "Item sysid too large.") return (NVINTF_BADSYSID); } if (id->itemID > NVOCMP_MAXITEMID) { // Too large for compressed header NVOCMP_ALERT(false, "Item itemid too large.") return (NVINTF_BADITEMID); } if (id->subID > NVOCMP_MAXSUBID) { // Too large for compressed header NVOCMP_ALERT(false, "Item subid too large.") return (NVINTF_BADSUBID); } if (NVOCMP_failF == NVINTF_NOTREADY) { // NV driver has not been initialized NVOCMP_ASSERT(false, "Driver uninitialized.") return (NVINTF_NOTREADY); } pHdr->len = len; pHdr->hofs = 0; pHdr->cmpid = NVOCMP_CMPRID(id->systemID, id->itemID, id->subID); pHdr->subid = id->subID; pHdr->itemid = id->itemID; pHdr->sysid = id->systemID; pHdr->sig = NVOCMP_SIGNATURE; return (NVINTF_SUCCESS); } /****************************************************************************** * @fn NVOCMP_getDstPage * * @brief Local function to find the page where an item will be written on * * @param pNvHandle - pointer to NV handle * @param len - item size to write * * @return page number or NVOCMP_NULLPAGE */ static uint8_t NVOCMP_getDstPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t len) { uint8_t dstPg = NVOCMP_NULLPAGE; uint8_t pg = pNvHandle->actPage; NVOCMP_pageHdr_t pageHdr; #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) uint16_t nvSearched = 0; for (pg = pNvHandle->actPage; nvSearched < NVOCMP_NVSIZE; pg = NVOCMP_INCPAGE(pg)) { nvSearched++; if (pg == pNvHandle->tailPage) { continue; } #endif NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); if (pageHdr.state == NVOCMP_PGFULL) { #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) continue; #else return (dstPg); #endif } if ((pageHdr.state == NVOCMP_PGNACT) || (pageHdr.state == NVOCMP_PGRDY)) { NVOCMP_changePageState(pNvHandle, pg, NVOCMP_PGACT); } pNvHandle->actPage = pg; pNvHandle->actOffset = pNvHandle->pageInfo[pg].offset; if ((pNvHandle->pageInfo[pg].offset + len) <= FLASH_PAGE_SIZE) { dstPg = pNvHandle->actPage; #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) break; #endif } else { NVOCMP_changePageState(pNvHandle, pg, NVOCMP_PGFULL); } #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) } #endif return (dstPg); } /****************************************************************************** * @fn NVOCMP_addItem * * @brief Local function to check for adequate space and create a new item * * @param pNvHandle - pointer to NV handle * @param iHdr - pointer to header buffer * @param pBuf - pointer to item * @param wm - write mode * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_addItem(NVOCMP_nvHandle_t * pNvHandle, NVOCMP_itemHdr_t * iHdr, uint8_t * pBuf, NVOCMP_writeMode_t wm) { uint8_t err; uint8_t dstPg; uint16_t iLen; bool compact = false; NVOCMP_itemHdr_t hdr; iLen = NVOCMP_ITEMHDRLEN + iHdr->len; dstPg = NVOCMP_getDstPage(pNvHandle, iLen); if (dstPg == NVOCMP_NULLPAGE) { compact = true; // Won't fit on the active page, compact and check again if (NVOCMP_compactPage(pNvHandle, iLen) < iLen) { // Failure means there's no place to put this item NVOCMP_ALERT(false, "Out of NV.") err = (NVOCMP_failW != NVINTF_SUCCESS) ? NVOCMP_failW : NVINTF_BADLENGTH; return (err); } } if ((NVOCMP_failW == NVINTF_SUCCESS) && ((wm == NVOCMP_WRITE) || ((wm == NVOCMP_UPDATE) && (compact == true)))) { /* get item offset after compaction */ hdr.sysid = iHdr->sysid; hdr.itemid = iHdr->itemid; hdr.subid = iHdr->subid; (void) NVOCMP_findItem(pNvHandle, pNvHandle->actPage, pNvHandle->actOffset, &hdr, NVOCMP_FINDSTRICT, NULL); iHdr->hpage = hdr.hpage; iHdr->hofs = hdr.hofs; } #if NVOCMP_NWSAMEITEM bool changed = false; if ((iHdr->hofs) && (iHdr->len)) { #define NVOCMP_COMPARE_SIZE 32 uint8_t readBuf[NVOCMP_COMPARE_SIZE]; uint16_t iOfs = (iHdr->hofs - iHdr->len); uint16_t dOfs = 0; uint16_t len2cmp = 0; // Failure to parse the command NVOCMP_ALERT(iHdr->hofs >= iHdr->len, "Something wrong in parsing.") do { len2cmp = (iHdr->len - dOfs) > NVOCMP_COMPARE_SIZE ? NVOCMP_COMPARE_SIZE : (iHdr->len - dOfs); NVOCMP_read(iHdr->hpage, iOfs + dOfs, readBuf, len2cmp); if (memcmp(readBuf, pBuf + dOfs, len2cmp)) { changed = true; break; } dOfs += len2cmp; } while (dOfs < iHdr->len); } else { changed = true; } if (changed) { // Create the new NV item NVOCMP_writeItem(pNvHandle, iHdr, pNvHandle->actPage, pNvHandle->actOffset, pBuf); } else { iHdr->hofs = 0; } #else // Create the new NV item NVOCMP_writeItem(pNvHandle, iHdr, pNvHandle->actPage, pNvHandle->actOffset, pBuf); #endif // Status of writing/erasing Flash return (NVOCMP_failW); } /****************************************************************************** * @fn NVOCMP_read * * @brief Writes to a flash buffer from RAM * * @param pg - Flash page to write from * @param off - Offset in destination page to read from * @param pBuf - Pointer to write the results into * @param len - Number of bytes to write into * * @return NVS_STATUS_SUCCESS or other NVS status code */ static inline void NVOCMP_read(uint8_t pg, uint16_t off, uint8_t * pBuf, uint16_t len) { #ifndef NV_LINUX NVS_read(NVOCMP_nvsHandle, NVOCMP_FLASHOFFSET(pg, off), (uint8_t *) pBuf, len); #else NV_LINUX_read(pg, off, pBuf, len); #endif } /****************************************************************************** * @fn NVOCMP_write * * @brief Writes to a flash buffer from RAM * * @param dstPg - Flash page to write to * @param off - offset in destination page to write to * @param pBuf - Pointer to caller's buffer to write & verify * @param len - number of bytes to write from pBuf * * @return NVS_STATUS_SUCCESS or other NVS status code */ static uint8_t NVOCMP_write(uint8_t dstPg, uint16_t off, uint8_t * pBuf, uint16_t len) { uint8_t err = NVINTF_SUCCESS; int_fast16_t nvsRes = 0; // check voltage if possible NVOCMP_FLASHACCESS(err) if (NVINTF_SUCCESS == err) { #ifndef NV_LINUX nvsRes = NVS_write(NVOCMP_nvsHandle, NVOCMP_FLASHOFFSET(dstPg, off), pBuf, len, NVS_WRITE_POST_VERIFY); #else nvsRes = NV_LINUX_write(dstPg, off, pBuf, len); #endif } else { err = NVINTF_LOWPOWER; } if (nvsRes < 0) { err = NVINTF_FAILURE; } NVOCMP_ALERT(NVINTF_LOWPOWER != err, "Voltage check failed.") if (NVINTF_FAILURE == err) { NVOCMP_ALERT(NVINTF_FAILURE != err, "NVS write failure.") } return (err); } /****************************************************************************** * @fn NVOCMP_erase * * @brief Erases a flash page * * @param pNvHandle - pointer to NV handle * @param dstPg - Flash page to write to * * @return NVINT_SUCCESS or other NVINTF status code */ static uint8_t NVOCMP_erase(NVOCMP_nvHandle_t * pNvHandle, uint8_t dstPg) { uint8_t err = NVINTF_SUCCESS; int_fast16_t nvsRes = 0; // Check voltage if possible NVOCMP_FLASHACCESS(err) if (NVINTF_SUCCESS == err) { #ifndef NV_LINUX nvsRes = NVS_erase(NVOCMP_nvsHandle, NVOCMP_FLASHOFFSET(dstPg, 0), NVOCMP_nvsAttrs.sectorSize); #else nvsRes = NV_LINUX_erase(dstPg); #endif if (nvsRes < 0) { err = NVINTF_FAILURE; } else { // Bump the compaction cycle counter, wrap-around if at maximum pNvHandle->pageInfo[dstPg].cycle = (pNvHandle->pageInfo[dstPg].cycle < NVOCMP_MAXCYCLE) ? (pNvHandle->pageInfo[dstPg].cycle + 1) : NVOCMP_MINCYCLE; NVOCMP_setPageState(pNvHandle, dstPg, NVOCMP_PGNACT); NVOCMP_setCompactHdr(dstPg, NVOCMP_NULLPAGE, NVOCMP_NULLOFFSET, THISPAGEHDR); NVOCMP_setCompactHdr(dstPg, NVOCMP_NULLPAGE, NVOCMP_NULLOFFSET, XSRCSTARTHDR); NVOCMP_setCompactHdr(dstPg, NVOCMP_NULLPAGE, NVOCMP_NULLOFFSET, XSRCENDHDR); pNvHandle->pageInfo[dstPg].offset = NVOCMP_PGDATAOFS; pNvHandle->pageInfo[dstPg].mode = NVOCMP_PGNORMAL; } } else { err = NVINTF_LOWPOWER; } NVOCMP_ALERT(NVINTF_LOWPOWER != err, "Voltage check failed.") NVOCMP_ALERT(NVINTF_FAILURE != err, "NVS erase failure.") return (err); } /****************************************************************************** * @fn NVOCMP_writeItem * * @brief Write entire NV item to new location on active Flash page. * Each call to NVS_write() does a read-back to verify. If an * error is detected, the 'failW' flag is set to inhibit further * flash write attempts until the next NV transaction. * * @param pNvHandle - pointer to NV handle * @param pHdr - Pointer to caller's item header buffer * @param dstPg - Destination NV Flash page * @param dstOff - Destination offset * @param pBuf - Points to buffer which will be written to item * * @return none */ static void NVOCMP_writeItem(NVOCMP_nvHandle_t * pNvHandle, NVOCMP_itemHdr_t * pHdr, uint8_t dstPg, uint16_t dstOff, uint8_t * pBuf) { uint16_t iLen; NVOCMP_pageHdr_t pageHdr; NVOCMP_read(dstPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); if (pageHdr.state == NVOCMP_PGRDY) { NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGACT); } if (pageHdr.state != NVOCMP_PGACT) { NVOCMP_failW = NVINTF_FAILURE; return; } NVOCMP_ALERT(pageHdr.state == NVOCMP_PGACT, "Something wrong.") // Total length of this item iLen = NVOCMP_ITEMHDRLEN + pHdr->len; if ((dstOff + iLen) <= FLASH_PAGE_SIZE) { cmpIH_t cHdr; uint16_t hOfs, dLen; uint8_t newCRC; // Compressed item header information <-- Lower Addr Higher Addr--> // Byte: [0] [1] [2] [3] [4] [5] [6] // Item: SSSSSSII IIIIIIII SSSSSSSS SSLLLLLL LLLLLLCC CCCCCCAV SSSSSSSS // LSB of field: ^ ^ ^ ^ ^ #if NVOCMP_HDRLE cHdr[0] = (pHdr->sysid & 0x3F) | ((pHdr->itemid & 0x3) << 6); cHdr[1] = (pHdr->itemid >> 2) & 0xFF; cHdr[2] = pHdr->subid & 0xFF; cHdr[3] = ((pHdr->subid >> 8) & 0x3) | ((pHdr->len & 0x3F) << 2); cHdr[4] = (pHdr->len >> 6) & 0x3F; #else cHdr[0] = ((pHdr->sysid << 2) | ((pHdr->itemid >> 8) & 0x3)); cHdr[1] = (pHdr->itemid & 0xFF); cHdr[2] = ((pHdr->subid >> 2) & 0xFF); cHdr[3] = ((pHdr->subid & 0x3) << 6) | ((pHdr->len >> 6) & 0x3F); cHdr[4] = ((pHdr->len & 0x3F) << 2); #endif // Header is located after the item data dLen = pHdr->len; hOfs = dstOff + dLen; if (iLen <= NVOCMP_SMALLITEM) { // Construct item in one buffer // Put data into buffer memcpy(NVOCMP_itemBuffer, (const void *) pBuf, dLen); // Put most of header into buffer memcpy(NVOCMP_itemBuffer + dLen, (const void *) cHdr, NVOCMP_HDRCRCINC); // Calculate CRC newCRC = NVOCMP_doRAMCRC(NVOCMP_itemBuffer, dLen + NVOCMP_HDRCRCINC, 0); #if NVOCMP_HDRLE // Insert CRC and last bytes cHdr[4] |= ((newCRC & 0x3) << 6); // Note NVOCMP_VALIDIDBIT set implicitly zero cHdr[5] = ((newCRC >> 2) & 0x3F) | (NVOCMP_ACTIVEIDBIT << 6); #else // Insert CRC and last bytes cHdr[4] |= ((newCRC >> 6) & 0x3); // Note NVOCMP_VALIDIDBIT set implicitly zero cHdr[5] = ((newCRC & 0x3F) << 2) | NVOCMP_ACTIVEIDBIT; #endif cHdr[6] = NVOCMP_SIGNATURE; memcpy(NVOCMP_itemBuffer + dLen, (const void *) cHdr, NVOCMP_ITEMHDRLEN); // NVS_write NVOCMP_failW = NVOCMP_write(dstPg, dstOff, NVOCMP_itemBuffer, iLen); // Advance to next location dstOff += iLen; pNvHandle->actOffset += iLen; pNvHandle->pageInfo[dstPg].offset = dstOff; } else { // Write header/item separately // Calculate CRC on data portion newCRC = NVOCMP_doRAMCRC(pBuf, dLen, 0); // Finish CRC using header portion newCRC = NVOCMP_doRAMCRC(cHdr, NVOCMP_HDRCRCINC, newCRC); // Complete Header with CRC, bits, and sig #if NVOCMP_HDRLE // Insert CRC and last bytes cHdr[4] |= ((newCRC & 0x3) << 6); // Note NVOCMP_VALIDIDBIT set implicitly zero cHdr[5] = ((newCRC >> 2) & 0x3F) | (NVOCMP_ACTIVEIDBIT << 6); #else // Insert CRC and last bytes cHdr[4] |= ((newCRC >> 6) & 0x3); // Note NVOCMP_VALIDIDBIT set implicitly zero cHdr[5] = ((newCRC & 0x3F) << 2) | NVOCMP_ACTIVEIDBIT; #endif cHdr[6] = NVOCMP_SIGNATURE; // Write data NVOCMP_failW = NVOCMP_write(dstPg, dstOff, pBuf, dLen); // Write header NVOCMP_failW |= NVOCMP_write(dstPg, hOfs, cHdr, NVOCMP_ITEMHDRLEN); // Advance to next location NVOCMP_ASSERT(dstOff < (dstOff + iLen), "Page offset overflow!") if (!NVOCMP_failW) { dstOff += iLen; pNvHandle->actOffset += iLen; pNvHandle->pageInfo[dstPg].offset = dstOff; } else { return; } } // If there was a write failure, delete item NVOCMP_ALERT(!NVOCMP_failW, "Driver write failure. Item deleted.") if (NVOCMP_failW) { NVOCMP_setItemInactive(pNvHandle, dstPg, hOfs); } } else { // Not enough room on page NVOCMP_failW = NVINTF_BADLENGTH; } } /****************************************************************************** * @fn NVOCMP_readHeader * * @brief Read header block from NV and expand into caller's buffer * * @param pg - A valid NV Flash page * @param ofs - A valid offset into the page * @param pHdr - Pointer to caller's item header buffer * @param flag - fast flag (not used if NVOCMP_RAM_OPTIMIZATION is defined) * * @return none */ static void NVOCMP_readHeader(uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * pHdr, bool flag) { #ifndef NVOCMP_RAM_OPTIMIZATION #ifdef NVOCMP_GPRAM uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; #else uint8_t * pTBuffer = (uint8_t *) tBuffer; #endif #endif cmpIH_t cHdr; #ifndef NVOCMP_RAM_OPTIMIZATION if (flag) { memcpy((uint8_t *) cHdr, (uint8_t *) (pTBuffer + ofs), NVOCMP_ITEMHDRLEN); } else { #endif // Get item header from Flash NVOCMP_read(pg, ofs, (uint8_t *) cHdr, NVOCMP_ITEMHDRLEN); #ifndef NVOCMP_RAM_OPTIMIZATION } #endif // Offset to compressed header pHdr->hofs = ofs; pHdr->hpage = pg; // Compressed item header information <-- Lower Addr Higher Addr--> // Byte: [0] [1] [2] [3] [4] [5] [6] // Item: SSSSSSII IIIIIIII SSSSSSSS SSLLLLLL LLLLLLCC CCCCCCAV SSSSSSSS // LSB of field: ^ ^ ^ ^ ^ #if NVOCMP_HDRLE pHdr->sysid = cHdr[0] & 0x3F; pHdr->itemid = ((cHdr[0] >> 6) & 0x3) | (cHdr[1] << 2); pHdr->subid = cHdr[2] | ((cHdr[3] & 0x3) << 8); pHdr->len = ((cHdr[3] >> 2) & 0x3F) | ((cHdr[4] & 0x3F) << 6); pHdr->crc8 = ((cHdr[4] >> 6) & 0x3) | ((cHdr[5] & 0x3F) << 2); pHdr->stats = (cHdr[5] >> 6) & (NVOCMP_VALIDIDBIT | NVOCMP_ACTIVEIDBIT); pHdr->sig = cHdr[6]; #else pHdr->sysid = (cHdr[0] >> 2) & 0x3F; pHdr->itemid = ((cHdr[0] & 0x3) << 8) | cHdr[1]; pHdr->subid = (cHdr[2] << 2) | ((cHdr[3] >> 6) & 0x3); pHdr->len = ((cHdr[3] & 0x3F) << 6) | ((cHdr[4] >> 2) & 0x3F); pHdr->crc8 = ((cHdr[4] & 0x3) << 6) | ((cHdr[5] >> 2) & 0x3F); pHdr->stats = cHdr[5] & (NVOCMP_VALIDIDBIT | NVOCMP_ACTIVEIDBIT); pHdr->sig = cHdr[6]; #endif pHdr->cmpid = NVOCMP_CMPRID(pHdr->sysid, pHdr->itemid, pHdr->subid); // Our item has correct signature? if (pHdr->sig != NVOCMP_SIGNATURE) { // Indicate item is invalid NVOCMP_ALERT(false, "Invalid signature detected! Item corrupted.") pHdr->stats |= NVOCMP_VALIDIDBIT; } else { (pHdr->stats) |= NVOCMP_FOLLOWBIT; NVOCMP_ALERT(pHdr->stats & NVOCMP_FOLLOWBIT, "Item gap detected. Item not followed.") } } /****************************************************************************** * @fn NVOCMP_readItem * * @brief Function to read an item described by iHdr into pBuf * * @param iHdr - pointer to an item header struct * @param bOfs - offset into NV data block * @param len - length of NV data to return (0 is illegal) * @param pBuf - pointer to caller's read data buffer (NULL is illegal) * @param flag - fast flag (not used if NVOCMP_RAM_OPTIMIZATION is defined) * * @return NVINTF_SUCCESS or specific failure code */ static uint8_t NVOCMP_readItem(NVOCMP_itemHdr_t * iHdr, uint16_t ofs, uint16_t len, void * pBuf, bool flag) { uint8_t err = NVINTF_SUCCESS; uint16_t dOfs, iOfs; #ifndef NVOCMP_RAM_OPTIMIZATION #ifdef NVOCMP_GPRAM uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; #else uint8_t * pTBuffer = (uint8_t *) tBuffer; #endif #endif iOfs = (iHdr->hofs - iHdr->len); // Optional CRC integrity check #if NVOCMP_CRCONREAD err = NVOCMP_verifyCRC(iOfs, iHdr->len, iHdr->crc8, iHdr->hpage, flag); #endif if (err == NVINTF_SUCCESS) { // Offset to start of item data dOfs = iOfs + ofs; if ((dOfs + len) <= iHdr->hofs) { #ifndef NVOCMP_RAM_OPTIMIZATION if (flag) { // Copy from RAM memcpy((uint8_t *) pBuf, (uint8_t *) (pTBuffer + dOfs), len); } else { #endif // Copy NV data block to caller's buffer NVOCMP_read(iHdr->hpage, dOfs, (uint8_t *) pBuf, len); #ifndef NVOCMP_RAM_OPTIMIZATION } #endif } else { // Bad length or offset err = (len > iHdr->len) ? NVINTF_BADLENGTH : NVINTF_BADOFFSET; } } return (err); } /****************************************************************************** * @fn NVOCMP_setItemInactive * * @brief Mark an item as inactive * * @param pNvHandle - pointer to NV handle * @param pg - page where the item is located * @param iOfs - Offset to item header (lowest address) in active page * * @return none */ static void NVOCMP_setItemInactive(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t iOfs) { uint8_t tmp; // Get byte with validity bit tmp = NVOCMP_readByte(pg, iOfs + NVOCMP_HDRVLDOFS); // Remove ACTIVE_IDS_MARK #if NVOCMP_HDRLE tmp &= ~(NVOCMP_ACTIVEIDBIT << 6); #else tmp &= ~NVOCMP_ACTIVEIDBIT; #endif // Mark the item as inactive NVOCMP_writeByte(pg, iOfs + NVOCMP_HDRVLDOFS, tmp); if (pNvHandle->pageInfo[pg].allActive) { tmp = NVOCMP_readByte(pg, NVOCMP_PGHDRVER); tmp &= ~NVOCMP_ALLACTIVE; NVOCMP_writeByte(pg, NVOCMP_PGHDRVER, tmp); pNvHandle->pageInfo[pg].allActive = NVOCMP_SOMEINACTIVE; } } /****************************************************************************** * @fn NVOCMP_setCompactHdr * * @brief Set compact header * * @param dstPg - destination page to write the header * @param pg - page of compaction * @param offset - offset of compaction * @param location - location of the header * * @return none */ static void NVOCMP_setCompactHdr(uint8_t dstPg, uint8_t pg, int16_t offset, uint16_t location) { NVOCMP_compactHdr_t hdr = DEFAULT_COMPACTHDR; hdr.page = pg; hdr.pageOffset = offset; NVOCMP_failW = NVOCMP_write(dstPg, (location + 1) * NVOCMP_COMPACTHDRLEN, (uint8_t *) &hdr, NVOCMP_COMPACTHDRLEN); } #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_getCompactHdr * * @brief Get compact header * * @param dstPg - destination page to read the header * @param location - location of the header * @param pHdr - pointer to the header * * @return none */ static void NVOCMP_getCompactHdr(uint8_t dstPg, uint16_t location, NVOCMP_compactHdr_t * pHdr) { NVOCMP_read(dstPg, (location + 1) * NVOCMP_COMPACTHDRLEN, (uint8_t *) pHdr, NVOCMP_COMPACTHDRLEN); } #endif /****************************************************************************** * @fn NVOCMP_setPageState * * @brief Set specified NV page state * * @param pNvHandle - pointer to NV handle * @param pg - target NV page * @param state - state of the page * * @return none */ static void NVOCMP_setPageState(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageState_t state) { NVOCMP_pageHdr_t pHdr; // Load header pHdr.state = (uint8_t) state; pHdr.cycle = (uint8_t) pNvHandle->pageInfo[pg].cycle; pHdr.allActive = NVOCMP_ALLACTIVE; pHdr.version = (uint8_t) NVOCMP_VERSION; pHdr.signature = (uint8_t) NVOCMP_SIGNATURE; // Write to page NVOCMP_failW = NVOCMP_write(pg, 0, (uint8_t *) &pHdr, NVOCMP_PGHDRLEN); if (NVOCMP_failW == NVINTF_SUCCESS) { if (state == NVOCMP_PGACT) { // No errors, switch active page pNvHandle->actPage = pg; } pNvHandle->pageInfo[pg].state = state; pNvHandle->pageInfo[pg].allActive = NVOCMP_ALLACTIVE; } } /****************************************************************************** * @fn NVOCMP_changePageState * * @brief Change NV page state * * @param pNvHandle - pointer to NV handle * @param pg - target NV page * @param state - state of the page * * @return none */ static void NVOCMP_changePageState(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageState_t state) { NVOCMP_writeByte(pg, NVOCMP_PGHDROFS, (uint8_t) state); if (NVOCMP_failW == NVINTF_SUCCESS) { if (state == NVOCMP_PGACT) { // No errors, switch active page pNvHandle->actPage = pg; } pNvHandle->pageInfo[pg].state = state; } } /****************************************************************************** * @fn NVOCMP_findOffset * * @brief Find the offset to next available empty space in specified page * * @param pg - Valid NV page on which to find offset to next available data * @param ofs - Beginning offset to start search * * @return Number of bytes from start of page to next available item location */ #if NVOCMP_FASTOFF static uint16_t NVOCMP_findOffset(uint8_t pg, uint16_t ofs) { uint8_t i, j; uint32_t * tmp; #ifdef NVOCMP_GPRAM uint32_t vm; uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; #else uint8_t * pTBuffer = (uint8_t *) tBuffer; #endif #ifdef NVOCMP_GPRAM NVOCMP_disableCache(&vm); #endif NVOCMP_read(pg, 0, (uint8_t *) pTBuffer, FLASH_PAGE_SIZE); // Find first non-erased 4-byte location tmp = (uint32_t *) pTBuffer; while (ofs >= sizeof(uint32_t)) { ofs -= sizeof(uint32_t); tmp = (uint32_t *) (pTBuffer + ofs); if ((*tmp) != NVOCMP_ERASEDWORD) { break; } } // Starting with LSB, look for non-erased byte for (i = j = 1; i <= 4; i++) { if (((*tmp) & NVOCMP_ERASEDBYTE) != NVOCMP_ERASEDBYTE) { // Last non-erased byte so far j = i; } (*tmp) >>= 8; } #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif return (ofs + j); } #else static uint16_t NVOCMP_findOffset(uint8_t pg, uint16_t ofs) { uint8_t i, j; uint32_t tmp = 0; // Find first non-erased 4-byte location while (ofs >= sizeof(tmp)) { ofs -= sizeof(tmp); NVOCMP_read(pg, ofs, (uint8_t *) &tmp, sizeof(tmp)); if (tmp != NVOCMP_ERASEDWORD) { break; } } // Starting with LSB, look for non-erased byte for (i = j = 1; i <= 4; i++) { if ((tmp & NVOCMP_ERASEDBYTE) != NVOCMP_ERASEDBYTE) { // Last non-erased byte so far j = i; } tmp >>= 8; } return (ofs + j); } #endif /****************************************************************************** * @fn NVOCMP_findItem * * @brief Find a valid item from designated page and offset * * @param pNvHandle - pointer to NV handle * @param pg - Valid NV page * @param ofs - Offset in NV page from where to start search * @param pHdr - pointer to item header * @param flag - specifies type of search * @param pInfo - pointer to item info * * @return When >0, offset to the item header for found item * When <=0, -number of items searched when item not found * */ #if NVOCMP_FASTITEM static int8_t NVOCMP_findItem(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * pHdr, int8_t flag, NVOCMP_itemInfo_t * pInfo) { bool found = false; uint8_t p; uint16_t items = 0; uint16_t nvSearched = 0; #ifdef NVOCMP_GPRAM uint32_t vm; uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; #else uint8_t * pTBuffer = (uint8_t *) tBuffer; #endif uint32_t cid = NVOCMP_CMPRID(pHdr->sysid, pHdr->itemid, pHdr->subid); #ifdef NVOCMP_GPRAM NVOCMP_disableCache(&vm); #endif for (p = pg; nvSearched < NVOCMP_NVSIZE; p = NVOCMP_DECPAGE(p), ofs = pNvHandle->pageInfo[p].offset) { nvSearched++; #if (NVOCMP_NVPAGES != NVOCMP_NVONEP) if (p == pNvHandle->tailPage) { continue; } #endif NVOCMP_read(pg, 0, (uint8_t *) pTBuffer, FLASH_PAGE_SIZE); while (ofs >= (NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN)) { NVOCMP_itemHdr_t iHdr; // Align to start of item header ofs -= NVOCMP_ITEMHDRLEN; // Read and decompress item header NVOCMP_readHeader(p, ofs, &iHdr, true); if ((iHdr.stats & NVOCMP_ACTIVEIDBIT) && !(iHdr.stats & NVOCMP_VALIDIDBIT)) { uint32_t sysid = pHdr->sysid; uint32_t itemid = pHdr->itemid; switch (flag & NVOCMP_FINDLMASK) { case NVOCMP_FINDANY: found = true; break; case NVOCMP_FINDSTRICT: // Return first cid match if (cid == iHdr.cmpid) { found = true; } break; case NVOCMP_FINDSYSID: // return first sysid match if (sysid == iHdr.sysid) { found = true; } break; case NVOCMP_FINDITMID: // return first sysid AND itemid match if (sysid == iHdr.sysid && itemid == iHdr.itemid) { found = true; } break; default: // Should not get here NVOCMP_EXCEPTION(p, NVINTF_BADPARAM); NVOCMP_ASSERT(false, "Unhandled case in findItem().") #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif return (NVINTF_BADPARAM); } // Item found - return offset of item header if (found) { if ((pInfo) && ((flag & NVOCMP_FINDHMASK) == NVOCMP_FINDCONTENT)) { if (!NVOCMP_readItem(&iHdr, 0, pInfo->rlength, pInfo->rBuf, false)) { if (!memcmp((uint8_t *) pInfo->rBuf + pInfo->coff, pInfo->cBuf, pInfo->clength)) { memcpy(pHdr, &iHdr, sizeof(NVOCMP_itemHdr_t)); #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif return (NVINTF_SUCCESS); } } found = false; } else { memcpy(pHdr, &iHdr, sizeof(NVOCMP_itemHdr_t)); #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif return (NVINTF_SUCCESS); } } } // Try to jump to next item if (iHdr.stats & NVOCMP_FOLLOWBIT) { // Appears to be an item there, check bounds if (iHdr.len < ofs) { // Adjust offset for next try ofs -= iHdr.len; } else { // Length is corrupt, mark item invalid and compact NVOCMP_ALERT(false, "Item length corrupted. Deleting item.") NVOCMP_setItemInactive(pNvHandle, p, ofs); #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif NVOCMP_compactPage(pNvHandle, 0); p = NVOCMP_INCPAGE(pNvHandle->actPage); ofs = 0; nvSearched = 0; } } else { // Something is corrupted, compact to fix NVOCMP_ALERT(false, "No item following current item, " "compaction needed.") #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif NVOCMP_compactPage(pNvHandle, 0); p = NVOCMP_INCPAGE(pNvHandle->actPage); ofs = 0; nvSearched = 0; } // Running count of items searched items += 1; } } #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif // Item not found (negate number of items searched) // or nth not found, return last found pHdr->hofs = 0; return (NVINTF_NOTFOUND); } #else static int8_t NVOCMP_findItem(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * pHdr, int8_t flag, NVOCMP_itemInfo_t * pInfo) { bool found = false; uint8_t p = pg; uint16_t items = 0; uint32_t cid = NVOCMP_CMPRID(pHdr->sysid, pHdr->itemid, pHdr->subid); #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) uint16_t nvSearched = 0; for (p = pg; nvSearched < NVOCMP_NVSIZE; p = NVOCMP_DECPAGE(p), ofs = pNvHandle->pageInfo[p].offset) { nvSearched++; if (p == pNvHandle->tailPage) { continue; } #endif while (ofs >= (NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN)) { NVOCMP_itemHdr_t iHdr; // Align to start of item header ofs -= NVOCMP_ITEMHDRLEN; // Read and decompress item header NVOCMP_readHeader(p, ofs, &iHdr, false); if ((iHdr.stats & NVOCMP_ACTIVEIDBIT) && !(iHdr.stats & NVOCMP_VALIDIDBIT)) { uint32_t sysid = pHdr->sysid; uint32_t itemid = pHdr->itemid; switch (flag & NVOCMP_FINDLMASK) { case NVOCMP_FINDANY: found = true; break; case NVOCMP_FINDSTRICT: // Return first cid match if (cid == iHdr.cmpid) { found = true; } break; case NVOCMP_FINDSYSID: // return first sysid match if (sysid == iHdr.sysid) { found = true; } break; case NVOCMP_FINDITMID: // return first sysid AND itemid match if (sysid == iHdr.sysid && itemid == iHdr.itemid) { found = true; } break; default: // Should not get here NVOCMP_EXCEPTION(p, NVINTF_BADPARAM); NVOCMP_ASSERT(false, "Unhandled case in findItem().") return (NVINTF_BADPARAM); } // Item found - return offset of item header if (found) { if ((pInfo) && ((flag & NVOCMP_FINDHMASK) == NVOCMP_FINDCONTENT)) { if (!NVOCMP_readItem(&iHdr, 0, pInfo->rlength, pInfo->rBuf, false)) { if (!memcmp((uint8_t *) pInfo->rBuf + pInfo->coff, pInfo->cBuf, pInfo->clength)) { memcpy(pHdr, &iHdr, sizeof(NVOCMP_itemHdr_t)); return (NVINTF_SUCCESS); } } found = false; } else { memcpy(pHdr, &iHdr, sizeof(NVOCMP_itemHdr_t)); return (NVINTF_SUCCESS); } } } // Try to jump to next item if (iHdr.stats & NVOCMP_FOLLOWBIT) { // Appears to be an item there, check bounds if (iHdr.len < ofs) { // Adjust offset for next try ofs -= iHdr.len; } else { // Length is corrupt, mark item invalid and compact NVOCMP_ALERT(false, "Item length corrupted. Deleting item.") NVOCMP_setItemInactive(pNvHandle, p, ofs); NVOCMP_compactPage(pNvHandle, 0); #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) p = NVOCMP_INCPAGE(pNvHandle->actPage); ofs = 0; nvSearched = 0; #else p = pNvHandle->actPage; ofs = pNvHandle->actOffset; #endif } } else { // Something is corrupted, compact to fix NVOCMP_ALERT(false, "No item following current item, " "compaction needed.") pNvHandle->forceCompact = 1; NVOCMP_compactPage(pNvHandle, 0); #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) p = NVOCMP_INCPAGE(pNvHandle->actPage); ofs = 0; nvSearched = 0; #else p = pNvHandle->actPage; ofs = pNvHandle->actOffset; #endif } // Running count of items searched items += 1; } #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) } #endif // Item not found (negate number of items searched) // or nth not found, return last found pHdr->hofs = 0; return (NVINTF_NOTFOUND); } #endif #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_cleanPage * * @brief Clean the page that compaction is done with * * @param pNvHandle - pointer to NV handle * * @return Number of pages cleaned */ static uint8_t NVOCMP_cleanPage(NVOCMP_nvHandle_t * pNvHandle) { uint8_t pg; uint8_t pages = 0; NVOCMP_pageHdr_t pageHdr; NVOCMP_compactHdr_t startHdr; NVOCMP_compactHdr_t endHdr; /* correct ofset */ pg = pNvHandle->compactInfo.xDstPage; NVOCMP_getCompactHdr(pg, XSRCSTARTHDR, &startHdr); NVOCMP_getCompactHdr(pg, XSRCENDHDR, &endHdr); for (pg = pNvHandle->compactInfo.xSrcSPage; pg != NVOCMP_INCPAGE(pNvHandle->compactInfo.xSrcEPage); pg = NVOCMP_INCPAGE(pg)) { if (pg == endHdr.page) { pNvHandle->pageInfo[pg].offset = endHdr.pageOffset; } else { pNvHandle->pageInfo[pg].offset = NVOCMP_PGDATAOFS; } } /* end of correction */ for (pg = pNvHandle->compactInfo.xSrcSPage; pg != NVOCMP_INCPAGE(pNvHandle->compactInfo.xSrcEPage); pg = NVOCMP_INCPAGE(pg)) { if (pNvHandle->pageInfo[pg].offset == NVOCMP_PGDATAOFS) { pages++; NVOCMP_failW = NVOCMP_erase(pNvHandle, pg); } else { NVOCMP_failW = NVOCMP_write(pg, (THISPAGEHDR + 1) * NVOCMP_COMPACTHDRLEN, (uint8_t *) &pNvHandle->pageInfo[pg].offset, sizeof(pNvHandle->pageInfo[pg].offset)); NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); if (pageHdr.state == NVOCMP_PGACT) { NVOCMP_changePageState(pNvHandle, pg, NVOCMP_PGFULL); } else { // this should not hit if (pageHdr.state == NVOCMP_PGXDST) { NVOCMP_ASSERT1(0); } } } } return (pages); } #endif #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_compactPage * * @brief Compact specified page by copying active items to other page * * Compaction occurs under three circumstances: (1) 'maintenance' * activity which is triggered by a user call to compactNvApi(), * (2) 'update' activity where an NV page is packed to make room * for an item being written. The 'update' mode is performed by * writing the item after the rest of the page has been compacted, * and (3) when corruption is detected in the NV page. The compaction * operation will move all active&valid items to the other page. * * @param pNvHandle - pointer to NV handle * @param nBytes - size of item to write if any * * @return Number of available bytes on compacted page, -1 if error */ static int16_t NVOCMP_compactPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t nBytes) { uint8_t pg; uint8_t mode; uint8_t srcPg; uint8_t dstPg; uint16_t needBytes; uint16_t compactPages; uint16_t cleanPages; uint16_t skipPages = 0; bool foundRoom = false; NVOCMP_compactStatus_t status; NVOCMP_pageHdr_t pageHdr; uint8_t allActivePages = 0; uint8_t err = NVINTF_SUCCESS; // Check voltage if possible NVOCMP_FLASHACCESS(err) if (err) { return (0); } srcPg = pNvHandle->headPage; dstPg = pNvHandle->tailPage; compactPages = NVOCMP_NVSIZE - 1; if (nBytes) { pNvHandle->compactInfo.xSrcPages = 1; } else { pNvHandle->compactInfo.xSrcPages = NVOCMP_NVSIZE - 1; } // mark page mode for (pg = 0; pg < NVOCMP_NVSIZE; pg++) { if (pg != dstPg) { NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); if (pageHdr.allActive) { allActivePages++; } if (pageHdr.state == NVOCMP_PGACT || pageHdr.state == NVOCMP_PGFULL) { mode = NVOCMP_PGCSRC; NVOCMP_writeByte(pg, NVOCMP_COMPMODEOFS, mode); pNvHandle->pageInfo[pg].mode = mode; } } } if ((allActivePages == NVOCMP_NVSIZE - 1) && !pNvHandle->forceCompact) { return (0); } while (compactPages) { if (pNvHandle->compactInfo.xSrcPages == 0) { pNvHandle->compactInfo.xSrcPages = 1; } // Get page header NVOCMP_read(srcPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); #if NVOCMP_FASTCP if (nBytes && pageHdr.allActive) { srcPg = NVOCMP_INCPAGE(srcPg); pNvHandle->compactInfo.xDstOffset = FLASH_PAGE_SIZE; pNvHandle->compactInfo.xSrcSPage = srcPg; pNvHandle->compactInfo.xSrcPages--; compactPages--; skipPages++; continue; } #endif // Mark source page to be in PGXSRC state if (pageHdr.state != NVOCMP_PGXSRC) { NVOCMP_changePageState(pNvHandle, srcPg, NVOCMP_PGXSRC); } // Get page header NVOCMP_read(dstPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); // Mark destination page to be in PGXDST state if (pageHdr.state != NVOCMP_PGXDST) { NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGXDST); } mode = NVOCMP_PGCDST; NVOCMP_writeByte(dstPg, NVOCMP_COMPMODEOFS, mode); pNvHandle->pageInfo[dstPg].mode = mode; pNvHandle->compactInfo.xDstPage = dstPg; pNvHandle->compactInfo.xDstOffset = FLASH_PAGE_SIZE; pNvHandle->compactInfo.xSrcSPage = srcPg; pNvHandle->compactInfo.xSrcSOffset = pNvHandle->pageInfo[srcPg].offset; pNvHandle->compactInfo.xSrcEPage = NVOCMP_NULLPAGE; pNvHandle->compactInfo.xSrcEOffset = 0; status = NVOCMP_compact(pNvHandle); if (status == NVOCMP_COMPACT_FAILURE) { #ifdef NVOCMP_RECOVER_FROM_COMPACT_FAILURE uint8_t p; for (p = 0; p < NVOCMP_NVSIZE; p++) { NVOCMP_failW |= NVOCMP_erase(pNvHandle, p); if ((p != 0) && (p != NVOCMP_NVSIZE - 1)) { NVOCMP_changePageState(pNvHandle, p, NVOCMP_PGRDY); } } pNvHandle->actPage = 0; pNvHandle->actOffset = pNvHandle->pageInfo[0].offset; pNvHandle->headPage = 0; pNvHandle->tailPage = NVOCMP_NVSIZE - 1; NVOCMP_changePageState(pNvHandle, NVOCMP_NVSIZE - 1, NVOCMP_PGXDST); NVOCMP_changePageState(pNvHandle, 0, NVOCMP_PGACT); pNvHandle->forceCompact = 0; #endif return (0); } needBytes = nBytes ? nBytes : 16; if (nBytes) { if (FLASH_PAGE_SIZE - pNvHandle->compactInfo.xDstOffset >= needBytes) { foundRoom = true; } } // change XDST page state if (FLASH_PAGE_SIZE - pNvHandle->compactInfo.xDstOffset >= needBytes) { NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGACT); } else { NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGFULL); } // clean XSRC pages cleanPages = NVOCMP_cleanPage(pNvHandle); compactPages -= cleanPages; pNvHandle->compactInfo.xSrcPages -= cleanPages; // mark XDST page as done mode = NVOCMP_PGCDONE; NVOCMP_writeByte(dstPg, NVOCMP_COMPMODEOFS, mode); pNvHandle->pageInfo[dstPg].mode = mode; // move tail page and head page pNvHandle->tailPage = NVOCMP_ADDPAGE(pNvHandle->tailPage, cleanPages + skipPages); pNvHandle->headPage = NVOCMP_INCPAGE(pNvHandle->tailPage); skipPages = 0; // set next source page if (pNvHandle->pageInfo[pNvHandle->compactInfo.xSrcEPage].offset == NVOCMP_PGDATAOFS) { srcPg = NVOCMP_INCPAGE(pNvHandle->compactInfo.xSrcEPage); } else { srcPg = pNvHandle->compactInfo.xSrcEPage; } // set next destination page dstPg = pNvHandle->tailPage; if (nBytes && foundRoom) { break; } } pg = NVOCMP_findPage(NVOCMP_PGACT); if (pg == NVOCMP_NULLPAGE) { pg = NVOCMP_findPage(NVOCMP_PGRDY); if (pg == NVOCMP_NULLPAGE) { if (pNvHandle->pageInfo[pNvHandle->headPage].state == NVOCMP_PGNACT) { pg = pNvHandle->headPage; NVOCMP_changePageState(pNvHandle, pg, NVOCMP_PGRDY); } else { pg = NVOCMP_DECPAGE(pNvHandle->tailPage); } } } pNvHandle->actPage = pg; pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); pNvHandle->forceCompact = 0; return (FLASH_PAGE_SIZE - pNvHandle->compactInfo.xDstOffset); } #else /****************************************************************************** * @fn NVOCMP_compactPage * * @brief Compact specified page by copying active items to other page * * Compaction occurs under three circumstances: (1) 'maintenance' * activity which is triggered by a user call to compactNvApi(), * (2) 'update' activity where an NV page is packed to make room * for an item being written. The 'update' mode is performed by * writing the item after the rest of the page has been compacted, * and (3) when corruption is detected in the NV page. The compaction * operation will move all active&valid items to the other page. * * @param pNvHandle - pointer to NV handle * @param nBytes - size of item to write if any * * @return Number of available bytes on compacted page, -1 if error */ static int16_t NVOCMP_compactPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t nBytes) { uint8_t srcPg; uint8_t dstPg; uint16_t needBytes; NVOCMP_compactStatus_t status; NVOCMP_pageHdr_t pageHdr; uint8_t err = NVINTF_SUCCESS; // Check voltage if possible NVOCMP_FLASHACCESS(err) if (err) { return (0); } #if (NVOCMP_NVPAGES == NVOCMP_NVONEP) srcPg = 0; dstPg = 0; #else srcPg = pNvHandle->headPage; dstPg = pNvHandle->tailPage; #endif pNvHandle->compactInfo.xSrcPages = 1; // mark page mode NVOCMP_read(srcPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); if ((NVOCMP_ALLACTIVE == pageHdr.allActive) && !pNvHandle->forceCompact) { return (0); } NVOCMP_writeByte(srcPg, NVOCMP_COMPMODEOFS, NVOCMP_PGCSRC); pNvHandle->pageInfo[srcPg].mode = NVOCMP_PGCSRC; // Mark source page to be in PGXSRC state if (pageHdr.state != NVOCMP_PGXSRC) { NVOCMP_changePageState(pNvHandle, srcPg, NVOCMP_PGXSRC); } #if (NVOCMP_NVPAGES > NVOCMP_NVONEP) // Get page header NVOCMP_read(dstPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); // Mark destination page to be in PGXDST state if (pageHdr.state != NVOCMP_PGXDST) { NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGXDST); } NVOCMP_writeByte(dstPg, NVOCMP_COMPMODEOFS, NVOCMP_PGCDST); pNvHandle->pageInfo[dstPg].mode = NVOCMP_PGCDST; #endif pNvHandle->compactInfo.xSrcSOffset = pNvHandle->pageInfo[srcPg].offset; status = NVOCMP_compact(pNvHandle); if (status == NVOCMP_COMPACT_FAILURE) { #ifdef NVOCMP_RECOVER_FROM_COMPACT_FAILURE uint8_t p; for (p = 0; p < NVOCMP_NVSIZE; p++) { NVOCMP_failW |= NVOCMP_erase(pNvHandle, p); if ((p != 0) && (p != NVOCMP_NVSIZE - 1)) { NVOCMP_changePageState(pNvHandle, p, NVOCMP_PGRDY); } } pNvHandle->actPage = 0; pNvHandle->actOffset = pNvHandle->pageInfo[0].offset; pNvHandle->headPage = 0; pNvHandle->tailPage = NVOCMP_NVSIZE - 1; NVOCMP_changePageState(pNvHandle, NVOCMP_NVSIZE - 1, NVOCMP_PGXDST); NVOCMP_changePageState(pNvHandle, 0, NVOCMP_PGACT); pNvHandle->forceCompact = 0; #endif return (0); } needBytes = nBytes ? nBytes : 16; // change XDST page state if (FLASH_PAGE_SIZE - pNvHandle->compactInfo.xDstOffset >= needBytes) { NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGACT); } else { NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGFULL); } #if (NVOCMP_NVPAGES > NVOCMP_NVONEP) // clean XSRC pages NVOCMP_failW = NVOCMP_erase(pNvHandle, srcPg); #endif // mark XDST page as done NVOCMP_writeByte(dstPg, NVOCMP_COMPMODEOFS, NVOCMP_PGCDONE); pNvHandle->pageInfo[dstPg].mode = NVOCMP_PGCDONE; // move tail page and head page pNvHandle->tailPage = srcPg; pNvHandle->headPage = dstPg; pNvHandle->actPage = dstPg; pNvHandle->actOffset = pNvHandle->pageInfo[dstPg].offset; #if (NVOCMP_NVPAGES > NVOCMP_NVONEP) NVOCMP_changePageState(pNvHandle, srcPg, NVOCMP_PGXDST); #endif pNvHandle->forceCompact = 0; return (FLASH_PAGE_SIZE - pNvHandle->actOffset); } #endif /****************************************************************************** * @fn NVOCMP_findSignature * * @brief Local function to scan page to get page information * * @param pg - page to search * @param pSrcOff - source off to start search from * * @return NVINTF_SUCCESS or specific failure code */ static bool NVOCMP_findSignature(uint8_t pg, uint16_t * pSrcOff) { uint16_t i; uint16_t rdLen; uint8_t readBuffer[NVOCMP_XFERBLKMAX]; uint16_t srcOff = *pSrcOff; uint16_t endOff = NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN - 1; while (srcOff > endOff) { rdLen = (srcOff - NVOCMP_XFERBLKMAX > endOff) ? NVOCMP_XFERBLKMAX : srcOff - endOff; srcOff -= rdLen; NVOCMP_read(pg, srcOff, readBuffer, rdLen); for (i = rdLen; i > 0; i--) { if (readBuffer[i - 1] == NVOCMP_SIGNATURE) { // Found possible header, resume normal operation NVOCMP_ALERT(false, "Found possible signature.") srcOff += i; // srcOff should point to one byte ahead *pSrcOff = srcOff; return (true); } } } return (false); } #if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) /****************************************************************************** * @fn NVOCMP_compact * * @brief Local function to compact NV * * @param pNvHandle - pointer to NV handler * * @return NVINTF_SUCCESS or specific failure code */ static NVOCMP_compactStatus_t NVOCMP_compact(NVOCMP_nvHandle_t * pNvHandle) { bool needScan = false; bool needSkip = false; bool dstFull = false; uint16_t dstOff; uint16_t endOff; uint16_t srcOff; uint16_t crcOff; uint8_t srcStartPg; uint8_t srcEndPg; uint8_t dstPg; uint8_t srcPg; uint32_t aItem = 0; #ifndef NVOCMP_RAM_OPTIMIZATION #ifdef NVOCMP_GPRAM uint32_t vm; uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; #else uint8_t * pTBuffer = (uint8_t *) tBuffer; #endif #endif NVOCMP_compactStatus_t status = NVOCMP_COMPACT_SUCCESS; #ifndef NVOCMP_GPRAM memset(tBuffer, 0, sizeof(tBuffer)); #endif // Reset Flash erase/write fail indicator NVOCMP_failW = NVINTF_SUCCESS; srcStartPg = pNvHandle->compactInfo.xSrcSPage; srcEndPg = NVOCMP_ADDPAGE(srcStartPg, pNvHandle->compactInfo.xSrcPages - 1); // Stop looking when we get to this offset endOff = NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN - 1; srcPg = srcStartPg; srcOff = pNvHandle->pageInfo[srcPg].offset; dstPg = pNvHandle->compactInfo.xDstPage; dstOff = pNvHandle->pageInfo[dstPg].offset; NVOCMP_ALERT(false, "Compaction triggered.") #ifdef NVOCMP_GPRAM NVOCMP_disableCache(&vm); #endif while (srcPg != dstPg) { if (dstFull) { if (aItem) { pNvHandle->pageInfo[srcPg].offset = srcOff; } break; } if (srcOff <= endOff) { if (aItem) { pNvHandle->pageInfo[srcPg].offset = srcOff; } if (srcPg == srcEndPg) { break; } else { srcPg = NVOCMP_INCPAGE(srcPg); srcOff = pNvHandle->pageInfo[srcPg].offset; aItem = 0; continue; } } if (NVOCMP_failW == NVINTF_SUCCESS) { NVOCMP_itemHdr_t srcHdr; uint16_t dataLen; uint16_t itemSize; needScan = false; needSkip = false; // Read and decompress item header NVOCMP_readHeader(srcPg, srcOff - NVOCMP_ITEMHDRLEN, &srcHdr, false); dataLen = srcHdr.len; itemSize = NVOCMP_ITEMHDRLEN + dataLen; crcOff = srcOff - NVOCMP_ITEMHDRLEN - dataLen; // Check if length is safe if (srcOff < (dataLen + NVOCMP_PGDATAOFS)) { NVOCMP_ALERT(false, "Item header corrupted: Data length too long") needScan = true; srcOff--; } else if (NVOCMP_SIGNATURE != srcHdr.sig) { NVOCMP_ALERT(false, "Item header corrupted: Invalid signature") needScan = true; srcOff--; } else if (NVOCMP_verifyCRC(crcOff, dataLen, srcHdr.crc8, srcPg, false)) { // Invalid CRC, corruption NVOCMP_ALERT(false, "Item CRC incorrect!") needScan = true; srcOff--; } else if (!(srcHdr.stats & NVOCMP_VALIDIDBIT) && // Item is valid (srcHdr.stats & NVOCMP_ACTIVEIDBIT)) // Item is active { // Valid CRC, item is active srcOff -= NVOCMP_ITEMHDRLEN; } else { // Valid CRC but item is inactive srcOff -= NVOCMP_ITEMHDRLEN; needSkip = true; } if (needScan) { // Detected a problem, find next header (scan for signature) NVOCMP_ALERT(false, "Attempting to find signature...") bool foundSig = NVOCMP_findSignature(srcPg, &srcOff); if (!foundSig) { #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif // If we get here and foundSig is false, we never found another // item in the page, break the loop so that any valid items // that were collected up to this point get written to the // destination page. NVOCMP_ALERT(foundSig, "Attempt to find signature failed.") break; } } else { if (!needSkip) { if (dstOff + itemSize > FLASH_PAGE_SIZE) { // cannot fit one page temp buffer and revert srcOff change srcOff += NVOCMP_ITEMHDRLEN; dstFull = true; continue; } else { // Get block of bytes from source page #ifndef NVOCMP_RAM_OPTIMIZATION #if NVOCMP_COMPR NVOCMP_read(srcPg, crcOff, (uint8_t *) (pTBuffer + dstOff), itemSize); #else NVOCMP_read(srcPg, crcOff, (uint8_t *) (pTBuffer + FLASH_PAGE_SIZE - dstOff - itemSize), itemSize); #endif #else NVOCMP_copyItem(srcPg, dstPg, crcOff, dstOff, itemSize); #endif dstOff += itemSize; aItem++; if (dstOff == FLASH_PAGE_SIZE) { dstFull = true; } } } NVOCMP_ALERT(srcOff > dataLen, "Offset overflow: srcOff") srcOff -= dataLen; } } else { #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif // Failure during item xfer makes next findItem() unreliable NVOCMP_ASSERT(false, "COMPACTION FAILURE") return (NVOCMP_COMPACT_FAILURE); } } // end of while if (NVOCMP_failW != NVINTF_SUCCESS) { #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif // Something bad happened when trying to compact the page NVOCMP_ASSERT(false, "COMPACTION FAILURE") return (NVOCMP_COMPACT_FAILURE); } // Write block to destination page #ifndef NVOCMP_RAM_OPTIMIZATION #if NVOCMP_COMPR uint16_t off = NVOCMP_PGDATAOFS; uint16_t len = dstOff - NVOCMP_PGDATAOFS; NVOCMP_failW |= NVOCMP_write(dstPg, off, (uint8_t *) (pTBuffer + off), len); #else uint16_t off = NVOCMP_PGDATAOFS; uint16_t doff = FLASH_PAGE_SIZE - dstOff; uint16_t len = dstOff - NVOCMP_PGDATAOFS; NVOCMP_failW |= NVOCMP_write(dstPg, off, (uint8_t *) (pTBuffer + doff), len); #endif #endif #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif if (srcPg == dstPg) { srcPg = NVOCMP_DECPAGE(srcPg); srcOff = pNvHandle->pageInfo[srcPg].offset; } pNvHandle->compactInfo.xDstOffset = dstOff; pNvHandle->compactInfo.xSrcEOffset = srcOff; pNvHandle->compactInfo.xSrcEPage = srcPg; pNvHandle->pageInfo[dstPg].offset = dstOff; NVOCMP_setCompactHdr(dstPg, pNvHandle->compactInfo.xSrcSPage, pNvHandle->compactInfo.xSrcSOffset, XSRCSTARTHDR); NVOCMP_setCompactHdr(dstPg, pNvHandle->compactInfo.xSrcEPage, pNvHandle->compactInfo.xSrcEOffset, XSRCENDHDR); if (srcOff <= endOff) { status |= NVOCMP_COMPACT_SRCDONE; } if (dstOff >= FLASH_PAGE_SIZE) { status |= NVOCMP_COMPACT_DSTDONE; } return (status); } #else /****************************************************************************** * @fn NVOCMP_compact * * @brief Local function to compact NV * * @param pNvHandle - pointer to NV handler * * @return NVINTF_SUCCESS or specific failure code */ static NVOCMP_compactStatus_t NVOCMP_compact(NVOCMP_nvHandle_t * pNvHandle) { bool needScan = false; bool needSkip = false; uint16_t dstOff; uint16_t endOff; uint16_t srcOff; uint16_t crcOff; uint8_t dstPg; uint8_t srcPg; uint32_t aItem = 0; #ifndef NVOCMP_RAM_OPTIMIZATION #ifdef NVOCMP_GPRAM uint32_t vm; uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; #else uint8_t * pTBuffer = (uint8_t *) tBuffer; #endif #endif #ifndef NVOCMP_GPRAM memset(tBuffer, 0, sizeof(tBuffer)); #endif // Reset Flash erase/write fail indicator NVOCMP_failW = NVINTF_SUCCESS; // Stop looking when we get to this offset endOff = NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN - 1; #if (NVOCMP_NVPAGES == NVOCMP_NVONEP) srcPg = 0; srcOff = pNvHandle->pageInfo[0].offset; dstPg = 0; dstOff = NVOCMP_PGDATAOFS; #else srcPg = pNvHandle->headPage; srcOff = pNvHandle->pageInfo[srcPg].offset; dstPg = pNvHandle->tailPage; dstOff = pNvHandle->pageInfo[dstPg].offset; #endif NVOCMP_ALERT(false, "Compaction triggered.") #ifdef NVOCMP_GPRAM NVOCMP_disableCache(&vm); #endif while (srcOff > endOff) { if (NVOCMP_failW == NVINTF_SUCCESS) { NVOCMP_itemHdr_t srcHdr; uint16_t dataLen; uint16_t itemSize; needScan = false; needSkip = false; // Read and decompress item header NVOCMP_readHeader(srcPg, srcOff - NVOCMP_ITEMHDRLEN, &srcHdr, false); dataLen = srcHdr.len; itemSize = NVOCMP_ITEMHDRLEN + dataLen; crcOff = srcOff - NVOCMP_ITEMHDRLEN - dataLen; // Check if length is safe if (srcOff < (dataLen + NVOCMP_PGDATAOFS)) { NVOCMP_ALERT(false, "Item header corrupted: Data length too long") needScan = true; srcOff--; } else if (NVOCMP_SIGNATURE != srcHdr.sig) { NVOCMP_ALERT(false, "Item header corrupted: Invalid signature") needScan = true; srcOff--; } else if (NVOCMP_verifyCRC(crcOff, dataLen, srcHdr.crc8, srcPg, false)) { // Invalid CRC, corruption NVOCMP_ALERT(false, "Item CRC incorrect!") needScan = true; srcOff--; } else if (!(srcHdr.stats & NVOCMP_VALIDIDBIT) && // Item is valid (srcHdr.stats & NVOCMP_ACTIVEIDBIT)) // Item is active { // Valid CRC, item is active srcOff -= NVOCMP_ITEMHDRLEN; } else { // Valid CRC but item is inactive srcOff -= NVOCMP_ITEMHDRLEN; needSkip = true; } if (needScan) { // Detected a problem, find next header (scan for signature) NVOCMP_ALERT(false, "Attempting to find signature...") bool foundSig = NVOCMP_findSignature(srcPg, &srcOff); if (!foundSig) { // If we get here and foundSig is false, we never found another // item in the page, break the loop so that any valid items // that were collected up to this point get written to the // destination page. NVOCMP_ALERT(foundSig, "Attempt to find signature failed.") break; } } else { if (!needSkip) { // Get block of bytes from source page #ifndef NVOCMP_RAM_OPTIMIZATION #if NVOCMP_COMPR NVOCMP_read(srcPg, crcOff, (uint8_t *) (pTBuffer + dstOff), itemSize); #else NVOCMP_read(srcPg, crcOff, (uint8_t *) (pTBuffer + FLASH_PAGE_SIZE - dstOff - itemSize), itemSize); #endif #else NVOCMP_copyItem(srcPg, dstPg, crcOff, dstOff, itemSize); #endif dstOff += itemSize; aItem++; } NVOCMP_ALERT(srcOff > dataLen, "Offset overflow: srcOff") srcOff -= dataLen; } } else { #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif // Failure during item xfer makes next findItem() unreliable NVOCMP_ASSERT(false, "COMPACTION FAILURE") return (NVOCMP_COMPACT_FAILURE); } } // end of while if (NVOCMP_failW != NVINTF_SUCCESS) { #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif // Something bad happened when trying to compact the page NVOCMP_ASSERT(false, "COMPACTION FAILURE") return (NVOCMP_COMPACT_FAILURE); } #if (NVOCMP_NVPAGES == NVOCMP_NVONEP) // Get XDST page ready NVOCMP_failW = NVOCMP_erase(pNvHandle, dstPg); NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGXDST); #endif // Write block to destination page #ifndef NVOCMP_RAM_OPTIMIZATION #if NVOCMP_COMPR uint16_t off = NVOCMP_PGDATAOFS; uint16_t len = dstOff - NVOCMP_PGDATAOFS; NVOCMP_failW |= NVOCMP_write(dstPg, off, (uint8_t *) (pTBuffer + off), len); #else uint16_t off = NVOCMP_PGDATAOFS; uint16_t doff = FLASH_PAGE_SIZE - dstOff; uint16_t len = dstOff - NVOCMP_PGDATAOFS; NVOCMP_failW |= NVOCMP_write(dstPg, off, (uint8_t *) (pTBuffer + doff), len); #endif #endif #ifdef NVOCMP_GPRAM NVOCMP_restoreCache(vm); #endif pNvHandle->pageInfo[dstPg].offset = dstOff; pNvHandle->compactInfo.xSrcEOffset = srcOff; pNvHandle->compactInfo.xDstOffset = dstOff; NVOCMP_setCompactHdr(dstPg, pNvHandle->compactInfo.xSrcSPage, pNvHandle->compactInfo.xSrcSOffset, XSRCSTARTHDR); NVOCMP_setCompactHdr(dstPg, pNvHandle->compactInfo.xSrcEPage, pNvHandle->compactInfo.xSrcEOffset, XSRCENDHDR); return (NVOCMP_COMPACT_SUCCESS); } #endif #if ((NVOCMP_NVPAGES > NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) || defined NVOCMP_RAM_OPTIMIZATION /****************************************************************************** * @fn NVOCMP_copyItem * * @brief Copy an NV item from active page to specified destination page * * @param srcPg - Source page * @param dstPg - Destination page * @param sOfs - Source page offset of original data in active page * @param dOfs - Destination page offset to transferred copy of the item * @param len - Length of data to copy * * @return none. */ static void NVOCMP_copyItem(uint8_t srcPg, uint8_t dstPg, uint16_t sOfs, uint16_t dOfs, uint16_t len) { uint16_t num; #ifndef NVOCMP_RAM_OPTIMIZATION uint8_t tmp[NVOCMP_XFERBLKMAX]; #else uint8_t * tmp = (uint8_t *) tBuffer; #endif // Copy over the data: Flash to RAM, then RAM to Flash while (len > 0 && !NVOCMP_failW) { // Number of bytes to transfer in this block #ifndef NVOCMP_RAM_OPTIMIZATION num = (len < NVOCMP_XFERBLKMAX) ? len : NVOCMP_XFERBLKMAX; #else num = (len < NVOCMP_RAM_BUFFER_SIZE) ? len : NVOCMP_RAM_BUFFER_SIZE; #endif // Get block of bytes from source page NVOCMP_read(srcPg, sOfs, (uint8_t *) &tmp[0], num); // Write block to destination page NVOCMP_failW = NVOCMP_write(dstPg, dOfs, (uint8_t *) &tmp[0], num); dOfs += num; sOfs += num; len -= num; } } #endif /****************************************************************************** * @fn NVOCMP_readByte * * @brief Read one byte from Flash memory * * @param pg - NV Flash page * @param ofs - Offset into the page * * @return byte read from flash memory */ static uint8_t NVOCMP_readByte(uint8_t pg, uint16_t ofs) { uint8_t byteVal; NVOCMP_read(pg, ofs, &byteVal, NVOCMP_ONEBYTE); return (byteVal); } /****************************************************************************** * @fn NVOCMP_writeByte * * @brief Write one byte to Flash and read back to verify * * @param pg - NV Flash page * @param ofs - offset into the page * @param bwv - byte to write & verify * * @return none ('failF' or 'failW' will be set if write fails) */ static void NVOCMP_writeByte(uint8_t pg, uint16_t ofs, uint8_t bwv) { NVOCMP_failW = NVOCMP_write(pg, ofs, &bwv, 1); } /****************************************************************************** * @fn NVOCMP_doNVCRC * * @brief Computes the CRC8 on the NV buffer indicated * CRC code external, API in crc.h * * @param pg - Flash page to check * @param ofs - Flash page offset to lowest address item byte * @param len - Item data length * @param crc - value to start with, should be NULL if new calculation * @param flag - fast flag (not used if NVOCMP_RAM_OPTIMIZATION is defined) * * @return crc byte */ static uint8_t NVOCMP_doNVCRC(uint8_t pg, uint16_t ofs, uint16_t len, uint8_t crc, bool flag) { uint16_t rdLen = 0; #ifndef NVOCMP_RAM_OPTIMIZATION #ifdef NVOCMP_GPRAM uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; #else uint8_t * pTBuffer = (uint8_t *) tBuffer; #endif #endif uint8_t tmp[NVOCMP_XFERBLKMAX]; crc_t newCRC = (crc_t) crc; // Read flash and compute CRC in blocks while (len > 0) { rdLen = (len < NVOCMP_XFERBLKMAX ? len : NVOCMP_XFERBLKMAX); #ifndef NVOCMP_RAM_OPTIMIZATION if (flag) { memcpy((uint8_t *) tmp, (uint8_t *) (pTBuffer + ofs), rdLen); } else { #endif NVOCMP_read(pg, ofs, tmp, rdLen); #ifndef NVOCMP_RAM_OPTIMIZATION } #endif newCRC = crc_update(newCRC, tmp, rdLen); len -= rdLen; ofs += rdLen; } return (newCRC); } /****************************************************************************** * @fn NVOCMP_doRAMCRC * * @brief Calculates CRC8 given a buffer and length * CRC code external, API in crc.h * * @param input - pointer to data buffer * @param len - length of data in buffer * @param crc - value to start with, should be NULL if new calculation * * @return CRC8 byte */ static uint8_t NVOCMP_doRAMCRC(uint8_t * input, uint16_t len, uint8_t crc) { crc_t newCRC = crc_update((crc_t) crc, input, len); return ((uint8_t) newCRC); } /****************************************************************************** * @fn NVOCMP_verifyCRC * * @brief Helper function to validate item crc from NV * * @param iOfs - offset to item data * @param len - length of item data * @param crc - crc to compare against * @param pg - page to work on * @param flag - fast flag (not used if NVOCMP_RAM_OPTIMIZATION is defined) * * @return status byte */ static uint8_t NVOCMP_verifyCRC(uint16_t iOfs, uint16_t len, uint8_t crc, uint8_t pg, bool flag) { uint8_t newCRC; uint16_t crcLen = len + NVOCMP_HDRCRCINC - 1; #if NVOCMP_HDRLE uint8_t finalByte = (len >> 6) & 0x3F; #else uint8_t finalByte = (len & 0x3F) << 2; #endif // CRC calculations stop at the length field of header // So the last byte must be done separately newCRC = NVOCMP_doNVCRC(pg, iOfs, crcLen, 0, flag); newCRC = NVOCMP_doRAMCRC(&finalByte, sizeof(finalByte), newCRC); NVOCMP_ALERT(newCRC == crc, "Invalid CRC detected.") #ifdef NVOCMP_STATS if (newCRC != crc) { NVOCMP_badCRCCount++; } #endif // NVOCMP_STATS return (newCRC == crc ? NVINTF_SUCCESS : NVINTF_CORRUPT); } #ifdef ENABLE_SANITY_CHECK /****************************************************************************** * @fn NVOCMP_sanityCheckApi * * @brief Global function to perform a sanity check on the active * partition to report if corruption has been detected. * * @param none * * @return 0: No failure found. * Non-zero: failure, each bit representing a particular error * as indicated in NV driver status codes defined in nvintf.h. */ static uint32_t NVOCMP_sanityCheckApi(void) { NVOCMP_nvHandle_t * pNvHandle = &NVOCMP_nvHandle; bool needScan = false; bool needSkip = false; uint16_t dstOff; uint16_t endOff; uint16_t srcOff; uint16_t crcOff; uint8_t dstPg; uint8_t srcPg; uint32_t ret = NVINTF_SUCCESS; uint32_t aItem = 0; // Reset Flash erase/write fail indicator NVOCMP_failW = NVINTF_SUCCESS; // Stop looking when we get to this offset endOff = NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN - 1; #if (NVOCMP_NVPAGES == NVOCMP_NVONEP) srcPg = 0; srcOff = pNvHandle->pageInfo[0].offset; dstPg = 0; dstOff = NVOCMP_PGDATAOFS; #else srcPg = pNvHandle->headPage; srcOff = pNvHandle->pageInfo[srcPg].offset; dstPg = pNvHandle->tailPage; dstOff = pNvHandle->pageInfo[dstPg].offset; #endif NVOCMP_ALERT(false, "Sanity Check") while (srcOff > endOff) { if (NVOCMP_failW == NVINTF_SUCCESS) { NVOCMP_itemHdr_t srcHdr; uint16_t dataLen; uint16_t itemSize; needScan = false; needSkip = false; // Read and decompress item header NVOCMP_readHeader(srcPg, srcOff - NVOCMP_ITEMHDRLEN, &srcHdr, false); dataLen = srcHdr.len; itemSize = NVOCMP_ITEMHDRLEN + dataLen; crcOff = srcOff - NVOCMP_ITEMHDRLEN - dataLen; // Check if length is safe if (srcOff < (dataLen + NVOCMP_PGDATAOFS)) { NVOCMP_ALERT(false, "Item header corrupted: Data length too long") ret |= (1 << NVINTF_BADLENGTH); needScan = true; srcOff--; } else if (NVOCMP_SIGNATURE != srcHdr.sig) { NVOCMP_ALERT(false, "Item header corrupted: Invalid signature") ret |= (1 << NVINTF_NO_SIG); needScan = true; srcOff--; } else if (NVOCMP_verifyCRC(crcOff, dataLen, srcHdr.crc8, srcPg, false)) { // Invalid CRC, corruption NVOCMP_ALERT(false, "Item CRC incorrect!") ret |= (1 << NVINTF_CORRUPT); needScan = true; srcOff--; } else if (!(srcHdr.stats & NVOCMP_VALIDIDBIT) && // Item is valid (srcHdr.stats & NVOCMP_ACTIVEIDBIT)) // Item is active { // Valid CRC, item is active srcOff -= NVOCMP_ITEMHDRLEN; } else { // Valid CRC but item is inactive srcOff -= NVOCMP_ITEMHDRLEN; needSkip = true; } if (needScan) { // Detected a problem, find next header (scan for signature) NVOCMP_ALERT(false, "Attempting to find signature...") bool foundSig = NVOCMP_findSignature(srcPg, &srcOff); if (!foundSig) { // If we get here and foundSig is false, we never found another // item in the page, break the loop and report that corruption // has been detected NVOCMP_ALERT(foundSig, "Attempt to find signature failed.") ret |= (1 << NVINTF_NO_SIG); break; } } else { if (!needSkip) { dstOff += itemSize; aItem++; } NVOCMP_ALERT(srcOff > dataLen, "Offset overflow: srcOff") srcOff -= dataLen; } } else { // Failure during item xfer makes next findItem() unreliable NVOCMP_ASSERT(false, "SANITY CHECK FAILURE") ret |= (1 << NVINTF_FAILURE); } } // end of while if (NVOCMP_failW != NVINTF_SUCCESS) { // Something bad happened when scanning the page NVOCMP_ASSERT(false, "SANITY CHECK FAILURE") ret |= (1 << NVINTF_FAILURE); } return (ret); } #endif //*****************************************************************************