/* * * Copyright (c) 2020-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::common::echo_cluster; use core::borrow::Borrow; use core::future::pending; use core::time::Duration; use embassy_futures::select::select3; use rs_matter::{ acl::{AclEntry, AuthMode}, data_model::{ cluster_basic_information::{self, BasicInfoConfig}, cluster_on_off::{self, OnOffCluster}, device_types::{DEV_TYPE_ON_OFF_LIGHT, DEV_TYPE_ROOT_NODE}, objects::{ AttrData, AttrDataEncoder, AttrDetails, Endpoint, Handler, HandlerCompat, Metadata, Node, NonBlockingHandler, Privilege, }, root_endpoint::{self, RootEndpointHandler}, sdm::{ admin_commissioning, dev_att::{DataType, DevAttDataFetcher}, general_commissioning, noc, nw_commissioning, }, system_model::{ access_control, descriptor::{self, DescriptorCluster}, }, }, error::{Error, ErrorCode}, handler_chain_type, interaction_model::core::{OpCode, PROTO_ID_INTERACTION_MODEL}, mdns::DummyMdns, secure_channel::{self, common::PROTO_ID_SECURE_CHANNEL, spake2p::VerifierData}, tlv::{TLVWriter, TagType, ToTLV}, transport::{ core::PacketBuffers, packet::{Packet, MAX_RX_BUF_SIZE, MAX_TX_BUF_SIZE}, pipe::Pipe, }, transport::{ network::Address, session::{CaseDetails, CloneData, NocCatIds, SessionMode}, }, utils::select::{EitherUnwrap, Notification}, CommissioningData, Matter, MATTER_PORT, }; use super::echo_cluster::EchoCluster; const BASIC_INFO: BasicInfoConfig<'static> = BasicInfoConfig { vid: 10, pid: 11, hw_ver: 12, sw_ver: 13, sw_ver_str: "13", serial_no: "aabbccdd", device_name: "Test Device", }; struct DummyDevAtt; impl DevAttDataFetcher for DummyDevAtt { fn get_devatt_data(&self, _data_type: DataType, _data: &mut [u8]) -> Result { Ok(2) } } pub const IM_ENGINE_PEER_ID: u64 = 445566; pub const IM_ENGINE_REMOTE_PEER_ID: u64 = 123456; const NODE: Node<'static> = Node { id: 0, endpoints: &[ Endpoint { id: 0, clusters: &[ descriptor::CLUSTER, cluster_basic_information::CLUSTER, general_commissioning::CLUSTER, nw_commissioning::CLUSTER, admin_commissioning::CLUSTER, noc::CLUSTER, access_control::CLUSTER, echo_cluster::CLUSTER, ], device_type: DEV_TYPE_ROOT_NODE, }, Endpoint { id: 1, clusters: &[ descriptor::CLUSTER, cluster_on_off::CLUSTER, echo_cluster::CLUSTER, ], device_type: DEV_TYPE_ON_OFF_LIGHT, }, ], }; pub struct ImInput<'a> { action: OpCode, data: &'a dyn ToTLV, delay: Option, } impl<'a> ImInput<'a> { pub fn new(action: OpCode, data: &'a dyn ToTLV) -> Self { Self::new_delayed(action, data, None) } pub fn new_delayed(action: OpCode, data: &'a dyn ToTLV, delay: Option) -> Self { Self { action, data, delay, } } } pub struct ImOutput { pub action: OpCode, pub data: heapless::Vec, } pub struct ImEngineHandler<'a> { handler: handler_chain_type!(OnOffCluster, EchoCluster, DescriptorCluster<'static>, EchoCluster | RootEndpointHandler<'a>), } impl<'a> ImEngineHandler<'a> { pub fn new(matter: &'a Matter<'a>) -> Self { let handler = root_endpoint::handler(0, matter) .chain(0, echo_cluster::ID, EchoCluster::new(2, *matter.borrow())) .chain(1, descriptor::ID, DescriptorCluster::new(*matter.borrow())) .chain(1, echo_cluster::ID, EchoCluster::new(3, *matter.borrow())) .chain(1, cluster_on_off::ID, OnOffCluster::new(*matter.borrow())); Self { handler } } pub fn echo_cluster(&self, endpoint: u16) -> &EchoCluster { match endpoint { 0 => &self.handler.next.next.next.handler, 1 => &self.handler.next.handler, _ => panic!(), } } } impl<'a> Handler for ImEngineHandler<'a> { fn read(&self, attr: &AttrDetails, encoder: AttrDataEncoder) -> Result<(), Error> { self.handler.read(attr, encoder) } fn write(&self, attr: &AttrDetails, data: AttrData) -> Result<(), Error> { self.handler.write(attr, data) } fn invoke( &self, exchange: &rs_matter::transport::exchange::Exchange, cmd: &rs_matter::data_model::objects::CmdDetails, data: &rs_matter::tlv::TLVElement, encoder: rs_matter::data_model::objects::CmdDataEncoder, ) -> Result<(), Error> { self.handler.invoke(exchange, cmd, data, encoder) } } impl<'a> NonBlockingHandler for ImEngineHandler<'a> {} impl<'a> Metadata for ImEngineHandler<'a> { type MetadataGuard<'g> = Node<'g> where Self: 'g; fn lock(&self) -> Self::MetadataGuard<'_> { NODE } } static mut DNS: DummyMdns = DummyMdns; /// An Interaction Model Engine to facilitate easy testing pub struct ImEngine<'a> { pub matter: Matter<'a>, cat_ids: NocCatIds, } impl<'a> ImEngine<'a> { pub fn new_default() -> Self { Self::new(Default::default()) } /// Create the interaction model engine pub fn new(cat_ids: NocCatIds) -> Self { #[cfg(feature = "std")] use rs_matter::utils::epoch::sys_epoch as epoch; #[cfg(not(feature = "std"))] use rs_matter::utils::epoch::dummy_epoch as epoch; #[cfg(feature = "std")] use rs_matter::utils::rand::sys_rand as rand; #[cfg(not(feature = "std"))] use rs_matter::utils::rand::dummy_rand as rand; let matter = Matter::new( &BASIC_INFO, &DummyDevAtt, unsafe { &mut DNS }, epoch, rand, MATTER_PORT, ); Self { matter, cat_ids } } pub fn add_default_acl(&self) { // Only allow the standard peer node id of the IM Engine let mut default_acl = AclEntry::new(1, Privilege::ADMIN, AuthMode::Case); default_acl.add_subject(IM_ENGINE_PEER_ID).unwrap(); self.matter.acl_mgr.borrow_mut().add(default_acl).unwrap(); } pub fn handler(&self) -> ImEngineHandler<'_> { ImEngineHandler::new(&self.matter) } pub fn process( &self, handler: &ImEngineHandler, input: &[&ImInput], out: &mut heapless::Vec, ) -> Result<(), Error> { self.matter.reset_transport(); let clone_data = CloneData::new( IM_ENGINE_REMOTE_PEER_ID, IM_ENGINE_PEER_ID, 1, 1, Address::default(), SessionMode::Case(CaseDetails::new(1, &self.cat_ids)), ); let sess_idx = self .matter .session_mgr .borrow_mut() .clone_session(&clone_data) .unwrap(); let mut tx_pipe_buf = [0; MAX_RX_BUF_SIZE]; let mut rx_pipe_buf = [0; MAX_TX_BUF_SIZE]; let mut tx_buf = [0; MAX_RX_BUF_SIZE]; let mut rx_buf = [0; MAX_TX_BUF_SIZE]; let tx_pipe = Pipe::new(&mut tx_buf); let rx_pipe = Pipe::new(&mut rx_buf); let tx_pipe = &tx_pipe; let rx_pipe = &rx_pipe; let tx_pipe_buf = &mut tx_pipe_buf; let rx_pipe_buf = &mut rx_pipe_buf; let handler = &handler; let mut msg_ctr = self .matter .session_mgr .borrow_mut() .mut_by_index(sess_idx) .unwrap() .get_msg_ctr(); let resp_notif = Notification::new(); let resp_notif = &resp_notif; let mut buffers = PacketBuffers::new(); let buffers = &mut buffers; embassy_futures::block_on(async move { select3( self.matter.run_piped( buffers, tx_pipe, rx_pipe, CommissioningData { // TODO: Hard-coded for now verifier: VerifierData::new_with_pw(123456, *self.matter.borrow()), discriminator: 250, }, &HandlerCompat(handler), ), async move { let mut acknowledge = false; for ip in input { Self::send(ip, tx_pipe_buf, rx_pipe, msg_ctr, acknowledge).await?; resp_notif.wait().await; if let Some(delay) = ip.delay { if delay > 0 { #[cfg(feature = "std")] std::thread::sleep(Duration::from_millis(delay as _)); } } msg_ctr += 2; acknowledge = true; } pending::<()>().await; Ok(()) }, async move { out.clear(); while out.len() < input.len() { let (len, _) = tx_pipe.recv(rx_pipe_buf).await; let mut rx = Packet::new_rx(&mut rx_pipe_buf[..len]); rx.plain_hdr_decode()?; rx.proto_decode(IM_ENGINE_REMOTE_PEER_ID, Some(&[0u8; 16]))?; if rx.get_proto_id() != PROTO_ID_SECURE_CHANNEL || rx.get_proto_opcode::()? != secure_channel::common::OpCode::MRPStandAloneAck { out.push(ImOutput { action: rx.get_proto_opcode()?, data: heapless::Vec::from_slice(rx.as_slice()) .map_err(|_| ErrorCode::NoSpace)?, }) .map_err(|_| ErrorCode::NoSpace)?; resp_notif.signal(()); } } Ok(()) }, ) .await .unwrap() })?; Ok(()) } async fn send( input: &ImInput<'_>, tx_buf: &mut [u8], rx_pipe: &Pipe<'_>, msg_ctr: u32, acknowledge: bool, ) -> Result<(), Error> { let mut tx = Packet::new_tx(tx_buf); tx.set_proto_id(PROTO_ID_INTERACTION_MODEL); tx.set_proto_opcode(input.action as u8); let mut tw = TLVWriter::new(tx.get_writebuf()?); input.data.to_tlv(&mut tw, TagType::Anonymous)?; tx.plain.ctr = msg_ctr + 1; tx.plain.sess_id = 1; tx.proto.set_initiator(); if acknowledge { tx.proto.set_ack(msg_ctr - 1); } tx.proto_encode( Address::default(), Some(IM_ENGINE_REMOTE_PEER_ID), IM_ENGINE_PEER_ID, false, Some(&[0u8; 16]), )?; rx_pipe.send(Address::default(), tx.as_slice()).await; Ok(()) } }