2012-08-01 15:21:31

by Johannes Berg

[permalink] [raw]
Subject: [RFC 0/8] channel switch improvements

mac80211 currently doesn't support extended channel switch
and also doesn't handle failures well.

Support extended channel switch when the operating class
is one of the global operating classes, and handle any
failures by disconnecting from the network.

This is completely untested so far, does anyone have a
good test environment for this kind of thing?

johannes



2012-08-01 15:21:35

by Johannes Berg

[permalink] [raw]
Subject: [RFC 8/8] mac80211: disconnect if channel switch fails

From: Johannes Berg <[email protected]>

Disconnect from the AP if channel switching in the
driver failed or if the new channel is unavailable.

Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/mlme.c | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index cc76481..359ec2b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -730,16 +730,13 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)

trace_api_chswitch_done(sdata, success);
if (!success) {
- /*
- * If the channel switch was not successful, stay
- * around on the old channel. We currently lack
- * good handling of this situation, possibly we
- * should just drop the association.
- */
- sdata->local->csa_channel = sdata->local->oper_channel;
+ sdata_info(sdata,
+ "driver channel switch failed, disconnecting\n");
+ ieee80211_queue_work(&sdata->local->hw,
+ &ifmgd->csa_connection_drop_work);
+ } else {
+ ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}
-
- ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}
EXPORT_SYMBOL(ieee80211_chswitch_done);

@@ -784,8 +781,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return;

new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
- if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
+ if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) {
+ sdata_info(sdata,
+ "AP %pM switches to unsupported channel %d, disconnecting\n",
+ ifmgd->associated->bssid, new_chan_no);
+ ieee80211_queue_work(&sdata->local->hw,
+ &ifmgd->csa_connection_drop_work);
return;
+ }

sdata->local->csa_channel = new_ch;

@@ -1704,6 +1707,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
transmit_frame, frame_buf);
+ ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
mutex_unlock(&ifmgd->mtx);

/*
@@ -1748,6 +1752,8 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
container_of(work, struct ieee80211_sub_if_data,
u.mgd.csa_connection_drop_work);

+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
__ieee80211_disconnect(sdata, true);
}

--
1.7.10.4


2012-08-20 11:57:34

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC 0/8] channel switch improvements

On Wed, 2012-08-01 at 17:21 +0200, Johannes Berg wrote:
> mac80211 currently doesn't support extended channel switch
> and also doesn't handle failures well.
>
> Support extended channel switch when the operating class
> is one of the global operating classes, and handle any
> failures by disconnecting from the network.
>
> This is completely untested so far, does anyone have a
> good test environment for this kind of thing?

Since they're just cleanups, I applied the first five patches. The rest
need to be tested and validated more etc.

johannes


2012-08-01 20:45:06

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC 7/8] mac80211: support extended channel switch

On Wed, 2012-08-01 at 22:54 +0300, Arik Nemtsov wrote:
> On Wed, Aug 1, 2012 at 10:33 PM, Johannes Berg
> <[email protected]> wrote:
> > On Wed, 2012-08-01 at 22:28 +0300, Arik Nemtsov wrote:
> >
> >> > +static void
> >> > +ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
> >> > + u8 new_chan_no, u8 count, u8 mode,
> >> > + enum ieee80211_band new_band,
> >> > + struct ieee80211_bss *bss, u64 timestamp)
> >> > {
> >> > struct cfg80211_bss *cbss =
> >> > container_of((void *)bss, struct cfg80211_bss, priv);
> >> > struct ieee80211_channel *new_ch;
> >> > struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
> >> > - int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
> >> > - cbss->channel->band);
> >> > + int new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
> >>
> >> I'm not sure a channel switch between bands should be allowed. Case in
> >> point - a device might have different HT capabilities between bands,
> >> so different rates might need to be programmed to FW. This would
> >> require a re-assoc..
> >
> > Hmm, that might require some new flags or something? I'm not even sure
> > the extended chanswitch is allowed to switch bands, but by the look of
> > it that must have been the intent?
>
> Maybe the idea was to change the operating class while staying in the
> same band? But now I'm thinking that even without different HT caps
> per band, the change of class alone might impose new regulatory
> restrictions. For instance HT40 may now be allowed/disallowed.
> So we have to notify the driver about this somehow?
>
> If it's too cumbersome, maybe we should just disconnect if the class
> changes? wpa_s would reconnect anyway in most situations (non p2p).

Yeah, maybe. I'm just going to drop patches 6 and 7 for now. The
extended switch handling was just something I thought about, not
actually necessary for what I'm doing (still multi-channel work)

johannes


2012-08-01 19:28:29

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [RFC 7/8] mac80211: support extended channel switch

On Wed, Aug 1, 2012 at 6:21 PM, Johannes Berg <[email protected]> wrote:
> From: Johannes Berg <[email protected]>
>
> Support extended channel switch when the operating
> class is one of the global operating classes as
> defined in Annex E of 802.11-2012. If it isn't,
> disconnect from the AP instead.
>
> Signed-off-by: Johannes Berg <[email protected]>
[...]
> +static void
> +ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
> + u8 new_chan_no, u8 count, u8 mode,
> + enum ieee80211_band new_band,
> + struct ieee80211_bss *bss, u64 timestamp)
> {
> struct cfg80211_bss *cbss =
> container_of((void *)bss, struct cfg80211_bss, priv);
> struct ieee80211_channel *new_ch;
> struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
> - int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
> - cbss->channel->band);
> + int new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);

I'm not sure a channel switch between bands should be allowed. Case in
point - a device might have different HT capabilities between bands,
so different rates might need to be programmed to FW. This would
require a re-assoc..

Arik

2012-08-01 15:21:34

by Johannes Berg

[permalink] [raw]
Subject: [RFC 6/8] cfg80211: add ieee80211_operating_class_to_band

From: Johannes Berg <[email protected]>

This function converts a (global only!) operating
class to an internal band identifier. This will
be needed for extended channel switch support.

Signed-off-by: Johannes Berg <[email protected]>
---
include/net/cfg80211.h | 11 +++++++++++
net/wireless/util.c | 20 ++++++++++++++++++++
2 files changed, 31 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 493fa0c..700232b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3520,6 +3520,17 @@ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
enum nl80211_channel_type type);

+/**
+ * ieee80211_operating_class_to_band - convert operating class to band
+ *
+ * @operating_class: the operating class to convert
+ * @band: band pointer to fill
+ *
+ * Returns %true if the conversion was successful, %false otherwise.
+ */
+bool ieee80211_operating_class_to_band(u8 operating_class,
+ enum ieee80211_band *band);
+
/*
* cfg80211_calculate_bitrate - calculate actual bitrate (in 100Kbps units)
* @rate: given rate_info to calculate bitrate from
diff --git a/net/wireless/util.c b/net/wireless/util.c
index ce393dd..14d3c5e 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -971,6 +971,26 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
}
EXPORT_SYMBOL(cfg80211_calculate_bitrate);

+bool ieee80211_operating_class_to_band(u8 operating_class,
+ enum ieee80211_band *band)
+{
+ switch (operating_class) {
+ case 112:
+ case 113:
+ case 115 ... 127:
+ *band = IEEE80211_BAND_5GHZ;
+ return true;
+
+ case 83:
+ case 84:
+ *band = IEEE80211_BAND_2GHZ;
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(ieee80211_operating_class_to_band);
+
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
u32 beacon_int)
{
--
1.7.10.4


2012-08-01 15:21:33

by Johannes Berg

[permalink] [raw]
Subject: [RFC 2/8] mac80211: clean up CSA handling code

From: Johannes Berg <[email protected]>

Clean up the CSA handling code by moving some
of it out of the if and using a C99 initializer
for the struct passed to the driver method.

While at it, also add a comment that we should
wait for a beacon after switching the channel.

Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/mlme.c | 34 ++++++++++++++++------------------
1 file changed, 16 insertions(+), 18 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 55cf04c..dc1c84b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -712,6 +712,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
/* XXX: shouldn't really modify cfg80211-owned data! */
ifmgd->associated->channel = sdata->local->oper_channel;

+ /* XXX: wait for a beacon first? */
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
out:
@@ -788,36 +789,33 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,

sdata->local->csa_channel = new_ch;

+ ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+
+ if (sw_elem->mode)
+ ieee80211_stop_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+
if (sdata->local->ops->channel_switch) {
/* use driver's channel switch callback */
- struct ieee80211_channel_switch ch_switch;
- memset(&ch_switch, 0, sizeof(ch_switch));
- ch_switch.timestamp = timestamp;
- if (sw_elem->mode) {
- ch_switch.block_tx = true;
- ieee80211_stop_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CSA);
- }
- ch_switch.channel = new_ch;
- ch_switch.count = sw_elem->count;
- ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+ struct ieee80211_channel_switch ch_switch = {
+ .timestamp = timestamp,
+ .block_tx = sw_elem->mode,
+ .channel = new_ch,
+ .count = sw_elem->count,
+ };
+
drv_channel_switch(sdata->local, &ch_switch);
return;
}

/* channel switch handled in software */
- if (sw_elem->count <= 1) {
+ if (sw_elem->count <= 1)
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
- } else {
- if (sw_elem->mode)
- ieee80211_stop_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CSA);
- ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+ else
mod_timer(&ifmgd->chswitch_timer,
jiffies +
msecs_to_jiffies(sw_elem->count *
cbss->beacon_interval));
- }
}

static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
--
1.7.10.4


2012-08-01 15:21:33

by Johannes Berg

[permalink] [raw]
Subject: [RFC 1/8] mac80211: remove unneeded 'bssid' variable

From: Johannes Berg <[email protected]>

There's no need to copy the BSSID just to print
it, remove the unnecessary variable.

Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/mlme.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c416a08..55cf04c 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1696,7 +1696,6 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- u8 bssid[ETH_ALEN];
u8 frame_buf[DEAUTH_DISASSOC_LEN];

mutex_lock(&ifmgd->mtx);
@@ -1705,9 +1704,8 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
return;
}

- memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
-
- sdata_info(sdata, "Connection to AP %pM lost\n", bssid);
+ sdata_info(sdata, "Connection to AP %pM lost\n",
+ ifmgd->associated->bssid);

ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
--
1.7.10.4


2012-08-01 15:21:34

by Johannes Berg

[permalink] [raw]
Subject: [RFC 5/8] mac80211: make ieee80211_beacon_connection_loss_work static

From: Johannes Berg <[email protected]>

There's no need to declare the function in the
header file since it's only used in a single
place, so make it static.

Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/ieee80211_i.h | 1 -
net/mac80211/mlme.c | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a906cc0..585827e 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1424,7 +1424,6 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
struct ieee80211_hdr *hdr);
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
struct ieee80211_hdr *hdr, bool ack);
-void ieee80211_beacon_connection_loss_work(struct work_struct *work);

void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d963017..7451dd4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1720,7 +1720,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&local->mtx);
}

-void ieee80211_beacon_connection_loss_work(struct work_struct *work)
+static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data,
--
1.7.10.4


2012-08-01 20:54:55

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [RFC 7/8] mac80211: support extended channel switch

On Wed, Aug 1, 2012 at 11:45 PM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2012-08-01 at 22:54 +0300, Arik Nemtsov wrote:
>> On Wed, Aug 1, 2012 at 10:33 PM, Johannes Berg
>> <[email protected]> wrote:
>> > On Wed, 2012-08-01 at 22:28 +0300, Arik Nemtsov wrote:
>> >
>> >> > +static void
>> >> > +ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
>> >> > + u8 new_chan_no, u8 count, u8 mode,
>> >> > + enum ieee80211_band new_band,
>> >> > + struct ieee80211_bss *bss, u64 timestamp)
>> >> > {
>> >> > struct cfg80211_bss *cbss =
>> >> > container_of((void *)bss, struct cfg80211_bss, priv);
>> >> > struct ieee80211_channel *new_ch;
>> >> > struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
>> >> > - int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
>> >> > - cbss->channel->band);
>> >> > + int new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
>> >>
>> >> I'm not sure a channel switch between bands should be allowed. Case in
>> >> point - a device might have different HT capabilities between bands,
>> >> so different rates might need to be programmed to FW. This would
>> >> require a re-assoc..
>> >
>> > Hmm, that might require some new flags or something? I'm not even sure
>> > the extended chanswitch is allowed to switch bands, but by the look of
>> > it that must have been the intent?
>>
>> Maybe the idea was to change the operating class while staying in the
>> same band? But now I'm thinking that even without different HT caps
>> per band, the change of class alone might impose new regulatory
>> restrictions. For instance HT40 may now be allowed/disallowed.
>> So we have to notify the driver about this somehow?
>>
>> If it's too cumbersome, maybe we should just disconnect if the class
>> changes? wpa_s would reconnect anyway in most situations (non p2p).
>
> Yeah, maybe. I'm just going to drop patches 6 and 7 for now. The
> extended switch handling was just something I thought about, not
> actually necessary for what I'm doing (still multi-channel work)

Sounds good.

2012-08-01 19:33:18

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC 7/8] mac80211: support extended channel switch

On Wed, 2012-08-01 at 22:28 +0300, Arik Nemtsov wrote:

> > +static void
> > +ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
> > + u8 new_chan_no, u8 count, u8 mode,
> > + enum ieee80211_band new_band,
> > + struct ieee80211_bss *bss, u64 timestamp)
> > {
> > struct cfg80211_bss *cbss =
> > container_of((void *)bss, struct cfg80211_bss, priv);
> > struct ieee80211_channel *new_ch;
> > struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
> > - int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
> > - cbss->channel->band);
> > + int new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
>
> I'm not sure a channel switch between bands should be allowed. Case in
> point - a device might have different HT capabilities between bands,
> so different rates might need to be programmed to FW. This would
> require a re-assoc..

Hmm, that might require some new flags or something? I'm not even sure
the extended chanswitch is allowed to switch bands, but by the look of
it that must have been the intent?

johannes


2012-08-01 15:21:35

by Johannes Berg

[permalink] [raw]
Subject: [RFC 7/8] mac80211: support extended channel switch

From: Johannes Berg <[email protected]>

Support extended channel switch when the operating
class is one of the global operating classes as
defined in Annex E of 802.11-2012. If it isn't,
disconnect from the AP instead.

Signed-off-by: Johannes Berg <[email protected]>
---
include/linux/ieee80211.h | 12 ++++++
net/mac80211/ieee80211_i.h | 6 +--
net/mac80211/mlme.c | 93 +++++++++++++++++++++++++++++++-------------
net/mac80211/util.c | 7 ++++
4 files changed, 87 insertions(+), 31 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index e02fc68..0c7190b 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -638,6 +638,18 @@ struct ieee80211_channel_sw_ie {
} __attribute__ ((packed));

/**
+ * struct ieee80211_ext_chansw_ie
+ *
+ * This structure represents the "Extended Channel Switch Announcement element"
+ */
+struct ieee80211_ext_chansw_ie {
+ u8 mode;
+ u8 new_operating_class;
+ u8 new_ch_num;
+ u8 count;
+} __packed;
+
+/**
* struct ieee80211_tim
*
* This structure refers to "Traffic Indication Map information element"
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 585827e..a096d26 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -405,6 +405,7 @@ struct ieee80211_if_managed {
struct work_struct monitor_work;
struct work_struct chswitch_work;
struct work_struct beacon_connection_loss_work;
+ struct work_struct csa_connection_drop_work;

unsigned long beacon_timeout;
unsigned long probe_timeout;
@@ -1131,6 +1132,7 @@ struct ieee802_11_elems {
u8 *perr;
struct ieee80211_rann_ie *rann;
struct ieee80211_channel_sw_ie *ch_switch_ie;
+ struct ieee80211_ext_chansw_ie *ext_chansw_ie;
u8 *country_elem;
u8 *pwr_constr_elem;
u8 *quiet_elem; /* first quite element */
@@ -1204,10 +1206,6 @@ void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata);
int ieee80211_max_network_latency(struct notifier_block *nb,
unsigned long data, void *dummy);
int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata);
-void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel_sw_ie *sw_elem,
- struct ieee80211_bss *bss,
- u64 timestamp);
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 7451dd4..cc76481 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -757,17 +757,17 @@ static void ieee80211_chswitch_timer(unsigned long data)
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}

-void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel_sw_ie *sw_elem,
- struct ieee80211_bss *bss,
- u64 timestamp)
+static void
+ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ u8 new_chan_no, u8 count, u8 mode,
+ enum ieee80211_band new_band,
+ struct ieee80211_bss *bss, u64 timestamp)
{
struct cfg80211_bss *cbss =
container_of((void *)bss, struct cfg80211_bss, priv);
struct ieee80211_channel *new_ch;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
- cbss->channel->band);
+ int new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);

ASSERT_MGD_MTX(ifmgd);

@@ -791,7 +791,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,

ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;

- if (sw_elem->mode)
+ if (mode)
ieee80211_stop_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);

@@ -799,9 +799,9 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
/* use driver's channel switch callback */
struct ieee80211_channel_switch ch_switch = {
.timestamp = timestamp,
- .block_tx = sw_elem->mode,
+ .block_tx = mode,
.channel = new_ch,
- .count = sw_elem->count,
+ .count = count,
};

drv_channel_switch(sdata->local, &ch_switch);
@@ -809,12 +809,11 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
}

/* channel switch handled in software */
- if (sw_elem->count <= 1)
+ if (count <= 1)
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
else
mod_timer(&ifmgd->chswitch_timer,
- TU_TO_EXP_TIME(sw_elem->count *
- cbss->beacon_interval));
+ TU_TO_EXP_TIME(count * cbss->beacon_interval));
}

static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@ -1689,7 +1688,8 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ap_probereq_get);

-static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
+static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
+ bool transmit_frame)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
@@ -1701,12 +1701,9 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
return;
}

- sdata_info(sdata, "Connection to AP %pM lost\n",
- ifmgd->associated->bssid);
-
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
- false, frame_buf);
+ transmit_frame, frame_buf);
mutex_unlock(&ifmgd->mtx);

/*
@@ -1736,10 +1733,22 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
rcu_read_unlock();
}

- if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
- __ieee80211_connection_loss(sdata);
- else
+ if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) {
+ sdata_info(sdata, "Connection to AP %pM lost\n",
+ ifmgd->bssid);
+ __ieee80211_disconnect(sdata, false);
+ } else {
ieee80211_mgd_probe_ap(sdata, true);
+ }
+}
+
+static void ieee80211_csa_connection_drop_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.csa_connection_drop_work);
+
+ __ieee80211_disconnect(sdata, true);
}

void ieee80211_beacon_loss(struct ieee80211_vif *vif)
@@ -2230,10 +2239,14 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
bool beacon)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
int freq;
struct ieee80211_bss *bss;
struct ieee80211_channel *channel;
bool need_ps = false;
+ enum ieee80211_band band;
+
+ lockdep_assert_held(&sdata->u.mgd.mtx);

if (sdata->u.mgd.associated &&
ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) {
@@ -2267,10 +2280,30 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->iflist_mtx);
}

- if (elems->ch_switch_ie &&
- memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0)
- ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie,
- bss, rx_status->mactime);
+ if (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN))
+ return;
+
+ if (elems->ext_chansw_ie) {
+ if (ieee80211_operating_class_to_band(
+ elems->ext_chansw_ie->new_operating_class,
+ &band)) {
+ ieee80211_sta_process_chanswitch(sdata,
+ elems->ext_chansw_ie->new_ch_num,
+ elems->ext_chansw_ie->count,
+ elems->ext_chansw_ie->mode,
+ band, bss, rx_status->mactime);
+ } else {
+ sdata_info(sdata,
+ "cannot understand ECSA IE, disconnecting\n");
+ ieee80211_queue_work(&local->hw,
+ &ifmgd->csa_connection_drop_work);
+ }
+ } else if (elems->ch_switch_ie)
+ ieee80211_sta_process_chanswitch(sdata,
+ elems->ch_switch_ie->new_ch_num,
+ elems->ch_switch_ie->count,
+ elems->ch_switch_ie->mode,
+ channel->band, bss, rx_status->mactime);
}


@@ -2580,9 +2613,12 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_SPECTRUM_MGMT:
ieee80211_sta_process_chanswitch(sdata,
- &mgmt->u.action.u.chan_switch.sw_elem,
- (void *)ifmgd->associated->priv,
- rx_status->mactime);
+ mgmt->u.action.u.chan_switch.sw_elem.new_ch_num,
+ mgmt->u.action.u.chan_switch.sw_elem.count,
+ mgmt->u.action.u.chan_switch.sw_elem.mode,
+ rx_status->band,
+ (void *)ifmgd->associated->priv,
+ rx_status->mactime);
break;
}
}
@@ -2925,6 +2961,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)

cancel_work_sync(&ifmgd->monitor_work);
cancel_work_sync(&ifmgd->beacon_connection_loss_work);
+ cancel_work_sync(&ifmgd->csa_connection_drop_work);
if (del_timer_sync(&ifmgd->timer))
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);

@@ -2981,6 +3018,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
INIT_WORK(&ifmgd->beacon_connection_loss_work,
ieee80211_beacon_connection_loss_work);
+ INIT_WORK(&ifmgd->csa_connection_drop_work,
+ ieee80211_csa_connection_drop_work);
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 7dff94e..f7f6cc5 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -774,6 +774,13 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
}
elems->ch_switch_ie = (void *)pos;
break;
+ case WLAN_EID_EXT_CHANSWITCH_ANN:
+ if (elen != sizeof(struct ieee80211_ext_chansw_ie)) {
+ elem_parse_failed = true;
+ break;
+ }
+ elems->ext_chansw_ie = (void *)pos;
+ break;
case WLAN_EID_QUIET:
if (!elems->quiet_elem) {
elems->quiet_elem = pos;
--
1.7.10.4


2012-08-01 19:54:37

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [RFC 7/8] mac80211: support extended channel switch

On Wed, Aug 1, 2012 at 10:33 PM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2012-08-01 at 22:28 +0300, Arik Nemtsov wrote:
>
>> > +static void
>> > +ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
>> > + u8 new_chan_no, u8 count, u8 mode,
>> > + enum ieee80211_band new_band,
>> > + struct ieee80211_bss *bss, u64 timestamp)
>> > {
>> > struct cfg80211_bss *cbss =
>> > container_of((void *)bss, struct cfg80211_bss, priv);
>> > struct ieee80211_channel *new_ch;
>> > struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
>> > - int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
>> > - cbss->channel->band);
>> > + int new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
>>
>> I'm not sure a channel switch between bands should be allowed. Case in
>> point - a device might have different HT capabilities between bands,
>> so different rates might need to be programmed to FW. This would
>> require a re-assoc..
>
> Hmm, that might require some new flags or something? I'm not even sure
> the extended chanswitch is allowed to switch bands, but by the look of
> it that must have been the intent?

Maybe the idea was to change the operating class while staying in the
same band? But now I'm thinking that even without different HT caps
per band, the change of class alone might impose new regulatory
restrictions. For instance HT40 may now be allowed/disallowed.
So we have to notify the driver about this somehow?

If it's too cumbersome, maybe we should just disconnect if the class
changes? wpa_s would reconnect anyway in most situations (non p2p).

2012-08-01 15:21:33

by Johannes Berg

[permalink] [raw]
Subject: [RFC 4/8] mac80211: check size of channel switch IE when parsing

From: Johannes Berg <[email protected]>

The channel switch IE has a fixed size, so we can
discard it in parsing if it's not the right size
and use the right struct pointer.

Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/ieee80211_i.h | 3 +--
net/mac80211/mlme.c | 10 +++-------
net/mac80211/util.c | 7 +++++--
3 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d1a7c58..a906cc0 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1130,7 +1130,7 @@ struct ieee802_11_elems {
u8 *prep;
u8 *perr;
struct ieee80211_rann_ie *rann;
- u8 *ch_switch_elem;
+ struct ieee80211_channel_sw_ie *ch_switch_ie;
u8 *country_elem;
u8 *pwr_constr_elem;
u8 *quiet_elem; /* first quite element */
@@ -1156,7 +1156,6 @@ struct ieee802_11_elems {
u8 preq_len;
u8 prep_len;
u8 perr_len;
- u8 ch_switch_elem_len;
u8 country_elem_len;
u8 pwr_constr_elem_len;
u8 quiet_elem_len;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 5eef6bd..d963017 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2267,14 +2267,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->iflist_mtx);
}

- if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
- (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid,
- ETH_ALEN) == 0)) {
- struct ieee80211_channel_sw_ie *sw_elem =
- (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
- ieee80211_sta_process_chanswitch(sdata, sw_elem,
+ if (elems->ch_switch_ie &&
+ memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0)
+ ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie,
bss, rx_status->mactime);
- }
}


diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 99e4258..7dff94e 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -768,8 +768,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
elem_parse_failed = true;
break;
case WLAN_EID_CHANNEL_SWITCH:
- elems->ch_switch_elem = pos;
- elems->ch_switch_elem_len = elen;
+ if (elen != sizeof(struct ieee80211_channel_sw_ie)) {
+ elem_parse_failed = true;
+ break;
+ }
+ elems->ch_switch_ie = (void *)pos;
break;
case WLAN_EID_QUIET:
if (!elems->quiet_elem) {
--
1.7.10.4


2012-08-01 15:21:33

by Johannes Berg

[permalink] [raw]
Subject: [RFC 3/8] mac80211: fix CSA handling timer

From: Johannes Berg <[email protected]>

The time until the channel switch is in TU,
not in milliseconds, so use TU_TO_EXP_TIME()
to correctly program the timer.

Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/mlme.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index dc1c84b..5eef6bd 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -813,9 +813,8 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
else
mod_timer(&ifmgd->chswitch_timer,
- jiffies +
- msecs_to_jiffies(sw_elem->count *
- cbss->beacon_interval));
+ TU_TO_EXP_TIME(sw_elem->count *
+ cbss->beacon_interval));
}

static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
--
1.7.10.4