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.send(Event::Frame { interface_id: id, data: frame }).is_err() {
115 return; }
117 }
118 }
119 Err(e) => {
120 log::warn!("[{}] serial read error: {}", config.name, e);
121 let _ = tx.send(Event::InterfaceDown(id));
122 match reconnect(&config, &tx) {
123 Some(new_reader) => {
124 reader = new_reader;
125 decoder = hdlc::Decoder::new();
126 continue;
127 }
128 None => return,
129 }
130 }
131 }
132 }
133}
134
135fn reconnect(
137 config: &SerialIfaceConfig,
138 tx: &EventSender,
139) -> Option<std::fs::File> {
140 loop {
141 thread::sleep(Duration::from_secs(5));
142 log::info!("[{}] attempting to reconnect serial port {}...", config.name, config.port);
143
144 let serial_config = SerialConfig {
145 path: config.port.clone(),
146 baud: config.speed,
147 data_bits: config.data_bits,
148 parity: config.parity,
149 stop_bits: config.stop_bits,
150 };
151
152 match SerialPort::open(&serial_config) {
153 Ok(port) => {
154 match (port.reader(), port.writer()) {
155 (Ok(reader), Ok(writer_file)) => {
156 log::info!("[{}] serial port reconnected", config.name);
157 let new_writer: Box<dyn Writer> = Box::new(SerialWriter { file: writer_file });
158 let _ = tx.send(Event::InterfaceUp(config.interface_id, Some(new_writer), None));
159 thread::sleep(Duration::from_millis(500));
160 return Some(reader);
161 }
162 _ => {
163 log::warn!("[{}] failed to get reader/writer from serial port", config.name);
164 }
165 }
166 }
167 Err(e) => {
168 log::warn!("[{}] serial reconnect failed: {}", config.name, e);
169 }
170 }
171 }
172}
173
174use std::collections::HashMap;
177use rns_core::transport::types::InterfaceInfo;
178use super::{InterfaceFactory, InterfaceConfigData, StartContext, StartResult};
179
180pub struct SerialFactory;
182
183impl InterfaceFactory for SerialFactory {
184 fn type_name(&self) -> &str { "SerialInterface" }
185
186 fn default_ifac_size(&self) -> usize { 8 }
187
188 fn parse_config(
189 &self,
190 name: &str,
191 id: InterfaceId,
192 params: &HashMap<String, String>,
193 ) -> Result<Box<dyn InterfaceConfigData>, String> {
194 let port = params.get("port")
195 .cloned()
196 .ok_or_else(|| "SerialInterface requires 'port'".to_string())?;
197
198 let speed = params.get("speed")
199 .and_then(|v| v.parse().ok())
200 .unwrap_or(9600u32);
201
202 let data_bits = params.get("databits")
203 .and_then(|v| v.parse().ok())
204 .unwrap_or(8u8);
205
206 let parity = params.get("parity")
207 .map(|v| match v.to_lowercase().as_str() {
208 "e" | "even" => crate::serial::Parity::Even,
209 "o" | "odd" => crate::serial::Parity::Odd,
210 _ => crate::serial::Parity::None,
211 })
212 .unwrap_or(crate::serial::Parity::None);
213
214 let stop_bits = params.get("stopbits")
215 .and_then(|v| v.parse().ok())
216 .unwrap_or(1u8);
217
218 Ok(Box::new(SerialIfaceConfig {
219 name: name.to_string(),
220 port,
221 speed,
222 data_bits,
223 parity,
224 stop_bits,
225 interface_id: id,
226 }))
227 }
228
229 fn start(
230 &self,
231 config: Box<dyn InterfaceConfigData>,
232 ctx: StartContext,
233 ) -> std::io::Result<StartResult> {
234 let serial_config = *config.into_any().downcast::<SerialIfaceConfig>()
235 .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "wrong config type"))?;
236
237 let id = serial_config.interface_id;
238 let name = serial_config.name.clone();
239 let bitrate = Some(serial_config.speed as u64);
240
241 let info = InterfaceInfo {
242 id,
243 name,
244 mode: ctx.mode,
245 out_capable: true,
246 in_capable: true,
247 bitrate,
248 announce_rate_target: None,
249 announce_rate_grace: 0,
250 announce_rate_penalty: 0.0,
251 announce_cap: rns_core::constants::ANNOUNCE_CAP,
252 is_local_client: false,
253 wants_tunnel: false,
254 tunnel_id: None,
255 mtu: rns_core::constants::MTU as u32,
256 ingress_control: false,
257 ia_freq: 0.0,
258 started: crate::time::now(),
259 };
260
261 let writer = start(serial_config, ctx.tx)?;
262
263 Ok(StartResult::Simple {
264 id,
265 info,
266 writer,
267 interface_type_name: "SerialInterface".to_string(),
268 })
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use crate::serial::open_pty_pair;
276 use std::os::unix::io::{AsRawFd, FromRawFd};
277 use std::sync::mpsc;
278 use std::time::Duration;
279
280 fn poll_read(fd: i32, timeout_ms: i32) -> bool {
282 let mut pfd = libc::pollfd {
283 fd,
284 events: libc::POLLIN,
285 revents: 0,
286 };
287 let ret = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
288 ret > 0
289 }
290
291 #[test]
292 fn serial_hdlc_roundtrip() {
293 let (master_fd, slave_fd) = open_pty_pair().unwrap();
294 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
295 let mut slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
296
297 let payload: Vec<u8> = (0..32).collect();
299 let framed = hdlc::frame(&payload);
300 master_file.write_all(&framed).unwrap();
301 master_file.flush().unwrap();
302
303 assert!(poll_read(slave_file.as_raw_fd(), 2000), "should have data available");
305
306 let mut decoder = hdlc::Decoder::new();
307 let mut buf = [0u8; 4096];
308 let n = slave_file.read(&mut buf).unwrap();
309 let frames = decoder.feed(&buf[..n]);
310 assert_eq!(frames.len(), 1);
311 assert_eq!(frames[0], payload);
312 }
313
314 #[test]
315 fn serial_writer_frames() {
316 let (master_fd, slave_fd) = open_pty_pair().unwrap();
317
318 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
319 let mut writer = SerialWriter { file: writer_file };
320
321 let payload = vec![0x01, 0x02, 0x7E, 0x7D, 0x03]; writer.send_frame(&payload).unwrap();
323
324 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
326 assert!(poll_read(master_file.as_raw_fd(), 2000), "should have data");
327
328 let expected = hdlc::frame(&payload);
329 let mut buf = [0u8; 256];
330 let n = master_file.read(&mut buf).unwrap();
331 assert_eq!(&buf[..n], &expected[..]);
332 }
333
334 #[test]
335 fn serial_fragmented_read() {
336 let (master_fd, slave_fd) = open_pty_pair().unwrap();
337 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
338 let slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
339
340 let (tx, rx) = mpsc::channel::<Event>();
341
342 let payload: Vec<u8> = (0..32).collect();
344 let framed = hdlc::frame(&payload);
345 let mid = framed.len() / 2;
346
347 let reader_thread = thread::spawn(move || {
349 let mut reader = slave_file;
350 let mut decoder = hdlc::Decoder::new();
351 let mut buf = [0u8; 4096];
352
353 loop {
354 match reader.read(&mut buf) {
355 Ok(n) if n > 0 => {
356 for frame in decoder.feed(&buf[..n]) {
357 let _ = tx.send(Event::Frame { interface_id: InterfaceId(0), data: frame });
358 return;
359 }
360 }
361 _ => return,
362 }
363 }
364 });
365
366 master_file.write_all(&framed[..mid]).unwrap();
368 master_file.flush().unwrap();
369
370 thread::sleep(Duration::from_millis(50));
372 master_file.write_all(&framed[mid..]).unwrap();
373 master_file.flush().unwrap();
374
375 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
376 match event {
377 Event::Frame { data, .. } => assert_eq!(data, payload),
378 other => panic!("expected Frame, got {:?}", other),
379 }
380
381 let _ = reader_thread.join();
382 }
383
384 #[test]
385 fn serial_reconnect_on_close() {
386 let (master_fd, slave_fd) = open_pty_pair().unwrap();
387
388 let (tx, rx) = mpsc::channel::<Event>();
389 let id = InterfaceId(42);
390
391 let slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
393 let reader_thread = thread::spawn(move || {
394 let mut reader = slave_file;
395 let mut buf = [0u8; 4096];
396 let mut decoder = hdlc::Decoder::new();
397 loop {
398 match reader.read(&mut buf) {
399 Ok(0) => {
400 let _ = tx.send(Event::InterfaceDown(id));
401 return;
402 }
403 Ok(n) => {
404 for frame in decoder.feed(&buf[..n]) {
405 let _ = tx.send(Event::Frame { interface_id: id, data: frame });
406 }
407 }
408 Err(_) => {
409 let _ = tx.send(Event::InterfaceDown(id));
410 return;
411 }
412 }
413 }
414 });
415
416 unsafe { libc::close(master_fd) };
418
419 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
420 assert!(matches!(event, Event::InterfaceDown(InterfaceId(42))));
421
422 let _ = reader_thread.join();
423 }
424}