use core::future::Future;
use embassy_futures::select::select4;
use rs_matter::crypto::{Crypto, RngCore};
use rs_matter::dm::clusters::gen_comm::CommPolicy;
use rs_matter::dm::clusters::gen_diag::{GenDiag, NetifDiag};
use rs_matter::dm::clusters::net_comm::{DummyNetworks, NetworkType};
use rs_matter::dm::clusters::sw_diag::SwDiag;
use rs_matter::dm::endpoints::{eth_sys_handler, EthSysHandler, ROOT_ENDPOINT_ID};
use rs_matter::dm::networks::wireless::NoopWirelessNetCtl;
use rs_matter::dm::networks::NetChangeNotif;
use rs_matter::dm::{ChainedHandler, DataModel, Endpoint, EpClMatcher};
use rs_matter::error::Error;
use rs_matter::pairing::DiscoveryCapabilities;
use rs_matter::persist::{KvBlobStore, KvBlobStoreAccess};
use rs_matter::root_endpoint;
use rs_matter::transport::network::NoNetwork;
use rs_matter::utils::init::{init, init_from_closure, Init};
use rs_matter::utils::select::Coalesce;
use crate::mdns::Mdns;
use crate::nal::NetStack;
use crate::network::{Embedding, Network};
use crate::private::Sealed;
use crate::{pin_alloc, DummyAttrNotifier, MatterStack, UserTask};
pub struct Eth<E = ()> {
embedding: E,
}
impl<E> Sealed for Eth<E> {}
impl<E> Network for Eth<E>
where
E: Embedding,
{
const INIT: Self = Self { embedding: E::INIT };
type Embedding<'a>
= E
where
E: 'a;
type Networks = DummyNetworks;
const NETWORKS: Self::Networks = DummyNetworks;
fn init() -> impl Init<Self> {
init!(Self {
embedding <- E::init(),
})
}
fn init_networks() -> impl Init<Self::Networks> {
unsafe {
init_from_closure(|slot: *mut DummyNetworks| {
slot.write(DummyNetworks);
Ok(())
})
}
}
fn discovery_capabilities(&self) -> DiscoveryCapabilities {
DiscoveryCapabilities::IP
}
fn embedding(&self) -> &Self::Embedding<'_> {
&self.embedding
}
}
pub type EthMatterStack<'a, const B: usize, E = ()> = MatterStack<'a, B, Eth<E>>;
pub trait EthernetTask {
async fn run<S, N, M>(&mut self, net_stack: S, netif: N, mdns: M) -> Result<(), Error>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
M: Mdns;
}
impl<T> EthernetTask for &mut T
where
T: EthernetTask,
{
fn run<S, N, M>(
&mut self,
net_stack: S,
netif: N,
mdns: M,
) -> impl Future<Output = Result<(), Error>>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
M: Mdns,
{
(*self).run(net_stack, netif, mdns)
}
}
pub trait Ethernet {
async fn run<T>(&mut self, task: T) -> Result<(), Error>
where
T: EthernetTask;
}
impl<T> Ethernet for &mut T
where
T: Ethernet,
{
fn run<A>(&mut self, task: A) -> impl Future<Output = Result<(), Error>>
where
A: EthernetTask,
{
(*self).run(task)
}
}
pub struct PreexistingEthernet<S, N, M> {
stack: S,
netif: N,
mdns: M,
}
impl<S, N, M> PreexistingEthernet<S, N, M> {
pub const fn new(stack: S, netif: N, mdns: M) -> Self {
Self { stack, netif, mdns }
}
}
impl<S, N, M> Ethernet for PreexistingEthernet<S, N, M>
where
S: NetStack,
N: NetifDiag + NetChangeNotif,
M: Mdns,
{
async fn run<T>(&mut self, mut task: T) -> Result<(), Error>
where
T: EthernetTask,
{
task.run(&self.stack, &self.netif, &mut self.mdns).await
}
}
impl<const B: usize, E> MatterStack<'_, B, Eth<E>>
where
E: Embedding,
{
pub const fn root_endpoint() -> Endpoint<'static> {
const ENDPOINT: Endpoint<'static> = root_endpoint!(eth);
ENDPOINT
}
fn root_handler<'a>(
&self,
comm_policy: &'a dyn CommPolicy,
gen_diag: &'a dyn GenDiag,
netif_diag: &'a dyn NetifDiag,
sw_diag: &'a dyn SwDiag,
rand: impl RngCore + Copy,
) -> EthSysHandler<'a> {
eth_sys_handler(comm_policy, gen_diag, netif_diag, sw_diag, rand)
}
pub async fn reset<C, H, S>(&mut self, crypto: C, handler: H, store: S) -> Result<(), Error>
where
C: Crypto,
H: DataModel,
S: KvBlobStore,
{
let kv = self.matter.kv(store);
self.matter.factory_reset(&kv)?;
self.im(
crypto,
handler,
&kv,
NoopWirelessNetCtl::new(NetworkType::Ethernet),
)
.factory_reset()
.await
}
pub async fn startup<C, S>(&mut self, crypto: C, store: S) -> Result<(), Error>
where
C: Crypto,
S: KvBlobStore,
{
let kv = self.matter.kv(store);
self.matter.startup(&kv)?;
if !self.matter().has_fabrics() {
info!("Device is not commissioned yet, opening commissioning window...");
self.open_basic_comm_window(crypto, &DummyAttrNotifier)?;
} else {
info!("Device is already commissioned");
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn run_preex<'t, U, N, M, C, H, K, X>(
&'t self,
net_stack: U,
netif: N,
mdns: M,
crypto: C,
handler: H,
kv: K,
user: X,
) -> impl Future<Output = Result<(), Error>> + 't
where
U: NetStack + 't,
N: NetifDiag + NetChangeNotif + 't,
M: Mdns + 't,
C: Crypto + 't,
H: DataModel + 't,
K: KvBlobStoreAccess + 't,
X: UserTask + 't,
{
self.run(
PreexistingEthernet::new(net_stack, netif, mdns),
crypto,
handler,
kv,
user,
)
}
pub async fn run<N, C, H, K, X>(
&self,
mut ethernet: N,
crypto: C,
handler: H,
kv: K,
user: X,
) -> Result<(), Error>
where
N: Ethernet,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
X: 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_ethernet(&mut ethernet, crypto, handler, &kv, user)
);
net_task.await
}
fn run_ethernet<'t, N, C, H, K, X>(
&'t self,
ethernet: &'t mut N,
crypto: C,
handler: H,
kv: K,
user: X,
) -> impl Future<Output = Result<(), Error>> + 't
where
N: Ethernet + 't,
C: Crypto + 't,
H: DataModel + 't,
K: KvBlobStoreAccess + 't,
X: UserTask + 't,
{
Ethernet::run(
ethernet,
MatterStackEthernetTask {
stack: self,
crypto,
handler,
kv,
user_task: user,
},
)
}
}
struct MatterStackEthernetTask<'a, const B: usize, E, C, H, K, X>
where
E: Embedding,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
X: UserTask,
{
stack: &'a MatterStack<'a, B, Eth<E>>,
crypto: C,
handler: H,
kv: K,
user_task: X,
}
impl<const B: usize, E, C, H, K, X> EthernetTask for MatterStackEthernetTask<'_, B, E, C, H, K, X>
where
E: Embedding,
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
X: UserTask,
{
async fn run<N, I, M>(&mut self, net_stack: N, netif: I, mut mdns: M) -> Result<(), Error>
where
N: NetStack,
I: NetifDiag + NetChangeNotif,
M: Mdns,
{
info!("Ethernet driver started");
let sys = self
.stack
.root_handler(&false, &(), &netif, &(), 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,
NoopWirelessNetCtl::new(NetworkType::Ethernet),
);
let mut net_task = pin_alloc!(
self.stack.bump,
self.stack.run_oper_net(
&self.crypto,
&net_stack,
0, core::future::pending(),
Option::<(NoNetwork, NoNetwork)>::None,
)
);
let mut mdns_task = pin_alloc!(
self.stack.bump,
self.stack
.run_oper_netif_mdns(&self.crypto, &net_stack, &netif, &mut mdns)
);
let mut im_task = pin_alloc!(self.stack.bump, self.stack.run_im_with_bump(&im));
let mut user_task = pin_alloc!(self.stack.bump, self.user_task.run(&net_stack, &netif));
select4(&mut net_task, &mut mdns_task, &mut im_task, &mut user_task)
.coalesce()
.await
}
}