From 1f6748dd3cbe8df84adbeb905424ab284f4208b4 Mon Sep 17 00:00:00 2001 From: Thomas Karpiniec Date: Thu, 2 Jan 2025 14:26:18 +1100 Subject: [PATCH] Handle incoming frames in TNC --- m17core/src/protocol.rs | 29 +++++++++- m17core/src/tnc.rs | 124 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 147 insertions(+), 6 deletions(-) diff --git a/m17core/src/protocol.rs b/m17core/src/protocol.rs index 375c3f0..82a5721 100755 --- a/m17core/src/protocol.rs +++ b/m17core/src/protocol.rs @@ -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 { diff --git a/m17core/src/tnc.rs b/m17core/src/tnc.rs index 3f9eb02..a847683 100644 --- a/m17core/src/tnc.rs +++ b/m17core/src/tnc.rs @@ -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, } -- 2.39.5