1use std::io::{self, Read, Write};
7use std::thread;
8use std::time::Duration;
9
10use rns_core::transport::types::InterfaceId;
11
12use crate::event::{Event, EventSender};
13use crate::hdlc;
14use crate::interface::Writer;
15use crate::serial::{Parity, SerialConfig, SerialPort};
16
17#[derive(Debug, Clone)]
19pub struct SerialIfaceConfig {
20 pub name: String,
21 pub port: String,
22 pub speed: u32,
23 pub data_bits: u8,
24 pub parity: Parity,
25 pub stop_bits: u8,
26 pub interface_id: InterfaceId,
27}
28
29impl Default for SerialIfaceConfig {
30 fn default() -> Self {
31 SerialIfaceConfig {
32 name: String::new(),
33 port: String::new(),
34 speed: 9600,
35 data_bits: 8,
36 parity: Parity::None,
37 stop_bits: 1,
38 interface_id: InterfaceId(0),
39 }
40 }
41}
42
43struct SerialWriter {
45 file: std::fs::File,
46}
47
48impl Writer for SerialWriter {
49 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
50 self.file.write_all(&hdlc::frame(data))
51 }
52}
53
54pub fn start(config: SerialIfaceConfig, tx: EventSender) -> io::Result<Box<dyn Writer>> {
57 let serial_config = SerialConfig {
58 path: config.port.clone(),
59 baud: config.speed,
60 data_bits: config.data_bits,
61 parity: config.parity,
62 stop_bits: config.stop_bits,
63 };
64
65 let port = SerialPort::open(&serial_config)?;
66 let reader_file = port.reader()?;
67 let writer_file = port.writer()?;
68
69 let id = config.interface_id;
70
71 let _ = tx.send(Event::InterfaceUp(id, None, None));
73
74 thread::sleep(Duration::from_millis(500));
76
77 thread::Builder::new()
79 .name(format!("serial-reader-{}", id.0))
80 .spawn(move || {
81 reader_loop(reader_file, id, config, tx);
82 })?;
83
84 Ok(Box::new(SerialWriter { file: writer_file }))
85}
86
87fn reader_loop(
89 mut reader: std::fs::File,
90 id: InterfaceId,
91 config: SerialIfaceConfig,
92 tx: EventSender,
93) {
94 let mut decoder = hdlc::Decoder::new();
95 let mut buf = [0u8; 4096];
96
97 loop {
98 match reader.read(&mut buf) {
99 Ok(0) => {
100 log::warn!("[{}] serial port closed", config.name);
102 let _ = tx.send(Event::InterfaceDown(id));
103 match reconnect(&config, &tx) {
104 Some(new_reader) => {
105 reader = new_reader;
106 decoder = hdlc::Decoder::new();
107 continue;
108 }
109 None => return,
110 }
111 }
112 Ok(n) => {
113 for frame in decoder.feed(&buf[..n]) {
114 if tx
115 .send(Event::Frame {
116 interface_id: id,
117 data: frame,
118 rssi: None,
119 snr: None,
120 })
121 .is_err()
122 {
123 return; }
125 }
126 }
127 Err(e) => {
128 log::warn!("[{}] serial read error: {}", config.name, e);
129 let _ = tx.send(Event::InterfaceDown(id));
130 match reconnect(&config, &tx) {
131 Some(new_reader) => {
132 reader = new_reader;
133 decoder = hdlc::Decoder::new();
134 continue;
135 }
136 None => return,
137 }
138 }
139 }
140 }
141}
142
143fn reconnect(config: &SerialIfaceConfig, tx: &EventSender) -> Option<std::fs::File> {
145 loop {
146 thread::sleep(Duration::from_secs(5));
147 log::info!(
148 "[{}] attempting to reconnect serial port {}...",
149 config.name,
150 config.port
151 );
152
153 let serial_config = SerialConfig {
154 path: config.port.clone(),
155 baud: config.speed,
156 data_bits: config.data_bits,
157 parity: config.parity,
158 stop_bits: config.stop_bits,
159 };
160
161 match SerialPort::open(&serial_config) {
162 Ok(port) => match (port.reader(), port.writer()) {
163 (Ok(reader), Ok(writer_file)) => {
164 log::info!("[{}] serial port reconnected", config.name);
165 let new_writer: Box<dyn Writer> = Box::new(SerialWriter { file: writer_file });
166 let _ = tx.send(Event::InterfaceUp(
167 config.interface_id,
168 Some(new_writer),
169 None,
170 ));
171 thread::sleep(Duration::from_millis(500));
172 return Some(reader);
173 }
174 _ => {
175 log::warn!(
176 "[{}] failed to get reader/writer from serial port",
177 config.name
178 );
179 }
180 },
181 Err(e) => {
182 log::warn!("[{}] serial reconnect failed: {}", config.name, e);
183 }
184 }
185 }
186}
187
188use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult};
191use rns_core::transport::types::InterfaceInfo;
192use std::collections::HashMap;
193
194pub struct SerialFactory;
196
197impl InterfaceFactory for SerialFactory {
198 fn type_name(&self) -> &str {
199 "SerialInterface"
200 }
201
202 fn default_ifac_size(&self) -> usize {
203 8
204 }
205
206 fn parse_config(
207 &self,
208 name: &str,
209 id: InterfaceId,
210 params: &HashMap<String, String>,
211 ) -> Result<Box<dyn InterfaceConfigData>, String> {
212 let port = params
213 .get("port")
214 .cloned()
215 .ok_or_else(|| "SerialInterface requires 'port'".to_string())?;
216
217 let speed = params
218 .get("speed")
219 .and_then(|v| v.parse().ok())
220 .unwrap_or(9600u32);
221
222 let data_bits = params
223 .get("databits")
224 .and_then(|v| v.parse().ok())
225 .unwrap_or(8u8);
226
227 let parity = params
228 .get("parity")
229 .map(|v| match v.to_lowercase().as_str() {
230 "e" | "even" => crate::serial::Parity::Even,
231 "o" | "odd" => crate::serial::Parity::Odd,
232 _ => crate::serial::Parity::None,
233 })
234 .unwrap_or(crate::serial::Parity::None);
235
236 let stop_bits = params
237 .get("stopbits")
238 .and_then(|v| v.parse().ok())
239 .unwrap_or(1u8);
240
241 Ok(Box::new(SerialIfaceConfig {
242 name: name.to_string(),
243 port,
244 speed,
245 data_bits,
246 parity,
247 stop_bits,
248 interface_id: id,
249 }))
250 }
251
252 fn start(
253 &self,
254 config: Box<dyn InterfaceConfigData>,
255 ctx: StartContext,
256 ) -> std::io::Result<StartResult> {
257 let serial_config = *config
258 .into_any()
259 .downcast::<SerialIfaceConfig>()
260 .map_err(|_| {
261 std::io::Error::new(std::io::ErrorKind::InvalidData, "wrong config type")
262 })?;
263
264 let id = serial_config.interface_id;
265 let name = serial_config.name.clone();
266 let bitrate = Some(serial_config.speed as u64);
267
268 let info = InterfaceInfo {
269 id,
270 name,
271 mode: ctx.mode,
272 gravity: ctx.gravity,
273 recursive_prs: ctx.recursive_prs,
274 announces_from_internal: ctx.announces_from_internal,
275 announces_to_internal: ctx.announces_to_internal,
276 out_capable: true,
277 in_capable: true,
278 bitrate,
279 airtime_profile: None,
280 announce_rate_target: None,
281 announce_rate_grace: 0,
282 announce_rate_penalty: 0.0,
283 announce_cap: rns_core::constants::ANNOUNCE_CAP,
284 is_local_client: false,
285 wants_tunnel: false,
286 tunnel_id: None,
287 mtu: rns_core::constants::MTU as u32,
288 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
289 ia_freq: 0.0,
290 ip_freq: 0.0,
291 op_freq: 0.0,
292 op_samples: 0,
293 started: crate::time::now(),
294 };
295
296 let writer = start(serial_config, ctx.tx)?;
297
298 Ok(StartResult::Simple {
299 id,
300 info,
301 writer,
302 interface_type_name: "SerialInterface".to_string(),
303 })
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use crate::serial::open_pty_pair;
311 use std::os::unix::io::{AsRawFd, FromRawFd};
312 use std::sync::mpsc;
313 use std::time::Duration;
314
315 fn poll_read(fd: i32, timeout_ms: i32) -> bool {
317 let mut pfd = libc::pollfd {
318 fd,
319 events: libc::POLLIN,
320 revents: 0,
321 };
322 let ret = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
323 ret > 0
324 }
325
326 #[test]
327 fn serial_hdlc_roundtrip() {
328 let (master_fd, slave_fd) = open_pty_pair().unwrap();
329 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
330 let mut slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
331
332 let payload: Vec<u8> = (0..32).collect();
334 let framed = hdlc::frame(&payload);
335 master_file.write_all(&framed).unwrap();
336 master_file.flush().unwrap();
337
338 assert!(
340 poll_read(slave_file.as_raw_fd(), 2000),
341 "should have data available"
342 );
343
344 let mut decoder = hdlc::Decoder::new();
345 let mut buf = [0u8; 4096];
346 let n = slave_file.read(&mut buf).unwrap();
347 let frames = decoder.feed(&buf[..n]);
348 assert_eq!(frames.len(), 1);
349 assert_eq!(frames[0], payload);
350 }
351
352 #[test]
353 fn serial_writer_frames() {
354 let (master_fd, slave_fd) = open_pty_pair().unwrap();
355
356 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
357 let mut writer = SerialWriter { file: writer_file };
358
359 let payload = vec![0x01, 0x02, 0x7E, 0x7D, 0x03]; writer.send_frame(&payload).unwrap();
361
362 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
364 assert!(poll_read(master_file.as_raw_fd(), 2000), "should have data");
365
366 let expected = hdlc::frame(&payload);
367 let mut buf = [0u8; 256];
368 let n = master_file.read(&mut buf).unwrap();
369 assert_eq!(&buf[..n], &expected[..]);
370 }
371
372 #[test]
373 fn serial_fragmented_read() {
374 let (master_fd, slave_fd) = open_pty_pair().unwrap();
375 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
376 let slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
377
378 let (tx, rx) = mpsc::channel::<Event>();
379
380 let payload: Vec<u8> = (0..32).collect();
382 let framed = hdlc::frame(&payload);
383 let mid = framed.len() / 2;
384
385 let reader_thread = thread::spawn(move || {
387 let mut reader = slave_file;
388 let mut decoder = hdlc::Decoder::new();
389 let mut buf = [0u8; 4096];
390
391 loop {
392 match reader.read(&mut buf) {
393 Ok(n) if n > 0 => {
394 if let Some(frame) = decoder.feed(&buf[..n]).into_iter().next() {
395 let _ = tx.send(Event::Frame {
396 interface_id: InterfaceId(0),
397 data: frame,
398 rssi: None,
399 snr: None,
400 });
401 return;
402 }
403 }
404 _ => return,
405 }
406 }
407 });
408
409 master_file.write_all(&framed[..mid]).unwrap();
411 master_file.flush().unwrap();
412
413 thread::sleep(Duration::from_millis(50));
415 master_file.write_all(&framed[mid..]).unwrap();
416 master_file.flush().unwrap();
417
418 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
419 match event {
420 Event::Frame { data, .. } => assert_eq!(data, payload),
421 other => panic!("expected Frame, got {:?}", other),
422 }
423
424 let _ = reader_thread.join();
425 }
426
427 #[test]
428 fn serial_reconnect_on_close() {
429 let (master_fd, slave_fd) = open_pty_pair().unwrap();
430
431 let (tx, rx) = mpsc::channel::<Event>();
432 let id = InterfaceId(42);
433
434 let slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
436 let reader_thread = thread::spawn(move || {
437 let mut reader = slave_file;
438 let mut buf = [0u8; 4096];
439 let mut decoder = hdlc::Decoder::new();
440 loop {
441 match reader.read(&mut buf) {
442 Ok(0) => {
443 let _ = tx.send(Event::InterfaceDown(id));
444 return;
445 }
446 Ok(n) => {
447 for frame in decoder.feed(&buf[..n]) {
448 let _ = tx.send(Event::Frame {
449 interface_id: id,
450 data: frame,
451 rssi: None,
452 snr: None,
453 });
454 }
455 }
456 Err(_) => {
457 let _ = tx.send(Event::InterfaceDown(id));
458 return;
459 }
460 }
461 }
462 });
463
464 unsafe { libc::close(master_fd) };
466
467 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
468 assert!(matches!(event, Event::InterfaceDown(InterfaceId(42))));
469
470 let _ = reader_thread.join();
471 }
472}