diff options
author | Thomas Pedersen <c_tpeder@qca.qualcomm.com> | 2012-04-06 13:35:48 -0700 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2012-04-12 09:55:52 +0300 |
commit | c4f7863eae6f580a0373cbd8dc2731d082570e69 (patch) | |
tree | 7fa45ad56b3cb202a082ccde40a5f29cf53b7498 /drivers/net/wireless/ath/ath6kl/main.c | |
parent | d968370ee7ce22583ab741961f0044d2922aa1a1 (diff) |
ath6kl: handle concurrent AP-STA channel switches
If an ath6kl AP vif is beaconing on one channel, and a STA vif
associates on a different channel, a WMI_DISCONNECT event will be sent
to the AP vif. Make the AP vif follow the STA interface, and notify
userspace.
kvalo: fix a sparse warning with vif->next_chan
Signed-off-by: Thomas Pedersen <c_tpeder@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/main.c')
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/main.c | 55 |
1 files changed, 54 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 4d818f96c415..4602be7ce23b 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -436,6 +436,13 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) break; } + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) { + ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); + /* we actually don't know the phymode, default to HT20 */ + ath6kl_cfg80211_ch_switch_notify(vif, channel, + WMI_11G_HT20); + } + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); set_bit(CONNECTED, &vif->flags); netif_carrier_on(vif->ndev); @@ -584,6 +591,45 @@ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); } +static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) +{ + + struct ath6kl *ar = vif->ar; + + vif->next_chan = channel; + vif->profile.ch = cpu_to_le16(channel); + + switch (vif->nw_type) { + case AP_NETWORK: + return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, + &vif->profile); + default: + ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type); + return -ENOTSUPP; + } +} + +static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) +{ + + struct ath6kl_vif *vif; + int res = 0; + + if (!ar->want_ch_switch) + return; + + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) + res = ath6kl_commit_ch_switch(vif, channel); + + if (res) + ath6kl_err("channel switch failed nw_type %d res %d\n", + vif->nw_type, res); + } + spin_unlock_bh(&ar->list_lock); +} + void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, u16 listen_int, u16 beacon_int, enum network_type net_type, u8 beacon_ie_len, @@ -601,9 +647,11 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, memcpy(vif->bssid, bssid, sizeof(vif->bssid)); vif->bss_ch = channel; - if ((vif->nw_type == INFRA_NETWORK)) + if ((vif->nw_type == INFRA_NETWORK)) { ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, vif->listen_intvl_t, 0); + ath6kl_check_ch_switch(ar, channel); + } netif_wake_queue(vif->ndev); @@ -926,6 +974,11 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, struct ath6kl *ar = vif->ar; if (vif->nw_type == AP_NETWORK) { + /* disconnect due to other STA vif switching channels */ + if (reason == BSS_DISCONNECTED && + prot_reason_status == WMI_AP_REASON_STA_ROAM) + ar->want_ch_switch |= 1 << vif->fw_vif_idx; + if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; |