1b032f27cSSam Leffler /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni *
4b032f27cSSam Leffler * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
5b032f27cSSam Leffler * All rights reserved.
6b032f27cSSam Leffler *
7b032f27cSSam Leffler * Redistribution and use in source and binary forms, with or without
8b032f27cSSam Leffler * modification, are permitted provided that the following conditions
9b032f27cSSam Leffler * are met:
10b032f27cSSam Leffler * 1. Redistributions of source code must retain the above copyright
11b032f27cSSam Leffler * notice, this list of conditions and the following disclaimer.
12b032f27cSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright
13b032f27cSSam Leffler * notice, this list of conditions and the following disclaimer in the
14b032f27cSSam Leffler * documentation and/or other materials provided with the distribution.
15b032f27cSSam Leffler *
16b032f27cSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17b032f27cSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18b032f27cSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19b032f27cSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20b032f27cSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21b032f27cSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22b032f27cSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23b032f27cSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24b032f27cSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25b032f27cSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26b032f27cSSam Leffler */
27b032f27cSSam Leffler
28b032f27cSSam Leffler #include <sys/cdefs.h>
29b032f27cSSam Leffler /*
30b032f27cSSam Leffler * IEEE 802.11 PHY-related support.
31b032f27cSSam Leffler */
32b032f27cSSam Leffler
33b032f27cSSam Leffler #include "opt_inet.h"
34b032f27cSSam Leffler
35b032f27cSSam Leffler #include <sys/param.h>
36b032f27cSSam Leffler #include <sys/kernel.h>
37b032f27cSSam Leffler #include <sys/systm.h>
38eedc7fd9SGleb Smirnoff #include <sys/malloc.h>
39b032f27cSSam Leffler
40b032f27cSSam Leffler #include <sys/socket.h>
41b032f27cSSam Leffler
42b032f27cSSam Leffler #include <net/if.h>
43b032f27cSSam Leffler #include <net/if_media.h>
44b032f27cSSam Leffler
45eedc7fd9SGleb Smirnoff #include <net/ethernet.h>
46eedc7fd9SGleb Smirnoff #include <net/route.h>
47eedc7fd9SGleb Smirnoff
48b032f27cSSam Leffler #include <net80211/ieee80211_var.h>
49b032f27cSSam Leffler #include <net80211/ieee80211_phy.h>
50b032f27cSSam Leffler
51b032f27cSSam Leffler #ifdef notyet
52b032f27cSSam Leffler struct ieee80211_ds_plcp_hdr {
53b032f27cSSam Leffler uint8_t i_signal;
54b032f27cSSam Leffler uint8_t i_service;
55b032f27cSSam Leffler uint16_t i_length;
56b032f27cSSam Leffler uint16_t i_crc;
57b032f27cSSam Leffler } __packed;
58b032f27cSSam Leffler
59b032f27cSSam Leffler #endif /* notyet */
60b032f27cSSam Leffler
61b032f27cSSam Leffler /* shorthands to compact tables for readability */
62b032f27cSSam Leffler #define OFDM IEEE80211_T_OFDM
63b032f27cSSam Leffler #define CCK IEEE80211_T_CCK
64b032f27cSSam Leffler #define TURBO IEEE80211_T_TURBO
6524a07b5bSSam Leffler #define HALF IEEE80211_T_OFDM_HALF
6624a07b5bSSam Leffler #define QUART IEEE80211_T_OFDM_QUARTER
67f8bf74f2SAdrian Chadd #define HT IEEE80211_T_HT
68f8bf74f2SAdrian Chadd /* XXX the 11n and the basic rate flag are unfortunately overlapping. Grr. */
69f8bf74f2SAdrian Chadd #define N(r) (IEEE80211_RATE_MCS | r)
7024a07b5bSSam Leffler #define PBCC (IEEE80211_T_OFDM_QUARTER+1) /* XXX */
71f8bf74f2SAdrian Chadd #define B(r) (IEEE80211_RATE_BASIC | r)
72c9f78f45SSam Leffler #define Mb(x) (x*1000)
73b032f27cSSam Leffler
74b032f27cSSam Leffler static struct ieee80211_rate_table ieee80211_11b_table = {
75c9f78f45SSam Leffler .rateCount = 4, /* XXX no PBCC */
76c9f78f45SSam Leffler .info = {
77b032f27cSSam Leffler /* short ctrl */
78b032f27cSSam Leffler /* Preamble dot11Rate Rate */
79c9f78f45SSam Leffler [0] = { .phy = CCK, 1000, 0x00, B(2), 0 },/* 1 Mb */
80c9f78f45SSam Leffler [1] = { .phy = CCK, 2000, 0x04, B(4), 1 },/* 2 Mb */
81c9f78f45SSam Leffler [2] = { .phy = CCK, 5500, 0x04, B(11), 1 },/* 5.5 Mb */
82c9f78f45SSam Leffler [3] = { .phy = CCK, 11000, 0x04, B(22), 1 },/* 11 Mb */
83c9f78f45SSam Leffler [4] = { .phy = PBCC, 22000, 0x04, 44, 3 } /* 22 Mb */
84b032f27cSSam Leffler },
85b032f27cSSam Leffler };
86b032f27cSSam Leffler
87b032f27cSSam Leffler static struct ieee80211_rate_table ieee80211_11g_table = {
88c9f78f45SSam Leffler .rateCount = 12,
89c9f78f45SSam Leffler .info = {
90b032f27cSSam Leffler /* short ctrl */
91b032f27cSSam Leffler /* Preamble dot11Rate Rate */
92c9f78f45SSam Leffler [0] = { .phy = CCK, 1000, 0x00, B(2), 0 },
93c9f78f45SSam Leffler [1] = { .phy = CCK, 2000, 0x04, B(4), 1 },
94c9f78f45SSam Leffler [2] = { .phy = CCK, 5500, 0x04, B(11), 2 },
95c9f78f45SSam Leffler [3] = { .phy = CCK, 11000, 0x04, B(22), 3 },
96c9f78f45SSam Leffler [4] = { .phy = OFDM, 6000, 0x00, 12, 4 },
97c9f78f45SSam Leffler [5] = { .phy = OFDM, 9000, 0x00, 18, 4 },
98c9f78f45SSam Leffler [6] = { .phy = OFDM, 12000, 0x00, 24, 6 },
99c9f78f45SSam Leffler [7] = { .phy = OFDM, 18000, 0x00, 36, 6 },
100c9f78f45SSam Leffler [8] = { .phy = OFDM, 24000, 0x00, 48, 8 },
101c9f78f45SSam Leffler [9] = { .phy = OFDM, 36000, 0x00, 72, 8 },
102c9f78f45SSam Leffler [10] = { .phy = OFDM, 48000, 0x00, 96, 8 },
103c9f78f45SSam Leffler [11] = { .phy = OFDM, 54000, 0x00, 108, 8 }
104b032f27cSSam Leffler },
105b032f27cSSam Leffler };
106b032f27cSSam Leffler
107b032f27cSSam Leffler static struct ieee80211_rate_table ieee80211_11a_table = {
108c9f78f45SSam Leffler .rateCount = 8,
109c9f78f45SSam Leffler .info = {
110b032f27cSSam Leffler /* short ctrl */
111b032f27cSSam Leffler /* Preamble dot11Rate Rate */
112c9f78f45SSam Leffler [0] = { .phy = OFDM, 6000, 0x00, B(12), 0 },
113c9f78f45SSam Leffler [1] = { .phy = OFDM, 9000, 0x00, 18, 0 },
114c9f78f45SSam Leffler [2] = { .phy = OFDM, 12000, 0x00, B(24), 2 },
115c9f78f45SSam Leffler [3] = { .phy = OFDM, 18000, 0x00, 36, 2 },
116c9f78f45SSam Leffler [4] = { .phy = OFDM, 24000, 0x00, B(48), 4 },
117c9f78f45SSam Leffler [5] = { .phy = OFDM, 36000, 0x00, 72, 4 },
118c9f78f45SSam Leffler [6] = { .phy = OFDM, 48000, 0x00, 96, 4 },
119c9f78f45SSam Leffler [7] = { .phy = OFDM, 54000, 0x00, 108, 4 }
120b032f27cSSam Leffler },
121b032f27cSSam Leffler };
122b032f27cSSam Leffler
123b032f27cSSam Leffler static struct ieee80211_rate_table ieee80211_half_table = {
124c9f78f45SSam Leffler .rateCount = 8,
125c9f78f45SSam Leffler .info = {
126b032f27cSSam Leffler /* short ctrl */
127b032f27cSSam Leffler /* Preamble dot11Rate Rate */
12824a07b5bSSam Leffler [0] = { .phy = HALF, 3000, 0x00, B(6), 0 },
12924a07b5bSSam Leffler [1] = { .phy = HALF, 4500, 0x00, 9, 0 },
13024a07b5bSSam Leffler [2] = { .phy = HALF, 6000, 0x00, B(12), 2 },
13124a07b5bSSam Leffler [3] = { .phy = HALF, 9000, 0x00, 18, 2 },
13224a07b5bSSam Leffler [4] = { .phy = HALF, 12000, 0x00, B(24), 4 },
13324a07b5bSSam Leffler [5] = { .phy = HALF, 18000, 0x00, 36, 4 },
13424a07b5bSSam Leffler [6] = { .phy = HALF, 24000, 0x00, 48, 4 },
13524a07b5bSSam Leffler [7] = { .phy = HALF, 27000, 0x00, 54, 4 }
136b032f27cSSam Leffler },
137b032f27cSSam Leffler };
138b032f27cSSam Leffler
139b032f27cSSam Leffler static struct ieee80211_rate_table ieee80211_quarter_table = {
140c9f78f45SSam Leffler .rateCount = 8,
141c9f78f45SSam Leffler .info = {
142b032f27cSSam Leffler /* short ctrl */
143b032f27cSSam Leffler /* Preamble dot11Rate Rate */
14424a07b5bSSam Leffler [0] = { .phy = QUART, 1500, 0x00, B(3), 0 },
14524a07b5bSSam Leffler [1] = { .phy = QUART, 2250, 0x00, 4, 0 },
14624a07b5bSSam Leffler [2] = { .phy = QUART, 3000, 0x00, B(9), 2 },
14724a07b5bSSam Leffler [3] = { .phy = QUART, 4500, 0x00, 9, 2 },
14824a07b5bSSam Leffler [4] = { .phy = QUART, 6000, 0x00, B(12), 4 },
14924a07b5bSSam Leffler [5] = { .phy = QUART, 9000, 0x00, 18, 4 },
15024a07b5bSSam Leffler [6] = { .phy = QUART, 12000, 0x00, 24, 4 },
15124a07b5bSSam Leffler [7] = { .phy = QUART, 13500, 0x00, 27, 4 }
152b032f27cSSam Leffler },
153b032f27cSSam Leffler };
154b032f27cSSam Leffler
155b032f27cSSam Leffler static struct ieee80211_rate_table ieee80211_turbog_table = {
156c9f78f45SSam Leffler .rateCount = 7,
157c9f78f45SSam Leffler .info = {
158b032f27cSSam Leffler /* short ctrl */
159b032f27cSSam Leffler /* Preamble dot11Rate Rate */
160c9f78f45SSam Leffler [0] = { .phy = TURBO, 12000, 0x00, B(12), 0 },
161c9f78f45SSam Leffler [1] = { .phy = TURBO, 24000, 0x00, B(24), 1 },
162c9f78f45SSam Leffler [2] = { .phy = TURBO, 36000, 0x00, 36, 1 },
163c9f78f45SSam Leffler [3] = { .phy = TURBO, 48000, 0x00, B(48), 3 },
164c9f78f45SSam Leffler [4] = { .phy = TURBO, 72000, 0x00, 72, 3 },
165c9f78f45SSam Leffler [5] = { .phy = TURBO, 96000, 0x00, 96, 3 },
166c9f78f45SSam Leffler [6] = { .phy = TURBO, 108000, 0x00, 108, 3 }
167b032f27cSSam Leffler },
168b032f27cSSam Leffler };
169b032f27cSSam Leffler
170b032f27cSSam Leffler static struct ieee80211_rate_table ieee80211_turboa_table = {
171c9f78f45SSam Leffler .rateCount = 8,
172c9f78f45SSam Leffler .info = {
173b032f27cSSam Leffler /* short ctrl */
174b032f27cSSam Leffler /* Preamble dot11Rate Rate */
175c9f78f45SSam Leffler [0] = { .phy = TURBO, 12000, 0x00, B(12), 0 },
176c9f78f45SSam Leffler [1] = { .phy = TURBO, 18000, 0x00, 18, 0 },
177c9f78f45SSam Leffler [2] = { .phy = TURBO, 24000, 0x00, B(24), 2 },
178c9f78f45SSam Leffler [3] = { .phy = TURBO, 36000, 0x00, 36, 2 },
179c9f78f45SSam Leffler [4] = { .phy = TURBO, 48000, 0x00, B(48), 4 },
180c9f78f45SSam Leffler [5] = { .phy = TURBO, 72000, 0x00, 72, 4 },
181c9f78f45SSam Leffler [6] = { .phy = TURBO, 96000, 0x00, 96, 4 },
182c9f78f45SSam Leffler [7] = { .phy = TURBO, 108000, 0x00, 108, 4 }
183b032f27cSSam Leffler },
184b032f27cSSam Leffler };
185b032f27cSSam Leffler
186f8bf74f2SAdrian Chadd static struct ieee80211_rate_table ieee80211_11ng_table = {
187f8bf74f2SAdrian Chadd .rateCount = 36,
188f8bf74f2SAdrian Chadd .info = {
189f8bf74f2SAdrian Chadd /* short ctrl */
190f8bf74f2SAdrian Chadd /* Preamble dot11Rate Rate */
191f8bf74f2SAdrian Chadd [0] = { .phy = CCK, 1000, 0x00, B(2), 0 },
192f8bf74f2SAdrian Chadd [1] = { .phy = CCK, 2000, 0x04, B(4), 1 },
193f8bf74f2SAdrian Chadd [2] = { .phy = CCK, 5500, 0x04, B(11), 2 },
194f8bf74f2SAdrian Chadd [3] = { .phy = CCK, 11000, 0x04, B(22), 3 },
195f8bf74f2SAdrian Chadd [4] = { .phy = OFDM, 6000, 0x00, 12, 4 },
196f8bf74f2SAdrian Chadd [5] = { .phy = OFDM, 9000, 0x00, 18, 4 },
197f8bf74f2SAdrian Chadd [6] = { .phy = OFDM, 12000, 0x00, 24, 6 },
198f8bf74f2SAdrian Chadd [7] = { .phy = OFDM, 18000, 0x00, 36, 6 },
199f8bf74f2SAdrian Chadd [8] = { .phy = OFDM, 24000, 0x00, 48, 8 },
200f8bf74f2SAdrian Chadd [9] = { .phy = OFDM, 36000, 0x00, 72, 8 },
201f8bf74f2SAdrian Chadd [10] = { .phy = OFDM, 48000, 0x00, 96, 8 },
202f8bf74f2SAdrian Chadd [11] = { .phy = OFDM, 54000, 0x00, 108, 8 },
203f8bf74f2SAdrian Chadd
204f8bf74f2SAdrian Chadd [12] = { .phy = HT, 6500, 0x00, N(0), 4 },
205f8bf74f2SAdrian Chadd [13] = { .phy = HT, 13000, 0x00, N(1), 6 },
206f8bf74f2SAdrian Chadd [14] = { .phy = HT, 19500, 0x00, N(2), 6 },
207f8bf74f2SAdrian Chadd [15] = { .phy = HT, 26000, 0x00, N(3), 8 },
208f8bf74f2SAdrian Chadd [16] = { .phy = HT, 39000, 0x00, N(4), 8 },
209f8bf74f2SAdrian Chadd [17] = { .phy = HT, 52000, 0x00, N(5), 8 },
210f8bf74f2SAdrian Chadd [18] = { .phy = HT, 58500, 0x00, N(6), 8 },
211f8bf74f2SAdrian Chadd [19] = { .phy = HT, 65000, 0x00, N(7), 8 },
212f8bf74f2SAdrian Chadd
213f8bf74f2SAdrian Chadd [20] = { .phy = HT, 13000, 0x00, N(8), 4 },
214f8bf74f2SAdrian Chadd [21] = { .phy = HT, 26000, 0x00, N(9), 6 },
215f8bf74f2SAdrian Chadd [22] = { .phy = HT, 39000, 0x00, N(10), 6 },
216f8bf74f2SAdrian Chadd [23] = { .phy = HT, 52000, 0x00, N(11), 8 },
217f8bf74f2SAdrian Chadd [24] = { .phy = HT, 78000, 0x00, N(12), 8 },
218f8bf74f2SAdrian Chadd [25] = { .phy = HT, 104000, 0x00, N(13), 8 },
219f8bf74f2SAdrian Chadd [26] = { .phy = HT, 117000, 0x00, N(14), 8 },
220f8bf74f2SAdrian Chadd [27] = { .phy = HT, 130000, 0x00, N(15), 8 },
221f8bf74f2SAdrian Chadd
222f8bf74f2SAdrian Chadd [28] = { .phy = HT, 19500, 0x00, N(16), 4 },
223f8bf74f2SAdrian Chadd [29] = { .phy = HT, 39000, 0x00, N(17), 6 },
224f8bf74f2SAdrian Chadd [30] = { .phy = HT, 58500, 0x00, N(18), 6 },
225f8bf74f2SAdrian Chadd [31] = { .phy = HT, 78000, 0x00, N(19), 8 },
226f8bf74f2SAdrian Chadd [32] = { .phy = HT, 117000, 0x00, N(20), 8 },
227f8bf74f2SAdrian Chadd [33] = { .phy = HT, 156000, 0x00, N(21), 8 },
228f8bf74f2SAdrian Chadd [34] = { .phy = HT, 175500, 0x00, N(22), 8 },
229f8bf74f2SAdrian Chadd [35] = { .phy = HT, 195000, 0x00, N(23), 8 },
230f8bf74f2SAdrian Chadd
231f8bf74f2SAdrian Chadd },
232f8bf74f2SAdrian Chadd };
233f8bf74f2SAdrian Chadd
234f8bf74f2SAdrian Chadd static struct ieee80211_rate_table ieee80211_11na_table = {
235f8bf74f2SAdrian Chadd .rateCount = 32,
236f8bf74f2SAdrian Chadd .info = {
237f8bf74f2SAdrian Chadd /* short ctrl */
238f8bf74f2SAdrian Chadd /* Preamble dot11Rate Rate */
239f8bf74f2SAdrian Chadd [0] = { .phy = OFDM, 6000, 0x00, B(12), 0 },
240f8bf74f2SAdrian Chadd [1] = { .phy = OFDM, 9000, 0x00, 18, 0 },
241f8bf74f2SAdrian Chadd [2] = { .phy = OFDM, 12000, 0x00, B(24), 2 },
242f8bf74f2SAdrian Chadd [3] = { .phy = OFDM, 18000, 0x00, 36, 2 },
243f8bf74f2SAdrian Chadd [4] = { .phy = OFDM, 24000, 0x00, B(48), 4 },
244f8bf74f2SAdrian Chadd [5] = { .phy = OFDM, 36000, 0x00, 72, 4 },
245f8bf74f2SAdrian Chadd [6] = { .phy = OFDM, 48000, 0x00, 96, 4 },
246f8bf74f2SAdrian Chadd [7] = { .phy = OFDM, 54000, 0x00, 108, 4 },
247f8bf74f2SAdrian Chadd
248f8bf74f2SAdrian Chadd [8] = { .phy = HT, 6500, 0x00, N(0), 0 },
249f8bf74f2SAdrian Chadd [9] = { .phy = HT, 13000, 0x00, N(1), 2 },
250f8bf74f2SAdrian Chadd [10] = { .phy = HT, 19500, 0x00, N(2), 2 },
251f8bf74f2SAdrian Chadd [11] = { .phy = HT, 26000, 0x00, N(3), 4 },
252f8bf74f2SAdrian Chadd [12] = { .phy = HT, 39000, 0x00, N(4), 4 },
253f8bf74f2SAdrian Chadd [13] = { .phy = HT, 52000, 0x00, N(5), 4 },
254f8bf74f2SAdrian Chadd [14] = { .phy = HT, 58500, 0x00, N(6), 4 },
255f8bf74f2SAdrian Chadd [15] = { .phy = HT, 65000, 0x00, N(7), 4 },
256f8bf74f2SAdrian Chadd
257f8bf74f2SAdrian Chadd [16] = { .phy = HT, 13000, 0x00, N(8), 0 },
258f8bf74f2SAdrian Chadd [17] = { .phy = HT, 26000, 0x00, N(9), 2 },
259f8bf74f2SAdrian Chadd [18] = { .phy = HT, 39000, 0x00, N(10), 2 },
260f8bf74f2SAdrian Chadd [19] = { .phy = HT, 52000, 0x00, N(11), 4 },
261f8bf74f2SAdrian Chadd [20] = { .phy = HT, 78000, 0x00, N(12), 4 },
262f8bf74f2SAdrian Chadd [21] = { .phy = HT, 104000, 0x00, N(13), 4 },
263f8bf74f2SAdrian Chadd [22] = { .phy = HT, 117000, 0x00, N(14), 4 },
264f8bf74f2SAdrian Chadd [23] = { .phy = HT, 130000, 0x00, N(15), 4 },
265f8bf74f2SAdrian Chadd
266f8bf74f2SAdrian Chadd [24] = { .phy = HT, 19500, 0x00, N(16), 0 },
267f8bf74f2SAdrian Chadd [25] = { .phy = HT, 39000, 0x00, N(17), 2 },
268f8bf74f2SAdrian Chadd [26] = { .phy = HT, 58500, 0x00, N(18), 2 },
269f8bf74f2SAdrian Chadd [27] = { .phy = HT, 78000, 0x00, N(19), 4 },
270f8bf74f2SAdrian Chadd [28] = { .phy = HT, 117000, 0x00, N(20), 4 },
271f8bf74f2SAdrian Chadd [29] = { .phy = HT, 156000, 0x00, N(21), 4 },
272f8bf74f2SAdrian Chadd [30] = { .phy = HT, 175500, 0x00, N(22), 4 },
273f8bf74f2SAdrian Chadd [31] = { .phy = HT, 195000, 0x00, N(23), 4 },
274f8bf74f2SAdrian Chadd
275f8bf74f2SAdrian Chadd },
276f8bf74f2SAdrian Chadd };
277f8bf74f2SAdrian Chadd
278c9f78f45SSam Leffler #undef Mb
279c9f78f45SSam Leffler #undef B
280b032f27cSSam Leffler #undef OFDM
28124a07b5bSSam Leffler #undef HALF
28224a07b5bSSam Leffler #undef QUART
283b032f27cSSam Leffler #undef CCK
284b032f27cSSam Leffler #undef TURBO
285b032f27cSSam Leffler #undef XR
286f8bf74f2SAdrian Chadd #undef HT
287f8bf74f2SAdrian Chadd #undef N
288b032f27cSSam Leffler
289b032f27cSSam Leffler /*
290b032f27cSSam Leffler * Setup a rate table's reverse lookup table and fill in
291b032f27cSSam Leffler * ack durations. The reverse lookup tables are assumed
292b032f27cSSam Leffler * to be initialized to zero (or at least the first entry).
293b032f27cSSam Leffler * We use this as a key that indicates whether or not
294b032f27cSSam Leffler * we've previously setup the reverse lookup table.
295b032f27cSSam Leffler *
296b032f27cSSam Leffler * XXX not reentrant, but shouldn't matter
297b032f27cSSam Leffler */
298b032f27cSSam Leffler static void
ieee80211_setup_ratetable(struct ieee80211_rate_table * rt)299b032f27cSSam Leffler ieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
300b032f27cSSam Leffler {
301b032f27cSSam Leffler #define WLAN_CTRL_FRAME_SIZE \
302b032f27cSSam Leffler (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
303b032f27cSSam Leffler
304b032f27cSSam Leffler int i;
305b032f27cSSam Leffler
306a3e08d6fSRui Paulo for (i = 0; i < nitems(rt->rateCodeToIndex); i++)
307b032f27cSSam Leffler rt->rateCodeToIndex[i] = (uint8_t) -1;
308b032f27cSSam Leffler for (i = 0; i < rt->rateCount; i++) {
309b032f27cSSam Leffler uint8_t code = rt->info[i].dot11Rate;
310b032f27cSSam Leffler uint8_t cix = rt->info[i].ctlRateIndex;
311b032f27cSSam Leffler uint8_t ctl_rate = rt->info[cix].dot11Rate;
312b032f27cSSam Leffler
313b032f27cSSam Leffler /*
314f8bf74f2SAdrian Chadd * Map without the basic rate bit.
315f8bf74f2SAdrian Chadd *
316f8bf74f2SAdrian Chadd * It's up to the caller to ensure that the basic
317f8bf74f2SAdrian Chadd * rate bit is stripped here.
318f8bf74f2SAdrian Chadd *
319f8bf74f2SAdrian Chadd * For HT, use the MCS rate bit.
320b032f27cSSam Leffler */
321b032f27cSSam Leffler code &= IEEE80211_RATE_VAL;
322f8bf74f2SAdrian Chadd if (rt->info[i].phy == IEEE80211_T_HT) {
323f8bf74f2SAdrian Chadd code |= IEEE80211_RATE_MCS;
324b032f27cSSam Leffler }
325b032f27cSSam Leffler
326f8bf74f2SAdrian Chadd /* XXX assume the control rate is non-MCS? */
327f8bf74f2SAdrian Chadd ctl_rate &= IEEE80211_RATE_VAL;
328f8bf74f2SAdrian Chadd rt->rateCodeToIndex[code] = i;
329f8bf74f2SAdrian Chadd
330b032f27cSSam Leffler /*
331b032f27cSSam Leffler * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
332b032f27cSSam Leffler * depends on whether they are marked as basic rates;
333b032f27cSSam Leffler * the static tables are setup with an 11b-compatible
334b032f27cSSam Leffler * 2Mb/s rate which will work but is suboptimal
335b032f27cSSam Leffler *
336b032f27cSSam Leffler * NB: Control rate is always less than or equal to the
337b032f27cSSam Leffler * current rate, so control rate's reverse lookup entry
338b032f27cSSam Leffler * has been installed and following call is safe.
339b032f27cSSam Leffler */
340b032f27cSSam Leffler rt->info[i].lpAckDuration = ieee80211_compute_duration(rt,
341b032f27cSSam Leffler WLAN_CTRL_FRAME_SIZE, ctl_rate, 0);
342b032f27cSSam Leffler rt->info[i].spAckDuration = ieee80211_compute_duration(rt,
343b032f27cSSam Leffler WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE);
344b032f27cSSam Leffler }
345b032f27cSSam Leffler
346b032f27cSSam Leffler #undef WLAN_CTRL_FRAME_SIZE
347b032f27cSSam Leffler }
348b032f27cSSam Leffler
349b032f27cSSam Leffler /* Setup all rate tables */
350b032f27cSSam Leffler static void
ieee80211_phy_init(void)351b032f27cSSam Leffler ieee80211_phy_init(void)
352b032f27cSSam Leffler {
353b032f27cSSam Leffler static struct ieee80211_rate_table * const ratetables[] = {
354b032f27cSSam Leffler &ieee80211_half_table,
355b032f27cSSam Leffler &ieee80211_quarter_table,
356f8bf74f2SAdrian Chadd &ieee80211_11na_table,
357f8bf74f2SAdrian Chadd &ieee80211_11ng_table,
358b032f27cSSam Leffler &ieee80211_turbog_table,
359b032f27cSSam Leffler &ieee80211_turboa_table,
360b032f27cSSam Leffler &ieee80211_11a_table,
361b032f27cSSam Leffler &ieee80211_11g_table,
362b032f27cSSam Leffler &ieee80211_11b_table
363b032f27cSSam Leffler };
364b032f27cSSam Leffler int i;
365b032f27cSSam Leffler
366a3e08d6fSRui Paulo for (i = 0; i < nitems(ratetables); ++i)
367b032f27cSSam Leffler ieee80211_setup_ratetable(ratetables[i]);
368b032f27cSSam Leffler
369b032f27cSSam Leffler }
370b032f27cSSam Leffler SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
371b032f27cSSam Leffler
372b032f27cSSam Leffler const struct ieee80211_rate_table *
ieee80211_get_ratetable(struct ieee80211_channel * c)373b032f27cSSam Leffler ieee80211_get_ratetable(struct ieee80211_channel *c)
374b032f27cSSam Leffler {
375b032f27cSSam Leffler const struct ieee80211_rate_table *rt;
376b032f27cSSam Leffler
377b032f27cSSam Leffler /* XXX HT */
378b032f27cSSam Leffler if (IEEE80211_IS_CHAN_HALF(c))
379b032f27cSSam Leffler rt = &ieee80211_half_table;
380b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_QUARTER(c))
381b032f27cSSam Leffler rt = &ieee80211_quarter_table;
382b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_HTA(c))
383f8bf74f2SAdrian Chadd rt = &ieee80211_11na_table;
384b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_HTG(c))
385f8bf74f2SAdrian Chadd rt = &ieee80211_11ng_table;
386b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_108G(c))
387b032f27cSSam Leffler rt = &ieee80211_turbog_table;
388b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_ST(c))
389b032f27cSSam Leffler rt = &ieee80211_turboa_table;
390b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_TURBO(c))
391b032f27cSSam Leffler rt = &ieee80211_turboa_table;
392b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_A(c))
393b032f27cSSam Leffler rt = &ieee80211_11a_table;
394b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_ANYG(c))
395b032f27cSSam Leffler rt = &ieee80211_11g_table;
396b032f27cSSam Leffler else if (IEEE80211_IS_CHAN_B(c))
397b032f27cSSam Leffler rt = &ieee80211_11b_table;
398b032f27cSSam Leffler else {
399b032f27cSSam Leffler /* NB: should not get here */
400b032f27cSSam Leffler panic("%s: no rate table for channel; freq %u flags 0x%x\n",
401b032f27cSSam Leffler __func__, c->ic_freq, c->ic_flags);
402b032f27cSSam Leffler }
403b032f27cSSam Leffler return rt;
404b032f27cSSam Leffler }
405b032f27cSSam Leffler
406b032f27cSSam Leffler /*
407b032f27cSSam Leffler * Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s)
408b032f27cSSam Leffler *
409b032f27cSSam Leffler * Note we do no parameter checking; this routine is mainly
410b032f27cSSam Leffler * used to derive an 802.11 rate for constructing radiotap
411b032f27cSSam Leffler * header data for rx frames.
412b032f27cSSam Leffler *
413b032f27cSSam Leffler * XXX might be a candidate for inline
414b032f27cSSam Leffler */
415b032f27cSSam Leffler uint8_t
ieee80211_plcp2rate(uint8_t plcp,enum ieee80211_phytype type)4168215d906SSam Leffler ieee80211_plcp2rate(uint8_t plcp, enum ieee80211_phytype type)
417b032f27cSSam Leffler {
4188215d906SSam Leffler if (type == IEEE80211_T_OFDM) {
419b032f27cSSam Leffler static const uint8_t ofdm_plcp2rate[16] = {
420b032f27cSSam Leffler [0xb] = 12,
421b032f27cSSam Leffler [0xf] = 18,
422b032f27cSSam Leffler [0xa] = 24,
423b032f27cSSam Leffler [0xe] = 36,
424b032f27cSSam Leffler [0x9] = 48,
425b032f27cSSam Leffler [0xd] = 72,
426b032f27cSSam Leffler [0x8] = 96,
427b032f27cSSam Leffler [0xc] = 108
428b032f27cSSam Leffler };
429b032f27cSSam Leffler return ofdm_plcp2rate[plcp & 0xf];
4308215d906SSam Leffler }
4318215d906SSam Leffler if (type == IEEE80211_T_CCK) {
432b032f27cSSam Leffler static const uint8_t cck_plcp2rate[16] = {
433b032f27cSSam Leffler [0xa] = 2, /* 0x0a */
434b032f27cSSam Leffler [0x4] = 4, /* 0x14 */
435b032f27cSSam Leffler [0x7] = 11, /* 0x37 */
436b032f27cSSam Leffler [0xe] = 22, /* 0x6e */
437b032f27cSSam Leffler [0xc] = 44, /* 0xdc , actually PBCC */
438b032f27cSSam Leffler };
439b032f27cSSam Leffler return cck_plcp2rate[plcp & 0xf];
440b032f27cSSam Leffler }
4418215d906SSam Leffler return 0;
442b032f27cSSam Leffler }
443b032f27cSSam Leffler
444b032f27cSSam Leffler /*
445b032f27cSSam Leffler * Covert 802.11 rate to PLCP signal.
446b032f27cSSam Leffler */
447b032f27cSSam Leffler uint8_t
ieee80211_rate2plcp(int rate,enum ieee80211_phytype type)4488215d906SSam Leffler ieee80211_rate2plcp(int rate, enum ieee80211_phytype type)
449b032f27cSSam Leffler {
4508215d906SSam Leffler /* XXX ignore type for now since rates are unique */
451b032f27cSSam Leffler switch (rate) {
452b032f27cSSam Leffler /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
453b032f27cSSam Leffler case 12: return 0xb;
454b032f27cSSam Leffler case 18: return 0xf;
455b032f27cSSam Leffler case 24: return 0xa;
456b032f27cSSam Leffler case 36: return 0xe;
457b032f27cSSam Leffler case 48: return 0x9;
458b032f27cSSam Leffler case 72: return 0xd;
459b032f27cSSam Leffler case 96: return 0x8;
460b032f27cSSam Leffler case 108: return 0xc;
4618215d906SSam Leffler /* CCK rates (IEEE Std 802.11b-1999 page 15, subclause 18.2.3.3) */
4628215d906SSam Leffler case 2: return 10;
4638215d906SSam Leffler case 4: return 20;
4648215d906SSam Leffler case 11: return 55;
4658215d906SSam Leffler case 22: return 110;
4668215d906SSam Leffler /* IEEE Std 802.11g-2003 page 19, subclause 19.3.2.1 */
4678215d906SSam Leffler case 44: return 220;
468b032f27cSSam Leffler }
4698215d906SSam Leffler return 0; /* XXX unsupported/unknown rate */
470b032f27cSSam Leffler }
4718215d906SSam Leffler
47224a07b5bSSam Leffler #define CCK_SIFS_TIME 10
47324a07b5bSSam Leffler #define CCK_PREAMBLE_BITS 144
47424a07b5bSSam Leffler #define CCK_PLCP_BITS 48
47524a07b5bSSam Leffler
47624a07b5bSSam Leffler #define OFDM_SIFS_TIME 16
47724a07b5bSSam Leffler #define OFDM_PREAMBLE_TIME 20
47824a07b5bSSam Leffler #define OFDM_PLCP_BITS 22
47924a07b5bSSam Leffler #define OFDM_SYMBOL_TIME 4
48024a07b5bSSam Leffler
48124a07b5bSSam Leffler #define OFDM_HALF_SIFS_TIME 32
48224a07b5bSSam Leffler #define OFDM_HALF_PREAMBLE_TIME 40
48324a07b5bSSam Leffler #define OFDM_HALF_PLCP_BITS 22
48424a07b5bSSam Leffler #define OFDM_HALF_SYMBOL_TIME 8
48524a07b5bSSam Leffler
48624a07b5bSSam Leffler #define OFDM_QUARTER_SIFS_TIME 64
48724a07b5bSSam Leffler #define OFDM_QUARTER_PREAMBLE_TIME 80
48824a07b5bSSam Leffler #define OFDM_QUARTER_PLCP_BITS 22
48924a07b5bSSam Leffler #define OFDM_QUARTER_SYMBOL_TIME 16
49024a07b5bSSam Leffler
49124a07b5bSSam Leffler #define TURBO_SIFS_TIME 8
49224a07b5bSSam Leffler #define TURBO_PREAMBLE_TIME 14
49324a07b5bSSam Leffler #define TURBO_PLCP_BITS 22
49424a07b5bSSam Leffler #define TURBO_SYMBOL_TIME 4
49524a07b5bSSam Leffler
496b032f27cSSam Leffler /*
497b032f27cSSam Leffler * Compute the time to transmit a frame of length frameLen bytes
498b032f27cSSam Leffler * using the specified rate, phy, and short preamble setting.
499b032f27cSSam Leffler * SIFS is included.
500b032f27cSSam Leffler */
501b032f27cSSam Leffler uint16_t
ieee80211_compute_duration(const struct ieee80211_rate_table * rt,uint32_t frameLen,uint16_t rate,int isShortPreamble)502b032f27cSSam Leffler ieee80211_compute_duration(const struct ieee80211_rate_table *rt,
503b032f27cSSam Leffler uint32_t frameLen, uint16_t rate, int isShortPreamble)
504b032f27cSSam Leffler {
505b032f27cSSam Leffler uint8_t rix = rt->rateCodeToIndex[rate];
506b032f27cSSam Leffler uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
507b032f27cSSam Leffler uint32_t kbps;
508b032f27cSSam Leffler
509b032f27cSSam Leffler KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
510b032f27cSSam Leffler kbps = rt->info[rix].rateKbps;
511b032f27cSSam Leffler if (kbps == 0) /* XXX bandaid for channel changes */
512b032f27cSSam Leffler return 0;
513b032f27cSSam Leffler
514b032f27cSSam Leffler switch (rt->info[rix].phy) {
515b032f27cSSam Leffler case IEEE80211_T_CCK:
516b032f27cSSam Leffler phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
517b032f27cSSam Leffler if (isShortPreamble && rt->info[rix].shortPreamble)
518b032f27cSSam Leffler phyTime >>= 1;
519b032f27cSSam Leffler numBits = frameLen << 3;
520b032f27cSSam Leffler txTime = CCK_SIFS_TIME + phyTime
521b032f27cSSam Leffler + ((numBits * 1000)/kbps);
522b032f27cSSam Leffler break;
523b032f27cSSam Leffler case IEEE80211_T_OFDM:
524b032f27cSSam Leffler bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
525b032f27cSSam Leffler KASSERT(bitsPerSymbol != 0, ("full rate bps"));
526b032f27cSSam Leffler
527b032f27cSSam Leffler numBits = OFDM_PLCP_BITS + (frameLen << 3);
528b032f27cSSam Leffler numSymbols = howmany(numBits, bitsPerSymbol);
529b032f27cSSam Leffler txTime = OFDM_SIFS_TIME
530b032f27cSSam Leffler + OFDM_PREAMBLE_TIME
531b032f27cSSam Leffler + (numSymbols * OFDM_SYMBOL_TIME);
532b032f27cSSam Leffler break;
53324a07b5bSSam Leffler case IEEE80211_T_OFDM_HALF:
53424a07b5bSSam Leffler bitsPerSymbol = (kbps * OFDM_HALF_SYMBOL_TIME) / 1000;
53524a07b5bSSam Leffler KASSERT(bitsPerSymbol != 0, ("1/4 rate bps"));
536b032f27cSSam Leffler
53724a07b5bSSam Leffler numBits = OFDM_PLCP_BITS + (frameLen << 3);
53824a07b5bSSam Leffler numSymbols = howmany(numBits, bitsPerSymbol);
53924a07b5bSSam Leffler txTime = OFDM_HALF_SIFS_TIME
54024a07b5bSSam Leffler + OFDM_HALF_PREAMBLE_TIME
54124a07b5bSSam Leffler + (numSymbols * OFDM_HALF_SYMBOL_TIME);
54224a07b5bSSam Leffler break;
54324a07b5bSSam Leffler case IEEE80211_T_OFDM_QUARTER:
54424a07b5bSSam Leffler bitsPerSymbol = (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000;
54524a07b5bSSam Leffler KASSERT(bitsPerSymbol != 0, ("1/2 rate bps"));
546b032f27cSSam Leffler
54724a07b5bSSam Leffler numBits = OFDM_PLCP_BITS + (frameLen << 3);
54824a07b5bSSam Leffler numSymbols = howmany(numBits, bitsPerSymbol);
54924a07b5bSSam Leffler txTime = OFDM_QUARTER_SIFS_TIME
55024a07b5bSSam Leffler + OFDM_QUARTER_PREAMBLE_TIME
55124a07b5bSSam Leffler + (numSymbols * OFDM_QUARTER_SYMBOL_TIME);
55224a07b5bSSam Leffler break;
553b032f27cSSam Leffler case IEEE80211_T_TURBO:
554b032f27cSSam Leffler /* we still save OFDM rates in kbps - so double them */
555b032f27cSSam Leffler bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
556b032f27cSSam Leffler KASSERT(bitsPerSymbol != 0, ("turbo bps"));
557b032f27cSSam Leffler
558b032f27cSSam Leffler numBits = TURBO_PLCP_BITS + (frameLen << 3);
559b032f27cSSam Leffler numSymbols = howmany(numBits, bitsPerSymbol);
560b032f27cSSam Leffler txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
561b032f27cSSam Leffler + (numSymbols * TURBO_SYMBOL_TIME);
562b032f27cSSam Leffler break;
563b032f27cSSam Leffler default:
564b032f27cSSam Leffler panic("%s: unknown phy %u (rate %u)\n", __func__,
565b032f27cSSam Leffler rt->info[rix].phy, rate);
566b032f27cSSam Leffler }
567b032f27cSSam Leffler return txTime;
568b032f27cSSam Leffler }
569f8bf74f2SAdrian Chadd
570f8bf74f2SAdrian Chadd static const uint16_t ht20_bps[32] = {
571f8bf74f2SAdrian Chadd 26, 52, 78, 104, 156, 208, 234, 260,
572f8bf74f2SAdrian Chadd 52, 104, 156, 208, 312, 416, 468, 520,
573f8bf74f2SAdrian Chadd 78, 156, 234, 312, 468, 624, 702, 780,
574f8bf74f2SAdrian Chadd 104, 208, 312, 416, 624, 832, 936, 1040
575f8bf74f2SAdrian Chadd };
576f8bf74f2SAdrian Chadd static const uint16_t ht40_bps[32] = {
577f8bf74f2SAdrian Chadd 54, 108, 162, 216, 324, 432, 486, 540,
578f8bf74f2SAdrian Chadd 108, 216, 324, 432, 648, 864, 972, 1080,
579f8bf74f2SAdrian Chadd 162, 324, 486, 648, 972, 1296, 1458, 1620,
580f8bf74f2SAdrian Chadd 216, 432, 648, 864, 1296, 1728, 1944, 2160
581f8bf74f2SAdrian Chadd };
582f8bf74f2SAdrian Chadd
583f8bf74f2SAdrian Chadd #define OFDM_PLCP_BITS 22
584f8bf74f2SAdrian Chadd #define HT_L_STF 8
585f8bf74f2SAdrian Chadd #define HT_L_LTF 8
586f8bf74f2SAdrian Chadd #define HT_L_SIG 4
587f8bf74f2SAdrian Chadd #define HT_SIG 8
588f8bf74f2SAdrian Chadd #define HT_STF 4
589f8bf74f2SAdrian Chadd #define HT_LTF(n) ((n) * 4)
590f8bf74f2SAdrian Chadd
591f8bf74f2SAdrian Chadd /*
592f8bf74f2SAdrian Chadd * Calculate the transmit duration of an 11n frame.
593f8bf74f2SAdrian Chadd */
594f8bf74f2SAdrian Chadd uint32_t
ieee80211_compute_duration_ht(uint32_t frameLen,uint16_t rate,int streams,int isht40,int isShortGI)595f8bf74f2SAdrian Chadd ieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate,
596f8bf74f2SAdrian Chadd int streams, int isht40, int isShortGI)
597f8bf74f2SAdrian Chadd {
598f8bf74f2SAdrian Chadd uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
599f8bf74f2SAdrian Chadd
600f8bf74f2SAdrian Chadd KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
601f8bf74f2SAdrian Chadd KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
602f8bf74f2SAdrian Chadd
603f8bf74f2SAdrian Chadd if (isht40)
604f8bf74f2SAdrian Chadd bitsPerSymbol = ht40_bps[rate & 0x1f];
605f8bf74f2SAdrian Chadd else
606f8bf74f2SAdrian Chadd bitsPerSymbol = ht20_bps[rate & 0x1f];
607f8bf74f2SAdrian Chadd numBits = OFDM_PLCP_BITS + (frameLen << 3);
608f8bf74f2SAdrian Chadd numSymbols = howmany(numBits, bitsPerSymbol);
609f8bf74f2SAdrian Chadd if (isShortGI)
610f8bf74f2SAdrian Chadd txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */
611f8bf74f2SAdrian Chadd else
612f8bf74f2SAdrian Chadd txTime = numSymbols * 4; /* 4us */
613f8bf74f2SAdrian Chadd return txTime + HT_L_STF + HT_L_LTF +
614f8bf74f2SAdrian Chadd HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
615f8bf74f2SAdrian Chadd }
616f8bf74f2SAdrian Chadd
617f8bf74f2SAdrian Chadd #undef HT_LTF
618f8bf74f2SAdrian Chadd #undef HT_STF
619f8bf74f2SAdrian Chadd #undef HT_SIG
620f8bf74f2SAdrian Chadd #undef HT_L_SIG
621f8bf74f2SAdrian Chadd #undef HT_L_LTF
622f8bf74f2SAdrian Chadd #undef HT_L_STF
623f8bf74f2SAdrian Chadd #undef OFDM_PLCP_BITS
6241568caafSAdrian Chadd
6251568caafSAdrian Chadd
6261568caafSAdrian Chadd /*
6271568caafSAdrian Chadd * A bitmask of allowable rates for each spatial stream
6281568caafSAdrian Chadd * and bandwidth.
6291568caafSAdrian Chadd *
6301568caafSAdrian Chadd * This is based on 802.11-2020 21.5 (Parameters for VHT-MCSs.)
6311568caafSAdrian Chadd *
6321568caafSAdrian Chadd * Not all MCS rate / channel widths are valid, as there needs
6331568caafSAdrian Chadd * to be an integer number of symbols and the number of tones
6341568caafSAdrian Chadd * available for each channel bandwidth doesn't result in
6351568caafSAdrian Chadd * an integer value.
6361568caafSAdrian Chadd */
6371568caafSAdrian Chadd static uint16_t ieee80211_vht_mcs_allowed_list_20[] = {
6381568caafSAdrian Chadd 0x01ff, 0x01ff, 0x03ff, 0x01ff, 0x01ff, 0x03ff, 0x01ff, 0x01ff,
6391568caafSAdrian Chadd };
6401568caafSAdrian Chadd
6411568caafSAdrian Chadd static uint16_t ieee80211_vht_mcs_allowed_list_40[] = {
6421568caafSAdrian Chadd 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
6431568caafSAdrian Chadd };
6441568caafSAdrian Chadd
6451568caafSAdrian Chadd static uint16_t ieee80211_vht_mcs_allowed_list_80[] = {
6461568caafSAdrian Chadd 0x03ff, 0x03ff, 0x03bf, 0x03ff, 0x03ff, 0x01ff, 0x03bf, 0x03ff,
6471568caafSAdrian Chadd };
6481568caafSAdrian Chadd
6491568caafSAdrian Chadd static uint16_t ieee80211_vht_mcs_allowed_list_160[] = {
6501568caafSAdrian Chadd 0x03ff, 0x03ff, 0x01ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
6511568caafSAdrian Chadd };
6521568caafSAdrian Chadd
6531568caafSAdrian Chadd /**
6541568caafSAdrian Chadd * @brief Fetch the allowable MCS mask for the given channel bandwidth and NSS
6551568caafSAdrian Chadd *
6561568caafSAdrian Chadd * Return a bitmask of valid MCS rates from 0..9 given a channel bandwith
6571568caafSAdrian Chadd * and number of spatial streams.
6581568caafSAdrian Chadd *
6591568caafSAdrian Chadd * See 802.11-2020 21.5 (Parameters for VHT-MCSs) for more details.
6601568caafSAdrian Chadd *
6611568caafSAdrian Chadd * @param bw channel bandwidth, via enum ieee80211_sta_rx_bw
6621568caafSAdrian Chadd * @param nss number of spatial streams, 1..8
6631568caafSAdrian Chadd * @returns bitmask of valid MCS rates from 0..9
6641568caafSAdrian Chadd */
6651568caafSAdrian Chadd uint16_t
ieee80211_phy_vht_get_mcs_mask(enum ieee80211_sta_rx_bw bw,uint8_t nss)6661568caafSAdrian Chadd ieee80211_phy_vht_get_mcs_mask(enum ieee80211_sta_rx_bw bw, uint8_t nss)
6671568caafSAdrian Chadd {
6681568caafSAdrian Chadd if (nss == 0 || nss > 8)
6691568caafSAdrian Chadd return (0);
6701568caafSAdrian Chadd
6711568caafSAdrian Chadd switch (bw) {
6721568caafSAdrian Chadd case IEEE80211_STA_RX_BW_20:
6731568caafSAdrian Chadd return (ieee80211_vht_mcs_allowed_list_20[nss - 1]);
6741568caafSAdrian Chadd case IEEE80211_STA_RX_BW_40:
6751568caafSAdrian Chadd return (ieee80211_vht_mcs_allowed_list_40[nss - 1]);
6761568caafSAdrian Chadd case IEEE80211_STA_RX_BW_80:
6771568caafSAdrian Chadd return (ieee80211_vht_mcs_allowed_list_80[nss - 1]);
6781568caafSAdrian Chadd case IEEE80211_STA_RX_BW_160:
6791568caafSAdrian Chadd return (ieee80211_vht_mcs_allowed_list_160[nss - 1]);
6801568caafSAdrian Chadd case IEEE80211_STA_RX_BW_320:
6811568caafSAdrian Chadd /* invalid for VHT */
6821568caafSAdrian Chadd return (0);
6831568caafSAdrian Chadd }
6841568caafSAdrian Chadd }
6851568caafSAdrian Chadd
6861568caafSAdrian Chadd /**
6871568caafSAdrian Chadd * @brief Check if the given NSS/MCS combination is valid for the given channel
6881568caafSAdrian Chadd * bandwidth
6891568caafSAdrian Chadd *
6901568caafSAdrian Chadd * See 802.11-2020 21.5 (Parameters for VHT-MCSs) for more details.
6911568caafSAdrian Chadd *
6921568caafSAdrian Chadd * @param bw channel bandwidth, via enum ieee80211_sta_rx_bw
6931568caafSAdrian Chadd * @param nss number of spatial streams, 1..8
6941568caafSAdrian Chadd * @param mcs MCS rate, 0..9
6951568caafSAdrian Chadd * @retval true if the NSS / MCS / bandwidth combination is valid
6961568caafSAdrian Chadd * @retval false if the NSS / MCS / bandwidth combination is not valid
6971568caafSAdrian Chadd */
6981568caafSAdrian Chadd bool
ieee80211_phy_vht_validate_mcs(enum ieee80211_sta_rx_bw bw,uint8_t nss,uint8_t mcs)6991568caafSAdrian Chadd ieee80211_phy_vht_validate_mcs(enum ieee80211_sta_rx_bw bw, uint8_t nss,
7001568caafSAdrian Chadd uint8_t mcs)
7011568caafSAdrian Chadd {
7021568caafSAdrian Chadd uint16_t mask;
7031568caafSAdrian Chadd
7041568caafSAdrian Chadd mask = ieee80211_phy_vht_get_mcs_mask(bw, nss);
7051568caafSAdrian Chadd if (mask == 0)
7061568caafSAdrian Chadd return (false);
7071568caafSAdrian Chadd
7081568caafSAdrian Chadd return ((mask & (1 << mcs)) != 0);
7091568caafSAdrian Chadd }
710*ff0e22f8SAdrian Chadd
711*ff0e22f8SAdrian Chadd struct mcs_entry {
712*ff0e22f8SAdrian Chadd int n_sym; /* Number of bits per symbol */
713*ff0e22f8SAdrian Chadd int cod_n; /* Coding rate numerator */
714*ff0e22f8SAdrian Chadd int cod_d; /* Coding rate denominator */
715*ff0e22f8SAdrian Chadd };
716*ff0e22f8SAdrian Chadd
717*ff0e22f8SAdrian Chadd /*
718*ff0e22f8SAdrian Chadd * These parameters are taken from 802.11-2020 Table 21-29
719*ff0e22f8SAdrian Chadd * (VHT-MCSs for Mandatory 20 MHZ, Nss=1).
720*ff0e22f8SAdrian Chadd *
721*ff0e22f8SAdrian Chadd * n_sym corresponds to "Nbpscs", cod_n/cod_d corresponds to
722*ff0e22f8SAdrian Chadd * "R".
723*ff0e22f8SAdrian Chadd */
724*ff0e22f8SAdrian Chadd static struct mcs_entry mcs_entries[] = {
725*ff0e22f8SAdrian Chadd { 1, 1, 2 }, /* MCS0 */
726*ff0e22f8SAdrian Chadd { 2, 1, 2 },
727*ff0e22f8SAdrian Chadd { 2, 3, 4 },
728*ff0e22f8SAdrian Chadd { 4, 1, 2 },
729*ff0e22f8SAdrian Chadd { 4, 3, 4 },
730*ff0e22f8SAdrian Chadd { 6, 2, 3 },
731*ff0e22f8SAdrian Chadd { 6, 3, 4 },
732*ff0e22f8SAdrian Chadd { 6, 5, 6 },
733*ff0e22f8SAdrian Chadd { 8, 3, 4 },
734*ff0e22f8SAdrian Chadd { 8, 5, 6 }, /* MCS9 */
735*ff0e22f8SAdrian Chadd };
736*ff0e22f8SAdrian Chadd
737*ff0e22f8SAdrian Chadd /**
738*ff0e22f8SAdrian Chadd * @brief Calculate the bitrate of the given VHT MCS rate.
739*ff0e22f8SAdrian Chadd *
740*ff0e22f8SAdrian Chadd * @param bw Channel bandwidth (enum ieee80211_sta_rx_bw)
741*ff0e22f8SAdrian Chadd * @param nss Number of spatial streams, 1..8
742*ff0e22f8SAdrian Chadd * @param mcs MCS, 0..9
743*ff0e22f8SAdrian Chadd * @param is_shortgi True if short guard-interval (400nS)
744*ff0e22f8SAdrian Chadd * false otherwise (800nS)
745*ff0e22f8SAdrian Chadd *
746*ff0e22f8SAdrian Chadd * @returns The bitrate in kbit/sec.
747*ff0e22f8SAdrian Chadd */
748*ff0e22f8SAdrian Chadd uint32_t
ieee80211_phy_vht_get_mcs_kbit(enum ieee80211_sta_rx_bw bw,uint8_t nss,uint8_t mcs,bool is_shortgi)749*ff0e22f8SAdrian Chadd ieee80211_phy_vht_get_mcs_kbit(enum ieee80211_sta_rx_bw bw,
750*ff0e22f8SAdrian Chadd uint8_t nss, uint8_t mcs, bool is_shortgi)
751*ff0e22f8SAdrian Chadd {
752*ff0e22f8SAdrian Chadd uint32_t sym_len, n_carriers;
753*ff0e22f8SAdrian Chadd
754*ff0e22f8SAdrian Chadd /* Validate MCS 0..9, NSS 1..8 */
755*ff0e22f8SAdrian Chadd if (mcs > 9)
756*ff0e22f8SAdrian Chadd return (0);
757*ff0e22f8SAdrian Chadd if (nss == 0 || nss > 8)
758*ff0e22f8SAdrian Chadd return (0);
759*ff0e22f8SAdrian Chadd
760*ff0e22f8SAdrian Chadd /*
761*ff0e22f8SAdrian Chadd * Short-GI - 3.6uS symbol time, long-GI - 4.0uS symbol time
762*ff0e22f8SAdrian Chadd *
763*ff0e22f8SAdrian Chadd * See 802.11-2020 Table 21-5 (Timing-related constraints.)
764*ff0e22f8SAdrian Chadd */
765*ff0e22f8SAdrian Chadd if (is_shortgi)
766*ff0e22f8SAdrian Chadd sym_len = 36;
767*ff0e22f8SAdrian Chadd else
768*ff0e22f8SAdrian Chadd sym_len = 40;
769*ff0e22f8SAdrian Chadd
770*ff0e22f8SAdrian Chadd /*
771*ff0e22f8SAdrian Chadd * Calculate the number of carriers for the given channel bandwidth
772*ff0e22f8SAdrian Chadd *
773*ff0e22f8SAdrian Chadd * See 802.11-2020 Table 21-5 (Timing-related constraints.)
774*ff0e22f8SAdrian Chadd */
775*ff0e22f8SAdrian Chadd switch (bw) {
776*ff0e22f8SAdrian Chadd case IEEE80211_STA_RX_BW_20:
777*ff0e22f8SAdrian Chadd n_carriers = 52;
778*ff0e22f8SAdrian Chadd break;
779*ff0e22f8SAdrian Chadd case IEEE80211_STA_RX_BW_40:
780*ff0e22f8SAdrian Chadd n_carriers = 108;
781*ff0e22f8SAdrian Chadd break;
782*ff0e22f8SAdrian Chadd case IEEE80211_STA_RX_BW_80:
783*ff0e22f8SAdrian Chadd n_carriers = 234;
784*ff0e22f8SAdrian Chadd break;
785*ff0e22f8SAdrian Chadd case IEEE80211_STA_RX_BW_160:
786*ff0e22f8SAdrian Chadd n_carriers = 468;
787*ff0e22f8SAdrian Chadd break;
788*ff0e22f8SAdrian Chadd default:
789*ff0e22f8SAdrian Chadd return (0);
790*ff0e22f8SAdrian Chadd }
791*ff0e22f8SAdrian Chadd
792*ff0e22f8SAdrian Chadd return ((n_carriers * mcs_entries[mcs].n_sym * mcs_entries[mcs].cod_n *
793*ff0e22f8SAdrian Chadd nss * 10000) / (mcs_entries[mcs].cod_d * sym_len));
794*ff0e22f8SAdrian Chadd }
795