Skip to content

Commit

Permalink
[ota-requestor] Build QueryImage based on Basic cluster attributes (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Damian-Nordic authored and pull[bot] committed Dec 22, 2023
1 parent 84f1417 commit 1545845
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@
"mfgCode": null,
"define": "BASIC_CLUSTER",
"side": "server",
"enabled": 0,
"enabled": 1,
"commands": [
{
"name": "StartUp",
Expand Down Expand Up @@ -3711,5 +3711,6 @@
"endpointVersion": 1,
"deviceIdentifier": 22
}
]
],
"log": []
}
160 changes: 82 additions & 78 deletions src/app/clusters/ota-requestor/OTARequestor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,61 +29,36 @@

#include "BDXDownloader.h"

#include <app-common/zap-generated/attributes/Accessors.h>
#include <controller/CHIPDeviceControllerFactory.h>
#include <controller/CommissioneeDeviceProxy.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>

#include <zap-generated/CHIPClientCallbacks.h>
#include <zap-generated/CHIPClusters.h>

using chip::ByteSpan;
using chip::CASESessionManager;
using chip::CASESessionManagerConfig;
using chip::CharSpan;
using chip::DeviceProxy;
using chip::EndpointId;
using chip::FabricIndex;
using chip::FabricInfo;
using chip::NodeId;
using chip::OnDeviceConnected;
using chip::OnDeviceConnectionFailure;
using chip::PeerId;
using chip::Server;
using chip::VendorId;
using chip::bdx::TransferSession;
using chip::Callback::Callback;
using chip::System::Layer;
using chip::Transport::PeerAddress;
// using namespace chip::ArgParser;
using namespace chip;
using namespace chip::app::Clusters;
using namespace chip::bdx;
using namespace chip::Messaging;
using namespace chip::app::Clusters::OtaSoftwareUpdateProvider::Commands;
using chip::Inet::IPAddress;

namespace {
// Global instance of the OTARequestorInterface.
OTARequestorInterface * globalOTARequestorInstance = nullptr;

constexpr uint32_t kImmediateStartDelayMs = 1; // Start the timer with this value when starting OTA "immediately"

// Callbacks for connection management
void OnConnected(void * context, chip::OperationalDeviceProxy * deviceProxy);
Callback<OnDeviceConnected> mOnConnectedCallback(OnConnected, nullptr);
Callback::Callback<OnDeviceConnected> mOnConnectedCallback(OnConnected, nullptr);

void OnConnectionFailure(void * context, NodeId deviceId, CHIP_ERROR error);
Callback<OnDeviceConnectionFailure> mOnConnectionFailureCallback(OnConnectionFailure, nullptr);
Callback::Callback<OnDeviceConnectionFailure> mOnConnectionFailureCallback(OnConnectionFailure, nullptr);

void OnQueryImageResponse(void * context, const QueryImageResponse::DecodableType & response);
void OnQueryImageFailure(void * context, EmberAfStatus status);

void SetRequestorInstance(OTARequestorInterface * instance)
{
globalOTARequestorInstance = instance;
}

OTARequestorInterface * GetRequestorInstance()
{
return globalOTARequestorInstance;
}

void StartDelayTimerHandler(chip::System::Layer * systemLayer, void * appState)
{
VerifyOrReturn(appState != nullptr);
Expand All @@ -95,6 +70,17 @@ void OnQueryImageFailure(void * context, EmberAfStatus status)
ChipLogDetail(SoftwareUpdate, "QueryImage failure response %" PRIu8, status);
}

// Called whenever FindOrEstablishSession is successful. Finds the Requestor instance
// and calls the corresponding OTARequestor member function
void OnConnected(void * context, chip::OperationalDeviceProxy * deviceProxy)
{
OTARequestor * requestorCore = static_cast<OTARequestor *>(GetRequestorInstance());

VerifyOrDie(requestorCore != nullptr);

requestorCore->mOnConnected(context, deviceProxy);
}

void OnConnectionFailure(void * context, NodeId deviceId, CHIP_ERROR error)
{
ChipLogError(SoftwareUpdate, "failed to connect to 0x%" PRIX64 ": %" CHIP_ERROR_FORMAT, deviceId, error.Format());
Expand All @@ -104,10 +90,27 @@ void OnQueryImageResponse(void * context, const QueryImageResponse::DecodableTyp
{
OTARequestor * requestorCore = static_cast<OTARequestor *>(GetRequestorInstance());

assert(requestorCore != nullptr);
VerifyOrDie(requestorCore != nullptr);

requestorCore->mOnQueryImageResponse(context, response);
}
} // namespace

void SetRequestorInstance(OTARequestorInterface * instance)
{
globalOTARequestorInstance = instance;
}

OTARequestorInterface * GetRequestorInstance()
{
return globalOTARequestorInstance;
}

struct OTARequestor::QueryImageRequest
{
char location[2];
QueryImage::Type args;
};

void OTARequestor::mOnQueryImageResponse(void * context, const QueryImageResponse::DecodableType & response)
{
Expand Down Expand Up @@ -297,7 +300,7 @@ void OTARequestor::ConnectToProvider()

// Explicitly calling UpdateDeviceData() should not be needed once OperationalDeviceProxy can resolve IP address from node ID
// and fabric index
PeerAddress addr = PeerAddress::UDP(mIpAddress, CHIP_PORT);
Transport::PeerAddress addr = Transport::PeerAddress::UDP(mIpAddress, CHIP_PORT);
operationalDeviceProxy->UpdateDeviceData(addr, operationalDeviceProxy->GetMRPConfig());

CHIP_ERROR err = operationalDeviceProxy->Connect(&mOnConnectedCallback, &mOnConnectionFailureCallback);
Expand All @@ -307,58 +310,25 @@ void OTARequestor::ConnectToProvider()
}
}

// Called whenever FindOrEstablishSession is successful. Finds the Requestor instance
// and calls the corresponding OTARequestor member function
void OnConnected(void * context, chip::OperationalDeviceProxy * deviceProxy)
{
OTARequestor * requestorCore = static_cast<OTARequestor *>(GetRequestorInstance());

assert(requestorCore != nullptr);

requestorCore->mOnConnected(context, deviceProxy);
}

// Member function called whenever FindOrEstablishSession is successful
void OTARequestor::mOnConnected(void * context, chip::DeviceProxy * deviceProxy)
{
switch (onConnectedState)
{
case kQueryImage: {
CHIP_ERROR err = CHIP_NO_ERROR;
chip::Controller::OtaSoftwareUpdateProviderCluster cluster;
constexpr EndpointId kOtaProviderEndpoint = 0;

// These QueryImage params have been chosen arbitrarily
constexpr VendorId kExampleVendorId = VendorId::Common;
constexpr uint16_t kExampleProductId = 77;
constexpr uint16_t kExampleHWVersion = 3;
constexpr uint16_t kExampleSoftwareVersion = 0;
constexpr EmberAfOTADownloadProtocol kExampleProtocolsSupported[] = { EMBER_ZCL_OTA_DOWNLOAD_PROTOCOL_BDX_SYNCHRONOUS };
const char locationBuf[] = { 'U', 'S' };
CharSpan exampleLocation(locationBuf);
constexpr bool kExampleClientCanConsent = false;
ByteSpan metadata;

err = cluster.Associate(deviceProxy, kOtaProviderEndpoint);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Associate() failed: %" CHIP_ERROR_FORMAT, err.Format());
return;
}
QueryImage::Type args;
args.vendorId = kExampleVendorId;
args.productId = kExampleProductId;
args.softwareVersion = kExampleSoftwareVersion;
args.protocolsSupported = kExampleProtocolsSupported;
args.hardwareVersion.Emplace(kExampleHWVersion);
args.location.Emplace(exampleLocation);
args.requestorCanConsent.Emplace(kExampleClientCanConsent);
args.metadataForProvider.Emplace(metadata);
err = cluster.InvokeCommand(args, /* context = */ nullptr, OnQueryImageResponse, OnQueryImageFailure);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "QueryImage() failed: %" CHIP_ERROR_FORMAT, err.Format());
}
QueryImageRequest request;
CHIP_ERROR err = BuildQueryImageRequest(request);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(SoftwareUpdate, "Failed to build QueryImage command: %" CHIP_ERROR_FORMAT, err.Format()));

Controller::OtaSoftwareUpdateProviderCluster cluster;
cluster.Associate(deviceProxy, kOtaProviderEndpoint);

err = cluster.InvokeCommand(request.args, /* context = */ nullptr, OnQueryImageResponse, OnQueryImageFailure);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(SoftwareUpdate, "Failed to send QueryImage command: %" CHIP_ERROR_FORMAT, err.Format()));

break;
}
Expand Down Expand Up @@ -410,3 +380,37 @@ void OTARequestor::TriggerImmediateQuery()
// Perhaps we don't need a separate function ConnectToProvider, revisit this
ConnectToProvider();
}

CHIP_ERROR OTARequestor::BuildQueryImageRequest(QueryImageRequest & request)
{
constexpr EmberAfOTADownloadProtocol kProtocolsSupported[] = { EMBER_ZCL_OTA_DOWNLOAD_PROTOCOL_BDX_SYNCHRONOUS };
constexpr bool kRequestorCanConsent = false;
QueryImage::Type & args = request.args;

uint16_t vendorId;
VerifyOrReturnError(Basic::Attributes::VendorID::Get(kRootEndpointId, &vendorId) == EMBER_ZCL_STATUS_SUCCESS,
CHIP_ERROR_READ_FAILED);
args.vendorId = static_cast<VendorId>(vendorId);

VerifyOrReturnError(Basic::Attributes::ProductID::Get(kRootEndpointId, &args.productId) == EMBER_ZCL_STATUS_SUCCESS,
CHIP_ERROR_READ_FAILED);

VerifyOrReturnError(Basic::Attributes::SoftwareVersion::Get(kRootEndpointId, &args.softwareVersion) == EMBER_ZCL_STATUS_SUCCESS,
CHIP_ERROR_READ_FAILED);

args.protocolsSupported = kProtocolsSupported;
args.requestorCanConsent.SetValue(kRequestorCanConsent);

uint16_t hardwareVersion;
if (Basic::Attributes::HardwareVersion::Get(kRootEndpointId, &hardwareVersion) == EMBER_ZCL_STATUS_SUCCESS)
{
args.hardwareVersion.SetValue(hardwareVersion);
}

if (Basic::Attributes::Location::Get(kRootEndpointId, MutableCharSpan(request.location)) == EMBER_ZCL_STATUS_SUCCESS)
{
args.location.SetValue(CharSpan(request.location));
}

return CHIP_NO_ERROR;
}
3 changes: 3 additions & 0 deletions src/app/clusters/ota-requestor/OTARequestor.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class OTARequestor : public OTARequestorInterface
kStartBDX,
};

struct QueryImageRequest;

// TODO: the application should define this, along with initializing the BDXDownloader

// This class is purely for delivering messages and sending outgoing messages to/from the BDXDownloader.
Expand Down Expand Up @@ -169,4 +171,5 @@ class OTARequestor : public OTARequestorInterface

// Functions
CHIP_ERROR SetupCASESessionManager(chip::FabricIndex fabricIndex);
CHIP_ERROR BuildQueryImageRequest(QueryImageRequest & req);
};
7 changes: 4 additions & 3 deletions src/lib/core/DataModelTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ typedef uint32_t FieldId;
typedef uint16_t ListIndex;
typedef uint32_t TransactionId;

static constexpr FabricIndex kUndefinedFabricIndex = 0;
static constexpr EndpointId kInvalidEndpointId = 0xFFFF;
static constexpr ListIndex kInvalidListIndex = 0xFFFF; // List index is a uint16 thus 0xFFFF is a invalid list index.
constexpr FabricIndex kUndefinedFabricIndex = 0;
constexpr EndpointId kInvalidEndpointId = 0xFFFF;
constexpr EndpointId kRootEndpointId = 0;
constexpr ListIndex kInvalidListIndex = 0xFFFF; // List index is a uint16 thus 0xFFFF is a invalid list index.

// ClusterId, AttributeId and EventId are MEIs,
// 0xFFFF is not a valid manufacturer code, thus 0xFFFF'FFFF is not a valid MEI.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 1545845

Please sign in to comment.