diff options
author | Thomas Pedersen <c_tpeder@qca.qualcomm.com> | 2012-04-06 16:35:48 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2012-04-12 02:55:52 -0400 |
commit | c4f7863eae6f580a0373cbd8dc2731d082570e69 (patch) | |
tree | 7fa45ad56b3cb202a082ccde40a5f29cf53b7498 | |
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>
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/cfg80211.c | 15 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/cfg80211.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/core.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/main.c | 55 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/wmi.h | 12 |
5 files changed, 85 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 28a65d3a03d0..6ea5ae54c160 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c | |||
@@ -1018,6 +1018,20 @@ out: | |||
1018 | vif->scan_req = NULL; | 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 | static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, | 1035 | static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, |
1022 | u8 key_index, bool pairwise, | 1036 | u8 key_index, bool pairwise, |
1023 | const u8 *mac_addr, | 1037 | const u8 *mac_addr, |
@@ -2766,6 +2780,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, | |||
2766 | return res; | 2780 | return res; |
2767 | } | 2781 | } |
2768 | 2782 | ||
2783 | memcpy(&vif->profile, &p, sizeof(p)); | ||
2769 | res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); | 2784 | res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); |
2770 | if (res < 0) | 2785 | if (res < 0) |
2771 | return res; | 2786 | return res; |
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index c5def436417f..5ea8cbb79f43 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h | |||
@@ -28,6 +28,8 @@ enum ath6kl_cfg_suspend_mode { | |||
28 | struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, | 28 | struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, |
29 | enum nl80211_iftype type, | 29 | enum nl80211_iftype type, |
30 | u8 fw_vif_idx, u8 nw_type); | 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 | void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); | 33 | void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); |
32 | 34 | ||
33 | void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, | 35 | void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, |
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 9d67964a51dd..8ca393fc9f18 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h | |||
@@ -552,6 +552,7 @@ struct ath6kl_vif { | |||
552 | u8 assoc_bss_dtim_period; | 552 | u8 assoc_bss_dtim_period; |
553 | struct net_device_stats net_stats; | 553 | struct net_device_stats net_stats; |
554 | struct target_stats target_stats; | 554 | struct target_stats target_stats; |
555 | struct wmi_connect_cmd profile; | ||
555 | 556 | ||
556 | struct list_head mc_filter; | 557 | struct list_head mc_filter; |
557 | }; | 558 | }; |
@@ -640,6 +641,7 @@ struct ath6kl { | |||
640 | u8 sta_list_index; | 641 | u8 sta_list_index; |
641 | struct ath6kl_req_key ap_mode_bkey; | 642 | struct ath6kl_req_key ap_mode_bkey; |
642 | struct sk_buff_head mcastpsq; | 643 | struct sk_buff_head mcastpsq; |
644 | u32 want_ch_switch; | ||
643 | 645 | ||
644 | /* | 646 | /* |
645 | * FIXME: protects access to mcastpsq but is actually useless as | 647 | * FIXME: protects access to mcastpsq but is actually useless as |
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) | |||
436 | break; | 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 | ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); | 446 | ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); |
440 | set_bit(CONNECTED, &vif->flags); | 447 | set_bit(CONNECTED, &vif->flags); |
441 | netif_carrier_on(vif->ndev); | 448 | netif_carrier_on(vif->ndev); |
@@ -584,6 +591,45 @@ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) | |||
584 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); | 591 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); |
585 | } | 592 | } |
586 | 593 | ||
594 | static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) | ||
595 | { | ||
596 | |||
597 | struct ath6kl *ar = vif->ar; | ||
598 | |||
599 | vif->next_chan = channel; | ||
600 | vif->profile.ch = cpu_to_le16(channel); | ||
601 | |||
602 | switch (vif->nw_type) { | ||
603 | case AP_NETWORK: | ||
604 | return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, | ||
605 | &vif->profile); | ||
606 | default: | ||
607 | ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type); | ||
608 | return -ENOTSUPP; | ||
609 | } | ||
610 | } | ||
611 | |||
612 | static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) | ||
613 | { | ||
614 | |||
615 | struct ath6kl_vif *vif; | ||
616 | int res = 0; | ||
617 | |||
618 | if (!ar->want_ch_switch) | ||
619 | return; | ||
620 | |||
621 | spin_lock_bh(&ar->list_lock); | ||
622 | list_for_each_entry(vif, &ar->vif_list, list) { | ||
623 | if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) | ||
624 | res = ath6kl_commit_ch_switch(vif, channel); | ||
625 | |||
626 | if (res) | ||
627 | ath6kl_err("channel switch failed nw_type %d res %d\n", | ||
628 | vif->nw_type, res); | ||
629 | } | ||
630 | spin_unlock_bh(&ar->list_lock); | ||
631 | } | ||
632 | |||
587 | void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, | 633 | void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, |
588 | u16 listen_int, u16 beacon_int, | 634 | u16 listen_int, u16 beacon_int, |
589 | enum network_type net_type, u8 beacon_ie_len, | 635 | 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, | |||
601 | memcpy(vif->bssid, bssid, sizeof(vif->bssid)); | 647 | memcpy(vif->bssid, bssid, sizeof(vif->bssid)); |
602 | vif->bss_ch = channel; | 648 | vif->bss_ch = channel; |
603 | 649 | ||
604 | if ((vif->nw_type == INFRA_NETWORK)) | 650 | if ((vif->nw_type == INFRA_NETWORK)) { |
605 | ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, | 651 | ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, |
606 | vif->listen_intvl_t, 0); | 652 | vif->listen_intvl_t, 0); |
653 | ath6kl_check_ch_switch(ar, channel); | ||
654 | } | ||
607 | 655 | ||
608 | netif_wake_queue(vif->ndev); | 656 | netif_wake_queue(vif->ndev); |
609 | 657 | ||
@@ -926,6 +974,11 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, | |||
926 | struct ath6kl *ar = vif->ar; | 974 | struct ath6kl *ar = vif->ar; |
927 | 975 | ||
928 | if (vif->nw_type == AP_NETWORK) { | 976 | if (vif->nw_type == AP_NETWORK) { |
977 | /* disconnect due to other STA vif switching channels */ | ||
978 | if (reason == BSS_DISCONNECTED && | ||
979 | prot_reason_status == WMI_AP_REASON_STA_ROAM) | ||
980 | ar->want_ch_switch |= 1 << vif->fw_vif_idx; | ||
981 | |||
929 | if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) | 982 | if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) |
930 | return; | 983 | return; |
931 | 984 | ||
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index d3d2ab5c1689..190b2c4e382f 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h | |||
@@ -1151,6 +1151,7 @@ enum wmi_phy_mode { | |||
1151 | WMI_11AG_MODE = 0x3, | 1151 | WMI_11AG_MODE = 0x3, |
1152 | WMI_11B_MODE = 0x4, | 1152 | WMI_11B_MODE = 0x4, |
1153 | WMI_11GONLY_MODE = 0x5, | 1153 | WMI_11GONLY_MODE = 0x5, |
1154 | WMI_11G_HT20 = 0x6, | ||
1154 | }; | 1155 | }; |
1155 | 1156 | ||
1156 | #define WMI_MAX_CHANNELS 32 | 1157 | #define WMI_MAX_CHANNELS 32 |
@@ -1468,6 +1469,17 @@ enum wmi_disconnect_reason { | |||
1468 | IBSS_MERGE = 0xe, | 1469 | IBSS_MERGE = 0xe, |
1469 | }; | 1470 | }; |
1470 | 1471 | ||
1472 | /* AP mode disconnect proto_reasons */ | ||
1473 | enum ap_disconnect_reason { | ||
1474 | WMI_AP_REASON_STA_LEFT = 101, | ||
1475 | WMI_AP_REASON_FROM_HOST = 102, | ||
1476 | WMI_AP_REASON_COMM_TIMEOUT = 103, | ||
1477 | WMI_AP_REASON_MAX_STA = 104, | ||
1478 | WMI_AP_REASON_ACL = 105, | ||
1479 | WMI_AP_REASON_STA_ROAM = 106, | ||
1480 | WMI_AP_REASON_DFS_CHANNEL = 107, | ||
1481 | }; | ||
1482 | |||
1471 | #define ATH6KL_COUNTRY_RD_SHIFT 16 | 1483 | #define ATH6KL_COUNTRY_RD_SHIFT 16 |
1472 | 1484 | ||
1473 | struct ath6kl_wmi_regdomain { | 1485 | struct ath6kl_wmi_regdomain { |