Skip to content

Commit fec6c7e

Browse files
andy31415andreilitvintcarmelveilleuxbzbarsky-apple
authored
Implement the codegen-data-model Write support (#34139)
* Initial copy/merge of the codegendatamodel::write support * Restyle * Remove the error translation for ACL checks for attribute writes * Comment correction after special access error code guarantees were removed * Set the namespace for DataModel to resolve nameclash for android builds * Restyle * Some changes to make darwin builds happy * Do not clang-tidy on CodegenDataModel_Write * Update src/app/codegen-data-model/CodegenDataModel_Write.cpp Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com> * Update src/app/codegen-data-model/CodegenDataModel_Write.cpp Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com> * Update src/app/codegen-data-model/CodegenDataModel_Write.cpp Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com> * Use little endian encoding for pascal long strings, since this is what ember-strings uses * Restyle * Fix code to compile and pass tests * Code review comments * Comment update * Update based on code review feedback * Wrong condition. Fixed * Return invalid value to match ember-compatibility-functions * switch invalid data point to constraint error for return codes * Fix code review comments: comments and return unsupportedaccess * Remove useless comment - error check should be clear enough * Comment update * Re-arrange code for read only and timed * Re-format the read only checks a bit * Use CHIP_ERROR_NOT_FOUND * Separate out variable names * Slight updated code layout * Updated return value for chip error * Updated test to verifyordie instead of just logging errors * Update src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com> * Update based on review feedback * Fix endianess and copying in test code * Restyle * Updated comment * Update src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com> * Add unit test for "lowest signed value write" * Restyle * A constraint error update and better tests for AAI returning errors * One more test for invalid ember usage * Restyle * more tests for more coverage * Comment update * Fix comment * One more test for more coverage * Also cover writing non-null value to nullable attribute * Fix the ember string usage * Update src/app/codegen-data-model/CodegenDataModel_Read.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Update src/app/codegen-data-model/CodegenDataModel_Read.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Remove duplicate code * Update src/app/codegen-data-model/CodegenDataModel_Write.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Remove chip::app:: prefix in unit test since we have a top level using * Fix copy & paste encode to decode * Replace decoded with converted * Update src/app/codegen-data-model/CodegenDataModel_Write.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Start using Failure for invalid data type instead of unsupported read. I do not expect this code path to actually be hit much * Fix comments * Updated encode/decode comment * Use failure instead of constraint error * Update src/app/codegen-data-model/CodegenDataModel_Write.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Update src/app/codegen-data-model/CodegenDataModel_Write.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Update src/app/codegen-data-model/CodegenDataModel_Write.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Use dataversion mismatch for write without a version * Add extra IsGlobalAttribute check * use external writes for the ember write logic, so that we have extra size and validations * Updated comments * more comments * Restyle * Use emberAfWriteAttribute * Add comment about ember-string * Restyle * Add context to unit tests, make write do the marking of dirty paths * Add some unit tests for dirty path handling * Move the change callback around a bit * Restyle * Fixed unit tests to support size checks * Add unit test for invalid data * Restyle * Fix linter errors * Update to make size enforcement and guarantees clearer * use size_t for getlength sizes * Review comments and updated code to compile for android * make datamodel unambiguous * More fixes for clang compilation for DataModel scoping * Restyle * Try to make darwin compiler happy ... ssize_t vs size_t * Fix typo * Restyle * Code review updates * Undo submodule update --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com> Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com> Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent e8bc7e7 commit fec6c7e

18 files changed

+1547
-125
lines changed

.github/workflows/build.yaml

+6-1
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,13 @@ jobs:
204204
run: |
205205
./scripts/run_in_build_env.sh "./scripts/run_codegen_targets.sh out/sanitizers"
206206
- name: Clang-tidy validation
207+
# NOTE: clang-tidy crashes on CodegenDataModel_Write due to Nullable/std::optional check.
208+
# See https://github.com/llvm/llvm-project/issues/97426
207209
run: |
208210
./scripts/run_in_build_env.sh \
209211
"./scripts/run-clang-tidy-on-compile-commands.py \
210212
--compile-database out/sanitizers/compile_commands.json \
211-
--file-exclude-regex '/(repo|zzz_generated|lwip/standalone)/|-ReadImpl|-InvokeSubscribeImpl' \
213+
--file-exclude-regex '/(repo|zzz_generated|lwip/standalone)/|-ReadImpl|-InvokeSubscribeImpl|CodegenDataModel_Write' \
212214
check \
213215
"
214216
- name: Clean output
@@ -422,10 +424,13 @@ jobs:
422424
run: |
423425
./scripts/run_in_build_env.sh "./scripts/run_codegen_targets.sh out/default"
424426
- name: Clang-tidy validation
427+
# NOTE: clang-tidy crashes on CodegenDataModel_Write due to Nullable/std::optional check.
428+
# See https://github.com/llvm/llvm-project/issues/97426
425429
run: |
426430
./scripts/run_in_build_env.sh \
427431
"./scripts/run-clang-tidy-on-compile-commands.py \
428432
--compile-database out/default/compile_commands.json \
433+
--file-exclude-regex '/(repo|zzz_generated|lwip/standalone)/|CodegenDataModel_Write' \
429434
check \
430435
"
431436
- name: Uploading diagnostic logs

.github/workflows/lint.yml

+11-1
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,17 @@ jobs:
288288
type-safe setters
289289
if: always()
290290
run: |
291-
git grep -I -n 'emberAfWriteAttribute' -- './*' ':(exclude).github/workflows/lint.yml' ':(exclude)zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp' ':(exclude)src/app/zap-templates/templates/app/attributes/Accessors-src.zapt' ':(exclude)src/app/util/attribute-table.cpp' ':(exclude)examples/common/pigweed/rpc_services/Attributes.h' ':(exclude)src/app/util/attribute-table.h' ':(exclude)src/app/util/ember-compatibility-functions.cpp' && exit 1 || exit 0
291+
git grep -I -n 'emberAfWriteAttribute' -- './*' \
292+
':(exclude).github/workflows/lint.yml' \
293+
':(exclude)examples/common/pigweed/rpc_services/Attributes.h' \
294+
':(exclude)src/app/codegen-data-model/CodegenDataModel_Write.cpp' \
295+
':(exclude)src/app/codegen-data-model/tests/EmberReadWriteOverride.cpp' \
296+
':(exclude)src/app/util/attribute-table.cpp' \
297+
':(exclude)src/app/util/attribute-table.h' \
298+
':(exclude)src/app/util/ember-compatibility-functions.cpp' \
299+
':(exclude)src/app/zap-templates/templates/app/attributes/Accessors-src.zapt' \
300+
':(exclude)zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp' \
301+
&& exit 1 || exit 0
292302
293303
# Run ruff python linter
294304
- name: Check for errors using ruff Python linter

src/app/codegen-data-model/BUILD.gn

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ import("//build_overrides/chip.gni")
2020
#
2121
# Use `model.gni` to get access to:
2222
# CodegenDataModel.cpp
23-
# CodegenDataModel_Read.cpp
2423
# CodegenDataModel.h
24+
# CodegenDataModel_Read.cpp
25+
# CodegenDataModel_Write.cpp
26+
# EmberMetadata.cpp
27+
# EmberMetadata.h
2528
#
2629
# The above list of files exists to satisfy the "dependency linter"
2730
# since those files should technically be "visible to gn" even though we

src/app/codegen-data-model/CodegenDataModel.cpp

-7
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,6 @@ bool CodegenDataModel::EmberCommandListIterator::Exists(const CommandId * list,
231231
return (*mCurrentHint == toCheck);
232232
}
233233

234-
CHIP_ERROR CodegenDataModel::WriteAttribute(const InteractionModel::WriteAttributeRequest & request,
235-
AttributeValueDecoder & decoder)
236-
{
237-
// TODO: this needs an implementation
238-
return CHIP_ERROR_NOT_IMPLEMENTED;
239-
}
240-
241234
CHIP_ERROR CodegenDataModel::Invoke(const InteractionModel::InvokeRequest & request, TLV::TLVReader & input_arguments,
242235
InteractionModel::InvokeReply & reply)
243236
{

src/app/codegen-data-model/CodegenDataModel_Read.cpp

+33-64
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
#include "lib/core/CHIPError.h"
1817
#include <app/codegen-data-model/CodegenDataModel.h>
1918

2019
#include <optional>
@@ -29,6 +28,7 @@
2928
#include <app/AttributeValueEncoder.h>
3029
#include <app/GlobalAttributes.h>
3130
#include <app/RequiredPrivilege.h>
31+
#include <app/codegen-data-model/EmberMetadata.h>
3232
#include <app/data-model/FabricScoped.h>
3333
#include <app/util/af-types.h>
3434
#include <app/util/attribute-metadata.h>
@@ -49,56 +49,6 @@ namespace app {
4949
namespace {
5050
using namespace chip::app::Compatibility::Internal;
5151

52-
// Fetch the source for the given attribute path: either a cluster (for global ones) or attribute
53-
// path.
54-
//
55-
// if returning a CHIP_ERROR, it will NEVER be CHIP_NO_ERROR.
56-
std::variant<const EmberAfCluster *, // global attribute, data from a cluster
57-
const EmberAfAttributeMetadata *, // a specific attribute stored by ember
58-
CHIP_ERROR // error, this will NEVER be CHIP_NO_ERROR
59-
>
60-
FindAttributeMetadata(const ConcreteAttributePath & aPath)
61-
{
62-
for (auto & attr : GlobalAttributesNotInMetadata)
63-
{
64-
65-
if (attr == aPath.mAttributeId)
66-
{
67-
const EmberAfCluster * cluster = emberAfFindServerCluster(aPath.mEndpointId, aPath.mClusterId);
68-
if (cluster == nullptr)
69-
{
70-
return (emberAfFindEndpointType(aPath.mEndpointId) == nullptr) ? CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint)
71-
: CHIP_IM_GLOBAL_STATUS(UnsupportedCluster);
72-
}
73-
74-
return cluster;
75-
}
76-
}
77-
const EmberAfAttributeMetadata * metadata =
78-
emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId);
79-
80-
if (metadata == nullptr)
81-
{
82-
const EmberAfEndpointType * type = emberAfFindEndpointType(aPath.mEndpointId);
83-
if (type == nullptr)
84-
{
85-
return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint);
86-
}
87-
88-
const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER);
89-
if (cluster == nullptr)
90-
{
91-
return CHIP_IM_GLOBAL_STATUS(UnsupportedCluster);
92-
}
93-
94-
// Since we know the attribute is unsupported and the endpoint/cluster are
95-
// OK, this is the only option left.
96-
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
97-
}
98-
99-
return metadata;
100-
}
101-
10252
/// Attempts to read via an attribute access interface (AAI)
10353
///
10454
/// If it returns a CHIP_ERROR, then this is a FINAL result (i.e. either failure or success).
@@ -138,13 +88,30 @@ struct ShortPascalString
13888
{
13989
using LengthType = uint8_t;
14090
static constexpr LengthType kNullLength = 0xFF;
91+
92+
static size_t GetLength(ByteSpan buffer)
93+
{
94+
VerifyOrDie(buffer.size() >= 1);
95+
// NOTE: we do NOT use emberAfStringLength from ember-strings.h because that will result in 0
96+
// length for null sizes (i.e. 0xFF is translated to 0 and we do not want that here)
97+
return buffer[0];
98+
}
14199
};
142100

143101
/// Metadata of what a ember/pascal LONG string means (prepended by a u16 length)
144102
struct LongPascalString
145103
{
146104
using LengthType = uint16_t;
147105
static constexpr LengthType kNullLength = 0xFFFF;
106+
107+
static size_t GetLength(ByteSpan buffer)
108+
{
109+
// NOTE: we do NOT use emberAfLongStringLength from ember-strings.h because that will result in 0
110+
// length for null sizes (i.e. 0xFFFF is translated to 0 and we do not want that here)
111+
VerifyOrDie(buffer.size() >= 2);
112+
const uint8_t * data = buffer.data();
113+
return Encoding::LittleEndian::Read16(data);
114+
}
148115
};
149116

150117
// ember assumptions ... should just work
@@ -157,20 +124,17 @@ static_assert(sizeof(LongPascalString::LengthType) == 2);
157124
template <class OUT, class ENCODING>
158125
std::optional<OUT> ExtractEmberString(ByteSpan data)
159126
{
160-
typename ENCODING::LengthType len;
161-
162-
// Ember storage format for pascal-prefix data is specifically "native byte order",
163-
// hence the use of memcpy.
164-
VerifyOrDie(sizeof(len) <= data.size());
165-
memcpy(&len, data.data(), sizeof(len));
127+
constexpr size_t kLengthTypeSize = sizeof(typename ENCODING::LengthType);
128+
VerifyOrDie(kLengthTypeSize <= data.size());
129+
auto len = ENCODING::GetLength(data);
166130

167131
if (len == ENCODING::kNullLength)
168132
{
169133
return std::nullopt;
170134
}
171135

172-
VerifyOrDie(static_cast<size_t>(len + sizeof(len)) <= data.size());
173-
return std::make_optional<OUT>(reinterpret_cast<typename OUT::pointer>(data.data() + sizeof(len)), len);
136+
VerifyOrDie(len + sizeof(len) <= data.size());
137+
return std::make_optional<OUT>(reinterpret_cast<typename OUT::pointer>(data.data() + kLengthTypeSize), len);
174138
}
175139

176140
/// Encode a value inside `encoder`
@@ -282,7 +246,7 @@ CHIP_ERROR EncodeEmberValue(ByteSpan data, const EmberAfAttributeMetadata * meta
282246
return EncodeStringLike<ByteSpan, LongPascalString>(data, isNullable, encoder);
283247
default:
284248
ChipLogError(DataManagement, "Attribute type 0x%x not handled", static_cast<int>(metadata->attributeType));
285-
return CHIP_IM_GLOBAL_STATUS(UnsupportedRead);
249+
return CHIP_IM_GLOBAL_STATUS(Failure);
286250
}
287251
}
288252

@@ -311,21 +275,26 @@ CHIP_ERROR CodegenDataModel::ReadAttribute(const InteractionModel::ReadAttribute
311275
RequiredPrivilege::ForReadAttribute(request.path));
312276
if (err != CHIP_NO_ERROR)
313277
{
278+
ReturnErrorCodeIf(err != CHIP_ERROR_ACCESS_DENIED, err);
279+
314280
// Implementation of 8.4.3.2 of the spec for path expansion
315-
if (request.path.mExpanded && (err == CHIP_ERROR_ACCESS_DENIED))
281+
if (request.path.mExpanded)
316282
{
317283
return CHIP_NO_ERROR;
318284
}
319-
return err;
285+
// access denied has a specific code for IM
286+
return CHIP_IM_GLOBAL_STATUS(UnsupportedAccess);
320287
}
321288
}
322289

323-
auto metadata = FindAttributeMetadata(request.path);
290+
auto metadata = Ember::FindAttributeMetadata(request.path);
324291

325292
// Explicit failure in finding a suitable metadata
326293
if (const CHIP_ERROR * err = std::get_if<CHIP_ERROR>(&metadata))
327294
{
328-
VerifyOrDie(*err != CHIP_NO_ERROR);
295+
VerifyOrDie((*err == CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint)) || //
296+
(*err == CHIP_IM_GLOBAL_STATUS(UnsupportedCluster)) || //
297+
(*err == CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute)));
329298
return *err;
330299
}
331300

0 commit comments

Comments
 (0)