|
@@ -23,10 +23,8 @@
|
|
|
#include "testmode.h"
|
|
|
|
|
|
static unsigned int ath6kl_p2p;
|
|
|
-static unsigned int multi_norm_if_support;
|
|
|
|
|
|
module_param(ath6kl_p2p, uint, 0644);
|
|
|
-module_param(multi_norm_if_support, uint, 0644);
|
|
|
|
|
|
#define RATETAB_ENT(_rate, _rateid, _flags) { \
|
|
|
.bitrate = (_rate), \
|
|
@@ -127,6 +125,37 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = {
|
|
|
|
|
|
#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
|
|
|
|
|
|
+/* returns true if scheduled scan was stopped */
|
|
|
+static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
|
|
|
+{
|
|
|
+ struct ath6kl *ar = vif->ar;
|
|
|
+
|
|
|
+ if (ar->state != ATH6KL_STATE_SCHED_SCAN)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ del_timer_sync(&vif->sched_scan_timer);
|
|
|
+
|
|
|
+ ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ ATH6KL_HOST_MODE_AWAKE);
|
|
|
+
|
|
|
+ ar->state = ATH6KL_STATE_ON;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
|
|
|
+{
|
|
|
+ struct ath6kl *ar = vif->ar;
|
|
|
+ bool stopped;
|
|
|
+
|
|
|
+ stopped = __ath6kl_cfg80211_sscan_stop(vif);
|
|
|
+
|
|
|
+ if (!stopped)
|
|
|
+ return;
|
|
|
+
|
|
|
+ cfg80211_sched_scan_stopped(ar->wiphy);
|
|
|
+}
|
|
|
+
|
|
|
static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
|
|
|
enum nl80211_wpa_versions wpa_version)
|
|
|
{
|
|
@@ -205,6 +234,10 @@ static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
|
|
|
*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;
|
|
@@ -355,7 +388,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
|
|
|
|
|
|
if (type == NL80211_IFTYPE_STATION ||
|
|
|
type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
|
|
|
- for (i = 0; i < MAX_NUM_VIF; i++) {
|
|
|
+ for (i = 0; i < ar->vif_max; i++) {
|
|
|
if ((ar->avail_idx_map >> i) & BIT(0)) {
|
|
|
*if_idx = i;
|
|
|
return true;
|
|
@@ -365,7 +398,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
|
|
|
|
|
|
if (type == NL80211_IFTYPE_P2P_CLIENT ||
|
|
|
type == NL80211_IFTYPE_P2P_GO) {
|
|
|
- for (i = ar->max_norm_iface; i < MAX_NUM_VIF; i++) {
|
|
|
+ for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
|
|
|
if ((ar->avail_idx_map >> i) & BIT(0)) {
|
|
|
*if_idx = i;
|
|
|
return true;
|
|
@@ -382,6 +415,9 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
|
|
|
struct ath6kl *ar = ath6kl_priv(dev);
|
|
|
struct ath6kl_vif *vif = netdev_priv(dev);
|
|
|
int status;
|
|
|
+ u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
|
|
|
+
|
|
|
+ ath6kl_cfg80211_sscan_disable(vif);
|
|
|
|
|
|
vif->sme_state = SME_CONNECTING;
|
|
|
|
|
@@ -427,9 +463,12 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
|
|
if (sme->ie && (sme->ie_len > 0)) {
|
|
|
status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
|
|
|
- if (status)
|
|
|
+ if (status) {
|
|
|
+ up(&ar->sem);
|
|
|
return status;
|
|
|
- }
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
|
|
|
|
|
|
if (test_bit(CONNECTED, &vif->flags) &&
|
|
|
vif->ssid_len == sme->ssid_len &&
|
|
@@ -519,6 +558,9 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
|
|
vif->nw_type = vif->next_mode;
|
|
|
|
|
|
+ if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
|
|
|
+ nw_subtype = SUBTYPE_P2PCLIENT;
|
|
|
+
|
|
|
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"
|
|
@@ -536,7 +578,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
|
|
|
vif->grp_crypto, vif->grp_crypto_len,
|
|
|
vif->ssid_len, vif->ssid,
|
|
|
vif->req_bssid, vif->ch_hint,
|
|
|
- ar->connect_ctrl_flags);
|
|
|
+ ar->connect_ctrl_flags, nw_subtype);
|
|
|
|
|
|
up(&ar->sem);
|
|
|
|
|
@@ -563,17 +605,28 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, const u8 *bssid,
|
|
|
+static int 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 = cfg80211_get_bss(ar->wiphy, chan, bssid,
|
|
|
- vif->ssid, vif->ssid_len, WLAN_CAPABILITY_ESS,
|
|
|
- WLAN_CAPABILITY_ESS);
|
|
|
+ vif->ssid, vif->ssid_len,
|
|
|
+ cap_mask, cap_val);
|
|
|
if (bss == NULL) {
|
|
|
/*
|
|
|
* Since cfg80211 may not yet know about the BSS,
|
|
@@ -591,13 +644,12 @@ static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, const u8 *bssid,
|
|
|
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, WLAN_CAPABILITY_ESS, 100,
|
|
|
+ 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 dummy bss for "
|
|
|
- "%pM prior to indicating connect/roamed "
|
|
|
- "event\n", bssid);
|
|
|
+ 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 "
|
|
@@ -660,16 +712,16 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
|
|
|
|
|
|
chan = ieee80211_get_channel(ar->wiphy, (int) channel);
|
|
|
|
|
|
-
|
|
|
- if (nw_type & ADHOC_NETWORK) {
|
|
|
- cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
|
|
|
+ if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
|
|
|
+ beacon_ie_len) < 0) {
|
|
|
+ ath6kl_err("could not add cfg80211 bss entry\n");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (ath6kl_add_bss_if_needed(vif, bssid, chan, assoc_info,
|
|
|
- beacon_ie_len) < 0) {
|
|
|
- ath6kl_err("could not add cfg80211 bss entry for "
|
|
|
- "connect/roamed notification\n");
|
|
|
+ if (nw_type & ADHOC_NETWORK) {
|
|
|
+ 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);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -691,12 +743,14 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
|
|
|
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 *ar = ath6kl_priv(dev);
|
|
|
struct ath6kl_vif *vif = netdev_priv(dev);
|
|
|
|
|
|
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
|
|
|
reason_code);
|
|
|
|
|
|
+ ath6kl_cfg80211_sscan_disable(vif);
|
|
|
+
|
|
|
if (!ath6kl_cfg80211_ready(vif))
|
|
|
return -EIO;
|
|
|
|
|
@@ -789,7 +843,7 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
|
|
|
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 *ar = ath6kl_priv(ndev);
|
|
|
struct ath6kl_vif *vif = netdev_priv(ndev);
|
|
|
s8 n_channels = 0;
|
|
|
u16 *channels = NULL;
|
|
@@ -799,6 +853,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
if (!ath6kl_cfg80211_ready(vif))
|
|
|
return -EIO;
|
|
|
|
|
|
+ ath6kl_cfg80211_sscan_disable(vif);
|
|
|
+
|
|
|
if (!ar->usr_bss_filter) {
|
|
|
clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
|
|
|
ret = ath6kl_wmi_bssfilter_cmd(
|
|
@@ -824,6 +880,10 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
request->ssids[i].ssid);
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * FIXME: we should clear the IE in fw if it's not set so just
|
|
|
+ * remove the check altogether
|
|
|
+ */
|
|
|
if (request->ie) {
|
|
|
ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
WMI_FRAME_PROBE_REQ,
|
|
@@ -860,9 +920,25 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
if (test_bit(CONNECTED, &vif->flags))
|
|
|
force_fg_scan = 1;
|
|
|
|
|
|
- ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN,
|
|
|
- force_fg_scan, false, 0, 0, n_channels,
|
|
|
- channels);
|
|
|
+ if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
|
|
|
+ ar->fw_capabilities)) {
|
|
|
+ /*
|
|
|
+ * If capable of doing P2P mgmt operations using
|
|
|
+ * station interface, send additional information like
|
|
|
+ * supported rates to advertise and xmit rates for
|
|
|
+ * probe requests
|
|
|
+ */
|
|
|
+ ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ WMI_LONG_SCAN, force_fg_scan,
|
|
|
+ false, 0, 0, n_channels,
|
|
|
+ channels, request->no_cck,
|
|
|
+ request->rates);
|
|
|
+ } else {
|
|
|
+ ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ WMI_LONG_SCAN, force_fg_scan,
|
|
|
+ false, 0, 0, n_channels,
|
|
|
+ channels);
|
|
|
+ }
|
|
|
if (ret)
|
|
|
ath6kl_err("wmi_startscan_cmd failed\n");
|
|
|
else
|
|
@@ -905,7 +981,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
const u8 *mac_addr,
|
|
|
struct key_params *params)
|
|
|
{
|
|
|
- struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
|
|
|
+ struct ath6kl *ar = ath6kl_priv(ndev);
|
|
|
struct ath6kl_vif *vif = netdev_priv(ndev);
|
|
|
struct ath6kl_key *key = NULL;
|
|
|
u8 key_usage;
|
|
@@ -937,13 +1013,19 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
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 ||
|
|
|
- params->seq_len > sizeof(key->seq))
|
|
|
+ seq_len > sizeof(key->seq))
|
|
|
return -EINVAL;
|
|
|
|
|
|
key->key_len = params->key_len;
|
|
|
memcpy(key->key, params->key, key->key_len);
|
|
|
- key->seq_len = params->seq_len;
|
|
|
+ key->seq_len = seq_len;
|
|
|
memcpy(key->seq, params->seq, key->seq_len);
|
|
|
key->cipher = params->cipher;
|
|
|
}
|
|
@@ -961,6 +1043,9 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
|
key_type = AES_CRYPT;
|
|
|
break;
|
|
|
+ case WLAN_CIPHER_SUITE_SMS4:
|
|
|
+ key_type = WAPI_CRYPT;
|
|
|
+ break;
|
|
|
|
|
|
default:
|
|
|
return -ENOTSUPP;
|
|
@@ -976,10 +1061,9 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
__func__, key_index, key->key_len, key_type,
|
|
|
key_usage, key->seq_len);
|
|
|
|
|
|
- vif->def_txkey_index = key_index;
|
|
|
-
|
|
|
if (vif->nw_type == AP_NETWORK && !pairwise &&
|
|
|
- (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
|
|
|
+ (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
|
|
|
+ key_type == WAPI_CRYPT) && params) {
|
|
|
ar->ap_mode_bkey.valid = true;
|
|
|
ar->ap_mode_bkey.key_index = key_index;
|
|
|
ar->ap_mode_bkey.key_type = key_type;
|
|
@@ -1012,8 +1096,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
- vif->def_txkey_index,
|
|
|
+ return 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,
|
|
@@ -1024,7 +1107,7 @@ 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 *ar = ath6kl_priv(ndev);
|
|
|
struct ath6kl_vif *vif = netdev_priv(ndev);
|
|
|
|
|
|
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
|
|
@@ -1090,7 +1173,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
|
|
|
u8 key_index, bool unicast,
|
|
|
bool multicast)
|
|
|
{
|
|
|
- struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
|
|
|
+ struct ath6kl *ar = ath6kl_priv(ndev);
|
|
|
struct ath6kl_vif *vif = netdev_priv(ndev);
|
|
|
struct ath6kl_key *key = NULL;
|
|
|
u8 key_usage;
|
|
@@ -1181,11 +1264,12 @@ static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
|
|
*/
|
|
|
static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
|
|
|
enum nl80211_tx_power_setting type,
|
|
|
- int dbm)
|
|
|
+ int mbm)
|
|
|
{
|
|
|
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
|
|
|
struct ath6kl_vif *vif;
|
|
|
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);
|
|
@@ -1288,7 +1372,7 @@ static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
|
|
|
struct net_device *ndev;
|
|
|
u8 if_idx, nw_type;
|
|
|
|
|
|
- if (ar->num_vif == MAX_NUM_VIF) {
|
|
|
+ if (ar->num_vif == ar->vif_max) {
|
|
|
ath6kl_err("Reached maximum number of supported vif\n");
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
}
|
|
@@ -1333,9 +1417,6 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
|
|
|
|
|
|
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
|
|
|
|
|
|
- if (!ath6kl_cfg80211_ready(vif))
|
|
|
- return -EIO;
|
|
|
-
|
|
|
switch (type) {
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
vif->next_mode = INFRA_NETWORK;
|
|
@@ -1426,7 +1507,7 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
|
|
|
vif->grp_crypto, vif->grp_crypto_len,
|
|
|
vif->ssid_len, vif->ssid,
|
|
|
vif->req_bssid, vif->ch_hint,
|
|
|
- ar->connect_ctrl_flags);
|
|
|
+ ar->connect_ctrl_flags, SUBTYPE_NONE);
|
|
|
set_bit(CONNECT_PEND, &vif->flags);
|
|
|
|
|
|
return 0;
|
|
@@ -1453,6 +1534,7 @@ static const u32 cipher_suites[] = {
|
|
|
WLAN_CIPHER_SUITE_TKIP,
|
|
|
WLAN_CIPHER_SUITE_CCMP,
|
|
|
CCKM_KRK_CIPHER_SUITE,
|
|
|
+ WLAN_CIPHER_SUITE_SMS4,
|
|
|
};
|
|
|
|
|
|
static bool is_rate_legacy(s32 rate)
|
|
@@ -1779,7 +1861,7 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,
|
|
|
|
|
|
case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
|
|
|
|
|
|
- ath6kl_cfg80211_stop(ar);
|
|
|
+ ath6kl_cfg80211_stop_all(ar);
|
|
|
|
|
|
/* save the current power mode before enabling power save */
|
|
|
ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
|
|
@@ -1796,7 +1878,7 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,
|
|
|
|
|
|
case ATH6KL_CFG_SUSPEND_CUTPOWER:
|
|
|
|
|
|
- ath6kl_cfg80211_stop(ar);
|
|
|
+ ath6kl_cfg80211_stop_all(ar);
|
|
|
|
|
|
if (ar->state == ATH6KL_STATE_OFF) {
|
|
|
ath6kl_dbg(ATH6KL_DBG_SUSPEND,
|
|
@@ -1816,6 +1898,13 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,
|
|
|
|
|
|
break;
|
|
|
|
|
|
+ case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
|
|
|
+ /*
|
|
|
+ * Nothing needed for schedule scan, firmware is already in
|
|
|
+ * wow mode and sleeping most of the time.
|
|
|
+ */
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
@@ -1864,6 +1953,9 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar)
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
+ case ATH6KL_STATE_SCHED_SCAN:
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
@@ -1987,7 +2079,7 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
|
int ies_len;
|
|
|
struct wmi_connect_cmd p;
|
|
|
int res;
|
|
|
- int i;
|
|
|
+ int i, ret;
|
|
|
|
|
|
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
|
|
|
|
|
@@ -2045,7 +2137,9 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
|
if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
|
|
|
return -EOPNOTSUPP; /* TODO */
|
|
|
|
|
|
- vif->dot11_auth_mode = OPEN_AUTH;
|
|
|
+ ret = ath6kl_set_auth_type(vif, info->auth_type);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
memset(&p, 0, sizeof(p));
|
|
|
|
|
@@ -2081,6 +2175,9 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
|
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) {
|
|
@@ -2100,6 +2197,9 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
|
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;
|
|
@@ -2114,6 +2214,16 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
|
p.dot11_auth_mode = vif->dot11_auth_mode;
|
|
|
p.ch = cpu_to_le16(vif->next_chan);
|
|
|
|
|
|
+ if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
|
|
|
+ p.nw_subtype = SUBTYPE_P2PGO;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * Due to firmware limitation, it is not possible to
|
|
|
+ * do P2P mgmt operations in AP mode
|
|
|
+ */
|
|
|
+ p.nw_subtype = SUBTYPE_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
|
|
|
if (res < 0)
|
|
|
return res;
|
|
@@ -2279,9 +2389,23 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
|
|
}
|
|
|
|
|
|
*cookie = id;
|
|
|
- return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
|
|
|
- chan->center_freq, wait,
|
|
|
- buf, len);
|
|
|
+
|
|
|
+ if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
|
|
|
+ ar->fw_capabilities)) {
|
|
|
+ /*
|
|
|
+ * If capable of doing P2P mgmt operations using
|
|
|
+ * station interface, send additional information like
|
|
|
+ * supported rates to advertise and xmit rates for
|
|
|
+ * probe requests
|
|
|
+ */
|
|
|
+ return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
|
|
|
+ chan->center_freq, wait,
|
|
|
+ buf, len, no_cck);
|
|
|
+ } else {
|
|
|
+ return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
|
|
|
+ chan->center_freq, wait,
|
|
|
+ buf, len);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
|
|
@@ -2302,6 +2426,90 @@ static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
|
|
|
+ struct net_device *dev,
|
|
|
+ struct cfg80211_sched_scan_request *request)
|
|
|
+{
|
|
|
+ struct ath6kl *ar = ath6kl_priv(dev);
|
|
|
+ struct ath6kl_vif *vif = netdev_priv(dev);
|
|
|
+ u16 interval;
|
|
|
+ int ret;
|
|
|
+ u8 i;
|
|
|
+
|
|
|
+ if (ar->state != ATH6KL_STATE_ON)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ if (vif->sme_state != SME_DISCONNECTED)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
|
|
|
+ ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ i, DISABLE_SSID_FLAG,
|
|
|
+ 0, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* fw uses seconds, also make sure that it's >0 */
|
|
|
+ interval = max_t(u16, 1, request->interval / 1000);
|
|
|
+
|
|
|
+ ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ interval, interval,
|
|
|
+ 10, 0, 0, 0, 3, 0, 0, 0);
|
|
|
+
|
|
|
+ if (request->n_ssids && request->ssids[0].ssid_len) {
|
|
|
+ for (i = 0; i < request->n_ssids; i++) {
|
|
|
+ ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ i, SPECIFIC_SSID_FLAG,
|
|
|
+ request->ssids[i].ssid_len,
|
|
|
+ request->ssids[i].ssid);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ ATH6KL_WOW_MODE_ENABLE,
|
|
|
+ WOW_FILTER_SSID,
|
|
|
+ WOW_HOST_REQ_DELAY);
|
|
|
+ if (ret) {
|
|
|
+ ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* this also clears IE in fw if it's not set */
|
|
|
+ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ WMI_FRAME_PROBE_REQ,
|
|
|
+ request->ie, request->ie_len);
|
|
|
+ if (ret) {
|
|
|
+ ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
|
|
|
+ ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ ATH6KL_HOST_MODE_ASLEEP);
|
|
|
+ if (ret) {
|
|
|
+ ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
|
|
|
+ ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ar->state = ATH6KL_STATE_SCHED_SCAN;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
|
|
|
+ struct net_device *dev)
|
|
|
+{
|
|
|
+ struct ath6kl_vif *vif = netdev_priv(dev);
|
|
|
+ bool stopped;
|
|
|
+
|
|
|
+ stopped = __ath6kl_cfg80211_sscan_stop(vif);
|
|
|
+
|
|
|
+ if (!stopped)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static const struct ieee80211_txrx_stypes
|
|
|
ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
|
|
|
[NL80211_IFTYPE_STATION] = {
|
|
@@ -2359,25 +2567,17 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
|
|
|
.cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
|
|
|
.mgmt_tx = ath6kl_mgmt_tx,
|
|
|
.mgmt_frame_register = ath6kl_mgmt_frame_register,
|
|
|
+ .sched_scan_start = ath6kl_cfg80211_sscan_start,
|
|
|
+ .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
|
|
|
};
|
|
|
|
|
|
-void ath6kl_cfg80211_stop(struct ath6kl *ar)
|
|
|
+void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
|
|
|
{
|
|
|
- struct ath6kl_vif *vif;
|
|
|
-
|
|
|
- /* FIXME: for multi vif */
|
|
|
- vif = ath6kl_vif_first(ar);
|
|
|
- if (!vif) {
|
|
|
- /* save the current power mode before enabling power save */
|
|
|
- ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
|
|
|
-
|
|
|
- if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
|
|
|
- ath6kl_warn("ath6kl_deep_sleep_enable: "
|
|
|
- "wmi_powermode_cmd failed\n");
|
|
|
- return;
|
|
|
- }
|
|
|
+ ath6kl_cfg80211_sscan_disable(vif);
|
|
|
|
|
|
switch (vif->sme_state) {
|
|
|
+ case SME_DISCONNECTED:
|
|
|
+ break;
|
|
|
case SME_CONNECTING:
|
|
|
cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
|
|
|
NULL, 0,
|
|
@@ -2385,33 +2585,50 @@ void ath6kl_cfg80211_stop(struct ath6kl *ar)
|
|
|
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.
|
|
|
- */
|
|
|
cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (test_bit(CONNECTED, &vif->flags) ||
|
|
|
test_bit(CONNECT_PEND, &vif->flags))
|
|
|
- ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
|
|
|
+ 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);
|
|
|
|
|
|
/* disable scanning */
|
|
|
- if (ath6kl_wmi_scanparams_cmd(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 (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
|
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
|
|
|
+ ath6kl_warn("failed to disable scan during stop\n");
|
|
|
|
|
|
ath6kl_cfg80211_scan_complete_event(vif, true);
|
|
|
}
|
|
|
|
|
|
+void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
|
|
|
+{
|
|
|
+ struct ath6kl_vif *vif;
|
|
|
+
|
|
|
+ vif = ath6kl_vif_first(ar);
|
|
|
+ if (!vif) {
|
|
|
+ /* save the current power mode before enabling power save */
|
|
|
+ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
|
|
|
+
|
|
|
+ if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
|
|
|
+ ath6kl_warn("ath6kl_deep_sleep_enable: "
|
|
|
+ "wmi_powermode_cmd failed\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * FIXME: we should take ar->list_lock to protect changes in the
|
|
|
+ * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
|
|
|
+ * sleeps.
|
|
|
+ */
|
|
|
+ list_for_each_entry(vif, &ar->vif_list, list)
|
|
|
+ ath6kl_cfg80211_stop(vif);
|
|
|
+}
|
|
|
+
|
|
|
struct ath6kl *ath6kl_core_alloc(struct device *dev)
|
|
|
{
|
|
|
struct ath6kl *ar;
|
|
@@ -2427,17 +2644,12 @@ struct ath6kl *ath6kl_core_alloc(struct device *dev)
|
|
|
}
|
|
|
|
|
|
ar = wiphy_priv(wiphy);
|
|
|
- if (!multi_norm_if_support)
|
|
|
- ar->p2p = !!ath6kl_p2p;
|
|
|
+ ar->p2p = !!ath6kl_p2p;
|
|
|
ar->wiphy = wiphy;
|
|
|
ar->dev = dev;
|
|
|
|
|
|
- if (multi_norm_if_support)
|
|
|
- ar->max_norm_iface = 2;
|
|
|
- else
|
|
|
- ar->max_norm_iface = 1;
|
|
|
+ ar->vif_max = 1;
|
|
|
|
|
|
- /* FIXME: Remove this once the multivif support is enabled */
|
|
|
ar->max_norm_iface = 1;
|
|
|
|
|
|
spin_lock_init(&ar->lock);
|
|
@@ -2459,9 +2671,6 @@ struct ath6kl *ath6kl_core_alloc(struct device *dev)
|
|
|
ar->tx_pwr = 0;
|
|
|
|
|
|
ar->intra_bss = 1;
|
|
|
- memset(&ar->sc_params, 0, sizeof(ar->sc_params));
|
|
|
- ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT;
|
|
|
- ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS;
|
|
|
ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
|
|
|
|
|
|
ar->state = ATH6KL_STATE_OFF;
|
|
@@ -2522,6 +2731,8 @@ int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
|
|
|
wiphy->wowlan.pattern_min_len = 1;
|
|
|
wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
|
|
|
|
|
|
+ wiphy->max_sched_scan_ssids = 10;
|
|
|
+
|
|
|
ret = wiphy_register(wiphy);
|
|
|
if (ret < 0) {
|
|
|
ath6kl_err("couldn't register wiphy device\n");
|
|
@@ -2541,6 +2752,9 @@ static int ath6kl_init_if_data(struct ath6kl_vif *vif)
|
|
|
|
|
|
setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
|
|
|
(unsigned long) vif->ndev);
|
|
|
+ setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
|
|
|
+ (unsigned long) vif);
|
|
|
+
|
|
|
set_bit(WMM_ENABLED, &vif->flags);
|
|
|
spin_lock_init(&vif->if_lock);
|
|
|
|