Skip to main content

openipc_core/
realtek_tx.rs

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}