1use std::collections::VecDeque;
8use std::io::{self, Read, Write};
9use std::sync::{Arc, Mutex};
10use std::thread;
11use std::time::{Duration, Instant};
12
13use rns_core::transport::types::InterfaceId;
14
15use crate::event::{Event, EventSender};
16use crate::interface::{lock_or_recover, Writer};
17use crate::kiss;
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 pub ax25_source: Option<Ax25Address>,
38 pub interface_type_name: String,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct Ax25Address {
43 pub callsign: String,
44 pub ssid: u8,
45}
46
47impl Default for KissIfaceConfig {
48 fn default() -> Self {
49 KissIfaceConfig {
50 name: String::new(),
51 port: String::new(),
52 speed: 9600,
53 data_bits: 8,
54 parity: Parity::None,
55 stop_bits: 1,
56 preamble: 350,
57 txtail: 20,
58 persistence: 64,
59 slottime: 20,
60 flow_control: false,
61 beacon_interval: None,
62 beacon_data: None,
63 interface_id: InterfaceId(0),
64 ax25_source: None,
65 interface_type_name: "KISSInterface".into(),
66 }
67 }
68}
69
70struct FlowState {
72 ready: bool,
73 queue: VecDeque<Vec<u8>>,
74 lock_time: Instant,
75}
76
77struct KissWriter {
80 file: std::fs::File,
81 flow_control: bool,
82 flow_state: Arc<Mutex<FlowState>>,
83 ax25_source: Option<Ax25Address>,
84}
85
86impl Writer for KissWriter {
87 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
88 let data = self
89 .ax25_source
90 .as_ref()
91 .map(|source| super::ax25_kiss::encode_ui_frame(source, data))
92 .unwrap_or_else(|| data.to_vec());
93 if self.flow_control {
94 let mut state = lock_or_recover(&self.flow_state, "kiss flow state");
95 if state.ready {
96 state.ready = false;
97 state.lock_time = Instant::now();
98 drop(state);
99 self.file.write_all(&kiss::frame(&data))
100 } else {
101 state.queue.push_back(data);
102 Ok(())
103 }
104 } else {
105 self.file.write_all(&kiss::frame(&data))
106 }
107 }
108}
109
110pub fn start(config: KissIfaceConfig, tx: EventSender) -> io::Result<Box<dyn Writer>> {
112 let serial_config = SerialConfig {
113 path: config.port.clone(),
114 baud: config.speed,
115 data_bits: config.data_bits,
116 parity: config.parity,
117 stop_bits: config.stop_bits,
118 };
119
120 let port = SerialPort::open(&serial_config)?;
121 let reader_file = port.reader()?;
122 let mut writer_file = port.writer()?;
123 let flow_writer_file = port.writer()?;
124
125 let id = config.interface_id;
126
127 thread::sleep(Duration::from_secs(2));
129
130 let _ = tx.send(Event::InterfaceUp(id, None, None));
132
133 configure_tnc(&mut writer_file, &config)?;
135
136 let flow_state = Arc::new(Mutex::new(FlowState {
137 ready: true,
138 queue: VecDeque::new(),
139 lock_time: Instant::now(),
140 }));
141
142 let reader_flow_state = flow_state.clone();
143
144 let reader_config = config.clone();
146 thread::Builder::new()
147 .name(format!("kiss-reader-{}", id.0))
148 .spawn(move || {
149 reader_loop(
150 reader_file,
151 flow_writer_file,
152 id,
153 reader_config,
154 tx,
155 reader_flow_state,
156 );
157 })?;
158
159 Ok(Box::new(KissWriter {
160 file: writer_file,
161 flow_control: config.flow_control,
162 flow_state,
163 ax25_source: config.ax25_source.clone(),
164 }))
165}
166
167fn configure_tnc(writer: &mut std::fs::File, config: &KissIfaceConfig) -> io::Result<()> {
170 log::info!("[{}] configuring KISS interface parameters", config.name);
171
172 let preamble_val = (config.preamble / 10).min(255) as u8;
174 writer.write_all(&kiss::command_frame(kiss::CMD_TXDELAY, &[preamble_val]))?;
175
176 let txtail_val = (config.txtail / 10).min(255) as u8;
178 writer.write_all(&kiss::command_frame(kiss::CMD_TXTAIL, &[txtail_val]))?;
179
180 writer.write_all(&kiss::command_frame(kiss::CMD_P, &[config.persistence]))?;
182
183 let slottime_val = (config.slottime / 10).min(255) as u8;
185 writer.write_all(&kiss::command_frame(kiss::CMD_SLOTTIME, &[slottime_val]))?;
186
187 writer.write_all(&kiss::command_frame(kiss::CMD_READY, &[0x01]))?;
189
190 log::info!("[{}] KISS interface configured", config.name);
191 Ok(())
192}
193
194fn reader_loop(
197 mut reader: std::fs::File,
198 mut flow_writer: std::fs::File,
199 id: InterfaceId,
200 config: KissIfaceConfig,
201 tx: EventSender,
202 flow_state: Arc<Mutex<FlowState>>,
203) {
204 let mut decoder = kiss::Decoder::new();
205 let mut buf = [0u8; 4096];
206 let mut first_tx: Option<Instant> = None;
207
208 loop {
209 match reader.read(&mut buf) {
210 Ok(0) => {
211 log::warn!("[{}] KISS port closed", config.name);
212 let _ = tx.send(Event::InterfaceDown(id));
213 match reconnect(&config, &tx, &flow_state) {
214 Some((new_reader, new_flow_writer)) => {
215 reader = new_reader;
216 flow_writer = new_flow_writer;
217 decoder = kiss::Decoder::new();
218 continue;
219 }
220 None => return,
221 }
222 }
223 Ok(n) => {
224 for event in decoder.feed(&buf[..n]) {
225 match event {
226 kiss::KissEvent::DataFrame(data) => {
227 let data = if config.ax25_source.is_some() {
228 match super::ax25_kiss::decode_ui_frame(&data) {
229 Some(data) => data.to_vec(),
230 None => continue,
231 }
232 } else {
233 data
234 };
235 if tx
236 .send(Event::Frame {
237 interface_id: id,
238 data,
239 rssi: None,
240 snr: None,
241 })
242 .is_err()
243 {
244 return;
245 }
246 }
247 kiss::KissEvent::Ready => {
248 process_queue(&flow_state, &mut flow_writer, &mut first_tx, &config);
249 }
250 }
251 }
252 }
253 Err(e) => {
254 log::warn!("[{}] KISS read error: {}", config.name, e);
255 let _ = tx.send(Event::InterfaceDown(id));
256 match reconnect(&config, &tx, &flow_state) {
257 Some((new_reader, new_flow_writer)) => {
258 reader = new_reader;
259 flow_writer = new_flow_writer;
260 decoder = kiss::Decoder::new();
261 continue;
262 }
263 None => return,
264 }
265 }
266 }
267
268 if config.flow_control {
270 let state = lock_or_recover(&flow_state, "kiss flow state");
271 if !state.ready && state.lock_time.elapsed() > Duration::from_secs(5) {
272 drop(state);
273 log::warn!("[{}] unlocking flow control due to timeout", config.name);
274 process_queue(&flow_state, &mut flow_writer, &mut first_tx, &config);
275 }
276 }
277
278 if let (Some(interval), Some(ref beacon_data)) =
280 (config.beacon_interval, &config.beacon_data)
281 {
282 if let Some(first) = first_tx {
283 if first.elapsed() > Duration::from_secs(interval as u64) {
284 log::debug!("[{}] transmitting beacon data", config.name);
285 let mut frame = beacon_data.clone();
287 while frame.len() < 15 {
288 frame.push(0x00);
289 }
290 let _ = flow_writer.write_all(&kiss::frame(&frame));
291 first_tx = None;
292 }
293 }
294 }
295 }
296}
297
298fn process_queue(
300 flow_state: &Arc<Mutex<FlowState>>,
301 writer: &mut std::fs::File,
302 first_tx: &mut Option<Instant>,
303 _config: &KissIfaceConfig,
304) {
305 let mut state = lock_or_recover(flow_state, "kiss flow state");
306 if let Some(data) = state.queue.pop_front() {
307 state.ready = false;
308 state.lock_time = Instant::now();
309 drop(state);
310 let _ = writer.write_all(&kiss::frame(&data));
311 if first_tx.is_none() {
312 *first_tx = Some(Instant::now());
313 }
314 } else {
315 state.ready = true;
316 }
317}
318
319fn reconnect(
321 config: &KissIfaceConfig,
322 tx: &EventSender,
323 flow_state: &Arc<Mutex<FlowState>>,
324) -> Option<(std::fs::File, std::fs::File)> {
325 loop {
326 thread::sleep(Duration::from_secs(5));
327 log::info!(
328 "[{}] attempting to reconnect KISS port {}...",
329 config.name,
330 config.port
331 );
332
333 let serial_config = SerialConfig {
334 path: config.port.clone(),
335 baud: config.speed,
336 data_bits: config.data_bits,
337 parity: config.parity,
338 stop_bits: config.stop_bits,
339 };
340
341 match SerialPort::open(&serial_config) {
342 Ok(port) => {
343 match (port.reader(), port.writer(), port.writer()) {
344 (Ok(reader), Ok(mut cfg_writer), Ok(flow_writer)) => {
345 thread::sleep(Duration::from_secs(2));
347 if let Err(e) = configure_tnc(&mut cfg_writer, config) {
348 log::warn!("[{}] TNC config failed: {}", config.name, e);
349 continue;
350 }
351 let mut state = lock_or_recover(flow_state, "kiss flow state");
353 state.ready = true;
354 state.queue.clear();
355 drop(state);
356
357 let new_writer: Box<dyn Writer> = Box::new(KissWriter {
358 file: cfg_writer,
359 flow_control: config.flow_control,
360 flow_state: flow_state.clone(),
361 ax25_source: config.ax25_source.clone(),
362 });
363 let _ = tx.send(Event::InterfaceUp(
364 config.interface_id,
365 Some(new_writer),
366 None,
367 ));
368 log::info!("[{}] KISS port reconnected", config.name);
369 return Some((reader, flow_writer));
370 }
371 _ => {
372 log::warn!("[{}] failed to get handles from serial port", config.name);
373 }
374 }
375 }
376 Err(e) => {
377 log::warn!("[{}] KISS reconnect failed: {}", config.name, e);
378 }
379 }
380 }
381}
382
383use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult};
386use rns_core::transport::types::InterfaceInfo;
387use std::collections::HashMap;
388
389pub struct KissFactory;
391
392impl InterfaceFactory for KissFactory {
393 fn type_name(&self) -> &str {
394 "KISSInterface"
395 }
396
397 fn default_ifac_size(&self) -> usize {
398 8
399 }
400
401 fn parse_config(
402 &self,
403 name: &str,
404 id: InterfaceId,
405 params: &HashMap<String, String>,
406 ) -> Result<Box<dyn InterfaceConfigData>, String> {
407 let port = params
408 .get("port")
409 .cloned()
410 .ok_or_else(|| "KISSInterface requires 'port'".to_string())?;
411
412 let speed = params
413 .get("speed")
414 .and_then(|v| v.parse().ok())
415 .unwrap_or(9600u32);
416
417 let data_bits = params
418 .get("databits")
419 .and_then(|v| v.parse().ok())
420 .unwrap_or(8u8);
421
422 let parity = params
423 .get("parity")
424 .map(|v| match v.to_lowercase().as_str() {
425 "e" | "even" => crate::serial::Parity::Even,
426 "o" | "odd" => crate::serial::Parity::Odd,
427 _ => crate::serial::Parity::None,
428 })
429 .unwrap_or(crate::serial::Parity::None);
430
431 let stop_bits = params
432 .get("stopbits")
433 .and_then(|v| v.parse().ok())
434 .unwrap_or(1u8);
435
436 let preamble = params
437 .get("preamble")
438 .and_then(|v| v.parse().ok())
439 .unwrap_or(350u16);
440
441 let txtail = params
442 .get("txtail")
443 .and_then(|v| v.parse().ok())
444 .unwrap_or(20u16);
445
446 let persistence = params
447 .get("persistence")
448 .and_then(|v| v.parse().ok())
449 .unwrap_or(64u8);
450
451 let slottime = params
452 .get("slottime")
453 .and_then(|v| v.parse().ok())
454 .unwrap_or(20u16);
455
456 let flow_control = params
457 .get("flow_control")
458 .and_then(|v| crate::config::parse_bool_pub(v))
459 .unwrap_or(false);
460
461 let beacon_interval = params
462 .get("id_interval")
463 .or_else(|| params.get("beacon_interval"))
464 .and_then(|v| v.parse().ok());
465
466 let beacon_data = params
467 .get("id_callsign")
468 .or_else(|| params.get("beacon_data"))
469 .map(|v| v.as_bytes().to_vec());
470
471 Ok(Box::new(KissIfaceConfig {
472 name: name.to_string(),
473 port,
474 speed,
475 data_bits,
476 parity,
477 stop_bits,
478 preamble,
479 txtail,
480 persistence,
481 slottime,
482 flow_control,
483 beacon_interval,
484 beacon_data,
485 interface_id: id,
486 ax25_source: None,
487 interface_type_name: "KISSInterface".into(),
488 }))
489 }
490
491 fn start(
492 &self,
493 config: Box<dyn InterfaceConfigData>,
494 ctx: StartContext,
495 ) -> std::io::Result<StartResult> {
496 let kiss_config = *config
497 .into_any()
498 .downcast::<KissIfaceConfig>()
499 .map_err(|_| {
500 std::io::Error::new(std::io::ErrorKind::InvalidData, "wrong config type")
501 })?;
502
503 let id = kiss_config.interface_id;
504 let name = kiss_config.name.clone();
505 let interface_type_name = kiss_config.interface_type_name.clone();
506
507 let info = InterfaceInfo {
508 id,
509 name,
510 mode: ctx.mode,
511 gravity: ctx.gravity,
512 recursive_prs: ctx.recursive_prs,
513 announces_from_internal: ctx.announces_from_internal,
514 announces_to_internal: ctx.announces_to_internal,
515 out_capable: true,
516 in_capable: true,
517 bitrate: Some(1200),
518 airtime_profile: None,
519 announce_rate_target: None,
520 announce_rate_grace: 0,
521 announce_rate_penalty: 0.0,
522 announce_cap: rns_core::constants::ANNOUNCE_CAP,
523 is_local_client: false,
524 wants_tunnel: false,
525 tunnel_id: None,
526 mtu: rns_core::constants::MTU as u32,
527 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
528 ia_freq: 0.0,
529 ip_freq: 0.0,
530 op_freq: 0.0,
531 op_samples: 0,
532 started: crate::time::now(),
533 };
534
535 let writer = start(kiss_config, ctx.tx)?;
536
537 Ok(StartResult::Simple {
538 id,
539 info,
540 writer,
541 interface_type_name,
542 })
543 }
544}
545
546#[cfg(test)]
547mod tests {
548 use super::*;
549 use crate::serial::open_pty_pair;
550 use std::os::unix::io::{AsRawFd, FromRawFd};
551 use std::sync::mpsc;
552
553 fn poll_read(fd: i32, timeout_ms: i32) -> bool {
555 let mut pfd = libc::pollfd {
556 fd,
557 events: libc::POLLIN,
558 revents: 0,
559 };
560 let ret = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
561 ret > 0
562 }
563
564 #[test]
565 fn kiss_data_roundtrip() {
566 let (master_fd, slave_fd) = open_pty_pair().unwrap();
567 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
568 let mut slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
569
570 let payload = vec![0x01, 0x02, 0x03, 0x04, 0x05];
572 let framed = kiss::frame(&payload);
573 master_file.write_all(&framed).unwrap();
574 master_file.flush().unwrap();
575
576 assert!(poll_read(slave_file.as_raw_fd(), 2000));
578
579 let mut decoder = kiss::Decoder::new();
580 let mut buf = [0u8; 4096];
581 let n = slave_file.read(&mut buf).unwrap();
582 let events = decoder.feed(&buf[..n]);
583 assert_eq!(events.len(), 1);
584 assert_eq!(events[0], kiss::KissEvent::DataFrame(payload));
585 }
586
587 #[test]
588 fn kiss_writer_frames() {
589 let (master_fd, slave_fd) = open_pty_pair().unwrap();
590
591 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
592 let flow_state = Arc::new(Mutex::new(FlowState {
593 ready: true,
594 queue: VecDeque::new(),
595 lock_time: Instant::now(),
596 }));
597
598 let mut writer = KissWriter {
599 file: writer_file,
600 flow_control: false,
601 flow_state,
602 ax25_source: None,
603 };
604
605 let payload = vec![0xC0, 0xDB, 0x01]; writer.send_frame(&payload).unwrap();
607
608 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
610 assert!(poll_read(master_file.as_raw_fd(), 2000));
611
612 let expected = kiss::frame(&payload);
613 let mut buf = [0u8; 256];
614 let n = master_file.read(&mut buf).unwrap();
615 assert_eq!(&buf[..n], &expected[..]);
616 }
617
618 #[test]
619 fn kiss_config_commands() {
620 use std::time::Instant;
621
622 let (master_fd, slave_fd) = open_pty_pair().unwrap();
623
624 let mut writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
625 let config = KissIfaceConfig {
626 preamble: 350,
627 txtail: 20,
628 persistence: 64,
629 slottime: 20,
630 ..Default::default()
631 };
632
633 configure_tnc(&mut writer_file, &config).unwrap();
634
635 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
637 let deadline = Instant::now() + Duration::from_secs(2);
638 let mut data = Vec::new();
639 let mut buf = [0u8; 1024];
640 while Instant::now() < deadline {
641 let remaining_ms = deadline
642 .saturating_duration_since(Instant::now())
643 .as_millis()
644 .min(i32::MAX as u128) as i32;
645 if remaining_ms <= 0 || !poll_read(master_file.as_raw_fd(), remaining_ms) {
646 break;
647 }
648
649 let n = master_file.read(&mut buf).unwrap();
650 if n == 0 {
651 break;
652 }
653 data.extend_from_slice(&buf[..n]);
654
655 let have_all = data.windows(4).any(|w| {
656 w[0] == kiss::FEND && w[1] == kiss::CMD_TXDELAY && w[2] == 35 && w[3] == kiss::FEND
657 }) && data.windows(4).any(|w| {
658 w[0] == kiss::FEND && w[1] == kiss::CMD_TXTAIL && w[2] == 2 && w[3] == kiss::FEND
659 }) && data.windows(4).any(|w| {
660 w[0] == kiss::FEND && w[1] == kiss::CMD_P && w[2] == 64 && w[3] == kiss::FEND
661 }) && data.windows(4).any(|w| {
662 w[0] == kiss::FEND && w[1] == kiss::CMD_SLOTTIME && w[2] == 2 && w[3] == kiss::FEND
663 });
664 if have_all {
665 break;
666 }
667 }
668
669 assert!(
672 data.windows(4).any(|w| w[0] == kiss::FEND
673 && w[1] == kiss::CMD_TXDELAY
674 && w[2] == 35
675 && w[3] == kiss::FEND),
676 "should contain TXDELAY command"
677 );
678
679 assert!(
681 data.windows(4).any(|w| w[0] == kiss::FEND
682 && w[1] == kiss::CMD_TXTAIL
683 && w[2] == 2
684 && w[3] == kiss::FEND),
685 "should contain TXTAIL command"
686 );
687
688 assert!(
690 data.windows(4).any(|w| w[0] == kiss::FEND
691 && w[1] == kiss::CMD_P
692 && w[2] == 64
693 && w[3] == kiss::FEND),
694 "should contain P command"
695 );
696
697 assert!(
699 data.windows(4).any(|w| w[0] == kiss::FEND
700 && w[1] == kiss::CMD_SLOTTIME
701 && w[2] == 2
702 && w[3] == kiss::FEND),
703 "should contain SLOTTIME command"
704 );
705 }
706
707 #[test]
708 fn kiss_flow_control_lock() {
709 let flow_state = Arc::new(Mutex::new(FlowState {
710 ready: true,
711 queue: VecDeque::new(),
712 lock_time: Instant::now(),
713 }));
714
715 let (master_fd, slave_fd) = open_pty_pair().unwrap();
716 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
717
718 let mut writer = KissWriter {
719 file: writer_file,
720 flow_control: true,
721 flow_state: flow_state.clone(),
722 ax25_source: None,
723 };
724
725 writer.send_frame(b"hello").unwrap();
727 assert!(!flow_state.lock().unwrap().ready);
728
729 writer.send_frame(b"world").unwrap();
731 assert_eq!(flow_state.lock().unwrap().queue.len(), 1);
732
733 let mut flow_writer = unsafe { std::fs::File::from_raw_fd(libc::dup(master_fd)) };
735 let mut first_tx = None;
736 let config = KissIfaceConfig::default();
737 process_queue(&flow_state, &mut flow_writer, &mut first_tx, &config);
738
739 assert_eq!(flow_state.lock().unwrap().queue.len(), 0);
741 assert!(!flow_state.lock().unwrap().ready);
742
743 process_queue(&flow_state, &mut flow_writer, &mut first_tx, &config);
745 assert!(flow_state.lock().unwrap().ready);
746
747 unsafe { libc::close(master_fd) };
749 }
750
751 #[test]
752 fn kiss_flow_control_timeout() {
753 let flow_state = Arc::new(Mutex::new(FlowState {
754 ready: false,
755 queue: VecDeque::new(),
756 lock_time: Instant::now() - Duration::from_secs(6), }));
758
759 let state = flow_state.lock().unwrap();
761 assert!(!state.ready);
762 assert!(state.lock_time.elapsed() > Duration::from_secs(5));
763 }
764
765 #[test]
766 fn kiss_fragmented() {
767 let (master_fd, slave_fd) = open_pty_pair().unwrap();
768 let mut master_file = unsafe { std::fs::File::from_raw_fd(master_fd) };
769 let slave_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
770
771 let payload = vec![0x01, 0x02, 0x03, 0x04, 0x05];
772 let framed = kiss::frame(&payload);
773 let mid = framed.len() / 2;
774
775 let (tx, rx) = mpsc::channel::<kiss::KissEvent>();
777 let reader_thread = thread::spawn(move || {
778 let mut reader = slave_file;
779 let mut decoder = kiss::Decoder::new();
780 let mut buf = [0u8; 4096];
781
782 loop {
783 match reader.read(&mut buf) {
784 Ok(n) if n > 0 => {
785 for event in decoder.feed(&buf[..n]) {
786 let _ = tx.send(event.clone());
787 if matches!(event, kiss::KissEvent::DataFrame(_)) {
788 return;
789 }
790 }
791 }
792 _ => return,
793 }
794 }
795 });
796
797 master_file.write_all(&framed[..mid]).unwrap();
799 master_file.flush().unwrap();
800
801 thread::sleep(Duration::from_millis(50));
802
803 master_file.write_all(&framed[mid..]).unwrap();
805 master_file.flush().unwrap();
806
807 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
808 assert_eq!(event, kiss::KissEvent::DataFrame(payload));
809
810 let _ = reader_thread.join();
811 }
812}