| /* |
| * Copyright (c) 2024 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. |
| */ |
| #include <app/codegen-data-model-provider/EmberAttributeDataBuffer.h> |
| |
| #include <app-common/zap-generated/attribute-type.h> |
| #include <app/AttributeValueEncoder.h> |
| #include <app/util/attribute-metadata.h> |
| #include <app/util/attribute-storage-null-handling.h> |
| #include <app/util/odd-sized-integers.h> |
| #include <lib/core/CHIPError.h> |
| #include <lib/core/TLVTags.h> |
| #include <lib/core/TLVTypes.h> |
| #include <lib/core/TLVWriter.h> |
| #include <lib/support/CodeUtils.h> |
| #include <protocols/interaction_model/Constants.h> |
| #include <protocols/interaction_model/StatusCode.h> |
| |
| #include <cstdint> |
| #include <limits> |
| |
| namespace chip { |
| namespace app { |
| namespace Ember { |
| |
| namespace { |
| |
| /// Maximum length of a string, inclusive |
| /// |
| /// the max size value (0xFF and 0xFFFF) is reserved for NULL representation so |
| /// it is not available |
| constexpr uint32_t MaxLength(EmberAttributeDataBuffer::PascalStringType s) |
| { |
| if (s == EmberAttributeDataBuffer::PascalStringType::kShort) |
| { |
| return std::numeric_limits<uint8_t>::max() - 1; |
| } |
| // EmberAttributeDataBuffer::PascalStringType::kLong: |
| return std::numeric_limits<uint16_t>::max() - 1; |
| } |
| |
| constexpr unsigned GetByteCountOfIntegerType(EmberAfAttributeType type) |
| { |
| // This TERRIBLE bit fiddling, however it is small in flash |
| // and we assert statically the actual values we care about |
| |
| // ZCL_INT8U_ATTRIBUTE_TYPE = 0x20, // Unsigned 8-bit integer |
| // ZCL_INT16U_ATTRIBUTE_TYPE = 0x21, // Unsigned 16-bit integer |
| // ZCL_INT24U_ATTRIBUTE_TYPE = 0x22, // Unsigned 24-bit integer |
| // ZCL_INT32U_ATTRIBUTE_TYPE = 0x23, // Unsigned 32-bit integer |
| // ZCL_INT40U_ATTRIBUTE_TYPE = 0x24, // Unsigned 40-bit integer |
| // ZCL_INT48U_ATTRIBUTE_TYPE = 0x25, // Unsigned 48-bit integer |
| // ZCL_INT56U_ATTRIBUTE_TYPE = 0x26, // Unsigned 56-bit integer |
| // ZCL_INT64U_ATTRIBUTE_TYPE = 0x27, // Unsigned 64-bit integer |
| // |
| // ZCL_INT8S_ATTRIBUTE_TYPE = 0x28, // Signed 8-bit integer |
| // ZCL_INT16S_ATTRIBUTE_TYPE = 0x29, // Signed 16-bit integer |
| // ZCL_INT24S_ATTRIBUTE_TYPE = 0x2A, // Signed 24-bit integer |
| // ZCL_INT32S_ATTRIBUTE_TYPE = 0x2B, // Signed 32-bit integer |
| // ZCL_INT40S_ATTRIBUTE_TYPE = 0x2C, // Signed 40-bit integer |
| // ZCL_INT48S_ATTRIBUTE_TYPE = 0x2D, // Signed 48-bit integer |
| // ZCL_INT56S_ATTRIBUTE_TYPE = 0x2E, // Signed 56-bit integer |
| // ZCL_INT64S_ATTRIBUTE_TYPE = 0x2F, // Signed 64-bit integer |
| |
| return (static_cast<unsigned>(type) % 8) + 1; |
| } |
| static_assert(GetByteCountOfIntegerType(ZCL_INT8U_ATTRIBUTE_TYPE) == 1); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT8S_ATTRIBUTE_TYPE) == 1); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT16U_ATTRIBUTE_TYPE) == 2); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT16S_ATTRIBUTE_TYPE) == 2); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT24U_ATTRIBUTE_TYPE) == 3); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT24S_ATTRIBUTE_TYPE) == 3); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT32U_ATTRIBUTE_TYPE) == 4); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT32S_ATTRIBUTE_TYPE) == 4); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT40U_ATTRIBUTE_TYPE) == 5); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT40S_ATTRIBUTE_TYPE) == 5); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT48U_ATTRIBUTE_TYPE) == 6); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT48S_ATTRIBUTE_TYPE) == 6); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT56U_ATTRIBUTE_TYPE) == 7); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT56S_ATTRIBUTE_TYPE) == 7); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT64U_ATTRIBUTE_TYPE) == 8); |
| static_assert(GetByteCountOfIntegerType(ZCL_INT64S_ATTRIBUTE_TYPE) == 8); |
| |
| /// Encodes the string of type stringType pointed to by `reader` into the TLV `writer`. |
| /// Then encoded string will be at tag `tag` and of type `tlvType` |
| CHIP_ERROR EncodeString(EmberAttributeDataBuffer::PascalStringType stringType, TLV::TLVType tlvType, TLV::TLVWriter & writer, |
| TLV::Tag tag, EmberAttributeDataBuffer::EndianReader & reader, bool nullable) |
| { |
| unsigned stringLen; |
| if (stringType == EmberAttributeDataBuffer::PascalStringType::kShort) |
| { |
| uint8_t len; |
| if (!reader.Read8(&len).IsSuccess()) |
| { |
| return reader.StatusCode(); |
| } |
| if (len == NumericAttributeTraits<uint8_t>::kNullValue) |
| { |
| VerifyOrReturnError(nullable, CHIP_ERROR_INVALID_ARGUMENT); |
| return writer.PutNull(tag); |
| } |
| stringLen = len; |
| } |
| else |
| { |
| uint16_t len; |
| if (!reader.Read16(&len).IsSuccess()) |
| { |
| return reader.StatusCode(); |
| } |
| if (len == NumericAttributeTraits<uint16_t>::kNullValue) |
| { |
| VerifyOrReturnError(nullable, CHIP_ERROR_INVALID_ARGUMENT); |
| return writer.PutNull(tag); |
| } |
| stringLen = len; |
| } |
| |
| const uint8_t * data; |
| if (!reader.ZeroCopyProcessBytes(stringLen, &data).IsSuccess()) |
| { |
| return reader.StatusCode(); |
| } |
| |
| if (tlvType == TLV::kTLVType_UTF8String) |
| { |
| return writer.PutString(tag, reinterpret_cast<const char *>(data), stringLen); |
| } |
| |
| return writer.PutBytes(tag, data, stringLen); |
| } |
| |
| } // namespace |
| |
| CHIP_ERROR EmberAttributeDataBuffer::DecodeUnsignedInteger(chip::TLV::TLVReader & reader, EndianWriter & writer) |
| { |
| const unsigned byteCount = GetByteCountOfIntegerType(mAttributeType); |
| const uint64_t maxValue = NumericLimits::MaxUnsignedValue(byteCount); |
| |
| // Any size of integer can be read by TLV getting 64-bit integers |
| uint64_t value; |
| |
| if (reader.GetType() == TLV::kTLVType_Null) |
| { |
| // we know mIsNullable due to the check at the top of ::Decode |
| value = NumericLimits::UnsignedMaxValueToNullValue(maxValue); |
| } |
| else |
| { |
| ReturnErrorOnFailure(reader.Get(value)); |
| |
| bool valid = |
| // Value is in [0, max] RANGE |
| (value <= maxValue) |
| // Nullable values reserve a specific value to mean NULL |
| && !(mIsNullable && (value == NumericLimits::UnsignedMaxValueToNullValue(maxValue))); |
| |
| VerifyOrReturnError(valid, CHIP_IM_GLOBAL_STATUS(ConstraintError)); |
| } |
| |
| writer.EndianPut(value, byteCount); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR EmberAttributeDataBuffer::DecodeSignedInteger(chip::TLV::TLVReader & reader, EndianWriter & writer) |
| { |
| const unsigned byteCount = GetByteCountOfIntegerType(mAttributeType); |
| const int64_t minValue = NumericLimits::MinSignedValue(byteCount); |
| const int64_t maxValue = NumericLimits::MaxSignedValue(byteCount); |
| |
| // Any size of integer can be read by TLV getting 64-bit integers |
| int64_t value; |
| |
| if (reader.GetType() == TLV::kTLVType_Null) |
| { |
| // we know mIsNullable due to the check at the top of ::Decode |
| value = NumericLimits::SignedMinValueToNullValue(minValue); |
| } |
| else |
| { |
| ReturnErrorOnFailure(reader.Get(value)); |
| |
| bool valid = |
| // Value is in [min, max] RANGE |
| ((value >= minValue) && (value <= maxValue)) |
| // Nullable values reserve a specific value to mean NULL |
| && !(mIsNullable && (value == NumericLimits::SignedMinValueToNullValue(minValue))); |
| |
| VerifyOrReturnError(valid, CHIP_IM_GLOBAL_STATUS(ConstraintError)); |
| } |
| writer.EndianPutSigned(value, byteCount); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR EmberAttributeDataBuffer::DecodeAsString(chip::TLV::TLVReader & reader, PascalStringType stringType, |
| TLV::TLVType tlvType, EndianWriter & writer) |
| { |
| // Handle null first, then the actual data |
| if (reader.GetType() == TLV::kTLVType_Null) |
| { |
| // we know mIsNullable due to the check at the top of ::Decode |
| switch (stringType) |
| { |
| case PascalStringType::kShort: |
| writer.Put8(NumericAttributeTraits<uint8_t>::kNullValue); |
| break; |
| case PascalStringType::kLong: |
| writer.Put16(NumericAttributeTraits<uint16_t>::kNullValue); |
| break; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| const uint32_t stringLength = reader.GetLength(); |
| |
| VerifyOrReturnError(reader.GetType() == tlvType, CHIP_ERROR_WRONG_TLV_TYPE); |
| VerifyOrReturnError(stringLength <= MaxLength(stringType), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // Size is a prefix, where 0xFF/0xFFFF is the null marker (if applicable) |
| switch (stringType) |
| { |
| case PascalStringType::kShort: |
| writer.Put8(static_cast<uint8_t>(stringLength)); |
| break; |
| case PascalStringType::kLong: |
| writer.Put16(static_cast<uint16_t>(stringLength)); |
| break; |
| } |
| |
| // data copy |
| const uint8_t * tlvData; |
| ReturnErrorOnFailure(reader.GetDataPtr(tlvData)); |
| writer.Put(tlvData, stringLength); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR EmberAttributeDataBuffer::Decode(chip::TLV::TLVReader & reader) |
| { |
| // all methods below assume that nullable setting matches (this is to reduce code size |
| // even though clarity suffers) |
| VerifyOrReturnError(mIsNullable || reader.GetType() != TLV::kTLVType_Null, CHIP_ERROR_WRONG_TLV_TYPE); |
| |
| EndianWriter endianWriter(mDataBuffer.data(), mDataBuffer.size()); |
| |
| switch (mAttributeType) |
| { |
| case ZCL_BOOLEAN_ATTRIBUTE_TYPE: // Boolean |
| // Boolean values: |
| // 0x00 is FALSE |
| // 0x01 is TRUE |
| // 0xFF is NULL |
| if (reader.GetType() == TLV::kTLVType_Null) |
| { |
| // we know mIsNullable due to the check at the top of ::Decode |
| endianWriter.Put8(NumericAttributeTraits<bool>::kNullValue); |
| } |
| else |
| { |
| bool value; |
| ReturnErrorOnFailure(reader.Get(value)); |
| endianWriter.Put8(value ? 1 : 0); |
| } |
| break; |
| case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer |
| case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer |
| case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer |
| case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer |
| case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer |
| case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer |
| case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer |
| case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer |
| ReturnErrorOnFailure(DecodeUnsignedInteger(reader, endianWriter)); |
| break; |
| case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer |
| case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer |
| case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer |
| case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer |
| case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer |
| case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer |
| case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer |
| case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer |
| ReturnErrorOnFailure(DecodeSignedInteger(reader, endianWriter)); |
| break; |
| case ZCL_SINGLE_ATTRIBUTE_TYPE: { // 32-bit float |
| float value; |
| if (reader.GetType() == TLV::kTLVType_Null) |
| { |
| // we know mIsNullable due to the check at the top of ::Decode |
| NumericAttributeTraits<float>::SetNull(value); |
| } |
| else |
| { |
| |
| ReturnErrorOnFailure(reader.Get(value)); |
| } |
| endianWriter.Put(&value, sizeof(value)); |
| break; |
| } |
| case ZCL_DOUBLE_ATTRIBUTE_TYPE: { // 64-bit float |
| double value; |
| if (reader.GetType() == TLV::kTLVType_Null) |
| { |
| // we know mIsNullable due to the check at the top of ::Decode |
| NumericAttributeTraits<double>::SetNull(value); |
| } |
| else |
| { |
| ReturnErrorOnFailure(reader.Get(value)); |
| } |
| endianWriter.Put(&value, sizeof(value)); |
| break; |
| } |
| case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string |
| ReturnErrorOnFailure(DecodeAsString(reader, PascalStringType::kShort, TLV::kTLVType_UTF8String, endianWriter)); |
| break; |
| case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: |
| ReturnErrorOnFailure(DecodeAsString(reader, PascalStringType::kLong, TLV::kTLVType_UTF8String, endianWriter)); |
| break; |
| case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string |
| ReturnErrorOnFailure(DecodeAsString(reader, PascalStringType::kShort, TLV::kTLVType_ByteString, endianWriter)); |
| break; |
| case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: |
| ReturnErrorOnFailure(DecodeAsString(reader, PascalStringType::kLong, TLV::kTLVType_ByteString, endianWriter)); |
| break; |
| default: |
| ChipLogError(DataManagement, "Attribute type 0x%x not handled", mAttributeType); |
| return CHIP_IM_GLOBAL_STATUS(Failure); |
| } |
| |
| size_t written; |
| if (!endianWriter.Fit(written)) |
| { |
| return CHIP_ERROR_NO_MEMORY; |
| } |
| |
| mDataBuffer.reduce_size(written); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR EmberAttributeDataBuffer::EncodeInteger(chip::TLV::TLVWriter & writer, TLV::Tag tag, EndianReader & reader) const |
| { |
| // Encodes an integer by first reading as raw bytes and then |
| // bitshift-convert |
| // |
| // This optimizes code size rather than readability at this point. |
| |
| uint8_t raw_bytes[8]; |
| |
| const bool isSigned = IsSignedAttributeType(mAttributeType); |
| const unsigned byteCount = GetByteCountOfIntegerType(mAttributeType); |
| |
| const uint64_t nullValueAsU64 = isSigned |
| ? static_cast<uint64_t>(NumericLimits::SignedMinValueToNullValue(NumericLimits::MinSignedValue(byteCount))) |
| : NumericLimits::UnsignedMaxValueToNullValue(NumericLimits::MaxUnsignedValue(byteCount)); |
| |
| VerifyOrDie(sizeof(raw_bytes) >= byteCount); |
| if (!reader.ReadBytes(raw_bytes, byteCount).IsSuccess()) |
| { |
| return reader.StatusCode(); |
| } |
| |
| // At this point, RAW_VALUE contains the actual value, need to make it "real" |
| union |
| { |
| int64_t int_value; |
| uint64_t uint_value; |
| } value; |
| |
| value.uint_value = 0; |
| |
| #if CHIP_CONFIG_BIG_ENDIAN_TARGET |
| bool isNegative = isSigned && (raw_bytes[0] >= 0x80); |
| if (isNegative) |
| { |
| value.int_value = -1; |
| } |
| for (int i = 0; i < static_cast<int>(byteCount); i++) |
| { |
| #else |
| bool isNegative = isSigned && (raw_bytes[byteCount - 1] >= 0x80); |
| if (isNegative) |
| { |
| value.int_value = -1; |
| } |
| for (int i = static_cast<int>(byteCount) - 1; i >= 0; i--) |
| { |
| #endif |
| value.uint_value <<= 8; |
| value.uint_value = (value.uint_value & ~0xFFULL) | raw_bytes[i]; |
| } |
| |
| // We place the null value as either int_value or uint_value into a union that is |
| // bit-formatted as both int64 and uint64. When we define the nullValue, |
| // it is bitcast into u64 hence this comparison. This is ugly, however this |
| // code prioritizes code size over readability here. |
| if (mIsNullable && (value.uint_value == nullValueAsU64)) |
| { |
| return writer.PutNull(tag); |
| } |
| |
| if (isSigned) |
| { |
| return writer.Put(tag, value.int_value); |
| } |
| |
| return writer.Put(tag, value.uint_value); |
| } |
| |
| CHIP_ERROR EmberAttributeDataBuffer::Encode(chip::TLV::TLVWriter & writer, TLV::Tag tag) const |
| { |
| EndianReader endianReader(mDataBuffer.data(), mDataBuffer.size()); |
| |
| switch (mAttributeType) |
| { |
| case ZCL_NO_DATA_ATTRIBUTE_TYPE: // No data |
| return writer.PutNull(tag); |
| case ZCL_BOOLEAN_ATTRIBUTE_TYPE: { // Boolean |
| uint8_t value; |
| if (!endianReader.Read8(&value).IsSuccess()) |
| { |
| return endianReader.StatusCode(); |
| } |
| switch (value) |
| { |
| case 0: |
| case 1: |
| return writer.PutBoolean(tag, value != 0); |
| case 0xFF: |
| VerifyOrReturnError(mIsNullable, CHIP_ERROR_INVALID_ARGUMENT); |
| return writer.PutNull(tag); |
| default: |
| // Unknown types |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| } |
| case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer |
| case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer |
| case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer |
| case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer |
| case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer |
| case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer |
| case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer |
| case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer |
| case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer |
| case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer |
| case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer |
| case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer |
| case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer |
| case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer |
| case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer |
| case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer |
| return EncodeInteger(writer, tag, endianReader); |
| case ZCL_SINGLE_ATTRIBUTE_TYPE: { // 32-bit float |
| union |
| { |
| uint8_t raw[sizeof(float)]; |
| float value; |
| } value; |
| |
| if (!endianReader.ReadBytes(value.raw, sizeof(value)).IsSuccess()) |
| { |
| return endianReader.StatusCode(); |
| } |
| if (mIsNullable && NumericAttributeTraits<float>::IsNullValue(value.value)) |
| { |
| return writer.PutNull(tag); |
| } |
| return writer.Put(tag, value.value); |
| } |
| case ZCL_DOUBLE_ATTRIBUTE_TYPE: { // 64-bit float |
| union |
| { |
| uint8_t raw[sizeof(double)]; |
| double value; |
| } value; |
| |
| if (!endianReader.ReadBytes(value.raw, sizeof(value)).IsSuccess()) |
| { |
| return endianReader.StatusCode(); |
| } |
| if (mIsNullable && NumericAttributeTraits<double>::IsNullValue(value.value)) |
| { |
| return writer.PutNull(tag); |
| } |
| return writer.Put(tag, value.value); |
| } |
| |
| case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string |
| return EncodeString(PascalStringType::kShort, TLV::kTLVType_UTF8String, writer, tag, endianReader, mIsNullable); |
| case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: |
| return EncodeString(PascalStringType::kLong, TLV::kTLVType_UTF8String, writer, tag, endianReader, mIsNullable); |
| case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string |
| return EncodeString(PascalStringType::kShort, TLV::kTLVType_ByteString, writer, tag, endianReader, mIsNullable); |
| case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: |
| return EncodeString(PascalStringType::kLong, TLV::kTLVType_ByteString, writer, tag, endianReader, mIsNullable); |
| default: |
| ChipLogError(DataManagement, "Attribute type 0x%x not handled", static_cast<int>(mAttributeType)); |
| return CHIP_IM_GLOBAL_STATUS(Failure); |
| } |
| } |
| |
| } // namespace Ember |
| } // namespace app |
| } // namespace chip |