The various components accessing the bitrates table must use consider the used channel bandwidth to select only available rates or calculate the bitrate correctly. There are some rates in reduced bandwidth modes which can't be represented as multiples of 500kbps, like 2.25 MBit/s in 5 MHz mode. The standard suggests to round up to the next multiple of 500kbps, just do that in mac80211 as well. Signed-off-by: Simon Wunderlich <siwu@xxxxxxxxxxxxxxxxxx> Signed-off-by: Mathias Kretschmer <mathias.kretschmer@xxxxxxxxxxxxxxxxxxx> --- include/net/mac80211.h | 1 - net/mac80211/cfg.c | 36 +++++++++-- net/mac80211/ibss.c | 77 +++++++++++++++++----- net/mac80211/mlme.c | 84 +++++++++++++++++------- net/mac80211/rate.c | 50 +++++++------- net/mac80211/rc80211_minstrel.c | 14 +++- net/mac80211/rc80211_minstrel_ht.c | 3 + net/mac80211/rx.c | 7 +- net/mac80211/status.c | 17 +++-- net/mac80211/tx.c | 14 ++-- net/mac80211/util.c | 126 ++++++++++++++++++++++++++++-------- 11 files changed, 321 insertions(+), 108 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 04c2d46..0ce4290 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4238,7 +4238,6 @@ rate_lowest_index(struct ieee80211_supported_band *sband, struct ieee80211_sta *sta) { int i; - for (i = 0; i < sband->n_bitrates; i++) if (rate_supported(sta, sband->band, i)) return i; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1a89c80..49b401c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -392,9 +392,13 @@ void sta_set_rate_info_tx(struct sta_info *sta, rinfo->nss = ieee80211_rate_get_vht_nss(rate); } else { struct ieee80211_supported_band *sband; + int divisor = ieee80211_vif_get_divisor(&sta->sdata->vif); + sband = sta->local->hw.wiphy->bands[ ieee80211_get_sdata_band(sta->sdata)]; - rinfo->legacy = sband->bitrates[rate->idx].bitrate; + rinfo->legacy = + DIV_ROUND_UP(sband->bitrates[rate->idx].bitrate, + divisor); } if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; @@ -419,11 +423,14 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) rinfo->mcs = sta->last_rx_rate_idx; } else { struct ieee80211_supported_band *sband; + int divisor = ieee80211_vif_get_divisor(&sta->sdata->vif); sband = sta->local->hw.wiphy->bands[ ieee80211_get_sdata_band(sta->sdata)]; rinfo->legacy = - sband->bitrates[sta->last_rx_rate_idx].bitrate; + DIV_ROUND_UP(sband->bitrates[ + sta->last_rx_rate_idx].bitrate, + divisor); } if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) @@ -1264,12 +1271,23 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->listen_interval = params->listen_interval; if (params->supported_rates) { + int divisor = ieee80211_vif_get_divisor(&sta->sdata->vif); + u32 rate_flags = ieee80211_chandef_rate_flags( + &sdata->vif.bss_conf.chandef); rates = 0; + for (i = 0; i < params->supported_rates_len; i++) { int rate = (params->supported_rates[i] & 0x7f) * 5; + int brate; for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) + brate = sband->bitrates[j].bitrate; + brate = DIV_ROUND_UP(brate, divisor); + if ((rate_flags & sband->bitrates[i].flags) + != rate_flags) + continue; + + if (brate == rate) rates |= BIT(j); } } @@ -1935,11 +1953,21 @@ static int ieee80211_change_bss(struct wiphy *wiphy, int i, j; u32 rates = 0; struct ieee80211_supported_band *sband = wiphy->bands[band]; + int divisor = ieee80211_vif_get_divisor(&sdata->vif); + u32 rate_flags = ieee80211_chandef_rate_flags( + &sdata->vif.bss_conf.chandef); for (i = 0; i < params->basic_rates_len; i++) { int rate = (params->basic_rates[i] & 0x7f) * 5; + int brate; for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) + brate = sband->bitrates[j].bitrate; + brate = DIV_ROUND_UP(brate, divisor); + if ((rate_flags & sband->bitrates[i].flags) + != rate_flags) + continue; + + if (brate == rate) rates |= BIT(j); } } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 4e1fb81..da95735 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -43,16 +43,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; - int rates, i; + int rates_n = 0, i, ri; struct ieee80211_mgmt *mgmt; u8 *pos; struct ieee80211_supported_band *sband; struct cfg80211_bss *bss; - u32 bss_change; + u32 bss_change, rate_flags, rates = 0, rates_added = 0; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; struct cfg80211_chan_def chandef; struct beacon_data *presp; int frame_len; + int divisor; lockdep_assert_held(&ifibss->mtx); @@ -99,6 +100,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memcpy(ifibss->bssid, bssid, ETH_ALEN); sband = local->hw.wiphy->bands[chan->band]; + divisor = ieee80211_vif_get_divisor(&sdata->vif); /* Build IBSS probe response */ frame_len = sizeof(struct ieee80211_hdr_3addr) + @@ -134,15 +136,29 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memcpy(pos, ifibss->ssid, ifibss->ssid_len); pos += ifibss->ssid_len; - rates = min_t(int, 8, sband->n_bitrates); + rate_flags = ieee80211_chandef_rate_flags(&chandef); + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + + rates |= BIT(i); + rates_n++; + } + *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = rates; - for (i = 0; i < rates; i++) { - int rate = sband->bitrates[i].bitrate; + *pos++ = min_t(int, 8, rates_n); + for (ri = 0; ri < sband->n_bitrates; ri++) { + int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, + 5 * divisor); u8 basic = 0; - if (basic_rates & BIT(i)) + if (!(rates & BIT(ri))) + continue; + + if (basic_rates & BIT(ri)) basic = 0x80; - *pos++ = basic | (u8) (rate / 5); + *pos++ = basic | (u8) rate; + if (++rates_added == 8) + break; } if (sband->band == IEEE80211_BAND_2GHZ) { @@ -157,15 +173,20 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, *pos++ = 0; *pos++ = 0; - if (sband->n_bitrates > 8) { + /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */ + if (rates_n > 8) { *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = sband->n_bitrates - 8; - for (i = 8; i < sband->n_bitrates; i++) { - int rate = sband->bitrates[i].bitrate; + *pos++ = rates_n - 8; + for (; ri < sband->n_bitrates; ri++) { + int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, + 5 * divisor); u8 basic = 0; - if (basic_rates & BIT(i)) + if (!(rates & BIT(ri))) + continue; + + if (basic_rates & BIT(ri)) basic = 0x80; - *pos++ = basic | (u8) (rate / 5); + *pos++ = basic | (u8) rate; } } @@ -240,7 +261,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.ibss_creator = creator; ieee80211_bss_info_change_notify(sdata, bss_change); - ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates); + ieee80211_sta_def_wmm_params(sdata, rates, supp_rates); ifibss->state = IEEE80211_IBSS_MLME_JOINED; mod_timer(&ifibss->timer, @@ -264,6 +285,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, u16 beacon_int = cbss->beacon_interval; const struct cfg80211_bss_ies *ies; u64 tsf; + u32 rate_flags; + int divisor; lockdep_assert_held(&sdata->u.ibss.mtx); @@ -271,15 +294,24 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, beacon_int = 10; sband = sdata->local->hw.wiphy->bands[cbss->channel->band]; + rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef); + divisor = ieee80211_vif_get_divisor(&sdata->vif); basic_rates = 0; for (i = 0; i < bss->supp_rates_len; i++) { - int rate = (bss->supp_rates[i] & 0x7f) * 5; + int rate = bss->supp_rates[i] & 0x7f; bool is_basic = !!(bss->supp_rates[i] & 0x80); for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { + int brate; + if ((rate_flags & sband->bitrates[j].flags) + != rate_flags) + continue; + + brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, + 5 * divisor); + if (brate == rate) { if (is_basic) basic_rates |= BIT(j); break; @@ -1042,6 +1074,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, struct cfg80211_ibss_params *params) { u32 changed = 0; + u32 rate_flags; + struct ieee80211_supported_band *sband; + int i; mutex_lock(&sdata->u.ibss.mtx); @@ -1054,6 +1089,14 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.privacy = params->privacy; sdata->u.ibss.control_port = params->control_port; sdata->u.ibss.basic_rates = params->basic_rates; + + /* fix basic_rates if channel does not support these rates */ + rate_flags = ieee80211_chandef_rate_flags(¶ms->chandef); + sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band]; + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + sdata->u.ibss.basic_rates &= ~BIT(i); + } memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate, sizeof(params->mcast_rate)); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0eaee23..f36b909 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -514,20 +514,27 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, struct ieee80211_supported_band *sband, - u32 *rates) + u32 *rates, int divisor, u32 rate_flags) { int i, j, count; *rates = 0; count = 0; for (i = 0; i < supp_rates_len; i++) { - int rate = (supp_rates[i] & 0x7F) * 5; + int rate = supp_rates[i] & 0x7F; - for (j = 0; j < sband->n_bitrates; j++) - if (sband->bitrates[j].bitrate == rate) { + for (j = 0; j < sband->n_bitrates; j++) { + int brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, + 5 * divisor); + if ((rate_flags & sband->bitrates[j].flags) + != rate_flags) + continue; + + if (brate == rate) { *rates |= BIT(j); count++; break; } + } } return count; @@ -651,12 +658,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) struct ieee80211_mgmt *mgmt; u8 *pos, qos_info; size_t offset = 0, noffset; - int i, count, rates_len, supp_rates_len; + int i, count, rates_len, supp_rates_len, divisor; u16 capab; struct ieee80211_supported_band *sband; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *chan; - u32 rates = 0; + u32 rate_flags, rates = 0; lockdep_assert_held(&ifmgd->mtx); @@ -667,8 +674,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) return; } chan = chanctx_conf->def.chan; + rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def); rcu_read_unlock(); sband = local->hw.wiphy->bands[chan->band]; + divisor = ieee80211_vif_get_divisor(&sdata->vif); if (assoc_data->supp_rates_len) { /* @@ -679,15 +688,22 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) */ rates_len = ieee80211_compatible_rates(assoc_data->supp_rates, assoc_data->supp_rates_len, - sband, &rates); + sband, &rates, divisor, + rate_flags); } else { /* * In case AP not provide any supported rates information * before association, we send information element(s) with * all rates that we support. */ - rates = ~0; - rates_len = sband->n_bitrates; + rates_len = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & sband->bitrates[i].flags) + != rate_flags) + continue; + rates |= BIT(i); + rates_len++; + } } skb = alloc_skb(local->hw.extra_tx_headroom + @@ -764,8 +780,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) count = 0; for (i = 0; i < sband->n_bitrates; i++) { if (BIT(i) & rates) { - int rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); + int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, + 5 * divisor); + *pos++ = (u8) rate; if (++count == 8) break; } @@ -778,8 +795,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) for (i++; i < sband->n_bitrates; i++) { if (BIT(i) & rates) { - int rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); + int rate; + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, + 5 * divisor); + *pos++ = (u8) rate; } } } @@ -2443,15 +2462,16 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, u8 *supp_rates, unsigned int supp_rates_len, u32 *rates, u32 *basic_rates, bool *have_higher_than_11mbit, - int *min_rate, int *min_rate_index) + int *min_rate, int *min_rate_index, + int divisor, u32 rate_flags) { int i, j; for (i = 0; i < supp_rates_len; i++) { - int rate = (supp_rates[i] & 0x7f) * 5; + int rate = supp_rates[i] & 0x7f; bool is_basic = !!(supp_rates[i] & 0x80); - if (rate > 110) + if ((rate * 5 * divisor) > 110) *have_higher_than_11mbit = true; /* @@ -2467,12 +2487,19 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, continue; for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { + int brate; + brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, + divisor * 5); + if ((rate_flags & sband->bitrates[j].flags) + != rate_flags) + continue; + + if (brate == rate) { *rates |= BIT(j); if (is_basic) *basic_rates |= BIT(j); - if (rate < *min_rate) { - *min_rate = rate; + if ((rate * 5) < *min_rate) { + *min_rate = rate * 5; *min_rate_index = j; } break; @@ -3853,27 +3880,40 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, if (!new_sta) return -ENOMEM; } - if (new_sta) { u32 rates = 0, basic_rates = 0; bool have_higher_than_11mbit; int min_rate = INT_MAX, min_rate_index = -1; + struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_supported_band *sband; const struct cfg80211_bss_ies *ies; + int divisor; + u32 rate_flags; sband = local->hw.wiphy->bands[cbss->channel->band]; err = ieee80211_prep_channel(sdata, cbss); if (err) { sta_info_free(local, new_sta); - return err; + return -EINVAL; } + divisor = ieee80211_vif_get_divisor(&sdata->vif); + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return -EINVAL; + } + rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def); + rcu_read_unlock(); ieee80211_get_rates(sband, bss->supp_rates, bss->supp_rates_len, &rates, &basic_rates, &have_higher_than_11mbit, - &min_rate, &min_rate_index); + &min_rate, &min_rate_index, + divisor, rate_flags); /* * This used to be a workaround for basic rates missing diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 118e47b..eb7e290 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -232,37 +232,28 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, /* could not find a basic rate; use original selection */ } -static inline s8 -rate_lowest_non_cck_index(struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta) -{ - int i; - - for (i = 0; i < sband->n_bitrates; i++) { - struct ieee80211_rate *srate = &sband->bitrates[i]; - if ((srate->bitrate == 10) || (srate->bitrate == 20) || - (srate->bitrate == 55) || (srate->bitrate == 110)) - continue; - - if (rate_supported(sta, sband->band, i)) - return i; - } - - /* No matching rate found */ - return 0; -} - static void __rate_control_send_low(struct ieee80211_hw *hw, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, struct ieee80211_tx_info *info) { - if ((sband->band != IEEE80211_BAND_2GHZ) || - !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) - info->control.rates[0].idx = rate_lowest_index(sband, sta); - else - info->control.rates[0].idx = - rate_lowest_non_cck_index(sband, sta); + int i; + u32 rate_flags = + ieee80211_chandef_rate_flags(&hw->conf.chandef); + + if ((sband->band == IEEE80211_BAND_2GHZ) && + (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) + rate_flags |= IEEE80211_RATE_ERP_G; + + info->control.rates[0].idx = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if (!rate_supported(sta, sband->band, i)) + continue; + + info->control.rates[0].idx = i; + break; + } + WARN_ON_ONCE(i == sband->n_bitrates); info->control.rates[0].count = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? @@ -585,6 +576,7 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata, u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; bool has_mcs_mask; u32 mask; + u32 rate_flags; int i; /* @@ -594,6 +586,12 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata, */ mask = sdata->rc_rateidx_mask[info->band]; has_mcs_mask = sdata->rc_has_mcs_mask[info->band]; + rate_flags = + ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); + for (i = 0; i < sband->n_bitrates; i++) + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + mask &= ~BIT(i); + if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask) return; diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index ffabe1d..2fcce45 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -428,6 +428,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_rate *ctl_rate; unsigned int i, n = 0; unsigned int t_slot = 9; /* FIXME: get real slot time */ + u32 rate_flags; mi->sta = sta; mi->lowest_rix = rate_lowest_index(sband, sta); @@ -437,6 +438,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1, ieee80211_hw_get_divisor(mp->hw)); + rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate)); mi->max_prob_rate = 0; @@ -445,14 +447,20 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0; unsigned int tx_time_single; unsigned int cw = mp->cw_min; + int divisor; if (!rate_supported(sta, sband->band, i)) continue; + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + n++; memset(mr, 0, sizeof(*mr)); mr->rix = i; - mr->bitrate = sband->bitrates[i].bitrate / 5; + divisor = ieee80211_hw_get_divisor(mp->hw); + mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate, + divisor * 5); calc_rate_durations(sband->band, mr, &sband->bitrates[i], mp); /* calculate maximum number of retransmissions before @@ -551,6 +559,7 @@ minstrel_init_cck_rates(struct minstrel_priv *mp) { static const int bitrates[4] = { 10, 20, 55, 110 }; struct ieee80211_supported_band *sband; + u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); int i, j; sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; @@ -563,6 +572,9 @@ minstrel_init_cck_rates(struct minstrel_priv *mp) if (rate->flags & IEEE80211_RATE_ERP_G) continue; + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + for (j = 0; j < ARRAY_SIZE(bitrates); j++) { if (rate->bitrate != bitrates[j]) continue; diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index f05eeff..c0f7501 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -816,6 +816,7 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, struct ieee80211_sta *sta) { int i; + u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); if (sband->band != IEEE80211_BAND_2GHZ) return; @@ -825,6 +826,8 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, for (i = 0; i < 4; i++) { if (!rate_supported(sta, sband->band, mp->cck_rates[i])) continue; + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; mi->cck_supported |= BIT(i); if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c8447af..4d8f817 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -207,8 +207,13 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, */ *pos = 0; } else { + int divisor = 1; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); - *pos = rate->bitrate / 5; + if (status->flag & RX_FLAG_10MHZ) + divisor = 2; + else if (status->flag & RX_FLAG_5MHZ) + divisor = 4; + *pos = DIV_ROUND_UP(rate->bitrate, 5 * divisor); } pos++; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 4343920..1841481 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -252,9 +252,11 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info) return len; } -static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band - *sband, struct sk_buff *skb, - int retry_count, int rtap_len) +static void +ieee80211_add_tx_radiotap_header(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct sk_buff *skb, int retry_count, + int rtap_len) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -280,8 +282,12 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band /* IEEE80211_RADIOTAP_RATE */ if (info->status.rates[0].idx >= 0 && !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) { + int divisor = ieee80211_vif_get_divisor(&sdata->vif); + int rate; + rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); - *pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5; + rate = sband->bitrates[info->status.rates[0].idx].bitrate; + *pos = DIV_ROUND_UP(rate, 5 * divisor); /* padding for tx flags */ pos += 2; } @@ -624,7 +630,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb(skb); return; } - ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len); + ieee80211_add_tx_radiotap_header(sdata, sband, skb, retry_count, + rtap_len); /* XXX: is this sufficient for BPF? */ skb_set_mac_header(skb, 0); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e1f18a8..116d3cb 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -40,12 +40,15 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, struct sk_buff *skb, int group_addr, int next_frag_len) { - int rate, mrate, erp, dur, i, divisor; + int rate, mrate, erp, dur, i; struct ieee80211_rate *txrate; struct ieee80211_local *local = tx->local; struct ieee80211_supported_band *sband; struct ieee80211_hdr *hdr; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + u32 rate_flags = + ieee80211_chandef_rate_flags(&local->hw.conf.chandef); + int divisor = ieee80211_vif_get_divisor(&tx->sdata->vif); /* assume HW handles this */ if (tx->rate.flags & IEEE80211_TX_RC_MCS) @@ -122,8 +125,11 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, if (r->bitrate > txrate->bitrate) break; + if ((rate_flags & r->flags) != rate_flags) + continue; + if (tx->sdata->vif.bss_conf.basic_rates & BIT(i)) - rate = r->bitrate; + rate = DIV_ROUND_UP(r->bitrate, divisor); switch (sband->band) { case IEEE80211_BAND_2GHZ: { @@ -150,11 +156,9 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, if (rate == -1) { /* No matching basic rate found; use highest suitable mandatory * PHY rate */ - rate = mrate; + rate = DIV_ROUND_UP(mrate, divisor); } - divisor = ieee80211_vif_get_divisor(&tx->sdata->vif); - /* Don't calculate ACKs for QoS Frames with NoAck Policy set */ if (ieee80211_is_data_qos(hdr->frame_control) && *(ieee80211_get_qos_ctl(hdr)) & IEEE80211_QOS_CTL_ACK_POLICY_NOACK) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 5496764..6eaddbf 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -141,14 +141,19 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len, dur = 16; /* SIFS + signal ext */ dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */ dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ - dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, - 4 * rate); /* T_SYM x N_SYM */ /* IEEE 802.11-2012 18.3.2.4: all values above are: * * times 4 for 5 MHz * * times 2 for 10 MHz */ dur *= divisor; + + /* rates should already consider the channel bandwidth, + * don't apply divisor again. + */ + dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, + 4 * rate); /* T_SYM x N_SYM */ + } else { /* * 802.11b or 802.11g with 802.11b compatibility: @@ -205,7 +210,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, struct ieee80211_rate *rate; struct ieee80211_sub_if_data *sdata; bool short_preamble; - int erp, divisor = 1; + int erp, divisor = 1, bitrate; u16 dur; struct ieee80211_supported_band *sband; @@ -224,14 +229,16 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, divisor = ieee80211_vif_get_divisor(vif); } + bitrate = DIV_ROUND_UP(rate->bitrate, divisor); + /* CTS duration */ - dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate, + dur = ieee80211_frame_duration(sband->band, 10, bitrate, erp, short_preamble, divisor); /* Data frame duration */ - dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate, + dur += ieee80211_frame_duration(sband->band, frame_len, bitrate, erp, short_preamble, divisor); /* ACK duration */ - dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate, + dur += ieee80211_frame_duration(sband->band, 10, bitrate, erp, short_preamble, divisor); return cpu_to_le16(dur); @@ -247,7 +254,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, struct ieee80211_rate *rate; struct ieee80211_sub_if_data *sdata; bool short_preamble; - int erp, divisor = 1; + int erp, divisor = 1, bitrate; u16 dur; struct ieee80211_supported_band *sband; @@ -265,12 +272,14 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, divisor = ieee80211_vif_get_divisor(vif); } + bitrate = DIV_ROUND_UP(rate->bitrate, divisor); + /* Data frame duration */ - dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate, + dur = ieee80211_frame_duration(sband->band, frame_len, bitrate, erp, short_preamble, divisor); if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { /* ACK duration */ - dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate, + dur += ieee80211_frame_duration(sband->band, 10, bitrate, erp, short_preamble, divisor); } @@ -1090,7 +1099,7 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local, { struct ieee80211_supported_band *sband; struct ieee80211_rate *bitrates; - u32 mandatory_rates; + u32 mandatory_rates, rate_flags; enum ieee80211_rate_flags mandatory_flag; int i; @@ -1098,16 +1107,27 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local, if (WARN_ON(!sband)) return 1; - if (band == IEEE80211_BAND_2GHZ) - mandatory_flag = IEEE80211_RATE_MANDATORY_B; - else + rate_flags = ieee80211_chandef_rate_flags(&local->hw.conf.chandef); + + if (band == IEEE80211_BAND_2GHZ) { + /* only OFDM used for 5/10 MHz channels */ + if ((rate_flags & (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ))) + mandatory_flag = IEEE80211_RATE_MANDATORY_G; + else + mandatory_flag = IEEE80211_RATE_MANDATORY_B; + } else { mandatory_flag = IEEE80211_RATE_MANDATORY_A; + } bitrates = sband->bitrates; mandatory_rates = 0; - for (i = 0; i < sband->n_bitrates; i++) + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & bitrates[i].flags) != rate_flags) + continue; if (bitrates[i].flags & mandatory_flag) mandatory_rates |= BIT(i); + } return mandatory_rates; } @@ -1204,16 +1224,26 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, u8 rates[32]; int num_rates; int ext_rates_len; + int divisor; + u32 rate_flags; sband = local->hw.wiphy->bands[band]; if (WARN_ON_ONCE(!sband)) return 0; + rate_flags = ieee80211_chandef_rate_flags(&local->hw.conf.chandef); + divisor = ieee80211_hw_get_divisor(&local->hw); + num_rates = 0; for (i = 0; i < sband->n_bitrates; i++) { if ((BIT(i) & rate_mask) == 0) continue; /* skip rate */ - rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5); + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + + rates[num_rates++] = + (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, + divisor * 5); } supp_rates_len = min_t(int, num_rates, 8); @@ -1387,10 +1417,13 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, struct ieee80211_supported_band *sband; struct ieee80211_rate *bitrates; size_t num_rates; - u32 supp_rates; - int i, j; + u32 supp_rates, rate_flags; + int i, j, divisor; sband = local->hw.wiphy->bands[band]; + rate_flags = ieee80211_chandef_rate_flags(&local->hw.conf.chandef); + divisor = ieee80211_hw_get_divisor(&local->hw); + if (WARN_ON(!sband)) return 1; @@ -1414,7 +1447,15 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, continue; for (j = 0; j < num_rates; j++) { - if (bitrates[j].bitrate == own_rate) { + int brate; + if ((rate_flags & sband->bitrates[j].flags) + != rate_flags) + continue; + + brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, + divisor); + + if (brate == own_rate) { supp_rates |= BIT(j); if (basic_rates && is_basic) *basic_rates |= BIT(j); @@ -2035,12 +2076,20 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - int rate; + int rate, divisor; u8 i, rates, *pos; u32 basic_rates = sdata->vif.bss_conf.basic_rates; + u32 rate_flags; + divisor = ieee80211_vif_get_divisor(&sdata->vif); + rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); sband = local->hw.wiphy->bands[band]; - rates = sband->n_bitrates; + rates = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + rates++; + } if (rates > 8) rates = 8; @@ -2052,10 +2101,15 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, *pos++ = rates; for (i = 0; i < rates; i++) { u8 basic = 0; + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + if (need_basic && basic_rates & BIT(i)) basic = 0x80; rate = sband->bitrates[i].bitrate; - *pos++ = basic | (u8) (rate / 5); + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, + 5 * divisor); + *pos++ = basic | (u8) rate; } return 0; @@ -2067,12 +2121,22 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - int rate; + int rate, skip, divisor; u8 i, exrates, *pos; u32 basic_rates = sdata->vif.bss_conf.basic_rates; + u32 rate_flags; + + rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); + divisor = ieee80211_vif_get_divisor(&sdata->vif); sband = local->hw.wiphy->bands[band]; - exrates = sband->n_bitrates; + exrates = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + exrates++; + } + if (exrates > 8) exrates -= 8; else @@ -2085,12 +2149,19 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, exrates + 2); *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = exrates; + skip = 0; for (i = 8; i < sband->n_bitrates; i++) { u8 basic = 0; + if ((rate_flags & sband->bitrates[i].flags) + != rate_flags) + continue; + if (skip++ < 8) + continue; if (need_basic && basic_rates & BIT(i)) basic = 0x80; - rate = sband->bitrates[i].bitrate; - *pos++ = basic | (u8) (rate / 5); + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, + 5 * divisor); + *pos++ = basic | (u8) rate; } } return 0; @@ -2174,9 +2245,12 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, ri.flags |= RATE_INFO_FLAGS_SHORT_GI; } else { struct ieee80211_supported_band *sband; + int divisor = ieee80211_hw_get_divisor(&local->hw); + int bitrate; sband = local->hw.wiphy->bands[status->band]; - ri.legacy = sband->bitrates[status->rate_idx].bitrate; + bitrate = sband->bitrates[status->rate_idx].bitrate; + ri.legacy = DIV_ROUND_UP(bitrate, divisor); } rate = cfg80211_calculate_bitrate(&ri); -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html