M7350/external/compat-wireless/drivers/net/wireless/ath/ath6kl-3.5/testmode.c

885 lines
20 KiB
C
Raw Normal View History

2024-09-09 08:57:42 +00:00
/*
* Copyright (c) 2010-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 "testmode.h"
#include <net/netlink.h>
#include "core.h"
/*
* netlink.h remove these macros from kernel 3.5.
* TODO : Error handle for nla_put_XXX calls.
*/
#ifndef NLA_PUT
#define NLA_PUT_U32 nla_put_u32
#define NLA_PUT nla_put
#else
#define _NLA_PUT_ERR_RTN
#endif
enum ath6kl_tm_attr {
__ATH6KL_TM_ATTR_INVALID = 0,
ATH6KL_TM_ATTR_CMD = 1,
ATH6KL_TM_ATTR_DATA = 2,
ATH6KL_TM_ATTR_TYPE = 3,
/* keep last */
__ATH6KL_TM_ATTR_AFTER_LAST,
ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1,
};
enum ath6kl_tm_cmd {
ATH6KL_TM_CMD_TCMD = 0,
ATH6KL_TM_CMD_WLAN_HB = 1,
ATH6KL_TM_CMD_WIFI_DISC = 2,
ATH6KL_TM_CMD_WIFI_KTK = 3,
ATH6KL_TM_CMD_DFS_SKIP = 4,
};
#define ATH6KL_TM_DATA_MAX_LEN 5000
static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = {
[ATH6KL_TM_ATTR_CMD] = { .type = NLA_U32 },
[ATH6KL_TM_ATTR_DATA] = { .type = NLA_BINARY,
.len = ATH6KL_TM_DATA_MAX_LEN },
};
#ifdef CONFIG_NL80211_TESTMODE
void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len)
{
if (down_interruptible(&ar->sem))
return;
kfree(ar->tm.rx_report);
ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL);
ar->tm.rx_report_len = buf_len;
up(&ar->sem);
wake_up(&ar->event_wq);
}
void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len)
{
struct sk_buff *skb;
if (!buf || buf_len == 0) {
printk(KERN_ERR "buf buflen is empty\n");
return;
}
skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "failed to allocate testmode rx skb!\n");
return;
}
NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD);
NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf);
cfg80211_testmode_event(skb, GFP_ATOMIC);
return;
#ifdef _NLA_PUT_ERR_RTN
nla_put_failure:
kfree_skb(skb);
printk(KERN_ERR "nla_put failed on testmode rx skb!\n");
#endif
}
#ifdef ATH6KL_SUPPORT_WLAN_HB
void ath6kl_wlan_hb_event(struct ath6kl *ar, u8 value,
void *buf, size_t buf_len)
{
struct sk_buff *skb;
if (!buf || buf_len == 0) {
printk(KERN_ERR "buf buflen is empty\n");
return;
}
skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "failed to allocate testmode event skb!\n");
return;
}
NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_WLAN_HB);
NLA_PUT_U32(skb, ATH6KL_TM_ATTR_TYPE, value);
NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf);
cfg80211_testmode_event(skb, GFP_ATOMIC);
return;
#ifdef _NLA_PUT_ERR_RTN
nla_put_failure:
kfree_skb(skb);
printk(KERN_ERR "nla_put failed on testmode event skb!\n");
#endif
}
#endif
#ifdef ATH6KL_SUPPORT_WIFI_DISC
void ath6kl_tm_disc_event(struct ath6kl *ar, void *buf, size_t buf_len)
{
struct sk_buff *skb;
if (!buf || buf_len == 0) {
printk(KERN_ERR "buf buflen is empty\n");
return;
}
skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "failed to allocate testmode event skb!\n");
return;
}
NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_WIFI_DISC);
NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf);
cfg80211_testmode_event(skb, GFP_ATOMIC);
return;
#ifdef _NLA_PUT_ERR_RTN
nla_put_failure:
kfree_skb(skb);
printk(KERN_ERR "nla_put failed on testmode event skb!\n");
#endif
}
#endif
static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len,
struct sk_buff *skb)
{
int ret = 0;
long left;
if (!test_bit(WMI_READY, &ar->flag)) {
ret = -EIO;
goto out;
}
if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
ret = -EBUSY;
goto out;
}
if (down_interruptible(&ar->sem))
return -EIO;
if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) {
up(&ar->sem);
return -EIO;
}
left = wait_event_interruptible_timeout(ar->event_wq,
ar->tm.rx_report != NULL,
WMI_TIMEOUT);
if (left == 0) {
ret = -ETIMEDOUT;
goto out;
} else if (left < 0) {
ret = left;
goto out;
}
if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) {
ret = -EINVAL;
goto out;
}
NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len,
ar->tm.rx_report);
kfree(ar->tm.rx_report);
ar->tm.rx_report = NULL;
out:
up(&ar->sem);
return ret;
#ifdef _NLA_PUT_ERR_RTN
nla_put_failure:
ret = -ENOBUFS;
goto out;
#endif
}
EXPORT_SYMBOL(ath6kl_tm_rx_report);
#ifdef ATH6KL_SUPPORT_WLAN_HB
enum nl80211_wlan_hb_cmd {
NL80211_WLAN_HB_ENABLE = 0,
NL80211_WLAN_TCP_PARAMS = 1,
NL80211_WLAN_TCP_FILTER = 2,
NL80211_WLAN_UDP_PARAMS = 3,
NL80211_WLAN_UDP_FILTER = 4,
};
#define WLAN_HB_UDP 0x1
#define WLAN_HB_TCP 0x2
struct wlan_hb_params {
u16 cmd;
u16 dummy;
union {
struct {
u8 enable;
u8 item;
u8 session;
} hb_params;
struct {
u32 srv_ip;
u32 dev_ip;
u32 seq;
u16 src_port;
u16 dst_port;
u16 interval;
u16 timeout;
u8 session;
u8 gateway_mac[ETH_ALEN];
} tcp_params;
struct {
u16 length;
u8 offset;
u8 session;
u8 filter[64];
} tcp_filter;
struct {
u32 srv_ip;
u32 dev_ip;
u16 src_port;
u16 dst_port;
u16 interval;
u16 timeout;
u8 session;
u8 gateway_mac[ETH_ALEN];
} udp_params;
struct {
u16 length;
u8 offset;
u8 session;
u8 filter[64];
} udp_filter;
} params;
};
#endif
#ifdef ATH6KL_SUPPORT_WIFI_DISC
#define WLAN_WIFI_DISC_MAX_IE_SIZE 200
enum nl80211_wifi_disc_cmd {
NL80211_WIFI_DISC_IE = 0,
NL80211_WIFI_DISC_IE_FILTER = 1,
NL80211_WIFI_DISC_START = 2,
NL80211_WIFI_DISC_STOP = 3,
};
struct wifi_disc_params {
u16 cmd;
union {
struct {
u16 length;
u8 ie[WLAN_WIFI_DISC_MAX_IE_SIZE];
} ie_params;
struct {
u16 enable;
u16 startPos;
u16 length;
u8 filter[WLAN_WIFI_DISC_MAX_IE_SIZE];
} ie_filter_params;
struct {
u16 channel;
u16 dwellTime;
u16 sleepTime;
u16 random;
u16 numPeers;
u16 peerTimeout;
u16 txPower;
} start_params;
} params;
};
#endif
#ifdef ATH6KL_SUPPORT_WIFI_KTK
#define WLAN_WIFI_KTK_MAX_IE_SIZE 200
enum nl80211_wifi_ktk_cmd {
NL80211_WIFI_KTK_IE = 0,
NL80211_WIFI_KTK_IE_FILTER = 1,
NL80211_WIFI_KTK_START = 2,
NL80211_WIFI_KTK_STOP = 3,
};
struct wifi_ktk_params {
u16 cmd;
union {
struct {
u16 length;
u8 ie[WLAN_WIFI_KTK_MAX_IE_SIZE];
} ie_params;
struct {
u16 enable;
u16 startPos;
u16 length;
u8 filter[WLAN_WIFI_KTK_MAX_IE_SIZE];
} ie_filter_params;
struct {
u16 ssid_len;
u8 ssid[32];
u8 passphrase[16];
} start_params;
} params;
};
#endif
struct wifi_dfs_skip_params {
u16 enable;
};
int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
{
struct ath6kl *ar = wiphy_priv(wiphy);
struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1];
int err, buf_len;
void *buf;
err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len,
ath6kl_tm_policy);
if (err)
return err;
if (!tb[ATH6KL_TM_ATTR_CMD])
return -EINVAL;
switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) {
case ATH6KL_TM_CMD_TCMD:
if (!tb[ATH6KL_TM_ATTR_DATA])
return -EINVAL;
buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len);
return 0;
break;
#ifdef ATH6KL_SUPPORT_WLAN_HB
case ATH6KL_TM_CMD_WLAN_HB:
{
struct wlan_hb_params *hb_params;
struct ath6kl_vif *vif;
vif = ath6kl_vif_first(ar);
if (!vif)
return -EINVAL;
if (!tb[ATH6KL_TM_ATTR_DATA]) {
printk(KERN_ERR "%s: NO DATA\n", __func__);
return -EINVAL;
}
buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
hb_params = (struct wlan_hb_params *)buf;
if (hb_params->cmd == NL80211_WLAN_HB_ENABLE) {
if (hb_params->params.hb_params.enable != 0) {
if (ath6kl_enable_wow_hb(ar)) {
printk(KERN_ERR
"%s: enable hb wow fail\n",
__func__);
return -EINVAL;
}
if (hb_params->params.hb_params.item
== WLAN_HB_TCP) {
if (ath6kl_wmi_set_heart_beat_params(
ar->wmi,
vif->fw_vif_idx,
1,
WLAN_HB_TCP,
hb_params->params.hb_params.session)) {
printk(KERN_ERR
"%s: set heart beat enable fail\n",
__func__);
return -EINVAL;
}
} else if (hb_params->params.hb_params.item
== WLAN_HB_UDP) {
if (ath6kl_wmi_set_heart_beat_params(
ar->wmi,
vif->fw_vif_idx,
1,
WLAN_HB_UDP,
hb_params->params.hb_params.session)) {
printk(KERN_ERR
"%s: set heart beat enable fail\n",
__func__);
return -EINVAL;
}
}
} else {
#ifdef CONFIG_ANDROID
if (ath6kl_android_enable_wow_default(ar)) {
printk(KERN_ERR
"%s: enable android defualt wow fail\n",
__func__);
}
#endif
if (hb_params->params.hb_params.item
== WLAN_HB_TCP) {
if (ath6kl_wmi_set_heart_beat_params(
ar->wmi,
vif->fw_vif_idx,
0,
WLAN_HB_TCP,
hb_params->params.hb_params.session)) {
printk(KERN_ERR
"%s: set heart beat disable fail\n",
__func__);
return -EINVAL;
}
} else if (hb_params->params.hb_params.item
== WLAN_HB_UDP) {
if (ath6kl_wmi_set_heart_beat_params(
ar->wmi,
vif->fw_vif_idx,
0,
WLAN_HB_UDP,
hb_params->params.hb_params.session)) {
printk(KERN_ERR
"%s: set heart beat disable fail\n",
__func__);
return -EINVAL;
}
}
}
} else if (hb_params->cmd == NL80211_WLAN_TCP_PARAMS) {
if (ath6kl_wmi_heart_beat_set_tcp_params(ar->wmi,
vif->fw_vif_idx,
hb_params->params.tcp_params.src_port,
hb_params->params.tcp_params.dst_port,
hb_params->params.tcp_params.srv_ip,
hb_params->params.tcp_params.dev_ip,
hb_params->params.tcp_params.seq,
hb_params->params.udp_params.interval,
hb_params->params.tcp_params.timeout,
hb_params->params.tcp_params.session,
hb_params->params.tcp_params.gateway_mac)) {
printk(KERN_ERR
"%s: set heart beat tcp params fail\n",
__func__);
return -EINVAL;
}
} else if (hb_params->cmd == NL80211_WLAN_TCP_FILTER) {
if (hb_params->params.tcp_filter.length
> WMI_MAX_TCP_FILTER_SIZE) {
printk(KERN_ERR "%s: size of tcp filter is too large: %d\n",
__func__,
hb_params->params.tcp_filter.length);
return -E2BIG;
}
if (ath6kl_wmi_heart_beat_set_tcp_filter(ar->wmi,
vif->fw_vif_idx,
hb_params->params.tcp_filter.filter,
hb_params->params.tcp_filter.length,
hb_params->params.tcp_filter.offset,
hb_params->params.tcp_filter.session)) {
printk(KERN_ERR
"%s: set heart beat tcp filter fail\n",
__func__);
return -EINVAL;
}
} else if (hb_params->cmd == NL80211_WLAN_UDP_PARAMS) {
if (ath6kl_wmi_heart_beat_set_udp_params(ar->wmi,
vif->fw_vif_idx,
hb_params->params.udp_params.src_port,
hb_params->params.udp_params.dst_port,
hb_params->params.udp_params.srv_ip,
hb_params->params.udp_params.dev_ip,
hb_params->params.udp_params.interval,
hb_params->params.udp_params.timeout,
hb_params->params.udp_params.session,
hb_params->params.udp_params.gateway_mac)) {
printk(KERN_ERR
"%s: set heart beat udp params fail\n",
__func__);
return -EINVAL;
}
} else if (hb_params->cmd == NL80211_WLAN_UDP_FILTER) {
if (hb_params->params.udp_filter.length
> WMI_MAX_UDP_FILTER_SIZE) {
printk(KERN_ERR
"%s: size of udp filter is too large: %d\n",
__func__,
hb_params->params.udp_filter.length);
return -E2BIG;
}
if (ath6kl_wmi_heart_beat_set_udp_filter(ar->wmi,
vif->fw_vif_idx,
hb_params->params.udp_filter.filter,
hb_params->params.udp_filter.length,
hb_params->params.udp_filter.offset,
hb_params->params.udp_filter.session)) {
printk(KERN_ERR
"%s: set heart beat udp filter fail\n",
__func__);
return -EINVAL;
}
}
}
return 0;
break;
#endif
#ifdef ATH6KL_SUPPORT_WIFI_DISC
case ATH6KL_TM_CMD_WIFI_DISC:
{
struct wifi_disc_params *disc_params;
struct ath6kl_vif *vif;
vif = ath6kl_vif_first(ar);
if (!vif)
return -EINVAL;
if (!tb[ATH6KL_TM_ATTR_DATA]) {
printk(KERN_ERR "%s: NO DATA\n", __func__);
return -EINVAL;
}
buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
disc_params = (struct wifi_disc_params *)buf;
if (disc_params->cmd == NL80211_WIFI_DISC_IE) {
u8 ie_hdr[6] = {0xDD, 0x00, 0x00, 0x03, 0x7f, 0x00};
u8 *ie = NULL;
u16 ie_length = disc_params->params.ie_params.length;
ie = kmalloc(ie_length+6, GFP_KERNEL);
if (ie == NULL)
return -ENOMEM;
memcpy(ie, ie_hdr, 6);
ie[1] = ie_length+4;
memcpy(ie+6,
disc_params->params.ie_params.ie,
ie_length);
if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_PROBE_REQ, ie, ie_length+6)) {
kfree(ie);
printk(KERN_ERR
"%s: wifi discovery set probe request ie fail\n",
__func__);
return -EINVAL;
}
if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_PROBE_RESP, ie, ie_length+6)) {
kfree(ie);
printk(KERN_ERR
"%s: wifi discovery set probe response ie fail\n",
__func__);
return -EINVAL;
}
kfree(ie);
} else if (disc_params->cmd == NL80211_WIFI_DISC_IE_FILTER) {
if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx,
disc_params->params.ie_filter_params.enable,
disc_params->params.ie_filter_params.startPos,
disc_params->params.ie_filter_params.filter,
disc_params->params.ie_filter_params.length)) {
printk(KERN_ERR
"%s: wifi discovery set ie filter fail\n",
__func__);
return -EINVAL;
}
} else if (disc_params->cmd == NL80211_WIFI_DISC_START) {
int band, freq, numPeers, random;
if (disc_params->params.start_params.channel <= 14)
band = IEEE80211_BAND_2GHZ;
else
band = IEEE80211_BAND_5GHZ;
freq = ieee80211_channel_to_frequency(
disc_params->params.start_params.channel, band);
if (!freq) {
printk(KERN_ERR "%s: wifi discovery start channel %d error\n",
__func__,
disc_params->params.start_params.channel);
return -EINVAL;
}
if (disc_params->params.start_params.numPeers == 0)
numPeers = 1;
else if (disc_params->params.start_params.numPeers > 4)
numPeers = 4;
else
numPeers =
disc_params->params.start_params.numPeers;
random = (disc_params->params.start_params.random == 0)
? 100 : disc_params->params.start_params.random;
if (disc_params->params.start_params.txPower)
ath6kl_wmi_set_tx_pwr_cmd(ar->wmi,
vif->fw_vif_idx,
disc_params->params.start_params.txPower);
/* disable scanning */
ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
0xFFFF, 0, 0,
0, 0, 0, 0, 0, 0, 0);
if (ath6kl_wmi_disc_mode_cmd(ar->wmi,
vif->fw_vif_idx, 1, freq,
disc_params->params.start_params.dwellTime,
disc_params->params.start_params.sleepTime,
random, numPeers,
disc_params->params.start_params.peerTimeout
)) {
printk(KERN_ERR "%s: wifi discovery start fail\n",
__func__);
return -EINVAL;
}
/* change disc state to active */
ar->disc_active = true;
} else if (disc_params->cmd == NL80211_WIFI_DISC_STOP) {
/* change disc state to inactive */
ar->disc_active = false;
if (ath6kl_wmi_disc_mode_cmd(ar->wmi, vif->fw_vif_idx,
0, 0, 0, 0, 0, 0, 0)) {
printk(KERN_ERR "%s: wifi discovery stop fail\n",
__func__);
return -EINVAL;
}
}
}
return 0;
break;
#endif
#ifdef ATH6KL_SUPPORT_WIFI_KTK
case ATH6KL_TM_CMD_WIFI_KTK:
{
struct wifi_ktk_params *ktk_params;
struct ath6kl_vif *vif;
vif = ath6kl_vif_first(ar);
if (!vif)
return -EINVAL;
if (!tb[ATH6KL_TM_ATTR_DATA]) {
printk(KERN_ERR "%s: NO DATA\n", __func__);
return -EINVAL;
}
buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
ktk_params = (struct wifi_ktk_params *)buf;
if (ktk_params->cmd == NL80211_WIFI_KTK_IE) {
u8 ie_hdr[6] = {0xDD, 0x00, 0x00, 0x03, 0x7f, 0x00};
u8 *ie = NULL;
u16 ie_length = ktk_params->params.ie_params.length;
ie = kmalloc(ie_length+6, GFP_KERNEL);
if (ie == NULL)
return -ENOMEM;
memcpy(ie, ie_hdr, 6);
ie[1] = ie_length+4;
memcpy(ie+6, ktk_params->params.ie_params.ie,
ie_length);
if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_PROBE_RESP,
ie,
ie_length+6)) {
kfree(ie);
printk(KERN_ERR
"%s: wifi ktk set probe response ie fail\n",
__func__);
return -EINVAL;
}
if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
WMI_FRAME_BEACON, ie, ie_length+6)) {
kfree(ie);
printk(KERN_ERR
"%s: wifi ktk set beacon ie fail\n",
__func__);
return -EINVAL;
}
kfree(ie);
} else if (ktk_params->cmd == NL80211_WIFI_KTK_IE_FILTER) {
if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx,
ktk_params->params.ie_filter_params.enable,
ktk_params->params.ie_filter_params.startPos,
ktk_params->params.ie_filter_params.filter,
ktk_params->params.ie_filter_params.length)) {
printk(KERN_ERR
"%s: wifi ktk set ie filter fail\n",
__func__);
return -EINVAL;
}
} else if (ktk_params->cmd == NL80211_WIFI_KTK_START) {
ar->ktk_active = true;
/* Clear the legacy ie pattern and filter */
if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx,
0,
0,
NULL,
0)) {
printk(KERN_ERR "%s: wifi ktk clear ie filter fail\n",
__func__);
return -EINVAL;
}
memcpy(ar->ktk_passphrase,
ktk_params->params.start_params.passphrase,
16);
if (ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
1, SPECIFIC_SSID_FLAG,
ktk_params->params.start_params.ssid_len,
ktk_params->params.start_params.ssid)) {
printk(KERN_ERR "%s: wifi ktk set probedssid fail\n",
__func__);
return -EINVAL;
}
if (ath6kl_wmi_ibss_pm_caps_cmd(ar->wmi,
vif->fw_vif_idx,
ADHOC_PS_KTK,
5,
10,
10)) {
printk(KERN_ERR "%s: wifi ktk set power save mode on fail\n",
__func__);
return -EINVAL;
}
} else if (ktk_params->cmd == NL80211_WIFI_KTK_STOP) {
ar->ktk_active = false;
if (ath6kl_wmi_ibss_pm_caps_cmd(ar->wmi,
vif->fw_vif_idx,
ADHOC_PS_DISABLE,
0,
0,
0)) {
printk(KERN_ERR "%s: wifi ktk set power save mode off fail\n",
__func__);
return -EINVAL;
}
}
}
return 0;
break;
#endif
case ATH6KL_TM_CMD_DFS_SKIP:
{
struct wifi_dfs_skip_params *dfs_skip_params;
struct ath6kl_vif *vif;
vif = ath6kl_vif_first(ar);
if (!vif)
return -EINVAL;
if (!tb[ATH6KL_TM_ATTR_DATA]) {
printk(KERN_ERR "%s: NO DATA\n", __func__);
return -EINVAL;
}
buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
dfs_skip_params = (struct wifi_dfs_skip_params *)buf;
if (dfs_skip_params->enable)
vif->sc_params.scan_ctrl_flags
|= ENABLE_DFS_SKIP_CTRL_FLAGS;
else
vif->sc_params.scan_ctrl_flags
&= ~ENABLE_DFS_SKIP_CTRL_FLAGS;
if (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)) {
printk(KERN_ERR "%s: wifi dfs skip enable fail\n",
__func__);
return -EINVAL;
}
}
return 0;
break;
default:
return -EOPNOTSUPP;
}
}
#endif /* CONFIG_NL80211_TESTMODE */