This patchset adds support for 5 and 10 MHz in nl80211/cfg80211/mac80211
and enables support in ath5k and ath9k, which already support this feature
on the driver side. 5 and 10 MHz wide channels might be useful for:
* long shot links, as the transmissions are more robust
* future support for 802.11y which allows some 5 and 10 MHz channels in
3.6 GHz range
* future support for 802.11p which uses 10 MHz in 5.9 GHz range
* ... and more "special" applications.
This patchset enables 5 and 10 MHz channels only for OFDM, and without
HT/MIMO/aggregation (for now). Support may be added later.
Changes to PATCHv2:
* instead of introducing new bitrate tables, use bitrate flags which mark
the bitrate with support for 5/10 MHz. All bitrates have then to be
calculated manually.
--> this saves a few changes where we only operate on indexes anyway,
but requires some more checking at other parts where it is required
to see if the rate is supported in 5/10 MHz. At least it is not
that ugly and bloaty, and also saves a lot of code in drivers.
* various DIV_ROUND_UP()s have been used instead of manual conversions
--> this was squashed with the "big" bitrate adjustment patch (-1 patch)
* found a bug in the rate mask matching code, which is fixed (+1 patch)
* fixed a few bugs I introuced myself. :)
As always, any comments are appreciated!
Cheers,
Simon
Simon Wunderlich (18):
nl80211/cfg80211: add 5 and 10 MHz defines and wiphy flag
nl80211: add rate flags for 5/10 Mhz channels
mac80211: Fix rate control mask matching call
mac80211: fix various components for the new 5 and 10 MHz widths
mac80211: fix timing for 5 MHz and 10 MHz channels
mac80211: select and adjust bitrates according for channel mode
mac80211: add radiotap flag and handling for 5/10 MHz
cfg80211/mac80211: use reduced txpower for 5 and 10 MHz
mac80211: change IBSS channel state to chandef
nl80211: allow 5 and 10 MHz channels for IBSS
ath9k: always use SIFS times from OFDM for 5/10 MHz
ath9k: use chandef instead of channel_type
ath9k: report 5/10 MHz channels
ath9k: set 5/10 MHz supported channels and fix bitrate
ath9k: announce that ath9k supports 5/10 MHz
ath5k: report 5/10 MHz channels
ath5k: set 5/10 MHz supported channels and fix duration
ath5k: enable support for 5 MHz and 10 MHz channels
drivers/net/wireless/ath/ath5k/ath5k.h | 1 +
drivers/net/wireless/ath/ath5k/base.c | 59 ++++++++--
drivers/net/wireless/ath/ath5k/base.h | 2 +-
drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +-
drivers/net/wireless/ath/ath5k/pcu.c | 2 +
drivers/net/wireless/ath/ath5k/qcu.c | 25 +++-
drivers/net/wireless/ath/ath9k/common.c | 67 +++++++----
drivers/net/wireless/ath/ath9k/common.h | 3 +-
drivers/net/wireless/ath/ath9k/htc_drv_main.c | 5 +-
drivers/net/wireless/ath/ath9k/hw.c | 5 +-
drivers/net/wireless/ath/ath9k/init.c | 29 +++--
drivers/net/wireless/ath/ath9k/main.c | 8 +-
drivers/net/wireless/ath/ath9k/rc.c | 10 +-
drivers/net/wireless/ath/ath9k/recv.c | 11 ++
include/net/cfg80211.h | 55 +++++++++
include/net/ieee80211_radiotap.h | 4 +
include/net/mac80211.h | 5 +-
include/uapi/linux/nl80211.h | 4 +
net/mac80211/cfg.c | 36 +++++-
net/mac80211/ibss.c | 101 +++++++++++-----
net/mac80211/ieee80211_i.h | 45 ++++++-
net/mac80211/iface.c | 2 +-
net/mac80211/main.c | 2 +-
net/mac80211/mesh.c | 4 +-
net/mac80211/mesh_plink.c | 8 +-
net/mac80211/mlme.c | 99 ++++++++++++----
net/mac80211/rate.c | 60 +++++-----
net/mac80211/rc80211_minstrel.c | 29 ++++-
net/mac80211/rc80211_minstrel_ht.c | 11 +-
net/mac80211/rx.c | 28 +++--
net/mac80211/status.c | 17 ++-
net/mac80211/tx.c | 16 ++-
net/mac80211/util.c | 157 +++++++++++++++++++------
net/wireless/chan.c | 57 +++++++--
net/wireless/nl80211.c | 23 +++-
35 files changed, 761 insertions(+), 231 deletions(-)
--
1.7.10.4
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 <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
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
Add defines for 5 and 10 MHz channel width and fix channel
handling functions accordingly.
Also check for and report the WIPHY_FLAG_SUPPORTS_5_10_MHZ
capability.
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
include/net/cfg80211.h | 2 ++
include/uapi/linux/nl80211.h | 4 +++
net/wireless/chan.c | 57 ++++++++++++++++++++++++++++++++++++------
net/wireless/nl80211.c | 21 +++++++++++++---
4 files changed, 72 insertions(+), 12 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 26b5b69..08489cc 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2307,6 +2307,7 @@ struct cfg80211_ops {
* responds to probe-requests in hardware.
* @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
* @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
+ * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
*/
enum wiphy_flags {
WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0),
@@ -2330,6 +2331,7 @@ enum wiphy_flags {
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD = BIT(19),
WIPHY_FLAG_OFFCHAN_TX = BIT(20),
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21),
+ WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22),
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index d1e48b5..3e95e74 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2730,6 +2730,8 @@ enum nl80211_channel_type {
* and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
* @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
* attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel
*/
enum nl80211_chan_width {
NL80211_CHAN_WIDTH_20_NOHT,
@@ -2738,6 +2740,8 @@ enum nl80211_chan_width {
NL80211_CHAN_WIDTH_80,
NL80211_CHAN_WIDTH_80P80,
NL80211_CHAN_WIDTH_160,
+ NL80211_CHAN_WIDTH_5,
+ NL80211_CHAN_WIDTH_10,
};
/**
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index fd556ac..126e31c 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -54,6 +54,8 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
control_freq = chandef->chan->center_freq;
switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT:
if (chandef->center_freq1 != control_freq)
@@ -152,6 +154,12 @@ static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
int width;
switch (c->width) {
+ case NL80211_CHAN_WIDTH_5:
+ width = 5;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ width = 10;
+ break;
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT:
width = 20;
@@ -194,6 +202,16 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
if (c1->width == c2->width)
return NULL;
+ /*
+ * can't be compatible if one of them are 5 or 10 MHz,
+ * but they have not the same width.
+ */
+ if (c1->width == NL80211_CHAN_WIDTH_5 ||
+ c1->width == NL80211_CHAN_WIDTH_10 ||
+ c2->width == NL80211_CHAN_WIDTH_5 ||
+ c2->width == NL80211_CHAN_WIDTH_10)
+ return NULL;
+
if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
c1->width == NL80211_CHAN_WIDTH_20)
return c2;
@@ -264,11 +282,17 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
u32 bandwidth)
{
struct ieee80211_channel *c;
- u32 freq;
+ u32 freq, start_freq, end_freq;
- for (freq = center_freq - bandwidth/2 + 10;
- freq <= center_freq + bandwidth/2 - 10;
- freq += 20) {
+ if (bandwidth <= 20) {
+ start_freq = center_freq;
+ end_freq = center_freq;
+ } else {
+ start_freq = center_freq - bandwidth/2 + 10;
+ end_freq = center_freq + bandwidth/2 - 10;
+ }
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
if (!c)
return -EINVAL;
@@ -310,11 +334,17 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 prohibited_flags)
{
struct ieee80211_channel *c;
- u32 freq;
+ u32 freq, start_freq, end_freq;
- for (freq = center_freq - bandwidth/2 + 10;
- freq <= center_freq + bandwidth/2 - 10;
- freq += 20) {
+ if (bandwidth <= 20) {
+ start_freq = center_freq;
+ end_freq = center_freq;
+ } else {
+ start_freq = center_freq - bandwidth/2 + 10;
+ end_freq = center_freq + bandwidth/2 - 10;
+ }
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
if (!c)
return false;
@@ -349,6 +379,12 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
control_freq = chandef->chan->center_freq;
switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ width = 5;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ width = 10;
+ break;
case NL80211_CHAN_WIDTH_20:
if (!ht_cap->ht_supported)
return false;
@@ -405,6 +441,11 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
if (width > 20)
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
+ /* 5 and 10 MHz are only defined for the OFDM PHY */
+ if (width < 20)
+ prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
+
+
if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
width, prohibited_flags))
return false;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index afa2838..75070da 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1228,6 +1228,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure;
+ if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
+ nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ))
+ goto nla_put_failure;
(*split_start)++;
if (split)
@@ -1771,6 +1774,11 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
IEEE80211_CHAN_DISABLED))
return -EINVAL;
+ if ((chandef->width == NL80211_CHAN_WIDTH_5 ||
+ chandef->width == NL80211_CHAN_WIDTH_10) &&
+ !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ))
+ return -EINVAL;
+
return 0;
}
@@ -6288,11 +6296,16 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
return -EINVAL;
- if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
- return -EINVAL;
- if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
- !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+ switch (ibss.chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)
+ break;
+ default:
return -EINVAL;
+ }
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
--
1.7.10.4
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath5k/base.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 7f702fe..13b0d37 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -1355,6 +1355,16 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate);
rxs->flag |= ath5k_rx_decrypted(ah, skb, rs);
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_5MHZ:
+ rxs->flag |= RX_FLAG_5MHZ;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ rxs->flag |= RX_FLAG_10MHZ;
+ break;
+ default:
+ break;
+ }
if (rxs->rate_idx >= 0 && rs->rs_rate ==
ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short)
--
1.7.10.4
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath9k/init.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index e4fb5e2..392ea80 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -803,6 +803,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
#ifdef CONFIG_PM_SLEEP
--
1.7.10.4
On 2013-05-16 1:00 PM, Simon Wunderlich wrote:
> Some regulations (like germany, but also FCC) express their transmission
> power limit in dBm/MHz or mW/MHz. To cope with that and be on the safe
> side, reduce the maximum power to half (10 MHz) or quarter (5 MHz)
> when operating on these reduced bandwidth channels.
As far as I know, chan->max_power can in some cases contain the hardware
limit instead of the regulatory limit. It's safer to check
chan->max_reg_power to see if a reduction is necessary.
- Felix
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
include/net/cfg80211.h | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 08489cc..c9f5194 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -188,6 +188,8 @@ struct ieee80211_channel {
* when used with 802.11g (on the 2.4 GHz band); filled by the
* core code when registering the wiphy.
* @IEEE80211_RATE_ERP_G: This is an ERP rate in 802.11g mode.
+ * @IEEE80211_RATE_SUPPORTS_5MHZ: Rate can be used in 5 MHz mode
+ * @IEEE80211_RATE_SUPPORTS_10MHZ: Rate can be used in 10 MHz mode
*/
enum ieee80211_rate_flags {
IEEE80211_RATE_SHORT_PREAMBLE = 1<<0,
@@ -195,6 +197,8 @@ enum ieee80211_rate_flags {
IEEE80211_RATE_MANDATORY_B = 1<<2,
IEEE80211_RATE_MANDATORY_G = 1<<3,
IEEE80211_RATE_ERP_G = 1<<4,
+ IEEE80211_RATE_SUPPORTS_5MHZ = 1<<5,
+ IEEE80211_RATE_SUPPORTS_10MHZ = 1<<6,
};
/**
@@ -433,6 +437,30 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
u32 prohibited_flags);
/**
+ * ieee80211_chandef_rate_flags - returns rate flags for a channel
+ *
+ * In some channel types, not all rates may be used - for example CCK
+ * rates may not be used in 5/10 MHz channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: rate flags which apply for this channel
+ */
+static inline enum ieee80211_rate_flags
+ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ return IEEE80211_RATE_SUPPORTS_5MHZ;
+ case NL80211_CHAN_WIDTH_10:
+ return IEEE80211_RATE_SUPPORTS_10MHZ;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/**
* enum survey_info_flags - survey information flags
*
* @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
--
1.7.10.4
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath9k/recv.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 8be2b5d..fe97818 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -865,6 +865,17 @@ static int ath9k_process_rate(struct ath_common *common,
band = hw->conf.chandef.chan->band;
sband = hw->wiphy->bands[band];
+ switch (hw->conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_5:
+ rxs->flag |= RX_FLAG_5MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ rxs->flag |= RX_FLAG_10MHZ;
+ break;
+ default:
+ break;
+ }
+
if (rx_stats->rs_rate & 0x80) {
/* HT rate */
rxs->flag |= RX_FLAG_HT;
--
1.7.10.4
Whether the wiphy supports it or not is already checked, so what is left
is to enable these channel types.
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
net/wireless/nl80211.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 75070da..a5f281a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6297,6 +6297,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
switch (ibss.chandef.width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20_NOHT:
break;
case NL80211_CHAN_WIDTH_20:
--
1.7.10.4
Wireshark already defines radiotap channel flags for 5 and 10 MHz, so
just use them in Linux radiotap too. Furthermore, add rx status flags to
allow drivers to report when they received data on 5 or 10 MHz channels.
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
include/net/ieee80211_radiotap.h | 4 ++++
include/net/mac80211.h | 4 ++++
net/mac80211/rx.c | 21 ++++++++++++---------
3 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index c399963..8b991ff 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -230,6 +230,10 @@ enum ieee80211_radiotap_type {
#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */
#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */
#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */
+#define IEEE80211_CHAN_GSM 0x1000 /* GSM (900 MHz) */
+#define IEEE80211_CHAN_STURBO 0x2000 /* Static Turbo */
+#define IEEE80211_CHAN_HALF 0x4000 /* Half channel (10 MHz wide) */
+#define IEEE80211_CHAN_QUARTER 0x8000 /* Quarter channel (5 MHz wide) */
/* For IEEE80211_RADIOTAP_FLAGS */
#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 0ce4290..4dae129 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -805,6 +805,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* on this subframe
* @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
* is stored in the @ampdu_delimiter_crc field)
+ * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
+ * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@@ -832,6 +834,8 @@ enum mac80211_rx_flags {
RX_FLAG_80MHZ = BIT(23),
RX_FLAG_80P80MHZ = BIT(24),
RX_FLAG_160MHZ = BIT(25),
+ RX_FLAG_10MHZ = BIT(26),
+ RX_FLAG_5MHZ = BIT(27),
};
/**
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4d8f817..d64f284 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -146,6 +146,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
u16 rx_flags = 0;
+ u16 channel_flags = 0;
int mpdulen;
mpdulen = skb->len;
@@ -220,20 +221,22 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_CHANNEL */
put_unaligned_le16(status->freq, pos);
pos += 2;
+ if (status->flag & RX_FLAG_10MHZ)
+ channel_flags |= IEEE80211_CHAN_HALF;
+ else if (status->flag & RX_FLAG_5MHZ)
+ channel_flags |= IEEE80211_CHAN_QUARTER;
+
if (status->band == IEEE80211_BAND_5GHZ)
- put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
- pos);
+ channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
- put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
- pos);
+ channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
- put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ,
- pos);
+ channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
else if (rate)
- put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ,
- pos);
+ channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
else
- put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos);
+ channel_flags |= IEEE80211_CHAN_2GHZ;
+ put_unaligned_le16(channel_flags, pos);
pos += 2;
/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
--
1.7.10.4
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath5k/ath5k.h | 1 +
drivers/net/wireless/ath/ath5k/base.c | 25 ++++++++++++++++++++++---
drivers/net/wireless/ath/ath5k/base.h | 2 +-
drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +-
4 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 2d691b8..74bd54d 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -29,6 +29,7 @@
#include <linux/average.h>
#include <linux/leds.h>
#include <net/mac80211.h>
+#include <net/cfg80211.h>
/* RX/TX descriptor hw structs
* TODO: Driver part should only see sw structs */
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 9be5f11..5af13b3 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -56,6 +56,7 @@
#include <linux/etherdevice.h>
#include <linux/nl80211.h>
+#include <net/cfg80211.h>
#include <net/ieee80211_radiotap.h>
#include <asm/unaligned.h>
@@ -442,11 +443,27 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
* Called with ah->lock.
*/
int
-ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
+ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef)
{
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"channel set, resetting (%u -> %u MHz)\n",
- ah->curchan->center_freq, chan->center_freq);
+ ah->curchan->center_freq, chandef->chan->center_freq);
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ah->ah_bwmode = AR5K_BWMODE_DEFAULT;
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ ah->ah_bwmode = AR5K_BWMODE_5MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ ah->ah_bwmode = AR5K_BWMODE_10MHZ;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
/*
* To switch channels clear any pending DMA operations;
@@ -454,7 +471,7 @@ ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
* hardware at the new frequency, and then re-enable
* the relevant bits of the h/w.
*/
- return ath5k_reset(ah, chan, true);
+ return ath5k_reset(ah, chandef->chan, true);
}
void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
@@ -2474,6 +2491,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
/* SW support for IBSS_RSN is provided by mac80211 */
hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
+
/* both antennas can be configured as RX or TX */
hw->wiphy->available_antennas_tx = 0x3;
hw->wiphy->available_antennas_rx = 0x3;
diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
index 6c94c7f..130075b 100644
--- a/drivers/net/wireless/ath/ath5k/base.h
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -99,7 +99,7 @@ void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable);
void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
struct ieee80211_vif *vif);
-int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
+int ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef);
void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 06f86f4..2129330 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -202,7 +202,7 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
mutex_lock(&ah->lock);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- ret = ath5k_chan_set(ah, conf->chandef.chan);
+ ret = ath5k_chan_set(ah, &conf->chandef);
if (ret < 0)
goto unlock;
}
--
1.7.10.4
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath5k/base.c | 24 ++++++++++++++++--------
drivers/net/wireless/ath/ath5k/pcu.c | 2 ++
drivers/net/wireless/ath/ath5k/qcu.c | 25 ++++++++++++++++++++++++-
3 files changed, 42 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 13b0d37..9be5f11 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -164,28 +164,36 @@ static const struct ieee80211_rate ath5k_rates[] = {
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 60,
.hw_value = ATH5K_RATE_CODE_6M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 90,
.hw_value = ATH5K_RATE_CODE_9M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 120,
.hw_value = ATH5K_RATE_CODE_12M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 180,
.hw_value = ATH5K_RATE_CODE_18M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 240,
.hw_value = ATH5K_RATE_CODE_24M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 360,
.hw_value = ATH5K_RATE_CODE_36M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 480,
.hw_value = ATH5K_RATE_CODE_48M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 540,
.hw_value = ATH5K_RATE_CODE_54M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
};
static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index 1f16b42..c60d36a 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -144,11 +144,13 @@ ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band,
sifs = AR5K_INIT_SIFS_HALF_RATE;
preamble *= 2;
sym_time *= 2;
+ bitrate = DIV_ROUND_UP(bitrate, 2);
break;
case AR5K_BWMODE_5MHZ:
sifs = AR5K_INIT_SIFS_QUARTER_RATE;
preamble *= 4;
sym_time *= 4;
+ bitrate = DIV_ROUND_UP(bitrate, 4);
break;
default:
sifs = AR5K_INIT_SIFS_DEFAULT_BG;
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index 65fe929..0583c69 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -566,9 +566,11 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
{
struct ieee80211_channel *channel = ah->ah_current_channel;
enum ieee80211_band band;
+ struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock;
u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
+ u32 rate_flags, i;
if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX)
return -EINVAL;
@@ -605,7 +607,28 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
else
band = IEEE80211_BAND_2GHZ;
- rate = &ah->sbands[band].bitrates[0];
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_5MHZ:
+ rate_flags = IEEE80211_RATE_SUPPORTS_5MHZ;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ rate_flags = IEEE80211_RATE_SUPPORTS_10MHZ;
+ break;
+ default:
+ rate_flags = 0;
+ break;
+ }
+ sband = &ah->sbands[band];
+ rate = NULL;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+ rate = &sband->bitrates[i];
+ break;
+ }
+ if (WARN_ON(!rate))
+ return -EINVAL;
+
ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false);
/* ack_tx_time includes an SIFS already */
--
1.7.10.4
This is a collection of minor fixes:
* don't allow HT IEs in IBSS for 5/10 MHz
* don't allow HT IEs in Mesh for 5/10 MHz
* consider 5 and 10 MHz channels when downgrading
* don't try HT rates for 5 and 10 MHz channels when selecting rates
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
net/mac80211/ibss.c | 2 ++
net/mac80211/mesh.c | 4 +++-
net/mac80211/mesh_plink.c | 8 +++++++-
net/mac80211/mlme.c | 12 ++++++++++++
net/mac80211/rate.c | 8 +++++++-
5 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 170f9a7..4e1fb81 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -176,6 +176,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
/* add HT capability and information IEs */
if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ chandef.width != NL80211_CHAN_WIDTH_5 &&
+ chandef.width != NL80211_CHAN_WIDTH_10 &&
sband->ht_cap.ht_supported) {
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
sband->ht_cap.cap);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 6952760..5227b73 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -417,7 +417,9 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[band];
if (!sband->ht_cap.ht_supported ||
- sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
return 0;
if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 09bebed..02c05fa 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -154,8 +154,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
u16 ht_opmode;
bool non_ht_sta = false, ht20_sta = false;
- if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+ switch (sdata->vif.bss_conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
return 0;
+ default:
+ break;
+ }
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 29620bf..0eaee23 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -224,6 +224,12 @@ static u32 chandef_downgrade(struct cfg80211_chan_def *c)
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ WARN_ON_ONCE(1);
+ /* keep c->width */
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ break;
}
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
@@ -3809,6 +3815,12 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
*/
ret = ieee80211_vif_use_channel(sdata, &chandef,
IEEE80211_CHANCTX_SHARED);
+
+ /* don't downgrade for 5 and 10 MHz channels, though. */
+ if (chandef.width == NL80211_CHAN_WIDTH_5 ||
+ chandef.width == NL80211_CHAN_WIDTH_10)
+ return ret;
+
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
ifmgd->flags |= chandef_downgrade(&chandef);
ret = ieee80211_vif_use_channel(sdata, &chandef,
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 8b95819..118e47b 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -397,8 +397,14 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
return;
/* if HT BSS, and we handle a data frame, also try HT rates */
- if (chan_width == NL80211_CHAN_WIDTH_20_NOHT)
+ switch (chan_width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
return;
+ default:
+ break;
+ }
alt_rate.idx = 0;
/* keep protection flags */
--
1.7.10.4
The order of parameters was mixed up, introduced in commit
"mac80211: improve the rate control API"
Cc: Felix Fietkau <[email protected]>
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
net/mac80211/rate.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 0d51877..8b95819 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -615,7 +615,7 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
if (rates[i].idx < 0)
break;
- rate_idx_match_mask(&rates[i], sband, mask, chan_width,
+ rate_idx_match_mask(&rates[i], sband, chan_width, mask,
mcs_mask);
}
}
--
1.7.10.4
5/10 MHz channels should always use SIFS times as defined in IEEE
802.11-2012 18.4.4 (OFDM PHY characteristics). This makes it compatible
to ath5k, which does the same.
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath9k/hw.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 7f25da8..89ee731 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1071,7 +1071,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
tx_lat += 11;
- sifstime *= 2;
+ sifstime = 32;
ack_offset = 16;
slottime = 13;
} else if (IS_CHAN_QUARTER_RATE(chan)) {
@@ -1081,7 +1081,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
tx_lat += 22;
- sifstime *= 4;
+ sifstime = 64;
ack_offset = 32;
slottime = 21;
} else {
@@ -1118,7 +1118,6 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
ctstimeout += 48 - sifstime - ah->slottime;
}
-
ath9k_hw_set_sifs_time(ah, sifstime);
ath9k_hw_setslottime(ah, slottime);
ath9k_hw_set_ack_timeout(ah, acktimeout);
--
1.7.10.4
Some regulations (like germany, but also FCC) express their transmission
power limit in dBm/MHz or mW/MHz. To cope with that and be on the safe
side, reduce the maximum power to half (10 MHz) or quarter (5 MHz)
when operating on these reduced bandwidth channels.
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
include/net/cfg80211.h | 25 +++++++++++++++++++++++++
net/mac80211/iface.c | 2 +-
net/mac80211/main.c | 2 +-
net/mac80211/mlme.c | 3 ++-
4 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index c9f5194..45f9b19 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -461,6 +461,31 @@ ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
}
/**
+ * ieee80211_chandef_max_power - maximum transmission power for the chandef
+ *
+ * In some regulations, the transmit power may depend on the configured channel
+ * bandwidth which may be defined as dBm/MHz. This function returns the actual
+ * max_power for non-standard (20 MHz) channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: maximum allowed transmission power in dBm for the chandef
+ */
+static inline int
+ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ return chandef->chan->max_power - 6;
+ case NL80211_CHAN_WIDTH_10:
+ return chandef->chan->max_power - 3;
+ default:
+ break;
+ }
+ return chandef->chan->max_power;
+}
+
+/**
* enum survey_info_flags - survey information flags
*
* @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 60f1ce5..06f1861 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -54,7 +54,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
return false;
}
- power = chanctx_conf->def.chan->max_power;
+ power = ieee80211_chandef_max_power(&chanctx_conf->def);
rcu_read_unlock();
if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 8a7bfc4..cb347d4 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -151,7 +151,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
changed |= IEEE80211_CONF_CHANGE_SMPS;
}
- power = chandef.chan->max_power;
+ power = ieee80211_chandef_max_power(&chandef);
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index f36b909..b10bba3 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -809,7 +809,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*pos++ = WLAN_EID_PWR_CAPABILITY;
*pos++ = 2;
*pos++ = 0; /* min tx power */
- *pos++ = chan->max_power; /* max tx power */
+ /* max tx power */
+ *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
/* 2. supported channels */
/* TODO: get this in reg domain format */
--
1.7.10.4
To enable support for 5/10 MHz, some internal functions must be
converted from using the (old) channel_type to chandef. This is a good
chance to change all remaining occurences.
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath9k/common.c | 67 +++++++++++++++----------
drivers/net/wireless/ath/ath9k/common.h | 3 +-
drivers/net/wireless/ath/ath9k/htc_drv_main.c | 5 +-
drivers/net/wireless/ath/ath9k/init.c | 4 +-
drivers/net/wireless/ath/ath9k/main.c | 8 ++-
drivers/net/wireless/ath/ath9k/rc.c | 4 +-
6 files changed, 51 insertions(+), 40 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index 344fdde..d3063c2 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -49,37 +49,40 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
}
EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
-static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef)
{
u32 chanmode = 0;
- switch (chan->band) {
+ switch (chandef->chan->band) {
case IEEE80211_BAND_2GHZ:
- switch (channel_type) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
chanmode = CHANNEL_G_HT20;
break;
- case NL80211_CHAN_HT40PLUS:
- chanmode = CHANNEL_G_HT40PLUS;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 > chandef->chan->center_freq)
+ chanmode = CHANNEL_G_HT40PLUS;
+ else
+ chanmode = CHANNEL_G_HT40MINUS;
break;
- case NL80211_CHAN_HT40MINUS:
- chanmode = CHANNEL_G_HT40MINUS;
+ default:
break;
}
break;
case IEEE80211_BAND_5GHZ:
- switch (channel_type) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
chanmode = CHANNEL_A_HT20;
break;
- case NL80211_CHAN_HT40PLUS:
- chanmode = CHANNEL_A_HT40PLUS;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 > chandef->chan->center_freq)
+ chanmode = CHANNEL_A_HT40PLUS;
+ else
+ chanmode = CHANNEL_A_HT40MINUS;
break;
- case NL80211_CHAN_HT40MINUS:
- chanmode = CHANNEL_A_HT40MINUS;
+ default:
break;
}
break;
@@ -94,13 +97,12 @@ static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan,
* Update internal channel flags.
*/
void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+ struct cfg80211_chan_def *chandef)
{
- ichan->channel = chan->center_freq;
- ichan->chan = chan;
+ ichan->channel = chandef->chan->center_freq;
+ ichan->chan = chandef->chan;
- if (chan->band == IEEE80211_BAND_2GHZ) {
+ if (chandef->chan->band == IEEE80211_BAND_2GHZ) {
ichan->chanmode = CHANNEL_G;
ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
} else {
@@ -108,8 +110,22 @@ void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
}
- if (channel_type != NL80211_CHAN_NO_HT)
- ichan->chanmode = ath9k_get_extchanmode(chan, channel_type);
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ ichan->channelFlags |= CHANNEL_QUARTER;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ ichan->channelFlags |= CHANNEL_HALF;
+ break;
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ ichan->chanmode = ath9k_get_extchanmode(chandef);
+ break;
+ default:
+ WARN_ON(1);
+ }
}
EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
@@ -125,8 +141,7 @@ struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
chan_idx = curchan->hw_value;
channel = &ah->channels[chan_idx];
- ath9k_cmn_update_ichannel(channel, curchan,
- cfg80211_get_chandef_type(&hw->conf.chandef));
+ ath9k_cmn_update_ichannel(channel, &hw->conf.chandef);
return channel;
}
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
index 207d069..e039bcb 100644
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -44,8 +44,7 @@
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type);
+ struct cfg80211_chan_def *chandef);
struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
struct ath_hw *ah);
int ath9k_cmn_count_streams(unsigned int chainmask, int max);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 0743a47..8c25c7a 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -1194,16 +1194,13 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) {
struct ieee80211_channel *curchan = hw->conf.chandef.chan;
- enum nl80211_channel_type channel_type =
- cfg80211_get_chandef_type(&hw->conf.chandef);
int pos = curchan->hw_value;
ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
curchan->center_freq);
ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
- hw->conf.chandef.chan,
- channel_type);
+ &hw->conf.chandef);
if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
ath_err(common, "Unable to set channel\n");
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 0237b28..5921194 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -684,13 +684,15 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
struct ath_hw *ah = sc->sc_ah;
+ struct cfg80211_chan_def chandef;
int i;
sband = &sc->sbands[band];
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
ah->curchan = &ah->channels[chan->hw_value];
- ath9k_cmn_update_ichannel(ah->curchan, chan, NL80211_CHAN_HT20);
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
+ ath9k_cmn_update_ichannel(ah->curchan, &chandef);
ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
}
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index a18414b..d21875c 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1194,8 +1194,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
struct ieee80211_channel *curchan = hw->conf.chandef.chan;
- enum nl80211_channel_type channel_type =
- cfg80211_get_chandef_type(&conf->chandef);
int pos = curchan->hw_value;
int old_pos = -1;
unsigned long flags;
@@ -1203,8 +1201,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
if (ah->curchan)
old_pos = ah->curchan - &ah->channels[0];
- ath_dbg(common, CONFIG, "Set channel: %d MHz type: %d\n",
- curchan->center_freq, channel_type);
+ ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+ curchan->center_freq, hw->conf.chandef.width);
/* update survey stats for the old channel before switching */
spin_lock_irqsave(&common->cc_lock, flags);
@@ -1219,7 +1217,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
ath9k_hw_getnf(ah, ah->curchan);
ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
- curchan, channel_type);
+ &conf->chandef);
/*
* If the operating channel changes, change the survey in-use flags
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index aa4d368..ed57d57 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -1327,8 +1327,8 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
ath_rc_init(sc, priv_sta);
ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG,
- "Operating HT Bandwidth changed to: %d\n",
- cfg80211_get_chandef_type(&sc->hw->conf.chandef));
+ "Operating Bandwidth changed to: %d\n",
+ &sc->hw->conf.chandef->width);
}
}
--
1.7.10.4
Hey,
just to bump here - this patchset is idling around for quite some time now.
I can integrate Felix suggestions easily if not a maintainer would like to do
that. Any further comments/suggestions?
Thanks,
Simon
On Thu, May 16, 2013 at 01:00:27PM +0200, Simon Wunderlich wrote:
> This patchset adds support for 5 and 10 MHz in nl80211/cfg80211/mac80211
> and enables support in ath5k and ath9k, which already support this feature
> on the driver side. 5 and 10 MHz wide channels might be useful for:
>
> * long shot links, as the transmissions are more robust
> * future support for 802.11y which allows some 5 and 10 MHz channels in
> 3.6 GHz range
> * future support for 802.11p which uses 10 MHz in 5.9 GHz range
> * ... and more "special" applications.
>
> This patchset enables 5 and 10 MHz channels only for OFDM, and without
> HT/MIMO/aggregation (for now). Support may be added later.
>
> Changes to PATCHv2:
> * instead of introducing new bitrate tables, use bitrate flags which mark
> the bitrate with support for 5/10 MHz. All bitrates have then to be
> calculated manually.
> --> this saves a few changes where we only operate on indexes anyway,
> but requires some more checking at other parts where it is required
> to see if the rate is supported in 5/10 MHz. At least it is not
> that ugly and bloaty, and also saves a lot of code in drivers.
> * various DIV_ROUND_UP()s have been used instead of manual conversions
> --> this was squashed with the "big" bitrate adjustment patch (-1 patch)
> * found a bug in the rate mask matching code, which is fixed (+1 patch)
> * fixed a few bugs I introuced myself. :)
>
> As always, any comments are appreciated!
> Cheers,
> Simon
>
> Simon Wunderlich (18):
> nl80211/cfg80211: add 5 and 10 MHz defines and wiphy flag
> nl80211: add rate flags for 5/10 Mhz channels
> mac80211: Fix rate control mask matching call
> mac80211: fix various components for the new 5 and 10 MHz widths
> mac80211: fix timing for 5 MHz and 10 MHz channels
> mac80211: select and adjust bitrates according for channel mode
> mac80211: add radiotap flag and handling for 5/10 MHz
> cfg80211/mac80211: use reduced txpower for 5 and 10 MHz
> mac80211: change IBSS channel state to chandef
> nl80211: allow 5 and 10 MHz channels for IBSS
> ath9k: always use SIFS times from OFDM for 5/10 MHz
> ath9k: use chandef instead of channel_type
> ath9k: report 5/10 MHz channels
> ath9k: set 5/10 MHz supported channels and fix bitrate
> ath9k: announce that ath9k supports 5/10 MHz
> ath5k: report 5/10 MHz channels
> ath5k: set 5/10 MHz supported channels and fix duration
> ath5k: enable support for 5 MHz and 10 MHz channels
>
> drivers/net/wireless/ath/ath5k/ath5k.h | 1 +
> drivers/net/wireless/ath/ath5k/base.c | 59 ++++++++--
> drivers/net/wireless/ath/ath5k/base.h | 2 +-
> drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +-
> drivers/net/wireless/ath/ath5k/pcu.c | 2 +
> drivers/net/wireless/ath/ath5k/qcu.c | 25 +++-
> drivers/net/wireless/ath/ath9k/common.c | 67 +++++++----
> drivers/net/wireless/ath/ath9k/common.h | 3 +-
> drivers/net/wireless/ath/ath9k/htc_drv_main.c | 5 +-
> drivers/net/wireless/ath/ath9k/hw.c | 5 +-
> drivers/net/wireless/ath/ath9k/init.c | 29 +++--
> drivers/net/wireless/ath/ath9k/main.c | 8 +-
> drivers/net/wireless/ath/ath9k/rc.c | 10 +-
> drivers/net/wireless/ath/ath9k/recv.c | 11 ++
> include/net/cfg80211.h | 55 +++++++++
> include/net/ieee80211_radiotap.h | 4 +
> include/net/mac80211.h | 5 +-
> include/uapi/linux/nl80211.h | 4 +
> net/mac80211/cfg.c | 36 +++++-
> net/mac80211/ibss.c | 101 +++++++++++-----
> net/mac80211/ieee80211_i.h | 45 ++++++-
> net/mac80211/iface.c | 2 +-
> net/mac80211/main.c | 2 +-
> net/mac80211/mesh.c | 4 +-
> net/mac80211/mesh_plink.c | 8 +-
> net/mac80211/mlme.c | 99 ++++++++++++----
> net/mac80211/rate.c | 60 +++++-----
> net/mac80211/rc80211_minstrel.c | 29 ++++-
> net/mac80211/rc80211_minstrel_ht.c | 11 +-
> net/mac80211/rx.c | 28 +++--
> net/mac80211/status.c | 17 ++-
> net/mac80211/tx.c | 16 ++-
> net/mac80211/util.c | 157 +++++++++++++++++++------
> net/wireless/chan.c | 57 +++++++--
> net/wireless/nl80211.c | 23 +++-
> 35 files changed, 761 insertions(+), 231 deletions(-)
>
> --
> 1.7.10.4
>
according to IEEE 802.11-2012 section 18, various timings change
when using 5 MHz and 10 MHz. Reflect this by using a "divisor" when
calculating durations.
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
net/mac80211/ieee80211_i.h | 42 +++++++++++++++++++++++++++++++++++-
net/mac80211/rc80211_minstrel.c | 15 ++++++++-----
net/mac80211/rc80211_minstrel_ht.c | 8 ++++---
net/mac80211/tx.c | 10 ++++++---
net/mac80211/util.c | 39 ++++++++++++++++++++++-----------
5 files changed, 89 insertions(+), 25 deletions(-)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 158e6eb..0e8f2d0 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -793,6 +793,45 @@ ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
return band;
}
+static inline int
+ieee80211_vif_get_divisor(struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int divisor = 1;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ if (chanctx_conf) {
+ switch (chanctx_conf->def.width) {
+ case NL80211_CHAN_WIDTH_5:
+ divisor = 4;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ divisor = 2;
+ break;
+ default:
+ divisor = 1;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return divisor;
+}
+
+static inline int
+ieee80211_hw_get_divisor(struct ieee80211_hw *hw)
+{
+ switch (hw->conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_5:
+ return 4;
+ case NL80211_CHAN_WIDTH_10:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
IEEE80211_SDATA_QUEUE_AGG_START = 1,
@@ -1448,7 +1487,8 @@ extern void *mac80211_wiphy_privid; /* for wiphy privid */
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type);
int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
- int rate, int erp, int short_preamble);
+ int rate, int erp, int short_preamble,
+ int divisor);
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
struct ieee80211_hdr *hdr, const u8 *tsc,
gfp_t gfp);
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index ac7ef54..ffabe1d 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -382,14 +382,18 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
static void
calc_rate_durations(enum ieee80211_band band,
struct minstrel_rate *d,
- struct ieee80211_rate *rate)
+ struct ieee80211_rate *rate,
+ struct minstrel_priv *mp)
{
int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
+ int divisor = ieee80211_hw_get_divisor(mp->hw);
d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
- rate->bitrate, erp, 1);
+ DIV_ROUND_UP(rate->bitrate, divisor), erp, 1,
+ divisor);
d->ack_time = ieee80211_frame_duration(band, 10,
- rate->bitrate, erp, 1);
+ DIV_ROUND_UP(rate->bitrate, divisor), erp, 1,
+ divisor);
}
static void
@@ -430,7 +434,8 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
ctl_rate = &sband->bitrates[mi->lowest_rix];
mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,
ctl_rate->bitrate,
- !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1);
+ !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1,
+ ieee80211_hw_get_divisor(mp->hw));
memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
mi->max_prob_rate = 0;
@@ -448,7 +453,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
mr->rix = i;
mr->bitrate = sband->bitrates[i].bitrate / 5;
- calc_rate_durations(sband->band, mr, &sband->bitrates[i]);
+ calc_rate_durations(sband->band, mr, &sband->bitrates[i], mp);
/* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 5b2d301..f05eeff 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -844,7 +844,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
u16 sta_cap = sta->ht_cap.cap;
int n_supported = 0;
- int ack_dur;
+ int ack_dur, divisor;
int stbc;
int i;
@@ -861,8 +861,10 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
mi->sta = sta;
mi->stats_update = jiffies;
- ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1);
- mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1) + ack_dur;
+ divisor = ieee80211_hw_get_divisor(mp->hw);
+ ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, divisor);
+ mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1,
+ divisor) + ack_dur;
mi->overhead_rtscts = mi->overhead + 2 * ack_dur;
mi->avg_ampdu_len = MINSTREL_FRAC(1, 1);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 9972e07..e1f18a8 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -40,7 +40,7 @@ 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;
+ int rate, mrate, erp, dur, i, divisor;
struct ieee80211_rate *txrate;
struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband;
@@ -153,6 +153,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
rate = mrate;
}
+ 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)
@@ -162,7 +164,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
* (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
* to closest integer */
dur = ieee80211_frame_duration(sband->band, 10, rate, erp,
- tx->sdata->vif.bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble,
+ divisor);
if (next_frag_len) {
/* Frame is fragmented: duration increases with time needed to
@@ -171,7 +174,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
/* next fragment */
dur += ieee80211_frame_duration(sband->band, next_frag_len,
txrate->bitrate, erp,
- tx->sdata->vif.bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble,
+ divisor);
}
return cpu_to_le16(dur);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 3f87fa4..5496764 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -107,7 +107,8 @@ void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
}
int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
- int rate, int erp, int short_preamble)
+ int rate, int erp, int short_preamble,
+ int divisor)
{
int dur;
@@ -118,6 +119,9 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
*
* rate is in 100 kbps, so divident is multiplied by 10 in the
* DIV_ROUND_UP() operations.
+ *
+ * divisor may be 4 for 5 MHz channels or 2 for 1 MHz channels, and
+ * is assumed to be 1 otherwise.
*/
if (band == IEEE80211_BAND_5GHZ || erp) {
@@ -130,15 +134,21 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
* TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
*
* T_SYM = 4 usec
- * 802.11a - 17.5.2: aSIFSTime = 16 usec
+ * 802.11a - 18.5.2: aSIFSTime = 16 usec
* 802.11g - 19.8.4: aSIFSTime = 10 usec +
* signal ext = 6 usec
*/
dur = 16; /* SIFS + signal ext */
- dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
- dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
+ 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;
} else {
/*
* 802.11b or 802.11g with 802.11b compatibility:
@@ -168,7 +178,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
{
struct ieee80211_sub_if_data *sdata;
u16 dur;
- int erp;
+ int erp, divisor = 1;
bool short_preamble = false;
erp = 0;
@@ -177,10 +187,11 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
+ divisor = ieee80211_vif_get_divisor(vif);
}
dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
- short_preamble);
+ short_preamble, divisor);
return cpu_to_le16(dur);
}
@@ -194,7 +205,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
struct ieee80211_rate *rate;
struct ieee80211_sub_if_data *sdata;
bool short_preamble;
- int erp;
+ int erp, divisor = 1;
u16 dur;
struct ieee80211_supported_band *sband;
@@ -210,17 +221,18 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
+ divisor = ieee80211_vif_get_divisor(vif);
}
/* CTS duration */
dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate,
- erp, short_preamble);
+ erp, short_preamble, divisor);
/* Data frame duration */
dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
- erp, short_preamble);
+ erp, short_preamble, divisor);
/* ACK duration */
dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
- erp, short_preamble);
+ erp, short_preamble, divisor);
return cpu_to_le16(dur);
}
@@ -235,7 +247,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
struct ieee80211_rate *rate;
struct ieee80211_sub_if_data *sdata;
bool short_preamble;
- int erp;
+ int erp, divisor = 1;
u16 dur;
struct ieee80211_supported_band *sband;
@@ -250,15 +262,16 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
+ divisor = ieee80211_vif_get_divisor(vif);
}
/* Data frame duration */
dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
- erp, short_preamble);
+ erp, short_preamble, divisor);
if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
/* ACK duration */
dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
- erp, short_preamble);
+ erp, short_preamble, divisor);
}
return cpu_to_le16(dur);
--
1.7.10.4
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath9k/init.c | 24 ++++++++++++++++--------
drivers/net/wireless/ath/ath9k/rc.c | 6 ++++++
2 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 5921194..e4fb5e2 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -145,14 +145,22 @@ static struct ieee80211_rate ath9k_legacy_rates[] = {
RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE),
RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE),
RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE),
- RATE(60, 0x0b, 0),
- RATE(90, 0x0f, 0),
- RATE(120, 0x0a, 0),
- RATE(180, 0x0e, 0),
- RATE(240, 0x09, 0),
- RATE(360, 0x0d, 0),
- RATE(480, 0x08, 0),
- RATE(540, 0x0c, 0),
+ RATE(60, 0x0b, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(90, 0x0f, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(120, 0x0a, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(180, 0x0e, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(240, 0x09, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(360, 0x0d, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(480, 0x08, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(540, 0x0c, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
};
#ifdef CONFIG_MAC80211_LEDS
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index ed57d57..ff7a9e4 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -1284,9 +1284,15 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_rate_priv *ath_rc_priv = priv_sta;
int i, j = 0;
+ int divisor = ieee80211_hw_get_divisor(sc->hw->conf.hw);
+ u32 rate_flags = ieee80211_chandef_rate_flags(&sc->hw->conf.chandef);
for (i = 0; i < sband->n_bitrates; i++) {
if (sta->supp_rates[sband->band] & BIT(i)) {
+ if ((rate_flags & sband->bitrates[i].flags)
+ != rate_flags)
+ continue;
+
ath_rc_priv->neg_rates.rs_rates[j]
= (sband->bitrates[i].bitrate * 2) / 10;
j++;
--
1.7.10.4
This should make some parts cleaner and is also required for handling
5/10 MHz properly.
Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
net/mac80211/ibss.c | 22 +++++++++++-----------
net/mac80211/ieee80211_i.h | 3 +--
2 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index da95735..fc84ee3 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -82,7 +82,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- cfg80211_chandef_create(&chandef, chan, ifibss->channel_type);
+ chandef = ifibss->chandef;
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
chandef.width = NL80211_CHAN_WIDTH_20;
chandef.center_freq1 = chan->center_freq;
@@ -546,7 +546,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
set_sta_flag(sta, WLAN_STA_WME);
if (sta && elems->ht_operation && elems->ht_cap_elem &&
- sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
+ sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
+ sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
/* we both use HT */
struct ieee80211_ht_cap htcap_ie;
struct cfg80211_chan_def chandef;
@@ -561,8 +563,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* fall back to HT20 if we don't use or use
* the other extension channel
*/
- if (cfg80211_get_chandef_type(&chandef) !=
- sdata->u.ibss.channel_type)
+ if (chandef.center_freq1 !=
+ sdata->u.ibss.chandef.center_freq1)
htcap_ie.cap_info &=
cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
@@ -601,7 +603,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
/* different channel */
if (sdata->u.ibss.fixed_channel &&
- sdata->u.ibss.channel != cbss->channel)
+ sdata->u.ibss.chandef.chan != cbss->channel)
goto put_bss;
/* different SSID */
@@ -789,7 +791,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
sdata->drop_unencrypted = 0;
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
- ifibss->channel, ifibss->basic_rates,
+ ifibss->chandef.chan, ifibss->basic_rates,
capability, 0, true);
}
@@ -821,7 +823,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
if (ifibss->fixed_bssid)
bssid = ifibss->bssid;
if (ifibss->fixed_channel)
- chan = ifibss->channel;
+ chan = ifibss->chandef.chan;
if (!is_zero_ether_addr(ifibss->bssid))
bssid = ifibss->bssid;
cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
@@ -1102,9 +1104,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
- sdata->u.ibss.channel = params->chandef.chan;
- sdata->u.ibss.channel_type =
- cfg80211_get_chandef_type(¶ms->chandef);
+ sdata->u.ibss.chandef = params->chandef;
sdata->u.ibss.fixed_channel = params->channel_fixed;
if (params->ie) {
@@ -1167,7 +1167,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
- cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->channel,
+ cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
WLAN_CAPABILITY_PRIVACY,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 0e8f2d0..46c4d12 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -505,8 +505,7 @@ struct ieee80211_if_ibss {
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len, ie_len;
u8 *ie;
- struct ieee80211_channel *channel;
- enum nl80211_channel_type channel_type;
+ struct cfg80211_chan_def chandef;
unsigned long ibss_join_req;
/* probe response/beacon for IBSS */
--
1.7.10.4
On Thu, 2013-05-16 at 13:00 +0200, Simon Wunderlich wrote:
> Wireshark
doesn't really matter all that much, but I guess the radiotap standard
also defines it.
> + RX_FLAG_10MHZ = BIT(26),
> + RX_FLAG_5MHZ = BIT(27),
Does that make sense? We know what kind of channel we're on? Though it
might be easier for the driver, so it may make sense I guess.
johannes
On Tue, Jun 18, 2013 at 04:21:23PM +0200, Johannes Berg wrote:
>
> > +static inline int
> > +ieee80211_hw_get_divisor(struct ieee80211_hw *hw)
> > +{
> > + switch (hw->conf.chandef.width) {
> > + case NL80211_CHAN_WIDTH_5:
> > + return 4;
> > + case NL80211_CHAN_WIDTH_10:
> > + return 2;
> > + default:
> > + return 1;
> > + }
> > +}
>
> Using this function will break with drivers that use channel contexts,
> you really shouldn't do that.
Hmm ... yeah, you are right. I've used this a lot in the following patches, argh.
Will find another solution.
>
> Also, you're disabling HT so why bother changing minstrel_ht?
Mhm, yeah that is probably not neccesary. I'll remove that, however we remember
it once HT support should be added. ;)
Thanks,
Simon
On Thu, 2013-05-16 at 13:00 +0200, Simon Wunderlich wrote:
> This should make some parts cleaner and is also required for handling
> 5/10 MHz properly.
Applied.
johannes
On Thu, 2013-05-16 at 13:00 +0200, Simon Wunderlich wrote:
> 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.
"suggests", heh ...
> @@ -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++)
please don't do that.
> 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);
> }
some refactoring might be useful ... having != at the start of a line is
a really good sign for that ;-)
johannes
On Tue, Jun 18, 2013 at 04:27:06PM +0200, Johannes Berg wrote:
> On Thu, 2013-05-16 at 13:00 +0200, Simon Wunderlich wrote:
>
> > Wireshark
>
> doesn't really matter all that much, but I guess the radiotap standard
> also defines it.
>
Hmm, if this [1] is the official standard, then no, it is not defined. Don't
know when this was added in wireshark, and it was only added implicitly in
the code (no header files etc). But since it was there, I figured it would
be a good idea to re-use it (unaware that there was some "standard").
How shall we proceed, propose that on some radiotap mailing list officially?
>
> > + RX_FLAG_10MHZ = BIT(26),
> > + RX_FLAG_5MHZ = BIT(27),
>
> Does that make sense? We know what kind of channel we're on? Though it
> might be easier for the driver, so it may make sense I guess.
Yeah, it's a little easier for the driver, and I thought it would be
cleaner to get this reported through a flag.
Cheers,
Simon
[1] http://www.radiotap.org/defined-fields/Channel
> +static inline int
> +ieee80211_hw_get_divisor(struct ieee80211_hw *hw)
> +{
> + switch (hw->conf.chandef.width) {
> + case NL80211_CHAN_WIDTH_5:
> + return 4;
> + case NL80211_CHAN_WIDTH_10:
> + return 2;
> + default:
> + return 1;
> + }
> +}
Using this function will break with drivers that use channel contexts,
you really shouldn't do that.
Also, you're disabling HT so why bother changing minstrel_ht?
johannes
On Tue, 2013-06-18 at 17:50 +0200, Simon Wunderlich wrote:
> Hmm, if this [1] is the official standard, then no, it is not defined. Don't
> know when this was added in wireshark, and it was only added implicitly in
> the code (no header files etc). But since it was there, I figured it would
> be a good idea to re-use it (unaware that there was some "standard").
>
> How shall we proceed, propose that on some radiotap mailing list officially?
Yes, see http://www.radiotap.org/Standardisation
> > > + RX_FLAG_10MHZ = BIT(26),
> > > + RX_FLAG_5MHZ = BIT(27),
> >
> > Does that make sense? We know what kind of channel we're on? Though it
> > might be easier for the driver, so it may make sense I guess.
>
> Yeah, it's a little easier for the driver, and I thought it would be
> cleaner to get this reported through a flag.
Yeah I tend to agree.
johannes
On Thu, 2013-05-16 at 13:00 +0200, Simon Wunderlich wrote:
> - cfg80211_chandef_create(&chandef, chan, ifibss->channel_type);
> + chandef = ifibss->chandef;
This part is obviously wrong - it ignores the given channel. Please post
a fix as soon as you can, with the code as it is now we never join an
existing IBSS correctly but always use the channel the user gave.
johannes