From c9b69bf71eca0f25405ae7e01e33b5a41b3229eb Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 18 Jul 2026 16:14:50 -0500 Subject: [PATCH 1/6] wip: add ptp impl --- embassy-net/Cargo.toml | 2 ++ embassy-net/src/lib.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index d5544e3de2..acf34bd5ab 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -109,6 +109,8 @@ std = ["smoltcp/std"] alloc = ["smoltcp/alloc"] ## Enable smoltcp packetmeta-id feature packetmeta-id = [ "smoltcp/packetmeta-id", "embassy-net-driver/packetmeta-id" ] +## Enable ptp support +ptp = [] [dependencies] diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index a89662c1c4..c9adbfc5ea 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -101,6 +101,10 @@ pub struct StackResources { // undersized buffer never corrupts the IP configuration, it only drops the extra options. #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: MaybeUninit<[u8; DHCP_RX_BUFFER_SIZE]>, + #[cfg(feature = "ptp")] + ids: MaybeUninit<[Option; SOCK]>, + #[cfg(feature = "ptp")] + times: MaybeUninit<[Option; SOCK]>, } #[cfg(feature = "dhcpv4-hostname")] @@ -124,6 +128,10 @@ impl StackResources { }, #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: MaybeUninit::uninit(), + #[cfg(feature = "ptp")] + ids: MaybeUninit::uninit(), + #[cfg(feature = "ptp")] + times: MaybeUninit::uninit(), } } } @@ -332,6 +340,10 @@ pub(crate) struct Inner { hostname: *mut HostnameResources, #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: *mut [u8], + #[cfg(feature = "ptp")] + ids: &'static mut [Option], + #[cfg(feature = "ptp")] + times: &'static mut [Option], } fn _assert_covariant<'a, 'b: 'a>(x: Stack<'b>) -> Stack<'a> { @@ -407,6 +419,10 @@ pub fn new<'d, D: Driver, const SOCK: usize>( hostname: &mut resources.hostname, #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: resources.dhcp_rx_buffer.write([0; DHCP_RX_BUFFER_SIZE]) as *mut [u8], + #[cfg(feature = "ptp")] + ids: unsafe { &mut transmute_slice(resources.ids.write([None; SOCK]))[..] }, + #[cfg(feature = "ptp")] + times: unsafe { &mut transmute_slice(resources.times.write([None; SOCK]))[..] }, }; #[cfg(feature = "proto-ipv4")] @@ -942,6 +958,21 @@ impl Inner { } } + #[cfg(feature = "ptp")] + while let Some((id, timestamp)) = driver.poll_timestamp(cx) { + if let Some(index) = self + .ids + .iter() + .enumerate() + .find(|x| x.1.is_some_and(|x| x == id)) + .map(|x| x.0) + { + self.times[index] = Some(timestamp); + + // TODO: wake socket waker + } + } + // Update link up let old_link_up = self.link_up; self.link_up = driver.link_state(cx) == LinkState::Up; From aaad2738ab009514d0fc203d1fa9e8cceb76a257 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 18 Jul 2026 17:56:26 -0500 Subject: [PATCH 2/6] wip --- embassy-net/src/lib.rs | 78 ++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index c9adbfc5ea..7a9bade92d 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -102,9 +102,7 @@ pub struct StackResources { #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: MaybeUninit<[u8; DHCP_RX_BUFFER_SIZE]>, #[cfg(feature = "ptp")] - ids: MaybeUninit<[Option; SOCK]>, - #[cfg(feature = "ptp")] - times: MaybeUninit<[Option; SOCK]>, + times: MaybeUninit>, } #[cfg(feature = "dhcpv4-hostname")] @@ -129,8 +127,6 @@ impl StackResources { #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: MaybeUninit::uninit(), #[cfg(feature = "ptp")] - ids: MaybeUninit::uninit(), - #[cfg(feature = "ptp")] times: MaybeUninit::uninit(), } } @@ -341,9 +337,51 @@ pub(crate) struct Inner { #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: *mut [u8], #[cfg(feature = "ptp")] - ids: &'static mut [Option], - #[cfg(feature = "ptp")] - times: &'static mut [Option], + times: &'static mut dyn LinearMap, +} + +#[cfg(feature = "ptp")] +struct TimeEntry { + id: u8, + time: Option, + waker: WakerRegistration, +} + +#[cfg(feature = "ptp")] +trait LinearMap { + #[allow(dead_code)] + fn insert(&mut self, key: K, val: V) -> Result, ()>; + #[allow(dead_code)] + fn get_mut(&mut self, key: &K) -> Option<&mut V>; + #[allow(dead_code)] + fn remove(&mut self, key: &K) -> Option; + #[allow(dead_code)] + fn iter(&self) -> heapless::linear_map::Iter<'_, K, V>; + #[allow(dead_code)] + fn iter_mut(&mut self) -> heapless::linear_map::IterMut<'_, K, V>; +} + +#[cfg(feature = "ptp")] +impl LinearMap for heapless::LinearMap { + fn insert(&mut self, key: K, val: V) -> Result, ()> { + self.insert(key, val).map_err(|_| ()) + } + + fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.get_mut(key) + } + + fn remove(&mut self, key: &K) -> Option { + self.remove(key) + } + + fn iter(&self) -> heapless::linear_map::Iter<'_, K, V> { + self.iter() + } + + fn iter_mut(&mut self) -> heapless::linear_map::IterMut<'_, K, V> { + self.iter_mut() + } } fn _assert_covariant<'a, 'b: 'a>(x: Stack<'b>) -> Stack<'a> { @@ -381,6 +419,11 @@ pub fn new<'d, D: Driver, const SOCK: usize>( core::mem::transmute(x) } + #[cfg(feature = "ptp")] + unsafe fn transmute_static(x: &mut T) -> &'static mut T { + core::mem::transmute(x) + } + let sockets = resources.sockets.write([SocketStorage::EMPTY; SOCK]); #[allow(unused_mut)] let mut sockets: SocketSet<'static> = SocketSet::new(unsafe { transmute_slice(sockets) }); @@ -420,9 +463,7 @@ pub fn new<'d, D: Driver, const SOCK: usize>( #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: resources.dhcp_rx_buffer.write([0; DHCP_RX_BUFFER_SIZE]) as *mut [u8], #[cfg(feature = "ptp")] - ids: unsafe { &mut transmute_slice(resources.ids.write([None; SOCK]))[..] }, - #[cfg(feature = "ptp")] - times: unsafe { &mut transmute_slice(resources.times.write([None; SOCK]))[..] }, + times: unsafe { transmute_static(resources.times.write(heapless::LinearMap::new())) }, }; #[cfg(feature = "proto-ipv4")] @@ -960,16 +1001,11 @@ impl Inner { #[cfg(feature = "ptp")] while let Some((id, timestamp)) = driver.poll_timestamp(cx) { - if let Some(index) = self - .ids - .iter() - .enumerate() - .find(|x| x.1.is_some_and(|x| x == id)) - .map(|x| x.0) - { - self.times[index] = Some(timestamp); - - // TODO: wake socket waker + for (_handle, entry) in self.times.iter_mut() { + if id == entry.id && entry.time.is_none() { + entry.time = Some(timestamp); + entry.waker.wake(); + } } } From 7e8c1242678c78e9e3a5aea54d069b74e532159c Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 18 Jul 2026 20:04:58 -0500 Subject: [PATCH 3/6] wip --- embassy-net/Cargo.toml | 2 +- embassy-net/src/lib.rs | 10 ++++++++-- embassy-net/src/udp.rs | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index acf34bd5ab..383a6b2ba7 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -110,7 +110,7 @@ alloc = ["smoltcp/alloc"] ## Enable smoltcp packetmeta-id feature packetmeta-id = [ "smoltcp/packetmeta-id", "embassy-net-driver/packetmeta-id" ] ## Enable ptp support -ptp = [] +ptp = ["packetmeta-id"] [dependencies] diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 7a9bade92d..ff7d797291 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -338,11 +338,15 @@ pub(crate) struct Inner { dhcp_rx_buffer: *mut [u8], #[cfg(feature = "ptp")] times: &'static mut dyn LinearMap, + #[cfg(feature = "ptp")] + next_id: u16, } #[cfg(feature = "ptp")] struct TimeEntry { - id: u8, + #[allow(unused)] + id: u16, + did: Option, time: Option, waker: WakerRegistration, } @@ -464,6 +468,8 @@ pub fn new<'d, D: Driver, const SOCK: usize>( dhcp_rx_buffer: resources.dhcp_rx_buffer.write([0; DHCP_RX_BUFFER_SIZE]) as *mut [u8], #[cfg(feature = "ptp")] times: unsafe { transmute_static(resources.times.write(heapless::LinearMap::new())) }, + #[cfg(feature = "ptp")] + next_id: 0, }; #[cfg(feature = "proto-ipv4")] @@ -1002,7 +1008,7 @@ impl Inner { #[cfg(feature = "ptp")] while let Some((id, timestamp)) = driver.poll_timestamp(cx) { for (_handle, entry) in self.times.iter_mut() { - if id == entry.id && entry.time.is_none() { + if entry.did.is_some_and(|did| id == did) && entry.time.is_none() { entry.time = Some(timestamp); entry.waker.wake(); } diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 93f298ee1a..604c49fc6e 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -311,6 +311,45 @@ impl<'a> UdpSocket<'a> { }) } + #[cfg(feature = "ptp")] + /// Send a datagram to the specified remote endpoint and retreive the timestamp in which it has been sent. + /// + /// This method will wait until the datagram has been sent. + /// + /// If the socket's send buffer is too small to fit `buf`, this method will return `Err(SendError::PacketTooLarge)` + /// + /// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)` + pub async fn send_to_timed( + &self, + buf: &[u8], + remote_endpoint: T, + ) -> Result + where + T: Into, + { + let mut remote_endpoint: UdpMetadata = remote_endpoint.into(); + + self.stack.with_mut(|s| { + s.next_id = s.next_id.wrapping_add(1); + remote_endpoint.meta.id = s.next_id as u32; + s.times + .insert( + self.handle, + crate::TimeEntry { + id: s.next_id, + did: None, + time: None, + waker: embassy_sync::waitqueue::WakerRegistration::new(), + }, + ) + .unwrap(); + }); + + poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await?; + + todo!() + } + /// Send a datagram to the specified remote endpoint. /// /// This method will wait until the datagram has been sent. From 6aa4445463128e640a098a343fe71eed776c9d9b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 18 Jul 2026 21:58:10 -0500 Subject: [PATCH 4/6] wip --- embassy-net/src/lib.rs | 12 ++++++++++++ embassy-net/src/udp.rs | 32 +++++++++++++++----------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ff7d797291..d9f303d037 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -351,6 +351,18 @@ struct TimeEntry { waker: WakerRegistration, } +#[cfg(feature = "ptp")] +impl TimeEntry { + pub const fn new(id: u16) -> Self { + Self { + id, + did: None, + time: None, + waker: WakerRegistration::new(), + } + } +} + #[cfg(feature = "ptp")] trait LinearMap { #[allow(dead_code)] diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 604c49fc6e..1927008a14 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -161,6 +161,17 @@ impl<'a> UdpSocket<'a> { }) } + #[cfg(feature = "ptp")] + fn with_id(&self, mut remote_endpoint: UdpMetadata) -> (UdpMetadata, u16) { + self.stack.with_mut(|i| { + i.next_id = i.next_id.wrapping_add(1); + remote_endpoint.meta.id = i.next_id as u32; + i.times.insert(self.handle, crate::TimeEntry::new(i.next_id)).unwrap(); + + (remote_endpoint, i.next_id) + }) + } + /// Wait until the socket becomes readable. /// /// A socket is readable when a packet has been received, or when there are queued packets in @@ -327,26 +338,13 @@ impl<'a> UdpSocket<'a> { where T: Into, { - let mut remote_endpoint: UdpMetadata = remote_endpoint.into(); - - self.stack.with_mut(|s| { - s.next_id = s.next_id.wrapping_add(1); - remote_endpoint.meta.id = s.next_id as u32; - s.times - .insert( - self.handle, - crate::TimeEntry { - id: s.next_id, - did: None, - time: None, - waker: embassy_sync::waitqueue::WakerRegistration::new(), - }, - ) - .unwrap(); - }); + let remote_endpoint: UdpMetadata = remote_endpoint.into(); + let (remote_endpoint, id) = self.with_id(remote_endpoint); poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await?; + // TODO: wait for the timestamp to be entered + todo!() } From 38928e84dfa87f76051ace82ac5b4a2a0ba434ab Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 18 Jul 2026 22:13:27 -0500 Subject: [PATCH 5/6] wip --- embassy-net/src/driver_util.rs | 3 +++ embassy-net/src/lib.rs | 9 ++++++++- embassy-net/src/udp.rs | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/embassy-net/src/driver_util.rs b/embassy-net/src/driver_util.rs index 4db57aa631..2796103504 100644 --- a/embassy-net/src/driver_util.rs +++ b/embassy-net/src/driver_util.rs @@ -13,6 +13,9 @@ where pub inner: &'d mut T, pub medium: Medium, pub tx_exhausted: bool, + #[cfg(feature = "ptp")] + #[allow(unused)] + pub times: &'d mut dyn crate::LinearMap, } impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T> diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index d9f303d037..42ebd9fe23 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -419,6 +419,9 @@ pub fn new<'d, D: Driver, const SOCK: usize>( iface_cfg.slaac = matches!(config.ipv6, ConfigV6::Slaac); } + #[cfg(feature = "ptp")] + let times = resources.times.write(heapless::LinearMap::new()); + #[allow(unused_mut)] let mut iface = Interface::new( iface_cfg, @@ -427,6 +430,8 @@ pub fn new<'d, D: Driver, const SOCK: usize>( cx: None, medium, tx_exhausted: false, + #[cfg(feature = "ptp")] + times, }, instant_to_smoltcp(Instant::now()), ); @@ -479,7 +484,7 @@ pub fn new<'d, D: Driver, const SOCK: usize>( #[cfg(feature = "dhcpv4-ntp")] dhcp_rx_buffer: resources.dhcp_rx_buffer.write([0; DHCP_RX_BUFFER_SIZE]) as *mut [u8], #[cfg(feature = "ptp")] - times: unsafe { transmute_static(resources.times.write(heapless::LinearMap::new())) }, + times: unsafe { transmute_static(times) }, #[cfg(feature = "ptp")] next_id: 0, }; @@ -1052,6 +1057,8 @@ impl Inner { inner: driver, medium, tx_exhausted: false, + #[cfg(feature = "ptp")] + times: self.times, }; self.iface.poll(timestamp, &mut smoldev, &mut self.sockets); let tx_exhausted = smoldev.tx_exhausted; diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 1927008a14..8abd5f0874 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -339,7 +339,7 @@ impl<'a> UdpSocket<'a> { T: Into, { let remote_endpoint: UdpMetadata = remote_endpoint.into(); - let (remote_endpoint, id) = self.with_id(remote_endpoint); + let (remote_endpoint, _id) = self.with_id(remote_endpoint); poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await?; From b29af44eff0a1ce096efa43a9615672db432e8b3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 18 Jul 2026 22:19:32 -0500 Subject: [PATCH 6/6] wip --- embassy-net/src/driver_util.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-net/src/driver_util.rs b/embassy-net/src/driver_util.rs index 2796103504..e689476ae5 100644 --- a/embassy-net/src/driver_util.rs +++ b/embassy-net/src/driver_util.rs @@ -123,6 +123,7 @@ where } fn set_meta(&mut self, meta: phy::PacketMeta) { + // TODO: when called with a nonzero ID, associate the inner ID of this token with the ID if set. self.0.set_meta(into_embassy_net_meta(meta)); } }