kernel_samsung_a53x/drivers/net/wireless/scsc/cfg80211_ops.c

4629 lines
153 KiB
C
Raw Normal View History

2024-06-15 16:02:09 -03:00
/***************************************************************************
*
* Copyright (c) 2014 - 2021 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/version.h>
#include <net/cfg80211.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 "scsc_wifilogger_ring_pktfate_api.h"
#include "scsc_wifilogger_ring_connectivity_api.h"
#include "log2us.h"
#include "ba.h"
#include <scsc/scsc_warn.h>
#ifdef CONFIG_SCSC_WLAN_ANDROID
#include "scsc_wifilogger_rings.h"
#endif
#include "nl80211_vendor.h"
/* Ext capab is decided by firmware. But there are certain bits
* which are set by supplicant. So we set the capab and mask in
* such way so that supplicant sets only the bits our solution supports
*/
static const u8 slsi_extended_cap[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const u8 slsi_extended_cap_mask[] = {
0xFF, 0xFF, 0xF7, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
#define SLSI_DEFAULT_CH_MAX (64 + SLSI_NUM_6GHZ_CHANNELS)
#else
#define SLSI_DEFAULT_CH_MAX 64
#endif
struct slsi_scan_params {
struct ieee80211_channel *channels[SLSI_DEFAULT_CH_MAX];
int chan_count;
int scan_type;
u8 *scan_ie;
size_t scan_ie_len;
int strip_wsc;
int strip_p2p;
};
static uint keep_alive_period = SLSI_P2PGO_KEEP_ALIVE_PERIOD_SEC;
module_param(keep_alive_period, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(keep_alive_period, "default is 10 seconds");
static bool slsi_is_mhs_active(struct slsi_dev *sdev)
{
struct net_device *mhs_dev = sdev->netdev_ap;
struct netdev_vif *ndev_vif;
bool ret;
if (mhs_dev) {
ndev_vif = netdev_priv(mhs_dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
ret = ndev_vif->activated;
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return ret;
}
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
struct wireless_dev *slsi_add_virtual_intf(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
struct vif_params *params)
{
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
struct wireless_dev *slsi_add_virtual_intf(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
#else
struct wireless_dev *slsi_add_virtual_intf(struct wiphy *wiphy,
const char *name,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
#endif
struct net_device *dev = NULL;
struct netdev_vif *ndev_vif = NULL;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 11, 0))
SLSI_UNUSED_PARAMETER(flags);
#endif
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Intf name:%s, type:%d, macaddr:%pM\n", name, type, params->macaddr);
if (slsi_is_mhs_active(sdev)) {
SLSI_ERR(sdev, "MHS is active. cannot add new interface\n");
return ERR_PTR(-EOPNOTSUPP);
}
dev = slsi_dynamic_interface_create(wiphy, name, type, params);
if (!dev)
goto exit_with_error;
ndev_vif = netdev_priv(dev);
return &ndev_vif->wdev;
exit_with_error:
return ERR_PTR(-ENODEV);
}
int slsi_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
{
struct net_device *dev = wdev->netdev;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
if (WARN_ON(!dev))
return -EINVAL;
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Dev name:%s\n", dev->name);
SLSI_MUTEX_LOCK(sdev->netdev_add_remove_mutex);
slsi_stop_net_dev(sdev, dev);
slsi_netif_remove_locked(sdev, dev);
if (dev == sdev->netdev_ap)
rcu_assign_pointer(sdev->netdev_ap, NULL);
if (!sdev->netdev[SLSI_NET_INDEX_P2PX_SWLAN])
rcu_assign_pointer(sdev->netdev[SLSI_NET_INDEX_P2PX_SWLAN], sdev->netdev_ap);
if (dev == sdev->netdev_p2p)
rcu_assign_pointer(sdev->netdev_p2p, NULL);
SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex);
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
int slsi_change_virtual_intf(struct wiphy *wiphy,
struct net_device *dev,
enum nl80211_iftype type,
struct vif_params *params)
{
#else
int slsi_change_virtual_intf(struct wiphy *wiphy,
struct net_device *dev,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
#endif
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0;
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 11, 0))
SLSI_UNUSED_PARAMETER(flags);
#endif
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG1(dev, SLSI_CFG80211, "type:%u, iftype:%d\n", type, ndev_vif->iftype);
if (WARN_ON(ndev_vif->activated)) {
r = -EINVAL;
goto exit;
}
switch (type) {
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_MONITOR:
ndev_vif->iftype = type;
dev->ieee80211_ptr->iftype = type;
if (params)
dev->ieee80211_ptr->use_4addr = params->use_4addr;
break;
default:
r = -EINVAL;
break;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_add_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_index, bool pairwise, const u8 *mac_addr,
struct key_params *params)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer = NULL;
int r = 0;
u16 key_type = FAPI_KEYTYPE_GROUP;
if (WARN_ON(pairwise && !mac_addr))
return -EINVAL;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG2(dev, SLSI_CFG80211, "(key_index:%d, pairwise:%d, address:%pM, cipher:0x%.8X, key_len:%d,"
"vif_type:%d)\n", key_index, pairwise, mac_addr, params->cipher, params->key_len,
ndev_vif->vif_type);
if (!ndev_vif->activated) {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "vif not active\n");
goto exit;
}
if (params->cipher == WLAN_CIPHER_SUITE_PMK) {
r = slsi_mlme_set_key(sdev, dev, key_index, key_type, mac_addr, params);
goto exit;
}
if (mac_addr && pairwise) {
/* All Pairwise Keys will have a peer record. */
peer = slsi_get_peer_from_mac(sdev, dev, mac_addr);
if (peer)
mac_addr = peer->address;
} else if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION) {
/* Sta Group Key will use the peer address */
peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET);
if (peer)
mac_addr = peer->address;
} else if (ndev_vif->vif_type == FAPI_VIFTYPE_AP && !pairwise)
/* AP Group Key will use the Interface address */
mac_addr = dev->dev_addr;
else {
r = -EINVAL;
goto exit;
}
/*Treat WEP key as pairwise key*/
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION &&
(params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
params->cipher == WLAN_CIPHER_SUITE_WEP104) && peer) {
u8 bc_mac_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
SLSI_NET_DBG3(dev, SLSI_CFG80211, "WEP Key: store key\n");
r = slsi_mlme_set_key(sdev, dev, key_index, FAPI_KEYTYPE_WEP, bc_mac_addr, params);
if (r == FAPI_RESULTCODE_SUCCESS) {
ndev_vif->sta.wep_key_set = true;
/* if static ip is set before connection, after setting keys enable powersave. */
if (ndev_vif->ipaddress)
slsi_mlme_powermgt(sdev, dev, ndev_vif->set_power_mode);
} else {
SLSI_NET_ERR(dev, "Error adding WEP key\n");
}
goto exit;
}
if (pairwise) {
key_type = FAPI_KEYTYPE_PAIRWISE;
if (WARN_ON(!peer)) {
r = -EINVAL;
goto exit;
}
} else if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC || params->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
params->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) {
key_type = FAPI_KEYTYPE_IGTK;
}
if (key_index == 6 || key_index == 7)
key_type = FAPI_KEYTYPE_BIGTK;
if (WARN(!mac_addr, "mac_addr not defined\n")) {
r = -EINVAL;
goto exit;
}
if (!(ndev_vif->vif_type == FAPI_VIFTYPE_AP && key_index == 4)) {
r = slsi_mlme_set_key(sdev, dev, key_index, key_type, mac_addr, params);
if (r) {
SLSI_NET_ERR(dev, "error in adding key (key_type: %d)\n", key_type);
goto exit;
}
}
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION) {
ndev_vif->sta.eap_hosttag = 0xFFFF;
/* if static IP is set before connection, after setting keys enable powersave. */
if (ndev_vif->ipaddress)
slsi_mlme_powermgt(sdev, dev, ndev_vif->set_power_mode);
}
if (key_type == FAPI_KEYTYPE_GROUP) {
ndev_vif->sta.group_key_set = true;
ndev_vif->ap.cipher = params->cipher;
} else if (key_type == FAPI_KEYTYPE_PAIRWISE) {
if (peer) {
slsi_ba_replay_reset_pn(dev, peer);
peer->pairwise_key_set = true;
}
}
if (peer) {
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION) {
if (pairwise && params->cipher == WLAN_CIPHER_SUITE_SMS4) {
slsi_mlme_connect_resp(sdev, dev);
if (ndev_vif->ipaddress)
slsi_ip_address_changed(sdev, dev, ndev_vif->ipaddress);
slsi_set_acl(sdev, dev);
slsi_set_packet_filters(sdev, dev);
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED);
}
if (ndev_vif->sta.gratuitous_arp_needed) {
ndev_vif->sta.gratuitous_arp_needed = false;
slsi_send_gratuitous_arp(sdev, dev);
}
} else if (ndev_vif->vif_type == FAPI_VIFTYPE_AP && pairwise) {
slsi_mlme_connected_resp(sdev, dev, peer->aid);
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED);
peer->connected_state = SLSI_STA_CONN_STATE_CONNECTED;
if (ndev_vif->iftype == NL80211_IFTYPE_P2P_GO)
ndev_vif->ap.p2p_gc_keys_set = true;
}
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_del_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_index, bool pairwise, const u8 *mac_addr)
{
SLSI_UNUSED_PARAMETER(wiphy);
SLSI_UNUSED_PARAMETER(key_index);
SLSI_UNUSED_PARAMETER(pairwise);
SLSI_UNUSED_PARAMETER(mac_addr);
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_DELETEKEYS.request\n");
return -EOPNOTSUPP;
}
return 0;
}
int slsi_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct netdev_vif *ap_dev_vif;
struct slsi_dev *sdev = ndev_vif->sdev;
struct net_device *wlan_dev;
struct netdev_vif *ndev_sta_vif;
int result = 0;
u16 center_freq = 0;
u16 chan_info = 0;
u16 current_chan_info = 0;
struct cfg80211_chan_def chandef = params->chandef;
struct cfg80211_chan_def current_chandef = ndev_vif->chandef_saved;
struct net_device *ap_dev = NULL;
struct ieee80211_channel *chan = chandef.chan;
int width = chandef.width;
wlan_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (ndev_vif->iftype != NL80211_IFTYPE_AP) {
SLSI_NET_ERR(dev, "AP Mode is not active\n");
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -EPERM;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
ndev_sta_vif = netdev_priv(wlan_dev);
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
if (SLSI_IS_VIF_INDEX_MHS(sdev, ndev_vif)) {
if (ndev_sta_vif) {
SLSI_MUTEX_LOCK(ndev_sta_vif->vif_mutex);
if (ndev_sta_vif->activated && ndev_sta_vif->vif_type == FAPI_VIFTYPE_STATION &&
(ndev_sta_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTING ||
ndev_sta_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED)) {
SLSI_NET_ERR(dev, "Sta is in connected state (Chan Switch not allowed in WiFi Sharing mode)\n");
SLSI_MUTEX_UNLOCK(ndev_sta_vif->vif_mutex);
return -EPERM;
}
SLSI_MUTEX_UNLOCK(ndev_sta_vif->vif_mutex);
}
}
#endif
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (current_chandef.chan->center_freq == chan->center_freq) {
current_chan_info = slsi_get_chann_info(sdev, &current_chandef);
if (current_chan_info == slsi_get_chann_info(sdev, &chandef)) {
SLSI_NET_ERR(dev, "Channel Switch requested on current channel freq-> %u and Chan info %d\n", current_chandef.chan->center_freq, current_chan_info);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -EPERM;
}
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
ap_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_P2PX_SWLAN);
chan_info = slsi_get_chann_info(sdev, &chandef);
center_freq = chan->center_freq;
if (width != NL80211_CHAN_WIDTH_20_NOHT && width != NL80211_CHAN_WIDTH_20)
center_freq = slsi_get_center_freq1(sdev, chan_info, center_freq);
ap_dev_vif = netdev_priv(ap_dev);
SLSI_MUTEX_LOCK(ap_dev_vif->vif_mutex);
result = slsi_mlme_channel_switch(sdev, ap_dev, center_freq, chan_info);
SLSI_MUTEX_UNLOCK(ap_dev_vif->vif_mutex);
return result;
}
int slsi_get_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_index, bool pairwise, const u8 *mac_addr,
void *cookie,
void (*callback)(void *cookie, struct key_params *))
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct key_params params;
#define SLSI_MAX_KEY_SIZE 8 /*used only for AP case, so WAPI not considered*/
u8 key_seq[SLSI_MAX_KEY_SIZE] = { 0 };
int r = 0;
SLSI_UNUSED_PARAMETER(mac_addr);
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_GET_KEY_SEQUENCE.request\n");
return -EOPNOTSUPP;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG2(dev, SLSI_CFG80211, "(key_index:%d, pairwise:%d, mac_addr:%pM, vif_type:%d)\n", key_index,
pairwise, mac_addr, ndev_vif->vif_type);
if (!ndev_vif->activated) {
SLSI_NET_ERR(dev, "vif not active\n");
r = -EINVAL;
goto exit;
}
/* The get_key call is expected only for AP vif with Group Key type */
if (FAPI_VIFTYPE_AP != ndev_vif->vif_type) {
SLSI_NET_ERR(dev, "Invalid vif type: %d\n", ndev_vif->vif_type);
r = -EINVAL;
goto exit;
}
if (pairwise) {
SLSI_NET_ERR(dev, "Invalid key type\n");
r = -EINVAL;
goto exit;
}
memset(&params, 0, sizeof(params));
/* Update params with sequence number, key field would be updated NULL */
params.key = NULL;
params.key_len = 0;
params.cipher = ndev_vif->ap.cipher;
if (!(ndev_vif->vif_type == FAPI_VIFTYPE_AP && key_index == 4)) {
r = slsi_mlme_get_key(sdev, dev, key_index, FAPI_KEYTYPE_GROUP, key_seq, &params.seq_len);
if (!r) {
params.seq = key_seq;
callback(cookie, &params);
}
}
#undef SLSI_MAX_KEY_SIZE
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
static bool slsi_is_p2p_scan_req(struct cfg80211_scan_request *request)
{
if (request->ie &&
cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, request->ie, request->ie_len) &&
request->ssids && SLSI_IS_P2P_SSID(request->ssids[0].ssid, request->ssids[0].ssid_len))
return true;
return false;
}
static void slsi_p2p_cancel_unset_channel(struct slsi_dev *sdev, struct cfg80211_scan_request *request)
{
struct net_device *dev = request->wdev->netdev;
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (!slsi_is_p2p_scan_req(request))
return;
if (ndev_vif->drv_in_p2p_procedure)
return;
if (delayed_work_pending(&ndev_vif->unsync.unset_channel_expiry_work)) {
cancel_delayed_work(&ndev_vif->unsync.unset_channel_expiry_work);
slsi_mlme_unset_channel_req(sdev, dev);
ndev_vif->driver_channel = 0;
}
}
static void slsi_set_wlan_scan_type_param(struct slsi_dev *sdev, struct net_device *dev,
struct slsi_scan_params *params)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
params->scan_type = FAPI_SCANTYPE_FULL_SCAN;
ndev_vif->unsync.slsi_p2p_continuous_fullscan = false;
if (params->chan_count == 1) {
params->scan_type = FAPI_SCANTYPE_SINGLE_CHANNEL_SCAN;
return;
}
if (sdev->initial_scan) {
sdev->initial_scan = false;
if (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTING &&
ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)
params->scan_type = FAPI_SCANTYPE_INITIAL_SCAN;
}
}
static void slsi_set_p2p_social_ch_param(struct cfg80211_scan_request *request,
struct slsi_scan_params *params)
{
int count = 0, chann = 0, i = 0;
for (i = 0; i < request->n_channels; i++) {
chann = params->channels[i]->hw_value & 0xFF;
if (SLSI_P2P_IS_SOCAIL_CHAN(chann)) {
params->channels[count] = request->channels[i];
count++;
}
}
params->chan_count = count;
}
static void slsi_set_p2p_scan_type_param(struct slsi_dev *sdev, struct net_device *dev,
struct cfg80211_scan_request *request,
struct slsi_scan_params *params)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (sdev->p2p_state == P2P_GROUP_FORMED_GO)
ndev_vif->unsync.slsi_p2p_continuous_fullscan = false;
/* In supplicant during joining procedure the P2P GO scan
* with GO's operating channel comes on P2P device. Hence added the
* check for n_channels as 1
*/
if (request->n_channels == SLSI_P2P_SOCIAL_CHAN_COUNT || request->n_channels == 1) {
params->scan_type = FAPI_SCANTYPE_P2P_SCAN_SOCIAL;
ndev_vif->unsync.slsi_p2p_continuous_fullscan = false;
} else if (request->n_channels > SLSI_P2P_SOCIAL_CHAN_COUNT) {
if (!ndev_vif->unsync.slsi_p2p_continuous_fullscan) {
params->scan_type = FAPI_SCANTYPE_P2P_SCAN_FULL;
ndev_vif->unsync.slsi_p2p_continuous_fullscan = true;
} else {
params->scan_type = FAPI_SCANTYPE_P2P_SCAN_SOCIAL;
ndev_vif->unsync.slsi_p2p_continuous_fullscan = false;
slsi_set_p2p_social_ch_param(request, params);
}
}
}
static void slsi_set_scan_type_param(struct slsi_dev *sdev, struct cfg80211_scan_request *request,
struct slsi_scan_params *params)
{
struct net_device *dev = request->wdev->netdev;
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif))
slsi_set_wlan_scan_type_param(sdev, dev, params);
if (slsi_is_p2p_scan_req(request))
slsi_set_p2p_scan_type_param(sdev, dev, request, params);
}
static bool slsi_is_p2p_wsc_ie_strip_condition(struct net_device *dev, struct cfg80211_scan_request *request,
struct slsi_scan_params *params)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
#ifdef CONFIG_SCSC_WLAN_DUAL_STATION
if ((SLSI_IS_VIF_INDEX_WLAN(ndev_vif) || ndev_vif->ifnum == SLSI_NET_INDEX_P2PX_SWLAN) && request->ie &&
!(params->scan_type == FAPI_SCANTYPE_P2P_SCAN_SOCIAL || params->scan_type == FAPI_SCANTYPE_P2P_SCAN_FULL))
return true;
#else
if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif) && request->ie)
return true;
#endif
return false;
}
static void slsi_scan_set_p2p_wps_strip_param(const u8 *request_ie, size_t request_ie_len,
struct slsi_scan_params *params)
{
const u8 *ie;
/* Supplicant adds wsc and p2p in Station scan at the end of scan request ie.
* for non-wps case remove both wps and p2p IEs
* for wps case remove only p2p IE
*/
ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPS, request_ie, request_ie_len);
if (ie && ie[1] > SLSI_WPS_REQUEST_TYPE_POS) {
/* Check whether scan is wps_scan or not, if not a wps_scan set strip_wsc to true
* to strip WPS IE
*/
if (ie[SLSI_WPS_REQUEST_TYPE_POS] == SLSI_WPS_REQUEST_TYPE_ENROLEE_INFO_ONLY)
params->strip_wsc = true;
}
ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, request_ie, request_ie_len);
if (ie)
params->strip_p2p = true;
}
static size_t slsi_strip_wsc_p2p_ie(const u8 *src_ie, size_t src_ie_len, u8 *dest_ie, bool strip_wsc, bool strip_p2p)
{
const u8 *ie;
const u8 *next_ie;
size_t dest_ie_len = 0;
if (!dest_ie || !(strip_p2p || strip_wsc))
return dest_ie_len;
for (ie = src_ie; (ie - src_ie) < src_ie_len; ie = next_ie) {
next_ie = ie + ie[1] + 2;
if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] > 4) {
int i;
unsigned int oui = 0;
for (i = 0; i < 4; i++)
oui = (oui << 8) | ie[5 - i];
if (strip_wsc && oui == SLSI_WPS_OUI_PATTERN)
continue;
if (strip_p2p && oui == SLSI_P2P_OUI_PATTERN)
continue;
}
if (next_ie - src_ie <= src_ie_len) {
memcpy(dest_ie + dest_ie_len, ie, ie[1] + 2);
dest_ie_len += ie[1] + 2;
}
}
return dest_ie_len;
}
static int slsi_alloc_scan_ie_param(struct net_device *dev, const u8 *req_ie, size_t req_ie_len,
struct slsi_scan_params *params)
{
if (params->strip_wsc || params->strip_p2p) {
params->scan_ie = kmalloc(req_ie_len, GFP_KERNEL);
if (!params->scan_ie) {
SLSI_NET_INFO(dev, "Out of memory for scan IEs\n");
return -ENOMEM;
}
params->scan_ie_len = slsi_strip_wsc_p2p_ie(req_ie, req_ie_len,
params->scan_ie, params->strip_wsc, params->strip_p2p);
} else {
params->scan_ie = (u8 *)req_ie;
params->scan_ie_len = req_ie_len;
}
return 0;
}
static void slsi_free_scan_ie_param(struct slsi_scan_params *params)
{
if (params->strip_p2p || params->strip_wsc)
kfree(params->scan_ie);
}
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
static void slsi_set_mac_randomization(struct slsi_dev *sdev, struct cfg80211_scan_request *request,
struct slsi_scan_params *params)
{
struct net_device *dev = request->wdev->netdev;
u8 mac_addr_mask[ETH_ALEN] = {0xFF};
bool wps_sta = false;
int r = 0;
const u8 *ie;
ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPS, request->ie, request->ie_len);
if (ie && ie[1] > SLSI_WPS_REQUEST_TYPE_POS)
if (ie[SLSI_WPS_REQUEST_TYPE_POS] != SLSI_WPS_REQUEST_TYPE_ENROLEE_INFO_ONLY)
wps_sta = true;
/* If Supplicant triggers WPS scan on station interface,
* mac radomization for scan should be disabled to avoid WPS overlap.
* Firmware also disables Mac Randomization for WPS Scan.
*/
if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR && !wps_sta) {
if (sdev->fw_mac_randomization_enabled) {
memcpy(sdev->scan_mac_addr, request->mac_addr, ETH_ALEN);
r = slsi_set_mac_randomisation_mask(sdev, request->mac_addr_mask);
if (!r)
sdev->scan_addr_set = 1;
} else {
SLSI_NET_INFO(dev, "Mac Randomization is not enabled in Firmware\n");
sdev->scan_addr_set = 0;
}
} else if (sdev->scan_addr_set) {
memset(mac_addr_mask, 0xFF, ETH_ALEN);
r = slsi_set_mac_randomisation_mask(sdev, mac_addr_mask);
sdev->scan_addr_set = 0;
}
}
#endif
#if defined(CONFIG_SLSI_WLAN_STA_FWD_BEACON) && (defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION >= 10)
static void slsi_set_sta_forward_beacon(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
int ret = 0;
if (!ndev_vif->is_wips_running)
return;
if (ndev_vif->vif_type != FAPI_VIFTYPE_STATION)
return;
if (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)
return;
SLSI_NET_DBG3(dev, SLSI_CFG80211, "Scan invokes DRIVER_BCN_ABORT\n");
ret = slsi_mlme_set_forward_beacon(sdev, dev, FAPI_ACTION_STOP);
if (ret < 0)
ret = slsi_send_forward_beacon_abort_vendor_event(sdev, dev,
SLSI_FORWARD_BEACON_ABORT_REASON_SCANNING);
}
#endif
static void slsi_update_state_scan_in_device_role(struct slsi_dev *sdev, struct net_device *dev,
struct slsi_scan_params *params)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (!SLSI_IS_VIF_INDEX_P2P(ndev_vif) || SLSI_IS_P2P_GROUP_STATE(sdev))
return;
if (params->scan_type == FAPI_SCANTYPE_P2P_SCAN_SOCIAL)
SLSI_P2P_STATE_CHANGE(sdev, P2P_SCANNING);
}
static void slsi_scan_save_probe_req_ies(struct cfg80211_scan_request *request,
struct slsi_scan_params *params)
{
struct net_device *dev = request->wdev->netdev;
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (SLSI_IS_VIF_INDEX_P2P(ndev_vif) || !params->scan_ie_len)
return;
kfree(ndev_vif->probe_req_ies);
ndev_vif->probe_req_ies = kmalloc(request->ie_len, GFP_KERNEL);
if (!ndev_vif->probe_req_ies) { /* Don't fail, continue as it would still work */
ndev_vif->probe_req_ie_len = 0;
} else {
ndev_vif->probe_req_ie_len = params->scan_ie_len;
memcpy(ndev_vif->probe_req_ies, params->scan_ie, params->scan_ie_len);
}
}
static void slsi_abort_hw_scan(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
SLSI_NET_INFO(dev, "Abort on-going scan, vif_index:%d,"
"ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req:%p\n", ndev_vif->ifnum,
ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req);
if (ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req) {
(void)slsi_mlme_del_scan(sdev, dev, ndev_vif->ifnum << 8 | SLSI_SCAN_HW_ID, false);
slsi_scan_complete(sdev, dev, SLSI_SCAN_HW_ID, false, false);
}
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
}
void slsi_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev)
{
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
slsi_abort_hw_scan(sdev, dev);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
int slsi_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *request)
{
struct net_device *dev = NULL;
struct netdev_vif *ndev_vif = NULL;
struct slsi_dev *sdev = NULL;
int r = 0, i = 0;
struct slsi_scan_params params = {
.scan_type = FAPI_SCANTYPE_FULL_SCAN, .chan_count = 0,
.strip_wsc = false, .strip_p2p = false
};
if (!request->wdev || !request->wdev->netdev) {
SLSI_ERR_NODEV("wdev or net_dev NULL\n");
return -EINVAL;
}
dev = request->wdev->netdev;
ndev_vif = netdev_priv(dev);
sdev = SDEV_FROM_WIPHY(wiphy);
if (slsi_is_test_mode_enabled()) {
SLSI_NET_WARN(dev, "not supported in WlanLite mode\n");
return -EOPNOTSUPP;
}
/* Reject scan request if Group Formation is in progress */
if (sdev->p2p_state == P2P_ACTION_FRAME_TX_RX) {
SLSI_NET_INFO(dev, "Scan received in P2P Action Frame Tx/Rx state - Reject (exp_frame=%s)\n",
slsi_pa_subtype_text((int)ndev_vif->mgmt_tx_data.exp_frame));
return -EBUSY;
}
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
if (ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req) {
SLSI_NET_INFO(dev, "Rejecting scan request as last scan is still running\n");
r = -EBUSY;
goto exit;
}
#if defined(CONFIG_SLSI_WLAN_STA_FWD_BEACON) && (defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION >= 10)
slsi_set_sta_forward_beacon(sdev, dev);
#endif
SLSI_NET_DBG3(dev, SLSI_CFG80211, "channels:%d, ssids:%d, ie_len:%d, vif_index:%d\n", request->n_channels,
request->n_ssids, (int)request->ie_len, ndev_vif->ifnum);
slsi_p2p_cancel_unset_channel(sdev, request);
for (i = 0; i < request->n_channels; i++)
params.channels[i] = request->channels[i];
params.chan_count = request->n_channels;
slsi_set_scan_type_param(sdev, request, &params);
if (slsi_is_p2p_wsc_ie_strip_condition(dev, request, &params))
slsi_scan_set_p2p_wps_strip_param(request->ie, request->ie_len, &params);
r = slsi_alloc_scan_ie_param(dev, request->ie, request->ie_len, &params);
if (r < 0)
goto exit;
/* Flush out any outstanding single scan timeout work */
cancel_delayed_work(&ndev_vif->scan_timeout_work);
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan = false;
slsi_purge_scan_results(ndev_vif, SLSI_SCAN_HW_ID);
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
slsi_set_mac_randomization(sdev, request, &params);
#endif
r = slsi_mlme_add_scan(sdev,
dev,
params.scan_type,
FAPI_REPORTMODE_REAL_TIME,
request->n_ssids,
request->ssids,
params.chan_count,
params.channels,
NULL,
params.scan_ie,
params.scan_ie_len,
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
request->n_6ghz_params,
request->scan_6ghz_params,
#endif
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan);
if (r == 0) {
ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req = request;
/* if delayed work is already scheduled, queue delayed work fails. So set
* requeue_timeout_work flag to enqueue delayed work in the timeout handler
*/
if (queue_delayed_work(sdev->device_wq, &ndev_vif->scan_timeout_work,
msecs_to_jiffies(SLSI_FW_SCAN_DONE_TIMEOUT_MSEC)))
ndev_vif->scan[SLSI_SCAN_HW_ID].requeue_timeout_work = false;
else
ndev_vif->scan[SLSI_SCAN_HW_ID].requeue_timeout_work = true;
slsi_update_state_scan_in_device_role(sdev, dev, &params);
slsi_scan_save_probe_req_ies(request, &params);
} else {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "add_scan error: %d\n", r);
r = -EIO;
}
slsi_free_scan_ie_param(&params);
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return r;
}
int slsi_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_sched_scan_request *request)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int r;
struct slsi_scan_params params;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_ADD_SCAN.request\n");
return -EOPNOTSUPP;
}
/* Allow sched_scan only on wlan0. For P2PCLI interface, sched_scan might get requested following a
* wlan0 scan and its results being shared to sibling interfaces. Reject sched_scan for other interfaces.
*/
if (!SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) {
SLSI_NET_INFO(dev, "Scheduled scan req received on vif %d - Reject\n", ndev_vif->ifnum);
return -EINVAL;
}
/* Unlikely to get a schedule scan while Group formation is in progress.
* In case it is requested, it will be rejected.
*/
if (sdev->p2p_state == P2P_ACTION_FRAME_TX_RX) {
SLSI_NET_INFO(dev, "Scheduled scan req received in P2P Action Frame Tx/Rx state - Reject\n");
return -EBUSY;
}
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
SLSI_NET_INFO(dev, "channels:%d, ssids:%d, ie_len:%d, vif_index:%d, scan_plans:%d\n", request->n_channels,
request->n_ssids, (int)request->ie_len, ndev_vif->ifnum, request->n_scan_plans);
if (ndev_vif->scan[SLSI_SCAN_HW_ID].sched_req) {
r = -EBUSY;
goto exit;
}
memset(&params, 0, sizeof(struct slsi_scan_params));
if (request->ie)
slsi_scan_set_p2p_wps_strip_param(request->ie, request->ie_len, &params);
r = slsi_alloc_scan_ie_param(dev, request->ie, request->ie_len, &params);
if (r != 0)
goto exit;
slsi_purge_scan_results(ndev_vif, SLSI_SCAN_SCHED_ID);
r = slsi_mlme_add_sched_scan(sdev, dev, request, params.scan_ie, params.scan_ie_len);
ndev_vif->scan[SLSI_SCAN_SCHED_ID].sched_req = request;
slsi_free_scan_ie_param(&params);
if (r != 0) {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "add_scan error: %d\n", r);
ndev_vif->scan[SLSI_SCAN_SCHED_ID].sched_req = NULL;
r = -EIO;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return r;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
int slsi_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, u64 reqid)
{
#else
int slsi_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
{
#endif
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int r = 0;
SLSI_UNUSED_PARAMETER(reqid);
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
SLSI_NET_DBG1(dev, SLSI_CFG80211, "vif_index:%d", ndev_vif->ifnum);
if (!ndev_vif->scan[SLSI_SCAN_SCHED_ID].sched_req) {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "No sched scan req\n");
goto exit;
}
r = slsi_mlme_del_scan(sdev, dev, (ndev_vif->ifnum << 8 | SLSI_SCAN_SCHED_ID), false);
ndev_vif->scan[SLSI_SCAN_SCHED_ID].sched_req = NULL;
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return r;
}
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
void slsi_save_connection_params(struct slsi_dev *sdev, struct net_device *dev,
struct cfg80211_connect_params *sme,
struct ieee80211_channel *channel, const u8 *bssid,
u32 action_frame_bmap, u32 action_frame_suspend_bmap)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
memset(&ndev_vif->sta.sme, 0, sizeof(struct cfg80211_connect_params));
ndev_vif->sta.sme = *sme;
if (sme->ie && sme->ie_len)
ndev_vif->sta.sme.ie = slsi_mem_dup((u8 *)sme->ie, sme->ie_len);
else
ndev_vif->sta.sme.ie = NULL;
if (sme->ssid && sme->ssid_len)
ndev_vif->sta.sme.ssid = slsi_mem_dup((u8 *)sme->ssid, sme->ssid_len);
else
ndev_vif->sta.sme.ssid = NULL;
if (sme->key && sme->key_len)
ndev_vif->sta.sme.key = slsi_mem_dup((u8 *)sme->key, sme->key_len);
else
ndev_vif->sta.sme.key = NULL;
ndev_vif->sta.action_frame_bmap = action_frame_bmap;
ndev_vif->sta.action_frame_suspend_bmap = action_frame_suspend_bmap;
ndev_vif->sta.connected_bssid = slsi_mem_dup((u8 *)bssid, ETH_ALEN);
ndev_vif->sta.connected_ssid_len = (int)sme->ssid_len;
ndev_vif->sta.connected_ssid = slsi_mem_dup((u8 *)sme->ssid, ndev_vif->sta.connected_ssid_len);
}
#endif
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
void slsi_set_params_on_bss(struct slsi_dev *sdev, struct net_device *dev, struct cfg80211_connect_params *sme,
struct ieee80211_channel **channel, const u8 **bssid)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
ndev_vif->sta.akm_type = slsi_bss_connect_type_get(sdev, sme->ie, sme->ie_len);
ndev_vif->sta.ssid_len = sme->ssid_len;
memcpy(ndev_vif->sta.ssid, sme->ssid, sme->ssid_len);
/* If bssid is not present, check if bssid hint is present, if even hint not present,
* select bssid in driver set connect_attempted to true.
*/
if (!sme->bssid) {
ndev_vif->sta.drv_bss_selection = true;
if (sme->bssid_hint) {
*bssid = sme->bssid_hint;
*channel = sme->channel_hint;
}
} else {
ndev_vif->sta.drv_bss_selection = false;
}
}
#endif
int slsi_check_wificonnect_to_connectedGo(struct slsi_dev *sdev, struct net_device *dev, const u8 *bssid)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct net_device *p2p_dev;
struct netdev_vif *ndev_p2p_vif;
if (bssid) {
if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif) && sdev->p2p_state == P2P_GROUP_FORMED_CLI) {
p2p_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_P2PX_SWLAN);
if (p2p_dev) {
ndev_p2p_vif = netdev_priv(p2p_dev);
if (ndev_p2p_vif->sta.sta_bss) {
if (SLSI_ETHER_EQUAL(ndev_p2p_vif->sta.sta_bss->bssid, bssid))
return -EINVAL;
}
}
}
}
return 0;
}
int slsi_set_roam_reassoc(struct net_device *dev, struct slsi_dev *sdev, struct cfg80211_connect_params *sme)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer;
const u8 *connected_ssid = NULL;
int r = 0;
/* reassociation */
peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET);
if (WARN_ON(!peer))
return -EINVAL;
if (!sme->bssid && !sme->bssid_hint) {
SLSI_NET_ERR(dev, "Require bssid in reassoc but received null\n");
return -EINVAL;
}
if ((sme->bssid && !memcmp(peer->address, sme->bssid, ETH_ALEN)) ||
(sme->bssid_hint && !memcmp(peer->address, sme->bssid_hint, ETH_ALEN))) { /* same bssid or bssid hint*/
r = slsi_mlme_reassociate(sdev, dev);
if (r) {
SLSI_NET_ERR(dev, "Failed to reassociate : %d\n", r);
} else {
ndev_vif->sta.vif_status = SLSI_VIF_STATUS_CONNECTING;
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DISCONNECTED);
}
} else { /* different bssid */
if (!ndev_vif->sta.sta_bss) {
SLSI_NET_ERR(dev, "Bss is not stored in ndev_vif sta\n");
return -EINVAL;
}
connected_ssid = cfg80211_find_ie(WLAN_EID_SSID, ndev_vif->sta.sta_bss->ies->data, ndev_vif->sta.sta_bss->ies->len);
if (!connected_ssid) {
SLSI_NET_ERR(dev, "Require ssid in roam but received null\n");
return -EINVAL;
}
if (!memcmp(&connected_ssid[2], sme->ssid, connected_ssid[1])) { /* same ssid */
if (!sme->channel) {
SLSI_NET_ERR(dev, "Roaming has been rejected, as sme->channel is null\n");
return -EINVAL;
}
if (sme->bssid) {
r = slsi_mlme_roam(sdev, dev, sme->bssid, sme->channel->center_freq);
} else if (sme->bssid_hint) {
r = slsi_mlme_roam(sdev, dev, sme->bssid_hint, sme->channel->center_freq);
} else {
SLSI_NET_ERR(dev, "Roaming has been rejected, as bssid and bssid_hint are null\n");
return -EINVAL;
}
if (r) {
SLSI_NET_ERR(dev, "Failed to roam : %d\n", r);
return -EINVAL;
}
} else {
SLSI_NET_ERR(dev, "Connected but received connect to new ESS, without disconnect");
return -EINVAL;
}
}
return r;
}
int slsi_check_valid_netdev_vif_state(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
/* Sta started case */
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
if (SLSI_IS_VIF_INDEX_MHS_DUALSTA(sdev, ndev_vif) && ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT) {
SLSI_NET_ERR(dev, "Iftype: %d\n", ndev_vif->iftype);
return -EINVAL;
}
#endif /* wifi sharing */
/* Check netdev_vif is activated or not */
if (WARN_ON(ndev_vif->activated)) {
SLSI_NET_ERR(dev, "Vif is already activated: %d\n", ndev_vif->activated);
return -EINVAL;
}
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION &&
ndev_vif->sta.vif_status != SLSI_VIF_STATUS_UNSPECIFIED) {
SLSI_NET_ERR(dev, "VIF status: %d\n", ndev_vif->sta.vif_status);
return -EINVAL;
}
return 0;
}
int slsi_set_bmap(struct slsi_dev *sdev, struct net_device *dev, u32 *action_frame_bmap, u32 *action_frame_suspend_bmap, u8 *device_address)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct net_device *p2p_dev;
switch (ndev_vif->iftype) {
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_STATION:
ndev_vif->iftype = NL80211_IFTYPE_STATION;
dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
*action_frame_bmap = SLSI_STA_ACTION_FRAME_BITMAP;
*action_frame_suspend_bmap = SLSI_STA_ACTION_FRAME_SUSPEND_BITMAP;
#ifdef CONFIG_SCSC_WLAN_WES_NCHO
if (sdev->device_config.wes_mode) {
*action_frame_bmap |= SLSI_ACTION_FRAME_VENDOR_SPEC;
*action_frame_suspend_bmap |= SLSI_ACTION_FRAME_VENDOR_SPEC;
}
#endif
break;
case NL80211_IFTYPE_P2P_CLIENT:
slsi_p2p_group_start_remove_unsync_vif(sdev);
p2p_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_P2P);
if (p2p_dev)
SLSI_ETHER_COPY(device_address, p2p_dev->dev_addr);
*action_frame_bmap = SLSI_ACTION_FRAME_PUBLIC;
*action_frame_suspend_bmap = SLSI_ACTION_FRAME_PUBLIC;
break;
default:
SLSI_NET_ERR(dev, "Invalid Device Type: %d\n", ndev_vif->iftype);
return -EINVAL;
}
return 0;
}
int slsi_set_sta_bss_info(struct wiphy *wiphy, struct net_device *dev, struct slsi_dev *sdev,
struct cfg80211_connect_params *sme, struct ieee80211_channel **channel,
const u8 **bssid, u16 prev_vif_type)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
u16 capability = WLAN_CAPABILITY_ESS;
int r = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
capability = sme->privacy ? IEEE80211_PRIVACY_ON : IEEE80211_PRIVACY_OFF;
#else
if (sme->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
#endif
ndev_vif->sta.sta_bss = cfg80211_get_bss(wiphy,
*channel,
*bssid,
sme->ssid,
sme->ssid_len,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
IEEE80211_BSS_TYPE_ANY,
#else
capability,
#endif
capability);
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
if (!sme->bssid && !sme->bssid_hint && !ndev_vif->sta.sta_bss) {
struct list_head *pos;
list_for_each(pos, &ndev_vif->sta.ssid_info) {
struct slsi_ssid_info *ssid_info = list_entry(pos, struct slsi_ssid_info, list);
struct list_head *pos_bssid;
if (ssid_info->ssid.ssid_len != ndev_vif->sta.ssid_len ||
memcmp(ssid_info->ssid.ssid, &ndev_vif->sta.ssid, ndev_vif->sta.ssid_len) != 0 ||
!(ssid_info->akm_type & ndev_vif->sta.akm_type))
continue;
list_for_each(pos_bssid, &ssid_info->bssid_list) {
struct slsi_bssid_info *bssid_info = list_entry(pos_bssid, struct slsi_bssid_info, list);
if (*bssid && !memcmp(bssid_info->bssid, *bssid, ETH_ALEN))
continue;
ndev_vif->sta.sta_bss = cfg80211_get_bss(wiphy,
ieee80211_get_channel(sdev->wiphy,
(bssid_info->freq / 2)),
bssid_info->bssid,
sme->ssid,
sme->ssid_len,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
IEEE80211_BSS_TYPE_ANY,
#else
capability,
#endif
capability);
if (ndev_vif->sta.sta_bss) {
*bssid = bssid_info->bssid;
*channel = ieee80211_get_channel(sdev->wiphy, (bssid_info->freq / 2));
break;
}
}
}
}
#endif
if (!ndev_vif->sta.sta_bss) {
struct cfg80211_ssid ssid;
SLSI_NET_DBG3(dev, SLSI_CFG80211, "BSS info is not available - Perform scan\n");
ssid.ssid_len = sme->ssid_len;
memcpy(ssid.ssid, sme->ssid, ssid.ssid_len);
if (!(ssid.ssid_len > 0 && *channel)) {
r = slsi_mlme_connect_scan(sdev, dev, 1, &ssid, *channel);
if (r) {
SLSI_NET_ERR(dev, "slsi_mlme_connect_scan failed\n");
return r;
}
ndev_vif->sta.sta_bss = cfg80211_get_bss(wiphy,
*channel,
*bssid,
sme->ssid,
sme->ssid_len,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
IEEE80211_BSS_TYPE_ANY,
#else
capability,
#endif
capability);
if (!ndev_vif->sta.sta_bss) {
if (*bssid)
SLSI_NET_ERR(dev, "cfg80211_get_bss(%.*s, " MACSTR ") Not found\n",
(int)sme->ssid_len, sme->ssid, MAC2STR(*bssid));
else
SLSI_NET_ERR(dev, "cfg80211_get_bss(%.*s) Not found\n",
(int)sme->ssid_len, sme->ssid);
/* Set previous status in case of failure */
ndev_vif->vif_type = prev_vif_type;
return -ENOENT;
}
*channel = ndev_vif->sta.sta_bss->channel;
*bssid = ndev_vif->sta.sta_bss->bssid;
}
} else {
#ifdef CONFIG_SEC_FACTORY
if (sdev->device_config.supported_band != SLSI_FREQ_BAND_AUTO) {
int supported_band = sdev->device_config.supported_band;
int bss_band = ndev_vif->sta.sta_bss->channel->band;
SLSI_NET_DBG3(dev, SLSI_CFG80211, "sup_band %d, bss_band %d\n", supported_band, bss_band);
if (supported_band == SLSI_FREQ_BAND_2GHZ && bss_band != NL80211_BAND_2GHZ)
return -EPERM;
if (supported_band == SLSI_FREQ_BAND_5GHZ && bss_band != NL80211_BAND_5GHZ)
return -EPERM;
if (supported_band == SLSI_FREQ_BAND_6GHZ && bss_band != NL80211_BAND_6GHZ)
return -EPERM;
}
#endif
*channel = ndev_vif->sta.sta_bss->channel;
*bssid = ndev_vif->sta.sta_bss->bssid;
}
return 0;
}
void slsi_config_rsn_ie(struct net_device *dev, struct cfg80211_connect_params *sme)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) {
const u8 *rsn;
SLSI_NET_DBG1(dev, SLSI_CFG80211, "N AKM Suites: : %1d\n", sme->crypto.n_akm_suites);
rsn = cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len);
#ifdef CONFIG_SCSC_WLAN_SAE_CONFIG
if (rsn) {
int pos;
/* Calculate the position of AKM suite in RSNIE
* RSNIE TAG(1 byte) + length(1 byte) + version(2 byte) + Group cipher suite(4 bytes)
* pairwise suite count(2 byte) + pairwise suite count * 4 + AKM suite count(2 byte)
* pos is the array index not length
*/
pos = 7 + 2 + (rsn[8] * 4) + 2;
ndev_vif->sta.crypto.akm_suites[0] = ((rsn[pos + 4] << 24) | (rsn[pos + 3] << 16) | (rsn[pos + 2] << 8) | (rsn[pos + 1]));
if ((rsn[pos + 1] == 0x00 && rsn[pos + 2] == 0x0f && rsn[pos + 3] == 0xac) && (rsn[pos + 4] == 0x08 || rsn[pos + 4] == 0x09)) {
ndev_vif->sta.crypto.wpa_versions = 3;
ndev_vif->sta.use_set_pmksa = 1;
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
ndev_vif->sta.wpa3_auth_state = SLSI_WPA3_PREAUTH;
#endif
} else if ((rsn[pos + 1] == 0x00 && rsn[pos + 2] == 0x0f && rsn[pos + 3] == 0xac) && (rsn[pos + 4] == 18)) {
ndev_vif->sta.use_set_pmksa = 1;
} else {
ndev_vif->sta.crypto.wpa_versions = 0;
ndev_vif->sta.use_set_pmksa = 0;
}
}
SLSI_NET_DBG1(dev, SLSI_CFG80211, "RSN IE: : %1d\n", ndev_vif->sta.crypto.akm_suites[0]);
#endif
if (rsn) {
ndev_vif->sta.rsn_ie_len = rsn[1];
kfree(ndev_vif->sta.rsn_ie);
ndev_vif->sta.rsn_ie = NULL;
/* Len+2 because RSN IE TAG and Length */
ndev_vif->sta.rsn_ie = kmalloc(ndev_vif->sta.rsn_ie_len + 2, GFP_KERNEL);
/* len+2 because RSNIE TAG and Length */
if (ndev_vif->sta.rsn_ie)
memcpy(ndev_vif->sta.rsn_ie, rsn, ndev_vif->sta.rsn_ie_len + 2);
}
}
}
int slsi_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
u8 device_address[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
int r = 0;
struct slsi_peer *peer;
u16 prev_vif_type;
u32 action_frame_bmap;
u32 action_frame_suspend_bmap;
const u8 *bssid;
struct ieee80211_channel *channel;
u8 peer_address[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
u16 center_freq = 0;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_CONNECT.request\n");
return -EOPNOTSUPP;
}
SLSI_MUTEX_LOCK(sdev->start_stop_mutex);
if (sdev->device_state != SLSI_DEVICE_STATE_STARTED) {
SLSI_WARN(sdev, "device not started yet (device_state:%d)\n", sdev->device_state);
SLSI_MUTEX_UNLOCK(sdev->start_stop_mutex);
return -EINVAL;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
channel = sme->channel;
bssid = sme->bssid;
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
slsi_set_params_on_bss(sdev, dev, sme, &channel, &bssid);
#endif
/* check if ap is found in the blacklist.
* if present in the blacklist return failure
*/
r = slsi_is_bssid_in_blacklist(sdev, dev, (u8 *)bssid);
if (r) {
SLSI_NET_ERR(dev, "Blacklist bssid not allowed\n");
goto exit_with_error;
}
if (ndev_vif->sta.sta_bss)
SLSI_ETHER_COPY(peer_address, ndev_vif->sta.sta_bss->bssid);
center_freq = channel ? channel->center_freq : 0;
if (WARN_ON(!sme->ssid) || WARN_ON(sme->ssid_len > IEEE80211_MAX_SSID_LEN))
goto exit_with_error;
if (bssid)
SLSI_NET_INFO(dev, "%.*s Freq=%d vifStatus=%d CurrBssid:" MACSTR " NewBssid:" MACSTR " auth_type: %d Qinfo:%d ieLen:%d\n",
(int)sme->ssid_len, sme->ssid, center_freq, ndev_vif->sta.vif_status,
MAC2STR(peer_address), MAC2STR(bssid), (int)sme->auth_type, sdev->device_config.qos_info, (int)sme->ie_len);
else
SLSI_NET_INFO(dev, "%.*s Freq=%d vifStatus=%d CurrBssid:" MACSTR " auth_type: %d Qinfo:%d ieLen:%d\n",
(int)sme->ssid_len, sme->ssid, center_freq, ndev_vif->sta.vif_status,
MAC2STR(peer_address), (int)sme->auth_type, sdev->device_config.qos_info, (int)sme->ie_len);
#if IS_ENABLED(CONFIG_SCSC_WIFILOGGER)
SCSC_WLOG_PKTFATE_NEW_ASSOC();
if (bssid) {
SCSC_WLOG_DRIVER_EVENT(WLOG_NORMAL, WIFI_EVENT_ASSOCIATION_REQUESTED, 3,
WIFI_TAG_BSSID, ETH_ALEN, bssid,
WIFI_TAG_SSID, sme->ssid_len, sme->ssid,
WIFI_TAG_CHANNEL, sizeof(u16), &center_freq);
// ?? WIFI_TAG_VENDOR_SPECIFIC, sizeof(RSSE), RSSE);
}
#endif
if (SLSI_IS_HS2_UNSYNC_VIF(ndev_vif)) {
slsi_wlan_unsync_vif_deactivate(sdev, dev, true);
} else if (SLSI_IS_VIF_INDEX_P2P(ndev_vif)) {
SLSI_NET_ERR(dev, "Connect requested on incorrect vif\n");
goto exit_with_error;
}
r = slsi_check_wificonnect_to_connectedGo(sdev, dev, bssid);
if (r != 0) {
SLSI_NET_ERR(dev, "Connect Request Rejected\n");
goto exit_with_error;
}
/* Determine Connection Type Reassoc or Roaming by BSSID */
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION && ndev_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED) {
r = slsi_set_roam_reassoc(dev, sdev, sme);
if (r == 0)
goto exit;
else
goto exit_with_error;
}
r = slsi_check_valid_netdev_vif_state(sdev, dev);
if (r != 0) {
SLSI_NET_ERR(dev, "Invalid netdev vif state\n");
goto exit_with_error;
}
/* Back up vif_type because setting each vif_type */
prev_vif_type = ndev_vif->vif_type;
r = slsi_set_bmap(sdev, dev, &action_frame_bmap, &action_frame_suspend_bmap, device_address);
if (r != 0)
goto exit_with_error;
/* Initial Roaming checks done - assign vif type */
ndev_vif->vif_type = FAPI_VIFTYPE_STATION;
#if (defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
channel = sme->channel;
bssid = sme->bssid;
#endif
r = slsi_set_sta_bss_info(wiphy, dev, sdev, sme, &channel, &bssid, prev_vif_type);
if (r != 0)
goto exit;
ndev_vif->chan = channel;
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
ndev_vif->sta.ssid_len = sme->ssid_len;
memcpy(ndev_vif->sta.ssid, sme->ssid, sme->ssid_len);
#endif
/* Always check the BSSID is not null during connection
* It will cause kernel panic if we access null BSSID.
*/
if (bssid)
SLSI_ETHER_COPY(ndev_vif->sta.bssid, bssid);
if (slsi_mlme_add_vif(sdev, dev, dev->dev_addr, device_address) != 0) {
SLSI_NET_ERR(dev, "slsi_mlme_add_vif failed\n");
goto exit_with_bss;
}
if (slsi_vif_activated(sdev, dev) != 0) {
SLSI_NET_ERR(dev, "slsi_vif_activated failed\n");
goto exit_with_vif;
}
if (slsi_mlme_register_action_frame(sdev, dev, action_frame_bmap, action_frame_suspend_bmap) != 0) {
SLSI_NET_ERR(dev, "Action frame registration failed for bitmap value 0x%x 0x%x\n", action_frame_bmap, action_frame_suspend_bmap);
goto exit_with_vif;
}
r = slsi_set_boost(sdev, dev);
if (r != 0)
SLSI_NET_ERR(dev, "Rssi Boost set failed: %d\n", r);
/* add_info_elements with Probe Req IEs. Proceed even if confirm fails for add_info as it would
* still work if the fw pre-join scan does not include the vendor IEs
*/
if (ndev_vif->probe_req_ies) {
if (ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT) {
if (sme->crypto.wpa_versions == 2)
ndev_vif->delete_probe_req_ies = true; /* Stored Probe Req can be deleted at vif
* deletion after WPA2 association
*/
else
/* Retain stored Probe Req at vif deletion until WPA2 connection to allow Probe req */
ndev_vif->delete_probe_req_ies = false;
} else {
ndev_vif->delete_probe_req_ies = true; /* Delete stored Probe Req at vif deletion for STA */
}
}
r = slsi_add_probe_ies_request(sdev, dev);
/* Sometimes netif stack takes more time to initialize and any packet
* sent to stack would be dropped. This behavior is random in nature,
* so start the netif stack before sending out the connect req, it shall
* give enough time to netstack to initialize.
*/
netif_carrier_on(dev);
ndev_vif->sta.vif_status = SLSI_VIF_STATUS_CONNECTING;
ndev_vif->sta.roam_on_disconnect = false;
r = slsi_set_ext_cap(sdev, dev, sme->ie, sme->ie_len, slsi_extended_cap_mask);
if (r != 0)
SLSI_NET_ERR(dev, "Failed to set extended capability MIB: %d\n", r);
slsi_config_rsn_ie(dev, sme);
slsi_conn_log2us_connecting(sdev, dev, sme);
r = slsi_mlme_connect(sdev, dev, sme, channel, bssid);
if (r != 0) {
ndev_vif->sta.is_wps = false;
SLSI_NET_ERR(dev, "connect failed: %d\n", r);
netif_carrier_off(dev);
goto exit_with_vif;
}
peer = slsi_peer_add(sdev, dev, (u8 *)bssid, SLSI_STA_PEER_QUEUESET + 1);
ndev_vif->sta.resp_id = 0;
if (!peer)
goto exit_with_error;
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
if (ndev_vif->sta.drv_bss_selection) {
slsi_save_connection_params(sdev, dev, sme, channel, bssid,
action_frame_bmap, action_frame_suspend_bmap);
slsi_set_reset_connect_attempted_flag(sdev, dev, bssid);
}
#endif
goto exit;
exit_with_vif:
if (slsi_mlme_del_vif(sdev, dev) != 0)
SLSI_NET_ERR(dev, "slsi_mlme_del_vif failed\n");
slsi_vif_deactivated(sdev, dev);
exit_with_bss:
if (ndev_vif->sta.sta_bss) {
slsi_cfg80211_put_bss(wiphy, ndev_vif->sta.sta_bss);
ndev_vif->sta.sta_bss = NULL;
}
exit_with_error:
slsi_conn_log2us_connecting_fail(sdev, dev, bssid, center_freq, 3);
r = -EINVAL;
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
SLSI_MUTEX_UNLOCK(sdev->start_stop_mutex);
return r;
}
int slsi_disconnect(struct wiphy *wiphy, struct net_device *dev,
u16 reason_code)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer;
int r = 0;
cancel_work_sync(&ndev_vif->set_multicast_filter_work);
cancel_work_sync(&ndev_vif->update_pkt_filter_work);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG2(dev, SLSI_CFG80211, "reason: %d, vif_index = %d, vif_type = %d\n", reason_code,
ndev_vif->ifnum, ndev_vif->vif_type);
/* Assuming that the time it takes the firmware to disconnect is not significant
* as this function holds the locks until the MLME-DISCONNECT-IND comes back.
* Unless the MLME-DISCONNECT-CFM fails.
*/
if (!ndev_vif->activated) {
r = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
cfg80211_disconnected(dev, reason_code, NULL, 0, false, GFP_KERNEL);
#else
cfg80211_disconnected(dev, reason_code, NULL, 0, GFP_KERNEL);
#endif
SLSI_NET_INFO(dev, "Vif is already Deactivated\n");
goto exit;
}
peer = ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET];
#if IS_ENABLED(CONFIG_SCSC_WIFILOGGER)
SCSC_WLOG_DRIVER_EVENT(WLOG_NORMAL, WIFI_EVENT_DISASSOCIATION_REQUESTED, 2,
WIFI_TAG_BSSID, ETH_ALEN, peer->address,
WIFI_TAG_REASON_CODE, sizeof(u16), &reason_code);
#endif
switch (ndev_vif->vif_type) {
case FAPI_VIFTYPE_STATION:
{
slsi_reset_throughput_stats(dev);
/* Disconnecting spans several host firmware interactions so track the status
* so that the Host can ignore connect related signaling eg. MLME-CONNECT-IND
* now that it has triggered a disconnect.
*/
ndev_vif->sta.vif_status = SLSI_VIF_STATUS_DISCONNECTING;
netif_carrier_off(dev);
if (peer->valid)
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DISCONNECTED);
/* MLME-DISCONNECT_CFM only means that the firmware has accepted the request it has not yet
* disconnected. Completion of the disconnect is indicated by MLME-DISCONNECT-IND, so have
* to wait for that before deleting the VIF. Also any new activities eg. connect can not yet
* be started on the VIF until the disconnection is completed. So the MLME function also handles
* waiting for the MLME-DISCONNECT-IND (if the CFM is successful)
*/
r = slsi_mlme_disconnect(sdev, dev, peer->address, reason_code, true);
if (r != 0)
SLSI_NET_ERR(dev, "Disconnection returned with failure\n");
/* Even if we fail to disconnect cleanly, tidy up. */
r = slsi_handle_disconnect(sdev, dev, peer->address, 0, NULL, 0);
break;
}
default:
SLSI_NET_WARN(dev, "Invalid - vif type:%d, device type:%d)\n", ndev_vif->vif_type, ndev_vif->iftype);
r = -EINVAL;
break;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_set_default_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_index, bool unicast, bool multicast)
{
SLSI_UNUSED_PARAMETER(wiphy);
SLSI_UNUSED_PARAMETER(dev);
SLSI_UNUSED_PARAMETER(key_index);
SLSI_UNUSED_PARAMETER(unicast);
SLSI_UNUSED_PARAMETER(multicast);
/* Key is set in add_key. Nothing to do here */
return 0;
}
int slsi_config_default_mgmt_key(struct wiphy *wiphy,
struct net_device *dev,
u8 key_index)
{
SLSI_UNUSED_PARAMETER(wiphy);
SLSI_UNUSED_PARAMETER(key_index);
SLSI_UNUSED_PARAMETER(dev);
return 0;
}
int slsi_set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int r = 0;
SLSI_DBG1(sdev, SLSI_CFG80211, "slsi_set_wiphy_parms Frag Threshold = %d, RTS Threshold = %d",
wiphy->frag_threshold, wiphy->rts_threshold);
if ((changed & WIPHY_PARAM_FRAG_THRESHOLD) && (wiphy->frag_threshold != -1)) {
r = slsi_set_uint_mib(sdev, NULL, SLSI_PSID_DOT11_FRAGMENTATION_THRESHOLD, wiphy->frag_threshold);
if (r != 0) {
SLSI_ERR(sdev, "Setting FRAG_THRESHOLD failed\n");
return r;
}
}
if ((changed & WIPHY_PARAM_RTS_THRESHOLD) && (wiphy->rts_threshold != -1)) {
r = slsi_set_uint_mib(sdev, NULL, SLSI_PSID_DOT11_RTS_THRESHOLD, wiphy->rts_threshold);
if (r != 0) {
SLSI_ERR(sdev, "Setting RTS_THRESHOLD failed\n");
return r;
}
}
return r;
}
int slsi_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, int mbm)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int r = 0;
SLSI_UNUSED_PARAMETER(wdev);
SLSI_UNUSED_PARAMETER(type);
SLSI_UNUSED_PARAMETER(mbm);
SLSI_UNUSED_PARAMETER(sdev);
r = -EOPNOTSUPP;
return r;
}
int slsi_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef)
{
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif = netdev_priv(dev);
int freq = 0;
int width = 0;
int r = 0;
if (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)
return -ENODATA;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
freq = ndev_vif->sta.bss_cf;
width = ndev_vif->sta.ch_width;
if (width == 20) {
width = NL80211_CHAN_WIDTH_20;
if (freq != ndev_vif->chan->center_freq) {
r = -EINVAL;
goto exit;
}
} else if (width == 40) {
width = NL80211_CHAN_WIDTH_40;
if (freq != ndev_vif->chan->center_freq + 10 &&
freq != ndev_vif->chan->center_freq - 10) {
r = -EINVAL;
goto exit;
}
} else if (width == 80) {
width = NL80211_CHAN_WIDTH_80;
if (freq != ndev_vif->chan->center_freq + 30 &&
freq != ndev_vif->chan->center_freq + 10 &&
freq != ndev_vif->chan->center_freq - 10 &&
freq != ndev_vif->chan->center_freq - 30) {
r = -EINVAL;
goto exit;
}
} else if (width == 160) {
width = NL80211_CHAN_WIDTH_160;
if (freq != ndev_vif->chan->center_freq + 70 &&
freq != ndev_vif->chan->center_freq + 50 &&
freq != ndev_vif->chan->center_freq + 30 &&
freq != ndev_vif->chan->center_freq + 10 &&
freq != ndev_vif->chan->center_freq - 10 &&
freq != ndev_vif->chan->center_freq - 30 &&
freq != ndev_vif->chan->center_freq - 50 &&
freq != ndev_vif->chan->center_freq - 70) {
r = -EINVAL;
goto exit;
}
}
if (ndev_vif->chan)
chandef->chan = ndev_vif->chan;
chandef->center_freq1 = freq;
chandef->width = width;
chandef->center_freq2 = 0;
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, int *dbm)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int r = 0;
SLSI_UNUSED_PARAMETER(wdev);
SLSI_UNUSED_PARAMETER(dbm);
SLSI_UNUSED_PARAMETER(sdev);
r = -EOPNOTSUPP;
return r;
}
int slsi_del_station(struct wiphy *wiphy, struct net_device *dev,
struct station_del_parameters *del_params)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer;
int r = 0;
u16 reason_code = WLAN_REASON_DEAUTH_LEAVING;
const u8 *mac = del_params->mac;
reason_code = del_params->reason_code;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG1(dev, SLSI_CFG80211, "%pM, vifType:%d, vifIndex:%d vifActivated:%d ap.p2p_gc_keys_set = %d\n",
mac, ndev_vif->vif_type, ndev_vif->ifnum, ndev_vif->activated, ndev_vif->ap.p2p_gc_keys_set);
/* Function is called by cfg80211 before the VIF is added */
if (!ndev_vif->activated)
goto exit;
if (FAPI_VIFTYPE_AP != ndev_vif->vif_type) {
r = -EINVAL;
goto exit;
}
/* MAC with NULL value will come in case of flushing VLANS . Ignore this.*/
if (!mac)
goto exit;
else if (is_broadcast_ether_addr(mac) && reason_code == WLAN_REASON_DEAUTH_LEAVING) {
int i = 0;
while (i < SLSI_PEER_INDEX_MAX) {
peer = ndev_vif->peer_sta_record[i];
if (peer && peer->valid) {
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DISCONNECTED);
}
++i;
}
/* Note AP :: mlme_disconnect_request with broadcast mac address is
* not required. Other third party devices don't support this. Conclusively,
* BIP support is not present with AP
*/
/* Free WPA and WMM IEs if present */
slsi_clear_cached_ies(&ndev_vif->ap.cache_wpa_ie, &ndev_vif->ap.wpa_ie_len);
slsi_clear_cached_ies(&ndev_vif->ap.cache_wmm_ie, &ndev_vif->ap.wmm_ie_len);
netif_carrier_off(dev);
/* All STA related packets and info should already have been flushed */
if (slsi_mlme_del_vif(sdev, dev) != 0)
SLSI_NET_ERR(dev, "slsi_mlme_del_vif failed\n");
slsi_vif_deactivated(sdev, dev);
ndev_vif->ipaddress = cpu_to_be32(0);
if (ndev_vif->ap.p2p_gc_keys_set) {
slsi_wake_unlock(&sdev->wlan_wl);
ndev_vif->ap.p2p_gc_keys_set = false;
}
} else {
peer = slsi_get_peer_from_mac(sdev, dev, mac);
if (peer) { /* To handle race condition when disconnect_req is sent before procedure_strted_ind and before mlme-connected_ind*/
if (peer->connected_state == SLSI_STA_CONN_STATE_CONNECTING) {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "SLSI_STA_CONN_STATE_CONNECTING : mlme-disconnect-req dropped at driver\n");
goto exit;
}
if (peer->is_wps) {
/* To inter-op with Intel STA in P2P cert need to discard the deauth after successful WPS handshake as a P2P GO */
SLSI_NET_INFO(dev, "DISCONNECT after WPS : mlme-disconnect-req dropped at driver\n");
goto exit;
}
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DISCONNECTED);
r = slsi_mlme_disconnect(sdev, dev, peer->address, reason_code, true);
if (r != 0)
SLSI_NET_ERR(dev, "Disconnection returned with failure\n");
/* Even if we fail to disconnect cleanly, tidy up. */
r = slsi_handle_disconnect(sdev, dev, peer->address, reason_code, NULL, 0);
}
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_get_station(struct wiphy *wiphy, struct net_device *dev,
const u8 *mac, struct station_info *sinfo)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer;
int r = 0;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated) {
r = -EINVAL;
goto exit;
}
peer = slsi_get_peer_from_mac(sdev, dev, mac);
if (!peer) {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "%pM : Not Found\n", mac);
r = -EINVAL;
goto exit;
}
if (((ndev_vif->iftype == NL80211_IFTYPE_STATION && !(ndev_vif->sta.roam_in_progress)) ||
ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
/*Read MIB and fill into the peer.sinfo*/
r = slsi_mlme_get_sinfo_mib(sdev, dev, peer);
if (r) {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "failed to read Station Info Error:%d\n", r);
goto exit;
}
}
*sinfo = peer->sinfo;
sinfo->generation = ndev_vif->cfg80211_sinfo_generation;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0))
SLSI_NET_DBG1(dev, SLSI_CFG80211,
"%pM, tx:%d, txbytes:%llu, rx:%d, rxbytes:%llu tx_fail:%d tx_retry:%d tx_retry_times:%d\n",
#else
SLSI_NET_DBG1(dev, SLSI_CFG80211, "%pM, tx:%d, txbytes:%llu, rx:%d, rxbytes:%llu tx_fail:%d tx_retry:%d\n",
#endif
mac,
peer->sinfo.tx_packets,
peer->sinfo.tx_bytes,
peer->sinfo.rx_packets,
peer->sinfo.rx_bytes,
peer->sinfo.tx_failed,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0))
peer->sinfo.tx_retries,
peer->sinfo.airtime_link_metric);
#else
peer->sinfo.tx_retries);
#endif
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = -EINVAL;
u16 pwr_mode = enabled ? FAPI_POWERMANAGEMENTMODE_POWER_SAVE : FAPI_POWERMANAGEMENTMODE_ACTIVE_MODE;
SLSI_UNUSED_PARAMETER(timeout);
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_POWERMGT.request\n");
return -EOPNOTSUPP;
}
if (slsi_is_rf_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, RF test does not support.\n");
return -EOPNOTSUPP;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_INFO(dev, "PS MODE enabled:%d, vif_type:%d, vif_index:%d\n", enabled, ndev_vif->vif_type,
ndev_vif->ifnum);
if (ndev_vif->activated && ndev_vif->vif_type == FAPI_VIFTYPE_STATION) {
ndev_vif->set_power_mode = pwr_mode;
r = slsi_mlme_powermgt(sdev, dev, pwr_mode);
} else {
r = 0;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0;
SLSI_NET_DBG3(dev, SLSI_CFG80211, "oper:%d, vif_type:%d, vif_index:%d\n", oper, ndev_vif->vif_type,
ndev_vif->ifnum);
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
return -ENOTSUPP;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated || SLSI_IS_VIF_INDEX_P2P_GROUP(sdev, ndev_vif) ||
ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED) {
r = -ENOTSUPP;
goto exit;
}
switch (oper) {
case NL80211_TDLS_DISCOVERY_REQ:
SLSI_NET_DBG1(dev, SLSI_CFG80211, "NL80211_TDLS_DISCOVERY_REQ\n");
r = slsi_mlme_tdls_action(sdev, dev, peer, FAPI_TDLSACTION_DISCOVERY);
break;
case NL80211_TDLS_SETUP:
r = slsi_mlme_tdls_action(sdev, dev, peer, FAPI_TDLSACTION_FORCED_SETUP);
break;
case NL80211_TDLS_TEARDOWN:
r = slsi_mlme_tdls_action(sdev, dev, peer, FAPI_TDLSACTION_TEARDOWN);
break;
default:
r = -EOPNOTSUPP;
goto exit;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_set_qos_map(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_qos_map *qos_map)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer;
int r = 0;
/* Cleaning up is inherently taken care by driver */
if (!qos_map)
return r;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated) {
r = -EINVAL;
goto exit;
}
if (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) {
r = -EINVAL;
goto exit;
}
SLSI_NET_DBG3(dev, SLSI_CFG80211, "Set QoS Map\n");
peer = ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET];
if (!peer || !peer->valid) {
r = -EINVAL;
goto exit;
}
memcpy(&peer->qos_map, qos_map, sizeof(struct cfg80211_qos_map));
peer->qos_map_set = true;
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_set_monitor_channel(struct wiphy *wiphy, struct cfg80211_chan_def *chandef)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct net_device *dev;
struct netdev_vif *ndev_vif;
SLSI_DBG1(sdev, SLSI_CFG80211, "channel (freq:%u)\n", chandef->chan->center_freq);
rcu_read_lock();
dev = slsi_get_netdev_rcu(sdev, SLSI_NET_INDEX_WLAN);
if (!dev) {
SLSI_ERR(sdev, "netdev No longer exists\n");
rcu_read_unlock();
return -EINVAL;
}
ndev_vif = netdev_priv(dev);
rcu_read_unlock();
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (slsi_mlme_configure_monitor_mode(sdev, dev, chandef) != 0) {
SLSI_ERR(sdev, "set Monitor channel failed\n");
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -EINVAL;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return 0;
}
int slsi_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow)
{
SLSI_UNUSED_PARAMETER(wow);
return 0;
}
int slsi_resume(struct wiphy *wiphy)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
/* Scheduling the IO thread */
/* (void)slsi_hip_run_bh(sdev); */
SLSI_UNUSED_PARAMETER(sdev);
return 0;
}
int slsi_set_pmksa(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
int i = 0;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
u8 *rsnie; /* final RSN IE with the PMKID*/
u8 *buf; /* Complete Buffer: RSNIE and Assoc add info elements used in connection */
u16 rsnie_len;
int left = 0;
u16 count = 0;
int ret = 0;
int pos = 0;
int buf_len = 0;
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (ndev_vif->sta.use_set_pmksa) {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "\n");
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (ndev_vif->sta.rsn_ie) {
rsnie_len = ndev_vif->sta.rsn_ie_len;
/* Tag and Len + Length of RSNIE + PMKID Count + PMKID */
rsnie = kmalloc(2 + rsnie_len + 2 + PMKID_LEN, GFP_KERNEL);
if (rsnie) {
memset(rsnie, 0, rsnie_len + 2 + PMKID_LEN);
/* parse the RSN IE and copy PMKID to Required position in RSN IE*/
left = rsnie_len + 2; //FOR RSN IE TAG and Length
pos = 4; /* RSN IE ID, LEN, and Version*/
left -= 4;
if (left < 4) {
kfree(rsnie);
kfree(ndev_vif->sta.rsn_ie);
ndev_vif->sta.rsn_ie = NULL;
ret = -EINVAL;
goto exit;
}
pos += RSN_SELECTOR_LEN; /* Group cipher suite */
left -= RSN_SELECTOR_LEN;
/* Pairwise and AKM suite count and suite list */
i = 0;
while (i < 2) {
pos += 2;
left -= 2;
count = le16_to_cpu(*(u16 *)(&ndev_vif->sta.rsn_ie[pos - 2]));
pos += count * RSN_SELECTOR_LEN;
left -= count * RSN_SELECTOR_LEN;
i++;
}
pos += 2; /* RSN Capabilities */
left -= 2;
memcpy(rsnie, ndev_vif->sta.rsn_ie, pos);
count = 0;
if (left > 0 && left >= 2) {
count = le16_to_cpu(*(u16 *)(&ndev_vif->sta.rsn_ie[pos])); /* PMKID count */
left -= 2;
}
pos += 2; /* PMKID count */
rsnie[pos - 2] = 1;
rsnie[pos - 1] = 0;
memcpy(&rsnie[pos], pmksa->pmkid, PMKID_LEN); /* copy PMKID */
pos += PMKID_LEN;
if (count && left > 0) {
left -= (count * PMKID_LEN);
memcpy(&rsnie[pos], &ndev_vif->sta.rsn_ie[pos + ((count - 1) * PMKID_LEN)], left);
} else if (left > 0) {
memcpy(&rsnie[pos], &ndev_vif->sta.rsn_ie[pos - PMKID_LEN], left);
}
pos += left;
rsnie_len = pos;
rsnie[1] = rsnie_len - 2;
buf_len = rsnie_len + ndev_vif->sta.assoc_req_add_info_elem_len;
buf = kmalloc(buf_len, GFP_KERNEL);
if (!buf) {
SLSI_NET_ERR(dev, "Out of memory for buffer\n");
ret = -ENOMEM;
kfree(rsnie);
goto exit;
}
memcpy(buf, rsnie, rsnie_len);
if (ndev_vif->sta.assoc_req_add_info_elem_len)
memcpy(buf + rsnie_len, ndev_vif->sta.assoc_req_add_info_elem, ndev_vif->sta.assoc_req_add_info_elem_len);
ret = slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_ASSOCIATION_REQUEST, buf, buf_len);
if (ret != 0) {
SLSI_NET_ERR(dev, "RSN IE with PMKID setting failed\n");
kfree(rsnie);
kfree(buf);
goto exit;
}
kfree(buf);
kfree(rsnie);
} else {
SLSI_NET_ERR(dev, "Out of memory for RSN IE\n");
ret = -ENOMEM;
goto exit;
}
} else {
SLSI_NET_ERR(dev, "RSN IE is not present in Station VIF\n");
ret = -EINVAL;
goto exit;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
return ret;
}
int slsi_del_pmksa(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (ndev_vif->sta.use_set_pmksa) {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "\n");
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated)
SLSI_NET_DBG1(dev, SLSI_CFG80211, "VIF not activated\n");
/* Just consume the DEL-PMKSA, because after DEL-PMKSA,
* ADD-PMKSA will override the PMKID
*/
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
return 0;
}
int slsi_flush_pmksa(struct wiphy *wiphy, struct net_device *dev)
{
SLSI_UNUSED_PARAMETER(wiphy);
SLSI_UNUSED_PARAMETER(dev);
return 0;
}
int slsi_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
unsigned int duration,
u64 *cookie)
{
struct net_device *dev = wdev->netdev;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0;
SLSI_MUTEX_LOCK(sdev->start_stop_mutex);
if (sdev->device_state != SLSI_DEVICE_STATE_STARTED) {
SLSI_WARN(sdev, "device not started yet (device_state:%d)\n", sdev->device_state);
goto exit_with_error;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG2(dev, SLSI_CFG80211, "channel freq = %d, duration = %d, vif_type = %d, vif_index = %d,"
"sdev->p2p_state = %s\n", chan->center_freq, duration, ndev_vif->vif_type, ndev_vif->ifnum,
slsi_p2p_state_text(sdev->p2p_state));
if (!SLSI_IS_VIF_INDEX_P2P(ndev_vif)) {
SLSI_NET_ERR(dev, "Invalid vif type\n");
goto exit_with_error;
}
if (SLSI_IS_P2P_GROUP_STATE(sdev)) {
slsi_assign_cookie_id(cookie, &ndev_vif->unsync.roc_cookie);
cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL);
cfg80211_remain_on_channel_expired(wdev, *cookie, chan, GFP_KERNEL);
goto exit;
}
/* Unsync vif will be required, cancel any pending work of its deletion */
cancel_delayed_work(&ndev_vif->unsync.del_vif_work);
if (sdev->p2p_state == P2P_ACTION_FRAME_TX_RX)
queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.del_vif_work,
msecs_to_jiffies(duration + SLSI_P2P_UNSYNC_VIF_EXTRA_MSEC));
/* Ideally, there should not be any ROC work pending. However, supplicant can send back to back ROC in a race scenario as below.
* If action frame is received while P2P social scan, the response frame tx is delayed till scan completes. After scan completion,
* frame tx is done and ROC is started. Upon frame tx status, supplicant sends another ROC without cancelling the previous one.
*/
cancel_delayed_work(&ndev_vif->unsync.roc_expiry_work);
if (delayed_work_pending(&ndev_vif->unsync.unset_channel_expiry_work))
cancel_delayed_work(&ndev_vif->unsync.unset_channel_expiry_work);
/* If action frame tx is in progress and ROC comes, then it would mean action frame tx was done in ROC and
* frame tx ind is awaited, don't change state. Also allow back to back ROC in case it comes.
*/
if (sdev->p2p_state == P2P_ACTION_FRAME_TX_RX || sdev->p2p_state == P2P_LISTENING) {
goto exit_with_roc;
}
/* Unsync vif activation: Possible P2P state at this point is P2P_IDLE_NO_VIF or P2P_IDLE_VIF_ACTIVE */
if (sdev->p2p_state == P2P_IDLE_NO_VIF) {
if (slsi_p2p_vif_activate(sdev, dev, chan, duration, true) != 0)
goto exit_with_error;
} else if (sdev->p2p_state == P2P_IDLE_VIF_ACTIVE) {
/* Configure Probe Response IEs in firmware if they have changed */
if (ndev_vif->unsync.ies_changed) {
u16 purpose = FAPI_PURPOSE_PROBE_RESPONSE;
if (slsi_mlme_add_info_elements(sdev, dev, purpose, ndev_vif->unsync.probe_rsp_ies, ndev_vif->unsync.probe_rsp_ies_len) != 0) {
SLSI_NET_ERR(dev, "Probe Rsp IEs setting failed\n");
goto exit_with_vif;
}
ndev_vif->unsync.ies_changed = false;
}
/* Channel Setting - Don't set if already on same channel */
if (ndev_vif->driver_channel != chan->hw_value) {
if (slsi_mlme_set_channel(sdev, dev, chan, SLSI_FW_CHANNEL_DURATION_UNSPECIFIED, 0, 0) != 0) {
SLSI_NET_ERR(dev, "Channel setting failed\n");
goto exit_with_vif;
} else {
ndev_vif->chan = chan;
ndev_vif->driver_channel = chan->hw_value;
}
}
} else {
SLSI_NET_ERR(dev, "Driver in incorrect P2P state (%s)", slsi_p2p_state_text(sdev->p2p_state));
goto exit_with_error;
}
SLSI_P2P_STATE_CHANGE(sdev, P2P_LISTENING);
exit_with_roc:
/* Cancel remain on channel is sent to the supplicant 10ms before the duration
*This is to avoid the race condition of supplicant sending cancel remain on channel and
*drv sending cancel_remain on channel because of roc expiry.
*This race condition causes delay to the next p2p search
*/
queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.roc_expiry_work,
msecs_to_jiffies(duration - SLSI_P2P_ROC_EXTRA_MSEC));
slsi_assign_cookie_id(cookie, &ndev_vif->unsync.roc_cookie);
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Cookie = 0x%llx\n", *cookie);
cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL);
goto exit;
exit_with_vif:
slsi_p2p_vif_deactivate(sdev, dev, true);
exit_with_error:
r = -EINVAL;
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
SLSI_MUTEX_UNLOCK(sdev->start_stop_mutex);
return r;
}
int slsi_cancel_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int r = 0;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Cookie = 0x%llx, vif_type = %d, vif_index = %d, sdev->p2p_state = %s,"
"ndev_vif->ap.p2p_gc_keys_set = %d, ndev_vif->unsync.roc_cookie = 0x%llx\n", cookie,
ndev_vif->vif_type, ndev_vif->ifnum, slsi_p2p_state_text(sdev->p2p_state),
ndev_vif->ap.p2p_gc_keys_set, ndev_vif->unsync.roc_cookie);
if (!SLSI_IS_VIF_INDEX_P2P(ndev_vif)) {
SLSI_NET_ERR(dev, "Invalid vif type\n");
r = -EINVAL;
goto exit;
}
if (!((sdev->p2p_state == P2P_LISTENING) || (sdev->p2p_state == P2P_ACTION_FRAME_TX_RX))) {
goto exit;
}
if (sdev->p2p_state == P2P_ACTION_FRAME_TX_RX && ndev_vif->mgmt_tx_data.exp_frame != SLSI_PA_INVALID) {
/* Reset the expected action frame as procedure got completed */
SLSI_INFO(sdev, "Action frame (%s) was not received\n", slsi_pa_subtype_text(ndev_vif->mgmt_tx_data.exp_frame));
ndev_vif->mgmt_tx_data.exp_frame = SLSI_PA_INVALID;
}
cancel_delayed_work(&ndev_vif->unsync.roc_expiry_work);
cfg80211_remain_on_channel_expired(&ndev_vif->wdev, ndev_vif->unsync.roc_cookie, ndev_vif->chan, GFP_KERNEL);
if (!ndev_vif->drv_in_p2p_procedure) {
if (delayed_work_pending(&ndev_vif->unsync.unset_channel_expiry_work))
cancel_delayed_work(&ndev_vif->unsync.unset_channel_expiry_work);
queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.unset_channel_expiry_work,
msecs_to_jiffies(SLSI_P2P_UNSET_CHANNEL_EXTRA_MSEC));
}
/* Queue work to delete unsync vif */
slsi_p2p_queue_unsync_vif_del_work(ndev_vif, SLSI_P2P_UNSYNC_VIF_EXTRA_MSEC);
SLSI_P2P_STATE_CHANGE(sdev, P2P_IDLE_VIF_ACTIVE);
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_change_bss(struct wiphy *wiphy, struct net_device *dev,
struct bss_parameters *params)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int r = 0;
SLSI_UNUSED_PARAMETER(params);
SLSI_UNUSED_PARAMETER(sdev);
return r;
}
static void slsi_ap_start_obss_scan(struct slsi_dev *sdev, struct net_device *dev, struct netdev_vif *ndev_vif)
{
struct cfg80211_ssid ssids;
struct ieee80211_channel *channel;
int n_ssids = 1, n_channels = 1, i;
if (!ndev_vif->chan) {
SLSI_NET_ERR(dev, "ndev_vif->chan is null\n");
return;
}
SLSI_NET_DBG1(dev, SLSI_CFG80211, "channel %u\n", ndev_vif->chan->hw_value);
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
ssids.ssid_len = 0;
for (i = 0; i < IEEE80211_MAX_SSID_LEN; i++)
ssids.ssid[i] = 0x00; /* Broadcast SSID */
channel = ieee80211_get_channel(sdev->wiphy, ndev_vif->chan->center_freq);
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan = true;
(void)slsi_mlme_add_scan(sdev,
dev,
FAPI_SCANTYPE_OBSS_SCAN,
FAPI_REPORTMODE_REAL_TIME,
n_ssids,
&ssids,
n_channels,
&channel,
NULL,
NULL, /* No IEs */
0,
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
0,
NULL,
#endif
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan /* Wait for scan_done_ind */);
slsi_ap_obss_scan_done_ind(dev, ndev_vif);
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan = false;
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
}
static int slsi_ap_start_validate(struct net_device *dev, struct slsi_dev *sdev, struct cfg80211_ap_settings *settings)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (SLSI_IS_VIF_INDEX_P2P(ndev_vif)) {
SLSI_NET_ERR(dev, "AP start requested on incorrect vif\n");
goto exit_with_error;
}
if (!settings->ssid_len || !settings->ssid) {
SLSI_NET_ERR(dev, "SSID not provided\n");
goto exit_with_error;
}
if (!settings->beacon.head_len || !settings->beacon.head) {
SLSI_NET_ERR(dev, "Beacon not provided\n");
goto exit_with_error;
}
if (!settings->beacon_interval) {
SLSI_NET_ERR(dev, "Beacon Interval not provided\n");
goto exit_with_error;
}
ndev_vif->chandef = &settings->chandef;
ndev_vif->chan = ndev_vif->chandef->chan;
ndev_vif->chandef_saved = settings->chandef;
if (WARN_ON(!ndev_vif->chan))
goto exit_with_error;
if (WARN_ON(ndev_vif->activated))
goto exit_with_error;
if (WARN_ON((ndev_vif->iftype != NL80211_IFTYPE_AP) && (ndev_vif->iftype != NL80211_IFTYPE_P2P_GO)))
goto exit_with_error;
if ((ndev_vif->chan->hw_value <= 14) && (!sdev->fw_SoftAp_2g_40mhz_enabled) &&
(ndev_vif->chandef->width == NL80211_CHAN_WIDTH_40)) {
SLSI_NET_ERR(dev, "Configuration error: 40 MHz on 2.4 GHz is not supported. Channel_no: %d Channel_width: %d\n", ndev_vif->chan->hw_value, slsi_get_chann_info(sdev, ndev_vif->chandef));
goto exit_with_error;
}
return 0;
exit_with_error:
return -EINVAL;
}
static int slsi_get_max_bw_mhz(struct slsi_dev *sdev, u16 prim_chan_cf)
{
int i;
struct ieee80211_regdomain *regd = sdev->device_config.domain_info.regdomain;
if (!regd) {
SLSI_WARN(sdev, "NO regdomain info\n");
return 0;
}
for (i = 0; i < regd->n_reg_rules; i++) {
if ((regd->reg_rules[i].freq_range.start_freq_khz / 1000 <= prim_chan_cf - 10) &&
(regd->reg_rules[i].freq_range.end_freq_khz / 1000 >= prim_chan_cf + 10))
return regd->reg_rules[i].freq_range.max_bandwidth_khz / 1000;
}
SLSI_WARN(sdev, "Freq(%d) not found in regdomain\n", prim_chan_cf);
return 0;
}
int slsi_configure_country_code(struct slsi_dev *sdev, struct cfg80211_ap_settings *settings)
{
const u8 *country_ie = NULL;
char alpha2[SLSI_COUNTRY_CODE_LEN];
/* Reg domain changes */
country_ie = cfg80211_find_ie(WLAN_EID_COUNTRY, settings->beacon.tail, settings->beacon.tail_len);
if (country_ie) {
country_ie += 2;
memcpy(alpha2, country_ie, SLSI_COUNTRY_CODE_LEN);
if (memcmp(sdev->device_config.domain_info.regdomain->alpha2, alpha2, SLSI_COUNTRY_CODE_LEN - 1) != 0) {
if (slsi_set_country_update_regd(sdev, alpha2, SLSI_COUNTRY_CODE_LEN) != 0)
return -EINVAL;
}
}
return 0;
}
int slsi_ap_start_get_indoor_chan_5g(struct slsi_dev *sdev, struct net_device *dev, struct cfg80211_ap_settings *settings, int skip_indoor_check_for_wifi_sharing, int *indoor_channel)
{
struct wiphy *wiphy = sdev->wiphy;
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct ieee80211_channel *channel = NULL;
u32 chan_flags;
u16 center_freq;
if (!skip_indoor_check_for_wifi_sharing && sdev->band_5g_supported &&
((settings->chandef.chan->center_freq / 1000) == 5)) {
channel = ieee80211_get_channel(sdev->wiphy, settings->chandef.chan->center_freq);
if (!channel) {
SLSI_ERR(sdev, "Invalid frequency %d used to start AP. Channel not found\n",
settings->chandef.chan->center_freq);
return -EINVAL;
}
if (ndev_vif->iftype != NL80211_IFTYPE_P2P_GO && (channel->flags & IEEE80211_CHAN_INDOOR_ONLY)) {
int i;
chan_flags = (IEEE80211_CHAN_INDOOR_ONLY | IEEE80211_CHAN_RADAR |
IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_NO_IR);
for (i = 0; i < wiphy->bands[NL80211_BAND_5GHZ]->n_channels; i++) {
if (!(wiphy->bands[NL80211_BAND_5GHZ]->channels[i].flags & chan_flags)) {
center_freq = wiphy->bands[NL80211_BAND_5GHZ]->channels[i].center_freq;
settings->chandef.chan = ieee80211_get_channel(wiphy, center_freq);
if (!settings->chandef.chan) {
SLSI_NET_DBG2(dev, SLSI_MLME, "Invalid chan for frequency %d\n", center_freq);
continue;
}
settings->chandef.center_freq1 = center_freq;
SLSI_DBG1(sdev, SLSI_CFG80211, "ap valid frequency:%d,chan_flags:%x\n",
center_freq,
wiphy->bands[NL80211_BAND_5GHZ]->channels[i].flags);
*indoor_channel = 1;
break;
}
}
if (*indoor_channel == 0) {
SLSI_ERR(sdev, "No valid channel found to start the AP");
return -EINVAL;
}
}
}
return 0;
}
int slsi_modify_ht_capa_ies(struct net_device *dev, struct slsi_dev *sdev, struct cfg80211_ap_settings *settings)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
if (ndev_vif->chandef->width <= NL80211_CHAN_WIDTH_20) {
/* Enable LDPC, SGI20 and SGI40 for both SoftAP & P2PGO if firmware supports */
if (cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, settings->beacon.tail, settings->beacon.tail_len)) {
u8 enforce_ht_cap1 = sdev->fw_ht_cap[0] & (IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_SGI_20);
u8 enforce_ht_cap2 = sdev->fw_ht_cap[1] & (IEEE80211_HT_CAP_RX_STBC >> 8);
slsi_modify_ies(dev, WLAN_EID_HT_CAPABILITY, (u8 *)settings->beacon.tail,
settings->beacon.tail_len, 2, enforce_ht_cap1);
slsi_modify_ies(dev, WLAN_EID_HT_CAPABILITY, (u8 *)settings->beacon.tail,
settings->beacon.tail_len, 3, enforce_ht_cap2);
}
} else if (cfg80211_chandef_valid(ndev_vif->chandef)) {
u8 *ht_operation_ie;
u8 sec_chan_offset = 0;
u8 ch;
u8 bw_40_minus_channels[] = { 40, 48, 153, 161, 5, 6, 7, 8, 9, 10, 11 };
ht_operation_ie = (u8 *)cfg80211_find_ie(WLAN_EID_HT_OPERATION, settings->beacon.tail,
settings->beacon.tail_len);
if (!ht_operation_ie) {
SLSI_NET_ERR(dev, "HT Operation IE is not passed by wpa_supplicant");
return -EINVAL;
}
sec_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
for (ch = 0; ch < ARRAY_SIZE(bw_40_minus_channels); ch++)
if (bw_40_minus_channels[ch] == ndev_vif->chandef->chan->hw_value) {
sec_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
break;
}
/* Change HT Information IE subset 1 */
ht_operation_ie += 3;
*(ht_operation_ie) |= sec_chan_offset;
*(ht_operation_ie) |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
/* For 80MHz, Enable HT Capabilities : Support 40MHz Channel Width, SGI20 and SGI40
* for AP (both softAp as well as P2P GO), if firmware supports.
*/
if (cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, settings->beacon.tail,
settings->beacon.tail_len)) {
u8 enforce_ht_cap1 = sdev->fw_ht_cap[0] & (IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_LDPC_CODING);
u8 enforce_ht_cap2 = sdev->fw_ht_cap[1] & (IEEE80211_HT_CAP_RX_STBC >> 8);
slsi_modify_ies(dev, WLAN_EID_HT_CAPABILITY, (u8 *)settings->beacon.tail,
settings->beacon.tail_len, 2, enforce_ht_cap1);
slsi_modify_ies(dev, WLAN_EID_HT_CAPABILITY, (u8 *)settings->beacon.tail,
settings->beacon.tail_len, 3, enforce_ht_cap2);
}
}
return 0;
}
void slsi_set_ap_mode(struct netdev_vif *ndev_vif, struct cfg80211_ap_settings *settings, bool append_vht_ies)
{
if (append_vht_ies) {
ndev_vif->ap.mode = SLSI_80211_MODE_11AC;
} else if (cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, settings->beacon.tail, settings->beacon.tail_len) &&
cfg80211_find_ie(WLAN_EID_HT_OPERATION, settings->beacon.tail, settings->beacon.tail_len)) {
ndev_vif->ap.mode = SLSI_80211_MODE_11N;
} else {
const u8 *ie;
ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, settings->beacon.tail, settings->beacon.tail_len);
if (ie)
ndev_vif->ap.mode = slsi_get_supported_mode(ie);
}
}
void slsi_store_settings_for_recovery(struct cfg80211_ap_settings *settings, struct netdev_vif *ndev_vif)
{
ndev_vif->backup_settings = *settings;
if (&ndev_vif->backup_settings == settings)
return;
ndev_vif->backup_settings.chandef.chan =
(struct ieee80211_channel *)slsi_mem_dup((u8 *)settings->chandef.chan, sizeof(struct ieee80211_channel));
ndev_vif->backup_settings.beacon.head = slsi_mem_dup((u8 *)settings->beacon.head, settings->beacon.head_len);
ndev_vif->backup_settings.beacon.tail = slsi_mem_dup((u8 *)settings->beacon.tail, settings->beacon.tail_len);
ndev_vif->backup_settings.beacon.beacon_ies = slsi_mem_dup((u8 *)settings->beacon.beacon_ies,
settings->beacon.beacon_ies_len);
ndev_vif->backup_settings.beacon.proberesp_ies = slsi_mem_dup((u8 *)settings->beacon.proberesp_ies,
settings->beacon.proberesp_ies_len);
ndev_vif->backup_settings.beacon.assocresp_ies = slsi_mem_dup((u8 *)settings->beacon.assocresp_ies,
settings->beacon.assocresp_ies_len);
ndev_vif->backup_settings.beacon.probe_resp = slsi_mem_dup((u8 *)settings->beacon.probe_resp,
settings->beacon.probe_resp_len);
ndev_vif->backup_settings.ssid = slsi_mem_dup((u8 *)settings->ssid, settings->ssid_len);
if (settings->ht_cap) {
ndev_vif->backup_settings.ht_cap =
(struct ieee80211_ht_cap *)slsi_mem_dup((u8 *)settings->ht_cap, sizeof(struct ieee80211_ht_cap));
}
if (settings->vht_cap) {
ndev_vif->backup_settings.vht_cap =
(struct ieee80211_vht_cap *)slsi_mem_dup((u8 *)settings->vht_cap, sizeof(struct ieee80211_vht_cap));
}
}
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
static bool slsi_ap_chandef_vht_ht(struct net_device *dev, struct slsi_dev *sdev, int wifi_sharing_channel_switched)
#else
static bool slsi_ap_chandef_vht_ht(struct net_device *dev, struct slsi_dev *sdev)
#endif
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
bool append_vht_ies = false;
SLSI_NET_DBG1(dev, SLSI_MLME, "Channel: %d, Maximum bandwidth: %d\n", ndev_vif->chandef->chan->hw_value,
slsi_get_max_bw_mhz(sdev, ndev_vif->chandef->chan->center_freq));
/* 11ac configuration (5GHz and VHT) */
if (ndev_vif->chandef->chan->hw_value >= 36 && ndev_vif->chandef->chan->hw_value < 165 &&
sdev->fw_vht_enabled && sdev->allow_switch_80_mhz &&
(slsi_get_max_bw_mhz(sdev, ndev_vif->chandef->chan->center_freq) >= 80)) {
u16 oper_chan = ndev_vif->chandef->chan->hw_value;
append_vht_ies = true;
ndev_vif->chandef->width = NL80211_CHAN_WIDTH_80;
SLSI_NET_DBG1(dev, SLSI_MLME, "5 GHz- Include VHT\n");
if (oper_chan >= 36 && oper_chan <= 48)
ndev_vif->chandef->center_freq1 = ieee80211_channel_to_frequency(42, NL80211_BAND_5GHZ);
else if (oper_chan >= 149 && oper_chan <= 161)
ndev_vif->chandef->center_freq1 = ieee80211_channel_to_frequency(155, NL80211_BAND_5GHZ);
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
/* In wifi sharing case, AP can start on STA channel even though it is DFS channel*/
if (wifi_sharing_channel_switched == 1) {
if (oper_chan >= 52 && oper_chan <= 64)
ndev_vif->chandef->center_freq1 = ieee80211_channel_to_frequency(58,
NL80211_BAND_5GHZ);
else if (oper_chan >= 100 && oper_chan <= 112)
ndev_vif->chandef->center_freq1 = ieee80211_channel_to_frequency(106,
NL80211_BAND_5GHZ);
else if (oper_chan >= 116 && oper_chan <= 128)
ndev_vif->chandef->center_freq1 = ieee80211_channel_to_frequency(122,
NL80211_BAND_5GHZ);
else if (oper_chan >= 132 && oper_chan <= 144)
ndev_vif->chandef->center_freq1 = ieee80211_channel_to_frequency(138,
NL80211_BAND_5GHZ);
}
#endif
} else if (sdev->fw_ht_enabled && sdev->allow_switch_40_mhz &&
slsi_get_max_bw_mhz(sdev, ndev_vif->chandef->chan->center_freq) >= 40 &&
((ndev_vif->chandef->chan->hw_value < 165 && ndev_vif->chandef->chan->hw_value >= 36))) {
/* HT40 configuration (5GHz/2GHz and HT) */
u16 oper_chan = ndev_vif->chandef->chan->hw_value;
u8 bw_40_minus_channels[] = { 40, 48, 153, 161, 5, 6, 7, 8, 9, 10, 11 };
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
u8 bw_40_minus_dfs_channels[] = { 144, 136, 128, 120, 112, 104, 64, 56 };
#endif
u8 ch;
ndev_vif->chandef->width = NL80211_CHAN_WIDTH_40;
ndev_vif->chandef->center_freq1 = ndev_vif->chandef->chan->center_freq + 10;
for (ch = 0; ch < ARRAY_SIZE(bw_40_minus_channels); ch++)
if (oper_chan == bw_40_minus_channels[ch]) {
ndev_vif->chandef->center_freq1 = ndev_vif->chandef->chan->center_freq - 10;
break;
}
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
if (wifi_sharing_channel_switched == 1) {
for (ch = 0; ch < ARRAY_SIZE(bw_40_minus_dfs_channels); ch++)
if (oper_chan == bw_40_minus_dfs_channels[ch]) {
ndev_vif->chandef->center_freq1 = ndev_vif->chandef->chan->center_freq - 10;
break;
}
}
#endif
}
return append_vht_ies;
}
void slsi_stop_p2p_group_iface_on_ap_start(struct slsi_dev *sdev)
{
struct net_device *p2p_dev = sdev->netdev[SLSI_NET_INDEX_P2PX_SWLAN];
struct netdev_vif *ndev_p2p_vif;
SLSI_NET_INFO(p2p_dev, "P2P group interface is not removed before starting AP\n");
if (!p2p_dev)
return;
ndev_p2p_vif = netdev_priv(p2p_dev);
SLSI_MUTEX_LOCK(ndev_p2p_vif->vif_mutex);
slsi_vif_cleanup(sdev, p2p_dev, true, 0);
SLSI_MUTEX_UNLOCK(ndev_p2p_vif->vif_mutex);
SLSI_MUTEX_LOCK(sdev->netdev_add_remove_mutex);
rcu_assign_pointer(sdev->netdev_p2p, p2p_dev);
if (sdev->netdev_ap)
rcu_assign_pointer(sdev->netdev[SLSI_NET_INDEX_P2PX_SWLAN], sdev->netdev_ap);
SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex);
}
int slsi_start_ap(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ap_settings *settings)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct net_device *wlan_dev;
u8 device_address[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
int r = 0;
const u8 *wpa_ie_pos = NULL;
int wpa_ie_len = 0;
const u8 *wmm_ie_pos = NULL;
int wmm_ie_len = 0;
bool append_vht_ies = false;
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
int wifi_sharing_channel_switched = 0;
struct netdev_vif *ndev_sta_vif;
int invalid_channel = 0;
#endif
int skip_indoor_check_for_wifi_sharing = 0;
u8 *ds_params_ie = NULL;
struct ieee80211_mgmt *mgmt;
u16 beacon_ie_head_len;
u8 *ht_operation_ie = NULL;
int indoor_channel = 0;
SLSI_MUTEX_LOCK(sdev->start_stop_mutex);
if (sdev->device_state != SLSI_DEVICE_STATE_STARTED) {
SLSI_WARN(sdev, "device not started yet (device_state:%d)\n", sdev->device_state);
r = -EINVAL;
goto exit_with_start_stop_mutex;
}
if (ndev_vif->iftype == NL80211_IFTYPE_AP && sdev->netdev_ap != sdev->netdev[SLSI_NET_INDEX_P2PX_SWLAN])
slsi_stop_p2p_group_iface_on_ap_start(sdev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
/* Abort any ongoing wlan scan. */
wlan_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
if (wlan_dev)
slsi_abort_hw_scan(sdev, wlan_dev);
SLSI_NET_DBG1(dev, SLSI_CFG80211, "AP frequency received: %d\n", settings->chandef.chan->center_freq);
mgmt = (struct ieee80211_mgmt *)settings->beacon.head;
beacon_ie_head_len = settings->beacon.head_len - ((u8 *)mgmt->u.beacon.variable - (u8 *)mgmt);
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
ndev_sta_vif = netdev_priv(wlan_dev);
if (SLSI_IS_VIF_INDEX_MHS(sdev, ndev_vif)) {
if (ndev_sta_vif) {
SLSI_MUTEX_LOCK(ndev_sta_vif->vif_mutex);
if ((ndev_sta_vif->activated) && (ndev_sta_vif->vif_type == FAPI_VIFTYPE_STATION) &&
(ndev_sta_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTING ||
ndev_sta_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED)) {
#ifdef CONFIG_SCSC_WLAN_SINGLE_ANTENNA
invalid_channel = slsi_get_mhs_ws_chan_vsdb(wiphy, dev, settings, sdev,
&wifi_sharing_channel_switched);
#else
invalid_channel = slsi_get_mhs_ws_chan_rsdb(wiphy, dev, settings, sdev,
&wifi_sharing_channel_switched);
#endif
skip_indoor_check_for_wifi_sharing = 1;
if (invalid_channel) {
SLSI_NET_ERR(dev, "Rejecting AP start req at host (invalid channel)\n");
SLSI_MUTEX_UNLOCK(ndev_sta_vif->vif_mutex);
r = -EINVAL;
goto exit_with_vif_mutex;
}
SLSI_DBG1(sdev, SLSI_CFG80211, "Station frequency: %d, SoftAP frequency: %d\n",
ndev_sta_vif->chan->center_freq, settings->chandef.chan->center_freq);
}
SLSI_MUTEX_UNLOCK(ndev_sta_vif->vif_mutex);
}
}
#endif
memset(&ndev_vif->ap, 0, sizeof(ndev_vif->ap));
/* Initialise all allocated peer structures to remove old data. */
/*slsi_netif_init_all_peers(sdev, dev);*/
r = slsi_configure_country_code(sdev, settings);
if (r != 0)
goto exit_with_vif_mutex;
r = slsi_ap_start_get_indoor_chan_5g(sdev, dev, settings, skip_indoor_check_for_wifi_sharing, &indoor_channel);
if (r != 0)
goto exit_with_vif_mutex;
r = slsi_ap_start_validate(dev, sdev, settings);
if (r != 0)
goto exit_with_vif_mutex;
if (ndev_vif->iftype == NL80211_IFTYPE_P2P_GO) {
struct net_device *p2p_dev;
slsi_p2p_group_start_remove_unsync_vif(sdev);
p2p_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_P2P);
SLSI_ETHER_COPY(device_address, p2p_dev->dev_addr);
if (keep_alive_period != SLSI_P2PGO_KEEP_ALIVE_PERIOD_SEC)
if (slsi_set_uint_mib(sdev, NULL, SLSI_PSID_UNIFI_MLMEGO_KEEP_ALIVE_TIMEOUT,
keep_alive_period) != 0) {
SLSI_NET_ERR(dev, "P2PGO Keep Alive MIB set failed");
r = -EINVAL;
goto exit_with_vif_mutex;
}
}
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
append_vht_ies = slsi_ap_chandef_vht_ht(dev, sdev, wifi_sharing_channel_switched);
if (slsi_check_channelization(sdev, ndev_vif->chandef, wifi_sharing_channel_switched) != 0) {
#else
append_vht_ies = slsi_ap_chandef_vht_ht(dev, sdev);
if (slsi_check_channelization(sdev, ndev_vif->chandef, 0) != 0) {
#endif
r = -EINVAL;
goto exit_with_vif_mutex;
}
if (ndev_vif->iftype == NL80211_IFTYPE_AP) {
/* Legacy AP */
if (ndev_vif->chandef->width == NL80211_CHAN_WIDTH_20)
slsi_ap_start_obss_scan(sdev, dev, ndev_vif);
}
r = slsi_modify_ht_capa_ies(dev, sdev, settings);
if (r != 0)
goto exit_with_vif_mutex;
if (indoor_channel == 1
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
|| (wifi_sharing_channel_switched == 1)
#endif
) {
slsi_modify_ies_on_channel_switch(dev, settings, ds_params_ie, ht_operation_ie, mgmt, beacon_ie_head_len);
}
ndev_vif->vif_type = FAPI_VIFTYPE_AP;
if (slsi_mlme_add_vif(sdev, dev, dev->dev_addr, device_address) != 0) {
SLSI_NET_ERR(dev, "slsi_mlme_add_vif failed\n");
r = -EINVAL;
goto exit_with_vif_mutex;
}
if (slsi_vif_activated(sdev, dev) != 0) {
SLSI_NET_ERR(dev, "slsi_vif_activated failed\n");
goto exit_with_vif;
}
/* Extract the WMM and WPA IEs from settings->beacon.tail - This is sent in add_info_elements and shouldn't be included in start_req
* Cache IEs to be used in later add_info_elements_req. The IEs would be freed during AP stop
*/
wpa_ie_pos = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA, settings->beacon.tail, settings->beacon.tail_len);
if (wpa_ie_pos) {
wpa_ie_len = *(wpa_ie_pos + 1) + 2; /* For 0xdd (1) and Tag Length (1) */
SLSI_NET_DBG2(dev, SLSI_CFG80211, "WPA IE found: Length = %zu\n", wpa_ie_len);
SLSI_EC_GOTO(slsi_cache_ies(wpa_ie_pos, wpa_ie_len, &ndev_vif->ap.cache_wpa_ie, &ndev_vif->ap.wpa_ie_len), r, exit_with_vif);
}
wmm_ie_pos = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WMM, settings->beacon.tail, settings->beacon.tail_len);
if (wmm_ie_pos) {
wmm_ie_len = *(wmm_ie_pos + 1) + 2;
SLSI_NET_DBG2(dev, SLSI_CFG80211, "WMM IE found: Length = %zu\n", wmm_ie_len);
SLSI_EC_GOTO(slsi_cache_ies(wmm_ie_pos, wmm_ie_len, &ndev_vif->ap.cache_wmm_ie, &ndev_vif->ap.wmm_ie_len), r, exit_with_vif);
}
slsi_clear_cached_ies(&ndev_vif->ap.add_info_ies, &ndev_vif->ap.add_info_ies_len);
/* Set Vendor specific IEs (WPA, WMM, WPS, P2P) for Beacon, Probe Response and Association Response
* The Beacon and Assoc Rsp IEs can include Extended Capability (WLAN_EID_EXT_CAPAB) IE when supported.
* Some other IEs (like internetworking, etc) can also come if supported.
* The add_info should include only vendor specific IEs and other IEs should be removed if supported in future.
*/
if ((wmm_ie_pos) || (wpa_ie_pos) || (settings->beacon.beacon_ies_len > 0 && settings->beacon.beacon_ies)) {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Add info elements for beacon\n");
SLSI_EC_GOTO(slsi_ap_prepare_add_info_ies(ndev_vif, settings->beacon.beacon_ies, settings->beacon.beacon_ies_len), r, exit_with_vif);
SLSI_EC_GOTO(slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_BEACON, ndev_vif->ap.add_info_ies, ndev_vif->ap.add_info_ies_len), r, exit_with_vif);
slsi_clear_cached_ies(&ndev_vif->ap.add_info_ies, &ndev_vif->ap.add_info_ies_len);
}
if ((wmm_ie_pos) || (wpa_ie_pos) || (settings->beacon.proberesp_ies_len > 0 && settings->beacon.proberesp_ies)) {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Add info elements for probe response\n");
SLSI_EC_GOTO(slsi_ap_prepare_add_info_ies(ndev_vif, settings->beacon.proberesp_ies, settings->beacon.proberesp_ies_len), r, exit_with_vif);
SLSI_EC_GOTO(slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_PROBE_RESPONSE, ndev_vif->ap.add_info_ies, ndev_vif->ap.add_info_ies_len), r, exit_with_vif);
slsi_clear_cached_ies(&ndev_vif->ap.add_info_ies, &ndev_vif->ap.add_info_ies_len);
}
if ((wmm_ie_pos) || (wpa_ie_pos) || (settings->beacon.assocresp_ies_len > 0 && settings->beacon.assocresp_ies)) {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Add info elements for assoc response\n");
SLSI_EC_GOTO(slsi_ap_prepare_add_info_ies(ndev_vif, settings->beacon.assocresp_ies, settings->beacon.assocresp_ies_len), r, exit_with_vif);
SLSI_EC_GOTO(slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_ASSOCIATION_RESPONSE, ndev_vif->ap.add_info_ies, ndev_vif->ap.add_info_ies_len), r, exit_with_vif);
slsi_clear_cached_ies(&ndev_vif->ap.add_info_ies, &ndev_vif->ap.add_info_ies_len);
}
if (ndev_vif->iftype == NL80211_IFTYPE_P2P_GO) {
u32 af_bmap_active = SLSI_ACTION_FRAME_PUBLIC;
u32 af_bmap_suspended = SLSI_ACTION_FRAME_PUBLIC;
r = slsi_mlme_register_action_frame(sdev, dev, af_bmap_active, af_bmap_suspended);
if (r != 0) {
SLSI_NET_ERR(dev, "slsi_mlme_register_action_frame failed: resultcode = %d\n", r);
goto exit_with_vif;
}
}
slsi_set_ap_mode(ndev_vif, settings, append_vht_ies);
r = slsi_mlme_start(sdev, dev, dev->dev_addr, settings, wpa_ie_pos, wmm_ie_pos, append_vht_ies);
cfg80211_ch_switch_notify(dev, &settings->chandef);
#ifdef CONFIG_SCSC_WLAN_WIFI_SHARING
if (r == 0)
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Soft Ap started on frequency: %d\n",
settings->chandef.chan->center_freq);
if (SLSI_IS_VIF_INDEX_MHS(sdev, ndev_vif))
ndev_vif->chan = settings->chandef.chan;
#endif
if (r != 0) {
SLSI_NET_ERR(dev, "Start ap failed: resultcode = %d frequency = %d\n", r,
settings->chandef.chan->center_freq);
goto exit_with_vif;
} else if (ndev_vif->iftype == NL80211_IFTYPE_P2P_GO) {
SLSI_P2P_STATE_CHANGE(sdev, P2P_GROUP_FORMED_GO);
}
ndev_vif->ap.beacon_interval = settings->beacon_interval;
ndev_vif->ap.ssid_len = settings->ssid_len;
memcpy(ndev_vif->ap.ssid, settings->ssid, settings->ssid_len);
netif_carrier_on(dev);
if (ndev_vif->ipaddress)
/* Static IP is assigned already */
slsi_ip_address_changed(sdev, dev, ndev_vif->ipaddress);
r = slsi_read_disconnect_ind_timeout(sdev, SLSI_PSID_UNIFI_DISCONNECT_TIMEOUT);
if (r != 0)
sdev->device_config.ap_disconnect_ind_timeout = *sdev->sig_wait_cfm_timeout;
SLSI_NET_DBG2(dev, SLSI_CFG80211, "slsi_read_disconnect_ind_timeout: timeout = %d", sdev->device_config.ap_disconnect_ind_timeout);
goto exit_with_vif_mutex;
exit_with_vif:
slsi_clear_cached_ies(&ndev_vif->ap.add_info_ies, &ndev_vif->ap.add_info_ies_len);
if (slsi_mlme_del_vif(sdev, dev) != 0)
SLSI_NET_ERR(dev, "slsi_mlme_del_vif failed\n");
slsi_vif_deactivated(sdev, dev);
r = -EINVAL;
exit_with_vif_mutex:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
exit_with_start_stop_mutex:
SLSI_MUTEX_UNLOCK(sdev->start_stop_mutex);
slsi_store_settings_for_recovery(settings, ndev_vif);
return r;
}
int slsi_change_beacon(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_beacon_data *info)
{
SLSI_UNUSED_PARAMETER(info);
return -EOPNOTSUPP;
}
int slsi_stop_ap(struct wiphy *wiphy, struct net_device *dev)
{
slsi_reset_throughput_stats(dev);
return 0;
}
static int slsi_p2p_group_mgmt_tx(const struct ieee80211_mgmt *mgmt, struct wiphy *wiphy,
struct net_device *dev, struct ieee80211_channel *chan,
unsigned int wait, const u8 *buf, size_t len,
bool dont_wait_for_ack, u64 *cookie)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif;
struct net_device *netdev;
int subtype = slsi_get_public_action_subtype(mgmt);
int r = 0;
u32 host_tag = slsi_tx_mgmt_host_tag(sdev);
u16 freq = 0;
u32 dwell_time = SLSI_FORCE_SCHD_ACT_FRAME_MSEC;
u16 data_unit_desc = FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME;
if (sdev->p2p_group_exp_frame != SLSI_PA_INVALID) {
SLSI_NET_ERR(dev, "sdev->p2p_group_exp_frame : %d\n", sdev->p2p_group_exp_frame);
return -EINVAL;
}
netdev = slsi_get_netdev(sdev, SLSI_NET_INDEX_P2PX_SWLAN);
ndev_vif = netdev_priv(netdev);
if (!ndev_vif->chan) {
SLSI_NET_ERR(dev, "ndev_vif->chan is null\n");
return -EINVAL;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Sending Action frame (%s) on p2p group vif (%d), vif_index = %d,"
"vif_type = %d, chan->hw_value = %d, ndev_vif->chan->hw_value = %d, wait = %d,"
"sdev->p2p_group_exp_frame = %d\n", slsi_pa_subtype_text(subtype), ndev_vif->activated,
ndev_vif->ifnum, ndev_vif->vif_type, chan->hw_value, ndev_vif->chan->hw_value, wait,
sdev->p2p_group_exp_frame);
if (!((ndev_vif->iftype == NL80211_IFTYPE_P2P_GO) || (ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT)))
goto exit_with_error;
if (chan->hw_value != ndev_vif->chan->hw_value) {
freq = SLSI_FREQ_HOST_TO_FW(chan->center_freq);
dwell_time = wait;
}
/* Incase of GO dont wait for resp/cfm packets for go-negotiation.*/
if (subtype != SLSI_P2P_PA_GO_NEG_RSP)
sdev->p2p_group_exp_frame = slsi_get_exp_peer_frame_subtype(subtype);
r = slsi_mlme_send_frame_mgmt(sdev, netdev, buf, len, data_unit_desc, FAPI_MESSAGETYPE_IEEE80211_ACTION, host_tag, freq, dwell_time * 1000, 0);
if (r)
goto exit_with_lock;
slsi_assign_cookie_id(cookie, &ndev_vif->mgmt_tx_cookie);
r = slsi_set_mgmt_tx_data(ndev_vif, *cookie, host_tag, buf, len); /* If error then it is returned in exit */
goto exit_with_lock;
exit_with_error:
r = -EINVAL;
exit_with_lock:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
/* Handle mgmt_tx callback for P2P modes */
static int slsi_p2p_mgmt_tx(const struct ieee80211_mgmt *mgmt, struct wiphy *wiphy,
struct net_device *dev, struct netdev_vif *ndev_vif,
struct ieee80211_channel *chan, unsigned int wait,
const u8 *buf, size_t len, bool dont_wait_for_ack, u64 *cookie)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
int ret = 0;
if (ieee80211_is_action(mgmt->frame_control)) {
u16 host_tag = slsi_tx_mgmt_host_tag(sdev);
int subtype = slsi_get_public_action_subtype(mgmt);
u8 exp_peer_frame;
u32 dwell_time = 0;
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Action frame (%s), unsync_vif_active (%d)\n", slsi_pa_subtype_text(subtype), ndev_vif->activated);
if (subtype == SLSI_PA_INVALID) {
SLSI_NET_ERR(dev, "Invalid Action frame subtype\n");
goto exit_with_error;
}
/* Check if unsync vif is available */
if (sdev->p2p_state == P2P_IDLE_NO_VIF)
if (slsi_p2p_vif_activate(sdev, dev, chan, wait, false) != 0)
goto exit_with_error;
/* Clear Probe Response IEs if vif was already present with a different channel */
if (ndev_vif->driver_channel != chan->hw_value) {
if (slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_PROBE_RESPONSE, NULL, 0) != 0)
SLSI_NET_ERR(dev, "Clearing Probe Response IEs failed for unsync vif\n");
slsi_unsync_vif_set_probe_rsp_ie(ndev_vif, NULL, 0);
if (slsi_mlme_set_channel(sdev, dev, chan, SLSI_FW_CHANNEL_DURATION_UNSPECIFIED, 0, 0) != 0)
goto exit_with_vif;
else {
ndev_vif->chan = chan;
ndev_vif->driver_channel = chan->hw_value;
}
}
/* Check if peer frame response is expected */
exp_peer_frame = slsi_get_exp_peer_frame_subtype(subtype);
if (exp_peer_frame != SLSI_PA_INVALID) {
if ((subtype == SLSI_P2P_PA_GO_NEG_RSP) && (slsi_p2p_get_go_neg_rsp_status(dev, mgmt) != SLSI_P2P_STATUS_CODE_SUCCESS)) {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "GO_NEG_RSP Tx, peer response not expected\n");
exp_peer_frame = SLSI_PA_INVALID;
} else {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Peer response expected with action frame (%s)\n",
slsi_pa_subtype_text(exp_peer_frame));
if (ndev_vif->mgmt_tx_data.exp_frame != SLSI_PA_INVALID)
(void)slsi_set_mgmt_tx_data(ndev_vif, 0, 0, NULL, 0);
if (subtype == SLSI_P2P_PA_GO_NEG_RSP)
ndev_vif->drv_in_p2p_procedure = true;
/* Change Force Schedule Duration as peer response is expected */
if (wait)
dwell_time = wait;
else
dwell_time = SLSI_FORCE_SCHD_ACT_FRAME_MSEC;
}
}
slsi_assign_cookie_id(cookie, &ndev_vif->mgmt_tx_cookie);
/* Send the action frame, transmission status indication would be received later */
if (slsi_mlme_send_frame_mgmt(sdev, dev, buf, len, FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME, FAPI_MESSAGETYPE_IEEE80211_ACTION, host_tag, 0, dwell_time * 1000, 0) != 0)
goto exit_with_vif;
if (subtype == SLSI_P2P_PA_GO_NEG_CFM)
ndev_vif->drv_in_p2p_procedure = false;
else if ((subtype == SLSI_P2P_PA_GO_NEG_REQ) || (subtype == SLSI_P2P_PA_PROV_DISC_REQ))
ndev_vif->drv_in_p2p_procedure = true;
/* If multiple frames are requested for tx, only the info of first frame would be stored */
if (ndev_vif->mgmt_tx_data.host_tag == 0) {
unsigned int n_wait = 0;
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Store mgmt frame tx data for cookie = 0x%llx\n", *cookie);
ret = slsi_set_mgmt_tx_data(ndev_vif, *cookie, host_tag, buf, len);
if (ret != 0)
goto exit_with_vif;
ndev_vif->mgmt_tx_data.exp_frame = exp_peer_frame;
SLSI_P2P_STATE_CHANGE(sdev, P2P_ACTION_FRAME_TX_RX);
if ((exp_peer_frame == SLSI_P2P_PA_GO_NEG_RSP) || (exp_peer_frame == SLSI_P2P_PA_GO_NEG_CFM))
/* Retain vif for larger duration that wpa_supplicant asks to wait,
* during GO-Negotiation to allow peer to retry GO neg in bad radio condition.
* Some of phones retry GO-Negotiation after 2 seconds
*/
n_wait = SLSI_P2P_NEG_PROC_UNSYNC_VIF_RETAIN_DURATION;
else if (exp_peer_frame != SLSI_PA_INVALID)
/* If a peer response is expected queue work to retain vif till wait time else the work will be handled in mgmt_tx_cancel_wait */
n_wait = wait + SLSI_P2P_MGMT_TX_EXTRA_MSEC;
if (n_wait) {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "retain unsync vif for duration (%d) msec\n", n_wait);
slsi_p2p_queue_unsync_vif_del_work(ndev_vif, n_wait);
}
} else {
/* Already a frame Tx is in progress, send immediate tx_status as success. Sending immediate tx status should be ok
* as supplicant is in another procedure and so these frames would be mostly only response frames.
*/
WLBT_WARN_ON(sdev->p2p_state != P2P_ACTION_FRAME_TX_RX);
if (!dont_wait_for_ack) {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Send immediate tx_status (cookie = 0x%llx)\n", *cookie);
cfg80211_mgmt_tx_status(&ndev_vif->wdev, *cookie, buf, len, true, GFP_KERNEL);
}
}
goto exit;
}
/* Else send failure for unexpected management frame */
SLSI_NET_ERR(dev, "Drop Tx frame: Unexpected Management frame\n");
goto exit_with_error;
exit_with_vif:
if (sdev->p2p_state != P2P_LISTENING)
slsi_p2p_vif_deactivate(sdev, dev, true);
exit_with_error:
ret = -EINVAL;
exit:
return ret;
}
int slsi_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
struct net_device *dev = wdev->netdev;
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_INFO(dev, "iface_num = %d, cookie = 0x%llx, vif_index = %d, vif_type = %d,"
"sdev->p2p_state = %d, ndev_vif->mgmt_tx_data.cookie = 0x%llx, sdev->p2p_group_exp_frame = %d,"
"sdev->wlan_unsync_vif_state = %d\n", (int)ndev_vif->ifnum, cookie,(int)ndev_vif->ifnum,
(int)ndev_vif->vif_type, sdev->p2p_state, ndev_vif->mgmt_tx_data.cookie,
(int)sdev->p2p_group_exp_frame, sdev->wlan_unsync_vif_state);
/* If device was in frame tx_rx state, clear mgmt tx data and change state */
if (SLSI_IS_VIF_INDEX_P2P(ndev_vif) && (sdev->p2p_state == P2P_ACTION_FRAME_TX_RX) && (ndev_vif->mgmt_tx_data.cookie == cookie)) {
if (ndev_vif->mgmt_tx_data.exp_frame != SLSI_PA_INVALID)
(void)slsi_mlme_reset_dwell_time(sdev, dev);
(void)slsi_set_mgmt_tx_data(ndev_vif, 0, 0, NULL, 0);
ndev_vif->mgmt_tx_data.exp_frame = SLSI_PA_INVALID;
if (delayed_work_pending(&ndev_vif->unsync.roc_expiry_work)) {
SLSI_P2P_STATE_CHANGE(sdev, P2P_LISTENING);
} else {
slsi_p2p_queue_unsync_vif_del_work(ndev_vif, SLSI_P2P_UNSYNC_VIF_EXTRA_MSEC);
SLSI_P2P_STATE_CHANGE(ndev_vif->sdev, P2P_IDLE_VIF_ACTIVE);
}
} else if ((SLSI_IS_P2P_GROUP_STATE(sdev)) && (sdev->p2p_group_exp_frame != SLSI_PA_INVALID)) {
/* acquire mutex lock if it is not group net dev */
slsi_clear_offchannel_data(sdev, (!SLSI_IS_VIF_INDEX_P2P_GROUP(sdev, ndev_vif)) ? true : false);
} else if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif) && (sdev->wlan_unsync_vif_state == WLAN_UNSYNC_VIF_TX) && (ndev_vif->mgmt_tx_data.cookie == cookie)) {
sdev->wlan_unsync_vif_state = WLAN_UNSYNC_VIF_ACTIVE;
cancel_delayed_work(&ndev_vif->unsync.hs2_del_vif_work);
queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.hs2_del_vif_work, msecs_to_jiffies(SLSI_HS2_UNSYNC_VIF_EXTRA_MSEC));
if (ndev_vif->mgmt_tx_data.exp_frame != SLSI_PA_INVALID) {
ndev_vif->mgmt_tx_data.exp_frame = SLSI_PA_INVALID;
(void)slsi_mlme_reset_dwell_time(sdev, dev);
}
} else if (ndev_vif->activated && ndev_vif->vif_type == FAPI_VIFTYPE_STATION
&& ndev_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED) {
if (ndev_vif->mgmt_tx_data.exp_frame != SLSI_PA_INVALID) {
ndev_vif->mgmt_tx_data.exp_frame = SLSI_PA_INVALID;
(void)slsi_mlme_reset_dwell_time(sdev, dev);
}
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return 0;
}
void slsi_mgmt_frame_register(struct wiphy *wiphy,
struct wireless_dev *wdev,
u16 frame_type, bool reg)
{
struct net_device *dev = wdev->netdev;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
SLSI_UNUSED_PARAMETER(frame_type);
SLSI_UNUSED_PARAMETER(reg);
if (WARN_ON(!dev))
return;
SLSI_UNUSED_PARAMETER(sdev);
}
int slsi_wlan_mgmt_tx(struct slsi_dev *sdev, struct net_device *dev,
struct ieee80211_channel *chan, unsigned int wait,
const u8 *buf, size_t len, bool dont_wait_for_ack, u64 *cookie)
{
u32 host_tag = slsi_tx_mgmt_host_tag(sdev);
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
u8 exp_peer_frame = SLSI_PA_INVALID;
int subtype = SLSI_PA_INVALID;
if (!ieee80211_is_auth(mgmt->frame_control))
slsi_wlan_dump_public_action_subtype(sdev, mgmt, true);
if (ieee80211_is_action(mgmt->frame_control)) {
subtype = slsi_get_public_action_subtype(mgmt);
if (subtype != SLSI_PA_INVALID)
exp_peer_frame = slsi_get_exp_peer_frame_subtype(subtype);
}
if (!ndev_vif->activated) {
if (subtype >= SLSI_PA_GAS_INITIAL_REQ_SUBTYPE && subtype <= SLSI_PA_GAS_COMEBACK_RSP_SUBTYPE) {
ndev_vif->mgmt_tx_gas_frame = true;
SLSI_ETHER_COPY(ndev_vif->gas_frame_mac_addr, mgmt->sa);
} else {
ndev_vif->mgmt_tx_gas_frame = false;
}
r = slsi_wlan_unsync_vif_activate(sdev, dev, chan, wait);
if (r)
return r;
r = slsi_mlme_send_frame_mgmt(sdev, dev, buf, len, FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME, FAPI_MESSAGETYPE_IEEE80211_ACTION, host_tag, 0, wait * 1000, 0);
if (r)
goto exit_with_vif;
sdev->wlan_unsync_vif_state = WLAN_UNSYNC_VIF_TX;
queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.hs2_del_vif_work, msecs_to_jiffies(wait));
} else {
/* vif is active*/
if (ieee80211_is_auth(mgmt->frame_control)) {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Transmit on the current frequency\n");
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION) {
ndev_vif->sta.sae_auth_type = mgmt->u.auth.auth_transaction;
wait = sdev->sae_dwell_time;
}
r = slsi_mlme_send_frame_mgmt(sdev, dev, buf, len, FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME,
FAPI_MESSAGETYPE_IEEE80211_MGMT, host_tag, 0, wait * 1000, 0);
if (r)
return r;
} else if (ndev_vif->vif_type == FAPI_VIFTYPE_PRECONNECT) {
if (subtype >= SLSI_PA_GAS_INITIAL_REQ_SUBTYPE && subtype <= SLSI_PA_GAS_COMEBACK_RSP_SUBTYPE) {
slsi_wlan_unsync_vif_deactivate(sdev, dev, true);
ndev_vif->mgmt_tx_gas_frame = true;
SLSI_ETHER_COPY(ndev_vif->gas_frame_mac_addr, mgmt->sa);
r = slsi_wlan_unsync_vif_activate(sdev, dev, chan, wait);
if (r)
return r;
} else {
if (ndev_vif->mgmt_tx_gas_frame) {
slsi_wlan_unsync_vif_deactivate(sdev, dev, true);
ndev_vif->mgmt_tx_gas_frame = false;
r = slsi_wlan_unsync_vif_activate(sdev, dev, chan, wait);
if (r)
return r;
}
}
cancel_delayed_work(&ndev_vif->unsync.hs2_del_vif_work);
/*even if we fail to cancel the delayed work, we shall go ahead and send action frames*/
if (ndev_vif->driver_channel != chan->hw_value) {
r = slsi_mlme_set_channel(sdev, dev, chan, SLSI_FW_CHANNEL_DURATION_UNSPECIFIED, 0, 0);
if (r)
goto exit_with_vif;
else
ndev_vif->driver_channel = chan->hw_value;
}
SLSI_NET_DBG1(dev, SLSI_CFG80211, "wlan unsync vif is active, send frame on channel freq = %d\n", chan->center_freq);
r = slsi_mlme_send_frame_mgmt(sdev, dev, buf, len, FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME, FAPI_MESSAGETYPE_IEEE80211_ACTION, host_tag, 0, wait * 1000, 0);
if (r)
goto exit_with_vif;
sdev->wlan_unsync_vif_state = WLAN_UNSYNC_VIF_TX;
queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.hs2_del_vif_work, msecs_to_jiffies(wait));
} else if (ndev_vif->chan && ndev_vif->chan->hw_value == chan->hw_value) {
/* Dwell time not provided when sending frames on connected channel. */
SLSI_NET_DBG1(dev, SLSI_CFG80211, "STA VIF is active on same channel, send frame on channel freq %d\n", chan->center_freq);
r = slsi_mlme_send_frame_mgmt(sdev, dev, buf, len, FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME, FAPI_MESSAGETYPE_IEEE80211_ACTION, host_tag, 0, 0, 0);
if (r)
return r;
} else {
SLSI_NET_DBG1(dev, SLSI_CFG80211, "STA VIF is active on a different channel, send frame on channel freq %d\n", chan->center_freq);
/* Dwell time for GAS (ANQP) request packet set to 100ms if dwell time(wait) is more than 100ms */
if ((subtype == SLSI_PA_GAS_INITIAL_REQ_SUBTYPE || subtype == SLSI_PA_GAS_COMEBACK_REQ_SUBTYPE) && wait > SLSI_FW_MAX_OFFCHANNEL_DWELL_TIME)
wait = SLSI_FW_MAX_OFFCHANNEL_DWELL_TIME;
r = slsi_mlme_send_frame_mgmt(sdev, dev, buf, len, FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME, FAPI_MESSAGETYPE_IEEE80211_ACTION, host_tag, SLSI_FREQ_HOST_TO_FW(chan->center_freq), wait * 1000, 0);
if (r)
return r;
}
}
ndev_vif->mgmt_tx_data.exp_frame = exp_peer_frame;
slsi_assign_cookie_id(cookie, &ndev_vif->mgmt_tx_cookie);
slsi_set_mgmt_tx_data(ndev_vif, *cookie, host_tag, buf, len);
return r;
exit_with_vif:
slsi_wlan_unsync_vif_deactivate(sdev, dev, true);
return r;
}
int slsi_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie)
{
/* Note to explore for AP ::All public action frames which come to host should be handled properly
* Additionally, if PMF is negotiated over the link, the host shall not issue "mlme-send-frame.request"
* primitive for action frames before the pairwise keys have been installed in F/W. Presently, for
* SoftAP with PMF support, there is no scenario in which slsi_mlme_send_frame will be called for
* action frames for VIF TYPE = AP.
*/
struct net_device *dev = wdev->netdev;
struct ieee80211_channel *chan = params->chan;
bool offchan = params->offchan;
unsigned int wait = params->wait;
const u8 *buf = params->buf;
size_t len = params->len;
bool no_cck = params->no_cck;
bool dont_wait_for_ack = params->dont_wait_for_ack;
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *)buf;
int r = 0;
SLSI_UNUSED_PARAMETER(offchan);
SLSI_UNUSED_PARAMETER(no_cck);
SLSI_MUTEX_LOCK(sdev->start_stop_mutex);
if (sdev->device_state != SLSI_DEVICE_STATE_STARTED) {
SLSI_WARN(sdev, "device not started yet (device_state:%d)\n", sdev->device_state);
r = -EINVAL;
goto exit;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_INFO(dev, "fc:%d, len:%d\n", mgmt->frame_control, len);
if (!(ieee80211_is_auth(mgmt->frame_control))) {
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Mgmt Frame Tx: iface_num = %d, channel = %d, wait = %d, noAck = %d,"
"offchannel = %d, mgmt->frame_control = %d, vif_type = %d\n", ndev_vif->ifnum, chan->hw_value,
wait, dont_wait_for_ack, offchan, mgmt->frame_control, ndev_vif->vif_type);
} else {
if (!ndev_vif->activated) {
SLSI_NET_ERR(dev, "Drop Auth Frame: VIF not activated\n");
r = -EINVAL;
goto exit;
}
SLSI_INFO(sdev, "Send Auth Frame\n");
}
if (!(ieee80211_is_mgmt(mgmt->frame_control))) {
SLSI_NET_ERR(dev, "Drop Tx frame: Not a Management frame\n");
r = -EINVAL;
goto exit;
}
if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif) || (ndev_vif->iftype == NL80211_IFTYPE_AP && (ieee80211_is_auth(mgmt->frame_control)))) {
r = slsi_wlan_mgmt_tx(SDEV_FROM_WIPHY(wiphy), dev, chan, wait, buf, len, dont_wait_for_ack, cookie);
goto exit;
}
/*P2P*/
/* Drop Probe Responses which can come in P2P Device and P2P Group role */
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
/* Ideally supplicant doesn't expect Tx status for Probe Rsp. Send tx status just in case it requests ack */
if (!dont_wait_for_ack) {
slsi_assign_cookie_id(cookie, &ndev_vif->mgmt_tx_cookie);
cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_KERNEL);
}
goto exit;
}
if (SLSI_IS_VIF_INDEX_P2P(ndev_vif)) {
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
/* Check whether STA scan is running or not. If yes, then abort the STA scan */
slsi_abort_sta_scan(sdev);
if (SLSI_IS_P2P_GROUP_STATE(sdev))
r = slsi_p2p_group_mgmt_tx(mgmt, wiphy, dev, chan, wait, buf, len, dont_wait_for_ack, cookie);
else
r = slsi_p2p_mgmt_tx(mgmt, wiphy, dev, ndev_vif, chan, wait, buf, len, dont_wait_for_ack, cookie);
} else if (SLSI_IS_VIF_INDEX_P2P_GROUP(sdev, ndev_vif))
if (ndev_vif->chan && chan->hw_value == ndev_vif->chan->hw_value) {
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
u16 host_tag = slsi_tx_mgmt_host_tag(sdev);
r = slsi_mlme_send_frame_mgmt(sdev, dev, buf, len, FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME, FAPI_MESSAGETYPE_IEEE80211_ACTION, host_tag, 0, 0, 0);
if (r) {
SLSI_NET_ERR(dev, "Failed to send action frame, r = %d\n", r);
goto exit;
}
slsi_assign_cookie_id(cookie, &ndev_vif->mgmt_tx_cookie);
r = slsi_set_mgmt_tx_data(ndev_vif, *cookie, host_tag, buf, len);
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
SLSI_MUTEX_UNLOCK(sdev->start_stop_mutex);
return r;
}
/* cw = (2^n -1). But WMM IE needs value n. */
u8 slsi_get_ecw(int cw)
{
int ecw = 0;
cw = cw + 1;
do {
cw = cw >> 1;
ecw++;
} while (cw);
return ecw - 1;
}
int slsi_set_txq_params(struct wiphy *wiphy, struct net_device *ndev,
struct ieee80211_txq_params *params)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(ndev);
struct slsi_wmm_parameter_element *wmm_ie = &ndev_vif->ap.wmm_ie;
int r = 0;
int ac = params->ac;
/* Index remapping for AC from nl80211_ac enum to slsi_ac_index_wmm enum (index to be used in the IE).
* Kernel version less than 3.5.0 doesn't support nl80211_ac enum hence not using the nl80211_ac enum.
* Eg. NL80211_AC_VO (index value 0) would be remapped to AC_VO (index value 3).
* Don't change the order of array elements.
*/
u8 ac_index_map[4] = { AC_VO, AC_VI, AC_BE, AC_BK };
int ac_remapped = ac_index_map[ac];
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG2(ndev, SLSI_CFG80211, " ac= %x, ac_remapped = %d aifs = %d, cmin=%x cmax = %x, txop = %x,"
"vif_index = %d vif_type = %d", ac, ac_remapped, params->aifs, params->cwmin, params->cwmax,
params->txop, ndev_vif->ifnum, ndev_vif->vif_type);
if (ndev_vif->activated) {
wmm_ie->ac[ac_remapped].aci_aifsn = (ac_remapped << 5) | (params->aifs & 0x0f);
wmm_ie->ac[ac_remapped].ecw = ((slsi_get_ecw(params->cwmax)) << 4) | ((slsi_get_ecw(params->cwmin)) & 0x0f);
wmm_ie->ac[ac_remapped].txop_limit = cpu_to_le16(params->txop);
if (ac == 3) {
wmm_ie->eid = SLSI_WLAN_EID_VENDOR_SPECIFIC;
wmm_ie->len = 24;
wmm_ie->oui[0] = 0x00;
wmm_ie->oui[1] = 0x50;
wmm_ie->oui[2] = 0xf2;
wmm_ie->oui_type = WLAN_OUI_TYPE_MICROSOFT_WMM;
wmm_ie->oui_subtype = 1;
wmm_ie->version = 1;
wmm_ie->qos_info = 0;
wmm_ie->reserved = 0;
r = slsi_mlme_add_info_elements(sdev, ndev, FAPI_PURPOSE_LOCAL, (const u8 *)wmm_ie, sizeof(struct slsi_wmm_parameter_element));
if (r)
SLSI_NET_ERR(ndev, "Error sending TX Queue Parameters for AP error = %d", r);
}
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
#ifdef CONFIG_SCSC_WLAN_SAE_CONFIG
int slsi_synchronised_response(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_external_auth_params *params)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
if (ndev_vif->sta.wpa3_sae_reconnection && !SLSI_ETHER_EQUAL(params->bssid, ndev_vif->sta.bssid)) {
SLSI_NET_ERR(dev, "Droping synchronised_resp for bssid:%pM\n", params->bssid);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
ndev_vif->sta.wpa3_sae_reconnection = false;
return 0;
}
#endif
r = slsi_mlme_synchronised_response(sdev, dev, params);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
#endif
static int slsi_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_update_ft_ies_params *ftie)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION) {
const u8 *keo_ie_pos = NULL;
u8 *ie_buf = NULL;
int ie_len = 0;
int ie_buf_len = 0;
keo_ie_pos = cfg80211_find_vendor_ie(WLAN_OUI_SAMSUNG, WLAN_OUI_TYPE_SAMSUNG_KEO,
ndev_vif->sta.assoc_req_add_info_elem,
ndev_vif->sta.assoc_req_add_info_elem_len);
if (keo_ie_pos) {
ie_buf_len = ftie->ie_len +
ndev_vif->sta.assoc_req_add_info_elem_len -
(keo_ie_pos - ndev_vif->sta.assoc_req_add_info_elem);
ie_buf = kmalloc(ie_buf_len, GFP_KERNEL);
if (!ie_buf) {
SLSI_NET_ERR(dev, "kmalloc failed\n");
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -ENOMEM;
}
ie_len = ftie->ie_len;
if (ie_buf_len < ie_len) {
SLSI_NET_ERR(dev, "ft_ie buffer overflow!!\n");
kfree(ie_buf);
ie_buf = NULL;
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -EINVAL;
}
memcpy(ie_buf, ftie->ie, ie_len);
if ((ie_buf_len - ie_len) >= ((int)keo_ie_pos[1] + 2)) {
memcpy(&ie_buf[ie_len], keo_ie_pos, ((int)keo_ie_pos[1] + 2));
ie_len += (keo_ie_pos[1] + 2);
keo_ie_pos += (keo_ie_pos[1] + 2);
} else {
SLSI_NET_ERR(dev, "ie_buf buffer overflow!!\n");
kfree(ie_buf);
ie_buf = NULL;
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -EINVAL;
}
while ((ndev_vif->sta.assoc_req_add_info_elem_len -
(keo_ie_pos - ndev_vif->sta.assoc_req_add_info_elem)) > 2) {
keo_ie_pos = cfg80211_find_vendor_ie(WLAN_OUI_SAMSUNG, WLAN_OUI_TYPE_SAMSUNG_KEO,
keo_ie_pos,
ndev_vif->sta.assoc_req_add_info_elem_len -
(keo_ie_pos - ndev_vif->sta.assoc_req_add_info_elem));
if (!keo_ie_pos)
break;
if ((ie_buf_len - ie_len) >= ((int)keo_ie_pos[1] + 2)) {
memcpy(&ie_buf[ie_len], keo_ie_pos, (keo_ie_pos[1] + 2));
ie_len += (keo_ie_pos[1] + 2);
keo_ie_pos += (keo_ie_pos[1] + 2);
} else {
SLSI_NET_ERR(dev, "ie_buf buffer overflow!\n");
kfree(ie_buf);
ie_buf = NULL;
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return -EINVAL;
}
}
r = slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_ASSOCIATION_REQUEST, ie_buf, ie_len);
} else {
r = slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_ASSOCIATION_REQUEST, ftie->ie, ftie->ie_len);
}
kfree(ie_buf);
ie_buf = NULL;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
#ifdef CONFIG_SCSC_WLAN_MAC_ACL_PER_MAC
int slsi_set_mac_acl_per_mac(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_acl_data *params)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0;
int i = 0;
int malloc_len = 0;
struct cfg80211_acl_data *saved_acl_data = NULL;
int last_index = 0;
struct mac_address zero_addr = {0};
bool found_flag = false;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_SET_ACL.request\n");
return -EOPNOTSUPP;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (ndev_vif->vif_type != FAPI_VIFTYPE_AP) {
SLSI_NET_ERR(dev, "Invalid vif type: %d\n", ndev_vif->vif_type);
r = -EINVAL;
goto exit;
}
SLSI_NET_DBG2(dev, SLSI_CFG80211, "ACL:: Policy: %d Number of stations: %d\n", params->acl_policy, params->n_acl_entries);
if (params->n_acl_entries != 1) {
SLSI_NET_ERR(dev, "n_acl_entries != 1, No action taken\n");
goto exit;
}
saved_acl_data = ndev_vif->ap.acl_data_blacklist;
if (!saved_acl_data) {
if (params->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED) {
SLSI_NET_ERR(dev, "Deletion requested on empty list\n");
r = -EINVAL;
goto exit;
}
malloc_len = sizeof(struct cfg80211_acl_data) + sizeof(struct mac_address) * SLSI_ACL_MAX_BSSID_COUNT;
saved_acl_data = kmalloc(malloc_len, GFP_KERNEL);
if (!saved_acl_data) {
SLSI_ERR(sdev, "Memory Allocation failure for ACL List");
r = -ENOMEM;
goto exit;
}
memset(saved_acl_data, 0, malloc_len);
ndev_vif->ap.acl_data_blacklist = saved_acl_data;
}
last_index = saved_acl_data->n_acl_entries;
if (params->acl_policy == NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) { /* Add mac address on the blacklist */
if (SLSI_ETHER_EQUAL(params->mac_addrs[0].addr, zero_addr.addr)) {
SLSI_NET_ERR(dev, "Addition of addr 00:00:00:00:00:00 in blacklist\n");
r = -EINVAL;
goto exit;
}
for (i = 0 ; i < last_index; i++) { /*Check for duplicate entries*/
if (SLSI_ETHER_EQUAL(saved_acl_data->mac_addrs[i].addr, params->mac_addrs[0].addr)) {
SLSI_NET_INFO(dev, "Mac addr already present in blacklist\n");
r = 0;
goto exit;
}
}
for (i = 0 ; i < last_index + 1 && i < SLSI_ACL_MAX_BSSID_COUNT; i++) {
if (SLSI_ETHER_EQUAL(saved_acl_data->mac_addrs[i].addr, zero_addr.addr)) {
SLSI_ETHER_COPY(saved_acl_data->mac_addrs[i].addr, params->mac_addrs[0].addr);
if (i == last_index)
last_index = i + 1;
break;
}
}
if (i == SLSI_ACL_MAX_BSSID_COUNT) {
SLSI_NET_ERR(dev, "Blacklist is full\n");
r = -EINVAL;
goto exit;
}
} else if (params->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED) { /* Delete mac address from the blacklist */
found_flag = false;
for (i = 0 ; i < last_index; i++) {
if (SLSI_ETHER_EQUAL(saved_acl_data->mac_addrs[i].addr, params->mac_addrs[0].addr)) {
SLSI_ETHER_COPY(saved_acl_data->mac_addrs[i].addr, zero_addr.addr);
found_flag = true;
if (i == last_index - 1) {
while (i >= 0 && SLSI_ETHER_EQUAL(saved_acl_data->mac_addrs[i].addr, zero_addr.addr))
i--;
last_index = i + 1;
}
break;
}
}
if (!found_flag) {
r = -EINVAL;
SLSI_NET_ERR(dev, "Deletion requested for addr not present in blacklist");
goto exit;
}
}
saved_acl_data->n_acl_entries = last_index;
r = slsi_mlme_set_acl(sdev, dev, ndev_vif->ifnum, saved_acl_data->acl_policy, saved_acl_data->n_acl_entries, saved_acl_data->mac_addrs);
exit:
if (!last_index) {
ndev_vif->ap.acl_data_blacklist = NULL;
kfree(saved_acl_data);
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
#endif
int slsi_set_mac_acl(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_acl_data *params)
{
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_SET_ACL.request\n");
return -EOPNOTSUPP;
}
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (FAPI_VIFTYPE_AP != ndev_vif->vif_type) {
SLSI_NET_ERR(dev, "Invalid vif type: %d\n", ndev_vif->vif_type);
r = -EINVAL;
goto exit;
}
SLSI_NET_DBG2(dev, SLSI_CFG80211, "ACL:: Policy: %d Number of stations: %d\n", params->acl_policy, params->n_acl_entries);
r = slsi_mlme_set_acl(sdev, dev, ndev_vif->ifnum, params->acl_policy, params->n_acl_entries, (struct mac_address *)params->mac_addrs);
if (r != 0)
SLSI_NET_ERR(dev, "mlme_set_acl_req returned with CFM failure\n");
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
static struct cfg80211_ops slsi_ops = {
.add_virtual_intf = slsi_add_virtual_intf,
.del_virtual_intf = slsi_del_virtual_intf,
.change_virtual_intf = slsi_change_virtual_intf,
.scan = slsi_scan,
.abort_scan = slsi_abort_scan,
.connect = slsi_connect,
.disconnect = slsi_disconnect,
.add_key = slsi_add_key,
.del_key = slsi_del_key,
.get_key = slsi_get_key,
.set_default_key = slsi_set_default_key,
.set_default_mgmt_key = slsi_config_default_mgmt_key,
.set_wiphy_params = slsi_set_wiphy_params,
.del_station = slsi_del_station,
.get_station = slsi_get_station,
.set_tx_power = slsi_set_tx_power,
.get_tx_power = slsi_get_tx_power,
.set_power_mgmt = slsi_set_power_mgmt,
.get_channel = slsi_get_channel,
.suspend = slsi_suspend,
.resume = slsi_resume,
.set_pmksa = slsi_set_pmksa,
.del_pmksa = slsi_del_pmksa,
.flush_pmksa = slsi_flush_pmksa,
.remain_on_channel = slsi_remain_on_channel,
.cancel_remain_on_channel = slsi_cancel_remain_on_channel,
.change_bss = slsi_change_bss,
.start_ap = slsi_start_ap,
.change_beacon = slsi_change_beacon,
.stop_ap = slsi_stop_ap,
.sched_scan_start = slsi_sched_scan_start,
.sched_scan_stop = slsi_sched_scan_stop,
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(5, 7, 0))
.mgmt_frame_register = slsi_mgmt_frame_register,
#endif
.mgmt_tx = slsi_mgmt_tx,
.mgmt_tx_cancel_wait = slsi_mgmt_tx_cancel_wait,
.set_txq_params = slsi_set_txq_params,
#ifdef CONFIG_SCSC_WLAN_SAE_CONFIG
.external_auth = slsi_synchronised_response,
#endif
#ifdef CONFIG_SCSC_WLAN_MAC_ACL_PER_MAC
.set_mac_acl = slsi_set_mac_acl_per_mac,
#else
.set_mac_acl = slsi_set_mac_acl,
#endif
.update_ft_ies = slsi_update_ft_ies,
.tdls_oper = slsi_tdls_oper,
.set_monitor_channel = slsi_set_monitor_channel,
.set_qos_map = slsi_set_qos_map,
.channel_switch = slsi_channel_switch
};
#define RATE_LEGACY(_rate, _hw_value, _flags) { \
.bitrate = (_rate), \
.hw_value = (_hw_value), \
.flags = (_flags), \
}
#define CHAN2G(_freq, _idx) { \
.band = NL80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 17, \
}
#define CHAN5G(_freq, _idx) { \
.band = NL80211_BAND_5GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 17, \
}
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
#define CHAN6G(_freq, _idx) { \
.band = NL80211_BAND_6GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 17, \
}
#endif
static struct ieee80211_channel slsi_2ghz_channels[] = {
CHAN2G(2412, 1),
CHAN2G(2417, 2),
CHAN2G(2422, 3),
CHAN2G(2427, 4),
CHAN2G(2432, 5),
CHAN2G(2437, 6),
CHAN2G(2442, 7),
CHAN2G(2447, 8),
CHAN2G(2452, 9),
CHAN2G(2457, 10),
CHAN2G(2462, 11),
CHAN2G(2467, 12),
CHAN2G(2472, 13),
CHAN2G(2484, 14),
};
static struct ieee80211_rate slsi_11g_rates[] = {
RATE_LEGACY(10, 1, 0),
RATE_LEGACY(20, 2, IEEE80211_RATE_SHORT_PREAMBLE),
RATE_LEGACY(55, 3, IEEE80211_RATE_SHORT_PREAMBLE),
RATE_LEGACY(110, 6, IEEE80211_RATE_SHORT_PREAMBLE),
RATE_LEGACY(60, 4, 0),
RATE_LEGACY(90, 5, 0),
RATE_LEGACY(120, 7, 0),
RATE_LEGACY(180, 8, 0),
RATE_LEGACY(240, 9, 0),
RATE_LEGACY(360, 10, 0),
RATE_LEGACY(480, 11, 0),
RATE_LEGACY(540, 12, 0),
};
static struct ieee80211_channel slsi_5ghz_channels[] = {
/* _We_ call this UNII 1 */
CHAN5G(5180, 36),
CHAN5G(5200, 40),
CHAN5G(5220, 44),
CHAN5G(5240, 48),
/* UNII 2 */
CHAN5G(5260, 52),
CHAN5G(5280, 56),
CHAN5G(5300, 60),
CHAN5G(5320, 64),
/* "Middle band" */
CHAN5G(5500, 100),
CHAN5G(5520, 104),
CHAN5G(5540, 108),
CHAN5G(5560, 112),
CHAN5G(5580, 116),
CHAN5G(5600, 120),
CHAN5G(5620, 124),
CHAN5G(5640, 128),
CHAN5G(5660, 132),
CHAN5G(5680, 136),
CHAN5G(5700, 140),
CHAN5G(5720, 144),
/* UNII 3 */
CHAN5G(5745, 149),
CHAN5G(5765, 153),
CHAN5G(5785, 157),
CHAN5G(5805, 161),
CHAN5G(5825, 165),
};
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
static struct ieee80211_channel slsi_6ghz_channels[] = {
/* U-NII-5 */
CHAN6G(5955, 1),
CHAN6G(5975, 5),
CHAN6G(5995, 9),
CHAN6G(6015, 13),
CHAN6G(6035, 17),
CHAN6G(6055, 21),
CHAN6G(6075, 25),
CHAN6G(6095, 29),
CHAN6G(6115, 33),
CHAN6G(6135, 37),
CHAN6G(6155, 41),
CHAN6G(6175, 45),
CHAN6G(6195, 49),
CHAN6G(6215, 53),
CHAN6G(6235, 57),
CHAN6G(6255, 61),
CHAN6G(6275, 65),
CHAN6G(6295, 69),
CHAN6G(6315, 73),
CHAN6G(6335, 77),
CHAN6G(6355, 81),
CHAN6G(6375, 85),
CHAN6G(6395, 89),
CHAN6G(6415, 93),
/* U-NII-6 */
CHAN6G(6435, 97),
CHAN6G(6455, 101),
CHAN6G(6475, 105),
CHAN6G(6495, 109),
CHAN6G(6515, 113),
/* U-NII-7 */
CHAN6G(6535, 117),
CHAN6G(6555, 121),
CHAN6G(6575, 125),
CHAN6G(6595, 129),
CHAN6G(6615, 133),
CHAN6G(6635, 137),
CHAN6G(6655, 141),
CHAN6G(6675, 145),
CHAN6G(6695, 149),
CHAN6G(6715, 153),
CHAN6G(6735, 157),
CHAN6G(6755, 161),
CHAN6G(6775, 165),
CHAN6G(6795, 169),
CHAN6G(6815, 173),
CHAN6G(6835, 177),
CHAN6G(6855, 181),
/* U-NII-8 */
CHAN6G(6875, 185),
CHAN6G(6895, 189),
CHAN6G(6915, 193),
CHAN6G(6935, 197),
CHAN6G(6955, 201),
CHAN6G(6975, 205),
CHAN6G(6995, 209),
CHAN6G(7015, 213),
CHAN6G(7035, 217),
CHAN6G(7055, 221),
CHAN6G(7075, 225),
CHAN6G(7095, 229),
CHAN6G(7115, 233),
};
#endif
/* note fw_rate_idx_to_host_11a_idx[] below must change if this table changes */
static struct ieee80211_rate wifi_11a_rates[] = {
RATE_LEGACY(60, 4, 0),
RATE_LEGACY(90, 5, 0),
RATE_LEGACY(120, 7, 0),
RATE_LEGACY(180, 8, 0),
RATE_LEGACY(240, 9, 0),
RATE_LEGACY(360, 10, 0),
RATE_LEGACY(480, 11, 0),
RATE_LEGACY(540, 12, 0),
};
static struct ieee80211_sta_ht_cap slsi_ht_cap = {
.ht_supported = true,
.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_RX_STBC |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_4,
.mcs = {
.rx_mask = { 0xff, 0, },
.rx_highest = cpu_to_le16(0),
.tx_params = 0,
},
};
struct ieee80211_sta_vht_cap slsi_vht_cap = {
.vht_supported = true,
.cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 |
IEEE80211_VHT_CAP_SHORT_GI_80 |
IEEE80211_VHT_CAP_RXSTBC_1 |
IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
(5 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT),
.vht_mcs = {
.rx_mcs_map = cpu_to_le16(0xfffe),
.rx_highest = cpu_to_le16(0),
.tx_mcs_map = cpu_to_le16(0xfffe),
.tx_highest = cpu_to_le16(0),
},
};
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
static struct ieee80211_sband_iftype_data slsi_sta_he_cap[] = {
{
.types_mask = BIT(NL80211_IFTYPE_STATION),
.he_cap = {
.has_he = true,
.he_cap_elem = {
.mac_cap_info[0] =
IEEE80211_HE_MAC_CAP0_HTC_HE,
.mac_cap_info[1] =
IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US,
.mac_cap_info[3] =
IEEE80211_HE_MAC_CAP3_OMI_CONTROL,
.phy_cap_info[0] =
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G,
.phy_cap_info[1] =
IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US,
.phy_cap_info[2] =
IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US,
.phy_cap_info[6] =
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
.phy_cap_info[7] =
IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR |
IEEE80211_HE_PHY_CAP7_MAX_NC_1,
.phy_cap_info[9] =
IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US |
IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM |
IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU |
IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU,
},
.he_mcs_nss_supp = {
.rx_mcs_80 = cpu_to_le16(0xfffa),
.tx_mcs_80 = cpu_to_le16(0xfffa),
},
.ppe_thres = { 0x79, 0x1C, 0xC7, 0x71, 0x1C, 0xC7, 0x71 },
},
.he_6ghz_capa = {
.capa = 0,
},
},
};
#endif
#ifdef CONFIG_SCSC_WLAN_HE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
static const struct ieee80211_sband_iftype_data slsi_softap_he_cap[] = {
{
.types_mask = BIT(NL80211_IFTYPE_AP),
.he_cap = {
.has_he = true,
.he_cap_elem = {
.mac_cap_info[0] =
IEEE80211_HE_MAC_CAP0_HTC_HE,
.mac_cap_info[1] =
IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US,
.mac_cap_info[3] =
IEEE80211_HE_MAC_CAP3_OMI_CONTROL,
.phy_cap_info[0] =
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G,
.phy_cap_info[1] =
IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US,
.phy_cap_info[2] =
IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US,
.phy_cap_info[6] =
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
.phy_cap_info[7] =
IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR |
IEEE80211_HE_PHY_CAP7_MAX_NC_1,
.phy_cap_info[9] =
IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US |
IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM |
IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU |
IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU,
},
.he_mcs_nss_supp = {
.rx_mcs_80 = cpu_to_le16(0xfffa),
.tx_mcs_80 = cpu_to_le16(0xfffa),
},
.ppe_thres = { 0x79, 0x1C, 0xC7, 0x71, 0x1C, 0xC7, 0x71 },
},
},
};
#endif
#endif
struct ieee80211_supported_band slsi_band_2ghz = {
.channels = slsi_2ghz_channels,
.band = NL80211_BAND_2GHZ,
.n_channels = ARRAY_SIZE(slsi_2ghz_channels),
.bitrates = slsi_11g_rates,
.n_bitrates = ARRAY_SIZE(slsi_11g_rates),
};
struct ieee80211_supported_band slsi_band_5ghz = {
.channels = slsi_5ghz_channels,
.band = NL80211_BAND_5GHZ,
.n_channels = ARRAY_SIZE(slsi_5ghz_channels),
.bitrates = wifi_11a_rates,
.n_bitrates = ARRAY_SIZE(wifi_11a_rates),
};
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
struct ieee80211_supported_band slsi_band_6ghz = {
.channels = slsi_6ghz_channels,
.band = NL80211_BAND_6GHZ,
.n_channels = ARRAY_SIZE(slsi_6ghz_channels),
.bitrates = wifi_11a_rates,
.n_bitrates = ARRAY_SIZE(wifi_11a_rates),
.ht_cap = {
.ht_supported = false,
},
.vht_cap = {
.vht_supported = false,
},
};
#endif
static const u32 slsi_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
WLAN_CIPHER_SUITE_AES_CMAC,
WLAN_CIPHER_SUITE_SMS4,
WLAN_CIPHER_SUITE_PMK,
WLAN_CIPHER_SUITE_GCMP,
WLAN_CIPHER_SUITE_GCMP_256,
WLAN_CIPHER_SUITE_CCMP_256,
WLAN_CIPHER_SUITE_BIP_GMAC_128,
WLAN_CIPHER_SUITE_BIP_GMAC_256
};
static const struct ieee80211_txrx_stypes
ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_AP] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4)
},
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4)
},
[NL80211_IFTYPE_P2P_GO] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4)
},
};
/* Interface combinations supported by driver */
static struct ieee80211_iface_limit iface_limits[] = {
#ifdef CONFIG_SCSC_WLAN_STA_ONLY
/* Basic STA-only */
{
.max = CONFIG_SCSC_WLAN_MAX_INTERFACES,
.types = BIT(NL80211_IFTYPE_STATION),
},
#else
/* AP mode: # AP <= 1 on channel = 1 */
{
.max = 1,
.types = BIT(NL80211_IFTYPE_AP),
},
/* STA and P2P mode: #STA <= 1, #{P2P-client,P2P-GO} <= 1 on two channels */
/* For P2P, the device mode and group mode is first started as STATION and then changed.
* Similarly it is changed to STATION on group removal. Hence set maximum interfaces for STATION.
*/
{
.max = CONFIG_SCSC_WLAN_MAX_INTERFACES,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO),
},
/* ADHOC mode: #ADHOC <= 1 on channel = 1 */
{
.max = 1,
.types = BIT(NL80211_IFTYPE_ADHOC),
},
#endif
};
static struct ieee80211_regdomain slsi_regdomain = {
.reg_rules = {
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
REG_RULE(0, 0, 0, 0, 0, 0),
}
};
static struct ieee80211_iface_combination iface_comb[] = {
{
.limits = iface_limits,
.n_limits = ARRAY_SIZE(iface_limits),
.num_different_channels = 2,
.max_interfaces = CONFIG_SCSC_WLAN_MAX_INTERFACES,
},
};
#ifdef CONFIG_PM
static struct cfg80211_wowlan slsi_wowlan_config = {
.any = true,
};
#endif
struct slsi_dev *slsi_cfg80211_new(struct device *dev)
{
struct wiphy *wiphy;
struct slsi_dev *sdev = NULL;
SLSI_DBG1_NODEV(SLSI_CFG80211, "wiphy_new()\n");
wiphy = wiphy_new(&slsi_ops, sizeof(struct slsi_dev));
if (!wiphy) {
SLSI_ERR_NODEV("wiphy_new() failed");
return NULL;
}
sdev = (struct slsi_dev *)wiphy->priv;
sdev->wiphy = wiphy;
set_wiphy_dev(wiphy, dev);
/* Allow changing of the netns, if NOT set then no changes are allowed */
wiphy->flags |= WIPHY_FLAG_NETNS_OK;
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
#if !(defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION < 11)
wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
#endif
wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
wiphy->max_num_csa_counters = 2;
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
WIPHY_FLAG_AP_UAPSD;
wiphy->max_acl_mac_addrs = SLSI_AP_PEER_CONNECTIONS_MAX;
wiphy->privid = sdev;
wiphy->interface_modes =
#ifdef CONFIG_SCSC_WLAN_STA_ONLY
BIT(NL80211_IFTYPE_STATION);
#else
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MONITOR) |
BIT(NL80211_IFTYPE_ADHOC);
#endif
slsi_band_2ghz.ht_cap = slsi_ht_cap;
slsi_band_5ghz.ht_cap = slsi_ht_cap;
slsi_band_5ghz.vht_cap = slsi_vht_cap;
#ifdef CONFIG_SCSC_WLAN_HE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
slsi_band_2ghz.iftype_data = slsi_softap_he_cap;
slsi_band_2ghz.n_iftype_data = ARRAY_SIZE(slsi_softap_he_cap);
slsi_band_5ghz.iftype_data = slsi_softap_he_cap;
slsi_band_5ghz.n_iftype_data = ARRAY_SIZE(slsi_softap_he_cap);
#endif
#endif
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->bands[NL80211_BAND_2GHZ] = &slsi_band_2ghz;
wiphy->bands[NL80211_BAND_5GHZ] = &slsi_band_5ghz;
memset(&sdev->device_config, 0, sizeof(struct slsi_dev_config));
sdev->device_config.band_5G = &slsi_band_5ghz;
sdev->device_config.band_2G = &slsi_band_2ghz;
sdev->device_config.domain_info.regdomain = &slsi_regdomain;
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
wiphy->max_remain_on_channel_duration = 5000; /* 5000 msec */
wiphy->cipher_suites = slsi_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(slsi_cipher_suites);
wiphy->extended_capabilities = slsi_extended_cap;
wiphy->extended_capabilities_mask = slsi_extended_cap_mask;
wiphy->extended_capabilities_len = ARRAY_SIZE(slsi_extended_cap);
wiphy->mgmt_stypes = ieee80211_default_mgmt_stypes;
/* Driver interface combinations */
wiphy->n_iface_combinations = ARRAY_SIZE(iface_comb);
wiphy->iface_combinations = iface_comb;
/* Basic scan parameters */
wiphy->max_scan_ssids = 10;
wiphy->max_scan_ie_len = 2048;
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 11, 0))
/* Scheduled scanning support */
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0))
/* Parameters for Scheduled Scanning Support */
wiphy->max_sched_scan_reqs = 1;
/* Setting the default scheduled scan plan to 1 */
wiphy->max_sched_scan_plans = 1;
#ifdef CONFIG_SCSC_WLAN_EXPONENTIAL_SCHED_SCAN
wiphy->max_sched_scan_plans = 2;
wiphy->max_sched_scan_plan_interval = 60;
wiphy->max_sched_scan_plan_iterations = 3;
#endif
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI);
/* Randomize TA of Public Action frames. */
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA);
#endif
#if !defined(__x86_64__)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
/* Beacon Protection */
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
#if defined(CONFIG_SCSC_WLAN_OCV_SUPPORT) || (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
/* Operating Channel Validation (OCV) */
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
#endif
#endif
#endif
/* Match the maximum number of SSIDs that could be requested from wpa_supplicant */
wiphy->max_sched_scan_ssids = 16;
/* To get a list of SSIDs rather than just the wildcard SSID need to support match sets */
wiphy->max_match_sets = 16;
wiphy->max_sched_scan_ie_len = 2048;
#ifdef CONFIG_PM
wiphy->wowlan = NULL;
wiphy->wowlan_config = &slsi_wowlan_config;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
wiphy->regulatory_flags |= (REGULATORY_STRICT_REG |
REGULATORY_CUSTOM_REG |
REGULATORY_DISABLE_BEACON_HINTS |
REGULATORY_COUNTRY_IE_IGNORE);
#else
wiphy->regulatory_flags |= (REGULATORY_STRICT_REG |
REGULATORY_CUSTOM_REG |
REGULATORY_DISABLE_BEACON_HINTS);
#endif
#ifndef CONFIG_SCSC_WLAN_STA_ONLY
/* P2P flags */
wiphy->flags |= WIPHY_FLAG_OFFCHAN_TX;
/* Enable Probe response offloading w.r.t WPS and P2P */
wiphy->probe_resp_offload |=
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
/* TDLS support */
wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
#endif
/* Mac Randomization */
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
#endif
#ifdef CONFIG_SCSC_WLAN_SAE_CONFIG
wiphy->features |= NL80211_FEATURE_SAE;
#endif
#ifdef CONFIG_SCSC_WLAN_HE
/* 11ax related parameters */
wiphy->support_mbssid = 1;
wiphy->support_only_he_mbssid = 1;
#endif
return sdev;
}
int slsi_cfg80211_register(struct slsi_dev *sdev)
{
SLSI_DBG1(sdev, SLSI_CFG80211, "wiphy_register()\n");
return wiphy_register(sdev->wiphy);
}
void slsi_cfg80211_unregister(struct slsi_dev *sdev)
{
#ifdef CONFIG_PM
sdev->wiphy->wowlan = NULL;
sdev->wiphy->wowlan_config = NULL;
#endif
SLSI_DBG1(sdev, SLSI_CFG80211, "wiphy_unregister()\n");
wiphy_unregister(sdev->wiphy);
}
void slsi_cfg80211_free(struct slsi_dev *sdev)
{
SLSI_DBG1(sdev, SLSI_CFG80211, "wiphy_free()\n");
wiphy_free(sdev->wiphy);
}
static bool slsi_update_wiphy_6ghz(struct slsi_dev *sdev)
{
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
bool split_scan_enabled = false;
u16 he_6ghz_capa = 0;
u32 temp, i;
if (!sdev->band_6g_supported) {
sdev->wiphy->bands[NL80211_BAND_6GHZ] = NULL;
sdev->device_config.band_6G = NULL;
sdev->wiphy->flags &= ~WIPHY_FLAG_SPLIT_SCAN_6GHZ;
return false;
}
he_6ghz_capa |= u16_encode_bits(slsi_ht_cap.ampdu_density, IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START);
temp = u32_get_bits(slsi_vht_cap.cap, IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
he_6ghz_capa |= u16_encode_bits(temp, IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
temp = u32_get_bits(slsi_vht_cap.cap, IEEE80211_VHT_CAP_MAX_MPDU_MASK);
he_6ghz_capa |= u16_encode_bits(temp, IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN);
for (i = 0; i < ARRAY_SIZE(slsi_sta_he_cap); i++)
slsi_sta_he_cap[i].he_6ghz_capa.capa = cpu_to_le16(he_6ghz_capa);
slsi_band_6ghz.iftype_data = slsi_sta_he_cap;
slsi_band_6ghz.n_iftype_data = ARRAY_SIZE(slsi_sta_he_cap);
sdev->wiphy->bands[NL80211_BAND_6GHZ] = &slsi_band_6ghz;
sdev->device_config.band_6G = &slsi_band_6ghz;
sdev->device_config.supported_band = SLSI_FREQ_BAND_AUTO;
sdev->device_config.supported_roam_band |= FAPI_BAND_6GHZ;
split_scan_enabled = slsi_dev_6ghz_split_scan_enabled();
if (split_scan_enabled)
sdev->wiphy->flags |= WIPHY_FLAG_SPLIT_SCAN_6GHZ;
SLSI_INFO(sdev, "6GHz split scan enable : %d (he_6ghz_capa : 0x%x)\n", split_scan_enabled, he_6ghz_capa);
return true;
#else
return false;
#endif
}
void slsi_cfg80211_update_wiphy(struct slsi_dev *sdev)
{
bool support_6ghz = false;
/* Band 2G probably be disabled by slsi_band_cfg_update() while factory test or NCHO.
* So, we need to make sure that Band 2.4G enabled when initialized. */
sdev->wiphy->bands[NL80211_BAND_2GHZ] = &slsi_band_2ghz;
sdev->device_config.band_2G = &slsi_band_2ghz;
sdev->device_config.supported_roam_band = FAPI_BAND_2_4GHZ;
/* update supported Bands */
if (sdev->band_5g_supported) {
sdev->wiphy->bands[NL80211_BAND_5GHZ] = &slsi_band_5ghz;
sdev->device_config.band_5G = &slsi_band_5ghz;
sdev->device_config.supported_band = SLSI_FREQ_BAND_AUTO;
sdev->device_config.supported_roam_band |= FAPI_BAND_5GHZ;
} else {
sdev->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
sdev->device_config.band_5G = NULL;
sdev->device_config.supported_band = SLSI_FREQ_BAND_2GHZ;
}
/* update HT features */
if (sdev->fw_ht_enabled) {
slsi_ht_cap.ht_supported = true;
slsi_ht_cap.cap = le16_to_cpu(*(u16 *)sdev->fw_ht_cap);
slsi_ht_cap.ampdu_density = (sdev->fw_ht_cap[2] & IEEE80211_HT_AMPDU_PARM_DENSITY) >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
slsi_ht_cap.ampdu_factor = sdev->fw_ht_cap[2] & IEEE80211_HT_AMPDU_PARM_FACTOR;
} else {
slsi_ht_cap.ht_supported = false;
}
slsi_band_2ghz.ht_cap = slsi_ht_cap;
slsi_band_5ghz.ht_cap = slsi_ht_cap;
/* update VHT features */
if (sdev->fw_vht_enabled) {
slsi_vht_cap.vht_supported = true;
slsi_vht_cap.cap = le32_to_cpu(*(u32 *)sdev->fw_vht_cap);
} else {
slsi_vht_cap.vht_supported = false;
}
slsi_band_5ghz.vht_cap = slsi_vht_cap;
support_6ghz = slsi_update_wiphy_6ghz(sdev);
SLSI_INFO(sdev, "BANDS SUPPORTED -> 2.4:'%c' 5:'%c' 6:'%c'\n",
sdev->wiphy->bands[NL80211_BAND_2GHZ] ? 'Y' : 'N',
sdev->wiphy->bands[NL80211_BAND_5GHZ] ? 'Y' : 'N',
support_6ghz ? 'Y' : 'N');
SLSI_INFO(sdev, "HT/VHT SUPPORTED -> HT:'%c' VHT:'%c'\n", sdev->fw_ht_enabled ? 'Y' : 'N',
sdev->fw_vht_enabled ? 'Y' : 'N');
SLSI_INFO(sdev, "HT -> cap:0x%04x ampdu_density:%d ampdu_factor:%d\n", slsi_ht_cap.cap, slsi_ht_cap.ampdu_density, slsi_ht_cap.ampdu_factor);
SLSI_INFO(sdev, "VHT -> cap:0x%08x\n", slsi_vht_cap.cap);
}