1187120Ssam/*- 2187120Ssam * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting 3187120Ssam * All rights reserved. 4187120Ssam * 5187120Ssam * Redistribution and use in source and binary forms, with or without 6187120Ssam * modification, are permitted provided that the following conditions 7187120Ssam * are met: 8187120Ssam * 1. Redistributions of source code must retain the above copyright 9187120Ssam * notice, this list of conditions and the following disclaimer. 10187120Ssam * 2. Redistributions in binary form must reproduce the above copyright 11187120Ssam * notice, this list of conditions and the following disclaimer in the 12187120Ssam * documentation and/or other materials provided with the distribution. 13187120Ssam * 14187120Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15187120Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16187120Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17187120Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18187120Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19187120Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20187120Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21187120Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22187120Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23187120Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24187120Ssam */ 25187120Ssam 26187120Ssam#include <sys/cdefs.h> 27187120Ssam__FBSDID("$FreeBSD$"); 28187120Ssam 29187120Ssam/* 30187120Ssam * IEEE 802.11 PHY-related support. 31187120Ssam */ 32187120Ssam 33187120Ssam#include <sys/param.h> 34187120Ssam#include <sys/types.h> 35187120Ssam 36187120Ssam#include <net/if_llc.h> 37187120Ssam 38187120Ssam#include <net80211/_ieee80211.h> 39187120Ssam#include <net80211/ieee80211.h> 40187120Ssam 41187120Ssam#define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */ 42187120Ssam 43187120Ssam#include <err.h> 44187120Ssam#include <stdio.h> 45187120Ssam#include <stdarg.h> 46187120Ssam#include <stdlib.h> 47187120Ssam#include <strings.h> 48187120Ssam#include <unistd.h> 49187120Ssam 50187120Ssamstruct ieee80211_rate_table { 51187120Ssam int rateCount; /* NB: for proper padding */ 52187120Ssam uint8_t rateCodeToIndex[256]; /* back mapping */ 53187120Ssam struct { 54187120Ssam uint8_t phy; /* CCK/OFDM/TURBO */ 55187120Ssam uint32_t rateKbps; /* transfer rate in kbs */ 56187120Ssam uint8_t shortPreamble; /* mask for enabling short 57187120Ssam * preamble in CCK rate code */ 58187120Ssam uint8_t dot11Rate; /* value for supported rates 59187120Ssam * info element of MLME */ 60187120Ssam uint8_t ctlRateIndex; /* index of next lower basic 61187120Ssam * rate; used for dur. calcs */ 62187120Ssam uint16_t lpAckDuration; /* long preamble ACK dur. */ 63187120Ssam uint16_t spAckDuration; /* short preamble ACK dur. */ 64187120Ssam } info[32]; 65187120Ssam}; 66187120Ssam 67187120Ssamuint16_t 68187120Ssamieee80211_compute_duration(const struct ieee80211_rate_table *rt, 69187120Ssam uint32_t frameLen, uint16_t rate, int isShortPreamble); 70187120Ssam 71187120Ssam#define KASSERT(c, msg) do { \ 72187120Ssam if (!(c)) { \ 73187120Ssam printf msg; \ 74187120Ssam putchar('\n'); \ 75187120Ssam exit(-1); \ 76187120Ssam } \ 77187120Ssam} while (0) 78187120Ssam 79187120Ssamstatic void 80187120Ssampanic(const char *fmt, ...) 81187120Ssam{ 82187120Ssam va_list ap; 83187120Ssam 84187120Ssam va_start(ap, fmt); 85187120Ssam vprintf(fmt, ap); 86187120Ssam va_end(ap); 87187120Ssam exit(-1); 88187120Ssam} 89187120Ssam 90187120Ssam/* shorthands to compact tables for readability */ 91187120Ssam#define OFDM IEEE80211_T_OFDM 92187120Ssam#define CCK IEEE80211_T_CCK 93187120Ssam#define TURBO IEEE80211_T_TURBO 94188785Ssam#define HALF IEEE80211_T_OFDM_HALF 95188785Ssam#define QUART IEEE80211_T_OFDM_QUARTER 96188785Ssam#define PBCC (IEEE80211_T_OFDM_QUARTER+1) /* XXX */ 97187120Ssam#define B(r) (0x80 | r) 98187120Ssam#define Mb(x) (x*1000) 99187120Ssam 100187120Ssamstatic struct ieee80211_rate_table ieee80211_11b_table = { 101187120Ssam .rateCount = 4, /* XXX no PBCC */ 102187120Ssam .info = { 103187120Ssam/* short ctrl */ 104187120Ssam/* Preamble dot11Rate Rate */ 105187120Ssam [0] = { .phy = CCK, 1000, 0x00, B(2), 0 },/* 1 Mb */ 106187120Ssam [1] = { .phy = CCK, 2000, 0x04, B(4), 1 },/* 2 Mb */ 107187120Ssam [2] = { .phy = CCK, 5500, 0x04, B(11), 1 },/* 5.5 Mb */ 108187120Ssam [3] = { .phy = CCK, 11000, 0x04, B(22), 1 },/* 11 Mb */ 109187120Ssam [4] = { .phy = PBCC, 22000, 0x04, 44, 3 } /* 22 Mb */ 110187120Ssam }, 111187120Ssam}; 112187120Ssam 113187120Ssamstatic struct ieee80211_rate_table ieee80211_11g_table = { 114187120Ssam .rateCount = 12, 115187120Ssam .info = { 116187120Ssam/* short ctrl */ 117187120Ssam/* Preamble dot11Rate Rate */ 118187120Ssam [0] = { .phy = CCK, 1000, 0x00, B(2), 0 }, 119187120Ssam [1] = { .phy = CCK, 2000, 0x04, B(4), 1 }, 120187120Ssam [2] = { .phy = CCK, 5500, 0x04, B(11), 2 }, 121187120Ssam [3] = { .phy = CCK, 11000, 0x04, B(22), 3 }, 122187120Ssam [4] = { .phy = OFDM, 6000, 0x00, 12, 4 }, 123187120Ssam [5] = { .phy = OFDM, 9000, 0x00, 18, 4 }, 124187120Ssam [6] = { .phy = OFDM, 12000, 0x00, 24, 6 }, 125187120Ssam [7] = { .phy = OFDM, 18000, 0x00, 36, 6 }, 126187120Ssam [8] = { .phy = OFDM, 24000, 0x00, 48, 8 }, 127187120Ssam [9] = { .phy = OFDM, 36000, 0x00, 72, 8 }, 128187120Ssam [10] = { .phy = OFDM, 48000, 0x00, 96, 8 }, 129187120Ssam [11] = { .phy = OFDM, 54000, 0x00, 108, 8 } 130187120Ssam }, 131187120Ssam}; 132187120Ssam 133187120Ssamstatic struct ieee80211_rate_table ieee80211_11a_table = { 134187120Ssam .rateCount = 8, 135187120Ssam .info = { 136187120Ssam/* short ctrl */ 137187120Ssam/* Preamble dot11Rate Rate */ 138187120Ssam [0] = { .phy = OFDM, 6000, 0x00, B(12), 0 }, 139187120Ssam [1] = { .phy = OFDM, 9000, 0x00, 18, 0 }, 140187120Ssam [2] = { .phy = OFDM, 12000, 0x00, B(24), 2 }, 141187120Ssam [3] = { .phy = OFDM, 18000, 0x00, 36, 2 }, 142187120Ssam [4] = { .phy = OFDM, 24000, 0x00, B(48), 4 }, 143187120Ssam [5] = { .phy = OFDM, 36000, 0x00, 72, 4 }, 144187120Ssam [6] = { .phy = OFDM, 48000, 0x00, 96, 4 }, 145187120Ssam [7] = { .phy = OFDM, 54000, 0x00, 108, 4 } 146187120Ssam }, 147187120Ssam}; 148187120Ssam 149187120Ssamstatic struct ieee80211_rate_table ieee80211_half_table = { 150187120Ssam .rateCount = 8, 151187120Ssam .info = { 152187120Ssam/* short ctrl */ 153187120Ssam/* Preamble dot11Rate Rate */ 154188785Ssam [0] = { .phy = HALF, 3000, 0x00, B(6), 0 }, 155188785Ssam [1] = { .phy = HALF, 4500, 0x00, 9, 0 }, 156188785Ssam [2] = { .phy = HALF, 6000, 0x00, B(12), 2 }, 157188785Ssam [3] = { .phy = HALF, 9000, 0x00, 18, 2 }, 158188785Ssam [4] = { .phy = HALF, 12000, 0x00, B(24), 4 }, 159188785Ssam [5] = { .phy = HALF, 18000, 0x00, 36, 4 }, 160188785Ssam [6] = { .phy = HALF, 24000, 0x00, 48, 4 }, 161188785Ssam [7] = { .phy = HALF, 27000, 0x00, 54, 4 } 162187120Ssam }, 163187120Ssam}; 164187120Ssam 165187120Ssamstatic struct ieee80211_rate_table ieee80211_quarter_table = { 166187120Ssam .rateCount = 8, 167187120Ssam .info = { 168187120Ssam/* short ctrl */ 169187120Ssam/* Preamble dot11Rate Rate */ 170188785Ssam [0] = { .phy = QUART, 1500, 0x00, B(3), 0 }, 171188785Ssam [1] = { .phy = QUART, 2250, 0x00, 4, 0 }, 172188785Ssam [2] = { .phy = QUART, 3000, 0x00, B(9), 2 }, 173188785Ssam [3] = { .phy = QUART, 4500, 0x00, 9, 2 }, 174188785Ssam [4] = { .phy = QUART, 6000, 0x00, B(12), 4 }, 175188785Ssam [5] = { .phy = QUART, 9000, 0x00, 18, 4 }, 176188785Ssam [6] = { .phy = QUART, 12000, 0x00, 24, 4 }, 177188785Ssam [7] = { .phy = QUART, 13500, 0x00, 27, 4 } 178187120Ssam }, 179187120Ssam}; 180187120Ssam 181187120Ssamstatic struct ieee80211_rate_table ieee80211_turbog_table = { 182187120Ssam .rateCount = 7, 183187120Ssam .info = { 184187120Ssam/* short ctrl */ 185187120Ssam/* Preamble dot11Rate Rate */ 186187120Ssam [0] = { .phy = TURBO, 12000, 0x00, B(12), 0 }, 187187120Ssam [1] = { .phy = TURBO, 24000, 0x00, B(24), 1 }, 188187120Ssam [2] = { .phy = TURBO, 36000, 0x00, 36, 1 }, 189187120Ssam [3] = { .phy = TURBO, 48000, 0x00, B(48), 3 }, 190187120Ssam [4] = { .phy = TURBO, 72000, 0x00, 72, 3 }, 191187120Ssam [5] = { .phy = TURBO, 96000, 0x00, 96, 3 }, 192187120Ssam [6] = { .phy = TURBO, 108000, 0x00, 108, 3 } 193187120Ssam }, 194187120Ssam}; 195187120Ssam 196187120Ssamstatic struct ieee80211_rate_table ieee80211_turboa_table = { 197187120Ssam .rateCount = 8, 198187120Ssam .info = { 199187120Ssam/* short ctrl */ 200187120Ssam/* Preamble dot11Rate Rate */ 201187120Ssam [0] = { .phy = TURBO, 12000, 0x00, B(12), 0 }, 202187120Ssam [1] = { .phy = TURBO, 18000, 0x00, 18, 0 }, 203187120Ssam [2] = { .phy = TURBO, 24000, 0x00, B(24), 2 }, 204187120Ssam [3] = { .phy = TURBO, 36000, 0x00, 36, 2 }, 205187120Ssam [4] = { .phy = TURBO, 48000, 0x00, B(48), 4 }, 206187120Ssam [5] = { .phy = TURBO, 72000, 0x00, 72, 4 }, 207187120Ssam [6] = { .phy = TURBO, 96000, 0x00, 96, 4 }, 208187120Ssam [7] = { .phy = TURBO, 108000, 0x00, 108, 4 } 209187120Ssam }, 210187120Ssam}; 211187120Ssam 212187120Ssam#undef Mb 213187120Ssam#undef B 214187120Ssam#undef OFDM 215187120Ssam#undef CCK 216187120Ssam#undef TURBO 217187120Ssam#undef XR 218187120Ssam 219187120Ssam/* 220187120Ssam * Setup a rate table's reverse lookup table and fill in 221187120Ssam * ack durations. The reverse lookup tables are assumed 222187120Ssam * to be initialized to zero (or at least the first entry). 223187120Ssam * We use this as a key that indicates whether or not 224187120Ssam * we've previously setup the reverse lookup table. 225187120Ssam * 226187120Ssam * XXX not reentrant, but shouldn't matter 227187120Ssam */ 228187120Ssamstatic void 229187120Ssamieee80211_setup_ratetable(struct ieee80211_rate_table *rt) 230187120Ssam{ 231187120Ssam#define N(a) (sizeof(a)/sizeof(a[0])) 232187120Ssam#define WLAN_CTRL_FRAME_SIZE \ 233187120Ssam (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN) 234187120Ssam 235187120Ssam int i; 236187120Ssam 237187120Ssam for (i = 0; i < N(rt->rateCodeToIndex); i++) 238187120Ssam rt->rateCodeToIndex[i] = (uint8_t) -1; 239187120Ssam for (i = 0; i < rt->rateCount; i++) { 240187120Ssam uint8_t code = rt->info[i].dot11Rate; 241187120Ssam uint8_t cix = rt->info[i].ctlRateIndex; 242187120Ssam uint8_t ctl_rate = rt->info[cix].dot11Rate; 243187120Ssam 244187120Ssam rt->rateCodeToIndex[code] = i; 245187120Ssam if (code & IEEE80211_RATE_BASIC) { 246187120Ssam /* 247187120Ssam * Map w/o basic rate bit too. 248187120Ssam */ 249187120Ssam code &= IEEE80211_RATE_VAL; 250187120Ssam rt->rateCodeToIndex[code] = i; 251187120Ssam } 252187120Ssam 253187120Ssam /* 254187120Ssam * XXX for 11g the control rate to use for 5.5 and 11 Mb/s 255187120Ssam * depends on whether they are marked as basic rates; 256187120Ssam * the static tables are setup with an 11b-compatible 257187120Ssam * 2Mb/s rate which will work but is suboptimal 258187120Ssam * 259187120Ssam * NB: Control rate is always less than or equal to the 260187120Ssam * current rate, so control rate's reverse lookup entry 261187120Ssam * has been installed and following call is safe. 262187120Ssam */ 263187120Ssam rt->info[i].lpAckDuration = ieee80211_compute_duration(rt, 264187120Ssam WLAN_CTRL_FRAME_SIZE, ctl_rate, 0); 265187120Ssam rt->info[i].spAckDuration = ieee80211_compute_duration(rt, 266187120Ssam WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE); 267187120Ssam } 268187120Ssam 269187120Ssam#undef WLAN_CTRL_FRAME_SIZE 270187120Ssam#undef N 271187120Ssam} 272187120Ssam 273187120Ssam/* Setup all rate tables */ 274187120Ssamstatic void 275187120Ssamieee80211_phy_init(void) 276187120Ssam{ 277187120Ssam#define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 278187120Ssam static struct ieee80211_rate_table * const ratetables[] = { 279187120Ssam &ieee80211_half_table, 280187120Ssam &ieee80211_quarter_table, 281187120Ssam &ieee80211_11a_table, 282187120Ssam &ieee80211_11g_table, 283187120Ssam &ieee80211_turbog_table, 284187120Ssam &ieee80211_turboa_table, 285187120Ssam &ieee80211_turboa_table, 286187120Ssam &ieee80211_11a_table, 287187120Ssam &ieee80211_11g_table, 288187120Ssam &ieee80211_11b_table 289187120Ssam }; 290187120Ssam int i; 291187120Ssam 292187120Ssam for (i = 0; i < N(ratetables); ++i) 293187120Ssam ieee80211_setup_ratetable(ratetables[i]); 294187120Ssam 295187120Ssam#undef N 296187120Ssam} 297188785Ssam#define CCK_SIFS_TIME 10 298188785Ssam#define CCK_PREAMBLE_BITS 144 299188785Ssam#define CCK_PLCP_BITS 48 300187120Ssam 301188785Ssam#define OFDM_SIFS_TIME 16 302188785Ssam#define OFDM_PREAMBLE_TIME 20 303188785Ssam#define OFDM_PLCP_BITS 22 304188785Ssam#define OFDM_SYMBOL_TIME 4 305188785Ssam 306188785Ssam#define OFDM_HALF_SIFS_TIME 32 307188785Ssam#define OFDM_HALF_PREAMBLE_TIME 40 308188785Ssam#define OFDM_HALF_PLCP_BITS 22 309188785Ssam#define OFDM_HALF_SYMBOL_TIME 8 310188785Ssam 311188785Ssam#define OFDM_QUARTER_SIFS_TIME 64 312188785Ssam#define OFDM_QUARTER_PREAMBLE_TIME 80 313188785Ssam#define OFDM_QUARTER_PLCP_BITS 22 314188785Ssam#define OFDM_QUARTER_SYMBOL_TIME 16 315188785Ssam 316188785Ssam#define TURBO_SIFS_TIME 8 317188785Ssam#define TURBO_PREAMBLE_TIME 14 318188785Ssam#define TURBO_PLCP_BITS 22 319188785Ssam#define TURBO_SYMBOL_TIME 4 320188785Ssam 321188785Ssam#define HT_L_STF 8 322188785Ssam#define HT_L_LTF 8 323188785Ssam#define HT_L_SIG 4 324188785Ssam#define HT_SIG 8 325188785Ssam#define HT_STF 4 326188785Ssam#define HT_LTF(n) ((n) * 4) 327188785Ssam 328187120Ssam/* 329187120Ssam * Compute the time to transmit a frame of length frameLen bytes 330187120Ssam * using the specified rate, phy, and short preamble setting. 331187120Ssam * SIFS is included. 332187120Ssam */ 333187120Ssamuint16_t 334187120Ssamieee80211_compute_duration(const struct ieee80211_rate_table *rt, 335187120Ssam uint32_t frameLen, uint16_t rate, int isShortPreamble) 336187120Ssam{ 337187120Ssam uint8_t rix = rt->rateCodeToIndex[rate]; 338187120Ssam uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime; 339187120Ssam uint32_t kbps; 340187120Ssam 341187120Ssam KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate)); 342187120Ssam kbps = rt->info[rix].rateKbps; 343187120Ssam if (kbps == 0) /* XXX bandaid for channel changes */ 344187120Ssam return 0; 345187120Ssam 346187120Ssam switch (rt->info[rix].phy) { 347187120Ssam case IEEE80211_T_CCK: 348187120Ssam phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; 349187120Ssam if (isShortPreamble && rt->info[rix].shortPreamble) 350187120Ssam phyTime >>= 1; 351187120Ssam numBits = frameLen << 3; 352187120Ssam txTime = CCK_SIFS_TIME + phyTime 353187120Ssam + ((numBits * 1000)/kbps); 354187120Ssam break; 355187120Ssam case IEEE80211_T_OFDM: 356188785Ssam bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; 357188785Ssam KASSERT(bitsPerSymbol != 0, ("full rate bps")); 358187120Ssam 359188785Ssam numBits = OFDM_PLCP_BITS + (frameLen << 3); 360188785Ssam numSymbols = howmany(numBits, bitsPerSymbol); 361188785Ssam txTime = OFDM_SIFS_TIME 362188785Ssam + OFDM_PREAMBLE_TIME 363188785Ssam + (numSymbols * OFDM_SYMBOL_TIME); 364188785Ssam break; 365188785Ssam case IEEE80211_T_OFDM_HALF: 366188785Ssam bitsPerSymbol = (kbps * OFDM_HALF_SYMBOL_TIME) / 1000; 367188785Ssam KASSERT(bitsPerSymbol != 0, ("1/4 rate bps")); 368187120Ssam 369188785Ssam numBits = OFDM_PLCP_BITS + (frameLen << 3); 370188785Ssam numSymbols = howmany(numBits, bitsPerSymbol); 371188785Ssam txTime = OFDM_HALF_SIFS_TIME 372188785Ssam + OFDM_HALF_PREAMBLE_TIME 373188785Ssam + (numSymbols * OFDM_HALF_SYMBOL_TIME); 374188785Ssam break; 375188785Ssam case IEEE80211_T_OFDM_QUARTER: 376188785Ssam bitsPerSymbol = (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000; 377188785Ssam KASSERT(bitsPerSymbol != 0, ("1/2 rate bps")); 378187120Ssam 379188785Ssam numBits = OFDM_PLCP_BITS + (frameLen << 3); 380188785Ssam numSymbols = howmany(numBits, bitsPerSymbol); 381188785Ssam txTime = OFDM_QUARTER_SIFS_TIME 382188785Ssam + OFDM_QUARTER_PREAMBLE_TIME 383188785Ssam + (numSymbols * OFDM_QUARTER_SYMBOL_TIME); 384187120Ssam break; 385187120Ssam case IEEE80211_T_TURBO: 386187120Ssam /* we still save OFDM rates in kbps - so double them */ 387187120Ssam bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000; 388187120Ssam KASSERT(bitsPerSymbol != 0, ("turbo bps")); 389187120Ssam 390187120Ssam numBits = TURBO_PLCP_BITS + (frameLen << 3); 391187120Ssam numSymbols = howmany(numBits, bitsPerSymbol); 392187120Ssam txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME 393187120Ssam + (numSymbols * TURBO_SYMBOL_TIME); 394187120Ssam break; 395187120Ssam default: 396187120Ssam panic("%s: unknown phy %u (rate %u)\n", __func__, 397187120Ssam rt->info[rix].phy, rate); 398187120Ssam break; 399187120Ssam } 400187120Ssam return txTime; 401187120Ssam} 402187120Ssam 403187120Ssamuint32_t 404187120Ssamieee80211_compute_duration_ht(const struct ieee80211_rate_table *rt, 405187120Ssam uint32_t frameLen, uint16_t rate, 406187120Ssam int streams, int isht40, int isShortGI) 407187120Ssam{ 408187120Ssam static const uint16_t ht20_bps[16] = { 409187120Ssam 26, 52, 78, 104, 156, 208, 234, 260, 410187120Ssam 52, 104, 156, 208, 312, 416, 468, 520 411187120Ssam }; 412187120Ssam static const uint16_t ht40_bps[16] = { 413187120Ssam 54, 108, 162, 216, 324, 432, 486, 540, 414187120Ssam 108, 216, 324, 432, 648, 864, 972, 1080, 415187120Ssam }; 416187120Ssam uint32_t bitsPerSymbol, numBits, numSymbols, txTime; 417187120Ssam 418187120Ssam KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate)); 419187120Ssam KASSERT((rate &~ IEEE80211_RATE_MCS) < 16, ("bad mcs 0x%x", rate)); 420187120Ssam 421187120Ssam if (isht40) 422187120Ssam bitsPerSymbol = ht40_bps[rate & 0xf]; 423187120Ssam else 424187120Ssam bitsPerSymbol = ht20_bps[rate & 0xf]; 425187120Ssam numBits = OFDM_PLCP_BITS + (frameLen << 3); 426187120Ssam numSymbols = howmany(numBits, bitsPerSymbol); 427187120Ssam if (isShortGI) 428187120Ssam txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */ 429187120Ssam else 430187120Ssam txTime = numSymbols * 4; /* 4us */ 431187120Ssam return txTime + HT_L_STF + HT_L_LTF + 432187120Ssam HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams); 433187120Ssam} 434187120Ssam 435187120Ssamstatic const struct ieee80211_rate_table * 436187120Ssammode2table(const char *mode) 437187120Ssam{ 438187120Ssam if (strcasecmp(mode, "half") == 0) 439187120Ssam return &ieee80211_half_table; 440187120Ssam else if (strcasecmp(mode, "quarter") == 0) 441187120Ssam return &ieee80211_quarter_table; 442187120Ssam else if (strcasecmp(mode, "hta") == 0) 443187120Ssam return &ieee80211_11a_table; /* XXX */ 444187120Ssam else if (strcasecmp(mode, "htg") == 0) 445187120Ssam return &ieee80211_11g_table; /* XXX */ 446187120Ssam else if (strcasecmp(mode, "108g") == 0) 447187120Ssam return &ieee80211_turbog_table; 448187120Ssam else if (strcasecmp(mode, "sturbo") == 0) 449187120Ssam return &ieee80211_turboa_table; 450187120Ssam else if (strcasecmp(mode, "turbo") == 0) 451187120Ssam return &ieee80211_turboa_table; 452187120Ssam else if (strcasecmp(mode, "11a") == 0) 453187120Ssam return &ieee80211_11a_table; 454187120Ssam else if (strcasecmp(mode, "11g") == 0) 455187120Ssam return &ieee80211_11g_table; 456187120Ssam else if (strcasecmp(mode, "11b") == 0) 457187120Ssam return &ieee80211_11b_table; 458187120Ssam else 459187120Ssam return NULL; 460187120Ssam} 461187120Ssam 462187120Ssamconst char * 463187120Ssamsrate(int rate) 464187120Ssam{ 465187120Ssam static char buf[32]; 466187120Ssam if (rate & 1) 467187120Ssam snprintf(buf, sizeof(buf), "%u.5", rate/2); 468187120Ssam else 469187120Ssam snprintf(buf, sizeof(buf), "%u", rate/2); 470187120Ssam return buf; 471187120Ssam} 472187120Ssam 473187120Ssamstatic int 474187120Ssamcheckpreamble(const struct ieee80211_rate_table *rt, uint8_t rix, 475187120Ssam int isShortPreamble, int verbose) 476187120Ssam{ 477187120Ssam if (isShortPreamble) { 478187120Ssam if (rt->info[rix].phy != IEEE80211_T_CCK) { 479187120Ssam if (verbose) 480187120Ssam warnx("short preamble not meaningful, ignored"); 481187120Ssam isShortPreamble = 0; 482187120Ssam } else if (!rt->info[rix].shortPreamble) { 483187120Ssam if (verbose) 484187120Ssam warnx("short preamble not meaningful with " 485187120Ssam "rate %s, ignored", 486187120Ssam srate(rt->info[rix].dot11Rate &~ IEEE80211_RATE_BASIC)); 487187120Ssam isShortPreamble = 0; 488187120Ssam } 489187120Ssam } 490187120Ssam return isShortPreamble; 491187120Ssam} 492187120Ssam 493187120Ssamstatic void 494187120Ssamusage(const char *progname) 495187120Ssam{ 496187120Ssam fprintf(stderr, "usage: %s [-a] [-l framelen] [-m mode] [-r rate] [-s]\n", 497187120Ssam progname); 498187120Ssam fprintf(stderr, "-a display calculations for all possible rates\n"); 499187120Ssam fprintf(stderr, "-l framelen length in bytes of 802.11 payload (default 1536)\n"); 500187120Ssam fprintf(stderr, "-m 11a calculate for 11a channel\n"); 501187120Ssam fprintf(stderr, "-m 11b calculate for 11b channel\n"); 502187120Ssam fprintf(stderr, "-m 11g calculate for 11g channel (default)\n"); 503187120Ssam fprintf(stderr, "-m half calculate for 1/2 width channel\n"); 504187120Ssam fprintf(stderr, "-m quarter calculate for 1/4 width channel\n"); 505187120Ssam fprintf(stderr, "-m 108g calculate for dynamic turbo 11g channel\n"); 506187120Ssam fprintf(stderr, "-m sturbo calculate for static turbo channel\n"); 507187120Ssam fprintf(stderr, "-m turbo calculate for dynamic turbo 11a channel\n"); 508187120Ssam fprintf(stderr, "-r rate IEEE rate code (default 54)\n"); 509187120Ssam fprintf(stderr, "-s short preamble (default long)\n"); 510187120Ssam exit(0); 511187120Ssam} 512187120Ssam 513187120Ssamint 514187120Ssammain(int argc, char *argv[]) 515187120Ssam{ 516187120Ssam const struct ieee80211_rate_table *rt; 517187120Ssam const char *mode; 518187120Ssam uint32_t frameLen; 519187120Ssam uint16_t rate; 520187120Ssam uint16_t time; 521187120Ssam uint8_t rix; 522187120Ssam int ch, allrates, isShortPreamble, isShort; 523187120Ssam float frate; 524187120Ssam 525187120Ssam ieee80211_phy_init(); 526187120Ssam 527187120Ssam mode = "11g"; 528187120Ssam isShortPreamble = 0; 529187120Ssam frameLen = 1500 530187120Ssam + sizeof(struct ieee80211_frame) 531187120Ssam + LLC_SNAPFRAMELEN 532187120Ssam + IEEE80211_CRC_LEN 533187120Ssam ; 534187120Ssam rate = 2*54; 535187120Ssam allrates = 0; 536187120Ssam while ((ch = getopt(argc, argv, "al:m:r:s")) != -1) { 537187120Ssam switch (ch) { 538187120Ssam case 'a': 539187120Ssam allrates = 1; 540187120Ssam break; 541187120Ssam case 'l': 542187120Ssam frameLen = strtoul(optarg, NULL, 0); 543187120Ssam break; 544187120Ssam case 'm': 545187120Ssam mode = optarg; 546187120Ssam break; 547187120Ssam case 'r': 548187120Ssam frate = atof(optarg); 549187120Ssam rate = (int) 2*frate; 550187120Ssam break; 551187120Ssam case 's': 552187120Ssam isShortPreamble = 1; 553187120Ssam break; 554187120Ssam default: 555187120Ssam usage(argv[0]); 556187120Ssam break; 557187120Ssam } 558187120Ssam } 559187120Ssam rt = mode2table(mode); 560187120Ssam if (rt == NULL) 561187120Ssam errx(-1, "unknown mode %s", mode); 562187120Ssam if (!allrates) { 563187120Ssam rix = rt->rateCodeToIndex[rate]; 564187120Ssam if (rix == (uint8_t) -1) 565187120Ssam errx(-1, "rate %s not valid for mode %s", srate(rate), mode); 566187120Ssam isShort = checkpreamble(rt, rix, isShortPreamble, 1); 567187120Ssam 568187120Ssam time = ieee80211_compute_duration(rt, frameLen, rate, isShort); 569187120Ssam printf("%u usec to send %u bytes @ %s Mb/s, %s preamble\n", 570187120Ssam time, frameLen, srate(rate), 571187120Ssam isShort ? "short" : "long"); 572187120Ssam } else { 573187120Ssam for (rix = 0; rix < rt->rateCount; rix++) { 574187120Ssam rate = rt->info[rix].dot11Rate &~ IEEE80211_RATE_BASIC; 575187120Ssam isShort = checkpreamble(rt, rix, isShortPreamble, 0); 576187120Ssam time = ieee80211_compute_duration(rt, frameLen, rate, 577187120Ssam isShort); 578187120Ssam printf("%u usec to send %u bytes @ %s Mb/s, %s preamble\n", 579187120Ssam time, frameLen, srate(rate), 580187120Ssam isShort ? "short" : "long"); 581187120Ssam } 582187120Ssam } 583187120Ssam return 0; 584187120Ssam} 585