Skip to main content

rs_matter/transport/
exchange.rs

1/*
2 *
3 *    Copyright (c) 2022-2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use core::fmt::{self, Display};
19use core::num::NonZeroU8;
20use core::pin::pin;
21
22use either::Either as EitherIo;
23use embassy_futures::select::{select, select3, Either, Either3};
24use embassy_time::{Duration, Instant, Timer};
25
26use crate::acl::Accessor;
27use crate::bdx::{self, PROTO_ID_BDX};
28use crate::crypto::Crypto;
29use crate::dm::NodeId;
30use crate::error::{Error, ErrorCode};
31use crate::im::{self, PROTO_ID_INTERACTION_MODEL};
32use crate::sc::{self, PROTO_ID_SECURE_CHANNEL};
33use crate::transport::session::Sessions;
34use crate::transport::TxPayloadState;
35use crate::utils::storage::pooled::{PooledBuffers, DEFAULT_BUFFER_POOL_SIZE};
36use crate::utils::storage::WriteBuf;
37use crate::{Matter, MatterState};
38
39use super::mrp::{ReliableMessage, RetransEntry};
40use super::network;
41use super::packet::PacketHdr;
42use super::plain_hdr::PlainHdr;
43use super::proto_hdr::ProtoHdr;
44use super::session::{Session, SessionMode};
45use super::{PacketAccess, MAX_RX_BUF_SIZE, MAX_TX_BUF_SIZE};
46
47/// Minimum buffer which should be allocated by user code that wants to pull RX messages via `Exchange::recv_into`
48///
49/// When the `large-buffers` feature is enabled, this tracks the larger TCP-capable packet
50/// size, so that IM-level buffers (e.g. `IMBuffer`) can absorb a full Matter-over-TCP
51/// message. Otherwise it stays at the UDP-sized default.
52#[cfg(feature = "large-buffers")]
53pub const MAX_EXCHANGE_RX_BUF_SIZE: usize = network::MAX_RX_LARGE_PACKET_SIZE;
54#[cfg(not(feature = "large-buffers"))]
55pub const MAX_EXCHANGE_RX_BUF_SIZE: usize = network::MAX_RX_PACKET_SIZE;
56
57/// Maximum buffer which should be allocated and used by user code that wants to send messages via `Exchange::send`
58///
59/// Mirrors `MAX_EXCHANGE_RX_BUF_SIZE` with respect to the `large-buffers` feature.
60#[cfg(feature = "large-buffers")]
61pub const MAX_EXCHANGE_TX_BUF_SIZE: usize =
62    network::MAX_TX_LARGE_PACKET_SIZE - PacketHdr::HDR_RESERVE - PacketHdr::TAIL_RESERVE;
63#[cfg(not(feature = "large-buffers"))]
64pub const MAX_EXCHANGE_TX_BUF_SIZE: usize =
65    network::MAX_TX_PACKET_SIZE - PacketHdr::HDR_RESERVE - PacketHdr::TAIL_RESERVE;
66
67/// A protocol payload buffer, sized to hold a full exchange RX message
68/// ([`MAX_EXCHANGE_RX_BUF_SIZE`]).
69///
70/// This is the central buffer type that both [`IMBuffer`](crate::im::IMBuffer)
71/// and [`BdxBuffer`](crate::bdx::BdxBuffer) alias, so a single
72/// [`PooledBuffers`](crate::utils::storage::pooled::PooledBuffers) pool can be
73/// shared across the data model and BDX.
74pub type Buffer = crate::utils::storage::Vec<u8, MAX_EXCHANGE_RX_BUF_SIZE>;
75
76/// A [`PooledBuffers`] pool pre-configured with Matter defaults: it holds
77/// [`Buffer`]s (the central exchange-sized buffer) and [`DEFAULT_BUFFER_POOL_SIZE`]
78/// of them, behind the default Matter raw mutex.
79pub type MatterBuffers<const N: usize = DEFAULT_BUFFER_POOL_SIZE> = PooledBuffers<Buffer, N>;
80
81/// An exchange identifier, uniquely identifying a session and an exchange within that session for a given Matter stack.
82#[derive(Copy, Clone, Debug, Eq, PartialEq)]
83pub struct ExchangeId(u32);
84
85impl ExchangeId {
86    pub(crate) fn new(session_id: u32, exchange_index: usize) -> Self {
87        if session_id > 0x0fff_ffff {
88            panic!("Session ID out of range");
89        }
90
91        if exchange_index >= 16 {
92            panic!("Exchange index out of range");
93        }
94
95        Self(((exchange_index as u32) << 28) | session_id)
96    }
97
98    pub(crate) fn session_id(&self) -> u32 {
99        self.0 & 0x0fff_ffff
100    }
101
102    pub(crate) fn exchange_index(&self) -> usize {
103        (self.0 >> 28) as _
104    }
105
106    /// Get the session associated with this exchange from the given sessions store.
107    ///
108    /// ATTENTION: This method will panic if the session is not found in the store, so make sure to only call it when you are sure the session exists.
109    pub(crate) fn session<'a>(&self, sessions: &'a mut Sessions) -> &'a mut Session {
110        unwrap!(sessions.get(self.session_id()))
111    }
112
113    /// Get the exchange state associated with this exchange from the given sessions store.
114    pub(crate) fn exch<'a>(&self, session: &'a mut Session) -> &'a mut ExchangeState {
115        unwrap!(session.exchanges[self.exchange_index()].as_mut())
116    }
117
118    pub(crate) fn display<'a>(&'a self, session: &'a Session) -> ExchangeIdDisplay<'a> {
119        ExchangeIdDisplay { id: self, session }
120    }
121
122    async fn recv<'a>(&self, matter: &'a Matter<'a>) -> Result<RxMessage<'a>, Error> {
123        self.check_no_pending_retrans(matter)?;
124
125        loop {
126            let mut recv = pin!(matter.transport().get_if_rx(|packet| {
127                if packet.buf.is_empty() {
128                    false
129                } else {
130                    let for_us = self.with_state(matter, |state| {
131                        let sess = self.session(&mut state.sessions);
132                        if sess.is_for_rx(&packet.peer, &packet.header.plain) {
133                            let exch = self.exch(sess);
134
135                            return Ok(exch.is_for_rx(&packet.header.proto));
136                        }
137
138                        Ok(false)
139                    });
140
141                    for_us.unwrap_or(true)
142                }
143            }));
144
145            let mut session_removed = pin!(matter.transport().wait_session_removed());
146
147            let mut timeout = pin!(Timer::after(Duration::from_millis(
148                RetransEntry::new(matter.dev_det().sai, 0).max_delay_ms() * 3 / 2
149            )));
150
151            match select3(&mut recv, &mut session_removed, &mut timeout).await {
152                Either3::First(mut packet) => {
153                    packet.clear_on_drop(true);
154
155                    self.check_no_pending_retrans(matter)?;
156
157                    break Ok(RxMessage(packet));
158                }
159                Either3::Second(_) => {
160                    // Session removed
161
162                    // Bail out if it was ours
163                    self.with_state(matter, |_| Ok(()))?;
164
165                    // If not, go back waiting for a packet
166                    continue;
167                }
168                Either3::Third(_) => {
169                    // Timeout waiting for an answer from the other peer
170                    Err(ErrorCode::RxTimeout)?;
171                }
172            };
173        }
174    }
175
176    /// Gets access to the TX buffer of the Matter stack for constructing a new TX message.
177    /// If the TX buffer is not available, the method will wait indefinitely until it becomes available.
178    ///
179    /// NOTE:
180    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
181    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
182    ///
183    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
184    /// (say, because of lack of resources or a hard networking error), the method will return an error.
185    async fn init_send<'a>(&self, matter: &'a Matter<'a>) -> Result<TxMessage<'a>, Error> {
186        self.with_state(matter, |_| Ok(()))?;
187
188        let mut packet = matter
189            .transport
190            .get_if_tx(|packet| {
191                packet.buf.is_empty() || self.with_state(matter, |_| Ok(())).is_err()
192            })
193            .await;
194
195        // TODO: Resizing might be a bit expensive with large buffers
196        unwrap!(packet.buf.resize_default(MAX_TX_BUF_SIZE));
197
198        packet.clear_on_drop(true);
199
200        let tx = TxMessage {
201            exchange_id: *self,
202            matter,
203            packet,
204        };
205
206        self.with_state(matter, |_| Ok(()))?;
207
208        Ok(tx)
209    }
210
211    /// Waits until the other side acknowledges the last message sent on this exchange,
212    /// or until time for a re-transmission had come.
213    ///
214    /// If the last sent message was not using the MRP protocol, the method will return immediately with `TxOutcome::Done`.
215    ///
216    /// NOTE:
217    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
218    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
219    ///
220    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
221    /// (say, because of lack of resources or a hard networking error), the method will return an error.
222    async fn wait_tx<'a>(&self, matter: &'a Matter<'a>) -> Result<TxOutcome, Error> {
223        if let Some(delay) = self.retrans_delay_ms(matter)? {
224            let expired = unwrap!(Instant::now().checked_add(Duration::from_millis(delay)));
225
226            loop {
227                let mut notification = pin!(self.internal_wait_ack(matter));
228                let mut session_removed = pin!(matter.transport().wait_session_removed());
229                let mut timer = pin!(Timer::at(expired));
230
231                if !matches!(
232                    select3(&mut notification, &mut session_removed, &mut timer).await,
233                    Either3::Second(_)
234                ) {
235                    break;
236                }
237
238                // Bail out if the removed session was ours
239                self.with_state(matter, |_| Ok(()))?;
240            }
241
242            if self.retrans_delay_ms(matter)?.is_some() {
243                Ok(TxOutcome::Retransmit)
244            } else {
245                Ok(TxOutcome::Done)
246            }
247        } else {
248            Ok(TxOutcome::Done)
249        }
250    }
251
252    fn accessor<'a>(&self, matter: &'a Matter<'a>) -> Result<Accessor<'a>, Error> {
253        self.with_state(matter, |state| {
254            let sess = self.session(&mut state.sessions);
255
256            Ok(Accessor::for_session(sess, matter))
257        })
258    }
259
260    fn with_state<'a, F, T>(&self, matter: &'a Matter<'a>, f: F) -> Result<T, Error>
261    where
262        F: FnOnce(&mut MatterState) -> Result<T, Error>,
263    {
264        self.with_state_ex(matter, f)
265    }
266
267    fn with_state_ex<'a, F, T, E>(&self, matter: &'a Matter<'a>, f: F) -> Result<T, E>
268    where
269        F: FnOnce(&mut MatterState) -> Result<T, E>,
270        E: From<Error>,
271    {
272        matter.with_state(|state| {
273            if state.sessions.get(self.session_id()).is_some() {
274                f(state)
275            } else {
276                warn!("Exchange {}: No session", self);
277                Err(Error::from(ErrorCode::NoSession).into())
278            }
279        })
280    }
281
282    async fn internal_wait_ack<'a>(&self, matter: &'a Matter<'a>) -> Result<(), Error> {
283        matter
284            .transport
285            .get_if_rx(|_| {
286                self.retrans_delay_ms(matter)
287                    .map(|retrans| retrans.is_none())
288                    .unwrap_or(true)
289            })
290            .await;
291
292        self.with_state(matter, |_| Ok(()))
293    }
294
295    fn retrans_delay_ms<'a>(&self, matter: &'a Matter<'a>) -> Result<Option<u64>, Error> {
296        self.with_state(matter, |state| {
297            let sess = self.session(&mut state.sessions);
298            let exch = self.exch(sess);
299
300            let mut jitter_rand = [0; 1];
301            // TODO XXX FIXME matter.rand()(&mut jitter_rand);
302            jitter_rand[0] = 100;
303
304            Ok(exch.retrans_delay_ms(jitter_rand[0]))
305        })
306    }
307
308    fn check_no_pending_retrans<'a>(&self, matter: &'a Matter<'a>) -> Result<(), Error> {
309        self.with_state(matter, |state| {
310            let sess = self.session(&mut state.sessions);
311            let exch = self.exch(sess);
312
313            if exch.mrp.is_retrans_pending() {
314                error!("Exchange {}: Retransmission pending", self.display(sess));
315                Err(ErrorCode::InvalidState)?;
316            }
317
318            Ok(())
319        })
320    }
321
322    fn pending_retrans<'a>(&self, matter: &'a Matter<'a>) -> Result<bool, Error> {
323        Ok(self.retrans_delay_ms(matter)?.is_some())
324    }
325
326    fn pending_ack<'a>(&self, matter: &'a Matter<'a>) -> Result<bool, Error> {
327        self.with_state(matter, |state| {
328            let sess = self.session(&mut state.sessions);
329            let exch = self.exch(sess);
330
331            Ok(exch.mrp.is_ack_pending())
332        })
333    }
334}
335
336impl Display for ExchangeId {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        write!(f, "{}::{}", self.session_id(), self.exchange_index())
339    }
340}
341
342#[cfg(feature = "defmt")]
343impl defmt::Format for ExchangeId {
344    fn format(&self, f: defmt::Formatter<'_>) {
345        defmt::write!(f, "{}::{}", self.session_id(), self.exchange_index())
346    }
347}
348
349/// A display wrapper for `ExchangeId` which also displays
350/// the packet session ID, packet peer session ID and packet exchange ID.
351pub struct ExchangeIdDisplay<'a> {
352    id: &'a ExchangeId,
353    session: &'a Session,
354}
355
356impl Display for ExchangeIdDisplay<'_> {
357    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358        let state = self.session.exchanges[self.id.exchange_index()].as_ref();
359
360        if let Some(state) = state {
361            write!(
362                f,
363                "{} [SID:{:x},RSID:{:x},EID:{:x}]",
364                self.id,
365                self.session.get_local_sess_id(),
366                self.session.get_peer_sess_id(),
367                state.exch_id
368            )
369        } else {
370            // This should never happen, as that would mean we have invalid exchange index
371            // but let's not crash when displaying that
372            write!(f, "{}???", self.id)
373        }
374    }
375}
376
377#[cfg(feature = "defmt")]
378impl defmt::Format for ExchangeIdDisplay<'_> {
379    fn format(&self, f: defmt::Formatter<'_>) {
380        let state = self.session.exchanges[self.id.exchange_index()].as_ref();
381
382        if let Some(state) = state {
383            defmt::write!(
384                f,
385                "{} [SID:{:x},RSID:{:x},EID:{:x}]",
386                self.id,
387                self.session.get_local_sess_id(),
388                self.session.get_peer_sess_id(),
389                state.exch_id
390            )
391        } else {
392            // This should never happen, as that would mean we have invalid exchange index
393            // but let's not crash when displaying that
394            defmt::write!(f, "{}???", self.id)
395        }
396    }
397}
398
399#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
400#[cfg_attr(feature = "defmt", derive(defmt::Format))]
401pub(crate) enum InitiatorState {
402    #[default]
403    Owned,
404    Dropped,
405}
406
407#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
408#[cfg_attr(feature = "defmt", derive(defmt::Format))]
409pub(crate) enum ResponderState {
410    #[default]
411    AcceptPending,
412    Owned,
413    Dropped,
414}
415
416#[derive(Debug, PartialEq, Eq, Copy, Clone)]
417#[cfg_attr(feature = "defmt", derive(defmt::Format))]
418pub(crate) enum Role {
419    Initiator(InitiatorState),
420    Responder(ResponderState),
421}
422
423impl Role {
424    pub fn is_dropped_state(&self) -> bool {
425        match self {
426            Self::Initiator(state) => *state == InitiatorState::Dropped,
427            Self::Responder(state) => *state == ResponderState::Dropped,
428        }
429    }
430
431    pub fn set_dropped_state(&mut self) {
432        match self {
433            Self::Initiator(state) => *state = InitiatorState::Dropped,
434            Self::Responder(state) => *state = ResponderState::Dropped,
435        }
436    }
437}
438
439#[derive(Debug)]
440#[cfg_attr(feature = "defmt", derive(defmt::Format))]
441pub(crate) struct ExchangeState {
442    pub(crate) exch_id: u16,
443    pub(crate) role: Role,
444    pub(crate) mrp: ReliableMessage,
445}
446
447impl ExchangeState {
448    pub fn is_for_rx(&self, rx_proto: &ProtoHdr) -> bool {
449        self.exch_id == rx_proto.exch_id
450            && rx_proto.is_initiator() == matches!(self.role, Role::Responder(_))
451    }
452
453    pub fn post_recv(&mut self, rx_plain: &PlainHdr, rx_proto: &ProtoHdr) -> Result<(), Error> {
454        self.mrp.post_recv(rx_plain, rx_proto)?;
455
456        Ok(())
457    }
458
459    pub fn pre_send(
460        &mut self,
461        tx_plain: &PlainHdr,
462        tx_proto: &mut ProtoHdr,
463        session_active_interval_ms: Option<u32>,
464        session_idle_interval_ms: Option<u32>,
465    ) -> Result<(), Error> {
466        if matches!(self.role, Role::Initiator(_)) {
467            tx_proto.set_initiator();
468        } else {
469            tx_proto.unset_initiator();
470        }
471
472        tx_proto.exch_id = self.exch_id;
473
474        self.mrp.pre_send(
475            tx_plain,
476            tx_proto,
477            session_active_interval_ms,
478            session_idle_interval_ms,
479        )
480    }
481
482    pub fn retrans_delay_ms(&mut self, jitter_rand: u8) -> Option<u64> {
483        self.mrp
484            .retrans
485            .as_ref()
486            .map(|retrans| retrans.delay_ms(jitter_rand))
487    }
488}
489
490/// Meta-data when sending/receving messages via an Exchange.
491/// Basically, the protocol ID, the protocol opcode and whether the message should be set in a reliable manner.
492#[derive(Debug, Eq, PartialEq, Copy, Clone)]
493pub struct MessageMeta {
494    pub proto_id: u16,
495    pub proto_opcode: u8,
496    pub reliable: bool,
497}
498
499impl MessageMeta {
500    // Create a new message meta-data instance
501    pub const fn new(proto_id: u16, proto_opcode: u8, reliable: bool) -> Self {
502        Self {
503            proto_id,
504            proto_opcode,
505            reliable,
506        }
507    }
508
509    /// Try to cast the protocol opcode to a specific type
510    pub fn opcode<T: num::FromPrimitive>(&self) -> Result<T, Error> {
511        num::FromPrimitive::from_u8(self.proto_opcode).ok_or(ErrorCode::InvalidOpcode.into())
512    }
513
514    /// Check if the protocol opcode is equal to a specific value
515    pub fn check_opcode<T: num::FromPrimitive + PartialEq>(&self, opcode: T) -> Result<(), Error> {
516        if self.opcode::<T>()? == opcode {
517            Ok(())
518        } else {
519            Err(ErrorCode::Invalid.into())
520        }
521    }
522
523    /// Create an instance from a ProtoHdr instance
524    pub fn from(proto: &ProtoHdr) -> Self {
525        Self {
526            proto_id: proto.proto_id,
527            proto_opcode: proto.proto_opcode,
528            reliable: proto.is_reliable(),
529        }
530    }
531
532    /// Set the protocol ID and opcode into a ProtoHdr instance
533    pub fn set_into(&self, proto: &mut ProtoHdr) {
534        proto.proto_id = self.proto_id;
535        proto.proto_opcode = self.proto_opcode;
536        proto.set_vendor(None);
537
538        if self.reliable {
539            proto.set_reliable();
540        } else {
541            proto.unset_reliable();
542        }
543    }
544
545    pub fn reliable(self, reliable: bool) -> Self {
546        Self { reliable, ..self }
547    }
548
549    /// Utility method to check if the specific proto opcode in the instance is expecting a TLV payload.
550    pub(crate) fn is_tlv(&self) -> bool {
551        match self.proto_id {
552            PROTO_ID_SECURE_CHANNEL => self
553                .opcode::<sc::OpCode>()
554                .ok()
555                .map(|op| op.is_tlv())
556                .unwrap_or(false),
557            PROTO_ID_INTERACTION_MODEL => self
558                .opcode::<im::OpCode>()
559                .ok()
560                .map(|op| op.is_tlv())
561                .unwrap_or(false),
562            _ => false,
563        }
564    }
565
566    /// Utility method to check if the protocol is Secure Channel, and the opcode is a standalone ACK (`MrpStandaloneAck`).
567    pub(crate) fn is_standalone_ack(&self) -> bool {
568        self.proto_id == PROTO_ID_SECURE_CHANNEL
569            && self.proto_opcode == sc::OpCode::MRPStandAloneAck as u8
570    }
571
572    /// Utility method to check if the protocol is Secure Channel, and the opcode is Status.
573    pub(crate) fn is_sc_status(&self) -> bool {
574        self.proto_id == PROTO_ID_SECURE_CHANNEL
575            && self.proto_opcode == sc::OpCode::StatusReport as u8
576    }
577
578    /// Utility method to check if the protocol is Secure Channel, and the opcode is a new session request.
579    pub(crate) fn is_new_session(&self) -> bool {
580        self.proto_id == PROTO_ID_SECURE_CHANNEL
581            && (self.proto_opcode == sc::OpCode::PBKDFParamRequest as u8
582                || self.proto_opcode == sc::OpCode::CASESigma1 as u8)
583    }
584
585    /// Utility method to check if the meta-data indicates a new exchange
586    pub(crate) fn is_new_exchange(&self) -> bool {
587        // Don't create new exchanges for standalone ACKs and for SC status codes
588        !self.is_standalone_ack() && !self.is_sc_status()
589    }
590}
591
592impl Display for MessageMeta {
593    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
594        match self.proto_id {
595            PROTO_ID_SECURE_CHANNEL => {
596                if let Ok(opcode) = self.opcode::<sc::OpCode>() {
597                    write!(f, "SC::{:?}", opcode)
598                } else {
599                    write!(f, "SC::{:02x}", self.proto_opcode)
600                }
601            }
602            PROTO_ID_INTERACTION_MODEL => {
603                if let Ok(opcode) = self.opcode::<im::OpCode>() {
604                    write!(f, "IM::{:?}", opcode)
605                } else {
606                    write!(f, "IM::{:02x}", self.proto_opcode)
607                }
608            }
609            PROTO_ID_BDX => {
610                if let Ok(opcode) = self.opcode::<bdx::OpCode>() {
611                    write!(f, "BDX::{:?}", opcode)
612                } else {
613                    write!(f, "BDX::{:02x}", self.proto_opcode)
614                }
615            }
616            _ => write!(f, "{:02x}::{:02x}", self.proto_id, self.proto_opcode),
617        }
618    }
619}
620
621#[cfg(feature = "defmt")]
622impl defmt::Format for MessageMeta {
623    fn format(&self, f: defmt::Formatter<'_>) {
624        match self.proto_id {
625            PROTO_ID_SECURE_CHANNEL => {
626                if let Ok(opcode) = self.opcode::<sc::OpCode>() {
627                    defmt::write!(f, "SC::{:?}", opcode)
628                } else {
629                    defmt::write!(f, "SC::{:02x}", self.proto_opcode)
630                }
631            }
632            PROTO_ID_INTERACTION_MODEL => {
633                if let Ok(opcode) = self.opcode::<im::OpCode>() {
634                    defmt::write!(f, "IM::{:?}", opcode)
635                } else {
636                    defmt::write!(f, "IM::{:02x}", self.proto_opcode)
637                }
638            }
639            PROTO_ID_BDX => {
640                if let Ok(opcode) = self.opcode::<bdx::OpCode>() {
641                    defmt::write!(f, "BDX::{:?}", opcode)
642                } else {
643                    defmt::write!(f, "BDX::{:02x}", self.proto_opcode)
644                }
645            }
646            _ => defmt::write!(f, "{:02x}::{:02x}", self.proto_id, self.proto_opcode),
647        }
648    }
649}
650
651/// An RX message pending on an `Exchange` instance.
652pub struct RxMessage<'a>(PacketAccess<'a, MAX_RX_BUF_SIZE>);
653
654impl RxMessage<'_> {
655    /// Get the meta-data of the pending message
656    pub fn meta(&self) -> MessageMeta {
657        MessageMeta::from(&self.0.header.proto)
658    }
659
660    /// Get the payload of the pending message
661    pub fn payload(&self) -> &[u8] {
662        &self.0.buf[self.0.payload_start..]
663    }
664}
665
666/// Accessor to the TX message buffer of the underlying Matter transport stack.
667///
668/// This is used to construct a new TX message to be sent on an `Exchange` instance.
669///
670/// NOTE: It is strongly advised to use the `TxMessage` accessor in combination with the `Sender` utility,
671/// which takes care of all message retransmission logic. Alternatively, one can use the
672/// `Exchange::send` or `Exchange::send_with` which also take care of re-transmissions.
673pub struct TxMessage<'a> {
674    exchange_id: ExchangeId,
675    matter: &'a Matter<'a>,
676    packet: PacketAccess<'a, MAX_TX_BUF_SIZE>,
677}
678
679impl TxMessage<'_> {
680    /// Get a reference to the payload buffer of the TX message being built
681    pub fn payload(&mut self) -> &mut [u8] {
682        &mut self.packet.buf[PacketHdr::HDR_RESERVE..MAX_TX_BUF_SIZE - PacketHdr::TAIL_RESERVE]
683    }
684
685    /// Complete and send a TX message by providing:
686    /// - The payload size that was filled-in by user code in the payload buffer returned by `TxMessage::payload`
687    /// - The TX message meta-data
688    pub fn complete<M>(
689        mut self,
690        payload_start: usize,
691        payload_end: usize,
692        meta: M,
693    ) -> Result<(), Error>
694    where
695        M: Into<MessageMeta>,
696    {
697        if payload_start > payload_end
698            || payload_end - payload_start
699                > MAX_TX_BUF_SIZE - PacketHdr::HDR_RESERVE - PacketHdr::TAIL_RESERVE
700        {
701            Err(ErrorCode::Invalid)?;
702        }
703
704        let meta: MessageMeta = meta.into();
705
706        self.packet.header.reset();
707
708        meta.set_into(&mut self.packet.header.proto);
709
710        self.matter.with_state(|state| {
711            let session = state
712                .sessions
713                .get(self.exchange_id.session_id())
714                .ok_or(ErrorCode::NoSession)?;
715
716            // The session's `peer_active_interval_ms` / `peer_idle_interval_ms`
717            // are seeded from our own `BasicInfoConfig` at session-creation
718            // time and overwritten once Sigma1 / Sigma2 / PBKDFParamRequest /
719            // PBKDFParamResponse delivers a real peer value, so MRP
720            // retransmission backoff to this peer reflects whichever is
721            // more accurate (Matter Core spec).
722            let (peer, retransmission) = session.pre_send(
723                Some(self.exchange_id.exchange_index()),
724                &mut self.packet.header,
725                Some(session.get_peer_active_interval_ms()),
726                Some(session.get_peer_idle_interval_ms()),
727            )?;
728
729            self.packet.peer = peer;
730            self.packet.payload_start = PacketHdr::HDR_RESERVE + payload_start;
731            self.packet
732                .buf
733                .truncate(PacketHdr::HDR_RESERVE + payload_end);
734            self.packet.tx_info.payload_state = TxPayloadState::NotEncoded {
735                session_id: session.id,
736            };
737            self.packet.tx_info.retransmission = retransmission;
738            self.packet.clear_on_drop(false);
739
740            Ok(())
741        })
742    }
743}
744
745/// Outcome from calling `Exchange::wait_tx`
746#[derive(Copy, Clone, Eq, PartialEq, Debug)]
747#[cfg_attr(feature = "defmt", derive(defmt::Format))]
748pub enum TxOutcome {
749    /// The other side has acknowledged the last message or the last message was not using the MRP protocol
750    /// Stop re-sending.
751    Done,
752    /// Need to re-send the last message.
753    Retransmit,
754}
755
756impl TxOutcome {
757    /// Check if the outcome is `Done`
758    pub const fn is_done(&self) -> bool {
759        matches!(self, Self::Done)
760    }
761}
762
763pub struct SenderTx<'a, 'b> {
764    sender: &'b mut Sender<'a>,
765    message: TxMessage<'a>,
766}
767
768impl SenderTx<'_, '_> {
769    pub fn split(&mut self) -> (&Exchange<'_>, &mut [u8]) {
770        (self.sender.exchange, self.message.payload())
771    }
772
773    pub fn payload(&mut self) -> &mut [u8] {
774        self.message.payload()
775    }
776
777    pub fn complete(
778        self,
779        payload_start: usize,
780        payload_end: usize,
781        meta: MessageMeta,
782    ) -> Result<(), Error> {
783        self.message.complete(payload_start, payload_end, meta)?;
784
785        self.sender.initial = false;
786
787        Ok(())
788    }
789}
790
791/// Utility struct for sending a message with potential retransmissions.
792pub struct Sender<'a> {
793    exchange: &'a Exchange<'a>,
794    initial: bool,
795    complete: bool,
796}
797
798impl<'a> Sender<'a> {
799    fn new(exchange: &'a Exchange<'a>) -> Result<Self, Error> {
800        exchange.id.check_no_pending_retrans(exchange.matter)?;
801
802        Ok(Self {
803            exchange,
804            initial: true,
805            complete: false,
806        })
807    }
808
809    /// Get the TX buffer of the underlying Matter stack for (re)constructing a new TX message,
810    /// waiting for the TX buffer to become available, if it is not.
811    ///
812    /// If the method returns `None`, it means that the message was already acknowledged by the other side,
813    /// or that the message does not need acknowledgement and re-transmissions.
814    ///
815    /// When called for the first time, the method will always return a `Some` value, as the message has not been sent even once yet.
816    /// Once the method returns `None`, it will always return `None` on subsequent calls, as the message has been acknowledged by the other side.
817    ///
818    /// Example:
819    /// ```ignore
820    /// let exchange = ...;
821    ///
822    /// let sender = exchange.sender()?;
823    ///
824    /// while let Some(mut tx) = sender.tx().await? {
825    ///     let (exchange, payload) = tx.split()?;
826    ///
827    ///     // Write the message payload in the `payload` `&mut [u8]` slice
828    ///     // On every iteration of the loop, write the _same_ payload (as message re-transmission is idempotent w.r.t. the message)
829    ///     ...
830    ///
831    ///     // Complete the payload by providing `MessageMeta`, payload start and payload end
832    ///     // On every iteration of the loop, proide the _same_ meta-data (as message re-transmission is idempotent w.r.t. the message)
833    ///     let meta = ...;
834    ///     let payload_start = ...;
835    ///     let payload_end = ...;
836    ///
837    ///     tx.complete(payload_start, payload_end, meta)?;
838    /// }
839    /// ```
840    pub async fn tx(&mut self) -> Result<Option<SenderTx<'a, '_>>, Error> {
841        trace!(
842            "Sender::tx called, initial={}, complete={}",
843            self.initial,
844            self.complete
845        );
846        if self.complete {
847            trace!("Sender::tx - already complete, returning None");
848            return Ok(None);
849        }
850
851        if !self.initial {
852            trace!("Sender::tx - not initial, calling wait_tx");
853            let outcome = self.exchange.id.wait_tx(self.exchange.matter).await?;
854            trace!("Sender::tx - wait_tx returned {:?}", outcome);
855            if outcome.is_done() {
856                // No need to re-transmit
857                self.complete = true;
858                trace!("Sender::tx - ACK received, returning None");
859                return Ok(None);
860            }
861            trace!("Sender::tx - need to retransmit");
862        }
863
864        let id = self.exchange.id;
865        let matter = self.exchange.matter;
866
867        trace!("Sender::tx - calling init_send");
868        let tx = id.init_send(matter).await?;
869        trace!("Sender::tx - init_send returned");
870
871        if self.initial || id.pending_retrans(matter)? {
872            trace!("Sender::tx - returning Some(SenderTx)");
873            Ok(Some(SenderTx {
874                sender: self,
875                message: tx,
876            }))
877        } else {
878            self.complete = true;
879            trace!("Sender::tx - no pending retrans, returning None");
880            Ok(None)
881        }
882    }
883}
884
885/// Owned-`Self` counterpart to [`SenderTx`].
886///
887/// Holds the [`Exchange`] by value (rather than via a `&mut` through
888/// the parent [`Sender`]), so consumers that consume their exchange
889/// can drive the retransmit loop without a self-referential struct.
890/// Returned by [`OwnedSender::tx`] when the framework needs the
891/// message bytes (re-)built into a fresh TX slot.
892///
893/// Pair with [`SenderTx`] for the borrowed-self mirror.
894pub struct OwnedSenderTx<'a> {
895    exchange: Exchange<'a>,
896    message: TxMessage<'a>,
897}
898
899impl<'a> OwnedSenderTx<'a> {
900    /// Get a `(borrowed-exchange, payload-slice)` pair for inspecting
901    /// the exchange and writing the message bytes.
902    pub fn split(&mut self) -> (&Exchange<'_>, &mut [u8]) {
903        (&self.exchange, self.message.payload())
904    }
905
906    /// Get a mutable reference to the payload slice the bytes go into.
907    pub fn payload(&mut self) -> &mut [u8] {
908        self.message.payload()
909    }
910
911    /// Commit the bytes currently in the slot. The framework dispatches
912    /// the wire-level send asynchronously; this call returns the
913    /// [`OwnedSender`] so the caller can ask for the next event
914    /// (retransmit, ACK).
915    pub fn complete(
916        self,
917        payload_start: usize,
918        payload_end: usize,
919        meta: MessageMeta,
920    ) -> Result<OwnedSender<'a>, Error> {
921        self.message.complete(payload_start, payload_end, meta)?;
922
923        Ok(OwnedSender {
924            exchange: self.exchange,
925            initial: false,
926            complete: false,
927        })
928    }
929}
930
931/// Owned-`Self` counterpart to [`Sender`].
932///
933/// Consumes the [`Exchange`] on construction (via
934/// [`Exchange::into_sender`]) and returns it back to the caller when
935/// the retransmit loop is done (i.e. when [`OwnedSender::tx`]
936/// completes with `Either::Right`, meaning the message has been
937/// ACK-ed by the peer or did not require ACK).
938///
939/// Use cases:
940/// - The Interaction Model client (`im::client::ImClient`) consumes
941///   an exchange to drive a single IM transaction end-to-end, and
942///   wants to walk the retransmit loop while still owning the
943///   `Exchange` for the subsequent receive phase. The owned-`Self`
944///   shape avoids a self-referential struct that would otherwise hold
945///   both an owned `Exchange` and a `&mut`-Sender on it.
946///
947/// User loop pattern (mirrors [`Sender`]):
948///
949/// ```ignore
950/// let mut sender = exchange.into_sender()?;
951/// let exchange = loop {
952///     match sender.tx().await? {
953///         Either::Left(mut slot) => {
954///             let (_, payload) = slot.split();
955///             // Write the message bytes (same every iteration —
956///             // retransmission is idempotent w.r.t. the message).
957///             let (start, end, meta) = /* … */;
958///             sender = slot.complete(start, end, meta)?;
959///         }
960///         Either::Right(exchange) => break exchange,
961///     }
962/// };
963/// // `exchange` is yours again.
964/// ```
965pub struct OwnedSender<'a> {
966    exchange: Exchange<'a>,
967    initial: bool,
968    complete: bool,
969}
970
971impl<'a> OwnedSender<'a> {
972    fn new(exchange: Exchange<'a>) -> Result<Self, Error> {
973        exchange.id.check_no_pending_retrans(exchange.matter)?;
974
975        Ok(Self {
976            exchange,
977            initial: true,
978            complete: false,
979        })
980    }
981
982    /// Next event from the framework. Owned-`Self` mirror of
983    /// [`Sender::tx`]:
984    /// - `Either::Left(slot)` — a TX slot is ready; (re-)build the
985    ///   message bytes into it and call `slot.complete(...)` to get
986    ///   the `OwnedSender` back for the next iteration.
987    /// - `Either::Right(exchange)` — the message has been ACK-ed (or
988    ///   did not need an ACK); the loop is done and the exchange is
989    ///   returned to the caller.
990    pub async fn tx(mut self) -> Result<EitherIo<OwnedSenderTx<'a>, Exchange<'a>>, Error> {
991        trace!(
992            "OwnedSender::tx called, initial={}, complete={}",
993            self.initial,
994            self.complete
995        );
996        if self.complete {
997            trace!("OwnedSender::tx - already complete, returning exchange");
998            return Ok(EitherIo::Right(self.exchange));
999        }
1000
1001        if !self.initial {
1002            trace!("OwnedSender::tx - not initial, calling wait_tx");
1003            let outcome = self.exchange.id.wait_tx(self.exchange.matter).await?;
1004            trace!("OwnedSender::tx - wait_tx returned {:?}", outcome);
1005            if outcome.is_done() {
1006                // No need to re-transmit
1007                self.complete = true;
1008                trace!("OwnedSender::tx - ACK received, returning exchange");
1009                return Ok(EitherIo::Right(self.exchange));
1010            }
1011            trace!("OwnedSender::tx - need to retransmit");
1012        }
1013
1014        let id = self.exchange.id;
1015        let matter = self.exchange.matter;
1016
1017        trace!("OwnedSender::tx - calling init_send");
1018        let tx = id.init_send(matter).await?;
1019        trace!("OwnedSender::tx - init_send returned");
1020
1021        if self.initial || id.pending_retrans(matter)? {
1022            trace!("OwnedSender::tx - returning Left(OwnedSenderTx)");
1023            Ok(EitherIo::Left(OwnedSenderTx {
1024                exchange: self.exchange,
1025                message: tx,
1026            }))
1027        } else {
1028            trace!("OwnedSender::tx - no pending retrans, returning exchange");
1029            Ok(EitherIo::Right(self.exchange))
1030        }
1031    }
1032}
1033
1034/// An exchange within a Matter stack, representing a session and an exchange within that session.
1035///
1036/// This is the main API for sending and receiving messages within the Matter stack.
1037/// Used by upper-level layers like the Secure Channel and Interaction Model.
1038pub struct Exchange<'a> {
1039    id: ExchangeId,
1040    matter: &'a Matter<'a>,
1041    rx: Option<RxMessage<'a>>,
1042}
1043
1044impl<'a> Exchange<'a> {
1045    pub(crate) const fn new(id: ExchangeId, matter: &'a Matter<'a>) -> Self {
1046        Self {
1047            id,
1048            matter,
1049            rx: None,
1050        }
1051    }
1052
1053    /// Get the Id of the exchange
1054    pub fn id(&self) -> ExchangeId {
1055        self.id
1056    }
1057
1058    /// Get the Matter stack instance associated with this exchange
1059    pub fn matter(&self) -> &'a Matter<'a> {
1060        self.matter
1061    }
1062
1063    /// Open an exchange over a CASE session to an already-commissioned node.
1064    ///
1065    /// If a CASE session for `(fabric_idx, peer_node_id)` already exists, an
1066    /// exchange is opened on it directly via [`Exchange::initiate_for_session`]
1067    /// (the common case - session and peer address reused, no mDNS). Otherwise
1068    /// the peer's operational address is resolved over mDNS and a fresh CASE
1069    /// session is established (driving [`crate::sc::case::CaseInitiator`]) before
1070    /// the exchange is opened on it; the peer's MRP/session parameters advertised
1071    /// in the mDNS TXT records seed the session.
1072    ///
1073    /// Establishing requires a running mDNS responder (e.g.
1074    /// `BuiltinMdns::run`) to service the resolve; without one the
1075    /// resolve times out and this returns [`ErrorCode::NotFound`].
1076    #[inline(always)]
1077    pub async fn initiate<C: Crypto>(
1078        matter: &'a Matter<'a>,
1079        crypto: C,
1080        fabric_idx: NonZeroU8,
1081        peer_node_id: NodeId,
1082    ) -> Result<Self, Error> {
1083        matter
1084            .transport
1085            .initiate(matter, crypto, fabric_idx, peer_node_id)
1086            .await
1087    }
1088
1089    /// Open an exchange over a **PASE** session to a not-yet-commissioned node
1090    /// at the given peer address (use-case 2).
1091    ///
1092    /// If a PASE session **to that peer** already exists, an exchange is opened on
1093    /// it directly via [`Exchange::initiate_for_session`]. Otherwise a new PASE
1094    /// session is established: a plaintext session is opened and the PASE protocol
1095    /// ([`crate::sc::pase::PaseInitiator`]) is run with `passcode`, then an
1096    /// exchange is opened on the resulting PASE session.
1097    ///
1098    /// Reuse is keyed by peer address (not a single global PASE session), so a
1099    /// commissioner can drive several concurrent commissionings.
1100    ///
1101    /// The peer's MRP/session parameters are negotiated by PASE itself
1102    /// (PBKDFParamRequest/Response).
1103    ///
1104    /// Discovery of the address is out of scope here (it may come from mDNS - see
1105    /// [`crate::transport::Transport::browse_commissionable`] - or from a BLE/BTP
1106    /// advertisement, etc.); this method is transport-agnostic and takes the
1107    /// already-known address.
1108    #[inline(always)]
1109    pub async fn initiate_pase<C: Crypto>(
1110        matter: &'a Matter<'a>,
1111        crypto: C,
1112        peer_addr: network::Address,
1113        passcode: u32,
1114    ) -> Result<Self, Error> {
1115        matter
1116            .transport
1117            .initiate_pase(matter, crypto, peer_addr, passcode)
1118            .await
1119    }
1120
1121    /// Create a new initiator exchange on the provided Matter stack for the provided session ID.
1122    #[inline(always)]
1123    pub fn initiate_for_session(matter: &'a Matter<'a>, session_id: u32) -> Result<Self, Error> {
1124        matter.transport().initiate_for_session(matter, session_id)
1125    }
1126
1127    /// Create a new initiator exchange on a new unsecured (plain-text) session to
1128    /// the given peer address.
1129    ///
1130    /// Low-level primitive below the three high-level entry points
1131    /// ([`Exchange::initiate`], [`Exchange::initiate_pase`],
1132    /// [`Exchange::initiate_for_session`]): the returned exchange carries the
1133    /// first handshake message of PASE ([`crate::sc::pase::PaseInitiator`]) or
1134    /// CASE ([`crate::sc::case::CaseInitiator`]). Use this only when driving a handshake protocol
1135    /// directly; otherwise prefer `initiate_pase` (which runs PASE for you) or
1136    /// `initiate` (CASE). If there is no space for a new session, an existing
1137    /// session is evicted and the operation retried.
1138    #[inline(always)]
1139    pub async fn initiate_unsecured<C: Crypto>(
1140        matter: &'a Matter<'a>,
1141        crypto: C,
1142        peer_addr: network::Address,
1143    ) -> Result<Self, Error> {
1144        matter
1145            .transport
1146            .initiate_plaintext(matter, crypto, peer_addr)
1147            .await
1148    }
1149
1150    /// Accepts a new responder exchange pending on the provided Matter stack.
1151    ///
1152    /// If there is no new pending responder exchange, the method will wait indefinitely until one appears.
1153    #[inline(always)]
1154    pub async fn accept(matter: &'a Matter<'a>) -> Result<Self, Error> {
1155        Self::accept_after(matter, 0).await
1156    }
1157
1158    /// Accepts a new responder exchange pending on the provided Matter stack, but only if the
1159    /// pending exchange was pending for longer than `received_timeout_ms`.
1160    ///
1161    /// If there is no new pending responder exchange, the method will wait indefinitely until one appears.
1162    pub async fn accept_after(
1163        matter: &'a Matter<'a>,
1164        received_timeout_ms: u32,
1165    ) -> Result<Self, Error> {
1166        if received_timeout_ms > 0 {
1167            loop {
1168                let mut accept = pin!(matter.transport().accept_if(matter, |_, exch, _| {
1169                    exch.mrp.has_rx_timed_out(received_timeout_ms as _)
1170                }));
1171
1172                let mut timer = pin!(Timer::after(embassy_time::Duration::from_millis(
1173                    received_timeout_ms as u64
1174                )));
1175
1176                if let Either::First(exchange) = select(&mut accept, &mut timer).await {
1177                    break exchange;
1178                }
1179            }
1180        } else {
1181            matter.transport().accept_if(matter, |_, _, _| true).await
1182        }
1183    }
1184
1185    /// Get access to the pending RX message on this exchange, and consume it when the returned `RxMessage` instance is dropped.
1186    ///
1187    /// If there is no pending RX message, the method will wait indefinitely until one appears.
1188    ///
1189    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1190    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1191    #[inline(always)]
1192    pub async fn recv(&mut self) -> Result<RxMessage<'_>, Error> {
1193        self.recv_fetch().await?;
1194
1195        self.rx.take().ok_or(ErrorCode::InvalidState.into())
1196    }
1197
1198    /// Get access to the pending RX message on this exchange, and consume it
1199    /// by copying the payload into the provided `WriteBuf` instance.
1200    ///
1201    /// A syntax sugar for calling ```self.recv().await?``` and then copying the payload.
1202    ///
1203    /// Returns the exchange message meta-data.
1204    ///
1205    /// If there is no pending RX message, the method will wait indefinitely until one appears.
1206    ///
1207    /// If there is already a pending RX message, which was already fetched using `Exchange::recv_fetch` and that
1208    /// message is not cleared yet using `Exchange::rx_done` or via some of the `Exchange::send*` methods,
1209    /// the method will return that message.
1210    ///
1211    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1212    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1213    #[inline(always)]
1214    pub async fn recv_into(&mut self, wb: &mut WriteBuf<'_>) -> Result<MessageMeta, Error> {
1215        let rx = self.recv().await?;
1216
1217        wb.reset();
1218        wb.append(rx.payload())?;
1219
1220        Ok(rx.meta())
1221    }
1222
1223    /// Return a _reference_ to the pending RX message on this exchange.
1224    ///
1225    /// If there is no pending RX message, the method will wait indefinitely until one appears.
1226    ///
1227    /// Unlike `recv` which returns the actual message object which - when dropped - allows the transport to
1228    /// fetch the _next_ RX message for this or other exchanges, `recv_fetch` keeps the received message around,
1229    /// which is convenient when the message needs to be examined / processed by multiple layers of application code.
1230    ///
1231    /// Note however that this does not come for free - keeping the RX message around means that the transport cannot receive
1232    /// _other_ RX messages which blocks the whole transport layer, as the transport layer uses a single RX message buffer.
1233    ///
1234    /// Therefore, calling `recv_fetch` should be done with care and the message should be marked as processed (and thus dropped) -
1235    /// via `rx_done` as soon as possible, ideally without `await`-ing between `recv_fetch` and `rx_done`
1236    ///
1237    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1238    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1239    #[inline(always)]
1240    pub async fn recv_fetch(&mut self) -> Result<&RxMessage<'a>, Error> {
1241        if self.rx.is_none() {
1242            let rx = self.id.recv(self.matter).await?;
1243
1244            self.rx = Some(rx);
1245        }
1246
1247        self.rx()
1248    }
1249
1250    /// Returns the RX message which was already fetched using a previous call to `recv_fetch`.
1251    /// If there is no fetched RX message, the method will fail with `ErrorCode::InvalidState`.
1252    ///
1253    /// This method only exists as a slight optimization for the cases where the user is sure, that there is
1254    /// an RX message already fetched with `recv_fetch`, as - unlike `recv_fetch` - this method does not `await` and hence
1255    /// variables used after calling `rx` do not have to be stored in the generated future.
1256    ///
1257    /// But in general and putting optimizations aside, it is always safe to replace calls to `rx` with calls to `recv_fetch`.
1258    #[inline(always)]
1259    pub fn rx(&self) -> Result<&RxMessage<'a>, Error> {
1260        self.rx.as_ref().ok_or(ErrorCode::InvalidState.into())
1261    }
1262
1263    /// Clears the RX message which was already fetched using a previous call to `recv_fetch`.
1264    /// If there is no fetched RX message, the method will do nothing.
1265    #[inline(always)]
1266    pub fn rx_done(&mut self) -> Result<(), Error> {
1267        self.rx = None;
1268
1269        Ok(())
1270    }
1271
1272    /// Gets access to the TX buffer of the Matter stack for constructing a new TX message.
1273    /// If the TX buffer is not available, the method will wait indefinitely until it becomes available.
1274    ///
1275    /// NOTE:
1276    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1277    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1278    ///
1279    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1280    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1281    #[inline(always)]
1282    pub async fn init_send(&mut self) -> Result<TxMessage<'_>, Error> {
1283        self.rx = None;
1284
1285        self.id.init_send(self.matter).await
1286    }
1287
1288    /// Waits until the other side acknowledges the last message sent on this exchange,
1289    /// or until time for a re-transmission had come.
1290    ///
1291    /// If the last sent message was not using the MRP protocol, the method will return immediately with `TxOutcome::Done`.
1292    ///
1293    /// NOTE:
1294    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1295    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1296    ///
1297    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1298    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1299    #[inline(always)]
1300    pub async fn wait_tx(&mut self) -> Result<TxOutcome, Error> {
1301        self.rx = None;
1302
1303        self.id.wait_tx(self.matter).await
1304    }
1305
1306    /// Returns `true` if there is a pending message re-transmission.
1307    /// A re-transmission will be pending if the last sent message was using the MRP protocol, and
1308    /// an acknowledgement for the other side is still pending.
1309    ///
1310    /// NOTE:
1311    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1312    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1313    ///
1314    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1315    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1316    pub fn pending_retrans(&self) -> Result<bool, Error> {
1317        self.id.pending_retrans(self.matter)
1318    }
1319
1320    /// Returns `true` if there is a pending message acknowledgement.
1321    /// An acknowledgement be pending if the last received message was using the MRP protocol, and we have to acknowledge it.
1322    ///
1323    /// NOTE:
1324    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1325    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1326    ///
1327    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1328    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1329    pub fn pending_ack(&self) -> Result<bool, Error> {
1330        self.id.pending_ack(self.matter)
1331    }
1332
1333    /// Acknowledge the last message received on this exchange (by sending a `MrpStandaloneAck`).
1334    ///
1335    /// If the last message was already acknowledged
1336    /// (either by a previous call to this method, by piggy-backing on a reliable message, or by the Matter stack itself),
1337    /// this method does nothing.
1338    ///
1339    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1340    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1341    #[inline(always)]
1342    pub async fn acknowledge(&mut self) -> Result<(), Error> {
1343        if self.pending_ack()? {
1344            let tx = self.id.init_send(self.matter).await?;
1345
1346            if self.pending_ack()? {
1347                // Check whether we still need to send an ACK.
1348                // Necessary because we `.await` above, and while we are awaiting, the transport
1349                // might automatically send an ACK for us.
1350                // (That is, if the global RX transport buffer happens to be already empty and if the other peer re-sends the message.)
1351                tx.complete::<MessageMeta>(0, 0, sc::OpCode::MRPStandAloneAck.into())?;
1352            }
1353        }
1354
1355        Ok(())
1356    }
1357
1358    /// Utility for sending a message on this exchange that automatically handles all re-transmission logic
1359    /// in case the constructed message needs to be send reliably.
1360    ///
1361    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1362    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1363    pub fn sender(&mut self) -> Result<Sender<'_>, Error> {
1364        self.rx = None;
1365
1366        Sender::new(self)
1367    }
1368
1369    /// Owned-`Self` counterpart to [`Exchange::sender`].
1370    ///
1371    /// Consumes the exchange and returns an [`OwnedSender`] that
1372    /// owns it for the lifetime of the retransmit loop. Once the
1373    /// loop completes ([`OwnedSender::tx`] returns
1374    /// `Either::Right(exchange)`), the exchange is handed back to
1375    /// the caller.
1376    ///
1377    /// Used by consumers — such as the IM client — that take an
1378    /// `Exchange` by value and need to drive a send + receive cycle
1379    /// in sequence without giving up ownership.
1380    pub fn into_sender(mut self) -> Result<OwnedSender<'a>, Error> {
1381        self.rx = None;
1382
1383        OwnedSender::new(self)
1384    }
1385
1386    /// Utility for sending a message on this exchange that automatically handles all re-transmission logic
1387    /// in case the constructed message needs to be send reliably.
1388    ///
1389    /// The message is constructed by the provided closure, which is given a `WriteBuf` instance to write the message payload into.
1390    ///
1391    /// Note that the closure is expected to construct the exact same message when called multiple times.
1392    ///
1393    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1394    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1395    pub async fn send_with<F>(&mut self, mut f: F) -> Result<(), Error>
1396    where
1397        F: FnMut(&Exchange, &mut WriteBuf) -> Result<Option<MessageMeta>, Error>,
1398    {
1399        let mut sender = self.sender()?;
1400
1401        while let Some(mut tx) = sender.tx().await? {
1402            let (exchange, payload) = tx.split();
1403
1404            let mut wb = WriteBuf::new(payload);
1405
1406            if let Some(meta) = f(exchange, &mut wb)? {
1407                let payload_start = wb.get_start();
1408                let payload_end = wb.get_tail();
1409                tx.complete(payload_start, payload_end, meta)?;
1410            } else {
1411                // Closure aborted sending
1412                break;
1413            }
1414        }
1415
1416        Ok(())
1417    }
1418
1419    /// Send the provided exchange meta-data and payload as part of this exchange.
1420    ///
1421    /// If the provided exchange meta-data indicates a reliable message, the message will be automatically re-transmitted until
1422    /// the other side acknowledges it.
1423    ///
1424    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1425    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1426    pub async fn send<M>(&mut self, meta: M, payload: &[u8]) -> Result<(), Error>
1427    where
1428        M: Into<MessageMeta>,
1429    {
1430        let meta = meta.into();
1431
1432        self.send_with(|_, wb| {
1433            wb.append(payload)?;
1434
1435            Ok(Some(meta))
1436        })
1437        .await
1438    }
1439
1440    pub(crate) fn accessor(&self) -> Result<Accessor<'a>, Error> {
1441        self.id.accessor(self.matter)
1442    }
1443
1444    pub fn is_groupcast(&self) -> Result<bool, Error> {
1445        self.with_state(|state| {
1446            Ok(matches!(
1447                self.id().session(&mut state.sessions).get_session_mode(),
1448                SessionMode::Group { .. }
1449            ))
1450        })
1451    }
1452
1453    pub(crate) fn with_state<F, T>(&self, f: F) -> Result<T, Error>
1454    where
1455        F: FnOnce(&mut MatterState) -> Result<T, Error>,
1456    {
1457        self.id.with_state(self.matter, f)
1458    }
1459
1460    pub(crate) fn with_state_ex<F, T, E>(&self, f: F) -> Result<T, E>
1461    where
1462        F: FnOnce(&mut MatterState) -> Result<T, E>,
1463        E: From<Error>,
1464    {
1465        self.id.with_state_ex(self.matter, f)
1466    }
1467}
1468
1469impl Drop for Exchange<'_> {
1470    fn drop(&mut self) {
1471        let closed = self.with_state(|state| {
1472            let sess = self.id().session(&mut state.sessions);
1473            let exch_index = self.id.exchange_index();
1474
1475            let closed = sess.remove_exch(exch_index);
1476            if closed {
1477                if matches!(sess.get_session_mode(), SessionMode::Group { .. })
1478                    && sess.exchanges.iter().all(Option::is_none)
1479                {
1480                    // Group session with no remaining exchanges — remove it
1481                    state.sessions.remove(self.id.session_id());
1482                    self.matter.transport().notify_session_removed();
1483                }
1484
1485                Ok(true)
1486            } else {
1487                Ok(false)
1488            }
1489        });
1490
1491        if !matches!(closed, Ok(true)) {
1492            self.matter.transport().exchange_dropped.notify();
1493        }
1494    }
1495}
1496
1497impl Display for Exchange<'_> {
1498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1499        write!(f, "{}", self.id)
1500    }
1501}
1502
1503#[cfg(test)]
1504mod tests {
1505    use super::*;
1506    use crate::crypto::test_only_crypto;
1507    use crate::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET};
1508    use crate::error::ErrorCode;
1509    use crate::transport::session::SessionMode;
1510    use crate::Matter;
1511    use futures_lite::future::block_on;
1512
1513    fn test_matter() -> Matter<'static> {
1514        Matter::new(&TEST_DEV_DET, TEST_DEV_COMM, &TEST_DEV_ATT, 0)
1515    }
1516
1517    fn fill_sessions(matter: &Matter<'_>, reserved: bool) {
1518        let dev_det = matter.dev_det();
1519        matter.with_state(|state| loop {
1520            if state
1521                .sessions
1522                .add(0, reserved, network::Address::new(), None, dev_det)
1523                .is_err()
1524            {
1525                break;
1526            }
1527        });
1528    }
1529
1530    #[test]
1531    fn test_initiate_unsecured_creates_initiator_exchange() {
1532        let matter = test_matter();
1533        let crypto = test_only_crypto();
1534        let peer = network::Address::new();
1535
1536        let exchange = block_on(Exchange::initiate_unsecured(&matter, &crypto, peer)).unwrap();
1537
1538        exchange
1539            .with_state(|state| {
1540                let sess = exchange.id().session(&mut state.sessions);
1541                let exch = exchange.id().exch(sess);
1542
1543                assert!(matches!(exch.role, Role::Initiator(_)));
1544                assert_eq!(sess.id, exchange.id().session_id());
1545                assert!(!sess.is_encrypted());
1546                assert_eq!(*sess.get_session_mode(), SessionMode::PlainText);
1547
1548                Ok(())
1549            })
1550            .unwrap();
1551    }
1552
1553    #[test]
1554    fn test_initiate_unsecured_retries_after_eviction() {
1555        let matter = test_matter();
1556        let crypto = test_only_crypto();
1557        let peer = network::Address::new();
1558
1559        fill_sessions(&matter, false);
1560
1561        let exchange = block_on(Exchange::initiate_unsecured(&matter, &crypto, peer)).unwrap();
1562
1563        exchange
1564            .with_state(|state| {
1565                let sess = exchange.id().session(&mut state.sessions);
1566                let exch = exchange.id().exch(sess);
1567
1568                assert!(matches!(exch.role, Role::Initiator(_)));
1569                assert_eq!(sess.id, exchange.id().session_id());
1570                assert!(!sess.is_encrypted());
1571                assert_eq!(*sess.get_session_mode(), SessionMode::PlainText);
1572
1573                Ok(())
1574            })
1575            .unwrap();
1576    }
1577
1578    #[test]
1579    fn test_initiate_unsecured_fails_when_no_session_can_be_evicted() {
1580        let matter = test_matter();
1581        let crypto = test_only_crypto();
1582        let peer = network::Address::new();
1583
1584        fill_sessions(&matter, true);
1585
1586        let result = block_on(Exchange::initiate_unsecured(&matter, &crypto, peer));
1587
1588        match result {
1589            Err(err) => assert!(matches!(err.code(), ErrorCode::NoSpaceSessions)),
1590            Ok(_) => panic!("expected NoSpaceSessions error"),
1591        }
1592    }
1593}