2013-05-15 14:19:33

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 00/18] Add support for 5 and 10 MHz channels

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 PATCHv1:
* the actual datarates are handled, not the corresponding 20MHz rates
as before. This should make it compatible to freebsd, although I was
informed that some other implementors might still use the corresponding
20MHz rates. Anyway, from looking at the standard using the actual rates
should be the right thing to do.
* txpower is decremented according to channel bandwidth (the regulation I
checked all define limits in dBm/MHz)
* radiotap support for 5/10 MHz channel flag was added
* some fixes for ath5k/ath9k were included to make them interoperable
(having the same SIFS improved performance quite well,
300kbps -> 6600 kbps for 5 MHz channels :) )

As always, any comments are appreciated!
Cheers,
Simon

Simon Wunderlich (18):
nl80211/cfg80211: add 5 and 10 MHz defines and wiphy flag
nl80211: add half/quarter channel bitrate tables to supported band
struct
mac80211: fix various components for the new 5 and 10 MHz widths
mac80211: fix timing for 5 MHz and 10 MHz channels
mac80211: round rates to the next multiple of 500kbps
mac80211: choose bitrate table according to bandwidth
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: add and use 5/10 MHz bitrate tables
ath9k: report 5/10 MHz channels
ath9k: announce that ath9k supports 5/10 MHz
ath5k: add and use 5/10 MHz bitrate tables
ath5k: report 5/10 MHz channels
ath5k: enable support for 5 MHz and 10 MHz channels

drivers/net/wireless/ath/ath5k/ath5k.h | 11 +-
drivers/net/wireless/ath/ath5k/base.c | 133 +++++++++++++++++++++---
drivers/net/wireless/ath/ath5k/base.h | 2 +-
drivers/net/wireless/ath/ath5k/debug.c | 23 ++++-
drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +-
drivers/net/wireless/ath/ath5k/pcu.c | 30 +++++-
drivers/net/wireless/ath/ath5k/qcu.c | 23 ++++-
drivers/net/wireless/ath/ath9k/beacon.c | 13 ++-
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 | 68 +++++++++---
drivers/net/wireless/ath/ath9k/main.c | 8 +-
drivers/net/wireless/ath/ath9k/rc.c | 13 ++-
drivers/net/wireless/ath/ath9k/recv.c | 24 ++++-
drivers/net/wireless/ath/ath9k/xmit.c | 14 ++-
include/net/cfg80211.h | 66 ++++++++++++
include/net/ieee80211_radiotap.h | 4 +
include/net/mac80211.h | 40 ++++++-
include/uapi/linux/nl80211.h | 4 +
net/mac80211/cfg.c | 50 +++++++--
net/mac80211/ibss.c | 58 +++++++----
net/mac80211/ieee80211_i.h | 47 ++++++++-
net/mac80211/iface.c | 7 +-
net/mac80211/main.c | 7 +-
net/mac80211/mesh.c | 4 +-
net/mac80211/mesh_plink.c | 18 +++-
net/mac80211/mlme.c | 84 ++++++++++-----
net/mac80211/rate.c | 102 ++++++++++++------
net/mac80211/rc80211_minstrel.c | 59 +++++++----
net/mac80211/rc80211_minstrel_ht.c | 26 +++--
net/mac80211/rc80211_pid.h | 2 +
net/mac80211/rc80211_pid_algo.c | 68 +++++++++---
net/mac80211/rx.c | 52 +++++++---
net/mac80211/status.c | 18 +++-
net/mac80211/tx.c | 43 ++++++--
net/mac80211/util.c | 138 +++++++++++++++++++------
net/wireless/chan.c | 57 ++++++++--
net/wireless/nl80211.c | 23 ++++-
net/wireless/util.c | 58 +++++++++++
41 files changed, 1179 insertions(+), 300 deletions(-)

--
1.7.10.4



2013-05-17 15:39:27

by Adrian Chadd

[permalink] [raw]
Subject: Re: [PATCHv2 00/18] Add support for 5 and 10 MHz channels

On 17 May 2013 02:58, Simon Wunderlich
<[email protected]> wrote:

> Maybe I can do the same for the Linux IEs too, just for reference.
> We basically enable it only if 802.11a or g is supported (802.11b is not
> supported), and announce OFDM only rates. The rates are divided by two or
> four (for 10 and 5 MHz), and rounded up. That's pretty much the only
> thing which is changed to 20 MHz.

Cool. I'll do it over the weekend. I'm currently at BSDCan 2013 fixing
and talking about wifi stuff. :-)

> BTW, how is HT handled for narrow channels in FreeBSD? Do you just send
> HT IEs as they are in 20 MHz? Also, do you disable secondary channels?
> (I would probably do that, using two adjacent 5 MHz channels should be
> replaceable by one 10 MHz channel ...)

I haven't enabled 11n half/quarter rate channels yet. Yeah, I intend
to leave the MCS IE's alone, as the MCS rates are a bitmap. I won't
disable HT40 but it wouldn't make sense to configure it.

(But who knows, people do odd crap.)



Adrian

2013-05-15 14:19:39

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 06/18] mac80211: choose bitrate table according to bandwidth

For situations where a reduced bandwidth (5 MHz, 10 MHz) is used, the
bitrates used internally (for building/interpreting IEs, rate control,
etc) must change accordingly. Therefore add bitrates_half and
bitrates_quarter bitrate table according to the currently selected
mode. Only drivers which support these modes have to fill these
tables.

This patch touches a lot of components, but is usually only selecting
the right bitrate table, returning an error if this is not possible or
falls back to the standard bitrate table.

Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
include/net/mac80211.h | 36 +++++++++++--
net/mac80211/cfg.c | 50 +++++++++++++++---
net/mac80211/ibss.c | 28 ++++++++---
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/iface.c | 5 ++
net/mac80211/main.c | 5 +-
net/mac80211/mesh_plink.c | 10 +++-
net/mac80211/mlme.c | 55 ++++++++++++++------
net/mac80211/rate.c | 94 +++++++++++++++++++++++-----------
net/mac80211/rc80211_minstrel.c | 47 +++++++++++------
net/mac80211/rc80211_minstrel_ht.c | 18 +++++--
net/mac80211/rc80211_pid.h | 2 +
net/mac80211/rc80211_pid_algo.c | 68 +++++++++++++++++++------
net/mac80211/rx.c | 29 +++++++++--
net/mac80211/status.c | 15 ++++--
net/mac80211/tx.c | 33 +++++++++---
net/mac80211/util.c | 98 +++++++++++++++++++++++++++++-------
17 files changed, 464 insertions(+), 131 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 04c2d46..3e52e22 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1648,27 +1648,54 @@ static inline struct ieee80211_rate *
ieee80211_get_tx_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c)
{
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+
if (WARN_ON_ONCE(c->control.rates[0].idx < 0))
return NULL;
- return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx];
+
+ if (WARN_ON(ieee80211_get_bitrates(hw->wiphy->bands[c->band],
+ hw->conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return NULL;
+
+ return &bitrates[c->control.rates[0].idx];
}

static inline struct ieee80211_rate *
ieee80211_get_rts_cts_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c)
{
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+
if (c->control.rts_cts_rate_idx < 0)
return NULL;
- return &hw->wiphy->bands[c->band]->bitrates[c->control.rts_cts_rate_idx];
+
+ if (WARN_ON(ieee80211_get_bitrates(hw->wiphy->bands[c->band],
+ hw->conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return NULL;
+
+ return &bitrates[c->control.rts_cts_rate_idx];
}

static inline struct ieee80211_rate *
ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c, int idx)
{
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+
if (c->control.rates[idx + 1].idx < 0)
return NULL;
- return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[idx + 1].idx];
+
+ if (WARN_ON(ieee80211_get_bitrates(hw->wiphy->bands[c->band],
+ hw->conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return NULL;
+
+ return &bitrates[c->control.rates[idx + 1].idx];
}

/**
@@ -4235,11 +4262,12 @@ bool rate_control_send_low(struct ieee80211_sta *sta,

static inline s8
rate_lowest_index(struct ieee80211_supported_band *sband,
+ int n_bitrates,
struct ieee80211_sta *sta)
{
int i;

- for (i = 0; i < sband->n_bitrates; i++)
+ for (i = 0; i < 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..4657f1b 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -392,9 +392,19 @@ void sta_set_rate_info_tx(struct sta_info *sta,
rinfo->nss = ieee80211_rate_get_vht_nss(rate);
} else {
struct ieee80211_supported_band *sband;
+ struct ieee80211_hw *hw;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+
sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)];
- rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+ hw = &sta->sdata->local->hw;
+ if (ieee80211_get_bitrates(sband, hw->conf.chandef.width,
+ &bitrates, &n_bitrates)) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+ rinfo->legacy = bitrates[rate->idx].bitrate;
}
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
@@ -419,11 +429,21 @@ 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;
+ struct ieee80211_rate *bitrates;
+ struct ieee80211_hw *hw;
+ int n_bitrates;

sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)];
+ hw = &sta->sdata->local->hw;
+ if (ieee80211_get_bitrates(sband, hw->conf.chandef.width,
+ &bitrates, &n_bitrates)) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
rinfo->legacy =
- sband->bitrates[sta->last_rx_rate_idx].bitrate;
+ bitrates[sta->last_rx_rate_idx].bitrate;
}

if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
@@ -1264,12 +1284,20 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta->listen_interval = params->listen_interval;

if (params->supported_rates) {
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
rates = 0;

+ if (ieee80211_get_bitrates(sband, local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates)) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
for (i = 0; i < params->supported_rates_len; i++) {
int rate = (params->supported_rates[i] & 0x7f) * 5;
- for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
+ for (j = 0; j < n_bitrates; j++) {
+ if (bitrates[j].bitrate == rate)
rates |= BIT(j);
}
}
@@ -1935,11 +1963,21 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
int i, j;
u32 rates = 0;
struct ieee80211_supported_band *sband = wiphy->bands[band];
+ struct ieee80211_hw *hw;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+
+ hw = &sdata->local->hw;
+ if (ieee80211_get_bitrates(sband, hw->conf.chandef.width,
+ &bitrates, &n_bitrates)) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }

for (i = 0; i < params->basic_rates_len; i++) {
int rate = (params->basic_rates[i] & 0x7f) * 5;
- for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
+ for (j = 0; j < n_bitrates; j++) {
+ if (bitrates[j].bitrate == rate)
rates |= BIT(j);
}
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 6a96663..3b55a43 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -53,6 +53,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct cfg80211_chan_def chandef;
struct beacon_data *presp;
int frame_len;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;

lockdep_assert_held(&ifibss->mtx);

@@ -100,6 +102,11 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,

sband = local->hw.wiphy->bands[chan->band];

+ if (WARN_ON(ieee80211_get_bitrates(sband, chandef.width,
+ &bitrates, &n_bitrates)))
+ return;
+
+
/* Build IBSS probe response */
frame_len = sizeof(struct ieee80211_hdr_3addr) +
12 /* struct ieee80211_mgmt.u.beacon */ +
@@ -134,11 +141,11 @@ 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);
+ rates = min_t(int, 8, n_bitrates);
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = rates;
for (i = 0; i < rates; i++) {
- int rate = sband->bitrates[i].bitrate;
+ int rate = bitrates[i].bitrate;
u8 basic = 0;
if (basic_rates & BIT(i))
basic = 0x80;
@@ -157,11 +164,11 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
*pos++ = 0;
*pos++ = 0;

- if (sband->n_bitrates > 8) {
+ if (n_bitrates > 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;
+ for (i = 8; i < n_bitrates; i++) {
+ int rate = bitrates[i].bitrate;
u8 basic = 0;
if (basic_rates & BIT(i))
basic = 0x80;
@@ -240,7 +247,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, n_bitrates, supp_rates);

ifibss->state = IEEE80211_IBSS_MLME_JOINED;
mod_timer(&ifibss->timer,
@@ -263,6 +270,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
int i, j;
u16 beacon_int = cbss->beacon_interval;
const struct cfg80211_bss_ies *ies;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
u64 tsf;

lockdep_assert_held(&sdata->u.ibss.mtx);
@@ -271,6 +280,9 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
beacon_int = 10;

sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
+ if (WARN_ON(ieee80211_get_bitrates(sband, sdata->u.ibss.chandef.width,
+ &bitrates, &n_bitrates)))
+ return;

basic_rates = 0;

@@ -278,8 +290,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
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 + 4) / 5 == rate) {
+ for (j = 0; j < n_bitrates; j++) {
+ if ((bitrates[j].bitrate + 4) / 5 == rate) {
if (is_basic)
basic_rates |= BIT(j);
break;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 0e8f2d0..3258039 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -741,6 +741,8 @@ struct ieee80211_sub_if_data {

/* bitmap of allowed (non-MCS) rate indexes for rate control */
u32 rc_rateidx_mask[IEEE80211_NUM_BANDS];
+ u32 rc_rateidx_mask_half[IEEE80211_NUM_BANDS];
+ u32 rc_rateidx_mask_quarter[IEEE80211_NUM_BANDS];

bool rc_has_mcs_mask[IEEE80211_NUM_BANDS];
u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN];
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 60f1ce5..28a38a7 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1615,6 +1615,11 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sband = local->hw.wiphy->bands[i];
sdata->rc_rateidx_mask[i] =
sband ? (1 << sband->n_bitrates) - 1 : 0;
+ sdata->rc_rateidx_mask_half[i] =
+ sband ? (1 << sband->n_bitrates_half) - 1 : 0;
+ sdata->rc_rateidx_mask_quarter[i] =
+ sband ? (1 << sband->n_bitrates_quarter) - 1 : 0;
+
if (sband)
memcpy(sdata->rc_rateidx_mcs_mask[i],
sband->ht_cap.mcs.rx_mask,
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 8a7bfc4..f6b6b57 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -761,8 +761,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)

channels += sband->n_channels;

- if (max_bitrates < sband->n_bitrates)
- max_bitrates = sband->n_bitrates;
+ max_bitrates = max(max_bitrates, sband->n_bitrates);
+ max_bitrates = max(max_bitrates, sband->n_bitrates_quarter);
+ max_bitrates = max(max_bitrates, sband->n_bitrates_half);
supp_ht = supp_ht || sband->ht_cap.ht_supported;
supp_vht = supp_vht || sband->vht_cap.vht_supported;

diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 02c05fa..2f07985 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -92,6 +92,8 @@ static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
struct ieee80211_local *local = sdata->local;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
struct sta_info *sta;
u32 erp_rates = 0, changed = 0;
int i;
@@ -106,8 +108,12 @@ static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
goto out;

- for (i = 0; i < sband->n_bitrates; i++)
- if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)
+ if (ieee80211_get_bitrates(sband, local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates))
+ goto out;
+
+ for (i = 0; i < n_bitrates; i++)
+ if (bitrates[i].flags & IEEE80211_RATE_ERP_G)
erp_rates |= BIT(i);

if (!erp_rates)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 03c1b73..837a580 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -513,8 +513,8 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
/* frame sending functions */

static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
- struct ieee80211_supported_band *sband,
- u32 *rates)
+ struct ieee80211_rate *bitrates,
+ int n_bitrates, u32 *rates)
{
int i, j, count;
*rates = 0;
@@ -522,8 +522,8 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
for (i = 0; i < supp_rates_len; i++) {
int rate = supp_rates[i] & 0x7F;

- for (j = 0; j < sband->n_bitrates; j++)
- if ((sband->bitrates[j].bitrate + 4) / 5 == rate) {
+ for (j = 0; j < n_bitrates; j++)
+ if ((bitrates[j].bitrate + 4) / 5 == rate) {
*rates |= BIT(j);
count++;
break;
@@ -656,6 +656,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
struct ieee80211_supported_band *sband;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
u32 rates = 0;

lockdep_assert_held(&ifmgd->mtx);
@@ -667,8 +669,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
return;
}
chan = chanctx_conf->def.chan;
- rcu_read_unlock();
sband = local->hw.wiphy->bands[chan->band];
+ if (WARN_ON(ieee80211_get_bitrates(sband, chanctx_conf->def.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+ rcu_read_unlock();

if (assoc_data->supp_rates_len) {
/*
@@ -679,7 +686,8 @@ 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);
+ bitrates, n_bitrates,
+ &rates);
} else {
/*
* In case AP not provide any supported rates information
@@ -687,7 +695,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
* all rates that we support.
*/
rates = ~0;
- rates_len = sband->n_bitrates;
+ rates_len = n_bitrates;
}

skb = alloc_skb(local->hw.extra_tx_headroom +
@@ -762,9 +770,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*pos++ = supp_rates_len;

count = 0;
- for (i = 0; i < sband->n_bitrates; i++) {
+ for (i = 0; i < n_bitrates; i++) {
if (BIT(i) & rates) {
- int rate = sband->bitrates[i].bitrate;
+ int rate = bitrates[i].bitrate;
*pos++ = (u8) ((rate + 4) / 5);
if (++count == 8)
break;
@@ -776,9 +784,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = rates_len - count;

- for (i++; i < sband->n_bitrates; i++) {
+ for (i++; i < n_bitrates; i++) {
if (BIT(i) & rates) {
- int rate = sband->bitrates[i].bitrate;
+ int rate = bitrates[i].bitrate;
*pos++ = (u8) ((rate + 4) / 5);
}
}
@@ -2439,7 +2447,8 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
return RX_MGMT_CFG80211_DISASSOC;
}

-static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
+static void ieee80211_get_rates(struct ieee80211_rate *bitrates,
+ int n_bitrates,
u8 *supp_rates, unsigned int supp_rates_len,
u32 *rates, u32 *basic_rates,
bool *have_higher_than_11mbit,
@@ -2466,8 +2475,8 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
(supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
continue;

- for (j = 0; j < sband->n_bitrates; j++) {
- if ((sband->bitrates[j].bitrate + 4) / 5 == rate) {
+ for (j = 0; j < n_bitrates; j++) {
+ if ((bitrates[j].bitrate + 4) / 5 == rate) {
*rates |= BIT(j);
if (is_basic)
*basic_rates |= BIT(j);
@@ -3860,6 +3869,9 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
int min_rate = INT_MAX, min_rate_index = -1;
struct ieee80211_supported_band *sband;
const struct cfg80211_bss_ies *ies;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;

sband = local->hw.wiphy->bands[cbss->channel->band];

@@ -3868,8 +3880,21 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
sta_info_free(local, new_sta);
return err;
}
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return err;
+ }
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ chanctx_conf->def.width,
+ &bitrates, &n_bitrates))) {
+ rcu_read_unlock();
+ return err;
+ }
+ rcu_read_unlock();

- ieee80211_get_rates(sband, bss->supp_rates,
+ ieee80211_get_rates(bitrates, n_bitrates, bss->supp_rates,
bss->supp_rates_len,
&rates, &basic_rates,
&have_higher_than_11mbit,
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 1ca7ded..99a3fbd 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -211,7 +211,8 @@ static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)
}

static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
- struct ieee80211_supported_band *sband)
+ struct ieee80211_rate *bitrates,
+ int n_bitrates)
{
u8 i;

@@ -222,7 +223,7 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
if (basic_rates & (1 << *idx))
return; /* selected rate is a basic rate */

- for (i = *idx + 1; i <= sband->n_bitrates; i++) {
+ for (i = *idx + 1; i <= n_bitrates; i++) {
if (basic_rates & (1 << i)) {
*idx = i;
return;
@@ -234,12 +235,14 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,

static inline s8
rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,
+ struct ieee80211_rate *bitrates,
+ int n_bitrates,
struct ieee80211_sta *sta)
{
int i;

- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *srate = &sband->bitrates[i];
+ for (i = 0; i < n_bitrates; i++) {
+ struct ieee80211_rate *srate = &bitrates[i];
if ((srate->bitrate == 10) || (srate->bitrate == 20) ||
(srate->bitrate == 55) || (srate->bitrate == 110))
continue;
@@ -254,15 +257,19 @@ rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,

static void __rate_control_send_low(struct ieee80211_hw *hw,
struct ieee80211_supported_band *sband,
+ struct ieee80211_rate *bitrates,
+ int n_bitrates,
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);
+ info->control.rates[0].idx =
+ rate_lowest_index(sband, n_bitrates, sta);
else
info->control.rates[0].idx =
- rate_lowest_non_cck_index(sband, sta);
+ rate_lowest_non_cck_index(sband, bitrates, n_bitrates,
+ sta);

info->control.rates[0].count =
(info->flags & IEEE80211_TX_CTL_NO_ACK) ?
@@ -278,10 +285,18 @@ bool rate_control_send_low(struct ieee80211_sta *sta,
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
struct ieee80211_supported_band *sband = txrc->sband;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
int mcast_rate;

+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ txrc->hw->conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return false;
+
if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
- __rate_control_send_low(txrc->hw, sband, sta, info);
+ __rate_control_send_low(txrc->hw, sband, bitrates, n_bitrates,
+ sta, info);

if (!sta && txrc->bss) {
mcast_rate = txrc->bss_conf->mcast_rate[sband->band];
@@ -292,7 +307,7 @@ bool rate_control_send_low(struct ieee80211_sta *sta,

rc_send_low_broadcast(&info->control.rates[0].idx,
txrc->bss_conf->basic_rates,
- sband);
+ bitrates, n_bitrates);
}
return true;
}
@@ -366,7 +381,8 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,


static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
- struct ieee80211_supported_band *sband,
+ struct ieee80211_rate *bitrates,
+ int n_bitrates,
enum nl80211_chan_width chan_width,
u32 mask,
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
@@ -387,13 +403,13 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
alt_rate.count = rate->count;
if (rate_idx_match_legacy_mask(&alt_rate,
- sband->n_bitrates, mask)) {
+ n_bitrates, mask)) {
*rate = alt_rate;
return;
}
} else {
/* handle legacy rates */
- if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask))
+ if (rate_idx_match_legacy_mask(rate, n_bitrates, mask))
return;

/* if HT BSS, and we handle a data frame, also try HT rates */
@@ -435,7 +451,8 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
}

static void rate_fixup_ratelist(struct ieee80211_vif *vif,
- struct ieee80211_supported_band *sband,
+ struct ieee80211_rate *bitrates,
+ int n_bitrates,
struct ieee80211_tx_info *info,
struct ieee80211_tx_rate *rates,
int max_rates)
@@ -456,18 +473,18 @@ static void rate_fixup_ratelist(struct ieee80211_vif *vif,
u32 basic_rates = vif->bss_conf.basic_rates;
s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0;

- rate = &sband->bitrates[rates[0].idx];
+ rate = &bitrates[rates[0].idx];

- for (i = 0; i < sband->n_bitrates; i++) {
+ for (i = 0; i < n_bitrates; i++) {
/* must be a basic rate */
if (!(basic_rates & BIT(i)))
continue;
/* must not be faster than the data rate */
- if (sband->bitrates[i].bitrate > rate->bitrate)
+ if (bitrates[i].bitrate > rate->bitrate)
continue;
/* maximum */
- if (sband->bitrates[baserate].bitrate <
- sband->bitrates[i].bitrate)
+ if (bitrates[baserate].bitrate <
+ bitrates[i].bitrate)
baserate = i;
}

@@ -515,12 +532,12 @@ static void rate_fixup_ratelist(struct ieee80211_vif *vif,
}

/* RC is busted */
- if (WARN_ON_ONCE(rates[i].idx >= sband->n_bitrates)) {
+ if (WARN_ON_ONCE(rates[i].idx >= n_bitrates)) {
rates[i].idx = -1;
continue;
}

- rate = &sband->bitrates[rates[i].idx];
+ rate = &bitrates[rates[i].idx];

/* set up short preamble */
if (info->control.short_preamble &&
@@ -576,7 +593,8 @@ static void rate_control_fill_sta_table(struct ieee80211_sta *sta,

static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta,
- struct ieee80211_supported_band *sband,
+ struct ieee80211_rate *bitrates,
+ int n_bitrates,
struct ieee80211_tx_info *info,
struct ieee80211_tx_rate *rates,
int max_rates)
@@ -592,9 +610,20 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
* default mask (allow all rates) is used to save some processing for
* the common case.
*/
- mask = sdata->rc_rateidx_mask[info->band];
+ chan_width = sdata->vif.bss_conf.chandef.width;
+ switch (chan_width) {
+ case NL80211_CHAN_WIDTH_5:
+ mask = sdata->rc_rateidx_mask_quarter[info->band];
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ mask = sdata->rc_rateidx_mask_half[info->band];
+ break;
+ default:
+ mask = sdata->rc_rateidx_mask[info->band];
+ break;
+ }
has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
- if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
+ if (mask == (1 << n_bitrates) - 1 && !has_mcs_mask)
return;

if (has_mcs_mask)
@@ -615,14 +644,13 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
* included in the configured mask and change the rate indexes
* if needed.
*/
- chan_width = sdata->vif.bss_conf.chandef.width;
for (i = 0; i < max_rates; i++) {
/* Skip invalid rates */
if (rates[i].idx < 0)
break;

- rate_idx_match_mask(&rates[i], sband, mask, chan_width,
- mcs_mask);
+ rate_idx_match_mask(&rates[i], bitrates, n_bitrates, mask,
+ chan_width, mcs_mask);
}
}

@@ -636,6 +664,8 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;

rate_control_fill_sta_table(sta, info, dest, max_rates);

@@ -645,14 +675,22 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
sdata = vif_to_sdata(vif);
sband = sdata->local->hw.wiphy->bands[info->band];

+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ sdata->local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return;
+
if (ieee80211_is_data(hdr->frame_control))
- rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates);
+ rate_control_apply_mask(sdata, sta, bitrates, n_bitrates, info,
+ dest, max_rates);

if (dest[0].idx < 0)
- __rate_control_send_low(&sdata->local->hw, sband, sta, info);
+ __rate_control_send_low(&sdata->local->hw, sband, bitrates,
+ n_bitrates, sta, info);

if (sta)
- rate_fixup_ratelist(vif, sband, info, dest, max_rates);
+ rate_fixup_ratelist(vif, bitrates, n_bitrates, info, dest,
+ max_rates);
}
EXPORT_SYMBOL(ieee80211_get_tx_rates);

diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 6c89c29..00024c8 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -427,10 +427,19 @@ 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 */
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ mp->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }

mi->sta = sta;
- mi->lowest_rix = rate_lowest_index(sband, sta);
- ctl_rate = &sband->bitrates[mi->lowest_rix];
+ mi->lowest_rix = rate_lowest_index(sband, n_bitrates, sta);
+ ctl_rate = &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,
@@ -439,7 +448,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
mi->max_prob_rate = 0;

- for (i = 0; i < sband->n_bitrates; i++) {
+ for (i = 0; i < n_bitrates; i++) {
struct minstrel_rate *mr = &mi->r[n];
unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
unsigned int tx_time_single;
@@ -451,8 +460,8 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
memset(mr, 0, sizeof(*mr));

mr->rix = i;
- mr->bitrate = (sband->bitrates[i].bitrate + 4) / 5;
- calc_rate_durations(sband->band, mr, &sband->bitrates[i], mp);
+ mr->bitrate = (bitrates[i].bitrate + 4) / 5;
+ calc_rate_durations(sband->band, mr, &bitrates[i], mp);

/* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */
@@ -481,11 +490,11 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
} while ((tx_time < mp->segment_size) &&
(++mr->retry_count < mp->max_retry));
mr->adjusted_retry_count = mr->retry_count;
- if (!(sband->bitrates[i].flags & IEEE80211_RATE_ERP_G))
+ if (!(bitrates[i].flags & IEEE80211_RATE_ERP_G))
mr->retry_count_cts = mr->retry_count;
}

- for (i = n; i < sband->n_bitrates; i++) {
+ for (i = n; i < n_bitrates; i++) {
struct minstrel_rate *mr = &mi->r[i];
mr->rix = -1;
}
@@ -513,8 +522,12 @@ minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)

for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
sband = hw->wiphy->bands[i];
- if (sband && sband->n_bitrates > max_rates)
- max_rates = sband->n_bitrates;
+ if (!sband)
+ continue;
+
+ max_rates = max(sband->n_bitrates, max_rates);
+ max_rates = max(sband->n_bitrates_half, max_rates);
+ max_rates = max(sband->n_bitrates_quarter, max_rates);
}

mi->r = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp);
@@ -548,22 +561,28 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
static void
minstrel_init_cck_rates(struct minstrel_priv *mp)
{
- static const int bitrates[4] = { 10, 20, 55, 110 };
+ static const int cck_bitrates[4] = { 10, 20, 55, 110 };
struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
int i, j;

sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
if (!sband)
return;

- for (i = 0, j = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *rate = &sband->bitrates[i];
+ if (ieee80211_get_bitrates(sband, mp->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))
+ return;
+
+ for (i = 0, j = 0; i < n_bitrates; i++) {
+ struct ieee80211_rate *rate = &bitrates[i];

if (rate->flags & IEEE80211_RATE_ERP_G)
continue;

- for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
- if (rate->bitrate != bitrates[j])
+ for (j = 0; j < ARRAY_SIZE(cck_bitrates); j++) {
+ if (rate->bitrate != cck_bitrates[j])
continue;

mp->cck_rates[j] = i;
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index f05eeff..21688ac 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -815,19 +815,25 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta)
{
- int i;
+ struct ieee80211_rate *bitrates;
+ int i, n_bitrates;
+

if (sband->band != IEEE80211_BAND_2GHZ)
return;

mi->cck_supported = 0;
mi->cck_supported_short = 0;
+ if (ieee80211_get_bitrates(sband, mp->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))
+ return;
+
for (i = 0; i < 4; i++) {
if (!rate_supported(sta, sband->band, mp->cck_rates[i]))
continue;

mi->cck_supported |= BIT(i);
- if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE)
+ if (bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE)
mi->cck_supported_short |= BIT(i);
}

@@ -963,8 +969,12 @@ minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)

for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
sband = hw->wiphy->bands[i];
- if (sband && sband->n_bitrates > max_rates)
- max_rates = sband->n_bitrates;
+ if (!sband)
+ continue;
+
+ max_rates = max(sband->n_bitrates, max_rates);
+ max_rates = max(sband->n_bitrates_half, max_rates);
+ max_rates = max(sband->n_bitrates_quarter, max_rates);
}

msp = kzalloc(sizeof(*msp), gfp);
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h
index 19111c7..dc48981 100644
--- a/net/mac80211/rc80211_pid.h
+++ b/net/mac80211/rc80211_pid.h
@@ -269,6 +269,8 @@ struct rc_pid_info {
/* Index of the last used rate. */
int oldrate;

+ struct ieee80211_hw *hw;
+
#ifdef CONFIG_MAC80211_DEBUGFS
/* Debugfs entries created for the parameters above. */
struct rc_pid_debugfs_entries dentries;
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index 502d3ec..cb9ee86 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -72,13 +72,14 @@
static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta,
struct rc_pid_sta_info *spinfo, int adj,
- struct rc_pid_rateinfo *rinfo)
+ struct rc_pid_rateinfo *rinfo,
+ struct ieee80211_rate *bitrates,
+ int n_bitrates)
{
- int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
+ int cur_sorted, new_sorted, probe, tmp, band;
int cur = spinfo->txrate_idx;

band = sband->band;
- n_bitrates = sband->n_bitrates;

/* Map passed arguments to sorted values. */
cur_sorted = rinfo[cur].rev_index;
@@ -121,7 +122,7 @@ static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband,
#ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_rate_change(&spinfo->events,
spinfo->txrate_idx,
- sband->bitrates[spinfo->txrate_idx].bitrate);
+ bitrates[spinfo->txrate_idx].bitrate);
#endif
}

@@ -155,6 +156,15 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
u32 err_der;
int adj, i, j, tmp;
unsigned long period;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ pinfo->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }

/* In case nothing happened during the previous control interval, turn
* the sharpening factor on. */
@@ -186,7 +196,7 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
rinfo[j].diff = rinfo[i].diff + tmp;
pinfo->oldrate = spinfo->txrate_idx;
}
- rate_control_pid_normalize(pinfo, sband->n_bitrates);
+ rate_control_pid_normalize(pinfo, n_bitrates);

/* Compute the proportional, integral and derivative errors. */
err_prop = (pinfo->target - pf) << RC_PID_ARITH_SHIFT;
@@ -213,7 +223,8 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,

/* Change rate. */
if (adj)
- rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo);
+ rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo,
+ bitrates, n_bitrates);
}

static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband,
@@ -265,7 +276,16 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct rc_pid_sta_info *spinfo = priv_sta;
- int rateidx;
+ struct ieee80211_rate *bitrates;
+ int rateidx, n_bitrates;
+
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ txrc->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+

if (txrc->rts)
info->control.rates[0].count =
@@ -280,14 +300,14 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,

rateidx = spinfo->txrate_idx;

- if (rateidx >= sband->n_bitrates)
- rateidx = sband->n_bitrates - 1;
+ if (rateidx >= n_bitrates)
+ rateidx = n_bitrates - 1;

info->control.rates[0].idx = rateidx;

#ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_tx_rate(&spinfo->events,
- rateidx, sband->bitrates[rateidx].bitrate);
+ rateidx, bitrates[rateidx].bitrate);
#endif
}

@@ -298,6 +318,8 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct rc_pid_sta_info *spinfo = priv_sta;
struct rc_pid_info *pinfo = priv;
struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
int i, j, tmp;
bool s;

@@ -309,7 +331,15 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
/* Sort the rates. This is optimized for the most common case (i.e.
* almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
* mapping too. */
- for (i = 0; i < sband->n_bitrates; i++) {
+
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ pinfo->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
+ for (i = 0; i < n_bitrates; i++) {
rinfo[i].index = i;
rinfo[i].rev_index = i;
if (RC_PID_FAST_START)
@@ -320,8 +350,8 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
for (i = 1; i < sband->n_bitrates; i++) {
s = false;
for (j = 0; j < sband->n_bitrates - i; j++)
- if (unlikely(sband->bitrates[rinfo[j].index].bitrate >
- sband->bitrates[rinfo[j + 1].index].bitrate)) {
+ if (unlikely(bitrates[rinfo[j].index].bitrate >
+ bitrates[rinfo[j + 1].index].bitrate)) {
tmp = rinfo[j].index;
rinfo[j].index = rinfo[j + 1].index;
rinfo[j + 1].index = tmp;
@@ -333,7 +363,7 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
break;
}

- spinfo->txrate_idx = rate_lowest_index(sband, sta);
+ spinfo->txrate_idx = rate_lowest_index(sband, n_bitrates, sta);
}

static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
@@ -351,10 +381,16 @@ static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
if (!pinfo)
return NULL;

+ pinfo->hw = hw;
+
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
sband = hw->wiphy->bands[i];
- if (sband && sband->n_bitrates > max_rates)
- max_rates = sband->n_bitrates;
+ if (!sband)
+ continue;
+
+ max_rates = max(sband->n_bitrates, max_rates);
+ max_rates = max(sband->n_bitrates_half, max_rates);
+ max_rates = max(sband->n_bitrates_quarter, max_rates);
}

rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 06f71c6..b0834a9 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2832,13 +2832,23 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
struct ieee80211_rate *rate = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_rx_status *status;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+ enum nl80211_chan_width width;

status = IEEE80211_SKB_RXCB((rx->skb));

sband = rx->local->hw.wiphy->bands[status->band];
if (!(status->flag & RX_FLAG_HT) &&
- !(status->flag & RX_FLAG_VHT))
- rate = &sband->bitrates[status->rate_idx];
+ !(status->flag & RX_FLAG_VHT)) {
+ width = rx->sdata->local->hw.conf.chandef.width;
+ if (WARN_ON(ieee80211_get_bitrates(sband, width,
+ &bitrates,
+ &n_bitrates)))
+ bitrates = sband->bitrates;
+
+ rate = &bitrates[status->rate_idx];
+ }

ieee80211_rx_cooked_monitor(rx, rate);
break;
@@ -3293,9 +3303,20 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
status->rate_idx, status->vht_nss))
goto drop;
} else {
- if (WARN_ON(status->rate_idx >= sband->n_bitrates))
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
+ enum nl80211_chan_width width;
+
+ width = local->hw.conf.chandef.width;
+
+ if (WARN_ON(ieee80211_get_bitrates(sband, width,
+ &bitrates,
+ &n_bitrates)))
goto drop;
- rate = &sband->bitrates[status->rate_idx];
+
+ if (WARN_ON(status->rate_idx >= n_bitrates))
+ goto drop;
+ rate = &bitrates[status->rate_idx];
}
}

diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index f62e4f6..41805ed 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -253,7 +253,8 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
}

static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
- *sband, struct sk_buff *skb,
+ *sband, struct ieee80211_hw *hw,
+ struct sk_buff *skb,
int retry_count, int rtap_len)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -280,10 +281,18 @@ 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)) {
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
int rate;

+ if (ieee80211_get_bitrates(sband, hw->conf.chandef.width,
+ &bitrates, &n_bitrates)) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
- rate = sband->bitrates[info->status.rates[0].idx].bitrate;
+ rate = bitrates[info->status.rates[0].idx].bitrate;
*pos = (rate + 4) / 5;
/* padding for tx flags */
pos += 2;
@@ -627,7 +636,7 @@ 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(sband, hw, 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..e8ef0bd 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -46,6 +46,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
struct ieee80211_supported_band *sband;
struct ieee80211_hdr *hdr;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;

/* assume HW handles this */
if (tx->rate.flags & IEEE80211_TX_RC_MCS)
@@ -56,7 +58,12 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
return 0;

sband = local->hw.wiphy->bands[info->band];
- txrate = &sband->bitrates[tx->rate.idx];
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return 0;
+
+ txrate = &bitrates[tx->rate.idx];

erp = txrate->flags & IEEE80211_RATE_ERP_G;

@@ -115,9 +122,9 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
*/
rate = -1;
/* use lowest available if everything fails */
- mrate = sband->bitrates[0].bitrate;
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *r = &sband->bitrates[i];
+ mrate = bitrates[0].bitrate;
+ for (i = 0; i < n_bitrates; i++) {
+ struct ieee80211_rate *r = &bitrates[i];

if (r->bitrate > txrate->bitrate)
break;
@@ -624,6 +631,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
u32 len;
struct ieee80211_tx_rate_control txrc;
struct ieee80211_sta_rates *ratetbl = NULL;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
bool assoc = false;

memset(&txrc, 0, sizeof(txrc));
@@ -633,6 +642,11 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
len = min_t(u32, tx->skb->len + FCS_LEN,
tx->local->hw.wiphy->frag_threshold);

+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ tx->local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return TX_DROP;
+
/* set up the tx rate control struct we give the RC algo */
txrc.hw = &tx->local->hw;
txrc.sband = sband;
@@ -640,7 +654,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
txrc.skb = tx->skb;
txrc.reported_rate.idx = -1;
txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[info->band];
- if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1)
+ if (txrc.rate_idx_mask == (1 << n_bitrates) - 1)
txrc.max_rate_idx = -1;
else
txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1;
@@ -2340,6 +2354,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
enum ieee80211_band band;
struct ieee80211_tx_rate_control txrc;
struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;

rcu_read_lock();

@@ -2431,6 +2447,11 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,

band = chanctx_conf->def.chan->band;

+ if (WARN_ON(ieee80211_get_bitrates(local->hw.wiphy->bands[band],
+ chanctx_conf->def.width,
+ &bitrates, &n_bitrates)))
+ goto out;
+
info = IEEE80211_SKB_CB(skb);

info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@ -2444,7 +2465,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
txrc.skb = skb;
txrc.reported_rate.idx = -1;
txrc.rate_idx_mask = sdata->rc_rateidx_mask[band];
- if (txrc.rate_idx_mask == (1 << txrc.sband->n_bitrates) - 1)
+ if (txrc.rate_idx_mask == (1 << n_bitrates) - 1)
txrc.max_rate_idx = -1;
else
txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 9d04989..617364d 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -141,14 +141,21 @@ 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:
@@ -204,6 +211,8 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rate *rate;
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
bool short_preamble;
int erp, divisor = 1;
u16 dur;
@@ -213,7 +222,13 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,

short_preamble = false;

- rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
+ if (WARN_ON(ieee80211_get_bitrates(sband, hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
+ rate = &bitrates[frame_txctl->control.rts_cts_rate_idx];

erp = 0;
if (vif) {
@@ -248,6 +263,8 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
struct ieee80211_sub_if_data *sdata;
bool short_preamble;
int erp, divisor = 1;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
u16 dur;
struct ieee80211_supported_band *sband;

@@ -255,7 +272,13 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,

short_preamble = false;

- rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
+ if (WARN_ON(ieee80211_get_bitrates(sband, hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
+ rate = &bitrates[frame_txctl->control.rts_cts_rate_idx];
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
@@ -1092,20 +1115,26 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
struct ieee80211_rate *bitrates;
u32 mandatory_rates;
enum ieee80211_rate_flags mandatory_flag;
- int i;
+ int i, n_bitrates;

sband = local->hw.wiphy->bands[band];
if (WARN_ON(!sband))
return 1;

+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
if (band == IEEE80211_BAND_2GHZ)
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 < n_bitrates; i++)
if (bitrates[i].flags & mandatory_flag)
mandatory_rates |= BIT(i);
return mandatory_rates;
@@ -1198,6 +1227,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
u8 channel)
{
struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
u8 *pos = buffer, *end = buffer + buffer_len;
size_t offset = 0, noffset;
int supp_rates_len, i;
@@ -1209,12 +1240,17 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
if (WARN_ON_ONCE(!sband))
return 0;

+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return 0;
+
num_rates = 0;
- for (i = 0; i < sband->n_bitrates; i++) {
+ for (i = 0; i < n_bitrates; i++) {
if ((BIT(i) & rate_mask) == 0)
continue; /* skip rate */
rates[num_rates++] = (u8)
- ((sband->bitrates[i].bitrate + 4) / 5);
+ ((bitrates[i].bitrate + 4) / 5);
}

supp_rates_len = min_t(int, num_rates, 8);
@@ -1395,8 +1431,11 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
if (WARN_ON(!sband))
return 1;

- bitrates = sband->bitrates;
- num_rates = sband->n_bitrates;
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ local->hw.conf.chandef.width,
+ &bitrates, &num_rates)))
+ return 1;
+
supp_rates = 0;
for (i = 0; i < elems->supp_rates_len +
elems->ext_supp_rates_len; i++) {
@@ -2036,12 +2075,18 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
- int rate;
- u8 i, rates, *pos;
+ struct ieee80211_rate *bitrates;
+ int rate, rates;
+ u8 i, *pos;
u32 basic_rates = sdata->vif.bss_conf.basic_rates;

sband = local->hw.wiphy->bands[band];
- rates = sband->n_bitrates;
+
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ sdata->local->hw.conf.chandef.width,
+ &bitrates, &rates)))
+ return -EINVAL;
+
if (rates > 8)
rates = 8;

@@ -2055,7 +2100,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
u8 basic = 0;
if (need_basic && basic_rates & BIT(i))
basic = 0x80;
- rate = sband->bitrates[i].bitrate;
+ rate = bitrates[i].bitrate;
*pos++ = basic | (u8) ((rate + 4) / 5);
}

@@ -2068,11 +2113,17 @@ 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;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates, rate;
u8 i, exrates, *pos;
u32 basic_rates = sdata->vif.bss_conf.basic_rates;

sband = local->hw.wiphy->bands[band];
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ sdata->local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return -EINVAL;
+
exrates = sband->n_bitrates;
if (exrates > 8)
exrates -= 8;
@@ -2086,11 +2137,11 @@ 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;
- for (i = 8; i < sband->n_bitrates; i++) {
+ for (i = 8; i < n_bitrates; i++) {
u8 basic = 0;
if (need_basic && basic_rates & BIT(i))
basic = 0x80;
- rate = sband->bitrates[i].bitrate;
+ rate = bitrates[i].bitrate;
*pos++ = basic | (u8) ((rate + 4) / 5);
}
}
@@ -2175,9 +2226,18 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
} else {
struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;

sband = local->hw.wiphy->bands[status->band];
- ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ local->hw.conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
+ ri.legacy = bitrates[status->rate_idx].bitrate;
}

rate = cfg80211_calculate_bitrate(&ri);
--
1.7.10.4


2013-05-15 14:19:37

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 16/18] ath5k: add and use 5/10 MHz bitrate tables

When a reduced bandwidth mode is enabled, the according bitrate tables
must be used instead of the standard tables. This patch adds these
bitrate tables and selects the appropriate table.

Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath5k/ath5k.h | 10 +++-
drivers/net/wireless/ath/ath5k/base.c | 98 ++++++++++++++++++++++++++++----
drivers/net/wireless/ath/ath5k/debug.c | 23 ++++++--
drivers/net/wireless/ath/ath5k/pcu.c | 30 ++++++++--
drivers/net/wireless/ath/ath5k/qcu.c | 23 +++++++-
5 files changed, 160 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 2d691b8..1dee0c4 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -486,7 +486,9 @@ enum ath5k_bw_mode {
AR5K_BWMODE_DEFAULT = 0,
AR5K_BWMODE_5MHZ = 1,
AR5K_BWMODE_10MHZ = 2,
- AR5K_BWMODE_40MHZ = 3
+ AR5K_BWMODE_40MHZ = 3,
+ AR5K_BWMODE_MAX
+
};


@@ -1264,8 +1266,10 @@ struct ath5k_hw {
struct ieee80211_hw *hw; /* IEEE 802.11 common */
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
struct ieee80211_channel channels[ATH_CHAN_MAX];
- struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
- s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
+ struct ieee80211_rate
+ rates[IEEE80211_NUM_BANDS][AR5K_BWMODE_MAX][AR5K_MAX_RATES];
+ s8
+ rate_idx[IEEE80211_NUM_BANDS][AR5K_BWMODE_MAX][AR5K_MAX_RATES];
enum nl80211_iftype opmode;

#ifdef CONFIG_ATH5K_DEBUG
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 7f702fe..a4e0b4e 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -330,18 +330,50 @@ ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
return count;
}

+static inline void
+ath5k_get_bitrates(int bwmode,
+ struct ieee80211_supported_band *b,
+ struct ieee80211_rate **bitrates,
+ int *n_bitrates)
+{
+ switch (bwmode) {
+ case AR5K_BWMODE_40MHZ:
+ /* 40 MHz not supported in mac80211, fall through to default */
+ case AR5K_BWMODE_DEFAULT:
+ *bitrates = b->bitrates;
+ *n_bitrates = b->n_bitrates;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ *bitrates = b->bitrates_quarter;
+ *n_bitrates = b->n_bitrates_quarter;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ *bitrates = b->bitrates_half;
+ *n_bitrates = b->n_bitrates_half;
+ break;
+ default:
+ BUG_ON(1);
+ }
+}
+
static void
ath5k_setup_rate_idx(struct ath5k_hw *ah, struct ieee80211_supported_band *b)
{
- u8 i;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates, bwmode;
+ u8 i, val_s;

- for (i = 0; i < AR5K_MAX_RATES; i++)
- ah->rate_idx[b->band][i] = -1;
+ for (bwmode = 0; bwmode < AR5K_BWMODE_MAX; bwmode++) {
+ for (i = 0; i < AR5K_MAX_RATES; i++)
+ ah->rate_idx[b->band][bwmode][i] = -1;
+ ath5k_get_bitrates(bwmode, b, &bitrates, &n_bitrates);

- for (i = 0; i < b->n_bitrates; i++) {
- ah->rate_idx[b->band][b->bitrates[i].hw_value] = i;
- if (b->bitrates[i].hw_value_short)
- ah->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
+ for (i = 0; i < n_bitrates; i++) {
+ ah->rate_idx[b->band][bwmode][bitrates[i].hw_value] = i;
+ val_s = bitrates[i].hw_value_short;
+ if (val_s)
+ ah->rate_idx[b->band][bwmode][val_s] = i;
+ }
}
}

@@ -359,7 +391,12 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
/* 2GHz band */
sband = &ah->sbands[IEEE80211_BAND_2GHZ];
sband->band = IEEE80211_BAND_2GHZ;
- sband->bitrates = &ah->rates[IEEE80211_BAND_2GHZ][0];
+ sband->bitrates =
+ &ah->rates[IEEE80211_BAND_2GHZ][AR5K_BWMODE_DEFAULT][0];
+ sband->bitrates_half =
+ &ah->rates[IEEE80211_BAND_2GHZ][AR5K_BWMODE_10MHZ][0];
+ sband->bitrates_quarter =
+ &ah->rates[IEEE80211_BAND_2GHZ][AR5K_BWMODE_5MHZ][0];

if (test_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode)) {
/* G mode */
@@ -367,6 +404,19 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
sizeof(struct ieee80211_rate) * 12);
sband->n_bitrates = 12;

+ memcpy(sband->bitrates_half, &ath5k_rates[4],
+ sizeof(struct ieee80211_rate) * 8);
+ sband->n_bitrates_half = 8;
+ for (i = 0; i < sband->n_bitrates_half; i++)
+ sband->bitrates_half[i].bitrate /= 2;
+
+ memcpy(sband->bitrates_quarter, &ath5k_rates[4],
+ sizeof(struct ieee80211_rate) * 8);
+ sband->n_bitrates_quarter = 8;
+ for (i = 0; i < sband->n_bitrates_quarter; i++)
+ sband->bitrates_quarter[i].bitrate /= 4;
+
+
sband->channels = ah->channels;
sband->n_channels = ath5k_setup_channels(ah, sband->channels,
AR5K_MODE_11G, max_c);
@@ -407,12 +457,29 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
if (test_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode)) {
sband = &ah->sbands[IEEE80211_BAND_5GHZ];
sband->band = IEEE80211_BAND_5GHZ;
- sband->bitrates = &ah->rates[IEEE80211_BAND_5GHZ][0];
+ sband->bitrates =
+ &ah->rates[IEEE80211_BAND_5GHZ][AR5K_BWMODE_DEFAULT][0];
+ sband->bitrates_half =
+ &ah->rates[IEEE80211_BAND_5GHZ][AR5K_BWMODE_10MHZ][0];
+ sband->bitrates_quarter =
+ &ah->rates[IEEE80211_BAND_5GHZ][AR5K_BWMODE_5MHZ][0];

memcpy(sband->bitrates, &ath5k_rates[4],
sizeof(struct ieee80211_rate) * 8);
sband->n_bitrates = 8;

+ memcpy(sband->bitrates_half, &ath5k_rates[4],
+ sizeof(struct ieee80211_rate) * 8);
+ sband->n_bitrates_half = 8;
+ for (i = 0; i < sband->n_bitrates_half; i++)
+ sband->bitrates_half[i].bitrate /= 2;
+
+ memcpy(sband->bitrates_quarter, &ath5k_rates[4],
+ sizeof(struct ieee80211_rate) * 8);
+ sband->n_bitrates_quarter = 8;
+ for (i = 0; i < sband->n_bitrates_quarter; i++)
+ sband->bitrates_quarter[i].bitrate /= 4;
+
sband->channels = &ah->channels[count_c];
sband->n_channels = ath5k_setup_channels(ah, sband->channels,
AR5K_MODE_11A, max_c);
@@ -556,7 +623,7 @@ ath5k_hw_to_driver_rix(struct ath5k_hw *ah, int hw_rix)
"hw_rix out of bounds: %x\n", hw_rix))
return 0;

- rix = ah->rate_idx[ah->curchan->band][hw_rix];
+ rix = ah->rate_idx[ah->curchan->band][ah->ah_bwmode][hw_rix];
if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
rix = 0;

@@ -1320,6 +1387,8 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
struct ath5k_rx_status *rs)
{
struct ieee80211_rx_status *rxs;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;

ath5k_remove_padding(skb);

@@ -1356,8 +1425,10 @@ 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);

+ ath5k_get_bitrates(ah->ah_bwmode, &ah->sbands[ah->curchan->band],
+ &bitrates, &n_bitrates);
if (rxs->rate_idx >= 0 && rs->rs_rate ==
- ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short)
+ bitrates[rxs->rate_idx].hw_value_short)
rxs->flag |= RX_FLAG_SHORTPRE;

trace_ath5k_rx(ah, skb);
@@ -2513,6 +2584,11 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
AR5K_INIT_RETRY_LONG);
}

+ /* don't enable for the 11B only chips */
+ if (test_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode) ||
+ test_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode))
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
+
hw->vif_data_size = sizeof(struct ath5k_vif);

/* Finish private driver data initialization */
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
index 9d00dab..cb6de88 100644
--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -961,10 +961,25 @@ ath5k_debug_dump_bands(struct ath5k_hw *ah)
printk(KERN_DEBUG " rates:\n");
for (i = 0; i < band->n_bitrates; i++)
printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n",
- band->bitrates[i].bitrate,
- band->bitrates[i].hw_value,
- band->bitrates[i].flags,
- band->bitrates[i].hw_value_short);
+ band->bitrates[i].bitrate,
+ band->bitrates[i].hw_value,
+ band->bitrates[i].flags,
+ band->bitrates[i].hw_value_short);
+ printk(KERN_DEBUG " 10 MHz rates:\n");
+ for (i = 0; i < band->n_bitrates_half; i++)
+ printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n",
+ band->bitrates_half[i].bitrate,
+ band->bitrates_half[i].hw_value,
+ band->bitrates_half[i].flags,
+ band->bitrates_half[i].hw_value_short);
+ printk(KERN_DEBUG " 5 MHz rates:\n");
+ for (i = 0; i < band->n_bitrates_quarter; i++)
+ printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n",
+ band->bitrates_quarter[i].bitrate,
+ band->bitrates_quarter[i].hw_value,
+ band->bitrates_quarter[i].flags,
+ band->bitrates_quarter[i].hw_value_short);
+
}
}

diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index 1f16b42..39d3f17 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -274,24 +274,44 @@ ath5k_hw_update_mib_counters(struct ath5k_hw *ah)
static inline void
ath5k_hw_write_rate_duration(struct ath5k_hw *ah)
{
+ struct ieee80211_rate *bitrates;
struct ieee80211_rate *rate;
- unsigned int i;
+ unsigned int i, n_bitrates;
/* 802.11g covers both OFDM and CCK */
u8 band = IEEE80211_BAND_2GHZ;

+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_DEFAULT:
+ case AR5K_BWMODE_40MHZ:
+ bitrates = ah->sbands[band].bitrates;
+ n_bitrates = ah->sbands[band].n_bitrates;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ bitrates = ah->sbands[band].bitrates_quarter;
+ n_bitrates = ah->sbands[band].n_bitrates_quarter;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ bitrates = ah->sbands[band].bitrates_half;
+ n_bitrates = ah->sbands[band].n_bitrates_half;
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
/* Write rate duration table */
- for (i = 0; i < ah->sbands[band].n_bitrates; i++) {
+ for (i = 0; i < n_bitrates; i++) {
u32 reg;
u16 tx_time;

if (ah->ah_ack_bitrate_high)
- rate = &ah->sbands[band].bitrates[ack_rates_high[i]];
+ rate = &bitrates[ack_rates_high[i]];
/* CCK -> 1Mb */
else if (i < 4)
- rate = &ah->sbands[band].bitrates[0];
+ rate = &bitrates[0];
/* OFDM -> 6Mb */
else
- rate = &ah->sbands[band].bitrates[4];
+ rate = &bitrates[4];

/* Set ACK timeout */
reg = AR5K_RATE_DUR(rate->hw_value);
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index 65fe929..2de20ae 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -567,6 +567,8 @@ 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_rate *rate;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock;
u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);

@@ -605,7 +607,26 @@ 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_DEFAULT:
+ case AR5K_BWMODE_40MHZ:
+ bitrates = ah->sbands[band].bitrates;
+ n_bitrates = ah->sbands[band].n_bitrates;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ bitrates = ah->sbands[band].bitrates_quarter;
+ n_bitrates = ah->sbands[band].n_bitrates_quarter;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ bitrates = ah->sbands[band].bitrates_half;
+ n_bitrates = ah->sbands[band].n_bitrates_half;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ rate = &bitrates[0];
ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false);

/* ack_tx_time includes an SIFS already */
--
1.7.10.4


2013-05-15 14:19:37

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 14/18] ath9k: report 5/10 MHz channels

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 9e7c929..697d7f2 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -872,6 +872,17 @@ static int ath9k_process_rate(struct ath_common *common,
&bitrates, &n_bitrates)))
return 0;

+ 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


2013-05-15 14:19:33

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 02/18] nl80211: add half/quarter channel bitrate tables to supported band struct

Reduced bandwidth channels use other bitrates (half or quarter of the
original 20 MHz rate). To allow internal use of these different rates,
add bitrate tables for these channel types. Drivers which support these
channel types must fill these tables, and mac80211 (or full-mac drivers
support 5/10 MHz) must check them.

Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
include/net/cfg80211.h | 39 ++++++++++++++++++++++++++++++++
net/wireless/util.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 97 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 08489cc..d4c687a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -273,13 +273,52 @@ struct ieee80211_sta_vht_cap {
struct ieee80211_supported_band {
struct ieee80211_channel *channels;
struct ieee80211_rate *bitrates;
+ struct ieee80211_rate *bitrates_half;
+ struct ieee80211_rate *bitrates_quarter;
enum ieee80211_band band;
int n_channels;
int n_bitrates;
+ int n_bitrates_half;
+ int n_bitrates_quarter;
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
};

+/**
+ * ieee80211_get_bitrates - get the bitrates for the channel width
+ *
+ * @sband: supported band
+ * @width: channel width where the bitrates are operated
+ * @bitrates: pointer to bitrate array for return
+ * @n_bitrates: pointer for the number of bitrates to return
+ *
+ * Return: 0 on success, -EINVAL if no rates are defined
+ */
+static inline int
+ieee80211_get_bitrates(struct ieee80211_supported_band *sband,
+ enum nl80211_chan_width width,
+ struct ieee80211_rate **bitrates,
+ int *n_bitrates)
+{
+ switch (width) {
+ case NL80211_CHAN_WIDTH_5:
+ *bitrates = sband->bitrates_quarter;
+ *n_bitrates = sband->n_bitrates_quarter;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ *bitrates = sband->bitrates_half;
+ *n_bitrates = sband->n_bitrates_half;
+ break;
+ default:
+ *bitrates = sband->bitrates;
+ *n_bitrates = sband->n_bitrates;
+ break;
+ }
+ if (*bitrates)
+ return 0;
+ else
+ return -EINVAL;
+}
/*
* Wireless hardware/device configuration structures and methods
*/
diff --git a/net/wireless/util.c b/net/wireless/util.c
index f5ad4d9..b0fd2f3 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -122,6 +122,32 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband,
}
}
WARN_ON(want);
+ if (sband->n_bitrates_half) {
+ want = 3;
+ for (i = 0; i < sband->n_bitrates_half; i++) {
+ if (sband->bitrates_half[i].bitrate == 30 ||
+ sband->bitrates_half[i].bitrate == 60 ||
+ sband->bitrates_half[i].bitrate == 120) {
+ sband->bitrates_half[i].flags |=
+ IEEE80211_RATE_MANDATORY_A;
+ want--;
+ }
+ }
+ WARN_ON(want);
+ }
+ if (sband->n_bitrates_quarter) {
+ want = 3;
+ for (i = 0; i < sband->n_bitrates_quarter; i++) {
+ if (sband->bitrates_quarter[i].bitrate == 15 ||
+ sband->bitrates_quarter[i].bitrate == 30 ||
+ sband->bitrates_quarter[i].bitrate == 60) {
+ sband->bitrates_quarter[i].flags |=
+ IEEE80211_RATE_MANDATORY_A;
+ want--;
+ }
+ }
+ WARN_ON(want);
+ }
break;
case IEEE80211_BAND_2GHZ:
want = 7;
@@ -152,6 +178,38 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband,
IEEE80211_RATE_ERP_G;
}
WARN_ON(want != 0 && want != 3 && want != 6);
+
+ /*
+ * NOTE: half/quarter bitrates are actually only defined for
+ * the OFDM PHY, yet some chipsets support them so define
+ * mandatory rates as for 5 GHZ.
+ */
+ if (sband->n_bitrates_half) {
+ want = 3;
+ for (i = 0; i < sband->n_bitrates_half; i++) {
+ if (sband->bitrates_half[i].bitrate == 30 ||
+ sband->bitrates_half[i].bitrate == 60 ||
+ sband->bitrates_half[i].bitrate == 120) {
+ sband->bitrates_half[i].flags |=
+ IEEE80211_RATE_MANDATORY_B;
+ want--;
+ }
+ }
+ WARN_ON(want);
+ }
+ if (sband->n_bitrates_quarter) {
+ want = 3;
+ for (i = 0; i < sband->n_bitrates_quarter; i++) {
+ if (sband->bitrates_quarter[i].bitrate == 15 ||
+ sband->bitrates_quarter[i].bitrate == 30 ||
+ sband->bitrates_quarter[i].bitrate == 60) {
+ sband->bitrates_quarter[i].flags |=
+ IEEE80211_RATE_MANDATORY_B;
+ want--;
+ }
+ }
+ WARN_ON(want);
+ }
break;
case IEEE80211_BAND_60GHZ:
/* check for mandatory HT MCS 1..4 */
--
1.7.10.4


2013-05-15 14:19:33

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 03/18] mac80211: fix various components for the new 5 and 10 MHz widths

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 0d51877..1ca7ded 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


2013-05-15 20:24:42

by Simon Wunderlich

[permalink] [raw]
Subject: Re: [PATCHv2 00/18] Add support for 5 and 10 MHz channels

On Wed, May 15, 2013 at 04:18:51PM +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 PATCHv1:
> * the actual datarates are handled, not the corresponding 20MHz rates
> as before. This should make it compatible to freebsd, although I was
> informed that some other implementors might still use the corresponding
> 20MHz rates. Anyway, from looking at the standard using the actual rates
> should be the right thing to do.

Johannes and me agreed that these bitrates tables are rather ugly, and I'll
rework the patchset to use rate flags instead of these bitrate_half/bitrate_quarter
tables as introduced in this patchset. This will not neccesarily make the mac80211
changes smaller but should avoid this bloat and make the driver implementations
nicer too. I'll post PATCHv3 soon.

Cheers,
Simon


Attachments:
(No filename) (1.40 kB)
signature.asc (198.00 B)
Digital signature
Download all attachments

2013-05-15 14:19:35

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 13/18] ath9k: add and use 5/10 MHz bitrate tables

When a reduced bandwidth mode is enabled, the according bitrate tables
must be used instead of the standard tables. This patch adds these
bitrate tables and selects the appropriate table.

Signed-off-by: Simon Wunderlich <[email protected]>
Signed-off-by: Mathias Kretschmer <[email protected]>
---
drivers/net/wireless/ath/ath9k/beacon.c | 13 ++++++-
drivers/net/wireless/ath/ath9k/init.c | 63 +++++++++++++++++++++++--------
drivers/net/wireless/ath/ath9k/rc.c | 9 +++++
drivers/net/wireless/ath/ath9k/recv.c | 13 +++++--
drivers/net/wireless/ath/ath9k/xmit.c | 14 ++++++-
5 files changed, 90 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 2ff570f..56383e5 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -76,13 +76,22 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
struct ath_common *common = ath9k_hw_common(ah);
struct ath_tx_info info;
struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
u8 chainmask = ah->txchainmask;
u8 rate = 0;
+ int n_bitrates;

sband = &sc->sbands[common->hw->conf.chandef.chan->band];
- rate = sband->bitrates[rateidx].hw_value;
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ common->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
+ rate = bitrates[rateidx].hw_value;
if (vif->bss_conf.use_short_preamble)
- rate |= sband->bitrates[rateidx].hw_value_short;
+ rate |= bitrates[rateidx].hw_value_short;

memset(&info, 0, sizeof(info));
info.pkt_len = skb->len + FCS_LEN;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 5921194..919d4c2 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -155,6 +155,30 @@ static struct ieee80211_rate ath9k_legacy_rates[] = {
RATE(540, 0x0c, 0),
};

+static struct ieee80211_rate ath9k_legacy_rates_half[] = {
+ RATE(30, 0x0b, 0),
+ RATE(45, 0x0f, 0),
+ RATE(60, 0x0a, 0),
+ RATE(90, 0x0e, 0),
+ RATE(120, 0x09, 0),
+ RATE(180, 0x0d, 0),
+ RATE(240, 0x08, 0),
+ RATE(270, 0x0c, 0),
+};
+
+static struct ieee80211_rate ath9k_legacy_rates_quarter[] = {
+ RATE(15, 0x0b, 0),
+ RATE(22, 0x0f, 0), /* actually 22.5 */
+ RATE(30, 0x0a, 0),
+ RATE(45, 0x0e, 0),
+ RATE(60, 0x09, 0),
+ RATE(90, 0x0d, 0),
+ RATE(120, 0x08, 0),
+ RATE(135, 0x0c, 0),
+};
+
+
+
#ifdef CONFIG_MAC80211_LEDS
static const struct ieee80211_tpt_blink ath9k_tpt_blink[] = {
{ .throughput = 0 * 1024, .blink_time = 334 },
@@ -442,6 +466,7 @@ static int ath9k_init_queues(struct ath_softc *sc)
static int ath9k_init_channels_rates(struct ath_softc *sc)
{
void *channels;
+ struct ieee80211_supported_band *sband;

BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) +
ARRAY_SIZE(ath9k_5ghz_chantable) !=
@@ -455,13 +480,18 @@ static int ath9k_init_channels_rates(struct ath_softc *sc)

memcpy(channels, ath9k_2ghz_chantable,
sizeof(ath9k_2ghz_chantable));
- sc->sbands[IEEE80211_BAND_2GHZ].channels = channels;
- sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
- sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
- ARRAY_SIZE(ath9k_2ghz_chantable);
- sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
- sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
- ARRAY_SIZE(ath9k_legacy_rates);
+ sband = &sc->sbands[IEEE80211_BAND_2GHZ];
+ sband->channels = channels;
+ sband->band = IEEE80211_BAND_2GHZ;
+ sband->n_channels = ARRAY_SIZE(ath9k_2ghz_chantable);
+ sband->bitrates = ath9k_legacy_rates;
+ sband->n_bitrates = ARRAY_SIZE(ath9k_legacy_rates);
+ sband->bitrates_half = ath9k_legacy_rates_half;
+ sband->n_bitrates_half = ARRAY_SIZE(ath9k_legacy_rates_half);
+ sband->bitrates_quarter = ath9k_legacy_rates_quarter;
+ sband->n_bitrates_quarter =
+ ARRAY_SIZE(ath9k_legacy_rates_quarter);
+
}

if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
@@ -472,14 +502,17 @@ static int ath9k_init_channels_rates(struct ath_softc *sc)

memcpy(channels, ath9k_5ghz_chantable,
sizeof(ath9k_5ghz_chantable));
- sc->sbands[IEEE80211_BAND_5GHZ].channels = channels;
- sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
- sc->sbands[IEEE80211_BAND_5GHZ].n_channels =
- ARRAY_SIZE(ath9k_5ghz_chantable);
- sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
- ath9k_legacy_rates + 4;
- sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates =
- ARRAY_SIZE(ath9k_legacy_rates) - 4;
+ sband = &sc->sbands[IEEE80211_BAND_5GHZ];
+ sband->channels = channels;
+ sband->band = IEEE80211_BAND_5GHZ;
+ sband->n_channels = ARRAY_SIZE(ath9k_5ghz_chantable);
+ sband->bitrates = ath9k_legacy_rates + 4;
+ sband->n_bitrates = ARRAY_SIZE(ath9k_legacy_rates) - 4;
+ sband->bitrates_half = ath9k_legacy_rates_half;
+ sband->n_bitrates_half = ARRAY_SIZE(ath9k_legacy_rates_half);
+ sband->bitrates_quarter = ath9k_legacy_rates_quarter;
+ sband->n_bitrates_quarter =
+ ARRAY_SIZE(ath9k_legacy_rates_quarter);
}
return 0;
}
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index ed57d57..0559ac6 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -1283,8 +1283,17 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct ath_softc *sc = priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_rate_priv *ath_rc_priv = priv_sta;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
int i, j = 0;

+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ sc->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }
+
for (i = 0; i < sband->n_bitrates; i++) {
if (sta->supp_rates[sband->band] & BIT(i)) {
ath_rc_priv->neg_rates.rs_rates[j]
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 8be2b5d..9e7c929 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -858,6 +858,8 @@ static int ath9k_process_rate(struct ath_common *common,
struct ieee80211_rx_status *rxs)
{
struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
+ int n_bitrates;
enum ieee80211_band band;
unsigned int i = 0;
struct ath_softc __maybe_unused *sc = common->priv;
@@ -865,6 +867,11 @@ static int ath9k_process_rate(struct ath_common *common,
band = hw->conf.chandef.chan->band;
sband = hw->wiphy->bands[band];

+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ hw->conf.chandef.width,
+ &bitrates, &n_bitrates)))
+ return 0;
+
if (rx_stats->rs_rate & 0x80) {
/* HT rate */
rxs->flag |= RX_FLAG_HT;
@@ -876,12 +883,12 @@ static int ath9k_process_rate(struct ath_common *common,
return 0;
}

- for (i = 0; i < sband->n_bitrates; i++) {
- if (sband->bitrates[i].hw_value == rx_stats->rs_rate) {
+ for (i = 0; i < n_bitrates; i++) {
+ if (bitrates[i].hw_value == rx_stats->rs_rate) {
rxs->rate_idx = i;
return 0;
}
- if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) {
+ if (bitrates[i].hw_value_short == rx_stats->rs_rate) {
rxs->flag |= RX_FLAG_SHORTPRE;
rxs->rate_idx = i;
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index eab0fcb..7062386 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -998,15 +998,25 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
struct ieee80211_tx_info *tx_info;
struct ieee80211_tx_rate *rates;
const struct ieee80211_rate *rate;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
struct ieee80211_hdr *hdr;
struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
- int i;
+ int i, n_bitrates;
u8 rix = 0;

skb = bf->bf_mpdu;
tx_info = IEEE80211_SKB_CB(skb);
rates = bf->rates;
hdr = (struct ieee80211_hdr *)skb->data;
+ sband = &sc->sbands[tx_info->band];
+
+ if (WARN_ON(ieee80211_get_bitrates(sband,
+ sc->hw->conf.chandef.width,
+ &bitrates, &n_bitrates))) {
+ bitrates = sband->bitrates;
+ n_bitrates = sband->n_bitrates;
+ }

/* set dur_update_en for l-sig computation except for PS-Poll frames */
info->dur_update = !ieee80211_is_pspoll(hdr->frame_control);
@@ -1052,7 +1062,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
}

/* legacy rates */
- rate = &sc->sbands[tx_info->band].bitrates[rates[i].idx];
+ rate = &bitrates[rates[i].idx];
if ((tx_info->band == IEEE80211_BAND_2GHZ) &&
!(rate->flags & IEEE80211_RATE_ERP_G))
phy = WLAN_RC_PHY_CCK;
--
1.7.10.4


2013-05-15 20:59:41

by Adrian Chadd

[permalink] [raw]
Subject: Re: [PATCHv2 00/18] Add support for 5 and 10 MHz channels

Have you tried interoperating with FreeBSD?

Have you tried interoperating with some of the atheros AP builds that
are out there?

I'd like to do some interoperability tests with this at some point
soon just to ensure things aren't too crazy.

thanks,



Adrian


On 15 May 2013 07:18, Simon Wunderlich
<[email protected]> 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 PATCHv1:
> * the actual datarates are handled, not the corresponding 20MHz rates
> as before. This should make it compatible to freebsd, although I was
> informed that some other implementors might still use the corresponding
> 20MHz rates. Anyway, from looking at the standard using the actual rates
> should be the right thing to do.
> * txpower is decremented according to channel bandwidth (the regulation I
> checked all define limits in dBm/MHz)
> * radiotap support for 5/10 MHz channel flag was added
> * some fixes for ath5k/ath9k were included to make them interoperable
> (having the same SIFS improved performance quite well,
> 300kbps -> 6600 kbps for 5 MHz channels :) )
>
> As always, any comments are appreciated!
> Cheers,
> Simon
>
> Simon Wunderlich (18):
> nl80211/cfg80211: add 5 and 10 MHz defines and wiphy flag
> nl80211: add half/quarter channel bitrate tables to supported band
> struct
> mac80211: fix various components for the new 5 and 10 MHz widths
> mac80211: fix timing for 5 MHz and 10 MHz channels
> mac80211: round rates to the next multiple of 500kbps
> mac80211: choose bitrate table according to bandwidth
> 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: add and use 5/10 MHz bitrate tables
> ath9k: report 5/10 MHz channels
> ath9k: announce that ath9k supports 5/10 MHz
> ath5k: add and use 5/10 MHz bitrate tables
> ath5k: report 5/10 MHz channels
> ath5k: enable support for 5 MHz and 10 MHz channels
>
> drivers/net/wireless/ath/ath5k/ath5k.h | 11 +-
> drivers/net/wireless/ath/ath5k/base.c | 133 +++++++++++++++++++++---
> drivers/net/wireless/ath/ath5k/base.h | 2 +-
> drivers/net/wireless/ath/ath5k/debug.c | 23 ++++-
> drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +-
> drivers/net/wireless/ath/ath5k/pcu.c | 30 +++++-
> drivers/net/wireless/ath/ath5k/qcu.c | 23 ++++-
> drivers/net/wireless/ath/ath9k/beacon.c | 13 ++-
> 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 | 68 +++++++++---
> drivers/net/wireless/ath/ath9k/main.c | 8 +-
> drivers/net/wireless/ath/ath9k/rc.c | 13 ++-
> drivers/net/wireless/ath/ath9k/recv.c | 24 ++++-
> drivers/net/wireless/ath/ath9k/xmit.c | 14 ++-
> include/net/cfg80211.h | 66 ++++++++++++
> include/net/ieee80211_radiotap.h | 4 +
> include/net/mac80211.h | 40 ++++++-
> include/uapi/linux/nl80211.h | 4 +
> net/mac80211/cfg.c | 50 +++++++--
> net/mac80211/ibss.c | 58 +++++++----
> net/mac80211/ieee80211_i.h | 47 ++++++++-
> net/mac80211/iface.c | 7 +-
> net/mac80211/main.c | 7 +-
> net/mac80211/mesh.c | 4 +-
> net/mac80211/mesh_plink.c | 18 +++-
> net/mac80211/mlme.c | 84 ++++++++++-----
> net/mac80211/rate.c | 102 ++++++++++++------
> net/mac80211/rc80211_minstrel.c | 59 +++++++----
> net/mac80211/rc80211_minstrel_ht.c | 26 +++--
> net/mac80211/rc80211_pid.h | 2 +
> net/mac80211/rc80211_pid_algo.c | 68 +++++++++---
> net/mac80211/rx.c | 52 +++++++---
> net/mac80211/status.c | 18 +++-
> net/mac80211/tx.c | 43 ++++++--
> net/mac80211/util.c | 138 +++++++++++++++++++------
> net/wireless/chan.c | 57 ++++++++--
> net/wireless/nl80211.c | 23 ++++-
> net/wireless/util.c | 58 +++++++++++
> 41 files changed, 1179 insertions(+), 300 deletions(-)
>
> --
> 1.7.10.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-05-15 14:19:37

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 18/18] ath5k: enable support for 5 MHz and 10 MHz channels

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 1dee0c4..b43ef34 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 2a2ec99..9490589 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>
@@ -501,11 +502,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;
@@ -513,7 +530,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)
@@ -2537,6 +2554,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


2013-05-15 18:12:49

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCHv2 03/18] mac80211: fix various components for the new 5 and 10 MHz widths

On Wed, 2013-05-15 at 10:16 -0700, Sam Leffler wrote:
> On Wed, May 15, 2013 at 7:18 AM, Simon Wunderlich
> <[email protected]> wrote:
> 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
>
>
> Perhaps I missed a standards discussion but unilaterally disallowing
> HT rates on 5/10 channels breaks existing functionality (e.g. I use
> them on openwrt). NOHT should be a regulatory constraint just like it
> is for 20 channels.

I don't see how this would break anything, since we wouldn't advertise
HT capabilities (for now). My point was that that the standard doesn't
(currently) define this, so I think it should be a separate second step
for 5/10 MHz channels to see how to do it in a way that wouldn't break
when/if the standard is changed to allow it, etc.

johannes



2013-05-15 14:19:33

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 01/18] nl80211/cfg80211: add 5 and 10 MHz defines and wiphy flag

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..598b609 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 = end_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


2013-05-15 14:19:35

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 09/18] mac80211: change IBSS channel state to chandef

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 3b55a43..b9356c3 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -83,7 +83,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;
@@ -526,7 +526,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;
@@ -541,8 +543,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);

@@ -581,7 +583,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 */
@@ -769,7 +771,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);
}

@@ -801,7 +803,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,
@@ -1071,9 +1073,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(&params->chandef);
+ sdata->u.ibss.chandef = params->chandef;
sdata->u.ibss.fixed_channel = params->channel_fixed;

if (params->ie) {
@@ -1136,7 +1136,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 3258039..d3d48cf 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


2013-05-15 14:19:34

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 07/18] mac80211: add radiotap flag and handling for 5/10 MHz

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 3e52e22..bae3138 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 b0834a9..8ab86cf 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;
@@ -215,20 +216,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


2013-05-15 14:19:38

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 12/18] ath9k: use chandef instead of channel_type

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


2013-05-15 20:22:25

by Simon Wunderlich

[permalink] [raw]
Subject: Re: [PATCHv2 03/18] mac80211: fix various components for the new 5 and 10 MHz widths

On Wed, May 15, 2013 at 08:12:42PM +0200, Johannes Berg wrote:
> On Wed, 2013-05-15 at 10:16 -0700, Sam Leffler wrote:
> > On Wed, May 15, 2013 at 7:18 AM, Simon Wunderlich
> > <[email protected]> wrote:
> > 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
> >
> >
> > Perhaps I missed a standards discussion but unilaterally disallowing
> > HT rates on 5/10 channels breaks existing functionality (e.g. I use
> > them on openwrt). NOHT should be a regulatory constraint just like it
> > is for 20 channels.
>
> I don't see how this would break anything, since we wouldn't advertise
> HT capabilities (for now). My point was that that the standard doesn't
> (currently) define this, so I think it should be a separate second step
> for 5/10 MHz channels to see how to do it in a way that wouldn't break
> when/if the standard is changed to allow it, etc.

I generally think that we could add HT rates too, but this patchset does not
include that yet. Felix also told me that HT rates work on current hardware,
and freebsd seems to have support as well. But I'd like to postpone this step
for now and fix this in a later patchset, where we can address concerns too.

Thanks,
Simon


Attachments:
(No filename) (1.43 kB)
signature.asc (198.00 B)
Digital signature
Download all attachments

2013-05-15 14:19:34

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 05/18] mac80211: round rates to the next multiple of 500kbps

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]>
---
net/mac80211/ibss.c | 8 ++++----
net/mac80211/mlme.c | 18 +++++++++---------
net/mac80211/rc80211_minstrel.c | 2 +-
net/mac80211/rx.c | 2 +-
net/mac80211/status.c | 5 ++++-
net/mac80211/util.c | 7 ++++---
6 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 4e1fb81..6a96663 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -142,7 +142,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
u8 basic = 0;
if (basic_rates & BIT(i))
basic = 0x80;
- *pos++ = basic | (u8) (rate / 5);
+ *pos++ = basic | (u8) ((rate + 4) / 5);
}

if (sband->band == IEEE80211_BAND_2GHZ) {
@@ -165,7 +165,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
u8 basic = 0;
if (basic_rates & BIT(i))
basic = 0x80;
- *pos++ = basic | (u8) (rate / 5);
+ *pos++ = basic | (u8) ((rate + 4) / 5);
}
}

@@ -275,11 +275,11 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
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) {
+ if ((sband->bitrates[j].bitrate + 4) / 5 == rate) {
if (is_basic)
basic_rates |= BIT(j);
break;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 0eaee23..03c1b73 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -520,10 +520,10 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
*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) {
+ if ((sband->bitrates[j].bitrate + 4) / 5 == rate) {
*rates |= BIT(j);
count++;
break;
@@ -765,7 +765,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
for (i = 0; i < sband->n_bitrates; i++) {
if (BIT(i) & rates) {
int rate = sband->bitrates[i].bitrate;
- *pos++ = (u8) (rate / 5);
+ *pos++ = (u8) ((rate + 4) / 5);
if (++count == 8)
break;
}
@@ -779,7 +779,7 @@ 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);
+ *pos++ = (u8) ((rate + 4) / 5);
}
}
}
@@ -2448,10 +2448,10 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
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) > 110)
*have_higher_than_11mbit = true;

/*
@@ -2467,12 +2467,12 @@ 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) {
+ if ((sband->bitrates[j].bitrate + 4) / 5 == 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;
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 3fe134a..6c89c29 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -451,7 +451,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
memset(mr, 0, sizeof(*mr));

mr->rix = i;
- mr->bitrate = sband->bitrates[i].bitrate / 5;
+ mr->bitrate = (sband->bitrates[i].bitrate + 4) / 5;
calc_rate_durations(sband->band, mr, &sband->bitrates[i], mp);

/* calculate maximum number of retransmissions before
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index c8447af..06f71c6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -208,7 +208,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos = 0;
} else {
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
- *pos = rate->bitrate / 5;
+ *pos = (rate->bitrate + 4) / 5;
}
pos++;

diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 4343920..f62e4f6 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -280,8 +280,11 @@ 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 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 = (rate + 4) / 5;
/* padding for tx flags */
pos += 2;
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 5496764..9d04989 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1213,7 +1213,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
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);
+ rates[num_rates++] = (u8)
+ ((sband->bitrates[i].bitrate + 4) / 5);
}

supp_rates_len = min_t(int, num_rates, 8);
@@ -2055,7 +2056,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
if (need_basic && basic_rates & BIT(i))
basic = 0x80;
rate = sband->bitrates[i].bitrate;
- *pos++ = basic | (u8) (rate / 5);
+ *pos++ = basic | (u8) ((rate + 4) / 5);
}

return 0;
@@ -2090,7 +2091,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
if (need_basic && basic_rates & BIT(i))
basic = 0x80;
rate = sband->bitrates[i].bitrate;
- *pos++ = basic | (u8) (rate / 5);
+ *pos++ = basic | (u8) ((rate + 4) / 5);
}
}
return 0;
--
1.7.10.4


2013-05-15 14:19:37

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 17/18] ath5k: report 5/10 MHz channels

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 a4e0b4e..2a2ec99 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -1424,6 +1424,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;
+ }

ath5k_get_bitrates(ah->ah_bwmode, &ah->sbands[ah->curchan->band],
&bitrates, &n_bitrates);
--
1.7.10.4


2013-05-16 11:40:35

by Simon Wunderlich

[permalink] [raw]
Subject: Re: [PATCHv2 00/18] Add support for 5 and 10 MHz channels

Hello Adrian,

On Wed, May 15, 2013 at 01:59:39PM -0700, Adrian Chadd wrote:
> Have you tried interoperating with FreeBSD?
>
> Have you tried interoperating with some of the atheros AP builds that
> are out there?

Both no - the only interoperability I tested was ath5k and ath9k (and there
was some stuff to be fixed too).

>
> I'd like to do some interoperability tests with this at some point
> soon just to ensure things aren't too crazy.

I don't have any Atheros AP builds here, and don't plan to test. If you
have access to them or would like test FreeBSD, that would be good. :)

Thanks,
Simon


Attachments:
(No filename) (605.00 B)
signature.asc (198.00 B)
Digital signature
Download all attachments

2013-05-15 14:19:35

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 15/18] ath9k: announce that ath9k supports 5/10 MHz

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 919d4c2..7eeb015 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -828,6 +828,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


2013-05-15 14:19:34

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 04/18] mac80211: fix timing for 5 MHz and 10 MHz channels

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 | 14 +++++++-----
net/mac80211/rc80211_minstrel_ht.c | 8 ++++---
net/mac80211/tx.c | 10 ++++++---
net/mac80211/util.c | 39 ++++++++++++++++++++++-----------
5 files changed, 88 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..3fe134a 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -382,14 +382,17 @@ 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);

d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
- rate->bitrate, erp, 1);
+ rate->bitrate, erp, 1,
+ ieee80211_hw_get_divisor(mp->hw));
d->ack_time = ieee80211_frame_duration(band, 10,
- rate->bitrate, erp, 1);
+ rate->bitrate, erp, 1,
+ ieee80211_hw_get_divisor(mp->hw));
}

static void
@@ -430,7 +433,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 +452,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


2013-05-16 15:34:41

by Adrian Chadd

[permalink] [raw]
Subject: Re: [PATCHv2 00/18] Add support for 5 and 10 MHz channels

How about I setup a couple of FreeBSD devices with 5/10MHz operating
modes and dump the beacon IEs, probe request/response and assoc
request/response?

That way you can see what the rate table entries look like.

Thanks!


Adrian

2013-05-15 14:19:36

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 11/18] ath9k: always use SIFS times from OFDM for 5/10 MHz

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


2013-05-17 09:58:34

by Simon Wunderlich

[permalink] [raw]
Subject: Re: [PATCHv2 00/18] Add support for 5 and 10 MHz channels

Hey Adrian,

On Thu, May 16, 2013 at 08:34:39AM -0700, Adrian Chadd wrote:
> How about I setup a couple of FreeBSD devices with 5/10MHz operating
> modes and dump the beacon IEs, probe request/response and assoc
> request/response?
>
> That way you can see what the rate table entries look like.

yeah, that would be cool to compare the IEs and see if this looks similar.

Maybe I can do the same for the Linux IEs too, just for reference.
We basically enable it only if 802.11a or g is supported (802.11b is not
supported), and announce OFDM only rates. The rates are divided by two or
four (for 10 and 5 MHz), and rounded up. That's pretty much the only
thing which is changed to 20 MHz.

BTW, how is HT handled for narrow channels in FreeBSD? Do you just send
HT IEs as they are in 20 MHz? Also, do you disable secondary channels?
(I would probably do that, using two adjacent 5 MHz channels should be
replaceable by one 10 MHz channel ...)

Thanks,
Simon


Attachments:
(No filename) (961.00 B)
signature.asc (198.00 B)
Digital signature
Download all attachments

2013-05-15 14:19:35

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 10/18] nl80211: allow 5 and 10 MHz channels for IBSS

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


2013-05-15 14:19:34

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv2 08/18] cfg80211/mac80211: use reduced txpower for 5 and 10 MHz

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 d4c687a..4e4a447 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -472,6 +472,31 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
u32 prohibited_flags);

/**
+ * 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 28a38a7..3d859c7 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 f6b6b57..eda4427 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 837a580..a7fd1db 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -798,7 +798,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