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