1use std::io::{self, Read, Write};
8use std::sync::{Arc, Mutex};
9use std::thread;
10use std::time::{Duration, Instant};
11use std::collections::VecDeque;
12
13use rns_core::transport::types::InterfaceId;
14
15use crate::event::{Event, EventSender};
16use crate::kiss;
17use crate::interface::Writer;
18use crate::serial::{Parity, SerialConfig, SerialPort};
19
20#[derive(Debug, Clone)]
22pub struct KissIfaceConfig {
23 pub name: String,
24 pub port: String,
25 pub speed: u32,
26 pub data_bits: u8,
27 pub parity: Parity,
28 pub stop_bits: u8,
29 pub preamble: u16, pub txtail: u16, pub persistence: u8, pub slottime: u16, pub flow_control: bool, pub beacon_interval: Option<u32>, pub beacon_data: Option<Vec<u8>>, pub interface_id: InterfaceId,
37}
38
39impl Default for KissIfaceConfig {
40 fn default() -> Self {
41 KissIfaceConfig {
42 name: String::new(),
43 port: String::new(),
44 speed: 9600,
45 data_bits: 8,
46 parity: Parity::None,
47 stop_bits: 1,
48 preamble: 350,
49 txtail: 20,
50 persistence: 64,
51 slottime: 20,
52 flow_control: false,
53 beacon_interval: None,
54 beacon_data: None,
55 interface_id: InterfaceId(0),
56 }
57 }
58}
59
60struct FlowState {
62 ready: bool,
63 queue: VecDeque<Vec<u8>>,
64 lock_time: Instant,
65}
66
67struct KissWriter {
70 file: std::fs::File,
71 flow_control: bool,
72 flow_state: Arc<Mutex<FlowState>>,
73}
74
75impl Writer for KissWriter {
76 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
77 if self.flow_control {
78 let mut state = self.flow_state.lock().unwrap();
79 if state.ready {
80 state.ready = false;
81 state.lock_time = Instant::now();
82 drop(state);
83 self.file.write_all(&kiss::frame(data))
84 } else {
85 state.queue.push_back(data.to_vec());
86 Ok(())
87 }
88 } else {
89 self.file.write_all(&kiss::frame(data))
90 }
91 }
92}
93
94pub fn start(config: KissIfaceConfig, tx: EventSender) -> io::Result<Box<dyn Writer>> {
96 let serial_config = SerialConfig {
97 path: config.port.clone(),
98 baud: config.speed,
99 data_bits: config.data_bits,
100 parity: config.parity,
101 stop_bits: config.stop_bits,
102 };
103
104 let port = SerialPort::open(&serial_config)?;
105 let reader_file = port.reader()?;
106 let mut writer_file = port.writer()?;
107 let flow_writer_file = port.writer()?;
108
109 let id = config.interface_id;
110
111 thread::sleep(Duration::from_secs(2));
113
114 let _ = tx.send(Event::InterfaceUp(id, None, None));
116
117 configure_tnc(&mut writer_file, &config)?;
119
120 let flow_state = Arc::new(Mutex::new(FlowState {
121 ready: true,
122 queue: VecDeque::new(),
123 lock_time: Instant::now(),
124 }));
125
126 let reader_flow_state = flow_state.clone();
127
128 let reader_config = config.clone();
130 thread::Builder::new()
131 .name(format!("kiss-reader-{}", id.0))
132 .spawn(move || {
133 reader_loop(reader_file, flow_writer_file, id, reader_config, tx, reader_flow_state);
134 })?;
135
136 Ok(Box::new(KissWriter {
137 file: writer_file,
138 flow_control: config.flow_control,
139 flow_state,
140 }))
141}
142
143fn configure_tnc(writer: &mut std::fs::File, config: &KissIfaceConfig) -> io::Result<()> {
146 log::info!("[{}] configuring KISS interface parameters", config.name);
147
148 let preamble_val = (config.preamble / 10).min(255) as u8;
150 writer.write_all(&kiss::command_frame(kiss::CMD_TXDELAY, &[preamble_val]))?;
151
152 let txtail_val = (config.txtail / 10).min(255) as u8;
154 writer.write_all(&kiss::command_frame(kiss::CMD_TXTAIL, &[txtail_val]))?;
155
156 writer.write_all(&kiss::command_frame(kiss::CMD_P, &[config.persistence]))?;
158
159 let slottime_val = (config.slottime / 10).min(255) as u8;
161 writer.write_all(&kiss::command_frame(kiss::CMD_SLOTTIME, &[slottime_val]))?;
162
163 writer.write_all(&kiss::command_frame(kiss::CMD_READY, &[0x01]))?;
165
166 log::info!("[{}] KISS interface configured", config.name);
167 Ok(())
168}
169
170fn reader_loop(
173 mut reader: std::fs::File,
174 mut flow_writer: std::fs::File,
175 id: InterfaceId,
176 config: KissIfaceConfig,
177 tx: EventSender,
178 flow_state: Arc<Mutex<FlowState>>,
179) {
180 let mut decoder = kiss::Decoder::new();
181 let mut buf = [0u8; 4096];
182 let mut first_tx: Option<Instant> = None;
183
184 loop {
185 match reader.read(&mut buf) {
186 Ok(0) => {
187 log::warn!("[{}] KISS port closed", config.name);
188 let _ = tx.send(Event::InterfaceDown(id));
189 match reconnect(&config, &tx, &flow_state) {
190 Some((new_reader, new_flow_writer)) => {
191 reader = new_reader;
192 flow_writer = new_flow_writer;
193 decoder = kiss::Decoder::new();
194 continue;
195 }
196 None => return,
197 }
198 }
199 Ok(n) => {
200 for event in decoder.feed(&buf[..n]) {
201 match event {
202 kiss::KissEvent::DataFrame(data) => {
203 if tx.send(Event::Frame { interface_id: id, data }).is_err() {
204 return;
205 }
206 }
207 kiss::KissEvent::Ready => {
208 process_queue(&flow_state, &mut flow_writer, &mut first_tx, &config);
209 }
210 }
211 }
212 }
213 Err(e) => {
214 log::warn!("[{}] KISS read error: {}", config.name, e);
215 let _ = tx.send(Event::InterfaceDown(id));
216 match reconnect(&config, &tx, &flow_state) {
217 Some((new_reader, new_flow_writer)) => {
218 reader = new_reader;
219 flow_writer = new_flow_writer;
220 decoder = kiss::Decoder::new();
221 continue;
222 }
223 None => return,
224 }
225 }
226 }
227
228 if config.flow_control {
230 let state = flow_state.lock().unwrap();
231 if !state.ready && state.lock_time.elapsed() > Duration::from_secs(5) {
232 drop(state);
233 log::warn!("[{}] unlocking flow control due to timeout", config.name);
234 process_queue(&flow_state, &mut flow_writer, &mut first_tx, &config);
235 }
236 }
237
238 if let (Some(interval), Some(ref beacon_data)) = (config.beacon_interval, &config.beacon_data) {
240 if let Some(first) = first_tx {
241 if first.elapsed() > Duration::from_secs(interval as u64) {
242 log::debug!("[{}] transmitting beacon data", config.name);
243 let mut frame = beacon_data.clone();
245 while frame.len() < 15 {
246 frame.push(0x00);
247 }
248 let _ = flow_writer.write_all(&kiss::frame(&frame));
249 first_tx = None;
250 }
251 }
252 }
253 }
254}
255
256fn process_queue(
258 flow_state: &Arc<Mutex<FlowState>>,
259 writer: &mut std::fs::File,
260 first_tx: &mut Option<Instant>,
261 _config: &KissIfaceConfig,
262) {
263 let mut state = flow_state.lock().unwrap();
264 if let Some(data) = state.queue.pop_front() {
265 state.ready = false;
266 state.lock_time = Instant::now();
267 drop(state);
268 let _ = writer.write_all(&kiss::frame(&data));
269 if first_tx.is_none() {
270 *first_tx = Some(Instant::now());
271 }
272 } else {
273 state.ready = true;
274 }
275}
276
277fn reconnect(
279 config: &KissIfaceConfig,
280 tx: &EventSender,
281 flow_state: &Arc<Mutex<FlowState>>,
282) -> Option<(std::fs::File, std::fs::File)> {
283 loop {
284 thread::sleep(Duration::from_secs(5));
285 log::info!("[{}] attempting to reconnect KISS port {}...", config.name, config.port);
286
287 let serial_config = SerialConfig {
288 path: config.port.clone(),
289 baud: config.speed,
290 data_bits: config.data_bits,
291 parity: config.parity,
292 stop_bits: config.stop_bits,
293 };
294
295 match SerialPort::open(&serial_config) {
296 Ok(port) => {
297 match (port.reader(), port.writer(), port.writer()) {
298 (Ok(reader), Ok(mut cfg_writer), Ok(flow_writer)) => {
299 thread::sleep(Duration::from_secs(2));
301 if let Err(e) = configure_tnc(&mut cfg_writer, config) {
302 log::warn!("[{}] TNC config failed: {}", config.name, e);
303 continue;
304 }
305 let mut state = flow_state.lock().unwrap();
307 state.ready = true;
308 state.queue.clear();
309 drop(state);
310
311 let new_writer: Box<dyn Writer> = Box::new(KissWriter {
312 file: cfg_writer,
313 flow_control: config.flow_control,
314 flow_state: flow_state.clone(),
315 });
316 let _ = tx.send(Event::InterfaceUp(config.interface_id, Some(new_writer), None));
317 log::info!("[{}] KISS port reconnected", config.name);
318 return Some((reader, flow_writer));
319 }
320 _ => {
321 log::warn!("[{}] failed to get handles from serial port", config.name);
322 }
323 }
324 }
325 Err(e) => {
326 log::warn!("[{}] KISS reconnect failed: {}", config.name, e);
327 }
328 }
329 }
330}
331
332use std::collections::HashMap;
335use rns_core::transport::types::InterfaceInfo;
336use super::{InterfaceFactory, InterfaceConfigData, StartContext, StartResult};
337
338pub struct KissFactory;
340
341impl InterfaceFactory for KissFactory {
342 fn type_name(&self) -> &str { "KISSInterface" }
343
344 fn default_ifac_size(&self) -> usize { 8 }
345
346 fn parse_config(
347 &self,
348 name: &str,
349 id: InterfaceId,
350 params: &HashMap<String, String>,
351 ) -> Result<Box<dyn InterfaceConfigData>, String> {
352 let port = params.get("port")
353 .cloned()
354 .ok_or_else(|| "KISSInterface requires 'port'".to_string())?;
355
356 let speed = params.get("speed")
357 .and_then(|v| v.parse().ok())
358 .unwrap_or(9600u32);
359
360 let data_bits = params.get("databits")
361 .and_then(|v| v.parse().ok())
362 .unwrap_or(8u8);
363
364 let parity = params.get("parity")
365 .map(|v| match v.to_lowercase().as_str() {
366 "e" | "even" => crate::serial::Parity::Even,
367 "o" | "odd" => crate::serial::Parity::Odd,
368 _ => crate::serial::Parity::None,
369 })
370 .unwrap_or(crate::serial::Parity::None);
371
372 let stop_bits = params.get("stopbits")
373 .and_then(|v| v.parse().ok())
374 .unwrap_or(1u8);
375
376 let preamble = params.get("preamble")
377 .and_then(|v| v.parse().ok())
378 .unwrap_or(350u16);
379
380 let txtail = params.get("txtail")
381 .and_then(|v| v.parse().ok())
382 .unwrap_or(20u16);
383
384 let persistence = params.get("persistence")
385 .and_then(|v| v.parse().ok())
386 .unwrap_or(64u8);
387
388 let slottime = params.get("slottime")
389 .and_then(|v| v.parse().ok())
390 .unwrap_or(20u16);
391
392 let flow_control = params.get("flow_control")
393 .and_then(|v| crate::config::parse_bool_pub(v))
394 .unwrap_or(false);
395
396 let beacon_interval = params.get("id_interval")
397 .or_else(|| params.get("beacon_interval"))
398 .and_then(|v| v.parse().ok());
399
400 let beacon_data = params.get("id_callsign")
401 .or_else(|| params.get("beacon_data"))
402 .map(|v| v.as_bytes().to_vec());
403
404 Ok(Box::new(KissIfaceConfig {
405 name: name.to_string(),
406 port,
407 speed,
408 data_bits,
409 parity,
410 stop_bits,
411 preamble,
412 txtail,
413 persistence,
414 slottime,
415 flow_control,
416 beacon_interval,
417 beacon_data,
418 interface_id: id,
419 }))
420 }
421
422 fn start(
423 &self,
424 config: Box<dyn InterfaceConfigData>,
425 ctx: StartContext,
426 ) -> std::io::Result<StartResult> {
427 let kiss_config = *config.into_any().downcast::<KissIfaceConfig>()
428 .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "wrong config type"))?;
429
430 let id = kiss_config.interface_id;
431 let name = kiss_config.name.clone();
432
433 let info = InterfaceInfo {
434 id,
435 name,
436 mode: ctx.mode,
437 out_capable: true,
438 in_capable: true,
439 bitrate: Some(1200),
440 announce_rate_target: None,
441 announce_rate_grace: 0,
442 announce_rate_penalty: 0.0,
443 announce_cap: rns_core::constants::ANNOUNCE_CAP,
444 is_local_client: false,
445 wants_tunnel: false,
446 tunnel_id: None,
447 mtu: rns_core::constants::MTU as u32,
448 ingress_control: false,
449 ia_freq: 0.0,
450 started: crate::time::now(),
451 };
452
453 let writer = start(kiss_config, ctx.tx)?;
454
455 Ok(StartResult::Simple {
456 id,
457 info,
458 writer,
459 interface_type_name: "KISSInterface".to_string(),
460 })
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467 use crate::serial::open_pty_pair;
468 use std::os::unix::io::{AsRawFd, FromRawFd};
469 use std::sync::mpsc;
470
471 fn poll_read(fd: i32, timeout_ms: i32) -> bool {
473 let mut pfd = libc::pollfd {
474 fd,
475 events: libc::POLLIN,
476 revents: 0,
477 };
478 let ret = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
479 ret > 0
480 }
481
482 #[test]
483 fn kiss_data_roundtrip() {
484 let (master_fd, slave_fd) = open_pty_pair().unwrap();
485 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
486 let mut slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
487
488 let payload = vec![0x01, 0x02, 0x03, 0x04, 0x05];
490 let framed = kiss::frame(&payload);
491 master_file.write_all(&framed).unwrap();
492 master_file.flush().unwrap();
493
494 assert!(poll_read(slave_file.as_raw_fd(), 2000));
496
497 let mut decoder = kiss::Decoder::new();
498 let mut buf = [0u8; 4096];
499 let n = slave_file.read(&mut buf).unwrap();
500 let events = decoder.feed(&buf[..n]);
501 assert_eq!(events.len(), 1);
502 assert_eq!(events[0], kiss::KissEvent::DataFrame(payload));
503 }
504
505 #[test]
506 fn kiss_writer_frames() {
507 let (master_fd, slave_fd) = open_pty_pair().unwrap();
508
509 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
510 let flow_state = Arc::new(Mutex::new(FlowState {
511 ready: true,
512 queue: VecDeque::new(),
513 lock_time: Instant::now(),
514 }));
515
516 let mut writer = KissWriter {
517 file: writer_file,
518 flow_control: false,
519 flow_state,
520 };
521
522 let payload = vec![0xC0, 0xDB, 0x01]; writer.send_frame(&payload).unwrap();
524
525 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
527 assert!(poll_read(master_file.as_raw_fd(), 2000));
528
529 let expected = kiss::frame(&payload);
530 let mut buf = [0u8; 256];
531 let n = master_file.read(&mut buf).unwrap();
532 assert_eq!(&buf[..n], &expected[..]);
533 }
534
535 #[test]
536 fn kiss_config_commands() {
537 let (master_fd, slave_fd) = open_pty_pair().unwrap();
538
539 let mut writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
540 let config = KissIfaceConfig {
541 preamble: 350,
542 txtail: 20,
543 persistence: 64,
544 slottime: 20,
545 ..Default::default()
546 };
547
548 configure_tnc(&mut writer_file, &config).unwrap();
549
550 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
552 assert!(poll_read(master_file.as_raw_fd(), 2000));
553
554 let mut buf = [0u8; 1024];
555 let n = master_file.read(&mut buf).unwrap();
556 let data = &buf[..n];
557
558 assert!(data.windows(4).any(|w|
561 w[0] == kiss::FEND && w[1] == kiss::CMD_TXDELAY && w[2] == 35 && w[3] == kiss::FEND
562 ), "should contain TXDELAY command");
563
564 assert!(data.windows(4).any(|w|
566 w[0] == kiss::FEND && w[1] == kiss::CMD_TXTAIL && w[2] == 2 && w[3] == kiss::FEND
567 ), "should contain TXTAIL command");
568
569 assert!(data.windows(4).any(|w|
571 w[0] == kiss::FEND && w[1] == kiss::CMD_P && w[2] == 64 && w[3] == kiss::FEND
572 ), "should contain P command");
573
574 assert!(data.windows(4).any(|w|
576 w[0] == kiss::FEND && w[1] == kiss::CMD_SLOTTIME && w[2] == 2 && w[3] == kiss::FEND
577 ), "should contain SLOTTIME command");
578 }
579
580 #[test]
581 fn kiss_flow_control_lock() {
582 let flow_state = Arc::new(Mutex::new(FlowState {
583 ready: true,
584 queue: VecDeque::new(),
585 lock_time: Instant::now(),
586 }));
587
588 let (master_fd, slave_fd) = open_pty_pair().unwrap();
589 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
590
591 let mut writer = KissWriter {
592 file: writer_file,
593 flow_control: true,
594 flow_state: flow_state.clone(),
595 };
596
597 writer.send_frame(b"hello").unwrap();
599 assert!(!flow_state.lock().unwrap().ready);
600
601 writer.send_frame(b"world").unwrap();
603 assert_eq!(flow_state.lock().unwrap().queue.len(), 1);
604
605 let mut flow_writer = unsafe { std::fs::File::from_raw_fd(libc::dup(master_fd)) };
607 let mut first_tx = None;
608 let config = KissIfaceConfig::default();
609 process_queue(&flow_state, &mut flow_writer, &mut first_tx, &config);
610
611 assert_eq!(flow_state.lock().unwrap().queue.len(), 0);
613 assert!(!flow_state.lock().unwrap().ready);
614
615 process_queue(&flow_state, &mut flow_writer, &mut first_tx, &config);
617 assert!(flow_state.lock().unwrap().ready);
618
619 unsafe { libc::close(master_fd) };
621 }
622
623 #[test]
624 fn kiss_flow_control_timeout() {
625 let flow_state = Arc::new(Mutex::new(FlowState {
626 ready: false,
627 queue: VecDeque::new(),
628 lock_time: Instant::now() - Duration::from_secs(6), }));
630
631 let state = flow_state.lock().unwrap();
633 assert!(!state.ready);
634 assert!(state.lock_time.elapsed() > Duration::from_secs(5));
635 }
636
637 #[test]
638 fn kiss_fragmented() {
639 let (master_fd, slave_fd) = open_pty_pair().unwrap();
640 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
641 let slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
642
643 let payload = vec![0x01, 0x02, 0x03, 0x04, 0x05];
644 let framed = kiss::frame(&payload);
645 let mid = framed.len() / 2;
646
647 let (tx, rx) = mpsc::channel::<kiss::KissEvent>();
649 let reader_thread = thread::spawn(move || {
650 let mut reader = slave_file;
651 let mut decoder = kiss::Decoder::new();
652 let mut buf = [0u8; 4096];
653
654 loop {
655 match reader.read(&mut buf) {
656 Ok(n) if n > 0 => {
657 for event in decoder.feed(&buf[..n]) {
658 let _ = tx.send(event.clone());
659 if matches!(event, kiss::KissEvent::DataFrame(_)) {
660 return;
661 }
662 }
663 }
664 _ => return,
665 }
666 }
667 });
668
669 master_file.write_all(&framed[..mid]).unwrap();
671 master_file.flush().unwrap();
672
673 thread::sleep(Duration::from_millis(50));
674
675 master_file.write_all(&framed[mid..]).unwrap();
677 master_file.flush().unwrap();
678
679 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
680 assert_eq!(event, kiss::KissEvent::DataFrame(payload));
681
682 let _ = reader_thread.join();
683 }
684}