From 1724725351eecf54f5bef13e26ebaf0bfe28fd50 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 4 Jul 2013 21:16:49 +0000 Subject: Implement basic 802.11n awareness in the PHY and AMRR rate control code. * Add 802.11n 2ghz and 5ghz tables, including legacy rates and up to MCS23 rates (3x3.) * Populate the rate code -> rate index lookup table with MCS _and_ normal rates, but _not_ the basic rate flag. Since the basic rate flag is the same as the MCS flag, we can only use one. * Introduce some accessor inlines that do PLCP and rate table lookup/access and enforce that it doesn't set the basic rate bit. They're not designed for MCS rates, so it will panic. * Start converting drivers that use the rate table stuff to use the accessor inlines and strip the basic flag. * Teach AMRR about basic 11n - it's still as crap for MCS as it is being used by iwn, so it's not a step _backwardS_. * Convert iwn over to accept 11n MCS rates rather than 'translate' legacy to MCS rates. It doesn't use a lookup table any longer; instead it's a function which takes the current node (for HT parameters) and the rate code, and returns the hardware PLCP code to use. Tested: * ath - it's a no-op, and it works that way * iwn - both 11n and non-11n --- sys/net80211/ieee80211_amrr.c | 101 +++++++++++++++++++--- sys/net80211/ieee80211_phy.c | 193 +++++++++++++++++++++++++++++++++++++++--- sys/net80211/ieee80211_phy.h | 51 ++++++++++- 3 files changed, 315 insertions(+), 30 deletions(-) (limited to 'sys/net80211') diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c index 73dd901..874659f 100644 --- a/sys/net80211/ieee80211_amrr.c +++ b/sys/net80211/ieee80211_amrr.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #endif #include +#include #include #include @@ -128,13 +129,25 @@ amrr_deinit(struct ieee80211vap *vap) free(vap->iv_rs, M_80211_RATECTL); } +static int +amrr_node_is_11n(struct ieee80211_node *ni) +{ + + if (ni->ni_chan == NULL) + return (0); + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return (0); + return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); +} + static void amrr_node_init(struct ieee80211_node *ni) { - const struct ieee80211_rateset *rs = &ni->ni_rates; + const struct ieee80211_rateset *rs = NULL; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_amrr *amrr = vap->iv_rs; struct ieee80211_amrr_node *amn; + uint8_t rate; if (ni->ni_rctls == NULL) { ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node), @@ -152,16 +165,50 @@ amrr_node_init(struct ieee80211_node *ni) amn->amn_txcnt = amn->amn_retrycnt = 0; amn->amn_success_threshold = amrr->amrr_min_success_threshold; - /* pick initial rate */ - for (amn->amn_rix = rs->rs_nrates - 1; - amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72; - amn->amn_rix--) - ; - ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; + /* 11n or not? Pick the right rateset */ + if (amrr_node_is_11n(ni)) { + /* XXX ew */ + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "%s: 11n node", __func__); + rs = (struct ieee80211_rateset *) &ni->ni_htrates; + } else { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "%s: non-11n node", __func__); + rs = &ni->ni_rates; + } + + /* Initial rate - lowest */ + rate = rs->rs_rates[0]; + + /* XXX clear the basic rate flag if it's not 11n */ + if (! amrr_node_is_11n(ni)) + rate &= IEEE80211_RATE_VAL; + + /* pick initial rate from the rateset - HT or otherwise */ + for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; + amn->amn_rix--) { + /* legacy - anything < 36mbit, stop searching */ + /* 11n - stop at MCS4 / MCS12 / MCS28 */ + if (amrr_node_is_11n(ni) && + (rs->rs_rates[amn->amn_rix] & 0x7) < 4) + break; + else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) + break; + rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; + } + + /* if the rate is an 11n rate, ensure the MCS bit is set */ + if (amrr_node_is_11n(ni)) + rate |= IEEE80211_RATE_MCS; + + /* Assign initial rate from the rateset */ + ni->ni_txrate = rate; amn->amn_ticks = ticks; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, - "AMRR initial rate %d", ni->ni_txrate); + "AMRR: nrates=%d, initial rate %d", + rs->rs_nrates, + rate); } static void @@ -175,19 +222,34 @@ amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, struct ieee80211_node *ni) { int rix = amn->amn_rix; + const struct ieee80211_rateset *rs = NULL; KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); + /* 11n or not? Pick the right rateset */ + if (amrr_node_is_11n(ni)) { + /* XXX ew */ + rs = (struct ieee80211_rateset *) &ni->ni_htrates; + } else { + rs = &ni->ni_rates; + } + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "AMRR: current rate %d, txcnt=%d, retrycnt=%d", + rs->rs_rates[rix] & IEEE80211_RATE_VAL, + amn->amn_txcnt, + amn->amn_retrycnt); + if (is_success(amn)) { amn->amn_success++; if (amn->amn_success >= amn->amn_success_threshold && - rix + 1 < ni->ni_rates.rs_nrates) { + rix + 1 < rs->rs_nrates) { amn->amn_recovery = 1; amn->amn_success = 0; rix++; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "AMRR increasing rate %d (txcnt=%d retrycnt=%d)", - ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL, + rs->rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); } else { amn->amn_recovery = 0; @@ -208,7 +270,7 @@ amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, rix--; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", - ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL, + rs->rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); } amn->amn_recovery = 0; @@ -231,14 +293,27 @@ amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) { struct ieee80211_amrr_node *amn = ni->ni_rctls; struct ieee80211_amrr *amrr = amn->amn_amrr; + const struct ieee80211_rateset *rs = NULL; int rix; + /* 11n or not? Pick the right rateset */ + if (amrr_node_is_11n(ni)) { + /* XXX ew */ + rs = (struct ieee80211_rateset *) &ni->ni_htrates; + } else { + rs = &ni->ni_rates; + } + if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { rix = amrr_update(amrr, amn, ni); if (rix != amn->amn_rix) { /* update public rate */ - ni->ni_txrate = - ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL; + ni->ni_txrate = rs->rs_rates[rix]; + /* XXX strip basic rate flag from txrate, if non-11n */ + if (amrr_node_is_11n(ni)) + ni->ni_txrate |= IEEE80211_RATE_MCS; + else + ni->ni_txrate &= IEEE80211_RATE_VAL; amn->amn_rix = rix; } amn->amn_ticks = ticks; diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c index d2ad60d..d1924d8 100644 --- a/sys/net80211/ieee80211_phy.c +++ b/sys/net80211/ieee80211_phy.c @@ -60,8 +60,11 @@ struct ieee80211_ds_plcp_hdr { #define TURBO IEEE80211_T_TURBO #define HALF IEEE80211_T_OFDM_HALF #define QUART IEEE80211_T_OFDM_QUARTER +#define HT IEEE80211_T_HT +/* XXX the 11n and the basic rate flag are unfortunately overlapping. Grr. */ +#define N(r) (IEEE80211_RATE_MCS | r) #define PBCC (IEEE80211_T_OFDM_QUARTER+1) /* XXX */ -#define B(r) (0x80 | r) +#define B(r) (IEEE80211_RATE_BASIC | r) #define Mb(x) (x*1000) static struct ieee80211_rate_table ieee80211_11b_table = { @@ -176,6 +179,98 @@ static struct ieee80211_rate_table ieee80211_turboa_table = { }, }; +static struct ieee80211_rate_table ieee80211_11ng_table = { + .rateCount = 36, + .info = { +/* short ctrl */ +/* Preamble dot11Rate Rate */ + [0] = { .phy = CCK, 1000, 0x00, B(2), 0 }, + [1] = { .phy = CCK, 2000, 0x04, B(4), 1 }, + [2] = { .phy = CCK, 5500, 0x04, B(11), 2 }, + [3] = { .phy = CCK, 11000, 0x04, B(22), 3 }, + [4] = { .phy = OFDM, 6000, 0x00, 12, 4 }, + [5] = { .phy = OFDM, 9000, 0x00, 18, 4 }, + [6] = { .phy = OFDM, 12000, 0x00, 24, 6 }, + [7] = { .phy = OFDM, 18000, 0x00, 36, 6 }, + [8] = { .phy = OFDM, 24000, 0x00, 48, 8 }, + [9] = { .phy = OFDM, 36000, 0x00, 72, 8 }, + [10] = { .phy = OFDM, 48000, 0x00, 96, 8 }, + [11] = { .phy = OFDM, 54000, 0x00, 108, 8 }, + + [12] = { .phy = HT, 6500, 0x00, N(0), 4 }, + [13] = { .phy = HT, 13000, 0x00, N(1), 6 }, + [14] = { .phy = HT, 19500, 0x00, N(2), 6 }, + [15] = { .phy = HT, 26000, 0x00, N(3), 8 }, + [16] = { .phy = HT, 39000, 0x00, N(4), 8 }, + [17] = { .phy = HT, 52000, 0x00, N(5), 8 }, + [18] = { .phy = HT, 58500, 0x00, N(6), 8 }, + [19] = { .phy = HT, 65000, 0x00, N(7), 8 }, + + [20] = { .phy = HT, 13000, 0x00, N(8), 4 }, + [21] = { .phy = HT, 26000, 0x00, N(9), 6 }, + [22] = { .phy = HT, 39000, 0x00, N(10), 6 }, + [23] = { .phy = HT, 52000, 0x00, N(11), 8 }, + [24] = { .phy = HT, 78000, 0x00, N(12), 8 }, + [25] = { .phy = HT, 104000, 0x00, N(13), 8 }, + [26] = { .phy = HT, 117000, 0x00, N(14), 8 }, + [27] = { .phy = HT, 130000, 0x00, N(15), 8 }, + + [28] = { .phy = HT, 19500, 0x00, N(16), 4 }, + [29] = { .phy = HT, 39000, 0x00, N(17), 6 }, + [30] = { .phy = HT, 58500, 0x00, N(18), 6 }, + [31] = { .phy = HT, 78000, 0x00, N(19), 8 }, + [32] = { .phy = HT, 117000, 0x00, N(20), 8 }, + [33] = { .phy = HT, 156000, 0x00, N(21), 8 }, + [34] = { .phy = HT, 175500, 0x00, N(22), 8 }, + [35] = { .phy = HT, 195000, 0x00, N(23), 8 }, + + }, +}; + +static struct ieee80211_rate_table ieee80211_11na_table = { + .rateCount = 32, + .info = { +/* short ctrl */ +/* Preamble dot11Rate Rate */ + [0] = { .phy = OFDM, 6000, 0x00, B(12), 0 }, + [1] = { .phy = OFDM, 9000, 0x00, 18, 0 }, + [2] = { .phy = OFDM, 12000, 0x00, B(24), 2 }, + [3] = { .phy = OFDM, 18000, 0x00, 36, 2 }, + [4] = { .phy = OFDM, 24000, 0x00, B(48), 4 }, + [5] = { .phy = OFDM, 36000, 0x00, 72, 4 }, + [6] = { .phy = OFDM, 48000, 0x00, 96, 4 }, + [7] = { .phy = OFDM, 54000, 0x00, 108, 4 }, + + [8] = { .phy = HT, 6500, 0x00, N(0), 0 }, + [9] = { .phy = HT, 13000, 0x00, N(1), 2 }, + [10] = { .phy = HT, 19500, 0x00, N(2), 2 }, + [11] = { .phy = HT, 26000, 0x00, N(3), 4 }, + [12] = { .phy = HT, 39000, 0x00, N(4), 4 }, + [13] = { .phy = HT, 52000, 0x00, N(5), 4 }, + [14] = { .phy = HT, 58500, 0x00, N(6), 4 }, + [15] = { .phy = HT, 65000, 0x00, N(7), 4 }, + + [16] = { .phy = HT, 13000, 0x00, N(8), 0 }, + [17] = { .phy = HT, 26000, 0x00, N(9), 2 }, + [18] = { .phy = HT, 39000, 0x00, N(10), 2 }, + [19] = { .phy = HT, 52000, 0x00, N(11), 4 }, + [20] = { .phy = HT, 78000, 0x00, N(12), 4 }, + [21] = { .phy = HT, 104000, 0x00, N(13), 4 }, + [22] = { .phy = HT, 117000, 0x00, N(14), 4 }, + [23] = { .phy = HT, 130000, 0x00, N(15), 4 }, + + [24] = { .phy = HT, 19500, 0x00, N(16), 0 }, + [25] = { .phy = HT, 39000, 0x00, N(17), 2 }, + [26] = { .phy = HT, 58500, 0x00, N(18), 2 }, + [27] = { .phy = HT, 78000, 0x00, N(19), 4 }, + [28] = { .phy = HT, 117000, 0x00, N(20), 4 }, + [29] = { .phy = HT, 156000, 0x00, N(21), 4 }, + [30] = { .phy = HT, 175500, 0x00, N(22), 4 }, + [31] = { .phy = HT, 195000, 0x00, N(23), 4 }, + + }, +}; + #undef Mb #undef B #undef OFDM @@ -184,6 +279,8 @@ static struct ieee80211_rate_table ieee80211_turboa_table = { #undef CCK #undef TURBO #undef XR +#undef HT +#undef N /* * Setup a rate table's reverse lookup table and fill in @@ -210,15 +307,23 @@ ieee80211_setup_ratetable(struct ieee80211_rate_table *rt) uint8_t cix = rt->info[i].ctlRateIndex; uint8_t ctl_rate = rt->info[cix].dot11Rate; - rt->rateCodeToIndex[code] = i; - if (code & IEEE80211_RATE_BASIC) { - /* - * Map w/o basic rate bit too. - */ - code &= IEEE80211_RATE_VAL; - rt->rateCodeToIndex[code] = i; + /* + * Map without the basic rate bit. + * + * It's up to the caller to ensure that the basic + * rate bit is stripped here. + * + * For HT, use the MCS rate bit. + */ + code &= IEEE80211_RATE_VAL; + if (rt->info[i].phy == IEEE80211_T_HT) { + code |= IEEE80211_RATE_MCS; } + /* XXX assume the control rate is non-MCS? */ + ctl_rate &= IEEE80211_RATE_VAL; + rt->rateCodeToIndex[code] = i; + /* * XXX for 11g the control rate to use for 5.5 and 11 Mb/s * depends on whether they are marked as basic rates; @@ -247,11 +352,10 @@ ieee80211_phy_init(void) static struct ieee80211_rate_table * const ratetables[] = { &ieee80211_half_table, &ieee80211_quarter_table, - &ieee80211_11a_table, - &ieee80211_11g_table, + &ieee80211_11na_table, + &ieee80211_11ng_table, &ieee80211_turbog_table, &ieee80211_turboa_table, - &ieee80211_turboa_table, &ieee80211_11a_table, &ieee80211_11g_table, &ieee80211_11b_table @@ -276,9 +380,9 @@ ieee80211_get_ratetable(struct ieee80211_channel *c) else if (IEEE80211_IS_CHAN_QUARTER(c)) rt = &ieee80211_quarter_table; else if (IEEE80211_IS_CHAN_HTA(c)) - rt = &ieee80211_11a_table; /* XXX */ + rt = &ieee80211_11na_table; else if (IEEE80211_IS_CHAN_HTG(c)) - rt = &ieee80211_11g_table; /* XXX */ + rt = &ieee80211_11ng_table; else if (IEEE80211_IS_CHAN_108G(c)) rt = &ieee80211_turbog_table; else if (IEEE80211_IS_CHAN_ST(c)) @@ -463,3 +567,66 @@ ieee80211_compute_duration(const struct ieee80211_rate_table *rt, } return txTime; } + +static const uint16_t ht20_bps[32] = { + 26, 52, 78, 104, 156, 208, 234, 260, + 52, 104, 156, 208, 312, 416, 468, 520, + 78, 156, 234, 312, 468, 624, 702, 780, + 104, 208, 312, 416, 624, 832, 936, 1040 +}; +static const uint16_t ht40_bps[32] = { + 54, 108, 162, 216, 324, 432, 486, 540, + 108, 216, 324, 432, 648, 864, 972, 1080, + 162, 324, 486, 648, 972, 1296, 1458, 1620, + 216, 432, 648, 864, 1296, 1728, 1944, 2160 +}; + + +#define OFDM_PLCP_BITS 22 +#define HT_L_STF 8 +#define HT_L_LTF 8 +#define HT_L_SIG 4 +#define HT_SIG 8 +#define HT_STF 4 +#define HT_LTF(n) ((n) * 4) + +#define HT_RC_2_MCS(_rc) ((_rc) & 0xf) +#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) +#define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS) + +/* + * Calculate the transmit duration of an 11n frame. + */ +uint32_t +ieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate, + int streams, int isht40, int isShortGI) +{ + uint32_t bitsPerSymbol, numBits, numSymbols, txTime; + + KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate)); + KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate)); + + if (isht40) + bitsPerSymbol = ht40_bps[rate & 0x1f]; + else + bitsPerSymbol = ht20_bps[rate & 0x1f]; + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = howmany(numBits, bitsPerSymbol); + if (isShortGI) + txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */ + else + txTime = numSymbols * 4; /* 4us */ + return txTime + HT_L_STF + HT_L_LTF + + HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams); +} + +#undef IS_HT_RATE +#undef HT_RC_2_STREAMS +#undef HT_RC_2_MCS +#undef HT_LTF +#undef HT_STF +#undef HT_SIG +#undef HT_L_SIG +#undef HT_L_LTF +#undef HT_L_STF +#undef OFDM_PLCP_BITS diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h index 56b404f7..bf39cdd 100644 --- a/sys/net80211/ieee80211_phy.h +++ b/sys/net80211/ieee80211_phy.h @@ -60,6 +60,8 @@ struct ieee80211_channel; +#define IEEE80211_RATE_TABLE_SIZE 128 + struct ieee80211_rate_table { int rateCount; /* NB: for proper padding */ uint8_t rateCodeToIndex[256]; /* back mapping */ @@ -74,7 +76,7 @@ struct ieee80211_rate_table { * rate; used for dur. calcs */ uint16_t lpAckDuration; /* long preamble ACK dur. */ uint16_t spAckDuration; /* short preamble ACK dur. */ - } info[32]; + } info[IEEE80211_RATE_TABLE_SIZE]; }; const struct ieee80211_rate_table *ieee80211_get_ratetable( @@ -83,7 +85,14 @@ const struct ieee80211_rate_table *ieee80211_get_ratetable( static __inline__ uint8_t ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate) { - uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex; + /* + * XXX Assert this is for a legacy rate; not for an MCS rate. + * If the caller wishes to use it for a basic rate, they should + * clear the high bit first. + */ + KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate)); + + uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex; KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate)); return rt->info[cix].dot11Rate; } @@ -91,7 +100,14 @@ ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate) static __inline__ uint8_t ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate) { - uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex; + /* + * XXX Assert this is for a legacy rate; not for an MCS rate. + * If the caller wishes to use it for a basic rate, they should + * clear the high bit first. + */ + KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate)); + + uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex; KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate)); return rt->info[cix].dot11Rate; } @@ -99,7 +115,14 @@ ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate) static __inline__ enum ieee80211_phytype ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate) { - uint8_t rix = rt->rateCodeToIndex[rate]; + /* + * XXX Assert this is for a legacy rate; not for an MCS rate. + * If the caller wishes to use it for a basic rate, they should + * clear the high bit first. + */ + KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate)); + + uint8_t rix = rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]; KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate)); return rt->info[rix].phy; } @@ -107,6 +130,13 @@ ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate) static __inline__ int ieee80211_isratevalid(const struct ieee80211_rate_table *rt, uint8_t rate) { + /* + * XXX Assert this is for a legacy rate; not for an MCS rate. + * If the caller wishes to use it for a basic rate, they should + * clear the high bit first. + */ + KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate)); + return rt->rateCodeToIndex[rate] != (uint8_t)-1; } @@ -134,6 +164,14 @@ ieee80211_ack_duration(const struct ieee80211_rate_table *rt, } } +static __inline__ uint8_t +ieee80211_legacy_rate_lookup(const struct ieee80211_rate_table *rt, + uint8_t rate) +{ + + return (rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]); +} + /* * Compute the time to transmit a frame of length frameLen bytes * using the specified 802.11 rate code, phy, and short preamble @@ -151,5 +189,10 @@ uint8_t ieee80211_plcp2rate(uint8_t, enum ieee80211_phytype); * Convert 802.11 rate code to PLCP signal. */ uint8_t ieee80211_rate2plcp(int, enum ieee80211_phytype); + +uint32_t ieee80211_compute_duration_ht(uint32_t frameLen, + uint16_t rate, int streams, int isht40, + int isShortGI); + #endif /* _KERNEL */ #endif /* !_NET80211_IEEE80211_PHY_H_ */ -- cgit v1.1