1pub const RX_DESC_SIZE: usize = 24;
3pub const DEFAULT_RX_TRANSFER_SIZE: usize = 32 * 1024;
5
6#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
12pub enum RxDescriptorKind {
13 #[default]
15 Jaguar1,
16 Jaguar3,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum RxPacketType {
23 NormalRx,
25 C2hPacket,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub struct RxPacketAttrib {
32 pub pkt_len: u16,
34 pub physt: bool,
36 pub drvinfo_sz: u8,
38 pub shift_sz: u8,
40 pub qos: bool,
42 pub priority: u8,
44 pub mdata: bool,
46 pub seq_num: u16,
48 pub frag_num: u8,
50 pub mfrag: bool,
52 pub bdecrypted: bool,
54 pub encrypt: u8,
56 pub crc_err: bool,
58 pub icv_err: bool,
60 pub tsfl: u32,
62 pub data_rate: u8,
64 pub bw: u8,
66 pub stbc: u8,
68 pub ldpc: u8,
70 pub sgi: u8,
72 pub scrambler: u8,
74 pub rssi: [u8; 4],
76 pub snr: [i8; 4],
78 pub evm: [i8; 4],
80 pub pkt_rpt_type: RxPacketType,
82}
83
84impl Default for RxPacketAttrib {
85 fn default() -> Self {
86 Self {
87 pkt_len: 0,
88 physt: false,
89 drvinfo_sz: 0,
90 shift_sz: 0,
91 qos: false,
92 priority: 0,
93 mdata: false,
94 seq_num: 0,
95 frag_num: 0,
96 mfrag: false,
97 bdecrypted: false,
98 encrypt: 0,
99 crc_err: false,
100 icv_err: false,
101 tsfl: 0,
102 data_rate: 0,
103 bw: 0,
104 stbc: 0,
105 ldpc: 0,
106 sgi: 0,
107 scrambler: 0,
108 rssi: [0; 4],
109 snr: [0; 4],
110 evm: [0; 4],
111 pkt_rpt_type: RxPacketType::NormalRx,
112 }
113 }
114}
115
116#[derive(Debug, Clone, Copy)]
118pub struct RealtekRxPacket<'a> {
119 pub attrib: RxPacketAttrib,
121 pub data: &'a [u8],
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub struct C2hPacket<'a> {
128 pub cmd_id: u8,
130 pub seq: Option<u8>,
132 pub payload: &'a [u8],
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub struct TxStatusReport8814 {
139 pub header_offset: usize,
141 pub queue_select: u8,
143 pub packet_broadcast: bool,
145 pub lifetime_over: bool,
147 pub retry_over: bool,
149 pub mac_id: u8,
151 pub data_retry_count: u8,
153 pub queue_time_us: u32,
155 pub final_data_rate: u8,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
161pub enum AggregateError {
162 DescriptorTooShort,
164 InvalidPacketLength {
166 pkt_len: u16,
168 pkt_offset: usize,
170 remaining: usize,
172 },
173}
174
175impl std::fmt::Display for AggregateError {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 match self {
178 Self::DescriptorTooShort => write!(f, "RX descriptor is shorter than {RX_DESC_SIZE} bytes"),
179 Self::InvalidPacketLength {
180 pkt_len,
181 pkt_offset,
182 remaining,
183 } => write!(
184 f,
185 "invalid RX packet length: pkt_len={pkt_len}, pkt_offset={pkt_offset}, remaining={remaining}"
186 ),
187 }
188 }
189}
190
191impl std::error::Error for AggregateError {}
192
193pub fn parse_rx_descriptor(desc: &[u8]) -> Result<RxPacketAttrib, AggregateError> {
195 parse_rx_descriptor_with_kind(desc, RxDescriptorKind::Jaguar1)
196}
197
198pub fn parse_rx_descriptor_with_kind(
200 desc: &[u8],
201 kind: RxDescriptorKind,
202) -> Result<RxPacketAttrib, AggregateError> {
203 if desc.len() < RX_DESC_SIZE {
204 return Err(AggregateError::DescriptorTooShort);
205 }
206
207 let d0 = le32(desc, 0);
208 let d1 = le32(desc, 4);
209 let d2 = le32(desc, 8);
210 let d3 = le32(desc, 12);
211 let d4 = le32(desc, 16);
212 let d5 = le32(desc, 20);
213
214 let mut attrib = RxPacketAttrib {
215 pkt_len: bits(d0, 0, 14) as u16,
216 crc_err: bits(d0, 14, 1) != 0,
217 icv_err: bits(d0, 15, 1) != 0,
218 drvinfo_sz: (bits(d0, 16, 4) * 8) as u8,
219 shift_sz: bits(d0, 24, 2) as u8,
220 physt: bits(d0, 26, 1) != 0,
221 data_rate: bits(d3, 0, 7) as u8,
222 pkt_rpt_type: if bits(d2, 28, 1) != 0 {
223 RxPacketType::C2hPacket
224 } else {
225 RxPacketType::NormalRx
226 },
227 ..Default::default()
228 };
229
230 if kind == RxDescriptorKind::Jaguar1 {
231 attrib.encrypt = bits(d0, 20, 3) as u8;
232 attrib.qos = bits(d0, 23, 1) != 0;
233 attrib.bdecrypted = bits(d0, 27, 1) == 0;
234 attrib.priority = bits(d1, 8, 4) as u8;
235 attrib.mdata = bits(d1, 26, 1) != 0;
236 attrib.mfrag = bits(d1, 27, 1) != 0;
237 attrib.seq_num = bits(d2, 0, 12) as u16;
238 attrib.frag_num = bits(d2, 12, 4) as u8;
239 attrib.sgi = bits(d4, 0, 1) as u8;
240 attrib.ldpc = bits(d4, 1, 1) as u8;
241 attrib.stbc = bits(d4, 2, 1) as u8;
242 attrib.bw = bits(d4, 4, 2) as u8;
243 attrib.scrambler = bits(d4, 9, 7) as u8;
244 attrib.tsfl = d5;
245 }
246
247 Ok(attrib)
248}
249
250pub fn parse_rx_aggregate(buf: &[u8]) -> Result<Vec<RealtekRxPacket<'_>>, AggregateError> {
256 parse_rx_aggregate_with_kind(buf, RxDescriptorKind::Jaguar1)
257}
258
259pub fn parse_rx_aggregate_with_kind(
261 buf: &[u8],
262 kind: RxDescriptorKind,
263) -> Result<Vec<RealtekRxPacket<'_>>, AggregateError> {
264 let mut packets = Vec::new();
265 let mut offset = 0usize;
266
267 while offset < buf.len() {
268 let remaining = buf.len() - offset;
269 if remaining < RX_DESC_SIZE {
270 break;
271 }
272
273 let desc = &buf[offset..offset + RX_DESC_SIZE];
274 let mut attrib = parse_rx_descriptor_with_kind(desc, kind)?;
275 let data_start =
276 offset + RX_DESC_SIZE + attrib.drvinfo_sz as usize + attrib.shift_sz as usize;
277 let pkt_offset = RX_DESC_SIZE
278 + attrib.drvinfo_sz as usize
279 + attrib.shift_sz as usize
280 + attrib.pkt_len as usize;
281 if attrib.pkt_len == 0 || pkt_offset > remaining {
282 return Err(AggregateError::InvalidPacketLength {
283 pkt_len: attrib.pkt_len,
284 pkt_offset,
285 remaining,
286 });
287 }
288
289 if attrib.pkt_rpt_type == RxPacketType::NormalRx {
290 let phy_start = offset + RX_DESC_SIZE;
291 let phy_end = phy_start + attrib.drvinfo_sz as usize;
292 parse_phy_status(&mut attrib, &buf[phy_start..phy_end]);
293 }
294
295 let data_end = data_start + attrib.pkt_len as usize;
296 packets.push(RealtekRxPacket {
297 attrib,
298 data: &buf[data_start..data_end],
299 });
300
301 let aligned = round_up_8(pkt_offset);
302 if aligned >= remaining {
303 break;
304 }
305 offset += aligned;
306 }
307
308 Ok(packets)
309}
310
311pub fn parse_c2h_packet(data: &[u8]) -> Option<C2hPacket<'_>> {
313 let (&cmd_id, rest) = data.split_first()?;
314 let seq = rest.first().copied();
315 let payload = if rest.len() > 1 { &rest[1..] } else { rest };
316 Some(C2hPacket {
317 cmd_id,
318 seq,
319 payload,
320 })
321}
322
323pub fn parse_8814_tx_status_reports(data: &[u8]) -> Vec<TxStatusReport8814> {
325 [1usize, 2usize]
326 .into_iter()
327 .filter_map(|offset| parse_8814_tx_status_report_at(data, offset))
328 .collect()
329}
330
331pub fn parse_8814_tx_status_report_at(
333 data: &[u8],
334 header_offset: usize,
335) -> Option<TxStatusReport8814> {
336 let h = data.get(header_offset..header_offset + 6)?;
337 let queue_time_raw = u16::from_le_bytes([h[3], h[4]]);
338 Some(TxStatusReport8814 {
339 header_offset,
340 queue_select: h[0] & 0x1f,
341 packet_broadcast: h[0] & (1 << 5) != 0,
342 lifetime_over: h[0] & (1 << 6) != 0,
343 retry_over: h[0] & (1 << 7) != 0,
344 mac_id: h[1],
345 data_retry_count: h[2] & 0x3f,
346 queue_time_us: u32::from(queue_time_raw) * 256,
347 final_data_rate: h[5],
348 })
349}
350
351const fn round_up_8(value: usize) -> usize {
352 (value + 7) & !7
353}
354
355fn le32(bytes: &[u8], offset: usize) -> u32 {
356 u32::from_le_bytes(
357 bytes[offset..offset + 4]
358 .try_into()
359 .expect("descriptor length checked"),
360 )
361}
362
363const fn bits(word: u32, offset: u8, len: u8) -> u32 {
364 if len == 32 {
365 word
366 } else {
367 (word >> offset) & ((1u32 << len) - 1)
368 }
369}
370
371fn parse_phy_status(attrib: &mut RxPacketAttrib, phy: &[u8]) {
372 if phy.len() < 2 {
373 return;
374 }
375
376 attrib.rssi[0] = phy[0];
377 attrib.rssi[1] = phy[1];
378
379 if phy.len() < 28 {
380 return;
381 }
382
383 attrib.rssi[2] = phy[23];
384 attrib.rssi[3] = phy[24];
385 attrib.snr[0] = phy[15] as i8;
386 attrib.snr[1] = phy[16] as i8;
387 attrib.snr[2] = phy[21] as i8;
388 attrib.snr[3] = phy[22] as i8;
389 attrib.evm[0] = phy[13] as i8;
390 attrib.evm[1] = phy[14] as i8;
391 attrib.evm[2] = phy[19] as i8;
392 attrib.evm[3] = phy[20] as i8;
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398
399 fn put_bits(word: &mut u32, offset: u8, len: u8, value: u32) {
400 let mask = ((1u32 << len) - 1) << offset;
401 *word = (*word & !mask) | ((value << offset) & mask);
402 }
403
404 fn descriptor(pkt_len: u16, drvinfo_units: u8, shift: u8, seq: u16) -> [u8; RX_DESC_SIZE] {
405 let mut desc = [0; RX_DESC_SIZE];
406 let mut d0 = 0u32;
407 put_bits(&mut d0, 0, 14, pkt_len as u32);
408 put_bits(&mut d0, 16, 4, drvinfo_units as u32);
409 put_bits(&mut d0, 24, 2, shift as u32);
410 let mut d2 = 0u32;
411 put_bits(&mut d2, 0, 12, seq as u32);
412 desc[0..4].copy_from_slice(&d0.to_le_bytes());
413 desc[8..12].copy_from_slice(&d2.to_le_bytes());
414 desc
415 }
416
417 fn jaguar3_descriptor(
418 pkt_len: u16,
419 drvinfo_units: u8,
420 shift: u8,
421 rx_rate: u8,
422 c2h: bool,
423 ) -> [u8; RX_DESC_SIZE] {
424 let mut desc = [0; RX_DESC_SIZE];
425 let mut d0 = 0u32;
426 put_bits(&mut d0, 0, 14, pkt_len as u32);
427 put_bits(&mut d0, 14, 1, 1);
428 put_bits(&mut d0, 15, 1, 1);
429 put_bits(&mut d0, 16, 4, drvinfo_units as u32);
430 put_bits(&mut d0, 24, 2, shift as u32);
431 let mut d2 = 0u32;
432 put_bits(&mut d2, 28, 1, u32::from(c2h));
433 let mut d3 = 0u32;
434 put_bits(&mut d3, 0, 7, rx_rate as u32);
435 desc[0..4].copy_from_slice(&d0.to_le_bytes());
436 desc[8..12].copy_from_slice(&d2.to_le_bytes());
437 desc[12..16].copy_from_slice(&d3.to_le_bytes());
438 desc
439 }
440
441 #[test]
442 fn parses_single_rx_packet() {
443 let mut aggregate = Vec::new();
444 aggregate.extend_from_slice(&descriptor(4, 0, 0, 77));
445 aggregate.extend_from_slice(&[1, 2, 3, 4]);
446
447 let packets = parse_rx_aggregate(&aggregate).unwrap();
448 assert_eq!(packets.len(), 1);
449 assert_eq!(packets[0].attrib.pkt_len, 4);
450 assert_eq!(packets[0].attrib.seq_num, 77);
451 assert_eq!(packets[0].data, &[1, 2, 3, 4]);
452 }
453
454 #[test]
455 fn ignores_short_tail_after_aligned_packet() {
456 let mut aggregate = Vec::new();
457 aggregate.extend_from_slice(&descriptor(4, 0, 0, 77));
458 aggregate.extend_from_slice(&[1, 2, 3, 4]);
459 aggregate.extend_from_slice(&[0, 0, 0, 0]);
460 aggregate.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
461
462 let packets = parse_rx_aggregate(&aggregate).unwrap();
463 assert_eq!(packets.len(), 1);
464 assert_eq!(packets[0].data, &[1, 2, 3, 4]);
465 }
466
467 #[test]
468 fn rejects_zero_length_descriptor() {
469 let aggregate = descriptor(0, 0, 0, 1);
470
471 let err = parse_rx_aggregate(&aggregate).unwrap_err();
472 assert_eq!(
473 err,
474 AggregateError::InvalidPacketLength {
475 pkt_len: 0,
476 pkt_offset: RX_DESC_SIZE,
477 remaining: RX_DESC_SIZE,
478 }
479 );
480 }
481
482 #[test]
483 fn rejects_descriptor_payload_past_transfer() {
484 let mut aggregate = Vec::new();
485 aggregate.extend_from_slice(&descriptor(8, 0, 0, 1));
486 aggregate.extend_from_slice(&[1, 2, 3, 4]);
487
488 let err = parse_rx_aggregate(&aggregate).unwrap_err();
489 assert_eq!(
490 err,
491 AggregateError::InvalidPacketLength {
492 pkt_len: 8,
493 pkt_offset: RX_DESC_SIZE + 8,
494 remaining: RX_DESC_SIZE + 4,
495 }
496 );
497 }
498
499 #[test]
500 fn surfaces_crc_and_icv_flags_from_jaguar1_descriptor() {
501 let mut desc = descriptor(4, 0, 0, 9);
502 let mut d0 = u32::from_le_bytes(desc[0..4].try_into().unwrap());
503 put_bits(&mut d0, 14, 1, 1);
504 put_bits(&mut d0, 15, 1, 1);
505 desc[0..4].copy_from_slice(&d0.to_le_bytes());
506
507 let mut aggregate = Vec::new();
508 aggregate.extend_from_slice(&desc);
509 aggregate.extend_from_slice(&[1, 2, 3, 4]);
510
511 let packets = parse_rx_aggregate(&aggregate).unwrap();
512 assert!(packets[0].attrib.crc_err);
513 assert!(packets[0].attrib.icv_err);
514 assert_eq!(packets[0].data, &[1, 2, 3, 4]);
515 }
516
517 #[test]
518 fn does_not_decode_payload_as_phy_status_when_drvinfo_absent() {
519 let mut payload = vec![0u8; 32];
520 payload[0] = 55;
521 payload[1] = 66;
522 payload[13] = 0x80;
523 payload[15] = 0x7f;
524 payload[23] = 77;
525
526 let mut aggregate = Vec::new();
527 aggregate.extend_from_slice(&descriptor(payload.len() as u16, 0, 0, 1));
528 aggregate.extend_from_slice(&payload);
529
530 let packets = parse_rx_aggregate(&aggregate).unwrap();
531 assert_eq!(packets[0].attrib.rssi, [0; 4]);
532 assert_eq!(packets[0].attrib.snr, [0; 4]);
533 assert_eq!(packets[0].attrib.evm, [0; 4]);
534 assert_eq!(packets[0].data, payload);
535 }
536
537 #[test]
538 fn parses_phy_status_only_from_driver_info_bytes() {
539 let mut phy = vec![0u8; 32];
540 phy[0] = 42;
541 phy[1] = 43;
542 phy[13] = 0xf0;
543 phy[14] = 0x0f;
544 phy[15] = 0x80;
545 phy[16] = 0x7f;
546 phy[19] = 0xee;
547 phy[20] = 0xdd;
548 phy[21] = 0xcc;
549 phy[22] = 0xbb;
550 phy[23] = 44;
551 phy[24] = 45;
552
553 let mut aggregate = Vec::new();
554 aggregate.extend_from_slice(&descriptor(3, 4, 1, 1));
555 aggregate.extend_from_slice(&phy);
556 aggregate.push(0xaa);
557 aggregate.extend_from_slice(&[1, 2, 3]);
558
559 let packets = parse_rx_aggregate(&aggregate).unwrap();
560 assert_eq!(packets[0].attrib.drvinfo_sz, 32);
561 assert_eq!(packets[0].attrib.shift_sz, 1);
562 assert_eq!(packets[0].attrib.rssi, [42, 43, 44, 45]);
563 assert_eq!(packets[0].attrib.snr, [-128, 127, -52, -69]);
564 assert_eq!(packets[0].attrib.evm, [-16, 15, -18, -35]);
565 assert_eq!(packets[0].data, &[1, 2, 3]);
566 }
567
568 #[test]
569 fn parses_c2h_packet_header() {
570 let packet = parse_c2h_packet(&[0x42, 0x7f, 1, 2]).unwrap();
571 assert_eq!(packet.cmd_id, 0x42);
572 assert_eq!(packet.seq, Some(0x7f));
573 assert_eq!(packet.payload, &[1, 2]);
574 }
575
576 #[test]
577 fn parses_8814_tx_status_at_devourer_offsets() {
578 let bytes = [0xaa, 0x83, 0x09, 0x25, 0x34, 0x12, 0x6c, 0xbb];
579 let reports = parse_8814_tx_status_reports(&bytes);
580 assert_eq!(reports.len(), 2);
581 assert_eq!(reports[0].header_offset, 1);
582 assert_eq!(reports[0].queue_select, 3);
583 assert!(reports[0].retry_over);
584 assert_eq!(reports[0].mac_id, 9);
585 assert_eq!(reports[0].data_retry_count, 0x25);
586 assert_eq!(reports[0].queue_time_us, 0x1234 * 256);
587 assert_eq!(reports[0].final_data_rate, 0x6c);
588 }
589
590 #[test]
591 fn advances_by_jaguar_eight_byte_alignment() {
592 let mut aggregate = Vec::new();
593 aggregate.extend_from_slice(&descriptor(5, 0, 0, 1));
594 aggregate.extend_from_slice(&[1, 2, 3, 4, 5]);
595 aggregate.extend_from_slice(&[0, 0, 0]);
596 aggregate.extend_from_slice(&descriptor(3, 0, 0, 2));
597 aggregate.extend_from_slice(&[6, 7, 8]);
598
599 let packets = parse_rx_aggregate(&aggregate).unwrap();
600 assert_eq!(packets.len(), 2);
601 assert_eq!(packets[0].data, &[1, 2, 3, 4, 5]);
602 assert_eq!(packets[1].data, &[6, 7, 8]);
603 }
604
605 #[test]
606 fn jaguar3_descriptor_matches_devourer_field_positions() {
607 let mut aggregate = Vec::new();
608 aggregate.extend_from_slice(&jaguar3_descriptor(4, 1, 2, 0x2c, false));
609 aggregate.extend_from_slice(&[41, 42, 0, 0, 0, 0, 0, 0]);
610 aggregate.extend_from_slice(&[0xaa, 0xbb]);
611 aggregate.extend_from_slice(&[1, 2, 3, 4]);
612
613 let packets = parse_rx_aggregate_with_kind(&aggregate, RxDescriptorKind::Jaguar3).unwrap();
614 assert_eq!(packets.len(), 1);
615 assert_eq!(packets[0].attrib.pkt_len, 4);
616 assert_eq!(packets[0].attrib.drvinfo_sz, 8);
617 assert_eq!(packets[0].attrib.shift_sz, 2);
618 assert_eq!(packets[0].attrib.data_rate, 0x2c);
619 assert!(packets[0].attrib.crc_err);
620 assert!(packets[0].attrib.icv_err);
621 assert_eq!(packets[0].attrib.pkt_rpt_type, RxPacketType::NormalRx);
622 assert_eq!(packets[0].data, &[1, 2, 3, 4]);
623 }
624
625 #[test]
626 fn jaguar3_c2h_bit_is_report_type() {
627 let mut aggregate = Vec::new();
628 aggregate.extend_from_slice(&jaguar3_descriptor(3, 0, 0, 0, true));
629 aggregate.extend_from_slice(&[0x61, 0x01, 0x02]);
630
631 let packets = parse_rx_aggregate_with_kind(&aggregate, RxDescriptorKind::Jaguar3).unwrap();
632 assert_eq!(packets.len(), 1);
633 assert_eq!(packets[0].attrib.pkt_rpt_type, RxPacketType::C2hPacket);
634 assert_eq!(packets[0].data, &[0x61, 0x01, 0x02]);
635 }
636
637 #[test]
638 fn c2h_payload_respects_drvinfo_and_shift_offsets() {
639 let mut desc = descriptor(3, 1, 2, 0);
640 let mut d2 = u32::from_le_bytes(desc[8..12].try_into().unwrap());
641 put_bits(&mut d2, 28, 1, 1);
642 desc[8..12].copy_from_slice(&d2.to_le_bytes());
643
644 let mut aggregate = Vec::new();
645 aggregate.extend_from_slice(&desc);
646 aggregate.extend_from_slice(&[0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48]);
647 aggregate.extend_from_slice(&[0xaa, 0xbb]);
648 aggregate.extend_from_slice(&[0x61, 0x07, 0xcc]);
649
650 let packets = parse_rx_aggregate(&aggregate).unwrap();
651 assert_eq!(packets.len(), 1);
652 assert_eq!(packets[0].attrib.pkt_rpt_type, RxPacketType::C2hPacket);
653 assert_eq!(packets[0].attrib.drvinfo_sz, 8);
654 assert_eq!(packets[0].attrib.shift_sz, 2);
655 assert_eq!(packets[0].data, &[0x61, 0x07, 0xcc]);
656 assert_eq!(
657 parse_c2h_packet(packets[0].data),
658 Some(C2hPacket {
659 cmd_id: 0x61,
660 seq: Some(0x07),
661 payload: &[0xcc],
662 })
663 );
664 }
665}