ieee80211_phy.c revision 182833
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: head/sys/net80211/ieee80211_phy.c 182833 2008-09-06 17:48:25Z sam $");
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
61178354Ssam#define	PBCC	(IEEE80211_T_HT+1)		/* XXX */
62182833Ssam#define	B(r)	(0x80 | r)
63182833Ssam#define	Mb(x)	(x*1000)
64178354Ssam
65178354Ssamstatic struct ieee80211_rate_table ieee80211_11b_table = {
66182833Ssam    .rateCount = 4,		/* XXX no PBCC */
67182833Ssam    .info = {
68182833Ssam/*                                   short            ctrl  */
69182833Ssam/*                                Preamble  dot11Rate Rate */
70182833Ssam     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },/*   1 Mb */
71182833Ssam     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },/*   2 Mb */
72182833Ssam     [2] = { .phy = CCK,     5500,    0x04,     B(11),   1 },/* 5.5 Mb */
73182833Ssam     [3] = { .phy = CCK,    11000,    0x04,     B(22),   1 },/*  11 Mb */
74182833Ssam     [4] = { .phy = PBCC,   22000,    0x04,        44,   3 } /*  22 Mb */
75182833Ssam    },
76178354Ssam};
77178354Ssam
78178354Ssamstatic struct ieee80211_rate_table ieee80211_11g_table = {
79182833Ssam    .rateCount = 12,
80182833Ssam    .info = {
81182833Ssam/*                                   short            ctrl  */
82182833Ssam/*                                Preamble  dot11Rate Rate */
83182833Ssam     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },
84182833Ssam     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },
85182833Ssam     [2] = { .phy = CCK,     5500,    0x04,     B(11),   2 },
86182833Ssam     [3] = { .phy = CCK,    11000,    0x04,     B(22),   3 },
87182833Ssam     [4] = { .phy = OFDM,    6000,    0x00,        12,   4 },
88182833Ssam     [5] = { .phy = OFDM,    9000,    0x00,        18,   4 },
89182833Ssam     [6] = { .phy = OFDM,   12000,    0x00,        24,   6 },
90182833Ssam     [7] = { .phy = OFDM,   18000,    0x00,        36,   6 },
91182833Ssam     [8] = { .phy = OFDM,   24000,    0x00,        48,   8 },
92182833Ssam     [9] = { .phy = OFDM,   36000,    0x00,        72,   8 },
93182833Ssam    [10] = { .phy = OFDM,   48000,    0x00,        96,   8 },
94182833Ssam    [11] = { .phy = OFDM,   54000,    0x00,       108,   8 }
95182833Ssam    },
96178354Ssam};
97178354Ssam
98178354Ssamstatic struct ieee80211_rate_table ieee80211_11a_table = {
99182833Ssam    .rateCount = 8,
100182833Ssam    .info = {
101182833Ssam/*                                   short            ctrl  */
102182833Ssam/*                                Preamble  dot11Rate Rate */
103182833Ssam     [0] = { .phy = OFDM,    6000,    0x00,     B(12),   0 },
104182833Ssam     [1] = { .phy = OFDM,    9000,    0x00,        18,   0 },
105182833Ssam     [2] = { .phy = OFDM,   12000,    0x00,     B(24),   2 },
106182833Ssam     [3] = { .phy = OFDM,   18000,    0x00,        36,   2 },
107182833Ssam     [4] = { .phy = OFDM,   24000,    0x00,     B(48),   4 },
108182833Ssam     [5] = { .phy = OFDM,   36000,    0x00,        72,   4 },
109182833Ssam     [6] = { .phy = OFDM,   48000,    0x00,        96,   4 },
110182833Ssam     [7] = { .phy = OFDM,   54000,    0x00,       108,   4 }
111182833Ssam    },
112178354Ssam};
113178354Ssam
114178354Ssamstatic struct ieee80211_rate_table ieee80211_half_table = {
115182833Ssam    .rateCount = 8,
116182833Ssam    .info = {
117182833Ssam/*                                   short            ctrl  */
118182833Ssam/*                                Preamble  dot11Rate Rate */
119182833Ssam     [0] = { .phy = OFDM,    3000,    0x00,      B(6),   0 },
120182833Ssam     [1] = { .phy = OFDM,    4500,    0x00,         9,   0 },
121182833Ssam     [2] = { .phy = OFDM,    6000,    0x00,     B(12),   2 },
122182833Ssam     [3] = { .phy = OFDM,    9000,    0x00,        18,   2 },
123182833Ssam     [4] = { .phy = OFDM,   12000,    0x00,     B(24),   4 },
124182833Ssam     [5] = { .phy = OFDM,   18000,    0x00,        36,   4 },
125182833Ssam     [6] = { .phy = OFDM,   24000,    0x00,        48,   4 },
126182833Ssam     [7] = { .phy = OFDM,   27000,    0x00,        54,   4 }
127182833Ssam    },
128178354Ssam};
129178354Ssam
130178354Ssamstatic struct ieee80211_rate_table ieee80211_quarter_table = {
131182833Ssam    .rateCount = 8,
132182833Ssam    .info = {
133182833Ssam/*                                   short            ctrl  */
134182833Ssam/*                                Preamble  dot11Rate Rate */
135182833Ssam     [0] = { .phy = OFDM,    1500,    0x00,      B(3),   0 },
136182833Ssam     [1] = { .phy = OFDM,    2250,    0x00,         4,   0 },
137182833Ssam     [2] = { .phy = OFDM,    3000,    0x00,      B(9),   2 },
138182833Ssam     [3] = { .phy = OFDM,    4500,    0x00,         9,   2 },
139182833Ssam     [4] = { .phy = OFDM,    6000,    0x00,     B(12),   4 },
140182833Ssam     [5] = { .phy = OFDM,    9000,    0x00,        18,   4 },
141182833Ssam     [6] = { .phy = OFDM,   12000,    0x00,        24,   4 },
142182833Ssam     [7] = { .phy = OFDM,   13500,    0x00,        27,   4 }
143182833Ssam    },
144178354Ssam};
145178354Ssam
146178354Ssamstatic struct ieee80211_rate_table ieee80211_turbog_table = {
147182833Ssam    .rateCount = 7,
148182833Ssam    .info = {
149182833Ssam/*                                   short            ctrl  */
150182833Ssam/*                                Preamble  dot11Rate Rate */
151182833Ssam     [0] = { .phy = TURBO,   12000,   0x00,     B(12),   0 },
152182833Ssam     [1] = { .phy = TURBO,   24000,   0x00,     B(24),   1 },
153182833Ssam     [2] = { .phy = TURBO,   36000,   0x00,        36,   1 },
154182833Ssam     [3] = { .phy = TURBO,   48000,   0x00,     B(48),   3 },
155182833Ssam     [4] = { .phy = TURBO,   72000,   0x00,        72,   3 },
156182833Ssam     [5] = { .phy = TURBO,   96000,   0x00,        96,   3 },
157182833Ssam     [6] = { .phy = TURBO,  108000,   0x00,       108,   3 }
158182833Ssam    },
159178354Ssam};
160178354Ssam
161178354Ssamstatic struct ieee80211_rate_table ieee80211_turboa_table = {
162182833Ssam    .rateCount = 8,
163182833Ssam    .info = {
164182833Ssam/*                                   short            ctrl  */
165182833Ssam/*                                Preamble  dot11Rate Rate */
166182833Ssam     [0] = { .phy = TURBO,   12000,   0x00,     B(12),   0 },
167182833Ssam     [1] = { .phy = TURBO,   18000,   0x00,        18,   0 },
168182833Ssam     [2] = { .phy = TURBO,   24000,   0x00,     B(24),   2 },
169182833Ssam     [3] = { .phy = TURBO,   36000,   0x00,        36,   2 },
170182833Ssam     [4] = { .phy = TURBO,   48000,   0x00,     B(48),   4 },
171182833Ssam     [5] = { .phy = TURBO,   72000,   0x00,        72,   4 },
172182833Ssam     [6] = { .phy = TURBO,   96000,   0x00,        96,   4 },
173182833Ssam     [7] = { .phy = TURBO,  108000,   0x00,       108,   4 }
174182833Ssam    },
175178354Ssam};
176178354Ssam
177182833Ssam#undef	Mb
178182833Ssam#undef	B
179178354Ssam#undef	OFDM
180178354Ssam#undef	CCK
181178354Ssam#undef	TURBO
182178354Ssam#undef	XR
183178354Ssam
184178354Ssam/*
185178354Ssam * Setup a rate table's reverse lookup table and fill in
186178354Ssam * ack durations.  The reverse lookup tables are assumed
187178354Ssam * to be initialized to zero (or at least the first entry).
188178354Ssam * We use this as a key that indicates whether or not
189178354Ssam * we've previously setup the reverse lookup table.
190178354Ssam *
191178354Ssam * XXX not reentrant, but shouldn't matter
192178354Ssam */
193178354Ssamstatic void
194178354Ssamieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
195178354Ssam{
196178354Ssam#define	N(a)	(sizeof(a)/sizeof(a[0]))
197178354Ssam#define	WLAN_CTRL_FRAME_SIZE \
198178354Ssam	(sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
199178354Ssam
200178354Ssam	int i;
201178354Ssam
202178354Ssam	for (i = 0; i < N(rt->rateCodeToIndex); i++)
203178354Ssam		rt->rateCodeToIndex[i] = (uint8_t) -1;
204178354Ssam	for (i = 0; i < rt->rateCount; i++) {
205178354Ssam		uint8_t code = rt->info[i].dot11Rate;
206178354Ssam		uint8_t cix = rt->info[i].ctlRateIndex;
207178354Ssam		uint8_t ctl_rate = rt->info[cix].dot11Rate;
208178354Ssam
209178354Ssam		rt->rateCodeToIndex[code] = i;
210178354Ssam		if (code & IEEE80211_RATE_BASIC) {
211178354Ssam			/*
212178354Ssam			 * Map w/o basic rate bit too.
213178354Ssam			 */
214178354Ssam			code &= IEEE80211_RATE_VAL;
215178354Ssam			rt->rateCodeToIndex[code] = i;
216178354Ssam		}
217178354Ssam
218178354Ssam		/*
219178354Ssam		 * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
220178354Ssam		 *     depends on whether they are marked as basic rates;
221178354Ssam		 *     the static tables are setup with an 11b-compatible
222178354Ssam		 *     2Mb/s rate which will work but is suboptimal
223178354Ssam		 *
224178354Ssam		 * NB: Control rate is always less than or equal to the
225178354Ssam		 *     current rate, so control rate's reverse lookup entry
226178354Ssam		 *     has been installed and following call is safe.
227178354Ssam		 */
228178354Ssam		rt->info[i].lpAckDuration = ieee80211_compute_duration(rt,
229178354Ssam			WLAN_CTRL_FRAME_SIZE, ctl_rate, 0);
230178354Ssam		rt->info[i].spAckDuration = ieee80211_compute_duration(rt,
231178354Ssam			WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE);
232178354Ssam	}
233178354Ssam
234178354Ssam#undef WLAN_CTRL_FRAME_SIZE
235178354Ssam#undef N
236178354Ssam}
237178354Ssam
238178354Ssam/* Setup all rate tables */
239178354Ssamstatic void
240178354Ssamieee80211_phy_init(void)
241178354Ssam{
242178354Ssam#define N(arr)	(int)(sizeof(arr) / sizeof(arr[0]))
243178354Ssam	static struct ieee80211_rate_table * const ratetables[] = {
244178354Ssam		&ieee80211_half_table,
245178354Ssam		&ieee80211_quarter_table,
246178354Ssam		&ieee80211_11a_table,
247178354Ssam		&ieee80211_11g_table,
248178354Ssam		&ieee80211_turbog_table,
249178354Ssam		&ieee80211_turboa_table,
250178354Ssam		&ieee80211_turboa_table,
251178354Ssam		&ieee80211_11a_table,
252178354Ssam		&ieee80211_11g_table,
253178354Ssam		&ieee80211_11b_table
254178354Ssam	};
255178354Ssam	int i;
256178354Ssam
257178354Ssam	for (i = 0; i < N(ratetables); ++i)
258178354Ssam		ieee80211_setup_ratetable(ratetables[i]);
259178354Ssam
260178354Ssam#undef N
261178354Ssam}
262178354SsamSYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
263178354Ssam
264178354Ssamconst struct ieee80211_rate_table *
265178354Ssamieee80211_get_ratetable(struct ieee80211_channel *c)
266178354Ssam{
267178354Ssam	const struct ieee80211_rate_table *rt;
268178354Ssam
269178354Ssam	/* XXX HT */
270178354Ssam	if (IEEE80211_IS_CHAN_HALF(c))
271178354Ssam		rt = &ieee80211_half_table;
272178354Ssam	else if (IEEE80211_IS_CHAN_QUARTER(c))
273178354Ssam		rt = &ieee80211_quarter_table;
274178354Ssam	else if (IEEE80211_IS_CHAN_HTA(c))
275178354Ssam		rt = &ieee80211_11a_table;	/* XXX */
276178354Ssam	else if (IEEE80211_IS_CHAN_HTG(c))
277178354Ssam		rt = &ieee80211_11g_table;	/* XXX */
278178354Ssam	else if (IEEE80211_IS_CHAN_108G(c))
279178354Ssam		rt = &ieee80211_turbog_table;
280178354Ssam	else if (IEEE80211_IS_CHAN_ST(c))
281178354Ssam		rt = &ieee80211_turboa_table;
282178354Ssam	else if (IEEE80211_IS_CHAN_TURBO(c))
283178354Ssam		rt = &ieee80211_turboa_table;
284178354Ssam	else if (IEEE80211_IS_CHAN_A(c))
285178354Ssam		rt = &ieee80211_11a_table;
286178354Ssam	else if (IEEE80211_IS_CHAN_ANYG(c))
287178354Ssam		rt = &ieee80211_11g_table;
288178354Ssam	else if (IEEE80211_IS_CHAN_B(c))
289178354Ssam		rt = &ieee80211_11b_table;
290178354Ssam	else {
291178354Ssam		/* NB: should not get here */
292178354Ssam		panic("%s: no rate table for channel; freq %u flags 0x%x\n",
293178354Ssam		      __func__, c->ic_freq, c->ic_flags);
294178354Ssam	}
295178354Ssam	return rt;
296178354Ssam}
297178354Ssam
298178354Ssam/*
299178354Ssam * Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s)
300178354Ssam *
301178354Ssam * Note we do no parameter checking; this routine is mainly
302178354Ssam * used to derive an 802.11 rate for constructing radiotap
303178354Ssam * header data for rx frames.
304178354Ssam *
305178354Ssam * XXX might be a candidate for inline
306178354Ssam */
307178354Ssamuint8_t
308178958Ssamieee80211_plcp2rate(uint8_t plcp, enum ieee80211_phytype type)
309178354Ssam{
310178958Ssam	if (type == IEEE80211_T_OFDM) {
311178354Ssam		static const uint8_t ofdm_plcp2rate[16] = {
312178354Ssam			[0xb]	= 12,
313178354Ssam			[0xf]	= 18,
314178354Ssam			[0xa]	= 24,
315178354Ssam			[0xe]	= 36,
316178354Ssam			[0x9]	= 48,
317178354Ssam			[0xd]	= 72,
318178354Ssam			[0x8]	= 96,
319178354Ssam			[0xc]	= 108
320178354Ssam		};
321178354Ssam		return ofdm_plcp2rate[plcp & 0xf];
322178958Ssam	}
323178958Ssam	if (type == IEEE80211_T_CCK) {
324178354Ssam		static const uint8_t cck_plcp2rate[16] = {
325178354Ssam			[0xa]	= 2,	/* 0x0a */
326178354Ssam			[0x4]	= 4,	/* 0x14 */
327178354Ssam			[0x7]	= 11,	/* 0x37 */
328178354Ssam			[0xe]	= 22,	/* 0x6e */
329178354Ssam			[0xc]	= 44,	/* 0xdc , actually PBCC */
330178354Ssam		};
331178354Ssam		return cck_plcp2rate[plcp & 0xf];
332178354Ssam	}
333178958Ssam	return 0;
334178354Ssam}
335178354Ssam
336178354Ssam/*
337178354Ssam * Covert 802.11 rate to PLCP signal.
338178354Ssam */
339178354Ssamuint8_t
340178958Ssamieee80211_rate2plcp(int rate, enum ieee80211_phytype type)
341178354Ssam{
342178958Ssam	/* XXX ignore type for now since rates are unique */
343178354Ssam	switch (rate) {
344178354Ssam	/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
345178354Ssam	case 12:	return 0xb;
346178354Ssam	case 18:	return 0xf;
347178354Ssam	case 24:	return 0xa;
348178354Ssam	case 36:	return 0xe;
349178354Ssam	case 48:	return 0x9;
350178354Ssam	case 72:	return 0xd;
351178354Ssam	case 96:	return 0x8;
352178354Ssam	case 108:	return 0xc;
353178958Ssam	/* CCK rates (IEEE Std 802.11b-1999 page 15, subclause 18.2.3.3) */
354178958Ssam	case 2:		return 10;
355178958Ssam	case 4:		return 20;
356178958Ssam	case 11:	return 55;
357178958Ssam	case 22:	return 110;
358178958Ssam	/* IEEE Std 802.11g-2003 page 19, subclause 19.3.2.1 */
359178958Ssam	case 44:	return 220;
360178354Ssam	}
361178958Ssam	return 0;		/* XXX unsupported/unknown rate */
362178354Ssam}
363178958Ssam
364178354Ssam/*
365178354Ssam * Compute the time to transmit a frame of length frameLen bytes
366178354Ssam * using the specified rate, phy, and short preamble setting.
367178354Ssam * SIFS is included.
368178354Ssam */
369178354Ssamuint16_t
370178354Ssamieee80211_compute_duration(const struct ieee80211_rate_table *rt,
371178354Ssam	uint32_t frameLen, uint16_t rate, int isShortPreamble)
372178354Ssam{
373178354Ssam	uint8_t rix = rt->rateCodeToIndex[rate];
374178354Ssam	uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
375178354Ssam	uint32_t kbps;
376178354Ssam
377178354Ssam	KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
378178354Ssam	kbps = rt->info[rix].rateKbps;
379178354Ssam	if (kbps == 0)			/* XXX bandaid for channel changes */
380178354Ssam		return 0;
381178354Ssam
382178354Ssam	switch (rt->info[rix].phy) {
383178354Ssam	case IEEE80211_T_CCK:
384178354Ssam#define CCK_SIFS_TIME		10
385178354Ssam#define CCK_PREAMBLE_BITS	144
386178354Ssam#define CCK_PLCP_BITS		48
387178354Ssam		phyTime		= CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
388178354Ssam		if (isShortPreamble && rt->info[rix].shortPreamble)
389178354Ssam			phyTime >>= 1;
390178354Ssam		numBits		= frameLen << 3;
391178354Ssam		txTime		= CCK_SIFS_TIME + phyTime
392178354Ssam				+ ((numBits * 1000)/kbps);
393178354Ssam		break;
394178354Ssam#undef CCK_SIFS_TIME
395178354Ssam#undef CCK_PREAMBLE_BITS
396178354Ssam#undef CCK_PLCP_BITS
397178354Ssam
398178354Ssam	case IEEE80211_T_OFDM:
399178354Ssam#define OFDM_SIFS_TIME		16
400178354Ssam#define OFDM_PREAMBLE_TIME	20
401178354Ssam#define OFDM_PLCP_BITS		22
402178354Ssam#define OFDM_SYMBOL_TIME	4
403178354Ssam
404178354Ssam#define OFDM_SIFS_TIME_HALF	32
405178354Ssam#define OFDM_PREAMBLE_TIME_HALF	40
406178354Ssam#define OFDM_PLCP_BITS_HALF	22
407178354Ssam#define OFDM_SYMBOL_TIME_HALF	8
408178354Ssam
409178354Ssam#define OFDM_SIFS_TIME_QUARTER 		64
410178354Ssam#define OFDM_PREAMBLE_TIME_QUARTER	80
411178354Ssam#define OFDM_PLCP_BITS_QUARTER		22
412178354Ssam#define OFDM_SYMBOL_TIME_QUARTER	16
413178354Ssam		if (rt == &ieee80211_half_table) {
414178354Ssam			bitsPerSymbol	= (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
415178354Ssam			KASSERT(bitsPerSymbol != 0, ("1/2 rate bps"));
416178354Ssam
417178354Ssam			numBits		= OFDM_PLCP_BITS + (frameLen << 3);
418178354Ssam			numSymbols	= howmany(numBits, bitsPerSymbol);
419178354Ssam			txTime		= OFDM_SIFS_TIME_QUARTER
420178354Ssam					+ OFDM_PREAMBLE_TIME_QUARTER
421178354Ssam					+ (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
422178354Ssam		} else if (rt == &ieee80211_quarter_table) {
423178354Ssam			bitsPerSymbol	= (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
424178354Ssam			KASSERT(bitsPerSymbol != 0, ("1/4 rate bps"));
425178354Ssam
426178354Ssam			numBits		= OFDM_PLCP_BITS + (frameLen << 3);
427178354Ssam			numSymbols	= howmany(numBits, bitsPerSymbol);
428178354Ssam			txTime		= OFDM_SIFS_TIME_HALF
429178354Ssam					+ OFDM_PREAMBLE_TIME_HALF
430178354Ssam					+ (numSymbols * OFDM_SYMBOL_TIME_HALF);
431178354Ssam		} else { /* full rate channel */
432178354Ssam			bitsPerSymbol	= (kbps * OFDM_SYMBOL_TIME) / 1000;
433178354Ssam			KASSERT(bitsPerSymbol != 0, ("full rate bps"));
434178354Ssam
435178354Ssam			numBits		= OFDM_PLCP_BITS + (frameLen << 3);
436178354Ssam			numSymbols	= howmany(numBits, bitsPerSymbol);
437178354Ssam			txTime		= OFDM_SIFS_TIME
438178354Ssam					+ OFDM_PREAMBLE_TIME
439178354Ssam					+ (numSymbols * OFDM_SYMBOL_TIME);
440178354Ssam		}
441178354Ssam		break;
442178354Ssam
443178354Ssam#undef OFDM_SIFS_TIME
444178354Ssam#undef OFDM_PREAMBLE_TIME
445178354Ssam#undef OFDM_PLCP_BITS
446178354Ssam#undef OFDM_SYMBOL_TIME
447178354Ssam
448178354Ssam	case IEEE80211_T_TURBO:
449178354Ssam#define TURBO_SIFS_TIME		8
450178354Ssam#define TURBO_PREAMBLE_TIME	14
451178354Ssam#define TURBO_PLCP_BITS		22
452178354Ssam#define TURBO_SYMBOL_TIME	4
453178354Ssam		/* we still save OFDM rates in kbps - so double them */
454178354Ssam		bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
455178354Ssam		KASSERT(bitsPerSymbol != 0, ("turbo bps"));
456178354Ssam
457178354Ssam		numBits       = TURBO_PLCP_BITS + (frameLen << 3);
458178354Ssam		numSymbols    = howmany(numBits, bitsPerSymbol);
459178354Ssam		txTime        = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
460178354Ssam			      + (numSymbols * TURBO_SYMBOL_TIME);
461178354Ssam		break;
462178354Ssam#undef TURBO_SIFS_TIME
463178354Ssam#undef TURBO_PREAMBLE_TIME
464178354Ssam#undef TURBO_PLCP_BITS
465178354Ssam#undef TURBO_SYMBOL_TIME
466178354Ssam
467178354Ssam	default:
468178354Ssam		panic("%s: unknown phy %u (rate %u)\n", __func__,
469178354Ssam		      rt->info[rix].phy, rate);
470178354Ssam		break;
471178354Ssam	}
472178354Ssam	return txTime;
473178354Ssam}
474