blob: deb753c7a0a0aa707a9153a5c3d2cfad81cc42cf [file] [log] [blame]
/** @file moal_cfgvendor.c
*
* @brief This file contains the functions for CFG80211 vendor.
*
* Copyright (C) 2011-2018, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
#include "moal_cfgvendor.h"
#include "moal_cfg80211.h"
/********************************************************
Local Variables
********************************************************/
/********************************************************
Global Variables
********************************************************/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
extern int dfs_offload;
#endif
extern int roamoffload_in_hs;
/********************************************************
Local Functions
********************************************************/
/********************************************************
Global Functions
********************************************************/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
/**marvell vendor command and event*/
#define MRVL_VENDOR_ID 0x005043
/** vendor events */
const struct nl80211_vendor_cmd_info vendor_events[] = {
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_hang,}, /*event_id 0 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_rssi_monitor,}, /*event_id 0x1501 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_set_key_mgmt_offload,}, /*event_id 0x10001 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_fw_roam_success,}, /*event_id 0x10002 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_radar_detected,}, /*event_id 0x10004 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_cac_started,}, /*event_id 0x10005 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_cac_finished,}, /*event_id 0x10006 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_cac_aborted,}, /*event_id 0x10007 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_nop_finished,}, /*event_id 0x10008 */
{.vendor_id = MRVL_VENDOR_ID,.subcmd =
event_wifi_logger_ring_buffer_data,},
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_wifi_logger_alert,},
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_packet_fate_monitor,},
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_wake_reason_report,},
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_nan_cb,},
/**add vendor event here*/
{.vendor_id = MRVL_VENDOR_ID,.subcmd = event_rtt_result,}, /*event_id ??? */
};
/**
* @brief get the event id of the events array
*
* @param event vendor event
*
* @return index of events array
*/
int
woal_get_event_id(int event)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(vendor_events); i++) {
if (vendor_events[i].subcmd == event)
return i;
}
return event_max;
}
/**
* @brief send vendor event to kernel
*
* @param priv A pointer to moal_private
* @param event vendor event
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
int
woal_cfg80211_vendor_event(IN moal_private *priv,
IN int event, IN t_u8 *data, IN int len)
{
struct wiphy *wiphy = NULL;
struct sk_buff *skb = NULL;
int event_id = 0;
t_u8 *pos = NULL;
int ret = 0;
ENTER();
if (!priv || !priv->wdev || !priv->wdev->wiphy) {
LEAVE();
return ret;
}
wiphy = priv->wdev->wiphy;
PRINTM(MEVENT, "vendor event :0x%x\n", event);
event_id = woal_get_event_id(event);
if (event_max == event_id) {
PRINTM(MERROR, "Not find this event %d \n", event_id);
ret = 1;
LEAVE();
return ret;
}
/**allocate skb*/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
skb = cfg80211_vendor_event_alloc(wiphy, NULL, len, event_id,
GFP_ATOMIC);
#else
skb = cfg80211_vendor_event_alloc(wiphy, len, event_id, GFP_ATOMIC);
#endif
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor event\n");
ret = 1;
LEAVE();
return ret;
}
pos = skb_put(skb, len);
memcpy(pos, data, len);
/**send event*/
cfg80211_vendor_event(skb, GFP_ATOMIC);
LEAVE();
return ret;
}
/**
* @brief send vendor event to kernel
*
* @param priv A pointer to moal_private
* @param event vendor event
* @param len data length
*
* @return 0: success 1: fail
*/
struct sk_buff *
woal_cfg80211_alloc_vendor_event(IN moal_private *priv,
IN int event, IN int len)
{
struct wiphy *wiphy = NULL;
struct sk_buff *skb = NULL;
int event_id = 0;
ENTER();
if (!priv || !priv->wdev || !priv->wdev->wiphy) {
PRINTM(MERROR, "Not find this event %d \n", event_id);
goto done;
}
wiphy = priv->wdev->wiphy;
PRINTM(MEVENT, "vendor event :0x%x\n", event);
event_id = woal_get_event_id(event);
if (event_max == event_id) {
PRINTM(MERROR, "Not find this event %d \n", event_id);
goto done;
}
/**allocate skb*/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
skb = cfg80211_vendor_event_alloc(wiphy, NULL, len, event_id,
GFP_ATOMIC);
#else
skb = cfg80211_vendor_event_alloc(wiphy, len, event_id, GFP_ATOMIC);
#endif
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor event\n");
goto done;
}
done:
LEAVE();
return skb;
}
/**
* @brief send dfs vendor event to kernel
*
* @param priv A pointer to moal_private
* @param event dfs vendor event
* @param chandef a pointer to struct cfg80211_chan_def
*
* @return N/A
*/
void
woal_cfg80211_dfs_vendor_event(moal_private *priv, int event,
struct cfg80211_chan_def *chandef)
{
dfs_event evt;
ENTER();
if (!chandef) {
LEAVE();
return;
}
memset(&evt, 0, sizeof(dfs_event));
evt.freq = chandef->chan->center_freq;
evt.chan_width = chandef->width;
evt.cf1 = chandef->center_freq1;
evt.cf2 = chandef->center_freq2;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_20_NOHT:
evt.ht_enabled = 0;
break;
case NL80211_CHAN_WIDTH_20:
evt.ht_enabled = 1;
break;
case NL80211_CHAN_WIDTH_40:
evt.ht_enabled = 1;
if (chandef->center_freq1 < chandef->chan->center_freq)
evt.chan_offset = -1;
else
evt.chan_offset = 1;
break;
case NL80211_CHAN_WIDTH_80:
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
evt.ht_enabled = 1;
break;
default:
break;
}
woal_cfg80211_vendor_event(priv, event, (t_u8 *)&evt,
sizeof(dfs_event));
LEAVE();
return;
}
/**
* @brief vendor command to set drvdbg
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_set_drvdbg(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
#ifdef DEBUG_LEVEL1
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
t_u8 *pos = NULL;
#endif
int ret = 1;
ENTER();
#ifdef DEBUG_LEVEL1
/**handle this sub command*/
DBG_HEXDUMP(MCMD_D, "Vendor drvdbg", (t_u8 *)data, data_len);
if (data_len) {
/* Get the driver debug bit masks from user */
drvdbg = *((t_u32 *)data);
PRINTM(MIOCTL, "new drvdbg %x\n", drvdbg);
/* Set the driver debug bit masks into mlan */
if (woal_set_drvdbg(priv, drvdbg)) {
PRINTM(MERROR, "Set drvdbg failed!\n");
ret = 1;
}
}
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(drvdbg));
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = 1;
LEAVE();
return ret;
}
pos = skb_put(skb, sizeof(drvdbg));
memcpy(pos, &drvdbg, sizeof(drvdbg));
ret = cfg80211_vendor_cmd_reply(skb);
#endif
LEAVE();
return ret;
}
/**
* @brief process one channel in bucket
*
* @param priv A pointer to moal_private struct
*
* @param channel a pointer to channel
*
* @return 0: success other: fail
*/
static mlan_status
woal_band_to_valid_channels(moal_private *priv, wifi_band w_band, int channel[],
t_u32 *nchannel)
{
int band = 0;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
int i = 0;
t_u8 cnt = 0;
int *ch_ptr = channel;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!priv->wdev->wiphy->bands[band])
continue;
if ((band == IEEE80211_BAND_2GHZ) && !(w_band & WIFI_BAND_BG))
continue;
if ((band == IEEE80211_BAND_5GHZ) &&
!((w_band & WIFI_BAND_A) || (w_band & WIFI_BAND_A_DFS)))
continue;
sband = priv->wdev->wiphy->bands[band];
for (i = 0; (i < sband->n_channels); i++) {
ch = &sband->channels[i];
if (ch->flags & IEEE80211_CHAN_DISABLED) {
PRINTM(MERROR, "Skip DISABLED channel %d\n",
ch->center_freq);
continue;
}
if ((band == IEEE80211_BAND_5GHZ)) {
if (((ch->flags & IEEE80211_CHAN_RADAR) &&
!(w_band & WIFI_BAND_A_DFS)) ||
(!(ch->flags & IEEE80211_CHAN_RADAR) &&
!(w_band & WIFI_BAND_A)))
continue;
}
if (cnt >= *nchannel) {
PRINTM(MERROR,
"cnt=%d is exceed %d, cur ch=%d %dMHz\n",
cnt, *nchannel, ch->hw_value,
ch->center_freq);
break;
}
*ch_ptr = ch->center_freq;
ch_ptr++;
cnt++;
}
}
PRINTM(MCMND, "w_band=%d cnt=%d\n", w_band, cnt);
*nchannel = cnt;
return MLAN_STATUS_SUCCESS;
}
/**
* @brief GSCAN subcmd - enable full scan results
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
*
* @param data a pointer to data
* @param data_len data length
*
* @return 0: success other: fail
*/
static int
woal_cfg80211_subcmd_get_valid_channels(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct nlattr *tb[ATTR_WIFI_MAX];
t_u32 band = 0;
int ch_out[MAX_CHANNEL_NUM];
t_u32 nchannel = 0;
t_u32 mem_needed = 0;
struct sk_buff *skb = NULL;
int err = 0;
ENTER();
PRINTM(MCMND, "Enter woal_cfg80211_subcmd_get_valid_channels\n");
err = nla_parse(tb, ATTR_WIFI_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (err) {
PRINTM(MERROR, "%s: nla_parse fail\n", __FUNCTION__);
err = -EFAULT;
goto done;
}
if (!tb[ATTR_CHANNELS_BAND]) {
PRINTM(MERROR, "%s: null attr: tb[ATTR_GET_CH]=%p\n",
__FUNCTION__, tb[ATTR_CHANNELS_BAND]);
err = -EINVAL;
goto done;
}
band = nla_get_u32(tb[ATTR_CHANNELS_BAND]);
if (band > WIFI_BAND_MAX) {
PRINTM(MERROR, "%s: invalid band=%d\n", __FUNCTION__, band);
err = -EINVAL;
goto done;
}
memset(ch_out, 0x00, sizeof(ch_out));
nchannel = MAX_CHANNEL_NUM;
if (woal_band_to_valid_channels(priv, band, ch_out, &nchannel) !=
MLAN_STATUS_SUCCESS) {
PRINTM(MERROR,
"get_channel_list: woal_band_to_valid_channels fail\n");
return -EFAULT;
}
mem_needed =
nla_total_size(nchannel * sizeof(ch_out[0])) +
nla_total_size(sizeof(nchannel))
+ VENDOR_REPLY_OVERHEAD;
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_needed);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed");
err = -ENOMEM;
goto done;
}
nla_put_u32(skb, ATTR_NUM_CHANNELS, nchannel);
nla_put(skb, ATTR_CHANNEL_LIST, nchannel * sizeof(ch_out[0]), ch_out);
err = cfg80211_vendor_cmd_reply(skb);
if (err) {
PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err);
goto done;
}
done:
LEAVE();
return err;
}
/**
* @brief vendor command to get driver version
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_drv_version(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
int ret = 0;
t_u32 drv_len = 0;
char drv_version[MLAN_MAX_VER_STR_LEN] = { 0 };
char *pos;
ENTER();
memcpy(drv_version, &priv->phandle->driver_version,
MLAN_MAX_VER_STR_LEN);
drv_len = strlen(drv_version);
pos = strstr(drv_version, "%s");
/* remove 3 char "-%s" in driver_version string */
if (pos != NULL)
memcpy(pos, pos + 3, strlen(pos) - 3);
reply_len = strlen(drv_version) + 1;
drv_len -= 3;
drv_version[drv_len] = '\0';
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_NAME, reply_len,
(t_u8 *)drv_version);
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to get firmware version
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_fw_version(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
char end_c = '\0';
int ret = 0;
char fw_ver[32] = { 0 };
union {
t_u32 l;
t_u8 c[4];
} ver;
ENTER();
ver.l = priv->phandle->fw_release_number;
snprintf(fw_ver, sizeof(fw_ver), "%u.%u.%u.p%u%c",
ver.c[2], ver.c[1], ver.c[0], ver.c[3], end_c);
reply_len = strlen(fw_ver) + 1;
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_NAME, reply_len, (t_u8 *)fw_ver);
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to get firmware memory dump
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_fw_dump(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = NULL;
moal_private *priv = NULL;
moal_handle *handle = NULL;
int ret = MLAN_STATUS_SUCCESS;
int length = 0;
struct sk_buff *skb = NULL;
ENTER();
if (!wdev || !wdev->netdev) {
LEAVE();
return -EFAULT;
}
dev = wdev->netdev;
priv = (moal_private *)woal_get_netdev_priv(dev);
handle = priv->phandle;
if (handle->firmware_dump_file) {
memset(handle->firmware_dump_file, 0,
sizeof(handle->firmware_dump_file));
}
length = sizeof(handle->firmware_dump_file);
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length);
if (!skb) {
PRINTM(MERROR, "Failed to allocate memory for skb\n");
ret = -ENOMEM;
goto done;
}
nla_put(skb, ATTR_FW_DUMP_PATH, sizeof(handle->firmware_dump_file),
handle->firmware_dump_file);
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to get driver memory dump
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_drv_dump(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = NULL;
moal_private *priv = NULL;
moal_handle *handle = NULL;
int ret = MLAN_STATUS_SUCCESS;
int length = 0;
char driver_dump_file[128];
char path_name[64];
struct sk_buff *skb = NULL;
ENTER();
if (!wdev || !wdev->netdev) {
LEAVE();
return -EFAULT;
}
dev = wdev->netdev;
priv = (moal_private *)woal_get_netdev_priv(dev);
handle = priv->phandle;
memset(path_name, 0, sizeof(path_name));
woal_create_dump_dir(handle, path_name, sizeof(path_name));
PRINTM(MMSG, "driver dump path name is %s\n", path_name);
woal_dump_drv_info(handle, path_name);
memset(driver_dump_file, 0, sizeof(driver_dump_file));
sprintf(driver_dump_file, "%s/%s", path_name, "file_drv_info");
PRINTM(MMSG, "driver dump file is %s\n", driver_dump_file);
length = sizeof(driver_dump_file);
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length);
if (!skb) {
PRINTM(MERROR, "Failed to allocate memory for skb\n");
ret = -ENOMEM;
goto done;
}
nla_put_string(skb, ATTR_DRV_DUMP_PATH, driver_dump_file);
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to get supported feature set
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_supp_feature_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
int ret = 0;
t_u32 supp_feature_set = 0;
ENTER();
supp_feature_set = WIFI_FEATURE_INFRA
#if defined(UAP_SUPPORT) && defined(STA_SUPPORT)
| WIFI_FEATURE_AP_STA
#endif
| WIFI_FEATURE_LINK_LAYER_STATS
| WIFI_FEATURE_LOGGER
| WIFI_FEATURE_RSSI_MONITOR
| WIFI_FEATURE_CONFIG_NDO | WIFI_FEATURE_SCAN_RAND;
reply_len = sizeof(supp_feature_set);
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
nla_put_u32(skb, ATTR_FEATURE_SET, supp_feature_set);
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to set country code
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_set_country_code(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
int ret = 0, rem, type;
const struct nlattr *iter;
char country[COUNTRY_CODE_LEN] = { 0 };
ENTER();
nla_for_each_attr(iter, data, data_len, rem) {
type = nla_type(iter);
switch (type) {
case ATTR_COUNTRY_CODE:
strncpy(country, nla_data(iter),
MIN(sizeof(country) - 1, nla_len(iter)));
break;
default:
PRINTM(MERROR, "Unknown type: %d\n", type);
return ret;
}
}
regulatory_hint(wiphy, country);
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to get supported feature set
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_wifi_logger_supp_feature_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
int ret = 0;
t_u32 supp_feature_set = 0;
ENTER();
supp_feature_set = WIFI_LOGGER_CONNECT_EVENT_SUPPORTED;
reply_len = sizeof(supp_feature_set);
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
nla_put_u32(skb, ATTR_WIFI_LOGGER_FEATURE_SET, supp_feature_set);
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief get ring status
*
* @param priv A pointer to moal_private struct
* @param ring_id ring buffer id
* @param status a pointer to wifi_ring_buffer_status
*
* @return void
*/
static void
woal_get_ring_status(moal_private *priv, int ring_id,
wifi_ring_buffer_status * status)
{
int id = 0;
wifi_ring_buffer *ring;
ENTER();
for (id = 0; id < RING_ID_MAX; id++) {
ring = (wifi_ring_buffer *) priv->rings[id];
if (VALID_RING(ring->ring_id) && ring_id == ring->ring_id) {
strncpy(status->name, ring->name,
sizeof(status->name) - 1);
status->ring_id = ring->ring_id;
status->ring_buffer_byte_size = ring->ring_size;
status->written_bytes = ring->ctrl.written_bytes;
status->written_records = ring->ctrl.written_records;
status->read_bytes = ring->ctrl.read_bytes;
status->verbose_level = ring->log_level;
PRINTM(MINFO,
"%s, name: %s, ring_id: %d, ring_size : %d, written_bytes: %d, written_records: "
"%d, read_bytes: %d \n",
__FUNCTION__, status->name, status->ring_id,
status->ring_buffer_byte_size,
status->written_bytes, status->written_records,
status->read_bytes);
break;
}
}
LEAVE();
return;
}
/**
* @brief vendor command to get ring buffer status
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_ring_buff_status(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
t_u8 ringIdx, ring_cnt = 0;
int ret = 0;
wifi_ring_buffer_status status[RING_ID_MAX];
wifi_ring_buffer_status ring_status;
ENTER();
reply_len =
RING_ID_MAX * sizeof(wifi_ring_buffer_status) + sizeof(t_u32);
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
/* shoud sync with HAL side to decide payload layout.
just ring buffer status or ring buffer num + ring buffer status
currently only have ring buffer status
*/
for (ringIdx = 0; ringIdx < RING_ID_MAX; ringIdx++) {
memset(&ring_status, 0, sizeof(wifi_ring_buffer_status));
woal_get_ring_status(priv, ringIdx, &ring_status);
memcpy(&status[ring_cnt++], &ring_status,
sizeof(wifi_ring_buffer_status));
}
nla_put_u32(skb, ATTR_NUM_RINGS, ring_cnt);
nla_put(skb, ATTR_RING_BUFFER_STATUS,
sizeof(wifi_ring_buffer_status) * ring_cnt, status);
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief return ring_id based on ring_name
*
* @param priv A pointer to moal_private struct
* @param ring_name A pointer to ring_name
*
* @return An invalid ring id for failure or valid ring id on success.
*/
int
woal_get_ring_id_by_name(moal_private *priv, char *ring_name)
{
int id;
wifi_ring_buffer *ring;
ENTER();
for (id = 0; id < RING_ID_MAX; id++) {
ring = (wifi_ring_buffer *) priv->rings[id];
if (!strncmp(ring->name, ring_name, sizeof(ring->name) - 1))
break;
}
LEAVE();
return id;
}
/**
* @brief start logger
*
* @param priv A pointer to moal_private struct
* @param ring_name string ring_name
* @param log_level log level to record
* @param flags reserved
* @param time_intval interval to report log to HAL
* @param threshold buffer threshold to report log to HAL
*
* @return 0: success 1: fail
*/
int
woal_start_logging(moal_private *priv, char *ring_name, int log_level,
int flags, int time_intval, int threshold)
{
int ret = 0;
int ring_id;
wifi_ring_buffer *ring_buffer;
unsigned long lock_flags;
ENTER();
ring_id = woal_get_ring_id_by_name(priv, ring_name);
if (!VALID_RING(ring_id)) {
ret = -EINVAL;
goto done;
}
PRINTM(MCMND,
"%s , log_level : %d, time_intval : %d, threshod %d Bytes\n",
__FUNCTION__, log_level, time_intval, threshold);
ring_buffer = (wifi_ring_buffer *) priv->rings[ring_id];
if (ring_buffer->state == RING_STOP) {
PRINTM(MERROR, "Ring is stopped!\n");
ret = -EAGAIN;
goto done;
}
spin_lock_irqsave(&ring_buffer->lock, lock_flags);
ring_buffer->log_level = log_level;
ring_buffer->threshold = threshold;
if (log_level == 0)
ring_buffer->state = RING_SUSPEND;
else
ring_buffer->state = RING_ACTIVE;
ring_buffer->interval = msecs_to_jiffies(time_intval * MSEC_PER_SEC);
spin_unlock_irqrestore(&ring_buffer->lock, lock_flags);
if (log_level == 0) {
cancel_delayed_work_sync(&ring_buffer->work);
} else {
schedule_delayed_work(&ring_buffer->work,
ring_buffer->interval);
}
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to start logging
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_start_logging(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
int ret = 0, rem, type;
char ring_name[RING_NAME_MAX] = { 0 };
int log_level = 0, flags = 0, time_intval = 0, threshold = 0;
const struct nlattr *iter;
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
nla_for_each_attr(iter, data, len, rem) {
type = nla_type(iter);
switch (type) {
case ATTR_WIFI_LOGGER_RING_ID:
strncpy(ring_name, nla_data(iter),
MIN(sizeof(ring_name) - 1, nla_len(iter)));
break;
case ATTR_WIFI_LOGGER_VERBOSE_LEVEL:
log_level = nla_get_u32(iter);
break;
case ATTR_WIFI_LOGGER_FLAGS:
flags = nla_get_u32(iter);
break;
case ATTR_WIFI_LOGGER_MAX_INTERVAL_SEC:
time_intval = nla_get_u32(iter);
break;
case ATTR_WIFI_LOGGER_MIN_DATA_SIZE:
threshold = nla_get_u32(iter);
break;
default:
PRINTM(MERROR, "Unknown type: %d\n", type);
ret = -EINVAL;
goto done;
}
}
ret = woal_start_logging(priv, ring_name, log_level, flags, time_intval,
threshold);
if (ret < 0) {
PRINTM(MERROR, "Start_logging is failed ret: %d\n", ret);
}
done:
LEAVE();
return ret;
}
/**
* @brief get log data in ring buffer
*
* @param priv A pointer to moal_private struct
* @param ring_name ring name string
*
* @return 0: success 1: fail
*/
static int
woal_trigger_get_ring_data(moal_private *priv, char *ring_name)
{
int ret = 0;
int ring_id;
wifi_ring_buffer *ring_buffer;
ENTER();
ring_id = woal_get_ring_id_by_name(priv, ring_name);
if (!VALID_RING(ring_id)) {
PRINTM(MERROR, "invalid ring_id \n");
ret = -EINVAL;
goto done;
}
ring_buffer = (wifi_ring_buffer *) priv->rings[ring_id];
if (ring_buffer->interval)
cancel_delayed_work_sync(&ring_buffer->work);
schedule_delayed_work(&ring_buffer->work, 0);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to get ring buffer data
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_ring_data(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
int ret = 0, rem, type;
char ring_name[RING_NAME_MAX] = { 0 };
const struct nlattr *iter;
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
nla_for_each_attr(iter, data, len, rem) {
type = nla_type(iter);
switch (type) {
case ATTR_WIFI_LOGGER_RING_ID:
strncpy(ring_name, nla_data(iter),
MIN(sizeof(ring_name) - 1, nla_len(iter)));
break;
default:
PRINTM(MERROR, "Unknown type: %d\n", type);
ret = -EINVAL;
goto done;
}
}
ret = woal_trigger_get_ring_data(priv, ring_name);
if (ret < 0) {
PRINTM(MERROR, "trigger_get_data failed ret:%d\n", ret);
}
done:
LEAVE();
return ret;
}
/**
* @brief get ring buffer entry
*
* @param ring A pointer to wifi_ring_buffer struct
* @param offset entry offset
*
* @return A pointer to wifi_ring_buffer_entry struct
*/
static wifi_ring_buffer_entry *
woal_get_ring_entry(wifi_ring_buffer * ring, t_u32 offset)
{
wifi_ring_buffer_entry *entry =
(wifi_ring_buffer_entry *) (ring->ring_buf + offset);
ENTER();
if (!entry->entry_size)
return (wifi_ring_buffer_entry *) ring->ring_buf;
LEAVE();
return entry;
}
/**
* @brief get next ring buffer entry
*
* @param ring A pointer to wifi_ring_buffer struct
* @param offset next entry offset
*
* @return offset of next entry
*/
static t_u32
woal_get_ring_next_entry(wifi_ring_buffer * ring, t_u32 offset)
{
wifi_ring_buffer_entry *entry =
(wifi_ring_buffer_entry *) (ring->ring_buf + offset);
ENTER();
if (!entry->entry_size) {
entry = (wifi_ring_buffer_entry *) ring->ring_buf;
LEAVE();
return ENTRY_LENGTH(entry);
}
LEAVE();
return offset + ENTRY_LENGTH(entry);
}
/**
* @brief prepare log data to send to HAL
*
* @param priv A pointer to moal_private struct
* @param ring_id ring ID
* @param data A pointer to data buffer
* @param buf_len log data length
*
* @return data length
*/
int
woal_ring_pull_data(moal_private *priv, int ring_id, void *data, t_s32 buf_len)
{
t_s32 avail_len, r_len = 0;
unsigned long flags;
wifi_ring_buffer *ring;
wifi_ring_buffer_entry *hdr;
ENTER();
ring = (wifi_ring_buffer *) priv->rings[ring_id];
if (ring->state != RING_ACTIVE) {
PRINTM(MERROR, "Ring is not active!\n");
goto done;
}
spin_lock_irqsave(&ring->lock, flags);
/* get a fresh pending length */
avail_len = READ_AVAIL_SPACE(ring->wp, ring->rp, ring->ring_size);
while (avail_len > 0 && buf_len > 0) {
hdr = woal_get_ring_entry(ring, ring->rp);
memcpy(data, hdr, ENTRY_LENGTH(hdr));
r_len += ENTRY_LENGTH(hdr);
/* update read pointer */
ring->rp = woal_get_ring_next_entry(ring, ring->rp);
data += ENTRY_LENGTH(hdr);
avail_len -= ENTRY_LENGTH(hdr);
buf_len -= ENTRY_LENGTH(hdr);
ring->ctrl.read_bytes += ENTRY_LENGTH(hdr);
PRINTM(MINFO, "%s read_bytes %d\n", __FUNCTION__,
ring->ctrl.read_bytes);
}
spin_unlock_irqrestore(&ring->lock, flags);
done:
LEAVE();
return r_len;
}
/**
* @brief send vendor event to kernel
*
* @param priv A pointer to moal_private
* @param event vendor event
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
int
woal_ring_buffer_data_vendor_event(IN moal_private *priv, IN int ring_id,
IN t_u8 *data, IN int len)
{
struct wiphy *wiphy = NULL;
struct sk_buff *skb = NULL;
int event_id = 0;
int ret = 0;
wifi_ring_buffer_status ring_status;
ENTER();
if (!priv || !priv->wdev || !priv->wdev->wiphy) {
PRINTM(MERROR, "priv is null \n");
ret = -EINVAL;
goto done;
}
wiphy = priv->wdev->wiphy;
PRINTM(MEVENT, "woal_ring_buffer_data_vendor_event ring_id:%d\n",
ring_id);
event_id = woal_get_event_id(event_wifi_logger_ring_buffer_data);
if (event_max == event_id) {
PRINTM(MERROR, "Not find this event %d \n", event_id);
ret = -EINVAL;
goto done;
}
/**allocate skb*/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
skb = cfg80211_vendor_event_alloc(wiphy, NULL,
len + sizeof(wifi_ring_buffer_status),
event_id, GFP_ATOMIC);
#else
skb = cfg80211_vendor_event_alloc(wiphy,
len + sizeof(wifi_ring_buffer_status),
event_id, GFP_ATOMIC);
#endif
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor event\n");
ret = -ENOMEM;
goto done;
}
woal_get_ring_status(priv, ring_id, &ring_status);
nla_put(skb, ATTR_RING_BUFFER_STATUS, sizeof(wifi_ring_buffer_status),
&ring_status);
DBG_HEXDUMP(MEVT_D, "ring_buffer_data", data, len);
nla_put(skb, ATTR_RING_BUFFER, len, data);
/**send event*/
cfg80211_vendor_event(skb, GFP_ATOMIC);
done:
LEAVE();
return ret;
}
/**
* @brief send log data to HAL
*
* @param priv A pointer to moal_private struct
* @param ring_id ring ID
* @param data A pointer to data buffer
* @param len log data length
* @param ring_status A pointer to wifi_ring_buffer status struct
*
* @return void
*/
static void
woal_ring_data_send(moal_private *priv, int ring_id, const void *data,
const t_u32 len, const wifi_ring_buffer_status ring_status)
{
struct net_device *ndev = priv->netdev;
ENTER();
if (!ndev)
goto done;
woal_ring_buffer_data_vendor_event(priv, ring_id, (t_u8 *)data, len);
done:
LEAVE();
return;
}
/**
* @brief main thread to send log data
*
* @param work A pointer to work_struct struct
*
* @return void
*/
void
woal_ring_poll_worker(struct work_struct *work)
{
struct delayed_work *d_work = to_delayed_work(work);
wifi_ring_buffer *ring_info =
container_of(d_work, wifi_ring_buffer, work);
moal_private *priv = ring_info->priv;
int ringid = ring_info->ring_id;
wifi_ring_buffer_status ring_status;
void *buf;
wifi_ring_buffer_entry *hdr;
t_s32 buflen, rlen;
ENTER();
woal_get_ring_status(priv, ringid, &ring_status);
PRINTM(MINFO, "woal_ring_poll_worker write %d, read %d, size %d\n",
ring_status.written_bytes, ring_status.read_bytes,
ring_status.ring_buffer_byte_size);
if (ring_status.written_bytes > ring_status.read_bytes)
buflen = ring_status.written_bytes - ring_status.read_bytes;
else if (ring_status.written_bytes < ring_status.read_bytes)
buflen = ring_status.ring_buffer_byte_size +
ring_status.written_bytes - ring_status.read_bytes;
else {
PRINTM(MERROR, "No new records\n");
goto exit;
}
buf = kmalloc(buflen, GFP_KERNEL);
if (!buf) {
PRINTM(MERROR, "%s failed to allocate read buf\n",
__FUNCTION__);
LEAVE();
return;
}
rlen = woal_ring_pull_data(priv, ringid, buf, buflen);
hdr = (wifi_ring_buffer_entry *) buf;
while (rlen > 0) {
ring_status.read_bytes += ENTRY_LENGTH(hdr);
woal_ring_data_send(priv, ringid, hdr, ENTRY_LENGTH(hdr),
ring_status);
rlen -= ENTRY_LENGTH(hdr);
hdr = (wifi_ring_buffer_entry *) ((void *)hdr +
ENTRY_LENGTH(hdr));
}
kfree(buf);
if (!ring_info->interval) {
LEAVE();
return;
}
woal_get_ring_status(priv, ring_info->ring_id, &ring_status);
exit:
if (ring_info->interval) {
/* retrigger the work at same interval */
if (READ_AVAIL_SPACE
(ring_info->wp, ring_info->rp, ring_info->ring_size) <= 0)
schedule_delayed_work(d_work, ring_info->interval);
else
schedule_delayed_work(d_work, 0);
}
LEAVE();
return;
}
/**
* @brief add log data to ring buffer
*
* @param priv A pointer to moal_private struct
* @param ring_id ring ID
* @param hdr A pointer to wifi_ring_buffer_entry struct
* @param data A pointer to data buffer
*
* @return 0: success -1: fail
*/
int
woal_ring_push_data(moal_private *priv, int ring_id,
wifi_ring_buffer_entry * hdr, void *data)
{
unsigned long flags;
t_u32 w_len;
wifi_ring_buffer *ring;
wifi_ring_buffer_entry *w_entry;
int ret = 0;
ENTER();
ring = (wifi_ring_buffer *) priv->rings[ring_id];
if (ring->state != RING_ACTIVE) {
PRINTM(MERROR, "Ring is not active\n");
ret = -EINVAL;
goto done;
}
spin_lock_irqsave(&ring->lock, flags);
w_len = ENTRY_LENGTH(hdr);
/* prep the space */
do {
if (ring->rp == ring->wp)
break;
if (ring->rp < ring->wp) {
if (ring->ring_size - ring->wp == w_len) {
if (ring->rp == 0)
ring->rp =
woal_get_ring_next_entry(ring,
ring->
rp);
break;
} else if (ring->ring_size - ring->wp < w_len) {
if (ring->rp == 0)
ring->rp =
woal_get_ring_next_entry(ring,
ring->
rp);
ring->wp = 0;
continue;
} else {
break;
}
}
if (ring->rp > ring->wp) {
if (ring->rp - ring->wp <= w_len) {
ring->rp =
woal_get_ring_next_entry(ring,
ring->rp);
if (ring->rp >= ring->ring_size) {
PRINTM(MINFO,
"log size exceed ring size\n");
ring->rp = 0;
break;
} else
continue;
} else {
break;
}
}
} while (1);
w_entry = (wifi_ring_buffer_entry *) (ring->ring_buf + ring->wp);
/* header */
memcpy(w_entry, hdr, RING_ENTRY_SIZE);
/* payload */
memcpy((char *)w_entry + RING_ENTRY_SIZE, data, w_entry->entry_size);
/* update write pointer */
ring->wp += w_len;
/* update statistics */
ring->ctrl.written_records++;
ring->ctrl.written_bytes += w_len;
spin_unlock_irqrestore(&ring->lock, flags);
PRINTM(MINFO, "%s : written_records %d, written_bytes %d\n",
__FUNCTION__, ring->ctrl.written_records,
ring->ctrl.written_bytes);
/* if the current pending size is bigger than threshold */
if (ring->threshold > 0 &&
(READ_AVAIL_SPACE(ring->wp, ring->rp, ring->ring_size) >=
ring->threshold) && ring->interval != 0)
schedule_delayed_work(&ring->work, 0);
done:
LEAVE();
return ret;
}
/**
* @brief This function will init wifi logger ring buffer
*
* @param priv A pointer to moal_private structure
* @param ring_buff A pointer to wifi_ring_buffer structure
* @param ringIdx Ring buffer ID
* @param name Ring buffer name
* @param ring_sz Ring buffer size
*
* @return 0 - success
*/
static int
woal_init_ring_buffer_internal(moal_private *priv, void **ring, t_u16 ringIdx,
t_u8 *name, t_u32 ring_sz)
{
unsigned long flags;
wifi_ring_buffer *ring_buff;
ENTER();
*ring = vmalloc(sizeof(wifi_ring_buffer));
memset(*ring, 0, sizeof(wifi_ring_buffer));
ring_buff = (wifi_ring_buffer *) * ring;
ring_buff->ring_buf = vmalloc(ring_sz);
if (!unlikely(ring_buff->ring_buf)) {
PRINTM(MERROR, "WiFi Logger: ring buffer data alloc failed\n");
}
memset(ring_buff->ring_buf, 0, ring_sz);
spin_lock_init(&ring_buff->lock);
spin_lock_irqsave(&ring_buff->lock, flags);
ring_buff->rp = ring_buff->wp = 0;
INIT_DELAYED_WORK(&ring_buff->work, woal_ring_poll_worker);
memcpy(ring_buff->name, name, MIN(strlen(name), RING_NAME_MAX));
ring_buff->name[RING_NAME_MAX - 1] = 0;
ring_buff->ring_id = ringIdx;
ring_buff->ring_size = ring_sz;
ring_buff->state = RING_SUSPEND;
ring_buff->threshold = ring_buff->ring_size / 2;
ring_buff->priv = priv;
spin_unlock_irqrestore(&ring_buff->lock, flags);
LEAVE();
return 0;
}
/**
* @brief init each ring in moal_private
*
* @param priv A pointer to moal_private struct
*
* @return data length
*/
static int
woal_init_ring_buffer(moal_private *priv)
{
ENTER();
woal_init_ring_buffer_internal(priv, &priv->rings[VERBOSE_RING_ID],
VERBOSE_RING_ID, VERBOSE_RING_NAME,
DEFAULT_RING_BUFFER_SIZE);
woal_init_ring_buffer_internal(priv, &priv->rings[EVENT_RING_ID],
EVENT_RING_ID, EVENT_RING_NAME,
DEFAULT_RING_BUFFER_SIZE);
LEAVE();
return 0;
}
/**
* @brief deinit each ring in moal_private
*
* @param priv A pointer to moal_private struct
*
* @return data length
*/
static int
woal_deinit_ring_buffer(moal_private *priv)
{
int i;
enum ring_state ring_state = RING_STOP;
unsigned long lock_flags = 0;
wifi_ring_buffer *ring_buff;
ENTER();
for (i = 0; i < RING_ID_MAX - 1; i++) {
ring_buff = (wifi_ring_buffer *) priv->rings[i];
spin_lock_irqsave(&ring_buff->lock, lock_flags);
ring_state = ring_buff->state;
if (ring_state == RING_ACTIVE) {
ring_buff->state = RING_STOP;
}
spin_unlock_irqrestore(&ring_buff->lock, lock_flags);
if (ring_state == RING_ACTIVE)
cancel_delayed_work_sync(&ring_buff->work);
vfree(ring_buff->ring_buf);
ring_buff->ring_buf = NULL;
vfree(ring_buff);
priv->rings[i] = NULL;
}
LEAVE();
return 0;
}
/**
* @brief add log data to ring buffer
*
* @param priv A pointer to moal_private struct
* @param ring_id ring ID
* @param hdr A pointer to wifi_ring_buffer_entry struct
* @param data A pointer to data buffer
*
* @return 0: success -1: fail
*/
int
woal_ring_event_logger(moal_private *priv, int ring_id, pmlan_event pmevent)
{
t_u8 event_buf[100] = { 0 };
wifi_ring_buffer_driver_connectivity_event *connectivity_event;
tlv_log *tlv;
t_u8 *pos;
wifi_ring_buffer_entry msg_hdr;
wifi_ring_buffer *ring;
ENTER();
ring = (wifi_ring_buffer *) priv->rings[ring_id];
if (ring->state != RING_ACTIVE) {
PRINTM(MINFO, "Ring is not active\n");
goto done;
}
switch (pmevent->event_id) {
case MLAN_EVENT_ID_DRV_ASSOC_SUCC_LOGGER:
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
assoc_logger_data *pbss_desc =
(assoc_logger_data *) pmevent->event_buf;
memset(&msg_hdr, 0, sizeof(msg_hdr));
msg_hdr.flags |= RING_BUFFER_ENTRY_FLAGS_HAS_TIMESTAMP;
msg_hdr.type = ENTRY_TYPE_CONNECT_EVENT;
connectivity_event =
(wifi_ring_buffer_driver_connectivity_event *)
event_buf;
connectivity_event->event = WIFI_EVENT_ASSOC_COMPLETE;
pos = (t_u8 *)connectivity_event->tlvs;
if (pbss_desc->oui) {
tlv = (tlv_log *) pos;
tlv->tag = WIFI_TAG_VENDOR_SPECIFIC;
tlv->length = MLAN_MAC_ADDR_LENGTH / 2;
memcpy(tlv->value, pbss_desc->oui, tlv->length);
msg_hdr.entry_size +=
tlv->length + TLV_LOG_HEADER_LEN;
pos = pos + tlv->length + TLV_LOG_HEADER_LEN;
}
if (pbss_desc->bssid) {
tlv = (tlv_log *) pos;
tlv->tag = WIFI_TAG_BSSID;
tlv->length = sizeof(pbss_desc->bssid);
memcpy(tlv->value, pbss_desc->bssid,
sizeof(pbss_desc->bssid));
msg_hdr.entry_size +=
tlv->length + TLV_LOG_HEADER_LEN;
pos = pos + tlv->length + TLV_LOG_HEADER_LEN;
}
if (pbss_desc->ssid) {
tlv = (tlv_log *) pos;
tlv->tag = WIFI_TAG_SSID;
tlv->length = strlen(pbss_desc->ssid);
memcpy(tlv->value, pbss_desc->ssid,
tlv->length);
msg_hdr.entry_size +=
tlv->length + TLV_LOG_HEADER_LEN;
pos = pos + tlv->length + TLV_LOG_HEADER_LEN;
}
if (pbss_desc->rssi) {
tlv = (tlv_log *) pos;
tlv->tag = WIFI_TAG_RSSI;
tlv->length = sizeof(pbss_desc->rssi);
memcpy(tlv->value, &pbss_desc->rssi,
tlv->length);
msg_hdr.entry_size +=
tlv->length + TLV_LOG_HEADER_LEN;
pos = pos + tlv->length + TLV_LOG_HEADER_LEN;
}
if (pbss_desc->channel) {
tlv = (tlv_log *) pos;
tlv->tag = WIFI_TAG_CHANNEL;
tlv->length = sizeof(pbss_desc->channel);
memcpy(tlv->value, &pbss_desc->channel,
sizeof(pbss_desc->channel));
msg_hdr.entry_size +=
tlv->length + TLV_LOG_HEADER_LEN;
}
msg_hdr.entry_size += sizeof(connectivity_event->event);
DBG_HEXDUMP(MCMD_D, "connectivity_event",
(t_u8 *)connectivity_event,
msg_hdr.entry_size);
woal_ring_push_data(priv, ring_id, &msg_hdr,
connectivity_event);
}
break;
case MLAN_EVENT_ID_DRV_ASSOC_FAILURE_LOGGER:
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
int status_code = *(int *)pmevent->event_buf;
memset(&msg_hdr, 0, sizeof(msg_hdr));
msg_hdr.flags |= RING_BUFFER_ENTRY_FLAGS_HAS_TIMESTAMP;
msg_hdr.type = ENTRY_TYPE_CONNECT_EVENT;
connectivity_event =
(wifi_ring_buffer_driver_connectivity_event *)
event_buf;
connectivity_event->event = WIFI_EVENT_ASSOC_COMPLETE;
pos = (t_u8 *)connectivity_event->tlvs;
tlv = (tlv_log *) pos;
tlv->tag = WIFI_TAG_STATUS;
tlv->length = sizeof(status_code);
memcpy(tlv->value, &status_code, sizeof(status_code));
msg_hdr.entry_size += tlv->length + 4;
msg_hdr.entry_size += sizeof(connectivity_event->event);
woal_ring_push_data(priv, ring_id, &msg_hdr,
connectivity_event);
}
break;
case MLAN_EVENT_ID_DRV_DISCONNECT_LOGGER:
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
t_u16 reason_code = *(t_u16 *)pmevent->event_buf;
memset(&msg_hdr, 0, sizeof(msg_hdr));
msg_hdr.flags |= RING_BUFFER_ENTRY_FLAGS_HAS_TIMESTAMP;
msg_hdr.type = ENTRY_TYPE_CONNECT_EVENT;
connectivity_event =
(wifi_ring_buffer_driver_connectivity_event *)
event_buf;
connectivity_event->event = WIFI_EVENT_ASSOC_COMPLETE;
pos = (t_u8 *)connectivity_event->tlvs;
tlv = (tlv_log *) pos;
tlv->tag = WIFI_TAG_REASON_CODE;
tlv->length = sizeof(reason_code);
memcpy(tlv->value, &reason_code, sizeof(reason_code));
msg_hdr.entry_size += tlv->length + 4;
msg_hdr.entry_size += sizeof(connectivity_event->event);
woal_ring_push_data(priv, ring_id, &msg_hdr,
connectivity_event);
}
break;
default:
break;
}
done:
LEAVE();
return 0;
}
/**
* @brief send vendor event to kernel
*
* @param priv A pointer to moal_private struct
* @param wake_reason wake_reason
*
* @return 0: success 1: fail
*/
int
woal_wake_reason_vendor_event(IN moal_private *priv,
IN mlan_ds_hs_wakeup_reason wake_reason)
{
struct wiphy *wiphy = NULL;
struct sk_buff *skb = NULL;
int event_id = 0;
int ret = 0;
ENTER();
if (!priv || !priv->wdev || !priv->wdev->wiphy) {
PRINTM(MERROR, "priv is null \n");
ret = -EINVAL;
goto done;
}
wiphy = priv->wdev->wiphy;
event_id = woal_get_event_id(event_wake_reason_report);
if (event_max == event_id) {
PRINTM(MERROR, "Not find this event %d \n", event_id);
ret = MLAN_STATUS_FAILURE;
goto done;
}
/**allocate skb*/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
skb = cfg80211_vendor_event_alloc(wiphy, NULL,
sizeof(wake_reason.hs_wakeup_reason),
event_id, GFP_ATOMIC);
#else
skb = cfg80211_vendor_event_alloc(wiphy,
sizeof(wake_reason.hs_wakeup_reason),
event_id, GFP_ATOMIC);
#endif
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor event\n");
ret = -ENOMEM;
goto done;
}
PRINTM(MINFO, "wake_reason.hs_wakeup_reason = %d\n",
wake_reason.hs_wakeup_reason);
nla_put_u16(skb, ATTR_WAKE_REASON_STAT, wake_reason.hs_wakeup_reason);
/**send event*/
cfg80211_vendor_event(skb, GFP_ATOMIC);
done:
PRINTM(MINFO, "wake reason vendor event ret %d\n", ret);
LEAVE();
return ret;
}
/**
* @brief log wake reason
*
* @param priv A pointer to moal_private struct
* @param wake_reason wake_reason
*
* @return 0: success -1: fail
*/
int
woal_wake_reason_logger(moal_private *priv,
mlan_ds_hs_wakeup_reason wake_reason)
{
int ret = 0;
ENTER();
ret = woal_wake_reason_vendor_event(priv, wake_reason);
LEAVE();
return ret;
}
/**
* @brief vendor command to start packet fate monitor
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_start_packet_fate_monitor(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
int ret = 0;
ENTER();
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
priv->pkt_fate_monitor_enable = MTRUE;
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief send vendor event to kernel
*
* @param priv A pointer to moal_private struct
* @param pkt_type tx or rx
* @param fate packet fate
* @param payload_type type of payload
* @param drv_ts_usec driver time in usec
* @param fw_ts_usec firmware time in usec
* @param data frame data
* @param len frame data len
*
* @return 0: success 1: fail
*/
int
woal_packet_fate_vendor_event(IN moal_private *priv,
IN packet_fate_packet_type pkt_type, IN t_u8 fate,
IN frame_type payload_type, IN t_u32 drv_ts_usec,
IN t_u32 fw_ts_usec, IN t_u8 *data, IN t_u32 len)
{
struct wiphy *wiphy = NULL;
struct sk_buff *skb = NULL;
int event_id = 0;
int ret = 0;
PACKET_FATE_REPORT fate_report;
ENTER();
if (!priv || !priv->wdev || !priv->wdev->wiphy) {
PRINTM(MERROR, "priv is null \n");
ret = -EINVAL;
goto done;
}
wiphy = priv->wdev->wiphy;
event_id = woal_get_event_id(event_packet_fate_monitor);
if (event_max == event_id) {
PRINTM(MERROR, "Not find this event %d \n", event_id);
ret = MLAN_STATUS_FAILURE;
goto done;
}
/**allocate skb*/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
skb = cfg80211_vendor_event_alloc(wiphy, NULL,
len + sizeof(PACKET_FATE_REPORT),
event_id, GFP_ATOMIC);
#else
skb = cfg80211_vendor_event_alloc(wiphy,
len + sizeof(PACKET_FATE_REPORT),
event_id, GFP_ATOMIC);
#endif
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor event\n");
ret = -ENOMEM;
goto done;
}
memset(&fate_report, 0, sizeof(PACKET_FATE_REPORT));
if (pkt_type == PACKET_TYPE_TX) {
fate_report.u.tx_report_i.fate = fate;
fate_report.u.tx_report_i.frame_inf.payload_type = payload_type;
fate_report.u.tx_report_i.frame_inf.driver_timestamp_usec =
drv_ts_usec;
fate_report.u.tx_report_i.frame_inf.firmware_timestamp_usec =
fw_ts_usec;
fate_report.u.tx_report_i.frame_inf.frame_len = len;
nla_put(skb, ATTR_PACKET_FATE_TX, sizeof(PACKET_FATE_REPORT),
&fate_report);
} else {
fate_report.u.rx_report_i.fate = fate;
fate_report.u.rx_report_i.frame_inf.payload_type = payload_type;
fate_report.u.rx_report_i.frame_inf.driver_timestamp_usec =
drv_ts_usec;
fate_report.u.rx_report_i.frame_inf.firmware_timestamp_usec =
fw_ts_usec;
fate_report.u.rx_report_i.frame_inf.frame_len = len;
nla_put(skb, ATTR_PACKET_FATE_RX, sizeof(PACKET_FATE_REPORT),
&fate_report);
}
DBG_HEXDUMP(MCMD_D, "fate_report", (t_u8 *)&fate_report,
sizeof(PACKET_FATE_REPORT));
nla_put(skb, ATTR_PACKET_FATE_DATA, len, data);
DBG_HEXDUMP(MCMD_D, "packet_fate_data", data, len);
/**send event*/
cfg80211_vendor_event(skb, GFP_ATOMIC);
done:
PRINTM(MINFO, "packet fate vendor event ret %d\n", ret);
LEAVE();
return ret;
}
/**
* @brief log packet fate
*
* @param priv A pointer to moal_private struct
* @param pkt_type tx or rx
* @param fate packet fate
* @param payload_type type of payload
* @param drv_ts_usec driver time in usec
* @param fw_ts_usec firmware time in usec
* @param data frame data
* @param len frame data len
*
* @return 0: success -1: fail
*/
int
woal_packet_fate_monitor(moal_private *priv, packet_fate_packet_type pkt_type,
t_u8 fate, frame_type payload_type, t_u32 drv_ts_usec,
t_u32 fw_ts_usec, t_u8 *data, t_u32 len)
{
int ret = 0;
ENTER();
if (priv->pkt_fate_monitor_enable)
ret = woal_packet_fate_vendor_event(priv, pkt_type, fate,
payload_type, drv_ts_usec,
fw_ts_usec, data, len);
PRINTM(MINFO, "packet fate monitor ret %d\n", ret);
LEAVE();
return ret;
}
/**
* @brief init packet_filter in moal_private
*
* @param priv A pointer to moal_private struct
*
* @return 0: success -1: fail
*/
static int
woal_init_packet_filter(moal_private *priv)
{
int ret = 0;
packet_filter *pkt_filter = NULL;
ENTER();
pkt_filter = vmalloc(sizeof(pkt_filter));
if (!unlikely(pkt_filter)) {
PRINTM(MERROR, "WiFi Logger: packet_filter alloc failed\n");
ret = -ENOMEM;
goto done;
}
memset(pkt_filter, 0, sizeof(packet_filter));
spin_lock_init(&pkt_filter->lock);
pkt_filter->packet_filter_max_len = PACKET_FILTER_MAX_LEN;
pkt_filter->packet_filter_len = 0;
pkt_filter->packet_filter_version = APF_VERSION;
pkt_filter->state = PACKET_FILTER_STATE_STOP;
priv->packet_filter = pkt_filter;
done:
LEAVE();
return ret;
}
/**
* @brief deinit packet_filter in moal_private
*
* @param priv A pointer to moal_private struct
*
* @return 0: success -1: fail
*/
static int
woal_deinit_packet_filter(moal_private *priv)
{
int ret = 0;
packet_filter *pkt_filter = NULL;
unsigned long flags;
ENTER();
pkt_filter = priv->packet_filter;
if (!unlikely(pkt_filter)) {
goto done;
}
spin_lock_irqsave(&pkt_filter->lock, flags);
pkt_filter->state = PACKET_FILTER_STATE_INIT;
spin_unlock_irqrestore(&pkt_filter->lock, flags);
vfree(pkt_filter);
priv->packet_filter = NULL;
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to set packet filter
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_set_packet_filter(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
int ret = 0, rem, type;
const struct nlattr *iter;
packet_filter *pkt_filter = NULL;
t_u32 packet_filter_len = 0;
unsigned long flags;
ENTER();
pkt_filter = priv->packet_filter;
if (!unlikely(pkt_filter) ||
pkt_filter->state == PACKET_FILTER_STATE_INIT) {
PRINTM(MERROR, "packet_filter not init\n");
ret = -EINVAL;
goto done;
}
nla_for_each_attr(iter, data, len, rem) {
type = nla_type(iter);
switch (type) {
case ATTR_PACKET_FILTER_TOTAL_LENGTH:
packet_filter_len = nla_get_u32(iter);
if (packet_filter_len >
pkt_filter->packet_filter_max_len) {
PRINTM(MERROR,
"packet_filter_len exceed max\n");
ret = -EINVAL;
goto done;
}
break;
case ATTR_PACKET_FILTER_PROGRAM:
spin_lock_irqsave(&pkt_filter->lock, flags);
strncpy(pkt_filter->packet_filter_program,
nla_data(iter), MIN(packet_filter_len,
nla_len(iter)));
pkt_filter->packet_filter_len =
MIN(packet_filter_len, nla_len(iter));
pkt_filter->state = PACKET_FILTER_STATE_START;
spin_unlock_irqrestore(&pkt_filter->lock, flags);
DBG_HEXDUMP(MCMD_D, "packet_filter_program",
pkt_filter->packet_filter_program,
pkt_filter->packet_filter_len);
break;
default:
PRINTM(MERROR, "Unknown type: %d\n", type);
ret = -EINVAL;
goto done;
}
}
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to get packet filter capability
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_get_packet_filter_capability(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
int ret = 0;
packet_filter *pkt_filter;
ENTER();
pkt_filter = priv->packet_filter;
if (!unlikely(pkt_filter)) {
PRINTM(MERROR, "packet_filter not init\n");
ret = -EINVAL;
goto done;
}
reply_len =
sizeof(pkt_filter->packet_filter_version) +
sizeof(pkt_filter->packet_filter_max_len);
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
nla_put_u32(skb, ATTR_PACKET_FILTER_VERSION,
pkt_filter->packet_filter_version);
nla_put_u32(skb, ATTR_PACKET_FILTER_MAX_LEN,
pkt_filter->packet_filter_max_len);
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* Runs a packet filtering program over a packet.
*
* @param program the program bytecode.
* @param program_len the length of {@code apf_program} in bytes.
* @param packet the packet bytes, starting from the 802.3 header and not
* including any CRC bytes at the end.
* @param packet_len the length of {@code packet} in bytes.
* @param filter_age the number of seconds since the filter was programmed.
*
* @return non-zero if packet should be passed to AP, zero if
* packet should be dropped.
*/
int
accept_packet(const t_u8 *program, t_u32 program_len, const t_u8 *packet,
t_u32 packet_len, t_u32 filter_age)
{
/* Program counter. */
t_u32 pc = 0;
/* Memory slot values. */
t_u32 memory[MEMORY_ITEMS] = { };
/* Register values. */
t_u32 registers[2] = { };
/* Count of instructions remaining to execute. This is done to ensure an
* upper bound on execution time. It should never be hit and is only for
* safety. Initialize to the number of bytes in the program which is an
* upper bound on the number of instructions in the program. */
t_u32 instructions_remaining = program_len;
t_u8 bytecode;
t_u32 opcode;
t_u32 reg_num;
t_u32 len_field;
t_u32 imm_len;
t_u32 end_offs;
t_u32 val = 0;
t_u32 last_packet_offs;
t_u32 imm = 0;
int32_t signed_imm = 0;
t_u32 offs = 0;
t_u32 i;
t_u32 load_size;
/* Is offset within program bounds? */
#define IN_PROGRAM_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < program_len)
/* Is offset within packet bounds? */
#define IN_PACKET_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < packet_len)
/* Verify an internal condition and accept packet if it fails. */
#define ASSERT_RETURN(c) if (!(c)) return PASS_PACKET
/* Accept packet if not within program bounds */
#define ASSERT_IN_PROGRAM_BOUNDS(p) ASSERT_RETURN(IN_PROGRAM_BOUNDS(p))
/* Accept packet if not within packet bounds */
#define ASSERT_IN_PACKET_BOUNDS(p) ASSERT_RETURN(IN_PACKET_BOUNDS(p))
/* Accept packet if not within program or not ahead of program counter */
#define ASSERT_FORWARD_IN_PROGRAM(p) ASSERT_RETURN(IN_PROGRAM_BOUNDS(p) && (p) >= pc)
/* Fill in pre-filled memory slot values. */
memory[MEMORY_OFFSET_PACKET_SIZE] = packet_len;
memory[MEMORY_OFFSET_FILTER_AGE] = filter_age;
ASSERT_IN_PACKET_BOUNDS(APF_FRAME_HEADER_SIZE);
/* Only populate if IP version is IPv4. */
if ((packet[APF_FRAME_HEADER_SIZE] & 0xf0) == 0x40) {
memory[MEMORY_OFFSET_IPV4_HEADER_SIZE] =
(packet[APF_FRAME_HEADER_SIZE] & 15) * 4;
}
do {
if (pc == program_len) {
return PASS_PACKET;
} else if (pc == (program_len + 1)) {
return DROP_PACKET;
}
ASSERT_IN_PROGRAM_BOUNDS(pc);
bytecode = program[pc++];
opcode = EXTRACT_OPCODE(bytecode);
reg_num = EXTRACT_REGISTER(bytecode);
/* All instructions have immediate fields, so load them now. */
len_field = EXTRACT_IMM_LENGTH(bytecode);
imm = 0;
signed_imm = 0;
#define REG (registers[reg_num])
#define OTHER_REG (registers[reg_num ^ 1])
if (len_field != 0) {
imm_len = 1 << (len_field - 1);
ASSERT_FORWARD_IN_PROGRAM(pc + imm_len - 1);
for (i = 0; i < imm_len; i++)
imm = (imm << 8) | program[pc++];
/* Sign extend imm into signed_imm. */
signed_imm = imm << ((4 - imm_len) * 8);
signed_imm >>= (4 - imm_len) * 8;
}
switch (opcode) {
case LDB_OPCODE:
case LDH_OPCODE:
case LDW_OPCODE:
case LDBX_OPCODE:
case LDHX_OPCODE:
case LDWX_OPCODE:{
offs = imm;
if (opcode >= LDBX_OPCODE) {
/* Note: this can overflow and actually decrease offs. */
offs += registers[1];
}
ASSERT_IN_PACKET_BOUNDS(offs);
switch (opcode) {
case LDB_OPCODE:
case LDBX_OPCODE:
load_size = 1;
break;
case LDH_OPCODE:
case LDHX_OPCODE:
load_size = 2;
break;
case LDW_OPCODE:
case LDWX_OPCODE:
load_size = 4;
break;
/* Immediately enclosing switch statement guarantees
* opcode cannot be any other value. */
}
end_offs = offs + (load_size - 1);
/* Catch overflow/wrap-around. */
ASSERT_RETURN(end_offs >= offs);
ASSERT_IN_PACKET_BOUNDS(end_offs);
val = 0;
while (load_size--)
val = (val << 8) | packet[offs++];
REG = val;
break;
}
case JMP_OPCODE:
/* This can jump backwards. Infinite looping prevented by instructions_remaining. */
pc += imm;
break;
case JEQ_OPCODE:
case JNE_OPCODE:
case JGT_OPCODE:
case JLT_OPCODE:
case JSET_OPCODE:
case JNEBS_OPCODE:{
/* Load second immediate field. */
t_u32 cmp_imm = 0;
if (reg_num == 1) {
cmp_imm = registers[1];
} else if (len_field != 0) {
t_u32 cmp_imm_len =
1 << (len_field - 1);
ASSERT_FORWARD_IN_PROGRAM(pc +
cmp_imm_len -
1);
for (i = 0; i < cmp_imm_len; i++)
cmp_imm =
(cmp_imm << 8) |
program[pc++];
}
switch (opcode) {
case JEQ_OPCODE:
if (registers[0] == cmp_imm)
pc += imm;
break;
case JNE_OPCODE:
if (registers[0] != cmp_imm)
pc += imm;
break;
case JGT_OPCODE:
if (registers[0] > cmp_imm)
pc += imm;
break;
case JLT_OPCODE:
if (registers[0] < cmp_imm)
pc += imm;
break;
case JSET_OPCODE:
if (registers[0] & cmp_imm)
pc += imm;
break;
case JNEBS_OPCODE:{
/* cmp_imm is size in bytes of data to compare.
* pc is offset of program bytes to compare.
* imm is jump target offset.
* REG is offset of packet bytes to compare. */
ASSERT_FORWARD_IN_PROGRAM(pc +
cmp_imm
- 1);
ASSERT_IN_PACKET_BOUNDS(REG);
last_packet_offs =
REG + cmp_imm - 1;
ASSERT_RETURN(last_packet_offs
>= REG);
ASSERT_IN_PACKET_BOUNDS
(last_packet_offs);
if (memcmp
(program + pc, packet + REG,
cmp_imm))
pc += imm;
/* skip past comparison bytes */
pc += cmp_imm;
break;
}
}
break;
}
case ADD_OPCODE:
registers[0] += reg_num ? registers[1] : imm;
break;
case MUL_OPCODE:
registers[0] *= reg_num ? registers[1] : imm;
break;
case DIV_OPCODE:{
const t_u32 div_operand =
reg_num ? registers[1] : imm;
ASSERT_RETURN(div_operand);
registers[0] /= div_operand;
break;
}
case AND_OPCODE:
registers[0] &= reg_num ? registers[1] : imm;
break;
case OR_OPCODE:
registers[0] |= reg_num ? registers[1] : imm;
break;
case SH_OPCODE:{
const int32_t shift_val =
reg_num ? (int32_t) registers[1] :
signed_imm;
if (shift_val > 0)
registers[0] <<= shift_val;
else
registers[0] >>= -shift_val;
break;
}
case LI_OPCODE:
REG = signed_imm;
break;
case EXT_OPCODE:
if (
/* If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result,
* instead just enforce that imm is unsigned (so it's always greater or equal to 0). */
#if LDM_EXT_OPCODE == 0
ENFORCE_UNSIGNED(imm) &&
#else
imm >= LDM_EXT_OPCODE &&
#endif
imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
REG = memory[imm - LDM_EXT_OPCODE];
} else if (imm >= STM_EXT_OPCODE &&
imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
memory[imm - STM_EXT_OPCODE] = REG;
} else
switch (imm) {
case NOT_EXT_OPCODE:
REG = ~REG;
break;
case NEG_EXT_OPCODE:
REG = -REG;
break;
case SWAP_EXT_OPCODE:{
t_u32 tmp = REG;
REG = OTHER_REG;
OTHER_REG = tmp;
break;
}
case MOV_EXT_OPCODE:
REG = OTHER_REG;
break;
/* Unknown extended opcode */
default:
/* Bail out */
return PASS_PACKET;
}
break;
/* Unknown opcode */
default:
/* Bail out */
return PASS_PACKET;
}
} while (instructions_remaining--);
return PASS_PACKET;
}
/**
* @brief filter packet
*
* @param priv A pointer to moal_private struct
* @param data packet data
* @param len packet len
* @param filter_age filter age
* @return non-zero if packet should be passed to AP, zero if
* packet should be dropped.
*/
int
woal_filter_packet(moal_private *priv, t_u8 *data, t_u32 len, t_u32 filter_age)
{
packet_filter *pkt_filter = NULL;
int ret = PASS_PACKET;
unsigned long flags;
ENTER();
pkt_filter = priv->packet_filter;
if (!unlikely(pkt_filter)) {
PRINTM(MINFO, "packet_filter not init\n");
goto done;
}
if (pkt_filter->state != PACKET_FILTER_STATE_START)
goto done;
DBG_HEXDUMP(MCMD_D, "packet_filter_program",
pkt_filter->packet_filter_program,
pkt_filter->packet_filter_len);
DBG_HEXDUMP(MCMD_D, "packet_filter_data", data, len);
spin_lock_irqsave(&pkt_filter->lock, flags);
ret = accept_packet(pkt_filter->packet_filter_program,
pkt_filter->packet_filter_len, data, len,
filter_age);
spin_unlock_irqrestore(&pkt_filter->lock, flags);
done:
PRINTM(MINFO, "packet filter ret %d\n", ret);
LEAVE();
return ret;
}
/**
* @brief send vendor event to kernel
*
* @param priv A pointer to moal_private
* @param cmd nan_cmd
*
* @return 0: success 1: fail
*/
int
woal_nan_vendor_event(IN moal_private *priv, nan_cmd cmd)
{
struct wiphy *wiphy = NULL;
struct sk_buff *skb = NULL;
int ret = 0;
int event_id = 0;
ENTER();
if (!priv || !priv->wdev || !priv->wdev->wiphy) {
PRINTM(MERROR, "priv is null \n");
ret = -EINVAL;
goto done;
}
wiphy = priv->wdev->wiphy;
PRINTM(MEVENT, "woal_nan_worker\n");
event_id = woal_get_event_id(event_nan_cb);
if (event_max == event_id) {
PRINTM(MERROR, "Not find this event %d \n", event_id);
ret = -EINVAL;
goto done;
}
/**allocate skb*/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
skb = cfg80211_vendor_event_alloc(wiphy, NULL, sizeof(nan_cmd),
event_id, GFP_ATOMIC);
#else
skb = cfg80211_vendor_event_alloc(wiphy, sizeof(nan_cmd), event_id,
GFP_ATOMIC);
#endif
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor event\n");
ret = -ENOMEM;
goto done;
}
nla_put(skb, ATTR_NAN_FAKE, sizeof(NanHeader_Ext), &cmd.nan_header_ext);
if (cmd.indicate_enable) {
nla_put_u32(skb, ATTR_NAN_IND, cmd.indicate_type);
}
PRINTM(MCMND, "woal_nan_vendor_event %d, %d, %d,%d\n",
cmd.nan_header_ext.nan_header.MsgId,
cmd.nan_header_ext.nan_header.transactionId, cmd.indicate_enable,
cmd.indicate_type);
/**send event*/
cfg80211_vendor_event(skb, GFP_ATOMIC);
done:
LEAVE();
return ret;
}
/**
* @brief worker to send vendor event
*
* @param work A pointer to work_struct struct
*
* @return void
*/
void
woal_nan_worker(struct work_struct *work)
{
struct delayed_work *d_work = to_delayed_work(work);
nan_cb *nan = container_of(d_work, nan_cb, response_work);
moal_private *priv = nan->priv;
nan_cmd cmd;
int ret;
ENTER();
PRINTM(MCMND, "fifo len: %u\n", kfifo_len(&nan->cmd_fifo));
ret = kfifo_out_peek(&nan->cmd_fifo, &cmd, sizeof(nan_cmd));
PRINTM(MCMND, "fifo ret: %u\n", ret);
if (ret) {
kfifo_out(&nan->cmd_fifo, &cmd, sizeof(nan_cmd));
} else {
PRINTM(MERROR, "not enough element:\n");
goto done;
}
woal_nan_vendor_event(priv, cmd);
ret = kfifo_out_peek(&nan->cmd_fifo, &cmd, sizeof(nan_cmd));
if (ret)
schedule_delayed_work(&nan->response_work, 0);
done:
LEAVE();
return;
}
/**
* @brief init nan in moal_private
*
* @param priv A pointer to moal_private struct
*
* @return 0: success -1: fail
*/
static int
woal_init_nan(moal_private *priv)
{
int ret = 0;
nan_cb *nan;
moal_handle *phandle = priv->phandle;
ENTER();
nan = vmalloc(sizeof(nan_cb));
if (!unlikely(nan)) {
PRINTM(MERROR, "WiFi Logger: packet_filter alloc failed\n");
ret = -ENOMEM;
goto done;
}
if (!unlikely(phandle)) {
PRINTM(MERROR, "WiFi Logger: phandle is null\n");
ret = -ENOMEM;
goto done;
}
memset(nan, 0, sizeof(nan_cb));
INIT_DELAYED_WORK(&nan->response_work, woal_nan_worker);
nan->priv = priv;
ret = kfifo_alloc(&nan->cmd_fifo, CMD_FIFO_SIZE * sizeof(nan_cmd),
GFP_KERNEL);
if (ret) {
PRINTM(MERROR, "error kfifo_alloc\n");
goto done;
}
priv->nan_cb = nan;
done:
LEAVE();
return ret;
}
/**
* @brief deinit nan in moal_private
*
* @param priv A pointer to moal_private struct
*
* @return 0: success -1: fail
*/
static int
woal_deinit_nan(moal_private *priv)
{
int ret = 0;
nan_cb *nan = NULL;
ENTER();
nan = priv->nan_cb;
if (!unlikely(nan)) {
goto done;
}
cancel_delayed_work_sync(&nan->response_work);
kfifo_free(&nan->cmd_fifo);
vfree(nan);
priv->nan_cb = NULL;
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to enable nan
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
nan_handler(struct wiphy *wiphy, struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
t_u32 reply_len = 0;
int ret = 0;
int rem, type;
const struct nlattr *iter;
nan_cb *nan = priv->nan_cb;
nan_cmd cmd;
t_u32 indicate_number_total = 0;
t_u32 indicate_array[MAX_INDICATE_ARRAY_SIZE] = { 0 };
int i = 0;
ENTER();
memset(&cmd, 0, sizeof(nan_cmd));
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = -ENOMEM;
goto done;
}
if (!unlikely(nan)) {
PRINTM(MERROR, "WiFi hal: nan not init\n");
ret = -EINVAL;
goto done;
}
nla_for_each_attr(iter, data, len, rem) {
type = nla_type(iter);
switch (type) {
case ATTR_NAN_FAKE:
memcpy(&cmd, nla_data(iter), sizeof(NanHeader));
PRINTM(MCMND, "ATTR_NAN_FAKE %d, %d\n",
cmd.nan_header_ext.nan_header.MsgId,
cmd.nan_header_ext.nan_header.transactionId);
break;
case ATTR_NAN_IND:
indicate_array[indicate_number_total] =
nla_get_u32(iter);
indicate_number_total++;
PRINTM(MCMND, "ATTR_NAN_IND %d, %d\n",
indicate_array[indicate_number_total - 1],
indicate_number_total);
break;
default:
PRINTM(MERROR, "Unknown type: %d\n", type);
ret = -EINVAL;
goto done;
}
}
cmd.indicate_enable = 0;
kfifo_in(&nan->cmd_fifo, &cmd, sizeof(nan_cmd));
PRINTM(MCMND, "fifo len: %u\n", kfifo_len(&nan->cmd_fifo));
for (i = 0; i < MAX_INDICATE_ARRAY_SIZE && i < indicate_number_total;
i++) {
cmd.indicate_enable = 1;
cmd.indicate_type = indicate_array[i];
kfifo_in(&nan->cmd_fifo, &cmd, sizeof(nan_cmd));
PRINTM(MCMND, "fifo len: %u\n", kfifo_len(&nan->cmd_fifo));
DBG_HEXDUMP(MCMD_D, "nan_cmd", (t_u8 *)&cmd, sizeof(nan_cmd));
}
ret = cfg80211_vendor_cmd_reply(skb);
schedule_delayed_work(&nan->response_work, 0);
if (ret)
PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to enable nan
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_enable_req(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to disable nan
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_disable_req(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to nan publish req
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_publish_req(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to cancel nan publish
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_publish_cancel(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to nan subscribe req
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_subscribe_req(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to cancel nan subscribe
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_subscribe_cancel(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to trasmit followup
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_trasmit_followup(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to req nan stats
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_stats_req(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to config nan
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_config_req(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to nan tca req
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_tca_req(struct wiphy *wiphy, struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to beacon sdf payload
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_beacon_sdf_payload(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to get nan version
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_get_version(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to get nan capability
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_get_capability(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to data if create
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_data_if_create(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to data if delete
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_data_if_delete(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to data req initor
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_data_req_initor(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to data indication resp
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_data_indi_resp(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief vendor command to date end
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_nan_data_end(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
int ret = 0;
ENTER();
ret = nan_handler(wiphy, wdev, data, len);
LEAVE();
return ret;
}
/**
* @brief init wifi hal
*
* @param priv A pointer to moal_private struct
*
* @return 0: success 1: fail
*/
int
woal_init_wifi_hal(moal_private *priv)
{
ENTER();
woal_init_ring_buffer(priv);
priv->pkt_fate_monitor_enable = MFALSE;
woal_init_packet_filter(priv);
woal_init_nan(priv);
LEAVE();
return 0;
}
/**
* @brief deinit wifi hal
*
* @param priv A pointer to moal_private struct
*
* @return 0: success 1: fail
*/
int
woal_deinit_wifi_hal(moal_private *priv)
{
ENTER();
woal_deinit_ring_buffer(priv);
priv->pkt_fate_monitor_enable = MFALSE;
woal_deinit_packet_filter(priv);
woal_deinit_nan(priv);
LEAVE();
return 0;
}
/**
* @brief vendor command to get correlated HW and System time
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_get_correlated_time(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_get_correlated_time *info = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
int err = -1;
int length = 0;
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
PRINTM(MERROR, "Could not allocate mlan ioctl request!\n");
return -ENOMEM;
}
/* Fill request buffer */
misc = (mlan_ds_misc_cfg *)req->pbuf;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_GET;
misc->sub_command = MLAN_OID_MISC_GET_CORRELATED_TIME;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "get correleted time fail\n");
goto done;
}
length = sizeof(mlan_ds_get_correlated_time);
info = (mlan_ds_get_correlated_time *) (&misc->param.host_clock);
DBG_HEXDUMP(MCMD_D, "get_correlated_time", (t_u8 *)info, length);
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed\n");
goto done;
}
/* Push the data to the skb */
nla_put_nohdr(skb, length, info);
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err)) {
PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err);
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
return err;
}
/**
* @brief vendor command to get link layer statistic
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_link_statistic_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct sk_buff *skb = NULL;
mlan_ioctl_req *req = NULL;
mlan_ds_get_info *info = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
wifi_radio_stat *radio_stat = NULL;
wifi_iface_stat *iface_stat = NULL;
t_u32 num_radio = 0, iface_stat_len = 0, radio_stat_len = 0;
int err = -1, length = 0, i;
char *ioctl_link_stats_buf = NULL;
mlan_ds_get_stats stats;
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) + BUF_MAXLEN);
if (req == NULL) {
PRINTM(MERROR, "Could not allocate mlan ioctl request!\n");
return -ENOMEM;
}
/* Fill request buffer */
info = (mlan_ds_get_info *)req->pbuf;
info->sub_command = MLAN_OID_LINK_STATS;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "get link layer statistic fail\n");
goto done;
}
/* Get Log from the firmware */
memset(&stats, 0, sizeof(mlan_ds_get_stats));
if (MLAN_STATUS_SUCCESS !=
woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) {
PRINTM(MERROR, "Error getting stats information\n");
goto done;
}
ioctl_link_stats_buf = info->param.link_statistic;
num_radio = *((t_u32 *)info->param.link_statistic);
radio_stat =
(wifi_radio_stat *) (info->param.link_statistic +
sizeof(num_radio));
radio_stat_len = num_radio * sizeof(wifi_radio_stat);
iface_stat =
(wifi_iface_stat *) (info->param.link_statistic +
sizeof(num_radio) + radio_stat_len);
iface_stat_len = sizeof(wifi_iface_stat);
/* Fill some fileds */
iface_stat->beacon_rx = stats.bcn_rcv_cnt;
/* could get peer info with seperate cmd */
for (i = 0; i < iface_stat->num_peers; i++) {
/* no need copy, just increase iface_stat length */
iface_stat_len +=
sizeof(wifi_peer_info) +
sizeof(wifi_rate_stat) *
iface_stat->peer_info[i].num_rate;
}
/* Here the length doesn't contain addition 2 attribute header length */
length = NLA_HDRLEN * 2 + sizeof(num_radio) + radio_stat_len +
iface_stat_len;
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed\n");
goto done;
}
if (nla_put_u32(skb, ATTR_LL_STATS_NUM_RADIO, num_radio) ||
nla_put(skb, ATTR_LL_STATS_RADIO, radio_stat_len, radio_stat) ||
nla_put(skb, ATTR_LL_STATS_IFACE, iface_stat_len, iface_stat)) {
PRINTM(MERROR, "nla_put failed!\n");
kfree(skb);
goto done;
}
PRINTM(MCMD_D, "num_radio=%d\n", num_radio);
DBG_HEXDUMP(MCMD_D, "radio_stat", (t_u8 *)radio_stat, radio_stat_len);
DBG_HEXDUMP(MCMD_D, "iface_stat", (t_u8 *)iface_stat, iface_stat_len);
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err)) {
PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err);
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
return err;
}
/**
* @brief API to trigger the link layer statistics collection.
* Unless his API is invoked - link layer statistics will not be collected.
* Radio statistics (once started) do not stop or get reset unless
* wifi_clear_link_stats is invoked, Interface statistics (once started)
* reset and start afresh after each connection.
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_link_statistic_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev);
struct nlattr *tb[ATTR_LL_STATS_MAX + 1];
wifi_link_layer_params ll_params;
mlan_ioctl_req *req = NULL;
mlan_ds_get_info *info = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
int err = 0;
err = nla_parse(tb, ATTR_LL_STATS_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (err)
return err;
if (!tb[ATTR_LL_STATS_MPDU_SIZE_THRESHOLD] ||
!tb[ATTR_LL_STATS_AGGRSSIVE_STATS_GATHERING])
return -EINVAL;
ll_params.mpdu_size_threshold =
nla_get_u32(tb[ATTR_LL_STATS_MPDU_SIZE_THRESHOLD]);
ll_params.aggressive_statistics_gathering =
nla_get_u32(tb[ATTR_LL_STATS_AGGRSSIVE_STATS_GATHERING]);
PRINTM(MEVENT,
"link layer params mpdu_size_threshold = 0x%x, aggressive_statistics_gathering = 0x%x\n",
ll_params.mpdu_size_threshold,
ll_params.aggressive_statistics_gathering);
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) +
sizeof(mlan_ds_get_info));
if (req == NULL) {
PRINTM(MERROR, "Could not allocate mlan ioctl request!\n");
return -ENOMEM;
}
/* Fill request buffer */
info = (mlan_ds_get_info *)req->pbuf;
info->sub_command = MLAN_OID_LINK_STATS;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_SET;
/* Configure parameter to firmware */
memcpy(info->param.link_statistic, &ll_params, sizeof(ll_params));
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status == MLAN_STATUS_SUCCESS) {
PRINTM(MMSG, "enable link layer statistic successfully\n");
}
if (status != MLAN_STATUS_PENDING)
kfree(req);
return 0;
}
/**
* @brief clear function should download command to fimrware,
* so that firmware could cleanup per peer statistic number
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_link_statistic_clr(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev);
struct nlattr *tb[ATTR_LL_STATS_MAX + 1];
mlan_ioctl_req *req = NULL;
mlan_ds_get_info *info = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
u32 stats_clear_req_mask = 0x0, stats_clear_rsp_mask = 0x0;
u8 stop_req = 0x0, stop_rsp = 0x0;
struct sk_buff *skb = NULL;
int err = 0, length = 0;
err = nla_parse(tb, ATTR_LL_STATS_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (err)
return err;
if (!tb[ATTR_LL_STATS_CLEAR_REQ_MASK])
return -EINVAL;
else
stats_clear_req_mask =
nla_get_u32(tb[ATTR_LL_STATS_CLEAR_REQ_MASK]);
if (!tb[ATTR_LL_STATS_STOP_REQ])
return -EINVAL;
else
stop_req = nla_get_u8(tb[ATTR_LL_STATS_STOP_REQ]);
PRINTM(MEVENT,
"link layer clear stats_clear_req_mask = 0x%x, stop_req = 0x%x\n",
stats_clear_req_mask, stop_req);
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) +
sizeof(mlan_ds_get_info));
if (req == NULL) {
PRINTM(MERROR, "Could not allocate mlan ioctl request!\n");
return -ENOMEM;
}
/* Fill request buffer */
info = (mlan_ds_get_info *)req->pbuf;
info->sub_command = MLAN_OID_LINK_STATS;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_CLEAR;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status == MLAN_STATUS_SUCCESS) {
PRINTM(MMSG, "enable link layer statistic successfully\n");
}
length = NLA_HDRLEN + sizeof(stats_clear_rsp_mask) + sizeof(stop_rsp);
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed\n");
err = -EINVAL;
goto exit;
}
/* clear api to reset statistics, stats_clear_rsp_mask identifies what stats have been cleared
* stop_req = 1 will imply whether to stop the statistics collection.
* stop_rsp = 1 will imply that stop_req was honored and statistics collection was stopped.
*/
stats_clear_rsp_mask = WIFI_STATS_RADIO | WIFI_STATS_IFACE;
stop_rsp = 1;
if (nla_put_u32(skb, ATTR_LL_STATS_CLEAR_RSP_MASK, stats_clear_rsp_mask)
|| nla_put_u8(skb, ATTR_LL_STATS_STOP_RSP, stop_rsp)) {
PRINTM(MERROR, "nla_put failed!\n");
kfree(skb);
err = -EINVAL;
goto exit;
}
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err)) {
PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err);
goto exit;;
}
exit:
if (status != MLAN_STATUS_PENDING)
kfree(req);
return err;
}
#ifdef STA_CFG80211
#define RSSI_MONOTOR_START 1
#define RSSI_MONOTOR_STOP 0
/**
* @brief vendor command to control rssi monitor
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rssi_monitor(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
struct nlattr *tb[ATTR_RSSI_MONITOR_MAX + 1];
moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev);
u32 rssi_monitor_control = 0x0;
s8 rssi_min = 0, rssi_max = 0;
int err = 0;
t_u8 *pos = NULL;
struct sk_buff *skb = NULL;
int ret = 0;
ENTER();
if (!priv->media_connected) {
ret = -EINVAL;
goto done;
}
ret = nla_parse(tb, ATTR_RSSI_MONITOR_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (ret)
goto done;
if (!tb[ATTR_RSSI_MONITOR_CONTROL]) {
ret = -EINVAL;
goto done;
}
rssi_monitor_control = nla_get_u32(tb[ATTR_RSSI_MONITOR_CONTROL]);
if (rssi_monitor_control == RSSI_MONOTOR_START) {
if ((!tb[ATTR_RSSI_MONITOR_MIN_RSSI]) ||
(!tb[ATTR_RSSI_MONITOR_MAX_RSSI])) {
ret = -EINVAL;
goto done;
}
rssi_min = nla_get_s8(tb[ATTR_RSSI_MONITOR_MIN_RSSI]);
rssi_max = nla_get_s8(tb[ATTR_RSSI_MONITOR_MAX_RSSI]);
PRINTM(MEVENT,
"start rssi monitor rssi_min = %d, rssi_max= %d\n",
rssi_min, rssi_max);
/* set rssi low/high threshold */
priv->cqm_rssi_high_thold = rssi_max;
priv->cqm_rssi_thold = rssi_min;
priv->cqm_rssi_hyst = 4;
woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT);
} else if (rssi_monitor_control == RSSI_MONOTOR_STOP) {
/* stop rssi monitor */
PRINTM(MEVENT, "stop rssi monitor\n");
/* set both rssi_thold/hyst to 0, will trigger subscribe event clear */
priv->cqm_rssi_high_thold = 0;
priv->cqm_rssi_thold = 0;
priv->cqm_rssi_hyst = 0;
woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT);
} else {
PRINTM(MERROR, "invalid rssi_monitor control request\n");
ret = -EINVAL;
goto done;
}
/* Alloc the SKB for cmd reply */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed\n");
ret = -EINVAL;
goto done;
}
pos = skb_put(skb, len);
memcpy(pos, data, len);
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err)) {
PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err);
ret = err;
}
done:
LEAVE();
return ret;
}
/**
* @brief send rssi event to kernel
*
* @param priv A pointer to moal_private
* @param rssi current rssi value
*
* @return N/A
*/
void
woal_cfg80211_rssi_monitor_event(moal_private *priv, t_s16 rssi)
{
struct sk_buff *skb = NULL;
t_s8 rssi_value = 0;
ENTER();
skb = dev_alloc_skb(NLA_HDRLEN * 2 + ETH_ALEN + sizeof(t_s8));
if (!skb)
goto done;
/* convert t_s16 to t_s8 */
rssi_value = -abs(rssi);
if (nla_put
(skb, ATTR_RSSI_MONITOR_CUR_BSSID, ETH_ALEN, priv->conn_bssid) ||
nla_put_s8(skb, ATTR_RSSI_MONITOR_CUR_RSSI, rssi_value)) {
PRINTM(MERROR, "nla_put failed!\n");
kfree(skb);
goto done;
}
woal_cfg80211_vendor_event(priv, event_rssi_monitor, (t_u8 *)skb->data,
skb->len);
kfree(skb);
done:
LEAVE();
}
#endif
/**
* @brief vendor command to key_mgmt_set_key
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success fail otherwise
*/
static int
woal_cfg80211_subcmd_set_roaming_offload_key(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
moal_private *priv;
struct net_device *dev;
struct sk_buff *skb = NULL;
t_u8 *pos = (t_u8 *)data;
int ret = MLAN_STATUS_SUCCESS;
ENTER();
if (data)
DBG_HEXDUMP(MCMD_D, "Vendor pmk", (t_u8 *)data, data_len);
if (!wdev || !wdev->netdev) {
LEAVE();
return -EFAULT;
}
dev = wdev->netdev;
priv = (moal_private *)woal_get_netdev_priv(dev);
if (!priv) {
LEAVE();
return -EFAULT;
}
if (data_len > MLAN_MAX_KEY_LENGTH) {
memcpy(&priv->pmk.pmk_r0, pos, MLAN_MAX_KEY_LENGTH);
pos += MLAN_MAX_KEY_LENGTH;
memcpy(&priv->pmk.pmk_r0_name, pos,
MIN(MLAN_MAX_PMKR0_NAME_LENGTH,
data_len - MLAN_MAX_KEY_LENGTH));
} else {
memcpy(&priv->pmk.pmk, data,
MIN(MLAN_MAX_KEY_LENGTH, data_len));
}
priv->pmk_saved = MTRUE;
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, data_len);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
LEAVE();
return -EFAULT;
}
pos = skb_put(skb, data_len);
memcpy(pos, data, data_len);
ret = cfg80211_vendor_cmd_reply(skb);
LEAVE();
return ret;
}
/**
* @brief vendor command to supplicant to update AP info
*
* @param priv A pointer to moal_private
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
int
woal_roam_ap_info(IN moal_private *priv, IN t_u8 *data, IN int len)
{
struct wiphy *wiphy = priv->wdev->wiphy;
struct sk_buff *skb = NULL;
int ret = MLAN_STATUS_SUCCESS;
key_info *pkey = NULL;
apinfo *pinfo = NULL;
apinfo *req_tlv = NULL;
MrvlIEtypesHeader_t *tlv = NULL;
t_u16 tlv_type = 0, tlv_len = 0, tlv_buf_left = 0;
int event_id = 0;
t_u8 authorized = 1;
ENTER();
event_id = woal_get_event_id(event_fw_roam_success);
if (event_max == event_id) {
PRINTM(MERROR, "Not find this event %d \n", event_id);
ret = 1;
LEAVE();
return ret;
}
/**allocate skb*/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
skb = cfg80211_vendor_event_alloc(wiphy, NULL, len + 50,
#else
skb = cfg80211_vendor_event_alloc(wiphy, len + 50,
#endif
event_id, GFP_ATOMIC);
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor event\n");
ret = 1;
LEAVE();
return ret;
}
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
MLAN_MAC_ADDR_LENGTH, (t_u8 *)data);
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
sizeof(authorized), &authorized);
tlv = (MrvlIEtypesHeader_t *)(data + MLAN_MAC_ADDR_LENGTH);
tlv_buf_left = len - MLAN_MAC_ADDR_LENGTH;
while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) {
tlv_type = woal_le16_to_cpu(tlv->type);
tlv_len = woal_le16_to_cpu(tlv->len);
if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) {
PRINTM(MERROR,
"Error processing firmware roam success TLVs, bytes left < TLV length\n");
break;
}
switch (tlv_type) {
case TLV_TYPE_APINFO:
pinfo = (apinfo *) tlv;
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
pinfo->header.len, pinfo->rsp_ie);
break;
case TLV_TYPE_ASSOC_REQ_IE:
req_tlv = (apinfo *) tlv;
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
req_tlv->header.len, req_tlv->rsp_ie);
break;
case TLV_TYPE_KEYINFO:
pkey = (key_info *) tlv;
nla_put(skb,
MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
MLAN_REPLAY_CTR_LEN, pkey->key.replay_ctr);
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
MLAN_KCK_LEN, pkey->key.kck);
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
MLAN_KEK_LEN, pkey->key.kek);
break;
default:
break;
}
tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t);
tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len +
sizeof(MrvlIEtypesHeader_t));
}
/**send event*/
cfg80211_vendor_event(skb, GFP_ATOMIC);
LEAVE();
return ret;
}
/**
* @brief vendor command to get fw roaming capability
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success fail otherwise
*/
static int
woal_cfg80211_subcmd_get_roaming_capability(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
int ret = MLAN_STATUS_SUCCESS;
wifi_roaming_capabilities capa;
struct sk_buff *skb = NULL;
int err = 0;
ENTER();
if (!wdev || !wdev->netdev) {
LEAVE();
return -EFAULT;
}
capa.max_blacklist_size = MAX_AP_LIST;
capa.max_whitelist_size = MAX_SSID_NUM;
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
sizeof
(wifi_roaming_capabilities) +
50);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed\n");
goto done;
}
/* Push the data to the skb */
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CAPA,
sizeof(wifi_roaming_capabilities), (t_u8 *)&capa);
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err)) {
PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err);
}
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to enable/disable fw roaming
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success fail otherwise
*/
static int
woal_cfg80211_subcmd_fw_roaming_enable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
moal_private *priv;
struct net_device *dev;
int ret = MLAN_STATUS_SUCCESS;
struct sk_buff *skb = NULL;
const struct nlattr *iter;
int type, rem, err;
t_u32 fw_roaming_enable = 0;
#ifdef STA_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
t_u8 enable = 0;
#endif
#endif
ENTER();
if (!wdev || !wdev->netdev) {
LEAVE();
return -EFAULT;
}
dev = wdev->netdev;
priv = (moal_private *)woal_get_netdev_priv(dev);
if (!priv || !priv->phandle) {
LEAVE();
return -EFAULT;
}
nla_for_each_attr(iter, data, len, rem) {
type = nla_type(iter);
switch (type) {
case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL:
fw_roaming_enable = nla_get_u32(iter);
break;
default:
PRINTM(MERROR, "Unknown type: %d\n", type);
ret = -EINVAL;
goto done;
}
}
PRINTM(MMSG, "FW roaming set enable=%d from wifi hal.\n",
fw_roaming_enable);
ret = woal_enable_fw_roaming(priv, fw_roaming_enable);
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(t_u32) + 50);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed\n");
goto done;
}
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL, sizeof(t_u32),
&fw_roaming_enable);
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err)) {
PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err);
}
#ifdef STA_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (!fw_roaming_enable) {
woal_cfg80211_vendor_event(priv, event_set_key_mgmt_offload,
&enable, sizeof(enable));
}
#endif
#endif
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to config blacklist and whitelist for fw roaming
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success fail otherwise
*/
static int
woal_cfg80211_subcmd_fw_roaming_config(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
moal_private *priv;
struct net_device *dev;
int ret = MLAN_STATUS_SUCCESS;
const struct nlattr *iter;
int type, rem;
woal_roam_offload_cfg *roam_offload_cfg = NULL;
wifi_bssid_params blacklist;
wifi_ssid_params whitelist;
ENTER();
if (!wdev || !wdev->netdev) {
LEAVE();
return -EFAULT;
}
dev = wdev->netdev;
priv = (moal_private *)woal_get_netdev_priv(dev);
if (!priv || !priv->phandle) {
LEAVE();
return -EFAULT;
}
memset((char *)&blacklist, 0, sizeof(wifi_bssid_params));
memset((char *)&whitelist, 0, sizeof(wifi_ssid_params));
nla_for_each_attr(iter, data, len, rem) {
type = nla_type(iter);
switch (type) {
case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_BSSID:
memcpy((t_u8 *)&blacklist, nla_data(iter),
sizeof(wifi_bssid_params));
break;
case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_SSID:
memcpy((t_u8 *)&whitelist, nla_data(iter),
sizeof(wifi_ssid_params));
break;
default:
PRINTM(MERROR, "Unknown type: %d\n", type);
ret = -EINVAL;
goto done;
}
}
if (roamoffload_in_hs) {
/*save blacklist and whitelist in driver */
priv->phandle->fw_roam_params.black_list.ap_num =
blacklist.num_bssid;
memcpy((t_u8 *)priv->phandle->fw_roam_params.black_list.ap_mac,
(t_u8 *)blacklist.mac_addr,
sizeof(wifi_bssid_params) - sizeof(blacklist.num_bssid));
priv->phandle->fw_roam_params.ssid_list.ssid_num =
whitelist.num_ssid;
memcpy((t_u8 *)priv->phandle->fw_roam_params.ssid_list.ssids,
(t_u8 *)whitelist.whitelist_ssid,
sizeof(wifi_ssid_params) - sizeof(whitelist.num_ssid));
} else {
roam_offload_cfg =
(woal_roam_offload_cfg *)
kmalloc(sizeof(woal_roam_offload_cfg), GFP_KERNEL);
if (!roam_offload_cfg) {
PRINTM(MERROR, "kmalloc failed!\n");
ret = -ENOMEM;
goto done;
}
/*download parameters directly to fw */
memset((char *)roam_offload_cfg, 0,
sizeof(woal_roam_offload_cfg));
roam_offload_cfg->black_list.ap_num = blacklist.num_bssid;
memcpy((t_u8 *)&roam_offload_cfg->black_list.ap_mac,
(t_u8 *)blacklist.mac_addr,
sizeof(wifi_bssid_params) - sizeof(blacklist.num_bssid));
roam_offload_cfg->ssid_list.ssid_num = whitelist.num_ssid;
memcpy((t_u8 *)&roam_offload_cfg->ssid_list.ssids,
(t_u8 *)whitelist.whitelist_ssid,
sizeof(wifi_ssid_params) - sizeof(whitelist.num_ssid));
woal_config_fw_roaming(priv, ROAM_OFFLOAD_PARAM_CFG,
roam_offload_cfg);
}
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to enable/disable 11k
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param data_len data length
*
* @return 0: success <0: fail
*/
static int
woal_cfg80211_subcmd_11k_cfg(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = NULL;
moal_private *priv = NULL;
mlan_ioctl_req *req = NULL;
mlan_ds_11k_cfg *pcfg_11k = NULL;
struct nlattr *tb_vendor[ATTR_ND_OFFLOAD_MAX + 1];
int ret = 0;
int status = MLAN_STATUS_SUCCESS;
ENTER();
if (!wdev || !wdev->netdev) {
LEAVE();
return -EFAULT;
}
dev = wdev->netdev;
priv = (moal_private *)woal_get_netdev_priv(dev);
nla_parse(tb_vendor, ATTR_ND_OFFLOAD_MAX,
(struct nlattr *)data, data_len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (!tb_vendor[ATTR_ND_OFFLOAD_CONTROL]) {
PRINTM(MINFO, "%s: ATTR_ND_OFFLOAD not found\n", __FUNCTION__);
ret = -EFAULT;
goto done;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11k_cfg));
if (req == NULL) {
PRINTM(MERROR, "Could not allocate mlan ioctl request!\n");
ret = -EFAULT;
goto done;
}
/* Fill request buffer */
pcfg_11k = (mlan_ds_11k_cfg *) req->pbuf;
pcfg_11k->sub_command = MLAN_OID_11K_CFG_ENABLE;
req->req_id = MLAN_IOCTL_11K_CFG;
req->action = MLAN_ACT_SET;
if (nla_get_u32(tb_vendor[ATTR_ND_OFFLOAD_CONTROL]))
pcfg_11k->param.enable_11k = MTRUE;
else
pcfg_11k->param.enable_11k = MFALSE;
PRINTM(MCMND, "11k enable = %d\n", pcfg_11k->param.enable_11k);
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief vendor command to set scan mac oui
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param data_len data length
*
* @return 0: success <0: fail
*/
static int
woal_cfg80211_subcmd_set_scan_mac_oui(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = NULL;
moal_private *priv = NULL;
struct nlattr *tb_vendor[ATTR_WIFI_MAX + 1];
t_u8 mac_oui[3];
int ret = MLAN_STATUS_SUCCESS;
ENTER();
if (!wdev || !wdev->netdev) {
LEAVE();
return -EFAULT;
}
dev = wdev->netdev;
priv = (moal_private *)woal_get_netdev_priv(dev);
nla_parse(tb_vendor, ATTR_WIFI_MAX,
(struct nlattr *)data, data_len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (!tb_vendor[ATTR_SCAN_MAC_OUI_SET]) {
PRINTM(MINFO, "%s: ATTR_SCAN_MAC_OUI_SET not found\n",
__FUNCTION__);
ret = -EFAULT;
goto done;
}
memcpy(mac_oui, nla_data(tb_vendor[ATTR_SCAN_MAC_OUI_SET]), 3);
memcpy(priv->random_mac, priv->current_addr, 6);
memcpy(priv->random_mac, mac_oui, 3);
PRINTM(MCMND, "random_mac is " FULL_MACSTR "\n",
FULL_MAC2STR(priv->random_mac));
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to set enable/disable dfs offload
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success 1: fail
*/
static int
woal_cfg80211_subcmd_set_dfs_offload(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct sk_buff *skb = NULL;
int ret = 1;
ENTER();
/** Allocate skb for cmd reply*/
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(dfs_offload));
if (!skb) {
PRINTM(MERROR, "allocate memory fail for vendor cmd\n");
ret = 1;
LEAVE();
return ret;
}
nla_put(skb, MRVL_WLAN_VENDOR_ATTR_DFS, sizeof(t_u32), &dfs_offload);
ret = cfg80211_vendor_cmd_reply(skb);
LEAVE();
return ret;
}
/**
* @brief vendor command to get rtt capability
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rtt_get_capa(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
moal_handle *handle = priv->phandle;
struct sk_buff *skb = NULL;
int err = 0;
ENTER();
PRINTM(MCMND, "CfgVendor: cfg80211_subcmd_rtt_get_capa\n");
DBG_HEXDUMP(MCMD_D, "input data", (t_u8 *)data, len);
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
nla_total_size(sizeof
(handle->
rtt_capa)) +
VENDOR_REPLY_OVERHEAD);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed in %s\n", __FUNCTION__);
goto done;
}
/* Put the attribute to the skb */
nla_put(skb, ATTR_RTT_CAPA, sizeof(handle->rtt_capa),
&(handle->rtt_capa));
PRINTM(MCMND, "NL80211_CMD_VENDOR=0x%x\n", NL80211_CMD_VENDOR);
PRINTM(MCMND, "NL80211_ATTR_WIPHY=0x%x\n", NL80211_ATTR_WIPHY);
PRINTM(MCMND, "NL80211_ATTR_VENDOR_ID=0x%x\n", NL80211_ATTR_VENDOR_ID);
PRINTM(MCMND, "NL80211_ATTR_VENDOR_SUBCMD=0x%x\n",
NL80211_ATTR_VENDOR_SUBCMD);
PRINTM(MCMND, "NL80211_ATTR_VENDOR_DATA=0x%x\n",
NL80211_ATTR_VENDOR_DATA);
PRINTM(MCMND, "NL80211_ATTR_VENDOR_EVENTS=0x%x\n",
NL80211_ATTR_VENDOR_EVENTS);
DBG_HEXDUMP(MCMD_D, "output data skb->head", (t_u8 *)skb->head, 50);
DBG_HEXDUMP(MCMD_D, "output data skb->data", (t_u8 *)skb->data, 50);
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err))
PRINTM(MERROR, "Vendor Command reply failed err:%d \n", err);
done:
LEAVE();
return err;
}
static void
woal_dump_rtt_params(wifi_rtt_config_params_t * rtt_params)
{
int i = 0;
PRINTM(MMSG, "===== Start DUMP RTT Params =====\n");
PRINTM(MMSG, "rtt_config_num=%d\n\n", rtt_params->rtt_config_num);
for (i = 0; i < rtt_params->rtt_config_num; i++) {
PRINTM(MMSG, "----------[%d]----------\n", i);
PRINTM(MMSG, "rtt_config[%d].addr=" MACSTR "\n", i,
MAC2STR(rtt_params->rtt_config[i].addr));
PRINTM(MMSG, "rtt_config[%d].type=%d\n", i,
rtt_params->rtt_config[i].type);
PRINTM(MMSG, "rtt_config[%d].peer=%d\n", i,
rtt_params->rtt_config[i].peer);
PRINTM(MMSG, "rtt_config[%d].channel=[%d %d %d %d]\n", i,
rtt_params->rtt_config[i].channel.width,
rtt_params->rtt_config[i].channel.center_freq,
rtt_params->rtt_config[i].channel.center_freq0,
rtt_params->rtt_config[i].channel.center_freq1);
PRINTM(MMSG, "rtt_config[%d].burst_period=%d\n", i,
rtt_params->rtt_config[i].burst_period);
PRINTM(MMSG, "rtt_config[%d].num_burst=%d\n", i,
rtt_params->rtt_config[i].num_burst);
PRINTM(MMSG, "rtt_config[%d].num_frames_per_burst=%d\n", i,
rtt_params->rtt_config[i].num_frames_per_burst);
PRINTM(MMSG, "rtt_config[%d].num_retries_per_rtt_frame=%d\n", i,
rtt_params->rtt_config[i].num_retries_per_rtt_frame);
PRINTM(MMSG, "rtt_config[%d].num_retries_per_ftmr=%d\n", i,
rtt_params->rtt_config[i].num_retries_per_ftmr);
PRINTM(MMSG, "rtt_config[%d].LCI_request=%d\n", i,
rtt_params->rtt_config[i].LCI_request);
PRINTM(MMSG, "rtt_config[%d].LCR_request=%d\n", i,
rtt_params->rtt_config[i].LCR_request);
PRINTM(MMSG, "rtt_config[%d].burst_duration=%d\n", i,
rtt_params->rtt_config[i].burst_duration);
PRINTM(MMSG, "rtt_config[%d].preamble=%d\n", i,
rtt_params->rtt_config[i].preamble);
PRINTM(MMSG, "rtt_config[%d].bw=%d\n", i,
rtt_params->rtt_config[i].bw);
PRINTM(MMSG, "\n");
}
}
/**
* @brief vendor command to request rtt range
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rtt_range_request(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
moal_handle *handle = priv->phandle;
struct nlattr *tb[ATTR_RTT_MAX];
t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0 };
t_u8 rtt_config_num = 0;
wifi_rtt_config *rtt_config = NULL;
t_u8 i = 0, j = 0;
wifi_rtt_config_params_t rtt_params;
mlan_status ret = MLAN_STATUS_SUCCESS;
int err = 0;
ENTER();
PRINTM(MCMND, "Enter woal_cfg80211_subcmd_rtt_range_request()\n");
err = nla_parse(tb, ATTR_RTT_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (err) {
err = -EFAULT;
PRINTM(MERROR, "%s: nla_parse fail\n", __FUNCTION__);
goto done;
}
if (!tb[ATTR_RTT_TARGET_NUM] || !tb[ATTR_RTT_TARGET_CONFIG]) {
PRINTM(MERROR,
"%s: null attr: tb[ATTR_RTT_TARGET_NUM]=%p tb[ATTR_RTT_TARGET_CONFIG]=%p\n",
__FUNCTION__, tb[ATTR_RTT_TARGET_NUM],
tb[ATTR_RTT_TARGET_CONFIG]);
err = -EINVAL;
goto done;
}
rtt_config_num = nla_get_u8(tb[ATTR_RTT_TARGET_NUM]);
if ((rtt_config_num == 0) ||
((handle->rtt_params.rtt_config_num + rtt_config_num) >
MAX_RTT_CONFIG_NUM)) {
PRINTM(MERROR, "%s: invalid num=%d num in handle=%d MAX=%d\n",
__FUNCTION__, rtt_config_num,
handle->rtt_params.rtt_config_num, MAX_RTT_CONFIG_NUM);
err = -EINVAL;
goto done;
}
if (nla_len(tb[ATTR_RTT_TARGET_CONFIG]) !=
sizeof(rtt_params.rtt_config[0]) * rtt_config_num) {
PRINTM(MERROR, "%s: invalid %d(total) != %d(num) * %lu(each)\n",
__FUNCTION__, nla_len(tb[ATTR_RTT_TARGET_CONFIG]),
rtt_config_num, sizeof(rtt_params.rtt_config[0]));
err = -EINVAL;
goto done;
}
rtt_config = (wifi_rtt_config *) nla_data(tb[ATTR_RTT_TARGET_CONFIG]);
memset(&rtt_params, 0, sizeof(rtt_params));
/** Strip the zero mac config */
for (i = 0; i < rtt_config_num; i++) {
if (!memcmp
(rtt_config[i].addr, zero_mac, sizeof(rtt_config[i].addr)))
continue;
else {
memcpy(&rtt_params.
rtt_config[rtt_params.rtt_config_num],
&rtt_config[i],
sizeof(rtt_params.
rtt_config[rtt_params.rtt_config_num]));
rtt_params.rtt_config_num++;
}
}
if (!rtt_params.rtt_config_num) {
PRINTM(MERROR, "%s: no valid mac addr\n", __FUNCTION__);
goto done;
}
woal_dump_rtt_params(&rtt_params);
ret = woal_config_rtt(priv, MOAL_IOCTL_WAIT, &rtt_params);
if (ret != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "%s: woal_config_rtt() failed\n", __FUNCTION__);
err = -EFAULT;
goto done;
}
for (i = 0; i < rtt_params.rtt_config_num; i++) {
for (j = 0; j < handle->rtt_params.rtt_config_num; j++) {
if (!memcmp
(handle->rtt_params.rtt_config[j].addr,
rtt_params.rtt_config[i].addr,
sizeof(handle->rtt_params.rtt_config[j].addr)))
break;
}
memcpy(&(handle->rtt_params.rtt_config[j]),
&(rtt_params.rtt_config[i]),
sizeof(handle->rtt_params.rtt_config[j]));
if (j == handle->rtt_params.rtt_config_num)
handle->rtt_params.rtt_config_num++;
}
woal_dump_rtt_params(&(handle->rtt_params));
done:
LEAVE();
return err;
}
/**
* @brief vendor command to cancel rtt range
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rtt_range_cancel(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
moal_handle *handle = priv->phandle;
t_u8 rtt_config_num = handle->rtt_params.rtt_config_num;
struct nlattr *tb[ATTR_RTT_MAX];
t_u32 target_num = 0;
t_u8 addr[MAX_RTT_CONFIG_NUM][MLAN_MAC_ADDR_LENGTH];
int i = 0, j = 0;
mlan_status ret = MLAN_STATUS_SUCCESS;
int err = 0;
ENTER();
PRINTM(MCMND, "Enter woal_cfg80211_subcmd_rtt_range_cancel()\n");
err = nla_parse(tb, ATTR_RTT_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (err) {
PRINTM(MERROR, "%s: nla_parse fail\n", __FUNCTION__);
goto done;
}
if (!tb[ATTR_RTT_TARGET_NUM] || !tb[ATTR_RTT_TARGET_ADDR]) {
PRINTM(MERROR,
"%s: null attr: tb[ATTR_RTT_TARGET_NUM]=%p tb[ATTR_RTT_TARGET_ADDR]=%p\n",
__FUNCTION__, tb[ATTR_RTT_TARGET_NUM],
tb[ATTR_RTT_TARGET_ADDR]);
err = -EINVAL;
goto done;
}
target_num = nla_get_u8(tb[ATTR_RTT_TARGET_NUM]);
if ((target_num <= 0 || target_num > MAX_RTT_CONFIG_NUM) ||
(nla_len(tb[ATTR_RTT_TARGET_ADDR]) !=
sizeof(t_u8) * MLAN_MAC_ADDR_LENGTH * target_num)) {
PRINTM(MERROR, "%s: Check if %din[1-%d] or %d*%lu=%d\n",
__FUNCTION__, target_num, MAX_RTT_CONFIG_NUM, target_num,
sizeof(t_u8) * MLAN_MAC_ADDR_LENGTH,
nla_len(tb[ATTR_RTT_TARGET_ADDR]));
err = -EINVAL;
goto done;
}
woal_dump_rtt_params(&(handle->rtt_params));
memcpy(addr, nla_data(tb[ATTR_RTT_TARGET_ADDR]),
nla_len(tb[ATTR_RTT_TARGET_ADDR]));
for (i = 0; i < target_num; i++)
PRINTM(MMSG, "cancel[%d].addr=" MACSTR "\n", i,
MAC2STR(addr[i]));
for (i = 0; i < target_num; i++) {
for (j = 0; j < handle->rtt_params.rtt_config_num; j++) {
if (!memcmp
(addr[i], handle->rtt_params.rtt_config[j].addr,
sizeof(addr[0]))) {
memset(&(handle->rtt_params.rtt_config[j]),
0x00,
sizeof(handle->rtt_params.
rtt_config[0]));
if ((j + 1) < handle->rtt_params.rtt_config_num) {
memmove(&
(handle->rtt_params.
rtt_config[j]),
&(handle->rtt_params.
rtt_config[j + 1]),
sizeof(handle->rtt_params.
rtt_config[0]) *
(handle->rtt_params.
rtt_config_num - (j + 1)));
memset(&
(handle->rtt_params.
rtt_config[handle->rtt_params.
rtt_config_num - 1]),
0x00,
sizeof(handle->rtt_params.
rtt_config[0]));
}
handle->rtt_params.rtt_config_num--;
continue;
}
}
}
if (handle->rtt_params.rtt_config_num >= rtt_config_num) {
PRINTM(MERROR, "%s: No matched mac addr in rtt_config\n",
__FUNCTION__);
goto done;
}
ret = woal_cancel_rtt(priv, MOAL_IOCTL_WAIT, target_num, addr);
if (ret != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "%s: woal_cancel_rtt() failed\n", __FUNCTION__);
err = -EFAULT;
goto done;
}
woal_dump_rtt_params(&(handle->rtt_params));
done:
LEAVE();
return err;
}
/**
* @brief vendor event to report RTT Results
*
* @param priv A pointer to moal_private
* @param data a pointer to data
* @param len data length
*
* @return mlan_status
*/
mlan_status
woal_cfg80211_event_rtt_result(IN moal_private *priv, IN t_u8 *data, IN int len)
{
//moal_handle *handle = priv->phandle;
mlan_status ret = MLAN_STATUS_SUCCESS;
t_u8 *pos = data;
t_u32 event_left_len = len;
struct sk_buff *skb = NULL;
t_u32 vdr_event_len = 0;
t_u32 complete = 0;
wifi_rtt_result_element *rtt_result_elem = NULL;
t_u32 num_results = 0;
ENTER();
PRINTM(MEVENT, "Enter woal_cfg80211_event_rtt_result()\n");
vdr_event_len = nla_total_size(sizeof(complete)) +
nla_total_size(sizeof(num_results)) +
nla_total_size(len) + NLA_ALIGNTO * num_results
+ VENDOR_REPLY_OVERHEAD;
PRINTM(MEVENT, "vdr_event_len = %d\n", vdr_event_len);
skb = woal_cfg80211_alloc_vendor_event(priv, event_rtt_result,
vdr_event_len);
if (!skb)
goto done;
complete = *pos;
nla_put(skb, ATTR_RTT_RESULT_COMPLETE, sizeof(complete), &complete);
pos++;
event_left_len--;
while (event_left_len > sizeof(wifi_rtt_result_element)) {
rtt_result_elem = (wifi_rtt_result_element *) pos;
nla_put(skb, ATTR_RTT_RESULT_FULL, rtt_result_elem->len,
rtt_result_elem->data);
num_results++;
pos += sizeof(*rtt_result_elem) + rtt_result_elem->len;
event_left_len -=
sizeof(*rtt_result_elem) + rtt_result_elem->len;
}
nla_put(skb, ATTR_RTT_RESULT_NUM, sizeof(num_results), &num_results);
DBG_HEXDUMP(MEVT_D, "output data skb->data", (t_u8 *)skb->data,
skb->len);
/**send event*/
cfg80211_vendor_event(skb, GFP_KERNEL);
done:
LEAVE();
return ret;
}
/**
* @brief vendor command to get rtt responder info
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rtt_get_responder_info(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
mlan_rtt_responder rtt_rsp_cfg;
struct sk_buff *skb = NULL;
wifi_rtt_responder rtt_rsp;
mlan_status ret = MLAN_STATUS_SUCCESS;
int err = 0;
ENTER();
PRINTM(MCMND, "Enter woal_cfg80211_subcmd_rtt_get_responder_info()\n");
memset(&rtt_rsp_cfg, 0x00, sizeof(rtt_rsp_cfg));
rtt_rsp_cfg.action = RTT_GET_RESPONDER_INFO;
ret = woal_rtt_responder_cfg(priv, MOAL_IOCTL_WAIT, &rtt_rsp_cfg);
if (ret != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "%s: woal_rtt_responder_cfg() failed\n",
__FUNCTION__);
err = -EFAULT;
goto done;
}
PRINTM(MCMD_D,
"mlan_rtt_responder from FW: channel=%d bandcfg=%d %d %d %d preamble=%d\n",
rtt_rsp_cfg.u.info.channel, rtt_rsp_cfg.u.info.bandcfg.chanBand,
rtt_rsp_cfg.u.info.bandcfg.chanWidth,
rtt_rsp_cfg.u.info.bandcfg.chan2Offset,
rtt_rsp_cfg.u.info.bandcfg.scanMode,
rtt_rsp_cfg.u.info.preamble);
memset(&rtt_rsp, 0x00, sizeof(rtt_rsp));
woal_bandcfg_to_channel_info(priv, &(rtt_rsp_cfg.u.info.bandcfg),
rtt_rsp_cfg.u.info.channel,
&(rtt_rsp.channel));
rtt_rsp.preamble = rtt_rsp_cfg.u.info.preamble;
PRINTM(MCMD_D, "wifi_rtt_responder report to HAL:\n");
PRINTM(MCMD_D,
"channel: width=%d center_freq=%d center_freq0=%d center_freq1=%d\n",
rtt_rsp.channel.width, rtt_rsp.channel.center_freq,
rtt_rsp.channel.center_freq0, rtt_rsp.channel.center_freq1);
PRINTM(MCMD_D, "preamble=%d\n", rtt_rsp.preamble);
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
nla_total_size(sizeof
(rtt_rsp)) +
VENDOR_REPLY_OVERHEAD);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed in %s\n", __FUNCTION__);
goto done;
}
/* Put the attribute to the skb */
nla_put(skb, ATTR_RTT_CHANNEL_INFO, sizeof(rtt_rsp.channel),
&(rtt_rsp.channel));
nla_put(skb, ATTR_RTT_PREAMBLE, sizeof(rtt_rsp.preamble),
&(rtt_rsp.preamble));
DBG_HEXDUMP(MCMD_D, "output data skb->data", (t_u8 *)skb->data,
skb->len);
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err))
PRINTM(MERROR, "Vendor Command reply failed err:%d \n", err);
done:
LEAVE();
return err;
}
/**
* @brief vendor command to enable rtt responder
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rtt_enable_responder(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct nlattr *tb[ATTR_RTT_MAX];
wifi_channel_info *ch_info = NULL;
t_u32 max_dur_sec = 0;
mlan_rtt_responder rtt_rsp_cfg;
wifi_rtt_responder rtt_rsp;
struct sk_buff *skb = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
int err = 0;
ENTER();
PRINTM(MCMND, "Enter woal_cfg80211_subcmd_rtt_enable_responder()\n");
err = nla_parse(tb, ATTR_RTT_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (err) {
err = -EFAULT;
PRINTM(MERROR, "%s: nla_parse fail\n", __FUNCTION__);
goto done;
}
if (!tb[ATTR_RTT_CHANNEL_INFO] || !tb[ATTR_RTT_MAX_DUR_SEC]) {
PRINTM(MERROR,
"%s: null attr: tb[ATTR_RTT_TARGET_NUM]=%p tb[ATTR_RTT_TARGET_CONFIG]=%p\n",
__FUNCTION__, tb[ATTR_RTT_CHANNEL_INFO],
tb[ATTR_RTT_MAX_DUR_SEC]);
err = -EINVAL;
goto done;
}
ch_info = (wifi_channel_info *) nla_data(tb[ATTR_RTT_CHANNEL_INFO]);
max_dur_sec = nla_get_u32(tb[ATTR_RTT_MAX_DUR_SEC]);
PRINTM(MCMD_D, "HAL input: \n");
PRINTM(MCMD_D,
"wifi_channel_info: width=%d center_freq=%d center_freq0=%d center_freq1=%d\n",
ch_info->width, ch_info->center_freq, ch_info->center_freq0,
ch_info->center_freq1);
PRINTM(MCMD_D, "max_dur_sec=%d\n", max_dur_sec);
memset(&rtt_rsp_cfg, 0x00, sizeof(rtt_rsp_cfg));
rtt_rsp_cfg.action = RTT_SET_RESPONDER_ENABLE;
rtt_rsp_cfg.u.encfg.channel =
ieee80211_frequency_to_channel(ch_info->center_freq);
woal_channel_info_to_bandcfg(priv, ch_info,
&(rtt_rsp_cfg.u.encfg.bandcfg));
rtt_rsp_cfg.u.encfg.max_dur_sec = max_dur_sec;
PRINTM(MCMD_D, "HAL input to rtt_responder_encfg: \n");
PRINTM(MCMD_D,
"channel=%d bandcfg=[chanBand=%d chanWidth=%d chan2Offset=%d scanMode=%d]\n",
rtt_rsp_cfg.u.encfg.channel,
rtt_rsp_cfg.u.encfg.bandcfg.chanBand,
rtt_rsp_cfg.u.encfg.bandcfg.chanWidth,
rtt_rsp_cfg.u.encfg.bandcfg.chan2Offset,
rtt_rsp_cfg.u.encfg.bandcfg.scanMode);
PRINTM(MCMD_D, "max_dur_sec=%d\n", rtt_rsp_cfg.u.encfg.max_dur_sec);
ret = woal_rtt_responder_cfg(priv, MOAL_IOCTL_WAIT, &rtt_rsp_cfg);
if (ret != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "%s: woal_rtt_responder_cfg() failed\n",
__FUNCTION__);
err = -EFAULT;
goto done;
}
memset(&rtt_rsp, 0x00, sizeof(rtt_rsp));
woal_bandcfg_to_channel_info(priv, &(rtt_rsp_cfg.u.info.bandcfg),
rtt_rsp_cfg.u.info.channel,
&(rtt_rsp.channel));
rtt_rsp.preamble = rtt_rsp_cfg.u.info.preamble;
PRINTM(MCMD_D, "wifi_rtt_responder report to HAL:\n");
PRINTM(MCMD_D,
"channel: width=%d center_freq=%d center_freq0=%d center_freq1=%d\n",
rtt_rsp.channel.width, rtt_rsp.channel.center_freq,
rtt_rsp.channel.center_freq0, rtt_rsp.channel.center_freq1);
PRINTM(MCMD_D, "preamble=%d\n", rtt_rsp.preamble);
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
nla_total_size(sizeof
(rtt_rsp)) +
VENDOR_REPLY_OVERHEAD);
if (unlikely(!skb)) {
PRINTM(MERROR, "skb alloc failed in %s\n", __FUNCTION__);
goto done;
}
/* Put the attribute to the skb */
nla_put(skb, ATTR_RTT_CHANNEL_INFO, sizeof(rtt_rsp.channel),
&(rtt_rsp.channel));
nla_put(skb, ATTR_RTT_PREAMBLE, sizeof(rtt_rsp.preamble),
&(rtt_rsp.preamble));
DBG_HEXDUMP(MCMD_D, "output data skb->data", (t_u8 *)skb->data,
skb->len);
err = cfg80211_vendor_cmd_reply(skb);
if (unlikely(err))
PRINTM(MERROR, "Vendor Command reply failed err:%d \n", err);
done:
LEAVE();
return err;
}
/**
* @brief vendor command to disable rtt responder
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rtt_disable_responder(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
mlan_rtt_responder rtt_rsp_cfg;
mlan_status ret = MLAN_STATUS_SUCCESS;
int err = 0;
ENTER();
PRINTM(MCMND, "Enter woal_cfg80211_subcmd_rtt_disable_responder()\n");
memset(&rtt_rsp_cfg, 0x00, sizeof(rtt_rsp_cfg));
rtt_rsp_cfg.action = RTT_SET_RESPONDER_DISABLE;
ret = woal_rtt_responder_cfg(priv, MOAL_IOCTL_WAIT, &rtt_rsp_cfg);
if (ret != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "%s: woal_rtt_responder_cfg() failed\n",
__FUNCTION__);
err = -EFAULT;
goto done;
}
done:
LEAVE();
return err;
}
/**
* @brief vendor command to set rtt lci
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rtt_set_lci(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct nlattr *tb[ATTR_RTT_MAX];
mlan_rtt_responder rtt_rsp_cfg;
wifi_lci_information *lci_info;
mlan_status ret = MLAN_STATUS_SUCCESS;
int err = 0;
ENTER();
PRINTM(MCMND, "Enter woal_cfg80211_subcmd_rtt_set_lci()\n");
err = nla_parse(tb, ATTR_RTT_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (err) {
err = -EFAULT;
PRINTM(MERROR, "%s: nla_parse fail\n", __FUNCTION__);
goto done;
}
if (!tb[ATTR_RTT_LCI_INFO]) {
PRINTM(MERROR, "%s: null attr: tb[ATTR_RTT_LCI_INFO]=%p\n",
__FUNCTION__, tb[ATTR_RTT_LCI_INFO]);
err = -EINVAL;
goto done;
}
lci_info = (wifi_lci_information *) nla_data(tb[ATTR_RTT_LCI_INFO]);
PRINTM(MCMD_D, "HAL input: \n");
PRINTM(MCMD_D,
"wifi_lci_information: latitude=%lu longitude=%lu altitude=%d latitude_unc=%d longitude_unc=%d altitude_unc=%d \n",
lci_info->latitude, lci_info->longitude, lci_info->altitude,
lci_info->latitude_unc, lci_info->longitude_unc,
lci_info->altitude_unc);
PRINTM(MCMD_D,
"wifi_lci_information: motion_pattern=%d floor=%d height_above_floor=%d height_unc=%d\n",
lci_info->motion_pattern, lci_info->floor,
lci_info->height_above_floor, lci_info->height_unc);
memset(&rtt_rsp_cfg, 0x00, sizeof(rtt_rsp_cfg));
rtt_rsp_cfg.action = RTT_SET_RESPONDER_LCI;
memcpy(&(rtt_rsp_cfg.u.lci), lci_info, sizeof(rtt_rsp_cfg.u.lci));
ret = woal_rtt_responder_cfg(priv, MOAL_IOCTL_WAIT, &rtt_rsp_cfg);
if (ret != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "%s: woal_rtt_responder_cfg() failed\n",
__FUNCTION__);
err = -EFAULT;
goto done;
}
done:
LEAVE();
return err;
}
/**
* @brief vendor command to set rtt lcr
*
* @param wiphy A pointer to wiphy struct
* @param wdev A pointer to wireless_dev struct
* @param data a pointer to data
* @param len data length
*
* @return 0: success -1: fail
*/
static int
woal_cfg80211_subcmd_rtt_set_lcr(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data,
int len)
{
struct net_device *dev = wdev->netdev;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct nlattr *tb[ATTR_RTT_MAX];
mlan_rtt_responder rtt_rsp_cfg;
wifi_lcr_information *lcr_info;
mlan_status ret = MLAN_STATUS_SUCCESS;
int err = 0;
ENTER();
PRINTM(MCMND, "Enter woal_cfg80211_subcmd_rtt_set_lcr()\n");
err = nla_parse(tb, ATTR_RTT_MAX, data, len, NULL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, NULL
#endif
);
if (err) {
err = -EFAULT;
PRINTM(MERROR, "%s: nla_parse fail\n", __FUNCTION__);
goto done;
}
if (!tb[ATTR_RTT_LCR_INFO]) {
PRINTM(MERROR, "%s: null attr: tb[ATTR_RTT_LCR_INFO]=%p\n",
__FUNCTION__, tb[ATTR_RTT_LCR_INFO]);
err = -EINVAL;
goto done;
}
lcr_info = (wifi_lcr_information *) nla_data(tb[ATTR_RTT_LCR_INFO]);
PRINTM(MCMD_D, "HAL input: \n");
PRINTM(MCMD_D, "wifi_lcr_information: country_code='%c' '%c'\n",
lcr_info->country_code[0], lcr_info->country_code[1]);
PRINTM(MCMD_D, "wifi_lci_information: length=%d civic_info=%s\n",
lcr_info->length, lcr_info->civic_info);
memset(&rtt_rsp_cfg, 0x00, sizeof(rtt_rsp_cfg));
rtt_rsp_cfg.action = RTT_SET_RESPONDER_LCR;
memcpy(&(rtt_rsp_cfg.u.lcr), lcr_info, sizeof(rtt_rsp_cfg.u.lcr));
ret = woal_rtt_responder_cfg(priv, MOAL_IOCTL_WAIT, &rtt_rsp_cfg);
if (ret != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "%s: woal_rtt_responder_cfg() failed\n",
__FUNCTION__);
err = -EFAULT;
goto done;
}
done:
LEAVE();
return err;
}
const struct wiphy_vendor_command vendor_commands[] = {
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = sub_cmd_set_drvdbg,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_set_drvdbg,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_valid_channels,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_get_valid_channels,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_set_scan_mac_oui,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_set_scan_mac_oui,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_link_statistic_set,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_link_statistic_set,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_link_statistic_get,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_link_statistic_get,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_link_statistic_clr,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_link_statistic_clr,
},
#ifdef STA_CFG80211
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = sub_cmd_rssi_monitor,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rssi_monitor,
},
#endif
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_set_roaming_offload_key,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_set_roaming_offload_key,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_roaming_capability,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_get_roaming_capability,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_fw_roaming_enable,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_fw_roaming_enable,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_fw_roaming_config,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_fw_roaming_config,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_dfs_capability,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_set_dfs_offload,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_correlated_time,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_get_correlated_time,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = SUBCMD_RTT_GET_CAPA,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rtt_get_capa,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
SUBCMD_RTT_RANGE_REQUEST,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rtt_range_request,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
SUBCMD_RTT_RANGE_CANCEL,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rtt_range_cancel,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
SUBCMD_RTT_GET_RESPONDER_INFO,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rtt_get_responder_info,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
SUBCMD_RTT_ENABLE_RESPONDER,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rtt_enable_responder,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
SUBCMD_RTT_DISABLE_RESPONDER,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rtt_disable_responder,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = SUBCMD_RTT_SET_LCI,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rtt_set_lci,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = SUBCMD_RTT_SET_LCR,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_rtt_set_lcr,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = sub_cmd_nd_offload},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_11k_cfg,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_drv_version,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_get_drv_version,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_fw_version,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_get_fw_version,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_wifi_supp_feature_set,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_get_supp_feature_set,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_set_country_code,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_set_country_code,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_wifi_logger_supp_feature_set,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = woal_cfg80211_subcmd_get_wifi_logger_supp_feature_set,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_ring_buff_status,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_get_ring_buff_status,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = sub_cmd_start_logging,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_start_logging,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_ring_buff_data,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_get_ring_data,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_start_packet_fate_monitor,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_start_packet_fate_monitor,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_fw_mem_dump,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_get_fw_dump,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_drv_mem_dump,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_get_drv_dump,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_set_packet_filter,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_set_packet_filter,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
sub_cmd_get_packet_filter_capability,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_get_packet_filter_capability,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = subcmd_nan_enable_req,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_enable_req,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_disable_req,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_disable_req,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_publish_req,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_publish_req,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_publish_cancel,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_publish_cancel,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_subscribe_req,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_subscribe_req,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_subscribe_cancel,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_subscribe_cancel,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_trasmit_followup,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_trasmit_followup,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = subcmd_nan_stats_req,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_stats_req,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = subcmd_nan_config_req,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_config_req,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = subcmd_nan_tca_req,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_tca_req,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_beacon_sdf_payload,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_beacon_sdf_payload,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_get_version,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_get_version,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = subcmd_nan_get_capa,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_get_capability,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_data_if_create,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_data_if_create,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_data_if_delete,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_data_if_delete,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_data_req_initor,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_data_req_initor,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd =
subcmd_nan_data_indi_resp,},
.flags =
WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_data_indi_resp,
},
{
.info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = subcmd_nan_data_end,},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = woal_cfg80211_subcmd_nan_data_end,
},
};
/**
* @brief register vendor commands and events
*
* @param wiphy A pointer to wiphy struct
*
* @return
*/
void
woal_register_cfg80211_vendor_command(struct wiphy *wiphy)
{
ENTER();
wiphy->vendor_commands = vendor_commands;
wiphy->n_vendor_commands = ARRAY_SIZE(vendor_commands);
wiphy->vendor_events = vendor_events;
wiphy->n_vendor_events = ARRAY_SIZE(vendor_events);
LEAVE();
}
#endif