1178354Ssam/*-
2178354Ssam * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
3178354Ssam * All rights reserved.
4178354Ssam *
5178354Ssam * Redistribution and use in source and binary forms, with or without
6178354Ssam * modification, are permitted provided that the following conditions
7178354Ssam * are met:
8178354Ssam * 1. Redistributions of source code must retain the above copyright
9178354Ssam *    notice, this list of conditions and the following disclaimer.
10178354Ssam * 2. Redistributions in binary form must reproduce the above copyright
11178354Ssam *    notice, this list of conditions and the following disclaimer in the
12178354Ssam *    documentation and/or other materials provided with the distribution.
13178354Ssam *
14178354Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15178354Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16178354Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17178354Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18178354Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19178354Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20178354Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21178354Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22178354Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23178354Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24178354Ssam */
25178354Ssam
26178354Ssam#include <sys/cdefs.h>
27178354Ssam__FBSDID("$FreeBSD$");
28178354Ssam
29178354Ssam/*
30178354Ssam * IEEE 802.11 PHY-related support.
31178354Ssam */
32178354Ssam
33178354Ssam#include "opt_inet.h"
34178354Ssam
35178354Ssam#include <sys/param.h>
36178354Ssam#include <sys/kernel.h>
37178354Ssam#include <sys/systm.h>
38257179Sglebius#include <sys/malloc.h>
39178354Ssam
40178354Ssam#include <sys/socket.h>
41178354Ssam
42178354Ssam#include <net/if.h>
43178354Ssam#include <net/if_media.h>
44178354Ssam
45257179Sglebius#include <net/ethernet.h>
46257179Sglebius#include <net/route.h>
47257179Sglebius
48178354Ssam#include <net80211/ieee80211_var.h>
49178354Ssam#include <net80211/ieee80211_phy.h>
50178354Ssam
51178354Ssam#ifdef notyet
52178354Ssamstruct ieee80211_ds_plcp_hdr {
53178354Ssam	uint8_t		i_signal;
54178354Ssam	uint8_t		i_service;
55178354Ssam	uint16_t	i_length;
56178354Ssam	uint16_t	i_crc;
57178354Ssam} __packed;
58178354Ssam
59178354Ssam#endif	/* notyet */
60178354Ssam
61178354Ssam/* shorthands to compact tables for readability */
62178354Ssam#define	OFDM	IEEE80211_T_OFDM
63178354Ssam#define	CCK	IEEE80211_T_CCK
64178354Ssam#define	TURBO	IEEE80211_T_TURBO
65188821Ssam#define	HALF	IEEE80211_T_OFDM_HALF
66188821Ssam#define	QUART	IEEE80211_T_OFDM_QUARTER
67252727Sadrian#define	HT	IEEE80211_T_HT
68252727Sadrian/* XXX the 11n and the basic rate flag are unfortunately overlapping. Grr. */
69252727Sadrian#define	N(r)	(IEEE80211_RATE_MCS | r)
70188821Ssam#define	PBCC	(IEEE80211_T_OFDM_QUARTER+1)		/* XXX */
71252727Sadrian#define	B(r)	(IEEE80211_RATE_BASIC | r)
72182833Ssam#define	Mb(x)	(x*1000)
73178354Ssam
74178354Ssamstatic struct ieee80211_rate_table ieee80211_11b_table = {
75182833Ssam    .rateCount = 4,		/* XXX no PBCC */
76182833Ssam    .info = {
77182833Ssam/*                                   short            ctrl  */
78182833Ssam/*                                Preamble  dot11Rate Rate */
79182833Ssam     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },/*   1 Mb */
80182833Ssam     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },/*   2 Mb */
81182833Ssam     [2] = { .phy = CCK,     5500,    0x04,     B(11),   1 },/* 5.5 Mb */
82182833Ssam     [3] = { .phy = CCK,    11000,    0x04,     B(22),   1 },/*  11 Mb */
83182833Ssam     [4] = { .phy = PBCC,   22000,    0x04,        44,   3 } /*  22 Mb */
84182833Ssam    },
85178354Ssam};
86178354Ssam
87178354Ssamstatic struct ieee80211_rate_table ieee80211_11g_table = {
88182833Ssam    .rateCount = 12,
89182833Ssam    .info = {
90182833Ssam/*                                   short            ctrl  */
91182833Ssam/*                                Preamble  dot11Rate Rate */
92182833Ssam     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },
93182833Ssam     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },
94182833Ssam     [2] = { .phy = CCK,     5500,    0x04,     B(11),   2 },
95182833Ssam     [3] = { .phy = CCK,    11000,    0x04,     B(22),   3 },
96182833Ssam     [4] = { .phy = OFDM,    6000,    0x00,        12,   4 },
97182833Ssam     [5] = { .phy = OFDM,    9000,    0x00,        18,   4 },
98182833Ssam     [6] = { .phy = OFDM,   12000,    0x00,        24,   6 },
99182833Ssam     [7] = { .phy = OFDM,   18000,    0x00,        36,   6 },
100182833Ssam     [8] = { .phy = OFDM,   24000,    0x00,        48,   8 },
101182833Ssam     [9] = { .phy = OFDM,   36000,    0x00,        72,   8 },
102182833Ssam    [10] = { .phy = OFDM,   48000,    0x00,        96,   8 },
103182833Ssam    [11] = { .phy = OFDM,   54000,    0x00,       108,   8 }
104182833Ssam    },
105178354Ssam};
106178354Ssam
107178354Ssamstatic struct ieee80211_rate_table ieee80211_11a_table = {
108182833Ssam    .rateCount = 8,
109182833Ssam    .info = {
110182833Ssam/*                                   short            ctrl  */
111182833Ssam/*                                Preamble  dot11Rate Rate */
112182833Ssam     [0] = { .phy = OFDM,    6000,    0x00,     B(12),   0 },
113182833Ssam     [1] = { .phy = OFDM,    9000,    0x00,        18,   0 },
114182833Ssam     [2] = { .phy = OFDM,   12000,    0x00,     B(24),   2 },
115182833Ssam     [3] = { .phy = OFDM,   18000,    0x00,        36,   2 },
116182833Ssam     [4] = { .phy = OFDM,   24000,    0x00,     B(48),   4 },
117182833Ssam     [5] = { .phy = OFDM,   36000,    0x00,        72,   4 },
118182833Ssam     [6] = { .phy = OFDM,   48000,    0x00,        96,   4 },
119182833Ssam     [7] = { .phy = OFDM,   54000,    0x00,       108,   4 }
120182833Ssam    },
121178354Ssam};
122178354Ssam
123178354Ssamstatic struct ieee80211_rate_table ieee80211_half_table = {
124182833Ssam    .rateCount = 8,
125182833Ssam    .info = {
126182833Ssam/*                                   short            ctrl  */
127182833Ssam/*                                Preamble  dot11Rate Rate */
128188821Ssam     [0] = { .phy = HALF,    3000,    0x00,      B(6),   0 },
129188821Ssam     [1] = { .phy = HALF,    4500,    0x00,         9,   0 },
130188821Ssam     [2] = { .phy = HALF,    6000,    0x00,     B(12),   2 },
131188821Ssam     [3] = { .phy = HALF,    9000,    0x00,        18,   2 },
132188821Ssam     [4] = { .phy = HALF,   12000,    0x00,     B(24),   4 },
133188821Ssam     [5] = { .phy = HALF,   18000,    0x00,        36,   4 },
134188821Ssam     [6] = { .phy = HALF,   24000,    0x00,        48,   4 },
135188821Ssam     [7] = { .phy = HALF,   27000,    0x00,        54,   4 }
136182833Ssam    },
137178354Ssam};
138178354Ssam
139178354Ssamstatic struct ieee80211_rate_table ieee80211_quarter_table = {
140182833Ssam    .rateCount = 8,
141182833Ssam    .info = {
142182833Ssam/*                                   short            ctrl  */
143182833Ssam/*                                Preamble  dot11Rate Rate */
144188821Ssam     [0] = { .phy = QUART,   1500,    0x00,      B(3),   0 },
145188821Ssam     [1] = { .phy = QUART,   2250,    0x00,         4,   0 },
146188821Ssam     [2] = { .phy = QUART,   3000,    0x00,      B(9),   2 },
147188821Ssam     [3] = { .phy = QUART,   4500,    0x00,         9,   2 },
148188821Ssam     [4] = { .phy = QUART,   6000,    0x00,     B(12),   4 },
149188821Ssam     [5] = { .phy = QUART,   9000,    0x00,        18,   4 },
150188821Ssam     [6] = { .phy = QUART,  12000,    0x00,        24,   4 },
151188821Ssam     [7] = { .phy = QUART,  13500,    0x00,        27,   4 }
152182833Ssam    },
153178354Ssam};
154178354Ssam
155178354Ssamstatic struct ieee80211_rate_table ieee80211_turbog_table = {
156182833Ssam    .rateCount = 7,
157182833Ssam    .info = {
158182833Ssam/*                                   short            ctrl  */
159182833Ssam/*                                Preamble  dot11Rate Rate */
160182833Ssam     [0] = { .phy = TURBO,   12000,   0x00,     B(12),   0 },
161182833Ssam     [1] = { .phy = TURBO,   24000,   0x00,     B(24),   1 },
162182833Ssam     [2] = { .phy = TURBO,   36000,   0x00,        36,   1 },
163182833Ssam     [3] = { .phy = TURBO,   48000,   0x00,     B(48),   3 },
164182833Ssam     [4] = { .phy = TURBO,   72000,   0x00,        72,   3 },
165182833Ssam     [5] = { .phy = TURBO,   96000,   0x00,        96,   3 },
166182833Ssam     [6] = { .phy = TURBO,  108000,   0x00,       108,   3 }
167182833Ssam    },
168178354Ssam};
169178354Ssam
170178354Ssamstatic struct ieee80211_rate_table ieee80211_turboa_table = {
171182833Ssam    .rateCount = 8,
172182833Ssam    .info = {
173182833Ssam/*                                   short            ctrl  */
174182833Ssam/*                                Preamble  dot11Rate Rate */
175182833Ssam     [0] = { .phy = TURBO,   12000,   0x00,     B(12),   0 },
176182833Ssam     [1] = { .phy = TURBO,   18000,   0x00,        18,   0 },
177182833Ssam     [2] = { .phy = TURBO,   24000,   0x00,     B(24),   2 },
178182833Ssam     [3] = { .phy = TURBO,   36000,   0x00,        36,   2 },
179182833Ssam     [4] = { .phy = TURBO,   48000,   0x00,     B(48),   4 },
180182833Ssam     [5] = { .phy = TURBO,   72000,   0x00,        72,   4 },
181182833Ssam     [6] = { .phy = TURBO,   96000,   0x00,        96,   4 },
182182833Ssam     [7] = { .phy = TURBO,  108000,   0x00,       108,   4 }
183182833Ssam    },
184178354Ssam};
185178354Ssam
186252727Sadrianstatic struct ieee80211_rate_table ieee80211_11ng_table = {
187252727Sadrian    .rateCount = 36,
188252727Sadrian    .info = {
189252727Sadrian/*                                   short            ctrl  */
190252727Sadrian/*                                Preamble  dot11Rate Rate */
191252727Sadrian     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },
192252727Sadrian     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },
193252727Sadrian     [2] = { .phy = CCK,     5500,    0x04,     B(11),   2 },
194252727Sadrian     [3] = { .phy = CCK,    11000,    0x04,     B(22),   3 },
195252727Sadrian     [4] = { .phy = OFDM,    6000,    0x00,        12,   4 },
196252727Sadrian     [5] = { .phy = OFDM,    9000,    0x00,        18,   4 },
197252727Sadrian     [6] = { .phy = OFDM,   12000,    0x00,        24,   6 },
198252727Sadrian     [7] = { .phy = OFDM,   18000,    0x00,        36,   6 },
199252727Sadrian     [8] = { .phy = OFDM,   24000,    0x00,        48,   8 },
200252727Sadrian     [9] = { .phy = OFDM,   36000,    0x00,        72,   8 },
201252727Sadrian    [10] = { .phy = OFDM,   48000,    0x00,        96,   8 },
202252727Sadrian    [11] = { .phy = OFDM,   54000,    0x00,       108,   8 },
203252727Sadrian
204252727Sadrian    [12] = { .phy = HT,      6500,    0x00,      N(0),   4 },
205252727Sadrian    [13] = { .phy = HT,     13000,    0x00,      N(1),   6 },
206252727Sadrian    [14] = { .phy = HT,     19500,    0x00,      N(2),   6 },
207252727Sadrian    [15] = { .phy = HT,     26000,    0x00,      N(3),   8 },
208252727Sadrian    [16] = { .phy = HT,     39000,    0x00,      N(4),   8 },
209252727Sadrian    [17] = { .phy = HT,     52000,    0x00,      N(5),   8 },
210252727Sadrian    [18] = { .phy = HT,     58500,    0x00,      N(6),   8 },
211252727Sadrian    [19] = { .phy = HT,     65000,    0x00,      N(7),   8 },
212252727Sadrian
213252727Sadrian    [20] = { .phy = HT,     13000,    0x00,      N(8),   4 },
214252727Sadrian    [21] = { .phy = HT,     26000,    0x00,      N(9),   6 },
215252727Sadrian    [22] = { .phy = HT,     39000,    0x00,     N(10),   6 },
216252727Sadrian    [23] = { .phy = HT,     52000,    0x00,     N(11),   8 },
217252727Sadrian    [24] = { .phy = HT,     78000,    0x00,     N(12),   8 },
218252727Sadrian    [25] = { .phy = HT,    104000,    0x00,     N(13),   8 },
219252727Sadrian    [26] = { .phy = HT,    117000,    0x00,     N(14),   8 },
220252727Sadrian    [27] = { .phy = HT,    130000,    0x00,     N(15),   8 },
221252727Sadrian
222252727Sadrian    [28] = { .phy = HT,     19500,    0x00,     N(16),   4 },
223252727Sadrian    [29] = { .phy = HT,     39000,    0x00,     N(17),   6 },
224252727Sadrian    [30] = { .phy = HT,     58500,    0x00,     N(18),   6 },
225252727Sadrian    [31] = { .phy = HT,     78000,    0x00,     N(19),   8 },
226252727Sadrian    [32] = { .phy = HT,    117000,    0x00,     N(20),   8 },
227252727Sadrian    [33] = { .phy = HT,    156000,    0x00,     N(21),   8 },
228252727Sadrian    [34] = { .phy = HT,    175500,    0x00,     N(22),   8 },
229252727Sadrian    [35] = { .phy = HT,    195000,    0x00,     N(23),   8 },
230252727Sadrian
231252727Sadrian    },
232252727Sadrian};
233252727Sadrian
234252727Sadrianstatic struct ieee80211_rate_table ieee80211_11na_table = {
235252727Sadrian    .rateCount = 32,
236252727Sadrian    .info = {
237252727Sadrian/*                                   short            ctrl  */
238252727Sadrian/*                                Preamble  dot11Rate Rate */
239252727Sadrian     [0] = { .phy = OFDM,    6000,    0x00,     B(12),   0 },
240252727Sadrian     [1] = { .phy = OFDM,    9000,    0x00,        18,   0 },
241252727Sadrian     [2] = { .phy = OFDM,   12000,    0x00,     B(24),   2 },
242252727Sadrian     [3] = { .phy = OFDM,   18000,    0x00,        36,   2 },
243252727Sadrian     [4] = { .phy = OFDM,   24000,    0x00,     B(48),   4 },
244252727Sadrian     [5] = { .phy = OFDM,   36000,    0x00,        72,   4 },
245252727Sadrian     [6] = { .phy = OFDM,   48000,    0x00,        96,   4 },
246252727Sadrian     [7] = { .phy = OFDM,   54000,    0x00,       108,   4 },
247252727Sadrian
248252727Sadrian     [8] = { .phy = HT,      6500,    0x00,      N(0),   0 },
249252727Sadrian     [9] = { .phy = HT,     13000,    0x00,      N(1),   2 },
250252727Sadrian    [10] = { .phy = HT,     19500,    0x00,      N(2),   2 },
251252727Sadrian    [11] = { .phy = HT,     26000,    0x00,      N(3),   4 },
252252727Sadrian    [12] = { .phy = HT,     39000,    0x00,      N(4),   4 },
253252727Sadrian    [13] = { .phy = HT,     52000,    0x00,      N(5),   4 },
254252727Sadrian    [14] = { .phy = HT,     58500,    0x00,      N(6),   4 },
255252727Sadrian    [15] = { .phy = HT,     65000,    0x00,      N(7),   4 },
256252727Sadrian
257252727Sadrian    [16] = { .phy = HT,     13000,    0x00,      N(8),   0 },
258252727Sadrian    [17] = { .phy = HT,     26000,    0x00,      N(9),   2 },
259252727Sadrian    [18] = { .phy = HT,     39000,    0x00,     N(10),   2 },
260252727Sadrian    [19] = { .phy = HT,     52000,    0x00,     N(11),   4 },
261252727Sadrian    [20] = { .phy = HT,     78000,    0x00,     N(12),   4 },
262252727Sadrian    [21] = { .phy = HT,    104000,    0x00,     N(13),   4 },
263252727Sadrian    [22] = { .phy = HT,    117000,    0x00,     N(14),   4 },
264252727Sadrian    [23] = { .phy = HT,    130000,    0x00,     N(15),   4 },
265252727Sadrian
266252727Sadrian    [24] = { .phy = HT,     19500,    0x00,     N(16),   0 },
267252727Sadrian    [25] = { .phy = HT,     39000,    0x00,     N(17),   2 },
268252727Sadrian    [26] = { .phy = HT,     58500,    0x00,     N(18),   2 },
269252727Sadrian    [27] = { .phy = HT,     78000,    0x00,     N(19),   4 },
270252727Sadrian    [28] = { .phy = HT,    117000,    0x00,     N(20),   4 },
271252727Sadrian    [29] = { .phy = HT,    156000,    0x00,     N(21),   4 },
272252727Sadrian    [30] = { .phy = HT,    175500,    0x00,     N(22),   4 },
273252727Sadrian    [31] = { .phy = HT,    195000,    0x00,     N(23),   4 },
274252727Sadrian
275252727Sadrian    },
276252727Sadrian};
277252727Sadrian
278182833Ssam#undef	Mb
279182833Ssam#undef	B
280178354Ssam#undef	OFDM
281188821Ssam#undef	HALF
282188821Ssam#undef	QUART
283178354Ssam#undef	CCK
284178354Ssam#undef	TURBO
285178354Ssam#undef	XR
286252727Sadrian#undef	HT
287252727Sadrian#undef	N
288178354Ssam
289178354Ssam/*
290178354Ssam * Setup a rate table's reverse lookup table and fill in
291178354Ssam * ack durations.  The reverse lookup tables are assumed
292178354Ssam * to be initialized to zero (or at least the first entry).
293178354Ssam * We use this as a key that indicates whether or not
294178354Ssam * we've previously setup the reverse lookup table.
295178354Ssam *
296178354Ssam * XXX not reentrant, but shouldn't matter
297178354Ssam */
298178354Ssamstatic void
299178354Ssamieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
300178354Ssam{
301178354Ssam#define	WLAN_CTRL_FRAME_SIZE \
302178354Ssam	(sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
303178354Ssam
304178354Ssam	int i;
305178354Ssam
306254315Srpaulo	for (i = 0; i < nitems(rt->rateCodeToIndex); i++)
307178354Ssam		rt->rateCodeToIndex[i] = (uint8_t) -1;
308178354Ssam	for (i = 0; i < rt->rateCount; i++) {
309178354Ssam		uint8_t code = rt->info[i].dot11Rate;
310178354Ssam		uint8_t cix = rt->info[i].ctlRateIndex;
311178354Ssam		uint8_t ctl_rate = rt->info[cix].dot11Rate;
312178354Ssam
313252727Sadrian		/*
314252727Sadrian		 * Map without the basic rate bit.
315252727Sadrian		 *
316252727Sadrian		 * It's up to the caller to ensure that the basic
317252727Sadrian		 * rate bit is stripped here.
318252727Sadrian		 *
319252727Sadrian		 * For HT, use the MCS rate bit.
320252727Sadrian		 */
321252727Sadrian		code &= IEEE80211_RATE_VAL;
322252727Sadrian		if (rt->info[i].phy == IEEE80211_T_HT) {
323252727Sadrian			code |= IEEE80211_RATE_MCS;
324178354Ssam		}
325178354Ssam
326252727Sadrian		/* XXX assume the control rate is non-MCS? */
327252727Sadrian		ctl_rate &= IEEE80211_RATE_VAL;
328252727Sadrian		rt->rateCodeToIndex[code] = i;
329252727Sadrian
330178354Ssam		/*
331178354Ssam		 * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
332178354Ssam		 *     depends on whether they are marked as basic rates;
333178354Ssam		 *     the static tables are setup with an 11b-compatible
334178354Ssam		 *     2Mb/s rate which will work but is suboptimal
335178354Ssam		 *
336178354Ssam		 * NB: Control rate is always less than or equal to the
337178354Ssam		 *     current rate, so control rate's reverse lookup entry
338178354Ssam		 *     has been installed and following call is safe.
339178354Ssam		 */
340178354Ssam		rt->info[i].lpAckDuration = ieee80211_compute_duration(rt,
341178354Ssam			WLAN_CTRL_FRAME_SIZE, ctl_rate, 0);
342178354Ssam		rt->info[i].spAckDuration = ieee80211_compute_duration(rt,
343178354Ssam			WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE);
344178354Ssam	}
345178354Ssam
346178354Ssam#undef WLAN_CTRL_FRAME_SIZE
347178354Ssam}
348178354Ssam
349178354Ssam/* Setup all rate tables */
350178354Ssamstatic void
351178354Ssamieee80211_phy_init(void)
352178354Ssam{
353178354Ssam	static struct ieee80211_rate_table * const ratetables[] = {
354178354Ssam		&ieee80211_half_table,
355178354Ssam		&ieee80211_quarter_table,
356252727Sadrian		&ieee80211_11na_table,
357252727Sadrian		&ieee80211_11ng_table,
358178354Ssam		&ieee80211_turbog_table,
359178354Ssam		&ieee80211_turboa_table,
360178354Ssam		&ieee80211_11a_table,
361178354Ssam		&ieee80211_11g_table,
362178354Ssam		&ieee80211_11b_table
363178354Ssam	};
364178354Ssam	int i;
365178354Ssam
366254315Srpaulo	for (i = 0; i < nitems(ratetables); ++i)
367178354Ssam		ieee80211_setup_ratetable(ratetables[i]);
368178354Ssam
369178354Ssam}
370178354SsamSYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
371178354Ssam
372178354Ssamconst struct ieee80211_rate_table *
373178354Ssamieee80211_get_ratetable(struct ieee80211_channel *c)
374178354Ssam{
375178354Ssam	const struct ieee80211_rate_table *rt;
376178354Ssam
377178354Ssam	/* XXX HT */
378178354Ssam	if (IEEE80211_IS_CHAN_HALF(c))
379178354Ssam		rt = &ieee80211_half_table;
380178354Ssam	else if (IEEE80211_IS_CHAN_QUARTER(c))
381178354Ssam		rt = &ieee80211_quarter_table;
382178354Ssam	else if (IEEE80211_IS_CHAN_HTA(c))
383252727Sadrian		rt = &ieee80211_11na_table;
384178354Ssam	else if (IEEE80211_IS_CHAN_HTG(c))
385252727Sadrian		rt = &ieee80211_11ng_table;
386178354Ssam	else if (IEEE80211_IS_CHAN_108G(c))
387178354Ssam		rt = &ieee80211_turbog_table;
388178354Ssam	else if (IEEE80211_IS_CHAN_ST(c))
389178354Ssam		rt = &ieee80211_turboa_table;
390178354Ssam	else if (IEEE80211_IS_CHAN_TURBO(c))
391178354Ssam		rt = &ieee80211_turboa_table;
392178354Ssam	else if (IEEE80211_IS_CHAN_A(c))
393178354Ssam		rt = &ieee80211_11a_table;
394178354Ssam	else if (IEEE80211_IS_CHAN_ANYG(c))
395178354Ssam		rt = &ieee80211_11g_table;
396178354Ssam	else if (IEEE80211_IS_CHAN_B(c))
397178354Ssam		rt = &ieee80211_11b_table;
398178354Ssam	else {
399178354Ssam		/* NB: should not get here */
400178354Ssam		panic("%s: no rate table for channel; freq %u flags 0x%x\n",
401178354Ssam		      __func__, c->ic_freq, c->ic_flags);
402178354Ssam	}
403178354Ssam	return rt;
404178354Ssam}
405178354Ssam
406178354Ssam/*
407178354Ssam * Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s)
408178354Ssam *
409178354Ssam * Note we do no parameter checking; this routine is mainly
410178354Ssam * used to derive an 802.11 rate for constructing radiotap
411178354Ssam * header data for rx frames.
412178354Ssam *
413178354Ssam * XXX might be a candidate for inline
414178354Ssam */
415178354Ssamuint8_t
416178958Ssamieee80211_plcp2rate(uint8_t plcp, enum ieee80211_phytype type)
417178354Ssam{
418178958Ssam	if (type == IEEE80211_T_OFDM) {
419178354Ssam		static const uint8_t ofdm_plcp2rate[16] = {
420178354Ssam			[0xb]	= 12,
421178354Ssam			[0xf]	= 18,
422178354Ssam			[0xa]	= 24,
423178354Ssam			[0xe]	= 36,
424178354Ssam			[0x9]	= 48,
425178354Ssam			[0xd]	= 72,
426178354Ssam			[0x8]	= 96,
427178354Ssam			[0xc]	= 108
428178354Ssam		};
429178354Ssam		return ofdm_plcp2rate[plcp & 0xf];
430178958Ssam	}
431178958Ssam	if (type == IEEE80211_T_CCK) {
432178354Ssam		static const uint8_t cck_plcp2rate[16] = {
433178354Ssam			[0xa]	= 2,	/* 0x0a */
434178354Ssam			[0x4]	= 4,	/* 0x14 */
435178354Ssam			[0x7]	= 11,	/* 0x37 */
436178354Ssam			[0xe]	= 22,	/* 0x6e */
437178354Ssam			[0xc]	= 44,	/* 0xdc , actually PBCC */
438178354Ssam		};
439178354Ssam		return cck_plcp2rate[plcp & 0xf];
440178354Ssam	}
441178958Ssam	return 0;
442178354Ssam}
443178354Ssam
444178354Ssam/*
445178354Ssam * Covert 802.11 rate to PLCP signal.
446178354Ssam */
447178354Ssamuint8_t
448178958Ssamieee80211_rate2plcp(int rate, enum ieee80211_phytype type)
449178354Ssam{
450178958Ssam	/* XXX ignore type for now since rates are unique */
451178354Ssam	switch (rate) {
452178354Ssam	/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
453178354Ssam	case 12:	return 0xb;
454178354Ssam	case 18:	return 0xf;
455178354Ssam	case 24:	return 0xa;
456178354Ssam	case 36:	return 0xe;
457178354Ssam	case 48:	return 0x9;
458178354Ssam	case 72:	return 0xd;
459178354Ssam	case 96:	return 0x8;
460178354Ssam	case 108:	return 0xc;
461178958Ssam	/* CCK rates (IEEE Std 802.11b-1999 page 15, subclause 18.2.3.3) */
462178958Ssam	case 2:		return 10;
463178958Ssam	case 4:		return 20;
464178958Ssam	case 11:	return 55;
465178958Ssam	case 22:	return 110;
466178958Ssam	/* IEEE Std 802.11g-2003 page 19, subclause 19.3.2.1 */
467178958Ssam	case 44:	return 220;
468178354Ssam	}
469178958Ssam	return 0;		/* XXX unsupported/unknown rate */
470178354Ssam}
471178958Ssam
472188821Ssam#define CCK_SIFS_TIME		10
473188821Ssam#define CCK_PREAMBLE_BITS	144
474188821Ssam#define CCK_PLCP_BITS		48
475188821Ssam
476188821Ssam#define OFDM_SIFS_TIME		16
477188821Ssam#define OFDM_PREAMBLE_TIME	20
478188821Ssam#define OFDM_PLCP_BITS		22
479188821Ssam#define OFDM_SYMBOL_TIME	4
480188821Ssam
481188821Ssam#define OFDM_HALF_SIFS_TIME	32
482188821Ssam#define OFDM_HALF_PREAMBLE_TIME	40
483188821Ssam#define OFDM_HALF_PLCP_BITS	22
484188821Ssam#define OFDM_HALF_SYMBOL_TIME	8
485188821Ssam
486188821Ssam#define OFDM_QUARTER_SIFS_TIME 		64
487188821Ssam#define OFDM_QUARTER_PREAMBLE_TIME	80
488188821Ssam#define OFDM_QUARTER_PLCP_BITS		22
489188821Ssam#define OFDM_QUARTER_SYMBOL_TIME	16
490188821Ssam
491188821Ssam#define TURBO_SIFS_TIME		8
492188821Ssam#define TURBO_PREAMBLE_TIME	14
493188821Ssam#define TURBO_PLCP_BITS		22
494188821Ssam#define TURBO_SYMBOL_TIME	4
495188821Ssam
496178354Ssam/*
497178354Ssam * Compute the time to transmit a frame of length frameLen bytes
498178354Ssam * using the specified rate, phy, and short preamble setting.
499178354Ssam * SIFS is included.
500178354Ssam */
501178354Ssamuint16_t
502178354Ssamieee80211_compute_duration(const struct ieee80211_rate_table *rt,
503178354Ssam	uint32_t frameLen, uint16_t rate, int isShortPreamble)
504178354Ssam{
505178354Ssam	uint8_t rix = rt->rateCodeToIndex[rate];
506178354Ssam	uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
507178354Ssam	uint32_t kbps;
508178354Ssam
509178354Ssam	KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
510178354Ssam	kbps = rt->info[rix].rateKbps;
511178354Ssam	if (kbps == 0)			/* XXX bandaid for channel changes */
512178354Ssam		return 0;
513178354Ssam
514178354Ssam	switch (rt->info[rix].phy) {
515178354Ssam	case IEEE80211_T_CCK:
516178354Ssam		phyTime		= CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
517178354Ssam		if (isShortPreamble && rt->info[rix].shortPreamble)
518178354Ssam			phyTime >>= 1;
519178354Ssam		numBits		= frameLen << 3;
520178354Ssam		txTime		= CCK_SIFS_TIME + phyTime
521178354Ssam				+ ((numBits * 1000)/kbps);
522178354Ssam		break;
523178354Ssam	case IEEE80211_T_OFDM:
524188821Ssam		bitsPerSymbol	= (kbps * OFDM_SYMBOL_TIME) / 1000;
525188821Ssam		KASSERT(bitsPerSymbol != 0, ("full rate bps"));
526178354Ssam
527188821Ssam		numBits		= OFDM_PLCP_BITS + (frameLen << 3);
528188821Ssam		numSymbols	= howmany(numBits, bitsPerSymbol);
529188821Ssam		txTime		= OFDM_SIFS_TIME
530188821Ssam				+ OFDM_PREAMBLE_TIME
531188821Ssam				+ (numSymbols * OFDM_SYMBOL_TIME);
532188821Ssam		break;
533188821Ssam	case IEEE80211_T_OFDM_HALF:
534188821Ssam		bitsPerSymbol	= (kbps * OFDM_HALF_SYMBOL_TIME) / 1000;
535188821Ssam		KASSERT(bitsPerSymbol != 0, ("1/4 rate bps"));
536178354Ssam
537188821Ssam		numBits		= OFDM_PLCP_BITS + (frameLen << 3);
538188821Ssam		numSymbols	= howmany(numBits, bitsPerSymbol);
539188821Ssam		txTime		= OFDM_HALF_SIFS_TIME
540188821Ssam				+ OFDM_HALF_PREAMBLE_TIME
541188821Ssam				+ (numSymbols * OFDM_HALF_SYMBOL_TIME);
542188821Ssam		break;
543188821Ssam	case IEEE80211_T_OFDM_QUARTER:
544188821Ssam		bitsPerSymbol	= (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000;
545188821Ssam		KASSERT(bitsPerSymbol != 0, ("1/2 rate bps"));
546178354Ssam
547188821Ssam		numBits		= OFDM_PLCP_BITS + (frameLen << 3);
548188821Ssam		numSymbols	= howmany(numBits, bitsPerSymbol);
549188821Ssam		txTime		= OFDM_QUARTER_SIFS_TIME
550188821Ssam				+ OFDM_QUARTER_PREAMBLE_TIME
551188821Ssam				+ (numSymbols * OFDM_QUARTER_SYMBOL_TIME);
552178354Ssam		break;
553178354Ssam	case IEEE80211_T_TURBO:
554178354Ssam		/* we still save OFDM rates in kbps - so double them */
555178354Ssam		bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
556178354Ssam		KASSERT(bitsPerSymbol != 0, ("turbo bps"));
557178354Ssam
558178354Ssam		numBits       = TURBO_PLCP_BITS + (frameLen << 3);
559178354Ssam		numSymbols    = howmany(numBits, bitsPerSymbol);
560178354Ssam		txTime        = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
561178354Ssam			      + (numSymbols * TURBO_SYMBOL_TIME);
562178354Ssam		break;
563178354Ssam	default:
564178354Ssam		panic("%s: unknown phy %u (rate %u)\n", __func__,
565178354Ssam		      rt->info[rix].phy, rate);
566178354Ssam	}
567178354Ssam	return txTime;
568178354Ssam}
569252727Sadrian
570252727Sadrianstatic const uint16_t ht20_bps[32] = {
571252727Sadrian	26, 52, 78, 104, 156, 208, 234, 260,
572252727Sadrian	52, 104, 156, 208, 312, 416, 468, 520,
573252727Sadrian	78, 156, 234, 312, 468, 624, 702, 780,
574252727Sadrian	104, 208, 312, 416, 624, 832, 936, 1040
575252727Sadrian};
576252727Sadrianstatic const uint16_t ht40_bps[32] = {
577252727Sadrian	54, 108, 162, 216, 324, 432, 486, 540,
578252727Sadrian	108, 216, 324, 432, 648, 864, 972, 1080,
579252727Sadrian	162, 324, 486, 648, 972, 1296, 1458, 1620,
580252727Sadrian	216, 432, 648, 864, 1296, 1728, 1944, 2160
581252727Sadrian};
582252727Sadrian
583252727Sadrian
584252727Sadrian#define	OFDM_PLCP_BITS	22
585252727Sadrian#define	HT_L_STF	8
586252727Sadrian#define	HT_L_LTF	8
587252727Sadrian#define	HT_L_SIG	4
588252727Sadrian#define	HT_SIG		8
589252727Sadrian#define	HT_STF		4
590252727Sadrian#define	HT_LTF(n)	((n) * 4)
591252727Sadrian
592252727Sadrian/*
593252727Sadrian * Calculate the transmit duration of an 11n frame.
594252727Sadrian */
595252727Sadrianuint32_t
596252727Sadrianieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate,
597252727Sadrian    int streams, int isht40, int isShortGI)
598252727Sadrian{
599252727Sadrian	uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
600252727Sadrian
601252727Sadrian	KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
602252727Sadrian	KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
603252727Sadrian
604252727Sadrian	if (isht40)
605252727Sadrian		bitsPerSymbol = ht40_bps[rate & 0x1f];
606252727Sadrian	else
607252727Sadrian		bitsPerSymbol = ht20_bps[rate & 0x1f];
608252727Sadrian	numBits = OFDM_PLCP_BITS + (frameLen << 3);
609252727Sadrian	numSymbols = howmany(numBits, bitsPerSymbol);
610252727Sadrian	if (isShortGI)
611252727Sadrian		txTime = ((numSymbols * 18) + 4) / 5;   /* 3.6us */
612252727Sadrian	else
613252727Sadrian		txTime = numSymbols * 4;                /* 4us */
614252727Sadrian	return txTime + HT_L_STF + HT_L_LTF +
615252727Sadrian	    HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
616252727Sadrian}
617252727Sadrian
618252727Sadrian#undef	HT_LTF
619252727Sadrian#undef	HT_STF
620252727Sadrian#undef	HT_SIG
621252727Sadrian#undef	HT_L_SIG
622252727Sadrian#undef	HT_L_LTF
623252727Sadrian#undef	HT_L_STF
624252727Sadrian#undef	OFDM_PLCP_BITS
625