simple end-to-end demo of receiving UDP packets asynchronously

This commit is contained in:
bunnie 2021-10-18 05:55:42 +08:00
parent e6643e725d
commit a8c6e8047f
8 changed files with 153 additions and 15 deletions

1
Cargo.lock generated
View file

@ -2010,6 +2010,7 @@ dependencies = [
"llio",
"log",
"log-server",
"net",
"num-derive",
"num-traits",
"oqc-test",

View file

@ -70,6 +70,7 @@ impl From<IpAddress> for NetIpAddr {
}
}
pub(crate) const UDP_RESPONSE_MAX_LEN: usize = 1800;
/// The data field for a UDP response is limited to less than the theoretical
/// size of 64k. While UDP allows for a 64k packet, it seems no protoctols
/// in practice utilize such a length (about 512 bytes is the biggest), due
@ -84,7 +85,7 @@ pub(crate) struct NetUdpResponse {
pub endpoint_ip_addr: NetIpAddr,
pub len: u16,
pub endpoint_port: u16,
pub data: [u8; 1800],
pub data: [u8; UDP_RESPONSE_MAX_LEN],
}
#[derive(Debug, Archive, Serialize, Deserialize, Copy, Clone)]

View file

@ -38,8 +38,8 @@ pub struct UdpSocket{
// next steps: build this stub, and figure out how to clean up the error handling code.
impl UdpSocket {
pub fn bind(maybe_socket: Result<&SocketAddr>, max_payload: Option<u16>) -> Result<UdpSocket> {
let socket = maybe_socket?;
pub fn bind(socket: SocketAddr, max_payload: Option<u16>) -> Result<UdpSocket> {
//let socket = maybe_socket.unwrap();
let xns = xous_names::XousNames::new().unwrap();
let net = NetConn::new(&xns).unwrap();
let cb_sid = xous::create_server().unwrap();
@ -76,8 +76,8 @@ impl UdpSocket {
endpoint,
data: Vec::new(),
};
for (&src, dst) in incoming.data.iter().zip(rx.data.iter_mut()) {
*dst = src;
for &d in incoming.data[..incoming.len as usize].iter() {
rx.data.push(d);
}
rx_buf.lock().unwrap().push(rx);
notify.lock().unwrap().notify(); // this will only notify if a destination has been set
@ -96,7 +96,7 @@ impl UdpSocket {
});
let request = NetUdpBind {
ip_addr: NetIpAddr::from(*socket),
ip_addr: NetIpAddr::from(socket),
port: socket.port(),
cb_sid: cb_sid.to_array(),
max_payload,
@ -110,7 +110,7 @@ impl UdpSocket {
Ok(UdpSocket {
net,
cb_sid,
socket_addr: *socket,
socket_addr: socket,
rx_buf,
handle: Some(handle),
notify,

View file

@ -8,6 +8,7 @@ use num_traits::*;
pub mod backend;
pub use backend::*;
pub use smoltcp::time::Duration;
/// NetConn is a crate-level structure that just counts the number of connections from this process to
/// the Net server. It's not mean to be created by user-facing code, so the visibility is (crate).

View file

@ -97,6 +97,11 @@ enum WaitOp {
Quit,
}
pub struct UdpState {
handle: SocketHandle,
cid: xous::CID,
}
#[xous::xous_main]
fn xmain() -> ! {
log_server::init_wait().unwrap();
@ -170,7 +175,7 @@ fn xmain() -> ! {
let mut net_config: Option<Ipv4Conf> = None;
// ping-specific storage
let ping_remote_addr = IpAddress::from_str("10.0.245.243").expect("invalid address format");
let ping_remote_addr = IpAddress::from_str("10.0.245.1").expect("invalid address format");
let icmp_rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 256]);
let icmp_tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 256]);
@ -178,7 +183,7 @@ fn xmain() -> ! {
let mut sockets = SocketSet::new(vec![]);
let icmp_handle = sockets.add(icmp_socket);
let mut udp_handles = HashMap::<u16, SocketHandle>::new();
let mut udp_handles = HashMap::<u16, UdpState>::new();
let mut send_at = Instant::from_millis(0);
let mut seq_no = 0;
@ -236,7 +241,11 @@ fn xmain() -> ! {
let mut udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer);
match udp_socket.bind(udpspec.port) {
Ok(_) => {
udp_handles.insert(udpspec.port, sockets.add(udp_socket));
let udpstate = UdpState {
handle: sockets.add(udp_socket),
cid: xous::connect(xous::SID::from_array(udpspec.cb_sid)).unwrap(),
};
udp_handles.insert(udpspec.port, udpstate);
buf.replace(NetMemResponse::Ok).unwrap();
}
Err(e) => {
@ -250,8 +259,8 @@ fn xmain() -> ! {
let mut buf = unsafe{Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap())};
let udpspec = buf.to_original::<NetUdpBind, _>().unwrap();
match udp_handles.remove(&udpspec.port) {
Some(handle) => {
sockets.get::<UdpSocket>(handle).close();
Some(udpstate) => {
sockets.get::<UdpSocket>(udpstate.handle).close();
buf.replace(NetMemResponse::Ok).unwrap()
}
_ => {
@ -358,7 +367,8 @@ fn xmain() -> ! {
}
{
for (port, &handle) in udp_handles.iter() {
for (port, udpstate) in udp_handles.iter() {
let handle = udpstate.handle;
let mut socket = sockets.get::<UdpSocket>(handle);
match socket.recv() {
Ok((data, endpoint)) => {
@ -369,8 +379,17 @@ fn xmain() -> ! {
endpoint
);
// return the data/endpoint tuple to the caller
// TODO
let mut response = NetUdpResponse {
endpoint_ip_addr: NetIpAddr::from(endpoint.addr),
len: data.len() as u16,
endpoint_port: endpoint.port,
data: [0; UDP_RESPONSE_MAX_LEN],
};
for (&src, dst) in data.iter().zip(response.data.iter_mut()) {
*dst = src;
}
let buf = Buffer::into_buf(response).expect("couldn't convert UDP response to memory message");
buf.send(udpstate.cid, NetUdpCallback::RxData.to_u32().unwrap()).expect("couldn't send UDP response");
}
Err(_) => {
// do nothing

View file

@ -32,6 +32,7 @@ engine-25519 = {path="../engine-25519"}
spinor = {path="../spinor"}
root-keys = {path="../root-keys"}
jtag = {path="../jtag"}
net = {path="../net"}
trng = {path = "../trng"}

View file

@ -99,6 +99,7 @@ mod memtest; use memtest::*;
mod keys; use keys::*;
mod wlan; use wlan::*;
mod jtag_cmd; use jtag_cmd::*;
mod net_cmd; use net_cmd::*;
//mod fcc; use fcc::*;
//mod pds; // dependency of the FCC file
@ -124,6 +125,7 @@ pub struct CmdEnv {
memtest_cmd: Memtest,
keys_cmd: Keys,
jtag_cmd: JtagCmd,
net_cmd: NetCmd,
//fcc_cmd: Fcc,
}
@ -176,6 +178,7 @@ impl CmdEnv {
memtest_cmd: memtest,
keys_cmd: Keys::new(&xns),
jtag_cmd: JtagCmd::new(&xns),
net_cmd: NetCmd::new(&xns),
//fcc_cmd: fcc,
}
@ -214,6 +217,7 @@ impl CmdEnv {
&mut self.keys_cmd,
&mut wlan_cmd,
&mut self.jtag_cmd,
&mut self.net_cmd,
//&mut self.fcc_cmd,
];

View file

@ -0,0 +1,111 @@
use crate::{ShellCmdApi, CommonEnv};
use xous_ipc::String;
use net::Duration;
use xous::MessageEnvelope;
use num_traits::*;
use std::net::ToSocketAddrs;
#[derive(Debug)]
pub struct NetCmd {
udp: Option<net::UdpSocket>,
callback_id: Option<u32>,
callback_conn: u32,
}
impl NetCmd {
pub fn new(xns: &xous_names::XousNames) -> Self {
NetCmd {
udp: None,
callback_id: None,
callback_conn: xns.request_connection_blocking(crate::SERVER_NAME_SHELLCHAT).unwrap(),
}
}
}
#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive)]
pub(crate) enum NetCmdDispatch {
UdpTest1,
}
pub const UDP_TEST_SIZE: usize = 64;
impl<'a> ShellCmdApi<'a> for NetCmd {
cmd_api!(net); // inserts boilerplate for command API
fn process(&mut self, args: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
if self.callback_id.is_none() {
let cb_id = env.register_handler(String::<256>::from_str(self.verb()));
log::info!("hooking net callback with ID {}", cb_id);
self.callback_id = Some(cb_id);
}
use core::fmt::Write;
let mut ret = String::<1024>::new();
let helpstring = "net [udp]";
let mut tokens = args.as_str().unwrap().split(' ');
if let Some(sub_cmd) = tokens.next() {
match sub_cmd {
"udp" => {
if self.udp.is_none() {
let mut udp = net::UdpSocket::bind(
"127.0.0.1:6969".to_socket_addrs().unwrap().into_iter().next().unwrap(),
Some(UDP_TEST_SIZE as u16)
).unwrap();
udp.set_read_timeout(Some(Duration::from_millis(1000))).unwrap();
udp.set_scalar_notification(
self.callback_conn,
self.callback_id.unwrap() as usize, // this is guaranteed in the prelude
[Some(NetCmdDispatch::UdpTest1.to_usize().unwrap()), None, None, None]
);
self.udp = Some(udp);
write!(ret, "Created UDP socket listener on port 6969").unwrap();
} else {
write!(ret, "Socket listener already installed on 6969").unwrap();
}
}
_ => {
write!(ret, "{}", helpstring).unwrap();
}
}
} else {
write!(ret, "{}", helpstring).unwrap();
}
Ok(Some(ret))
}
fn callback(&mut self, msg: &MessageEnvelope, _env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
use core::fmt::Write;
log::info!("net callback");
let mut ret = String::<1024>::new();
xous::msg_scalar_unpack!(msg, dispatch, _, _, _, {
match FromPrimitive::from_usize(dispatch) {
Some(NetCmdDispatch::UdpTest1) => {
if let Some(udp_socket) = &self.udp {
let mut pkt: [u8; UDP_TEST_SIZE] = [0; UDP_TEST_SIZE];
match udp_socket.recv_from(&mut pkt) {
Ok((len, addr)) => {
write!(ret, "UDP received {} bytes: {:?}: {}", len, addr, std::str::from_utf8(&pkt[..len]).unwrap()).unwrap();
log::info!("UDP received {} bytes: {:?}: {:?}", len, addr, &pkt[..len]);
},
Err(e) => {
log::error!("Net UDP error: {:?}", e);
write!(ret, "UDP receive error: {:?}", e).unwrap();
}
}
} else {
log::error!("Got NetCmd callback from uninitialized socket");
write!(ret, "Got NetCmd callback from uninitialized socket").unwrap();
}
}
None => {
log::error!("NetCmd callback with unrecognized dispatch ID");
write!(ret, "NetCmd callback with unrecognized dispatch ID").unwrap();
}
}
});
Ok(Some(ret))
}
}