]> code.octet-stream.net Git - m17rt/blob - m17core/src/tnc.rs
Start handling KISS frames in SoftTnc
[m17rt] / m17core / src / tnc.rs
1 use crate::address::{Address, Callsign};
2 use crate::kiss::{KissBuffer, KissFrame, PORT_PACKET_BASIC, PORT_PACKET_FULL, PORT_STREAM};
3 use crate::modem::ModulatorFrame;
4 use crate::protocol::{
5 Frame, LichCollection, LsfFrame, Mode, PacketFrame, PacketFrameCounter, StreamFrame,
6 };
7
8 /// Handles the KISS protocol and frame management for `SoftModulator` and `SoftDemodulator`.
9 ///
10 /// These components work alongside each other. User is responsible for chaining them together
11 /// or doing something else with the data.
12 pub struct SoftTnc {
13 /// Handle framing of KISS commands from the host, which may arrive in arbitrary binary blobs.
14 kiss_buffer: KissBuffer,
15
16 /// Kiss message that needs to be sent to the host.
17 outgoing_kiss: Option<OutgoingKiss>,
18
19 /// Current RX or TX function of the TNC.
20 state: State,
21
22 /// Latest state of data carrier detect from demodulator - controls whether we can go to TX
23 dcd: bool,
24
25 /// Current monotonic time, counted in samples
26 now: u64,
27
28 // TODO: use a static ring buffer crate of some sort?
29 /// Circular buffer of packets enqueued for transmission
30 packet_queue: [PendingPacket; 4],
31
32 /// Next slot to fill
33 packet_next: usize,
34
35 /// Current packet index, which is either partly transmitted or not transmitted at all.
36 packet_curr: usize,
37
38 /// If true, packet_next == packet_curr implies full queue. packet_next is invalid.
39 /// If false, it implies empty queue.
40 packet_full: bool,
41
42 /// The LSF for a stream we are going to start transmitting.
43 ///
44 /// This serves as a general indicator that we want to tx a stream.
45 stream_pending_lsf: Option<LsfFrame>,
46
47 /// Circular buffer of stream data enqueued for transmission.
48 ///
49 /// When the queue empties out, we hope that the last one has the end-of-stream flag set.
50 /// Otherwise a buffer underrun has occurred.
51 ///
52 /// Overruns are less troublesome - we can drop frames and receiving stations should cope.
53 stream_queue: [StreamFrame; 8],
54
55 /// Next slot to fill
56 stream_next: usize,
57
58 /// Current unsent stream frame index
59 stream_curr: usize,
60
61 /// True if stream_next == stream_curr because the queue is full. stream_next is invalid.
62 stream_full: bool,
63
64 /// Should PTT be on right now? Polled by external
65 ptt: bool,
66 }
67
68 impl SoftTnc {
69 pub fn new() -> Self {
70 Self {
71 kiss_buffer: KissBuffer::new(),
72 outgoing_kiss: None,
73 state: State::Idle,
74 dcd: false,
75 now: 0,
76 packet_queue: Default::default(),
77 packet_next: 0,
78 packet_curr: 0,
79 packet_full: false,
80 stream_pending_lsf: None,
81 stream_queue: Default::default(),
82 stream_next: 0,
83 stream_curr: 0,
84 stream_full: false,
85 ptt: false,
86 }
87 }
88
89 /// Process an individual `Frame` that has been decoded by the modem.
90 pub fn handle_frame(&mut self, frame: Frame) {
91 match frame {
92 Frame::Lsf(lsf) => {
93 // A new LSF implies a clean slate.
94 // If we were partway through decoding something else then we missed it.
95 match lsf.mode() {
96 Mode::Packet => {
97 self.state = State::RxPacket(RxPacketState {
98 lsf,
99 packet: [0u8; 825],
100 count: 0,
101 })
102 }
103 Mode::Stream => {
104 let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap();
105 self.kiss_to_host(kiss);
106 self.state = State::RxStream(RxStreamState { lsf, index: 0 });
107 }
108 }
109 }
110 Frame::Packet(packet) => {
111 match &mut self.state {
112 State::RxPacket(ref mut rx) => {
113 match packet.counter {
114 PacketFrameCounter::Frame { index } => {
115 if index == rx.count && index < 32 {
116 let start = 25 * index;
117 rx.packet[start..(start + 25)].copy_from_slice(&packet.payload);
118 rx.count += 1;
119 } else {
120 // unexpected order - something has gone wrong
121 self.state = State::Idle;
122 }
123 }
124 PacketFrameCounter::FinalFrame { payload_len } => {
125 let start = 25 * rx.count;
126 let end = start + payload_len;
127 rx.packet[start..(start + payload_len)]
128 .copy_from_slice(&packet.payload);
129 // TODO: compatible packets should be sent on port 0 too
130 let kiss =
131 KissFrame::new_full_packet(&rx.lsf.0, &rx.packet[0..end])
132 .unwrap();
133 self.kiss_to_host(kiss);
134 self.state = State::Idle;
135 }
136 }
137 }
138 _ => {
139 // Invalid transition
140 self.state = State::Idle;
141 }
142 }
143 }
144 Frame::Stream(stream) => {
145 match &mut self.state {
146 State::RxStream(ref mut rx) => {
147 // TODO: consider wraparound from 0x7fff
148 if stream.frame_number < rx.index {
149 let mut lich = LichCollection::new();
150 lich.set_segment(stream.lich_idx, stream.lich_part);
151 self.state = State::RxAcquiringStream(RxAcquiringStreamState { lich });
152 } else {
153 rx.index = stream.frame_number + 1;
154 let kiss = KissFrame::new_stream_data(&stream).unwrap();
155 self.kiss_to_host(kiss);
156 // TODO: end stream if LICH updates indicate non-META part has changed
157 // (this implies a new station)
158 if stream.end_of_stream {
159 self.state = State::Idle;
160 }
161 }
162 }
163 State::RxAcquiringStream(ref mut rx) => {
164 rx.lich.set_segment(stream.lich_idx, stream.lich_part);
165 if let Some(maybe_lsf) = rx.lich.try_assemble() {
166 let lsf = LsfFrame(maybe_lsf);
167 // LICH can change mid-transmission so wait until the CRC is correct
168 // to ensure (to high probability) we haven't done a "torn read"
169 if lsf.check_crc() == 0 {
170 let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap();
171 self.kiss_to_host(kiss);
172 // TODO: avoid discarding the first data payload here
173 // need a queue depth of 2 for outgoing kiss
174 self.state = State::RxStream(RxStreamState {
175 lsf,
176 index: stream.frame_number + 1,
177 });
178 }
179 }
180 }
181 _ => {
182 // If coming from another state, we have missed something.
183 // Never mind, let's start tracking LICH.
184 let mut lich = LichCollection::new();
185 lich.set_segment(stream.lich_idx, stream.lich_part);
186 self.state = State::RxAcquiringStream(RxAcquiringStreamState { lich })
187 }
188 }
189 }
190 }
191 }
192
193 pub fn set_data_carrier_detect(&mut self, dcd: bool) {
194 self.dcd = dcd;
195 }
196
197 pub fn set_now(&mut self, now_samples: u64) {
198 self.now = now_samples;
199 match self.state {
200 State::TxEndingAtTime(time) => {
201 if now_samples >= time {
202 self.ptt = false;
203 self.state = State::Idle;
204 }
205 }
206 _ => (),
207 }
208 }
209
210 pub fn ptt(&self) -> bool {
211 self.ptt
212 }
213
214 pub fn set_tx_end_time(&mut self, in_samples: usize) {
215 match self.state {
216 State::TxEnding => {
217 self.state = State::TxEndingAtTime(self.now + in_samples as u64);
218 }
219 _ => (),
220 }
221 }
222
223 pub fn read_tx_frame(&mut self) -> Option<ModulatorFrame> {
224 match self.state {
225 State::Idle | State::RxAcquiringStream(_) | State::RxStream(_) | State::RxPacket(_) => {
226 // We will let CSMA decide whether to actually go ahead.
227 // That's not implemented yet, so let's just check DCD.
228 let channel_free = !self.dcd;
229 let stream_wants_to_tx = self.stream_pending_lsf.is_some();
230 let packet_wants_to_tx = self.packet_full || (self.packet_next != self.packet_curr);
231 if channel_free && stream_wants_to_tx {
232 self.state = State::TxStream;
233 } else if channel_free && packet_wants_to_tx {
234 self.state = State::TxPacket;
235 } else {
236 return None;
237 }
238 self.ptt = true;
239 // TODO: true txdelay
240 Some(ModulatorFrame::Preamble { tx_delay: 0 })
241 }
242 State::TxStream => {
243 if !self.stream_full && self.stream_next == self.stream_curr {
244 return None;
245 }
246 if let Some(lsf) = self.stream_pending_lsf.take() {
247 return Some(ModulatorFrame::Lsf(lsf));
248 }
249 let frame = self.stream_queue[self.stream_curr].clone();
250 if self.stream_full {
251 self.stream_full = false;
252 }
253 self.stream_curr = (self.stream_curr + 1) % 8;
254 if frame.end_of_stream {
255 self.state = State::Idle;
256 }
257 Some(ModulatorFrame::Stream(frame))
258 }
259 State::TxStreamSentEndOfStream => {
260 self.state = State::TxEnding;
261 Some(ModulatorFrame::EndOfTransmission)
262 }
263 State::TxPacket => {
264 if !self.packet_full && self.packet_next == self.packet_curr {
265 return None;
266 }
267 while self.packet_next != self.packet_curr {
268 match self.packet_queue[self.packet_curr].next_frame() {
269 Some(frame) => {
270 return Some(frame);
271 }
272 None => {
273 self.packet_curr = (self.packet_curr + 1) % 4;
274 }
275 }
276 }
277 self.state = State::TxEnding;
278 Some(ModulatorFrame::EndOfTransmission)
279 }
280 State::TxEnding | State::TxEndingAtTime(_) => {
281 // Once we have signalled EOT we withold any new frames until
282 // the channel fully clears and we are ready to TX again
283 None
284 }
285 }
286 }
287
288 /// Read KISS message to be sent to host.
289 ///
290 /// After each frame input, this should be consumed in a loop until length 0 is returned.
291 /// This component will never block. Upstream interface can provide blocking `read()` if desired.
292 pub fn read_kiss(&mut self, target_buf: &mut [u8]) -> usize {
293 match self.outgoing_kiss.as_mut() {
294 Some(outgoing) => {
295 let n = (outgoing.kiss_frame.len - outgoing.sent).min(target_buf.len());
296 target_buf[0..n]
297 .copy_from_slice(&outgoing.kiss_frame.data[outgoing.sent..(outgoing.sent + n)]);
298 outgoing.sent += n;
299 if outgoing.sent == outgoing.kiss_frame.len {
300 self.outgoing_kiss = None;
301 }
302 n
303 }
304 None => 0,
305 }
306 }
307
308 /// Host sends in some KISS data.
309 pub fn write_kiss(&mut self, buf: &[u8]) -> usize {
310 let target_buf = self.kiss_buffer.buf_remaining();
311 let n = buf.len().min(target_buf.len());
312 target_buf[0..n].copy_from_slice(&buf[0..n]);
313 self.kiss_buffer.did_write(n);
314 while let Some(kiss_frame) = self.kiss_buffer.next_frame() {
315 let Ok(port) = kiss_frame.port() else {
316 continue;
317 };
318 if port == PORT_PACKET_BASIC {
319 if self.packet_full {
320 continue;
321 }
322 let mut pending = PendingPacket::new();
323 pending.app_data[0] = 0x00; // RAW
324 let Ok(mut len) = kiss_frame.decode_payload(&mut pending.app_data[1..]) else {
325 continue;
326 };
327 len += 1; // for RAW prefix
328 let packet_crc = crate::crc::m17_crc(&pending.app_data[0..len]);
329 pending.app_data[len..len + 2].copy_from_slice(&packet_crc.to_be_bytes());
330 pending.app_data_len = len + 2;
331 pending.lsf = Some(LsfFrame::new_packet(
332 &Address::Callsign(Callsign(b"M17RT-PKT".clone())),
333 &Address::Broadcast,
334 ));
335 self.packet_queue[self.packet_next] = pending;
336 self.packet_next = (self.packet_next + 1) % 4;
337 if self.packet_next == self.packet_curr {
338 self.packet_full = true;
339 }
340 } else if port == PORT_PACKET_FULL {
341 if self.packet_full {
342 continue;
343 }
344 let mut pending = PendingPacket::new();
345 let mut payload = [0u8; 855];
346 let Ok(len) = kiss_frame.decode_payload(&mut payload) else {
347 continue;
348 };
349 if len < 33 {
350 continue;
351 }
352 let mut lsf = LsfFrame([0u8; 30]);
353 lsf.0.copy_from_slice(&payload[0..30]);
354 if lsf.check_crc() != 0 {
355 continue;
356 }
357 pending.lsf = Some(lsf);
358 let app_data_len = len - 30;
359 pending.app_data[0..app_data_len].copy_from_slice(&payload[30..]);
360 pending.app_data_len = app_data_len;
361 self.packet_queue[self.packet_next] = pending;
362 self.packet_next = (self.packet_next + 1) % 4;
363 if self.packet_next == self.packet_curr {
364 self.packet_full = true;
365 }
366 } else if port == PORT_STREAM {
367 // TODO: handle port 2
368 }
369 }
370 n
371 }
372
373 fn kiss_to_host(&mut self, kiss_frame: KissFrame) {
374 self.outgoing_kiss = Some(OutgoingKiss {
375 kiss_frame,
376 sent: 0,
377 });
378 }
379 }
380
381 #[derive(Debug, PartialEq, Eq, Clone)]
382 pub enum SoftTncError {
383 General(&'static str),
384 InvalidState,
385 }
386
387 struct OutgoingKiss {
388 kiss_frame: KissFrame,
389 sent: usize,
390 }
391
392 enum State {
393 /// Nothing happening. We may have TX data queued but we won't act on it until CSMA opens up.
394 Idle,
395
396 /// We received some stream data but missed the leading LSF so we are trying to assemble from LICH.
397 RxAcquiringStream(RxAcquiringStreamState),
398
399 /// We have acquired an identified stream transmission and are sending data payloads to the host.
400 RxStream(RxStreamState),
401
402 /// We are receiving a packet. All is well so far, and there is more data to come before we tell the host.
403 RxPacket(RxPacketState),
404
405 /// PTT is on and this is a stream-type transmission. New data may be added.
406 TxStream,
407
408 /// We have delivered the last frame in the current stream
409 TxStreamSentEndOfStream,
410
411 /// PTT is on and this is a packet-type transmission. New packets may be enqueued.
412 TxPacket,
413
414 /// We gave modulator an EndOfTransmission. PTT is still on, waiting for modulator to advise end time.
415 TxEnding,
416
417 /// Ending transmission, PTT remains on, but we know the timestamp at which we should disengage it.
418 TxEndingAtTime(u64),
419 }
420
421 struct RxAcquiringStreamState {
422 /// Partial assembly of LSF by accumulating LICH fields.
423 lich: LichCollection,
424 }
425
426 struct RxStreamState {
427 /// Track identifying information for this transmission so we can tell if it changes.
428 lsf: LsfFrame,
429
430 /// Expected next frame number. Allowed to skip values on RX, but not go backwards.
431 index: u16,
432 }
433
434 struct RxPacketState {
435 /// Initial LSF
436 lsf: LsfFrame,
437
438 /// Accumulation of packet data that we have received so far.
439 packet: [u8; 825],
440
441 /// Number of payload frames we have received. If we are stably in the RxPacket state,
442 /// this will be between 0 and 32 inclusive.
443 count: usize,
444 }
445
446 struct PendingPacket {
447 lsf: Option<LsfFrame>,
448
449 app_data: [u8; 825],
450 app_data_len: usize,
451 app_data_transmitted: usize,
452 }
453
454 impl PendingPacket {
455 fn new() -> Self {
456 Self {
457 lsf: None,
458 app_data: [0u8; 825],
459 app_data_len: 0,
460 app_data_transmitted: 0,
461 }
462 }
463
464 /// Returns next frame, not including preamble or EOT.
465 ///
466 /// False means all data frames have been sent.
467 fn next_frame(&mut self) -> Option<ModulatorFrame> {
468 if let Some(lsf) = self.lsf.take() {
469 return Some(ModulatorFrame::Lsf(lsf));
470 }
471 if self.app_data_len == self.app_data_transmitted {
472 return None;
473 }
474 let remaining = self.app_data_len - self.app_data_transmitted;
475 let (counter, data_len) = if remaining <= 25 {
476 (
477 PacketFrameCounter::FinalFrame {
478 payload_len: remaining,
479 },
480 remaining,
481 )
482 } else {
483 (
484 PacketFrameCounter::Frame {
485 index: self.app_data_transmitted / 25,
486 },
487 25,
488 )
489 };
490 let mut payload = [0u8; 25];
491 payload.copy_from_slice(
492 &self.app_data[self.app_data_transmitted..(self.app_data_transmitted + data_len)],
493 );
494 self.app_data_transmitted += data_len;
495 Some(ModulatorFrame::Packet(PacketFrame { payload, counter }))
496 }
497 }
498
499 impl Default for PendingPacket {
500 fn default() -> Self {
501 Self {
502 lsf: None,
503 app_data: [0u8; 825],
504 app_data_len: 0,
505 app_data_transmitted: 0,
506 }
507 }
508 }
509
510 #[cfg(test)]
511 mod tests {
512 use super::*;
513 use crate::kiss::{KissCommand, PORT_STREAM};
514 use crate::protocol::StreamFrame;
515
516 // TODO: finish all handle_frame tests as below
517 // this will be much more straightforward when we have a way to create LSFs programatically
518
519 // receiving a single-frame packet
520
521 // receiving a multi-frame packet
522
523 // part of one packet and then another
524
525 #[test]
526 fn tnc_receive_stream() {
527 let lsf = LsfFrame([
528 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
529 0, 0, 0, 0, 0, 131, 53,
530 ]);
531 let stream1 = StreamFrame {
532 lich_idx: 0,
533 lich_part: [255, 255, 255, 255, 255],
534 frame_number: 0,
535 end_of_stream: false,
536 stream_data: [
537 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
538 ],
539 };
540 let stream2 = StreamFrame {
541 lich_idx: 1,
542 lich_part: [255, 0, 0, 0, 159],
543 frame_number: 1,
544 end_of_stream: true,
545 stream_data: [
546 17, 0, 94, 82, 216, 135, 181, 15, 30, 0, 125, 195, 152, 183, 41, 57,
547 ],
548 };
549 let mut tnc = SoftTnc::new();
550 let mut kiss = KissFrame::new_empty();
551 assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
552
553 tnc.handle_frame(Frame::Lsf(lsf));
554 kiss.len = tnc.read_kiss(&mut kiss.data);
555 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
556 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
557
558 let mut payload_buf = [0u8; 2048];
559 let n = kiss.decode_payload(&mut payload_buf).unwrap();
560 assert_eq!(n, 30);
561
562 tnc.handle_frame(Frame::Stream(stream1));
563 kiss.len = tnc.read_kiss(&mut kiss.data);
564 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
565 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
566
567 let n = kiss.decode_payload(&mut payload_buf).unwrap();
568 assert_eq!(n, 26);
569
570 tnc.handle_frame(Frame::Stream(stream2));
571 kiss.len = tnc.read_kiss(&mut kiss.data);
572 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
573 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
574
575 let n = kiss.decode_payload(&mut payload_buf).unwrap();
576 assert_eq!(n, 26);
577 }
578
579 #[test]
580 fn tnc_acquire_stream() {
581 let frames = [
582 StreamFrame {
583 lich_idx: 0,
584 lich_part: [255, 255, 255, 255, 255],
585 frame_number: 0,
586 end_of_stream: false,
587 stream_data: [
588 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
589 ],
590 },
591 StreamFrame {
592 lich_idx: 1,
593 lich_part: [255, 0, 0, 0, 159],
594 frame_number: 1,
595 end_of_stream: false,
596 stream_data: [
597 17, 0, 94, 82, 216, 135, 181, 15, 30, 0, 125, 195, 152, 183, 41, 57,
598 ],
599 },
600 StreamFrame {
601 lich_idx: 2,
602 lich_part: [221, 81, 5, 5, 0],
603 frame_number: 2,
604 end_of_stream: false,
605 stream_data: [
606 17, 128, 93, 74, 154, 167, 169, 11, 20, 0, 116, 91, 158, 220, 45, 111,
607 ],
608 },
609 StreamFrame {
610 lich_idx: 3,
611 lich_part: [0, 0, 0, 0, 0],
612 frame_number: 3,
613 end_of_stream: false,
614 stream_data: [
615 15, 128, 114, 83, 218, 252, 59, 111, 31, 128, 116, 91, 84, 231, 45, 105,
616 ],
617 },
618 StreamFrame {
619 lich_idx: 4,
620 lich_part: [0, 0, 0, 0, 0],
621 frame_number: 4,
622 end_of_stream: false,
623 stream_data: [
624 9, 128, 119, 115, 220, 220, 57, 15, 48, 128, 124, 83, 158, 236, 181, 91,
625 ],
626 },
627 StreamFrame {
628 lich_idx: 5,
629 lich_part: [0, 0, 0, 131, 53],
630 frame_number: 5,
631 end_of_stream: false,
632 stream_data: [
633 52, 0, 116, 90, 152, 167, 225, 216, 32, 0, 116, 83, 156, 212, 33, 216,
634 ],
635 },
636 ];
637
638 let mut tnc = SoftTnc::new();
639 let mut kiss = KissFrame::new_empty();
640 for f in frames {
641 tnc.handle_frame(Frame::Stream(f));
642 }
643 kiss.len = tnc.read_kiss(&mut kiss.data);
644 let mut payload_buf = [0u8; 2048];
645 let n = kiss.decode_payload(&mut payload_buf).unwrap();
646 assert_eq!(n, 30);
647 assert_eq!(
648 &payload_buf[0..30],
649 [
650 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0,
651 0, 0, 0, 0, 0, 0, 131, 53,
652 ]
653 );
654 }
655
656 #[test]
657 fn tnc_handle_skipped_stream_frame() {
658 let lsf = LsfFrame([
659 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
660 0, 0, 0, 0, 0, 131, 53,
661 ]);
662 let stream1 = StreamFrame {
663 lich_idx: 0,
664 lich_part: [255, 255, 255, 255, 255],
665 frame_number: 0,
666 end_of_stream: false,
667 stream_data: [
668 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
669 ],
670 };
671 let stream3 = StreamFrame {
672 lich_idx: 2,
673 lich_part: [221, 81, 5, 5, 0],
674 frame_number: 2,
675 end_of_stream: false,
676 stream_data: [
677 17, 128, 93, 74, 154, 167, 169, 11, 20, 0, 116, 91, 158, 220, 45, 111,
678 ],
679 };
680 let mut tnc = SoftTnc::new();
681 let mut kiss = KissFrame::new_empty();
682 assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
683
684 tnc.handle_frame(Frame::Lsf(lsf));
685 kiss.len = tnc.read_kiss(&mut kiss.data);
686 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
687 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
688
689 let mut payload_buf = [0u8; 2048];
690 let n = kiss.decode_payload(&mut payload_buf).unwrap();
691 assert_eq!(n, 30);
692
693 tnc.handle_frame(Frame::Stream(stream1));
694 kiss.len = tnc.read_kiss(&mut kiss.data);
695 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
696 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
697
698 let n = kiss.decode_payload(&mut payload_buf).unwrap();
699 assert_eq!(n, 26);
700
701 tnc.handle_frame(Frame::Stream(stream3));
702 kiss.len = tnc.read_kiss(&mut kiss.data);
703 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
704 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
705
706 let n = kiss.decode_payload(&mut payload_buf).unwrap();
707 assert_eq!(n, 26);
708 }
709 }