1use super::{control_field::ControlField, MessageType};
2use crate::datastructures::{
3 common::{PortIdentity, TimeInterval},
4 WireFormat, WireFormatError,
5};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct Header {
9 pub(crate) sdo_id: SdoId,
10 pub(crate) version: PtpVersion,
11 pub(crate) domain_number: u8,
12 pub(crate) alternate_master_flag: bool,
13 pub(crate) two_step_flag: bool,
14 pub(crate) unicast_flag: bool,
15 pub(crate) ptp_profile_specific_1: bool,
16 pub(crate) ptp_profile_specific_2: bool,
17 pub(crate) leap61: bool,
18 pub(crate) leap59: bool,
19 pub(crate) current_utc_offset_valid: bool,
20 pub(crate) ptp_timescale: bool,
21 pub(crate) time_tracable: bool,
22 pub(crate) frequency_tracable: bool,
23 pub(crate) synchronization_uncertain: bool,
24 pub(crate) correction_field: TimeInterval,
25 pub(crate) source_port_identity: PortIdentity,
26 pub(crate) sequence_id: u16,
27 pub(crate) log_message_interval: i8,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub(crate) struct DeserializedHeader {
32 pub(crate) header: Header,
33 pub(crate) message_type: MessageType,
34 pub(crate) message_length: u16,
35}
36
37impl Header {
38 pub(crate) fn new(minor_ptp_version: u8) -> Self {
39 Self {
40 sdo_id: SdoId(0),
41 version: PtpVersion {
42 major: 2,
43 minor: minor_ptp_version,
44 },
45 domain_number: 0,
46 alternate_master_flag: false,
47 two_step_flag: false,
48 unicast_flag: false,
49 ptp_profile_specific_1: false,
50 ptp_profile_specific_2: false,
51 leap59: false,
52 leap61: false,
53 current_utc_offset_valid: false,
54 ptp_timescale: false,
55 time_tracable: false,
56 frequency_tracable: false,
57 synchronization_uncertain: false,
58 correction_field: TimeInterval::default(),
59 source_port_identity: PortIdentity::default(),
60 sequence_id: 0,
61 log_message_interval: 0,
62 }
63 }
64
65 pub(crate) fn wire_size(&self) -> usize {
66 34
67 }
68
69 pub(crate) fn serialize_header(
70 &self,
71 content_type: MessageType,
72 content_length: usize,
73 buffer: &mut [u8],
74 ) -> Result<(), WireFormatError> {
75 buffer[0] = ((self.sdo_id.high_byte()) << 4) | ((content_type as u8) & 0x0f);
76 buffer[1] = self.version.as_byte();
77 buffer[2..4].copy_from_slice(&((content_length + self.wire_size()) as u16).to_be_bytes());
78 buffer[4] = self.domain_number;
79 buffer[5] = self.sdo_id.low_byte();
80 buffer[6] = 0;
81 buffer[7] = 0;
82 buffer[6] |= self.alternate_master_flag as u8;
83 buffer[6] |= (self.two_step_flag as u8) << 1;
84 buffer[6] |= (self.unicast_flag as u8) << 2;
85 buffer[6] |= (self.ptp_profile_specific_1 as u8) << 5;
86 buffer[6] |= (self.ptp_profile_specific_2 as u8) << 6;
87 buffer[7] |= self.leap61 as u8;
88 buffer[7] |= (self.leap59 as u8) << 1;
89 buffer[7] |= (self.current_utc_offset_valid as u8) << 2;
90 buffer[7] |= (self.ptp_timescale as u8) << 3;
91 buffer[7] |= (self.time_tracable as u8) << 4;
92 buffer[7] |= (self.frequency_tracable as u8) << 5;
93 buffer[7] |= (self.synchronization_uncertain as u8) << 6;
94 self.correction_field.serialize(&mut buffer[8..16])?;
95 buffer[16..20].copy_from_slice(&[0, 0, 0, 0]);
96 self.source_port_identity.serialize(&mut buffer[20..30])?;
97 buffer[30..32].copy_from_slice(&self.sequence_id.to_be_bytes());
98 buffer[32] = ControlField::from(content_type).to_primitive();
99 buffer[33] = self.log_message_interval as u8;
100
101 Ok(())
102 }
103
104 pub(crate) fn deserialize_header(buffer: &[u8]) -> Result<DeserializedHeader, WireFormatError> {
105 if buffer.len() < 34 {
106 return Err(WireFormatError::BufferTooShort);
107 }
108
109 let version = PtpVersion::from_byte(buffer[1]);
110 let sdo_id = SdoId((((buffer[0] & 0xf0) as u16) << 4) | (buffer[5] as u16));
111
112 Ok(DeserializedHeader {
113 header: Self {
114 sdo_id,
115 version,
116 domain_number: buffer[4],
117 alternate_master_flag: (buffer[6] & (1 << 0)) > 0,
118 two_step_flag: (buffer[6] & (1 << 1)) > 0,
119 unicast_flag: (buffer[6] & (1 << 2)) > 0,
120 ptp_profile_specific_1: (buffer[6] & (1 << 5)) > 0,
121 ptp_profile_specific_2: (buffer[6] & (1 << 6)) > 0,
122 leap61: (buffer[7] & (1 << 0)) > 0,
123 leap59: (buffer[7] & (1 << 1)) > 0,
124 current_utc_offset_valid: (buffer[7] & (1 << 2)) > 0,
125 ptp_timescale: (buffer[7] & (1 << 3)) > 0,
126 time_tracable: (buffer[7] & (1 << 4)) > 0,
127 frequency_tracable: (buffer[7] & (1 << 5)) > 0,
128 synchronization_uncertain: (buffer[7] & (1 << 6)) > 0,
129 correction_field: TimeInterval::deserialize(&buffer[8..16])?,
130 source_port_identity: PortIdentity::deserialize(&buffer[20..30])?,
131 sequence_id: u16::from_be_bytes(buffer[30..32].try_into().unwrap()),
132 log_message_interval: buffer[33] as i8,
133 },
134 message_type: (buffer[0] & 0x0f).try_into()?,
135 message_length: u16::from_be_bytes(buffer[2..4].try_into().unwrap()),
136 })
137 }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
157#[cfg_attr(feature = "serde", derive(serde::Serialize))]
158pub struct SdoId(u16);
159
160impl core::fmt::Display for SdoId {
161 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
162 self.0.fmt(f)
163 }
164}
165
166impl SdoId {
167 const fn high_byte(self) -> u8 {
168 (self.0 >> 8) as u8
169 }
170
171 const fn low_byte(self) -> u8 {
172 self.0 as u8
173 }
174}
175
176#[cfg(feature = "serde")]
177struct SdoIdVisitor;
178
179#[cfg(feature = "serde")]
180impl<'de> serde::Deserialize<'de> for SdoId {
181 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182 where
183 D: serde::Deserializer<'de>,
184 {
185 deserializer.deserialize_newtype_struct("SdoId", SdoIdVisitor)
186 }
187}
188
189#[cfg(all(feature = "serde", feature = "std"))]
190impl<'de> serde::de::Visitor<'de> for SdoIdVisitor {
191 type Value = SdoId;
192
193 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
194 formatter.write_str("a 12 bit value within the 0..=0xFFF range")
195 }
196
197 fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
198 where
199 D: serde::Deserializer<'de>,
200 {
201 use serde::{de::Error, Deserialize};
202 let v = u16::deserialize(deserializer)?;
203 SdoId::try_from(v).or(Err(D::Error::custom(std::format!(
204 "SdoId not in range of 0..=0xFFF: {}",
205 v
206 ))))
207 }
208
209 fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
210 where
211 E: serde::de::Error,
212 {
213 SdoId::try_from(v).or(Err(E::custom(std::format!(
214 "SdoId not in range of 0..=0xFFF: {}",
215 v
216 ))))
217 }
218}
219
220impl TryFrom<u16> for SdoId {
221 type Error = ();
222
223 fn try_from(sdo_id: u16) -> Result<Self, Self::Error> {
225 (0..=0xfff)
226 .contains(&sdo_id)
227 .then_some(Self(sdo_id))
228 .ok_or(())
229 }
230}
231
232impl From<SdoId> for u16 {
233 fn from(value: SdoId) -> Self {
234 value.0
235 }
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub struct PtpVersion {
240 major: u8,
241 minor: u8,
242}
243
244impl PtpVersion {
245 #[allow(unused)]
246 pub fn new(major: u8, minor: u8) -> Option<Self> {
247 if major >= 0x10 || minor >= 0x10 {
248 None
249 } else {
250 Some(Self { major, minor })
251 }
252 }
253
254 fn as_byte(&self) -> u8 {
255 (self.minor << 4) | self.major
256 }
257
258 fn from_byte(byte: u8) -> Self {
259 Self {
260 major: byte & 0x0f,
261 minor: byte >> 4,
262 }
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use fixed::types::I48F16;
269
270 use super::*;
271 use crate::datastructures::common::ClockIdentity;
272
273 #[test]
274 fn flagfield_wireformat() {
275 #[rustfmt::skip]
276 let representations = [
277 ([0x00, 0x00u8], Header::new(1)),
278 ([0x01, 0x00u8], Header { alternate_master_flag: true, ..Header::new(1) }),
279 ([0x02, 0x00u8], Header { two_step_flag: true, ..Header::new(1) }),
280 ([0x04, 0x00u8], Header { unicast_flag: true, ..Header::new(1) }),
281 ([0x20, 0x00u8], Header { ptp_profile_specific_1: true, ..Header::new(1) }),
282 ([0x40, 0x00u8], Header { ptp_profile_specific_2: true, ..Header::new(1) }),
283 ([0x00, 0x01u8], Header { leap61: true, ..Header::new(1) }),
284 ([0x00, 0x02u8], Header { leap59: true, ..Header::new(1) }),
285 ([0x00, 0x04u8], Header { current_utc_offset_valid: true, ..Header::new(1) }),
286 ([0x00, 0x08u8], Header { ptp_timescale: true, ..Header::new(1) }),
287 ([0x00, 0x10u8], Header { time_tracable: true, ..Header::new(1) }),
288 ([0x00, 0x20u8], Header { frequency_tracable: true, ..Header::new(1) }),
289 ([0x00, 0x40u8], Header { synchronization_uncertain: true, ..Header::new(1) }),
290 ];
291
292 for (i, (byte_representation, flag_representation)) in
293 representations.into_iter().enumerate()
294 {
295 let mut serialization_buffer = [0; 34];
297 flag_representation
298 .serialize_header(MessageType::Sync, 0, &mut serialization_buffer)
299 .unwrap();
300 assert_eq!(
301 serialization_buffer[6..8],
302 byte_representation,
303 "The serialized flag field is not what it's supposed to for variant {}",
304 i
305 );
306
307 serialization_buffer[6] = byte_representation[0];
309 serialization_buffer[7] = byte_representation[1];
310 let deserialized_flag_field =
311 Header::deserialize_header(&serialization_buffer).unwrap();
312 assert_eq!(
313 deserialized_flag_field.header, flag_representation,
314 "The deserialized flag field is not what it's supposed to for variant {}",
315 i
316 );
317 }
318 }
319
320 #[test]
321 fn header_wireformat() {
322 let representations = [(
323 [
324 0x59,
325 0xa1,
326 0x12,
327 0x34,
328 0xaa,
329 0xbb,
330 0b0100_0101,
331 0b0010_1010,
332 0x00,
333 0x00,
334 0x00,
335 0x00,
336 0x00,
337 0x01,
338 0x80,
339 0x00,
340 0,
341 0,
342 0,
343 0,
344 0,
345 1,
346 2,
347 3,
348 4,
349 5,
350 6,
351 7,
352 0x55,
353 0x55,
354 0xde,
355 0xad,
356 0x03,
357 0x16,
358 ],
359 DeserializedHeader {
360 header: Header {
361 sdo_id: SdoId(0x5bb),
362 version: PtpVersion {
363 major: 0x1,
364 minor: 0xa,
365 },
366 domain_number: 0xaa,
367 alternate_master_flag: true,
368 two_step_flag: false,
369 unicast_flag: true,
370 ptp_profile_specific_1: false,
371 ptp_profile_specific_2: true,
372 leap61: false,
373 leap59: true,
374 current_utc_offset_valid: false,
375 ptp_timescale: true,
376 time_tracable: false,
377 frequency_tracable: true,
378 synchronization_uncertain: false,
379 correction_field: TimeInterval(I48F16::from_num(1.5f64)),
380 source_port_identity: PortIdentity {
381 clock_identity: ClockIdentity([0, 1, 2, 3, 4, 5, 6, 7]),
382 port_number: 0x5555,
383 },
384 sequence_id: 0xdead,
385 log_message_interval: 0x16,
386 },
387 message_type: MessageType::DelayResp,
388 message_length: 0x1234,
389 },
390 )];
391
392 for (byte_representation, object_representation) in representations {
393 let mut serialization_buffer = [0; 34];
395 object_representation
396 .header
397 .serialize_header(
398 object_representation.message_type,
399 object_representation.message_length as usize
400 - object_representation.header.wire_size(),
401 &mut serialization_buffer,
402 )
403 .unwrap();
404 assert_eq!(serialization_buffer, byte_representation);
405
406 let deserialized_data = Header::deserialize_header(&byte_representation).unwrap();
408 assert_eq!(deserialized_data, object_representation);
409 }
410 }
411
412 #[test]
413 fn sdo_id_checks() {
414 use serde_test::{assert_de_tokens_error, assert_tokens, Token};
415 let correct_sdo_id = SdoId::try_from(0xfff).unwrap();
416 let faulty_sdo_id = SdoId::try_from(0x1000);
417
418 assert_eq!(0xfff, u16::from(correct_sdo_id));
419 assert!(faulty_sdo_id.is_err());
420
421 assert_tokens(
422 &correct_sdo_id,
423 &[Token::NewtypeStruct { name: "SdoId" }, Token::U16(4095)],
424 );
425
426 assert_de_tokens_error::<SdoId>(
427 &[Token::NewtypeStruct { name: "SdoId" }, Token::U16(4096)],
428 "SdoId not in range of 0..=0xFFF: 4096",
429 );
430 }
431}