1pub const RX_DESC_SIZE: usize = 24;
2pub const DEFAULT_RX_TRANSFER_SIZE: usize = 32 * 1024;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum RxPacketType {
6 NormalRx,
7 C2hPacket,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct RxPacketAttrib {
12 pub pkt_len: u16,
13 pub physt: bool,
14 pub drvinfo_sz: u8,
15 pub shift_sz: u8,
16 pub qos: bool,
17 pub priority: u8,
18 pub mdata: bool,
19 pub seq_num: u16,
20 pub frag_num: u8,
21 pub mfrag: bool,
22 pub bdecrypted: bool,
23 pub encrypt: u8,
24 pub crc_err: bool,
25 pub icv_err: bool,
26 pub tsfl: u32,
27 pub data_rate: u8,
28 pub bw: u8,
29 pub stbc: u8,
30 pub ldpc: u8,
31 pub sgi: u8,
32 pub scrambler: u8,
33 pub rssi: [u8; 4],
34 pub snr: [i8; 4],
35 pub evm: [i8; 4],
36 pub pkt_rpt_type: RxPacketType,
37}
38
39impl Default for RxPacketAttrib {
40 fn default() -> Self {
41 Self {
42 pkt_len: 0,
43 physt: false,
44 drvinfo_sz: 0,
45 shift_sz: 0,
46 qos: false,
47 priority: 0,
48 mdata: false,
49 seq_num: 0,
50 frag_num: 0,
51 mfrag: false,
52 bdecrypted: false,
53 encrypt: 0,
54 crc_err: false,
55 icv_err: false,
56 tsfl: 0,
57 data_rate: 0,
58 bw: 0,
59 stbc: 0,
60 ldpc: 0,
61 sgi: 0,
62 scrambler: 0,
63 rssi: [0; 4],
64 snr: [0; 4],
65 evm: [0; 4],
66 pkt_rpt_type: RxPacketType::NormalRx,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Copy)]
72pub struct RealtekRxPacket<'a> {
73 pub attrib: RxPacketAttrib,
74 pub data: &'a [u8],
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub struct C2hPacket<'a> {
79 pub cmd_id: u8,
80 pub seq: Option<u8>,
81 pub payload: &'a [u8],
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub struct TxStatusReport8814 {
86 pub header_offset: usize,
87 pub queue_select: u8,
88 pub packet_broadcast: bool,
89 pub lifetime_over: bool,
90 pub retry_over: bool,
91 pub mac_id: u8,
92 pub data_retry_count: u8,
93 pub queue_time_us: u32,
94 pub final_data_rate: u8,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub enum AggregateError {
99 DescriptorTooShort,
100 InvalidPacketLength {
101 pkt_len: u16,
102 pkt_offset: usize,
103 remaining: usize,
104 },
105}
106
107impl std::fmt::Display for AggregateError {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 match self {
110 Self::DescriptorTooShort => write!(f, "RX descriptor is shorter than {RX_DESC_SIZE} bytes"),
111 Self::InvalidPacketLength {
112 pkt_len,
113 pkt_offset,
114 remaining,
115 } => write!(
116 f,
117 "invalid RX packet length: pkt_len={pkt_len}, pkt_offset={pkt_offset}, remaining={remaining}"
118 ),
119 }
120 }
121}
122
123impl std::error::Error for AggregateError {}
124
125pub fn parse_rx_descriptor(desc: &[u8]) -> Result<RxPacketAttrib, AggregateError> {
126 if desc.len() < RX_DESC_SIZE {
127 return Err(AggregateError::DescriptorTooShort);
128 }
129
130 let d0 = le32(desc, 0);
131 let d1 = le32(desc, 4);
132 let d2 = le32(desc, 8);
133 let d3 = le32(desc, 12);
134 let d4 = le32(desc, 16);
135 let d5 = le32(desc, 20);
136
137 Ok(RxPacketAttrib {
138 pkt_len: bits(d0, 0, 14) as u16,
139 crc_err: bits(d0, 14, 1) != 0,
140 icv_err: bits(d0, 15, 1) != 0,
141 drvinfo_sz: (bits(d0, 16, 4) * 8) as u8,
142 encrypt: bits(d0, 20, 3) as u8,
143 qos: bits(d0, 23, 1) != 0,
144 shift_sz: bits(d0, 24, 2) as u8,
145 physt: bits(d0, 26, 1) != 0,
146 bdecrypted: bits(d0, 27, 1) == 0,
147 priority: bits(d1, 8, 4) as u8,
148 mdata: bits(d1, 26, 1) != 0,
149 mfrag: bits(d1, 27, 1) != 0,
150 seq_num: bits(d2, 0, 12) as u16,
151 frag_num: bits(d2, 12, 4) as u8,
152 pkt_rpt_type: if bits(d2, 28, 1) != 0 {
153 RxPacketType::C2hPacket
154 } else {
155 RxPacketType::NormalRx
156 },
157 data_rate: bits(d3, 0, 7) as u8,
158 sgi: bits(d4, 0, 1) as u8,
159 ldpc: bits(d4, 1, 1) as u8,
160 stbc: bits(d4, 2, 1) as u8,
161 bw: bits(d4, 4, 2) as u8,
162 scrambler: bits(d4, 9, 7) as u8,
163 tsfl: d5,
164 ..Default::default()
165 })
166}
167
168pub fn parse_rx_aggregate(buf: &[u8]) -> Result<Vec<RealtekRxPacket<'_>>, AggregateError> {
169 let mut packets = Vec::new();
170 let mut offset = 0usize;
171
172 while offset < buf.len() {
173 let remaining = buf.len() - offset;
174 if remaining < RX_DESC_SIZE {
175 break;
176 }
177
178 let desc = &buf[offset..offset + RX_DESC_SIZE];
179 let mut attrib = parse_rx_descriptor(desc)?;
180 let data_start =
181 offset + RX_DESC_SIZE + attrib.drvinfo_sz as usize + attrib.shift_sz as usize;
182 let pkt_offset = RX_DESC_SIZE
183 + attrib.drvinfo_sz as usize
184 + attrib.shift_sz as usize
185 + attrib.pkt_len as usize;
186 if attrib.pkt_len == 0 || pkt_offset > remaining {
187 return Err(AggregateError::InvalidPacketLength {
188 pkt_len: attrib.pkt_len,
189 pkt_offset,
190 remaining,
191 });
192 }
193
194 if attrib.pkt_rpt_type == RxPacketType::NormalRx {
195 let phy_start = offset + RX_DESC_SIZE;
196 let phy_end = phy_start + attrib.drvinfo_sz as usize;
197 parse_phy_status(&mut attrib, &buf[phy_start..phy_end]);
198 }
199
200 let data_end = data_start + attrib.pkt_len as usize;
201 packets.push(RealtekRxPacket {
202 attrib,
203 data: &buf[data_start..data_end],
204 });
205
206 let aligned = round_up_8(pkt_offset);
207 if aligned >= remaining {
208 break;
209 }
210 offset += aligned;
211 }
212
213 Ok(packets)
214}
215
216pub fn parse_c2h_packet(data: &[u8]) -> Option<C2hPacket<'_>> {
217 let (&cmd_id, rest) = data.split_first()?;
218 let seq = rest.first().copied();
219 let payload = if rest.len() > 1 { &rest[1..] } else { rest };
220 Some(C2hPacket {
221 cmd_id,
222 seq,
223 payload,
224 })
225}
226
227pub fn parse_8814_tx_status_reports(data: &[u8]) -> Vec<TxStatusReport8814> {
228 [1usize, 2usize]
229 .into_iter()
230 .filter_map(|offset| parse_8814_tx_status_report_at(data, offset))
231 .collect()
232}
233
234pub fn parse_8814_tx_status_report_at(
235 data: &[u8],
236 header_offset: usize,
237) -> Option<TxStatusReport8814> {
238 let h = data.get(header_offset..header_offset + 6)?;
239 let queue_time_raw = u16::from_le_bytes([h[3], h[4]]);
240 Some(TxStatusReport8814 {
241 header_offset,
242 queue_select: h[0] & 0x1f,
243 packet_broadcast: h[0] & (1 << 5) != 0,
244 lifetime_over: h[0] & (1 << 6) != 0,
245 retry_over: h[0] & (1 << 7) != 0,
246 mac_id: h[1],
247 data_retry_count: h[2] & 0x3f,
248 queue_time_us: u32::from(queue_time_raw) * 256,
249 final_data_rate: h[5],
250 })
251}
252
253const fn round_up_8(value: usize) -> usize {
254 (value + 7) & !7
255}
256
257fn le32(bytes: &[u8], offset: usize) -> u32 {
258 u32::from_le_bytes(
259 bytes[offset..offset + 4]
260 .try_into()
261 .expect("descriptor length checked"),
262 )
263}
264
265const fn bits(word: u32, offset: u8, len: u8) -> u32 {
266 if len == 32 {
267 word
268 } else {
269 (word >> offset) & ((1u32 << len) - 1)
270 }
271}
272
273fn parse_phy_status(attrib: &mut RxPacketAttrib, phy: &[u8]) {
274 if phy.len() < 2 {
275 return;
276 }
277
278 attrib.rssi[0] = phy[0];
279 attrib.rssi[1] = phy[1];
280
281 if phy.len() < 28 {
282 return;
283 }
284
285 attrib.rssi[2] = phy[23];
286 attrib.rssi[3] = phy[24];
287 attrib.snr[0] = phy[15] as i8;
288 attrib.snr[1] = phy[16] as i8;
289 attrib.snr[2] = phy[21] as i8;
290 attrib.snr[3] = phy[22] as i8;
291 attrib.evm[0] = phy[13] as i8;
292 attrib.evm[1] = phy[14] as i8;
293 attrib.evm[2] = phy[19] as i8;
294 attrib.evm[3] = phy[20] as i8;
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 fn put_bits(word: &mut u32, offset: u8, len: u8, value: u32) {
302 let mask = ((1u32 << len) - 1) << offset;
303 *word = (*word & !mask) | ((value << offset) & mask);
304 }
305
306 fn descriptor(pkt_len: u16, drvinfo_units: u8, shift: u8, seq: u16) -> [u8; RX_DESC_SIZE] {
307 let mut desc = [0; RX_DESC_SIZE];
308 let mut d0 = 0u32;
309 put_bits(&mut d0, 0, 14, pkt_len as u32);
310 put_bits(&mut d0, 16, 4, drvinfo_units as u32);
311 put_bits(&mut d0, 24, 2, shift as u32);
312 let mut d2 = 0u32;
313 put_bits(&mut d2, 0, 12, seq as u32);
314 desc[0..4].copy_from_slice(&d0.to_le_bytes());
315 desc[8..12].copy_from_slice(&d2.to_le_bytes());
316 desc
317 }
318
319 #[test]
320 fn parses_single_rx_packet() {
321 let mut aggregate = Vec::new();
322 aggregate.extend_from_slice(&descriptor(4, 0, 0, 77));
323 aggregate.extend_from_slice(&[1, 2, 3, 4]);
324
325 let packets = parse_rx_aggregate(&aggregate).unwrap();
326 assert_eq!(packets.len(), 1);
327 assert_eq!(packets[0].attrib.pkt_len, 4);
328 assert_eq!(packets[0].attrib.seq_num, 77);
329 assert_eq!(packets[0].data, &[1, 2, 3, 4]);
330 }
331
332 #[test]
333 fn parses_c2h_packet_header() {
334 let packet = parse_c2h_packet(&[0x42, 0x7f, 1, 2]).unwrap();
335 assert_eq!(packet.cmd_id, 0x42);
336 assert_eq!(packet.seq, Some(0x7f));
337 assert_eq!(packet.payload, &[1, 2]);
338 }
339
340 #[test]
341 fn parses_8814_tx_status_at_devourer_offsets() {
342 let bytes = [0xaa, 0x83, 0x09, 0x25, 0x34, 0x12, 0x6c, 0xbb];
343 let reports = parse_8814_tx_status_reports(&bytes);
344 assert_eq!(reports.len(), 2);
345 assert_eq!(reports[0].header_offset, 1);
346 assert_eq!(reports[0].queue_select, 3);
347 assert!(reports[0].retry_over);
348 assert_eq!(reports[0].mac_id, 9);
349 assert_eq!(reports[0].data_retry_count, 0x25);
350 assert_eq!(reports[0].queue_time_us, 0x1234 * 256);
351 assert_eq!(reports[0].final_data_rate, 0x6c);
352 }
353
354 #[test]
355 fn advances_by_jaguar_eight_byte_alignment() {
356 let mut aggregate = Vec::new();
357 aggregate.extend_from_slice(&descriptor(5, 0, 0, 1));
358 aggregate.extend_from_slice(&[1, 2, 3, 4, 5]);
359 aggregate.extend_from_slice(&[0, 0, 0]);
360 aggregate.extend_from_slice(&descriptor(3, 0, 0, 2));
361 aggregate.extend_from_slice(&[6, 7, 8]);
362
363 let packets = parse_rx_aggregate(&aggregate).unwrap();
364 assert_eq!(packets.len(), 2);
365 assert_eq!(packets[0].data, &[1, 2, 3, 4, 5]);
366 assert_eq!(packets[1].data, &[6, 7, 8]);
367 }
368}