Simplify main user-facing API

This commit is contained in:
ivmarkov 2023-06-09 07:47:49 +00:00
parent 8e9d8887da
commit 1b879f1a5b
9 changed files with 138 additions and 97 deletions

View file

@ -27,7 +27,6 @@ use matter::data_model::core::DataModel;
use matter::data_model::device_types::DEV_TYPE_ON_OFF_LIGHT; use matter::data_model::device_types::DEV_TYPE_ON_OFF_LIGHT;
use matter::data_model::objects::*; use matter::data_model::objects::*;
use matter::data_model::root_endpoint; use matter::data_model::root_endpoint;
use matter::data_model::sdm::dev_att::DevAttDataFetcher;
use matter::data_model::system_model::descriptor; use matter::data_model::system_model::descriptor;
use matter::error::Error; use matter::error::Error;
use matter::interaction_model::core::InteractionModel; use matter::interaction_model::core::InteractionModel;
@ -36,7 +35,7 @@ use matter::persist;
use matter::secure_channel::spake2p::VerifierData; use matter::secure_channel::spake2p::VerifierData;
use matter::transport::network::{Address, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use matter::transport::network::{Address, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use matter::transport::{ use matter::transport::{
mgr::RecvAction, mgr::TransportMgr, packet::MAX_RX_BUF_SIZE, packet::MAX_TX_BUF_SIZE, core::RecvAction, core::Transport, packet::MAX_RX_BUF_SIZE, packet::MAX_TX_BUF_SIZE,
udp::UdpListener, udp::UdpListener,
}; };
use matter::utils::select::EitherUnwrap; use matter::utils::select::EitherUnwrap;
@ -66,7 +65,7 @@ fn run() -> Result<(), Error> {
info!( info!(
"Matter memory: mDNS={}, Transport={} (of which Matter={})", "Matter memory: mDNS={}, Transport={} (of which Matter={})",
core::mem::size_of::<Mdns>(), core::mem::size_of::<Mdns>(),
core::mem::size_of::<TransportMgr>(), core::mem::size_of::<Transport>(),
core::mem::size_of::<Matter>(), core::mem::size_of::<Matter>(),
); );
@ -83,9 +82,11 @@ fn run() -> Result<(), Error> {
//let (mut mdns, mdns_runner) = (matter::mdns::astro::AstroMdns::new()?, core::future::pending::pending()); //let (mut mdns, mdns_runner) = (matter::mdns::astro::AstroMdns::new()?, core::future::pending::pending());
//let (mut mdns, mdns_runner) = (matter::mdns::DummyMdns {}, core::future::pending::pending()); //let (mut mdns, mdns_runner) = (matter::mdns::DummyMdns {}, core::future::pending::pending());
let dev_att = dev_att::HardCodedDevAtt::new();
let matter = Matter::new_default( let matter = Matter::new_default(
// vid/pid should match those in the DAC // vid/pid should match those in the DAC
&BasicInfoConfig { BasicInfoConfig {
vid: 0xFFF1, vid: 0xFFF1,
pid: 0x8000, pid: 0x8000,
hw_ver: 2, hw_ver: 2,
@ -94,12 +95,11 @@ fn run() -> Result<(), Error> {
serial_no: "aabbccdd", serial_no: "aabbccdd",
device_name: "OnOff Light", device_name: "OnOff Light",
}, },
&dev_att,
&mut mdns, &mut mdns,
matter::MATTER_PORT, matter::MATTER_PORT,
); );
let dev_att = dev_att::HardCodedDevAtt::new();
let psm_path = std::env::temp_dir().join("matter-iot"); let psm_path = std::env::temp_dir().join("matter-iot");
info!("Persisting from/to {}", psm_path.display()); info!("Persisting from/to {}", psm_path.display());
@ -115,7 +115,9 @@ fn run() -> Result<(), Error> {
matter.load_fabrics(data)?; matter.load_fabrics(data)?;
} }
matter.start( let mut transport = Transport::new(&matter);
transport.start(
CommissioningData { CommissioningData {
// TODO: Hard-coded for now // TODO: Hard-coded for now
verifier: VerifierData::new_with_pw(123456, *matter.borrow()), verifier: VerifierData::new_with_pw(123456, *matter.borrow()),
@ -125,10 +127,7 @@ fn run() -> Result<(), Error> {
)?; )?;
let matter = &matter; let matter = &matter;
let dev_att = &dev_att;
let mdns_runner = &mut mdns_runner; let mdns_runner = &mut mdns_runner;
let mut transport = TransportMgr::new(matter);
let transport = &mut transport; let transport = &mut transport;
let mut io_fut = pin!(async move { let mut io_fut = pin!(async move {
@ -165,7 +164,7 @@ fn run() -> Result<(), Error> {
], ],
}; };
let mut handler = handler(matter, dev_att); let mut handler = handler(matter);
let mut im = let mut im =
InteractionModel(DataModel::new(matter.borrow(), &node, &mut handler)); InteractionModel(DataModel::new(matter.borrow(), &node, &mut handler));
@ -180,11 +179,11 @@ fn run() -> Result<(), Error> {
} }
} }
if let Some(data) = matter.store_fabrics(&mut buf)? { if let Some(data) = transport.matter().store_fabrics(&mut buf)? {
psm.store("fabrics", data)?; psm.store("fabrics", data)?;
} }
if let Some(data) = matter.store_acls(&mut buf)? { if let Some(data) = transport.matter().store_acls(&mut buf)? {
psm.store("acls", data)?; psm.store("acls", data)?;
} }
} }
@ -202,8 +201,8 @@ fn run() -> Result<(), Error> {
Ok::<_, matter::error::Error>(()) Ok::<_, matter::error::Error>(())
} }
fn handler<'a>(matter: &'a Matter<'a>, dev_att: &'a dyn DevAttDataFetcher) -> impl Handler + 'a { fn handler<'a>(matter: &'a Matter<'a>) -> impl Handler + 'a {
root_endpoint::handler(0, dev_att, matter) root_endpoint::handler(0, matter)
.chain( .chain(
1, 1,
descriptor::ID, descriptor::ID,

View file

@ -19,7 +19,10 @@ use core::{borrow::Borrow, cell::RefCell};
use crate::{ use crate::{
acl::AclMgr, acl::AclMgr,
data_model::{cluster_basic_information::BasicInfoConfig, sdm::failsafe::FailSafe}, data_model::{
cluster_basic_information::BasicInfoConfig,
sdm::{dev_att::DevAttDataFetcher, failsafe::FailSafe},
},
error::*, error::*,
fabric::FabricMgr, fabric::FabricMgr,
mdns::{Mdns, MdnsMgr}, mdns::{Mdns, MdnsMgr},
@ -48,17 +51,24 @@ pub struct Matter<'a> {
pub mdns_mgr: RefCell<MdnsMgr<'a>>, pub mdns_mgr: RefCell<MdnsMgr<'a>>,
pub epoch: Epoch, pub epoch: Epoch,
pub rand: Rand, pub rand: Rand,
pub dev_det: &'a BasicInfoConfig<'a>, pub dev_det: BasicInfoConfig<'a>,
pub dev_att: &'a dyn DevAttDataFetcher,
pub port: u16,
} }
impl<'a> Matter<'a> { impl<'a> Matter<'a> {
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[inline(always)] #[inline(always)]
pub fn new_default(dev_det: &'a BasicInfoConfig, mdns: &'a mut dyn Mdns, port: u16) -> Self { pub fn new_default(
dev_det: BasicInfoConfig<'a>,
dev_att: &'a dyn DevAttDataFetcher,
mdns: &'a mut dyn Mdns,
port: u16,
) -> Self {
use crate::utils::epoch::sys_epoch; use crate::utils::epoch::sys_epoch;
use crate::utils::rand::sys_rand; use crate::utils::rand::sys_rand;
Self::new(dev_det, mdns, sys_epoch, sys_rand, port) Self::new(dev_det, dev_att, mdns, sys_epoch, sys_rand, port)
} }
/// Creates a new Matter object /// Creates a new Matter object
@ -69,7 +79,8 @@ impl<'a> Matter<'a> {
/// this object to return the device attestation details when queried upon. /// this object to return the device attestation details when queried upon.
#[inline(always)] #[inline(always)]
pub fn new( pub fn new(
dev_det: &'a BasicInfoConfig, dev_det: BasicInfoConfig<'a>,
dev_att: &'a dyn DevAttDataFetcher,
mdns: &'a mut dyn Mdns, mdns: &'a mut dyn Mdns,
epoch: Epoch, epoch: Epoch,
rand: Rand, rand: Rand,
@ -90,11 +101,21 @@ impl<'a> Matter<'a> {
epoch, epoch,
rand, rand,
dev_det, dev_det,
dev_att,
port,
} }
} }
pub fn dev_det(&self) -> &BasicInfoConfig { pub fn dev_det(&self) -> &BasicInfoConfig<'_> {
self.dev_det &self.dev_det
}
pub fn dev_att(&self) -> &dyn DevAttDataFetcher {
self.dev_att
}
pub fn port(&self) -> u16 {
self.port
} }
pub fn load_fabrics(&self, data: &[u8]) -> Result<(), Error> { pub fn load_fabrics(&self, data: &[u8]) -> Result<(), Error> {
@ -119,11 +140,15 @@ impl<'a> Matter<'a> {
self.acl_mgr.borrow().is_changed() || self.fabric_mgr.borrow().is_changed() self.acl_mgr.borrow().is_changed() || self.fabric_mgr.borrow().is_changed()
} }
pub fn start(&self, dev_comm: CommissioningData, buf: &mut [u8]) -> Result<(), Error> { pub fn start_comissioning(
let open_comm_window = self.fabric_mgr.borrow().is_empty(); &self,
if open_comm_window { dev_comm: CommissioningData,
buf: &mut [u8],
) -> Result<bool, Error> {
if !self.pase_mgr.borrow().is_pase_session_enabled() && self.fabric_mgr.borrow().is_empty()
{
print_pairing_code_and_qr( print_pairing_code_and_qr(
self.dev_det, &self.dev_det,
&dev_comm, &dev_comm,
DiscoveryCapabilities::default(), DiscoveryCapabilities::default(),
buf, buf,
@ -134,9 +159,11 @@ impl<'a> Matter<'a> {
dev_comm.discriminator, dev_comm.discriminator,
&mut self.mdns_mgr.borrow_mut(), &mut self.mdns_mgr.borrow_mut(),
)?; )?;
}
Ok(()) Ok(true)
} else {
Ok(false)
}
} }
} }

View file

@ -55,15 +55,11 @@ pub fn endpoint(id: EndptId) -> Endpoint<'static> {
} }
} }
pub fn handler<'a>( pub fn handler<'a>(endpoint_id: u16, matter: &'a Matter<'a>) -> RootEndpointHandler<'a> {
endpoint_id: u16,
dev_att: &'a dyn DevAttDataFetcher,
matter: &'a Matter<'a>,
) -> RootEndpointHandler<'a> {
wrap( wrap(
endpoint_id, endpoint_id,
matter.dev_det(), matter.dev_det(),
dev_att, matter.dev_att(),
matter.borrow(), matter.borrow(),
matter.borrow(), matter.borrow(),
matter.borrow(), matter.borrow(),

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
use core::cell::RefCell; use core::{borrow::Borrow, cell::RefCell};
use crate::{ use crate::{
error::*, error::*,
@ -24,7 +24,7 @@ use crate::{
secure_channel::common::*, secure_channel::common::*,
tlv, tlv,
transport::{proto_ctx::ProtoCtx, session::CloneData}, transport::{proto_ctx::ProtoCtx, session::CloneData},
utils::rand::Rand, utils::{epoch::Epoch, rand::Rand},
}; };
use log::{error, info}; use log::{error, info};
use num; use num;
@ -42,14 +42,32 @@ pub struct SecureChannel<'a> {
impl<'a> SecureChannel<'a> { impl<'a> SecureChannel<'a> {
#[inline(always)] #[inline(always)]
pub fn new( pub fn new<
T: Borrow<RefCell<FabricMgr>>
+ Borrow<RefCell<PaseMgr>>
+ Borrow<RefCell<MdnsMgr<'a>>>
+ Borrow<Epoch>
+ Borrow<Rand>,
>(
matter: &'a T,
) -> Self {
Self::wrap(
matter.borrow(),
matter.borrow(),
matter.borrow(),
*matter.borrow(),
)
}
#[inline(always)]
pub fn wrap(
pase: &'a RefCell<PaseMgr>, pase: &'a RefCell<PaseMgr>,
fabric_mgr: &'a RefCell<FabricMgr>, fabric: &'a RefCell<FabricMgr>,
mdns: &'a RefCell<MdnsMgr<'a>>, mdns: &'a RefCell<MdnsMgr<'a>>,
rand: Rand, rand: Rand,
) -> Self { ) -> Self {
SecureChannel { Self {
case: Case::new(fabric_mgr, rand), case: Case::new(fabric, rand),
pase, pase,
mdns, mdns,
} }

View file

@ -59,6 +59,10 @@ impl PaseMgr {
} }
} }
pub fn is_pase_session_enabled(&self) -> bool {
matches!(&self.state, PaseMgrState::Enabled(_, _, _))
}
pub fn enable_pase_session( pub fn enable_pase_session(
&mut self, &mut self,
verifier: VerifierData, verifier: VerifierData,

View file

@ -15,22 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
use core::borrow::Borrow;
use core::cell::RefCell;
use log::info; use log::info;
use crate::error::*; use crate::{error::*, CommissioningData, Matter};
use crate::fabric::FabricMgr;
use crate::mdns::MdnsMgr;
use crate::secure_channel::pake::PaseMgr;
use crate::secure_channel::common::PROTO_ID_SECURE_CHANNEL; use crate::secure_channel::common::PROTO_ID_SECURE_CHANNEL;
use crate::secure_channel::core::SecureChannel; use crate::secure_channel::core::SecureChannel;
use crate::transport::mrp::ReliableMessage; use crate::transport::mrp::ReliableMessage;
use crate::transport::{exchange, network::Address, packet::Packet}; use crate::transport::{exchange, network::Address, packet::Packet};
use crate::utils::epoch::Epoch;
use crate::utils::rand::Rand;
use super::proto_ctx::ProtoCtx; use super::proto_ctx::ProtoCtx;
use super::session::CloneData; use super::session::CloneData;
@ -50,7 +42,7 @@ pub enum RecvAction<'r, 'p> {
} }
pub struct RecvCompletion<'r, 'a, 'p> { pub struct RecvCompletion<'r, 'a, 'p> {
mgr: &'r mut TransportMgr<'a>, transport: &'r mut Transport<'a>,
rx: Packet<'p>, rx: Packet<'p>,
tx: Packet<'p>, tx: Packet<'p>,
state: RecvState, state: RecvState,
@ -69,20 +61,25 @@ impl<'r, 'a, 'p> RecvCompletion<'r, 'a, 'p> {
} }
fn maybe_next_action(&mut self) -> Result<Option<Option<RecvAction<'_, 'p>>>, Error> { fn maybe_next_action(&mut self) -> Result<Option<Option<RecvAction<'_, 'p>>>, Error> {
self.mgr.exch_mgr.purge(); self.transport.exch_mgr.purge();
self.tx.reset(); self.tx.reset();
let (state, next) = match core::mem::replace(&mut self.state, RecvState::New) { let (state, next) = match core::mem::replace(&mut self.state, RecvState::New) {
RecvState::New => { RecvState::New => {
self.mgr.exch_mgr.get_sess_mgr().decode(&mut self.rx)?; self.transport
.exch_mgr
.get_sess_mgr()
.decode(&mut self.rx)?;
(RecvState::OpenExchange, None) (RecvState::OpenExchange, None)
} }
RecvState::OpenExchange => match self.mgr.exch_mgr.recv(&mut self.rx) { RecvState::OpenExchange => match self.transport.exch_mgr.recv(&mut self.rx) {
Ok(Some(exch_ctx)) => { Ok(Some(exch_ctx)) => {
if self.rx.get_proto_id() == PROTO_ID_SECURE_CHANNEL { if self.rx.get_proto_id() == PROTO_ID_SECURE_CHANNEL {
let mut proto_ctx = ProtoCtx::new(exch_ctx, &self.rx, &mut self.tx); let mut proto_ctx = ProtoCtx::new(exch_ctx, &self.rx, &mut self.tx);
let (reply, clone_data) = self.mgr.secure_channel.handle(&mut proto_ctx)?; let mut secure_channel = SecureChannel::new(self.transport.matter);
let (reply, clone_data) = secure_channel.handle(&mut proto_ctx)?;
let state = if let Some(clone_data) = clone_data { let state = if let Some(clone_data) = clone_data {
RecvState::AddSession(clone_data) RecvState::AddSession(clone_data)
@ -115,15 +112,17 @@ impl<'r, 'a, 'p> RecvCompletion<'r, 'a, 'p> {
_ => Err(e)?, _ => Err(e)?,
}, },
}, },
RecvState::AddSession(clone_data) => match self.mgr.exch_mgr.add_session(&clone_data) { RecvState::AddSession(clone_data) => {
Ok(_) => (RecvState::Ack, None), match self.transport.exch_mgr.add_session(&clone_data) {
Err(e) => match e.code() { Ok(_) => (RecvState::Ack, None),
ErrorCode::NoSpace => (RecvState::EvictSession2(clone_data), None), Err(e) => match e.code() {
_ => Err(e)?, ErrorCode::NoSpace => (RecvState::EvictSession2(clone_data), None),
}, _ => Err(e)?,
}, },
}
}
RecvState::EvictSession => { RecvState::EvictSession => {
if self.mgr.exch_mgr.evict_session(&mut self.tx)? { if self.transport.exch_mgr.evict_session(&mut self.tx)? {
( (
RecvState::OpenExchange, RecvState::OpenExchange,
Some(Some(RecvAction::Send(self.tx.peer, self.tx.as_slice()))), Some(Some(RecvAction::Send(self.tx.peer, self.tx.as_slice()))),
@ -133,7 +132,7 @@ impl<'r, 'a, 'p> RecvCompletion<'r, 'a, 'p> {
} }
} }
RecvState::EvictSession2(clone_data) => { RecvState::EvictSession2(clone_data) => {
if self.mgr.exch_mgr.evict_session(&mut self.tx)? { if self.transport.exch_mgr.evict_session(&mut self.tx)? {
( (
RecvState::AddSession(clone_data), RecvState::AddSession(clone_data),
Some(Some(RecvAction::Send(self.tx.peer, self.tx.as_slice()))), Some(Some(RecvAction::Send(self.tx.peer, self.tx.as_slice()))),
@ -143,12 +142,12 @@ impl<'r, 'a, 'p> RecvCompletion<'r, 'a, 'p> {
} }
} }
RecvState::Ack => { RecvState::Ack => {
if let Some(exch_id) = self.mgr.exch_mgr.pending_ack() { if let Some(exch_id) = self.transport.exch_mgr.pending_ack() {
info!("Sending MRP Standalone ACK for exch {}", exch_id); info!("Sending MRP Standalone ACK for exch {}", exch_id);
ReliableMessage::prepare_ack(exch_id, &mut self.tx); ReliableMessage::prepare_ack(exch_id, &mut self.tx);
if self.mgr.exch_mgr.send(exch_id, &mut self.tx)? { if self.transport.exch_mgr.send(exch_id, &mut self.tx)? {
( (
RecvState::Ack, RecvState::Ack,
Some(Some(RecvAction::Send(self.tx.peer, self.tx.as_slice()))), Some(Some(RecvAction::Send(self.tx.peer, self.tx.as_slice()))),
@ -176,7 +175,7 @@ pub enum NotifyAction<'r, 'p> {
pub struct NotifyCompletion<'r, 'a, 'p> { pub struct NotifyCompletion<'r, 'a, 'p> {
// TODO // TODO
_mgr: &'r mut TransportMgr<'a>, _transport: &'r mut Transport<'a>,
_rx: &'r mut Packet<'p>, _rx: &'r mut Packet<'p>,
_tx: &'r mut Packet<'p>, _tx: &'r mut Packet<'p>,
_state: NotifyState, _state: NotifyState,
@ -199,38 +198,35 @@ impl<'r, 'a, 'p> NotifyCompletion<'r, 'a, 'p> {
} }
} }
pub struct TransportMgr<'a> { pub struct Transport<'a> {
matter: &'a Matter<'a>,
exch_mgr: exchange::ExchangeMgr, exch_mgr: exchange::ExchangeMgr,
secure_channel: SecureChannel<'a>,
} }
impl<'a> TransportMgr<'a> { impl<'a> Transport<'a> {
pub fn new< #[inline(always)]
T: Borrow<RefCell<FabricMgr>> pub fn new(matter: &'a Matter<'a>) -> Self {
+ Borrow<RefCell<PaseMgr>> let epoch = matter.epoch;
+ Borrow<RefCell<MdnsMgr<'a>>> let rand = matter.rand;
+ Borrow<Epoch>
+ Borrow<Rand>, Self {
>( matter,
matter: &'a T, exch_mgr: exchange::ExchangeMgr::new(epoch, rand),
) -> Self { }
Self::wrap(
SecureChannel::new(
matter.borrow(),
matter.borrow(),
matter.borrow(),
*matter.borrow(),
),
*matter.borrow(),
*matter.borrow(),
)
} }
pub fn wrap(secure_channel: SecureChannel<'a>, epoch: Epoch, rand: Rand) -> Self { pub fn matter(&self) -> &Matter<'a> {
Self { &self.matter
exch_mgr: exchange::ExchangeMgr::new(epoch, rand), }
secure_channel,
pub fn start(&mut self, dev_comm: CommissioningData, buf: &mut [u8]) -> Result<(), Error> {
info!("Starting Matter transport");
if self.matter().start_comissioning(dev_comm, buf)? {
info!("Comissioning started");
} }
Ok(())
} }
pub fn recv<'r, 'p>( pub fn recv<'r, 'p>(
@ -245,7 +241,7 @@ impl<'a> TransportMgr<'a> {
rx.peer = addr; rx.peer = addr;
RecvCompletion { RecvCompletion {
mgr: self, transport: self,
rx, rx,
tx, tx,
state: RecvState::New, state: RecvState::New,

View file

@ -279,6 +279,7 @@ pub struct ExchangeMgr {
pub const MAX_MRP_ENTRIES: usize = 4; pub const MAX_MRP_ENTRIES: usize = 4;
impl ExchangeMgr { impl ExchangeMgr {
#[inline(always)]
pub fn new(epoch: Epoch, rand: Rand) -> Self { pub fn new(epoch: Epoch, rand: Rand) -> Self {
Self { Self {
sess_mgr: SessionMgr::new(epoch, rand), sess_mgr: SessionMgr::new(epoch, rand),

View file

@ -15,9 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
pub mod core;
mod dedup; mod dedup;
pub mod exchange; pub mod exchange;
pub mod mgr;
pub mod mrp; pub mod mrp;
pub mod network; pub mod network;
pub mod packet; pub mod packet;

View file

@ -65,7 +65,7 @@ const BASIC_INFO: BasicInfoConfig<'static> = BasicInfoConfig {
device_name: "Test Device", device_name: "Test Device",
}; };
pub struct DummyDevAtt {} struct DummyDevAtt;
impl DevAttDataFetcher for DummyDevAtt { impl DevAttDataFetcher for DummyDevAtt {
fn get_devatt_data(&self, _data_type: DataType, _data: &mut [u8]) -> Result<usize, Error> { fn get_devatt_data(&self, _data_type: DataType, _data: &mut [u8]) -> Result<usize, Error> {
@ -110,7 +110,7 @@ pub fn matter(mdns: &mut dyn Mdns) -> Matter<'_> {
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use matter::utils::epoch::dummy_epoch as epoch; use matter::utils::epoch::dummy_epoch as epoch;
Matter::new(&BASIC_INFO, mdns, epoch, dummy_rand, 5540) Matter::new(BASIC_INFO, &DummyDevAtt, mdns, epoch, dummy_rand, 5540)
} }
/// An Interaction Model Engine to facilitate easy testing /// An Interaction Model Engine to facilitate easy testing
@ -161,7 +161,7 @@ impl<'a> ImEngine<'a> {
}, },
], ],
}, },
root_endpoint::handler(0, &DummyDevAtt {}, matter) root_endpoint::handler(0, matter)
.chain(0, echo_cluster::ID, EchoCluster::new(2, *matter.borrow())) .chain(0, echo_cluster::ID, EchoCluster::new(2, *matter.borrow()))
.chain(1, descriptor::ID, DescriptorCluster::new(*matter.borrow())) .chain(1, descriptor::ID, DescriptorCluster::new(*matter.borrow()))
.chain(1, echo_cluster::ID, EchoCluster::new(3, *matter.borrow())) .chain(1, echo_cluster::ID, EchoCluster::new(3, *matter.borrow()))