use core::future::Future;
use core::marker::PhantomData;
use core::pin::pin;
use embassy_futures::select::{select, select3, select4};
use rs_matter::crypto::{Crypto, RngCore};
use rs_matter::dm::clusters::gen_comm::CommPolicy;
use rs_matter::dm::clusters::gen_diag::GenDiag;
use rs_matter::dm::clusters::gen_diag::NetifDiag;
use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType};
use rs_matter::dm::clusters::sw_diag::SwDiag;
use rs_matter::dm::clusters::thread_diag::ThreadDiag;
use rs_matter::dm::clusters::time_sync::TimeSync;
use rs_matter::dm::endpoints::{thread_sys_handler, ThreadSysHandler, ROOT_ENDPOINT_ID};
use rs_matter::dm::networks::wireless::{self, NetCtlWithStatusImpl, NoopWirelessNetCtl};
use rs_matter::dm::networks::NetChangeNotif;
use rs_matter::dm::{ChainedHandler, DataModel, Endpoint, EpClMatcher};
use rs_matter::error::Error;
use rs_matter::persist::KvBlobStoreAccess;
use rs_matter::root_endpoint;
use rs_matter::transport::network::NoNetwork;
use rs_matter::utils::select::Coalesce;
use crate::mdns::Mdns;
use crate::nal::NetStack;
use crate::network::Embedding;
use crate::wireless::{GattPeripheral, GattTask, MatterStackWirelessTask, WirelessNetCtl};
use crate::{pin_alloc, UserTask};
use super::{Gatt, PreexistingWireless, WirelessMatterStack};
pub type ThreadMatterStack<'a, const B: usize, E = ()> =
WirelessMatterStack<'a, B, wireless::Thread, E>;
impl<const B: usize, E> WirelessMatterStack<'_, B, wireless::Thread, E>
where
E: Embedding,
{
#[allow(clippy::too_many_arguments)]
pub fn run_preex<'t, U, N, Q, D, G, C, H, K, X>(
&'t self,
net_stack: U,
netif: N,
net_ctl: Q,
mdns: D,
gatt: G,
crypto: C,
handler: H,
kv: K,
user: X,
) -> impl Future<Output = Result<(), Error>> + 't
where
U: NetStack + 't,
N: NetifDiag + NetChangeNotif + 't,
Q: NetCtl + ThreadDiag + NetChangeNotif + 't,
D: Mdns + 't,
G: GattPeripheral + 't,
C: Crypto + 't,
H: DataModel + 't,
K: KvBlobStoreAccess + 't,
X: UserTask + 't,
{
self.run_coex(
PreexistingWireless::new(net_stack, netif, net_ctl, mdns, gatt),
crypto,
handler,
kv,
user,
)
}
pub async fn run_coex<W, C, H, K, U>(
&self,
mut thread: W,
crypto: C,
handler: H,
kv: K,
user: U,
) -> Result<(), Error>
where
W: ThreadCoex,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
U: UserTask,
{
let _lock = self.run_lock.lock().await;
info!("Matter Stack memory: {}b", core::mem::size_of_val(self));
let _defer = scopeguard::guard((), |_| unsafe {
self.bump.reset();
});
self.matter().reset_transport()?;
let net_task = pin_alloc!(
self.bump,
self.run_thread_coex(&mut thread, crypto, handler, kv, user)
);
net_task.await
}
pub async fn run<W, C, H, K, U>(
&self,
thread: W,
crypto: C,
handler: H,
kv: K,
user: U,
) -> Result<(), Error>
where
W: Thread + Gatt,
K: KvBlobStoreAccess,
C: Crypto,
H: DataModel,
U: UserTask,
{
let _lock = self.run_lock.lock().await;
info!("Matter Stack memory: {}b", core::mem::size_of_val(self));
let _defer = scopeguard::guard((), |_| unsafe {
self.bump.reset();
});
self.matter().reset_transport()?;
let net_task = pin_alloc!(
self.bump,
self.run_thread(thread, crypto, handler, kv, user)
);
net_task.await
}
async fn run_thread_coex<W, C, H, K, U>(
&self,
thread: &mut W,
crypto: C,
handler: H,
kv: K,
user: U,
) -> Result<(), Error>
where
W: ThreadCoex,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
U: UserTask,
{
thread
.run(
MatterStackWirelessTask::<'_, _, _, _, _, _, _, _, NoopWirelessNetCtl> {
stack: self,
crypto,
handler,
kv: &kv,
user_task: user,
_net_ctl: PhantomData,
},
)
.await
}
async fn run_thread<W, C, H, K, U>(
&self,
mut thread: W,
crypto: C,
handler: H,
kv: K,
mut user: U,
) -> Result<(), Error>
where
W: Thread + Gatt,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
U: UserTask,
{
loop {
let commissioned = self.is_commissioned();
if !commissioned {
Gatt::run(
&mut thread,
MatterStackWirelessTask::<'_, _, _, _, _, _, _, _, <W as Thread>::NetCtl<'_>> {
stack: self,
crypto: &crypto,
handler: &handler,
kv: &kv,
user_task: &mut user,
_net_ctl: PhantomData,
},
)
.await?;
}
if commissioned {
let net_ctl = NetCtlWithStatusImpl::new(
&self.network.net_state,
WirelessNetCtl::<<W as Thread>::NetCtl<'_>>::Commissioning(NetworkType::Thread),
);
let sys =
self.root_handler(&false, &(), &(), &net_ctl, &(), &(), crypto.weak_rand()?);
let combined = ChainedHandler::new(
EpClMatcher::new(Some(ROOT_ENDPOINT_ID), None),
sys,
&handler,
);
let im = self.im(&crypto, (&handler, combined), &kv, &net_ctl);
im.close_comm_window()?;
}
Thread::run(
&mut thread,
MatterStackWirelessTask::<'_, _, _, _, _, _, _, _, <W as Thread>::NetCtl<'_>> {
stack: self,
crypto: &crypto,
handler: &handler,
kv: &kv,
user_task: &mut user,
_net_ctl: PhantomData,
},
)
.await?;
}
}
pub const fn root_endpoint() -> Endpoint<'static> {
const ENDPOINT: Endpoint<'static> = root_endpoint!(thread);
ENDPOINT
}
#[allow(clippy::too_many_arguments)]
fn root_handler<'a, N>(
&'a self,
comm_policy: &'a dyn CommPolicy,
gen_diag: &'a dyn GenDiag,
netif_diag: &'a dyn NetifDiag,
net_ctl: &'a N,
time_sync: &'a dyn TimeSync,
sw_diag: &'a dyn SwDiag,
rand: impl RngCore + Copy,
) -> ThreadSysHandler<'a, &'a N>
where
N: NetCtl + NetCtlStatus + ThreadDiag,
{
thread_sys_handler(
comm_policy,
gen_diag,
netif_diag,
net_ctl,
time_sync,
sw_diag,
net_ctl,
rand,
)
}
}
pub trait ThreadTask {
async fn run<S, N, C, M>(
&mut self,
net_stack: S,
netif: N,
net_ctl: C,
mdns: M,
) -> Result<(), Error>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
C: NetCtl + ThreadDiag + NetChangeNotif,
M: Mdns;
}
impl<T> ThreadTask for &mut T
where
T: ThreadTask,
{
fn run<S, N, C, M>(
&mut self,
net_stack: S,
netif: N,
net_ctl: C,
mdns: M,
) -> impl Future<Output = Result<(), Error>>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
C: NetCtl + ThreadDiag + NetChangeNotif,
M: Mdns,
{
T::run(*self, net_stack, netif, net_ctl, mdns)
}
}
pub trait Thread {
type NetCtl<'a>: NetCtl + ThreadDiag + NetChangeNotif
where
Self: 'a;
async fn run<T>(&mut self, task: T) -> Result<(), Error>
where
T: ThreadTask;
}
impl<T> Thread for &mut T
where
T: Thread,
{
type NetCtl<'a>
= T::NetCtl<'a>
where
Self: 'a;
fn run<A>(&mut self, task: A) -> impl Future<Output = Result<(), Error>>
where
A: ThreadTask,
{
T::run(self, task)
}
}
pub trait ThreadCoexTask {
async fn run<S, N, C, M, G>(
&mut self,
net_stack: S,
netif: N,
net_task: C,
mdns: M,
gatt: G,
) -> Result<(), Error>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
C: NetCtl + ThreadDiag + NetChangeNotif,
M: Mdns,
G: GattPeripheral;
}
impl<T> ThreadCoexTask for &mut T
where
T: ThreadCoexTask,
{
fn run<S, N, C, M, G>(
&mut self,
net_stack: S,
netif: N,
net_ctl: C,
mdns: M,
gatt: G,
) -> impl Future<Output = Result<(), Error>>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
C: NetCtl + ThreadDiag + NetChangeNotif,
M: Mdns,
G: GattPeripheral,
{
T::run(*self, net_stack, netif, net_ctl, mdns, gatt)
}
}
pub trait ThreadCoex {
async fn run<T>(&mut self, task: T) -> Result<(), Error>
where
T: ThreadCoexTask;
}
impl<T> ThreadCoex for &mut T
where
T: ThreadCoex,
{
fn run<A>(&mut self, task: A) -> impl Future<Output = Result<(), Error>>
where
A: ThreadCoexTask,
{
T::run(self, task)
}
}
impl<S, N, C, M, P> Thread for PreexistingWireless<S, N, C, M, P>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
C: NetCtl + ThreadDiag + NetChangeNotif,
M: Mdns,
{
type NetCtl<'a>
= &'a C
where
Self: 'a;
async fn run<T>(&mut self, mut task: T) -> Result<(), Error>
where
T: ThreadTask,
{
task.run(&self.net_stack, &self.netif, &self.net_ctl, &mut self.mdns)
.await
}
}
impl<S, N, C, M, P> ThreadCoex for PreexistingWireless<S, N, C, M, P>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
C: NetCtl + ThreadDiag + NetChangeNotif,
M: Mdns,
P: GattPeripheral,
{
async fn run<T>(&mut self, mut task: T) -> Result<(), Error>
where
T: ThreadCoexTask,
{
task.run(
&self.net_stack,
&self.netif,
&self.net_ctl,
&mut self.mdns,
&mut self.gatt,
)
.await
}
}
impl<'a, const B: usize, E, C, H, K, X, Q> GattTask
for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, K, X, Q>
where
E: Embedding,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
Q: NetCtl + ThreadDiag + NetChangeNotif,
{
async fn run<P>(&mut self, peripheral: P) -> Result<(), Error>
where
P: GattPeripheral,
{
let net_ctl = NetCtlWithStatusImpl::new(
&self.stack.network.net_state,
WirelessNetCtl::<Q>::Commissioning(NetworkType::Thread),
);
let sys = self.stack.root_handler(
&false,
&(),
&(),
&net_ctl,
&(),
&(),
self.crypto.weak_rand()?,
);
let combined = ChainedHandler::new(
EpClMatcher::new(Some(ROOT_ENDPOINT_ID), None),
sys,
&self.handler,
);
let im = self
.stack
.im(&self.crypto, (&self.handler, combined), &self.kv, &net_ctl);
let mut btp_task = pin!(self.stack.run_btp(&self.crypto, peripheral));
let mut im_task = pin!(self.stack.run_im(&im));
select(&mut btp_task, &mut im_task).coalesce().await
}
}
impl<'a, const B: usize, E, C, H, K, X, Z> ThreadTask
for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, K, X, Z>
where
E: Embedding,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
X: UserTask,
Z: NetCtl + ThreadDiag + NetChangeNotif,
{
async fn run<T, N, Q, D>(
&mut self,
net_stack: T,
netif: N,
net_ctl: Q,
mut mdns: D,
) -> Result<(), Error>
where
T: NetStack,
N: NetifDiag + NetChangeNotif,
Q: NetCtl + ThreadDiag + NetChangeNotif,
D: Mdns,
{
info!("Thread driver started");
let net_ctl_s = NetCtlWithStatusImpl::new(
&self.stack.network.net_state,
WirelessNetCtl::Operational(&net_ctl),
);
let sys = self.stack.root_handler(
&false,
&(),
&netif,
&net_ctl_s,
&(),
&(),
self.crypto.weak_rand()?,
);
let combined = ChainedHandler::new(
EpClMatcher::new(Some(ROOT_ENDPOINT_ID), None),
sys,
&self.handler,
);
let im = self.stack.im(
&self.crypto,
(&self.handler, combined),
&self.kv,
&net_ctl_s,
);
let stack = &self.stack;
let mut net_task = pin!(stack.run_oper_net(
&self.crypto,
&net_stack,
0, core::future::pending(),
Option::<(NoNetwork, NoNetwork)>::None
));
let mut mdns_task =
pin!(stack.run_oper_netif_mdns(&self.crypto, &net_stack, &netif, &mut mdns));
let deferred_connect_id = self.stack.network.net_state.lock(|state| {
let state = state.borrow();
state.is_prov_ready().then(|| state.network_id.clone())
});
if let Some(network_id) = deferred_connect_id {
info!("Non-concurrent commissioning: performing the deferred connect");
im.connect_once(&network_id).await?;
}
let mut im_task = pin!(self.stack.run_im(&im));
let mut user_task = pin!(self.user_task.run(&net_stack, &netif));
select4(&mut net_task, &mut mdns_task, &mut im_task, &mut user_task)
.coalesce()
.await
}
}
impl<'a, const B: usize, E, C, H, K, X, Z> ThreadCoexTask
for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, K, X, Z>
where
E: Embedding,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
X: UserTask,
Z: NetCtl + ThreadDiag + NetChangeNotif,
{
async fn run<T, N, Q, D, G>(
&mut self,
net_stack: T,
netif: N,
net_ctl: Q,
mut mdns: D,
mut gatt: G,
) -> Result<(), Error>
where
T: NetStack,
N: NetifDiag + NetChangeNotif,
Q: NetCtl + ThreadDiag + NetChangeNotif,
D: Mdns,
G: GattPeripheral,
{
info!("Thread and BLE drivers started");
let net_ctl_s = NetCtlWithStatusImpl::new(
&self.stack.network.net_state,
WirelessNetCtl::Operational(&net_ctl),
);
let sys = self.stack.root_handler(
&true,
&(),
&netif,
&net_ctl_s,
&(),
&(),
self.crypto.weak_rand()?,
);
let combined = ChainedHandler::new(
EpClMatcher::new(Some(ROOT_ENDPOINT_ID), None),
sys,
&self.handler,
);
let im = self.stack.im(
&self.crypto,
(&self.handler, combined),
&self.kv,
&net_ctl_s,
);
let stack = &self.stack;
let bump = &stack.bump;
let mut net_task = pin_alloc!(
bump,
stack.run_net_coex(&self.crypto, &net_stack, &netif, &mut mdns, &mut gatt)
);
let mut im_task = pin_alloc!(bump, self.stack.run_im_with_bump(&im));
let mut user_task = pin_alloc!(bump, self.user_task.run(&net_stack, &netif));
select3(&mut net_task, &mut im_task, &mut user_task)
.coalesce()
.await
}
}