mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
3485 lines
113 KiB
C
Executable file
3485 lines
113 KiB
C
Executable file
/***************************************************************************
|
|
*
|
|
* Copyright (c) 2014 - 2016 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 "oxygen_ioctl.h"
|
|
|
|
#define SLSI_FW_SCAN_DONE_TIMEOUT_MSEC (15 * 1000)
|
|
#define SLSI_MAX_CHAN_2G_BAND 14
|
|
|
|
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");
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
struct wireless_dev *slsi_add_virtual_intf(struct wiphy *wiphy,
|
|
const char *name,
|
|
enum nl80211_iftype type,
|
|
u32 *flags,
|
|
struct vif_params *params)
|
|
{
|
|
#else
|
|
struct net_device *slsi_add_virtual_intf(struct wiphy *wiphy,
|
|
char *name,
|
|
enum nl80211_iftype type,
|
|
u32 *flags,
|
|
struct vif_params *params)
|
|
{
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) */
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
struct net_device *dev = NULL;
|
|
struct netdev_vif *ndev_vif = NULL;
|
|
int err = -EINVAL;
|
|
int iface;
|
|
|
|
SLSI_UNUSED_PARAMETER(flags);
|
|
|
|
SLSI_DBG1(sdev, SLSI_CFG80211, "name:%s\n", name);
|
|
|
|
iface = slsi_netif_add(sdev, name);
|
|
if (iface < 0)
|
|
goto exit_with_error;
|
|
|
|
dev = slsi_get_netdev(sdev, iface);
|
|
if (dev == NULL)
|
|
goto exit_with_error;
|
|
|
|
ndev_vif = netdev_priv(dev);
|
|
|
|
err = slsi_netif_register_rtlnl_locked(sdev, dev);
|
|
if (err)
|
|
goto exit_with_error;
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
ndev_vif->iftype = type;
|
|
dev->ieee80211_ptr->iftype = type;
|
|
if (params)
|
|
dev->ieee80211_ptr->use_4addr = params->use_4addr;
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
return &ndev_vif->wdev;
|
|
#else
|
|
return dev;
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) */
|
|
|
|
exit_with_error:
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
int slsi_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
|
|
{
|
|
struct net_device *dev = wdev->netdev;
|
|
|
|
#else
|
|
int slsi_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev)
|
|
{
|
|
#endif
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
|
|
if (WARN_ON(!dev))
|
|
return -EINVAL;
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "\n");
|
|
|
|
slsi_stop_net_dev(sdev, dev);
|
|
slsi_netif_remove_rtlnl_locked(sdev, dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int slsi_change_virtual_intf(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
enum nl80211_iftype type, u32 *flags,
|
|
struct vif_params *params)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
int r = 0;
|
|
|
|
SLSI_UNUSED_PARAMETER(flags);
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "type:%u\n", type);
|
|
|
|
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:
|
|
ndev_vif->iftype = type;
|
|
dev->ieee80211_ptr->iftype = type;
|
|
if (params)
|
|
dev->ieee80211_ptr->use_4addr = params->use_4addr;
|
|
break;
|
|
default:
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Operation not supported\n");
|
|
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;
|
|
u8 oxygen_broad_cast_mac[ETH_ALEN] = { 0 };
|
|
|
|
#ifndef CONFIG_SCSC_WLAN_OXYGEN_ENABLE
|
|
if (WARN_ON(pairwise && !mac_addr))
|
|
return -EINVAL;
|
|
#else
|
|
/* mac_addr is NULL and pairwise is 1 in case of FAPI_VIFTYPE_ADHOC. */
|
|
if (WARN_ON(ndev_vif->vif_type != FAPI_VIFTYPE_ADHOC && pairwise && !mac_addr))
|
|
return -EINVAL;
|
|
#endif
|
|
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)\n", key_index, pairwise, mac_addr, params->cipher, params->key_len);
|
|
|
|
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_pmk(sdev, dev, params->key, params->key_len);
|
|
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 if (ndev_vif->vif_type == FAPI_VIFTYPE_ADHOC) {
|
|
mac_addr = oxygen_broad_cast_mac;
|
|
key_type = FAPI_KEYTYPE_OXYGEN;
|
|
} else {
|
|
r = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "(key_index:%d, pairwise:%d, address:%pM, cipher:0x%.8X, key_len:%d)\n", key_index, pairwise, mac_addr, params->cipher, params->key_len);
|
|
/*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_DEFAULT, bc_mac_addr, params);
|
|
if (r == FAPI_RESULTCODE_SUCCESS) {
|
|
/* 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) {
|
|
key_type = FAPI_KEYTYPE_IGTK;
|
|
}
|
|
|
|
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)
|
|
peer->pairwise_key_set = true;
|
|
}
|
|
|
|
if (peer) {
|
|
if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION) {
|
|
if (pairwise) {
|
|
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED);
|
|
if (params->cipher == WLAN_CIPHER_SUITE_SMS4) { /*WAPI */
|
|
slsi_mlme_connect_resp(sdev, dev);
|
|
slsi_set_packet_filters(sdev, dev);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_ENABLE
|
|
else if (ndev_vif->vif_type == FAPI_VIFTYPE_ADHOC && pairwise)
|
|
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED);
|
|
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* Firmware does not require a del key */
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "index: %d pairwise: %d %pM\n", key_index, pairwise, mac_addr);
|
|
return 0;
|
|
}
|
|
|
|
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)\n", key_index, pairwise);
|
|
|
|
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(¶ms, 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, ¶ms.seq_len);
|
|
|
|
if (!r) {
|
|
params.seq = key_seq;
|
|
callback(cookie, ¶ms);
|
|
}
|
|
}
|
|
#undef SLSI_MAX_KEY_SIZE
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
int slsi_scan(struct wiphy *wiphy,
|
|
struct cfg80211_scan_request *request)
|
|
{
|
|
struct net_device *dev = request->wdev->netdev;
|
|
|
|
#else
|
|
int slsi_scan(struct wiphy *wiphy, struct net_device *dev,
|
|
struct cfg80211_scan_request *request)
|
|
{
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) */
|
|
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
u16 scan_type = FAPI_SCANTYPE_FULL_SCAN;
|
|
int r;
|
|
u16 p2p_state = sdev->p2p_state;
|
|
u8 *scan_ie;
|
|
size_t scan_ie_len;
|
|
bool strip_wsc = false;
|
|
bool strip_p2p = false;
|
|
struct ieee80211_channel *channels[64];
|
|
int i, chan_count = 0;
|
|
|
|
if (WARN_ON(!request->wdev))
|
|
return -EINVAL;
|
|
if (WARN_ON(!dev))
|
|
return -EINVAL;
|
|
|
|
#ifdef CONFIG_SCSC_DEBUG_CODE_COMMENTED_OUT /* Commented out build issue during cherry pick of "SSB-11164: Kernel NULL pointer access" */
|
|
if (!slsi_is_wlan_service_active()) {
|
|
SLSI_NET_INFO(dev, "wlan service is not active\n");
|
|
return -ENODEV;
|
|
}
|
|
#endif
|
|
|
|
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\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "channels:%d, ssids:%d, ie_len:%d\n", request->n_channels, request->n_ssids, (int)request->ie_len);
|
|
|
|
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;
|
|
}
|
|
|
|
for (i = 0; i < request->n_channels; i++)
|
|
channels[i] = request->channels[i];
|
|
chan_count = request->n_channels;
|
|
|
|
if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif))
|
|
if (sdev->scan_init_24g) {
|
|
/* Start initial wlan0 scan
|
|
* -- consider all 2.4G channels
|
|
* -- consider only 1 5Ghz channel
|
|
* -- use reduced dwell_time
|
|
*/
|
|
bool first_5g_chan = true;
|
|
int j = 0;
|
|
|
|
for (i = 0; i < request->n_channels; i++) {
|
|
if (j > SLSI_MAX_CHAN_2G_BAND)
|
|
break;
|
|
if ((request->channels[i]->hw_value > SLSI_MAX_CHAN_2G_BAND) && (!first_5g_chan))
|
|
continue;
|
|
if (request->channels[i]->hw_value > SLSI_MAX_CHAN_2G_BAND)
|
|
first_5g_chan = false;
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "First 2G Scan - Consider chan %d for scan\n", request->channels[i]->hw_value);
|
|
channels[j] = request->channels[i];
|
|
j++;
|
|
}
|
|
chan_count = j;
|
|
sdev->scan_init_24g = false;
|
|
scan_type = FAPI_SCANTYPE_INITIAL_SCAN;
|
|
}
|
|
|
|
/* Update scan timing for P2P social channels scan.
|
|
* Checking for SSID and num_channel looks good enough, hence skip social channel check
|
|
*/
|
|
if (SLSI_IS_VIF_INDEX_P2P(ndev_vif) && (request->n_ssids == 1) &&
|
|
SLSI_IS_P2P_SSID(request->ssids[0].ssid, request->ssids[0].ssid_len)) {
|
|
/* 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) {
|
|
p2p_state = P2P_SCANNING;
|
|
scan_type = FAPI_SCANTYPE_P2P_SCAN_SOCIAL;
|
|
} else if (request->n_channels > SLSI_P2P_SOCIAL_CHAN_COUNT) {
|
|
scan_type = FAPI_SCANTYPE_P2P_SCAN_FULL;
|
|
}
|
|
}
|
|
|
|
if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif) && (request->ie)) {
|
|
const u8 *ie;
|
|
|
|
/* check HS2 related bits in extended capabilties (interworking, WNM,QoS Map, BSS transition) and set in MIB*/
|
|
r = slsi_mlme_set_hs2_ext_cap(sdev, dev, request->ie, request->ie_len);
|
|
if (r)
|
|
goto exit;
|
|
|
|
/* 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 &&
|
|
ie[SLSI_WPS_REQUEST_TYPE_POS] == SLSI_WPS_REQUEST_TYPE_ENROLEE_INFO_ONLY)
|
|
strip_wsc = true;
|
|
|
|
ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, request->ie, request->ie_len);
|
|
if (ie)
|
|
strip_p2p = true;
|
|
}
|
|
|
|
if (strip_wsc || strip_p2p) {
|
|
scan_ie = kmalloc(request->ie_len, GFP_KERNEL);
|
|
if (!scan_ie) {
|
|
SLSI_NET_INFO(dev, "Out of memory for scan IEs\n");
|
|
r = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
scan_ie_len = slsi_strip_wsc_p2p_ie(request->ie, request->ie_len, scan_ie, strip_wsc, strip_p2p);
|
|
} else {
|
|
scan_ie = (u8 *)request->ie;
|
|
scan_ie_len = request->ie_len;
|
|
}
|
|
|
|
slsi_skb_queue_purge(&ndev_vif->scan[SLSI_SCAN_HW_ID].scan_results);
|
|
r = slsi_mlme_add_scan(sdev,
|
|
dev,
|
|
scan_type,
|
|
FAPI_REPORTMODE_REAL_TIME,
|
|
request->n_ssids,
|
|
request->ssids,
|
|
chan_count,
|
|
channels,
|
|
NULL,
|
|
scan_ie,
|
|
scan_ie_len,
|
|
false);
|
|
|
|
if (strip_p2p || strip_wsc)
|
|
kfree(scan_ie);
|
|
|
|
if (r != 0) {
|
|
if (r > 0) {
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Nothing to be done\n");
|
|
cfg80211_scan_done(request, false);
|
|
r = 0;
|
|
} else {
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "add_scan error: %d\n", r);
|
|
r = -EIO;
|
|
}
|
|
} else {
|
|
ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req = request;
|
|
|
|
queue_delayed_work(sdev->device_wq, &ndev_vif->scan_timeout_work,
|
|
msecs_to_jiffies(SLSI_FW_SCAN_DONE_TIMEOUT_MSEC));
|
|
|
|
/* Update State only for scan in Device role */
|
|
if (SLSI_IS_VIF_INDEX_P2P(ndev_vif) && (!SLSI_IS_P2P_GROUP_STATE(sdev))) {
|
|
SLSI_P2P_STATE_CHANGE(sdev, p2p_state);
|
|
} else if (SLSI_IS_VIF_INDEX_P2P_GROUP(ndev_vif) && (request->ie)) {
|
|
/* Supplicant includes only WPS and P2P IE as of now. Hence a blind copy is done. */
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "Scan IEs present with length = %zu\n", request->ie_len);
|
|
sdev->gc_probe_req_ies = kmalloc(request->ie_len, GFP_KERNEL);
|
|
if (sdev->gc_probe_req_ies == NULL) /* Don't fail, continue as it would still work */
|
|
sdev->gc_probe_req_ie_len = 0;
|
|
else {
|
|
sdev->gc_probe_req_ie_len = request->ie_len;
|
|
memcpy(sdev->gc_probe_req_ies, request->ie, request->ie_len);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
u8 *scan_ie;
|
|
size_t scan_ie_len;
|
|
bool strip_wsc = false;
|
|
bool strip_p2p = false;
|
|
|
|
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_DBG3(dev, SLSI_CFG80211, "channels:%d, ssids:%d, ie_len:%d\n", request->n_channels, request->n_ssids, (int)request->ie_len);
|
|
|
|
if (ndev_vif->scan[SLSI_SCAN_HW_ID].sched_req) {
|
|
r = -EBUSY;
|
|
goto exit;
|
|
}
|
|
|
|
if (request->ie) {
|
|
const u8 *ie;
|
|
/* Supplicant adds wsc and p2p in Station scan at the end of scan request ie.
|
|
* Remove both wps and p2p IEs.
|
|
* Scheduled scan is not used for wsc, So no need to check for wsc request type
|
|
*/
|
|
|
|
ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPS, request->ie, request->ie_len);
|
|
if (ie)
|
|
strip_wsc = true;
|
|
|
|
ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, request->ie, request->ie_len);
|
|
if (ie)
|
|
strip_p2p = true;
|
|
}
|
|
|
|
if (strip_wsc || strip_p2p) {
|
|
scan_ie = kmalloc(request->ie_len, GFP_KERNEL);
|
|
if (!scan_ie) {
|
|
SLSI_NET_INFO(dev, "Out of memory for scan IEs\n");
|
|
r = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
scan_ie_len = slsi_strip_wsc_p2p_ie(request->ie, request->ie_len, scan_ie, strip_wsc, strip_p2p);
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "Stripped IEs(p2p:%d, wsc:%d)\n", strip_p2p, strip_wsc);
|
|
} else {
|
|
scan_ie = (u8 *)request->ie;
|
|
scan_ie_len = request->ie_len;
|
|
}
|
|
|
|
slsi_skb_queue_purge(&ndev_vif->scan[SLSI_SCAN_SCHED_ID].scan_results);
|
|
r = slsi_mlme_add_sched_scan(sdev, dev, request, scan_ie, scan_ie_len);
|
|
|
|
if (strip_p2p || strip_wsc)
|
|
kfree(scan_ie);
|
|
|
|
if (r != 0) {
|
|
if (r > 0) {
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Nothing to be done\n");
|
|
cfg80211_sched_scan_stopped(wiphy);
|
|
r = 0;
|
|
} else {
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "add_scan error: %d\n", r);
|
|
r = -EIO;
|
|
}
|
|
} else {
|
|
ndev_vif->scan[SLSI_SCAN_SCHED_ID].sched_req = request;
|
|
}
|
|
|
|
goto exit;
|
|
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
|
|
return r;
|
|
}
|
|
|
|
int slsi_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
int r = 0;
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
|
|
|
|
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));
|
|
|
|
ndev_vif->scan[SLSI_SCAN_SCHED_ID].sched_req = NULL;
|
|
|
|
goto exit;
|
|
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
|
|
return r;
|
|
}
|
|
|
|
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;
|
|
u16 capability = WLAN_CAPABILITY_ESS;
|
|
struct slsi_peer *peer;
|
|
u16 prev_vif_type;
|
|
u32 action_frame_bmap;
|
|
struct net_device *p2p_dev;
|
|
const u8 *bssid;
|
|
struct ieee80211_channel *channel;
|
|
|
|
if (slsi_is_test_mode_enabled()) {
|
|
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_CONNECT.request\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
if (sme->privacy)
|
|
capability |= WLAN_CAPABILITY_PRIVACY;
|
|
|
|
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);
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "%.*s, %pM\n", (int)sme->ssid_len, sme->ssid, sme->bssid);
|
|
|
|
if (SLSI_IS_HS2_UNSYNC_VIF(ndev_vif)) {
|
|
slsi_hs2_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;
|
|
}
|
|
|
|
if (WARN_ON(!sme->ssid))
|
|
goto exit_with_error;
|
|
|
|
if (WARN_ON(sme->ssid_len > IEEE80211_MAX_SSID_LEN))
|
|
goto exit_with_error;
|
|
|
|
if ((ndev_vif->vif_type == FAPI_VIFTYPE_STATION) && (ndev_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED)) {
|
|
/*reassociation*/
|
|
peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET);
|
|
if (WARN_ON(peer == NULL))
|
|
goto exit_with_error;
|
|
|
|
if (sme->bssid == NULL) {
|
|
SLSI_NET_ERR(dev, "Require bssid in reassoc but received null\n");
|
|
goto exit_with_error;
|
|
}
|
|
|
|
if (!memcmp(peer->address, sme->bssid, ETH_ALEN)) { /*same bssid*/
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Reassociate to %pM\n", peer->address);
|
|
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);
|
|
}
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (WARN_ON(ndev_vif->activated))
|
|
goto exit_with_error;
|
|
|
|
if (WARN_ON(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);
|
|
goto exit_with_error;
|
|
}
|
|
prev_vif_type = ndev_vif->vif_type;
|
|
|
|
prev_vif_type = ndev_vif->vif_type;
|
|
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;
|
|
#ifdef CONFIG_SCSC_WLAN_WES_NCHO
|
|
if (sdev->device_config.wes_mode)
|
|
action_frame_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;
|
|
break;
|
|
default:
|
|
SLSI_NET_ERR(dev, "Invalid Device Type: %d\n", ndev_vif->iftype);
|
|
goto exit_with_error;
|
|
}
|
|
|
|
/* Initial Roaming checks done - assign vif type */
|
|
ndev_vif->vif_type = FAPI_VIFTYPE_STATION;
|
|
|
|
/* Abort on-going scans (if any) */
|
|
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
|
|
if (ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Abort on-going scan\n");
|
|
(void)slsi_mlme_del_scan(sdev, dev, ndev_vif->ifnum << 8 | SLSI_SCAN_HW_ID);
|
|
slsi_skb_queue_purge(&ndev_vif->scan[SLSI_SCAN_HW_ID].scan_results);
|
|
cfg80211_scan_done(ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req, true);
|
|
ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req = NULL;
|
|
}
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
|
|
|
|
channel = sme->channel;
|
|
bssid = sme->bssid;
|
|
|
|
ndev_vif->sta.sta_bss = cfg80211_get_bss(wiphy,
|
|
sme->channel,
|
|
sme->bssid,
|
|
sme->ssid,
|
|
sme->ssid_len,
|
|
capability,
|
|
capability);
|
|
if (ndev_vif->sta.sta_bss == NULL) {
|
|
if (sme->bssid == NULL) {
|
|
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);
|
|
r = slsi_mlme_connect_scan(sdev, dev, 1, &ssid, sme->channel);
|
|
|
|
if (r) {
|
|
SLSI_NET_ERR(dev, "slsi_mlme_connect_scan failed\n");
|
|
goto exit;
|
|
}
|
|
ndev_vif->sta.sta_bss = cfg80211_get_bss(wiphy,
|
|
sme->channel,
|
|
sme->bssid,
|
|
sme->ssid,
|
|
sme->ssid_len,
|
|
capability,
|
|
capability);
|
|
if (!ndev_vif->sta.sta_bss) {
|
|
SLSI_NET_ERR(dev, "cfg80211_get_bss(%.*s, %pM) Not found\n", (int)sme->ssid_len, sme->ssid, sme->bssid);
|
|
/*Set previous status in case of failure */
|
|
ndev_vif->vif_type = prev_vif_type;
|
|
r = -ENOENT;
|
|
goto exit;
|
|
}
|
|
channel = ndev_vif->sta.sta_bss->channel;
|
|
bssid = ndev_vif->sta.sta_bss->bssid;
|
|
} else {
|
|
/* The cfg80211 timeout for bss is 3 seconds so the sta_bss can be NULL */
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "sta_bss is not available for %.*s, %pM\n", (int)sme->ssid_len, sme->ssid, sme->bssid);
|
|
}
|
|
} else {
|
|
channel = ndev_vif->sta.sta_bss->channel;
|
|
bssid = ndev_vif->sta.sta_bss->bssid;
|
|
}
|
|
|
|
ndev_vif->channel_type = NL80211_CHAN_NO_HT;
|
|
ndev_vif->chan = channel;
|
|
|
|
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_bmap) != 0) {
|
|
SLSI_NET_ERR(dev, "Action frame registration failed for bitmap value %d\n", action_frame_bmap);
|
|
goto exit_with_vif;
|
|
}
|
|
|
|
if (sdev->device_config.qos_info != 0) {
|
|
r = slsi_set_uapsd_qos_info(sdev, dev, sdev->device_config.qos_info);
|
|
if (r != 0) {
|
|
SLSI_NET_ERR(dev, "qosInfo MIB write failed: %d\n", r);
|
|
goto exit_with_vif;
|
|
}
|
|
}
|
|
|
|
/* If P2P CLI, 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->iftype == NL80211_IFTYPE_P2P_CLIENT) && (sdev->gc_probe_req_ies)) {
|
|
if (sme->crypto.wpa_versions == 2)
|
|
sdev->delete_gc_probe_req_ies = true; /* Stored P2P Probe Req can be deleted at vif deletion, after WPA2 association */
|
|
else
|
|
/* Retain stored P2P Probe Req at vif deletion until WPA2 connection to allow Probe request with P2P IE, after WSC */
|
|
sdev->delete_gc_probe_req_ies = false;
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "Setting Probe Req IEs for P2P CLI\n");
|
|
(void)slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_PROBE_REQUEST, sdev->gc_probe_req_ies, sdev->gc_probe_req_ie_len);
|
|
}
|
|
|
|
/* 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;
|
|
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;
|
|
|
|
goto exit;
|
|
|
|
exit_with_vif:
|
|
slsi_mlme_del_vif(sdev, dev);
|
|
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:
|
|
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;
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "reason: %d\n", reason_code);
|
|
|
|
/* 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 = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
peer = ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET];
|
|
|
|
switch (ndev_vif->vif_type) {
|
|
case FAPI_VIFTYPE_STATION:
|
|
{
|
|
/* 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);
|
|
|
|
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);
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "Completed disconnect\n");
|
|
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);
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "TBD\n");
|
|
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");
|
|
|
|
if ((changed & WIPHY_PARAM_FRAG_THRESHOLD) && (wiphy->frag_threshold != -1)) {
|
|
SLSI_DBG1(sdev, SLSI_CFG80211, "Frag Threshold = %d\n", wiphy->frag_threshold);
|
|
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)) {
|
|
SLSI_DBG1(sdev, SLSI_CFG80211, "RTS Threshold = %d\n", wiphy->rts_threshold);
|
|
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;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
int slsi_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
enum nl80211_tx_power_setting type, int mbm)
|
|
#else
|
|
int slsi_set_tx_power(struct wiphy *wiphy,
|
|
enum nl80211_tx_power_setting type, int mbm)
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) */
|
|
{
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
int r = 0;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
SLSI_UNUSED_PARAMETER(wdev);
|
|
SLSI_UNUSED_PARAMETER(type);
|
|
#endif
|
|
SLSI_UNUSED_PARAMETER(mbm);
|
|
SLSI_UNUSED_PARAMETER_NOT_DEBUG(sdev);
|
|
/* TODO_HARDMAC: What locking is needed? */
|
|
|
|
SLSI_DBG1(sdev, SLSI_CFG80211, "Operation not supported\n");
|
|
r = -EOPNOTSUPP;
|
|
|
|
goto exit;
|
|
|
|
exit:
|
|
return r;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
int slsi_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, int *dbm)
|
|
#else
|
|
int slsi_get_tx_power(struct wiphy *wiphy, int *dbm)
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) */
|
|
{
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
int r = 0;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
SLSI_UNUSED_PARAMETER(wdev);
|
|
#endif
|
|
SLSI_UNUSED_PARAMETER(dbm);
|
|
SLSI_UNUSED_PARAMETER_NOT_DEBUG(sdev);
|
|
/* TODO_HARDMAC: What locking is needed? */
|
|
|
|
SLSI_DBG1(sdev, SLSI_CFG80211, "Operation not supported\n");
|
|
r = -EOPNOTSUPP;
|
|
|
|
goto exit;
|
|
|
|
exit:
|
|
return r;
|
|
}
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_ENABLE
|
|
|
|
int slsi_join_ibss(struct wiphy *wiphy, struct net_device *dev,
|
|
struct cfg80211_ibss_params *params)
|
|
{
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
|
|
/* this address is changed only if DUT is P2P mode. by BY. */
|
|
u8 device_address[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
int r = 0;
|
|
const u8 *wpa_ie_pos = NULL;
|
|
size_t wpa_ie_len = 0;
|
|
/* alpha2 will be set to worldwide (0x30, 0x30, 0x00) */
|
|
/* 0x4B, 0x52, 0x20 is KR, 0x55, 0x53, 0x20 is US. */
|
|
char alpha2[SLSI_COUNTRY_CODE_LEN];
|
|
u16 fw_freq;
|
|
u8 *new_ies = NULL;
|
|
size_t new_ies_len;
|
|
const u8 *tmp_ies = NULL;
|
|
|
|
size_t offset = 0;
|
|
|
|
u8 ie_ibss_params[] = { WLAN_EID_IBSS_PARAMS,
|
|
0x02,
|
|
0x00, 0x00, };
|
|
u8 ie_erp_element[] = { WLAN_EID_ERP_INFO,
|
|
0x01,
|
|
0x00, };
|
|
u8 ie_ht_oper[] = { WLAN_EID_HT_OPERATION,
|
|
0x16,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
u8 ie_extended_capa[] = { WLAN_EID_EXT_CAPABILITY,
|
|
0x08,
|
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 };
|
|
u8 ie_wmm_parameter[] = { WLAN_EID_VENDOR_SPECIFIC,
|
|
0x18,
|
|
0x00, 0x50, 0xf2, 0x02, 0x01, 0x01, 0x00, 0x00,
|
|
0x03, 0xa4, 0x00, 0x00, 0x27, 0xa4, 0x00, 0x00,
|
|
0x42, 0x43, 0x5e, 0x00, 0x62, 0x32, 0x2f, 0x00, };
|
|
u8 ie_wmm_information[] = { WLAN_EID_VENDOR_SPECIFIC,
|
|
0x07, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x01, 0x00, };
|
|
SLSI_MUTEX_LOCK(sdev->start_stop_mutex);
|
|
if (sdev->device_state != SLSI_DEVICE_STATE_STARTED) {
|
|
SLSI_WARN(sdev, "device not started (device_state:%d)\n", sdev->device_state);
|
|
SLSI_MUTEX_UNLOCK(sdev->start_stop_mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Enter:%s\n", __func__);
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
ndev_vif->chandef = ¶ms->chandef;
|
|
ndev_vif->chan = ndev_vif->chandef->chan;
|
|
#else
|
|
ndev_vif->chan = params->channel;
|
|
#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 9 ))*/
|
|
|
|
if (!params->ssid_len || !params->ssid) {
|
|
SLSI_NET_ERR(dev, "SSID not provided\n");
|
|
goto exit_with_error;
|
|
}
|
|
|
|
if (WARN_ON(ndev_vif->chan == NULL))
|
|
goto exit_with_error;
|
|
|
|
if (WARN_ON(ndev_vif->activated))
|
|
goto exit_with_error;
|
|
|
|
if (WARN_ON(ndev_vif->iftype != NL80211_IFTYPE_ADHOC))
|
|
goto exit_with_error;
|
|
|
|
fw_freq = ndev_vif->chan->center_freq;
|
|
|
|
switch (ndev_vif->chan->band) {
|
|
case IEEE80211_BAND_2GHZ: {
|
|
u8 ie_supported_rates_2g[] = { WLAN_EID_SUPP_RATES,
|
|
0x08,
|
|
0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, };
|
|
u8 ie_extended_rates[] = { WLAN_EID_EXT_SUPP_RATES,
|
|
0x04,
|
|
0x30, 0x48, 0x60, 0x6c, };
|
|
u8 ie_ht_capa_2g[] = { WLAN_EID_HT_CAPABILITY,
|
|
0x1a,
|
|
0x3c, 0x09, 0x1f, 0xff, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, };
|
|
|
|
new_ies_len = (params->ssid_len + 2)
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_UT_EXCEPTION
|
|
+ 3 /* DS parameter */
|
|
#endif
|
|
+ sizeof(ie_supported_rates_2g)
|
|
+ sizeof(ie_extended_rates)
|
|
+ sizeof(ie_ibss_params)
|
|
+ sizeof(ie_erp_element)
|
|
+ sizeof(ie_ht_capa_2g)
|
|
+ sizeof(ie_ht_oper)
|
|
+ sizeof(ie_extended_capa)
|
|
+ sizeof(ie_extended_rates);
|
|
new_ies = kmalloc(new_ies_len, GFP_KERNEL);
|
|
if (!new_ies) {
|
|
r = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
/* Add SSID */
|
|
*(new_ies + offset) = WLAN_EID_SSID;
|
|
offset += 1;
|
|
*(new_ies + offset) = params->ssid_len;
|
|
offset += 1;
|
|
memcpy((new_ies + offset), params->ssid, params->ssid_len);
|
|
offset += params->ssid_len;
|
|
/* Add supported rates */
|
|
ie_ht_oper[2] = (fw_freq - 2407) / 5;
|
|
memcpy((new_ies + offset), ie_supported_rates_2g, sizeof(ie_supported_rates_2g) / sizeof(ie_supported_rates_2g[0]));
|
|
offset += 10;
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_UT_EXCEPTION
|
|
/* TODO: This is used only for UT. This will be removed for release */
|
|
/* add DS parameter */
|
|
*(new_ies + offset) = WLAN_EID_DS_PARAMS;
|
|
offset += 1;
|
|
*(new_ies + offset) = 0x01;
|
|
offset += 1;
|
|
*(new_ies + offset) = (fw_freq - 2407) / 5;
|
|
offset += 1;
|
|
#endif
|
|
/* add IBSS parameter*/
|
|
memcpy((new_ies + offset), ie_ibss_params, 4);
|
|
offset += 4;
|
|
/* add ERP information*/
|
|
memcpy((new_ies + offset), ie_erp_element, 3);
|
|
offset += 3;
|
|
/* add HT capability and operation*/
|
|
memcpy((new_ies + offset), ie_ht_capa_2g, 28);
|
|
offset += 28;
|
|
memcpy((new_ies + offset), ie_ht_oper, 24);
|
|
offset += 24;
|
|
/* add Extended capability */
|
|
memcpy((new_ies + offset), ie_extended_capa, 10);
|
|
offset += 10;
|
|
/* add extended supported rates: 2G only*/
|
|
memcpy((new_ies + offset), ie_extended_rates, 6);
|
|
offset += 6;
|
|
}
|
|
break;
|
|
|
|
case IEEE80211_BAND_5GHZ: {
|
|
u8 ie_supported_rates_5g[] = { WLAN_EID_SUPP_RATES,
|
|
0x08,
|
|
0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c, };
|
|
u8 ie_ht_capa_5g[] = { WLAN_EID_HT_CAPABILITY,
|
|
0x1a,
|
|
0x7e, 0x09, 0x1f, 0xff, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, };
|
|
u8 ie_vht_capa[] = { WLAN_EID_VHT_CAPABILITY,
|
|
0x0c,
|
|
0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, };
|
|
u8 ie_vht_oper[] = { WLAN_EID_VHT_OPERATION,
|
|
0x05,
|
|
0x01, 0x2a, 0x00, 0x00, 0x00, };
|
|
new_ies_len = (params->ssid_len + 2)
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_UT_EXCEPTION
|
|
+ 3 /* DS parameter */
|
|
#endif
|
|
+ sizeof(ie_supported_rates_5g)
|
|
+ sizeof(ie_ibss_params)
|
|
+ sizeof(ie_erp_element)
|
|
+ sizeof(ie_ht_capa_5g)
|
|
+ sizeof(ie_ht_oper)
|
|
+ sizeof(ie_vht_capa)
|
|
+ sizeof(ie_vht_oper);
|
|
|
|
new_ies = kmalloc(new_ies_len, GFP_KERNEL);
|
|
if (!new_ies) {
|
|
r = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
/* Add SSID */
|
|
*(new_ies + offset) = WLAN_EID_SSID;
|
|
offset += 1;
|
|
*(new_ies + offset) = params->ssid_len;
|
|
offset += 1;
|
|
memcpy((new_ies + offset), params->ssid, params->ssid_len);
|
|
offset += params->ssid_len;
|
|
|
|
ie_ht_oper[2] = (fw_freq - 5000) / 5;
|
|
|
|
if (ie_ht_oper[2] == 36 || ie_ht_oper[2] == 44 || ie_ht_oper[2] == 52 || ie_ht_oper[2] == 60 || ie_ht_oper[2] == 149 || ie_ht_oper[2] == 157)
|
|
ie_ht_oper[3] = 0x01 | 0x04; /* Secondary Channel offset:1 SCA */
|
|
if (ie_ht_oper[2] == 40 || ie_ht_oper[2] == 48 || ie_ht_oper[2] == 56 || ie_ht_oper[2] == 64 || ie_ht_oper[2] == 153 || ie_ht_oper[2] == 161)
|
|
ie_ht_oper[3] = 0x03 | 0x04; /* Secondary Channel offset:3 SCB */
|
|
|
|
memcpy((new_ies + offset), ie_supported_rates_5g, sizeof(ie_supported_rates_5g) / sizeof(ie_supported_rates_5g[0]));
|
|
offset += 10;
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_UT_EXCEPTION
|
|
/* add DS parameter */
|
|
*(new_ies + offset) = WLAN_EID_DS_PARAMS;
|
|
offset += 1;
|
|
*(new_ies + offset) = 0x01;
|
|
offset += 1;
|
|
*(new_ies + offset) = (fw_freq - 5000) / 5;
|
|
offset += 1;
|
|
#endif
|
|
/* add IBSS parameter*/
|
|
memcpy((new_ies + offset), ie_ibss_params, 4);
|
|
offset += 4;
|
|
/* add ERP information*/
|
|
memcpy((new_ies + offset), ie_erp_element, 3);
|
|
offset += 3;
|
|
/* add HT capability */
|
|
memcpy((new_ies + offset), ie_ht_capa_5g, 28);
|
|
offset += 28;
|
|
memcpy((new_ies + offset), ie_ht_oper, 24);
|
|
offset += 24;
|
|
/* add VHT capability and operation */
|
|
memcpy((new_ies + offset), ie_vht_capa, 14);
|
|
offset += 14;
|
|
memcpy((new_ies + offset), ie_vht_oper, 7);
|
|
offset += 7;
|
|
}
|
|
break;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
|
case IEEE80211_BAND_60GHZ:
|
|
#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))*/
|
|
default:
|
|
SLSI_NET_ERR(dev, "ndev_vif->chan->band %d is NOT supported.\n",
|
|
ndev_vif->chan->band);
|
|
goto exit_with_vif;
|
|
}
|
|
|
|
new_ies_len = offset;
|
|
|
|
slsi_read_default_country(sdev, alpha2, 1);
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "alpha2 : %hhx %hhx %hhx. ", alpha2[0] , alpha2[1], alpha2[2]);
|
|
|
|
/*Reg domain changes */
|
|
slsi_set_country_update_regd(sdev, alpha2, SLSI_COUNTRY_CODE_LEN);
|
|
|
|
ndev_vif->vif_type = FAPI_VIFTYPE_ADHOC;
|
|
|
|
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;
|
|
}
|
|
|
|
if (slsi_vif_activated(sdev, dev) != 0) {
|
|
SLSI_NET_ERR(dev, "slsi_vif_activated failed\n");
|
|
goto exit_with_vif;
|
|
}
|
|
|
|
/* add wmm parameter as local.*/
|
|
SLSI_EC_GOTO(slsi_cache_ies(ie_wmm_parameter, (ie_wmm_parameter[1] + 2), &ndev_vif->ibss.cache_wmm_param_ie, &ndev_vif->ibss.wmm_param_ie_len), r, exit_with_vif);
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "Add info elements for local\n");
|
|
SLSI_EC_GOTO(slsi_ibss_prepare_add_info_ies(ndev_vif), r, exit_with_vif);
|
|
SLSI_EC_GOTO(slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_LOCAL, ndev_vif->ibss.add_info_ies, ndev_vif->ibss.add_info_ies_len), r, exit_with_vif);
|
|
slsi_clear_cached_ies(&ndev_vif->ibss.add_info_ies, &ndev_vif->ibss.add_info_ies_len);
|
|
slsi_clear_cached_ies(&ndev_vif->ibss.cache_wmm_param_ie, &ndev_vif->ibss.wmm_param_ie_len);
|
|
|
|
/* Extract the WPA IEs from params->ie - 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 at leave_ibss function
|
|
*/
|
|
wpa_ie_pos = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA, params->ie, params->ie_len);
|
|
if (wpa_ie_pos) {
|
|
wpa_ie_len = (size_t)(*(wpa_ie_pos + 1) + 2);
|
|
SLSI_NET_DBG3(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->ibss.cache_wpa_ie, &ndev_vif->ibss.wpa_ie_len), r, exit_with_vif);
|
|
}
|
|
|
|
/* Copy wmm information .*/
|
|
SLSI_EC_GOTO(slsi_cache_ies(ie_wmm_information, (ie_wmm_information[1] + 2), &ndev_vif->ibss.cache_wmm_info_ie, &ndev_vif->ibss.wmm_info_ie_len), r, exit_with_vif);
|
|
|
|
/* Oxygen IOP with QCOM */
|
|
params->privacy = wpa_ie_pos == NULL ? 0 : 1;
|
|
|
|
tmp_ies = params->ie;
|
|
params->ie = new_ies;
|
|
params->ie_len = new_ies_len;
|
|
|
|
r = slsi_mlme_start_ibss(sdev, dev, params->bssid, params);
|
|
params->ie = tmp_ies;
|
|
|
|
if (r != 0) {
|
|
SLSI_NET_ERR(dev, "start ibss failed: resultcode = %d\n", r);
|
|
goto exit_with_vif;
|
|
} else {
|
|
struct cfg80211_bss *bss = NULL;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
|
|
bss = cfg80211_inform_bss(wiphy, ndev_vif->chan, CFG80211_BSS_FTYPE_UNKNOWN,
|
|
params->bssid, 0, WLAN_CAPABILITY_IBSS,
|
|
params->beacon_interval, new_ies, new_ies_len, 1, GFP_KERNEL);
|
|
#else
|
|
bss = cfg80211_inform_bss(wiphy, ndev_vif->chan,
|
|
params->bssid, 0, WLAN_CAPABILITY_IBSS,
|
|
params->beacon_interval, new_ies, new_ies_len, 1, GFP_KERNEL);
|
|
#endif
|
|
|
|
if (bss == NULL) {
|
|
SLSI_NET_ERR(dev, "failed to call cfg80211_inform_bss()\n");
|
|
} else {
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
|
|
cfg80211_put_bss(wiphy, bss);
|
|
#else
|
|
cfg80211_put_bss(bss);
|
|
#endif
|
|
|
|
/*Notify ibss joined to upper layer*/
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
|
|
cfg80211_ibss_joined(dev, params->bssid, ndev_vif->chan, GFP_KERNEL);
|
|
#else
|
|
cfg80211_ibss_joined(dev, params->bssid, GFP_KERNEL);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* store beacon interval */
|
|
ndev_vif->ibss.beacon_interval = params->beacon_interval;
|
|
ndev_vif->ibss.rmc_txrate = SLSI_IBSS_RMC_DEFAULT_TX_RATE;
|
|
slsi_eth_zero_addr(ndev_vif->ibss.rmc_addr);
|
|
ndev_vif->ibss.ba_tx_userpri = 0;
|
|
ndev_vif->ibss.tx_fail_seq = 0;
|
|
|
|
netif_carrier_on(dev);
|
|
|
|
oxygen_netlink_init();
|
|
|
|
goto exit;
|
|
|
|
exit_with_vif:
|
|
slsi_mlme_del_vif(sdev, dev);
|
|
slsi_vif_deactivated(sdev, dev);
|
|
|
|
exit_with_error:
|
|
r = -EINVAL;
|
|
|
|
exit:
|
|
kfree(new_ies);
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
SLSI_MUTEX_UNLOCK(sdev->start_stop_mutex);
|
|
return r;
|
|
}
|
|
|
|
int slsi_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
|
|
{
|
|
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);
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Enter:%s\n", __func__);
|
|
|
|
if (WARN_ON(!ndev_vif->activated)) {
|
|
r = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (FAPI_VIFTYPE_ADHOC != ndev_vif->vif_type) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Invalid VIF Type: %d\n", ndev_vif->vif_type);
|
|
r = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
/* Free WPA and WMM IEs if present */
|
|
slsi_clear_cached_ies(&ndev_vif->ibss.cache_wpa_ie, &ndev_vif->ibss.wpa_ie_len);
|
|
slsi_clear_cached_ies(&ndev_vif->ibss.cache_wmm_info_ie, &ndev_vif->ibss.wmm_info_ie_len);
|
|
slsi_clear_cached_ies(&ndev_vif->ibss.cache_oui_ie, &ndev_vif->ibss.oui_ie_len);
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
slsi_mlme_del_vif(sdev, dev);
|
|
slsi_vif_deactivated(sdev, dev);
|
|
ndev_vif->ipaddress = cpu_to_be32(0);
|
|
|
|
oxygen_netlink_release();
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0))
|
|
int slsi_del_station(struct wiphy *wiphy, struct net_device *dev,
|
|
struct station_del_parameters *del_params)
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
|
|
int slsi_del_station(struct wiphy *wiphy, struct net_device *dev,
|
|
const u8 *mac)
|
|
#else
|
|
int slsi_del_station(struct wiphy *wiphy, struct net_device *dev,
|
|
u8 *mac)
|
|
#endif
|
|
{
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct slsi_peer *peer;
|
|
int r = 0;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0))
|
|
const u8 *mac = del_params->mac;
|
|
#endif
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "%pM\n", mac);
|
|
|
|
/* Function is called by cfg80211 before the VIF is added */
|
|
if (!ndev_vif->activated)
|
|
goto exit;
|
|
|
|
if (FAPI_VIFTYPE_AP != ndev_vif->vif_type) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Invalid Device Type: %d\n", ndev_vif->iftype);
|
|
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)) {
|
|
int i = 0;
|
|
bool peer_connected = false;
|
|
|
|
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);
|
|
peer_connected = true;
|
|
}
|
|
++i;
|
|
}
|
|
if (peer_connected) {
|
|
if (slsi_mlme_disconnect(sdev, dev, 0, WLAN_REASON_DEAUTH_LEAVING, true) != 0)
|
|
SLSI_NET_ERR(dev, "Disconnection for peermac=00:00:00:00:00:00 returned with CFM failure\n");
|
|
r = slsi_handle_disconnect(sdev, dev, peer->address, WLAN_REASON_DEAUTH_LEAVING);
|
|
}
|
|
|
|
/* 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
|
|
*/
|
|
} 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, WLAN_REASON_DEAUTH_LEAVING, 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, WLAN_REASON_DEAUTH_LEAVING);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
|
|
int slsi_get_station(struct wiphy *wiphy, struct net_device *dev,
|
|
const u8 *mac, struct station_info *sinfo)
|
|
#else
|
|
int slsi_get_station(struct wiphy *wiphy, struct net_device *dev,
|
|
u8 *mac, struct station_info *sinfo)
|
|
#endif
|
|
{
|
|
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 (WARN_ON(!peer)) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "%pM : Not Found\n", mac);
|
|
r = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "%pM, tx:%d, txbytes:%llu, rx:%d, rxbytes:%llu\n",
|
|
mac,
|
|
peer->sinfo.tx_packets,
|
|
peer->sinfo.tx_bytes,
|
|
peer->sinfo.rx_packets,
|
|
peer->sinfo.rx_bytes);
|
|
#else
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "%pM, tx:%d, txbytes:%d, rx:%d, rxbytes:%d\n",
|
|
mac,
|
|
peer->sinfo.tx_packets,
|
|
peer->sinfo.tx_bytes,
|
|
peer->sinfo.rx_packets,
|
|
peer->sinfo.rx_bytes);
|
|
#endif
|
|
|
|
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;
|
|
|
|
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_232338_test_mode_enabled()) {
|
|
SLSI_NET_INFO(dev, "Skip sending signal, MCD 232338 RF test does not support.\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "enabled:%d\n", enabled);
|
|
|
|
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;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 18))
|
|
int slsi_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper)
|
|
#else
|
|
int slsi_tdls_oper(struct wiphy *wiphy, struct net_device *dev, u8 *peer, enum nl80211_tdls_operation oper)
|
|
#endif
|
|
{
|
|
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:\n", oper);
|
|
|
|
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
|
return -ENOTSUPP;
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
if ((!ndev_vif->activated) || (ndev_vif->ifnum == SLSI_NET_INDEX_P2PX) ||
|
|
(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, 0, 0);
|
|
break;
|
|
case NL80211_TDLS_SETUP:
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "NL80211_TDLS_SETUP\n");
|
|
r = slsi_mlme_tdls_action(sdev, dev, peer, FAPI_TDLSACTION_SETUP, 0, 0);
|
|
break;
|
|
case NL80211_TDLS_TEARDOWN:
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "NL80211_TDLS_TEARDOWN\n");
|
|
r = slsi_mlme_tdls_action(sdev, dev, peer, FAPI_TDLSACTION_TEARDOWN, 0, 0);
|
|
break;
|
|
default:
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Operation not supported\n");
|
|
r = -EOPNOTSUPP;
|
|
goto exit;
|
|
}
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
if (qos_map) {
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
int slsi_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow)
|
|
{
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
|
|
SLSI_UNUSED_PARAMETER(wow);
|
|
|
|
SLSI_INFO(sdev, "Suspended\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int slsi_resume(struct wiphy *wiphy)
|
|
{
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
|
|
SLSI_INFO(sdev, "Resumed\n");
|
|
|
|
/* Scheduling the IO thread */
|
|
/* (void)slsi_hip_run_bh(sdev); */
|
|
|
|
return 0;
|
|
}
|
|
|
|
int slsi_set_pmksa(struct wiphy *wiphy, struct net_device *dev,
|
|
struct cfg80211_pmksa *pmksa)
|
|
{
|
|
SLSI_UNUSED_PARAMETER(wiphy);
|
|
SLSI_UNUSED_PARAMETER(dev);
|
|
SLSI_UNUSED_PARAMETER(pmksa);
|
|
return 0;
|
|
}
|
|
|
|
int slsi_del_pmksa(struct wiphy *wiphy, struct net_device *dev,
|
|
struct cfg80211_pmksa *pmksa)
|
|
{
|
|
SLSI_UNUSED_PARAMETER(wiphy);
|
|
SLSI_UNUSED_PARAMETER(dev);
|
|
SLSI_UNUSED_PARAMETER(pmksa);
|
|
return 0;
|
|
}
|
|
|
|
int slsi_flush_pmksa(struct wiphy *wiphy, struct net_device *dev)
|
|
{
|
|
SLSI_UNUSED_PARAMETER(wiphy);
|
|
SLSI_UNUSED_PARAMETER(dev);
|
|
return 0;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
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;
|
|
|
|
#else
|
|
int slsi_remain_on_channel(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
struct ieee80211_channel *chan,
|
|
enum nl80211_channel_type channel_type,
|
|
unsigned int duration,
|
|
u64 *cookie)
|
|
{
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) */
|
|
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\n", chan->center_freq, duration);
|
|
|
|
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_NET_DBG1(dev, SLSI_CFG80211, "ROC requested in Group Role (%s) - Allow but do nothing\n",
|
|
((sdev->p2p_state == P2P_GROUP_FORMED_GO) ? "P2P GO" : "P2P CLI"));
|
|
|
|
slsi_assign_cookie_id(cookie, &ndev_vif->unsync.roc_cookie);
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL);
|
|
cfg80211_remain_on_channel_expired(wdev, *cookie, chan, GFP_KERNEL);
|
|
#else
|
|
cfg80211_ready_on_channel(dev, *cookie, chan, channel_type, duration, GFP_KERNEL);
|
|
cfg80211_remain_on_channel_expired(dev, *cookie, chan, channel_type, GFP_KERNEL);
|
|
#endif
|
|
goto exit;
|
|
}
|
|
|
|
/* Unsync vif will be required, cancel any pending work of its deletion */
|
|
cancel_delayed_work(&ndev_vif->unsync.del_vif_work);
|
|
|
|
/* 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 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)) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Allow ROC requested in %s state\n", slsi_p2p_state_text(sdev->p2p_state));
|
|
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->chan && (ndev_vif->chan->hw_value != 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;
|
|
}
|
|
}
|
|
} else {
|
|
SLSI_NET_ERR(dev, "Driver in incorrect P2P state (%s)", slsi_p2p_state_text(sdev->p2p_state));
|
|
goto exit_with_error;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 9))
|
|
ndev_vif->channel_type = channel_type;
|
|
#endif
|
|
|
|
SLSI_P2P_STATE_CHANGE(sdev, P2P_LISTENING);
|
|
|
|
exit_with_roc:
|
|
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);
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL);
|
|
#else
|
|
cfg80211_ready_on_channel(dev, *cookie, chan, channel_type, duration, GFP_KERNEL);
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
int slsi_cancel_remain_on_channel(struct wiphy *wiphy,
|
|
struct wireless_dev *wdev,
|
|
u64 cookie)
|
|
{
|
|
struct net_device *dev = wdev->netdev;
|
|
|
|
#else
|
|
int slsi_cancel_remain_on_channel(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
u64 cookie)
|
|
{
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) */
|
|
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\n", 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))) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Driver not in listen - Ignore Cancel ROC\n");
|
|
goto exit;
|
|
}
|
|
|
|
if (sdev->p2p_state == P2P_ACTION_FRAME_TX_RX && ndev_vif->mgmt_tx_data.exp_frame != SLSI_P2P_PA_INVALID) {
|
|
/* Reset the expected action frame as procedure got completed */
|
|
SLSI_INFO(sdev, "Action frame (%s) was not received\n", slsi_p2p_pa_subtype_text(ndev_vif->mgmt_tx_data.exp_frame));
|
|
ndev_vif->mgmt_tx_data.exp_frame = SLSI_P2P_PA_INVALID;
|
|
}
|
|
|
|
WARN_ON(cookie != ndev_vif->unsync.roc_cookie);
|
|
|
|
cancel_delayed_work(&ndev_vif->unsync.roc_expiry_work);
|
|
|
|
/* Supplicant has stopped FIND/LISTEN. Clear Probe Response IEs in firmware and driver */
|
|
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 (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
cfg80211_remain_on_channel_expired(&ndev_vif->wdev, ndev_vif->unsync.roc_cookie, ndev_vif->chan, GFP_KERNEL);
|
|
#else
|
|
cfg80211_remain_on_channel_expired(ndev_vif->wdev.netdev, ndev_vif->unsync.roc_cookie,
|
|
ndev_vif->chan, ndev_vif->channel_type, GFP_KERNEL);
|
|
#endif
|
|
|
|
/* 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);
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
int r = 0;
|
|
|
|
SLSI_UNUSED_PARAMETER(params);
|
|
SLSI_UNUSED_PARAMETER_NOT_DEBUG(sdev);
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "TBD\n");
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "use_cts_prot :%d\n", params->use_cts_prot);
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "use_short_preamble :%d\n", params->use_short_preamble);
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "use_short_slot_time:%d\n", params->use_short_slot_time);
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "ap_isolate :%d\n", params->ap_isolate);
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "ht_opmode :%d\n", params->ht_opmode);
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "basic_rates_len :%d\n", params->basic_rates_len);
|
|
|
|
goto exit;
|
|
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 5, 0))
|
|
int slsi_set_channel(struct wiphy *wiphy, struct net_device *dev,
|
|
struct ieee80211_channel *chan,
|
|
enum nl80211_channel_type channel_type)
|
|
{
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
int r = 0;
|
|
|
|
SLSI_UNUSED_PARAMETER_NOT_DEBUG(sdev);
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "channel_type:%u, freq:%u\n", channel_type, chan->center_freq);
|
|
if (WARN_ON(ndev_vif->activated)) {
|
|
r = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
ndev_vif->channel_type = channel_type;
|
|
ndev_vif->chan = chan;
|
|
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 5, 0)) */
|
|
|
|
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;
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Perform scan before starting 20 MHz HT AP on 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);
|
|
|
|
(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,
|
|
true /* Wait for scan_done_ind */);
|
|
|
|
slsi_ap_obss_scan_done_ind(dev, ndev_vif);
|
|
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 (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
ndev_vif->chandef = &settings->chandef;
|
|
ndev_vif->chan = ndev_vif->chandef->chan;
|
|
#endif
|
|
|
|
if (WARN_ON(ndev_vif->chan == NULL))
|
|
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 (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
if ((ndev_vif->chan->hw_value <= 14) && (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;
|
|
}
|
|
#else
|
|
if ((ndev_vif->chan->hw_value <= 14) && (ndev_vif->channel_type > NL80211_CHAN_HT20)) {
|
|
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->channel_type));
|
|
goto exit_with_error;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
exit_with_error:
|
|
return -EINVAL;
|
|
}
|
|
|
|
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);
|
|
u8 device_address[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
int r = 0;
|
|
const u8 *wpa_ie_pos = NULL;
|
|
size_t wpa_ie_len = 0;
|
|
const u8 *wmm_ie_pos = NULL;
|
|
size_t wmm_ie_len = 0;
|
|
const u8 *country_ie = NULL;
|
|
char alpha2[SLSI_COUNTRY_CODE_LEN];
|
|
bool append_vht_ies = false;
|
|
|
|
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;
|
|
}
|
|
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Iftype: %d\n", ndev_vif->iftype);
|
|
|
|
r = slsi_ap_start_validate(dev, sdev, settings);
|
|
if (r != 0)
|
|
goto exit_with_vif_mutex;
|
|
|
|
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);*/
|
|
|
|
/* 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) {
|
|
r = -EINVAL;
|
|
goto exit_with_vif_mutex;
|
|
}
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
if (slsi_check_channelization(sdev, ndev_vif->chandef) != 0) {
|
|
#else
|
|
if (slsi_check_channelization(sdev, ndev_vif->channel_type) != 0) {
|
|
#endif
|
|
r = -EINVAL;
|
|
goto exit_with_vif_mutex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ndev_vif->iftype == NL80211_IFTYPE_P2P_GO) {
|
|
slsi_p2p_group_start_remove_unsync_vif(sdev);
|
|
SLSI_ETHER_COPY(device_address, sdev->netdev_addresses[SLSI_NET_INDEX_P2P]);
|
|
if (keep_alive_period != SLSI_P2PGO_KEEP_ALIVE_PERIOD_SEC)
|
|
if (slsi_set_uint_mib(sdev, NULL, SLSI_PSID_UNIFI_GO_KEEP_ALIVE_PERIOD, keep_alive_period) != 0) {
|
|
SLSI_NET_ERR(dev, "P2PGO Keep Alive MIB set failed");
|
|
r = -EINVAL;
|
|
goto exit_with_vif_mutex;
|
|
}
|
|
#ifdef SSB_4963_FIXED
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
/* Default 11ac configuration */
|
|
if (!(cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, settings->beacon.tail, settings->beacon.tail_len)) && (ndev_vif->chandef->chan->hw_value >= 36)) {
|
|
u16 oper_chan = ndev_vif->chandef->chan->hw_value;
|
|
|
|
ndev_vif->chandef->width = NL80211_CHAN_WIDTH_80;
|
|
if ((oper_chan >= 36) && (oper_chan <= 48))
|
|
ndev_vif->chandef->center_freq1 = ieee80211_channel_to_frequency(42, IEEE80211_BAND_5GHZ);
|
|
else if ((oper_chan >= 149) && (oper_chan <= 161))
|
|
ndev_vif->chandef->center_freq1 = ieee80211_channel_to_frequency(155, IEEE80211_BAND_5GHZ);
|
|
if (cfg80211_chandef_valid(ndev_vif->chandef)) {
|
|
u8 *ht_operation_ie;
|
|
u8 sec_chan_offset = 0;
|
|
|
|
append_vht_ies = true;
|
|
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");
|
|
r = -EINVAL;
|
|
goto exit_with_vif_mutex;
|
|
}
|
|
|
|
if (oper_chan == 36 || oper_chan == 44 || oper_chan == 149 || oper_chan == 157)
|
|
sec_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
|
else
|
|
sec_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
|
/* 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;
|
|
}
|
|
}
|
|
/* End */
|
|
#endif
|
|
#else
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
/* Default HT40 configuration */
|
|
if ((ndev_vif->chandef->chan->hw_value >= 36) && (ndev_vif->chandef->width == NL80211_CHAN_WIDTH_20)) {
|
|
u16 oper_chan = ndev_vif->chandef->chan->hw_value;
|
|
u8 ht_info_value = 0;
|
|
u8 bw_40_minus_channels[4] = { 40, 48, 153, 161 };
|
|
u8 ch;
|
|
bool offset_above = true;
|
|
|
|
ndev_vif->chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
|
for (ch = 0; ch < 4; ch++)
|
|
if (oper_chan == bw_40_minus_channels[ch]) {
|
|
ht_info_value = IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
|
ndev_vif->chandef->center_freq1 = ndev_vif->chandef->chan->center_freq - 10;
|
|
offset_above = false;
|
|
break;
|
|
}
|
|
|
|
if (offset_above) {
|
|
ht_info_value = IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
|
ndev_vif->chandef->center_freq1 = ndev_vif->chandef->chan->center_freq + 10;
|
|
}
|
|
|
|
if (cfg80211_chandef_valid(ndev_vif->chandef)) {
|
|
slsi_modify_ies(dev, WLAN_EID_HT_OPERATION, (u8 *)settings->beacon.tail, settings->beacon.tail_len, 3, ht_info_value);
|
|
slsi_modify_ies(dev, WLAN_EID_HT_CAPABILITY, (u8 *)settings->beacon.tail, settings->beacon.tail_len, 2, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
|
|
} else {
|
|
SLSI_NET_WARN(dev, "chandef is not valid");
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
} else { /* Legacy AP */
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
if (ndev_vif->chandef->width == NL80211_CHAN_WIDTH_20)
|
|
#else
|
|
if (ndev_vif->channel_type == NL80211_CHAN_HT20)
|
|
#endif
|
|
slsi_ap_start_obss_scan(sdev, dev, ndev_vif);
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
/* Enable SGI by default */
|
|
if (cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, settings->beacon.tail, settings->beacon.tail_len)) {
|
|
u8 mod_value = IEEE80211_HT_CAP_SGI_20;
|
|
|
|
if (ndev_vif->chandef->width == NL80211_CHAN_WIDTH_40)
|
|
mod_value |= IEEE80211_HT_CAP_SGI_40;
|
|
|
|
slsi_modify_ies(dev, WLAN_EID_HT_CAPABILITY, (u8 *)settings->beacon.tail, settings->beacon.tail_len, 2, mod_value);
|
|
}
|
|
|
|
if (cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, settings->beacon.tail, settings->beacon.tail_len))
|
|
slsi_modify_ies(dev, WLAN_EID_VHT_CAPABILITY, (u8 *)settings->beacon.tail, settings->beacon.tail_len, 2, IEEE80211_VHT_CAP_SHORT_GI_80);
|
|
#endif
|
|
}
|
|
|
|
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 = (size_t)(*(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 = (size_t)(*(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;
|
|
}
|
|
}
|
|
|
|
r = slsi_mlme_start(sdev, dev, dev->dev_addr, settings, wpa_ie_pos, wmm_ie_pos, append_vht_ies);
|
|
if (r != 0) {
|
|
SLSI_NET_ERR(dev, "Start ap failed: resultcode = %d\n", r);
|
|
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;
|
|
|
|
netif_carrier_on(dev);
|
|
|
|
if (ndev_vif->ipaddress != cpu_to_be32(0))
|
|
/* 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 + 2000;
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "slsi_read_disconnect_ind_timeout: timeout = %d", sdev->device_config.ap_disconnect_ind_timeout);
|
|
|
|
#ifdef ANDROID_BUILD
|
|
pm_qos_add_request(&ap_pm_qos_req, PM_QOS_CPU_FREQ_MIN, 500000);
|
|
#endif
|
|
|
|
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);
|
|
slsi_mlme_del_vif(sdev, dev);
|
|
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);
|
|
return r;
|
|
}
|
|
|
|
int slsi_change_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
struct cfg80211_beacon_data *info)
|
|
{
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
int r = 0;
|
|
|
|
SLSI_UNUSED_PARAMETER(info);
|
|
SLSI_UNUSED_PARAMETER_NOT_DEBUG(sdev);
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Operation not supported\n");
|
|
r = -EOPNOTSUPP;
|
|
|
|
goto exit;
|
|
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
|
|
int slsi_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
|
{
|
|
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);
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "\n");
|
|
|
|
if (!ndev_vif->activated) {
|
|
r = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (FAPI_VIFTYPE_AP != ndev_vif->vif_type) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Invalid Device Type: %d\n", ndev_vif->iftype);
|
|
r = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
/* 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 */
|
|
slsi_mlme_del_vif(sdev, dev);
|
|
slsi_vif_deactivated(sdev, dev);
|
|
ndev_vif->ipaddress = cpu_to_be32(0);
|
|
|
|
if (ndev_vif->ap.p2p_gc_keys_set) {
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "P2PGO - Release wakelock acquired after last client disconnection\n");
|
|
slsi_wakeunlock(&sdev->wlan_wl);
|
|
ndev_vif->ap.p2p_gc_keys_set = false;
|
|
}
|
|
#ifdef ANDROID_BUILD
|
|
pm_qos_remove_request(&ap_pm_qos_req);
|
|
#endif
|
|
exit:
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
|
|
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_p2p_get_public_action_subtype(mgmt);
|
|
int r = 0;
|
|
u32 host_tag = slsi_tx_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 (WARN_ON(sdev->p2p_group_exp_frame != SLSI_P2P_PA_INVALID))
|
|
return -EINVAL;
|
|
netdev = slsi_get_netdev(sdev, SLSI_NET_INDEX_P2PX);
|
|
ndev_vif = netdev_priv(netdev);
|
|
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
|
|
|
|
if (!((ndev_vif->iftype == NL80211_IFTYPE_P2P_GO) || (ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT)))
|
|
goto exit_with_error;
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Sending Action frame (%s) on p2p group vif (%d)\n", slsi_p2p_pa_subtype_text(subtype), ndev_vif->activated);
|
|
|
|
if (chan->hw_value != ndev_vif->chan->hw_value) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Off-channel TX: channel freq = %d, dwell time = %dms\n", chan->center_freq, wait);
|
|
freq = SLSI_FREQ_HOST_TO_FW(chan->center_freq);
|
|
dwell_time = wait;
|
|
}
|
|
|
|
sdev->p2p_group_exp_frame = slsi_p2p_get_exp_peer_frame_subtype(subtype);
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Expected action frame is (%s)\n", slsi_p2p_pa_subtype_text(sdev->p2p_group_exp_frame));
|
|
|
|
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_error;
|
|
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_host_tag(sdev);
|
|
int subtype = slsi_p2p_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_p2p_pa_subtype_text(subtype), ndev_vif->activated);
|
|
|
|
if (subtype == SLSI_P2P_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;
|
|
|
|
/* Vif might be present but frame tx could be requested on a different channel */
|
|
if (ndev_vif->chan && (ndev_vif->chan->hw_value != chan->hw_value)) {
|
|
/* Clear Probe Response IEs if vif was already present with a different channel */
|
|
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;
|
|
}
|
|
|
|
/* Check if peer frame response is expected */
|
|
exp_peer_frame = slsi_p2p_get_exp_peer_frame_subtype(subtype);
|
|
|
|
if (exp_peer_frame != SLSI_P2P_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_P2P_PA_INVALID;
|
|
} else {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Peer response expected with action frame (%s)\n",
|
|
slsi_p2p_pa_subtype_text(exp_peer_frame));
|
|
|
|
if (ndev_vif->mgmt_tx_data.exp_frame != SLSI_P2P_PA_INVALID)
|
|
(void)slsi_set_mgmt_tx_data(ndev_vif, 0, 0, NULL, 0);
|
|
|
|
/* Change Force Schedule Duration as peer response is expected */
|
|
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 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_P2P_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.
|
|
*/
|
|
WARN_ON(sdev->p2p_state != P2P_ACTION_FRAME_TX_RX);
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Received another frame Tx while already in Tx\n");
|
|
|
|
if (!dont_wait_for_ack) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Send immediate tx_status (cookie = 0x%llx)\n", *cookie);
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
|
cfg80211_mgmt_tx_status(&ndev_vif->wdev, *cookie, buf, len, true, GFP_KERNEL);
|
|
#else
|
|
cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, GFP_KERNEL);
|
|
#endif
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
|
int slsi_mgmt_tx_cancel_wait(struct wiphy *wiphy,
|
|
struct wireless_dev *wdev,
|
|
u64 cookie)
|
|
{
|
|
struct net_device *dev = wdev->netdev;
|
|
|
|
#else
|
|
int slsi_mgmt_tx_cancel_wait(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
u64 cookie)
|
|
{
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) */
|
|
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_DBG2(dev, SLSI_CFG80211, "iface_num = %d, cookie = 0x%llx\n", ndev_vif->ifnum, cookie);
|
|
|
|
/* If device was in frame tx_rx state, clear mgmt tx data and change state */
|
|
if ((sdev->p2p_state == P2P_ACTION_FRAME_TX_RX) && (ndev_vif->mgmt_tx_data.cookie == cookie)) {
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Clear mgmt_tx data for cookie = 0x%llx\n", cookie);
|
|
|
|
if (ndev_vif->mgmt_tx_data.exp_frame != SLSI_P2P_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_P2P_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_P2P_PA_INVALID)) {
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Reset P2P group action frame data\n");
|
|
/* acquire mutex lock if it is not group net dev */
|
|
slsi_clear_offchannel_data(sdev, (ndev_vif->ifnum != SLSI_NET_INDEX_P2PX) ? true : false);
|
|
} else if ((sdev->hs2_state == HS2_VIF_TX) && (ndev_vif->mgmt_tx_data.cookie == cookie)) {
|
|
sdev->hs2_state = HS2_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));
|
|
}
|
|
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return 0;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
void slsi_mgmt_frame_register(struct wiphy *wiphy,
|
|
struct wireless_dev *wdev,
|
|
u16 frame_type, bool reg)
|
|
{
|
|
struct net_device *dev = wdev->netdev;
|
|
|
|
#else
|
|
void slsi_mgmt_frame_register(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
u16 frame_type, bool reg)
|
|
{
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) */
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
SLSI_UNUSED_PARAMETER(frame_type);
|
|
SLSI_UNUSED_PARAMETER(reg);
|
|
#endif
|
|
|
|
if (WARN_ON(!dev))
|
|
return;
|
|
|
|
SLSI_UNUSED_PARAMETER_NOT_DEBUG(sdev);
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "frame_type:%d, reg:%d\n", frame_type, reg);
|
|
}
|
|
|
|
static int slsi_hs2_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_host_tag(sdev);
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
int r = 0;
|
|
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
|
|
|
|
slsi_hs2_dump_public_action_subtype(mgmt, true);
|
|
|
|
if (!ndev_vif->activated) {
|
|
r = slsi_hs2_vif_activate(sdev, dev, chan, wait);
|
|
if (r)
|
|
return r;
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "Send action frame : channel freq = %d, dwell time = %d ms)\n", chan->center_freq, wait);
|
|
|
|
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->hs2_state = HS2_VIF_TX;
|
|
queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.hs2_del_vif_work, msecs_to_jiffies(wait));
|
|
} else {
|
|
/* vif is active*/
|
|
if (ndev_vif->vif_type == FAPI_VIFTYPE_UNSYNCHRONISED) {
|
|
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->chan->hw_value != chan->hw_value) {
|
|
r = slsi_mlme_set_channel(sdev, dev, chan, SLSI_FW_CHANNEL_DURATION_UNSPECIFIED, 0, 0);
|
|
if (r)
|
|
goto exit_with_vif;
|
|
}
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "HS2 vif is active ,send GAS (ANQP) request 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->hs2_state = HS2_VIF_TX;
|
|
queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.hs2_del_vif_work, msecs_to_jiffies(wait));
|
|
} else if (ndev_vif->chan->hw_value == chan->hw_value) {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "STA VIF is active on same channel, send GAS (ANQP) request 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)
|
|
return r;
|
|
} else {
|
|
SLSI_NET_DBG1(dev, SLSI_CFG80211, "STA VIF is active on a different channel, send GAS (ANQP) request 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, SLSI_FREQ_HOST_TO_FW(chan->center_freq), wait * 1000, 0);
|
|
if (r)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
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_hs2_vif_deactivate(sdev, dev, true);
|
|
return r;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
|
|
int slsi_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
struct cfg80211_mgmt_tx_params *params,
|
|
u64 *cookie)
|
|
{
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
|
int slsi_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
struct ieee80211_channel *chan, bool offchan,
|
|
unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie)
|
|
{
|
|
struct net_device *dev = wdev->netdev;
|
|
|
|
#else
|
|
int slsi_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
|
struct ieee80211_channel *chan, bool offchan,
|
|
enum nl80211_channel_type channel_type,
|
|
bool channel_type_valid, unsigned int wait,
|
|
const u8 *buf, size_t len, bool no_cck,
|
|
bool dont_wait_for_ack, u64 *cookie)
|
|
{
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) */
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
|
|
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;
|
|
#endif
|
|
|
|
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_DBG2(dev, SLSI_CFG80211, "Mgmt Frame Tx: iface_num = %d, channel = %d, wait = %d, noAck = %d, offchannel = %d\n",
|
|
ndev_vif->ifnum, chan->hw_value, wait, dont_wait_for_ack, offchan);
|
|
|
|
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)) {
|
|
r = slsi_hs2_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)) {
|
|
SLSI_NET_DBG3(dev, SLSI_CFG80211, "Drop Probe Response from supplicant");
|
|
/* 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);
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
|
cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_KERNEL);
|
|
#else
|
|
cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, GFP_KERNEL);
|
|
#endif
|
|
}
|
|
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 (ndev_vif->ifnum == SLSI_NET_INDEX_P2PX)
|
|
if (chan->hw_value == ndev_vif->chan->hw_value) {
|
|
struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
|
|
u16 host_tag = slsi_tx_host_tag(sdev);
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_CFG80211, "Mgmt TX received on group vif\n");
|
|
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;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
|
int ac = params->ac;
|
|
#else
|
|
int ac = params->queue;
|
|
#endif
|
|
/* 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", ac, ac_remapped, params->aifs, params->cwmin, params->cwmax, params->txop);
|
|
|
|
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;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
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)
|
|
r = slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_ASSOCIATION_REQUEST, ftie->ie, ftie->ie_len);
|
|
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
|
|
return r;
|
|
}
|
|
|
|
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, params);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
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,
|
|
.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,
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_ENABLE
|
|
.join_ibss = slsi_join_ibss,
|
|
.leave_ibss = slsi_leave_ibss,
|
|
#endif
|
|
.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,
|
|
|
|
.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,
|
|
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 5, 0))
|
|
.set_channel = slsi_set_channel,
|
|
#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 5, 0)) */
|
|
|
|
.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,
|
|
|
|
.mgmt_frame_register = slsi_mgmt_frame_register,
|
|
.mgmt_tx = slsi_mgmt_tx,
|
|
.mgmt_tx_cancel_wait = slsi_mgmt_tx_cancel_wait,
|
|
.set_txq_params = slsi_set_txq_params,
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
.set_mac_acl = slsi_set_mac_acl,
|
|
.update_ft_ies = slsi_update_ft_ies,
|
|
#endif
|
|
.tdls_oper = slsi_tdls_oper,
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
|
|
.set_qos_map = slsi_set_qos_map
|
|
#endif
|
|
};
|
|
|
|
#define RATE_LEGACY(_rate, _hw_value, _flags) { \
|
|
.bitrate = (_rate), \
|
|
.hw_value = (_hw_value), \
|
|
.flags = (_flags), \
|
|
}
|
|
|
|
#define CHAN2G(_freq, _idx) { \
|
|
.band = IEEE80211_BAND_2GHZ, \
|
|
.center_freq = (_freq), \
|
|
.hw_value = (_idx), \
|
|
.max_power = 17, \
|
|
}
|
|
|
|
#define CHAN5G(_freq, _idx) { \
|
|
.band = IEEE80211_BAND_5GHZ, \
|
|
.center_freq = (_freq), \
|
|
.hw_value = (_idx), \
|
|
.max_power = 17, \
|
|
}
|
|
|
|
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),
|
|
/* UNII 3 */
|
|
CHAN5G(5745, 149),
|
|
CHAN5G(5765, 153),
|
|
CHAN5G(5785, 157),
|
|
CHAN5G(5805, 161),
|
|
CHAN5G(5825, 165),
|
|
};
|
|
|
|
/* 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_16,
|
|
.mcs = {
|
|
.rx_mask = { 0xff, 0, },
|
|
.rx_highest = cpu_to_le16(0),
|
|
.tx_params = 0,
|
|
},
|
|
};
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
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),
|
|
},
|
|
};
|
|
#endif
|
|
|
|
struct ieee80211_supported_band slsi_band_2ghz = {
|
|
.channels = slsi_2ghz_channels,
|
|
.band = IEEE80211_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 = IEEE80211_BAND_5GHZ,
|
|
.n_channels = ARRAY_SIZE(slsi_5ghz_channels),
|
|
.bitrates = wifi_11a_rates,
|
|
.n_bitrates = ARRAY_SIZE(wifi_11a_rates),
|
|
};
|
|
|
|
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
|
|
};
|
|
|
|
static const struct ieee80211_txrx_stypes
|
|
ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
|
|
[NL80211_IFTYPE_AP] = {
|
|
.tx = 0xffff,
|
|
.rx = BIT(IEEE80211_STYPE_ACTION >> 4)
|
|
},
|
|
[NL80211_IFTYPE_STATION] = {
|
|
.tx = 0xffff,
|
|
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
|
|
BIT(IEEE80211_STYPE_PROBE_REQ >> 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
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
|
|
static struct cfg80211_wowlan slsi_wowlan_config = {
|
|
.any = true,
|
|
};
|
|
#endif
|
|
#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;
|
|
|
|
/* wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
|
|
*
|
|
* Whilst the firmware does support roaming the driver MUST NOT advertise it
|
|
* as the supplicant will NOT send the BSSID and frequency information in the
|
|
* connect cfg80211 op.
|
|
* If the driver advertises FW_ROAM then the supplicant expects it to perform
|
|
* any scans required to find an appropriate AP and will only pass the SSID
|
|
*/
|
|
|
|
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
|
|
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
|
|
WIPHY_FLAG_AP_UAPSD;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
wiphy->max_acl_mac_addrs = SLSI_AP_PEER_CONNECTIONS_MAX;
|
|
#endif
|
|
|
|
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_ADHOC);
|
|
#endif
|
|
slsi_band_2ghz.ht_cap = slsi_ht_cap;
|
|
slsi_band_5ghz.ht_cap = slsi_ht_cap;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
|
|
slsi_band_5ghz.vht_cap = slsi_vht_cap;
|
|
#endif
|
|
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
|
|
|
|
wiphy->bands[IEEE80211_BAND_2GHZ] = &slsi_band_2ghz;
|
|
/* Disable 5Ghz in wiphy configuration for single band support */
|
|
#ifdef SCSC_WLAN_ENABLE_5GHZ
|
|
wiphy->bands[IEEE80211_BAND_5GHZ] = &slsi_band_5ghz;
|
|
#else
|
|
wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
|
|
#endif
|
|
|
|
/* Disable 5Ghz in device configuration for single band support */
|
|
#ifdef SCSC_WLAN_ENABLE_5GHZ
|
|
sdev->device_config.band_5G = &slsi_band_5ghz;
|
|
#else
|
|
sdev->device_config.band_5G = NULL;
|
|
#endif
|
|
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->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;
|
|
|
|
/* Scheduled scanning support */
|
|
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
|
|
/* 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
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
|
|
wiphy->wowlan = NULL;
|
|
wiphy->wowlan_config = &slsi_wowlan_config;
|
|
#endif
|
|
#endif
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
|
|
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
|
|
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
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
|
|
sdev->wiphy->wowlan = NULL;
|
|
sdev->wiphy->wowlan_config = NULL;
|
|
#endif
|
|
#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);
|
|
}
|