Skip to content

Commit

Permalink
[ota-requestor] Add "ota query" shell command (#11810)
Browse files Browse the repository at this point in the history
* Enable OTA Provider client cluster in lighting-app

* Include client callbacks

* Add shell commands for OTA requestor role

ota query <fabric-index> <node-id> <ip-address> <udp-port>
    Connect to OTA provider node <node-id> and query a new
    software/firmware image. If available, automatically
    download and apply the upgrade.

For nRF Connect platform use DFU Target library to store
a downloaded image in the secondary image slot in flash,
so that it can be swapped with the primary/current image
by MCUBoot bootloader on a subsequent boot.

* Enable OTA requestor for lighting-app

* Restyled by gn

* Enable OTA Requestor only when built with BUILD_WITH_DFU

* Address review comments

* Add endpoint-id as another "query" command argument.
* Add "show-update" command to view udpate token of the
  current update.
* Add "apply" command for sending ApplyUpdate request.

* Re-generate ZAP

Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
2 people authored and pull[bot] committed Jan 25, 2022
1 parent 486ca13 commit 7877748
Show file tree
Hide file tree
Showing 26 changed files with 1,150 additions and 6 deletions.
17 changes: 17 additions & 0 deletions config/nrfconnect/app/overlay-ota_requestor.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# Copyright (c) 2021 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.
#

CONFIG_CHIP_OTA_REQUESTOR=y
1 change: 1 addition & 0 deletions config/nrfconnect/chip-module/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ chip_gn_arg_bool ("chip_enable_openthread" CONFIG_NET_L2_OPENTH
chip_gn_arg_bool ("chip_config_network_layer_ble" CONFIG_BT)
chip_gn_arg_bool ("chip_inet_config_enable_ipv4" CONFIG_NET_IPV4)
chip_gn_arg_bool ("chip_enable_nfc" CONFIG_CHIP_NFC_COMMISSIONING)
chip_gn_arg_bool ("chip_enable_ota_requestor" CONFIG_CHIP_OTA_REQUESTOR)
chip_gn_arg_bool ("chip_build_tests" CONFIG_CHIP_BUILD_TESTS)
chip_gn_arg_bool ("chip_monolithic_tests" CONFIG_CHIP_BUILD_TESTS)
chip_gn_arg_bool ("chip_inet_config_enable_tcp_endpoint" CONFIG_CHIP_BUILD_TESTS)
Expand Down
9 changes: 8 additions & 1 deletion config/nrfconnect/chip-module/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,11 @@ config CHIP_NFC_COMMISSIONING
imply NFC_NDEF_URI_REC
imply NFC_NDEF_URI_MSG
help
Enables NFC commissioning by sharing onboarding payload in NFC tag.
Enables NFC commissioning by sharing onboarding payload in NFC tag.

# See config/zephyr/Kconfig for full definition
config CHIP_OTA_REQUESTOR
bool
imply DFU_TARGET
imply STREAM_FLASH
imply STREAM_FLASH_ERASE
7 changes: 7 additions & 0 deletions config/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ config CHIP_ENABLE_SLEEPY_END_DEVICE_SUPPORT
help
Enables Thread Sleepy End Device support in Matter.

config CHIP_OTA_REQUESTOR
bool "Enable OTA requestor"
help
Enables OTA (Over-the-air) Requestor role that allows a device to perform
Device Firmware Upgrade by quering and downloading a new firmware image
from an external OTA Provider node.

config APP_LINK_WITH_CHIP
bool "Link 'app' with Connected Home over IP"
default y
Expand Down
78 changes: 78 additions & 0 deletions examples/lighting-app/lighting-common/lighting-app.zap
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,84 @@
}
]
},
{
"name": "OTA Software Update Provider",
"code": 41,
"mfgCode": null,
"define": "OTA_PROVIDER_CLUSTER",
"side": "client",
"enabled": 1,
"commands": [
{
"name": "QueryImage",
"code": 0,
"mfgCode": null,
"source": "client",
"incoming": 0,
"outgoing": 1
},
{
"name": "ApplyUpdateRequest",
"code": 1,
"mfgCode": null,
"source": "client",
"incoming": 0,
"outgoing": 1
},
{
"name": "NotifyUpdateApplied",
"code": 2,
"mfgCode": null,
"source": "client",
"incoming": 0,
"outgoing": 1
}
],
"attributes": [
{
"name": "ClusterRevision",
"code": 65533,
"mfgCode": null,
"side": "client",
"included": 0,
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
"defaultValue": "1",
"reportable": 0,
"minInterval": 1,
"maxInterval": 65534,
"reportableChange": 0
}
]
},
{
"name": "OTA Software Update Provider",
"code": 41,
"mfgCode": null,
"define": "OTA_PROVIDER_CLUSTER",
"side": "server",
"enabled": 0,
"commands": [
{
"name": "QueryImageResponse",
"code": 3,
"mfgCode": null,
"source": "server",
"incoming": 1,
"outgoing": 0
},
{
"name": "ApplyUpdateResponse",
"code": 4,
"mfgCode": null,
"source": "server",
"incoming": 1,
"outgoing": 0
}
],
"attributes": []
},
{
"name": "General Commissioning",
"code": 48,
Expand Down
5 changes: 5 additions & 0 deletions examples/lighting-app/nrfconnect/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ endif()

option(BUILD_WITH_DFU "Build target with Device Firmware Upgrade support" OFF)
if(BUILD_WITH_DFU)
list(INSERT OVERLAY_CONFIG 0 ${CHIP_ROOT}/config/nrfconnect/app/overlay-ota_requestor.conf)
if(${BOARD} STREQUAL "nrf5340dk_nrf5340_cpuapp")
list(INSERT OVERLAY_CONFIG 0 ${CHIP_ROOT}/config/nrfconnect/app/overlay-multi_image_dfu_support.conf)
set(mcuboot_OVERLAY_CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/configuration/mcuboot_multi_image_dfu.conf CACHE INTERNAL "")
Expand All @@ -61,6 +62,7 @@ target_compile_options(app PRIVATE -Werror)
target_include_directories(app PRIVATE
main/include
${LIGHTING_COMMON}
${GEN_DIR}
${GEN_DIR}/app-common
${GEN_DIR}/lighting-app
${NLIO_ROOT}
Expand All @@ -74,13 +76,16 @@ target_sources(app PRIVATE
main/ZclCallbacks.cpp
${GEN_DIR}/lighting-app/zap-generated/attribute-size.cpp
${GEN_DIR}/lighting-app/zap-generated/callback-stub.cpp
${GEN_DIR}/lighting-app/zap-generated/CHIPClusters.cpp
${GEN_DIR}/lighting-app/zap-generated/IMClusterCommandHandler.cpp
${NRFCONNECT_COMMON}/util/LEDWidget.cpp
${NRFCONNECT_COMMON}/util/ThreadUtil.cpp)

chip_configure_data_model(app
INCLUDE_CLIENT_CALLBACKS
INCLUDE_SERVER
ZAP_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../lighting-common/lighting-app.zap
GEN_DIR ${GEN_DIR}/lighting-app/zap-generated
)

if(BUILD_WITH_DFU)
Expand Down
2 changes: 2 additions & 0 deletions src/app/DeviceProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class DLL_EXPORT DeviceProxy

virtual bool IsActive() const { return true; }

uint32_t GetMRPIdleInterval() const { return mMrpIdleInterval; }
uint32_t GetMRPActiveInterval() const { return mMrpActiveInterval; }
void GetMRPIntervals(uint32_t & idleInterval, uint32_t & activeInterval) const
{
idleInterval = mMrpIdleInterval;
Expand Down
9 changes: 8 additions & 1 deletion src/app/chip_data_model.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ endfunction()
# supported by the application.
#
function(chip_configure_data_model APP_TARGET)
cmake_parse_arguments(ARG "INCLUDE_SERVER" "ZAP_FILE" "" ${ARGN})
cmake_parse_arguments(ARG "INCLUDE_CLIENT_CALLBACKS;INCLUDE_SERVER" "ZAP_FILE;GEN_DIR" "" ${ARGN})

if (ARG_INCLUDE_CLIENT_CALLBACKS)
target_sources(${APP_TARGET} PRIVATE
${CHIP_APP_BASE_DIR}/util/im-client-callbacks.cpp
${ARG_GEN_DIR}/CHIPClientCallbacks.cpp
)
endif()

if (ARG_INCLUDE_SERVER)
target_sources(${APP_TARGET} PRIVATE
Expand Down
6 changes: 6 additions & 0 deletions src/lib/shell/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ void RegisterConfigCommands();
*/
void RegisterDeviceCommands();

/**
* This function registers the OTA (Over-the-Air) software update commands.
*
*/
void RegisterOtaCommands();

/**
* This function registers the device onboarding codes commands.
*
Expand Down
3 changes: 3 additions & 0 deletions src/lib/shell/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ void Engine::RegisterDefaultCommands()
#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
RegisterDnsCommands();
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
RegisterOtaCommands();
#endif
}

} // namespace Shell
Expand Down
2 changes: 1 addition & 1 deletion src/lib/shell/MainLoopZephyr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static int RegisterCommands(const struct device * dev)

SYS_INIT(RegisterCommands, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

SHELL_CMD_ARG_REGISTER(matter, NULL, "Matter commands", cmd_matter, 1, 4);
SHELL_CMD_ARG_REGISTER(matter, NULL, "Matter commands", cmd_matter, 1, 10);

namespace chip {
namespace Shell {
Expand Down
11 changes: 10 additions & 1 deletion src/lib/shell/commands/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ source_set("commands") {
"Meta.cpp",
]

public_deps = [ "${chip_root}/src/lib/shell:shell_core" ]

if (chip_device_platform != "none") {
sources += [
"Config.cpp",
Expand All @@ -49,7 +51,14 @@ source_set("commands") {
sources += [ "Dns.cpp" ]
}

public_deps = [ "${chip_root}/src/lib/shell:shell_core" ]
if (chip_enable_ota_requestor && chip_device_platform != "none") {
sources += [ "Ota.cpp" ]
configs += [ "${chip_root}/src/controller:config" ]

if (chip_device_platform == "nrfconnect") {
sources += [ "DFUManager_nrfconnect.h" ]
}
}

if (chip_device_platform != "none") {
public_deps += [ "${chip_root}/src/app/server" ]
Expand Down
142 changes: 142 additions & 0 deletions src/lib/shell/commands/DFUManager_nrfconnect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
*
* Copyright (c) 2021 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 <lib/support/TypeTraits.h>
#include <lib/support/logging/CHIPLogging.h>
#include <messaging/ExchangeContext.h>
#include <protocols/bdx/BdxMessages.h>
#include <protocols/bdx/BdxTransferSession.h>
#include <protocols/bdx/TransferFacilitator.h>

#include <dfu/dfu_target.h>
#include <dfu/dfu_target_mcuboot.h>

namespace chip {
namespace Shell {

class DFUManager : public bdx::Initiator
{
public:
void SetInitialExchange(Messaging::ExchangeContext * ec) { mExchangeCtx = ec; }
CHIP_ERROR ApplyUpdate();
CHIP_ERROR DiscardUpdate();

private:
void HandleTransferSessionOutput(bdx::TransferSession::OutputEvent & event) override;

uint8_t mDfuBuffer[1024];
bool mIsTransferComplete = false;
};

inline CHIP_ERROR DFUManager::ApplyUpdate()
{
return System::MapErrorZephyr(dfu_target_done(true));
}

inline CHIP_ERROR DFUManager::DiscardUpdate()
{
return System::MapErrorZephyr(dfu_target_reset());
}

inline void DFUManager::HandleTransferSessionOutput(bdx::TransferSession::OutputEvent & event)
{
using OutputEventType = bdx::TransferSession::OutputEventType;
using MessageType = bdx::MessageType;
using SendMessageFlags = Messaging::SendMessageFlags;

CHIP_ERROR error = CHIP_NO_ERROR;
int status = 0;

switch (event.EventType)
{
case OutputEventType::kNone:
if (mIsTransferComplete)
{
ChipLogProgress(BDX, "Transfer complete!");
mTransfer.Reset();
mIsTransferComplete = false;
}
break;
case OutputEventType::kMsgToSend: {
Messaging::SendFlags flags;
flags.Set(SendMessageFlags::kFromInitiator, event.msgTypeData.MessageType == to_underlying(MessageType::ReceiveInit));
flags.Set(SendMessageFlags::kExpectResponse, event.msgTypeData.MessageType != to_underlying(MessageType::BlockAckEOF));

VerifyOrReturn(mExchangeCtx != nullptr, ChipLogError(BDX, "Exchange context is null"));
error =
mExchangeCtx->SendMessage(event.msgTypeData.ProtocolId, event.msgTypeData.MessageType, std::move(event.MsgData), flags);
VerifyOrReturn(error == CHIP_NO_ERROR, ChipLogError(BDX, "SendMessage() failed: %" CHIP_ERROR_FORMAT, error.Format()));
break;
}
case OutputEventType::kAcceptReceived:
ChipLogProgress(BDX, "Starting transfer of %uB", static_cast<unsigned>(event.transferAcceptData.Length));

dfu_target_mcuboot_set_buf(mDfuBuffer, sizeof(mDfuBuffer));
dfu_target_reset();
status = dfu_target_init(DFU_TARGET_IMAGE_TYPE_MCUBOOT, event.transferAcceptData.Length, nullptr);
VerifyOrReturn(status == 0, ChipLogError(BDX, "dfu_target_init() failed: %d", status));

error = mTransfer.PrepareBlockQuery();
VerifyOrReturn(error == CHIP_NO_ERROR,
ChipLogError(BDX, "PrepareBlockQuery() failed: %" CHIP_ERROR_FORMAT, error.Format()));
break;
case OutputEventType::kBlockReceived:
ChipLogProgress(BDX, "Received %uB (total: %ukB)", static_cast<unsigned>(event.blockdata.Length),
static_cast<unsigned>(mTransfer.GetNumBytesProcessed()) / 1024u);

status = dfu_target_write(event.blockdata.Data, event.blockdata.Length);
VerifyOrReturn(status == 0, ChipLogError(BDX, "dfu_target_write() failed: %d", status));

if (event.blockdata.IsEof)
{
error = mTransfer.PrepareBlockAck();
VerifyOrReturn(error == CHIP_NO_ERROR,
ChipLogError(BDX, "PrepareBlockAck() failed: %" CHIP_ERROR_FORMAT, error.Format()));
mIsTransferComplete = true;
}
else
{
error = mTransfer.PrepareBlockQuery();
VerifyOrReturn(error == CHIP_NO_ERROR,
ChipLogError(BDX, "PrepareBlockQuery() failed: %" CHIP_ERROR_FORMAT, error.Format()));
}
break;
case OutputEventType::kStatusReceived:
ChipLogError(BDX, "Received status %" PRIu16, to_underlying(event.statusData.statusCode));
mTransfer.Reset();
dfu_target_reset();
break;
case OutputEventType::kInternalError:
ChipLogError(BDX, "Transfer stopped due to internal error");
mTransfer.Reset();
dfu_target_reset();
break;
case OutputEventType::kTransferTimeout:
ChipLogError(BDX, "Transfer timed out");
mTransfer.Reset();
dfu_target_reset();
break;
default:
ChipLogError(BDX, "Unexpected event type: %" PRIu16, to_underlying(event.EventType));
break;
}
}

} // namespace Shell
} // namespace chip
Loading

0 comments on commit 7877748

Please sign in to comment.