Skip to content

Commit

Permalink
RPC: Use ReadSingleClusterData for Attribute Service (#19772)
Browse files Browse the repository at this point in the history
Switch from ember APIs to ReadSingleClusterData to support
attributes which are not in the attribute store.

Add returning the TLV data as part of the AttributeData which
can be used to get more complex data types.
  • Loading branch information
rgoliver authored and pull[bot] committed Jan 8, 2024
1 parent 346dc21 commit 4700361
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 47 deletions.
3 changes: 2 additions & 1 deletion examples/common/pigweed/protos/attributes_service.options
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@

chip.rpc.AttributeData.data_bytes max_size:128
chip.rpc.AttributeData.data_bytes max_size:128
chip.rpc.AttributeData.tlv_data max_size:256
1 change: 1 addition & 0 deletions examples/common/pigweed/protos/attributes_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ message AttributeData {
int32 data_int16 = 7;
int32 data_int32 = 8;
};
optional bytes tlv_data = 9;
}

message AttributeWrite {
Expand Down
163 changes: 117 additions & 46 deletions examples/common/pigweed/rpc_services/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
#include <app-common/zap-generated/attribute-id.h>
#include <app-common/zap-generated/attribute-type.h>
#include <app-common/zap-generated/cluster-id.h>
#include <app/InteractionModelEngine.h>
#include <app/MessageDef/AttributeReportIBs.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/CHIPTLVTags.h>
#include <lib/core/CHIPTLVTypes.h>
#include <platform/PlatformManager.h>

namespace chip {
Expand Down Expand Up @@ -68,70 +73,68 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service<Attributes>
return pw::Status::InvalidArgument();
}
RETURN_STATUS_IF_NOT_OK(
emberAfWriteServerAttribute(request.metadata.endpoint, request.metadata.cluster, request.metadata.attribute_id,
const_cast<uint8_t *>(static_cast<const uint8_t *>(data)), request.metadata.type));
emberAfWriteAttribute(request.metadata.endpoint, request.metadata.cluster, request.metadata.attribute_id,
const_cast<uint8_t *>(static_cast<const uint8_t *>(data)), request.metadata.type));
return pw::OkStatus();
}

::pw::Status Read(const chip_rpc_AttributeMetadata & request, chip_rpc_AttributeData & response)
{
void * data;
size_t size = 0;
DeviceLayer::StackLock lock;
app::ConcreteAttributePath path(request.endpoint, request.cluster, request.attribute_id);
MutableByteSpan tlvBuffer(response.tlv_data.bytes);
PW_TRY(ReadAttributeIntoTlvBuffer(path, tlvBuffer));
response.tlv_data.size = tlvBuffer.size();
response.has_tlv_data = true;

switch (request.type)
{
case chip_rpc_AttributeType_ZCL_BOOLEAN_ATTRIBUTE_TYPE:
data = &response.data.data_bool;
size = sizeof(response.data.data_bool);
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_Boolean, response.data.data_bool));
response.which_data = chip_rpc_AttributeData_data_bool_tag;
break;
case chip_rpc_AttributeType_ZCL_ENUM8_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_BITMAP8_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT8U_ATTRIBUTE_TYPE:
data = &response.data.data_uint8;
size = sizeof(response.data.data_uint8);
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint8));
response.which_data = chip_rpc_AttributeData_data_uint8_tag;
break;
case chip_rpc_AttributeType_ZCL_ENUM16_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_BITMAP16_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint16));
response.which_data = chip_rpc_AttributeData_data_uint16_tag;
break;
case chip_rpc_AttributeType_ZCL_INT8U_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint8));
response.which_data = chip_rpc_AttributeData_data_uint8_tag;
break;
case chip_rpc_AttributeType_ZCL_INT16U_ATTRIBUTE_TYPE:
data = &response.data.data_uint16;
size = sizeof(response.data.data_uint16);
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint16));
response.which_data = chip_rpc_AttributeData_data_uint16_tag;
break;
case chip_rpc_AttributeType_ZCL_BITMAP32_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT32U_ATTRIBUTE_TYPE:
data = &response.data.data_uint32;
size = sizeof(response.data.data_uint32);
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint32));
response.which_data = chip_rpc_AttributeData_data_uint32_tag;
break;
case chip_rpc_AttributeType_ZCL_ARRAY_ATTRIBUTE_TYPE:
// We don't know how to read these; need to get the right
// AttributeAccessInterface.
return pw::Status::InvalidArgument();
case chip_rpc_AttributeType_ZCL_BITMAP64_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT24U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT40U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT48U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT56U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT64U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT8S_ATTRIBUTE_TYPE:
data = &response.data.data_int8;
size = sizeof(response.data.data_int8);
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_int8));
response.which_data = chip_rpc_AttributeData_data_int8_tag;
break;
case chip_rpc_AttributeType_ZCL_INT16S_ATTRIBUTE_TYPE:
data = &response.data.data_int16;
size = sizeof(response.data.data_int16);
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_int16));
response.which_data = chip_rpc_AttributeData_data_int16_tag;
break;
case chip_rpc_AttributeType_ZCL_INT24S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT32S_ATTRIBUTE_TYPE:
data = &response.data.data_int32;
size = sizeof(response.data.data_int32);
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_int32));
response.which_data = chip_rpc_AttributeData_data_int32_tag;
break;
case chip_rpc_AttributeType_ZCL_BITMAP8_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_BITMAP16_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_BITMAP32_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_ARRAY_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_BITMAP64_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT24U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT40U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT48U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT56U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT64U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT24S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT40S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT48S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT56S_ATTRIBUTE_TYPE:
Expand Down Expand Up @@ -173,24 +176,92 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service<Attributes>
case chip_rpc_AttributeType_ZCL_IPV6ADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPV6PRE_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_HWADR_ATTRIBUTE_TYPE:
data = response.data.data_bytes.bytes;
size = emberAfGetDataSize(request.type);
if (size > sizeof(response.data.data_bytes.bytes))
{
return pw::Status::OutOfRange();
}
response.data.data_bytes.size = size;
response.which_data = chip_rpc_AttributeData_data_bytes_tag;
break;
case chip_rpc_AttributeType_ZCL_NO_DATA_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_UNKNOWN_ATTRIBUTE_TYPE:
default:
return pw::Status::InvalidArgument();
break;
// These are currently not returned as decoded data, but can be
// decoded from the returned TLV data.
}
RETURN_STATUS_IF_NOT_OK(emberAfReadServerAttribute(request.endpoint, request.cluster, request.attribute_id,
static_cast<uint8_t *>(data), size));

return pw::OkStatus();
}

private:
static constexpr uint8_t kReportContextTag = 0x01;

::pw::Status ReadAttributeIntoTlvBuffer(const app::ConcreteAttributePath & path, MutableByteSpan & tlvBuffer)
{
Access::SubjectDescriptor subjectDescriptor{ .authMode = chip::Access::AuthMode::kPase };
app::AttributeReportIBs::Builder attributeReports;
TLV::TLVWriter writer;
TLV::TLVType outer;
DeviceLayer::StackLock lock;

writer.Init(tlvBuffer);
PW_TRY(ChipErrorToPwStatus(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer)));
PW_TRY(ChipErrorToPwStatus(attributeReports.Init(&writer, kReportContextTag)));
PW_TRY(ChipErrorToPwStatus(app::ReadSingleClusterData(subjectDescriptor, false, path, attributeReports, nullptr)));
attributeReports.EndOfContainer();
PW_TRY(ChipErrorToPwStatus(writer.EndContainer(outer)));
PW_TRY(ChipErrorToPwStatus(writer.Finalize()));
tlvBuffer.reduce_size(writer.GetLengthWritten());

return ::pw::OkStatus();
}

template <typename T>
::pw::Status TlvBufferGetData(ByteSpan tlvBuffer, TLV::TLVType expectedDataType, T & responseData)
{
TLV::TLVReader reader;
reader.Init(tlvBuffer);
// Open outer container
PW_TRY(ChipErrorToPwStatus(reader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag())));
TLV::TLVType readerRoot;
PW_TRY(ChipErrorToPwStatus(reader.EnterContainer(readerRoot)));

// Open report container
PW_TRY(ChipErrorToPwStatus(reader.Next(TLV::TLVType::kTLVType_Array, TLV::ContextTag(kReportContextTag))));
TLV::TLVType readerArray;
PW_TRY(ChipErrorToPwStatus(reader.EnterContainer(readerArray)));

// Skip first array element which is the empty array from spec 10.5.4.3
PW_TRY(ChipErrorToPwStatus(reader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag())));

// Parse the AttributeDataIB to pull out data
app::AttributeReportIB::Parser reportParser;
PW_TRY(ChipErrorToPwStatus(reportParser.Init(reader)));
app::AttributeDataIB::Parser dataParser;
PW_TRY(ChipErrorToPwStatus(reportParser.GetAttributeData(&dataParser)));
TLV::TLVReader dataReader;
PW_TRY(ChipErrorToPwStatus(dataParser.GetData(&dataReader)));
PW_TRY(CheckTlvTagAndType(&dataReader, TLV::ContextTag(0x2), expectedDataType));
PW_TRY(ChipErrorToPwStatus(dataReader.Get(responseData)));

return ::pw::OkStatus();
}

static ::pw::Status ChipErrorToPwStatus(CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
return ::pw::OkStatus();
}
else if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
{
return ::pw::Status::ResourceExhausted();
}
return ::pw::Status::Internal();
}

static ::pw::Status CheckTlvTagAndType(TLV::TLVReader * reader, TLV::Tag expectedTag, TLV::TLVType expectedType)
{
if (reader->GetTag() != expectedTag || reader->GetType() != expectedType)
{
return ::pw::Status::NotFound();
}
return ::pw::OkStatus();
}
};

} // namespace rpc
Expand Down

0 comments on commit 4700361

Please sign in to comment.