596 lines
16 KiB
C
596 lines
16 KiB
C
|
/*
|
||
|
* Linux cfg80211 Vendor Extension Code
|
||
|
*
|
||
|
* Copyright (C) 1999-2014, Broadcom Corporation
|
||
|
* Copyright (C) 2015-2017, NVIDIA CORPORATION. All rights reserved.
|
||
|
*
|
||
|
* Unless you and Broadcom execute a separate written software license
|
||
|
* agreement governing use of this software, this software is licensed to you
|
||
|
* under the terms of the GNU General Public License version 2 (the "GPL"),
|
||
|
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
|
||
|
* following added to such license:
|
||
|
*
|
||
|
* As a special exception, the copyright holders of this software give you
|
||
|
* permission to link this software with independent modules, and to copy and
|
||
|
* distribute the resulting executable under terms of your choice, provided that
|
||
|
* you also meet, for each linked independent module, the terms and conditions of
|
||
|
* the license of that module. An independent module is a module which is not
|
||
|
* derived from this software. The special exception does not apply to any
|
||
|
* modifications of the software.
|
||
|
*
|
||
|
* Notwithstanding the above, under no circumstances may you combine this
|
||
|
* software in any way with any other Broadcom software provided under a license
|
||
|
* other than the GPL, without Broadcom's express prior written consent.
|
||
|
*
|
||
|
* $Id: wl_cfgvendor.c 473890 2014-04-30 01:55:06Z $
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* New vendor interface additon to nl80211/cfg80211 to allow vendors
|
||
|
* to implement proprietary features over the cfg80211 stack.
|
||
|
*/
|
||
|
|
||
|
#include <typedefs.h>
|
||
|
#include <linuxver.h>
|
||
|
#include <osl.h>
|
||
|
#include <linux/kernel.h>
|
||
|
|
||
|
#include <bcmutils.h>
|
||
|
#include <bcmwifi_channels.h>
|
||
|
#include <bcmendian.h>
|
||
|
#include <proto/ethernet.h>
|
||
|
#include <proto/802.11.h>
|
||
|
#include <linux/if_arp.h>
|
||
|
#include <asm/uaccess.h>
|
||
|
|
||
|
|
||
|
#include <dngl_stats.h>
|
||
|
#include <dhd.h>
|
||
|
#include <dhdioctl.h>
|
||
|
#include <wlioctl.h>
|
||
|
#include <dhd_cfg80211.h>
|
||
|
#ifdef PNO_SUPPORT
|
||
|
#include <dhd_pno.h>
|
||
|
#endif /* PNO_SUPPORT */
|
||
|
#ifdef RTT_SUPPORT
|
||
|
#include <dhd_rtt.h>
|
||
|
#endif /* RTT_SUPPORT */
|
||
|
#include <proto/ethernet.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/kthread.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/etherdevice.h>
|
||
|
#include <linux/wireless.h>
|
||
|
#include <linux/ieee80211.h>
|
||
|
#include <linux/wait.h>
|
||
|
#include <linux/vmalloc.h>
|
||
|
#include <net/cfg80211.h>
|
||
|
#include <net/rtnetlink.h>
|
||
|
|
||
|
#include <wlioctl.h>
|
||
|
#include <wldev_common.h>
|
||
|
#include <wl_cfg80211.h>
|
||
|
#include <wl_cfgp2p.h>
|
||
|
#include <wl_android.h>
|
||
|
#include <wl_cfgvendor.h>
|
||
|
#ifdef PROP_TXSTATUS
|
||
|
#include <dhd_wlfc.h>
|
||
|
#endif
|
||
|
|
||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0)) || defined(WL_VENDOR_EXT_SUPPORT)
|
||
|
/*
|
||
|
* This API is to be used for asynchronous vendor events. This
|
||
|
* shouldn't be used in response to a vendor command from its
|
||
|
* do_it handler context (instead wl_cfgvendor_send_cmd_reply should
|
||
|
* be used).
|
||
|
*/
|
||
|
int wl_cfgvendor_send_async_event(struct wiphy *wiphy,
|
||
|
struct net_device *dev, int event_id, const void *data, int len)
|
||
|
{
|
||
|
u16 kflags;
|
||
|
struct sk_buff *skb;
|
||
|
|
||
|
kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
|
||
|
|
||
|
/* Alloc the SKB for vendor_event */
|
||
|
#ifdef VENDOR_NET_SKB_ALLOC
|
||
|
skb = cfg80211_vendor_event_skb_alloc(dev, wiphy, len, event_id, kflags);
|
||
|
#else
|
||
|
#if defined(CONFIG_ARCH_MSM) && defined(SUPPORT_WDEV_CFG80211_VENDOR_EVENT_ALLOC)
|
||
|
skb = cfg80211_vendor_event_alloc(wiphy, NULL, len, event_id, kflags);
|
||
|
#else
|
||
|
skb = cfg80211_vendor_event_alloc(wiphy, len, event_id, kflags);
|
||
|
#endif /* CONFIG_ARCH_MSM && SUPPORT_WDEV_CFG80211_VENDOR_EVENT_ALLOC */
|
||
|
#endif /* VENDOR_NET_SKB_ALLOC */
|
||
|
if (!skb) {
|
||
|
WL_ERR(("skb alloc failed"));
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
/* Push the data to the skb */
|
||
|
nla_put_nohdr(skb, len, data);
|
||
|
|
||
|
cfg80211_vendor_event(skb, kflags);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int wl_cfgvendor_send_cmd_reply(struct wiphy *wiphy,
|
||
|
struct net_device *dev, const void *data, int len)
|
||
|
{
|
||
|
struct sk_buff *skb;
|
||
|
|
||
|
/* Alloc the SKB for vendor_event */
|
||
|
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len);
|
||
|
if (unlikely(!skb)) {
|
||
|
WL_ERR(("skb alloc failed"));
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
/* Push the data to the skb */
|
||
|
nla_put_nohdr(skb, len, data);
|
||
|
|
||
|
return cfg80211_vendor_cmd_reply(skb);
|
||
|
}
|
||
|
|
||
|
static int wl_cfgvendor_unsupported_feature(struct wiphy *wiphy,
|
||
|
struct wireless_dev *wdev, const void *data, int len)
|
||
|
{
|
||
|
// return unsupported error code
|
||
|
return WIFI_ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
static int wl_cfgvendor_set_country(struct wiphy *wiphy,
|
||
|
struct wireless_dev *wdev, const void *data, int len)
|
||
|
{
|
||
|
int err = BCME_ERROR, rem, type;
|
||
|
char country_code[WLC_CNTRY_BUF_SZ] = {0};
|
||
|
const struct nlattr *iter;
|
||
|
|
||
|
nla_for_each_attr(iter, data, len, rem) {
|
||
|
type = nla_type(iter);
|
||
|
switch (type) {
|
||
|
case ANDR_WIFI_ATTRIBUTE_COUNTRY:
|
||
|
memcpy(country_code, nla_data(iter),
|
||
|
MIN(nla_len(iter), WLC_CNTRY_BUF_SZ));
|
||
|
break;
|
||
|
default:
|
||
|
WL_ERR(("Unknown type: %d\n", type));
|
||
|
return err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err = wldev_set_country(wdev->netdev, country_code, true, true);
|
||
|
if (err < 0) {
|
||
|
WL_ERR(("Set country failed ret:%d\n", err));
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
#ifdef GSCAN_SUPPORT
|
||
|
static int wl_cfgvendor_gscan_get_channel_list(struct wiphy *wiphy,
|
||
|
struct wireless_dev *wdev, const void *data, int len)
|
||
|
{
|
||
|
int err = 0, type, band;
|
||
|
struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
|
||
|
uint16 *reply = NULL;
|
||
|
uint32 reply_len = 0, num_channels, mem_needed;
|
||
|
struct sk_buff *skb;
|
||
|
|
||
|
type = nla_type(data);
|
||
|
|
||
|
if (type == GSCAN_ATTRIBUTE_BAND) {
|
||
|
band = nla_get_u32(data);
|
||
|
} else {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
reply = dhd_dev_pno_get_gscan(bcmcfg_to_prmry_ndev(cfg),
|
||
|
DHD_PNO_GET_CHANNEL_LIST, &band, &reply_len);
|
||
|
|
||
|
if (!reply) {
|
||
|
WL_ERR(("Could not get channel list\n"));
|
||
|
err = -EINVAL;
|
||
|
return err;
|
||
|
}
|
||
|
num_channels = reply_len/ sizeof(uint32);
|
||
|
mem_needed = reply_len + VENDOR_REPLY_OVERHEAD + (ATTRIBUTE_U32_LEN * 2);
|
||
|
|
||
|
/* Alloc the SKB for vendor_event */
|
||
|
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_needed);
|
||
|
if (unlikely(!skb)) {
|
||
|
WL_ERR(("skb alloc failed"));
|
||
|
err = -ENOMEM;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
nla_put_u32(skb, GSCAN_ATTRIBUTE_NUM_CHANNELS, num_channels);
|
||
|
nla_put(skb, GSCAN_ATTRIBUTE_CHANNEL_LIST, reply_len, reply);
|
||
|
|
||
|
err = cfg80211_vendor_cmd_reply(skb);
|
||
|
|
||
|
if (unlikely(err))
|
||
|
WL_ERR(("Vendor Command reply failed ret:%d \n", err));
|
||
|
exit:
|
||
|
kfree(reply);
|
||
|
return err;
|
||
|
}
|
||
|
#endif /* GSCAN_SUPPORT */
|
||
|
|
||
|
#if defined(KEEP_ALIVE)
|
||
|
static int wl_cfgvendor_start_mkeep_alive(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||
|
const void *data, int len)
|
||
|
{
|
||
|
/* max size of IP packet for keep alive */
|
||
|
const int MKEEP_ALIVE_IP_PKT_MAX = 256;
|
||
|
|
||
|
int ret = BCME_OK, rem, type;
|
||
|
u8 mkeep_alive_id = 0;
|
||
|
u8 *ip_pkt = NULL;
|
||
|
u16 ip_pkt_len = 0;
|
||
|
u8 src_mac[ETHER_ADDR_LEN];
|
||
|
u8 dst_mac[ETHER_ADDR_LEN];
|
||
|
u32 period_msec = 0;
|
||
|
const struct nlattr *iter;
|
||
|
struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
|
||
|
dhd_pub_t *dhd_pub = cfg->pub;
|
||
|
gfp_t kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
|
||
|
nla_for_each_attr(iter, data, len, rem) {
|
||
|
type = nla_type(iter);
|
||
|
switch (type) {
|
||
|
case MKEEP_ALIVE_ATTRIBUTE_ID:
|
||
|
mkeep_alive_id = nla_get_u8(iter);
|
||
|
break;
|
||
|
case MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN:
|
||
|
ip_pkt_len = nla_get_u16(iter);
|
||
|
if (ip_pkt_len > MKEEP_ALIVE_IP_PKT_MAX) {
|
||
|
ret = BCME_BADARG;
|
||
|
goto exit;
|
||
|
}
|
||
|
break;
|
||
|
case MKEEP_ALIVE_ATTRIBUTE_IP_PKT:
|
||
|
if (!ip_pkt_len) {
|
||
|
ret = BCME_BADARG;
|
||
|
WL_ERR(("ip packet length is 0\n"));
|
||
|
goto exit;
|
||
|
}
|
||
|
ip_pkt = (u8 *)kzalloc(ip_pkt_len, kflags);
|
||
|
if (ip_pkt == NULL) {
|
||
|
ret = BCME_NOMEM;
|
||
|
WL_ERR(("Failed to allocate mem for ip packet\n"));
|
||
|
goto exit;
|
||
|
}
|
||
|
memcpy(ip_pkt, (u8*)nla_data(iter), ip_pkt_len);
|
||
|
break;
|
||
|
case MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR:
|
||
|
memcpy(src_mac, nla_data(iter), ETHER_ADDR_LEN);
|
||
|
break;
|
||
|
case MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR:
|
||
|
memcpy(dst_mac, nla_data(iter), ETHER_ADDR_LEN);
|
||
|
break;
|
||
|
case MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC:
|
||
|
period_msec = nla_get_u32(iter);
|
||
|
break;
|
||
|
default:
|
||
|
WL_ERR(("Unknown type: %d\n", type));
|
||
|
ret = BCME_BADARG;
|
||
|
goto exit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ip_pkt == NULL) {
|
||
|
ret = BCME_BADARG;
|
||
|
WL_ERR(("ip packet is NULL\n"));
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
ret = dhd_dev_start_mkeep_alive(dhd_pub, mkeep_alive_id, ip_pkt, ip_pkt_len, src_mac,
|
||
|
dst_mac, period_msec);
|
||
|
if (ret < 0) {
|
||
|
WL_ERR(("start_mkeep_alive is failed ret: %d\n", ret));
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
if (ip_pkt) {
|
||
|
kfree(ip_pkt);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int wl_cfgvendor_stop_mkeep_alive(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||
|
const void *data, int len)
|
||
|
{
|
||
|
int ret = BCME_OK, rem, type;
|
||
|
u8 mkeep_alive_id = 0;
|
||
|
const struct nlattr *iter;
|
||
|
struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
|
||
|
dhd_pub_t *dhd_pub = cfg->pub;
|
||
|
|
||
|
nla_for_each_attr(iter, data, len, rem) {
|
||
|
type = nla_type(iter);
|
||
|
switch (type) {
|
||
|
case MKEEP_ALIVE_ATTRIBUTE_ID:
|
||
|
mkeep_alive_id = nla_get_u8(iter);
|
||
|
break;
|
||
|
default:
|
||
|
WL_ERR(("Unknown type: %d\n", type));
|
||
|
ret = BCME_BADARG;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ret = dhd_dev_stop_mkeep_alive(dhd_pub, mkeep_alive_id);
|
||
|
if (ret < 0) {
|
||
|
WL_ERR(("stop_mkeep_alive is failed ret: %d\n", ret));
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif /* defined(KEEP_ALIVE) */
|
||
|
|
||
|
static int wl_cfgvendor_get_feature_set(struct wiphy *wiphy,
|
||
|
struct wireless_dev *wdev, const void *data, int len)
|
||
|
{
|
||
|
int err = 0;
|
||
|
struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
|
||
|
int reply;
|
||
|
|
||
|
reply = dhd_dev_get_feature_set(bcmcfg_to_prmry_ndev(cfg));
|
||
|
|
||
|
err = wl_cfgvendor_send_cmd_reply(wiphy, bcmcfg_to_prmry_ndev(cfg),
|
||
|
&reply, sizeof(int));
|
||
|
|
||
|
if (unlikely(err))
|
||
|
WL_ERR(("Vendor Command reply failed ret:%d \n", err));
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
wl_cfgvendor_set_pno_mac_oui(struct wiphy *wiphy,
|
||
|
struct wireless_dev *wdev, const void *data, int len)
|
||
|
{
|
||
|
int err = 0;
|
||
|
struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
|
||
|
int type;
|
||
|
uint8 pno_random_mac_oui[DOT11_OUI_LEN];
|
||
|
|
||
|
type = nla_type(data);
|
||
|
|
||
|
if (type == ANDR_WIFI_ATTRIBUTE_RANDOM_MAC_OUI) {
|
||
|
memcpy(pno_random_mac_oui, nla_data(data), DOT11_OUI_LEN);
|
||
|
|
||
|
err = dhd_dev_pno_set_mac_oui(bcmcfg_to_prmry_ndev(cfg), pno_random_mac_oui);
|
||
|
|
||
|
if (unlikely(err))
|
||
|
WL_ERR(("Bad OUI, could not set:%d \n", err));
|
||
|
|
||
|
|
||
|
} else {
|
||
|
err = -1;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static int wl_cfgvendor_dbg_get_version(struct wiphy *wiphy,
|
||
|
struct wireless_dev *wdev, const void *data, int len)
|
||
|
{
|
||
|
int ret = BCME_OK, rem, type;
|
||
|
int buf_len = 1024;
|
||
|
bool dhd_ver = FALSE;
|
||
|
char *buf_ptr;
|
||
|
const struct nlattr *iter;
|
||
|
gfp_t kflags;
|
||
|
struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
|
||
|
kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
|
||
|
buf_ptr = kzalloc(buf_len, kflags);
|
||
|
if (!buf_ptr) {
|
||
|
WL_ERR(("failed to allocate the buffer for version n"));
|
||
|
ret = BCME_NOMEM;
|
||
|
goto exit;
|
||
|
}
|
||
|
nla_for_each_attr(iter, data, len, rem) {
|
||
|
type = nla_type(iter);
|
||
|
switch (type) {
|
||
|
case DEBUG_ATTRIBUTE_GET_DRIVER:
|
||
|
dhd_ver = TRUE;
|
||
|
break;
|
||
|
case DEBUG_ATTRIBUTE_GET_FW:
|
||
|
dhd_ver = FALSE;
|
||
|
break;
|
||
|
default:
|
||
|
WL_ERR(("Unknown type: %d\n", type));
|
||
|
ret = BCME_ERROR;
|
||
|
goto exit;
|
||
|
}
|
||
|
}
|
||
|
ret = dhd_os_get_version(bcmcfg_to_prmry_ndev(cfg), dhd_ver, &buf_ptr, buf_len);
|
||
|
if (ret < 0) {
|
||
|
WL_ERR(("failed to get the version %d\n", ret));
|
||
|
goto exit;
|
||
|
}
|
||
|
ret = wl_cfgvendor_send_cmd_reply(wiphy, bcmcfg_to_prmry_ndev(cfg),
|
||
|
buf_ptr, strlen(buf_ptr));
|
||
|
exit:
|
||
|
kfree(buf_ptr);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct wiphy_vendor_command wl_vendor_cmds [] = {
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = ANDR_WIFI_SET_COUNTRY
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_set_country
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = ANDR_WIFI_SUBCMD_GET_FEATURE_SET
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_get_feature_set
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = ANDR_WIFI_RANDOM_MAC_OUI
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_set_pno_mac_oui
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_GET_VER
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_dbg_get_version
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_GET_RING_STATUS
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_unsupported_feature
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_START_LOGGING
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_unsupported_feature
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_TRIGGER_MEM_DUMP
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_unsupported_feature
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_GET_MEM_DUMP
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_unsupported_feature
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_GET_RING_STATUS
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_unsupported_feature
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_GET_RING_DATA
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_unsupported_feature
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_GET_FEATURE
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_unsupported_feature
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = DEBUG_RESET_LOGGING
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_unsupported_feature
|
||
|
},
|
||
|
#ifdef GSCAN_SUPPORT
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = GSCAN_SUBCMD_GET_CHANNEL_LIST
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_gscan_get_channel_list
|
||
|
},
|
||
|
#endif /* GSCAN_SUPPORT */
|
||
|
#ifdef KEEP_ALIVE
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_start_mkeep_alive
|
||
|
},
|
||
|
{
|
||
|
{
|
||
|
.vendor_id = OUI_GOOGLE,
|
||
|
.subcmd = WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE
|
||
|
},
|
||
|
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
|
||
|
.doit = wl_cfgvendor_stop_mkeep_alive
|
||
|
},
|
||
|
#endif /* KEEP_ALIVE */
|
||
|
};
|
||
|
|
||
|
static const struct nl80211_vendor_cmd_info wl_vendor_events [] = {
|
||
|
{ OUI_BRCM, BRCM_VENDOR_EVENT_UNSPEC },
|
||
|
{ OUI_BRCM, BRCM_VENDOR_EVENT_PRIV_STR },
|
||
|
{ OUI_GOOGLE, GOOGLE_GSCAN_SIGNIFICANT_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_GSCAN_GEOFENCE_FOUND_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_GSCAN_BATCH_SCAN_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_SCAN_FULL_RESULTS_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_RTT_COMPLETE_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_SCAN_COMPLETE_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_GSCAN_GEOFENCE_LOST_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_SCAN_EPNO_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_DEBUG_RING_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_FW_DUMP_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_PNO_HOTSPOT_FOUND_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_RSSI_MONITOR_EVENT },
|
||
|
{ OUI_GOOGLE, GOOGLE_MKEEP_ALIVE_EVENT },
|
||
|
{ OUI_BRCM, BRCM_VENDOR_EVENT_IDSUP_STATUS },
|
||
|
{ OUI_BRCM, BRCM_VENDOR_EVENT_DRIVER_HANG }
|
||
|
};
|
||
|
|
||
|
int wl_cfgvendor_attach(struct wiphy *wiphy, dhd_pub_t *dhd)
|
||
|
{
|
||
|
|
||
|
WL_INFORM(("Vendor: Register BRCM cfg80211 vendor cmd(0x%x) interface \n",
|
||
|
NL80211_CMD_VENDOR));
|
||
|
|
||
|
wiphy->vendor_commands = wl_vendor_cmds;
|
||
|
wiphy->n_vendor_commands = ARRAY_SIZE(wl_vendor_cmds);
|
||
|
wiphy->vendor_events = wl_vendor_events;
|
||
|
wiphy->n_vendor_events = ARRAY_SIZE(wl_vendor_events);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int wl_cfgvendor_detach(struct wiphy *wiphy)
|
||
|
{
|
||
|
WL_INFORM(("Vendor: Unregister BRCM cfg80211 vendor interface \n"));
|
||
|
|
||
|
wiphy->vendor_commands = NULL;
|
||
|
wiphy->vendor_events = NULL;
|
||
|
wiphy->n_vendor_commands = 0;
|
||
|
wiphy->n_vendor_events = 0;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0)) || defined(WL_VENDOR_EXT_SUPPORT) */
|