2008-12-31 10:12:47

by Sujith

[permalink] [raw]
Subject: [PATCH] mac80211: Add 802.11h CSA support

Move to the advertised channel on reception of
a CSA element. This is needed for 802.11h compliance.

Signed-off-by: Sujith <[email protected]>
---
net/mac80211/ieee80211_i.h | 9 +++++
net/mac80211/iface.c | 2 +
net/mac80211/mlme.c | 10 ++++++
net/mac80211/rx.c | 17 ++++++++++
net/mac80211/spectmgmt.c | 77 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 115 insertions(+), 0 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 117718b..a238a1d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -259,6 +259,7 @@ struct mesh_preq_queue {
#define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12)
#define IEEE80211_STA_PRIVACY_INVOKED BIT(13)
#define IEEE80211_STA_TKIP_WEP_USED BIT(14)
+#define IEEE80211_STA_CSA_RECEIVED BIT(15)
/* flags for MLME request */
#define IEEE80211_STA_REQ_SCAN 0
#define IEEE80211_STA_REQ_DIRECT_PROBE 1
@@ -283,7 +284,9 @@ enum ieee80211_sta_mlme_state {

struct ieee80211_if_sta {
struct timer_list timer;
+ struct timer_list chswitch_timer;
struct work_struct work;
+ struct work_struct chswitch_work;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
enum ieee80211_sta_mlme_state state;
@@ -542,6 +545,7 @@ enum {
enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_DRIVER,
IEEE80211_QUEUE_STOP_REASON_PS,
+ IEEE80211_QUEUE_STOP_REASON_CSA
};

/* maximum number of hardware queues we support. */
@@ -964,6 +968,11 @@ 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_chswitch_work(struct work_struct *work);
+void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_sw_ie *sw_elem,
+ struct ieee80211_bss *bss);

/* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 2c7a87d..f927641 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -443,6 +443,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
@@ -452,6 +453,7 @@ static int ieee80211_stop(struct net_device *dev)
* it no longer is.
*/
cancel_work_sync(&sdata->u.sta.work);
+ cancel_work_sync(&sdata->u.sta.chswitch_work);
/*
* When we get here, the interface is marked down.
* Call synchronize_rcu() to wait for the RX path
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index e317c6d..c611105 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1645,6 +1645,13 @@ 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) &&
+ (memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0)) {
+ struct ieee80211_channel_sw_ie *sw_elem =
+ (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
+ ieee80211_process_chanswitch(sdata, sw_elem, bss);
+ }
+
/* was just updated in ieee80211_bss_info_update */
beacon_timestamp = bss->timestamp;

@@ -2447,8 +2454,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)

ifsta = &sdata->u.sta;
INIT_WORK(&ifsta->work, ieee80211_sta_work);
+ INIT_WORK(&ifsta->chswitch_work, ieee80211_chswitch_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 384cb3b..c8daf8a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1564,7 +1564,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
+ struct ieee80211_bss *bss;
int len = rx->skb->len;

if (!ieee80211_is_action(mgmt->frame_control))
@@ -1613,6 +1615,21 @@ 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;
+
+ bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+ local->hw.conf.channel->center_freq,
+ ifsta->ssid, ifsta->ssid_len);
+ if (!bss)
+ return RX_DROP_MONITOR;
+
+ ieee80211_process_chanswitch(sdata,
+ &mgmt->u.action.u.chan_switch.sw_elem, bss);
+ ieee80211_rx_bss_put(local, bss);
+ break;
}
break;
default:
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index f72bad6..3977050 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -84,3 +84,80 @@ 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_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, u.sta.chswitch_work);
+ struct ieee80211_bss *bss;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+
+ 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:
+ ifsta->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+}
+
+void ieee80211_chswitch_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata =
+ (struct ieee80211_sub_if_data *) data;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+
+ queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work);
+}
+
+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;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
+ int exp;
+
+ /* FIXME: Handle ADHOC later */
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATED)
+ return;
+
+ if (sdata->local->sw_scanning || sdata->local->hw_scanning)
+ return;
+
+ /* Disregard subsequent beacons if we are already running a timer
+ processing a CSA */
+
+ if (ifsta->flags & IEEE80211_STA_CSA_RECEIVED)
+ return;
+
+ new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
+ if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
+ return;
+
+ sdata->local->oper_channel = new_ch;
+
+ if (sw_elem->count <= 1) {
+ queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work);
+ } else {
+ ieee80211_stop_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+ ifsta->flags |= IEEE80211_STA_CSA_RECEIVED;
+ exp = sw_elem->count * (1024 * bss->beacon_int / 1000 * HZ / 1000);
+ mod_timer(&ifsta->chswitch_timer, jiffies + exp);
+ }
+}
--
1.6.0.3



2008-12-31 10:19:41

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH] mac80211: Add 802.11h CSA support

On Wed, 2008-12-31 at 15:39 +0530, Sujith wrote:
> Move to the advertised channel on reception of
> a CSA element. This is needed for 802.11h compliance.
>
> Signed-off-by: Sujith <[email protected]>

One thing I've been wondering about though, should we have a hook into
the driver to actually stop transmit at a lower level, where possible?
For example Broadcom hardware we can actually set some value on the chip
and then let it reject all frames, drain its FIFOs, and then in the
driver we could schedule those frames again for afterwards.

One other small note:

> + 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;

This looks a little odd. (read on)


> +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;
> + struct ieee80211_if_sta *ifsta = &sdata->u.sta;
> + int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
> + int exp;
> +
> + /* FIXME: Handle ADHOC later */
> + if (sdata->vif.type != NL80211_IFTYPE_STATION)
> + return;
> +
> + if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATED)
> + return;
> +
> + if (sdata->local->sw_scanning || sdata->local->hw_scanning)
> + return;
> +
> + /* Disregard subsequent beacons if we are already running a timer
> + processing a CSA */
> +
> + if (ifsta->flags & IEEE80211_STA_CSA_RECEIVED)
> + return;
> +
> + new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
> + if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
> + return;
> +
> + sdata->local->oper_channel = new_ch;

Here you're setting oper_channel, but is that really the right thing to
do? That means any hw config call that comes in between this and later
will already switch to that channel, and hw config can happen for
various other reasons, for instance PS mode stuff. I think we may need
to have a separate variable?

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2008-12-31 10:24:38

by Tomas Winkler

[permalink] [raw]
Subject: Re: [PATCH] mac80211: Add 802.11h CSA support

> + } else {
> + ieee80211_stop_queues_by_reason(&sdata->local->hw,
> + IEEE80211_QUEUE_STOP_REASON_CSA);
Why to stop the queues before the switching time

> + ifsta->flags |= IEEE80211_STA_CSA_RECEIVED;
> + exp = sw_elem->count * (1024 * bss->beacon_int / 1000 * HZ / 1000);
> + mod_timer(&ifsta->chswitch_timer, jiffies + exp);
What about:
mod_timer(&ifsta->chswitch_timer, jiffies +
msecs_to_jiffies(sw_elm->count * local->hw.conf.beacon_int));

> + }
> +}

Thanks
Tomas

2008-12-31 10:46:14

by Sujith

[permalink] [raw]
Subject: Re: [PATCH] mac80211: Add 802.11h CSA support

Tomas Winkler wrote:
> > + } else {
> > + ieee80211_stop_queues_by_reason(&sdata->local->hw,
> > + IEEE80211_QUEUE_STOP_REASON_CSA);
> Why to stop the queues before the switching time
>

Transmission of frames has to be stopped until the channel is switched.

> > + ifsta->flags |= IEEE80211_STA_CSA_RECEIVED;
> > + exp = sw_elem->count * (1024 * bss->beacon_int / 1000 * HZ / 1000);
> > + mod_timer(&ifsta->chswitch_timer, jiffies + exp);
> What about:
> mod_timer(&ifsta->chswitch_timer, jiffies +
> msecs_to_jiffies(sw_elm->count * local->hw.conf.beacon_int));
>

This is cleaner, I'll add this, thanks.

Sujith

2008-12-31 10:43:49

by Sujith

[permalink] [raw]
Subject: Re: [PATCH] mac80211: Add 802.11h CSA support

Johannes Berg wrote:
> On Wed, 2008-12-31 at 15:39 +0530, Sujith wrote:
> > Move to the advertised channel on reception of
> > a CSA element. This is needed for 802.11h compliance.
> >
> > Signed-off-by: Sujith <[email protected]>
>
> One thing I've been wondering about though, should we have a hook into
> the driver to actually stop transmit at a lower level, where possible?
> For example Broadcom hardware we can actually set some value on the chip
> and then let it reject all frames, drain its FIFOs, and then in the
> driver we could schedule those frames again for afterwards.

I think that is needed only when handling Quiet IE, which I was planning to work
on separately, with a notification to the driver to stop transmission completely for a period of time.

> > + sdata->local->oper_channel = new_ch;
>
> Here you're setting oper_channel, but is that really the right thing to
> do? That means any hw config call that comes in between this and later
> will already switch to that channel, and hw config can happen for
> various other reasons, for instance PS mode stuff. I think we may need
> to have a separate variable?

Right, a *switch_to in sdata would suffice, I guess.
I'll send an updated patch.

Sujith

2009-01-06 13:04:56

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH] mac80211: Add 802.11h CSA support

On Tue, 2009-01-06 at 09:28 +0530, Sujith wrote:
> Move to the advertised channel on reception of
> a CSA element. This is needed for 802.11h compliance.
>
> Signed-off-by: Sujith <[email protected]>

Looks good to me now, sorry about the ping pong.

Acked-by: Johannes Berg <[email protected]>

> ---
> v2
> --
> * Add a new variable to hold the CSA channel
> * Use msecs_to_jiffies for calculating expiration time
> * Add a check to drop beacons in case of a frequency mismatch
>
> v3
> --
> * Add a BSSID check when handling CSA action frame
>
> net/mac80211/ieee80211_i.h | 11 ++++++-
> net/mac80211/iface.c | 2 +
> net/mac80211/mlme.c | 13 +++++++
> net/mac80211/rx.c | 20 +++++++++++
> net/mac80211/spectmgmt.c | 77 ++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 122 insertions(+), 1 deletions(-)
>
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index 117718b..d2a007a 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -259,6 +259,7 @@ struct mesh_preq_queue {
> #define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12)
> #define IEEE80211_STA_PRIVACY_INVOKED BIT(13)
> #define IEEE80211_STA_TKIP_WEP_USED BIT(14)
> +#define IEEE80211_STA_CSA_RECEIVED BIT(15)
> /* flags for MLME request */
> #define IEEE80211_STA_REQ_SCAN 0
> #define IEEE80211_STA_REQ_DIRECT_PROBE 1
> @@ -283,7 +284,9 @@ enum ieee80211_sta_mlme_state {
>
> struct ieee80211_if_sta {
> struct timer_list timer;
> + struct timer_list chswitch_timer;
> struct work_struct work;
> + struct work_struct chswitch_work;
> u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
> u8 ssid[IEEE80211_MAX_SSID_LEN];
> enum ieee80211_sta_mlme_state state;
> @@ -542,6 +545,7 @@ enum {
> enum queue_stop_reason {
> IEEE80211_QUEUE_STOP_REASON_DRIVER,
> IEEE80211_QUEUE_STOP_REASON_PS,
> + IEEE80211_QUEUE_STOP_REASON_CSA
> };
>
> /* maximum number of hardware queues we support. */
> @@ -631,7 +635,7 @@ struct ieee80211_local {
> unsigned long last_scan_completed;
> struct delayed_work scan_work;
> struct ieee80211_sub_if_data *scan_sdata;
> - struct ieee80211_channel *oper_channel, *scan_channel;
> + struct ieee80211_channel *oper_channel, *scan_channel, *csa_channel;
> enum nl80211_channel_type oper_channel_type;
> u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
> size_t scan_ssid_len;
> @@ -964,6 +968,11 @@ 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_chswitch_work(struct work_struct *work);
> +void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
> + struct ieee80211_channel_sw_ie *sw_elem,
> + struct ieee80211_bss *bss);
>
> /* utility functions/constants */
> extern void *mac80211_wiphy_privid; /* for wiphy privid */
> diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
> index 2c7a87d..f927641 100644
> --- a/net/mac80211/iface.c
> +++ b/net/mac80211/iface.c
> @@ -443,6 +443,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
> @@ -452,6 +453,7 @@ static int ieee80211_stop(struct net_device *dev)
> * it no longer is.
> */
> cancel_work_sync(&sdata->u.sta.work);
> + cancel_work_sync(&sdata->u.sta.chswitch_work);
> /*
> * When we get here, the interface is marked down.
> * Call synchronize_rcu() to wait for the RX path
> diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
> index e317c6d..4d25cf9 100644
> --- a/net/mac80211/mlme.c
> +++ b/net/mac80211/mlme.c
> @@ -1645,6 +1645,13 @@ 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) &&
> + (memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0)) {
> + struct ieee80211_channel_sw_ie *sw_elem =
> + (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
> + ieee80211_process_chanswitch(sdata, sw_elem, bss);
> + }
> +
> /* was just updated in ieee80211_bss_info_update */
> beacon_timestamp = bss->timestamp;
>
> @@ -1781,6 +1788,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
> memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0)
> return;
>
> + if (rx_status->freq != local->hw.conf.channel->center_freq)
> + return;
> +
> ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
> elems.wmm_param_len);
>
> @@ -2447,8 +2457,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
>
> ifsta = &sdata->u.sta;
> INIT_WORK(&ifsta->work, ieee80211_sta_work);
> + INIT_WORK(&ifsta->chswitch_work, ieee80211_chswitch_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 384cb3b..4b7bc3d 100644
> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -1564,7 +1564,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
> {
> struct ieee80211_local *local = rx->local;
> struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
> + struct ieee80211_if_sta *ifsta = &sdata->u.sta;
> struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
> + struct ieee80211_bss *bss;
> int len = rx->skb->len;
>
> if (!ieee80211_is_action(mgmt->frame_control))
> @@ -1613,6 +1615,24 @@ 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;
> +
> + if (memcmp(mgmt->bssid, ifsta->bssid, ETH_ALEN) != 0)
> + return RX_DROP_MONITOR;
> +
> + bss = ieee80211_rx_bss_get(local, ifsta->bssid,
> + local->hw.conf.channel->center_freq,
> + ifsta->ssid, ifsta->ssid_len);
> + if (!bss)
> + return RX_DROP_MONITOR;
> +
> + ieee80211_process_chanswitch(sdata,
> + &mgmt->u.action.u.chan_switch.sw_elem, bss);
> + ieee80211_rx_bss_put(local, bss);
> + break;
> }
> break;
> default:
> diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
> index f72bad6..22ad480 100644
> --- a/net/mac80211/spectmgmt.c
> +++ b/net/mac80211/spectmgmt.c
> @@ -84,3 +84,80 @@ 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_work(struct work_struct *work)
> +{
> + struct ieee80211_sub_if_data *sdata =
> + container_of(work, struct ieee80211_sub_if_data, u.sta.chswitch_work);
> + struct ieee80211_bss *bss;
> + struct ieee80211_if_sta *ifsta = &sdata->u.sta;
> +
> + 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;
> +
> + sdata->local->oper_channel = sdata->local->csa_channel;
> + 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:
> + ifsta->flags &= ~IEEE80211_STA_CSA_RECEIVED;
> + ieee80211_wake_queues_by_reason(&sdata->local->hw,
> + IEEE80211_QUEUE_STOP_REASON_CSA);
> +}
> +
> +void ieee80211_chswitch_timer(unsigned long data)
> +{
> + struct ieee80211_sub_if_data *sdata =
> + (struct ieee80211_sub_if_data *) data;
> + struct ieee80211_if_sta *ifsta = &sdata->u.sta;
> +
> + queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work);
> +}
> +
> +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;
> + struct ieee80211_if_sta *ifsta = &sdata->u.sta;
> + 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;
> +
> + if (sdata->local->sw_scanning || sdata->local->hw_scanning)
> + return;
> +
> + /* Disregard subsequent beacons if we are already running a timer
> + processing a CSA */
> +
> + if (ifsta->flags & IEEE80211_STA_CSA_RECEIVED)
> + return;
> +
> + new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
> + if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
> + return;
> +
> + sdata->local->csa_channel = new_ch;
> +
> + if (sw_elem->count <= 1) {
> + queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work);
> + } else {
> + ieee80211_stop_queues_by_reason(&sdata->local->hw,
> + IEEE80211_QUEUE_STOP_REASON_CSA);
> + ifsta->flags |= IEEE80211_STA_CSA_RECEIVED;
> + mod_timer(&ifsta->chswitch_timer,
> + jiffies + msecs_to_jiffies(sw_elem->count * bss->beacon_int));
> + }
> +}


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part