2008-12-17 14:57:39

by Sujith

[permalink] [raw]
Subject: [RFC] Handle Channel Switch Announcement

This is an attempt to handle CSA elements from an AP and switch
to the specified channel.

Only mode 1 is handled, i.e, we just stop transmission on receiving a CSA element.
User space notification is not handled yet.

Please review.

diff --git a/include/net/wireless.h b/include/net/wireless.h
index aedefa5..701f597 100644
--- a/include/net/wireless.h
+++ b/include/net/wireless.h
@@ -44,6 +44,8 @@ enum ieee80211_band {
* is not permitted.
* @IEEE80211_CHAN_NO_FAT_BELOW: extension channel below this channel
* is not permitted.
+ * @IEEE80211_CHAN_CSA_DISABLED: Disabled because a
+ Channel Switch Announcement was received on this channel.
*/
enum ieee80211_channel_flags {
IEEE80211_CHAN_DISABLED = 1<<0,
@@ -52,6 +54,7 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_RADAR = 1<<3,
IEEE80211_CHAN_NO_FAT_ABOVE = 1<<4,
IEEE80211_CHAN_NO_FAT_BELOW = 1<<5,
+ IEEE80211_CHAN_CSA_DISABLED = 1<<6
};

/**
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a7dabae..5ed227b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -282,6 +282,7 @@ enum ieee80211_sta_mlme_state {

struct ieee80211_if_sta {
struct timer_list timer;
+ struct timer_list chswitch_timer;
struct work_struct work;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -951,6 +952,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len);
+void ieee80211_chswitch_timer(unsigned long data);
+void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_sw_ie *sw_elem);

/* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 5abbc3f..095ce36 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -441,6 +441,7 @@ static int ieee80211_stop(struct net_device *dev)
WLAN_REASON_DEAUTH_LEAVING);

memset(sdata->u.sta.bssid, 0, ETH_ALEN);
+ del_timer_sync(&sdata->u.sta.chswitch_timer);
del_timer_sync(&sdata->u.sta.timer);
/*
* If the timer fired while we waited for it, it will have
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 9a06905..fe77046 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1592,6 +1592,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (!bss)
return;

+ if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3)) {
+ struct ieee80211_channel_sw_ie *sw_elem =
+ (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
+ ieee80211_process_chanswitch(sdata, sw_elem);
+ }
+
/* was just updated in ieee80211_bss_info_update */
beacon_timestamp = bss->timestamp;

@@ -2384,6 +2390,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
INIT_WORK(&ifsta->work, ieee80211_sta_work);
setup_timer(&ifsta->timer, ieee80211_sta_timer,
(unsigned long) sdata);
+ setup_timer(&ifsta->chswitch_timer, ieee80211_chswitch_timer,
+ (unsigned long) sdata);
skb_queue_head_init(&ifsta->skb_queue);

ifsta->capab = WLAN_CAPABILITY_ESS;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 91b7c9f..7c371fe 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1612,6 +1612,13 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
ieee80211_process_measurement_req(sdata, mgmt, len);
break;
+ case WLAN_ACTION_SPCT_CHL_SWITCH:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.chan_switch)))
+ return RX_DROP_MONITOR;
+ ieee80211_process_chanswitch(sdata,
+ &mgmt->u.action.u.chan_switch.sw_elem);
+ break;
}
break;
default:
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index f5c7c33..7a8b96b 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -382,7 +382,8 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,

channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq);

- if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
+ if (!channel || channel->flags &
+ (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_CSA_DISABLED))
return RX_DROP_MONITOR;

bss = ieee80211_bss_info_update(sdata->local, rx_status,
@@ -538,7 +539,8 @@ void ieee80211_scan_work(struct work_struct *work)
skip = 0;
chan = &sband->channels[local->scan_channel_idx];

- if (chan->flags & IEEE80211_CHAN_DISABLED ||
+ if (chan->flags &
+ (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_CSA_DISABLED) ||
(sdata->vif.type == NL80211_IFTYPE_ADHOC &&
chan->flags & IEEE80211_CHAN_NO_IBSS))
skip = 1;
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index f72bad6..e85387e 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -84,3 +84,85 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
mgmt->sa, mgmt->bssid,
mgmt->u.action.u.measurement.dialog_token);
}
+
+void ieee80211_chswitch_timer(unsigned long data)
+{
+ struct ieee80211_bss *bss;
+ struct ieee80211_sub_if_data *sdata =
+ (struct ieee80211_sub_if_data *) data;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+
+ printk("expire\n");
+
+ if (!netif_running(sdata->dev))
+ return;
+
+ bss = ieee80211_rx_bss_get(sdata->local, ifsta->bssid,
+ sdata->local->hw.conf.channel->center_freq,
+ ifsta->ssid, ifsta->ssid_len);
+ if (!bss)
+ goto exit;
+
+ if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
+ bss->freq = sdata->local->oper_channel->center_freq;
+
+ ieee80211_rx_bss_put(sdata->local, bss);
+exit:
+ ieee80211_wake_queues(&sdata->local->hw);
+}
+
+void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_sw_ie *sw_elem)
+{
+ struct ieee80211_bss *bss;
+ struct ieee80211_channel *new_ch, *cur_ch;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ int ret, exp;
+ int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
+
+ /* FIXME: Handle ADHOC later */
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATED)
+ return;
+
+ new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
+ if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
+ return;
+
+ bss = ieee80211_rx_bss_get(sdata->local, ifsta->bssid,
+ sdata->local->hw.conf.channel->center_freq,
+ ifsta->ssid, ifsta->ssid_len);
+ if (!bss)
+ return;
+
+ /* If the current channel has already been marked as CSA_DISABLED,
+ disregard further beacons until the channel moves when the timer expires */
+
+ cur_ch = ieee80211_get_channel(sdata->local->hw.wiphy,
+ sdata->local->hw.conf.channel->center_freq);
+ if (cur_ch && cur_ch->flags & IEEE80211_CHAN_CSA_DISABLED)
+ goto bss_put;
+ else
+ cur_ch->flags |= IEEE80211_CHAN_CSA_DISABLED;
+
+ if (sdata->local->sw_scanning || sdata->local->hw_scanning)
+ goto bss_put;
+
+ new_ch->flags &= ~IEEE80211_CHAN_CSA_DISABLED;
+ sdata->local->oper_channel = new_ch;
+
+ if (sw_elem->count <= 1) {
+ ret = ieee80211_hw_config(sdata->local,
+ IEEE80211_CONF_CHANGE_CHANNEL);
+ if (!ret)
+ bss->freq = new_freq;
+ } else {
+ ieee80211_stop_queues(&sdata->local->hw);
+ exp = sw_elem->count * (1024 * bss->beacon_int / 1000 * HZ / 1000);
+ mod_timer(&ifsta->chswitch_timer, jiffies + exp);
+ }
+bss_put:
+ ieee80211_rx_bss_put(sdata->local, bss);
+}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 71a8391..b9693d8 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -636,7 +636,8 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)

chan = ieee80211_get_channel(local->hw.wiphy, freqMHz);

- if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
+ if (chan && !(chan->flags & (IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_CSA_DISABLED))) {
if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
chan->flags & IEEE80211_CHAN_NO_IBSS)
return ret;


2008-12-18 01:38:50

by Sujith

[permalink] [raw]
Subject: Re: [RFC] Handle Channel Switch Announcement

Johannes Berg wrote:
> I agree with Jouni, this is probably not a good idea, especially since
> this flag would be shared between hardware for those drivers that have
> the channel structs allocated statically. We should probably just stick
> a struct ieee80211_channel *switch_from; pointer somewhere into the
> sdata struct or so.

Ok.

> I think I'd like to have them dropped to monitor for !station here,
> instead of silently in the processing function.

Ok.

> That should probably be removed or be more specific & protected by some
> debug #ifdef.

That just slipped through, I'll remove it.

> Can you rebase this on top of Kalle's patch that adds the "stop reasons"
> for each queue? As it is now, the driver could wake up the queues again
> if one was full.

Ok, I will.

Sujith

2008-12-18 01:34:57

by Sujith

[permalink] [raw]
Subject: Re: [RFC] Handle Channel Switch Announcement

Jouni Malinen wrote:
> Why is this flag needed? Or well, the better question: Why is the
> channel disabled? CSA may request the STA to stop transmitting _within
> the BSS_ until the AP has changed channels (i.e., it could be possible
> to use other BSSes even on the same channel). This notification can be
> used for other things than just reporting a radar on the channel (e.g.,
> the AP could move to a channel that has less traffic on it).
>

Ok, agreed. I'll remove this flag.

But after receiving a CSA on the current frequency, and having moved to the new
advertised frequency, can the STA send out probe requests in the earlier frequency ?
(In case of a scan run or something)

Sujith

2008-12-18 07:18:48

by Jouni Malinen

[permalink] [raw]
Subject: Re: [RFC] Handle Channel Switch Announcement

On Thu, Dec 18, 2008 at 09:05:08AM +0200, Jouni Malinen wrote:
> received a CSA may not be that good of an idea. Then again, channels
> that require radar detection should not really allow active scanning on
> them in the first place.

Just remembered one topic related to this that I was supposed to include
here.. We should really start looking at using passive scanning more in
some cases. The channel itself may not be completely restricted from
active scanning, but especially when roaming to a new regulatory domain,
it would be better to only use passive scanning until some life is
found (i.e., a Beacon is received) and only enable active scanning after
having learned which channels are allowed. This is actually a
requirement in 802.11-2007, too (see 9.8.1).

When receiving a CSA on a channel that requires DFS, but does not
enforce strict passive-scanning-only policy (should there be such
channels somewhere), it could be a good policy to drop that channel out
from the currently allowed active-scan-channels and only add it back if
passive scan find an AP Beaconing on the channel regardless of whether
this is strictly speaking required or not by the standard. This type of
dynamic mode changes for channels may need some work in mac80211, but it
would probably be a good idea to do it.

--
Jouni Malinen PGP id EFC895FA

2008-12-17 18:22:50

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] Handle Channel Switch Announcement

On Wed, 2008-12-17 at 20:25 +0530, Sujith wrote:

> + * @IEEE80211_CHAN_CSA_DISABLED: Disabled because a
> + Channel Switch Announcement was received on this channel.

I agree with Jouni, this is probably not a good idea, especially since
this flag would be shared between hardware for those drivers that have
the channel structs allocated statically. We should probably just stick
a struct ieee80211_channel *switch_from; pointer somewhere into the
sdata struct or so.

> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -1612,6 +1612,13 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
> return RX_DROP_MONITOR;
> ieee80211_process_measurement_req(sdata, mgmt, len);
> break;
> + case WLAN_ACTION_SPCT_CHL_SWITCH:
> + if (len < (IEEE80211_MIN_ACTION_SIZE +
> + sizeof(mgmt->u.action.u.chan_switch)))
> + return RX_DROP_MONITOR;
> + ieee80211_process_chanswitch(sdata,
> + &mgmt->u.action.u.chan_switch.sw_elem);

I think I'd like to have them dropped to monitor for !station here,
instead of silently in the processing function.

> +
> +void ieee80211_chswitch_timer(unsigned long data)
> +{
> + struct ieee80211_bss *bss;
> + struct ieee80211_sub_if_data *sdata =
> + (struct ieee80211_sub_if_data *) data;
> + struct ieee80211_if_sta *ifsta = &sdata->u.sta;
> +
> + printk("expire\n");

That should probably be removed or be more specific & protected by some
debug #ifdef.

> +void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
> + struct ieee80211_channel_sw_ie *sw_elem)


> + } else {
> + ieee80211_stop_queues(&sdata->local->hw);

Can you rebase this on top of Kalle's patch that adds the "stop reasons"
for each queue? As it is now, the driver could wake up the queues again
if one was full.

johannes


2008-12-17 16:20:59

by Jouni Malinen

[permalink] [raw]
Subject: Re: [RFC] Handle Channel Switch Announcement

On Wed, Dec 17, 2008 at 08:25:34PM +0530, Sujith wrote:
> This is an attempt to handle CSA elements from an AP and switch
> to the specified channel.
>
> Only mode 1 is handled, i.e, we just stop transmission on receiving a CSA element.
> User space notification is not handled yet.

> diff --git a/include/net/wireless.h b/include/net/wireless.h
> + * @IEEE80211_CHAN_CSA_DISABLED: Disabled because a
> + Channel Switch Announcement was received on this channel.

Why is this flag needed? Or well, the better question: Why is the
channel disabled? CSA may request the STA to stop transmitting _within
the BSS_ until the AP has changed channels (i.e., it could be possible
to use other BSSes even on the same channel). This notification can be
used for other things than just reporting a radar on the channel (e.g.,
the AP could move to a channel that has less traffic on it).

--
Jouni Malinen PGP id EFC895FA

2008-12-18 04:02:59

by Sujith

[permalink] [raw]
Subject: Re: [RFC] Handle Channel Switch Announcement

Sujith wrote:
> Jouni Malinen wrote:
> > Why is this flag needed? Or well, the better question: Why is the
> > channel disabled? CSA may request the STA to stop transmitting _within
> > the BSS_ until the AP has changed channels (i.e., it could be possible
> > to use other BSSes even on the same channel). This notification can be
> > used for other things than just reporting a radar on the channel (e.g.,
> > the AP could move to a channel that has less traffic on it).
> >
>
> Ok, agreed. I'll remove this flag.
>
> But after receiving a CSA on the current frequency, and having moved to the new
> advertised frequency, can the STA send out probe requests in the earlier frequency ?
> (In case of a scan run or something)

If that is the case, then we should probably check for CSA received on a frequency
and not send out probe requests in that frequency during a scan run.

Something like:

case SCAN_SEND_PROBE:
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->scan_state = SCAN_SET_CHANNEL;

if (local->scan_channel->flags & (IEEE80211_CHAN_PASSIVE_SCAN |
IEEE80211_CHAN_CSA_DISABLED))
break;
ieee80211_send_probe_req(sdata, NULL, local->scan_ssid,
local->scan_ssid_len);
next_delay = IEEE80211_CHANNEL_TIME;
break;

But am not sure what constraints are imposed on the station on reception of a CSA.

Sujith

2008-12-18 07:05:21

by Jouni Malinen

[permalink] [raw]
Subject: Re: [RFC] Handle Channel Switch Announcement

On Thu, Dec 18, 2008 at 07:02:55AM +0530, Sujith wrote:

> But after receiving a CSA on the current frequency, and having moved to the new
> advertised frequency, can the STA send out probe requests in the earlier frequency ?
> (In case of a scan run or something)

As far as IEEE 802.11 standard is concerned, yes, that would be allowed.
There are not even explicit limits on doing that (if it is a broadcast
probe request, not unicast to the current BSS) before the channel move.
The only thing that the STA has to do immediately (if mode=1) is to
cease to send anything to the current BSS before the channel switch.
When receiving the CSA, the STA needs to decide whether it wants to
follow the AP or not. If not, it could start scanning immediately for
another AP.. The standard even has a following statement about channel
switch: "It should also be stressed that the channel switch process is
distinct from the regulatory requirement to cease transmission on a
particular channel in the presence of radar."

However, the STA must discontinue operation on the channel if it detects
a radar operating in the channel or learns this from another STA. The
regulatory rules on how quickly this must be done (and how much airtime
may be used after the radar has been detected) may be quire strict, so
doing an active scan on the previous channel immediately after having
received a CSA may not be that good of an idea. Then again, channels
that require radar detection should not really allow active scanning on
them in the first place. In that way it would be enough for the non-AP
STA to just stop using the current BSS because any other AP on the same
channel should stop Beaconing, too, when a radar is detected and as
such, the non-AP STA should not end up using the channel anymore.

--
Jouni Malinen PGP id EFC895FA