diff options
author | Sujith <Sujith.Manoharan@atheros.com> | 2009-01-06 09:28:37 +0530 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-29 15:59:50 -0500 |
commit | c481ec9705d4a5d566393bc17374cfd82c870715 (patch) | |
tree | 383b90aa8cf172ee81a7e91c49440cf75c8c0278 /net/mac80211/spectmgmt.c | |
parent | b522ed56ef90f5078a2a1253e390299723510a89 (diff) | |
download | lwn-c481ec9705d4a5d566393bc17374cfd82c870715.tar.gz lwn-c481ec9705d4a5d566393bc17374cfd82c870715.zip |
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 <Sujith.Manoharan@atheros.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/spectmgmt.c')
-rw-r--r-- | net/mac80211/spectmgmt.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index f72bad636d8e..22ad4808e01a 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)); + } +} |