summaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-05-15 11:52:31 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-05-20 14:46:25 -0400
commitcc32abd494c0a8f76f2638e3f3a76e01c68bc9ea (patch)
tree02e6870c634e637c7082596f4e3d23804285c8de /net/mac80211/mlme.c
parent9f26a952210e44691f784b77bf1f83a500d63f58 (diff)
mac80211: move channel switch code
The channel switch code is currently in the spectrum management file, where arguably it belongs. However, it is for managed mode only and uses the structures for that mode only so having it in a more generic file can be confusing. Additionally, my next patch gets simpler with the code here. When/if we ever implement this for IBSS or mesh then we will need to rework the structures it uses anyway at which point we could move the code back. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c99
1 files changed, 98 insertions, 1 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 47bc3030ca8..41f3c1f98cc 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -486,6 +486,103 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
ieee80211_tx_skb(sdata, skb, 0);
}
+/* spectrum management related things */
+static void ieee80211_chswitch_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
+ struct ieee80211_bss *bss;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ if (!netif_running(sdata->dev))
+ return;
+
+ bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
+ sdata->local->hw.conf.channel->center_freq,
+ ifmgd->ssid, ifmgd->ssid_len);
+ if (!bss)
+ goto exit;
+
+ sdata->local->oper_channel = sdata->local->csa_channel;
+ /* XXX: shouldn't really modify cfg80211-owned data! */
+ if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
+ bss->cbss.channel = sdata->local->oper_channel;
+
+ ieee80211_rx_bss_put(sdata->local, bss);
+exit:
+ ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+}
+
+static void ieee80211_chswitch_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata =
+ (struct ieee80211_sub_if_data *) data;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ queue_work(sdata->local->hw.workqueue, &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)
+{
+ 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);
+
+ if (ifmgd->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 (ifmgd->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, &ifmgd->chswitch_work);
+ } else {
+ ieee80211_stop_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+ ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+ mod_timer(&ifmgd->chswitch_timer,
+ jiffies +
+ msecs_to_jiffies(sw_elem->count *
+ bss->cbss.beacon_interval));
+ }
+}
+
+static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
+ u16 capab_info, u8 *pwr_constr_elem,
+ u8 pwr_constr_elem_len)
+{
+ struct ieee80211_conf *conf = &sdata->local->hw.conf;
+
+ if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
+ return;
+
+ /* Power constraint IE length should be 1 octet */
+ if (pwr_constr_elem_len != 1)
+ return;
+
+ if ((*pwr_constr_elem <= conf->channel->max_power) &&
+ (*pwr_constr_elem != sdata->local->power_constr_level)) {
+ sdata->local->power_constr_level = *pwr_constr_elem;
+ ieee80211_hw_config(sdata->local, 0);
+ }
+}
+
/* powersave */
static void ieee80211_enable_ps(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
@@ -1736,7 +1833,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
(memcmp(mgmt->bssid, sdata->u.mgd.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);
+ ieee80211_sta_process_chanswitch(sdata, sw_elem, bss);
}
ieee80211_rx_bss_put(local, bss);