1use std::io::{self, Read, Write};
10use std::sync::{Arc, Mutex};
11use std::thread;
12use std::time::Duration;
13
14use rns_core::transport::types::InterfaceId;
15
16use crate::event::{Event, EventSender};
17use crate::interface::Writer;
18use crate::rnode_kiss;
19use crate::serial::{Parity, SerialConfig, SerialPort};
20
21pub const FREQ_MIN: u32 = 137_000_000;
23pub const FREQ_MAX: u32 = 3_000_000_000;
24pub const BW_MIN: u32 = 7_800;
25pub const BW_MAX: u32 = 1_625_000;
26pub const SF_MIN: u8 = 5;
27pub const SF_MAX: u8 = 12;
28pub const CR_MIN: u8 = 5;
29pub const CR_MAX: u8 = 8;
30pub const TXPOWER_MIN: i8 = 0;
31pub const TXPOWER_MAX: i8 = 37;
32pub const HW_MTU: u32 = 508;
33
34#[derive(Debug, Clone)]
36pub struct RNodeSubConfig {
37 pub name: String,
38 pub frequency: u32,
39 pub bandwidth: u32,
40 pub txpower: i8,
41 pub spreading_factor: u8,
42 pub coding_rate: u8,
43 pub flow_control: bool,
44 pub st_alock: Option<f32>,
45 pub lt_alock: Option<f32>,
46}
47
48#[derive(Debug, Clone)]
50pub struct RNodeConfig {
51 pub name: String,
52 pub port: String,
53 pub speed: u32,
54 pub subinterfaces: Vec<RNodeSubConfig>,
55 pub id_interval: Option<u32>,
56 pub id_callsign: Option<Vec<u8>>,
57 pub base_interface_id: InterfaceId,
58 pub pre_opened_fd: Option<i32>,
61 pub runtime: Arc<Mutex<RNodeRuntime>>,
62}
63
64#[derive(Debug, Clone)]
65pub struct RNodeRuntime {
66 pub sub: RNodeSubConfig,
67 pub writer: Option<Arc<Mutex<std::fs::File>>>,
68}
69
70impl RNodeRuntime {
71 pub fn from_config(config: &RNodeConfig) -> Self {
72 Self {
73 sub: config
74 .subinterfaces
75 .first()
76 .cloned()
77 .unwrap_or_else(|| RNodeSubConfig {
78 name: config.name.clone(),
79 frequency: 868_000_000,
80 bandwidth: 125_000,
81 txpower: 7,
82 spreading_factor: 8,
83 coding_rate: 5,
84 flow_control: false,
85 st_alock: None,
86 lt_alock: None,
87 }),
88 writer: None,
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
94pub struct RNodeRuntimeConfigHandle {
95 pub interface_name: String,
96 pub runtime: Arc<Mutex<RNodeRuntime>>,
97 pub startup: RNodeRuntime,
98}
99
100impl Default for RNodeConfig {
101 fn default() -> Self {
102 let mut config = RNodeConfig {
103 name: String::new(),
104 port: String::new(),
105 speed: 115200,
106 subinterfaces: Vec::new(),
107 id_interval: None,
108 id_callsign: None,
109 base_interface_id: InterfaceId(0),
110 pre_opened_fd: None,
111 runtime: Arc::new(Mutex::new(RNodeRuntime {
112 sub: RNodeSubConfig {
113 name: String::new(),
114 frequency: 868_000_000,
115 bandwidth: 125_000,
116 txpower: 7,
117 spreading_factor: 8,
118 coding_rate: 5,
119 flow_control: false,
120 st_alock: None,
121 lt_alock: None,
122 },
123 writer: None,
124 })),
125 };
126 let startup = RNodeRuntime::from_config(&config);
127 config.runtime = Arc::new(Mutex::new(startup));
128 config
129 }
130}
131
132pub fn validate_sub_config(sub: &RNodeSubConfig) -> Option<String> {
134 if sub.frequency < FREQ_MIN || sub.frequency > FREQ_MAX {
135 return Some(format!(
136 "Invalid frequency {} for {}",
137 sub.frequency, sub.name
138 ));
139 }
140 if sub.bandwidth < BW_MIN || sub.bandwidth > BW_MAX {
141 return Some(format!(
142 "Invalid bandwidth {} for {}",
143 sub.bandwidth, sub.name
144 ));
145 }
146 if sub.spreading_factor < SF_MIN || sub.spreading_factor > SF_MAX {
147 return Some(format!(
148 "Invalid SF {} for {}",
149 sub.spreading_factor, sub.name
150 ));
151 }
152 if sub.coding_rate < CR_MIN || sub.coding_rate > CR_MAX {
153 return Some(format!("Invalid CR {} for {}", sub.coding_rate, sub.name));
154 }
155 if sub.txpower < TXPOWER_MIN || sub.txpower > TXPOWER_MAX {
156 return Some(format!("Invalid TX power {} for {}", sub.txpower, sub.name));
157 }
158 if let Some(st) = sub.st_alock {
159 if st < 0.0 || st > 100.0 {
160 return Some(format!("Invalid ST airtime limit {} for {}", st, sub.name));
161 }
162 }
163 if let Some(lt) = sub.lt_alock {
164 if lt < 0.0 || lt > 100.0 {
165 return Some(format!("Invalid LT airtime limit {} for {}", lt, sub.name));
166 }
167 }
168 None
169}
170
171struct RNodeSubWriter {
174 writer: Arc<Mutex<std::fs::File>>,
175 index: u8,
176 flow_control: bool,
177 flow_state: Arc<Mutex<SubFlowState>>,
178}
179
180struct SubFlowState {
181 ready: bool,
182 queue: std::collections::VecDeque<Vec<u8>>,
183}
184
185impl Writer for RNodeSubWriter {
186 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
187 let frame = rnode_kiss::rnode_data_frame(self.index, data);
188 if self.flow_control {
189 let mut state = self.flow_state.lock().unwrap();
190 if state.ready {
191 state.ready = false;
192 drop(state);
193 self.writer.lock().unwrap().write_all(&frame)
194 } else {
195 state.queue.push_back(data.to_vec());
196 Ok(())
197 }
198 } else {
199 self.writer.lock().unwrap().write_all(&frame)
200 }
201 }
202}
203
204pub fn start(
211 config: RNodeConfig,
212 tx: EventSender,
213) -> io::Result<Vec<(InterfaceId, Box<dyn Writer>)>> {
214 for sub in &config.subinterfaces {
216 if let Some(err) = validate_sub_config(sub) {
217 return Err(io::Error::new(io::ErrorKind::InvalidInput, err));
218 }
219 }
220
221 if config.subinterfaces.is_empty() {
222 return Err(io::Error::new(
223 io::ErrorKind::InvalidInput,
224 "No subinterfaces configured",
225 ));
226 }
227
228 let (reader_file, shared_writer) = if let Some(fd) = config.pre_opened_fd {
229 let port = SerialPort::from_raw_fd(fd);
231 let r = port.reader()?;
232 let w = port.writer()?;
233 std::mem::forget(port); (r, Arc::new(Mutex::new(w)))
235 } else {
236 let serial_config = SerialConfig {
237 path: config.port.clone(),
238 baud: config.speed,
239 data_bits: 8,
240 parity: Parity::None,
241 stop_bits: 1,
242 };
243 let port = SerialPort::open(&serial_config)?;
244 let r = port.reader()?;
245 let w = port.writer()?;
246 (r, Arc::new(Mutex::new(w)))
247 };
248
249 let num_subs = config.subinterfaces.len();
251 let mut writers: Vec<(InterfaceId, Box<dyn Writer>)> = Vec::with_capacity(num_subs);
252 let mut flow_states: Vec<Arc<Mutex<SubFlowState>>> = Vec::with_capacity(num_subs);
253
254 for (i, sub) in config.subinterfaces.iter().enumerate() {
255 let sub_id = InterfaceId(config.base_interface_id.0 + i as u64);
256 let flow_state = Arc::new(Mutex::new(SubFlowState {
257 ready: true,
258 queue: std::collections::VecDeque::new(),
259 }));
260 flow_states.push(flow_state.clone());
261
262 let sub_writer: Box<dyn Writer> = Box::new(RNodeSubWriter {
263 writer: shared_writer.clone(),
264 index: i as u8,
265 flow_control: sub.flow_control,
266 flow_state,
267 });
268 writers.push((sub_id, sub_writer));
269 }
270
271 let reader_shared_writer = shared_writer.clone();
273 {
274 let mut runtime = config.runtime.lock().unwrap();
275 runtime.writer = Some(shared_writer.clone());
276 runtime.sub = config
277 .subinterfaces
278 .first()
279 .cloned()
280 .unwrap_or(runtime.sub.clone());
281 }
282 let reader_config = config.clone();
283 let reader_flow_states = flow_states;
284 thread::Builder::new()
285 .name(format!("rnode-reader-{}", config.base_interface_id.0))
286 .spawn(move || {
287 reader_loop(
288 reader_file,
289 reader_shared_writer,
290 reader_config,
291 tx,
292 reader_flow_states,
293 );
294 })?;
295
296 let keepalive_writer = shared_writer.clone();
299 let keepalive_name = config.name.clone();
300 thread::Builder::new()
301 .name(format!("rnode-keepalive-{}", config.base_interface_id.0))
302 .spawn(move || {
303 let detect = rnode_kiss::detect_request();
304 loop {
305 thread::sleep(Duration::from_secs(15));
306 if let Err(e) = keepalive_writer.lock().unwrap().write_all(&detect) {
307 log::debug!("[{}] keepalive write failed: {}", keepalive_name, e);
308 return;
309 }
310 }
311 })?;
312
313 Ok(writers)
314}
315
316fn reader_loop(
318 mut reader: std::fs::File,
319 writer: Arc<Mutex<std::fs::File>>,
320 config: RNodeConfig,
321 tx: EventSender,
322 flow_states: Vec<Arc<Mutex<SubFlowState>>>,
323) {
324 thread::sleep(Duration::from_secs(2));
326
327 {
329 let detect_cmd = rnode_kiss::detect_request();
330 let mut cmd = detect_cmd;
332 cmd.extend_from_slice(&rnode_kiss::rnode_command(
333 rnode_kiss::CMD_FW_VERSION,
334 &[0x00],
335 ));
336 cmd.extend_from_slice(&rnode_kiss::rnode_command(
337 rnode_kiss::CMD_FW_DETAIL,
338 &[0x00],
339 ));
340 cmd.extend_from_slice(&rnode_kiss::rnode_command(
341 rnode_kiss::CMD_PLATFORM,
342 &[0x00],
343 ));
344 cmd.extend_from_slice(&rnode_kiss::rnode_command(rnode_kiss::CMD_MCU, &[0x00]));
345
346 if let Err(e) = writer.lock().unwrap().write_all(&cmd) {
347 log::error!("[{}] failed to send detect: {}", config.name, e);
348 return;
349 }
350 }
351
352 let mut decoder = rnode_kiss::RNodeDecoder::new();
353 let mut buf = [0u8; 4096];
354 let mut detected = false;
355
356 let detect_start = std::time::Instant::now();
358 let detect_timeout = Duration::from_secs(5);
359
360 while !detected && detect_start.elapsed() < detect_timeout {
361 match reader.read(&mut buf) {
362 Ok(0) => {
363 log::warn!("[{}] serial port closed during detect", config.name);
364 return;
365 }
366 Ok(n) => {
367 for event in decoder.feed(&buf[..n]) {
368 match event {
369 rnode_kiss::RNodeEvent::Detected(true) => {
370 detected = true;
371 log::info!("[{}] RNode device detected", config.name);
372 }
373 rnode_kiss::RNodeEvent::FirmwareVersion { major, minor } => {
374 log::info!("[{}] firmware version {}.{}", config.name, major, minor);
375 }
376 rnode_kiss::RNodeEvent::FirmwareDetail(ref detail) => {
377 log::info!("[{}] firmware detail: {}", config.name, detail);
378 }
379 rnode_kiss::RNodeEvent::Platform(p) => {
380 log::info!("[{}] platform: 0x{:02X}", config.name, p);
381 }
382 rnode_kiss::RNodeEvent::Mcu(m) => {
383 log::info!("[{}] MCU: 0x{:02X}", config.name, m);
384 }
385 _ => {}
386 }
387 }
388 }
389 Err(e) => {
390 log::error!("[{}] serial read error during detect: {}", config.name, e);
391 return;
392 }
393 }
394 }
395
396 if !detected {
397 log::error!("[{}] RNode detection timed out", config.name);
398 return;
399 }
400
401 for (i, sub) in config.subinterfaces.iter().enumerate() {
403 if let Err(e) =
404 configure_subinterface(&writer, i as u8, sub, config.subinterfaces.len() > 1)
405 {
406 log::error!(
407 "[{}] failed to configure subinterface {}: {}",
408 config.name,
409 i,
410 e
411 );
412 return;
413 }
414 }
415
416 thread::sleep(Duration::from_millis(300));
418
419 for i in 0..config.subinterfaces.len() {
421 let sub_id = InterfaceId(config.base_interface_id.0 + i as u64);
422 let _ = tx.send(Event::InterfaceUp(sub_id, None, None));
423 }
424
425 log::info!(
426 "[{}] RNode configured with {} subinterface(s)",
427 config.name,
428 config.subinterfaces.len()
429 );
430
431 loop {
433 match reader.read(&mut buf) {
434 Ok(0) => {
435 log::warn!("[{}] serial port closed", config.name);
436 for i in 0..config.subinterfaces.len() {
437 let sub_id = InterfaceId(config.base_interface_id.0 + i as u64);
438 let _ = tx.send(Event::InterfaceDown(sub_id));
439 }
440 return;
442 }
443 Ok(n) => {
444 for event in decoder.feed(&buf[..n]) {
445 match event {
446 rnode_kiss::RNodeEvent::DataFrame { index, data } => {
447 let sub_id = InterfaceId(config.base_interface_id.0 + index as u64);
448 if tx
449 .send(Event::Frame {
450 interface_id: sub_id,
451 data,
452 })
453 .is_err()
454 {
455 return;
456 }
457 }
458 rnode_kiss::RNodeEvent::Ready => {
459 for (i, fs) in flow_states.iter().enumerate() {
461 if config.subinterfaces[i].flow_control {
462 process_flow_queue(fs, &writer, i as u8);
463 }
464 }
465 }
466 rnode_kiss::RNodeEvent::Error(code) => {
467 log::error!("[{}] RNode error: 0x{:02X}", config.name, code);
468 }
469 _ => {
470 }
472 }
473 }
474 }
475 Err(e) => {
476 log::error!("[{}] serial read error: {}", config.name, e);
477 for i in 0..config.subinterfaces.len() {
478 let sub_id = InterfaceId(config.base_interface_id.0 + i as u64);
479 let _ = tx.send(Event::InterfaceDown(sub_id));
480 }
481 return;
482 }
483 }
484 }
485}
486
487pub(crate) fn configure_subinterface(
489 writer: &Arc<Mutex<std::fs::File>>,
490 index: u8,
491 sub: &RNodeSubConfig,
492 multi: bool,
493) -> io::Result<()> {
494 let mut w = writer.lock().unwrap();
495
496 let freq_bytes = [
498 (sub.frequency >> 24) as u8,
499 (sub.frequency >> 16) as u8,
500 (sub.frequency >> 8) as u8,
501 (sub.frequency & 0xFF) as u8,
502 ];
503 let bw_bytes = [
504 (sub.bandwidth >> 24) as u8,
505 (sub.bandwidth >> 16) as u8,
506 (sub.bandwidth >> 8) as u8,
507 (sub.bandwidth & 0xFF) as u8,
508 ];
509 let txp = if sub.txpower < 0 {
510 (sub.txpower as i16 + 256) as u8
511 } else {
512 sub.txpower as u8
513 };
514
515 if multi {
516 w.write_all(&rnode_kiss::rnode_select_command(
517 index,
518 rnode_kiss::CMD_FREQUENCY,
519 &freq_bytes,
520 ))?;
521 w.write_all(&rnode_kiss::rnode_select_command(
522 index,
523 rnode_kiss::CMD_BANDWIDTH,
524 &bw_bytes,
525 ))?;
526 w.write_all(&rnode_kiss::rnode_select_command(
527 index,
528 rnode_kiss::CMD_TXPOWER,
529 &[txp],
530 ))?;
531 w.write_all(&rnode_kiss::rnode_select_command(
532 index,
533 rnode_kiss::CMD_SF,
534 &[sub.spreading_factor],
535 ))?;
536 w.write_all(&rnode_kiss::rnode_select_command(
537 index,
538 rnode_kiss::CMD_CR,
539 &[sub.coding_rate],
540 ))?;
541 } else {
542 w.write_all(&rnode_kiss::rnode_command(
543 rnode_kiss::CMD_FREQUENCY,
544 &freq_bytes,
545 ))?;
546 w.write_all(&rnode_kiss::rnode_command(
547 rnode_kiss::CMD_BANDWIDTH,
548 &bw_bytes,
549 ))?;
550 w.write_all(&rnode_kiss::rnode_command(rnode_kiss::CMD_TXPOWER, &[txp]))?;
551 w.write_all(&rnode_kiss::rnode_command(
552 rnode_kiss::CMD_SF,
553 &[sub.spreading_factor],
554 ))?;
555 w.write_all(&rnode_kiss::rnode_command(
556 rnode_kiss::CMD_CR,
557 &[sub.coding_rate],
558 ))?;
559 }
560
561 if let Some(st) = sub.st_alock {
563 let st_val = (st * 100.0) as u16;
564 let st_bytes = [(st_val >> 8) as u8, (st_val & 0xFF) as u8];
565 if multi {
566 w.write_all(&rnode_kiss::rnode_select_command(
567 index,
568 rnode_kiss::CMD_ST_ALOCK,
569 &st_bytes,
570 ))?;
571 } else {
572 w.write_all(&rnode_kiss::rnode_command(
573 rnode_kiss::CMD_ST_ALOCK,
574 &st_bytes,
575 ))?;
576 }
577 }
578 if let Some(lt) = sub.lt_alock {
579 let lt_val = (lt * 100.0) as u16;
580 let lt_bytes = [(lt_val >> 8) as u8, (lt_val & 0xFF) as u8];
581 if multi {
582 w.write_all(&rnode_kiss::rnode_select_command(
583 index,
584 rnode_kiss::CMD_LT_ALOCK,
585 <_bytes,
586 ))?;
587 } else {
588 w.write_all(&rnode_kiss::rnode_command(
589 rnode_kiss::CMD_LT_ALOCK,
590 <_bytes,
591 ))?;
592 }
593 }
594
595 if multi {
597 w.write_all(&rnode_kiss::rnode_select_command(
598 index,
599 rnode_kiss::CMD_RADIO_STATE,
600 &[rnode_kiss::RADIO_STATE_ON],
601 ))?;
602 } else {
603 w.write_all(&rnode_kiss::rnode_command(
604 rnode_kiss::CMD_RADIO_STATE,
605 &[rnode_kiss::RADIO_STATE_ON],
606 ))?;
607 }
608
609 Ok(())
610}
611
612fn process_flow_queue(
614 flow_state: &Arc<Mutex<SubFlowState>>,
615 writer: &Arc<Mutex<std::fs::File>>,
616 index: u8,
617) {
618 let mut state = flow_state.lock().unwrap();
619 if let Some(data) = state.queue.pop_front() {
620 state.ready = false;
621 drop(state);
622 let frame = rnode_kiss::rnode_data_frame(index, &data);
623 let _ = writer.lock().unwrap().write_all(&frame);
624 } else {
625 state.ready = true;
626 }
627}
628
629use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult, SubInterface};
632use rns_core::transport::types::InterfaceInfo;
633use std::collections::HashMap;
634
635pub struct RNodeFactory;
637
638impl InterfaceFactory for RNodeFactory {
639 fn type_name(&self) -> &str {
640 "RNodeInterface"
641 }
642
643 fn default_ifac_size(&self) -> usize {
644 8
645 }
646
647 fn parse_config(
648 &self,
649 name: &str,
650 id: InterfaceId,
651 params: &HashMap<String, String>,
652 ) -> Result<Box<dyn InterfaceConfigData>, String> {
653 let pre_opened_fd = params.get("fd").and_then(|v| v.parse::<i32>().ok());
654
655 let port = params
656 .get("port")
657 .cloned()
658 .or_else(|| pre_opened_fd.map(|_| "usb-bridge".to_string()))
659 .ok_or_else(|| "RNodeInterface requires 'port' or 'fd'".to_string())?;
660
661 let speed = params
662 .get("speed")
663 .and_then(|v| v.parse().ok())
664 .unwrap_or(115200u32);
665
666 let frequency = params
667 .get("frequency")
668 .and_then(|v| v.parse().ok())
669 .unwrap_or(868_000_000u32);
670
671 let bandwidth = params
672 .get("bandwidth")
673 .and_then(|v| v.parse().ok())
674 .unwrap_or(125_000u32);
675
676 let txpower = params
677 .get("txpower")
678 .and_then(|v| v.parse().ok())
679 .unwrap_or(7i8);
680
681 let spreading_factor = params
682 .get("spreadingfactor")
683 .or_else(|| params.get("spreading_factor"))
684 .and_then(|v| v.parse().ok())
685 .unwrap_or(8u8);
686
687 let coding_rate = params
688 .get("codingrate")
689 .or_else(|| params.get("coding_rate"))
690 .and_then(|v| v.parse().ok())
691 .unwrap_or(5u8);
692
693 let flow_control = params
694 .get("flow_control")
695 .and_then(|v| crate::config::parse_bool_pub(v))
696 .unwrap_or(false);
697
698 let st_alock = params.get("st_alock").and_then(|v| v.parse().ok());
699
700 let lt_alock = params.get("lt_alock").and_then(|v| v.parse().ok());
701
702 let id_interval = params.get("id_interval").and_then(|v| v.parse().ok());
703
704 let id_callsign = params.get("id_callsign").map(|v| v.as_bytes().to_vec());
705
706 let sub = RNodeSubConfig {
707 name: name.to_string(),
708 frequency,
709 bandwidth,
710 txpower,
711 spreading_factor,
712 coding_rate,
713 flow_control,
714 st_alock,
715 lt_alock,
716 };
717
718 Ok(Box::new(RNodeConfig {
719 name: name.to_string(),
720 port,
721 speed,
722 subinterfaces: vec![sub],
723 id_interval,
724 id_callsign,
725 base_interface_id: id,
726 pre_opened_fd,
727 runtime: Arc::new(Mutex::new(RNodeRuntime {
728 sub: RNodeSubConfig {
729 name: name.to_string(),
730 frequency,
731 bandwidth,
732 txpower,
733 spreading_factor,
734 coding_rate,
735 flow_control,
736 st_alock,
737 lt_alock,
738 },
739 writer: None,
740 })),
741 }))
742 }
743
744 fn start(
745 &self,
746 config: Box<dyn InterfaceConfigData>,
747 ctx: StartContext,
748 ) -> std::io::Result<StartResult> {
749 let rnode_config = *config.into_any().downcast::<RNodeConfig>().map_err(|_| {
750 std::io::Error::new(std::io::ErrorKind::InvalidData, "wrong config type")
751 })?;
752
753 let name = rnode_config.name.clone();
754
755 let pairs = start(rnode_config, ctx.tx)?;
756
757 let mut subs = Vec::with_capacity(pairs.len());
758 for (index, (sub_id, writer)) in pairs.into_iter().enumerate() {
759 let sub_name = if index == 0 {
760 name.clone()
761 } else {
762 format!("{}/{}", name, index)
763 };
764
765 let info = InterfaceInfo {
766 id: sub_id,
767 name: sub_name,
768 mode: ctx.mode,
769 out_capable: true,
770 in_capable: true,
771 bitrate: None,
772 announce_rate_target: None,
773 announce_rate_grace: 0,
774 announce_rate_penalty: 0.0,
775 announce_cap: rns_core::constants::ANNOUNCE_CAP,
776 is_local_client: false,
777 wants_tunnel: false,
778 tunnel_id: None,
779 mtu: rns_core::constants::MTU as u32,
780 ingress_control: false,
781 ia_freq: 0.0,
782 started: crate::time::now(),
783 };
784
785 subs.push(SubInterface {
786 id: sub_id,
787 info,
788 writer,
789 interface_type_name: "RNodeInterface".to_string(),
790 });
791 }
792
793 Ok(StartResult::Multi(subs))
794 }
795}
796
797pub(crate) fn rnode_runtime_handle_from_config(config: &RNodeConfig) -> RNodeRuntimeConfigHandle {
798 RNodeRuntimeConfigHandle {
799 interface_name: config.name.clone(),
800 runtime: Arc::clone(&config.runtime),
801 startup: RNodeRuntime::from_config(config),
802 }
803}
804
805#[cfg(test)]
806mod tests {
807 use super::*;
808 use crate::kiss;
809 use crate::serial::open_pty_pair;
810 use std::os::unix::io::{AsRawFd, FromRawFd};
811 fn poll_read(fd: i32, timeout_ms: i32) -> bool {
813 let mut pfd = libc::pollfd {
814 fd,
815 events: libc::POLLIN,
816 revents: 0,
817 };
818 let ret = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
819 ret > 0
820 }
821
822 fn read_available(file: &mut std::fs::File) -> Vec<u8> {
824 let mut all = Vec::new();
825 let mut buf = [0u8; 4096];
826 while poll_read(file.as_raw_fd(), 100) {
827 match file.read(&mut buf) {
828 Ok(n) if n > 0 => all.extend_from_slice(&buf[..n]),
829 _ => break,
830 }
831 }
832 all
833 }
834
835 fn mock_respond_detect(master: &mut std::fs::File) {
837 master
839 .write_all(&rnode_kiss::rnode_command(
840 rnode_kiss::CMD_DETECT,
841 &[rnode_kiss::DETECT_RESP],
842 ))
843 .unwrap();
844 master
846 .write_all(&rnode_kiss::rnode_command(
847 rnode_kiss::CMD_FW_VERSION,
848 &[0x01, 0x4A],
849 ))
850 .unwrap();
851 master
853 .write_all(&rnode_kiss::rnode_command(
854 rnode_kiss::CMD_PLATFORM,
855 &[0x80],
856 ))
857 .unwrap();
858 master
860 .write_all(&rnode_kiss::rnode_command(rnode_kiss::CMD_MCU, &[0x01]))
861 .unwrap();
862 master.flush().unwrap();
863 }
864
865 #[test]
866 fn rnode_detect_over_pty() {
867 let (master_fd, slave_fd) = open_pty_pair().unwrap();
869 let mut master = unsafe { std::fs::File::from_raw_fd(master_fd) };
870 let mut slave = unsafe { std::fs::File::from_raw_fd(slave_fd) };
871
872 mock_respond_detect(&mut master);
874
875 assert!(poll_read(slave.as_raw_fd(), 2000));
877 let mut decoder = rnode_kiss::RNodeDecoder::new();
878 let mut buf = [0u8; 4096];
879 let n = slave.read(&mut buf).unwrap();
880 let events = decoder.feed(&buf[..n]);
881
882 assert!(
883 events
884 .iter()
885 .any(|e| matches!(e, rnode_kiss::RNodeEvent::Detected(true))),
886 "should detect device"
887 );
888 assert!(
889 events
890 .iter()
891 .any(|e| matches!(e, rnode_kiss::RNodeEvent::FirmwareVersion { .. })),
892 "should get firmware version"
893 );
894 }
895
896 #[test]
897 fn rnode_configure_commands() {
898 let (master_fd, slave_fd) = open_pty_pair().unwrap();
899 let mut master = unsafe { std::fs::File::from_raw_fd(master_fd) };
900 let writer_file = unsafe { std::fs::File::from_raw_fd(libc::dup(slave_fd)) };
901 let writer = Arc::new(Mutex::new(writer_file));
902
903 let sub = RNodeSubConfig {
904 name: "test".into(),
905 frequency: 868_000_000,
906 bandwidth: 125_000,
907 txpower: 7,
908 spreading_factor: 8,
909 coding_rate: 5,
910 flow_control: false,
911 st_alock: None,
912 lt_alock: None,
913 };
914
915 configure_subinterface(&writer, 0, &sub, false).unwrap();
916
917 let data = read_available(&mut master);
919
920 assert!(
922 data.windows(2)
923 .any(|w| w[0] == kiss::FEND && w[1] == rnode_kiss::CMD_FREQUENCY),
924 "should contain FREQUENCY command"
925 );
926 assert!(
928 data.windows(2)
929 .any(|w| w[0] == kiss::FEND && w[1] == rnode_kiss::CMD_BANDWIDTH),
930 "should contain BANDWIDTH command"
931 );
932 assert!(
934 data.windows(3).any(|w| w[0] == kiss::FEND
935 && w[1] == rnode_kiss::CMD_RADIO_STATE
936 && w[2] == rnode_kiss::RADIO_STATE_ON),
937 "should contain RADIO_STATE ON command"
938 );
939
940 unsafe { libc::close(slave_fd) };
941 }
942
943 #[test]
944 fn rnode_data_roundtrip() {
945 let (master_fd, slave_fd) = open_pty_pair().unwrap();
946 let mut master = unsafe { std::fs::File::from_raw_fd(master_fd) };
947 let slave = unsafe { std::fs::File::from_raw_fd(slave_fd) };
948
949 let payload = vec![0x01, 0x02, 0x03, 0x04, 0x05];
951 let frame = rnode_kiss::rnode_data_frame(0, &payload);
952 master.write_all(&frame).unwrap();
953 master.flush().unwrap();
954
955 assert!(poll_read(slave.as_raw_fd(), 2000));
957 let mut decoder = rnode_kiss::RNodeDecoder::new();
958 let mut buf = [0u8; 4096];
959 let mut slave_file = slave;
960 let n = slave_file.read(&mut buf).unwrap();
961 let events = decoder.feed(&buf[..n]);
962
963 assert_eq!(events.len(), 1);
964 match &events[0] {
965 rnode_kiss::RNodeEvent::DataFrame { index, data } => {
966 assert_eq!(*index, 0);
967 assert_eq!(data, &payload);
968 }
969 other => panic!("expected DataFrame, got {:?}", other),
970 }
971 }
972
973 #[test]
974 fn rnode_flow_control() {
975 let (master_fd, slave_fd) = open_pty_pair().unwrap();
976 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
977 let shared_writer = Arc::new(Mutex::new(writer_file));
978
979 let flow_state = Arc::new(Mutex::new(SubFlowState {
980 ready: true,
981 queue: std::collections::VecDeque::new(),
982 }));
983
984 let mut sub_writer = RNodeSubWriter {
985 writer: shared_writer.clone(),
986 index: 0,
987 flow_control: true,
988 flow_state: flow_state.clone(),
989 };
990
991 sub_writer.send_frame(b"hello").unwrap();
993 assert!(!flow_state.lock().unwrap().ready);
994
995 sub_writer.send_frame(b"world").unwrap();
997 assert_eq!(flow_state.lock().unwrap().queue.len(), 1);
998
999 process_flow_queue(&flow_state, &shared_writer, 0);
1001 assert_eq!(flow_state.lock().unwrap().queue.len(), 0);
1002 assert!(!flow_state.lock().unwrap().ready); process_flow_queue(&flow_state, &shared_writer, 0);
1006 assert!(flow_state.lock().unwrap().ready);
1007
1008 unsafe { libc::close(master_fd) };
1009 }
1010
1011 #[test]
1012 fn rnode_sub_writer_format() {
1013 let (master_fd, slave_fd) = open_pty_pair().unwrap();
1014 let mut master = unsafe { std::fs::File::from_raw_fd(master_fd) };
1015 let writer_file = unsafe { std::fs::File::from_raw_fd(slave_fd) };
1016 let shared_writer = Arc::new(Mutex::new(writer_file));
1017
1018 let flow_state = Arc::new(Mutex::new(SubFlowState {
1019 ready: true,
1020 queue: std::collections::VecDeque::new(),
1021 }));
1022
1023 let mut sub_writer = RNodeSubWriter {
1024 writer: shared_writer,
1025 index: 1, flow_control: false,
1027 flow_state,
1028 };
1029
1030 let payload = vec![0xAA, 0xBB, 0xCC];
1031 sub_writer.send_frame(&payload).unwrap();
1032
1033 assert!(poll_read(master.as_raw_fd(), 2000));
1035 let mut buf = [0u8; 256];
1036 let n = master.read(&mut buf).unwrap();
1037
1038 assert_eq!(buf[0], kiss::FEND);
1040 assert_eq!(buf[1], 0x10); assert_eq!(buf[n - 1], kiss::FEND);
1042 }
1043
1044 #[test]
1045 fn rnode_multi_sub_routing() {
1046 let mut decoder = rnode_kiss::RNodeDecoder::new();
1048
1049 let payload0 = vec![0x01, 0x02];
1050 let frame0 = rnode_kiss::rnode_data_frame(0, &payload0);
1051 let events0 = decoder.feed(&frame0);
1052 assert_eq!(events0.len(), 1);
1053 assert_eq!(
1054 events0[0],
1055 rnode_kiss::RNodeEvent::DataFrame {
1056 index: 0,
1057 data: payload0
1058 }
1059 );
1060
1061 let payload1 = vec![0x03, 0x04];
1062 let frame1 = rnode_kiss::rnode_data_frame(1, &payload1);
1063 let events1 = decoder.feed(&frame1);
1064 assert_eq!(events1.len(), 1);
1065 assert_eq!(
1066 events1[0],
1067 rnode_kiss::RNodeEvent::DataFrame {
1068 index: 1,
1069 data: payload1
1070 }
1071 );
1072 }
1073
1074 #[test]
1075 fn rnode_error_handling() {
1076 let mut decoder = rnode_kiss::RNodeDecoder::new();
1084 let frame = rnode_kiss::rnode_command(rnode_kiss::CMD_ERROR, &[0x02]);
1085 let events = decoder.feed(&frame);
1086 assert_eq!(events.len(), 1);
1087 assert_eq!(
1089 events[0],
1090 rnode_kiss::RNodeEvent::DataFrame {
1091 index: 5,
1092 data: vec![0x02]
1093 }
1094 );
1095 }
1096
1097 #[test]
1098 fn rnode_config_validation() {
1099 let good = RNodeSubConfig {
1100 name: "test".into(),
1101 frequency: 868_000_000,
1102 bandwidth: 125_000,
1103 txpower: 7,
1104 spreading_factor: 8,
1105 coding_rate: 5,
1106 flow_control: false,
1107 st_alock: None,
1108 lt_alock: None,
1109 };
1110 assert!(validate_sub_config(&good).is_none());
1111
1112 let mut bad = good.clone();
1114 bad.frequency = 100;
1115 assert!(validate_sub_config(&bad).is_some());
1116
1117 bad = good.clone();
1119 bad.spreading_factor = 13;
1120 assert!(validate_sub_config(&bad).is_some());
1121
1122 bad = good.clone();
1124 bad.coding_rate = 9;
1125 assert!(validate_sub_config(&bad).is_some());
1126
1127 bad = good.clone();
1129 bad.bandwidth = 5;
1130 assert!(validate_sub_config(&bad).is_some());
1131
1132 bad = good.clone();
1134 bad.txpower = 50;
1135 assert!(validate_sub_config(&bad).is_some());
1136 }
1137}