| /****************************************************************************** |
| |
| @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 <string.h> |
| #ifdef NVOCMP_POSIX_MUTEX |
| #include <pthread.h> |
| #elif defined(NVOCMP_POSIX_SEM) |
| #include <semaphore.h> |
| #else |
| #include <ti/sysbios/gates/GateMutexPri.h> |
| #endif |
| #include <crc.h> |
| #include <nvocmp.h> |
| #ifndef NV_LINUX |
| |
| #include <ti/devices/DeviceFamily.h> |
| #include <ti/devices/cc13x4_cc26x4/driverlib/vims.h> |
| |
| #ifdef NVOCMP_MIN_VDD_FLASH_MV |
| #include <driverlib/aon_batmon.h> |
| #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 |
| |
| //***************************************************************************** |