Long reads and subscriptions reintroduced

This commit is contained in:
ivmarkov 2023-04-22 14:39:17 +00:00
parent 817d55aecc
commit fcc87bfaf4
28 changed files with 1372 additions and 739 deletions

View file

@ -15,7 +15,7 @@ name = "matter"
path = "src/lib.rs" path = "src/lib.rs"
[features] [features]
default = ["std", "crypto_mbedtls"] default = ["std", "crypto_mbedtls", "nightly"]
std = [] std = []
nightly = [] nightly = []
crypto_openssl = ["openssl", "foreign-types", "hmac", "sha2"] crypto_openssl = ["openssl", "foreign-types", "hmac", "sha2"]

View file

@ -179,14 +179,14 @@ impl<'a> Accessor<'a> {
let _ = subject.add_catid(i); let _ = subject.add_catid(i);
} }
} }
Accessor::new(c.fab_idx, subject, AuthMode::Case, &acl_mgr) Accessor::new(c.fab_idx, subject, AuthMode::Case, acl_mgr)
} }
SessionMode::Pase => { SessionMode::Pase => {
Accessor::new(0, AccessorSubjects::new(1), AuthMode::Pase, &acl_mgr) Accessor::new(0, AccessorSubjects::new(1), AuthMode::Pase, acl_mgr)
} }
SessionMode::PlainText => { SessionMode::PlainText => {
Accessor::new(0, AccessorSubjects::new(1), AuthMode::Invalid, &acl_mgr) Accessor::new(0, AccessorSubjects::new(1), AuthMode::Invalid, acl_mgr)
} }
} }
} }
@ -514,7 +514,7 @@ impl AclMgr {
let mut wb = WriteBuf::new(&mut buf); let mut wb = WriteBuf::new(&mut buf);
let mut tw = TLVWriter::new(&mut wb); let mut tw = TLVWriter::new(&mut wb);
self.entries.to_tlv(&mut tw, TagType::Anonymous)?; self.entries.to_tlv(&mut tw, TagType::Anonymous)?;
psm.set_kv_slice(ACL_KV_ENTRY, wb.into_slice())?; psm.set_kv_slice(ACL_KV_ENTRY, wb.as_slice())?;
self.changed = false; self.changed = false;
} }
@ -546,7 +546,7 @@ impl AclMgr {
let mut wb = WriteBuf::new(&mut buf); let mut wb = WriteBuf::new(&mut buf);
let mut tw = TLVWriter::new(&mut wb); let mut tw = TLVWriter::new(&mut wb);
self.entries.to_tlv(&mut tw, TagType::Anonymous)?; self.entries.to_tlv(&mut tw, TagType::Anonymous)?;
psm.set_kv_slice(ACL_KV_ENTRY, wb.into_slice()).await?; psm.set_kv_slice(ACL_KV_ENTRY, wb.as_slice()).await?;
self.changed = false; self.changed = false;
} }
@ -561,10 +561,7 @@ impl AclMgr {
{ {
let mut buf = [0u8; ACL_KV_MAX_SIZE]; let mut buf = [0u8; ACL_KV_MAX_SIZE];
let acl_tlvs = psm.get_kv_slice(ACL_KV_ENTRY, &mut buf).await?; let acl_tlvs = psm.get_kv_slice(ACL_KV_ENTRY, &mut buf).await?;
let root = TLVList::new(&acl_tlvs) let root = TLVList::new(acl_tlvs).iter().next().ok_or(Error::Invalid)?;
.iter()
.next()
.ok_or(Error::Invalid)?;
self.entries = AclEntries::from_tlv(&root)?; self.entries = AclEntries::from_tlv(&root)?;
self.changed = false; self.changed = false;

View file

@ -597,7 +597,7 @@ impl Cert {
let mut wb = WriteBuf::new(buf); let mut wb = WriteBuf::new(buf);
let mut tw = TLVWriter::new(&mut wb); let mut tw = TLVWriter::new(&mut wb);
self.to_tlv(&mut tw, TagType::Anonymous)?; self.to_tlv(&mut tw, TagType::Anonymous)?;
Ok(wb.into_slice().len()) Ok(wb.as_slice().len())
} }
pub fn as_asn1(&self, buf: &mut [u8]) -> Result<usize, Error> { pub fn as_asn1(&self, buf: &mut [u8]) -> Result<usize, Error> {
@ -823,7 +823,7 @@ mod tests {
let mut wb = WriteBuf::new(&mut buf); let mut wb = WriteBuf::new(&mut buf);
let mut tw = TLVWriter::new(&mut wb); let mut tw = TLVWriter::new(&mut wb);
cert.to_tlv(&mut tw, TagType::Anonymous).unwrap(); cert.to_tlv(&mut tw, TagType::Anonymous).unwrap();
assert_eq!(*input, wb.into_slice()); assert_eq!(*input, wb.as_slice());
} }
} }

View file

@ -19,11 +19,11 @@ use core::convert::TryInto;
use super::objects::*; use super::objects::*;
use crate::{attribute_enum, error::Error, utils::rand::Rand}; use crate::{attribute_enum, error::Error, utils::rand::Rand};
use strum::{EnumDiscriminants, FromRepr}; use strum::FromRepr;
pub const ID: u32 = 0x0028; pub const ID: u32 = 0x0028;
#[derive(Clone, Copy, Debug, FromRepr, EnumDiscriminants)] #[derive(Clone, Copy, Debug, FromRepr)]
#[repr(u16)] #[repr(u16)]
pub enum Attributes { pub enum Attributes {
DMRevision(AttrType<u8>) = 0, DMRevision(AttrType<u8>) = 0,
@ -37,6 +37,16 @@ pub enum Attributes {
attribute_enum!(Attributes); attribute_enum!(Attributes);
pub enum AttributesDiscriminants {
DMRevision = 0,
VendorId = 2,
ProductId = 4,
HwVer = 7,
SwVer = 9,
SwVerString = 0xa,
SerialNo = 0x0f,
}
#[derive(Default)] #[derive(Default)]
pub struct BasicInfoConfig<'a> { pub struct BasicInfoConfig<'a> {
pub vid: u16, pub vid: u16,

View file

@ -15,17 +15,26 @@
* limitations under the License. * limitations under the License.
*/ */
use core::cell::RefCell; use core::{
cell::RefCell,
sync::atomic::{AtomicU32, Ordering},
};
use super::objects::*; use super::objects::*;
use crate::{ use crate::{
acl::{Accessor, AclMgr}, acl::{Accessor, AclMgr},
error::*, error::*,
interaction_model::core::{Interaction, Transaction}, interaction_model::{
tlv::TLVWriter, core::{Interaction, Transaction},
messages::msg::SubscribeResp,
},
tlv::{TLVWriter, TagType, ToTLV},
transport::packet::Packet, transport::packet::Packet,
}; };
// TODO: For now...
static SUBS_ID: AtomicU32 = AtomicU32::new(1);
pub struct DataModel<'a, T> { pub struct DataModel<'a, T> {
pub acl_mgr: &'a RefCell<AclMgr>, pub acl_mgr: &'a RefCell<AclMgr>,
pub node: &'a Node<'a>, pub node: &'a Node<'a>,
@ -43,7 +52,7 @@ impl<'a, T> DataModel<'a, T> {
pub fn handle( pub fn handle(
&mut self, &mut self,
interaction: &Interaction, interaction: Interaction,
tx: &mut Packet, tx: &mut Packet,
transaction: &mut Transaction, transaction: &mut Transaction,
) -> Result<bool, Error> ) -> Result<bool, Error>
@ -55,44 +64,89 @@ impl<'a, T> DataModel<'a, T> {
match interaction { match interaction {
Interaction::Read(req) => { Interaction::Read(req) => {
for item in self.node.read(req, &accessor) { let mut resume_path = None;
AttrDataEncoder::handle_read(item, &self.handler, &mut tw)?;
for item in self.node.read(&req, &accessor) {
if let Some(path) = AttrDataEncoder::handle_read(item, &self.handler, &mut tw)?
{
resume_path = Some(path);
break;
}
} }
req.complete(tx, transaction, resume_path)
} }
Interaction::Write(req) => { Interaction::Write(req) => {
for item in self.node.write(req, &accessor) { for item in self.node.write(&req, &accessor) {
AttrDataEncoder::handle_write(item, &mut self.handler, &mut tw)?; AttrDataEncoder::handle_write(item, &mut self.handler, &mut tw)?;
} }
req.complete(tx, transaction)
} }
Interaction::Invoke(req) => { Interaction::Invoke(req) => {
for item in self.node.invoke(req, &accessor) { for item in self.node.invoke(&req, &accessor) {
CmdDataEncoder::handle(item, &mut self.handler, transaction, &mut tw)?; CmdDataEncoder::handle(item, &mut self.handler, transaction, &mut tw)?;
} }
req.complete(tx, transaction)
} }
Interaction::Subscribe(req) => { Interaction::Subscribe(req) => {
for item in self.node.subscribing_read(req, &accessor) { let mut resume_path = None;
AttrDataEncoder::handle_read(item, &self.handler, &mut tw)?;
}
}
Interaction::Status(_resp) => {
todo!()
// for item in self.node.subscribing_read(req, &accessor) {
// AttrDataEncoder::handle_read(item, &self.handler, &mut tw)?;
// }
}
Interaction::Timed(_) => (),
}
interaction.complete_tx(tx, transaction) for item in self.node.subscribing_read(&req, &accessor) {
if let Some(path) = AttrDataEncoder::handle_read(item, &self.handler, &mut tw)?
{
resume_path = Some(path);
break;
}
}
req.complete(tx, transaction, resume_path)
}
Interaction::Timed(_) => Ok(false),
Interaction::ResumeRead(req) => {
let mut resume_path = None;
for item in self.node.resume_read(&req, &accessor) {
if let Some(path) = AttrDataEncoder::handle_read(item, &self.handler, &mut tw)?
{
resume_path = Some(path);
break;
}
}
req.complete(tx, transaction, resume_path)
}
Interaction::ResumeSubscribe(req) => {
let mut resume_path = None;
if req.resume_path.is_some() {
for item in self.node.resume_subscribing_read(&req, &accessor) {
if let Some(path) =
AttrDataEncoder::handle_read(item, &self.handler, &mut tw)?
{
resume_path = Some(path);
break;
}
}
} else {
// TODO
let resp = SubscribeResp::new(SUBS_ID.fetch_add(1, Ordering::SeqCst), 40);
resp.to_tlv(&mut tw, TagType::Anonymous)?;
}
req.complete(tx, transaction, resume_path)
}
}
} }
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub async fn handle_async<'p>( pub async fn handle_async<'p>(
&mut self, &mut self,
interaction: &Interaction<'_>, interaction: Interaction<'_>,
tx: &'p mut Packet<'_>, tx: &'p mut Packet<'_>,
transaction: &mut Transaction<'_, '_>, transaction: &mut Transaction<'_, '_>,
) -> Result<Option<&'p [u8]>, Error> ) -> Result<bool, Error>
where where
T: super::objects::asynch::AsyncHandler, T: super::objects::asynch::AsyncHandler,
{ {
@ -101,32 +155,91 @@ impl<'a, T> DataModel<'a, T> {
match interaction { match interaction {
Interaction::Read(req) => { Interaction::Read(req) => {
for item in self.node.read(req, &accessor) { let mut resume_path = None;
AttrDataEncoder::handle_read_async(item, &self.handler, &mut tw).await?;
for item in self.node.read(&req, &accessor) {
if let Some(path) =
AttrDataEncoder::handle_read_async(item, &self.handler, &mut tw).await?
{
resume_path = Some(path);
break;
}
} }
req.complete(tx, transaction, resume_path)
} }
Interaction::Write(req) => { Interaction::Write(req) => {
for item in self.node.write(req, &accessor) { for item in self.node.write(&req, &accessor) {
AttrDataEncoder::handle_write_async(item, &mut self.handler, &mut tw).await?; AttrDataEncoder::handle_write_async(item, &mut self.handler, &mut tw).await?;
} }
req.complete(tx, transaction)
} }
Interaction::Invoke(req) => { Interaction::Invoke(req) => {
for item in self.node.invoke(req, &accessor) { for item in self.node.invoke(&req, &accessor) {
CmdDataEncoder::handle_async(item, &mut self.handler, transaction, &mut tw) CmdDataEncoder::handle_async(item, &mut self.handler, transaction, &mut tw)
.await?; .await?;
} }
}
Interaction::Timed(_) => (),
}
interaction.complete_tx(tx, transaction) req.complete(tx, transaction)
}
Interaction::Subscribe(req) => {
let mut resume_path = None;
for item in self.node.subscribing_read(&req, &accessor) {
if let Some(path) =
AttrDataEncoder::handle_read_async(item, &self.handler, &mut tw).await?
{
resume_path = Some(path);
break;
}
}
req.complete(tx, transaction, resume_path)
}
Interaction::Timed(_) => Ok(false),
Interaction::ResumeRead(req) => {
let mut resume_path = None;
for item in self.node.resume_read(&req, &accessor) {
if let Some(path) =
AttrDataEncoder::handle_read_async(item, &self.handler, &mut tw).await?
{
resume_path = Some(path);
break;
}
}
req.complete(tx, transaction, resume_path)
}
Interaction::ResumeSubscribe(req) => {
let mut resume_path = None;
if req.resume_path.is_some() {
for item in self.node.resume_subscribing_read(&req, &accessor) {
if let Some(path) =
AttrDataEncoder::handle_read_async(item, &self.handler, &mut tw).await?
{
resume_path = Some(path);
break;
}
}
} else {
// TODO
let resp = SubscribeResp::new(SUBS_ID.fetch_add(1, Ordering::SeqCst), 40);
resp.to_tlv(&mut tw, TagType::Anonymous)?;
}
req.complete(tx, transaction, resume_path)
}
}
} }
} }
pub trait DataHandler { pub trait DataHandler {
fn handle( fn handle(
&mut self, &mut self,
interaction: &Interaction, interaction: Interaction,
tx: &mut Packet, tx: &mut Packet,
transaction: &mut Transaction, transaction: &mut Transaction,
) -> Result<bool, Error>; ) -> Result<bool, Error>;
@ -138,7 +251,7 @@ where
{ {
fn handle( fn handle(
&mut self, &mut self,
interaction: &Interaction, interaction: Interaction,
tx: &mut Packet, tx: &mut Packet,
transaction: &mut Transaction, transaction: &mut Transaction,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
@ -152,7 +265,7 @@ where
{ {
fn handle( fn handle(
&mut self, &mut self,
interaction: &Interaction, interaction: Interaction,
tx: &mut Packet, tx: &mut Packet,
transaction: &mut Transaction, transaction: &mut Transaction,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
@ -172,24 +285,24 @@ pub mod asynch {
use super::DataModel; use super::DataModel;
pub trait AsyncDataHandler { pub trait AsyncDataHandler {
async fn handle<'p>( async fn handle(
&mut self, &mut self,
interaction: &Interaction, interaction: Interaction<'_>,
tx: &'p mut Packet, tx: &mut Packet,
transaction: &mut Transaction, transaction: &mut Transaction,
) -> Result<Option<&'p [u8]>, Error>; ) -> Result<bool, Error>;
} }
impl<T> AsyncDataHandler for &mut T impl<T> AsyncDataHandler for &mut T
where where
T: AsyncDataHandler, T: AsyncDataHandler,
{ {
async fn handle<'p>( async fn handle(
&mut self, &mut self,
interaction: &Interaction<'_>, interaction: Interaction<'_>,
tx: &'p mut Packet<'_>, tx: &mut Packet<'_>,
transaction: &mut Transaction<'_, '_>, transaction: &mut Transaction<'_, '_>,
) -> Result<Option<&'p [u8]>, Error> { ) -> Result<bool, Error> {
(**self).handle(interaction, tx, transaction).await (**self).handle(interaction, tx, transaction).await
} }
} }
@ -198,12 +311,12 @@ pub mod asynch {
where where
T: AsyncHandler, T: AsyncHandler,
{ {
async fn handle<'p>( async fn handle(
&mut self, &mut self,
interaction: &Interaction<'_>, interaction: Interaction<'_>,
tx: &'p mut Packet<'_>, tx: &mut Packet<'_>,
transaction: &mut Transaction<'_, '_>, transaction: &mut Transaction<'_, '_>,
) -> Result<Option<&'p [u8]>, Error> { ) -> Result<bool, Error> {
DataModel::handle_async(self, interaction, tx, transaction).await DataModel::handle_async(self, interaction, tx, transaction).await
} }
} }

View file

@ -64,6 +64,7 @@ pub const ATTRIBUTE_LIST: Attribute = Attribute::new(
// TODO: What if we instead of creating this, we just pass the AttrData/AttrPath to the read/write // TODO: What if we instead of creating this, we just pass the AttrData/AttrPath to the read/write
// methods? // methods?
/// The Attribute Details structure records the details about the attribute under consideration. /// The Attribute Details structure records the details about the attribute under consideration.
#[derive(Debug)]
pub struct AttrDetails<'a> { pub struct AttrDetails<'a> {
pub node: &'a Node<'a>, pub node: &'a Node<'a>,
/// The actual endpoint ID /// The actual endpoint ID
@ -129,6 +130,7 @@ impl<'a> AttrDetails<'a> {
} }
} }
#[derive(Debug)]
pub struct CmdDetails<'a> { pub struct CmdDetails<'a> {
pub node: &'a Node<'a>, pub node: &'a Node<'a>,
pub endpoint_id: EndptId, pub endpoint_id: EndptId,
@ -208,49 +210,23 @@ impl<'a> Cluster<'a> {
} }
} }
pub(crate) fn match_attributes<'m>( pub fn match_attributes(
&'m self, &self,
accessor: &'m Accessor<'m>,
ep: EndptId,
attr: Option<AttrId>, attr: Option<AttrId>,
write: bool, ) -> impl Iterator<Item = &'_ Attribute> + '_ {
) -> impl Iterator<Item = AttrId> + 'm {
self.attributes self.attributes
.iter() .iter()
.filter(move |attribute| attr.map(|attr| attr == attribute.id).unwrap_or(true)) .filter(move |attribute| attr.map(|attr| attr == attribute.id).unwrap_or(true))
.filter(move |attribute| {
let mut access_req = AccessReq::new(
accessor,
GenericPath::new(Some(ep), Some(self.id), Some(attribute.id as _)),
if write { Access::WRITE } else { Access::READ },
);
self.check_attr_access(&mut access_req, attribute.access)
.is_ok()
})
.map(|attribute| attribute.id)
} }
pub fn match_commands<'m>( pub fn match_commands(&self, cmd: Option<CmdId>) -> impl Iterator<Item = CmdId> + '_ {
&'m self,
accessor: &'m Accessor<'m>,
ep: EndptId,
cmd: Option<CmdId>,
) -> impl Iterator<Item = CmdId> + 'm {
self.commands self.commands
.iter() .iter()
.filter(move |id| cmd.map(|cmd| **id == cmd).unwrap_or(true)) .filter(move |id| cmd.map(|cmd| **id == cmd).unwrap_or(true))
.filter(move |id| {
let mut access_req = AccessReq::new(
accessor,
GenericPath::new(Some(ep), Some(self.id), Some(**id as _)),
Access::WRITE,
);
self.check_cmd_access(&mut access_req).is_ok()
})
.copied() .copied()
} }
pub(crate) fn check_attribute( pub fn check_attribute(
&self, &self,
accessor: &Accessor, accessor: &Accessor,
ep: EndptId, ep: EndptId,
@ -263,16 +239,15 @@ impl<'a> Cluster<'a> {
.find(|attribute| attribute.id == attr) .find(|attribute| attribute.id == attr)
.ok_or(IMStatusCode::UnsupportedAttribute)?; .ok_or(IMStatusCode::UnsupportedAttribute)?;
let mut access_req = AccessReq::new( Self::check_attr_access(
accessor, accessor,
GenericPath::new(Some(ep), Some(self.id), Some(attr as _)), GenericPath::new(Some(ep), Some(self.id), Some(attr as _)),
if write { Access::WRITE } else { Access::READ }, write,
); attribute.access,
)
self.check_attr_access(&mut access_req, attribute.access)
} }
pub(crate) fn check_command( pub fn check_command(
&self, &self,
accessor: &Accessor, accessor: &Accessor,
ep: EndptId, ep: EndptId,
@ -283,20 +258,24 @@ impl<'a> Cluster<'a> {
.find(|id| **id == cmd) .find(|id| **id == cmd)
.ok_or(IMStatusCode::UnsupportedCommand)?; .ok_or(IMStatusCode::UnsupportedCommand)?;
let mut access_req = AccessReq::new( Self::check_cmd_access(
accessor, accessor,
GenericPath::new(Some(ep), Some(self.id), Some(cmd as _)), GenericPath::new(Some(ep), Some(self.id), Some(cmd)),
Access::WRITE, )
);
self.check_cmd_access(&mut access_req)
} }
fn check_attr_access( pub(crate) fn check_attr_access(
&self, accessor: &Accessor,
access_req: &mut AccessReq, path: GenericPath,
write: bool,
target_perms: Access, target_perms: Access,
) -> Result<(), IMStatusCode> { ) -> Result<(), IMStatusCode> {
let mut access_req = AccessReq::new(
accessor,
path,
if write { Access::WRITE } else { Access::READ },
);
if !target_perms.contains(access_req.operation()) { if !target_perms.contains(access_req.operation()) {
Err(if matches!(access_req.operation(), Access::WRITE) { Err(if matches!(access_req.operation(), Access::WRITE) {
IMStatusCode::UnsupportedWrite IMStatusCode::UnsupportedWrite
@ -313,7 +292,12 @@ impl<'a> Cluster<'a> {
} }
} }
fn check_cmd_access(&self, access_req: &mut AccessReq) -> Result<(), IMStatusCode> { pub(crate) fn check_cmd_access(
accessor: &Accessor,
path: GenericPath,
) -> Result<(), IMStatusCode> {
let mut access_req = AccessReq::new(accessor, path, Access::WRITE);
access_req.set_target_perms( access_req.set_target_perms(
Access::WRITE Access::WRITE
.union(Access::NEED_OPERATE) .union(Access::NEED_OPERATE)

View file

@ -23,6 +23,7 @@ use crate::interaction_model::core::{IMStatusCode, Transaction};
use crate::interaction_model::messages::ib::{ use crate::interaction_model::messages::ib::{
AttrPath, AttrResp, AttrStatus, CmdDataTag, CmdPath, CmdStatus, InvResp, InvRespTag, AttrPath, AttrResp, AttrStatus, CmdDataTag, CmdPath, CmdStatus, InvResp, InvRespTag,
}; };
use crate::interaction_model::messages::GenericPath;
use crate::tlv::UtfStr; use crate::tlv::UtfStr;
use crate::{ use crate::{
error::Error, error::Error,
@ -127,13 +128,14 @@ impl<'a, 'b, 'c> AttrDataEncoder<'a, 'b, 'c> {
item: Result<AttrDetails, AttrStatus>, item: Result<AttrDetails, AttrStatus>,
handler: &T, handler: &T,
tw: &mut TLVWriter, tw: &mut TLVWriter,
) -> Result<(), Error> { ) -> Result<Option<GenericPath>, Error> {
let status = match item { let status = match item {
Ok(attr) => { Ok(attr) => {
let encoder = AttrDataEncoder::new(&attr, tw); let encoder = AttrDataEncoder::new(&attr, tw);
match handler.read(&attr, encoder) { match handler.read(&attr, encoder) {
Ok(()) => None, Ok(()) => None,
Err(Error::NoSpace) => return Ok(Some(attr.path().to_gp())),
Err(error) => attr.status(error.into())?, Err(error) => attr.status(error.into())?,
} }
} }
@ -144,7 +146,7 @@ impl<'a, 'b, 'c> AttrDataEncoder<'a, 'b, 'c> {
AttrResp::Status(status).to_tlv(tw, TagType::Anonymous)?; AttrResp::Status(status).to_tlv(tw, TagType::Anonymous)?;
} }
Ok(()) Ok(None)
} }
pub fn handle_write<T: Handler>( pub fn handle_write<T: Handler>(
@ -172,13 +174,14 @@ impl<'a, 'b, 'c> AttrDataEncoder<'a, 'b, 'c> {
item: Result<AttrDetails<'_>, AttrStatus>, item: Result<AttrDetails<'_>, AttrStatus>,
handler: &T, handler: &T,
tw: &mut TLVWriter<'_, '_>, tw: &mut TLVWriter<'_, '_>,
) -> Result<(), Error> { ) -> Result<Option<GenericPath>, Error> {
let status = match item { let status = match item {
Ok(attr) => { Ok(attr) => {
let encoder = AttrDataEncoder::new(&attr, tw); let encoder = AttrDataEncoder::new(&attr, tw);
match handler.read(&attr, encoder).await { match handler.read(&attr, encoder).await {
Ok(()) => None, Ok(()) => None,
Err(Error::NoSpace) => return Ok(Some(attr.path().to_gp())),
Err(error) => attr.status(error.into())?, Err(error) => attr.status(error.into())?,
} }
} }
@ -189,7 +192,7 @@ impl<'a, 'b, 'c> AttrDataEncoder<'a, 'b, 'c> {
AttrResp::Status(status).to_tlv(tw, TagType::Anonymous)?; AttrResp::Status(status).to_tlv(tw, TagType::Anonymous)?;
} }
Ok(()) Ok(None)
} }
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]

View file

@ -19,7 +19,7 @@ use crate::{acl::Accessor, interaction_model::core::IMStatusCode};
use core::fmt; use core::fmt;
use super::{AttrId, Cluster, ClusterId, CmdId, DeviceType, EndptId}; use super::{AttrId, Attribute, Cluster, ClusterId, CmdId, DeviceType, EndptId};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Endpoint<'a> { pub struct Endpoint<'a> {
@ -29,34 +29,28 @@ pub struct Endpoint<'a> {
} }
impl<'a> Endpoint<'a> { impl<'a> Endpoint<'a> {
pub(crate) fn match_attributes<'m>( pub fn match_attributes(
&'m self, &self,
accessor: &'m Accessor<'m>,
cl: Option<ClusterId>, cl: Option<ClusterId>,
attr: Option<AttrId>, attr: Option<AttrId>,
write: bool, ) -> impl Iterator<Item = (&'_ Cluster, &'_ Attribute)> + '_ {
) -> impl Iterator<Item = (ClusterId, AttrId)> + 'm {
self.match_clusters(cl).flat_map(move |cluster| { self.match_clusters(cl).flat_map(move |cluster| {
cluster cluster
.match_attributes(accessor, self.id, attr, write) .match_attributes(attr)
.map(move |attr| (cluster.id, attr)) .map(move |attr| (cluster, attr))
}) })
} }
pub(crate) fn match_commands<'m>( pub fn match_commands(
&'m self, &self,
accessor: &'m Accessor<'m>,
cl: Option<ClusterId>, cl: Option<ClusterId>,
cmd: Option<CmdId>, cmd: Option<CmdId>,
) -> impl Iterator<Item = (ClusterId, CmdId)> + 'm { ) -> impl Iterator<Item = (&'_ Cluster, CmdId)> + '_ {
self.match_clusters(cl).flat_map(move |cluster| { self.match_clusters(cl)
cluster .flat_map(move |cluster| cluster.match_commands(cmd).map(move |cmd| (cluster, cmd)))
.match_commands(accessor, self.id, cmd)
.map(move |cmd| (cluster.id, cmd))
})
} }
pub(crate) fn check_attribute( pub fn check_attribute(
&self, &self,
accessor: &Accessor, accessor: &Accessor,
cl: ClusterId, cl: ClusterId,
@ -67,7 +61,7 @@ impl<'a> Endpoint<'a> {
.and_then(|cluster| cluster.check_attribute(accessor, self.id, attr, write)) .and_then(|cluster| cluster.check_attribute(accessor, self.id, attr, write))
} }
pub(crate) fn check_command( pub fn check_command(
&self, &self,
accessor: &Accessor, accessor: &Accessor,
cl: ClusterId, cl: ClusterId,
@ -77,13 +71,13 @@ impl<'a> Endpoint<'a> {
.and_then(|cluster| cluster.check_command(accessor, self.id, cmd)) .and_then(|cluster| cluster.check_command(accessor, self.id, cmd))
} }
fn match_clusters(&self, cl: Option<ClusterId>) -> impl Iterator<Item = &Cluster> + '_ { pub fn match_clusters(&self, cl: Option<ClusterId>) -> impl Iterator<Item = &'_ Cluster> + '_ {
self.clusters self.clusters
.iter() .iter()
.filter(move |cluster| cl.map(|id| id == cluster.id).unwrap_or(true)) .filter(move |cluster| cl.map(|id| id == cluster.id).unwrap_or(true))
} }
fn check_cluster(&self, cl: ClusterId) -> Result<&Cluster, IMStatusCode> { pub fn check_cluster(&self, cl: ClusterId) -> Result<&Cluster, IMStatusCode> {
self.clusters self.clusters
.iter() .iter()
.find(|cluster| cluster.id == cl) .find(|cluster| cluster.id == cl)

View file

@ -186,9 +186,16 @@ macro_rules! handler_chain_type {
($h:ty) => { ($h:ty) => {
$crate::data_model::objects::ChainedHandler<$h, $crate::data_model::objects::EmptyHandler> $crate::data_model::objects::ChainedHandler<$h, $crate::data_model::objects::EmptyHandler>
}; };
($h1:ty, $($rest:ty),+) => { ($h1:ty $(, $rest:ty)+) => {
$crate::data_model::objects::ChainedHandler<$h1, handler_chain_type!($($rest),+)> $crate::data_model::objects::ChainedHandler<$h1, handler_chain_type!($($rest),+)>
}; };
($h:ty | $f:ty) => {
$crate::data_model::objects::ChainedHandler<$h, $f>
};
($h1:ty $(, $rest:ty)+ | $f:ty) => {
$crate::data_model::objects::ChainedHandler<$h1, handler_chain_type!($($rest),+ | $f)>
};
} }
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]

View file

@ -19,7 +19,7 @@ use crate::{
acl::Accessor, acl::Accessor,
data_model::objects::Endpoint, data_model::objects::Endpoint,
interaction_model::{ interaction_model::{
core::IMStatusCode, core::{IMStatusCode, ResumeReadReq, ResumeSubscribeReq},
messages::{ messages::{
ib::{AttrPath, AttrStatus, CmdStatus, DataVersionFilter}, ib::{AttrPath, AttrStatus, CmdStatus, DataVersionFilter},
msg::{InvReq, ReadReq, SubscribeReq, WriteReq}, msg::{InvReq, ReadReq, SubscribeReq, WriteReq},
@ -27,16 +27,16 @@ use crate::{
}, },
}, },
// TODO: This layer shouldn't really depend on the TLV layer, should create an abstraction layer // TODO: This layer shouldn't really depend on the TLV layer, should create an abstraction layer
tlv::{TLVArray, TLVElement}, tlv::{TLVArray, TLVArrayIter, TLVElement},
}; };
use core::{ use core::{
fmt, fmt,
iter::{once, Once}, iter::{once, Once},
}; };
use super::{AttrDetails, AttrId, ClusterId, CmdDetails, CmdId, EndptId}; use super::{AttrDetails, AttrId, Attribute, Cluster, ClusterId, CmdDetails, CmdId, EndptId};
enum WildcardIter<T, E> { pub enum WildcardIter<T, E> {
None, None,
Single(Once<E>), Single(Once<E>),
Wildcard(T), Wildcard(T),
@ -57,6 +57,41 @@ where
} }
} }
pub trait Iterable {
type Item;
type Iterator<'a>: Iterator<Item = Self::Item>
where
Self: 'a;
fn iter(&self) -> Self::Iterator<'_>;
}
impl<'a> Iterable for Option<&'a TLVArray<'a, DataVersionFilter>> {
type Item = DataVersionFilter;
type Iterator<'i> = WildcardIter<TLVArrayIter<'i, DataVersionFilter>, DataVersionFilter> where Self: 'i;
fn iter(&self) -> Self::Iterator<'_> {
if let Some(filters) = self {
WildcardIter::Wildcard(filters.iter())
} else {
WildcardIter::None
}
}
}
impl<'a> Iterable for &'a [DataVersionFilter] {
type Item = DataVersionFilter;
type Iterator<'i> = core::iter::Copied<core::slice::Iter<'i, DataVersionFilter>> where Self: 'i;
fn iter(&self) -> Self::Iterator<'_> {
let slice: &[DataVersionFilter] = self;
slice.iter().copied()
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Node<'a> { pub struct Node<'a> {
pub id: u16, pub id: u16,
@ -73,10 +108,30 @@ impl<'a> Node<'a> {
's: 'm, 's: 'm,
{ {
self.read_attr_requests( self.read_attr_requests(
req.attr_requests.as_ref(), req.attr_requests
.iter()
.flat_map(|attr_requests| attr_requests.iter()),
req.dataver_filters.as_ref(), req.dataver_filters.as_ref(),
req.fabric_filtered, req.fabric_filtered,
accessor, accessor,
None,
)
}
pub fn resume_read<'s, 'm>(
&'s self,
req: &'m ResumeReadReq,
accessor: &'m Accessor<'m>,
) -> impl Iterator<Item = Result<AttrDetails, AttrStatus>> + 'm
where
's: 'm,
{
self.read_attr_requests(
req.paths.iter().copied(),
req.filters.as_slice(),
req.fabric_filtered,
accessor,
Some(req.resume_path),
) )
} }
@ -89,60 +144,115 @@ impl<'a> Node<'a> {
's: 'm, 's: 'm,
{ {
self.read_attr_requests( self.read_attr_requests(
req.attr_requests.as_ref(), req.attr_requests
.iter()
.flat_map(|attr_requests| attr_requests.iter()),
req.dataver_filters.as_ref(), req.dataver_filters.as_ref(),
req.fabric_filtered, req.fabric_filtered,
accessor, accessor,
None,
) )
} }
fn read_attr_requests<'s, 'm>( pub fn resume_subscribing_read<'s, 'm>(
&'s self, &'s self,
attr_requests: Option<&'m TLVArray<AttrPath>>, req: &'m ResumeSubscribeReq,
dataver_filters: Option<&'m TLVArray<DataVersionFilter>>,
fabric_filtered: bool,
accessor: &'m Accessor<'m>, accessor: &'m Accessor<'m>,
) -> impl Iterator<Item = Result<AttrDetails, AttrStatus>> + 'm ) -> impl Iterator<Item = Result<AttrDetails, AttrStatus>> + 'm
where where
's: 'm, 's: 'm,
{ {
if let Some(attr_requests) = attr_requests.as_ref() { self.read_attr_requests(
WildcardIter::Wildcard(attr_requests.iter().flat_map( req.paths.iter().copied(),
move |path| match self.expand_attr(accessor, path.to_gp(), false) { req.filters.as_slice(),
Ok(iter) => { req.fabric_filtered,
let wildcard = matches!(iter, WildcardIter::Wildcard(_)); accessor,
Some(req.resume_path.unwrap()),
)
}
WildcardIter::Wildcard(iter.map(move |(ep, cl, attr)| { fn read_attr_requests<'s, 'm, P, D>(
let dataver_filter = dataver_filters &'s self,
.as_ref() attr_requests: P,
.iter() dataver_filters: D,
.flat_map(|array| array.iter()) fabric_filtered: bool,
.find_map(|filter| { accessor: &'m Accessor<'m>,
(filter.path.endpoint == ep && filter.path.cluster == cl) from: Option<GenericPath>,
.then_some(filter.data_ver) ) -> impl Iterator<Item = Result<AttrDetails, AttrStatus>> + 'm
}); where
's: 'm,
P: Iterator<Item = AttrPath> + 'm,
D: Iterable<Item = DataVersionFilter> + Clone + 'm,
{
attr_requests.flat_map(move |path| {
if path.to_gp().is_wildcard() {
let dataver_filters = dataver_filters.clone();
let from = from;
Ok(AttrDetails { let iter = self
node: self, .match_attributes(path.endpoint, path.cluster, path.attr)
endpoint_id: ep, .skip_while(move |(ep, cl, attr)| {
cluster_id: cl, !Self::matches(from.as_ref(), ep.id, cl.id, attr.id as _)
attr_id: attr, })
list_index: path.list_index, .filter(move |(ep, cl, attr)| {
fab_idx: accessor.fab_idx, Cluster::check_attr_access(
fab_filter: fabric_filtered, accessor,
dataver: dataver_filter, GenericPath::new(Some(ep.id), Some(cl.id), Some(attr.id as _)),
wildcard, false,
}) attr.access,
})) )
.is_ok()
})
.map(move |(ep, cl, attr)| {
let dataver = dataver_filters.iter().find_map(|filter| {
(filter.path.endpoint == ep.id && filter.path.cluster == cl.id)
.then_some(filter.data_ver)
});
Ok(AttrDetails {
node: self,
endpoint_id: ep.id,
cluster_id: cl.id,
attr_id: attr.id,
list_index: path.list_index,
fab_idx: accessor.fab_idx,
fab_filter: fabric_filtered,
dataver,
wildcard: true,
})
});
WildcardIter::Wildcard(iter)
} else {
let ep = path.endpoint.unwrap();
let cl = path.cluster.unwrap();
let attr = path.attr.unwrap();
let result = match self.check_attribute(accessor, ep, cl, attr, false) {
Ok(()) => {
let dataver = dataver_filters.iter().find_map(|filter| {
(filter.path.endpoint == ep && filter.path.cluster == cl)
.then_some(filter.data_ver)
});
Ok(AttrDetails {
node: self,
endpoint_id: ep,
cluster_id: cl,
attr_id: attr,
list_index: path.list_index,
fab_idx: accessor.fab_idx,
fab_filter: fabric_filtered,
dataver,
wildcard: false,
})
} }
Err(err) => { Err(err) => Err(AttrStatus::new(&path.to_gp(), err, 0)),
WildcardIter::Single(once(Err(AttrStatus::new(&path.to_gp(), err, 0)))) };
}
}, WildcardIter::Single(once(result))
)) }
} else { })
WildcardIter::None
}
} }
pub fn write<'m>( pub fn write<'m>(
@ -163,34 +273,64 @@ impl<'a> Node<'a> {
IMStatusCode::UnsupportedAttribute, IMStatusCode::UnsupportedAttribute,
0, 0,
)))) ))))
} else { } else if attr_data.path.to_gp().is_wildcard() {
match self.expand_attr(accessor, attr_data.path.to_gp(), true) { let iter = self
Ok(iter) => { .match_attributes(
let wildcard = matches!(iter, WildcardIter::Wildcard(_)); attr_data.path.endpoint,
attr_data.path.cluster,
attr_data.path.attr,
)
.filter(move |(ep, cl, attr)| {
Cluster::check_attr_access(
accessor,
GenericPath::new(Some(ep.id), Some(cl.id), Some(attr.id as _)),
true,
attr.access,
)
.is_ok()
})
.map(move |(ep, cl, attr)| {
Ok((
AttrDetails {
node: self,
endpoint_id: ep.id,
cluster_id: cl.id,
attr_id: attr.id,
list_index: attr_data.path.list_index,
fab_idx: accessor.fab_idx,
fab_filter: false,
dataver: attr_data.data_ver,
wildcard: true,
},
attr_data.data.unwrap_tlv().unwrap(),
))
});
WildcardIter::Wildcard(iter.map(move |(ep, cl, attr)| { WildcardIter::Wildcard(iter)
Ok(( } else {
AttrDetails { let ep = attr_data.path.endpoint.unwrap();
node: self, let cl = attr_data.path.cluster.unwrap();
endpoint_id: ep, let attr = attr_data.path.attr.unwrap();
cluster_id: cl,
attr_id: attr, let result = match self.check_attribute(accessor, ep, cl, attr, true) {
list_index: attr_data.path.list_index, Ok(()) => Ok((
fab_idx: accessor.fab_idx, AttrDetails {
fab_filter: false, node: self,
dataver: attr_data.data_ver, endpoint_id: ep,
wildcard, cluster_id: cl,
}, attr_id: attr,
attr_data.data.unwrap_tlv().unwrap(), list_index: attr_data.path.list_index,
)) fab_idx: accessor.fab_idx,
})) fab_filter: false,
} dataver: attr_data.data_ver,
Err(err) => WildcardIter::Single(once(Err(AttrStatus::new( wildcard: false,
&attr_data.path.to_gp(), },
err, attr_data.data.unwrap_tlv().unwrap(),
0, )),
)))), Err(err) => Err(AttrStatus::new(&attr_data.path.to_gp(), err, 0)),
} };
WildcardIter::Single(once(result))
} }
}) })
} }
@ -200,136 +340,99 @@ impl<'a> Node<'a> {
req: &'m InvReq, req: &'m InvReq,
accessor: &'m Accessor<'m>, accessor: &'m Accessor<'m>,
) -> impl Iterator<Item = Result<(CmdDetails, TLVElement<'m>), CmdStatus>> + 'm { ) -> impl Iterator<Item = Result<(CmdDetails, TLVElement<'m>), CmdStatus>> + 'm {
if let Some(inv_requests) = req.inv_requests.as_ref() { req.inv_requests
WildcardIter::Wildcard(inv_requests.iter().flat_map(move |cmd_data| { .iter()
match self.expand_cmd(accessor, cmd_data.path.path) { .flat_map(|inv_requests| inv_requests.iter())
Ok(iter) => { .flat_map(move |cmd_data| {
let wildcard = matches!(iter, WildcardIter::Wildcard(_)); if cmd_data.path.path.is_wildcard() {
let iter = self
WildcardIter::Wildcard(iter.map(move |(ep, cl, cmd)| { .match_commands(
cmd_data.path.path.endpoint,
cmd_data.path.path.cluster,
cmd_data.path.path.leaf.map(|leaf| leaf as _),
)
.filter(move |(ep, cl, cmd)| {
Cluster::check_cmd_access(
accessor,
GenericPath::new(Some(ep.id), Some(cl.id), Some(*cmd)),
)
.is_ok()
})
.map(move |(ep, cl, cmd)| {
Ok(( Ok((
CmdDetails { CmdDetails {
node: self, node: self,
endpoint_id: ep, endpoint_id: ep.id,
cluster_id: cl, cluster_id: cl.id,
cmd_id: cmd, cmd_id: cmd,
wildcard, wildcard: true,
}, },
cmd_data.data.unwrap_tlv().unwrap(), cmd_data.data.unwrap_tlv().unwrap(),
)) ))
})) });
}
Err(err) => { WildcardIter::Wildcard(iter)
WildcardIter::Single(once(Err(CmdStatus::new(cmd_data.path, err, 0)))) } else {
} let ep = cmd_data.path.path.endpoint.unwrap();
let cl = cmd_data.path.path.cluster.unwrap();
let cmd = cmd_data.path.path.leaf.unwrap();
let result = match self.check_command(accessor, ep, cl, cmd) {
Ok(()) => Ok((
CmdDetails {
node: self,
endpoint_id: cmd_data.path.path.endpoint.unwrap(),
cluster_id: cmd_data.path.path.cluster.unwrap(),
cmd_id: cmd_data.path.path.leaf.unwrap(),
wildcard: false,
},
cmd_data.data.unwrap_tlv().unwrap(),
)),
Err(err) => Err(CmdStatus::new(cmd_data.path, err, 0)),
};
WildcardIter::Single(once(result))
} }
})) })
}
fn matches(path: Option<&GenericPath>, ep: EndptId, cl: ClusterId, leaf: u32) -> bool {
if let Some(path) = path {
path.endpoint.map(|id| id == ep).unwrap_or(true)
&& path.cluster.map(|id| id == cl).unwrap_or(true)
&& path.leaf.map(|id| id == leaf).unwrap_or(true)
} else { } else {
WildcardIter::None true
} }
} }
fn expand_attr<'m>( pub fn match_attributes(
&'m self, &self,
accessor: &'m Accessor<'m>,
path: GenericPath,
write: bool,
) -> Result<
WildcardIter<
impl Iterator<Item = (EndptId, ClusterId, AttrId)> + 'm,
(EndptId, ClusterId, AttrId),
>,
IMStatusCode,
> {
if path.is_wildcard() {
Ok(WildcardIter::Wildcard(self.match_attributes(
accessor,
path.endpoint,
path.cluster,
path.leaf.map(|leaf| leaf as u16),
write,
)))
} else {
self.check_attribute(
accessor,
path.endpoint.unwrap(),
path.cluster.unwrap(),
path.leaf.unwrap() as _,
write,
)?;
Ok(WildcardIter::Single(once((
path.endpoint.unwrap(),
path.cluster.unwrap(),
path.leaf.unwrap() as _,
))))
}
}
fn expand_cmd<'m>(
&'m self,
accessor: &'m Accessor<'m>,
path: GenericPath,
) -> Result<
WildcardIter<
impl Iterator<Item = (EndptId, ClusterId, CmdId)> + 'm,
(EndptId, ClusterId, CmdId),
>,
IMStatusCode,
> {
if path.is_wildcard() {
Ok(WildcardIter::Wildcard(self.match_commands(
accessor,
path.endpoint,
path.cluster,
path.leaf,
)))
} else {
self.check_command(
accessor,
path.endpoint.unwrap(),
path.cluster.unwrap(),
path.leaf.unwrap(),
)?;
Ok(WildcardIter::Single(once((
path.endpoint.unwrap(),
path.cluster.unwrap(),
path.leaf.unwrap(),
))))
}
}
fn match_attributes<'m>(
&'m self,
accessor: &'m Accessor<'m>,
ep: Option<EndptId>, ep: Option<EndptId>,
cl: Option<ClusterId>, cl: Option<ClusterId>,
attr: Option<AttrId>, attr: Option<AttrId>,
write: bool, ) -> impl Iterator<Item = (&'_ Endpoint, &'_ Cluster, &'_ Attribute)> + '_ {
) -> impl Iterator<Item = (EndptId, ClusterId, AttrId)> + 'm {
self.match_endpoints(ep).flat_map(move |endpoint| { self.match_endpoints(ep).flat_map(move |endpoint| {
endpoint endpoint
.match_attributes(accessor, cl, attr, write) .match_attributes(cl, attr)
.map(move |(cl, attr)| (endpoint.id, cl, attr)) .map(move |(cl, attr)| (endpoint, cl, attr))
}) })
} }
fn match_commands<'m>( pub fn match_commands(
&'m self, &self,
accessor: &'m Accessor<'m>,
ep: Option<EndptId>, ep: Option<EndptId>,
cl: Option<ClusterId>, cl: Option<ClusterId>,
cmd: Option<CmdId>, cmd: Option<CmdId>,
) -> impl Iterator<Item = (EndptId, ClusterId, CmdId)> + 'm { ) -> impl Iterator<Item = (&'_ Endpoint, &'_ Cluster, CmdId)> + '_ {
self.match_endpoints(ep).flat_map(move |endpoint| { self.match_endpoints(ep).flat_map(move |endpoint| {
endpoint endpoint
.match_commands(accessor, cl, cmd) .match_commands(cl, cmd)
.map(move |(cl, cmd)| (endpoint.id, cl, cmd)) .map(move |(cl, cmd)| (endpoint, cl, cmd))
}) })
} }
fn check_attribute( pub fn check_attribute(
&self, &self,
accessor: &Accessor, accessor: &Accessor,
ep: EndptId, ep: EndptId,
@ -341,7 +444,7 @@ impl<'a> Node<'a> {
.and_then(|endpoint| endpoint.check_attribute(accessor, cl, attr, write)) .and_then(|endpoint| endpoint.check_attribute(accessor, cl, attr, write))
} }
fn check_command( pub fn check_command(
&self, &self,
accessor: &Accessor, accessor: &Accessor,
ep: EndptId, ep: EndptId,
@ -352,13 +455,13 @@ impl<'a> Node<'a> {
.and_then(|endpoint| endpoint.check_command(accessor, cl, cmd)) .and_then(|endpoint| endpoint.check_command(accessor, cl, cmd))
} }
fn match_endpoints(&self, ep: Option<EndptId>) -> impl Iterator<Item = &Endpoint> + '_ { pub fn match_endpoints(&self, ep: Option<EndptId>) -> impl Iterator<Item = &'_ Endpoint> + '_ {
self.endpoints self.endpoints
.iter() .iter()
.filter(move |endpoint| ep.map(|id| id == endpoint.id).unwrap_or(true)) .filter(move |endpoint| ep.map(|id| id == endpoint.id).unwrap_or(true))
} }
fn check_endpoint(&self, ep: EndptId) -> Result<&Endpoint, IMStatusCode> { pub fn check_endpoint(&self, ep: EndptId) -> Result<&Endpoint, IMStatusCode> {
self.endpoints self.endpoints
.iter() .iter()
.find(|endpoint| endpoint.id == ep) .find(|endpoint| endpoint.id == ep)

View file

@ -21,19 +21,24 @@ use super::{
noc::{self, NocCluster}, noc::{self, NocCluster},
nw_commissioning::{self, NwCommCluster}, nw_commissioning::{self, NwCommCluster},
}, },
system_model::access_control::{self, AccessControlCluster}, system_model::{
access_control::{self, AccessControlCluster},
descriptor::{self, DescriptorCluster},
},
}; };
pub type RootEndpointHandler<'a> = handler_chain_type!( pub type RootEndpointHandler<'a> = handler_chain_type!(
AccessControlCluster<'a>, DescriptorCluster,
NocCluster<'a>, BasicInfoCluster<'a>,
AdminCommCluster<'a>,
NwCommCluster,
GenCommCluster, GenCommCluster,
BasicInfoCluster<'a> NwCommCluster,
AdminCommCluster<'a>,
NocCluster<'a>,
AccessControlCluster<'a>
); );
pub const CLUSTERS: [Cluster<'static>; 6] = [ pub const CLUSTERS: [Cluster<'static>; 7] = [
descriptor::CLUSTER,
cluster_basic_information::CLUSTER, cluster_basic_information::CLUSTER,
general_commissioning::CLUSTER, general_commissioning::CLUSTER,
nw_commissioning::CLUSTER, nw_commissioning::CLUSTER,
@ -77,32 +82,29 @@ pub fn wrap<'a>(
EmptyHandler EmptyHandler
.chain( .chain(
endpoint_id, endpoint_id,
cluster_basic_information::CLUSTER.id, access_control::ID,
BasicInfoCluster::new(basic_info, rand), AccessControlCluster::new(acl, rand),
) )
.chain( .chain(
endpoint_id, endpoint_id,
general_commissioning::CLUSTER.id, noc::ID,
GenCommCluster::new(rand),
)
.chain(
endpoint_id,
nw_commissioning::CLUSTER.id,
NwCommCluster::new(rand),
)
.chain(
endpoint_id,
admin_commissioning::CLUSTER.id,
AdminCommCluster::new(pase, mdns_mgr, rand),
)
.chain(
endpoint_id,
noc::CLUSTER.id,
NocCluster::new(dev_att, fabric, acl, failsafe, mdns_mgr, epoch, rand), NocCluster::new(dev_att, fabric, acl, failsafe, mdns_mgr, epoch, rand),
) )
.chain( .chain(
endpoint_id, endpoint_id,
access_control::CLUSTER.id, admin_commissioning::ID,
AccessControlCluster::new(acl, rand), AdminCommCluster::new(pase, mdns_mgr, rand),
) )
.chain(endpoint_id, nw_commissioning::ID, NwCommCluster::new(rand))
.chain(
endpoint_id,
general_commissioning::ID,
GenCommCluster::new(rand),
)
.chain(
endpoint_id,
cluster_basic_information::ID,
BasicInfoCluster::new(basic_info, rand),
)
.chain(endpoint_id, descriptor::ID, DescriptorCluster::new(rand))
} }

View file

@ -215,7 +215,7 @@ impl GenCommCluster {
encoder encoder
.with_command(RespCommands::ArmFailsafeResp as _)? .with_command(RespCommands::ArmFailsafeResp as _)?
.set(&cmd_data) .set(cmd_data)
} }
fn handle_command_setregulatoryconfig( fn handle_command_setregulatoryconfig(
@ -238,7 +238,7 @@ impl GenCommCluster {
encoder encoder
.with_command(RespCommands::SetRegulatoryConfigResp as _)? .with_command(RespCommands::SetRegulatoryConfigResp as _)?
.set(&cmd_data) .set(cmd_data)
} }
fn handle_command_commissioningcomplete( fn handle_command_commissioningcomplete(
@ -272,7 +272,7 @@ impl GenCommCluster {
encoder encoder
.with_command(RespCommands::CommissioningCompleteResp as _)? .with_command(RespCommands::CommissioningCompleteResp as _)?
.set(&cmd_data) .set(cmd_data)
} }
} }

View file

@ -398,7 +398,7 @@ impl<'a> NocCluster<'a> {
encoder encoder
.with_command(RespCommands::NOCResp as _)? .with_command(RespCommands::NOCResp as _)?
.set(&cmd_data) .set(cmd_data)
} }
fn handle_command_updatefablabel( fn handle_command_updatefablabel(
@ -527,7 +527,7 @@ impl<'a> NocCluster<'a> {
encoder encoder
.with_command(RespCommands::CertChainResp as _)? .with_command(RespCommands::CertChainResp as _)?
.set(&cmd_data) .set(cmd_data)
} }
fn handle_command_csrrequest( fn handle_command_csrrequest(

View file

@ -52,8 +52,16 @@ impl NwCommCluster {
} }
impl Handler for NwCommCluster { impl Handler for NwCommCluster {
fn read(&self, _attr: &AttrDetails, _encoder: AttrDataEncoder) -> Result<(), Error> { fn read(&self, attr: &AttrDetails, encoder: AttrDataEncoder) -> Result<(), Error> {
Err(Error::AttributeNotFound) if let Some(writer) = encoder.with_dataver(self.data_ver.get())? {
if attr.is_system() {
CLUSTER.read(attr.attr_id, writer)
} else {
Err(Error::AttributeNotFound)
}
} else {
Ok(())
}
} }
} }

View file

@ -20,7 +20,7 @@ use core::convert::TryInto;
use strum::{EnumDiscriminants, FromRepr}; use strum::{EnumDiscriminants, FromRepr};
use crate::acl::{AclEntry, AclMgr}; use crate::acl::{self, AclEntry, AclMgr};
use crate::data_model::objects::*; use crate::data_model::objects::*;
use crate::interaction_model::messages::ib::{attr_list_write, ListOperation}; use crate::interaction_model::messages::ib::{attr_list_write, ListOperation};
use crate::tlv::{FromTLV, TLVElement, TagType, ToTLV}; use crate::tlv::{FromTLV, TLVElement, TagType, ToTLV};
@ -116,9 +116,14 @@ impl<'a> AccessControlCluster<'a> {
writer.complete() writer.complete()
} }
_ => { Attributes::SubjectsPerEntry(codec) => {
error!("Attribute not yet supported: this shouldn't happen"); codec.encode(writer, acl::SUBJECTS_PER_ENTRY as u16)
Err(Error::AttributeNotFound) }
Attributes::TargetsPerEntry(codec) => {
codec.encode(writer, acl::TARGETS_PER_ENTRY as u16)
}
Attributes::EntriesPerFabric(codec) => {
codec.encode(writer, acl::ENTRIES_PER_FABRIC as u16)
} }
} }
} }
@ -365,7 +370,7 @@ mod tests {
writebuf.as_slice() writebuf.as_slice()
); );
} }
writebuf.reset(0); writebuf.reset();
// Test 2, only single entry is read in the response with fabric filtering and fabric idx 1 // Test 2, only single entry is read in the response with fabric filtering and fabric idx 1
{ {
@ -400,7 +405,7 @@ mod tests {
writebuf.as_slice() writebuf.as_slice()
); );
} }
writebuf.reset(0); writebuf.reset();
// Test 3, only single entry is read in the response with fabric filtering and fabric idx 2 // Test 3, only single entry is read in the response with fabric filtering and fabric idx 2
{ {

View file

@ -495,7 +495,11 @@ impl FabricMgr {
} }
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub async fn load_async<T>(&mut self, mut psm: T, mdns_mgr: &mut MdnsMgr) -> Result<(), Error> pub async fn load_async<T>(
&mut self,
mut psm: T,
mdns_mgr: &mut MdnsMgr<'_>,
) -> Result<(), Error>
where where
T: crate::persist::asynch::AsyncPsm, T: crate::persist::asynch::AsyncPsm,
{ {

View file

@ -21,14 +21,23 @@ use crate::{
data_model::core::DataHandler, data_model::core::DataHandler,
error::*, error::*,
tlv::{get_root_node_struct, print_tlv_list, FromTLV, TLVElement, TLVWriter, TagType, ToTLV}, tlv::{get_root_node_struct, print_tlv_list, FromTLV, TLVElement, TLVWriter, TagType, ToTLV},
transport::{exchange::ExchangeCtx, packet::Packet, proto_ctx::ProtoCtx, session::Session}, transport::{
exchange::{Exchange, ExchangeCtx},
packet::Packet,
proto_ctx::ProtoCtx,
session::Session,
},
}; };
use colored::Colorize; use colored::Colorize;
use log::{error, info}; use log::{error, info};
use num; use num;
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use super::messages::msg::{self, InvReq, ReadReq, StatusResp, SubscribeReq, TimedReq, WriteReq}; use super::messages::{
ib::{AttrPath, DataVersionFilter},
msg::{self, InvReq, ReadReq, StatusResp, SubscribeReq, TimedReq, WriteReq},
GenericPath,
};
#[macro_export] #[macro_export]
macro_rules! cmd_enter { macro_rules! cmd_enter {
@ -132,6 +141,14 @@ impl<'a, 'b> Transaction<'a, 'b> {
} }
} }
pub fn exch(&self) -> &Exchange {
self.ctx.exch
}
pub fn exch_mut(&mut self) -> &mut Exchange {
self.ctx.exch
}
pub fn session(&self) -> &Session { pub fn session(&self) -> &Session {
self.ctx.sess.session() self.ctx.sess.session()
} }
@ -182,17 +199,25 @@ impl<'a, 'b> Transaction<'a, 'b> {
/* Interaction Model ID as per the Matter Spec */ /* Interaction Model ID as per the Matter Spec */
const PROTO_ID_INTERACTION_MODEL: usize = 0x01; const PROTO_ID_INTERACTION_MODEL: usize = 0x01;
const MAX_RESUME_PATHS: usize = 128;
const MAX_RESUME_DATAVER_FILTERS: usize = 128;
// This is the amount of space we reserve for other things to be attached towards
// the end of long reads.
const LONG_READS_TLV_RESERVE_SIZE: usize = 24;
pub enum Interaction<'a> { pub enum Interaction<'a> {
Read(ReadReq<'a>), Read(ReadReq<'a>),
Write(WriteReq<'a>), Write(WriteReq<'a>),
Invoke(InvReq<'a>), Invoke(InvReq<'a>),
Subscribe(SubscribeReq<'a>), Subscribe(SubscribeReq<'a>),
Status(StatusResp),
Timed(TimedReq), Timed(TimedReq),
ResumeRead(ResumeReadReq),
ResumeSubscribe(ResumeSubscribeReq),
} }
impl<'a> Interaction<'a> { impl<'a> Interaction<'a> {
pub fn new(rx: &'a Packet) -> Result<Self, Error> { fn new(rx: &'a Packet, transaction: &mut Transaction) -> Result<Option<Self>, Error> {
let opcode: OpCode = let opcode: OpCode =
num::FromPrimitive::from_u8(rx.get_proto_opcode()).ok_or(Error::Invalid)?; num::FromPrimitive::from_u8(rx.get_proto_opcode()).ok_or(Error::Invalid)?;
@ -202,243 +227,67 @@ impl<'a> Interaction<'a> {
print_tlv_list(rx_data); print_tlv_list(rx_data);
match opcode { match opcode {
OpCode::ReadRequest => Ok(Self::Read(ReadReq::from_tlv(&get_root_node_struct( OpCode::ReadRequest => Ok(Some(Self::Read(ReadReq::from_tlv(&get_root_node_struct(
rx_data, rx_data,
)?)?)), )?)?))),
OpCode::WriteRequest => Ok(Self::Write(WriteReq::from_tlv(&get_root_node_struct( OpCode::WriteRequest => Ok(Some(Self::Write(WriteReq::from_tlv(
rx_data,
)?)?)),
OpCode::InvokeRequest => Ok(Self::Invoke(InvReq::from_tlv(&get_root_node_struct(
rx_data,
)?)?)),
OpCode::SubscribeRequest => Ok(Self::Subscribe(SubscribeReq::from_tlv(
&get_root_node_struct(rx_data)?, &get_root_node_struct(rx_data)?,
)?)), )?))),
OpCode::StatusResponse => Ok(Self::Status(StatusResp::from_tlv( OpCode::InvokeRequest => Ok(Some(Self::Invoke(InvReq::from_tlv(
&get_root_node_struct(rx_data)?, &get_root_node_struct(rx_data)?,
)?)), )?))),
OpCode::TimedRequest => Ok(Self::Timed(TimedReq::from_tlv(&get_root_node_struct( OpCode::SubscribeRequest => Ok(Some(Self::Subscribe(SubscribeReq::from_tlv(
rx_data, &get_root_node_struct(rx_data)?,
)?)?)), )?))),
OpCode::StatusResponse => {
let resp = StatusResp::from_tlv(&get_root_node_struct(rx_data)?)?;
if resp.status == IMStatusCode::Success {
if let Some(req) = transaction.exch_mut().take_suspended_read_req() {
Ok(Some(Self::ResumeRead(req)))
} else if let Some(req) = transaction.exch_mut().take_suspended_subscribe_req()
{
Ok(Some(Self::ResumeSubscribe(req)))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
OpCode::TimedRequest => Ok(Some(Self::Timed(TimedReq::from_tlv(
&get_root_node_struct(rx_data)?,
)?))),
_ => { _ => {
error!("Opcode Not Handled: {:?}", opcode); error!("Opcode not handled: {:?}", opcode);
Err(Error::InvalidOpcode) Err(Error::InvalidOpcode)
} }
} }
} }
pub fn initiate_tx( pub fn initiate(
&self, rx: &'a Packet,
tx: &mut Packet, tx: &mut Packet,
transaction: &mut Transaction, transaction: &mut Transaction,
) -> Result<bool, Error> { ) -> Result<Option<Self>, Error> {
let reply = match self { if let Some(interaction) = Self::new(rx, transaction)? {
Self::Read(request) => { let initiated = match &interaction {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16); Interaction::Read(req) => req.initiate(tx, transaction)?,
tx.set_proto_opcode(OpCode::ReportData as u8); Interaction::Write(req) => req.initiate(tx, transaction)?,
Interaction::Invoke(req) => req.initiate(tx, transaction)?,
let mut tw = TLVWriter::new(tx.get_writebuf()?); Interaction::Subscribe(req) => req.initiate(tx, transaction)?,
Interaction::Timed(req) => {
tw.start_struct(TagType::Anonymous)?; req.process(tx, transaction)?;
if request.attr_requests.is_some() {
tw.start_array(TagType::Context(msg::ReportDataTag::AttributeReports as u8))?;
}
false
}
Self::Write(_) => {
if transaction.has_timed_out() {
Self::create_status_response(tx, IMStatusCode::Timeout)?;
transaction.complete();
transaction.ctx.exch.close();
true
} else {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::WriteResponse as u8);
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.start_struct(TagType::Anonymous)?;
tw.start_array(TagType::Context(msg::WriteRespTag::WriteResponses as u8))?;
false false
} }
} Interaction::ResumeRead(req) => req.initiate(tx, transaction)?,
Self::Invoke(request) => { Interaction::ResumeSubscribe(req) => req.initiate(tx, transaction)?,
if transaction.has_timed_out() { };
Self::create_status_response(tx, IMStatusCode::Timeout)?;
transaction.complete(); Ok(initiated.then_some(interaction))
transaction.ctx.exch.close(); } else {
Ok(None)
true
} else {
let timed_tx = transaction.get_timeout().map(|_| true);
let timed_request = request.timed_request.filter(|a| *a);
// Either both should be None, or both should be Some(true)
if timed_tx != timed_request {
Self::create_status_response(tx, IMStatusCode::TimedRequestMisMatch)?;
true
} else {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::InvokeResponse as u8);
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.start_struct(TagType::Anonymous)?;
// Suppress Response -> TODO: Need to revisit this for cases where we send a command back
tw.bool(
TagType::Context(msg::InvRespTag::SupressResponse as u8),
false,
)?;
if request.inv_requests.is_some() {
tw.start_array(TagType::Context(
msg::InvRespTag::InvokeResponses as u8,
))?;
}
false
}
}
}
Self::Subscribe(request) => {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::ReportData as u8);
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.start_struct(TagType::Anonymous)?;
if request.attr_requests.is_some() {
tw.start_array(TagType::Context(msg::ReportDataTag::AttributeReports as u8))?;
}
true
}
Self::Status(_) => {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::SubscribeResponse as u8);
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.start_struct(TagType::Anonymous)?;
true
}
Self::Timed(request) => {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::StatusResponse as u8);
let mut tw = TLVWriter::new(tx.get_writebuf()?);
transaction.set_timeout(request.timeout.into());
let status = StatusResp {
status: IMStatusCode::Success,
};
status.to_tlv(&mut tw, TagType::Anonymous)?;
true
}
};
Ok(!reply)
}
pub fn complete_tx(
&self,
tx: &mut Packet,
transaction: &mut Transaction,
) -> Result<bool, Error> {
let reply = match self {
Self::Read(request) => {
let mut tw = TLVWriter::new(tx.get_writebuf()?);
if request.attr_requests.is_some() {
tw.end_container()?;
}
// Suppress response always true for read interaction
tw.bool(
TagType::Context(msg::ReportDataTag::SupressResponse as u8),
true,
)?;
tw.end_container()?;
transaction.complete();
true
}
Self::Write(request) => {
let suppress = request.supress_response.unwrap_or_default();
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.end_container()?;
tw.end_container()?;
transaction.complete();
if suppress {
error!("Supress response is set, is this the expected handling?");
false
} else {
true
}
}
Self::Invoke(request) => {
let mut tw = TLVWriter::new(tx.get_writebuf()?);
if request.inv_requests.is_some() {
tw.end_container()?;
}
tw.end_container()?;
true
}
Self::Subscribe(request) => {
let mut tw = TLVWriter::new(tx.get_writebuf()?);
if request.attr_requests.is_some() {
tw.end_container()?;
}
tw.end_container()?;
true
}
Self::Status(_) => {
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.end_container()?;
true
}
Self::Timed(_) => false,
};
if reply {
info!("Sending response");
print_tlv_list(tx.as_slice());
} }
if transaction.is_terminate() {
transaction.ctx.exch.terminate();
} else if transaction.is_complete() {
transaction.ctx.exch.close();
}
Ok(true)
} }
fn create_status_response(tx: &mut Packet, status: IMStatusCode) -> Result<(), Error> { fn create_status_response(tx: &mut Packet, status: IMStatusCode) -> Result<(), Error> {
@ -452,6 +301,414 @@ impl<'a> Interaction<'a> {
} }
} }
impl<'a> ReadReq<'a> {
fn suspend(self, resume_path: GenericPath) -> ResumeReadReq {
ResumeReadReq {
paths: self
.attr_requests
.iter()
.flat_map(|attr_requests| attr_requests.iter())
.collect(),
filters: self
.dataver_filters
.iter()
.flat_map(|filters| filters.iter())
.collect(),
fabric_filtered: self.fabric_filtered,
resume_path,
}
}
fn initiate(&self, tx: &mut Packet, _transaction: &mut Transaction) -> Result<bool, Error> {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::ReportData as u8);
let mut tw = Self::reserve_long_read_space(tx)?;
tw.start_struct(TagType::Anonymous)?;
if self.attr_requests.is_some() {
tw.start_array(TagType::Context(msg::ReportDataTag::AttributeReports as u8))?;
}
Ok(true)
}
pub fn complete(
self,
tx: &mut Packet,
transaction: &mut Transaction,
resume_path: Option<GenericPath>,
) -> Result<bool, Error> {
let mut tw = Self::restore_long_read_space(tx)?;
if self.attr_requests.is_some() {
tw.end_container()?;
}
let more_chunks = if let Some(resume_path) = resume_path {
tw.bool(
TagType::Context(msg::ReportDataTag::MoreChunkedMsgs as u8),
true,
)?;
transaction
.exch_mut()
.set_suspended_read_req(self.suspend(resume_path));
true
} else {
false
};
tw.bool(
TagType::Context(msg::ReportDataTag::SupressResponse as u8),
!more_chunks,
)?;
tw.end_container()?;
if !more_chunks {
transaction.complete();
}
Ok(true)
}
fn reserve_long_read_space<'p, 'b>(tx: &'p mut Packet<'b>) -> Result<TLVWriter<'p, 'b>, Error> {
let wb = tx.get_writebuf()?;
wb.shrink(LONG_READS_TLV_RESERVE_SIZE)?;
Ok(TLVWriter::new(wb))
}
fn restore_long_read_space<'p, 'b>(tx: &'p mut Packet<'b>) -> Result<TLVWriter<'p, 'b>, Error> {
let wb = tx.get_writebuf()?;
wb.expand(LONG_READS_TLV_RESERVE_SIZE)?;
Ok(TLVWriter::new(wb))
}
}
impl<'a> WriteReq<'a> {
fn initiate(&self, tx: &mut Packet, transaction: &mut Transaction) -> Result<bool, Error> {
if transaction.has_timed_out() {
Interaction::create_status_response(tx, IMStatusCode::Timeout)?;
transaction.complete();
transaction.ctx.exch.close();
Ok(false)
} else {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::WriteResponse as u8);
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.start_struct(TagType::Anonymous)?;
tw.start_array(TagType::Context(msg::WriteRespTag::WriteResponses as u8))?;
Ok(true)
}
}
pub fn complete(self, tx: &mut Packet, transaction: &mut Transaction) -> Result<bool, Error> {
let suppress = self.supress_response.unwrap_or_default();
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.end_container()?;
tw.end_container()?;
transaction.complete();
Ok(if suppress {
error!("Supress response is set, is this the expected handling?");
false
} else {
true
})
}
}
impl<'a> InvReq<'a> {
fn initiate(&self, tx: &mut Packet, transaction: &mut Transaction) -> Result<bool, Error> {
if transaction.has_timed_out() {
Interaction::create_status_response(tx, IMStatusCode::Timeout)?;
transaction.complete();
transaction.ctx.exch.close();
Ok(false)
} else {
let timed_tx = transaction.get_timeout().map(|_| true);
let timed_request = self.timed_request.filter(|a| *a);
// Either both should be None, or both should be Some(true)
if timed_tx != timed_request {
Interaction::create_status_response(tx, IMStatusCode::TimedRequestMisMatch)?;
Ok(false)
} else {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::InvokeResponse as u8);
let mut tw = TLVWriter::new(tx.get_writebuf()?);
tw.start_struct(TagType::Anonymous)?;
// Suppress Response -> TODO: Need to revisit this for cases where we send a command back
tw.bool(
TagType::Context(msg::InvRespTag::SupressResponse as u8),
false,
)?;
if self.inv_requests.is_some() {
tw.start_array(TagType::Context(msg::InvRespTag::InvokeResponses as u8))?;
}
Ok(true)
}
}
}
pub fn complete(self, tx: &mut Packet, _transaction: &mut Transaction) -> Result<bool, Error> {
let mut tw = TLVWriter::new(tx.get_writebuf()?);
if self.inv_requests.is_some() {
tw.end_container()?;
}
tw.end_container()?;
Ok(true)
}
}
impl TimedReq {
pub fn process(&self, tx: &mut Packet, transaction: &mut Transaction) -> Result<(), Error> {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::StatusResponse as u8);
let mut tw = TLVWriter::new(tx.get_writebuf()?);
transaction.set_timeout(self.timeout.into());
let status = StatusResp {
status: IMStatusCode::Success,
};
status.to_tlv(&mut tw, TagType::Anonymous)?;
Ok(())
}
}
impl<'a> SubscribeReq<'a> {
fn suspend(&self, resume_path: Option<GenericPath>) -> ResumeSubscribeReq {
ResumeSubscribeReq {
paths: self
.attr_requests
.iter()
.flat_map(|attr_requests| attr_requests.iter())
.collect(),
filters: self
.dataver_filters
.iter()
.flat_map(|filters| filters.iter())
.collect(),
fabric_filtered: self.fabric_filtered,
resume_path,
keep_subs: self.keep_subs,
min_int_floor: self.min_int_floor,
max_int_ceil: self.max_int_ceil,
}
}
fn initiate(&self, tx: &mut Packet, _transaction: &mut Transaction) -> Result<bool, Error> {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::ReportData as u8);
let mut tw = ReadReq::reserve_long_read_space(tx)?;
tw.start_struct(TagType::Anonymous)?;
if self.attr_requests.is_some() {
tw.start_array(TagType::Context(msg::ReportDataTag::AttributeReports as u8))?;
}
Ok(true)
}
pub fn complete(
self,
tx: &mut Packet,
transaction: &mut Transaction,
resume_path: Option<GenericPath>,
) -> Result<bool, Error> {
let mut tw = ReadReq::restore_long_read_space(tx)?;
if self.attr_requests.is_some() {
tw.end_container()?;
}
if resume_path.is_some() {
tw.bool(
TagType::Context(msg::ReportDataTag::MoreChunkedMsgs as u8),
true,
)?;
}
transaction
.exch_mut()
.set_suspended_subscribe_req(self.suspend(resume_path));
tw.bool(
TagType::Context(msg::ReportDataTag::SupressResponse as u8),
false,
)?;
tw.end_container()?;
Ok(true)
}
}
pub struct ResumeReadReq {
pub paths: heapless::Vec<AttrPath, MAX_RESUME_PATHS>,
pub filters: heapless::Vec<DataVersionFilter, MAX_RESUME_DATAVER_FILTERS>,
pub fabric_filtered: bool,
pub resume_path: GenericPath,
}
impl ResumeReadReq {
fn initiate(&self, tx: &mut Packet, _transaction: &mut Transaction) -> Result<bool, Error> {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
tx.set_proto_opcode(OpCode::ReportData as u8);
let mut tw = ReadReq::reserve_long_read_space(tx)?;
tw.start_struct(TagType::Anonymous)?;
tw.start_array(TagType::Context(msg::ReportDataTag::AttributeReports as u8))?;
Ok(true)
}
pub fn complete(
mut self,
tx: &mut Packet,
transaction: &mut Transaction,
resume_path: Option<GenericPath>,
) -> Result<bool, Error> {
let mut tw = ReadReq::restore_long_read_space(tx)?;
tw.end_container()?;
let continue_interaction = if let Some(resume_path) = resume_path {
tw.bool(
TagType::Context(msg::ReportDataTag::MoreChunkedMsgs as u8),
true,
)?;
self.resume_path = resume_path;
transaction.exch_mut().set_suspended_read_req(self);
true
} else {
false
};
tw.bool(
TagType::Context(msg::ReportDataTag::SupressResponse as u8),
!continue_interaction,
)?;
tw.end_container()?;
if !continue_interaction {
transaction.complete();
}
Ok(true)
}
}
pub struct ResumeSubscribeReq {
pub paths: heapless::Vec<AttrPath, MAX_RESUME_PATHS>,
pub filters: heapless::Vec<DataVersionFilter, MAX_RESUME_DATAVER_FILTERS>,
pub fabric_filtered: bool,
pub resume_path: Option<GenericPath>,
pub keep_subs: bool,
pub min_int_floor: u16,
pub max_int_ceil: u16,
}
impl ResumeSubscribeReq {
fn initiate(&self, tx: &mut Packet, _transaction: &mut Transaction) -> Result<bool, Error> {
tx.set_proto_id(PROTO_ID_INTERACTION_MODEL as u16);
if self.resume_path.is_some() {
tx.set_proto_opcode(OpCode::ReportData as u8);
let mut tw = ReadReq::reserve_long_read_space(tx)?;
tw.start_struct(TagType::Anonymous)?;
tw.start_array(TagType::Context(msg::ReportDataTag::AttributeReports as u8))?;
} else {
tx.set_proto_opcode(OpCode::SubscribeResponse as u8);
// let mut tw = TLVWriter::new(tx.get_writebuf()?);
// tw.start_struct(TagType::Anonymous)?;
}
Ok(true)
}
pub fn complete(
mut self,
tx: &mut Packet,
transaction: &mut Transaction,
resume_path: Option<GenericPath>,
) -> Result<bool, Error> {
if self.resume_path.is_none() && resume_path.is_some() {
panic!("Cannot resume subscribe");
}
if self.resume_path.is_some() {
// Completing a ReportData message
let mut tw = ReadReq::restore_long_read_space(tx)?;
tw.end_container()?;
if resume_path.is_some() {
tw.bool(
TagType::Context(msg::ReportDataTag::MoreChunkedMsgs as u8),
true,
)?;
}
tw.bool(
TagType::Context(msg::ReportDataTag::SupressResponse as u8),
false,
)?;
tw.end_container()?;
self.resume_path = resume_path;
transaction.exch_mut().set_suspended_subscribe_req(self);
} else {
// Completing a SubscribeResponse message
// let mut tw = TLVWriter::new(tx.get_writebuf()?);
// tw.end_container()?;
transaction.complete();
}
Ok(true)
}
}
pub trait InteractionHandler { pub trait InteractionHandler {
fn handle<'a>(&mut self, ctx: &'a mut ProtoCtx) -> Result<Option<&'a [u8]>, Error>; fn handle<'a>(&mut self, ctx: &'a mut ProtoCtx) -> Result<Option<&'a [u8]>, Error>;
} }
@ -472,15 +729,14 @@ where
T: DataHandler, T: DataHandler,
{ {
pub fn handle<'a>(&mut self, ctx: &'a mut ProtoCtx) -> Result<Option<&'a [u8]>, Error> { pub fn handle<'a>(&mut self, ctx: &'a mut ProtoCtx) -> Result<Option<&'a [u8]>, Error> {
let interaction = Interaction::new(ctx.rx)?;
let mut transaction = Transaction::new(&mut ctx.exch_ctx); let mut transaction = Transaction::new(&mut ctx.exch_ctx);
let reply = if interaction.initiate_tx(ctx.tx, &mut transaction)? { let reply =
self.0.handle(&interaction, ctx.tx, &mut transaction)?; if let Some(interaction) = Interaction::initiate(ctx.rx, ctx.tx, &mut transaction)? {
interaction.complete_tx(ctx.tx, &mut transaction)? self.0.handle(interaction, ctx.tx, &mut transaction)?
} else { } else {
true true
}; };
Ok(reply.then_some(ctx.tx.as_slice())) Ok(reply.then_some(ctx.tx.as_slice()))
} }
@ -495,17 +751,14 @@ where
&mut self, &mut self,
ctx: &'a mut ProtoCtx<'_, '_>, ctx: &'a mut ProtoCtx<'_, '_>,
) -> Result<Option<&'a [u8]>, Error> { ) -> Result<Option<&'a [u8]>, Error> {
let interaction = Interaction::new(ctx.rx)?;
let mut transaction = Transaction::new(&mut ctx.exch_ctx); let mut transaction = Transaction::new(&mut ctx.exch_ctx);
let reply = if interaction.initiate_tx(ctx.tx, &mut transaction)? { let reply =
self.0 if let Some(interaction) = Interaction::initiate(ctx.rx, ctx.tx, &mut transaction)? {
.handle(&interaction, ctx.tx, &mut transaction) self.0.handle(interaction, ctx.tx, &mut transaction).await?
.await?; } else {
interaction.complete_tx(ctx.tx, &mut transaction)? true
} else { };
true
};
Ok(reply.then_some(ctx.tx.as_slice())) Ok(reply.then_some(ctx.tx.as_slice()))
} }

View file

@ -328,7 +328,7 @@ impl<'a> Case<'a> {
tw.end_container()?; tw.end_container()?;
let key = KeyPair::new_from_public(initiator_noc_cert.get_pubkey())?; let key = KeyPair::new_from_public(initiator_noc_cert.get_pubkey())?;
key.verify_msg(write_buf.into_slice(), sign)?; key.verify_msg(write_buf.as_slice(), sign)?;
Ok(()) Ok(())
} }
@ -508,7 +508,7 @@ impl<'a> Case<'a> {
cipher_text, cipher_text,
cipher_text.len() - TAG_LEN, cipher_text.len() - TAG_LEN,
)?; )?;
Ok(write_buf.into_slice().len()) Ok(write_buf.as_slice().len())
} }
fn get_sigma2_sign( fn get_sigma2_sign(
@ -531,7 +531,7 @@ impl<'a> Case<'a> {
tw.str8(TagType::Context(4), peer_pub_key)?; tw.str8(TagType::Context(4), peer_pub_key)?;
tw.end_container()?; tw.end_container()?;
//println!("TBS is {:x?}", write_buf.as_borrow_slice()); //println!("TBS is {:x?}", write_buf.as_borrow_slice());
fabric.sign_msg(write_buf.into_slice(), signature) fabric.sign_msg(write_buf.as_slice(), signature)
} }
} }

View file

@ -22,6 +22,7 @@ use core::time::Duration;
use log::{error, info, trace}; use log::{error, info, trace};
use crate::error::Error; use crate::error::Error;
use crate::interaction_model::core::{ResumeReadReq, ResumeSubscribeReq};
use crate::secure_channel; use crate::secure_channel;
use crate::secure_channel::case::CaseSession; use crate::secure_channel::case::CaseSession;
use crate::utils::epoch::Epoch; use crate::utils::epoch::Epoch;
@ -68,6 +69,8 @@ enum State {
pub enum DataOption { pub enum DataOption {
CaseSession(CaseSession), CaseSession(CaseSession),
Time(Duration), Time(Duration),
SuspendedReadReq(ResumeReadReq),
SuspendedSubscibeReq(ResumeSubscribeReq),
#[default] #[default]
None, None,
} }
@ -124,18 +127,14 @@ impl Exchange {
self.role self.role
} }
pub fn is_data_none(&self) -> bool { pub fn clear_data(&mut self) {
matches!(self.data, DataOption::None) self.data = DataOption::None;
} }
pub fn set_case_session(&mut self, session: CaseSession) { pub fn set_case_session(&mut self, session: CaseSession) {
self.data = DataOption::CaseSession(session); self.data = DataOption::CaseSession(session);
} }
pub fn clear_data(&mut self) {
self.data = DataOption::None;
}
pub fn get_case_session(&mut self) -> Option<&mut CaseSession> { pub fn get_case_session(&mut self) -> Option<&mut CaseSession> {
if let DataOption::CaseSession(session) = &mut self.data { if let DataOption::CaseSession(session) = &mut self.data {
Some(session) Some(session)
@ -154,6 +153,34 @@ impl Exchange {
} }
} }
pub fn set_suspended_read_req(&mut self, req: ResumeReadReq) {
self.data = DataOption::SuspendedReadReq(req);
}
pub fn take_suspended_read_req(&mut self) -> Option<ResumeReadReq> {
let old = core::mem::replace(&mut self.data, DataOption::None);
if let DataOption::SuspendedReadReq(req) = old {
Some(req)
} else {
self.data = old;
None
}
}
pub fn set_suspended_subscribe_req(&mut self, req: ResumeSubscribeReq) {
self.data = DataOption::SuspendedSubscibeReq(req);
}
pub fn take_suspended_subscribe_req(&mut self) -> Option<ResumeSubscribeReq> {
let old = core::mem::replace(&mut self.data, DataOption::None);
if let DataOption::SuspendedSubscibeReq(req) = old {
Some(req)
} else {
self.data = old;
None
}
}
pub fn set_data_time(&mut self, expiry_ts: Option<Duration>) { pub fn set_data_time(&mut self, expiry_ts: Option<Duration>) {
if let Some(t) = expiry_ts { if let Some(t) = expiry_ts {
self.data = DataOption::Time(t); self.data = DataOption::Time(t);
@ -430,7 +457,7 @@ mod tests {
error::Error, error::Error,
transport::{ transport::{
network::Address, network::Address,
packet::Packet, packet::{Packet, MAX_TX_BUF_SIZE},
session::{CloneData, SessionMode, MAX_SESSIONS}, session::{CloneData, SessionMode, MAX_SESSIONS},
}, },
utils::{ utils::{
@ -505,7 +532,7 @@ mod tests {
/// - The sessions are evicted in LRU /// - The sessions are evicted in LRU
/// - The exchanges associated with those sessions are evicted too /// - The exchanges associated with those sessions are evicted too
fn test_sess_evict() { fn test_sess_evict() {
let mut mgr = ExchangeMgr::new(sys_epoch, dummy_rand); // TODO let mut mgr = ExchangeMgr::new(sys_epoch, dummy_rand);
fill_sessions(&mut mgr, MAX_SESSIONS + 1); fill_sessions(&mut mgr, MAX_SESSIONS + 1);
// Sessions are now full from local session id 1 to 16 // Sessions are now full from local session id 1 to 16
@ -531,7 +558,7 @@ mod tests {
let result = mgr.add_session(&get_clone_data(new_peer_sess_id, new_local_sess_id)); let result = mgr.add_session(&get_clone_data(new_peer_sess_id, new_local_sess_id));
assert!(matches!(result, Err(Error::NoSpace))); assert!(matches!(result, Err(Error::NoSpace)));
let mut buf = [0; 1500]; let mut buf = [0; MAX_TX_BUF_SIZE];
let tx = &mut Packet::new_tx(&mut buf); let tx = &mut Packet::new_tx(&mut buf);
let evicted = mgr.evict_session(tx).unwrap(); let evicted = mgr.evict_session(tx).unwrap();
assert!(evicted); assert!(evicted);

View file

@ -33,6 +33,7 @@ use super::{
}; };
pub const MAX_RX_BUF_SIZE: usize = 1583; pub const MAX_RX_BUF_SIZE: usize = 1583;
pub const MAX_TX_BUF_SIZE: usize = 1280 - 40/*IPV6 header size*/ - 8/*UDP header size*/;
type Buffer = [u8; MAX_RX_BUF_SIZE]; type Buffer = [u8; MAX_RX_BUF_SIZE];
// TODO: I am not very happy with this construction, need to find another way to do this // TODO: I am not very happy with this construction, need to find another way to do this

View file

@ -311,7 +311,7 @@ mod tests {
encrypt_in_place(send_ctr, 0, &plain_hdr, &mut writebuf, &key).unwrap(); encrypt_in_place(send_ctr, 0, &plain_hdr, &mut writebuf, &key).unwrap();
assert_eq!( assert_eq!(
writebuf.into_slice(), writebuf.as_slice(),
[ [
189, 83, 250, 121, 38, 87, 97, 17, 153, 78, 243, 20, 36, 11, 131, 142, 136, 165, 189, 83, 250, 121, 38, 87, 97, 17, 153, 78, 243, 20, 36, 11, 131, 142, 136, 165,
227, 107, 204, 129, 193, 153, 42, 131, 138, 254, 22, 190, 76, 244, 116, 45, 156, 227, 107, 204, 129, 193, 153, 42, 131, 138, 254, 22, 190, 76, 244, 116, 45, 156,

View file

@ -267,7 +267,7 @@ impl Session {
let mut tmp_buf = [0_u8; proto_hdr::max_proto_hdr_len()]; let mut tmp_buf = [0_u8; proto_hdr::max_proto_hdr_len()];
let mut write_buf = WriteBuf::new(&mut tmp_buf); let mut write_buf = WriteBuf::new(&mut tmp_buf);
tx.proto.encode(&mut write_buf)?; tx.proto.encode(&mut write_buf)?;
tx.get_writebuf()?.prepend(write_buf.into_slice())?; tx.get_writebuf()?.prepend(write_buf.as_slice())?;
// Generate plain-text header // Generate plain-text header
if self.mode == SessionMode::PlainText { if self.mode == SessionMode::PlainText {
@ -278,7 +278,7 @@ impl Session {
let mut tmp_buf = [0_u8; plain_hdr::max_plain_hdr_len()]; let mut tmp_buf = [0_u8; plain_hdr::max_plain_hdr_len()];
let mut write_buf = WriteBuf::new(&mut tmp_buf); let mut write_buf = WriteBuf::new(&mut tmp_buf);
tx.plain.encode(&mut write_buf)?; tx.plain.encode(&mut write_buf)?;
let plain_hdr_bytes = write_buf.into_slice(); let plain_hdr_bytes = write_buf.as_slice();
trace!("unencrypted packet: {:x?}", tx.as_mut_slice()); trace!("unencrypted packet: {:x?}", tx.as_mut_slice());
let ctr = tx.plain.ctr; let ctr = tx.plain.ctr;

View file

@ -18,44 +18,21 @@
use crate::error::*; use crate::error::*;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
/// Shrink WriteBuf
///
/// This Macro creates a new (child) WriteBuf which has a truncated slice end.
/// - It accepts a WriteBuf, and the size to reserve (truncate) towards the end.
/// - It returns the new (child) WriteBuf
#[macro_export]
macro_rules! wb_shrink {
($orig_wb:ident, $reserve:ident) => {{
let m_data = $orig_wb.empty_as_mut_slice();
let m_wb = WriteBuf::new(m_data, m_data.len() - $reserve);
(m_wb)
}};
}
/// Unshrink WriteBuf
///
/// This macro unshrinks the WriteBuf
/// - It accepts the original WriteBuf and the child WriteBuf (that was the result of wb_shrink)
/// After this call, the child WriteBuf shouldn't be used
#[macro_export]
macro_rules! wb_unshrink {
($orig_wb:ident, $new_wb:ident) => {{
let m_data_len = $new_wb.as_slice().len();
$orig_wb.forward_tail_by(m_data_len);
}};
}
#[derive(Debug)] #[derive(Debug)]
pub struct WriteBuf<'a> { pub struct WriteBuf<'a> {
buf: &'a mut [u8], buf: &'a mut [u8],
buf_size: usize,
start: usize, start: usize,
end: usize, end: usize,
} }
impl<'a> WriteBuf<'a> { impl<'a> WriteBuf<'a> {
pub fn new(buf: &'a mut [u8]) -> Self { pub fn new(buf: &'a mut [u8]) -> Self {
let buf_size = buf.len();
Self { Self {
buf, buf,
buf_size,
start: 0, start: 0,
end: 0, end: 0,
} }
@ -73,10 +50,6 @@ impl<'a> WriteBuf<'a> {
self.end += new_offset self.end += new_offset
} }
pub fn into_slice(self) -> &'a [u8] {
&self.buf[self.start..self.end]
}
pub fn as_slice(&self) -> &[u8] { pub fn as_slice(&self) -> &[u8] {
&self.buf[self.start..self.end] &self.buf[self.start..self.end]
} }
@ -86,20 +59,43 @@ impl<'a> WriteBuf<'a> {
} }
pub fn empty_as_mut_slice(&mut self) -> &mut [u8] { pub fn empty_as_mut_slice(&mut self) -> &mut [u8] {
&mut self.buf[self.end..] &mut self.buf[self.end..self.buf_size]
} }
pub fn reset(&mut self, reserve: usize) { pub fn reset(&mut self) {
self.start = reserve; self.buf_size = self.buf.len();
self.end = reserve; self.start = 0;
self.end = 0;
} }
pub fn reserve(&mut self, reserve: usize) -> Result<(), Error> { pub fn reserve(&mut self, reserve: usize) -> Result<(), Error> {
if self.end != 0 || self.start != 0 { if self.end != 0 || self.start != 0 || self.buf_size != self.buf.len() {
return Err(Error::Invalid); Err(Error::Invalid)
} else if reserve > self.buf_size {
Err(Error::NoSpace)
} else {
self.start = reserve;
self.end = reserve;
Ok(())
}
}
pub fn shrink(&mut self, with: usize) -> Result<(), Error> {
if self.end + with <= self.buf_size {
self.buf_size -= with;
Ok(())
} else {
Err(Error::NoSpace)
}
}
pub fn expand(&mut self, by: usize) -> Result<(), Error> {
if self.buf.len() - self.buf_size >= by {
self.buf_size += by;
Ok(())
} else {
Err(Error::NoSpace)
} }
self.reset(reserve);
Ok(())
} }
pub fn prepend_with<F>(&mut self, size: usize, f: F) -> Result<(), Error> pub fn prepend_with<F>(&mut self, size: usize, f: F) -> Result<(), Error>
@ -125,7 +121,7 @@ impl<'a> WriteBuf<'a> {
where where
F: FnOnce(&mut Self), F: FnOnce(&mut Self),
{ {
if self.end + size <= self.buf.len() { if self.end + size <= self.buf_size {
f(self); f(self);
self.end += size; self.end += size;
return Ok(()); return Ok(());
@ -274,7 +270,7 @@ mod tests {
buf.prepend(&new_slice).unwrap(); buf.prepend(&new_slice).unwrap();
assert_eq!( assert_eq!(
buf.into_slice(), buf.as_slice(),
[ [
0xa, 0xb, 0xc, 1, 65, 0, 0xbe, 0xba, 0xfe, 0xca, 0xbe, 0xba, 0xfe, 0xca, 0xbe, 0xa, 0xb, 0xc, 1, 65, 0, 0xbe, 0xba, 0xfe, 0xca, 0xbe, 0xba, 0xfe, 0xca, 0xbe,
0xba, 0xfe, 0xca 0xba, 0xfe, 0xca

View file

@ -24,16 +24,20 @@ use matter::{
cluster_on_off::{self, OnOffCluster}, cluster_on_off::{self, OnOffCluster},
core::DataModel, core::DataModel,
device_types::{DEV_TYPE_ON_OFF_LIGHT, DEV_TYPE_ROOT_NODE}, device_types::{DEV_TYPE_ON_OFF_LIGHT, DEV_TYPE_ROOT_NODE},
objects::{ChainedHandler, Endpoint, Node, Privilege}, objects::{Endpoint, Node, Privilege},
root_endpoint::{self, RootEndpointHandler}, root_endpoint::{self, RootEndpointHandler},
sdm::{ sdm::{
admin_commissioning, admin_commissioning,
dev_att::{DataType, DevAttDataFetcher}, dev_att::{DataType, DevAttDataFetcher},
general_commissioning, noc, nw_commissioning, general_commissioning, noc, nw_commissioning,
}, },
system_model::access_control, system_model::{
access_control,
descriptor::{self, DescriptorCluster},
},
}, },
error::Error, error::Error,
handler_chain_type,
interaction_model::core::{InteractionModel, OpCode}, interaction_model::core::{InteractionModel, OpCode},
mdns::Mdns, mdns::Mdns,
tlv::{TLVWriter, TagType, ToTLV}, tlv::{TLVWriter, TagType, ToTLV},
@ -41,6 +45,7 @@ use matter::{
transport::{ transport::{
exchange::{self, Exchange, ExchangeCtx}, exchange::{self, Exchange, ExchangeCtx},
network::Address, network::Address,
packet::MAX_RX_BUF_SIZE,
proto_ctx::ProtoCtx, proto_ctx::ProtoCtx,
session::{CaseDetails, CloneData, NocCatIds, SessionMgr, SessionMode}, session::{CaseDetails, CloneData, NocCatIds, SessionMgr, SessionMode},
}, },
@ -97,12 +102,9 @@ impl<'a> ImInput<'a> {
} }
} }
pub type DmHandler<'a> = ChainedHandler< pub type DmHandler<'a> = handler_chain_type!(OnOffCluster, EchoCluster, DescriptorCluster, EchoCluster | RootEndpointHandler<'a>);
OnOffCluster,
ChainedHandler<EchoCluster, ChainedHandler<EchoCluster, RootEndpointHandler<'a>>>,
>;
pub fn matter<'a>(mdns: &'a mut dyn Mdns) -> Matter<'_> { pub fn matter(mdns: &mut dyn Mdns) -> Matter<'_> {
Matter::new(&BASIC_INFO, mdns, sys_epoch, dummy_rand) Matter::new(&BASIC_INFO, mdns, sys_epoch, dummy_rand)
} }
@ -132,6 +134,7 @@ impl<'a> ImEngine<'a> {
Endpoint { Endpoint {
id: 0, id: 0,
clusters: &[ clusters: &[
descriptor::CLUSTER,
cluster_basic_information::CLUSTER, cluster_basic_information::CLUSTER,
general_commissioning::CLUSTER, general_commissioning::CLUSTER,
nw_commissioning::CLUSTER, nw_commissioning::CLUSTER,
@ -144,13 +147,18 @@ impl<'a> ImEngine<'a> {
}, },
Endpoint { Endpoint {
id: 1, id: 1,
clusters: &[echo_cluster::CLUSTER, cluster_on_off::CLUSTER], clusters: &[
descriptor::CLUSTER,
cluster_on_off::CLUSTER,
echo_cluster::CLUSTER,
],
device_type: DEV_TYPE_ON_OFF_LIGHT, device_type: DEV_TYPE_ON_OFF_LIGHT,
}, },
], ],
}, },
root_endpoint::handler(0, &DummyDevAtt {}, matter) root_endpoint::handler(0, &DummyDevAtt {}, 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, echo_cluster::ID, EchoCluster::new(3, *matter.borrow())) .chain(1, echo_cluster::ID, EchoCluster::new(3, *matter.borrow()))
.chain(1, cluster_on_off::ID, OnOffCluster::new(*matter.borrow())), .chain(1, cluster_on_off::ID, OnOffCluster::new(*matter.borrow())),
); );
@ -164,7 +172,7 @@ impl<'a> ImEngine<'a> {
pub fn echo_cluster(&self, endpoint: u16) -> &EchoCluster { pub fn echo_cluster(&self, endpoint: u16) -> &EchoCluster {
match endpoint { match endpoint {
0 => &self.im.0.handler.next.next.handler, 0 => &self.im.0.handler.next.next.next.handler,
1 => &self.im.0.handler.next.handler, 1 => &self.im.0.handler.next.handler,
_ => panic!(), _ => panic!(),
} }
@ -196,8 +204,8 @@ impl<'a> ImEngine<'a> {
sess, sess,
epoch: *self.matter.borrow(), epoch: *self.matter.borrow(),
}; };
let mut tx_buf = [0; 1500]; let mut rx_buf = [0; MAX_RX_BUF_SIZE];
let mut rx_buf = [0; 1500]; let mut tx_buf = [0; 1450]; // For the long read tests to run unchanged
let mut rx = Packet::new_rx(&mut rx_buf); let mut rx = Packet::new_rx(&mut rx_buf);
let mut tx = Packet::new_tx(&mut tx_buf); let mut tx = Packet::new_tx(&mut tx_buf);
// Create fake rx packet // Create fake rx packet

View file

@ -30,11 +30,13 @@ use matter::{
}, },
messages::{msg::SubscribeReq, GenericPath}, messages::{msg::SubscribeReq, GenericPath},
}, },
mdns::DummyMdns,
tlv::{self, ElementType, FromTLV, TLVElement, TagType, ToTLV}, tlv::{self, ElementType, FromTLV, TLVElement, TagType, ToTLV},
transport::{ transport::{
exchange::{self, Exchange}, exchange::{self, Exchange},
udp::MAX_RX_BUF_SIZE, udp::MAX_RX_BUF_SIZE,
}, },
Matter,
}; };
use crate::{ use crate::{
@ -42,28 +44,28 @@ use crate::{
common::{ common::{
attributes::*, attributes::*,
echo_cluster as echo, echo_cluster as echo,
im_engine::{ImEngine, ImInput}, im_engine::{matter, ImEngine, ImInput},
}, },
}; };
pub struct LongRead { pub struct LongRead<'a> {
im_engine: ImEngine, im_engine: ImEngine<'a>,
} }
impl LongRead { impl<'a> LongRead<'a> {
pub fn new() -> Self { pub fn new(matter: &'a Matter<'a>) -> Self {
let mut im_engine = ImEngine::new(); let mut im_engine = ImEngine::new(matter);
// Use the same exchange for all parts of the transaction // Use the same exchange for all parts of the transaction
im_engine.exch = Some(Exchange::new(1, 0, exchange::Role::Responder)); im_engine.exch = Some(Exchange::new(1, 0, exchange::Role::Responder));
Self { im_engine } Self { im_engine }
} }
pub fn process<'a>( pub fn process<'p>(
&mut self, &mut self,
action: OpCode, action: OpCode,
data: &dyn ToTLV, data: &dyn ToTLV,
data_out: &'a mut [u8], data_out: &'p mut [u8],
) -> (u8, &'a mut [u8]) { ) -> (u8, &'p [u8]) {
let input = ImInput::new(action, data); let input = ImInput::new(action, data);
let (response, output) = self.im_engine.process(&input, data_out); let (response, output) = self.im_engine.process(&input, data_out);
(response, output) (response, output)
@ -82,49 +84,139 @@ fn wildcard_read_resp(part: u8) -> Vec<AttrResp<'static>> {
attr_data!(0, 29, descriptor::Attributes::ClientList, dont_care), attr_data!(0, 29, descriptor::Attributes::ClientList, dont_care),
attr_data!(0, 40, GlobalElements::FeatureMap, dont_care), attr_data!(0, 40, GlobalElements::FeatureMap, dont_care),
attr_data!(0, 40, GlobalElements::AttributeList, dont_care), attr_data!(0, 40, GlobalElements::AttributeList, dont_care),
attr_data!(0, 40, basic_info::Attributes::DMRevision, dont_care), attr_data!(
attr_data!(0, 40, basic_info::Attributes::VendorId, dont_care), 0,
attr_data!(0, 40, basic_info::Attributes::ProductId, dont_care), 40,
attr_data!(0, 40, basic_info::Attributes::HwVer, dont_care), basic_info::AttributesDiscriminants::DMRevision,
attr_data!(0, 40, basic_info::Attributes::SwVer, dont_care), dont_care
attr_data!(0, 40, basic_info::Attributes::SwVerString, dont_care), ),
attr_data!(0, 40, basic_info::Attributes::SerialNo, dont_care), attr_data!(
0,
40,
basic_info::AttributesDiscriminants::VendorId,
dont_care
),
attr_data!(
0,
40,
basic_info::AttributesDiscriminants::ProductId,
dont_care
),
attr_data!(0, 40, basic_info::AttributesDiscriminants::HwVer, dont_care),
attr_data!(0, 40, basic_info::AttributesDiscriminants::SwVer, dont_care),
attr_data!(
0,
40,
basic_info::AttributesDiscriminants::SwVerString,
dont_care
),
attr_data!(
0,
40,
basic_info::AttributesDiscriminants::SerialNo,
dont_care
),
attr_data!(0, 48, GlobalElements::FeatureMap, dont_care), attr_data!(0, 48, GlobalElements::FeatureMap, dont_care),
attr_data!(0, 48, GlobalElements::AttributeList, dont_care), attr_data!(0, 48, GlobalElements::AttributeList, dont_care),
attr_data!(0, 48, gen_comm::Attributes::BreadCrumb, dont_care),
attr_data!(0, 48, gen_comm::Attributes::RegConfig, dont_care),
attr_data!(0, 48, gen_comm::Attributes::LocationCapability, dont_care),
attr_data!( attr_data!(
0, 0,
48, 48,
gen_comm::Attributes::BasicCommissioningInfo, gen_comm::AttributesDiscriminants::BreadCrumb,
dont_care
),
attr_data!(
0,
48,
gen_comm::AttributesDiscriminants::RegConfig,
dont_care
),
attr_data!(
0,
48,
gen_comm::AttributesDiscriminants::LocationCapability,
dont_care
),
attr_data!(
0,
48,
gen_comm::AttributesDiscriminants::BasicCommissioningInfo,
dont_care dont_care
), ),
attr_data!(0, 49, GlobalElements::FeatureMap, dont_care), attr_data!(0, 49, GlobalElements::FeatureMap, dont_care),
attr_data!(0, 49, GlobalElements::AttributeList, dont_care), attr_data!(0, 49, GlobalElements::AttributeList, dont_care),
attr_data!(0, 60, GlobalElements::FeatureMap, dont_care), attr_data!(0, 60, GlobalElements::FeatureMap, dont_care),
attr_data!(0, 60, GlobalElements::AttributeList, dont_care), attr_data!(0, 60, GlobalElements::AttributeList, dont_care),
attr_data!(0, 60, adm_comm::Attributes::WindowStatus, dont_care), attr_data!(
attr_data!(0, 60, adm_comm::Attributes::AdminFabricIndex, dont_care), 0,
attr_data!(0, 60, adm_comm::Attributes::AdminVendorId, dont_care), 60,
adm_comm::AttributesDiscriminants::WindowStatus,
dont_care
),
attr_data!(
0,
60,
adm_comm::AttributesDiscriminants::AdminFabricIndex,
dont_care
),
attr_data!(
0,
60,
adm_comm::AttributesDiscriminants::AdminVendorId,
dont_care
),
attr_data!(0, 62, GlobalElements::FeatureMap, dont_care), attr_data!(0, 62, GlobalElements::FeatureMap, dont_care),
attr_data!(0, 62, GlobalElements::AttributeList, dont_care), attr_data!(0, 62, GlobalElements::AttributeList, dont_care),
attr_data!(0, 62, noc::Attributes::CurrentFabricIndex, dont_care), attr_data!(
attr_data!(0, 62, noc::Attributes::Fabrics, dont_care), 0,
attr_data!(0, 62, noc::Attributes::SupportedFabrics, dont_care), 62,
attr_data!(0, 62, noc::Attributes::CommissionedFabrics, dont_care), noc::AttributesDiscriminants::CurrentFabricIndex,
dont_care
),
attr_data!(0, 62, noc::AttributesDiscriminants::Fabrics, dont_care),
attr_data!(
0,
62,
noc::AttributesDiscriminants::SupportedFabrics,
dont_care
),
attr_data!(
0,
62,
noc::AttributesDiscriminants::CommissionedFabrics,
dont_care
),
attr_data!(0, 31, GlobalElements::FeatureMap, dont_care), attr_data!(0, 31, GlobalElements::FeatureMap, dont_care),
attr_data!(0, 31, GlobalElements::AttributeList, dont_care), attr_data!(0, 31, GlobalElements::AttributeList, dont_care),
attr_data!(0, 31, acl::Attributes::Acl, dont_care), attr_data!(0, 31, acl::AttributesDiscriminants::Acl, dont_care),
attr_data!(0, 31, acl::Attributes::Extension, dont_care), attr_data!(0, 31, acl::AttributesDiscriminants::Extension, dont_care),
attr_data!(0, 31, acl::Attributes::SubjectsPerEntry, dont_care), attr_data!(
attr_data!(0, 31, acl::Attributes::TargetsPerEntry, dont_care), 0,
attr_data!(0, 31, acl::Attributes::EntriesPerFabric, dont_care), 31,
acl::AttributesDiscriminants::SubjectsPerEntry,
dont_care
),
attr_data!(
0,
31,
acl::AttributesDiscriminants::TargetsPerEntry,
dont_care
),
attr_data!(
0,
31,
acl::AttributesDiscriminants::EntriesPerFabric,
dont_care
),
attr_data!(0, echo::ID, GlobalElements::FeatureMap, dont_care), attr_data!(0, echo::ID, GlobalElements::FeatureMap, dont_care),
attr_data!(0, echo::ID, GlobalElements::AttributeList, dont_care), attr_data!(0, echo::ID, GlobalElements::AttributeList, dont_care),
attr_data!(0, echo::ID, echo::Attributes::Att1, dont_care), attr_data!(0, echo::ID, echo::AttributesDiscriminants::Att1, dont_care),
attr_data!(0, echo::ID, echo::Attributes::Att2, dont_care), attr_data!(0, echo::ID, echo::AttributesDiscriminants::Att2, dont_care),
attr_data!(0, echo::ID, echo::Attributes::AttCustom, dont_care), attr_data!(
0,
echo::ID,
echo::AttributesDiscriminants::AttCustom,
dont_care
),
attr_data!(1, 29, GlobalElements::FeatureMap, dont_care), attr_data!(1, 29, GlobalElements::FeatureMap, dont_care),
attr_data!(1, 29, GlobalElements::AttributeList, dont_care), attr_data!(1, 29, GlobalElements::AttributeList, dont_care),
attr_data!(1, 29, descriptor::Attributes::DeviceTypeList, dont_care), attr_data!(1, 29, descriptor::Attributes::DeviceTypeList, dont_care),
@ -136,12 +228,17 @@ fn wildcard_read_resp(part: u8) -> Vec<AttrResp<'static>> {
attr_data!(1, 29, descriptor::Attributes::ClientList, dont_care), attr_data!(1, 29, descriptor::Attributes::ClientList, dont_care),
attr_data!(1, 6, GlobalElements::FeatureMap, dont_care), attr_data!(1, 6, GlobalElements::FeatureMap, dont_care),
attr_data!(1, 6, GlobalElements::AttributeList, dont_care), attr_data!(1, 6, GlobalElements::AttributeList, dont_care),
attr_data!(1, 6, onoff::Attributes::OnOff, dont_care), attr_data!(1, 6, onoff::AttributesDiscriminants::OnOff, dont_care),
attr_data!(1, echo::ID, GlobalElements::FeatureMap, dont_care), attr_data!(1, echo::ID, GlobalElements::FeatureMap, dont_care),
attr_data!(1, echo::ID, GlobalElements::AttributeList, dont_care), attr_data!(1, echo::ID, GlobalElements::AttributeList, dont_care),
attr_data!(1, echo::ID, echo::Attributes::Att1, dont_care), attr_data!(1, echo::ID, echo::AttributesDiscriminants::Att1, dont_care),
attr_data!(1, echo::ID, echo::Attributes::Att2, dont_care), attr_data!(1, echo::ID, echo::AttributesDiscriminants::Att2, dont_care),
attr_data!(1, echo::ID, echo::Attributes::AttCustom, dont_care), attr_data!(
1,
echo::ID,
echo::AttributesDiscriminants::AttCustom,
dont_care
),
]; ];
if part == 1 { if part == 1 {
@ -155,7 +252,9 @@ fn wildcard_read_resp(part: u8) -> Vec<AttrResp<'static>> {
fn test_long_read_success() { fn test_long_read_success() {
// Read the entire attribute database, which requires 2 reads to complete // Read the entire attribute database, which requires 2 reads to complete
let _ = env_logger::try_init(); let _ = env_logger::try_init();
let mut lr = LongRead::new(); let mut mdns = DummyMdns;
let matter = matter(&mut mdns);
let mut lr = LongRead::new(&matter);
let mut output = [0_u8; MAX_RX_BUF_SIZE + 100]; let mut output = [0_u8; MAX_RX_BUF_SIZE + 100];
let wc_path = GenericPath::new(None, None, None); let wc_path = GenericPath::new(None, None, None);
@ -187,7 +286,9 @@ fn test_long_read_success() {
fn test_long_read_subscription_success() { fn test_long_read_subscription_success() {
// Subscribe to the entire attribute database, which requires 2 reads to complete // Subscribe to the entire attribute database, which requires 2 reads to complete
let _ = env_logger::try_init(); let _ = env_logger::try_init();
let mut lr = LongRead::new(); let mut mdns = DummyMdns;
let matter = matter(&mut mdns);
let mut lr = LongRead::new(&matter);
let mut output = [0_u8; MAX_RX_BUF_SIZE + 100]; let mut output = [0_u8; MAX_RX_BUF_SIZE + 100];
let wc_path = GenericPath::new(None, None, None); let wc_path = GenericPath::new(None, None, None);
@ -219,6 +320,6 @@ fn test_long_read_subscription_success() {
tlv::print_tlv_list(out_data); tlv::print_tlv_list(out_data);
let root = tlv::get_root_node_struct(out_data).unwrap(); let root = tlv::get_root_node_struct(out_data).unwrap();
let subs_resp = SubscribeResp::from_tlv(&root).unwrap(); let subs_resp = SubscribeResp::from_tlv(&root).unwrap();
assert_eq!(out_code, OpCode::SubscriptResponse as u8); assert_eq!(out_code, OpCode::SubscribeResponse as u8);
assert_eq!(subs_resp.subs_id, 1); assert_eq!(subs_resp.subs_id, 1);
} }

View file

@ -22,6 +22,6 @@ mod data_model {
mod attribute_lists; mod attribute_lists;
mod attributes; mod attributes;
mod commands; mod commands;
// TODO mod long_reads; mod long_reads;
mod timed_requests; mod timed_requests;
} }

View file

@ -25,6 +25,8 @@ use matter::transport::exchange::Exchange;
use matter::transport::exchange::ExchangeCtx; use matter::transport::exchange::ExchangeCtx;
use matter::transport::network::Address; use matter::transport::network::Address;
use matter::transport::packet::Packet; use matter::transport::packet::Packet;
use matter::transport::packet::MAX_RX_BUF_SIZE;
use matter::transport::packet::MAX_TX_BUF_SIZE;
use matter::transport::proto_ctx::ProtoCtx; use matter::transport::proto_ctx::ProtoCtx;
use matter::transport::session::SessionMgr; use matter::transport::session::SessionMgr;
use matter::utils::epoch::dummy_epoch; use matter::utils::epoch::dummy_epoch;
@ -52,30 +54,27 @@ impl DataModel {
impl DataHandler for DataModel { impl DataHandler for DataModel {
fn handle( fn handle(
&mut self, &mut self,
interaction: &Interaction, interaction: Interaction,
_tx: &mut Packet, _tx: &mut Packet,
_transaction: &mut Transaction, _transaction: &mut Transaction,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
match interaction { if let Interaction::Invoke(req) = interaction {
Interaction::Invoke(req) => { if let Some(inv_requests) = &req.inv_requests {
if let Some(inv_requests) = &req.inv_requests { for i in inv_requests.iter() {
for i in inv_requests.iter() { let data = if let Some(data) = i.data.unwrap_tlv() {
let data = if let Some(data) = i.data.unwrap_tlv() { data
data } else {
} else { continue;
continue; };
}; let cmd_path_ib = i.path;
let cmd_path_ib = i.path; let mut common_data = &mut self.node;
let mut common_data = &mut self.node; common_data.endpoint = cmd_path_ib.path.endpoint.unwrap_or(1);
common_data.endpoint = cmd_path_ib.path.endpoint.unwrap_or(1); common_data.cluster = cmd_path_ib.path.cluster.unwrap_or(0);
common_data.cluster = cmd_path_ib.path.cluster.unwrap_or(0); common_data.command = cmd_path_ib.path.leaf.unwrap_or(0) as u16;
common_data.command = cmd_path_ib.path.leaf.unwrap_or(0) as u16; data.confirm_struct().unwrap();
data.confirm_struct().unwrap(); common_data.variable = data.find_tag(0).unwrap().u8().unwrap();
common_data.variable = data.find_tag(0).unwrap().u8().unwrap();
}
} }
} }
_ => (),
} }
Ok(false) Ok(false)
@ -109,8 +108,8 @@ fn handle_data(action: OpCode, data_in: &[u8], data_out: &mut [u8]) -> (DataMode
sess, sess,
epoch: dummy_epoch, epoch: dummy_epoch,
}; };
let mut rx_buf = [0; 1500]; let mut rx_buf = [0; MAX_RX_BUF_SIZE];
let mut tx_buf = [0; 1500]; let mut tx_buf = [0; MAX_TX_BUF_SIZE];
let mut rx = Packet::new_rx(&mut rx_buf); let mut rx = Packet::new_rx(&mut rx_buf);
let mut tx = Packet::new_tx(&mut tx_buf); let mut tx = Packet::new_tx(&mut tx_buf);
// Create fake rx packet // Create fake rx packet

View file

@ -138,11 +138,20 @@ fn gen_totlv_for_struct(
let expanded = quote! { let expanded = quote! {
impl #generics ToTLV for #struct_name #generics { impl #generics ToTLV for #struct_name #generics {
fn to_tlv(&self, tw: &mut TLVWriter, tag_type: TagType) -> Result<(), Error> { fn to_tlv(&self, tw: &mut TLVWriter, tag_type: TagType) -> Result<(), Error> {
tw. #datatype (tag_type)?; let anchor = tw.get_tail();
#(
self.#idents.to_tlv(tw, TagType::Context(#tags))?; if let Err(err) = (|| {
)* tw. #datatype (tag_type)?;
tw.end_container() #(
self.#idents.to_tlv(tw, TagType::Context(#tags))?;
)*
tw.end_container()
})() {
tw.rewind_to(anchor);
Err(err)
} else {
Ok(())
}
} }
} }
}; };
@ -179,17 +188,26 @@ fn gen_totlv_for_enum(
} }
let expanded = quote! { let expanded = quote! {
impl #generics ToTLV for #enum_name #generics { impl #generics ToTLV for #enum_name #generics {
fn to_tlv(&self, tw: &mut TLVWriter, tag_type: TagType) -> Result<(), Error> { fn to_tlv(&self, tw: &mut TLVWriter, tag_type: TagType) -> Result<(), Error> {
tw.start_struct(tag_type)?; let anchor = tw.get_tail();
match self {
#( if let Err(err) = (|| {
Self::#variant_names(c) => { c.to_tlv(tw, TagType::Context(#tags))?; }, tw.start_struct(tag_type)?;
)* match self {
} #(
tw.end_container() Self::#variant_names(c) => { c.to_tlv(tw, TagType::Context(#tags))?; },
} )*
} }
tw.end_container()
})() {
tw.rewind_to(anchor);
Err(err)
} else {
Ok(())
}
}
}
}; };
// panic!("Expanded to {}", expanded); // panic!("Expanded to {}", expanded);