Skip to content

Commit

Permalink
Support for async-io-mini
Browse files Browse the repository at this point in the history
  • Loading branch information
ivmarkov committed May 16, 2024
1 parent 913d342 commit fef010f
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 38 deletions.
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ build = "build.rs"
#documentation = "https://docs.esp-rs.org/esp-idf-matter/"
rust-version = "1.77"

#[patch.'https://github.com/ivmarkov/async-io-mini']
#async-io-mini = { path = "../async-io-mini" }

[patch.crates-io]
embedded-svc = { git = "https://github.com/esp-rs/embedded-svc" }
esp-idf-svc = { git = "https://github.com/esp-rs/esp-idf-svc", branch = "gatt" }
Expand All @@ -32,8 +35,8 @@ opt-level = "z"

[features]
default = ["std"]
std = ["async-io", "rs-matter/std", "rs-matter/async-io", "esp-idf-svc/std"]
examples = ["std", "esp-idf-svc/binstart", "esp-idf-svc/critical-section"] # Enable only when building the examples
std = ["rs-matter/std", "esp-idf-svc/std"]
examples = ["std", "async-io", "esp-idf-svc/binstart", "esp-idf-svc/critical-section"] # Enable only when building the examples

[dependencies]
log = { version = "0.4", default-features = false }
Expand All @@ -49,6 +52,7 @@ embedded-svc = { version = "0.27", default-features = false }
rs-matter = { version = "0.1", default-features = false, features = ["rustcrypto"] }
rs-matter-macros = "0.1"
async-io = { version = "=2.0.0", optional = true, default-features = false } # Workaround for https://github.com/smol-rs/async-lock/issues/84
async-io-mini = { git = "https://github.com/ivmarkov/async-io-mini", optional = true }

[build-dependencies]
embuild = "0.31.3"
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extern crate alloc;
pub use error::*;
#[cfg(all(
feature = "std",
any(feature = "async-io", feature = "async-io-mini"),
esp_idf_comp_nvs_flash_enabled,
esp_idf_comp_esp_netif_enabled,
esp_idf_comp_esp_event_enabled
Expand All @@ -32,9 +33,11 @@ pub mod netif;
pub mod nvs;
#[cfg(all(
feature = "std",
any(feature = "async-io", feature = "async-io-mini"),
esp_idf_comp_nvs_flash_enabled,
esp_idf_comp_esp_netif_enabled,
esp_idf_comp_esp_event_enabled
))]
mod stack;
mod udp;
pub mod wifi;
78 changes: 52 additions & 26 deletions src/multicast.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,54 @@
#![cfg(feature = "std")]
#![cfg(all(feature = "std", any(feature = "async-io", feature = "async-io-mini")))]

use core::net::{Ipv4Addr, Ipv6Addr};

use std::net::UdpSocket;

use async_io::Async;
#[cfg(feature = "async-io-mini")]
use async_io_mini as async_io;

use log::info;

use rs_matter::error::{Error, ErrorCode};

/// Join an IPV6 multicast group on a specific interface
pub fn join_multicast_v6(
socket: &Async<UdpSocket>,
socket: &async_io::Async<UdpSocket>,
multiaddr: Ipv6Addr,
interface: u32,
) -> Result<(), Error> {
#[cfg(not(target_os = "espidf"))]
socket.as_ref().join_multicast_v6(&multiaddr, interface)?;

// join_multicast_v6() is broken for ESP-IDF due to mismatch w.r.t. sizes
// (u8 expected but u32 passed to setsockopt() and sometimes the other way around)
#[cfg(target_os = "espidf")]
{
let mreq = esp_idf_svc::sys::ipv6_mreq {
ipv6mr_multiaddr: esp_idf_svc::sys::in6_addr {
un: esp_idf_svc::sys::in6_addr__bindgen_ty_1 {
u8_addr: multiaddr.octets(),
},
},
ipv6mr_interface: interface,
};

esp_setsockopt(
socket,
esp_idf_svc::sys::IPPROTO_IPV6,
esp_idf_svc::sys::IPV6_ADD_MEMBERSHIP,
mreq,
)?;
}

info!("Joined IPV6 multicast {}/{}", multiaddr, interface);

Ok(())
}

/// Join an IPV4 multicast group on a specific interface
pub fn join_multicast_v4(
socket: &Async<UdpSocket>,
socket: &async_io::Async<UdpSocket>,
multiaddr: Ipv4Addr,
interface: Ipv4Addr,
) -> Result<(), Error> {
Expand All @@ -39,28 +62,6 @@ pub fn join_multicast_v4(
// due to mismatch w.r.t. sizes (u8 expected but u32 passed to setsockopt() and sometimes the other way around)
#[cfg(target_os = "espidf")]
{
fn esp_setsockopt<T>(
socket: &Async<UdpSocket>,
proto: u32,
option: u32,
value: T,
) -> Result<(), Error> {
use std::os::fd::AsRawFd;

esp_idf_svc::sys::esp!(unsafe {
esp_idf_svc::sys::lwip_setsockopt(
socket.as_raw_fd(),
proto as _,
option as _,
&value as *const _ as *const _,
core::mem::size_of::<T>() as _,
)
})
.map_err(|_| ErrorCode::StdIoError)?;

Ok(())
}

let mreq = esp_idf_svc::sys::ip_mreq {
imr_multiaddr: esp_idf_svc::sys::in_addr {
s_addr: u32::from_ne_bytes(multiaddr.octets()),
Expand All @@ -82,3 +83,28 @@ pub fn join_multicast_v4(

Ok(())
}

// Most *_multicast_v4 and *_multicast_v6 methods are broken in Rust STD for the ESP-IDF
// due to mismatch w.r.t. sizes (u8 expected but u32 passed to setsockopt() and sometimes the other way around)
#[cfg(target_os = "espidf")]
fn esp_setsockopt<T>(
socket: &async_io::Async<UdpSocket>,
proto: u32,
option: u32,
value: T,
) -> Result<(), Error> {
use std::os::fd::AsRawFd;

esp_idf_svc::sys::esp!(unsafe {
esp_idf_svc::sys::lwip_setsockopt(
socket.as_raw_fd(),
proto as _,
option as _,
&value as *const _ as *const _,
core::mem::size_of::<T>() as _,
)
})
.map_err(|_| ErrorCode::StdIoError)?;

Ok(())
}
31 changes: 21 additions & 10 deletions src/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use core::cell::RefCell;
use core::fmt::Write as _;
use core::net::{Ipv6Addr, SocketAddr, SocketAddrV6};
use core::pin::pin;
use core::time::Duration;

#[cfg(feature = "async-io-mini")]
use async_io_mini as async_io;

use embassy_futures::select::select3;
use embassy_sync::blocking_mutex::raw::{NoopRawMutex, RawMutex};
Expand Down Expand Up @@ -51,6 +53,7 @@ use crate::error::Error;
use crate::multicast::{join_multicast_v4, join_multicast_v6};
use crate::netif::{get_info, NetifAccess, NetifInfo};
use crate::nvs;
use crate::udp;

pub use eth::*;
#[cfg(all(
Expand Down Expand Up @@ -238,8 +241,8 @@ where
))?;

let mut main = pin!(self.run_with_transport(
&socket,
&socket,
udp::Udp(&socket),
udp::Udp(&socket),
nvs.clone(),
dev_comm.clone(),
&handler
Expand Down Expand Up @@ -375,8 +378,8 @@ where

self.matter()
.run_builtin_mdns(
&socket,
&socket,
udp::Udp(&socket),
udp::Udp(&socket),
&Host {
id: 0,
hostname: &hostname,
Expand Down Expand Up @@ -537,15 +540,23 @@ pub fn init_async_io() -> Result<(), Error> {

block_on(init_async_io_async());

info!("Async IO initialized");

Ok(())
}

#[inline(never)]
#[cold]
async fn init_async_io_async() {
// Force the `async-io` lazy initialization to trigger earlier rather than later,
// as it consumes a lot of temp stack memory
async_io::Timer::after(Duration::from_millis(100)).await;
#[cfg(not(feature = "async-io-mini"))]
{
// Force the `async-io` lazy initialization to trigger earlier rather than later,
// as it consumes a lot of temp stack memory
async_io::Timer::after(core::time::Duration::from_millis(100)).await;
info!("Async IO initialized; using `async-io`");
}

#[cfg(feature = "async-io-mini")]
{
// Nothing to initialize for `async-io-mini`
info!("Async IO initialized; using `async-io-mini`");
}
}
40 changes: 40 additions & 0 deletions src/udp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![cfg(all(feature = "std", any(feature = "async-io", feature = "async-io-mini")))]

//! UDP transport implementation for async-io and async-io-mini
use std::net::UdpSocket;

#[cfg(feature = "async-io-mini")]
use async_io_mini as async_io;

use rs_matter::error::{Error, ErrorCode};
use rs_matter::transport::network::{Address, NetworkReceive, NetworkSend};

pub struct Udp<'a>(pub &'a async_io::Async<UdpSocket>);

impl NetworkSend for Udp<'_> {
async fn send_to(&mut self, data: &[u8], addr: Address) -> Result<(), Error> {
async_io::Async::<UdpSocket>::send_to(
self.0,
data,
addr.udp().ok_or(ErrorCode::NoNetworkInterface)?,
)
.await?;

Ok(())
}
}

impl NetworkReceive for Udp<'_> {
async fn wait_available(&mut self) -> Result<(), Error> {
async_io::Async::<UdpSocket>::readable(self.0).await?;

Ok(())
}

async fn recv_from(&mut self, buffer: &mut [u8]) -> Result<(usize, Address), Error> {
let (len, addr) = async_io::Async::<UdpSocket>::recv_from(self.0, buffer).await?;

Ok((len, Address::Udp(addr)))
}
}

0 comments on commit fef010f

Please sign in to comment.