lineage_kernel_xcoverpro/drivers/net/wireless/scsc/nl80211_vendor.c

6503 lines
202 KiB
C
Executable File

/*****************************************************************************
*
* Copyright (c) 2014 - 2020 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/version.h>
#include <net/cfg80211.h>
#include <net/ip.h>
#include <linux/etherdevice.h>
#include "dev.h"
#include "cfg80211_ops.h"
#include "debug.h"
#include "mgt.h"
#include "mlme.h"
#include "netif.h"
#include "unifiio.h"
#include "mib.h"
#include "nl80211_vendor.h"
#include <linux/uaccess.h>
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
#include "scsc_wifilogger.h"
#include "scsc_wifilogger_rings.h"
#include "scsc_wifilogger_types.h"
#endif
#define SLSI_WIFI_TAG_VENDOR_SPECIFIC 0
#define SLSI_WIFI_TAG_BSSID 1
#define SLSI_WIFI_TAG_SSID 3
#define SLSI_WIFI_TAG_STATUS 4
#define SLSI_WIFI_TAG_IE 12
#define SLSI_WIFI_TAG_REASON_CODE 14
#define SLSI_WIFI_TAG_RSSI 21
#define SLSI_WIFI_TAG_CHANNEL 22
#define SLSI_WIFI_TAG_EAPOL_MESSAGE_TYPE 29
#define SLSI_GSCAN_INVALID_RSSI 0x7FFF
#define SLSI_EPNO_AUTH_FIELD_WEP_OPEN 1
#define SLSI_EPNO_AUTH_FIELD_WPA_PSK 2
#define SLSI_EPNO_AUTH_FIELD_WPA_EAP 4
#define WIFI_EVENT_FW_BTM_FRAME_REQUEST 56 /* Request for a BTM frame is received */
#define WIFI_EVENT_FW_BTM_FRAME_RESPONSE 57 /* A BTM frame is transmitted. */
#define WIFI_EVENT_FW_NR_FRAME_REQUEST 58
#define WIFI_EVENT_FW_RM_FRAME_RESPONSE 59
#define WIFI_EVENT_FW_CONNECTION_ATTEMPT_ABORTED 60
#define WIFI_EVENT_ROAM_AUTH_TIMEOUT 61
#define WIFI_EVENT_ROAM_SCAN_RESULT 62
#define WIFI_EVENT_ROAM_RSSI_THRESHOLD 63
#define WIFI_EVENT_FW_BEACON_REPORT_REQUEST 64
#define WIFI_EVENT_FW_FTM_RANGE_REQUEST 65
#define WIFI_EVENT_FW_NAN_ROLE_TYPE 66
#define WIFI_EVENT_FW_FRAME_TRANSMIT_FAILURE 67
#define WIFI_EVENT_FW_NR_FRAME_RESPONSE 68
#define WIFI_EVENT_NAN_AVAILABILITY_UPDATE 69 /* NDL Schedule setup or updated. */
#define WIFI_EVENT_NAN_ULW_UPDATE 70 /* ULW added or removed. */
#define WIFI_EVENT_NAN_TRAFFIC_UPDATE 71 /* NAN Traffic Information. */
#define WIFI_EVENT_ASSOCIATING_DEAUTH_RECEIVED 72
#define SLSI_WIFI_TAG_VD_RETRY_COUNT 0xf00f
#define SLSI_WIFI_TAG_VD_EAPOL_KEY_TYPE 0xF008
#define SLSI_WIFI_TAG_VD_SCAN_TYPE 0xf012
#define SLSI_WIFI_TAG_VD_ROAMING_REASON 0xf019
#define SLSI_WIFI_TAG_VD_CHANNEL_UTILISATION 0xf01a
#define SLSI_WIFI_TAG_VD_BTM_REQUEST_MODE 0xf01b
#define SLSI_WIFI_TAG_VD_BTM_RESPONSE_STATUS 0xf01c
#define SLSI_WIFI_TAG_VD_SCORE 0xf01d
#define SLSI_WIFI_TAG_VD_RSSI_THRESHOLD 0xf01e
#define SLSI_WIFI_TAG_VD_CANDIDATE_LIST_COUNT 0xf021
#define SLSI_WIFI_TAG_VD_OPERATING_CLASS 0xf022
#define SLSI_WIFI_TAG_VD_MEASUREMENT_MODE 0xf023
#define SLSI_WIFI_TAG_VD_MEASUREMENT_DURATION 0xf024
#define SLSI_WIFI_TAG_VD_MIN_AP_COUNT 0xf025
#define SLSI_WIFI_TAG_VD_CLUSTER_ID 0xf026
#define SLSI_WIFI_TAG_VD_NAN_ROLE 0xf027
#define SLSI_WIFI_TAG_VD_NAN_AMR 0xf028
#define SLSI_WIFI_TAG_VD_NAN_HOP_COUNT 0xf029
#define SLSI_WIFI_TAG_VD_NAN_NMI 0xf02a
#define SLSI_WIFI_TAG_VD_MESSAGE_TYPE 0xf02b
#define SLSI_WIFI_TAG_VD_ESTIMATED_TP 0xf02c
#define SLSI_WIFI_TAG_VD_EXPIRED_TIMER_VALUE 0xf02d
#define SLSI_WIFI_TAG_VD_MASTER_TSF 0xf030
#define SLSI_WIFI_TAG_VD_SCHEDULE_TYPE 0xf031
#define SLSI_WIFI_TAG_VD_START_OFFSET 0xf032
#define SLSI_WIFI_TAG_VD_SLOT_DURATION 0xf033
#define SLSI_WIFI_TAG_VD_SLOT_PERIOD 0xf034
#define SLSI_WIFI_TAG_VD_BITMAP 0xf035
#define SLSI_WIFI_TAG_VD_CHANNEL_INFO 0xf036
#define SLSI_WIFI_TAG_VD_ULW_REASON 0xf037
#define SLSI_WIFI_TAG_VD_ULW_INDEX 0xf038
#define SLSI_WIFI_TAG_VD_ULW_START_TIME 0xf039
#define SLSI_WIFI_TAG_VD_ULW_PERIOD 0xf03a
#define SLSI_WIFI_TAG_VD_ULW_DURATION 0xf03b
#define SLSI_WIFI_TAG_VD_ULW_COUNT 0xf03c
#define SLSI_WIFI_TAG_VD_NAN_RX_TOTAL 0xf03d
#define SLSI_WIFI_TAG_VD_NAN_TX_TOTAL 0xf03e
#define SLSI_WIFI_TAG_VD_NAN_RX_AVERAGE 0xf03f
#define SLSI_WIFI_TAG_VD_NAN_TX_AVERAGE 0xf040
#define SLSI_WIFI_TAG_VD_PARAMETER_SET 0xff00
#define SLSI_WIFI_EAPOL_KEY_TYPE_GTK 0x0000
#define SLSI_WIFI_EAPOL_KEY_TYPE_PTK 0x0001
#define SLSI_WIFI_ROAMING_SEARCH_REASON_RESERVED 0
#define SLSI_WIFI_ROAMING_SEARCH_REASON_LOW_RSSI 1
#define SLSI_WIFI_ROAMING_SEARCH_REASON_LINK_LOSS 2
#define SLSI_WIFI_ROAMING_SEARCH_REASON_BTM_REQ 3
#define SLSI_WIFI_ROAMING_SEARCH_REASON_CU_TRIGGER 4
#define SLSI_WIFI_ROAMING_SEARCH_REASON_EMERGENCY 5
#define SLSI_WIFI_ROAMING_SEARCH_REASON_IDLE 6
#define SLSI_WIFI_ROAMING_SEARCH_REASON_SCAN_TIMER1_EXPIRY 7
#define SLSI_WIFI_ROAMING_SEARCH_REASON_SCAN_TIMER2_EXPIRY 8
#define SLSI_WIFI_ROAMING_SEARCH_REASON_INACTIVE_TIMER_EXPIRY 9
#define MAX_SSID_LEN 100
#define SLSI_MAX_NUM_RING 10
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
static int mem_dump_buffer_size;
static char *mem_dump_buffer;
#endif
#ifdef CONFIG_SCSC_WLAN_DEBUG
char *slsi_print_event_name(int event_id)
{
switch (event_id) {
case SLSI_NL80211_SCAN_RESULTS_AVAILABLE_EVENT:
return "SCAN_RESULTS_AVAILABLE_EVENT";
case SLSI_NL80211_FULL_SCAN_RESULT_EVENT:
return "FULL_SCAN_RESULT_EVENT";
case SLSI_NL80211_SCAN_EVENT:
return "BUCKET_SCAN_DONE_EVENT";
#ifdef CONFIG_SCSC_WLAN_KEY_MGMT_OFFLOAD
case SLSI_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
return "KEY_MGMT_ROAM_AUTH";
#endif
case SLSI_NL80211_VENDOR_HANGED_EVENT:
return "SLSI_NL80211_VENDOR_HANGED_EVENT";
case SLSI_NL80211_EPNO_EVENT:
return "SLSI_NL80211_EPNO_EVENT";
case SLSI_NL80211_HOTSPOT_MATCH:
return "SLSI_NL80211_HOTSPOT_MATCH";
case SLSI_NL80211_RSSI_REPORT_EVENT:
return "SLSI_NL80211_RSSI_REPORT_EVENT";
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
case SLSI_NL80211_LOGGER_RING_EVENT:
return "SLSI_NL80211_LOGGER_RING_EVENT";
case SLSI_NL80211_LOGGER_FW_DUMP_EVENT:
return "SLSI_NL80211_LOGGER_FW_DUMP_EVENT";
#endif
case SLSI_NL80211_NAN_RESPONSE_EVENT:
return "SLSI_NL80211_NAN_RESPONSE_EVENT";
case SLSI_NL80211_NAN_PUBLISH_TERMINATED_EVENT:
return "SLSI_NL80211_NAN_PUBLISH_TERMINATED_EVENT";
case SLSI_NL80211_NAN_MATCH_EVENT:
return "SLSI_NL80211_NAN_MATCH_EVENT";
case SLSI_NL80211_NAN_MATCH_EXPIRED_EVENT:
return "SLSI_NL80211_NAN_MATCH_EXPIRED_EVENT";
case SLSI_NL80211_NAN_SUBSCRIBE_TERMINATED_EVENT:
return "SLSI_NL80211_NAN_SUBSCRIBE_TERMINATED_EVENT";
case SLSI_NL80211_NAN_FOLLOWUP_EVENT:
return "SLSI_NL80211_NAN_FOLLOWUP_EVENT";
case SLSI_NL80211_NAN_DISCOVERY_ENGINE_EVENT:
return "SLSI_NL80211_NAN_DISCOVERY_ENGINE_EVENT";
case SLSI_NL80211_RTT_RESULT_EVENT:
return "SLSI_NL80211_RTT_RESULT_EVENT";
case SLSI_NL80211_RTT_COMPLETE_EVENT:
return "SLSI_NL80211_RTT_COMPLETE_EVENT";
case SLSI_NL80211_VENDOR_ACS_EVENT:
return "SLSI_NL80211_VENDOR_ACS_EVENT";
case SLSI_NL80211_NAN_TRANSMIT_FOLLOWUP_STATUS:
return "SLSI_NL80211_NAN_TRANSMIT_FOLLOWUP_STATUS";
case SLSI_NAN_EVENT_NDP_REQ:
return "SLSI_NAN_EVENT_NDP_REQ";
case SLSI_NAN_EVENT_NDP_CFM:
return "SLSI_NAN_EVENT_NDP_CFM";
case SLSI_NAN_EVENT_NDP_END:
return "SLSI_NAN_EVENT_NDP_END";
default:
return "UNKNOWN_EVENT";
}
}
#endif
int slsi_vendor_event(struct slsi_dev *sdev, int event_id, const void *data, int len)
{
struct sk_buff *skb;
int ret = 0;
#ifdef CONFIG_SCSC_WLAN_DEBUG
SLSI_DBG1_NODEV(SLSI_MLME, "Event: %s(%d), data = %p, len = %d\n",
slsi_print_event_name(event_id), event_id, data, len);
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
skb = cfg80211_vendor_event_alloc(sdev->wiphy, NULL, len, event_id, GFP_KERNEL);
#else
skb = cfg80211_vendor_event_alloc(sdev->wiphy, len, event_id, GFP_KERNEL);
#endif
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for vendor event: %d\n", event_id);
return -ENOMEM;
}
ret = nla_put_nohdr(skb, len, data);
if (ret) {
SLSI_ERR(sdev, "Error in nla_put*:%x\n", ret);
kfree_skb(skb);
return -EINVAL;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
return 0;
}
static int slsi_vendor_cmd_reply(struct wiphy *wiphy, const void *data, int len)
{
struct sk_buff *skb;
int ret = 0;
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb\n");
return -ENOMEM;
}
ret = nla_put_nohdr(skb, len, data);
if (ret) {
SLSI_ERR_NODEV("Error in nla_put_*:%x\n", ret);
kfree_skb(skb);
return -EINVAL;
}
return cfg80211_vendor_cmd_reply(skb);
}
static struct net_device *slsi_gscan_get_netdev(struct slsi_dev *sdev)
{
return slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
}
static struct netdev_vif *slsi_gscan_get_vif(struct slsi_dev *sdev)
{
struct net_device *dev;
dev = slsi_gscan_get_netdev(sdev);
if (!dev) {
SLSI_WARN_NODEV("Dev is NULL\n");
return NULL;
}
return netdev_priv(dev);
}
int slsi_number_digits(int num)
{
int dig = 0;
while (num) {
dig++;
num = num / 10;
}
return dig;
}
char *slsi_print_channel_list(int channel_list[], int channel_count)
{
int i, slen = 0;
char *string = kmalloc((channel_count * 4) + 1, GFP_KERNEL); /* channel max characters length(3)+space(1) = 4 */
int max_size = (channel_count * 4) + 1;
if (!string) {
SLSI_ERR_NODEV("Failed to allocate channel string\n");
return "-1";
}
for (i = 0; i < channel_count && slen < max_size; i++)
slen += snprintf(&string[slen], max_size - slen, "%d ", channel_list[i]);
return string;
}
#ifdef CONFIG_SCSC_WLAN_DEBUG
static void slsi_gscan_add_dump_params(struct slsi_nl_gscan_param *nl_gscan_param)
{
int i;
int j;
SLSI_DBG2_NODEV(SLSI_GSCAN, "Parameters for SLSI_NL80211_VENDOR_SUBCMD_ADD_GSCAN sub-command:\n");
SLSI_DBG2_NODEV(SLSI_GSCAN, "base_period: %d max_ap_per_scan: %d report_threshold_percent: %d report_threshold_num_scans = %d num_buckets: %d\n",
nl_gscan_param->base_period, nl_gscan_param->max_ap_per_scan,
nl_gscan_param->report_threshold_percent, nl_gscan_param->report_threshold_num_scans,
nl_gscan_param->num_buckets);
for (i = 0; i < nl_gscan_param->num_buckets; i++) {
SLSI_DBG2_NODEV(SLSI_GSCAN, "Bucket: %d\n", i);
SLSI_DBG2_NODEV(SLSI_GSCAN, "\tbucket_index: %d band: %d period: %d report_events: %d num_channels: %d\n",
nl_gscan_param->nl_bucket[i].bucket_index, nl_gscan_param->nl_bucket[i].band,
nl_gscan_param->nl_bucket[i].period, nl_gscan_param->nl_bucket[i].report_events,
nl_gscan_param->nl_bucket[i].num_channels);
for (j = 0; j < nl_gscan_param->nl_bucket[i].num_channels; j++)
SLSI_DBG2_NODEV(SLSI_GSCAN, "\tchannel_list[%d]: %d\n",
j, nl_gscan_param->nl_bucket[i].channels[j].channel);
}
}
void slsi_gscan_scan_res_dump(struct slsi_gscan_result *scan_data)
{
struct slsi_nl_scan_result_param *nl_scan_res = &scan_data->nl_scan_res;
SLSI_DBG3_NODEV(SLSI_GSCAN, "TS:%llu SSID:%s BSSID:%pM Chan:%d RSSI:%d Bcn_Int:%d Capab:%#x IE_Len:%d\n",
nl_scan_res->ts, nl_scan_res->ssid, nl_scan_res->bssid, nl_scan_res->channel,
nl_scan_res->rssi, nl_scan_res->beacon_period, nl_scan_res->capability, nl_scan_res->ie_length);
SLSI_DBG_HEX_NODEV(SLSI_GSCAN, &nl_scan_res->ie_data[0], nl_scan_res->ie_length, "IE_Data:\n");
if (scan_data->anqp_length) {
SLSI_DBG3_NODEV(SLSI_GSCAN, "ANQP_LENGTH:%d\n", scan_data->anqp_length);
SLSI_DBG_HEX_NODEV(SLSI_GSCAN, nl_scan_res->ie_data + nl_scan_res->ie_length, scan_data->anqp_length, "ANQP_info:\n");
}
}
#endif
static int slsi_gscan_get_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_nl_gscan_capabilities nl_cap;
int ret = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_GET_GSCAN_CAPABILITIES\n");
if (!slsi_dev_gscan_supported())
return -ENOTSUPP;
memset(&nl_cap, 0, sizeof(struct slsi_nl_gscan_capabilities));
ret = slsi_mib_get_gscan_cap(sdev, &nl_cap);
if (ret != 0) {
SLSI_ERR(sdev, "Failed to read mib\n");
return ret;
}
nl_cap.max_scan_cache_size = SLSI_GSCAN_MAX_SCAN_CACHE_SIZE;
nl_cap.max_ap_cache_per_scan = SLSI_GSCAN_MAX_AP_CACHE_PER_SCAN;
nl_cap.max_scan_reporting_threshold = SLSI_GSCAN_MAX_SCAN_REPORTING_THRESHOLD;
ret = slsi_vendor_cmd_reply(wiphy, &nl_cap, sizeof(struct slsi_nl_gscan_capabilities));
if (ret)
SLSI_ERR_NODEV("gscan_get_capabilities vendor cmd reply failed (err = %d)\n", ret);
return ret;
}
static u32 slsi_gscan_put_channels(struct ieee80211_supported_band *chan_data, bool no_dfs, bool only_dfs, u32 *buf)
{
u32 chan_count = 0;
u32 chan_flags;
int i;
if (chan_data == NULL) {
SLSI_DBG3_NODEV(SLSI_GSCAN, "Band not supported\n");
return 0;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
chan_flags = (IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_OFDM | IEEE80211_CHAN_RADAR);
#else
chan_flags = (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_OFDM | IEEE80211_CHAN_RADAR);
#endif
for (i = 0; i < chan_data->n_channels; i++) {
if (chan_data->channels[i].flags & IEEE80211_CHAN_DISABLED)
continue;
if (only_dfs) {
if (chan_data->channels[i].flags & chan_flags)
buf[chan_count++] = chan_data->channels[i].center_freq;
continue;
}
if (no_dfs && (chan_data->channels[i].flags & chan_flags))
continue;
buf[chan_count++] = chan_data->channels[i].center_freq;
}
return chan_count;
}
static int slsi_gscan_get_valid_channel(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
int ret = 0, type, band;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
u32 *chan_list;
u32 chan_count = 0, mem_len = 0;
struct sk_buff *reply;
if (len < SLSI_NL_VENDOR_DATA_OVERHEAD || !data)
return -EINVAL;
type = nla_type(data);
if (type == GSCAN_ATTRIBUTE_BAND) {
if (slsi_util_nla_get_u32((struct nlattr *)data, (u32 *)(&band)))
return -EINVAL;
} else
return -EINVAL;
if (band == 0) {
SLSI_WARN(sdev, "NO Bands. return 0 channel\n");
return ret;
}
SLSI_MUTEX_LOCK(sdev->netdev_add_remove_mutex);
SLSI_DBG3(sdev, SLSI_GSCAN, "band %d\n", band);
if (wiphy->bands[NL80211_BAND_2GHZ])
mem_len += wiphy->bands[NL80211_BAND_2GHZ]->n_channels * sizeof(u32);
if (wiphy->bands[NL80211_BAND_5GHZ])
mem_len += wiphy->bands[NL80211_BAND_5GHZ]->n_channels * sizeof(u32);
if (mem_len == 0) {
ret = -ENOTSUPP;
goto exit;
}
chan_list = kmalloc(mem_len, GFP_KERNEL);
if (chan_list == NULL) {
ret = -ENOMEM;
goto exit;
}
mem_len += SLSI_NL_VENDOR_REPLY_OVERHEAD + (SLSI_NL_ATTRIBUTE_U32_LEN * 2);
reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_len);
if (reply == NULL) {
ret = -ENOMEM;
goto exit_with_chan_list;
}
switch (band) {
case WIFI_BAND_BG:
chan_count = slsi_gscan_put_channels(wiphy->bands[NL80211_BAND_2GHZ], false, false, chan_list);
break;
case WIFI_BAND_A:
chan_count = slsi_gscan_put_channels(wiphy->bands[NL80211_BAND_5GHZ], true, false, chan_list);
break;
case WIFI_BAND_A_DFS:
chan_count = slsi_gscan_put_channels(wiphy->bands[NL80211_BAND_5GHZ], false, true, chan_list);
break;
case WIFI_BAND_A_WITH_DFS:
chan_count = slsi_gscan_put_channels(wiphy->bands[NL80211_BAND_5GHZ], false, false, chan_list);
break;
case WIFI_BAND_ABG:
chan_count = slsi_gscan_put_channels(wiphy->bands[NL80211_BAND_2GHZ], true, false, chan_list);
chan_count += slsi_gscan_put_channels(wiphy->bands[NL80211_BAND_5GHZ], true, false, chan_list + chan_count);
break;
case WIFI_BAND_ABG_WITH_DFS:
chan_count = slsi_gscan_put_channels(wiphy->bands[NL80211_BAND_2GHZ], false, false, chan_list);
chan_count += slsi_gscan_put_channels(wiphy->bands[NL80211_BAND_5GHZ], false, false, chan_list + chan_count);
break;
default:
chan_count = 0;
SLSI_WARN(sdev, "Invalid Band %d\n", band);
}
ret |= nla_put_u32(reply, GSCAN_ATTRIBUTE_NUM_CHANNELS, chan_count);
ret |= nla_put(reply, GSCAN_ATTRIBUTE_CHANNEL_LIST, chan_count * sizeof(u32), chan_list);
if (ret) {
SLSI_ERR(sdev, "Error in nla_put*:%x\n", ret);
kfree_skb(reply);
goto exit_with_chan_list;
}
ret = cfg80211_vendor_cmd_reply(reply);
if (ret)
SLSI_ERR(sdev, "FAILED to reply GET_VALID_CHANNELS\n");
exit_with_chan_list:
kfree(chan_list);
exit:
SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex);
return ret;
}
struct slsi_gscan_result *slsi_prepare_scan_result(struct sk_buff *skb, u16 anqp_length, int hs2_id)
{
struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb);
struct slsi_gscan_result *scan_res;
struct timespec ts;
const u8 *ssid_ie;
int mem_reqd;
int ie_len;
u8 *ie;
ie = &mgmt->u.beacon.variable[0];
ie_len = fapi_get_datalen(skb) - (ie - (u8 *)mgmt) - anqp_length;
/* Exclude 1 byte for ie_data[1]. sizeof(u16) to include anqp_length, sizeof(int) for hs_id */
mem_reqd = (sizeof(struct slsi_gscan_result) - 1) + ie_len + anqp_length + sizeof(int) + sizeof(u16);
/* Allocate memory for scan result */
scan_res = kmalloc(mem_reqd, GFP_KERNEL);
if (scan_res == NULL) {
SLSI_ERR_NODEV("Failed to allocate memory for scan result\n");
return NULL;
}
/* Exclude 1 byte for ie_data[1] */
scan_res->scan_res_len = (sizeof(struct slsi_nl_scan_result_param) - 1) + ie_len;
scan_res->anqp_length = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
ts = ktime_to_timespec(ktime_get_boottime());
#else
get_monotonic_boottime(&ts);
#endif
scan_res->nl_scan_res.ts = (u64)TIMESPEC_TO_US(ts);
ssid_ie = cfg80211_find_ie(WLAN_EID_SSID, &mgmt->u.beacon.variable[0], ie_len);
if ((ssid_ie != NULL) && (ssid_ie[1] > 0) && (ssid_ie[1] < IEEE80211_MAX_SSID_LEN)) {
memcpy(scan_res->nl_scan_res.ssid, &ssid_ie[2], ssid_ie[1]);
scan_res->nl_scan_res.ssid[ssid_ie[1]] = '\0';
} else {
scan_res->nl_scan_res.ssid[0] = '\0';
}
SLSI_ETHER_COPY(scan_res->nl_scan_res.bssid, mgmt->bssid);
scan_res->nl_scan_res.channel = fapi_get_u16(skb, u.mlme_scan_ind.channel_frequency) / 2;
scan_res->nl_scan_res.rssi = fapi_get_s16(skb, u.mlme_scan_ind.rssi);
scan_res->nl_scan_res.rtt = SLSI_GSCAN_RTT_UNSPECIFIED;
scan_res->nl_scan_res.rtt_sd = SLSI_GSCAN_RTT_UNSPECIFIED;
scan_res->nl_scan_res.beacon_period = mgmt->u.beacon.beacon_int;
scan_res->nl_scan_res.capability = mgmt->u.beacon.capab_info;
scan_res->nl_scan_res.ie_length = ie_len;
memcpy(scan_res->nl_scan_res.ie_data, ie, ie_len);
memcpy(scan_res->nl_scan_res.ie_data + ie_len, &hs2_id, sizeof(int));
memcpy(scan_res->nl_scan_res.ie_data + ie_len + sizeof(int), &anqp_length, sizeof(u16));
if (anqp_length) {
memcpy(scan_res->nl_scan_res.ie_data + ie_len + sizeof(u16) + sizeof(int), ie + ie_len, anqp_length);
scan_res->anqp_length = anqp_length + sizeof(u16) + sizeof(int);
}
#ifdef CONFIG_SCSC_WLAN_DEBUG
slsi_gscan_scan_res_dump(scan_res);
#endif
return scan_res;
}
static void slsi_gscan_hash_add(struct slsi_dev *sdev, struct slsi_gscan_result *scan_res)
{
u8 key = SLSI_GSCAN_GET_HASH_KEY(scan_res->nl_scan_res.bssid[5]);
struct netdev_vif *ndev_vif;
ndev_vif = slsi_gscan_get_vif(sdev);
if (!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_mutex))
SLSI_WARN_NODEV("ndev_vif->scan_mutex is not locked\n");
scan_res->hnext = sdev->gscan_hash_table[key];
sdev->gscan_hash_table[key] = scan_res;
/* Update the total buffer consumed and number of scan results */
sdev->buffer_consumed += scan_res->scan_res_len;
sdev->num_gscan_results++;
}
static struct slsi_gscan_result *slsi_gscan_hash_get(struct slsi_dev *sdev, u8 *mac)
{
struct slsi_gscan_result *temp;
struct netdev_vif *ndev_vif;
u8 key = SLSI_GSCAN_GET_HASH_KEY(mac[5]);
ndev_vif = slsi_gscan_get_vif(sdev);
if (!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_mutex))
SLSI_WARN_NODEV("ndev_vif->scan_mutex is not locked\n");
temp = sdev->gscan_hash_table[key];
while (temp != NULL) {
if (memcmp(temp->nl_scan_res.bssid, mac, ETH_ALEN) == 0)
return temp;
temp = temp->hnext;
}
return NULL;
}
void slsi_gscan_hash_remove(struct slsi_dev *sdev, u8 *mac)
{
u8 key = SLSI_GSCAN_GET_HASH_KEY(mac[5]);
struct slsi_gscan_result *curr;
struct slsi_gscan_result *prev;
struct netdev_vif *ndev_vif;
struct slsi_gscan_result *scan_res = NULL;
ndev_vif = slsi_gscan_get_vif(sdev);
if (!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_mutex))
SLSI_WARN_NODEV("ndev_vif->scan_mutex is not locked\n");
if (sdev->gscan_hash_table[key] == NULL)
return;
if (memcmp(sdev->gscan_hash_table[key]->nl_scan_res.bssid, mac, ETH_ALEN) == 0) {
scan_res = sdev->gscan_hash_table[key];
sdev->gscan_hash_table[key] = sdev->gscan_hash_table[key]->hnext;
} else {
prev = sdev->gscan_hash_table[key];
curr = prev->hnext;
while (curr != NULL) {
if (memcmp(curr->nl_scan_res.bssid, mac, ETH_ALEN) == 0) {
scan_res = curr;
prev->hnext = curr->hnext;
break;
}
prev = curr;
curr = curr->hnext;
}
}
if (scan_res) {
/* Update the total buffer consumed and number of scan results */
sdev->buffer_consumed -= scan_res->scan_res_len;
sdev->num_gscan_results--;
kfree(scan_res);
}
if (sdev->num_gscan_results < 0)
SLSI_ERR(sdev, "Wrong num_gscan_results: %d\n", sdev->num_gscan_results);
}
int slsi_check_scan_result(struct slsi_dev *sdev, struct slsi_bucket *bucket, struct slsi_gscan_result *new_scan_res)
{
struct slsi_gscan_result *scan_res;
/* Check if the scan result for the same BSS already exists in driver buffer */
scan_res = slsi_gscan_hash_get(sdev, new_scan_res->nl_scan_res.bssid);
if (scan_res == NULL) { /* New scan result */
if ((sdev->buffer_consumed + new_scan_res->scan_res_len) >= SLSI_GSCAN_MAX_SCAN_CACHE_SIZE) {
SLSI_DBG2(sdev, SLSI_GSCAN,
"Scan buffer full, discarding scan result, buffer_consumed = %d, buffer_threshold = %d\n",
sdev->buffer_consumed, sdev->buffer_threshold);
/* Scan buffer is full can't store anymore new results */
return SLSI_DISCARD_SCAN_RESULT;
}
return SLSI_KEEP_SCAN_RESULT;
}
/* Even if scan buffer is full existing results can be replaced with the latest one */
if (scan_res->scan_cycle == bucket->scan_cycle)
/* For the same scan cycle the result will be replaced only if the RSSI is better */
if (new_scan_res->nl_scan_res.rssi < scan_res->nl_scan_res.rssi)
return SLSI_DISCARD_SCAN_RESULT;
/* Remove the existing scan result */
slsi_gscan_hash_remove(sdev, scan_res->nl_scan_res.bssid);
return SLSI_KEEP_SCAN_RESULT;
}
void slsi_gscan_handle_scan_result(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, u16 scan_id, bool scan_done)
{
struct slsi_gscan_result *scan_res = NULL;
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_bucket *bucket;
u16 bucket_index;
int event_type = WIFI_SCAN_FAILED;
u16 anqp_length;
int hs2_network_id;
if (!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_mutex))
SLSI_WARN_NODEV("ndev_vif->scan_mutex is not locked\n");
SLSI_NET_DBG_HEX(dev, SLSI_GSCAN, skb->data, skb->len, "mlme_scan_ind skb->len: %d\n", skb->len);
bucket_index = scan_id - SLSI_GSCAN_SCAN_ID_START;
if (bucket_index >= SLSI_GSCAN_MAX_BUCKETS) {
SLSI_NET_ERR(dev, "Invalid bucket index: %d (scan_id = %#x)\n", bucket_index, scan_id);
goto out;
}
bucket = &sdev->bucket[bucket_index];
if (!bucket->used) {
SLSI_NET_DBG1(dev, SLSI_GSCAN, "Bucket is not active, index: %d (scan_id = %#x)\n", bucket_index, scan_id);
goto out;
}
/* For scan_done indication - no need to store the results */
if (scan_done) {
bucket->scan_cycle++;
bucket->gscan->num_scans++;
SLSI_NET_DBG3(dev, SLSI_GSCAN, "scan done, scan_cycle = %d, num_scans = %d\n",
bucket->scan_cycle, bucket->gscan->num_scans);
if (bucket->report_events & SLSI_REPORT_EVENTS_EACH_SCAN)
event_type = WIFI_SCAN_RESULTS_AVAILABLE;
if (bucket->gscan->num_scans % bucket->gscan->report_threshold_num_scans == 0)
event_type = WIFI_SCAN_THRESHOLD_NUM_SCANS;
if (sdev->buffer_consumed >= sdev->buffer_threshold)
event_type = WIFI_SCAN_THRESHOLD_PERCENT;
if (event_type != WIFI_SCAN_FAILED)
slsi_vendor_event(sdev, SLSI_NL80211_SCAN_EVENT, &event_type, sizeof(event_type));
goto out;
}
anqp_length = fapi_get_u16(skb, u.mlme_scan_ind.anqp_elements_length);
/* TODO new FAPI 3.c has mlme_scan_ind.network_block_id, use that when fapi is updated. */
hs2_network_id = 1;
scan_res = slsi_prepare_scan_result(skb, anqp_length, hs2_network_id);
if (scan_res == NULL) {
SLSI_NET_ERR(dev, "Failed to prepare scan result\n");
goto out;
}
/* Check for ePNO networks */
if (fapi_get_u16(skb, u.mlme_scan_ind.preferrednetwork_ap)) {
if (anqp_length == 0)
slsi_vendor_event(sdev, SLSI_NL80211_EPNO_EVENT,
&scan_res->nl_scan_res, scan_res->scan_res_len);
else
slsi_vendor_event(sdev, SLSI_NL80211_HOTSPOT_MATCH,
&scan_res->nl_scan_res, scan_res->scan_res_len + scan_res->anqp_length);
}
if (bucket->report_events & SLSI_REPORT_EVENTS_FULL_RESULTS) {
struct sk_buff *nlevent;
SLSI_NET_DBG3(dev, SLSI_GSCAN, "report_events: SLSI_REPORT_EVENTS_FULL_RESULTS\n");
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
nlevent = cfg80211_vendor_event_alloc(sdev->wiphy, NULL, scan_res->scan_res_len + 4, SLSI_NL80211_FULL_SCAN_RESULT_EVENT, GFP_KERNEL);
#else
nlevent = cfg80211_vendor_event_alloc(sdev->wiphy, scan_res->scan_res_len + 4, SLSI_NL80211_FULL_SCAN_RESULT_EVENT, GFP_KERNEL);
#endif
if (!nlevent) {
SLSI_ERR(sdev, "failed to allocate sbk of size:%d\n", scan_res->scan_res_len + 4);
kfree(scan_res);
goto out;
}
if (nla_put_u32(nlevent, GSCAN_ATTRIBUTE_SCAN_BUCKET_BIT, (1 << bucket_index)) ||
nla_put(nlevent, GSCAN_ATTRIBUTE_SCAN_RESULTS, scan_res->scan_res_len, &scan_res->nl_scan_res)) {
SLSI_ERR(sdev, "failed to put data\n");
kfree_skb(nlevent);
kfree(scan_res);
goto out;
}
cfg80211_vendor_event(nlevent, GFP_KERNEL);
}
if (slsi_check_scan_result(sdev, bucket, scan_res) == SLSI_DISCARD_SCAN_RESULT) {
kfree(scan_res);
goto out;
}
slsi_gscan_hash_add(sdev, scan_res);
out:
kfree_skb(skb);
}
u8 slsi_gscan_get_scan_policy(enum wifi_band band)
{
u8 scan_policy;
switch (band) {
case WIFI_BAND_UNSPECIFIED:
scan_policy = FAPI_SCANPOLICY_ANY_RA;
break;
case WIFI_BAND_BG:
scan_policy = FAPI_SCANPOLICY_2_4GHZ;
break;
case WIFI_BAND_A:
scan_policy = (FAPI_SCANPOLICY_5GHZ |
FAPI_SCANPOLICY_NON_DFS);
break;
case WIFI_BAND_A_DFS:
scan_policy = (FAPI_SCANPOLICY_5GHZ |
FAPI_SCANPOLICY_DFS);
break;
case WIFI_BAND_A_WITH_DFS:
scan_policy = (FAPI_SCANPOLICY_5GHZ |
FAPI_SCANPOLICY_NON_DFS |
FAPI_SCANPOLICY_DFS);
break;
case WIFI_BAND_ABG:
scan_policy = (FAPI_SCANPOLICY_5GHZ |
FAPI_SCANPOLICY_NON_DFS |
FAPI_SCANPOLICY_2_4GHZ);
break;
case WIFI_BAND_ABG_WITH_DFS:
scan_policy = (FAPI_SCANPOLICY_5GHZ |
FAPI_SCANPOLICY_NON_DFS |
FAPI_SCANPOLICY_DFS |
FAPI_SCANPOLICY_2_4GHZ);
break;
default:
scan_policy = FAPI_SCANPOLICY_ANY_RA;
break;
}
SLSI_DBG2_NODEV(SLSI_GSCAN, "Scan Policy: %#x\n", scan_policy);
return scan_policy;
}
static int slsi_gscan_add_read_params(struct slsi_nl_gscan_param *nl_gscan_param, const void *data, int len)
{
int j = 0;
int type, tmp, tmp1, tmp2, k = 0;
const struct nlattr *iter, *iter1, *iter2;
struct slsi_nl_bucket_param *nl_bucket;
u32 val = 0;
nla_for_each_attr(iter, data, len, tmp) {
if (!iter)
return -EINVAL;
type = nla_type(iter);
if (j >= SLSI_GSCAN_MAX_BUCKETS)
break;
switch (type) {
case GSCAN_ATTRIBUTE_BASE_PERIOD:
if (slsi_util_nla_get_u32(iter, &nl_gscan_param->base_period))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN:
if (slsi_util_nla_get_u32(iter, &nl_gscan_param->max_ap_per_scan))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_REPORT_THRESHOLD:
if (slsi_util_nla_get_u32(iter, &nl_gscan_param->report_threshold_percent))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_REPORT_THRESHOLD_NUM_SCANS:
if (slsi_util_nla_get_u32(iter, &nl_gscan_param->report_threshold_num_scans))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_NUM_BUCKETS:
if (slsi_util_nla_get_u32(iter, &nl_gscan_param->num_buckets))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_CH_BUCKET_1:
case GSCAN_ATTRIBUTE_CH_BUCKET_2:
case GSCAN_ATTRIBUTE_CH_BUCKET_3:
case GSCAN_ATTRIBUTE_CH_BUCKET_4:
case GSCAN_ATTRIBUTE_CH_BUCKET_5:
case GSCAN_ATTRIBUTE_CH_BUCKET_6:
case GSCAN_ATTRIBUTE_CH_BUCKET_7:
case GSCAN_ATTRIBUTE_CH_BUCKET_8:
nla_for_each_nested(iter1, iter, tmp1) {
if (!iter1)
return -EINVAL;
type = nla_type(iter1);
nl_bucket = nl_gscan_param->nl_bucket;
switch (type) {
case GSCAN_ATTRIBUTE_BUCKET_ID:
if (slsi_util_nla_get_u32(iter1, &(nl_bucket[j].bucket_index)))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_BUCKET_PERIOD:
if (slsi_util_nla_get_u32(iter1, &(nl_bucket[j].period)))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS:
if (slsi_util_nla_get_u32(iter1, &(nl_bucket[j].num_channels)))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_BUCKET_CHANNELS:
nla_for_each_nested(iter2, iter1, tmp2) {
if (k >= SLSI_GSCAN_MAX_CHANNELS)
break;
if (slsi_util_nla_get_u32(iter2, &(nl_bucket[j].channels[k].channel)))
return -EINVAL;
k++;
}
k = 0;
break;
case GSCAN_ATTRIBUTE_BUCKETS_BAND:
if (slsi_util_nla_get_u32(iter1, &(nl_bucket[j].band)))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_REPORT_EVENTS:
if (slsi_util_nla_get_u32(iter1, &val))
return -EINVAL;
nl_bucket[j].report_events = (u8)val;
break;
case GSCAN_ATTRIBUTE_BUCKET_EXPONENT:
if (slsi_util_nla_get_u32(iter1, &(nl_bucket[j].exponent)))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT:
if (slsi_util_nla_get_u32(iter1, &(nl_bucket[j].step_count)))
return -EINVAL;
break;
case GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD:
if (slsi_util_nla_get_u32(iter1, &(nl_bucket[j].max_period)))
return -EINVAL;
break;
default:
SLSI_ERR_NODEV("No ATTR_BUKTS_type - %x\n", type);
break;
}
}
j++;
break;
default:
SLSI_ERR_NODEV("No GSCAN_ATTR_CH_BUKT_type - %x\n", type);
break;
}
}
return 0;
}
int slsi_gscan_add_verify_params(struct slsi_nl_gscan_param *nl_gscan_param)
{
int i;
if ((nl_gscan_param->max_ap_per_scan < 0) || (nl_gscan_param->max_ap_per_scan > SLSI_GSCAN_MAX_AP_CACHE_PER_SCAN)) {
SLSI_ERR_NODEV("Invalid max_ap_per_scan: %d\n", nl_gscan_param->max_ap_per_scan);
return -EINVAL;
}
if ((nl_gscan_param->report_threshold_percent < 0) || (nl_gscan_param->report_threshold_percent > SLSI_GSCAN_MAX_SCAN_REPORTING_THRESHOLD)) {
SLSI_ERR_NODEV("Invalid report_threshold_percent: %d\n", nl_gscan_param->report_threshold_percent);
return -EINVAL;
}
if ((nl_gscan_param->num_buckets <= 0) || (nl_gscan_param->num_buckets > SLSI_GSCAN_MAX_BUCKETS)) {
SLSI_ERR_NODEV("Invalid num_buckets: %d\n", nl_gscan_param->num_buckets);
return -EINVAL;
}
for (i = 0; i < nl_gscan_param->num_buckets; i++) {
if ((nl_gscan_param->nl_bucket[i].band == WIFI_BAND_UNSPECIFIED) && (nl_gscan_param->nl_bucket[i].num_channels == 0)) {
SLSI_ERR_NODEV("No band/channels provided for gscan: band = %d, num_channel = %d\n",
nl_gscan_param->nl_bucket[i].band, nl_gscan_param->nl_bucket[i].num_channels);
return -EINVAL;
}
if (nl_gscan_param->nl_bucket[i].report_events > 4) {
SLSI_ERR_NODEV("Unsupported report event: report_event = %d\n", nl_gscan_param->nl_bucket[i].report_events);
return -EINVAL;
}
}
return 0;
}
void slsi_gscan_add_to_list(struct slsi_gscan **sdev_gscan, struct slsi_gscan *gscan)
{
gscan->next = *sdev_gscan;
*sdev_gscan = gscan;
}
int slsi_gscan_alloc_buckets(struct slsi_dev *sdev, struct slsi_gscan *gscan, int num_buckets)
{
int i;
int bucket_index = 0;
int free_buckets = 0;
for (i = 0; i < SLSI_GSCAN_MAX_BUCKETS; i++)
if (!sdev->bucket[i].used)
free_buckets++;
if (num_buckets > free_buckets) {
SLSI_ERR_NODEV("Not enough free buckets, num_buckets = %d, free_buckets = %d\n",
num_buckets, free_buckets);
return -EINVAL;
}
/* Allocate free buckets for the current gscan */
for (i = 0; i < SLSI_GSCAN_MAX_BUCKETS; i++)
if (!sdev->bucket[i].used) {
sdev->bucket[i].used = true;
sdev->bucket[i].gscan = gscan;
gscan->bucket[bucket_index] = &sdev->bucket[i];
bucket_index++;
if (bucket_index == num_buckets)
break;
}
return 0;
}
static void slsi_gscan_free_buckets(struct slsi_gscan *gscan)
{
struct slsi_bucket *bucket;
int i;
SLSI_DBG1_NODEV(SLSI_GSCAN, "gscan = %p, num_buckets = %d\n", gscan, gscan->num_buckets);
for (i = 0; i < gscan->num_buckets; i++) {
bucket = gscan->bucket[i];
SLSI_DBG2_NODEV(SLSI_GSCAN, "bucket = %p, used = %d, report_events = %d, scan_id = %#x, gscan = %p\n",
bucket, bucket->used, bucket->report_events, bucket->scan_id, bucket->gscan);
if (bucket->used) {
bucket->used = false;
bucket->report_events = 0;
bucket->gscan = NULL;
}
}
}
void slsi_gscan_flush_scan_results(struct slsi_dev *sdev)
{
struct netdev_vif *ndev_vif;
struct slsi_gscan_result *temp;
int i;
ndev_vif = slsi_gscan_get_vif(sdev);
if (!ndev_vif) {
SLSI_WARN_NODEV("ndev_vif is NULL");
return;
}
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
for (i = 0; i < SLSI_GSCAN_HASH_TABLE_SIZE; i++)
while (sdev->gscan_hash_table[i]) {
temp = sdev->gscan_hash_table[i];
sdev->gscan_hash_table[i] = sdev->gscan_hash_table[i]->hnext;
sdev->num_gscan_results--;
sdev->buffer_consumed -= temp->scan_res_len;
kfree(temp);
}
SLSI_DBG2(sdev, SLSI_GSCAN, "num_gscan_results: %d, buffer_consumed = %d\n",
sdev->num_gscan_results, sdev->buffer_consumed);
if (sdev->num_gscan_results != 0)
SLSI_WARN_NODEV("sdev->num_gscan_results is not zero\n");
if (sdev->buffer_consumed != 0)
SLSI_WARN_NODEV("sdev->buffer_consumedis not zero\n");
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
}
static int slsi_gscan_add_mlme(struct slsi_dev *sdev, struct slsi_nl_gscan_param *nl_gscan_param, struct slsi_gscan *gscan)
{
struct slsi_gscan_param gscan_param;
struct net_device *dev;
int ret = 0;
int i, j;
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
u8 mac_addr_mask[ETH_ALEN];
#endif
dev = slsi_gscan_get_netdev(sdev);
if (!dev) {
SLSI_WARN_NODEV("dev is NULL\n");
return -EINVAL;
}
for (i = 0; i < nl_gscan_param->num_buckets; i++) {
u16 report_mode = 0;
gscan_param.nl_bucket = &nl_gscan_param->nl_bucket[i]; /* current bucket */
gscan_param.bucket = gscan->bucket[i];
if (gscan_param.bucket->report_events) {
if (gscan_param.bucket->report_events & SLSI_REPORT_EVENTS_EACH_SCAN)
report_mode |= FAPI_REPORTMODE_END_OF_SCAN_CYCLE;
if (gscan_param.bucket->report_events & SLSI_REPORT_EVENTS_FULL_RESULTS)
report_mode |= FAPI_REPORTMODE_REAL_TIME;
if (gscan_param.bucket->report_events & SLSI_REPORT_EVENTS_NO_BATCH)
report_mode |= FAPI_REPORTMODE_NO_BATCH;
} else {
report_mode = FAPI_REPORTMODE_RESERVED;
}
if (report_mode == 0) {
SLSI_NET_ERR(dev, "Invalid report event value: %d\n", gscan_param.bucket->report_events);
return -EINVAL;
}
/* In case of epno no_batch mode should be set. */
if (sdev->epno_active)
report_mode |= FAPI_REPORTMODE_NO_BATCH;
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
memset(mac_addr_mask, 0xFF, ETH_ALEN);
if (sdev->scan_addr_set == 1) {
for (j = 3; j < ETH_ALEN; j++)
mac_addr_mask[j] = 0x00;
ret = slsi_set_mac_randomisation_mask(sdev, mac_addr_mask);
if (ret)
sdev->scan_addr_set = 0;
} else
slsi_set_mac_randomisation_mask(sdev, mac_addr_mask);
#endif
ret = slsi_mlme_add_scan(sdev,
dev,
FAPI_SCANTYPE_GSCAN,
report_mode,
0, /* n_ssids */
NULL, /* ssids */
nl_gscan_param->nl_bucket[i].num_channels,
NULL, /* ieee80211_channel */
&gscan_param,
NULL, /* ies */
0, /* ies_len */
false /* wait_for_ind */);
if (ret != 0) {
SLSI_NET_ERR(dev, "Failed to add bucket: %d\n", i);
/* Delete the scan those are already added */
for (i = (i - 1); i >= 0; i--)
slsi_mlme_del_scan(sdev, dev, gscan->bucket[i]->scan_id, false);
break;
}
}
return ret;
}
static int slsi_gscan_add(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
int ret = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct slsi_nl_gscan_param *nl_gscan_param = NULL;
struct slsi_gscan *gscan;
struct netdev_vif *ndev_vif;
int buffer_threshold;
int i;
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_ADD_GSCAN\n");
if (!sdev) {
SLSI_WARN_NODEV("sdev is NULL\n");
return -EINVAL;
}
if (!slsi_dev_gscan_supported())
return -ENOTSUPP;
ndev_vif = slsi_gscan_get_vif(sdev);
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
/* Allocate memory for the received scan params */
nl_gscan_param = kzalloc(sizeof(*nl_gscan_param), GFP_KERNEL);
if (nl_gscan_param == NULL) {
SLSI_ERR_NODEV("Failed for allocate memory for gscan params\n");
ret = -ENOMEM;
goto exit;
}
slsi_gscan_add_read_params(nl_gscan_param, data, len);
#ifdef CONFIG_SCSC_WLAN_DEBUG
slsi_gscan_add_dump_params(nl_gscan_param);
#endif
ret = slsi_gscan_add_verify_params(nl_gscan_param);
if (ret) {
/* After adding a hotlist a new gscan is added with 0 buckets - return success */
if (nl_gscan_param->num_buckets == 0) {
kfree(nl_gscan_param);
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return 0;
}
goto exit;
}
/* Allocate Memory for the new gscan */
gscan = kzalloc(sizeof(*gscan), GFP_KERNEL);
if (gscan == NULL) {
SLSI_ERR_NODEV("Failed to allocate memory for gscan\n");
ret = -ENOMEM;
goto exit;
}
gscan->num_buckets = nl_gscan_param->num_buckets;
gscan->report_threshold_percent = nl_gscan_param->report_threshold_percent;
gscan->report_threshold_num_scans = nl_gscan_param->report_threshold_num_scans;
gscan->nl_bucket = nl_gscan_param->nl_bucket[0];
/* If multiple gscan is added; consider the lowest report_threshold_percent */
buffer_threshold = (SLSI_GSCAN_MAX_SCAN_CACHE_SIZE * nl_gscan_param->report_threshold_percent) / 100;
if ((sdev->buffer_threshold == 0) || (buffer_threshold < sdev->buffer_threshold))
sdev->buffer_threshold = buffer_threshold;
ret = slsi_gscan_alloc_buckets(sdev, gscan, nl_gscan_param->num_buckets);
if (ret)
goto exit_with_gscan_free;
for (i = 0; i < nl_gscan_param->num_buckets; i++)
gscan->bucket[i]->report_events = nl_gscan_param->nl_bucket[i].report_events;
ret = slsi_gscan_add_mlme(sdev, nl_gscan_param, gscan);
if (ret) {
/* Free the buckets */
slsi_gscan_free_buckets(gscan);
goto exit_with_gscan_free;
}
slsi_gscan_add_to_list(&sdev->gscan, gscan);
goto exit;
exit_with_gscan_free:
kfree(gscan);
exit:
kfree(nl_gscan_param);
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return ret;
}
static int slsi_gscan_del(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev;
struct netdev_vif *ndev_vif;
struct slsi_gscan *gscan;
int ret = 0;
int i;
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_DEL_GSCAN\n");
dev = slsi_gscan_get_netdev(sdev);
if (!dev) {
SLSI_WARN_NODEV("dev is NULL\n");
return -EINVAL;
}
ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
while (sdev->gscan != NULL) {
gscan = sdev->gscan;
SLSI_DBG3(sdev, SLSI_GSCAN, "gscan = %p, num_buckets = %d\n", gscan, gscan->num_buckets);
for (i = 0; i < gscan->num_buckets; i++)
if (gscan->bucket[i]->used)
slsi_mlme_del_scan(sdev, dev, gscan->bucket[i]->scan_id, false);
slsi_gscan_free_buckets(gscan);
sdev->gscan = gscan->next;
kfree(gscan);
}
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
slsi_gscan_flush_scan_results(sdev);
sdev->buffer_threshold = 0;
return ret;
}
static int slsi_gscan_get_scan_results(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct sk_buff *skb;
struct slsi_gscan_result *scan_res;
struct nlattr *scan_hdr;
struct netdev_vif *ndev_vif;
int num_results = 0;
int mem_needed;
const struct nlattr *attr;
int nl_num_results = 0;
int ret = 0;
int temp;
int type;
int i;
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_GET_SCAN_RESULTS\n");
/* Read the number of scan results need to be given */
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case GSCAN_ATTRIBUTE_NUM_OF_RESULTS:
if (slsi_util_nla_get_u32(attr, &nl_num_results))
return -EINVAL;
break;
default:
SLSI_ERR_NODEV("Unknown attribute: %d\n", type);
break;
}
}
ndev_vif = slsi_gscan_get_vif(sdev);
if (!ndev_vif) {
SLSI_WARN_NODEV("ndev_vif is NULL\n");
return -EINVAL;
}
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
num_results = sdev->num_gscan_results;
SLSI_DBG3(sdev, SLSI_GSCAN, "nl_num_results: %d, num_results = %d\n", nl_num_results, sdev->num_gscan_results);
if (num_results == 0) {
SLSI_DBG1(sdev, SLSI_GSCAN, "No scan results available\n");
/* Return value should be 0 for this scenario */
goto exit;
}
/* Find the number of results to return */
if (num_results > nl_num_results)
num_results = nl_num_results;
/* 12 bytes additional for scan_id, flags and num_resuls */
mem_needed = num_results * sizeof(struct slsi_nl_scan_result_param) + 12;
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_needed);
if (skb == NULL) {
SLSI_ERR_NODEV("skb alloc failed");
ret = -ENOMEM;
goto exit;
}
scan_hdr = nla_nest_start(skb, GSCAN_ATTRIBUTE_SCAN_RESULTS);
if (scan_hdr == NULL) {
kfree_skb(skb);
SLSI_ERR_NODEV("scan_hdr is NULL.\n");
ret = -ENOMEM;
goto exit;
}
ret |= nla_put_u32(skb, GSCAN_ATTRIBUTE_SCAN_ID, 0);
ret |= nla_put_u8(skb, GSCAN_ATTRIBUTE_SCAN_FLAGS, 0);
ret |= nla_put_u32(skb, GSCAN_ATTRIBUTE_NUM_OF_RESULTS, num_results);
for (i = 0; i < SLSI_GSCAN_HASH_TABLE_SIZE; i++)
while (sdev->gscan_hash_table[i]) {
scan_res = sdev->gscan_hash_table[i];
sdev->gscan_hash_table[i] = sdev->gscan_hash_table[i]->hnext;
sdev->num_gscan_results--;
sdev->buffer_consumed -= scan_res->scan_res_len;
/* TODO: If IE is included then HAL is not able to parse the results */
ret |= nla_put(skb, GSCAN_ATTRIBUTE_SCAN_RESULTS, sizeof(struct slsi_nl_scan_result_param), &scan_res->nl_scan_res);
kfree(scan_res);
num_results--;
if (num_results == 0)
goto out;
}
out:
nla_nest_end(skb, scan_hdr);
if (ret) {
SLSI_ERR(sdev, "Error in nla_put*:%x\n", ret);
kfree_skb(skb);
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return ret;
}
void slsi_rx_rssi_report_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_rssi_monitor_evt event_data;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_ETHER_COPY(event_data.bssid, fapi_get_buff(skb, u.mlme_rssi_report_ind.bssid));
event_data.rssi = fapi_get_s16(skb, u.mlme_rssi_report_ind.rssi);
SLSI_DBG3(sdev, SLSI_GSCAN, "RSSI threshold breached, Current RSSI for %pM= %d\n", event_data.bssid, event_data.rssi);
slsi_vendor_event(sdev, SLSI_NL80211_RSSI_REPORT_EVENT, &event_data, sizeof(event_data));
kfree_skb(skb);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
#ifdef CONFIG_SCSC_WLAN_KEY_MGMT_OFFLOAD
static int slsi_key_mgmt_set_pmk(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *pmk, int pmklen)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev;
struct netdev_vif *ndev_vif;
int r = 0;
if (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) {
SLSI_DBG3(sdev, SLSI_GSCAN, "Not required to set PMK for P2P client\n");
return r;
}
SLSI_DBG3(sdev, SLSI_GSCAN, "SUBCMD_SET_PMK Received\n");
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
ndev_vif = netdev_priv(net_dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
r = slsi_mlme_set_pmk(sdev, net_dev, pmk, (u16)pmklen);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
#endif
static int slsi_set_bssid_blacklist(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev;
struct netdev_vif *ndev_vif;
int temp1;
int type;
const struct nlattr *attr;
u32 num_bssids = 0;
u8 i = 0;
int ret;
u8 *bssid = NULL;
struct cfg80211_acl_data *acl_data = NULL;
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_SET_BSSID_BLACK_LIST\n");
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
if (!net_dev) {
SLSI_WARN_NODEV("net_dev is NULL\n");
return -EINVAL;
}
ndev_vif = netdev_priv(net_dev);
/*This subcmd can be issued in either connected or disconnected state.
* Hence using scan_mutex and not vif_mutex
*/
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
nla_for_each_attr(attr, data, len, temp1) {
if (!attr) {
ret = -EINVAL;
break;
}
type = nla_type(attr);
switch (type) {
case GSCAN_ATTRIBUTE_NUM_BSSID:
if (acl_data)
break;
if (slsi_util_nla_get_u32(attr, &num_bssids)) {
ret = -EINVAL;
goto exit;
}
if (num_bssids == 0 || (num_bssids > (u32)((ULONG_MAX - sizeof(*acl_data)) / (sizeof(struct mac_address))))) {
ret = -EINVAL;
goto exit;
}
acl_data = kmalloc(sizeof(*acl_data) + (sizeof(struct mac_address) * num_bssids), GFP_KERNEL);
if (!acl_data) {
ret = -ENOMEM;
goto exit;
}
acl_data->n_acl_entries = num_bssids;
break;
case GSCAN_ATTRIBUTE_BLACKLIST_BSSID:
if (!acl_data) {
ret = -EINVAL;
goto exit;
}
if (nla_len(attr) != 6) { /*Attribute length should be equal to length of mac address which is 6 bytes.*/
ret = -EINVAL;
goto exit;
}
if (i >= num_bssids) {
ret = -EINVAL;
goto exit;
}
if (nla_len(attr) < ETH_ALEN) {
ret = -EINVAL;
goto exit;
}
bssid = (u8 *)nla_data(attr);
SLSI_ETHER_COPY(acl_data->mac_addrs[i].addr, bssid);
SLSI_DBG3_NODEV(SLSI_GSCAN, "mac_addrs[%d]:%pM)\n", i, acl_data->mac_addrs[i].addr);
i++;
break;
default:
SLSI_ERR_NODEV("Unknown attribute: %d\n", type);
ret = -EINVAL;
goto exit;
}
}
if (acl_data) {
acl_data->acl_policy = FAPI_ACLPOLICY_BLACKLIST;
ret = slsi_mlme_set_acl(sdev, net_dev, 0, acl_data->acl_policy, acl_data->n_acl_entries, acl_data->mac_addrs);
if (ret)
SLSI_ERR_NODEV("Failed to set bssid blacklist\n");
} else {
ret = -EINVAL;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
kfree(acl_data);
return ret;
}
static int slsi_start_keepalive_offload(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
#ifndef CONFIG_SCSC_WLAN_NAT_KEEPALIVE_DISABLE
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev;
struct netdev_vif *ndev_vif;
int temp;
int type;
const struct nlattr *attr;
u16 ip_pkt_len = 0;
u8 *ip_pkt = NULL, *src_mac_addr = NULL, *dst_mac_addr = NULL;
u32 period = 0;
struct slsi_peer *peer;
struct sk_buff *skb;
struct ethhdr *ehdr;
int r = 0;
u16 host_tag = 0;
u8 index = 0;
SLSI_DBG3(sdev, SLSI_MLME, "SUBCMD_START_KEEPALIVE_OFFLOAD received\n");
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
ndev_vif = netdev_priv(net_dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated) {
SLSI_WARN_NODEV("ndev_vif is not activated\n");
r = -EINVAL;
goto exit;
}
if (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) {
SLSI_WARN_NODEV("ndev_vif->vif_type is not FAPI_VIFTYPE_STATION\n");
r = -EINVAL;
goto exit;
}
if (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED) {
SLSI_WARN_NODEV("ndev_vif->sta.vif_status is not SLSI_VIF_STATUS_CONNECTED\n");
r = -EINVAL;
goto exit;
}
peer = slsi_get_peer_from_qs(sdev, net_dev, SLSI_STA_PEER_QUEUESET);
if (!peer) {
SLSI_WARN_NODEV("peer is NULL\n");
r = -EINVAL;
goto exit;
}
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN:
if (slsi_util_nla_get_u16(attr, &ip_pkt_len)) {
r = -EINVAL;
goto exit;
}
break;
case MKEEP_ALIVE_ATTRIBUTE_IP_PKT:
if (nla_len(attr) < ip_pkt_len) {
r = -EINVAL;
goto exit;
}
ip_pkt = (u8 *)nla_data(attr);
break;
case MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC:
if (slsi_util_nla_get_u32(attr, &period)) {
r = -EINVAL;
goto exit;
}
break;
case MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR:
if (nla_len(attr) < ETH_ALEN) {
r = -EINVAL;
goto exit;
}
dst_mac_addr = (u8 *)nla_data(attr);
break;
case MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR:
if (nla_len(attr) < ETH_ALEN) {
r = -EINVAL;
goto exit;
}
src_mac_addr = (u8 *)nla_data(attr);
break;
case MKEEP_ALIVE_ATTRIBUTE_ID:
if (slsi_util_nla_get_u8(attr, &index)) {
r = -EINVAL;
goto exit;
}
if (index > SLSI_MAX_KEEPALIVE_ID) {
r = -EINVAL;
goto exit;
}
break;
default:
SLSI_ERR_NODEV("Unknown attribute: %d\n", type);
r = -EINVAL;
goto exit;
}
}
/* Stop any existing request. This may fail if no request exists
* so ignore the return value
*/
if (!slsi_mlme_send_frame_mgmt(sdev, net_dev, NULL, 0,
FAPI_DATAUNITDESCRIPTOR_IEEE802_3_FRAME,
FAPI_MESSAGETYPE_ANY_OTHER,
ndev_vif->sta.keepalive_host_tag[index - 1], 0, 0, 0))
SLSI_DBG3(sdev, SLSI_MLME, "slsi_mlme_send_frame_mgmt returned failure\n");
skb = alloc_skb(SLSI_NETIF_SKB_HEADROOM + SLSI_NETIF_SKB_TAILROOM + sizeof(struct ethhdr) + ip_pkt_len, GFP_KERNEL);
if (!skb) {
SLSI_WARN_NODEV("memory allocation failed for skb (size: %d)\n", SLSI_NETIF_SKB_HEADROOM + SLSI_NETIF_SKB_TAILROOM + sizeof(struct ethhdr) + ip_pkt_len);
r = -ENOMEM;
goto exit;
}
skb_reserve(skb, SLSI_NETIF_SKB_HEADROOM - SLSI_SKB_GET_ALIGNMENT_OFFSET(skb));
skb_reset_mac_header(skb);
skb_set_network_header(skb, sizeof(struct ethhdr));
/* Ethernet Header */
ehdr = (struct ethhdr *)skb_put(skb, sizeof(struct ethhdr));
if (dst_mac_addr)
SLSI_ETHER_COPY(ehdr->h_dest, dst_mac_addr);
if (src_mac_addr)
SLSI_ETHER_COPY(ehdr->h_source, src_mac_addr);
ehdr->h_proto = cpu_to_be16(ETH_P_IP);
if (ip_pkt)
memcpy(skb_put(skb, ip_pkt_len), ip_pkt, ip_pkt_len);
skb->dev = net_dev;
skb->protocol = ETH_P_IP;
skb->ip_summed = CHECKSUM_UNNECESSARY;
/* Queueset 0 AC 0 */
skb->queue_mapping = slsi_netif_get_peer_queue(0, 0);
/* Enabling the "Don't Fragment" Flag in the IP Header */
ip_hdr(skb)->frag_off |= htons(IP_DF);
/* Calculation of IP header checksum */
ip_hdr(skb)->check = 0;
ip_send_check(ip_hdr(skb));
host_tag = slsi_tx_mgmt_host_tag(sdev);
r = slsi_mlme_send_frame_data(sdev, net_dev, skb, FAPI_MESSAGETYPE_ANY_OTHER, host_tag,
0, (period * 1000));
if (r == 0)
ndev_vif->sta.keepalive_host_tag[index - 1] = host_tag;
else
kfree_skb(skb);
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
#else
SLSI_DBG3_NODEV(SLSI_MLME, "SUBCMD_START_KEEPALIVE_OFFLOAD received\n");
SLSI_DBG3_NODEV(SLSI_MLME, "NAT Keep Alive Feature is disabled\n");
return -EOPNOTSUPP;
#endif
}
static int slsi_stop_keepalive_offload(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
#ifndef CONFIG_SCSC_WLAN_NAT_KEEPALIVE_DISABLE
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev;
struct netdev_vif *ndev_vif;
int r = 0;
int temp;
int type;
const struct nlattr *attr;
u8 index = 0;
SLSI_DBG3(sdev, SLSI_MLME, "SUBCMD_STOP_KEEPALIVE_OFFLOAD received\n");
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
ndev_vif = netdev_priv(net_dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated) {
SLSI_WARN(sdev, "VIF is not activated\n");
r = -EINVAL;
goto exit;
}
if (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) {
SLSI_WARN(sdev, "Not a STA VIF\n");
r = -EINVAL;
goto exit;
}
if (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED) {
SLSI_WARN(sdev, "VIF is not connected\n");
r = -EINVAL;
goto exit;
}
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case MKEEP_ALIVE_ATTRIBUTE_ID:
if (slsi_util_nla_get_u8(attr, &index)) {
r = -EINVAL;
goto exit;
}
if (index > SLSI_MAX_KEEPALIVE_ID) {
r = -EINVAL;
goto exit;
}
break;
default:
SLSI_ERR_NODEV("Unknown attribute: %d\n", type);
r = -EINVAL;
goto exit;
}
}
r = slsi_mlme_send_frame_mgmt(sdev, net_dev, NULL, 0, FAPI_DATAUNITDESCRIPTOR_IEEE802_3_FRAME,
FAPI_MESSAGETYPE_ANY_OTHER, ndev_vif->sta.keepalive_host_tag[index - 1], 0, 0, 0);
ndev_vif->sta.keepalive_host_tag[index - 1] = 0;
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
#else
SLSI_DBG3_NODEV(SLSI_MLME, "SUBCMD_STOP_KEEPALIVE_OFFLOAD received\n");
SLSI_DBG3_NODEV(SLSI_MLME, "NAT Keep Alive Feature is disabled\n");
return -EOPNOTSUPP;
#endif
}
static inline int slsi_epno_ssid_list_get(struct slsi_dev *sdev,
struct slsi_epno_ssid_param *epno_ssid_params, const struct nlattr *outer)
{
int type, tmp;
u8 epno_auth;
u8 len = 0;
u16 val = 0;
const struct nlattr *inner;
nla_for_each_nested(inner, outer, tmp) {
type = nla_type(inner);
switch (type) {
case SLSI_ATTRIBUTE_EPNO_FLAGS:
if (slsi_util_nla_get_u16(inner, &val))
return -EINVAL;
epno_ssid_params->flags |= val;
break;
case SLSI_ATTRIBUTE_EPNO_AUTH:
if (slsi_util_nla_get_u8(inner, &epno_auth))
return -EINVAL;
if (epno_auth & SLSI_EPNO_AUTH_FIELD_WEP_OPEN)
epno_ssid_params->flags |= FAPI_EPNOPOLICY_AUTH_OPEN;
else if (epno_auth & SLSI_EPNO_AUTH_FIELD_WPA_PSK)
epno_ssid_params->flags |= FAPI_EPNOPOLICY_AUTH_PSK;
else if (epno_auth & SLSI_EPNO_AUTH_FIELD_WPA_EAP)
epno_ssid_params->flags |= FAPI_EPNOPOLICY_AUTH_EAPOL;
break;
case SLSI_ATTRIBUTE_EPNO_SSID_LEN:
if (slsi_util_nla_get_u8(inner, &len))
return -EINVAL;
if (len <= 32) {
epno_ssid_params->ssid_len = len;
} else {
SLSI_ERR(sdev, "SSID too long %d\n", len);
return -EINVAL;
}
break;
case SLSI_ATTRIBUTE_EPNO_SSID:
if (slsi_util_nla_get_data(inner, len, epno_ssid_params->ssid))
return -EINVAL;
break;
default:
SLSI_WARN(sdev, "Ignoring unknown type:%d\n", type);
}
}
return 0;
}
static int slsi_set_epno_ssid(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev;
struct netdev_vif *ndev_vif;
int r = 0;
int tmp, tmp1, type, num = 0;
const struct nlattr *outer, *iter;
u8 i = 0, val = 0;
struct slsi_epno_ssid_param *epno_ssid_params;
struct slsi_epno_param *epno_params;
SLSI_DBG3(sdev, SLSI_GSCAN, "SUBCMD_SET_EPNO_LIST Received\n");
if (!slsi_dev_epno_supported())
return -EOPNOTSUPP;
epno_params = kmalloc((sizeof(*epno_params) + (sizeof(*epno_ssid_params) * SLSI_GSCAN_MAX_EPNO_SSIDS)),
GFP_KERNEL);
if (!epno_params) {
SLSI_ERR(sdev, "Mem alloc fail\n");
return -ENOMEM;
}
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
ndev_vif = netdev_priv(net_dev);
nla_for_each_attr(iter, data, len, tmp1) {
type = nla_type(iter);
switch (type) {
case SLSI_ATTRIBUTE_EPNO_MINIMUM_5G_RSSI:
if (slsi_util_nla_get_u16(iter, &epno_params->min_5g_rssi)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_MINIMUM_2G_RSSI:
if (slsi_util_nla_get_u16(iter, &epno_params->min_2g_rssi)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_INITIAL_SCORE_MAX:
if (slsi_util_nla_get_u16(iter, &epno_params->initial_score_max)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_CUR_CONN_BONUS:
if (slsi_util_nla_get_u8(iter, &epno_params->current_connection_bonus)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_SAME_NETWORK_BONUS:
if (slsi_util_nla_get_u8(iter, &epno_params->same_network_bonus)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_SECURE_BONUS:
if (slsi_util_nla_get_u8(iter, &epno_params->secure_bonus)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_5G_BONUS:
if (slsi_util_nla_get_u8(iter, &epno_params->band_5g_bonus)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_SSID_LIST:
nla_for_each_nested(outer, iter, tmp) {
epno_ssid_params = &epno_params->epno_ssid[i];
epno_ssid_params->flags = 0;
r = slsi_epno_ssid_list_get(sdev, epno_ssid_params, outer);
if (r)
goto exit;
i++;
}
break;
case SLSI_ATTRIBUTE_EPNO_SSID_NUM:
if (slsi_util_nla_get_u8(iter, &val)) {
r = -EINVAL;
goto exit;
}
num = (int)val;
if (num > SLSI_GSCAN_MAX_EPNO_SSIDS) {
SLSI_ERR(sdev, "Cannot support %d SSIDs. max %d\n", num, SLSI_GSCAN_MAX_EPNO_SSIDS);
r = -EINVAL;
goto exit;
}
epno_params->num_networks = num;
break;
default:
SLSI_ERR(sdev, "Invalid attribute %d\n", type);
r = -EINVAL;
goto exit;
}
}
if (i != num) {
SLSI_ERR(sdev, "num_ssid %d does not match ssids sent %d\n", num, i);
r = -EINVAL;
goto exit;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
r = slsi_mlme_set_pno_list(sdev, num, epno_params, NULL);
if (r == 0)
sdev->epno_active = (num != 0);
else
sdev->epno_active = false;
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
exit:
kfree(epno_params);
return r;
}
static int slsi_set_hs_params(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev;
struct netdev_vif *ndev_vif;
int r = 0;
int tmp, tmp1, tmp2, type, num = 0;
const struct nlattr *outer, *inner, *iter;
u8 i = 0, val = 0;
struct slsi_epno_hs2_param *epno_hs2_params_array;
struct slsi_epno_hs2_param *epno_hs2_params;
SLSI_DBG3(sdev, SLSI_GSCAN, "SUBCMD_SET_HS_LIST Received\n");
if (!slsi_dev_epno_supported())
return -EOPNOTSUPP;
epno_hs2_params_array = kmalloc(sizeof(*epno_hs2_params_array) * SLSI_GSCAN_MAX_EPNO_HS2_PARAM, GFP_KERNEL);
if (!epno_hs2_params_array) {
SLSI_ERR(sdev, "Mem alloc fail\n");
return -ENOMEM;
}
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
ndev_vif = netdev_priv(net_dev);
nla_for_each_attr(iter, data, len, tmp2) {
type = nla_type(iter);
switch (type) {
case SLSI_ATTRIBUTE_EPNO_HS_PARAM_LIST:
nla_for_each_nested(outer, iter, tmp) {
if (i >= SLSI_GSCAN_MAX_EPNO_HS2_PARAM)
break;
epno_hs2_params = &epno_hs2_params_array[i];
i++;
nla_for_each_nested(inner, outer, tmp1) {
type = nla_type(inner);
switch (type) {
case SLSI_ATTRIBUTE_EPNO_HS_ID:
if (slsi_util_nla_get_u32(inner, &epno_hs2_params->id)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_HS_REALM:
if (slsi_util_nla_get_data(inner, 256, epno_hs2_params->realm)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_HS_CONSORTIUM_IDS:
if (slsi_util_nla_get_data(inner, (16 * 8), epno_hs2_params->roaming_consortium_ids)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_ATTRIBUTE_EPNO_HS_PLMN:
if (slsi_util_nla_get_data(inner, 3, epno_hs2_params->plmn)) {
r = -EINVAL;
goto exit;
}
break;
default:
SLSI_WARN(sdev, "Ignoring unknown type:%d\n", type);
}
}
}
break;
case SLSI_ATTRIBUTE_EPNO_HS_NUM:
if (slsi_util_nla_get_u8(iter, &val)) {
r = -EINVAL;
goto exit;
}
num = (int)val;
if (num > SLSI_GSCAN_MAX_EPNO_HS2_PARAM) {
SLSI_ERR(sdev, "Cannot support %d SSIDs. max %d\n", num, SLSI_GSCAN_MAX_EPNO_SSIDS);
r = -EINVAL;
goto exit;
}
break;
default:
SLSI_ERR(sdev, "Invalid attribute %d\n", type);
r = -EINVAL;
goto exit;
}
}
if (i != num) {
SLSI_ERR(sdev, "num_ssid %d does not match ssids sent %d\n", num, i);
r = -EINVAL;
goto exit;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
r = slsi_mlme_set_pno_list(sdev, num, NULL, epno_hs2_params_array);
if (r == 0)
sdev->epno_active = true;
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
exit:
kfree(epno_hs2_params_array);
return r;
}
static int slsi_reset_hs_params(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev;
struct netdev_vif *ndev_vif;
int r;
SLSI_DBG3(sdev, SLSI_GSCAN, "SUBCMD_RESET_HS_LIST Received\n");
if (!slsi_dev_epno_supported())
return -EOPNOTSUPP;
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
ndev_vif = netdev_priv(net_dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
r = slsi_mlme_set_pno_list(sdev, 0, NULL, NULL);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
sdev->epno_active = false;
return r;
}
static int slsi_set_rssi_monitor(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev;
struct netdev_vif *ndev_vif;
int r = 0;
int temp;
int type;
const struct nlattr *attr;
s8 min_rssi = 0, max_rssi = 0;
u16 enable = 0;
u8 val = 0;
SLSI_DBG3(sdev, SLSI_GSCAN, "Recd RSSI monitor command\n");
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
if (net_dev == NULL) {
SLSI_ERR(sdev, "netdev is NULL!!\n");
return -ENODEV;
}
ndev_vif = netdev_priv(net_dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated) {
SLSI_ERR(sdev, "Vif not activated\n");
r = -EINVAL;
goto exit;
}
if (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) {
SLSI_ERR(sdev, "Not a STA vif\n");
r = -EINVAL;
goto exit;
}
if (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED) {
SLSI_ERR(sdev, "STA vif not connected\n");
r = -EINVAL;
goto exit;
}
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_RSSI_MONITOR_ATTRIBUTE_START:
if (slsi_util_nla_get_u8(attr, &val)) {
r = -EINVAL;
goto exit;
}
enable = (u16)val;
break;
case SLSI_RSSI_MONITOR_ATTRIBUTE_MIN_RSSI:
if (slsi_util_nla_get_s8(attr, &min_rssi)) {
r = -EINVAL;
goto exit;
}
break;
case SLSI_RSSI_MONITOR_ATTRIBUTE_MAX_RSSI:
if (slsi_util_nla_get_s8(attr, &max_rssi)) {
r = -EINVAL;
goto exit;
}
break;
default:
r = -EINVAL;
goto exit;
}
}
if (min_rssi > max_rssi) {
SLSI_ERR(sdev, "Invalid params, min_rssi= %d ,max_rssi = %d\n", min_rssi, max_rssi);
r = -EINVAL;
goto exit;
}
r = slsi_mlme_set_rssi_monitor(sdev, net_dev, enable, min_rssi, max_rssi);
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
#ifdef CONFIG_SCSC_WLAN_DEBUG
void slsi_lls_debug_dump_stats(struct slsi_dev *sdev, struct slsi_lls_radio_stat *radio_stat,
struct slsi_lls_iface_stat *iface_stat, u8 *buf, int buf_len, int num_of_radios)
{
int i, j;
for (j = 0; j < num_of_radios; j++) {
SLSI_DBG3(sdev, SLSI_GSCAN, "radio_stat====\n");
SLSI_DBG3(sdev, SLSI_GSCAN, "\tradio_id : %d, on_time : %d, tx_time : %d, rx_time : %d,"
"on_time_scan : %d, num_channels : %d\n", radio_stat->radio, radio_stat->on_time,
radio_stat->tx_time, radio_stat->rx_time, radio_stat->on_time_scan,
radio_stat->num_channels);
radio_stat = (struct slsi_lls_radio_stat *)((u8 *)radio_stat + sizeof(struct slsi_lls_radio_stat) +
(sizeof(struct slsi_lls_channel_stat) * radio_stat->num_channels));
}
SLSI_DBG3(sdev, SLSI_GSCAN, "iface_stat====\n");
SLSI_DBG3(sdev, SLSI_GSCAN, "\tiface %p info : (mode : %d, mac_addr : %pM, state : %d, roaming : %d,"
" capabilities : %d, ssid : %s, bssid : %pM, ap_country_str : [%d%d%d])\trssi_data : %d\n",
iface_stat->iface, iface_stat->info.mode, iface_stat->info.mac_addr, iface_stat->info.state,
iface_stat->info.roaming, iface_stat->info.capabilities, iface_stat->info.ssid,
iface_stat->info.bssid, iface_stat->info.ap_country_str[0], iface_stat->info.ap_country_str[1],
iface_stat->info.ap_country_str[2], iface_stat->rssi_data);
SLSI_DBG3(sdev, SLSI_GSCAN, "\tnum_peers %d\n", iface_stat->num_peers);
for (i = 0; i < iface_stat->num_peers; i++) {
SLSI_DBG3(sdev, SLSI_GSCAN, "\t\tpeer_mac_address %pM\n", iface_stat->peer_info[i].peer_mac_address);
}
SLSI_DBG_HEX(sdev, SLSI_GSCAN, buf, buf_len, "return buffer\n");
}
#endif
static int slsi_lls_set_stats(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *net_dev = NULL;
struct netdev_vif *ndev_vif = NULL;
int temp;
int type;
const struct nlattr *attr;
u32 mpdu_size_threshold = 0;
u32 aggr_stat_gathering = 0;
int r = 0, i;
if (!slsi_dev_lls_supported())
return -EOPNOTSUPP;
if (slsi_is_test_mode_enabled()) {
SLSI_WARN(sdev, "not supported in WlanLite mode\n");
return -EOPNOTSUPP;
}
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case LLS_ATTRIBUTE_SET_MPDU_SIZE_THRESHOLD:
if (slsi_util_nla_get_u32(attr, &mpdu_size_threshold))
return -EINVAL;
break;
case LLS_ATTRIBUTE_SET_AGGR_STATISTICS_GATHERING:
if (slsi_util_nla_get_u32(attr, &aggr_stat_gathering))
return -EINVAL;
break;
default:
SLSI_ERR_NODEV("Unknown attribute: %d\n", type);
r = -EINVAL;
}
}
SLSI_MUTEX_LOCK(sdev->device_config_mutex);
/* start Statistics measurements in Firmware */
(void)slsi_mlme_start_link_stats_req(sdev, mpdu_size_threshold, aggr_stat_gathering);
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
if (net_dev) {
ndev_vif = netdev_priv(net_dev);
for (i = 0; i < SLSI_LLS_AC_MAX; i++) {
ndev_vif->rx_packets[i] = 0;
ndev_vif->tx_packets[i] = 0;
ndev_vif->tx_no_ack[i] = 0;
}
}
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
return 0;
}
static int slsi_lls_clear_stats(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int temp;
int type;
const struct nlattr *attr;
u32 stats_clear_req_mask = 0;
u32 stop_req = 0;
int r = 0, i;
struct net_device *net_dev = NULL;
struct netdev_vif *ndev_vif = NULL;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case LLS_ATTRIBUTE_CLEAR_STOP_REQUEST_MASK:
if (slsi_util_nla_get_u32(attr, &stats_clear_req_mask))
return -EINVAL;
SLSI_DBG3(sdev, SLSI_GSCAN, "stats_clear_req_mask:%u\n", stats_clear_req_mask);
break;
case LLS_ATTRIBUTE_CLEAR_STOP_REQUEST:
if (slsi_util_nla_get_u32(attr, &stop_req))
return -EINVAL;
SLSI_DBG3(sdev, SLSI_GSCAN, "stop_req:%u\n", stop_req);
break;
default:
SLSI_ERR(sdev, "Unknown attribute:%d\n", type);
r = -EINVAL;
}
}
/* stop_req = 0 : clear the stats which are flaged 0
* stop_req = 1 : clear the stats which are flaged 1
*/
if (!stop_req)
stats_clear_req_mask = ~stats_clear_req_mask;
SLSI_MUTEX_LOCK(sdev->device_config_mutex);
(void)slsi_mlme_stop_link_stats_req(sdev, stats_clear_req_mask);
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
if (net_dev) {
ndev_vif = netdev_priv(net_dev);
for (i = 0; i < SLSI_LLS_AC_MAX; i++) {
ndev_vif->rx_packets[i] = 0;
ndev_vif->tx_packets[i] = 0;
ndev_vif->tx_no_ack[i] = 0;
}
}
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
return 0;
}
static u32 slsi_lls_ie_to_cap(const u8 *ies, int ies_len)
{
u32 capabilities = 0;
const u8 *ie_data;
const u8 *ie;
int ie_len;
if (!ies || ies_len == 0) {
SLSI_ERR_NODEV("no ie[&%p %d]\n", ies, ies_len);
return 0;
}
ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len);
if (ie) {
ie_len = ie[1];
ie_data = &ie[2];
if ((ie_len >= 4) && (ie_data[3] & SLSI_WLAN_EXT_CAPA3_INTERWORKING_ENABLED))
capabilities |= SLSI_LLS_CAPABILITY_INTERWORKING;
if ((ie_len >= 7) && (ie_data[6] & 0x01)) /* Bit48: UTF-8 ssid */
capabilities |= SLSI_LLS_CAPABILITY_SSID_UTF8;
}
ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, SLSI_WLAN_OUI_TYPE_WFA_HS20_IND, ies, ies_len);
if (ie)
capabilities |= SLSI_LLS_CAPABILITY_HS20;
return capabilities;
}
static void slsi_lls_iface_sta_stats(struct slsi_dev *sdev, struct netdev_vif *ndev_vif,
struct slsi_lls_iface_stat *iface_stat)
{
int i;
struct slsi_lls_interface_link_layer_info *lls_info = &iface_stat->info;
enum slsi_lls_peer_type peer_type;
struct slsi_peer *peer;
const u8 *ie_data, *ie;
int ie_len;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
if (ndev_vif->ifnum == SLSI_NET_INDEX_WLAN) {
lls_info->mode = SLSI_LLS_INTERFACE_STA;
peer_type = SLSI_LLS_PEER_AP;
} else {
lls_info->mode = SLSI_LLS_INTERFACE_P2P_CLIENT;
peer_type = SLSI_LLS_PEER_P2P_GO;
}
switch (ndev_vif->sta.vif_status) {
case SLSI_VIF_STATUS_CONNECTING:
lls_info->state = SLSI_LLS_AUTHENTICATING;
break;
case SLSI_VIF_STATUS_CONNECTED:
lls_info->state = SLSI_LLS_ASSOCIATED;
break;
default:
lls_info->state = SLSI_LLS_DISCONNECTED;
}
lls_info->roaming = ndev_vif->sta.roam_in_progress ?
SLSI_LLS_ROAMING_ACTIVE : SLSI_LLS_ROAMING_IDLE;
iface_stat->info.capabilities = 0;
lls_info->ssid[0] = 0;
if (ndev_vif->sta.sta_bss) {
ie = cfg80211_find_ie(WLAN_EID_SSID, ndev_vif->sta.sta_bss->ies->data,
ndev_vif->sta.sta_bss->ies->len);
if (ie) {
ie_len = ie[1];
ie_data = &ie[2];
memcpy(lls_info->ssid, ie_data, ie_len);
lls_info->ssid[ie_len] = 0;
}
SLSI_ETHER_COPY(lls_info->bssid, ndev_vif->sta.sta_bss->bssid);
ie = cfg80211_find_ie(WLAN_EID_COUNTRY, ndev_vif->sta.sta_bss->ies->data,
ndev_vif->sta.sta_bss->ies->len);
if (ie) {
ie_data = &ie[2];
memcpy(lls_info->ap_country_str, ie_data, 3);
iface_stat->peer_info[0].capabilities |= SLSI_LLS_CAPABILITY_COUNTRY;
}
}
peer = ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET]; /* connected AP */
if (peer && peer->valid && peer->assoc_ie && peer->assoc_resp_ie) {
iface_stat->info.capabilities |= slsi_lls_ie_to_cap(peer->assoc_ie->data, peer->assoc_ie->len);
if (peer->capabilities & WLAN_CAPABILITY_PRIVACY) {
iface_stat->peer_info[0].capabilities |= SLSI_LLS_CAPABILITY_PROTECTED;
iface_stat->info.capabilities |= SLSI_LLS_CAPABILITY_PROTECTED;
}
if (peer->qos_enabled) {
iface_stat->peer_info[0].capabilities |= SLSI_LLS_CAPABILITY_QOS;
iface_stat->info.capabilities |= SLSI_LLS_CAPABILITY_QOS;
}
iface_stat->peer_info[0].capabilities |= slsi_lls_ie_to_cap(peer->assoc_resp_ie->data, peer->assoc_resp_ie->len);
SLSI_ETHER_COPY(iface_stat->peer_info[0].peer_mac_address, peer->address);
iface_stat->peer_info[0].type = peer_type;
iface_stat->num_peers = 1;
}
for (i = MAP_AID_TO_QS(SLSI_TDLS_PEER_INDEX_MIN); i <= MAP_AID_TO_QS(SLSI_TDLS_PEER_INDEX_MAX); i++) {
peer = ndev_vif->peer_sta_record[i];
if (peer && peer->valid) {
SLSI_ETHER_COPY(iface_stat->peer_info[iface_stat->num_peers].peer_mac_address, peer->address);
iface_stat->peer_info[iface_stat->num_peers].type = SLSI_LLS_PEER_TDLS;
if (peer->qos_enabled)
iface_stat->peer_info[iface_stat->num_peers].capabilities |= SLSI_LLS_CAPABILITY_QOS;
iface_stat->peer_info[iface_stat->num_peers].num_rate = 0;
iface_stat->num_peers++;
}
}
}
static void slsi_lls_iface_ap_stats(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, struct slsi_lls_iface_stat *iface_stat)
{
enum slsi_lls_peer_type peer_type = SLSI_LLS_PEER_INVALID;
struct slsi_peer *peer;
int i;
struct net_device *dev;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
/* We are AP/GO, so we advertize our own country. */
memcpy(iface_stat->info.ap_country_str, iface_stat->info.country_str, 3);
if (ndev_vif->ifnum == SLSI_NET_INDEX_WLAN) {
iface_stat->info.mode = SLSI_LLS_INTERFACE_SOFTAP;
peer_type = SLSI_LLS_PEER_STA;
} else if (ndev_vif->ifnum == SLSI_NET_INDEX_P2PX_SWLAN) {
dev = sdev->netdev[SLSI_NET_INDEX_P2PX_SWLAN];
if (SLSI_IS_VIF_INDEX_P2P_GROUP(sdev, ndev_vif)) {
iface_stat->info.mode = SLSI_LLS_INTERFACE_P2P_GO;
peer_type = SLSI_LLS_PEER_P2P_CLIENT;
}
}
for (i = MAP_AID_TO_QS(SLSI_PEER_INDEX_MIN); i <= MAP_AID_TO_QS(SLSI_PEER_INDEX_MAX); i++) {
peer = ndev_vif->peer_sta_record[i];
if (peer && peer->valid) {
SLSI_ETHER_COPY(iface_stat->peer_info[iface_stat->num_peers].peer_mac_address, peer->address);
iface_stat->peer_info[iface_stat->num_peers].type = peer_type;
iface_stat->peer_info[iface_stat->num_peers].num_rate = 0;
if (peer->qos_enabled)
iface_stat->peer_info[iface_stat->num_peers].capabilities = SLSI_LLS_CAPABILITY_QOS;
iface_stat->num_peers++;
}
}
memcpy(iface_stat->info.ssid, ndev_vif->ap.ssid, ndev_vif->ap.ssid_len);
iface_stat->info.ssid[ndev_vif->ap.ssid_len] = 0;
if (ndev_vif->ap.privacy)
iface_stat->info.capabilities |= SLSI_LLS_CAPABILITY_PROTECTED;
if (ndev_vif->ap.qos_enabled)
iface_stat->info.capabilities |= SLSI_LLS_CAPABILITY_QOS;
}
static void slsi_lls_iface_stat_fill(struct slsi_dev *sdev,
struct net_device *net_dev,
struct slsi_lls_iface_stat *iface_stat)
{
int i;
struct netdev_vif *ndev_vif;
struct slsi_mib_data mibrsp = { 0, NULL };
struct slsi_mib_value *values = NULL;
struct slsi_mib_get_entry get_values[] = {{ SLSI_PSID_UNIFI_AC_RETRIES, { SLSI_TRAFFIC_Q_BE + 1, 0 } },
{ SLSI_PSID_UNIFI_AC_RETRIES, { SLSI_TRAFFIC_Q_BK + 1, 0 } },
{ SLSI_PSID_UNIFI_AC_RETRIES, { SLSI_TRAFFIC_Q_VI + 1, 0 } },
{ SLSI_PSID_UNIFI_AC_RETRIES, { SLSI_TRAFFIC_Q_VO + 1, 0 } },
{ SLSI_PSID_UNIFI_BEACON_RECEIVED, {0, 0} },
{ SLSI_PSID_UNIFI_PS_LEAKY_AP, {0, 0} },
{ SLSI_PSID_UNIFI_RSSI, {0, 0} } };
iface_stat->iface = NULL;
iface_stat->info.mode = SLSI_LLS_INTERFACE_UNKNOWN;
SLSI_MUTEX_LOCK(sdev->device_config_mutex);
iface_stat->info.country_str[0] = sdev->device_config.domain_info.regdomain->alpha2[0];
iface_stat->info.country_str[1] = sdev->device_config.domain_info.regdomain->alpha2[1];
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
iface_stat->info.country_str[2] = ' '; /* 3rd char of our country code is ASCII<space> */
for (i = 0; i < SLSI_LLS_AC_MAX; i++)
iface_stat->ac[i].ac = SLSI_LLS_AC_MAX;
if (!net_dev)
return;
ndev_vif = netdev_priv(net_dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated)
goto exit;
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION) {
slsi_lls_iface_sta_stats(sdev, ndev_vif, iface_stat);
} else if (ndev_vif->vif_type == FAPI_VIFTYPE_AP) {
slsi_lls_iface_ap_stats(sdev, ndev_vif, iface_stat);
SLSI_ETHER_COPY(iface_stat->info.bssid, net_dev->dev_addr);
}
SLSI_ETHER_COPY(iface_stat->info.mac_addr, net_dev->dev_addr);
if ((ndev_vif->vif_type == FAPI_VIFTYPE_STATION) && (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)) {
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return;
}
mibrsp.dataLength = 10 * sizeof(get_values) / sizeof(get_values[0]);
mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL);
if (!mibrsp.data) {
SLSI_ERR(sdev, "Cannot kmalloc %d bytes for interface MIBs\n", mibrsp.dataLength);
goto exit;
}
values = slsi_read_mibs(sdev, net_dev, get_values, sizeof(get_values) / sizeof(get_values[0]), &mibrsp);
if (!values)
goto exit;
for (i = 0; i < SLSI_LLS_AC_MAX; i++) {
if (values[i].type == SLSI_MIB_TYPE_UINT) {
iface_stat->ac[i].ac = slsi_fapi_to_android_traffic_q(i);
iface_stat->ac[i].retries = values[i].u.uintValue;
iface_stat->ac[i].rx_mpdu = ndev_vif->rx_packets[i];
iface_stat->ac[i].tx_mpdu = ndev_vif->tx_packets[i];
iface_stat->ac[i].mpdu_lost = ndev_vif->tx_no_ack[i];
}
}
if (values[4].type == SLSI_MIB_TYPE_UINT)
iface_stat->beacon_rx = values[4].u.uintValue;
if (values[5].type == SLSI_MIB_TYPE_UINT) {
iface_stat->leaky_ap_detected = values[5].u.uintValue;
iface_stat->leaky_ap_guard_time = 5; /* 5 milli sec. As mentioned in lls document */
}
if (values[6].type == SLSI_MIB_TYPE_INT)
iface_stat->rssi_data = values[6].u.intValue;
exit:
kfree(values);
kfree(mibrsp.data);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
static void slsi_lls_radio_stat_fill(struct slsi_dev *sdev, struct net_device *dev,
struct slsi_lls_radio_stat *radio_stat,
int max_chan_count, int radio_index, int twoorfive)
{
struct netdev_vif *ndev_vif;
struct slsi_mib_data mibrsp = { 0, NULL };
struct slsi_mib_data supported_chan_mib = { 0, NULL };
struct slsi_mib_value *values = NULL;
struct slsi_mib_get_entry get_values[] = {{ SLSI_PSID_UNIFI_RADIO_SCAN_TIME, { radio_index, 0 } },
{ SLSI_PSID_UNIFI_RADIO_RX_TIME, { radio_index, 0 } },
{ SLSI_PSID_UNIFI_RADIO_TX_TIME, { radio_index, 0 } },
{ SLSI_PSID_UNIFI_RADIO_ON_TIME, { radio_index, 0 } },
{ SLSI_PSID_UNIFI_SUPPORTED_CHANNELS, { 0, 0 } } };
u32 *radio_data[] = {&radio_stat->on_time_scan, &radio_stat->rx_time,
&radio_stat->tx_time, &radio_stat->on_time};
int i, j, chan_count, chan_start, k;
ndev_vif = netdev_priv(dev);
radio_stat->radio = radio_index;
/* Expect each mib length in response is <= 15 So assume 15 bytes for each MIB */
mibrsp.dataLength = 15 * sizeof(get_values) / sizeof(get_values[0]);
mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL);
if (mibrsp.data == NULL) {
SLSI_ERR(sdev, "Cannot kmalloc %d bytes\n", mibrsp.dataLength);
return;
}
values = slsi_read_mibs(sdev, NULL, get_values, sizeof(get_values) / sizeof(get_values[0]), &mibrsp);
if (!values)
goto exit_with_mibrsp;
for (i = 0; i < (sizeof(get_values) / sizeof(get_values[0])) - 1; i++) {
if (values[i].type == SLSI_MIB_TYPE_UINT) {
*radio_data[i] = values[i].u.uintValue;
} else {
SLSI_ERR(sdev, "invalid type. iter:%d", i);
}
}
if (values[4].type != SLSI_MIB_TYPE_OCTET) {
SLSI_ERR(sdev, "Supported_Chan invalid type.");
goto exit_with_values;
}
supported_chan_mib = values[4].u.octetValue;
if (!supported_chan_mib.data)
goto exit_with_values;
for (j = 0; j < supported_chan_mib.dataLength / 2; j++) {
struct slsi_lls_channel_info *radio_chan;
chan_start = supported_chan_mib.data[j * 2];
chan_count = supported_chan_mib.data[j * 2 + 1];
if (radio_stat->num_channels + chan_count > max_chan_count)
chan_count = max_chan_count - radio_stat->num_channels;
if (chan_start == 1 && (twoorfive & BIT(0))) { /* for 2.4GHz */
for (k = 0; k < chan_count; k++) {
radio_chan = &radio_stat->channels[radio_stat->num_channels + k].channel;
if (k + chan_start == 14)
radio_chan->center_freq = 2484;
else
radio_chan->center_freq = 2407 + (chan_start + k) * 5;
radio_chan->width = SLSI_LLS_CHAN_WIDTH_20;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION &&
ndev_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED) {
if (ndev_vif->chan->hw_value == (chan_start + k))
radio_stat->channels[radio_stat->num_channels + k].on_time =
radio_stat->on_time;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
radio_stat->num_channels += chan_count;
} else if (chan_start != 1 && (twoorfive & BIT(1))) {
/* for 5GHz */
for (k = 0; k < chan_count; k++) {
radio_chan = &radio_stat->channels[radio_stat->num_channels + k].channel;
radio_chan->center_freq = 5000 + (chan_start + (k * 4)) * 5;
radio_chan->width = SLSI_LLS_CHAN_WIDTH_20;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION &&
ndev_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED) {
if (ndev_vif->chan->hw_value == (chan_start + (k * 4)))
radio_stat->channels[radio_stat->num_channels + k].on_time =
radio_stat->on_time;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
radio_stat->num_channels += chan_count;
}
}
exit_with_values:
kfree(values);
exit_with_mibrsp:
kfree(mibrsp.data);
}
static int slsi_lls_fill(struct slsi_dev *sdev, u8 **src_buf)
{
struct net_device *net_dev = NULL;
struct slsi_lls_radio_stat *radio_stat;
struct slsi_lls_radio_stat *radio_stat_temp;
struct slsi_lls_iface_stat *iface_stat;
int buf_len = 0;
int max_chan_count = 0;
u8 *buf;
int num_of_radios_supported;
int i = 0;
int radio_type[2] = {BIT(0), BIT(1)};
if (sdev->lls_num_radio == 0) {
SLSI_ERR(sdev, "Number of radios are zero for this platform\n");
return -EIO;
}
num_of_radios_supported = sdev->lls_num_radio;
net_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
if (sdev->wiphy->bands[NL80211_BAND_2GHZ])
max_chan_count = sdev->wiphy->bands[NL80211_BAND_2GHZ]->n_channels;
if (sdev->wiphy->bands[NL80211_BAND_5GHZ])
max_chan_count += sdev->wiphy->bands[NL80211_BAND_5GHZ]->n_channels;
buf_len = (int)((num_of_radios_supported * sizeof(struct slsi_lls_radio_stat))
+ sizeof(struct slsi_lls_iface_stat)
+ sizeof(u8)
+ (sizeof(struct slsi_lls_peer_info) * SLSI_ADHOC_PEER_CONNECTIONS_MAX)
+ (sizeof(struct slsi_lls_channel_stat) * max_chan_count));
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf) {
SLSI_ERR(sdev, "No mem. Size:%d\n", buf_len);
return -ENOMEM;
}
buf[0] = num_of_radios_supported;
*src_buf = buf;
iface_stat = (struct slsi_lls_iface_stat *)(buf + sizeof(u8));
slsi_lls_iface_stat_fill(sdev, net_dev, iface_stat);
radio_stat = (struct slsi_lls_radio_stat *)(buf + sizeof(u8) + sizeof(struct slsi_lls_iface_stat) +
(sizeof(struct slsi_lls_peer_info) * iface_stat->num_peers));
radio_stat_temp = radio_stat;
if (num_of_radios_supported == 1) {
radio_type[0] = BIT(0) | BIT(1);
slsi_lls_radio_stat_fill(sdev, net_dev, radio_stat, max_chan_count, 0, radio_type[0]);
radio_stat = (struct slsi_lls_radio_stat *)((u8 *)radio_stat + sizeof(struct slsi_lls_radio_stat) +
(sizeof(struct slsi_lls_channel_stat) * radio_stat->num_channels));
} else {
for (i = 1; i <= num_of_radios_supported ; i++) {
slsi_lls_radio_stat_fill(sdev, net_dev, radio_stat, max_chan_count, i, radio_type[i - 1]);
radio_stat = (struct slsi_lls_radio_stat *)((u8 *)radio_stat +
sizeof(struct slsi_lls_radio_stat) + (sizeof(struct slsi_lls_channel_stat)
* radio_stat->num_channels));
}
}
#ifdef CONFIG_SCSC_WLAN_DEBUG
if (slsi_dev_llslogs_supported())
slsi_lls_debug_dump_stats(sdev, radio_stat_temp, iface_stat, buf, buf_len, num_of_radios_supported);
#endif
return buf_len;
}
static int slsi_lls_get_stats(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret;
u8 *buf = NULL;
int buf_len;
if (!slsi_dev_lls_supported())
return -EOPNOTSUPP;
if (slsi_is_test_mode_enabled()) {
SLSI_WARN(sdev, "not supported in WlanLite mode\n");
return -EOPNOTSUPP;
}
if (!sdev) {
SLSI_ERR(sdev, "sdev is Null\n");
return -EINVAL;
}
/* In case of lower layer failure do not read LLS MIBs */
if (sdev->mlme_blocked)
buf_len = -EIO;
else
buf_len = slsi_lls_fill(sdev, &buf);
if (buf_len > 0) {
ret = slsi_vendor_cmd_reply(wiphy, buf, buf_len);
if (ret)
SLSI_ERR_NODEV("vendor cmd reply failed (err:%d)\n", ret);
} else {
ret = buf_len;
}
kfree(buf);
return ret;
}
static int slsi_gscan_set_oui(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
int ret = 0;
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif;
int temp;
int type;
const struct nlattr *attr;
u8 scan_oui[6];
memset(&scan_oui, 0, 6);
if (!dev) {
SLSI_ERR(sdev, "dev is NULL!!\n");
return -EINVAL;
}
ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
sdev->scan_addr_set = 0;
nla_for_each_attr(attr, data, len, temp) {
if (!attr) {
ret = -EINVAL;
break;
}
type = nla_type(attr);
switch (type) {
case SLSI_NL_ATTRIBUTE_PNO_RANDOM_MAC_OUI:
{
if (slsi_util_nla_get_data(attr, 3, &scan_oui)) {
ret = -EINVAL;
break;
}
memcpy(sdev->scan_mac_addr, scan_oui, 6);
sdev->scan_addr_set = 1;
break;
}
default:
ret = -EINVAL;
SLSI_ERR(sdev, "Invalid type : %d\n", type);
break;
}
}
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
#endif
return ret;
}
static int slsi_get_feature_set(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
u32 feature_set = 0;
int ret = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
SLSI_DBG3_NODEV(SLSI_GSCAN, "\n");
feature_set |= SLSI_WIFI_HAL_FEATURE_INFRA;
if (sdev->band_5g_supported)
feature_set |= SLSI_WIFI_HAL_FEATURE_INFRA_5G;
#ifndef CONFIG_SCSC_WLAN_STA_ONLY
feature_set |= SLSI_WIFI_HAL_FEATURE_P2P;
feature_set |= SLSI_WIFI_HAL_FEATURE_SOFT_AP;
#endif
feature_set |= SLSI_WIFI_HAL_FEATURE_RSSI_MONITOR;
feature_set |= SLSI_WIFI_HAL_FEATURE_CONTROL_ROAMING;
feature_set |= SLSI_WIFI_HAL_FEATURE_TDLS | SLSI_WIFI_HAL_FEATURE_TDLS_OFFCHANNEL;
feature_set |= SLSI_WIFI_HAL_FEATURE_HOTSPOT;
#ifndef CONFIG_SCSC_WLAN_NAT_KEEPALIVE_DISABLE
feature_set |= SLSI_WIFI_HAL_FEATURE_MKEEP_ALIVE;
#endif
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
feature_set |= SLSI_WIFI_HAL_FEATURE_LOGGER;
#endif
if (slsi_dev_gscan_supported())
feature_set |= SLSI_WIFI_HAL_FEATURE_GSCAN;
if (slsi_dev_lls_supported())
feature_set |= SLSI_WIFI_HAL_FEATURE_LINK_LAYER_STATS;
if (slsi_dev_epno_supported())
feature_set |= SLSI_WIFI_HAL_FEATURE_HAL_EPNO;
if (slsi_dev_nan_supported(SDEV_FROM_WIPHY(wiphy)))
feature_set |= SLSI_WIFI_HAL_FEATURE_NAN;
if (slsi_dev_rtt_supported()) {
feature_set |= SLSI_WIFI_HAL_FEATURE_D2D_RTT;
feature_set |= SLSI_WIFI_HAL_FEATURE_D2AP_RTT;
}
feature_set |= SLSI_WIFI_HAL_FEATURE_BATCH_SCAN;
#if !defined(SCSC_SEP_VERSION) || (defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION >= 11)
feature_set |= SLSI_WIFI_HAL_FEATURE_PNO;
#endif
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
feature_set |= SLSI_WIFI_HAL_FEATURE_AP_STA;
#endif
feature_set |= SLSI_WIFI_HAL_FEATURE_CONFIG_NDO;
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
feature_set |= SLSI_WIFI_HAL_FEATURE_SCAN_RAND;
#endif
feature_set |= SLSI_WIFI_HAL_FEATURE_LOW_LATENCY;
feature_set |= SLSI_WIFI_HAL_FEATURE_P2P_RAND_MAC;
ret = slsi_vendor_cmd_reply(wiphy, &feature_set, sizeof(feature_set));
return ret;
}
static int slsi_set_country_code(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int temp;
int type;
const struct nlattr *attr;
char country_code[SLSI_COUNTRY_CODE_LEN];
SLSI_DBG3(sdev, SLSI_GSCAN, "Received country code command\n");
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_NL_ATTRIBUTE_COUNTRY_CODE:
{
if (slsi_util_nla_get_data(attr, (SLSI_COUNTRY_CODE_LEN - 1), country_code)) {
ret = -EINVAL;
SLSI_ERR(sdev, "Insufficient Country Code Length : %d\n", nla_len(attr));
return ret;
}
break;
}
default:
ret = -EINVAL;
SLSI_ERR(sdev, "Invalid type : %d\n", type);
return ret;
}
}
ret = slsi_set_country_update_regd(sdev, country_code, SLSI_COUNTRY_CODE_LEN);
if (ret < 0)
SLSI_ERR(sdev, "Set country failed ret:%d\n", ret);
return ret;
}
static int slsi_apf_read_filter(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
int ret = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
u8 *host_dst;
int datalen;
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_APF_READ_FILTER\n");
SLSI_MUTEX_LOCK(sdev->device_config_mutex);
if (!sdev->device_config.fw_apf_supported) {
SLSI_WARN(sdev, "APF not supported by the firmware.\n");
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
return -ENOTSUPP;
}
ret = slsi_mlme_read_apf_request(sdev, dev, &host_dst, &datalen);
if (!ret)
ret = slsi_vendor_cmd_reply(wiphy, host_dst, datalen);
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
return ret;
}
static int slsi_apf_get_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
int ret = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
struct sk_buff *nl_skb;
struct nlattr *nlattr_start;
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_APF_GET_CAPABILITIES\n");
SLSI_MUTEX_LOCK(sdev->device_config_mutex);
if (!sdev->device_config.fw_apf_supported) {
SLSI_WARN(sdev, "APF not supported by the firmware.\n");
ret = -ENOTSUPP;
goto exit;
}
memset(&sdev->device_config.apf_cap, 0, sizeof(struct slsi_apf_capabilities));
ret = slsi_mib_get_apf_cap(sdev, dev);
if (ret != 0) {
SLSI_ERR(sdev, "Failed to read mib\n");
goto exit;
}
SLSI_DBG3(sdev, SLSI_GSCAN, "APF version: %d Max_Length:%d\n", sdev->device_config.apf_cap.version,
sdev->device_config.apf_cap.max_length);
nl_skb = cfg80211_vendor_cmd_alloc_reply_skb(sdev->wiphy, NLMSG_DEFAULT_SIZE);
if (!nl_skb) {
SLSI_ERR(sdev, "NO MEM for nl_skb!!!\n");
ret = -ENOMEM;
goto exit;
}
nlattr_start = nla_nest_start(nl_skb, NL80211_ATTR_VENDOR_DATA);
if (!nlattr_start) {
SLSI_ERR(sdev, "failed to put NL80211_ATTR_VENDOR_DATA\n");
/* Dont use slsi skb wrapper for this free */
kfree_skb(nl_skb);
ret = -EINVAL;
goto exit;
}
ret = nla_put_u16(nl_skb, SLSI_APF_ATTR_VERSION, sdev->device_config.apf_cap.version);
ret |= nla_put_u16(nl_skb, SLSI_APF_ATTR_MAX_LEN, sdev->device_config.apf_cap.max_length);
if (ret) {
SLSI_ERR(sdev, "Error in nla_put*:%x\n", ret);
/* Dont use slsi skb wrapper for this free */
kfree_skb(nl_skb);
goto exit;
}
ret = cfg80211_vendor_cmd_reply(nl_skb);
if (ret)
SLSI_ERR(sdev, "apf_get_capabilities cfg80211_vendor_cmd_reply failed :%d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
return ret;
}
static int slsi_apf_set_filter(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
int ret = 0;
int temp;
int type;
const struct nlattr *attr;
u32 program_len = 0;
u8 *program = NULL;
SLSI_DBG3(sdev, SLSI_GSCAN, "Received apf_set_filter command\n");
SLSI_MUTEX_LOCK(sdev->device_config_mutex);
if (!sdev->device_config.fw_apf_supported) {
SLSI_WARN(sdev, "APF not supported by the firmware.\n");
ret = -ENOTSUPP;
goto exit;
}
if (!dev) {
SLSI_ERR(sdev, "dev is NULL!!\n");
ret = -EINVAL;
goto exit;
}
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_APF_ATTR_PROGRAM_LEN:
{
if (slsi_util_nla_get_u32(attr, &program_len)) {
ret = -EINVAL;
goto exit;
}
kfree(program);
program = kmalloc(program_len, GFP_KERNEL);
if (!program) {
ret = -ENOMEM;
goto exit;
}
break;
}
case SLSI_APF_ATTR_PROGRAM:
{
if (!program) {
SLSI_ERR(sdev, "Program len is not set!\n");
ret = -EINVAL;
goto exit;
}
if (slsi_util_nla_get_data(attr, program_len, program)) {
ret = -EINVAL;
goto exit;
}
break;
}
default:
SLSI_ERR(sdev, "Invalid type : %d\n", type);
ret = -EINVAL;
goto exit;
}
}
ret = slsi_mlme_install_apf_request(sdev, dev, program, program_len);
if (ret < 0) {
SLSI_ERR(sdev, "apf_set_filter failed ret:%d\n", ret);
ret = -EINVAL;
goto exit;
}
exit:
kfree(program);
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
return ret;
}
static int slsi_rtt_get_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_rtt_capabilities rtt_cap;
int ret = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_GET_RTT_CAPABILITIES\n");
if (!slsi_dev_rtt_supported()) {
SLSI_WARN(sdev, "RTT not supported.\n");
return -ENOTSUPP;
}
memset(&rtt_cap, 0, sizeof(struct slsi_rtt_capabilities));
ret = slsi_mib_get_rtt_cap(sdev, dev, &rtt_cap);
if (ret != 0) {
SLSI_ERR(sdev, "Failed to read mib\n");
return ret;
}
ret = slsi_vendor_cmd_reply(wiphy, &rtt_cap, sizeof(struct slsi_rtt_capabilities));
if (ret)
SLSI_ERR_NODEV("rtt_get_capabilities vendor cmd reply failed (err = %d)\n", ret);
return ret;
}
static int slsi_rtt_process_target_info(const struct nlattr *iter, struct slsi_rtt_config *nl_rtt_params,
u8 *rtt_peer, int num_devices)
{
int j = 0, tmp1, tmp2;
u16 channel_freq = 0;
const struct nlattr *outer, *inner;
nla_for_each_nested(outer, iter, tmp1) {
nla_for_each_nested(inner, outer, tmp2) {
switch (nla_type(inner)) {
case SLSI_RTT_ATTRIBUTE_TARGET_MAC:
if (slsi_util_nla_get_data(inner, ETH_ALEN, nl_rtt_params[j].peer_addr))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_TYPE:
if (slsi_util_nla_get_u8(inner, &nl_rtt_params[j].rtt_type))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_PEER:
if (slsi_util_nla_get_u8(inner, rtt_peer))
return -EINVAL;
nl_rtt_params[j].rtt_peer = *rtt_peer;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_CHAN_FREQ:
if (slsi_util_nla_get_u16(inner, &channel_freq))
return -EINVAL;
nl_rtt_params[j].channel_freq = channel_freq * 2;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_PERIOD:
if (slsi_util_nla_get_u8(inner, &nl_rtt_params[j].burst_period))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_NUM_BURST:
if (slsi_util_nla_get_u8(inner, &nl_rtt_params[j].num_burst))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_NUM_FTM_BURST:
if (slsi_util_nla_get_u8(inner, &nl_rtt_params[j].num_frames_per_burst))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTMR:
if (slsi_util_nla_get_u8(inner, &nl_rtt_params[j].num_retries_per_ftmr))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_BURST_DURATION:
if (slsi_util_nla_get_u8(inner, &nl_rtt_params[j].burst_duration))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_PREAMBLE:
if (slsi_util_nla_get_u16(inner, &nl_rtt_params[j].preamble))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_BW:
if (slsi_util_nla_get_u16(inner, &nl_rtt_params[j].bw))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_LCI:
if (slsi_util_nla_get_u16(inner, &nl_rtt_params[j].LCI_request))
return -EINVAL;
break;
case SLSI_RTT_ATTRIBUTE_TARGET_LCR:
if (slsi_util_nla_get_u16(inner, &nl_rtt_params[j].LCR_request))
return -EINVAL;
break;
default:
break;
}
}
j++;
if (j > num_devices)
return 0;
}
return 0;
}
static int slsi_rtt_set_config(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
int r = -EINVAL, type, rtt_id, j = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif;
struct net_device *dev = wdev->netdev;
struct slsi_rtt_config *nl_rtt_params;
struct slsi_rtt_id_params *rtt_id_params = NULL;
const struct nlattr *iter;
u8 source_addr[ETH_ALEN];
int tmp;
u16 request_id = 0;
u8 num_devices = 0;
u8 rtt_peer = SLSI_RTT_PEER_AP;
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_RTT_RANGE_START\n");
if (!slsi_dev_rtt_supported()) {
SLSI_ERR(sdev, "RTT not supported.\n");
return WIFI_HAL_ERROR_NOT_SUPPORTED;
}
if (!dev) {
SLSI_ERR(sdev, "dev is NULL!!\n");
return r;
}
ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
nla_for_each_attr(iter, data, len, tmp) {
type = nla_type(iter);
switch (type) {
case SLSI_RTT_ATTRIBUTE_TARGET_CNT:
if (slsi_util_nla_get_u8(iter, &num_devices))
goto exit_with_mutex;
SLSI_DBG1_NODEV(SLSI_GSCAN, "Target cnt %d\n", num_devices);
break;
case SLSI_RTT_ATTRIBUTE_TARGET_ID:
if (slsi_util_nla_get_u16(iter, &request_id))
goto exit_with_mutex;
SLSI_DBG1_NODEV(SLSI_GSCAN, "Request ID: %d\n", request_id);
break;
default:
break;
}
}
if (!num_devices) {
SLSI_ERR_NODEV("No device found for rtt configuration!\n");
goto exit_with_mutex;
}
/* Allocate memory for the received config params */
nl_rtt_params = kcalloc(num_devices, sizeof(*nl_rtt_params), GFP_KERNEL);
if (!nl_rtt_params) {
SLSI_ERR_NODEV("Failed to allocate memory for config rtt_param\n");
r = -ENOMEM;
goto exit_with_mutex;
}
nla_for_each_attr(iter, data, len, tmp) {
type = nla_type(iter);
switch (type) {
case SLSI_RTT_ATTRIBUTE_TARGET_INFO:
r = slsi_rtt_process_target_info(iter, nl_rtt_params, &rtt_peer, num_devices);
if (r)
goto exit_with_nl_rtt_params;
break;
default:
break;
}
}
SLSI_ETHER_COPY(source_addr, dev->dev_addr);
/* Check for the first available rtt_id and allocate memory for rtt_id_aprams. */
for (rtt_id = SLSI_MIN_RTT_ID; rtt_id <= SLSI_MAX_RTT_ID; rtt_id++) {
if (!sdev->rtt_id_params[rtt_id - 1]) {
rtt_id_params = kzalloc(sizeof(struct slsi_rtt_id_params) + ETH_ALEN * num_devices, GFP_KERNEL);
if (!rtt_id_params) {
SLSI_INFO(sdev, "Failed to allocate memory for rtt_id_params.\n");
r = -ENOMEM;
goto exit_with_nl_rtt_params;
}
sdev->rtt_id_params[rtt_id - 1] = rtt_id_params;
break;
}
}
if (rtt_id > SLSI_MAX_RTT_ID) {
SLSI_ERR_NODEV("RTT_ID(1-7) is in use currently!!\n");
goto exit_with_nl_rtt_params;
}
rtt_id_params->fapi_req_id = rtt_id;
rtt_id_params->hal_request_id = request_id;
rtt_id_params->peer_count = num_devices;
rtt_id_params->peer_type = rtt_peer;
/*Store mac addr list corresponding to each rtt_id. */
for (j = 0; j < num_devices; j++)
SLSI_ETHER_COPY(&rtt_id_params->peers[j * ETH_ALEN], nl_rtt_params[j].peer_addr);
if (rtt_peer == SLSI_RTT_PEER_NAN) {
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
r = slsi_send_nan_range_config(sdev, num_devices, nl_rtt_params, rtt_id);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
#else
SLSI_ERR_NODEV("NAN not enabled\n");
r = -ENOTSUPP;
#endif
} else {
r = slsi_mlme_add_range_req(sdev, dev, num_devices, nl_rtt_params, rtt_id, source_addr);
if (r) {
kfree(rtt_id_params);
sdev->rtt_id_params[rtt_id - 1] = NULL;
SLSI_ERR_NODEV("Failed to set rtt config\n");
}
}
exit_with_nl_rtt_params:
kfree(nl_rtt_params);
exit_with_mutex:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_tx_rate_calc(struct sk_buff *nl_skb, u16 fw_rate, int res, bool tx_rate)
{
u8 preamble;
const u32 fw_rate_idx_to_80211_rate[] = { 0, 10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 };
u32 data_rate = 0;
u32 mcs = 0, nss = 0;
u32 chan_bw_idx = 0;
int gi_idx;
preamble = (fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) >> 14;
if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_NON_HT_SELECTED) {
u16 fw_rate_idx = fw_rate & SLSI_FW_API_RATE_INDEX_FIELD;
if (fw_rate > 0 && fw_rate_idx < ARRAY_SIZE(fw_rate_idx_to_80211_rate))
data_rate = fw_rate_idx_to_80211_rate[fw_rate_idx];
} else if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_HT_SELECTED) {
nss = (SLSI_FW_API_RATE_HT_NSS_FIELD & fw_rate) >> 6;
chan_bw_idx = (fw_rate & SLSI_FW_API_RATE_BW_FIELD) >> 9;
gi_idx = ((fw_rate & SLSI_FW_API_RATE_SGI) == SLSI_FW_API_RATE_SGI) ? 1 : 0;
mcs = SLSI_FW_API_RATE_HT_MCS_FIELD & fw_rate;
if (chan_bw_idx < 2 && mcs <= 7) {
data_rate = (nss + 1) * slsi_rates_table[chan_bw_idx][gi_idx][mcs];
} else if (mcs == 32 && chan_bw_idx == 1) {
if (gi_idx == 1)
data_rate = (nss + 1) * 67;
else
data_rate = (nss + 1) * 60;
} else {
SLSI_WARN_NODEV("FW DATA RATE decode error fw_rate:%x, bw:%x, mcs_idx:%x, nss : %d\n",
fw_rate, chan_bw_idx, mcs, nss);
}
} else if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_VHT_SELECTED) {
/* report vht rate in legacy units and not as mcs index. reason: upper layers may still be not
* updated with vht msc table.
*/
chan_bw_idx = (fw_rate & SLSI_FW_API_RATE_BW_FIELD) >> 9;
gi_idx = ((fw_rate & SLSI_FW_API_RATE_SGI) == SLSI_FW_API_RATE_SGI) ? 1 : 0;
/* Calculate NSS --> bits 6 to 4*/
nss = (SLSI_FW_API_RATE_VHT_NSS_FIELD & fw_rate) >> 4;
mcs = SLSI_FW_API_RATE_VHT_MCS_FIELD & fw_rate;
/* Bandwidth (BW): 0x0= 20 MHz, 0x1= 40 MHz, 0x2= 80 MHz, 0x3= 160/ 80+80 MHz. 0x3 is not supported */
if (chan_bw_idx <= 2 && mcs <= 9)
data_rate = (nss + 1) * slsi_rates_table[chan_bw_idx][gi_idx][mcs];
else
SLSI_WARN_NODEV("FW DATA RATE decode error fw_rate:%x, bw:%x, mcs_idx:%x,nss : %d\n",
fw_rate, chan_bw_idx, mcs, nss);
if (nss > 1)
nss += 1;
}
if (tx_rate) {
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_TX_PREAMBLE, preamble);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_TX_NSS, nss);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_TX_BW, chan_bw_idx);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_TX_MCS, mcs);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_TX_RATE, data_rate);
} else {
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_RX_PREAMBLE, preamble);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_RX_NSS, nss);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_RX_BW, chan_bw_idx);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_RX_MCS, mcs);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_RX_RATE, data_rate);
}
return res;
}
void slsi_rx_range_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
u32 i, tm;
u16 rtt_entry_count = fapi_get_u16(skb, u.mlme_range_ind.entries);
u16 rtt_id = fapi_get_u16(skb, u.mlme_range_ind.rtt_id);
u16 request_id = sdev->rtt_id_params[rtt_id - 1]->hal_request_id;
u32 tmac = fapi_get_u32(skb, u.mlme_range_ind.spare_3);
int data_len = fapi_get_datalen(skb);
u8 *ip_ptr, *start_ptr;
u16 tx_data = 0, rx_data = 0;
struct sk_buff *nl_skb;
int res = 0;
struct nlattr *nlattr_nested;
struct timespec ts;
u64 tkernel;
u8 rep_cnt = 0;
__le16 *le16_ptr = NULL;
__le32 *le32_ptr = NULL;
u16 value;
u32 temp_value;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
nl_skb = cfg80211_vendor_event_alloc(sdev->wiphy, NULL, NLMSG_DEFAULT_SIZE,
SLSI_NL80211_RTT_RESULT_EVENT, GFP_KERNEL);
#else
nl_skb = cfg80211_vendor_event_alloc(sdev->wiphy, NLMSG_DEFAULT_SIZE, SLSI_NL80211_RTT_RESULT_EVENT,
GFP_KERNEL);
#endif
#ifdef CONFIG_SCSC_WLAN_DEBUG
SLSI_DBG1_NODEV(SLSI_GSCAN, "Event: %s(%d)\n",
slsi_print_event_name(SLSI_NL80211_RTT_RESULT_EVENT), SLSI_NL80211_RTT_RESULT_EVENT);
#endif
if (!nl_skb) {
SLSI_ERR(sdev, "NO MEM for nl_skb!!!\n");
goto exit;
}
ip_ptr = fapi_get_data(skb);
start_ptr = fapi_get_data(skb);
res |= nla_put_u16(nl_skb, SLSI_RTT_ATTRIBUTE_RESULT_CNT, rtt_entry_count);
res |= nla_put_u16(nl_skb, SLSI_RTT_ATTRIBUTE_TARGET_ID, request_id);
res |= nla_put_u8(nl_skb, SLSI_RTT_ATTRIBUTE_RESULTS_PER_TARGET, 1);
for (i = 0; i < rtt_entry_count; i++) {
nlattr_nested = nla_nest_start(nl_skb, SLSI_RTT_ATTRIBUTE_RESULT);
if (!nlattr_nested) {
SLSI_ERR(sdev, "Error in nla_nest_start\n");
/* Dont use slsi skb wrapper for this free */
kfree_skb(nl_skb);
goto exit;
}
ip_ptr += 7; /*skip first 7 bytes for fapi_ie_generic */
res |= nla_put(nl_skb, SLSI_RTT_EVENT_ATTR_ADDR, ETH_ALEN, ip_ptr);
ip_ptr += 6;
le16_ptr = (__le16 *)&ip_ptr[i];
value = le16_to_cpu(*le16_ptr);
res |= nla_put_u16(nl_skb, SLSI_RTT_EVENT_ATTR_BURST_NUM, value);
ip_ptr += 2;
res |= nla_put_u8(nl_skb, SLSI_RTT_EVENT_ATTR_MEASUREMENT_NUM, *ip_ptr++);
res |= nla_put_u8(nl_skb, SLSI_RTT_EVENT_ATTR_SUCCESS_NUM, *ip_ptr++);
res |= nla_put_u8(nl_skb, SLSI_RTT_EVENT_ATTR_NUM_PER_BURST_PEER, *ip_ptr++);
le16_ptr = (__le16 *)&ip_ptr[i];
value = le16_to_cpu(*le16_ptr);
res |= nla_put_u16(nl_skb, SLSI_RTT_EVENT_ATTR_STATUS, value);
ip_ptr += 2;
res |= nla_put_u8(nl_skb, SLSI_RTT_EVENT_ATTR_RETRY_AFTER_DURATION, *ip_ptr++);
res |= nla_put_u8(nl_skb, SLSI_RTT_EVENT_ATTR_TYPE, *ip_ptr++);
le16_ptr = (__le16 *)&ip_ptr[i];
value = le16_to_cpu(*le16_ptr);
res |= nla_put_u16(nl_skb, SLSI_RTT_EVENT_ATTR_RSSI, value);
ip_ptr += 2;
le16_ptr = (__le16 *)&ip_ptr[i];
value = le16_to_cpu(*le16_ptr);
res |= nla_put_u16(nl_skb, SLSI_RTT_EVENT_ATTR_RSSI_SPREAD, value);
ip_ptr += 2;
memcpy(&tx_data, ip_ptr, 2);
res = slsi_tx_rate_calc(nl_skb, tx_data, res, 1);
ip_ptr += 2;
memcpy(&rx_data, ip_ptr, 2);
res = slsi_tx_rate_calc(nl_skb, rx_data, res, 0);
ip_ptr += 2;
le32_ptr = (__le32 *)&ip_ptr[i];
temp_value = le32_to_cpu(*le32_ptr);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_RTT, temp_value);
ip_ptr += 4;
le16_ptr = (__le16 *)&ip_ptr[i];
value = le16_to_cpu(*le16_ptr);
res |= nla_put_u16(nl_skb, SLSI_RTT_EVENT_ATTR_RTT_SD, value);
ip_ptr += 2;
le16_ptr = (__le16 *)&ip_ptr[i];
value = le16_to_cpu(*le16_ptr);
res |= nla_put_u16(nl_skb, SLSI_RTT_EVENT_ATTR_RTT_SPREAD, value);
ip_ptr += 2;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
ts = ktime_to_timespec(ktime_get_boottime());
#else
get_monotonic_boottime(&ts);
#endif
tkernel = (u64)TIMESPEC_TO_US(ts);
le32_ptr = (__le32 *)&ip_ptr[i];
temp_value = le32_to_cpu(*le32_ptr);
tm = temp_value;
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_TIMESTAMP_US, tkernel - (tmac - tm));
ip_ptr += 4;
le32_ptr = (__le32 *)&ip_ptr[i];
temp_value = le32_to_cpu(*le32_ptr);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_DISTANCE_MM, temp_value);
ip_ptr += 4;
le32_ptr = (__le32 *)&ip_ptr[i];
temp_value = le32_to_cpu(*le32_ptr);
res |= nla_put_u32(nl_skb, SLSI_RTT_EVENT_ATTR_DISTANCE_SD_MM, temp_value);
ip_ptr += 4;
res |= nla_put_u8(nl_skb, SLSI_RTT_EVENT_ATTR_BURST_DURATION_MSN, *ip_ptr++);
res |= nla_put_u8(nl_skb, SLSI_RTT_EVENT_ATTR_NEGOTIATED_BURST_NUM, *ip_ptr++);
for (rep_cnt = 0; rep_cnt < 2; rep_cnt++) {
if (ip_ptr - start_ptr < data_len && ip_ptr[0] == WLAN_EID_MEASURE_REPORT) {
if (ip_ptr[4] == 8) /*LCI Element*/
res |= nla_put(nl_skb, SLSI_RTT_EVENT_ATTR_LCI,
ip_ptr[1] + 2, ip_ptr);
else if (ip_ptr[4] == 11) /*LCR element */
res |= nla_put(nl_skb, SLSI_RTT_EVENT_ATTR_LCR,
ip_ptr[1] + 2, ip_ptr);
ip_ptr += ip_ptr[1] + 2;
}
}
nla_nest_end(nl_skb, nlattr_nested);
}
SLSI_DBG_HEX(sdev, SLSI_GSCAN, fapi_get_data(skb), fapi_get_datalen(skb), "range indication skb buffer:\n");
if (res) {
SLSI_ERR(sdev, "Error in nla_put*:%x\n", res);
kfree_skb(nl_skb);
goto exit;
}
cfg80211_vendor_event(nl_skb, GFP_KERNEL);
exit:
kfree_skb(skb);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
void slsi_rx_range_done_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
u16 rtt_id = fapi_get_u16(skb, u.mlme_range_ind.rtt_id);
u16 request_id = sdev->rtt_id_params[rtt_id - 1]->hal_request_id;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
#ifdef CONFIG_SCSC_WLAN_DEBUG
SLSI_DBG1_NODEV(SLSI_GSCAN, "Event: %s(%d)\n",
slsi_print_event_name(SLSI_NL80211_RTT_COMPLETE_EVENT), SLSI_NL80211_RTT_COMPLETE_EVENT);
#endif
slsi_vendor_event(sdev, SLSI_NL80211_RTT_COMPLETE_EVENT, &request_id, sizeof(request_id));
kfree(sdev->rtt_id_params[rtt_id - 1]);
sdev->rtt_id_params[rtt_id - 1] = NULL;
kfree_skb(skb);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
/* Function to remove peer from structure rtt_id_params corresponding to rtt_id_idx. */
void slsi_rtt_remove_peer(struct slsi_dev *sdev, u8 *addr, u8 rtt_id_idx, u8 count_addr)
{
int i = 0, j;
u8 zero_addr[ETH_ALEN];
u8 remove_id = 1;
memset(zero_addr, 0, ETH_ALEN);
/* Check for each peer if it's exists in the count_addr list if so then set it to zero */
for (i = 0; i < sdev->rtt_id_params[rtt_id_idx]->peer_count; i++) {
for (j = 0; j < count_addr; j++)
if (SLSI_ETHER_EQUAL(&sdev->rtt_id_params[rtt_id_idx]->peers[i * ETH_ALEN],
&addr[j * ETH_ALEN]))
SLSI_ETHER_COPY(&sdev->rtt_id_params[rtt_id_idx]->peers[i * ETH_ALEN], zero_addr);
/* If peer doesn't exist in addr list then no need to remove this rtt id from rtt_id_params. */
if (!SLSI_ETHER_EQUAL(&sdev->rtt_id_params[rtt_id_idx]->peers[i * ETH_ALEN], zero_addr))
remove_id = 0;
}
/* If all the peer addresses are set to zero then remove rtt id and make it available for use */
if (remove_id) {
SLSI_INFO(sdev, "Remove rtt id:%d\n", rtt_id_idx + 1);
kfree(sdev->rtt_id_params[rtt_id_idx]);
sdev->rtt_id_params[rtt_id_idx] = NULL;
}
}
static int slsi_rtt_cancel_config(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
int temp, r = 1, j = 0, type, count = 0, i = 0, k = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif;
u8 *addr;
/* List to store requested addresses corresponding to each rtt id which needs to be cancelled. */
u8 *cancel_addr_list;
const struct nlattr *iter;
u16 num_devices = 0;
u8 count_addr = 0, peer_count = 0;
SLSI_DBG1_NODEV(SLSI_GSCAN, "RTT_SUBCMD_CANCEL_CONFIG\n");
if (!slsi_dev_rtt_supported()) {
SLSI_WARN(sdev, "RTT not supported.\n");
return -ENOTSUPP;
}
if (!dev) {
SLSI_ERR(sdev, "dev is NULL!!\n");
return -EINVAL;
}
ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
nla_for_each_attr(iter, data, len, temp) {
type = nla_type(iter);
switch (type) {
case SLSI_RTT_ATTRIBUTE_TARGET_CNT:
if (slsi_util_nla_get_u16(iter, &num_devices)) {
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -EINVAL;
}
SLSI_DBG1_NODEV(SLSI_GSCAN, "Target cnt %d\n", num_devices);
break;
default:
SLSI_ERR_NODEV("No ATTRIBUTE_Target cnt - %d\n", type);
break;
}
}
if (!num_devices) {
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
/* Allocate memory for the received mac addresses */
addr = kzalloc(ETH_ALEN * num_devices, GFP_KERNEL);
if (!addr) {
SLSI_ERR_NODEV("Failed to allocate memory for mac addresses\n");
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -ENOMEM;
}
nla_for_each_attr(iter, data, len, temp) {
type = nla_type(iter);
if (type == SLSI_RTT_ATTRIBUTE_TARGET_MAC) {
if (count >= num_devices)
break;
if (slsi_util_nla_get_data(iter, ETH_ALEN, &addr[j]))
continue;
j = j + ETH_ALEN;
count++;
} else {
SLSI_ERR_NODEV("No ATTRIBUTE_MAC - %d\n", type);
}
}
cancel_addr_list = kzalloc(ETH_ALEN * num_devices, GFP_KERNEL);
if (!cancel_addr_list) {
SLSI_INFO(sdev, "Failed to allocate memory for cancel_addr_list.\n");
kfree(addr);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -ENOMEM;
}
/* Iterate over each rtt_id and check if the requested address present in the current peer list. */
for (i = 0; i < SLSI_MAX_RTT_ID; i++) {
count_addr = 0;
if (!sdev->rtt_id_params[i])
continue;
for (j = 0; j < num_devices; j++) {
peer_count = sdev->rtt_id_params[i]->peer_count;
for (k = 0; k < peer_count; k++)
if (SLSI_ETHER_EQUAL(&sdev->rtt_id_params[i]->peers[k * ETH_ALEN],
&addr[j * ETH_ALEN])) {
SLSI_ETHER_COPY(&cancel_addr_list[count_addr * ETH_ALEN], &addr[j * ETH_ALEN]);
count_addr++;
break;
}
}
if (!count_addr)
continue;
if (sdev->rtt_id_params[i]->peer_type == SLSI_RTT_PEER_NAN) {
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
r = slsi_send_nan_range_cancel(sdev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
#else
SLSI_ERR_NODEV("NAN not enabled\n");
r = -ENOTSUPP;
#endif
} else {
r = slsi_mlme_del_range_req(sdev, dev, count_addr, cancel_addr_list, i + 1);
}
if (r)
SLSI_ERR_NODEV("Failed to cancel rtt config for id:%d\n", i + 1);
slsi_rtt_remove_peer(sdev, cancel_addr_list, i, count_addr);
memset(cancel_addr_list, 0, ETH_ALEN * num_devices);
}
kfree(addr);
kfree(cancel_addr_list);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
static int slsi_configure_nd_offload(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif;
int ret = 0;
int temp;
int type;
const struct nlattr *attr;
u8 nd_offload_enabled = 0;
SLSI_DBG3(sdev, SLSI_GSCAN, "Received nd_offload command\n");
if (!dev) {
SLSI_ERR(sdev, "dev is NULL!!\n");
return -EINVAL;
}
ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated || (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) ||
(ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)) {
SLSI_DBG3(sdev, SLSI_GSCAN, "vif error\n");
ret = -EPERM;
goto exit;
}
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_NL_ATTRIBUTE_ND_OFFLOAD_VALUE:
{
if (slsi_util_nla_get_u8(attr, &nd_offload_enabled)) {
ret = -EINVAL;
goto exit;
}
break;
}
default:
SLSI_ERR(sdev, "Invalid type : %d\n", type);
ret = -EINVAL;
goto exit;
}
}
ndev_vif->sta.nd_offload_enabled = nd_offload_enabled;
ret = slsi_mlme_set_ipv6_address(sdev, dev);
if (ret < 0) {
SLSI_ERR(sdev, "Configure nd_offload failed ret:%d nd_offload_enabled: %d\n", ret, nd_offload_enabled);
ret = -EINVAL;
goto exit;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return ret;
}
static int slsi_get_roaming_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif;
int ret = 0;
struct slsi_mib_value *values = NULL;
struct slsi_mib_data mibrsp = { 0, NULL };
struct slsi_mib_get_entry get_values[] = {{ SLSI_PSID_UNIFI_ROAM_BLACKLIST_SIZE, { 0, 0 } } };
u32 max_blacklist_size = 0;
u32 max_whitelist_size = 0;
struct sk_buff *nl_skb;
struct nlattr *nlattr_start;
if (!dev) {
SLSI_ERR(sdev, "dev is NULL!!\n");
return -EINVAL;
}
ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
mibrsp.dataLength = 10;
mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL);
if (!mibrsp.data) {
SLSI_ERR(sdev, "Cannot kmalloc %d bytes\n", mibrsp.dataLength);
ret = -ENOMEM;
goto exit;
}
values = slsi_read_mibs(sdev, NULL, get_values, ARRAY_SIZE(get_values), &mibrsp);
if (values && (values[0].type == SLSI_MIB_TYPE_UINT || values[0].type == SLSI_MIB_TYPE_INT))
max_blacklist_size = values[0].u.uintValue;
nl_skb = cfg80211_vendor_cmd_alloc_reply_skb(sdev->wiphy, NLMSG_DEFAULT_SIZE);
if (!nl_skb) {
SLSI_ERR(sdev, "NO MEM for nl_skb!!!\n");
ret = -ENOMEM;
goto exit_with_mib_resp;
}
nlattr_start = nla_nest_start(nl_skb, NL80211_ATTR_VENDOR_DATA);
if (!nlattr_start) {
SLSI_ERR(sdev, "failed to put NL80211_ATTR_VENDOR_DATA\n");
/* Dont use slsi skb wrapper for this free */
kfree_skb(nl_skb);
ret = -EINVAL;
goto exit_with_mib_resp;
}
ret = nla_put_u32(nl_skb, SLSI_NL_ATTR_MAX_BLACKLIST_SIZE, max_blacklist_size);
ret |= nla_put_u32(nl_skb, SLSI_NL_ATTR_MAX_WHITELIST_SIZE, max_whitelist_size);
if (ret) {
SLSI_ERR(sdev, "Error in nla_put*:%x\n", ret);
/* Dont use slsi skb wrapper for this free */
kfree_skb(nl_skb);
goto exit_with_mib_resp;
}
ret = cfg80211_vendor_cmd_reply(nl_skb);
if (ret)
SLSI_ERR(sdev, "cfg80211_vendor_cmd_reply failed :%d\n", ret);
exit_with_mib_resp:
kfree(mibrsp.data);
kfree(values);
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return ret;
}
static int slsi_set_roaming_state(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
int temp = 0;
int type = 0;
const struct nlattr *attr;
int ret = 0;
int roam_state = 0;
u8 val = 0;
if (!dev) {
SLSI_WARN_NODEV("net_dev is NULL\n");
return -EINVAL;
}
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_NL_ATTR_ROAM_STATE:
if (slsi_util_nla_get_u8(attr, &val)) {
ret = -EINVAL;
goto exit;
}
roam_state = (int)val;
break;
default:
SLSI_ERR_NODEV("Unknown attribute: %d\n", type);
ret = -EINVAL;
goto exit;
}
}
SLSI_DBG1_NODEV(SLSI_GSCAN, "SUBCMD_SET_ROAMING_STATE roam_state = %d\n", roam_state);
ret = slsi_set_mib_roam(sdev, NULL, SLSI_PSID_UNIFI_ROAMING_ACTIVATED, roam_state);
if (ret < 0)
SLSI_ERR_NODEV("Failed to set roaming state\n");
exit:
return ret;
}
char *slsi_get_roam_reason_str(int roam_reason)
{
switch (roam_reason) {
case SLSI_WIFI_ROAMING_SEARCH_REASON_RESERVED:
return "WIFI_ROAMING_SEARCH_REASON_RESERVED";
case SLSI_WIFI_ROAMING_SEARCH_REASON_LOW_RSSI:
return "WIFI_ROAMING_SEARCH_REASON_LOW_RSSI";
case SLSI_WIFI_ROAMING_SEARCH_REASON_LINK_LOSS:
return "WIFI_ROAMING_SEARCH_REASON_LINK_LOSS";
case SLSI_WIFI_ROAMING_SEARCH_REASON_BTM_REQ:
return "WIFI_ROAMING_SEARCH_REASON_BTM_REQ";
case SLSI_WIFI_ROAMING_SEARCH_REASON_CU_TRIGGER:
return "WIFI_ROAMING_SEARCH_REASON_CU_TRIGGER";
case SLSI_WIFI_ROAMING_SEARCH_REASON_EMERGENCY:
return "WIFI_ROAMING_SEARCH_REASON_EMERGENCY";
case SLSI_WIFI_ROAMING_SEARCH_REASON_IDLE:
return "WIFI_ROAMING_SEARCH_REASON_IDLE";
case SLSI_WIFI_ROAMING_SEARCH_REASON_SCAN_TIMER1_EXPIRY:
return "WIFI_ROAMING_SEARCH_REASON_SCAN_TIMER1_EXPIRY";
case SLSI_WIFI_ROAMING_SEARCH_REASON_SCAN_TIMER2_EXPIRY:
return "WIFI_ROAMING_SEARCH_REASON_SCAN_TIMER2_EXPIRY";
case SLSI_WIFI_ROAMING_SEARCH_REASON_INACTIVE_TIMER_EXPIRY:
return "WIFI_ROAMING_SEARCH_REASON_INACTIVE_TIMER_EXPIRY";
default:
return "UNKNOWN_REASON";
}
}
char *slsi_get_nan_role_str(int nan_role)
{
switch (nan_role) {
case 0:
return "Not Set";
case 1:
return "Anchor Master";
case 2:
return "Master";
case 3:
return "Sync";
case 4:
return "Non Sync";
default:
return "Undefined";
}
}
char *slsi_frame_transmit_failure_message_type(int message_type)
{
switch (message_type) {
case 0x0001:
return "eap_message";
case 0x0002:
return "eapol_key_m123";
case 0x0003:
return "eapol_key_m4";
case 0x0004:
return "arp";
case 0x0005:
return "dhcp";
case 0x0006:
return "neighbor_discovery";
case 0x0007:
return "wai_message";
case 0x0008:
return "any_other";
default:
return "Undefined";
}
}
char *slsi_get_scan_type(int scan_type)
{
switch (scan_type) {
case FAPI_SCANTYPE_SOFT_CACHED_ROAMING_SCAN:
return "Soft Cached scan";
case FAPI_SCANTYPE_SOFT_FULL_ROAMING_SCAN:
return "Soft Full scan";
case FAPI_SCANTYPE_HARD_CACHED_ROAMING_SCAN:
return "Hard Cached scan";
case FAPI_SCANTYPE_HARD_FULL_ROAMING_SCAN:
return "Hard Full scan";
default:
return "Undefined";
}
}
char *slsi_get_measure_mode(int measure_mode)
{
switch (measure_mode) {
case 0:
return "Passive Mode";
case 1:
return "Active Mode";
case 2:
return "Table Mode";
default:
return "Undefined";
}
}
char *slsi_get_nan_schedule_type_str(int schedule_type)
{
switch (schedule_type) {
case 1:
return "NAN_FWC";
case 2:
return "NAN_NDC";
case 3:
return "NAN_DW";
default:
return "Undefind";
}
}
char *slsi_get_nan_ulw_reason_str(int ulw_reason)
{
switch (ulw_reason) {
case 1:
return "Peer Requested";
case 2:
return "Concurrent Operation";
case 3:
return "Scan";
case 4:
return "BT_COEX";
case 5:
return "Power Saving";
case 6:
return "Deleted";
default:
return "Undefined";
}
}
void slsi_handle_nan_rx_event_log_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
u16 event_id = 0;
u64 timestamp = 0;
u8 *tlv_data;
int tlv_buffer__len = fapi_get_datalen(skb), i = 0;
u16 vendor_len, tag_id, tag_len, vtag_id;
u32 tag_value, vtag_value;
bool multi_param = false, param_set_available = false;
u32 nan_role = 0, nan_amr = 0, hop_count = 0, master_tsf = 0, channel = 0;
u32 schedule_type = 0, start_offset = 0, slot_duration = 0, slot_bitmap = 0;
u32 ulw_reason = 0, ulw_index = 0, ulw_start = 0, ulw_period = 0, ulw_duration = 0, ulw_count = 0;
u32 tx_mpdu_total = 0, rx_mpdu_total = 0, slot_avg_rx = 0, slot_avg_tx = 0;
u8 nan_cluster_id[6] = {0}, nan_nmi[6] = {0}, width = 0, position = 0;
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
struct netdev_vif *ndev_vif = netdev_priv(dev);
#endif
event_id = fapi_get_s16(skb, u.mlme_event_log_ind.event);
timestamp = fapi_get_u64(skb, u.mlme_event_log_ind.timestamp);
tlv_data = fapi_get_data(skb);
while (i + 4 < tlv_buffer__len) {
tag_id = le16_to_cpu(*((__le16 *)&tlv_data[i]));
tag_len = le16_to_cpu(*((__le16 *)&tlv_data[i + 2]));
i += 4;
if (i + tag_len > tlv_buffer__len) {
SLSI_INFO(sdev, "Incorrect fapi bulk data\n");
return;
}
tag_value = slsi_convert_tlv_data_to_value(&tlv_data[i], tag_len);
multi_param = false;
switch (tag_id) {
case SLSI_WIFI_TAG_CHANNEL:
channel = tag_value / 2;
break;
case SLSI_WIFI_TAG_VENDOR_SPECIFIC:
vendor_len = tag_len - 2;
vtag_id = le16_to_cpu(*((__le16 *)&tlv_data[i]));
vtag_value = slsi_convert_tlv_data_to_value(&tlv_data[i + 2], vendor_len);
switch (vtag_id) {
case SLSI_WIFI_TAG_VD_CLUSTER_ID:
if (vendor_len != ETH_ALEN) {
memset(nan_cluster_id, 0, ETH_ALEN);
SLSI_ERR(sdev, "Cluser ID should be of 6 bytes,bytes received:%d\n", vendor_len);
break;
}
memcpy(nan_cluster_id, &tlv_data[i], vendor_len);
break;
case SLSI_WIFI_TAG_VD_NAN_ROLE:
nan_role = vtag_value;
break;
case SLSI_WIFI_TAG_VD_NAN_AMR:
nan_amr = vtag_value;
break;
case SLSI_WIFI_TAG_VD_NAN_NMI:
if (vendor_len != ETH_ALEN) {
memset(nan_nmi, 0, ETH_ALEN);
SLSI_ERR(sdev, "NAN NMI should be of 6 bytes,bytes received:%d\n", vendor_len);
break;
}
memcpy(nan_nmi, &tlv_data[i], vendor_len);
break;
case SLSI_WIFI_TAG_VD_NAN_HOP_COUNT:
hop_count = vtag_value;
break;
case SLSI_WIFI_TAG_VD_MASTER_TSF:
master_tsf = vtag_value;
break;
case SLSI_WIFI_TAG_VD_CHANNEL_INFO:
if (vendor_len < 2) {
SLSI_ERR(sdev, "Channel_info should be atleast 2 bytes!\n");
break;
}
width = tlv_data[i + 2];
position = tlv_data[i + 3];
break;
case SLSI_WIFI_TAG_VD_SCHEDULE_TYPE:
schedule_type = vtag_value;
break;
case SLSI_WIFI_TAG_VD_START_OFFSET:
start_offset = vtag_value;
break;
case SLSI_WIFI_TAG_VD_SLOT_DURATION:
slot_duration = vtag_value;
break;
case SLSI_WIFI_TAG_VD_BITMAP:
slot_bitmap = vtag_value;
break;
case SLSI_WIFI_TAG_VD_ULW_REASON:
ulw_reason = vtag_value;
break;
case SLSI_WIFI_TAG_VD_ULW_INDEX:
ulw_index = vtag_value;
break;
case SLSI_WIFI_TAG_VD_ULW_START_TIME:
ulw_start = vtag_value;
break;
case SLSI_WIFI_TAG_VD_ULW_PERIOD:
ulw_period = vtag_value;
break;
case SLSI_WIFI_TAG_VD_ULW_DURATION:
ulw_duration = vtag_value;
break;
case SLSI_WIFI_TAG_VD_ULW_COUNT:
ulw_count = vtag_value;
break;
case SLSI_WIFI_TAG_VD_NAN_RX_TOTAL:
rx_mpdu_total = vtag_value;
break;
case SLSI_WIFI_TAG_VD_NAN_TX_TOTAL:
tx_mpdu_total = vtag_value;
break;
case SLSI_WIFI_TAG_VD_NAN_RX_AVERAGE:
slot_avg_rx = vtag_value;
break;
case SLSI_WIFI_TAG_VD_NAN_TX_AVERAGE:
slot_avg_tx = vtag_value;
break;
case SLSI_WIFI_TAG_VD_PARAMETER_SET:
multi_param = true;
if (!param_set_available) {
param_set_available = true;
break;
}
switch (event_id) {
case WIFI_EVENT_NAN_AVAILABILITY_UPDATE:
SLSI_INFO(sdev, "WIFI_EVENT_NAN_AVAILABILITY_UPDATE, Master_TSF: %d,"
"Channel: %d, Width: %d, Primary channel Position: %d, Schedule Type: %s,"
"Start Offset: %d, Slot Duration: %d, Slot Bitmap: %d\n",
master_tsf, channel, width, position,
slsi_get_nan_schedule_type_str(schedule_type), start_offset,
slot_duration, slot_bitmap);
param_set_available = false;
break;
case WIFI_EVENT_NAN_TRAFFIC_UPDATE:
SLSI_INFO(sdev, "WIFI_EVENT_NAN_TRAFFIC_UPDATE, Rx MPDUs total: %d,"
"Tx MPDUs Total: %d, channel: %d, Slot Average Rx:%d,"
"Slot Average Tx: %d\n", rx_mpdu_total, tx_mpdu_total, channel,
slot_avg_rx, slot_avg_tx);
param_set_available = false;
break;
}
break;
}
break;
}
if (multi_param)
i += 2; /* To skip VD_PARAMETER_SET */
else
i += tag_len;
}
switch (event_id) {
case WIFI_EVENT_FW_NAN_ROLE_TYPE:
SLSI_INFO(sdev, "WIFI_EVENT_FW_NAN_ROLE_TYPE, Cluster Id:" MACSTR ", NAN Role:%s, AMR:%d, NMI:" MACSTR ", Hop Count:%d\n",
MAC2STR(nan_cluster_id), slsi_get_nan_role_str(nan_role), nan_amr, MAC2STR(nan_nmi), hop_count);
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
ndev_vif->nan.amr = nan_amr;
ndev_vif->nan.hopcount = hop_count;
ndev_vif->nan.role = nan_role;
#endif
break;
case WIFI_EVENT_NAN_AVAILABILITY_UPDATE:
SLSI_INFO(sdev, "WIFI_EVENT_NAN_AVAILABILITY_UPDATE, Master_TSF: %d, Channel: %d,"
"Width: %d, Primary channel Position: %d, Schedule Type: %s, Start Offset: %d,"
"Slot Duration: %d, Slot Bitmap: %d\n", master_tsf, channel, width, position,
slsi_get_nan_schedule_type_str(schedule_type), start_offset, slot_duration, slot_bitmap);
break;
case WIFI_EVENT_NAN_ULW_UPDATE:
SLSI_INFO(sdev, "WIFI_EVENT_NAN_ULW_UPDATE, Master_TSF: %d, ULW_Reason:%s, ULW_Index: %d,"
" ULW_start_time:%dms, ULW_Period: %dms, ULW_Duration: %dms, ULW_Count: %d, Channel: %d\n",
master_tsf, slsi_get_nan_ulw_reason_str(ulw_reason), ulw_index, ulw_start,
ulw_period, ulw_duration, ulw_count, channel);
break;
case WIFI_EVENT_NAN_TRAFFIC_UPDATE:
SLSI_INFO(sdev, "WIFI_EVENT_NAN_TRAFFIC_UPDATE, Rx MPDUs total: %d, Tx MPDUs Total: %d, channel: %d,"
"Slot Average Rx:%d, Slot Average Tx: %d\n", rx_mpdu_total, tx_mpdu_total, channel,
slot_avg_rx, slot_avg_tx);
break;
}
}
void slsi_rx_event_log_indication(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
u16 event_id = 0;
u64 timestamp = 0;
u8 *tlv_data;
u32 roam_reason = 0, chan_utilisation = 0;
u32 btm_request_mode = 0, btm_response = 0, eapol_msg_type = 0;
u32 deauth_reason = 0, eapol_retry_count = 0, status_code = 0;
u16 vendor_len, tag_id, tag_len, vtag_id, eapol_key_type = 0;
u32 tag_value, vtag_value, chan_frequency = 0, scan_type = 0;
short score_val = 0, rssi_thresh = 0;
u32 operating_class = 0, measure_mode = 0, measure_duration = 0, ap_count = 0, candidate_count = 0;
u32 message_type = 0, expired_timer_value = 0;
short roam_rssi_val = 0, roam_result_count = 1;
u8 mac_addr[6];
int tlv_buffer__len = fapi_get_datalen(skb), i = 0, channel_val = 0, iter = 0, channel_count = 0, lim = 0;
int channel_list[MAX_CHANNEL_COUNT] = {0};
char ssid[MAX_SSID_LEN];
char *string = NULL;
bool multi_param = false;
int roam_result_ap_count = 0;
struct slsi_logging_ap_info candidate_ap = {0}, current_ap = {0};
SLSI_MUTEX_LOCK(sdev->logger_mutex);
event_id = fapi_get_s16(skb, u.mlme_event_log_ind.event);
timestamp = fapi_get_u64(skb, u.mlme_event_log_ind.timestamp);
tlv_data = fapi_get_data(skb);
SLSI_DBG3(sdev, SLSI_GSCAN,
"slsi_rx_event_log_indication, event id = %d, len = %d\n", event_id, tlv_buffer__len);
#if IS_ENABLED(CONFIG_SCSC_WIFILOGGER)
SCSC_WLOG_FW_EVENT(WLOG_NORMAL, event_id, timestamp, fapi_get_data(skb), fapi_get_datalen(skb));
#endif
switch (event_id) {
case WIFI_EVENT_FW_NAN_ROLE_TYPE:
case WIFI_EVENT_NAN_AVAILABILITY_UPDATE:
case WIFI_EVENT_NAN_ULW_UPDATE:
case WIFI_EVENT_NAN_TRAFFIC_UPDATE:
slsi_handle_nan_rx_event_log_ind(sdev, dev, skb);
goto exit;
}
while (i + 4 < tlv_buffer__len) {
tag_id = le16_to_cpu(*((__le16 *)&tlv_data[i]));
tag_len = le16_to_cpu(*((__le16 *)&tlv_data[i + 2]));
i += 4;
if (i + tag_len > tlv_buffer__len) {
SLSI_INFO(sdev, "Incorrect fapi bulk data\n");
kfree_skb(skb);
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return;
}
tag_value = slsi_convert_tlv_data_to_value(&tlv_data[i], tag_len);
multi_param = false;
switch (tag_id) {
case SLSI_WIFI_TAG_RSSI:
roam_rssi_val = (short)tag_value;
if (event_id == WIFI_EVENT_ROAM_SCAN_RESULT) {
/* Following expected order of parameters in UDI
* (rssi value is parameter 2 in the log)
*/
if (roam_result_count == 2)
candidate_ap.rssi = roam_rssi_val;
else
current_ap.rssi = roam_rssi_val;
roam_result_count++;
}
break;
case SLSI_WIFI_TAG_REASON_CODE:
deauth_reason = tag_value;
break;
case SLSI_WIFI_TAG_VENDOR_SPECIFIC:
vendor_len = tag_len - 2;
vtag_id = le16_to_cpu(*((__le16 *)&tlv_data[i]));
vtag_value = slsi_convert_tlv_data_to_value(&tlv_data[i + 2], vendor_len);
switch (vtag_id) {
case SLSI_WIFI_TAG_VD_CHANNEL_UTILISATION:
chan_utilisation = vtag_value;
if (event_id == WIFI_EVENT_ROAM_SCAN_RESULT) {
if (roam_result_count == 3)
candidate_ap.ch_util = chan_utilisation;
else
current_ap.ch_util = chan_utilisation;
roam_result_count++;
}
break;
case SLSI_WIFI_TAG_VD_ROAMING_REASON:
roam_reason = vtag_value;
break;
case SLSI_WIFI_TAG_VD_BTM_REQUEST_MODE:
btm_request_mode = vtag_value;
break;
case SLSI_WIFI_TAG_VD_BTM_RESPONSE_STATUS:
btm_response = vtag_value;
break;
case SLSI_WIFI_TAG_VD_RETRY_COUNT:
eapol_retry_count = vtag_value;
break;
case SLSI_WIFI_TAG_VD_EAPOL_KEY_TYPE:
eapol_key_type = vtag_value;
break;
case SLSI_WIFI_TAG_VD_SCAN_TYPE:
scan_type = vtag_value;
break;
case SLSI_WIFI_TAG_VD_SCORE:
score_val = (short)vtag_value;
if (event_id == WIFI_EVENT_ROAM_SCAN_RESULT) {
if (roam_result_count == 4)
candidate_ap.score = score_val;
else
current_ap.score = score_val;
roam_result_count++;
}
break;
case SLSI_WIFI_TAG_VD_RSSI_THRESHOLD:
rssi_thresh = (short)vtag_value;
break;
case SLSI_WIFI_TAG_VD_OPERATING_CLASS:
operating_class = vtag_value;
break;
case SLSI_WIFI_TAG_VD_MEASUREMENT_MODE:
measure_mode = vtag_value;
break;
case SLSI_WIFI_TAG_VD_MEASUREMENT_DURATION:
measure_duration = vtag_value;
break;
case SLSI_WIFI_TAG_VD_MIN_AP_COUNT:
ap_count = vtag_value;
break;
case SLSI_WIFI_TAG_VD_CANDIDATE_LIST_COUNT:
candidate_count = vtag_value;
break;
case SLSI_WIFI_TAG_VD_MESSAGE_TYPE:
message_type = vtag_value;
break;
case SLSI_WIFI_TAG_VD_EXPIRED_TIMER_VALUE:
expired_timer_value = vtag_value;
break;
case SLSI_WIFI_TAG_VD_ESTIMATED_TP:
if (event_id == WIFI_EVENT_ROAM_SCAN_RESULT) {
if (roam_result_count == 5)
candidate_ap.tp_score = vtag_value;
else
current_ap.tp_score = vtag_value;
roam_result_count++;
}
break;
case SLSI_WIFI_TAG_VD_PARAMETER_SET:
multi_param = true;
if (event_id == WIFI_EVENT_ROAM_SCAN_RESULT) {
if (roam_result_ap_count > 0 && (roam_result_ap_count % 2 == 0)) {
roam_result_count = 1;
SLSI_INFO(sdev, "WIFI_EVENT_ROAM_SCAN_RESULT, Candidate BSSID:" MACSTR ", Candidate RSSI:%d, "
"Candidate Channel Utilisation:%d, Candidate Score:%d, Candidate TP Score:%d,"
"Current BSSID:" MACSTR ", Current RSSI:%d,"
"Current Channel Utilisation:%d, Current Score:%d, Current TP Score:%d\n",
MAC2STR(candidate_ap.mac), candidate_ap.rssi, candidate_ap.ch_util,
candidate_ap.score, candidate_ap.tp_score, MAC2STR(current_ap.mac),
current_ap.rssi, current_ap.ch_util, current_ap.score, current_ap.tp_score);
memset(&candidate_ap, 0, sizeof(candidate_ap));
memset(&current_ap, 0, sizeof(current_ap));
}
roam_result_ap_count++;
}
break;
}
break;
case SLSI_WIFI_TAG_EAPOL_MESSAGE_TYPE:
eapol_msg_type = tag_value;
break;
case SLSI_WIFI_TAG_STATUS:
status_code = tag_value;
break;
case SLSI_WIFI_TAG_BSSID:
SLSI_ETHER_COPY(mac_addr, &tlv_data[i]);
if (event_id == WIFI_EVENT_ROAM_SCAN_RESULT) {
if (roam_result_count == 1)
SLSI_ETHER_COPY(candidate_ap.mac, mac_addr);
else
SLSI_ETHER_COPY(current_ap.mac, mac_addr);
roam_result_count++;
}
break;
case SLSI_WIFI_TAG_CHANNEL:
chan_frequency = tag_value;
break;
case SLSI_WIFI_TAG_IE:
iter = i;
lim = iter + tlv_data[iter + 1] + 2;
iter += 7; /* 1byte (id) + 1byte(length) + 3byte (oui) + 2byte */
while (iter < lim && lim <= i + tag_len) {
channel_val = le16_to_cpu(*((__le16 *)&tlv_data[iter]));
channel_list[channel_count] = ieee80211_frequency_to_channel(channel_val / 2);
if (channel_list[channel_count] < 1 || channel_list[channel_count] > 196) {
SLSI_ERR(sdev, "ERR: Invalid channel received %d\n", channel_list[channel_count]);
/* Invalid channel is received. Prints out TLV data for SLSI_WIFI_TAG_IE */
SCSC_BIN_TAG_INFO(BINARY, &tlv_data[i], tlv_data[i + 1] + 2);
break;
}
iter += 3;
channel_count += 1;
if (channel_count == MAX_CHANNEL_COUNT) {
SLSI_ERR(sdev, "ERR: Channel list received >= %d\n", MAX_CHANNEL_COUNT);
break;
}
}
break;
case SLSI_WIFI_TAG_SSID:
memset(ssid, '\0', sizeof(ssid));
if (tag_len > MAX_SSID_LEN)
memcpy(ssid, &tlv_data[i], MAX_SSID_LEN);
else
memcpy(ssid, &tlv_data[i], tag_len);
break;
}
if (multi_param)
i += 2; /* To skip VD_PARAMETER_SET */
else
i += tag_len;
}
switch (event_id) {
case FAPI_EVENT_WIFI_EVENT_FW_EAPOL_FRAME_TRANSMIT_START:
if (eapol_key_type == SLSI_WIFI_EAPOL_KEY_TYPE_GTK)
SLSI_INFO(sdev, "WIFI_EVENT_FW_EAPOL_FRAME_TRANSMIT_START, Send GTK, G%d\n", eapol_msg_type);
else if (eapol_key_type == SLSI_WIFI_EAPOL_KEY_TYPE_PTK)
SLSI_INFO(sdev, "WIFI_EVENT_FW_EAPOL_FRAME_TRANSMIT_START, Send 4way-H/S, M%d\n",
eapol_msg_type);
break;
case FAPI_EVENT_WIFI_EVENT_FW_EAPOL_FRAME_TRANSMIT_STOP:
SLSI_INFO(sdev, "WIFI_EVENT_FW_EAPOL_FRAME_TRANSMIT_STOP,Result Code:%d, Retry Count:%d\n",
status_code, eapol_retry_count);
break;
case FAPI_EVENT_WIFI_EVENT_FW_EAPOL_FRAME_RECEIVED:
if (eapol_key_type == SLSI_WIFI_EAPOL_KEY_TYPE_GTK)
SLSI_INFO(sdev, "WIFI_EVENT_FW_EAPOL_FRAME_RECEIVED, Received GTK, G%d\n", eapol_msg_type);
else if (eapol_key_type == SLSI_WIFI_EAPOL_KEY_TYPE_PTK)
SLSI_INFO(sdev, "WIFI_EVENT_FW_EAPOL_FRAME_RECEIVED, Received 4way-H/S, M%d\n", eapol_msg_type);
break;
case WIFI_EVENT_FW_BTM_FRAME_REQUEST:
string = slsi_print_channel_list(channel_list, channel_count);
SLSI_INFO(sdev, "WIFI_EVENT_FW_BTM_FRAME_REQUEST,Request Mode:%d, Candidate List Count:%d, "
"Channel List:%s\n", btm_request_mode, candidate_count, string);
kfree(string);
break;
case WIFI_EVENT_FW_BTM_FRAME_RESPONSE:
SLSI_INFO(sdev, "WIFI_EVENT_FW_BTM_FRAME_RESPONSE,Status code:%d\n", btm_response);
break;
case FAPI_EVENT_WIFI_EVENT_ROAM_SEARCH_STARTED:
SLSI_INFO(sdev, "WIFI_EVENT_ROAM_SEARCH_STARTED, RSSI:%d, Deauth Reason:0x%04x, "
"RSSI Threshold:%d,Channel Utilisation:%d,Roam Reason: %s Expired Timer Value: %d\n",
roam_rssi_val, deauth_reason, rssi_thresh, chan_utilisation,
slsi_get_roam_reason_str(roam_reason), expired_timer_value);
break;
case FAPI_EVENT_WIFI_EVENT_FW_AUTH_STARTED:
SLSI_INFO(sdev, "WIFI_EVENT_FW_AUTH_STARTED, BSSID:" MACSTR "\n", MAC2STR(mac_addr));
break;
case FAPI_EVENT_WIFI_EVENT_AUTH_COMPLETE:
SLSI_INFO(sdev, "WIFI_EVENT_AUTH_COMPLETE,Status code:%d\n", status_code);
break;
case FAPI_EVENT_WIFI_EVENT_ROAM_ASSOC_COMPLETE:
SLSI_INFO(sdev, "Received Association Response\n");
break;
case WIFI_EVENT_FW_NR_FRAME_REQUEST:
SLSI_INFO(sdev, "Send Radio Measurement Frame (Neighbor Report Req)\n");
break;
case WIFI_EVENT_FW_RM_FRAME_RESPONSE:
SLSI_INFO(sdev, "Received Radio Measurement Frame (Radio Measurement Rep)\n");
break;
case FAPI_EVENT_WIFI_EVENT_ROAM_AUTH_STARTED:
SLSI_INFO(sdev, "WIFI_EVENT_ROAM_AUTH_STARTED, BSSID:" MACSTR ", Frequency:%d\n", MAC2STR(mac_addr), chan_frequency / 2);
break;
case WIFI_EVENT_FW_NR_FRAME_RESPONSE:
string = slsi_print_channel_list(channel_list, channel_count);
SLSI_INFO(sdev, "WIFI_EVENT_FW_NR_FRAME_RESPONSE, Candidate List Count:%d, Channel List:%s\n",
candidate_count, string);
kfree(string);
break;
case FAPI_EVENT_WIFI_EVENT_ROAM_AUTH_COMPLETE:
SLSI_INFO(sdev, "WIFI_EVENT_ROAM_AUTH_COMPLETE, BSSID:" MACSTR ", Result:%d\n", MAC2STR(mac_addr), status_code);
break;
case FAPI_EVENT_WIFI_EVENT_AUTH_TIMEOUT:
SLSI_INFO(sdev, "WIFI_EVENT_AUTH_TIMEOUT, BSSID:" MACSTR ", Result:%d\n", MAC2STR(mac_addr), status_code);
break;
case WIFI_EVENT_ROAM_AUTH_TIMEOUT:
SLSI_INFO(sdev, "WIFI_EVENT_AUTH_TIMEOUT, BSSID:" MACSTR ", Result:%d\n", MAC2STR(mac_addr), status_code);
break;
case WIFI_EVENT_FW_CONNECTION_ATTEMPT_ABORTED:
SLSI_INFO(sdev, "WIFI_EVENT_FW_CONNECTION_ATTEMPT_ABORTED, BSSID:" MACSTR ", Result:%d\n", MAC2STR(mac_addr), status_code);
break;
case FAPI_EVENT_WIFI_EVENT_ROAM_SCAN_STARTED:
string = slsi_print_channel_list(channel_list, channel_count);
SLSI_INFO(sdev, "WIFI_EVENT_ROAM_SCAN_STARTED, SSID:%s, Scan Type:%s, Channel List:%s\n",
ssid, slsi_get_scan_type(scan_type), string);
kfree(string);
break;
case WIFI_EVENT_ROAM_SCAN_RESULT:
if (roam_result_ap_count > 0)
SLSI_INFO(sdev, "WIFI_EVENT_ROAM_SCAN_RESULT, Candidate BSSID:" MACSTR ", Candidate RSSI:%d, "
"Candidate Channel Utilisation:%d, Candidate Score:%d, Candidate TP Score:%d, Current BSSID:" MACSTR ", Current RSSI:%d, "
"Current Channel Utilisation:%d, Current Score:%d, Current TP Score:%d\n", MAC2STR(candidate_ap.mac), candidate_ap.rssi, candidate_ap.ch_util,
candidate_ap.score, candidate_ap.tp_score, MAC2STR(current_ap.mac), current_ap.rssi, current_ap.ch_util, current_ap.score, current_ap.tp_score);
else
SLSI_INFO(sdev, "WIFI_EVENT_ROAM_SCAN_RESULT, Candidate BSSID:" MACSTR ", Candidate RSSI:%d, "
"Candidate Channel Utilisation:%d, Candidate Score:%d, Current BSSID:" MACSTR ", Current RSSI:%d, "
"Current Channel Utilisation:%d, Current Score:%d\n", MAC2STR(candidate_ap.mac), candidate_ap.rssi, candidate_ap.ch_util,
candidate_ap.score, MAC2STR(current_ap.mac), current_ap.rssi, current_ap.ch_util, current_ap.score);
break;
case WIFI_EVENT_ROAM_RSSI_THRESHOLD:
SLSI_INFO(sdev, "WIFI_EVENT_ROAM_RSSI_THRESHOLD, RSSI Threshold:%d\n", rssi_thresh);
break;
case WIFI_EVENT_FW_BEACON_REPORT_REQUEST:
SLSI_INFO(sdev, "WIFI_EVENT_FW_BEACON_REPORT_REQUEST, Operating Class:%d, Measurement Mode:%s,"
"Measurement Duration:%d\n", operating_class, slsi_get_measure_mode(measure_mode), measure_duration);
break;
case WIFI_EVENT_FW_FTM_RANGE_REQUEST:
SLSI_INFO(sdev, "WIFI_EVENT_FW_FTM_RANGE_REQUEST, Min Ap Count:%d, Candidate List Count:%d\n",
ap_count, candidate_count);
break;
case WIFI_EVENT_FW_FRAME_TRANSMIT_FAILURE:
SLSI_INFO(sdev, "WIFI_EVENT_FW_FRAME_TRANSMIT_FAILURE, Message Type:%s, Result:%d, Retry Count:%d\n",
slsi_frame_transmit_failure_message_type(message_type), status_code, eapol_retry_count);
break;
case WIFI_EVENT_ASSOCIATING_DEAUTH_RECEIVED:
SLSI_INFO(sdev, "WIFI_EVENT_ASSOCIATING_DEAUTH_RECEIVED, BSSID:" MACSTR ", Reason:%d\n",
MAC2STR(mac_addr), deauth_reason);
break;
}
exit:
kfree_skb(skb);
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
}
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
static void slsi_on_ring_buffer_data(char *ring_name, char *buffer, int buffer_size,
struct scsc_wifi_ring_buffer_status *buffer_status, void *ctx)
{
struct sk_buff *skb;
int event_id = SLSI_NL80211_LOGGER_RING_EVENT;
struct slsi_dev *sdev = ctx;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
skb = cfg80211_vendor_event_alloc(sdev->wiphy, NULL, buffer_size, event_id, GFP_KERNEL);
#else
skb = cfg80211_vendor_event_alloc(sdev->wiphy, buffer_size, event_id, GFP_KERNEL);
#endif
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for vendor event: %d\n", event_id);
return;
}
if (nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_STATUS, sizeof(*buffer_status), buffer_status) ||
nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_DATA, buffer_size, buffer)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
return;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
}
static void slsi_on_alert(char *buffer, int buffer_size, int err_code, void *ctx)
{
struct sk_buff *skb;
int event_id = SLSI_NL80211_LOGGER_FW_DUMP_EVENT;
struct slsi_dev *sdev = ctx;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
skb = cfg80211_vendor_event_alloc(sdev->wiphy, NULL, buffer_size, event_id, GFP_KERNEL);
#else
skb = cfg80211_vendor_event_alloc(sdev->wiphy, buffer_size, event_id, GFP_KERNEL);
#endif
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for vendor event: %d\n", event_id);
goto exit;
}
if (nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_DUMP_LEN, buffer_size) ||
nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_DATA, buffer_size, buffer)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
goto exit;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
}
static void slsi_on_firmware_memory_dump(char *buffer, int buffer_size, void *ctx)
{
SLSI_ERR_NODEV("slsi_on_firmware_memory_dump\n");
kfree(mem_dump_buffer);
mem_dump_buffer = NULL;
mem_dump_buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!mem_dump_buffer) {
SLSI_ERR_NODEV("Failed to allocate memory for mem_dump_buffer\n");
return;
}
mem_dump_buffer_size = buffer_size;
memcpy(mem_dump_buffer, buffer, mem_dump_buffer_size);
}
static void slsi_on_driver_memory_dump(char *buffer, int buffer_size, void *ctx)
{
SLSI_ERR_NODEV("slsi_on_driver_memory_dump\n");
kfree(mem_dump_buffer);
mem_dump_buffer = NULL;
mem_dump_buffer_size = buffer_size;
mem_dump_buffer = kmalloc(mem_dump_buffer_size, GFP_KERNEL);
if (!mem_dump_buffer) {
SLSI_ERR_NODEV("Failed to allocate memory for mem_dump_buffer\n");
return;
}
memcpy(mem_dump_buffer, buffer, mem_dump_buffer_size);
}
static int slsi_enable_logging(struct slsi_dev *sdev, bool enable)
{
int status = 0;
#ifdef ENABLE_WIFI_LOGGER_MIB_WRITE
struct slsi_mib_data mib_data = { 0, NULL };
SLSI_DBG3(sdev, SLSI_GSCAN, "Value of enable is : %d\n", enable);
status = slsi_mib_encode_bool(&mib_data, SLSI_PSID_UNIFI_LOGGER_ENABLED, enable, 0);
if (status != SLSI_MIB_STATUS_SUCCESS) {
SLSI_ERR(sdev, "slsi_enable_logging failed: no mem for MIB\n");
status = -ENOMEM;
goto exit;
}
status = slsi_mlme_set(sdev, NULL, mib_data.data, mib_data.dataLength);
kfree(mib_data.data);
if (status)
SLSI_ERR(sdev, "Err setting unifiLoggerEnabled MIB. error = %d\n", status);
exit:
return status;
#else
SLSI_DBG3(sdev, SLSI_GSCAN, "UnifiLoggerEnabled MIB write disabled\n");
return status;
#endif
}
static int slsi_start_logging(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int temp = 0;
int type = 0;
char ring_name[32] = {0};
int verbose_level = 0;
int ring_flags = 0;
int max_interval_sec = 0;
int min_data_size = 0;
const struct nlattr *attr;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
nla_for_each_attr(attr, data, len, temp) {
if (!attr) {
ret = -EINVAL;
goto exit;
}
type = nla_type(attr);
switch (type) {
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_NAME:
strncpy(ring_name, nla_data(attr), MIN(sizeof(ring_name) - 1, nla_len(attr)));
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_VERBOSE_LEVEL:
if (slsi_util_nla_get_u32(attr, &verbose_level)) {
ret = -EINVAL;
goto exit;
}
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_FLAGS:
if (slsi_util_nla_get_u32(attr, &ring_flags)) {
ret = -EINVAL;
goto exit;
}
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_LOG_MAX_INTERVAL:
if (slsi_util_nla_get_u32(attr, &max_interval_sec)) {
ret = -EINVAL;
goto exit;
}
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_LOG_MIN_DATA_SIZE:
if (slsi_util_nla_get_u32(attr, &min_data_size)) {
ret = -EINVAL;
goto exit;
}
break;
default:
SLSI_ERR(sdev, "Unknown type: %d\n", type);
ret = -EINVAL;
goto exit;
}
}
ret = scsc_wifi_set_log_handler(slsi_on_ring_buffer_data, sdev);
if (ret < 0) {
SLSI_ERR(sdev, "scsc_wifi_set_log_handler failed ret: %d\n", ret);
goto exit;
}
ret = scsc_wifi_set_alert_handler(slsi_on_alert, sdev);
if (ret < 0) {
SLSI_ERR(sdev, "Warning : scsc_wifi_set_alert_handler failed ret: %d\n", ret);
}
ret = slsi_enable_logging(sdev, 1);
if (ret < 0) {
SLSI_ERR(sdev, "slsi_enable_logging for enable = 1, failed ret: %d\n", ret);
goto exit_with_reset_alert_handler;
}
ret = scsc_wifi_start_logging(verbose_level, ring_flags, max_interval_sec, min_data_size, ring_name);
if (ret < 0) {
SLSI_ERR(sdev, "scsc_wifi_start_logging failed ret: %d\n", ret);
goto exit_with_disable_logging;
} else {
goto exit;
}
exit_with_disable_logging:
ret = slsi_enable_logging(sdev, 0);
if (ret < 0)
SLSI_ERR(sdev, "slsi_enable_logging for enable = 0, failed ret: %d\n", ret);
exit_with_reset_alert_handler:
ret = scsc_wifi_reset_alert_handler();
if (ret < 0)
SLSI_ERR(sdev, "Warning : scsc_wifi_reset_alert_handler failed ret: %d\n", ret);
ret = scsc_wifi_reset_log_handler();
if (ret < 0)
SLSI_ERR(sdev, "scsc_wifi_reset_log_handler failed ret: %d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_reset_logging(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
ret = slsi_enable_logging(sdev, 0);
if (ret < 0)
SLSI_ERR(sdev, "slsi_enable_logging for enable = 0, failed ret: %d\n", ret);
ret = scsc_wifi_reset_log_handler();
if (ret < 0)
SLSI_ERR(sdev, "scsc_wifi_reset_log_handler failed ret: %d\n", ret);
ret = scsc_wifi_reset_alert_handler();
if (ret < 0)
SLSI_ERR(sdev, "Warning : scsc_wifi_reset_alert_handler failed ret: %d\n", ret);
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_trigger_fw_mem_dump(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
struct sk_buff *skb = NULL;
int length = 100;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
ret = scsc_wifi_get_firmware_memory_dump(slsi_on_firmware_memory_dump, sdev);
if (ret) {
SLSI_ERR(sdev, "scsc_wifi_get_firmware_memory_dump failed : %d\n", ret);
goto exit;
}
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for Vendor event\n");
ret = -ENOMEM;
goto exit;
}
if (nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_DUMP_LEN, mem_dump_buffer_size)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
SLSI_ERR(sdev, "Vendor Command reply failed ret:%d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_get_fw_mem_dump(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int temp = 0;
int type = 0;
int buf_len = 0;
void __user *user_buf = NULL;
const struct nlattr *attr;
struct sk_buff *skb;
u64 val = 0;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
if (!data)
return -EINVAL;
SLSI_MUTEX_LOCK(sdev->logger_mutex);
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_DUMP_LEN:
if (slsi_util_nla_get_u32(attr, &buf_len)) {
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return -EINVAL;
}
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_DUMP_DATA:
if (slsi_util_nla_get_u64(attr, &val)) {
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return -EINVAL;
}
user_buf = (void __user *)(unsigned long)(val);
break;
default:
SLSI_ERR(sdev, "Unknown type: %d\n", type);
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return -EINVAL;
}
}
if (buf_len > 0 && user_buf && mem_dump_buffer) {
if (buf_len > mem_dump_buffer_size)
buf_len = mem_dump_buffer_size;
ret = copy_to_user(user_buf, mem_dump_buffer, buf_len);
if (ret) {
SLSI_ERR(sdev, "failed to copy memdump into user buffer : %d\n", ret);
goto exit;
}
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 100);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for Vendor event\n");
ret = -ENOMEM;
goto exit;
}
/* Indicate the memdump is successfully copied */
if (nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_DUMP_DATA, sizeof(ret), &ret)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
SLSI_ERR(sdev, "Vendor Command reply failed ret:%d\n", ret);
}
exit:
kfree(mem_dump_buffer);
mem_dump_buffer = NULL;
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_trigger_driver_mem_dump(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
struct sk_buff *skb = NULL;
int length = 100;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
ret = scsc_wifi_get_driver_memory_dump(slsi_on_driver_memory_dump, sdev);
if (ret) {
SLSI_ERR(sdev, "scsc_wifi_get_driver_memory_dump failed : %d\n", ret);
goto exit;
}
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for Vendor event\n");
ret = -ENOMEM;
goto exit;
}
if (nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_DRIVER_DUMP_LEN, mem_dump_buffer_size)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
SLSI_ERR(sdev, "Vendor Command reply failed ret:%d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_get_driver_mem_dump(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int temp = 0;
int type = 0;
int buf_len = 0;
void __user *user_buf = NULL;
const struct nlattr *attr;
struct sk_buff *skb;
u64 val = 0;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_DRIVER_DUMP_LEN:
if (slsi_util_nla_get_u32(attr, &buf_len)) {
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return -EINVAL;
}
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_DRIVER_DUMP_DATA:
if (slsi_util_nla_get_u64(attr, &val)) {
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return -EINVAL;
}
user_buf = (void __user *)(unsigned long)(val);
break;
default:
SLSI_ERR(sdev, "Unknown type: %d\n", type);
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return -EINVAL;
}
}
if (buf_len > 0 && user_buf && mem_dump_buffer_size) {
if (buf_len > mem_dump_buffer_size)
buf_len = mem_dump_buffer_size;
ret = copy_to_user(user_buf, mem_dump_buffer, buf_len);
if (ret) {
SLSI_ERR(sdev, "failed to copy memdump into user buffer : %d\n", ret);
goto exit;
}
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 100);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for Vendor event\n");
ret = -ENOMEM;
goto exit;
}
/* Indicate the memdump is successfully copied */
if (nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_DRIVER_DUMP_DATA, sizeof(ret), &ret)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
SLSI_ERR(sdev, "Vendor Command reply failed ret:%d\n", ret);
}
exit:
kfree(mem_dump_buffer);
mem_dump_buffer = NULL;
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_get_version(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int temp = 0;
int type = 0;
int buffer_size = 1024;
bool log_version = false;
char *buffer;
const struct nlattr *attr;
buffer = kzalloc(buffer_size, GFP_KERNEL);
if (!buffer) {
SLSI_ERR(sdev, "No mem. Size:%d\n", buffer_size);
return -ENOMEM;
}
SLSI_MUTEX_LOCK(sdev->logger_mutex);
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_DRIVER_VERSION:
log_version = true;
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_VERSION:
log_version = false;
break;
default:
SLSI_ERR(sdev, "Unknown type: %d\n", type);
ret = -EINVAL;
goto exit;
}
}
if (log_version)
ret = scsc_wifi_get_driver_version(buffer, buffer_size);
else
ret = scsc_wifi_get_firmware_version(buffer, buffer_size);
if (ret < 0) {
SLSI_ERR(sdev, "failed to get the version %d\n", ret);
goto exit;
}
ret = slsi_vendor_cmd_reply(wiphy, buffer, strlen(buffer));
exit:
kfree(buffer);
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_get_ring_buffers_status(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int num_rings = SLSI_MAX_NUM_RING;
struct sk_buff *skb;
struct scsc_wifi_ring_buffer_status status[SLSI_MAX_NUM_RING];
SLSI_DBG1(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
memset(status, 0, sizeof(struct scsc_wifi_ring_buffer_status) * num_rings);
ret = scsc_wifi_get_ring_buffers_status(&num_rings, status);
if (ret < 0) {
SLSI_ERR(sdev, "scsc_wifi_get_ring_buffers_status failed ret:%d\n", ret);
goto exit;
}
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 700);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for Vendor event\n");
ret = -ENOMEM;
goto exit;
}
/* Indicate that the ring count and ring buffers status is successfully copied */
if (nla_put_u8(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_NUM, num_rings) ||
nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_STATUS, sizeof(status[0]) * num_rings, status)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
SLSI_ERR(sdev, "Vendor Command reply failed ret:%d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_get_ring_data(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int temp = 0;
int type = 0;
char ring_name[32] = {0};
const struct nlattr *attr;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
nla_for_each_attr(attr, data, len, temp) {
if (!attr) {
ret = -EINVAL;
goto exit;
}
type = nla_type(attr);
switch (type) {
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_NAME:
strncpy(ring_name, nla_data(attr), MIN(sizeof(ring_name) - 1, nla_len(attr)));
break;
default:
SLSI_ERR(sdev, "Unknown type: %d\n", type);
goto exit;
}
}
ret = scsc_wifi_get_ring_data(ring_name);
if (ret < 0)
SLSI_ERR(sdev, "trigger_get_data failed ret:%d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_get_logger_supported_feature_set(struct wiphy *wiphy, struct wireless_dev *wdev,
const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
u32 supported_features = 0;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
ret = scsc_wifi_get_logger_supported_feature_set(&supported_features);
if (ret < 0) {
SLSI_ERR(sdev, "scsc_wifi_get_logger_supported_feature_set failed ret:%d\n", ret);
goto exit;
}
ret = slsi_vendor_cmd_reply(wiphy, &supported_features, sizeof(supported_features));
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_start_pkt_fate_monitoring(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
int ret = 0;
#ifdef ENABLE_WIFI_LOGGER_MIB_WRITE
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct slsi_mib_data mib_data = { 0, NULL };
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
ret = slsi_mib_encode_bool(&mib_data, SLSI_PSID_UNIFI_TX_DATA_CONFIRM, 1, 0);
if (ret != SLSI_MIB_STATUS_SUCCESS) {
SLSI_ERR(sdev, "Failed to set UnifiTxDataConfirm MIB : no mem for MIB\n");
ret = -ENOMEM;
goto exit;
}
ret = slsi_mlme_set(sdev, NULL, mib_data.data, mib_data.dataLength);
if (ret) {
SLSI_ERR(sdev, "Err setting UnifiTxDataConfirm MIB. error = %d\n", ret);
goto exit;
}
ret = scsc_wifi_start_pkt_fate_monitoring();
if (ret < 0) {
SLSI_ERR(sdev, "scsc_wifi_start_pkt_fate_monitoring failed, ret=%d\n", ret);
// Resetting the SLSI_PSID_UNIFI_TX_DATA_CONFIRM mib back to 0.
mib_data.dataLength = 0;
mib_data.data = NULL;
ret = slsi_mib_encode_bool(&mib_data, SLSI_PSID_UNIFI_TX_DATA_CONFIRM, 1, 0);
if (ret != SLSI_MIB_STATUS_SUCCESS) {
SLSI_ERR(sdev, "Failed to set UnifiTxDataConfirm MIB : no mem for MIB\n");
ret = -ENOMEM;
goto exit;
}
ret = slsi_mlme_set(sdev, NULL, mib_data.data, mib_data.dataLength);
if (ret) {
SLSI_ERR(sdev, "Err setting UnifiTxDataConfirm MIB. error = %d\n", ret);
goto exit;
}
}
exit:
kfree(mib_data.data);
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
#else
SLSI_ERR_NODEV("slsi_start_pkt_fate_monitoring : UnifiTxDataConfirm MIB write disabled\n");
return ret;
#endif
}
static int slsi_get_tx_pkt_fates(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int temp = 0;
int type = 0;
void __user *user_buf = NULL;
u32 req_count = 0;
size_t provided_count = 0;
struct sk_buff *skb;
const struct nlattr *attr;
u64 val = 0;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_NUM:
if (slsi_util_nla_get_u32(attr, &req_count)) {
ret = -EINVAL;
goto exit;
}
if (req_count > MAX_FATE_LOG_LEN) {
SLSI_ERR(sdev, "Found invalid req_count %d for SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_NUM", req_count);
ret = -EINVAL;
goto exit;
}
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_DATA:
if (slsi_util_nla_get_u64(attr, &val)) {
ret = -EINVAL;
goto exit;
}
user_buf = (void __user *)(unsigned long)(val);
break;
default:
SLSI_ERR(sdev, "Unknown type: %d\n", type);
ret = -EINVAL;
goto exit;
}
}
ret = scsc_wifi_get_tx_pkt_fates(user_buf, req_count, &provided_count, true);
if (ret < 0) {
SLSI_ERR(sdev, "scsc_wifi_get_tx_pkt_fates failed ret: %d\n", ret);
goto exit;
}
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 200);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for Vendor event\n");
ret = -ENOMEM;
goto exit;
}
if (nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_NUM, sizeof(provided_count), &provided_count)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
SLSI_ERR(sdev, "Vendor Command reply failed ret:%d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_get_rx_pkt_fates(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
int temp = 0;
int type = 0;
void __user *user_buf = NULL;
u32 req_count = 0;
size_t provided_count = 0;
struct sk_buff *skb;
const struct nlattr *attr;
u64 val = 0;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
SLSI_MUTEX_LOCK(sdev->logger_mutex);
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_NUM:
if (slsi_util_nla_get_u32(attr, &req_count)) {
ret = -EINVAL;
goto exit;
}
if (req_count > MAX_FATE_LOG_LEN) {
SLSI_ERR(sdev, "Found invalid req_count %d for SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_NUM", req_count);
ret = -EINVAL;
goto exit;
}
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_DATA:
if (slsi_util_nla_get_u64(attr, &val)) {
ret = -EINVAL;
goto exit;
}
user_buf = (void __user *)(unsigned long)(val);
break;
default:
SLSI_ERR(sdev, "Unknown type: %d\n", type);
ret = -EINVAL;
goto exit;
}
}
ret = scsc_wifi_get_rx_pkt_fates(user_buf, req_count, &provided_count, true);
if (ret < 0) {
SLSI_ERR(sdev, "scsc_wifi_get_rx_pkt_fates failed ret: %d\n", ret);
goto exit;
}
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 200);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for Vendor event\n");
ret = -ENOMEM;
goto exit;
}
if (nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_NUM, sizeof(provided_count), &provided_count)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
SLSI_ERR(sdev, "Vendor Command reply failed ret:%d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
static int slsi_get_wake_reason_stats(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct slsi_wlan_driver_wake_reason_cnt wake_reason_count;
int ret = 0;
int temp = 0;
int type = 0;
const struct nlattr *attr;
struct sk_buff *skb;
SLSI_DBG3(sdev, SLSI_GSCAN, "\n");
// Initialising the wake_reason_count structure values to 0.
memset(&wake_reason_count, 0, sizeof(struct slsi_wlan_driver_wake_reason_cnt));
SLSI_MUTEX_LOCK(sdev->logger_mutex);
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ:
if (slsi_util_nla_get_u32(attr, &(wake_reason_count.cmd_event_wake_cnt_sz))) {
ret = -EINVAL;
goto exit;
}
break;
case SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ:
if (slsi_util_nla_get_u32(attr, &(wake_reason_count.driver_fw_local_wake_cnt_sz))) {
ret = -EINVAL;
goto exit;
}
break;
default:
SLSI_ERR(sdev, "Unknown type: %d\n", type);
ret = -EINVAL;
goto exit;
}
}
if (ret < 0) {
SLSI_ERR(sdev, "Failed to get wake reason stats : %d\n", ret);
goto exit;
}
/* Alloc the SKB for vendor_event */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 700);
if (!skb) {
SLSI_ERR_NODEV("Failed to allocate skb for Vendor event\n");
ret = -ENOMEM;
goto exit;
}
if (nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_TOTAL_CMD_EVENT_WAKE,
wake_reason_count.total_cmd_event_wake)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
if (nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR, 0,
wake_reason_count.cmd_event_wake_cnt)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
if (nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE,
wake_reason_count.total_driver_fw_local_wake)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
if (nla_put(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR, 0,
wake_reason_count.driver_fw_local_wake_cnt)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
if (nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_TOTAL_RX_DATA_WAKE,
wake_reason_count.total_rx_data_wake) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_RX_UNICAST_CNT,
wake_reason_count.rx_wake_details.rx_unicast_cnt) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_RX_MULTICAST_CNT,
wake_reason_count.rx_wake_details.rx_multicast_cnt) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_RX_BROADCAST_CNT,
wake_reason_count.rx_wake_details.rx_broadcast_cnt) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_ICMP_PKT,
wake_reason_count.rx_wake_pkt_classification_info.icmp_pkt) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_ICMP6_PKT,
wake_reason_count.rx_wake_pkt_classification_info.icmp6_pkt) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_ICMP6_RA,
wake_reason_count.rx_wake_pkt_classification_info.icmp6_ra) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_ICMP6_NA,
wake_reason_count.rx_wake_pkt_classification_info.icmp6_na) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_ICMP6_NS,
wake_reason_count.rx_wake_pkt_classification_info.icmp6_ns) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_ICMP4_RX_MULTICAST_CNT,
wake_reason_count.rx_multicast_wake_pkt_info.ipv4_rx_multicast_addr_cnt) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_ICMP6_RX_MULTICAST_CNT,
wake_reason_count.rx_multicast_wake_pkt_info.ipv6_rx_multicast_addr_cnt) ||
nla_put_u32(skb, SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_OTHER_RX_MULTICAST_CNT,
wake_reason_count.rx_multicast_wake_pkt_info.other_rx_multicast_addr_cnt)) {
SLSI_ERR_NODEV("Failed nla_put\n");
kfree_skb(skb);
ret = -EINVAL;
goto exit;
}
ret = cfg80211_vendor_cmd_reply(skb);
if (ret)
SLSI_ERR(sdev, "Vendor Command reply failed ret:%d\n", ret);
exit:
SLSI_MUTEX_UNLOCK(sdev->logger_mutex);
return ret;
}
#endif /* CONFIG_SCSC_WLAN_ENHANCED_LOGGING */
static int slsi_acs_validate_width_hw_mode(struct slsi_acs_request *request)
{
if (request->hw_mode != SLSI_ACS_MODE_IEEE80211A && request->hw_mode != SLSI_ACS_MODE_IEEE80211B &&
request->hw_mode != SLSI_ACS_MODE_IEEE80211G && request->hw_mode != SLSI_ACS_MODE_IEEE80211ANY)
return -EINVAL;
if (request->ch_width != 20 && request->ch_width != 40 && request->ch_width != 80)
return -EINVAL;
return 0;
}
static int slsi_acs_init(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif;
struct slsi_acs_request *request;
int temp;
int type;
const struct nlattr *attr;
int r = 0;
u32 *freq_list = NULL;
int freq_list_len = 0;
SLSI_INFO(sdev, "SUBCMD_ACS_INIT Received\n");
if (slsi_is_test_mode_enabled()) {
SLSI_ERR(sdev, "Not supported in WlanLite mode\n");
return -EOPNOTSUPP;
}
if (wdev->iftype != NL80211_IFTYPE_AP) {
SLSI_ERR(sdev, "Invalid iftype: %d\n", wdev->iftype);
return -EINVAL;
}
if (!dev) {
SLSI_ERR(sdev, "Dev not found!\n");
return -ENODEV;
}
request = kcalloc(1, sizeof(*request), GFP_KERNEL);
if (!request) {
SLSI_ERR(sdev, "No memory for request!");
return -ENOMEM;
}
ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
nla_for_each_attr(attr, data, len, temp) {
if (!attr) {
kfree(request);
r = -EINVAL;
goto exit;
}
type = nla_type(attr);
switch (type) {
case SLSI_ACS_ATTR_HW_MODE:
{
if (slsi_util_nla_get_u8(attr, &(request->hw_mode))) {
kfree(request);
r = -EINVAL;
goto exit;
}
SLSI_INFO(sdev, "ACS hw mode: %d\n", request->hw_mode);
break;
}
case SLSI_ACS_ATTR_CHWIDTH:
{
if (slsi_util_nla_get_u16(attr, &(request->ch_width))) {
kfree(request);
r = -EINVAL;
goto exit;
}
SLSI_INFO(sdev, "ACS ch_width: %d\n", request->ch_width);
break;
}
case SLSI_ACS_ATTR_FREQ_LIST:
{
if (freq_list) /* This check is to avoid Prevent Issue */
break;
freq_list = kmalloc(nla_len(attr), GFP_KERNEL);
if (!freq_list) {
SLSI_ERR(sdev, "No memory for frequency list!");
kfree(request);
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return -ENOMEM;
}
memcpy(freq_list, nla_data(attr), nla_len(attr));
freq_list_len = nla_len(attr) / sizeof(u32);
SLSI_INFO(sdev, "ACS freq_list_len: %d\n", freq_list_len);
if (freq_list_len > MAX_CHAN_VALUE_ACS)
freq_list_len = MAX_CHAN_VALUE_ACS;
break;
}
default:
if (type > SLSI_ACS_ATTR_MAX)
SLSI_ERR(sdev, "Invalid type : %d\n", type);
break;
}
}
r = slsi_acs_validate_width_hw_mode(request);
if (r == 0 && freq_list_len) {
struct ieee80211_channel *channels[MAX_CHAN_VALUE_ACS];
struct slsi_acs_chan_info ch_info[MAX_CHAN_VALUE_ACS];
struct slsi_acs_selected_channels acs_selected_channels;
int i = 0, num_channels = 0;
int idx;
u32 chan_flags = (IEEE80211_CHAN_INDOOR_ONLY | IEEE80211_CHAN_RADAR |
IEEE80211_CHAN_DISABLED |
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 13)
IEEE80211_CHAN_PASSIVE_SCAN
#else
IEEE80211_CHAN_NO_IR
#endif
);
memset(channels, 0, sizeof(channels));
memset(&ch_info, 0, sizeof(ch_info));
for (i = 0; i < freq_list_len; i++) {
channels[num_channels] = ieee80211_get_channel(wiphy, freq_list[i]);
if (!channels[num_channels]) {
SLSI_INFO(sdev, "Ignore invalid freq:%d in freq list\n", freq_list[i]);
} else if (channels[num_channels]->flags & chan_flags) {
SLSI_INFO(sdev, "Skip invalid channel:%d for ACS\n", channels[num_channels]->hw_value);
} else {
idx = slsi_find_chan_idx(channels[num_channels]->hw_value, request->hw_mode);
ch_info[idx].chan = channels[num_channels]->hw_value;
num_channels++;
}
}
if (num_channels == 1) {
memset(&acs_selected_channels, 0, sizeof(acs_selected_channels));
acs_selected_channels.ch_width = 20;
acs_selected_channels.hw_mode = request->hw_mode;
acs_selected_channels.pri_channel = channels[0]->hw_value;
r = slsi_send_acs_event(sdev, acs_selected_channels);
sdev->acs_channel_switched = true;
kfree(freq_list);
kfree(request);
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return r;
}
if (request->hw_mode == SLSI_ACS_MODE_IEEE80211A)
request->ch_list_len = MAX_5G_CHANNELS;
else if (request->hw_mode == SLSI_ACS_MODE_IEEE80211B || request->hw_mode == SLSI_ACS_MODE_IEEE80211G)
request->ch_list_len = MAX_24G_CHANNELS;
else
request->ch_list_len = MAX_CHAN_VALUE_ACS;
memcpy(&request->acs_chan_info[0], &ch_info[0], sizeof(ch_info));
ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request = request;
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan = false;
r = slsi_mlme_add_scan(sdev,
dev,
FAPI_SCANTYPE_AP_AUTO_CHANNEL_SELECTION,
FAPI_REPORTMODE_REAL_TIME,
0, /* n_ssids */
NULL, /* ssids */
num_channels,
channels,
NULL,
NULL, /* ie */
0, /* ie_len */
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan);
} else {
SLSI_ERR(sdev, "Invalid freq_list len:%d or ch_width:%d or hw_mode:%d\n", freq_list_len,
request->ch_width, request->hw_mode);
r = -EINVAL;
kfree(request);
}
exit:
kfree(freq_list);
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return r;
}
static int slsi_configure_latency_mode(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev = wdev->netdev;
int temp = 0;
int type = 0;
const struct nlattr *attr;
int ret = 0;
int low_latency_mode = 0;
u8 val = 0;
if (!dev) {
SLSI_ERR(sdev, "dev is NULL!!\n");
return -EINVAL;
}
nla_for_each_attr(attr, data, len, temp) {
type = nla_type(attr);
switch (type) {
case SLSI_NL_ATTRIBUTE_LATENCY_MODE:
if (slsi_util_nla_get_u8(attr, &val)) {
ret = -EINVAL;
goto exit;
}
low_latency_mode = (int)val;
break;
default:
SLSI_ERR_NODEV("Unknown attribute: %d\n", type);
ret = -EINVAL;
goto exit;
}
}
ret = slsi_set_latency_mode(dev, low_latency_mode, len);
if (ret)
SLSI_ERR(sdev, "Error in setting low latency mode ret:%d\n", ret);
exit:
return ret;
}
static const struct nl80211_vendor_cmd_info slsi_vendor_events[] = {
/**********Deprecated now due to fapi updates.Do not remove*/
{ OUI_GOOGLE, SLSI_NL80211_SIGNIFICANT_CHANGE_EVENT },
{ OUI_GOOGLE, SLSI_NL80211_HOTLIST_AP_FOUND_EVENT },
/******************************************/
{ OUI_GOOGLE, SLSI_NL80211_SCAN_RESULTS_AVAILABLE_EVENT },
{ OUI_GOOGLE, SLSI_NL80211_FULL_SCAN_RESULT_EVENT },
{ OUI_GOOGLE, SLSI_NL80211_SCAN_EVENT },
/**********Deprecated now due to fapi updates.Do not remove*/
{ OUI_GOOGLE, SLSI_NL80211_HOTLIST_AP_LOST_EVENT },
/******************************************/
#ifdef CONFIG_SCSC_WLAN_KEY_MGMT_OFFLOAD
{ OUI_SAMSUNG, SLSI_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH },
#endif
{ OUI_SAMSUNG, SLSI_NL80211_VENDOR_HANGED_EVENT },
{ OUI_GOOGLE, SLSI_NL80211_EPNO_EVENT },
{ OUI_GOOGLE, SLSI_NL80211_HOTSPOT_MATCH },
{ OUI_GOOGLE, SLSI_NL80211_RSSI_REPORT_EVENT},
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
{ OUI_GOOGLE, SLSI_NL80211_LOGGER_RING_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_LOGGER_FW_DUMP_EVENT},
#endif
{ OUI_GOOGLE, SLSI_NL80211_NAN_RESPONSE_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_PUBLISH_TERMINATED_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_MATCH_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_MATCH_EXPIRED_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_SUBSCRIBE_TERMINATED_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_FOLLOWUP_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_DISCOVERY_ENGINE_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_DISABLED_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_RTT_RESULT_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_RTT_COMPLETE_EVENT},
{ OUI_SAMSUNG, SLSI_NL80211_VENDOR_ACS_EVENT},
{ OUI_SAMSUNG, SLSI_NL80211_VENDOR_FORWARD_BEACON},
{ OUI_SAMSUNG, SLSI_NL80211_VENDOR_FORWARD_BEACON_ABORT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_TRANSMIT_FOLLOWUP_STATUS},
{ OUI_GOOGLE, SLSI_NAN_EVENT_NDP_REQ},
{ OUI_GOOGLE, SLSI_NAN_EVENT_NDP_CFM},
{ OUI_GOOGLE, SLSI_NAN_EVENT_NDP_END},
{ OUI_SAMSUNG, SLSI_NL80211_VENDOR_RCL_CHANNEL_LIST_EVENT}
};
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0))
static const struct nla_policy slsi_no_policy[2] = {0};
static const struct nla_policy
slsi_wlan_vendor_acs_policy[SLSI_ACS_ATTR_MAX + 1] = {
[SLSI_ACS_ATTR_HW_MODE] = {.type = NLA_U8},
[SLSI_ACS_ATTR_HT_ENABLED] = {.type = NLA_FLAG},
[SLSI_ACS_ATTR_HT40_ENABLED] = {.type = NLA_FLAG},
[SLSI_ACS_ATTR_VHT_ENABLED] = {.type = NLA_FLAG },
[SLSI_ACS_ATTR_CHWIDTH] = {.type = NLA_U16},
[SLSI_ACS_ATTR_FREQ_LIST] = {.type = NLA_BINARY,
.len = (MAX_CHAN_VALUE_ACS * sizeof(u32)) },
};
static const struct nla_policy
slsi_wlan_vendor_lls_policy[LLS_ATTRIBUTE_MAX + 1] = {
[LLS_ATTRIBUTE_SET_MPDU_SIZE_THRESHOLD] = {.type = NLA_U32},
[LLS_ATTRIBUTE_SET_AGGR_STATISTICS_GATHERING] = {.type = NLA_U32},
[LLS_ATTRIBUTE_CLEAR_STOP_REQUEST_MASK] = {.type = NLA_U32},
[LLS_ATTRIBUTE_CLEAR_STOP_REQUEST] = {.type = NLA_U32},
};
static const struct nla_policy
slsi_wlan_vendor_start_keepalive_offload_policy[MKEEP_ALIVE_ATTRIBUTE_MAX + 1] = {
[MKEEP_ALIVE_ATTRIBUTE_ID] = {.type = NLA_U8},
[MKEEP_ALIVE_ATTRIBUTE_IP_PKT] = {.type = NLA_BINARY},
[MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN] = {.type = NLA_U16},
[MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC] = {.type = NLA_U32},
[MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR] = {.type = NLA_BINARY,
.len = ETH_ALEN},
[MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR] = {.type = NLA_BINARY,
.len = ETH_ALEN},
};
static const struct nla_policy
slsi_wlan_vendor_low_latency_policy[SLSI_NL_ATTRIBUTE_LATENCY_MAX + 1] = {
[SLSI_NL_ATTRIBUTE_LATENCY_MODE] = {.type = NLA_U8},
};
static const struct nla_policy
slsi_wlan_vendor_country_code_policy[SLSI_NL_ATTRIBUTE_COUNTRY_CODE_MAX + 1] = {
[SLSI_NL_ATTRIBUTE_COUNTRY_CODE] = {.type = NLA_BINARY},
};
static const struct nla_policy
slsi_wlan_vendor_roam_state_policy[SLSI_NL_ATTR_ROAM_MAX + 1] = {
[SLSI_NL_ATTR_ROAM_STATE] = {.type = NLA_U8},
};
static const struct nla_policy
slsi_wlan_vendor_rssi_monitor[SLSI_RSSI_MONITOR_ATTRIBUTE_MAX + 1] = {
[SLSI_RSSI_MONITOR_ATTRIBUTE_START] = {.type = NLA_U8},
[SLSI_RSSI_MONITOR_ATTRIBUTE_MIN_RSSI] = {.type = NLA_S8},
[SLSI_RSSI_MONITOR_ATTRIBUTE_MAX_RSSI] = {.type = NLA_S8},
};
static const struct nla_policy
slsi_wlan_vendor_rtt_policy[SLSI_RTT_ATTRIBUTE_MAX + 1] = {
[SLSI_RTT_ATTRIBUTE_TARGET_CNT] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_ID] = {.type = NLA_U16},
[SLSI_RTT_ATTRIBUTE_TARGET_INFO] = {.type = NLA_NESTED_ARRAY,
.len = SLSI_RTT_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_rtt_policy},
[SLSI_RTT_ATTRIBUTE_TARGET_MAC] = {.type = NLA_BINARY},
[SLSI_RTT_ATTRIBUTE_TARGET_TYPE] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_PEER] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_CHAN_FREQ] = {.type = NLA_U16},
[SLSI_RTT_ATTRIBUTE_TARGET_PERIOD] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_NUM_BURST] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_NUM_FTM_BURST] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTM] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTMR] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_BURST_DURATION] = {.type = NLA_U8},
[SLSI_RTT_ATTRIBUTE_TARGET_PREAMBLE] = {.type = NLA_U16},
[SLSI_RTT_ATTRIBUTE_TARGET_BW] = {.type = NLA_U16},
[SLSI_RTT_ATTRIBUTE_TARGET_LCI] = {.type = NLA_U16},
[SLSI_RTT_ATTRIBUTE_TARGET_LCR] = {.type = NLA_U16},
};
static const struct nla_policy
slsi_wlan_vendor_apf_filter_policy[SLSI_APF_ATTR_MAX + 1] = {
[SLSI_APF_ATTR_PROGRAM_LEN] = {.type = NLA_U32},
[SLSI_APF_ATTR_PROGRAM] = {.type = NLA_BINARY},
};
static const struct nla_policy
slsi_wlan_vendor_gscan_policy[GSCAN_ATTRIBUTE_MAX] = {
[GSCAN_ATTRIBUTE_NUM_BUCKETS] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BASE_PERIOD] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKETS_BAND] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKET_ID] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKET_PERIOD] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKET_CHANNELS] = {.type = NLA_NESTED},
[GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_REPORT_THRESHOLD] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_REPORT_THRESHOLD_NUM_SCANS] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKETS_BAND] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_REPORT_EVENTS] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKET_EXPONENT] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_CH_BUCKET_1] = {.type = NLA_NESTED,
.len = GSCAN_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_gscan_policy},
[GSCAN_ATTRIBUTE_CH_BUCKET_2] = {.type = NLA_NESTED,
.len = GSCAN_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_gscan_policy},
[GSCAN_ATTRIBUTE_CH_BUCKET_3] = {.type = NLA_NESTED,
.len = GSCAN_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_gscan_policy},
[GSCAN_ATTRIBUTE_CH_BUCKET_4] = {.type = NLA_NESTED,
.len = GSCAN_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_gscan_policy},
[GSCAN_ATTRIBUTE_CH_BUCKET_5] = {.type = NLA_NESTED,
.len = GSCAN_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_gscan_policy},
[GSCAN_ATTRIBUTE_CH_BUCKET_6] = {.type = NLA_NESTED,
.len = GSCAN_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_gscan_policy},
[GSCAN_ATTRIBUTE_CH_BUCKET_7] = {.type = NLA_NESTED,
.len = GSCAN_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_gscan_policy},
[GSCAN_ATTRIBUTE_CH_BUCKET_8] = {.type = NLA_NESTED,
.len = GSCAN_ATTRIBUTE_MAX,
.validation_data = slsi_wlan_vendor_gscan_policy},
[GSCAN_ATTRIBUTE_NUM_OF_RESULTS] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_NUM_BSSID] = {.type = NLA_U32},
[GSCAN_ATTRIBUTE_BLACKLIST_BSSID] = {.type = NLA_BINARY},
};
static const struct nla_policy
slsi_wlan_vendor_gscan_oui_policy[SLSI_NL_ATTRIBUTE_MAC_OUI_MAX] = {
[SLSI_NL_ATTRIBUTE_ND_OFFLOAD_VALUE] = {.type = NLA_U8},
[SLSI_NL_ATTRIBUTE_PNO_RANDOM_MAC_OUI] = {.type = NLA_BINARY},
};
static const struct nla_policy
slsi_wlan_vendor_epno_policy[SLSI_ATTRIBUTE_EPNO_MAX] = {
[SLSI_ATTRIBUTE_EPNO_MINIMUM_5G_RSSI] = {.type = NLA_U16},
[SLSI_ATTRIBUTE_EPNO_MINIMUM_2G_RSSI] = {.type = NLA_U16},
[SLSI_ATTRIBUTE_EPNO_INITIAL_SCORE_MAX] = {.type = NLA_U16},
[SLSI_ATTRIBUTE_EPNO_CUR_CONN_BONUS] = {.type = NLA_U8},
[SLSI_ATTRIBUTE_EPNO_SAME_NETWORK_BONUS] = {.type = NLA_U8},
[SLSI_ATTRIBUTE_EPNO_SECURE_BONUS] = {.type = NLA_U8},
[SLSI_ATTRIBUTE_EPNO_5G_BONUS] = {.type = NLA_U8},
[SLSI_ATTRIBUTE_EPNO_SSID_LIST] = {.type = NLA_NESTED},
[SLSI_ATTRIBUTE_EPNO_SSID_NUM] = {.type = NLA_U8},
};
static const struct nla_policy
slsi_wlan_vendor_epno_hs_policy[SLSI_ATTRIBUTE_EPNO_HS_MAX] = {
[SLSI_ATTRIBUTE_EPNO_HS_PARAM_LIST] = {.type = NLA_NESTED_ARRAY,
.len = SLSI_ATTRIBUTE_EPNO_HS_MAX,
.validation_data = slsi_wlan_vendor_epno_hs_policy},
[SLSI_ATTRIBUTE_EPNO_HS_NUM] = {.type = NLA_U8},
[SLSI_ATTRIBUTE_EPNO_HS_ID] = {.type = NLA_U32},
[SLSI_ATTRIBUTE_EPNO_HS_REALM] = {.type = NLA_BINARY},
[SLSI_ATTRIBUTE_EPNO_HS_CONSORTIUM_IDS] = {.type = NLA_BINARY},
[SLSI_ATTRIBUTE_EPNO_HS_PLMN] = {.type = NLA_BINARY},
};
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
static const struct nla_policy
slsi_wlan_vendor_enhanced_logging_policy[SLSI_ENHANCED_LOGGING_ATTRIBUTE_MAX] = {
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_NAME] = {.type = NLA_BINARY},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_VERBOSE_LEVEL] = {.type = NLA_U32},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_RING_FLAGS] = {.type = NLA_U32},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_LOG_MAX_INTERVAL] = {.type = NLA_U32},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_LOG_MIN_DATA_SIZE] = {.type = NLA_U32},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_DUMP_LEN] = {.type = NLA_U32},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_DUMP_DATA] = {.type = NLA_U64},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_DRIVER_DUMP_LEN] = {.type = NLA_U32},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_DRIVER_DUMP_DATA] = {.type = NLA_U64},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_DRIVER_VERSION] = {.type = NLA_BINARY},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_FW_VERSION] = {.type = NLA_BINARY},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_NUM] = {.type = NLA_U32},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_PKT_FATE_DATA] = {.type = NLA_U64},
};
static const struct nla_policy
slsi_wlan_vendor_wake_reason_stats_policy[SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_MAX] = {
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ] = {.type = NLA_U32},
[SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ] = {.type = NLA_U32},
};
#endif
#endif
static struct wiphy_vendor_command slsi_vendor_cmd[] = {
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_CAPABILITIES
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_gscan_get_capabilities
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_VALID_CHANNELS
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_gscan_get_valid_channel
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_ADD_GSCAN
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_gscan_add
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_DEL_GSCAN
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_gscan_del
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_SCAN_RESULTS
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_gscan_get_scan_results
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_GSCAN_OUI
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_gscan_set_oui
},
#ifdef CONFIG_SCSC_WLAN_KEY_MGMT_OFFLOAD
{
{
.vendor_id = OUI_SAMSUNG,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_key_mgmt_set_pmk
},
#endif
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_BSSID_BLACKLIST
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_set_bssid_blacklist
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_START_KEEP_ALIVE_OFFLOAD
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_start_keepalive_offload
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_STOP_KEEP_ALIVE_OFFLOAD
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_stop_keepalive_offload
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_EPNO_LIST
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_set_epno_ssid
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_HS_LIST
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_set_hs_params
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_RESET_HS_LIST
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_reset_hs_params
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_RSSI_MONITOR
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_set_rssi_monitor
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_LSTATS_SUBCMD_SET_STATS
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_lls_set_stats
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_LSTATS_SUBCMD_GET_STATS
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_lls_get_stats
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_LSTATS_SUBCMD_CLEAR_STATS
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_lls_clear_stats
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_FEATURE_SET
},
.flags = 0,
.doit = slsi_get_feature_set
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_COUNTRY_CODE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_set_country_code
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_CONFIGURE_ND_OFFLOAD
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_configure_nd_offload
},
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_START_LOGGING
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_start_logging
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_RESET_LOGGING
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_reset_logging
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_TRIGGER_FW_MEM_DUMP
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_trigger_fw_mem_dump
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_FW_MEM_DUMP
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_fw_mem_dump
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_TRIGGER_DRIVER_MEM_DUMP
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_trigger_driver_mem_dump
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_DRIVER_MEM_DUMP
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_driver_mem_dump
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_VERSION
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_version
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_RING_STATUS
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_ring_buffers_status
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_RING_DATA
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_ring_data
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_FEATURE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_logger_supported_feature_set
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_START_PKT_FATE_MONITORING
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_start_pkt_fate_monitoring
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_TX_PKT_FATES
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_tx_pkt_fates
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_RX_PKT_FATES
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_rx_pkt_fates
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_wake_reason_stats
},
#endif /* CONFIG_SCSC_WLAN_ENHANCED_LOGGING */
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_ENABLE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_enable
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_DISABLE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_disable
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_PUBLISH
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_publish
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_PUBLISHCANCEL
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_publish_cancel
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_SUBSCRIBE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_subscribe
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_SUBSCRIBECANCEL
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_subscribe_cancel
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_TXFOLLOWUP
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_transmit_followup
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_CONFIG
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_set_config
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_CAPABILITIES
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_get_capabilities
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INTERFACE_CREATE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_data_iface_create
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INTERFACE_DELETE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_data_iface_delete
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_REQUEST_INITIATOR
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_ndp_initiate
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INDICATION_RESPONSE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_ndp_respond
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_END
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_nan_ndp_end
},
#endif
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_GET_ROAMING_CAPABILITIES
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_get_roaming_capabilities
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_ROAMING_STATE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_set_roaming_state
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_RTT_GET_CAPABILITIES
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_rtt_get_capabilities
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_RTT_RANGE_START
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_rtt_set_config
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_RTT_RANGE_CANCEL
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_rtt_cancel_config
},
{
{
.vendor_id = OUI_SAMSUNG,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_ACS_INIT
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_acs_init
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_APF_GET_CAPABILITIES
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_apf_get_capabilities
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_APF_SET_FILTER
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_apf_set_filter
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_APF_READ_FILTER
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_apf_read_filter
},
{
{
.vendor_id = OUI_GOOGLE,
.subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_LATENCY_MODE
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_configure_latency_mode
}
};
void slsi_nl80211_vendor_deinit(struct slsi_dev *sdev)
{
SLSI_DBG2(sdev, SLSI_GSCAN, "De-initialise vendor command and events\n");
sdev->wiphy->vendor_commands = NULL;
sdev->wiphy->n_vendor_commands = 0;
sdev->wiphy->vendor_events = NULL;
sdev->wiphy->n_vendor_events = 0;
SLSI_DBG2(sdev, SLSI_GSCAN, "Gscan cleanup\n");
slsi_gscan_flush_scan_results(sdev);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0))
static void slsi_nll80211_vendor_init_policy(struct wiphy_vendor_command *slsi_vendor_cmd, int n_vendor_commands)
{
int i;
struct wiphy_vendor_command *vcmd;
for(i = 0; i < n_vendor_commands; i++) {
vcmd = &slsi_vendor_cmd[i];
switch (vcmd->info.subcmd) {
case SLSI_NL80211_VENDOR_SUBCMD_GET_CAPABILITIES:
case SLSI_NL80211_VENDOR_SUBCMD_DEL_GSCAN:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_GET_VALID_CHANNELS:
case SLSI_NL80211_VENDOR_SUBCMD_ADD_GSCAN:
case SLSI_NL80211_VENDOR_SUBCMD_GET_SCAN_RESULTS:
vcmd->policy = slsi_wlan_vendor_gscan_policy;
vcmd->maxattr = GSCAN_ATTRIBUTE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_SET_GSCAN_OUI:
vcmd->policy = slsi_wlan_vendor_gscan_oui_policy;
vcmd->maxattr = SLSI_NL_ATTRIBUTE_MAC_OUI_MAX;
break;
#ifdef CONFIG_SCSC_WLAN_KEY_MGMT_OFFLOAD
case SLSI_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
#endif
case SLSI_NL80211_VENDOR_SUBCMD_SET_BSSID_BLACKLIST:
vcmd->policy = slsi_wlan_vendor_gscan_policy;
vcmd->maxattr = GSCAN_ATTRIBUTE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_START_KEEP_ALIVE_OFFLOAD:
case SLSI_NL80211_VENDOR_SUBCMD_STOP_KEEP_ALIVE_OFFLOAD:
vcmd->policy = slsi_wlan_vendor_start_keepalive_offload_policy;
vcmd->maxattr = MKEEP_ALIVE_ATTRIBUTE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_SET_EPNO_LIST:
vcmd->policy = slsi_wlan_vendor_epno_policy;
vcmd->maxattr = SLSI_ATTRIBUTE_EPNO_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_SET_HS_LIST:
vcmd->policy = slsi_wlan_vendor_epno_hs_policy;
vcmd->maxattr = SLSI_ATTRIBUTE_EPNO_HS_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_RESET_HS_LIST:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_SET_RSSI_MONITOR:
vcmd->policy = slsi_wlan_vendor_rssi_monitor;
vcmd->maxattr = SLSI_RSSI_MONITOR_ATTRIBUTE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_LSTATS_SUBCMD_SET_STATS:
vcmd->policy = slsi_wlan_vendor_lls_policy;
vcmd->maxattr = LLS_ATTRIBUTE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_LSTATS_SUBCMD_GET_STATS:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_LSTATS_SUBCMD_CLEAR_STATS:
vcmd->policy = slsi_wlan_vendor_lls_policy;
vcmd->maxattr = LLS_ATTRIBUTE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_GET_FEATURE_SET:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_SET_COUNTRY_CODE:
vcmd->policy = slsi_wlan_vendor_country_code_policy;
vcmd->maxattr = SLSI_NL_ATTRIBUTE_COUNTRY_CODE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_CONFIGURE_ND_OFFLOAD:
vcmd->policy = slsi_wlan_vendor_gscan_oui_policy;
vcmd->maxattr = SLSI_NL_ATTRIBUTE_MAC_OUI_MAX;
break;
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
case SLSI_NL80211_VENDOR_SUBCMD_RESET_LOGGING:
case SLSI_NL80211_VENDOR_SUBCMD_TRIGGER_FW_MEM_DUMP:
case SLSI_NL80211_VENDOR_SUBCMD_TRIGGER_DRIVER_MEM_DUMP:
case SLSI_NL80211_VENDOR_SUBCMD_GET_RING_STATUS:
case SLSI_NL80211_VENDOR_SUBCMD_GET_FEATURE:
case SLSI_NL80211_VENDOR_SUBCMD_START_PKT_FATE_MONITORING:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_START_LOGGING:
case SLSI_NL80211_VENDOR_SUBCMD_GET_FW_MEM_DUMP:
case SLSI_NL80211_VENDOR_SUBCMD_GET_DRIVER_MEM_DUMP:
case SLSI_NL80211_VENDOR_SUBCMD_GET_VERSION:
case SLSI_NL80211_VENDOR_SUBCMD_GET_RING_DATA:
case SLSI_NL80211_VENDOR_SUBCMD_GET_TX_PKT_FATES:
case SLSI_NL80211_VENDOR_SUBCMD_GET_RX_PKT_FATES:
vcmd->policy = slsi_wlan_vendor_enhanced_logging_policy;
vcmd->maxattr = SLSI_ENHANCED_LOGGING_ATTRIBUTE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS:
vcmd->policy = slsi_wlan_vendor_wake_reason_stats_policy;
vcmd->maxattr = SLSI_ENHANCED_LOGGING_ATTRIBUTE_WAKE_STATS_MAX;
break;
#endif /* CONFIG_SCSC_WLAN_ENHANCED_LOGGING */
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
case SLSI_NL80211_VENDOR_SUBCMD_NAN_ENABLE:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DISABLE:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_PUBLISH:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_PUBLISHCANCEL:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_SUBSCRIBE:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_SUBSCRIBECANCEL:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_TXFOLLOWUP:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_CONFIG:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_CAPABILITIES:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INTERFACE_CREATE:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INTERFACE_DELETE:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_REQUEST_INITIATOR:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INDICATION_RESPONSE:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_END:
vcmd->policy = slsi_no_policy;
vcmd->maxattr = 0;
break;
#endif
case SLSI_NL80211_VENDOR_SUBCMD_GET_ROAMING_CAPABILITIES:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_SET_ROAMING_STATE:
vcmd->policy = slsi_wlan_vendor_roam_state_policy;
vcmd->maxattr = SLSI_NL_ATTR_ROAM_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_RTT_GET_CAPABILITIES:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_RTT_RANGE_START:
case SLSI_NL80211_VENDOR_SUBCMD_RTT_RANGE_CANCEL:
vcmd->policy = slsi_wlan_vendor_rtt_policy;
vcmd->maxattr = SLSI_RTT_ATTRIBUTE_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_ACS_INIT:
vcmd->policy = slsi_wlan_vendor_acs_policy;
vcmd->maxattr = SLSI_ACS_ATTR_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_APF_GET_CAPABILITIES:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_APF_SET_FILTER:
vcmd->policy = slsi_wlan_vendor_apf_filter_policy;
vcmd->maxattr = SLSI_APF_ATTR_MAX;
break;
case SLSI_NL80211_VENDOR_SUBCMD_APF_READ_FILTER:
vcmd->policy = VENDOR_CMD_RAW_DATA;
vcmd->maxattr = 0;
break;
case SLSI_NL80211_VENDOR_SUBCMD_SET_LATENCY_MODE:
vcmd->policy = slsi_wlan_vendor_low_latency_policy;
vcmd->maxattr = SLSI_NL_ATTRIBUTE_LATENCY_MAX;
break;
}
}
}
#endif
void slsi_nl80211_vendor_init(struct slsi_dev *sdev)
{
int i;
SLSI_DBG2(sdev, SLSI_GSCAN, "Init vendor command and events\n");
sdev->wiphy->vendor_commands = (const struct wiphy_vendor_command *)slsi_vendor_cmd;
sdev->wiphy->n_vendor_commands = ARRAY_SIZE(slsi_vendor_cmd);
sdev->wiphy->vendor_events = slsi_vendor_events;
sdev->wiphy->n_vendor_events = ARRAY_SIZE(slsi_vendor_events);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0))
slsi_nll80211_vendor_init_policy(slsi_vendor_cmd, sdev->wiphy->n_vendor_commands);
#endif
for (i = 0; i < SLSI_GSCAN_MAX_BUCKETS; i++)
sdev->bucket[i].scan_id = (SLSI_GSCAN_SCAN_ID_START + i);
for (i = 0; i < SLSI_GSCAN_HASH_TABLE_SIZE; i++)
sdev->gscan_hash_table[i] = NULL;
INIT_LIST_HEAD(&sdev->hotlist_results);
}