M7350/external/compat-wireless/drivers/net/wireless/ath/ath6kl-3.5/cfg80211.c
2024-09-09 08:57:42 +00:00

7539 lines
185 KiB
C
Executable File

/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/moduleparam.h>
#ifndef CE_OLD_KERNEL_SUPPORT_2_6_23
#include <linux/gpio.h>
#endif
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/inetdevice.h>
#include "core.h"
#include "cfg80211.h"
#include "debug.h"
#include "hif-ops.h"
#include "testmode.h"
#include "cfg80211_btcoex.h"
#ifdef ATH6KL_DIAGNOSTIC
#include "diagnose.h"
#endif
#include "pm.h"
#include "rttm.h"
unsigned int debug_quirks = ATH6KL_MODULE_DEF_DEBUG_QUIRKS;
module_param(debug_quirks, uint, 0644);
#define RATETAB_ENT(_rate, _rateid, _flags) { \
.bitrate = (_rate), \
.flags = (_flags), \
.hw_value = (_rateid), \
}
#define CHAN2G(_channel, _freq, _flags) { \
.band = IEEE80211_BAND_2GHZ, \
.hw_value = (_channel), \
.center_freq = (_freq), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
#define CHAN5G(_channel, _flags) { \
.band = IEEE80211_BAND_5GHZ, \
.hw_value = (_channel), \
.center_freq = 5000 + (5 * (_channel)), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
static struct ieee80211_rate ath6kl_rates[] = {
RATETAB_ENT(10, 0x1, 0),
RATETAB_ENT(20, 0x2, 0),
RATETAB_ENT(55, 0x4, 0),
RATETAB_ENT(110, 0x8, 0),
RATETAB_ENT(60, 0x10, 0),
RATETAB_ENT(90, 0x20, 0),
RATETAB_ENT(120, 0x40, 0),
RATETAB_ENT(180, 0x80, 0),
RATETAB_ENT(240, 0x100, 0),
RATETAB_ENT(360, 0x200, 0),
RATETAB_ENT(480, 0x400, 0),
RATETAB_ENT(540, 0x800, 0),
};
#define ath6kl_a_rates (ath6kl_rates + 4)
#define ath6kl_a_rates_size 8
#define ath6kl_g_rates (ath6kl_rates + 0)
#define ath6kl_g_rates_size 12
#define ath6kl_b_rates_size 4
/* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */
static const u8 up_to_ac[] = {
WMM_AC_BE,
WMM_AC_BK,
WMM_AC_BK,
WMM_AC_BE,
WMM_AC_VI,
WMM_AC_VI,
WMM_AC_VO,
WMM_AC_VO,
};
static struct ieee80211_channel ath6kl_2ghz_channels[] = {
CHAN2G(1, 2412, 0),
CHAN2G(2, 2417, 0),
CHAN2G(3, 2422, 0),
CHAN2G(4, 2427, 0),
CHAN2G(5, 2432, 0),
CHAN2G(6, 2437, 0),
CHAN2G(7, 2442, 0),
CHAN2G(8, 2447, 0),
CHAN2G(9, 2452, 0),
CHAN2G(10, 2457, 0),
CHAN2G(11, 2462, 0),
CHAN2G(12, 2467, 0),
CHAN2G(13, 2472, 0),
CHAN2G(14, 2484, 0),
};
static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
CHAN5G(34, 0), CHAN5G(36, 0),
CHAN5G(38, 0), CHAN5G(40, 0),
CHAN5G(42, 0), CHAN5G(44, 0),
CHAN5G(46, 0), CHAN5G(48, 0),
CHAN5G(52, 0), CHAN5G(56, 0),
CHAN5G(60, 0), CHAN5G(64, 0),
CHAN5G(100, 0), CHAN5G(104, 0),
CHAN5G(108, 0), CHAN5G(112, 0),
CHAN5G(116, 0), CHAN5G(120, 0),
CHAN5G(124, 0), CHAN5G(128, 0),
CHAN5G(132, 0), CHAN5G(136, 0),
CHAN5G(140, 0), CHAN5G(144, 0),
CHAN5G(149, 0), CHAN5G(153, 0),
CHAN5G(157, 0), CHAN5G(161, 0),
CHAN5G(165, 0),
};
static struct ieee80211_supported_band ath6kl_band_2ghz = {
.n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
.channels = ath6kl_2ghz_channels,
.n_bitrates = ath6kl_g_rates_size,
.bitrates = ath6kl_g_rates,
.ht_cap = {
.ht_supported = true,
.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_TX_STBC |
0x100 | /* FIXME : One chain RX STBC */
IEEE80211_HT_CAP_SM_PS,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8,
.mcs = {
.rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
},
},
};
static struct ieee80211_supported_band ath6kl_band_5ghz = {
.n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
.channels = ath6kl_5ghz_a_channels,
.n_bitrates = ath6kl_a_rates_size,
.bitrates = ath6kl_a_rates,
.ht_cap = {
.ht_supported = true,
.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_TX_STBC |
0x100 | /* FIXME : One chain RX STBC */
IEEE80211_HT_CAP_SM_PS,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8,
.mcs = {
.rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
},
},
};
/* Max. 2 devices = 1STA + 1SOFTAP */
static const struct ieee80211_iface_limit ath6kl_limits_sta_ap[] = {
{
.max = 1,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_AP),
},
};
static struct ieee80211_iface_combination
ath6kl_iface_combinations_sta_ap[] = {
{
.num_different_channels = 1,
.max_interfaces = 2,
.limits = ath6kl_limits_sta_ap,
.n_limits = ARRAY_SIZE(ath6kl_limits_sta_ap),
},
};
/* Max. 2 devices = 1STA&P2P-DEVICE + 1P2P-GO|P2P-CLIENT */
static const struct ieee80211_iface_limit ath6kl_limits_p2p_concurrent2[] = {
{
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO),
},
};
static struct ieee80211_iface_combination
ath6kl_iface_combinations_p2p_concurrent2[] = {
{
.num_different_channels = 1,
.max_interfaces = 2,
.limits = ath6kl_limits_p2p_concurrent2,
.n_limits = ARRAY_SIZE(ath6kl_limits_p2p_concurrent2),
},
};
/* Max. 3 devices = 1STA + 1P2P-DEVICE + 1P2P-GO|P2P-CLIENT */
static const struct ieee80211_iface_limit ath6kl_limits_p2p_concurrent3[] = {
{
.max = 3,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO),
},
};
static struct ieee80211_iface_combination
ath6kl_iface_combinations_p2p_concurrent3[] = {
{
.num_different_channels = 1,
.max_interfaces = 3,
.limits = ath6kl_limits_p2p_concurrent3,
.n_limits = ARRAY_SIZE(ath6kl_limits_p2p_concurrent3),
},
};
/* Max. 4 devices = 1STA + 1P2P-DEVICE + 2P2P-GO|P2P-CLIENT */
static const struct ieee80211_iface_limit ath6kl_limits_p2p_concurrent4[] = {
{
.max = 4,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 2,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO),
},
};
static struct ieee80211_iface_combination
ath6kl_iface_combinations_p2p_concurrent4[] = {
{
.num_different_channels = 1,
.max_interfaces = 4,
.limits = ath6kl_limits_p2p_concurrent4,
.n_limits = ARRAY_SIZE(ath6kl_limits_p2p_concurrent4),
},
};
/* Max. 4 devices = 1STA + 1P2P-DEVICE + 1P2P-GO|P2P-CLIENT + 1SOFTAP */
static const struct ieee80211_iface_limit ath6kl_limits_p2p_concurrent4_1[] = {
{
.max = 3,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_AP),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO),
},
};
static struct ieee80211_iface_combination
ath6kl_iface_combinations_p2p_concurrent4_1[] = {
{
.num_different_channels = 1,
.max_interfaces = 4,
.limits = ath6kl_limits_p2p_concurrent4_1,
.n_limits = ARRAY_SIZE(ath6kl_limits_p2p_concurrent4_1),
},
};
#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
#ifdef PMF_SUPPORT
#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
static int ath6kl_set_akm_suites(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme);
static int ath6kl_set_rsn_cap(struct wiphy *wiphy, struct net_device *dev,
const u8 *ies, int ies_len);
#endif
static void ath6kl_eapol_shprotect_timer_handler(unsigned long ptr);
static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
enum nl80211_wpa_versions wpa_version)
{
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
if (!wpa_version) {
vif->auth_mode = NONE_AUTH;
} else if (wpa_version & NL80211_WPA_VERSION_2) {
vif->auth_mode = WPA2_AUTH;
} else if (wpa_version & NL80211_WPA_VERSION_1) {
vif->auth_mode = WPA_AUTH;
} else {
ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
return -ENOTSUPP;
}
return 0;
}
static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
enum nl80211_auth_type auth_type)
{
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
switch (auth_type) {
case NL80211_AUTHTYPE_OPEN_SYSTEM:
vif->dot11_auth_mode = OPEN_AUTH;
break;
case NL80211_AUTHTYPE_SHARED_KEY:
vif->dot11_auth_mode = SHARED_AUTH;
break;
case NL80211_AUTHTYPE_NETWORK_EAP:
vif->dot11_auth_mode = LEAP_AUTH;
break;
case NL80211_AUTHTYPE_AUTOMATIC:
vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
break;
default:
ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
return -ENOTSUPP;
}
return 0;
}
static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
{
u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
&vif->grp_crypto_len;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
__func__, cipher, ucast);
switch (cipher) {
case 0:
/* our own hack to use value 0 as no crypto used */
*ar_cipher = NONE_CRYPT;
*ar_cipher_len = 0;
break;
case WLAN_CIPHER_SUITE_WEP40:
*ar_cipher = WEP_CRYPT;
*ar_cipher_len = 5;
break;
case WLAN_CIPHER_SUITE_WEP104:
*ar_cipher = WEP_CRYPT;
*ar_cipher_len = 13;
break;
case WLAN_CIPHER_SUITE_TKIP:
*ar_cipher = TKIP_CRYPT;
*ar_cipher_len = 0;
break;
case WLAN_CIPHER_SUITE_CCMP:
*ar_cipher = AES_CRYPT;
*ar_cipher_len = 0;
break;
case WLAN_CIPHER_SUITE_SMS4:
*ar_cipher = WAPI_CRYPT;
*ar_cipher_len = 0;
break;
default:
ath6kl_err("cipher 0x%x not supported\n", cipher);
return -ENOTSUPP;
}
return 0;
}
static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
{
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
if (key_mgmt == WLAN_AKM_SUITE_PSK) {
if (vif->auth_mode == WPA_AUTH)
vif->auth_mode = WPA_PSK_AUTH;
else if (vif->auth_mode == WPA2_AUTH)
vif->auth_mode = WPA2_PSK_AUTH;
} else if (key_mgmt == 0x00409600) {
if (vif->auth_mode == WPA_AUTH)
vif->auth_mode = WPA_AUTH_CCKM;
else if (vif->auth_mode == WPA2_AUTH)
vif->auth_mode = WPA2_AUTH_CCKM;
#ifdef PMF_SUPPORT
} else if (key_mgmt == WLAN_AKM_SUITE_PSK_SHA256) {
vif->auth_mode = WPA2_PSK_SHA256_AUTH;
} else if ((key_mgmt != WLAN_AKM_SUITE_8021X) &&
(key_mgmt != WLAN_AKM_SUITE_8021X_SHA256)) {
#else
} else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
#endif
vif->auth_mode = NONE_AUTH;
}
}
#ifdef ATH6KL_SUPPORT_WIFI_KTK
static void ath6kl_install_ktk_ptk(struct ath6kl_vif *vif)
{
struct ath6kl *ar = vif->ar;
struct ath6kl_key *ptk = NULL;
enum wmi_sync_flag sync_flag = SYNC_BOTH_WMIFLAG;
int status;
char buff[32];
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s\n", __func__);
memset(buff, 0, 32);
memcpy(buff, ar->ktk_passphrase, 16);
/* set ptk at index 0 */
ptk = &vif->keys[0];
memset(ptk, 0, sizeof(struct ath6kl_key));
ptk->key_len = 16;
memcpy(ptk->key, ar->ktk_passphrase, ptk->key_len);
ptk->seq_len = 6;
memset(ptk->seq, 0, ptk->seq_len);
if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WMI_SYC))
sync_flag = NO_SYNC_WMIFLAG;
status = ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, 0,
KTK_CRYPT, GROUP_USAGE | TX_USAGE,
ptk->key_len,
ptk->seq,
ptk->seq_len,
ptk->key,
KEY_OP_INIT_VAL,
NULL,
sync_flag);
if (status)
ath6kl_err("%s: set ptk at index 0 failed\n", __func__);
}
#endif
static bool __ath6kl_cfg80211_ready(struct ath6kl *ar)
{
if (!test_bit(WMI_READY, &ar->flag)) {
ath6kl_err("wmi is not ready\n");
return false;
}
return true;
}
static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
{
if (!__ath6kl_cfg80211_ready(vif->ar))
return false;
#if defined(USB_AUTO_SUSPEND)
if ((vif->ar->state == ATH6KL_STATE_WOW) ||
(vif->ar->state == ATH6KL_STATE_DEEPSLEEP) ||
(vif->ar->state == ATH6KL_STATE_PRE_SUSPEND)) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG |
ATH6KL_DBG_EXT_AUTOPM,
"autopm ignore wlan disabled in autopm mode!\n");
} else {
if (!test_bit(WLAN_ENABLED, &vif->flags))
return false;
}
#else
if (!test_bit(WLAN_ENABLED, &vif->flags))
return false;
#endif
return true;
}
static bool ath6kl_is_wpa_ie(const u8 *pos)
{
return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
pos[2] == 0x00 && pos[3] == 0x50 &&
pos[4] == 0xf2 && pos[5] == 0x01;
}
static bool ath6kl_is_rsn_ie(const u8 *pos)
{
return pos[0] == WLAN_EID_RSN;
}
static bool ath6kl_is_wps_ie(const u8 *pos)
{
return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
pos[1] >= 4 &&
pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
pos[5] == 0x04);
}
static bool ath6kl_is_wmm_ie(const u8 *pos)
{
return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
pos[1] >= 4 &&
pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
pos[5] == 0x02);
}
static bool ath6kl_is_exteneded_cap_ie(const u8 *pos)
{
return (pos[0] == WLAN_EID_EXT_CAPABILITY &&
pos[1] == 4);
}
static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
size_t ies_len)
{
struct ath6kl *ar = vif->ar;
const u8 *pos;
u8 *buf = NULL;
size_t len = 0;
int ret = 0;
/*
* Clear previously set flag
*/
vif->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
/*
* Filter out RSN/WPA or P2P/WFD IE(s)
*/
if (ies && ies_len) {
buf = kmalloc(ies_len, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
pos = ies;
while (pos + 1 < ies + ies_len) {
if (pos + 2 + pos[1] > ies + ies_len)
break;
/* Strip the EXTENDED CAPABILITY IE */
if (ath6kl_is_exteneded_cap_ie(pos)) {
pos += 2 + pos[1];
continue;
}
if (!(ath6kl_is_wpa_ie(pos) ||
ath6kl_is_rsn_ie(pos))) {
if ((ath6kl_is_p2p_ie(pos) ||
ath6kl_is_wfd_ie(pos)) &&
!ath6kl_p2p_ie_append(vif,
P2P_IE_IN_ASSOC_REQ))
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"Remove Asso's P2P IE\n");
else {
memcpy(buf + len, pos, 2 + pos[1]);
len += 2 + pos[1];
}
}
if (ath6kl_is_wps_ie(pos))
vif->connect_ctrl_flags |= CONNECT_WPS_FLAG;
pos += 2 + pos[1];
}
}
ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_ASSOC_REQ, buf, len);
kfree(buf);
return ret;
}
static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
{
switch (type) {
case NL80211_IFTYPE_STATION:
*nw_type = INFRA_NETWORK;
break;
case NL80211_IFTYPE_ADHOC:
*nw_type = ADHOC_NETWORK;
break;
case NL80211_IFTYPE_AP:
*nw_type = AP_NETWORK;
break;
case NL80211_IFTYPE_P2P_CLIENT:
*nw_type = INFRA_NETWORK;
break;
case NL80211_IFTYPE_P2P_GO:
*nw_type = AP_NETWORK;
break;
default:
ath6kl_err("invalid interface type %u\n", type);
return -ENOTSUPP;
}
return 0;
}
static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
u8 *if_idx, u8 *nw_type)
{
int i;
if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
return false;
if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
ar->num_vif))
return false;
/* we use firmware index in 1, 4, 3, 2 order to align with
* firmware design in the dedicate interface case
*/
if (ar->p2p_concurrent && ar->p2p_dedicate) {
if ((ar->p2p_concurrent_ap) &&
(type == NL80211_IFTYPE_AP)) {
for (i = 0; i < ar->max_norm_iface; i++) {
if ((ar->avail_idx_map >> i) & BIT(0)) {
*if_idx = i;
return true;
}
}
return false;
}
if (type == NL80211_IFTYPE_STATION ||
type == NL80211_IFTYPE_AP ||
type == NL80211_IFTYPE_ADHOC) {
for (i = (ar->vif_max - 1); i > 0; i--) {
if ((ar->avail_idx_map >> i) & BIT(0)) {
*if_idx = i;
return true;
}
}
}
if (type == NL80211_IFTYPE_P2P_CLIENT ||
type == NL80211_IFTYPE_P2P_GO) {
for (i = (ar->vif_max - 1); i >
(ar->max_norm_iface - 1); i--) {
if ((ar->avail_idx_map >> i) & BIT(0)) {
*if_idx = i;
return true;
}
}
}
} else {
if (type == NL80211_IFTYPE_STATION ||
type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
for (i = 0; i < ar->vif_max; i++) {
if ((ar->avail_idx_map >> i) & BIT(0)) {
*if_idx = i;
return true;
}
}
}
if (type == NL80211_IFTYPE_P2P_CLIENT ||
type == NL80211_IFTYPE_P2P_GO) {
for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
if ((ar->avail_idx_map >> i) & BIT(0)) {
*if_idx = i;
return true;
}
}
}
}
return false;
}
/* avoid using roam in p2p, ap, adhoc mode, and current USB devices */
/* use in connect_cmd, disconnect event, ap_start */
/* only support lrssi roam at this time*/
void ath6kl_judge_roam_parameter(
struct ath6kl_vif *vif,
bool call_on_disconnect)
{
struct ath6kl *ar = vif->ar;
u8 connected_count = 0;
bool lrssi_scan_enable = true;
struct ath6kl_vif *vif_temp;
if (ar->roam_mode == ATH6KL_MODULEROAM_DISABLE ||
ar->roam_mode == ATH6KL_MODULEROAM_DISABLE_LRSSI_SCAN) {
lrssi_scan_enable = false;
goto DONE;
} else if (ar->roam_mode == ATH6KL_MODULEROAM_NO_LRSSI_SCAN_AT_MULTI) {
lrssi_scan_enable = true;
list_for_each_entry(vif_temp, &ar->vif_list, list) {
if (vif->fw_vif_idx == vif_temp->fw_vif_idx)
continue;
if (test_bit(CONNECTED, &vif_temp->flags))
connected_count++;
if (call_on_disconnect) {
if ((connected_count == 1) &&
(vif_temp->wdev.iftype ==
NL80211_IFTYPE_STATION)) {
lrssi_scan_enable = true;
} else {
lrssi_scan_enable = false;
}
} else if (connected_count) {
lrssi_scan_enable = false;
goto DONE;
}
}
} else { /* ATH6KL_MODULEROAM_ENABLE_ALL */
lrssi_scan_enable = true;
}
/* disable roam when it is in any other mode */
if (!call_on_disconnect &&
vif->wdev.iftype != NL80211_IFTYPE_STATION)
lrssi_scan_enable = false;
DONE:
if (lrssi_scan_enable) {
ath6kl_wmi_set_roam_ctrl_cmd(ar->wmi,
0,
ar->low_rssi_roam_params.lrssi_scan_period,
ar->low_rssi_roam_params.lrssi_scan_threshold,
ar->low_rssi_roam_params.lrssi_roam_threshold,
ar->low_rssi_roam_params.roam_rssi_floor);
} else {
ath6kl_wmi_set_roam_ctrl_cmd(ar->wmi,
0, 0xFFFF, 0, 0, 100);
}
}
static void switch_tid_rx_timeout(
struct ath6kl_vif *vif,
bool enlarge_aggr_rx_timer)
{
u8 i, j = 0;
struct aggr_conn_info *aggr_conn;
struct rxtid *rxtid;
for (i = 0; i < AP_MAX_NUM_STA; i++) {
aggr_conn = vif->sta_list[i].aggr_conn_cntxt;
for (j = 0; j < NUM_OF_TIDS; j++) {
rxtid = AGGR_GET_RXTID(aggr_conn, j);
spin_lock_bh(&rxtid->lock);
switch (up_to_ac[j]) {
case WMM_AC_BK:
case WMM_AC_BE:
case WMM_AC_VI:
if (enlarge_aggr_rx_timer)
aggr_conn->tid_timeout_setting[j] =
MCC_AGGR_RX_TIMEOUT;
else
aggr_conn->tid_timeout_setting[j] =
AGGR_RX_TIMEOUT;
break;
case WMM_AC_VO:
if (enlarge_aggr_rx_timer)
aggr_conn->tid_timeout_setting[j] =
MCC_AGGR_RX_TIMEOUT_VO;
else
aggr_conn->tid_timeout_setting[j] =
AGGR_RX_TIMEOUT_VO;
break;
}
spin_unlock_bh(&rxtid->lock);
}
}
}
#ifdef USB_AUTO_SUSPEND
void ath6kl_check_autopm_onoff(struct ath6kl *ar, bool call_on_disconnect)
{
struct ath6kl_vif *vif_temp;
/*
* Switch Auto PM On/Off
* In folloing case, we will turn off AUTOPM
* 1. p2p0-P2P-xxx is connected
* 2. any nw_type is AP network
*/
int vif_cnt = 0;
int autopm_turn_on = 1;
list_for_each_entry(vif_temp, &ar->vif_list, list) {
vif_cnt++;
if (test_bit(CONNECTED, &vif_temp->flags)) {
if (vif_cnt > 1) {
autopm_turn_on = 0;
break;
}
if (vif_temp->nw_type == AP_NETWORK) {
autopm_turn_on = 0;
break;
}
}
}
ath6kl_dbg(ATH6KL_DBG_EXT_AUTOPM,
"autopm next mode(%d) %s --> %s, vif_cnt %d cur-delay_cnt/time %d/%d\n",
call_on_disconnect,
(ar->autopm_turn_on ? "ON" : "OFF"),
(autopm_turn_on ? "ON" : "OFF"),
vif_cnt,
ar->autopm_defer_delay_change_cnt,
ar->autopm_curr_delay_time);
if (autopm_turn_on) {
ath6kl_hif_auto_pm_turnon(ar);
if (call_on_disconnect) {
ar->autopm_defer_delay_change_cnt =
USB_SUSPEND_DEFER_DELAY_CHANGE_CNT;
ath6kl_hif_auto_pm_set_delay(ar,
USB_SUSPEND_DELAY_REENABLE);
}
} else {
ath6kl_hif_auto_pm_turnoff(ar);
ar->autopm_defer_delay_change_cnt = 0;
ath6kl_hif_auto_pm_set_delay(ar,
USB_SUSPEND_DELAY_MAX);
}
ar->autopm_turn_on = autopm_turn_on;
}
#endif
/* This function is used by sta/p2pclient/go interface to switch paramters
* needed for MCC/connection specific parameters
*/
void ath6kl_switch_parameter_based_on_connection(
struct ath6kl_vif *vif,
bool call_on_disconnect)
{
struct ath6kl *ar = vif->ar;
u8 connected_count = 0;
struct ath6kl_vif *vif_temp;
bool mcc = false;
u16 pre_vifch = 0;
list_for_each_entry(vif_temp, &ar->vif_list, list) {
if (test_bit(CONNECTED, &vif_temp->flags)) {
connected_count++;
if (pre_vifch == 0) {
pre_vifch = vif_temp->bss_ch;
} else if (vif_temp->bss_ch != 0 &&
pre_vifch != vif_temp->bss_ch) {
mcc = true;
}
}
}
if (connected_count) {
list_for_each_entry(vif_temp, &ar->vif_list, list) {
if (test_bit(CONNECTED, &vif_temp->flags)) {
if (call_on_disconnect &&
vif->fw_vif_idx == vif_temp->fw_vif_idx)
continue;
if (vif->wdev.iftype ==
NL80211_IFTYPE_STATION && !mcc)
switch_tid_rx_timeout(vif_temp, false);
else
switch_tid_rx_timeout(vif_temp, true);
}
}
}
/*
* Delete rekey protection timer if
* 1. all vifs disconnected
* 2. the timer was only fireed by this vif.
*/
if ((call_on_disconnect) &&
test_bit(EAPOL_HANDSHAKE_PROTECT, &ar->flag)) {
if ((connected_count == 0) ||
(ar->eapol_shprotect_vif == (1 << vif->fw_vif_idx))) {
clear_bit(EAPOL_HANDSHAKE_PROTECT, &ar->flag);
del_timer(&ar->eapol_shprotect_timer);
}
}
/*
* Switch scan parameter
* Currently, when there are more than one vif connected,
* revise the passive dwell time as the default
* ATH6KL_SCAN_PAS_DEWELL_TIME
*/
if (connected_count) {
list_for_each_entry(vif_temp, &ar->vif_list, list) {
vif_temp->sc_params.pas_chdwell_time =
ATH6KL_SCAN_PAS_DEWELL_TIME;
}
} else {
list_for_each_entry(vif_temp, &ar->vif_list, list) {
memcpy(&vif->sc_params,
&vif->sc_params_default,
sizeof(struct wmi_scan_params_cmd));
}
}
if (mcc)
set_bit(MCC_ENABLED, &ar->flag);
else
clear_bit(MCC_ENABLED, &ar->flag);
if (ar->conf_flags & ATH6KL_CONF_ENABLE_FLOWCTRL) {
if (ar->conf_flags & ATH6KL_CONF_DISABLE_SKIP_FLOWCTRL) {
clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag);
} else {
if (test_bit(MCC_ENABLED, &ar->flag))
clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag);
else
set_bit(SKIP_FLOWCTRL_EVENT, &ar->flag);
}
} else {
clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag);
}
if (mcc) {
list_for_each_entry(vif_temp, &ar->vif_list, list) {
if (test_bit(CONNECTED, &vif_temp->flags)) {
if (call_on_disconnect &&
vif->fw_vif_idx == vif_temp->fw_vif_idx)
continue;
if (vif_temp->nw_type == AP_NETWORK) {
ath6kl_ap_keepalive_config(vif_temp,
ATH6KL_AP_KA_INTERVAL_DEFAULT,
ATH6KL_AP_KA_RECLAIM_CYCLE_MCC);
}
}
}
} else {
list_for_each_entry(vif_temp, &ar->vif_list, list) {
if (test_bit(CONNECTED, &vif_temp->flags)) {
if (call_on_disconnect &&
vif->fw_vif_idx == vif_temp->fw_vif_idx)
continue;
if (vif_temp->nw_type == AP_NETWORK) {
ath6kl_ap_keepalive_config(vif_temp,
ATH6KL_AP_KA_INTERVAL_DEFAULT,
ATH6KL_AP_KA_RECLAIM_CYCLE_SCC);
}
}
}
}
/* Update HIF queue policy */
ath6kl_hif_pipe_set_max_queue_number(ar, mcc);
/* Reconfigurate the PS mode case by case. */
ath6kl_p2p_reconfig_ps(ar, mcc, call_on_disconnect);
#ifdef USB_AUTO_SUSPEND
ath6kl_check_autopm_onoff(ar, call_on_disconnect);
#endif /* USB_AUTO_SUSPEND */
}
static inline void _change_sta_scan_plan(struct ath6kl_vif *vif)
{
if (vif->ssid_len) {
int i, chan_num;
u16 chan_list[64]; /* WMI_MAX_CHANNELS */
memset(chan_list, 0, sizeof(u16) * 64);
chan_num = ath6kl_bss_post_proc_candidate_bss(vif,
vif->ssid,
vif->ssid_len,
chan_list);
if (chan_num) {
vif->scan_plan.type = ATH6KL_SCAN_PLAN_HOST_ORDER;
/* For STA, prefer to use 5G APs. */
if (chan_num <= 21) { /* (64 / 3) */
vif->scan_plan.numChan = chan_num * 3;
for (i = 0; i < chan_num; i++)
vif->scan_plan.chanList[i * 3] =
vif->scan_plan.chanList[i * 3 + 1] =
vif->scan_plan.chanList[i * 3 + 2] =
chan_list[chan_num - 1 - i];
} else {
vif->scan_plan.numChan = chan_num;
for (i = 0; i < chan_num; i++)
vif->scan_plan.chanList[i] =
chan_list[chan_num - 1 - i];
}
/*
* If only one channel need to try and 1s' disconnection
* timeout is enough.
*/
if (chan_num == 1)
ath6kl_wmi_disctimeout_cmd(vif->ar->wmi,
vif->fw_vif_idx,
1);
} else
vif->scan_plan.type = ATH6KL_SCAN_PLAN_REVERSE_ORDER;
} else
vif->scan_plan.type = ATH6KL_SCAN_PLAN_REVERSE_ORDER;
return;
}
static inline void _change_p2p_scan_plan(struct ath6kl_vif *vif)
{
#define _MAX_STAY_TIME (200) /* 2 Beacon Time */
struct ath6kl *ar = vif->ar;
struct ath6kl_vif *tmp;
bool change_scan_plan = true;
int i;
/*
* Only fine tune for SCC case to stay the target channel
* at least 2 beacon time for connection try rather than report
* disconnect immediately.
*/
list_for_each_entry(tmp, &ar->vif_list, list) {
if ((tmp != vif) &&
test_bit(CONNECTED, &tmp->flags) &&
(tmp->bss_ch != vif->ch_hint)) {
change_scan_plan = false;
break;
}
}
if (change_scan_plan) {
vif->scan_plan.type = ATH6KL_SCAN_PLAN_HOST_ORDER;
vif->scan_plan.numChan = _MAX_STAY_TIME /
vif->sc_params.maxact_chdwell_time;
for (i = 0; i < vif->scan_plan.numChan; i++)
vif->scan_plan.chanList[i] = vif->ch_hint;
}
return;
#undef _MAX_STAY_TIME
}
void ath6kl_change_scan_plan(struct ath6kl_vif *vif, bool reset)
{
struct ath6kl *ar = vif->ar;
/* Default */
vif->scan_plan.type = ATH6KL_SCAN_PLAN_REVERSE_ORDER;
vif->scan_plan.numChan = 0;
if (!reset) {
/*
* Currently, only fine tune scan-plan when turn-on target
* roaming.
*/
if (!(ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM))
goto done;
if (vif->wdev.iftype == NL80211_IFTYPE_STATION)
_change_sta_scan_plan(vif);
else if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) &&
(vif->ssid_len)) {
struct cfg80211_bss *bss;
/*
* Firmware roaming is global setting in cfg80211 but
* not in target and P2P-Client actually no need
* roaming and therefore pass the channel & bssid hint
* to target.
*/
bss = ath6kl_bss_get(vif->ar,
NULL,
NULL,
vif->ssid,
vif->ssid_len,
WLAN_CAPABILITY_ESS,
WLAN_CAPABILITY_ESS);
if (bss && bss->channel) {
#ifdef CFG80211_SAFE_BSS_INFO_ACCESS
rcu_read_lock();
#endif
vif->ch_hint = bss->channel->center_freq;
memcpy(vif->req_bssid, bss->bssid, ETH_ALEN);
#ifdef CFG80211_SAFE_BSS_INFO_ACCESS
rcu_read_unlock();
#endif
}
if (bss)
ath6kl_bss_put(vif->ar, bss);
/* Change scan-plan to host order if need. */
_change_p2p_scan_plan(vif);
}
}
done:
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG | ATH6KL_DBG_EXT_SCAN,
"change scan plan, vif %d reset %d type %d numChan %d\n",
vif->fw_vif_idx,
reset,
vif->scan_plan.type,
vif->scan_plan.numChan);
/* Update to the target */
ath6kl_wmi_set_scan_chan_plan(ar->wmi,
vif->fw_vif_idx,
vif->scan_plan.type,
vif->scan_plan.numChan,
vif->scan_plan.chanList);
return;
}
/* WAR some framework implementation,
* Called after auth/prwise,dot11 get set.
*/
static void ath6kl_wep_auth_auto(struct ath6kl_vif *vif)
{
if ((vif->prwise_crypto_len != 0) &&
(vif->auth_mode == NONE_AUTH) &&
(vif->prwise_crypto == WEP_CRYPT) &&
(vif->dot11_auth_mode == OPEN_AUTH)) {
vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s,"
"dot11_auth_mode change %d\n",
__func__, vif->dot11_auth_mode);
}
}
static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
int status, left;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
ath6kl_err("%s destroy in progress %lu\n", __func__ ,ar->flag);
return -EBUSY;
}
if (test_bit(SKIP_SCAN, &ar->flag) &&
((sme->channel && sme->channel->center_freq == 0) ||
(sme->bssid && is_zero_ether_addr(sme->bssid)))) {
ath6kl_err("SkipScan: channel or bssid invalid\n");
return -EINVAL;
}
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
#ifdef USB_AUTO_SUSPEND
if (ar->autopm_turn_on) {
ar->autopm_defer_delay_change_cnt =
USB_SUSPEND_DEFER_DELAY_FOR_P2P;
ath6kl_hif_auto_pm_set_delay(ar, USB_SUSPEND_DELAY_MAX);
}
#endif
vif->sme_state = SME_CONNECTING;
if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
/*
* sleep until the command queue drains
*/
left = wait_event_interruptible_timeout(ar->event_wq,
ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
WMI_TIMEOUT);
if (left == 0) {
ath6kl_warn("clear wmi ctrl data timeout connect\n");
up(&ar->sem);
return -ETIMEDOUT;
} else if (signal_pending(current)) {
ath6kl_err("cmd queue drain timeout\n");
up(&ar->sem);
return -EINTR;
}
}
/* Diable background scan */
vif->sc_params.bg_period = 0xFFFF;
ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
vif->sc_params.fg_start_period,
vif->sc_params.fg_end_period,
vif->sc_params.bg_period,
vif->sc_params.minact_chdwell_time,
vif->sc_params.maxact_chdwell_time,
vif->sc_params.pas_chdwell_time,
vif->sc_params.short_scan_ratio,
vif->sc_params.scan_ctrl_flags,
vif->sc_params.max_dfsch_act_time,
vif->sc_params.maxact_scan_per_ssid);
ath6kl_judge_roam_parameter(vif, false);
if (vif->wdev.iftype == NL80211_IFTYPE_STATION)
ath6kl_wmi_set_green_tx_params(ar->wmi, &ar->green_tx_params);
if (ath6kl_wmi_set_rate_ctrl_cmd(ar->wmi,
vif->fw_vif_idx, RATECTRL_MODE_PERONLY))
ath6kl_err("set rate_ctrl failed\n");
if (sme->ie && (sme->ie_len > 0)) {
status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
if (status) {
up(&ar->sem);
return status;
}
} else
vif->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
#ifdef PMF_SUPPORT
/* Update AKM suites. */
if (ath6kl_set_akm_suites(wiphy, dev, sme)) {
up(&ar->sem);
return -EIO;
}
/* Update RSN Capabilities. */
if (ath6kl_set_rsn_cap(wiphy, dev, sme->ie, sme->ie_len)) {
up(&ar->sem);
return -EIO;
}
#endif
vif->connect_ctrl_flags |= CONNECT_IGNORE_WPAx_GROUP_CIPHER;
if (test_bit(CONNECTED, &vif->flags) &&
vif->ssid_len == sme->ssid_len &&
!memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
vif->reconnect_flag = true;
status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
vif->req_bssid,
vif->ch_hint);
up(&ar->sem);
if (status) {
ath6kl_err("wmi_reconnect_cmd failed\n");
return -EIO;
}
return 0;
} else if (vif->ssid_len == sme->ssid_len &&
!memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
ath6kl_disconnect(vif);
}
memset(vif->ssid, 0, sizeof(vif->ssid));
vif->ssid_len = sme->ssid_len;
memcpy(vif->ssid, sme->ssid, sme->ssid_len);
if (sme->channel)
vif->ch_hint = sme->channel->center_freq;
memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
status = ath6kl_set_auth_type(vif, sme->auth_type);
if (status) {
up(&ar->sem);
return status;
}
if (sme->crypto.n_ciphers_pairwise)
ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
else
ath6kl_set_cipher(vif, 0, true);
ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
if (sme->crypto.n_akm_suites)
ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
/* WAR framework for wep auth */
ath6kl_wep_auth_auto(vif);
if ((sme->key_len) &&
(vif->auth_mode == NONE_AUTH) &&
(vif->prwise_crypto == WEP_CRYPT)) {
struct ath6kl_key *key = NULL;
if (sme->key_idx > WMI_MAX_KEY_INDEX) {
ath6kl_err("key index %d out of bounds\n",
sme->key_idx);
up(&ar->sem);
return -ENOENT;
}
key = &vif->keys[sme->key_idx];
key->key_len = sme->key_len;
memcpy(key->key, sme->key, key->key_len);
key->cipher = vif->prwise_crypto;
vif->def_txkey_index = sme->key_idx;
ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
vif->prwise_crypto,
GROUP_USAGE | TX_USAGE,
key->key_len,
NULL, 0,
key->key, KEY_OP_INIT_VAL, NULL,
NO_SYNC_WMIFLAG);
}
if (!vif->usr_bss_filter) {
clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
ALL_BSS_FILTER, 0) != 0) {
ath6kl_err("couldn't set bss filtering\n");
up(&ar->sem);
return -EIO;
}
}
vif->nw_type = vif->next_mode;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: connect called with authmode %d dot11 auth %d"
" PW crypto %d PW crypto len %d GRP crypto %d"
" GRP crypto len %d channel hint %u\n",
__func__,
vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
vif->prwise_crypto_len, vif->grp_crypto,
vif->grp_crypto_len, vif->ch_hint);
vif->reconnect_flag = 0;
/*
* Set disconnection timeout to 0 to cause firmware report
* disconnection event immediately rather than waiting defualt timer
* timeout (10sec) or supplicant trigger connection timeout (10sec).
*/
ath6kl_wmi_disctimeout_cmd(ar->wmi, vif->fw_vif_idx, 0);
ath6kl_change_scan_plan(vif, false);
status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
vif->dot11_auth_mode, vif->auth_mode,
vif->prwise_crypto,
vif->prwise_crypto_len,
vif->grp_crypto, vif->grp_crypto_len,
vif->ssid_len, vif->ssid,
vif->req_bssid, vif->ch_hint,
vif->connect_ctrl_flags);
up(&ar->sem);
if (status == -EINVAL) {
memset(vif->ssid, 0, sizeof(vif->ssid));
vif->ssid_len = 0;
ath6kl_err("invalid request\n");
return -ENOENT;
} else if (status) {
ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
return -EIO;
}
if ((!(vif->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
((vif->auth_mode == WPA_PSK_AUTH)
|| (vif->auth_mode == WPA2_PSK_AUTH))) {
mod_timer(&vif->disconnect_timer,
jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
}
vif->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
set_bit(CONNECT_PEND, &vif->flags);
return 0;
}
static struct cfg80211_bss *ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
enum network_type nw_type,
const u8 *bssid,
struct ieee80211_channel *chan,
const u8 *beacon_ie, size_t beacon_ie_len)
{
struct ath6kl *ar = vif->ar;
struct cfg80211_bss *bss;
u16 cap_mask, cap_val;
u8 *ie;
if (nw_type & ADHOC_NETWORK) {
cap_mask = WLAN_CAPABILITY_IBSS;
cap_val = WLAN_CAPABILITY_IBSS;
} else {
cap_mask = WLAN_CAPABILITY_ESS;
cap_val = WLAN_CAPABILITY_ESS;
}
bss = ath6kl_bss_get(ar, chan, bssid,
vif->ssid, vif->ssid_len,
cap_mask, cap_val);
if (bss == NULL) {
/*
* Since cfg80211 may not yet know about the BSS,
* generate a partial entry until the first BSS info
* event becomes available.
*
* Prepend SSID element since it is not included in the Beacon
* IEs from the target.
*/
ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
if (ie == NULL)
return NULL;
ie[0] = WLAN_EID_SSID;
ie[1] = vif->ssid_len;
memcpy(ie + 2, vif->ssid, vif->ssid_len);
memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
bss = cfg80211_inform_bss(ar->wiphy, chan,
bssid, 0, cap_val, 100,
ie, 2 + vif->ssid_len + beacon_ie_len,
0, GFP_KERNEL);
if (bss)
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
"cfg80211\n", bssid);
kfree(ie);
} else
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
return bss;
}
static bool ath6kl_roamed_indicate(struct ath6kl_vif *vif,
u8 *bssid, bool reset_hk_prot)
{
struct ath6kl *ar = vif->ar;
if ((vif->sme_state == SME_CONNECTED) &&
(ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
((!test_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags)) ||
(reset_hk_prot == true))) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %pM roam, vif->flags 0x%lu,"
"reset_hk_prot %x\n",
__func__, bssid, vif->flags, reset_hk_prot);
return true;
}
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %pM notroam\n", __func__, bssid);
return false;
}
static bool ath6kl_handshake_protect(struct ath6kl_vif *vif, u8 *bssid)
{
bool reset_handshake_pro = false;
if ((vif->auth_mode > NONE_AUTH) &&
(vif->prwise_crypto > WEP_CRYPT) &&
(vif->nw_type == INFRA_NETWORK)) {
if ((memcmp(bssid, vif->bssid, ETH_ALEN) != 0) ||
(!test_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags))) {
spin_lock_bh(&vif->if_lock);
set_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags);
if (vif->pend_skb)
ath6kl_flush_pend_skb(vif);
set_bit(FIRST_EAPOL_PENDSENT, &vif->flags);
mod_timer(&vif->shprotect_timer,
jiffies + ATH6KL_HANDSHAKE_PROC_TIMEOUT);
spin_unlock_bh(&vif->if_lock);
reset_handshake_pro = true;
} else {
reset_handshake_pro = false;
}
} else {
spin_lock_bh(&vif->if_lock);
clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags);
if (vif->pend_skb)
ath6kl_flush_pend_skb(vif);
del_timer(&vif->shprotect_timer);
spin_unlock_bh(&vif->if_lock);
reset_handshake_pro = false;
}
return reset_handshake_pro;
}
void ath6kl_cfg80211_connect_result(struct ath6kl_vif *vif,
const u8 *bssid,
const u8 *req_ie,
size_t req_ie_len,
const u8 *resp_ie,
size_t resp_ie_len,
u16 status,
gfp_t gfp)
{
bool need_pending;
ath6kl_change_scan_plan(vif, true);
need_pending = ath6kl_p2p_pending_connect_event(vif,
bssid,
req_ie,
req_ie_len,
resp_ie,
resp_ie_len,
status,
gfp);
if (!need_pending)
cfg80211_connect_result(vif->ndev,
bssid,
req_ie,
req_ie_len,
resp_ie,
resp_ie_len,
status,
gfp);
return;
}
void ath6kl_cfg80211_disconnected(struct ath6kl_vif *vif,
u16 reason,
u8 *ie,
size_t ie_len,
gfp_t gfp)
{
ath6kl_change_scan_plan(vif, true);
ath6kl_p2p_pending_disconnect_event(vif, reason, ie, ie_len, gfp);
cfg80211_disconnected(vif->ndev,
reason,
ie,
ie_len,
gfp);
return;
}
void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
u8 *bssid, u16 listen_intvl,
u16 beacon_intvl,
enum network_type nw_type,
u8 beacon_ie_len, u8 assoc_req_len,
u8 assoc_resp_len, u8 *assoc_info)
{
struct ieee80211_channel *chan;
struct ath6kl *ar = vif->ar;
struct cfg80211_bss *bss;
bool reset_hk_prot = false;
/* capinfo + listen interval */
u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
/* capinfo + status code + associd */
u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
assoc_resp_ie_offset;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: bssid %pM\n", __func__, bssid);
assoc_req_len -= assoc_req_ie_offset;
assoc_resp_len -= assoc_resp_ie_offset;
/*
* Store Beacon interval here; DTIM period will be available only once
* a Beacon frame from the AP is seen.
*/
vif->assoc_bss_beacon_int = beacon_intvl;
clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
if (nw_type & ADHOC_NETWORK) {
if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in ibss mode\n", __func__);
return;
}
#ifdef ATH6KL_SUPPORT_WIFI_KTK
if (ar->ktk_active)
ath6kl_install_ktk_ptk(vif);
#endif
}
if (nw_type & INFRA_NETWORK) {
if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in station mode\n", __func__);
return;
}
}
chan = ieee80211_get_channel(ar->wiphy, (int) channel);
if (chan == NULL) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k could not get channel information\n",
__func__);
return;
}
bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
assoc_info, beacon_ie_len);
if (!bss) {
ath6kl_err("could not add cfg80211 bss entry\n");
return;
}
if (nw_type & ADHOC_NETWORK) {
#ifdef ATH6KL_DIAGNOSTIC
wifi_diag_mac_fsm_event(vif,
(enum wifi_diag_mac_fsm_t)WIFI_DIAG_MAC_FSM_CONNECTED,
vif->diag.connect_seq_num);
#endif
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
nw_type & ADHOC_CREATOR ? "creator" : "joiner");
cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
ath6kl_bss_put(vif->ar, bss);
return;
}
if (nw_type & INFRA_NETWORK)
vif->phymode = ((u16)nw_type >> 8) & 0xff;
reset_hk_prot = ath6kl_handshake_protect(vif, bssid);
if (vif->sme_state == SME_CONNECTING) {
/* inform connect result to cfg80211 */
vif->sme_state = SME_CONNECTED;
#ifdef ATH6KL_DIAGNOSTIC
wifi_diag_mac_fsm_event(vif,
(enum wifi_diag_mac_fsm_t)WIFI_DIAG_MAC_FSM_CONNECTED,
vif->diag.connect_seq_num);
#endif
ath6kl_bss_put(vif->ar, bss);
ath6kl_cfg80211_connect_result(vif, bssid,
assoc_req_ie, assoc_req_len,
assoc_resp_ie, assoc_resp_len,
WLAN_STATUS_SUCCESS, GFP_KERNEL);
} else if (ath6kl_roamed_indicate(vif, bssid, reset_hk_prot) == true) {
cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
}
#ifdef USB_AUTO_SUSPEND
if (ar->autopm_turn_on) {
ar->autopm_defer_delay_change_cnt =
USB_SUSPEND_DEFER_DELAY_CHANGE_CNT;
ath6kl_hif_auto_pm_set_delay(ar, USB_SUSPEND_DELAY_CONNECTED);
}
#endif
}
static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
struct net_device *dev, u16 reason_code)
{
struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
int ret;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG | ATH6KL_DBG_EXT_INFO1, "%s: reason=%u\n",
__func__, reason_code);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
ath6kl_err("%s destroy in progress %lu\n", __func__, ar->flag);
return -EBUSY;
}
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
vif->reconnect_flag = 0;
ret = ath6kl_disconnect(vif);
memset(vif->ssid, 0, sizeof(vif->ssid));
vif->ssid_len = 0;
if (!test_bit(SKIP_SCAN, &ar->flag))
memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
up(&ar->sem);
vif->sme_state = SME_DISCONNECTED;
#ifdef USB_AUTO_SUSPEND
if (ar->autopm_turn_on) {
ar->autopm_defer_delay_change_cnt = 0;
ath6kl_hif_auto_pm_set_delay(ar, USB_SUSPEND_DELAY_MAX);
}
#endif
/*
* To avoid race condition between driver and supplicant, waiting
* until received disconnect event.
*/
if ((!ret) &&
(vif->nw_type == INFRA_NETWORK) &&
(test_bit(CONNECTED, &vif->flags))) {
if (test_bit(DISCONNECT_PEND, &vif->flags)) {
/*
* Already be called by other commands
* (ex, interface down), so ignore.
*/
return 0;
}
set_bit(DISCONNECT_PEND, &vif->flags);
wait_event_interruptible_timeout(ar->event_wq,
!test_bit(DISCONNECT_PEND,
&vif->flags),
(HZ/2));
if (signal_pending(current)) {
ath6kl_err("wait DISCONNECT timeout!\n");
return -EINTR;
}
}
return 0;
}
void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
u8 *bssid, u8 assoc_resp_len,
u8 *assoc_info, u16 proto_reason)
{
struct ath6kl *ar = vif->ar;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG | ATH6KL_DBG_EXT_INFO1,
"%s: reason=%u, proto_reason %u, flag %lu\n",
__func__, reason, proto_reason, ar->flag);
/* avoid wmi event be processed while driver unloading */
if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
ath6kl_err("%s destroy in progress %lu\n", __func__, ar->flag);
return;
}
if (vif->scan_req) {
del_timer(&vif->vifscan_timer);
ath6kl_wmi_abort_scan_cmd(ar->wmi, vif->fw_vif_idx);
cfg80211_scan_done(vif->scan_req, true);
#ifdef USB_AUTO_SUSPEND
if (ath6kl_hif_auto_pm_get_usage_cnt(ar) == 0) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG |
ATH6KL_DBG_EXT_AUTOPM,
"%s: warnning refcnt=0, my=%d/%d\n",
__func__,
ar->auto_pm_cnt,
ar->auto_pm_fail_cnt);
} else
ath6kl_hif_auto_pm_enable(ar);
#endif
vif->scan_req = NULL;
clear_bit(SCANNING, &vif->flags);
}
if (vif->nw_type & ADHOC_NETWORK) {
if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in ibss mode\n", __func__);
}
return;
}
if (vif->nw_type & INFRA_NETWORK) {
if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in station mode\n", __func__);
return;
}
}
if (vif->pend_skb)
ath6kl_flush_pend_skb(vif);
/*
* Send a disconnect command to target when a disconnect event is
* received with reason code other than 3 (DISCONNECT_CMD - disconnect
* request from host) to make the firmware stop trying to connect even
* after giving disconnect event. There will be one more disconnect
* event for this disconnect command with reason code DISCONNECT_CMD
* which will be notified to cfg80211.
*/
if (reason != DISCONNECT_CMD) {
if ((reason == AUTH_FAILED) &&
(vif->dot11_auth_mode & SHARED_AUTH))
vif->next_conn_status = WLAN_STATUS_CHALLENGE_FAIL;
else
vif->next_conn_status = WLAN_STATUS_UNSPECIFIED_FAILURE;
ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
return;
}
clear_bit(CONNECT_PEND, &vif->flags);
if (vif->sme_state == SME_CONNECTING) {
u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
if (vif->next_conn_status)
status = vif->next_conn_status;
ath6kl_cfg80211_connect_result(vif,
bssid, NULL, 0,
NULL, 0,
status,
GFP_KERNEL);
} else if (vif->sme_state == SME_CONNECTED) {
ath6kl_cfg80211_disconnected(vif, proto_reason,
NULL, 0, GFP_KERNEL);
}
#ifdef ATH6KL_DIAGNOSTIC
wifi_diag_mac_fsm_event(vif,
(enum wifi_diag_mac_fsm_t)WIFI_DIAG_MAC_FSM_DISCONNECTED,
vif->diag.disconnect_seq_num);
#endif
vif->sme_state = SME_DISCONNECTED;
vif->next_conn_status = WLAN_STATUS_SUCCESS;
if (reason == DISCONNECT_CMD) {
if (test_bit(DISCONNECT_PEND, &vif->flags) &&
(vif->nw_type == INFRA_NETWORK)) {
clear_bit(DISCONNECT_PEND, &vif->flags);
wake_up(&ar->event_wq);
}
}
}
static int ath6kl_cfg80211_change_bss(struct wiphy *wiphy,
struct net_device *ndev, struct bss_parameters *params)
{
struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
struct ath6kl_vif *vif = netdev_priv(ndev);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (params->ap_isolate >= 0)
vif->intra_bss = !params->ap_isolate;
/* FIXME : support others. */
up(&ar->sem);
return 0;
}
void ath6kl_scan_timer_handler(unsigned long ptr)
{
struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr;
struct ath6kl *ar = vif->ar;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s scan timer hit\n", __func__);
if (vif->scan_req) {
ath6kl_wmi_abort_scan_cmd(ar->wmi, vif->fw_vif_idx);
cfg80211_scan_done(vif->scan_req, true);
#ifdef USB_AUTO_SUSPEND
/*
* Here maybe false alarmif race-condition happened between
* scan-comp-event & scan-abort-command.
*/
if (ath6kl_hif_auto_pm_get_usage_cnt(ar) == 0) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG |
ATH6KL_DBG_EXT_SCAN |
ATH6KL_DBG_EXT_AUTOPM,
"%s: warnning refcnt=0, my=%d/%d\n",
__func__,
ar->auto_pm_cnt,
ar->auto_pm_fail_cnt);
} else
ath6kl_hif_auto_pm_enable(ar);
#endif
vif->scan_req = NULL;
clear_bit(SCANNING, &vif->flags);
}
}
/* assume we support not more than two differnet channels */
static int ath6kl_scan_timeout_cal(struct ath6kl *ar)
{
struct ath6kl_vif *vif;
u16 connected_count = 0;
if (!(ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM))
return ATH6KL_SCAN_TIMEOUT_WITHOUT_ROAM;
if (ath6kl_scan_timeout &&
(ath6kl_scan_timeout < ATH6KL_SCAN_TIMEOUT_SHORT))
return ATH6KL_SCAN_TIMEOUT_SHORT;
list_for_each_entry(vif, &ar->vif_list, list) {
if (test_bit(CONNECTED, &vif->flags)) {
connected_count++;
if (connected_count == 1)
return ATH6KL_SCAN_TIMEOUT_ONE_CON;
else if (connected_count > 1)
return ATH6KL_SCAN_TIMEOUT_LONG;
}
}
return ATH6KL_SCAN_TIMEOUT_SHORT;
}
static int ath6kl_set_probe_req_ies(struct ath6kl_vif *vif, const u8 *ies,
size_t ies_len)
{
struct ath6kl *ar = vif->ar;
const u8 *pos;
u8 *buf = NULL;
size_t len = 0;
int ret;
/*
* Filter out P2P/WFD IE(s)
*/
if (ies && ies_len) {
buf = kmalloc(ies_len, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
pos = ies;
while (pos + 1 < ies + ies_len) {
if (pos + 2 + pos[1] > ies + ies_len)
break;
if ((ath6kl_is_p2p_ie(pos) ||
ath6kl_is_wfd_ie(pos)) &&
!ath6kl_p2p_ie_append(vif,
P2P_IE_IN_PROBE_REQ))
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"Remove Probe's P2P IE\n");
else {
memcpy(buf + len, pos, 2 + pos[1]);
len += 2 + pos[1];
}
pos += 2 + pos[1];
}
}
ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_PROBE_REQ, buf, len);
kfree(buf);
return ret;
}
static bool _ath6kl_scanband_ignore_ch(struct ath6kl_vif *vif, u16 freq)
{
int i;
for (i = 0; i < 64; i++) {
if (freq == vif->scanband_ignore_chan[i])
return true;
if (vif->scanband_ignore_chan[i] == 0)
return false;
}
return false;
}
static s8 ath6kl_scanband(struct ath6kl_vif *vif,
u16 *channels,
s8 n_channels,
struct cfg80211_scan_request *request)
{
int i;
u8 skip_chan_num = 0;
u8 num_chan = n_channels;
switch (vif->scanband_type) {
case SCANBAND_TYPE_CHAN_ONLY:
channels[0] = vif->scanband_chan;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"Only signal channel scan, channel %d\n",
channels[0]);
num_chan = 1;
break;
case SCANBAND_TYPE_5G:
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"Only 5G channels scan, channel list - ");
for (i = 0; i < n_channels; i++) {
if (request->channels[i]->center_freq <= 2484) {
skip_chan_num++;
continue;
}
channels[i - skip_chan_num] =
request->channels[i]->center_freq;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"%d ", channels[i - skip_chan_num]);
}
num_chan -= skip_chan_num;
break;
case SCANBAND_TYPE_2G:
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"Only 2G channels scan, channel list - ");
for (i = 0; i < n_channels; i++) {
if (request->channels[i]->center_freq > 2484) {
skip_chan_num++;
continue;
}
channels[i - skip_chan_num] =
request->channels[i]->center_freq;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"%d ", channels[i - skip_chan_num]);
}
num_chan -= skip_chan_num;
break;
case SCANBAND_TYPE_P2PCHAN:
num_chan = ath6kl_p2p_build_scan_chan(vif,
n_channels,
channels);
if (num_chan) {
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"Only P2P channels scan, full scan instead\n");
break;
} else
num_chan = n_channels;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"Only P2P channels scan, channel list - ");
for (i = 0; i < n_channels; i++) {
if (!ath6kl_reg_is_p2p_channel(vif->ar,
request->channels[i]->center_freq)) {
skip_chan_num++;
continue;
}
channels[i - skip_chan_num] =
request->channels[i]->center_freq;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"%d ", channels[i - skip_chan_num]);
}
num_chan -= skip_chan_num;
break;
case SCANBAND_TYPE_2_P2PCHAN:
num_chan = ath6kl_p2p_build_scan_chan(vif,
n_channels,
channels);
if (num_chan) {
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"x2 P2P channels scan, full scan instead\n");
break;
} else
num_chan = n_channels;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"x2 P2P channels scan, channel list - ");
for (i = 0; i < n_channels; i++) {
if (!ath6kl_reg_is_p2p_channel(vif->ar,
request->channels[i]->center_freq)) {
skip_chan_num++;
continue;
}
channels[i - skip_chan_num] =
request->channels[i]->center_freq;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"%d ", channels[i - skip_chan_num]);
}
num_chan -= skip_chan_num;
/* Avoid to scan lots of channels. */
if (num_chan <= (WMI_MAX_CHANNELS >> 1)) {
memcpy(&channels[num_chan],
&channels[0],
num_chan * sizeof(u16));
num_chan *= 2;
}
break;
case SCANBAND_TYPE_IGNORE_DFS:
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"No DFS channels scan, channel list - ");
for (i = 0; i < n_channels; i++) {
if (ath6kl_reg_is_dfs_channel(vif->ar,
request->channels[i]->center_freq)) {
skip_chan_num++;
continue;
}
channels[i - skip_chan_num] =
request->channels[i]->center_freq;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"%d ", channels[i - skip_chan_num]);
}
num_chan -= skip_chan_num;
break;
case SCANBAND_TYPE_IGNORE_CH:
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"Ignore channels scan, channel list - ");
for (i = 0; i < n_channels; i++) {
if (_ath6kl_scanband_ignore_ch(vif,
request->channels[i]->center_freq)) {
skip_chan_num++;
continue;
}
channels[i - skip_chan_num] =
request->channels[i]->center_freq;
ath6kl_dbg(ATH6KL_DBG_INFO | ATH6KL_DBG_EXT_SCAN,
"%d ", channels[i - skip_chan_num]);
}
num_chan -= skip_chan_num;
break;
default:
for (i = 0; i < n_channels; i++)
channels[i] = request->channels[i]->center_freq;
break;
}
return num_chan;
}
static int _ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_scan_request *request)
{
struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
struct ath6kl_vif *vif = netdev_priv(ndev);
s8 n_channels = 0;
u16 *channels = NULL;
int ret = 0;
u32 force_fg_scan = 0;
bool sche_scan_trig, left;
if (test_bit(DISABLE_SCAN, &ar->flag)) {
ath6kl_dbg(ATH6KL_DBG_EXT_SCAN,
"scan is disabled temporarily\n");
return -EIO;
}
if (vif->sme_state == SME_CONNECTING) {
ath6kl_dbg(ATH6KL_DBG_EXT_SCAN,
"Connection on-going, reject scan\n");
return -EBUSY;
}
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG | ATH6KL_DBG_EXT_SCAN,
"%s\n", __func__);
/*
* Last Cancel-RoC not yet finished. To update vif->last_cancel_roc_id
* first to avoid wrong cookie report to supplicant.
*/
if (test_bit(ROC_CANCEL_PEND, &vif->flags)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : Scan but Cancel-RoC not yet back, wait it finish %x\n",
vif->last_cancel_roc_id);
wait_event_interruptible_timeout(ar->event_wq,
!test_bit(ROC_ONGOING, &vif->flags),
WMI_TIMEOUT);
if (signal_pending(current)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : target did not respond\n");
up(&ar->sem);
return -EINTR;
}
}
/* RoC is ongoing and stop it first. */
if (test_bit(ROC_ONGOING, &vif->flags)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : Scan but On-going-RoC, cancel it first %x\n",
vif->last_roc_id);
set_bit(ROC_CANCEL_PEND, &vif->flags);
if (ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi,
vif->fw_vif_idx) != 0) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : cancel ROC failed\n");
clear_bit(ROC_CANCEL_PEND, &vif->flags);
up(&ar->sem);
return -EIO;
}
left = wait_event_interruptible_timeout(ar->event_wq,
!test_bit(ROC_ONGOING, &vif->flags),
WMI_TIMEOUT);
if (left == 0) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : wait cancel RoC timeout\n");
clear_bit(ROC_CANCEL_PEND, &vif->flags);
clear_bit(ROC_ONGOING, &vif->flags);
up(&ar->sem);
return -EINTR;
}
if (signal_pending(current)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : target did not respond\n");
up(&ar->sem);
return -EINTR;
}
}
sche_scan_trig = ath6kl_sched_scan_trigger(vif);
if (!vif->usr_bss_filter) {
clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
/*
* EV: 109005
* Fix bug that Probe response from the connected AP
* will be filtered by setting this filter ,
* thus the AP's information won't be updated.
*/
ret = ath6kl_wmi_bssfilter_cmd(
ar->wmi, vif->fw_vif_idx, ALL_BSS_FILTER , 0);
if (ret) {
ath6kl_err("couldn't set bss filtering\n");
up(&ar->sem);
return ret;
}
}
if ((request->n_ssids && request->ssids[0].ssid_len) &&
(!sche_scan_trig)) {
u8 i;
if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
for (i = 0; i < request->n_ssids; i++)
ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
i + 1, SPECIFIC_SSID_FLAG,
request->ssids[i].ssid_len,
request->ssids[i].ssid);
} else if (ar->p2p)
ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
MAX_PROBED_SSID_INDEX, ANY_SSID_FLAG,
0,
NULL);
if ((request->ie) &&
(!sche_scan_trig)) {
ret = ath6kl_set_probe_req_ies(vif,
request->ie,
request->ie_len);
if (ret) {
ath6kl_err("failed to set Probe Request appie for "
"scan");
up(&ar->sem);
return ret;
}
}
/*
* Scan only the requested channels if the request specifies a set of
* channels. If the list is longer than the target supports, do not
* configure the list and instead, scan all available channels.
*/
if ((request->n_channels > 0 &&
request->n_channels <= WMI_MAX_CHANNELS) &&
(!sche_scan_trig)) {
n_channels = request->n_channels;
channels = kzalloc(WMI_MAX_CHANNELS * sizeof(u16) * 2,
GFP_KERNEL);
if (channels == NULL) {
ath6kl_dbg(ATH6KL_DBG_EXT_SCAN,
"failed to set scan chan, scan all channel\n");
n_channels = 0;
}
if (n_channels) {
/* Rearrange according scanband */
n_channels = ath6kl_scanband(vif,
channels,
n_channels,
request);
}
}
if (test_bit(CONNECTED, &vif->flags))
force_fg_scan = 1;
if (test_and_set_bit(SCANNING, &vif->flags)) {
kfree(channels);
up(&ar->sem);
return -EBUSY;
}
if (test_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags) ||
test_bit(EAPOL_HANDSHAKE_PROTECT, &ar->flag)) {
ath6kl_dbg(ATH6KL_DBG_EXT_SCAN,
"EAPOL %s_handshake_protect reject scan\n",
(test_bit(EAPOL_HANDSHAKE_PROTECT, &ar->flag) ?
"rekey" : "connect"));
clear_bit(SCANNING, &vif->flags);
kfree(channels);
up(&ar->sem);
return -EBUSY;
}
/* Disable DFS channel scan */
vif->sc_params.scan_ctrl_flags |= ENABLE_DFS_SKIP_CTRL_FLAGS;
ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
vif->sc_params.fg_start_period,
vif->sc_params.fg_end_period,
vif->sc_params.bg_period,
vif->sc_params.minact_chdwell_time,
vif->sc_params.maxact_chdwell_time,
vif->sc_params.pas_chdwell_time,
vif->sc_params.short_scan_ratio,
vif->sc_params.scan_ctrl_flags,
vif->sc_params.max_dfsch_act_time,
vif->sc_params.maxact_scan_per_ssid);
if (ret) {
ath6kl_err("ath6kl_cfg80211_scan: set scan parameter failed\n");
clear_bit(SCANNING, &vif->flags);
kfree(channels);
up(&ar->sem);
return ret;
}
ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN,
force_fg_scan, false, 0,
ATH6KL_FG_SCAN_INTERVAL,
n_channels, channels);
if (ret) {
ath6kl_err("wmi_startscan_cmd failed\n");
clear_bit(SCANNING, &vif->flags);
} else {
vif->scan_req = request;
mod_timer(&vif->vifscan_timer,
jiffies + ath6kl_scan_timeout_cal(ar));
#ifdef USB_AUTO_SUSPEND
/* Disable autopm until scan finished. */
ath6kl_hif_auto_pm_disable(ar);
#endif
}
kfree(channels);
ath6kl_bss_post_proc_bss_scan_start(vif);
ath6kl_p2p_rc_scan_start(vif, false);
up(&ar->sem);
return ret;
}
#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV
static int ath6kl_cfg80211_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *request)
{
BUG_ON(!request->wdev);
BUG_ON(!request->wdev->netdev);
return _ath6kl_cfg80211_scan(wiphy,
request->wdev->netdev,
request);
}
#else
static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_scan_request *request)
{
return _ath6kl_cfg80211_scan(wiphy, ndev, request);
}
#endif
void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
{
struct ath6kl *ar = vif->ar;
int i;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG | ATH6KL_DBG_EXT_SCAN,
"%s: status%s\n", __func__,
aborted ? " aborted" : " complete");
#ifdef USB_AUTO_SUSPEND
/*
* Here maybe flase alarm. Ex, th6kl_cfg80211_stop() try
* to finish all actions after stop the device and not really
* wait event back, or this interface will be deleted later
* (like p2p-p2p0-x) for some reaseon.
*/
if (ath6kl_hif_auto_pm_get_usage_cnt(ar) == 0) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG |
ATH6KL_DBG_EXT_SCAN |
ATH6KL_DBG_EXT_AUTOPM,
"%s: warnning refcnt=0, my=%d/%d\n",
__func__,
ar->auto_pm_cnt,
ar->auto_pm_fail_cnt);
} else
ath6kl_hif_auto_pm_enable(ar);
#endif
del_timer(&vif->vifscan_timer);
if (test_bit(SCANNING_WAIT, &vif->flags)) {
clear_bit(SCANNING_WAIT, &vif->flags);
wake_up(&ar->event_wq);
}
if (!vif->scan_req)
return;
if (aborted)
goto out;
if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
for (i = 0; i < vif->scan_req->n_ssids; i++) {
ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
i + 1, DISABLE_SSID_FLAG,
0, NULL);
}
} else if (ar->p2p)
ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
MAX_PROBED_SSID_INDEX,
DISABLE_SSID_FLAG,
0, NULL);
out:
cfg80211_scan_done(vif->scan_req, aborted);
vif->scan_req = NULL;
clear_bit(SCANNING, &vif->flags);
}
#ifdef PMF_SUPPORT
static int ath6kl_cfg80211_add_mgmt_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr,
struct key_params *params)
{
struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
struct ath6kl_vif *vif = netdev_priv(ndev);
struct ath6kl_key *key = NULL;
enum wmi_sync_flag sync_flag = NO_SYNC_WMIFLAG;
int ret;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (!params)
return -EINVAL;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (key_index < WMI_MIN_IGTK_INDEX || key_index > WMI_MAX_IGTK_INDEX) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: key index %d out of bounds\n", __func__,
key_index);
up(&ar->sem);
return -ENOENT;
}
key = &vif->keys[key_index];
memset(key, 0, sizeof(struct ath6kl_key));
if (params) {
int seq_len = params->seq_len;
if (params->key_len > WMI_IGTK_KEY_LEN ||
seq_len > sizeof(key->seq)) {
up(&ar->sem);
return -EINVAL;
}
key->key_len = params->key_len;
memcpy(key->key, params->key, key->key_len);
key->seq_len = seq_len;
memcpy(key->seq, params->seq, key->seq_len);
key->cipher = params->cipher;
}
if (vif->nw_type == AP_NETWORK && !pairwise && params) {
/* Do something here for AP/GO mode. */
;
}
if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WMI_SYC))
sync_flag = NO_SYNC_WMIFLAG;
ret = ath6kl_wmi_addkey_igtk_cmd(ar->wmi, vif->fw_vif_idx, key_index,
key->key_len, key->seq,
key->key, sync_flag);
up(&ar->sem);
return ret;
}
#endif
static int ath6kl_cfg80211_add_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr,
struct key_params *params)
{
struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
struct ath6kl_vif *vif = netdev_priv(ndev);
struct ath6kl_key *key = NULL;
enum wmi_sync_flag sync_flag = SYNC_BOTH_WMIFLAG;
u8 key_usage;
u8 key_type;
int ret;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (!params)
return -EINVAL;
#ifdef PMF_SUPPORT
if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
return ath6kl_cfg80211_add_mgmt_key(wiphy, ndev, key_index,
pairwise, mac_addr, params);
#endif
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
if (params->key_len != WMI_KRK_LEN) {
up(&ar->sem);
return -EINVAL;
}
ret = ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
params->key);
up(&ar->sem);
return ret;
}
if (key_index > WMI_MAX_KEY_INDEX) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: key index %d out of bounds\n", __func__,
key_index);
up(&ar->sem);
return -ENOENT;
}
key = &vif->keys[key_index];
memset(key, 0, sizeof(struct ath6kl_key));
if (pairwise)
key_usage = PAIRWISE_USAGE;
else
key_usage = GROUP_USAGE;
if (params) {
int seq_len = params->seq_len;
if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
seq_len > ATH6KL_KEY_SEQ_LEN) {
/* Only first half of the WPI PN is configured */
seq_len = ATH6KL_KEY_SEQ_LEN;
}
if (params->key_len > WLAN_MAX_KEY_LEN ||
seq_len > sizeof(key->seq)) {
up(&ar->sem);
return -EINVAL;
}
key->key_len = params->key_len;
memcpy(key->key, params->key, key->key_len);
key->seq_len = seq_len;
memcpy(key->seq, params->seq, key->seq_len);
key->cipher = params->cipher;
}
switch (key->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
key_type = WEP_CRYPT;
break;
case WLAN_CIPHER_SUITE_TKIP:
key_type = TKIP_CRYPT;
break;
case WLAN_CIPHER_SUITE_CCMP:
key_type = AES_CRYPT;
break;
case WLAN_CIPHER_SUITE_SMS4:
key_type = WAPI_CRYPT;
break;
default:
up(&ar->sem);
return -ENOTSUPP;
}
if (((vif->auth_mode == WPA_PSK_AUTH)
|| (vif->auth_mode == WPA2_PSK_AUTH))
&& (key_usage & GROUP_USAGE))
del_timer(&vif->disconnect_timer);
if (key_usage & GROUP_USAGE) {
if (vif->pend_skb) {
ath6kl_err("eapol protect shall be off already\n");
ath6kl_flush_pend_skb(vif);
}
spin_lock_bh(&vif->if_lock);
clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags);
del_timer(&vif->shprotect_timer);
spin_unlock_bh(&vif->if_lock);
}
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
__func__, key_index, key->key_len, key_type,
key_usage, key->seq_len);
if (vif->nw_type == AP_NETWORK && !pairwise &&
(key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
vif->ap_mode_bkey.valid = true;
vif->ap_mode_bkey.key_index = key_index;
vif->ap_mode_bkey.key_type = key_type;
vif->ap_mode_bkey.key_len = key->key_len;
memcpy(vif->ap_mode_bkey.key, key->key, key->key_len);
if (!test_bit(CONNECTED, &vif->flags)) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
"key configuration until AP mode has been "
"started\n");
/*
* The key will be set in ath6kl_connect_ap_mode() once
* the connected event is received from the target.
*/
up(&ar->sem);
return 0;
}
}
if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
!test_bit(CONNECTED, &vif->flags)) {
/*
* Store the key locally so that it can be re-configured after
* the AP mode has properly started
* (ath6kl_install_statioc_wep_keys).
*/
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
"until AP mode has been started\n");
vif->wep_key_list[key_index].key_len = key->key_len;
memcpy(vif->wep_key_list[key_index].key, key->key,
key->key_len);
up(&ar->sem);
return 0;
}
if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WMI_SYC))
sync_flag = NO_SYNC_WMIFLAG;
ret = ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
key_type, key_usage, key->key_len,
key->seq, key->seq_len, key->key,
KEY_OP_INIT_VAL,
(u8 *) mac_addr, sync_flag);
up(&ar->sem);
return ret;
}
static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr)
{
struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
struct ath6kl_vif *vif = netdev_priv(ndev);
int ret;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (key_index > WMI_MAX_KEY_INDEX) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: key index %d out of bounds\n", __func__,
key_index);
up(&ar->sem);
return -ENOENT;
}
if (!vif->keys[key_index].key_len) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: index %d is empty\n", __func__, key_index);
up(&ar->sem);
return 0;
}
vif->keys[key_index].key_len = 0;
ret = ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
up(&ar->sem);
return ret;
}
static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr, void *cookie,
void (*callback) (void *cookie,
struct key_params *))
{
struct ath6kl_vif *vif = netdev_priv(ndev);
struct ath6kl_key *key = NULL;
struct key_params params;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&vif->ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (key_index > WMI_MAX_KEY_INDEX) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: key index %d out of bounds\n", __func__,
key_index);
up(&vif->ar->sem);
return -ENOENT;
}
key = &vif->keys[key_index];
memset(&params, 0, sizeof(params));
params.cipher = key->cipher;
params.key_len = key->key_len;
params.seq_len = key->seq_len;
params.seq = key->seq;
params.key = key->key;
callback(cookie, &params);
up(&vif->ar->sem);
return key->key_len ? 0 : -ENOENT;
}
static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool unicast,
bool multicast)
{
struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
struct ath6kl_vif *vif = netdev_priv(ndev);
struct ath6kl_key *key = NULL;
u8 key_usage;
enum crypto_type key_type = NONE_CRYPT;
enum wmi_sync_flag sync_flag = SYNC_BOTH_WMIFLAG;
int ret;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (key_index > WMI_MAX_KEY_INDEX) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: key index %d out of bounds\n",
__func__, key_index);
up(&ar->sem);
return -ENOENT;
}
if (!vif->keys[key_index].key_len) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
__func__, key_index);
up(&ar->sem);
return -EINVAL;
}
vif->def_txkey_index = key_index;
key = &vif->keys[vif->def_txkey_index];
key_usage = GROUP_USAGE;
if (vif->prwise_crypto == WEP_CRYPT)
key_usage |= TX_USAGE;
if (unicast)
key_type = vif->prwise_crypto;
if (multicast)
key_type = vif->grp_crypto;
if (vif->next_mode ==
AP_NETWORK && !test_bit(CONNECTED, &vif->flags)) {
up(&ar->sem);
return 0; /* Delay until AP mode has been started */
}
if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WMI_SYC))
sync_flag = NO_SYNC_WMIFLAG;
ret = ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
vif->def_txkey_index,
key_type, key_usage,
key->key_len, key->seq, key->seq_len,
key->key,
KEY_OP_INIT_VAL, NULL,
sync_flag);
up(&ar->sem);
return ret;
}
void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
bool ismcast)
{
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
(ismcast ? NL80211_KEYTYPE_GROUP :
NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
GFP_KERNEL);
}
static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct ath6kl_vif *vif;
int ret;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
changed);
vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
ret = ath6kl_wmi_set_rts_cmd(ar->wmi, 0, wiphy->rts_threshold);
if (ret != 0) {
ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
up(&ar->sem);
return -EIO;
}
}
up(&ar->sem);
return 0;
}
/*
* The type nl80211_tx_power_setting replaces the following
* data type from 2.6.36 onwards
*/
static int _ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_tx_power_setting type,
int mbm)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct ath6kl_vif *vif = netdev_priv(ndev);
u8 ath6kl_dbm;
int dbm = MBM_TO_DBM(mbm);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
type, dbm);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
up(&ar->sem);
return 0;
case NL80211_TX_POWER_LIMITED:
ar->tx_pwr = ath6kl_dbm = dbm;
break;
default:
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
__func__, type);
up(&ar->sem);
return -EOPNOTSUPP;
}
ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
up(&ar->sem);
return 0;
}
static int _ath6kl_cfg80211_get_txpower(struct wiphy *wiphy,
struct net_device *ndev,
int *dbm)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct ath6kl_vif *vif = netdev_priv(ndev);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (test_bit(CONNECTED, &vif->flags)) {
ar->tx_pwr = 0;
if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
up(&ar->sem);
return -EIO;
}
wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
5 * HZ);
if (signal_pending(current)) {
ath6kl_err("target did not respond\n");
up(&ar->sem);
return -EINTR;
}
}
*dbm = ar->tx_pwr;
up(&ar->sem);
return 0;
}
#ifdef CFG80211_TX_POWER_PER_WDEV
static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_tx_power_setting type,
int mbm)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct net_device *ndev;
if (wdev == NULL) {
struct ath6kl_vif *vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
ndev = vif->ndev;
} else
ndev = wdev->netdev;
return _ath6kl_cfg80211_set_txpower(wiphy, ndev, type, mbm);
}
static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy,
struct wireless_dev *wdev,
int *dbm)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct net_device *ndev;
if (wdev == NULL) {
struct ath6kl_vif *vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
ndev = vif->ndev;
} else
ndev = wdev->netdev;
return _ath6kl_cfg80211_get_txpower(wiphy, ndev , dbm);
}
#else
static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
enum nl80211_tx_power_setting type,
int mbm)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct ath6kl_vif *vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
return _ath6kl_cfg80211_set_txpower(wiphy, vif->ndev, type, mbm);
}
static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct ath6kl_vif *vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
return _ath6kl_cfg80211_get_txpower(wiphy, vif->ndev, dbm);
}
#endif
static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
struct net_device *dev,
bool pmgmt, int timeout)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct wmi_power_mode_cmd mode;
struct ath6kl_vif *vif = netdev_priv(dev);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
__func__, pmgmt, timeout);
if (test_bit(PS_STICK, &vif->flags)) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "PS mode already stick (%d).\n",
vif->last_pwr_mode);
return 0;
}
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (pmgmt) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
mode.pwr_mode = REC_POWER;
} else {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
mode.pwr_mode = MAX_PERF_POWER;
}
if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
mode.pwr_mode) != 0) {
ath6kl_err("wmi_powermode_cmd failed\n");
up(&ar->sem);
return -EIO;
}
up(&ar->sem);
return 0;
}
static struct net_device *_ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
char *name,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
struct ath6kl *ar = wiphy_priv(wiphy);
struct net_device *ndev;
u8 if_idx, nw_type;
if (!__ath6kl_cfg80211_ready(ar))
return NULL;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return NULL;
}
if (ar->num_vif == ar->vif_max) {
ath6kl_err("Reached maximum number of supported vif\n");
up(&ar->sem);
return NULL;
}
if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
ath6kl_err("Not a supported interface type\n");
up(&ar->sem);
return NULL;
}
ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
if (!ndev) {
up(&ar->sem);
return NULL;
}
ar->num_vif++;
up(&ar->sem);
return ndev;
}
static int _ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
struct net_device *ndev)
{
struct ath6kl *ar = wiphy_priv(wiphy);
struct ath6kl_vif *vif = netdev_priv(ndev);
if (!__ath6kl_cfg80211_ready(ar))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
/* fix EV110820 */
clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
NONE_BSS_FILTER, 0);
ath6kl_judge_roam_parameter(vif, true);
ath6kl_switch_parameter_based_on_connection(vif, true);
spin_lock_bh(&ar->list_lock);
list_del(&vif->list);
spin_unlock_bh(&ar->list_lock);
ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
ath6kl_deinit_if_data(vif);
up(&ar->sem);
return 0;
}
#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV
static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
const char *name,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
struct net_device *dev;
dev = _ath6kl_cfg80211_add_iface(wiphy,
(char *)name,
type,
flags,
params);
if (dev)
return dev->ieee80211_ptr;
else
return ERR_PTR(-EINVAL);
}
static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
BUG_ON(!wdev->netdev);
return _ath6kl_cfg80211_del_iface(wiphy, wdev->netdev);
}
#else
static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
char *name,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
struct net_device *dev;
dev = _ath6kl_cfg80211_add_iface(wiphy,
name,
type,
flags,
params);
if (dev)
return dev;
else
return ERR_PTR(-EINVAL);
}
static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
struct net_device *ndev)
{
return _ath6kl_cfg80211_del_iface(wiphy, ndev);
}
#endif
static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
struct ath6kl_vif *vif = netdev_priv(ndev);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
if (down_interruptible(&vif->ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
switch (type) {
case NL80211_IFTYPE_STATION:
vif->next_mode = INFRA_NETWORK;
break;
case NL80211_IFTYPE_ADHOC:
vif->next_mode = ADHOC_NETWORK;
break;
case NL80211_IFTYPE_AP:
vif->next_mode = AP_NETWORK;
break;
case NL80211_IFTYPE_P2P_CLIENT:
vif->next_mode = INFRA_NETWORK;
break;
case NL80211_IFTYPE_P2P_GO:
vif->next_mode = AP_NETWORK;
break;
default:
ath6kl_err("invalid interface type %u\n", type);
up(&vif->ar->sem);
return -EOPNOTSUPP;
}
vif->wdev.iftype = type;
up(&vif->ar->sem);
return 0;
}
static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_ibss_params *ibss_param)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
struct ieee80211_channel *chan;
int status;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
vif->ssid_len = ibss_param->ssid_len;
memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
#ifdef CFG80211_NEW_CHAN_DEFINITION
chan = ibss_param->chandef.chan;
#else
chan = ibss_param->channel;
#endif
if (chan)
vif->ch_hint = chan->center_freq;
if (ibss_param->channel_fixed) {
/*
* TODO: channel_fixed: The channel should be fixed, do not
* search for IBSSs to join on other channels. Target
* firmware does not support this feature, needs to be
* updated.
*/
up(&ar->sem);
return -EOPNOTSUPP;
}
/* Diable background scan */
vif->sc_params.bg_period = 0xFFFF;
#ifdef ATH6KL_SUPPORT_WIFI_KTK
vif->sc_params.minact_chdwell_time = 0;
vif->sc_params.maxact_chdwell_time = 105;
#endif
ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
vif->sc_params.fg_start_period,
vif->sc_params.fg_end_period,
vif->sc_params.bg_period,
vif->sc_params.minact_chdwell_time,
vif->sc_params.maxact_chdwell_time,
vif->sc_params.pas_chdwell_time,
vif->sc_params.short_scan_ratio,
vif->sc_params.scan_ctrl_flags,
vif->sc_params.max_dfsch_act_time,
vif->sc_params.maxact_scan_per_ssid);
memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
memcpy(vif->req_bssid, ibss_param->bssid,
sizeof(vif->req_bssid));
ath6kl_set_wpa_version(vif, 0);
status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
if (status) {
up(&ar->sem);
return status;
}
#ifdef ATH6KL_SUPPORT_WIFI_KTK
if (!ar->ktk_active) {
#endif
if (ibss_param->privacy) {
ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
} else {
ath6kl_set_cipher(vif, 0, true);
ath6kl_set_cipher(vif, 0, false);
}
#ifdef ATH6KL_SUPPORT_WIFI_KTK
} else {
ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_CCMP, true);
ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_CCMP, false);
}
#endif
vif->nw_type = vif->next_mode;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: connect called with authmode %d dot11 auth %d"
" PW crypto %d PW crypto len %d GRP crypto %d"
" GRP crypto len %d channel hint %u\n",
__func__,
vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
vif->prwise_crypto_len, vif->grp_crypto,
vif->grp_crypto_len, vif->ch_hint);
status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
vif->dot11_auth_mode, vif->auth_mode,
vif->prwise_crypto,
vif->prwise_crypto_len,
vif->grp_crypto, vif->grp_crypto_len,
vif->ssid_len, vif->ssid,
vif->req_bssid, vif->ch_hint,
vif->connect_ctrl_flags);
set_bit(CONNECT_PEND, &vif->flags);
up(&ar->sem);
return 0;
}
static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
struct net_device *dev)
{
struct ath6kl_vif *vif = netdev_priv(dev);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&vif->ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
ath6kl_disconnect(vif);
memset(vif->ssid, 0, sizeof(vif->ssid));
vif->ssid_len = 0;
up(&vif->ar->sem);
return 0;
}
static const u32 cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
CCKM_KRK_CIPHER_SUITE,
WLAN_CIPHER_SUITE_SMS4,
#ifdef PMF_SUPPORT
WLAN_CIPHER_SUITE_AES_CMAC,
#endif
};
static bool is_rate_legacy(s32 rate)
{
static const s32 legacy[] = { 1000, 2000, 5500, 11000,
6000, 9000, 12000, 18000, 24000,
36000, 48000, 54000
};
u8 i;
for (i = 0; i < ARRAY_SIZE(legacy); i++)
if (rate == legacy[i])
return true;
return false;
}
static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
{
static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
52000, 58500, 65000
};
static const s32 ht20_sgi[] = { 7200, 14400, 21700, 28900, 43300,
57800, 65000, 72200
};
u8 i;
for (i = 0; i < ARRAY_SIZE(ht20); i++) {
if (rate == ht20[i]) {
*sgi = false;
*mcs = i;
return true;
}
}
for (i = 0; i < ARRAY_SIZE(ht20_sgi); i++) {
if (rate == ht20_sgi[i]) {
*sgi = true;
*mcs = i;
return true;
}
}
return false;
}
static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
{
static const s32 ht40[] = { 13500, 27000, 40500, 54000,
81000, 108000, 121500, 135000
};
static const s32 ht40_sgi[] = { 15000, 30000, 45000, 60000,
90000, 120000, 135000, 150000
};
u8 i;
for (i = 0; i < ARRAY_SIZE(ht40); i++) {
if (rate == ht40[i]) {
*sgi = false;
*mcs = i;
return true;
}
}
for (i = 0; i < ARRAY_SIZE(ht40_sgi); i++) {
if (rate == ht40_sgi[i]) {
*sgi = true;
*mcs = i;
return true;
}
}
return false;
}
static int __get_rate_info(struct rate_info *txrate, s32 rate, s8 rate_idx)
{
s8 rate_id = rate_idx & 0x7f;
txrate->flags = 0;
if (rate_id <= 11) {
txrate->legacy = rate / 100;
} else if (rate_id <= 43) {
txrate->flags |= RATE_INFO_FLAGS_MCS;
if (rate_idx >> 7)
txrate->flags |= RATE_INFO_FLAGS_SHORT_GI;
if (rate_id <= 27)
txrate->mcs = rate_id - 12;
else {
txrate->mcs = rate_id - 28;
txrate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
}
} else {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"invalid rate from ar6004 stats: rate %d idx %d\n",
rate, rate_idx);
return -1;
}
return 0;
}
static void _get_sta_info(struct ath6kl_vif *vif,
struct station_info *sinfo)
{
struct target_stats *stats = &vif->target_stats;
s32 rate;
s8 rateid;
u8 mcs;
bool sgi;
/* Not only STA but also IBSS mode. */
if (stats->rx_byte) {
sinfo->rx_bytes = stats->rx_byte;
sinfo->filled |= STATION_INFO_RX_BYTES;
sinfo->rx_packets = stats->rx_pkt;
sinfo->filled |= STATION_INFO_RX_PACKETS;
}
if (stats->tx_byte) {
sinfo->tx_bytes = stats->tx_byte;
sinfo->filled |= STATION_INFO_TX_BYTES;
sinfo->tx_packets = stats->tx_pkt;
sinfo->filled |= STATION_INFO_TX_PACKETS;
}
sinfo->signal = stats->cs_rssi;
sinfo->filled |= STATION_INFO_SIGNAL;
rate = stats->tx_ucast_rate;
if (vif->ar->target_type == TARGET_TYPE_AR6004) {
rateid = stats->tx_rate_index;
if (__get_rate_info(&sinfo->txrate, rate, rateid))
ath6kl_debug_war(vif->ar, ATH6KL_WAR_INVALID_RATE);
else
sinfo->filled |= STATION_INFO_TX_BITRATE;
} else {
sinfo->txrate.flags = 0;
if (is_rate_legacy(rate)) {
sinfo->txrate.legacy = rate / 100;
sinfo->filled |= STATION_INFO_TX_BITRATE;
} else if (is_rate_ht20(rate, &mcs, &sgi)) {
if (sgi)
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
sinfo->txrate.mcs = mcs;
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
sinfo->filled |= STATION_INFO_TX_BITRATE;
} else if (is_rate_ht40(rate, &mcs, &sgi)) {
if (sgi)
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
sinfo->txrate.mcs = mcs;
sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
sinfo->filled |= STATION_INFO_TX_BITRATE;
} else {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"invalid rate from stats: %d\n", rate);
ath6kl_debug_war(vif->ar, ATH6KL_WAR_INVALID_RATE);
}
}
if ((vif->nw_type == INFRA_NETWORK) &&
test_bit(CONNECTED, &vif->flags) &&
test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
vif->nw_type == INFRA_NETWORK) {
sinfo->filled |= STATION_INFO_BSS_PARAM;
sinfo->bss_param.flags = 0;
sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
}
return;
}
static void _get_ap_sta_info(struct ath6kl_vif *vif,
struct wmi_per_sta_stat *sta,
u8 *mac_addr,
struct station_info *sinfo)
{
s32 rate;
s8 rateid;
if (ath6kl_ap_keepalive_by_supp(vif))
sinfo->inactive_time =
ath6kl_ap_keepalive_get_inactive_time(vif, mac_addr);
else {
/*
* Always report 1 sec. to let supplicant bypass
* its keep-alive mechanims.
*/
sinfo->inactive_time = 1 * 1000;
}
sinfo->filled |= STATION_INFO_INACTIVE_TIME;
sinfo->rx_bytes = sta->rx_bytes;
sinfo->filled |= STATION_INFO_RX_BYTES;
sinfo->rx_packets = sta->rx_pkts;
sinfo->filled |= STATION_INFO_RX_PACKETS;
sinfo->tx_bytes = sta->tx_bytes;
sinfo->filled |= STATION_INFO_TX_BYTES;
sinfo->tx_packets = sta->tx_pkts;
sinfo->filled |= STATION_INFO_TX_PACKETS;
if (vif->ar->target_type == TARGET_TYPE_AR6004) {
rate = ath6kl_wmi_get_rate_ar6004(sta->tx_ucast_rate);
rateid = sta->tx_ucast_rate;
if (__get_rate_info(&sinfo->txrate, rate, rateid))
ath6kl_debug_war(vif->ar, ATH6KL_WAR_INVALID_RATE);
else
sinfo->filled |= STATION_INFO_TX_BITRATE;
}
return;
}
static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_info *sinfo)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
struct ath6kl_sta *conn = NULL;
long left;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (vif->nw_type == AP_NETWORK) {
conn = ath6kl_find_sta(vif, mac);
if (conn == NULL) {
ath6kl_err("Can't find %02x%02x%02x%02x%02x%02x\n",
mac[0], mac[1], mac[2],
mac[3], mac[4], mac[5]);
return -EINVAL;
}
} else {
if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
return -ENOENT;
}
#ifdef USB_AUTO_SUSPEND
/* skip to update FW statistics in 1min when wow suspend */
if (ar->state == ATH6KL_STATE_WOW) {
struct timeval cur_time;
long diff_time;
do_gettimeofday(&cur_time);
if (cur_time.tv_sec >= vif->target_stats.update_time.tv_sec) {
diff_time = cur_time.tv_sec
- vif->target_stats.update_time.tv_sec;
if (diff_time <= 60)
goto skip_fw_stats;
}
}
#endif
if (down_interruptible(&ar->sem))
return -EBUSY;
set_bit(STATS_UPDATE_PEND, &vif->flags);
if (ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx)) {
up(&ar->sem);
return -EIO;
}
left = wait_event_interruptible_timeout(ar->event_wq,
!test_bit(STATS_UPDATE_PEND,
&vif->flags),
WMI_TIMEOUT);
clear_bit(STATS_UPDATE_PEND, &vif->flags);
up(&ar->sem);
if (left == 0)
return -ETIMEDOUT;
else if (left < 0)
return left;
#ifdef USB_AUTO_SUSPEND
skip_fw_stats:
#endif
if (vif->nw_type == AP_NETWORK) {
struct wmi_ap_mode_stat *ap = &vif->ap_stats;
struct wmi_per_sta_stat *sta = NULL;
for (left = 0; left < AP_MAX_NUM_STA; left++) {
if (conn->aid == ap->sta[left].aid) {
sta = &ap->sta[left];
break;
}
}
if (!sta)
return -EINVAL;
_get_ap_sta_info(vif, sta, mac, sinfo);
return 0;
}
_get_sta_info(vif, sinfo);
return 0;
}
static int ath6kl_dump_station(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *mac, struct station_info *sinfo)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
struct ath6kl_sta *conn;
struct wmi_ap_mode_stat *ap;
struct wmi_per_sta_stat *sta;
long left;
int next;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (vif->nw_type == AP_NETWORK) {
/* Get AP stats only at least one station associated. */
if ((vif->sta_list_index == 0) ||
(idx >= AP_MAX_NUM_STA))
return -ENOENT;
/* Only need to update it when 1st STA dump. */
if (idx == 0)
vif->last_dump_ap_stats_idx = 0;
else
goto update_done;
} else {
if ((idx != 0) ||
!test_bit(CONNECTED, &vif->flags))
return -ENOENT;
}
if (down_interruptible(&ar->sem))
return -EBUSY;
set_bit(STATS_UPDATE_PEND, &vif->flags);
if (ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx)) {
up(&ar->sem);
return -EIO;
}
left = wait_event_interruptible_timeout(ar->event_wq,
!test_bit(STATS_UPDATE_PEND,
&vif->flags),
WMI_TIMEOUT);
clear_bit(STATS_UPDATE_PEND, &vif->flags);
up(&ar->sem);
if (left == 0)
return -ETIMEDOUT;
else if (left < 0)
return left;
update_done:
if (vif->nw_type == AP_NETWORK) {
if (vif->last_dump_ap_stats_idx >= AP_MAX_NUM_STA)
return -ENOENT;
/* Find next STA */
ap = &vif->ap_stats;
for (next = vif->last_dump_ap_stats_idx;
next < AP_MAX_NUM_STA;
next++) {
sta = &(ap->sta[next]);
if (sta->aid) {
conn = ath6kl_find_sta_by_aid(vif, sta->aid);
if (conn)
break;
}
}
/* All STAs reported */
if (next == AP_MAX_NUM_STA)
return -ENOENT;
/* Set to the next one. */
vif->last_dump_ap_stats_idx = next + 1;
memcpy(mac, conn->mac, ETH_ALEN);
_get_ap_sta_info(vif, sta, conn->mac, sinfo);
return 0;
}
memcpy(mac, vif->ndev->dev_addr, ETH_ALEN);
_get_sta_info(vif, sinfo);
return 0;
}
static int ath6kl_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
struct net_device *dev,
const u8 *peer,
const struct cfg80211_bitrate_mask *mask)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
int ret, disabled;
WARN_ON(peer);
if ((peer) ||
((mask->control[NL80211_BAND_2GHZ].legacy != 0xfff) &&
(mask->control[NL80211_BAND_2GHZ].legacy != 0xff0))) {
/* FIXME : not support yet */
return -ENOTSUPP;
}
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (mask->control[NL80211_BAND_2GHZ].legacy &
((1 << ath6kl_b_rates_size) - 1))
disabled = 0;
else
disabled = 1;
ret = ath6kl_wmi_disable_11b_rates_cmd(ar->wmi, vif->fw_vif_idx,
disabled);
up(&ar->sem);
return ret;
}
static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_pmksa *pmksa)
{
struct ath6kl *ar = ath6kl_priv(netdev);
struct ath6kl_vif *vif = netdev_priv(netdev);
int ret;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
ret = ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
pmksa->pmkid, true);
up(&ar->sem);
return ret;
}
static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_pmksa *pmksa)
{
struct ath6kl *ar = ath6kl_priv(netdev);
struct ath6kl_vif *vif = netdev_priv(netdev);
int ret;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
ret = ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
pmksa->pmkid, false);
up(&ar->sem);
return ret;
}
static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
{
struct ath6kl *ar = ath6kl_priv(netdev);
struct ath6kl_vif *vif = netdev_priv(netdev);
int ret;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (test_bit(CONNECTED, &vif->flags)) {
ret = ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
vif->bssid, NULL, false);
up(&ar->sem);
return ret;
}
up(&ar->sem);
return 0;
}
static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
{
struct ath6kl_vif *vif;
int ret, left;
#ifndef CONFIG_ANDROID
int pos;
u32 filter = 0;
u8 mask[WOW_MASK_SIZE];
#endif
u16 i;
struct in_device *in_dev;
struct in_ifaddr *ifa;
unsigned char src_ip[4];
/*if already in wow state just return without error*/
if (ar->state == ATH6KL_STATE_WOW)
return 0;
vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
#if (!defined(CONFIG_ANDROID) && !defined(USB_AUTO_SUSPEND))
if (!ar->get_wow_pattern) {
/* Clear existing WOW patterns */
for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
WOW_LIST_ID, i);
/* Configure new WOW patterns */
for (i = 0; i < wow->n_patterns; i++) {
if (ath6kl_wow_ext) {
ret = ath6kl_wmi_add_wow_ext_pattern_cmd(
ar->wmi,
vif->fw_vif_idx, WOW_LIST_ID,
wow->patterns[i].pattern_len,
i,
0,
wow->patterns[i].pattern,
wow->patterns[i].mask);
/* filter for wow ext pattern */
filter |= WOW_FILTER_OPTION_PATTERNS;
} else {
/*
* Convert given nl80211 specific mask value to
* equivalent driver specific mask value
* and send it to the chip along with patterns.
* For example, if the mask value defined
* in struct cfg80211_wowlan is 0xA
* (equivalent binary is 1010), then equivalent
* driver specific mask value is
* "0xFF 0x00 0xFF 0x00".
*/
memset(&mask, 0, sizeof(mask));
for (pos = 0;
pos < wow->patterns[i].pattern_len;
pos++) {
if (wow->patterns[i].mask[pos / 8] &
(0x1 << (pos % 8)))
mask[pos] = 0xFF;
}
/*
* Note: Pattern's offset is not passed
* as part of wowlan parameter from CFG layer.
* So it's always passed as ZERO to
* the firmware.
* It means, given WOW patterns are always
* matched from the first byte
* of received pkt in the firmware.
*/
ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
vif->fw_vif_idx, WOW_LIST_ID,
wow->patterns[i].pattern_len,
0 /* pattern offset */,
wow->patterns[i].pattern, mask);
}
if (ret)
return ret;
}
} else
filter |= WOW_FILTER_OPTION_PATTERNS;
if (wow) {
if (wow->disconnect || wow->any)
filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
if (wow->magic_pkt || wow->any)
filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
if (wow->gtk_rekey_failure || wow->any) {
filter |= (WOW_FILTER_OPTION_EAP_REQ |
WOW_FILTER_OPTION_8021X_4WAYHS |
WOW_FILTER_OPTION_GTK_ERROR |
WOW_FILTER_OPTION_OFFLOAD_GTK);
}
if (wow->eap_identity_req || wow->any)
filter |= WOW_FILTER_OPTION_EAP_REQ;
if (wow->four_way_handshake || wow->any)
filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
if (vif->arp_offload_ip_set || wow->any)
filter |= WOW_FILTER_OPTION_OFFLOAD_ARP;
}
/*Do GTK offload in WPA/WPA2 auth mode connection.*/
if (vif->auth_mode == WPA2_AUTH_CCKM || vif->auth_mode == WPA2_PSK_AUTH
|| vif->auth_mode == WPA_AUTH_CCKM || vif->auth_mode == WPA_PSK_AUTH){
filter |= WOW_FILTER_OPTION_OFFLOAD_GTK;
}
if (filter || (wow && wow->n_patterns)) {
ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_WOW_MODE_ENABLE,
filter,
WOW_HOST_REQ_DELAY);
if (ret)
return ret;
}
#endif /*!CONFIG_ANDROID*/
/* Setup own IP addr for ARP agent. */
for (i = 0; i < ar->vif_max; i++) {
struct ath6kl_vif *mvif = ath6kl_get_vif_by_index(ar, i);
if (!mvif)
continue;
memset(src_ip, 0x0, 4);
in_dev = __in_dev_get_rtnl(mvif->ndev);
if (in_dev) {
ifa = in_dev->ifa_list;
if (ifa && ifa->ifa_local) {
if (mvif->arp_offload_ip != ifa->ifa_local) {
memcpy(src_ip, &ifa->ifa_local, 4);
if (!ath6kl_wmi_set_arp_offload_ip_cmd(
ar->wmi, mvif->fw_vif_idx,
src_ip)) {
mvif->arp_offload_ip =
ifa->ifa_local;
ath6kl_dbg(ATH6KL_DBG_WOWLAN,
"%s: enable %s arp offload %d.%d.%d.%d\n",
__func__,
mvif->ndev->name,
src_ip[0], src_ip[1],
src_ip[2], src_ip[3]);
}
}
} else if (mvif->arp_offload_ip != 0) {
if (!ath6kl_wmi_set_arp_offload_ip_cmd(
ar->wmi, mvif->fw_vif_idx,
src_ip)) {
mvif->arp_offload_ip = 0;
ath6kl_dbg(ATH6KL_DBG_WOWLAN,
"%s: disable %s arp offload\n",
__func__,
mvif->ndev->name);
}
}
}
}
clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_HOST_MODE_ASLEEP);
if (ret)
return ret;
left = wait_event_interruptible_timeout(ar->event_wq,
test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
WMI_TIMEOUT);
if (left == 0) {
ath6kl_warn("timeout, didn't get host sleep cmd "
"processed event\n");
ret = -ETIMEDOUT;
} else if (left < 0) {
ath6kl_warn("error while waiting for host sleep cmd "
"processed event %d\n", left);
ret = left;
}
if (ar->tx_pending[ar->ctrl_ep]) {
left = wait_event_interruptible_timeout(ar->event_wq,
ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
if (left == 0) {
ath6kl_warn("clear wmi ctrl data timeout\n");
ret = -ETIMEDOUT;
} else if (left < 0) {
ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
ret = left;
}
}
#ifdef CONFIG_ANDROID
if (ret)
ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_HOST_MODE_AWAKE);
#endif
return ret;
}
static int ath6kl_wow_resume(struct ath6kl *ar)
{
struct ath6kl_vif *vif;
int ret;
vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_HOST_MODE_AWAKE);
/* Sync replay_counter back to the user. */
if (!ret) {
if ((vif->nw_type == INFRA_NETWORK) &&
test_bit(CONNECTED, &vif->flags) &&
(vif->ar->last_wow_fliter &
WOW_FILTER_OPTION_8021X_4WAYHS) &&
(vif->auth_mode == WPA2_AUTH_CCKM ||
vif->auth_mode == WPA2_PSK_AUTH ||
vif->auth_mode == WPA_AUTH_CCKM ||
vif->auth_mode == WPA_PSK_AUTH))
ret = ath6kl_wmi_get_gtk_offload(ar->wmi,
vif->fw_vif_idx);
}
return ret;
}
#ifdef CONFIG_ANDROID
static irqreturn_t ath6kl_wow_irq(int irq, void *dev_id)
{
struct rttm_context *prttm = NULL;
prttm = DEV_GETRTT_HDL();
if ((prttm) && (prttm->rttdhclkcal_active)) {
struct timespec ts;
getnstimeofday(&ts);
prttm->rttd2h2_clk.tabs_h2[prttm->dhclkcal_index].sec =
ts.tv_sec;
prttm->rttd2h2_clk.tabs_h2[prttm->dhclkcal_index].nsec =
ts.tv_nsec;
prttm->dhclkcal_index++;
}
return IRQ_HANDLED;
}
#endif
static bool ath6kl_cfg80211_need_suspend(struct ath6kl *ar, u32 *suspend_vif)
{
struct ath6kl_vif *vif;
int i;
vif = ath6kl_vif_first(ar);
if (!vif)
return false;
if (!test_bit(WMI_READY, &ar->flag)) {
ath6kl_err("deepsleep failed as wmi is not ready\n");
return false;
}
/*
* If one of all virtual interfaces is AP mode then
* force to awake.
*/
*suspend_vif = 0;
for (i = 0; i < ar->vif_max; i++) {
vif = ath6kl_get_vif_by_index(ar, i);
if (vif) {
#ifdef CE_SUPPORT
if (vif->nw_type != AP_NETWORK)
*suspend_vif |= (1 << i);
#else
if (vif->nw_type == AP_NETWORK) {
*suspend_vif = 0;
return false;
}
*suspend_vif |= (1 << i);
#endif
}
}
return true;
}
static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
{
struct ath6kl_vif *vif;
int i, ret = 0, left;
u32 need_suspend_vif = 0;
if (!ath6kl_cfg80211_need_suspend(ar, &need_suspend_vif))
return -EOPNOTSUPP;
ath6kl_dbg(ATH6KL_DBG_SUSPEND |
ATH6KL_DBG_EXT_AUTOPM,
"deep sleep suspend, need_suspend_vif 0x%x\n",
need_suspend_vif);
#ifdef CONFIG_ANDROID
if (ar->wow_irq) {
if (disable_irq_wake(ar->wow_irq))
ath6kl_err("Couldn't disable hostwake IRQ wakeup mode\n");
free_irq(ar->wow_irq, ar);
}
#endif
for (i = 0; i < ar->vif_max; i++) {
if (need_suspend_vif & (1 << i)) {
vif = ath6kl_get_vif_by_index(ar, i);
if (vif) {
clear_bit(WLAN_ENABLED, &vif->flags);
netif_dormant_on(vif->ndev);
set_bit(DORMANT, &vif->flags);
}
}
}
#ifdef USB_AUTO_SUSPEND
/*
* After McK3.x, we won't have credit full problem as before,
* so we could move ATH6KL_STATE_DEEPSLEEP after stop_all()
*/
ath6kl_cfg80211_stop_all(ar);
spin_lock_bh(&ar->state_lock);
ar->state = ATH6KL_STATE_DEEPSLEEP;
spin_unlock_bh(&ar->state_lock);
#else
spin_lock_bh(&ar->state_lock);
ar->state = ATH6KL_STATE_DEEPSLEEP;
spin_unlock_bh(&ar->state_lock);
ath6kl_cfg80211_stop_all(ar);
#endif
/*
* Flush data packets and wait for all control packets
* to be cleared in TX path before deep sleep suspend.
*/
ath6kl_tx_data_cleanup(ar);
if (ar->tx_pending[ar->ctrl_ep]) {
left = wait_event_interruptible_timeout(ar->event_wq,
ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
if (left == 0) {
ath6kl_warn("clear wmi ctrl data timeout txpend %d\n",
ar->tx_pending[ar->ctrl_ep]);
ret = -ETIMEDOUT;
} else if (left < 0) {
ath6kl_warn("clear wmi ctrl data failed:%d tx_pend=%d\n",
left, ar->tx_pending[ar->ctrl_ep]);
ret = left;
}
}
return ret;
}
static char *_get_suspend_mode_string(enum ath6kl_cfg_suspend_mode mode)
{
if (mode == ATH6KL_CFG_SUSPEND_DEEPSLEEP)
return "DEEPSLEEP";
else if (mode == ATH6KL_CFG_SUSPEND_CUTPOWER)
return "CUTPOWER";
else if (mode == ATH6KL_CFG_SUSPEND_WOW)
return "WOW";
return "UNKNOWN";
}
static char *_get_suspend_stat_string(enum ath6kl_state state)
{
if (state == ATH6KL_STATE_OFF)
return "OFF";
else if (state == ATH6KL_STATE_ON)
return "ON";
else if (state == ATH6KL_STATE_DEEPSLEEP)
return "DEEPSLEEP";
else if (state == ATH6KL_STATE_CUTPOWER)
return "CUTPOWER";
else if (state == ATH6KL_STATE_WOW)
return "WOW";
else if (state == ATH6KL_STATE_PRE_SUSPEND)
return "PRE-SUSPEND";
else if (state == ATH6KL_STATE_PRE_SUSPEND_DEEPSLEEP)
return "PRE-DEEPSLEEP";
return "UNKNOWN";
}
int ath6kl_cfg80211_suspend(struct ath6kl *ar,
enum ath6kl_cfg_suspend_mode mode,
struct cfg80211_wowlan *wow)
{
int ret;
u32 need_suspend_vif = 0;
/* make sure no AP mode at any vif*/
if (ath6kl_cfg80211_need_suspend(ar, &need_suspend_vif)
&& ar->get_wow_pattern == true)
mode = ATH6KL_CFG_SUSPEND_WOW;
ath6kl_dbg(ATH6KL_DBG_SUSPEND |
ATH6KL_DBG_EXT_AUTOPM,
"Start suspend mode %s, ar->state %s\n",
_get_suspend_mode_string(mode),
_get_suspend_stat_string(ar->state));
switch (mode) {
case ATH6KL_CFG_SUSPEND_WOW:
/* Flush all non control pkts in TX path */
ath6kl_tx_data_cleanup(ar);
#ifdef USB_AUTO_SUSPEND
ar->state = ATH6KL_STATE_PRE_SUSPEND;
#endif
ret = ath6kl_wow_suspend(ar, wow);
if (ret) {
ath6kl_err("wow suspend failed: %d\n", ret);
#ifdef USB_AUTO_SUSPEND
ar->state = ATH6KL_STATE_WOW;
#endif
return ret;
}
spin_lock_bh(&ar->state_lock);
ar->state = ATH6KL_STATE_WOW;
spin_unlock_bh(&ar->state_lock);
break;
case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
if (ar->state == ATH6KL_STATE_DEEPSLEEP)
break;
#ifdef USB_AUTO_SUSPEND
ar->state = ATH6KL_STATE_PRE_SUSPEND_DEEPSLEEP;
#endif
ret = ath6kl_cfg80211_deepsleep_suspend(ar);
if (ret) {
if (ret != -ENOTSUPP)
ath6kl_err("deepsleep suspend failed:"
" %d\n", ret);
return ret;
}
break;
case ATH6KL_CFG_SUSPEND_CUTPOWER:
#ifdef CONFIG_ANDROID
if (ar->wow_irq) {
if (disable_irq_wake(ar->wow_irq))
ath6kl_err("Couldn't disable hostwake IRQ wakeup mode\n");
free_irq(ar->wow_irq, ar);
}
#endif
ath6kl_cfg80211_stop_all(ar);
if (ar->state == ATH6KL_STATE_OFF) {
ath6kl_dbg(ATH6KL_DBG_SUSPEND,
"suspend hw off, no action for cutpower\n");
break;
}
ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
ret = ath6kl_init_hw_stop(ar);
if (ret) {
ath6kl_warn("failed to stop hw during suspend: %d\n",
ret);
}
ar->state = ATH6KL_STATE_CUTPOWER;
break;
default:
break;
}
ath6kl_dbg(ATH6KL_DBG_SUSPEND |
ATH6KL_DBG_EXT_AUTOPM,
"End suspend mode %s, ar->state %s\n",
_get_suspend_mode_string(mode),
_get_suspend_stat_string(ar->state));
return 0;
}
int ath6kl_cfg80211_resume(struct ath6kl *ar)
{
int i, ret;
struct ath6kl_vif *vif;
ath6kl_dbg(ATH6KL_DBG_SUSPEND |
ATH6KL_DBG_EXT_AUTOPM,
"Start resume, ar->state %s\n",
_get_suspend_stat_string(ar->state));
switch (ar->state) {
case ATH6KL_STATE_WOW:
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_timeout(&ar->wake_lock, 3*HZ);
#else
/* TODO: What should I do if there is no wake lock?? */
#endif
spin_lock_bh(&ar->state_lock);
ar->state = ATH6KL_STATE_ON;
spin_unlock_bh(&ar->state_lock);
ret = ath6kl_wow_resume(ar);
if (ret) {
ath6kl_warn("wow mode resume failed: %d\n", ret);
return ret;
}
#ifdef USB_AUTO_SUSPEND
spin_lock_bh(&ar->usb_pm_lock);
ath6kl_auto_pm_wakeup_resume(ar);
spin_unlock_bh(&ar->usb_pm_lock);
#endif
break;
case ATH6KL_STATE_DEEPSLEEP:
spin_lock_bh(&ar->state_lock);
ar->state = ATH6KL_STATE_ON;
spin_unlock_bh(&ar->state_lock);
#ifdef USB_AUTO_SUSPEND
spin_lock_bh(&ar->usb_pm_lock);
ath6kl_auto_pm_wakeup_resume(ar);
spin_unlock_bh(&ar->usb_pm_lock);
#endif
#ifdef CONFIG_ANDROID
if (ar->wow_irq) {
int ret;
ret = request_irq(ar->wow_irq, ath6kl_wow_irq,
IRQF_SHARED | IRQF_TRIGGER_RISING,
"ar6000" "sdiowakeup", ar);
if (!ret) {
ret = enable_irq_wake(ar->wow_irq);
if (ret < 0) {
ath6kl_err("Couldn't enable WoW IRQ as wakeup interrupt");
return ret;
}
ath6kl_info("ath6kl: WoW IRQ %d\n",
ar->wow_irq);
}
}
#endif
for (i = 0; i < ar->vif_max; i++) {
vif = ath6kl_get_vif_by_index(ar, i);
if (vif) {
if (test_bit(DORMANT, &vif->flags)) {
set_bit(WLAN_ENABLED, &vif->flags);
netif_dormant_off(vif->ndev);
clear_bit(DORMANT, &vif->flags);
}
/* restore previous power mode */
if (vif->last_pwr_mode != vif->saved_pwr_mode) {
if (ath6kl_wmi_powermode_cmd(ar->wmi, i,
vif->saved_pwr_mode) != 0) {
ath6kl_err("wmi powermode command failed during resume\n");
}
}
}
}
break;
case ATH6KL_STATE_CUTPOWER:
ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
#ifdef CONFIG_ANDROID
if (ar->wow_irq) {
int ret;
ret = request_irq(ar->wow_irq, ath6kl_wow_irq,
IRQF_SHARED | IRQF_TRIGGER_RISING,
"ar6000" "sdiowakeup", ar);
if (!ret) {
ret = enable_irq_wake(ar->wow_irq);
if (ret < 0) {
ath6kl_err("Couldn't enable WoW IRQ as wakeup interrupt");
return ret;
}
ath6kl_info("ath6kl: WoW IRQ %d\n",
ar->wow_irq);
}
}
#endif
ret = ath6kl_init_hw_start(ar);
if (ret) {
ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
return ret;
}
break;
default:
break;
}
ath6kl_dbg(ATH6KL_DBG_SUSPEND |
ATH6KL_DBG_EXT_AUTOPM,
"End resume, ar->state %s\n",
_get_suspend_stat_string(ar->state));
return 0;
}
#ifdef CONFIG_PM
/* hif layer decides what suspend mode to use */
static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
struct cfg80211_wowlan *wow)
{
struct ath6kl *ar = wiphy_priv(wiphy);
#if defined(USB_AUTO_SUSPEND)
if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode))
return 0;
#endif
return ath6kl_hif_suspend(ar, wow);
}
static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
{
struct ath6kl *ar = wiphy_priv(wiphy);
if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode))
return 0;
return ath6kl_hif_resume(ar);
}
/*
* FIXME: WOW suspend mode is selected if the host sdio controller supports
* both sdio irq wake up and keep power. The target pulls sdio data line to
* wake up the host when WOW pattern matches. This causes sdio irq handler
* is being called in the host side which internally hits ath6kl's RX path.
*
* Since sdio interrupt is not disabled, RX path executes even before
* the host executes the actual resume operation from PM module.
*
* In the current scenario, WOW resume should happen before start processing
* any data from the target. So It's required to perform WOW resume in RX path.
* Ideally we should perform WOW resume only in the actual platform
* resume path. This area needs bit rework to avoid WOW resume in RX path.
*
* ath6kl_check_wow_status() is called from ath6kl_rx().
*/
void ath6kl_check_wow_status(struct ath6kl *ar)
{
if (ar->state == ATH6KL_STATE_WOW)
ath6kl_cfg80211_resume(ar);
}
#else
void ath6kl_check_wow_status(struct ath6kl *ar)
{
}
#endif
#ifndef CFG80211_NO_SET_CHAN_OPERATION
static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type)
{
struct ath6kl_vif *vif;
if (dev == NULL)
return -EBUSY;
vif = netdev_priv(dev);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&vif->ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u channel_type=%d\n",
__func__, chan->center_freq, chan->hw_value, channel_type);
vif->next_chan = chan->center_freq;
if (ath6kl_mod_debug_quirks(vif->ar,
ATH6KL_MODULE_ENABLE_P2P_CHANMODE)) {
if (channel_type == NL80211_CHAN_HT40PLUS)
vif->next_chan_type = ATH6KL_CHAN_TYPE_HT40PLUS;
else if (channel_type == NL80211_CHAN_HT40MINUS)
vif->next_chan_type = ATH6KL_CHAN_TYPE_HT40MINUS;
else if (channel_type == NL80211_CHAN_HT20)
vif->next_chan_type = ATH6KL_CHAN_TYPE_HT20;
else
vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE;
} else
vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE;
up(&vif->ar->sem);
return 0;
}
#endif
static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
const u8 *ies, size_t ies_len)
{
struct ath6kl *ar = vif->ar;
const u8 *pos;
u8 *buf = NULL;
size_t len = 0;
int ret;
/*
* Filter out P2P/WFD IE(s) since they will be included depending on
* the Probe Request frame in ath6kl_wmi_send_go_probe_response_cmd().
*/
if (ies && ies_len) {
buf = kmalloc(ies_len, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
pos = ies;
while (pos + 1 < ies + ies_len) {
if (pos + 2 + pos[1] > ies + ies_len)
break;
if ((!ath6kl_is_p2p_ie(pos)) &&
(!ath6kl_is_wfd_ie(pos))) {
memcpy(buf + len, pos, 2 + pos[1]);
len += 2 + pos[1];
}
pos += 2 + pos[1];
}
}
ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_PROBE_RESP, buf, len);
kfree(buf);
return ret;
}
static int ath6kl_set_uapsd(struct wiphy *wiphy, struct net_device *dev,
const u8 *ies, int ies_len)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
const u8 *pos;
int uapsd = 0; /* default is OFF */
bool found = false;
if (ies && ies_len) {
pos = ies;
while (pos + 1 < ies + ies_len) {
if (pos + 2 + pos[1] > ies + ies_len)
break;
if (ath6kl_is_wmm_ie(pos)) {
found = true;
if (pos[8] & 0x80) /* QOS-INFO, BIT(7) */
uapsd = 1;
break;
}
pos += 2 + pos[1];
}
}
if (!found)
clear_bit(WMM_ENABLED, &vif->flags);
return ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, uapsd);
}
#ifdef PMF_SUPPORT
static int ath6kl_set_akm_suites(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme)
{
const u8 *pos, *ies = sme->ie;
int ies_len = sme->ie_len;
int ret = 0;
if (ies && ies_len) {
pos = ies;
while (pos + 1 < ies + ies_len) {
if (pos + 2 + pos[1] > ies + ies_len)
break;
if (ath6kl_is_rsn_ie(pos)) {
int offset, i;
/*
* VER [2] +
* 1 GROUP Cipher [4] +
* n Pairwise Ciphers [2 + 4*n] +
* n AKM suites [2 + 4*n] +
* RSN-Capabilities [2] +
* n PMKID Count/List [2 + 16*n] +
* 1 Group Mgmt Cipher [4]
*/
if (pos[1] <= 18)
break;
/* Get/Set AKM value */
offset = 1 + 1 + 2 + 4 + 2 + (pos[8] * 4);
if (pos[offset]) {
sme->crypto.n_akm_suites = pos[offset];
sme->crypto.akm_suites[0]
= pos[offset + 2];
for (i = 1; i < 4; i++) {
sme->crypto.akm_suites[0]
= sme->crypto.akm_suites[0]
<< 8;
sme->crypto.akm_suites[0]
+= pos[offset + 2 + i];
}
break;
}
}
pos += 2 + pos[1];
}
}
return ret;
}
#endif
static int ath6kl_set_rsn_cap(struct wiphy *wiphy, struct net_device *dev,
const u8 *ies, int ies_len)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
const u8 *pos;
u16 rsn_cap = ATH6KL_RSN_CAP_NULLCONF;
int ret = 0;
if (ies && ies_len) {
pos = ies;
while (pos + 1 < ies + ies_len) {
if (pos + 2 + pos[1] > ies + ies_len)
break;
if (ath6kl_is_rsn_ie(pos)) {
int offset;
/*
* VER[2] +
* 1 GROUP Cipher[4] +
* 1 Pairwise Cipher[2+4] +
* 1 Auth suit[2+4] = 18
*/
if (pos[1] <= 18)
break;
/* Get RSN-CAP offset */
offset = 1 + 1 + 2 + 4 + 2 + (pos[8] * 4);
offset += 2 + (pos[offset] * 4);
if (offset > (pos[1] + 2))
break;
rsn_cap = (pos[offset] |
(pos[offset + 1] << 8));
break;
}
pos += 2 + pos[1];
}
}
vif->last_rsn_cap = ATH6KL_RSN_CAP_NULLCONF;
if (rsn_cap != ATH6KL_RSN_CAP_NULLCONF) {
ret = ath6kl_wmi_get_rsn_cap(ar->wmi, vif->fw_vif_idx);
if (!ret)
ret = ath6kl_wmi_set_rsn_cap(ar->wmi,
vif->fw_vif_idx, rsn_cap);
}
return ret;
}
static u16 _ath6kl_ap_get_channel(struct ath6kl_vif *vif,
struct ath6kl_beacon_parameters *info)
{
u16 chan = 0;
/* The wpa_supplicant will set-channel first then start AP. */
#ifdef CFG80211_NO_SET_CHAN_OPERATION
/*
* New cfg80211 implementation will cache channel information and
* setup it when start AP operation.
* Here get the channel information from the parameter of start AP
* operation then set to driver.
*/
if (ath6kl_mod_debug_quirks(vif->ar,
ATH6KL_MODULE_ENABLE_P2P_CHANMODE)) {
if (info->channel_type == NL80211_CHAN_HT40PLUS)
vif->next_chan_type = ATH6KL_CHAN_TYPE_HT40PLUS;
else if (info->channel_type == NL80211_CHAN_HT40MINUS)
vif->next_chan_type = ATH6KL_CHAN_TYPE_HT40MINUS;
else if (info->channel_type == NL80211_CHAN_HT20)
vif->next_chan_type = ATH6KL_CHAN_TYPE_HT20;
else
vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE;
} else
vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE;
WARN_ON(!info->channel);
if (info->channel)
vif->next_chan = info->channel->center_freq;
else {
vif->next_chan = 0;
vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE;
}
#endif
/*
* The driver need to cache channel information in the set-channel
* operation for old cfg80211 implemenation.
* Here just report the cached channel information.
*/
chan = vif->next_chan;
if (vif->next_chan_type != ATH6KL_CHAN_TYPE_NONE) {
enum wmi_connect_ap_channel_type chan_type =
AP_CHANNEL_TYPE_NONE;
if (vif->next_chan_type == ATH6KL_CHAN_TYPE_HT40PLUS)
chan_type = AP_CHANNEL_TYPE_HT40PLUS;
else if (vif->next_chan_type == ATH6KL_CHAN_TYPE_HT40MINUS)
chan_type = AP_CHANNEL_TYPE_HT40MINUS;
else if (vif->next_chan_type == ATH6KL_CHAN_TYPE_HT20)
chan_type = AP_CHANNEL_TYPE_HT20;
chan |= (chan_type << WMI_CONNECT_AP_CHAN_SELECT_OFFSET);
}
/* Overwrite the channel if AP recommand channel turns on */
chan = ath6kl_ap_rc_get(vif, chan);
return chan;
}
#ifdef NL80211_CMD_SET_AP_MAC_ACL
static void _ath6kl_acl_config(struct ath6kl_vif *vif,
const struct cfg80211_acl_data *acl)
{
enum ap_acl_mode mode;
u8 mac_addr[ETH_ALEN];
int i;
/* Remove all ACL address first. */
ath6kl_ap_acl_config_mac_list_reset(vif);
/* Disable ACL. */
ath6kl_ap_acl_config_policy(vif, AP_ACL_MODE_DISABLE);
/* Turn-on if need. */
if (acl->n_acl_entries == 0)
mode = AP_ACL_MODE_DISABLE;
else if (acl->acl_policy == NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED)
mode = AP_ACL_MODE_ALLOW;
else if (acl->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
mode = AP_ACL_MODE_DENY;
else {
WARN_ON(1);
mode = AP_ACL_MODE_DISABLE;
}
ath6kl_ap_acl_config_policy(vif, mode);
/* Add new ACL address. */
for (i = 0; i < acl->n_acl_entries; i++) {
if (i < AP_ACL_SIZE) {
memcpy(mac_addr, acl->mac_addrs[i].addr, ETH_ALEN);
ath6kl_ap_acl_config_mac_list(vif, mac_addr, false);
} else {
ath6kl_err("Wrong ACL number %d\n", acl->n_acl_entries);
break;
}
}
return;
}
#endif
static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
struct ath6kl_beacon_parameters *info, bool add)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
struct ieee80211_mgmt *mgmt;
u8 *ies;
int ies_len;
struct wmi_connect_cmd p;
int res;
int i;
u16 chan;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (vif->next_mode != AP_NETWORK) {
up(&ar->sem);
return -EOPNOTSUPP;
}
if (info->beacon_ies) {
u8 *beacon_ies = (u8 *)info->beacon_ies;
size_t beacon_ies_len = info->beacon_ies_len;
ath6kl_p2p_ps_user_app_ie(vif->p2p_ps_info_ctx,
WMI_FRAME_BEACON,
&beacon_ies,
&beacon_ies_len);
res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_BEACON,
beacon_ies,
beacon_ies_len);
if (res) {
up(&ar->sem);
return res;
}
}
/*
* IOT : Set default DTIM period to 1 .
* wpa_supplicant default is 2 and target default is 5.
*/
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: set DTIM from %d to 1.\n",
__func__, info->dtim_period);
ath6kl_wmi_set_dtim_cmd(ar->wmi, vif->fw_vif_idx, 1);
if (info->proberesp_ies) {
res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
info->proberesp_ies_len);
if (res) {
up(&ar->sem);
return res;
}
}
if (info->assocresp_ies) {
res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_ASSOC_RESP,
info->assocresp_ies,
info->assocresp_ies_len);
if (res) {
up(&ar->sem);
return res;
}
}
if (!add) {
up(&ar->sem);
return 0;
}
#ifdef NL80211_CMD_SET_AP_MAC_ACL
/* Config ACL */
if (info->acl)
_ath6kl_acl_config(vif, info->acl);
#endif
/* Config keep-alive */
if (info->inactivity_timeout)
ath6kl_ap_keepalive_config_by_supp(vif,
info->inactivity_timeout);
/* Turn-on/off uAPS. */
if (ath6kl_set_uapsd(wiphy, dev, info->tail, info->tail_len)) {
up(&ar->sem);
return -EIO;
}
/* Update RSN Capabilities. */
if (ath6kl_set_rsn_cap(wiphy, dev, info->tail, info->tail_len)) {
up(&ar->sem);
return -EIO;
}
/* Turn off power saving mode, if the first interface is AP mode */
if (vif == ath6kl_vif_first(ar)) {
if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
MAX_PERF_POWER)) {
up(&ar->sem);
return -EIO;
}
}
vif->ap_mode_bkey.valid = false;
if (info->beacon_interval) {
res = ath6kl_wmi_set_beacon_interval_cmd(ar->wmi,
vif->fw_vif_idx, info->beacon_interval);
if (res) {
up(&ar->sem);
return res;
}
}
if (info->head == NULL) {
up(&ar->sem);
return -EINVAL;
}
mgmt = (struct ieee80211_mgmt *) info->head;
ies = mgmt->u.beacon.variable;
if (ies > info->head + info->head_len) {
up(&ar->sem);
return -EINVAL;
}
ies_len = info->head + info->head_len - ies;
if (info->ssid == NULL) {
up(&ar->sem);
return -EINVAL;
}
memcpy(vif->ssid, info->ssid, info->ssid_len);
vif->ssid_len = info->ssid_len;
if (info->hidden_ssid == NL80211_HIDDEN_SSID_ZERO_LEN) {
up(&ar->sem);
return -ENOTSUPP;
} else if (info->hidden_ssid == NL80211_HIDDEN_SSID_ZERO_CONTENTS) {
res = ath6kl_wmi_set_hidden_ssid_cmd(
ar->wmi, vif->fw_vif_idx, 1);
} else {
res = ath6kl_wmi_set_hidden_ssid_cmd(
ar->wmi, vif->fw_vif_idx, 0);
}
if (res) {
up(&ar->sem);
return res;
}
res = ath6kl_set_auth_type(vif, info->auth_type);
if (res) {
up(&ar->sem);
return res;
}
memset(&p, 0, sizeof(p));
for (i = 0; i < info->crypto.n_akm_suites; i++) {
switch (info->crypto.akm_suites[i]) {
case WLAN_AKM_SUITE_8021X:
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
p.auth_mode |= WPA_AUTH;
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
p.auth_mode |= WPA2_AUTH;
break;
case WLAN_AKM_SUITE_PSK:
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
p.auth_mode |= WPA_PSK_AUTH;
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
p.auth_mode |= WPA2_PSK_AUTH;
break;
}
}
if (p.auth_mode == 0)
p.auth_mode = NONE_AUTH;
vif->auth_mode = p.auth_mode;
for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
switch (info->crypto.ciphers_pairwise[i]) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
p.prwise_crypto_type |= WEP_CRYPT;
break;
case WLAN_CIPHER_SUITE_TKIP:
p.prwise_crypto_type |= TKIP_CRYPT;
break;
case WLAN_CIPHER_SUITE_CCMP:
p.prwise_crypto_type |= AES_CRYPT;
break;
case WLAN_CIPHER_SUITE_SMS4:
p.prwise_crypto_type |= WAPI_CRYPT;
break;
}
}
if (p.prwise_crypto_type == 0) {
p.prwise_crypto_type = NONE_CRYPT;
ath6kl_set_cipher(vif, 0, true);
} else if (info->crypto.n_ciphers_pairwise == 1)
ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
switch (info->crypto.cipher_group) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
p.grp_crypto_type = WEP_CRYPT;
break;
case WLAN_CIPHER_SUITE_TKIP:
p.grp_crypto_type = TKIP_CRYPT;
break;
case WLAN_CIPHER_SUITE_CCMP:
p.grp_crypto_type = AES_CRYPT;
break;
case WLAN_CIPHER_SUITE_SMS4:
p.grp_crypto_type = WAPI_CRYPT;
break;
default:
p.grp_crypto_type = NONE_CRYPT;
break;
}
ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
p.nw_type = AP_NETWORK;
vif->nw_type = vif->next_mode;
p.ssid_len = vif->ssid_len;
memcpy(p.ssid, vif->ssid, vif->ssid_len);
p.dot11_auth_mode = vif->dot11_auth_mode;
chan = _ath6kl_ap_get_channel(vif, info);
p.ch = cpu_to_le16(chan);
res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
if (res < 0) {
up(&ar->sem);
return res;
}
up(&ar->sem);
return 0;
}
#ifdef NL80211_CMD_START_STOP_AP
static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ap_settings *settings)
{
struct ath6kl_beacon_parameters *info;
int ret = 0;
info = kzalloc(sizeof(struct ath6kl_beacon_parameters), GFP_ATOMIC);
if (info == NULL)
return -ENOMEM;
/* Fetch to local setting */
info->beacon_interval = settings->beacon_interval;
info->dtim_period = settings->dtim_period;
info->ssid = settings->ssid;
info->ssid_len = settings->ssid_len;
info->hidden_ssid = settings->hidden_ssid;
memcpy(&info->crypto,
&settings->crypto,
sizeof(struct cfg80211_crypto_settings));
info->privacy = settings->privacy;
info->auth_type = settings->auth_type;
info->inactivity_timeout = settings->inactivity_timeout;
#ifdef CFG80211_NO_SET_CHAN_OPERATION
#ifdef CFG80211_NEW_CHAN_DEFINITION
info->channel = settings->chandef.chan;
info->channel_type = cfg80211_get_chandef_type(&settings->chandef);
#else
info->channel = settings->channel;
info->channel_type = settings->channel_type;
#endif
#endif
info->p2p_ctwindow = 0;
info->p2p_opp_ps = false;
#ifdef NL80211_CMD_SET_AP_MAC_ACL
info->acl = settings->acl;
#endif
/*
* The target will take care DFS behavior and the host just need to
* report events back to the user.
*/
info->radar_required = false;
info->head = settings->beacon.head;
info->tail = settings->beacon.tail;
info->head_len = settings->beacon.head_len;
info->tail_len = settings->beacon.tail_len;
info->beacon_ies = settings->beacon.beacon_ies;
info->beacon_ies_len = settings->beacon.beacon_ies_len;
info->proberesp_ies = settings->beacon.proberesp_ies;
info->proberesp_ies_len = settings->beacon.proberesp_ies_len;
info->assocresp_ies = settings->beacon.assocresp_ies;
info->assocresp_ies_len = settings->beacon.assocresp_ies_len;
info->probe_resp = settings->beacon.probe_resp;
info->probe_resp_len = settings->beacon.probe_resp_len;
ret = ath6kl_ap_beacon(wiphy, dev, info, true);
kfree(info);
return ret;
}
static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_beacon_data *settings)
{
struct ath6kl_beacon_parameters *info;
int ret = 0;
info = kzalloc(sizeof(struct ath6kl_beacon_parameters), GFP_ATOMIC);
if (info == NULL)
return -ENOMEM;
/* Fetch to local setting */
info->head = settings->head;
info->tail = settings->tail;
info->head_len = settings->head_len;
info->tail_len = settings->tail_len;
info->beacon_ies = settings->beacon_ies;
info->beacon_ies_len = settings->beacon_ies_len;
info->proberesp_ies = settings->proberesp_ies;
info->proberesp_ies_len = settings->proberesp_ies_len;
info->assocresp_ies = settings->assocresp_ies;
info->assocresp_ies_len = settings->assocresp_ies_len;
info->probe_resp = settings->probe_resp;
info->probe_resp_len = settings->probe_resp_len;
ret = ath6kl_ap_beacon(wiphy, dev, info, false);
kfree(info);
return ret;
}
#else
static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *settings)
{
struct ath6kl_beacon_parameters *info;
int ret = 0;
info = kzalloc(sizeof(struct ath6kl_beacon_parameters), GFP_ATOMIC);
if (info == NULL)
return -ENOMEM;
/* Fetch to local setting */
info->beacon_interval = settings->interval;
info->dtim_period = settings->dtim_period;
info->ssid = settings->ssid;
info->ssid_len = settings->ssid_len;
info->hidden_ssid = settings->hidden_ssid;
memcpy(&info->crypto,
&settings->crypto,
sizeof(struct cfg80211_crypto_settings));
info->privacy = settings->privacy;
info->auth_type = settings->auth_type;
info->head = settings->head;
info->tail = settings->tail;
info->head_len = settings->head_len;
info->tail_len = settings->tail_len;
info->beacon_ies = settings->beacon_ies;
info->beacon_ies_len = settings->beacon_ies_len;
info->proberesp_ies = settings->proberesp_ies;
info->proberesp_ies_len = settings->proberesp_ies_len;
info->assocresp_ies = settings->assocresp_ies;
info->assocresp_ies_len = settings->assocresp_ies_len;
info->probe_resp = settings->probe_resp;
info->probe_resp_len = settings->probe_resp_len;
ret = ath6kl_ap_beacon(wiphy, dev, info, true);
kfree(info);
return ret;
}
static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *settings)
{
struct ath6kl_beacon_parameters *info;
int ret = 0;
info = kzalloc(sizeof(struct ath6kl_beacon_parameters), GFP_ATOMIC);
if (info == NULL)
return -ENOMEM;
/* Fetch to local setting */
info->head = settings->head;
info->tail = settings->tail;
info->head_len = settings->head_len;
info->tail_len = settings->tail_len;
info->beacon_ies = settings->beacon_ies;
info->beacon_ies_len = settings->beacon_ies_len;
info->proberesp_ies = settings->proberesp_ies;
info->proberesp_ies_len = settings->proberesp_ies_len;
info->assocresp_ies = settings->assocresp_ies;
info->assocresp_ies_len = settings->assocresp_ies_len;
info->probe_resp = settings->probe_resp;
info->probe_resp_len = settings->probe_resp_len;
ret = ath6kl_ap_beacon(wiphy, dev, info, false);
kfree(info);
return ret;
}
#endif
static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (vif->nw_type != AP_NETWORK) {
up(&ar->sem);
return -EOPNOTSUPP;
}
if (!test_bit(CONNECTED, &vif->flags)) {
up(&ar->sem);
return -ENOTCONN;
}
/* Back to origional value. */
if (vif->last_rsn_cap != ATH6KL_RSN_CAP_NULLCONF)
ath6kl_wmi_set_rsn_cap(ar->wmi, vif->fw_vif_idx,
vif->last_rsn_cap);
/* Stop keep-alive. */
ath6kl_ap_keepalive_stop(vif);
/* Stop ACL. */
ath6kl_ap_acl_stop(vif);
/* Stop Admission-Control */
ath6kl_ap_admc_stop(vif);
ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
clear_bit(CONNECTED, &vif->flags);
ath6kl_judge_roam_parameter(vif, true);
ath6kl_switch_parameter_based_on_connection(vif, true);
up(&ar->sem);
return 0;
}
static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
const u8 *addr = mac ? mac : bcast_addr;
int ret;
struct ath6kl_sta *sta_conn = NULL;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (!is_broadcast_ether_addr(addr)) {
sta_conn = ath6kl_find_sta(vif, (u8 *)addr);
if (sta_conn == NULL) {
up(&ar->sem);
ret = 0;
return ret;
}
}
ret = ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
up(&ar->sem);
return ret;
}
static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_parameters *params)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
int ret = 0;
struct ath6kl_sta *sta_conn = NULL;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if (vif->nw_type != AP_NETWORK) {
up(&ar->sem);
return -EOPNOTSUPP;
}
/* Use this only for authorizing/unauthorizing a station */
if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) {
up(&ar->sem);
return -EOPNOTSUPP;
}
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
ret = ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
WMI_AP_MLME_AUTHORIZE, mac, 0);
up(&ar->sem);
return ret;
}
if (!is_broadcast_ether_addr(mac)) {
sta_conn = ath6kl_find_sta(vif, mac);
if (sta_conn == NULL) {
up(&ar->sem);
return ret;
}
}
ret = ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
WMI_AP_MLME_UNAUTHORIZE, mac, 0);
up(&ar->sem);
return ret;
}
static int _ath6kl_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie)
{
#define MAX_ROC_PERIOD \
(ATH6KL_ROC_MAX_PERIOD * HZ + HZ / ATH6KL_ROC_MAX_PERIOD)
#define MAX_SCAN_PERIOD (ATH6KL_SCAN_FG_MAX_PERIOD * HZ)
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
u32 id;
int ret;
long left;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
#ifdef USB_AUTO_SUSPEND
if (ar->autopm_turn_on) {
ar->autopm_defer_delay_change_cnt =
USB_SUSPEND_DEFER_DELAY_FOR_P2P;
ath6kl_hif_auto_pm_set_delay(ar, USB_SUSPEND_DELAY_MAX);
}
#endif
/* If already ongoing scan then wait it finish. */
if (vif->scan_req) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : Schedule a RoC but on-going Scan %x\n",
vif->last_roc_id);
wait_event_interruptible_timeout(ar->event_wq,
!vif->scan_req,
MAX_SCAN_PERIOD);
if (signal_pending(current)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : last scan not yet finish?\n");
up(&ar->sem);
return -EBUSY;
}
}
/* If already pending remain-on-channel then reject request. */
if (test_bit(ROC_PEND, &vif->flags)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : Receive duplicate ROC.\n");
up(&ar->sem);
return -EBUSY;
}
/* If ongoing remain-on-channel and wait it finish. */
if (test_bit(ROC_ONGOING, &vif->flags)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : Last RoC not yet finish, %x %d %d",
vif->last_roc_id,
((test_bit(ROC_PEND, &vif->flags)) ? 1 : 0),
((test_bit(ROC_ONGOING, &vif->flags)) ? 1 : 0));
set_bit(ROC_WAIT_EVENT, &vif->flags);
left = wait_event_interruptible_timeout(ar->event_wq,
!test_bit(ROC_ONGOING, &vif->flags),
MAX_ROC_PERIOD);
if (left == 0) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : wait ROC_WAIT_EVENT timeout\n");
clear_bit(ROC_WAIT_EVENT, &vif->flags);
clear_bit(ROC_ONGOING, &vif->flags);
}
if (signal_pending(current)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : last RoC not yet finish?\n");
up(&ar->sem);
return -EBUSY;
}
}
spin_lock_bh(&vif->if_lock);
id = ++vif->last_roc_id;
if (id == 0) {
/* Do not use 0 as the cookie value */
id = ++vif->last_roc_id;
}
*cookie = id;
spin_unlock_bh(&vif->if_lock);
/* Cache request channel and report to cfg80211 when target reject. */
vif->last_roc_channel = chan;
vif->last_roc_duration = duration;
set_bit(ROC_PEND, &vif->flags);
ret = ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
chan->center_freq, duration);
up(&ar->sem);
return ret;
#undef MAX_ROC_PERIOD
#undef MAX_SCAN_PERIOD
}
static int _ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
u64 cookie)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
int ret;
long left;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
#ifdef USB_AUTO_SUSPEND
if (ar->autopm_turn_on) {
ar->autopm_defer_delay_change_cnt =
USB_SUSPEND_DEFER_DELAY_FOR_P2P;
ath6kl_hif_auto_pm_set_delay(ar, USB_SUSPEND_DELAY_MAX);
}
#endif
/*
* RoC not yet start but be cancelled. Wait it started then cancel
* it by order to avoid wrong cookie report to supplicant.
*/
if (test_bit(ROC_PEND, &vif->flags)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : RoC not start but canceled %x\n",
vif->last_roc_id);
set_bit(ROC_WAIT_EVENT, &vif->flags);
left = wait_event_interruptible_timeout(ar->event_wq,
test_bit(ROC_ONGOING, &vif->flags),
WMI_TIMEOUT);
/*
* Another corner case is that last RoC not yet start & also
* be rejected by target, but be canceled. In this case,
* treat WMI_TIMEOUT as target reject then return.
*/
if (left == 0) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : wait ROC_WAIT_EVENT timeout, %lld %d\n",
cookie,
vif->last_roc_id);
clear_bit(ROC_WAIT_EVENT, &vif->flags);
clear_bit(ROC_PEND, &vif->flags);
up(&ar->sem);
return 0;
}
if (signal_pending(current)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : target did not respond\n");
up(&ar->sem);
return -EINTR;
}
}
spin_lock_bh(&vif->if_lock);
if (cookie != vif->last_roc_id) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : Cancel-RoC %llx but current is %x\n", cookie,
vif->last_roc_id);
spin_unlock_bh(&vif->if_lock);
up(&ar->sem);
return -ENOENT;
}
vif->last_cancel_roc_id = cookie;
set_bit(ROC_CANCEL_PEND, &vif->flags);
spin_unlock_bh(&vif->if_lock);
ret = ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
up(&ar->sem);
return ret;
}
static int _ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
u32 id;
u32 wmi_data_flags = 0;
const struct ieee80211_mgmt *mgmt;
int ret = 0;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return -ERESTARTSYS;
}
if ((ar->p2p) && !ar->p2p_compat &&
(ar->p2p_concurrent) &&
(vif->fw_vif_idx == (ar->vif_max - 1)) &&
!test_bit(ROC_ONGOING, &vif->flags)) {
if (ath6kl_p2p_is_p2p_frame(ar, buf, len)) {
ath6kl_dbg(ATH6KL_DBG_EXT_ROC,
"RoC : Channel closed, ignore action frame\n");
up(&ar->sem);
return -EINVAL;
}
}
id = vif->send_action_id++;
if (id == 0) {
/*
* 0 is a reserved value in the WMI command and shall not be
* used for the command.
*/
id = vif->send_action_id++;
}
*cookie = id;
/* AP mode Power saving processing */
if (vif->nw_type == AP_NETWORK) {
if (ath6kl_mgmt_powersave_ap(vif,
id,
chan->center_freq,
wait,
buf,
len,
no_cck,
dont_wait_for_ack,
&wmi_data_flags)) {
up(&ar->sem);
return ret;
}
}
mgmt = (const struct ieee80211_mgmt *) buf;
if (buf + len >= mgmt->u.probe_resp.variable &&
vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
ieee80211_is_probe_resp(mgmt->frame_control)) {
/*
* Send Probe Response frame in AP mode using a separate WMI
* command to allow the target to fill in the generic IEs.
*/
ret = ath6kl_wmi_send_go_probe_response_cmd(ar->wmi, vif,
buf, len,
chan->center_freq);
up(&ar->sem);
return ret;
}
ret = ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
chan->center_freq, wait,
buf, len);
up(&ar->sem);
return ret;
}
static void _ath6kl_mgmt_frame_register(struct wiphy *wiphy,
struct net_device *dev,
u16 frame_type, bool reg)
{
struct ath6kl_vif *vif = netdev_priv(dev);
if (!ath6kl_cfg80211_ready(vif))
return;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
__func__, frame_type, reg);
if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
/*
* Note: This notification callback is not allowed to sleep, so
* we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
* hardcode target to report Probe Request frames all the time.
*/
vif->probe_req_report = reg;
}
#ifdef CE_SUPPORT
if (frame_type == IEEE80211_STYPE_PROBE_RESP)
vif->probe_resp_report = reg;
#endif
}
#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV
#ifdef CFG80211_REMOVE_ROC_CHAN_TYPE
static int ath6kl_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
unsigned int duration,
u64 *cookie)
{
BUG_ON(!wdev->netdev);
return _ath6kl_remain_on_channel(wiphy,
wdev->netdev,
chan,
NL80211_CHAN_NO_HT,
duration,
cookie);
}
#else
static int ath6kl_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie)
{
BUG_ON(!wdev->netdev);
return _ath6kl_remain_on_channel(wiphy,
wdev->netdev,
chan,
channel_type,
duration,
cookie);
}
#endif
static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
BUG_ON(!wdev->netdev);
return _ath6kl_cancel_remain_on_channel(wiphy,
wdev->netdev,
cookie);
}
#ifdef CFG80211_REMOVE_ROC_CHAN_TYPE
static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie)
{
BUG_ON(!wdev->netdev);
return _ath6kl_mgmt_tx(wiphy,
wdev->netdev,
chan,
offchan,
NL80211_CHAN_NO_HT,
true,
wait,
buf,
len,
no_cck,
dont_wait_for_ack,
cookie);
}
#else
static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie)
{
BUG_ON(!wdev->netdev);
return _ath6kl_mgmt_tx(wiphy,
wdev->netdev,
chan,
offchan,
channel_type,
channel_type_valid,
wait,
buf,
len,
no_cck,
dont_wait_for_ack,
cookie);
}
#endif
static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
struct wireless_dev *wdev,
u16 frame_type, bool reg)
{
BUG_ON(!wdev->netdev);
return _ath6kl_mgmt_frame_register(wiphy,
wdev->netdev,
frame_type,
reg);
}
#else
static int ath6kl_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie)
{
return _ath6kl_remain_on_channel(wiphy,
dev,
chan,
channel_type,
duration,
cookie);
}
static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
u64 cookie)
{
return _ath6kl_cancel_remain_on_channel(wiphy,
dev,
cookie);
}
static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie)
{
return _ath6kl_mgmt_tx(wiphy,
dev,
chan,
offchan,
channel_type,
channel_type_valid,
wait,
buf,
len,
no_cck,
dont_wait_for_ack,
cookie);
}
static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
struct net_device *dev,
u16 frame_type, bool reg)
{
return _ath6kl_mgmt_frame_register(wiphy,
dev,
frame_type,
reg);
}
#endif
int ath6kl_set_gtk_rekey_offload(struct wiphy *wiphy,
struct net_device *dev, struct cfg80211_gtk_rekey_data *data)
{
int ret = 0;
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct wmi_gtk_offload_op cmd;
struct ath6kl_vif *vif = netdev_priv(dev);
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: vif %d\n",
__func__,
vif->fw_vif_idx);
if (!vif)
return -EIO;
/* Only support GTK offload for 1st interface now. */
if (vif->fw_vif_idx != 0)
return 0;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (!data)
return ret;
memset(&cmd, 0, sizeof(struct wmi_gtk_offload_op));
memcpy(cmd.kek, data->kek, GTK_OFFLOAD_KEK_BYTES);
memcpy(cmd.kck, data->kck, GTK_OFFLOAD_KCK_BYTES);
memcpy(cmd.replay_counter, data->replay_ctr, GTK_REPLAY_COUNTER_BYTES);
ret = ath6kl_wmi_set_gtk_offload(ar->wmi, vif->fw_vif_idx, cmd.kek,
cmd.kck, cmd.replay_counter);
return ret;
}
static int ath6kl_ap_probe_client(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, u64 *cookie)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
struct ath6kl_vif *vif = netdev_priv(dev);
struct ath6kl_sta *conn;
int ret;
BUG_ON(vif->nw_type != AP_NETWORK);
conn = ath6kl_find_sta(vif, (u8 *)peer);
if (conn)
ret = ath6kl_wmi_ap_poll_sta(ar->wmi,
vif->fw_vif_idx,
conn->aid);
else {
ret = -EINTR;
ath6kl_dbg(ATH6KL_DBG_INFO,
"can't find sta %02x:%02x:%02x:%02x:%02x:%02x, vif-idx %d\n",
peer[0], peer[1], peer[2], peer[3], peer[4], peer[5],
vif->fw_vif_idx);
}
return ret;
}
bool ath6kl_sched_scan_trigger(struct ath6kl_vif *vif)
{
struct ath6kl *ar = vif->ar;
int i;
/* NOT YET */
if (!ar->sche_scan)
return false;
for (i = 0; i < ar->vif_max; i++) {
struct ath6kl_vif *vif_trig;
vif_trig = ath6kl_get_vif_by_index(ar, i);
if ((vif_trig) &&
(vif_trig != vif) &&
(!test_bit(CONNECTED, &vif_trig->flags)) &&
(vif_trig->sche_scan_interval)) {
ath6kl_dbg(ATH6KL_DBG_INFO, "sche scan triggered\n");
return true;
}
}
return false;
}
static void ath6kl_sched_scan_timer(unsigned long ptr)
{
struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr;
/* NOT YET */
ath6kl_dbg(ATH6KL_DBG_INFO, "report sche scan\n");
cfg80211_sched_scan_results(vif->ar->wiphy);
mod_timer(&vif->sche_scan_timer,
jiffies + msecs_to_jiffies(vif->sche_scan_interval));
return;
}
static int ath6kl_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_sched_scan_request *request)
{
struct ath6kl_vif *vif = netdev_priv(dev);
int ret = 0;
/* NOT YET */
ath6kl_dbg(ATH6KL_DBG_INFO,
"start sche scan, interval %d, n_ssids %d, n_channels %d\n",
request->interval,
request->n_ssids,
request->n_channels);
if ((vif->ar->sche_scan) &&
(vif->nw_type == INFRA_NETWORK)) {
vif->sche_scan_interval = request->interval;
mod_timer(&vif->sche_scan_timer,
jiffies +
msecs_to_jiffies(vif->sche_scan_interval));
} else
ret = -EOPNOTSUPP;
return ret;
}
static int ath6kl_sched_scan_stop(struct wiphy *wiphy,
struct net_device *dev)
{
struct ath6kl_vif *vif = netdev_priv(dev);
/* NOT YET */
ath6kl_dbg(ATH6KL_DBG_INFO, "stop sche scan\n");
if (vif->sche_scan_interval) {
del_timer(&vif->sche_scan_timer);
vif->sche_scan_interval = 0;
}
return 0;
}
static int ath6kl_sched_scan_init(struct ath6kl_vif *vif)
{
struct ath6kl *ar = vif->ar;
/* NOT YET */
if (ar->sche_scan) {
vif->sche_scan_interval = 0;
init_timer(&vif->sche_scan_timer);
vif->sche_scan_timer.function = ath6kl_sched_scan_timer;
vif->sche_scan_timer.data = (unsigned long)(vif);
}
return 0;
}
static int ath6kl_sched_scan_deinit(struct ath6kl_vif *vif)
{
struct ath6kl *ar = vif->ar;
ath6kl_sched_scan_stop(ar->wiphy, vif->ndev);
return 0;
}
#if defined(CONFIG_ANDROID) || defined(USB_AUTO_SUSPEND)
int ath6kl_set_wow_mode(struct wiphy *wiphy, struct cfg80211_wowlan *wow)
{
struct ath6kl_vif *vif;
int ret = 0, pos, i;
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
u32 filter = 0;
u8 mask[WOW_MASK_SIZE];
u32 host_req_delay = WOW_HOST_REQ_DELAY;
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "%s: +++\n", __func__);
vif = ath6kl_vif_first(ar);
if (!vif) {
ret = -EIO;
goto FAIL;
}
if (!ath6kl_cfg80211_ready(vif)) {
ret = -EIO;
goto FAIL;
}
if (!ar->get_wow_pattern && WARN_ON(!wow))
goto FAIL;
/* Reset wakeup delay time to 2 secs for sdio, keep 5 secs for
* usb now
*/
if (ar->hif_type == ATH6KL_HIF_TYPE_SDIO)
host_req_delay = 2000;
/* for hsic mode, reduce the wakeup time to 2 secs */
if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode))
host_req_delay = 2000;
/* Clear existing WOW patterns */
if (!ar->get_wow_pattern) {
for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
WOW_LIST_ID, i);
/* Configure new WOW patterns */
for (i = 0; i < wow->n_patterns; i++) {
if (ath6kl_wow_ext) {
ret = ath6kl_wmi_add_wow_ext_pattern_cmd(
ar->wmi,
vif->fw_vif_idx, WOW_LIST_ID,
wow->patterns[i].pattern_len,
i,
0,
wow->patterns[i].pattern,
wow->patterns[i].mask);
/* filter for wow ext pattern */
filter |= WOW_FILTER_OPTION_PATTERNS;
} else {
/*
* Convert given nl80211 specific mask value to
* equivalent driver specific mask value
* and send it to the chip along with patterns.
* For example, if the mask value defined
* in struct cfg80211_wowlan is 0xA
* (equivalent binary is 1010), then equivalent
* driver specific mask value is
* "0xFF 0x00 0xFF 0x00".
*/
memset(&mask, 0, sizeof(mask));
for (pos = 0;
pos < wow->patterns[i].pattern_len;
pos++) {
if (wow->patterns[i].mask[pos / 8] &
(0x1 << (pos % 8)))
mask[pos] = 0xFF;
}
/*
* Note: Pattern's offset is not passed
* as part of wowlan parameter from CFG layer.
* So it's always passed as ZERO to
* the firmware.
* It means, given WOW patterns are always
* matched from the first byte
* of received pkt in the firmware.
*/
ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
vif->fw_vif_idx, WOW_LIST_ID,
wow->patterns[i].pattern_len,
0 /* pattern offset */,
wow->patterns[i].pattern, mask);
}
if (ret)
return ret;
}
} else
filter |= WOW_FILTER_OPTION_PATTERNS;
if (wow) {
if (wow->disconnect || wow->any) {
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_NWK_DISASSOC\n");
filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
}
if (wow->magic_pkt || wow->any) {
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_MAGIC_PACKET\n");
filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
}
if (wow->gtk_rekey_failure || wow->any) {
ath6kl_dbg(ATH6KL_DBG_WOWLAN,
"filter: WOW_FILTER_OPTION_OFFLOAD_GTK/GTK_ERROR\n");
filter |= (WOW_FILTER_OPTION_EAP_REQ |
WOW_FILTER_OPTION_8021X_4WAYHS |
WOW_FILTER_OPTION_GTK_ERROR |
WOW_FILTER_OPTION_OFFLOAD_GTK);
}
if (wow->eap_identity_req || wow->any) {
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_EAP_REQ\n");
filter |= WOW_FILTER_OPTION_EAP_REQ;
}
if (wow->four_way_handshake || wow->any) {
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_8021X_4WAYHS\n");
filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
}
if (vif->arp_offload_ip_set || wow->any) {
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_OFFLOAD_ARP\n");
filter |= WOW_FILTER_OPTION_OFFLOAD_ARP;
}
}
/*Do GTK offload in WPA/WPA2 auth mode connection.*/
if (vif->auth_mode == WPA2_AUTH_CCKM || vif->auth_mode == WPA2_PSK_AUTH
|| vif->auth_mode == WPA_AUTH_CCKM || vif->auth_mode == WPA_PSK_AUTH){
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_OFFLOAD_GTK\n");
filter |= WOW_FILTER_OPTION_OFFLOAD_GTK;
}
if (filter || (wow && wow->n_patterns)) {
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "Set filter: 0x%x ", filter);
ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_WOW_MODE_ENABLE,
filter,
host_req_delay);
}
set_bit(WLAN_WOW_ENABLE, &vif->flags);
FAIL:
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "%s: --- return %d\n", __func__, ret);
return ret;
}
int ath6kl_clear_wow_mode(struct wiphy *wiphy)
{
struct ath6kl_vif *vif;
struct ath6kl *ar = wiphy_priv(wiphy);
int ret = 0;
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "%s: +++\n", __func__);
vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_WOW_MODE_DISABLE,
0,
0);
ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_HOST_MODE_AWAKE);
clear_bit(WLAN_WOW_ENABLE, &vif->flags);
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "%s: ---\n", __func__);
return ret;
}
#endif /*CONFIG_ANDROID*/
static const struct ieee80211_txrx_stypes
ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
#ifdef CE_SUPPORT
| BIT(IEEE80211_STYPE_PROBE_RESP >> 4)
#endif
},
[NL80211_IFTYPE_AP] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
#ifdef CE_SUPPORT
| BIT(IEEE80211_STYPE_PROBE_RESP >> 4)
#endif
},
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
#ifdef CE_SUPPORT
| BIT(IEEE80211_STYPE_PROBE_RESP >> 4)
#endif
},
[NL80211_IFTYPE_P2P_GO] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
#ifdef CE_SUPPORT
|BIT(IEEE80211_STYPE_PROBE_RESP >> 4)
#endif
},
};
#ifdef NL80211_CMD_SET_AP_MAC_ACL
static int ath6kl_cfg80211_ap_acl(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_acl_data *params)
{
struct ath6kl_vif *vif = netdev_priv(dev);
if (params)
_ath6kl_acl_config(vif, params);
return 0;
}
#endif
/* NOTE : this table may be over-wrote by ath6kl_change_cfg80211_ops() call. */
static struct cfg80211_ops ath6kl_cfg80211_ops = {
.add_virtual_intf = ath6kl_cfg80211_add_iface,
.del_virtual_intf = ath6kl_cfg80211_del_iface,
.change_virtual_intf = ath6kl_cfg80211_change_iface,
.change_bss = ath6kl_cfg80211_change_bss,
.scan = ath6kl_cfg80211_scan,
.connect = ath6kl_cfg80211_connect,
.disconnect = ath6kl_cfg80211_disconnect,
.add_key = ath6kl_cfg80211_add_key,
.get_key = ath6kl_cfg80211_get_key,
.del_key = ath6kl_cfg80211_del_key,
.set_default_key = ath6kl_cfg80211_set_default_key,
.set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
.set_tx_power = ath6kl_cfg80211_set_txpower,
.get_tx_power = ath6kl_cfg80211_get_txpower,
.set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
.join_ibss = ath6kl_cfg80211_join_ibss,
.leave_ibss = ath6kl_cfg80211_leave_ibss,
.get_station = ath6kl_get_station,
.dump_station = ath6kl_dump_station,
.set_bitrate_mask = ath6kl_cfg80211_set_bitrate_mask,
.set_pmksa = ath6kl_set_pmksa,
.del_pmksa = ath6kl_del_pmksa,
.flush_pmksa = ath6kl_flush_pmksa,
CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
#ifdef CONFIG_PM
.suspend = __ath6kl_cfg80211_suspend,
.resume = __ath6kl_cfg80211_resume,
#ifdef NL80211_CMD_GET_WOWLAN_QCA
.set_wow_mode = ath6kl_set_wow_mode,
.clr_wow_mode = ath6kl_clear_wow_mode,
#endif
#endif
.set_rekey_data = ath6kl_set_gtk_rekey_offload,
#ifndef CFG80211_NO_SET_CHAN_OPERATION
.set_channel = ath6kl_set_channel,
#endif
#ifdef NL80211_CMD_START_STOP_AP
.start_ap = ath6kl_start_ap,
.change_beacon = ath6kl_change_beacon,
.stop_ap = ath6kl_del_beacon,
#else
.add_beacon = ath6kl_add_beacon,
.set_beacon = ath6kl_set_beacon,
.del_beacon = ath6kl_del_beacon,
#endif
.del_station = ath6kl_del_station,
.change_station = ath6kl_change_station,
.remain_on_channel = ath6kl_remain_on_channel,
.cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
.mgmt_tx = ath6kl_mgmt_tx,
.mgmt_frame_register = ath6kl_mgmt_frame_register,
#ifdef NL80211_CMD_BTCOEX_QCA
.notify_btcoex = ath6kl_notify_btcoex,
#endif
};
void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
{
vif->saved_pwr_mode = vif->last_pwr_mode;
/* put all interfaces to power save */
if (ath6kl_wmi_powermode_cmd(vif->ar->wmi,
vif->fw_vif_idx, REC_POWER) != 0) {
ath6kl_err("wmi powermode command failed during suspend\n");
}
switch (vif->sme_state) {
case SME_CONNECTING:
ath6kl_cfg80211_connect_result(vif, vif->bssid, NULL, 0,
NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
break;
case SME_CONNECTED:
default:
/*
* FIXME: oddly enough smeState is in DISCONNECTED during
* suspend, why? Need to send disconnected event in that
* state.
*/
ath6kl_cfg80211_disconnected(vif, 0, NULL, 0, GFP_KERNEL);
break;
}
if (test_bit(CONNECTED, &vif->flags) ||
test_bit(CONNECT_PEND, &vif->flags))
ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
vif->sme_state = SME_DISCONNECTED;
clear_bit(CONNECTED, &vif->flags);
clear_bit(CONNECT_PEND, &vif->flags);
clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags);
clear_bit(PS_STICK, &vif->flags);
del_timer(&vif->shprotect_timer);
/* disable scanning */
if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx,
0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
printk(KERN_WARNING "ath6kl: failed to disable scan "
"during suspend\n");
if (test_bit(SCANNING, &vif->flags))
ath6kl_cfg80211_scan_complete_event(vif, true);
return;
}
void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
{
struct ath6kl_vif *vif, *tmp_vif;
#ifdef CE_SUPPORT
list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) {
if (ar->state == ATH6KL_STATE_DEEPSLEEP) {
if (vif->nw_type != AP_NETWORK)
ath6kl_cfg80211_stop(vif);
}
}
#else
list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list)
ath6kl_cfg80211_stop(vif);
#endif
}
static void ath6kl_change_cfg80211_ops(struct cfg80211_ops *cfg80211_ops)
{
/* Offload AP keep-alive function to user. */
if (debug_quirks & ATH6KL_MODULE_KEEPALIVE_BY_SUPP) {
WARN_ON(cfg80211_ops->probe_client);
cfg80211_ops->probe_client = ath6kl_ap_probe_client;
ath6kl_info("Offload AP Keep-alive to supplicant/hostapd\n");
}
/* Support Scheduled-Scan (a.k.a. PNO) */
if (debug_quirks & ATH6KL_MODULE_ENABLE_SCHE_SCAN) {
cfg80211_ops->sched_scan_start = ath6kl_sched_scan_start;
cfg80211_ops->sched_scan_stop = ath6kl_sched_scan_stop;
}
#ifdef NL80211_CMD_SET_AP_MAC_ACL
if (debug_quirks & ATH6KL_MODULE_AP_ACL_BY_NL80211) {
cfg80211_ops->set_mac_acl = ath6kl_cfg80211_ap_acl;
ath6kl_info("Configurate AP-ACL from NL80211.\n");
}
#endif
return;
}
static void ath6kl_reset_cfg80211_ops(struct cfg80211_ops *cfg80211_ops)
{
cfg80211_ops->probe_client = NULL;
cfg80211_ops->sched_scan_start = NULL;
cfg80211_ops->sched_scan_stop = NULL;
return;
}
static void _judge_p2p_framework(struct ath6kl *ar, unsigned int p2p_config)
{
ar->p2p = !!p2p_config;
ar->p2p_concurrent =
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_ENABLE_DEDICATE) ||
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_NO_DEDICATE) ||
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_MULTICHAN) ||
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_COMPAT);
ar->p2p_dedicate =
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_ENABLE_DEDICATE) ||
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_COMPAT);
ar->p2p_multichan_concurrent =
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_MULTICHAN);
ar->p2p_compat =
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_COMPAT);
ar->p2p_concurrent_ap =
!!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_AP);
ar->p2p_in_pasv_chan =
!!(p2p_config & ATH6KL_MODULEP2P_P2P_IN_PASSIVE_CHAN);
ar->p2p_wise_scan =
!!(p2p_config & ATH6KL_MODULEP2P_P2P_WISE_SCAN);
WARN_ON((!ar->p2p_concurrent) && (ar->p2p_multichan_concurrent));
WARN_ON((ar->p2p_concurrent_ap) &&
((!ar->p2p_concurrent) || (!ar->p2p_dedicate)));
WARN_ON((!ar->p2p) && (ar->p2p_wise_scan));
if (ar->p2p_concurrent) {
if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_P2P_MAX_FW_VIF))
ar->vif_max = TARGET_VIF_MAX; /* TODO */
else {
if (ar->p2p_dedicate)
ar->vif_max = 3;
else
ar->vif_max = 2; /*currently we only support 2*/
}
} else
ar->vif_max = 1;
/*
* P2P-Concurrent w/ softAP:
* STA + AP : vif_max == 3 && ar->p2p_concurrent_ap
* STA + P2P + AP : vif_max >= 4 && ar->p2p_concurrent_ap (not yet)
* w/ MCC : (not yet)
*/
WARN_ON((ar->p2p_concurrent_ap) && (ar->vif_max >= TARGET_VIF_MAX));
WARN_ON((ar->p2p_concurrent_ap) && (ar->p2p_multichan_concurrent));
ar->max_norm_iface = 1;
if (ar->p2p_concurrent_ap)
ar->max_norm_iface++;
ar->p2p_frame_retry = true;
if (ar->p2p_compat)
ar->p2p_frame_not_report = true;
else
ar->p2p_frame_not_report = false;
ar->p2p_war_bad_intel_go = true;
ar->p2p_war_bad_broadcom_go = true;
ar->p2p_war_p2p_client_awake = true;
ar->p2p_ie_not_append = P2P_IE_IN_PROBE_REQ;
ath6kl_info("%dVAP/%d, P2P %s, concurrent %s %s,"
" %s dedicate p2p-device,"
" multi-channel-concurrent %s, p2p-compat %s%s%s%s\n",
ar->vif_max,
ar->max_norm_iface,
(ar->p2p ? "enable" : "disable"),
(ar->p2p_concurrent ? "on" : "off"),
(ar->p2p_concurrent_ap ? "with softAP" : ""),
(ar->p2p_dedicate ? "with" : "without"),
(ar->p2p_multichan_concurrent ? "enable" : "disable"),
(ar->p2p_compat ? "enable (ignore p2p_dedicate)" : "disable"),
(ar->p2p_ie_not_append ? ", sta-p2p-ie removed" : ""),
(ar->p2p_in_pasv_chan ? ", p2p_in_pasv_chan enable" : ""),
(ar->p2p_wise_scan ? ", p2p_wise_scan enable" : ""));
return;
}
static void _judge_vap_framework(struct ath6kl *ar, unsigned int vap_config)
{
int i;
int ap_num = 0, sta_num = 0;
/*
* TODO : To replace older ath6kl_p2p, in case of vap_config have
* P2P related VAP mode and transfer vap_config to XXX here
* then feed to _judge_p2p_framework() to configure P2P's
* framework.
*/
for (i = 0; i < ATH6KL_VIF_MAX; i++) {
ar->next_mode[i] = (vap_config >>
(i * ATH6KL_VAPMODE_OFFSET)) &
ATH6KL_VAPMODE_MASK;
if (ar->next_mode[i] == ATH6KL_VAPMODE_DISABLED)
break;
if (ar->next_mode[i] == ATH6KL_VAPMODE_STA)
sta_num++;
else if (ar->next_mode[i] == ATH6KL_VAPMODE_AP)
ap_num++;
}
/* Now, only support 1STA+1AP or 2AP */
WARN_ON((sta_num > 1) ||
(ap_num > 2) ||
(sta_num + ap_num > 2));
ar->vif_max = i;
ar->max_norm_iface = i;
ath6kl_info("%dVAP/%d, NO P2P, vapmode %d, %d, %d, %d\n",
ar->vif_max,
ar->max_norm_iface,
ar->next_mode[0], ar->next_mode[1],
ar->next_mode[2], ar->next_mode[3]);
}
struct ath6kl *ath6kl_core_alloc(struct device *dev)
{
struct ath6kl *ar;
struct wiphy *wiphy;
/*
* Overwrite cfg80211_ops function table if we need to
* enable/disable some features.
*/
ath6kl_change_cfg80211_ops(&ath6kl_cfg80211_ops);
/* create a new wiphy for use with cfg80211 */
wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
if (!wiphy) {
ath6kl_err("couldn't allocate wiphy device\n");
return NULL;
}
ar = wiphy_priv(wiphy);
ar->mod_debug_quirks = debug_quirks;
ar->wiphy = wiphy;
ar->dev = dev;
BUG_ON((ath6kl_p2p && ath6kl_vap));
/*
* For backward compatible, keep ATH6KL_MODULE_TESTMODE_ENABLE as
* testmode = 1. Note that if want to configure as testmode=2,
* Must not configure ATH6KL_MODULE_TESTMODE_ENABLE,
* And configure testmode = 2 as module parameter directly.
*/
if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_TESTMODE_ENABLE))
testmode = 1;
if (testmode ||
ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_ENABLE_EPPING)) {
ath6kl_p2p = 0;
ath6kl_vap = 0;
ath6kl_roam_mode = ATH6KL_MODULEROAM_DISABLE;
}
if (ath6kl_p2p)
_judge_p2p_framework(ar, ath6kl_p2p);
else if (ath6kl_vap)
_judge_vap_framework(ar, ath6kl_vap);
else {
ar->vif_max = 1;
ar->max_norm_iface = 1;
}
spin_lock_init(&ar->lock);
spin_lock_init(&ar->list_lock);
spin_lock_init(&ar->state_lock);
init_waitqueue_head(&ar->event_wq);
sema_init(&ar->sem, 1);
sema_init(&ar->wmi_evt_sem, 1);
INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
INIT_LIST_HEAD(&ar->vif_list);
setup_timer(&ar->eapol_shprotect_timer,
ath6kl_eapol_shprotect_timer_handler,
(unsigned long) ar);
clear_bit(WMI_ENABLED, &ar->flag);
clear_bit(SKIP_SCAN, &ar->flag);
clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
clear_bit(EAPOL_HANDSHAKE_PROTECT, &ar->flag);
clear_bit(RECOVER_IN_PROCESS, &ar->flag);
ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
ar->listen_intvl_b = 0;
ar->tx_pwr = 0;
ar->low_rssi_roam_params.lrssi_scan_period = WMI_ROAM_LRSSI_SCAN_PERIOD;
ar->low_rssi_roam_params.lrssi_scan_threshold = \
WMI_ROAM_LRSSI_SCAN_THRESHOLD;
ar->low_rssi_roam_params.lrssi_roam_threshold = \
WMI_ROAM_LRSSI_ROAM_THRESHOLD;
ar->low_rssi_roam_params.roam_rssi_floor = WMI_ROAM_LRSSI_ROAM_FLOOR;
ar->state = ATH6KL_STATE_OFF;
if ((ar->p2p_concurrent) &&
(ar->p2p_dedicate))
ar->sche_scan = ath6kl_mod_debug_quirks(ar,
ATH6KL_MODULE_ENABLE_SCHE_SCAN);
ar->roam_mode = ath6kl_roam_mode;
if (ar->sche_scan &&
ar->roam_mode != ATH6KL_MODULEROAM_DISABLE) {
ath6kl_err("Roam shall be exclusive with schedule scan. Disabled\n");
ar->roam_mode = ATH6KL_MODULEROAM_DISABLE;
}
return ar;
}
int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
{
struct wiphy *wiphy = ar->wiphy;
int ret;
wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
wiphy->max_remain_on_channel_duration = ATH6KL_ROC_MAX_PERIOD * 1000;
/* set device pointer for wiphy */
set_wiphy_dev(wiphy, ar->dev);
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP);
if (ar->p2p) {
if (!IS_STA_AP_ONLY(ar))
wiphy->interface_modes |=
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT);
}
if (ar->p2p_concurrent) {
struct ieee80211_iface_combination
*ieee80211_iface_combination = NULL;
switch (ar->vif_max) {
case 4:
ieee80211_iface_combination =
ath6kl_iface_combinations_p2p_concurrent4;
wiphy->n_iface_combinations = ARRAY_SIZE(
ath6kl_iface_combinations_p2p_concurrent4);
break;
case 3:
ieee80211_iface_combination =
ath6kl_iface_combinations_p2p_concurrent3;
wiphy->n_iface_combinations = ARRAY_SIZE(
ath6kl_iface_combinations_p2p_concurrent3);
break;
case 2:
ieee80211_iface_combination =
ath6kl_iface_combinations_p2p_concurrent2;
wiphy->n_iface_combinations = ARRAY_SIZE(
ath6kl_iface_combinations_p2p_concurrent2);
break;
default:
WARN_ON(1);
}
/* Overwrite it if P2P-Concurrent w/ softAP mode. */
if (ar->p2p_concurrent_ap) {
if (IS_STA_AP_ONLY(ar)) {
ieee80211_iface_combination =
ath6kl_iface_combinations_sta_ap;
wiphy->n_iface_combinations = ARRAY_SIZE(
ath6kl_iface_combinations_sta_ap);
} else {
ieee80211_iface_combination =
ath6kl_iface_combinations_p2p_concurrent4_1;
wiphy->n_iface_combinations = ARRAY_SIZE(
ath6kl_iface_combinations_p2p_concurrent4_1);
}
}
/* Update max. channel support */
if (ar->p2p_multichan_concurrent &&
ieee80211_iface_combination != NULL) {
ieee80211_iface_combination[0].num_different_channels =
ieee80211_iface_combination[0].max_interfaces;
}
wiphy->iface_combinations = ieee80211_iface_combination;
if (ar->p2p_compat) {
wiphy->n_iface_combinations = 0;
wiphy->iface_combinations = NULL;
}
}
/* update MCS rate mask & TX STBC. */
if (!(ar->target_subtype & TARGET_SUBTYPE_2SS)) {
ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0;
ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0;
ath6kl_band_2ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
ath6kl_band_5ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
}
/* update 2G-HT40 capability. */
if (!(ar->target_subtype & TARGET_SUBTYPE_HT40)) {
ath6kl_band_2ghz.ht_cap.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
ath6kl_band_2ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
ath6kl_band_2ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_DSSSCCK40;
}
/* max num of ssids that can be probed during scanning */
wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
wiphy->max_scan_ie_len = MAX_APP_IE_LEN;
if (ar->sche_scan) {
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX;
wiphy->max_sched_scan_ie_len = MAX_APP_IE_LEN;
}
wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
if ((!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_5G)) &&
(ar->target_subtype & TARGET_SUBTYPE_DUAL))
wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
else {
ath6kl_info("Disable 5G support by %s!\n",
(ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_5G) ?
"driver" : "board-data"));
}
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->cipher_suites = cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
#ifdef CFG80211_WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL
if ((ath6kl_cfg80211_ops.remain_on_channel) &&
(ath6kl_cfg80211_ops.cancel_remain_on_channel))
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
#endif
if (ar->roam_mode != ATH6KL_MODULEROAM_DISABLE)
wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
#ifdef NL80211_ATTR_AP_INACTIVITY_TIMEOUT
if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_KEEPALIVE_CONFIG_BY_SUPP))
wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
#endif
#ifdef NL80211_WIPHY_FEATURE_SCAN_FLUSH
/*
* The cfg80211 support it by default but we disabled here.
* To speed up the scan time and the channel dewell time is not really
* longer enough. Cache BSS information may be helpful to ath6kl.
*/
wiphy->features &= ~NL80211_FEATURE_SCAN_FLUSH;
#endif
#ifdef CFG80211_TX_POWER_PER_WDEV
wiphy->features |= NL80211_FEATURE_VIF_TXPOWER;
#endif
wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_DISCONNECT |
WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
WIPHY_WOWLAN_GTK_REKEY_FAILURE |
WIPHY_WOWLAN_EAP_IDENTITY_REQ |
WIPHY_WOWLAN_4WAY_HANDSHAKE |
WIPHY_WOWLAN_RFKILL_RELEASE;
wiphy->wowlan.n_patterns =
(WOW_MAX_FILTER_LISTS*WOW_MAX_FILTERS_PER_LIST);
wiphy->wowlan.pattern_min_len = WOW_MIN_PATTERN_SIZE;
wiphy->wowlan.pattern_max_len = WOW_MAX_PATTERN_SIZE;
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "wow attr: flags: %x, n_patterns: %d, ",
wiphy->wowlan.flags,
wiphy->wowlan.n_patterns);
ath6kl_dbg(ATH6KL_DBG_WOWLAN, "min_len: %d, max_len: %d\n",
wiphy->wowlan.pattern_min_len,
wiphy->wowlan.pattern_max_len);
#ifdef CONFIG_ANDROID
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_init(&ar->wake_lock, WAKE_LOCK_SUSPEND, "ath6kl_suspend_wl");
#endif
if (ar->wow_irq) {
int ret;
ret = request_irq(ar->wow_irq, ath6kl_wow_irq,
IRQF_SHARED | IRQF_TRIGGER_RISING,
"ar6000" "sdiowakeup", ar);
if (!ret) {
ret = enable_irq_wake(ar->wow_irq);
if (ret < 0) {
ath6kl_err("Couldn't enable WoW IRQ as wakeup interrupt");
return ret;
}
printk("ath6kl: WoW IRQ %d\n", ar->wow_irq);
}
}
ath6kl_setup_android_resource(ar);
#endif
if (test_bit(INTERNAL_REGDB, &ar->flag) ||
test_bit(CFG80211_REGDB, &ar->flag)) {
#ifdef CFG80211_VOID_REG_NOTIFIER
wiphy->reg_notifier = ath6kl_reg_notifier2;
#else
wiphy->reg_notifier = ath6kl_reg_notifier;
#endif
wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS;
}
#ifdef NL80211_CMD_SET_AP_MAC_ACL
if (debug_quirks & ATH6KL_MODULE_AP_ACL_BY_NL80211)
wiphy->max_acl_mac_addrs = ATH6KL_AP_ACL_MAX_NUM;
#endif
ret = wiphy_register(wiphy);
if (ret < 0) {
ath6kl_err("couldn't register wiphy device\n");
return ret;
}
return 0;
}
static int ath6kl_init_if_data(struct ath6kl_vif *vif)
{
int i;
struct ath6kl *ar = vif->ar;
vif->aggr_cntxt = aggr_init(vif);
if (!vif->aggr_cntxt) {
ath6kl_err("failed to initialize aggr\n");
return -ENOMEM;
}
for (i = 0; i < AP_MAX_NUM_STA; i++) {
vif->sta_list[i].aggr_conn_cntxt = aggr_init_conn(vif);
if (!vif->sta_list[i].aggr_conn_cntxt) {
ath6kl_err("failed to initialize aggr_node\n");
return -ENOMEM;
}
}
#ifdef ACS_SUPPORT
vif->acs_ctx = ath6kl_acs_init(vif);
if (!vif->acs_ctx) {
ath6kl_err("failed to initialize acs");
return -ENOMEM;
}
#endif
vif->htcoex_ctx = ath6kl_htcoex_init(vif);
if (!vif->htcoex_ctx) {
ath6kl_err("failed to initialize htcoex\n");
return -ENOMEM;
}
#ifdef CONFIG_ANDROID
/* Enable htcoex for wlan0 in Android. Scan period is 60s */
if ((vif->fw_vif_idx == 0) &&
(vif->ar->target_subtype & TARGET_SUBTYPE_HT40))
ath6kl_htcoex_config(vif, ATH6KL_HTCOEX_SCAN_PERIOD, 0);
#endif
#ifndef CE_SUPPORT
/* WAR: CR480066 */
vif->bss_post_proc_ctx = ath6kl_bss_post_proc_init(vif);
if (!vif->bss_post_proc_ctx) {
ath6kl_err("failed to initialize bss_post_proc\n");
return -ENOMEM;
}
#endif
vif->p2p_ps_info_ctx = ath6kl_p2p_ps_init(vif);
if (!vif->p2p_ps_info_ctx) {
ath6kl_err("failed to initialize p2p_ps\n");
return -ENOMEM;
}
if (!test_bit(TESTMODE_EPPING, &ar->flag)) {
if (ath6kl_mod_debug_quirks(vif->ar,
ATH6KL_MODULE_KEEPALIVE_BY_SUPP)) {
vif->ap_keepalive_ctx =
ath6kl_ap_keepalive_init(vif,
AP_KA_MODE_BYSUPP);
} else if (ath6kl_mod_debug_quirks(vif->ar,
ATH6KL_MODULE_KEEPALIVE_CONFIG_BY_SUPP)) {
vif->ap_keepalive_ctx =
ath6kl_ap_keepalive_init(vif,
AP_KA_MODE_CONFIG_BYSUPP);
} else {
vif->ap_keepalive_ctx =
ath6kl_ap_keepalive_init(vif,
AP_KA_MODE_ENABLE);
}
if (!vif->ap_keepalive_ctx) {
ath6kl_err("failed to initialize ap_keepalive\n");
return -ENOMEM;
}
}
vif->ap_acl_ctx = ath6kl_ap_acl_init(vif);
if (!vif->ap_acl_ctx) {
ath6kl_err("failed to initialize ap_acl\n");
return -ENOMEM;
}
vif->ap_admc_ctx = ath6kl_ap_admc_init(vif, AP_ADMC_MODE_ACCEPT_ALWAYS);
if (!vif->ap_admc_ctx) {
ath6kl_err("failed to initialize ap_admc\n");
return -ENOMEM;
}
ath6kl_ap_rc_init(vif);
setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
(unsigned long) vif->ndev);
set_bit(WMM_ENABLED, &vif->flags);
spin_lock_init(&vif->if_lock);
setup_timer(&vif->vifscan_timer, ath6kl_scan_timer_handler,
(unsigned long) vif);
setup_timer(&vif->shprotect_timer, ath6kl_shprotect_timer_handler,
(unsigned long) vif);
spin_lock_init(&vif->pend_skb_lock);
vif->scan_req = NULL;
vif->pend_skb = NULL;
vif->scanband_chan = 0;
if (ar->p2p_wise_scan &&
(vif->fw_vif_idx != 0) &&
(vif->fw_vif_idx != (ar->vif_max - 1))) {
/* Only apply to P2P interfaces. */
vif->scanband_type = SCANBAND_TYPE_2_P2PCHAN;
} else
vif->scanband_type = SCANBAND_TYPE_ALL;
vif->saved_pwr_mode =
vif->last_pwr_mode = REC_POWER;
return 0;
}
void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
{
struct ath6kl *ar = vif->ar;
int ctr;
#ifdef ATH6KL_DIAGNOSTIC
wifi_diag_timer_destroy(vif);
#endif
for (ctr = 0; ctr < AP_MAX_NUM_STA ; ctr++) {
if (vif->sta_list[ctr].psq_age_active) {
del_timer_sync(&vif->sta_list[ctr].psq_age_timer);
vif->sta_list[ctr].psq_age_active = 0;
}
if (!ath6kl_ps_queue_empty(&vif->sta_list[ctr].psq_data))
ath6kl_ps_queue_purge(&vif->sta_list[ctr].psq_data);
if (!ath6kl_ps_queue_empty(&vif->sta_list[ctr].psq_mgmt))
ath6kl_ps_queue_purge(&vif->sta_list[ctr].psq_mgmt);
aggr_module_destroy_conn(vif->sta_list[ctr].aggr_conn_cntxt);
}
netif_carrier_off(vif->ndev);
netif_stop_queue(vif->ndev);
ath6kl_tx_data_cleanup_by_if(vif);
aggr_module_destroy(vif->aggr_cntxt);
#ifdef ACS_SUPPORT
ath6kl_acs_deinit(vif);
#endif
ath6kl_htcoex_deinit(vif);
ath6kl_bss_post_proc_deinit(vif);
ath6kl_p2p_ps_deinit(vif);
ath6kl_ap_keepalive_deinit(vif);
ath6kl_ap_acl_deinit(vif);
ath6kl_ap_admc_deinit(vif);
ath6kl_sched_scan_deinit(vif);
ar->avail_idx_map |= BIT(vif->fw_vif_idx);
if (vif->nw_type == ADHOC_NETWORK)
ar->ibss_if_active = false;
del_timer(&vif->vifscan_timer);
unregister_netdevice(vif->ndev);
ar->num_vif--;
}
struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
enum nl80211_iftype type, u8 fw_vif_idx,
u8 nw_type)
{
struct net_device *ndev;
struct ath6kl_vif *vif;
int i;
#ifdef ATH6KL_HSIC_RECOVER
u8 zero_mac[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
#endif
ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
if (!ndev)
return NULL;
vif = netdev_priv(ndev);
ndev->ieee80211_ptr = &vif->wdev;
vif->wdev.wiphy = ar->wiphy;
vif->ar = ar;
vif->ndev = ndev;
SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
vif->wdev.netdev = ndev;
vif->wdev.iftype = type;
vif->fw_vif_idx = fw_vif_idx;
vif->nw_type = vif->next_mode = nw_type;
memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
#ifdef ATH6KL_HSIC_RECOVER
/* If we don't get the mac address just in time */
if (memcmp(ar->mac_addr, zero_mac, ETH_ALEN) == 0 &&
cached_mac_valid == true) {
memcpy(ndev->dev_addr, cached_mac, ETH_ALEN);
}
#endif
if (fw_vif_idx != 0)
ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
0x2;
init_netdev(ndev);
ath6kl_init_control_info(vif);
/* ATH6KL_MODULE_ENABLE_EPPING is enable will skip
* the following procedure */
if (!ath6kl_mod_debug_quirks(vif->ar, ATH6KL_MODULE_ENABLE_EPPING)) {
/* TODO: Pass interface specific pointer instead of ar */
if (ath6kl_init_if_data(vif))
goto err;
}
if (register_netdevice(ndev))
goto err;
ar->avail_idx_map &= ~BIT(fw_vif_idx);
vif->sme_state = SME_DISCONNECTED;
set_bit(WLAN_ENABLED, &vif->flags);
ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
set_bit(NETDEV_REGISTERED, &vif->flags);
if (type == NL80211_IFTYPE_ADHOC)
ar->ibss_if_active = true;
spin_lock_bh(&ar->list_lock);
list_add_tail(&vif->list, &ar->vif_list);
spin_unlock_bh(&ar->list_lock);
ath6kl_p2p_utils_init_port(vif, type);
ath6kl_sched_scan_init(vif);
return ndev;
err:
for (i = 0; i < AP_MAX_NUM_STA ; i++)
aggr_module_destroy_conn(vif->sta_list[i].aggr_conn_cntxt);
aggr_module_destroy(vif->aggr_cntxt);
ath6kl_htcoex_deinit(vif);
ath6kl_bss_post_proc_deinit(vif);
ath6kl_p2p_ps_deinit(vif);
ath6kl_ap_keepalive_deinit(vif);
ath6kl_ap_acl_deinit(vif);
ath6kl_ap_admc_deinit(vif);
free_netdev(ndev);
return NULL;
}
void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar)
{
#ifdef CONFIG_ANDROID
if (ar->wow_irq) {
if (disable_irq_wake(ar->wow_irq))
ath6kl_err("Couldn't disable hostwake IRQ wakeup mode\n");
free_irq(ar->wow_irq, ar);
ar->wow_irq = 0;
}
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_destroy(&ar->wake_lock);
#endif
ath6kl_cleanup_android_resource(ar);
#endif
wiphy_unregister(ar->wiphy);
wiphy_free(ar->wiphy);
ath6kl_reset_cfg80211_ops(&ath6kl_cfg80211_ops);
}
void ath6kl_core_init_defer(struct work_struct *wk)
{
#define MAX_RD_WAIT_CNT (20) /* 20 * 100 = 2 sec. */
struct ath6kl *ar;
struct vif_params params;
int i;
ar = container_of(wk, struct ath6kl,
init_defer_wk);
/* Automatically create virtual interface. */
if (!ath6kl_mod_debug_quirks(ar,
ATH6KL_MODULE_DISABLE_AUTO_ADD_INF) &&
(ar->p2p_concurrent) &&
(ar->p2p_dedicate)) {
if (!IS_STA_AP_ONLY(ar)) {
ath6kl_info("Create dedicated p2p interface\n");
rtnl_lock();
params.use_4addr = 0;
if (ath6kl_cfg80211_add_iface(ar->wiphy,
ATH6KL_DEVNAME_DEF_P2P,
NL80211_IFTYPE_STATION,
NULL,
&params) == NULL) {
ath6kl_err("Create dedicated p2p interface fail!\n");
}
rtnl_unlock();
}
if (ar->p2p_concurrent_ap) {
ath6kl_info("Create concurrent ap interface\n");
rtnl_lock();
params.use_4addr = 0;
if (ath6kl_cfg80211_add_iface(ar->wiphy,
ATH6KL_DEVNAME_DEF_AP,
NL80211_IFTYPE_AP,
NULL,
&params) == NULL) {
ath6kl_err("Create concurrent ap interface fail!\n");
}
rtnl_unlock();
}
}
/* Automatically create virtual interface. */
if (!ath6kl_mod_debug_quirks(ar,
ATH6KL_MODULE_DISABLE_AUTO_ADD_INF) &&
(!ar->p2p)) {
for (i = 1; i < ATH6KL_VIF_MAX; i++) {
rtnl_lock();
params.use_4addr = 0;
if (ar->next_mode[i] == ATH6KL_VAPMODE_STA) {
if (ath6kl_cfg80211_add_iface(
ar->wiphy,
ATH6KL_DEVNAME_DEF_STA,
NL80211_IFTYPE_STATION,
NULL,
&params) == NULL) {
ath6kl_err("Create sta interface fail!\n");
}
} else if (ar->next_mode[i] == ATH6KL_VAPMODE_AP) {
if (ath6kl_cfg80211_add_iface(
ar->wiphy,
ATH6KL_DEVNAME_DEF_AP,
NL80211_IFTYPE_AP,
NULL,
&params) == NULL) {
ath6kl_err("Create ap interface fail!\n");
}
}
rtnl_unlock();
}
}
/* Wait target report WMI_REGDOMAIN_EVENTID done */
for (i = 0; i < MAX_RD_WAIT_CNT; i++) {
if (ath6kl_reg_is_init_done(ar)) {
/* wait more 2 jiffies for regdb updated */
schedule_timeout_interruptible(2);
break;
}
schedule_timeout_interruptible(msecs_to_jiffies(100));
}
clear_bit(INIT_DEFER_PROGRESS, &ar->flag);
if (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WAIT_DEFER))
wake_up(&ar->init_defer_wait_wq);
return;
#undef MAX_RD_WAIT_CNT
}
/* we use this flag to protect 4 way handshake */
void ath6kl_shprotect_timer_handler(unsigned long ptr)
{
struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr;
struct ath6kl *ar = vif->ar;
u8 bssid[ETH_ALEN];
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s\n", __func__);
clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags);
if (vif->pend_skb) {
ath6kl_err("%s, shall not have pend skb\n", __func__);
ath6kl_flush_pend_skb(vif);
}
if (ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) {
/*trigger roam, only work if firmware support*/
memset(bssid, 0, ETH_ALEN);
bssid[0] = 0xFF;
ath6kl_wmi_force_roam_cmd(ar->wmi, (const u8 *)bssid);
}
}
static void ath6kl_eapol_shprotect_timer_handler(unsigned long ptr)
{
struct ath6kl *ar = (struct ath6kl *)ptr;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s\n", __func__);
clear_bit(EAPOL_HANDSHAKE_PROTECT, &ar->flag);
ar->eapol_shprotect_vif = 0;
return;
}
#if defined(CONFIG_ANDROID) || defined(USB_AUTO_SUSPEND)
int ath6kl_android_enable_wow_default(struct ath6kl *ar)
{
unsigned char mask = 0x3F;
int mask_len;
int rv = 0, i;
struct cfg80211_wowlan wow;
if (test_bit(TESTMODE_EPPING, &ar->flag))
return 0;
memset(&wow, 0, sizeof(wow));
/*default filters : ANY + all self MACs*/
wow.any = true;
wow.patterns = kcalloc(ar->vif_max,
sizeof(wow.patterns[0]), GFP_KERNEL);
if (!wow.patterns)
return -ENOMEM;
mask_len = DIV_ROUND_UP(ETH_ALEN, 8);
for (i = 0; i < ar->vif_max; i++) {
wow.patterns[i].mask = kmalloc(mask_len + ETH_ALEN, GFP_KERNEL);
if (!wow.patterns[i].mask) {
rv = -ENOMEM;
goto failed;
}
wow.patterns[i].pattern = wow.patterns[i].mask + mask_len;
memcpy(wow.patterns[i].mask, &mask, mask_len);
wow.patterns[i].pattern_len = ETH_ALEN;
memcpy(wow.patterns[i].pattern, ar->mac_addr, ETH_ALEN);
if (i != 0)
wow.patterns[i].pattern[0] =
(wow.patterns[i].pattern[0] ^ (1 << i)) | 0x2;
wow.n_patterns++;
ath6kl_dbg(ATH6KL_DBG_WOWLAN,
"Add wow pattern:%x:%x:%x:%x:%x:%x\n",
wow.patterns[i].pattern[0], wow.patterns[i].pattern[1],
wow.patterns[i].pattern[2], wow.patterns[i].pattern[3],
wow.patterns[i].pattern[4], wow.patterns[i].pattern[5]);
}
/*Set wow mode to target firmware*/
rv = ath6kl_set_wow_mode(ar->wiphy, &wow);
failed:
for (i = 0; i < ar->vif_max; i++)
kfree(wow.patterns[i].mask);
kfree(wow.patterns);
return rv;
}
bool ath6kl_android_need_wow_suspend(struct ath6kl *ar)
{
struct ath6kl_vif *vif = NULL;
int i;
bool isConnected = false;
vif = ath6kl_vif_first(ar);
if (!vif)
return false;
if (!test_bit(WLAN_WOW_ENABLE, &vif->flags))
return false;
#ifdef ATH6KL_SUPPORT_WIFI_DISC
/* Always allow WoW suspend in discovery mode */
if (ar->disc_active)
return true;
#endif
/*If p2p-GO or softAP interface, we don't do Wow suspend.
*Otherwise, if one of the interfaces is connected, we do WoW
*to save power.
*/
for (i = 0; i < ar->vif_max; i++) {
vif = ath6kl_get_vif_by_index(ar, i);
if (vif) {
/*if p2p-GO or softAP interface, don't do wow*/
if (vif->nw_type == AP_NETWORK)
return false;
else if (test_bit(CONNECTED, &vif->flags))
isConnected = true;
}
}
return isConnected;
}
#endif
#ifdef ATH6KL_SUPPORT_WLAN_HB
int ath6kl_enable_wow_hb(struct ath6kl *ar)
{
u32 filter = 0;
int i, ret;
struct ath6kl_vif *vif;
vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
if (!ath6kl_cfg80211_ready(vif))
return -EIO;
/* Clear existing WOW patterns */
for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
WOW_LIST_ID, i);
filter |= WOW_FILTER_OPTION_MAGIC_PACKET |
WOW_FILTER_OPTION_NWK_DISASSOC;
/*Do GTK offload in WPA/WPA2 auth mode connection.*/
if (vif->auth_mode == WPA2_AUTH_CCKM ||
vif->auth_mode == WPA2_PSK_AUTH ||
vif->auth_mode == WPA_AUTH_CCKM ||
vif->auth_mode == WPA_PSK_AUTH) {
filter |= WOW_FILTER_OPTION_OFFLOAD_GTK;
}
ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_WOW_MODE_ENABLE,
filter,
WOW_HOST_REQ_DELAY);
return ret;
}
#endif