1use crate::radiotap::{parse_radiotap_tx_info, radiotap_len, ChannelBandwidth, RadiotapError};
2
3pub const TX_DESC_SIZE: usize = 40;
4
5const MGN_1M: u8 = 0x02;
6const MGN_2M: u8 = 0x04;
7const MGN_5_5M: u8 = 0x0b;
8const MGN_6M: u8 = 0x0c;
9const MGN_9M: u8 = 0x12;
10const MGN_11M: u8 = 0x16;
11const MGN_12M: u8 = 0x18;
12const MGN_18M: u8 = 0x24;
13const MGN_24M: u8 = 0x30;
14const MGN_36M: u8 = 0x48;
15const MGN_48M: u8 = 0x60;
16const MGN_54M: u8 = 0x6c;
17const MGN_MCS0: u8 = 0x80;
18const MGN_VHT1SS_MCS0: u8 = 0xa0;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct RealtekTxOptions {
22 pub current_channel: u8,
23 pub is_8814a: bool,
24}
25
26impl Default for RealtekTxOptions {
27 fn default() -> Self {
28 Self {
29 current_channel: 36,
30 is_8814a: false,
31 }
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum RealtekTxError {
37 Radiotap(RadiotapError),
38 PayloadTooLarge,
39}
40
41impl std::fmt::Display for RealtekTxError {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 Self::Radiotap(err) => write!(f, "{err}"),
45 Self::PayloadTooLarge => write!(f, "802.11 TX payload is too large for descriptor"),
46 }
47 }
48}
49
50impl std::error::Error for RealtekTxError {}
51
52impl From<RadiotapError> for RealtekTxError {
53 fn from(value: RadiotapError) -> Self {
54 Self::Radiotap(value)
55 }
56}
57
58pub fn build_usb_tx_frame(
59 radiotap_packet: &[u8],
60 options: RealtekTxOptions,
61) -> Result<Vec<u8>, RealtekTxError> {
62 let rtap_len = radiotap_len(radiotap_packet)?;
63 let tx_info = parse_radiotap_tx_info(radiotap_packet)?;
64 let payload = &radiotap_packet[rtap_len..];
65 if payload.len() > u16::MAX as usize {
66 return Err(RealtekTxError::PayloadTooLarge);
67 }
68
69 let mut fixed_rate = if tx_info.vht {
70 MGN_VHT1SS_MCS0 + ((tx_info.nss - 1) * 10 + tx_info.mcs_index)
71 } else {
72 MGN_MCS0 + tx_info.mcs_index
73 };
74 if options.current_channel > 14 && is_cck_rate(fixed_rate) {
75 fixed_rate = MGN_6M;
76 }
77
78 let mut out = vec![0; TX_DESC_SIZE + payload.len()];
79 set_bits_le32(
80 &mut out,
81 20,
82 5,
83 2,
84 tx_info.bandwidth.realtek_desc_bits() as u32,
85 );
86 set_bits_le32(&mut out, 0, 26, 1, 1);
87 if !options.is_8814a {
88 set_bits_le32(&mut out, 0, 31, 1, 1);
89 }
90 set_bits_le32(&mut out, 0, 0, 16, payload.len() as u32);
91 set_bits_le32(&mut out, 0, 16, 8, TX_DESC_SIZE as u32);
92 set_bits_le32(&mut out, 4, 0, 7, 0x01);
93 set_bits_le32(&mut out, 0, 24, 1, 1);
94 set_bits_le32(&mut out, 4, 16, 5, if tx_info.vht { 9 } else { 8 });
95 set_bits_le32(&mut out, 4, 8, 5, 0x12);
96 set_bits_le32(&mut out, 32, 15, 1, 1);
97 if !options.is_8814a {
98 set_bits_le32(&mut out, 8, 24, 6, 0x3f);
99 }
100 set_bits_le32(&mut out, 24, 0, 12, 0x001);
101 set_bits_le32(&mut out, 16, 17, 1, 1);
102 if !options.is_8814a {
103 set_bits_le32(&mut out, 16, 18, 6, 12);
104 }
105 if tx_info.short_gi {
106 set_bits_le32(&mut out, 20, 4, 1, 1);
107 }
108 set_bits_le32(&mut out, 12, 8, 1, 1);
109 set_bits_le32(&mut out, 16, 0, 7, mrate_to_hw_rate(fixed_rate) as u32);
110 if tx_info.ldpc {
111 set_bits_le32(&mut out, 20, 7, 1, 1);
112 }
113 set_bits_le32(&mut out, 20, 8, 2, (tx_info.stbc & 0x03) as u32);
114 tx_desc_checksum(&mut out[..TX_DESC_SIZE]);
115 out[TX_DESC_SIZE..].copy_from_slice(payload);
116 Ok(out)
117}
118
119fn set_bits_le32(bytes: &mut [u8], offset: usize, bit_offset: u8, bit_len: u8, value: u32) {
120 let mut word = u32::from_le_bytes(
121 bytes[offset..offset + 4]
122 .try_into()
123 .expect("descriptor offset is in range"),
124 );
125 let mask = if bit_len == 32 {
126 u32::MAX
127 } else {
128 ((1u32 << bit_len) - 1) << bit_offset
129 };
130 word = (word & !mask) | ((value << bit_offset) & mask);
131 bytes[offset..offset + 4].copy_from_slice(&word.to_le_bytes());
132}
133
134fn tx_desc_checksum(desc: &mut [u8]) {
135 set_bits_le32(desc, 28, 0, 16, 0);
136 let mut checksum = 0u16;
137 for idx in 0..16 {
138 let offset = idx * 2;
139 checksum ^= u16::from_le_bytes([desc[offset], desc[offset + 1]]);
140 }
141 set_bits_le32(desc, 28, 0, 16, checksum as u32);
142}
143
144const fn is_cck_rate(rate: u8) -> bool {
145 matches!(rate, MGN_1M | MGN_2M | MGN_5_5M | MGN_11M)
146}
147
148const fn mrate_to_hw_rate(rate: u8) -> u8 {
149 match rate {
150 MGN_1M => 0x00,
151 MGN_2M => 0x01,
152 MGN_5_5M => 0x02,
153 MGN_11M => 0x03,
154 MGN_6M => 0x04,
155 MGN_9M => 0x05,
156 MGN_12M => 0x06,
157 MGN_18M => 0x07,
158 MGN_24M => 0x08,
159 MGN_36M => 0x09,
160 MGN_48M => 0x0a,
161 MGN_54M => 0x0b,
162 MGN_MCS0..=0x9f => 0x0c + (rate - MGN_MCS0),
163 MGN_VHT1SS_MCS0..=0xc7 => 0x2c + (rate - MGN_VHT1SS_MCS0),
164 _ => 0x00,
165 }
166}
167
168#[allow(dead_code)]
169const fn bandwidth_from_desc_bits(bits: u8) -> ChannelBandwidth {
170 match bits {
171 1 => ChannelBandwidth::Mhz40,
172 2 => ChannelBandwidth::Mhz80,
173 _ => ChannelBandwidth::Mhz20,
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use crate::ieee80211::build_wfb_header_with_frame_type;
181 use crate::radiotap::{build_radiotap_header, TxRadioParams, FRAME_TYPE_RTS};
182 use crate::ChannelId;
183
184 fn le32(bytes: &[u8], offset: usize) -> u32 {
185 u32::from_le_bytes(bytes[offset..offset + 4].try_into().unwrap())
186 }
187
188 fn bits(word: u32, offset: u8, len: u8) -> u32 {
189 (word >> offset) & ((1u32 << len) - 1)
190 }
191
192 #[test]
193 fn builds_descriptor_and_strips_radiotap() {
194 let params = TxRadioParams::default();
195 let mut packet = build_radiotap_header(params);
196 packet.extend_from_slice(&build_wfb_header_with_frame_type(
197 ChannelId::default_video(),
198 [0x10, 0x00],
199 FRAME_TYPE_RTS,
200 ));
201 packet.extend_from_slice(&[1, 2, 3]);
202
203 let usb = build_usb_tx_frame(&packet, RealtekTxOptions::default()).unwrap();
204 assert_eq!(usb.len(), TX_DESC_SIZE + 27);
205 assert_eq!(bits(le32(&usb, 0), 0, 16), 27);
206 assert_eq!(bits(le32(&usb, 0), 16, 8), TX_DESC_SIZE as u32);
207 assert_eq!(bits(le32(&usb, 16), 0, 7), 0x0c);
208 assert_eq!(
209 &usb[TX_DESC_SIZE..TX_DESC_SIZE + 2],
210 &[FRAME_TYPE_RTS, 0x01]
211 );
212 }
213}