]> code.octet-stream.net Git - m17rt/commitdiff
Handle incoming frames in TNC
authorThomas Karpiniec <tom.karpiniec@outlook.com>
Thu, 2 Jan 2025 03:26:18 +0000 (14:26 +1100)
committerThomas Karpiniec <tom.karpiniec@outlook.com>
Thu, 2 Jan 2025 03:26:18 +0000 (14:26 +1100)
m17core/src/protocol.rs
m17core/src/tnc.rs

index 375c3f0f8533fd56cf9a439ddf9b1bee56f0e1e7..82a5721b543752cf8369f5d00554f429beaa2efb 100755 (executable)
@@ -29,7 +29,7 @@ pub enum EncryptionType {
 pub enum Frame {
     Lsf(LsfFrame),
     Stream(StreamFrame),
-    // Packet
+    Packet(PacketFrame),
     // BERT
 }
 
@@ -158,6 +158,33 @@ pub struct StreamFrame {
     pub stream_data: [u8; 16],
 }
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PacketFrame {
+    /// Application packet payload (chunk)
+    pub payload: [u8; 25],
+
+    /// Frame counter, which provides different information depending on whether this is the last frame or not.
+    pub counter: PacketFrameCounter,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PacketFrameCounter {
+    /// Any packet frame that comes after the LSF and is not the final frame.
+    Frame {
+        /// Which frame this is in the superframe, from 0 to 31 inclusive.
+        ///
+        /// If a 33rd frame exists (index 32), it will be a `FinalFrame` instead.
+        ///
+        /// All 25 bytes of of `payload` are filled and valid.
+        index: usize,
+    },
+    /// The final frame in the packet superframe.
+    FinalFrame {
+        /// The number of bytes in `payload` that are filled.
+        payload_len: usize,
+    },
+}
+
 pub struct LichCollection([Option<[u8; 5]>; 6]);
 
 impl LichCollection {
index 3f9eb0241473037d803325413cfa1db6f06ed77c..a847683a548d360f3a25f11a912e6e8c2a663018 100644 (file)
@@ -1,5 +1,5 @@
 use crate::kiss::{KissBuffer, KissFrame};
-use crate::protocol::{Frame, LichCollection, LsfFrame};
+use crate::protocol::{Frame, LichCollection, LsfFrame, Mode, PacketFrameCounter};
 
 /// Handles the KISS protocol and frame management for `SoftModulator` and `SoftDemodulator`.
 ///
@@ -26,7 +26,105 @@ impl SoftTnc {
     }
 
     /// Process an individual `Frame` that has been decoded by the modem.
-    pub fn handle_frame(&mut self, _frame: Frame) -> Result<(), SoftTncError> {
+    pub fn handle_frame(&mut self, frame: Frame) -> Result<(), SoftTncError> {
+        match frame {
+            Frame::Lsf(lsf) => {
+                // A new LSF implies a clean slate.
+                // If we were partway through decoding something else then we missed it.
+                match lsf.mode() {
+                    Mode::Packet => {
+                        self.state = State::RxPacket(RxPacketState {
+                            lsf,
+                            packet: [0u8; 825],
+                            count: 0,
+                        })
+                    }
+                    Mode::Stream => {
+                        self.state = State::RxStream(RxStreamState { lsf, index: 0 });
+                    }
+                }
+            }
+            Frame::Packet(packet) => {
+                match &mut self.state {
+                    State::RxPacket(ref mut rx) => {
+                        match packet.counter {
+                            PacketFrameCounter::Frame { index } => {
+                                if index == rx.count && index < 32 {
+                                    let start = 25 * index;
+                                    rx.packet[start..(start + 25)].copy_from_slice(&packet.payload);
+                                    rx.count += 1;
+                                } else {
+                                    // unexpected order - something has gone wrong
+                                    self.state = State::Idle;
+                                }
+                            }
+                            PacketFrameCounter::FinalFrame { payload_len } => {
+                                let start = 25 * rx.count;
+                                let end = start + payload_len;
+                                rx.packet[start..(start + payload_len)]
+                                    .copy_from_slice(&packet.payload);
+                                let kiss =
+                                    KissFrame::new_full_packet(&rx.lsf.0, &rx.packet[0..end])
+                                        .unwrap();
+                                self.kiss_to_host(kiss);
+                                self.state = State::Idle;
+                            }
+                        }
+                    }
+                    _ => {
+                        // Invalid transition
+                        self.state = State::Idle;
+                    }
+                }
+            }
+            Frame::Stream(stream) => {
+                match &mut self.state {
+                    State::RxStream(ref mut rx) => {
+                        // TODO: consider wraparound from 0x7fff
+                        if stream.frame_number < rx.index {
+                            let mut lich = LichCollection::new();
+                            lich.set_segment(stream.lich_idx, stream.lich_part);
+                            self.state = State::RxAcquiringStream(RxAcquiringStreamState { lich });
+                        } else {
+                            rx.index = stream.frame_number + 1;
+                            let kiss = KissFrame::new_stream_data(&stream.stream_data).unwrap();
+                            self.kiss_to_host(kiss);
+                            // TODO: handle LICH contents to track META content
+                            // TODO: end stream if LICH updates indicate non-META part has changed
+                            // (this implies a new station)
+                            if stream.end_of_stream {
+                                self.state = State::Idle;
+                            }
+                        }
+                    }
+                    State::RxAcquiringStream(ref mut rx) => {
+                        rx.lich.set_segment(stream.lich_idx, stream.lich_part);
+                        if let Some(maybe_lsf) = rx.lich.try_assemble() {
+                            let lsf = LsfFrame(maybe_lsf);
+                            // LICH can change mid-transmission so wait until the CRC is correct
+                            // to ensure (to high probability) we haven't done a "torn read"
+                            if lsf.crc() == 0 {
+                                let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap();
+                                self.kiss_to_host(kiss);
+                                // TODO: avoid discarding the first data payload here
+                                // need a queue depth of 2 for outgoing kiss
+                                self.state = State::RxStream(RxStreamState {
+                                    lsf,
+                                    index: stream.frame_number + 1,
+                                });
+                            }
+                        }
+                    }
+                    _ => {
+                        // If coming from another state, we have missed something.
+                        // Never mind, let's start tracking LICH.
+                        let mut lich = LichCollection::new();
+                        lich.set_segment(stream.lich_idx, stream.lich_part);
+                        self.state = State::RxAcquiringStream(RxAcquiringStreamState { lich })
+                    }
+                }
+            }
+        }
         Ok(())
     }
 
@@ -52,6 +150,9 @@ impl SoftTnc {
                 target_buf[0..n]
                     .copy_from_slice(&outgoing.kiss_frame.data[outgoing.sent..(outgoing.sent + n)]);
                 outgoing.sent += n;
+                if outgoing.sent == outgoing.kiss_frame.len {
+                    self.outgoing_kiss = None;
+                }
                 Ok(n)
             }
             None => Ok(0),
@@ -68,11 +169,19 @@ impl SoftTnc {
         }
         Ok(n)
     }
+
+    fn kiss_to_host(&mut self, kiss_frame: KissFrame) {
+        self.outgoing_kiss = Some(OutgoingKiss {
+            kiss_frame,
+            sent: 0,
+        });
+    }
 }
 
 #[derive(Debug)]
 pub enum SoftTncError {
     General(&'static str),
+    InvalidState,
 }
 
 struct OutgoingKiss {
@@ -103,14 +212,19 @@ struct RxAcquiringStreamState {
 struct RxStreamState {
     /// Track identifying information for this transmission so we can tell if it changes.
     lsf: LsfFrame,
+
+    /// Expected next frame number. Allowed to skip values on RX, but not go backwards.
+    index: u16,
 }
 
 struct RxPacketState {
+    /// Initial LSF
+    lsf: LsfFrame,
+
     /// Accumulation of packet data that we have received so far.
     packet: [u8; 825],
 
-    /// Number of frames we have received. If we are stably in the RxPacket state,
-    /// this will be between 1 and 32 inclusive. The first frame gets us into the
-    /// rx state, and the maximum 33rd frame must end the transmission and state.
+    /// Number of payload frames we have received. If we are stably in the RxPacket state,
+    /// this will be between 0 and 32 inclusive.
     count: usize,
 }