Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Thomas Pedersen and committed by
Kalle Valo
c4f7863e d968370e

+85 -1
+15
drivers/net/wireless/ath/ath6kl/cfg80211.c
··· 1018 1018 vif->scan_req = NULL; 1019 1019 } 1020 1020 1021 + void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, 1022 + enum wmi_phy_mode mode) 1023 + { 1024 + enum nl80211_channel_type type; 1025 + 1026 + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 1027 + "channel switch notify nw_type %d freq %d mode %d\n", 1028 + vif->nw_type, freq, mode); 1029 + 1030 + type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; 1031 + 1032 + cfg80211_ch_switch_notify(vif->ndev, freq, type); 1033 + } 1034 + 1021 1035 static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, 1022 1036 u8 key_index, bool pairwise, 1023 1037 const u8 *mac_addr, ··· 2780 2766 return res; 2781 2767 } 2782 2768 2769 + memcpy(&vif->profile, &p, sizeof(p)); 2783 2770 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); 2784 2771 if (res < 0) 2785 2772 return res;
+2
drivers/net/wireless/ath/ath6kl/cfg80211.h
··· 28 28 struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, 29 29 enum nl80211_iftype type, 30 30 u8 fw_vif_idx, u8 nw_type); 31 + void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, 32 + enum wmi_phy_mode mode); 31 33 void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); 32 34 33 35 void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
+2
drivers/net/wireless/ath/ath6kl/core.h
··· 552 552 u8 assoc_bss_dtim_period; 553 553 struct net_device_stats net_stats; 554 554 struct target_stats target_stats; 555 + struct wmi_connect_cmd profile; 555 556 556 557 struct list_head mc_filter; 557 558 }; ··· 641 640 u8 sta_list_index; 642 641 struct ath6kl_req_key ap_mode_bkey; 643 642 struct sk_buff_head mcastpsq; 643 + u32 want_ch_switch; 644 644 645 645 /* 646 646 * FIXME: protects access to mcastpsq but is actually useless as
+54 -1
drivers/net/wireless/ath/ath6kl/main.c
··· 436 436 break; 437 437 } 438 438 439 + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) { 440 + ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); 441 + /* we actually don't know the phymode, default to HT20 */ 442 + ath6kl_cfg80211_ch_switch_notify(vif, channel, 443 + WMI_11G_HT20); 444 + } 445 + 439 446 ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); 440 447 set_bit(CONNECTED, &vif->flags); 441 448 netif_carrier_on(vif->ndev); ··· 591 584 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); 592 585 } 593 586 587 + static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) 588 + { 589 + 590 + struct ath6kl *ar = vif->ar; 591 + 592 + vif->next_chan = channel; 593 + vif->profile.ch = cpu_to_le16(channel); 594 + 595 + switch (vif->nw_type) { 596 + case AP_NETWORK: 597 + return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, 598 + &vif->profile); 599 + default: 600 + ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type); 601 + return -ENOTSUPP; 602 + } 603 + } 604 + 605 + static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) 606 + { 607 + 608 + struct ath6kl_vif *vif; 609 + int res = 0; 610 + 611 + if (!ar->want_ch_switch) 612 + return; 613 + 614 + spin_lock_bh(&ar->list_lock); 615 + list_for_each_entry(vif, &ar->vif_list, list) { 616 + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) 617 + res = ath6kl_commit_ch_switch(vif, channel); 618 + 619 + if (res) 620 + ath6kl_err("channel switch failed nw_type %d res %d\n", 621 + vif->nw_type, res); 622 + } 623 + spin_unlock_bh(&ar->list_lock); 624 + } 625 + 594 626 void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, 595 627 u16 listen_int, u16 beacon_int, 596 628 enum network_type net_type, u8 beacon_ie_len, ··· 647 601 memcpy(vif->bssid, bssid, sizeof(vif->bssid)); 648 602 vif->bss_ch = channel; 649 603 650 - if ((vif->nw_type == INFRA_NETWORK)) 604 + if ((vif->nw_type == INFRA_NETWORK)) { 651 605 ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, 652 606 vif->listen_intvl_t, 0); 607 + ath6kl_check_ch_switch(ar, channel); 608 + } 653 609 654 610 netif_wake_queue(vif->ndev); 655 611 ··· 974 926 struct ath6kl *ar = vif->ar; 975 927 976 928 if (vif->nw_type == AP_NETWORK) { 929 + /* disconnect due to other STA vif switching channels */ 930 + if (reason == BSS_DISCONNECTED && 931 + prot_reason_status == WMI_AP_REASON_STA_ROAM) 932 + ar->want_ch_switch |= 1 << vif->fw_vif_idx; 933 + 977 934 if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) 978 935 return; 979 936
+12
drivers/net/wireless/ath/ath6kl/wmi.h
··· 1151 1151 WMI_11AG_MODE = 0x3, 1152 1152 WMI_11B_MODE = 0x4, 1153 1153 WMI_11GONLY_MODE = 0x5, 1154 + WMI_11G_HT20 = 0x6, 1154 1155 }; 1155 1156 1156 1157 #define WMI_MAX_CHANNELS 32 ··· 1467 1466 PROFILE_MISMATCH = 0x0c, 1468 1467 CONNECTION_EVICTED = 0x0d, 1469 1468 IBSS_MERGE = 0xe, 1469 + }; 1470 + 1471 + /* AP mode disconnect proto_reasons */ 1472 + enum ap_disconnect_reason { 1473 + WMI_AP_REASON_STA_LEFT = 101, 1474 + WMI_AP_REASON_FROM_HOST = 102, 1475 + WMI_AP_REASON_COMM_TIMEOUT = 103, 1476 + WMI_AP_REASON_MAX_STA = 104, 1477 + WMI_AP_REASON_ACL = 105, 1478 + WMI_AP_REASON_STA_ROAM = 106, 1479 + WMI_AP_REASON_DFS_CHANNEL = 107, 1470 1480 }; 1471 1481 1472 1482 #define ATH6KL_COUNTRY_RD_SHIFT 16