Skip to content

Commit bd166e4

Browse files
authored
Merge pull request #85 from kedars/bugfix/ios_fixes_part2
Multiple fixes for working with iOS
2 parents ce3bf6b + e305ec1 commit bd166e4

File tree

9 files changed

+167
-22
lines changed

9 files changed

+167
-22
lines changed

examples/onoff_light/src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ mod dev_att;
3939
#[cfg(feature = "std")]
4040
fn main() -> Result<(), Error> {
4141
let thread = std::thread::Builder::new()
42-
.stack_size(150 * 1024)
42+
.stack_size(160 * 1024)
4343
.spawn(run)
4444
.unwrap();
4545

@@ -72,6 +72,8 @@ fn run() -> Result<(), Error> {
7272
sw_ver_str: "1",
7373
serial_no: "aabbccdd",
7474
device_name: "OnOff Light",
75+
product_name: "Light123",
76+
vendor_name: "Vendor PQR",
7577
};
7678

7779
let (ipv4_addr, ipv6_addr, interface) = initialize_network()?;

rs-matter/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rs-matter"
3-
version = "0.1.0"
3+
version = "0.1.1"
44
edition = "2021"
55
authors = ["Kedar Sovani <[email protected]>", "Ivan Markov", "Project CHIP Authors"]
66
description = "Native Rust implementation of the Matter (Smart-Home) ecosystem"

rs-matter/src/acl.rs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::{
2222
error::{Error, ErrorCode},
2323
fabric,
2424
interaction_model::messages::GenericPath,
25-
tlv::{self, FromTLV, TLVElement, TLVList, TLVWriter, TagType, ToTLV},
25+
tlv::{self, FromTLV, Nullable, TLVElement, TLVList, TLVWriter, TagType, ToTLV},
2626
transport::session::{Session, SessionMode, MAX_CAT_IDS_PER_NOC},
2727
utils::writebuf::WriteBuf,
2828
};
@@ -282,7 +282,15 @@ impl Target {
282282
}
283283

284284
type Subjects = [Option<u64>; SUBJECTS_PER_ENTRY];
285-
type Targets = [Option<Target>; TARGETS_PER_ENTRY];
285+
286+
type Targets = Nullable<[Option<Target>; TARGETS_PER_ENTRY]>;
287+
impl Targets {
288+
fn init_notnull() -> Self {
289+
const INIT_TARGETS: Option<Target> = None;
290+
Nullable::NotNull([INIT_TARGETS; TARGETS_PER_ENTRY])
291+
}
292+
}
293+
286294
#[derive(ToTLV, FromTLV, Clone, Debug, PartialEq)]
287295
#[tlvargs(start = 1)]
288296
pub struct AclEntry {
@@ -298,14 +306,12 @@ pub struct AclEntry {
298306
impl AclEntry {
299307
pub fn new(fab_idx: u8, privilege: Privilege, auth_mode: AuthMode) -> Self {
300308
const INIT_SUBJECTS: Option<u64> = None;
301-
const INIT_TARGETS: Option<Target> = None;
302-
303309
Self {
304310
fab_idx: Some(fab_idx),
305311
privilege,
306312
auth_mode,
307313
subjects: [INIT_SUBJECTS; SUBJECTS_PER_ENTRY],
308-
targets: [INIT_TARGETS; TARGETS_PER_ENTRY],
314+
targets: Targets::init_notnull(),
309315
}
310316
}
311317

@@ -324,12 +330,20 @@ impl AclEntry {
324330
}
325331

326332
pub fn add_target(&mut self, target: Target) -> Result<(), Error> {
333+
if self.targets.is_null() {
334+
self.targets = Targets::init_notnull();
335+
}
327336
let index = self
328337
.targets
338+
.as_ref()
339+
.notnull()
340+
.unwrap()
329341
.iter()
330342
.position(|s| s.is_none())
331343
.ok_or(ErrorCode::NoSpace)?;
332-
self.targets[index] = Some(target);
344+
345+
self.targets.as_mut().notnull().unwrap()[index] = Some(target);
346+
333347
Ok(())
334348
}
335349

@@ -358,12 +372,17 @@ impl AclEntry {
358372
fn match_access_desc(&self, object: &AccessDesc) -> bool {
359373
let mut allow = false;
360374
let mut entries_exist = false;
361-
for t in self.targets.iter().flatten() {
362-
entries_exist = true;
363-
if (t.endpoint.is_none() || t.endpoint == object.path.endpoint)
364-
&& (t.cluster.is_none() || t.cluster == object.path.cluster)
365-
{
366-
allow = true
375+
match self.targets.as_ref().notnull() {
376+
None => allow = true, // Allow if targets are NULL
377+
Some(targets) => {
378+
for t in targets.iter().flatten() {
379+
entries_exist = true;
380+
if (t.endpoint.is_none() || t.endpoint == object.path.endpoint)
381+
&& (t.cluster.is_none() || t.cluster == object.path.cluster)
382+
{
383+
allow = true
384+
}
385+
}
367386
}
368387
}
369388
if !entries_exist {

rs-matter/src/data_model/cluster_basic_information.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
* limitations under the License.
1616
*/
1717

18-
use core::convert::TryInto;
18+
use core::{cell::RefCell, convert::TryInto};
1919

2020
use super::objects::*;
21-
use crate::{attribute_enum, error::Error, utils::rand::Rand};
21+
use crate::{
22+
attribute_enum,
23+
error::{Error, ErrorCode},
24+
utils::rand::Rand,
25+
};
26+
use heapless::String;
2227
use strum::FromRepr;
2328

2429
pub const ID: u32 = 0x0028;
@@ -27,8 +32,11 @@ pub const ID: u32 = 0x0028;
2732
#[repr(u16)]
2833
pub enum Attributes {
2934
DMRevision(AttrType<u8>) = 0,
35+
VendorName(AttrUtfType) = 1,
3036
VendorId(AttrType<u16>) = 2,
37+
ProductName(AttrUtfType) = 3,
3138
ProductId(AttrType<u16>) = 4,
39+
NodeLabel(AttrUtfType) = 5,
3240
HwVer(AttrType<u16>) = 7,
3341
SwVer(AttrType<u32>) = 9,
3442
SwVerString(AttrUtfType) = 0xa,
@@ -39,8 +47,11 @@ attribute_enum!(Attributes);
3947

4048
pub enum AttributesDiscriminants {
4149
DMRevision = 0,
50+
VendorName = 1,
4251
VendorId = 2,
52+
ProductName = 3,
4353
ProductId = 4,
54+
NodeLabel = 5,
4455
HwVer = 7,
4556
SwVer = 9,
4657
SwVerString = 0xa,
@@ -57,6 +68,8 @@ pub struct BasicInfoConfig<'a> {
5768
pub serial_no: &'a str,
5869
/// Device name; up to 32 characters
5970
pub device_name: &'a str,
71+
pub vendor_name: &'a str,
72+
pub product_name: &'a str,
6073
}
6174

6275
pub const CLUSTER: Cluster<'static> = Cluster {
@@ -70,16 +83,31 @@ pub const CLUSTER: Cluster<'static> = Cluster {
7083
Access::RV,
7184
Quality::FIXED,
7285
),
86+
Attribute::new(
87+
AttributesDiscriminants::VendorName as u16,
88+
Access::RV,
89+
Quality::FIXED,
90+
),
7391
Attribute::new(
7492
AttributesDiscriminants::VendorId as u16,
7593
Access::RV,
7694
Quality::FIXED,
7795
),
96+
Attribute::new(
97+
AttributesDiscriminants::ProductName as u16,
98+
Access::RV,
99+
Quality::FIXED,
100+
),
78101
Attribute::new(
79102
AttributesDiscriminants::ProductId as u16,
80103
Access::RV,
81104
Quality::FIXED,
82105
),
106+
Attribute::new(
107+
AttributesDiscriminants::NodeLabel as u16,
108+
Access::RWVM,
109+
Quality::N,
110+
),
83111
Attribute::new(
84112
AttributesDiscriminants::HwVer as u16,
85113
Access::RV,
@@ -107,13 +135,16 @@ pub const CLUSTER: Cluster<'static> = Cluster {
107135
pub struct BasicInfoCluster<'a> {
108136
data_ver: Dataver,
109137
cfg: &'a BasicInfoConfig<'a>,
138+
node_label: RefCell<String<32>>, // Max node-label as per the spec
110139
}
111140

112141
impl<'a> BasicInfoCluster<'a> {
113142
pub fn new(cfg: &'a BasicInfoConfig<'a>, rand: Rand) -> Self {
143+
let node_label = RefCell::new(String::from(""));
114144
Self {
115145
data_ver: Dataver::new(rand),
116146
cfg,
147+
node_label,
117148
}
118149
}
119150

@@ -124,8 +155,13 @@ impl<'a> BasicInfoCluster<'a> {
124155
} else {
125156
match attr.attr_id.try_into()? {
126157
Attributes::DMRevision(codec) => codec.encode(writer, 1),
158+
Attributes::VendorName(codec) => codec.encode(writer, self.cfg.vendor_name),
127159
Attributes::VendorId(codec) => codec.encode(writer, self.cfg.vid),
160+
Attributes::ProductName(codec) => codec.encode(writer, self.cfg.product_name),
128161
Attributes::ProductId(codec) => codec.encode(writer, self.cfg.pid),
162+
Attributes::NodeLabel(codec) => {
163+
codec.encode(writer, self.node_label.borrow().as_str())
164+
}
129165
Attributes::HwVer(codec) => codec.encode(writer, self.cfg.hw_ver),
130166
Attributes::SwVer(codec) => codec.encode(writer, self.cfg.sw_ver),
131167
Attributes::SwVerString(codec) => codec.encode(writer, self.cfg.sw_ver_str),
@@ -136,12 +172,35 @@ impl<'a> BasicInfoCluster<'a> {
136172
Ok(())
137173
}
138174
}
175+
176+
pub fn write(&self, attr: &AttrDetails, data: AttrData) -> Result<(), Error> {
177+
let data = data.with_dataver(self.data_ver.get())?;
178+
179+
match attr.attr_id.try_into()? {
180+
Attributes::NodeLabel(codec) => {
181+
*self.node_label.borrow_mut() = String::from(
182+
codec
183+
.decode(data)
184+
.map_err(|_| Error::new(ErrorCode::InvalidAction))?,
185+
);
186+
}
187+
_ => return Err(Error::new(ErrorCode::InvalidAction)),
188+
}
189+
190+
self.data_ver.changed();
191+
192+
Ok(())
193+
}
139194
}
140195

141196
impl<'a> Handler for BasicInfoCluster<'a> {
142197
fn read(&self, attr: &AttrDetails, encoder: AttrDataEncoder) -> Result<(), Error> {
143198
BasicInfoCluster::read(self, attr, encoder)
144199
}
200+
201+
fn write(&self, attr: &AttrDetails, data: AttrData) -> Result<(), Error> {
202+
BasicInfoCluster::write(self, attr, data)
203+
}
145204
}
146205

147206
impl<'a> NonBlockingHandler for BasicInfoCluster<'a> {}

rs-matter/src/data_model/core.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ use crate::{
2828
// TODO: For now...
2929
static SUBS_ID: AtomicU32 = AtomicU32::new(1);
3030

31+
/// The Maximum number of expanded writer request per transaction
32+
///
33+
/// The write requests are first wildcard-expanded, and these many number of
34+
/// write requests per-transaction will be supported.
35+
pub const MAX_WRITE_ATTRS_IN_ONE_TRANS: usize = 7;
36+
3137
pub struct DataModel<T>(T);
3238

3339
impl<T> DataModel<T> {
@@ -93,8 +99,21 @@ impl<T> DataModel<T> {
9399
ref mut driver,
94100
} => {
95101
let accessor = driver.accessor()?;
96-
97-
for item in metadata.node().write(req, &accessor) {
102+
// The spec expects that a single write request like DeleteList + AddItem
103+
// should cause all ACLs of that fabric to be deleted and the new one to be added (Case 1).
104+
//
105+
// This is in conflict with the immediate-effect expectation of ACL: an ACL
106+
// write should instantaneously update the ACL so that immediate next WriteAttribute
107+
// *in the same WriteRequest* should see that effect (Case 2).
108+
//
109+
// As with the C++ SDK, here we do all the ACLs checks first, before any write begins.
110+
// Thus we support the Case1 by doing this. It does come at the cost of maintaining an
111+
// additional list of expanded write requests as we start processing those.
112+
let node = metadata.node();
113+
let write_attrs: heapless::Vec<_, MAX_WRITE_ATTRS_IN_ONE_TRANS> =
114+
node.write(req, &accessor).collect();
115+
116+
for item in write_attrs {
98117
AttrDataEncoder::handle_write(&item, &self.0, &mut driver.writer()?)
99118
.await?;
100119
}

rs-matter/src/tlv/traits.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,28 @@ pub enum Nullable<T> {
265265
}
266266

267267
impl<T> Nullable<T> {
268+
pub fn as_mut(&mut self) -> Nullable<&mut T> {
269+
match self {
270+
Nullable::Null => Nullable::Null,
271+
Nullable::NotNull(t) => Nullable::NotNull(t),
272+
}
273+
}
274+
275+
pub fn as_ref(&self) -> Nullable<&T> {
276+
match self {
277+
Nullable::Null => Nullable::Null,
278+
Nullable::NotNull(t) => Nullable::NotNull(t),
279+
}
280+
}
281+
268282
pub fn is_null(&self) -> bool {
269283
match self {
270284
Nullable::Null => true,
271285
Nullable::NotNull(_) => false,
272286
}
273287
}
274288

275-
pub fn unwrap_notnull(self) -> Option<T> {
289+
pub fn notnull(self) -> Option<T> {
276290
match self {
277291
Nullable::Null => None,
278292
Nullable::NotNull(t) => Some(t),

rs-matter/tests/common/im_engine.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ const BASIC_INFO: BasicInfoConfig<'static> = BasicInfoConfig {
7070
sw_ver_str: "13",
7171
serial_no: "aabbccdd",
7272
device_name: "Test Device",
73+
product_name: "TestProd",
74+
vendor_name: "TestVendor",
7375
};
7476

7577
struct DummyDevAtt;

rs-matter/tests/data_model/acl_and_dataver.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,18 @@ fn insufficient_perms_write() {
371371
);
372372
}
373373

374+
/// Disabling this test as it conflicts with another part of the spec.
375+
///
376+
/// The spec expects that a single write request like DeleteList + AddItem
377+
/// should cause all ACLs of that fabric to be deleted and the new one to be added
378+
///
379+
/// This is in conflict with the immediate-effect expectation of ACL: an ACL
380+
/// write should instantaneously update the ACL so that immediate next WriteAttribute
381+
/// *in the same WriteRequest* should see that effect.
382+
///
383+
/// This test validates the immediate effect expectation of ACL, but that is disabled
384+
/// since ecosystems routinely send DeleteList+AddItem, so we support that over this.
385+
#[ignore]
374386
#[test]
375387
/// Ensure that a write to the ACL attribute instantaneously grants permission
376388
/// Here we have 2 ACLs, the first (basic_acl) allows access only to the ACL cluster

0 commit comments

Comments
 (0)