1use alloc::vec::Vec;
4use core::cell::RefCell;
5
6use embassy_futures::select::{select, select3};
7use embassy_net::{IpCidr, udp::UdpSocket};
8use embassy_sync::{blocking_mutex::raw::NoopRawMutex, signal::Signal};
9use embassy_time::{Instant, Timer};
10use hick_smoltcp::Engine;
11use mdns_proto::{
12 CollectedAnswer, EndpointConfig, QueryHandle, QuerySpec, ServiceHandle, ServiceSpec,
13 error::{RegisterServiceError, StartQueryError},
14 event::{EndpointEvent, QueryUpdate, ServiceUpdate},
15};
16use rand_core::Rng;
17
18use crate::{
19 io::{DualUdp, wait_either_recv},
20 time::EmbassyInstant,
21};
22
23pub struct MdnsState<R> {
31 engine: RefCell<Engine<EmbassyInstant, R>>,
32 wake: Signal<NoopRawMutex, ()>,
33}
34
35impl<R: Rng> MdnsState<R> {
36 pub fn new(config: EndpointConfig, rng: R) -> Self {
39 Self {
40 engine: RefCell::new(Engine::new(config, rng)),
41 wake: Signal::new(),
42 }
43 }
44
45 pub fn set_local_subnets(&self, subnets: Vec<IpCidr>) {
54 self.engine.borrow_mut().set_local_subnets(subnets);
55 }
56
57 pub fn register_service(&self, spec: ServiceSpec) -> Result<ServiceHandle, RegisterServiceError> {
59 let handle = self.engine.borrow_mut().register_service(spec, now())?;
60 self.wake.signal(());
61 Ok(handle)
62 }
63
64 pub fn unregister_service(&self, handle: ServiceHandle) {
66 self.engine.borrow_mut().unregister_service(handle, now());
67 self.wake.signal(());
68 }
69
70 pub fn start_query(&self, spec: QuerySpec) -> Result<QueryHandle, StartQueryError> {
72 let handle = self.engine.borrow_mut().start_query(spec, now())?;
73 self.wake.signal(());
74 Ok(handle)
75 }
76
77 pub fn cancel_query(&self, handle: QueryHandle) {
79 self.engine.borrow_mut().cancel_query(handle);
80 self.wake.signal(());
81 }
82
83 pub fn collected_answers(&self, handle: QueryHandle) -> Vec<CollectedAnswer> {
90 self
91 .engine
92 .borrow()
93 .collected_answers(handle)
94 .cloned()
95 .collect()
96 }
97
98 pub fn query_accepted_count(&self, handle: QueryHandle) -> Option<u64> {
102 self.engine.borrow().query_accepted_count(handle)
103 }
104
105 pub fn poll_service_update(&self, handle: ServiceHandle) -> Option<ServiceUpdate> {
107 self.engine.borrow_mut().poll_service_update(handle)
108 }
109
110 pub fn poll_query_update(&self, handle: QueryHandle) -> Option<QueryUpdate> {
112 self.engine.borrow_mut().poll_query_update(handle)
113 }
114
115 pub fn poll_endpoint_event(&self) -> Option<EndpointEvent> {
117 self.engine.borrow_mut().poll_endpoint_event()
118 }
119
120 #[cfg(feature = "stats")]
122 #[cfg_attr(docsrs, doc(cfg(feature = "stats")))]
123 pub fn stats(&self) -> hick_trace::stats::StatsSnapshot {
124 self.engine.borrow().stats()
125 }
126
127 pub async fn run(
135 &self,
136 mut v4: Option<&mut UdpSocket<'_>>,
137 mut v6: Option<&mut UdpSocket<'_>>,
138 scratch: &mut [u8],
139 ) -> ! {
140 if let Some(s) = v4.as_deref_mut() {
142 s.set_hop_limit(Some(255));
143 #[cfg(feature = "defmt")]
144 defmt::debug!("hick-embassy MdnsState: v4 hop-limit set to 255 (RFC 6762 §11)");
145 }
146 if let Some(s) = v6.as_deref_mut() {
147 s.set_hop_limit(Some(255));
148 #[cfg(feature = "defmt")]
149 defmt::debug!("hick-embassy MdnsState: v6 hop-limit set to 255 (RFC 6762 §11)");
150 }
151 loop {
152 let deadline = {
153 let mut engine = self.engine.borrow_mut();
154 let mut io = DualUdp::new(v4.as_deref(), v6.as_deref());
155 engine.pump(now(), &mut io, scratch)
156 };
157 match deadline {
160 Some(d) => {
161 #[cfg(feature = "defmt")]
162 defmt::trace!("hick-embassy MdnsState: waiting for recv, deadline, or wake signal");
163 let _ = select3(
164 wait_either_recv(v4.as_deref(), v6.as_deref()),
165 Timer::at(d.0),
166 self.wake.wait(),
167 )
168 .await;
169 }
170 None => {
171 #[cfg(feature = "defmt")]
172 defmt::trace!("hick-embassy MdnsState: no deadline, waiting for recv or wake signal");
173 let _ = select(
174 wait_either_recv(v4.as_deref(), v6.as_deref()),
175 self.wake.wait(),
176 )
177 .await;
178 }
179 }
180 }
181 }
182}
183
184fn now() -> EmbassyInstant {
186 EmbassyInstant(Instant::now())
187}
188
189#[cfg(test)]
190#[allow(clippy::unwrap_used, clippy::expect_used)]
191mod tests;