1use crate::radiotap::{
2 parse_radiotap_tx_mode, radiotap_len, ChannelBandwidth, RadiotapError, TxMode, TxModeKind,
3};
4
5pub const TX_DESC_SIZE: usize = 40;
6
7const MGN_1M: u8 = 0x02;
8const MGN_2M: u8 = 0x04;
9const MGN_5_5M: u8 = 0x0b;
10const MGN_6M: u8 = 0x0c;
11const MGN_9M: u8 = 0x12;
12const MGN_11M: u8 = 0x16;
13const MGN_12M: u8 = 0x18;
14const MGN_18M: u8 = 0x24;
15const MGN_24M: u8 = 0x30;
16const MGN_36M: u8 = 0x48;
17const MGN_48M: u8 = 0x60;
18const MGN_54M: u8 = 0x6c;
19const MGN_MCS0: u8 = 0x80;
20const MGN_VHT1SS_MCS0: u8 = 0xa0;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct RealtekTxOptions {
24 pub current_channel: u8,
25 pub is_8814a: bool,
26 pub legacy_8812_descriptor: bool,
27 pub tx_mode_default: Option<TxMode>,
28}
29
30impl Default for RealtekTxOptions {
31 fn default() -> Self {
32 Self {
33 current_channel: 36,
34 is_8814a: false,
35 legacy_8812_descriptor: false,
36 tx_mode_default: None,
37 }
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum RealtekTxError {
43 Radiotap(RadiotapError),
44 PayloadTooLarge,
45}
46
47impl std::fmt::Display for RealtekTxError {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 Self::Radiotap(err) => write!(f, "{err}"),
51 Self::PayloadTooLarge => write!(f, "802.11 TX payload is too large for descriptor"),
52 }
53 }
54}
55
56impl std::error::Error for RealtekTxError {}
57
58impl From<RadiotapError> for RealtekTxError {
59 fn from(value: RadiotapError) -> Self {
60 Self::Radiotap(value)
61 }
62}
63
64pub fn build_usb_tx_frame(
65 radiotap_packet: &[u8],
66 options: RealtekTxOptions,
67) -> Result<Vec<u8>, RealtekTxError> {
68 let rtap_len = radiotap_len(radiotap_packet)?;
69 let tx_mode = parse_radiotap_tx_mode(radiotap_packet)?
70 .or(options.tx_mode_default)
71 .unwrap_or_else(TxMode::legacy_1m);
72 let payload = &radiotap_packet[rtap_len..];
73 if payload.len() > u16::MAX as usize {
74 return Err(RealtekTxError::PayloadTooLarge);
75 }
76
77 let mut fixed_rate = tx_mode_fixed_rate(tx_mode);
78 if options.current_channel > 14 && is_cck_rate(fixed_rate) {
79 fixed_rate = MGN_6M;
80 }
81
82 let mut out = vec![0; TX_DESC_SIZE + payload.len()];
83 set_bits_le32(
84 &mut out,
85 20,
86 5,
87 2,
88 tx_mode.bandwidth.realtek_desc_bits() as u32,
89 );
90 let use_8814_descriptor_exceptions = options.is_8814a && !options.legacy_8812_descriptor;
91
92 set_bits_le32(&mut out, 0, 26, 1, 1);
93 if !use_8814_descriptor_exceptions {
94 set_bits_le32(&mut out, 0, 31, 1, 1);
95 }
96 set_bits_le32(&mut out, 0, 0, 16, payload.len() as u32);
97 set_bits_le32(&mut out, 0, 16, 8, TX_DESC_SIZE as u32);
98 set_bits_le32(&mut out, 4, 0, 7, 0x01);
99 set_bits_le32(&mut out, 0, 24, 1, 1);
100 set_bits_le32(
101 &mut out,
102 4,
103 16,
104 5,
105 if tx_mode.kind == TxModeKind::Vht {
106 9
107 } else {
108 8
109 },
110 );
111 set_bits_le32(&mut out, 4, 8, 5, 0x12);
112 if use_8814_descriptor_exceptions {
113 set_bits_le32(&mut out, 24, 0, 12, 0x001);
114 set_bits_le32(&mut out, 32, 15, 1, 1);
115 } else {
116 set_bits_le32(&mut out, 8, 24, 6, 0x3f);
117 set_bits_le32(&mut out, 16, 18, 6, 12);
118 set_bits_le32(&mut out, 24, 0, 12, 0x001);
119 set_bits_le32(&mut out, 32, 15, 1, 1);
120 }
121 set_bits_le32(&mut out, 16, 17, 1, 1);
122 if tx_mode.short_gi {
123 set_bits_le32(&mut out, 20, 4, 1, 1);
124 }
125 set_bits_le32(&mut out, 12, 8, 1, 1);
126 set_bits_le32(&mut out, 16, 0, 7, mrate_to_hw_rate(fixed_rate) as u32);
127 if tx_mode.ldpc {
128 set_bits_le32(&mut out, 20, 7, 1, 1);
129 }
130 set_bits_le32(&mut out, 20, 8, 2, u32::from(tx_mode.stbc));
131 tx_desc_checksum(&mut out[..TX_DESC_SIZE]);
132 out[TX_DESC_SIZE..].copy_from_slice(payload);
133 Ok(out)
134}
135
136fn tx_mode_fixed_rate(mode: TxMode) -> u8 {
137 match mode.kind {
138 TxModeKind::Legacy => mode.legacy_rate_500kbps,
139 TxModeKind::Ht => MGN_MCS0 + mode.ht_mcs,
140 TxModeKind::Vht => {
141 MGN_VHT1SS_MCS0 + ((mode.vht_nss.clamp(1, 4) - 1) * 10 + mode.vht_mcs.min(9))
142 }
143 }
144}
145
146fn set_bits_le32(bytes: &mut [u8], offset: usize, bit_offset: u8, bit_len: u8, value: u32) {
147 let mut word = u32::from_le_bytes(
148 bytes[offset..offset + 4]
149 .try_into()
150 .expect("descriptor offset is in range"),
151 );
152 let mask = if bit_len == 32 {
153 u32::MAX
154 } else {
155 ((1u32 << bit_len) - 1) << bit_offset
156 };
157 word = (word & !mask) | ((value << bit_offset) & mask);
158 bytes[offset..offset + 4].copy_from_slice(&word.to_le_bytes());
159}
160
161fn tx_desc_checksum(desc: &mut [u8]) {
162 set_bits_le32(desc, 28, 0, 16, 0);
163 let mut checksum = 0u16;
164 for idx in 0..16 {
165 let offset = idx * 2;
166 checksum ^= u16::from_le_bytes([desc[offset], desc[offset + 1]]);
167 }
168 set_bits_le32(desc, 28, 0, 16, checksum as u32);
169}
170
171const fn is_cck_rate(rate: u8) -> bool {
172 matches!(rate, MGN_1M | MGN_2M | MGN_5_5M | MGN_11M)
173}
174
175const fn mrate_to_hw_rate(rate: u8) -> u8 {
176 match rate {
177 MGN_1M => 0x00,
178 MGN_2M => 0x01,
179 MGN_5_5M => 0x02,
180 MGN_11M => 0x03,
181 MGN_6M => 0x04,
182 MGN_9M => 0x05,
183 MGN_12M => 0x06,
184 MGN_18M => 0x07,
185 MGN_24M => 0x08,
186 MGN_36M => 0x09,
187 MGN_48M => 0x0a,
188 MGN_54M => 0x0b,
189 MGN_MCS0..=0x9f => 0x0c + (rate - MGN_MCS0),
190 MGN_VHT1SS_MCS0..=0xc7 => 0x2c + (rate - MGN_VHT1SS_MCS0),
191 _ => 0x00,
192 }
193}
194
195#[allow(dead_code)]
196const fn bandwidth_from_desc_bits(bits: u8) -> ChannelBandwidth {
197 match bits {
198 1 => ChannelBandwidth::Mhz40,
199 2 => ChannelBandwidth::Mhz80,
200 _ => ChannelBandwidth::Mhz20,
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use crate::ieee80211::build_wfb_header_with_frame_type;
208 use crate::radiotap::{build_radiotap_header, TxRadioParams, FRAME_TYPE_RTS};
209 use crate::ChannelId;
210
211 fn le32(bytes: &[u8], offset: usize) -> u32 {
212 u32::from_le_bytes(bytes[offset..offset + 4].try_into().unwrap())
213 }
214
215 fn bits(word: u32, offset: u8, len: u8) -> u32 {
216 (word >> offset) & ((1u32 << len) - 1)
217 }
218
219 #[test]
220 fn builds_descriptor_and_strips_radiotap() {
221 let params = TxRadioParams::default();
222 let mut packet = build_radiotap_header(params);
223 packet.extend_from_slice(&build_wfb_header_with_frame_type(
224 ChannelId::default_video(),
225 [0x10, 0x00],
226 FRAME_TYPE_RTS,
227 ));
228 packet.extend_from_slice(&[1, 2, 3]);
229
230 let usb = build_usb_tx_frame(&packet, RealtekTxOptions::default()).unwrap();
231 assert_eq!(usb.len(), TX_DESC_SIZE + 27);
232 assert_eq!(bits(le32(&usb, 0), 0, 16), 27);
233 assert_eq!(bits(le32(&usb, 0), 16, 8), TX_DESC_SIZE as u32);
234 assert_eq!(bits(le32(&usb, 0), 26, 1), 1);
235 assert_eq!(bits(le32(&usb, 0), 27, 1), 0);
236 assert_eq!(bits(le32(&usb, 0), 31, 1), 1);
237 assert_eq!(bits(le32(&usb, 4), 16, 5), 8);
238 assert_eq!(bits(le32(&usb, 8), 24, 6), 0x3f);
239 assert_eq!(bits(le32(&usb, 16), 18, 6), 12);
240 assert_eq!(bits(le32(&usb, 16), 0, 7), 0x0c);
241 assert_eq!(bits(le32(&usb, 24), 0, 12), 1);
242 assert_eq!(bits(le32(&usb, 32), 15, 1), 1);
243 assert_eq!(
244 &usb[TX_DESC_SIZE..TX_DESC_SIZE + 2],
245 &[FRAME_TYPE_RTS, 0x01]
246 );
247 }
248
249 #[test]
250 fn rtl8812_descriptor_copies_80211_sequence() {
251 let params = TxRadioParams::default();
252 let mut packet = build_radiotap_header(params);
253 let mut frame = build_wfb_header_with_frame_type(
254 ChannelId::default_video(),
255 [0x10, 0x00],
256 FRAME_TYPE_RTS,
257 );
258 frame[22] = 0x50;
259 frame[23] = 0x12;
260 packet.extend_from_slice(&frame);
261
262 let usb = build_usb_tx_frame(&packet, RealtekTxOptions::default()).unwrap();
263 assert_eq!(bits(le32(&usb, 36), 12, 12), 0);
264 assert_eq!(bits(le32(&usb, 32), 15, 1), 1);
265 }
266
267 #[test]
268 fn rtl8814_descriptor_keeps_kernel_matching_exceptions() {
269 let params = TxRadioParams::default();
270 let mut packet = build_radiotap_header(params);
271 packet.extend_from_slice(&build_wfb_header_with_frame_type(
272 ChannelId::default_video(),
273 [0x10, 0x00],
274 FRAME_TYPE_RTS,
275 ));
276
277 let usb = build_usb_tx_frame(
278 &packet,
279 RealtekTxOptions {
280 is_8814a: true,
281 ..RealtekTxOptions::default()
282 },
283 )
284 .unwrap();
285 assert_eq!(bits(le32(&usb, 0), 27, 1), 0);
286 assert_eq!(bits(le32(&usb, 0), 31, 1), 0);
287 assert_eq!(bits(le32(&usb, 24), 0, 12), 1);
288 assert_eq!(bits(le32(&usb, 32), 15, 1), 1);
289 assert_eq!(bits(le32(&usb, 8), 24, 6), 0);
290 assert_eq!(bits(le32(&usb, 16), 18, 6), 0);
291 }
292
293 #[test]
294 fn rtl8814_descriptor_can_use_legacy_8812_shape() {
295 let params = TxRadioParams::default();
296 let mut packet = build_radiotap_header(params);
297 packet.extend_from_slice(&build_wfb_header_with_frame_type(
298 ChannelId::default_video(),
299 [0x10, 0x00],
300 FRAME_TYPE_RTS,
301 ));
302
303 let usb = build_usb_tx_frame(
304 &packet,
305 RealtekTxOptions {
306 is_8814a: true,
307 legacy_8812_descriptor: true,
308 ..RealtekTxOptions::default()
309 },
310 )
311 .unwrap();
312 assert_eq!(bits(le32(&usb, 0), 31, 1), 1);
313 assert_eq!(bits(le32(&usb, 8), 24, 6), 0x3f);
314 assert_eq!(bits(le32(&usb, 16), 18, 6), 12);
315 }
316
317 #[test]
318 fn rateless_radiotap_uses_default_tx_mode() {
319 let mut packet = vec![
320 0x00, 0x00, 0x0c, 0x00, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
321 ];
322 packet.extend_from_slice(&build_wfb_header_with_frame_type(
323 ChannelId::default_video(),
324 [0x10, 0x00],
325 FRAME_TYPE_RTS,
326 ));
327
328 let mut mode = TxMode::ht(5);
329 mode.bandwidth = ChannelBandwidth::Mhz40;
330 mode.short_gi = true;
331 let usb = build_usb_tx_frame(
332 &packet,
333 RealtekTxOptions {
334 tx_mode_default: Some(mode),
335 ..RealtekTxOptions::default()
336 },
337 )
338 .unwrap();
339 assert_eq!(bits(le32(&usb, 16), 0, 7), 0x0c + 5);
340 assert_eq!(bits(le32(&usb, 20), 5, 2), 1);
341 assert_eq!(bits(le32(&usb, 20), 4, 1), 1);
342 }
343}