| /* |
| * |
| * 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 |