aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath6kl/cfg80211.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2012-07-04 13:13:55 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2012-07-04 13:13:55 -0700
commit404c3bc30cb1361e1b3533643326ab472d24a618 (patch)
tree156cc9032c8aee17167d926c5bdae009ba8f36d2 /drivers/net/wireless/ath/ath6kl/cfg80211.c
parentInput: wacom - TPC2FG doesn't store touch id for slots (diff)
parentLinux 3.5-rc5 (diff)
downloadlinux-dev-404c3bc30cb1361e1b3533643326ab472d24a618.tar.xz
linux-dev-404c3bc30cb1361e1b3533643326ab472d24a618.zip
Merge commit 'v3.5-rc5' into next
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/cfg80211.c')
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c621
1 files changed, 490 insertions, 131 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 00d38952b5fb..b869a358ce43 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -15,6 +15,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/moduleparam.h>
#include <linux/inetdevice.h>
#include <linux/export.h>
@@ -49,6 +51,8 @@
.max_power = 30, \
}
+#define DEFAULT_BG_SCAN_PERIOD 60
+
static struct ieee80211_rate ath6kl_rates[] = {
RATETAB_ENT(10, 0x1, 0),
RATETAB_ENT(20, 0x2, 0),
@@ -69,7 +73,8 @@ static struct ieee80211_rate ath6kl_rates[] = {
#define ath6kl_g_rates (ath6kl_rates + 0)
#define ath6kl_g_rates_size 12
-#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
+#define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20
+#define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
IEEE80211_HT_CAP_SGI_20 | \
IEEE80211_HT_CAP_SGI_40)
@@ -126,7 +131,7 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = {
.channels = ath6kl_5ghz_a_channels,
.n_bitrates = ath6kl_a_rates_size,
.bitrates = ath6kl_a_rates,
- .ht_cap.cap = ath6kl_g_htcap,
+ .ht_cap.cap = ath6kl_a_htcap,
.ht_cap.ht_supported = true,
};
@@ -607,6 +612,17 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
vif->req_bssid, vif->ch_hint,
ar->connect_ctrl_flags, nw_subtype);
+ /* disable background scan if period is 0 */
+ if (sme->bg_scan_period == 0)
+ sme->bg_scan_period = 0xffff;
+
+ /* configure default value if not specified */
+ if (sme->bg_scan_period == -1)
+ sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+
+ ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0,
+ sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
+
up(&ar->sem);
if (status == -EINVAL) {
@@ -677,8 +693,8 @@ ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
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);
+ 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");
@@ -866,6 +882,32 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
vif->sme_state = SME_DISCONNECTED;
}
+static int ath6kl_set_probed_ssids(struct ath6kl *ar,
+ struct ath6kl_vif *vif,
+ struct cfg80211_ssid *ssids, int n_ssids)
+{
+ u8 i;
+
+ if (n_ssids > MAX_PROBED_SSID_INDEX)
+ return -EINVAL;
+
+ for (i = 0; i < n_ssids; i++) {
+ ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
+ ssids[i].ssid_len ?
+ SPECIFIC_SSID_FLAG : ANY_SSID_FLAG,
+ ssids[i].ssid_len,
+ ssids[i].ssid);
+ }
+
+ /* Make sure no old entries are left behind */
+ for (i = n_ssids; i < MAX_PROBED_SSID_INDEX; i++) {
+ ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
+ DISABLE_SSID_FLAG, 0, NULL);
+ }
+
+ return 0;
+}
+
static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_scan_request *request)
{
@@ -883,36 +925,25 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
if (!ar->usr_bss_filter) {
clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
- ret = ath6kl_wmi_bssfilter_cmd(
- ar->wmi, vif->fw_vif_idx,
- (test_bit(CONNECTED, &vif->flags) ?
- ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
+ 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");
return ret;
}
}
- if (request->n_ssids && request->ssids[0].ssid_len) {
- 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);
- }
+ ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
+ request->n_ssids);
+ if (ret < 0)
+ 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_err("failed to set Probe Request appie for "
- "scan");
+ ath6kl_err("failed to set Probe Request appie for scan");
return ret;
}
@@ -929,8 +960,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
if (channels == NULL) {
- ath6kl_warn("failed to set scan channels, "
- "scan all channels");
+ ath6kl_warn("failed to set scan channels, scan all channels");
n_channels = 0;
}
@@ -941,6 +971,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
if (test_bit(CONNECTED, &vif->flags))
force_fg_scan = 1;
+ vif->scan_req = request;
+
if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
ar->fw_capabilities)) {
/*
@@ -963,10 +995,10 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
ATH6KL_FG_SCAN_INTERVAL,
n_channels, channels);
}
- if (ret)
+ if (ret) {
ath6kl_err("wmi_startscan_cmd failed\n");
- else
- vif->scan_req = request;
+ vif->scan_req = NULL;
+ }
kfree(channels);
@@ -1000,6 +1032,20 @@ out:
vif->scan_req = NULL;
}
+void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
+ enum wmi_phy_mode mode)
+{
+ enum nl80211_channel_type type;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "channel switch notify nw_type %d freq %d mode %d\n",
+ vif->nw_type, freq, mode);
+
+ type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT;
+
+ cfg80211_ch_switch_notify(vif->ndev, freq, type);
+}
+
static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr,
@@ -1093,9 +1139,8 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
ar->ap_mode_bkey.key_len = key->key_len;
memcpy(ar->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");
+ 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.
@@ -1111,8 +1156,8 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
* 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");
+ 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);
@@ -1436,9 +1481,38 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
struct vif_params *params)
{
struct ath6kl_vif *vif = netdev_priv(ndev);
+ int i;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
+ /*
+ * Don't bring up p2p on an interface which is not initialized
+ * for p2p operation where fw does not have capability to switch
+ * dynamically between non-p2p and p2p type interface.
+ */
+ if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+ vif->ar->fw_capabilities) &&
+ (type == NL80211_IFTYPE_P2P_CLIENT ||
+ type == NL80211_IFTYPE_P2P_GO)) {
+ if (vif->ar->vif_max == 1) {
+ if (vif->fw_vif_idx != 0)
+ return -EINVAL;
+ else
+ goto set_iface_type;
+ }
+
+ for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) {
+ if (i == vif->fw_vif_idx)
+ break;
+ }
+
+ if (i == vif->ar->vif_max) {
+ ath6kl_err("Invalid interface to bring up P2P\n");
+ return -EINVAL;
+ }
+ }
+
+set_iface_type:
switch (type) {
case NL80211_IFTYPE_STATION:
vif->next_mode = INFRA_NETWORK;
@@ -1915,8 +1989,7 @@ static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
sizeof(discvr_pattern), discvr_offset,
discvr_pattern, discvr_mask);
if (ret) {
- ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
- "pattern\n");
+ ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
return ret;
}
}
@@ -1924,17 +1997,70 @@ static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
return 0;
}
+static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
+{
+ return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+}
+
+static bool is_ctrl_ep_empty(struct ath6kl *ar)
+{
+ return !ar->tx_pending[ar->ctrl_ep];
+}
+
+static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+ int ret, left;
+
+ 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,
+ is_hsleep_mode_procsed(vif),
+ 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,
+ is_ctrl_ep_empty(ar),
+ 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;
+ }
+ }
+
+ return ret;
+}
+
static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
{
struct in_device *in_dev;
struct in_ifaddr *ifa;
struct ath6kl_vif *vif;
- int ret, left;
+ int ret;
u32 filter = 0;
u16 i, bmiss_time;
u8 index = 0;
__be32 ips[MAX_IP_ADDRS];
+ /* The FW currently can't support multi-vif WoW properly. */
+ if (ar->num_vif > 1)
+ return -EIO;
+
vif = ath6kl_vif_first(ar);
if (!vif)
return -EIO;
@@ -1948,6 +2074,13 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
return -EINVAL;
+ if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) {
+ ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
+ vif->fw_vif_idx, false);
+ if (ret)
+ return ret;
+ }
+
/* 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,
@@ -2030,39 +2163,11 @@ skip_arp:
if (ret)
return ret;
- 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);
+ ret = ath6kl_cfg80211_host_sleep(ar, vif);
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;
- }
- }
-
- return ret;
+ return 0;
}
static int ath6kl_wow_resume(struct ath6kl *ar)
@@ -2079,8 +2184,8 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_HOST_MODE_AWAKE);
if (ret) {
- ath6kl_warn("Failed to configure host sleep mode for "
- "wow resume: %d\n", ret);
+ ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
+ ret);
ar->state = ATH6KL_STATE_WOW;
return ret;
}
@@ -2104,15 +2209,96 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
ar->state = ATH6KL_STATE_ON;
+ if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags)) {
+ ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
+ vif->fw_vif_idx, true);
+ if (ret)
+ return ret;
+ }
+
netif_wake_queue(vif->ndev);
return 0;
}
+static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
+{
+ struct ath6kl_vif *vif;
+ int ret;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ if (!test_bit(WMI_READY, &ar->flag)) {
+ ath6kl_err("deepsleep failed as wmi is not ready\n");
+ return -EIO;
+ }
+
+ ath6kl_cfg80211_stop_all(ar);
+
+ /* Save the current power mode before enabling power save */
+ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
+
+ ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
+ if (ret)
+ return ret;
+
+ /* Disable WOW mode */
+ ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
+ ATH6KL_WOW_MODE_DISABLE,
+ 0, 0);
+ if (ret)
+ return ret;
+
+ /* Flush all non control pkts in TX path */
+ ath6kl_tx_data_cleanup(ar);
+
+ ret = ath6kl_cfg80211_host_sleep(ar, vif);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
+{
+ struct ath6kl_vif *vif;
+ int ret;
+
+ vif = ath6kl_vif_first(ar);
+
+ if (!vif)
+ return -EIO;
+
+ if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
+ ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
+ ar->wmi->saved_pwr_mode);
+ if (ret)
+ return ret;
+ }
+
+ ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
+ ATH6KL_HOST_MODE_AWAKE);
+ if (ret)
+ return ret;
+
+ ar->state = ATH6KL_STATE_ON;
+
+ /* Reset scan parameter to default values */
+ ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
+ 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
int ath6kl_cfg80211_suspend(struct ath6kl *ar,
enum ath6kl_cfg_suspend_mode mode,
struct cfg80211_wowlan *wow)
{
+ struct ath6kl_vif *vif;
enum ath6kl_state prev_state;
int ret;
@@ -2137,15 +2323,12 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,
case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
- ath6kl_cfg80211_stop_all(ar);
-
- /* save the current power mode before enabling power save */
- ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
- ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
+ ret = ath6kl_cfg80211_deepsleep_suspend(ar);
if (ret) {
- ath6kl_warn("wmi powermode command failed during suspend: %d\n",
- ret);
+ ath6kl_err("deepsleep suspend failed: %d\n", ret);
+ return ret;
}
ar->state = ATH6KL_STATE_DEEPSLEEP;
@@ -2185,6 +2368,9 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,
break;
}
+ list_for_each_entry(vif, &ar->vif_list, list)
+ ath6kl_cfg80211_scan_complete_event(vif, true);
+
return 0;
}
EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
@@ -2206,17 +2392,13 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar)
break;
case ATH6KL_STATE_DEEPSLEEP:
- if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
- ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
- ar->wmi->saved_pwr_mode);
- if (ret) {
- ath6kl_warn("wmi powermode command failed during resume: %d\n",
- ret);
- }
- }
-
- ar->state = ATH6KL_STATE_ON;
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
+ ret = ath6kl_cfg80211_deepsleep_resume(ar);
+ if (ret) {
+ ath6kl_warn("deep sleep resume failed: %d\n", ret);
+ return ret;
+ }
break;
case ATH6KL_STATE_CUTPOWER:
@@ -2290,31 +2472,43 @@ void ath6kl_check_wow_status(struct ath6kl *ar)
}
#endif
-static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
+ bool ht_enable)
{
- struct ath6kl_vif *vif;
+ struct ath6kl_htcap *htcap = &vif->htcap;
- /*
- * 'dev' could be NULL if a channel change is required for the hardware
- * device itself, instead of a particular VIF.
- *
- * FIXME: To be handled properly when monitor mode is supported.
- */
- if (!dev)
- return -EBUSY;
+ if (htcap->ht_enable == ht_enable)
+ return 0;
- vif = netdev_priv(dev);
+ if (ht_enable) {
+ /* Set default ht capabilities */
+ htcap->ht_enable = true;
+ htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
+ ath6kl_g_htcap : ath6kl_a_htcap;
+ htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
+ } else /* Disable ht */
+ memset(htcap, 0, sizeof(*htcap));
- if (!ath6kl_cfg80211_ready(vif))
- return -EIO;
+ return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
+ band, htcap);
+}
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
- __func__, chan->center_freq, chan->hw_value);
- vif->next_chan = chan->center_freq;
+static int ath6kl_restore_htcap(struct ath6kl_vif *vif)
+{
+ struct wiphy *wiphy = vif->ar->wiphy;
+ int band, ret = 0;
- return 0;
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ if (!wiphy->bands[band])
+ continue;
+
+ ret = ath6kl_set_htcap(vif, band,
+ wiphy->bands[band]->ht_cap.ht_supported);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
}
static bool ath6kl_is_p2p_ie(const u8 *pos)
@@ -2391,6 +2585,87 @@ static int ath6kl_set_ies(struct ath6kl_vif *vif,
return 0;
}
+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;
+
+ /*
+ * 'dev' could be NULL if a channel change is required for the hardware
+ * device itself, instead of a particular VIF.
+ *
+ * FIXME: To be handled properly when monitor mode is supported.
+ */
+ if (!dev)
+ return -EBUSY;
+
+ vif = netdev_priv(dev);
+
+ if (!ath6kl_cfg80211_ready(vif))
+ return -EIO;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
+ __func__, chan->center_freq, chan->hw_value);
+ vif->next_chan = chan->center_freq;
+ vif->next_ch_type = channel_type;
+ vif->next_ch_band = chan->band;
+
+ return 0;
+}
+
+static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
+ u8 *rsn_capab)
+{
+ const u8 *rsn_ie;
+ size_t rsn_ie_len;
+ u16 cnt;
+
+ if (!beacon->tail)
+ return -EINVAL;
+
+ rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
+ if (!rsn_ie)
+ return -EINVAL;
+
+ rsn_ie_len = *(rsn_ie + 1);
+ /* skip element id and length */
+ rsn_ie += 2;
+
+ /* skip version */
+ if (rsn_ie_len < 2)
+ return -EINVAL;
+ rsn_ie += 2;
+ rsn_ie_len -= 2;
+
+ /* skip group cipher suite */
+ if (rsn_ie_len < 4)
+ return 0;
+ rsn_ie += 4;
+ rsn_ie_len -= 4;
+
+ /* skip pairwise cipher suite */
+ if (rsn_ie_len < 2)
+ return 0;
+ cnt = get_unaligned_le16(rsn_ie);
+ rsn_ie += (2 + cnt * 4);
+ rsn_ie_len -= (2 + cnt * 4);
+
+ /* skip akm suite */
+ if (rsn_ie_len < 2)
+ return 0;
+ cnt = get_unaligned_le16(rsn_ie);
+ rsn_ie += (2 + cnt * 4);
+ rsn_ie_len -= (2 + cnt * 4);
+
+ if (rsn_ie_len < 2)
+ return 0;
+
+ memcpy(rsn_capab, rsn_ie, 2);
+
+ return 0;
+}
+
static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ap_settings *info)
{
@@ -2403,6 +2678,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
struct wmi_connect_cmd p;
int res;
int i, ret;
+ u16 rsn_capab = 0;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
@@ -2532,6 +2808,35 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
p.nw_subtype = SUBTYPE_NONE;
}
+ if (info->inactivity_timeout) {
+ res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
+ info->inactivity_timeout);
+ if (res < 0)
+ return res;
+ }
+
+ if (ath6kl_set_htcap(vif, vif->next_ch_band,
+ vif->next_ch_type != NL80211_CHAN_NO_HT))
+ return -EIO;
+
+ /*
+ * Get the PTKSA replay counter in the RSN IE. Supplicant
+ * will use the RSN IE in M3 message and firmware has to
+ * advertise the same in beacon/probe response. Send
+ * the complete RSN IE capability field to firmware
+ */
+ if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
+ test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+ ar->fw_capabilities)) {
+ res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
+ WLAN_EID_RSN, WMI_RSN_IE_CAPB,
+ (const u8 *) &rsn_capab,
+ sizeof(rsn_capab));
+ if (res < 0)
+ return res;
+ }
+
+ memcpy(&vif->profile, &p, sizeof(p));
res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
if (res < 0)
return res;
@@ -2566,7 +2871,8 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
clear_bit(CONNECTED, &vif->flags);
- return 0;
+ /* Restore ht setting in firmware */
+ return ath6kl_restore_htcap(vif);
}
static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -2747,6 +3053,21 @@ static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
return false;
}
+/* Check if SSID length is greater than DIRECT- */
+static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ mgmt = (const struct ieee80211_mgmt *) buf;
+
+ /* variable[1] contains the SSID tag length */
+ if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
+ (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
+ return true;
+ }
+
+ return false;
+}
+
static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
@@ -2761,11 +3082,11 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
bool more_data, queued;
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)) {
+ if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
+ ieee80211_is_probe_resp(mgmt->frame_control) &&
+ ath6kl_is_p2p_go_ssid(buf, len)) {
/*
- * Send Probe Response frame in AP mode using a separate WMI
+ * Send Probe Response frame in GO mode using a separate WMI
* command to allow the target to fill in the generic IEs.
*/
*cookie = 0; /* TX status not supported */
@@ -2825,7 +3146,6 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
struct ath6kl_vif *vif = netdev_priv(dev);
u16 interval;
int ret;
- u8 i;
if (ar->state != ATH6KL_STATE_ON)
return -EIO;
@@ -2833,27 +3153,23 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
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);
- }
+ /* The FW currently can't support multi-vif WoW properly. */
+ if (ar->num_vif > 1)
+ return -EIO;
+
+ ath6kl_cfg80211_scan_complete_event(vif, true);
+
+ ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
+ request->n_ssids);
+ if (ret < 0)
+ return ret;
/* 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);
- }
- }
+ vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_WOW_MODE_ENABLE,
@@ -3013,8 +3329,7 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
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");
+ ath6kl_warn("ath6kl_deep_sleep_enable: wmi_powermode_cmd failed\n");
return;
}
@@ -3094,6 +3409,8 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
vif->next_mode = nw_type;
vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
+ vif->bg_scan_period = 0;
+ vif->htcap.ht_enable = true;
memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
if (fw_vif_idx != 0)
@@ -3134,6 +3451,7 @@ err:
int ath6kl_cfg80211_init(struct ath6kl *ar)
{
struct wiphy *wiphy = ar->wiphy;
+ bool band_2gig = false, band_5gig = false, ht = false;
int ret;
wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
@@ -3154,8 +3472,46 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
/* max num of ssids that can be probed during scanning */
wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
- wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
- wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
+ switch (ar->hw.cap) {
+ case WMI_11AN_CAP:
+ ht = true;
+ case WMI_11A_CAP:
+ band_5gig = true;
+ break;
+ case WMI_11GN_CAP:
+ ht = true;
+ case WMI_11G_CAP:
+ band_2gig = true;
+ break;
+ case WMI_11AGN_CAP:
+ ht = true;
+ case WMI_11AG_CAP:
+ band_2gig = true;
+ band_5gig = true;
+ break;
+ default:
+ ath6kl_err("invalid phy capability!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Even if the fw has HT support, advertise HT cap only when
+ * the firmware has support to override RSN capability, otherwise
+ * 4-way handshake would fail.
+ */
+ if (!(ht &&
+ test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+ ar->fw_capabilities))) {
+ ath6kl_band_2ghz.ht_cap.cap = 0;
+ ath6kl_band_2ghz.ht_cap.ht_supported = false;
+ ath6kl_band_5ghz.ht_cap.cap = 0;
+ ath6kl_band_5ghz.ht_cap.ht_supported = false;
+ }
+ if (band_2gig)
+ wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
+ if (band_5gig)
+ wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
+
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->cipher_suites = cipher_suites;
@@ -3171,7 +3527,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
wiphy->wowlan.pattern_min_len = 1;
wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
- wiphy->max_sched_scan_ssids = 10;
+ wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX;
ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
WIPHY_FLAG_HAVE_AP_SME |
@@ -3181,11 +3537,14 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+ if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
+ ar->fw_capabilities))
+ ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER;
+
ar->wiphy->probe_resp_offload =
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
- NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
- NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
ret = wiphy_register(wiphy);
if (ret < 0) {