awdl_frame_parser/
action_frame.rs

1use macro_bits::serializable_enum;
2use scroll::{
3    ctx::{MeasureWith, TryFromCtx, TryIntoCtx},
4    Endian, Pread, Pwrite,
5};
6
7use core::{fmt::Debug, time::Duration};
8
9use crate::tlvs::{ReadTLVs, AWDLTLV};
10
11serializable_enum! {
12    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
13    pub enum AWDLActionFrameSubType: u8 {
14        #[default]
15        /// **P**eriodic **S**ynchronization **F**rame
16        PSF => 0x00,
17        /// **M**aster **I**ndication **F**rame
18        MIF => 0x03
19    }
20}
21
22#[derive(Clone, PartialEq, Eq)]
23/// An AWDL AF(**A**ction **F**rame).
24pub struct AWDLActionFrame<I> {
25    /// This is the subtype of the AF. Options are [MIF](AWDLActionFrameSubType::MIF) and [PSF](AWDLActionFrameSubType::PSF).
26    pub subtype: AWDLActionFrameSubType,
27
28    /// The time the NIC physically started sending the frame, in μs.
29    pub phy_tx_time: Duration,
30
31    /// The time the driver send the frame to the NIC, in μs.
32    pub target_tx_time: Duration,
33
34    /// The TLVs contained in the action frame.
35    pub tagged_data: I,
36}
37impl<I> AWDLActionFrame<I> {
38    /// Calculate the time, between the driver sending the frame to the WNIC and the transmission starting.
39    pub fn tx_delta(&self) -> Duration {
40        self.phy_tx_time - self.target_tx_time
41    }
42}
43impl<'a, I: Debug, MACIterator, LabelIterator> Debug for AWDLActionFrame<I>
44where
45    AWDLTLV<'a, MACIterator, LabelIterator>: Debug,
46    I: IntoIterator<Item = AWDLTLV<'a, MACIterator, LabelIterator>> + Clone,
47{
48    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
49        f.debug_struct("AWDLActionFrame")
50            .field("subtype", &self.subtype)
51            .field("phy_tx_time", &self.phy_tx_time)
52            .field("target_tx_time", &self.target_tx_time)
53            .field("tagged_data", &self.tagged_data)
54            .finish()
55    }
56}
57impl<I: MeasureWith<()>> MeasureWith<()> for AWDLActionFrame<I>
58{
59    fn measure_with(&self, ctx: &()) -> usize {
60        12 + self
61            .tagged_data.measure_with(ctx)
62    }
63}
64
65impl<'a> TryFromCtx<'a> for AWDLActionFrame<ReadTLVs<'a>> {
66    type Error = scroll::Error;
67    fn try_from_ctx(from: &'a [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> {
68        let mut offset = 0;
69        if from.gread::<u8>(&mut offset)? != 0x8u8 {
70            return Err(scroll::Error::BadInput {
71                size: offset,
72                msg: "AF didn't start with 0x8.",
73            });
74        }
75        if from.gread::<u8>(&mut offset)? != 0x10u8 {
76            return Err(scroll::Error::BadInput {
77                size: offset,
78                msg: "AF header version wasn't 1.0.",
79            });
80        }
81        let subtype = AWDLActionFrameSubType::from_bits(from.gread(&mut offset)?);
82        offset += 1;
83
84        let phy_tx_time =
85            Duration::from_micros(from.gread_with::<u32>(&mut offset, Endian::Little)? as u64);
86        let target_tx_time =
87            Duration::from_micros(from.gread_with::<u32>(&mut offset, Endian::Little)? as u64);
88        let tagged_data = ReadTLVs::new(&from[offset..]);
89
90        Ok((
91            Self {
92                subtype,
93                phy_tx_time,
94                target_tx_time,
95                tagged_data,
96            },
97            offset,
98        ))
99    }
100}
101impl<I: TryIntoCtx<(), Error = scroll::Error>> TryIntoCtx for AWDLActionFrame<I>
102{
103    type Error = scroll::Error;
104    fn try_into_ctx(self, buf: &mut [u8], _ctx: ()) -> Result<usize, Self::Error> {
105        let mut offset = 0;
106        buf.gwrite(8u8, &mut offset)?;
107        buf.gwrite(0x10u8, &mut offset)?;
108        buf.gwrite(self.subtype.into_bits(), &mut offset)?;
109        offset += 1;
110        buf.gwrite_with(
111            self.phy_tx_time.as_micros() as u32,
112            &mut offset,
113            Endian::Little,
114        )?;
115        buf.gwrite_with(
116            self.target_tx_time.as_micros() as u32,
117            &mut offset,
118            Endian::Little,
119        )?;
120        buf.gwrite(self.tagged_data, &mut offset)?;
121        Ok(offset)
122    }
123}
124/// The default awdl action frame returned by reading.
125pub type DefaultAWDLActionFrame<'a> = AWDLActionFrame<ReadTLVs<'a>>;
126#[cfg(test)]
127#[test]
128fn test_action_frame() {
129    use alloc::vec;
130
131    let packet_bytes = include_bytes!("../test_bins/mif.bin");
132    let parsed_af = packet_bytes.pread::<DefaultAWDLActionFrame>(0).unwrap();
133    //panic!("{parsed_af:#?}");
134    let mut buf = vec![0; parsed_af.measure_with(&())];
135    buf.pwrite(parsed_af, 0).unwrap();
136    assert_eq!(packet_bytes, buf.as_slice());
137}