Skip to main content

uart_fpl/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![deny(unsafe_code)]
6
7//! UART Framing Protocol Library (`uart_fpl`).
8//!
9//! Provides binary packet framing, streaming parsing with preamble synchronization,
10//! CRC-8 header protection, Channel 0 dynamic protocol negotiation, and
11//! sliding-window flow control primitives for reliable transport over serial links.
12
13mod error;
14mod frame;
15mod handshake;
16mod parser;
17mod resend_sp;
18mod window;
19
20pub use error::{FrameError, HandshakeError, RetransmissionLimitExceeded, UnexpectedSeqError};
21pub use frame::{
22    CONTROL_CHANNEL_ID, Frame, FrameType, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE, ProtocolId,
23    encode_frame,
24};
25pub use handshake::{HandshakeResponse, HandshakeStatus, HostHandshake, TargetHandshake};
26pub use parser::FrameParser;
27pub use resend_sp::{
28    AckOutcome, DEFAULT_MAX_RETRANSMISSION_ATTEMPTS, DEFAULT_RETRANSMISSION_TIMEOUT,
29    DEFAULT_WINDOW_SIZE, FrameStatus, ResendReceiver, ResendSender,
30};
31pub use window::AckTracker;
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    fn assert_frame_eq(
38        frame: &Frame,
39        session_id: u32,
40        channel_id: u16,
41        seq: u8,
42        frame_type: FrameType,
43        payload: &[u8],
44    ) {
45        assert_eq!(frame.session_id, session_id);
46        assert_eq!(frame.channel_id, channel_id);
47        assert_eq!(frame.seq, seq);
48        assert_eq!(frame.frame_type, frame_type);
49        assert_eq!(frame.payload, payload);
50    }
51
52    #[fuchsia::test]
53    fn test_complete_flow_negotiation_and_traffic() {
54        let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
55        let target = TargetHandshake::new(vec![ProtocolId::ResendSP]);
56
57        let (req_type, req_payload) = host.start().unwrap();
58        let req_frame = encode_frame(100, 0, 0, req_type, &req_payload).unwrap();
59
60        let mut target_parser = FrameParser::new();
61        target_parser.feed(&req_frame);
62        let parsed_req = target_parser.next_frame().unwrap();
63        assert_frame_eq(&parsed_req, 100, 0, 0, FrameType::NegotiateReq, &req_payload);
64
65        let (selected, resp_payload) = target.handle_request(&parsed_req.payload).unwrap();
66        assert_eq!(selected, Some(ProtocolId::ResendSP));
67
68        let resp_frame = encode_frame(100, 0, 1, FrameType::NegotiateResp, &resp_payload).unwrap();
69        let mut host_parser = FrameParser::new();
70        host_parser.feed(&resp_frame);
71        let parsed_resp = host_parser.next_frame().unwrap();
72        assert_frame_eq(&parsed_resp, 100, 0, 1, FrameType::NegotiateResp, &resp_payload);
73
74        let negotiated = host.handle_response(&parsed_resp.payload).unwrap();
75        assert_eq!(negotiated, ProtocolId::ResendSP);
76
77        // Traffic exchange on logical channel
78        let data_frame = encode_frame(100, 1, 0, FrameType::Data, b"payload_data").unwrap();
79        target_parser.feed(&data_frame);
80        let parsed_data = target_parser.next_frame().unwrap();
81        assert_frame_eq(&parsed_data, 100, 1, 0, FrameType::Data, b"payload_data");
82    }
83
84    #[fuchsia::test]
85    fn test_host_reboot_recovery_flow() {
86        let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
87        let target = TargetHandshake::new(vec![ProtocolId::ResendSP]);
88        let mut target_parser = FrameParser::new();
89        let mut host_parser = FrameParser::new();
90
91        let (req_type, req_payload) = host.start().unwrap();
92        let req_frame = encode_frame(111, 0, 0, req_type, &req_payload).unwrap();
93        target_parser.feed(&req_frame);
94        let parsed_req = target_parser.next_frame().unwrap();
95        let (selected, resp_payload) = target.handle_request(&parsed_req.payload).unwrap();
96        assert_eq!(selected, Some(ProtocolId::ResendSP));
97
98        let resp_frame = encode_frame(111, 0, 1, FrameType::NegotiateResp, &resp_payload).unwrap();
99        host_parser.feed(&resp_frame);
100        let parsed_resp = host_parser.next_frame().unwrap();
101        let negotiated = host.handle_response(&parsed_resp.payload).unwrap();
102        assert_eq!(negotiated, ProtocolId::ResendSP);
103
104        let data_frame = encode_frame(111, 1, 2, FrameType::Data, b"hello").unwrap();
105        target_parser.feed(&data_frame);
106        let parsed_data = target_parser.next_frame().unwrap();
107        assert_eq!(parsed_data.payload, b"hello");
108
109        // Host reboots and starts new session 222
110        let host_rebooted = HostHandshake::new(vec![ProtocolId::ResendSP]);
111        let (req_type_2, req_payload_2) = host_rebooted.start().unwrap();
112        let req_frame_2 = encode_frame(222, 0, 0, req_type_2, &req_payload_2).unwrap();
113
114        target_parser.feed(&req_frame_2);
115        let parsed_req_2 = target_parser.next_frame().expect("Target parses reboot handshake");
116        assert_eq!(parsed_req_2.session_id, 222);
117        assert_eq!(parsed_req_2.channel_id, 0);
118        assert_eq!(parsed_req_2.frame_type, FrameType::NegotiateReq);
119        let (selected_2, _) = target.handle_request(&parsed_req_2.payload).unwrap();
120        assert_eq!(selected_2, Some(ProtocolId::ResendSP));
121    }
122
123    #[test]
124    fn test_constants() {
125        assert_eq!(MAX_FRAME_SIZE, 13 + 1024 + 4);
126        assert_eq!(DEFAULT_WINDOW_SIZE, 64);
127        assert_eq!(DEFAULT_MAX_RETRANSMISSION_ATTEMPTS, 60);
128    }
129}