From 668d121797a0dc9e19d4347eab81057ee60cba4f Mon Sep 17 00:00:00 2001 From: liyashuai Date: Tue, 3 Sep 2024 16:35:19 +0800 Subject: [PATCH] esp32: add thread border router ota function --- examples/thread-br-app/esp32/README.md | 13 + examples/thread-br-app/esp32/partitions.csv | 6 +- .../thread-br-app/esp32/sdkconfig.defaults | 25 +- .../thread-br-common/thread-br-app.matter | 249 ++++++++++ .../thread-br-common/thread-br-app.zap | 451 ++++++++++++++++++ src/platform/ESP32/OTAImageProcessorImpl.cpp | 191 +++++++- src/platform/ESP32/OTAImageProcessorImpl.h | 4 + src/platform/ESP32/OpenthreadLauncher.cpp | 14 +- 8 files changed, 944 insertions(+), 9 deletions(-) diff --git a/examples/thread-br-app/esp32/README.md b/examples/thread-br-app/esp32/README.md index 2578a106abeb3b..f684c01e9241e6 100644 --- a/examples/thread-br-app/esp32/README.md +++ b/examples/thread-br-app/esp32/README.md @@ -16,6 +16,7 @@ guides to get started. - [OpenThread CLI](#openthread-cli) - [Setup Thread Network](#setup-thread-network) - [Commissioning Thread End Devices](#commissioning-thread-end-devices) +- [Generate OTA Firmware For BR](#generate-ota-firmware-for-br) --- @@ -92,3 +93,15 @@ the Thread network. ``` ./chip-tool pairing ble-wifi 2 hex: ``` + +### Generate OTA Firmware For BR + +Can use this python +[script](https://github.com/espressif/esp-thread-br/blob/main/components/esp_rcp_update/create_ota_image.py) +to merge thread border router firmware and the RCP firmware + +``` +python create_ota_image.py --rcp-build-dir /path/to/rcp_image/dir --br-firmware /path/to/br_image --target-file ota_target_file.bin +``` + +Then can use the generated bin file to create a Matter OTA file normally. diff --git a/examples/thread-br-app/esp32/partitions.csv b/examples/thread-br-app/esp32/partitions.csv index 745321df3785fc..b2c541aced41de 100644 --- a/examples/thread-br-app/esp32/partitions.csv +++ b/examples/thread-br-app/esp32/partitions.csv @@ -3,6 +3,6 @@ nvs, data, nvs, , 0xC000, otadata, data, ota, , 0x2000, phy_init, data, phy, , 0x1000, -ota_0, app, ota_0, , 1800K, -ota_1, app, ota_1, , 1800K, -rcp_fw, data, spiffs, , 300K, +ota_0, app, ota_0, , 0x1A9000, +ota_1, app, ota_1, , 0x190000, +rcp_fw, data, spiffs, , 0xA0000, diff --git a/examples/thread-br-app/esp32/sdkconfig.defaults b/examples/thread-br-app/esp32/sdkconfig.defaults index 498081c21f8c47..f79e26cf0bdf86 100644 --- a/examples/thread-br-app/esp32/sdkconfig.defaults +++ b/examples/thread-br-app/esp32/sdkconfig.defaults @@ -29,8 +29,9 @@ CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n # Increase some stack size CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=7200 -CONFIG_ESP_MAIN_TASK_STACK_SIZE=5120 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=10240 CONFIG_ESP_TIMER_TASK_STACK_SIZE=5120 +CONFIG_CHIP_TASK_STACK_SIZE=10240 # USB console for Thread border board CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y @@ -69,9 +70,27 @@ CONFIG_THREAD_NETWORK_COMMISSIONING_DRIVER=n # Auto update RCP firmware CONFIG_AUTO_UPDATE_RCP=y +CONFIG_ENABLE_OTA_REQUESTOR=y +CONFIG_CREATE_OTA_IMAGE_WITH_RCP_FW=y # Use platform mDNS CONFIG_USE_MINIMAL_MDNS=n -# Enable Matter shell -CONFIG_ENABLE_CHIP_SHELL=y +# Reduce flash size +CONFIG_ENABLE_CHIP_SHELL=n +CONFIG_OPENTHREAD_CLI=n +CONFIG_NEWLIB_NANO_FORMAT=y + +CONFIG_NIMBLE_MAX_CONNECTIONS=1 +CONFIG_BTDM_CTRL_BLE_MAX_CONN=1 +CONFIG_BT_NIMBLE_ROLE_CENTRAL=n +CONFIG_BT_NIMBLE_ROLE_OBSERVER=n + +CONFIG_EVENT_LOGGING_CRIT_BUFFER_SIZE=1024 + +#CONFIG_LOG_DEFAULT_LEVEL_ERROR=y + +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y +CONFIG_ESP_ERR_TO_NAME_LOOKUP=n diff --git a/examples/thread-br-app/thread-br-common/thread-br-app.matter b/examples/thread-br-app/thread-br-common/thread-br-app.matter index b1ef02cd5aa47f..1f100b6e2dcfcc 100644 --- a/examples/thread-br-app/thread-br-common/thread-br-app.matter +++ b/examples/thread-br-app/thread-br-common/thread-br-app.matter @@ -496,6 +496,221 @@ cluster BasicInformation = 40 { command MfgSpecificPing(): DefaultSuccess = 0; } +/** Provides an interface for providing OTA software updates */ +cluster OtaSoftwareUpdateProvider = 41 { + revision 1; // NOTE: Default/not specifically set + + enum ApplyUpdateActionEnum : enum8 { + kProceed = 0; + kAwaitNextAction = 1; + kDiscontinue = 2; + } + + enum DownloadProtocolEnum : enum8 { + kBDXSynchronous = 0; + kBDXAsynchronous = 1; + kHTTPS = 2; + kVendorSpecific = 3; + } + + enum StatusEnum : enum8 { + kUpdateAvailable = 0; + kBusy = 1; + kNotAvailable = 2; + kDownloadProtocolNotSupported = 3; + } + + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct QueryImageRequest { + vendor_id vendorID = 0; + int16u productID = 1; + int32u softwareVersion = 2; + DownloadProtocolEnum protocolsSupported[] = 3; + optional int16u hardwareVersion = 4; + optional char_string<2> location = 5; + optional boolean requestorCanConsent = 6; + optional octet_string<512> metadataForProvider = 7; + } + + response struct QueryImageResponse = 1 { + StatusEnum status = 0; + optional int32u delayedActionTime = 1; + optional char_string<256> imageURI = 2; + optional int32u softwareVersion = 3; + optional char_string<64> softwareVersionString = 4; + optional octet_string<32> updateToken = 5; + optional boolean userConsentNeeded = 6; + optional octet_string<512> metadataForRequestor = 7; + } + + request struct ApplyUpdateRequestRequest { + octet_string<32> updateToken = 0; + int32u newVersion = 1; + } + + response struct ApplyUpdateResponse = 3 { + ApplyUpdateActionEnum action = 0; + int32u delayedActionTime = 1; + } + + request struct NotifyUpdateAppliedRequest { + octet_string<32> updateToken = 0; + int32u softwareVersion = 1; + } + + /** Determine availability of a new Software Image */ + command QueryImage(QueryImageRequest): QueryImageResponse = 0; + /** Determine next action to take for a downloaded Software Image */ + command ApplyUpdateRequest(ApplyUpdateRequestRequest): ApplyUpdateResponse = 2; + /** Notify OTA Provider that an update was applied */ + command NotifyUpdateApplied(NotifyUpdateAppliedRequest): DefaultSuccess = 4; +} + +/** Provides an interface for downloading and applying OTA software updates */ +cluster OtaSoftwareUpdateRequestor = 42 { + revision 1; // NOTE: Default/not specifically set + + enum AnnouncementReasonEnum : enum8 { + kSimpleAnnouncement = 0; + kUpdateAvailable = 1; + kUrgentUpdateAvailable = 2; + } + + enum ChangeReasonEnum : enum8 { + kUnknown = 0; + kSuccess = 1; + kFailure = 2; + kTimeOut = 3; + kDelayByProvider = 4; + } + + enum UpdateStateEnum : enum8 { + kUnknown = 0; + kIdle = 1; + kQuerying = 2; + kDelayedOnQuery = 3; + kDownloading = 4; + kApplying = 5; + kDelayedOnApply = 6; + kRollingBack = 7; + kDelayedOnUserConsent = 8; + } + + fabric_scoped struct ProviderLocation { + node_id providerNodeID = 1; + endpoint_no endpoint = 2; + fabric_idx fabricIndex = 254; + } + + info event StateTransition = 0 { + UpdateStateEnum previousState = 0; + UpdateStateEnum newState = 1; + ChangeReasonEnum reason = 2; + nullable int32u targetSoftwareVersion = 3; + } + + critical event VersionApplied = 1 { + int32u softwareVersion = 0; + int16u productID = 1; + } + + info event DownloadError = 2 { + int32u softwareVersion = 0; + int64u bytesDownloaded = 1; + nullable int8u progressPercent = 2; + nullable int64s platformCode = 3; + } + + attribute access(write: administer) ProviderLocation defaultOTAProviders[] = 0; + readonly attribute boolean updatePossible = 1; + readonly attribute UpdateStateEnum updateState = 2; + readonly attribute nullable int8u updateStateProgress = 3; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct AnnounceOTAProviderRequest { + node_id providerNodeID = 0; + vendor_id vendorID = 1; + AnnouncementReasonEnum announcementReason = 2; + optional octet_string<512> metadataForNode = 3; + endpoint_no endpoint = 4; + } + + /** Announce the presence of an OTA Provider */ + command AnnounceOTAProvider(AnnounceOTAProviderRequest): DefaultSuccess = 0; +} + +/** Nodes should be expected to be deployed to any and all regions of the world. These global regions + may have differing common languages, units of measurements, and numerical formatting + standards. As such, Nodes that visually or audibly convey information need a mechanism by which + they can be configured to use a user’s preferred language, units, etc */ +cluster LocalizationConfiguration = 43 { + revision 1; // NOTE: Default/not specifically set + + attribute access(write: manage) char_string<35> activeLocale = 0; + readonly attribute char_string supportedLocales[] = 1; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + +/** Nodes should be expected to be deployed to any and all regions of the world. These global regions + may have differing preferences for how dates and times are conveyed. As such, Nodes that visually + or audibly convey time information need a mechanism by which they can be configured to use a + user’s preferred format. */ +cluster TimeFormatLocalization = 44 { + revision 1; // NOTE: Default/not specifically set + + enum CalendarTypeEnum : enum8 { + kBuddhist = 0; + kChinese = 1; + kCoptic = 2; + kEthiopian = 3; + kGregorian = 4; + kHebrew = 5; + kIndian = 6; + kIslamic = 7; + kJapanese = 8; + kKorean = 9; + kPersian = 10; + kTaiwanese = 11; + kUseActiveLocale = 255; + } + + enum HourFormatEnum : enum8 { + k12hr = 0; + k24hr = 1; + kUseActiveLocale = 255; + } + + bitmap Feature : bitmap32 { + kCalendarFormat = 0x1; + } + + attribute access(write: manage) HourFormatEnum hourFormat = 0; + attribute access(write: manage) optional CalendarTypeEnum activeCalendarType = 1; + readonly attribute optional CalendarTypeEnum supportedCalendarTypes[] = 2; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + /** This cluster is used to manage global aspects of the Commissioning flow. */ cluster GeneralCommissioning = 48 { revision 1; // NOTE: Default/not specifically set @@ -1403,6 +1618,7 @@ provisional cluster ThreadBorderRouterManagement = 1106 { endpoint 0 { device type ma_rootdevice = 22, version 1; + binding cluster OtaSoftwareUpdateProvider; server cluster Descriptor { callback attribute deviceTypeList; @@ -1458,6 +1674,39 @@ endpoint 0 { ram attribute clusterRevision default = 3; } + server cluster OtaSoftwareUpdateRequestor { + emits event StateTransition; + emits event VersionApplied; + emits event DownloadError; + callback attribute defaultOTAProviders; + ram attribute updatePossible default = true; + ram attribute updateState default = 0; + ram attribute updateStateProgress; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command AnnounceOTAProvider; + } + + server cluster LocalizationConfiguration { + persist attribute activeLocale default = "en-US"; + callback attribute supportedLocales; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + } + + server cluster TimeFormatLocalization { + persist attribute hourFormat default = 0; + persist attribute activeCalendarType default = 0; + callback attribute supportedCalendarTypes; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + } + server cluster GeneralCommissioning { ram attribute breadcrumb default = 0x0000000000000000; callback attribute basicCommissioningInfo; diff --git a/examples/thread-br-app/thread-br-common/thread-br-app.zap b/examples/thread-br-app/thread-br-common/thread-br-app.zap index 09f5a2e3a90442..9930d525fc41e7 100644 --- a/examples/thread-br-app/thread-br-common/thread-br-app.zap +++ b/examples/thread-br-app/thread-br-common/thread-br-app.zap @@ -770,6 +770,457 @@ } ] }, + { + "name": "OTA Software Update Provider", + "code": 41, + "mfgCode": null, + "define": "OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER", + "side": "client", + "enabled": 1, + "commands": [ + { + "name": "QueryImage", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "QueryImageResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ApplyUpdateRequest", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ApplyUpdateResponse", + "code": 3, + "mfgCode": null, + "source": "server", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "NotifyUpdateApplied", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "client", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "client", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "OTA Software Update Requestor", + "code": 42, + "mfgCode": null, + "define": "OTA_SOFTWARE_UPDATE_REQUESTOR_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "AnnounceOTAProvider", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "DefaultOTAProviders", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "UpdatePossible", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "true", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "UpdateState", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "UpdateStateEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "UpdateStateProgress", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "StateTransition", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "VersionApplied", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "DownloadError", + "code": 2, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] + }, + { + "name": "Localization Configuration", + "code": 43, + "mfgCode": null, + "define": "LOCALIZATION_CONFIGURATION_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "ActiveLocale", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "en-US", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SupportedLocales", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Time Format Localization", + "code": 44, + "mfgCode": null, + "define": "TIME_FORMAT_LOCALIZATION_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "HourFormat", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "HourFormatEnum", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ActiveCalendarType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "CalendarTypeEnum", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SupportedCalendarTypes", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "General Commissioning", "code": 48, diff --git a/src/platform/ESP32/OTAImageProcessorImpl.cpp b/src/platform/ESP32/OTAImageProcessorImpl.cpp index 284f21c0639eec..2a47bbb47beb10 100644 --- a/src/platform/ESP32/OTAImageProcessorImpl.cpp +++ b/src/platform/ESP32/OTAImageProcessorImpl.cpp @@ -36,6 +36,12 @@ #include #endif // CONFIG_ENABLE_DELTA_OTA +#if defined(CONFIG_AUTO_UPDATE_RCP) && defined(CONFIG_OPENTHREAD_BORDER_ROUTER) +#include "esp_br_firmware.h" +#include "esp_check.h" +#include "esp_rcp_update.h" +#endif + #define TAG "OTAImageProcessor" #ifdef CONFIG_ENABLE_DELTA_OTA @@ -43,6 +49,11 @@ #define DIGEST_SIZE 32 #endif // CONFIG_ENABLE_DELTA_OTA +#if defined(CONFIG_AUTO_UPDATE_RCP) && defined(CONFIG_OPENTHREAD_BORDER_ROUTER) +#define MAX_WRITE_RETRY_COUNT 5 +#define OTA_MAX_WRITE_SIZE 16 +#endif // defined(CONFIG_AUTO_UPDATE_RCP) && defined(CONFIG_OPENTHREAD_BORDER_ROUTER) + using namespace chip::System; using namespace ::chip::DeviceLayer::Internal; @@ -280,6 +291,182 @@ esp_err_t OTAImageProcessorImpl::DeltaOTAWriteCallback(const uint8_t * buf, size } #endif // CONFIG_ENABLE_DELTA_OTA +#if defined(CONFIG_AUTO_UPDATE_RCP) && defined(CONFIG_OPENTHREAD_BORDER_ROUTER) +typedef enum rcpImageState +{ + BR_OTA_INIT = 0, + RCP_DOWNLOAD, + BR_FW_DOWNLOAD, +} RcpImageState_t; + +static int WriteFileForLength(FILE * fp, const void * buf, size_t size) +{ + int retryCount = 0; + int offset = 0; + const uint8_t * data = (const uint8_t *)buf; + while (offset < size) + { + int ret = fwrite(data + offset, 1, ((size - offset) < OTA_MAX_WRITE_SIZE ? (size - offset) : OTA_MAX_WRITE_SIZE), fp); + if (ret < 0) + { + return ret; + } + if (ret == 0) + { + retryCount++; + } + else + { + offset += ret; + retryCount = 0; + } + + if (retryCount > MAX_WRITE_RETRY_COUNT) + { + return -1; + } + } + return size; +} + +esp_err_t OTAImageProcessorImpl::ProcessRcpImage(esp_ota_handle_t handle, const uint8_t * buffer, uint32_t bufLengthen) +{ + static char rcpTargetPath[RCP_FILENAME_MAX_SIZE]; + static RcpImageState_t state = BR_OTA_INIT; + static const char * rcpFirmwareDir = esp_rcp_get_firmware_dir(); + static int8_t rcpUpdateSeq = esp_rcp_get_next_update_seq(); + static uint32_t headerSize = 0; + static uint32_t totalRevSize = 0; + static uint32_t brFirmwareOffset = 0; + static uint32_t brFirmwareSize = 0; + static FILE * fp = NULL; + + switch (state) + { + case BR_OTA_INIT: { + sprintf(rcpTargetPath, "%s_%d/" ESP_BR_RCP_IMAGE_FILENAME, rcpFirmwareDir, rcpUpdateSeq); + ESP_LOGI(TAG, "Downloading RCP target file %s", rcpTargetPath); + + fp = fopen(rcpTargetPath, "w"); + if (!fp) + { + ESP_LOGE(TAG, "Fail to open %s, will delete it", rcpTargetPath); + remove(rcpTargetPath); + return ESP_FAIL; + } + + esp_br_subfile_info_t subFileInfo[7]; + if (sizeof(subFileInfo) > bufLengthen) + { + ESP_LOGE(TAG, "Packet len is less than the RCP header len"); + if (fp != NULL) + { + fclose(fp); + } + return ESP_FAIL; + } + + memcpy((char *) subFileInfo, buffer, sizeof(subFileInfo)); + for (int i = 0; i < 7; i++) + { + ESP_LOGD(TAG, "subFileInfo[%d].tag:0x%lx--offset:0x%lx---size:0x%lx\n", i, subFileInfo[i].tag, subFileInfo[i].offset, + subFileInfo[i].size); + if (subFileInfo[i].tag == FILETAG_IMAGE_HEADER) + { + headerSize = subFileInfo[i].size; + ESP_LOGI(TAG, "RCP image header-----headerSize:%ld\n", headerSize); + } + else if (subFileInfo[i].tag == FILETAG_BR_FIRMWARE) + { + brFirmwareOffset = subFileInfo[i].offset; + brFirmwareSize = subFileInfo[i].size; + ESP_LOGI(TAG, "Border Router image--offset:%ld--size:%ld\n", brFirmwareOffset, brFirmwareSize); + } + } + + if ((headerSize != sizeof(subFileInfo)) || (brFirmwareOffset == 0) || (brFirmwareSize == 0)) + { + ESP_LOGE(TAG, "RCP header error"); + fclose(fp); + return ESP_FAIL; + } + + if (WriteFileForLength(fp, buffer, bufLengthen) != bufLengthen) + { + ESP_LOGE(TAG, "Failed to write data"); + fclose(fp); + return ESP_FAIL; + } + + state = RCP_DOWNLOAD; + totalRevSize = bufLengthen; + break; + } + case RCP_DOWNLOAD: { + if (totalRevSize + bufLengthen >= brFirmwareOffset) + { + uint32_t len = brFirmwareOffset - totalRevSize; + if (WriteFileForLength(fp, buffer, len) == len) + { + ESP_LOGI(TAG, "RCP receive done, total size %ld bytes", brFirmwareOffset); + state = BR_FW_DOWNLOAD; + fclose(fp); + if (esp_rcp_submit_new_image() != ESP_OK) + { + ESP_LOGI(TAG, "Failed to submit RCP image"); + state = BR_OTA_INIT; + return ESP_FAIL; + } + } + else + { + ESP_LOGE(TAG, "Failed to write data"); + fclose(fp); + state = BR_OTA_INIT; + return ESP_FAIL; + } + + if (esp_ota_write(handle, buffer + len, bufLengthen - len) != ESP_OK) + { + ESP_LOGE(TAG, "OTA write failed"); + state = BR_OTA_INIT; + return ESP_FAIL; + } + + totalRevSize = bufLengthen - len; + } + else + { + if (WriteFileForLength(fp, buffer, bufLengthen) != bufLengthen) + { + ESP_LOGE(TAG, "Failed to write data"); + fclose(fp); + state = BR_OTA_INIT; + return ESP_FAIL; + } + totalRevSize += bufLengthen; + } + break; + } + case BR_FW_DOWNLOAD: { + if (esp_ota_write(handle, buffer, bufLengthen) != ESP_OK) + { + ESP_LOGE(TAG, "OTA write failed"); + state = BR_OTA_INIT; + return ESP_FAIL; + ESP_LOGI(TAG, "Thread BR image receive done"); + } + break; + } + default: { + return ESP_FAIL; + } + } + + return ESP_OK; +} +#endif + void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context) { auto * imageProcessor = reinterpret_cast(context); @@ -483,8 +670,10 @@ void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context) // Apply the patch and writes that data to the passive partition. err = esp_delta_ota_feed_patch(imageProcessor->mDeltaOTAUpdateHandle, blockToWrite.data() + index, blockToWrite.size() - index); +#elif defined(CONFIG_AUTO_UPDATE_RCP) && defined(CONFIG_OPENTHREAD_BORDER_ROUTER) + err = imageProcessor->ProcessRcpImage(imageProcessor->mOTAUpdateHandle, blockToWrite.data(), blockToWrite.size()); #else - err = esp_ota_write(imageProcessor->mOTAUpdateHandle, blockToWrite.data(), blockToWrite.size()); + err = esp_ota_write(imageProcessor->mOTAUpdateHandle, blockToWrite.data(), blockToWrite.size()); #endif // CONFIG_ENABLE_DELTA_OTA #ifdef CONFIG_ENABLE_ENCRYPTED_OTA diff --git a/src/platform/ESP32/OTAImageProcessorImpl.h b/src/platform/ESP32/OTAImageProcessorImpl.h index 3414f90b425505..058935b8d70cc8 100644 --- a/src/platform/ESP32/OTAImageProcessorImpl.h +++ b/src/platform/ESP32/OTAImageProcessorImpl.h @@ -99,6 +99,10 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface bool mEncryptedOTAEnabled = false; esp_decrypt_handle_t mOTADecryptionHandle = nullptr; #endif // CONFIG_ENABLE_ENCRYPTED_OTA + +#if defined(CONFIG_AUTO_UPDATE_RCP) && defined(CONFIG_OPENTHREAD_BORDER_ROUTER) + esp_err_t ProcessRcpImage(esp_ota_handle_t handle, const uint8_t * buffer, uint32_t bufLengthen); +#endif }; } // namespace chip diff --git a/src/platform/ESP32/OpenthreadLauncher.cpp b/src/platform/ESP32/OpenthreadLauncher.cpp index 0ab3dadf6862cb..575e583eeb8bbd 100644 --- a/src/platform/ESP32/OpenthreadLauncher.cpp +++ b/src/platform/ESP32/OpenthreadLauncher.cpp @@ -196,8 +196,18 @@ static void try_update_ot_rcp(const esp_openthread_platform_config_t * config) static void rcp_failure_handler(void) { esp_rcp_mark_image_unusable(); - try_update_ot_rcp(s_platform_config); - esp_rcp_reset(); + char internal_rcp_version[kRcpVersionMaxSize]; + if (esp_rcp_load_version_in_storage(internal_rcp_version, sizeof(internal_rcp_version)) == ESP_OK) + { + ESP_LOGI(TAG, "Internal RCP Version: %s", internal_rcp_version); + update_rcp(); + } + else + { + ESP_LOGI(TAG, "RCP firmware not found in storage, will reboot to try next image"); + esp_rcp_mark_image_verified(false); + esp_restart(); + } } #endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP