leodos_protocols/transport/cfdp/pdu/
header.rs1use bon::bon;
2use zerocopy::FromBytes;
3use zerocopy::Immutable;
4use zerocopy::IntoBytes;
5use zerocopy::KnownLayout;
6use zerocopy::Unaligned;
7use zerocopy::network_endian::U16;
8
9use crate::transport::cfdp::CfdpError;
10use crate::utils::get_bits_u8;
11use crate::utils::set_bits_u8;
12
13#[repr(C)]
17#[derive(Copy, Clone, Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
18pub struct PduHeaderFixedPart {
19 version_and_flags: u8,
22 data_field_len: U16,
24 lengths_and_metadata_flag: u8,
27}
28
29#[repr(u8)]
31#[derive(Debug, PartialEq, Eq)]
32pub enum PduType {
33 FileData = 0,
35 FileDirective = 1,
37}
38
39#[repr(u8)]
41#[derive(Debug, PartialEq, Eq, Copy, Clone)]
42pub enum Direction {
43 TowardReceiver = 0,
45 TowardSender = 1,
47}
48
49#[repr(u8)]
51#[derive(Debug, PartialEq, Eq, Copy, Clone)]
52pub enum TransmissionMode {
53 Acknowledged = 0,
55 Unacknowledged = 1,
57}
58
59impl From<bool> for PduType {
61 fn from(val: bool) -> Self {
62 if !val {
63 PduType::FileData
64 } else {
65 PduType::FileDirective
66 }
67 }
68}
69
70impl From<bool> for Direction {
72 fn from(val: bool) -> Self {
73 if !val {
74 Direction::TowardReceiver
75 } else {
76 Direction::TowardSender
77 }
78 }
79}
80
81impl From<bool> for TransmissionMode {
83 fn from(val: bool) -> Self {
84 if !val {
85 TransmissionMode::Acknowledged
86 } else {
87 TransmissionMode::Unacknowledged
88 }
89 }
90}
91
92#[rustfmt::skip]
93mod bitmasks {
95 pub const VERSION_MASK: u8 = 0b_11100000;
97 pub const PDU_TYPE_MASK: u8 = 0b_00010000;
99 pub const DIRECTION_MASK: u8 = 0b_00001000;
101 pub const TX_MODE_MASK: u8 = 0b_00000100;
103 pub const CRC_FLAG_MASK: u8 = 0b_00000010;
105 pub const LARGE_FILE_FLAG_MASK: u8 = 0b_00000001;
107
108 pub const SEG_CTRL_MASK: u8 = 0b_10000000;
110 pub const ENTITY_ID_LEN_MINUS_ONE_MASK: u8 = 0b_01110000;
112 pub const SEG_META_FLAG_MASK: u8 = 0b_00001000;
114 pub const TXN_SEQ_NUM_LEN_MINUS_ONE_MASK: u8 = 0b_00000111;
116}
117
118use bitmasks::*;
119
120#[bon]
121impl PduHeaderFixedPart {
122 #[builder]
124 pub fn new(
125 version: u8,
126 pdu_type: PduType,
127 direction: Direction,
128 tx_mode: TransmissionMode,
129 crc_flag: bool,
130 large_file_flag: bool,
131 data_field_len: u16,
132 seg_ctrl: bool,
133 seg_meta_flag: bool,
134 ) -> Result<Self, CfdpError> {
135 let mut header = PduHeaderFixedPart {
136 version_and_flags: 0,
137 data_field_len: U16::new(0),
138 lengths_and_metadata_flag: 0,
139 };
140
141 header.set_version(version);
142 header.set_pdu_type(pdu_type);
143 header.set_direction(direction);
144 header.set_tx_mode(tx_mode);
145 header.set_crc_flag(crc_flag);
146 header.set_large_file_flag(large_file_flag);
147 header.set_data_field_len(data_field_len);
148 header.set_segmentation_control(seg_ctrl);
149 header.set_segment_metadata_flag(seg_meta_flag);
150 header.set_entity_id_len(1)?;
153 header.set_txn_seq_num_len(1)?;
154
155 Ok(header)
156 }
157}
158
159impl PduHeaderFixedPart {
160 pub fn version(&self) -> u8 {
163 get_bits_u8(self.version_and_flags, VERSION_MASK)
164 }
165 pub fn set_version(&mut self, version: u8) {
167 set_bits_u8(&mut self.version_and_flags, VERSION_MASK, version);
168 }
169
170 pub fn pdu_type(&self) -> PduType {
172 PduType::from(get_bits_u8(self.version_and_flags, PDU_TYPE_MASK) == 1)
173 }
174 pub fn set_pdu_type(&mut self, pdu_type: PduType) {
176 let val = match pdu_type {
177 PduType::FileData => 0,
178 PduType::FileDirective => 1,
179 };
180 set_bits_u8(&mut self.version_and_flags, PDU_TYPE_MASK, val);
181 }
182
183 pub fn direction(&self) -> Direction {
185 Direction::from(get_bits_u8(self.version_and_flags, DIRECTION_MASK) == 1)
186 }
187 pub fn set_direction(&mut self, direction: Direction) {
189 set_bits_u8(&mut self.version_and_flags, DIRECTION_MASK, direction as u8);
190 }
191
192 pub fn tx_mode(&self) -> TransmissionMode {
194 TransmissionMode::from(get_bits_u8(self.version_and_flags, TX_MODE_MASK) == 1)
195 }
196 pub fn set_tx_mode(&mut self, tx_mode: TransmissionMode) {
198 set_bits_u8(&mut self.version_and_flags, TX_MODE_MASK, tx_mode as u8);
199 }
200
201 pub fn crc_flag(&self) -> bool {
203 get_bits_u8(self.version_and_flags, CRC_FLAG_MASK) == 1
204 }
205 pub fn set_crc_flag(&mut self, crc_flag: bool) {
207 let val = if crc_flag { 1 } else { 0 };
208 set_bits_u8(&mut self.version_and_flags, CRC_FLAG_MASK, val);
209 }
210
211 pub fn large_file_flag(&self) -> bool {
213 get_bits_u8(self.version_and_flags, LARGE_FILE_FLAG_MASK) == 1
214 }
215 pub fn set_large_file_flag(&mut self, large_file_flag: bool) {
217 let val = if large_file_flag { 1 } else { 0 };
218 set_bits_u8(&mut self.version_and_flags, LARGE_FILE_FLAG_MASK, val);
219 }
220
221 pub fn data_field_len(&self) -> usize {
224 self.data_field_len.get() as usize
225 }
226 pub fn set_data_field_len(&mut self, len: u16) {
228 self.data_field_len.set(len);
229 }
230
231 pub fn segmentation_control(&self) -> bool {
234 get_bits_u8(self.lengths_and_metadata_flag, SEG_CTRL_MASK) == 1
235 }
236 pub fn set_segmentation_control(&mut self, seg_ctrl: bool) {
238 let val = if seg_ctrl { 1 } else { 0 };
239 set_bits_u8(&mut self.lengths_and_metadata_flag, SEG_CTRL_MASK, val);
240 }
241
242 pub fn entity_id_len(&self) -> usize {
244 let val = get_bits_u8(self.lengths_and_metadata_flag, ENTITY_ID_LEN_MINUS_ONE_MASK);
245 val as usize + 1
246 }
247 pub fn set_entity_id_len(&mut self, len: usize) -> Result<(), CfdpError> {
249 if len == 0 || len > 8 {
250 return Err(CfdpError::Custom(
251 "Entity ID length must be between 1 and 8",
252 ));
253 }
254 set_bits_u8(
255 &mut self.lengths_and_metadata_flag,
256 ENTITY_ID_LEN_MINUS_ONE_MASK,
257 len as u8 - 1,
258 );
259 Ok(())
260 }
261
262 pub fn segment_metadata_flag(&self) -> bool {
264 get_bits_u8(self.lengths_and_metadata_flag, SEG_META_FLAG_MASK) == 1
265 }
266 pub fn set_segment_metadata_flag(&mut self, seg_meta_flag: bool) {
268 let val = if seg_meta_flag { 1 } else { 0 };
269 set_bits_u8(&mut self.lengths_and_metadata_flag, SEG_META_FLAG_MASK, val);
270 }
271
272 pub fn txn_seq_num_len(&self) -> usize {
274 let val = get_bits_u8(
275 self.lengths_and_metadata_flag,
276 TXN_SEQ_NUM_LEN_MINUS_ONE_MASK,
277 );
278 val as usize + 1
279 }
280 pub fn set_txn_seq_num_len(&mut self, len: usize) -> Result<(), CfdpError> {
282 if len == 0 || len > 8 {
283 return Err(CfdpError::Custom(
284 "Transaction Sequence Number length must be between 1 and 8",
285 ));
286 }
287 set_bits_u8(
288 &mut self.lengths_and_metadata_flag,
289 TXN_SEQ_NUM_LEN_MINUS_ONE_MASK,
290 len as u8 - 1,
291 );
292 Ok(())
293 }
294
295 pub fn fixed_header_len(&self) -> usize {
297 core::mem::size_of::<PduHeaderFixedPart>()
298 }
299
300 pub fn variable_header_len(&self) -> usize {
302 let entity_id_len = self.entity_id_len();
303 let txn_seq_num_len = self.txn_seq_num_len();
304 entity_id_len * 2 + txn_seq_num_len
305 }
306
307 pub fn total_header_len(&self) -> usize {
309 self.fixed_header_len() + self.variable_header_len()
310 }
311
312 pub fn total_pdu_len(&self) -> usize {
314 self.total_header_len() + self.data_field_len()
315 }
316}