riot_wrappers/socket_embedded_nal.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
//! An implementation of the [embedded_nal] (Network Abstradtion Layer) UDP traits based on RIOT
//! sockets
use core::convert::TryInto;
use core::mem::MaybeUninit;
use crate::error::{NegativeErrorExt, NumericError};
use crate::socket::UdpEp;
use embedded_nal::SocketAddr;
/// The operating system's network stack, used to get an implementation of
/// ``embedded_nal::UdpClientStack``.
///
/// Using this is not trivial, as RIOT needs its sockets pinned to memory for their lifetime.
/// Without a heap allocator, this is achieved by allocating all the required UDP sockets in a
/// stack object. To ensure that it is not moved, sockets on it can only be created in (and live
/// only for the duration of) a the `run` callback, which gives the actual implementation of
/// UdpClientStack.
///
/// The number of UDP sockets allocated is configurable using the UDPCOUNT const generic.
pub struct Stack<const UDPCOUNT: usize> {
udp_sockets: heapless::Vec<riot_sys::sock_udp_t, UDPCOUNT>,
}
impl<const UDPCOUNT: usize> core::fmt::Debug for Stack<UDPCOUNT> {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
fmt,
"Stack {{ {} of {} sockets used }}",
self.udp_sockets.len(),
UDPCOUNT
)
}
}
// FIXME: This should really just use Pin like socket_embedded_nal_tcp does; unfortunately, this
// doesn't align well with the .run() API, maybe that's best just to break.
#[derive(Debug)]
pub struct StackAccessor<'a, const UDPCOUNT: usize> {
stack: &'a mut Stack<UDPCOUNT>,
}
impl<const UDPCOUNT: usize> Stack<UDPCOUNT> {
pub fn new() -> Self {
Self {
udp_sockets: Default::default(),
}
}
pub fn run(&mut self, runner: impl for<'a> FnOnce(StackAccessor<'a, UDPCOUNT>)) {
let accessor = StackAccessor { stack: self };
runner(accessor);
// In particular, this would require tracking of whether the sockets are closed
unimplemented!("Allocator does not have clean-up implemented");
}
}
pub struct UdpSocket<'a> {
// This indirection -- not having the sock_udp_t inside UdpSocket -- is necessary because the
// way they are created (embedded-nal .socket()) produces owned values and needs owned values
// later -- while what we'd prefer would be producing owned values and needing pinned ones.
//
// See also https://github.com/rust-embedded-community/embedded-nal/issues/61
socket: Option<&'a mut riot_sys::sock_udp_t>,
}
impl<'a> UdpSocket<'a> {
/// Version of socket() that gives errors compatible with Self::Error
fn access(&mut self) -> Result<*mut riot_sys::sock_udp_t, NumericError> {
self.socket()
.ok_or(NumericError::from_constant(riot_sys::ENOTCONN as _))
}
/// Accessor to the inner socket pointer
///
/// This can be used by users of the wrapper to alter properties of the socket, as long as that
/// does not interfere with the wrapper's operation. It is not specified which parts that are;
/// users of this beware that what the wrapper handles can be changed in subsequent versions.
///
/// The method is safe on its own because all operations on the `*mut` are unsafe anyway
/// (including the functions exported in riot-sys). It is not returning a &mut on the inner
/// socket because that would allow swapping it out (which RIOT doesn't like at all).
pub fn socket(&mut self) -> Option<*mut riot_sys::sock_udp_t> {
self.socket.as_mut().map(|s| &mut **s as _)
}
/// If there is an actual socket in here, close it
fn close(&mut self) {
if let Some(socket) = self.socket.take() {
unsafe { riot_sys::sock_udp_close(&mut *socket) };
}
}
}
impl<'a, const UDPCOUNT: usize> StackAccessor<'a, UDPCOUNT> {
/// Take one of the stack accessor's allocated slots
fn allocate(&mut self) -> Result<*mut riot_sys::sock_udp, NumericError> {
// This happens rarely enough that any MaybeUninit trickery is unwarranted
self.stack
.udp_sockets
.push(Default::default())
.map_err(|_| NumericError::from_constant(riot_sys::ENOMEM as _))?;
let last = self.stack.udp_sockets.len() - 1;
Ok(&mut self.stack.udp_sockets[last] as *mut _)
}
/// Wrapper around sock_udp_create
fn create(
&mut self,
handle: &mut UdpSocket<'a>,
local: &UdpEp,
remote: Option<&UdpEp>,
) -> Result<(), NumericError> {
handle.close();
let socket = self.allocate()?;
(unsafe {
riot_sys::sock_udp_create(
socket,
local.as_ref(),
remote
.map(|r| {
let r: &riot_sys::sock_udp_ep_t = r.as_ref();
r as *const _
})
.unwrap_or(core::ptr::null()),
0,
)
})
.negative_to_error()?;
// unsafe: Having an 'a mutable reference for it is OK because the StackAccessor guarantees
// that the stack is available for 'a and won't move.
let socket: &'a mut _ = unsafe { &mut *socket };
handle.socket = Some(socket);
Ok(())
}
}
impl<'a, const UDPCOUNT: usize> embedded_nal::UdpClientStack for StackAccessor<'a, UDPCOUNT> {
type UdpSocket = UdpSocket<'a>;
type Error = NumericError;
fn socket(&mut self) -> Result<UdpSocket<'a>, Self::Error> {
Ok(UdpSocket { socket: None })
}
fn connect(
&mut self,
handle: &mut Self::UdpSocket,
remote: SocketAddr,
) -> Result<(), Self::Error> {
// unsafe: Side effect free C macros
let local = unsafe {
match remote {
SocketAddr::V4(_) => riot_sys::macro_SOCK_IPV4_EP_ANY(),
SocketAddr::V6(_) => riot_sys::macro_SOCK_IPV6_EP_ANY(),
}
.into()
};
let remote = remote.into();
self.create(handle, &local, Some(&remote))
}
fn send(
&mut self,
socket: &mut Self::UdpSocket,
buffer: &[u8],
) -> Result<(), nb::Error<Self::Error>> {
let socket = socket.access()?;
(unsafe {
riot_sys::sock_udp_send(
crate::inline_cast_mut(&mut *socket as *mut _),
buffer.as_ptr() as _,
buffer.len().try_into().unwrap(),
0 as *const _,
)
})
.negative_to_error()
.map(|_| ())
// Sending never blocks in RIOT sockets
.map_err(|e| nb::Error::Other(e))
}
fn receive(
&mut self,
socket: &mut Self::UdpSocket,
buffer: &mut [u8],
) -> Result<(usize, SocketAddr), nb::Error<Self::Error>> {
let socket = socket.access()?;
let mut remote = MaybeUninit::uninit();
let read = (unsafe {
riot_sys::sock_udp_recv(
crate::inline_cast_mut(&mut *socket as *mut _),
buffer.as_mut_ptr() as _,
buffer.len().try_into().unwrap(),
0,
crate::inline_cast_mut(remote.as_mut_ptr() as *mut _),
)
})
.negative_to_error()
.map(|e| e as usize)
.map_err(|e| e.again_is_wouldblock());
// unsafe: Set by C function
let remote = UdpEp(unsafe { remote.assume_init() });
Ok((read?, remote.into()))
}
fn close(&mut self, mut socket: Self::UdpSocket) -> Result<(), Self::Error> {
socket.close();
Ok(())
}
}
impl<'a, const UDPCOUNT: usize> embedded_nal::UdpFullStack for StackAccessor<'a, UDPCOUNT> {
fn bind(&mut self, handle: &mut UdpSocket<'a>, port: u16) -> Result<(), Self::Error> {
let local = UdpEp::ipv6_any().with_port(port);
self.create(handle, &local, None)
}
fn send_to(
&mut self,
handle: &mut UdpSocket<'a>,
remote: SocketAddr,
buffer: &[u8],
) -> Result<(), nb::Error<Self::Error>> {
let socket = handle.access()?;
let remote: UdpEp = remote.into();
(unsafe {
riot_sys::sock_udp_send(
crate::inline_cast_mut(&mut *socket as *mut _),
buffer.as_ptr() as _,
buffer.len().try_into().unwrap(),
remote.as_ref(),
)
})
.negative_to_error()
.map(|_| ())
// Sending never blocks in RIOT sockets
.map_err(|e| nb::Error::Other(e))
}
}