From f97a4e9db8a6f7de36b6d794b8e2f528cd215186 Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Mon, 5 Aug 2024 10:15:38 +0300 Subject: [PATCH 01/11] [platform][common] Add old nxp/k32w/common files Moving these files here since they are used by multiple platforms. K32W1 will be updated in the future and the old nxp/k32w/common will be removed. Keep it for now (duplicated files) to ease the synchronization of changes. Signed-off-by: marius-alex-tache --- .../nxp/common/legacy/BLEManagerCommon.cpp | 1435 +++++++++++++++++ .../nxp/common/legacy/BLEManagerCommon.h | 249 +++ .../nxp/common/legacy/FactoryDataDriver.cpp | 76 + .../nxp/common/legacy/FactoryDataDriver.h | 113 ++ .../nxp/common/legacy/FactoryDataProvider.cpp | 442 +++++ .../nxp/common/legacy/FactoryDataProvider.h | 177 ++ .../common/legacy/OTAFactoryDataProcessor.cpp | 165 ++ .../common/legacy/OTAFactoryDataProcessor.h | 78 + .../common/legacy/OTAImageProcessorImpl.cpp | 424 +++++ .../nxp/common/legacy/OTAImageProcessorImpl.h | 114 ++ .../nxp/common/legacy/OTATlvProcessor.cpp | 178 ++ .../nxp/common/legacy/OTATlvProcessor.h | 180 +++ src/platform/nxp/common/legacy/OTA_README.md | 149 ++ 13 files changed, 3780 insertions(+) create mode 100644 src/platform/nxp/common/legacy/BLEManagerCommon.cpp create mode 100644 src/platform/nxp/common/legacy/BLEManagerCommon.h create mode 100644 src/platform/nxp/common/legacy/FactoryDataDriver.cpp create mode 100644 src/platform/nxp/common/legacy/FactoryDataDriver.h create mode 100644 src/platform/nxp/common/legacy/FactoryDataProvider.cpp create mode 100644 src/platform/nxp/common/legacy/FactoryDataProvider.h create mode 100644 src/platform/nxp/common/legacy/OTAFactoryDataProcessor.cpp create mode 100644 src/platform/nxp/common/legacy/OTAFactoryDataProcessor.h create mode 100644 src/platform/nxp/common/legacy/OTAImageProcessorImpl.cpp create mode 100644 src/platform/nxp/common/legacy/OTAImageProcessorImpl.h create mode 100644 src/platform/nxp/common/legacy/OTATlvProcessor.cpp create mode 100644 src/platform/nxp/common/legacy/OTATlvProcessor.h create mode 100644 src/platform/nxp/common/legacy/OTA_README.md diff --git a/src/platform/nxp/common/legacy/BLEManagerCommon.cpp b/src/platform/nxp/common/legacy/BLEManagerCommon.cpp new file mode 100644 index 00000000000000..d9dbde88f3a614 --- /dev/null +++ b/src/platform/nxp/common/legacy/BLEManagerCommon.cpp @@ -0,0 +1,1435 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2020 Nest Labs, Inc. + * 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. + */ + +/** + * @file + * Provides an implementation of the BLEManager singleton object + * for the K32W platforms. + */ + +/* this file behaves like a config.h, comes first */ +#include + +#include + +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE + +#include + +#include "board.h" +#include "gatt_db_app_interface.h" +#include "gatt_db_handles.h" +#include "stdio.h" +#include "timers.h" + +#if defined(CPU_JN518X) && defined(chip_with_low_power) && (chip_with_low_power == 1) +#include "PWR_Configuration.h" +#endif + +#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING +#include +#include +#endif + +/******************************************************************************* + * Local data types + *******************************************************************************/ +extern "C" bool_t Ble_ConfigureHostStackConfig(void); + +#if defined(chip_with_low_power) && (chip_with_low_power == 1) +extern "C" void PWR_DisallowDeviceToSleep(void); +extern "C" void PWR_AllowDeviceToSleep(void); +#endif + +using namespace ::chip; +using namespace ::chip::Ble; + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +namespace { +/******************************************************************************* + * Macros & Constants definitions + *******************************************************************************/ +/* Timeout of BLE commands */ +#define CHIP_BLE_KW_EVNT_TIMEOUT 1000 / portTICK_PERIOD_MS + +/** BLE advertisement state changed */ +#define CHIP_BLE_KW_EVNT_ADV_CHANGED 0x0001 +/** BLE advertisement command failed */ +#define CHIP_BLE_KW_EVNT_ADV_FAILED 0x0002 +/** BLE advertisement setup failed */ +#define CHIP_BLE_KW_EVNT_ADV_SETUP_FAILED 0x0004 +/** BLE advertisement parameters setup complete */ +#define CHIP_BLE_KW_EVNT_ADV_PAR_SETUP_COMPLETE 0x0008 +/** BLE advertisement data setup complete */ +#define CHIP_BLE_KW_EVNT_ADV_DAT_SETUP_COMPLETE 0x0010 +/** BLE random address set */ +#define CHIP_BLE_KW_EVNT_RND_ADDR_SET 0x0020 +/** BLE Initialization complete */ +#define CHIP_BLE_KW_EVNT_INIT_COMPLETE 0x0040 +/** BLE Received a handle value confirmation from the client */ +#define CHIP_BLE_KW_EVNT_INDICATION_CONFIRMED 0x0080 +/** BLE send indication failed */ +#define CHIP_BLE_KW_EVNT_INDICATION_FAILED 0x0100 +/** TX Power Level Set */ +#define CHIP_BLE_KW_EVNT_POWER_LEVEL_SET 0x0200 +/** Maximal time of connection without activity */ +#define CHIP_BLE_KW_CONN_TIMEOUT 60000 +/** Maximum number of pending BLE events */ +#define CHIP_BLE_EVENT_QUEUE_MAX_ENTRIES 10 + +#define LOOP_EV_BLE (0x08) + +/* controller task configuration */ +#define CONTROLLER_TASK_PRIORITY (6U) +#define CONTROLLER_TASK_STACK_SIZE (gControllerTaskStackSize_c / sizeof(StackType_t)) + +/* host task configuration */ +#define HOST_TASK_PRIORITY (4U) +#define HOST_TASK_STACK_SIZE (gHost_TaskStackSize_c / sizeof(StackType_t)) + +/* advertising configuration */ +#define BLEKW_ADV_MAX_NO (2) +#define BLEKW_SCAN_RSP_MAX_NO (2) +#define BLEKW_MAX_ADV_DATA_LEN (31) +#define CHIP_ADV_SHORT_UUID_LEN (2) + +/* FreeRTOS sw timer */ +TimerHandle_t sbleAdvTimeoutTimer; + +/* Queue used to synchronize asynchronous messages from the KW BLE tasks */ +QueueHandle_t sBleEventQueue; + +/* Used to manage asynchronous events from BLE Stack: e.g.: GAP setup finished */ +EventGroupHandle_t sEventGroup; + +TimerHandle_t connectionTimeout; + +const uint8_t ShortUUID_CHIPoBLEService[] = { 0xF6, 0xFF }; + +#if defined(chip_with_low_power) && (chip_with_low_power == 1) +static bool bleAppStopInProgress; +#endif + +BLEManagerCommon * sImplInstance = nullptr; + +} // namespace + +CHIP_ERROR BLEManagerCommon::_Init() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + EventBits_t eventBits; + uint16_t attChipRxHandle[1] = { (uint16_t) value_chipoble_rx }; + +#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING + uint16_t attChipC3Handle[1] = { (uint16_t) value_chipoble_c3 }; +#endif + + mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled; + + // Check if BLE stack is initialized + VerifyOrExit(!mFlags.Has(Flags::kK32WBLEStackInitialized), err = CHIP_ERROR_INCORRECT_STATE); + + // Initialize the Chip BleLayer. + err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); + SuccessOrExit(err); + + /* Initialization of message wait events - + * used for receiving BLE Stack events */ + sEventGroup = xEventGroupCreate(); + VerifyOrExit(sEventGroup != NULL, err = CHIP_ERROR_INCORRECT_STATE); + + /* Prepare callback input queue.*/ + sBleEventQueue = xQueueCreate(CHIP_BLE_EVENT_QUEUE_MAX_ENTRIES, sizeof(blekw_msg_t *)); + VerifyOrExit(sBleEventQueue != NULL, err = CHIP_ERROR_INCORRECT_STATE); + + /* Create the connection timeout timer. */ + connectionTimeout = + xTimerCreate("bleTimeoutTmr", pdMS_TO_TICKS(CHIP_BLE_KW_CONN_TIMEOUT), pdFALSE, (void *) 0, blekw_connection_timeout_cb); + VerifyOrExit(connectionTimeout != NULL, err = CHIP_ERROR_INCORRECT_STATE); + + sImplInstance = GetImplInstance(); + + /* BLE platform code initialization */ + SuccessOrExit(err = InitHostController(&blekw_generic_cb)); + + /* Register the GATT server callback */ + VerifyOrExit(GattServer_RegisterCallback(blekw_gatt_server_cb) == gBleSuccess_c, err = CHIP_ERROR_INCORRECT_STATE); + + /* Wait until BLE Stack is ready */ + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_INIT_COMPLETE, pdTRUE, pdTRUE, CHIP_BLE_KW_EVNT_TIMEOUT); + VerifyOrExit(eventBits & CHIP_BLE_KW_EVNT_INIT_COMPLETE, err = CHIP_ERROR_INCORRECT_STATE); + +#if BLE_HIGH_TX_POWER + /* Set Adv Power */ + Gap_SetTxPowerLevel(gAdvertisingPowerLeveldBm_c, gTxPowerAdvChannel_c); + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_POWER_LEVEL_SET, pdTRUE, pdTRUE, CHIP_BLE_KW_EVNT_TIMEOUT); + VerifyOrExit(eventBits & CHIP_BLE_KW_EVNT_POWER_LEVEL_SET, err = CHIP_ERROR_INCORRECT_STATE); + + /* Set Connect Power */ + Gap_SetTxPowerLevel(gConnectPowerLeveldBm_c, gTxPowerConnChannel_c); + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_POWER_LEVEL_SET, pdTRUE, pdTRUE, CHIP_BLE_KW_EVNT_TIMEOUT); + VerifyOrExit(eventBits & CHIP_BLE_KW_EVNT_POWER_LEVEL_SET, err = CHIP_ERROR_INCORRECT_STATE); +#endif + +#if defined(CPU_JN518X) && defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_ChangeDeepSleepMode(cPWR_PowerDown_RamRet); +#endif + + GattServer_RegisterHandlesForWriteNotifications(1, attChipRxHandle); +#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING + VerifyOrExit(GattServer_RegisterHandlesForReadNotifications(1, attChipC3Handle) == gBleSuccess_c, + err = CHIP_ERROR_INCORRECT_STATE); +#endif + + mFlags.Set(Flags::kK32WBLEStackInitialized); + mFlags.Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART ? true : false); + mFlags.Set(Flags::kFastAdvertisingEnabled); + + // Create FreeRTOS sw timer for BLE timeouts and interval change. + sbleAdvTimeoutTimer = xTimerCreate("BleAdvTimer", // Just a text name, not used by the RTOS kernel + pdMS_TO_TICKS(100), // == default timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = ble obj context + BleAdvTimeoutHandler // timer callback handler + ); + VerifyOrExit(sbleAdvTimeoutTimer != NULL, err = CHIP_ERROR_INCORRECT_STATE); + +exit: + return err; +} + +uint16_t BLEManagerCommon::_NumConnections(void) +{ + return static_cast(mDeviceConnected == true); +} + +bool BLEManagerCommon::_IsAdvertisingEnabled(void) +{ + return mFlags.Has(Flags::kAdvertisingEnabled); +} + +bool BLEManagerCommon::_IsAdvertising(void) +{ + return mFlags.Has(Flags::kAdvertising); +} + +CHIP_ERROR BLEManagerCommon::_SetAdvertisingEnabled(bool val) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + if (mFlags.Has(Flags::kAdvertisingEnabled) != val) + { + mFlags.Set(Flags::kAdvertisingEnabled, val); + PlatformMgr().ScheduleWork(DriveBLEState, 0); + } + +exit: + return err; +} + +CHIP_ERROR BLEManagerCommon::_SetAdvertisingMode(BLEAdvertisingMode mode) +{ + switch (mode) + { + case BLEAdvertisingMode::kFastAdvertising: + mFlags.Set(Flags::kFastAdvertisingEnabled); + break; + case BLEAdvertisingMode::kSlowAdvertising: { + // We are in FreeRTOS timer service context, which is a default daemon task and has + // the highest priority. Stop advertising should be scheduled to run from Matter task. + mFlags.Clear(Flags::kFastAdvertisingEnabled); + PlatformMgr().ScheduleWork(StopAdvertisingPriorToSwitchingMode, 0); + break; + } + default: + return CHIP_ERROR_INVALID_ARGUMENT; + } + mFlags.Set(Flags::kRestartAdvertising); + PlatformMgr().ScheduleWork(DriveBLEState, 0); + return CHIP_NO_ERROR; +} + +CHIP_ERROR BLEManagerCommon::_GetDeviceName(char * buf, size_t bufSize) +{ + if (strlen(mDeviceName) >= bufSize) + { + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + strcpy(buf, mDeviceName); + return CHIP_NO_ERROR; +} + +CHIP_ERROR BLEManagerCommon::_SetDeviceName(const char * deviceName) +{ + if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_NotSupported) + { + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } + if (deviceName != NULL && deviceName[0] != 0) + { + if (strlen(deviceName) >= kMaxDeviceNameLength) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + memset(mDeviceName, 0, kMaxDeviceNameLength); + strcpy(mDeviceName, deviceName); + mFlags.Set(Flags::kDeviceNameSet); + ChipLogProgress(DeviceLayer, "Setting device name to : \"%s\"", deviceName); + } + else + { + mDeviceName[0] = 0; + mFlags.Clear(Flags::kDeviceNameSet); + } + + return CHIP_NO_ERROR; +} + +void BLEManagerCommon::_OnPlatformEvent(const ChipDeviceEvent * event) +{ + switch (event->Type) + { + case DeviceEventType::kCHIPoBLESubscribe: + ChipDeviceEvent connEstEvent; + + HandleSubscribeReceived(event->CHIPoBLESubscribe.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); + connEstEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished; + PlatformMgr().PostEventOrDie(&connEstEvent); + break; + + case DeviceEventType::kCHIPoBLEUnsubscribe: + HandleUnsubscribeReceived(event->CHIPoBLEUnsubscribe.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); + break; + + case DeviceEventType::kCHIPoBLEWriteReceived: + HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_1_UUID, + PacketBufferHandle::Adopt(event->CHIPoBLEWriteReceived.Data)); + break; + + case DeviceEventType::kCHIPoBLEConnectionError: + HandleConnectionError(event->CHIPoBLEConnectionError.ConId, event->CHIPoBLEConnectionError.Reason); + break; + + case DeviceEventType::kCHIPoBLEIndicateConfirm: + HandleIndicationConfirmation(event->CHIPoBLEIndicateConfirm.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); + break; + + default: + break; + } +} + +CHIP_ERROR BLEManagerCommon::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, + const ChipBleUUID * charId) +{ + ChipLogProgress(DeviceLayer, "BLEManagerCommon::SubscribeCharacteristic() not supported"); + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR BLEManagerCommon::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, + const ChipBleUUID * charId) +{ + ChipLogProgress(DeviceLayer, "BLEManagerCommon::UnsubscribeCharacteristic() not supported"); + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR BLEManagerCommon::CloseConnection(BLE_CONNECTION_OBJECT conId) +{ + return blekw_stop_connection_internal(conId); +} + +uint16_t BLEManagerCommon::GetMTU(BLE_CONNECTION_OBJECT conId) const +{ + uint16_t tempMtu = 0; + (void) Gatt_GetMtu(conId, &tempMtu); + + return tempMtu; +} + +CHIP_ERROR BLEManagerCommon::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, + PacketBufferHandle pBuf) +{ + ChipLogProgress(DeviceLayer, "BLEManagerCommon::SendWriteRequest() not supported"); + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +void BLEManagerCommon::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) +{ + BLEMgrImpl().CloseConnection(conId); +} + +CHIP_ERROR BLEManagerCommon::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, + PacketBufferHandle data) +{ + VerifyOrReturnError(UUIDsMatch(&Ble::CHIP_BLE_CHAR_2_UUID, charId), BLE_ERROR_GATT_WRITE_FAILED); + + CHIP_ERROR err = CHIP_NO_ERROR; + + if (blekw_send_event(conId, value_chipoble_tx, data->Start(), data->DataLength()) != BLE_OK) + { + err = CHIP_ERROR_SENDING_BLOCKED; + } + else + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kCHIPoBLEIndicateConfirm; + event.CHIPoBLEIndicateConfirm.ConId = conId; + err = PlatformMgr().PostEvent(&event); + } + + return err; +} + +BLEManagerCommon::ble_err_t BLEManagerCommon::blekw_send_event(int8_t connection_handle, uint16_t handle, uint8_t * data, + uint32_t len) +{ + EventBits_t eventBits; + +#if CHIP_DEVICE_CHIP0BLE_DEBUG + ChipLogProgress(DeviceLayer, "Trying to send event."); +#endif + + if (connection_handle < 0 || handle <= 0) + { + ChipLogProgress(DeviceLayer, "BLE Event - Bad Handle"); + return BLE_E_FAIL; + } + + if (len > 0 && data == NULL) + { + ChipLogProgress(DeviceLayer, "BLE Event - Invalid Data"); + return BLE_E_FAIL; + } + + /************* Send the indication *************/ + xEventGroupClearBits(sEventGroup, CHIP_BLE_KW_EVNT_INDICATION_CONFIRMED | CHIP_BLE_KW_EVNT_INDICATION_FAILED); + + if (GattServer_SendInstantValueIndication(connection_handle, handle, len, data) != gBleSuccess_c) + { + ChipLogProgress(DeviceLayer, "BLE Event - Can't sent indication"); + return BLE_E_FAIL; + } + + /* Wait until BLE Stack is ready */ + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_INDICATION_CONFIRMED | CHIP_BLE_KW_EVNT_INDICATION_FAILED, pdTRUE, + pdFALSE, CHIP_BLE_KW_EVNT_TIMEOUT); + + if (eventBits & CHIP_BLE_KW_EVNT_INDICATION_FAILED) + { + ChipLogProgress(DeviceLayer, "BLE Event - Sent Failed"); + return BLE_E_FAIL; + } + +#if CHIP_DEVICE_CHIP0BLE_DEBUG + ChipLogProgress(DeviceLayer, "BLE Event - Sent :-) "); +#endif + + return BLE_OK; +} +/******************************************************************************* + * Private functions + *******************************************************************************/ + +BLEManagerCommon::ble_err_t BLEManagerCommon::blekw_start_advertising(gapAdvertisingParameters_t * adv_params, + gapAdvertisingData_t * adv, gapScanResponseData_t * scnrsp) +{ + EventBits_t eventBits; + + /************* Set the advertising parameters *************/ + xEventGroupClearBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_SETUP_FAILED | CHIP_BLE_KW_EVNT_ADV_PAR_SETUP_COMPLETE); + + /* Set the advertising parameters */ + if (Gap_SetAdvertisingParameters(adv_params) != gBleSuccess_c) + { + vTaskDelay(1); + + /* Retry, just to make sure before giving up and sending an error. */ + if (Gap_SetAdvertisingParameters(adv_params) != gBleSuccess_c) + { + return BLE_E_SET_ADV_PARAMS; + } + } + + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_SETUP_FAILED | CHIP_BLE_KW_EVNT_ADV_PAR_SETUP_COMPLETE, + pdTRUE, pdFALSE, CHIP_BLE_KW_EVNT_TIMEOUT); + + if (!(eventBits & CHIP_BLE_KW_EVNT_ADV_PAR_SETUP_COMPLETE)) + { + return BLE_E_ADV_PARAMS_FAILED; + } + + /************* Set the advertising data *************/ + xEventGroupClearBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_SETUP_FAILED | CHIP_BLE_KW_EVNT_ADV_DAT_SETUP_COMPLETE); + + /* Set the advertising data */ + if (Gap_SetAdvertisingData(adv, scnrsp) != gBleSuccess_c) + { + return BLE_E_SET_ADV_DATA; + } + + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_SETUP_FAILED | CHIP_BLE_KW_EVNT_ADV_DAT_SETUP_COMPLETE, + pdTRUE, pdFALSE, CHIP_BLE_KW_EVNT_TIMEOUT); + + if (!(eventBits & CHIP_BLE_KW_EVNT_ADV_DAT_SETUP_COMPLETE)) + { + return BLE_E_ADV_SETUP_FAILED; + } + + /************* Start the advertising *************/ + xEventGroupClearBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_CHANGED | CHIP_BLE_KW_EVNT_ADV_FAILED); + + if (gBleSuccess_c != Gap_CreateRandomDeviceAddress(NULL, NULL)) + { + return BLE_E_SET_ADV_PARAMS; + } + + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_RND_ADDR_SET, pdTRUE, pdTRUE, CHIP_BLE_KW_EVNT_TIMEOUT); + + if (!(eventBits & CHIP_BLE_KW_EVNT_RND_ADDR_SET)) + { + return BLE_E_ADV_PARAMS_FAILED; + } + + /* Start the advertising */ + if (Gap_StartAdvertising(blekw_gap_advertising_cb, blekw_gap_connection_cb) != gBleSuccess_c) + { + return BLE_E_START_ADV; + } + +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_DisallowDeviceToSleep(); +#endif + + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_CHANGED | CHIP_BLE_KW_EVNT_ADV_FAILED, pdTRUE, pdFALSE, + CHIP_BLE_KW_EVNT_TIMEOUT); + if (!(eventBits & CHIP_BLE_KW_EVNT_ADV_CHANGED)) + { +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_AllowDeviceToSleep(); +#endif + return BLE_E_START_ADV_FAILED; + } + +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_AllowDeviceToSleep(); +#endif + + return BLE_OK; +} + +BLEManagerCommon::ble_err_t BLEManagerCommon::blekw_stop_advertising(void) +{ + EventBits_t eventBits; + bleResult_t res; + + xEventGroupClearBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_CHANGED | CHIP_BLE_KW_EVNT_ADV_FAILED); + + /* Stop the advertising data */ + res = Gap_StopAdvertising(); + if (res != gBleSuccess_c) + { + ChipLogProgress(DeviceLayer, "Failed to stop advertising %d", res); + return BLE_E_STOP; + } + + eventBits = xEventGroupWaitBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_CHANGED | CHIP_BLE_KW_EVNT_ADV_FAILED, pdTRUE, pdFALSE, + CHIP_BLE_KW_EVNT_TIMEOUT); + + if (eventBits & CHIP_BLE_KW_EVNT_ADV_FAILED) + { + ChipLogProgress(DeviceLayer, "Stop advertising flat out failed."); + return BLE_E_ADV_FAILED; + } + else if (!(eventBits & CHIP_BLE_KW_EVNT_ADV_CHANGED)) + { + ChipLogProgress(DeviceLayer, "Stop advertising event timeout."); + return BLE_E_ADV_CHANGED; + } + + return BLE_OK; +} + +CHIP_ERROR BLEManagerCommon::ConfigureAdvertisingData(void) +{ + ble_err_t err; + CHIP_ERROR chipErr; + uint16_t discriminator; + uint16_t advInterval = 0; + gapAdvertisingData_t adv = { 0 }; + gapAdStructure_t adv_data[BLEKW_ADV_MAX_NO] = { { 0 } }; + gapAdStructure_t scan_rsp_data[BLEKW_SCAN_RSP_MAX_NO] = { { 0 } }; + uint8_t advPayload[BLEKW_MAX_ADV_DATA_LEN] = { 0 }; + gapScanResponseData_t scanRsp = { 0 }; + gapAdvertisingParameters_t adv_params = { 0 }; + uint8_t chipAdvDataFlags = (gLeGeneralDiscoverableMode_c | gBrEdrNotSupported_c); + uint8_t chipOverBleService[2]; + ChipBLEDeviceIdentificationInfo mDeviceIdInfo = { 0 }; + uint8_t mDeviceIdInfoLength = 0; + + chipErr = GetCommissionableDataProvider()->GetSetupDiscriminator(discriminator); + if (chipErr != CHIP_NO_ERROR) + { + return chipErr; + } + + if (!mFlags.Has(Flags::kDeviceNameSet)) + { + memset(mDeviceName, 0, kMaxDeviceNameLength); + snprintf(mDeviceName, kMaxDeviceNameLength, "%s%04u", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, discriminator); + } + + /**************** Prepare advertising data *******************************************/ + adv.cNumAdStructures = BLEKW_ADV_MAX_NO; + + chipErr = ConfigurationMgr().GetBLEDeviceIdentificationInfo(mDeviceIdInfo); + SuccessOrExit(chipErr); + mDeviceIdInfoLength = sizeof(mDeviceIdInfo); + + if ((mDeviceIdInfoLength + CHIP_ADV_SHORT_UUID_LEN + 1) > BLEKW_MAX_ADV_DATA_LEN) + { + return CHIP_ERROR_INCORRECT_STATE; + } + + adv_data[0].length = 0x02; + adv_data[0].adType = gAdFlags_c; + adv_data[0].aData = (uint8_t *) (&chipAdvDataFlags); + + adv_data[1].length = static_cast(mDeviceIdInfoLength + CHIP_ADV_SHORT_UUID_LEN + 1); + adv_data[1].adType = gAdServiceData16bit_c; + memcpy(advPayload, ShortUUID_CHIPoBLEService, CHIP_ADV_SHORT_UUID_LEN); + memcpy(&advPayload[CHIP_ADV_SHORT_UUID_LEN], (void *) &mDeviceIdInfo, mDeviceIdInfoLength); + adv_data[1].aData = advPayload; + + adv.aAdStructures = adv_data; + +#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING + ReturnErrorOnFailure(EncodeAdditionalDataTlv()); +#endif + + /**************** Prepare scan response data *******************************************/ + scanRsp.cNumAdStructures = BLEKW_SCAN_RSP_MAX_NO; + + scan_rsp_data[0].length = static_cast(strlen(mDeviceName) + 1); + scan_rsp_data[0].adType = gAdCompleteLocalName_c; + scan_rsp_data[0].aData = (uint8_t *) mDeviceName; + + scan_rsp_data[1].length = sizeof(chipOverBleService) + 1; + scan_rsp_data[1].adType = gAdComplete16bitServiceList_c; + chipOverBleService[0] = ShortUUID_CHIPoBLEService[0]; + chipOverBleService[1] = ShortUUID_CHIPoBLEService[1]; + scan_rsp_data[1].aData = (uint8_t *) chipOverBleService; + + scanRsp.aAdStructures = scan_rsp_data; + + /**************** Prepare advertising parameters *************************************/ + if (mFlags.Has(Flags::kFastAdvertisingEnabled)) + { + advInterval = CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MAX; + } + else + { + advInterval = CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MAX; + } + advInterval = (uint16_t) (advInterval * 0.625F); + + adv_params.minInterval = adv_params.maxInterval = advInterval; + adv_params.advertisingType = gAdvConnectableUndirected_c; + adv_params.ownAddressType = gBleAddrTypeRandom_c; + adv_params.peerAddressType = gBleAddrTypePublic_c; + memset(adv_params.peerAddress, 0, gcBleDeviceAddressSize_c); + adv_params.channelMap = (gapAdvertisingChannelMapFlags_t) (gAdvChanMapFlag37_c | gAdvChanMapFlag38_c | gAdvChanMapFlag39_c); + adv_params.filterPolicy = gProcessAll_c; + + err = blekw_start_advertising(&adv_params, &adv, &scanRsp); + if (err == BLE_OK) + { + ChipLogProgress(DeviceLayer, "Started Advertising at %d ms", advInterval); + } + else + { + ChipLogProgress(DeviceLayer, "Advertising error 0x%x!", err); + mFlags.Clear(Flags::kAdvertising); + return CHIP_ERROR_INCORRECT_STATE; + } + +exit: + return chipErr; +} + +#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING +CHIP_ERROR BLEManagerCommon::EncodeAdditionalDataTlv() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + BitFlags dataFields; + AdditionalDataPayloadGeneratorParams params; + +#if CHIP_ENABLE_ROTATING_DEVICE_ID && defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID) + uint8_t rotatingDeviceIdUniqueId[ConfigurationManager::kRotatingDeviceIDUniqueIDLength] = {}; + MutableByteSpan rotatingDeviceIdUniqueIdSpan(rotatingDeviceIdUniqueId); + + err = DeviceLayer::GetDeviceInstanceInfoProvider()->GetRotatingDeviceIdUniqueId(rotatingDeviceIdUniqueIdSpan); + SuccessOrExit(err); + err = ConfigurationMgr().GetLifetimeCounter(params.rotatingDeviceIdLifetimeCounter); + SuccessOrExit(err); + params.rotatingDeviceIdUniqueId = rotatingDeviceIdUniqueIdSpan; + dataFields.Set(AdditionalDataFields::RotatingDeviceId); +#endif /* CHIP_ENABLE_ROTATING_DEVICE_ID && defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID) */ + err = AdditionalDataPayloadGenerator().generateAdditionalDataPayload(params, sImplInstance->c3AdditionalDataBufferHandle, + dataFields); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to generate TLV encoded Additional Data (%s)", __func__); + } + + return err; +} + +void BLEManagerCommon::HandleC3ReadRequest(blekw_msg_t * msg) +{ + bleResult_t result; + blekw_att_read_data_t * att_rd_data = (blekw_att_read_data_t *) msg->data.data; + deviceId_t deviceId = att_rd_data->device_id; + uint16_t handle = att_rd_data->handle; + uint16_t length = sImplInstance->c3AdditionalDataBufferHandle->DataLength(); + const uint8_t * data = (const uint8_t *) sImplInstance->c3AdditionalDataBufferHandle->Start(); + + result = GattDb_WriteAttribute(handle, length, data); + if (result != gBleSuccess_c) + { + ChipLogError(DeviceLayer, "Failed to write C3 characteristic: %d", result); + } + + result = GattServer_SendAttributeReadStatus(deviceId, handle, gAttErrCodeNoError_c); + if (result != gBleSuccess_c) + { + ChipLogError(DeviceLayer, "Failed to send response to C3 read request: %d", result); + } +} +#endif /* CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING */ + +CHIP_ERROR BLEManagerCommon::StartAdvertising(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + mFlags.Set(Flags::kAdvertising); + mFlags.Clear(Flags::kRestartAdvertising); + + if (mFlags.Has(Flags::kFastAdvertisingEnabled)) + { + StartBleAdvTimeoutTimer(CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_TIMEOUT); + } + + err = ConfigureAdvertisingData(); + + if (err == CHIP_NO_ERROR) + /* schedule NFC emulation stop */ + { + ChipDeviceEvent advChange; + advChange.Type = DeviceEventType::kCHIPoBLEAdvertisingChange; + advChange.CHIPoBLEAdvertisingChange.Result = kActivity_Started; + err = PlatformMgr().PostEvent(&advChange); + } + + return err; +} + +CHIP_ERROR BLEManagerCommon::StopAdvertising(void) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + if (mFlags.Has(Flags::kAdvertising)) + { + mFlags.Clear(Flags::kAdvertising); + mFlags.Clear(Flags::kRestartAdvertising); + + if (!mDeviceConnected) + { + ble_err_t err = blekw_stop_advertising(); + VerifyOrReturnError(err == BLE_OK, CHIP_ERROR_INCORRECT_STATE); + CancelBleAdvTimeoutTimer(); + } + +#if CONFIG_CHIP_NFC_COMMISSIONING + /* schedule NFC emulation stop */ + ChipDeviceEvent advChange; + advChange.Type = DeviceEventType::kCHIPoBLEAdvertisingChange; + advChange.CHIPoBLEAdvertisingChange.Result = kActivity_Stopped; + error = PlatformMgr().PostEvent(&advChange); +#endif + } + + return error; +} + +void BLEManagerCommon::DriveBLEState(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Check if BLE stack is initialized + VerifyOrExit(mFlags.Has(Flags::kK32WBLEStackInitialized), err = CHIP_ERROR_INCORRECT_STATE); + + // Start advertising if needed... + if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && mFlags.Has(Flags::kAdvertisingEnabled)) + { + // Start/re-start advertising if not already started, or if there is a pending change + // to the advertising configuration. + if (!mFlags.Has(Flags::kAdvertising) || mFlags.Has(Flags::kRestartAdvertising)) + { + err = StartAdvertising(); + SuccessOrExit(err); + } + } + // Otherwise, stop advertising if it is enabled. + else if (mFlags.Has(Flags::kAdvertising)) + { + err = StopAdvertising(); + SuccessOrExit(err); + // Reset to fast advertising mode only if SetBLEAdvertisingEnabled(false) was called (usually from app). + mFlags.Set(Flags::kFastAdvertisingEnabled); + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); + mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; + } +} + +void BLEManagerCommon::DriveBLEState(intptr_t arg) +{ + sImplInstance->DriveBLEState(); +} + +void BLEManagerCommon::StopAdvertisingPriorToSwitchingMode(intptr_t arg) +{ + if (CHIP_NO_ERROR != sImplInstance->StopAdvertising()) + { + ChipLogProgress(DeviceLayer, "Failed to stop advertising"); + } +} + +void BLEManagerCommon::DoBleProcessing(void) +{ + blekw_msg_t * msg = NULL; + + while ((xQueueReceive(sBleEventQueue, &msg, 0) == pdTRUE) && msg) + { + if (msg->type == BLE_KW_MSG_ERROR) + { + if (msg->data.u8 == BLE_KW_MSG_2M_UPGRADE_ERROR) + { + ChipLogProgress(DeviceLayer, + "Warning. BLE is using 1Mbps. Couldn't upgrade to 2Mbps, " + "maybe the peer is missing 2Mbps support."); + } + else + { + ChipLogProgress(DeviceLayer, "BLE Error: %d.\n", msg->data.u8); + } + } + else if (msg->type == BLE_KW_MSG_CONNECTED) + { + sImplInstance->HandleConnectEvent(msg); + } + else if (msg->type == BLE_KW_MSG_DISCONNECTED) + { + sImplInstance->HandleConnectionCloseEvent(msg); + } + else if (msg->type == BLE_KW_MSG_MTU_CHANGED) + { + blekw_start_connection_timeout(); + ChipLogProgress(DeviceLayer, "BLE MTU size has been changed to %d.", msg->data.u16); + } + else if (msg->type == BLE_KW_MSG_ATT_WRITTEN || msg->type == BLE_KW_MSG_ATT_LONG_WRITTEN || + msg->type == BLE_KW_MSG_ATT_CCCD_WRITTEN) + { + sImplInstance->HandleWriteEvent(msg); + } + else if (msg->type == BLE_KW_MSG_ATT_READ) + { +#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING + blekw_att_read_data_t * att_rd_data = (blekw_att_read_data_t *) msg->data.data; + if (value_chipoble_c3 == att_rd_data->handle) + sImplInstance->HandleC3ReadRequest(msg); +#endif + } + else if (msg->type == BLE_KW_MSG_FORCE_DISCONNECT) + { + sImplInstance->HandleForceDisconnect(); + } + + /* Free the message from the queue */ + free(msg); + msg = NULL; + } +} + +void BLEManagerCommon::RegisterAppCallbacks(BLECallbackDelegate::GapGenericCallback gapCallback, + BLECallbackDelegate::GattServerCallback gattCallback) +{ + callbackDelegate.gapCallback = gapCallback; + callbackDelegate.gattCallback = gattCallback; +} + +void BLEManagerCommon::HandleConnectEvent(blekw_msg_t * msg) +{ + uint8_t deviceId = msg->data.u8; + ChipLogProgress(DeviceLayer, "BLE is connected with device: %d.\n", deviceId); + +#if gClkUseFro32K && defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_DisallowDeviceToSleep(); +#endif + + mDeviceId = deviceId; + mDeviceConnected = true; + + blekw_start_connection_timeout(); + PlatformMgr().ScheduleWork(DriveBLEState, 0); +} + +void BLEManagerCommon::HandleConnectionCloseEvent(blekw_msg_t * msg) +{ + uint8_t deviceId = msg->data.u8; + ChipLogProgress(DeviceLayer, "BLE is disconnected with device: %d.\n", deviceId); + +#if gClkUseFro32K && defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_AllowDeviceToSleep(); +#endif + + mDeviceConnected = false; + + ChipDeviceEvent event; + event.Type = DeviceEventType::kCHIPoBLEConnectionClosed; + event.CHIPoBLEConnectionError.ConId = deviceId; + event.CHIPoBLEConnectionError.Reason = BLE_ERROR_REMOTE_DEVICE_DISCONNECTED; + + CancelBleAdvTimeoutTimer(); + + PlatformMgr().PostEventOrDie(&event); + mFlags.Set(Flags::kRestartAdvertising); + mFlags.Set(Flags::kFastAdvertisingEnabled); + PlatformMgr().ScheduleWork(DriveBLEState, 0); +} + +void BLEManagerCommon::HandleWriteEvent(blekw_msg_t * msg) +{ + blekw_att_written_data_t * att_wr_data = (blekw_att_written_data_t *) msg->data.data; + attErrorCode_t status = gAttErrCodeNoError_c; + +#if CHIP_DEVICE_CHIP0BLE_DEBUG + ChipLogProgress(DeviceLayer, "Attribute write request(device: %d,handle: %d).", att_wr_data->device_id, att_wr_data->handle); +#endif + + blekw_start_connection_timeout(); + + if (value_chipoble_rx == att_wr_data->handle) + { + sImplInstance->HandleRXCharWrite(msg); + } + else if (cccd_chipoble_tx == att_wr_data->handle) + { + sImplInstance->HandleTXCharCCCDWrite(msg); + } + + /* TODO: do we need to send the status also for CCCD_WRITTEN? */ + if (msg->type != BLE_KW_MSG_ATT_CCCD_WRITTEN) + { + bleResult_t res = GattServer_SendAttributeWrittenStatus(att_wr_data->device_id, att_wr_data->handle, status); + + if (res != gBleSuccess_c) + { + ChipLogProgress(DeviceLayer, "GattServer_SendAttributeWrittenStatus returned %d", res); + } + } +} + +void BLEManagerCommon::HandleTXCharCCCDWrite(blekw_msg_t * msg) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + blekw_att_written_data_t * att_wr_data = (blekw_att_written_data_t *) msg->data.data; + ChipDeviceEvent event; + + VerifyOrExit(att_wr_data->length != 0, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(att_wr_data->data != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + +#if CHIP_DEVICE_CHIP0BLE_DEBUG + ChipLogProgress(DeviceLayer, "CHIPoBLE %s received", *att_wr_data->data ? "subscribe" : "unsubscribe"); +#endif + + if (*att_wr_data->data) + { + if (!mDeviceSubscribed) + { + mDeviceSubscribed = true; + event.Type = DeviceEventType::kCHIPoBLESubscribe; + event.CHIPoBLESubscribe.ConId = att_wr_data->device_id; + err = PlatformMgr().PostEvent(&event); + } + } + else + { + mDeviceSubscribed = false; + event.Type = DeviceEventType::kCHIPoBLEUnsubscribe; + event.CHIPoBLESubscribe.ConId = att_wr_data->device_id; + err = PlatformMgr().PostEvent(&event); + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "HandleTXCharCCCDWrite() failed: %s", ErrorStr(err)); + } +} + +void BLEManagerCommon::HandleRXCharWrite(blekw_msg_t * msg) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + System::PacketBufferHandle buf; + blekw_att_written_data_t * att_wr_data = (blekw_att_written_data_t *) msg->data.data; + + VerifyOrExit(att_wr_data->length != 0, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(att_wr_data->data != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + // Copy the data to a PacketBuffer. + buf = System::PacketBufferHandle::NewWithData(att_wr_data->data, att_wr_data->length); + VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY); + +#if CHIP_DEVICE_CHIP0BLE_DEBUG + ChipLogDetail(DeviceLayer, + "Write request/command received for" + "CHIPoBLE RX characteristic (con %u, len %u)", + att_wr_data->device_id, buf->DataLength()); +#endif + + // Post an event to the CHIP queue to deliver the data into the CHIP stack. + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kCHIPoBLEWriteReceived; + event.CHIPoBLEWriteReceived.ConId = att_wr_data->device_id; + event.CHIPoBLEWriteReceived.Data = std::move(buf).UnsafeRelease(); + err = PlatformMgr().PostEvent(&event); + } +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "HandleRXCharWrite() failed: %s", ErrorStr(err)); + } +} + +void BLEManagerCommon::HandleForceDisconnect() +{ + ChipLogProgress(DeviceLayer, "BLE connection timeout: Forcing disconnection."); + + /* Set the advertising parameters */ + if (Gap_Disconnect(mDeviceId) != gBleSuccess_c) + { + ChipLogProgress(DeviceLayer, "Gap_Disconnect() failed."); + } + +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_AllowDeviceToSleep(); +#endif +} + +/******************************************************************************* + * BLE stack callbacks + *******************************************************************************/ +void BLEManagerCommon::blekw_generic_cb(gapGenericEvent_t * pGenericEvent) +{ + /* Call BLE Conn Manager */ + BleConnManager_GenericEvent(pGenericEvent); + + if (sImplInstance && sImplInstance->callbackDelegate.gapCallback) + { + sImplInstance->callbackDelegate.gapCallback(pGenericEvent); + } + + switch (pGenericEvent->eventType) + { + case gInternalError_c: + /* Notify the CHIP that the BLE hardware report fail */ + ChipLogProgress(DeviceLayer, "BLE Internal Error: Code 0x%04X, Source 0x%08X, HCI OpCode %d.\n", + pGenericEvent->eventData.internalError.errorCode, pGenericEvent->eventData.internalError.errorSource, + pGenericEvent->eventData.internalError.hciCommandOpcode); + if ((gHciUnsupportedRemoteFeature_c == pGenericEvent->eventData.internalError.errorCode) && + (gLeSetPhy_c == pGenericEvent->eventData.internalError.errorSource)) + { + (void) blekw_msg_add_u8(BLE_KW_MSG_ERROR, BLE_KW_MSG_2M_UPGRADE_ERROR); + } + else + { + (void) blekw_msg_add_u8(BLE_KW_MSG_ERROR, BLE_INTERNAL_ERROR); + } + break; + + case gAdvertisingSetupFailed_c: + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_SETUP_FAILED); + break; + + case gAdvertisingParametersSetupComplete_c: + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_PAR_SETUP_COMPLETE); + break; + + case gAdvertisingDataSetupComplete_c: + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_DAT_SETUP_COMPLETE); + break; + + case gRandomAddressReady_c: + Gap_SetRandomAddress(pGenericEvent->eventData.addrReady.aAddress); + break; + + case gRandomAddressSet_c: + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_RND_ADDR_SET); + break; + +#if BLE_HIGH_TX_POWER + case gTxPowerLevelSetComplete_c: + if (gBleSuccess_c == pGenericEvent->eventData.txPowerLevelSetStatus) + { + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_POWER_LEVEL_SET); + } + break; +#endif + + case gInitializationComplete_c: + /* Common GAP configuration */ + BleConnManager_GapCommonConfig(); + + /* Set the local synchronization event */ + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_INIT_COMPLETE); + break; + default: + break; + } +} + +void BLEManagerCommon::blekw_gap_advertising_cb(gapAdvertisingEvent_t * pAdvertisingEvent) +{ + if (pAdvertisingEvent->eventType == gAdvertisingStateChanged_c) + { + /* Set the local synchronization event */ + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_CHANGED); + } + else + { + /* The advertisement start failed */ + ChipLogProgress(DeviceLayer, "Advertising failed: event=%d reason=0x%04X\n", pAdvertisingEvent->eventType, + pAdvertisingEvent->eventData.failReason); + + /* Set the local synchronization event */ + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_ADV_FAILED); + } +} + +void BLEManagerCommon::blekw_gap_connection_cb(deviceId_t deviceId, gapConnectionEvent_t * pConnectionEvent) +{ + /* Call BLE Conn Manager */ + BleConnManager_GapPeripheralEvent(deviceId, pConnectionEvent); + + if (pConnectionEvent->eventType == gConnEvtConnected_c) + { +#if CHIP_DEVICE_K32W1 +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + /* Disallow must be called here for K32W1, otherwise an assert will be reached. + * Disclaimer: this is a workaround until a better cross platform solution is found. */ + PWR_DisallowDeviceToSleep(); +#endif +#endif + +#if CHIP_DEVICE_CONFIG_BLE_SET_PHY_2M_REQ + ChipLogProgress(DeviceLayer, "BLE K32W: Trying to set the PHY to 2M"); + + (void) Gap_LeSetPhy(FALSE, deviceId, 0, gConnPhyUpdateReqTxPhySettings_c, gConnPhyUpdateReqRxPhySettings_c, + (uint16_t) gConnPhyUpdateReqPhyOptions_c); +#endif + + /* Notify App Task that the BLE is connected now */ + (void) blekw_msg_add_u8(BLE_KW_MSG_CONNECTED, (uint8_t) deviceId); +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_AllowDeviceToSleep(); +#endif + } + else if (pConnectionEvent->eventType == gConnEvtDisconnected_c) + { + blekw_stop_connection_timeout(); + + /* Notify App Task that the BLE is disconnected now */ + (void) blekw_msg_add_u8(BLE_KW_MSG_DISCONNECTED, (uint8_t) deviceId); + +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + if (bleAppStopInProgress == TRUE) + { + bleAppStopInProgress = FALSE; + PWR_AllowDeviceToSleep(); + } +#endif + } + else if (pConnectionEvent->eventType == gConnEvtPairingRequest_c) + { + /* Reject request for pairing */ + Gap_RejectPairing(deviceId, gPairingNotSupported_c); + } + else if (pConnectionEvent->eventType == gConnEvtAuthenticationRejected_c) + { + ChipLogProgress(DeviceLayer, "BLE Authentication rejected (reason:%d).\n", + pConnectionEvent->eventData.authenticationRejectedEvent.rejectReason); + } +} + +void BLEManagerCommon::blekw_connection_timeout_cb(TimerHandle_t timer) +{ + (void) blekw_msg_add_u8(BLE_KW_MSG_FORCE_DISCONNECT, 0); +} + +void BLEManagerCommon::blekw_start_connection_timeout(void) +{ + xTimerReset(connectionTimeout, 0); +} + +void BLEManagerCommon::blekw_stop_connection_timeout(void) +{ + ChipLogProgress(DeviceLayer, "Stopped connectionTimeout timer."); + xTimerStop(connectionTimeout, 0); +} + +void BLEManagerCommon::blekw_gatt_server_cb(deviceId_t deviceId, gattServerEvent_t * pServerEvent) +{ + if (sImplInstance && sImplInstance->callbackDelegate.gattCallback) + { + sImplInstance->callbackDelegate.gattCallback(deviceId, pServerEvent); + } + + switch (pServerEvent->eventType) + { + case gEvtMtuChanged_c: { + uint16_t tempMtu = 0; + + (void) Gatt_GetMtu(deviceId, &tempMtu); + blekw_msg_add_u16(BLE_KW_MSG_MTU_CHANGED, tempMtu); + break; + } + + case gEvtAttributeWritten_c: + blekw_msg_add_att_written(BLE_KW_MSG_ATT_WRITTEN, deviceId, pServerEvent->eventData.attributeWrittenEvent.handle, + pServerEvent->eventData.attributeWrittenEvent.aValue, + pServerEvent->eventData.attributeWrittenEvent.cValueLength); + break; + + case gEvtLongCharacteristicWritten_c: + blekw_msg_add_att_written(BLE_KW_MSG_ATT_LONG_WRITTEN, deviceId, pServerEvent->eventData.longCharWrittenEvent.handle, + pServerEvent->eventData.longCharWrittenEvent.aValue, + pServerEvent->eventData.longCharWrittenEvent.cValueLength); + break; + + case gEvtAttributeRead_c: + blekw_msg_add_att_read(BLE_KW_MSG_ATT_READ, deviceId, pServerEvent->eventData.attributeReadEvent.handle); + break; + + case gEvtCharacteristicCccdWritten_c: { + uint16_t cccd_val = pServerEvent->eventData.charCccdWrittenEvent.newCccd; + + blekw_msg_add_att_written(BLE_KW_MSG_ATT_CCCD_WRITTEN, deviceId, pServerEvent->eventData.charCccdWrittenEvent.handle, + (uint8_t *) &cccd_val, 2); + break; + } + + case gEvtHandleValueConfirmation_c: + /* Set the local synchronization event */ + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_INDICATION_CONFIRMED); + break; + + case gEvtError_c: + if (pServerEvent->eventData.procedureError.procedureType == gSendIndication_c) + { + /* Set the local synchronization event */ + xEventGroupSetBits(sEventGroup, CHIP_BLE_KW_EVNT_INDICATION_FAILED); + } + else + { + ChipLogProgress(DeviceLayer, "BLE Gatt Server Error: Code 0x%04X, Source %d.\n", + pServerEvent->eventData.procedureError.error, pServerEvent->eventData.procedureError.procedureType); + + /* Notify CHIP BLE App Task that the BLE hardware report fail */ + (void) blekw_msg_add_u8(BLE_KW_MSG_ERROR, BLE_INTERNAL_GATT_ERROR); + } + break; + + default: + break; + } +} +/******************************************************************************* + * Add to message queue functions + *******************************************************************************/ +CHIP_ERROR BLEManagerCommon::blekw_msg_add_att_written(blekw_msg_type_t type, uint8_t device_id, uint16_t handle, uint8_t * data, + uint16_t length) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + blekw_msg_t * msg = NULL; + blekw_att_written_data_t * att_wr_data; + + /* Allocate a buffer with enough space to store the packet */ + msg = (blekw_msg_t *) malloc(sizeof(blekw_msg_t) + sizeof(blekw_att_written_data_t) + length); + VerifyOrExit(msg, err = CHIP_ERROR_NO_MEMORY); + + msg->type = type; + msg->length = sizeof(blekw_att_written_data_t) + length; + att_wr_data = (blekw_att_written_data_t *) msg->data.data; + att_wr_data->device_id = device_id; + att_wr_data->handle = handle; + att_wr_data->length = length; + FLib_MemCpy(att_wr_data->data, data, length); + + VerifyOrExit(xQueueSend(sBleEventQueue, &msg, 0) == pdTRUE, err = CHIP_ERROR_NO_MEMORY); + otTaskletsSignalPending(NULL); + +exit: + return err; +} + +CHIP_ERROR BLEManagerCommon::blekw_msg_add_att_read(blekw_msg_type_t type, uint8_t device_id, uint16_t handle) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + blekw_msg_t * msg = NULL; + blekw_att_read_data_t * att_rd_data; + + /* Allocate a buffer with enough space to store the packet */ + msg = (blekw_msg_t *) malloc(sizeof(blekw_msg_t) + sizeof(blekw_att_read_data_t)); + VerifyOrExit(msg, err = CHIP_ERROR_NO_MEMORY); + + msg->type = type; + msg->length = sizeof(blekw_att_read_data_t); + att_rd_data = (blekw_att_read_data_t *) msg->data.data; + att_rd_data->device_id = device_id; + att_rd_data->handle = handle; + + VerifyOrExit(xQueueSend(sBleEventQueue, &msg, 0) == pdTRUE, err = CHIP_ERROR_NO_MEMORY); + otTaskletsSignalPending(NULL); + +exit: + return err; +} + +CHIP_ERROR BLEManagerCommon::blekw_msg_add_u8(blekw_msg_type_t type, uint8_t data) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + blekw_msg_t * msg = NULL; + + /* Allocate a buffer with enough space to store the packet */ + msg = (blekw_msg_t *) malloc(sizeof(blekw_msg_t)); + VerifyOrExit(msg, err = CHIP_ERROR_NO_MEMORY); + + msg->type = type; + msg->length = 0; + msg->data.u8 = data; + + VerifyOrExit(xQueueSend(sBleEventQueue, &msg, 0) == pdTRUE, err = CHIP_ERROR_NO_MEMORY); + otTaskletsSignalPending(NULL); + +exit: + return err; +} + +CHIP_ERROR BLEManagerCommon::blekw_msg_add_u16(blekw_msg_type_t type, uint16_t data) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + blekw_msg_t * msg = NULL; + + /* Allocate a buffer with enough space to store the packet */ + msg = (blekw_msg_t *) malloc(sizeof(blekw_msg_t)); + VerifyOrExit(msg, err = CHIP_ERROR_NO_MEMORY); + + msg->type = type; + msg->length = 0; + msg->data.u16 = data; + + VerifyOrExit(xQueueSend(sBleEventQueue, &msg, 0) == pdTRUE, err = CHIP_ERROR_NO_MEMORY); + otTaskletsSignalPending(NULL); + +exit: + return err; +} + +void BLEManagerCommon::BleAdvTimeoutHandler(TimerHandle_t xTimer) +{ + if (BLEMgrImpl().mFlags.Has(Flags::kFastAdvertisingEnabled)) + { + ChipLogDetail(DeviceLayer, "Start slow advertisement"); + BLEMgr().SetAdvertisingMode(BLEAdvertisingMode::kSlowAdvertising); + } +} + +void BLEManagerCommon::CancelBleAdvTimeoutTimer(void) +{ + if (xTimerStop(sbleAdvTimeoutTimer, 0) == pdFAIL) + { + ChipLogError(DeviceLayer, "Failed to stop BledAdv timeout timer"); + } +} + +void BLEManagerCommon::StartBleAdvTimeoutTimer(uint32_t aTimeoutInMs) +{ + if (xTimerIsTimerActive(sbleAdvTimeoutTimer)) + { + CancelBleAdvTimeoutTimer(); + } + + // 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(sbleAdvTimeoutTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS) + { + ChipLogError(DeviceLayer, "Failed to start BledAdv timeout timer"); + } +} + +CHIP_ERROR BLEManagerCommon::blekw_stop_connection_internal(BLE_CONNECTION_OBJECT conId) +{ + ChipLogProgress(DeviceLayer, "Closing BLE GATT connection (con %u)", conId); + + if (Gap_Disconnect(conId) != gBleSuccess_c) + { + ChipLogProgress(DeviceLayer, "Gap_Disconnect() failed."); + return CHIP_ERROR_INTERNAL; + } +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + else + { + bleAppStopInProgress = TRUE; + PWR_DisallowDeviceToSleep(); + } +#endif + + return CHIP_NO_ERROR; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip +#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE diff --git a/src/platform/nxp/common/legacy/BLEManagerCommon.h b/src/platform/nxp/common/legacy/BLEManagerCommon.h new file mode 100644 index 00000000000000..b7fc1275d2501f --- /dev/null +++ b/src/platform/nxp/common/legacy/BLEManagerCommon.h @@ -0,0 +1,249 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2020 Nest Labs, Inc. + * 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. + */ + +/** + * @file + * Provides an implementation of the BLEManager singleton object + * for the K32W platforms. + */ + +#pragma once + +#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE + +#include "fsl_os_abstraction.h" + +#include "ble_conn_manager.h" +#include "ble_general.h" +#include "ble_host_task_config.h" +#include "ble_host_tasks.h" +#include "gap_interface.h" +#include "gatt_db_dynamic.h" +#include "gatt_server_interface.h" + +#include "FreeRTOS.h" +#include "event_groups.h" +#include "timers.h" + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +using namespace chip::Ble; + +/** + * A delegate class that can be used by the application to subscribe to BLE events. + */ +struct BLECallbackDelegate +{ + using GapGenericCallback = void (*)(gapGenericEvent_t * event); + using GattServerCallback = void (*)(deviceId_t id, gattServerEvent_t * event); + + GapGenericCallback gapCallback = nullptr; + GattServerCallback gattCallback = nullptr; +}; + +/** + * Base class for different platform implementations (K32W0 and K32W1 for now). + */ +class BLEManagerCommon : public BLEManager, protected BleLayer, private BlePlatformDelegate, private BleApplicationDelegate +{ +protected: + // ===== Members that implement the BLEManager internal interface. + + CHIP_ERROR _Init(void); + CHIP_ERROR _Shutdown() { return CHIP_NO_ERROR; } + CHIPoBLEServiceMode _GetCHIPoBLEServiceMode(void); + CHIP_ERROR _SetCHIPoBLEServiceMode(CHIPoBLEServiceMode val); + bool _IsAdvertisingEnabled(void); + CHIP_ERROR _SetAdvertisingEnabled(bool val); + bool _IsAdvertising(void); + CHIP_ERROR _SetAdvertisingMode(BLEAdvertisingMode mode); + CHIP_ERROR _GetDeviceName(char * buf, size_t bufSize); + CHIP_ERROR _SetDeviceName(const char * deviceName); + uint16_t _NumConnections(void); + void _OnPlatformEvent(const ChipDeviceEvent * event); + + // ===== Members that implement virtual methods on BlePlatformDelegate. + + CHIP_ERROR SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, + const Ble::ChipBleUUID * charId) override; + CHIP_ERROR UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, + const Ble::ChipBleUUID * charId) override; + CHIP_ERROR CloseConnection(BLE_CONNECTION_OBJECT conId) override; + uint16_t GetMTU(BLE_CONNECTION_OBJECT conId) const override; + CHIP_ERROR SendIndication(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId, + System::PacketBufferHandle pBuf) override; + CHIP_ERROR SendWriteRequest(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId, + System::PacketBufferHandle pBuf) override; + + // ===== Members that implement virtual methods on BleApplicationDelegate. + + void NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) override; + + // ===== Private members reserved for use by this class only. + + enum class Flags : uint8_t + { + kAdvertisingEnabled = 0x0001, + kFastAdvertisingEnabled = 0x0002, + kAdvertising = 0x0004, + kRestartAdvertising = 0x0008, + kK32WBLEStackInitialized = 0x0010, + kDeviceNameSet = 0x0020, + }; + BitFlags mFlags; + + enum + { + kMaxDeviceNameLength = 16, + kUnusedIndex = 0xFF, + }; + + typedef enum + { + BLE_KW_MSG_ERROR = 0x01, + BLE_KW_MSG_CONNECTED, + BLE_KW_MSG_DISCONNECTED, + BLE_KW_MSG_MTU_CHANGED, + BLE_KW_MSG_ATT_WRITTEN, + BLE_KW_MSG_ATT_LONG_WRITTEN, + BLE_KW_MSG_ATT_READ, + BLE_KW_MSG_ATT_CCCD_WRITTEN, + BLE_KW_MSG_FORCE_DISCONNECT, + } blekw_msg_type_t; + + typedef struct hk_ble_kw_msg_s + { + blekw_msg_type_t type; + uint16_t length; + union + { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint8_t data[1]; + char * str; + } data; + } blekw_msg_t; + + typedef enum ble_err_t + { + BLE_OK = 0, + BLE_INTERNAL_GATT_ERROR, + BLE_E_SET_ADV_PARAMS, + BLE_E_ADV_PARAMS_FAILED, + BLE_E_SET_ADV_DATA, + BLE_E_ADV_CHANGED, + BLE_E_ADV_FAILED, + BLE_E_ADV_SETUP_FAILED, + BLE_E_START_ADV, + BLE_E_STOP, + BLE_E_FAIL, + BLE_E_START_ADV_FAILED, + BLE_KW_MSG_2M_UPGRADE_ERROR, + BLE_INTERNAL_ERROR, + } ble_err_t; + + typedef struct ble_att_written_data_s + { + uint8_t device_id; + uint16_t handle; + uint16_t length; + uint8_t data[1]; + } blekw_att_written_data_t; + + typedef struct hk_ble_att_read_data_s + { + uint8_t device_id; + uint16_t handle; + } blekw_att_read_data_t; + + CHIPoBLEServiceMode mServiceMode; + char mDeviceName[kMaxDeviceNameLength + 1]; +#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING + chip::System::PacketBufferHandle c3AdditionalDataBufferHandle; +#endif + uint8_t mDeviceId; + bool mDeviceSubscribed = false; + bool mDeviceConnected = false; + + void DriveBLEState(void); + CHIP_ERROR ConfigureAdvertising(void); + CHIP_ERROR ConfigureAdvertisingData(void); + CHIP_ERROR StartAdvertising(void); + CHIP_ERROR StopAdvertising(void); + + void HandleConnectEvent(blekw_msg_t * msg); + void HandleConnectionCloseEvent(blekw_msg_t * msg); + void HandleWriteEvent(blekw_msg_t * msg); + void HandleRXCharWrite(blekw_msg_t * msg); + void HandleTXCharCCCDWrite(blekw_msg_t * msg); + void HandleForceDisconnect(); + +#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING + CHIP_ERROR EncodeAdditionalDataTlv(); + void HandleC3ReadRequest(blekw_msg_t * msg); +#endif + BLEManagerCommon::ble_err_t blekw_send_event(int8_t connection_handle, uint16_t handle, uint8_t * data, uint32_t len); + + static void DriveBLEState(intptr_t arg); + static void StopAdvertisingPriorToSwitchingMode(intptr_t arg); + static void BleAdvTimeoutHandler(TimerHandle_t xTimer); + static void CancelBleAdvTimeoutTimer(void); + static void StartBleAdvTimeoutTimer(uint32_t aTimeoutInMs); + + static void blekw_connection_timeout_cb(TimerHandle_t timer); + static void blekw_generic_cb(gapGenericEvent_t * pGenericEvent); + static void blekw_gatt_server_cb(deviceId_t deviceId, gattServerEvent_t * pServerEvent); + static CHIP_ERROR blekw_msg_add_u8(blekw_msg_type_t type, uint8_t data); + static CHIP_ERROR blekw_msg_add_u16(blekw_msg_type_t type, uint16_t data); + static CHIP_ERROR blekw_msg_add_att_written(blekw_msg_type_t type, uint8_t device_id, uint16_t handle, uint8_t * data, + uint16_t length); + static CHIP_ERROR blekw_msg_add_att_read(blekw_msg_type_t type, uint8_t device_id, uint16_t handle); + static BLEManagerCommon::ble_err_t blekw_start_advertising(gapAdvertisingParameters_t * adv_params, gapAdvertisingData_t * adv, + gapScanResponseData_t * scnrsp); + static BLEManagerCommon::ble_err_t blekw_stop_advertising(void); + static void blekw_gap_advertising_cb(gapAdvertisingEvent_t * pAdvertisingEvent); + static void blekw_gap_connection_cb(deviceId_t deviceId, gapConnectionEvent_t * pConnectionEvent); + static void blekw_start_connection_timeout(void); + static void blekw_stop_connection_timeout(void); + static CHIP_ERROR blekw_stop_connection_internal(BLE_CONNECTION_OBJECT conId); + +public: + virtual CHIP_ERROR InitHostController(BLECallbackDelegate::GapGenericCallback cb_fp) = 0; + virtual BLEManagerCommon * GetImplInstance() = 0; + virtual CHIP_ERROR ResetController() { return CHIP_NO_ERROR; } + void DoBleProcessing(void); + + BLECallbackDelegate callbackDelegate; + void RegisterAppCallbacks(BLECallbackDelegate::GapGenericCallback gapCallback, + BLECallbackDelegate::GattServerCallback gattCallback); +}; + +inline BLEManager::CHIPoBLEServiceMode BLEManagerCommon::_GetCHIPoBLEServiceMode(void) +{ + return mServiceMode; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE diff --git a/src/platform/nxp/common/legacy/FactoryDataDriver.cpp b/src/platform/nxp/common/legacy/FactoryDataDriver.cpp new file mode 100644 index 00000000000000..6ddbedc3ca35c8 --- /dev/null +++ b/src/platform/nxp/common/legacy/FactoryDataDriver.cpp @@ -0,0 +1,76 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * 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 +#include +#include +#include + +namespace chip { +namespace DeviceLayer { + +FactoryDataDriver::~FactoryDataDriver() {} + +CHIP_ERROR FactoryDataDriver::UpdateValueInRam(uint8_t tag, ByteSpan & newValue) +{ + uint16_t oldLength = 0; + uint16_t newLength = newValue.size(); + uint32_t offset = 0; + uint8_t * factoryData = mFactoryDataRamBuff; + FactoryDataProvider::Header * header = (FactoryDataProvider::Header *) factoryData; + uint8_t * data = factoryData + sizeof(FactoryDataProvider::Header); + + while (offset < header->size) + { + memcpy(&oldLength, &data[offset + FactoryDataProvider::kLengthOffset], sizeof(oldLength)); + + if (tag != data[offset]) + { + offset += FactoryDataProvider::kValueOffset + oldLength; + continue; + } + + if (oldLength == newLength) + { + memcpy(&data[offset + FactoryDataProvider::kValueOffset], newValue.data(), newLength); + } + else + { + uint32_t oldEndOffset = offset + FactoryDataProvider::kValueOffset + oldLength; + + memcpy(&data[offset + FactoryDataProvider::kLengthOffset], &newLength, sizeof(newLength)); + memmove(&data[offset + FactoryDataProvider::kValueOffset + newLength], &data[oldEndOffset], + header->size - oldEndOffset); + memcpy(&data[offset + FactoryDataProvider::kValueOffset], newValue.data(), newLength); + } + + header->size = header->size - oldLength + newLength; + + uint8_t sha256Output[SHA256_HASH_SIZE] = { 0 }; + SHA256_Hash(data, header->size, sha256Output); + memcpy(header->hash, sha256Output, sizeof(header->hash)); + + ChipLogProgress(DeviceLayer, "Value at tag %d updated successfully.", tag); + return CHIP_NO_ERROR; + } + + ChipLogError(DeviceLayer, "Failed to find tag %d.", tag); + return CHIP_ERROR_NOT_FOUND; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nxp/common/legacy/FactoryDataDriver.h b/src/platform/nxp/common/legacy/FactoryDataDriver.h new file mode 100644 index 00000000000000..93eb43a183e23c --- /dev/null +++ b/src/platform/nxp/common/legacy/FactoryDataDriver.h @@ -0,0 +1,113 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * 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 + +namespace chip { +namespace DeviceLayer { + +// Forward declaration to define the getter for factory data provider impl instance +class FactoryDataDriverImpl; + +/** + * @brief This interface provides the functions that should be implemented + * to handle factory data update and factory data ram backup operations. + * This interface must be implemented by each platform. + */ + +class FactoryDataDriver +{ +public: + virtual ~FactoryDataDriver(); + + /*! + * \brief Initializes the FactoryDataDriver instance. + * + * @retval CHIP_NO_ERROR if operation was successful. + */ + virtual CHIP_ERROR Init() = 0; + + /*! + * \brief Checks whether the backup of the factory data exists (e.g in persistent storage). + * + * @retval true if backup exists otherwise return false. + */ + virtual bool DoesBackupExist(uint16_t * size) = 0; + + /*! + * \brief Deletes the backup of the factory data (e.g. from persistent storage). + * + * @retval CHIP_NO_ERROR if operation was successful. + */ + virtual CHIP_ERROR DeleteBackup(void) = 0; + + /*! + * \brief Allocates and initializes the factory data ram backup and copies + * factory data into it. + * + * @retval CHIP_NO_ERROR if operation was successful. + */ + virtual CHIP_ERROR InitRamBackup(void) = 0; + + /*! + * \brief Clear and deallocate the factory data ram backup. + * + * @retval CHIP_NO_ERROR if operation was successful. + */ + virtual CHIP_ERROR ClearRamBackup(void) = 0; + + /*! + * \brief Read the factory data from persistent storage into the factory data + * ram backup. + * + * @retval CHIP_NO_ERROR if operation was successful. + */ + virtual CHIP_ERROR ReadBackupInRam(void) = 0; + + /*! + * \brief Save / Backup the factory data into the persistent storage + * + * @retval CHIP_NO_ERROR if operation was successful. + */ + virtual CHIP_ERROR BackupFactoryData(void) = 0; + + /*! + * \brief Update / Write the factory data from the ram buffer + * + * @retval CHIP_NO_ERROR if operation was successful. + */ + virtual CHIP_ERROR UpdateFactoryData(void) = 0; + + /*! + * \brief Update TLV value from factory data based on tag + * @param tag TLV tag of component to update + * @param newValue Reference to the new value of the TLV component + * @retval CHIP_NO_ERROR if operation was successful. + */ + CHIP_ERROR UpdateValueInRam(uint8_t tag, ByteSpan & newValue); + +protected: + uint8_t * mFactoryDataRamBuff = nullptr; + uint32_t mSize = 0; + uint32_t mMaxSize = 0; +}; + +extern FactoryDataDriver & FactoryDataDrv(); + +extern FactoryDataDriverImpl & FactoryDataDrvImpl(); + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nxp/common/legacy/FactoryDataProvider.cpp b/src/platform/nxp/common/legacy/FactoryDataProvider.cpp new file mode 100644 index 00000000000000..7ecc29a54cf077 --- /dev/null +++ b/src/platform/nxp/common/legacy/FactoryDataProvider.cpp @@ -0,0 +1,442 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +#if (!CONFIG_CHIP_LOAD_REAL_FACTORY_DATA || !(defined CONFIG_CHIP_LOAD_REAL_FACTORY_DATA)) +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { + +static constexpr size_t kSpake2pSerializedVerifier_MaxBase64Len = + BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_VerifierSerialized_Length) + 1; +static constexpr size_t kSpake2pSalt_MaxBase64Len = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length) + 1; +/* Secure subsystem private key blob size is 32 + 24 = 56. + * DAC private key may be used to store an SSS exported blob instead of the private key. + */ +static constexpr size_t kDacPrivateKey_MaxLen = Crypto::kP256_PrivateKey_Length + 24; + +FactoryDataProvider::~FactoryDataProvider() {} + +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR + +CHIP_ERROR FactoryDataProvider::ValidateWithRestore() +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + VerifyOrReturnError(mRestoreMechanisms.size() > 0, CHIP_FACTORY_DATA_RESTORE_MECHANISM); + + for (auto & restore : mRestoreMechanisms) + { + error = restore(); + if (error != CHIP_NO_ERROR) + { + continue; + } + + error = Validate(); + if (error != CHIP_NO_ERROR) + { + continue; + } + + break; + } + + if (error == CHIP_NO_ERROR) + { + error = mFactoryDataDriver->DeleteBackup(); + } + + return error; +} + +void FactoryDataProvider::RegisterRestoreMechanism(RestoreMechanism restore) +{ + mRestoreMechanisms.insert(mRestoreMechanisms.end(), restore); +} + +#endif + +CHIP_ERROR FactoryDataProvider::SignWithDacKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + Crypto::P256ECDSASignature signature; + Crypto::P256Keypair keypair; + Crypto::P256SerializedKeypair serializedKeypair; + uint8_t keyBuf[Crypto::kP256_PrivateKey_Length]; + MutableByteSpan dacPrivateKeySpan(keyBuf); + uint16_t keySize = 0; + + VerifyOrExit(!outSignBuffer.empty(), error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(!messageToSign.empty(), error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(outSignBuffer.size() >= signature.Capacity(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + + /* Get private key of DAC certificate from reserved section */ + error = SearchForId(FactoryDataId::kDacPrivateKeyId, dacPrivateKeySpan.data(), dacPrivateKeySpan.size(), keySize); + SuccessOrExit(error); + dacPrivateKeySpan.reduce_size(keySize); + + /* Only the private key is used when signing */ + error = serializedKeypair.SetLength(Crypto::kP256_PublicKey_Length + dacPrivateKeySpan.size()); + SuccessOrExit(error); + memcpy(serializedKeypair.Bytes() + Crypto::kP256_PublicKey_Length, dacPrivateKeySpan.data(), dacPrivateKeySpan.size()); + + error = keypair.Deserialize(serializedKeypair); + SuccessOrExit(error); + + error = keypair.ECDSA_sign_msg(messageToSign.data(), messageToSign.size(), signature); + SuccessOrExit(error); + + error = CopySpanToMutableSpan(ByteSpan{ signature.ConstBytes(), signature.Length() }, outSignBuffer); + +exit: + /* Sanitize temporary buffer */ + memset(keyBuf, 0, Crypto::kP256_PrivateKey_Length); + return error; +} + +CHIP_ERROR FactoryDataProvider::Validate() +{ + uint8_t output[Crypto::kSHA256_Hash_Length] = { 0 }; + + memcpy(&mHeader, (void *) mConfig.start, sizeof(Header)); + ReturnErrorCodeIf(mHeader.hashId != kHashId, CHIP_FACTORY_DATA_HASH_ID); + + ReturnErrorOnFailure(Crypto::Hash_SHA256((uint8_t *) mConfig.payload, mHeader.size, output)); + ReturnErrorCodeIf(memcmp(output, mHeader.hash, kHashLen) != 0, CHIP_FACTORY_DATA_SHA_CHECK); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::SearchForId(uint8_t searchedType, uint8_t * pBuf, size_t bufLength, uint16_t & length, + uint32_t * offset) +{ + uint32_t addr = mConfig.payload; + uint8_t type = 0; + + while (addr < (mConfig.payload + mHeader.size)) + { + memcpy(&type, (void *) addr, sizeof(type)); + memcpy(&length, (void *) (addr + 1), sizeof(length)); + + if (searchedType == type) + { + ReturnErrorCodeIf(bufLength < length, CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(pBuf, (void *) (addr + kValueOffset), length); + + if (offset) + *offset = (addr - mConfig.payload); + + return CHIP_NO_ERROR; + } + else + { + /* Jump past 3 bytes of length and then use length to jump to next data */ + addr = addr + kValueOffset + length; + } + } + + return CHIP_ERROR_NOT_FOUND; +} + +CHIP_ERROR FactoryDataProvider::GetCertificationDeclaration(MutableByteSpan & outBuffer) +{ +#if CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION + constexpr uint8_t kCdForAllExamples[] = CHIP_DEVICE_CONFIG_CERTIFICATION_DECLARATION; + + return CopySpanToMutableSpan(ByteSpan{ kCdForAllExamples }, outBuffer); +#else + uint16_t declarationSize = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kCertDeclarationId, outBuffer.data(), outBuffer.size(), declarationSize)); + outBuffer.reduce_size(declarationSize); + + return CHIP_NO_ERROR; +#endif +} + +CHIP_ERROR FactoryDataProvider::GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetDeviceAttestationCert(MutableByteSpan & outBuffer) +{ + uint16_t certificateSize = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kDacCertificateId, outBuffer.data(), outBuffer.size(), certificateSize)); + outBuffer.reduce_size(certificateSize); + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductAttestationIntermediateCert(MutableByteSpan & outBuffer) +{ + uint16_t certificateSize = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kPaiCertificateId, outBuffer.data(), outBuffer.size(), certificateSize)); + outBuffer.reduce_size(certificateSize); + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer) +{ + return SignWithDacKey(messageToSign, outSignBuffer); +} + +CHIP_ERROR FactoryDataProvider::GetSetupDiscriminator(uint16_t & setupDiscriminator) +{ + uint32_t discriminator = 0; + uint16_t temp = 0; + + ReturnErrorOnFailure(SearchForId(FactoryDataId::kDiscriminatorId, (uint8_t *) &discriminator, sizeof(discriminator), temp)); + setupDiscriminator = (uint16_t) (discriminator & 0x0000FFFF); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::SetSetupDiscriminator(uint16_t setupDiscriminator) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR FactoryDataProvider::GetSpake2pIterationCount(uint32_t & iterationCount) +{ + uint16_t temp = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kIcId, (uint8_t *) &iterationCount, sizeof(iterationCount), temp)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetSpake2pSalt(MutableByteSpan & saltBuf) +{ + char saltB64[kSpake2pSalt_MaxBase64Len] = { 0 }; + uint16_t saltB64Len = 0; + + ReturnErrorOnFailure(SearchForId(FactoryDataId::kSaltId, (uint8_t *) (&saltB64[0]), sizeof(saltB64), saltB64Len)); + size_t saltLen = chip::Base64Decode32(saltB64, saltB64Len, reinterpret_cast(saltB64)); + + ReturnErrorCodeIf(saltLen > saltBuf.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(saltBuf.data(), saltB64, saltLen); + saltBuf.reduce_size(saltLen); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetSpake2pVerifier(MutableByteSpan & verifierBuf, size_t & verifierLen) +{ + char verifierB64[kSpake2pSerializedVerifier_MaxBase64Len] = { 0 }; + uint16_t verifierB64Len = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kVerifierId, (uint8_t *) &verifierB64[0], sizeof(verifierB64), verifierB64Len)); + + verifierLen = chip::Base64Decode32(verifierB64, verifierB64Len, reinterpret_cast(verifierB64)); + ReturnErrorCodeIf(verifierLen > verifierBuf.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(verifierBuf.data(), verifierB64, verifierLen); + verifierBuf.reduce_size(verifierLen); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetSetupPasscode(uint32_t & setupPasscode) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kSetupPasscodeId, (uint8_t *) &setupPasscode, sizeof(setupPasscode), length)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::SetSetupPasscode(uint32_t setupPasscode) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR FactoryDataProvider::GetVendorName(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kVendorNameId, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetVendorId(uint16_t & vendorId) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kVidId, (uint8_t *) &vendorId, sizeof(vendorId), length)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductName(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kProductNameId, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductId(uint16_t & productId) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kPidId, (uint8_t *) &productId, sizeof(productId), length)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetPartNumber(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kPartNumber, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductURL(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kProductURL, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductLabel(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kProductLabel, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetSerialNumber(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kSerialNumberId, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetManufacturingDate(uint16_t & year, uint8_t & month, uint8_t & day) +{ + uint16_t length = 0; + uint8_t date[ConfigurationManager::kMaxManufacturingDateLength]; + + ReturnErrorOnFailure( + SearchForId(FactoryDataId::kManufacturingDateId, date, ConfigurationManager::kMaxManufacturingDateLength, length)); + date[length] = '\0'; + + if (length == 10 && isdigit(date[0]) && isdigit(date[1]) && isdigit(date[2]) && isdigit(date[3]) && date[4] == '-' && + isdigit(date[5]) && isdigit(date[6]) && date[7] == '-' && isdigit(date[8]) && isdigit(date[9])) + { + year = 1000 * (date[0] - '0') + 100 * (date[1] - '0') + 10 * (date[2] - '0') + date[3] - '0'; + month = 10 * (date[5] - '0') + date[6] - '0'; + day = 10 * (date[8] - '0') + date[9] - '0'; + } + else + { + ChipLogError(DeviceLayer, "Manufacturing date is not formatted correctly: YYYY-MM-DD."); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetHardwareVersion(uint16_t & hardwareVersion) +{ + uint16_t length = 0; + ReturnErrorOnFailure( + SearchForId(FactoryDataId::kHardwareVersionId, (uint8_t *) &hardwareVersion, sizeof(hardwareVersion), length)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetHardwareVersionString(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kHardwareVersionStrId, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetRotatingDeviceIdUniqueId(MutableByteSpan & uniqueIdSpan) +{ + CHIP_ERROR err = CHIP_ERROR_NOT_IMPLEMENTED; +#if CHIP_ENABLE_ROTATING_DEVICE_ID + static_assert(ConfigurationManager::kRotatingDeviceIDUniqueIDLength >= ConfigurationManager::kMinRotatingDeviceIDUniqueIDLength, + "Length of unique ID for rotating device ID is smaller than minimum."); + uint16_t uniqueIdLen = 0; + err = SearchForId(FactoryDataId::kUniqueId, (uint8_t *) uniqueIdSpan.data(), uniqueIdSpan.size(), uniqueIdLen); +#if defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID) + if (err != CHIP_NO_ERROR) + { + constexpr uint8_t uniqueId[] = CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID; + + ReturnErrorCodeIf(sizeof(uniqueId) > uniqueIdSpan.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(uniqueIdSpan.data(), uniqueId, sizeof(uniqueId)); + uniqueIdLen = sizeof(uniqueId); + err = CHIP_NO_ERROR; + } +#endif // CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID + ReturnErrorOnFailure(err); + uniqueIdSpan.reduce_size(uniqueIdLen); +#endif + + return err; +} + +CHIP_ERROR FactoryDataProvider::GetProductFinish(app::Clusters::BasicInformation::ProductFinishEnum * finish) +{ + uint8_t productFinish; + uint16_t length = 0; + auto err = SearchForId(FactoryDataId::kProductFinish, &productFinish, sizeof(productFinish), length); + ReturnErrorCodeIf(err != CHIP_NO_ERROR, CHIP_ERROR_NOT_IMPLEMENTED); + + *finish = static_cast(productFinish); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductPrimaryColor(app::Clusters::BasicInformation::ColorEnum * primaryColor) +{ + uint8_t color; + uint16_t length = 0; + auto err = SearchForId(FactoryDataId::kProductPrimaryColor, &color, sizeof(color), length); + ReturnErrorCodeIf(err != CHIP_NO_ERROR, CHIP_ERROR_NOT_IMPLEMENTED); + + *primaryColor = static_cast(color); + + return CHIP_NO_ERROR; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nxp/common/legacy/FactoryDataProvider.h b/src/platform/nxp/common/legacy/FactoryDataProvider.h new file mode 100644 index 00000000000000..f7d05c3f1da1f4 --- /dev/null +++ b/src/platform/nxp/common/legacy/FactoryDataProvider.h @@ -0,0 +1,177 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * 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 +#include +#include +#include + +#include + +#include + +#include "CHIPPlatformConfig.h" + +#include + +namespace chip { +namespace DeviceLayer { + +#define CHIP_FACTORY_DATA_ERROR(e) \ + ChipError(ChipError::Range::kLastRange, ((uint8_t) ChipError::Range::kLastRange << 2) | e, __FILE__, __LINE__) + +#define CHIP_FACTORY_DATA_SHA_CHECK CHIP_FACTORY_DATA_ERROR(0x01) +#define CHIP_FACTORY_DATA_HEADER_READ CHIP_FACTORY_DATA_ERROR(0x02) +#define CHIP_FACTORY_DATA_HASH_ID CHIP_FACTORY_DATA_ERROR(0x03) +#define CHIP_FACTORY_DATA_PDM_RESTORE CHIP_FACTORY_DATA_ERROR(0x04) +#define CHIP_FACTORY_DATA_NULL CHIP_FACTORY_DATA_ERROR(0x05) +#define CHIP_FACTORY_DATA_FLASH_ERASE CHIP_FACTORY_DATA_ERROR(0x06) +#define CHIP_FACTORY_DATA_FLASH_PROGRAM CHIP_FACTORY_DATA_ERROR(0x07) +#define CHIP_FACTORY_DATA_INTERNAL_FLASH_READ CHIP_FACTORY_DATA_ERROR(0x08) +#define CHIP_FACTORY_DATA_PDM_SAVE_RECORD CHIP_FACTORY_DATA_ERROR(0x09) +#define CHIP_FACTORY_DATA_PDM_READ_RECORD CHIP_FACTORY_DATA_ERROR(0x0A) +#define CHIP_FACTORY_DATA_RESTORE_MECHANISM CHIP_FACTORY_DATA_ERROR(0x0B) + +// Forward declaration to define the getter for factory data provider impl instance +class FactoryDataProviderImpl; + +/** + * @brief This class provides Commissionable data, Device Attestation Credentials, + * and Device Instance Info. + */ + +class FactoryDataProvider : public DeviceInstanceInfoProvider, + public CommissionableDataProvider, + public Credentials::DeviceAttestationCredentialsProvider +{ +public: + struct Header + { + uint32_t hashId; + uint32_t size; + uint8_t hash[4]; + }; + + struct FactoryDataConfig + { + uint32_t start; + uint32_t size; + uint32_t payload; + }; + + // Default factory data IDs + enum FactoryDataId + { + kVerifierId = 1, + kSaltId, + kIcId, + kDacPrivateKeyId, + kDacCertificateId, + kPaiCertificateId, + kDiscriminatorId, + kSetupPasscodeId, + kVidId, + kPidId, + kCertDeclarationId, + kVendorNameId, + kProductNameId, + kSerialNumberId, + kManufacturingDateId, + kHardwareVersionId, + kHardwareVersionStrId, + kUniqueId, + kPartNumber, + kProductURL, + kProductLabel, + kProductFinish, + kProductPrimaryColor, + kMaxId + }; + + static uint32_t kFactoryDataMaxSize; + static constexpr uint32_t kLengthOffset = 1; + static constexpr uint32_t kValueOffset = 3; + static constexpr uint32_t kHashLen = 4; + static constexpr size_t kHashId = 0xCE47BA5E; + + virtual ~FactoryDataProvider(); + + virtual CHIP_ERROR Init() = 0; + virtual CHIP_ERROR SignWithDacKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer); + virtual CHIP_ERROR Validate(); + + virtual CHIP_ERROR SearchForId(uint8_t searchedType, uint8_t * pBuf, size_t bufLength, uint16_t & length, + uint32_t * offset = nullptr); + +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR + using RestoreMechanism = CHIP_ERROR (*)(void); + + CHIP_ERROR ValidateWithRestore(); + void RegisterRestoreMechanism(RestoreMechanism mechanism); + + virtual CHIP_ERROR PreResetCheck() = 0; + virtual CHIP_ERROR PostResetCheck() = 0; +#endif + + // ===== Members functions that implement the CommissionableDataProvider + CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator) override; + CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator) override; + CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) override; + CHIP_ERROR GetSpake2pSalt(MutableByteSpan & saltBuf) override; + CHIP_ERROR GetSpake2pVerifier(MutableByteSpan & verifierBuf, size_t & verifierLen) override; + CHIP_ERROR GetSetupPasscode(uint32_t & setupPasscode) override; + CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override; + + // ===== Members functions that implement the DeviceAttestationCredentialsProvider + CHIP_ERROR GetCertificationDeclaration(MutableByteSpan & outBuffer) override; + CHIP_ERROR GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) override; + CHIP_ERROR GetDeviceAttestationCert(MutableByteSpan & outBuffer) override; + CHIP_ERROR GetProductAttestationIntermediateCert(MutableByteSpan & outBuffer) override; + CHIP_ERROR SignWithDeviceAttestationKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer) override; + + // ===== Members functions that implement the GenericDeviceInstanceInfoProvider + CHIP_ERROR GetVendorName(char * buf, size_t bufSize) override; + CHIP_ERROR GetVendorId(uint16_t & vendorId) override; + CHIP_ERROR GetProductName(char * buf, size_t bufSize) override; + CHIP_ERROR GetProductId(uint16_t & productId) override; + CHIP_ERROR GetPartNumber(char * buf, size_t bufSize) override; + CHIP_ERROR GetProductURL(char * buf, size_t bufSize) override; + CHIP_ERROR GetProductLabel(char * buf, size_t bufSize) override; + CHIP_ERROR GetHardwareVersionString(char * buf, size_t bufSize) override; + CHIP_ERROR GetSerialNumber(char * buf, size_t bufSize) override; + CHIP_ERROR GetManufacturingDate(uint16_t & year, uint8_t & month, uint8_t & day) override; + CHIP_ERROR GetHardwareVersion(uint16_t & hardwareVersion) override; + CHIP_ERROR GetRotatingDeviceIdUniqueId(MutableByteSpan & uniqueIdSpan) override; + CHIP_ERROR GetProductFinish(app::Clusters::BasicInformation::ProductFinishEnum * finish) override; + CHIP_ERROR GetProductPrimaryColor(app::Clusters::BasicInformation::ColorEnum * primaryColor) override; + +protected: + Header mHeader; + FactoryDataConfig mConfig; +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR + std::vector mRestoreMechanisms; + FactoryDataDriver * mFactoryDataDriver = nullptr; +#endif +}; + +extern FactoryDataProvider & FactoryDataPrvd(); + +extern FactoryDataProviderImpl & FactoryDataPrvdImpl(); + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nxp/common/legacy/OTAFactoryDataProcessor.cpp b/src/platform/nxp/common/legacy/OTAFactoryDataProcessor.cpp new file mode 100644 index 00000000000000..4960ca2c4e0669 --- /dev/null +++ b/src/platform/nxp/common/legacy/OTAFactoryDataProcessor.cpp @@ -0,0 +1,165 @@ +/* + * + * Copyright (c) 2023 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 +#include +#include +#include + +namespace chip { + +CHIP_ERROR OTAFactoryDataProcessor::Init() +{ + mAccumulator.Init(mLength); + mFactoryDataDriver = &chip::DeviceLayer::FactoryDataDrv(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::Clear() +{ + OTATlvProcessor::ClearInternal(); + mAccumulator.Clear(); + mPayload.Clear(); + mFactoryDataDriver->ClearRamBackup(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::ProcessInternal(ByteSpan & block) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + ReturnErrorOnFailure(mAccumulator.Accumulate(block)); +#if OTA_ENCRYPTION_ENABLE + MutableByteSpan mBlock = MutableByteSpan(mAccumulator.data(), mAccumulator.GetThreshold()); + OTATlvProcessor::vOtaProcessInternalEncryption(mBlock); +#endif + error = DecodeTlv(); + + if (error != CHIP_NO_ERROR) + { + // The factory data payload can contain a variable number of fields + // to be updated. CHIP_END_OF_TLV is returned if no more fields are + // found. + if (error == CHIP_END_OF_TLV) + { + return CHIP_NO_ERROR; + } + + Clear(); + } + + return error; +} + +CHIP_ERROR OTAFactoryDataProcessor::ApplyAction() +{ + CHIP_ERROR error = CHIP_NO_ERROR; + FactoryProvider * provider; + + ReturnErrorOnFailure(mFactoryDataDriver->InitRamBackup()); + ReturnErrorOnFailure(mFactoryDataDriver->BackupFactoryData()); + + SuccessOrExit(error = Update((uint8_t) Tags::kDacPrivateKeyId, mPayload.mCertDacKey)); + SuccessOrExit(error = Update((uint8_t) Tags::kDacCertificateId, mPayload.mCertDac)); + SuccessOrExit(error = Update((uint8_t) Tags::kPaiCertificateId, mPayload.mCertPai)); + SuccessOrExit(error = Update((uint8_t) Tags::kCertDeclarationId, mPayload.mCertDeclaration)); + + SuccessOrExit(error = mFactoryDataDriver->UpdateFactoryData()); + + provider = &chip::DeviceLayer::FactoryDataPrvd(); + VerifyOrReturnError(provider != nullptr, CHIP_ERROR_INTERNAL); + + SuccessOrExit(error = provider->PreResetCheck()); + +exit: + if (error != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to update factory data. Error: %s", ErrorStr(error)); + } + else + { + ChipLogProgress(DeviceLayer, "Factory data update finished."); + } + + return error; +} + +CHIP_ERROR OTAFactoryDataProcessor::AbortAction() +{ + CHIP_ERROR error = CHIP_NO_ERROR; + ReturnErrorOnFailure(mFactoryDataDriver->ReadBackupInRam()); + ReturnErrorOnFailure(mFactoryDataDriver->UpdateFactoryData()); + + error = mFactoryDataDriver->DeleteBackup(); + ReturnErrorOnFailure(error); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::DecodeTlv() +{ + TLV::TLVReader tlvReader; + tlvReader.Init(mAccumulator.data(), mLength); + ReturnErrorOnFailure(tlvReader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag())); + + TLV::TLVType outerType; + ReturnErrorOnFailure(tlvReader.EnterContainer(outerType)); + ReturnErrorOnFailure(tlvReader.Next()); + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) Tags::kDacPrivateKeyId)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDacKey.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) Tags::kDacCertificateId)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDac.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) Tags::kPaiCertificateId)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertPai.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) Tags::kCertDeclarationId)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDeclaration.Emplace())); + } + + ReturnErrorOnFailure(tlvReader.ExitContainer(outerType)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::Update(uint8_t tag, Optional & optional) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + if (optional.HasValue()) + { + error = mFactoryDataDriver->UpdateValueInRam(tag, optional.Value()); + } + + return error; +} + +} // namespace chip diff --git a/src/platform/nxp/common/legacy/OTAFactoryDataProcessor.h b/src/platform/nxp/common/legacy/OTAFactoryDataProcessor.h new file mode 100644 index 00000000000000..862aad2d9a6341 --- /dev/null +++ b/src/platform/nxp/common/legacy/OTAFactoryDataProcessor.h @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2023 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 +#include +#include +#include +#include +#include +#include PLATFORM_FACTORY_DATA_PROVIDER_IMPL_HEADER + +namespace chip { + +using FactoryProvider = DeviceLayer::FactoryDataProvider; +using FactoryProviderImpl = DeviceLayer::FactoryDataProviderImpl; +using FactoryDataDriver = DeviceLayer::FactoryDataDriver; +using Tags = FactoryProvider::FactoryDataId; + +/** + * OTA custom payload that uses Matter TLVs. + * The custom payload is used when factory data needs updating. + * Factory data will be encoded using Matter TLV format to make + * use of the ChipTlv reader. A payload contains metadata (size of + * TLVs) and the TLVs themselves contained in a structure. + * If no factory data need to be updated, the metadata will be 0 + */ +struct OTAFactoryPayload +{ + Optional mCertDacKey; + Optional mCertDac; + Optional mCertPai; + Optional mCertDeclaration; + + void Clear() + { + mCertDacKey.ClearValue(); + mCertDac.ClearValue(); + mCertPai.ClearValue(); + mCertDeclaration.ClearValue(); + } +}; + +class OTAFactoryDataProcessor : public OTATlvProcessor +{ +public: + CHIP_ERROR Init() override; + CHIP_ERROR Clear() override; + CHIP_ERROR ApplyAction() override; + CHIP_ERROR AbortAction() override; + +private: + CHIP_ERROR ProcessInternal(ByteSpan & block) override; + CHIP_ERROR DecodeTlv(); + CHIP_ERROR Update(uint8_t tag, Optional & optional); + + OTAFactoryPayload mPayload; + OTADataAccumulator mAccumulator; + FactoryDataDriver * mFactoryDataDriver = nullptr; +}; + +} // namespace chip diff --git a/src/platform/nxp/common/legacy/OTAImageProcessorImpl.cpp b/src/platform/nxp/common/legacy/OTAImageProcessorImpl.cpp new file mode 100644 index 00000000000000..249b51b7681c22 --- /dev/null +++ b/src/platform/nxp/common/legacy/OTAImageProcessorImpl.cpp @@ -0,0 +1,424 @@ +/* + * + * Copyright (c) 2021-2023 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 +#include +#include +#include +#include +#include + +#include + +using namespace chip::DeviceLayer; +using namespace ::chip::DeviceLayer::Internal; + +#if USE_SMU2_STATIC +// The attribute specifier should not be changed. +static chip::OTAImageProcessorImpl gImageProcessor __attribute__((section(".smu2"))); +#else +static chip::OTAImageProcessorImpl gImageProcessor; +#endif + +namespace chip { + +CHIP_ERROR OTAImageProcessorImpl::Init(OTADownloader * downloader) +{ + ReturnErrorCodeIf(downloader == nullptr, CHIP_ERROR_INVALID_ARGUMENT); + mDownloader = downloader; + + OtaHookInit(); + + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::Clear() +{ + mHeaderParser.Clear(); + mAccumulator.Clear(); + mParams.totalFileBytes = 0; + mParams.downloadedBytes = 0; + mCurrentProcessor = nullptr; + + ReleaseBlock(); +} + +CHIP_ERROR OTAImageProcessorImpl::PrepareDownload() +{ + DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Finalize() +{ + DeviceLayer::PlatformMgr().ScheduleWork(HandleFinalize, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Apply() +{ + DeviceLayer::PlatformMgr().ScheduleWork(HandleApply, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Abort() +{ + DeviceLayer::PlatformMgr().ScheduleWork(HandleAbort, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block) +{ + if ((block.data() == nullptr) || block.empty()) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + // Store block data for HandleProcessBlock to access + CHIP_ERROR err = SetBlock(block); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Cannot set block data: %" CHIP_ERROR_FORMAT, err.Format()); + } + + DeviceLayer::PlatformMgr().ScheduleWork(HandleProcessBlock, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + + VerifyOrReturn(imageProcessor != nullptr, ChipLogError(SoftwareUpdate, "ImageProcessor context is null")); + + VerifyOrReturn(imageProcessor->mDownloader != nullptr, ChipLogError(SoftwareUpdate, "mDownloader is null")); + + GetRequestorInstance()->GetProviderLocation(imageProcessor->mBackupProviderLocation); + + imageProcessor->mHeaderParser.Init(); + imageProcessor->mAccumulator.Init(sizeof(OTATlvHeader)); + imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR); +} + +CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & block) +{ + OTAImageHeader header; + ReturnErrorOnFailure(mHeaderParser.AccumulateAndDecode(block, header)); + + mParams.totalFileBytes = header.mPayloadSize; + mHeaderParser.Clear(); + ChipLogError(SoftwareUpdate, "Processed header successfully"); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::ProcessPayload(ByteSpan & block) +{ + CHIP_ERROR status = CHIP_NO_ERROR; + + while (true) + { + if (!mCurrentProcessor) + { + ReturnErrorOnFailure(mAccumulator.Accumulate(block)); + ByteSpan tlvHeader{ mAccumulator.data(), sizeof(OTATlvHeader) }; + ReturnErrorOnFailure(SelectProcessor(tlvHeader)); + ReturnErrorOnFailure(mCurrentProcessor->Init()); + } + + status = mCurrentProcessor->Process(block); + if (status == CHIP_ERROR_OTA_CHANGE_PROCESSOR) + { + mAccumulator.Clear(); + mAccumulator.Init(sizeof(OTATlvHeader)); + + mCurrentProcessor = nullptr; + + // If the block size is 0, it means that the processed data was a multiple of + // received BDX block size (e.g. 8 blocks of 1024 bytes were transferred). + // After state for selecting next processor is reset, a request for fetching next + // data must be sent. + if (block.size() == 0) + { + status = CHIP_NO_ERROR; + break; + } + } + else + { + break; + } + } + + return status; +} + +CHIP_ERROR OTAImageProcessorImpl::SelectProcessor(ByteSpan & block) +{ + OTATlvHeader header; + Encoding::LittleEndian::Reader reader(block.data(), sizeof(header)); + + ReturnErrorOnFailure(reader.Read32(&header.tag).StatusCode()); + ReturnErrorOnFailure(reader.Read32(&header.length).StatusCode()); + + auto pair = mProcessorMap.find(header.tag); + if (pair == mProcessorMap.end()) + { + ChipLogError(SoftwareUpdate, "There is no registered processor for tag: %" PRIu32, header.tag); + return CHIP_ERROR_OTA_PROCESSOR_NOT_REGISTERED; + } + + ChipLogDetail(SoftwareUpdate, "Selected processor with tag: %ld", pair->first); + mCurrentProcessor = pair->second; + mCurrentProcessor->SetLength(header.length); + mCurrentProcessor->SetWasSelected(true); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::RegisterProcessor(uint32_t tag, OTATlvProcessor * processor) +{ + auto pair = mProcessorMap.find(tag); + if (pair != mProcessorMap.end()) + { + ChipLogError(SoftwareUpdate, "A processor for tag %" PRIu32 " is already registered.", tag); + return CHIP_ERROR_OTA_PROCESSOR_ALREADY_REGISTERED; + } + + mProcessorMap.insert({ tag, processor }); + + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::HandleAbort(intptr_t context) +{ + ChipLogError(SoftwareUpdate, "OTA was aborted"); + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor != nullptr) + { + imageProcessor->AbortAllProcessors(); + } + imageProcessor->Clear(); + + OtaHookAbort(); +} + +void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + + VerifyOrReturn(imageProcessor != nullptr, ChipLogError(SoftwareUpdate, "ImageProcessor context is null")); + + VerifyOrReturn(imageProcessor->mDownloader != nullptr, ChipLogError(SoftwareUpdate, "mDownloader is null")); + + CHIP_ERROR status; + auto block = ByteSpan(imageProcessor->mBlock.data(), imageProcessor->mBlock.size()); + + if (imageProcessor->mHeaderParser.IsInitialized()) + { + status = imageProcessor->ProcessHeader(block); + if (status != CHIP_NO_ERROR) + { + imageProcessor->HandleStatus(status); + } + } + + status = imageProcessor->ProcessPayload(block); + imageProcessor->HandleStatus(status); +} + +void OTAImageProcessorImpl::HandleStatus(CHIP_ERROR status) +{ + if (status == CHIP_NO_ERROR || status == CHIP_ERROR_BUFFER_TOO_SMALL) + { + mParams.downloadedBytes += mBlock.size(); + FetchNextData(0); + } + else if (status == CHIP_ERROR_OTA_FETCH_ALREADY_SCHEDULED) + { + mParams.downloadedBytes += mBlock.size(); + } + else + { + ChipLogError(SoftwareUpdate, "Image update canceled. Failed to process OTA block: %s", ErrorStr(status)); + GetRequestorInstance()->CancelImageUpdate(); + } +} + +void OTAImageProcessorImpl::AbortAllProcessors() +{ + ChipLogError(SoftwareUpdate, "All selected processors will call abort action"); + + for (auto const & pair : mProcessorMap) + { + if (pair.second->WasSelected()) + { + pair.second->AbortAction(); + pair.second->Clear(); + pair.second->SetWasSelected(false); + } + } +} + +bool OTAImageProcessorImpl::IsFirstImageRun() +{ + OTARequestorInterface * requestor = chip::GetRequestorInstance(); + if (requestor == nullptr) + { + return false; + } + + return requestor->GetCurrentUpdateState() == OTARequestorInterface::OTAUpdateStateEnum::kApplying; +} + +CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage() +{ + uint32_t currentVersion; + uint32_t targetVersion; + + OTARequestorInterface * requestor = chip::GetRequestorInstance(); + ReturnErrorCodeIf(requestor == nullptr, CHIP_ERROR_INTERNAL); + + targetVersion = requestor->GetTargetVersion(); + ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(currentVersion)); + if (currentVersion != targetVersion) + { + ChipLogError(SoftwareUpdate, "Current sw version %" PRIu32 " is different than the expected sw version = %" PRIu32, + currentVersion, targetVersion); + return CHIP_ERROR_INCORRECT_STATE; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::SetBlock(ByteSpan & block) +{ + if (!IsSpanUsable(block)) + { + return CHIP_NO_ERROR; + } + + if (mBlock.size() < block.size()) + { + if (!mBlock.empty()) + { + ReleaseBlock(); + } + uint8_t * mBlock_ptr = static_cast(chip::Platform::MemoryAlloc(block.size())); + if (mBlock_ptr == nullptr) + { + return CHIP_ERROR_NO_MEMORY; + } + mBlock = MutableByteSpan(mBlock_ptr, block.size()); + } + + CHIP_ERROR err = CopySpanToMutableSpan(block, mBlock); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Cannot copy block data: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::HandleFinalize(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + return; + } + + imageProcessor->ReleaseBlock(); +} + +void OTAImageProcessorImpl::HandleApply(intptr_t context) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + return; + } + + for (auto const & pair : imageProcessor->mProcessorMap) + { + if (pair.second->WasSelected()) + { + error = pair.second->ApplyAction(); + if (error != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Apply action for tag %d processor failed.", (uint8_t) pair.first); + // Revert all previously applied actions if current apply action fails. + // Reset image processor and requestor states. + imageProcessor->AbortAllProcessors(); + imageProcessor->Clear(); + GetRequestorInstance()->Reset(); + + return; + } + } + } + + for (auto const & pair : imageProcessor->mProcessorMap) + { + pair.second->Clear(); + pair.second->SetWasSelected(false); + } + + imageProcessor->mAccumulator.Clear(); + + ConfigurationManagerImpl().StoreSoftwareUpdateCompleted(); + PlatformMgr().HandleServerShuttingDown(); + + // Set the necessary information to inform the SSBL that a new image is available + // and trigger the actual device reboot after some time, to take into account + // queued actions, e.g. sending events to a subscription + SystemLayer().StartTimer( + chip::System::Clock::Milliseconds32(CHIP_DEVICE_LAYER_OTA_REBOOT_DELAY), + [](chip::System::Layer *, void *) { OtaHookReset(); }, nullptr); +} + +CHIP_ERROR OTAImageProcessorImpl::ReleaseBlock() +{ + if (mBlock.data() != nullptr) + { + chip::Platform::MemoryFree(mBlock.data()); + } + + mBlock = MutableByteSpan(); + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::FetchNextData(uint32_t context) +{ + auto * imageProcessor = &OTAImageProcessorImpl::GetDefaultInstance(); + SystemLayer().ScheduleLambda([imageProcessor] { + if (imageProcessor->mDownloader) + { + imageProcessor->mDownloader->FetchNextData(); + } + }); +} + +OTAImageProcessorImpl & OTAImageProcessorImpl::GetDefaultInstance() +{ + return gImageProcessor; +} + +} // namespace chip diff --git a/src/platform/nxp/common/legacy/OTAImageProcessorImpl.h b/src/platform/nxp/common/legacy/OTAImageProcessorImpl.h new file mode 100644 index 00000000000000..a30677037ea7f4 --- /dev/null +++ b/src/platform/nxp/common/legacy/OTAImageProcessorImpl.h @@ -0,0 +1,114 @@ +/* + * + * Copyright (c) 2021-2023 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 +#include +#include +#include +#include +#include +#include + +/* + * OTA hooks that can be overwritten by application. + * Default behavior is implemented as WEAK symbols in platform OtaHooks.cpp. + */ + +/* + * This hook is called at the end of OTAImageProcessorImpl::Init. + * It should generally register the OTATlvProcessor instances. + */ +extern "C" CHIP_ERROR OtaHookInit(); + +/* + * This hook is called at the end of OTAImageProcessorImpl::HandleApply. + * The default implementation saves the internal OTA entry structure and resets the device. + */ +extern "C" void OtaHookReset(); + +/* + * This hook is called at the end of OTAImageProcessorImpl::HandleAbort. + * For example, it can be used to schedule a retry. + */ +extern "C" void OtaHookAbort(); + +namespace chip { + +class OTAImageProcessorImpl : public OTAImageProcessorInterface +{ +public: + using ProviderLocation = chip::OTARequestorInterface::ProviderLocationType; + + CHIP_ERROR Init(OTADownloader * downloader); + void Clear(); + + //////////// OTAImageProcessorInterface Implementation /////////////// + CHIP_ERROR PrepareDownload() override; + CHIP_ERROR Finalize() override; + CHIP_ERROR Apply() override; + CHIP_ERROR Abort() override; + CHIP_ERROR ProcessBlock(ByteSpan & block) override; + bool IsFirstImageRun() override; + CHIP_ERROR ConfirmCurrentImage() override; + + CHIP_ERROR ProcessHeader(ByteSpan & block); + CHIP_ERROR ProcessPayload(ByteSpan & block); + CHIP_ERROR SelectProcessor(ByteSpan & block); + CHIP_ERROR RegisterProcessor(uint32_t tag, OTATlvProcessor * processor); + Optional & GetBackupProvider() { return mBackupProviderLocation; } + + static void FetchNextData(uint32_t context); + static OTAImageProcessorImpl & GetDefaultInstance(); + +private: + //////////// Actual handlers for the OTAImageProcessorInterface /////////////// + static void HandlePrepareDownload(intptr_t context); + static void HandleFinalize(intptr_t context); + static void HandleApply(intptr_t context); + static void HandleAbort(intptr_t context); + static void HandleProcessBlock(intptr_t context); + + void HandleStatus(CHIP_ERROR status); + + /** + * Called to allocate memory for mBlock if necessary and set it to block + */ + CHIP_ERROR SetBlock(ByteSpan & block); + + /** + * Called to release allocated memory for mBlock + */ + CHIP_ERROR ReleaseBlock(); + + /** + * Call AbortAction for all processors that were used + */ + void AbortAllProcessors(); + + MutableByteSpan mBlock; + OTADownloader * mDownloader; + OTAImageHeaderParser mHeaderParser; + OTATlvProcessor * mCurrentProcessor = nullptr; + OTADataAccumulator mAccumulator; + std::map mProcessorMap; + Optional mBackupProviderLocation; +}; + +} // namespace chip diff --git a/src/platform/nxp/common/legacy/OTATlvProcessor.cpp b/src/platform/nxp/common/legacy/OTATlvProcessor.cpp new file mode 100644 index 00000000000000..e50da13cecdd31 --- /dev/null +++ b/src/platform/nxp/common/legacy/OTATlvProcessor.cpp @@ -0,0 +1,178 @@ +/* + * + * Copyright (c) 2023 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 +#include +#include + +#include +#include +#if OTA_ENCRYPTION_ENABLE +#include "OtaUtils.h" +#include "rom_aes.h" +#endif +namespace chip { + +#if OTA_ENCRYPTION_ENABLE +constexpr uint8_t au8Iv[] = { 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x00, 0x00, 0x00, 0x00 }; +#endif + +CHIP_ERROR OTATlvProcessor::ApplyAction() +{ + return mApplyState == ApplyState::kApply ? CHIP_NO_ERROR : CHIP_ERROR_OTA_PROCESSOR_DO_NOT_APPLY; +} + +CHIP_ERROR OTATlvProcessor::Process(ByteSpan & block) +{ + CHIP_ERROR status = CHIP_NO_ERROR; + uint32_t bytes = chip::min(mLength - mProcessedLength, static_cast(block.size())); + ByteSpan relevantData = block.SubSpan(0, bytes); + + status = ProcessInternal(relevantData); + if (!IsError(status)) + { + mProcessedLength += bytes; + block = block.SubSpan(bytes); + if (mProcessedLength == mLength) + { + status = ExitAction(); + if (!IsError(status)) + { + // If current block was processed fully and the block still contains data, it + // means that the block contains another TLV's data and the current processor + // should be changed by OTAImageProcessorImpl. + return CHIP_ERROR_OTA_CHANGE_PROCESSOR; + } + } + } + + return status; +} + +void OTATlvProcessor::ClearInternal() +{ + mLength = 0; + mProcessedLength = 0; + mWasSelected = false; + mApplyState = ApplyState::kApply; +#if OTA_ENCRYPTION_ENABLE + mIVOffset = 0; +#endif +} + +bool OTATlvProcessor::IsError(CHIP_ERROR & status) +{ + return status != CHIP_NO_ERROR && status != CHIP_ERROR_BUFFER_TOO_SMALL && status != CHIP_ERROR_OTA_FETCH_ALREADY_SCHEDULED; +} + +void OTADataAccumulator::Init(uint32_t threshold) +{ + mThreshold = threshold; + mBufferOffset = 0; + mBuffer.Alloc(mThreshold); +} + +void OTADataAccumulator::Clear() +{ + mThreshold = 0; + mBufferOffset = 0; + mBuffer.Free(); +} + +CHIP_ERROR OTADataAccumulator::Accumulate(ByteSpan & block) +{ + uint32_t numBytes = chip::min(mThreshold - mBufferOffset, static_cast(block.size())); + memcpy(&mBuffer[mBufferOffset], block.data(), numBytes); + mBufferOffset += numBytes; + block = block.SubSpan(numBytes); + + if (mBufferOffset < mThreshold) + { + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + + return CHIP_NO_ERROR; +} + +#if OTA_ENCRYPTION_ENABLE +CHIP_ERROR OTATlvProcessor::vOtaProcessInternalEncryption(MutableByteSpan & block) +{ + uint8_t iv[16]; + uint8_t key[kOTAEncryptionKeyLength]; + uint8_t dataOut[16] = { 0 }; + uint32_t u32IVCount; + uint32_t Offset = 0; + uint8_t data; + tsReg128 sKey; + aesContext_t Context; + + memcpy(iv, au8Iv, sizeof(au8Iv)); + + u32IVCount = (((uint32_t) iv[12]) << 24) | (((uint32_t) iv[13]) << 16) | (((uint32_t) iv[14]) << 8) | (iv[15]); + u32IVCount += (mIVOffset >> 4); + + iv[12] = (uint8_t) ((u32IVCount >> 24) & 0xff); + iv[13] = (uint8_t) ((u32IVCount >> 16) & 0xff); + iv[14] = (uint8_t) ((u32IVCount >> 8) & 0xff); + iv[15] = (uint8_t) (u32IVCount & 0xff); + + if (Encoding::HexToBytes(OTA_ENCRYPTION_KEY, strlen(OTA_ENCRYPTION_KEY), key, kOTAEncryptionKeyLength) != + kOTAEncryptionKeyLength) + { + // Failed to convert the OTAEncryptionKey string to octstr type value + return CHIP_ERROR_INVALID_STRING_LENGTH; + } + + ByteSpan KEY = ByteSpan(key); + Encoding::LittleEndian::Reader reader_key(KEY.data(), KEY.size()); + ReturnErrorOnFailure(reader_key.Read32(&sKey.u32register0) + .Read32(&sKey.u32register1) + .Read32(&sKey.u32register2) + .Read32(&sKey.u32register3) + .StatusCode()); + + while (Offset + 16 <= block.size()) + { + /*Encrypt the IV*/ + Context.mode = AES_MODE_ECB_ENCRYPT; + Context.pSoftwareKey = (uint32_t *) &sKey; + AES_128_ProcessBlocks(&Context, (uint32_t *) &iv[0], (uint32_t *) &dataOut[0], 1); + + /* Decrypt a block of the buffer */ + for (uint8_t i = 0; i < 16; i++) + { + data = block[Offset + i] ^ dataOut[i]; + memcpy(&block[Offset + i], &data, sizeof(uint8_t)); + } + + /* increment the IV for the next block */ + u32IVCount++; + + iv[12] = (uint8_t) ((u32IVCount >> 24) & 0xff); + iv[13] = (uint8_t) ((u32IVCount >> 16) & 0xff); + iv[14] = (uint8_t) ((u32IVCount >> 8) & 0xff); + iv[15] = (uint8_t) (u32IVCount & 0xff); + + Offset += 16; /* increment the buffer offset */ + mIVOffset += 16; + } + + return CHIP_NO_ERROR; +} +#endif +} // namespace chip diff --git a/src/platform/nxp/common/legacy/OTATlvProcessor.h b/src/platform/nxp/common/legacy/OTATlvProcessor.h new file mode 100644 index 00000000000000..f1faef7e8eecf9 --- /dev/null +++ b/src/platform/nxp/common/legacy/OTATlvProcessor.h @@ -0,0 +1,180 @@ +/* + * + * Copyright (c) 2023 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 +#include +#include + +namespace chip { + +#define CHIP_ERROR_TLV_PROCESSOR(e) \ + ChipError(ChipError::Range::kLastRange, ((uint8_t) ChipError::Range::kLastRange << 3) | e, __FILE__, __LINE__) + +#define CHIP_ERROR_OTA_CHANGE_PROCESSOR CHIP_ERROR_TLV_PROCESSOR(0x02) +#define CHIP_ERROR_OTA_PROCESSOR_NOT_REGISTERED CHIP_ERROR_TLV_PROCESSOR(0x03) +#define CHIP_ERROR_OTA_PROCESSOR_ALREADY_REGISTERED CHIP_ERROR_TLV_PROCESSOR(0x04) +#define CHIP_ERROR_OTA_PROCESSOR_CLIENT_INIT CHIP_ERROR_TLV_PROCESSOR(0x05) +#define CHIP_ERROR_OTA_PROCESSOR_MAKE_ROOM CHIP_ERROR_TLV_PROCESSOR(0x06) +#define CHIP_ERROR_OTA_PROCESSOR_PUSH_CHUNK CHIP_ERROR_TLV_PROCESSOR(0x07) +#define CHIP_ERROR_OTA_PROCESSOR_IMG_AUTH CHIP_ERROR_TLV_PROCESSOR(0x08) +#define CHIP_ERROR_OTA_FETCH_ALREADY_SCHEDULED CHIP_ERROR_TLV_PROCESSOR(0x09) +#define CHIP_ERROR_OTA_PROCESSOR_IMG_COMMIT CHIP_ERROR_TLV_PROCESSOR(0x0A) +#define CHIP_ERROR_OTA_PROCESSOR_CB_NOT_REGISTERED CHIP_ERROR_TLV_PROCESSOR(0x0B) +#define CHIP_ERROR_OTA_PROCESSOR_EEPROM_OFFSET CHIP_ERROR_TLV_PROCESSOR(0x0C) +#define CHIP_ERROR_OTA_PROCESSOR_EXTERNAL_STORAGE CHIP_ERROR_TLV_PROCESSOR(0x0D) +#define CHIP_ERROR_OTA_PROCESSOR_START_IMAGE CHIP_ERROR_TLV_PROCESSOR(0x0E) +#define CHIP_ERROR_OTA_PROCESSOR_DO_NOT_APPLY CHIP_ERROR_TLV_PROCESSOR(0x0F) + +// Descriptor constants +constexpr size_t kVersionStringSize = 64; +constexpr size_t kBuildDateSize = 64; + +constexpr uint16_t requestedOtaMaxBlockSize = 1024; + +/** + * Used alongside RegisterDescriptorCallback to register + * a custom descriptor processing function with a certain + * TLV processor. + */ +typedef CHIP_ERROR (*ProcessDescriptor)(void * descriptor); + +struct OTATlvHeader +{ + uint32_t tag; + uint32_t length; +}; + +/** + * This class defines an interface for a Matter TLV processor. + * Instances of derived classes can be registered as processors + * in OTAImageProcessorImpl. Based on the TLV type, a certain + * processor is used to process subsequent blocks until the number + * of bytes found in the metadata is processed. In case a block contains + * data from two different TLVs, the processor should ensure the remaining + * data is returned in the block passed as input. + * The default processors: application, SSBL and factory data are registered + * in OTAImageProcessorImpl::Init through OtaHookInit. + * Applications should use OTAImageProcessorImpl::RegisterProcessor + * to register additional processors. + */ +class OTATlvProcessor +{ +public: + enum class ApplyState : uint8_t + { + kApply = 0, + kDoNotApply + }; + + virtual ~OTATlvProcessor() {} + + virtual CHIP_ERROR Init() = 0; + virtual CHIP_ERROR Clear() = 0; + virtual CHIP_ERROR AbortAction() = 0; + virtual CHIP_ERROR ExitAction() { return CHIP_NO_ERROR; } + virtual CHIP_ERROR ApplyAction(); + + CHIP_ERROR Process(ByteSpan & block); + void RegisterDescriptorCallback(ProcessDescriptor callback) { mCallbackProcessDescriptor = callback; } + void SetLength(uint32_t length) { mLength = length; } + void SetWasSelected(bool selected) { mWasSelected = selected; } + bool WasSelected() { return mWasSelected; } +#if OTA_ENCRYPTION_ENABLE + CHIP_ERROR vOtaProcessInternalEncryption(MutableByteSpan & block); +#endif + +protected: + /** + * @brief Process custom TLV payload + * + * The method takes subsequent chunks of the Matter OTA image file and processes them. + * If more image chunks are needed, CHIP_ERROR_BUFFER_TOO_SMALL error is returned. + * Other error codes indicate that an error occurred during processing. Fetching + * next data is scheduled automatically by OTAImageProcessorImpl if the return value + * is neither an error code, nor CHIP_ERROR_OTA_FETCH_ALREADY_SCHEDULED (which implies the + * scheduling is done inside ProcessInternal or will be done in the future, through a + * callback). + * + * @param block Byte span containing a subsequent Matter OTA image chunk. When the method + * returns CHIP_NO_ERROR, the byte span is used to return a remaining part + * of the chunk, not used by current TLV processor. + * + * @retval CHIP_NO_ERROR Block was processed successfully. + * @retval CHIP_ERROR_BUFFER_TOO_SMALL Provided buffers are insufficient to decode some + * metadata (e.g. a descriptor). + * @retval CHIP_ERROR_OTA_FETCH_ALREADY_SCHEDULED Should be returned if ProcessInternal schedules + * fetching next data (e.g. through a callback). + * @retval Error code Something went wrong. Current OTA process will be + * canceled. + */ + virtual CHIP_ERROR ProcessInternal(ByteSpan & block) = 0; + + void ClearInternal(); + + bool IsError(CHIP_ERROR & status); + +#if OTA_ENCRYPTION_ENABLE + /*ota decryption*/ + uint32_t mIVOffset = 0; + /* Expected byte size of the OTAEncryptionKeyLength */ + static constexpr size_t kOTAEncryptionKeyLength = 16; +#endif + uint32_t mLength = 0; + uint32_t mProcessedLength = 0; + bool mWasSelected = false; + + /** + * @brief A flag to account for corner cases during OTA apply + * + * Used by the default ApplyAction implementation. + * + * If something goes wrong during ExitAction of the TLV processor, + * then mApplyState should be set to kDoNotApply and the image processor + * should abort. In this case, the BDX transfer was already finished + * and calling CancelImageUpdate will not abort the transfer, hence + * the device will reboot even though it should not have. If ApplyAction + * fails during HandleApply, then the process will be aborted. + */ + ApplyState mApplyState = ApplyState::kApply; + ProcessDescriptor mCallbackProcessDescriptor = nullptr; +}; + +/** + * This class can be used to accumulate data until a given threshold. + * Should be used by OTATlvProcessor derived classes if they need + * metadata accumulation (e.g. for custom header decoding). + */ +class OTADataAccumulator +{ +public: + void Init(uint32_t threshold); + void Clear(); + CHIP_ERROR Accumulate(ByteSpan & block); + + inline uint8_t * data() { return mBuffer.Get(); } + inline uint32_t GetThreshold() { return mThreshold; } + +private: + uint32_t mThreshold; + uint32_t mBufferOffset; + Platform::ScopedMemoryBuffer mBuffer; +}; + +} // namespace chip diff --git a/src/platform/nxp/common/legacy/OTA_README.md b/src/platform/nxp/common/legacy/OTA_README.md new file mode 100644 index 00000000000000..0c9715b4610ff8 --- /dev/null +++ b/src/platform/nxp/common/legacy/OTA_README.md @@ -0,0 +1,149 @@ +# K32W OTA + +The OTA processing is now delegated to instances of `OTATlvProcessor` derived +classes. These instances are registered with the `OTAImageProcessorImpl` +instance, which manages the selection of processors that should process the next +blocks, until a full TLV block was transferred. + +The application is able to define its own processors, thus extending the default +OTA functionality. The application can also opt to disable the default +processors (application, SSBL and factory data). + +Please note that if an OTA image containing multiple TLV is transferred, then +the action for each TLV is applied sequentially, If one of the actions fails, +the remaining actions will not be applied and OTA abort is called. TBD: should +all actions be applied only if there is no error? Or should each action be +applied separately? + +## Default processors + +The default processors for K32W0 are already implemented in: + +- `OTAFirmwareProcessor` for application/SSBL update. Enabled by default. +- `OTAFactoryDataProcessor` for factory data update. Disabled by default, user + has to specify `chip_ota_enable_factory_data_processor=1` in the build args. + +Some SDK OTA module flags are defined to support additional features: + +- `gOTAAllowCustomStartAddress=1` - enable `EEPROM` offset value. Used + internally by SDK OTA module. +- `gOTAUseCustomOtaEntry=1` - support custom OTA entry for multi-image. +- `gOTACustomOtaEntryMemory=1` - K32W0 uses `OTACustomStorage_ExtFlash` (1) by + default. + +## Implementing custom processors + +A custom processor should implement the abstract interface defined in +`OTATlvProcessor.h`. Below is a compact version: + +``` +class OTATlvProcessor +{ +public: + virtual CHIP_ERROR Init() = 0; + virtual CHIP_ERROR Clear() = 0; + virtual CHIP_ERROR ApplyAction() = 0; + virtual CHIP_ERROR AbortAction() = 0; + virtual CHIP_ERROR ExitAction(); + + CHIP_ERROR Process(ByteSpan & block); + void RegisterDescriptorCallback(ProcessDescriptor callback); +protected: + virtual CHIP_ERROR ProcessInternal(ByteSpan & block) = 0; +}; + +``` + +Some details regarding the interface: + +- `Init` will be called whenever the processor is selected. +- `Clear` will be called when abort occurs or after the apply action takes + place. +- `ApplyAction` will be called in `OTAImageProcessorImpl::HandleApply`, before + the board is reset. +- `AbortAction` will be called in `OTAImageProcessorImpl::HandleAbort`. + Processors should reset state here. +- `ExitAction` is optional and should be implemented by the processors that + want to execute an action after all data has been transferred, but before + `HandleApply` is called. It's called before the new processor selection + takes place. This is useful in the context of multiple TLV transferred in a + single OTA process. +- `Process` is the public API used inside `OTAImageProcessorImpl` for data + processing. This is a wrapper over `ProcessInternal`, which can return + `CHIP_OTA_CHANGE_PROCESSOR` to notify a new processor should be selected for + the remaining data. +- `RegisterDescriptorCallback` can be used to register a callback for + processing the descriptor. It's optional. +- `ProcessInternal` should return: _ `CHIP_NO_ERROR` if block was processed + successfully. _ `CHIP_ERROR_BUFFER_TOO_SMALL` if current block doesn't + contain all necessary data. This can happen when a TLV value field has a + header, but it is split across two blocks. \* + `CHIP_OTA_FETCH_ALREADY_SCHEDULED` if block was processed successfully and + the fetching is already scheduled by the processor. This happens in the + default application processor, because the next data fetching is scheduled + through a callback (called when enough external flash was erased). + +Furthermore, a processor can use an instance of `OTADataAccumulator` to +accumulate data until a given threshold. This is useful when a custom payload +contains metadata that need parsing: accumulate data until the threshold is +reached or return `CHIP_ERROR_BUFFER_TOO_SMALL` to signal +`OTAImageProcessorImpl` more data is needed. + +``` +/** + * This class can be used to accumulate data until a given threshold. + * Should be used by OTATlvProcessor derived classes if they need + * metadata accumulation (e.g. for custom header decoding). + */ +class OTADataAccumulator +{ +public: + void Init(uint32_t threshold); + void Clear(); + CHIP_ERROR Accumulate(ByteSpan & block); + + inline uint8_t* data() { return mBuffer.Get(); } + +private: + uint32_t mThreshold; + uint32_t mBufferOffset; + Platform::ScopedMemoryBuffer mBuffer; +}; +``` + +## SSBL max entries example + +`CONFIG_CHIP_K32W0_MAX_ENTRIES_TEST` can be set to 1 to enable max entries test. +There will be 8 additional processors registered in default `OtaHooks` +implementation. The OTA image should be generated with the +`create_ota_images.sh` script from `./scripts/tools/nxp/ota/examples`. + +## Factory data restore mechanism + +Prior to factory data update, the old factory data is backed up in external +flash. If anything interrupts the update (e.g. power loss), there is a slight +chance the internal flash factory data section is erased and has to be restored +at next boot. The `FactoryDataProvider` offers a default restore mechanism and +support for registering additional restore mechanisms or overwriting the default +one. + +Prior to factory data update, the old factory data is backed up in external +flash. If anything interrupts the update (e.g. power loss), there is a slight +chance the internal flash factory data section is erased and has to be restored +at next boot. The `FactoryDataProvider` offers a default restore mechanism and +support for registering additional restore mechanisms or overwriting the default +one. + +Restore mechanisms are just functions that have this signature: +`CHIP_ERROR (*)(void)`. Any such function can be registered through +`FactoryDataProvider::RegisterRestoreMechanism`. + +The default restore mechanism is implemented as a weak function: +`FactoryDataDefaultRestoreMechanism`. It is registered in +`FactoryDataProvider::Init`, before factory data validation, and it can be +overwritten at application level. When doing the actual restore, the mechanisms +are called in the order they were registered. + +Please note that the restore mechanisms registration order matters. Once a +restore mechanism is successful (`CHIP_NO_ERROR` is returned), the restore +process has finished and subsequent restore mechanisms will not be called. From c5da43aef32e53ccc107cfc4aa9d55e16dc38639 Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Fri, 19 Jul 2024 12:59:24 +0300 Subject: [PATCH 02/11] [NXP][k32w0] Remove k32w parent folder - examples/*/nxp/k32w/k32w0 becomes examples/*/nxp/k32w0 - src/platform/nxp/k32w/k32w0 becomes src/platform/nxp/k32w0 - third_party/openthread/platforms/nxp/k32w/k32w0 becomes third_party/openthread/platforms/nxp/k32w0 Signed-off-by: marius-alex-tache --- .../nxp/k32w/k32w0/build_overrides | 1 - .../k32w/k32w0/third_party/connectedhomeip | 1 - .../nxp/{k32w => }/k32w0/.gn | 2 +- .../nxp/{k32w => }/k32w0/BUILD.gn | 31 +- .../nxp/{k32w => }/k32w0/README.md | 454 ++++++++---------- .../nxp/{k32w => }/k32w0/args.gni | 0 .../nxp/k32w0/build_overrides | 1 + .../k32w0/include/CHIPProjectConfig.h | 4 +- .../{k32w => }/k32w0/include/FreeRTOSConfig.h | 0 .../nxp/{k32w => }/k32w0/main/AppTask.cpp | 21 +- .../k32w0/main/ContactSensorManager.cpp | 0 .../{k32w => }/k32w0/main/ZclCallbacks.cpp | 0 .../{k32w => }/k32w0/main/include/AppEvent.h | 0 .../{k32w => }/k32w0/main/include/AppTask.h | 5 +- .../k32w0/main/include/ContactSensorManager.h | 0 .../k32w0/main/include/app_config.h | 0 .../nxp/{k32w => }/k32w0/main/main.cpp | 2 +- .../nxp/k32w0/third_party/connectedhomeip | 1 + .../nxp/k32w/k32w0/build_overrides | 1 - .../k32w/k32w0/third_party/connectedhomeip | 1 - .../lighting-app/nxp/{k32w => }/k32w0/.gn | 2 +- .../nxp/{k32w => }/k32w0/BUILD.gn | 40 +- .../nxp/{k32w => }/k32w0/README.md | 398 +++++++-------- .../nxp/{k32w => }/k32w0/args.gni | 0 .../lighting-app/nxp/k32w0/build_overrides | 1 + .../k32w0/include/CHIPProjectConfig.h | 6 +- .../{k32w => }/k32w0/include/FreeRTOSConfig.h | 0 .../nxp/{k32w => }/k32w0/main/AppTask.cpp | 11 +- .../{k32w => }/k32w0/main/LightingManager.cpp | 0 .../{k32w => }/k32w0/main/ZclCallbacks.cpp | 0 .../{k32w => }/k32w0/main/include/AppEvent.h | 0 .../{k32w => }/k32w0/main/include/AppTask.h | 5 +- .../k32w0/main/include/LightingManager.h | 0 .../k32w0/main/include/app_config.h | 0 .../nxp/{k32w => }/k32w0/main/main.cpp | 2 +- .../nxp/k32w0/third_party/connectedhomeip | 1 + .../k32w0/DefaultTestEventTriggerDelegate.cpp | 31 -- .../k32w0/DefaultTestEventTriggerDelegate.h | 36 -- .../nxp/{k32w => }/k32w0/BLEManagerImpl.cpp | 2 +- .../nxp/{k32w => }/k32w0/BLEManagerImpl.h | 2 +- src/platform/nxp/{k32w => }/k32w0/BUILD.gn | 40 +- .../nxp/{k32w => }/k32w0/BlePlatformConfig.h | 0 .../k32w0/CHIPDevicePlatformConfig.h | 10 +- .../k32w0/CHIPDevicePlatformEvent.h | 0 .../CHIPDevicePlatformRamStorageConfig.h | 185 +++++++ .../nxp/{k32w => }/k32w0/CHIPPlatformConfig.h | 0 .../k32w0/ConfigurationManagerImpl.cpp | 4 +- .../k32w0/ConfigurationManagerImpl.h | 2 +- .../k32w0/ConnectivityManagerImpl.cpp | 0 .../k32w0/ConnectivityManagerImpl.h | 0 .../k32w0/DiagnosticDataProviderImpl.cpp | 2 +- .../k32w0/DiagnosticDataProviderImpl.h | 0 .../nxp/k32w0/FactoryDataProvider.cpp | 368 ++++++++++++++ src/platform/nxp/k32w0/FactoryDataProvider.h | 158 ++++++ .../k32w0/FactoryDataProviderImpl.cpp | 12 +- .../k32w0/FactoryDataProviderImpl.h | 6 +- .../nxp/{k32w => }/k32w0/InetPlatformConfig.h | 0 .../nxp/{k32w => }/k32w0/K32W0Config.cpp | 2 +- .../nxp/{k32w => }/k32w0/K32W0Config.h | 2 +- .../k32w0/KeyValueStoreManagerImpl.cpp | 21 +- .../k32w0/KeyValueStoreManagerImpl.h | 4 +- src/platform/nxp/{k32w => }/k32w0/Logging.cpp | 0 .../nxp/{k32w => }/k32w0/LowPowerHooks.cpp | 0 .../nxp/{k32w => }/k32w0/NFCManagerImpl.cpp | 0 .../nxp/{k32w => }/k32w0/NFCManagerImpl.h | 0 .../k32w0/OTAFactoryDataProcessor.cpp | 7 +- .../k32w0/OTAFactoryDataProcessor.h | 6 +- .../{k32w => }/k32w0/OTAFirmwareProcessor.cpp | 6 +- .../{k32w => }/k32w0/OTAFirmwareProcessor.h | 2 +- .../nxp/{k32w => }/k32w0/OTAHooks.cpp | 20 +- .../{k32w => }/k32w0/PlatformManagerImpl.cpp | 4 +- .../{k32w => }/k32w0/PlatformManagerImpl.h | 0 .../nxp/{k32w => }/k32w0/RamStorage.cpp | 2 +- .../nxp/{k32w => }/k32w0/RamStorage.h | 0 .../{k32w => }/k32w0/SystemPlatformConfig.h | 0 .../{k32w => }/k32w0/SystemTimeSupport.cpp | 0 .../k32w0/ThreadStackManagerImpl.cpp | 0 .../{k32w => }/k32w0/ThreadStackManagerImpl.h | 0 src/platform/nxp/{k32w => }/k32w0/args.gni | 8 +- .../nxp/{k32w => }/k32w0/ble_function_mux.c | 0 .../nxp/{k32w => }/k32w0/ble_function_mux.h | 0 .../crypto/CHIPCryptoPALNXPUltrafastP256.cpp | 0 src/platform/nxp/{k32w => }/k32w0/gatt_db.h | 0 .../nxp/{k32w => }/k32w0/gatt_uuid128.h | 0 .../k32w0/k32w0-chip-mbedtls-config.h | 0 .../platforms/nxp/{k32w => }/k32w0/BUILD.gn | 8 +- 86 files changed, 1265 insertions(+), 676 deletions(-) delete mode 120000 examples/contact-sensor-app/nxp/k32w/k32w0/build_overrides delete mode 120000 examples/contact-sensor-app/nxp/k32w/k32w0/third_party/connectedhomeip rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/.gn (93%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/BUILD.gn (90%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/README.md (71%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/args.gni (100%) create mode 120000 examples/contact-sensor-app/nxp/k32w0/build_overrides rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/include/CHIPProjectConfig.h (98%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/include/FreeRTOSConfig.h (100%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/main/AppTask.cpp (96%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/main/ContactSensorManager.cpp (100%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/main/ZclCallbacks.cpp (100%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/main/include/AppEvent.h (100%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/main/include/AppTask.h (95%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/main/include/ContactSensorManager.h (100%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/main/include/app_config.h (100%) rename examples/contact-sensor-app/nxp/{k32w => }/k32w0/main/main.cpp (98%) create mode 120000 examples/contact-sensor-app/nxp/k32w0/third_party/connectedhomeip delete mode 120000 examples/lighting-app/nxp/k32w/k32w0/build_overrides delete mode 120000 examples/lighting-app/nxp/k32w/k32w0/third_party/connectedhomeip rename examples/lighting-app/nxp/{k32w => }/k32w0/.gn (94%) rename examples/lighting-app/nxp/{k32w => }/k32w0/BUILD.gn (87%) rename examples/lighting-app/nxp/{k32w => }/k32w0/README.md (71%) rename examples/lighting-app/nxp/{k32w => }/k32w0/args.gni (100%) create mode 120000 examples/lighting-app/nxp/k32w0/build_overrides rename examples/lighting-app/nxp/{k32w => }/k32w0/include/CHIPProjectConfig.h (97%) rename examples/lighting-app/nxp/{k32w => }/k32w0/include/FreeRTOSConfig.h (100%) rename examples/lighting-app/nxp/{k32w => }/k32w0/main/AppTask.cpp (98%) rename examples/lighting-app/nxp/{k32w => }/k32w0/main/LightingManager.cpp (100%) rename examples/lighting-app/nxp/{k32w => }/k32w0/main/ZclCallbacks.cpp (100%) rename examples/lighting-app/nxp/{k32w => }/k32w0/main/include/AppEvent.h (100%) rename examples/lighting-app/nxp/{k32w => }/k32w0/main/include/AppTask.h (95%) rename examples/lighting-app/nxp/{k32w => }/k32w0/main/include/LightingManager.h (100%) rename examples/lighting-app/nxp/{k32w => }/k32w0/main/include/app_config.h (100%) rename examples/lighting-app/nxp/{k32w => }/k32w0/main/main.cpp (97%) create mode 120000 examples/lighting-app/nxp/k32w0/third_party/connectedhomeip delete mode 100644 src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.cpp delete mode 100644 src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h rename src/platform/nxp/{k32w => }/k32w0/BLEManagerImpl.cpp (98%) rename src/platform/nxp/{k32w => }/k32w0/BLEManagerImpl.h (97%) rename src/platform/nxp/{k32w => }/k32w0/BUILD.gn (77%) rename src/platform/nxp/{k32w => }/k32w0/BlePlatformConfig.h (100%) rename src/platform/nxp/{k32w => }/k32w0/CHIPDevicePlatformConfig.h (97%) rename src/platform/nxp/{k32w => }/k32w0/CHIPDevicePlatformEvent.h (100%) create mode 100644 src/platform/nxp/k32w0/CHIPDevicePlatformRamStorageConfig.h rename src/platform/nxp/{k32w => }/k32w0/CHIPPlatformConfig.h (100%) rename src/platform/nxp/{k32w => }/k32w0/ConfigurationManagerImpl.cpp (98%) rename src/platform/nxp/{k32w => }/k32w0/ConfigurationManagerImpl.h (98%) rename src/platform/nxp/{k32w => }/k32w0/ConnectivityManagerImpl.cpp (100%) rename src/platform/nxp/{k32w => }/k32w0/ConnectivityManagerImpl.h (100%) rename src/platform/nxp/{k32w => }/k32w0/DiagnosticDataProviderImpl.cpp (99%) rename src/platform/nxp/{k32w => }/k32w0/DiagnosticDataProviderImpl.h (100%) create mode 100644 src/platform/nxp/k32w0/FactoryDataProvider.cpp create mode 100644 src/platform/nxp/k32w0/FactoryDataProvider.h rename src/platform/nxp/{k32w => }/k32w0/FactoryDataProviderImpl.cpp (94%) rename src/platform/nxp/{k32w => }/k32w0/FactoryDataProviderImpl.h (88%) rename src/platform/nxp/{k32w => }/k32w0/InetPlatformConfig.h (100%) rename src/platform/nxp/{k32w => }/k32w0/K32W0Config.cpp (99%) rename src/platform/nxp/{k32w => }/k32w0/K32W0Config.h (98%) rename src/platform/nxp/{k32w => }/k32w0/KeyValueStoreManagerImpl.cpp (93%) rename src/platform/nxp/{k32w => }/k32w0/KeyValueStoreManagerImpl.h (95%) rename src/platform/nxp/{k32w => }/k32w0/Logging.cpp (100%) rename src/platform/nxp/{k32w => }/k32w0/LowPowerHooks.cpp (100%) rename src/platform/nxp/{k32w => }/k32w0/NFCManagerImpl.cpp (100%) rename src/platform/nxp/{k32w => }/k32w0/NFCManagerImpl.h (100%) rename src/platform/nxp/{k32w => }/k32w0/OTAFactoryDataProcessor.cpp (97%) rename src/platform/nxp/{k32w => }/k32w0/OTAFactoryDataProcessor.h (93%) rename src/platform/nxp/{k32w => }/k32w0/OTAFirmwareProcessor.cpp (96%) rename src/platform/nxp/{k32w => }/k32w0/OTAFirmwareProcessor.h (96%) rename src/platform/nxp/{k32w => }/k32w0/OTAHooks.cpp (87%) rename src/platform/nxp/{k32w => }/k32w0/PlatformManagerImpl.cpp (98%) rename src/platform/nxp/{k32w => }/k32w0/PlatformManagerImpl.h (100%) rename src/platform/nxp/{k32w => }/k32w0/RamStorage.cpp (99%) rename src/platform/nxp/{k32w => }/k32w0/RamStorage.h (100%) rename src/platform/nxp/{k32w => }/k32w0/SystemPlatformConfig.h (100%) rename src/platform/nxp/{k32w => }/k32w0/SystemTimeSupport.cpp (100%) rename src/platform/nxp/{k32w => }/k32w0/ThreadStackManagerImpl.cpp (100%) rename src/platform/nxp/{k32w => }/k32w0/ThreadStackManagerImpl.h (100%) rename src/platform/nxp/{k32w => }/k32w0/args.gni (92%) rename src/platform/nxp/{k32w => }/k32w0/ble_function_mux.c (100%) rename src/platform/nxp/{k32w => }/k32w0/ble_function_mux.h (100%) rename src/platform/nxp/{k32w => }/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp (100%) rename src/platform/nxp/{k32w => }/k32w0/gatt_db.h (100%) rename src/platform/nxp/{k32w => }/k32w0/gatt_uuid128.h (100%) rename src/platform/nxp/{k32w => }/k32w0/k32w0-chip-mbedtls-config.h (100%) rename third_party/openthread/platforms/nxp/{k32w => }/k32w0/BUILD.gn (93%) diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/build_overrides b/examples/contact-sensor-app/nxp/k32w/k32w0/build_overrides deleted file mode 120000 index ad07557834803a..00000000000000 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/build_overrides +++ /dev/null @@ -1 +0,0 @@ -../../../../build_overrides/ \ No newline at end of file diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/third_party/connectedhomeip b/examples/contact-sensor-app/nxp/k32w/k32w0/third_party/connectedhomeip deleted file mode 120000 index 305f2077ffe860..00000000000000 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/third_party/connectedhomeip +++ /dev/null @@ -1 +0,0 @@ -../../../../../.. \ No newline at end of file diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/.gn b/examples/contact-sensor-app/nxp/k32w0/.gn similarity index 93% rename from examples/contact-sensor-app/nxp/k32w/k32w0/.gn rename to examples/contact-sensor-app/nxp/k32w0/.gn index 363727423ce903..1f7e4941f8d9fc 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/.gn +++ b/examples/contact-sensor-app/nxp/k32w0/.gn @@ -27,5 +27,5 @@ default_args = { import("//args.gni") # Import default platform configs - import("${chip_root}/src/platform/nxp/k32w/k32w0/args.gni") + import("${chip_root}/src/platform/nxp/k32w0/args.gni") } diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/BUILD.gn b/examples/contact-sensor-app/nxp/k32w0/BUILD.gn similarity index 90% rename from examples/contact-sensor-app/nxp/k32w/k32w0/BUILD.gn rename to examples/contact-sensor-app/nxp/k32w0/BUILD.gn index b69897172a1e8d..8ca969aaaba95c 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/BUILD.gn +++ b/examples/contact-sensor-app/nxp/k32w0/BUILD.gn @@ -41,7 +41,7 @@ if (chip_pw_tokenizer_logging) { assert(current_os == "freertos") -k32w0_platform_dir = "${chip_root}/examples/platform/nxp/k32w/k32w0" +k32w0_platform_dir = "${chip_root}/examples/platform/nxp/k32w0" k32w0_sdk("sdk") { sources = [ @@ -88,6 +88,7 @@ k32w0_executable("contact_sensor_app") { defines = [] sources = [ + "${k32w0_platform_dir}/util/DefaultTestEventTriggerDelegate.cpp", "${k32w0_platform_dir}/util/LEDWidget.cpp", "${k32w0_platform_dir}/util/include/LEDWidget.h", "main/AppTask.cpp", @@ -119,17 +120,14 @@ k32w0_executable("contact_sensor_app") { "${k32w0_platform_dir}/app/support:freertos_mbedtls_utils", ] - if (chip_openthread_ftd) { - deps += [ - "${chip_root}/third_party/openthread/repo:libopenthread-cli-ftd", - "${chip_root}/third_party/openthread/repo:libopenthread-ftd", - ] - } else { - deps += [ - "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd", - "${chip_root}/third_party/openthread/repo:libopenthread-mtd", - ] - } + defines += [ + "CONNECTIVITY_MANAGER_THREAD_DEVICE_TYPE=ConnectivityManager::kThreadDeviceType_SleepyEndDevice" + ] + + deps += [ + "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd", + "${chip_root}/third_party/openthread/repo:libopenthread-mtd", + ] #lit and sit are using different zap files if (chip_enable_icd_lit) { @@ -187,6 +185,11 @@ group("k32w0") { ":binsign", ":contact_sensor_app", ] + + if (chip_enable_ota_requestor) { + deps += [ "${k32w0_platform_dir}/ssbl:ssbl" ] + } + if (chip_pw_tokenizer_logging) { deps += [ ":contact_sensor_app.database" ] } @@ -201,6 +204,10 @@ action("binsign") { if (chip_simple_hash_verification == 1) { args = [ "--simple-hash" ] } + + if (chip_enable_ota_requestor) { + args = [ "--ota-enabled" ] + } } group("default") { diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/README.md b/examples/contact-sensor-app/nxp/k32w0/README.md similarity index 71% rename from examples/contact-sensor-app/nxp/k32w/k32w0/README.md rename to examples/contact-sensor-app/nxp/k32w0/README.md index ac418ecbd4aa77..542dc31f314a0a 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/README.md +++ b/examples/contact-sensor-app/nxp/k32w0/README.md @@ -14,46 +14,41 @@ network.
-- [CHIP K32W061 Contact Sensor Example Application](#chip-k32w061-contact-sensor-example-application) - - [Introduction](#introduction) - - [SE051H Secure Element](#se051h-secure-element) - - [Bluetooth LE Advertising](#bluetooth-le-advertising) - - [LIT ICD Active Mode](#lit-icd-active-mode) - - [Bluetooth LE Rendezvous](#bluetooth-le-rendezvous) - - [Thread Provisioning](#thread-provisioning) - - [Device UI](#device-ui) - - [No expansion board](#no-expansion-board) - - [Building](#building) - - [Long Idle Time ICD Support](#long-idle-time-icd-support) - - [Overwrite board config files](#overwrite-board-config-files) - - [Known issues building](#known-issues-building) - - [Rotating device id](#rotating-device-id) - - [Manufacturing data](#manufacturing-data) - - [Flashing and debugging](#flashing-and-debugging) - - [Pigweed tokenizer](#pigweed-tokenizer) - - [Detokenizer script](#detokenizer-script) - - [Notes](#notes) - - [Known issues tokenizer](#known-issues-tokenizer) - - [NXP Ultrafast P256 ECC Library](#nxp-ultrafast-p256-ecc-library) - - [Building steps](#building-steps) - - [Tinycrypt ECC library](#tinycrypt-ecc-library) - - [Building steps](#building-steps-1) - - [OTA](#ota) - - [Writing the SSBL](#writing-the-ssbl) - - [Features](#features) - - [Multi image](#multi-image) - - [Simple hash verification](#simple-hash-verification) - - [Writing the PSECT](#writing-the-psect) - - [Writing the application](#writing-the-application) - - [OTA Testing](#ota-testing) - - [Known issues ota](#known-issues-ota) - - [Low power](#low-power) - - [Known issues low power](#known-issues-low-power) - - [Removing SSBL Upgrade Region](#removing-ssbl-upgrade-region) +- [CHIP K32W061 Contact Sensor Example Application](#chip-k32w061-contact-sensor-example-application) + - [Introduction](#introduction) + - [SE051H Secure Element](#se051h-secure-element) + - [Bluetooth LE Advertising](#bluetooth-le-advertising) + - [LIT ICD Active Mode](#lit-icd-active-mode) + - [Bluetooth LE Rendezvous](#bluetooth-le-rendezvous) + - [Thread Provisioning](#thread-provisioning) + - [Device UI](#device-ui) + - [No expansion board](#no-expansion-board) + - [Building](#building) + - [Long Idle Time ICD Support](#long-idle-time-icd-support) + - [Overwrite board config files](#overwrite-board-config-files) + - [Known issues building](#known-issues-building) + - [Rotating device id](#rotating-device-id) + - [Manufacturing data](#manufacturing-data) + - [Flashing and debugging](#flashing-and-debugging) + - [Using DK6programmer](#using-dk6programmer) + - [Using MCUXpresso](#using-mcuxpresso) + - [Pigweed tokenizer](#pigweed-tokenizer) + - [Detokenizer script](#detokenizer-script) + - [Notes](#notes) + - [Known issues tokenizer](#known-issues-tokenizer) + - [NXP Ultrafast P256 ECC Library](#nxp-ultrafast-p256-ecc-library) + - [Building steps](#building-steps) + - [Tinycrypt ECC library](#tinycrypt-ecc-library) + - [Building steps](#building-steps-1) + - [OTA](#ota) + - [OTA Testing](#ota-testing) + - [Known issues OTA](#known-issues-ota) + - [Low power](#low-power) + - [Known issues low power](#known-issues-low-power) ## Introduction -![K32W061 DK6](../../../../platform/nxp/k32w/k32w0/doc/images/k32w-dk6.jpg) +![K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-dk6.jpg) The K32W061 contact sensor example application provides a working demonstration of a connected contact sensor device, built using the Project CHIP codebase and @@ -83,7 +78,7 @@ Deployment of this firmware configuration requires the K32W061 board setups using the K32W061 module board, SE051 Expansion board and Generic Expansion board as shown below: -![SE051H + K32W061 DK6](../../../../platform/nxp/k32w/k32w0/doc/images/k32w-se.jpg) +![SE051H + K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-se.jpg) The SE051H Secure Element extension may be used for best in class security and offloading some of the Project CHIP cryptographic operations. Depending on your @@ -186,10 +181,10 @@ contact status. In order to build the Project CHIP example, we recommend using a Linux distribution (supported Operating Systems are listed in -[BUILDING.md](../../../../../docs/guides/BUILDING.md#tested-operating systems)). +[BUILDING.md](../../../../docs/guides/BUILDING.md#tested-operating-systems)). - Make sure that below prerequisites are correctly installed (as described in - [BUILDING.md](../../../../../docs/guides/BUILDING.md#prerequisites))) + [BUILDING.md](../../../../docs/guides/BUILDING.md#prerequisites)) ``` sudo apt-get install git gcc g++ pkg-config libssl-dev libdbus-1-dev \ @@ -228,9 +223,9 @@ SDKs. Arg "-- help" could be used to view all available options. - Start building the application: ```bash -user@ubuntu:~/Desktop/git/connectedhomeip$ cd examples/contact-sensor-app/nxp/k32w/k32w0 -user@ubuntu:~/Desktop/git/connectedhomeip/examples/contact-sensor-app/nxp/k32w/k32w0$ gn gen out/debug -user@ubuntu:~/Desktop/git/connectedhomeip/examples/contact-sensor-app/nxp/k32w/k32w0$ ninja -C out/debug +user@ubuntu:~/Desktop/git/connectedhomeip$ cd examples/contact-sensor-app/nxp/k32w0 +user@ubuntu:~/Desktop/git/connectedhomeip/examples/contact-sensor-app/nxp/k32w0$ gn gen out/debug +user@ubuntu:~/Desktop/git/connectedhomeip/examples/contact-sensor-app/nxp/k32w0$ ninja -C out/debug ``` To build with Secure Element, follow the same steps as above but set @@ -357,17 +352,17 @@ Please use the following build args: ## Manufacturing data See -[Guide for writing manufacturing data on NXP devices](../../../../../docs/guides/nxp/nxp_manufacturing_flow.md). +[Guide for writing manufacturing data on NXP devices](../../../../docs/guides/nxp/nxp_manufacturing_flow.md). There are factory data generated binaries available in -examples/platform/nxp/k32w/k32w0/scripts/demo_generated_factory_data folder. +examples/platform/nxp/k32w0/scripts/demo_generated_factory_data folder. These are based on the DAC, PAI and PAA certificates found in scripts/tools/nxp/demo_generated_certs folder. The demo_factory_data_dut1.bin uses the DAC certificate and private key found in -examples/platform/nxp/k32w/k32w0/scripts/demo_generated_factory_data/dac/dut1 +examples/platform/nxp/k32w0/scripts/demo_generated_factory_data/dac/dut1 folder. The demo_factory_data_dut2.bin uses the DAC certificate and private key found in -examples/platform/nxp/k32w/k32w0/scripts/demo_generated_factory_data/dac/dut2 +examples/platform/nxp/k32w0/scripts/demo_generated_factory_data/dac/dut2 folder. These two factory data binaries can be used for testing topologies with 2 DUTS. They contain the corresponding DACs/PAIs generated using generate_nxp_chip_factory_bin.py script. The discriminator is 14014 and the @@ -379,18 +374,128 @@ Regarding factory data provider, there are two options: - use the default factory data provider: `FactoryDataProviderImpl` by setting `chip_with_factory_data=1` in the gn build command. - use a custom factory data provider: please see - [Guide for implementing a custom factory data provider](../../../../platform/nxp/k32w/k32w0/common/README.md). + [Guide for implementing a custom factory data provider](../../../platform/nxp/k32w0/common/README.md). This can be enabled when `chip_with_factory_data=1` by setting `use_custom_factory_provider=1` in the gn build command. ## Flashing and debugging -Program the firmware using the official +Instructions to program the firmware can be found also at [OpenThread Flash Instructions](https://github.com/openthread/ot-nxp/tree/main/src/k32w0/k32w061#flash-binaries). -All you have to do is to replace the Openthread binaries from the above -documentation with _out/debug/chip-k32w0x-contact-example.bin_ if DK6Programmer -is used or with _out/debug/chip-k32w0x-contact-example_ if MCUXpresso is used. +### Using DK6programmer + +The application binary's path is _out/debug/chip-k32w0x-contact-example.bin_. + +DK6Programmer can be used for flashing the application. There are two available versions of the +DK6Programmer tool. + +The legacy version consists of a Windows executable found inside the +[SDK](https://mcuxpresso.nxp.com/en/welcome) at path `tools/JN-SW-4407-DK6-Flash-Programmer`. This is a +Windows application that can be installed using the .exe file. Once the +application is installed, the COM port for K32W061 must be identified: + +``` +C:\nxp\DK6ProductionFlashProgrammer>DK6Programmer.exe --list +Available connections: + +``` + +Once the COM port is identified, the required binary can be flashed: + +``` +DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x4000="chip-k32w0x-contact-example.bin" +``` + +> **_Note:_** The above example takes into account that the binary uses the `chip_enable_ota_requestor=true` option. +The address offset corresponds to the space left for the SSBL binary and the OTA space for the SSBL. If +`chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be replaced with `0x0`. + +DK6 Flash Programmer tool has also been integrated part of +[NXP Secure Provisioning SDK (SPSDK)](https://github.com/nxp-mcuxpresso/spsdk). This tool is supported by +environments like Windows, Linux or Mac. + +SPSDK can be installed and run from a Python environment using [these instructions](https://spsdk.readthedocs.io/en/latest/usage/installation.html). +This enables the user to have transparent access to the dk6 programming tool through SPSDK. + +``` +# after specific environment installation steps +$ spsdk --help +... + +-- dk6prog Tool for reading and programming flash memory of DK6 target devices. + � +-- erase Erase the memory. + � +-- info Prints the information about the connected device. + � +-- isp Issues ISP sequence as defined in Driver interface. + � +-- listdev Prints the information about the connected devices. + � +-- read Reads the memory and writes it to the file or stdout. + � +-- write Write the memory. +... +``` + +Dependencies for the dk6prog module can be installed using the following command, more details [here](https://spsdk.readthedocs.io/en/latest/usage/installation.html#dk6-tools): + +``` +$ pip install spsdk[dk6] +``` + +The SPSDK installation adds dk6prog as executable to system path, so user can use directly `dk6prog` from terminal. +The following commands are to be used to write the chip-k32w0x-contact-example binary to the board. + +``` +$ dk6prog listdev +This is an experimental utility. Use with caution! + +List of available devices: +DEVICE ID: DN038ZH3, VID: 0x403, PID: 0x6015, Serial number: DN038ZH3, Description: DK6 Carrier Board, Address: 9, Backend: Backend.PYFTDI +$ dk6prog -d DN038ZH3 write 0x4000 ~/path/to/bin/chip-k32w0x-contact-example.bin + +This is an experimental utility. Use with caution! + +Writing memory [####################################] 100% +Writen 596450 bytes to memory ID 0 at address 0x4000 +``` + +> **_Note:_** Running `dk6prog` from Windows OS command line requires an integer value for DEVICE ID. + +``` +C:\nxp\spsdk>dk6prog listdev + +This is an experimental utility. Use with caution! + +List of available devices: +DEVICE ID: 0, VID: 0x0, PID: 0x0, Serial number: b'DN038ZH3', Description: b'DK6 Carrier Board', Address: 67330069, Backend: Backend.FTD2xx + +C:\nxp\spsdk>dk6prog -d 0 info + +This is an experimental utility. Use with caution! + +Chip ID: 0x88888888 +ROM Version: 0x140000cc +MAC Address: A8:2B:1F:03:00:8D:15:00 + +Detected DEVICE: UNKNOWN + + Memory Memory ID Base Address Length Sector Size Memory Type Access +---------------------------------------------------------------------------------------------- + FLASH 0 0x0 0x9de00 0x200 FLASH All is available + PSECT 1 0x0 0x1e0 0x10 FLASH All is available + pFLASH 2 0x0 0x1e0 0x10 FLASH All is available + Config 3 0x9fc00 0x200 0x200 FLASH All is available + EFUSE 4 0x0 0x80 0x2 EFUSE (OTP) Write Enabled + ROM 5 0x3000000 0x20000 0x1 ROM Write Enabled + RAM0 6 0x4000000 0x16000 0x1 RAM Write Enabled + RAM1 7 0x4020000 0x10000 0x1 RAM Write Enabled +``` + +> **_Note:_** The above example takes into account that the binary uses the `chip_enable_ota_requestor=true` option. +The address offset corresponds to the space left for the SSBL binary and the OTA space for the SSBL. If +`chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be replaced with `0x0`. + +### Using MCUXpresso + +If flashing and debugging is required, MCUXpresso can be used as instructed in +[MCUXpresso flashing and debugging instructions](https://github.com/openthread/ot-nxp/tree/main/src/k32w0/k32w061#using-mcuxpresso-ide). +The file needed to be used in MCUXpresso is _out/debug/chip-k32w0x-contact-example_. ## Pigweed tokenizer @@ -403,7 +508,7 @@ needed for parsing the hashed scripts. The python3 script detokenizer.py is a script that decodes the tokenized logs either from a file or from a serial port. It is located in the following path -`examples/platform/nxp/k32w/k32w0/scripts/detokenizer.py`. +`examples/platform/nxp/k32w0/scripts/detokenizer.py`. The script can be used in the following ways: @@ -438,7 +543,7 @@ by the script is loaded by the environment. An example of running the detokenizer script to see logs of a contact-sensor app: ``` -python3 ../../../../../examples/platform/nxp/k32w/k32w0/scripts/detokenizer.py serial -i /dev/ttyACM0 -d out/debug/chip-k32w0x-contact-example-database.bin -o device.txt +python3 ../../../../examples/platform/nxp/k32w0/scripts/detokenizer.py serial -i /dev/ttyACM0 -d out/debug/chip-k32w0x-contact-example-database.bin -o device.txt ``` ### Known issues tokenizer @@ -483,179 +588,41 @@ In order to use the Tinycrypt ECC library, use the following build arguments: ## OTA -The internal flash needs to be prepared for the OTA process. First 16K of the -internal flash needs to be populated with a Secondary Stage Bootloader (SSBL) -related data while the last 8.5K of flash space is holding image directory -related data (PSECT). The space between these two zones will be filled by the -application. - -### Writing the SSBL - -The SDK already provides an SSBL binary compiled with external flash support: -`boards/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl_ext_flash_pdm_support.bin`, -but it does not offer multi-image OTA support. - -Alternatively, the SSBL can ge generated from one of the SDK demo examples. The -SSBL demo application can be imported from the `Quickstart panel`: -`Import SDK example(s) -> select wireless -> framework -> ssbl` application. - -![SSBL Application Select](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_select.JPG) - -### Features - -#### Multi image - -To support multi-image OTA feature, the SSBL project must be compiled using the -following defines: - -- `PDM_EXT_FLASH=1` - support PDM in external flash. -- `gOTAUseCustomOtaEntry=1` - support custom OTA entry for multi-image. -- `gOTACustomOtaEntryMemory=OTACustomStorage_ExtFlash` - K32W0 uses - `OTACustomStorage_ExtFlash` (1) by default. -- `SPIFI_DUAL_MODE_SUPPORT=1` - only for configurations that use dual `SPIFI` - flash (e.g. K32W041AM variant). - -Optionally, add the following defines: - -- `SPIFI_OPTIM_SIZE=1` - to optimize SSBL size. -- `EXTERNAL_FLASH_DATA_OTA=1` - to support external read only data. - -![SSBL_MULTI_IMAGE](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_multi_image.JPG) - -#### Simple hash verification - -When secure boot is not used, a simple hash can be appended at the end of the -image for integrity check. Applications should be built with -`chip_simple_hash_verification=1`. - -To support simple hash verification feature, the SSBL project must be compiled -with: - -- `gSimpleHashVerification=1` +Over the air updates (OTA) require several software components running on the K32W0x1. +Firstly, a Secondary Stage Bootloader (SSBL) is required written in the first part of the +internal flash memory, usually starting at address 0x0. This enables the board to boot and +check if a new OTA binary has been received. If this is true, the bootloader writes the OTA +binary to the appropriate storage, internal and/or external flash, after which it reboots +the board. If no new OTA binaries have been found, then the bootloader gives execution control +to the application. -and update the post-build command to use simple hash verification instead of the -default options. Go to -`Project -> Properties -> C/C++ Build -> Settings -> Build steps` and press -`Edit` under `Post-build steps` subsection. The command should look similar to: +The internal flash needs to be prepared for the OTA process. First 16K of the internal flash +needs to be populated with SSBL related data while the last 8.5K of flash space is holding flash +configuration related data. The space between these two zones will be filled by the application. +More details regarding the internal flash space can be found in the +[linker file](../../../platform/nxp/k32w0/app/ldscripts/chip-k32w0x-linker.ld). -![SSBL_SIMPLE_HASH_VERIFICATION](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG) +The steps for building the SSBL binary with appropriate configuration and writing to the board +the binary and other OTA related configurations are descrived in the +[K32W0x1 OTA guide](../../../../docs/guides/nxp/nxp_k32w0_ota_guide.md). -Once compiled, the required SSBL file is called `k32w061dk6_ssbl.bin`. - -![SSBL_BIN](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_bin.JPG) - -Before writing the SSBL, it it recommanded to fully erase the internal flash: - -``` -DK6Programmer.exe -V 5 -P 1000000 -s -e Flash -``` - -`k32w061dk6_ssbl.bin` must be written at address 0 in the internal flash: - -``` -DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x00="k32w061dk6_ssbl.bin" -``` - -### Writing the PSECT - -This is the list of all supported partitions: - -``` -0000000010000000 : SSBL partition - - 00000000 -----------> Start Address - 1000 ---------------> 0x0010 Number of 512-bytes pages - 00 -----------------> 0x00 Bootable flag - 00 -----------------> 0x00 Image type (0x00 = SSBL) - -00400000c9040101: Application partition - - 00400000 -----------> 0x00004000 Start Address - c904 ---------------> 0x04c9 Number of 512-bytes pages - 01 -----------------> 0x01 Bootable flag - 01 -----------------> 0x01 Image type (0x01 = Application) - -00000010800000fe: Ext Flash text partition - - 00000010 -----------> 0x10000000 Start Address (external flash) - 8000 ---------------> 0x0080 Number of 512-bytes pages - 00 -----------------> 0x00 Bootable flag - fe -----------------> 0xFE Image type (0xFE = Ext Flash text) - -00000110300200fc : OTA Image partition - - 00000110 -----------> 0x10010000 Start Address - 3002----------------> 0x0230 Number of 512-bytes pages - 00 -----------------> 0x00 Bootable flag - fc -----------------> 0xFC Image type (0xFC = OTA partition) - -00000510100000fd: NVM partition - - 00000510 -----------> 0x10050000 Start Address - 1000 ---------------> 0x0010 Number of 512-bytes pages - 00 -----------------> 0x00 Bootable flag - fd -----------------> 0xFD Image type (0xFD = NVM partition) -``` - -First, image directory 0 (SSBL partition) must be written: - -``` -DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_0=0000000010000000 -``` - -Here is the interpretation of the fields: - -``` -00000000 -> start address 0x00000000 -1000 -> size = 0x0010 pages of 512-bytes (= 8kB) -00 -> not bootable (only used by the SSBL to support SSBL update) -00 -> SSBL Image Type -``` - -Second, image directory 1 (application partition) must be written: - -``` -DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_1=00400000C9040101 -``` - -Here is the interpretation of the fields: - -``` -00400000 -> start address 0x00004000 -C904 -> 0x4C9 pages of 512-bytes (= 612.5kB) -01 -> bootable flag -01 -> image type for the application -``` - -Please note the user can write additional partitions by writing -`image_dir_2/3/4` with the wanted configuration. - -### Writing the application - -DK6Programmer can be used for flashing the application: - -``` -DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x4000="chip-k32w0x-contact-example.bin" -``` - -If debugging is needed, MCUXpresso can be used then for flashing the -application. Please make sure that the application is written at address 0x4000: - -![FLASH_LOCATION](../../../../platform/nxp/k32w/k32w0/doc/images/flash_location.JPG) +Note that the application needs to be built using the `chip_enable_ota_requestor=true` option. +This is enabled in the configuration by default if no `chip_enable_ota_requestor` explicit setting +is done. ### OTA Testing The OTA topology used for OTA testing is illustrated in the figure below. Topology is similar with the one used for Matter Test Events. -![OTA_TOPOLOGY](../../../../platform/nxp/k32w/k32w0/doc/images/ota_topology.JPG) +![OTA_TOPOLOGY](../../../platform/nxp/k32w0/doc/images/ota_topology.JPG) The concept for OTA is the next one: - there is an OTA Provider Application that holds the OTA image. In our case, this is a Linux application running on an Ubuntu based-system; -- the OTA Requestor functionality is embedded inside the Lighting Application. - It will be used for requesting OTA blocks from the OTA Provider; +- the OTA Requestor functionality is embedded inside the Contact Sensor + Application. It will be used for requesting OTA blocks from the OTA Provider; - the controller (a linux application called chip-tool) will be used for commissioning both the device and the OTA Provider App. The device will be commissioned using the standard Matter flow (BLE + IEEE 802.15.4) while the @@ -663,17 +630,17 @@ The concept for OTA is the next one: of chip-tool; - during commissioning, each device is assigned a node id by the chip-tool (can be specified manually by the user). Using the node id of the device and - of the lighting application, chip-tool triggers the OTA transfer by invoking - the _announce-ota-provider_ command - basically, the OTA Requestor is + of the contact sensor application, chip-tool triggers the OTA transfer by + invoking the _announce-ota-provider_ command - basically, the OTA Requestor is informed of the node id of the OTA Provider Application. _Computer #1_ can be any system running an Ubuntu distribution. We recommand -using TE 7.5 instructions from -[here](https://groups.csa-iot.org/wg/matter-csg/document/24839), where RPi 4 are -proposed. Also, TE 7.5 instructions document point to the OS/Docker images that +using CSA official instructions from +[here](https://groups.csa-iot.org/wg/matter-csg/document/28566), where RPi 4 are +proposed. Also, CSA official instructions document point to the OS/Docker images that should be used on the RPis. For compatibility reasons, we recommand compiling chip-tool and OTA Provider applications with the same commit id that was used -for compiling the Lighting Application. Also, please note that there is a single +for compiling the Contact Sensor Application. Also, please note that there is a single controller (chip-tool) running on Computer #1 which is used for commissioning both the device and the OTA Provider Application. If needed, [these instructions](https://itsfoss.com/connect-wifi-terminal-ubuntu/) could be @@ -696,7 +663,7 @@ Build OTA image: In order to build an OTA image, use NXP wrapper over the standard tool `src/app/ota_image_tool.py`: -- `scripts/tools/nxp/ota/ota_image_tool.py`. +- `scripts/tools/nxp/ota/ota_image_tool.py` The tool can be used to generate an OTA image with the following format: @@ -725,7 +692,7 @@ The address for storing the custom OTA entry can also be specified: address that does not overlap with anything else. Please see more in the -[OTA image tool guide](../../../../../scripts/tools/nxp/ota/README.md). +[OTA image tool guide](../../../../scripts/tools/nxp/ota/README.md). Here is an example that generates an OTA image with application update TLV: @@ -740,11 +707,11 @@ configuration. A note regarding OTA image header version (`-vn` option). An application binary has its own software version, given by -`CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION` (`42020` by default), which can be +`CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION` (`1` by default), which can be overwritten. For having a correct OTA process, the OTA header version should be -the same as the binary embedded software version. A user can set a custom -software version in the gn build args by setting `chip_software_version` to the -wanted version. +the same as the binary embedded software version. When building the update image, +the build arguments `nxp_software_version=2` and `nxp_sofware_version_string=\"2.0\"` +can be added to the gn gen command in order to specify the upgraded version. Start the OTA Provider Application: @@ -774,7 +741,7 @@ Start the OTA process: user@computer1:~/connectedhomeip$ : ./out/chip-tool-app/chip-tool otasoftwareupdaterequestor announce-otaprovider 1 0 0 0 2 0 ``` -### Known issues ota +### Known issues OTA - SRP cache on the openthread border router needs to flushed each time a new commissioning process is attempted. For this, factory reset the device, then @@ -827,13 +794,13 @@ Power Measurement Tool can be used inside MCUXpresso for checking the power consumption pattern: Window -> Show View -> Other -> Power Measurement Tool. The configuration for this tool is the next one: -![POWER_CONF](../../../../platform/nxp/k32w/k32w0/doc/images/power_conf.JPG) +![POWER_CONF](../../../platform/nxp/k32w0/doc/images/power_conf.JPG) Also, please make sure that the J14 jumper is set to the _ENABLED_ position and no expansion board is attached to the DK6. A view from this tool is illustrated below: -![POWER_VIEW](../../../../platform/nxp/k32w/k32w0/doc/images/power_view.JPG) +![POWER_VIEW](../../../platform/nxp/k32w0/doc/images/power_view.JPG) Please note that that the Power Measurement Tool is not very accurate and professional tools must be used if exact power consumption needs to be known. @@ -842,34 +809,3 @@ professional tools must be used if exact power consumption needs to be known. - Power Measurement Tool may not work correctly in MCUXpresso versions greater that 11.0.1. - -## Removing SSBL Upgrade Region - -The example also offers the possibility to remove SSBL upgrade region, for -reserving more space for application level. - -A new flag `chip_reduce_ssbl_size` is introduced. In order to remove the SSBL -upgrade region, `chip_reduce_ssbl_size=true` must be provided to the build -system - -The programming method will change: - -- writing image directory 1 should change to - - ``` - DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_1=00200000D9040101 - ``` - - Here is the interpretation of the fields: - - ``` - 00200000 -> start address 0x00002000 - D904 -> 0x4D9 pages of 512-bytes (= 620.5kB) - 01 -> bootable flag - 01 -> image type for the application - ``` - -- Matter application offset address should change to - ``` - DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x2000="chip-k32w0x-contact-example.bin" - ``` diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/args.gni b/examples/contact-sensor-app/nxp/k32w0/args.gni similarity index 100% rename from examples/contact-sensor-app/nxp/k32w/k32w0/args.gni rename to examples/contact-sensor-app/nxp/k32w0/args.gni diff --git a/examples/contact-sensor-app/nxp/k32w0/build_overrides b/examples/contact-sensor-app/nxp/k32w0/build_overrides new file mode 120000 index 00000000000000..ee19c065d619a2 --- /dev/null +++ b/examples/contact-sensor-app/nxp/k32w0/build_overrides @@ -0,0 +1 @@ +../../../build_overrides/ \ No newline at end of file diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h b/examples/contact-sensor-app/nxp/k32w0/include/CHIPProjectConfig.h similarity index 98% rename from examples/contact-sensor-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h rename to examples/contact-sensor-app/nxp/k32w0/include/CHIPProjectConfig.h index 301d53824a7e55..2b0afde85d56fe 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h +++ b/examples/contact-sensor-app/nxp/k32w0/include/CHIPProjectConfig.h @@ -145,11 +145,11 @@ * {MAJOR_VERSION}.0d{MINOR_VERSION} */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "03-2022-te8" +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING NXP_CONFIG_DEVICE_SOFTWARE_VERSION_STRING #endif #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 42020 +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION NXP_CONFIG_DEVICE_SOFTWARE_VERSION #endif #ifndef CHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/include/FreeRTOSConfig.h b/examples/contact-sensor-app/nxp/k32w0/include/FreeRTOSConfig.h similarity index 100% rename from examples/contact-sensor-app/nxp/k32w/k32w0/include/FreeRTOSConfig.h rename to examples/contact-sensor-app/nxp/k32w0/include/FreeRTOSConfig.h diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp b/examples/contact-sensor-app/nxp/k32w0/main/AppTask.cpp similarity index 96% rename from examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp rename to examples/contact-sensor-app/nxp/k32w0/main/AppTask.cpp index d7d9763e491a62..d350a4527d281c 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp +++ b/examples/contact-sensor-app/nxp/k32w0/main/AppTask.cpp @@ -42,11 +42,12 @@ #include #include #include -#include +#include #endif -#include +#include +#include "DefaultTestEventTriggerDelegate.h" #include "Keyboard.h" #include "LED.h" #include "LEDWidget.h" @@ -94,6 +95,11 @@ static chip::DeviceLayer::CustomFactoryDataProvider sCustomFactoryDataProvider; #endif #endif +// This key is for testing/certification only and should not be used in production devices. +// For production devices this key must be provided from factory data. +uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + static Identify gIdentify = { chip::EndpointId{ 1 }, AppTask::OnIdentifyStart, AppTask::OnIdentifyStop, Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator }; @@ -107,7 +113,7 @@ static BDXDownloader gDownloader; constexpr uint16_t requestedOtaBlockSize = 1024; #endif -#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA && CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA && CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR CHIP_ERROR CustomFactoryDataRestoreMechanism(void) { K32W_LOG("This is a custom factory data restore mechanism."); @@ -154,7 +160,7 @@ static void CheckOtaEntry() if (ota_entries.ota_state == otaApplied) { K32W_LOG("OTA successfully applied"); -#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA && CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA && CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR // If this point is reached, it means OTA_CommitCustomEntries was successfully called. // Delete the factory data backup to stop doing a restore when the factory data provider // is initialized. This ensures that both the factory data and app were updated, otherwise @@ -184,7 +190,7 @@ CHIP_ERROR AppTask::Init() if (ContactSensorMgr().Init() != 0) { K32W_LOG("ContactSensorMgr().Init() failed"); - assert(status == 0); + assert(0); } PlatformMgr().AddEventHandler(MatterEventHandler, 0); @@ -196,9 +202,8 @@ CHIP_ERROR AppTask::Init() CheckOtaEntry(); #endif - // Initialize device attestation config #if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR sFactoryDataProvider.RegisterRestoreMechanism(CustomFactoryDataRestoreMechanism); #endif ReturnErrorOnFailure(sFactoryDataProvider.Init()); @@ -299,6 +304,8 @@ void AppTask::InitServer(intptr_t arg) chip::DeviceLayer::SetDeviceInfoProvider(&infoProvider); // Init ZCL Data Model and start server + static DefaultTestEventTriggerDelegate sTestEventTriggerDelegate{ ByteSpan(sTestEventTriggerEnableKey) }; + initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; nativeParams.lockCb = LockOpenThreadTask; nativeParams.unlockCb = UnlockOpenThreadTask; diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/ContactSensorManager.cpp b/examples/contact-sensor-app/nxp/k32w0/main/ContactSensorManager.cpp similarity index 100% rename from examples/contact-sensor-app/nxp/k32w/k32w0/main/ContactSensorManager.cpp rename to examples/contact-sensor-app/nxp/k32w0/main/ContactSensorManager.cpp diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/ZclCallbacks.cpp b/examples/contact-sensor-app/nxp/k32w0/main/ZclCallbacks.cpp similarity index 100% rename from examples/contact-sensor-app/nxp/k32w/k32w0/main/ZclCallbacks.cpp rename to examples/contact-sensor-app/nxp/k32w0/main/ZclCallbacks.cpp diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/include/AppEvent.h b/examples/contact-sensor-app/nxp/k32w0/main/include/AppEvent.h similarity index 100% rename from examples/contact-sensor-app/nxp/k32w/k32w0/main/include/AppEvent.h rename to examples/contact-sensor-app/nxp/k32w0/main/include/AppEvent.h diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/include/AppTask.h b/examples/contact-sensor-app/nxp/k32w0/main/include/AppTask.h similarity index 95% rename from examples/contact-sensor-app/nxp/k32w/k32w0/main/include/AppTask.h rename to examples/contact-sensor-app/nxp/k32w0/main/include/AppTask.h index c3203d78eb543e..3f81915dca23f9 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/main/include/AppTask.h +++ b/examples/contact-sensor-app/nxp/k32w0/main/include/AppTask.h @@ -27,7 +27,7 @@ #include "CHIPProjectConfig.h" #if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA -#include +#include #if CHIP_DEVICE_CONFIG_USE_CUSTOM_PROVIDER #include "CustomFactoryDataProvider.h" #endif @@ -50,6 +50,9 @@ class AppTask { public: +#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA + using FactoryDataProvider = chip::DeviceLayer::FactoryDataProviderImpl; +#endif CHIP_ERROR StartAppTask(); static void AppTaskMain(void * pvParameter); diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/include/ContactSensorManager.h b/examples/contact-sensor-app/nxp/k32w0/main/include/ContactSensorManager.h similarity index 100% rename from examples/contact-sensor-app/nxp/k32w/k32w0/main/include/ContactSensorManager.h rename to examples/contact-sensor-app/nxp/k32w0/main/include/ContactSensorManager.h diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/include/app_config.h b/examples/contact-sensor-app/nxp/k32w0/main/include/app_config.h similarity index 100% rename from examples/contact-sensor-app/nxp/k32w/k32w0/main/include/app_config.h rename to examples/contact-sensor-app/nxp/k32w0/main/include/app_config.h diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/main.cpp b/examples/contact-sensor-app/nxp/k32w0/main/main.cpp similarity index 98% rename from examples/contact-sensor-app/nxp/k32w/k32w0/main/main.cpp rename to examples/contact-sensor-app/nxp/k32w0/main/main.cpp index 56288842a3f5de..9517d7e2cf4309 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/main/main.cpp +++ b/examples/contact-sensor-app/nxp/k32w0/main/main.cpp @@ -154,7 +154,7 @@ extern "C" void main_task(void const * argument) */ sched_enable(); - err = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_SleepyEndDevice); + err = ConnectivityMgr().SetThreadDeviceType(CONNECTIVITY_MANAGER_THREAD_DEVICE_TYPE); if (err != CHIP_NO_ERROR) { goto exit; diff --git a/examples/contact-sensor-app/nxp/k32w0/third_party/connectedhomeip b/examples/contact-sensor-app/nxp/k32w0/third_party/connectedhomeip new file mode 120000 index 00000000000000..3efed95be5dbe9 --- /dev/null +++ b/examples/contact-sensor-app/nxp/k32w0/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../../../ \ No newline at end of file diff --git a/examples/lighting-app/nxp/k32w/k32w0/build_overrides b/examples/lighting-app/nxp/k32w/k32w0/build_overrides deleted file mode 120000 index ad07557834803a..00000000000000 --- a/examples/lighting-app/nxp/k32w/k32w0/build_overrides +++ /dev/null @@ -1 +0,0 @@ -../../../../build_overrides/ \ No newline at end of file diff --git a/examples/lighting-app/nxp/k32w/k32w0/third_party/connectedhomeip b/examples/lighting-app/nxp/k32w/k32w0/third_party/connectedhomeip deleted file mode 120000 index 305f2077ffe860..00000000000000 --- a/examples/lighting-app/nxp/k32w/k32w0/third_party/connectedhomeip +++ /dev/null @@ -1 +0,0 @@ -../../../../../.. \ No newline at end of file diff --git a/examples/lighting-app/nxp/k32w/k32w0/.gn b/examples/lighting-app/nxp/k32w0/.gn similarity index 94% rename from examples/lighting-app/nxp/k32w/k32w0/.gn rename to examples/lighting-app/nxp/k32w0/.gn index cfa8fcb8c07dc4..e0a89b885879e6 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/.gn +++ b/examples/lighting-app/nxp/k32w0/.gn @@ -31,5 +31,5 @@ default_args = { [ "${chip_root}/scripts/setup/requirements.build.txt" ] # Import default platform configs - import("${chip_root}/src/platform/nxp/k32w/k32w0/args.gni") + import("${chip_root}/src/platform/nxp/k32w0/args.gni") } diff --git a/examples/lighting-app/nxp/k32w/k32w0/BUILD.gn b/examples/lighting-app/nxp/k32w0/BUILD.gn similarity index 87% rename from examples/lighting-app/nxp/k32w/k32w0/BUILD.gn rename to examples/lighting-app/nxp/k32w0/BUILD.gn index 82afd7ad22864d..61ec7f0dea4a1c 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/BUILD.gn +++ b/examples/lighting-app/nxp/k32w0/BUILD.gn @@ -40,7 +40,7 @@ if (chip_pw_tokenizer_logging) { assert(current_os == "freertos") -k32w0_platform_dir = "${chip_root}/examples/platform/nxp/k32w/k32w0" +k32w0_platform_dir = "${chip_root}/examples/platform/nxp/k32w0" k32w0_sdk("sdk") { sources = [ @@ -84,7 +84,10 @@ k32w0_sdk("sdk") { k32w0_executable("light_app") { output_name = "chip-k32w0x-light-example" + defines = [] + sources = [ + "${k32w0_platform_dir}/util/DefaultTestEventTriggerDelegate.cpp", "${k32w0_platform_dir}/util/LEDWidget.cpp", "${k32w0_platform_dir}/util/include/LEDWidget.h", "main/AppTask.cpp", @@ -96,15 +99,15 @@ k32w0_executable("light_app") { "main/main.cpp", ] - public = [ "${chip_root}/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h" ] - if (chip_with_factory_data == 1 && use_custom_factory_provider == 1) { sources += [ "${k32w0_platform_dir}/common/CustomFactoryDataProvider.cpp", "${k32w0_platform_dir}/common/CustomFactoryDataProvider.h", ] - defines = [ "CHIP_DEVICE_CONFIG_USE_CUSTOM_PROVIDER=1" ] + defines += [ + "CHIP_DEVICE_CONFIG_USE_CUSTOM_PROVIDER=1", + ] } deps = [ @@ -112,7 +115,6 @@ k32w0_executable("light_app") { "${chip_root}/examples/common/QRCode", "${chip_root}/examples/lighting-app/nxp/zap/", "${chip_root}/examples/providers:device_info_provider", - "${chip_root}/src/app:test-event-trigger", "${chip_root}/src/lib", "${chip_root}/src/platform:syscalls_stub", "${chip_root}/src/platform/logging:default", @@ -120,17 +122,14 @@ k32w0_executable("light_app") { "${k32w0_platform_dir}/app/support:freertos_mbedtls_utils", ] - if (chip_openthread_ftd) { - deps += [ - "${chip_root}/third_party/openthread/repo:libopenthread-cli-ftd", - "${chip_root}/third_party/openthread/repo:libopenthread-ftd", - ] - } else { - deps += [ - "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd", - "${chip_root}/third_party/openthread/repo:libopenthread-mtd", - ] - } + defines += [ + "CONNECTIVITY_MANAGER_THREAD_DEVICE_TYPE=ConnectivityManager::kThreadDeviceType_MinimalEndDevice" + ] + + deps += [ + "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd", + "${chip_root}/third_party/openthread/repo:libopenthread-mtd", + ] cflags = [ "-Wconversion" ] @@ -169,6 +168,11 @@ group("k32w0") { ":binsign", ":light_app", ] + + if (chip_enable_ota_requestor) { + deps += [ "${k32w0_platform_dir}/ssbl:ssbl" ] + } + if (chip_pw_tokenizer_logging) { deps += [ ":light_app.database" ] } @@ -183,6 +187,10 @@ action("binsign") { if (chip_simple_hash_verification == 1) { args = [ "--simple-hash" ] } + + if (chip_enable_ota_requestor) { + args = [ "--ota-enabled" ] + } } group("default") { diff --git a/examples/lighting-app/nxp/k32w/k32w0/README.md b/examples/lighting-app/nxp/k32w0/README.md similarity index 71% rename from examples/lighting-app/nxp/k32w/k32w0/README.md rename to examples/lighting-app/nxp/k32w0/README.md index a0f560e2e884c4..72af8f444225ac 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/README.md +++ b/examples/lighting-app/nxp/k32w0/README.md @@ -16,42 +16,38 @@ network.
-- [CHIP K32W061 Lighting Example Application](#chip-k32w061-lighting-example-application) - - [Introduction](#introduction) - - [SE051H Secure Element](#se051h-secure-element) - - [Bluetooth LE Advertising](#bluetooth-le-advertising) - - [Bluetooth LE Rendezvous](#bluetooth-le-rendezvous) - - [Thread Provisioning](#thread-provisioning) - - [Device UI](#device-ui) - - [No expansion board](#no-expansion-board) - - [Identify cluster LED state](#identify-cluster-led-state) - - [Building](#building) - - [Overwrite board config files](#overwrite-board-config-files) - - [Known issues building](#known-issues-building) - - [Rotating device id](#rotating-device-id) - - [Manufacturing data](#manufacturing-data) - - [Flashing and debugging](#flashing-and-debugging) - - [Pigweed tokenizer](#pigweed-tokenizer) - - [Detokenizer script](#detokenizer-script) - - [Notes](#notes) - - [Known issues tokenizer](#known-issues-tokenizer) - - [NXP Ultrafast P256 ECC Library](#nxp-ultrafast-p256-ecc-library) - - [Building steps](#building-steps) - - [Tinycrypt ECC library](#tinycrypt-ecc-library) - - [Building steps](#building-steps-1) - - [OTA](#ota) - - [Writing the SSBL](#writing-the-ssbl) - - [Features](#features) - - [Multi image](#multi-image) - - [Simple hash verification](#simple-hash-verification) - - [Writing the PSECT](#writing-the-psect) - - [Writing the application](#writing-the-application) - - [OTA Testing](#ota-testing) - - [Known issues ota](#known-issues-ota) +- [CHIP K32W061 Lighting Example Application](#chip-k32w061-lighting-example-application) + - [Introduction](#introduction) + - [SE051H Secure Element](#se051h-secure-element) + - [Bluetooth LE Advertising](#bluetooth-le-advertising) + - [Bluetooth LE Rendezvous](#bluetooth-le-rendezvous) + - [Thread Provisioning](#thread-provisioning) + - [Device UI](#device-ui) + - [No expansion board](#no-expansion-board) + - [Identify cluster LED state](#identify-cluster-led-state) + - [Building](#building) + - [Overwrite board config files](#overwrite-board-config-files) + - [Known issues building](#known-issues-building) + - [Rotating device id](#rotating-device-id) + - [Manufacturing data](#manufacturing-data) + - [Flashing and debugging](#flashing-and-debugging) + - [Using DK6programmer](#using-dk6programmer) + - [Using MCUXpresso](#using-mcuxpresso) + - [Pigweed tokenizer](#pigweed-tokenizer) + - [Detokenizer script](#detokenizer-script) + - [Notes](#notes) + - [Known issues tokenizer](#known-issues-tokenizer) + - [NXP Ultrafast P256 ECC Library](#nxp-ultrafast-p256-ecc-library) + - [Building steps](#building-steps) + - [Tinycrypt ECC library](#tinycrypt-ecc-library) + - [Building steps](#building-steps-1) + - [OTA](#ota) + - [OTA Testing](#ota-testing) + - [Known issues OTA](#known-issues-ota) ## Introduction -![K32W061 DK6](../../../../platform/nxp/k32w/k32w0/doc/images/k32w-dk6.jpg) +![K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-dk6.jpg) The K32W061 lighting example application provides a working demonstration of a light bulb device, built using the Project CHIP codebase and the NXP K32W061 @@ -81,7 +77,7 @@ Deployment of this firmware configuration requires the K32W061 board setups using the K32W061 module board, SE051 Expansion board and Generic Expansion board as shown below: -![SE051H + K32W061 DK6](../../../../platform/nxp/k32w/k32w0/doc/images/k32w-se.jpg) +![SE051H + K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-se.jpg) The SE051H Secure Element extension may be used for best in class security and offloading some of the Project CHIP cryptographic operations. Depending on your @@ -198,10 +194,10 @@ effects: In order to build the Project CHIP example, we recommend using a Linux distribution (supported Operating Systems are listed in -[BUILDING.md](../../../../../docs/guides/BUILDING.md#tested-operating systems)). +[BUILDING.md](../../../../docs/guides/BUILDING.md#tested-operating-systems)). - Make sure that below prerequisites are correctly installed (as described in - [BUILDING.md](../../../../../docs/guides/BUILDING.md#prerequisites))) + [BUILDING.md](../../../../docs/guides/BUILDING.md#prerequisites)) ``` sudo apt-get install git gcc g++ pkg-config libssl-dev libdbus-1-dev \ @@ -240,9 +236,9 @@ SDKs. Arg "-- help" could be used to view all available options. - Start building the application: ```bash -user@ubuntu:~/Desktop/git/connectedhomeip$ cd examples/lighting-app/nxp/k32w/k32w0 -user@ubuntu:~/Desktop/git/connectedhomeip/examples/lighting-app/nxp/k32w/k32w0$ gn gen out/debug -user@ubuntu:~/Desktop/git/connectedhomeip/examples/lighting-app/nxp/k32w/k32w0$ ninja -C out/debug +user@ubuntu:~/Desktop/git/connectedhomeip$ cd examples/lighting-app/nxp/k32w0 +user@ubuntu:~/Desktop/git/connectedhomeip/examples/lighting-app/nxp/k32w0$ gn gen out/debug +user@ubuntu:~/Desktop/git/connectedhomeip/examples/lighting-app/nxp/k32w0$ ninja -C out/debug ``` To build with Secure Element, follow the same steps as above but set @@ -310,7 +306,7 @@ k32w0_sdk("sdk") { This variable will be used by `k32w0_sdk.gni` to overwrite `chip_with_DK6` option, thus the reference board configuration files will no longer be used. -## Known issues building +### Known issues building - When using Secure element and cross-compiling on Linux, log messages from the Plug&Trust middleware stack may not echo to the console. @@ -333,17 +329,17 @@ Please use the following build args: ## Manufacturing data See -[Guide for writing manufacturing data on NXP devices](../../../../../docs/guides/nxp/nxp_manufacturing_flow.md). +[Guide for writing manufacturing data on NXP devices](../../../../docs/guides/nxp/nxp_manufacturing_flow.md). There are factory data generated binaries available in -examples/platform/nxp/k32w/k32w0/scripts/demo_generated_factory_data folder. +examples/platform/nxp/k32w0/scripts/demo_generated_factory_data folder. These are based on the DAC, PAI and PAA certificates found in scripts/tools/nxp/demo_generated_certs folder. The demo_factory_data_dut1.bin uses the DAC certificate and private key found in -examples/platform/nxp/k32w/k32w0/scripts/demo_generated_factory_data/dac/dut1 +examples/platform/nxp/k32w0/scripts/demo_generated_factory_data/dac/dut1 folder. The demo_factory_data_dut2.bin uses the DAC certificate and private key found in -examples/platform/nxp/k32w/k32w0/scripts/demo_generated_factory_data/dac/dut2 +examples/platform/nxp/k32w0/scripts/demo_generated_factory_data/dac/dut2 folder. These two factory data binaries can be used for testing topologies with 2 DUTS. They contain the corresponding DACs/PAIs generated using generate_nxp_chip_factory_bin.py script. The discriminator is 14014 and the @@ -355,18 +351,128 @@ Regarding factory data provider, there are two options: - use the default factory data provider: `FactoryDataProviderImpl` by setting `chip_with_factory_data=1` in the gn build command. - use a custom factory data provider: please see - [Guide for implementing a custom factory data provider](../../../../platform/nxp/k32w/k32w0/common/README.md). + [Guide for implementing a custom factory data provider](../../../platform/nxp/k32w0/common/README.md). This can be enabled when `chip_with_factory_data=1` by setting `use_custom_factory_provider=1` in the gn build command. ## Flashing and debugging -Program the firmware using the official +Instructions to program the firmware can be found also at [OpenThread Flash Instructions](https://github.com/openthread/ot-nxp/tree/main/src/k32w0/k32w061#flash-binaries). -All you have to do is to replace the Openthread binaries from the above -documentation with _out/debug/chip-k32w0x-light-example.bin_ if DK6Programmer is -used or with _out/debug/chip-k32w0x-light-example_ if MCUXpresso is used. +### Using DK6programmer + +The application binary's path is _out/debug/chip-k32w0x-light-example.bin_. + +DK6Programmer can be used for flashing the application. There are two available versions of the +DK6Programmer tool. + +The legacy version consists of a Windows executable found inside the +[SDK](https://mcuxpresso.nxp.com/en/welcome) at path `tools/JN-SW-4407-DK6-Flash-Programmer`. This is a +Windows application that can be installed using the .exe file. Once the +application is installed, the COM port for K32W061 must be identified: + +``` +C:\nxp\DK6ProductionFlashProgrammer>DK6Programmer.exe --list +Available connections: + +``` + +Once the COM port is identified, the required binary can be flashed: + +``` +DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x4000="chip-k32w0x-light-example.bin" +``` + +> **_Note:_** The above example takes into account that the binary uses the `chip_enable_ota_requestor=true` option. +The address offset corresponds to the space left for the SSBL binary and the OTA space for the SSBL. If +`chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be replaced with `0x0`. + +DK6 Flash Programmer tool has also been integrated part of +[NXP Secure Provisioning SDK (SPSDK)](https://github.com/nxp-mcuxpresso/spsdk). This tool is supported by +environments like Windows, Linux or Mac. + +SPSDK can be installed and run from a Python environment using [these instructions](https://spsdk.readthedocs.io/en/latest/usage/installation.html). +This enables the user to have transparent access to the dk6 programming tool through SPSDK. + +``` +# after specific environment installation steps +$ spsdk --help +... + +-- dk6prog Tool for reading and programming flash memory of DK6 target devices. + � +-- erase Erase the memory. + � +-- info Prints the information about the connected device. + � +-- isp Issues ISP sequence as defined in Driver interface. + � +-- listdev Prints the information about the connected devices. + � +-- read Reads the memory and writes it to the file or stdout. + � +-- write Write the memory. +... +``` + +Dependencies for the dk6prog module can be installed using the following command, more details [here](https://spsdk.readthedocs.io/en/latest/usage/installation.html#dk6-tools): + +``` +$ pip install spsdk[dk6] +``` + +The SPSDK installation adds dk6prog as executable to system path, so user can use directly `dk6prog` from terminal. +The following commands are to be used to write the chip-k32w0x-light-example binary to the board. + +``` +$ dk6prog listdev +This is an experimental utility. Use with caution! + +List of available devices: +DEVICE ID: DN038ZH3, VID: 0x403, PID: 0x6015, Serial number: DN038ZH3, Description: DK6 Carrier Board, Address: 9, Backend: Backend.PYFTDI +$ dk6prog -d DN038ZH3 write 0x4000 ~/path/to/bin/chip-k32w0x-light-example.bin + +This is an experimental utility. Use with caution! + +Writing memory [####################################] 100% +Writen 596450 bytes to memory ID 0 at address 0x4000 +``` + +> **_Note:_** Running `dk6prog` from Windows OS command line requires an integer value for DEVICE ID. + +``` +C:\nxp\spsdk>dk6prog listdev + +This is an experimental utility. Use with caution! + +List of available devices: +DEVICE ID: 0, VID: 0x0, PID: 0x0, Serial number: b'DN038ZH3', Description: b'DK6 Carrier Board', Address: 67330069, Backend: Backend.FTD2xx + +C:\nxp\spsdk>dk6prog -d 0 info + +This is an experimental utility. Use with caution! + +Chip ID: 0x88888888 +ROM Version: 0x140000cc +MAC Address: A8:2B:1F:03:00:8D:15:00 + +Detected DEVICE: UNKNOWN + + Memory Memory ID Base Address Length Sector Size Memory Type Access +---------------------------------------------------------------------------------------------- + FLASH 0 0x0 0x9de00 0x200 FLASH All is available + PSECT 1 0x0 0x1e0 0x10 FLASH All is available + pFLASH 2 0x0 0x1e0 0x10 FLASH All is available + Config 3 0x9fc00 0x200 0x200 FLASH All is available + EFUSE 4 0x0 0x80 0x2 EFUSE (OTP) Write Enabled + ROM 5 0x3000000 0x20000 0x1 ROM Write Enabled + RAM0 6 0x4000000 0x16000 0x1 RAM Write Enabled + RAM1 7 0x4020000 0x10000 0x1 RAM Write Enabled +``` + +> **_Note:_** The above example takes into account that the binary uses the `chip_enable_ota_requestor=true` option. +The address offset corresponds to the space left for the SSBL binary and the OTA space for the SSBL. If +`chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be replaced with `0x0`. + +### Using MCUXpresso + +If flashing and debugging is required, MCUXpresso can be used as instructed in +[MCUXpresso flashing and debugging instructions](https://github.com/openthread/ot-nxp/tree/main/src/k32w0/k32w061#using-mcuxpresso-ide). +The file needed to be used in MCUXpresso is _out/debug/chip-k32w0x-light-example_. ## Pigweed tokenizer @@ -379,7 +485,7 @@ needed for parsing the hashed scripts. The python3 script detokenizer.py is a script that decodes the tokenized logs either from a file or from a serial port. It is located in the following path -`examples/platform/nxp/k32w/k32w0/scripts/detokenizer.py`. +`examples/platform/nxp/k32w0/scripts/detokenizer.py`. The script can be used in the following ways: @@ -414,7 +520,7 @@ by the script is loaded by the environment. An example of running the detokenizer script to see logs of a lighting app: ``` -python3 ../../../../../examples/platform/nxp/k32w/k32w0/scripts/detokenizer.py serial -i /dev/ttyACM0 -d out/debug/chip-k32w0x-light-example-database.bin -o device.txt +python3 ../../../../examples/platform/nxp/k32w0/scripts/detokenizer.py serial -i /dev/ttyACM0 -d out/debug/chip-k32w0x-light-example-database.bin -o device.txt ``` ### Known issues tokenizer @@ -459,172 +565,34 @@ In order to use the Tinycrypt ECC library, use the following build arguments: ## OTA -The internal flash needs to be prepared for the OTA process. First 16K of the -internal flash needs to be populated with a Secondary Stage Bootloader (SSBL) -related data while the last 8.5K of flash space is holding image directory -related data (PSECT). The space between these two zones will be filled by the -application. - -### Writing the SSBL - -The SDK already provides an SSBL binary compiled with external flash support: -`boards/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl_ext_flash_pdm_support.bin`, -but it does not offer multi-image OTA support. - -Alternatively, the SSBL can ge generated from one of the SDK demo examples. The -SSBL demo application can be imported from the `Quickstart panel`: -`Import SDK example(s) -> select wireless -> framework -> ssbl` application. - -![SSBL Application Select](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_select.JPG) - -### Features - -#### Multi image - -To support multi-image OTA feature, the SSBL project must be compiled using the -following defines: - -- `PDM_EXT_FLASH=1` - support PDM in external flash. -- `gOTAUseCustomOtaEntry=1` - support custom OTA entry for multi-image. -- `gOTACustomOtaEntryMemory=OTACustomStorage_ExtFlash` - K32W0 uses - `OTACustomStorage_ExtFlash` (1) by default. -- `SPIFI_DUAL_MODE_SUPPORT=1` - only for configurations that use dual `SPIFI` - flash (e.g. K32W041AM variant). - -Optionally, add the following defines: - -- `SPIFI_OPTIM_SIZE=1` - to optimize SSBL size. -- `EXTERNAL_FLASH_DATA_OTA=1` - to support external read only data. - -![SSBL_MULTI_IMAGE](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_multi_image.JPG) +Over the air updates (OTA) require several software components running on the K32W0x1. +Firstly, a Secondary Stage Bootloader (SSBL) is required written in the first part of the +internal flash memory, usually starting at address 0x0. This enables the board to boot and +check if a new OTA binary has been received. If this is true, the bootloader writes the OTA +binary to the appropriate storage, internal and/or external flash, after which it reboots +the board. If no new OTA binaries have been found, then the bootloader gives execution control +to the application. -#### Simple hash verification - -When secure boot is not used, a simple hash can be appended at the end of the -image for integrity check. Applications should be built with -`chip_simple_hash_verification=1`. - -To support simple hash verification feature, the SSBL project must be compiled -with: - -- `gSimpleHashVerification=1` - -and update the post-build command to use simple hash verification instead of the -default options. Go to -`Project -> Properties -> C/C++ Build -> Settings -> Build steps` and press -`Edit` under `Post-build steps` subsection. The command should look similar to: - -![SSBL_SIMPLE_HASH_VERIFICATION](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG) - -Once compiled, the required SSBL file is called `k32w061dk6_ssbl.bin`. - -![SSBL_BIN](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_bin.JPG) - -Before writing the SSBL, it it recommanded to fully erase the internal flash: - -``` -DK6Programmer.exe -V 5 -P 1000000 -s -e Flash -``` - -`k32w061dk6_ssbl.bin` must be written at address 0 in the internal flash: - -``` -DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x00="k32w061dk6_ssbl.bin" -``` - -### Writing the PSECT - -This is the list of all supported partitions: - -``` -0000000010000000 : SSBL partition - - 00000000 -----------> Start Address - 1000 ---------------> 0x0010 Number of 512-bytes pages - 00 -----------------> 0x00 Bootable flag - 00 -----------------> 0x00 Image type (0x00 = SSBL) - -00400000c9040101: Application partition - - 00400000 -----------> 0x00004000 Start Address - c904 ---------------> 0x04c9 Number of 512-bytes pages - 01 -----------------> 0x01 Bootable flag - 01 -----------------> 0x01 Image type (0x01 = Application) - -00000010800000fe: Ext Flash text partition - - 00000010 -----------> 0x10000000 Start Address (external flash) - 8000 ---------------> 0x0080 Number of 512-bytes pages - 00 -----------------> 0x00 Bootable flag - fe -----------------> 0xFE Image type (0xFE = Ext Flash text) - -00000110300200fc : OTA Image partition - - 00000110 -----------> 0x10010000 Start Address - 3002----------------> 0x0230 Number of 512-bytes pages - 00 -----------------> 0x00 Bootable flag - fc -----------------> 0xFC Image type (0xFC = OTA partition) - -00000510100000fd: NVM partition - - 00000510 -----------> 0x10050000 Start Address - 1000 ---------------> 0x0010 Number of 512-bytes pages - 00 -----------------> 0x00 Bootable flag - fd -----------------> 0xFD Image type (0xFD = NVM partition) -``` - -First, image directory 0 (SSBL partition) must be written: - -``` -DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_0=0000000010000000 -``` - -Here is the interpretation of the fields: - -``` -00000000 -> start address 0x00000000 -1000 -> size = 0x0010 pages of 512-bytes (= 8kB) -00 -> not bootable (only used by the SSBL to support SSBL update) -00 -> SSBL Image Type -``` - -Second, image directory 1 (application partition) must be written: - -``` -DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_1=00400000C9040101 -``` - -Here is the interpretation of the fields: - -``` -00400000 -> start address 0x00004000 -C904 -> 0x4C9 pages of 512-bytes (= 612.5kB) -01 -> bootable flag -01 -> image type for the application -``` - -Please note the user can write additional partitions by writing -`image_dir_2/3/4` with the wanted configuration. - -### Writing the application - -DK6Programmer can be used for flashing the application: - -``` -DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x4000="chip-k32w0x-light-example.bin" -``` +The internal flash needs to be prepared for the OTA process. First 16K of the internal flash +needs to be populated with SSBL related data while the last 8.5K of flash space is holding flash +configuration related data. The space between these two zones will be filled by the application. +More details regarding the internal flash space can be found in the +[linker file](../../../platform/nxp/k32w0/app/ldscripts/chip-k32w0x-linker.ld). -If debugging is needed, MCUXpresso can be used then for flashing the -application. Please make sure that the application is written at address 0x4000: +The steps for building the SSBL binary with appropriate configuration and writing to the board +the binary and other OTA related configurations are descrived in the +[K32W0x1 OTA guide](../../../../docs/guides/nxp/nxp_k32w0_ota_guide.md). -![FLASH_LOCATION](../../../../platform/nxp/k32w/k32w0/doc/images/flash_location.JPG) +Note that the application needs to be built using the `chip_enable_ota_requestor=true` option. +This is enabled in the configuration by default if no `chip_enable_ota_requestor` explicit setting +is done. ### OTA Testing The OTA topology used for OTA testing is illustrated in the figure below. Topology is similar with the one used for Matter Test Events. -![OTA_TOPOLOGY](../../../../platform/nxp/k32w/k32w0/doc/images/ota_topology.JPG) +![OTA_TOPOLOGY](../../../platform/nxp/k32w0/doc/images/ota_topology.JPG) The concept for OTA is the next one: @@ -701,21 +669,21 @@ The address for storing the custom OTA entry can also be specified: address that does not overlap with anything else. Please see more in the -[OTA image tool guide](../../../../../scripts/tools/nxp/ota/README.md). +[OTA image tool guide](../../../../scripts/tools/nxp/ota/README.md). Here is an example that generates an OTA image with application update TLV: ``` -./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 42021 -vs "1.0" -da sha256 --app-input-file chip-k32w0x-light-example.bin chip-k32w0x-light-example.ota +./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 2 -vs "2.0" -da sha256 --app-input-file chip-k32w0x-light-example.bin chip-k32w0x-light-example.ota ``` A note regarding OTA image header version (`-vn` option). An application binary has its own software version, given by -`CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION` (`42020` by default), which can be +`CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION` (`1` by default), which can be overwritten. For having a correct OTA process, the OTA header version should be -the same as the binary embedded software version. A user can set a custom -software version in the gn build args by setting `chip_software_version` to the -wanted version. +the same as the binary embedded software version. When building the update image, +the build arguments `nxp_software_version=2` and `nxp_sofware_version_string=\"2.0\"` +can be added to the gn gen command in order to specify the upgraded version. Start the OTA Provider Application: @@ -745,7 +713,7 @@ Start the OTA process: user@computer1:~/connectedhomeip$ : ./out/chip-tool-app/chip-tool otasoftwareupdaterequestor announce-otaprovider 1 0 0 0 2 0 ``` -## Known issues ota +### Known issues OTA - SRP cache on the openthread border router needs to flushed each time a new commissioning process is attempted. For this, factory reset the device, then diff --git a/examples/lighting-app/nxp/k32w/k32w0/args.gni b/examples/lighting-app/nxp/k32w0/args.gni similarity index 100% rename from examples/lighting-app/nxp/k32w/k32w0/args.gni rename to examples/lighting-app/nxp/k32w0/args.gni diff --git a/examples/lighting-app/nxp/k32w0/build_overrides b/examples/lighting-app/nxp/k32w0/build_overrides new file mode 120000 index 00000000000000..ee19c065d619a2 --- /dev/null +++ b/examples/lighting-app/nxp/k32w0/build_overrides @@ -0,0 +1 @@ +../../../build_overrides/ \ No newline at end of file diff --git a/examples/lighting-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h b/examples/lighting-app/nxp/k32w0/include/CHIPProjectConfig.h similarity index 97% rename from examples/lighting-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h rename to examples/lighting-app/nxp/k32w0/include/CHIPProjectConfig.h index e32575e55ff5c4..62b2b0273f1da4 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h +++ b/examples/lighting-app/nxp/k32w0/include/CHIPProjectConfig.h @@ -86,7 +86,7 @@ 0xe4, 0x76, 0x1b, 0xfd, 0x05, \ } -// All remaining data will be pulled from provisioning region of flash. +// All remaining data will be pulled from the provisioning region of flash. #endif #else @@ -146,11 +146,11 @@ * {MAJOR_VERSION}.0d{MINOR_VERSION} */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "03-2022-te8" +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING NXP_CONFIG_DEVICE_SOFTWARE_VERSION_STRING #endif #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 42020 +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION NXP_CONFIG_DEVICE_SOFTWARE_VERSION #endif #ifndef CHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME diff --git a/examples/lighting-app/nxp/k32w/k32w0/include/FreeRTOSConfig.h b/examples/lighting-app/nxp/k32w0/include/FreeRTOSConfig.h similarity index 100% rename from examples/lighting-app/nxp/k32w/k32w0/include/FreeRTOSConfig.h rename to examples/lighting-app/nxp/k32w0/include/FreeRTOSConfig.h diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp b/examples/lighting-app/nxp/k32w0/main/AppTask.cpp similarity index 98% rename from examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp rename to examples/lighting-app/nxp/k32w0/main/AppTask.cpp index 22535a1c974c92..7db25b0a7c0451 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp +++ b/examples/lighting-app/nxp/k32w0/main/AppTask.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -44,9 +43,10 @@ #include #include #include -#include +#include #endif +#include "DefaultTestEventTriggerDelegate.h" #include "Keyboard.h" #include "LED.h" #include "LEDWidget.h" @@ -116,7 +116,7 @@ static BDXDownloader gDownloader; constexpr uint16_t requestedOtaBlockSize = 1024; #endif -#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA && CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA && CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR CHIP_ERROR CustomFactoryDataRestoreMechanism(void) { K32W_LOG("This is a custom factory data restore mechanism."); @@ -151,7 +151,7 @@ static void CheckOtaEntry() if (ota_entries.ota_state == otaApplied) { K32W_LOG("OTA successfully applied"); -#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA && CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA && CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR // If this point is reached, it means OTA_CommitCustomEntries was successfully called. // Delete the factory data backup to stop doing a restore when the factory data provider // is initialized. This ensures that both the factory data and app were updated, otherwise @@ -187,9 +187,8 @@ CHIP_ERROR AppTask::Init() CheckOtaEntry(); #endif - // Initialize device attestation config #if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR sFactoryDataProvider.RegisterRestoreMechanism(CustomFactoryDataRestoreMechanism); #endif ReturnErrorOnFailure(sFactoryDataProvider.Init()); diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/LightingManager.cpp b/examples/lighting-app/nxp/k32w0/main/LightingManager.cpp similarity index 100% rename from examples/lighting-app/nxp/k32w/k32w0/main/LightingManager.cpp rename to examples/lighting-app/nxp/k32w0/main/LightingManager.cpp diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/ZclCallbacks.cpp b/examples/lighting-app/nxp/k32w0/main/ZclCallbacks.cpp similarity index 100% rename from examples/lighting-app/nxp/k32w/k32w0/main/ZclCallbacks.cpp rename to examples/lighting-app/nxp/k32w0/main/ZclCallbacks.cpp diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/include/AppEvent.h b/examples/lighting-app/nxp/k32w0/main/include/AppEvent.h similarity index 100% rename from examples/lighting-app/nxp/k32w/k32w0/main/include/AppEvent.h rename to examples/lighting-app/nxp/k32w0/main/include/AppEvent.h diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/include/AppTask.h b/examples/lighting-app/nxp/k32w0/main/include/AppTask.h similarity index 95% rename from examples/lighting-app/nxp/k32w/k32w0/main/include/AppTask.h rename to examples/lighting-app/nxp/k32w0/main/include/AppTask.h index 0c455f431a8cf4..7dca1f700f6b89 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/main/include/AppTask.h +++ b/examples/lighting-app/nxp/k32w0/main/include/AppTask.h @@ -27,7 +27,7 @@ #include "CHIPProjectConfig.h" #if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA -#include +#include #if CHIP_DEVICE_CONFIG_USE_CUSTOM_PROVIDER #include "CustomFactoryDataProvider.h" #endif @@ -50,6 +50,9 @@ class AppTask { public: +#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA + using FactoryDataProvider = chip::DeviceLayer::FactoryDataProviderImpl; +#endif CHIP_ERROR StartAppTask(); static void AppTaskMain(void * pvParameter); diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/include/LightingManager.h b/examples/lighting-app/nxp/k32w0/main/include/LightingManager.h similarity index 100% rename from examples/lighting-app/nxp/k32w/k32w0/main/include/LightingManager.h rename to examples/lighting-app/nxp/k32w0/main/include/LightingManager.h diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/include/app_config.h b/examples/lighting-app/nxp/k32w0/main/include/app_config.h similarity index 100% rename from examples/lighting-app/nxp/k32w/k32w0/main/include/app_config.h rename to examples/lighting-app/nxp/k32w0/main/include/app_config.h diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/main.cpp b/examples/lighting-app/nxp/k32w0/main/main.cpp similarity index 97% rename from examples/lighting-app/nxp/k32w/k32w0/main/main.cpp rename to examples/lighting-app/nxp/k32w0/main/main.cpp index a8963a17c09096..6d95f10fe1168b 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/main/main.cpp +++ b/examples/lighting-app/nxp/k32w0/main/main.cpp @@ -139,7 +139,7 @@ extern "C" void main_task(void const * argument) */ sched_enable(); - err = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_MinimalEndDevice); + err = ConnectivityMgr().SetThreadDeviceType(CONNECTIVITY_MANAGER_THREAD_DEVICE_TYPE); if (err != CHIP_NO_ERROR) { goto exit; diff --git a/examples/lighting-app/nxp/k32w0/third_party/connectedhomeip b/examples/lighting-app/nxp/k32w0/third_party/connectedhomeip new file mode 120000 index 00000000000000..3efed95be5dbe9 --- /dev/null +++ b/examples/lighting-app/nxp/k32w0/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../../../ \ No newline at end of file diff --git a/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.cpp b/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.cpp deleted file mode 100644 index 69935bb024559d..00000000000000 --- a/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * 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/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h b/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h deleted file mode 100644 index 0bfd4c5b0fa725..00000000000000 --- a/src/platform/nxp/k32w/k32w0/DefaultTestEventTriggerDelegate.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * 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/nxp/k32w/k32w0/BLEManagerImpl.cpp b/src/platform/nxp/k32w0/BLEManagerImpl.cpp similarity index 98% rename from src/platform/nxp/k32w/k32w0/BLEManagerImpl.cpp rename to src/platform/nxp/k32w0/BLEManagerImpl.cpp index af56961cf23dc1..aa56e06a8d5598 100644 --- a/src/platform/nxp/k32w/k32w0/BLEManagerImpl.cpp +++ b/src/platform/nxp/k32w0/BLEManagerImpl.cpp @@ -37,7 +37,7 @@ osaEventId_t mControllerTaskEvent; extern msgQueue_t gApp2Host_TaskQueue; extern msgQueue_t gHci2Host_TaskQueue; -#include +#include extern "C" bool_t Ble_ConfigureHostStackConfig(void); diff --git a/src/platform/nxp/k32w/k32w0/BLEManagerImpl.h b/src/platform/nxp/k32w0/BLEManagerImpl.h similarity index 97% rename from src/platform/nxp/k32w/k32w0/BLEManagerImpl.h rename to src/platform/nxp/k32w0/BLEManagerImpl.h index eb8a25804804d1..686eb1e3f703ee 100644 --- a/src/platform/nxp/k32w/k32w0/BLEManagerImpl.h +++ b/src/platform/nxp/k32w0/BLEManagerImpl.h @@ -25,7 +25,7 @@ #include "ble_host_task_config.h" #include "controller_interface.h" -#include +#include /* host task configuration */ #define HOST_TASK_PRIORITY (4U) diff --git a/src/platform/nxp/k32w/k32w0/BUILD.gn b/src/platform/nxp/k32w0/BUILD.gn similarity index 77% rename from src/platform/nxp/k32w/k32w0/BUILD.gn rename to src/platform/nxp/k32w0/BUILD.gn index 76af2760bc9051..41a80a77a7aa53 100644 --- a/src/platform/nxp/k32w/k32w0/BUILD.gn +++ b/src/platform/nxp/k32w0/BUILD.gn @@ -19,17 +19,18 @@ import("${chip_root}/src/platform/device.gni") import("${nxp_sdk_build_root}/${nxp_sdk_name}/${nxp_sdk_name}.gni") assert(chip_device_platform == "nxp") -assert(nxp_platform == "k32w/k32w0") +assert(nxp_platform == "k32w0") if (chip_enable_openthread) { import("//build_overrides/openthread.gni") } static_library("nxp_platform") { + defines = [] sources = [ - "../../../SingletonConfigurationManager.cpp", - "../common/BLEManagerCommon.cpp", - "../common/BLEManagerCommon.h", + "../../SingletonConfigurationManager.cpp", + "../common/legacy/BLEManagerCommon.cpp", + "../common/legacy/BLEManagerCommon.h", "BLEManagerImpl.cpp", "BLEManagerImpl.h", "CHIPDevicePlatformConfig.h", @@ -38,8 +39,6 @@ static_library("nxp_platform") { "ConfigurationManagerImpl.h", "ConnectivityManagerImpl.cpp", "ConnectivityManagerImpl.h", - "DefaultTestEventTriggerDelegate.cpp", - "DefaultTestEventTriggerDelegate.h", "DiagnosticDataProviderImpl.cpp", "DiagnosticDataProviderImpl.h", "K32W0Config.cpp", @@ -60,12 +59,12 @@ static_library("nxp_platform") { "${chip_root}/src/credentials/examples/DeviceAttestationCredsExample.h", "${chip_root}/src/credentials/examples/ExampleDACs.h", "${chip_root}/src/credentials/examples/ExamplePAI.h", - "${chip_root}/src/platform/nxp/k32w/k32w0/BLEManagerImpl.h", + "${chip_root}/src/platform/nxp/k32w0/BLEManagerImpl.h", ] if (chip_with_factory_data == 1) { sources += [ - "../common/FactoryDataProvider.cpp", + "FactoryDataProvider.cpp", "FactoryDataProviderImpl.cpp", ] public += [ @@ -79,13 +78,13 @@ static_library("nxp_platform") { } if (chip_enable_ota_requestor) { - public += [ "../common/OTAImageProcessorImpl.h" ] + public += [ "../common/legacy/OTAImageProcessorImpl.h" ] sources += [ - "../common/OTAImageProcessorImpl.cpp", - "../common/OTAImageProcessorImpl.h", - "../common/OTATlvProcessor.cpp", - "../common/OTATlvProcessor.h", + "../common/legacy/OTAImageProcessorImpl.cpp", + "../common/legacy/OTAImageProcessorImpl.h", + "../common/legacy/OTATlvProcessor.cpp", + "../common/legacy/OTATlvProcessor.h", ] if (chip_enable_ota_firmware_processor == 1) { @@ -107,10 +106,7 @@ static_library("nxp_platform") { deps = [ "${chip_root}/src/platform/logging:headers" ] - public_deps = [ - "${chip_root}/src/app:test-event-trigger", - "${chip_root}/src/platform:platform_base", - ] + public_deps = [ "${chip_root}/src/platform:platform_base" ] if (chip_crypto == "platform") { if (chip_crypto_flavor == "tinycrypt") { @@ -120,7 +116,7 @@ static_library("nxp_platform") { } if (chip_crypto_flavor == "NXP-Ultrafast-P256") { - sources += [ "${chip_root}/src/platform/nxp/k32w/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp" ] + sources += [ "${chip_root}/src/platform/nxp/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp" ] public_deps += [ "${mbedtls_root}:mbedtls", @@ -131,16 +127,16 @@ static_library("nxp_platform") { if (chip_enable_openthread) { sources += [ - "../../../OpenThread/OpenThreadUtils.cpp", + "../../OpenThread/OpenThreadUtils.cpp", "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] if (chip_mdns == "platform") { sources += [ - "../../../OpenThread/DnssdImpl.cpp", - "../../../OpenThread/OpenThreadDnssdImpl.cpp", - "../../../OpenThread/OpenThreadDnssdImpl.h", + "../../OpenThread/DnssdImpl.cpp", + "../../OpenThread/OpenThreadDnssdImpl.cpp", + "../../OpenThread/OpenThreadDnssdImpl.h", ] deps += [ "${chip_root}/src/lib/dnssd:platform_header" ] } diff --git a/src/platform/nxp/k32w/k32w0/BlePlatformConfig.h b/src/platform/nxp/k32w0/BlePlatformConfig.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/BlePlatformConfig.h rename to src/platform/nxp/k32w0/BlePlatformConfig.h diff --git a/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h b/src/platform/nxp/k32w0/CHIPDevicePlatformConfig.h similarity index 97% rename from src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h rename to src/platform/nxp/k32w0/CHIPDevicePlatformConfig.h index faa283ffbe41dc..523d6e8941232d 100644 --- a/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h +++ b/src/platform/nxp/k32w0/CHIPDevicePlatformConfig.h @@ -135,14 +135,14 @@ #endif // CONFIG_CHIP_K32W0_KVS_MOVE_KEYS_TO_SPECIFIC_STORAGE /** - * @def CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR + * @def CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR * * Enables default OTA TLV factory data processor. * Disabled by default. */ -#ifndef CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR -#define CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR 0 -#endif // CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#ifndef CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR +#define CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR 0 +#endif // CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR /** * @def CHIP_DEVICE_LAYER_ENABLE_PDM_LOGS @@ -211,4 +211,4 @@ #define CHIP_DEVICE_CONFIG_ENABLE_TEST_SETUP_PARAMS 1 -#include +#include diff --git a/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformEvent.h b/src/platform/nxp/k32w0/CHIPDevicePlatformEvent.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/CHIPDevicePlatformEvent.h rename to src/platform/nxp/k32w0/CHIPDevicePlatformEvent.h diff --git a/src/platform/nxp/k32w0/CHIPDevicePlatformRamStorageConfig.h b/src/platform/nxp/k32w0/CHIPDevicePlatformRamStorageConfig.h new file mode 100644 index 00000000000000..51ee8c4d27ee79 --- /dev/null +++ b/src/platform/nxp/k32w0/CHIPDevicePlatformRamStorageConfig.h @@ -0,0 +1,185 @@ +/* + * + * 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. + */ + +/** + * @file + * Configuration of RAM storage metadata: key IDs and NVM IDs. + */ + +#pragma once + +/* Base key IDs used when creating new keys for RAM storage instances. */ +/** + * @def kKeyId_Factory + * + * Base key id used for factory RAM storage. + */ +#ifndef kKeyId_Factory +#define kKeyId_Factory (uint8_t) 0x01 +#endif + +/** + * @def kKeyId_Config + * + * Base key id used for config RAM storage. + */ +#ifndef kKeyId_Config +#define kKeyId_Config (uint8_t) 0x02 +#endif + +/** + * @def kKeyId_Counter + * + * Base key id used for counter RAM storage. + */ +#ifndef kKeyId_Counter +#define kKeyId_Counter (uint8_t) 0x03 +#endif + +/** + * @def kKeyId_KvsKeys + * + * Base key id used for KVS keys RAM storage. + */ +#ifndef kKeyId_KvsKeys +#define kKeyId_KvsKeys (uint8_t) 0x04 +#endif + +/** + * @def kKeyId_KvsValues + * + * Base key id used for KVS values RAM storage. + */ +#ifndef kKeyId_KvsValues +#define kKeyId_KvsValues (uint8_t) 0x05 +#endif + +/* PDM IDs used when defining RAM storage instances or RAM buffers (OT). */ +/** + * @def kNvmId_Factory + * + * PDM ID used for factory RAM storage. + */ +#ifndef kNvmId_Factory +#define kNvmId_Factory (uint16_t) 0x5001 +#endif + +/** + * @def kNvmId_Config + * + * PDM ID used for config RAM storage. + */ +#ifndef kNvmId_Config +#define kNvmId_Config (uint16_t) 0x5002 +#endif + +/** + * @def kNvmId_Counter + * + * PDM ID used for counter RAM storage. + */ +#ifndef kNvmId_Counter +#define kNvmId_Counter (uint16_t) 0x5003 +#endif + +/** + * @def kNvmId_KvsKeys + * + * PDM ID used for KVS keys RAM storage. + */ +#ifndef kNvmId_KvsKeys +#define kNvmId_KvsKeys (uint16_t) 0x6000 +#endif + +/** + * @def kNvmId_KvsValues + * + * PDM ID used for KVS values RAM storage. + * KVS buffer can become quite big, so this PDM + * id is used as base id for subsequent PDM ids + * used to store data in chunks of PDM page size. + * This will use the extended search feature, so + * subsequent PDM ids should not be used. + */ +#ifndef kNvmId_KvsValues +#define kNvmId_KvsValues (uint16_t) 0x6001 +#endif + +/** + * @def kNvmId_KvsSubscription + * + * PDM ID used for KVS subscription RAM storage. + * It will store both keys and values for those keys. + */ +#ifndef kNvmId_KvsSubscription +#define kNvmId_KvsSubscription (uint16_t) 0x6100 +#endif + +/** + * @def kNvmId_KvsGroups + * + * PDM ID used for KVS groups RAM storage. + * It will store both keys and values for those keys. + * This will use the extended search feature, so + * subsequent PDM ids should not be used. + */ +#ifndef kNvmId_KvsGroups +#define kNvmId_KvsGroups (uint16_t) 0x6200 +#endif + +/** + * @def kNvmId_KvsAcl + * + * PDM ID used for KVS groups RAM storage. + * It will store both keys and values for those keys. + * This will use the extended search feature, so + * subsequent PDM ids should not be used. + */ +#ifndef kNvmId_KvsAcl +#define kNvmId_KvsAcl (uint16_t) 0x6300 +#endif + +/** + * @def kNvmId_OTConfigData + * + * PDM ID used for OT RAM buffer. + */ +#ifndef kNvmId_OTConfigData +#define kNvmId_OTConfigData (uint16_t) 0x4F00 +#endif + +/** + * @def kNvmId_ApplicationBase + * + * Base PDM ID to be used by applications to define their own + * PDM IDs by using an offset. + */ +#ifndef kNvmId_ApplicationBase +#define kNvmId_ApplicationBase (uint16_t) 0xA000 +#endif + +#if CONFIG_CHIP_LOAD_REAL_FACTORY_DATA +/** + * @def kNvmId_FactoryDataBackup + * + * PDM ID used for factory data backup in FactoryDataProvider. + */ +#ifndef kNvmId_FactoryDataBackup +#define kNvmId_FactoryDataBackup (uint16_t) 0x7000 +#endif +#endif // CONFIG_CHIP_LOAD_REAL_FACTORY_DATA diff --git a/src/platform/nxp/k32w/k32w0/CHIPPlatformConfig.h b/src/platform/nxp/k32w0/CHIPPlatformConfig.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/CHIPPlatformConfig.h rename to src/platform/nxp/k32w0/CHIPPlatformConfig.h diff --git a/src/platform/nxp/k32w/k32w0/ConfigurationManagerImpl.cpp b/src/platform/nxp/k32w0/ConfigurationManagerImpl.cpp similarity index 98% rename from src/platform/nxp/k32w/k32w0/ConfigurationManagerImpl.cpp rename to src/platform/nxp/k32w0/ConfigurationManagerImpl.cpp index b8477051faeade..7343c75b5d5865 100644 --- a/src/platform/nxp/k32w/k32w0/ConfigurationManagerImpl.cpp +++ b/src/platform/nxp/k32w0/ConfigurationManagerImpl.cpp @@ -29,8 +29,8 @@ #include #include #include -#include -#include +#include +#include #include "fsl_power.h" #include "fsl_reset.h" diff --git a/src/platform/nxp/k32w/k32w0/ConfigurationManagerImpl.h b/src/platform/nxp/k32w0/ConfigurationManagerImpl.h similarity index 98% rename from src/platform/nxp/k32w/k32w0/ConfigurationManagerImpl.h rename to src/platform/nxp/k32w0/ConfigurationManagerImpl.h index 8fded100d00a87..c7cf2366ca97cb 100644 --- a/src/platform/nxp/k32w/k32w0/ConfigurationManagerImpl.h +++ b/src/platform/nxp/k32w0/ConfigurationManagerImpl.h @@ -26,7 +26,7 @@ #pragma once #include -#include +#include namespace chip { namespace DeviceLayer { diff --git a/src/platform/nxp/k32w/k32w0/ConnectivityManagerImpl.cpp b/src/platform/nxp/k32w0/ConnectivityManagerImpl.cpp similarity index 100% rename from src/platform/nxp/k32w/k32w0/ConnectivityManagerImpl.cpp rename to src/platform/nxp/k32w0/ConnectivityManagerImpl.cpp diff --git a/src/platform/nxp/k32w/k32w0/ConnectivityManagerImpl.h b/src/platform/nxp/k32w0/ConnectivityManagerImpl.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/ConnectivityManagerImpl.h rename to src/platform/nxp/k32w0/ConnectivityManagerImpl.h diff --git a/src/platform/nxp/k32w/k32w0/DiagnosticDataProviderImpl.cpp b/src/platform/nxp/k32w0/DiagnosticDataProviderImpl.cpp similarity index 99% rename from src/platform/nxp/k32w/k32w0/DiagnosticDataProviderImpl.cpp rename to src/platform/nxp/k32w0/DiagnosticDataProviderImpl.cpp index b3e02f3f64cb71..8cf924d1a63424 100644 --- a/src/platform/nxp/k32w/k32w0/DiagnosticDataProviderImpl.cpp +++ b/src/platform/nxp/k32w0/DiagnosticDataProviderImpl.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #if CHIP_SYSTEM_CONFIG_USE_LWIP #include diff --git a/src/platform/nxp/k32w/k32w0/DiagnosticDataProviderImpl.h b/src/platform/nxp/k32w0/DiagnosticDataProviderImpl.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/DiagnosticDataProviderImpl.h rename to src/platform/nxp/k32w0/DiagnosticDataProviderImpl.h diff --git a/src/platform/nxp/k32w0/FactoryDataProvider.cpp b/src/platform/nxp/k32w0/FactoryDataProvider.cpp new file mode 100644 index 00000000000000..23f8ffe7c0586b --- /dev/null +++ b/src/platform/nxp/k32w0/FactoryDataProvider.cpp @@ -0,0 +1,368 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +#if (!CONFIG_CHIP_LOAD_REAL_FACTORY_DATA || !(defined CONFIG_CHIP_LOAD_REAL_FACTORY_DATA)) +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { + +static constexpr size_t kSpake2pSerializedVerifier_MaxBase64Len = + BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_VerifierSerialized_Length) + 1; +static constexpr size_t kSpake2pSalt_MaxBase64Len = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length) + 1; +/* Secure subsystem private key blob size is 32 + 24 = 56. + * DAC private key may be used to store an SSS exported blob instead of the private key. + */ +static constexpr size_t kDacPrivateKey_MaxLen = Crypto::kP256_PrivateKey_Length + 24; + +uint32_t FactoryDataProvider::kFactoryDataStart = (uint32_t) __MATTER_FACTORY_DATA_START; +uint32_t FactoryDataProvider::kFactoryDataSize = (uint32_t) __MATTER_FACTORY_DATA_SIZE; +uint32_t FactoryDataProvider::kFactoryDataPayloadStart = kFactoryDataStart + sizeof(FactoryDataProvider::Header); + +FactoryDataProvider::~FactoryDataProvider() {} + +CHIP_ERROR FactoryDataProvider::Validate() +{ + uint8_t output[Crypto::kSHA256_Hash_Length] = { 0 }; + + memcpy(&mHeader, (void *) kFactoryDataStart, sizeof(Header)); + ReturnErrorCodeIf(mHeader.hashId != kHashId, CHIP_FACTORY_DATA_HASH_ID); + + ReturnErrorOnFailure(Crypto::Hash_SHA256((uint8_t *) kFactoryDataPayloadStart, mHeader.size, output)); + ReturnErrorCodeIf(memcmp(output, mHeader.hash, kHashLen) != 0, CHIP_FACTORY_DATA_SHA_CHECK); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::SearchForId(uint8_t searchedType, uint8_t * pBuf, size_t bufLength, uint16_t & length, + uint32_t * offset) +{ + uint32_t addr = kFactoryDataPayloadStart; + uint8_t type = 0; + + while (addr < (kFactoryDataPayloadStart + mHeader.size)) + { + memcpy(&type, (void *) addr, sizeof(type)); + memcpy(&length, (void *) (addr + 1), sizeof(length)); + + if (searchedType == type) + { + ReturnErrorCodeIf(bufLength < length, CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(pBuf, (void *) (addr + kValueOffset), length); + + if (offset) + *offset = (addr - kFactoryDataPayloadStart); + + return CHIP_NO_ERROR; + } + else + { + /* Jump past 3 bytes of length and then use length to jump to next data */ + addr = addr + kValueOffset + length; + } + } + + return CHIP_ERROR_NOT_FOUND; +} + +CHIP_ERROR FactoryDataProvider::GetCertificationDeclaration(MutableByteSpan & outBuffer) +{ +#if CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION + constexpr uint8_t kCdForAllExamples[] = CHIP_DEVICE_CONFIG_CERTIFICATION_DECLARATION; + + return CopySpanToMutableSpan(ByteSpan{ kCdForAllExamples }, outBuffer); +#else + uint16_t declarationSize = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kCertDeclarationId, outBuffer.data(), outBuffer.size(), declarationSize)); + outBuffer.reduce_size(declarationSize); + + return CHIP_NO_ERROR; +#endif +} + +CHIP_ERROR FactoryDataProvider::GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetDeviceAttestationCert(MutableByteSpan & outBuffer) +{ + uint16_t certificateSize = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kDacCertificateId, outBuffer.data(), outBuffer.size(), certificateSize)); + outBuffer.reduce_size(certificateSize); + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductAttestationIntermediateCert(MutableByteSpan & outBuffer) +{ + uint16_t certificateSize = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kPaiCertificateId, outBuffer.data(), outBuffer.size(), certificateSize)); + outBuffer.reduce_size(certificateSize); + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer) +{ + return SignWithDacKey(messageToSign, outSignBuffer); +} + +CHIP_ERROR FactoryDataProvider::GetSetupDiscriminator(uint16_t & setupDiscriminator) +{ + uint32_t discriminator = 0; + uint16_t temp = 0; + + ReturnErrorOnFailure(SearchForId(FactoryDataId::kDiscriminatorId, (uint8_t *) &discriminator, sizeof(discriminator), temp)); + setupDiscriminator = (uint16_t) (discriminator & 0x0000FFFF); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::SetSetupDiscriminator(uint16_t setupDiscriminator) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR FactoryDataProvider::GetSpake2pIterationCount(uint32_t & iterationCount) +{ + uint16_t temp = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kIcId, (uint8_t *) &iterationCount, sizeof(iterationCount), temp)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetSpake2pSalt(MutableByteSpan & saltBuf) +{ + char saltB64[kSpake2pSalt_MaxBase64Len] = { 0 }; + uint16_t saltB64Len = 0; + + ReturnErrorOnFailure(SearchForId(FactoryDataId::kSaltId, (uint8_t *) (&saltB64[0]), sizeof(saltB64), saltB64Len)); + size_t saltLen = chip::Base64Decode32(saltB64, saltB64Len, reinterpret_cast(saltB64)); + + ReturnErrorCodeIf(saltLen > saltBuf.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(saltBuf.data(), saltB64, saltLen); + saltBuf.reduce_size(saltLen); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetSpake2pVerifier(MutableByteSpan & verifierBuf, size_t & verifierLen) +{ + char verifierB64[kSpake2pSerializedVerifier_MaxBase64Len] = { 0 }; + uint16_t verifierB64Len = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kVerifierId, (uint8_t *) &verifierB64[0], sizeof(verifierB64), verifierB64Len)); + + verifierLen = chip::Base64Decode32(verifierB64, verifierB64Len, reinterpret_cast(verifierB64)); + ReturnErrorCodeIf(verifierLen > verifierBuf.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(verifierBuf.data(), verifierB64, verifierLen); + verifierBuf.reduce_size(verifierLen); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetSetupPasscode(uint32_t & setupPasscode) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kSetupPasscodeId, (uint8_t *) &setupPasscode, sizeof(setupPasscode), length)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::SetSetupPasscode(uint32_t setupPasscode) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR FactoryDataProvider::GetVendorName(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kVendorNameId, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetVendorId(uint16_t & vendorId) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kVidId, (uint8_t *) &vendorId, sizeof(vendorId), length)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductName(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kProductNameId, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductId(uint16_t & productId) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kPidId, (uint8_t *) &productId, sizeof(productId), length)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetPartNumber(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kPartNumber, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductURL(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kProductURL, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductLabel(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kProductLabel, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetSerialNumber(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kSerialNumberId, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetManufacturingDate(uint16_t & year, uint8_t & month, uint8_t & day) +{ + uint16_t length = 0; + uint8_t date[ConfigurationManager::kMaxManufacturingDateLength]; + + ReturnErrorOnFailure( + SearchForId(FactoryDataId::kManufacturingDateId, date, ConfigurationManager::kMaxManufacturingDateLength, length)); + date[length] = '\0'; + + if (length == 10 && isdigit(date[0]) && isdigit(date[1]) && isdigit(date[2]) && isdigit(date[3]) && date[4] == '-' && + isdigit(date[5]) && isdigit(date[6]) && date[7] == '-' && isdigit(date[8]) && isdigit(date[9])) + { + year = 1000 * (date[0] - '0') + 100 * (date[1] - '0') + 10 * (date[2] - '0') + date[3] - '0'; + month = 10 * (date[5] - '0') + date[6] - '0'; + day = 10 * (date[8] - '0') + date[9] - '0'; + } + else + { + ChipLogError(DeviceLayer, "Manufacturing date is not formatted correctly: YYYY-MM-DD."); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetHardwareVersion(uint16_t & hardwareVersion) +{ + uint16_t length = 0; + ReturnErrorOnFailure( + SearchForId(FactoryDataId::kHardwareVersionId, (uint8_t *) &hardwareVersion, sizeof(hardwareVersion), length)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetHardwareVersionString(char * buf, size_t bufSize) +{ + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kHardwareVersionStrId, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetRotatingDeviceIdUniqueId(MutableByteSpan & uniqueIdSpan) +{ + CHIP_ERROR err = CHIP_ERROR_NOT_IMPLEMENTED; +#if CHIP_ENABLE_ROTATING_DEVICE_ID + static_assert(ConfigurationManager::kRotatingDeviceIDUniqueIDLength >= ConfigurationManager::kMinRotatingDeviceIDUniqueIDLength, + "Length of unique ID for rotating device ID is smaller than minimum."); + uint16_t uniqueIdLen = 0; + err = SearchForId(FactoryDataId::kUniqueId, (uint8_t *) uniqueIdSpan.data(), uniqueIdSpan.size(), uniqueIdLen); +#if defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID) + if (err != CHIP_NO_ERROR) + { + constexpr uint8_t uniqueId[] = CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID; + + ReturnErrorCodeIf(sizeof(uniqueId) > uniqueIdSpan.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(uniqueIdSpan.data(), uniqueId, sizeof(uniqueId)); + uniqueIdLen = sizeof(uniqueId); + err = CHIP_NO_ERROR; + } +#endif // CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID + ReturnErrorOnFailure(err); + uniqueIdSpan.reduce_size(uniqueIdLen); +#endif + + return err; +} + +CHIP_ERROR FactoryDataProvider::GetProductFinish(app::Clusters::BasicInformation::ProductFinishEnum * finish) +{ + uint8_t productFinish; + uint16_t length = 0; + auto err = SearchForId(FactoryDataId::kProductFinish, &productFinish, sizeof(productFinish), length); + ReturnErrorCodeIf(err != CHIP_NO_ERROR, CHIP_ERROR_NOT_IMPLEMENTED); + + *finish = static_cast(productFinish); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR FactoryDataProvider::GetProductPrimaryColor(app::Clusters::BasicInformation::ColorEnum * primaryColor) +{ + uint8_t color; + uint16_t length = 0; + auto err = SearchForId(FactoryDataId::kProductPrimaryColor, &color, sizeof(color), length); + ReturnErrorCodeIf(err != CHIP_NO_ERROR, CHIP_ERROR_NOT_IMPLEMENTED); + + *primaryColor = static_cast(color); + + return CHIP_NO_ERROR; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nxp/k32w0/FactoryDataProvider.h b/src/platform/nxp/k32w0/FactoryDataProvider.h new file mode 100644 index 00000000000000..8a76a84800baab --- /dev/null +++ b/src/platform/nxp/k32w0/FactoryDataProvider.h @@ -0,0 +1,158 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * 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 +#include +#include +#include + +#include + +#include "CHIPPlatformConfig.h" + +#include + +/* Grab symbol for the base address from the linker file. */ +extern uint32_t __MATTER_FACTORY_DATA_START[]; +extern uint32_t __MATTER_FACTORY_DATA_SIZE[]; + +namespace chip { +namespace DeviceLayer { + +#define CHIP_FACTORY_DATA_ERROR(e) \ + ChipError(ChipError::Range::kLastRange, ((uint8_t) ChipError::Range::kLastRange << 2) | e, __FILE__, __LINE__) + +#define CHIP_FACTORY_DATA_SHA_CHECK CHIP_FACTORY_DATA_ERROR(0x01) +#define CHIP_FACTORY_DATA_HEADER_READ CHIP_FACTORY_DATA_ERROR(0x02) +#define CHIP_FACTORY_DATA_HASH_ID CHIP_FACTORY_DATA_ERROR(0x03) +#define CHIP_FACTORY_DATA_PDM_RESTORE CHIP_FACTORY_DATA_ERROR(0x04) +#define CHIP_FACTORY_DATA_NULL CHIP_FACTORY_DATA_ERROR(0x05) +#define CHIP_FACTORY_DATA_FLASH_ERASE CHIP_FACTORY_DATA_ERROR(0x06) +#define CHIP_FACTORY_DATA_FLASH_PROGRAM CHIP_FACTORY_DATA_ERROR(0x07) +#define CHIP_FACTORY_DATA_INTERNAL_FLASH_READ CHIP_FACTORY_DATA_ERROR(0x08) +#define CHIP_FACTORY_DATA_PDM_SAVE_RECORD CHIP_FACTORY_DATA_ERROR(0x09) +#define CHIP_FACTORY_DATA_PDM_READ_RECORD CHIP_FACTORY_DATA_ERROR(0x0A) +#define CHIP_FACTORY_DATA_RESTORE_MECHANISM CHIP_FACTORY_DATA_ERROR(0x0B) + +// Forward declaration to define the getter for factory data provider impl instance +class FactoryDataProviderImpl; + +/** + * @brief This class provides Commissionable data, Device Attestation Credentials, + * and Device Instance Info. + */ + +class FactoryDataProvider : public DeviceInstanceInfoProvider, + public CommissionableDataProvider, + public Credentials::DeviceAttestationCredentialsProvider +{ +public: + struct Header + { + uint32_t hashId; + uint32_t size; + uint8_t hash[4]; + }; + + // Default factory data IDs + enum FactoryDataId + { + kVerifierId = 1, + kSaltId, + kIcId, + kDacPrivateKeyId, + kDacCertificateId, + kPaiCertificateId, + kDiscriminatorId, + kSetupPasscodeId, + kVidId, + kPidId, + kCertDeclarationId, + kVendorNameId, + kProductNameId, + kSerialNumberId, + kManufacturingDateId, + kHardwareVersionId, + kHardwareVersionStrId, + kUniqueId, + kPartNumber, + kProductURL, + kProductLabel, + kProductFinish, + kProductPrimaryColor, + kMaxId + }; + + static uint32_t kFactoryDataStart; + static uint32_t kFactoryDataSize; + static uint32_t kFactoryDataPayloadStart; + static constexpr uint32_t kLengthOffset = 1; + static constexpr uint32_t kValueOffset = 3; + static constexpr uint32_t kHashLen = 4; + static constexpr size_t kHashId = 0xCE47BA5E; + + virtual ~FactoryDataProvider(); + + virtual CHIP_ERROR Init() = 0; + virtual CHIP_ERROR SignWithDacKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer) = 0; + CHIP_ERROR Validate(); + + CHIP_ERROR SearchForId(uint8_t searchedType, uint8_t * pBuf, size_t bufLength, uint16_t & length, uint32_t * offset = nullptr); + + // ===== Members functions that implement the CommissionableDataProvider + CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator) override; + CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator) override; + CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) override; + CHIP_ERROR GetSpake2pSalt(MutableByteSpan & saltBuf) override; + CHIP_ERROR GetSpake2pVerifier(MutableByteSpan & verifierBuf, size_t & verifierLen) override; + CHIP_ERROR GetSetupPasscode(uint32_t & setupPasscode) override; + CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override; + + // ===== Members functions that implement the DeviceAttestationCredentialsProvider + CHIP_ERROR GetCertificationDeclaration(MutableByteSpan & outBuffer) override; + CHIP_ERROR GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) override; + CHIP_ERROR GetDeviceAttestationCert(MutableByteSpan & outBuffer) override; + CHIP_ERROR GetProductAttestationIntermediateCert(MutableByteSpan & outBuffer) override; + CHIP_ERROR SignWithDeviceAttestationKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer) override; + + // ===== Members functions that implement the GenericDeviceInstanceInfoProvider + CHIP_ERROR GetVendorName(char * buf, size_t bufSize) override; + CHIP_ERROR GetVendorId(uint16_t & vendorId) override; + CHIP_ERROR GetProductName(char * buf, size_t bufSize) override; + CHIP_ERROR GetProductId(uint16_t & productId) override; + CHIP_ERROR GetPartNumber(char * buf, size_t bufSize) override; + CHIP_ERROR GetProductURL(char * buf, size_t bufSize) override; + CHIP_ERROR GetProductLabel(char * buf, size_t bufSize) override; + CHIP_ERROR GetHardwareVersionString(char * buf, size_t bufSize) override; + CHIP_ERROR GetSerialNumber(char * buf, size_t bufSize) override; + CHIP_ERROR GetManufacturingDate(uint16_t & year, uint8_t & month, uint8_t & day) override; + CHIP_ERROR GetHardwareVersion(uint16_t & hardwareVersion) override; + CHIP_ERROR GetRotatingDeviceIdUniqueId(MutableByteSpan & uniqueIdSpan) override; + CHIP_ERROR GetProductFinish(app::Clusters::BasicInformation::ProductFinishEnum * finish) override; + CHIP_ERROR GetProductPrimaryColor(app::Clusters::BasicInformation::ColorEnum * primaryColor) override; + +protected: + Header mHeader; +}; + +extern FactoryDataProvider & FactoryDataPrvd(); + +extern FactoryDataProviderImpl & FactoryDataPrvdImpl(); + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/FactoryDataProviderImpl.cpp b/src/platform/nxp/k32w0/FactoryDataProviderImpl.cpp similarity index 94% rename from src/platform/nxp/k32w/k32w0/FactoryDataProviderImpl.cpp rename to src/platform/nxp/k32w0/FactoryDataProviderImpl.cpp index dc6acd3b619272..93c09adf5c749d 100644 --- a/src/platform/nxp/k32w/k32w0/FactoryDataProviderImpl.cpp +++ b/src/platform/nxp/k32w0/FactoryDataProviderImpl.cpp @@ -17,9 +17,9 @@ #include #include -#include +#include -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR extern "C" { #include "Flash_Adapter.h" } @@ -31,7 +31,7 @@ namespace DeviceLayer { FactoryDataProviderImpl::FactoryDataProviderImpl() { -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR RegisterRestoreMechanism(FactoryDataDefaultRestoreMechanism); #endif } @@ -40,7 +40,7 @@ CHIP_ERROR FactoryDataProviderImpl::Init() { CHIP_ERROR error = CHIP_NO_ERROR; -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR error = ValidateWithRestore(); #else error = Validate(); @@ -91,7 +91,7 @@ CHIP_ERROR FactoryDataProviderImpl::SignWithDacKey(const ByteSpan & messageToSig return error; } -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR extern "C" WEAK CHIP_ERROR FactoryDataDefaultRestoreMechanism() { CHIP_ERROR error = CHIP_NO_ERROR; @@ -170,7 +170,7 @@ void FactoryDataProviderImpl::RegisterRestoreMechanism(RestoreMechanism restore) { mRestoreMechanisms.insert(mRestoreMechanisms.end(), restore); } -#endif // CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#endif // CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/FactoryDataProviderImpl.h b/src/platform/nxp/k32w0/FactoryDataProviderImpl.h similarity index 88% rename from src/platform/nxp/k32w/k32w0/FactoryDataProviderImpl.h rename to src/platform/nxp/k32w0/FactoryDataProviderImpl.h index b359a6d3f196cd..afeb802352bc7b 100644 --- a/src/platform/nxp/k32w/k32w0/FactoryDataProviderImpl.h +++ b/src/platform/nxp/k32w0/FactoryDataProviderImpl.h @@ -16,7 +16,7 @@ */ #pragma once -#include +#include #include namespace chip { @@ -24,7 +24,7 @@ namespace DeviceLayer { /** * This class provides K32W0 specific factory data features. - * CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR - enables factory data OTA + * CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR - enables factory data OTA */ class FactoryDataProviderImpl : public FactoryDataProvider @@ -35,7 +35,7 @@ class FactoryDataProviderImpl : public FactoryDataProvider CHIP_ERROR Init() override; CHIP_ERROR SignWithDacKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer) override; -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR using RestoreMechanism = CHIP_ERROR (*)(void); static CHIP_ERROR UpdateData(uint8_t * pBuf); diff --git a/src/platform/nxp/k32w/k32w0/InetPlatformConfig.h b/src/platform/nxp/k32w0/InetPlatformConfig.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/InetPlatformConfig.h rename to src/platform/nxp/k32w0/InetPlatformConfig.h diff --git a/src/platform/nxp/k32w/k32w0/K32W0Config.cpp b/src/platform/nxp/k32w0/K32W0Config.cpp similarity index 99% rename from src/platform/nxp/k32w/k32w0/K32W0Config.cpp rename to src/platform/nxp/k32w0/K32W0Config.cpp index 850a1b7398ef06..2054ef5da619bf 100644 --- a/src/platform/nxp/k32w/k32w0/K32W0Config.cpp +++ b/src/platform/nxp/k32w0/K32W0Config.cpp @@ -25,7 +25,7 @@ /* this file behaves like a config.h, comes first */ #include -#include +#include #include #include diff --git a/src/platform/nxp/k32w/k32w0/K32W0Config.h b/src/platform/nxp/k32w0/K32W0Config.h similarity index 98% rename from src/platform/nxp/k32w/k32w0/K32W0Config.h rename to src/platform/nxp/k32w0/K32W0Config.h index e364c045c25b1c..7080e62e747bf1 100644 --- a/src/platform/nxp/k32w/k32w0/K32W0Config.h +++ b/src/platform/nxp/k32w0/K32W0Config.h @@ -20,7 +20,7 @@ #include #include -#include +#include namespace chip { namespace DeviceLayer { diff --git a/src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.cpp b/src/platform/nxp/k32w0/KeyValueStoreManagerImpl.cpp similarity index 93% rename from src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.cpp rename to src/platform/nxp/k32w0/KeyValueStoreManagerImpl.cpp index 8dbf3e5bd71903..50f900583769f7 100644 --- a/src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.cpp +++ b/src/platform/nxp/k32w0/KeyValueStoreManagerImpl.cpp @@ -25,9 +25,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include namespace chip { @@ -41,6 +41,7 @@ Internal::RamStorage KeyValueStoreManagerImpl::sKeysStorage = { kNvmId_K Internal::RamStorage KeyValueStoreManagerImpl::sValuesStorage = { kNvmId_KvsValues, "Values" }; Internal::RamStorage KeyValueStoreManagerImpl::sSubscriptionStorage = { kNvmId_KvsSubscription, "Subscriptions" }; Internal::RamStorage KeyValueStoreManagerImpl::sGroupsStorage = { kNvmId_KvsGroups, "Groups" }; +Internal::RamStorage KeyValueStoreManagerImpl::sAclStorage = { kNvmId_KvsAcl, "Acl" }; KeyValueStoreManagerImpl KeyValueStoreManagerImpl::sInstance; @@ -63,6 +64,12 @@ static inline bool IsKeyRelatedToSubscriptions(const char * key) return _key.find("g/su") == 0; } +static inline bool IsKeyRelatedToAcl(const char * key) +{ + std::string _key(key); + return _key.find("/ac/") != std::string::npos; +} + static Internal::RamStorage * GetValStorage(const char * key) { Internal::RamStorage * storage = nullptr; @@ -70,6 +77,7 @@ static Internal::RamStorage * GetValStorage(const char * key) storage = IsKeyRelatedToSubscriptions(key) ? &KeyValueStoreManagerImpl::sSubscriptionStorage : &KeyValueStoreManagerImpl::sValuesStorage; storage = IsKeyRelatedToGroups(key) ? &KeyValueStoreManagerImpl::sGroupsStorage : storage; + storage = IsKeyRelatedToAcl(key) ? &KeyValueStoreManagerImpl::sAclStorage : storage; return storage; } @@ -81,6 +89,7 @@ static Internal::RamStorage * GetKeyStorage(const char * key) storage = IsKeyRelatedToSubscriptions(key) ? &KeyValueStoreManagerImpl::sSubscriptionStorage : &KeyValueStoreManagerImpl::sKeysStorage; storage = IsKeyRelatedToGroups(key) ? &KeyValueStoreManagerImpl::sGroupsStorage : storage; + storage = IsKeyRelatedToAcl(key) ? &KeyValueStoreManagerImpl::sAclStorage : storage; return storage; } @@ -145,6 +154,12 @@ CHIP_ERROR KeyValueStoreManagerImpl::Init() ChipLogProgress(DeviceLayer, "Cannot init KVS groups storage with id: %d. Error: %s", kNvmId_KvsGroups, ErrorStr(err)); } + err = sAclStorage.Init(Internal::RamStorage::kRamBufferInitialSize, true); + if (err != CHIP_NO_ERROR) + { + ChipLogProgress(DeviceLayer, "Cannot init KVS acl storage with id: %d. Error: %s", kNvmId_KvsAcl, ErrorStr(err)); + } + #if CONFIG_CHIP_K32W0_KVS_MOVE_KEYS_TO_SPECIFIC_STORAGE ChipLogProgress(DeviceLayer, "Moving some keys to dedicated storage"); MoveKeysAndValues(); diff --git a/src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.h b/src/platform/nxp/k32w0/KeyValueStoreManagerImpl.h similarity index 95% rename from src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.h rename to src/platform/nxp/k32w0/KeyValueStoreManagerImpl.h index 718b92df6beb39..57d877beaeaaf9 100644 --- a/src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.h +++ b/src/platform/nxp/k32w0/KeyValueStoreManagerImpl.h @@ -24,7 +24,7 @@ #pragma once -#include +#include namespace chip { namespace DeviceLayer { @@ -41,6 +41,8 @@ class KeyValueStoreManagerImpl final : public KeyValueStoreManager static Internal::RamStorage sSubscriptionStorage; /* Storage for KVS groups keys and values. Cleared during factory reset. */ static Internal::RamStorage sGroupsStorage; + /* Storage for KVS ACL keys and values. Cleared during factory reset. */ + static Internal::RamStorage sAclStorage; // Allow the KeyValueStoreManager interface class to delegate method calls to // the implementation methods provided by this class. diff --git a/src/platform/nxp/k32w/k32w0/Logging.cpp b/src/platform/nxp/k32w0/Logging.cpp similarity index 100% rename from src/platform/nxp/k32w/k32w0/Logging.cpp rename to src/platform/nxp/k32w0/Logging.cpp diff --git a/src/platform/nxp/k32w/k32w0/LowPowerHooks.cpp b/src/platform/nxp/k32w0/LowPowerHooks.cpp similarity index 100% rename from src/platform/nxp/k32w/k32w0/LowPowerHooks.cpp rename to src/platform/nxp/k32w0/LowPowerHooks.cpp diff --git a/src/platform/nxp/k32w/k32w0/NFCManagerImpl.cpp b/src/platform/nxp/k32w0/NFCManagerImpl.cpp similarity index 100% rename from src/platform/nxp/k32w/k32w0/NFCManagerImpl.cpp rename to src/platform/nxp/k32w0/NFCManagerImpl.cpp diff --git a/src/platform/nxp/k32w/k32w0/NFCManagerImpl.h b/src/platform/nxp/k32w0/NFCManagerImpl.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/NFCManagerImpl.h rename to src/platform/nxp/k32w0/NFCManagerImpl.h diff --git a/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp b/src/platform/nxp/k32w0/OTAFactoryDataProcessor.cpp similarity index 97% rename from src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp rename to src/platform/nxp/k32w0/OTAFactoryDataProcessor.cpp index d40d2ae3c17f5a..b6777c7dc32820 100644 --- a/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp +++ b/src/platform/nxp/k32w0/OTAFactoryDataProcessor.cpp @@ -18,9 +18,10 @@ #include #include -#include -#include -#include +#include +#include +#include +#include #include "PDM.h" #include "fsl_flash.h" diff --git a/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.h b/src/platform/nxp/k32w0/OTAFactoryDataProcessor.h similarity index 93% rename from src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.h rename to src/platform/nxp/k32w0/OTAFactoryDataProcessor.h index 8682d0bcc1e9dc..3109e64a8d0ae3 100644 --- a/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.h +++ b/src/platform/nxp/k32w0/OTAFactoryDataProcessor.h @@ -21,9 +21,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include namespace chip { diff --git a/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.cpp b/src/platform/nxp/k32w0/OTAFirmwareProcessor.cpp similarity index 96% rename from src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.cpp rename to src/platform/nxp/k32w0/OTAFirmwareProcessor.cpp index f63aa745378851..c7a1a2bbf435dd 100644 --- a/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.cpp +++ b/src/platform/nxp/k32w0/OTAFirmwareProcessor.cpp @@ -17,9 +17,9 @@ */ #include -#include -#include -#include +#include +#include +#include #include "OtaSupport.h" #include "OtaUtils.h" diff --git a/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.h b/src/platform/nxp/k32w0/OTAFirmwareProcessor.h similarity index 96% rename from src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.h rename to src/platform/nxp/k32w0/OTAFirmwareProcessor.h index 5a1092ef5fbc46..444243f0c885d0 100644 --- a/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.h +++ b/src/platform/nxp/k32w0/OTAFirmwareProcessor.h @@ -19,7 +19,7 @@ #pragma once #include -#include +#include namespace chip { diff --git a/src/platform/nxp/k32w/k32w0/OTAHooks.cpp b/src/platform/nxp/k32w0/OTAHooks.cpp similarity index 87% rename from src/platform/nxp/k32w/k32w0/OTAHooks.cpp rename to src/platform/nxp/k32w0/OTAHooks.cpp index a00ae428a0f40d..30df88177ffdc6 100644 --- a/src/platform/nxp/k32w/k32w0/OTAHooks.cpp +++ b/src/platform/nxp/k32w0/OTAHooks.cpp @@ -16,16 +16,16 @@ * limitations under the License. */ -#include +#include #include #include -#include -#include -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR -#include -#endif // CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#include +#include +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR +#include +#endif // CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR #include "OtaSupport.h" @@ -51,9 +51,9 @@ extern "C" WEAK CHIP_ERROR OtaHookInit() { static chip::OTAFirmwareProcessor sApplicationProcessor; static chip::OTAFirmwareProcessor sBootloaderProcessor; -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR static chip::OTAFactoryDataProcessor sFactoryDataProcessor; -#endif // CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#endif // CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR #if CONFIG_CHIP_K32W0_MAX_ENTRIES_TEST static chip::OTAFirmwareProcessor processors[8]; #endif @@ -64,9 +64,9 @@ extern "C" WEAK CHIP_ERROR OtaHookInit() auto & imageProcessor = chip::OTAImageProcessorImpl::GetDefaultInstance(); ReturnErrorOnFailure(imageProcessor.RegisterProcessor(1, &sApplicationProcessor)); ReturnErrorOnFailure(imageProcessor.RegisterProcessor(2, &sBootloaderProcessor)); -#if CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#if CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR ReturnErrorOnFailure(imageProcessor.RegisterProcessor(3, &sFactoryDataProcessor)); -#endif // CONFIG_CHIP_K32W0_OTA_FACTORY_DATA_PROCESSOR +#endif // CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR #if CONFIG_CHIP_K32W0_MAX_ENTRIES_TEST for (auto i = 0; i < 8; i++) { diff --git a/src/platform/nxp/k32w/k32w0/PlatformManagerImpl.cpp b/src/platform/nxp/k32w0/PlatformManagerImpl.cpp similarity index 98% rename from src/platform/nxp/k32w/k32w0/PlatformManagerImpl.cpp rename to src/platform/nxp/k32w0/PlatformManagerImpl.cpp index 198f9318ba41fb..1a98552cb7e0d9 100644 --- a/src/platform/nxp/k32w/k32w0/PlatformManagerImpl.cpp +++ b/src/platform/nxp/k32w0/PlatformManagerImpl.cpp @@ -29,8 +29,8 @@ #include #include #include -#include -#include +#include +#include #if CHIP_SYSTEM_CONFIG_USE_LWIP #include diff --git a/src/platform/nxp/k32w/k32w0/PlatformManagerImpl.h b/src/platform/nxp/k32w0/PlatformManagerImpl.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/PlatformManagerImpl.h rename to src/platform/nxp/k32w0/PlatformManagerImpl.h diff --git a/src/platform/nxp/k32w/k32w0/RamStorage.cpp b/src/platform/nxp/k32w0/RamStorage.cpp similarity index 99% rename from src/platform/nxp/k32w/k32w0/RamStorage.cpp rename to src/platform/nxp/k32w0/RamStorage.cpp index ea32a7f9c525c8..a478a582b98fb1 100644 --- a/src/platform/nxp/k32w/k32w0/RamStorage.cpp +++ b/src/platform/nxp/k32w0/RamStorage.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include "pdm_ram_storage_glue.h" diff --git a/src/platform/nxp/k32w/k32w0/RamStorage.h b/src/platform/nxp/k32w0/RamStorage.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/RamStorage.h rename to src/platform/nxp/k32w0/RamStorage.h diff --git a/src/platform/nxp/k32w/k32w0/SystemPlatformConfig.h b/src/platform/nxp/k32w0/SystemPlatformConfig.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/SystemPlatformConfig.h rename to src/platform/nxp/k32w0/SystemPlatformConfig.h diff --git a/src/platform/nxp/k32w/k32w0/SystemTimeSupport.cpp b/src/platform/nxp/k32w0/SystemTimeSupport.cpp similarity index 100% rename from src/platform/nxp/k32w/k32w0/SystemTimeSupport.cpp rename to src/platform/nxp/k32w0/SystemTimeSupport.cpp diff --git a/src/platform/nxp/k32w/k32w0/ThreadStackManagerImpl.cpp b/src/platform/nxp/k32w0/ThreadStackManagerImpl.cpp similarity index 100% rename from src/platform/nxp/k32w/k32w0/ThreadStackManagerImpl.cpp rename to src/platform/nxp/k32w0/ThreadStackManagerImpl.cpp diff --git a/src/platform/nxp/k32w/k32w0/ThreadStackManagerImpl.h b/src/platform/nxp/k32w0/ThreadStackManagerImpl.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/ThreadStackManagerImpl.h rename to src/platform/nxp/k32w0/ThreadStackManagerImpl.h diff --git a/src/platform/nxp/k32w/k32w0/args.gni b/src/platform/nxp/k32w0/args.gni similarity index 92% rename from src/platform/nxp/k32w/k32w0/args.gni rename to src/platform/nxp/k32w0/args.gni index 5f90c8248e207a..ca88077e4a59a1 100644 --- a/src/platform/nxp/k32w/k32w0/args.gni +++ b/src/platform/nxp/k32w0/args.gni @@ -16,7 +16,7 @@ import("//build_overrides/chip.gni") import("//build_overrides/nxp_sdk.gni") import("//build_overrides/openthread.gni") -nxp_platform = "k32w/k32w0" +nxp_platform = "k32w0" nxp_sdk_name = "k32w0_sdk" nxp_device_layer = "nxp/${nxp_platform}" nxp_use_lwip = false @@ -24,7 +24,7 @@ nxp_use_mbedtls_port = false if (getenv("NXP_K32W0_SDK_ROOT") == "") { k32w0_sdk_root = - "${nxp_sdk_matter_support_root}/github_sdk/k32w0_sdk/repo/core" + "${nxp_sdk_matter_support_root}/github_sdk/k32w0_sdk/repo" } else { k32w0_sdk_root = getenv("NXP_K32W0_SDK_ROOT") } @@ -72,6 +72,6 @@ openthread_external_mbedtls = mbedtls_target openthread_project_core_config_file = "OpenThreadConfig.h" openthread_core_config_platform_check_file = "openthread-core-k32w061-config-check.h" -openthread_core_config_deps = [ "${chip_root}/examples/platform/nxp/k32w/k32w0:openthread_core_config_k32w0_chip_examples" ] +openthread_core_config_deps = [ "${chip_root}/examples/platform/nxp/k32w0:openthread_core_config_k32w0_chip_examples" ] -openthread_external_platform = "${chip_root}/third_party/openthread/platforms/nxp/k32w/k32w0:libopenthread-k32w0" +openthread_external_platform = "${chip_root}/third_party/openthread/platforms/nxp/k32w0:libopenthread-k32w0" diff --git a/src/platform/nxp/k32w/k32w0/ble_function_mux.c b/src/platform/nxp/k32w0/ble_function_mux.c similarity index 100% rename from src/platform/nxp/k32w/k32w0/ble_function_mux.c rename to src/platform/nxp/k32w0/ble_function_mux.c diff --git a/src/platform/nxp/k32w/k32w0/ble_function_mux.h b/src/platform/nxp/k32w0/ble_function_mux.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/ble_function_mux.h rename to src/platform/nxp/k32w0/ble_function_mux.h diff --git a/src/platform/nxp/k32w/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp b/src/platform/nxp/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp similarity index 100% rename from src/platform/nxp/k32w/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp rename to src/platform/nxp/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp diff --git a/src/platform/nxp/k32w/k32w0/gatt_db.h b/src/platform/nxp/k32w0/gatt_db.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/gatt_db.h rename to src/platform/nxp/k32w0/gatt_db.h diff --git a/src/platform/nxp/k32w/k32w0/gatt_uuid128.h b/src/platform/nxp/k32w0/gatt_uuid128.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/gatt_uuid128.h rename to src/platform/nxp/k32w0/gatt_uuid128.h diff --git a/src/platform/nxp/k32w/k32w0/k32w0-chip-mbedtls-config.h b/src/platform/nxp/k32w0/k32w0-chip-mbedtls-config.h similarity index 100% rename from src/platform/nxp/k32w/k32w0/k32w0-chip-mbedtls-config.h rename to src/platform/nxp/k32w0/k32w0-chip-mbedtls-config.h diff --git a/third_party/openthread/platforms/nxp/k32w/k32w0/BUILD.gn b/third_party/openthread/platforms/nxp/k32w0/BUILD.gn similarity index 93% rename from third_party/openthread/platforms/nxp/k32w/k32w0/BUILD.gn rename to third_party/openthread/platforms/nxp/k32w0/BUILD.gn index 7ebd860e571371..0e73beae657f66 100644 --- a/third_party/openthread/platforms/nxp/k32w/k32w0/BUILD.gn +++ b/third_party/openthread/platforms/nxp/k32w0/BUILD.gn @@ -30,7 +30,7 @@ config("openthread_k32w0_config") { "${openthread_nxp_root}/src/k32w0/platform", "${openthread_nxp_root}/src/common", ] - include_dirs += [ "${chip_root}/examples/platform/nxp/k32w/k32w0" ] + include_dirs += [ "${chip_root}/examples/platform/nxp/k32w0" ] if (is_clang) { cflags = [ "-Wno-format-nonliteral" ] @@ -46,7 +46,7 @@ config("openthread_k32w0_config") { source_set("openthread_core_config_k32w0") { sources = [ - "${chip_root}/examples/platform/nxp/k32w/k32w0/app/project_include/OpenThreadConfig.h", + "${chip_root}/examples/platform/nxp/k32w0/app/project_include/OpenThreadConfig.h", "${openthread_nxp_root}/src/k32w0/k32w061/openthread-core-k32w061-config-check.h", ] @@ -95,7 +95,7 @@ source_set("libopenthread-k32w0") { "${nxp_sdk_build_root}:nxp_sdk", "${nxp_sdk_build_root}/${nxp_sdk_name}:mbedtls", "${openthread_root}/src/core:libopenthread_core_headers", - "../../..:libopenthread-platform", - "../../..:libopenthread-platform-utils", + "../..:libopenthread-platform", + "../..:libopenthread-platform-utils", ] } From 5c94dae6ea04d545567a7526c8d670c79111869e Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Thu, 11 Jul 2024 12:17:09 +0300 Subject: [PATCH 03/11] [NXP][common][k32w0] Update some files after k32w parent removal Signed-off-by: marius-alex-tache (cherry picked from commit 039c51d1731de3acc583da906583d8fc0f482b8d) --- .restyled.yaml | 1 - BUILD.gn | 6 +- config/k32w/toolchain/BUILD.gn | 8 +- docs/QUICK_START.md | 4 +- docs/guides/nxp/nxp_k32w0_ota_guide.md | 219 ++++++++++++++++++ .../nxp/nxp_k32w_android_commissioning.md | 37 +-- gn_build.sh | 2 +- scripts/build/builders/nxp.py | 2 +- 8 files changed, 249 insertions(+), 30 deletions(-) create mode 100644 docs/guides/nxp/nxp_k32w0_ota_guide.md diff --git a/.restyled.yaml b/.restyled.yaml index f55a0e367d91bd..c201729d30e41a 100644 --- a/.restyled.yaml +++ b/.restyled.yaml @@ -72,7 +72,6 @@ exclude: - "scripts/tools/zap/tests/outputs/**/*" # Matches generated output 1:1 - "examples/chef/sample_app_util/test_files/*.yaml" - "examples/chef/zzz_generated/**/*" - - "examples/platform/nxp/k32w/k32w0/scripts/demo_generated_certs/**/*" - "examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/zap-generated/*" # zap-generated files - "integrations/cloudbuild/*.yaml" # uglier long command line content - "scripts/run_codegen_targets.sh" # shellharden breaks for loops over command outputs diff --git a/BUILD.gn b/BUILD.gn index 82c8d855bfd291..8a78d8f55f3bd0 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -689,7 +689,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { if (enable_k32w_lighting_app_build) { group("k32w_lighting_app") { - deps = [ "${chip_root}/examples/lighting-app/nxp/k32w/k32w0/(${chip_root}/config/k32w/toolchain:k32w_lighting_app)" ] + deps = [ "${chip_root}/examples/lighting-app/nxp/k32w0/(${chip_root}/config/k32w/toolchain:k32w_lighting_app)" ] } extra_build_deps += [ ":k32w_lighting_app" ] @@ -697,7 +697,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { if (enable_k32w_lock_app_build) { group("k32w_lock_app") { - deps = [ "${chip_root}/examples/lock-app/nxp/k32w/k32w0/(${chip_root}/config/k32w/toolchain:k32w_lock_app)" ] + deps = [ "${chip_root}/examples/lock-app/nxp/k32w0/(${chip_root}/config/k32w/toolchain:k32w_lock_app)" ] } extra_build_deps += [ ":k32w_lock_app" ] @@ -705,7 +705,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { if (enable_k32w_shell_app_build) { group("k32w_shell_app") { - deps = [ "${chip_root}/examples/shell/nxp/k32w/k32w0/(${chip_root}/config/k32w/toolchain:k32w_shell_app)" ] + deps = [ "${chip_root}/examples/shell/nxp/k32w0/(${chip_root}/config/k32w/toolchain:k32w_shell_app)" ] } extra_build_deps += [ ":k32w_shell_app" ] diff --git a/config/k32w/toolchain/BUILD.gn b/config/k32w/toolchain/BUILD.gn index f386bcd0cd4c97..5189a17eff5a67 100644 --- a/config/k32w/toolchain/BUILD.gn +++ b/config/k32w/toolchain/BUILD.gn @@ -20,27 +20,27 @@ import("${build_root}/toolchain/arm_gcc/arm_toolchain.gni") arm_toolchain("k32w_lighting_app") { toolchain_args = { current_os = "freertos" - import("${chip_root}/examples/lighting-app/nxp/k32w/k32w0/args.gni") + import("${chip_root}/examples/lighting-app/nxp/k32w0/args.gni") } } arm_toolchain("k32w_lock_app") { toolchain_args = { current_os = "freertos" - import("${chip_root}/examples/lock-app/nxp/k32w/k32w0/args.gni") + import("${chip_root}/examples/lock-app/nxp/k32w0/args.gni") } } arm_toolchain("k32w_contact_sensor_app") { toolchain_args = { current_os = "freertos" - import("${chip_root}/examples/contact-sensor-app/nxp/k32w/k32w0/args.gni") + import("${chip_root}/examples/contact-sensor-app/nxp/k32w0/args.gni") } } arm_toolchain("k32w_shell_app") { toolchain_args = { current_os = "freertos" - import("${chip_root}/examples/shell/nxp/k32w/k32w0/args.gni") + import("${chip_root}/examples/shell/nxp/k32w0/args.gni") } } diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md index 10833e0c0c1ed7..7a000d94919f75 100644 --- a/docs/QUICK_START.md +++ b/docs/QUICK_START.md @@ -19,8 +19,8 @@ combination listed below. |
Border Router
|
Node
| Description | | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [**ot-br**](https://openthread.io/guides/border-router/build)
Thread Border Router
  • RasPi
  • BeagleBone | **lighting-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nrfconnect/README.md)
  • [NXP K32W](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nxp/k32w/k32w0/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/silabs/README.md) | The Lighting example is supported by many of the available Thread platforms. See the chip-tool controller instructions for how to actuate the light on/off cluster. | -| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lock-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/nrfconnect/README.md)
  • [NXP K32W](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/nxp/k32w/k32w0/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/efr32/README.md)
  • [TI CC13x2x7](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/cc13x2x7_26x2x7/README.md) | The Lock example is supported by many of the available Thread and Wi-Fi platforms. | +| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lighting-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nrfconnect/README.md)
  • [NXP K32W](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nxp/k32w0/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/silabs/README.md) | The Lighting example is supported by many of the available Thread platforms. See the chip-tool controller instructions for how to actuate the light on/off cluster. | +| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lock-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/nrfconnect/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/efr32/README.md)
  • [TI CC13x2x7](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/cc13x2x7_26x2x7/README.md) | The Lock example is supported by many of the available Thread and Wi-Fi platforms. | ## Controllers diff --git a/docs/guides/nxp/nxp_k32w0_ota_guide.md b/docs/guides/nxp/nxp_k32w0_ota_guide.md new file mode 100644 index 00000000000000..d5265d01e895d7 --- /dev/null +++ b/docs/guides/nxp/nxp_k32w0_ota_guide.md @@ -0,0 +1,219 @@ +# NXP K32W0x1 OTA guide + +## The Secondary Stage Bootloader (SSBL) + +There are multiple SSBL binaries provided by the SDK: + +| description | github SDK path | package SDK path | +| ------ | --------------- | ---------------- | +| Default SSBL | NA | `boards/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl.bin` | +| SSBL with PDM in external flash | `examples/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl_ext_flash_pdm_support.bin` | `boards/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl_ext_flash_pdm_support.bin` | + +The SSBL is also built alongside the reference application and it can be configured +according to the following table: + +| gn arg | default | description | +| ------ | ------- | ----------- | +| `ssbl_pdm_external_flash` | true | Enable/disable PDM in external flash | +| `ssbl_multi_image_support` | true | Enable/disable multi-image OTA feature | +| `ssbl_ota_entry_storage` | "OTACustomStorage_ExtFlash" | Configure custom OTA entry storage type | +| `ssbl_simple_hash_verification` | false | Enable/disable simple hash verification alternative to secure boot | +| `ssbl_optimize_spifi_flash` | false | Optimize SPIFI flash driver size | +| `ssbl_spifi_dual_mode` | false | Enable/disable SPIFI dual mode support (e.g. used by K32W041AM variant) | +| `ssbl_version` | 0 | Set SSBL version | +| `ssbl_use_redlib` | false | Enable/disable usage of redlib NXP library. If false, the build will use newlib nano | +| `ssbl_ota_data_in_external_flash` | false | Enable/disable OTA support for application with sections stored in external flash | + +## Simple hash verification + +When secure boot is not used, a simple hash can be appended at the end of the +image for integrity check. Applications should be built with +`chip_simple_hash_verification=1`. + +## Writing the SSBL + +Before writing the SSBL, it it recommanded to fully erase the internal flash. + +Using DK6Programmer utility from Windows: +``` +DK6Programmer.exe -V 5 -P 1000000 -s -e Flash +``` + +Using dk6prog from SPSDK: +``` +$ dk6prog listdev +This is an experimental utility. Use with caution! + +List of available devices: +DEVICE ID: DN038ZH3, VID: 0x403, PID: 0x6015, Serial number: DN038ZH3, Description: DK6 Carrier Board, Address: 9, Backend: Backend.PYFTDI +$ dk6prog -d DN038ZH3 erase 0 0x9de00 + +This is an experimental utility. Use with caution! + +Erasing memory [####################################] 100% +``` + +`chip-k32w0x-ssbl.bin` must be written at address 0 in the internal flash: + +Using DK6Programmer utility from Windows: +``` +DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x00="chip-k32w0x-ssbl.bin" +``` + +Using dk6prog from SPSDK: + +``` +$ dk6prog -d DN038ZH3 write 0 ~/path/to/bin/chip-k32w0x-ssbl.bin + +This is an experimental utility. Use with caution! + +Writing memory [####################################] 100% +Writen 7890 bytes to memory ID 0 at address 0x0 +``` + +### Writing the PSECT + +This is the list of all supported partitions: + +``` +0000000010000000 : SSBL partition + + 00000000 -----------> Start Address + 1000 ---------------> 0x0010 Number of 512-bytes pages + 00 -----------------> 0x00 Bootable flag + 00 -----------------> 0x00 Image type (0x00 = SSBL) + +00400000c9040101: Application partition + + 00400000 -----------> 0x00004000 Start Address + c904 ---------------> 0x04c9 Number of 512-bytes pages + 01 -----------------> 0x01 Bootable flag + 01 -----------------> 0x01 Image type (0x01 = Application) + +00000010800000fe: Ext Flash text partition + + 00000010 -----------> 0x10000000 Start Address (external flash) + 8000 ---------------> 0x0080 Number of 512-bytes pages + 00 -----------------> 0x00 Bootable flag + fe -----------------> 0xFE Image type (0xFE = Ext Flash text) + +00000110300200fc : OTA Image partition + + 00000110 -----------> 0x10010000 Start Address + 3002----------------> 0x0230 Number of 512-bytes pages + 00 -----------------> 0x00 Bootable flag + fc -----------------> 0xFC Image type (0xFC = OTA partition) + +00000510100000fd: NVM partition + + 00000510 -----------> 0x10050000 Start Address + 1000 ---------------> 0x0010 Number of 512-bytes pages + 00 -----------------> 0x00 Bootable flag + fd -----------------> 0xFD Image type (0xFD = NVM partition) +``` + +First, image directory 0 (SSBL partition) must be written: + +Using DK6Programmer utility from Windows: +``` +DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_0=0000000010000000 +``` + +Using dk6prog from SPSDK: +``` +$ dk6prog -d DN038ZH3 write 0x160 [[0000000010000000]] 8 PSECT + +This is an experimental utility. Use with caution! + +Writing memory [####################################] 100% +Writen 8 bytes to memory ID PSECT at address 0x160 +``` + +Here is the interpretation of the fields: + +``` +00000000 -> start address 0x00000000 +1000 -> size = 0x0010 pages of 512-bytes (= 8kB) +00 -> not bootable (only used by the SSBL to support SSBL update) +00 -> SSBL Image Type +``` + +Second, image directory 1 (application partition) must be written: + +Using DK6Programmer utility from Windows: +``` +DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_1=00400000C9040101 +``` + +Using dk6prog from SPSDK: +``` +$ dk6prog -d DN038ZH3 write 0x168 [[00400000C9040101]] 8 PSECT + +This is an experimental utility. Use with caution! + +Writing memory [####################################] 100% +Writen 8 bytes to memory ID PSECT at address 0x168 +``` + +Here is the interpretation of the fields: + +``` +00400000 -> start address 0x00004000 +C904 -> 0x4C9 pages of 512-bytes (= 612.5kB) +01 -> bootable flag +01 -> image type for the application +``` + +Please note the user can write additional partitions by writing `image_dir_2/3/4` +with the wanted configuration. In case of using the SPSDK tool, the appropriate offset +must be calculated + +## Removing SSBL Upgrade Region + +The example also offers the possibility to remove SSBL upgrade region, for reserving more +space for application level. + +A new flag `chip_reduce_ssbl_size` is introduced. In order to remove the SSBL upgrade region, +`chip_reduce_ssbl_size=true` must be provided to the build system + +The programming method will change: + +* Writing image directory 1 should change to + Using DK6Programmer utility from Windows: + ``` + DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_1=00200000D9040101 + ``` + + Using dk6prog from SPSDK: + ``` + $ dk6prog -d DN038ZH3 write 0x168 [[00200000D9040101]] 8 PSECT + + This is an experimental utility. Use with caution! + + Writing memory [####################################] 100% + Writen 8 bytes to memory ID PSECT at address 0x168 + ``` + + Here is the interpretation of the fields: + + ``` + 00200000 -> start address 0x00002000 + D904 -> 0x4D9 pages of 512-bytes (= 620.5kB) + 01 -> bootable flag + 01 -> image type for the application + ``` +* Matter application offset address should change to + Using DK6Programmer utility from Windows: + ``` + DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x2000="chip-k32w0x-contact-example.bin" + ``` + + Using dk6prog from SPSDK: + ``` + $ dk6prog -d DN038ZH3 write 0x2000 ~/path/to/bin/chip-k32w0x-contact-example.bin + + This is an experimental utility. Use with caution! + + Writing memory [####################################] 100% + Writen 596450 bytes to memory ID 0 at address 0x2000 + ``` \ No newline at end of file diff --git a/docs/guides/nxp/nxp_k32w_android_commissioning.md b/docs/guides/nxp/nxp_k32w_android_commissioning.md index 6d872f5f7ba268..dd1d017b1490b2 100644 --- a/docs/guides/nxp/nxp_k32w_android_commissioning.md +++ b/docs/guides/nxp/nxp_k32w_android_commissioning.md @@ -8,16 +8,17 @@ onto a CHIP-enabled Thread network.
    -- [Overview](#overview) -- [Requirements](#requirements) -- [Building and programming OpenThread RCP firmware](#building-and-programming-openthread-rcp-firmware) -- [Configuring PC as Thread Border Router](#configuring-pc-as-a-thread-border-router) -- [Building and programming NXP K32W Light Example Application](#building-and-programming-nxp-k32w-light-example-application) -- [Building and installing Android CHIPTool](#building-and-installing-android-chiptool) -- [Forming a Thread network on the Border Router](#forming-a-thread-network-on-the-border-router) -- [Preparing accessory device](#preparing-accessory-device) -- [Commissioning accessory device](#commissioning-accessory-device) -- [Sending CHIP commands](#sending-chip-commands) +- [Commissioning NXP K32W using Android CHIPTool](#commissioning-nxp-k32w-using-android-chiptool) + - [Overview](#overview) + - [Requirements](#requirements) + - [Building and programming OpenThread RCP firmware](#building-and-programming-openthread-rcp-firmware) + - [Configuring PC as a Thread Border Router](#configuring-pc-as-a-thread-border-router) + - [Building and programming NXP K32W Light Example Application](#building-and-programming-nxp-k32w-light-example-application) + - [Building and installing Android CHIPTool](#building-and-installing-android-chiptool) + - [Forming a Thread network on the Border Router](#forming-a-thread-network-on-the-border-router) + - [Preparing accessory device](#preparing-accessory-device) + - [Commissioning accessory device](#commissioning-accessory-device) + - [Sending CHIP commands](#sending-chip-commands)
    @@ -47,7 +48,7 @@ The following diagram shows the connectivity between network components required to allow communication between devices running the CHIPTool and Light applications: -![nxp_hw_connectivity](../../../examples/platform/nxp/k32w/k32w0/doc/images/nxp_hw_connectivity.JPG) +![nxp_hw_connectivity](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/nxp_hw_connectivity.JPG)
    @@ -111,7 +112,7 @@ the RCP firmware onto an K32W061 DK6: This creates an RCP image in the `bin/ot-rcp` directory. 6. Program the RCP firmware using the official - [OpenThread Flash Instructions](https://github.com/openthread/openthread/blob/master/examples/platforms/k32w/k32w061/README.md#flash-binaries). + [OpenThread Flash Instructions](https://github.com/openthread/openthread/blob/master/examples/platforms/k32w061/README.md#flash-binaries). 7. Plug-in the K32W061 DK6 to the PC. @@ -348,7 +349,7 @@ To make your PC work as a Thread Border Router, complete the following tasks: ## Building and programming NXP K32W Light Example Application See -[NXP K32W Light Example Application README](../../../examples/lighting-app/nxp/k32w/k32w0/README.md) +[NXP K32W Light Example Application README](../../../examples/lighting-app/nxp/k32w0/README.md) to learn how to build and program the light example onto an K32W061 DK6.
    @@ -396,7 +397,7 @@ CHIPTool is now ready to be used for commissioning. 3. Navigate to the _Form_ tab then push the _Form_ button using the default parameters: - ![nxp_form_nwk](../../../examples/platform/nxp/k32w/k32w0/doc/images/form_web.JPG) + ![nxp_form_nwk](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/form_web.JPG) 4. The message _Form operation is successful_ should be display after a few seconds. @@ -430,7 +431,7 @@ To prepare the accessory device for commissioning, complete the following steps: 1. Make sure that JP4 and JP7 jumpers are in leftmost position and a mini-USB cable is connected between the LPC connector and PC - ![nxp_connectors](../../../examples/platform/nxp/k32w/k32w0/doc/images/k32w-dk6-connectors.jpg) + ![nxp_connectors](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/k32w-dk6-connectors.jpg) 2. Use a terminal emulator (e.g.: Putty) to connect to the UART console of the accessory device. Use a baudrate of 115200. @@ -466,14 +467,14 @@ section, complete the following steps: progress with scanning, connection, and pairing. At the end of this process, the Thread network settings screen appears. - ![chiptool_main_screen](../../../examples/platform/nxp/k32w/k32w0/doc/images/chiptool_main_screen.png) + ![chiptool_main_screen](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/chiptool_main_screen.png) 6. In the Thread network settings screen, use the default settings and tap the _SAVE NETWORK_ button to send a Thread provisioning message to the accessory device. You will see the "Network provisioning completed" message when the accessory device successfully joins the Thread network. - ![chiptool_credentials](../../../examples/platform/nxp/k32w/k32w0/doc/images/thread_credentials.png) + ![chiptool_credentials](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/thread_credentials.png)
    @@ -483,7 +484,7 @@ section, complete the following steps: the provisioning is completed successfully and you are connected to the device. - ![on_off_cluster.png](../../../examples/platform/nxp/k32w/k32w0/doc/images/on_off_cluster.png) + ![on_off_cluster.png](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/on_off_cluster.png) 2. Verify that the text box on the screen is not empty and contains the IPv6 address of the accessory device. diff --git a/gn_build.sh b/gn_build.sh index 9f6c76571da593..e9110442a52293 100755 --- a/gn_build.sh +++ b/gn_build.sh @@ -164,7 +164,7 @@ if [[ ! -d "$NXP_K32W0_SDK_ROOT" ]]; then echo "Hint: Set \$NXP_K32W0_SDK_ROOT to enable building for K32W061" else echo 'To build the K32W lock sample as a standalone project': - echo "(cd $CHIP_ROOT/examples/lock-app/nxp/k32w/k32w0; gn gen out/debug --args='$k32w_sdk_args'; ninja -C out/debug)" + echo "(cd $CHIP_ROOT/examples/lock-app/nxp/k32w0; gn gen out/debug --args='$k32w_sdk_args'; ninja -C out/debug)" fi echo diff --git a/scripts/build/builders/nxp.py b/scripts/build/builders/nxp.py index b72015f0afc677..e119a78b5e2cc2 100644 --- a/scripts/build/builders/nxp.py +++ b/scripts/build/builders/nxp.py @@ -54,7 +54,7 @@ def Name(self, os_env): def FolderName(self, os_env): if self == NxpBoard.K32W0: - return 'k32w/k32w0' + return 'k32w0' elif self == NxpBoard.K32W1: return 'k32w/k32w1' elif self == NxpBoard.RW61X: From b542facbea7ec2431ecb5b8e2c586ddbe7675d8e Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Fri, 12 Jul 2024 15:29:42 +0300 Subject: [PATCH 04/11] [NXP][examples][k32w0] Remove examples/platform/nxp/k32w0 folder except docs examples/platform/nxp/k32w0/doc will remain in Matter repo. The other folders were moved to nxp_matter_support submodule. Signed-off-by: marius-alex-tache --- examples/platform/nxp/k32w/k32w0/BUILD.gn | 35 -- examples/platform/nxp/k32w/k32w0/app/BUILD.gn | 27 -- examples/platform/nxp/k32w/k32w0/app/args.gni | 28 -- .../k32w0/app/ldscripts/chip-k32w0x-linker.ld | 448 ------------------ .../app/project_include/OpenThreadConfig.h | 116 ----- .../nxp/k32w/k32w0/app/support/BUILD.gn | 52 -- .../k32w/k32w0/app/support/FreeRtosHooks.c | 274 ----------- .../k32w/k32w0/app/support/FreeRtosHooks.h | 42 -- .../nxp/k32w/k32w0/app/support/Memconfig.cpp | 184 ------- examples/platform/nxp/k32w/k32w0/args.gni | 32 -- .../common/CustomFactoryDataProvider.cpp | 51 -- .../k32w0/common/CustomFactoryDataProvider.h | 51 -- .../nxp/k32w/k32w0/doc/images/ssbl_bin.JPG | Bin 45572 -> 0 bytes .../k32w0/doc/images/ssbl_multi_image.JPG | Bin 114896 -> 0 bytes .../nxp/k32w/k32w0/doc/images/ssbl_select.JPG | Bin 107428 -> 0 bytes .../k32w0/doc/images/ssbl_simple_hash.JPG | Bin 56397 -> 0 bytes .../demo_factory_data_dut1.bin | Bin 1161 -> 0 bytes .../demo_factory_data_dut2.bin | Bin 1161 -> 0 bytes .../nxp/k32w/k32w0/scripts/detokenizer.py | 148 ------ .../nxp/k32w/k32w0/scripts/sign-outdir.py | 37 -- .../nxp/k32w/k32w0/util/LEDWidget.cpp | 86 ---- .../nxp/k32w/k32w0/util/include/LEDWidget.h | 41 -- .../doc/CustomFactoryDataProvider.md} | 0 .../k32w0/doc/images/chiptool_main_screen.png | Bin .../k32w0/doc/images/flash_location.JPG | Bin .../{k32w => }/k32w0/doc/images/form_web.JPG | Bin .../k32w0/doc/images/k32w-dk6-connectors.jpg | Bin .../{k32w => }/k32w0/doc/images/k32w-dk6.jpg | Bin .../{k32w => }/k32w0/doc/images/k32w-se.jpg | Bin .../k32w0/doc/images/mcux-sdk-download.JPG | Bin .../k32w0/doc/images/nxp_hw_connectivity.JPG | Bin .../k32w0/doc/images/on_off_cluster.png | Bin .../k32w0/doc/images/ota_topology.JPG | Bin .../k32w0/doc/images/pdm_ext_flash.JPG | Bin .../k32w0/doc/images/power_conf.JPG | Bin .../k32w0/doc/images/power_view.JPG | Bin .../k32w0/doc/images/select-sdk.JPG | Bin .../k32w0/doc/images/thread_credentials.png | Bin 38 files changed, 1652 deletions(-) delete mode 100644 examples/platform/nxp/k32w/k32w0/BUILD.gn delete mode 100644 examples/platform/nxp/k32w/k32w0/app/BUILD.gn delete mode 100644 examples/platform/nxp/k32w/k32w0/app/args.gni delete mode 100644 examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld delete mode 100644 examples/platform/nxp/k32w/k32w0/app/project_include/OpenThreadConfig.h delete mode 100644 examples/platform/nxp/k32w/k32w0/app/support/BUILD.gn delete mode 100644 examples/platform/nxp/k32w/k32w0/app/support/FreeRtosHooks.c delete mode 100644 examples/platform/nxp/k32w/k32w0/app/support/FreeRtosHooks.h delete mode 100644 examples/platform/nxp/k32w/k32w0/app/support/Memconfig.cpp delete mode 100644 examples/platform/nxp/k32w/k32w0/args.gni delete mode 100644 examples/platform/nxp/k32w/k32w0/common/CustomFactoryDataProvider.cpp delete mode 100644 examples/platform/nxp/k32w/k32w0/common/CustomFactoryDataProvider.h delete mode 100644 examples/platform/nxp/k32w/k32w0/doc/images/ssbl_bin.JPG delete mode 100644 examples/platform/nxp/k32w/k32w0/doc/images/ssbl_multi_image.JPG delete mode 100644 examples/platform/nxp/k32w/k32w0/doc/images/ssbl_select.JPG delete mode 100755 examples/platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG delete mode 100644 examples/platform/nxp/k32w/k32w0/scripts/demo_generated_factory_data/demo_factory_data_dut1.bin delete mode 100644 examples/platform/nxp/k32w/k32w0/scripts/demo_generated_factory_data/demo_factory_data_dut2.bin delete mode 100644 examples/platform/nxp/k32w/k32w0/scripts/detokenizer.py delete mode 100644 examples/platform/nxp/k32w/k32w0/scripts/sign-outdir.py delete mode 100644 examples/platform/nxp/k32w/k32w0/util/LEDWidget.cpp delete mode 100644 examples/platform/nxp/k32w/k32w0/util/include/LEDWidget.h rename examples/platform/nxp/{k32w/k32w0/common/README.md => k32w0/doc/CustomFactoryDataProvider.md} (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/chiptool_main_screen.png (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/flash_location.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/form_web.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/k32w-dk6-connectors.jpg (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/k32w-dk6.jpg (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/k32w-se.jpg (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/mcux-sdk-download.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/nxp_hw_connectivity.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/on_off_cluster.png (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/ota_topology.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/pdm_ext_flash.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/power_conf.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/power_view.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/select-sdk.JPG (100%) rename examples/platform/nxp/{k32w => }/k32w0/doc/images/thread_credentials.png (100%) diff --git a/examples/platform/nxp/k32w/k32w0/BUILD.gn b/examples/platform/nxp/k32w/k32w0/BUILD.gn deleted file mode 100644 index eb0c79742f3b99..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/BUILD.gn +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2020 Project CHIP Authors -# -# 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. - -import("//build_overrides/chip.gni") -import("//build_overrides/nxp_sdk.gni") - -import("${nxp_sdk_build_root}/nxp_sdk.gni") - -import("${nxp_sdk_build_root}/${nxp_sdk_name}/${nxp_sdk_name}.gni") - -config("chip_examples_project_config") { - include_dirs = [ - "app/project_include", - "${chip_root}", - ] -} - -source_set("openthread_core_config_k32w0_chip_examples") { - sources = [ "app/project_include/OpenThreadConfig.h" ] - - public_deps = [ "${chip_root}/third_party/openthread/platforms/nxp/k32w/k32w0:openthread_core_config_k32w0" ] - - public_configs = [ ":chip_examples_project_config" ] -} diff --git a/examples/platform/nxp/k32w/k32w0/app/BUILD.gn b/examples/platform/nxp/k32w/k32w0/app/BUILD.gn deleted file mode 100644 index 8651bd56be4ecd..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/app/BUILD.gn +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2020 Project CHIP Authors -# -# 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. - -import("//build_overrides/chip.gni") - -config("chip_examples_project_config") { - include_dirs = [ "app/project_include" ] -} - -source_set("openthread_core_config_k32w0_chip_examples") { - sources = [ "app/project_include/OpenThreadConfig.h" ] - - public_deps = [ "${chip_root}/third_party/openthread/platforms/nxp/k32w/k32w0:openthread_core_config_k32w0" ] - - public_configs = [ ":chip_examples_project_config" ] -} diff --git a/examples/platform/nxp/k32w/k32w0/app/args.gni b/examples/platform/nxp/k32w/k32w0/app/args.gni deleted file mode 100644 index 44a96b88a65b1b..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/app/args.gni +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2020 Project CHIP Authors -# -# 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. - -import("//build_overrides/chip.gni") - -import("${chip_root}/src/platform/nxp/k32w/k32w0/args.gni") - -openthread_project_core_config_file = "OpenThreadConfig.h" - -chip_ble_project_config_include = "" -chip_device_project_config_include = "" -chip_project_config_include = "" -chip_inet_project_config_include = "" -chip_system_project_config_include = "" - -chip_system_config_provide_statistics = false -chip_with_nlfaultinjection = true diff --git a/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld b/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld deleted file mode 100644 index c51182b104ec53..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (c) 2019, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of the copyright holder 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 HOLDER 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. - */ - -/** - * @file - * GCC linker script for K32W061/K32W041. - */ - -/******************* Map of K32W0 internal flash *********************************** - - 0x000A_0000 - - - - +---------------+ - - - - - - - - - | | - 8.5K | Flash config | - | RESERVED | 0x0009_DE00 - - - - +---------------+ - - - - - - - - - | | - 2K | Factory data | - | | 0x0009_D600 - - - - +---------------+ - - - - - - - - - | | - 1K | App metadata | - | | 0x0009_D200 - - - - +---------------+ - - - - - - - - - | | - 612.5K | Application | - | | 0x0000_4000 - - - - +---------------+ - - - - - - - - - | | - 8K | SSBL update | - | | 0x0000_2000 - - - - +---------------+ - - - - - - - - - | | - 8K | SSBL | - | | 0x0000_0000 - - - - +---------------+ - - - - - - - - - 0x0000_0000 - -* - If OTA is disabled, SSBL and SSBL updated region are not present. - - The only address range that changes is the application, which will span from - 0x0000_0000 to 0x0009_D200, having 628.5K max size. - *****************************************************************************/ - -/******************* Map of DK6 external flash *********************************** - - 0x0010_0000 - - - - +---------------+ - - - - - - - - - | | - 252K | PDM area | - | | 0x000C_1000 - - - - +---------------+ - - - - - - - - - | | - 4K | OTA entry | - | | 0x000C_0000 - - - - +---------------+ - - - - - - - - - | | - 768K | OTA area | - | | - - - - +---------------+ - - - - - - - - - 0x0000_0000 - - *****************************************************************************/ - -OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") - -/* - * stack size for the boot rom during warm boot and application - * 256 is sufficient (pwrm_test) but keep it large to 1024 - */ -BOOT_RESUME_STACK_SIZE = 1024; - -STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x01000; - -MEM_RAM0_BASE = 0x4000400; -MEM_RAM0_SIZE = 0x0015c00; - -MEM_RAM1_BASE = 0x4020000; -MEM_RAM1_SIZE = 0x10000; - -/* internal flash size: 640K */ -m_int_flash_size = 0xA0000; -m_int_sector_size = 512; - -/* first 8K: SSBL, next 8K: SSBL update region */ -m_app_start = DEFINED(__app_load_address__) ? __app_load_address__ : 0x0; - -/* flash_config: 8.5K */ -m_flash_config_size = 0x2200; - -/* sizeof(BOOT_BLOCK_T) + sizeof(IMAGE_CERT_T) + SIGNATURE_LEN + alignment = 1024 bytes */ -m_app_meta_data = 0x400; - -/* manufacturing data: 2K */ -m_factory_data_size = 0x800; - -/* 16K: SSBL + SSBL update region */ -m_ssbl_size = 0x4000; - -/* default app size, without OTA */ -m_app_default_size = m_int_flash_size - m_flash_config_size - m_app_meta_data - m_factory_data_size; - -m_app_size = DEFINED(__app_stated_size__) ? __app_stated_size__ : m_app_default_size; - -MEMORY -{ - Flash640 (rx) : ORIGIN = m_app_start, LENGTH = m_app_size - - SCRATCH_RAM(rwx) : ORIGIN = 0x4000000, LENGTH = 0x400 /* 1K bytes (alias SCRATCH_RAM) */ - RAM0 (rwx) : ORIGIN = 0x4000400, LENGTH = 0x0015BE0 /* [87K - 32] bytes (alias RAM) */ - reserved (rwx) : ORIGIN = 0x4015FE0, LENGTH = 0x20 /* 32 bytes (reserved for ROM code) */ - RAM1 (rwx) : ORIGIN = 0x4020000, LENGTH = 0x10000 /* 64K bytes (alias RAM2) */ -} - -/* Define a symbol for the top of each memory region */ -__top_RAM1 = MEM_RAM1_BASE + MEM_RAM1_SIZE; /* 64K bytes */ - -/* The second RAM bank is dedicated entirely to heap + stack. */ -HEAP_SIZE = MEM_RAM1_SIZE - STACK_SIZE; -ASSERT(((HEAP_SIZE + STACK_SIZE) == MEM_RAM1_SIZE), "Heap size + stack size should total RAM1 size."); - -/* set external flash properties - external flash is present on the DK6 board */ -m_ext_flash_size = 0x00100000; -m_ext_flash_base = 0x00000000; -m_ext_flash_sector_size = 4096; - -NVMSectorCountLink = 63; - -NV_STORAGE_SIZE = NVMSectorCountLink * m_ext_flash_sector_size; -NV_STORAGE_MAX_SECTORS = NVMSectorCountLink; -NV_STORAGE_SECTOR_SIZE = m_ext_flash_sector_size; -NV_STORAGE_START_ADDRESS = m_ext_flash_size - 1; -NV_STORAGE_END_ADDRESS = NV_STORAGE_START_ADDRESS - NV_STORAGE_SIZE + 1; - -INT_STORAGE_START = m_int_flash_size - 1; -INT_STORAGE_SIZE = m_int_flash_size; -INT_STORAGE_END = 0x00000000; -INT_STORAGE_SECTOR_SIZE = m_int_sector_size; - -FACTORY_DATA_START_ADDRESS = m_int_flash_size - m_flash_config_size - m_factory_data_size; -FACTORY_DATA_END_ADDRESS = FACTORY_DATA_START_ADDRESS + m_factory_data_size - 1; - -__ram_vector_table__ = 1; -vector_table_size = 0x120; -M_VECTOR_RAM_SIZE = DEFINED(__ram_vector_table__) ? vector_table_size : 0x0; - -__base_RAM0 = 0x4000400; - -ENTRY(ResetISR) - -SECTIONS -{ - /* MAIN TEXT SECTION */ - .header : ALIGN(4) - { - _flash_start = ABSOLUTE(.); - _flash_beg = ABSOLUTE(.); - - FILL(0xff) - __vectors_start__ = ABSOLUTE(.) ; - __VECTOR_TABLE = .; - __Vectors = .; - KEEP(*(.isr_vector)) - /* Global Section Table */ - . = ALIGN(4) ; - __section_table_start = .; - __data_section_table = .; - LONG(LOADADDR(.data)); - LONG( ADDR(.data)); - LONG( SIZEOF(.data)); - __data_section_table_end = .; - __bss_section_table = .; - LONG( ADDR(.bss)); - LONG( SIZEOF(.bss)); - __bss_section_table_end = .; - __section_table_end = . ; - /* End of Global Section Table */ - - FILL(0xff) - . = ALIGN (0x10); - } >Flash640 - - .ro_nonce : ALIGN(0x10) - { - _FlsNonceStart = ABSOLUTE(.); - *(.ro_nonce) /* nonce value is 16 bytes.*/ - FILL(0xff) - . = ALIGN (0x10); - } > Flash640 - - .ro_ota_header : ALIGN(0x10) - { - _enc_start = ABSOLUTE(.); - _enc_offset = (_enc_start & 0x0000000F); - _FlsOtaHeader = ABSOLUTE(.); - *(.ro_ota_header) /* Ota Header 69 bytes*/ - FILL(0xff) - . = ALIGN (0x10); - } > Flash640 - - .ro_se_lnkKey (ALIGN((. - _enc_offset), 16) + _enc_offset): - { - _FlsLinkKey = ABSOLUTE(.); - *(.ro_se_lnkKey) /* Link Key 16 bytes*/ - FILL(0xff) - . = ALIGN (0x10); - } > Flash640 - - .filler : - { - BYTE(0xff); - FILL(0xff); - . = ALIGN(0x40); - } > Flash640 - - .text : ALIGN(0x40) - { - FILL(0xff) - - *(.after_vectors*) - *(.text*) - - KEEP(*(.init)) - KEEP(*(.fini)) - - /* .ctors */ - *crtbegin.o(.ctors) - *crtbegin?.o(.ctors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) - *(SORT(.ctors.*)) - *(.ctors) - - /* .dtors */ - *crtbegin.o(.dtors) - *crtbegin?.o(.dtors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) - *(SORT(.dtors.*)) - *(.dtors) - - *(.rodata .rodata.* .constdata .constdata.*) - - . = ALIGN(4); - } > Flash640 - /* - * for exception handling/unwind - some Newlib functions (in common - * with C++ and STDC++) use this. - */ - .ARM.extab : ALIGN(4) - { - FILL(0xff) - *(.ARM.extab* .gnu.linkonce.armextab.*) - } > Flash640 - __exidx_start = .; - - .ARM.exidx : ALIGN(4) - { - FILL(0xff) - *(.ARM.exidx* .gnu.linkonce.armexidx.*) - } > Flash640 - __exidx_end = .; - - _etext = .; - - .heap (COPY): - { - __HeapBase = .; - _heap = .; - KEEP(*(.heap*)) - PROVIDE(end = .); - . = ALIGN(4); - __end__ = .; - _end_heap = .; - __HeapLimit = .; - } > RAM1 - - .interrupts_ram : ALIGN(0x200) - { - . = ALIGN(4); - __VECTOR_RAM__ = .; - __interrupts_ram_start__ = .; /* Create a global symbol at data start */ - *(.m_interrupts_ram) /* This is a user defined section */ - . += M_VECTOR_RAM_SIZE; - . = ALIGN(4); - __interrupts_ram_end__ = .; /* Define a global symbol at data end */ - } > RAM0 - .scratch_area (NOLOAD): ALIGN(4) - { - __scratch_area_start__ = .; - . = ALIGN(4) ; - . += 0x400; - __scratch_area_top__ = .; - } > SCRATCH_RAM - - /* MAIN DATA SECTION */ - .uninit_RESERVED : ALIGN(4) - { - KEEP(*(.bss.$RESERVED*)) - . = ALIGN(4) ; - _end_uninit_RESERVED = .; - } > RAM0 - - /* Main DATA section (RAM0) */ - .data : ALIGN(4) - { - FILL(0xff) - _data = . ; - *(vtable) - *(.ramfunc*) - *(.data*) - - . = ALIGN(4); - /* preinit data */ - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP(*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); - - . = ALIGN(4); - /* init data */ - __init_array_start = . ; - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - __init_array_end = . ; - - . = ALIGN(4); - /* finit data */ - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP(*(SORT(.fini_array.*))) - KEEP(*(.fini_array)) - PROVIDE_HIDDEN (__fini_array_end = .); - - KEEP(*(.jcr*)) - - . = ALIGN(4) ; - _edata = . ; - } > RAM0 AT>Flash640 - - __VECTOR_RAM = __VECTOR_RAM__; - __RAM_VECTOR_TABLE_SIZE_BYTES = DEFINED(__ram_vector_table__) ? (__interrupts_ram_end__ - __interrupts_ram_start__) : 0x0; - - /* MAIN BSS SECTION */ - .bss (NOLOAD) : ALIGN(4) - { - _bss = .; - *(.bss*) - *(COMMON) - *(g_u32NwkFrameCounter) - . = ALIGN(4) ; - _ebss = .; - - PROVIDE(end = .); - } > RAM0 - - /* BSS section for MAC buffers */ - .bss_MAC (NOLOAD) : ALIGN(4) - { - /* MAC buffer section: must be within 128kB block. __mac_buffer_base is - defined further down to be on 128kB alignment */ - __mac_buffer_start = .; - *(.mac_buffer) - - . = ALIGN (. != 0 ? 4 : 1) ; /* avoid empty segment */ - } > RAM0 - - /* BSS section for unretained contents */ - .bss_discard (NOLOAD) : ALIGN(4) - { - *(.discard.bss.pdm) - - . = ALIGN (. != 0 ? 4 : 1) ; /* avoid empty segment */ - } > RAM0 - - /* DEFAULT NOINIT SECTION */ - .noinit (NOLOAD): ALIGN(4) - { - _noinit = .; - *(.noinit*) - . = ALIGN(4) ; - _end_noinit = .; - } > RAM0 - - /* end of firmware RAM to be retained in power down mode */ - _end_fw_retention = .; - - /* non retained RAM section */ - /* stack for rom boot during warm resume */ - .boot_resume_stack (NOLOAD): ALIGN(4) - { - _boot_resume_stack = .; - *(.boot_resume_stack*) - . += BOOT_RESUME_STACK_SIZE; - . = ALIGN(4) ; - _end_boot_resume_stack = .; - } > RAM0 - - __nv_storage_end_address = NV_STORAGE_END_ADDRESS; - __nv_storage_start_address = NV_STORAGE_START_ADDRESS; - - PROVIDE(_vStackTop = __top_RAM1); - PROVIDE(__mac_buffer_base = (__mac_buffer_start & 0xfffe0000)); - PROVIDE(BOOT_GetStartPowerMode = 0x03000e9d); - PROVIDE(ROM_GetFlash = 0x03000e0d); - PROVIDE(pmc_reset_get_cause = 0x030046e9); - PROVIDE(psector_ReadIeee802_15_4_MacId1 = 0x030053b1); - PROVIDE(Chip_LOWPOWER_ChipSoftwareReset = 0x03003fa1); - PROVIDE(_pvHeapStart = _heap); - PROVIDE(_pvHeapLimit = _pvHeapStart + (HEAP_SIZE)); - PROVIDE(_scratch_buf_start = __scratch_area_start__); - PROVIDE(_scratch_buf_end = __scratch_area_top__); - - __StackLimit = _vStackTop - STACK_SIZE; - ASSERT(__StackLimit >= _end_heap, "Stack and heap spaces are overlapping!") - - __MATTER_FACTORY_DATA_START = FACTORY_DATA_START_ADDRESS; - __MATTER_FACTORY_DATA_SIZE = m_factory_data_size; - - /* The .ro_version section inside SSBL is set after the .m_interrupts sections, - * which is assumed to never change, so the offset remains the same across different - * SSBL versions. This symbol is used in Matter Application to retrieve the SSBL version. */ - __MATTER_SSBL_VERSION_START = 0x00000120; - - ASSERT(((m_app_start + m_app_size + m_app_meta_data + m_factory_data_size + m_flash_config_size) <= m_int_flash_size), - "Internal flash capacity exceeded") - -} diff --git a/examples/platform/nxp/k32w/k32w0/app/project_include/OpenThreadConfig.h b/examples/platform/nxp/k32w/k32w0/app/project_include/OpenThreadConfig.h deleted file mode 100644 index d76a51a6fbb55b..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/app/project_include/OpenThreadConfig.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * - * Copyright (c) 2020 Google LLC. - * 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. - */ - -/** - * @file - * Overrides to default OpenThread configuration. - * - */ - -#include "openthread-core-k32w061-config.h" - -#pragma once - -// Disable the Nxp-supplied OpenThread logging facilities -// and use the facilities provided by the Device Layer -// (see src/platform/K32W/Logging.cpp). -#undef OPENTHREAD_CONFIG_LOG_OUTPUT -#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_APP - -// When operating in a less than ideal RF environment, having a more forgiving configuration -// of OpenThread makes thread a great deal more reliable. -#undef OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY -#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY 120 // default is 28800 - -#undef OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT -#define OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT 15 // default is 3 - -#undef OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_INDIRECT -#define OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_INDIRECT 1 // default is 0 - -#undef OPENTHREAD_CONFIG_MAC_MAX_TX_ATTEMPTS_INDIRECT_POLLS -#define OPENTHREAD_CONFIG_MAC_MAX_TX_ATTEMPTS_INDIRECT_POLLS 16 // default is 4 - -// Enable periodic parent search to speed up finding a better parent. -#undef OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE -#define OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE 1 // default is 0 - -#undef OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD -#define OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD -45 // default is -65 - -#undef OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH -#define OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH 1 // default is 0 - -// Use smaller maximum interval to speed up reattaching. -#undef OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_MAXIMUM_INTERVAL -#define OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_MAXIMUM_INTERVAL (60 * 10 * 1000) // default 1200000 ms - -// disable unused features -#undef OPENTHREAD_CONFIG_COAP_API_ENABLE -#define OPENTHREAD_CONFIG_COAP_API_ENABLE 0 - -#undef OPENTHREAD_CONFIG_JOINER_ENABLE -#define OPENTHREAD_CONFIG_JOINER_ENABLE 0 - -#undef OPENTHREAD_CONFIG_COMMISSIONER_ENABLE -#define OPENTHREAD_CONFIG_COMMISSIONER_ENABLE 0 - -#undef OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE -#define OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE 0 - -#undef OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE -#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0 - -#undef OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE -#define OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE 0 - -#undef OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE -#define OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE 0 - -#undef OPENTHREAD_CONFIG_TCP_ENABLE -#define OPENTHREAD_CONFIG_TCP_ENABLE 0 - -#undef OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE 0 - -#undef OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE -#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE 0 - -#undef OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS -#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 44 - -#undef OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE -#define OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 0 - -#undef OPENTHREAD_CONFIG_SRP_SERVER_ENABLE -#define OPENTHREAD_CONFIG_SRP_SERVER_ENABLE 0 - -#undef OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE -#define OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE 0 - -#define UART_USE_SERIAL_MGR 1 -#define UART_USE_SERIAL_MGR_LOG 1 - -// #define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_DEBG - -// Use the NXP-supplied default platform configuration for remainder -// of OpenThread config options. -// -// NB: This file gets included during the build of OpenThread. Hence -// it cannot use "openthread" in the path to the included file. -// diff --git a/examples/platform/nxp/k32w/k32w0/app/support/BUILD.gn b/examples/platform/nxp/k32w/k32w0/app/support/BUILD.gn deleted file mode 100644 index 7b10c468a02fe6..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/app/support/BUILD.gn +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2020 Project CHIP Authors -# -# 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. - -import("//build_overrides/chip.gni") -import("//build_overrides/nxp_sdk.gni") - -config("support_config") { - include_dirs = [ "../../../../.." ] - - # Link options that provides replace dynamic memory operations in standard - # library with the FreeRTOS malloc in platform code. - ldflags = [ - # memory allocation -- these must be re-entrant and do locking - "-Wl,--wrap=malloc", - "-Wl,--wrap=free", - "-Wl,--wrap=realloc", - "-Wl,--wrap=calloc", - "-Wl,--wrap=MemoryAlloc", - - # Wrap these in case internal newlib call them (e.g. strdup will) - # directly call _malloc_r) - "-Wl,--wrap=_malloc_r", - "-Wl,--wrap=_realloc_r", - "-Wl,--wrap=_free_r", - "-Wl,--wrap=_calloc_r", - ] -} - -source_set("freertos_mbedtls_utils") { - sources = [ - "FreeRtosHooks.c", - "FreeRtosHooks.h", - "Memconfig.cpp", - ] - - deps = [ "${chip_root}/src/lib/support" ] - - cflags = [ "-Wconversion" ] - - public_configs = [ ":support_config" ] -} diff --git a/examples/platform/nxp/k32w/k32w0/app/support/FreeRtosHooks.c b/examples/platform/nxp/k32w/k32w0/app/support/FreeRtosHooks.c deleted file mode 100644 index 59cae3f6d14cf9..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/app/support/FreeRtosHooks.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * - * Copyright (c) 2020 Google LLC. - * 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 "FreeRtosHooks.h" - -#include "FreeRTOS.h" -#include "semphr.h" - -#include - -#include -#include - -#include "PDM.h" -#include "PWR_Interface.h" -#include "TimersManager.h" -#include "board.h" -#include "pdm_ram_storage_glue.h" - -/* Bluetooth Low Energy */ -#include "ble_config.h" -#include "l2ca_cb_interface.h" - -#include "controller_interface.h" - -#if defined gLoggingActive_d && (gLoggingActive_d > 0) -#include "dbg_logging.h" -#ifndef DBG_APP -#define DBG_APP 0 -#endif -#define APP_DBG_LOG(fmt, ...) \ - if (DBG_APP) \ - do \ - { \ - DbgLogAdd(__FUNCTION__, fmt, VA_NUM_ARGS(__VA_ARGS__), ##__VA_ARGS__); \ - } while (0); -#else -#define APP_DBG_LOG(...) -#endif - -#if (configUSE_TICKLESS_IDLE != 0) -#define DBG_PostStepTickAssess 0 -#if DBG_PostStepTickAssess && !gTimestampUseWtimer_c -#error "gTimestampUseWtimer_c required for DBG_PostStepTickAssess" -#endif -#if defined(gPWR_FreqScalingWFI) && (gPWR_FreqScalingWFI != 0) -/* this MACRO is required when gPWR_FreqScalingWFI is not equal to zero (system clock frequency - reduced in WFI) */ -#define App_SuppressTickInStopMode 1 -#else -#define App_SuppressTickInStopMode 0 -#endif -#if gPWR_CpuClk_48MHz -/* for systick accuracy, this is recommended to enable FRO calibration */ -#define gApp_SystemClockCalibration 1 -#endif -#endif - -#define PDM_MAX_WRITES_INFINITE 0xFF - -static inline void mutex_init(mbedtls_threading_mutex_t * p_mutex) -{ - assert(p_mutex != NULL); - *p_mutex = xSemaphoreCreateMutex(); - assert(*p_mutex != NULL); -} - -static inline void mutex_free(mbedtls_threading_mutex_t * p_mutex) -{ - assert(p_mutex != NULL); - assert(*p_mutex != NULL); - vSemaphoreDelete(*p_mutex); -} - -static inline int mutex_lock(mbedtls_threading_mutex_t * p_mutex) -{ - assert(p_mutex != NULL); - assert(*p_mutex != NULL); - return xSemaphoreTake(*p_mutex, portMAX_DELAY) != pdTRUE; -} - -static inline int mutex_unlock(mbedtls_threading_mutex_t * p_mutex) -{ - assert(p_mutex != NULL); - assert(*p_mutex != NULL); - return xSemaphoreGive(*p_mutex) != pdTRUE; -} - -void freertos_mbedtls_mutex_init(void) -{ - mbedtls_threading_set_alt(mutex_init, mutex_free, mutex_lock, mutex_unlock); -} - -void freertos_mbedtls_mutex_free(void) -{ - mbedtls_threading_free_alt(); -} - -#if (configUSE_TICKLESS_IDLE != 0) - -/* - * Setup the systick timer to generate the tick interrupts at the required - * frequency. - */ -void vPortSetupTimerInterrupt(void) -{ - /* Stop and clear the SysTick. */ - SysTick->CTRL = 0UL; - SysTick->VAL = 0UL; - -#if DBG_PostStepTickAssess - tickless_SystickCheckDriftInit(); -#endif - - /* configure module for tickless mode */ - tickless_init(); - -#if gApp_SystemClockCalibration - /* calibration on the internal FRO48MHz clock - this clock is inaccurate (2%), - * so it needs calibration if we want the systick to be accurate to less - * than 500pp. */ - tickless_StartFroCalibration(); -#endif - - /* Configure SysTick to interrupt at the requested rate. */ - SysTick_Config(CLOCK_GetFreq(kCLOCK_CoreSysClk) / configTICK_RATE_HZ); -} - -void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime) -{ - tmrlp_tickless_systick_t lp_ctx; - APP_DBG_LOG("xExpectedIdleTime = %d ticks %d ms", xExpectedIdleTime, xExpectedIdleTime * portTICK_PERIOD_MS); - OSA_InterruptDisable(); - - /* Do not go to sleep, lowpower or WFI if there is a pending task to schedule - * Scheduler is suspended when calling vPortSuppressTicksAndSleep, - * as interrupt are disabled if one is pending, eTaskConfirmSleepModeStatus - * will prevent sleep. - */ - if (eTaskConfirmSleepModeStatus() == eAbortSleep) - { - APP_DBG_LOG("task to schedule"); - /* Do nothing */ - } -#if gApp_SystemClockCalibration - /* Prevent lowpower / tickless mode if cal ongoing - get estimated core freq */ - else if (tickless_EstimateCoreClockFreq()) - { - /* can eventually enter sleep or re evaluate on next idle loop, the calibration shall take 4 ms average */ - // PWR_EnterSleep(); - } -#endif - - /* Do not go to power down if: - * - the RTOS is expecting a wake-up too close to the current date - * To worth a power down the xExpectedIdleTime should be - * > to 2 rtos tick (which includes residual_count worst margin + carry after wake-up worst margin) - * + enter/exist power down duration converted in RTOS tick - * - power down is not allowed - */ - else if ((xExpectedIdleTime > - (2 + ((PWR_SYSTEM_EXIT_LOW_POWER_DURATION_MS + PWR_SYSTEM_ENTER_LOW_POWER_DURATION_MS) / portTICK_PERIOD_MS) + 1))) - { - int result = PWR_CheckIfDeviceCanGoToSleepExt(); - if (result >= kPmPowerDown0) - { - PWR_WakeupReason_t wakeupReason; - lp_ctx.exitTicklessDuration32kTick = MILLISECONDS_TO_TICKS32K(PWR_SYSTEM_EXIT_LOW_POWER_DURATION_MS); - /* Tickless pre processing */ - tickless_PreProcessing(&lp_ctx, xExpectedIdleTime); - - /* Enter power down */ - wakeupReason = PWR_EnterPowerDown(); - - APP_DBG_LOG("wakeReason=%x", (uint16_t) wakeupReason.AllBits); - (void) wakeupReason; - - /* Tickless post processing */ - tickless_PostProcessing(&lp_ctx); - -#if DBG_PostStepTickAssess - if (wakeupReason.Bits.FromTMR == 1) - configASSERT(lp_ctx.idle_tick_jump == xExpectedIdleTime); - if (wakeupReason.Bits.DidPowerDown == 1) - configASSERT((wakeupReason.AllBits & ~0x8000U) != 0); -#endif - } - else if ((result == kPmSleep) || (result < 0)) - { -#if App_SuppressTickInStopMode - lp_ctx.exitTicklessDuration32kTick = 0; - /* Tickless pre processing */ - tickless_PreProcessing(&lp_ctx, xExpectedIdleTime); -#endif - - PWR_EnterSleep(); - -#if App_SuppressTickInStopMode - /* Tickless post processing */ - tickless_PostProcessing(&lp_ctx); -#endif - } - } - else - { -#if App_SuppressTickInStopMode - lp_ctx.exitTicklessDuration32kTick = 0; - /* Tickless pre processing */ - tickless_PreProcessing(&lp_ctx, xExpectedIdleTime); -#endif - - PWR_EnterSleep(); - -#if App_SuppressTickInStopMode - /* Tickless post processing */ - tickless_PostProcessing(&lp_ctx); -#endif - } - -#if DBG_PostStepTickAssess - tickless_SystickCheckDrift(); -#endif - - OSA_InterruptEnable(); -} -#endif /* (configUSE_TICKLESS_IDLE != 0) */ - -static void BOARD_ActionOnIdle(void) -{ -#if ((defined gTcxo32k_ModeEn_c) && (gTcxo32k_ModeEn_c != 0)) - BOARD_tcxo32k_compensation_run(2, 0); -#endif -#if ((defined gTcxo32M_ModeEn_c) && (gTcxo32M_ModeEn_c != 0)) - BOARD_tcxo32M_compensation_run(2, 10); /* 2 degrees - wait for 10us */ -#endif -} - -extern void OTAIdleActivities(void); -extern bool AppHaveBLEConnections(void); - -void vApplicationIdleHook(void) -{ -#if PDM_SAVE_IDLE - /* While in BLE connection during commissioning, PDM saves should be paused */ - if (!AppHaveBLEConnections()) - { - FS_vIdleTask(PDM_MAX_WRITES_INFINITE); - } -#endif - - OTAIdleActivities(); - -#if gAdcUsed_d - BOARD_ADCMeasure(); -#endif - - BOARD_ActionOnIdle(); -} diff --git a/examples/platform/nxp/k32w/k32w0/app/support/FreeRtosHooks.h b/examples/platform/nxp/k32w/k32w0/app/support/FreeRtosHooks.h deleted file mode 100644 index a27f72d498f81d..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/app/support/FreeRtosHooks.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright (c) 2020 Nest Labs, Inc. - * 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 - -typedef void * mbedtls_threading_mutex_t; - -extern void mbedtls_threading_set_alt(void (*mutex_init)(mbedtls_threading_mutex_t *), - void (*mutex_free)(mbedtls_threading_mutex_t *), - int (*mutex_lock)(mbedtls_threading_mutex_t *), - int (*mutex_unlock)(mbedtls_threading_mutex_t *)); - -extern void mbedtls_threading_free_alt(void); - -#ifdef __cplusplus -extern "C" { -#endif - -/**@brief Function for initializing alternative MbedTLS mutexes to enable the usage of the FreeRTOS implementation. */ -void freertos_mbedtls_mutex_init(void); - -/**@brief Function for releasing MbedTLS alternative mutexes. */ -void freertos_mbedtls_mutex_free(void); - -#ifdef __cplusplus -} -#endif diff --git a/examples/platform/nxp/k32w/k32w0/app/support/Memconfig.cpp b/examples/platform/nxp/k32w/k32w0/app/support/Memconfig.cpp deleted file mode 100644 index e5acf5ea3ceecb..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/app/support/Memconfig.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * - * Copyright (c) 2020-2021 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. - */ - -/** - * @file - * This file contains platform specific implementations for stdlib malloc/calloc/realloc/free - * functions, so that CHIPPlatformMemory* works as intended with the platform's heap. - */ - -#include "FreeRTOS.h" -#include "task.h" -#include -#include -#include - -#ifndef NDEBUG -#include -#include -#endif - -#include - -#if CHIP_CONFIG_MEMORY_DEBUG_DMALLOC -#include -#include -#endif // CHIP_CONFIG_MEMORY_DEBUG_DMALLOC - -/* Assumes 8bit bytes! */ -#define heapBITS_PER_BYTE ((size_t) 8) - -/* Define the linked list structure. This is used to link free blocks in order -of their memory address. */ -typedef struct A_BLOCK_LINK -{ - struct A_BLOCK_LINK * pxNextFreeBlock; /*<< The next free block in the list. */ - size_t xBlockSize; /*<< The size of the free block. */ -} BlockLink_t; - -/* The size of the structure placed at the beginning of each allocated memory -block must by correctly byte aligned. */ -static const size_t xHeapStructSize = - (sizeof(BlockLink_t) + ((size_t) (portBYTE_ALIGNMENT - 1))) & ~((size_t) portBYTE_ALIGNMENT_MASK); - -/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize -member of an BlockLink_t structure is set then the block belongs to the -application. When the bit is free the block is still part of the free heap -space. */ -static size_t xBlockAllocatedBit = ((size_t) 1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1); - -extern "C" { - -/* xPortMallocUsableSize relies on heap4 implementation. -It returns the size of an allocated block and it is -called by __wrap_realloc. -Thus it is validated in __wrap_realloc function that the allocated size -of the old_ptr is smaller than the allocated size of new_ptr */ -size_t xPortMallocUsableSize(void * pv) -{ - uint8_t * puc = (uint8_t *) pv; - BlockLink_t * pxLink; - void * voidp; - size_t sz = 0; - - if (pv != NULL) - { - vTaskSuspendAll(); - { - /* The memory being checked will have an BlockLink_t structure immediately - before it. */ - puc -= xHeapStructSize; - - /* This casting is to keep the compiler from issuing warnings. */ - voidp = (void *) puc; - pxLink = (BlockLink_t *) voidp; - - /* Check if the block is actually allocated. */ - configASSERT((pxLink->xBlockSize & xBlockAllocatedBit) != 0); - configASSERT(pxLink->pxNextFreeBlock == NULL); - - sz = (pxLink->xBlockSize & ~xBlockAllocatedBit) - xHeapStructSize; - } - (void) xTaskResumeAll(); - } - - return sz; -} - -void * __wrap_malloc(size_t size) -{ - return pvPortMalloc(size); -} - -void __wrap_free(void * ptr) -{ - vPortFree(ptr); -} - -void * __wrap_calloc(size_t num, size_t size) -{ - size_t total_size = num * size; - - // Handle overflow from (num * size) - if ((size != 0) && ((total_size / size) != num)) - { - return nullptr; - } - - void * ptr = pvPortMalloc(total_size); - if (ptr) - { - memset(ptr, 0, total_size); - } - else - { - ChipLogError(DeviceLayer, "__wrap_calloc: Could not allocate memory!"); - } - - return ptr; -} - -void * __wrap_realloc(void * ptr, size_t new_size) -{ - - void * new_ptr = NULL; - - if (new_size) - { - size_t old_ptr_size = xPortMallocUsableSize(ptr); - if (new_size <= old_ptr_size) - { - /* Return old pointer if the newly allocated size is smaller - or equal to the allocated size for old_ptr */ - return ptr; - } - - /* if old_ptr is NULL, then old_ptr_size is zero and new_ptr will be returned */ - new_ptr = pvPortMalloc(new_size); - - if (!new_ptr) - { - ChipLogError(DeviceLayer, "__wrap_realloc: Could not allocate memory!"); - return NULL; - } - - memset(reinterpret_cast(new_ptr) + old_ptr_size, 0, (new_size - old_ptr_size)); - memcpy(new_ptr, ptr, old_ptr_size); - } - - vPortFree(ptr); - - return new_ptr; -} - -void * __wrap__malloc_r(void * REENT, size_t size) -{ - return __wrap_malloc(size); -} - -void __wrap__free_r(void * REENT, void * ptr) -{ - __wrap_free(ptr); -} - -void * __wrap__realloc_r(void * REENT, void * ptr, size_t new_size) -{ - return __wrap_realloc(ptr, new_size); -} - -} // extern "C" diff --git a/examples/platform/nxp/k32w/k32w0/args.gni b/examples/platform/nxp/k32w/k32w0/args.gni deleted file mode 100644 index 5af5e1d18b3123..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/args.gni +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2020 Project CHIP Authors -# -# 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. - -import("//build_overrides/chip.gni") - -import("${chip_root}/src/platform/nxp/k32w/k32w0/args.gni") - -openthread_core_config_deps = [] -openthread_core_config_deps = [ "${chip_root}/examples/platform/nxp/k32w/k32w0:openthread_core_config_k32w0_chip_examples" ] - -chip_ble_project_config_include = "" -chip_device_project_config_include = "" -chip_project_config_include = "" -chip_inet_project_config_include = "" -chip_system_project_config_include = "" - -chip_system_config_provide_statistics = false -chip_with_nlfaultinjection = true - -chip_system_config_use_open_thread_inet_endpoints = true -chip_with_lwip = false diff --git a/examples/platform/nxp/k32w/k32w0/common/CustomFactoryDataProvider.cpp b/examples/platform/nxp/k32w/k32w0/common/CustomFactoryDataProvider.cpp deleted file mode 100644 index 7f2f6d9bc0cad0..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/common/CustomFactoryDataProvider.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright (c) 2022 Project CHIP Authors - * - * 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 "CustomFactoryDataProvider.h" - -namespace chip { -namespace DeviceLayer { - -static constexpr size_t kMaxLengthCustomId1 = 10; -static constexpr size_t kMaxLengthCustomId2 = 50; -static constexpr size_t kMaxLengthCustomId3 = 100; - -CustomFactoryDataProvider::CustomFactoryDataProvider() -{ - // Custom ids should be from a range that does not overlap with the standard factory data range. - static_assert((uint16_t) CustomFactoryIds::kCustomId1 >= (uint16_t) FactoryDataProvider::FactoryDataId::kMaxId); -} - -CHIP_ERROR CustomFactoryDataProvider::ParseFunctionExample() -{ - uint8_t data_buf[kMaxLengthCustomId1]; - MutableByteSpan buffer(data_buf); - memset(buffer.data(), 0, buffer.size()); - uint16_t userDataSize = 0; - // A user can use FactoryDataProvider::SearchForId to read an id from internal - // flash factory data section. - auto * provider = static_cast(DeviceLayer::GetDeviceInstanceInfoProvider()); - ReturnErrorOnFailure((provider != nullptr) ? CHIP_NO_ERROR : CHIP_ERROR_INVALID_ADDRESS); - ReturnErrorOnFailure(provider->SearchForId(CustomFactoryIds::kCustomId1, buffer.data(), buffer.size(), userDataSize)); - - // Data should now be ready for custom parsing. - - return CHIP_NO_ERROR; -} - -} // namespace DeviceLayer -} // namespace chip diff --git a/examples/platform/nxp/k32w/k32w0/common/CustomFactoryDataProvider.h b/examples/platform/nxp/k32w/k32w0/common/CustomFactoryDataProvider.h deleted file mode 100644 index dfb722d67fdc11..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/common/CustomFactoryDataProvider.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright (c) 2022 Project CHIP Authors - * - * 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 { -namespace DeviceLayer { - -/** - * @brief This is an example class that extends the platform factory data provider - * to support custom functionality. Users should implement their own custom - * provider based on this example. - */ - -class CustomFactoryDataProvider -{ -public: - /* Custom IDs should start from at least FactoryDataId::kMaxId, which is - * the next available valid ID. Last default ID is kMaxId - 1. - */ - enum CustomFactoryIds - { - kCustomId1 = 200, // Random id that is greater than FactoryDataId::kMaxId. - kCustomId2, - kCustomId3, - kCustomMaxId - }; - - CustomFactoryDataProvider(); - - /* Declare here custom functions to be implemented. */ - CHIP_ERROR ParseFunctionExample(); -}; - -} // namespace DeviceLayer -} // namespace chip diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/ssbl_bin.JPG b/examples/platform/nxp/k32w/k32w0/doc/images/ssbl_bin.JPG deleted file mode 100644 index fb871d45aeb5f98f646d44beed6819c14eb87cc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45572 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

    G^k2O*s2mrL0`Y)7PQbm=4?yBI0yR7;M{ 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

    gOi$J=+rj#`=uYE6{J>CtMBGOUQuraQf`;L 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 diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/ssbl_select.JPG b/examples/platform/nxp/k32w/k32w0/doc/images/ssbl_select.JPG deleted file mode 100644 index 79ea51b62698e85ebbb68dbc66abf9097e02ba4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107428 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 diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG b/examples/platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG deleted file mode 100755 index 9ec701b0bb1d86cac72c2df63702fde227d33f77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56397 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 z 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 /dev/null) && (dos2unix " + sign_images_path + ")", shell=True) - - # Call sign_images.sh script with the output directory - cmd = sign_images_path + " " + os.getcwd() - if args.simple_hash: - cmd = cmd + " -SimpleHashVerification" - - subprocess.call(cmd, shell=True) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - "--simple-hash", - help="When enabled, adds a hash of the whole image at the end of the binary.", - action="store_true" - ) - args = parser.parse_args() - - main(args) diff --git a/examples/platform/nxp/k32w/k32w0/util/LEDWidget.cpp b/examples/platform/nxp/k32w/k32w0/util/LEDWidget.cpp deleted file mode 100644 index fec3a5b0afca70..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/util/LEDWidget.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2019 Google LLC. - * 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 "LEDWidget.h" - -#include - -void LEDWidget::Init(LED_t led) -{ - mLastChangeTimeMS = 0; - mBlinkOnTimeMS = 0; - mBlinkOffTimeMS = 0; - mGPIONum = led; - mState = false; - - Set(false); -} - -void LEDWidget::Invert(void) -{ - Set(!mState); -} - -void LEDWidget::Set(bool state) -{ - mLastChangeTimeMS = mBlinkOnTimeMS = mBlinkOffTimeMS = 0; - DoSet(state); -} - -void LEDWidget::Blink(uint32_t changeRateMS) -{ - Blink(changeRateMS, changeRateMS); -} - -void LEDWidget::Blink(uint32_t onTimeMS, uint32_t offTimeMS) -{ - mBlinkOnTimeMS = onTimeMS; - mBlinkOffTimeMS = offTimeMS; - Animate(); -} - -void LEDWidget::Animate() -{ - if (mBlinkOnTimeMS != 0 && mBlinkOffTimeMS != 0) - { - uint64_t nowMS = chip::System::SystemClock().GetMonotonicMilliseconds64().count(); - uint64_t stateDurMS = mState ? mBlinkOnTimeMS : mBlinkOffTimeMS; - uint64_t nextChangeTimeMS = mLastChangeTimeMS + stateDurMS; - - if (nextChangeTimeMS < nowMS) - { - DoSet(!mState); - mLastChangeTimeMS = nowMS; - } - } -} - -void LEDWidget::DoSet(bool state) -{ - mState = state; - - if (state) - { - LED_TurnOnLed(mGPIONum); - } - else - { - LED_TurnOffLed(mGPIONum); - } -} diff --git a/examples/platform/nxp/k32w/k32w0/util/include/LEDWidget.h b/examples/platform/nxp/k32w/k32w0/util/include/LEDWidget.h deleted file mode 100644 index f725eeaa9d74f5..00000000000000 --- a/examples/platform/nxp/k32w/k32w0/util/include/LEDWidget.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * Copyright (c) 2020 Google LLC. - * 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 "LED.h" - -#pragma once - -class LEDWidget -{ -public: - void Init(LED_t gpioNum); - void Set(bool state); - void Invert(void); - void Blink(uint32_t changeRateMS); - void Blink(uint32_t onTimeMS, uint32_t offTimeMS); - void Animate(); - -private: - uint64_t mLastChangeTimeMS; - uint32_t mBlinkOnTimeMS; - uint32_t mBlinkOffTimeMS; - LED_t mGPIONum; - bool mState; - - void DoSet(bool state); -}; diff --git a/examples/platform/nxp/k32w/k32w0/common/README.md b/examples/platform/nxp/k32w0/doc/CustomFactoryDataProvider.md similarity index 100% rename from examples/platform/nxp/k32w/k32w0/common/README.md rename to examples/platform/nxp/k32w0/doc/CustomFactoryDataProvider.md diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/chiptool_main_screen.png b/examples/platform/nxp/k32w0/doc/images/chiptool_main_screen.png similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/chiptool_main_screen.png rename to examples/platform/nxp/k32w0/doc/images/chiptool_main_screen.png diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/flash_location.JPG b/examples/platform/nxp/k32w0/doc/images/flash_location.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/flash_location.JPG rename to examples/platform/nxp/k32w0/doc/images/flash_location.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/form_web.JPG b/examples/platform/nxp/k32w0/doc/images/form_web.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/form_web.JPG rename to examples/platform/nxp/k32w0/doc/images/form_web.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/k32w-dk6-connectors.jpg b/examples/platform/nxp/k32w0/doc/images/k32w-dk6-connectors.jpg similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/k32w-dk6-connectors.jpg rename to examples/platform/nxp/k32w0/doc/images/k32w-dk6-connectors.jpg diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/k32w-dk6.jpg b/examples/platform/nxp/k32w0/doc/images/k32w-dk6.jpg similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/k32w-dk6.jpg rename to examples/platform/nxp/k32w0/doc/images/k32w-dk6.jpg diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/k32w-se.jpg b/examples/platform/nxp/k32w0/doc/images/k32w-se.jpg similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/k32w-se.jpg rename to examples/platform/nxp/k32w0/doc/images/k32w-se.jpg diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/mcux-sdk-download.JPG b/examples/platform/nxp/k32w0/doc/images/mcux-sdk-download.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/mcux-sdk-download.JPG rename to examples/platform/nxp/k32w0/doc/images/mcux-sdk-download.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/nxp_hw_connectivity.JPG b/examples/platform/nxp/k32w0/doc/images/nxp_hw_connectivity.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/nxp_hw_connectivity.JPG rename to examples/platform/nxp/k32w0/doc/images/nxp_hw_connectivity.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/on_off_cluster.png b/examples/platform/nxp/k32w0/doc/images/on_off_cluster.png similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/on_off_cluster.png rename to examples/platform/nxp/k32w0/doc/images/on_off_cluster.png diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/ota_topology.JPG b/examples/platform/nxp/k32w0/doc/images/ota_topology.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/ota_topology.JPG rename to examples/platform/nxp/k32w0/doc/images/ota_topology.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/pdm_ext_flash.JPG b/examples/platform/nxp/k32w0/doc/images/pdm_ext_flash.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/pdm_ext_flash.JPG rename to examples/platform/nxp/k32w0/doc/images/pdm_ext_flash.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/power_conf.JPG b/examples/platform/nxp/k32w0/doc/images/power_conf.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/power_conf.JPG rename to examples/platform/nxp/k32w0/doc/images/power_conf.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/power_view.JPG b/examples/platform/nxp/k32w0/doc/images/power_view.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/power_view.JPG rename to examples/platform/nxp/k32w0/doc/images/power_view.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/select-sdk.JPG b/examples/platform/nxp/k32w0/doc/images/select-sdk.JPG similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/select-sdk.JPG rename to examples/platform/nxp/k32w0/doc/images/select-sdk.JPG diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/thread_credentials.png b/examples/platform/nxp/k32w0/doc/images/thread_credentials.png similarity index 100% rename from examples/platform/nxp/k32w/k32w0/doc/images/thread_credentials.png rename to examples/platform/nxp/k32w0/doc/images/thread_credentials.png From 7de26da331adebd2b579d21120239220cad1d42b Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Thu, 11 Jul 2024 14:09:10 +0300 Subject: [PATCH 05/11] [NXP][doc][k32w0] Updating path to update_nxp_sdk.py script Signed-off-by: Gatien Chapon (cherry picked from commit c729b5c6f8ac3b21345482d5f1585d1df1032770) --- examples/contact-sensor-app/nxp/k32w0/README.md | 5 +++-- examples/lighting-app/nxp/k32w0/README.md | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/contact-sensor-app/nxp/k32w0/README.md b/examples/contact-sensor-app/nxp/k32w0/README.md index 542dc31f314a0a..c0d697a361b7aa 100644 --- a/examples/contact-sensor-app/nxp/k32w0/README.md +++ b/examples/contact-sensor-app/nxp/k32w0/README.md @@ -46,6 +46,7 @@ network. - [Low power](#low-power) - [Known issues low power](#known-issues-low-power) + ## Introduction ![K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-dk6.jpg) @@ -214,10 +215,10 @@ user@ubuntu:~/Desktop/git/connectedhomeip$ source scripts/bootstrap.sh - Step 3: Init NXP SDK(s) ``` -user@ubuntu:~/Desktop/git/connectedhomeip$ scripts/setup/nxp/update_nxp_sdk.py --platform k32w0 +user@ubuntu:~/Desktop/git/connectedhomeip$ third_party/nxp/nxp_matter_support/scripts/update_nxp_sdk.py --platform k32w0 ``` -Note: By default setup/nxp/update_nxp_sdk.py will try to initialize all NXP +Note: By default update_nxp_sdk.py will try to initialize all NXP SDKs. Arg "-- help" could be used to view all available options. - Start building the application: diff --git a/examples/lighting-app/nxp/k32w0/README.md b/examples/lighting-app/nxp/k32w0/README.md index 72af8f444225ac..6f162432811da2 100644 --- a/examples/lighting-app/nxp/k32w0/README.md +++ b/examples/lighting-app/nxp/k32w0/README.md @@ -227,10 +227,10 @@ user@ubuntu:~/Desktop/git/connectedhomeip$ source scripts/bootstrap.sh - Step 3: Init NXP SDK(s) ``` -user@ubuntu:~/Desktop/git/connectedhomeip$ scripts/setup/nxp/update_nxp_sdk.py --platform k32w0 +user@ubuntu:~/Desktop/git/connectedhomeip$ third_party/nxp/nxp_matter_support/scripts/update_nxp_sdk.py --platform k32w0 ``` -Note: By default setup/nxp/update_nxp_sdk.py will try to initialize all NXP +Note: By default update_nxp_sdk.py will try to initialize all NXP SDKs. Arg "-- help" could be used to view all available options. - Start building the application: From 54e03fc985d211270923c72ba16f8d68cfe14cb2 Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Thu, 11 Jul 2024 14:04:34 +0300 Subject: [PATCH 06/11] [NXP][examples][k32w0] Update paths after nxp_matter_support switch Signed-off-by: marius-alex-tache (cherry picked from commit 871ea893b3ad1f7d6d7b6943827a666b89f50b8f) --- .../contact-sensor-app/nxp/k32w0/BUILD.gn | 2 +- .../contact-sensor-app/nxp/k32w0/README.md | 24 +++++++++---------- examples/lighting-app/nxp/k32w0/BUILD.gn | 2 +- examples/lighting-app/nxp/k32w0/README.md | 20 ++++++++-------- src/platform/nxp/k32w0/args.gni | 4 ++-- .../openthread/platforms/nxp/k32w0/BUILD.gn | 4 ++-- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/contact-sensor-app/nxp/k32w0/BUILD.gn b/examples/contact-sensor-app/nxp/k32w0/BUILD.gn index 8ca969aaaba95c..6d95d90a167653 100644 --- a/examples/contact-sensor-app/nxp/k32w0/BUILD.gn +++ b/examples/contact-sensor-app/nxp/k32w0/BUILD.gn @@ -41,7 +41,7 @@ if (chip_pw_tokenizer_logging) { assert(current_os == "freertos") -k32w0_platform_dir = "${chip_root}/examples/platform/nxp/k32w0" +k32w0_platform_dir = "${nxp_sdk_matter_support_root}/examples/platform/k32w0" k32w0_sdk("sdk") { sources = [ diff --git a/examples/contact-sensor-app/nxp/k32w0/README.md b/examples/contact-sensor-app/nxp/k32w0/README.md index c0d697a361b7aa..c93dd8d67c1b22 100644 --- a/examples/contact-sensor-app/nxp/k32w0/README.md +++ b/examples/contact-sensor-app/nxp/k32w0/README.md @@ -49,7 +49,7 @@ network. ## Introduction -![K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-dk6.jpg) +![K32W061 DK6](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/k32w-dk6.jpg) The K32W061 contact sensor example application provides a working demonstration of a connected contact sensor device, built using the Project CHIP codebase and @@ -79,7 +79,7 @@ Deployment of this firmware configuration requires the K32W061 board setups using the K32W061 module board, SE051 Expansion board and Generic Expansion board as shown below: -![SE051H + K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-se.jpg) +![SE051H + K32W061 DK6](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/k32w-se.jpg) The SE051H Secure Element extension may be used for best in class security and offloading some of the Project CHIP cryptographic operations. Depending on your @@ -356,17 +356,17 @@ See [Guide for writing manufacturing data on NXP devices](../../../../docs/guides/nxp/nxp_manufacturing_flow.md). There are factory data generated binaries available in -examples/platform/nxp/k32w0/scripts/demo_generated_factory_data folder. +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data` folder. These are based on the DAC, PAI and PAA certificates found in scripts/tools/nxp/demo_generated_certs folder. The demo_factory_data_dut1.bin uses the DAC certificate and private key found in -examples/platform/nxp/k32w0/scripts/demo_generated_factory_data/dac/dut1 +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data/dac/dut1` folder. The demo_factory_data_dut2.bin uses the DAC certificate and private key found in -examples/platform/nxp/k32w0/scripts/demo_generated_factory_data/dac/dut2 +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data/dac/dut2` folder. These two factory data binaries can be used for testing topologies with 2 DUTS. They contain the corresponding DACs/PAIs generated using -generate_nxp_chip_factory_bin.py script. The discriminator is 14014 and the +`generate_nxp_chip_factory_bin.py` script. The discriminator is 14014 and the passcode is 1000. These demo certificates are working with the CDs installed in CHIPProjectConfig.h. @@ -375,7 +375,7 @@ Regarding factory data provider, there are two options: - use the default factory data provider: `FactoryDataProviderImpl` by setting `chip_with_factory_data=1` in the gn build command. - use a custom factory data provider: please see - [Guide for implementing a custom factory data provider](../../../platform/nxp/k32w0/common/README.md). + [Guide for implementing a custom factory data provider](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/common/README.md). This can be enabled when `chip_with_factory_data=1` by setting `use_custom_factory_provider=1` in the gn build command. @@ -509,7 +509,7 @@ needed for parsing the hashed scripts. The python3 script detokenizer.py is a script that decodes the tokenized logs either from a file or from a serial port. It is located in the following path -`examples/platform/nxp/k32w0/scripts/detokenizer.py`. +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/detokenizer.py`. The script can be used in the following ways: @@ -544,7 +544,7 @@ by the script is loaded by the environment. An example of running the detokenizer script to see logs of a contact-sensor app: ``` -python3 ../../../../examples/platform/nxp/k32w0/scripts/detokenizer.py serial -i /dev/ttyACM0 -d out/debug/chip-k32w0x-contact-example-database.bin -o device.txt +python3 ../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/detokenizer.py serial -i /dev/ttyACM0 -d out/debug/chip-k32w0x-contact-example-database.bin -o device.txt ``` ### Known issues tokenizer @@ -616,7 +616,7 @@ is done. The OTA topology used for OTA testing is illustrated in the figure below. Topology is similar with the one used for Matter Test Events. -![OTA_TOPOLOGY](../../../platform/nxp/k32w0/doc/images/ota_topology.JPG) +![OTA_TOPOLOGY](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/ota_topology.JPG) The concept for OTA is the next one: @@ -795,13 +795,13 @@ Power Measurement Tool can be used inside MCUXpresso for checking the power consumption pattern: Window -> Show View -> Other -> Power Measurement Tool. The configuration for this tool is the next one: -![POWER_CONF](../../../platform/nxp/k32w0/doc/images/power_conf.JPG) +![POWER_CONF](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/power_conf.JPG) Also, please make sure that the J14 jumper is set to the _ENABLED_ position and no expansion board is attached to the DK6. A view from this tool is illustrated below: -![POWER_VIEW](../../../platform/nxp/k32w0/doc/images/power_view.JPG) +![POWER_VIEW](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/power_view.JPG) Please note that that the Power Measurement Tool is not very accurate and professional tools must be used if exact power consumption needs to be known. diff --git a/examples/lighting-app/nxp/k32w0/BUILD.gn b/examples/lighting-app/nxp/k32w0/BUILD.gn index 61ec7f0dea4a1c..3a760acf4bd69c 100644 --- a/examples/lighting-app/nxp/k32w0/BUILD.gn +++ b/examples/lighting-app/nxp/k32w0/BUILD.gn @@ -40,7 +40,7 @@ if (chip_pw_tokenizer_logging) { assert(current_os == "freertos") -k32w0_platform_dir = "${chip_root}/examples/platform/nxp/k32w0" +k32w0_platform_dir = "${nxp_sdk_matter_support_root}/examples/platform/k32w0" k32w0_sdk("sdk") { sources = [ diff --git a/examples/lighting-app/nxp/k32w0/README.md b/examples/lighting-app/nxp/k32w0/README.md index 6f162432811da2..12ce6799afb8d6 100644 --- a/examples/lighting-app/nxp/k32w0/README.md +++ b/examples/lighting-app/nxp/k32w0/README.md @@ -47,7 +47,7 @@ network. ## Introduction -![K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-dk6.jpg) +![K32W061 DK6](../../../../third_party/nxp/nxp_matter_support/examples/platform//k32w0/doc/images/k32w-dk6.jpg) The K32W061 lighting example application provides a working demonstration of a light bulb device, built using the Project CHIP codebase and the NXP K32W061 @@ -77,7 +77,7 @@ Deployment of this firmware configuration requires the K32W061 board setups using the K32W061 module board, SE051 Expansion board and Generic Expansion board as shown below: -![SE051H + K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-se.jpg) +![SE051H + K32W061 DK6](../../../../third_party/nxp/nxp_matter_support/examples/platform//k32w0/doc/images/k32w-se.jpg) The SE051H Secure Element extension may be used for best in class security and offloading some of the Project CHIP cryptographic operations. Depending on your @@ -332,17 +332,17 @@ See [Guide for writing manufacturing data on NXP devices](../../../../docs/guides/nxp/nxp_manufacturing_flow.md). There are factory data generated binaries available in -examples/platform/nxp/k32w0/scripts/demo_generated_factory_data folder. +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data` folder. These are based on the DAC, PAI and PAA certificates found in scripts/tools/nxp/demo_generated_certs folder. The demo_factory_data_dut1.bin uses the DAC certificate and private key found in -examples/platform/nxp/k32w0/scripts/demo_generated_factory_data/dac/dut1 +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data/dac/dut1` folder. The demo_factory_data_dut2.bin uses the DAC certificate and private key found in -examples/platform/nxp/k32w0/scripts/demo_generated_factory_data/dac/dut2 +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data/dac/dut2` folder. These two factory data binaries can be used for testing topologies with 2 DUTS. They contain the corresponding DACs/PAIs generated using -generate_nxp_chip_factory_bin.py script. The discriminator is 14014 and the +`generate_nxp_chip_factory_bin.py` script. The discriminator is 14014 and the passcode is 1000. These demo certificates are working with the CDs installed in CHIPProjectConfig.h. @@ -351,7 +351,7 @@ Regarding factory data provider, there are two options: - use the default factory data provider: `FactoryDataProviderImpl` by setting `chip_with_factory_data=1` in the gn build command. - use a custom factory data provider: please see - [Guide for implementing a custom factory data provider](../../../platform/nxp/k32w0/common/README.md). + [Guide for implementing a custom factory data provider](../../../../third_party/nxp/nxp_matter_support/examples/platform//k32w0/common/README.md). This can be enabled when `chip_with_factory_data=1` by setting `use_custom_factory_provider=1` in the gn build command. @@ -485,7 +485,7 @@ needed for parsing the hashed scripts. The python3 script detokenizer.py is a script that decodes the tokenized logs either from a file or from a serial port. It is located in the following path -`examples/platform/nxp/k32w0/scripts/detokenizer.py`. +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/detokenizer.py`. The script can be used in the following ways: @@ -520,7 +520,7 @@ by the script is loaded by the environment. An example of running the detokenizer script to see logs of a lighting app: ``` -python3 ../../../../examples/platform/nxp/k32w0/scripts/detokenizer.py serial -i /dev/ttyACM0 -d out/debug/chip-k32w0x-light-example-database.bin -o device.txt +python3 ../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/detokenizer.py serial -i /dev/ttyACM0 -d out/debug/chip-k32w0x-light-example-database.bin -o device.txt ``` ### Known issues tokenizer @@ -592,7 +592,7 @@ is done. The OTA topology used for OTA testing is illustrated in the figure below. Topology is similar with the one used for Matter Test Events. -![OTA_TOPOLOGY](../../../platform/nxp/k32w0/doc/images/ota_topology.JPG) +![OTA_TOPOLOGY](../../../../third_party/nxp/nxp_matter_support/examples/platform//k32w0/doc/images/ota_topology.JPG) The concept for OTA is the next one: diff --git a/src/platform/nxp/k32w0/args.gni b/src/platform/nxp/k32w0/args.gni index ca88077e4a59a1..9d91b0fe39a645 100644 --- a/src/platform/nxp/k32w0/args.gni +++ b/src/platform/nxp/k32w0/args.gni @@ -24,7 +24,7 @@ nxp_use_mbedtls_port = false if (getenv("NXP_K32W0_SDK_ROOT") == "") { k32w0_sdk_root = - "${nxp_sdk_matter_support_root}/github_sdk/k32w0_sdk/repo" + "${nxp_sdk_matter_support_root}/github_sdk/k32w0/repo" } else { k32w0_sdk_root = getenv("NXP_K32W0_SDK_ROOT") } @@ -72,6 +72,6 @@ openthread_external_mbedtls = mbedtls_target openthread_project_core_config_file = "OpenThreadConfig.h" openthread_core_config_platform_check_file = "openthread-core-k32w061-config-check.h" -openthread_core_config_deps = [ "${chip_root}/examples/platform/nxp/k32w0:openthread_core_config_k32w0_chip_examples" ] +openthread_core_config_deps = [ "${nxp_sdk_matter_support_root}/examples/platform/k32w0:openthread_core_config_k32w0_chip_examples" ] openthread_external_platform = "${chip_root}/third_party/openthread/platforms/nxp/k32w0:libopenthread-k32w0" diff --git a/third_party/openthread/platforms/nxp/k32w0/BUILD.gn b/third_party/openthread/platforms/nxp/k32w0/BUILD.gn index 0e73beae657f66..bf9ed42e894965 100644 --- a/third_party/openthread/platforms/nxp/k32w0/BUILD.gn +++ b/third_party/openthread/platforms/nxp/k32w0/BUILD.gn @@ -30,7 +30,7 @@ config("openthread_k32w0_config") { "${openthread_nxp_root}/src/k32w0/platform", "${openthread_nxp_root}/src/common", ] - include_dirs += [ "${chip_root}/examples/platform/nxp/k32w0" ] + include_dirs += [ "${nxp_sdk_matter_support_root}/examples/platform/k32w0" ] if (is_clang) { cflags = [ "-Wno-format-nonliteral" ] @@ -46,7 +46,7 @@ config("openthread_k32w0_config") { source_set("openthread_core_config_k32w0") { sources = [ - "${chip_root}/examples/platform/nxp/k32w0/app/project_include/OpenThreadConfig.h", + "${nxp_sdk_matter_support_root}/examples/platform/k32w0/app/project_include/OpenThreadConfig.h", "${openthread_nxp_root}/src/k32w0/k32w061/openthread-core-k32w061-config-check.h", ] From 00a83b677b83edf1ab05d64b094c6a1dafca182d Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Thu, 25 Jul 2024 12:12:09 +0300 Subject: [PATCH 07/11] [NXP][examples][k32w0] Enable chip_generate_link_map_file by default Signed-off-by: marius-alex-tache --- examples/contact-sensor-app/nxp/k32w0/args.gni | 1 + examples/lighting-app/nxp/k32w0/args.gni | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/contact-sensor-app/nxp/k32w0/args.gni b/examples/contact-sensor-app/nxp/k32w0/args.gni index e328a6ede9011f..1709f1da735d5d 100644 --- a/examples/contact-sensor-app/nxp/k32w0/args.gni +++ b/examples/contact-sensor-app/nxp/k32w0/args.gni @@ -24,6 +24,7 @@ chip_with_ota_key = "1234567890ABCDEFA1B2C3D4E5F6F1B4" chip_stack_lock_tracking = "fatal" chip_enable_ble = true +chip_generate_link_map_file = true chip_enable_icd_server = true chip_enable_icd_lit = false diff --git a/examples/lighting-app/nxp/k32w0/args.gni b/examples/lighting-app/nxp/k32w0/args.gni index f5fd7a83cd9005..7126cf38ad5f3b 100644 --- a/examples/lighting-app/nxp/k32w0/args.gni +++ b/examples/lighting-app/nxp/k32w0/args.gni @@ -20,6 +20,7 @@ k32w0_sdk_target = get_label_info(":sdk", "label_no_toolchain") chip_enable_ota_requestor = true chip_stack_lock_tracking = "fatal" chip_enable_ble = true +chip_generate_link_map_file = true is_debug = false From 5a16698b3303bee26719b78cbb8f2731b4558d26 Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Tue, 9 Jul 2024 15:24:35 +0300 Subject: [PATCH 08/11] [NXP][examples][k32w0] Add OTA encryption info in contact sensor README Signed-off-by: marius-alex-tache --- examples/contact-sensor-app/nxp/k32w0/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/contact-sensor-app/nxp/k32w0/README.md b/examples/contact-sensor-app/nxp/k32w0/README.md index c93dd8d67c1b22..2b0ed3519ded4a 100644 --- a/examples/contact-sensor-app/nxp/k32w0/README.md +++ b/examples/contact-sensor-app/nxp/k32w0/README.md @@ -611,6 +611,10 @@ Note that the application needs to be built using the `chip_enable_ota_requestor This is enabled in the configuration by default if no `chip_enable_ota_requestor` explicit setting is done. +Please also note that, by default, the device expects the OTA image +to be encrypted with the same key specified by `chip_with_ota_key`. +See `args.gni` for the default gn configuration. + ### OTA Testing The OTA topology used for OTA testing is illustrated in the figure below. @@ -701,10 +705,9 @@ Here is an example that generates an OTA image with application update TLV: ./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 2 -vs "2.0" -da sha256 --enc_enable --input_ota_key "1234567890ABCDEFA1B2C3D4E5F6F1B4" --app-input-file chip-k32w0x-contact-example.bin chip-k32w0x-contact-example.ota ``` -Please note the two options `--enc_enable` and `--input_ota_key`, which are -mandatory when `chip_with_ota_encryption=1`. The value of `--input_ota_key` must -match the value of `chip_with_ota_key`. See `args.gni` for the default gn -configuration. +Please note the two options `--enc_enable` and `--input_ota_key`, +which are mandatory when `chip_with_ota_encryption=1`. The value +of `--input_ota_key` must match the value of `chip_with_ota_key`. A note regarding OTA image header version (`-vn` option). An application binary has its own software version, given by From 40011c1c31356de049138c306710298b0121c561 Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Mon, 5 Aug 2024 11:09:55 +0300 Subject: [PATCH 09/11] [NXP][docs] Fix spellcheck errors Signed-off-by: marius-alex-tache --- docs/QUICK_START.md | 8 +- docs/guides/nxp/nxp_k32w0_ota_guide.md | 95 ++++---- .../nxp/nxp_k32w_android_commissioning.md | 34 +-- .../contact-sensor-app/nxp/k32w0/BUILD.gn | 4 +- .../contact-sensor-app/nxp/k32w0/README.md | 216 ++++++++++-------- examples/lighting-app/nxp/k32w0/BUILD.gn | 8 +- examples/lighting-app/nxp/k32w0/README.md | 174 +++++++------- src/platform/nxp/k32w0/args.gni | 3 +- 8 files changed, 290 insertions(+), 252 deletions(-) diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md index 7a000d94919f75..d5d80703c5e61f 100644 --- a/docs/QUICK_START.md +++ b/docs/QUICK_START.md @@ -17,10 +17,10 @@ and platforms. Use one of the controllers listed above and then a Border Router and Node combination listed below. -|

    |
    Node
    | Description | -| -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lighting-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nrfconnect/README.md)
  • [NXP K32W](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nxp/k32w0/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/silabs/README.md) | The Lighting example is supported by many of the available Thread platforms. See the chip-tool controller instructions for how to actuate the light on/off cluster. | -| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lock-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/nrfconnect/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/efr32/README.md)
  • [TI CC13x2x7](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/cc13x2x7_26x2x7/README.md) | The Lock example is supported by many of the available Thread and Wi-Fi platforms. | +|
    Border Router
    |
    Node
    | Description | +| -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lighting-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nrfconnect/README.md)
  • [NXP K32W](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nxp/k32w0/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/silabs/README.md) | The Lighting example is supported by many of the available Thread platforms. See the chip-tool controller instructions for how to actuate the light on/off cluster. | +| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lock-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/nrfconnect/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/efr32/README.md)
  • [TI CC13x2x7](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/cc13x2x7_26x2x7/README.md) | The Lock example is supported by many of the available Thread and Wi-Fi platforms. | ## Controllers diff --git a/docs/guides/nxp/nxp_k32w0_ota_guide.md b/docs/guides/nxp/nxp_k32w0_ota_guide.md index d5265d01e895d7..147bfdf2ee97e4 100644 --- a/docs/guides/nxp/nxp_k32w0_ota_guide.md +++ b/docs/guides/nxp/nxp_k32w0_ota_guide.md @@ -4,25 +4,25 @@ There are multiple SSBL binaries provided by the SDK: -| description | github SDK path | package SDK path | -| ------ | --------------- | ---------------- | -| Default SSBL | NA | `boards/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl.bin` | +| description | github SDK path | package SDK path | +| ------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| Default SSBL | NA | `boards/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl.bin` | | SSBL with PDM in external flash | `examples/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl_ext_flash_pdm_support.bin` | `boards/k32w061dk6/wireless_examples/framework/ssbl/binary/ssbl_ext_flash_pdm_support.bin` | -The SSBL is also built alongside the reference application and it can be configured -according to the following table: - -| gn arg | default | description | -| ------ | ------- | ----------- | -| `ssbl_pdm_external_flash` | true | Enable/disable PDM in external flash | -| `ssbl_multi_image_support` | true | Enable/disable multi-image OTA feature | -| `ssbl_ota_entry_storage` | "OTACustomStorage_ExtFlash" | Configure custom OTA entry storage type | -| `ssbl_simple_hash_verification` | false | Enable/disable simple hash verification alternative to secure boot | -| `ssbl_optimize_spifi_flash` | false | Optimize SPIFI flash driver size | -| `ssbl_spifi_dual_mode` | false | Enable/disable SPIFI dual mode support (e.g. used by K32W041AM variant) | -| `ssbl_version` | 0 | Set SSBL version | -| `ssbl_use_redlib` | false | Enable/disable usage of redlib NXP library. If false, the build will use newlib nano | -| `ssbl_ota_data_in_external_flash` | false | Enable/disable OTA support for application with sections stored in external flash | +The SSBL is also built alongside the reference application and it can be +configured according to the following table: + +| gn arg | default | description | +| --------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------- | +| `ssbl_pdm_external_flash` | true | Enable/disable PDM in external flash | +| `ssbl_multi_image_support` | true | Enable/disable multi-image OTA feature | +| `ssbl_ota_entry_storage` | "OTACustomStorage_ExtFlash" | Configure custom OTA entry storage type | +| `ssbl_simple_hash_verification` | false | Enable/disable simple hash verification alternative to secure boot | +| `ssbl_optimize_spifi_flash` | false | Optimize `SPIFI` flash driver size | +| `ssbl_spifi_dual_mode` | false | Enable/disable `SPIFI` dual mode support (e.g. used by K32W041AM variant) | +| `ssbl_version` | 0 | Set SSBL version | +| `ssbl_use_redlib` | false | Enable/disable usage of `redlib` NXP library. If false, the build will use `newlib` nano | +| `ssbl_ota_data_in_external_flash` | false | Enable/disable OTA support for application with sections stored in external flash | ## Simple hash verification @@ -35,11 +35,13 @@ image for integrity check. Applications should be built with Before writing the SSBL, it it recommanded to fully erase the internal flash. Using DK6Programmer utility from Windows: + ``` DK6Programmer.exe -V 5 -P 1000000 -s -e Flash ``` -Using dk6prog from SPSDK: +Using `dk6prog` from `SPSDK`: + ``` $ dk6prog listdev This is an experimental utility. Use with caution! @@ -56,11 +58,12 @@ Erasing memory [####################################] 100% `chip-k32w0x-ssbl.bin` must be written at address 0 in the internal flash: Using DK6Programmer utility from Windows: + ``` DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x00="chip-k32w0x-ssbl.bin" ``` -Using dk6prog from SPSDK: +Using `dk6prog` from `SPSDK`: ``` $ dk6prog -d DN038ZH3 write 0 ~/path/to/bin/chip-k32w0x-ssbl.bin @@ -68,7 +71,7 @@ $ dk6prog -d DN038ZH3 write 0 ~/path/to/bin/chip-k32w0x-ssbl.bin This is an experimental utility. Use with caution! Writing memory [####################################] 100% -Writen 7890 bytes to memory ID 0 at address 0x0 +Written 7890 bytes to memory ID 0 at address 0x0 ``` ### Writing the PSECT @@ -115,18 +118,20 @@ This is the list of all supported partitions: First, image directory 0 (SSBL partition) must be written: Using DK6Programmer utility from Windows: + ``` DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_0=0000000010000000 ``` -Using dk6prog from SPSDK: +Using `dk6prog` from `SPSDK`: + ``` $ dk6prog -d DN038ZH3 write 0x160 [[0000000010000000]] 8 PSECT This is an experimental utility. Use with caution! Writing memory [####################################] 100% -Writen 8 bytes to memory ID PSECT at address 0x160 +Written 8 bytes to memory ID PSECT at address 0x160 ``` Here is the interpretation of the fields: @@ -141,18 +146,20 @@ Here is the interpretation of the fields: Second, image directory 1 (application partition) must be written: Using DK6Programmer utility from Windows: + ``` DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_1=00400000C9040101 ``` -Using dk6prog from SPSDK: +Using `dk6prog` from `SPSDK`: + ``` $ dk6prog -d DN038ZH3 write 0x168 [[00400000C9040101]] 8 PSECT This is an experimental utility. Use with caution! Writing memory [####################################] 100% -Writen 8 bytes to memory ID PSECT at address 0x168 +Written 8 bytes to memory ID PSECT at address 0x168 ``` Here is the interpretation of the fields: @@ -164,34 +171,37 @@ C904 -> 0x4C9 pages of 512-bytes (= 612.5kB) 01 -> image type for the application ``` -Please note the user can write additional partitions by writing `image_dir_2/3/4` -with the wanted configuration. In case of using the SPSDK tool, the appropriate offset -must be calculated +Please note the user can write additional partitions by writing +`image_dir_2/3/4` with the wanted configuration. In case of using the `SPSDK` +tool, the appropriate offset must be calculated ## Removing SSBL Upgrade Region -The example also offers the possibility to remove SSBL upgrade region, for reserving more -space for application level. +The example also offers the possibility to remove SSBL upgrade region, for +reserving more space for application level. -A new flag `chip_reduce_ssbl_size` is introduced. In order to remove the SSBL upgrade region, -`chip_reduce_ssbl_size=true` must be provided to the build system +A new flag `chip_reduce_ssbl_size` is introduced. In order to remove the SSBL +upgrade region, `chip_reduce_ssbl_size=true` must be provided to the build +system The programming method will change: -* Writing image directory 1 should change to - Using DK6Programmer utility from Windows: +- Writing image directory 1 should change to Using DK6Programmer utility from + Windows: + ``` DK6Programmer.exe -V5 -s -P 1000000 -w image_dir_1=00200000D9040101 ``` - Using dk6prog from SPSDK: + Using `dk6prog` from `SPSDK`: + ``` $ dk6prog -d DN038ZH3 write 0x168 [[00200000D9040101]] 8 PSECT This is an experimental utility. Use with caution! Writing memory [####################################] 100% - Writen 8 bytes to memory ID PSECT at address 0x168 + Written 8 bytes to memory ID PSECT at address 0x168 ``` Here is the interpretation of the fields: @@ -202,18 +212,21 @@ The programming method will change: 01 -> bootable flag 01 -> image type for the application ``` -* Matter application offset address should change to - Using DK6Programmer utility from Windows: + +- Matter application offset address should change to Using DK6Programmer + utility from Windows: + ``` DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x2000="chip-k32w0x-contact-example.bin" ``` - Using dk6prog from SPSDK: - ``` + Using `dk6prog` from `SPSDK`: + + ``` $ dk6prog -d DN038ZH3 write 0x2000 ~/path/to/bin/chip-k32w0x-contact-example.bin This is an experimental utility. Use with caution! Writing memory [####################################] 100% - Writen 596450 bytes to memory ID 0 at address 0x2000 - ``` \ No newline at end of file + Written 596450 bytes to memory ID 0 at address 0x2000 + ``` diff --git a/docs/guides/nxp/nxp_k32w_android_commissioning.md b/docs/guides/nxp/nxp_k32w_android_commissioning.md index dd1d017b1490b2..a37d60f0f0ba4e 100644 --- a/docs/guides/nxp/nxp_k32w_android_commissioning.md +++ b/docs/guides/nxp/nxp_k32w_android_commissioning.md @@ -8,17 +8,17 @@ onto a CHIP-enabled Thread network.
    -- [Commissioning NXP K32W using Android CHIPTool](#commissioning-nxp-k32w-using-android-chiptool) - - [Overview](#overview) - - [Requirements](#requirements) - - [Building and programming OpenThread RCP firmware](#building-and-programming-openthread-rcp-firmware) - - [Configuring PC as a Thread Border Router](#configuring-pc-as-a-thread-border-router) - - [Building and programming NXP K32W Light Example Application](#building-and-programming-nxp-k32w-light-example-application) - - [Building and installing Android CHIPTool](#building-and-installing-android-chiptool) - - [Forming a Thread network on the Border Router](#forming-a-thread-network-on-the-border-router) - - [Preparing accessory device](#preparing-accessory-device) - - [Commissioning accessory device](#commissioning-accessory-device) - - [Sending CHIP commands](#sending-chip-commands) +- [Commissioning NXP K32W using Android CHIPTool](#commissioning-nxp-k32w-using-android-chiptool) + - [Overview](#overview) + - [Requirements](#requirements) + - [Building and programming OpenThread RCP firmware](#building-and-programming-openthread-rcp-firmware) + - [Configuring PC as a Thread Border Router](#configuring-pc-as-a-thread-border-router) + - [Building and programming NXP K32W Light Example Application](#building-and-programming-nxp-k32w-light-example-application) + - [Building and installing Android CHIPTool](#building-and-installing-android-chiptool) + - [Forming a Thread network on the Border Router](#forming-a-thread-network-on-the-border-router) + - [Preparing accessory device](#preparing-accessory-device) + - [Commissioning accessory device](#commissioning-accessory-device) + - [Sending CHIP commands](#sending-chip-commands)
    @@ -48,7 +48,7 @@ The following diagram shows the connectivity between network components required to allow communication between devices running the CHIPTool and Light applications: -![nxp_hw_connectivity](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/nxp_hw_connectivity.JPG) +![nxp_hw_connectivity](../../../examples/platform/nxp/k32w0/doc/images/nxp_hw_connectivity.JPG)
    @@ -397,7 +397,7 @@ CHIPTool is now ready to be used for commissioning. 3. Navigate to the _Form_ tab then push the _Form_ button using the default parameters: - ![nxp_form_nwk](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/form_web.JPG) + ![nxp_form_nwk](../../../examples/platform/nxp/k32w0/doc/images/form_web.JPG) 4. The message _Form operation is successful_ should be display after a few seconds. @@ -431,7 +431,7 @@ To prepare the accessory device for commissioning, complete the following steps: 1. Make sure that JP4 and JP7 jumpers are in leftmost position and a mini-USB cable is connected between the LPC connector and PC - ![nxp_connectors](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/k32w-dk6-connectors.jpg) + ![nxp_connectors](../../../examples/platform/nxp/k32w0/doc/images/k32w-dk6-connectors.jpg) 2. Use a terminal emulator (e.g.: Putty) to connect to the UART console of the accessory device. Use a baudrate of 115200. @@ -467,14 +467,14 @@ section, complete the following steps: progress with scanning, connection, and pairing. At the end of this process, the Thread network settings screen appears. - ![chiptool_main_screen](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/chiptool_main_screen.png) + ![chiptool_main_screen](../../../examples/platform/nxp/k32w0/doc/images/chiptool_main_screen.png) 6. In the Thread network settings screen, use the default settings and tap the _SAVE NETWORK_ button to send a Thread provisioning message to the accessory device. You will see the "Network provisioning completed" message when the accessory device successfully joins the Thread network. - ![chiptool_credentials](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/thread_credentials.png) + ![chiptool_credentials](../../../examples/platform/nxp/k32w0/doc/images/thread_credentials.png)
    @@ -484,7 +484,7 @@ section, complete the following steps: the provisioning is completed successfully and you are connected to the device. - ![on_off_cluster.png](../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/on_off_cluster.png) + ![on_off_cluster.png](../../../examples/platform/nxp/k32w0/doc/images/on_off_cluster.png) 2. Verify that the text box on the screen is not empty and contains the IPv6 address of the accessory device. diff --git a/examples/contact-sensor-app/nxp/k32w0/BUILD.gn b/examples/contact-sensor-app/nxp/k32w0/BUILD.gn index 6d95d90a167653..c51b8787ea2922 100644 --- a/examples/contact-sensor-app/nxp/k32w0/BUILD.gn +++ b/examples/contact-sensor-app/nxp/k32w0/BUILD.gn @@ -120,9 +120,7 @@ k32w0_executable("contact_sensor_app") { "${k32w0_platform_dir}/app/support:freertos_mbedtls_utils", ] - defines += [ - "CONNECTIVITY_MANAGER_THREAD_DEVICE_TYPE=ConnectivityManager::kThreadDeviceType_SleepyEndDevice" - ] + defines += [ "CONNECTIVITY_MANAGER_THREAD_DEVICE_TYPE=ConnectivityManager::kThreadDeviceType_SleepyEndDevice" ] deps += [ "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd", diff --git a/examples/contact-sensor-app/nxp/k32w0/README.md b/examples/contact-sensor-app/nxp/k32w0/README.md index 2b0ed3519ded4a..77e88a01ab46ab 100644 --- a/examples/contact-sensor-app/nxp/k32w0/README.md +++ b/examples/contact-sensor-app/nxp/k32w0/README.md @@ -14,42 +14,41 @@ network.
    -- [CHIP K32W061 Contact Sensor Example Application](#chip-k32w061-contact-sensor-example-application) - - [Introduction](#introduction) - - [SE051H Secure Element](#se051h-secure-element) - - [Bluetooth LE Advertising](#bluetooth-le-advertising) - - [LIT ICD Active Mode](#lit-icd-active-mode) - - [Bluetooth LE Rendezvous](#bluetooth-le-rendezvous) - - [Thread Provisioning](#thread-provisioning) - - [Device UI](#device-ui) - - [No expansion board](#no-expansion-board) - - [Building](#building) - - [Long Idle Time ICD Support](#long-idle-time-icd-support) - - [Overwrite board config files](#overwrite-board-config-files) - - [Known issues building](#known-issues-building) - - [Rotating device id](#rotating-device-id) - - [Manufacturing data](#manufacturing-data) - - [Flashing and debugging](#flashing-and-debugging) - - [Using DK6programmer](#using-dk6programmer) - - [Using MCUXpresso](#using-mcuxpresso) - - [Pigweed tokenizer](#pigweed-tokenizer) - - [Detokenizer script](#detokenizer-script) - - [Notes](#notes) - - [Known issues tokenizer](#known-issues-tokenizer) - - [NXP Ultrafast P256 ECC Library](#nxp-ultrafast-p256-ecc-library) - - [Building steps](#building-steps) - - [Tinycrypt ECC library](#tinycrypt-ecc-library) - - [Building steps](#building-steps-1) - - [OTA](#ota) - - [OTA Testing](#ota-testing) - - [Known issues OTA](#known-issues-ota) - - [Low power](#low-power) - - [Known issues low power](#known-issues-low-power) - +- [CHIP K32W061 Contact Sensor Example Application](#chip-k32w061-contact-sensor-example-application) + - [Introduction](#introduction) + - [SE051H Secure Element](#se051h-secure-element) + - [Bluetooth LE Advertising](#bluetooth-le-advertising) + - [LIT ICD Active Mode](#lit-icd-active-mode) + - [Bluetooth LE Rendezvous](#bluetooth-le-rendezvous) + - [Thread Provisioning](#thread-provisioning) + - [Device UI](#device-ui) + - [No expansion board](#no-expansion-board) + - [Building](#building) + - [Long Idle Time ICD Support](#long-idle-time-icd-support) + - [Overwrite board config files](#overwrite-board-config-files) + - [Known issues building](#known-issues-building) + - [Rotating device id](#rotating-device-id) + - [Manufacturing data](#manufacturing-data) + - [Flashing and debugging](#flashing-and-debugging) + - [Using DK6programmer](#using-dk6programmer) + - [Using MCUXpresso](#using-mcuxpresso) + - [Pigweed tokenizer](#pigweed-tokenizer) + - [Detokenizer script](#detokenizer-script) + - [Notes](#notes) + - [Known issues tokenizer](#known-issues-tokenizer) + - [NXP Ultrafast P256 ECC Library](#nxp-ultrafast-p256-ecc-library) + - [Building steps](#building-steps) + - [Tinycrypt ECC library](#tinycrypt-ecc-library) + - [Building steps](#building-steps-1) + - [OTA](#ota) + - [OTA Testing](#ota-testing) + - [Known issues OTA](#known-issues-ota) + - [Low power](#low-power) + - [Known issues low power](#known-issues-low-power) ## Introduction -![K32W061 DK6](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/k32w-dk6.jpg) +![K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-dk6.jpg) The K32W061 contact sensor example application provides a working demonstration of a connected contact sensor device, built using the Project CHIP codebase and @@ -79,7 +78,7 @@ Deployment of this firmware configuration requires the K32W061 board setups using the K32W061 module board, SE051 Expansion board and Generic Expansion board as shown below: -![SE051H + K32W061 DK6](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/k32w-se.jpg) +![SE051H + K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-se.jpg) The SE051H Secure Element extension may be used for best in class security and offloading some of the Project CHIP cryptographic operations. Depending on your @@ -218,8 +217,8 @@ user@ubuntu:~/Desktop/git/connectedhomeip$ source scripts/bootstrap.sh user@ubuntu:~/Desktop/git/connectedhomeip$ third_party/nxp/nxp_matter_support/scripts/update_nxp_sdk.py --platform k32w0 ``` -Note: By default update_nxp_sdk.py will try to initialize all NXP -SDKs. Arg "-- help" could be used to view all available options. +Note: By default update_nxp_sdk.py will try to initialize all NXP SDKs. Arg "-- +help" could be used to view all available options. - Start building the application: @@ -356,8 +355,8 @@ See [Guide for writing manufacturing data on NXP devices](../../../../docs/guides/nxp/nxp_manufacturing_flow.md). There are factory data generated binaries available in -`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data` folder. -These are based on the DAC, PAI and PAA certificates found in +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data` +folder. These are based on the DAC, PAI and PAA certificates found in scripts/tools/nxp/demo_generated_certs folder. The demo_factory_data_dut1.bin uses the DAC certificate and private key found in `third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data/dac/dut1` @@ -375,7 +374,7 @@ Regarding factory data provider, there are two options: - use the default factory data provider: `FactoryDataProviderImpl` by setting `chip_with_factory_data=1` in the gn build command. - use a custom factory data provider: please see - [Guide for implementing a custom factory data provider](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/common/README.md). + [Guide for implementing a custom factory data provider](../../../platform/nxp/k32w0/doc/CustomFactoryDataProvider.md). This can be enabled when `chip_with_factory_data=1` by setting `use_custom_factory_provider=1` in the gn build command. @@ -388,13 +387,14 @@ Instructions to program the firmware can be found also at The application binary's path is _out/debug/chip-k32w0x-contact-example.bin_. -DK6Programmer can be used for flashing the application. There are two available versions of the -DK6Programmer tool. +DK6Programmer can be used for flashing the application. There are two available +versions of the DK6Programmer tool. The legacy version consists of a Windows executable found inside the -[SDK](https://mcuxpresso.nxp.com/en/welcome) at path `tools/JN-SW-4407-DK6-Flash-Programmer`. This is a -Windows application that can be installed using the .exe file. Once the -application is installed, the COM port for K32W061 must be identified: +[SDK](https://mcuxpresso.nxp.com/en/welcome) at path +`tools/JN-SW-4407-DK6-Flash-Programmer`. This is a Windows application that can +be installed using the .exe file. Once the application is installed, the COM +port for K32W061 must be identified: ``` C:\nxp\DK6ProductionFlashProgrammer>DK6Programmer.exe --list @@ -408,16 +408,20 @@ Once the COM port is identified, the required binary can be flashed: DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x4000="chip-k32w0x-contact-example.bin" ``` -> **_Note:_** The above example takes into account that the binary uses the `chip_enable_ota_requestor=true` option. -The address offset corresponds to the space left for the SSBL binary and the OTA space for the SSBL. If -`chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be replaced with `0x0`. +> **_Note:_** The above example takes into account that the binary uses the +> `chip_enable_ota_requestor=true` option. The address offset corresponds to the +> space left for the SSBL binary and the OTA space for the SSBL. If +> `chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be +> replaced with `0x0`. DK6 Flash Programmer tool has also been integrated part of -[NXP Secure Provisioning SDK (SPSDK)](https://github.com/nxp-mcuxpresso/spsdk). This tool is supported by -environments like Windows, Linux or Mac. +[NXP Secure Provisioning SDK (SPSDK)](https://github.com/nxp-mcuxpresso/spsdk). +This tool is supported by environments like Windows, Linux or Mac. -SPSDK can be installed and run from a Python environment using [these instructions](https://spsdk.readthedocs.io/en/latest/usage/installation.html). -This enables the user to have transparent access to the dk6 programming tool through SPSDK. +`SPSDK` can be installed and run from a Python environment using +[these instructions](https://spsdk.readthedocs.io/en/latest/usage/installation.html). +This enables the user to have transparent access to the dk6 programming tool +through `SPSDK`. ``` # after specific environment installation steps @@ -433,14 +437,17 @@ $ spsdk --help ... ``` -Dependencies for the dk6prog module can be installed using the following command, more details [here](https://spsdk.readthedocs.io/en/latest/usage/installation.html#dk6-tools): +Dependencies for the `dk6prog` module can be installed using the following +command, more details +[here](https://spsdk.readthedocs.io/en/latest/usage/installation.html#dk6-tools): ``` $ pip install spsdk[dk6] ``` -The SPSDK installation adds dk6prog as executable to system path, so user can use directly `dk6prog` from terminal. -The following commands are to be used to write the chip-k32w0x-contact-example binary to the board. +The `SPSDK` installation adds `dk6prog` as executable to system path, so user +can use directly `dk6prog` from terminal. The following commands are to be used +to write the chip-k32w0x-contact-example binary to the board. ``` $ dk6prog listdev @@ -453,10 +460,11 @@ $ dk6prog -d DN038ZH3 write 0x4000 ~/path/to/bin/chip-k32w0x-contact-example.bin This is an experimental utility. Use with caution! Writing memory [####################################] 100% -Writen 596450 bytes to memory ID 0 at address 0x4000 +Written 596450 bytes to memory ID 0 at address 0x4000 ``` -> **_Note:_** Running `dk6prog` from Windows OS command line requires an integer value for DEVICE ID. +> **_Note:_** Running `dk6prog` from Windows OS command line requires an integer +> value for DEVICE ID. ``` C:\nxp\spsdk>dk6prog listdev @@ -488,15 +496,18 @@ Detected DEVICE: UNKNOWN RAM1 7 0x4020000 0x10000 0x1 RAM Write Enabled ``` -> **_Note:_** The above example takes into account that the binary uses the `chip_enable_ota_requestor=true` option. -The address offset corresponds to the space left for the SSBL binary and the OTA space for the SSBL. If -`chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be replaced with `0x0`. +> **_Note:_** The above example takes into account that the binary uses the +> `chip_enable_ota_requestor=true` option. The address offset corresponds to the +> space left for the SSBL binary and the OTA space for the SSBL. If +> `chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be +> replaced with `0x0`. ### Using MCUXpresso If flashing and debugging is required, MCUXpresso can be used as instructed in [MCUXpresso flashing and debugging instructions](https://github.com/openthread/ot-nxp/tree/main/src/k32w0/k32w061#using-mcuxpresso-ide). -The file needed to be used in MCUXpresso is _out/debug/chip-k32w0x-contact-example_. +The file needed to be used in MCUXpresso is +_out/debug/chip-k32w0x-contact-example_. ## Pigweed tokenizer @@ -589,45 +600,49 @@ In order to use the Tinycrypt ECC library, use the following build arguments: ## OTA -Over the air updates (OTA) require several software components running on the K32W0x1. -Firstly, a Secondary Stage Bootloader (SSBL) is required written in the first part of the -internal flash memory, usually starting at address 0x0. This enables the board to boot and -check if a new OTA binary has been received. If this is true, the bootloader writes the OTA -binary to the appropriate storage, internal and/or external flash, after which it reboots -the board. If no new OTA binaries have been found, then the bootloader gives execution control -to the application. - -The internal flash needs to be prepared for the OTA process. First 16K of the internal flash -needs to be populated with SSBL related data while the last 8.5K of flash space is holding flash -configuration related data. The space between these two zones will be filled by the application. -More details regarding the internal flash space can be found in the -[linker file](../../../platform/nxp/k32w0/app/ldscripts/chip-k32w0x-linker.ld). - -The steps for building the SSBL binary with appropriate configuration and writing to the board -the binary and other OTA related configurations are descrived in the +Over the air updates (OTA) require several software components running on the +K32W0x1. Firstly, a Secondary Stage Bootloader (SSBL) is required written in the +first part of the internal flash memory, usually starting at address 0x0. This +enables the board to boot and check if a new OTA binary has been received. If +this is true, the bootloader writes the OTA binary to the appropriate storage, +internal and/or external flash, after which it reboots the board. If no new OTA +binaries have been found, then the bootloader gives execution control to the +application. + +The internal flash needs to be prepared for the OTA process. First 16K of the +internal flash needs to be populated with SSBL related data while the last 8.5K +of flash space is holding flash configuration related data. The space between +these two zones will be filled by the application. More details regarding the +internal flash space can be found in the +[linker file](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/app/ldscripts/chip-k32w0x-linker.ld). + +The steps for building the SSBL binary with appropriate configuration and +writing to the board the binary and other OTA related configurations are +described in the [K32W0x1 OTA guide](../../../../docs/guides/nxp/nxp_k32w0_ota_guide.md). -Note that the application needs to be built using the `chip_enable_ota_requestor=true` option. -This is enabled in the configuration by default if no `chip_enable_ota_requestor` explicit setting -is done. +Note that the application needs to be built using the +`chip_enable_ota_requestor=true` option. This is enabled in the configuration by +default if no `chip_enable_ota_requestor` explicit setting is done. -Please also note that, by default, the device expects the OTA image -to be encrypted with the same key specified by `chip_with_ota_key`. -See `args.gni` for the default gn configuration. +Please also note that, by default, the device expects the OTA image to be +encrypted with the same key specified by `chip_with_ota_key`. See `args.gni` for +the default gn configuration. ### OTA Testing The OTA topology used for OTA testing is illustrated in the figure below. Topology is similar with the one used for Matter Test Events. -![OTA_TOPOLOGY](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/ota_topology.JPG) +![OTA_TOPOLOGY](../../../platform/nxp/k32w0/doc/images/ota_topology.JPG) The concept for OTA is the next one: - there is an OTA Provider Application that holds the OTA image. In our case, this is a Linux application running on an Ubuntu based-system; - the OTA Requestor functionality is embedded inside the Contact Sensor - Application. It will be used for requesting OTA blocks from the OTA Provider; + Application. It will be used for requesting OTA blocks from the OTA + Provider; - the controller (a linux application called chip-tool) will be used for commissioning both the device and the OTA Provider App. The device will be commissioned using the standard Matter flow (BLE + IEEE 802.15.4) while the @@ -636,18 +651,18 @@ The concept for OTA is the next one: - during commissioning, each device is assigned a node id by the chip-tool (can be specified manually by the user). Using the node id of the device and of the contact sensor application, chip-tool triggers the OTA transfer by - invoking the _announce-ota-provider_ command - basically, the OTA Requestor is - informed of the node id of the OTA Provider Application. + invoking the _announce-ota-provider_ command - basically, the OTA Requestor + is informed of the node id of the OTA Provider Application. _Computer #1_ can be any system running an Ubuntu distribution. We recommand using CSA official instructions from [here](https://groups.csa-iot.org/wg/matter-csg/document/28566), where RPi 4 are -proposed. Also, CSA official instructions document point to the OS/Docker images that -should be used on the RPis. For compatibility reasons, we recommand compiling -chip-tool and OTA Provider applications with the same commit id that was used -for compiling the Contact Sensor Application. Also, please note that there is a single -controller (chip-tool) running on Computer #1 which is used for commissioning -both the device and the OTA Provider Application. If needed, +proposed. Also, CSA official instructions document point to the OS/Docker images +that should be used on the RPis. For compatibility reasons, we recommand +compiling chip-tool and OTA Provider applications with the same commit id that +was used for compiling the Contact Sensor Application. Also, please note that +there is a single controller (chip-tool) running on Computer #1 which is used +for commissioning both the device and the OTA Provider Application. If needed, [these instructions](https://itsfoss.com/connect-wifi-terminal-ubuntu/) could be used for connecting the RPis to WiFi. @@ -705,17 +720,18 @@ Here is an example that generates an OTA image with application update TLV: ./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 2 -vs "2.0" -da sha256 --enc_enable --input_ota_key "1234567890ABCDEFA1B2C3D4E5F6F1B4" --app-input-file chip-k32w0x-contact-example.bin chip-k32w0x-contact-example.ota ``` -Please note the two options `--enc_enable` and `--input_ota_key`, -which are mandatory when `chip_with_ota_encryption=1`. The value -of `--input_ota_key` must match the value of `chip_with_ota_key`. +Please note the two options `--enc_enable` and `--input_ota_key`, which are +mandatory when `chip_with_ota_encryption=1`. The value of `--input_ota_key` must +match the value of `chip_with_ota_key`. A note regarding OTA image header version (`-vn` option). An application binary has its own software version, given by `CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION` (`1` by default), which can be overwritten. For having a correct OTA process, the OTA header version should be -the same as the binary embedded software version. When building the update image, -the build arguments `nxp_software_version=2` and `nxp_sofware_version_string=\"2.0\"` -can be added to the gn gen command in order to specify the upgraded version. +the same as the binary embedded software version. When building the update +image, the build arguments `nxp_software_version=2` and +`nxp_sofware_version_string=\"2.0\"` can be added to the gn gen command in order +to specify the upgraded version. Start the OTA Provider Application: @@ -798,13 +814,13 @@ Power Measurement Tool can be used inside MCUXpresso for checking the power consumption pattern: Window -> Show View -> Other -> Power Measurement Tool. The configuration for this tool is the next one: -![POWER_CONF](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/power_conf.JPG) +![POWER_CONF](../../../platform/nxp/k32w0/doc/images/power_conf.JPG) Also, please make sure that the J14 jumper is set to the _ENABLED_ position and no expansion board is attached to the DK6. A view from this tool is illustrated below: -![POWER_VIEW](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/doc/images/power_view.JPG) +![POWER_VIEW](../../../platform/nxp/k32w0/doc/images/power_view.JPG) Please note that that the Power Measurement Tool is not very accurate and professional tools must be used if exact power consumption needs to be known. diff --git a/examples/lighting-app/nxp/k32w0/BUILD.gn b/examples/lighting-app/nxp/k32w0/BUILD.gn index 3a760acf4bd69c..a63a5967c3948b 100644 --- a/examples/lighting-app/nxp/k32w0/BUILD.gn +++ b/examples/lighting-app/nxp/k32w0/BUILD.gn @@ -105,9 +105,7 @@ k32w0_executable("light_app") { "${k32w0_platform_dir}/common/CustomFactoryDataProvider.h", ] - defines += [ - "CHIP_DEVICE_CONFIG_USE_CUSTOM_PROVIDER=1", - ] + defines += [ "CHIP_DEVICE_CONFIG_USE_CUSTOM_PROVIDER=1" ] } deps = [ @@ -122,9 +120,7 @@ k32w0_executable("light_app") { "${k32w0_platform_dir}/app/support:freertos_mbedtls_utils", ] - defines += [ - "CONNECTIVITY_MANAGER_THREAD_DEVICE_TYPE=ConnectivityManager::kThreadDeviceType_MinimalEndDevice" - ] + defines += [ "CONNECTIVITY_MANAGER_THREAD_DEVICE_TYPE=ConnectivityManager::kThreadDeviceType_MinimalEndDevice" ] deps += [ "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd", diff --git a/examples/lighting-app/nxp/k32w0/README.md b/examples/lighting-app/nxp/k32w0/README.md index 12ce6799afb8d6..810012f2507916 100644 --- a/examples/lighting-app/nxp/k32w0/README.md +++ b/examples/lighting-app/nxp/k32w0/README.md @@ -16,38 +16,38 @@ network.
    -- [CHIP K32W061 Lighting Example Application](#chip-k32w061-lighting-example-application) - - [Introduction](#introduction) - - [SE051H Secure Element](#se051h-secure-element) - - [Bluetooth LE Advertising](#bluetooth-le-advertising) - - [Bluetooth LE Rendezvous](#bluetooth-le-rendezvous) - - [Thread Provisioning](#thread-provisioning) - - [Device UI](#device-ui) - - [No expansion board](#no-expansion-board) - - [Identify cluster LED state](#identify-cluster-led-state) - - [Building](#building) - - [Overwrite board config files](#overwrite-board-config-files) - - [Known issues building](#known-issues-building) - - [Rotating device id](#rotating-device-id) - - [Manufacturing data](#manufacturing-data) - - [Flashing and debugging](#flashing-and-debugging) - - [Using DK6programmer](#using-dk6programmer) - - [Using MCUXpresso](#using-mcuxpresso) - - [Pigweed tokenizer](#pigweed-tokenizer) - - [Detokenizer script](#detokenizer-script) - - [Notes](#notes) - - [Known issues tokenizer](#known-issues-tokenizer) - - [NXP Ultrafast P256 ECC Library](#nxp-ultrafast-p256-ecc-library) - - [Building steps](#building-steps) - - [Tinycrypt ECC library](#tinycrypt-ecc-library) - - [Building steps](#building-steps-1) - - [OTA](#ota) - - [OTA Testing](#ota-testing) - - [Known issues OTA](#known-issues-ota) +- [CHIP K32W061 Lighting Example Application](#chip-k32w061-lighting-example-application) + - [Introduction](#introduction) + - [SE051H Secure Element](#se051h-secure-element) + - [Bluetooth LE Advertising](#bluetooth-le-advertising) + - [Bluetooth LE Rendezvous](#bluetooth-le-rendezvous) + - [Thread Provisioning](#thread-provisioning) + - [Device UI](#device-ui) + - [No expansion board](#no-expansion-board) + - [Identify cluster LED state](#identify-cluster-led-state) + - [Building](#building) + - [Overwrite board config files](#overwrite-board-config-files) + - [Known issues building](#known-issues-building) + - [Rotating device id](#rotating-device-id) + - [Manufacturing data](#manufacturing-data) + - [Flashing and debugging](#flashing-and-debugging) + - [Using DK6programmer](#using-dk6programmer) + - [Using MCUXpresso](#using-mcuxpresso) + - [Pigweed tokenizer](#pigweed-tokenizer) + - [Detokenizer script](#detokenizer-script) + - [Notes](#notes) + - [Known issues tokenizer](#known-issues-tokenizer) + - [NXP Ultrafast P256 ECC Library](#nxp-ultrafast-p256-ecc-library) + - [Building steps](#building-steps) + - [Tinycrypt ECC library](#tinycrypt-ecc-library) + - [Building steps](#building-steps-1) + - [OTA](#ota) + - [OTA Testing](#ota-testing) + - [Known issues OTA](#known-issues-ota) ## Introduction -![K32W061 DK6](../../../../third_party/nxp/nxp_matter_support/examples/platform//k32w0/doc/images/k32w-dk6.jpg) +![K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-dk6.jpg) The K32W061 lighting example application provides a working demonstration of a light bulb device, built using the Project CHIP codebase and the NXP K32W061 @@ -77,7 +77,7 @@ Deployment of this firmware configuration requires the K32W061 board setups using the K32W061 module board, SE051 Expansion board and Generic Expansion board as shown below: -![SE051H + K32W061 DK6](../../../../third_party/nxp/nxp_matter_support/examples/platform//k32w0/doc/images/k32w-se.jpg) +![SE051H + K32W061 DK6](../../../platform/nxp/k32w0/doc/images/k32w-se.jpg) The SE051H Secure Element extension may be used for best in class security and offloading some of the Project CHIP cryptographic operations. Depending on your @@ -230,8 +230,8 @@ user@ubuntu:~/Desktop/git/connectedhomeip$ source scripts/bootstrap.sh user@ubuntu:~/Desktop/git/connectedhomeip$ third_party/nxp/nxp_matter_support/scripts/update_nxp_sdk.py --platform k32w0 ``` -Note: By default update_nxp_sdk.py will try to initialize all NXP -SDKs. Arg "-- help" could be used to view all available options. +Note: By default update_nxp_sdk.py will try to initialize all NXP SDKs. Arg "-- +help" could be used to view all available options. - Start building the application: @@ -332,8 +332,8 @@ See [Guide for writing manufacturing data on NXP devices](../../../../docs/guides/nxp/nxp_manufacturing_flow.md). There are factory data generated binaries available in -`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data` folder. -These are based on the DAC, PAI and PAA certificates found in +`third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data` +folder. These are based on the DAC, PAI and PAA certificates found in scripts/tools/nxp/demo_generated_certs folder. The demo_factory_data_dut1.bin uses the DAC certificate and private key found in `third_party/nxp/nxp_matter_support/examples/platform/k32w0/scripts/demo_generated_factory_data/dac/dut1` @@ -351,7 +351,7 @@ Regarding factory data provider, there are two options: - use the default factory data provider: `FactoryDataProviderImpl` by setting `chip_with_factory_data=1` in the gn build command. - use a custom factory data provider: please see - [Guide for implementing a custom factory data provider](../../../../third_party/nxp/nxp_matter_support/examples/platform//k32w0/common/README.md). + [Guide for implementing a custom factory data provider](../../../platform/nxp/k32w0/doc/CustomFactoryDataProvider.md). This can be enabled when `chip_with_factory_data=1` by setting `use_custom_factory_provider=1` in the gn build command. @@ -364,13 +364,14 @@ Instructions to program the firmware can be found also at The application binary's path is _out/debug/chip-k32w0x-light-example.bin_. -DK6Programmer can be used for flashing the application. There are two available versions of the -DK6Programmer tool. +DK6Programmer can be used for flashing the application. There are two available +versions of the DK6Programmer tool. The legacy version consists of a Windows executable found inside the -[SDK](https://mcuxpresso.nxp.com/en/welcome) at path `tools/JN-SW-4407-DK6-Flash-Programmer`. This is a -Windows application that can be installed using the .exe file. Once the -application is installed, the COM port for K32W061 must be identified: +[SDK](https://mcuxpresso.nxp.com/en/welcome) at path +`tools/JN-SW-4407-DK6-Flash-Programmer`. This is a Windows application that can +be installed using the .exe file. Once the application is installed, the COM +port for K32W061 must be identified: ``` C:\nxp\DK6ProductionFlashProgrammer>DK6Programmer.exe --list @@ -384,16 +385,20 @@ Once the COM port is identified, the required binary can be flashed: DK6Programmer.exe -V2 -s -P 1000000 -Y -p FLASH@0x4000="chip-k32w0x-light-example.bin" ``` -> **_Note:_** The above example takes into account that the binary uses the `chip_enable_ota_requestor=true` option. -The address offset corresponds to the space left for the SSBL binary and the OTA space for the SSBL. If -`chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be replaced with `0x0`. +> **_Note:_** The above example takes into account that the binary uses the +> `chip_enable_ota_requestor=true` option. The address offset corresponds to the +> space left for the SSBL binary and the OTA space for the SSBL. If +> `chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be +> replaced with `0x0`. DK6 Flash Programmer tool has also been integrated part of -[NXP Secure Provisioning SDK (SPSDK)](https://github.com/nxp-mcuxpresso/spsdk). This tool is supported by -environments like Windows, Linux or Mac. +[NXP Secure Provisioning SDK (SPSDK)](https://github.com/nxp-mcuxpresso/spsdk). +This tool is supported by environments like Windows, Linux or Mac. -SPSDK can be installed and run from a Python environment using [these instructions](https://spsdk.readthedocs.io/en/latest/usage/installation.html). -This enables the user to have transparent access to the dk6 programming tool through SPSDK. +`SPSDK` can be installed and run from a Python environment using +[these instructions](https://spsdk.readthedocs.io/en/latest/usage/installation.html). +This enables the user to have transparent access to the dk6 programming tool +through `SPSDK`. ``` # after specific environment installation steps @@ -409,14 +414,17 @@ $ spsdk --help ... ``` -Dependencies for the dk6prog module can be installed using the following command, more details [here](https://spsdk.readthedocs.io/en/latest/usage/installation.html#dk6-tools): +Dependencies for the `dk6prog` module can be installed using the following +command, more details +[here](https://spsdk.readthedocs.io/en/latest/usage/installation.html#dk6-tools): ``` $ pip install spsdk[dk6] ``` -The SPSDK installation adds dk6prog as executable to system path, so user can use directly `dk6prog` from terminal. -The following commands are to be used to write the chip-k32w0x-light-example binary to the board. +The `SPSDK` installation adds `dk6prog` as executable to system path, so user +can use directly `dk6prog` from terminal. The following commands are to be used +to write the chip-k32w0x-light-example binary to the board. ``` $ dk6prog listdev @@ -429,10 +437,11 @@ $ dk6prog -d DN038ZH3 write 0x4000 ~/path/to/bin/chip-k32w0x-light-example.bin This is an experimental utility. Use with caution! Writing memory [####################################] 100% -Writen 596450 bytes to memory ID 0 at address 0x4000 +Written 596450 bytes to memory ID 0 at address 0x4000 ``` -> **_Note:_** Running `dk6prog` from Windows OS command line requires an integer value for DEVICE ID. +> **_Note:_** Running `dk6prog` from Windows OS command line requires an integer +> value for DEVICE ID. ``` C:\nxp\spsdk>dk6prog listdev @@ -464,15 +473,18 @@ Detected DEVICE: UNKNOWN RAM1 7 0x4020000 0x10000 0x1 RAM Write Enabled ``` -> **_Note:_** The above example takes into account that the binary uses the `chip_enable_ota_requestor=true` option. -The address offset corresponds to the space left for the SSBL binary and the OTA space for the SSBL. If -`chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be replaced with `0x0`. +> **_Note:_** The above example takes into account that the binary uses the +> `chip_enable_ota_requestor=true` option. The address offset corresponds to the +> space left for the SSBL binary and the OTA space for the SSBL. If +> `chip_enable_ota_requestor` is set to `false`, then `0x4000` needs to be +> replaced with `0x0`. ### Using MCUXpresso If flashing and debugging is required, MCUXpresso can be used as instructed in [MCUXpresso flashing and debugging instructions](https://github.com/openthread/ot-nxp/tree/main/src/k32w0/k32w061#using-mcuxpresso-ide). -The file needed to be used in MCUXpresso is _out/debug/chip-k32w0x-light-example_. +The file needed to be used in MCUXpresso is +_out/debug/chip-k32w0x-light-example_. ## Pigweed tokenizer @@ -565,34 +577,37 @@ In order to use the Tinycrypt ECC library, use the following build arguments: ## OTA -Over the air updates (OTA) require several software components running on the K32W0x1. -Firstly, a Secondary Stage Bootloader (SSBL) is required written in the first part of the -internal flash memory, usually starting at address 0x0. This enables the board to boot and -check if a new OTA binary has been received. If this is true, the bootloader writes the OTA -binary to the appropriate storage, internal and/or external flash, after which it reboots -the board. If no new OTA binaries have been found, then the bootloader gives execution control -to the application. - -The internal flash needs to be prepared for the OTA process. First 16K of the internal flash -needs to be populated with SSBL related data while the last 8.5K of flash space is holding flash -configuration related data. The space between these two zones will be filled by the application. -More details regarding the internal flash space can be found in the -[linker file](../../../platform/nxp/k32w0/app/ldscripts/chip-k32w0x-linker.ld). - -The steps for building the SSBL binary with appropriate configuration and writing to the board -the binary and other OTA related configurations are descrived in the +Over the air updates (OTA) require several software components running on the +K32W0x1. Firstly, a Secondary Stage Bootloader (SSBL) is required written in the +first part of the internal flash memory, usually starting at address 0x0. This +enables the board to boot and check if a new OTA binary has been received. If +this is true, the bootloader writes the OTA binary to the appropriate storage, +internal and/or external flash, after which it reboots the board. If no new OTA +binaries have been found, then the bootloader gives execution control to the +application. + +The internal flash needs to be prepared for the OTA process. First 16K of the +internal flash needs to be populated with SSBL related data while the last 8.5K +of flash space is holding flash configuration related data. The space between +these two zones will be filled by the application. More details regarding the +internal flash space can be found in the +[linker file](../../../../third_party/nxp/nxp_matter_support/examples/platform/k32w0/app/ldscripts/chip-k32w0x-linker.ld). + +The steps for building the SSBL binary with appropriate configuration and +writing to the board the binary and other OTA related configurations are +described in the [K32W0x1 OTA guide](../../../../docs/guides/nxp/nxp_k32w0_ota_guide.md). -Note that the application needs to be built using the `chip_enable_ota_requestor=true` option. -This is enabled in the configuration by default if no `chip_enable_ota_requestor` explicit setting -is done. +Note that the application needs to be built using the +`chip_enable_ota_requestor=true` option. This is enabled in the configuration by +default if no `chip_enable_ota_requestor` explicit setting is done. ### OTA Testing The OTA topology used for OTA testing is illustrated in the figure below. Topology is similar with the one used for Matter Test Events. -![OTA_TOPOLOGY](../../../../third_party/nxp/nxp_matter_support/examples/platform//k32w0/doc/images/ota_topology.JPG) +![OTA_TOPOLOGY](../../../platform/nxp/k32w0/doc/images/ota_topology.JPG) The concept for OTA is the next one: @@ -681,9 +696,10 @@ A note regarding OTA image header version (`-vn` option). An application binary has its own software version, given by `CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION` (`1` by default), which can be overwritten. For having a correct OTA process, the OTA header version should be -the same as the binary embedded software version. When building the update image, -the build arguments `nxp_software_version=2` and `nxp_sofware_version_string=\"2.0\"` -can be added to the gn gen command in order to specify the upgraded version. +the same as the binary embedded software version. When building the update +image, the build arguments `nxp_software_version=2` and +`nxp_sofware_version_string=\"2.0\"` can be added to the gn gen command in order +to specify the upgraded version. Start the OTA Provider Application: diff --git a/src/platform/nxp/k32w0/args.gni b/src/platform/nxp/k32w0/args.gni index 9d91b0fe39a645..1076eea4f4cf7f 100644 --- a/src/platform/nxp/k32w0/args.gni +++ b/src/platform/nxp/k32w0/args.gni @@ -23,8 +23,7 @@ nxp_use_lwip = false nxp_use_mbedtls_port = false if (getenv("NXP_K32W0_SDK_ROOT") == "") { - k32w0_sdk_root = - "${nxp_sdk_matter_support_root}/github_sdk/k32w0/repo" + k32w0_sdk_root = "${nxp_sdk_matter_support_root}/github_sdk/k32w0/repo" } else { k32w0_sdk_root = getenv("NXP_K32W0_SDK_ROOT") } From 2e56314b4bf01550d0e1654e0f061368fd4f10c4 Mon Sep 17 00:00:00 2001 From: marius-alex-tache Date: Mon, 5 Aug 2024 10:34:28 +0300 Subject: [PATCH 10/11] [NXP] Bump nxp_matter_support repo Signed-off-by: marius-alex-tache --- third_party/nxp/nxp_matter_support | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/nxp/nxp_matter_support b/third_party/nxp/nxp_matter_support index ac504a0cc38963..e6deaf56001387 160000 --- a/third_party/nxp/nxp_matter_support +++ b/third_party/nxp/nxp_matter_support @@ -1 +1 @@ -Subproject commit ac504a0cc389632c0e26b4f04e65914d3a9ba8bd +Subproject commit e6deaf5600138763ea68418e34bc62a02d1aef0d From 131324039e5d43965f1559a6046c9b9edf9b2fad Mon Sep 17 00:00:00 2001 From: Mihai Ignat Date: Wed, 31 Jul 2024 15:56:24 +0300 Subject: [PATCH 11/11] [k32w0][doc] Remove obsolete python dependency --- examples/contact-sensor-app/nxp/k32w0/README.md | 3 +-- examples/lighting-app/nxp/k32w0/README.md | 3 +-- scripts/setup/requirements.nxp.txt | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/contact-sensor-app/nxp/k32w0/README.md b/examples/contact-sensor-app/nxp/k32w0/README.md index 77e88a01ab46ab..305aa0b176af2a 100644 --- a/examples/contact-sensor-app/nxp/k32w0/README.md +++ b/examples/contact-sensor-app/nxp/k32w0/README.md @@ -262,7 +262,7 @@ used to control an antenna switch. In order to use this feature, user must set In case signing errors are encountered when running the "sign_images.sh" script (run automatically) install the recommanded packages (python version > 3, pip3, -pycrypto, pycryptodome): +pycryptodome): ``` user@ubuntu:~$ python3 --version @@ -270,7 +270,6 @@ Python 3.8.2 user@ubuntu:~$ pip3 --version pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.8) user@ubuntu:~$ pip3 list | grep -i pycrypto -pycrypto 2.6.1 pycryptodome 3.9.8 ``` diff --git a/examples/lighting-app/nxp/k32w0/README.md b/examples/lighting-app/nxp/k32w0/README.md index 810012f2507916..518e0232a4f6cb 100644 --- a/examples/lighting-app/nxp/k32w0/README.md +++ b/examples/lighting-app/nxp/k32w0/README.md @@ -275,7 +275,7 @@ used to control an antenna switch. In order to use this feature, user must set In case signing errors are encountered when running the "sign_images.sh" script (run automatically) install the recommanded packages (python version > 3, pip3, -pycrypto, pycryptodome): +pycryptodome): ``` user@ubuntu:~$ python3 --version @@ -283,7 +283,6 @@ Python 3.8.2 user@ubuntu:~$ pip3 --version pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.8) user@ubuntu:~$ pip3 list | grep -i pycrypto -pycrypto 2.6.1 pycryptodome 3.9.8 ``` diff --git a/scripts/setup/requirements.nxp.txt b/scripts/setup/requirements.nxp.txt index a78e1f0fc6230d..615e170ce83ce4 100644 --- a/scripts/setup/requirements.nxp.txt +++ b/scripts/setup/requirements.nxp.txt @@ -1,4 +1,3 @@ crc>=7.0.0 jsonschema>=4.17.0 -pycrypto>=2.6.1 pycryptodome>=3.20.0