ieee80211/frames/data_frame/
amsdu.rs

1use mac_parser::MACAddress;
2use scroll::{
3    ctx::{MeasureWith, TryFromCtx, TryIntoCtx},
4    Endian, Pread, Pwrite,
5};
6
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
9/// A single subframe from an aggregate MSDU.
10///
11/// The payload is generic, to avoid the need for an intermediate step when writing.
12/// It can be any type, which implements [`TryIntoCtx<Ctx = (), Error = scroll::Error>`](TryIntoCtx).
13pub struct AMSDUSubframe<Payload> {
14    pub destination_address: MACAddress,
15    pub source_address: MACAddress,
16    /// This is the payload of the subframe.
17    /// Although it's generic, it's always a byte slice, when returned by parsing.
18    pub payload: Payload,
19}
20impl AMSDUSubframe<&'_ [u8]> {
21    /// Returns the length in bytes.
22    /// This is currently only const for byte slices, since const traits are unstable.
23    pub const fn length_in_bytes(&self) -> usize {
24        14 + self.payload.len()
25    }
26}
27impl<Payload: MeasureWith<()>> MeasureWith<()> for AMSDUSubframe<Payload> {
28    fn measure_with(&self, ctx: &()) -> usize {
29        14 + self.payload.measure_with(ctx)
30    }
31}
32impl<'a> TryFromCtx<'a> for AMSDUSubframe<&'a [u8]> {
33    type Error = scroll::Error;
34    fn try_from_ctx(from: &'a [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> {
35        let mut offset = 0;
36
37        let destination_address = from.gread(&mut offset)?;
38        let source_address = from.gread(&mut offset)?;
39        let length = from.gread_with::<u16>(&mut offset, Endian::Little)?;
40        let payload = from.gread_with(&mut offset, length as usize)?;
41        // Round to the nearest multiple of four.
42        offset += 3;
43        offset &= !0b0000_0011;
44        Ok((
45            Self {
46                destination_address,
47                source_address,
48                payload,
49            },
50            offset,
51        ))
52    }
53}
54impl<Payload: TryIntoCtx<Error = scroll::Error> + MeasureWith<()>> TryIntoCtx
55    for AMSDUSubframe<Payload>
56{
57    type Error = scroll::Error;
58    fn try_into_ctx(self, buf: &mut [u8], _ctx: ()) -> Result<usize, Self::Error> {
59        let mut offset = 0;
60
61        buf.gwrite(self.destination_address, &mut offset)?;
62        buf.gwrite(self.source_address, &mut offset)?;
63        buf.gwrite_with(
64            self.payload.measure_with(&()) as u16,
65            &mut offset,
66            Endian::Little,
67        )?;
68        buf.gwrite(self.payload, &mut offset)?;
69        // Round to the nearest multiple of four.
70        offset += 3;
71        offset &= !0b0000_0011;
72
73        Ok(offset)
74    }
75}
76
77#[cfg_attr(feature = "defmt", derive(defmt::Format))]
78#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
79/// An iterator over the subframes of an A-MSDU.
80///
81/// This internally keeps the bytes slice and the offset and returns [Some] until [scroll] returns an error.
82/// This has the side effect, that if an error is encoutered while reading, the iterator may stop early, even if data is still left.
83pub struct AMSDUSubframeIterator<'a> {
84    // Making this an option comes with the advantage, that after encoutering an error, subsequent iterations will be almost instant.
85    pub(crate) bytes: Option<&'a [u8]>,
86}
87impl<'a> AMSDUSubframeIterator<'a> {
88    /// Initializes the iterator with the offset set to zero.
89    pub const fn from_bytes(bytes: &'a [u8]) -> Self {
90        Self { bytes: Some(bytes) }
91    }
92    /// Returns the complete length in bytes.
93    pub const fn length_in_bytes(&self) -> usize {
94        match self.bytes {
95            Some(bytes) => bytes.len(),
96            None => 0,
97        }
98    }
99}
100impl<'a> Iterator for AMSDUSubframeIterator<'a> {
101    type Item = AMSDUSubframe<&'a [u8]>;
102    fn next(&mut self) -> Option<Self::Item> {
103        let mut offset = 0;
104        let bytes = self.bytes?;
105        let sub_frame = bytes.gread(&mut offset).ok();
106        match sub_frame {
107            Some(sub_frame) => {
108                self.bytes = Some(&bytes[offset..]);
109                Some(sub_frame)
110            }
111            None => {
112                self.bytes = None;
113                None
114            }
115        }
116    }
117}
118#[cfg_attr(feature = "defmt", derive(defmt::Format))]
119#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
120/// This can be used for writing an aggregate MSDU.
121///
122/// The generic paramter can be any type, which implements [IntoIterator].
123/// It can only be written.
124pub struct AMSDUPayload<Frames> {
125    pub sub_frames: Frames,
126}
127impl<'a> AMSDUPayload<&'a [AMSDUSubframe<&'a [u8]>]> {
128    /// Returns the total length in bytes.
129    pub const fn length_in_bytes(&self) -> usize {
130        let mut size = 0;
131        let mut i = 0;
132        while i != self.sub_frames.len() {
133            size += self.sub_frames[i].length_in_bytes();
134            i += 1;
135        }
136        size
137    }
138}
139impl<Frames: IntoIterator<Item = Payload> + Clone, Payload: MeasureWith<()>> MeasureWith<()>
140    for AMSDUPayload<Frames>
141{
142    fn measure_with(&self, _ctx: &()) -> usize {
143        self.sub_frames
144            .clone()
145            .into_iter()
146            .map(|sub_frame| sub_frame.measure_with(&()))
147            .sum()
148    }
149}
150impl<Frames: IntoIterator<Item = Payload>, Payload: Copy + TryIntoCtx<Error = scroll::Error>>
151    TryIntoCtx for AMSDUPayload<Frames>
152{
153    type Error = scroll::Error;
154    fn try_into_ctx(self, buf: &mut [u8], _ctx: ()) -> Result<usize, Self::Error> {
155        let mut offset = 0;
156        for sub_frame in self.sub_frames.into_iter() {
157            buf.gwrite(sub_frame, &mut offset)?;
158        }
159        Ok(offset)
160    }
161}