Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New module for integrating Matter into RIOT OS #92

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ embedded-hal-async = { version = "1", optional = true }

critical-section = { version = "1.0", optional = true }

# for with_matter feature
rs-matter = { git = "https://github.com/maikerlab/rs-matter", branch = "feature/RIOT_OS", default-features = false, features = ["riot-os"], optional = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it foreseeable that the adjustments there can be upstreamed? Are there open PRs?

Copy link
Author

@maikerlab maikerlab Apr 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not yet. I will discuss this with rs-matter contributors, bc it's also not ideal if all the possible specific adaptations for different OS etc. are in this repo

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked again and I guess we could get rid of all the changes I made in rs-matter.
The only "big" task which needs to be done is implementing embassy-time-driver and embassy-time-queue-driver using ztimer.

embassy-futures = { version = "0.1.1", optional = true }
embassy-sync = { version = "0.5.0", optional = true }
embassy-executor-riot = { git = "https://gitlab.com/etonomy/riot-module-examples", optional = true }
embassy-executor = { version = "0.5", features = ["nightly"], optional = true }
log = { version = "0.4.21", optional = true }

[build-dependencies]
shlex = "0.1.1"
syn = { version = "1.0.107", features = [ "parsing" ] }
Expand Down Expand Up @@ -93,6 +101,8 @@ with_embedded_hal_async = [ "embedded-hal-async" ]
# guarantees.
with_msg_v2 = []

with_matter = ["rs-matter", "with_embedded_nal_async", "embassy-futures", "embassy-sync", "embassy-executor-riot", "embassy-executor", "log"]

# Use actual `!` rather than the stable Never workaround. As far as the
# workaround is understood, this causes no actual change in any types, just in
# the private names that are assigned to them.
Expand Down
13 changes: 12 additions & 1 deletion src/gnrc/netapi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use riot_sys::gnrc_nettype_t;

use crate::gnrc::pktbuf::{Pktsnip, Shared};
use riot_sys::inline::gnrc_netapi_set;
use riot_sys::{netopt_t_NETOPT_IPV6_GROUP, size_t};
use crate::thread::KernelPID;
use core::ffi::c_void;

/// Dispatch a packet to all listeners of the given nettype and demux context.
///
Expand All @@ -21,3 +24,11 @@ pub fn dispatch_send(
}
subscribers
}

/// Joins an IPV6 multicast group at the provided address
pub fn join_multicast_v6(pid: KernelPID, addr: &[u8; 16]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is sure a more suitable type for an IP address than [u8; 16]; too many to pick from, maybe (given there is gnrc::Address but now also core::net::Ipv6Addr, I'd pick the latter for new code).

The pid parameter could be named scope_id, that's the term used within Rust for the zone identifier.

Copy link
Author

@maikerlab maikerlab Apr 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

embedded-nal also has the Ipv6Addr type. Wouldn't it make more sense to use this one? Using core::net::Ipv6Addr, we would have two different types then in riot-wrappers for the same purpose.
If I use embedded-nal in the application code, I have to convert it to core::net::Ipv6Addr just for this function, which is a bit unhandy.
Suggestion: If with_embedded_nal feature is activated, pub use embedded_nal::Ipv6Addr, otherwise use the one from core::net?

unsafe {
let addr_ptr = addr.as_ptr() as *const c_void;
let _ = gnrc_netapi_set(pid.into(), netopt_t_NETOPT_IPV6_GROUP, 0, addr_ptr, addr.len() as size_t);
maikerlab marked this conversation as resolved.
Show resolved Hide resolved
}
}
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,11 @@ pub mod led;
pub mod auto_init;

mod async_helpers;

#[cfg(all(
riot_module_sock_udp,
riot_module_sock_aux_local,
feature = "with_embedded_nal_async",
feature = "with_matter"
))]
pub mod matter;
114 changes: 114 additions & 0 deletions src/matter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use crate::println;
maikerlab marked this conversation as resolved.
Show resolved Hide resolved
use crate::mutex::Mutex;
use crate::socket_embedded_nal_async_udp::UnconnectedUdpSocket;
use crate::ztimer;

use embedded_nal_async::{SocketAddr, UnconnectedUdp};
use embassy_futures::select::{Either, select};
use embedded_hal_async::delay::DelayNs as _;
use embassy_sync::{
signal::Signal,
blocking_mutex::raw::NoopRawMutex,
};
use rs_matter::error::{Error, ErrorCode};
use rs_matter::transport::network::{UdpReceive, UdpSend};
use log::{debug, warn, error, Level, LevelFilter, Log, Record, SetLoggerError};

pub struct MatterCompatUdpSocket {
local_addr: SocketAddr,
socket: Mutex<UnconnectedUdpSocket>,
release_socket_notification: Notification,
socket_released_notification: Notification,
}

struct RiotLogger;

pub type Notification = Signal<NoopRawMutex, ()>;

static LOGGER: RiotLogger = RiotLogger;

impl Log for RiotLogger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() >= Level::Info
}

fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("[{}] {}", record.level(), record.args());
}
}

fn flush(&self) {}
}

pub fn init_logger(level: LevelFilter) -> Result<(), SetLoggerError> {
log::set_logger(&LOGGER)
.map(|_| log::set_max_level(level))
}

impl MatterCompatUdpSocket {
pub fn new(local_addr: SocketAddr, socket: UnconnectedUdpSocket) -> Self {
Self {
local_addr,
socket: Mutex::new(socket),
release_socket_notification: Notification::new(),
socket_released_notification: Notification::new(),
}
}
}

impl UdpSend for &MatterCompatUdpSocket {
async fn send_to(&mut self, data: &[u8], addr: SocketAddr) -> Result<(), Error> {
if addr.is_ipv4() {
// IPv4 not supported!
return Ok(());
}
// Tell recv_from to release mutex
self.release_socket_notification.signal(());
ztimer::Delay.delay_ms(10).await;
let mut sock = self.socket.try_lock().expect("receiver should have ensured that this mutex is free");
sock.send(self.local_addr, addr, data)
.await
.map_err(|_| Error::new(ErrorCode::StdIoError))?;
// Release socket and notify recv_from -> sending is finished
drop(sock);
self.socket_released_notification.signal(());
Ok(())
}
}

impl UdpReceive for &MatterCompatUdpSocket {
async fn recv_from(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), Error> {
loop {
let mut sock = self.socket.try_lock().expect("sender should have ensured that this mutex is free");
match select(
self.release_socket_notification.wait(),
sock.receive_into(buffer),
).await {
Either::First(_) => {
// Release Mutex for send_to
drop(sock);
// ... and wait until available again
self.socket_released_notification.wait().await;
continue;
}
Either::Second(res) => {
match res {
Ok((bytes_recvd, local_addr, remote_addr)) => {
if remote_addr.is_ipv4() {
// IPv4 not supported!
return Ok((bytes_recvd, remote_addr));
}
}
Err(_) => { error!("Error during UDP receive!"); }
}
// return receive result
let (bytes_recvd, remote_addr) = res.map(|(bytes_recvd, _, remote_addr)|
(bytes_recvd, remote_addr)
).map_err(|_| Error::new(ErrorCode::StdIoError))?;
return Ok((bytes_recvd, remote_addr));
}
}
}
}
}
Loading