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>
38178354Ssam
39178354Ssam#include <sys/socket.h>
40178354Ssam
41178354Ssam#include <net/if.h>
42178354Ssam#include <net/if_media.h>
43178354Ssam
44178354Ssam#include <net80211/ieee80211_var.h>
45178354Ssam#include <net80211/ieee80211_phy.h>
46178354Ssam
47178354Ssam#ifdef notyet
48178354Ssamstruct ieee80211_ds_plcp_hdr {
49178354Ssam	uint8_t		i_signal;
50178354Ssam	uint8_t		i_service;
51178354Ssam	uint16_t	i_length;
52178354Ssam	uint16_t	i_crc;
53178354Ssam} __packed;
54178354Ssam
55178354Ssam#endif	/* notyet */
56178354Ssam
57178354Ssam/* shorthands to compact tables for readability */
58178354Ssam#define	OFDM	IEEE80211_T_OFDM
59178354Ssam#define	CCK	IEEE80211_T_CCK
60178354Ssam#define	TURBO	IEEE80211_T_TURBO
61188821Ssam#define	HALF	IEEE80211_T_OFDM_HALF
62188821Ssam#define	QUART	IEEE80211_T_OFDM_QUARTER
63252727Sadrian#define	HT	IEEE80211_T_HT
64252727Sadrian/* XXX the 11n and the basic rate flag are unfortunately overlapping. Grr. */
65252727Sadrian#define	N(r)	(IEEE80211_RATE_MCS | r)
66188821Ssam#define	PBCC	(IEEE80211_T_OFDM_QUARTER+1)		/* XXX */
67252727Sadrian#define	B(r)	(IEEE80211_RATE_BASIC | r)
68182833Ssam#define	Mb(x)	(x*1000)
69178354Ssam
70178354Ssamstatic struct ieee80211_rate_table ieee80211_11b_table = {
71182833Ssam    .rateCount = 4,		/* XXX no PBCC */
72182833Ssam    .info = {
73182833Ssam/*                                   short            ctrl  */
74182833Ssam/*                                Preamble  dot11Rate Rate */
75182833Ssam     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },/*   1 Mb */
76182833Ssam     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },/*   2 Mb */
77182833Ssam     [2] = { .phy = CCK,     5500,    0x04,     B(11),   1 },/* 5.5 Mb */
78182833Ssam     [3] = { .phy = CCK,    11000,    0x04,     B(22),   1 },/*  11 Mb */
79182833Ssam     [4] = { .phy = PBCC,   22000,    0x04,        44,   3 } /*  22 Mb */
80182833Ssam    },
81178354Ssam};
82178354Ssam
83178354Ssamstatic struct ieee80211_rate_table ieee80211_11g_table = {
84182833Ssam    .rateCount = 12,
85182833Ssam    .info = {
86182833Ssam/*                                   short            ctrl  */
87182833Ssam/*                                Preamble  dot11Rate Rate */
88182833Ssam     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },
89182833Ssam     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },
90182833Ssam     [2] = { .phy = CCK,     5500,    0x04,     B(11),   2 },
91182833Ssam     [3] = { .phy = CCK,    11000,    0x04,     B(22),   3 },
92182833Ssam     [4] = { .phy = OFDM,    6000,    0x00,        12,   4 },
93182833Ssam     [5] = { .phy = OFDM,    9000,    0x00,        18,   4 },
94182833Ssam     [6] = { .phy = OFDM,   12000,    0x00,        24,   6 },
95182833Ssam     [7] = { .phy = OFDM,   18000,    0x00,        36,   6 },
96182833Ssam     [8] = { .phy = OFDM,   24000,    0x00,        48,   8 },
97182833Ssam     [9] = { .phy = OFDM,   36000,    0x00,        72,   8 },
98182833Ssam    [10] = { .phy = OFDM,   48000,    0x00,        96,   8 },
99182833Ssam    [11] = { .phy = OFDM,   54000,    0x00,       108,   8 }
100182833Ssam    },
101178354Ssam};
102178354Ssam
103178354Ssamstatic struct ieee80211_rate_table ieee80211_11a_table = {
104182833Ssam    .rateCount = 8,
105182833Ssam    .info = {
106182833Ssam/*                                   short            ctrl  */
107182833Ssam/*                                Preamble  dot11Rate Rate */
108182833Ssam     [0] = { .phy = OFDM,    6000,    0x00,     B(12),   0 },
109182833Ssam     [1] = { .phy = OFDM,    9000,    0x00,        18,   0 },
110182833Ssam     [2] = { .phy = OFDM,   12000,    0x00,     B(24),   2 },
111182833Ssam     [3] = { .phy = OFDM,   18000,    0x00,        36,   2 },
112182833Ssam     [4] = { .phy = OFDM,   24000,    0x00,     B(48),   4 },
113182833Ssam     [5] = { .phy = OFDM,   36000,    0x00,        72,   4 },
114182833Ssam     [6] = { .phy = OFDM,   48000,    0x00,        96,   4 },
115182833Ssam     [7] = { .phy = OFDM,   54000,    0x00,       108,   4 }
116182833Ssam    },
117178354Ssam};
118178354Ssam
119178354Ssamstatic struct ieee80211_rate_table ieee80211_half_table = {
120182833Ssam    .rateCount = 8,
121182833Ssam    .info = {
122182833Ssam/*                                   short            ctrl  */
123182833Ssam/*                                Preamble  dot11Rate Rate */
124188821Ssam     [0] = { .phy = HALF,    3000,    0x00,      B(6),   0 },
125188821Ssam     [1] = { .phy = HALF,    4500,    0x00,         9,   0 },
126188821Ssam     [2] = { .phy = HALF,    6000,    0x00,     B(12),   2 },
127188821Ssam     [3] = { .phy = HALF,    9000,    0x00,        18,   2 },
128188821Ssam     [4] = { .phy = HALF,   12000,    0x00,     B(24),   4 },
129188821Ssam     [5] = { .phy = HALF,   18000,    0x00,        36,   4 },
130188821Ssam     [6] = { .phy = HALF,   24000,    0x00,        48,   4 },
131188821Ssam     [7] = { .phy = HALF,   27000,    0x00,        54,   4 }
132182833Ssam    },
133178354Ssam};
134178354Ssam
135178354Ssamstatic struct ieee80211_rate_table ieee80211_quarter_table = {
136182833Ssam    .rateCount = 8,
137182833Ssam    .info = {
138182833Ssam/*                                   short            ctrl  */
139182833Ssam/*                                Preamble  dot11Rate Rate */
140188821Ssam     [0] = { .phy = QUART,   1500,    0x00,      B(3),   0 },
141188821Ssam     [1] = { .phy = QUART,   2250,    0x00,         4,   0 },
142188821Ssam     [2] = { .phy = QUART,   3000,    0x00,      B(9),   2 },
143188821Ssam     [3] = { .phy = QUART,   4500,    0x00,         9,   2 },
144188821Ssam     [4] = { .phy = QUART,   6000,    0x00,     B(12),   4 },
145188821Ssam     [5] = { .phy = QUART,   9000,    0x00,        18,   4 },
146188821Ssam     [6] = { .phy = QUART,  12000,    0x00,        24,   4 },
147188821Ssam     [7] = { .phy = QUART,  13500,    0x00,        27,   4 }
148182833Ssam    },
149178354Ssam};
150178354Ssam
151178354Ssamstatic struct ieee80211_rate_table ieee80211_turbog_table = {
152182833Ssam    .rateCount = 7,
153182833Ssam    .info = {
154182833Ssam/*                                   short            ctrl  */
155182833Ssam/*                                Preamble  dot11Rate Rate */
156182833Ssam     [0] = { .phy = TURBO,   12000,   0x00,     B(12),   0 },
157182833Ssam     [1] = { .phy = TURBO,   24000,   0x00,     B(24),   1 },
158182833Ssam     [2] = { .phy = TURBO,   36000,   0x00,        36,   1 },
159182833Ssam     [3] = { .phy = TURBO,   48000,   0x00,     B(48),   3 },
160182833Ssam     [4] = { .phy = TURBO,   72000,   0x00,        72,   3 },
161182833Ssam     [5] = { .phy = TURBO,   96000,   0x00,        96,   3 },
162182833Ssam     [6] = { .phy = TURBO,  108000,   0x00,       108,   3 }
163182833Ssam    },
164178354Ssam};
165178354Ssam
166178354Ssamstatic struct ieee80211_rate_table ieee80211_turboa_table = {
167182833Ssam    .rateCount = 8,
168182833Ssam    .info = {
169182833Ssam/*                                   short            ctrl  */
170182833Ssam/*                                Preamble  dot11Rate Rate */
171182833Ssam     [0] = { .phy = TURBO,   12000,   0x00,     B(12),   0 },
172182833Ssam     [1] = { .phy = TURBO,   18000,   0x00,        18,   0 },
173182833Ssam     [2] = { .phy = TURBO,   24000,   0x00,     B(24),   2 },
174182833Ssam     [3] = { .phy = TURBO,   36000,   0x00,        36,   2 },
175182833Ssam     [4] = { .phy = TURBO,   48000,   0x00,     B(48),   4 },
176182833Ssam     [5] = { .phy = TURBO,   72000,   0x00,        72,   4 },
177182833Ssam     [6] = { .phy = TURBO,   96000,   0x00,        96,   4 },
178182833Ssam     [7] = { .phy = TURBO,  108000,   0x00,       108,   4 }
179182833Ssam    },
180178354Ssam};
181178354Ssam
182252727Sadrianstatic struct ieee80211_rate_table ieee80211_11ng_table = {
183252727Sadrian    .rateCount = 36,
184252727Sadrian    .info = {
185252727Sadrian/*                                   short            ctrl  */
186252727Sadrian/*                                Preamble  dot11Rate Rate */
187252727Sadrian     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },
188252727Sadrian     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },
189252727Sadrian     [2] = { .phy = CCK,     5500,    0x04,     B(11),   2 },
190252727Sadrian     [3] = { .phy = CCK,    11000,    0x04,     B(22),   3 },
191252727Sadrian     [4] = { .phy = OFDM,    6000,    0x00,        12,   4 },
192252727Sadrian     [5] = { .phy = OFDM,    9000,    0x00,        18,   4 },
193252727Sadrian     [6] = { .phy = OFDM,   12000,    0x00,        24,   6 },
194252727Sadrian     [7] = { .phy = OFDM,   18000,    0x00,        36,   6 },
195252727Sadrian     [8] = { .phy = OFDM,   24000,    0x00,        48,   8 },
196252727Sadrian     [9] = { .phy = OFDM,   36000,    0x00,        72,   8 },
197252727Sadrian    [10] = { .phy = OFDM,   48000,    0x00,        96,   8 },
198252727Sadrian    [11] = { .phy = OFDM,   54000,    0x00,       108,   8 },
199252727Sadrian
200252727Sadrian    [12] = { .phy = HT,      6500,    0x00,      N(0),   4 },
201252727Sadrian    [13] = { .phy = HT,     13000,    0x00,      N(1),   6 },
202252727Sadrian    [14] = { .phy = HT,     19500,    0x00,      N(2),   6 },
203252727Sadrian    [15] = { .phy = HT,     26000,    0x00,      N(3),   8 },
204252727Sadrian    [16] = { .phy = HT,     39000,    0x00,      N(4),   8 },
205252727Sadrian    [17] = { .phy = HT,     52000,    0x00,      N(5),   8 },
206252727Sadrian    [18] = { .phy = HT,     58500,    0x00,      N(6),   8 },
207252727Sadrian    [19] = { .phy = HT,     65000,    0x00,      N(7),   8 },
208252727Sadrian
209252727Sadrian    [20] = { .phy = HT,     13000,    0x00,      N(8),   4 },
210252727Sadrian    [21] = { .phy = HT,     26000,    0x00,      N(9),   6 },
211252727Sadrian    [22] = { .phy = HT,     39000,    0x00,     N(10),   6 },
212252727Sadrian    [23] = { .phy = HT,     52000,    0x00,     N(11),   8 },
213252727Sadrian    [24] = { .phy = HT,     78000,    0x00,     N(12),   8 },
214252727Sadrian    [25] = { .phy = HT,    104000,    0x00,     N(13),   8 },
215252727Sadrian    [26] = { .phy = HT,    117000,    0x00,     N(14),   8 },
216252727Sadrian    [27] = { .phy = HT,    130000,    0x00,     N(15),   8 },
217252727Sadrian
218252727Sadrian    [28] = { .phy = HT,     19500,    0x00,     N(16),   4 },
219252727Sadrian    [29] = { .phy = HT,     39000,    0x00,     N(17),   6 },
220252727Sadrian    [30] = { .phy = HT,     58500,    0x00,     N(18),   6 },
221252727Sadrian    [31] = { .phy = HT,     78000,    0x00,     N(19),   8 },
222252727Sadrian    [32] = { .phy = HT,    117000,    0x00,     N(20),   8 },
223252727Sadrian    [33] = { .phy = HT,    156000,    0x00,     N(21),   8 },
224252727Sadrian    [34] = { .phy = HT,    175500,    0x00,     N(22),   8 },
225252727Sadrian    [35] = { .phy = HT,    195000,    0x00,     N(23),   8 },
226252727Sadrian
227252727Sadrian    },
228252727Sadrian};
229252727Sadrian
230252727Sadrianstatic struct ieee80211_rate_table ieee80211_11na_table = {
231252727Sadrian    .rateCount = 32,
232252727Sadrian    .info = {
233252727Sadrian/*                                   short            ctrl  */
234252727Sadrian/*                                Preamble  dot11Rate Rate */
235252727Sadrian     [0] = { .phy = OFDM,    6000,    0x00,     B(12),   0 },
236252727Sadrian     [1] = { .phy = OFDM,    9000,    0x00,        18,   0 },
237252727Sadrian     [2] = { .phy = OFDM,   12000,    0x00,     B(24),   2 },
238252727Sadrian     [3] = { .phy = OFDM,   18000,    0x00,        36,   2 },
239252727Sadrian     [4] = { .phy = OFDM,   24000,    0x00,     B(48),   4 },
240252727Sadrian     [5] = { .phy = OFDM,   36000,    0x00,        72,   4 },
241252727Sadrian     [6] = { .phy = OFDM,   48000,    0x00,        96,   4 },
242252727Sadrian     [7] = { .phy = OFDM,   54000,    0x00,       108,   4 },
243252727Sadrian
244252727Sadrian     [8] = { .phy = HT,      6500,    0x00,      N(0),   0 },
245252727Sadrian     [9] = { .phy = HT,     13000,    0x00,      N(1),   2 },
246252727Sadrian    [10] = { .phy = HT,     19500,    0x00,      N(2),   2 },
247252727Sadrian    [11] = { .phy = HT,     26000,    0x00,      N(3),   4 },
248252727Sadrian    [12] = { .phy = HT,     39000,    0x00,      N(4),   4 },
249252727Sadrian    [13] = { .phy = HT,     52000,    0x00,      N(5),   4 },
250252727Sadrian    [14] = { .phy = HT,     58500,    0x00,      N(6),   4 },
251252727Sadrian    [15] = { .phy = HT,     65000,    0x00,      N(7),   4 },
252252727Sadrian
253252727Sadrian    [16] = { .phy = HT,     13000,    0x00,      N(8),   0 },
254252727Sadrian    [17] = { .phy = HT,     26000,    0x00,      N(9),   2 },
255252727Sadrian    [18] = { .phy = HT,     39000,    0x00,     N(10),   2 },
256252727Sadrian    [19] = { .phy = HT,     52000,    0x00,     N(11),   4 },
257252727Sadrian    [20] = { .phy = HT,     78000,    0x00,     N(12),   4 },
258252727Sadrian    [21] = { .phy = HT,    104000,    0x00,     N(13),   4 },
259252727Sadrian    [22] = { .phy = HT,    117000,    0x00,     N(14),   4 },
260252727Sadrian    [23] = { .phy = HT,    130000,    0x00,     N(15),   4 },
261252727Sadrian
262252727Sadrian    [24] = { .phy = HT,     19500,    0x00,     N(16),   0 },
263252727Sadrian    [25] = { .phy = HT,     39000,    0x00,     N(17),   2 },
264252727Sadrian    [26] = { .phy = HT,     58500,    0x00,     N(18),   2 },
265252727Sadrian    [27] = { .phy = HT,     78000,    0x00,     N(19),   4 },
266252727Sadrian    [28] = { .phy = HT,    117000,    0x00,     N(20),   4 },
267252727Sadrian    [29] = { .phy = HT,    156000,    0x00,     N(21),   4 },
268252727Sadrian    [30] = { .phy = HT,    175500,    0x00,     N(22),   4 },
269252727Sadrian    [31] = { .phy = HT,    195000,    0x00,     N(23),   4 },
270252727Sadrian
271252727Sadrian    },
272252727Sadrian};
273252727Sadrian
274182833Ssam#undef	Mb
275182833Ssam#undef	B
276178354Ssam#undef	OFDM
277188821Ssam#undef	HALF
278188821Ssam#undef	QUART
279178354Ssam#undef	CCK
280178354Ssam#undef	TURBO
281178354Ssam#undef	XR
282252727Sadrian#undef	HT
283252727Sadrian#undef	N
284178354Ssam
285178354Ssam/*
286178354Ssam * Setup a rate table's reverse lookup table and fill in
287178354Ssam * ack durations.  The reverse lookup tables are assumed
288178354Ssam * to be initialized to zero (or at least the first entry).
289178354Ssam * We use this as a key that indicates whether or not
290178354Ssam * we've previously setup the reverse lookup table.
291178354Ssam *
292178354Ssam * XXX not reentrant, but shouldn't matter
293178354Ssam */
294178354Ssamstatic void
295178354Ssamieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
296178354Ssam{
297178354Ssam#define	WLAN_CTRL_FRAME_SIZE \
298178354Ssam	(sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
299178354Ssam
300178354Ssam	int i;
301178354Ssam
302254315Srpaulo	for (i = 0; i < nitems(rt->rateCodeToIndex); i++)
303178354Ssam		rt->rateCodeToIndex[i] = (uint8_t) -1;
304178354Ssam	for (i = 0; i < rt->rateCount; i++) {
305178354Ssam		uint8_t code = rt->info[i].dot11Rate;
306178354Ssam		uint8_t cix = rt->info[i].ctlRateIndex;
307178354Ssam		uint8_t ctl_rate = rt->info[cix].dot11Rate;
308178354Ssam
309252727Sadrian		/*
310252727Sadrian		 * Map without the basic rate bit.
311252727Sadrian		 *
312252727Sadrian		 * It's up to the caller to ensure that the basic
313252727Sadrian		 * rate bit is stripped here.
314252727Sadrian		 *
315252727Sadrian		 * For HT, use the MCS rate bit.
316252727Sadrian		 */
317252727Sadrian		code &= IEEE80211_RATE_VAL;
318252727Sadrian		if (rt->info[i].phy == IEEE80211_T_HT) {
319252727Sadrian			code |= IEEE80211_RATE_MCS;
320178354Ssam		}
321178354Ssam
322252727Sadrian		/* XXX assume the control rate is non-MCS? */
323252727Sadrian		ctl_rate &= IEEE80211_RATE_VAL;
324252727Sadrian		rt->rateCodeToIndex[code] = i;
325252727Sadrian
326178354Ssam		/*
327178354Ssam		 * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
328178354Ssam		 *     depends on whether they are marked as basic rates;
329178354Ssam		 *     the static tables are setup with an 11b-compatible
330178354Ssam		 *     2Mb/s rate which will work but is suboptimal
331178354Ssam		 *
332178354Ssam		 * NB: Control rate is always less than or equal to the
333178354Ssam		 *     current rate, so control rate's reverse lookup entry
334178354Ssam		 *     has been installed and following call is safe.
335178354Ssam		 */
336178354Ssam		rt->info[i].lpAckDuration = ieee80211_compute_duration(rt,
337178354Ssam			WLAN_CTRL_FRAME_SIZE, ctl_rate, 0);
338178354Ssam		rt->info[i].spAckDuration = ieee80211_compute_duration(rt,
339178354Ssam			WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE);
340178354Ssam	}
341178354Ssam
342178354Ssam#undef WLAN_CTRL_FRAME_SIZE
343178354Ssam}
344178354Ssam
345178354Ssam/* Setup all rate tables */
346178354Ssamstatic void
347178354Ssamieee80211_phy_init(void)
348178354Ssam{
349178354Ssam	static struct ieee80211_rate_table * const ratetables[] = {
350178354Ssam		&ieee80211_half_table,
351178354Ssam		&ieee80211_quarter_table,
352252727Sadrian		&ieee80211_11na_table,
353252727Sadrian		&ieee80211_11ng_table,
354178354Ssam		&ieee80211_turbog_table,
355178354Ssam		&ieee80211_turboa_table,
356178354Ssam		&ieee80211_11a_table,
357178354Ssam		&ieee80211_11g_table,
358178354Ssam		&ieee80211_11b_table
359178354Ssam	};
360178354Ssam	int i;
361178354Ssam
362254315Srpaulo	for (i = 0; i < nitems(ratetables); ++i)
363178354Ssam		ieee80211_setup_ratetable(ratetables[i]);
364178354Ssam
365178354Ssam}
366178354SsamSYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
367178354Ssam
368178354Ssamconst struct ieee80211_rate_table *
369178354Ssamieee80211_get_ratetable(struct ieee80211_channel *c)
370178354Ssam{
371178354Ssam	const struct ieee80211_rate_table *rt;
372178354Ssam
373178354Ssam	/* XXX HT */
374178354Ssam	if (IEEE80211_IS_CHAN_HALF(c))
375178354Ssam		rt = &ieee80211_half_table;
376178354Ssam	else if (IEEE80211_IS_CHAN_QUARTER(c))
377178354Ssam		rt = &ieee80211_quarter_table;
378178354Ssam	else if (IEEE80211_IS_CHAN_HTA(c))
379252727Sadrian		rt = &ieee80211_11na_table;
380178354Ssam	else if (IEEE80211_IS_CHAN_HTG(c))
381252727Sadrian		rt = &ieee80211_11ng_table;
382178354Ssam	else if (IEEE80211_IS_CHAN_108G(c))
383178354Ssam		rt = &ieee80211_turbog_table;
384178354Ssam	else if (IEEE80211_IS_CHAN_ST(c))
385178354Ssam		rt = &ieee80211_turboa_table;
386178354Ssam	else if (IEEE80211_IS_CHAN_TURBO(c))
387178354Ssam		rt = &ieee80211_turboa_table;
388178354Ssam	else if (IEEE80211_IS_CHAN_A(c))
389178354Ssam		rt = &ieee80211_11a_table;
390178354Ssam	else if (IEEE80211_IS_CHAN_ANYG(c))
391178354Ssam		rt = &ieee80211_11g_table;
392178354Ssam	else if (IEEE80211_IS_CHAN_B(c))
393178354Ssam		rt = &ieee80211_11b_table;
394178354Ssam	else {
395178354Ssam		/* NB: should not get here */
396178354Ssam		panic("%s: no rate table for channel; freq %u flags 0x%x\n",
397178354Ssam		      __func__, c->ic_freq, c->ic_flags);
398178354Ssam	}
399178354Ssam	return rt;
400178354Ssam}
401178354Ssam
402178354Ssam/*
403178354Ssam * Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s)
404178354Ssam *
405178354Ssam * Note we do no parameter checking; this routine is mainly
406178354Ssam * used to derive an 802.11 rate for constructing radiotap
407178354Ssam * header data for rx frames.
408178354Ssam *
409178354Ssam * XXX might be a candidate for inline
410178354Ssam */
411178354Ssamuint8_t
412178958Ssamieee80211_plcp2rate(uint8_t plcp, enum ieee80211_phytype type)
413178354Ssam{
414178958Ssam	if (type == IEEE80211_T_OFDM) {
415178354Ssam		static const uint8_t ofdm_plcp2rate[16] = {
416178354Ssam			[0xb]	= 12,
417178354Ssam			[0xf]	= 18,
418178354Ssam			[0xa]	= 24,
419178354Ssam			[0xe]	= 36,
420178354Ssam			[0x9]	= 48,
421178354Ssam			[0xd]	= 72,
422178354Ssam			[0x8]	= 96,
423178354Ssam			[0xc]	= 108
424178354Ssam		};
425178354Ssam		return ofdm_plcp2rate[plcp & 0xf];
426178958Ssam	}
427178958Ssam	if (type == IEEE80211_T_CCK) {
428178354Ssam		static const uint8_t cck_plcp2rate[16] = {
429178354Ssam			[0xa]	= 2,	/* 0x0a */
430178354Ssam			[0x4]	= 4,	/* 0x14 */
431178354Ssam			[0x7]	= 11,	/* 0x37 */
432178354Ssam			[0xe]	= 22,	/* 0x6e */
433178354Ssam			[0xc]	= 44,	/* 0xdc , actually PBCC */
434178354Ssam		};
435178354Ssam		return cck_plcp2rate[plcp & 0xf];
436178354Ssam	}
437178958Ssam	return 0;
438178354Ssam}
439178354Ssam
440178354Ssam/*
441178354Ssam * Covert 802.11 rate to PLCP signal.
442178354Ssam */
443178354Ssamuint8_t
444178958Ssamieee80211_rate2plcp(int rate, enum ieee80211_phytype type)
445178354Ssam{
446178958Ssam	/* XXX ignore type for now since rates are unique */
447178354Ssam	switch (rate) {
448178354Ssam	/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
449178354Ssam	case 12:	return 0xb;
450178354Ssam	case 18:	return 0xf;
451178354Ssam	case 24:	return 0xa;
452178354Ssam	case 36:	return 0xe;
453178354Ssam	case 48:	return 0x9;
454178354Ssam	case 72:	return 0xd;
455178354Ssam	case 96:	return 0x8;
456178354Ssam	case 108:	return 0xc;
457178958Ssam	/* CCK rates (IEEE Std 802.11b-1999 page 15, subclause 18.2.3.3) */
458178958Ssam	case 2:		return 10;
459178958Ssam	case 4:		return 20;
460178958Ssam	case 11:	return 55;
461178958Ssam	case 22:	return 110;
462178958Ssam	/* IEEE Std 802.11g-2003 page 19, subclause 19.3.2.1 */
463178958Ssam	case 44:	return 220;
464178354Ssam	}
465178958Ssam	return 0;		/* XXX unsupported/unknown rate */
466178354Ssam}
467178958Ssam
468188821Ssam#define CCK_SIFS_TIME		10
469188821Ssam#define CCK_PREAMBLE_BITS	144
470188821Ssam#define CCK_PLCP_BITS		48
471188821Ssam
472188821Ssam#define OFDM_SIFS_TIME		16
473188821Ssam#define OFDM_PREAMBLE_TIME	20
474188821Ssam#define OFDM_PLCP_BITS		22
475188821Ssam#define OFDM_SYMBOL_TIME	4
476188821Ssam
477188821Ssam#define OFDM_HALF_SIFS_TIME	32
478188821Ssam#define OFDM_HALF_PREAMBLE_TIME	40
479188821Ssam#define OFDM_HALF_PLCP_BITS	22
480188821Ssam#define OFDM_HALF_SYMBOL_TIME	8
481188821Ssam
482188821Ssam#define OFDM_QUARTER_SIFS_TIME 		64
483188821Ssam#define OFDM_QUARTER_PREAMBLE_TIME	80
484188821Ssam#define OFDM_QUARTER_PLCP_BITS		22
485188821Ssam#define OFDM_QUARTER_SYMBOL_TIME	16
486188821Ssam
487188821Ssam#define TURBO_SIFS_TIME		8
488188821Ssam#define TURBO_PREAMBLE_TIME	14
489188821Ssam#define TURBO_PLCP_BITS		22
490188821Ssam#define TURBO_SYMBOL_TIME	4
491188821Ssam
492178354Ssam/*
493178354Ssam * Compute the time to transmit a frame of length frameLen bytes
494178354Ssam * using the specified rate, phy, and short preamble setting.
495178354Ssam * SIFS is included.
496178354Ssam */
497178354Ssamuint16_t
498178354Ssamieee80211_compute_duration(const struct ieee80211_rate_table *rt,
499178354Ssam	uint32_t frameLen, uint16_t rate, int isShortPreamble)
500178354Ssam{
501178354Ssam	uint8_t rix = rt->rateCodeToIndex[rate];
502178354Ssam	uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
503178354Ssam	uint32_t kbps;
504178354Ssam
505178354Ssam	KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
506178354Ssam	kbps = rt->info[rix].rateKbps;
507178354Ssam	if (kbps == 0)			/* XXX bandaid for channel changes */
508178354Ssam		return 0;
509178354Ssam
510178354Ssam	switch (rt->info[rix].phy) {
511178354Ssam	case IEEE80211_T_CCK:
512178354Ssam		phyTime		= CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
513178354Ssam		if (isShortPreamble && rt->info[rix].shortPreamble)
514178354Ssam			phyTime >>= 1;
515178354Ssam		numBits		= frameLen << 3;
516178354Ssam		txTime		= CCK_SIFS_TIME + phyTime
517178354Ssam				+ ((numBits * 1000)/kbps);
518178354Ssam		break;
519178354Ssam	case IEEE80211_T_OFDM:
520188821Ssam		bitsPerSymbol	= (kbps * OFDM_SYMBOL_TIME) / 1000;
521188821Ssam		KASSERT(bitsPerSymbol != 0, ("full rate bps"));
522178354Ssam
523188821Ssam		numBits		= OFDM_PLCP_BITS + (frameLen << 3);
524188821Ssam		numSymbols	= howmany(numBits, bitsPerSymbol);
525188821Ssam		txTime		= OFDM_SIFS_TIME
526188821Ssam				+ OFDM_PREAMBLE_TIME
527188821Ssam				+ (numSymbols * OFDM_SYMBOL_TIME);
528188821Ssam		break;
529188821Ssam	case IEEE80211_T_OFDM_HALF:
530188821Ssam		bitsPerSymbol	= (kbps * OFDM_HALF_SYMBOL_TIME) / 1000;
531188821Ssam		KASSERT(bitsPerSymbol != 0, ("1/4 rate bps"));
532178354Ssam
533188821Ssam		numBits		= OFDM_PLCP_BITS + (frameLen << 3);
534188821Ssam		numSymbols	= howmany(numBits, bitsPerSymbol);
535188821Ssam		txTime		= OFDM_HALF_SIFS_TIME
536188821Ssam				+ OFDM_HALF_PREAMBLE_TIME
537188821Ssam				+ (numSymbols * OFDM_HALF_SYMBOL_TIME);
538188821Ssam		break;
539188821Ssam	case IEEE80211_T_OFDM_QUARTER:
540188821Ssam		bitsPerSymbol	= (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000;
541188821Ssam		KASSERT(bitsPerSymbol != 0, ("1/2 rate bps"));
542178354Ssam
543188821Ssam		numBits		= OFDM_PLCP_BITS + (frameLen << 3);
544188821Ssam		numSymbols	= howmany(numBits, bitsPerSymbol);
545188821Ssam		txTime		= OFDM_QUARTER_SIFS_TIME
546188821Ssam				+ OFDM_QUARTER_PREAMBLE_TIME
547188821Ssam				+ (numSymbols * OFDM_QUARTER_SYMBOL_TIME);
548178354Ssam		break;
549178354Ssam	case IEEE80211_T_TURBO:
550178354Ssam		/* we still save OFDM rates in kbps - so double them */
551178354Ssam		bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
552178354Ssam		KASSERT(bitsPerSymbol != 0, ("turbo bps"));
553178354Ssam
554178354Ssam		numBits       = TURBO_PLCP_BITS + (frameLen << 3);
555178354Ssam		numSymbols    = howmany(numBits, bitsPerSymbol);
556178354Ssam		txTime        = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
557178354Ssam			      + (numSymbols * TURBO_SYMBOL_TIME);
558178354Ssam		break;
559178354Ssam	default:
560178354Ssam		panic("%s: unknown phy %u (rate %u)\n", __func__,
561178354Ssam		      rt->info[rix].phy, rate);
562178354Ssam		break;
563178354Ssam	}
564178354Ssam	return txTime;
565178354Ssam}
566252727Sadrian
567252727Sadrianstatic const uint16_t ht20_bps[32] = {
568252727Sadrian	26, 52, 78, 104, 156, 208, 234, 260,
569252727Sadrian	52, 104, 156, 208, 312, 416, 468, 520,
570252727Sadrian	78, 156, 234, 312, 468, 624, 702, 780,
571252727Sadrian	104, 208, 312, 416, 624, 832, 936, 1040
572252727Sadrian};
573252727Sadrianstatic const uint16_t ht40_bps[32] = {
574252727Sadrian	54, 108, 162, 216, 324, 432, 486, 540,
575252727Sadrian	108, 216, 324, 432, 648, 864, 972, 1080,
576252727Sadrian	162, 324, 486, 648, 972, 1296, 1458, 1620,
577252727Sadrian	216, 432, 648, 864, 1296, 1728, 1944, 2160
578252727Sadrian};
579252727Sadrian
580252727Sadrian
581252727Sadrian#define	OFDM_PLCP_BITS	22
582252727Sadrian#define	HT_L_STF	8
583252727Sadrian#define	HT_L_LTF	8
584252727Sadrian#define	HT_L_SIG	4
585252727Sadrian#define	HT_SIG		8
586252727Sadrian#define	HT_STF		4
587252727Sadrian#define	HT_LTF(n)	((n) * 4)
588252727Sadrian
589252727Sadrian#define	HT_RC_2_MCS(_rc)	((_rc) & 0xf)
590252727Sadrian#define	HT_RC_2_STREAMS(_rc)	((((_rc) & 0x78) >> 3) + 1)
591252727Sadrian#define	IS_HT_RATE(_rc)		( (_rc) & IEEE80211_RATE_MCS)
592252727Sadrian
593252727Sadrian/*
594252727Sadrian * Calculate the transmit duration of an 11n frame.
595252727Sadrian */
596252727Sadrianuint32_t
597252727Sadrianieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate,
598252727Sadrian    int streams, int isht40, int isShortGI)
599252727Sadrian{
600252727Sadrian	uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
601252727Sadrian
602252727Sadrian	KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
603252727Sadrian	KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
604252727Sadrian
605252727Sadrian	if (isht40)
606252727Sadrian		bitsPerSymbol = ht40_bps[rate & 0x1f];
607252727Sadrian	else
608252727Sadrian		bitsPerSymbol = ht20_bps[rate & 0x1f];
609252727Sadrian	numBits = OFDM_PLCP_BITS + (frameLen << 3);
610252727Sadrian	numSymbols = howmany(numBits, bitsPerSymbol);
611252727Sadrian	if (isShortGI)
612252727Sadrian		txTime = ((numSymbols * 18) + 4) / 5;   /* 3.6us */
613252727Sadrian	else
614252727Sadrian		txTime = numSymbols * 4;                /* 4us */
615252727Sadrian	return txTime + HT_L_STF + HT_L_LTF +
616252727Sadrian	    HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
617252727Sadrian}
618252727Sadrian
619252727Sadrian#undef	IS_HT_RATE
620252727Sadrian#undef	HT_RC_2_STREAMS
621252727Sadrian#undef	HT_RC_2_MCS
622252727Sadrian#undef	HT_LTF
623252727Sadrian#undef	HT_STF
624252727Sadrian#undef	HT_SIG
625252727Sadrian#undef	HT_L_SIG
626252727Sadrian#undef	HT_L_LTF
627252727Sadrian#undef	HT_L_STF
628252727Sadrian#undef	OFDM_PLCP_BITS
629