use core::{
net::{IpAddr, Ipv4Addr, SocketAddr},
task::{Context, Poll, Waker},
};
use embassy_futures::select::select;
use embassy_net::{IpEndpoint, udp::UdpSocket};
use hick_smoltcp::{RecvMeta, SendError, UdpIo};
fn recv_from(socket: &UdpSocket<'_>, buf: &mut [u8]) -> Option<RecvMeta> {
let mut cx = Context::from_waker(Waker::noop());
match socket.poll_recv_from(buf, &mut cx) {
Poll::Ready(Ok((len, meta))) => Some(RecvMeta {
src: meta.endpoint.into(),
local: meta.local_address.map(Into::into),
hop_limit: None,
len,
}),
Poll::Ready(Err(_)) => Some(RecvMeta {
src: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0),
local: None,
hop_limit: None,
len: 0,
}),
Poll::Pending => None,
}
}
fn send_from(socket: &UdpSocket<'_>, buf: &[u8], dst: SocketAddr) -> Result<(), SendError> {
use embassy_net::udp::SendError as EmbassySendError;
let mut cx = Context::from_waker(Waker::noop());
match socket.poll_send_to(buf, IpEndpoint::from(dst), &mut cx) {
Poll::Ready(Ok(())) => Ok(()),
Poll::Pending => Err(SendError::Busy),
Poll::Ready(Err(EmbassySendError::PacketTooLarge)) => Err(SendError::TooLarge),
Poll::Ready(Err(_)) => Err(SendError::Busy),
}
}
pub struct DualUdp<'sock, 'b> {
v4: Option<&'b UdpSocket<'sock>>,
v6: Option<&'b UdpSocket<'sock>>,
take_v6_first: bool,
}
impl<'sock, 'b> DualUdp<'sock, 'b> {
pub fn new(v4: Option<&'b UdpSocket<'sock>>, v6: Option<&'b UdpSocket<'sock>>) -> Self {
Self {
v4,
v6,
take_v6_first: false,
}
}
}
impl UdpIo for DualUdp<'_, '_> {
fn try_recv(&mut self, buf: &mut [u8]) -> Option<RecvMeta> {
let take_v6_first = self.take_v6_first;
self.take_v6_first = !take_v6_first;
let (first, second) = if take_v6_first {
(self.v6, self.v4)
} else {
(self.v4, self.v6)
};
if let Some(socket) = first
&& let Some(meta) = recv_from(socket, buf)
{
return Some(meta);
}
if let Some(socket) = second {
return recv_from(socket, buf);
}
None
}
fn try_send(&mut self, buf: &[u8], dst: SocketAddr) -> Result<(), SendError> {
let socket = if dst.is_ipv4() { self.v4 } else { self.v6 };
match socket {
Some(socket) => send_from(socket, buf, dst),
None => Err(SendError::Unsupported),
}
}
}
pub(crate) async fn wait_either_recv(v4: Option<&UdpSocket<'_>>, v6: Option<&UdpSocket<'_>>) {
match (v4, v6) {
(Some(a), Some(b)) => {
let _ = select(a.wait_recv_ready(), b.wait_recv_ready()).await;
}
(Some(a), None) => a.wait_recv_ready().await,
(None, Some(b)) => b.wait_recv_ready().await,
(None, None) => core::future::pending::<()>().await,
}
}