1bdcd8170SKalle Valo /* 2bdcd8170SKalle Valo * Copyright (c) 2004-2011 Atheros Communications Inc. 3bdcd8170SKalle Valo * 4bdcd8170SKalle Valo * Permission to use, copy, modify, and/or distribute this software for any 5bdcd8170SKalle Valo * purpose with or without fee is hereby granted, provided that the above 6bdcd8170SKalle Valo * copyright notice and this permission notice appear in all copies. 7bdcd8170SKalle Valo * 8bdcd8170SKalle Valo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9bdcd8170SKalle Valo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10bdcd8170SKalle Valo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11bdcd8170SKalle Valo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12bdcd8170SKalle Valo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13bdcd8170SKalle Valo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14bdcd8170SKalle Valo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15bdcd8170SKalle Valo */ 16bdcd8170SKalle Valo 17bdcd8170SKalle Valo #include "core.h" 18bdcd8170SKalle Valo #include "cfg80211.h" 19bdcd8170SKalle Valo #include "debug.h" 20abcb344bSKalle Valo #include "hif-ops.h" 21003353b0SKalle Valo #include "testmode.h" 22bdcd8170SKalle Valo 236bbc7c35SJouni Malinen static unsigned int ath6kl_p2p; 246bbc7c35SJouni Malinen 256bbc7c35SJouni Malinen module_param(ath6kl_p2p, uint, 0644); 266bbc7c35SJouni Malinen 27bdcd8170SKalle Valo #define RATETAB_ENT(_rate, _rateid, _flags) { \ 28bdcd8170SKalle Valo .bitrate = (_rate), \ 29bdcd8170SKalle Valo .flags = (_flags), \ 30bdcd8170SKalle Valo .hw_value = (_rateid), \ 31bdcd8170SKalle Valo } 32bdcd8170SKalle Valo 33bdcd8170SKalle Valo #define CHAN2G(_channel, _freq, _flags) { \ 34bdcd8170SKalle Valo .band = IEEE80211_BAND_2GHZ, \ 35bdcd8170SKalle Valo .hw_value = (_channel), \ 36bdcd8170SKalle Valo .center_freq = (_freq), \ 37bdcd8170SKalle Valo .flags = (_flags), \ 38bdcd8170SKalle Valo .max_antenna_gain = 0, \ 39bdcd8170SKalle Valo .max_power = 30, \ 40bdcd8170SKalle Valo } 41bdcd8170SKalle Valo 42bdcd8170SKalle Valo #define CHAN5G(_channel, _flags) { \ 43bdcd8170SKalle Valo .band = IEEE80211_BAND_5GHZ, \ 44bdcd8170SKalle Valo .hw_value = (_channel), \ 45bdcd8170SKalle Valo .center_freq = 5000 + (5 * (_channel)), \ 46bdcd8170SKalle Valo .flags = (_flags), \ 47bdcd8170SKalle Valo .max_antenna_gain = 0, \ 48bdcd8170SKalle Valo .max_power = 30, \ 49bdcd8170SKalle Valo } 50bdcd8170SKalle Valo 51bdcd8170SKalle Valo static struct ieee80211_rate ath6kl_rates[] = { 52bdcd8170SKalle Valo RATETAB_ENT(10, 0x1, 0), 53bdcd8170SKalle Valo RATETAB_ENT(20, 0x2, 0), 54bdcd8170SKalle Valo RATETAB_ENT(55, 0x4, 0), 55bdcd8170SKalle Valo RATETAB_ENT(110, 0x8, 0), 56bdcd8170SKalle Valo RATETAB_ENT(60, 0x10, 0), 57bdcd8170SKalle Valo RATETAB_ENT(90, 0x20, 0), 58bdcd8170SKalle Valo RATETAB_ENT(120, 0x40, 0), 59bdcd8170SKalle Valo RATETAB_ENT(180, 0x80, 0), 60bdcd8170SKalle Valo RATETAB_ENT(240, 0x100, 0), 61bdcd8170SKalle Valo RATETAB_ENT(360, 0x200, 0), 62bdcd8170SKalle Valo RATETAB_ENT(480, 0x400, 0), 63bdcd8170SKalle Valo RATETAB_ENT(540, 0x800, 0), 64bdcd8170SKalle Valo }; 65bdcd8170SKalle Valo 66bdcd8170SKalle Valo #define ath6kl_a_rates (ath6kl_rates + 4) 67bdcd8170SKalle Valo #define ath6kl_a_rates_size 8 68bdcd8170SKalle Valo #define ath6kl_g_rates (ath6kl_rates + 0) 69bdcd8170SKalle Valo #define ath6kl_g_rates_size 12 70bdcd8170SKalle Valo 71bdcd8170SKalle Valo static struct ieee80211_channel ath6kl_2ghz_channels[] = { 72bdcd8170SKalle Valo CHAN2G(1, 2412, 0), 73bdcd8170SKalle Valo CHAN2G(2, 2417, 0), 74bdcd8170SKalle Valo CHAN2G(3, 2422, 0), 75bdcd8170SKalle Valo CHAN2G(4, 2427, 0), 76bdcd8170SKalle Valo CHAN2G(5, 2432, 0), 77bdcd8170SKalle Valo CHAN2G(6, 2437, 0), 78bdcd8170SKalle Valo CHAN2G(7, 2442, 0), 79bdcd8170SKalle Valo CHAN2G(8, 2447, 0), 80bdcd8170SKalle Valo CHAN2G(9, 2452, 0), 81bdcd8170SKalle Valo CHAN2G(10, 2457, 0), 82bdcd8170SKalle Valo CHAN2G(11, 2462, 0), 83bdcd8170SKalle Valo CHAN2G(12, 2467, 0), 84bdcd8170SKalle Valo CHAN2G(13, 2472, 0), 85bdcd8170SKalle Valo CHAN2G(14, 2484, 0), 86bdcd8170SKalle Valo }; 87bdcd8170SKalle Valo 88bdcd8170SKalle Valo static struct ieee80211_channel ath6kl_5ghz_a_channels[] = { 89bdcd8170SKalle Valo CHAN5G(34, 0), CHAN5G(36, 0), 90bdcd8170SKalle Valo CHAN5G(38, 0), CHAN5G(40, 0), 91bdcd8170SKalle Valo CHAN5G(42, 0), CHAN5G(44, 0), 92bdcd8170SKalle Valo CHAN5G(46, 0), CHAN5G(48, 0), 93bdcd8170SKalle Valo CHAN5G(52, 0), CHAN5G(56, 0), 94bdcd8170SKalle Valo CHAN5G(60, 0), CHAN5G(64, 0), 95bdcd8170SKalle Valo CHAN5G(100, 0), CHAN5G(104, 0), 96bdcd8170SKalle Valo CHAN5G(108, 0), CHAN5G(112, 0), 97bdcd8170SKalle Valo CHAN5G(116, 0), CHAN5G(120, 0), 98bdcd8170SKalle Valo CHAN5G(124, 0), CHAN5G(128, 0), 99bdcd8170SKalle Valo CHAN5G(132, 0), CHAN5G(136, 0), 100bdcd8170SKalle Valo CHAN5G(140, 0), CHAN5G(149, 0), 101bdcd8170SKalle Valo CHAN5G(153, 0), CHAN5G(157, 0), 102bdcd8170SKalle Valo CHAN5G(161, 0), CHAN5G(165, 0), 103bdcd8170SKalle Valo CHAN5G(184, 0), CHAN5G(188, 0), 104bdcd8170SKalle Valo CHAN5G(192, 0), CHAN5G(196, 0), 105bdcd8170SKalle Valo CHAN5G(200, 0), CHAN5G(204, 0), 106bdcd8170SKalle Valo CHAN5G(208, 0), CHAN5G(212, 0), 107bdcd8170SKalle Valo CHAN5G(216, 0), 108bdcd8170SKalle Valo }; 109bdcd8170SKalle Valo 110bdcd8170SKalle Valo static struct ieee80211_supported_band ath6kl_band_2ghz = { 111bdcd8170SKalle Valo .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels), 112bdcd8170SKalle Valo .channels = ath6kl_2ghz_channels, 113bdcd8170SKalle Valo .n_bitrates = ath6kl_g_rates_size, 114bdcd8170SKalle Valo .bitrates = ath6kl_g_rates, 115bdcd8170SKalle Valo }; 116bdcd8170SKalle Valo 117bdcd8170SKalle Valo static struct ieee80211_supported_band ath6kl_band_5ghz = { 118bdcd8170SKalle Valo .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels), 119bdcd8170SKalle Valo .channels = ath6kl_5ghz_a_channels, 120bdcd8170SKalle Valo .n_bitrates = ath6kl_a_rates_size, 121bdcd8170SKalle Valo .bitrates = ath6kl_a_rates, 122bdcd8170SKalle Valo }; 123bdcd8170SKalle Valo 124837cb97eSJouni Malinen #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */ 125837cb97eSJouni Malinen 126240d2799SVasanthakumar Thiagarajan static int ath6kl_set_wpa_version(struct ath6kl_vif *vif, 127bdcd8170SKalle Valo enum nl80211_wpa_versions wpa_version) 128bdcd8170SKalle Valo { 129bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version); 130bdcd8170SKalle Valo 131bdcd8170SKalle Valo if (!wpa_version) { 1323450334fSVasanthakumar Thiagarajan vif->auth_mode = NONE_AUTH; 133bdcd8170SKalle Valo } else if (wpa_version & NL80211_WPA_VERSION_2) { 1343450334fSVasanthakumar Thiagarajan vif->auth_mode = WPA2_AUTH; 135bdcd8170SKalle Valo } else if (wpa_version & NL80211_WPA_VERSION_1) { 1363450334fSVasanthakumar Thiagarajan vif->auth_mode = WPA_AUTH; 137bdcd8170SKalle Valo } else { 138bdcd8170SKalle Valo ath6kl_err("%s: %u not supported\n", __func__, wpa_version); 139bdcd8170SKalle Valo return -ENOTSUPP; 140bdcd8170SKalle Valo } 141bdcd8170SKalle Valo 142bdcd8170SKalle Valo return 0; 143bdcd8170SKalle Valo } 144bdcd8170SKalle Valo 145240d2799SVasanthakumar Thiagarajan static int ath6kl_set_auth_type(struct ath6kl_vif *vif, 146bdcd8170SKalle Valo enum nl80211_auth_type auth_type) 147bdcd8170SKalle Valo { 148bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type); 149bdcd8170SKalle Valo 150bdcd8170SKalle Valo switch (auth_type) { 151bdcd8170SKalle Valo case NL80211_AUTHTYPE_OPEN_SYSTEM: 1523450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode = OPEN_AUTH; 153bdcd8170SKalle Valo break; 154bdcd8170SKalle Valo case NL80211_AUTHTYPE_SHARED_KEY: 1553450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode = SHARED_AUTH; 156bdcd8170SKalle Valo break; 157bdcd8170SKalle Valo case NL80211_AUTHTYPE_NETWORK_EAP: 1583450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode = LEAP_AUTH; 159bdcd8170SKalle Valo break; 160bdcd8170SKalle Valo 161bdcd8170SKalle Valo case NL80211_AUTHTYPE_AUTOMATIC: 1623450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH; 163bdcd8170SKalle Valo break; 164bdcd8170SKalle Valo 165bdcd8170SKalle Valo default: 166bdcd8170SKalle Valo ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type); 167bdcd8170SKalle Valo return -ENOTSUPP; 168bdcd8170SKalle Valo } 169bdcd8170SKalle Valo 170bdcd8170SKalle Valo return 0; 171bdcd8170SKalle Valo } 172bdcd8170SKalle Valo 173240d2799SVasanthakumar Thiagarajan static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast) 174bdcd8170SKalle Valo { 1753450334fSVasanthakumar Thiagarajan u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto; 1763450334fSVasanthakumar Thiagarajan u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len : 1773450334fSVasanthakumar Thiagarajan &vif->grp_crypto_len; 178bdcd8170SKalle Valo 179bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n", 180bdcd8170SKalle Valo __func__, cipher, ucast); 181bdcd8170SKalle Valo 182bdcd8170SKalle Valo switch (cipher) { 183bdcd8170SKalle Valo case 0: 184bdcd8170SKalle Valo /* our own hack to use value 0 as no crypto used */ 185bdcd8170SKalle Valo *ar_cipher = NONE_CRYPT; 186bdcd8170SKalle Valo *ar_cipher_len = 0; 187bdcd8170SKalle Valo break; 188bdcd8170SKalle Valo case WLAN_CIPHER_SUITE_WEP40: 189bdcd8170SKalle Valo *ar_cipher = WEP_CRYPT; 190bdcd8170SKalle Valo *ar_cipher_len = 5; 191bdcd8170SKalle Valo break; 192bdcd8170SKalle Valo case WLAN_CIPHER_SUITE_WEP104: 193bdcd8170SKalle Valo *ar_cipher = WEP_CRYPT; 194bdcd8170SKalle Valo *ar_cipher_len = 13; 195bdcd8170SKalle Valo break; 196bdcd8170SKalle Valo case WLAN_CIPHER_SUITE_TKIP: 197bdcd8170SKalle Valo *ar_cipher = TKIP_CRYPT; 198bdcd8170SKalle Valo *ar_cipher_len = 0; 199bdcd8170SKalle Valo break; 200bdcd8170SKalle Valo case WLAN_CIPHER_SUITE_CCMP: 201bdcd8170SKalle Valo *ar_cipher = AES_CRYPT; 202bdcd8170SKalle Valo *ar_cipher_len = 0; 203bdcd8170SKalle Valo break; 204bdcd8170SKalle Valo default: 205bdcd8170SKalle Valo ath6kl_err("cipher 0x%x not supported\n", cipher); 206bdcd8170SKalle Valo return -ENOTSUPP; 207bdcd8170SKalle Valo } 208bdcd8170SKalle Valo 209bdcd8170SKalle Valo return 0; 210bdcd8170SKalle Valo } 211bdcd8170SKalle Valo 212240d2799SVasanthakumar Thiagarajan static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt) 213bdcd8170SKalle Valo { 214bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt); 215bdcd8170SKalle Valo 216bdcd8170SKalle Valo if (key_mgmt == WLAN_AKM_SUITE_PSK) { 2173450334fSVasanthakumar Thiagarajan if (vif->auth_mode == WPA_AUTH) 2183450334fSVasanthakumar Thiagarajan vif->auth_mode = WPA_PSK_AUTH; 2193450334fSVasanthakumar Thiagarajan else if (vif->auth_mode == WPA2_AUTH) 2203450334fSVasanthakumar Thiagarajan vif->auth_mode = WPA2_PSK_AUTH; 221837cb97eSJouni Malinen } else if (key_mgmt == 0x00409600) { 2223450334fSVasanthakumar Thiagarajan if (vif->auth_mode == WPA_AUTH) 2233450334fSVasanthakumar Thiagarajan vif->auth_mode = WPA_AUTH_CCKM; 2243450334fSVasanthakumar Thiagarajan else if (vif->auth_mode == WPA2_AUTH) 2253450334fSVasanthakumar Thiagarajan vif->auth_mode = WPA2_AUTH_CCKM; 226bdcd8170SKalle Valo } else if (key_mgmt != WLAN_AKM_SUITE_8021X) { 2273450334fSVasanthakumar Thiagarajan vif->auth_mode = NONE_AUTH; 228bdcd8170SKalle Valo } 229bdcd8170SKalle Valo } 230bdcd8170SKalle Valo 231bdcd8170SKalle Valo static bool ath6kl_cfg80211_ready(struct ath6kl *ar) 232bdcd8170SKalle Valo { 23359c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = ar->vif; 23459c98449SVasanthakumar Thiagarajan 235bdcd8170SKalle Valo if (!test_bit(WMI_READY, &ar->flag)) { 236bdcd8170SKalle Valo ath6kl_err("wmi is not ready\n"); 237bdcd8170SKalle Valo return false; 238bdcd8170SKalle Valo } 239bdcd8170SKalle Valo 24059c98449SVasanthakumar Thiagarajan if (!test_bit(WLAN_ENABLED, &vif->flags)) { 241bdcd8170SKalle Valo ath6kl_err("wlan disabled\n"); 242bdcd8170SKalle Valo return false; 243bdcd8170SKalle Valo } 244bdcd8170SKalle Valo 245bdcd8170SKalle Valo return true; 246bdcd8170SKalle Valo } 247bdcd8170SKalle Valo 2486981ffdcSKevin Fang static bool ath6kl_is_wpa_ie(const u8 *pos) 2496981ffdcSKevin Fang { 2506981ffdcSKevin Fang return pos[0] == WLAN_EID_WPA && pos[1] >= 4 && 2516981ffdcSKevin Fang pos[2] == 0x00 && pos[3] == 0x50 && 2526981ffdcSKevin Fang pos[4] == 0xf2 && pos[5] == 0x01; 2536981ffdcSKevin Fang } 2546981ffdcSKevin Fang 2556981ffdcSKevin Fang static bool ath6kl_is_rsn_ie(const u8 *pos) 2566981ffdcSKevin Fang { 2576981ffdcSKevin Fang return pos[0] == WLAN_EID_RSN; 2586981ffdcSKevin Fang } 2596981ffdcSKevin Fang 260334234b5SVasanthakumar Thiagarajan static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies, 2616981ffdcSKevin Fang size_t ies_len) 2626981ffdcSKevin Fang { 263334234b5SVasanthakumar Thiagarajan struct ath6kl *ar = vif->ar; 2646981ffdcSKevin Fang const u8 *pos; 2656981ffdcSKevin Fang u8 *buf = NULL; 2666981ffdcSKevin Fang size_t len = 0; 2676981ffdcSKevin Fang int ret; 2686981ffdcSKevin Fang 2696981ffdcSKevin Fang /* 2706981ffdcSKevin Fang * Filter out RSN/WPA IE(s) 2716981ffdcSKevin Fang */ 2726981ffdcSKevin Fang 2736981ffdcSKevin Fang if (ies && ies_len) { 2746981ffdcSKevin Fang buf = kmalloc(ies_len, GFP_KERNEL); 2756981ffdcSKevin Fang if (buf == NULL) 2766981ffdcSKevin Fang return -ENOMEM; 2776981ffdcSKevin Fang pos = ies; 2786981ffdcSKevin Fang 2796981ffdcSKevin Fang while (pos + 1 < ies + ies_len) { 2806981ffdcSKevin Fang if (pos + 2 + pos[1] > ies + ies_len) 2816981ffdcSKevin Fang break; 2826981ffdcSKevin Fang if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) { 2836981ffdcSKevin Fang memcpy(buf + len, pos, 2 + pos[1]); 2846981ffdcSKevin Fang len += 2 + pos[1]; 2856981ffdcSKevin Fang } 2866981ffdcSKevin Fang pos += 2 + pos[1]; 2876981ffdcSKevin Fang } 2886981ffdcSKevin Fang } 2896981ffdcSKevin Fang 290334234b5SVasanthakumar Thiagarajan ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, 291334234b5SVasanthakumar Thiagarajan WMI_FRAME_ASSOC_REQ, buf, len); 2926981ffdcSKevin Fang kfree(buf); 2936981ffdcSKevin Fang return ret; 2946981ffdcSKevin Fang } 2956981ffdcSKevin Fang 296bdcd8170SKalle Valo static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, 297bdcd8170SKalle Valo struct cfg80211_connect_params *sme) 298bdcd8170SKalle Valo { 299bdcd8170SKalle Valo struct ath6kl *ar = ath6kl_priv(dev); 30059c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 301bdcd8170SKalle Valo int status; 302bdcd8170SKalle Valo 30314ee6f6bSVasanthakumar Thiagarajan vif->sme_state = SME_CONNECTING; 304bdcd8170SKalle Valo 305bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 306bdcd8170SKalle Valo return -EIO; 307bdcd8170SKalle Valo 308bdcd8170SKalle Valo if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { 309bdcd8170SKalle Valo ath6kl_err("destroy in progress\n"); 310bdcd8170SKalle Valo return -EBUSY; 311bdcd8170SKalle Valo } 312bdcd8170SKalle Valo 313bdcd8170SKalle Valo if (test_bit(SKIP_SCAN, &ar->flag) && 314bdcd8170SKalle Valo ((sme->channel && sme->channel->center_freq == 0) || 315bdcd8170SKalle Valo (sme->bssid && is_zero_ether_addr(sme->bssid)))) { 316bdcd8170SKalle Valo ath6kl_err("SkipScan: channel or bssid invalid\n"); 317bdcd8170SKalle Valo return -EINVAL; 318bdcd8170SKalle Valo } 319bdcd8170SKalle Valo 320bdcd8170SKalle Valo if (down_interruptible(&ar->sem)) { 321bdcd8170SKalle Valo ath6kl_err("busy, couldn't get access\n"); 322bdcd8170SKalle Valo return -ERESTARTSYS; 323bdcd8170SKalle Valo } 324bdcd8170SKalle Valo 325bdcd8170SKalle Valo if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { 326bdcd8170SKalle Valo ath6kl_err("busy, destroy in progress\n"); 327bdcd8170SKalle Valo up(&ar->sem); 328bdcd8170SKalle Valo return -EBUSY; 329bdcd8170SKalle Valo } 330bdcd8170SKalle Valo 331bdcd8170SKalle Valo if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) { 332bdcd8170SKalle Valo /* 333bdcd8170SKalle Valo * sleep until the command queue drains 334bdcd8170SKalle Valo */ 335bdcd8170SKalle Valo wait_event_interruptible_timeout(ar->event_wq, 336bdcd8170SKalle Valo ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0, 337bdcd8170SKalle Valo WMI_TIMEOUT); 338bdcd8170SKalle Valo if (signal_pending(current)) { 339bdcd8170SKalle Valo ath6kl_err("cmd queue drain timeout\n"); 340bdcd8170SKalle Valo up(&ar->sem); 341bdcd8170SKalle Valo return -EINTR; 342bdcd8170SKalle Valo } 343bdcd8170SKalle Valo } 344bdcd8170SKalle Valo 3456981ffdcSKevin Fang if (sme->ie && (sme->ie_len > 0)) { 346334234b5SVasanthakumar Thiagarajan status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len); 3476981ffdcSKevin Fang if (status) 3486981ffdcSKevin Fang return status; 3496981ffdcSKevin Fang } 3506981ffdcSKevin Fang 35159c98449SVasanthakumar Thiagarajan if (test_bit(CONNECTED, &vif->flags) && 3523450334fSVasanthakumar Thiagarajan vif->ssid_len == sme->ssid_len && 3533450334fSVasanthakumar Thiagarajan !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) { 354cf5333d7SVasanthakumar Thiagarajan vif->reconnect_flag = true; 355334234b5SVasanthakumar Thiagarajan status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx, 356334234b5SVasanthakumar Thiagarajan vif->req_bssid, 357f74bac54SVasanthakumar Thiagarajan vif->ch_hint); 358bdcd8170SKalle Valo 359bdcd8170SKalle Valo up(&ar->sem); 360bdcd8170SKalle Valo if (status) { 361bdcd8170SKalle Valo ath6kl_err("wmi_reconnect_cmd failed\n"); 362bdcd8170SKalle Valo return -EIO; 363bdcd8170SKalle Valo } 364bdcd8170SKalle Valo return 0; 3653450334fSVasanthakumar Thiagarajan } else if (vif->ssid_len == sme->ssid_len && 3663450334fSVasanthakumar Thiagarajan !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) { 367240d2799SVasanthakumar Thiagarajan ath6kl_disconnect(vif); 368bdcd8170SKalle Valo } 369bdcd8170SKalle Valo 3703450334fSVasanthakumar Thiagarajan memset(vif->ssid, 0, sizeof(vif->ssid)); 3713450334fSVasanthakumar Thiagarajan vif->ssid_len = sme->ssid_len; 3723450334fSVasanthakumar Thiagarajan memcpy(vif->ssid, sme->ssid, sme->ssid_len); 373bdcd8170SKalle Valo 374bdcd8170SKalle Valo if (sme->channel) 375f74bac54SVasanthakumar Thiagarajan vif->ch_hint = sme->channel->center_freq; 376bdcd8170SKalle Valo 3778c8b65e3SVasanthakumar Thiagarajan memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); 378bdcd8170SKalle Valo if (sme->bssid && !is_broadcast_ether_addr(sme->bssid)) 3798c8b65e3SVasanthakumar Thiagarajan memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid)); 380bdcd8170SKalle Valo 381240d2799SVasanthakumar Thiagarajan ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions); 382bdcd8170SKalle Valo 383240d2799SVasanthakumar Thiagarajan status = ath6kl_set_auth_type(vif, sme->auth_type); 384bdcd8170SKalle Valo if (status) { 385bdcd8170SKalle Valo up(&ar->sem); 386bdcd8170SKalle Valo return status; 387bdcd8170SKalle Valo } 388bdcd8170SKalle Valo 389bdcd8170SKalle Valo if (sme->crypto.n_ciphers_pairwise) 390240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true); 391bdcd8170SKalle Valo else 392240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, 0, true); 393bdcd8170SKalle Valo 394240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, sme->crypto.cipher_group, false); 395bdcd8170SKalle Valo 396bdcd8170SKalle Valo if (sme->crypto.n_akm_suites) 397240d2799SVasanthakumar Thiagarajan ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]); 398bdcd8170SKalle Valo 399bdcd8170SKalle Valo if ((sme->key_len) && 4003450334fSVasanthakumar Thiagarajan (vif->auth_mode == NONE_AUTH) && 4013450334fSVasanthakumar Thiagarajan (vif->prwise_crypto == WEP_CRYPT)) { 402bdcd8170SKalle Valo struct ath6kl_key *key = NULL; 403bdcd8170SKalle Valo 404bdcd8170SKalle Valo if (sme->key_idx < WMI_MIN_KEY_INDEX || 405bdcd8170SKalle Valo sme->key_idx > WMI_MAX_KEY_INDEX) { 406bdcd8170SKalle Valo ath6kl_err("key index %d out of bounds\n", 407bdcd8170SKalle Valo sme->key_idx); 408bdcd8170SKalle Valo up(&ar->sem); 409bdcd8170SKalle Valo return -ENOENT; 410bdcd8170SKalle Valo } 411bdcd8170SKalle Valo 4126f2a73f9SVasanthakumar Thiagarajan key = &vif->keys[sme->key_idx]; 413bdcd8170SKalle Valo key->key_len = sme->key_len; 414bdcd8170SKalle Valo memcpy(key->key, sme->key, key->key_len); 4153450334fSVasanthakumar Thiagarajan key->cipher = vif->prwise_crypto; 4163450334fSVasanthakumar Thiagarajan vif->def_txkey_index = sme->key_idx; 417bdcd8170SKalle Valo 418334234b5SVasanthakumar Thiagarajan ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx, 4193450334fSVasanthakumar Thiagarajan vif->prwise_crypto, 420bdcd8170SKalle Valo GROUP_USAGE | TX_USAGE, 421bdcd8170SKalle Valo key->key_len, 422bdcd8170SKalle Valo NULL, 423bdcd8170SKalle Valo key->key, KEY_OP_INIT_VAL, NULL, 424bdcd8170SKalle Valo NO_SYNC_WMIFLAG); 425bdcd8170SKalle Valo } 426bdcd8170SKalle Valo 427bdcd8170SKalle Valo if (!ar->usr_bss_filter) { 42859c98449SVasanthakumar Thiagarajan clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); 429240d2799SVasanthakumar Thiagarajan if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, 430240d2799SVasanthakumar Thiagarajan ALL_BSS_FILTER, 0) != 0) { 431bdcd8170SKalle Valo ath6kl_err("couldn't set bss filtering\n"); 432bdcd8170SKalle Valo up(&ar->sem); 433bdcd8170SKalle Valo return -EIO; 434bdcd8170SKalle Valo } 435bdcd8170SKalle Valo } 436bdcd8170SKalle Valo 437f5938f24SVasanthakumar Thiagarajan vif->nw_type = vif->next_mode; 438bdcd8170SKalle Valo 439bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 440bdcd8170SKalle Valo "%s: connect called with authmode %d dot11 auth %d" 441bdcd8170SKalle Valo " PW crypto %d PW crypto len %d GRP crypto %d" 442bdcd8170SKalle Valo " GRP crypto len %d channel hint %u\n", 443bdcd8170SKalle Valo __func__, 4443450334fSVasanthakumar Thiagarajan vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto, 4453450334fSVasanthakumar Thiagarajan vif->prwise_crypto_len, vif->grp_crypto, 446f74bac54SVasanthakumar Thiagarajan vif->grp_crypto_len, vif->ch_hint); 447bdcd8170SKalle Valo 448cf5333d7SVasanthakumar Thiagarajan vif->reconnect_flag = 0; 449334234b5SVasanthakumar Thiagarajan status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type, 4503450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode, vif->auth_mode, 4513450334fSVasanthakumar Thiagarajan vif->prwise_crypto, 4523450334fSVasanthakumar Thiagarajan vif->prwise_crypto_len, 4533450334fSVasanthakumar Thiagarajan vif->grp_crypto, vif->grp_crypto_len, 4543450334fSVasanthakumar Thiagarajan vif->ssid_len, vif->ssid, 455f74bac54SVasanthakumar Thiagarajan vif->req_bssid, vif->ch_hint, 456bdcd8170SKalle Valo ar->connect_ctrl_flags); 457bdcd8170SKalle Valo 458bdcd8170SKalle Valo up(&ar->sem); 459bdcd8170SKalle Valo 460bdcd8170SKalle Valo if (status == -EINVAL) { 4613450334fSVasanthakumar Thiagarajan memset(vif->ssid, 0, sizeof(vif->ssid)); 4623450334fSVasanthakumar Thiagarajan vif->ssid_len = 0; 463bdcd8170SKalle Valo ath6kl_err("invalid request\n"); 464bdcd8170SKalle Valo return -ENOENT; 465bdcd8170SKalle Valo } else if (status) { 466bdcd8170SKalle Valo ath6kl_err("ath6kl_wmi_connect_cmd failed\n"); 467bdcd8170SKalle Valo return -EIO; 468bdcd8170SKalle Valo } 469bdcd8170SKalle Valo 470bdcd8170SKalle Valo if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) && 4713450334fSVasanthakumar Thiagarajan ((vif->auth_mode == WPA_PSK_AUTH) 4723450334fSVasanthakumar Thiagarajan || (vif->auth_mode == WPA2_PSK_AUTH))) { 473de3ad713SVasanthakumar Thiagarajan mod_timer(&vif->disconnect_timer, 474bdcd8170SKalle Valo jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL)); 475bdcd8170SKalle Valo } 476bdcd8170SKalle Valo 477bdcd8170SKalle Valo ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD; 47859c98449SVasanthakumar Thiagarajan set_bit(CONNECT_PEND, &vif->flags); 479bdcd8170SKalle Valo 480bdcd8170SKalle Valo return 0; 481bdcd8170SKalle Valo } 482bdcd8170SKalle Valo 483240d2799SVasanthakumar Thiagarajan static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, const u8 *bssid, 48401cac476SJouni Malinen struct ieee80211_channel *chan, 48501cac476SJouni Malinen const u8 *beacon_ie, size_t beacon_ie_len) 48601cac476SJouni Malinen { 487240d2799SVasanthakumar Thiagarajan struct ath6kl *ar = vif->ar; 48801cac476SJouni Malinen struct cfg80211_bss *bss; 48901cac476SJouni Malinen u8 *ie; 49001cac476SJouni Malinen 491be98e3a4SVasanthakumar Thiagarajan bss = cfg80211_get_bss(ar->wiphy, chan, bssid, 4923450334fSVasanthakumar Thiagarajan vif->ssid, vif->ssid_len, WLAN_CAPABILITY_ESS, 49301cac476SJouni Malinen WLAN_CAPABILITY_ESS); 49401cac476SJouni Malinen if (bss == NULL) { 49501cac476SJouni Malinen /* 49601cac476SJouni Malinen * Since cfg80211 may not yet know about the BSS, 49701cac476SJouni Malinen * generate a partial entry until the first BSS info 49801cac476SJouni Malinen * event becomes available. 49901cac476SJouni Malinen * 50001cac476SJouni Malinen * Prepend SSID element since it is not included in the Beacon 50101cac476SJouni Malinen * IEs from the target. 50201cac476SJouni Malinen */ 5033450334fSVasanthakumar Thiagarajan ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL); 50401cac476SJouni Malinen if (ie == NULL) 50501cac476SJouni Malinen return -ENOMEM; 50601cac476SJouni Malinen ie[0] = WLAN_EID_SSID; 5073450334fSVasanthakumar Thiagarajan ie[1] = vif->ssid_len; 5083450334fSVasanthakumar Thiagarajan memcpy(ie + 2, vif->ssid, vif->ssid_len); 5093450334fSVasanthakumar Thiagarajan memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len); 510be98e3a4SVasanthakumar Thiagarajan bss = cfg80211_inform_bss(ar->wiphy, chan, 51101cac476SJouni Malinen bssid, 0, WLAN_CAPABILITY_ESS, 100, 5123450334fSVasanthakumar Thiagarajan ie, 2 + vif->ssid_len + beacon_ie_len, 51301cac476SJouni Malinen 0, GFP_KERNEL); 51401cac476SJouni Malinen if (bss) 51501cac476SJouni Malinen ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for " 51601cac476SJouni Malinen "%pM prior to indicating connect/roamed " 51701cac476SJouni Malinen "event\n", bssid); 51801cac476SJouni Malinen kfree(ie); 51901cac476SJouni Malinen } else 52001cac476SJouni Malinen ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss " 52101cac476SJouni Malinen "entry\n"); 52201cac476SJouni Malinen 52301cac476SJouni Malinen if (bss == NULL) 52401cac476SJouni Malinen return -ENOMEM; 52501cac476SJouni Malinen 52601cac476SJouni Malinen cfg80211_put_bss(bss); 52701cac476SJouni Malinen 52801cac476SJouni Malinen return 0; 52901cac476SJouni Malinen } 53001cac476SJouni Malinen 531240d2799SVasanthakumar Thiagarajan void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, 532bdcd8170SKalle Valo u8 *bssid, u16 listen_intvl, 533bdcd8170SKalle Valo u16 beacon_intvl, 534bdcd8170SKalle Valo enum network_type nw_type, 535bdcd8170SKalle Valo u8 beacon_ie_len, u8 assoc_req_len, 536bdcd8170SKalle Valo u8 assoc_resp_len, u8 *assoc_info) 537bdcd8170SKalle Valo { 53801cac476SJouni Malinen struct ieee80211_channel *chan; 539240d2799SVasanthakumar Thiagarajan struct ath6kl *ar = vif->ar; 540bdcd8170SKalle Valo 541bdcd8170SKalle Valo /* capinfo + listen interval */ 542bdcd8170SKalle Valo u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16); 543bdcd8170SKalle Valo 544bdcd8170SKalle Valo /* capinfo + status code + associd */ 545bdcd8170SKalle Valo u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16); 546bdcd8170SKalle Valo 547bdcd8170SKalle Valo u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset; 548bdcd8170SKalle Valo u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len + 549bdcd8170SKalle Valo assoc_resp_ie_offset; 550bdcd8170SKalle Valo 551bdcd8170SKalle Valo assoc_req_len -= assoc_req_ie_offset; 552bdcd8170SKalle Valo assoc_resp_len -= assoc_resp_ie_offset; 553bdcd8170SKalle Valo 55432c10874SJouni Malinen /* 55532c10874SJouni Malinen * Store Beacon interval here; DTIM period will be available only once 55632c10874SJouni Malinen * a Beacon frame from the AP is seen. 55732c10874SJouni Malinen */ 558cf5333d7SVasanthakumar Thiagarajan vif->assoc_bss_beacon_int = beacon_intvl; 55959c98449SVasanthakumar Thiagarajan clear_bit(DTIM_PERIOD_AVAIL, &vif->flags); 56032c10874SJouni Malinen 561bdcd8170SKalle Valo if (nw_type & ADHOC_NETWORK) { 562bdcd8170SKalle Valo if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) { 563bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 564bdcd8170SKalle Valo "%s: ath6k not in ibss mode\n", __func__); 565bdcd8170SKalle Valo return; 566bdcd8170SKalle Valo } 567bdcd8170SKalle Valo } 568bdcd8170SKalle Valo 569bdcd8170SKalle Valo if (nw_type & INFRA_NETWORK) { 5706b5e5d25SJouni Malinen if (ar->wdev->iftype != NL80211_IFTYPE_STATION && 5716b5e5d25SJouni Malinen ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) { 572bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 573bdcd8170SKalle Valo "%s: ath6k not in station mode\n", __func__); 574bdcd8170SKalle Valo return; 575bdcd8170SKalle Valo } 576bdcd8170SKalle Valo } 577bdcd8170SKalle Valo 578be98e3a4SVasanthakumar Thiagarajan chan = ieee80211_get_channel(ar->wiphy, (int) channel); 579bdcd8170SKalle Valo 580bdcd8170SKalle Valo 581bdcd8170SKalle Valo if (nw_type & ADHOC_NETWORK) { 582240d2799SVasanthakumar Thiagarajan cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); 583bdcd8170SKalle Valo return; 584bdcd8170SKalle Valo } 585bdcd8170SKalle Valo 586240d2799SVasanthakumar Thiagarajan if (ath6kl_add_bss_if_needed(vif, bssid, chan, assoc_info, 58701cac476SJouni Malinen beacon_ie_len) < 0) { 58801cac476SJouni Malinen ath6kl_err("could not add cfg80211 bss entry for " 58901cac476SJouni Malinen "connect/roamed notification\n"); 59001cac476SJouni Malinen return; 59101cac476SJouni Malinen } 59201cac476SJouni Malinen 59314ee6f6bSVasanthakumar Thiagarajan if (vif->sme_state == SME_CONNECTING) { 594bdcd8170SKalle Valo /* inform connect result to cfg80211 */ 59514ee6f6bSVasanthakumar Thiagarajan vif->sme_state = SME_CONNECTED; 596240d2799SVasanthakumar Thiagarajan cfg80211_connect_result(vif->ndev, bssid, 597bdcd8170SKalle Valo assoc_req_ie, assoc_req_len, 598bdcd8170SKalle Valo assoc_resp_ie, assoc_resp_len, 599bdcd8170SKalle Valo WLAN_STATUS_SUCCESS, GFP_KERNEL); 60014ee6f6bSVasanthakumar Thiagarajan } else if (vif->sme_state == SME_CONNECTED) { 601bdcd8170SKalle Valo /* inform roam event to cfg80211 */ 602240d2799SVasanthakumar Thiagarajan cfg80211_roamed(vif->ndev, chan, bssid, 603bdcd8170SKalle Valo assoc_req_ie, assoc_req_len, 604bdcd8170SKalle Valo assoc_resp_ie, assoc_resp_len, GFP_KERNEL); 605bdcd8170SKalle Valo } 606bdcd8170SKalle Valo } 607bdcd8170SKalle Valo 608bdcd8170SKalle Valo static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy, 609bdcd8170SKalle Valo struct net_device *dev, u16 reason_code) 610bdcd8170SKalle Valo { 611bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev); 6123450334fSVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 613bdcd8170SKalle Valo 614bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__, 615bdcd8170SKalle Valo reason_code); 616bdcd8170SKalle Valo 617bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 618bdcd8170SKalle Valo return -EIO; 619bdcd8170SKalle Valo 620bdcd8170SKalle Valo if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { 621bdcd8170SKalle Valo ath6kl_err("busy, destroy in progress\n"); 622bdcd8170SKalle Valo return -EBUSY; 623bdcd8170SKalle Valo } 624bdcd8170SKalle Valo 625bdcd8170SKalle Valo if (down_interruptible(&ar->sem)) { 626bdcd8170SKalle Valo ath6kl_err("busy, couldn't get access\n"); 627bdcd8170SKalle Valo return -ERESTARTSYS; 628bdcd8170SKalle Valo } 629bdcd8170SKalle Valo 630cf5333d7SVasanthakumar Thiagarajan vif->reconnect_flag = 0; 631240d2799SVasanthakumar Thiagarajan ath6kl_disconnect(vif); 6323450334fSVasanthakumar Thiagarajan memset(vif->ssid, 0, sizeof(vif->ssid)); 6333450334fSVasanthakumar Thiagarajan vif->ssid_len = 0; 634bdcd8170SKalle Valo 635bdcd8170SKalle Valo if (!test_bit(SKIP_SCAN, &ar->flag)) 6368c8b65e3SVasanthakumar Thiagarajan memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); 637bdcd8170SKalle Valo 638bdcd8170SKalle Valo up(&ar->sem); 639bdcd8170SKalle Valo 64014ee6f6bSVasanthakumar Thiagarajan vif->sme_state = SME_DISCONNECTED; 641170826ddSVasanthakumar Thiagarajan 642bdcd8170SKalle Valo return 0; 643bdcd8170SKalle Valo } 644bdcd8170SKalle Valo 645240d2799SVasanthakumar Thiagarajan void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, 646bdcd8170SKalle Valo u8 *bssid, u8 assoc_resp_len, 647bdcd8170SKalle Valo u8 *assoc_info, u16 proto_reason) 648bdcd8170SKalle Valo { 649240d2799SVasanthakumar Thiagarajan struct ath6kl *ar = vif->ar; 65059c98449SVasanthakumar Thiagarajan 65114ee6f6bSVasanthakumar Thiagarajan if (vif->scan_req) { 65214ee6f6bSVasanthakumar Thiagarajan cfg80211_scan_done(vif->scan_req, true); 65314ee6f6bSVasanthakumar Thiagarajan vif->scan_req = NULL; 654bdcd8170SKalle Valo } 655bdcd8170SKalle Valo 656f5938f24SVasanthakumar Thiagarajan if (vif->nw_type & ADHOC_NETWORK) { 657bdcd8170SKalle Valo if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) { 658bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 659bdcd8170SKalle Valo "%s: ath6k not in ibss mode\n", __func__); 660bdcd8170SKalle Valo return; 661bdcd8170SKalle Valo } 662bdcd8170SKalle Valo memset(bssid, 0, ETH_ALEN); 663240d2799SVasanthakumar Thiagarajan cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); 664bdcd8170SKalle Valo return; 665bdcd8170SKalle Valo } 666bdcd8170SKalle Valo 667f5938f24SVasanthakumar Thiagarajan if (vif->nw_type & INFRA_NETWORK) { 6686b5e5d25SJouni Malinen if (ar->wdev->iftype != NL80211_IFTYPE_STATION && 6696b5e5d25SJouni Malinen ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) { 670bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 671bdcd8170SKalle Valo "%s: ath6k not in station mode\n", __func__); 672bdcd8170SKalle Valo return; 673bdcd8170SKalle Valo } 674bdcd8170SKalle Valo } 675bdcd8170SKalle Valo 6761de547d6SVasanthakumar Thiagarajan /* 6771de547d6SVasanthakumar Thiagarajan * Send a disconnect command to target when a disconnect event is 6781de547d6SVasanthakumar Thiagarajan * received with reason code other than 3 (DISCONNECT_CMD - disconnect 6791de547d6SVasanthakumar Thiagarajan * request from host) to make the firmware stop trying to connect even 6801de547d6SVasanthakumar Thiagarajan * after giving disconnect event. There will be one more disconnect 6811de547d6SVasanthakumar Thiagarajan * event for this disconnect command with reason code DISCONNECT_CMD 6821de547d6SVasanthakumar Thiagarajan * which will be notified to cfg80211. 6831de547d6SVasanthakumar Thiagarajan */ 684bdcd8170SKalle Valo 6851de547d6SVasanthakumar Thiagarajan if (reason != DISCONNECT_CMD) { 686334234b5SVasanthakumar Thiagarajan ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); 687bdcd8170SKalle Valo return; 688bdcd8170SKalle Valo } 689bdcd8170SKalle Valo 69059c98449SVasanthakumar Thiagarajan clear_bit(CONNECT_PEND, &vif->flags); 691bdcd8170SKalle Valo 69214ee6f6bSVasanthakumar Thiagarajan if (vif->sme_state == SME_CONNECTING) { 693240d2799SVasanthakumar Thiagarajan cfg80211_connect_result(vif->ndev, 694bdcd8170SKalle Valo bssid, NULL, 0, 695bdcd8170SKalle Valo NULL, 0, 696bdcd8170SKalle Valo WLAN_STATUS_UNSPECIFIED_FAILURE, 697bdcd8170SKalle Valo GFP_KERNEL); 69814ee6f6bSVasanthakumar Thiagarajan } else if (vif->sme_state == SME_CONNECTED) { 699240d2799SVasanthakumar Thiagarajan cfg80211_disconnected(vif->ndev, reason, 700bdcd8170SKalle Valo NULL, 0, GFP_KERNEL); 701bdcd8170SKalle Valo } 702bdcd8170SKalle Valo 70314ee6f6bSVasanthakumar Thiagarajan vif->sme_state = SME_DISCONNECTED; 704bdcd8170SKalle Valo } 705bdcd8170SKalle Valo 706bdcd8170SKalle Valo static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, 707bdcd8170SKalle Valo struct cfg80211_scan_request *request) 708bdcd8170SKalle Valo { 709bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); 71059c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(ndev); 7111276c9efSEdward Lu s8 n_channels = 0; 7121276c9efSEdward Lu u16 *channels = NULL; 713bdcd8170SKalle Valo int ret = 0; 714f1f92179SVasanthakumar Thiagarajan u32 force_fg_scan = 0; 715bdcd8170SKalle Valo 716bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 717bdcd8170SKalle Valo return -EIO; 718bdcd8170SKalle Valo 719bdcd8170SKalle Valo if (!ar->usr_bss_filter) { 72059c98449SVasanthakumar Thiagarajan clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); 7211b1e6ee3SJouni Malinen ret = ath6kl_wmi_bssfilter_cmd( 722240d2799SVasanthakumar Thiagarajan ar->wmi, vif->fw_vif_idx, 72359c98449SVasanthakumar Thiagarajan (test_bit(CONNECTED, &vif->flags) ? 7241b1e6ee3SJouni Malinen ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0); 7251b1e6ee3SJouni Malinen if (ret) { 726bdcd8170SKalle Valo ath6kl_err("couldn't set bss filtering\n"); 7271b1e6ee3SJouni Malinen return ret; 728bdcd8170SKalle Valo } 729bdcd8170SKalle Valo } 730bdcd8170SKalle Valo 731bdcd8170SKalle Valo if (request->n_ssids && request->ssids[0].ssid_len) { 732bdcd8170SKalle Valo u8 i; 733bdcd8170SKalle Valo 734bdcd8170SKalle Valo if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1)) 735bdcd8170SKalle Valo request->n_ssids = MAX_PROBED_SSID_INDEX - 1; 736bdcd8170SKalle Valo 737bdcd8170SKalle Valo for (i = 0; i < request->n_ssids; i++) 738334234b5SVasanthakumar Thiagarajan ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, 739334234b5SVasanthakumar Thiagarajan i + 1, SPECIFIC_SSID_FLAG, 740bdcd8170SKalle Valo request->ssids[i].ssid_len, 741bdcd8170SKalle Valo request->ssids[i].ssid); 742bdcd8170SKalle Valo } 743bdcd8170SKalle Valo 744b84da8c7SJouni Malinen if (request->ie) { 745334234b5SVasanthakumar Thiagarajan ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, 746334234b5SVasanthakumar Thiagarajan WMI_FRAME_PROBE_REQ, 747b84da8c7SJouni Malinen request->ie, request->ie_len); 748b84da8c7SJouni Malinen if (ret) { 749b84da8c7SJouni Malinen ath6kl_err("failed to set Probe Request appie for " 750b84da8c7SJouni Malinen "scan"); 751b84da8c7SJouni Malinen return ret; 752b84da8c7SJouni Malinen } 753b84da8c7SJouni Malinen } 754b84da8c7SJouni Malinen 75511869befSJouni Malinen /* 75611869befSJouni Malinen * Scan only the requested channels if the request specifies a set of 75711869befSJouni Malinen * channels. If the list is longer than the target supports, do not 75811869befSJouni Malinen * configure the list and instead, scan all available channels. 75911869befSJouni Malinen */ 76011869befSJouni Malinen if (request->n_channels > 0 && 76111869befSJouni Malinen request->n_channels <= WMI_MAX_CHANNELS) { 7621276c9efSEdward Lu u8 i; 7631276c9efSEdward Lu 76411869befSJouni Malinen n_channels = request->n_channels; 7651276c9efSEdward Lu 7661276c9efSEdward Lu channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL); 7671276c9efSEdward Lu if (channels == NULL) { 7681276c9efSEdward Lu ath6kl_warn("failed to set scan channels, " 7691276c9efSEdward Lu "scan all channels"); 7701276c9efSEdward Lu n_channels = 0; 7711276c9efSEdward Lu } 7721276c9efSEdward Lu 7731276c9efSEdward Lu for (i = 0; i < n_channels; i++) 7741276c9efSEdward Lu channels[i] = request->channels[i]->center_freq; 7751276c9efSEdward Lu } 7761276c9efSEdward Lu 77759c98449SVasanthakumar Thiagarajan if (test_bit(CONNECTED, &vif->flags)) 778f1f92179SVasanthakumar Thiagarajan force_fg_scan = 1; 779f1f92179SVasanthakumar Thiagarajan 780334234b5SVasanthakumar Thiagarajan ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN, 781334234b5SVasanthakumar Thiagarajan force_fg_scan, false, 0, 0, n_channels, 782334234b5SVasanthakumar Thiagarajan channels); 7831b1e6ee3SJouni Malinen if (ret) 784bdcd8170SKalle Valo ath6kl_err("wmi_startscan_cmd failed\n"); 78511869befSJouni Malinen else 78614ee6f6bSVasanthakumar Thiagarajan vif->scan_req = request; 787bdcd8170SKalle Valo 7881276c9efSEdward Lu kfree(channels); 7891276c9efSEdward Lu 790bdcd8170SKalle Valo return ret; 791bdcd8170SKalle Valo } 792bdcd8170SKalle Valo 793240d2799SVasanthakumar Thiagarajan void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, int status) 794bdcd8170SKalle Valo { 795240d2799SVasanthakumar Thiagarajan struct ath6kl *ar = vif->ar; 7966fd1eaceSKalle Valo int i; 797bdcd8170SKalle Valo 798bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status %d\n", __func__, status); 799bdcd8170SKalle Valo 80014ee6f6bSVasanthakumar Thiagarajan if (!vif->scan_req) 8016fd1eaceSKalle Valo return; 8026fd1eaceSKalle Valo 8036fd1eaceSKalle Valo if ((status == -ECANCELED) || (status == -EBUSY)) { 80414ee6f6bSVasanthakumar Thiagarajan cfg80211_scan_done(vif->scan_req, true); 8056fd1eaceSKalle Valo goto out; 8066fd1eaceSKalle Valo } 8076fd1eaceSKalle Valo 80814ee6f6bSVasanthakumar Thiagarajan cfg80211_scan_done(vif->scan_req, false); 809bdcd8170SKalle Valo 81014ee6f6bSVasanthakumar Thiagarajan if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) { 81114ee6f6bSVasanthakumar Thiagarajan for (i = 0; i < vif->scan_req->n_ssids; i++) { 812334234b5SVasanthakumar Thiagarajan ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, 813334234b5SVasanthakumar Thiagarajan i + 1, DISABLE_SSID_FLAG, 814bdcd8170SKalle Valo 0, NULL); 815bdcd8170SKalle Valo } 816bdcd8170SKalle Valo } 8176fd1eaceSKalle Valo 8186fd1eaceSKalle Valo out: 81914ee6f6bSVasanthakumar Thiagarajan vif->scan_req = NULL; 820bdcd8170SKalle Valo } 821bdcd8170SKalle Valo 822bdcd8170SKalle Valo static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, 823bdcd8170SKalle Valo u8 key_index, bool pairwise, 824bdcd8170SKalle Valo const u8 *mac_addr, 825bdcd8170SKalle Valo struct key_params *params) 826bdcd8170SKalle Valo { 827bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); 82859c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(ndev); 829bdcd8170SKalle Valo struct ath6kl_key *key = NULL; 830bdcd8170SKalle Valo u8 key_usage; 831bdcd8170SKalle Valo u8 key_type; 832bdcd8170SKalle Valo int status = 0; 833bdcd8170SKalle Valo 834bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 835bdcd8170SKalle Valo return -EIO; 836bdcd8170SKalle Valo 837837cb97eSJouni Malinen if (params->cipher == CCKM_KRK_CIPHER_SUITE) { 838837cb97eSJouni Malinen if (params->key_len != WMI_KRK_LEN) 839837cb97eSJouni Malinen return -EINVAL; 840240d2799SVasanthakumar Thiagarajan return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx, 841240d2799SVasanthakumar Thiagarajan params->key); 842837cb97eSJouni Malinen } 843837cb97eSJouni Malinen 844bdcd8170SKalle Valo if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { 845bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 846bdcd8170SKalle Valo "%s: key index %d out of bounds\n", __func__, 847bdcd8170SKalle Valo key_index); 848bdcd8170SKalle Valo return -ENOENT; 849bdcd8170SKalle Valo } 850bdcd8170SKalle Valo 8516f2a73f9SVasanthakumar Thiagarajan key = &vif->keys[key_index]; 852bdcd8170SKalle Valo memset(key, 0, sizeof(struct ath6kl_key)); 853bdcd8170SKalle Valo 854bdcd8170SKalle Valo if (pairwise) 855bdcd8170SKalle Valo key_usage = PAIRWISE_USAGE; 856bdcd8170SKalle Valo else 857bdcd8170SKalle Valo key_usage = GROUP_USAGE; 858bdcd8170SKalle Valo 859bdcd8170SKalle Valo if (params) { 860bdcd8170SKalle Valo if (params->key_len > WLAN_MAX_KEY_LEN || 861bdcd8170SKalle Valo params->seq_len > sizeof(key->seq)) 862bdcd8170SKalle Valo return -EINVAL; 863bdcd8170SKalle Valo 864bdcd8170SKalle Valo key->key_len = params->key_len; 865bdcd8170SKalle Valo memcpy(key->key, params->key, key->key_len); 866bdcd8170SKalle Valo key->seq_len = params->seq_len; 867bdcd8170SKalle Valo memcpy(key->seq, params->seq, key->seq_len); 868bdcd8170SKalle Valo key->cipher = params->cipher; 869bdcd8170SKalle Valo } 870bdcd8170SKalle Valo 871bdcd8170SKalle Valo switch (key->cipher) { 872bdcd8170SKalle Valo case WLAN_CIPHER_SUITE_WEP40: 873bdcd8170SKalle Valo case WLAN_CIPHER_SUITE_WEP104: 874bdcd8170SKalle Valo key_type = WEP_CRYPT; 875bdcd8170SKalle Valo break; 876bdcd8170SKalle Valo 877bdcd8170SKalle Valo case WLAN_CIPHER_SUITE_TKIP: 878bdcd8170SKalle Valo key_type = TKIP_CRYPT; 879bdcd8170SKalle Valo break; 880bdcd8170SKalle Valo 881bdcd8170SKalle Valo case WLAN_CIPHER_SUITE_CCMP: 882bdcd8170SKalle Valo key_type = AES_CRYPT; 883bdcd8170SKalle Valo break; 884bdcd8170SKalle Valo 885bdcd8170SKalle Valo default: 886bdcd8170SKalle Valo return -ENOTSUPP; 887bdcd8170SKalle Valo } 888bdcd8170SKalle Valo 8893450334fSVasanthakumar Thiagarajan if (((vif->auth_mode == WPA_PSK_AUTH) 8903450334fSVasanthakumar Thiagarajan || (vif->auth_mode == WPA2_PSK_AUTH)) 891bdcd8170SKalle Valo && (key_usage & GROUP_USAGE)) 892de3ad713SVasanthakumar Thiagarajan del_timer(&vif->disconnect_timer); 893bdcd8170SKalle Valo 894bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 895bdcd8170SKalle Valo "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n", 896bdcd8170SKalle Valo __func__, key_index, key->key_len, key_type, 897bdcd8170SKalle Valo key_usage, key->seq_len); 898bdcd8170SKalle Valo 8993450334fSVasanthakumar Thiagarajan vif->def_txkey_index = key_index; 9009a5b1318SJouni Malinen 901f5938f24SVasanthakumar Thiagarajan if (vif->nw_type == AP_NETWORK && !pairwise && 9029a5b1318SJouni Malinen (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) { 9039a5b1318SJouni Malinen ar->ap_mode_bkey.valid = true; 9049a5b1318SJouni Malinen ar->ap_mode_bkey.key_index = key_index; 9059a5b1318SJouni Malinen ar->ap_mode_bkey.key_type = key_type; 9069a5b1318SJouni Malinen ar->ap_mode_bkey.key_len = key->key_len; 9079a5b1318SJouni Malinen memcpy(ar->ap_mode_bkey.key, key->key, key->key_len); 90859c98449SVasanthakumar Thiagarajan if (!test_bit(CONNECTED, &vif->flags)) { 9099a5b1318SJouni Malinen ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group " 9109a5b1318SJouni Malinen "key configuration until AP mode has been " 9119a5b1318SJouni Malinen "started\n"); 9129a5b1318SJouni Malinen /* 9139a5b1318SJouni Malinen * The key will be set in ath6kl_connect_ap_mode() once 9149a5b1318SJouni Malinen * the connected event is received from the target. 9159a5b1318SJouni Malinen */ 9169a5b1318SJouni Malinen return 0; 9179a5b1318SJouni Malinen } 9189a5b1318SJouni Malinen } 9199a5b1318SJouni Malinen 920f5938f24SVasanthakumar Thiagarajan if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT && 92159c98449SVasanthakumar Thiagarajan !test_bit(CONNECTED, &vif->flags)) { 922151411e8SJouni Malinen /* 923151411e8SJouni Malinen * Store the key locally so that it can be re-configured after 924151411e8SJouni Malinen * the AP mode has properly started 925151411e8SJouni Malinen * (ath6kl_install_statioc_wep_keys). 926151411e8SJouni Malinen */ 927151411e8SJouni Malinen ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration " 928151411e8SJouni Malinen "until AP mode has been started\n"); 9296f2a73f9SVasanthakumar Thiagarajan vif->wep_key_list[key_index].key_len = key->key_len; 9306f2a73f9SVasanthakumar Thiagarajan memcpy(vif->wep_key_list[key_index].key, key->key, 9316f2a73f9SVasanthakumar Thiagarajan key->key_len); 932151411e8SJouni Malinen return 0; 933151411e8SJouni Malinen } 934151411e8SJouni Malinen 935334234b5SVasanthakumar Thiagarajan status = ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, 936334234b5SVasanthakumar Thiagarajan vif->def_txkey_index, 937bdcd8170SKalle Valo key_type, key_usage, key->key_len, 938bdcd8170SKalle Valo key->seq, key->key, KEY_OP_INIT_VAL, 939bdcd8170SKalle Valo (u8 *) mac_addr, SYNC_BOTH_WMIFLAG); 940bdcd8170SKalle Valo 941bdcd8170SKalle Valo if (status) 942bdcd8170SKalle Valo return -EIO; 943bdcd8170SKalle Valo 944bdcd8170SKalle Valo return 0; 945bdcd8170SKalle Valo } 946bdcd8170SKalle Valo 947bdcd8170SKalle Valo static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, 948bdcd8170SKalle Valo u8 key_index, bool pairwise, 949bdcd8170SKalle Valo const u8 *mac_addr) 950bdcd8170SKalle Valo { 951bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); 9526f2a73f9SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(ndev); 953bdcd8170SKalle Valo 954bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); 955bdcd8170SKalle Valo 956bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 957bdcd8170SKalle Valo return -EIO; 958bdcd8170SKalle Valo 959bdcd8170SKalle Valo if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { 960bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 961bdcd8170SKalle Valo "%s: key index %d out of bounds\n", __func__, 962bdcd8170SKalle Valo key_index); 963bdcd8170SKalle Valo return -ENOENT; 964bdcd8170SKalle Valo } 965bdcd8170SKalle Valo 9666f2a73f9SVasanthakumar Thiagarajan if (!vif->keys[key_index].key_len) { 967bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 968bdcd8170SKalle Valo "%s: index %d is empty\n", __func__, key_index); 969bdcd8170SKalle Valo return 0; 970bdcd8170SKalle Valo } 971bdcd8170SKalle Valo 9726f2a73f9SVasanthakumar Thiagarajan vif->keys[key_index].key_len = 0; 973bdcd8170SKalle Valo 974334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index); 975bdcd8170SKalle Valo } 976bdcd8170SKalle Valo 977bdcd8170SKalle Valo static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, 978bdcd8170SKalle Valo u8 key_index, bool pairwise, 979bdcd8170SKalle Valo const u8 *mac_addr, void *cookie, 980bdcd8170SKalle Valo void (*callback) (void *cookie, 981bdcd8170SKalle Valo struct key_params *)) 982bdcd8170SKalle Valo { 983bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); 9846f2a73f9SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(ndev); 985bdcd8170SKalle Valo struct ath6kl_key *key = NULL; 986bdcd8170SKalle Valo struct key_params params; 987bdcd8170SKalle Valo 988bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); 989bdcd8170SKalle Valo 990bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 991bdcd8170SKalle Valo return -EIO; 992bdcd8170SKalle Valo 993bdcd8170SKalle Valo if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { 994bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 995bdcd8170SKalle Valo "%s: key index %d out of bounds\n", __func__, 996bdcd8170SKalle Valo key_index); 997bdcd8170SKalle Valo return -ENOENT; 998bdcd8170SKalle Valo } 999bdcd8170SKalle Valo 10006f2a73f9SVasanthakumar Thiagarajan key = &vif->keys[key_index]; 1001bdcd8170SKalle Valo memset(¶ms, 0, sizeof(params)); 1002bdcd8170SKalle Valo params.cipher = key->cipher; 1003bdcd8170SKalle Valo params.key_len = key->key_len; 1004bdcd8170SKalle Valo params.seq_len = key->seq_len; 1005bdcd8170SKalle Valo params.seq = key->seq; 1006bdcd8170SKalle Valo params.key = key->key; 1007bdcd8170SKalle Valo 1008bdcd8170SKalle Valo callback(cookie, ¶ms); 1009bdcd8170SKalle Valo 1010bdcd8170SKalle Valo return key->key_len ? 0 : -ENOENT; 1011bdcd8170SKalle Valo } 1012bdcd8170SKalle Valo 1013bdcd8170SKalle Valo static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, 1014bdcd8170SKalle Valo struct net_device *ndev, 1015bdcd8170SKalle Valo u8 key_index, bool unicast, 1016bdcd8170SKalle Valo bool multicast) 1017bdcd8170SKalle Valo { 1018bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); 101959c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(ndev); 1020bdcd8170SKalle Valo struct ath6kl_key *key = NULL; 1021bdcd8170SKalle Valo int status = 0; 1022bdcd8170SKalle Valo u8 key_usage; 1023229ed6b5SEdward Lu enum crypto_type key_type = NONE_CRYPT; 1024bdcd8170SKalle Valo 1025bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); 1026bdcd8170SKalle Valo 1027bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 1028bdcd8170SKalle Valo return -EIO; 1029bdcd8170SKalle Valo 1030bdcd8170SKalle Valo if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { 1031bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 1032bdcd8170SKalle Valo "%s: key index %d out of bounds\n", 1033bdcd8170SKalle Valo __func__, key_index); 1034bdcd8170SKalle Valo return -ENOENT; 1035bdcd8170SKalle Valo } 1036bdcd8170SKalle Valo 10376f2a73f9SVasanthakumar Thiagarajan if (!vif->keys[key_index].key_len) { 1038bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n", 1039bdcd8170SKalle Valo __func__, key_index); 1040bdcd8170SKalle Valo return -EINVAL; 1041bdcd8170SKalle Valo } 1042bdcd8170SKalle Valo 10433450334fSVasanthakumar Thiagarajan vif->def_txkey_index = key_index; 10446f2a73f9SVasanthakumar Thiagarajan key = &vif->keys[vif->def_txkey_index]; 1045bdcd8170SKalle Valo key_usage = GROUP_USAGE; 10463450334fSVasanthakumar Thiagarajan if (vif->prwise_crypto == WEP_CRYPT) 1047bdcd8170SKalle Valo key_usage |= TX_USAGE; 1048229ed6b5SEdward Lu if (unicast) 10493450334fSVasanthakumar Thiagarajan key_type = vif->prwise_crypto; 1050229ed6b5SEdward Lu if (multicast) 10513450334fSVasanthakumar Thiagarajan key_type = vif->grp_crypto; 1052bdcd8170SKalle Valo 1053f5938f24SVasanthakumar Thiagarajan if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags)) 10549a5b1318SJouni Malinen return 0; /* Delay until AP mode has been started */ 10559a5b1318SJouni Malinen 1056334234b5SVasanthakumar Thiagarajan status = ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, 1057334234b5SVasanthakumar Thiagarajan vif->def_txkey_index, 1058229ed6b5SEdward Lu key_type, key_usage, 1059bdcd8170SKalle Valo key->key_len, key->seq, key->key, 1060bdcd8170SKalle Valo KEY_OP_INIT_VAL, NULL, 1061bdcd8170SKalle Valo SYNC_BOTH_WMIFLAG); 1062bdcd8170SKalle Valo if (status) 1063bdcd8170SKalle Valo return -EIO; 1064bdcd8170SKalle Valo 1065bdcd8170SKalle Valo return 0; 1066bdcd8170SKalle Valo } 1067bdcd8170SKalle Valo 1068240d2799SVasanthakumar Thiagarajan void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, 1069bdcd8170SKalle Valo bool ismcast) 1070bdcd8170SKalle Valo { 1071bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 1072bdcd8170SKalle Valo "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast); 1073bdcd8170SKalle Valo 1074240d2799SVasanthakumar Thiagarajan cfg80211_michael_mic_failure(vif->ndev, vif->bssid, 1075bdcd8170SKalle Valo (ismcast ? NL80211_KEYTYPE_GROUP : 1076bdcd8170SKalle Valo NL80211_KEYTYPE_PAIRWISE), keyid, NULL, 1077bdcd8170SKalle Valo GFP_KERNEL); 1078bdcd8170SKalle Valo } 1079bdcd8170SKalle Valo 1080bdcd8170SKalle Valo static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) 1081bdcd8170SKalle Valo { 1082bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); 1083bdcd8170SKalle Valo int ret; 1084bdcd8170SKalle Valo 1085bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__, 1086bdcd8170SKalle Valo changed); 1087bdcd8170SKalle Valo 1088bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 1089bdcd8170SKalle Valo return -EIO; 1090bdcd8170SKalle Valo 1091bdcd8170SKalle Valo if (changed & WIPHY_PARAM_RTS_THRESHOLD) { 1092bdcd8170SKalle Valo ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold); 1093bdcd8170SKalle Valo if (ret != 0) { 1094bdcd8170SKalle Valo ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n"); 1095bdcd8170SKalle Valo return -EIO; 1096bdcd8170SKalle Valo } 1097bdcd8170SKalle Valo } 1098bdcd8170SKalle Valo 1099bdcd8170SKalle Valo return 0; 1100bdcd8170SKalle Valo } 1101bdcd8170SKalle Valo 1102bdcd8170SKalle Valo /* 1103bdcd8170SKalle Valo * The type nl80211_tx_power_setting replaces the following 1104bdcd8170SKalle Valo * data type from 2.6.36 onwards 1105bdcd8170SKalle Valo */ 1106bdcd8170SKalle Valo static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, 1107bdcd8170SKalle Valo enum nl80211_tx_power_setting type, 1108bdcd8170SKalle Valo int dbm) 1109bdcd8170SKalle Valo { 1110bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); 1111bdcd8170SKalle Valo u8 ath6kl_dbm; 1112bdcd8170SKalle Valo 1113bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__, 1114bdcd8170SKalle Valo type, dbm); 1115bdcd8170SKalle Valo 1116bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 1117bdcd8170SKalle Valo return -EIO; 1118bdcd8170SKalle Valo 1119bdcd8170SKalle Valo switch (type) { 1120bdcd8170SKalle Valo case NL80211_TX_POWER_AUTOMATIC: 1121bdcd8170SKalle Valo return 0; 1122bdcd8170SKalle Valo case NL80211_TX_POWER_LIMITED: 1123bdcd8170SKalle Valo ar->tx_pwr = ath6kl_dbm = dbm; 1124bdcd8170SKalle Valo break; 1125bdcd8170SKalle Valo default: 1126bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n", 1127bdcd8170SKalle Valo __func__, type); 1128bdcd8170SKalle Valo return -EOPNOTSUPP; 1129bdcd8170SKalle Valo } 1130bdcd8170SKalle Valo 1131bdcd8170SKalle Valo ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, ath6kl_dbm); 1132bdcd8170SKalle Valo 1133bdcd8170SKalle Valo return 0; 1134bdcd8170SKalle Valo } 1135bdcd8170SKalle Valo 1136bdcd8170SKalle Valo static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) 1137bdcd8170SKalle Valo { 1138bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); 113959c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = ar->vif; 1140bdcd8170SKalle Valo 1141bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 1142bdcd8170SKalle Valo return -EIO; 1143bdcd8170SKalle Valo 114459c98449SVasanthakumar Thiagarajan if (test_bit(CONNECTED, &vif->flags)) { 1145bdcd8170SKalle Valo ar->tx_pwr = 0; 1146bdcd8170SKalle Valo 1147bdcd8170SKalle Valo if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi) != 0) { 1148bdcd8170SKalle Valo ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n"); 1149bdcd8170SKalle Valo return -EIO; 1150bdcd8170SKalle Valo } 1151bdcd8170SKalle Valo 1152bdcd8170SKalle Valo wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0, 1153bdcd8170SKalle Valo 5 * HZ); 1154bdcd8170SKalle Valo 1155bdcd8170SKalle Valo if (signal_pending(current)) { 1156bdcd8170SKalle Valo ath6kl_err("target did not respond\n"); 1157bdcd8170SKalle Valo return -EINTR; 1158bdcd8170SKalle Valo } 1159bdcd8170SKalle Valo } 1160bdcd8170SKalle Valo 1161bdcd8170SKalle Valo *dbm = ar->tx_pwr; 1162bdcd8170SKalle Valo return 0; 1163bdcd8170SKalle Valo } 1164bdcd8170SKalle Valo 1165bdcd8170SKalle Valo static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy, 1166bdcd8170SKalle Valo struct net_device *dev, 1167bdcd8170SKalle Valo bool pmgmt, int timeout) 1168bdcd8170SKalle Valo { 1169bdcd8170SKalle Valo struct ath6kl *ar = ath6kl_priv(dev); 1170bdcd8170SKalle Valo struct wmi_power_mode_cmd mode; 1171334234b5SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 1172bdcd8170SKalle Valo 1173bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n", 1174bdcd8170SKalle Valo __func__, pmgmt, timeout); 1175bdcd8170SKalle Valo 1176bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 1177bdcd8170SKalle Valo return -EIO; 1178bdcd8170SKalle Valo 1179bdcd8170SKalle Valo if (pmgmt) { 1180bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__); 1181bdcd8170SKalle Valo mode.pwr_mode = REC_POWER; 1182bdcd8170SKalle Valo } else { 1183bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__); 1184bdcd8170SKalle Valo mode.pwr_mode = MAX_PERF_POWER; 1185bdcd8170SKalle Valo } 1186bdcd8170SKalle Valo 1187334234b5SVasanthakumar Thiagarajan if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx, 1188334234b5SVasanthakumar Thiagarajan mode.pwr_mode) != 0) { 1189bdcd8170SKalle Valo ath6kl_err("wmi_powermode_cmd failed\n"); 1190bdcd8170SKalle Valo return -EIO; 1191bdcd8170SKalle Valo } 1192bdcd8170SKalle Valo 1193bdcd8170SKalle Valo return 0; 1194bdcd8170SKalle Valo } 1195bdcd8170SKalle Valo 1196bdcd8170SKalle Valo static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, 1197bdcd8170SKalle Valo struct net_device *ndev, 1198bdcd8170SKalle Valo enum nl80211_iftype type, u32 *flags, 1199bdcd8170SKalle Valo struct vif_params *params) 1200bdcd8170SKalle Valo { 1201bdcd8170SKalle Valo struct ath6kl *ar = ath6kl_priv(ndev); 1202bdcd8170SKalle Valo struct wireless_dev *wdev = ar->wdev; 1203f5938f24SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(ndev); 1204bdcd8170SKalle Valo 1205bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); 1206bdcd8170SKalle Valo 1207bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 1208bdcd8170SKalle Valo return -EIO; 1209bdcd8170SKalle Valo 1210bdcd8170SKalle Valo switch (type) { 1211bdcd8170SKalle Valo case NL80211_IFTYPE_STATION: 1212f5938f24SVasanthakumar Thiagarajan vif->next_mode = INFRA_NETWORK; 1213bdcd8170SKalle Valo break; 1214bdcd8170SKalle Valo case NL80211_IFTYPE_ADHOC: 1215f5938f24SVasanthakumar Thiagarajan vif->next_mode = ADHOC_NETWORK; 1216bdcd8170SKalle Valo break; 12176e4604c8SJouni Malinen case NL80211_IFTYPE_AP: 1218f5938f24SVasanthakumar Thiagarajan vif->next_mode = AP_NETWORK; 12196e4604c8SJouni Malinen break; 12206b5e5d25SJouni Malinen case NL80211_IFTYPE_P2P_CLIENT: 1221f5938f24SVasanthakumar Thiagarajan vif->next_mode = INFRA_NETWORK; 12226b5e5d25SJouni Malinen break; 12236b5e5d25SJouni Malinen case NL80211_IFTYPE_P2P_GO: 1224f5938f24SVasanthakumar Thiagarajan vif->next_mode = AP_NETWORK; 12256b5e5d25SJouni Malinen break; 1226bdcd8170SKalle Valo default: 1227bdcd8170SKalle Valo ath6kl_err("invalid interface type %u\n", type); 1228bdcd8170SKalle Valo return -EOPNOTSUPP; 1229bdcd8170SKalle Valo } 1230bdcd8170SKalle Valo 1231bdcd8170SKalle Valo wdev->iftype = type; 1232bdcd8170SKalle Valo 1233bdcd8170SKalle Valo return 0; 1234bdcd8170SKalle Valo } 1235bdcd8170SKalle Valo 1236bdcd8170SKalle Valo static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, 1237bdcd8170SKalle Valo struct net_device *dev, 1238bdcd8170SKalle Valo struct cfg80211_ibss_params *ibss_param) 1239bdcd8170SKalle Valo { 1240bdcd8170SKalle Valo struct ath6kl *ar = ath6kl_priv(dev); 124159c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 1242bdcd8170SKalle Valo int status; 1243bdcd8170SKalle Valo 1244bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 1245bdcd8170SKalle Valo return -EIO; 1246bdcd8170SKalle Valo 12473450334fSVasanthakumar Thiagarajan vif->ssid_len = ibss_param->ssid_len; 12483450334fSVasanthakumar Thiagarajan memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len); 1249bdcd8170SKalle Valo 1250bdcd8170SKalle Valo if (ibss_param->channel) 1251f74bac54SVasanthakumar Thiagarajan vif->ch_hint = ibss_param->channel->center_freq; 1252bdcd8170SKalle Valo 1253bdcd8170SKalle Valo if (ibss_param->channel_fixed) { 1254bdcd8170SKalle Valo /* 1255bdcd8170SKalle Valo * TODO: channel_fixed: The channel should be fixed, do not 1256bdcd8170SKalle Valo * search for IBSSs to join on other channels. Target 1257bdcd8170SKalle Valo * firmware does not support this feature, needs to be 1258bdcd8170SKalle Valo * updated. 1259bdcd8170SKalle Valo */ 1260bdcd8170SKalle Valo return -EOPNOTSUPP; 1261bdcd8170SKalle Valo } 1262bdcd8170SKalle Valo 12638c8b65e3SVasanthakumar Thiagarajan memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); 1264bdcd8170SKalle Valo if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid)) 12658c8b65e3SVasanthakumar Thiagarajan memcpy(vif->req_bssid, ibss_param->bssid, 12668c8b65e3SVasanthakumar Thiagarajan sizeof(vif->req_bssid)); 1267bdcd8170SKalle Valo 1268240d2799SVasanthakumar Thiagarajan ath6kl_set_wpa_version(vif, 0); 1269bdcd8170SKalle Valo 1270240d2799SVasanthakumar Thiagarajan status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM); 1271bdcd8170SKalle Valo if (status) 1272bdcd8170SKalle Valo return status; 1273bdcd8170SKalle Valo 1274bdcd8170SKalle Valo if (ibss_param->privacy) { 1275240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true); 1276240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false); 1277bdcd8170SKalle Valo } else { 1278240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, 0, true); 1279240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, 0, false); 1280bdcd8170SKalle Valo } 1281bdcd8170SKalle Valo 1282f5938f24SVasanthakumar Thiagarajan vif->nw_type = vif->next_mode; 1283bdcd8170SKalle Valo 1284bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 1285bdcd8170SKalle Valo "%s: connect called with authmode %d dot11 auth %d" 1286bdcd8170SKalle Valo " PW crypto %d PW crypto len %d GRP crypto %d" 1287bdcd8170SKalle Valo " GRP crypto len %d channel hint %u\n", 1288bdcd8170SKalle Valo __func__, 12893450334fSVasanthakumar Thiagarajan vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto, 12903450334fSVasanthakumar Thiagarajan vif->prwise_crypto_len, vif->grp_crypto, 1291f74bac54SVasanthakumar Thiagarajan vif->grp_crypto_len, vif->ch_hint); 1292bdcd8170SKalle Valo 1293334234b5SVasanthakumar Thiagarajan status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type, 12943450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode, vif->auth_mode, 12953450334fSVasanthakumar Thiagarajan vif->prwise_crypto, 12963450334fSVasanthakumar Thiagarajan vif->prwise_crypto_len, 12973450334fSVasanthakumar Thiagarajan vif->grp_crypto, vif->grp_crypto_len, 12983450334fSVasanthakumar Thiagarajan vif->ssid_len, vif->ssid, 1299f74bac54SVasanthakumar Thiagarajan vif->req_bssid, vif->ch_hint, 1300bdcd8170SKalle Valo ar->connect_ctrl_flags); 130159c98449SVasanthakumar Thiagarajan set_bit(CONNECT_PEND, &vif->flags); 1302bdcd8170SKalle Valo 1303bdcd8170SKalle Valo return 0; 1304bdcd8170SKalle Valo } 1305bdcd8170SKalle Valo 1306bdcd8170SKalle Valo static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy, 1307bdcd8170SKalle Valo struct net_device *dev) 1308bdcd8170SKalle Valo { 1309bdcd8170SKalle Valo struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev); 13103450334fSVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 1311bdcd8170SKalle Valo 1312bdcd8170SKalle Valo if (!ath6kl_cfg80211_ready(ar)) 1313bdcd8170SKalle Valo return -EIO; 1314bdcd8170SKalle Valo 1315240d2799SVasanthakumar Thiagarajan ath6kl_disconnect(vif); 13163450334fSVasanthakumar Thiagarajan memset(vif->ssid, 0, sizeof(vif->ssid)); 13173450334fSVasanthakumar Thiagarajan vif->ssid_len = 0; 1318bdcd8170SKalle Valo 1319bdcd8170SKalle Valo return 0; 1320bdcd8170SKalle Valo } 1321bdcd8170SKalle Valo 1322bdcd8170SKalle Valo static const u32 cipher_suites[] = { 1323bdcd8170SKalle Valo WLAN_CIPHER_SUITE_WEP40, 1324bdcd8170SKalle Valo WLAN_CIPHER_SUITE_WEP104, 1325bdcd8170SKalle Valo WLAN_CIPHER_SUITE_TKIP, 1326bdcd8170SKalle Valo WLAN_CIPHER_SUITE_CCMP, 1327837cb97eSJouni Malinen CCKM_KRK_CIPHER_SUITE, 1328bdcd8170SKalle Valo }; 1329bdcd8170SKalle Valo 1330bdcd8170SKalle Valo static bool is_rate_legacy(s32 rate) 1331bdcd8170SKalle Valo { 1332bdcd8170SKalle Valo static const s32 legacy[] = { 1000, 2000, 5500, 11000, 1333bdcd8170SKalle Valo 6000, 9000, 12000, 18000, 24000, 1334bdcd8170SKalle Valo 36000, 48000, 54000 1335bdcd8170SKalle Valo }; 1336bdcd8170SKalle Valo u8 i; 1337bdcd8170SKalle Valo 1338bdcd8170SKalle Valo for (i = 0; i < ARRAY_SIZE(legacy); i++) 1339bdcd8170SKalle Valo if (rate == legacy[i]) 1340bdcd8170SKalle Valo return true; 1341bdcd8170SKalle Valo 1342bdcd8170SKalle Valo return false; 1343bdcd8170SKalle Valo } 1344bdcd8170SKalle Valo 1345bdcd8170SKalle Valo static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi) 1346bdcd8170SKalle Valo { 1347bdcd8170SKalle Valo static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000, 1348bdcd8170SKalle Valo 52000, 58500, 65000, 72200 1349bdcd8170SKalle Valo }; 1350bdcd8170SKalle Valo u8 i; 1351bdcd8170SKalle Valo 1352bdcd8170SKalle Valo for (i = 0; i < ARRAY_SIZE(ht20); i++) { 1353bdcd8170SKalle Valo if (rate == ht20[i]) { 1354bdcd8170SKalle Valo if (i == ARRAY_SIZE(ht20) - 1) 1355bdcd8170SKalle Valo /* last rate uses sgi */ 1356bdcd8170SKalle Valo *sgi = true; 1357bdcd8170SKalle Valo else 1358bdcd8170SKalle Valo *sgi = false; 1359bdcd8170SKalle Valo 1360bdcd8170SKalle Valo *mcs = i; 1361bdcd8170SKalle Valo return true; 1362bdcd8170SKalle Valo } 1363bdcd8170SKalle Valo } 1364bdcd8170SKalle Valo return false; 1365bdcd8170SKalle Valo } 1366bdcd8170SKalle Valo 1367bdcd8170SKalle Valo static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi) 1368bdcd8170SKalle Valo { 1369bdcd8170SKalle Valo static const s32 ht40[] = { 13500, 27000, 40500, 54000, 1370bdcd8170SKalle Valo 81000, 108000, 121500, 135000, 1371bdcd8170SKalle Valo 150000 1372bdcd8170SKalle Valo }; 1373bdcd8170SKalle Valo u8 i; 1374bdcd8170SKalle Valo 1375bdcd8170SKalle Valo for (i = 0; i < ARRAY_SIZE(ht40); i++) { 1376bdcd8170SKalle Valo if (rate == ht40[i]) { 1377bdcd8170SKalle Valo if (i == ARRAY_SIZE(ht40) - 1) 1378bdcd8170SKalle Valo /* last rate uses sgi */ 1379bdcd8170SKalle Valo *sgi = true; 1380bdcd8170SKalle Valo else 1381bdcd8170SKalle Valo *sgi = false; 1382bdcd8170SKalle Valo 1383bdcd8170SKalle Valo *mcs = i; 1384bdcd8170SKalle Valo return true; 1385bdcd8170SKalle Valo } 1386bdcd8170SKalle Valo } 1387bdcd8170SKalle Valo 1388bdcd8170SKalle Valo return false; 1389bdcd8170SKalle Valo } 1390bdcd8170SKalle Valo 1391bdcd8170SKalle Valo static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, 1392bdcd8170SKalle Valo u8 *mac, struct station_info *sinfo) 1393bdcd8170SKalle Valo { 1394bdcd8170SKalle Valo struct ath6kl *ar = ath6kl_priv(dev); 139559c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 1396bdcd8170SKalle Valo long left; 1397bdcd8170SKalle Valo bool sgi; 1398bdcd8170SKalle Valo s32 rate; 1399bdcd8170SKalle Valo int ret; 1400bdcd8170SKalle Valo u8 mcs; 1401bdcd8170SKalle Valo 14028c8b65e3SVasanthakumar Thiagarajan if (memcmp(mac, vif->bssid, ETH_ALEN) != 0) 1403bdcd8170SKalle Valo return -ENOENT; 1404bdcd8170SKalle Valo 1405bdcd8170SKalle Valo if (down_interruptible(&ar->sem)) 1406bdcd8170SKalle Valo return -EBUSY; 1407bdcd8170SKalle Valo 1408b95907a7SVasanthakumar Thiagarajan set_bit(STATS_UPDATE_PEND, &vif->flags); 1409bdcd8170SKalle Valo 1410334234b5SVasanthakumar Thiagarajan ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx); 1411bdcd8170SKalle Valo 1412bdcd8170SKalle Valo if (ret != 0) { 1413bdcd8170SKalle Valo up(&ar->sem); 1414bdcd8170SKalle Valo return -EIO; 1415bdcd8170SKalle Valo } 1416bdcd8170SKalle Valo 1417bdcd8170SKalle Valo left = wait_event_interruptible_timeout(ar->event_wq, 1418bdcd8170SKalle Valo !test_bit(STATS_UPDATE_PEND, 1419b95907a7SVasanthakumar Thiagarajan &vif->flags), 1420bdcd8170SKalle Valo WMI_TIMEOUT); 1421bdcd8170SKalle Valo 1422bdcd8170SKalle Valo up(&ar->sem); 1423bdcd8170SKalle Valo 1424bdcd8170SKalle Valo if (left == 0) 1425bdcd8170SKalle Valo return -ETIMEDOUT; 1426bdcd8170SKalle Valo else if (left < 0) 1427bdcd8170SKalle Valo return left; 1428bdcd8170SKalle Valo 1429b95907a7SVasanthakumar Thiagarajan if (vif->target_stats.rx_byte) { 1430b95907a7SVasanthakumar Thiagarajan sinfo->rx_bytes = vif->target_stats.rx_byte; 1431bdcd8170SKalle Valo sinfo->filled |= STATION_INFO_RX_BYTES; 1432b95907a7SVasanthakumar Thiagarajan sinfo->rx_packets = vif->target_stats.rx_pkt; 1433bdcd8170SKalle Valo sinfo->filled |= STATION_INFO_RX_PACKETS; 1434bdcd8170SKalle Valo } 1435bdcd8170SKalle Valo 1436b95907a7SVasanthakumar Thiagarajan if (vif->target_stats.tx_byte) { 1437b95907a7SVasanthakumar Thiagarajan sinfo->tx_bytes = vif->target_stats.tx_byte; 1438bdcd8170SKalle Valo sinfo->filled |= STATION_INFO_TX_BYTES; 1439b95907a7SVasanthakumar Thiagarajan sinfo->tx_packets = vif->target_stats.tx_pkt; 1440bdcd8170SKalle Valo sinfo->filled |= STATION_INFO_TX_PACKETS; 1441bdcd8170SKalle Valo } 1442bdcd8170SKalle Valo 1443b95907a7SVasanthakumar Thiagarajan sinfo->signal = vif->target_stats.cs_rssi; 1444bdcd8170SKalle Valo sinfo->filled |= STATION_INFO_SIGNAL; 1445bdcd8170SKalle Valo 1446b95907a7SVasanthakumar Thiagarajan rate = vif->target_stats.tx_ucast_rate; 1447bdcd8170SKalle Valo 1448bdcd8170SKalle Valo if (is_rate_legacy(rate)) { 1449bdcd8170SKalle Valo sinfo->txrate.legacy = rate / 100; 1450bdcd8170SKalle Valo } else if (is_rate_ht20(rate, &mcs, &sgi)) { 1451bdcd8170SKalle Valo if (sgi) { 1452bdcd8170SKalle Valo sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; 1453bdcd8170SKalle Valo sinfo->txrate.mcs = mcs - 1; 1454bdcd8170SKalle Valo } else { 1455bdcd8170SKalle Valo sinfo->txrate.mcs = mcs; 1456bdcd8170SKalle Valo } 1457bdcd8170SKalle Valo 1458bdcd8170SKalle Valo sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; 1459bdcd8170SKalle Valo } else if (is_rate_ht40(rate, &mcs, &sgi)) { 1460bdcd8170SKalle Valo if (sgi) { 1461bdcd8170SKalle Valo sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; 1462bdcd8170SKalle Valo sinfo->txrate.mcs = mcs - 1; 1463bdcd8170SKalle Valo } else { 1464bdcd8170SKalle Valo sinfo->txrate.mcs = mcs; 1465bdcd8170SKalle Valo } 1466bdcd8170SKalle Valo 1467bdcd8170SKalle Valo sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; 1468bdcd8170SKalle Valo sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; 1469bdcd8170SKalle Valo } else { 14709a730834SKalle Valo ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, 14719a730834SKalle Valo "invalid rate from stats: %d\n", rate); 14729a730834SKalle Valo ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE); 1473bdcd8170SKalle Valo return 0; 1474bdcd8170SKalle Valo } 1475bdcd8170SKalle Valo 1476bdcd8170SKalle Valo sinfo->filled |= STATION_INFO_TX_BITRATE; 1477bdcd8170SKalle Valo 147859c98449SVasanthakumar Thiagarajan if (test_bit(CONNECTED, &vif->flags) && 147959c98449SVasanthakumar Thiagarajan test_bit(DTIM_PERIOD_AVAIL, &vif->flags) && 1480f5938f24SVasanthakumar Thiagarajan vif->nw_type == INFRA_NETWORK) { 148132c10874SJouni Malinen sinfo->filled |= STATION_INFO_BSS_PARAM; 148232c10874SJouni Malinen sinfo->bss_param.flags = 0; 1483cf5333d7SVasanthakumar Thiagarajan sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period; 1484cf5333d7SVasanthakumar Thiagarajan sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int; 148532c10874SJouni Malinen } 148632c10874SJouni Malinen 1487bdcd8170SKalle Valo return 0; 1488bdcd8170SKalle Valo } 1489bdcd8170SKalle Valo 1490bdcd8170SKalle Valo static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, 1491bdcd8170SKalle Valo struct cfg80211_pmksa *pmksa) 1492bdcd8170SKalle Valo { 1493bdcd8170SKalle Valo struct ath6kl *ar = ath6kl_priv(netdev); 1494334234b5SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(netdev); 1495334234b5SVasanthakumar Thiagarajan 1496334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid, 1497bdcd8170SKalle Valo pmksa->pmkid, true); 1498bdcd8170SKalle Valo } 1499bdcd8170SKalle Valo 1500bdcd8170SKalle Valo static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, 1501bdcd8170SKalle Valo struct cfg80211_pmksa *pmksa) 1502bdcd8170SKalle Valo { 1503bdcd8170SKalle Valo struct ath6kl *ar = ath6kl_priv(netdev); 1504334234b5SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(netdev); 1505334234b5SVasanthakumar Thiagarajan 1506334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid, 1507bdcd8170SKalle Valo pmksa->pmkid, false); 1508bdcd8170SKalle Valo } 1509bdcd8170SKalle Valo 1510bdcd8170SKalle Valo static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) 1511bdcd8170SKalle Valo { 1512bdcd8170SKalle Valo struct ath6kl *ar = ath6kl_priv(netdev); 151359c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(netdev); 151459c98449SVasanthakumar Thiagarajan 151559c98449SVasanthakumar Thiagarajan if (test_bit(CONNECTED, &vif->flags)) 1516334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, 1517334234b5SVasanthakumar Thiagarajan vif->bssid, NULL, false); 1518bdcd8170SKalle Valo return 0; 1519bdcd8170SKalle Valo } 1520bdcd8170SKalle Valo 1521abcb344bSKalle Valo #ifdef CONFIG_PM 1522abcb344bSKalle Valo static int ar6k_cfg80211_suspend(struct wiphy *wiphy, 1523abcb344bSKalle Valo struct cfg80211_wowlan *wow) 1524abcb344bSKalle Valo { 1525abcb344bSKalle Valo struct ath6kl *ar = wiphy_priv(wiphy); 1526abcb344bSKalle Valo 1527abcb344bSKalle Valo return ath6kl_hif_suspend(ar); 1528abcb344bSKalle Valo } 1529aa6cffc1SChilam Ng 1530aa6cffc1SChilam Ng static int ar6k_cfg80211_resume(struct wiphy *wiphy) 1531aa6cffc1SChilam Ng { 1532aa6cffc1SChilam Ng struct ath6kl *ar = wiphy_priv(wiphy); 1533aa6cffc1SChilam Ng 1534aa6cffc1SChilam Ng return ath6kl_hif_resume(ar); 1535aa6cffc1SChilam Ng } 1536abcb344bSKalle Valo #endif 1537abcb344bSKalle Valo 15386a7c9badSJouni Malinen static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, 15396a7c9badSJouni Malinen struct ieee80211_channel *chan, 15406a7c9badSJouni Malinen enum nl80211_channel_type channel_type) 15416a7c9badSJouni Malinen { 15426a7c9badSJouni Malinen struct ath6kl *ar = ath6kl_priv(dev); 1543cf5333d7SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 15446a7c9badSJouni Malinen 15456a7c9badSJouni Malinen if (!ath6kl_cfg80211_ready(ar)) 15466a7c9badSJouni Malinen return -EIO; 15476a7c9badSJouni Malinen 15486a7c9badSJouni Malinen ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", 15496a7c9badSJouni Malinen __func__, chan->center_freq, chan->hw_value); 1550cf5333d7SVasanthakumar Thiagarajan vif->next_chan = chan->center_freq; 15516a7c9badSJouni Malinen 15526a7c9badSJouni Malinen return 0; 15536a7c9badSJouni Malinen } 15546a7c9badSJouni Malinen 15558bdfbf40SJouni Malinen static bool ath6kl_is_p2p_ie(const u8 *pos) 15568bdfbf40SJouni Malinen { 15578bdfbf40SJouni Malinen return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 15588bdfbf40SJouni Malinen pos[2] == 0x50 && pos[3] == 0x6f && 15598bdfbf40SJouni Malinen pos[4] == 0x9a && pos[5] == 0x09; 15608bdfbf40SJouni Malinen } 15618bdfbf40SJouni Malinen 1562334234b5SVasanthakumar Thiagarajan static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif, 1563334234b5SVasanthakumar Thiagarajan const u8 *ies, size_t ies_len) 15648bdfbf40SJouni Malinen { 1565334234b5SVasanthakumar Thiagarajan struct ath6kl *ar = vif->ar; 15668bdfbf40SJouni Malinen const u8 *pos; 15678bdfbf40SJouni Malinen u8 *buf = NULL; 15688bdfbf40SJouni Malinen size_t len = 0; 15698bdfbf40SJouni Malinen int ret; 15708bdfbf40SJouni Malinen 15718bdfbf40SJouni Malinen /* 15728bdfbf40SJouni Malinen * Filter out P2P IE(s) since they will be included depending on 15738bdfbf40SJouni Malinen * the Probe Request frame in ath6kl_send_go_probe_resp(). 15748bdfbf40SJouni Malinen */ 15758bdfbf40SJouni Malinen 15768bdfbf40SJouni Malinen if (ies && ies_len) { 15778bdfbf40SJouni Malinen buf = kmalloc(ies_len, GFP_KERNEL); 15788bdfbf40SJouni Malinen if (buf == NULL) 15798bdfbf40SJouni Malinen return -ENOMEM; 15808bdfbf40SJouni Malinen pos = ies; 15818bdfbf40SJouni Malinen while (pos + 1 < ies + ies_len) { 15828bdfbf40SJouni Malinen if (pos + 2 + pos[1] > ies + ies_len) 15838bdfbf40SJouni Malinen break; 15848bdfbf40SJouni Malinen if (!ath6kl_is_p2p_ie(pos)) { 15858bdfbf40SJouni Malinen memcpy(buf + len, pos, 2 + pos[1]); 15868bdfbf40SJouni Malinen len += 2 + pos[1]; 15878bdfbf40SJouni Malinen } 15888bdfbf40SJouni Malinen pos += 2 + pos[1]; 15898bdfbf40SJouni Malinen } 15908bdfbf40SJouni Malinen } 15918bdfbf40SJouni Malinen 1592334234b5SVasanthakumar Thiagarajan ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, 1593334234b5SVasanthakumar Thiagarajan WMI_FRAME_PROBE_RESP, buf, len); 15948bdfbf40SJouni Malinen kfree(buf); 15958bdfbf40SJouni Malinen return ret; 15968bdfbf40SJouni Malinen } 15978bdfbf40SJouni Malinen 15986a7c9badSJouni Malinen static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, 15996a7c9badSJouni Malinen struct beacon_parameters *info, bool add) 16006a7c9badSJouni Malinen { 16016a7c9badSJouni Malinen struct ath6kl *ar = ath6kl_priv(dev); 16023450334fSVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 16036a7c9badSJouni Malinen struct ieee80211_mgmt *mgmt; 16046a7c9badSJouni Malinen u8 *ies; 16056a7c9badSJouni Malinen int ies_len; 16066a7c9badSJouni Malinen struct wmi_connect_cmd p; 16076a7c9badSJouni Malinen int res; 16086a7c9badSJouni Malinen int i; 16096a7c9badSJouni Malinen 16106a7c9badSJouni Malinen ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); 16116a7c9badSJouni Malinen 16126a7c9badSJouni Malinen if (!ath6kl_cfg80211_ready(ar)) 16136a7c9badSJouni Malinen return -EIO; 16146a7c9badSJouni Malinen 1615f5938f24SVasanthakumar Thiagarajan if (vif->next_mode != AP_NETWORK) 16166a7c9badSJouni Malinen return -EOPNOTSUPP; 16176a7c9badSJouni Malinen 16186a7c9badSJouni Malinen if (info->beacon_ies) { 1619334234b5SVasanthakumar Thiagarajan res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, 1620334234b5SVasanthakumar Thiagarajan WMI_FRAME_BEACON, 16216a7c9badSJouni Malinen info->beacon_ies, 16226a7c9badSJouni Malinen info->beacon_ies_len); 16236a7c9badSJouni Malinen if (res) 16246a7c9badSJouni Malinen return res; 16256a7c9badSJouni Malinen } 16266a7c9badSJouni Malinen if (info->proberesp_ies) { 1627334234b5SVasanthakumar Thiagarajan res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies, 16286a7c9badSJouni Malinen info->proberesp_ies_len); 16296a7c9badSJouni Malinen if (res) 16306a7c9badSJouni Malinen return res; 16316a7c9badSJouni Malinen } 16326a7c9badSJouni Malinen if (info->assocresp_ies) { 1633334234b5SVasanthakumar Thiagarajan res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, 1634334234b5SVasanthakumar Thiagarajan WMI_FRAME_ASSOC_RESP, 16356a7c9badSJouni Malinen info->assocresp_ies, 16366a7c9badSJouni Malinen info->assocresp_ies_len); 16376a7c9badSJouni Malinen if (res) 16386a7c9badSJouni Malinen return res; 16396a7c9badSJouni Malinen } 16406a7c9badSJouni Malinen 16416a7c9badSJouni Malinen if (!add) 16426a7c9badSJouni Malinen return 0; 16436a7c9badSJouni Malinen 16449a5b1318SJouni Malinen ar->ap_mode_bkey.valid = false; 16459a5b1318SJouni Malinen 16466a7c9badSJouni Malinen /* TODO: 16476a7c9badSJouni Malinen * info->interval 16486a7c9badSJouni Malinen * info->dtim_period 16496a7c9badSJouni Malinen */ 16506a7c9badSJouni Malinen 16516a7c9badSJouni Malinen if (info->head == NULL) 16526a7c9badSJouni Malinen return -EINVAL; 16536a7c9badSJouni Malinen mgmt = (struct ieee80211_mgmt *) info->head; 16546a7c9badSJouni Malinen ies = mgmt->u.beacon.variable; 16556a7c9badSJouni Malinen if (ies > info->head + info->head_len) 16566a7c9badSJouni Malinen return -EINVAL; 16576a7c9badSJouni Malinen ies_len = info->head + info->head_len - ies; 16586a7c9badSJouni Malinen 16596a7c9badSJouni Malinen if (info->ssid == NULL) 16606a7c9badSJouni Malinen return -EINVAL; 16613450334fSVasanthakumar Thiagarajan memcpy(vif->ssid, info->ssid, info->ssid_len); 16623450334fSVasanthakumar Thiagarajan vif->ssid_len = info->ssid_len; 16636a7c9badSJouni Malinen if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE) 16646a7c9badSJouni Malinen return -EOPNOTSUPP; /* TODO */ 16656a7c9badSJouni Malinen 16663450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode = OPEN_AUTH; 16676a7c9badSJouni Malinen 16686a7c9badSJouni Malinen memset(&p, 0, sizeof(p)); 16696a7c9badSJouni Malinen 16706a7c9badSJouni Malinen for (i = 0; i < info->crypto.n_akm_suites; i++) { 16716a7c9badSJouni Malinen switch (info->crypto.akm_suites[i]) { 16726a7c9badSJouni Malinen case WLAN_AKM_SUITE_8021X: 16736a7c9badSJouni Malinen if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) 16746a7c9badSJouni Malinen p.auth_mode |= WPA_AUTH; 16756a7c9badSJouni Malinen if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) 16766a7c9badSJouni Malinen p.auth_mode |= WPA2_AUTH; 16776a7c9badSJouni Malinen break; 16786a7c9badSJouni Malinen case WLAN_AKM_SUITE_PSK: 16796a7c9badSJouni Malinen if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) 16806a7c9badSJouni Malinen p.auth_mode |= WPA_PSK_AUTH; 16816a7c9badSJouni Malinen if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) 16826a7c9badSJouni Malinen p.auth_mode |= WPA2_PSK_AUTH; 16836a7c9badSJouni Malinen break; 16846a7c9badSJouni Malinen } 16856a7c9badSJouni Malinen } 16866a7c9badSJouni Malinen if (p.auth_mode == 0) 16876a7c9badSJouni Malinen p.auth_mode = NONE_AUTH; 16883450334fSVasanthakumar Thiagarajan vif->auth_mode = p.auth_mode; 16896a7c9badSJouni Malinen 16906a7c9badSJouni Malinen for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) { 16916a7c9badSJouni Malinen switch (info->crypto.ciphers_pairwise[i]) { 16926a7c9badSJouni Malinen case WLAN_CIPHER_SUITE_WEP40: 16936a7c9badSJouni Malinen case WLAN_CIPHER_SUITE_WEP104: 16946a7c9badSJouni Malinen p.prwise_crypto_type |= WEP_CRYPT; 16956a7c9badSJouni Malinen break; 16966a7c9badSJouni Malinen case WLAN_CIPHER_SUITE_TKIP: 16976a7c9badSJouni Malinen p.prwise_crypto_type |= TKIP_CRYPT; 16986a7c9badSJouni Malinen break; 16996a7c9badSJouni Malinen case WLAN_CIPHER_SUITE_CCMP: 17006a7c9badSJouni Malinen p.prwise_crypto_type |= AES_CRYPT; 17016a7c9badSJouni Malinen break; 17026a7c9badSJouni Malinen } 17036a7c9badSJouni Malinen } 1704229ed6b5SEdward Lu if (p.prwise_crypto_type == 0) { 17056a7c9badSJouni Malinen p.prwise_crypto_type = NONE_CRYPT; 1706240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, 0, true); 1707229ed6b5SEdward Lu } else if (info->crypto.n_ciphers_pairwise == 1) 1708240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true); 17096a7c9badSJouni Malinen 17106a7c9badSJouni Malinen switch (info->crypto.cipher_group) { 17116a7c9badSJouni Malinen case WLAN_CIPHER_SUITE_WEP40: 17126a7c9badSJouni Malinen case WLAN_CIPHER_SUITE_WEP104: 17136a7c9badSJouni Malinen p.grp_crypto_type = WEP_CRYPT; 17146a7c9badSJouni Malinen break; 17156a7c9badSJouni Malinen case WLAN_CIPHER_SUITE_TKIP: 17166a7c9badSJouni Malinen p.grp_crypto_type = TKIP_CRYPT; 17176a7c9badSJouni Malinen break; 17186a7c9badSJouni Malinen case WLAN_CIPHER_SUITE_CCMP: 17196a7c9badSJouni Malinen p.grp_crypto_type = AES_CRYPT; 17206a7c9badSJouni Malinen break; 17216a7c9badSJouni Malinen default: 17226a7c9badSJouni Malinen p.grp_crypto_type = NONE_CRYPT; 17236a7c9badSJouni Malinen break; 17246a7c9badSJouni Malinen } 1725240d2799SVasanthakumar Thiagarajan ath6kl_set_cipher(vif, info->crypto.cipher_group, false); 17266a7c9badSJouni Malinen 17276a7c9badSJouni Malinen p.nw_type = AP_NETWORK; 1728f5938f24SVasanthakumar Thiagarajan vif->nw_type = vif->next_mode; 17296a7c9badSJouni Malinen 17303450334fSVasanthakumar Thiagarajan p.ssid_len = vif->ssid_len; 17313450334fSVasanthakumar Thiagarajan memcpy(p.ssid, vif->ssid, vif->ssid_len); 17323450334fSVasanthakumar Thiagarajan p.dot11_auth_mode = vif->dot11_auth_mode; 1733cf5333d7SVasanthakumar Thiagarajan p.ch = cpu_to_le16(vif->next_chan); 17346a7c9badSJouni Malinen 1735334234b5SVasanthakumar Thiagarajan res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); 17369a5b1318SJouni Malinen if (res < 0) 17379a5b1318SJouni Malinen return res; 17389a5b1318SJouni Malinen 17399a5b1318SJouni Malinen return 0; 17406a7c9badSJouni Malinen } 17416a7c9badSJouni Malinen 17426a7c9badSJouni Malinen static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev, 17436a7c9badSJouni Malinen struct beacon_parameters *info) 17446a7c9badSJouni Malinen { 17456a7c9badSJouni Malinen return ath6kl_ap_beacon(wiphy, dev, info, true); 17466a7c9badSJouni Malinen } 17476a7c9badSJouni Malinen 17486a7c9badSJouni Malinen static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev, 17496a7c9badSJouni Malinen struct beacon_parameters *info) 17506a7c9badSJouni Malinen { 17516a7c9badSJouni Malinen return ath6kl_ap_beacon(wiphy, dev, info, false); 17526a7c9badSJouni Malinen } 17536a7c9badSJouni Malinen 17546a7c9badSJouni Malinen static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) 17556a7c9badSJouni Malinen { 17566a7c9badSJouni Malinen struct ath6kl *ar = ath6kl_priv(dev); 175759c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 17586a7c9badSJouni Malinen 1759f5938f24SVasanthakumar Thiagarajan if (vif->nw_type != AP_NETWORK) 17606a7c9badSJouni Malinen return -EOPNOTSUPP; 176159c98449SVasanthakumar Thiagarajan if (!test_bit(CONNECTED, &vif->flags)) 17626a7c9badSJouni Malinen return -ENOTCONN; 17636a7c9badSJouni Malinen 1764334234b5SVasanthakumar Thiagarajan ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); 176559c98449SVasanthakumar Thiagarajan clear_bit(CONNECTED, &vif->flags); 17666a7c9badSJouni Malinen 17676a7c9badSJouni Malinen return 0; 17686a7c9badSJouni Malinen } 17696a7c9badSJouni Malinen 177023875136SJouni Malinen static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, 177123875136SJouni Malinen u8 *mac, struct station_parameters *params) 177223875136SJouni Malinen { 177323875136SJouni Malinen struct ath6kl *ar = ath6kl_priv(dev); 1774f5938f24SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 177523875136SJouni Malinen 1776f5938f24SVasanthakumar Thiagarajan if (vif->nw_type != AP_NETWORK) 177723875136SJouni Malinen return -EOPNOTSUPP; 177823875136SJouni Malinen 177923875136SJouni Malinen /* Use this only for authorizing/unauthorizing a station */ 178023875136SJouni Malinen if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) 178123875136SJouni Malinen return -EOPNOTSUPP; 178223875136SJouni Malinen 178323875136SJouni Malinen if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) 1784334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, 1785334234b5SVasanthakumar Thiagarajan WMI_AP_MLME_AUTHORIZE, mac, 0); 1786334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, 1787334234b5SVasanthakumar Thiagarajan WMI_AP_MLME_UNAUTHORIZE, mac, 0); 178823875136SJouni Malinen } 178923875136SJouni Malinen 179063fa1e0cSJouni Malinen static int ath6kl_remain_on_channel(struct wiphy *wiphy, 179163fa1e0cSJouni Malinen struct net_device *dev, 179263fa1e0cSJouni Malinen struct ieee80211_channel *chan, 179363fa1e0cSJouni Malinen enum nl80211_channel_type channel_type, 179463fa1e0cSJouni Malinen unsigned int duration, 179563fa1e0cSJouni Malinen u64 *cookie) 179663fa1e0cSJouni Malinen { 179763fa1e0cSJouni Malinen struct ath6kl *ar = ath6kl_priv(dev); 1798334234b5SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 179963fa1e0cSJouni Malinen 180063fa1e0cSJouni Malinen /* TODO: if already pending or ongoing remain-on-channel, 180163fa1e0cSJouni Malinen * return -EBUSY */ 180263fa1e0cSJouni Malinen *cookie = 1; /* only a single pending request is supported */ 180363fa1e0cSJouni Malinen 1804334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx, 1805334234b5SVasanthakumar Thiagarajan chan->center_freq, duration); 180663fa1e0cSJouni Malinen } 180763fa1e0cSJouni Malinen 180863fa1e0cSJouni Malinen static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, 180963fa1e0cSJouni Malinen struct net_device *dev, 181063fa1e0cSJouni Malinen u64 cookie) 181163fa1e0cSJouni Malinen { 181263fa1e0cSJouni Malinen struct ath6kl *ar = ath6kl_priv(dev); 1813334234b5SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 181463fa1e0cSJouni Malinen 181563fa1e0cSJouni Malinen if (cookie != 1) 181663fa1e0cSJouni Malinen return -ENOENT; 181763fa1e0cSJouni Malinen 1818334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx); 181963fa1e0cSJouni Malinen } 182063fa1e0cSJouni Malinen 1821334234b5SVasanthakumar Thiagarajan static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif, 1822334234b5SVasanthakumar Thiagarajan const u8 *buf, size_t len, 1823334234b5SVasanthakumar Thiagarajan unsigned int freq) 18248bdfbf40SJouni Malinen { 1825334234b5SVasanthakumar Thiagarajan struct ath6kl *ar = vif->ar; 18268bdfbf40SJouni Malinen const u8 *pos; 18278bdfbf40SJouni Malinen u8 *p2p; 18288bdfbf40SJouni Malinen int p2p_len; 18298bdfbf40SJouni Malinen int ret; 18308bdfbf40SJouni Malinen const struct ieee80211_mgmt *mgmt; 18318bdfbf40SJouni Malinen 18328bdfbf40SJouni Malinen mgmt = (const struct ieee80211_mgmt *) buf; 18338bdfbf40SJouni Malinen 18348bdfbf40SJouni Malinen /* Include P2P IE(s) from the frame generated in user space. */ 18358bdfbf40SJouni Malinen 18368bdfbf40SJouni Malinen p2p = kmalloc(len, GFP_KERNEL); 18378bdfbf40SJouni Malinen if (p2p == NULL) 18388bdfbf40SJouni Malinen return -ENOMEM; 18398bdfbf40SJouni Malinen p2p_len = 0; 18408bdfbf40SJouni Malinen 18418bdfbf40SJouni Malinen pos = mgmt->u.probe_resp.variable; 18428bdfbf40SJouni Malinen while (pos + 1 < buf + len) { 18438bdfbf40SJouni Malinen if (pos + 2 + pos[1] > buf + len) 18448bdfbf40SJouni Malinen break; 18458bdfbf40SJouni Malinen if (ath6kl_is_p2p_ie(pos)) { 18468bdfbf40SJouni Malinen memcpy(p2p + p2p_len, pos, 2 + pos[1]); 18478bdfbf40SJouni Malinen p2p_len += 2 + pos[1]; 18488bdfbf40SJouni Malinen } 18498bdfbf40SJouni Malinen pos += 2 + pos[1]; 18508bdfbf40SJouni Malinen } 18518bdfbf40SJouni Malinen 1852334234b5SVasanthakumar Thiagarajan ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq, 1853334234b5SVasanthakumar Thiagarajan mgmt->da, p2p, p2p_len); 18548bdfbf40SJouni Malinen kfree(p2p); 18558bdfbf40SJouni Malinen return ret; 18568bdfbf40SJouni Malinen } 18578bdfbf40SJouni Malinen 18588a6c8060SJouni Malinen static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, 18598a6c8060SJouni Malinen struct ieee80211_channel *chan, bool offchan, 18608a6c8060SJouni Malinen enum nl80211_channel_type channel_type, 18618a6c8060SJouni Malinen bool channel_type_valid, unsigned int wait, 1862e247bd90SJohannes Berg const u8 *buf, size_t len, bool no_cck, 1863e247bd90SJohannes Berg bool dont_wait_for_ack, u64 *cookie) 18648a6c8060SJouni Malinen { 18658a6c8060SJouni Malinen struct ath6kl *ar = ath6kl_priv(dev); 186659c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 18678a6c8060SJouni Malinen u32 id; 18688bdfbf40SJouni Malinen const struct ieee80211_mgmt *mgmt; 18698bdfbf40SJouni Malinen 18708bdfbf40SJouni Malinen mgmt = (const struct ieee80211_mgmt *) buf; 18718bdfbf40SJouni Malinen if (buf + len >= mgmt->u.probe_resp.variable && 1872f5938f24SVasanthakumar Thiagarajan vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && 18738bdfbf40SJouni Malinen ieee80211_is_probe_resp(mgmt->frame_control)) { 18748bdfbf40SJouni Malinen /* 18758bdfbf40SJouni Malinen * Send Probe Response frame in AP mode using a separate WMI 18768bdfbf40SJouni Malinen * command to allow the target to fill in the generic IEs. 18778bdfbf40SJouni Malinen */ 18788bdfbf40SJouni Malinen *cookie = 0; /* TX status not supported */ 1879334234b5SVasanthakumar Thiagarajan return ath6kl_send_go_probe_resp(vif, buf, len, 18808bdfbf40SJouni Malinen chan->center_freq); 18818bdfbf40SJouni Malinen } 18828a6c8060SJouni Malinen 1883cf5333d7SVasanthakumar Thiagarajan id = vif->send_action_id++; 18848a6c8060SJouni Malinen if (id == 0) { 18858a6c8060SJouni Malinen /* 18868a6c8060SJouni Malinen * 0 is a reserved value in the WMI command and shall not be 18878a6c8060SJouni Malinen * used for the command. 18888a6c8060SJouni Malinen */ 1889cf5333d7SVasanthakumar Thiagarajan id = vif->send_action_id++; 18908a6c8060SJouni Malinen } 18918a6c8060SJouni Malinen 18928a6c8060SJouni Malinen *cookie = id; 1893334234b5SVasanthakumar Thiagarajan return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id, 1894334234b5SVasanthakumar Thiagarajan chan->center_freq, wait, 18958a6c8060SJouni Malinen buf, len); 18968a6c8060SJouni Malinen } 18978a6c8060SJouni Malinen 1898ae32c30aSJouni Malinen static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, 1899ae32c30aSJouni Malinen struct net_device *dev, 1900ae32c30aSJouni Malinen u16 frame_type, bool reg) 1901ae32c30aSJouni Malinen { 1902cf5333d7SVasanthakumar Thiagarajan struct ath6kl_vif *vif = netdev_priv(dev); 1903ae32c30aSJouni Malinen 1904ae32c30aSJouni Malinen ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n", 1905ae32c30aSJouni Malinen __func__, frame_type, reg); 1906ae32c30aSJouni Malinen if (frame_type == IEEE80211_STYPE_PROBE_REQ) { 1907ae32c30aSJouni Malinen /* 1908ae32c30aSJouni Malinen * Note: This notification callback is not allowed to sleep, so 1909ae32c30aSJouni Malinen * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we 1910ae32c30aSJouni Malinen * hardcode target to report Probe Request frames all the time. 1911ae32c30aSJouni Malinen */ 1912cf5333d7SVasanthakumar Thiagarajan vif->probe_req_report = reg; 1913ae32c30aSJouni Malinen } 1914ae32c30aSJouni Malinen } 1915ae32c30aSJouni Malinen 1916f80574aeSJouni Malinen static const struct ieee80211_txrx_stypes 1917f80574aeSJouni Malinen ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { 1918f80574aeSJouni Malinen [NL80211_IFTYPE_STATION] = { 1919f80574aeSJouni Malinen .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 1920f80574aeSJouni Malinen BIT(IEEE80211_STYPE_PROBE_RESP >> 4), 1921f80574aeSJouni Malinen .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 1922f80574aeSJouni Malinen BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 1923f80574aeSJouni Malinen }, 1924f80574aeSJouni Malinen [NL80211_IFTYPE_P2P_CLIENT] = { 1925f80574aeSJouni Malinen .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 1926f80574aeSJouni Malinen BIT(IEEE80211_STYPE_PROBE_RESP >> 4), 1927f80574aeSJouni Malinen .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 1928f80574aeSJouni Malinen BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 1929f80574aeSJouni Malinen }, 1930f80574aeSJouni Malinen [NL80211_IFTYPE_P2P_GO] = { 1931f80574aeSJouni Malinen .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 1932f80574aeSJouni Malinen BIT(IEEE80211_STYPE_PROBE_RESP >> 4), 1933f80574aeSJouni Malinen .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 1934f80574aeSJouni Malinen BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 1935f80574aeSJouni Malinen }, 1936f80574aeSJouni Malinen }; 1937f80574aeSJouni Malinen 1938bdcd8170SKalle Valo static struct cfg80211_ops ath6kl_cfg80211_ops = { 1939bdcd8170SKalle Valo .change_virtual_intf = ath6kl_cfg80211_change_iface, 1940bdcd8170SKalle Valo .scan = ath6kl_cfg80211_scan, 1941bdcd8170SKalle Valo .connect = ath6kl_cfg80211_connect, 1942bdcd8170SKalle Valo .disconnect = ath6kl_cfg80211_disconnect, 1943bdcd8170SKalle Valo .add_key = ath6kl_cfg80211_add_key, 1944bdcd8170SKalle Valo .get_key = ath6kl_cfg80211_get_key, 1945bdcd8170SKalle Valo .del_key = ath6kl_cfg80211_del_key, 1946bdcd8170SKalle Valo .set_default_key = ath6kl_cfg80211_set_default_key, 1947bdcd8170SKalle Valo .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params, 1948bdcd8170SKalle Valo .set_tx_power = ath6kl_cfg80211_set_txpower, 1949bdcd8170SKalle Valo .get_tx_power = ath6kl_cfg80211_get_txpower, 1950bdcd8170SKalle Valo .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt, 1951bdcd8170SKalle Valo .join_ibss = ath6kl_cfg80211_join_ibss, 1952bdcd8170SKalle Valo .leave_ibss = ath6kl_cfg80211_leave_ibss, 1953bdcd8170SKalle Valo .get_station = ath6kl_get_station, 1954bdcd8170SKalle Valo .set_pmksa = ath6kl_set_pmksa, 1955bdcd8170SKalle Valo .del_pmksa = ath6kl_del_pmksa, 1956bdcd8170SKalle Valo .flush_pmksa = ath6kl_flush_pmksa, 1957003353b0SKalle Valo CFG80211_TESTMODE_CMD(ath6kl_tm_cmd) 1958abcb344bSKalle Valo #ifdef CONFIG_PM 1959abcb344bSKalle Valo .suspend = ar6k_cfg80211_suspend, 1960aa6cffc1SChilam Ng .resume = ar6k_cfg80211_resume, 1961abcb344bSKalle Valo #endif 19626a7c9badSJouni Malinen .set_channel = ath6kl_set_channel, 19636a7c9badSJouni Malinen .add_beacon = ath6kl_add_beacon, 19646a7c9badSJouni Malinen .set_beacon = ath6kl_set_beacon, 19656a7c9badSJouni Malinen .del_beacon = ath6kl_del_beacon, 196623875136SJouni Malinen .change_station = ath6kl_change_station, 196763fa1e0cSJouni Malinen .remain_on_channel = ath6kl_remain_on_channel, 196863fa1e0cSJouni Malinen .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, 19698a6c8060SJouni Malinen .mgmt_tx = ath6kl_mgmt_tx, 1970ae32c30aSJouni Malinen .mgmt_frame_register = ath6kl_mgmt_frame_register, 1971bdcd8170SKalle Valo }; 1972bdcd8170SKalle Valo 19738dafb70eSVasanthakumar Thiagarajan struct ath6kl *ath6kl_core_alloc(struct device *dev) 1974bdcd8170SKalle Valo { 19756bbc7c35SJouni Malinen struct ath6kl *ar; 1976be98e3a4SVasanthakumar Thiagarajan struct wiphy *wiphy; 19778dafb70eSVasanthakumar Thiagarajan u8 ctr; 1978bdcd8170SKalle Valo 1979bdcd8170SKalle Valo /* create a new wiphy for use with cfg80211 */ 1980be98e3a4SVasanthakumar Thiagarajan wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl)); 19818dafb70eSVasanthakumar Thiagarajan 1982be98e3a4SVasanthakumar Thiagarajan if (!wiphy) { 1983bdcd8170SKalle Valo ath6kl_err("couldn't allocate wiphy device\n"); 1984bdcd8170SKalle Valo return NULL; 1985bdcd8170SKalle Valo } 1986bdcd8170SKalle Valo 1987be98e3a4SVasanthakumar Thiagarajan ar = wiphy_priv(wiphy); 19886bbc7c35SJouni Malinen ar->p2p = !!ath6kl_p2p; 1989be98e3a4SVasanthakumar Thiagarajan ar->wiphy = wiphy; 19908dafb70eSVasanthakumar Thiagarajan ar->dev = dev; 19918dafb70eSVasanthakumar Thiagarajan 19928dafb70eSVasanthakumar Thiagarajan spin_lock_init(&ar->lock); 19938dafb70eSVasanthakumar Thiagarajan spin_lock_init(&ar->mcastpsq_lock); 19948dafb70eSVasanthakumar Thiagarajan 19958dafb70eSVasanthakumar Thiagarajan init_waitqueue_head(&ar->event_wq); 19968dafb70eSVasanthakumar Thiagarajan sema_init(&ar->sem, 1); 19978dafb70eSVasanthakumar Thiagarajan 19988dafb70eSVasanthakumar Thiagarajan INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue); 19998dafb70eSVasanthakumar Thiagarajan 20008dafb70eSVasanthakumar Thiagarajan clear_bit(WMI_ENABLED, &ar->flag); 20018dafb70eSVasanthakumar Thiagarajan clear_bit(SKIP_SCAN, &ar->flag); 20028dafb70eSVasanthakumar Thiagarajan clear_bit(DESTROY_IN_PROGRESS, &ar->flag); 20038dafb70eSVasanthakumar Thiagarajan 20048dafb70eSVasanthakumar Thiagarajan ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL; 20058dafb70eSVasanthakumar Thiagarajan ar->listen_intvl_b = 0; 20068dafb70eSVasanthakumar Thiagarajan ar->tx_pwr = 0; 20078dafb70eSVasanthakumar Thiagarajan 20088dafb70eSVasanthakumar Thiagarajan ar->intra_bss = 1; 20098dafb70eSVasanthakumar Thiagarajan memset(&ar->sc_params, 0, sizeof(ar->sc_params)); 20108dafb70eSVasanthakumar Thiagarajan ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT; 20118dafb70eSVasanthakumar Thiagarajan ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS; 20128dafb70eSVasanthakumar Thiagarajan ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; 20138dafb70eSVasanthakumar Thiagarajan 20148dafb70eSVasanthakumar Thiagarajan memset((u8 *)ar->sta_list, 0, 20158dafb70eSVasanthakumar Thiagarajan AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); 20168dafb70eSVasanthakumar Thiagarajan 20178dafb70eSVasanthakumar Thiagarajan /* Init the PS queues */ 20188dafb70eSVasanthakumar Thiagarajan for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { 20198dafb70eSVasanthakumar Thiagarajan spin_lock_init(&ar->sta_list[ctr].psq_lock); 20208dafb70eSVasanthakumar Thiagarajan skb_queue_head_init(&ar->sta_list[ctr].psq); 20218dafb70eSVasanthakumar Thiagarajan } 20228dafb70eSVasanthakumar Thiagarajan 20238dafb70eSVasanthakumar Thiagarajan skb_queue_head_init(&ar->mcastpsq); 20248dafb70eSVasanthakumar Thiagarajan 20258dafb70eSVasanthakumar Thiagarajan memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3); 20268dafb70eSVasanthakumar Thiagarajan 20278dafb70eSVasanthakumar Thiagarajan return ar; 20288dafb70eSVasanthakumar Thiagarajan } 20298dafb70eSVasanthakumar Thiagarajan 20308dafb70eSVasanthakumar Thiagarajan int ath6kl_register_ieee80211_hw(struct ath6kl *ar) 20318dafb70eSVasanthakumar Thiagarajan { 20328dafb70eSVasanthakumar Thiagarajan struct wiphy *wiphy = ar->wiphy; 20338dafb70eSVasanthakumar Thiagarajan int ret; 20346bbc7c35SJouni Malinen 2035be98e3a4SVasanthakumar Thiagarajan wiphy->mgmt_stypes = ath6kl_mgmt_stypes; 2036f80574aeSJouni Malinen 2037be98e3a4SVasanthakumar Thiagarajan wiphy->max_remain_on_channel_duration = 5000; 203863fa1e0cSJouni Malinen 2039bdcd8170SKalle Valo /* set device pointer for wiphy */ 20408dafb70eSVasanthakumar Thiagarajan set_wiphy_dev(wiphy, ar->dev); 2041bdcd8170SKalle Valo 2042be98e3a4SVasanthakumar Thiagarajan wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 20438dafb70eSVasanthakumar Thiagarajan BIT(NL80211_IFTYPE_ADHOC) | 20448dafb70eSVasanthakumar Thiagarajan BIT(NL80211_IFTYPE_AP); 20456bbc7c35SJouni Malinen if (ar->p2p) { 2046be98e3a4SVasanthakumar Thiagarajan wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) | 20476bbc7c35SJouni Malinen BIT(NL80211_IFTYPE_P2P_CLIENT); 20486bbc7c35SJouni Malinen } 20498dafb70eSVasanthakumar Thiagarajan 2050bdcd8170SKalle Valo /* max num of ssids that can be probed during scanning */ 2051be98e3a4SVasanthakumar Thiagarajan wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; 2052be98e3a4SVasanthakumar Thiagarajan wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ 2053be98e3a4SVasanthakumar Thiagarajan wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; 2054be98e3a4SVasanthakumar Thiagarajan wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; 2055be98e3a4SVasanthakumar Thiagarajan wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 2056bdcd8170SKalle Valo 2057be98e3a4SVasanthakumar Thiagarajan wiphy->cipher_suites = cipher_suites; 2058be98e3a4SVasanthakumar Thiagarajan wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); 2059bdcd8170SKalle Valo 2060be98e3a4SVasanthakumar Thiagarajan ret = wiphy_register(wiphy); 2061bdcd8170SKalle Valo if (ret < 0) { 2062bdcd8170SKalle Valo ath6kl_err("couldn't register wiphy device\n"); 20638dafb70eSVasanthakumar Thiagarajan return ret; 20648dafb70eSVasanthakumar Thiagarajan } 20658dafb70eSVasanthakumar Thiagarajan 20668dafb70eSVasanthakumar Thiagarajan return 0; 20678dafb70eSVasanthakumar Thiagarajan } 20688dafb70eSVasanthakumar Thiagarajan 2069108438bcSVasanthakumar Thiagarajan static int ath6kl_init_if_data(struct ath6kl_vif *vif) 20708dafb70eSVasanthakumar Thiagarajan { 20712132c69cSVasanthakumar Thiagarajan vif->aggr_cntxt = aggr_init(vif->ndev); 20722132c69cSVasanthakumar Thiagarajan if (!vif->aggr_cntxt) { 20738dafb70eSVasanthakumar Thiagarajan ath6kl_err("failed to initialize aggr\n"); 20748dafb70eSVasanthakumar Thiagarajan return -ENOMEM; 20758dafb70eSVasanthakumar Thiagarajan } 20768dafb70eSVasanthakumar Thiagarajan 2077de3ad713SVasanthakumar Thiagarajan setup_timer(&vif->disconnect_timer, disconnect_timer_handler, 2078108438bcSVasanthakumar Thiagarajan (unsigned long) vif->ndev); 2079de3ad713SVasanthakumar Thiagarajan set_bit(WMM_ENABLED, &vif->flags); 2080478ac027SVasanthakumar Thiagarajan spin_lock_init(&vif->if_lock); 20818dafb70eSVasanthakumar Thiagarajan 20828dafb70eSVasanthakumar Thiagarajan return 0; 20838dafb70eSVasanthakumar Thiagarajan } 20848dafb70eSVasanthakumar Thiagarajan 2085108438bcSVasanthakumar Thiagarajan void ath6kl_deinit_if_data(struct ath6kl_vif *vif) 20868dafb70eSVasanthakumar Thiagarajan { 20872132c69cSVasanthakumar Thiagarajan aggr_module_destroy(vif->aggr_cntxt); 2088108438bcSVasanthakumar Thiagarajan 20892132c69cSVasanthakumar Thiagarajan vif->aggr_cntxt = NULL; 20908dafb70eSVasanthakumar Thiagarajan 209159c98449SVasanthakumar Thiagarajan if (test_bit(NETDEV_REGISTERED, &vif->flags)) { 2092108438bcSVasanthakumar Thiagarajan unregister_netdev(vif->ndev); 209359c98449SVasanthakumar Thiagarajan clear_bit(NETDEV_REGISTERED, &vif->flags); 20948dafb70eSVasanthakumar Thiagarajan } 20958dafb70eSVasanthakumar Thiagarajan 2096108438bcSVasanthakumar Thiagarajan free_netdev(vif->ndev); 20978dafb70eSVasanthakumar Thiagarajan } 20988dafb70eSVasanthakumar Thiagarajan 20998dafb70eSVasanthakumar Thiagarajan struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, 2100334234b5SVasanthakumar Thiagarajan enum nl80211_iftype type, u8 fw_vif_idx) 21018dafb70eSVasanthakumar Thiagarajan { 21028dafb70eSVasanthakumar Thiagarajan struct net_device *ndev; 2103108438bcSVasanthakumar Thiagarajan struct ath6kl_vif *vif; 21048dafb70eSVasanthakumar Thiagarajan 2105108438bcSVasanthakumar Thiagarajan ndev = alloc_netdev(sizeof(*vif), "wlan%d", ether_setup); 21068dafb70eSVasanthakumar Thiagarajan if (!ndev) 21078dafb70eSVasanthakumar Thiagarajan return NULL; 21088dafb70eSVasanthakumar Thiagarajan 2109108438bcSVasanthakumar Thiagarajan vif = netdev_priv(ndev); 2110108438bcSVasanthakumar Thiagarajan ndev->ieee80211_ptr = &vif->wdev; 2111108438bcSVasanthakumar Thiagarajan vif->wdev.wiphy = ar->wiphy; 2112108438bcSVasanthakumar Thiagarajan vif->ar = ar; 2113108438bcSVasanthakumar Thiagarajan ar->vif = vif; 2114108438bcSVasanthakumar Thiagarajan vif->ndev = ndev; 2115108438bcSVasanthakumar Thiagarajan SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy)); 2116108438bcSVasanthakumar Thiagarajan vif->wdev.netdev = ndev; 2117108438bcSVasanthakumar Thiagarajan vif->wdev.iftype = type; 2118334234b5SVasanthakumar Thiagarajan vif->fw_vif_idx = fw_vif_idx; 2119108438bcSVasanthakumar Thiagarajan ar->wdev = &vif->wdev; 21208dafb70eSVasanthakumar Thiagarajan 21218dafb70eSVasanthakumar Thiagarajan init_netdev(ndev); 21228dafb70eSVasanthakumar Thiagarajan 2123e29f25f5SVasanthakumar Thiagarajan ath6kl_init_control_info(vif); 21248dafb70eSVasanthakumar Thiagarajan 21258dafb70eSVasanthakumar Thiagarajan /* TODO: Pass interface specific pointer instead of ar */ 2126108438bcSVasanthakumar Thiagarajan if (ath6kl_init_if_data(vif)) 21278dafb70eSVasanthakumar Thiagarajan goto err; 21288dafb70eSVasanthakumar Thiagarajan 21298dafb70eSVasanthakumar Thiagarajan if (register_netdev(ndev)) 21308dafb70eSVasanthakumar Thiagarajan goto err; 21318dafb70eSVasanthakumar Thiagarajan 213214ee6f6bSVasanthakumar Thiagarajan vif->sme_state = SME_DISCONNECTED; 213359c98449SVasanthakumar Thiagarajan set_bit(WLAN_ENABLED, &vif->flags); 21348dafb70eSVasanthakumar Thiagarajan ar->wlan_pwr_state = WLAN_POWER_STATE_ON; 213559c98449SVasanthakumar Thiagarajan set_bit(NETDEV_REGISTERED, &vif->flags); 21368dafb70eSVasanthakumar Thiagarajan 21378dafb70eSVasanthakumar Thiagarajan return ndev; 21388dafb70eSVasanthakumar Thiagarajan 21398dafb70eSVasanthakumar Thiagarajan err: 2140108438bcSVasanthakumar Thiagarajan ath6kl_deinit_if_data(vif); 21418dafb70eSVasanthakumar Thiagarajan 2142bdcd8170SKalle Valo return NULL; 2143bdcd8170SKalle Valo } 2144bdcd8170SKalle Valo 21458dafb70eSVasanthakumar Thiagarajan void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar) 2146bdcd8170SKalle Valo { 2147be98e3a4SVasanthakumar Thiagarajan wiphy_unregister(ar->wiphy); 2148be98e3a4SVasanthakumar Thiagarajan wiphy_free(ar->wiphy); 2149bdcd8170SKalle Valo } 2150