blob: d34d7e5789c3cd3e087019662e5b855df4ded53e [file] [log] [blame]
/*
*
* Copyright (c) 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.
*/
#pragma once
#include "attributes_service/attributes_service.rpc.pb.h"
#include "pigweed/rpc_services/internal/StatusUtils.h"
#include <app-common/zap-generated/attribute-type.h>
#include <app/AppConfig.h>
#include <app/InteractionModelEngine.h>
#include <app/MessageDef/AttributeReportIBs.h>
#include <app/util/attribute-storage.h>
#include <app/util/attribute-table.h>
#include <app/util/ember-compatibility-functions.h>
#include <lib/core/TLV.h>
#include <lib/core/TLVTags.h>
#include <lib/core/TLVTypes.h>
#include <platform/PlatformManager.h>
#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE
#include <app/AttributeValueEncoder.h>
#include <app/data-model-provider/ActionReturnStatus.h>
#include <app/data-model-provider/OperationTypes.h>
#include <app/data-model-provider/Provider.h>
#endif
namespace chip {
namespace rpc {
// Implementation class for chip.rpc.Attributes.
class Attributes : public pw_rpc::nanopb::Attributes::Service<Attributes>
{
public:
::pw::Status Write(const chip_rpc_AttributeWrite & request, pw_protobuf_Empty & response)
{
const void * data;
DeviceLayer::StackLock lock;
switch (request.data.which_data)
{
case chip_rpc_AttributeData_data_bool_tag:
data = &request.data.data.data_bool;
break;
case chip_rpc_AttributeData_data_uint8_tag:
data = &request.data.data.data_uint8;
break;
case chip_rpc_AttributeData_data_uint16_tag:
data = &request.data.data.data_uint16;
break;
case chip_rpc_AttributeData_data_uint32_tag:
data = &request.data.data.data_uint32;
break;
case chip_rpc_AttributeData_data_int8_tag:
data = &request.data.data.data_int8;
break;
case chip_rpc_AttributeData_data_int16_tag:
data = &request.data.data.data_int16;
break;
case chip_rpc_AttributeData_data_int32_tag:
data = &request.data.data.data_int32;
break;
case chip_rpc_AttributeData_data_bytes_tag:
data = &request.data.data.data_bytes;
break;
case chip_rpc_AttributeData_data_single_tag:
data = &request.data.data.data_single;
break;
default:
return pw::Status::InvalidArgument();
}
RETURN_STATUS_IF_NOT_OK(
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)
{
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:
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:
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:
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:
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_INT32U_ATTRIBUTE_TYPE:
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_INT8S_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_SignedInteger, response.data.data_int8));
response.which_data = chip_rpc_AttributeData_data_int8_tag;
break;
case chip_rpc_AttributeType_ZCL_INT16S_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_SignedInteger, response.data.data_int16));
response.which_data = chip_rpc_AttributeData_data_int16_tag;
break;
case chip_rpc_AttributeType_ZCL_INT32S_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_SignedInteger, response.data.data_int32));
response.which_data = chip_rpc_AttributeData_data_int32_tag;
break;
case chip_rpc_AttributeType_ZCL_SINGLE_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_FloatingPointNumber, response.data.data_single));
response.which_data = chip_rpc_AttributeData_data_single_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:
case chip_rpc_AttributeType_ZCL_INT64S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_DOUBLE_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_OCTET_STRING_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_CHAR_STRING_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_STRUCT_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_TOD_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_DATE_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_UTC_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_EPOCH_US_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_EPOCH_S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_SYSTIME_US_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_PERCENT_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_PERCENT100THS_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_CLUSTER_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_ATTRIB_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_FIELD_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_EVENT_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_COMMAND_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_ACTION_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_TRANS_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_NODE_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_VENDOR_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_FABRIC_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_GROUP_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_STATUS_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_DATA_VER_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_EVENT_NO_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_FABRIC_IDX_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPV4ADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPV6ADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPV6PRE_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_HWADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_NO_DATA_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_UNKNOWN_ATTRIBUTE_TYPE:
default:
break;
// These are currently not returned as decoded data, but can be
// decoded from the returned TLV data.
}
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)));
#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE
// TODO: this assumes a singleton data model provider
app::DataModel::Provider * provider = app::InteractionModelEngine::GetInstance()->GetDataModelProvider();
app::DataModel::ReadAttributeRequest request;
request.path = path;
request.operationFlags.Set(app::DataModel::OperationFlags::kInternal);
request.subjectDescriptor = &subjectDescriptor;
std::optional<app::DataModel::ClusterInfo> info = provider->GetClusterInfo(path);
if (!info.has_value())
{
return ::pw::Status::NotFound();
}
app::AttributeValueEncoder encoder(attributeReports, subjectDescriptor, path, info->dataVersion,
false /* isFabricFiltered */, nullptr /* attributeEncodingState */);
app::DataModel::ActionReturnStatus result = provider->ReadAttribute(request, encoder);
if (!result.IsSuccess())
{
app::DataModel::ActionReturnStatus::StringStorage storage;
ChipLogError(Support, "Failed to read data: %s", result.c_str(storage));
return ::pw::Status::Internal();
}
#else
PW_TRY(ChipErrorToPwStatus(app::ReadSingleClusterData(subjectDescriptor, false, path, attributeReports, nullptr)));
#endif
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
} // namespace chip