modules/woz_matter/include/matter_im.h
openaliro/openaliro
Module

matter_im.h

the Interaction Model, as far as a commissioner needs it.

modules/woz_matter/include/matter_im.h8 documented symbols

Overview

the Interaction Model, as far as a commissioner needs it. Once PASE is done the commissioner stops speaking Secure Channel and starts asking questions. The first one is a ReadRequest, and until something answers it the phone waits, gives up, and shows "pairing failed" with no further clue. in ReadRequest (protocol 0x0001, opcode 0x02) out ReportData (protocol 0x0001, opcode 0x05) This is the read half only. Write, Invoke and Subscribe are separate messages and land when something needs them; commissioning cannot start without Read. It holds no device data. Which endpoints exist and what their attributes say is matter_clusters.h's, reached through @ref matter_im_server, so the wire format can be tested without a device and the device without a wire.

depends on matter_status.h matter_tlv.h  ·  used by matter_clusters.h matter_case.c matter_im.c

API

Cstruct matter_im_path

modules/woz_matter/include/matter_im.h:119

One requested path. An absent field is a WILDCARD, and that distinction decides how a path that matches nothing is answered -- see matter_im_report_data_encode().

Fstatic inline bool matter_im_path_is_wildcard(const struct matter_im_path *p)

modules/woz_matter/include/matter_im.h:129

True when any component is wildcarded (app/AttributePathParams.h:57-59).

Cstruct matter_im_read

modules/woz_matter/include/matter_im.h:135

A decoded ReadRequest.

Cstruct matter_im_subscribe

modules/woz_matter/include/matter_im.h:151

A SubscribeRequest: the same paths a read asks for, plus how often.

Cstruct matter_im_invoke

modules/woz_matter/include/matter_im.h:256

One command a commissioner asked this node to run.

Cstruct matter_im_server

modules/woz_matter/include/matter_im.h:353

Callback table for the Matter interaction model server, defining functions to query attribute status, read values, list endpoints and clusters, and handle write and command operations.

Cstruct matter_im_write

modules/woz_matter/include/matter_im.h:385

One attribute write. Exactly one per message, the same restriction matter_im_invoke has and for the same reason: a commissioner that sent two and saw one status would be entitled to assume both applied.

Cstruct matter_im_report_stats

modules/woz_matter/include/matter_im.h:448

What encoding a report had to leave out. Worth logging; none of it is fatal.

##define MATTER_IM_OP_STATUS_RESPONSE 0x01u

modules/woz_matter/include/matter_im.h:47

##define MATTER_IM_OP_READ_REQUEST 0x02u

modules/woz_matter/include/matter_im.h:48

##define MATTER_IM_OP_REPORT_DATA 0x05u

modules/woz_matter/include/matter_im.h:49

##define MATTER_IM_OP_SUBSCRIBE_REQUEST 0x03u

modules/woz_matter/include/matter_im.h:50

##define MATTER_IM_OP_SUBSCRIBE_RESPONSE 0x04u

modules/woz_matter/include/matter_im.h:51

##define MATTER_IM_OP_WRITE_REQUEST 0x06u

modules/woz_matter/include/matter_im.h:52

##define MATTER_IM_OP_WRITE_RESPONSE 0x07u

modules/woz_matter/include/matter_im.h:53

##define MATTER_IM_OP_INVOKE_COMMAND_REQUEST 0x08u

modules/woz_matter/include/matter_im.h:54

##define MATTER_IM_OP_INVOKE_COMMAND_RESPONSE 0x09u

modules/woz_matter/include/matter_im.h:55

##define MATTER_IM_OP_TIMED_REQUEST 0x0Au

modules/woz_matter/include/matter_im.h:65

TimedRequest, which precedes any command that must not be replayed. The peer sends this FIRST, waits for a StatusResponse, and only then sends the invoke it actually wanted. Ignoring it is not an error anywhere: the peer simply waits out its own timeout and reports the transaction as timed out, which is what a real controller did to this node for ten seconds before abandoning a pairing.

##define MATTER_IM_REVISION 12u

modules/woz_matter/include/matter_im.h:73

Interaction Model revision this node claims (app/SpecificationDefinedRevisions.h:34). Sent under tag 0xFF (line 35), which is why it is a define rather than an argument: a peer that disagrees about the revision disagrees about the whole message.

##define MATTER_IM_STATUS_SUCCESS 0x00u

modules/woz_matter/include/matter_im.h:79

##define MATTER_IM_STATUS_FAILURE 0x01u

modules/woz_matter/include/matter_im.h:80

##define MATTER_IM_STATUS_UNSUPPORTED_ENDPOINT 0x7Fu

modules/woz_matter/include/matter_im.h:81

##define MATTER_IM_STATUS_UNSUPPORTED_COMMAND 0x81u

modules/woz_matter/include/matter_im.h:82

##define MATTER_IM_STATUS_INVALID_COMMAND 0x85u

modules/woz_matter/include/matter_im.h:83

##define MATTER_IM_STATUS_CONSTRAINT_ERROR 0x87u

modules/woz_matter/include/matter_im.h:85

A field was well-formed but the wrong size or out of range (line 55).

##define MATTER_IM_STATUS_UNSUPPORTED_ATTRIBUTE 0x86u

modules/woz_matter/include/matter_im.h:86

##define MATTER_IM_STATUS_UNSUPPORTED_WRITE 0x88u

modules/woz_matter/include/matter_im.h:87

##define MATTER_IM_STATUS_RESOURCE_EXHAUSTED 0x89u

modules/woz_matter/include/matter_im.h:88

##define MATTER_IM_STATUS_UNSUPPORTED_CLUSTER 0xC3u

modules/woz_matter/include/matter_im.h:89

##define MATTER_IM_STATUS_FAILSAFE_REQUIRED 0xCAu

modules/woz_matter/include/matter_im.h:90

##define MATTER_IM_NO_RESPONSE 0xFFFFFFFFu

modules/woz_matter/include/matter_im.h:102

"This command succeeded and has no response payload." Most commands answer with a response command; some -- AddTrustedRootCertificate among them -- answer with a bare SUCCESS status and nothing else. A handler says so by leaving *response_command at this value, which is also what matter_im_invoke_response_encode() initialises it to, so forgetting to set a response command produces a legal empty reply rather than one claiming to be command 0. kInvalidCommandId (lib/core/DataModelTypes.h:86).

##define MATTER_IM_MAX_PATHS 16

modules/woz_matter/include/matter_im.h:112

How many attribute paths one ReadRequest may carry before this refuses it. A real iPhone's commissioning read asked for NINE. The bound exists so a peer cannot choose how much stack this uses, and it is a refusal rather than a truncation: answering some of what was asked, silently, would look to the commissioner like the device genuinely has nothing more.

Fint matter_im_subscribe_request_decode(const uint8_t *tlv, size_t len, struct matter_im_subscribe *out)

modules/woz_matter/include/matter_im.h:167

Decode a SubscribeRequestMessage (app/MessageDef/SubscribeRequestMessage.h). The paths live at tag 3 here and at tag 0 in a ReadRequest, which is the only reason this is not the read decoder. @return MATTER_OK; MATTER_E_NOSPACE past MATTER_IM_MAX_PATHS; MATTER_E_INVAL for a malformed message.

Fint matter_im_subscribe_response_encode(uint32_t subscription_id, uint16_t max_interval_s, uint8_t *out, size_t cap, size_t *out_len)

modules/woz_matter/include/matter_im.h:182

Encode a SubscribeResponseMessage (SubscribeResponseMessage.h:39-42). Sent LAST, after the priming report has been answered with a StatusResponse -- not in reply to the SubscribeRequest itself. Sending it first leaves the subscriber holding an id for a subscription whose initial values it never received. @param max_interval_s what this node COMMITS to, which must be no larger than the ceiling the subscriber asked for: it is the deadline by which the next report has to arrive or the subscription is considered dead.

Fint matter_im_status_response_encode(uint8_t status, uint8_t *out, size_t cap, size_t *out_len)

modules/woz_matter/include/matter_im.h:191

Encode a bare StatusResponseMessage (app/MessageDef/StatusResponseMessage.h). This is the whole answer to a TimedRequest, and it is also what a subscriber sends back after a priming report -- the same message in both directions.

Fint matter_im_timed_request_decode(const uint8_t *buf, size_t len, uint16_t *timeout_ms)

modules/woz_matter/include/matter_im.h:203

Decode a TimedRequestMessage, whose only field is the timeout. The timeout is the peer's own deadline for sending the invoke that follows, measured from when it receives the StatusResponse. Nothing here has to enforce it to interoperate; a node that does enforce it must answer a late invoke with TIMEOUT rather than running it. @return MATTER_OK, or MATTER_E_INVAL / MATTER_E_TRUNC on a malformed message.

Fint matter_im_read_request_decode(const uint8_t *tlv, size_t len, struct matter_im_read *out)

modules/woz_matter/include/matter_im.h:212

Decode a ReadRequestMessage (app/MessageDef/ReadRequestMessage.h). @return MATTER_OK; MATTER_E_NOSPACE when the peer asked for more than MATTER_IM_MAX_PATHS paths; MATTER_E_INVAL for a malformed message; or whatever the TLV decoder returned.

Ctypedef uint8_t (*matter_im_status_fn)(void *ctx, uint16_t endpoint, uint32_t cluster, uint32_t attribute)

modules/woz_matter/include/matter_im.h:232

What answering this path will produce, WITHOUT writing anything. Split from writing the value on purpose. An information block is built outside-in -- container, data version, path, then the value -- so by the time a writing callback could report a failure, three fields are already committed and TLV offers no way to take them back. Asking first means the encoder never has to unwind, which matters because the writer latches its first error and turns later calls into no-ops: an unwind would have to reason about container depth that was never incremented. @return MATTER_IM_STATUS_SUCCESS, or the status explaining why not -- UNSUPPORTED_ATTRIBUTE when the cluster exists but the attribute does not, UNSUPPORTED_CLUSTER when the endpoint exists but the cluster does not, UNSUPPORTED_ENDPOINT when it does not (app/data-model-provider/MetadataLookup.cpp:68-88 chooses in that order).

Ctypedef void (*matter_im_value_fn)(void *ctx, uint16_t endpoint, uint32_t cluster, uint32_t attribute, struct matter_tlv_writer *w, matter_tlv_tag_t tag)

modules/woz_matter/include/matter_im.h:243

Write one attribute's value. Called only after @ref matter_im_status_fn returned SUCCESS for the same path, so it has nothing to refuse. @param w write EXACTLY ONE element, tagged tag. A structure is one element; two siblings are not. Errors need no checking -- the writer latches them and the encoder reports them once at the end.

Ctypedef bool (*matter_im_has_cluster_fn)(void *ctx, uint16_t endpoint, uint32_t cluster)

modules/woz_matter/include/matter_im.h:253

Does endpoint carry cluster? Only consulted for wildcard paths, where a miss must be silent rather than an error, so this decides between answering and saying nothing at all.

Ctypedef uint8_t (*matter_im_command_fn)(void *ctx, const struct matter_im_invoke *inv, uint32_t *response_command)

modules/woz_matter/include/matter_im.h:289

Run one command. Called EXACTLY ONCE per request, which is why it is separate from writing the reply: this is where side effects belong. Anything the response needs must be recorded in the device's own state here, because @ref matter_im_command_fields_fn runs afterwards and must be pure. @param response_command receives the command id to answer with. Ignored unless SUCCESS is returned. @return MATTER_IM_STATUS_SUCCESS to send a command response, or the status to send instead -- UNSUPPORTED_COMMAND when the cluster exists but the command does not, INVALID_COMMAND when its arguments do not parse.

Ctypedef void (*matter_im_command_fields_fn)(void *ctx, uint16_t endpoint, uint32_t cluster, uint32_t response_command, struct matter_tlv_writer *w, matter_tlv_tag_t tag)

modules/woz_matter/include/matter_im.h:298

Write the fields of the response @ref matter_im_command_fn chose. Pure: it serialises what the command already computed. Write exactly one element, tagged tag, as with @ref matter_im_value_fn.

Ctypedef size_t (*matter_im_list_attrs_fn)(void *ctx, uint16_t endpoint, uint32_t cluster, const uint32_t **out)

modules/woz_matter/include/matter_im.h:308

List the attributes of one cluster, for expanding a wildcard read. @param out receives a pointer to a static array of attribute ids. @return how many, or 0 when the cluster is not on that endpoint.

Ctypedef size_t (*matter_im_list_clusters_fn)(void *ctx, uint16_t endpoint, const uint32_t **out)

modules/woz_matter/include/matter_im.h:323

Every cluster on one endpoint, for expanding a path that names none. A commissioner that has just adopted a node subscribes to ALL of it -- no endpoint, no cluster, no attribute -- and that is not an edge case, it is what Apple does immediately after writing its ACL. Returning 0 makes such a subscription report nothing at all, which the subscriber cannot distinguish from a node that has stopped talking. @param out receives a pointer to a static array of cluster ids. @return how many, or 0 when the endpoint does not exist.

Ctypedef size_t (*matter_im_list_endpoints_fn)(void *ctx, const uint16_t **out)

modules/woz_matter/include/matter_im.h:335

Every endpoint this node has, for expanding a path that names none. A commissioner asks about a cluster without saying where it lives -- Apple reads NetworkCommissioning exactly that way -- and the only honest answer is to look on each endpoint. Returning 0 makes such a path unanswerable, which reads to the commissioner as a node that has no such cluster anywhere. @param out receives a borrowed array. @return how many.

Ctypedef uint8_t (*matter_im_write_fn)(void *ctx, const struct matter_im_path *path, const uint8_t *data, size_t data_len)

modules/woz_matter/include/matter_im.h:346

Apply one attribute write. Supplied by the cluster layer. @param data the encoded value, exactly as it arrived. @return an IM status: MATTER_IM_STATUS_SUCCESS, or MATTER_IM_STATUS_UNSUPPORTED_ATTRIBUTE / _CLUSTER / _ENDPOINT for a path this node does not have, which is what tells a commissioner the difference between "refused" and "does not exist".

Fint matter_im_write_request_decode(const uint8_t *tlv, size_t len, struct matter_im_write *out)

modules/woz_matter/include/matter_im.h:420

Decode a WriteRequestMessage (app/MessageDef/WriteRequestMessage.h:39-44). @return MATTER_OK, MATTER_E_NOSPACE for a batch, MATTER_E_INVAL for a malformed message or a path that is not concrete -- a wildcard write is refused rather than expanded, because guessing which attributes a commissioner meant to overwrite is not a recoverable mistake.

Fint matter_im_write_response_encode(const struct matter_im_server *srv, const struct matter_im_write *wr, uint8_t *out, size_t cap, size_t *out_len)

modules/woz_matter/include/matter_im.h:428

Run the write and encode its WriteResponseMessage. @param out_len set to 0 when the request suppressed the response, which is not an error: the write still ran.

Fint matter_im_invoke_request_decode(const uint8_t *tlv, size_t len, struct matter_im_invoke *out)

modules/woz_matter/include/matter_im.h:432

Fint matter_im_invoke_response_encode(const struct matter_im_server *srv, const struct matter_im_invoke *inv, uint8_t *out, size_t cap, size_t *out_len)

modules/woz_matter/include/matter_im.h:443

Run inv and encode the InvokeResponseMessage answering it. The command runs even when the peer set SuppressResponse -- it asked for the effect, not for silence about it -- but then out_len is set to 0 and there is nothing to send. @return MATTER_OK, MATTER_E_NOSPACE, or MATTER_E_INVAL.

Fint matter_im_report_data_encode(const struct matter_im_server *srv, const struct matter_im_read *req, uint8_t *out, size_t cap, size_t *out_len, struct matter_im_report_stats *stats)

modules/woz_matter/include/matter_im.h:485

Encode the ReportData answering req. The rule that is easy to get backwards, and is not symmetric: - A CONCRETE path naming something unsupported gets an AttributeStatusIB. Reporting nothing would tell the commissioner the attribute is absent from a device that never said so (reporting/Engine.cpp:92-106, 495). - A WILDCARD path matching nothing gets SILENCE. An error would claim the whole expansion failed (AttributePathExpandIterator.cpp:239-255). SuppressResponse is set, because this answers a Read rather than a Subscribe and CHIP sets it for exactly that case (reporting/Engine.cpp:834-836). The commissioner therefore sends no StatusResponse and the exchange simply ends. Chunking is not implemented: an answer too large for cap fails rather than setting MoreChunkedMessages. A truncated report is indistinguishable from a complete one to the peer, so it must not be possible to send one by accident. @param stats optional; may be NULL. @return MATTER_OK, MATTER_E_NOSPACE if the report does not fit, or MATTER_E_INVAL.

##define MATTER_MAX_MESSAGE_LEN 1232u

modules/woz_matter/include/matter_im.h:498

The most a Matter message may be, per spec. The guaranteed IPv6 MTU is 1280 bytes and Matter does not exceed it (CHIPConfig.h:320-324); over Thread there is nothing to fragment for. A report larger than this is not slow, it is UNDELIVERABLE -- the subscriber simply never sees it and re-subscribes forever, which is exactly what a 1513-byte report produced.

Fint matter_im_report_data_chunk(const struct matter_im_server *srv, const struct matter_im_read *req, uint16_t sent, uint8_t *out, size_t cap, size_t *out_len, bool *more, uint16_t *emitted, struct matter_im_report_stats *stats)

modules/woz_matter/include/matter_im.h:516

Encode ONE CHUNK of a report, continuing where the last one stopped. A controller subscribes to the whole data model, and this node's answer to that does not fit one message. The spec's answer is MoreChunkedMessages: send what fits, let the peer acknowledge it with a StatusResponse, send the rest. @param sent how many attribute reports previous chunks already carried. Zero starts a fresh report. Expansion is deterministic, so re-walking and skipping is equivalent to resuming -- and far harder to get wrong than a cursor into four nested loops. @param more set true when reports remain; the caller adds emitted to sent and calls again once the peer has acknowledged this chunk. @param emitted how many this chunk carried. Zero with more true means not even one report fits in cap, which is a caller bug, not a chunk.