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>
This commit is contained in:
parent
d968370ee7
commit
c4f7863eae
@ -1018,6 +1018,20 @@ out:
|
|||||||
vif->scan_req = NULL;
|
vif->scan_req = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
|
||||||
|
enum wmi_phy_mode mode)
|
||||||
|
{
|
||||||
|
enum nl80211_channel_type type;
|
||||||
|
|
||||||
|
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
|
||||||
|
"channel switch notify nw_type %d freq %d mode %d\n",
|
||||||
|
vif->nw_type, freq, mode);
|
||||||
|
|
||||||
|
type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT;
|
||||||
|
|
||||||
|
cfg80211_ch_switch_notify(vif->ndev, freq, type);
|
||||||
|
}
|
||||||
|
|
||||||
static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
|
static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
|
||||||
u8 key_index, bool pairwise,
|
u8 key_index, bool pairwise,
|
||||||
const u8 *mac_addr,
|
const u8 *mac_addr,
|
||||||
@ -2766,6 +2780,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(&vif->profile, &p, sizeof(p));
|
||||||
res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
|
res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
return res;
|
return res;
|
||||||
|
@ -28,6 +28,8 @@ enum ath6kl_cfg_suspend_mode {
|
|||||||
struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
|
struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
|
||||||
enum nl80211_iftype type,
|
enum nl80211_iftype type,
|
||||||
u8 fw_vif_idx, u8 nw_type);
|
u8 fw_vif_idx, u8 nw_type);
|
||||||
|
void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
|
||||||
|
enum wmi_phy_mode mode);
|
||||||
void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted);
|
void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted);
|
||||||
|
|
||||||
void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
|
void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
|
||||||
|
@ -552,6 +552,7 @@ struct ath6kl_vif {
|
|||||||
u8 assoc_bss_dtim_period;
|
u8 assoc_bss_dtim_period;
|
||||||
struct net_device_stats net_stats;
|
struct net_device_stats net_stats;
|
||||||
struct target_stats target_stats;
|
struct target_stats target_stats;
|
||||||
|
struct wmi_connect_cmd profile;
|
||||||
|
|
||||||
struct list_head mc_filter;
|
struct list_head mc_filter;
|
||||||
};
|
};
|
||||||
@ -640,6 +641,7 @@ struct ath6kl {
|
|||||||
u8 sta_list_index;
|
u8 sta_list_index;
|
||||||
struct ath6kl_req_key ap_mode_bkey;
|
struct ath6kl_req_key ap_mode_bkey;
|
||||||
struct sk_buff_head mcastpsq;
|
struct sk_buff_head mcastpsq;
|
||||||
|
u32 want_ch_switch;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FIXME: protects access to mcastpsq but is actually useless as
|
* FIXME: protects access to mcastpsq but is actually useless as
|
||||||
|
@ -436,6 +436,13 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel)
|
|||||||
break;
|
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);
|
ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0);
|
||||||
set_bit(CONNECTED, &vif->flags);
|
set_bit(CONNECTED, &vif->flags);
|
||||||
netif_carrier_on(vif->ndev);
|
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);
|
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,
|
void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid,
|
||||||
u16 listen_int, u16 beacon_int,
|
u16 listen_int, u16 beacon_int,
|
||||||
enum network_type net_type, u8 beacon_ie_len,
|
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));
|
memcpy(vif->bssid, bssid, sizeof(vif->bssid));
|
||||||
vif->bss_ch = channel;
|
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,
|
ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
|
||||||
vif->listen_intvl_t, 0);
|
vif->listen_intvl_t, 0);
|
||||||
|
ath6kl_check_ch_switch(ar, channel);
|
||||||
|
}
|
||||||
|
|
||||||
netif_wake_queue(vif->ndev);
|
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;
|
struct ath6kl *ar = vif->ar;
|
||||||
|
|
||||||
if (vif->nw_type == AP_NETWORK) {
|
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))
|
if (!ath6kl_remove_sta(ar, bssid, prot_reason_status))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1151,6 +1151,7 @@ enum wmi_phy_mode {
|
|||||||
WMI_11AG_MODE = 0x3,
|
WMI_11AG_MODE = 0x3,
|
||||||
WMI_11B_MODE = 0x4,
|
WMI_11B_MODE = 0x4,
|
||||||
WMI_11GONLY_MODE = 0x5,
|
WMI_11GONLY_MODE = 0x5,
|
||||||
|
WMI_11G_HT20 = 0x6,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define WMI_MAX_CHANNELS 32
|
#define WMI_MAX_CHANNELS 32
|
||||||
@ -1468,6 +1469,17 @@ enum wmi_disconnect_reason {
|
|||||||
IBSS_MERGE = 0xe,
|
IBSS_MERGE = 0xe,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* AP mode disconnect proto_reasons */
|
||||||
|
enum ap_disconnect_reason {
|
||||||
|
WMI_AP_REASON_STA_LEFT = 101,
|
||||||
|
WMI_AP_REASON_FROM_HOST = 102,
|
||||||
|
WMI_AP_REASON_COMM_TIMEOUT = 103,
|
||||||
|
WMI_AP_REASON_MAX_STA = 104,
|
||||||
|
WMI_AP_REASON_ACL = 105,
|
||||||
|
WMI_AP_REASON_STA_ROAM = 106,
|
||||||
|
WMI_AP_REASON_DFS_CHANNEL = 107,
|
||||||
|
};
|
||||||
|
|
||||||
#define ATH6KL_COUNTRY_RD_SHIFT 16
|
#define ATH6KL_COUNTRY_RD_SHIFT 16
|
||||||
|
|
||||||
struct ath6kl_wmi_regdomain {
|
struct ath6kl_wmi_regdomain {
|
||||||
|
Loading…
Reference in New Issue
Block a user