diff --git a/.github/workflows/examples-cc13xx_26xx.yaml b/.github/workflows/examples-cc13xx_26xx.yaml index abdc5605ada47b..d462d9f2725bbb 100644 --- a/.github/workflows/examples-cc13xx_26xx.yaml +++ b/.github/workflows/examples-cc13xx_26xx.yaml @@ -17,16 +17,19 @@ name: Build example - TI CC13XX_26XX on: push: branches-ignore: - - 'dependabot/**' + - "dependabot/**" pull_request: merge_group: concurrency: - group: ${{ github.ref }}-${{ github.workflow }}-${{ (github.event_name == 'pull_request' && github.event.number) || (github.event_name == 'workflow_dispatch' && github.run_number) || github.sha }} + group: + ${{ github.ref }}-${{ github.workflow }}-${{ (github.event_name == + 'pull_request' && github.event.number) || (github.event_name == + 'workflow_dispatch' && github.run_number) || github.sha }} cancel-in-progress: true env: CHIP_NO_LOG_TIMESTAMPS: true - + jobs: cc13xx_26xx: name: cc13xx_26xx @@ -47,13 +50,14 @@ jobs: - name: Checkout submodules & Bootstrap uses: ./.github/actions/checkout-submodules-and-bootstrap with: - platform: cc13xx_26xx + platform: cc13xx_26xx + extra-submodule-parameters: " --recursive" - name: Set up environment for size reports uses: ./.github/actions/setup-size-reports if: ${{ !env.ACT }} with: - gh-context: ${{ toJson(github) }} - + gh-context: ${{ toJson(github) }} + - name: Build examples run: | scripts/run_in_build_env.sh "\ @@ -103,4 +107,4 @@ jobs: uses: ./.github/actions/upload-size-reports if: ${{ !env.ACT }} with: - platform-name: cc13x4_26x4 + platform-name: cc13x4_26x4 diff --git a/docs/guides/README.md b/docs/guides/README.md index e9a42a15861648..a00cc83e280c91 100644 --- a/docs/guides/README.md +++ b/docs/guides/README.md @@ -24,7 +24,7 @@ - [Silicon Labs - Software Update](./silabs_efr32_software_update.md) - [Silicon Labs - CLI Guide](./silabs_cli_guide.md) - [STMicroelectronics (STM32)](./stm32_getting_started_guide.md) -- [TI - Platform Overview](./ti/ti_platform_overview.md) +- [TI - Platform Overview](./ti/ti_matter_overview.md) - [Open IoT SDK - Platform Overview](./openiotsdk_platform_overview.md) - [Open IoT SDK - Examples](./openiotsdk_examples.md) - [Open IoT SDK - Unit Tests](./openiotsdk_unit_tests.md) diff --git a/docs/guides/darwin.md b/docs/guides/darwin.md index 4a9a8da939952e..2a84067da92ed0 100644 --- a/docs/guides/darwin.md +++ b/docs/guides/darwin.md @@ -267,7 +267,7 @@ Example: - [Silicon Labs](./silabs_getting_started.md) - [Simulated Linux](./simulated_device_linux.md) - [Telink](/examples/lighting-app/telink/README.md) -- [TI Platform](./ti/ti_platform_overview.md) +- [TI Platform](./ti/ti_matter_overview.md) - [TI All Clusters](/examples/all-clusters-app/cc13x4_26x4/README.md) - [Tizen](/examples/lighting-app/tizen/README.md) diff --git a/docs/guides/images/matter_ti_overview_wifi.png b/docs/guides/images/matter_ti_overview_wifi.png new file mode 100644 index 00000000000000..4d73a30db7ff99 Binary files /dev/null and b/docs/guides/images/matter_ti_overview_wifi.png differ diff --git a/docs/guides/index.md b/docs/guides/index.md index 7ec0ec61a3cc69..8f747ab27e684f 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -11,7 +11,7 @@ and features. * esp32/README nxp/README -ti/ti_platform_overview +ti/ti_matter_overview ``` ## Build Guides @@ -40,7 +40,7 @@ ti/ti_platform_overview - [Silicon Labs - Getting Started](./silabs_getting_started.md) - [Silicon Labs - Software Update](./silabs_efr32_software_update.md) - [Silicon Labs - CLI Guide](./silabs_cli_guide.md) -- [TI - Platform Overview](./ti/ti_platform_overview.md) +- [TI - Platform Overview](./ti/ti_matter_overview.md) ## Tool Guides diff --git a/docs/guides/ti/matter_cc2674_migration.md b/docs/guides/ti/matter-migration-guide/matter_cc2674_migration.md similarity index 96% rename from docs/guides/ti/matter_cc2674_migration.md rename to docs/guides/ti/matter-migration-guide/matter_cc2674_migration.md index 994954d6ae7a53..a6081d9c9a73de 100644 --- a/docs/guides/ti/matter_cc2674_migration.md +++ b/docs/guides/ti/matter-migration-guide/matter_cc2674_migration.md @@ -50,9 +50,9 @@ The GPIO pin values for SPI will need to be adjusted based on your design. 4. Select _Board_ as _None_ and _Device_ as _`CC2674P10RGZ`_, Unselect _`Lock PinMux`_, and click _Confirm_. 5. To fix errors, make the following module changes: - - _RF Design_ and _RF Stacks -> BLE -> Radio_: click on _accepting the - current value_, which should be _`LP_CC2674P10_RGZ`_ in the drop down menu - for _Based On RF Design_ + - _RF Design_ and _RF Stacks -> Bluetooth LE -> Radio_: click on _accepting + the current value_, which should be _`LP_CC2674P10_RGZ`_ in the drop down + menu for _Based On RF Design_ - _TI DEVICES -> Device Configuration_: Clear _`XOSC Cap Array Modification`_ - _TI DRIVERS -> RF_: Set _Global Event Mask_ as _None_ and _No. of Antenna diff --git a/docs/guides/ti/matter-syscfg/getting-started.md b/docs/guides/ti/matter-syscfg/getting-started.md new file mode 100644 index 00000000000000..af90b79c7c87f6 --- /dev/null +++ b/docs/guides/ti/matter-syscfg/getting-started.md @@ -0,0 +1,62 @@ +# Get started with SysConfig + +System Configuration Tool (SysConfig) is a graphical interface for configuring +your project. Configuration files, C source files and header files are generated +based on the parameters configured in the SysConfig dashboard. + +SysConfig should be used as a standalone tool for script-based builds (such as +for Matter). + +The document will help for getting started in tailoring the `syscfg` file to +your application needs. + +## Using Stand-alone SysConfig + +You can find the standalone SysConfig tool on the +[SysConfig Standalone Installer Download Page](https://www.ti.com/tool/SYSCONFIG). + +The location of the Matter `syscfg` file is located here: +`/examples//cc13x4_26x4/chip.syscfg` For example, on the github web: +`https://github.com/project-chip/connectedhomeip/blob/master/examples/lock-app/cc13x4_26x4/chip.syscfg` +The local file in your cloned repository should be used. + +This file is used by Matter build scripts, when it invokes SysConfig CLI. In +order to edit the file for your custom application configurations, you'll need +to open the file with the SysConfig GUI. Before doing so, you should add the +following line above the `/* Modules */` comment: + +``` +// @cliArgs --product /.metadata/product.json --board /ti/boards/LP_EM_CC1354P10_6 --rtos freertos +``` + +As an example, after adding the necessary ``, it would look like: + +``` +// @cliArgs --product /home/Documents/repos/connectedhomeip/third_party/ti_simplelink_sdk/repo_cc13xx_cc26xx/.metadata/product.json --board /ti/boards/LP_EM_CC1354P10_6 --rtos freertos +``` + +If the file is not found, then you may need to first initialize the submodules: + +``` +git submodule update --init +``` + +Now you can open the file in SysConfig GUI. Once you are done, remove the +`@cliArgs` line then save the file. Now the Matter build scripts will use the +updated `.syscfg` file. + +## Viewing SysConfig Output + +To preview the code to be generated reference the `Generated Files` image, the +button at the top of the `Configuration` pane you will find a list of Generated +Files. After selecting a file you will be able, in real-time, to identify how +the changes in the GUI affect the generated files. This will help you review the +relationship between the GUI and the code. + +
+ Generated Files +
Generated Files
+
+ +Whenever you re-build the project, SysConfig will re-generate the files. Because +of this, any changes made directly in the generated files will be overwritten. diff --git a/docs/guides/ti/matter-syscfg/images/board_view.png b/docs/guides/ti/matter-syscfg/images/board_view.png new file mode 100644 index 00000000000000..4067a24495f843 Binary files /dev/null and b/docs/guides/ti/matter-syscfg/images/board_view.png differ diff --git a/docs/guides/ti/matter-syscfg/images/generated_files_ble.png b/docs/guides/ti/matter-syscfg/images/generated_files_ble.png new file mode 100644 index 00000000000000..39cf576cac92b4 Binary files /dev/null and b/docs/guides/ti/matter-syscfg/images/generated_files_ble.png differ diff --git a/docs/guides/ti/matter-syscfg/images/hardware_view.png b/docs/guides/ti/matter-syscfg/images/hardware_view.png new file mode 100644 index 00000000000000..7ba5dd2fed8da6 Binary files /dev/null and b/docs/guides/ti/matter-syscfg/images/hardware_view.png differ diff --git a/docs/guides/ti/matter-syscfg/images/reserve-gpio.png b/docs/guides/ti/matter-syscfg/images/reserve-gpio.png new file mode 100644 index 00000000000000..5d769306f6d731 Binary files /dev/null and b/docs/guides/ti/matter-syscfg/images/reserve-gpio.png differ diff --git a/docs/guides/ti/matter-syscfg/images/reserve-peripheral-panel.png b/docs/guides/ti/matter-syscfg/images/reserve-peripheral-panel.png new file mode 100644 index 00000000000000..1aee0be975db2e Binary files /dev/null and b/docs/guides/ti/matter-syscfg/images/reserve-peripheral-panel.png differ diff --git a/docs/guides/ti/matter-syscfg/images/show_generated_files_tab.png b/docs/guides/ti/matter-syscfg/images/show_generated_files_tab.png new file mode 100644 index 00000000000000..19f09e419f4336 Binary files /dev/null and b/docs/guides/ti/matter-syscfg/images/show_generated_files_tab.png differ diff --git a/docs/guides/ti/matter-syscfg/sysconfig-board.md b/docs/guides/ti/matter-syscfg/sysconfig-board.md new file mode 100644 index 00000000000000..371b5e00b5ea3c --- /dev/null +++ b/docs/guides/ti/matter-syscfg/sysconfig-board.md @@ -0,0 +1,69 @@ +# Configure The Board Files with SysConfig + +The board view shows you what pins are available on your board, and what +peripherals are assigned to what pins. Hover over each pin for more information. + +
+ +
+ +Enabled and configured peripherals are displayed with green symbols. To add a +new peripheral, press the plus sign next to the peripheral type. The \"Show +generated files\" tab lets you see the files generated based on SysConfig. +Generated files are dynamically updated every time you make a change in +SysConfig. When you build your project, the generated files are exported into +the output folder of your project. + +
+ +
+ +## Reserve Peripherals + +Certain use cases may require preventing SysConfig from generating the +configuration of specific peripherals. + +Examples of use cases requiring to reserve peripherals are: + +- dynamic re-configuration of a peripheral +- leveraging the Sensor Controller (on devices enabling it) +- already handling certain peripherals configuration +- with hardware constraints preventing to use certain peripherals (typically + `GPIOs`) +- peripherals reserved for future use + +Below are the steps required to utilize the Reserve Peripherals feature. + +1. Open the `.syscfg` file using the SysConfig GUI + +2. Open the panel \"Reserve Peripherals\" + +
+ +
+ +3. Reserve the resources + + Reserve a peripheral using SysConfig. This image shows how to reserve `DIO` + 15 + +
+ +
+ +SysConfig ensures no resource conflict occurs. In case no solution can be found, +an error is raised. + +## Hardware View + +The Hardware view may be used to add software modules for supporting hardware +resources on EVMs or LaunchPads. The same capability could be done in the +Software view by adding a Hardware. However the Hardware view provides a +hardware-first perspective to show which software could be used with hardware +resources. + +Sysconfig Hardware View: + +
+ +
diff --git a/docs/guides/ti/enabling_icd_on_ti_devices.md b/docs/guides/ti/matter-users-guide/enabling_icd_on_ti_devices.md similarity index 92% rename from docs/guides/ti/enabling_icd_on_ti_devices.md rename to docs/guides/ti/matter-users-guide/enabling_icd_on_ti_devices.md index 7e10ceb0c11ee0..2603343ac0c26c 100644 --- a/docs/guides/ti/enabling_icd_on_ti_devices.md +++ b/docs/guides/ti/matter-users-guide/enabling_icd_on_ti_devices.md @@ -1,4 +1,4 @@ -# Configuring Intermittently Connected Devices on TI CC13x4 Platforms +# Configuring Intermittently Connected Devices on TI SimpleLink CC13x4_CC26x4 Platforms ## Overview @@ -6,7 +6,7 @@ Intermittently Connected Devices are devices in a network that do not always need to be active. Matter has defined a cluster that helps capture this behavior; this configuration is ideal for devices that need to operate with low power consumption or do not have a need to always be on the network. Matter -examples on the TI CC13x4 platform can be configured to act as ICDs. +examples on the TI CC13x4_CC26x4 platform can be configured to act as ICDs. ## Platform Code Changes diff --git a/docs/guides/ti/images/cc13x4_memmap.png b/docs/guides/ti/matter-users-guide/images/cc13x4_memmap.png similarity index 100% rename from docs/guides/ti/images/cc13x4_memmap.png rename to docs/guides/ti/matter-users-guide/images/cc13x4_memmap.png diff --git a/docs/guides/ti/images/factory_data_overview.png b/docs/guides/ti/matter-users-guide/images/factory_data_overview.png similarity index 100% rename from docs/guides/ti/images/factory_data_overview.png rename to docs/guides/ti/matter-users-guide/images/factory_data_overview.png diff --git a/docs/guides/ti/ti_factory_data_user_guide.md b/docs/guides/ti/matter-users-guide/ti_factory_data_user_guide.md similarity index 91% rename from docs/guides/ti/ti_factory_data_user_guide.md rename to docs/guides/ti/matter-users-guide/ti_factory_data_user_guide.md index 50dfc41b15231d..4081b0239cda31 100644 --- a/docs/guides/ti/ti_factory_data_user_guide.md +++ b/docs/guides/ti/matter-users-guide/ti_factory_data_user_guide.md @@ -16,8 +16,7 @@ programming infrastructure for their Matter devices. TI Matter examples allow the use of factory data in the following two ways: - **Example Out of Box Factory Data** : Use TI example DAC values to get - started. This is intended to be used when just starting with Matter or - during development until customer or product specific data is not required. + started. This is intended to be used during development. - **Custom factory data** : Allows users to configure custom factory data via a JSON file. The custom values are then processed by a script provided by TI and merged with the Matter application to create a binary that can be @@ -50,9 +49,9 @@ Each element is described in more detail below: ## How to use -Out of box factory data location is configured to be on second last page of -flash. For CC13x4, the starting address is `0xFE800`. This can be configured in -the linker file. +Out of box factory data location is configured to be on second to last page of +flash. For CC13x4_CC26x4, the starting address is `0xFE800`. This can be +configured in the linker file. To configure: @@ -94,7 +93,7 @@ FLASH_FACTORY_DATA (R) : ORIGIN = 0x000fe800, LENGTH = 0x00000900 3. In the example's args.gni file, set 'custom_factory_data' to true -It is recommended to keep 2 dedicated pages for CC13x4 for factory data. +It is recommended to keep 2 dedicated pages for CC13x4_CC26x4 for factory data. ### Formatting certs and keys for JSON file diff --git a/docs/guides/ti/ti_openthread_library_usage.md b/docs/guides/ti/matter-users-guide/ti_openthread_library_usage.md similarity index 57% rename from docs/guides/ti/ti_openthread_library_usage.md rename to docs/guides/ti/matter-users-guide/ti_openthread_library_usage.md index 2b209fca44e744..d6412c729d69fa 100644 --- a/docs/guides/ti/ti_openthread_library_usage.md +++ b/docs/guides/ti/matter-users-guide/ti_openthread_library_usage.md @@ -1,19 +1,27 @@ -# Configuring Openthread libraries on TI CC13x4 Platforms +# Configuring OpenThread libraries on TI CC13x4_CC26x4 Platforms ## Overview -TI Matter example applications provide configuration option for how Thread code -is included in the build. Thread code can be included in as (1) full source or -(2) as optimized for matter thread certified library. The library builds have -been optimized for matter to disable features such as Thread Joiner capability -in order to save on Flash/RAM usage. +TI Matter example applications provide configuration options for how Thread code +is included in the build. Thread code can be included in as: + +1. Thread certified library optimized for Matter (recommended for development + and production) +2. Full Source (can be used for development) +3. Custom option where customers can update the Thread config file to change the + included OpenThread feature set + +The library builds have been optimized for Matter to disable features such as +Thread Joiner capability in order to save on Flash/RAM usage. Refer to the +`ti_matter_production_certification.md` to get the cert ID for your Matter +certification application when using the certified Thread libraries from TI. Build arguments have been added to enable Matter applications to link against -certified Openthread FTD/MTD libraries or custom user libraries. +certified OpenThread FTD/MTD libraries or custom user libraries. ## Platform Code Changes -To configure a TI example to utilize either an Openthread source or library +To configure a TI example to utilize either an OpenThread source or library build, open up the `args.gni` file of the example: To configure the device as an FTD or MTD, set following parameter to either true @@ -23,7 +31,9 @@ or false respectively. chip_openthread_ftd = true ``` -- **Openthread Library From Source**: +- **TI Certified OpenThread Library**: + + - Typically this is used for development. 1. `ot_ti_lib_dir` Is set to an empty string @@ -31,18 +41,18 @@ chip_openthread_ftd = true ot_ti_lib_dir="" ``` - 2. `chip_openthread_target` Is set to an empty string + 2. `chip_openthread_target` Points to the TI certified library dependency ``` - chip_openthread_target = "" + chip_openthread_target = "${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert" ``` - 3. `openthread_external_platform` Points to the Openthread build dependency + 3. `openthread_external_platform` Points to the OpenThread build dependency ``` - openthread_external_platform="${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" + `openthread_external_platform="${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti"` ``` -- **TI Certified Openthread Library**: +- **OpenThread Library From Source**: 1. `ot_ti_lib_dir` Is set to an empty string @@ -50,18 +60,21 @@ chip_openthread_ftd = true ot_ti_lib_dir="" ``` - 2. `chip_openthread_target` Points to the TI certified library dependency + 2. `chip_openthread_target` Is set to an empty string ``` - chip_openthread_target = "${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert" + chip_openthread_target = "" ``` - 3. `openthread_external_platform` Points to the Openthread build dependency + 3. `openthread_external_platform` Points to the OpenThread build dependency ``` - `openthread_external_platform="${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti"` + openthread_external_platform="${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" ``` -- **Custom Openthread Library**: +- **Custom OpenThread Library**: + + - The custom OpenThread library is used to implement extra features, or + when modifying the stack in anyway. 1. `ot_ti_lib_dir` Points to a library directory containing a custom `libopenthread-ftd/mtd` variant @@ -76,24 +89,24 @@ chip_openthread_ftd = true chip_openthread_target = "" ``` - 3. `openthread_external_platform` Points to the Openthread build dependency + 3. `openthread_external_platform` Points to the OpenThread build dependency ``` openthread_external_platform="${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" ``` -The TI Openthread certified library for the MTD/FTD configurations is based on +The TI OpenThread certified library for the MTD/FTD configurations is based on the following header file: - `third_party/openthread/ot-ti/src/openthread-core-cc13xx_cc26xx-config-matter.h` -In order to update the Openthread configuration when building from source or a +In order to update the OpenThread configuration when building from source or a custom library, users may adjust features via the following configuration header file: - `${chip_root}/examples/platform/cc13x4_26x4/project_include/OpenThreadConfig.h` -Please refer to TI's standalone Openthread Application build process for +Please refer to TI's standalone OpenThread Application build process for instructions on acquiring FTD/MTD libraries as they are automatically built when generating the standalone applications. diff --git a/docs/guides/ti/ti_matter_overview.md b/docs/guides/ti/ti_matter_overview.md new file mode 100644 index 00000000000000..44943fa46ba871 --- /dev/null +++ b/docs/guides/ti/ti_matter_overview.md @@ -0,0 +1,173 @@ +```{toctree} +:glob: +:maxdepth: 1 +:hidden: + +matter-migration-guide/matter_cc2674_migration.md +matter-syscfg/getting-started.md +matter-syscfg/sysconfig-board.md +matter-users-guide/ti_openthread_library_usage.md +matter-users-guide/ti_factory_data_user_guide.md +matter-users-guide/enabling_icd_on_ti_devices.md +``` + +# Texas Instruments platform overview + +The Texas Instruments Matter platform is based on the TI SimpleLink™ SDK. + +The following diagram is a simplified representation of a Matter application +which is built on the TI Platform. + +
+ +
+ +## Texas Instruments SimpleLink SDK + +This Matter GitHub repository contains the software development components and +tools that enable engineers to develop Matter based products. This repository is +the starting point for Matter development on all SimpleLink Thread and Wi-Fi +wireless microcontrollers (MCUs). + +The SimpleLink MCU portfolio offers a single development environment that +delivers flexible hardware, software, and tool options for customers developing +wired and wireless applications. With 100 percent code reuse across host MCUs, +Wi-Fi™, Bluetooth Low Energy, 2.4GHz, Sub-1GHz devices and more, choose the MCU +or connectivity standard that fits your design. A one-time investment with the +SimpleLink software development kit allows you to reuse often, opening the door +to create unlimited applications. For more information, visit +www.ti.com/simplelink. + +
+ +## Bluetooth LE and Thread stacks + +In the TI example applications the Bluetooth Low Energy protocol is used to +provision the Thread protocol to enable Matter communication. Then Thread is +used for IP communication with other Matter devices. + +The TI applications leverage the Bluetooth Low Energy stack on the CC13XX and +CC26XX families. This Bluetooth LE software is distributed in binary form within +the TI SimpleLink SDK. The Bluetooth LE stack leverages code that is present in +the device ROM for certain common Bluetooth LE operations. + +These applications leverage the OpenThread stack available within the Matter +repository for Thread communication. Platform support source is built from the +SimpleLink SDK. + +These connection protocols can be run concurrently by using the Texas +Instruments Dynamic Multi-protocol Manager. + +
+ +## LwIP stack + +The Lightweight IP stack interfaces with the OpenThread stack to offer standard +IP connectivity protocols that OpenThread does not natively support. This offers +a standard socket based interface to the Matter platform. + +
+ +## MbedTLS + +The MbedTLS library is used by OpenThread and Matter for a wide variety of +protocols. This ranges from basic AES and SHA to cryptographic protocols like +ECDSA and ECDH. + +The MbedTLS library is hardware accelerated using the TI SimpleLink SDK drivers. +This is achieved through the usage of `_ALT` defines in the MbedTLS +configuration file. + +
+ +## Matter Stack to TI Platform Interface + +Matter Stack interacts with LwIP, OpenThread, and the TI-BLE stack to achieve +the protocol and application functionality. A Bluetooth LE profile is registered +with the TI-BLE stack to enable provisioning and configuration. Once the device +is provisioned Matter will configure the OpenThread interface to connect to an +existing Thread network or to start its own network. From there the Matter IP +messages are sent to the LwIP stack to be routed to the OpenThread stack for +transmission. Matter Impl (Implementation) layer acts as an interface between +Matter stack and the TI platform components such as BLE stack, OpenThread, +FreeRTOS. It also supports components such as connectivity manager that provides +the implementation for functionality required by Matter stack. Overall, +applications generally only need to interface with the Cluster Library from +Matter. The transport of messages and configuration of the device is all handled +by the platform implementation files. + +
+ +## Matter Development Resources + +Below are several resources available for Matter development: + +- [Matter Protocol Overview](https://handbook.buildwithmatter.com/howitworks/roles/) +- [Matter Build Guide](../BUILDING.md) +- [Matter over Thread Getting Started](https://dev.ti.com/tirex/explore/node?node=A__AciOYyNq9gli.nsvJzBtQg__com.ti.SIMPLELINK_ACADEMY_CC13XX_CC26XX_SDK__AfkT0vQ__LATEST) +- [TI Matter over Wi-Fi Getting Started](https://e2e.ti.com/support/wireless-connectivity/wi-fi-group/wifi/f/wi-fi-forum/1122413/faq-cc3235sf-matter----getting-started-guide) +- [TI Matter Application Development](https://dev.ti.com/tirex/explore/node?node=A__AXNOPYikmtBCHJ-L6eRivA__com.ti.SIMPLELINK_ACADEMY_CC13XX_CC26XX_SDK__AfkT0vQ__LATEST) +- [TI Matter OTA Guide](https://dev.ti.com/tirex/explore/node?node=A__AYTiKtu5heqgH4KPFa.6RQ__com.ti.SIMPLELINK_ACADEMY_CC13XX_CC26XX_SDK__AfkT0vQ__LATEST) + +
+ +## Matter Example Applications + +Sample Matter applications are provided for the TI platform. These can be used +as reference for your own application. + +- [lock-app](../../../examples/lock-app/cc13x4_26x4/README.md) +- [pump-app](../../../examples/pump-app/cc13x4_26x4/README.md) +- [pump-controller-app](../../../examples/pump-controller-app/cc13x4_26x4/README.md) +- [lighting-app](../../../examples/lighting-app/cc13x4_26x4/README.md) +
+ +### Build system + +The TI platform uses GN to generate ninja build scripts. Build files have +already been written to build and link the TI specific code within the +SimpleLink SDK. + +
+ +## CC2674 Migration + +For instructions on how to migrate the CC1354P10-6 examples to either the +CC2674P10 or the CC2674R10, please refer to the guide linked below. + +- [TI CC2674 Migration Guide](./matter-migration-guide/matter_cc2674_migration.md) + +
+ +## Factory Data Programming Tool + +For instructions on how to program custom factory data on TI devices, please +refer to the guide linked below. + +- [TI Factory Data User Guide](./matter-users-guide/ti_factory_data_user_guide.md) + +
+ +## Intermittently Connected Devices + +For instructions on how to use the Matter ICD feature on TI devices, please +refer to the guide linked below. + +- [Enabling ICD On TI Devices](./matter-users-guide/enabling_icd_on_ti_devices.md) + +
+ +## Matter OpenThread Library Configuration + +For instructions on how to configure the OpenThread build configuration for a +Matter application, please refer to the guide linked below. + +- [OpenThread Library Configuration](./matter-users-guide/ti_openthread_library_usage.md) + +### TI Support + +For technical support, please consider creating a post on TI's [E2E forum][e2e]. +Additionally, we welcome any feedback. + +[e2e]: https://e2e.ti.com/support/wireless-connectivity/zigbee-and-thread +[matter_gh]: https://github.com/project-chip/connectedhomeip diff --git a/docs/guides/ti/ti_platform_overview.md b/docs/guides/ti/ti_platform_overview.md deleted file mode 100644 index bae464ac107224..00000000000000 --- a/docs/guides/ti/ti_platform_overview.md +++ /dev/null @@ -1,162 +0,0 @@ -```{toctree} -:glob: -:maxdepth: 1 - -* -``` - -# Texas Instruments platform overview - -The TI platform is a [Matter][matter_gh] platform based on the Texas Instruments -Incorporated SimpleLink SDK. - -The following diagram is a simplified representation of a Matter application -which built on the TI Platform. - -![matter_ti_overview_simplified](./../images/matter_ti_overview_simplified.png) - -## Texas Instruments SimpleLink SDK - -The SimpleLink™ CC13xx and CC26xx Software Development Kit (SDK) delivers -components that enable engineers to develop applications on the Texas -Instruments SimpleLink CC13xx and CC26xx family of wireless microcontrollers -(MCUs). This software toolkit provides a cohesive and consistent software -experience for all SimpleLink CC13xx and CC26xx wireless MCU users by packaging -essential software components, such as a Bluetooth® Low Energy (BLE) protocol -stack supporting Bluetooth 5.2, Bluetooth Mesh, Thread 1.1.1 networking stack -based on OpenThread, Zigbee 3.0 compliant protocol suite, RF-Proprietary -examples, TI’s 15.4 Stack as well as the TI-RTOS kernel and TI Drivers in one -easy-to-use software package along with example applications and documentation. -In addition, the Dynamic Multi-Protocol Manager (DMM) software component enables -multiprotocol development on a single SimpleLink wireless MCU through -time-division multiplexing. - -The SimpleLink MCU portfolio offers a single development environment that -delivers flexible hardware, software, and tool options for customers developing -wired and wireless applications. With 100 percent code reuse across host MCUs, -Wi-Fi™, Bluetooth Low Energy, Sub-1GHz devices and more, choose the MCU or -connectivity standard that fits your design. A one-time investment with the -SimpleLink software development kit allows you to reuse often, opening the door -to create unlimited applications. For more information, visit -www.ti.com/simplelink. - -
- -## BLE and Thread stacks - -In the TI example applications the Bluetooth Low Energy protocol is used to -provision the Thread protocol to enable Matter communication. Then Thread is -used for IP communication with other Matter devices. - -The TI applications leverage the Bluetooth Low Energy stack on the CC13XX and -CC26XX families. This BLE software is distributed in binary form within the TI -SimpleLink SDK. The BLE stack leverages code that is present in the device ROM -for certain common BLE operations. - -These applications leverage the OpenThread stack available within the Matter -repository for Thread communication. Platform support source is built from the -SimpleLink SDK. - -These connection protocols can be run concurrently by using the Texas -Instruments Dynamic Multi-protocol Manager. - -
- -## LwIP stack - -The Lightweight IP stack interfaces with the OpenThread stack to offer standard -IP connectivity protocols that OpenThread does not natively support. This offers -a standard socket based interface to the Matter platform. - -
- -## MbedTLS - -The MbedTLS library is used by OpenThread and Matter for a wide variety of -protocols. This ranges from basic AES and SHA to cryptographic protocols like -ECDSA and ECDH. - -The MbedTLS library is hardware accelerated using the TI SimpleLink SDK drivers. -This is achieved through the usage of `_ALT` defines in the MbedTLS -configuration file. - -
- -## Matter integration - -Matter interacts with LwIP, OpenThread, and the TI BLE stack to achieve the -protocol and application functionality. A BLE profile is registered with the -TI-BLE stack to enable provisioning and configuration. Once the device is -provisioned Matter will configure the OpenThread interface to connect to an -existing Thread network or to start its own network. From there the Matter IP -messages are sent to the LwIP stack to be routed to the OpenThread stack for -transmission. - -Overall, applications generally only need to interface with the Cluster Library -from Matter. The transport of messages and configuration of the device is all -handled by the platform implementation files. - -
- -## Matter example applications - -Sample Matter applications are provided for the TI platform. These can be used -as reference for your own application. - -- [lock-app](../../../examples/lock-app/cc13x4_26x4/README.md) -- [pump-app](../../../examples/pump-app/cc13x4_26x4/README.md) -- [pump-controller-app](../../../examples/pump-controller-app/cc13x4_26x4/README.md) - -
- -### Build system - -The TI platform uses GN to generate ninja build scripts. Build files have -already been written to build and link the TI specific code within the -SimpleLink SDK. - -
- -## CC2674 Migration - -For instructions on how to migrate the CC1354P10-6 examples to either the -CC2674P10 or the CC2674R10, please refer to the guide linked below. - -- [TI CC2674 Migration Guide](./matter_cc2674_migration.md) - -
- -## Factory Data Programming Tool - -For instructions on how to program custom factory data on TI devices, please -refer to the guide linked below. - -- [TI Factory Data User Guide](./ti_factory_data_user_guide.md) - -
- -## Intermittently Connected Devices - -For instructions on how to use Matters' ICD feature on TI devices, please refer -to the guide linked below. - -- [Enabling ICD On TI Devices](./enabling_icd_on_ti_devices.md) - -
- -## Matter Openthread Library Configuration - -For instructions on how to configure the Openthread build configuration for a -Matter application, please refer to the guide linked below. - -- [Openthread Library Configuration](./ti_openthread_library_usage.md) - -
- -### TI Support - -For technical support, please consider creating a post on TI's [E2E forum][e2e]. -Additionally, we welcome any feedback. - -[e2e]: https://e2e.ti.com/support/wireless-connectivity/zigbee-and-thread -[matter_gh]: https://github.com/project-chip/connectedhomeip diff --git a/docs/images/ti_logo.png b/docs/images/ti_logo.png new file mode 100644 index 00000000000000..e860023a5d60ba Binary files /dev/null and b/docs/images/ti_logo.png differ diff --git a/examples/all-clusters-app/cc13x4_26x4/BUILD.gn b/examples/all-clusters-app/cc13x4_26x4/BUILD.gn index be89a8b3aecd80..2a5609fb98b928 100644 --- a/examples/all-clusters-app/cc13x4_26x4/BUILD.gn +++ b/examples/all-clusters-app/cc13x4_26x4/BUILD.gn @@ -78,6 +78,7 @@ ti_simplelink_executable("all-clusters-app") { "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", "${chip_root}/examples/providers/DeviceInfoProviderImpl.cpp", + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${project_dir}/main/AppTask.cpp", "${project_dir}/main/ClusterManager.cpp", "${project_dir}/main/Globals.cpp", @@ -85,6 +86,10 @@ ti_simplelink_executable("all-clusters-app") { "${project_dir}/main/main.cpp", ] + public = [ + "${chip_root}/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.h", + ] + deps = [ ":sdk", ":sysconfig", diff --git a/examples/all-clusters-app/cc13x4_26x4/README.md b/examples/all-clusters-app/cc13x4_26x4/README.md index 4cb76380ce2df4..be6a38277edafc 100644 --- a/examples/all-clusters-app/cc13x4_26x4/README.md +++ b/examples/all-clusters-app/cc13x4_26x4/README.md @@ -65,10 +65,16 @@ guide assumes that the environment is linux based, and recommends Ubuntu 20.04. ``` - Run the bootstrap script to setup the build environment. +- Note, a recursive submodule checkout is required to utilize TI's Openthread + reference commit. +- Note, in order to build the chip-tool and ota-provider examples, a recursive + submodule checkout is required for the linux platform as seen in the command + below. ``` $ cd ~/connectedhomeip $ source ./scripts/bootstrap.sh + $ ./scripts/checkout_submodules.py --shallow --platform cc13xx_26xx linux --recursive ``` diff --git a/examples/all-clusters-app/cc13x4_26x4/args.gni b/examples/all-clusters-app/cc13x4_26x4/args.gni index bebfb47fa12665..56b7504ebf03f5 100644 --- a/examples/all-clusters-app/cc13x4_26x4/args.gni +++ b/examples/all-clusters-app/cc13x4_26x4/args.gni @@ -32,7 +32,11 @@ chip_enable_ota_requestor = true openthread_external_platform = "${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" -chip_openthread_target = "" + +# When using TI Certified Openthread libs set to ${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert +# For source builds, set to empty string. +chip_openthread_target = + "${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert" chip_openthread_ftd = true @@ -61,3 +65,8 @@ chip_persist_subscriptions = false chip_subscription_timeout_resumption = false freertos_root = "//third_party/connectedhomeip/third_party/ti_simplelink_sdk/repo_cc13xx_cc26xx/source/third_party/freertos" + +if (chip_openthread_target != "") { + openthread_root = + "//third_party/connectedhomeip/third_party/openthread/ot-ti/openthread" +} diff --git a/examples/all-clusters-app/cc13x4_26x4/chip.syscfg b/examples/all-clusters-app/cc13x4_26x4/chip.syscfg index e4ae2b6acfbbd2..16ce3520e42674 100644 --- a/examples/all-clusters-app/cc13x4_26x4/chip.syscfg +++ b/examples/all-clusters-app/cc13x4_26x4/chip.syscfg @@ -89,8 +89,8 @@ CCFG.enableCodeGeneration = false; /* NVS */ NVS1.$name = "CONFIG_NVSINTERNAL"; -NVS1.internalFlash.regionBase = 0xFB800; -NVS1.internalFlash.regionSize = 0x2800; +NVS1.internalFlash.regionBase = 0xF8800; +NVS1.internalFlash.regionSize = 0x6000; NVS2.$name = "CONFIG_NVSEXTERNAL"; diff --git a/examples/all-clusters-app/cc13x4_26x4/main/AppTask.cpp b/examples/all-clusters-app/cc13x4_26x4/main/AppTask.cpp index 6fc9642b8c063a..98926214c4cb36 100644 --- a/examples/all-clusters-app/cc13x4_26x4/main/AppTask.cpp +++ b/examples/all-clusters-app/cc13x4_26x4/main/AppTask.cpp @@ -45,6 +45,10 @@ #endif #include +#include +#include +#include + #include #include @@ -56,6 +60,8 @@ #define APP_EVENT_QUEUE_SIZE 10 #define BUTTON_ENABLE 1 +#define OTAREQUESTOR_INIT_TIMER_DELAY_MS 10000 + using namespace ::chip; using namespace ::chip::Credentials; using namespace ::chip::DeviceLayer; @@ -70,6 +76,12 @@ AppTask AppTask::sAppTask; constexpr EndpointId kNetworkCommissioningEndpointSecondary = 0xFFFE; +void StartTimer(uint32_t aTimeoutMs); +void CancelTimer(void); + +uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR static DefaultOTARequestor sRequestorCore; static DefaultOTARequestorStorage sRequestorStorage; @@ -90,6 +102,15 @@ void InitializeOTARequestor(void) } #endif +TimerHandle_t sOTAInitTimer = 0; + +// The OTA Init Timer is only started upon the first Thread State Change +// detected if the device is already on a Thread Network, or during the AppTask +// Init sequence if the device is not yet on a Thread Network. Once the timer +// has been started once, it does not need to be started again so the flag will +// be set to false. +bool isAppStarting = true; + #ifdef AUTO_PRINT_METRICS static void printMetrics(void) { @@ -132,6 +153,22 @@ void DeviceEventCallback(const ChipDeviceEvent * event, intptr_t arg) case DeviceEventType::kCommissioningComplete: PLAT_LOG("Commissioning complete"); break; + case DeviceEventType::kThreadStateChange: + PLAT_LOG("Thread State Change"); + bool isThreadAttached = ThreadStackMgrImpl().IsThreadAttached(); + + if (isThreadAttached) + { + PLAT_LOG("Device is on the Thread Network"); +#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR + if (isAppStarting) + { + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; + } +#endif + } + break; } #ifdef AUTO_PRINT_METRICS @@ -139,6 +176,11 @@ void DeviceEventCallback(const ChipDeviceEvent * event, intptr_t arg) #endif } +void OTAInitTimerEventHandler(TimerHandle_t xTimer) +{ + InitializeOTARequestor(); +} + int AppTask::StartAppTask() { int ret = 0; @@ -171,6 +213,9 @@ int AppTask::Init() // Init Chip memory management before the stack Platform::MemoryInit(); + PLAT_LOG("Software Version: %d", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION); + PLAT_LOG("Software Version String: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); + CHIP_ERROR ret = PlatformMgr().InitChipStack(); if (ret != CHIP_NO_ERROR) { @@ -179,6 +224,23 @@ int AppTask::Init() ; } + // Create FreeRTOS sw timer for OTA timer. + sOTAInitTimer = xTimerCreate("OTAInitTmr", // Just a text name, not used by the RTOS kernel + OTAREQUESTOR_INIT_TIMER_DELAY_MS, // timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = light obj context + OTAInitTimerEventHandler // timer callback handler + ); + + if (sOTAInitTimer == NULL) + { + PLAT_LOG("sOTAInitTimer timer create failed"); + } + else + { + PLAT_LOG("sOTAInitTimer timer created successfully "); + } + ret = ThreadStackMgr().InitThreadStack(); if (ret != CHIP_NO_ERROR) { @@ -202,14 +264,6 @@ int AppTask::Init() ; } - ret = PlatformMgr().StartEventLoopTask(); - if (ret != CHIP_NO_ERROR) - { - PLAT_LOG("PlatformMgr().StartEventLoopTask() failed"); - while (1) - ; - } - ret = ThreadStackMgrImpl().StartThreadTask(); if (ret != CHIP_NO_ERROR) { @@ -234,6 +288,9 @@ int AppTask::Init() // Init ZCL Data Model and start server PLAT_LOG("Initialize Server"); static chip::CommonCaseDeviceServerInitParams initParams; + static DefaultTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; + (void) initParams.InitializeStaticResourcesBeforeServerInit(); // Initialize info provider @@ -242,6 +299,14 @@ int AppTask::Init() chip::Server::GetInstance().Init(initParams); + ret = PlatformMgr().StartEventLoopTask(); + if (ret != CHIP_NO_ERROR) + { + PLAT_LOG("PlatformMgr().StartEventLoopTask() failed"); + while (1) + ; + } + ConfigurationMgr().LogDeviceConfig(); // We only have network commissioning on endpoint 0. @@ -251,9 +316,15 @@ int AppTask::Init() // this function will happen on the CHIP event loop thread, not the app_main thread. PlatformMgr().AddEventHandler(DeviceEventCallback, reinterpret_cast(nullptr)); + bool isThreadEnabled = ThreadStackMgrImpl().IsThreadEnabled(); + if (!isThreadEnabled && isAppStarting) + { #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR - InitializeOTARequestor(); + PLAT_LOG("Thread is Disabled, enable OTA Requestor"); + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; #endif + } // QR code will be used with CHIP Tool PrintOnboardingCodes(RendezvousInformationFlags(RendezvousInformationFlag::kBLE)); @@ -276,6 +347,32 @@ void AppTask::AppTaskMain(void * pvParameter) } } +void StartTimer(uint32_t aTimeoutMs) +{ + PLAT_LOG("Start OTA Init Timer") + if (xTimerIsTimerActive(sOTAInitTimer)) + { + PLAT_LOG("app timer already started!"); + CancelTimer(); + } + + // timer is not active, change its period to required value (== restart). + // FreeRTOS- Block for a maximum of 100 ticks if the change period command + // cannot immediately be sent to the timer command queue. + if (xTimerChangePeriod(sOTAInitTimer, pdMS_TO_TICKS(aTimeoutMs), 100) != pdPASS) + { + PLAT_LOG("sOTAInitTimer timer start() failed"); + } +} + +void CancelTimer(void) +{ + if (xTimerStop(sOTAInitTimer, 0) == pdFAIL) + { + PLAT_LOG("sOTAInitTimer stop() failed"); + } +} + void AppTask::PostEvent(const AppEvent * aEvent) { if (xQueueSend(sAppEventQueue, aEvent, 0) != pdPASS) diff --git a/examples/lighting-app/cc13x4_26x4/BUILD.gn b/examples/lighting-app/cc13x4_26x4/BUILD.gn index d3c0df34e6ce3d..38bb95ce8d53d7 100644 --- a/examples/lighting-app/cc13x4_26x4/BUILD.gn +++ b/examples/lighting-app/cc13x4_26x4/BUILD.gn @@ -65,12 +65,17 @@ ti_simplelink_executable("lighting_app") { sources = [ "${chip_root}/examples/providers/DeviceInfoProviderImpl.cpp", + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${project_dir}/src/AppTask.cpp", "${project_dir}/src/LightingManager.cpp", "${project_dir}/src/ZclCallbacks.cpp", "${project_dir}/src/main.cpp", ] + public = [ + "${chip_root}/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.h", + ] + deps = [ ":sdk", ":sysconfig", diff --git a/examples/lighting-app/cc13x4_26x4/README.md b/examples/lighting-app/cc13x4_26x4/README.md index 40dc195b218399..73718a0bf76a86 100644 --- a/examples/lighting-app/cc13x4_26x4/README.md +++ b/examples/lighting-app/cc13x4_26x4/README.md @@ -64,10 +64,16 @@ guide assumes that the environment is linux based, and recommends Ubuntu 20.04. ``` - Run the bootstrap script to setup the build environment. +- Note, a recursive submodule checkout is required to utilize TI's Openthread + reference commit. +- Note, in order to build the chip-tool and ota-provider examples, a recursive + submodule checkout is required for the linux platform as seen in the command + below. ``` $ cd ~/connectedhomeip $ source ./scripts/bootstrap.sh + $ ./scripts/checkout_submodules.py --shallow --platform cc13xx_26xx linux --recursive ``` @@ -101,7 +107,6 @@ Ninja to build the executable. If you would like to define arguments on the command line you may add them to the GN call. - ``` gn gen out/debug --args="ti_sysconfig_root=\"$HOME/ti/sysconfig_1.18.1\" target_defines=[\"CC13X4_26X4_ATTESTATION_CREDENTIALS=1\"]" ``` diff --git a/examples/lighting-app/cc13x4_26x4/args.gni b/examples/lighting-app/cc13x4_26x4/args.gni index 823c0385aeb643..2c9257e09e9a68 100644 --- a/examples/lighting-app/cc13x4_26x4/args.gni +++ b/examples/lighting-app/cc13x4_26x4/args.gni @@ -30,13 +30,16 @@ lwip_debug = false chip_enable_ota_requestor = true -chip_openthread_ftd = true +chip_openthread_ftd = false ot_ti_lib_dir = "" openthread_external_platform = "${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" -chip_openthread_target = "" +# When using TI Certified Openthread libs set to ${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert +# For source builds, set to empty string. +chip_openthread_target = + "${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert" # Disable CHIP Logging chip_progress_logging = true @@ -55,7 +58,7 @@ matter_device_pid = "0x8005" matter_software_ver = "0x0001" matter_software_ver_str = "1.0.1+1" -custom_factory_data = true +custom_factory_data = false # ICD Default configurations # when enabled the device will be configured as a sleepy end device @@ -64,3 +67,8 @@ chip_persist_subscriptions = false chip_subscription_timeout_resumption = false freertos_root = "//third_party/connectedhomeip/third_party/ti_simplelink_sdk/repo_cc13xx_cc26xx/source/third_party/freertos" + +if (chip_openthread_target != "") { + openthread_root = + "//third_party/connectedhomeip/third_party/openthread/ot-ti/openthread" +} diff --git a/examples/lighting-app/cc13x4_26x4/chip.syscfg b/examples/lighting-app/cc13x4_26x4/chip.syscfg index e4ae2b6acfbbd2..57ba89574d53cf 100644 --- a/examples/lighting-app/cc13x4_26x4/chip.syscfg +++ b/examples/lighting-app/cc13x4_26x4/chip.syscfg @@ -16,7 +16,6 @@ * limitations under the License. */ - /* Modules */ var AESCCM = scripting.addModule("/ti/drivers/AESCCM"); var AESECB = scripting.addModule("/ti/drivers/AESECB"); @@ -89,8 +88,8 @@ CCFG.enableCodeGeneration = false; /* NVS */ NVS1.$name = "CONFIG_NVSINTERNAL"; -NVS1.internalFlash.regionBase = 0xFB800; -NVS1.internalFlash.regionSize = 0x2800; +NVS1.internalFlash.regionBase = 0xF8800; +NVS1.internalFlash.regionSize = 0x6000; NVS2.$name = "CONFIG_NVSEXTERNAL"; diff --git a/examples/lighting-app/cc13x4_26x4/src/AppEvent.h b/examples/lighting-app/cc13x4_26x4/src/AppEvent.h index b6138ec479949c..3edcc4e4809faf 100644 --- a/examples/lighting-app/cc13x4_26x4/src/AppEvent.h +++ b/examples/lighting-app/cc13x4_26x4/src/AppEvent.h @@ -34,6 +34,7 @@ struct AppEvent kEventType_IdentifyStop, kEventType_Light, kEventType_Timer, + kEventType_Identify, }; enum AppEventButtonType diff --git a/examples/lighting-app/cc13x4_26x4/src/AppTask.cpp b/examples/lighting-app/cc13x4_26x4/src/AppTask.cpp index 5b5bb7d7df222f..aa9bdbccefa3c3 100644 --- a/examples/lighting-app/cc13x4_26x4/src/AppTask.cpp +++ b/examples/lighting-app/cc13x4_26x4/src/AppTask.cpp @@ -48,6 +48,10 @@ #include #include +#include +#include +#include + #include #include @@ -74,6 +78,8 @@ static uint32_t identify_trigger_effect = IDENTIFY_TRIGGER_EFFECT_FINISH_STOP; #endif #define BUTTON_ENABLE 1 +#define OTAREQUESTOR_INIT_TIMER_DELAY_MS 10000 + using namespace ::chip; using namespace ::chip::app; using namespace ::chip::Credentials; @@ -96,6 +102,12 @@ void uiTurnedOn(void); void uiTurnOff(void); void uiTurnedOff(void); +void StartTimer(uint32_t aTimeoutMs); +void CancelTimer(void); + +uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR static DefaultOTARequestor sRequestorCore; static DefaultOTARequestorStorage sRequestorStorage; @@ -116,6 +128,15 @@ void InitializeOTARequestor(void) } #endif +TimerHandle_t sOTAInitTimer = 0; + +// The OTA Init Timer is only started upon the first Thread State Change +// detected if the device is already on a Thread Network, or during the AppTask +// Init sequence if the device is not yet on a Thread Network. Once the timer +// has been started once, it does not need to be started again so the flag will +// be set to false. +bool isAppStarting = true; + ::Identify stIdentify = { LIGHTING_APPLICATION_IDENTIFY_ENDPOINT, AppTask::IdentifyStartHandler, AppTask::IdentifyStopHandler, Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, AppTask::TriggerIdentifyEffectHandler }; @@ -160,6 +181,45 @@ void identify_StopAction(void) #endif // LED_ENABLE } +void DeviceEventCallback(const ChipDeviceEvent * event, intptr_t arg) +{ + switch (event->Type) + { + case DeviceEventType::kCHIPoBLEConnectionEstablished: + PLAT_LOG("CHIPoBLE connection established"); + break; + + case DeviceEventType::kCHIPoBLEConnectionClosed: + PLAT_LOG("CHIPoBLE disconnected"); + break; + + case DeviceEventType::kCommissioningComplete: + PLAT_LOG("Commissioning complete"); + break; + case DeviceEventType::kThreadStateChange: + PLAT_LOG("Thread State Change"); + bool isThreadAttached = ThreadStackMgrImpl().IsThreadAttached(); + + if (isThreadAttached) + { + PLAT_LOG("Device is on the Thread Network"); +#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR + if (isAppStarting) + { + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; + } +#endif + } + break; + } +} + +void OTAInitTimerEventHandler(TimerHandle_t xTimer) +{ + InitializeOTARequestor(); +} + int AppTask::Init() { cc13xx_26xxLogInit(); @@ -167,14 +227,35 @@ int AppTask::Init() // Init Chip memory management before the stack Platform::MemoryInit(); + PLAT_LOG("Software Version: %d", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION); + PLAT_LOG("Software Version String: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); + CHIP_ERROR ret = PlatformMgr().InitChipStack(); if (ret != CHIP_NO_ERROR) { + PLAT_LOG("PlatformMgr().InitChipStack() failed"); while (1) ; } + // Create FreeRTOS sw timer for OTA timer. + sOTAInitTimer = xTimerCreate("OTAInitTmr", // Just a text name, not used by the RTOS kernel + OTAREQUESTOR_INIT_TIMER_DELAY_MS, // timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = light obj context + OTAInitTimerEventHandler // timer callback handler + ); + + if (sOTAInitTimer == NULL) + { + PLAT_LOG("sOTAInitTimer timer create failed"); + } + else + { + PLAT_LOG("sOTAInitTimer timer created successfully "); + } + ret = ThreadStackMgr().InitThreadStack(); if (ret != CHIP_NO_ERROR) { @@ -182,6 +263,7 @@ int AppTask::Init() while (1) ; } + #if CHIP_DEVICE_CONFIG_THREAD_FTD ret = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_Router); #elif CHIP_CONFIG_ENABLE_ICD_SERVER @@ -197,14 +279,6 @@ int AppTask::Init() ; } - ret = PlatformMgr().StartEventLoopTask(); - if (ret != CHIP_NO_ERROR) - { - PLAT_LOG("PlatformMgr().StartEventLoopTask() failed"); - while (1) - ; - } - ret = ThreadStackMgrImpl().StartThreadTask(); if (ret != CHIP_NO_ERROR) { @@ -229,6 +303,9 @@ int AppTask::Init() // Init ZCL Data Model and start server PLAT_LOG("Initialize Server"); static CommonCaseDeviceServerInitParams initParams; + static DefaultTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; + (void) initParams.InitializeStaticResourcesBeforeServerInit(); // Initialize info provider @@ -237,6 +314,16 @@ int AppTask::Init() Server::GetInstance().Init(initParams); + ret = PlatformMgr().StartEventLoopTask(); + if (ret != CHIP_NO_ERROR) + { + PLAT_LOG("PlatformMgr().StartEventLoopTask() failed"); + while (1) + ; + } + + PlatformMgr().AddEventHandler(DeviceEventCallback, reinterpret_cast(nullptr)); + uiInit(); ret = LightMgr().Init(); @@ -252,9 +339,15 @@ int AppTask::Init() ConfigurationMgr().LogDeviceConfig(); + bool isThreadEnabled = ThreadStackMgrImpl().IsThreadEnabled(); + if (!isThreadEnabled && isAppStarting) + { #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR - InitializeOTARequestor(); + PLAT_LOG("Thread is Disabled, enable OTA Requestor"); + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; #endif + } // QR code will be used with CHIP Tool PrintOnboardingCodes(RendezvousInformationFlags(RendezvousInformationFlag::kBLE)); @@ -278,6 +371,32 @@ void AppTask::AppTaskMain(void * pvParameter) } } +void StartTimer(uint32_t aTimeoutMs) +{ + PLAT_LOG("Start OTA Init Timer") + if (xTimerIsTimerActive(sOTAInitTimer)) + { + PLAT_LOG("app timer already started!"); + CancelTimer(); + } + + // timer is not active, change its period to required value (== restart). + // FreeRTOS- Block for a maximum of 100 ticks if the change period command + // cannot immediately be sent to the timer command queue. + if (xTimerChangePeriod(sOTAInitTimer, pdMS_TO_TICKS(aTimeoutMs), 100) != pdPASS) + { + PLAT_LOG("sOTAInitTimer timer start() failed"); + } +} + +void CancelTimer(void) +{ + if (xTimerStop(sOTAInitTimer, 0) == pdFAIL) + { + PLAT_LOG("sOTAInitTimer stop() failed"); + } +} + void AppTask::ActionInitiated(LightingManager::Action_t aAction, int32_t aActor) { if (aAction == LightingManager::ON_ACTION) @@ -406,6 +525,14 @@ void AppTask::DispatchEvent(AppEvent * aEvent) PLAT_LOG("Identify started"); break; + case AppEvent::kEventType_Identify: + // blink LED + PLAT_LOG("Identify cmd received, will blink green led three times now"); +#if (LED_ENABLE == 1) + LED_startBlinking(sAppGreenHandle, 250, 3); +#endif + break; + case AppEvent::kEventType_IdentifyStop: identify_StopAction(); PLAT_LOG("Identify stopped"); diff --git a/examples/lighting-app/cc13x4_26x4/src/LightingManager.cpp b/examples/lighting-app/cc13x4_26x4/src/LightingManager.cpp index ed5fc52e7e5638..cd84fe48b39056 100644 --- a/examples/lighting-app/cc13x4_26x4/src/LightingManager.cpp +++ b/examples/lighting-app/cc13x4_26x4/src/LightingManager.cpp @@ -174,3 +174,10 @@ void LightingManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) } } } + +void LightingManager::IdentifyEventHandler() +{ + AppEvent event; + event.Type = AppEvent::kEventType_Identify; + AppTask::GetAppTask().PostEvent(&event); +} diff --git a/examples/lighting-app/cc13x4_26x4/src/LightingManager.h b/examples/lighting-app/cc13x4_26x4/src/LightingManager.h index 818feed45ee87a..a87e3a3d806dc0 100644 --- a/examples/lighting-app/cc13x4_26x4/src/LightingManager.h +++ b/examples/lighting-app/cc13x4_26x4/src/LightingManager.h @@ -36,6 +36,7 @@ class LightingManager { ON_ACTION = 0, OFF_ACTION, + IDENTIFY_ACTION, INVALID_ACTION } Action; @@ -58,6 +59,7 @@ class LightingManager void SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB); static void OnTriggerOffWithEffect(OnOffEffect * effect); + void IdentifyEventHandler(); private: friend LightingManager & LightMgr(void); diff --git a/examples/lighting-app/cc13x4_26x4/src/ZclCallbacks.cpp b/examples/lighting-app/cc13x4_26x4/src/ZclCallbacks.cpp index 7bb20a45944545..8191935e0878f1 100644 --- a/examples/lighting-app/cc13x4_26x4/src/ZclCallbacks.cpp +++ b/examples/lighting-app/cc13x4_26x4/src/ZclCallbacks.cpp @@ -46,6 +46,7 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & { ChipLogProgress(Zcl, "Identify attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u", ChipLogValueMEI(attributeId), type, *value, size); + LightMgr().IdentifyEventHandler(); } else if (clusterId == Groups::Id) { diff --git a/examples/lock-app/cc13x4_26x4/BUILD.gn b/examples/lock-app/cc13x4_26x4/BUILD.gn index 638e26cafb683d..92c1fc8bf9acb1 100644 --- a/examples/lock-app/cc13x4_26x4/BUILD.gn +++ b/examples/lock-app/cc13x4_26x4/BUILD.gn @@ -73,12 +73,17 @@ ti_simplelink_executable("lock_app") { sources = [ "${chip_root}/examples/providers/DeviceInfoProviderImpl.cpp", + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${project_dir}/src/AppTask.cpp", "${project_dir}/src/LockManager.cpp", "${project_dir}/src/ZclCallbacks.cpp", "${project_dir}/src/main.cpp", ] + public = [ + "${chip_root}/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.h", + ] + deps = [ ":sdk", ":sysconfig", diff --git a/examples/lock-app/cc13x4_26x4/README.md b/examples/lock-app/cc13x4_26x4/README.md index b8b43d6ec6a218..7ff622a8e14f90 100644 --- a/examples/lock-app/cc13x4_26x4/README.md +++ b/examples/lock-app/cc13x4_26x4/README.md @@ -65,10 +65,16 @@ guide assumes that the environment is linux based, and recommends Ubuntu 20.04. ``` - Run the bootstrap script to setup the build environment. +- Note, a recursive submodule checkout is required to utilize TI's Openthread + reference commit. +- Note, in order to build the chip-tool and ota-provider examples, a recursive + submodule checkout is required for the linux platform as seen in the command + below. ``` $ cd ~/connectedhomeip $ source ./scripts/bootstrap.sh + $ ./scripts/checkout_submodules.py --shallow --platform cc13xx_26xx linux --recursive ``` @@ -102,7 +108,6 @@ Ninja to build the executable. If you would like to define arguments on the command line you may add them to the GN call. - ``` gn gen out/debug --args="ti_sysconfig_root=\"$HOME/ti/sysconfig_1.18.1\" target_defines=[\"CC13X4_26X4_ATTESTATION_CREDENTIALS=1\"]" ``` diff --git a/examples/lock-app/cc13x4_26x4/args.gni b/examples/lock-app/cc13x4_26x4/args.gni index ca53f6e3bbc603..c2e2e643a2f2ef 100644 --- a/examples/lock-app/cc13x4_26x4/args.gni +++ b/examples/lock-app/cc13x4_26x4/args.gni @@ -34,7 +34,11 @@ chip_openthread_ftd = true ot_ti_lib_dir = "" openthread_external_platform = "${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" -chip_openthread_target = "" + +# When using TI Certified Openthread libs set to ${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert +# For source builds, set to empty string. +chip_openthread_target = + "${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert" # Disable CHIP Logging chip_progress_logging = true @@ -63,3 +67,8 @@ chip_persist_subscriptions = false chip_subscription_timeout_resumption = false freertos_root = "//third_party/connectedhomeip/third_party/ti_simplelink_sdk/repo_cc13xx_cc26xx/source/third_party/freertos" + +if (chip_openthread_target != "") { + openthread_root = + "//third_party/connectedhomeip/third_party/openthread/ot-ti/openthread" +} diff --git a/examples/lock-app/cc13x4_26x4/chip.syscfg b/examples/lock-app/cc13x4_26x4/chip.syscfg index e4ae2b6acfbbd2..8a0283b5db3ee5 100644 --- a/examples/lock-app/cc13x4_26x4/chip.syscfg +++ b/examples/lock-app/cc13x4_26x4/chip.syscfg @@ -89,9 +89,8 @@ CCFG.enableCodeGeneration = false; /* NVS */ NVS1.$name = "CONFIG_NVSINTERNAL"; -NVS1.internalFlash.regionBase = 0xFB800; -NVS1.internalFlash.regionSize = 0x2800; - +NVS1.internalFlash.regionBase = 0xF8800; +NVS1.internalFlash.regionSize = 0x6000; NVS2.$name = "CONFIG_NVSEXTERNAL"; NVS2.nvsType = "External"; // NVS Region Type diff --git a/examples/lock-app/cc13x4_26x4/src/AppTask.cpp b/examples/lock-app/cc13x4_26x4/src/AppTask.cpp index 2a202ce432ed7a..b26bd6ae43f8c1 100644 --- a/examples/lock-app/cc13x4_26x4/src/AppTask.cpp +++ b/examples/lock-app/cc13x4_26x4/src/AppTask.cpp @@ -47,6 +47,10 @@ #include #include +#include +#include +#include + #include #include @@ -64,6 +68,8 @@ #endif #define BUTTON_ENABLE 1 +#define OTAREQUESTOR_INIT_TIMER_DELAY_MS 10000 + using namespace ::chip; using namespace ::chip::app; using namespace ::chip::Credentials; @@ -83,6 +89,17 @@ static DeviceInfoProviderImpl sExampleDeviceInfoProvider; AppTask AppTask::sAppTask; +void uiLocking(void); +void uiLocked(void); +void uiUnlocking(void); +void uiUnlocked(void); + +void StartTimer(uint32_t aTimeoutMs); +void CancelTimer(void); + +uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR static DefaultOTARequestor sRequestorCore; static DefaultOTARequestorStorage sRequestorStorage; @@ -103,6 +120,15 @@ void InitializeOTARequestor(void) } #endif +TimerHandle_t sOTAInitTimer = 0; + +// The OTA Init Timer is only started upon the first Thread State Change +// detected if the device is already on a Thread Network, or during the AppTask +// Init sequence if the device is not yet on a Thread Network. Once the timer +// has been started once, it does not need to be started again so the flag will +// be set to false. +bool isAppStarting = true; + ::Identify stIdentify = { 0, AppTask::IdentifyStartHandler, AppTask::IdentifyStopHandler, Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, AppTask::TriggerIdentifyEffectHandler }; @@ -129,11 +155,6 @@ int AppTask::StartAppTask() return ret; } -void uiLocking(void); -void uiLocked(void); -void uiUnlocking(void); -void uiUnlocked(void); - // Identify take action void identify_TakeAction(void) { @@ -152,6 +173,45 @@ void identify_StopAction(void) #endif // LED_ENABLE } +void DeviceEventCallback(const ChipDeviceEvent * event, intptr_t arg) +{ + switch (event->Type) + { + case DeviceEventType::kCHIPoBLEConnectionEstablished: + PLAT_LOG("CHIPoBLE connection established"); + break; + + case DeviceEventType::kCHIPoBLEConnectionClosed: + PLAT_LOG("CHIPoBLE disconnected"); + break; + + case DeviceEventType::kCommissioningComplete: + PLAT_LOG("Commissioning complete"); + break; + case DeviceEventType::kThreadStateChange: + PLAT_LOG("Thread State Change"); + bool isThreadAttached = ThreadStackMgrImpl().IsThreadAttached(); + + if (isThreadAttached) + { + PLAT_LOG("Device is on the Thread Network"); +#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR + if (isAppStarting) + { + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; + } +#endif + } + break; + } +} + +void OTAInitTimerEventHandler(TimerHandle_t xTimer) +{ + InitializeOTARequestor(); +} + int AppTask::Init() { cc13xx_26xxLogInit(); @@ -159,6 +219,9 @@ int AppTask::Init() // Init Chip memory management before the stack Platform::MemoryInit(); + PLAT_LOG("Software Version: %d", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION); + PLAT_LOG("Software Version String: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); + CHIP_ERROR ret = PlatformMgr().InitChipStack(); if (ret != CHIP_NO_ERROR) { @@ -167,6 +230,23 @@ int AppTask::Init() ; } + // Create FreeRTOS sw timer for OTA timer. + sOTAInitTimer = xTimerCreate("OTAInitTmr", // Just a text name, not used by the RTOS kernel + OTAREQUESTOR_INIT_TIMER_DELAY_MS, // timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = light obj context + OTAInitTimerEventHandler // timer callback handler + ); + + if (sOTAInitTimer == NULL) + { + PLAT_LOG("sOTAInitTimer timer create failed"); + } + else + { + PLAT_LOG("sOTAInitTimer timer created successfully "); + } + ret = ThreadStackMgr().InitThreadStack(); if (ret != CHIP_NO_ERROR) { @@ -189,14 +269,6 @@ int AppTask::Init() ; } - ret = PlatformMgr().StartEventLoopTask(); - if (ret != CHIP_NO_ERROR) - { - PLAT_LOG("PlatformMgr().StartEventLoopTask() failed"); - while (1) - ; - } - ret = ThreadStackMgrImpl().StartThreadTask(); if (ret != CHIP_NO_ERROR) { @@ -221,6 +293,9 @@ int AppTask::Init() // Init ZCL Data Model and start server PLAT_LOG("Initialize Server"); static CommonCaseDeviceServerInitParams initParams; + static DefaultTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; + (void) initParams.InitializeStaticResourcesBeforeServerInit(); // Initialize info provider @@ -229,8 +304,18 @@ int AppTask::Init() Server::GetInstance().Init(initParams); + ret = PlatformMgr().StartEventLoopTask(); + if (ret != CHIP_NO_ERROR) + { + PLAT_LOG("PlatformMgr().StartEventLoopTask() failed"); + while (1) + ; + } + uiInit(); + PlatformMgr().AddEventHandler(DeviceEventCallback, reinterpret_cast(nullptr)); + PlatformMgr().LockChipStack(); { uint8_t numberOfCredentialsPerUser = 0; @@ -299,9 +384,16 @@ int AppTask::Init() ConfigurationMgr().LogDeviceConfig(); + bool isThreadEnabled = ThreadStackMgrImpl().IsThreadEnabled(); + if (!isThreadEnabled && isAppStarting) + { #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR - InitializeOTARequestor(); + PLAT_LOG("Thread is Disabled, enable OTA Requestor"); + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; #endif + } + // QR code will be used with CHIP Tool PrintOnboardingCodes(RendezvousInformationFlags(RendezvousInformationFlag::kBLE)); @@ -326,6 +418,32 @@ void AppTask::AppTaskMain(void * pvParameter) } } +void StartTimer(uint32_t aTimeoutMs) +{ + PLAT_LOG("Start OTA Init Timer") + if (xTimerIsTimerActive(sOTAInitTimer)) + { + PLAT_LOG("app timer already started!"); + CancelTimer(); + } + + // timer is not active, change its period to required value (== restart). + // FreeRTOS- Block for a maximum of 100 ticks if the change period command + // cannot immediately be sent to the timer command queue. + if (xTimerChangePeriod(sOTAInitTimer, pdMS_TO_TICKS(aTimeoutMs), 100) != pdPASS) + { + PLAT_LOG("sOTAInitTimer timer start() failed"); + } +} + +void CancelTimer(void) +{ + if (xTimerStop(sOTAInitTimer, 0) == pdFAIL) + { + PLAT_LOG("sOTAInitTimer stop() failed"); + } +} + void AppTask::ActionInitiated(LockManager::Action_t aAction) { if (aAction == LockManager::LOCK_ACTION) @@ -426,7 +544,6 @@ void AppTask::DispatchEvent(AppEvent * aEvent) } } break; - case AppEvent::kEventType_IdentifyStart: identify_TakeAction(); PLAT_LOG("Identify started"); @@ -436,7 +553,6 @@ void AppTask::DispatchEvent(AppEvent * aEvent) identify_StopAction(); PLAT_LOG("Identify stopped"); break; - case AppEvent::kEventType_AppEvent: if (NULL != aEvent->Handler) { diff --git a/examples/platform/cc13x4_26x4/project_include/OpenThreadConfig.h b/examples/platform/cc13x4_26x4/project_include/OpenThreadConfig.h index c4087578b982c8..f1a569e012e4de 100644 --- a/examples/platform/cc13x4_26x4/project_include/OpenThreadConfig.h +++ b/examples/platform/cc13x4_26x4/project_include/OpenThreadConfig.h @@ -23,30 +23,6 @@ #pragma once -#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 22 -#define OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE 0 -#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0 -#define OPENTHREAD_CONFIG_DIAG_ENABLE 0 -#define OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 0 -#define OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE 1 -#define OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE 1 -#define OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE 1 -#define OPENTHREAD_CONFIG_MAC_FILTER_ENABLE 0 - -#define OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD 6 - -// TCP disabled until OpenThread has a GN/Ninja build for the tcplp library -#define OPENTHREAD_CONFIG_TCP_ENABLE 0 -#define OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS 0 -#define OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 0 -#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 0 -#define OPENTHREAD_CONFIG_COMMISSIONER_ENABLE 0 -#define OPENTHREAD_CONFIG_JOINER_ENABLE 0 -#define UART_AS_SERIAL_TRANSPORT 1 -#ifdef OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE -#undef OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE 0 -#endif #define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_APP // Use the TI-supplied default platform configuration for remainder #ifdef OPENTHREAD_PROJECT_CORE_CONFIG_FILE diff --git a/examples/pump-app/cc13x4_26x4/BUILD.gn b/examples/pump-app/cc13x4_26x4/BUILD.gn index 1429379e2bedb3..853b88288424aa 100644 --- a/examples/pump-app/cc13x4_26x4/BUILD.gn +++ b/examples/pump-app/cc13x4_26x4/BUILD.gn @@ -63,6 +63,7 @@ ti_simplelink_executable("pump_app") { output_name = "chip-${ti_simplelink_board}-pump-example.out" sources = [ + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${project_dir}/main/AppTask.cpp", "${project_dir}/main/CHIPDeviceManager.cpp", "${project_dir}/main/DeviceCallbacks.cpp", @@ -71,6 +72,10 @@ ti_simplelink_executable("pump_app") { "${project_dir}/main/main.cpp", ] + public = [ + "${chip_root}/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.h", + ] + deps = [ ":sdk", ":sysconfig", diff --git a/examples/pump-app/cc13x4_26x4/README.md b/examples/pump-app/cc13x4_26x4/README.md index 9c46aa697263ce..1e7e0c33b2136a 100644 --- a/examples/pump-app/cc13x4_26x4/README.md +++ b/examples/pump-app/cc13x4_26x4/README.md @@ -64,10 +64,16 @@ guide assumes that the environment is linux based, and recommends Ubuntu 20.04. ``` - Run the bootstrap script to setup the build environment. +- Note, a recursive submodule checkout is required to utilize TI's Openthread + reference commit. +- Note, in order to build the chip-tool and ota-provider examples, a recursive + submodule checkout is required for the linux platform as seen in the command + below. ``` $ cd ~/connectedhomeip $ source ./scripts/bootstrap.sh + $ ./scripts/checkout_submodules.py --shallow --platform cc13xx_26xx linux --recursive ``` diff --git a/examples/pump-app/cc13x4_26x4/args.gni b/examples/pump-app/cc13x4_26x4/args.gni index a1018dafc1f370..b14472acfe551d 100644 --- a/examples/pump-app/cc13x4_26x4/args.gni +++ b/examples/pump-app/cc13x4_26x4/args.gni @@ -33,7 +33,11 @@ chip_enable_ota_requestor = true ot_ti_lib_dir = "" openthread_external_platform = "${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" -chip_openthread_target = "" + +# When using TI Certified Openthread libs set to ${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert +# For source builds, set to empty string. +chip_openthread_target = + "${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert" chip_openthread_ftd = true @@ -62,3 +66,8 @@ chip_persist_subscriptions = false chip_subscription_timeout_resumption = false freertos_root = "//third_party/connectedhomeip/third_party/ti_simplelink_sdk/repo_cc13xx_cc26xx/source/third_party/freertos" + +if (chip_openthread_target != "") { + openthread_root = + "//third_party/connectedhomeip/third_party/openthread/ot-ti/openthread" +} diff --git a/examples/pump-app/cc13x4_26x4/chip.syscfg b/examples/pump-app/cc13x4_26x4/chip.syscfg index e4ae2b6acfbbd2..16ce3520e42674 100644 --- a/examples/pump-app/cc13x4_26x4/chip.syscfg +++ b/examples/pump-app/cc13x4_26x4/chip.syscfg @@ -89,8 +89,8 @@ CCFG.enableCodeGeneration = false; /* NVS */ NVS1.$name = "CONFIG_NVSINTERNAL"; -NVS1.internalFlash.regionBase = 0xFB800; -NVS1.internalFlash.regionSize = 0x2800; +NVS1.internalFlash.regionBase = 0xF8800; +NVS1.internalFlash.regionSize = 0x6000; NVS2.$name = "CONFIG_NVSEXTERNAL"; diff --git a/examples/pump-app/cc13x4_26x4/main/AppTask.cpp b/examples/pump-app/cc13x4_26x4/main/AppTask.cpp index 0aaa1c6b4029a7..9ec109313d3ad1 100644 --- a/examples/pump-app/cc13x4_26x4/main/AppTask.cpp +++ b/examples/pump-app/cc13x4_26x4/main/AppTask.cpp @@ -49,6 +49,10 @@ #include +#include +#include +#include + #include #include @@ -70,6 +74,8 @@ #endif #define BUTTON_ENABLE 1 +#define OTAREQUESTOR_INIT_TIMER_DELAY_MS 10000 + using namespace chip; using namespace chip::app; using namespace chip::Credentials; @@ -90,6 +96,12 @@ AppTask AppTask::sAppTask; static DeviceCallbacks sDeviceCallbacks; +void StartTimer(uint32_t aTimeoutMs); +void CancelTimer(void); + +uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR static DefaultOTARequestor sRequestorCore; static DefaultOTARequestorStorage sRequestorStorage; @@ -110,12 +122,60 @@ void InitializeOTARequestor(void) } #endif +TimerHandle_t sOTAInitTimer = 0; + +// The OTA Init Timer is only started upon the first Thread State Change +// detected if the device is already on a Thread Network, or during the AppTask +// Init sequence if the device is not yet on a Thread Network. Once the timer +// has been started once, it does not need to be started again so the flag will +// be set to false. +bool isAppStarting = true; + static const chip::EndpointId sIdentifyEndpointId = 0; static const uint32_t sIdentifyBlinkRateMs = 500; ::Identify stIdentify = { sIdentifyEndpointId, AppTask::IdentifyStartHandler, AppTask::IdentifyStopHandler, Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, AppTask::TriggerIdentifyEffectHandler }; +void DeviceEventCallback(const ChipDeviceEvent * event, intptr_t arg) +{ + switch (event->Type) + { + case DeviceEventType::kCHIPoBLEConnectionEstablished: + PLAT_LOG("CHIPoBLE connection established"); + break; + + case DeviceEventType::kCHIPoBLEConnectionClosed: + PLAT_LOG("CHIPoBLE disconnected"); + break; + + case DeviceEventType::kCommissioningComplete: + PLAT_LOG("Commissioning complete"); + break; + case DeviceEventType::kThreadStateChange: + PLAT_LOG("Thread State Change"); + bool isThreadAttached = ThreadStackMgrImpl().IsThreadAttached(); + + if (isThreadAttached) + { + PLAT_LOG("Device is on the Thread Network"); +#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR + if (isAppStarting) + { + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; + } +#endif + } + break; + } +} + +void OTAInitTimerEventHandler(TimerHandle_t xTimer) +{ + InitializeOTARequestor(); +} + int AppTask::StartAppTask() { int ret = 0; @@ -146,6 +206,9 @@ int AppTask::Init() // Init Chip memory management before the stack Platform::MemoryInit(); + PLAT_LOG("Software Version: %d", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION); + PLAT_LOG("Software Version String: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); + CHIP_ERROR ret = PlatformMgr().InitChipStack(); if (ret != CHIP_NO_ERROR) { @@ -154,6 +217,23 @@ int AppTask::Init() ; } + // Create FreeRTOS sw timer for OTA timer. + sOTAInitTimer = xTimerCreate("OTAInitTmr", // Just a text name, not used by the RTOS kernel + OTAREQUESTOR_INIT_TIMER_DELAY_MS, // timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = light obj context + OTAInitTimerEventHandler // timer callback handler + ); + + if (sOTAInitTimer == NULL) + { + PLAT_LOG("sOTAInitTimer timer create failed"); + } + else + { + PLAT_LOG("sOTAInitTimer timer created successfully "); + } + ret = ThreadStackMgr().InitThreadStack(); if (ret != CHIP_NO_ERROR) { @@ -185,6 +265,8 @@ int AppTask::Init() ; } + PlatformMgr().AddEventHandler(DeviceEventCallback, reinterpret_cast(nullptr)); + uiInit(); // Initialize device attestation config @@ -211,15 +293,26 @@ int AppTask::Init() #endif // Init ZCL Data Model - static chip::CommonCaseDeviceServerInitParams initParams; + PLAT_LOG("Initialize Server"); + static CommonCaseDeviceServerInitParams initParams; + static DefaultTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; + (void) initParams.InitializeStaticResourcesBeforeServerInit(); + chip::Server::GetInstance().Init(initParams); ConfigurationMgr().LogDeviceConfig(); + bool isThreadEnabled = ThreadStackMgrImpl().IsThreadEnabled(); + if (!isThreadEnabled && isAppStarting) + { #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR - InitializeOTARequestor(); + PLAT_LOG("Thread is Disabled, enable OTA Requestor"); + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; #endif + } // QR code will be used with CHIP Tool PrintOnboardingCodes(RendezvousInformationFlags(RendezvousInformationFlag::kBLE)); @@ -260,6 +353,32 @@ void AppTask::PostEvent(const AppEvent * aEvent) } } +void StartTimer(uint32_t aTimeoutMs) +{ + PLAT_LOG("Start OTA Init Timer") + if (xTimerIsTimerActive(sOTAInitTimer)) + { + PLAT_LOG("app timer already started!"); + CancelTimer(); + } + + // timer is not active, change its period to required value (== restart). + // FreeRTOS- Block for a maximum of 100 ticks if the change period command + // cannot immediately be sent to the timer command queue. + if (xTimerChangePeriod(sOTAInitTimer, pdMS_TO_TICKS(aTimeoutMs), 100) != pdPASS) + { + PLAT_LOG("sOTAInitTimer timer start() failed"); + } +} + +void CancelTimer(void) +{ + if (xTimerStop(sOTAInitTimer, 0) == pdFAIL) + { + PLAT_LOG("sOTAInitTimer stop() failed"); + } +} + void AppTask::ActionInitiated(PumpManager::Action_t aAction, int32_t aActor) { // If the action has been initiated by the pump, update the pump trait @@ -363,7 +482,7 @@ void AppTask::DispatchEvent(AppEvent * aEvent) PLAT_LOG("Disabled BLE Advertisements"); } } - else if (AppEvent::kAppEventButtonType_LongPressed == aEvent->ButtonEvent.Type) + else if (AppEvent::kAppEventButtonType_LongClicked == aEvent->ButtonEvent.Type) { chip::Server::GetInstance().ScheduleFactoryReset(); } diff --git a/examples/pump-app/cc13x4_26x4/main/include/AppEvent.h b/examples/pump-app/cc13x4_26x4/main/include/AppEvent.h index 3a9c606e95c330..cd5017a3bee60d 100644 --- a/examples/pump-app/cc13x4_26x4/main/include/AppEvent.h +++ b/examples/pump-app/cc13x4_26x4/main/include/AppEvent.h @@ -39,7 +39,6 @@ struct AppEvent kAppEventButtonType_None = 0, kAppEventButtonType_Clicked, kAppEventButtonType_LongClicked, - kAppEventButtonType_LongPressed, }; enum AppEventType Type; diff --git a/examples/pump-controller-app/cc13x4_26x4/BUILD.gn b/examples/pump-controller-app/cc13x4_26x4/BUILD.gn index 7227fe31adafcc..d495b6fb45b323 100644 --- a/examples/pump-controller-app/cc13x4_26x4/BUILD.gn +++ b/examples/pump-controller-app/cc13x4_26x4/BUILD.gn @@ -64,12 +64,17 @@ ti_simplelink_executable("pump_controller_app") { output_name = "chip-${ti_simplelink_board}-pump-controller-example.out" sources = [ + "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${project_dir}/main/AppTask.cpp", "${project_dir}/main/PumpManager.cpp", "${project_dir}/main/ZclCallbacks.cpp", "${project_dir}/main/main.cpp", ] + public = [ + "${chip_root}/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.h", + ] + deps = [ ":sdk", ":sysconfig", diff --git a/examples/pump-controller-app/cc13x4_26x4/README.md b/examples/pump-controller-app/cc13x4_26x4/README.md index bff60fdd3b1169..bd357fb25f2961 100644 --- a/examples/pump-controller-app/cc13x4_26x4/README.md +++ b/examples/pump-controller-app/cc13x4_26x4/README.md @@ -65,10 +65,16 @@ guide assumes that the environment is linux based, and recommends Ubuntu 20.04. ``` - Run the bootstrap script to setup the build environment. +- Note, a recursive submodule checkout is required to utilize TI's Openthread + reference commit. +- Note, in order to build the chip-tool and ota-provider examples, a recursive + submodule checkout is required for the linux platform as seen in the command + below. ``` $ cd ~/connectedhomeip $ source ./scripts/bootstrap.sh + $ ./scripts/checkout_submodules.py --shallow --platform cc13xx_26xx linux --recursive ``` @@ -82,6 +88,7 @@ Ninja to build the executable. ``` $ cd ~/connectedhomeip $ source ./scripts/activate.sh + $ ./scripts/checkout_submodules.py --shallow --platform cc13xx_26xx --recursive ``` @@ -247,7 +254,7 @@ Commissioning complete, notify platform driver to persist network credentials. Read generic vendor name from Basic cluster ``` -./chip-tool basic read vendor-name 1 0 +./chip-tool basicinformation read vendor-name 1 0 ``` ### Provisioning diff --git a/examples/pump-controller-app/cc13x4_26x4/args.gni b/examples/pump-controller-app/cc13x4_26x4/args.gni index dbbc3cd09f6b7e..d6af087119dfe5 100644 --- a/examples/pump-controller-app/cc13x4_26x4/args.gni +++ b/examples/pump-controller-app/cc13x4_26x4/args.gni @@ -33,7 +33,11 @@ chip_enable_ota_requestor = true ot_ti_lib_dir = "" openthread_external_platform = "${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" -chip_openthread_target = "" + +# When using TI Certified Openthread libs set to ${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert +# For source builds, set to empty string. +chip_openthread_target = + "${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert" chip_openthread_ftd = true @@ -62,3 +66,8 @@ chip_persist_subscriptions = false chip_subscription_timeout_resumption = false freertos_root = "//third_party/connectedhomeip/third_party/ti_simplelink_sdk/repo_cc13xx_cc26xx/source/third_party/freertos" + +if (chip_openthread_target != "") { + openthread_root = + "//third_party/connectedhomeip/third_party/openthread/ot-ti/openthread" +} diff --git a/examples/pump-controller-app/cc13x4_26x4/chip.syscfg b/examples/pump-controller-app/cc13x4_26x4/chip.syscfg index d0cdc696f0e832..81271837d1511d 100644 --- a/examples/pump-controller-app/cc13x4_26x4/chip.syscfg +++ b/examples/pump-controller-app/cc13x4_26x4/chip.syscfg @@ -89,8 +89,8 @@ CCFG.enableCodeGeneration = false; /* NVS */ NVS1.$name = "CONFIG_NVSINTERNAL"; -NVS1.internalFlash.regionBase = 0xFB800; -NVS1.internalFlash.regionSize = 0x2800; +NVS1.internalFlash.regionBase = 0xF8800; +NVS1.internalFlash.regionSize = 0x6000; NVS2.$name = "CONFIG_NVSEXTERNAL"; diff --git a/examples/pump-controller-app/cc13x4_26x4/main/AppTask.cpp b/examples/pump-controller-app/cc13x4_26x4/main/AppTask.cpp index fd44a3df454b57..a8d1e1d88f5cd3 100644 --- a/examples/pump-controller-app/cc13x4_26x4/main/AppTask.cpp +++ b/examples/pump-controller-app/cc13x4_26x4/main/AppTask.cpp @@ -44,6 +44,10 @@ #include +#include +#include +#include + #include #include @@ -61,6 +65,8 @@ #endif #define BUTTON_ENABLE 1 +#define OTAREQUESTOR_INIT_TIMER_DELAY_MS 10000 + using namespace ::chip; using namespace ::chip::app; using namespace ::chip::Credentials; @@ -77,6 +83,12 @@ static Button_Handle sAppRightHandle; AppTask AppTask::sAppTask; +void StartTimer(uint32_t aTimeoutMs); +void CancelTimer(void); + +uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR static DefaultOTARequestor sRequestorCore; static DefaultOTARequestorStorage sRequestorStorage; @@ -97,6 +109,15 @@ void InitializeOTARequestor(void) } #endif +TimerHandle_t sOTAInitTimer = 0; + +// The OTA Init Timer is only started upon the first Thread State Change +// detected if the device is already on a Thread Network, or during the AppTask +// Init sequence if the device is not yet on a Thread Network. Once the timer +// has been started once, it does not need to be started again so the flag will +// be set to false. +bool isAppStarting = true; + static const chip::EndpointId sIdentifyEndpointId = 0; static const uint32_t sIdentifyBlinkRateMs = 500; @@ -144,6 +165,45 @@ void identify_StopAction(void) #endif // LED_ENABLE } +void DeviceEventCallback(const ChipDeviceEvent * event, intptr_t arg) +{ + switch (event->Type) + { + case DeviceEventType::kCHIPoBLEConnectionEstablished: + PLAT_LOG("CHIPoBLE connection established"); + break; + + case DeviceEventType::kCHIPoBLEConnectionClosed: + PLAT_LOG("CHIPoBLE disconnected"); + break; + + case DeviceEventType::kCommissioningComplete: + PLAT_LOG("Commissioning complete"); + break; + case DeviceEventType::kThreadStateChange: + PLAT_LOG("Thread State Change"); + bool isThreadAttached = ThreadStackMgrImpl().IsThreadAttached(); + + if (isThreadAttached) + { + PLAT_LOG("Device is on the Thread Network"); +#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR + if (isAppStarting) + { + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; + } +#endif + } + break; + } +} + +void OTAInitTimerEventHandler(TimerHandle_t xTimer) +{ + InitializeOTARequestor(); +} + int AppTask::Init() { cc13xx_26xxLogInit(); @@ -151,6 +211,9 @@ int AppTask::Init() // Init Chip memory management before the stack chip::Platform::MemoryInit(); + PLAT_LOG("Software Version: %d", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION); + PLAT_LOG("Software Version String: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); + CHIP_ERROR ret = PlatformMgr().InitChipStack(); if (ret != CHIP_NO_ERROR) { @@ -159,6 +222,23 @@ int AppTask::Init() ; } + // Create FreeRTOS sw timer for OTA timer. + sOTAInitTimer = xTimerCreate("OTAInitTmr", // Just a text name, not used by the RTOS kernel + OTAREQUESTOR_INIT_TIMER_DELAY_MS, // timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = light obj context + OTAInitTimerEventHandler // timer callback handler + ); + + if (sOTAInitTimer == NULL) + { + PLAT_LOG("sOTAInitTimer timer create failed"); + } + else + { + PLAT_LOG("sOTAInitTimer timer created successfully "); + } + ret = ThreadStackMgr().InitThreadStack(); if (ret != CHIP_NO_ERROR) { @@ -182,14 +262,6 @@ int AppTask::Init() ; } - ret = PlatformMgr().StartEventLoopTask(); - if (ret != CHIP_NO_ERROR) - { - PLAT_LOG("PlatformMgr().StartEventLoopTask() failed"); - while (true) - ; - } - ret = ThreadStackMgrImpl().StartThreadTask(); if (ret != CHIP_NO_ERROR) { @@ -214,9 +286,22 @@ int AppTask::Init() // Init ZCL Data Model and start server PLAT_LOG("Initialize Server"); static chip::CommonCaseDeviceServerInitParams initParams; + static DefaultTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; + (void) initParams.InitializeStaticResourcesBeforeServerInit(); chip::Server::GetInstance().Init(initParams); + ret = PlatformMgr().StartEventLoopTask(); + if (ret != CHIP_NO_ERROR) + { + PLAT_LOG("PlatformMgr().StartEventLoopTask() failed"); + while (true) + ; + } + + PlatformMgr().AddEventHandler(DeviceEventCallback, reinterpret_cast(nullptr)); + uiInit(); // Initialize Pump module @@ -227,9 +312,15 @@ int AppTask::Init() ConfigurationMgr().LogDeviceConfig(); + bool isThreadEnabled = ThreadStackMgrImpl().IsThreadEnabled(); + if (!isThreadEnabled && isAppStarting) + { #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR - InitializeOTARequestor(); + PLAT_LOG("Thread is Disabled, enable OTA Requestor"); + StartTimer(OTAREQUESTOR_INIT_TIMER_DELAY_MS); + isAppStarting = false; #endif + } // QR code will be used with CHIP Tool PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)); @@ -261,6 +352,32 @@ void AppTask::PostEvent(const AppEvent * aEvent) } } +void StartTimer(uint32_t aTimeoutMs) +{ + PLAT_LOG("Start OTA Init Timer") + if (xTimerIsTimerActive(sOTAInitTimer)) + { + PLAT_LOG("app timer already started!"); + CancelTimer(); + } + + // timer is not active, change its period to required value (== restart). + // FreeRTOS- Block for a maximum of 100 ticks if the change period command + // cannot immediately be sent to the timer command queue. + if (xTimerChangePeriod(sOTAInitTimer, pdMS_TO_TICKS(aTimeoutMs), 100) != pdPASS) + { + PLAT_LOG("sOTAInitTimer timer start() failed"); + } +} + +void CancelTimer(void) +{ + if (xTimerStop(sOTAInitTimer, 0) == pdFAIL) + { + PLAT_LOG("sOTAInitTimer stop() failed"); + } +} + void AppTask::ActionInitiated(PumpManager::Action_t aAction, int32_t aActor) { // If the action has been initiated by the pump, update the pump trait @@ -355,7 +472,7 @@ void AppTask::DispatchEvent(AppEvent * aEvent) PLAT_LOG("Disabled BLE Advertisements"); } } - else if (AppEvent::kAppEventButtonType_LongPressed == aEvent->ButtonEvent.Type) + else if (AppEvent::kAppEventButtonType_LongClicked == aEvent->ButtonEvent.Type) { chip::Server::GetInstance().ScheduleFactoryReset(); } diff --git a/examples/pump-controller-app/cc13x4_26x4/main/include/AppEvent.h b/examples/pump-controller-app/cc13x4_26x4/main/include/AppEvent.h index 3a9c606e95c330..cd5017a3bee60d 100644 --- a/examples/pump-controller-app/cc13x4_26x4/main/include/AppEvent.h +++ b/examples/pump-controller-app/cc13x4_26x4/main/include/AppEvent.h @@ -39,7 +39,6 @@ struct AppEvent kAppEventButtonType_None = 0, kAppEventButtonType_Clicked, kAppEventButtonType_LongClicked, - kAppEventButtonType_LongPressed, }; enum AppEventType Type; diff --git a/examples/shell/cc13x4_26x4/README.md b/examples/shell/cc13x4_26x4/README.md index d3a6bc9374f83b..3f1923f3db33af 100644 --- a/examples/shell/cc13x4_26x4/README.md +++ b/examples/shell/cc13x4_26x4/README.md @@ -22,10 +22,13 @@ guide assumes that the environment is linux based, and recommends Ubuntu 20.04. ``` - Run the bootstrap script to setup the build environment. +- Note, a recursive submodule checkout is required to utilize TI's Openthread + reference commit. ``` $ cd ~/connectedhomeip $ source ./scripts/bootstrap.sh + $ ./scripts/checkout_submodules.py --shallow --platform cc13xx_26xx --recursive ``` diff --git a/examples/shell/cc13x4_26x4/args.gni b/examples/shell/cc13x4_26x4/args.gni index 631165271a0399..2fd57c3a580f69 100644 --- a/examples/shell/cc13x4_26x4/args.gni +++ b/examples/shell/cc13x4_26x4/args.gni @@ -32,7 +32,11 @@ chip_enable_ota_requestor = true chip_openthread_ftd = false openthread_external_platform = "${chip_root}/third_party/openthread/platforms/ti:libopenthread-ti" -chip_openthread_target = "" + +# When using TI Certified Openthread libs set to ${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert +# For source builds, set to empty string. +chip_openthread_target = + "${chip_root}/third_party/openthread/platforms/ti:ot-ti-cert" # Disable CHIP Logging chip_progress_logging = false @@ -61,3 +65,8 @@ chip_persist_subscriptions = false chip_subscription_timeout_resumption = false freertos_root = "//third_party/connectedhomeip/third_party/ti_simplelink_sdk/repo_cc13xx_cc26xx/source/third_party/freertos" + +if (chip_openthread_target != "") { + openthread_root = + "//third_party/connectedhomeip/third_party/openthread/ot-ti/openthread" +} diff --git a/examples/shell/cc13x4_26x4/chip.syscfg b/examples/shell/cc13x4_26x4/chip.syscfg index 725bac1377a875..6f4e021d328709 100644 --- a/examples/shell/cc13x4_26x4/chip.syscfg +++ b/examples/shell/cc13x4_26x4/chip.syscfg @@ -89,8 +89,8 @@ CCFG.enableCodeGeneration = false; /* NVS */ NVS1.$name = "CONFIG_NVSINTERNAL"; -NVS1.internalFlash.regionBase = 0xFB800; -NVS1.internalFlash.regionSize = 0x2800; +NVS1.internalFlash.regionBase = 0xF8800; +NVS1.internalFlash.regionSize = 0x6000; NVS2.$name = "CONFIG_NVSEXTERNAL"; diff --git a/examples/shell/cc13x4_26x4/main/AppTask.cpp b/examples/shell/cc13x4_26x4/main/AppTask.cpp index 83684b0fbcd592..86432279d5c8ad 100644 --- a/examples/shell/cc13x4_26x4/main/AppTask.cpp +++ b/examples/shell/cc13x4_26x4/main/AppTask.cpp @@ -136,13 +136,6 @@ CHIP_ERROR AppTask::Init() ; } - ret = PlatformMgr().StartEventLoopTask(); - if (ret != CHIP_NO_ERROR) - { - while (true) - ; - } - ret = ThreadStackMgrImpl().StartThreadTask(); if (ret != CHIP_NO_ERROR) { @@ -168,6 +161,13 @@ CHIP_ERROR AppTask::Init() (void) initParams.InitializeStaticResourcesBeforeServerInit(); chip::Server::GetInstance().Init(initParams); + ret = PlatformMgr().StartEventLoopTask(); + if (ret != CHIP_NO_ERROR) + { + while (true) + ; + } + #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR InitializeOTARequestor(); #endif diff --git a/src/platform/cc13xx_26xx/BLEManagerImpl.cpp b/src/platform/cc13xx_26xx/BLEManagerImpl.cpp index 912db7c6f81f57..7cc761f16d3b35 100644 --- a/src/platform/cc13xx_26xx/BLEManagerImpl.cpp +++ b/src/platform/cc13xx_26xx/BLEManagerImpl.cpp @@ -557,7 +557,7 @@ void BLEManagerImpl::EventHandler_init(void) CHIPoBLEProfile_AddService(GATT_ALL_SERVICES); // Start Bond Manager and register callback - VOID GAPBondMgr_Register(BLEMgr_BondMgrCBs); + VOID GAPBondMgr_Register(&BLEMgr_BondMgrCBs); // Register with GAP for HCI/Host messages. This is needed to receive HCI // events. For more information, see the HCI section in the User's Guide: @@ -950,9 +950,6 @@ void BLEManagerImpl::ProcessEvtHdrMsg(QueuedEvt_t * pMsg) case PAIR_STATE_EVT: { BLEMGR_LOG("BLEMGR: PAIR_STATE_EVT"); - - // Send passcode response - GAPBondMgr_PasscodeRsp(((PasscodeData_t *) (pMsg->pData))->connHandle, SUCCESS, B_APP_DEFAULT_PASSCODE); } break; diff --git a/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.cpp b/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.cpp new file mode 100644 index 00000000000000..69935bb024559d --- /dev/null +++ b/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.cpp @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DefaultTestEventTriggerDelegate.h" + +#include +#include + +namespace chip { + +bool DefaultTestEventTriggerDelegate::DoesEnableKeyMatch(const ByteSpan & enableKey) const +{ + return !mEnableKey.empty() && mEnableKey.data_equal(enableKey); +} + +} // namespace chip diff --git a/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.h b/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.h new file mode 100644 index 00000000000000..0bfd4c5b0fa725 --- /dev/null +++ b/src/platform/cc13xx_26xx/DefaultTestEventTriggerDelegate.h @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace chip { + +class DefaultTestEventTriggerDelegate : public TestEventTriggerDelegate +{ +public: + explicit DefaultTestEventTriggerDelegate(const ByteSpan & enableKey) : mEnableKey(enableKey) {} + + bool DoesEnableKeyMatch(const ByteSpan & enableKey) const override; + +private: + ByteSpan mEnableKey; +}; + +} // namespace chip diff --git a/src/platform/cc13xx_26xx/ThreadStackManagerImpl.cpp b/src/platform/cc13xx_26xx/ThreadStackManagerImpl.cpp index 46cb487eece86f..98855b5ea7037f 100644 --- a/src/platform/cc13xx_26xx/ThreadStackManagerImpl.cpp +++ b/src/platform/cc13xx_26xx/ThreadStackManagerImpl.cpp @@ -204,6 +204,16 @@ void ThreadStackManagerImpl::GetExtAddress(otExtAddress & aExtAddr) memcpy(aExtAddr.m8, extAddr->m8, OT_EXT_ADDRESS_SIZE); } +bool ThreadStackManagerImpl::IsThreadAttached() +{ + return _IsThreadAttached(); +} + +bool ThreadStackManagerImpl::IsThreadEnabled() +{ + return _IsThreadEnabled(); +} + } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/cc13xx_26xx/cc13x4_26x4/BUILD.gn b/src/platform/cc13xx_26xx/cc13x4_26x4/BUILD.gn index 0684d4eec8cde2..8fc4e76daaf6d4 100644 --- a/src/platform/cc13xx_26xx/cc13x4_26x4/BUILD.gn +++ b/src/platform/cc13xx_26xx/cc13x4_26x4/BUILD.gn @@ -32,6 +32,8 @@ static_library("cc13x4_26x4") { "../CC13XX_26XXConfig.h", "../ConfigurationManagerImpl.cpp", "../ConnectivityManagerImpl.cpp", + "../DefaultTestEventTriggerDelegate.cpp", + "../DefaultTestEventTriggerDelegate.h", "../DiagnosticDataProviderImpl.cpp", "../DiagnosticDataProviderImpl.h", "../KeyValueStoreManagerImpl.cpp", @@ -56,6 +58,7 @@ static_library("cc13x4_26x4") { ] public_deps = [ + "${chip_root}/src/app:test-event-trigger", "${chip_root}/src/crypto", "${chip_root}/src/platform:platform_base", ] diff --git a/src/platform/cc13xx_26xx/cc13x4_26x4/OTAImageProcessorImpl.cpp b/src/platform/cc13xx_26xx/cc13x4_26x4/OTAImageProcessorImpl.cpp index 94e5a2295aaaf7..060e2d5f7a2397 100644 --- a/src/platform/cc13xx_26xx/cc13x4_26x4/OTAImageProcessorImpl.cpp +++ b/src/platform/cc13xx_26xx/cc13x4_26x4/OTAImageProcessorImpl.cpp @@ -103,8 +103,7 @@ bool OTAImageProcessorImpl::IsFirstImageRun() requestor = GetRequestorInstance(); - return (requestor->GetTargetVersion() == runningSwVer) && - (requestor->GetCurrentUpdateState() == chip::app::Clusters::OtaSoftwareUpdateRequestor::OTAUpdateStateEnum::kApplying); + return (requestor->GetCurrentUpdateState() == chip::app::Clusters::OtaSoftwareUpdateRequestor::OTAUpdateStateEnum::kApplying); } /* makes room for the new block if needed */ @@ -167,7 +166,7 @@ static bool writeExtFlashImgPages(NVS_Handle handle, ssize_t offset, MutableByte } /* Erase the MCUBoot slot */ -#define BOOT_SLOT_SIZE (0x000F6000) /* must match flash_map_backend */ +#define BOOT_SLOT_SIZE (0x000F2000) /* must match flash_map_backend */ static bool eraseExtSlot(NVS_Handle handle) { int_fast16_t status; @@ -200,6 +199,22 @@ static bool eraseExtHeader(NVS_Handle handle) CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage() { + OTARequestorInterface * requestor = chip::GetRequestorInstance(); + if (requestor == nullptr) + { + return CHIP_ERROR_INTERNAL; + } + + uint32_t currentVersion; + uint32_t targetVersion = requestor->GetTargetVersion(); + ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(currentVersion)); + if (currentVersion != targetVersion) + { + ChipLogError(SoftwareUpdate, "Current software version = %" PRIu32 ", expected software version = %" PRIu32, currentVersion, + targetVersion); + return CHIP_ERROR_INCORRECT_STATE; + } + return CHIP_NO_ERROR; } @@ -268,6 +283,7 @@ void OTAImageProcessorImpl::HandleApply(intptr_t context) } /* reset SoC to kick MCUBoot */ + ChipLogProgress(SoftwareUpdate, "Resetting device to kick off MCUBoot"); SysCtrlSystemReset(); } diff --git a/src/platform/cc13xx_26xx/cc13x4_26x4/ThreadStackManagerImpl.h b/src/platform/cc13xx_26xx/cc13x4_26x4/ThreadStackManagerImpl.h index dc4127d7033af1..187627794965d9 100644 --- a/src/platform/cc13xx_26xx/cc13x4_26x4/ThreadStackManagerImpl.h +++ b/src/platform/cc13xx_26xx/cc13x4_26x4/ThreadStackManagerImpl.h @@ -95,6 +95,8 @@ class ThreadStackManagerImpl final : public ThreadStackManager, void _ProcMessage(otInstance * aInstance); void GetExtAddress(otExtAddress & aExtAddr); CHIP_ERROR GetBufferInfo(void); + bool IsThreadAttached(void); + bool IsThreadEnabled(void); private: // ===== Methods that implement the ThreadStackManager abstract interface. diff --git a/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos.lds b/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos.lds index f639f640d2414d..9ffd458366aded 100644 --- a/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos.lds +++ b/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos.lds @@ -212,9 +212,9 @@ SECTIONS { /* End of executable code/data, NVS is not part of the OTA image */ PROVIDE (_flash_end_address = .); - /* 5 pages of NV Memory (0x800 each) offset by 1 page for BIM/CCFG */ + /* 12 pages of NV Memory (0x800 each) offset by 1 page for BIM/CCFG */ /*.nvs (NOLOAD) : ALIGN(0x2000) { */ - .nvs (0xFB800) (NOLOAD) : AT (0xFB800) ALIGN(0x800) { + .nvs (0xF8800) (NOLOAD) : AT (0xF8000) ALIGN(0x800) { *(.nvs) } > REGION_TEXT diff --git a/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_factory_data.lds b/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_factory_data.lds index c082497d88429e..bf614d70c718c1 100644 --- a/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_factory_data.lds +++ b/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_factory_data.lds @@ -222,9 +222,9 @@ SECTIONS { /* End of executable code/data, NVS is not part of the OTA image */ PROVIDE (_flash_end_address = .); - /* 5 pages of NV Memory (0x800 each) offset by 1 page for BIM/CCFG */ + /* 12 pages of NV Memory (0x800 each) offset by 1 page for BIM/CCFG */ /*.nvs (NOLOAD) : ALIGN(0x2000) { */ - .nvs (0xFB000) (NOLOAD) : AT (0xFB000) ALIGN(0x800) { + .nvs (0xF8800) (NOLOAD) : AT (0xF8800) ALIGN(0x800) { *(.nvs) } > REGION_TEXT diff --git a/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_ota.lds b/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_ota.lds index a28b2669b3c0b8..c212cedafbaeb1 100644 --- a/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_ota.lds +++ b/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_ota.lds @@ -28,11 +28,13 @@ MEMORY MCUBOOT_HDR (RX) : ORIGIN = 0x00006000, LENGTH = 0x00000080 FLASH (RX) : ORIGIN = 0x00006080, LENGTH = 0x000F8780 /* - * NVS is the last 5 pages of slot, this area is not erased + * NVS is the last 12 pages of slot, this area is not erased * during OTA. The slot size for the primary and secondary slots - * is therefore 0xF6000 (MCUBoot Header + all the remaining space) + * is therefore 0xF2000 (MCUBoot Header + all the remaining space + * leaves 0xF2800 of space, but the slot size needs to be divisible + * by 4k so the slot size therefore becomes 0xF2000) */ - /* FLASH_NVS (RX) : ORIGIN = 0x000FC000, LENGTH = 0x00002800 */ + /* FLASH_NVS (RX) : ORIGIN = 0x000F8800, LENGTH = 0x00006000 */ FLASH_FACTORY_DATA (R) : ORIGIN = 0x000FE800, LENGTH = 0x900 /* * Customer Configuration Area and Bootloader Backdoor configuration in @@ -231,8 +233,8 @@ SECTIONS { /* End of executable code/data, NVS is not part of the OTA image */ PROVIDE (_flash_end_address = .); - /* 5 pages of NV Memory (0x800 each) at the end of Flash */ - .nvs (0xFC000) (NOLOAD) : AT (0xFC000) ALIGN(0x800) { + /* 12 pages of NV Memory (0x800 each) at the end of Flash */ + .nvs (0xF8800) (NOLOAD) : AT (0xF8800) ALIGN(0x800) { *(.nvs) } > REGION_TEXT diff --git a/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_ota_factory_data.lds b/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_ota_factory_data.lds index 1228a3cc38d3e0..d7e9996477fd73 100644 --- a/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_ota_factory_data.lds +++ b/src/platform/cc13xx_26xx/cc13x4_26x4/cc13x4_cc26x4_freertos_ota_factory_data.lds @@ -28,11 +28,13 @@ MEMORY MCUBOOT_HDR (RX) : ORIGIN = 0x00006000, LENGTH = 0x00000080 FLASH (RX) : ORIGIN = 0x00006080, LENGTH = 0x000F8780 /* - * NVS is the last 5 pages of slot, this area is not erased + * NVS is the last 12 pages of slot, this area is not erased * during OTA. The slot size for the primary and secondary slots - * is therefore 0xF6000 (MCUBoot Header + all the remaining space) + * is therefore 0xF2000 (MCUBoot Header + all the remaining space + * leaves 0xF2800 of space, but the slot size needs to be divisible + * by 4k so the slot size therefore becomes 0xF2000) */ - /* FLASH_NVS (RX) : ORIGIN = 0x000FC000, LENGTH = 0x00002800 */ + /* FLASH_NVS (RX) : ORIGIN = 0x000F8800, LENGTH = 0x00006000 */ FLASH_FACTORY_DATA (R) : ORIGIN = 0x000FE800, LENGTH = 0x900 /* * Customer Configuration Area and Bootloader Backdoor configuration in @@ -231,8 +233,8 @@ SECTIONS { /* End of executable code/data, NVS is not part of the OTA image */ PROVIDE (_flash_end_address = .); - /* 5 pages of NV Memory (0x800 each) at the end of Flash */ - .nvs (0xFC000) (NOLOAD) : AT (0xFC000) ALIGN(0x800) { + /* 12 pages of NV Memory (0x800 each) at the end of Flash */ + .nvs (0xF8800) (NOLOAD) : AT (0xF8800) ALIGN(0x800) { *(.nvs) } > REGION_TEXT diff --git a/src/platform/cc13xx_26xx/nvocmp.c b/src/platform/cc13xx_26xx/nvocmp.c new file mode 100644 index 00000000000000..a77a28977601eb --- /dev/null +++ b/src/platform/cc13xx_26xx/nvocmp.c @@ -0,0 +1,5201 @@ +/****************************************************************************** + + @file nvocmp.c + + @brief NV driver for CC26x2 devices - On-Chip Multi-Page Flash Memory + + Group: CMCU, LPC + Target Device: cc13xx_cc26xx + + ****************************************************************************** + + Copyright (c) 2023-2024, Texas Instruments Incorporated + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Texas Instruments Incorporated nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ****************************************************************************** + + + *****************************************************************************/ + +//***************************************************************************** +// Design Overview +//***************************************************************************** +/* +This driver implements a non-volatile (NV) memory system that utilizes multi pages +(consecutive) of on-chip Flash memory. After initialization, all pages except one +are ACTIVE and the remaining one page is available for "compaction" when the ACTIVE +pages do not have enough empty space for data write operation. Compaction can occur +'just in time' during a data write operation or 'on demand' by application request. +The compaction process is designed to survive a power cycle before it completes. It +will resume where it was interrupted and complete the process. + +This driver makes the following assumptions and uses them to optimize the code +and data storage design: (1) Flash memory is addressable at individual, 1-byte +resolution so no padding or word-alignment is necessary (2) Flash has limited +number of writes per flash 'sector' between erases. To prevent going over this +limit, "small" items are written in one operation. + +Each Flash page has a "page header" which indicates its current state, +located at the first byte of the Flash page and "compact header" which indicates +its compaction state, located following "page header". The remainder of +the Flash page contains NV data items which are packed together following the +page header and compact header. Each NV data item has two parts, (1) a data block +which is stored first (lower memory address), (2) immediately followed by item header +(higher memory address). The item header contains information necessary to traverse the +packed data items, as well as, current status of each data item. Obsolete items +marked accordingly but a search for the newest instance of an item is sped up +by starting the search at the last entry in the page (higher memory address). + +Each item is unique, addressed using three ID values (system ID, item ID, sub +ID). These three values are stored in the header along with a 'signature', a +CRC8 value, the length of the data block, and two status bits. The two status +bits indicate whether an item is still active and the health or validity of an +item. The signature byte is used by the driver to detect the presence of an +item, and is the same for all items as well as the page header and compact header. +The CRC8 value allows the driver to confirm the integrity of the items during +compaction and optionally when an item read operation is requested. The length +of the data block is used to jump from one item to the next. If this field is +corrupted, the driver is forced to search for items by signature and possibly +compute multiple CRC's to confirm it has found a valid item. Note that any +corruption event forces a compaction to recover. + +To reduce further RAM consumption, the user can define NVOCMP_RAM_OPTIMIZATION to +enable this feature. Note that for cc23x0 and cc27xx, NVOCMP_RAM_OPTIMIZATION is enabled +by default. Alternatively, if this optimization is not needed in a particular +application for cc23x0 and cc27xx, the user can define NVOCMP_NO_RAM_OPTIMIZATION to +effectively disable this feature for cc23x0 and cc27xx. + +When RAM optimization is enabled, the user can configure the size of the +working buffer by setting NVOCMP_RAM_BUFFER_SIZE, which defaults to 500. + +*/ +//***************************************************************************** +// Use / Configuration +//***************************************************************************** +/* +Since this is multi page NV driver, the number of NV pages is configurable. +NVOCMP_NVPAGES = 1 means 1 page storage and 0 compaction page. +NVOCMP_NVPAGES = 2 means 1 page storage and 1 compaction page. +NVOCMP_NVPAGES = 3 means 2 pages storage and 1 compaction page. +NVOCMP_NVPAGES = 4 means 3 pages storage and 1 compaction page. +NVOCMP_NVPAGES = 5 means 4 pages storage and 1 compaction page. +NVOCMP_NVPAGES = 6 means 5 pages storage and 1 compaction page. +NVOCMP_NVPAGES can be configured from project option. If this flag is not +configured, NVOCMP_NVPAGES = 2 will be by default. +"nvintf.h" describes the generic NV interface which is used to access NVOCMP +after initialization. Initialization is done by passing a function pointer +struct to one of NVOCMP pointer loader functions. Once this is done, the +pointer struct (which is part of the nvintf interface) should be used to call +the nvintf initialization function, which will initialize NVOCMP. At this point +NVOCMP is ready and loaded API functions can be called through the pointer +structure. Note that some pointers may be NULL depending on which NVOCMP +loader function was called. For example NVOCMP_loadApiPtrsMin() loads only +the essential functions to reduce code size at link time. +A sample code block is shown below: + +NVINTF_nvFuncts_t nvFps; +NVOCMP_loadApiPtrs(&nvFps); + +nvFps.initNV(NULL); +// Do some NV operations +nvFps.compactNV(NULL); +status = nvFps.readItem(id, 0, len, buf); + +Note: Each item operation results in a traversal of the page starting at +the most recently written item. This makes 'finding' items by 'trying' item IDs +in order extremely inefficient. The doNext() API call allows the user to find, +read, or delete items in one page traversal. However, this call requires the +user to lock access to NV until the operation is complete so it should be used +carefully and sparingly. + +Note: The compile flag NVDEBUG can be passed to enable ASSERT and ALERT +macros which provide assert and logging functionality. When this flag is used, +a printf function of the form void nvprint(char * str) MUST be +provided to link the driver. The function need not be functional, but it must +exist. NVDEBUG also exposes driver global variables for debug and testing. + +Not all user-defines (such as NVDEBUG) are supported when using NVOCMP in a +Linux environment. If debugging/logging functionality is required, the +"nv-debug" or "nv-rdwr" logging flags can be enabled in the cfg INI file. + +Configuration: +NVOCMP_STATS - Places a protected item with driver stats + +NVOCMP_CRCONREAD (on:1 off:0) - item crc is checked on read. Disabling this may +increase driver speed but safety is reduced. + +NVOCMP_NVS_INDEX - The index of the NVS_Config structure which describes the +flash sector that NVOCMP should use. Default is 0. + +NVOCMP_RECOVER_FROM_COMPACT_FAILURE - This define needs to be enabled by +the customer. It is disabled be default. When enabled, it causes the +NV driver to reformat all NV pages when there is an error while +collecting valid items for compaction. When disabled and an error +occurs while collecting valid items for compaction, NV pages will +preserve the original information prior to the start of such +operation. + +ENABLE_SANITY_CHECK - This define needs to be enabled if user needs the +NVOCMP_sanityCheckApi() to be available. This function is used to perform +a sanity check on the active partition to report if corruption has been +detected. + +NVOCMP_RAM_OPTIMIZATION - Enables RAM optimization. +NVOCMP_NO_RAM_OPTIMIZATION - Disables RAM optimization for cc23x0 and cc27xx, as it +is enabled by default on this family of devices. +NVOCMP_RAM_BUFFER_SIZE - Sets the size for the RAM buffer used when +RAM optimization is enabled. Default value is 500. + +Dependencies: +Requires NVS for NV access. +Requires TI-RTOS GateMutexPri or POSIX mutex to be enabled in configuration. +Requires API's in a crc.h to implement CRC functionality. +*/ + +//***************************************************************************** +// Includes +//***************************************************************************** + +#include +#ifdef NVOCMP_POSIX_MUTEX +#include +#elif defined(NVOCMP_POSIX_SEM) +#include +#else +#include +#endif +#include +#include +#ifndef NV_LINUX + +#include +#include + +#ifdef NVOCMP_MIN_VDD_FLASH_MV +#include +#endif +#endif + +#ifdef NV_LINUX +#include "nv_linux.h" +#endif + +//***************************************************************************** +// Constants and Definitions +//***************************************************************************** + +#define NVINTF_DONOWRAP 0x80 // Do not wrap around NV space +#define NVOCMP_FASTCP 1 // Fast Compaction by Skipping All Active Item Pages +#define NVOCMP_COMPR 0 // Order Change When Compaction +#define NVOCMP_HDRLE 0 // Little Endian Format Item Header +#define NVOCMP_FASTOFF 1 // Fast Search Offset +#define NVOCMP_FASTITEM 0 // Fast Find Item + +#ifndef NVOCMP_NWSAMEITEM +#define NVOCMP_NWSAMEITEM 0 // Not Write Same Item +#endif + +#ifndef NVOCMP_MIGRATE_ENABLED +#define NVOCMP_MIGRATE_DISABLED // Migration from old NVOCTP disabled by default +#endif + +#define NVOCMP_NVONEP 1 // One Page NV +#define NVOCMP_NVTWOP 2 // Two Page NV + +#define NVOCMP_NVSIZE NVOCMP_size +#define NVOCMP_ADDPAGE(p, n) (((p) + (n)) % NVOCMP_NVSIZE) +#define NVOCMP_INCPAGE(p) NVOCMP_ADDPAGE(p, 1) +#define NVOCMP_DECPAGE(p) NVOCMP_ADDPAGE(p, NVOCMP_NVSIZE - 1) +// Which NVS_config indice is used to initialize NVS. +#ifndef NVOCMP_NVS_INDEX +#define NVOCMP_NVS_INDEX 0 +#endif // NVOCMP_NVS_INDEX + +// Maximum ID parameters - must be coordinated with header format +#define NVOCMP_MAXSYSID 0x003F // 6 bits +#define NVOCMP_MAXITEMID 0x03FF // 10 bits +#define NVOCMP_MAXSUBID 0x03FF // 10 bits +#define NVOCMP_MAXLEN 0x0FFF // 12 bits +#define NVOCMP_INVALIDSUBID 0xFFFF // Invalid Sub Id + +// Contents of an erased Flash memory locations +#define NVOCMP_ERASEDBYTE 0xFF +#define NVOCMP_ERASEDWORD 0xFFFFFFFF + +// Size of byte +#define NVOCMP_ONEBYTE 1 + +// Compressed ID bit spacing +#define NVOCMP_CMPSPACE 12 + +// Invalid NV page - if 0xFF is ever used, change this definition +#define NVOCMP_NULLPAGE 0xFF + +// Block size for Flash-Flash XFER (Bytes) +#define NVOCMP_XFERBLKMAX 32 + +// Size in bytes of biggest item size that will be concatenated +// in RAM before write, instead of header/data written separately +#define NVOCMP_SMALLITEM 12 + +#if defined(NVOCMP_STATS) +// NV item ID for driver diagnostics +static const NVINTF_itemID_t diagId = NVOCMP_NVID_DIAG; +#endif // NVOCMP_STATS + +// CRC options +// When not NULL, reads will result in a CRC check before returning +#define NVOCMP_CRCONREAD 1 + +// findItem search types +// Find any item, item of spec'd sysid, item of spec'd sys and item id +// or find the exact item specified +enum +{ + NVOCMP_FINDANY = 0x00, + NVOCMP_FINDSYSID, + NVOCMP_FINDITMID, + NVOCMP_FINDSTRICT, + NVOCMP_FINDCONTENT = 0x10 +}; + +#define NVOCMP_FINDLMASK 0x0F +#define NVOCMP_FINDHMASK 0xF0 +//***************************************************************************** +// Macros +//***************************************************************************** + +// Makes an NV Flash address (for 0x2000 page size) +#define NVOCMP_FLASHOFFSET(pg, ofs) ((uint32_t) (((pg) << PAGE_SIZE_LSHIFT) + (ofs))) + +// Optional user provided function is called before writes/erases +// Intention is to check for sufficient voltage for operation +#ifndef NV_LINUX +#define NVOCMP_FLASHACCESS(err) \ + { \ + if (NVOCMP_voltCheckFptr) \ + { \ + if (!NVOCMP_voltCheckFptr()) \ + { \ + err = NVINTF_LOWPOWER; \ + } \ + } \ + } +#endif + +#ifdef NVOCMP_POSIX_MUTEX +// Lock driver access via TI-RTOS gatemutex +#define NVOCMP_LOCK() pthread_mutex_lock(&NVOCMP_gPosixMutex); + +// Unlock driver access via TI-RTOS gatemutex and return error code +#define NVOCMP_UNLOCK(err) \ + { \ + pthread_mutex_unlock(&NVOCMP_gPosixMutex); \ + return (err); \ + } +#elif defined(NVOCMP_POSIX_SEM) +// Lock driver access via POSIX semaphore +#define NVOCMP_LOCK() sem_wait(&NVOCMP_gPosixSem); + +// Unlock driver access via POSIX semaphore and return error code +#define NVOCMP_UNLOCK(err) \ + { \ + sem_post(&NVOCMP_gPosixSem); \ + return (err); \ + } +#else +// Lock driver access via TI-RTOS gatemutex +#define NVOCMP_LOCK() int32_t key = GateMutexPri_enter(NVOCMP_gMutexPri); + +// Unlock driver access via TI-RTOS gatemutex and return error code +#define NVOCMP_UNLOCK(err) \ + { \ + GateMutexPri_leave(NVOCMP_gMutexPri, key); \ + return (err); \ + } +#endif + +// Generate a compressed NV ID (NOTE: bit31 must be zero) +#define NVOCMP_CMPRID(s, i, b) \ + ((uint32_t) ((((((s) &NVOCMP_MAXSYSID) << NVOCMP_CMPSPACE) | ((i) &NVOCMP_MAXITEMID)) << NVOCMP_CMPSPACE) | \ + ((b) &NVOCMP_MAXSUBID))) + +// NVOCMP Unit Test Assert Macro/Function +#ifdef NVDEBUG +extern void nvprint(char * message); +extern void Main_assertHandler(uint8_t assertReason); +static void NVOCMP_assert(bool cond, char * message, bool fatal) +{ + if (!cond) + { + nvprint("NVDEBUG: "); + nvprint(message); + if (fatal) + { + Main_assertHandler(0); + } + } +} +#define NVOCMP_ASSERT(cond, message) NVOCMP_assert((cond), (message), true); +#define NVOCMP_ALERT(cond, message) NVOCMP_assert((cond), (message), false); +#define NVOCMP_ASSERT1(cond) \ + { \ + if (!cond) \ + while (1) \ + ; \ + } +#else +#ifdef NV_LINUX +#define NVOCMP_ASSERT1(cond) NVOCMP_ASSERT((cond), "NVOCMP_ASSERT1") +#else +#define NVOCMP_ASSERT(cond, message) +#define NVOCMP_ALERT(cond, message) +#define NVOCMP_ASSERT1(cond) \ + { \ + if (!cond) \ + while (1) \ + ; \ + } +#endif +#endif // NVDEBUG + +//***************************************************************************** +// Page and Header Definitions +//***************************************************************************** +#if defined(DeviceFamily_CC13X4) || defined(DeviceFamily_CC26X4) || defined(DeviceFamily_CC26X3) || \ + defined(DeviceFamily_CC23X0R5) || defined(DeviceFamily_CC23X0R2) || defined(DeviceFamily_CC27XX) +// CC26x4/CC13x4/CC23x0/cc27xx devices flash page size is (1 << 11) or 0x800 +#define PAGE_SIZE_LSHIFT 11 +#else +// CC26x2/CC13x2 devices flash page size is (1 << 13) or 0x2000 +#define PAGE_SIZE_LSHIFT 13 +#endif +#if !defined(FLASH_PAGE_SIZE) +#define FLASH_PAGE_SIZE (1 << PAGE_SIZE_LSHIFT) +#endif // FLASH_PAGE_SIZE + +#if !defined(NVOCMP_VERSION) +// Version of NV page format (do not use 0xFF) +#define NVOCMP_VERSION 0x03 +#endif // NVOCMP_VERSION + +#if !defined(NVOCMP_SIGNATURE) +// Page header validation byte (do not use 0xFF) +#define NVOCMP_SIGNATURE 0x96 +#endif // NVOCMP_SIGNATURE + +#ifndef NVOCMP_NO_RAM_OPTIMIZATION +#if defined(DeviceFamily_CC23X0R5) || defined(DeviceFamily_CC23X0R2) || defined(DeviceFamily_CC27XX) +#define NVOCMP_RAM_OPTIMIZATION +#endif +#endif + +#ifndef NVOCMP_RAM_OPTIMIZATION +// Compact Memory +#if !defined(NV_LINUX) && !defined(DeviceFamily_CC13X4) && !defined(DeviceFamily_CC26X4) && !defined(DeviceFamily_CC26X3) && \ + !defined(DeviceFamily_CC23X0R5) && !defined(DeviceFamily_CC23X0R2) && !defined(DeviceFamily_CC27XX) +#define NVOCMP_GPRAM +#endif + +#ifdef NVOCMP_GPRAM +#define RAM_BUFFER_ADDRESS (uint8_t *) GPRAM_BASE +#else +/* When CC23X0/CC27XX is used, as GPRAM is not supported, + * an SRAM buffer of FLASH_PAGE_SIZE length is declared, + * as the NVOCMP algorithm relies on it. + * Also, when CC13X4 / CC26X3 / CC26X4 is used, + * GPRAM cannot be used as it is always mapped to secure + * address space, and therefore cannot be used by non-secure + * application. A buffer in SRAM is used instead. + * */ +uint32_t tBuffer[FLASH_PAGE_SIZE >> 2]; +#endif +#else +#ifdef NVOCMP_FASTOFF +#undef NVOCMP_FASTOFF +#endif +#define NVOCMP_FASTOFF 0 + +#ifdef NVOCMP_FASTITEM +#undef NVOCMP_FASTITEM +#endif +#define NVOCMP_FASTITEM 0 + +#ifndef NVOCMP_RAM_BUFFER_SIZE +#define NVOCMP_RAM_BUFFER_SIZE 500 +#endif +uint8_t tBuffer[NVOCMP_RAM_BUFFER_SIZE]; +#endif + +// Page header structure +typedef struct +{ + uint32_t state : 8; + uint32_t cycle : 8; // Rolling page compaction count (0x00, 0xFF not used) + uint32_t allActive : 2; + uint32_t version : 6; // Version of NV page format + uint32_t signature : 8; // Signature for formatted NV page +} NVOCMP_pageHdr_t; + +typedef struct +{ + uint16_t pageOffset; + uint8_t page; + uint8_t signature; +} NVOCMP_compactHdr_t; + +// Page header size (bytes) +#define NVOCMP_PGHDRLEN (sizeof(NVOCMP_pageHdr_t)) +#define NVOCMP_COMPACTHDRLEN (sizeof(NVOCMP_compactHdr_t)) + +// Page header offsets (from 1st byte of page) +#define NVOCMP_PGHDROFS 0 +#define NVOCMP_PGHDRPST 0 // Page state +#define NVOCMP_PGHDRCYC 1 // Cycle count +#define NVOCMP_PGHDRVER 2 // Format version +#define NVOCMP_PGHDRSIG 3 // Page signature + +// Compact header offsets +#define NVOCMP_COMPMODEOFS 6 // Compact Mode Offset + +// Number of Compact headers +#define NVOCMP_NOCOMPHDR 3 + +// Page data size, offset into page +#define NVOCMP_PGDATAOFS (NVOCMP_PGHDRLEN + NVOCMP_NOCOMPHDR * NVOCMP_COMPACTHDRLEN) +#define NVOCMP_PGDATAEND (FLASH_PAGE_SIZE - 1) +#define NVOCMP_PGDATALEN (FLASH_PAGE_SIZE - NVOCMP_PGDATAOFS) + +// Page mode of operation +#define NVOCMP_PGNORMAL 0xFF // normal operation +#define NVOCMP_PGCDST 0xFE // used as compact destination +#define NVOCMP_PGCDONE 0xFC +#define NVOCMP_PGCSRC 0xF8 // used as compact source +#define NVOCMP_PGMODEBIT 0x04 + +// NVOCTP header defines +#define NVOCTP_PGACTIVE 0xA5 // Current active page +#define NVOCTP_PGXFER 0x24 // Active page being compacted +#define NVOCTP_PGDATAOFS NVOCMP_PGHDRLEN +#define NVOCTP_VERSION 0x02 +#define NVOCTP_SIGNATURE 0x96 + +// Page header state values - transitions change 1 bit in each nybble +typedef enum NVOCMP_pageState +{ + NVOCMP_PGNACT = 0xFF, + NVOCMP_PGXDST = 0xFE, + NVOCMP_PGRDY = 0x7E, + NVOCMP_PGACT = 0x7C, + NVOCMP_PGFULL = 0x78, + NVOCMP_PGXSRC = 0x70, + NVOCMP_PGNDEF = 0x00, +} NVOCMP_pageState_t; + +typedef enum NVOCMP_compactStatus +{ + NVOCMP_COMPACT_SUCCESS = 0x00, + NVOCMP_COMPACT_SRCDONE = 0x01, + NVOCMP_COMPACT_DSTDONE = 0x02, + NVOCMP_COMPACT_BOTHDOE = 0x03, + NVOCMP_COMPACT_FAILURE = 0x10, +} NVOCMP_compactStatus_t; + +// Page compaction cycle count limits (0x00 and 0xFF not used) +#define NVOCMP_MINCYCLE 0x01 // Minimum cycle count (after rollover) +#define NVOCMP_MAXCYCLE 0xFE // Maximum cycle count (before rollover) + +#define NVOCMP_ALLACTIVE 0x3 // All Items are active +#define NVOCMP_SOMEINACTIVE 0x0 // Some Items are inactive + +//***************************************************************************** +// Item Header Definitions +//***************************************************************************** + +// Item header structure +typedef struct +{ + uint32_t cmpid; // Compressed ID + uint16_t subid; // Sub ID + uint16_t itemid; // Item ID + uint8_t sysid; // System ID + uint8_t crc8; // crc byte + uint8_t sig; // signature byte + uint8_t stats; // Status 'marks' + uint16_t hofs; // Header offset + uint16_t len; // Data length + uint8_t hpage; // Header page +} NVOCMP_itemHdr_t; + +// Length (bytes) of compressed header +#define NVOCMP_ITEMHDRLEN 7 + +// Offset from beginning (low address) of header to fields in the header +#define NVOCMP_HDRSIGOFS 6 +#define NVOCMP_HDRVLDOFS 5 + +// Number of bytes in header to include in CRC calculation +#define NVOCMP_HDRCRCINC 5 + +// Compressed item header information <-- Lower Addr Higher Addr--> +// Byte: [0] [1] [2] [3] [4] [5] [6] +// Item: SSSSSSII IIIIIIII SSSSSSSS SSLLLLLL LLLLLLCC CCCCCCAV SSSSSSSS +// LSB of field: ^ ^ ^ ^ ^ +// Bit: 0 15 25 37 45 55 +// +// Bit(s) Bit Field Description +// ============================= +// 48-55: Signature byte (NVOCMP_SIGNATURE) +// 47: valid id mark (0=valid) +// 46: active id mark (1=active) +// 38-45: CRC8 value +// 26-37: data length (0-4095) +// 16-25: item sub id (0-1023) +// 6-15: nv item id (0-1023) +// 0-5: system id (0-63) + +// Bit47 in compressed header - '1' indicates 'active' NV item +// A deleted item is 'inactive' +#define NVOCMP_ACTIVEIDBIT 0x2 +// Bit46 in compressed header - '0' indicates 'valid' NV item +// A corrupted item is 'invalid' +#define NVOCMP_VALIDIDBIT 0x1 +// This bit is NOT included in the NV item itself but is encoded +// the 'stats' field of the itemHdr_t struct when the item is read +#define NVOCMP_FOLLOWBIT 0x4 + +// Index of last item header byte +#define NVOCMP_ITEMHDREND (NVOCMP_ITEMHDRLEN - 1) + +// Compressed item header byte array +typedef uint8_t cmpIH_t[NVOCMP_ITEMHDRLEN]; + +// Item write parameters +typedef struct +{ + NVOCMP_itemHdr_t * iHdr; // Ptr to item header + uint16_t dOfs; // Source data offset + uint16_t bOfs; // Buffer data offset + uint16_t len; // Buffer data length + uint8_t * pBuf; // Ptr to data buffer +} NVOCMP_itemWrp_t; + +typedef struct +{ + void * cBuf; // Pointer to content to search + uint16_t clength; // Length of content to search + uint16_t coff; // Offset content to search + void * rBuf; // Pointer to content to read + uint16_t rlength; // Length content to read +} NVOCMP_itemInfo_t; + +typedef enum NVOCMP_initAction +{ + NVOCMP_NORMAL_INIT = 0, + NVOCMP_NORMAL_RESUME, + NVOCMP_RECOVER_COMPACT, + NVOCMP_RECOVER_ERASE, + NVOCMP_FORCE_CLEAN, + NVOCMP_NORMAL_MIGRATE, + NVOCMP_ERROR_UNKNOWN, +} NVOCMP_initAction_t; + +typedef enum NVOCMP_writeMode +{ + NVOCMP_WRITE = 0, + NVOCMP_CREATE, + NVOCMP_UPDATE, +} NVOCMP_writeMode_t; + +typedef struct +{ + uint8_t state; // page state + uint8_t cycle; // page compaction cycle count. Used to select the 'newest' active page + // at device reset, in the very unlikely scenario that both pages are active. + uint8_t mode; // compact mode + uint8_t allActive; // all items are active or not + uint8_t sPage; + uint8_t ePage; + uint16_t offset; // page offset + uint16_t sOffset; + uint16_t eOffset; +} NVOCMP_pageInfo_t; + +typedef struct +{ + uint8_t xDstPage; // xdst page + uint8_t xSrcSPage; // xsrc start page + uint8_t xSrcEPage; // xsrc end page + uint8_t xSrcPages; // no of xsrc pages + uint16_t xDstOffset; // xdst offset + uint16_t xSrcSOffset; // xsrc start offset + uint16_t xSrcEOffset; // xsrc end offset +} NVOCMP_compactInfo_t; + +typedef struct +{ + uint8_t nvSize; // no of NV pages + uint8_t headPage; // head active page + uint8_t tailPage; // transfer destination page + uint8_t actPage; // current active page + uint8_t xsrcPage; // transfer source page + uint8_t forceCompact; // force compaction to happen + uint16_t actOffset; // active page offset + uint16_t xsrcOffset; // transfer source page offset + uint16_t xdstOffset; // transfer destination page offset + NVOCMP_compactInfo_t compactInfo; + NVOCMP_pageInfo_t pageInfo[NVOCMP_NVPAGES]; +} NVOCMP_nvHandle_t; +//***************************************************************************** +// Local variables +//***************************************************************************** +#define NVOCMP_NULLOFFSET 0xFFFF +#define DEFAULT_COMPACTHDR { NVOCMP_NULLOFFSET, NVOCMP_NULLPAGE, NVOCMP_SIGNATURE }; +#define THISPAGEHDR 0 +#define XSRCSTARTHDR 1 +#define XSRCENDHDR 2 +#define NVOCMP_COMPACTHDRLEN (sizeof(NVOCMP_compactHdr_t)) +// NVS Objects +#ifdef NVDEBUG +// Expose these in debug mode +NVS_Handle NVOCMP_nvsHandle; +NVS_Attrs NVOCMP_nvsAttrs; +NVOCMP_nvHandle_t NVOCMP_nvHandle; +#else +static NVS_Handle NVOCMP_nvsHandle; +static NVS_Attrs NVOCMP_nvsAttrs; + +/* The following variable has been made non-static, so it can be accessed + * through other modules through "extern". However, this is a temporary + * solution, as this variable is meant to be used only by NVOCMP. + * Users of NVOCMP must only access these features through the available + * APIs.*/ +/*static*/ NVOCMP_nvHandle_t NVOCMP_nvHandle; +#endif // NVDEBUG + +// Flag to indicate that a fatal error occurred while writing to or erasing the +// Flash memory. If flag is set, it's unsafe to attempt another write or erase. +// This flag locks writes to Flash until the next system reset. +static uint8_t NVOCMP_failF = NVINTF_NOTREADY; + +// Flag to indicate that a non-fatal error occurred while writing to or erasing +// Flash memory. With flag set, it's still safe to attempt a write or erase. +// This flag is reset by any API calls that cause an erase/write to Flash. +static uint8_t NVOCMP_failW; + +// TI-RTOS gateMutexPri for the NV driver API functions +#ifdef NVOCMP_POSIX_MUTEX +static pthread_mutex_t NVOCMP_gPosixMutex; +#elif defined(NVOCMP_POSIX_SEM) +static sem_t NVOCMP_gPosixSem; +#else +static GateMutexPri_Handle NVOCMP_gMutexPri; +#endif + +// Small NV Item Buffer, for item construction +static uint8_t NVOCMP_itemBuffer[NVOCMP_SMALLITEM]; + +// Function Pointer to an optional user provided voltage check function +static bool (*NVOCMP_voltCheckFptr)(void); +// Diagnostic counter for bad CRCs +#ifdef NVOCMP_STATS +static uint16_t NVOCMP_badCRCCount = 0; +#endif // NVOCMP_STATS + +NVOCMP_initAction_t gAction; +uint8_t NVOCMP_size; + +//***************************************************************************** +// NV API Function Prototypes +//***************************************************************************** + +static uint8_t NVOCMP_initNvApi(void * param); +static uint8_t NVOCMP_compactNvApi(uint16_t min); +static uint8_t NVOCMP_createItemApi(NVINTF_itemID_t id, uint32_t len, void * buf); +static uint8_t NVOCMP_updateItemApi(NVINTF_itemID_t id, uint32_t len, void * buf); +static uint8_t NVOCMP_deleteItemApi(NVINTF_itemID_t id); +static uint32_t NVOCMP_getItemLenApi(NVINTF_itemID_t id); +static uint8_t NVOCMP_readItemApi(NVINTF_itemID_t id, uint16_t ofs, uint16_t len, void * buf); +static uint8_t NVOCMP_readContItemApi(NVINTF_itemID_t id, uint16_t ofs, uint16_t rlen, void * rBuf, uint16_t clen, uint16_t coff, + void * cBuf, uint16_t * pSubId); +static uint8_t NVOCMP_writeItemApi(NVINTF_itemID_t id, uint16_t len, void * buf); +static uint8_t NVOCMP_doNextApi(NVINTF_nvProxy_t * prx); +static int32_t NVOCMP_lockNvApi(void); +static void NVOCMP_unlockNvApi(int32_t); +static bool NVOCMP_expectCompApi(uint16_t len); +static uint8_t NVOCMP_eraseNvApi(void); +static uint32_t NVOCMP_getFreeNvApi(void); + +#ifdef ENABLE_SANITY_CHECK +static uint32_t NVOCMP_sanityCheckApi(void); +#endif + +//***************************************************************************** +// NV Local Function Prototypes +//***************************************************************************** + +static void NVOCMP_initNv(NVOCMP_nvHandle_t * pNvHandle); +static uint8_t NVOCMP_scanPage(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageInfo_t * pPageInfo); +static int8_t NVOCMP_findItem(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * pHdr, int8_t flag, + NVOCMP_itemInfo_t * pInfo); +static uint8_t NVOCMP_addItem(NVOCMP_nvHandle_t * pNvHandle, NVOCMP_itemHdr_t * iHdr, uint8_t * pBuf, NVOCMP_writeMode_t wm); +static void NVOCMP_writeItem(NVOCMP_nvHandle_t * pNvHandle, NVOCMP_itemHdr_t * pHdr, uint8_t dstPg, uint16_t dstOff, + uint8_t * pBuf); +static uint8_t NVOCMP_erase(NVOCMP_nvHandle_t * pNvHandle, uint8_t dstPg); +static int16_t NVOCMP_compactPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t nBytes); +static NVOCMP_compactStatus_t NVOCMP_compact(NVOCMP_nvHandle_t * pNvHandle); +static uint8_t NVOCMP_getDstPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t len); +static void NVOCMP_changePageState(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageState_t state); +static void NVOCMP_setPageState(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageState_t state); +static void NVOCMP_setItemInactive(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t iOfs); +static uint8_t NVOCMP_readItem(NVOCMP_itemHdr_t * iHdr, uint16_t ofs, uint16_t len, void * pBuf, bool flag); +static uint8_t NVOCMP_checkItem(NVINTF_itemID_t * id, uint16_t len, NVOCMP_itemHdr_t * iHdr, uint8_t flag); +static inline void NVOCMP_read(uint8_t pg, uint16_t off, uint8_t * pBuf, uint16_t len); +static uint8_t NVOCMP_write(uint8_t dstPg, uint16_t off, uint8_t * pBuf, uint16_t len); +static void NVOCMP_readHeader(uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * iHdr, bool flag); +static void NVOCMP_setCompactHdr(uint8_t dstPg, uint8_t pg, int16_t offset, uint16_t location); +static uint16_t NVOCMP_findOffset(uint8_t pg, uint16_t ofs); +static uint8_t NVOCMP_doNVCRC(uint8_t pg, uint16_t ofs, uint16_t len, uint8_t crc, bool flag); +static uint8_t NVOCMP_doRAMCRC(uint8_t * input, uint16_t len, uint8_t crc); +static uint8_t NVOCMP_verifyCRC(uint16_t iOfs, uint16_t len, uint8_t crc, uint8_t pg, bool flag); +static uint8_t NVOCMP_readByte(uint8_t pg, uint16_t ofs); +static void NVOCMP_writeByte(uint8_t pg, uint16_t ofs, uint8_t bwv); + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +static uint8_t NVOCMP_findDstPage(NVOCMP_nvHandle_t * pNvHandle); +static uint8_t NVOCMP_cleanPage(NVOCMP_nvHandle_t * pNvHandle); +static uint8_t NVOCMP_findPage(NVOCMP_pageState_t state); +static void NVOCMP_getCompactHdr(uint8_t dstPg, uint16_t location, NVOCMP_compactHdr_t * pHdr); +#endif + +#if (NVOCMP_NVPAGES > NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED) +static void NVOCMP_migratePage(NVOCMP_nvHandle_t * pNvHandle, uint8_t page); +#endif + +#if ((NVOCMP_NVPAGES > NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) || defined NVOCMP_RAM_OPTIMIZATION +static void NVOCMP_copyItem(uint8_t srcPg, uint8_t dstPg, uint16_t sOfs, uint16_t dOfs, uint16_t len); +#endif + +//***************************************************************************** +// Load Pointer Functions (These are declared in nvoctp.h) +//***************************************************************************** + +/** + * @fn NVOCMP_loadApiPtrs + * + * @brief Global function to return function pointers for NV driver API that + * are supported by this module, NULL for functions not supported. + * + * @param pfn - pointer to caller's structure of NV function pointers + * + * @return none + */ +void NVOCMP_loadApiPtrs(NVINTF_nvFuncts_t * pfn) +{ + // Load caller's structure with pointers to the NV API functions + pfn->initNV = &NVOCMP_initNvApi; + pfn->compactNV = &NVOCMP_compactNvApi; + pfn->createItem = &NVOCMP_createItemApi; + pfn->updateItem = &NVOCMP_updateItemApi; + pfn->deleteItem = &NVOCMP_deleteItemApi; + pfn->readItem = &NVOCMP_readItemApi; + pfn->readContItem = &NVOCMP_readContItemApi; + pfn->writeItem = &NVOCMP_writeItemApi; + pfn->getItemLen = &NVOCMP_getItemLenApi; + pfn->lockNV = NULL; + pfn->unlockNV = NULL; + pfn->doNext = NULL; + pfn->expectComp = &NVOCMP_expectCompApi; + pfn->eraseNV = &NVOCMP_eraseNvApi; + pfn->getFreeNV = &NVOCMP_getFreeNvApi; +#ifdef ENABLE_SANITY_CHECK + pfn->sanityCheck = &NVOCMP_sanityCheckApi; +#endif +} + +/** + * @fn NVOCMP_loadApiPtrsMin + * + * @brief Global function to return function pointers for NV driver API that + * are supported by this module, NULL for functions not supported. + * This function loads the minimum necessary API functions. + * This should allow smaller code size. + * + * @param pfn - pointer to caller's structure of NV function pointers + * + * @return none + */ +void NVOCMP_loadApiPtrsMin(NVINTF_nvFuncts_t * pfn) +{ + // Load caller's structure with pointers to the NV API functions + pfn->initNV = &NVOCMP_initNvApi; + pfn->compactNV = &NVOCMP_compactNvApi; + pfn->createItem = NULL; + pfn->updateItem = NULL; + pfn->deleteItem = NULL; + pfn->readItem = &NVOCMP_readItemApi; + pfn->readContItem = NULL; + pfn->writeItem = &NVOCMP_writeItemApi; + pfn->getItemLen = NULL; + pfn->lockNV = NULL; + pfn->unlockNV = NULL; + pfn->doNext = NULL; + pfn->expectComp = &NVOCMP_expectCompApi; + pfn->eraseNV = &NVOCMP_eraseNvApi; + pfn->getFreeNV = &NVOCMP_getFreeNvApi; +#ifdef ENABLE_SANITY_CHECK + pfn->sanityCheck = &NVOCMP_sanityCheckApi; +#endif +} + +/** + * @fn NVOCMP_loadApiPtrsExt + * + * @brief Global function to return function pointers for NV driver API that + * are supported by this module, NULL for functions not supported. + * This function also loads the 'extended' API function pointers. + * + * @param pfn - pointer to caller's structure of NV function pointers + * + * @return none + */ +void NVOCMP_loadApiPtrsExt(NVINTF_nvFuncts_t * pfn) +{ + // Load caller's structure with pointers to the NV API functions + pfn->initNV = &NVOCMP_initNvApi; + pfn->compactNV = &NVOCMP_compactNvApi; + pfn->createItem = &NVOCMP_createItemApi; + pfn->updateItem = &NVOCMP_updateItemApi; + pfn->deleteItem = &NVOCMP_deleteItemApi; + pfn->readItem = &NVOCMP_readItemApi; + pfn->readContItem = &NVOCMP_readContItemApi; + pfn->writeItem = &NVOCMP_writeItemApi; + pfn->getItemLen = &NVOCMP_getItemLenApi; + pfn->lockNV = &NVOCMP_lockNvApi; + pfn->unlockNV = &NVOCMP_unlockNvApi; + pfn->doNext = &NVOCMP_doNextApi; + pfn->expectComp = &NVOCMP_expectCompApi; + pfn->eraseNV = &NVOCMP_eraseNvApi; + pfn->getFreeNV = &NVOCMP_getFreeNvApi; +#ifdef ENABLE_SANITY_CHECK + pfn->sanityCheck = &NVOCMP_sanityCheckApi; +#endif +} + +/** + * @fn NVOCMP_setCheckVoltage + * + * @brief Global function to allow user to provide a voltage check function + * for the driver to use. If a pointer is provided, the driver will + * call the provided function before flash erases and writes. The + * provided function should return true when the battery voltage is + * sufficient and vice versa. The user can withdraw their function + * by passing a NULL pointer to this function. + * + * @param funcPtr - pointer to a function which returns a bool. + * + * @return none + */ +extern void NVOCMP_setCheckVoltage(void * funcPtr) +{ +#ifndef NV_LINUX + NVOCMP_voltCheckFptr = (bool (*)()) funcPtr; +#else + // Do nothing + (void) NVOCMP_voltCheckFptr; +#endif +} + +#ifdef NVOCMP_MIN_VDD_FLASH_MV +/** + * @fn NVOCMP_setLowVoltageCb + * + * @brief Global function to allow user to provide a low voltage callback function + * for the driver to use. If a pointer is provided, the driver will + * call the provided function when low voltage detected. + * + * @param funcPtr - pointer to a function. + * + * @return none + */ +static lowVoltCbFptr NVOCMP_lowVoltCbFptr = NULL; +extern void NVOCMP_setLowVoltageCb(lowVoltCbFptr funcPtr) +{ +#ifndef NV_LINUX + NVOCMP_lowVoltCbFptr = (lowVoltCbFptr) funcPtr; +#else + // Do nothing + (void) NVOCMP_lowVoltCbFptr; +#endif +} + +/******************************************************************************* + * @fn NVOCMP_checkVoltage() + * + * @brief Checks the caller supplied voltage threshold against the value read + * from the CC26xx BATMON register. + * + * @param none + * + * @return false if device voltage less than limit, otherwise true + ******************************************************************************* + */ +static bool NVOCMP_checkVoltage(void) +{ + uint32_t voltage = AONBatMonBatteryVoltageGet(); + voltage = (voltage * 1000) >> AON_BATMON_BAT_FRAC_W; + if (voltage < NVOCMP_MIN_VDD_FLASH_MV) + { + // Measured device voltage is below threshold + if (NVOCMP_lowVoltCbFptr) + { + NVOCMP_lowVoltCbFptr(voltage); + } + return (false); + } + + return (true); +} +#endif + +#ifdef NVDEBUG +void NVOCMP_corruptData(uint8_t pg, uint16_t off, uint16_t len, uint8_t buf) +{ + NVS_write(NVOCMP_nvsHandle, NVOCMP_FLASHOFFSET(pg, off), (uint8_t *) &buf, len, NVS_WRITE_POST_VERIFY); +} +#endif + +/****************************************************************************** + * @fn NVOCMP_initNvApi + * + * @brief API function to initialize the specified NV Flash pages + * + * @param param - pointer to caller's structure of NV init parameters + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_initNvApi(void * param) +{ + NVOCMP_ALERT(false, "NVOCMP Init. Called!") + NVOCMP_failW = NVOCMP_failF; + + if (NVOCMP_failF == NVINTF_NOTREADY) + { +#ifdef NVOCMP_POSIX_MUTEX + pthread_mutexattr_t attr; +#elif defined(NVOCMP_POSIX_SEM) +#else + GateMutexPri_Params gateParams; +#endif + + // Only one init per device reset + NVOCMP_failF = NVINTF_SUCCESS; + NVOCMP_failW = NVINTF_SUCCESS; + + // Create a priority gate mutex for the NV driver +#ifdef NVOCMP_POSIX_MUTEX + if (pthread_mutexattr_init(&attr) != 0) + { + NVOCMP_failF = NVINTF_FAILURE; + return (NVOCMP_failF); + } + +#ifndef NV_LINUX + attr.type = PTHREAD_MUTEX_RECURSIVE; +#else + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif + if (pthread_mutex_init(&NVOCMP_gPosixMutex, &attr) != 0) + { + NVOCMP_failF = NVINTF_FAILURE; + return (NVOCMP_failF); + } +#elif defined(NVOCMP_POSIX_SEM) + sem_init(&NVOCMP_gPosixSem, 0 /* ignored */, 1); +#else + GateMutexPri_Params_init(&gateParams); + NVOCMP_gMutexPri = GateMutexPri_create(&gateParams, NULL); +#endif + + memset(&NVOCMP_nvHandle, 0, sizeof(NVOCMP_nvHandle_t)); + memset(&NVOCMP_nvHandle.compactInfo, 0xFF, sizeof(NVOCMP_compactInfo_t)); + +#ifndef NV_LINUX +#ifdef NVOCMP_MIN_VDD_FLASH_MV + NVOCMP_setCheckVoltage((void *) &NVOCMP_checkVoltage); +#endif + // Initialize NVS objects + NVS_init(); + + // Use default NVS_Params to open this flash region + NVOCMP_nvsHandle = NVS_open(NVOCMP_NVS_INDEX, param); + + // Get NV hardware attributes + NVS_getAttrs(NVOCMP_nvsHandle, &NVOCMP_nvsAttrs); +#else + NV_LINUX_init(); + + NVOCMP_nvsHandle = NVS_HANDLE; + NVOCMP_nvsAttrs.sectorSize = FLASH_PAGE_SIZE; + NVOCMP_nvsAttrs.regionSize = FLASH_PAGE_SIZE * NVOCMP_NVPAGES; +#endif + + NVOCMP_nvHandle.nvSize = NVOCMP_nvsAttrs.regionSize / NVOCMP_nvsAttrs.sectorSize; + NVOCMP_nvHandle.nvSize = NVOCMP_nvHandle.nvSize > NVOCMP_NVPAGES ? NVOCMP_NVPAGES : NVOCMP_nvHandle.nvSize; + NVOCMP_size = NVOCMP_nvHandle.nvSize; + + // Confirm NV region has expected characteristics + if (FLASH_PAGE_SIZE != NVOCMP_nvsAttrs.sectorSize || (NVOCMP_NVSIZE * FLASH_PAGE_SIZE > NVOCMP_nvsAttrs.regionSize)) + { + NVOCMP_failF = NVINTF_FAILURE; + NVOCMP_EXCEPTION(pg, NVINTF_FAILURE) + return (NVOCMP_failF); + } + + // Confirm that the NVS region opened properly + if (NVOCMP_nvsHandle == NULL) + { + NVOCMP_failF = NVINTF_FAILURE; + NVOCMP_ASSERT(false, "NVS HANDLE IS NULL") + NVOCMP_EXCEPTION(pg, NVINTF_NOTREADY); + return (NVOCMP_failF); + } + + // Initialize force compaction to false + NVOCMP_nvHandle.forceCompact = 0; + + // Look for active page and clean up the other if necessary + NVOCMP_nvHandle.actPage = NVOCMP_NULLPAGE; + NVOCMP_nvHandle.actOffset = FLASH_PAGE_SIZE; + + NVOCMP_initNv(&NVOCMP_nvHandle); + +#if defined(NVOCMP_STATS) + { + uint8_t err; + NVOCMP_diag_t diags; + + // Look for a copy of diagnostic info + err = NVOCMP_readItemApi(diagId, 0, sizeof(diags), &diags); + if (err == NVINTF_NOTFOUND) + { + // Assume this is the first time, + memset(&diags, 0, sizeof(diags)); + // Space available for everything else + diags.available = FLASH_PAGE_SIZE - (NVOCMP_nvHandle.actOffset + NVOCMP_ITEMHDRLEN + sizeof(diags)); + } + // Remember this reset + diags.resets += 1; + // Create/Update the diagnostic NV item + NVOCMP_writeItemApi(diagId, sizeof(diags), &diags); + } +#endif // NVOCMP_STATS + } + + return (NVOCMP_failW); +} + +/****************************************************************************** + * @fn NVOCMP_eraseNvApi + * + * @brief API function to erase whole NV pages + * + * @param none + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_eraseNvApi(void) +{ + uint8_t pg; + uint8_t err = NVINTF_SUCCESS; + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + if (err) + { + return (err); + } + + NVOCMP_LOCK(); + + // Erase All pages before start + for (pg = 0; pg < NVOCMP_NVSIZE; pg++) + { + NVOCMP_failW |= NVOCMP_erase(&NVOCMP_nvHandle, pg); + } + + err = NVOCMP_failW; + + // initial state, set head page, act page and tail page + NVOCMP_nvHandle.headPage = 0; + NVOCMP_nvHandle.tailPage = NVOCMP_NVSIZE - 1; + NVOCMP_nvHandle.actPage = 0; + NVOCMP_nvHandle.actOffset = NVOCMP_nvHandle.pageInfo[NVOCMP_nvHandle.actPage].offset; + NVOCMP_changePageState(&NVOCMP_nvHandle, NVOCMP_nvHandle.headPage, NVOCMP_PGRDY); + NVOCMP_changePageState(&NVOCMP_nvHandle, NVOCMP_nvHandle.tailPage, NVOCMP_PGXDST); + +#ifdef NV_LINUX + if (err == NVINTF_SUCCESS) + { + NV_LINUX_save(); + } +#endif + + NVOCMP_UNLOCK(err); +} + +/****************************************************************************** + * @fn NVOCMP_getFreeNvApi + * + * @brief API function to get free space in whole NV pages + * + * @param none + * + * @return bytes of free space + */ +static uint32_t NVOCMP_getFreeNvApi(void) +{ + uint8_t pg = NVOCMP_nvHandle.actPage; + NVOCMP_pageHdr_t pageHdr; + uint32_t freespace = 0; +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + uint16_t nvSearched = 0; + for (pg = NVOCMP_nvHandle.actPage; nvSearched < NVOCMP_NVSIZE; pg = NVOCMP_INCPAGE(pg)) + { + nvSearched++; + if (pg == NVOCMP_nvHandle.tailPage) + { + continue; + } +#endif + NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + if ((pageHdr.state == NVOCMP_PGNACT) || (pageHdr.state == NVOCMP_PGRDY) || (pageHdr.state == NVOCMP_PGACT)) + { + freespace += (FLASH_PAGE_SIZE - NVOCMP_nvHandle.pageInfo[pg].offset); + } +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + } +#endif + return (freespace); +} + +/****************************************************************************** + * @fn NVOCMP_compactNvApi + * + * @brief API function to force NV active page compaction + * + * @param minAvail - threshold size of available bytes on Flash page to do + * compaction: 0 = always, >0 = minimum remaining bytes + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_compactNvApi(uint16_t minAvail) +{ + uint8_t err = NVINTF_SUCCESS; + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + if (err) + { + return (err); + } + + // Prevent RTOS thread contention + NVOCMP_LOCK(); + NVOCMP_ALERT(false, "API Compaction Request.") + err = NVOCMP_failF; + // Check for a fatal error + if (err == NVINTF_SUCCESS) + { + int16_t left; + + // Number of bytes left on active page + left = FLASH_PAGE_SIZE - NVOCMP_nvHandle.actOffset; + + // Time to do a compaction? + if ((left < minAvail) || (minAvail == 0)) + { + // Transfer all items to non-ACTIVE page + (void) NVOCMP_compactPage(&NVOCMP_nvHandle, 0); + // 'failW' indicates compaction status + err = NVOCMP_failW; + } + else + { + // Indicate "bad" minAvail value + err = NVINTF_BADPARAM; + } + } + +#ifdef NV_LINUX + if (err == NVINTF_SUCCESS) + { + NV_LINUX_save(); + } +#endif + + NVOCMP_UNLOCK(err); +} + +//***************************************************************************** +// API Functions - NV Data Items +//***************************************************************************** + +/****************************************************************************** + * @fn NVOCMP_createItemApi + * + * @brief API function to create a new NV item in Flash memory. This function + * will return an error if the specified item already exists. + * + * @param id - NV item type identifier + * @param len - length of NV data + * @param pBuf - pointer to caller's data buffer (NULL is illegal) + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_createItemApi(NVINTF_itemID_t id, uint32_t len, void * pBuf) +{ + uint8_t err; + NVOCMP_itemHdr_t iHdr; + + // Parameter Sanity Check + if (pBuf == NULL || len == 0) + { + return (NVINTF_BADPARAM); + } + + err = NVOCMP_checkItem(&id, len, &iHdr, NVOCMP_FINDSTRICT); + if (err) + { + return (err); + } + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + if (err) + { + return (err); + } + + // Prevent RTOS thread contention + NVOCMP_LOCK(); + + err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL); + + if (err == NVINTF_SUCCESS) + { + err = NVINTF_EXIST; + } + else if (err == NVINTF_NOTFOUND) + { + // Create the new item + err = NVOCMP_addItem(&NVOCMP_nvHandle, &iHdr, pBuf, NVOCMP_CREATE); + if (err != NVINTF_SUCCESS) + { + NVOCMP_ALERT(false, "createItem failed.") + err = NVINTF_FAILURE; + } + } + else + { + NVOCMP_ALERT(false, "createItem failed.") + err = NVINTF_FAILURE; + } + +#ifdef NV_LINUX + if (err == NVINTF_SUCCESS) + { + NV_LINUX_save(); + } +#endif + + NVOCMP_UNLOCK(err); +} + +/****************************************************************************** + * @fn NVOCMP_updateItemApi + * + * @brief API function to update an existing NV item in Flash memory. This function + * will return an error if the specified item does not exist. + * + * @param id - NV item type identifier + * @param len - length of NV data + * @param pBuf - pointer to caller's data buffer (NULL is illegal) + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_updateItemApi(NVINTF_itemID_t id, uint32_t len, void * pBuf) +{ + uint8_t err; + NVOCMP_itemHdr_t iHdr; + + // Parameter Sanity Check + if (pBuf == NULL || len == 0) + { + return (NVINTF_BADPARAM); + } + + err = NVOCMP_checkItem(&id, len, &iHdr, NVOCMP_FINDSTRICT); + if (err) + { + return (err); + } + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + if (err) + { + return (err); + } + + // Prevent RTOS thread contention + NVOCMP_LOCK(); + + err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL); + + if (err == NVINTF_SUCCESS) + { + // Create the new item + err = NVOCMP_addItem(&NVOCMP_nvHandle, &iHdr, pBuf, NVOCMP_UPDATE); + if ((err == NVINTF_SUCCESS) && (iHdr.hofs > 0)) + { + // Mark old item as inactive + NVOCMP_setItemInactive(&NVOCMP_nvHandle, iHdr.hpage, iHdr.hofs); + + err = NVOCMP_failW; + } + } + else if (err == NVINTF_NOTFOUND) + { + err = NVINTF_NOTFOUND; + } + else + { + NVOCMP_ALERT(false, "updateItem failed.") + err = NVINTF_FAILURE; + } + +#ifdef NV_LINUX + if (err == NVINTF_SUCCESS) + { + NV_LINUX_save(); + } +#endif + + NVOCMP_UNLOCK(err); +} + +/****************************************************************************** + * @fn NVOCMP_deleteItemApi + * + * @brief API function to delete an existing NV item from Flash memory. Note, + * it is inefficient to use this function to delete a range of items. + * The doNext call is recommended for that use case. + * + * @param id - NV item type identifier + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_deleteItemApi(NVINTF_itemID_t id) +{ + uint8_t err; + NVOCMP_itemHdr_t iHdr; + +#if defined(NVOCMP_STATS) + if (!memcmp(&id, &diagId, sizeof(NVINTF_itemID_t))) + { + // Protect NV driver item(s) + return (NVINTF_BADSYSID); + } +#endif // NVOCMP_STATS + + err = NVOCMP_checkItem(&id, 0, &iHdr, NVOCMP_FINDSTRICT); + if (err) + { + return (err); + } + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + if (err) + { + return (err); + } + + // Prevent RTOS thread contention + NVOCMP_LOCK(); + + err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL); + + if (!err) + { + // Mark this item as inactive + NVOCMP_setItemInactive(&NVOCMP_nvHandle, iHdr.hpage, iHdr.hofs); + + // Verify that item has been removed + err = (NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, + NULL) == NVINTF_NOTFOUND) + ? NVOCMP_failW + : NVINTF_FAILURE; + + // If item did get deleted, report 'failW' status + NVOCMP_ALERT(err == NVOCMP_failW, "Item delete failed.") + } + +#ifdef NV_LINUX + if (err == NVINTF_SUCCESS) + { + NV_LINUX_save(); + } +#endif + + NVOCMP_UNLOCK(err); +} + +/****************************************************************************** + * @fn NVOCMP_getItemLenApi + * + * @brief API function to return the length of an NV data item + * + * @param id - NV item type identifier + * + * @return NV item length or 0 if item not found + */ +static uint32_t NVOCMP_getItemLenApi(NVINTF_itemID_t id) +{ + uint8_t err; + uint32_t len = 0; + NVOCMP_itemHdr_t iHdr; + + err = NVOCMP_checkItem(&id, 0, &iHdr, NVOCMP_FINDSTRICT); + if (err) + { + return (len); + } + + // Prevent RTOS thread contention + NVOCMP_LOCK(); + + // If there was any error, report zero length + len = (NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL) != + NVINTF_SUCCESS) + ? 0 + : iHdr.len; + + NVOCMP_UNLOCK(len); +} + +/****************************************************************************** + * @fn NVOCMP_readContItemApi + * + * @brief API function to read data from an NV item by comparing content + * + * @param id - NV item type identifier + * @param ofs - offset into NV data + * @param rlen - length of NV data to return (0 is illegal) + * @param rBuf - pointer to caller's read data buffer (NULL is illegal) + * @param clen - length of NV data to return (0 is illegal) + * @param coff - offset of content in data + * @param cBuf - pointer to caller's read data buffer (NULL is illegal) + * @param pSubId - pointer to store sub Id (NULL is illegal) + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_readContItemApi(NVINTF_itemID_t id, uint16_t ofs, uint16_t rlen, void * rBuf, uint16_t clen, uint16_t coff, + void * cBuf, uint16_t * pSubId) +{ + uint8_t err; + NVOCMP_itemHdr_t iHdr; + NVOCMP_itemInfo_t itemInfo; + + *pSubId = NVOCMP_INVALIDSUBID; + // Parameter Sanity Check + if (rBuf == NULL || rlen == 0 || cBuf == NULL || clen == 0 || coff > FLASH_PAGE_SIZE) + { + return (NVINTF_BADPARAM); + } + + err = NVOCMP_checkItem(&id, rlen, &iHdr, NVOCMP_FINDSTRICT); + if (err) + { + return (err); + } + + // Prevent RTOS thread contention + NVOCMP_LOCK(); + + itemInfo.cBuf = cBuf; + itemInfo.clength = clen; + itemInfo.coff = coff; + itemInfo.rBuf = rBuf; + itemInfo.rlength = rlen; + err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, + NVOCMP_FINDITMID | NVOCMP_FINDCONTENT, &itemInfo); + + if (!err) + { + *pSubId = iHdr.subid; + } + + NVOCMP_UNLOCK(err); +} + +/****************************************************************************** + * @fn NVOCMP_readItemApi + * + * @brief API function to read data from an NV item + * + * @param id - NV item type identifier + * @param ofs - offset into NV data + * @param len - length of NV data to return (0 is illegal) + * @param pBuf - pointer to caller's read data buffer (NULL is illegal) + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_readItemApi(NVINTF_itemID_t id, uint16_t ofs, uint16_t len, void * pBuf) +{ + uint8_t err; + NVOCMP_itemHdr_t iHdr; + + // Parameter Sanity Check + if (pBuf == NULL || len == 0) + { + return (NVINTF_BADPARAM); + } + + err = NVOCMP_checkItem(&id, len, &iHdr, NVOCMP_FINDSTRICT); + if (err) + { + return (err); + } + + // Prevent RTOS thread contention + NVOCMP_LOCK(); + + err = NVOCMP_findItem(&NVOCMP_nvHandle, NVOCMP_nvHandle.actPage, NVOCMP_nvHandle.actOffset, &iHdr, NVOCMP_FINDSTRICT, NULL); + + // Read Item + if (!err) + { + err = NVOCMP_readItem(&iHdr, ofs, len, pBuf, false); + } + + NVOCMP_UNLOCK(err); +} + +/****************************************************************************** + * @fn NVOCMP_writeItemApi + * + * @brief API function to write data to item, creates item if needed. + * Note that when writing to an existing item, data is not + * checked for redundancy. Data passed to this function will be + * written to NV. NOTE: It is not recommended to write items with + * SYSID 0 as this is reserved for the driver. NVOCMP will not + * delete items with this SYSID. + * + * @param id - NV item type identifier + * @param len - data buffer length to write into NV block (0 is illegal) + * @param pBuf - pointer to caller's data buffer to write (NULL is illegal) + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_writeItemApi(NVINTF_itemID_t id, uint16_t len, void * pBuf) +{ + uint8_t err; + NVOCMP_itemHdr_t iHdr; + + // Parameter Sanity Check + if (pBuf == NULL || len == 0) + { + return (NVINTF_BADPARAM); + } + + err = NVOCMP_checkItem(&id, len, &iHdr, NVOCMP_FINDSTRICT); + if (err) + { + return (err); + } + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + if (err) + { + return (err); + } + + // Prevent RTOS thread contention + NVOCMP_LOCK(); + + // Create a new item + err = NVOCMP_addItem(&NVOCMP_nvHandle, &iHdr, pBuf, NVOCMP_WRITE); + if ((err == NVINTF_SUCCESS) && (iHdr.hofs > 0)) + { + // Mark old item as inactive + NVOCMP_setItemInactive(&NVOCMP_nvHandle, iHdr.hpage, iHdr.hofs); + + err = NVOCMP_failW; + } + +#ifdef NV_LINUX + if (err == NVINTF_SUCCESS) + { + NV_LINUX_save(); + } +#endif + + NVOCMP_UNLOCK(err); +} + +//***************************************************************************** +// Extended API Functions +//***************************************************************************** + +/** + * @fn NVOCMP_lockNvApi + * + * @brief Global function to lock the NV priority gate mutex + * + * @return Key value needed to unlock the gate + */ +static int32_t NVOCMP_lockNvApi(void) +{ +#ifdef NVOCMP_POSIX_MUTEX + return (pthread_mutex_lock(&NVOCMP_gPosixMutex)); +#elif defined(NVOCMP_POSIX_SEM) + return (sem_wait(&NVOCMP_gPosixSem)); +#else + return (GateMutexPri_enter(NVOCMP_gMutexPri)); +#endif +} + +/** + * @fn NVOCMP_unlockNvApi + * + * @brief Global function to unlock the NV priority gate mutex + * + * @return none + */ +static void NVOCMP_unlockNvApi(int32_t key) +{ +#ifdef NVOCMP_POSIX_MUTEX + (void) key; + pthread_mutex_unlock(&NVOCMP_gPosixMutex); +#elif defined(NVOCMP_POSIX_SEM) + (void) key; + sem_post(&NVOCMP_gPosixSem); +#else + GateMutexPri_leave(NVOCMP_gMutexPri, key); +#endif +} + +/****************************************************************************** + * @fn NVOCMP_doNextApi + * + * @brief API function which allows operations on batches of NV items. This + * function provides a faster way of finding, reading, or deleting multiple + * NV items. However, the user must first lock access to NV with lockNV() to + * ensure consistent results. The user must take care to minimize the time NV + * is locked if NV access is shared. User must also remember to unlock NV when + * done with unlockNV(). + * + * Usage Details: + * doNext is controlled through the nvProxy item pointed to by prx + * User will set flag bit NVINTF_DOSTART and then other flags based on the + * desired operation. For example to find all items in system NVINTF_SYS_BLE, + * the user would set flag bit NVINTF_DOFIND and set prx->sysid = NVINTF_SYS_BLE. + * Then every call to doNextApi() returns with a status code and with the proxy + * item populated with the found item if there was one. NVINTF_SUCCESS is + * returned on a successful item operation, NVINTF_NOTFOUND is returned when a + * matching item is not found, and other error codes can be returned. + * Sample Code: + * + * // Use doNext to delete items of sysid + * NVINTF_nvFuncts_t nvFps; + * NVINTF_nvProxy_t nvProxy; + * NVOCMP_loadApiPtrsExt(&nvFps); + * nvFps.initNV(NULL); + * nvProxy.sysid = sysid; + * nvProxy.flag = NVINTF_DOSTART | NVINTF_DOSYSID | NVINTF_DODELETE; + * + * key = nvFps.lockNV(); + * while(!status) + * { + * status |= nvFps.doNext(&nvProxy); + * } + * nvFps.unlockNV(key); + * + * Notes: + * -User changes to the proxy struct will have no effect until a new search is + * started by setting NVINTF_DOSTART + * -On read operations, the user will supply a buffer and length into the proxy + * -Items with system id NVINTF_SYSID_NVDRVR cannot be deleted with this API, + * deleteItemApi must be used one an individual item basis + * + * @param prx - pointer to nvProxy item which contains user inputs + * + * @return NVINTF_SUCCESS or specific failure code + */ + +static uint8_t NVOCMP_doNextApi(NVINTF_nvProxy_t * prx) +{ + static enum { doFind, doRead, doDelete } op = doFind; + NVOCMP_itemHdr_t hdr; + static uint8_t search; + static uint8_t sPage; + static int16_t fOfs = FLASH_PAGE_SIZE; + static uint16_t bufLen = 0; + uint8_t status = NVINTF_SUCCESS; + int16_t iOfs = NVOCMP_nvHandle.actOffset; + static int16_t initialFindiofs = -1; + static uint8_t initialsPage = 0xFF; + + // Sanitize inputs + if (NULL == prx) + { + return (NVINTF_BADPARAM); + } + else if (0 == prx->flag) + { + return (NVINTF_BADPARAM); + } + + // Locks NV + NVOCMP_LOCK(); + + // New search if start flag set + if (prx->flag & NVINTF_DOSTART) + { + // Remove start flag + prx->flag &= ~NVINTF_DOSTART; + // Start at latest item + sPage = NVOCMP_nvHandle.actPage; + fOfs = NVOCMP_nvHandle.actOffset; + + initialFindiofs = -1; + initialsPage = 0xFF; + + // Read in buffer len + bufLen = prx->len; + // Decode flag + if (prx->flag & NVINTF_DOSYSID) + { + search = NVOCMP_FINDSYSID; + } + else if (prx->flag & NVINTF_DOITMID) + { + search = NVOCMP_FINDITMID; + } + else if (prx->flag & NVINTF_DOANYID) + { + search = NVOCMP_FINDANY; + } + if (prx->flag & NVINTF_DOFIND) + { + op = doFind; + } + else if (prx->flag & NVINTF_DOREAD) + { + op = doRead; + } + else if (prx->flag & NVINTF_DODELETE) + { + op = doDelete; + } + } + + hdr.sysid = prx->sysid; + hdr.itemid = prx->itemid; + hdr.subid = prx->subid; + // Look for item + if (!NVOCMP_findItem(&NVOCMP_nvHandle, sPage, fOfs, &hdr, search, NULL)) + { + iOfs = hdr.hofs; + // store its attributes + prx->sysid = hdr.sysid; + prx->itemid = hdr.itemid; + prx->subid = hdr.subid; + prx->len = hdr.len; + + if (prx->flag & NVINTF_DONOWRAP) + { + if ((initialFindiofs == hdr.hofs) && (initialsPage == hdr.hpage)) + { + status = NVINTF_NOTFOUND; + } + + if ((initialFindiofs == -1) && (initialsPage == 0xFF)) + { + initialFindiofs = hdr.hofs; + initialsPage = hdr.hpage; + } + } + + // start from this item on next findItem() + fOfs = iOfs - hdr.len; + sPage = hdr.hpage; + + if (status == NVINTF_SUCCESS) + { + // Do operation based on flag + switch (op) + { + case doFind: + // nothing, we already stored its info + break; + case doRead: + // read item into user supplied buffer + if (prx->buffer != NULL && hdr.len <= bufLen) + { + status = NVOCMP_readItem(&hdr, 0, hdr.len, prx->buffer, false); + } + break; + case doDelete: + if (prx->sysid != NVINTF_SYSID_NVDRVR) + { + NVOCMP_setItemInactive(&NVOCMP_nvHandle, hdr.hpage, iOfs); + } + break; + default: + NVOCMP_ALERT(false, "doNext flag is invalid.") + status = NVINTF_BADPARAM; + } + } + } + else + { + // No more items match, done. + status = NVINTF_NOTFOUND; + } + + // Unlocks NV + NVOCMP_UNLOCK(status); +} +/****************************************************************************** + * @fn NVOCMP_expectCompApi + * + * @brief API function to check if compaction will happen if a data with size = len is written + * + * @param len - data buffer length to write into NV block + * + * @return true or false + */ +static bool NVOCMP_expectCompApi(uint16_t len) +{ + uint8_t dstPg; + uint16_t iLen; + bool compact = false; + + if (len) + { + iLen = NVOCMP_ITEMHDRLEN + len; + dstPg = NVOCMP_getDstPage(&NVOCMP_nvHandle, iLen); + + if (dstPg == NVOCMP_NULLPAGE) + { + compact = true; + } + } + return (compact); +} + +//***************************************************************************** +// Local NV Driver Utility Functions +//***************************************************************************** + +#ifdef NVOCMP_GPRAM +/****************************************************************************** + * @fn NVOCMP_disableCache + * + * @brief Local function to disable cache + * + * @param vm - pointer to mode storage + * + * @return none + */ +static void NVOCMP_disableCache(uint32_t * vm) +{ + // Save current cache mode + *vm = VIMSModeGet(VIMS_BASE) & VIMS_STAT_MODE_M; + // Disable the cache + VIMSModeSet(VIMS_BASE, VIMS_MODE_DISABLED); + // Wait until it is + while (VIMSModeGet(VIMS_BASE) != VIMS_MODE_DISABLED) + ; +} + +/****************************************************************************** + * @fn NVOCMP_restoreCache + * + * @brief Local function to disable cache + * + * @param vm - mode + * + * @return none + */ +static void NVOCMP_restoreCache(uint32_t vm) +{ + // Restore cache to previous state + VIMSModeSet(VIMS_BASE, vm); +} +#endif + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_findPage + * + * @brief Local function to find page with specified state + * + * @param state - page state to find + * + * @return page number or NVOCMP_NULLPAGE + */ +static uint8_t NVOCMP_findPage(NVOCMP_pageState_t state) +{ + uint8_t pg; + NVOCMP_pageHdr_t pageHdr; + + for (pg = 0; pg < NVOCMP_NVSIZE; pg++) + { + // Get page header + NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + if (pageHdr.state == state) + { + return (pg); + } + } + return (NVOCMP_NULLPAGE); +} +#endif + +#if (NVOCMP_NVPAGES > NVOCMP_NVONEP) +#if !defined(NVOCMP_MIGRATE_DISABLED) +/****************************************************************************** + * @fn NVOCMP_migratePage + * + * @brief Local function to find page with specified state + * + * @param pNvHandle - pointer to NV handle + * @param page - page to convert + * + * @return none + */ +static void NVOCMP_migratePage(NVOCMP_nvHandle_t * pNvHandle, uint8_t page) +{ + uint8_t dstPg; + uint8_t tmp; + uint16_t offset1; + uint16_t offset2; + NVOCMP_itemHdr_t iHdr; + + offset1 = pNvHandle->pageInfo[page].offset; + offset2 = NVOCTP_PGDATAOFS; + if (offset1 - NVOCTP_PGDATAOFS > FLASH_PAGE_SIZE - NVOCMP_PGDATAOFS) + { + offset2 = offset1; + while (offset2 - NVOCTP_PGDATAOFS > FLASH_PAGE_SIZE - NVOCMP_PGDATAOFS) + { + NVOCMP_findItem(pNvHandle, page, offset2, &iHdr, NVOCMP_FINDANY, NULL); + offset2 = iHdr.hofs - iHdr.len; + } + } + if ((pNvHandle->nvSize <= NVOCMP_NVTWOP) && (offset2 != NVOCTP_PGDATAOFS)) + { + offset1 = offset2; + offset2 = NVOCTP_PGDATAOFS; + } + + dstPg = page; + // copy from offset2 to top + if (offset2 - NVOCTP_PGDATAOFS > 0) + { + dstPg = NVOCMP_INCPAGE(dstPg); + NVOCMP_copyItem(page, dstPg, NVOCTP_PGDATAOFS, NVOCMP_PGDATAOFS, offset2 - NVOCTP_PGDATAOFS); + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGFULL); + pNvHandle->pageInfo[dstPg].allActive = NVOCMP_SOMEINACTIVE; + pNvHandle->pageInfo[dstPg].offset = NVOCMP_PGDATAOFS + offset2 - NVOCTP_PGDATAOFS; + pNvHandle->actPage = dstPg; + pNvHandle->actOffset = pNvHandle->pageInfo[dstPg].offset; + tmp = NVOCMP_readByte(dstPg, NVOCMP_PGHDRVER); + tmp &= ~NVOCMP_ALLACTIVE; + NVOCMP_writeByte(dstPg, NVOCMP_PGHDRVER, tmp); + } + // copy from offset 1 to offset2 + if (offset1 - offset2 > 0) + { + dstPg = NVOCMP_INCPAGE(dstPg); + NVOCMP_copyItem(page, dstPg, offset2, NVOCMP_PGDATAOFS, offset1 - offset2); + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGFULL); + pNvHandle->pageInfo[dstPg].allActive = NVOCMP_SOMEINACTIVE; + pNvHandle->pageInfo[dstPg].offset = NVOCMP_PGDATAOFS + offset1 - offset2; + pNvHandle->actPage = dstPg; + pNvHandle->actOffset = pNvHandle->pageInfo[dstPg].offset; + tmp = NVOCMP_readByte(dstPg, NVOCMP_PGHDRVER); + tmp &= ~NVOCMP_ALLACTIVE; + NVOCMP_writeByte(dstPg, NVOCMP_PGHDRVER, tmp); + } + + NVOCMP_failW |= NVOCMP_erase(pNvHandle, page); + pNvHandle->tailPage = page; + pNvHandle->headPage = NVOCMP_INCPAGE(page); + NVOCMP_changePageState(pNvHandle, page, NVOCMP_PGXDST); +} +#endif +#endif + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_scanPage + * + * @brief Local function to scan page to get page information + * + * @param pNvHandle - pointer to NV handle + * @param pg - page to scan + * @param pPageInfo - page info pointer + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_scanPage(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageInfo_t * pPageInfo) +{ + uint32_t pageHdr; + NVOCMP_compactHdr_t thisHdr; + NVOCMP_compactHdr_t startHdr; + NVOCMP_compactHdr_t endHdr; + NVOCMP_pageHdr_t * pHdr = (NVOCMP_pageHdr_t *) &pageHdr; + + // Get page header + NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) pHdr, NVOCMP_PGHDRLEN); +#if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) + uint8_t version = (pHdr->version << 2) | (pHdr->allActive); + if ((pHdr->signature == NVOCTP_SIGNATURE) && (version == NVOCTP_VERSION)) + { + pPageInfo->state = pHdr->state; + pPageInfo->cycle = pHdr->cycle; + pPageInfo->allActive = NVOCMP_SOMEINACTIVE; + pPageInfo->mode = NVOCMP_PGNORMAL; + pPageInfo->offset = NVOCMP_findOffset(pg, FLASH_PAGE_SIZE); + return (NVINTF_SUCCESS); + } +#endif + + NVOCMP_getCompactHdr(pg, THISPAGEHDR, &thisHdr); + NVOCMP_getCompactHdr(pg, XSRCSTARTHDR, &startHdr); + NVOCMP_getCompactHdr(pg, XSRCENDHDR, &endHdr); + uint8_t corruptFlag = ((pHdr->state != NVOCMP_PGNACT) && (pHdr->state != NVOCMP_PGXDST) && (pHdr->state != NVOCMP_PGRDY) && + (pHdr->state != NVOCMP_PGACT) && (pHdr->state != NVOCMP_PGFULL) && (pHdr->state != NVOCMP_PGXSRC)); + + if (corruptFlag || (pHdr->version != NVOCMP_VERSION) || (pHdr->signature != NVOCMP_SIGNATURE)) + { + // NV page and NV driver versions are different + NVOCMP_ALERT(false, "Corrupted or Version/Signature mismatch.") + NVOCMP_EXCEPTION(pg, NVINTF_BADVERSION); + NVOCMP_failW = NVOCMP_erase(pNvHandle, pg); + if (NVOCMP_failW == NVINTF_SUCCESS) + { + // Get page header + NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) pHdr, NVOCMP_PGHDRLEN); + NVOCMP_getCompactHdr(pg, THISPAGEHDR, &thisHdr); + NVOCMP_getCompactHdr(pg, XSRCSTARTHDR, &startHdr); + NVOCMP_getCompactHdr(pg, XSRCENDHDR, &endHdr); + } + else if (NVOCMP_failW == NVINTF_LOWPOWER) + { + return (NVINTF_LOWPOWER); + } + else + { + return (NVINTF_FAILURE); + } + } + + pPageInfo->state = pHdr->state; + pPageInfo->cycle = pHdr->cycle; + pPageInfo->allActive = pHdr->allActive; + pPageInfo->mode = thisHdr.page; + if ((pPageInfo->state == NVOCMP_PGNACT) || (pPageInfo->state == NVOCMP_PGXDST)) + { + pPageInfo->offset = NVOCMP_PGDATAOFS; + } + else if (thisHdr.pageOffset != NVOCMP_NULLOFFSET) + { + pPageInfo->offset = thisHdr.pageOffset; + } + else + { + pPageInfo->offset = NVOCMP_findOffset(pg, FLASH_PAGE_SIZE); + } + pPageInfo->sPage = startHdr.page; + pPageInfo->sOffset = startHdr.pageOffset; + pPageInfo->ePage = endHdr.page; + pPageInfo->eOffset = endHdr.pageOffset; + return (NVINTF_SUCCESS); +} +#else +/****************************************************************************** + * @fn NVOCMP_scanPage + * + * @brief Local function to scan page to get page information + * + * @param pNvHandle - pointer to NV handle + * @param pg - page to scan + * @param pPageInfo - page info pointer + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_scanPage(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageInfo_t * pPageInfo) +{ + uint32_t pageHdr; + NVOCMP_pageHdr_t * pHdr = (NVOCMP_pageHdr_t *) &pageHdr; + + // Get page header + NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) pHdr, NVOCMP_PGHDRLEN); + pPageInfo->state = pHdr->state; + pPageInfo->cycle = pHdr->cycle; +#if ((NVOCMP_NVPAGES > NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) + uint8_t version = (pHdr->version << 2) | (pHdr->allActive); + if ((pHdr->signature == NVOCTP_SIGNATURE) && (version == NVOCTP_VERSION)) + { + pPageInfo->allActive = NVOCMP_SOMEINACTIVE; + pPageInfo->mode = NVOCMP_PGNORMAL; + pPageInfo->offset = NVOCMP_findOffset(pg, FLASH_PAGE_SIZE); + } +#endif + if ((pHdr->version == NVOCMP_VERSION) || (pHdr->signature == NVOCMP_SIGNATURE)) + { + pPageInfo->allActive = pHdr->allActive; + if ((pPageInfo->state == NVOCMP_PGNACT) || (pPageInfo->state == NVOCMP_PGXDST) || (pPageInfo->state == NVOCMP_PGRDY)) + { + pPageInfo->offset = NVOCMP_PGDATAOFS; + } + else + { + pPageInfo->offset = NVOCMP_findOffset(pg, FLASH_PAGE_SIZE); + } + } + return (NVINTF_SUCCESS); +} +#endif + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_findDstPage + * + * @brief Local function to find dst page after power loss + * + * @param pNvHandle - pointer to NV handle + * + * @return page + */ +static uint8_t NVOCMP_findDstPage(NVOCMP_nvHandle_t * pNvHandle) +{ + uint8_t pg; + for (pg = 0; pg < NVOCMP_NVSIZE; pg++) + { + if (pNvHandle->pageInfo[pg].mode == NVOCMP_PGCDST) + { + return (pg); + } + } + return (NVOCMP_NVSIZE); +} +#endif + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_initNv + * + * @brief Local function to init whole NV area + * + * @param pNvHandle - pointer to NV handle + * + * @return none + */ +static void NVOCMP_initNv(NVOCMP_nvHandle_t * pNvHandle) +{ + uint8_t status; + uint8_t pg; + uint8_t tmpPg; + NVOCMP_initAction_t action = NVOCMP_NORMAL_INIT; + NVOCMP_pageInfo_t * pPageInfo; + uint8_t noPgNact = 0; + uint8_t noPgXdst = 0; + uint8_t noPgRdy = 0; + uint8_t noPgAct = 0; + uint8_t noPgFull = 0; + uint8_t noPgXsrc = 0; + uint8_t noPgNdef = 0; + uint16_t pgXdst = NVOCMP_NULLPAGE; + uint16_t pgRdy = NVOCMP_NULLPAGE; + uint16_t pgAct = NVOCMP_NULLPAGE; + uint16_t pgNact = NVOCMP_NULLPAGE; +#if !defined(NVOCMP_MIGRATE_DISABLED) + uint8_t noPgLeg = 0; + uint16_t pgLegAct = NVOCMP_NULLPAGE; + uint16_t pgLegXsrc = NVOCMP_NULLPAGE; +#endif + uint16_t cleanPages = 0; + + // Scan Pages + for (pg = 0; pg < NVOCMP_NVSIZE; pg++) + { + pPageInfo = &pNvHandle->pageInfo[pg]; + status = NVOCMP_scanPage(pNvHandle, pg, pPageInfo); + if (status != NVINTF_SUCCESS) + { + return; + } + if (pPageInfo->state == NVOCMP_PGNACT) + { + noPgNact++; + pgNact = pg; + } + else if (pPageInfo->state == NVOCMP_PGXDST) + { + pgXdst = pg; + noPgXdst++; + } + else if (pPageInfo->state == NVOCMP_PGRDY) + { + pgRdy = pg; + noPgRdy++; + } + else if (pPageInfo->state == NVOCMP_PGACT) + { + pgAct = pg; + noPgAct++; + } + else if (pPageInfo->state == NVOCMP_PGFULL) + { + noPgFull++; + } + else if (pPageInfo->state == NVOCMP_PGXSRC) + { + noPgXsrc++; + } +#if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) + else if (pPageInfo->state == NVOCTP_PGACTIVE) + { + pgLegAct = pg; + noPgLeg++; + } + else if (pPageInfo->state == NVOCTP_PGXFER) + { + pgLegXsrc = pg; + noPgLeg++; + } +#endif + else + { + noPgNdef++; + } + } + + // Decide Action based on Page Informations +#if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) + if (noPgLeg > 0) + { + action = NVOCMP_NORMAL_MIGRATE; + } + else +#endif + if ((noPgNdef > 0) || (noPgXdst > 1) || (noPgXsrc > 1) || (noPgRdy > 1)) + { + // This should not happen + NVOCMP_ASSERT(false, "Something wrong serious"); + action = NVOCMP_FORCE_CLEAN; + } + else + { + if (noPgNact == NVOCMP_NVSIZE) + { + action = NVOCMP_NORMAL_INIT; + } + else if (noPgXsrc) + { + // Power lost during compaction in progress + if (noPgXdst) + { + action = NVOCMP_RECOVER_COMPACT; + } + // Corrupted due to power lost while writing onto XDST page and erased the XDST page + else if (noPgNact) + { + pgXdst = pgNact; + action = NVOCMP_RECOVER_COMPACT; + } + // Power lost after compaction done, but before erasing PGXSRC page + else + { + if (NVOCMP_findDstPage(pNvHandle) < NVOCMP_NVSIZE) + { + action = NVOCMP_RECOVER_ERASE; + } + else + { + action = NVOCMP_ERROR_UNKNOWN; + } + } + } + else if (noPgXdst) + { + action = NVOCMP_NORMAL_RESUME; + } + else if (noPgAct || noPgFull) + { + if (NVOCMP_findDstPage(pNvHandle) < NVOCMP_NVSIZE) + { + action = NVOCMP_RECOVER_ERASE; + } + else if (noPgNact) + { + pgXdst = pgNact; + NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); + action = NVOCMP_NORMAL_RESUME; + } + else + { + action = NVOCMP_ERROR_UNKNOWN; + } + } + else + { + // This case should be considered more + NVOCMP_ASSERT(false, "Something wrong serious"); + action = NVOCMP_FORCE_CLEAN; + } + } + + gAction = action; + + switch (action) + { + case NVOCMP_FORCE_CLEAN: + // Erase All pages before start + for (pg = 0; pg < NVOCMP_NVSIZE; pg++) + { + NVOCMP_failW |= NVOCMP_erase(pNvHandle, pg); + } + // init should be followed by force clean + case NVOCMP_NORMAL_INIT: + // initial state, set head page, act page and tail page + pNvHandle->headPage = 0; + pNvHandle->tailPage = NVOCMP_NVSIZE - 1; + pNvHandle->actPage = 0; + pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; + NVOCMP_changePageState(pNvHandle, pNvHandle->headPage, NVOCMP_PGRDY); + NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); + break; + case NVOCMP_NORMAL_RESUME: + // resume state, set head page, act page and tail page + pNvHandle->tailPage = pgXdst; + pNvHandle->headPage = NVOCMP_INCPAGE(pgXdst); + if (pgAct != NVOCMP_NULLPAGE) + { + pNvHandle->actPage = pgAct; + pNvHandle->actOffset = pNvHandle->pageInfo[pgAct].offset; + + NVOCMP_itemHdr_t iHdr; + int8_t status; + if (pNvHandle->actOffset > NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN) + { + NVOCMP_readHeader(pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN, &iHdr, false); + if (iHdr.stats & NVOCMP_FOLLOWBIT) + { + status = NVOCMP_findItem(pNvHandle, pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN - iHdr.len, + &iHdr, NVOCMP_FINDSTRICT, NULL); + if ((status == NVINTF_SUCCESS) && (iHdr.hofs > 0)) + { + NVOCMP_setItemInactive(pNvHandle, iHdr.hpage, iHdr.hofs); + } + } + else + { + NVOCMP_compactPage(pNvHandle, 0); + } + } + } + else if (pgRdy != NVOCMP_NULLPAGE) + { + pNvHandle->actPage = pgRdy; + pNvHandle->actOffset = pNvHandle->pageInfo[pgRdy].offset; + } + else + { + pNvHandle->actPage = pNvHandle->headPage; + pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; + } +#ifdef NVOCMP_COMPACT_WHEN_RESUME + NVOCMP_compactPage(pNvHandle, 0); +#endif + break; + case NVOCMP_RECOVER_COMPACT: + pNvHandle->tailPage = pgXdst; + pNvHandle->headPage = NVOCMP_INCPAGE(pgXdst); + NVOCMP_failW = NVOCMP_erase(pNvHandle, pgXdst); + NVOCMP_changePageState(pNvHandle, pgXdst, NVOCMP_PGXDST); + pNvHandle->forceCompact = 1; + NVOCMP_compactPage(pNvHandle, 0); + break; + case NVOCMP_RECOVER_ERASE: + pg = NVOCMP_findDstPage(pNvHandle); + pNvHandle->compactInfo.xDstPage = pg; + pNvHandle->compactInfo.xSrcSPage = pNvHandle->pageInfo[pg].sPage; + pNvHandle->compactInfo.xSrcEPage = pNvHandle->pageInfo[pg].ePage; + cleanPages = NVOCMP_cleanPage(pNvHandle); + pNvHandle->tailPage = NVOCMP_ADDPAGE(pg, cleanPages); + pNvHandle->headPage = NVOCMP_INCPAGE(pNvHandle->tailPage); + + tmpPg = NVOCMP_findPage(NVOCMP_PGACT); + if (tmpPg == NVOCMP_NULLPAGE) + { + tmpPg = NVOCMP_findPage(NVOCMP_PGRDY); + if (tmpPg == NVOCMP_NULLPAGE) + { + tmpPg = NVOCMP_findPage(NVOCMP_PGNACT); + if (tmpPg == NVOCMP_NULLPAGE) + { + tmpPg = NVOCMP_findPage(NVOCMP_PGFULL); + if (tmpPg == NVOCMP_NULLPAGE) + { + tmpPg = pNvHandle->headPage; + } + } + } + } + + pNvHandle->actPage = tmpPg; + pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; + NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); + break; +#if !defined(NVOCMP_MIGRATE_DISABLED) + case NVOCMP_NORMAL_MIGRATE: + if (pgLegAct != NVOCMP_NULLPAGE) + { + pNvHandle->actPage = pgLegAct; + } + else + { + pNvHandle->actPage = pgLegXsrc; + } + pNvHandle->headPage = pNvHandle->actPage; + pNvHandle->tailPage = NVOCMP_DECPAGE(pNvHandle->headPage); + pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; + NVOCMP_migratePage(pNvHandle, pNvHandle->actPage); + NVOCMP_compactPage(pNvHandle, 0); + break; +#endif + case NVOCMP_ERROR_UNKNOWN: + /* When this error happens, NV area should be erased to restart. + * This while loop is for only debug purpose */ + NVOCMP_ASSERT1(0); + default: + break; + } +} +#endif +#if (NVOCMP_NVPAGES == NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_initNv + * + * @brief Local function to init whole NV area + * + * @param pNvHandle - pointer to NV handle + * + * @return none + */ +static void NVOCMP_initNv(NVOCMP_nvHandle_t * pNvHandle) +{ + uint8_t status, prevactPage; + NVOCMP_itemHdr_t iHdr; + uint8_t pg; + NVOCMP_initAction_t action; + NVOCMP_pageInfo_t * pPageInfo; + uint16_t pgAct; +#if !defined(NVOCMP_MIGRATE_DISABLED) + uint8_t noPgLeg = 0; +#endif + bool compact = false, compaction_occurred = false; + + // Scan Pages + pNvHandle->xsrcPage = NVOCMP_NULLPAGE; + pNvHandle->tailPage = NVOCMP_NULLPAGE; + + for (pg = 0; pg < NVOCMP_NVSIZE; pg++) + { + pPageInfo = &pNvHandle->pageInfo[pg]; + NVOCMP_scanPage(pNvHandle, pg, pPageInfo); +#if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) + if (pPageInfo->state == NVOCTP_PGACTIVE) + { + pNvHandle->actPage = pg; + noPgLeg++; + } + else if (pPageInfo->state == NVOCTP_PGXFER) + { + pNvHandle->xsrcPage = pg; + noPgLeg++; + } + else +#endif + if ((pPageInfo->state == NVOCMP_PGACT) || (pPageInfo->state == NVOCMP_PGFULL)) + { + pNvHandle->actPage = pg; + } + else if (pPageInfo->state == NVOCMP_PGXSRC) + { + pNvHandle->xsrcPage = pg; + } + else if (pPageInfo->state == NVOCMP_PGXDST) + { + pNvHandle->tailPage = pg; + } + } + + action = NVOCMP_NORMAL_INIT; + // Decide Action based on Page Informations +#if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) + if (noPgLeg > 0) + { + action = NVOCMP_NORMAL_MIGRATE; + } + else + { +#endif + if (pNvHandle->actPage != NVOCMP_NULLPAGE) + { + action = NVOCMP_NORMAL_RESUME; + } + else if (pNvHandle->xsrcPage != NVOCMP_NULLPAGE) + { + pNvHandle->actPage = pNvHandle->xsrcPage; + action = NVOCMP_RECOVER_COMPACT; + } + else if (pNvHandle->tailPage != NVOCMP_NULLPAGE) + { + pgAct = NVOCMP_INCPAGE(pNvHandle->tailPage); + if (pNvHandle->pageInfo[pgAct].offset) + { + pNvHandle->actPage = pgAct; + action = NVOCMP_RECOVER_COMPACT; + } + else + { + pNvHandle->tailPage = NVOCMP_NULLPAGE; + } + } + + if (pNvHandle->actPage == NVOCMP_NULLPAGE) + { + pNvHandle->actPage = 0; + NVOCMP_failW |= NVOCMP_erase(pNvHandle, 0); + NVOCMP_changePageState(pNvHandle, 0, NVOCMP_PGRDY); + } + + if (pNvHandle->tailPage == NVOCMP_NULLPAGE) + { + pNvHandle->tailPage = NVOCMP_INCPAGE(pNvHandle->actPage); + ; + NVOCMP_failW |= NVOCMP_erase(pNvHandle, pNvHandle->tailPage); + NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); + } + + pNvHandle->headPage = pNvHandle->actPage; + pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; +#if ((NVOCMP_NVPAGES != NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) + } +#endif + + gAction = action; + + switch (action) + { + case NVOCMP_NORMAL_RESUME: + // resume state, set head page, act page and tail page + do + { + compaction_occurred = false; + if (pNvHandle->actOffset > NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN) + { + NVOCMP_readHeader(pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN, &iHdr, false); + if (iHdr.stats & NVOCMP_FOLLOWBIT) + { + /* Cache current active page value before search starts */ + prevactPage = pNvHandle->actPage; + status = NVOCMP_findItem(pNvHandle, pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN - iHdr.len, + &iHdr, NVOCMP_FINDSTRICT, NULL); + + /* If the current active page is different than previous, then + * compaction occurred and the search for duplicate must occur + * again. + * If item is found and compaction did not occur, it is a true + * duplicate, so the item can be deleted. */ + compaction_occurred = (prevactPage != pNvHandle->actPage); + if ((status == NVINTF_SUCCESS) && (iHdr.hofs > 0) && !compaction_occurred) + { + NVOCMP_setItemInactive(pNvHandle, iHdr.hpage, iHdr.hofs); + } + } + else + { + pNvHandle->forceCompact = 1; + compact = true; + } + } + } while (compaction_occurred); /* Repeat until compaction does not occur while searching */ +#ifdef NVOCMP_COMPACT_WHEN_RESUME + compact = true; +#endif + break; +#if !defined(NVOCMP_MIGRATE_DISABLED) + case NVOCMP_NORMAL_MIGRATE: + if (pNvHandle->actPage == NVOCMP_NULLPAGE) + { + pNvHandle->actPage = pNvHandle->xsrcPage; + } + pNvHandle->headPage = pNvHandle->actPage; + pNvHandle->tailPage = NVOCMP_DECPAGE(pNvHandle->headPage); + pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; + NVOCMP_failW = NVOCMP_erase(pNvHandle, pNvHandle->tailPage); + NVOCMP_migratePage(pNvHandle, pNvHandle->actPage); + compact = true; + break; +#endif + case NVOCMP_RECOVER_COMPACT: + pNvHandle->forceCompact = 1; + compact = true; + break; + default: + break; + } + + if (compact) + { + NVOCMP_compactPage(pNvHandle, 0); + } +} +#endif + +#if (NVOCMP_NVPAGES == NVOCMP_NVONEP) +/****************************************************************************** + * @fn NVOCMP_initNv + * + * @brief Local function to init whole NV area + * + * @param pNvHandle - pointer to NV handle + * + * @return none + */ +static void NVOCMP_initNv(NVOCMP_nvHandle_t * pNvHandle) +{ + uint8_t status; + NVOCMP_itemHdr_t iHdr; + NVOCMP_initAction_t action; + NVOCMP_pageInfo_t * pPageInfo; + bool compact = false; + + // Scan Pages + pNvHandle->xsrcPage = NVOCMP_NULLPAGE; + pNvHandle->tailPage = NVOCMP_NULLPAGE; + + pPageInfo = &pNvHandle->pageInfo[0]; + NVOCMP_scanPage(pNvHandle, 0, pPageInfo); + if ((pPageInfo->state == NVOCMP_PGACT) || (pPageInfo->state == NVOCMP_PGFULL)) + { + pNvHandle->actPage = 0; + } + else if (pPageInfo->state == NVOCMP_PGXSRC) + { + pNvHandle->xsrcPage = 0; + } + + action = NVOCMP_NORMAL_INIT; + // Decide Action based on Page Informations + if (pNvHandle->actPage != NVOCMP_NULLPAGE) + { + action = NVOCMP_NORMAL_RESUME; + } + else if (pNvHandle->xsrcPage != NVOCMP_NULLPAGE) + { + pNvHandle->actPage = 0; + action = NVOCMP_RECOVER_COMPACT; + } + + if (pNvHandle->actPage == NVOCMP_NULLPAGE) + { + pNvHandle->actPage = 0; + NVOCMP_failW |= NVOCMP_erase(pNvHandle, 0); + NVOCMP_changePageState(pNvHandle, 0, NVOCMP_PGRDY); + } + + pNvHandle->tailPage = 0; + pNvHandle->headPage = 0; + pNvHandle->actOffset = pNvHandle->pageInfo[0].offset; + + gAction = action; + + switch (action) + { + case NVOCMP_NORMAL_RESUME: + // resume state, set head page, act page and tail page + if (pNvHandle->actOffset > NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN) + { + NVOCMP_readHeader(pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN, &iHdr, false); + if (iHdr.stats & NVOCMP_FOLLOWBIT) + { + status = NVOCMP_findItem(pNvHandle, pNvHandle->actPage, pNvHandle->actOffset - NVOCMP_ITEMHDRLEN - iHdr.len, &iHdr, + NVOCMP_FINDSTRICT, NULL); + if ((status == NVINTF_SUCCESS) && (iHdr.hofs > 0)) + { + NVOCMP_setItemInactive(pNvHandle, iHdr.hpage, iHdr.hofs); + } + } + else + { + pNvHandle->forceCompact = 1; + compact = true; + } + } +#ifdef NVOCMP_COMPACT_WHEN_RESUME + compact = true; +#endif + break; + case NVOCMP_RECOVER_COMPACT: + pNvHandle->forceCompact = 1; + compact = true; + break; + default: + break; + } + + if (compact) + { + NVOCMP_compactPage(pNvHandle, 0); + } +} +#endif + +/****************************************************************************** + * @fn NVOCMP_checkItem + * + * @brief Local function to check parameters and locate existing item + * + * @param id - NV item type identifier + * @param len - NV item data length + * @param pHdr - pointer to header buffer + * @param flag - flag for item search + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_checkItem(NVINTF_itemID_t * id, uint16_t len, NVOCMP_itemHdr_t * pHdr, uint8_t flag) +{ + if (len > NVOCMP_MAXLEN) + { + // Item data is too long + NVOCMP_ALERT(false, "Item data too large.") + return (NVINTF_BADLENGTH); + } + if (id->systemID > NVOCMP_MAXSYSID) + { + // Too large for compressed header + NVOCMP_ALERT(false, "Item sysid too large.") + return (NVINTF_BADSYSID); + } + if (id->itemID > NVOCMP_MAXITEMID) + { + // Too large for compressed header + NVOCMP_ALERT(false, "Item itemid too large.") + return (NVINTF_BADITEMID); + } + if (id->subID > NVOCMP_MAXSUBID) + { + // Too large for compressed header + NVOCMP_ALERT(false, "Item subid too large.") + return (NVINTF_BADSUBID); + } + + if (NVOCMP_failF == NVINTF_NOTREADY) + { + // NV driver has not been initialized + NVOCMP_ASSERT(false, "Driver uninitialized.") + return (NVINTF_NOTREADY); + } + + pHdr->len = len; + pHdr->hofs = 0; + pHdr->cmpid = NVOCMP_CMPRID(id->systemID, id->itemID, id->subID); + pHdr->subid = id->subID; + pHdr->itemid = id->itemID; + pHdr->sysid = id->systemID; + pHdr->sig = NVOCMP_SIGNATURE; + + return (NVINTF_SUCCESS); +} + +/****************************************************************************** + * @fn NVOCMP_getDstPage + * + * @brief Local function to find the page where an item will be written on + * + * @param pNvHandle - pointer to NV handle + * @param len - item size to write + * + * @return page number or NVOCMP_NULLPAGE + */ +static uint8_t NVOCMP_getDstPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t len) +{ + uint8_t dstPg = NVOCMP_NULLPAGE; + uint8_t pg = pNvHandle->actPage; + NVOCMP_pageHdr_t pageHdr; +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + uint16_t nvSearched = 0; + for (pg = pNvHandle->actPage; nvSearched < NVOCMP_NVSIZE; pg = NVOCMP_INCPAGE(pg)) + { + nvSearched++; + if (pg == pNvHandle->tailPage) + { + continue; + } +#endif + NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + if (pageHdr.state == NVOCMP_PGFULL) + { +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + continue; +#else + return (dstPg); +#endif + } + if ((pageHdr.state == NVOCMP_PGNACT) || (pageHdr.state == NVOCMP_PGRDY)) + { + NVOCMP_changePageState(pNvHandle, pg, NVOCMP_PGACT); + } + + pNvHandle->actPage = pg; + pNvHandle->actOffset = pNvHandle->pageInfo[pg].offset; + if ((pNvHandle->pageInfo[pg].offset + len) <= FLASH_PAGE_SIZE) + { + dstPg = pNvHandle->actPage; +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + break; +#endif + } + else + { + NVOCMP_changePageState(pNvHandle, pg, NVOCMP_PGFULL); + } +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + } +#endif + return (dstPg); +} + +/****************************************************************************** + * @fn NVOCMP_addItem + * + * @brief Local function to check for adequate space and create a new item + * + * @param pNvHandle - pointer to NV handle + * @param iHdr - pointer to header buffer + * @param pBuf - pointer to item + * @param wm - write mode + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_addItem(NVOCMP_nvHandle_t * pNvHandle, NVOCMP_itemHdr_t * iHdr, uint8_t * pBuf, NVOCMP_writeMode_t wm) +{ + uint8_t err; + uint8_t dstPg; + uint16_t iLen; + bool compact = false; + NVOCMP_itemHdr_t hdr; + + iLen = NVOCMP_ITEMHDRLEN + iHdr->len; + dstPg = NVOCMP_getDstPage(pNvHandle, iLen); + + if (dstPg == NVOCMP_NULLPAGE) + { + compact = true; + // Won't fit on the active page, compact and check again + if (NVOCMP_compactPage(pNvHandle, iLen) < iLen) + { + // Failure means there's no place to put this item + NVOCMP_ALERT(false, "Out of NV.") + err = (NVOCMP_failW != NVINTF_SUCCESS) ? NVOCMP_failW : NVINTF_BADLENGTH; + return (err); + } + } + + if ((NVOCMP_failW == NVINTF_SUCCESS) && ((wm == NVOCMP_WRITE) || ((wm == NVOCMP_UPDATE) && (compact == true)))) + { + /* get item offset after compaction */ + hdr.sysid = iHdr->sysid; + hdr.itemid = iHdr->itemid; + hdr.subid = iHdr->subid; + (void) NVOCMP_findItem(pNvHandle, pNvHandle->actPage, pNvHandle->actOffset, &hdr, NVOCMP_FINDSTRICT, NULL); + iHdr->hpage = hdr.hpage; + iHdr->hofs = hdr.hofs; + } + +#if NVOCMP_NWSAMEITEM + bool changed = false; + if ((iHdr->hofs) && (iHdr->len)) + { +#define NVOCMP_COMPARE_SIZE 32 + uint8_t readBuf[NVOCMP_COMPARE_SIZE]; + uint16_t iOfs = (iHdr->hofs - iHdr->len); + uint16_t dOfs = 0; + uint16_t len2cmp = 0; + + // Failure to parse the command + NVOCMP_ALERT(iHdr->hofs >= iHdr->len, "Something wrong in parsing.") + + do + { + len2cmp = (iHdr->len - dOfs) > NVOCMP_COMPARE_SIZE ? NVOCMP_COMPARE_SIZE : (iHdr->len - dOfs); + NVOCMP_read(iHdr->hpage, iOfs + dOfs, readBuf, len2cmp); + if (memcmp(readBuf, pBuf + dOfs, len2cmp)) + { + changed = true; + break; + } + dOfs += len2cmp; + } while (dOfs < iHdr->len); + } + else + { + changed = true; + } + + if (changed) + { + // Create the new NV item + NVOCMP_writeItem(pNvHandle, iHdr, pNvHandle->actPage, pNvHandle->actOffset, pBuf); + } + else + { + iHdr->hofs = 0; + } +#else + // Create the new NV item + NVOCMP_writeItem(pNvHandle, iHdr, pNvHandle->actPage, pNvHandle->actOffset, pBuf); +#endif + + // Status of writing/erasing Flash + return (NVOCMP_failW); +} + +/****************************************************************************** + * @fn NVOCMP_read + * + * @brief Writes to a flash buffer from RAM + * + * @param pg - Flash page to write from + * @param off - Offset in destination page to read from + * @param pBuf - Pointer to write the results into + * @param len - Number of bytes to write into + * + * @return NVS_STATUS_SUCCESS or other NVS status code + */ +static inline void NVOCMP_read(uint8_t pg, uint16_t off, uint8_t * pBuf, uint16_t len) +{ +#ifndef NV_LINUX + NVS_read(NVOCMP_nvsHandle, NVOCMP_FLASHOFFSET(pg, off), (uint8_t *) pBuf, len); +#else + NV_LINUX_read(pg, off, pBuf, len); +#endif +} + +/****************************************************************************** + * @fn NVOCMP_write + * + * @brief Writes to a flash buffer from RAM + * + * @param dstPg - Flash page to write to + * @param off - offset in destination page to write to + * @param pBuf - Pointer to caller's buffer to write & verify + * @param len - number of bytes to write from pBuf + * + * @return NVS_STATUS_SUCCESS or other NVS status code + */ +static uint8_t NVOCMP_write(uint8_t dstPg, uint16_t off, uint8_t * pBuf, uint16_t len) +{ + uint8_t err = NVINTF_SUCCESS; + int_fast16_t nvsRes = 0; + + // check voltage if possible + NVOCMP_FLASHACCESS(err) + + if (NVINTF_SUCCESS == err) + { +#ifndef NV_LINUX + nvsRes = NVS_write(NVOCMP_nvsHandle, NVOCMP_FLASHOFFSET(dstPg, off), pBuf, len, NVS_WRITE_POST_VERIFY); +#else + nvsRes = NV_LINUX_write(dstPg, off, pBuf, len); +#endif + } + else + { + err = NVINTF_LOWPOWER; + } + + if (nvsRes < 0) + { + err = NVINTF_FAILURE; + } + + NVOCMP_ALERT(NVINTF_LOWPOWER != err, "Voltage check failed.") + if (NVINTF_FAILURE == err) + { + NVOCMP_ALERT(NVINTF_FAILURE != err, "NVS write failure.") + } + + return (err); +} + +/****************************************************************************** + * @fn NVOCMP_erase + * + * @brief Erases a flash page + * + * @param pNvHandle - pointer to NV handle + * @param dstPg - Flash page to write to + * + * @return NVINT_SUCCESS or other NVINTF status code + */ +static uint8_t NVOCMP_erase(NVOCMP_nvHandle_t * pNvHandle, uint8_t dstPg) +{ + uint8_t err = NVINTF_SUCCESS; + int_fast16_t nvsRes = 0; + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + + if (NVINTF_SUCCESS == err) + { +#ifndef NV_LINUX + nvsRes = NVS_erase(NVOCMP_nvsHandle, NVOCMP_FLASHOFFSET(dstPg, 0), NVOCMP_nvsAttrs.sectorSize); +#else + nvsRes = NV_LINUX_erase(dstPg); +#endif + if (nvsRes < 0) + { + err = NVINTF_FAILURE; + } + else + { + // Bump the compaction cycle counter, wrap-around if at maximum + pNvHandle->pageInfo[dstPg].cycle = + (pNvHandle->pageInfo[dstPg].cycle < NVOCMP_MAXCYCLE) ? (pNvHandle->pageInfo[dstPg].cycle + 1) : NVOCMP_MINCYCLE; + NVOCMP_setPageState(pNvHandle, dstPg, NVOCMP_PGNACT); + NVOCMP_setCompactHdr(dstPg, NVOCMP_NULLPAGE, NVOCMP_NULLOFFSET, THISPAGEHDR); + NVOCMP_setCompactHdr(dstPg, NVOCMP_NULLPAGE, NVOCMP_NULLOFFSET, XSRCSTARTHDR); + NVOCMP_setCompactHdr(dstPg, NVOCMP_NULLPAGE, NVOCMP_NULLOFFSET, XSRCENDHDR); + pNvHandle->pageInfo[dstPg].offset = NVOCMP_PGDATAOFS; + pNvHandle->pageInfo[dstPg].mode = NVOCMP_PGNORMAL; + } + } + else + { + err = NVINTF_LOWPOWER; + } + + NVOCMP_ALERT(NVINTF_LOWPOWER != err, "Voltage check failed.") + NVOCMP_ALERT(NVINTF_FAILURE != err, "NVS erase failure.") + + return (err); +} + +/****************************************************************************** + * @fn NVOCMP_writeItem + * + * @brief Write entire NV item to new location on active Flash page. + * Each call to NVS_write() does a read-back to verify. If an + * error is detected, the 'failW' flag is set to inhibit further + * flash write attempts until the next NV transaction. + * + * @param pNvHandle - pointer to NV handle + * @param pHdr - Pointer to caller's item header buffer + * @param dstPg - Destination NV Flash page + * @param dstOff - Destination offset + * @param pBuf - Points to buffer which will be written to item + * + * @return none + */ +static void NVOCMP_writeItem(NVOCMP_nvHandle_t * pNvHandle, NVOCMP_itemHdr_t * pHdr, uint8_t dstPg, uint16_t dstOff, uint8_t * pBuf) +{ + uint16_t iLen; + NVOCMP_pageHdr_t pageHdr; + + NVOCMP_read(dstPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + if (pageHdr.state == NVOCMP_PGRDY) + { + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGACT); + } + + if (pageHdr.state != NVOCMP_PGACT) + { + NVOCMP_failW = NVINTF_FAILURE; + return; + } + + NVOCMP_ALERT(pageHdr.state == NVOCMP_PGACT, "Something wrong.") + + // Total length of this item + iLen = NVOCMP_ITEMHDRLEN + pHdr->len; + + if ((dstOff + iLen) <= FLASH_PAGE_SIZE) + { + cmpIH_t cHdr; + uint16_t hOfs, dLen; + uint8_t newCRC; + + // Compressed item header information <-- Lower Addr Higher Addr--> + // Byte: [0] [1] [2] [3] [4] [5] [6] + // Item: SSSSSSII IIIIIIII SSSSSSSS SSLLLLLL LLLLLLCC CCCCCCAV SSSSSSSS + // LSB of field: ^ ^ ^ ^ ^ +#if NVOCMP_HDRLE + cHdr[0] = (pHdr->sysid & 0x3F) | ((pHdr->itemid & 0x3) << 6); + cHdr[1] = (pHdr->itemid >> 2) & 0xFF; + cHdr[2] = pHdr->subid & 0xFF; + cHdr[3] = ((pHdr->subid >> 8) & 0x3) | ((pHdr->len & 0x3F) << 2); + cHdr[4] = (pHdr->len >> 6) & 0x3F; +#else + cHdr[0] = ((pHdr->sysid << 2) | ((pHdr->itemid >> 8) & 0x3)); + cHdr[1] = (pHdr->itemid & 0xFF); + cHdr[2] = ((pHdr->subid >> 2) & 0xFF); + cHdr[3] = ((pHdr->subid & 0x3) << 6) | ((pHdr->len >> 6) & 0x3F); + cHdr[4] = ((pHdr->len & 0x3F) << 2); +#endif + // Header is located after the item data + dLen = pHdr->len; + hOfs = dstOff + dLen; + + if (iLen <= NVOCMP_SMALLITEM) + { + // Construct item in one buffer + // Put data into buffer + memcpy(NVOCMP_itemBuffer, (const void *) pBuf, dLen); + // Put most of header into buffer + memcpy(NVOCMP_itemBuffer + dLen, (const void *) cHdr, NVOCMP_HDRCRCINC); + // Calculate CRC + newCRC = NVOCMP_doRAMCRC(NVOCMP_itemBuffer, dLen + NVOCMP_HDRCRCINC, 0); +#if NVOCMP_HDRLE + // Insert CRC and last bytes + cHdr[4] |= ((newCRC & 0x3) << 6); + // Note NVOCMP_VALIDIDBIT set implicitly zero + cHdr[5] = ((newCRC >> 2) & 0x3F) | (NVOCMP_ACTIVEIDBIT << 6); +#else + // Insert CRC and last bytes + cHdr[4] |= ((newCRC >> 6) & 0x3); + // Note NVOCMP_VALIDIDBIT set implicitly zero + cHdr[5] = ((newCRC & 0x3F) << 2) | NVOCMP_ACTIVEIDBIT; +#endif + cHdr[6] = NVOCMP_SIGNATURE; + memcpy(NVOCMP_itemBuffer + dLen, (const void *) cHdr, NVOCMP_ITEMHDRLEN); + // NVS_write + NVOCMP_failW = NVOCMP_write(dstPg, dstOff, NVOCMP_itemBuffer, iLen); + // Advance to next location + dstOff += iLen; + pNvHandle->actOffset += iLen; + pNvHandle->pageInfo[dstPg].offset = dstOff; + } + else + { + // Write header/item separately + // Calculate CRC on data portion + newCRC = NVOCMP_doRAMCRC(pBuf, dLen, 0); + // Finish CRC using header portion + newCRC = NVOCMP_doRAMCRC(cHdr, NVOCMP_HDRCRCINC, newCRC); + // Complete Header with CRC, bits, and sig +#if NVOCMP_HDRLE + // Insert CRC and last bytes + cHdr[4] |= ((newCRC & 0x3) << 6); + // Note NVOCMP_VALIDIDBIT set implicitly zero + cHdr[5] = ((newCRC >> 2) & 0x3F) | (NVOCMP_ACTIVEIDBIT << 6); +#else + // Insert CRC and last bytes + cHdr[4] |= ((newCRC >> 6) & 0x3); + // Note NVOCMP_VALIDIDBIT set implicitly zero + cHdr[5] = ((newCRC & 0x3F) << 2) | NVOCMP_ACTIVEIDBIT; +#endif + cHdr[6] = NVOCMP_SIGNATURE; + // Write data + NVOCMP_failW = NVOCMP_write(dstPg, dstOff, pBuf, dLen); + // Write header + NVOCMP_failW |= NVOCMP_write(dstPg, hOfs, cHdr, NVOCMP_ITEMHDRLEN); + // Advance to next location + NVOCMP_ASSERT(dstOff < (dstOff + iLen), "Page offset overflow!") + if (!NVOCMP_failW) + { + dstOff += iLen; + pNvHandle->actOffset += iLen; + pNvHandle->pageInfo[dstPg].offset = dstOff; + } + else + { + return; + } + } + // If there was a write failure, delete item + NVOCMP_ALERT(!NVOCMP_failW, "Driver write failure. Item deleted.") + if (NVOCMP_failW) + { + NVOCMP_setItemInactive(pNvHandle, dstPg, hOfs); + } + } + else + { + // Not enough room on page + NVOCMP_failW = NVINTF_BADLENGTH; + } +} + +/****************************************************************************** + * @fn NVOCMP_readHeader + * + * @brief Read header block from NV and expand into caller's buffer + * + * @param pg - A valid NV Flash page + * @param ofs - A valid offset into the page + * @param pHdr - Pointer to caller's item header buffer + * @param flag - fast flag (not used if NVOCMP_RAM_OPTIMIZATION is defined) + * + * @return none + */ +static void NVOCMP_readHeader(uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * pHdr, bool flag) +{ +#ifndef NVOCMP_RAM_OPTIMIZATION +#ifdef NVOCMP_GPRAM + uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; +#else + uint8_t * pTBuffer = (uint8_t *) tBuffer; +#endif +#endif + + cmpIH_t cHdr; +#ifndef NVOCMP_RAM_OPTIMIZATION + if (flag) + { + memcpy((uint8_t *) cHdr, (uint8_t *) (pTBuffer + ofs), NVOCMP_ITEMHDRLEN); + } + else + { +#endif + // Get item header from Flash + NVOCMP_read(pg, ofs, (uint8_t *) cHdr, NVOCMP_ITEMHDRLEN); +#ifndef NVOCMP_RAM_OPTIMIZATION + } +#endif + // Offset to compressed header + pHdr->hofs = ofs; + pHdr->hpage = pg; + + // Compressed item header information <-- Lower Addr Higher Addr--> + // Byte: [0] [1] [2] [3] [4] [5] [6] + // Item: SSSSSSII IIIIIIII SSSSSSSS SSLLLLLL LLLLLLCC CCCCCCAV SSSSSSSS + // LSB of field: ^ ^ ^ ^ ^ +#if NVOCMP_HDRLE + pHdr->sysid = cHdr[0] & 0x3F; + pHdr->itemid = ((cHdr[0] >> 6) & 0x3) | (cHdr[1] << 2); + pHdr->subid = cHdr[2] | ((cHdr[3] & 0x3) << 8); + pHdr->len = ((cHdr[3] >> 2) & 0x3F) | ((cHdr[4] & 0x3F) << 6); + pHdr->crc8 = ((cHdr[4] >> 6) & 0x3) | ((cHdr[5] & 0x3F) << 2); + pHdr->stats = (cHdr[5] >> 6) & (NVOCMP_VALIDIDBIT | NVOCMP_ACTIVEIDBIT); + pHdr->sig = cHdr[6]; +#else + pHdr->sysid = (cHdr[0] >> 2) & 0x3F; + pHdr->itemid = ((cHdr[0] & 0x3) << 8) | cHdr[1]; + pHdr->subid = (cHdr[2] << 2) | ((cHdr[3] >> 6) & 0x3); + pHdr->len = ((cHdr[3] & 0x3F) << 6) | ((cHdr[4] >> 2) & 0x3F); + pHdr->crc8 = ((cHdr[4] & 0x3) << 6) | ((cHdr[5] >> 2) & 0x3F); + pHdr->stats = cHdr[5] & (NVOCMP_VALIDIDBIT | NVOCMP_ACTIVEIDBIT); + pHdr->sig = cHdr[6]; +#endif + pHdr->cmpid = NVOCMP_CMPRID(pHdr->sysid, pHdr->itemid, pHdr->subid); + // Our item has correct signature? + if (pHdr->sig != NVOCMP_SIGNATURE) + { + // Indicate item is invalid + NVOCMP_ALERT(false, "Invalid signature detected! Item corrupted.") + pHdr->stats |= NVOCMP_VALIDIDBIT; + } + else + { + (pHdr->stats) |= NVOCMP_FOLLOWBIT; + NVOCMP_ALERT(pHdr->stats & NVOCMP_FOLLOWBIT, "Item gap detected. Item not followed.") + } +} + +/****************************************************************************** + * @fn NVOCMP_readItem + * + * @brief Function to read an item described by iHdr into pBuf + * + * @param iHdr - pointer to an item header struct + * @param bOfs - offset into NV data block + * @param len - length of NV data to return (0 is illegal) + * @param pBuf - pointer to caller's read data buffer (NULL is illegal) + * @param flag - fast flag (not used if NVOCMP_RAM_OPTIMIZATION is defined) + * + * @return NVINTF_SUCCESS or specific failure code + */ +static uint8_t NVOCMP_readItem(NVOCMP_itemHdr_t * iHdr, uint16_t ofs, uint16_t len, void * pBuf, bool flag) +{ + uint8_t err = NVINTF_SUCCESS; + uint16_t dOfs, iOfs; +#ifndef NVOCMP_RAM_OPTIMIZATION +#ifdef NVOCMP_GPRAM + uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; +#else + uint8_t * pTBuffer = (uint8_t *) tBuffer; +#endif +#endif + iOfs = (iHdr->hofs - iHdr->len); + + // Optional CRC integrity check +#if NVOCMP_CRCONREAD + err = NVOCMP_verifyCRC(iOfs, iHdr->len, iHdr->crc8, iHdr->hpage, flag); +#endif + + if (err == NVINTF_SUCCESS) + { + // Offset to start of item data + dOfs = iOfs + ofs; + if ((dOfs + len) <= iHdr->hofs) + { +#ifndef NVOCMP_RAM_OPTIMIZATION + if (flag) + { + // Copy from RAM + memcpy((uint8_t *) pBuf, (uint8_t *) (pTBuffer + dOfs), len); + } + else + { +#endif + // Copy NV data block to caller's buffer + NVOCMP_read(iHdr->hpage, dOfs, (uint8_t *) pBuf, len); +#ifndef NVOCMP_RAM_OPTIMIZATION + } +#endif + } + else + { + // Bad length or offset + err = (len > iHdr->len) ? NVINTF_BADLENGTH : NVINTF_BADOFFSET; + } + } + + return (err); +} + +/****************************************************************************** + * @fn NVOCMP_setItemInactive + * + * @brief Mark an item as inactive + * + * @param pNvHandle - pointer to NV handle + * @param pg - page where the item is located + * @param iOfs - Offset to item header (lowest address) in active page + * + * @return none + */ +static void NVOCMP_setItemInactive(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t iOfs) +{ + uint8_t tmp; + + // Get byte with validity bit + tmp = NVOCMP_readByte(pg, iOfs + NVOCMP_HDRVLDOFS); + + // Remove ACTIVE_IDS_MARK +#if NVOCMP_HDRLE + tmp &= ~(NVOCMP_ACTIVEIDBIT << 6); +#else + tmp &= ~NVOCMP_ACTIVEIDBIT; +#endif + // Mark the item as inactive + NVOCMP_writeByte(pg, iOfs + NVOCMP_HDRVLDOFS, tmp); + + if (pNvHandle->pageInfo[pg].allActive) + { + tmp = NVOCMP_readByte(pg, NVOCMP_PGHDRVER); + tmp &= ~NVOCMP_ALLACTIVE; + NVOCMP_writeByte(pg, NVOCMP_PGHDRVER, tmp); + pNvHandle->pageInfo[pg].allActive = NVOCMP_SOMEINACTIVE; + } +} + +/****************************************************************************** + * @fn NVOCMP_setCompactHdr + * + * @brief Set compact header + * + * @param dstPg - destination page to write the header + * @param pg - page of compaction + * @param offset - offset of compaction + * @param location - location of the header + * + * @return none + */ +static void NVOCMP_setCompactHdr(uint8_t dstPg, uint8_t pg, int16_t offset, uint16_t location) +{ + NVOCMP_compactHdr_t hdr = DEFAULT_COMPACTHDR; + hdr.page = pg; + hdr.pageOffset = offset; + + NVOCMP_failW = NVOCMP_write(dstPg, (location + 1) * NVOCMP_COMPACTHDRLEN, (uint8_t *) &hdr, NVOCMP_COMPACTHDRLEN); +} + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_getCompactHdr + * + * @brief Get compact header + * + * @param dstPg - destination page to read the header + * @param location - location of the header + * @param pHdr - pointer to the header + * + * @return none + */ +static void NVOCMP_getCompactHdr(uint8_t dstPg, uint16_t location, NVOCMP_compactHdr_t * pHdr) +{ + NVOCMP_read(dstPg, (location + 1) * NVOCMP_COMPACTHDRLEN, (uint8_t *) pHdr, NVOCMP_COMPACTHDRLEN); +} +#endif + +/****************************************************************************** + * @fn NVOCMP_setPageState + * + * @brief Set specified NV page state + * + * @param pNvHandle - pointer to NV handle + * @param pg - target NV page + * @param state - state of the page + * + * @return none + */ +static void NVOCMP_setPageState(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageState_t state) +{ + NVOCMP_pageHdr_t pHdr; + + // Load header + pHdr.state = (uint8_t) state; + pHdr.cycle = (uint8_t) pNvHandle->pageInfo[pg].cycle; + pHdr.allActive = NVOCMP_ALLACTIVE; + pHdr.version = (uint8_t) NVOCMP_VERSION; + pHdr.signature = (uint8_t) NVOCMP_SIGNATURE; + + // Write to page + NVOCMP_failW = NVOCMP_write(pg, 0, (uint8_t *) &pHdr, NVOCMP_PGHDRLEN); + + if (NVOCMP_failW == NVINTF_SUCCESS) + { + if (state == NVOCMP_PGACT) + { + // No errors, switch active page + pNvHandle->actPage = pg; + } + pNvHandle->pageInfo[pg].state = state; + pNvHandle->pageInfo[pg].allActive = NVOCMP_ALLACTIVE; + } +} + +/****************************************************************************** + * @fn NVOCMP_changePageState + * + * @brief Change NV page state + * + * @param pNvHandle - pointer to NV handle + * @param pg - target NV page + * @param state - state of the page + * + * @return none + */ +static void NVOCMP_changePageState(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, NVOCMP_pageState_t state) +{ + NVOCMP_writeByte(pg, NVOCMP_PGHDROFS, (uint8_t) state); + + if (NVOCMP_failW == NVINTF_SUCCESS) + { + if (state == NVOCMP_PGACT) + { + // No errors, switch active page + pNvHandle->actPage = pg; + } + pNvHandle->pageInfo[pg].state = state; + } +} + +/****************************************************************************** + * @fn NVOCMP_findOffset + * + * @brief Find the offset to next available empty space in specified page + * + * @param pg - Valid NV page on which to find offset to next available data + * @param ofs - Beginning offset to start search + * + * @return Number of bytes from start of page to next available item location + */ +#if NVOCMP_FASTOFF +static uint16_t NVOCMP_findOffset(uint8_t pg, uint16_t ofs) +{ + uint8_t i, j; + uint32_t * tmp; +#ifdef NVOCMP_GPRAM + uint32_t vm; + uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; +#else + uint8_t * pTBuffer = (uint8_t *) tBuffer; +#endif + +#ifdef NVOCMP_GPRAM + NVOCMP_disableCache(&vm); +#endif + + NVOCMP_read(pg, 0, (uint8_t *) pTBuffer, FLASH_PAGE_SIZE); + + // Find first non-erased 4-byte location + tmp = (uint32_t *) pTBuffer; + while (ofs >= sizeof(uint32_t)) + { + ofs -= sizeof(uint32_t); + tmp = (uint32_t *) (pTBuffer + ofs); + if ((*tmp) != NVOCMP_ERASEDWORD) + { + break; + } + } + + // Starting with LSB, look for non-erased byte + for (i = j = 1; i <= 4; i++) + { + if (((*tmp) & NVOCMP_ERASEDBYTE) != NVOCMP_ERASEDBYTE) + { + // Last non-erased byte so far + j = i; + } + (*tmp) >>= 8; + } + +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + + return (ofs + j); +} +#else +static uint16_t NVOCMP_findOffset(uint8_t pg, uint16_t ofs) +{ + uint8_t i, j; + uint32_t tmp = 0; + + // Find first non-erased 4-byte location + while (ofs >= sizeof(tmp)) + { + ofs -= sizeof(tmp); + NVOCMP_read(pg, ofs, (uint8_t *) &tmp, sizeof(tmp)); + if (tmp != NVOCMP_ERASEDWORD) + { + break; + } + } + + // Starting with LSB, look for non-erased byte + for (i = j = 1; i <= 4; i++) + { + if ((tmp & NVOCMP_ERASEDBYTE) != NVOCMP_ERASEDBYTE) + { + // Last non-erased byte so far + j = i; + } + tmp >>= 8; + } + + return (ofs + j); +} +#endif + +/****************************************************************************** + * @fn NVOCMP_findItem + * + * @brief Find a valid item from designated page and offset + * + * @param pNvHandle - pointer to NV handle + * @param pg - Valid NV page + * @param ofs - Offset in NV page from where to start search + * @param pHdr - pointer to item header + * @param flag - specifies type of search + * @param pInfo - pointer to item info + * + * @return When >0, offset to the item header for found item + * When <=0, -number of items searched when item not found + * + */ +#if NVOCMP_FASTITEM +static int8_t NVOCMP_findItem(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * pHdr, int8_t flag, + NVOCMP_itemInfo_t * pInfo) +{ + bool found = false; + uint8_t p; + uint16_t items = 0; + uint16_t nvSearched = 0; +#ifdef NVOCMP_GPRAM + uint32_t vm; + uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; +#else + uint8_t * pTBuffer = (uint8_t *) tBuffer; +#endif + uint32_t cid = NVOCMP_CMPRID(pHdr->sysid, pHdr->itemid, pHdr->subid); + +#ifdef NVOCMP_GPRAM + NVOCMP_disableCache(&vm); +#endif + + for (p = pg; nvSearched < NVOCMP_NVSIZE; p = NVOCMP_DECPAGE(p), ofs = pNvHandle->pageInfo[p].offset) + { + nvSearched++; +#if (NVOCMP_NVPAGES != NVOCMP_NVONEP) + if (p == pNvHandle->tailPage) + { + continue; + } +#endif + NVOCMP_read(pg, 0, (uint8_t *) pTBuffer, FLASH_PAGE_SIZE); + + while (ofs >= (NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN)) + { + NVOCMP_itemHdr_t iHdr; + + // Align to start of item header + ofs -= NVOCMP_ITEMHDRLEN; + + // Read and decompress item header + NVOCMP_readHeader(p, ofs, &iHdr, true); + + if ((iHdr.stats & NVOCMP_ACTIVEIDBIT) && !(iHdr.stats & NVOCMP_VALIDIDBIT)) + { + uint32_t sysid = pHdr->sysid; + uint32_t itemid = pHdr->itemid; + + switch (flag & NVOCMP_FINDLMASK) + { + case NVOCMP_FINDANY: + found = true; + break; + case NVOCMP_FINDSTRICT: + // Return first cid match + if (cid == iHdr.cmpid) + { + found = true; + } + break; + case NVOCMP_FINDSYSID: + // return first sysid match + if (sysid == iHdr.sysid) + { + found = true; + } + break; + case NVOCMP_FINDITMID: + // return first sysid AND itemid match + if (sysid == iHdr.sysid && itemid == iHdr.itemid) + { + found = true; + } + break; + default: + // Should not get here + NVOCMP_EXCEPTION(p, NVINTF_BADPARAM); + NVOCMP_ASSERT(false, "Unhandled case in findItem().") +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + return (NVINTF_BADPARAM); + } + // Item found - return offset of item header + if (found) + { + if ((pInfo) && ((flag & NVOCMP_FINDHMASK) == NVOCMP_FINDCONTENT)) + { + if (!NVOCMP_readItem(&iHdr, 0, pInfo->rlength, pInfo->rBuf, false)) + { + if (!memcmp((uint8_t *) pInfo->rBuf + pInfo->coff, pInfo->cBuf, pInfo->clength)) + { + memcpy(pHdr, &iHdr, sizeof(NVOCMP_itemHdr_t)); +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + return (NVINTF_SUCCESS); + } + } + found = false; + } + else + { + memcpy(pHdr, &iHdr, sizeof(NVOCMP_itemHdr_t)); +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + return (NVINTF_SUCCESS); + } + } + } + // Try to jump to next item + if (iHdr.stats & NVOCMP_FOLLOWBIT) + { + // Appears to be an item there, check bounds + if (iHdr.len < ofs) + { + // Adjust offset for next try + ofs -= iHdr.len; + } + else + { + // Length is corrupt, mark item invalid and compact + NVOCMP_ALERT(false, "Item length corrupted. Deleting item.") + NVOCMP_setItemInactive(pNvHandle, p, ofs); +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + NVOCMP_compactPage(pNvHandle, 0); + p = NVOCMP_INCPAGE(pNvHandle->actPage); + ofs = 0; + nvSearched = 0; + } + } + else + { + // Something is corrupted, compact to fix + NVOCMP_ALERT(false, + "No item following current item, " + "compaction needed.") +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + NVOCMP_compactPage(pNvHandle, 0); + p = NVOCMP_INCPAGE(pNvHandle->actPage); + ofs = 0; + nvSearched = 0; + } + // Running count of items searched + items += 1; + } + } +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + // Item not found (negate number of items searched) + // or nth not found, return last found + pHdr->hofs = 0; + return (NVINTF_NOTFOUND); +} +#else +static int8_t NVOCMP_findItem(NVOCMP_nvHandle_t * pNvHandle, uint8_t pg, uint16_t ofs, NVOCMP_itemHdr_t * pHdr, int8_t flag, + NVOCMP_itemInfo_t * pInfo) +{ + bool found = false; + uint8_t p = pg; + uint16_t items = 0; + uint32_t cid = NVOCMP_CMPRID(pHdr->sysid, pHdr->itemid, pHdr->subid); + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + uint16_t nvSearched = 0; + for (p = pg; nvSearched < NVOCMP_NVSIZE; p = NVOCMP_DECPAGE(p), ofs = pNvHandle->pageInfo[p].offset) + { + nvSearched++; + if (p == pNvHandle->tailPage) + { + continue; + } +#endif + while (ofs >= (NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN)) + { + NVOCMP_itemHdr_t iHdr; + + // Align to start of item header + ofs -= NVOCMP_ITEMHDRLEN; + + // Read and decompress item header + NVOCMP_readHeader(p, ofs, &iHdr, false); + + if ((iHdr.stats & NVOCMP_ACTIVEIDBIT) && !(iHdr.stats & NVOCMP_VALIDIDBIT)) + { + uint32_t sysid = pHdr->sysid; + uint32_t itemid = pHdr->itemid; + + switch (flag & NVOCMP_FINDLMASK) + { + case NVOCMP_FINDANY: + found = true; + break; + case NVOCMP_FINDSTRICT: + // Return first cid match + if (cid == iHdr.cmpid) + { + found = true; + } + break; + case NVOCMP_FINDSYSID: + // return first sysid match + if (sysid == iHdr.sysid) + { + found = true; + } + break; + case NVOCMP_FINDITMID: + // return first sysid AND itemid match + if (sysid == iHdr.sysid && itemid == iHdr.itemid) + { + found = true; + } + break; + default: + // Should not get here + NVOCMP_EXCEPTION(p, NVINTF_BADPARAM); + NVOCMP_ASSERT(false, "Unhandled case in findItem().") + return (NVINTF_BADPARAM); + } + // Item found - return offset of item header + if (found) + { + if ((pInfo) && ((flag & NVOCMP_FINDHMASK) == NVOCMP_FINDCONTENT)) + { + if (!NVOCMP_readItem(&iHdr, 0, pInfo->rlength, pInfo->rBuf, false)) + { + if (!memcmp((uint8_t *) pInfo->rBuf + pInfo->coff, pInfo->cBuf, pInfo->clength)) + { + memcpy(pHdr, &iHdr, sizeof(NVOCMP_itemHdr_t)); + return (NVINTF_SUCCESS); + } + } + found = false; + } + else + { + memcpy(pHdr, &iHdr, sizeof(NVOCMP_itemHdr_t)); + return (NVINTF_SUCCESS); + } + } + } + // Try to jump to next item + if (iHdr.stats & NVOCMP_FOLLOWBIT) + { + // Appears to be an item there, check bounds + if (iHdr.len < ofs) + { + // Adjust offset for next try + ofs -= iHdr.len; + } + else + { + // Length is corrupt, mark item invalid and compact + NVOCMP_ALERT(false, "Item length corrupted. Deleting item.") + NVOCMP_setItemInactive(pNvHandle, p, ofs); + NVOCMP_compactPage(pNvHandle, 0); +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + p = NVOCMP_INCPAGE(pNvHandle->actPage); + ofs = 0; + nvSearched = 0; +#else + p = pNvHandle->actPage; + ofs = pNvHandle->actOffset; +#endif + } + } + else + { + // Something is corrupted, compact to fix + NVOCMP_ALERT(false, + "No item following current item, " + "compaction needed.") + pNvHandle->forceCompact = 1; + NVOCMP_compactPage(pNvHandle, 0); +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + p = NVOCMP_INCPAGE(pNvHandle->actPage); + ofs = 0; + nvSearched = 0; +#else + p = pNvHandle->actPage; + ofs = pNvHandle->actOffset; +#endif + } + // Running count of items searched + items += 1; + } +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) + } +#endif + // Item not found (negate number of items searched) + // or nth not found, return last found + pHdr->hofs = 0; + return (NVINTF_NOTFOUND); +} +#endif + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_cleanPage + * + * @brief Clean the page that compaction is done with + * + * @param pNvHandle - pointer to NV handle + * + * @return Number of pages cleaned + */ +static uint8_t NVOCMP_cleanPage(NVOCMP_nvHandle_t * pNvHandle) +{ + uint8_t pg; + uint8_t pages = 0; + NVOCMP_pageHdr_t pageHdr; + NVOCMP_compactHdr_t startHdr; + NVOCMP_compactHdr_t endHdr; + + /* correct ofset */ + pg = pNvHandle->compactInfo.xDstPage; + NVOCMP_getCompactHdr(pg, XSRCSTARTHDR, &startHdr); + NVOCMP_getCompactHdr(pg, XSRCENDHDR, &endHdr); + for (pg = pNvHandle->compactInfo.xSrcSPage; pg != NVOCMP_INCPAGE(pNvHandle->compactInfo.xSrcEPage); pg = NVOCMP_INCPAGE(pg)) + { + if (pg == endHdr.page) + { + pNvHandle->pageInfo[pg].offset = endHdr.pageOffset; + } + else + { + pNvHandle->pageInfo[pg].offset = NVOCMP_PGDATAOFS; + } + } + /* end of correction */ + + for (pg = pNvHandle->compactInfo.xSrcSPage; pg != NVOCMP_INCPAGE(pNvHandle->compactInfo.xSrcEPage); pg = NVOCMP_INCPAGE(pg)) + { + if (pNvHandle->pageInfo[pg].offset == NVOCMP_PGDATAOFS) + { + pages++; + NVOCMP_failW = NVOCMP_erase(pNvHandle, pg); + } + else + { + NVOCMP_failW = NVOCMP_write(pg, (THISPAGEHDR + 1) * NVOCMP_COMPACTHDRLEN, (uint8_t *) &pNvHandle->pageInfo[pg].offset, + sizeof(pNvHandle->pageInfo[pg].offset)); + NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + if (pageHdr.state == NVOCMP_PGACT) + { + NVOCMP_changePageState(pNvHandle, pg, NVOCMP_PGFULL); + } + else + { + // this should not hit + if (pageHdr.state == NVOCMP_PGXDST) + { + NVOCMP_ASSERT1(0); + } + } + } + } + return (pages); +} +#endif + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_compactPage + * + * @brief Compact specified page by copying active items to other page + * + * Compaction occurs under three circumstances: (1) 'maintenance' + * activity which is triggered by a user call to compactNvApi(), + * (2) 'update' activity where an NV page is packed to make room + * for an item being written. The 'update' mode is performed by + * writing the item after the rest of the page has been compacted, + * and (3) when corruption is detected in the NV page. The compaction + * operation will move all active&valid items to the other page. + * + * @param pNvHandle - pointer to NV handle + * @param nBytes - size of item to write if any + * + * @return Number of available bytes on compacted page, -1 if error + */ +static int16_t NVOCMP_compactPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t nBytes) +{ + uint8_t pg; + uint8_t mode; + uint8_t srcPg; + uint8_t dstPg; + uint16_t needBytes; + uint16_t compactPages; + uint16_t cleanPages; + uint16_t skipPages = 0; + bool foundRoom = false; + NVOCMP_compactStatus_t status; + NVOCMP_pageHdr_t pageHdr; + uint8_t allActivePages = 0; + uint8_t err = NVINTF_SUCCESS; + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + if (err) + { + return (0); + } + + srcPg = pNvHandle->headPage; + dstPg = pNvHandle->tailPage; + compactPages = NVOCMP_NVSIZE - 1; + if (nBytes) + { + pNvHandle->compactInfo.xSrcPages = 1; + } + else + { + pNvHandle->compactInfo.xSrcPages = NVOCMP_NVSIZE - 1; + } + + // mark page mode + for (pg = 0; pg < NVOCMP_NVSIZE; pg++) + { + if (pg != dstPg) + { + NVOCMP_read(pg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + if (pageHdr.allActive) + { + allActivePages++; + } + if (pageHdr.state == NVOCMP_PGACT || pageHdr.state == NVOCMP_PGFULL) + { + mode = NVOCMP_PGCSRC; + NVOCMP_writeByte(pg, NVOCMP_COMPMODEOFS, mode); + pNvHandle->pageInfo[pg].mode = mode; + } + } + } + + if ((allActivePages == NVOCMP_NVSIZE - 1) && !pNvHandle->forceCompact) + { + return (0); + } + + while (compactPages) + { + if (pNvHandle->compactInfo.xSrcPages == 0) + { + pNvHandle->compactInfo.xSrcPages = 1; + } + // Get page header + NVOCMP_read(srcPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); +#if NVOCMP_FASTCP + if (nBytes && pageHdr.allActive) + { + srcPg = NVOCMP_INCPAGE(srcPg); + pNvHandle->compactInfo.xDstOffset = FLASH_PAGE_SIZE; + pNvHandle->compactInfo.xSrcSPage = srcPg; + pNvHandle->compactInfo.xSrcPages--; + compactPages--; + skipPages++; + continue; + } +#endif + // Mark source page to be in PGXSRC state + if (pageHdr.state != NVOCMP_PGXSRC) + { + NVOCMP_changePageState(pNvHandle, srcPg, NVOCMP_PGXSRC); + } + // Get page header + NVOCMP_read(dstPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + // Mark destination page to be in PGXDST state + if (pageHdr.state != NVOCMP_PGXDST) + { + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGXDST); + } + + mode = NVOCMP_PGCDST; + NVOCMP_writeByte(dstPg, NVOCMP_COMPMODEOFS, mode); + pNvHandle->pageInfo[dstPg].mode = mode; + + pNvHandle->compactInfo.xDstPage = dstPg; + pNvHandle->compactInfo.xDstOffset = FLASH_PAGE_SIZE; + pNvHandle->compactInfo.xSrcSPage = srcPg; + pNvHandle->compactInfo.xSrcSOffset = pNvHandle->pageInfo[srcPg].offset; + pNvHandle->compactInfo.xSrcEPage = NVOCMP_NULLPAGE; + pNvHandle->compactInfo.xSrcEOffset = 0; + status = NVOCMP_compact(pNvHandle); + + if (status == NVOCMP_COMPACT_FAILURE) + { +#ifdef NVOCMP_RECOVER_FROM_COMPACT_FAILURE + uint8_t p; + for (p = 0; p < NVOCMP_NVSIZE; p++) + { + NVOCMP_failW |= NVOCMP_erase(pNvHandle, p); + if ((p != 0) && (p != NVOCMP_NVSIZE - 1)) + { + NVOCMP_changePageState(pNvHandle, p, NVOCMP_PGRDY); + } + } + pNvHandle->actPage = 0; + pNvHandle->actOffset = pNvHandle->pageInfo[0].offset; + pNvHandle->headPage = 0; + pNvHandle->tailPage = NVOCMP_NVSIZE - 1; + NVOCMP_changePageState(pNvHandle, NVOCMP_NVSIZE - 1, NVOCMP_PGXDST); + NVOCMP_changePageState(pNvHandle, 0, NVOCMP_PGACT); + + pNvHandle->forceCompact = 0; +#endif + return (0); + } + + needBytes = nBytes ? nBytes : 16; + + if (nBytes) + { + if (FLASH_PAGE_SIZE - pNvHandle->compactInfo.xDstOffset >= needBytes) + { + foundRoom = true; + } + } + // change XDST page state + if (FLASH_PAGE_SIZE - pNvHandle->compactInfo.xDstOffset >= needBytes) + { + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGACT); + } + else + { + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGFULL); + } + // clean XSRC pages + cleanPages = NVOCMP_cleanPage(pNvHandle); + compactPages -= cleanPages; + pNvHandle->compactInfo.xSrcPages -= cleanPages; + + // mark XDST page as done + mode = NVOCMP_PGCDONE; + NVOCMP_writeByte(dstPg, NVOCMP_COMPMODEOFS, mode); + pNvHandle->pageInfo[dstPg].mode = mode; + + // move tail page and head page + pNvHandle->tailPage = NVOCMP_ADDPAGE(pNvHandle->tailPage, cleanPages + skipPages); + pNvHandle->headPage = NVOCMP_INCPAGE(pNvHandle->tailPage); + skipPages = 0; + // set next source page + if (pNvHandle->pageInfo[pNvHandle->compactInfo.xSrcEPage].offset == NVOCMP_PGDATAOFS) + { + srcPg = NVOCMP_INCPAGE(pNvHandle->compactInfo.xSrcEPage); + } + else + { + srcPg = pNvHandle->compactInfo.xSrcEPage; + } + + // set next destination page + dstPg = pNvHandle->tailPage; + if (nBytes && foundRoom) + { + break; + } + } + + pg = NVOCMP_findPage(NVOCMP_PGACT); + if (pg == NVOCMP_NULLPAGE) + { + pg = NVOCMP_findPage(NVOCMP_PGRDY); + if (pg == NVOCMP_NULLPAGE) + { + if (pNvHandle->pageInfo[pNvHandle->headPage].state == NVOCMP_PGNACT) + { + pg = pNvHandle->headPage; + NVOCMP_changePageState(pNvHandle, pg, NVOCMP_PGRDY); + } + else + { + pg = NVOCMP_DECPAGE(pNvHandle->tailPage); + } + } + } + + pNvHandle->actPage = pg; + pNvHandle->actOffset = pNvHandle->pageInfo[pNvHandle->actPage].offset; + NVOCMP_changePageState(pNvHandle, pNvHandle->tailPage, NVOCMP_PGXDST); + + pNvHandle->forceCompact = 0; + return (FLASH_PAGE_SIZE - pNvHandle->compactInfo.xDstOffset); +} +#else +/****************************************************************************** + * @fn NVOCMP_compactPage + * + * @brief Compact specified page by copying active items to other page + * + * Compaction occurs under three circumstances: (1) 'maintenance' + * activity which is triggered by a user call to compactNvApi(), + * (2) 'update' activity where an NV page is packed to make room + * for an item being written. The 'update' mode is performed by + * writing the item after the rest of the page has been compacted, + * and (3) when corruption is detected in the NV page. The compaction + * operation will move all active&valid items to the other page. + * + * @param pNvHandle - pointer to NV handle + * @param nBytes - size of item to write if any + * + * @return Number of available bytes on compacted page, -1 if error + */ +static int16_t NVOCMP_compactPage(NVOCMP_nvHandle_t * pNvHandle, uint16_t nBytes) +{ + uint8_t srcPg; + uint8_t dstPg; + uint16_t needBytes; + NVOCMP_compactStatus_t status; + NVOCMP_pageHdr_t pageHdr; + uint8_t err = NVINTF_SUCCESS; + + // Check voltage if possible + NVOCMP_FLASHACCESS(err) + if (err) + { + return (0); + } + +#if (NVOCMP_NVPAGES == NVOCMP_NVONEP) + srcPg = 0; + dstPg = 0; +#else + srcPg = pNvHandle->headPage; + dstPg = pNvHandle->tailPage; +#endif + pNvHandle->compactInfo.xSrcPages = 1; + + // mark page mode + NVOCMP_read(srcPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + if ((NVOCMP_ALLACTIVE == pageHdr.allActive) && !pNvHandle->forceCompact) + { + return (0); + } + + NVOCMP_writeByte(srcPg, NVOCMP_COMPMODEOFS, NVOCMP_PGCSRC); + pNvHandle->pageInfo[srcPg].mode = NVOCMP_PGCSRC; + + // Mark source page to be in PGXSRC state + if (pageHdr.state != NVOCMP_PGXSRC) + { + NVOCMP_changePageState(pNvHandle, srcPg, NVOCMP_PGXSRC); + } +#if (NVOCMP_NVPAGES > NVOCMP_NVONEP) + // Get page header + NVOCMP_read(dstPg, NVOCMP_PGHDROFS, (uint8_t *) &pageHdr, NVOCMP_PGHDRLEN); + // Mark destination page to be in PGXDST state + if (pageHdr.state != NVOCMP_PGXDST) + { + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGXDST); + } + + NVOCMP_writeByte(dstPg, NVOCMP_COMPMODEOFS, NVOCMP_PGCDST); + pNvHandle->pageInfo[dstPg].mode = NVOCMP_PGCDST; +#endif + + pNvHandle->compactInfo.xSrcSOffset = pNvHandle->pageInfo[srcPg].offset; + status = NVOCMP_compact(pNvHandle); + + if (status == NVOCMP_COMPACT_FAILURE) + { +#ifdef NVOCMP_RECOVER_FROM_COMPACT_FAILURE + uint8_t p; + for (p = 0; p < NVOCMP_NVSIZE; p++) + { + NVOCMP_failW |= NVOCMP_erase(pNvHandle, p); + if ((p != 0) && (p != NVOCMP_NVSIZE - 1)) + { + NVOCMP_changePageState(pNvHandle, p, NVOCMP_PGRDY); + } + } + pNvHandle->actPage = 0; + pNvHandle->actOffset = pNvHandle->pageInfo[0].offset; + pNvHandle->headPage = 0; + pNvHandle->tailPage = NVOCMP_NVSIZE - 1; + NVOCMP_changePageState(pNvHandle, NVOCMP_NVSIZE - 1, NVOCMP_PGXDST); + NVOCMP_changePageState(pNvHandle, 0, NVOCMP_PGACT); + + pNvHandle->forceCompact = 0; +#endif + return (0); + } + + needBytes = nBytes ? nBytes : 16; + + // change XDST page state + if (FLASH_PAGE_SIZE - pNvHandle->compactInfo.xDstOffset >= needBytes) + { + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGACT); + } + else + { + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGFULL); + } + +#if (NVOCMP_NVPAGES > NVOCMP_NVONEP) + // clean XSRC pages + NVOCMP_failW = NVOCMP_erase(pNvHandle, srcPg); +#endif + + // mark XDST page as done + NVOCMP_writeByte(dstPg, NVOCMP_COMPMODEOFS, NVOCMP_PGCDONE); + pNvHandle->pageInfo[dstPg].mode = NVOCMP_PGCDONE; + + // move tail page and head page + pNvHandle->tailPage = srcPg; + pNvHandle->headPage = dstPg; + pNvHandle->actPage = dstPg; + pNvHandle->actOffset = pNvHandle->pageInfo[dstPg].offset; +#if (NVOCMP_NVPAGES > NVOCMP_NVONEP) + NVOCMP_changePageState(pNvHandle, srcPg, NVOCMP_PGXDST); +#endif + + pNvHandle->forceCompact = 0; + return (FLASH_PAGE_SIZE - pNvHandle->actOffset); +} +#endif + +/****************************************************************************** + * @fn NVOCMP_findSignature + * + * @brief Local function to scan page to get page information + * + * @param pg - page to search + * @param pSrcOff - source off to start search from + * + * @return NVINTF_SUCCESS or specific failure code + */ +static bool NVOCMP_findSignature(uint8_t pg, uint16_t * pSrcOff) +{ + uint16_t i; + uint16_t rdLen; + uint8_t readBuffer[NVOCMP_XFERBLKMAX]; + uint16_t srcOff = *pSrcOff; + uint16_t endOff = NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN - 1; + + while (srcOff > endOff) + { + rdLen = (srcOff - NVOCMP_XFERBLKMAX > endOff) ? NVOCMP_XFERBLKMAX : srcOff - endOff; + srcOff -= rdLen; + NVOCMP_read(pg, srcOff, readBuffer, rdLen); + for (i = rdLen; i > 0; i--) + { + if (readBuffer[i - 1] == NVOCMP_SIGNATURE) + { + // Found possible header, resume normal operation + NVOCMP_ALERT(false, "Found possible signature.") + srcOff += i; // srcOff should point to one byte ahead + *pSrcOff = srcOff; + return (true); + } + } + } + return (false); +} + +#if (NVOCMP_NVPAGES > NVOCMP_NVTWOP) +/****************************************************************************** + * @fn NVOCMP_compact + * + * @brief Local function to compact NV + * + * @param pNvHandle - pointer to NV handler + * + * @return NVINTF_SUCCESS or specific failure code + */ +static NVOCMP_compactStatus_t NVOCMP_compact(NVOCMP_nvHandle_t * pNvHandle) +{ + bool needScan = false; + bool needSkip = false; + bool dstFull = false; + uint16_t dstOff; + uint16_t endOff; + uint16_t srcOff; + uint16_t crcOff; + uint8_t srcStartPg; + uint8_t srcEndPg; + uint8_t dstPg; + uint8_t srcPg; + uint32_t aItem = 0; +#ifndef NVOCMP_RAM_OPTIMIZATION +#ifdef NVOCMP_GPRAM + uint32_t vm; + uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; +#else + uint8_t * pTBuffer = (uint8_t *) tBuffer; +#endif +#endif + + NVOCMP_compactStatus_t status = NVOCMP_COMPACT_SUCCESS; + +#ifndef NVOCMP_GPRAM + memset(tBuffer, 0, sizeof(tBuffer)); +#endif + // Reset Flash erase/write fail indicator + NVOCMP_failW = NVINTF_SUCCESS; + srcStartPg = pNvHandle->compactInfo.xSrcSPage; + srcEndPg = NVOCMP_ADDPAGE(srcStartPg, pNvHandle->compactInfo.xSrcPages - 1); + + // Stop looking when we get to this offset + endOff = NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN - 1; + + srcPg = srcStartPg; + srcOff = pNvHandle->pageInfo[srcPg].offset; + dstPg = pNvHandle->compactInfo.xDstPage; + dstOff = pNvHandle->pageInfo[dstPg].offset; + + NVOCMP_ALERT(false, "Compaction triggered.") + +#ifdef NVOCMP_GPRAM + NVOCMP_disableCache(&vm); +#endif + while (srcPg != dstPg) + { + if (dstFull) + { + if (aItem) + { + pNvHandle->pageInfo[srcPg].offset = srcOff; + } + break; + } + + if (srcOff <= endOff) + { + if (aItem) + { + pNvHandle->pageInfo[srcPg].offset = srcOff; + } + if (srcPg == srcEndPg) + { + break; + } + else + { + srcPg = NVOCMP_INCPAGE(srcPg); + srcOff = pNvHandle->pageInfo[srcPg].offset; + aItem = 0; + continue; + } + } + + if (NVOCMP_failW == NVINTF_SUCCESS) + { + NVOCMP_itemHdr_t srcHdr; + uint16_t dataLen; + uint16_t itemSize; + + needScan = false; + needSkip = false; + + // Read and decompress item header + NVOCMP_readHeader(srcPg, srcOff - NVOCMP_ITEMHDRLEN, &srcHdr, false); + dataLen = srcHdr.len; + itemSize = NVOCMP_ITEMHDRLEN + dataLen; + crcOff = srcOff - NVOCMP_ITEMHDRLEN - dataLen; + + // Check if length is safe + if (srcOff < (dataLen + NVOCMP_PGDATAOFS)) + { + NVOCMP_ALERT(false, "Item header corrupted: Data length too long") + needScan = true; + srcOff--; + } + else if (NVOCMP_SIGNATURE != srcHdr.sig) + { + NVOCMP_ALERT(false, "Item header corrupted: Invalid signature") + needScan = true; + srcOff--; + } + else if (NVOCMP_verifyCRC(crcOff, dataLen, srcHdr.crc8, srcPg, false)) + { + // Invalid CRC, corruption + NVOCMP_ALERT(false, "Item CRC incorrect!") + needScan = true; + srcOff--; + } + else if (!(srcHdr.stats & NVOCMP_VALIDIDBIT) && // Item is valid + (srcHdr.stats & NVOCMP_ACTIVEIDBIT)) // Item is active + { + // Valid CRC, item is active + srcOff -= NVOCMP_ITEMHDRLEN; + } + else + { + // Valid CRC but item is inactive + srcOff -= NVOCMP_ITEMHDRLEN; + needSkip = true; + } + + if (needScan) + { + // Detected a problem, find next header (scan for signature) + NVOCMP_ALERT(false, "Attempting to find signature...") + bool foundSig = NVOCMP_findSignature(srcPg, &srcOff); + if (!foundSig) + { +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + // If we get here and foundSig is false, we never found another + // item in the page, break the loop so that any valid items + // that were collected up to this point get written to the + // destination page. + NVOCMP_ALERT(foundSig, "Attempt to find signature failed.") + break; + } + } + else + { + if (!needSkip) + { + if (dstOff + itemSize > FLASH_PAGE_SIZE) + { + // cannot fit one page temp buffer and revert srcOff change + srcOff += NVOCMP_ITEMHDRLEN; + dstFull = true; + continue; + } + else + { + // Get block of bytes from source page +#ifndef NVOCMP_RAM_OPTIMIZATION +#if NVOCMP_COMPR + NVOCMP_read(srcPg, crcOff, (uint8_t *) (pTBuffer + dstOff), itemSize); +#else + NVOCMP_read(srcPg, crcOff, (uint8_t *) (pTBuffer + FLASH_PAGE_SIZE - dstOff - itemSize), itemSize); +#endif +#else + NVOCMP_copyItem(srcPg, dstPg, crcOff, dstOff, itemSize); +#endif + dstOff += itemSize; + aItem++; + if (dstOff == FLASH_PAGE_SIZE) + { + dstFull = true; + } + } + } + NVOCMP_ALERT(srcOff > dataLen, "Offset overflow: srcOff") + srcOff -= dataLen; + } + } + else + { +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + // Failure during item xfer makes next findItem() unreliable + NVOCMP_ASSERT(false, "COMPACTION FAILURE") + return (NVOCMP_COMPACT_FAILURE); + } + } // end of while + + if (NVOCMP_failW != NVINTF_SUCCESS) + { +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + // Something bad happened when trying to compact the page + NVOCMP_ASSERT(false, "COMPACTION FAILURE") + return (NVOCMP_COMPACT_FAILURE); + } + + // Write block to destination page +#ifndef NVOCMP_RAM_OPTIMIZATION +#if NVOCMP_COMPR + uint16_t off = NVOCMP_PGDATAOFS; + uint16_t len = dstOff - NVOCMP_PGDATAOFS; + + NVOCMP_failW |= NVOCMP_write(dstPg, off, (uint8_t *) (pTBuffer + off), len); +#else + uint16_t off = NVOCMP_PGDATAOFS; + uint16_t doff = FLASH_PAGE_SIZE - dstOff; + uint16_t len = dstOff - NVOCMP_PGDATAOFS; + + NVOCMP_failW |= NVOCMP_write(dstPg, off, (uint8_t *) (pTBuffer + doff), len); +#endif +#endif + +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + + if (srcPg == dstPg) + { + srcPg = NVOCMP_DECPAGE(srcPg); + srcOff = pNvHandle->pageInfo[srcPg].offset; + } + pNvHandle->compactInfo.xDstOffset = dstOff; + pNvHandle->compactInfo.xSrcEOffset = srcOff; + + pNvHandle->compactInfo.xSrcEPage = srcPg; + pNvHandle->pageInfo[dstPg].offset = dstOff; + + NVOCMP_setCompactHdr(dstPg, pNvHandle->compactInfo.xSrcSPage, pNvHandle->compactInfo.xSrcSOffset, XSRCSTARTHDR); + NVOCMP_setCompactHdr(dstPg, pNvHandle->compactInfo.xSrcEPage, pNvHandle->compactInfo.xSrcEOffset, XSRCENDHDR); + + if (srcOff <= endOff) + { + status |= NVOCMP_COMPACT_SRCDONE; + } + if (dstOff >= FLASH_PAGE_SIZE) + { + status |= NVOCMP_COMPACT_DSTDONE; + } + + return (status); +} +#else +/****************************************************************************** + * @fn NVOCMP_compact + * + * @brief Local function to compact NV + * + * @param pNvHandle - pointer to NV handler + * + * @return NVINTF_SUCCESS or specific failure code + */ +static NVOCMP_compactStatus_t NVOCMP_compact(NVOCMP_nvHandle_t * pNvHandle) +{ + bool needScan = false; + bool needSkip = false; + uint16_t dstOff; + uint16_t endOff; + uint16_t srcOff; + uint16_t crcOff; + uint8_t dstPg; + uint8_t srcPg; + uint32_t aItem = 0; +#ifndef NVOCMP_RAM_OPTIMIZATION +#ifdef NVOCMP_GPRAM + uint32_t vm; + uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; +#else + uint8_t * pTBuffer = (uint8_t *) tBuffer; +#endif +#endif + +#ifndef NVOCMP_GPRAM + memset(tBuffer, 0, sizeof(tBuffer)); +#endif + // Reset Flash erase/write fail indicator + NVOCMP_failW = NVINTF_SUCCESS; + + // Stop looking when we get to this offset + endOff = NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN - 1; + +#if (NVOCMP_NVPAGES == NVOCMP_NVONEP) + srcPg = 0; + srcOff = pNvHandle->pageInfo[0].offset; + dstPg = 0; + dstOff = NVOCMP_PGDATAOFS; +#else + srcPg = pNvHandle->headPage; + srcOff = pNvHandle->pageInfo[srcPg].offset; + dstPg = pNvHandle->tailPage; + dstOff = pNvHandle->pageInfo[dstPg].offset; +#endif + + NVOCMP_ALERT(false, "Compaction triggered.") + +#ifdef NVOCMP_GPRAM + NVOCMP_disableCache(&vm); +#endif + while (srcOff > endOff) + { + if (NVOCMP_failW == NVINTF_SUCCESS) + { + NVOCMP_itemHdr_t srcHdr; + uint16_t dataLen; + uint16_t itemSize; + + needScan = false; + needSkip = false; + + // Read and decompress item header + NVOCMP_readHeader(srcPg, srcOff - NVOCMP_ITEMHDRLEN, &srcHdr, false); + dataLen = srcHdr.len; + itemSize = NVOCMP_ITEMHDRLEN + dataLen; + crcOff = srcOff - NVOCMP_ITEMHDRLEN - dataLen; + + // Check if length is safe + if (srcOff < (dataLen + NVOCMP_PGDATAOFS)) + { + NVOCMP_ALERT(false, "Item header corrupted: Data length too long") + needScan = true; + srcOff--; + } + else if (NVOCMP_SIGNATURE != srcHdr.sig) + { + NVOCMP_ALERT(false, "Item header corrupted: Invalid signature") + needScan = true; + srcOff--; + } + else if (NVOCMP_verifyCRC(crcOff, dataLen, srcHdr.crc8, srcPg, false)) + { + // Invalid CRC, corruption + NVOCMP_ALERT(false, "Item CRC incorrect!") + needScan = true; + srcOff--; + } + else if (!(srcHdr.stats & NVOCMP_VALIDIDBIT) && // Item is valid + (srcHdr.stats & NVOCMP_ACTIVEIDBIT)) // Item is active + { + // Valid CRC, item is active + srcOff -= NVOCMP_ITEMHDRLEN; + } + else + { + // Valid CRC but item is inactive + srcOff -= NVOCMP_ITEMHDRLEN; + needSkip = true; + } + + if (needScan) + { + // Detected a problem, find next header (scan for signature) + NVOCMP_ALERT(false, "Attempting to find signature...") + bool foundSig = NVOCMP_findSignature(srcPg, &srcOff); + if (!foundSig) + { + // If we get here and foundSig is false, we never found another + // item in the page, break the loop so that any valid items + // that were collected up to this point get written to the + // destination page. + NVOCMP_ALERT(foundSig, "Attempt to find signature failed.") + break; + } + } + else + { + if (!needSkip) + { + // Get block of bytes from source page +#ifndef NVOCMP_RAM_OPTIMIZATION +#if NVOCMP_COMPR + NVOCMP_read(srcPg, crcOff, (uint8_t *) (pTBuffer + dstOff), itemSize); +#else + NVOCMP_read(srcPg, crcOff, (uint8_t *) (pTBuffer + FLASH_PAGE_SIZE - dstOff - itemSize), itemSize); +#endif +#else + NVOCMP_copyItem(srcPg, dstPg, crcOff, dstOff, itemSize); +#endif + dstOff += itemSize; + aItem++; + } + NVOCMP_ALERT(srcOff > dataLen, "Offset overflow: srcOff") + srcOff -= dataLen; + } + } + else + { +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + // Failure during item xfer makes next findItem() unreliable + NVOCMP_ASSERT(false, "COMPACTION FAILURE") + return (NVOCMP_COMPACT_FAILURE); + } + } // end of while + + if (NVOCMP_failW != NVINTF_SUCCESS) + { +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + // Something bad happened when trying to compact the page + NVOCMP_ASSERT(false, "COMPACTION FAILURE") + return (NVOCMP_COMPACT_FAILURE); + } + +#if (NVOCMP_NVPAGES == NVOCMP_NVONEP) + // Get XDST page ready + NVOCMP_failW = NVOCMP_erase(pNvHandle, dstPg); + NVOCMP_changePageState(pNvHandle, dstPg, NVOCMP_PGXDST); +#endif + + // Write block to destination page +#ifndef NVOCMP_RAM_OPTIMIZATION +#if NVOCMP_COMPR + uint16_t off = NVOCMP_PGDATAOFS; + uint16_t len = dstOff - NVOCMP_PGDATAOFS; + NVOCMP_failW |= NVOCMP_write(dstPg, off, (uint8_t *) (pTBuffer + off), len); +#else + uint16_t off = NVOCMP_PGDATAOFS; + uint16_t doff = FLASH_PAGE_SIZE - dstOff; + uint16_t len = dstOff - NVOCMP_PGDATAOFS; + NVOCMP_failW |= NVOCMP_write(dstPg, off, (uint8_t *) (pTBuffer + doff), len); +#endif +#endif + +#ifdef NVOCMP_GPRAM + NVOCMP_restoreCache(vm); +#endif + + pNvHandle->pageInfo[dstPg].offset = dstOff; + pNvHandle->compactInfo.xSrcEOffset = srcOff; + pNvHandle->compactInfo.xDstOffset = dstOff; + + NVOCMP_setCompactHdr(dstPg, pNvHandle->compactInfo.xSrcSPage, pNvHandle->compactInfo.xSrcSOffset, XSRCSTARTHDR); + NVOCMP_setCompactHdr(dstPg, pNvHandle->compactInfo.xSrcEPage, pNvHandle->compactInfo.xSrcEOffset, XSRCENDHDR); + + return (NVOCMP_COMPACT_SUCCESS); +} +#endif + +#if ((NVOCMP_NVPAGES > NVOCMP_NVONEP) && !defined(NVOCMP_MIGRATE_DISABLED)) || defined NVOCMP_RAM_OPTIMIZATION +/****************************************************************************** + * @fn NVOCMP_copyItem + * + * @brief Copy an NV item from active page to specified destination page + * + * @param srcPg - Source page + * @param dstPg - Destination page + * @param sOfs - Source page offset of original data in active page + * @param dOfs - Destination page offset to transferred copy of the item + * @param len - Length of data to copy + * + * @return none. + */ +static void NVOCMP_copyItem(uint8_t srcPg, uint8_t dstPg, uint16_t sOfs, uint16_t dOfs, uint16_t len) +{ + uint16_t num; +#ifndef NVOCMP_RAM_OPTIMIZATION + uint8_t tmp[NVOCMP_XFERBLKMAX]; +#else + uint8_t * tmp = (uint8_t *) tBuffer; +#endif + + // Copy over the data: Flash to RAM, then RAM to Flash + while (len > 0 && !NVOCMP_failW) + { + // Number of bytes to transfer in this block +#ifndef NVOCMP_RAM_OPTIMIZATION + num = (len < NVOCMP_XFERBLKMAX) ? len : NVOCMP_XFERBLKMAX; +#else + num = (len < NVOCMP_RAM_BUFFER_SIZE) ? len : NVOCMP_RAM_BUFFER_SIZE; +#endif + + // Get block of bytes from source page + NVOCMP_read(srcPg, sOfs, (uint8_t *) &tmp[0], num); + + // Write block to destination page + NVOCMP_failW = NVOCMP_write(dstPg, dOfs, (uint8_t *) &tmp[0], num); + + dOfs += num; + sOfs += num; + len -= num; + } +} +#endif +/****************************************************************************** + * @fn NVOCMP_readByte + * + * @brief Read one byte from Flash memory + * + * @param pg - NV Flash page + * @param ofs - Offset into the page + * + * @return byte read from flash memory + */ +static uint8_t NVOCMP_readByte(uint8_t pg, uint16_t ofs) +{ + uint8_t byteVal; + NVOCMP_read(pg, ofs, &byteVal, NVOCMP_ONEBYTE); + + return (byteVal); +} + +/****************************************************************************** + * @fn NVOCMP_writeByte + * + * @brief Write one byte to Flash and read back to verify + * + * @param pg - NV Flash page + * @param ofs - offset into the page + * @param bwv - byte to write & verify + * + * @return none ('failF' or 'failW' will be set if write fails) + */ +static void NVOCMP_writeByte(uint8_t pg, uint16_t ofs, uint8_t bwv) +{ + NVOCMP_failW = NVOCMP_write(pg, ofs, &bwv, 1); +} + +/****************************************************************************** + * @fn NVOCMP_doNVCRC + * + * @brief Computes the CRC8 on the NV buffer indicated + * CRC code external, API in crc.h + * + * @param pg - Flash page to check + * @param ofs - Flash page offset to lowest address item byte + * @param len - Item data length + * @param crc - value to start with, should be NULL if new calculation + * @param flag - fast flag (not used if NVOCMP_RAM_OPTIMIZATION is defined) + * + * @return crc byte + */ +static uint8_t NVOCMP_doNVCRC(uint8_t pg, uint16_t ofs, uint16_t len, uint8_t crc, bool flag) +{ + uint16_t rdLen = 0; +#ifndef NVOCMP_RAM_OPTIMIZATION +#ifdef NVOCMP_GPRAM + uint8_t * pTBuffer = RAM_BUFFER_ADDRESS; +#else + uint8_t * pTBuffer = (uint8_t *) tBuffer; +#endif +#endif + uint8_t tmp[NVOCMP_XFERBLKMAX]; + crc_t newCRC = (crc_t) crc; + + // Read flash and compute CRC in blocks + while (len > 0) + { + rdLen = (len < NVOCMP_XFERBLKMAX ? len : NVOCMP_XFERBLKMAX); +#ifndef NVOCMP_RAM_OPTIMIZATION + if (flag) + { + memcpy((uint8_t *) tmp, (uint8_t *) (pTBuffer + ofs), rdLen); + } + else + { +#endif + NVOCMP_read(pg, ofs, tmp, rdLen); +#ifndef NVOCMP_RAM_OPTIMIZATION + } +#endif + newCRC = crc_update(newCRC, tmp, rdLen); + len -= rdLen; + ofs += rdLen; + } + + return (newCRC); +} + +/****************************************************************************** + * @fn NVOCMP_doRAMCRC + * + * @brief Calculates CRC8 given a buffer and length + * CRC code external, API in crc.h + * + * @param input - pointer to data buffer + * @param len - length of data in buffer + * @param crc - value to start with, should be NULL if new calculation + * + * @return CRC8 byte + */ +static uint8_t NVOCMP_doRAMCRC(uint8_t * input, uint16_t len, uint8_t crc) +{ + crc_t newCRC = crc_update((crc_t) crc, input, len); + + return ((uint8_t) newCRC); +} + +/****************************************************************************** + * @fn NVOCMP_verifyCRC + * + * @brief Helper function to validate item crc from NV + * + * @param iOfs - offset to item data + * @param len - length of item data + * @param crc - crc to compare against + * @param pg - page to work on + * @param flag - fast flag (not used if NVOCMP_RAM_OPTIMIZATION is defined) + * + * @return status byte + */ +static uint8_t NVOCMP_verifyCRC(uint16_t iOfs, uint16_t len, uint8_t crc, uint8_t pg, bool flag) +{ + uint8_t newCRC; + uint16_t crcLen = len + NVOCMP_HDRCRCINC - 1; +#if NVOCMP_HDRLE + uint8_t finalByte = (len >> 6) & 0x3F; +#else + uint8_t finalByte = (len & 0x3F) << 2; +#endif + // CRC calculations stop at the length field of header + // So the last byte must be done separately + newCRC = NVOCMP_doNVCRC(pg, iOfs, crcLen, 0, flag); + newCRC = NVOCMP_doRAMCRC(&finalByte, sizeof(finalByte), newCRC); + NVOCMP_ALERT(newCRC == crc, "Invalid CRC detected.") +#ifdef NVOCMP_STATS + if (newCRC != crc) + { + NVOCMP_badCRCCount++; + } +#endif // NVOCMP_STATS + return (newCRC == crc ? NVINTF_SUCCESS : NVINTF_CORRUPT); +} + +#ifdef ENABLE_SANITY_CHECK +/****************************************************************************** + * @fn NVOCMP_sanityCheckApi + * + * @brief Global function to perform a sanity check on the active + * partition to report if corruption has been detected. + * + * @param none + * + * @return 0: No failure found. + * Non-zero: failure, each bit representing a particular error + * as indicated in NV driver status codes defined in nvintf.h. + */ +static uint32_t NVOCMP_sanityCheckApi(void) +{ + NVOCMP_nvHandle_t * pNvHandle = &NVOCMP_nvHandle; + bool needScan = false; + bool needSkip = false; + uint16_t dstOff; + uint16_t endOff; + uint16_t srcOff; + uint16_t crcOff; + uint8_t dstPg; + uint8_t srcPg; + uint32_t ret = NVINTF_SUCCESS; + uint32_t aItem = 0; + + // Reset Flash erase/write fail indicator + NVOCMP_failW = NVINTF_SUCCESS; + + // Stop looking when we get to this offset + endOff = NVOCMP_PGDATAOFS + NVOCMP_ITEMHDRLEN - 1; + +#if (NVOCMP_NVPAGES == NVOCMP_NVONEP) + srcPg = 0; + srcOff = pNvHandle->pageInfo[0].offset; + dstPg = 0; + dstOff = NVOCMP_PGDATAOFS; +#else + srcPg = pNvHandle->headPage; + srcOff = pNvHandle->pageInfo[srcPg].offset; + dstPg = pNvHandle->tailPage; + dstOff = pNvHandle->pageInfo[dstPg].offset; +#endif + + NVOCMP_ALERT(false, "Sanity Check") + + while (srcOff > endOff) + { + if (NVOCMP_failW == NVINTF_SUCCESS) + { + NVOCMP_itemHdr_t srcHdr; + uint16_t dataLen; + uint16_t itemSize; + + needScan = false; + needSkip = false; + + // Read and decompress item header + NVOCMP_readHeader(srcPg, srcOff - NVOCMP_ITEMHDRLEN, &srcHdr, false); + dataLen = srcHdr.len; + itemSize = NVOCMP_ITEMHDRLEN + dataLen; + crcOff = srcOff - NVOCMP_ITEMHDRLEN - dataLen; + + // Check if length is safe + if (srcOff < (dataLen + NVOCMP_PGDATAOFS)) + { + NVOCMP_ALERT(false, "Item header corrupted: Data length too long") + ret |= (1 << NVINTF_BADLENGTH); + needScan = true; + srcOff--; + } + else if (NVOCMP_SIGNATURE != srcHdr.sig) + { + NVOCMP_ALERT(false, "Item header corrupted: Invalid signature") + ret |= (1 << NVINTF_NO_SIG); + needScan = true; + srcOff--; + } + else if (NVOCMP_verifyCRC(crcOff, dataLen, srcHdr.crc8, srcPg, false)) + { + // Invalid CRC, corruption + NVOCMP_ALERT(false, "Item CRC incorrect!") + ret |= (1 << NVINTF_CORRUPT); + needScan = true; + srcOff--; + } + else if (!(srcHdr.stats & NVOCMP_VALIDIDBIT) && // Item is valid + (srcHdr.stats & NVOCMP_ACTIVEIDBIT)) // Item is active + { + // Valid CRC, item is active + srcOff -= NVOCMP_ITEMHDRLEN; + } + else + { + // Valid CRC but item is inactive + srcOff -= NVOCMP_ITEMHDRLEN; + needSkip = true; + } + + if (needScan) + { + // Detected a problem, find next header (scan for signature) + NVOCMP_ALERT(false, "Attempting to find signature...") + bool foundSig = NVOCMP_findSignature(srcPg, &srcOff); + if (!foundSig) + { + // If we get here and foundSig is false, we never found another + // item in the page, break the loop and report that corruption + // has been detected + NVOCMP_ALERT(foundSig, "Attempt to find signature failed.") + ret |= (1 << NVINTF_NO_SIG); + break; + } + } + else + { + if (!needSkip) + { + dstOff += itemSize; + aItem++; + } + NVOCMP_ALERT(srcOff > dataLen, "Offset overflow: srcOff") + srcOff -= dataLen; + } + } + else + { + // Failure during item xfer makes next findItem() unreliable + NVOCMP_ASSERT(false, "SANITY CHECK FAILURE") + ret |= (1 << NVINTF_FAILURE); + } + } // end of while + + if (NVOCMP_failW != NVINTF_SUCCESS) + { + // Something bad happened when scanning the page + NVOCMP_ASSERT(false, "SANITY CHECK FAILURE") + ret |= (1 << NVINTF_FAILURE); + } + + return (ret); +} +#endif + +//***************************************************************************** diff --git a/third_party/openthread/ot-ti b/third_party/openthread/ot-ti index 6f30243676724e..e7fbbcc60c25d1 160000 --- a/third_party/openthread/ot-ti +++ b/third_party/openthread/ot-ti @@ -1 +1 @@ -Subproject commit 6f30243676724ef1472d20098e00eb20b3f20679 +Subproject commit e7fbbcc60c25d1dec3ed4d02cff9acd866091ce7 diff --git a/third_party/ti_simplelink_sdk/mcuboot/flash_map_backend.h b/third_party/ti_simplelink_sdk/mcuboot/flash_map_backend.h index de84f13558d573..e6c567591dc59b 100644 --- a/third_party/ti_simplelink_sdk/mcuboot/flash_map_backend.h +++ b/third_party/ti_simplelink_sdk/mcuboot/flash_map_backend.h @@ -77,7 +77,7 @@ #define BOOT_SECONDARY_2_BASE_ADDRESS (BOOT_SECONDARY_1_BASE_ADDRESS + BOOT_SECONDARY_1_SIZE) #define BOOT_SECONDARY_2_SIZE BOOT_SLOT_2_SIZE #else -#define BOOT_SLOT_1_SIZE 0x000F6000 +#define BOOT_SLOT_1_SIZE 0x000F2000 /* Internal Flash locations */ #define BOOTLOADER_BASE_ADDRESS 0x00000000 diff --git a/third_party/ti_simplelink_sdk/mcuboot/mcuboot_config/mcuboot_config.h b/third_party/ti_simplelink_sdk/mcuboot/mcuboot_config/mcuboot_config.h index 1977bb7af88f4d..cac6f807abd0b6 100644 --- a/third_party/ti_simplelink_sdk/mcuboot/mcuboot_config/mcuboot_config.h +++ b/third_party/ti_simplelink_sdk/mcuboot/mcuboot_config/mcuboot_config.h @@ -27,7 +27,7 @@ #define MCUBOOT_DOWNGRADE_PREVENTION #define MCUBOOT_HAVE_LOGGING 1 #define MCUBOOT_IMAGE_NUMBER 1 -#define MCUBOOT_MAX_IMG_SECTORS 492 +#define MCUBOOT_MAX_IMG_SECTORS 484 #define MCUBOOT_OVERWRITE_ONLY #define MCUBOOT_SIGN_EC256 #define MCUBOOT_USE_FLASH_AREA_GET_SECTORS diff --git a/third_party/ti_simplelink_sdk/ti_simplelink_executable.gni b/third_party/ti_simplelink_sdk/ti_simplelink_executable.gni index 94e7851bc60a35..3df24854297f49 100644 --- a/third_party/ti_simplelink_sdk/ti_simplelink_executable.gni +++ b/third_party/ti_simplelink_sdk/ti_simplelink_executable.gni @@ -176,7 +176,7 @@ template("ti_simplelink_executable") { "--align", "4", "--slot-size", - "0x000F6000", # must match the flash_map_backend definitions + "0x000F2000", # must match the flash_map_backend definitions "--version", matter_software_ver_str, "--pad-header", diff --git a/third_party/ti_simplelink_sdk/ti_simplelink_sdk.gni b/third_party/ti_simplelink_sdk/ti_simplelink_sdk.gni index af3e810d67e6bf..b3aae392cfb39d 100644 --- a/third_party/ti_simplelink_sdk/ti_simplelink_sdk.gni +++ b/third_party/ti_simplelink_sdk/ti_simplelink_sdk.gni @@ -347,8 +347,8 @@ template("ti_simplelink_sdk") { configs -= [ "${build_root}/config/compiler:std_default" ] configs += [ ":${sdk_target_name}_posix_config" ] sources = [ + "${chip_root}/src/platform/cc13xx_26xx/nvocmp.c", "${ti_simplelink_sdk_root}/source/ti/common/nv/crc.c", - "${ti_simplelink_sdk_root}/source/ti/common/nv/nvocmp.c", ] public_configs = [ ":${sdk_target_name}_config" ] @@ -398,7 +398,7 @@ template("ti_simplelink_sdk") { defines += [ "ONE_BLE_LIB_SIZE_OPTIMIZATION", - "NVOCMP_NVPAGES=5", + "NVOCMP_NVPAGES=12", "NVOCMP_NWSAMEITEM=1", "CC13X2P", "SYSCFG",