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
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use crate::serial::open_pty_pair;
178 use std::os::unix::io::{AsRawFd, FromRawFd};
179 use std::sync::mpsc;
180 use std::time::Duration;
181
182 fn poll_read(fd: i32, timeout_ms: i32) -> bool {
184 let mut pfd = libc::pollfd {
185 fd,
186 events: libc::POLLIN,
187 revents: 0,
188 };
189 let ret = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
190 ret > 0
191 }
192
193 #[test]
194 fn serial_hdlc_roundtrip() {
195 let (master_fd, slave_fd) = open_pty_pair().unwrap();
196 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
197 let mut slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
198
199 let payload: Vec<u8> = (0..32).collect();
201 let framed = hdlc::frame(&payload);
202 master_file.write_all(&framed).unwrap();
203 master_file.flush().unwrap();
204
205 assert!(poll_read(slave_file.as_raw_fd(), 2000), "should have data available");
207
208 let mut decoder = hdlc::Decoder::new();
209 let mut buf = [0u8; 4096];
210 let n = slave_file.read(&mut buf).unwrap();
211 let frames = decoder.feed(&buf[..n]);
212 assert_eq!(frames.len(), 1);
213 assert_eq!(frames[0], payload);
214 }
215
216 #[test]
217 fn serial_writer_frames() {
218 let (master_fd, slave_fd) = open_pty_pair().unwrap();
219
220 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
221 let mut writer = SerialWriter { file: writer_file };
222
223 let payload = vec![0x01, 0x02, 0x7E, 0x7D, 0x03]; writer.send_frame(&payload).unwrap();
225
226 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
228 assert!(poll_read(master_file.as_raw_fd(), 2000), "should have data");
229
230 let expected = hdlc::frame(&payload);
231 let mut buf = [0u8; 256];
232 let n = master_file.read(&mut buf).unwrap();
233 assert_eq!(&buf[..n], &expected[..]);
234 }
235
236 #[test]
237 fn serial_fragmented_read() {
238 let (master_fd, slave_fd) = open_pty_pair().unwrap();
239 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
240 let slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
241
242 let (tx, rx) = mpsc::channel::<Event>();
243
244 let payload: Vec<u8> = (0..32).collect();
246 let framed = hdlc::frame(&payload);
247 let mid = framed.len() / 2;
248
249 let reader_thread = thread::spawn(move || {
251 let mut reader = slave_file;
252 let mut decoder = hdlc::Decoder::new();
253 let mut buf = [0u8; 4096];
254
255 loop {
256 match reader.read(&mut buf) {
257 Ok(n) if n > 0 => {
258 for frame in decoder.feed(&buf[..n]) {
259 let _ = tx.send(Event::Frame { interface_id: InterfaceId(0), data: frame });
260 return;
261 }
262 }
263 _ => return,
264 }
265 }
266 });
267
268 master_file.write_all(&framed[..mid]).unwrap();
270 master_file.flush().unwrap();
271
272 thread::sleep(Duration::from_millis(50));
274 master_file.write_all(&framed[mid..]).unwrap();
275 master_file.flush().unwrap();
276
277 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
278 match event {
279 Event::Frame { data, .. } => assert_eq!(data, payload),
280 other => panic!("expected Frame, got {:?}", other),
281 }
282
283 let _ = reader_thread.join();
284 }
285
286 #[test]
287 fn serial_reconnect_on_close() {
288 let (master_fd, slave_fd) = open_pty_pair().unwrap();
289
290 let (tx, rx) = mpsc::channel::<Event>();
291 let id = InterfaceId(42);
292
293 let slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
295 let reader_thread = thread::spawn(move || {
296 let mut reader = slave_file;
297 let mut buf = [0u8; 4096];
298 let mut decoder = hdlc::Decoder::new();
299 loop {
300 match reader.read(&mut buf) {
301 Ok(0) => {
302 let _ = tx.send(Event::InterfaceDown(id));
303 return;
304 }
305 Ok(n) => {
306 for frame in decoder.feed(&buf[..n]) {
307 let _ = tx.send(Event::Frame { interface_id: id, data: frame });
308 }
309 }
310 Err(_) => {
311 let _ = tx.send(Event::InterfaceDown(id));
312 return;
313 }
314 }
315 }
316 });
317
318 unsafe { libc::close(master_fd) };
320
321 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
322 assert!(matches!(event, Event::InterfaceDown(InterfaceId(42))));
323
324 let _ = reader_thread.join();
325 }
326}