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

4914 lines
160 KiB
C
Executable File

/*****************************************************************************
*
* Copyright (c) 2012 - 2020 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/delay.h>
#include <net/cfg80211.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <scsc/scsc_log_collector.h>
#include "dev.h"
#include "debug.h"
#include "mlme.h"
#include "mib.h"
#include "mgt.h"
#include "cac.h"
#define SLSI_SCAN_PRIVATE_IE_CHANNEL_LIST_HEADER_LEN 7
#define SLSI_SCAN_PRIVATE_IE_SSID_FILTER_HEADER_LEN 7
#define SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE 3
#define SLSI_CHANN_INFO_HT_SCB 0x0100
#define SLSI_NOA_CONFIG_REQUEST_ID (1)
#define SLSI_MLME_ARP_DROP_FREE_SLOTS_COUNT 16
static bool missing_cfm_ind_panic = true;
module_param(missing_cfm_ind_panic, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(missing_cfm_ind_panic, "Panic on missing confirm or indication from the chip");
struct slsi_mlme_rsse {
u8 group_cs_count;
const u8 *group_cs;
u8 pairwise_cs_count;
const u8 *pairwise_cs;
u8 akm_suite_count;
const u8 *akm_suite;
u8 pmkid_count;
const u8 *pmkid;
const u8 *group_mgmt_cs; /* used for PMF*/
};
static struct sk_buff *slsi_mlme_wait_for_cfm(struct slsi_dev *sdev, struct slsi_sig_send *sig_wait)
{
struct sk_buff *cfm = NULL;
int tm;
tm = wait_for_completion_timeout(&sig_wait->completion, msecs_to_jiffies(*sdev->sig_wait_cfm_timeout));
spin_lock_bh(&sig_wait->send_signal_lock);
/* Confirm timed out? */
if (!sig_wait->cfm) {
SLSI_ERR(sdev, "No cfm(0x%.4X) for req(0x%04X) senderid=0x%x\n", sig_wait->cfm_id, sig_wait->req_id, sig_wait->process_id);
if (tm == 0) {
char reason[80];
WARN(1, "Timeout - confirm 0x%04x not received from chip\n", sig_wait->cfm_id);
if (missing_cfm_ind_panic) {
snprintf(reason, sizeof(reason), "Timed out while waiting for the cfm(0x%.4x) for req(0x%04x)",
sig_wait->cfm_id, sig_wait->req_id);
spin_unlock_bh(&sig_wait->send_signal_lock);
slsi_sm_service_failed(sdev, reason);
spin_lock_bh(&sig_wait->send_signal_lock);
}
} else {
WARN(1, "Confirm 0x%04x lost\n", sig_wait->cfm_id);
}
} else {
WARN_ON(fapi_get_u16(sig_wait->cfm, receiver_pid) != sig_wait->process_id);
WARN_ON(fapi_get_u16(sig_wait->cfm, id) != sig_wait->cfm_id);
}
sig_wait->cfm_id = 0;
cfm = sig_wait->cfm;
sig_wait->cfm = NULL;
if (!cfm)
sig_wait->ind_id = 0;
spin_unlock_bh(&sig_wait->send_signal_lock);
return cfm;
}
static int panic_on_lost_ind(u16 ind_id)
{
if (ind_id == MLME_SCAN_DONE_IND)
return 0;
return 1;
}
static struct sk_buff *slsi_mlme_wait_for_ind(struct slsi_dev *sdev, struct net_device *dev, struct slsi_sig_send *sig_wait, u16 ind_id)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *ind = NULL;
int tm = 0;
/* The indication and confirm may have been received in the same HIP read.
* The HIP receive buffer processes all received signals in one thread whilst the
* waiting process may not be scheduled even if the "complete" call is made.
* In this scenario, the complete() call has already been made for this object
* and the wait will return immediately.
*/
if (ind_id == MLME_SCAN_DONE_IND)
/* To handle the coex scenario where BTscan has high priority increasing the wait time to 40 secs */
tm = wait_for_completion_timeout(&sig_wait->completion, msecs_to_jiffies(SLSI_SCAN_DONE_IND_WAIT_TIMEOUT));
else if ((ind_id == MLME_DISCONNECT_IND) && (ndev_vif->vif_type == FAPI_VIFTYPE_AP))
tm = wait_for_completion_timeout(&sig_wait->completion, msecs_to_jiffies(sdev->device_config.ap_disconnect_ind_timeout));
else
tm = wait_for_completion_timeout(&sig_wait->completion, msecs_to_jiffies(*sdev->sig_wait_cfm_timeout));
spin_lock_bh(&sig_wait->send_signal_lock);
/* Indication timed out? */
if (!sig_wait->ind) {
SLSI_ERR(sdev, "No ind(0x%.4X) for req(0x%04X) senderid=0x%x\n", sig_wait->ind_id, sig_wait->req_id, sig_wait->process_id);
if (tm == 0) {
char reason[80];
WARN(1, "Timeout - indication 0x%04x not received from chip\n", sig_wait->ind_id);
if (missing_cfm_ind_panic && panic_on_lost_ind(ind_id)) {
snprintf(reason, sizeof(reason), "Timed out while waiting for the ind(0x%.4x) for req(0x%04x)",
sig_wait->ind_id, sig_wait->req_id);
spin_unlock_bh(&sig_wait->send_signal_lock);
slsi_sm_service_failed(sdev, reason);
spin_lock_bh(&sig_wait->send_signal_lock);
}
} else {
WARN(1, "Indication 0x%04x lost\n", sig_wait->ind_id);
}
} else {
WARN_ON(fapi_get_u16(sig_wait->ind, receiver_pid) != sig_wait->process_id);
if (!(sig_wait->ind_id == MLME_DISCONNECT_IND && fapi_get_u16(sig_wait->ind, id) == MLME_DISCONNECTED_IND))
WARN_ON(fapi_get_u16(sig_wait->ind, id) != sig_wait->ind_id);
}
sig_wait->ind_id = 0;
ind = sig_wait->ind;
sig_wait->ind = NULL;
spin_unlock_bh(&sig_wait->send_signal_lock);
return ind;
}
/* mib_error: NULL when not required
* ind: 0 when not required, if used validate_cfm_wait_ind MUST be supplied
* validate_cfm_wait_ind: NULL when not required, if used ind MUS not be 0
* NOTE: dev can be NULL!
*/
static struct sk_buff *slsi_mlme_tx_rx(struct slsi_dev *sdev,
struct net_device *dev,
struct sk_buff *skb,
u16 cfm_id,
struct sk_buff **mib_error,
u16 ind_id,
bool (*validate_cfm_wait_ind)(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *cfm))
{
struct sk_buff *rx = NULL;
int err;
u16 req_id = fapi_get_u16(skb, id);
struct slsi_sig_send *sig_wait = &sdev->sig_wait;
if (dev) {
struct netdev_vif *ndev_vif = netdev_priv(dev);
sig_wait = &ndev_vif->sig_wait;
}
#ifdef SLSI_TEST_DEV
if (sdev->mlme_blocked) {
SLSI_DBG3(sdev, SLSI_TX, "Rejected. mlme_blocked=%d\n", sdev->mlme_blocked);
kfree_skb(skb);
return NULL;
}
#else
if (sdev->mlme_blocked || !sdev->wlan_service_on) {
SLSI_DBG3(sdev, SLSI_TX, "Rejected. mlme_blocked=%d, wlan_service_on=%d\n", sdev->mlme_blocked,
sdev->wlan_service_on);
kfree_skb(skb);
return NULL;
}
#endif
slsi_wake_lock(&sdev->wlan_wl);
SLSI_MUTEX_LOCK(sig_wait->mutex);
spin_lock_bh(&sig_wait->send_signal_lock);
if (++sig_wait->process_id > SLSI_TX_PROCESS_ID_MAX)
sig_wait->process_id = SLSI_TX_PROCESS_ID_MIN;
WARN_ON(sig_wait->cfm);
WARN_ON(sig_wait->ind);
kfree_skb(sig_wait->cfm);
kfree_skb(sig_wait->ind);
kfree_skb(sig_wait->mib_error);
sig_wait->cfm = NULL;
sig_wait->ind = NULL;
sig_wait->mib_error = NULL;
sig_wait->req_id = req_id;
sig_wait->cfm_id = cfm_id;
sig_wait->ind_id = ind_id;
fapi_set_u16(skb, sender_pid, sig_wait->process_id);
spin_unlock_bh(&sig_wait->send_signal_lock);
err = slsi_tx_control(sdev, dev, skb);
if (err != 0) {
SLSI_ERR(sdev, "Failed to send mlme signal:0x%.4X, err=%d\n", req_id, err);
kfree_skb(skb);
goto clean_exit;
}
if (cfm_id) {
rx = slsi_mlme_wait_for_cfm(sdev, sig_wait);
if (rx && ind_id) {
/* The cfm skb is owned by the validate_cfm_wait_ind() function and MUST be freed or saved there */
if (validate_cfm_wait_ind(sdev, dev, rx)) {
rx = slsi_mlme_wait_for_ind(sdev, dev, sig_wait, ind_id);
} else {
sig_wait->ind_id = 0; /* Reset as there is no wait for indication */
rx = NULL;
}
}
} else if (ind_id) {
rx = slsi_mlme_wait_for_ind(sdev, dev, sig_wait, ind_id);
}
/* The cfm_id and ind_id should ALWAYS be 0 at this point */
WARN_ON(sig_wait->cfm_id);
WARN_ON(sig_wait->ind_id);
WARN_ON(sig_wait->cfm);
WARN_ON(sig_wait->ind);
clean_exit:
spin_lock_bh(&sig_wait->send_signal_lock);
sig_wait->req_id = 0;
sig_wait->cfm_id = 0;
sig_wait->ind_id = 0;
kfree_skb(sig_wait->cfm);
kfree_skb(sig_wait->ind);
sig_wait->cfm = NULL;
sig_wait->ind = NULL;
if (mib_error)
*mib_error = sig_wait->mib_error;
else
kfree_skb(sig_wait->mib_error);
sig_wait->mib_error = NULL;
spin_unlock_bh(&sig_wait->send_signal_lock);
SLSI_MUTEX_UNLOCK(sig_wait->mutex);
slsi_wake_unlock(&sdev->wlan_wl);
return rx;
}
/**
* NOTE: dev can be NULL!
*/
int slsi_mlme_req(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
int ret = 0;
struct slsi_sig_send *sig_wait = &sdev->sig_wait;
if (dev) {
struct netdev_vif *ndev_vif = netdev_priv(dev);
sig_wait = &ndev_vif->sig_wait;
}
spin_lock_bh(&sig_wait->send_signal_lock);
if (++sig_wait->process_id > SLSI_TX_PROCESS_ID_MAX)
sig_wait->process_id = SLSI_TX_PROCESS_ID_MIN;
fapi_set_u16(skb, sender_pid, sig_wait->process_id);
spin_unlock_bh(&sig_wait->send_signal_lock);
ret = slsi_tx_control(sdev, dev, skb);
if (ret)
kfree_skb(skb);
return ret;
}
struct sk_buff *slsi_mlme_req_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, u16 ind_id)
{
if (WARN_ON(!ind_id))
goto err;
return slsi_mlme_tx_rx(sdev, dev, skb, 0, NULL, ind_id, NULL);
err:
kfree_skb(skb);
return NULL;
}
struct sk_buff *slsi_mlme_req_no_cfm(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
return slsi_mlme_tx_rx(sdev, dev, skb, 0, NULL, 0, NULL);
}
struct sk_buff *slsi_mlme_req_cfm(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, u16 cfm_id)
{
if (WARN_ON(!cfm_id))
goto err;
return slsi_mlme_tx_rx(sdev, dev, skb, cfm_id, NULL, 0, NULL);
err:
kfree_skb(skb);
return NULL;
}
/* NOTE: dev can be NULL! */
static inline struct sk_buff *slsi_mlme_req_cfm_mib(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, u16 cfm_id, struct sk_buff **mib_error)
{
if (WARN_ON(!cfm_id))
goto err;
if (WARN_ON(!mib_error))
goto err;
return slsi_mlme_tx_rx(sdev, dev, skb, cfm_id, mib_error, 0, NULL);
err:
kfree_skb(skb);
return NULL;
}
/* NOTE: dev can be NULL! */
struct sk_buff *slsi_mlme_req_cfm_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb,
u16 cfm_id, u16 ind_id,
bool (*validate_cfm_wait_ind)(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *cfm))
{
if (WARN_ON(!cfm_id))
goto err;
if (WARN_ON(!ind_id))
goto err;
if (WARN_ON(!validate_cfm_wait_ind))
goto err;
return slsi_mlme_tx_rx(sdev, dev, skb, cfm_id, NULL, ind_id, validate_cfm_wait_ind);
err:
kfree_skb(skb);
return NULL;
}
static struct ieee80211_reg_rule *slsi_get_reg_rule(u32 center_freq, struct slsi_802_11d_reg_domain *domain_info)
{
struct ieee80211_reg_rule *rule;
int i;
for (i = 0; i < domain_info->regdomain->n_reg_rules; i++) {
rule = &domain_info->regdomain->reg_rules[i];
/* Consider 10Mhz on both side from the center frequency */
if (((center_freq - MHZ_TO_KHZ(10)) >= rule->freq_range.start_freq_khz) &&
((center_freq + MHZ_TO_KHZ(10)) <= rule->freq_range.end_freq_khz))
return rule;
}
return NULL;
}
u16 slsi_compute_chann_info(struct slsi_dev *sdev, u16 width, u16 center_freq0, u16 channel_freq)
{
u16 chann_info;
u16 prim_chan_pos = 0;
SLSI_DBG3(sdev, SLSI_MLME, "compute channel info\n");
switch (width) {
case NL80211_CHAN_WIDTH_20:
chann_info = 20;
break;
case NL80211_CHAN_WIDTH_40:
chann_info = 40;
/* Check HT Minus */
if (center_freq0 < channel_freq)
chann_info |= SLSI_CHANN_INFO_HT_SCB;
break;
case NL80211_CHAN_WIDTH_80:
/* F = { F1-30, ... F1+30 } => { 0x0000, ... 0x0300} */
prim_chan_pos = ((30 + channel_freq - center_freq0) / 20);
if (prim_chan_pos > 3) {
SLSI_ERR(sdev, "Invalid center_freq0 in chandef : %u, primary channel = %u,"
"primary chan pos calculated = %d\n", center_freq0, channel_freq, prim_chan_pos);
prim_chan_pos = 0;
}
prim_chan_pos = 0xFFFF & (prim_chan_pos << 8);
chann_info = 80 | prim_chan_pos;
break;
default:
SLSI_WARN(sdev, "Invalid chandef.width(0x%x)\n", width);
chann_info = 0;
break;
}
SLSI_DBG3(sdev, SLSI_MLME, "channel_width:%u, chann_info:0x%x\n", width, chann_info);
return chann_info;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
u16 slsi_get_chann_info(struct slsi_dev *sdev, struct cfg80211_chan_def *chandef)
{
u16 chann_info = 0;
SLSI_UNUSED_PARAMETER(sdev);
if (chandef->width == NL80211_CHAN_WIDTH_20 || chandef->width == NL80211_CHAN_WIDTH_20_NOHT) {
chann_info = 20;
SLSI_DBG3(sdev, SLSI_MLME, "channel_width:%u, chann_info:0x%x\n", chandef->width, chann_info);
} else if (chandef->chan) {
chann_info = slsi_compute_chann_info(sdev, chandef->width, chandef->center_freq1,
chandef->chan->center_freq);
}
return chann_info;
}
int slsi_check_channelization(struct slsi_dev *sdev, struct cfg80211_chan_def *chandef,
int wifi_sharing_channel_switched)
{
u8 width;
struct ieee80211_reg_rule *rule = NULL;
struct ieee80211_channel *channel = NULL;
u32 ref_flags;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT:
width = 20;
break;
case NL80211_CHAN_WIDTH_40:
width = 40;
break;
case NL80211_CHAN_WIDTH_80:
width = 80;
break;
default:
SLSI_ERR(sdev, "Invalid chandef.width(0x%x)\n", chandef->width);
return -EINVAL;
}
channel = ieee80211_get_channel(sdev->wiphy, chandef->chan->center_freq);
if (!channel) {
SLSI_ERR(sdev, "Invalid channel %d used to start AP. Channel not found\n", chandef->chan->center_freq);
return -EINVAL;
}
if (wifi_sharing_channel_switched == 1) {
ref_flags = IEEE80211_CHAN_DISABLED
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 13)
| IEEE80211_CHAN_PASSIVE_SCAN
#endif
;
} else {
ref_flags = IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_RADAR
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 13)
| IEEE80211_CHAN_PASSIVE_SCAN
#endif
;
}
if (channel->flags & ref_flags) {
SLSI_ERR(sdev, "Invalid channel %d used to start AP\n", chandef->chan->center_freq);
return -EINVAL;
}
rule = slsi_get_reg_rule(MHZ_TO_KHZ(chandef->center_freq1), &sdev->device_config.domain_info);
if (!rule) {
SLSI_ERR(sdev, "Invalid channel %d used to start AP. No reg rule found for this channel\n", chandef->chan->center_freq);
return -EINVAL;
}
if (MHZ_TO_KHZ(width) <= rule->freq_range.max_bandwidth_khz) {
u32 width_boundary1, width_boundary2;
width_boundary1 = MHZ_TO_KHZ(chandef->center_freq1 - width / 2);
width_boundary2 = MHZ_TO_KHZ(chandef->center_freq1 + width / 2);
if ((width_boundary1 >= rule->freq_range.start_freq_khz) && (width_boundary2 <= rule->freq_range.end_freq_khz))
return 0;
SLSI_ERR(sdev, "Invalid channel %d used to start AP. Channel not within frequency range of the reg rule\n", chandef->chan->center_freq);
return -EINVAL;
}
return -EINVAL;
}
#else
u16 slsi_get_chann_info(struct slsi_dev *sdev, enum nl80211_channel_type channel_type)
{
u16 chann_info;
SLSI_UNUSED_PARAMETER(sdev);
/* Channel Info
* bits 0 ~ 7 : Channel Width (5, 10, 20, 40)
* bit 8 : Set to 1 if primary channel is greater than secondary channel (HT Minus)
*/
switch (channel_type) {
case NL80211_CHAN_NO_HT:
case NL80211_CHAN_HT20:
chann_info = 20;
break;
case NL80211_CHAN_HT40MINUS:
chann_info = 40 | SLSI_CHANN_INFO_HT_SCB;
break;
case NL80211_CHAN_HT40PLUS:
chann_info = 40;
break;
default:
SLSI_WARN(sdev, "Unknown channel_type: %d\n", channel_type);
chann_info = 0;
break;
}
SLSI_DBG3(sdev, SLSI_MLME, "channel_type:%d, chann_info:0x%x\n", channel_type, chann_info);
return chann_info;
}
int slsi_check_channelization(struct slsi_dev *sdev, enum nl80211_channel_type channel_type)
{
return 0;
}
#endif
/* Called in the case of MIB SET errors.
* Decode and print a MIB buffer to the log for debug purposes.
*/
static void mib_buffer_dump_to_log(struct slsi_dev *sdev, u8 *mib_buffer, unsigned int mib_buffer_len)
{
size_t mib_decode_result;
size_t offset = 0;
struct slsi_mib_entry decoded_mib_value;
struct slsi_mib_data mibdata;
int error_out_len = mib_buffer_len * 3;
int error_out_pos = 0;
char *error_out;
SLSI_UNUSED_PARAMETER(sdev);
FUNC_ENTER(sdev);
SLSI_ERR(sdev, "MIB buffer length: %u. MIB Error (decoded):", mib_buffer_len);
if (!mib_buffer) {
SLSI_ERR(sdev, "MIB buffer pointer is NULL - can not decode MIB keys\n");
return;
}
error_out = kmalloc(error_out_len, GFP_KERNEL);
while (offset < mib_buffer_len) {
error_out_pos = 0;
mibdata.data = &mib_buffer[offset];
mibdata.dataLength = mib_buffer_len - offset;
mib_decode_result = slsi_mib_decode(&mibdata, &decoded_mib_value);
if (!mib_decode_result) {
SLSI_ERR_HEX(sdev, mibdata.data, mibdata.dataLength, "slsi_mib_decode() Failed to Decode:\n");
break;
}
offset += mib_decode_result;
/* Time for some eye candy - output the decoded MIB key at error level in the log */
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, "%d", (int)(decoded_mib_value.psid));
if (decoded_mib_value.index[0]) {
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, ".%d", (int)(decoded_mib_value.index[0]));
if (decoded_mib_value.index[1])
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, ".%d", (int)(decoded_mib_value.index[1]));
}
switch (decoded_mib_value.value.type) {
case SLSI_MIB_TYPE_BOOL:
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, "=%s\n", decoded_mib_value.value.u.boolValue ? "TRUE" : "FALSE");
break;
case SLSI_MIB_TYPE_UINT:
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, "=%d\n", (int)decoded_mib_value.value.u.uintValue);
break;
case SLSI_MIB_TYPE_INT:
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, "=%d\n", (int)decoded_mib_value.value.u.intValue);
break;
case SLSI_MIB_TYPE_OCTET:
{
u32 i;
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, "=[");
for (i = 0; i < decoded_mib_value.value.u.octetValue.dataLength; i++)
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, "%.2X", (int)decoded_mib_value.value.u.octetValue.data[i]);
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, "]\n");
break;
}
default:
error_out_pos += snprintf(error_out + error_out_pos, error_out_len - error_out_pos, "=Can not decode MIB key type\n");
break;
}
SLSI_INFO_NODEV("%s", error_out);
}
kfree(error_out);
FUNC_EXIT(sdev);
}
int slsi_mlme_set_ip_address(struct slsi_dev *sdev, struct net_device *dev)
{
struct sk_buff *req;
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *cfm;
int r = 0;
u32 ipaddr;
u8 multicast_add[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_SET_IP_ADDRESS.request\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
req = fapi_alloc(mlme_set_ip_address_req, MLME_SET_IP_ADDRESS_REQ, ndev_vif->ifnum, sizeof(ndev_vif->ipaddress));
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_set_ip_address_req.ip_version, 4);
fapi_set_memcpy(req, u.mlme_set_ip_address_req.multicast_address, multicast_add);
ipaddr = htonl(be32_to_cpu(ndev_vif->ipaddress));
fapi_append_data(req, (const u8 *)(&ipaddr), sizeof(ipaddr));
SLSI_DBG2(sdev, SLSI_MLME, "slsi_mlme_set_ip_address(vif: %d, IP: %pI4)\n", ndev_vif->ifnum, &ndev_vif->ipaddress);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_IP_ADDRESS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_ip_address_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_ip_address_cfm(result:0x%04x) ERROR\n", fapi_get_u16(cfm, u.mlme_set_ip_address_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
#ifndef CONFIG_SCSC_WLAN_BLOCK_IPV6
int slsi_mlme_set_ipv6_address(struct slsi_dev *sdev, struct net_device *dev)
{
struct sk_buff *req;
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *cfm;
int r = 0;
u8 solicited_node_addr[ETH_ALEN] = { 0x33, 0x33, 0xff, 0x00, 0x00, 0x00 };
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_SET_IP_ADDRESS.request\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
req = fapi_alloc(mlme_set_ip_address_req, MLME_SET_IP_ADDRESS_REQ, ndev_vif->ifnum, 16);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_set_ip_address_req.ip_version, 6);
if (ndev_vif->sta.nd_offload_enabled == 1) {
slsi_spinlock_lock(&ndev_vif->ipv6addr_lock);
memcpy(&solicited_node_addr[3], &ndev_vif->ipv6address.s6_addr[13], 3);
slsi_spinlock_unlock(&ndev_vif->ipv6addr_lock);
fapi_set_memcpy(req, u.mlme_set_ip_address_req.multicast_address, solicited_node_addr);
fapi_append_data(req, ndev_vif->ipv6address.s6_addr, 16);
SLSI_DBG2(sdev, SLSI_MLME, "mlme_set_ip_address_req(vif: %d, IP: %pI6)\n", ndev_vif->ifnum,
&ndev_vif->ipv6address);
} else {
u8 node_addr_nd_disable[16];
memset(&node_addr_nd_disable, 0, sizeof(node_addr_nd_disable));
fapi_append_data(req, node_addr_nd_disable, 16);
SLSI_DBG2(sdev, SLSI_MLME, "mlme_set_ip_address_req(vif: %d, IP-setting ip address to all zeros)\n",
ndev_vif->ifnum);
}
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_IP_ADDRESS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_ip_address_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_ip_address_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_ip_address_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
#endif
int slsi_mlme_set(struct slsi_dev *sdev, struct net_device *dev, u8 *mib, int mib_len)
{
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
u16 ifnum = 0;
if (dev) {
struct netdev_vif *ndev_vif = netdev_priv(dev);
ifnum = ndev_vif->ifnum;
}
req = fapi_alloc(mlme_set_req, MLME_SET_REQ, ifnum, mib_len);
if (!req)
return -ENOMEM;
fapi_append_data(req, mib, mib_len);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_CFM);
if (!cfm)
return -EIO;
if (fapi_get_datalen(cfm)) {
mib_buffer_dump_to_log(sdev, fapi_get_data(cfm), fapi_get_datalen(cfm));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_get(struct slsi_dev *sdev, struct net_device *dev, u8 *mib, int mib_len, u8 *resp,
int resp_buf_len, int *resp_len)
{
struct sk_buff *req;
struct sk_buff *err = NULL;
struct sk_buff *cfm;
int r = 0;
u16 ifnum = 0;
*resp_len = 0;
if (dev) {
struct netdev_vif *ndev_vif = netdev_priv(dev);
ifnum = ndev_vif->ifnum;
}
req = fapi_alloc(mlme_get_req, MLME_GET_REQ, ifnum, mib_len);
if (!req)
return -ENOMEM;
fapi_append_data(req, mib, mib_len);
cfm = slsi_mlme_req_cfm_mib(sdev, dev, req, MLME_GET_CFM, &err);
if (!cfm)
return -EIO;
if (err) {
SLSI_DBG1(sdev, SLSI_MLME, "ERROR: mlme_get_cfm with mib error\n");
mib_buffer_dump_to_log(sdev, fapi_get_data(err), fapi_get_datalen(err));
LOG_CONDITIONALLY(fapi_get_datalen(cfm) > resp_buf_len,
SLSI_ERR(sdev, "Insufficient resp_buf_len(%d). mlme_get_cfm(%d)\n",
resp_buf_len, fapi_get_datalen(cfm)));
r = -EINVAL;
}
/* if host has requested for multiple PSIDs in same request, we can get a
* combination of error and success
*/
if (fapi_get_datalen(cfm) <= resp_buf_len) {
*resp_len = fapi_get_datalen(cfm);
memcpy(resp, fapi_get_data(cfm), fapi_get_datalen(cfm));
r = 0;
} else {
SLSI_WARN(sdev, "Insufficient length (%d) to read MIB values, expected =%d\n", resp_buf_len, fapi_get_datalen(cfm));
r = -EINVAL;
}
kfree_skb(err);
kfree_skb(cfm);
return r;
}
int slsi_mlme_add_vif(struct slsi_dev *sdev, struct net_device *dev, u8 *interface_address, u8 *device_address)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0, i;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_ADD_VIF.request\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (sdev->require_vif_delete[ndev_vif->ifnum]) {
r = slsi_mlme_del_vif(sdev, dev);
if (r != 0) {
SLSI_NET_ERR(dev, "slsi_mlme_del_vif before add_vif failed\n");
return r;
}
}
/* reset host stats */
for (i = 0; i < SLSI_LLS_AC_MAX; i++) {
ndev_vif->tx_no_ack[i] = 0;
ndev_vif->tx_packets[i] = 0;
ndev_vif->rx_packets[i] = 0;
}
req = fapi_alloc(mlme_add_vif_req, MLME_ADD_VIF_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_add_vif_req.virtual_interface_type, ndev_vif->vif_type);
fapi_set_memcpy(req, u.mlme_add_vif_req.interface_address, interface_address);
fapi_set_memcpy(req, u.mlme_add_vif_req.device_address, device_address);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_add_vif_req(vif:%d)\n", ndev_vif->ifnum);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_ADD_VIF_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_add_vif_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_add_vif_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_add_vif_cfm.result_code));
r = -EINVAL;
}
/* By default firmware vif will be in active mode */
ndev_vif->power_mode = FAPI_POWERMANAGEMENTMODE_ACTIVE_MODE;
#ifdef CONFIG_SCSC_WLAN_ARP_FLOW_CONTROL
/* netdev arp_tx_count is expected to be 0. If its not 0, there is some
* error. Do not reset/decrement sdev arp_tx_count
*/
if (atomic_read(&ndev_vif->arp_tx_count))
SLSI_WARN(sdev,
"ndev_vif->arp_tx_count:%d expected:0 | sdev:%d\n",
ndev_vif->arp_tx_count, sdev->arp_tx_count);
atomic_set(&ndev_vif->arp_tx_count, 0);
#endif
kfree_skb(cfm);
return r;
}
int slsi_mlme_del_vif(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int ret = 0;
#ifdef CONFIG_SCSC_WLAN_ARP_FLOW_CONTROL
u32 arp_tx_count;
#endif
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_DEL_VIF.request\n");
return ret;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG2(dev, SLSI_MLME, "del_vif vif:%d, mlme_blocked:%s\n", ndev_vif->ifnum,
sdev->mlme_blocked ? "true" : "false");
if (sdev->mlme_blocked)
return ret;
req = fapi_alloc(mlme_del_vif_req, MLME_DEL_VIF_REQ, ndev_vif->ifnum, 0);
if (!req) {
sdev->require_vif_delete[ndev_vif->ifnum] = true;
return -ENOMEM;
}
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_DEL_VIF_CFM);
if (!cfm) {
sdev->require_vif_delete[ndev_vif->ifnum] = true;
return -EIO;
}
if (fapi_get_u16(cfm, u.mlme_del_vif_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_del_vif_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_del_vif_cfm.result_code));
sdev->require_vif_delete[ndev_vif->ifnum] = true;
ret = -EINVAL;
}
if (((ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT) || (ndev_vif->iftype == NL80211_IFTYPE_STATION)) &&
(ndev_vif->delete_probe_req_ies)) {
kfree(ndev_vif->probe_req_ies);
ndev_vif->probe_req_ies = NULL;
ndev_vif->probe_req_ie_len = 0;
ndev_vif->delete_probe_req_ies = false;
}
if (SLSI_IS_VIF_INDEX_P2P(ndev_vif))
ndev_vif->drv_in_p2p_procedure = false;
#ifdef CONFIG_SCSC_WLAN_ARP_FLOW_CONTROL
/* cleanup outstanding arp count for this vif*/
arp_tx_count = atomic_read(&ndev_vif->arp_tx_count);
if (arp_tx_count) {
atomic_sub(arp_tx_count, &sdev->arp_tx_count);
atomic_set(&ndev_vif->arp_tx_count, 0);
if (atomic_read(&sdev->ctrl_pause_state))
scsc_wifi_unpause_arp_q_all_vif(sdev);
}
#endif
if (!ret)
sdev->require_vif_delete[ndev_vif->ifnum] = false;
kfree_skb(cfm);
return ret;
}
#if defined(CONFIG_SLSI_WLAN_STA_FWD_BEACON) && (defined(SCSC_SEP_VERSION) && SCSC_SEP_VERSION >= 10)
int slsi_mlme_set_forward_beacon(struct slsi_dev *sdev, struct net_device *dev, int action)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "wlanlite does not support mlme_forward_bacon_req\n");
return -EOPNOTSUPP;
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_forward_beacon_req(action = %s(%d))\n", action ? "start" : "stop", action);
req = fapi_alloc(mlme_forward_beacon_req, MLME_FORWARD_BEACON_REQ, ndev_vif->ifnum, 0);
if (!req) {
SLSI_NET_ERR(dev, "fapi alloc for mlme_forward_beacon_req is failed\n");
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_forward_beacon_req.wips_action, action);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_FORWARD_BEACON_CFM);
if (!cfm) {
SLSI_NET_ERR(dev, "receiving mlme_forward_beacon_cfm is failed\n");
return -EIO;
}
if (fapi_get_u16(cfm, u.mlme_forward_beacon_cfm.result_code) != FAPI_RESULTCODE_HOST_REQUEST_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_forward_beacon_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_forward_beacon_cfm.result_code));
return -EINVAL;
}
ndev_vif->is_wips_running = (action ? true : false);
kfree_skb(cfm);
return 0;
}
#endif
int slsi_mlme_set_band_req(struct slsi_dev *sdev, struct net_device *dev, uint band)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int ret = 0;
SLSI_DBG1_NODEV(SLSI_MLME, "mlme_set_band_req(vif:%u band:%u)\n", ndev_vif->ifnum, band);
req = fapi_alloc(mlme_set_band_req, MLME_SET_BAND_REQ, ndev_vif->ifnum, 0);
if (!req)
return -EIO;
fapi_set_u16(req, u.mlme_set_band_req.vif, ndev_vif->ifnum);
fapi_set_u16(req, u.mlme_set_band_req.band, band);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_BAND_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_band_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_band_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_band_cfm.result_code));
ret = -EINVAL;
}
kfree_skb(cfm);
return ret;
}
int slsi_mlme_set_scan_mode_req(struct slsi_dev *sdev, struct net_device *dev, u16 scan_mode, u16 max_channel_time,
u16 home_away_time, u16 home_time, u16 max_channel_passive_time)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int ret = 0;
if (!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex)) {
SLSI_ERR(sdev, "ndev_vif mutex is not locked\n");
return -EINVAL;
}
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_set_scan_mode_req(vif:0, scan_mode:%d)\n", scan_mode);
req = fapi_alloc(mlme_set_scan_mode_req, MLME_SET_SCAN_MODE_REQ, 0, 0);
if (!req)
return -EIO;
fapi_set_u16(req, u.mlme_set_scan_mode_req.scan_mode, scan_mode);
fapi_set_u16(req, u.mlme_set_scan_mode_req.max_channel_time_active, max_channel_time);
fapi_set_u16(req, u.mlme_set_scan_mode_req.home_away_time, home_away_time);
fapi_set_u16(req, u.mlme_set_scan_mode_req.home_time, home_time);
fapi_set_u16(req, u.mlme_set_scan_mode_req.max_channel_time_passive, max_channel_passive_time);
cfm = slsi_mlme_req_cfm(sdev, NULL, req, MLME_SET_SCAN_MODE_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_scan_mode_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_scan_mode_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_scan_mode_cfm.result_code));
ret = -EINVAL;
}
kfree_skb(cfm);
return ret;
}
int slsi_mlme_set_roaming_parameters(struct slsi_dev *sdev, struct net_device *dev, u16 psid, int mib_value, int mib_length)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int ret = 0;
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_set_roaming_parameters_req(vif:%d value:%d)\n", ndev_vif->ifnum, mib_value);
req = fapi_alloc(mlme_set_roaming_parameters_req, MLME_SET_ROAMING_PARAMETERS_REQ, ndev_vif->ifnum, 0);
if (!req) {
SLSI_NET_ERR(dev, "fapi alloc failure\n");
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_set_roaming_parameters_req.vif, ndev_vif->ifnum);
fapi_append_data_u16(req, psid);
fapi_append_data_u16(req, mib_length);
switch (mib_length) {
case 1:
fapi_append_data_u8(req, mib_value);
break;
case 2:
fapi_append_data_u16(req, mib_value);
break;
case 4:
fapi_append_data_u32(req, mib_value);
break;
default:
kfree_skb(req);
return -EINVAL;
}
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_ROAMING_PARAMETERS_CFM);
if (!cfm) {
SLSI_NET_ERR(dev, "mlme_set_roaming_parameters_cfm failure\n");
return -EIO;
}
if (fapi_get_u16(cfm, u.mlme_set_roaming_parameters_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_roaming_parameters_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_roaming_type_cfm.result_code));
ret = -EINVAL;
}
kfree_skb(cfm);
return ret;
}
int slsi_mlme_set_channel(struct slsi_dev *sdev, struct net_device *dev, struct ieee80211_channel *chan, u16 duration, u16 interval, u16 count)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "wlanlite does not support MLME_SET_CHANNEL.request\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_set_channel_req(freq:%u, duration:%u, interval:%u, count:%u)\n", chan->center_freq, duration, interval, count);
req = fapi_alloc(mlme_set_channel_req, MLME_SET_CHANNEL_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_set_channel_req.availability_duration, duration);
fapi_set_u16(req, u.mlme_set_channel_req.availability_interval, interval);
fapi_set_u16(req, u.mlme_set_channel_req.count, count);
fapi_set_u16(req, u.mlme_set_channel_req.channel_frequency, SLSI_FREQ_HOST_TO_FW(chan->center_freq));
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_CHANNEL_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_channel_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_channel_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_channel_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_unset_channel_req(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
SLSI_NET_DBG3(dev, SLSI_MLME, "slsi_mlme_unset_channel_req\n");
req = fapi_alloc(mlme_unset_channel_req, MLME_UNSET_CHANNEL_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_UNSET_CHANNEL_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_unset_channel_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_unset_channel_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_unset_channel_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
void slsi_ap_obss_scan_done_ind(struct net_device *dev, struct netdev_vif *ndev_vif)
{
struct sk_buff *scan_res;
u16 scan_id = SLSI_SCAN_HW_ID;
SLSI_UNUSED_PARAMETER(dev);
SLSI_NET_DBG1(dev, SLSI_MLME, "Scan before AP start completed\n");
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_mutex));
SLSI_MUTEX_LOCK(ndev_vif->scan_result_mutex);
scan_res = slsi_dequeue_cached_scan_result(&ndev_vif->scan[scan_id], NULL);
while (scan_res) {
struct ieee80211_mgmt *mgmt = fapi_get_mgmt(scan_res);
size_t mgmt_len = fapi_get_mgmtlen(scan_res);
size_t ie_len = mgmt_len - offsetof(struct ieee80211_mgmt, u.beacon.variable); /* ieee80211_mgmt structure is similar for Probe Response and Beacons */
SLSI_NET_DBG4(dev, SLSI_MLME, "OBSS scan result (scan_id:%d, %pM, freq:%d, rssi:%d, ie_len = %zu)\n",
fapi_get_u16(scan_res, u.mlme_scan_ind.scan_id),
fapi_get_mgmt(scan_res)->bssid,
fapi_get_u16(scan_res, u.mlme_scan_ind.channel_frequency) / 2,
fapi_get_s16(scan_res, u.mlme_scan_ind.rssi),
ie_len);
if (!cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, mgmt->u.beacon.variable, ie_len)) {
SLSI_NET_DBG1(dev, SLSI_MLME, "Non HT BSS detected on primary channel\n");
ndev_vif->ap.non_ht_bss_present = true;
}
kfree_skb(scan_res);
scan_res = slsi_dequeue_cached_scan_result(&ndev_vif->scan[scan_id], NULL);
}
SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex);
}
/* Null check for cfm done in caller function */
static bool slsi_scan_cfm_validate(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *cfm)
{
bool r = true;
if (fapi_get_u16(cfm, u.mlme_add_scan_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_ERR_NODEV("mlme_add_scan_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_add_scan_cfm.result_code));
r = false;
}
kfree_skb(cfm);
return r;
}
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
int slsi_mlme_append_gscan_channel_list(struct slsi_dev *sdev,
struct net_device *dev,
struct sk_buff *req,
struct slsi_nl_bucket_param *nl_bucket)
{
u16 channel_freq;
u8 i;
u8 *p;
const u8 channels_list_ie_header[] = {
0xDD, /* Element ID: Vendor Specific */
0x05, /* Length: actual length will be updated later */
0x00, 0x16, 0x32, /* OUI: Samsung Electronics Co. */
0x01, /* OUI Type: Scan parameters */
0x02 /* OUI Subtype: channel list */
};
u8 *channels_list_ie = fapi_append_data(req, channels_list_ie_header, sizeof(channels_list_ie_header));
if (!channels_list_ie) {
SLSI_WARN(sdev, "channel list IE append failed\n");
kfree_skb(req);
return -EINVAL;
}
if (nl_bucket->band == WIFI_BAND_UNSPECIFIED)
/* channel list is added only if band is UNSPECIFIED */
for (i = 0; i < nl_bucket->num_channels; i++) {
p = fapi_append_data(req, NULL, SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE);
if (!p) {
SLSI_ERR(sdev, "chan desc[%d] append failed\n", i);
kfree_skb(req);
return -EINVAL;
}
channel_freq = SLSI_FREQ_HOST_TO_FW(nl_bucket->channels[i].channel);
channel_freq = cpu_to_le16(channel_freq);
memcpy(p, &channel_freq, sizeof(channel_freq));
p[2] = FAPI_SCANPOLICY_ANY_RA;
channels_list_ie[1] += SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE; /* Length */
}
else {
p = fapi_append_data(req, NULL, SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE);
if (!p) {
SLSI_ERR(sdev, "chan desc(band specific)append failed\n");
kfree_skb(req);
return -EINVAL;
}
/* Channel frequency set to 0 for all channels allowed by the corresponding regulatory domain and scan policy */
channel_freq = 0;
memcpy(p, &channel_freq, sizeof(channel_freq));
p[2] = slsi_gscan_get_scan_policy(nl_bucket->band);
channels_list_ie[1] += SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE;
}
return 0;
}
#endif
static int slsi_mlme_append_channel_list(struct slsi_dev *sdev,
struct net_device *dev,
struct sk_buff *req,
u32 num_channels,
struct ieee80211_channel *channels[],
u16 scan_type,
bool passive_scan)
{
int chann;
u16 freq_fw_unit;
u8 i;
int n_valid_channels = 0;
u8 *p;
const u8 channels_list_ie_header[] = {
0xDD, /* Element ID: vendor specific */
0x05, /* Length: actual length will be updated later */
0x00, 0x16, 0x32, /* OUI: Samsung Electronics Co. */
0x01, /* OUI Type: scan parameters */
0x02 /* OUI Subtype: channel list */
};
u8 *channels_list_ie = fapi_append_data(req, channels_list_ie_header, sizeof(channels_list_ie_header));
if (!channels_list_ie) {
SLSI_WARN(sdev, "channel list IE append failed\n");
kfree_skb(req);
return -EINVAL;
}
/* For P2P Full Scan, Setting Channel Frequency = 0x0000, Scan Policy = 2.4GHz, 5GHz and Non-Dfs. */
if (scan_type == FAPI_SCANTYPE_P2P_SCAN_FULL) {
p = fapi_append_data(req, NULL, SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE);
if (!p) {
SLSI_WARN(sdev, "scan channel descriptor append failed\n");
kfree_skb(req);
return -EINVAL;
}
p[0] = 0;
p[1] = 0;
p[2] = FAPI_SCANPOLICY_2_4GHZ | FAPI_SCANPOLICY_5GHZ | FAPI_SCANPOLICY_NON_DFS;
channels_list_ie[1] += SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE;
return 0;
}
for (i = 0; i < num_channels; i++) {
chann = channels[i]->hw_value & 0xFF;
if (sdev->device_config.supported_band) {
if (channels[i]->band == NL80211_BAND_2GHZ && sdev->device_config.supported_band != SLSI_FREQ_BAND_2GHZ)
continue;
if (channels[i]->band == NL80211_BAND_5GHZ && sdev->device_config.supported_band != SLSI_FREQ_BAND_5GHZ)
continue;
}
n_valid_channels++;
p = fapi_append_data(req, NULL, SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE);
if (!p) {
SLSI_WARN(sdev, "scan channel descriptor append failed\n");
kfree_skb(req);
return -EINVAL;
}
freq_fw_unit = 2 * ieee80211_channel_to_frequency(chann, (chann <= 14) ?
NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
freq_fw_unit = cpu_to_le16(freq_fw_unit);
memcpy(p, &freq_fw_unit, sizeof(freq_fw_unit));
if (passive_scan && (scan_type != FAPI_SCANTYPE_AP_AUTO_CHANNEL_SELECTION))
p[2] = FAPI_SCANPOLICY_PASSIVE;
else
p[2] = 0;
channels_list_ie[1] += SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE;
}
if (n_valid_channels == 0) {
SLSI_NET_ERR(dev, "no valid channels to Scan\n");
kfree_skb(req);
return -EINVAL;
}
return 0;
}
static inline int slsi_set_scan_params(
struct net_device *dev,
u16 scan_id,
u16 scan_type,
u16 report_mode,
int num_ssids,
struct cfg80211_ssid *ssids,
struct sk_buff *req)
{
u8 *p = NULL;
u8 i;
struct cfg80211_ssid *pssid = ssids;
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
struct netdev_vif *netdev_vif = netdev_priv(dev);
struct slsi_dev *sdev = netdev_vif->sdev;
#endif
fapi_set_u16(req, u.mlme_add_scan_req.scan_id, scan_id);
fapi_set_u16(req, u.mlme_add_scan_req.scan_type, scan_type);
fapi_set_u16(req, u.mlme_add_scan_req.report_mode_bitmap, report_mode);
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
if (sdev->scan_addr_set)
fapi_set_memcpy(req, u.mlme_add_scan_req.device_address, sdev->scan_mac_addr);
else
#endif
fapi_set_memcpy(req, u.mlme_add_scan_req.device_address, dev->dev_addr);
for (i = 0; i < num_ssids; i++, pssid++) {
p = fapi_append_data(req, NULL, 2 + pssid->ssid_len);
if (!p) {
kfree_skb(req);
SLSI_NET_WARN(dev, "fail to append SSID element to scan request\n");
return -EINVAL;
}
*p++ = WLAN_EID_SSID;
*p++ = pssid->ssid_len;
if (pssid->ssid_len)
memcpy(p, pssid->ssid, pssid->ssid_len);
}
return 0;
}
#define SLSI_MAX_SSID_DESC_IN_SSID_FILTER_ELEM 7
int slsi_mlme_add_sched_scan(struct slsi_dev *sdev,
struct net_device *dev,
struct cfg80211_sched_scan_request *request,
const u8 *ies,
u16 ies_len)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
size_t alloc_data_size = 0;
u32 i, j;
u32 num_ssid_filter_elements = 0;
/* Scan Timing IE: default values */
u8 scan_timing_ie[] = {
0xdd, /* Element ID: Vendor Specific */
0x11, /* Length */
0x00, 0x16, 0x32, /* OUI: Samsung Electronics Co. */
0x01, /* OUI Type: Scan parameters */
0x01, /* OUI Subtype: Scan timing */
0x00, 0x00, 0x00, 0x00, /* Min_Period: filled later in the function */
0x00, 0x00, 0x00, 0x00, /* Max_Period: filled later in the function */
0x01, /* Exponent */
0x01, /* Step count */
0x00, 0x00 /* Skip first period: false*/
};
u8 ssid_filter_ie_hdr[] = {
0xdd, /* Element ID: Vendor Specific */
0x05, /* Length */
0x00, 0x16, 0x32, /* OUI: Samsung Electronics Co. */
0x01, /* OUI Type: Scan parameters */
0x04 /* OUI Subtype: SSID Filter */
};
if (slsi_is_test_mode_enabled()) {
SLSI_NET_WARN(dev, "not supported in WlanLite mode\n");
return -EOPNOTSUPP;
}
if (WARN_ON(!(dev->dev_addr)))
return -EINVAL;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_mutex));
#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0))
if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
if (sdev->fw_mac_randomization_enabled) {
memcpy(sdev->scan_mac_addr, request->mac_addr, ETH_ALEN);
r = slsi_set_mac_randomisation_mask(sdev, request->mac_addr_mask);
if (!r)
sdev->scan_addr_set = 1;
} else {
SLSI_NET_INFO(dev, "Mac Randomization is not enabled in Firmware\n");
sdev->scan_addr_set = 0;
}
}
#endif
#endif
alloc_data_size += sizeof(scan_timing_ie) + ies_len + SLSI_SCAN_PRIVATE_IE_CHANNEL_LIST_HEADER_LEN +
(request->n_channels * SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE);
for (i = 0; i < request->n_ssids; i++) {
/* 2 bytes for SSID EID and length field + variable length SSID */
alloc_data_size += (2 + request->ssids[i].ssid_len);
}
if (request->n_match_sets) {
num_ssid_filter_elements = (request->n_match_sets / SLSI_MAX_SSID_DESC_IN_SSID_FILTER_ELEM) + 1;
/* EID(1) + len(1) + oui(3) + type/subtype(2) + 7 ssid descriptors(7 * 33) */
alloc_data_size += 238 * num_ssid_filter_elements;
}
req = fapi_alloc(mlme_add_scan_req, MLME_ADD_SCAN_REQ, 0, alloc_data_size);
if (!req)
return -ENOMEM;
r = slsi_set_scan_params(dev, (ndev_vif->ifnum << 8 | SLSI_SCAN_SCHED_ID),
FAPI_SCANTYPE_SCHEDULED_SCAN,
FAPI_REPORTMODE_REAL_TIME,
request->n_ssids,
request->ssids,
req);
if (r)
return r;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
#ifdef CONFIG_SCSC_WLAN_EXPONENTIAL_SCHED_SCAN
/* 16th byte (index 15): exponent, 17th byte (index 16): step count */
SLSI_U32_TO_BUFF_LE(request->scan_plans[0].interval * 1000 * 1000, &scan_timing_ie[7]);
if (request->scan_plans[0].interval < request->scan_plans[1].interval) {
SLSI_U32_TO_BUFF_LE(request->scan_plans[1].interval * 1000 * 1000, &scan_timing_ie[11]);
scan_timing_ie[15] = request->scan_plans[1].interval / request->scan_plans[0].interval;
scan_timing_ie[16] = request->scan_plans[0].iterations;
} else {
SLSI_U32_TO_BUFF_LE(request->scan_plans[0].interval * 1000 * 1000, &scan_timing_ie[11]);
scan_timing_ie[15] = 1;
scan_timing_ie[16] = 0;
}
#else
SLSI_U32_TO_BUFF_LE(request->scan_plans->interval * 1000 * 1000, &scan_timing_ie[7]);
SLSI_U32_TO_BUFF_LE(request->scan_plans->interval * 1000 * 1000, &scan_timing_ie[11]);
#endif
#else
SLSI_U32_TO_BUFF_LE(request->interval * 1000, &scan_timing_ie[7]);
SLSI_U32_TO_BUFF_LE(request->interval * 1000, &scan_timing_ie[11]);
#endif
fapi_append_data(req, scan_timing_ie, sizeof(scan_timing_ie));
fapi_append_data(req, ies, ies_len);
if (request->n_match_sets) {
struct cfg80211_match_set *match_sets = request->match_sets;
u8 *ssid_filter_ie;
for (j = 0; j < num_ssid_filter_elements; j++) {
ssid_filter_ie = fapi_append_data(req, ssid_filter_ie_hdr, sizeof(ssid_filter_ie_hdr));
if (!ssid_filter_ie) {
kfree_skb(req);
SLSI_ERR(sdev, "ssid_filter_ie append failed\n");
return -EIO;
}
for (i = 0; i < SLSI_MAX_SSID_DESC_IN_SSID_FILTER_ELEM; i++, match_sets++) {
if ((j * SLSI_MAX_SSID_DESC_IN_SSID_FILTER_ELEM) + i >= request->n_match_sets)
break;
SLSI_NET_DBG2(dev, SLSI_MLME, "SSID: %.*s",
match_sets->ssid.ssid_len, match_sets->ssid.ssid);
ssid_filter_ie[1] += (1 + match_sets->ssid.ssid_len);
fapi_append_data(req, &match_sets->ssid.ssid_len, 1);
fapi_append_data(req, match_sets->ssid.ssid, match_sets->ssid.ssid_len);
}
}
}
if (request->n_channels) {
r = slsi_mlme_append_channel_list(sdev, dev, req, request->n_channels, request->channels,
FAPI_SCANTYPE_SCHEDULED_SCAN, request->n_ssids == 0);
if (r)
return r;
}
rx = slsi_mlme_req_cfm(sdev, NULL, req, MLME_ADD_SCAN_CFM);
if (!rx)
return -EIO;
if (fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_add_scan_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code));
r = -EINVAL;
}
kfree_skb(rx);
return r;
}
int slsi_mlme_add_scan(
struct slsi_dev *sdev,
struct net_device *dev,
u16 scan_type,
u16 report_mode,
u32 n_ssids,
struct cfg80211_ssid *ssids,
u32 n_channels,
struct ieee80211_channel *channels[],
void *gscan,
const u8 *ies,
u16 ies_len,
bool wait_for_ind)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
size_t alloc_data_size = 0;
u32 i;
/* Scan Timing IE: default values */
u8 scan_timing_ie[] = {
0xdd, /* Element ID: Vendor Specific */
0x11, /* Length */
0x00, 0x16, 0x32, /* OUI: Samsung Electronics Co. */
0x01, /* OUI Type: Scan parameters */
0x01, /* OUI Subtype: Scan timing */
0x00, 0x00, 0x00, 0x00, /* Min_Period: filled later in the function */
0x00, 0x00, 0x00, 0x00, /* Max_Period: filled later in the function */
0x00, /* Exponent */
0x00, /* Step count */
0x00, 0x00 /* Skip first period: false */
};
if (slsi_is_test_mode_enabled()) {
SLSI_NET_WARN(dev, "not supported in WlanLite mode\n");
return -EOPNOTSUPP;
}
if (WARN_ON(!(dev->dev_addr)))
return -EINVAL;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_mutex));
SLSI_INFO(sdev, "scan started for id:0x%x, n_channels:%d, n_ssids:%d, scan_type:%d\n",
(ndev_vif->ifnum << 8 | SLSI_SCAN_HW_ID), n_channels, n_ssids, scan_type);
alloc_data_size += sizeof(scan_timing_ie) +
ies_len +
(SLSI_SCAN_PRIVATE_IE_CHANNEL_LIST_HEADER_LEN + (n_channels * SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE));
for (i = 0; i < n_ssids; i++)
alloc_data_size += 2 + ssids[i].ssid_len; /* 2: SSID EID + len */
req = fapi_alloc(mlme_add_scan_req, MLME_ADD_SCAN_REQ, 0, alloc_data_size);
if (!req)
return -ENOMEM;
if (!gscan) {
r = slsi_set_scan_params(
dev,
(ndev_vif->ifnum << 8 | SLSI_SCAN_HW_ID),
scan_type,
report_mode,
n_ssids,
ssids,
req);
if (r)
return r;
fapi_append_data(req, scan_timing_ie, sizeof(scan_timing_ie));
fapi_append_data(req, ies, ies_len);
if (n_channels) {
r = slsi_mlme_append_channel_list(sdev, dev, req, n_channels, channels, scan_type,
n_ssids == 0);
if (r)
return r;
}
}
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
else {
struct slsi_gscan_param *gscan_param = (struct slsi_gscan_param *)gscan;
r = slsi_set_scan_params(
dev,
gscan_param->bucket->scan_id,
scan_type,
report_mode,
n_ssids,
ssids,
req);
if (r)
return r;
SLSI_U32_TO_BUFF_LE((gscan_param->nl_bucket->period * 1000), &scan_timing_ie[7]);
if (gscan_param->nl_bucket->exponent) {
SLSI_U32_TO_BUFF_LE((gscan_param->nl_bucket->max_period * 1000), &scan_timing_ie[11]);
scan_timing_ie[15] = (u8)gscan_param->nl_bucket->exponent;
scan_timing_ie[16] = (u8)gscan_param->nl_bucket->step_count;
}
fapi_append_data(req, scan_timing_ie, sizeof(scan_timing_ie));
r = slsi_mlme_append_gscan_channel_list(sdev, dev, req, gscan_param->nl_bucket);
if (r)
return r;
}
#endif
if (wait_for_ind) {
/* Use the Global sig_wait not the Interface specific for Scan Req */
rx = slsi_mlme_req_cfm_ind(sdev, NULL, req, MLME_ADD_SCAN_CFM, MLME_SCAN_DONE_IND, slsi_scan_cfm_validate);
if (!rx)
return -EIO;
SLSI_NET_DBG3(dev, SLSI_MLME, "mlme_scan_done_ind()\n");
/* slsi_mlme_add_scan is a generic definition for multiple handlers
* Any added functionality, if not generic, should not be defined here.
* It should be a part of calling function.
*/
} else {
/* Use the Global sig_wait not the Interface specific for Scan Req */
rx = slsi_mlme_req_cfm(sdev, NULL, req, MLME_ADD_SCAN_CFM);
if (!rx)
return -EIO;
if (fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_add_scan_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code));
r = -EINVAL;
}
}
kfree_skb(rx);
return r;
}
int slsi_mlme_del_scan(struct slsi_dev *sdev, struct net_device *dev, u16 scan_id, bool scan_timed_out)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_WARN(dev, "not supported in WlanLite mode\n");
return -EOPNOTSUPP;
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_del_scan_req(scan_id:%d)\n", scan_id);
if ((scan_id & 0xFF) == SLSI_SCAN_HW_ID && ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req && !scan_timed_out)
cancel_delayed_work(&ndev_vif->scan_timeout_work);
req = fapi_alloc(mlme_del_scan_req, MLME_DEL_SCAN_REQ, 0, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_del_scan_req.scan_id, scan_id);
/* Use the Global sig_wait not the Interface specific for Scan Req */
cfm = slsi_mlme_req_cfm(sdev, NULL, req, MLME_DEL_SCAN_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_del_scan_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_del_scan_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_del_scan_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
static void slsi_ap_add_ext_capab_ie(struct sk_buff *req, struct netdev_vif *ndev_vif, const u8 *prev_ext)
{
u8 ext_capa_ie[SLSI_AP_EXT_CAPAB_IE_LEN_MAX];
int i;
int prev_len = 0;
ext_capa_ie[0] = WLAN_EID_EXT_CAPABILITY;
ext_capa_ie[1] = SLSI_AP_EXT_CAPAB_IE_LEN_MAX - 1 - 1;
if (prev_ext) {
prev_len = prev_ext[1];
for (i = 2; i < prev_len + 2; i++)
ext_capa_ie[i] = prev_ext[i];
}
for (i = prev_len + 2; i < SLSI_AP_EXT_CAPAB_IE_LEN_MAX; i++)
ext_capa_ie[i] = 0x00;
SLSI_DBG3(ndev_vif->sdev, SLSI_MLME, "New Ext capab Added\n");
/* For VHT, set the Operating Mode Notification field - Bit 62 (8th Octet) */
ext_capa_ie[9] |= 0x40;
fapi_append_data(req, &ext_capa_ie[0], SLSI_AP_EXT_CAPAB_IE_LEN_MAX);
}
#endif
static int slsi_prepare_country_ie(struct slsi_dev *sdev, u16 center_freq, u8 *country_ie, u8 **new_country_ie)
{
struct ieee80211_supported_band band;
struct ieee80211_reg_rule *rule;
struct ieee80211_channel *channels;
u8 *ie;
int offset = 0;
int i;
/* Select frequency band */
if (center_freq < 5180)
band = slsi_band_2ghz;
else
band = slsi_band_5ghz;
/* Allocate memory for the new country IE - EID(1) + Len(1) + CountryString(3) + ChannelInfo (n * 3) */
ie = kmalloc(5 + (band.n_channels * 3), GFP_KERNEL);
if (!ie) {
SLSI_ERR(sdev, "Failed to allocate memory\n");
return -ENOMEM;
}
/* Preapre the new country IE */
ie[offset++] = country_ie[0]; /* Element IE */
ie[offset++] = 0; /* IE Length - initialized at the end of this function */
ie[offset++] = sdev->device_config.domain_info.regdomain->alpha2[0]; /* Country code */
ie[offset++] = sdev->device_config.domain_info.regdomain->alpha2[1]; /* Country code */
ie[offset++] = country_ie[4]; /* CountryString: 3rd octet */
channels = band.channels;
for (i = 0; i < band.n_channels; i++, channels++) {
/* Get the regulatory rule for the channel */
rule = slsi_get_reg_rule(MHZ_TO_KHZ(channels->center_freq), &sdev->device_config.domain_info);
if (rule) {
ie[offset++] = channels->hw_value; /* Channel number */
ie[offset++] = 1; /* Number of channels */
ie[offset++] = MBM_TO_DBM(rule->power_rule.max_eirp); /* Max TX power */
}
}
ie[1] = offset - 2; /* Length of IE */
*new_country_ie = ie;
return 0;
}
int slsi_modify_ies(struct net_device *dev, u8 eid, u8 *ies, int ies_len, u8 ie_index, u8 ie_value)
{
u8 *ie;
SLSI_NET_DBG1(dev, SLSI_MLME, "eid: %d, ie_value = 0x%x\n", eid, ie_value);
ie = (u8 *)cfg80211_find_ie(eid, ies, ies_len);
if (ie) {
switch (eid) {
case WLAN_EID_HT_CAPABILITY:
case WLAN_EID_VHT_CAPABILITY:
ie[ie_index] |= ie_value;
break;
case WLAN_EID_DS_PARAMS:
case WLAN_EID_HT_OPERATION:
if (ie_index == 2)
ie[ie_index] = ie_value;
else
ie[ie_index] |= ie_value;
break;
default:
SLSI_NET_WARN(dev, "slsi_modify_ies: IE type mismatch : %d\n", eid);
return false;
}
return true;
}
SLSI_NET_WARN(dev, "slsi_modify_ies: IE not found : %d\n", eid);
return false;
}
static void slsi_mlme_start_prepare_ies(struct sk_buff *req, struct netdev_vif *ndev_vif, struct cfg80211_ap_settings *settings, const u8 *wpa_ie_pos, const u8 *wmm_ie_pos)
{
const u8 *wps_ie, *vht_capab_ie, *tail_pos = NULL, *ext_capab_ie;
size_t beacon_ie_len = 0, tail_length = 0;
u8 *country_ie;
const u8 *beacon_tail = settings->beacon.tail;
size_t beacon_tail_len = settings->beacon.tail_len;
/**
* Channel list of Country IE prepared by hostapd is wrong, so driver needs remove the existing country IE and prepare correct one.
* Hostapd adds country IE at the beginning of the tail, beacon_tail is moved to the next IE to avoid the default county IE.
*/
country_ie = (u8 *)cfg80211_find_ie(WLAN_EID_COUNTRY, beacon_tail, beacon_tail_len);
if (country_ie) {
u8 *new_country_ie = NULL;
SLSI_DBG3(ndev_vif->sdev, SLSI_MLME, "Country IE found, length = %d", country_ie[1]);
/* Prepare the new country IE */
if (slsi_prepare_country_ie(ndev_vif->sdev, ndev_vif->chan->center_freq, country_ie, &new_country_ie) != 0)
SLSI_ERR(ndev_vif->sdev, "Failed to prepare country IE");
/* Add the new country IE */
if (new_country_ie) {
/* new_country_ie[1] ontains the length of IE */
fapi_append_data(req, new_country_ie, (new_country_ie[1] + 2));
/* Free the memory allocated for the new country IE */
kfree(new_country_ie);
/* Remove the default country IE from the beacon_tail */
beacon_tail += (country_ie[1] + 2);
beacon_tail_len -= (country_ie[1] + 2);
}
}
/* Modify HT IE based on OBSS scan data */
if (ndev_vif->ap.non_ht_bss_present) {
u8 op_mode = 1;
SLSI_NET_DBG1(ndev_vif->wdev.netdev, SLSI_MLME, "Modify Operating mode of BSS in HT IE\n");
slsi_modify_ies(ndev_vif->wdev.netdev, WLAN_EID_HT_OPERATION, (u8 *)settings->beacon.tail, settings->beacon.tail_len, 4, op_mode);
ndev_vif->ap.non_ht_bss_present = false;
}
/* Vendor IEs are excluded from start_req. Currently WPA IE, WMM IE, WPS IE and P2P IE need to be excluded.
* From hostapd, order of IEs are - WPA, WMM, WPS and P2P
* Of these the WMM, WPS and P2P IE are usually at the end.
* Note: There can be "eid_p2p_manage" and "eid_hs20" after WPS and P2P IE. Both of these are currently not supported.
*/
/* Exclude WMM or WPS IE */
if (wmm_ie_pos) /* WMM IE is present. Remove from this position onwards, i.e. copy only till this data. WPS and P2P IE will also get removed. */
beacon_ie_len = wmm_ie_pos - beacon_tail;
else {
/* WMM IE is not present. Check for WPS IE (and thereby P2P IE) and exclude it */
wps_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPS, beacon_tail, beacon_tail_len);
if (wps_ie)
beacon_ie_len = wps_ie - beacon_tail;
else
beacon_ie_len = beacon_tail_len;
}
/* Exclude WPA IE if present */
if (wpa_ie_pos) {
size_t len_before, len;
len_before = wpa_ie_pos - beacon_tail;
fapi_append_data(req, beacon_tail, len_before);
len = len_before + ndev_vif->ap.wpa_ie_len;
if (beacon_ie_len > len) { /* More IEs to go */
tail_length = beacon_ie_len - len;
tail_pos = (beacon_tail + len);
} else /* No more IEs, don't add Ext Capab IE as no HT/VHT */
return;
} else {
tail_length = beacon_ie_len;
tail_pos = beacon_tail;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
/* Add Ext Capab IE only for VHT mode for now */
if (ndev_vif->chandef->width == NL80211_CHAN_WIDTH_80) {
/* Ext Capab should be before VHT IEs */
vht_capab_ie = (cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, tail_pos, tail_length));
ext_capab_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, tail_pos, tail_length);
while (tail_length > 2) {
if (tail_pos[0] == WLAN_EID_VHT_CAPABILITY)
slsi_ap_add_ext_capab_ie(req, ndev_vif, ext_capab_ie);
else if (tail_pos[0] != WLAN_EID_EXT_CAPABILITY && tail_pos[0] != WLAN_EID_VHT_OPERATION)
fapi_append_data(req, tail_pos, tail_pos[1] + 2);
tail_length -= tail_pos[1] + 2;
tail_pos += tail_pos[1] + 2;
}
if (!vht_capab_ie)
slsi_ap_add_ext_capab_ie(req, ndev_vif, ext_capab_ie);
} else {
fapi_append_data(req, tail_pos, tail_length);
}
#else
fapi_append_data(req, tail_pos, tail_length);
#endif
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
/*EID + LEN + CAPABILITIES + MCS */
/* 1+1+4+8 */
#define SLSI_VHT_CAPABILITIES_IE_LEN 14
/* EID + LEN + WIDTH + SEG0 + SEG1 + MCS */
/* 1+1+1+1+1+2 */
#define SLSI_VHT_OPERATION_IE_LEN 7
static int slsi_prepare_vht_ies(struct net_device *dev, u8 **vht_ie_capab, u8 **vht_ie_operation)
{
u32 capabs;
u16 mcs;
u8 *p_cap, *p_oper;
struct netdev_vif *ndev_vif = netdev_priv(dev);
*vht_ie_capab = kmalloc(SLSI_VHT_CAPABILITIES_IE_LEN, GFP_KERNEL);
if (!(*vht_ie_capab))
return -EINVAL;
*vht_ie_operation = kmalloc(SLSI_VHT_OPERATION_IE_LEN, GFP_KERNEL);
if (!(*vht_ie_operation)) {
kfree(*vht_ie_capab);
return -EINVAL;
}
p_cap = *vht_ie_capab;
p_oper = *vht_ie_operation;
*p_cap++ = WLAN_EID_VHT_CAPABILITY;
*p_cap++ = SLSI_VHT_CAPABILITIES_IE_LEN - 1 - 1;
capabs = cpu_to_le32(slsi_vht_cap.cap);
memcpy(p_cap, &capabs, sizeof(capabs));
p_cap += sizeof(capabs);
memcpy(p_cap, &slsi_vht_cap.vht_mcs, sizeof(slsi_vht_cap.vht_mcs));
*p_oper++ = WLAN_EID_VHT_OPERATION;
*p_oper++ = SLSI_VHT_OPERATION_IE_LEN - 1 - 1;
*p_oper++ = IEEE80211_VHT_CHANWIDTH_80MHZ;
*p_oper++ = ieee80211_frequency_to_channel(ndev_vif->chandef->center_freq1);
*p_oper++ = 0;
mcs = cpu_to_le16(0xfffc);
memcpy(p_oper, &mcs, sizeof(mcs));
return 0;
}
#endif
int slsi_mlme_start(struct slsi_dev *sdev, struct net_device *dev, u8 *bssid, struct cfg80211_ap_settings *settings, const u8 *wpa_ie_pos, const u8 *wmm_ie_pos, bool append_vht_ies)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
struct ieee80211_mgmt *mgmt;
int r = 0;
u8 *p;
enum nl80211_auth_type auth_type = settings->auth_type;
u16 beacon_ie_head_len;
u16 chan_info;
u16 fw_freq;
u16 vht_ies_len = 0;
u8 ext_capab_len = 0;
u32 channel_encode = 0;
const u8 *recv_vht_capab_ie, *recv_vht_operation_ie;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
u8 *vht_ie_capab, *vht_ie_operation;
#endif
SLSI_UNUSED_PARAMETER(bssid);
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_START.request\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
mgmt = (struct ieee80211_mgmt *)settings->beacon.head;
beacon_ie_head_len = settings->beacon.head_len - ((u8 *)mgmt->u.beacon.variable - (u8 *)mgmt);
/* For port enabling, save the privacy bit used in assoc response or beacon */
ndev_vif->ap.privacy = (mgmt->u.beacon.capab_info & WLAN_CAPABILITY_PRIVACY);
ndev_vif->ap.qos_enabled = (mgmt->u.beacon.capab_info & WLAN_CAPABILITY_QOS);
switch (auth_type) {
case NL80211_AUTHTYPE_OPEN_SYSTEM:
case NL80211_AUTHTYPE_SHARED_KEY:
break;
case NL80211_AUTHTYPE_AUTOMATIC:
auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
if (settings->privacy && settings->crypto.cipher_group == 0)
auth_type = NL80211_AUTHTYPE_SHARED_KEY;
break;
default:
SLSI_NET_ERR(dev, "Unsupported auth_type: %d\n", auth_type);
return -EOPNOTSUPP;
}
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_start_req(vif:%u, bssid:%pM, ssid:%.*s, hidden:%d)\n", ndev_vif->ifnum, bssid, (int)settings->ssid_len, settings->ssid, settings->hidden_ssid);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
if (append_vht_ies) {
vht_ies_len = SLSI_VHT_CAPABILITIES_IE_LEN + SLSI_VHT_OPERATION_IE_LEN;
recv_vht_capab_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, settings->beacon.tail,
settings->beacon.tail_len);
if (recv_vht_capab_ie)
vht_ies_len -= (recv_vht_capab_ie[1] + 2);
recv_vht_operation_ie = cfg80211_find_ie(WLAN_EID_VHT_OPERATION, settings->beacon.tail,
settings->beacon.tail_len);
if (recv_vht_operation_ie)
vht_ies_len -= (recv_vht_operation_ie[1] + 2);
}
if (ndev_vif->chandef->width == NL80211_CHAN_WIDTH_80) {
/* Ext Capab are not advertised by driver and so the IE would not be sent by hostapd.
* Frame the IE in driver and set the required bit(s).
*/
SLSI_NET_DBG1(dev, SLSI_MLME, "VHT - Ext Capab IE to be included\n");
ext_capab_len = SLSI_AP_EXT_CAPAB_IE_LEN_MAX;
}
#endif
if (settings->hidden_ssid == 1)
req = fapi_alloc(mlme_start_req, MLME_START_REQ, ndev_vif->ifnum, settings->ssid_len + beacon_ie_head_len + settings->beacon.tail_len + vht_ies_len + ext_capab_len);
else
req = fapi_alloc(mlme_start_req, MLME_START_REQ, ndev_vif->ifnum, beacon_ie_head_len + settings->beacon.tail_len + vht_ies_len + ext_capab_len);
if (!req)
return -ENOMEM;
fapi_set_memcpy(req, u.mlme_start_req.bssid, dev->dev_addr);
fapi_set_u16(req, u.mlme_start_req.beacon_period, settings->beacon_interval);
fapi_set_u16(req, u.mlme_start_req.dtim_period, settings->dtim_period);
fapi_set_u16(req, u.mlme_start_req.capability_information, le16_to_cpu(mgmt->u.beacon.capab_info));
fapi_set_u16(req, u.mlme_start_req.authentication_type, auth_type);
fapi_set_u16(req, u.mlme_start_req.hidden_ssid, settings->hidden_ssid < 3 ? settings->hidden_ssid : NL80211_HIDDEN_SSID_ZERO_LEN);
channel_encode = ndev_vif->acs == true ? 0 : 1;
fapi_set_u32(req, u.mlme_start_req.spare_1, channel_encode);
fw_freq = ndev_vif->chan->center_freq;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
chan_info = slsi_get_chann_info(sdev, ndev_vif->chandef);
#else
chan_info = slsi_get_chann_info(sdev, ndev_vif->channel_type);
#endif
if ((chan_info & 20) != 20)
fw_freq = slsi_get_center_freq1(sdev, chan_info, fw_freq);
fapi_set_u16(req, u.mlme_start_req.channel_frequency, (2 * fw_freq));
fapi_set_u16(req, u.mlme_start_req.channel_information, chan_info);
ndev_vif->ap.channel_freq = fw_freq;
/* Addition of SSID IE in mlme_start_req for hiddenSSID case */
if (settings->hidden_ssid != 0) {
p = fapi_append_data(req, NULL, 2 + settings->ssid_len);
if (!p) {
kfree_skb(req);
return -EINVAL;
}
*p++ = WLAN_EID_SSID;
*p++ = settings->ssid_len;
memcpy(p, settings->ssid, settings->ssid_len);
}
if (beacon_ie_head_len && settings->hidden_ssid == 0)
fapi_append_data(req, mgmt->u.beacon.variable, beacon_ie_head_len);
else if (beacon_ie_head_len && settings->hidden_ssid == 1)
fapi_append_data(req, mgmt->u.beacon.variable + 2, beacon_ie_head_len - 2);
else if (beacon_ie_head_len && settings->hidden_ssid == 2)
fapi_append_data(req, mgmt->u.beacon.variable + 2 + settings->ssid_len, beacon_ie_head_len - (2 + settings->ssid_len));
if (settings->beacon.tail_len)
slsi_mlme_start_prepare_ies(req, ndev_vif, settings, wpa_ie_pos, wmm_ie_pos);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
if ((append_vht_ies) && !slsi_prepare_vht_ies(dev, &vht_ie_capab, &vht_ie_operation)) {
fapi_append_data(req, vht_ie_capab, SLSI_VHT_CAPABILITIES_IE_LEN);
fapi_append_data(req, vht_ie_operation, SLSI_VHT_OPERATION_IE_LEN);
kfree(vht_ie_capab);
kfree(vht_ie_operation);
}
#endif
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_START_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_start_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_start_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_start_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
static const u8 *slsi_mlme_connect_get_sec_ie(struct cfg80211_connect_params *sme, int *sec_ie_len)
{
u16 version;
const u8 *ptr = NULL;
if (sme->crypto.wpa_versions == 0) {
/* WAPI */
ptr = cfg80211_find_ie(SLSI_WLAN_EID_WAPI, sme->ie, sme->ie_len);
if (ptr) {
version = ptr[3] << 8 | ptr[2];
if (version != 1) {
SLSI_ERR_NODEV("Unexpected version (%d) in WAPI ie\n", version);
return NULL;
}
}
} else if (sme->crypto.wpa_versions == 2) {
/* RSN */
ptr = cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len);
if (ptr) {
/* version index is 2 for RSN */
version = ptr[2 + 1] << 8 | ptr[2];
if (version != 1) {
SLSI_ERR_NODEV("Unexpected version (%d) in rsn ie\n", version);
return NULL;
}
}
}
*sec_ie_len = ptr ? ptr[1] + 2 : 0;
return ptr;
}
/* If is_copy is true copy the required IEs from connect_ie to ie_dest. else
* calculate the required ie length
*/
static int slsi_mlme_connect_info_elems_ie_prep(struct slsi_dev *sdev, const u8 *connect_ie,
const size_t connect_ie_len, bool is_copy, u8 *ie_dest, int ie_dest_len)
{
const u8 *ie_pos = NULL;
int info_elem_length = 0;
u16 curr_ie_len;
int i = 0;
u8 ie_eid[] = {SLSI_WLAN_EID_INTERWORKING,
SLSI_WLAN_EID_EXTENSION,
WLAN_EID_VENDOR_SPECIFIC}; /*Vendor IE has to be the last element */
if (is_copy && (!ie_dest || ie_dest_len == 0))
return -EINVAL;
for (i = 0; i < sizeof(ie_eid) / sizeof(u8); i++) {
ie_pos = cfg80211_find_ie(ie_eid[i], connect_ie, connect_ie_len);
if (ie_pos) {
if (ie_eid[i] == WLAN_EID_VENDOR_SPECIFIC) /*Vendor IE will be the last element */
curr_ie_len = connect_ie_len - (ie_pos - connect_ie);
else
curr_ie_len = *(ie_pos + 1) + 2;
SLSI_DBG2(sdev, SLSI_MLME, "IE[%d] is present having length:%d\n", ie_eid[i], curr_ie_len);
if (is_copy) {
if (ie_dest_len >= curr_ie_len) {
memcpy(ie_dest, ie_pos, curr_ie_len);
ie_dest += curr_ie_len;
/* free space avail in ie_dest for next ie*/
ie_dest_len -= curr_ie_len;
} else {
SLSI_ERR_NODEV("IE[%d] extract error (ie_copy_l:%d, c_ie_l:%d):\n", ie_eid[i],
ie_dest_len, curr_ie_len);
return -EINVAL;
}
} else {
info_elem_length += curr_ie_len;
}
}
}
if (sdev->device_config.qos_info != -1) {
if (is_copy) {
if (ie_dest_len >= 9) {
int pos = 0;
ie_dest[pos++] = SLSI_WLAN_EID_VENDOR_SPECIFIC;
ie_dest[pos++] = 0x07;
ie_dest[pos++] = 0x00;
ie_dest[pos++] = 0x50;
ie_dest[pos++] = 0xf2;
ie_dest[pos++] = WLAN_OUI_TYPE_MICROSOFT_WMM;
ie_dest[pos++] = WMM_OUI_SUBTYPE_INFORMATION_ELEMENT;
ie_dest[pos++] = WMM_VERSION;
ie_dest[pos++] = sdev->device_config.qos_info & 0x0F;
ie_dest += pos;
ie_dest_len -= pos;
} else {
SLSI_ERR_NODEV("Required 9bytes but left:%d\n", ie_dest_len);
return -EINVAL;
}
sdev->device_config.qos_info = -1;
} else {
info_elem_length += 9;
}
}
return info_elem_length;
}
static int slsi_mlme_connect_info_elements(struct slsi_dev *sdev, struct net_device *dev, struct cfg80211_connect_params *sme)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
int info_elem_length = 0;
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
u8 *p;
info_elem_length = slsi_mlme_connect_info_elems_ie_prep(sdev, sme->ie, sme->ie_len, false, NULL, 0);
/* NO IE required in MLME-ADD-INFO-ELEMENTS */
if (info_elem_length <= 0)
return info_elem_length;
req = fapi_alloc(mlme_add_info_elements_req, MLME_ADD_INFO_ELEMENTS_REQ,
ndev_vif->ifnum, info_elem_length);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_add_info_elements_req.purpose, FAPI_PURPOSE_ASSOCIATION_REQUEST);
p = fapi_append_data(req, NULL, info_elem_length);
if (!p) {
kfree_skb(req);
return -EINVAL;
}
(void)slsi_mlme_connect_info_elems_ie_prep(sdev, sme->ie, sme->ie_len, true, p, info_elem_length);
/* backup ies */
if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) {
if (ndev_vif->sta.assoc_req_add_info_elem_len)
kfree(ndev_vif->sta.assoc_req_add_info_elem);
ndev_vif->sta.assoc_req_add_info_elem_len = 0;
ndev_vif->sta.assoc_req_add_info_elem = kmalloc(info_elem_length, GFP_KERNEL);
if (ndev_vif->sta.assoc_req_add_info_elem) {
memcpy(ndev_vif->sta.assoc_req_add_info_elem, p, info_elem_length);
ndev_vif->sta.assoc_req_add_info_elem_len = info_elem_length;
} else {
SLSI_WARN(sdev, "No mem for ndev_vif->sta.assoc_req_add_info_elem size %d\n", info_elem_length);
}
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_add_info_elements_req(vif:%u)\n", ndev_vif->ifnum);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_ADD_INFO_ELEMENTS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_add_info_elements_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_add_info_elements_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_connect_cfm.result_code));
r = -EINVAL;
}
if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPS, sme->ie, sme->ie_len))
ndev_vif->sta.is_wps = true;
kfree_skb(cfm);
return r;
}
int slsi_mlme_connect(struct slsi_dev *sdev, struct net_device *dev, struct cfg80211_connect_params *sme, struct ieee80211_channel *channel, const u8 *bssid)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
u8 *p;
enum nl80211_auth_type auth_type = sme->auth_type;
u8 mac_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
struct key_params slsi_key;
const u8 *sec_ie = NULL;
int sec_ie_len = 0;
memset(&slsi_key, 0, sizeof(slsi_key));
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_CONNECT.request\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (WARN(!bssid, "BSSID is Null"))
return -EINVAL;
if (WARN(!sme->ssid_len, "SSID is Null"))
return -EINVAL;
switch (auth_type) {
case NL80211_AUTHTYPE_OPEN_SYSTEM:
case NL80211_AUTHTYPE_SHARED_KEY:
break;
case NL80211_AUTHTYPE_SAE:
auth_type = NL80211_AUTHTYPE_NETWORK_EAP;
break;
case NL80211_AUTHTYPE_AUTOMATIC:
/* In case of WEP, need to try both open and shared.
* FW does this if auth is shared_key. So set it to shared.
*/
if (sme->privacy &&
(sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40 ||
sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104))
auth_type = NL80211_AUTHTYPE_SHARED_KEY;
else
auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
break;
default:
SLSI_NET_ERR(dev, "Unsupported auth_type: %d\n", auth_type);
return -EOPNOTSUPP;
}
/* We save the WEP key for shared authentication. */
if ((auth_type == NL80211_AUTHTYPE_SHARED_KEY) &&
((sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
(sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104)) &&
(ndev_vif->vif_type == FAPI_VIFTYPE_STATION)) {
SLSI_NET_DBG3(dev, SLSI_MLME, "key len (%d)\n", sme->key_len);
slsi_key.key = (u8 *)sme->key;
if (!slsi_key.key)
return -EINVAL;
slsi_key.key_len = sme->key_len;
slsi_key.seq_len = 0;
if (sme->crypto.n_ciphers_pairwise)
slsi_key.cipher = sme->crypto.ciphers_pairwise[0];
r = slsi_mlme_set_key(sdev, dev, sme->key_idx, FAPI_KEYTYPE_WEP, mac_addr, &slsi_key);
if (r != 0) {
SLSI_NET_ERR(dev, "Error Setting Shared key (%d)", r);
return r;
}
}
/*Do not check sme->ie as wpa_supplicant sends some invalid value in it even if ie_len is zero .*/
if (sme->ie_len) {
r = slsi_mlme_connect_info_elements(sdev, dev, sme);
if (r)
return r;
sec_ie = slsi_mlme_connect_get_sec_ie(sme, &sec_ie_len);
if (sec_ie_len < 0) {
SLSI_NET_ERR(dev, "ERROR preparing Security IEs\n");
return sec_ie_len;
}
}
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_connect_req(vif:%u, bssid:%pM, ssid:%.*s)\n", ndev_vif->ifnum, bssid, (int)sme->ssid_len, sme->ssid);
req = fapi_alloc(mlme_connect_req, MLME_CONNECT_REQ, ndev_vif->ifnum,
2 + sme->ssid_len + /*SSID IE*/
sec_ie_len); /*WPA/WPA2/WAPI/OSEN*/
if (!req)
return -ENOMEM;
fapi_set_memcpy(req, u.mlme_connect_req.bssid, bssid);
fapi_set_u16(req, u.mlme_connect_req.authentication_type, auth_type);
/* Need to double the freq for the firmware */
fapi_set_u16(req, u.mlme_connect_req.channel_frequency, (2 * channel->center_freq));
p = fapi_append_data(req, NULL, 2 + sme->ssid_len + sec_ie_len);
if (!p) {
kfree_skb(req);
return -EINVAL;
}
*p++ = WLAN_EID_SSID;
*p++ = sme->ssid_len;
memcpy(p, sme->ssid, sme->ssid_len);
p += sme->ssid_len;
if (sec_ie_len)
memcpy(p, sec_ie, sec_ie_len);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_CONNECT_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_connect_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_connect_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_connect_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
void slsi_mlme_connect_resp(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_CONNECT_RESP\n");
return;
}
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_connect_resp(vif:%u)\n", ndev_vif->ifnum);
req = fapi_alloc(mlme_connect_res, MLME_CONNECT_RES, ndev_vif->ifnum, 0);
if (!req)
return;
cfm = slsi_mlme_req_no_cfm(sdev, dev, req);
WARN_ON(cfm);
}
void slsi_mlme_connected_resp(struct slsi_dev *sdev, struct net_device *dev, u16 peer_index)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_CONNECT_RESP\n");
return;
}
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_connected_resp(vif:%u, peer_index:%d)\n", ndev_vif->ifnum, peer_index);
req = fapi_alloc(mlme_connected_res, MLME_CONNECTED_RES, ndev_vif->ifnum, 0);
if (!req) {
SLSI_NET_ERR(dev, "mlme-connected-response :: memory allocation failed\n");
return;
}
fapi_set_u16(req, u.mlme_connected_res.association_identifier, peer_index);
slsi_mlme_req_no_cfm(sdev, dev, req);
}
void slsi_mlme_roamed_resp(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_ROAMED_RESP\n");
return;
}
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_roamed_resp\n");
req = fapi_alloc(mlme_roamed_res, MLME_ROAMED_RES, ndev_vif->ifnum, 0);
if (!req)
return;
cfm = slsi_mlme_req_no_cfm(sdev, dev, req);
WARN_ON(cfm);
}
/* Null check for cfm done in caller function */
bool slsi_disconnect_cfm_validate(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *cfm)
{
int result = fapi_get_u16(cfm, u.mlme_disconnect_cfm.result_code);
bool r = false;
SLSI_UNUSED_PARAMETER(sdev);
if (WARN_ON(!dev))
goto exit;
if (result == FAPI_RESULTCODE_SUCCESS)
r = true;
/* Not present code would mean peer is already disconnected and hence no ind (could be race scenario), don't log as error */
else if (result != FAPI_RESULTCODE_NOT_PRESENT)
SLSI_NET_ERR(dev, "mlme_disconnect_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_disconnect_cfm.result_code));
exit:
kfree_skb(cfm);
return r;
}
/* Null check for cfm done in caller function */
bool slsi_roaming_channel_list_cfm_validate(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *cfm)
{
int result = fapi_get_u16(cfm, u.mlme_roaming_channel_list_cfm.result_code);
bool r = false;
SLSI_UNUSED_PARAMETER(sdev);
if (WARN_ON(!dev))
goto exit;
if (result == FAPI_RESULTCODE_SUCCESS)
r = true;
/* Not present code would mean peer is already disconnected and hence no ind (could be race scenario), don't log as error */
else if (result != FAPI_RESULTCODE_NOT_PRESENT)
SLSI_NET_ERR(dev, "mlme_roaming_channel_list_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_roaming_channel_list_cfm.result_code));
exit:
kfree_skb(cfm);
return r;
}
struct sk_buff *slsi_mlme_roaming_channel_list_req(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_DISCONNECT.request\n");
return NULL;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_roaming_channel_list_req(vif:%u)\n", ndev_vif->ifnum);
req = fapi_alloc(mlme_roaming_channel_list_req, MLME_ROAMING_CHANNEL_LIST_REQ, ndev_vif->ifnum, 0);
if (!req) {
SLSI_NET_ERR(dev, "fapi_alloc failed!\n");
return NULL;
}
rx = slsi_mlme_req_cfm_ind(sdev, dev, req, MLME_ROAMING_CHANNEL_LIST_CFM, MLME_ROAMING_CHANNEL_LIST_IND, slsi_roaming_channel_list_cfm_validate);
if (!rx) {
SLSI_NET_ERR(dev, "mlme_roaming_channel_list_cfm() ERROR\n");
return NULL;
}
return rx;
}
int slsi_mlme_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *mac, u16 reason_code, bool wait_ind)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_DISCONNECT.request\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_disconnect_req(vif:%u, bssid:%pM, reason:%d)\n", ndev_vif->ifnum, mac, reason_code);
req = fapi_alloc(mlme_disconnect_req, MLME_DISCONNECT_REQ, ndev_vif->ifnum,
ndev_vif->sta.vendor_disconnect_ies_len);
if (!req)
return -ENOMEM;
SLSI_INFO(sdev, "Send DEAUTH, reason = %d\n", reason_code);
fapi_set_u16(req, u.mlme_disconnect_req.reason_code, reason_code);
if (ndev_vif->sta.vendor_disconnect_ies_len > 0)
fapi_append_data(req, ndev_vif->sta.vendor_disconnect_ies, ndev_vif->sta.vendor_disconnect_ies_len);
kfree(ndev_vif->sta.vendor_disconnect_ies);
ndev_vif->sta.vendor_disconnect_ies = NULL;
ndev_vif->sta.vendor_disconnect_ies_len = 0;
if (mac)
fapi_set_memcpy(req, u.mlme_disconnect_req.peer_sta_address, mac);
else
fapi_set_memset(req, u.mlme_disconnect_req.peer_sta_address, 0);
if (wait_ind) {
rx = slsi_mlme_req_cfm_ind(sdev, dev, req, MLME_DISCONNECT_CFM, MLME_DISCONNECT_IND, slsi_disconnect_cfm_validate);
if (!rx) {
SLSI_NET_ERR(dev, "mlme_disconnect_cfm() ERROR\n");
r = -EINVAL;
}
} else {
rx = slsi_mlme_req_cfm(sdev, dev, req, MLME_DISCONNECT_CFM);
if (rx) {
if (fapi_get_u16(rx, u.mlme_disconnect_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_disconnect_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_disconnect_cfm.result_code));
r = -EINVAL;
}
} else {
r = -EIO;
}
}
kfree_skb(rx);
return r;
}
int slsi_mlme_set_key(struct slsi_dev *sdev, struct net_device *dev, u16 key_id, u16 key_type, const u8 *address, struct key_params *key)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_SETKEYS.request\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_setkeys_req(key_id:%d, key_type:%d, address:%pM, length:%d, cipher:0x%.8X)\n", key_id, key_type, address, key->key_len, key->cipher);
req = fapi_alloc(mlme_setkeys_req, MLME_SETKEYS_REQ, ndev_vif->ifnum, key->key_len + 1); /* + 1 for the wep key index */
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_setkeys_req.length, key->key_len * 8);
fapi_set_u16(req, u.mlme_setkeys_req.key_id, key_id);
fapi_set_u16(req, u.mlme_setkeys_req.key_type, key_type);
fapi_set_memcpy(req, u.mlme_setkeys_req.address, address);
fapi_set_memset(req, u.mlme_setkeys_req.sequence_number, 0x00);
if (key->seq_len && key->seq) {
int i;
u16 temp_seq;
SLSI_NET_DBG3(dev, SLSI_MLME, "mlme_setkeys_req(key->seq_len:%d)\n", key->seq_len);
/* Sequence would be in little endian format
* If sequence is say key->seq is
* 04 03 02 01 00 00 00 00, it would be encoded as :
* 0x0304 0x0102 0x0000 0x0000 for firmware
*/
for (i = 0; i < key->seq_len; i += 2) {
temp_seq = (u16)(key->seq[i + 1] << 8) | (u16)(key->seq[i]);
fapi_set_u16(req, u.mlme_setkeys_req.sequence_number[i / 2], temp_seq);
}
}
fapi_set_u32(req, u.mlme_setkeys_req.cipher_suite_selector, key->cipher);
if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) {
u8 wep_key_id = (u8)key_id;
if (key_id > 3)
SLSI_NET_WARN(dev, "Key ID is greater than 3");
/* Incase of WEP key index is appended before key.
* So increment length by one
*/
fapi_set_u16(req, u.mlme_setkeys_req.length, (key->key_len + 1) * 8);
fapi_append_data(req, &wep_key_id, 1);
}
fapi_append_data(req, key->key, key->key_len);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SETKEYS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_setkeys_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_setkeys_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_setkeys_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_get_key(struct slsi_dev *sdev, struct net_device *dev, u16 key_id, u16 key_type, u8 *seq, int *seq_len)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_get_key_sequence_req(key_id:%d, key_type:%d)\n", key_id, key_type);
req = fapi_alloc(mlme_get_key_sequence_req, MLME_GET_KEY_SEQUENCE_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_get_key_sequence_req.key_id, key_id);
fapi_set_u16(req, u.mlme_get_key_sequence_req.key_type, key_type);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_GET_KEY_SEQUENCE_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_get_key_sequence_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_get_key_sequence_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_get_key_sequence_cfm.result_code));
r = -ENOENT;
} else {
int i;
u16 temp_seq;
/* For WPA2 Key RSC - 8 octets. For WPAI, it would be 16 octets (code would need to be updated)
* Length is not available in cfm but even if max length 8 is assigned, it should be ok as other octets
* would be padded with 0s
*/
*seq_len = 8;
/* Sequence from firmware is of a[8] type u16 (16 octets) and only 8 octets are required for WPA/WPA2.
* If sequence is say 0x01 0x02 0x03 0x04 with 0x01 as MSB and 0x04 as LSB then
* it would be encoded as: 0x0304 0x0102 by firmware.
* Sequence is expected to be returned in little endian
*/
for (i = 0; i < *seq_len / 2; i++) {
temp_seq = fapi_get_u16(cfm, u.mlme_get_key_sequence_cfm.sequence_number[i]);
*seq = (u8)(temp_seq & 0xFF);
*(seq + 1) = (u8)((temp_seq >> 8) & 0xFF);
seq += 2;
}
}
kfree_skb(cfm);
return r;
}
#ifdef CONFIG_SCSC_WLAN_MAX_LINK_SPEED
void slsi_calc_max_data_rate(struct net_device *dev, u8 bandwidth, u8 antenna_mode)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
u8 bandwidth_index, sta_mode, mcs_index;
if (bandwidth == 0 || antenna_mode > 3) {
SLSI_NET_ERR(dev, "MIB value is wrong.");
return;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
/* Bandwidth (BW): 0x0= 20 MHz, 0x1= 40 MHz, 0x2= 80 MHz, 0x3= 160/ 80+80 MHz. 0x3 is not supported */
bandwidth_index = bandwidth / 40;
sta_mode = slsi_sta_ieee80211_mode(dev, ndev_vif->sta.sta_bss->channel->center_freq);
if (sta_mode == SLSI_80211_MODE_11B) {
ndev_vif->sta.max_rate_mbps = 11;
} else if (sta_mode == SLSI_80211_MODE_11G || sta_mode == SLSI_80211_MODE_11A) {
ndev_vif->sta.max_rate_mbps = 54;
} else if (sta_mode == SLSI_80211_MODE_11N) { /* max mcs index = 7 */
ndev_vif->sta.max_rate_mbps = (unsigned long)(slsi_rates_table[bandwidth_index][1][7] * (antenna_mode + 1)) / 10;
} else if (sta_mode == SLSI_80211_MODE_11AC) {
if (bandwidth_index == 0)
mcs_index = 8;
else
mcs_index = 9;
ndev_vif->sta.max_rate_mbps = (unsigned long)(slsi_rates_table[bandwidth_index][1][mcs_index] * (antenna_mode + 1)) / 10;
}
}
#endif
void slsi_decode_fw_rate(u32 fw_rate, struct rate_info *rate, unsigned long *data_rate_mbps)
{
const int fw_rate_idx_to_80211_rate[] = { 0, 10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 };
if (rate) {
rate->flags = 0;
rate->legacy = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
rate->bw = 0;
#endif
}
if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_NON_HT_SELECTED) {
u16 fw_rate_idx = fw_rate & SLSI_FW_API_RATE_INDEX_FIELD;
if (fw_rate > 0 && fw_rate_idx < ARRAY_SIZE(fw_rate_idx_to_80211_rate)) {
if (rate)
rate->legacy = fw_rate_idx_to_80211_rate[fw_rate_idx];
if (data_rate_mbps)
*data_rate_mbps = (unsigned long)fw_rate_idx_to_80211_rate[fw_rate_idx] / 10;
}
} else if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_HT_SELECTED) {
u8 mcs_idx = SLSI_FW_API_RATE_HT_MCS_FIELD & fw_rate;
u8 nss = ((SLSI_FW_API_RATE_HT_NSS_FIELD & fw_rate) >> 6) + 1;
if (rate) {
rate->flags |= RATE_INFO_FLAGS_MCS;
rate->mcs = mcs_idx;
if ((fw_rate & SLSI_FW_API_RATE_BW_FIELD) == SLSI_FW_API_RATE_BW_40MHZ)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
rate->bw |= RATE_INFO_BW_40;
#else
rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
if (fw_rate & SLSI_FW_API_RATE_SGI)
rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
}
if (data_rate_mbps) {
int chan_bw_idx;
int gi_idx;
chan_bw_idx = (fw_rate & SLSI_FW_API_RATE_BW_FIELD) >> 9;
gi_idx = ((fw_rate & SLSI_FW_API_RATE_SGI) == SLSI_FW_API_RATE_SGI) ? 1 : 0;
/* nss will be 1 when mcs_idx <= 7 or mcs == 32 */
if (chan_bw_idx < 2) {
if (mcs_idx <= 7) {
*data_rate_mbps = (unsigned long)slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx] / 10;
} else if (mcs_idx <= 15) {
*data_rate_mbps = (unsigned long)(nss * slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx - 8]) / 10;
} else if (mcs_idx == 32 && chan_bw_idx == 1) {
/* TODO: Fix this : unsigned long will not hold decimal values */
if (gi_idx == 1)
*data_rate_mbps = (unsigned long) 6.7;
else
*data_rate_mbps = 6;
}
} else {
SLSI_WARN_NODEV("FW DATA RATE decode error fw_rate:%x, bw:%x, mcs_idx:%x, nss : %d\n",
fw_rate, chan_bw_idx, mcs_idx, nss);
}
}
} else if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_VHT_SELECTED) {
int chan_bw_idx;
int gi_idx;
int mcs_idx;
u8 nss;
/* report vht rate in legacy units and not as mcs index. reason: upper layers may still be not
* updated with vht msc table.
*/
chan_bw_idx = (fw_rate & SLSI_FW_API_RATE_BW_FIELD) >> 9;
gi_idx = ((fw_rate & SLSI_FW_API_RATE_SGI) == SLSI_FW_API_RATE_SGI) ? 1 : 0;
/* Calculate NSS --> bits 6 to 4*/
nss = ((SLSI_FW_API_RATE_VHT_NSS_FIELD & fw_rate) >> 4) + 1;
mcs_idx = SLSI_FW_API_RATE_VHT_MCS_FIELD & fw_rate;
/* Bandwidth (BW): 0x0= 20 MHz, 0x1= 40 MHz, 0x2= 80 MHz, 0x3= 160/ 80+80 MHz. 0x3 is not supported */
if ((chan_bw_idx <= 2) && (mcs_idx <= 9)) {
if (rate)
rate->legacy = nss * slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx];
if (data_rate_mbps)
*data_rate_mbps = (unsigned long)(nss * slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx]) / 10;
} else {
SLSI_DBG1_NODEV(SLSI_MLME, "FW DATA RATE decode error fw_rate:%x, bw:%x, mcs_idx:%x,nss : %d\n\n",
fw_rate, chan_bw_idx, mcs_idx, nss);
}
} else if ((fw_rate & SLSI_FW_API_RATE_HE_SELECTED) == SLSI_FW_API_RATE_HE_SELECTED) {
int chan_bw_idx;
int gi_idx;
int mcs_idx;
u8 nss;
/* report vht rate in legacy units and not as mcs index. reason: upper layers may still be not
* updated with vht msc table.
*/
chan_bw_idx = (fw_rate & SLSI_FW_API_RATE_BW_FIELD) >> 9;
gi_idx = SLSI_FW_API_GET_11AX_GI(fw_rate);
/* Calculate NSS --> bits 6 to 4*/
nss = ((SLSI_FW_API_RATE_VHT_NSS_FIELD & fw_rate) >> 4) + 1;
mcs_idx = SLSI_FW_API_RATE_VHT_MCS_FIELD & fw_rate;
/* Bandwidth (BW): 0x0= 20 MHz, 0x1= 40 MHz, 0x2= 80 MHz, 0x3= 160/ 80+80 MHz. 0x3 is not supported */
if ((chan_bw_idx <= 3) && (mcs_idx < 12)) {
if (rate)
rate->legacy = nss * (slsi_he_rates_table_2x2[chan_bw_idx][mcs_idx][gi_idx] / 2) * 10;
if (data_rate_mbps)
*data_rate_mbps = (unsigned long)nss * (slsi_he_rates_table_2x2[chan_bw_idx][mcs_idx][gi_idx] / 2);
} else {
SLSI_DBG1_NODEV(SLSI_MLME, "FW DATA RATE decode error fw_rate:%x, bw:%x, mcs_idx:%x,nss : %d\n\n",
fw_rate, chan_bw_idx, mcs_idx, nss);
}
}
}
int slsi_mlme_get_sinfo_mib(struct slsi_dev *sdev, struct net_device *dev,
struct slsi_peer *peer)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_mib_data mibreq = { 0, NULL };
struct slsi_mib_data mibrsp = { 0, NULL };
struct slsi_mib_value *values = NULL;
#ifdef CONFIG_SCSC_WLAN_MAX_LINK_SPEED
u8 bandwidth = 0, antenna_mode = 4;
#endif
int data_length = 0;
int r = 0;
int mib_index = 0;
static const struct slsi_mib_get_entry get_values[] = {
{ SLSI_PSID_UNIFI_TX_DATA_RATE, { 0, 0 } }, /* to get STATION_INFO_TX_BITRATE*/
{ SLSI_PSID_UNIFI_RX_DATA_RATE, { 0, 0 } }, /* to get STATION_INFO_RX_BITRATE*/
{ SLSI_PSID_UNIFI_RSSI, { 0, 0 } }, /* to get STATION_INFO_SIGNAL_AVG*/
{ SLSI_PSID_UNIFI_THROUGHPUT_DEBUG, { 3, 0 } }, /* bad_fcs_count*/
{ SLSI_PSID_UNIFI_THROUGHPUT_DEBUG, { 25, 0 } }, /* mac_bad_sig_count*/
{ SLSI_PSID_UNIFI_THROUGHPUT_DEBUG, { 30, 0 } }, /* rx_error_count*/
{ SLSI_PSID_UNIFI_FRAME_TX_COUNTERS, { 1, 0 } }, /*tx good count*/
{ SLSI_PSID_UNIFI_FRAME_TX_COUNTERS, { 2, 0 } }, /*tx bad count*/
{ SLSI_PSID_UNIFI_FRAME_RX_COUNTERS, { 1, 0 } }, /*rx good count*/
#ifdef CONFIG_SCSC_ENHANCED_PACKET_STATS
{ SLSI_PSID_UNIFI_FRAME_TX_COUNTERS, { 3, 0 } }, /*tx retry count*/
#endif
#ifdef CONFIG_SCSC_WLAN_MAX_LINK_SPEED
{ SLSI_PSID_UNIFI_CURRENT_BSS_BANDWIDTH, { 0, 0 } }, /* bss bandwidth */
{ SLSI_PSID_UNIFI_STA_VIF_LINK_NSS, { 0, 0 } } /* current nss */
#endif
};
int rx_counter = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (!peer) {
SLSI_WARN(sdev, "Peer Not available\n");
return -EINVAL;
}
/*check if function is called within given period*/
if (__ratelimit(&peer->sinfo_mib_get_rs))
return 0;
r = slsi_mib_encode_get_list(&mibreq, (sizeof(get_values) / sizeof(struct slsi_mib_get_entry)),
get_values);
if (r != SLSI_MIB_STATUS_SUCCESS)
return -ENOMEM;
/* Fixed fields len (5) : 2 bytes(PSID) + 2 bytes (Len) + 1 byte (VLDATA header ) [10 for 2 PSIDs]
* Data : 3*2 bytes for SLSI_PSID_UNIFI_TX_DATA_RATE & SLSI_PSID_UNIFI_RX_DATA_RATE, 1 byte for SLSI_PSID_UNIFI_RSSI
* 10*7 bytes for 3 Throughput Mib's and 4 counter Mib's
*/
mibrsp.dataLength = 114;
mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL);
if (!mibrsp.data) {
SLSI_NET_DBG1(dev, SLSI_MLME, "failed to allocate memory\n");
kfree(mibreq.data);
return -ENOMEM;
}
r = slsi_mlme_get(sdev, dev, mibreq.data, mibreq.dataLength, mibrsp.data,
mibrsp.dataLength, &data_length);
kfree(mibreq.data);
if (r == 0) {
mibrsp.dataLength = (u32)data_length;
values = slsi_mib_decode_get_list(&mibrsp,
(sizeof(get_values) / sizeof(struct slsi_mib_get_entry)), get_values);
if (!values) {
SLSI_NET_DBG1(dev, SLSI_MLME, "mib decode list failed\n");
kfree(mibrsp.data);
return -ENOMEM;
}
if (values[mib_index].type != SLSI_MIB_TYPE_NONE) {
SLSI_CHECK_TYPE(sdev, values[mib_index].type, SLSI_MIB_TYPE_UINT);
slsi_decode_fw_rate((u32)values[mib_index].u.uintValue, &peer->sinfo.txrate, &ndev_vif->sta.data_rate_mbps);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
peer->sinfo.filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
#else
peer->sinfo.filled |= STATION_INFO_TX_BITRATE;
#endif
SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_TX_DATA_RATE = 0x%x\n",
values[mib_index].u.uintValue);
} else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type != SLSI_MIB_TYPE_NONE) {
SLSI_CHECK_TYPE(sdev, values[mib_index].type, SLSI_MIB_TYPE_UINT);
slsi_decode_fw_rate((u32)values[mib_index].u.uintValue, &peer->sinfo.rxrate, NULL);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
peer->sinfo.filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
#else
peer->sinfo.filled |= STATION_INFO_RX_BITRATE;
#endif
SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_RX_DATA_RATE = 0x%x\n",
values[mib_index].u.uintValue);
} else
SLSI_DBG3(sdev, SLSI_MLME, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type != SLSI_MIB_TYPE_NONE) {
SLSI_CHECK_TYPE(sdev, values[mib_index].type, SLSI_MIB_TYPE_INT);
if (values[mib_index].u.intValue >= 0)
peer->sinfo.signal = -1;
else
peer->sinfo.signal = (s8)values[mib_index].u.intValue;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
peer->sinfo.filled |= BIT(NL80211_STA_INFO_SIGNAL);
#else
peer->sinfo.filled |= STATION_INFO_SIGNAL;
#endif
SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_RSSI = %d\n",
values[mib_index].u.intValue);
} else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT)
rx_counter += values[mib_index].u.uintValue; /*bad_fcs_count*/
else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT)
rx_counter += values[mib_index].u.uintValue; /*mac_bad_sig_count*/
else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT)
rx_counter += values[mib_index].u.uintValue; /*rx_error_count*/
else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT)
peer->sinfo.tx_packets = values[mib_index].u.uintValue; /*tx good count*/
else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT)
peer->sinfo.tx_failed = values[mib_index].u.uintValue; /*tx bad count*/
else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT)
peer->sinfo.rx_packets = values[mib_index].u.uintValue; /*rx good count*/
else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
#ifdef CONFIG_SCSC_ENHANCED_PACKET_STATS
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT)
peer->sinfo.tx_retries = values[mib_index].u.uintValue; /*tx retry count*/
else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
#endif
#ifdef CONFIG_SCSC_WLAN_MAX_LINK_SPEED
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT)
bandwidth = values[mib_index].u.uintValue; /* bss bandwidth */
else
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) {
antenna_mode = values[mib_index].u.uintValue; /* current nss */
slsi_calc_max_data_rate(dev, bandwidth, antenna_mode);
} else {
SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid);
}
#endif
peer->sinfo.rx_dropped_misc = rx_counter;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
peer->sinfo.filled |= BIT(NL80211_STA_INFO_TX_FAILED) | BIT(NL80211_STA_INFO_RX_DROP_MISC) |
BIT(NL80211_STA_INFO_TX_PACKETS) | BIT(NL80211_STA_INFO_RX_PACKETS);
#ifdef CONFIG_SCSC_ENHANCED_PACKET_STATS
peer->sinfo.filled |= BIT(NL80211_STA_INFO_TX_RETRIES);
#endif
#endif
} else {
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_get_req failed(result:0x%4x)\n", r);
}
kfree(mibrsp.data);
kfree(values);
return r;
}
int slsi_mlme_connect_scan(struct slsi_dev *sdev, struct net_device *dev,
u32 n_ssids, struct cfg80211_ssid *ssids, struct ieee80211_channel *channel)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0;
struct ieee80211_channel **scan_channels = NULL;
struct ieee80211_channel **add_scan_channels;
int n_channels = 0;
struct sk_buff *scan;
struct cfg80211_scan_info info = {.aborted = true};
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
if (ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req) {
SLSI_NET_DBG3(dev, SLSI_MLME, "stop on-going Scan\n");
(void)slsi_mlme_del_scan(sdev, dev, ndev_vif->ifnum << 8 | SLSI_SCAN_HW_ID, false);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
cfg80211_scan_done(ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req, &info);
#else
cfg80211_scan_done(ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req, true);
#endif
ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req = NULL;
}
if (!channel) {
enum nl80211_band band;
struct wiphy *wiphy = sdev->wiphy;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
for (band = 0; band < NUM_NL80211_BANDS; band++) {
#else
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
#endif
if (!wiphy->bands[band])
continue;
n_channels += wiphy->bands[band]->n_channels;
}
WARN_ON(n_channels == 0);
scan_channels = kmalloc_array((size_t)n_channels, sizeof(*scan_channels), GFP_KERNEL);
if (!scan_channels) {
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return -ENOMEM;
}
n_channels = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
for (band = 0; band < NUM_NL80211_BANDS; band++) {
#else
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
#endif
int j;
if (!wiphy->bands[band])
continue;
for (j = 0; j < wiphy->bands[band]->n_channels; j++)
if (!(wiphy->bands[band]->channels[j].flags & IEEE80211_CHAN_DISABLED)) {
scan_channels[n_channels] = &wiphy->bands[band]->channels[j];
n_channels++;
}
}
add_scan_channels = scan_channels;
} else {
n_channels = 1;
add_scan_channels = &channel;
}
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan = true;
r = slsi_mlme_add_scan(sdev,
dev,
FAPI_SCANTYPE_FULL_SCAN,
FAPI_REPORTMODE_REAL_TIME,
n_ssids,
ssids,
n_channels,
add_scan_channels,
NULL,
ndev_vif->probe_req_ies,
ndev_vif->probe_req_ie_len,
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan);
SLSI_MUTEX_LOCK(ndev_vif->scan_result_mutex);
scan = slsi_dequeue_cached_scan_result(&ndev_vif->scan[SLSI_SCAN_HW_ID], NULL);
while (scan) {
slsi_rx_scan_pass_to_cfg80211(sdev, dev, scan);
scan = slsi_dequeue_cached_scan_result(&ndev_vif->scan[SLSI_SCAN_HW_ID], NULL);
}
SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex);
kfree(scan_channels);
ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan = false;
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
return r;
}
/**
* The powermgt_lock mutex is to ensure atomic update of the power management state.
*/
static DEFINE_MUTEX(powermgt_lock);
/**
* The slsi_mlme_powermgt_unlocked() must be called from a context that is synchronised
* with ndev_vif. if called without the ndev_vif mutex already taken, other mechanisms
* must ensure that ndev_vif will exist for the duration of the function.
*/
int slsi_mlme_powermgt_unlocked(struct slsi_dev *sdev, struct net_device *dev, u16 power_mode)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
mutex_lock(&powermgt_lock);
if (WARN_ON(!ndev_vif->activated)) {
mutex_unlock(&powermgt_lock);
return -EINVAL;
}
if (ndev_vif->power_mode == power_mode) {
mutex_unlock(&powermgt_lock);
SLSI_NET_DBG3(dev, SLSI_MLME, "power management mode is same as requested. No changes done\n");
return 0;
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_powermgt_req(vif:%d, power_management_mode:%d)\n", ndev_vif->ifnum, power_mode);
req = fapi_alloc(mlme_powermgt_req, MLME_POWERMGT_REQ, ndev_vif->ifnum, 0);
if (!req) {
mutex_unlock(&powermgt_lock);
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_powermgt_req.power_management_mode, power_mode);
rx = slsi_mlme_req_cfm(sdev, dev, req, MLME_POWERMGT_CFM);
if (!rx) {
mutex_unlock(&powermgt_lock);
return -EIO;
}
if (fapi_get_u16(rx, u.mlme_powermgt_cfm.result_code) == FAPI_RESULTCODE_SUCCESS) {
ndev_vif->power_mode = power_mode;
} else {
SLSI_NET_ERR(dev, "mlme_powermgt_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_powermgt_cfm.result_code));
r = -EINVAL;
}
kfree_skb(rx);
mutex_unlock(&powermgt_lock);
return r;
}
int slsi_mlme_powermgt(struct slsi_dev *sdev, struct net_device *dev, u16 power_mode)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
return slsi_mlme_powermgt_unlocked(sdev, dev, power_mode);
}
#ifdef CONFIG_SCSC_WLAN_NUM_ANTENNAS
int slsi_mlme_get_num_antennas(struct slsi_dev *sdev, struct net_device *dev, int *num_antennas)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int ret = 0;
const bool is_sta = (ndev_vif->iftype == NL80211_IFTYPE_STATION);
const bool is_softap = (ndev_vif->iftype == NL80211_IFTYPE_AP);
if (!is_sta && !is_softap) {
SLSI_NET_ERR(dev, "Invalid interface type %s\n", dev->name);
return -EPERM;
}
if (is_sta && (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)) {
SLSI_NET_ERR(dev, "sta is not in connected state\n");
return -EPERM;
}
SLSI_DBG1_NODEV(SLSI_MLME, "mlme_get_num_antennas_req(vif:%u)\n", ndev_vif->ifnum);
req = fapi_alloc(mlme_get_num_antennas_req, MLME_GET_NUM_ANTENNAS_REQ, ndev_vif->ifnum, 0);
if (!req)
return -EIO;
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_GET_NUM_ANTENNAS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_get_num_antennas_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_get_num_antennas_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_get_num_antennas_cfm.result_code));
ret = -EINVAL;
}
*num_antennas = fapi_get_u16(cfm, u.mlme_get_num_antennas_cfm.number_of_antennas);
kfree_skb(cfm);
return ret;
}
#endif
#ifdef CONFIG_SCSC_WLAN_SAE_CONFIG
int slsi_mlme_synchronised_response(struct slsi_dev *sdev, struct net_device *dev,
struct cfg80211_external_auth_params *params)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
if (ndev_vif->activated) {
SLSI_NET_DBG3(dev, SLSI_MLME, "MLME_SYNCHRONISED_RES\n");
req = fapi_alloc(mlme_synchronised_res, MLME_SYNCHRONISED_RES, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_synchronised_res.result_code, params->status);
fapi_set_memcpy(req, u.mlme_synchronised_res.bssid, params->bssid);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_synchronised_response(vif:%d) status:%d\n",
ndev_vif->ifnum, params->status);
cfm = slsi_mlme_req_no_cfm(sdev, dev, req);
if (cfm)
SLSI_NET_ERR(dev, "Received cfm for MLME_SYNCHRONISED_RES\n");
} else
SLSI_NET_DBG1(dev, SLSI_MLME, "vif is not active");
#ifdef CONFIG_SCSC_WLAN_BSS_SELECTION
ndev_vif->sta.wpa3_auth_state = SLSI_WPA3_AUTHENTICATED;
#endif
return 0;
}
#endif
int slsi_mlme_register_action_frame(struct slsi_dev *sdev, struct net_device *dev, u32 af_bitmap_active, u32 af_bitmap_suspended)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
req = fapi_alloc(mlme_register_action_frame_req, MLME_REGISTER_ACTION_FRAME_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u32(req, u.mlme_register_action_frame_req.action_frame_category_bitmap_active, af_bitmap_active);
fapi_set_u32(req, u.mlme_register_action_frame_req.action_frame_category_bitmap_suspended, af_bitmap_suspended);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_register_action_frame(vif:%d, active:%d, suspended:%d)\n", ndev_vif->ifnum, af_bitmap_active, af_bitmap_suspended);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_REGISTER_ACTION_FRAME_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_register_action_frame_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_register_action_frame_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_register_action_frame_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_channel_switch(struct slsi_dev *sdev, struct net_device *dev, u16 center_freq, u16 chan_info)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_channel_switch_req(vif:%d, freq: %d, channel info: 0x%x)\n", ndev_vif->ifnum, center_freq, chan_info);
req = fapi_alloc(mlme_channel_switch_req, MLME_CHANNEL_SWITCH_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_channel_switch_req.channel_frequency, SLSI_FREQ_HOST_TO_FW(center_freq));
fapi_set_u16(req, u.mlme_channel_switch_req.channel_information, chan_info);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_CHANNEL_SWITCH_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_channel_switch_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_channel_switch_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_channel_switch_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_add_info_elements(struct slsi_dev *sdev, struct net_device *dev, u16 purpose, const u8 *ies, const u16 ies_len)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
u8 *p;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
req = fapi_alloc(mlme_add_info_elements_req, MLME_ADD_INFO_ELEMENTS_REQ, ndev_vif->ifnum, ies_len);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_add_info_elements_req.purpose, purpose);
if (ies_len != 0) {
p = fapi_append_data(req, ies, ies_len);
if (!p) {
kfree_skb(req);
return -EINVAL;
}
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_add_info_elements_req(vif:%d, ies_len:%d)\n", ndev_vif->ifnum, ies_len);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_ADD_INFO_ELEMENTS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_add_info_elements_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_add_info_elements_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_add_info_elements_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_send_frame_data(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, u16 msg_type,
u16 host_tag, u32 dwell_time, u32 period)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
u16 len = skb->len;
struct sk_buff *original_skb = 0;
int ret;
#ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT
int is_enhanced_arp_request_frame = 0;
#endif
/* don't let ARP frames exhaust all the control slots */
if (msg_type == FAPI_MESSAGETYPE_ARP) {
int free_slots = 0;
free_slots = hip4_free_ctrl_slots_count(&sdev->hip4_inst);
if (free_slots < 0) {
SLSI_DBG1(sdev, SLSI_MLME,
"drop ARP (free slot count error)\n");
return free_slots;
}
if (free_slots < SLSI_MLME_ARP_DROP_FREE_SLOTS_COUNT) {
SLSI_DBG1(sdev, SLSI_MLME,
"drop ARP (No ARP Control slots:%d)\n", free_slots);
kfree_skb(skb);
return NETDEV_TX_OK;
}
#ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT
if (ndev_vif->enhanced_arp_detect_enabled && (msg_type == FAPI_MESSAGETYPE_ARP)) {
u8 *frame = skb->data + sizeof(struct ethhdr);
u16 arp_opcode = frame[SLSI_ARP_OPCODE_OFFSET] << 8 | frame[SLSI_ARP_OPCODE_OFFSET + 1];
if ((arp_opcode == SLSI_ARP_REQUEST_OPCODE) &&
!SLSI_IS_GRATUITOUS_ARP(frame) &&
!memcmp(&frame[SLSI_ARP_DEST_IP_ADDR_OFFSET], &ndev_vif->target_ip_addr, 4))
is_enhanced_arp_request_frame = 1;
}
#endif
}
/* check for headroom to push signal header; if not available, re-alloc headroom */
if (skb_headroom(skb) < (fapi_sig_size(mlme_send_frame_req))) {
struct sk_buff *skb2 = NULL;
skb2 = skb_realloc_headroom(skb, fapi_sig_size(mlme_send_frame_req));
if (!skb2)
return -EINVAL;
original_skb = skb;
skb = skb2;
}
len = skb->len;
(void)skb_push(skb, (fapi_sig_size(mlme_send_frame_req)));
/* fill the signal header */
fapi_set_u16(skb, id, MLME_SEND_FRAME_REQ);
fapi_set_u16(skb, receiver_pid, 0);
fapi_set_u16(skb, sender_pid, SLSI_TX_PROCESS_ID_MIN);
fapi_set_u16(skb, fw_reference, 0);
/* fill in signal parameters */
fapi_set_u16(skb, u.mlme_send_frame_req.vif, ndev_vif->ifnum);
if (host_tag == 0)
host_tag = slsi_tx_mgmt_host_tag(sdev);
#ifdef CONFIG_SCSC_WLAN_ARP_FLOW_CONTROL
if (msg_type == FAPI_MESSAGETYPE_ARP && sdev->fw_max_arp_count)
host_tag |= SLSI_HOST_TAG_ARP_MASK;
#endif
fapi_set_u16(skb, u.mlme_send_frame_req.host_tag, host_tag);
fapi_set_u16(skb, u.mlme_send_frame_req.data_unit_descriptor, FAPI_DATAUNITDESCRIPTOR_IEEE802_3_FRAME);
fapi_set_u16(skb, u.mlme_send_frame_req.message_type, msg_type);
fapi_set_u16(skb, u.mlme_send_frame_req.channel_frequency, 0);
fapi_set_u32(skb, u.mlme_send_frame_req.dwell_time, dwell_time);
fapi_set_u32(skb, u.mlme_send_frame_req.period, period);
SLSI_DBG2(sdev, SLSI_MLME, "vif:%d, message_type:%d, host_tag:0x%x\n", ndev_vif->ifnum, msg_type, host_tag);
/* slsi_tx_control frees the skb. Do not use it after this call. */
ret = slsi_tx_control(sdev, dev, skb);
if (ret != 0) {
SLSI_WARN(sdev, "failed to send MLME signal(err=%d)\n", ret);
return ret;
}
#ifdef CONFIG_SCSC_WLAN_ARP_FLOW_CONTROL
if (host_tag & SLSI_HOST_TAG_ARP_MASK) {
atomic_inc(&sdev->arp_tx_count);
atomic_inc(&ndev_vif->arp_tx_count);
/* Stop all the netif queues, if max arp threshold reached */
if (atomic_read(&sdev->arp_tx_count) == sdev->fw_max_arp_count)
scsc_wifi_pause_arp_q_all_vif(sdev);
}
#endif
#ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT
if (is_enhanced_arp_request_frame) {
int i;
ndev_vif->enhanced_arp_stats.arp_req_count_to_lower_mac++;
for (i = 0; i < SLSI_MAX_ARP_SEND_FRAME; i++) {
if (!ndev_vif->enhanced_arp_host_tag[i]) {
ndev_vif->enhanced_arp_host_tag[i] = host_tag;
break;
}
}
}
#endif
if (original_skb)
consume_skb(original_skb);
/* as the frame is queued to HIP for transmission, store the host tag of the frames
* to validate the transmission status in MLME-Frame-Transmission.indication.
* Take necessary action based on the type of frame and status of it's transmission
*/
if (msg_type == FAPI_MESSAGETYPE_EAPOL_KEY_M4) {
ndev_vif->sta.m4_host_tag = host_tag;
SLSI_NET_DBG1(dev, SLSI_MLME, "EAPOL-Key M4 frame (host_tag:%d)\n", ndev_vif->sta.m4_host_tag);
} else if (msg_type == FAPI_MESSAGETYPE_EAP_MESSAGE) {
if (!ndev_vif->sta.is_wps && (ndev_vif->iftype == NL80211_IFTYPE_STATION)) {
/* In case of non-P2P station and Enterprise security store the host_tag.
* If transmission of such frame fails, inform supplicant to disconnect.
*/
ndev_vif->sta.eap_hosttag = host_tag;
SLSI_NET_DBG1(dev, SLSI_MLME, "EAP frame (host_tag:%d)\n", ndev_vif->sta.eap_hosttag);
}
}
return ret;
}
int slsi_mlme_send_frame_mgmt(struct slsi_dev *sdev, struct net_device *dev, const u8 *frame, int frame_len,
u16 data_desc, u16 msg_type, u16 host_tag, u16 freq, u32 dwell_time, u32 period)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
u8 *p;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
req = fapi_alloc(mlme_send_frame_req, MLME_SEND_FRAME_REQ, ndev_vif->ifnum, frame_len);
if (!req) {
SLSI_WARN(sdev, "failed to alloc memory\n");
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_send_frame_req.host_tag, host_tag);
fapi_set_u16(req, u.mlme_send_frame_req.data_unit_descriptor, data_desc);
fapi_set_u16(req, u.mlme_send_frame_req.message_type, msg_type);
fapi_set_u16(req, u.mlme_send_frame_req.channel_frequency, freq);
fapi_set_u32(req, u.mlme_send_frame_req.dwell_time, dwell_time);
fapi_set_u32(req, u.mlme_send_frame_req.period, period);
p = fapi_append_data(req, frame, frame_len);
if (!p) {
kfree_skb(req);
SLSI_WARN(sdev, "failed to append data\n");
return -EINVAL;
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_send_frame_req(vif:%d, message_type:%d,host_tag:%d)\n", ndev_vif->ifnum, msg_type, host_tag);
slsi_debug_frame(sdev, dev, req, "TX");
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SEND_FRAME_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_send_frame_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_send_frame_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_send_frame_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_wifisharing_permitted_channels(struct slsi_dev *sdev, struct net_device *dev, u8 *permitted_channels)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
req = fapi_alloc(mlme_wifisharing_permitted_channels_req, MLME_WIFISHARING_PERMITTED_CHANNELS_REQ,
ndev_vif->ifnum, 8);
if (!req) {
r = -ENOMEM;
goto exit;
}
fapi_append_data(req, permitted_channels, 8);
SLSI_NET_DBG2(dev, SLSI_MLME, "(vif:%u)\n", ndev_vif->ifnum);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_WIFISHARING_PERMITTED_CHANNELS_CFM);
if (!cfm) {
r = -EIO;
goto exit;
}
if (fapi_get_u16(cfm, u.mlme_wifisharing_permitted_channels_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_wifisharing_permitted_channels_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_wifisharing_permitted_channels_cfm.result_code));
r = -EINVAL;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_mlme_reset_dwell_time(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_reset_dwell_time_req (vif:%d)\n", ndev_vif->ifnum);
req = fapi_alloc(mlme_reset_dwell_time_req, MLME_RESET_DWELL_TIME_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_RESET_DWELL_TIME_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_reset_dwell_time_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_reset_dwell_time_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_reset_dwell_time_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_set_packet_filter(struct slsi_dev *sdev, struct net_device *dev,
int pkt_filter_len,
u8 num_filters,
struct slsi_mlme_pkt_filter_elem *pkt_filter_elems)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0, i = 0, j = 0;
u8 *p;
u8 index = 0;
if (WARN_ON(!ndev_vif->activated))
return -EINVAL;
if (WARN_ON(!num_filters))
return -EINVAL;
req = fapi_alloc(mlme_set_packet_filter_req, MLME_SET_PACKET_FILTER_REQ, ndev_vif->ifnum, pkt_filter_len);
if (!req)
return -ENOMEM;
p = fapi_append_data(req, NULL, pkt_filter_len);
if (!p) {
kfree_skb(req);
return -EINVAL;
}
for (i = 0; i < num_filters; i++) {
struct slsi_mlme_pkt_filter_elem pkt_filter_elem = pkt_filter_elems[i];
memcpy(&p[index], pkt_filter_elem.header, SLSI_PKT_FILTER_ELEM_HDR_LEN);
index += SLSI_PKT_FILTER_ELEM_HDR_LEN;
for (j = 0; j < pkt_filter_elem.num_pattern_desc; j++) {
p[index++] = pkt_filter_elem.pattern_desc[j].offset;
p[index++] = pkt_filter_elem.pattern_desc[j].mask_length;
memcpy(&p[index], pkt_filter_elem.pattern_desc[j].mask, pkt_filter_elem.pattern_desc[j].mask_length);
index += pkt_filter_elem.pattern_desc[j].mask_length;
memcpy(&p[index], pkt_filter_elem.pattern_desc[j].pattern, pkt_filter_elem.pattern_desc[j].mask_length);
index += pkt_filter_elem.pattern_desc[j].mask_length;
}
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_set_packet_filter_req(vif:%d, num_filters:%d)\n", ndev_vif->ifnum, num_filters);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_PACKET_FILTER_CFM);
if (!cfm)
return -EIO;
kfree_skb(cfm);
return r;
}
int slsi_mlme_set_pmk(struct slsi_dev *sdev, struct net_device *dev, const u8 *pmk, u16 pmklen)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (WARN_ON(!ndev_vif->activated))
return -EINVAL;
if (pmk)
req = fapi_alloc(mlme_set_pmk_req, MLME_SET_PMK_REQ, ndev_vif->ifnum, pmklen);
else
req = fapi_alloc(mlme_set_pmk_req, MLME_SET_PMK_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
if (pmk)
fapi_append_data(req, pmk, pmklen);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_set_pmk_req(vif:%u, pmklen:%d)\n", ndev_vif->ifnum, pmklen);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_PMK_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_pmk_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_pmk_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_pmk_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_roam(struct slsi_dev *sdev, struct net_device *dev, const u8 *bssid, u16 freq)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (WARN_ON(!ndev_vif->activated))
return -EINVAL;
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_roam_req(vif:%u, bssid:%pM, freq:%d)\n", ndev_vif->ifnum, bssid, freq);
req = fapi_alloc(mlme_roam_req, MLME_ROAM_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_memcpy(req, u.mlme_roam_req.bssid, bssid);
fapi_set_u16(req, u.mlme_roam_req.channel_frequency, SLSI_FREQ_HOST_TO_FW(freq));
atomic_set(&ndev_vif->sta.drop_roamed_ind, 1);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_ROAM_CFM);
atomic_set(&ndev_vif->sta.drop_roamed_ind, 0);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_roam_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_roam_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_roam_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
ndev_vif->sta.roam_in_progress = true;
return r;
}
int slsi_mlme_set_cached_channels(struct slsi_dev *sdev, struct net_device *dev, u32 channels_count, u8 *channels)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
u8 *p;
int r = 0;
size_t channel_ie = 0;
int i;
const u8 channels_list_ie_header[] = {
0xDD, /* Element ID: Vendor Specific */
0x05, /* Length: actual length will be updated later */
0x00, 0x16, 0x32, /* OUI: Samsung Electronics Co. */
0x01, /* OUI Type: Scan parameters */
0x02 /* OUI Subtype: channel list */
};
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (WARN_ON(!ndev_vif->activated))
return -EINVAL;
if (channels_count) {
channel_ie += 6 + (channels_count * SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE);
req = fapi_alloc(mlme_set_cached_channels_req, MLME_SET_CACHED_CHANNELS_REQ, ndev_vif->ifnum, channel_ie);
} else {
req = fapi_alloc(mlme_set_cached_channels_req, MLME_SET_CACHED_CHANNELS_REQ, ndev_vif->ifnum, 0);
}
if (!req)
return -ENOMEM;
if (channels_count) {
u16 freq_fw_unit;
u8 *channels_list_ie = fapi_append_data(req, channels_list_ie_header, sizeof(channels_list_ie_header));
if (!channels_list_ie) {
SLSI_WARN(sdev, "channel list IE append failed\n");
kfree_skb(req);
return -EINVAL;
}
for (i = 0; i < channels_count; i++) {
SLSI_NET_DBG3(dev, SLSI_MLME, "request for channels %d\n", channels[i]);
p = fapi_append_data(req, NULL, SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE);
if (!p) {
kfree_skb(req);
return -EINVAL;
}
freq_fw_unit = 2 * ieee80211_channel_to_frequency(channels[i], (channels[i] <= 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
freq_fw_unit = cpu_to_le16(freq_fw_unit);
memcpy(p, &freq_fw_unit, sizeof(freq_fw_unit));
p[2] = FAPI_SCANPOLICY_2_4GHZ | FAPI_SCANPOLICY_5GHZ;
channels_list_ie[1] += SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE;
}
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_set_cached_channels_req(vif:%d)\n", ndev_vif->ifnum);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_CACHED_CHANNELS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_cached_channels_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_cached_channels_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_cached_channels_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9))
int slsi_mlme_set_acl(struct slsi_dev *sdev, struct net_device *dev, u16 ifnum,
enum nl80211_acl_policy acl_policy, int max_acl_entries,
struct mac_address mac_addrs[])
{
struct sk_buff *req;
struct sk_buff *cfm;
size_t mac_acl_size = 0;
int i, r = 0;
int n_acl_entries = 0;
u8 zero_addr[ETH_ALEN] = {0};
for (i = 0; i < max_acl_entries; i++) {
if (!SLSI_ETHER_EQUAL(mac_addrs[i].addr, zero_addr))
n_acl_entries++;
}
mac_acl_size = sizeof((mac_addrs[0])) * (n_acl_entries);
req = fapi_alloc(mlme_set_acl_req, MLME_SET_ACL_REQ, ifnum, mac_acl_size);
if (!req) {
SLSI_NET_ERR(dev, "fapi alloc failure\n");
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_set_acl_req.entries, n_acl_entries);
fapi_set_u16(req, u.mlme_set_acl_req.acl_policy, acl_policy);
for (i = 0; i < max_acl_entries; i++) {
if (!SLSI_ETHER_EQUAL(mac_addrs[i].addr, zero_addr))
fapi_append_data(req, mac_addrs[i].addr, sizeof((mac_addrs[i])));
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_set_acl_req(vif:%u, n_acl_entries:%d)\n", ifnum, n_acl_entries);
if (ifnum)
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_ACL_CFM);
else
cfm = slsi_mlme_req_cfm(sdev, NULL, req, MLME_SET_ACL_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_acl_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_acl_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_acl_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
#endif
int slsi_mlme_set_traffic_parameters(struct slsi_dev *sdev, struct net_device *dev, u16 user_priority, u16 medium_time, u16 minimun_data_rate, u8 *mac)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (WARN_ON(!ndev_vif->activated))
return -EINVAL;
if (WARN_ON(ndev_vif->vif_type != FAPI_VIFTYPE_STATION && ndev_vif->iftype == NL80211_IFTYPE_STATION))
return -EINVAL;
req = fapi_alloc(mlme_set_traffic_parameters_req, MLME_SET_TRAFFIC_PARAMETERS_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_set_traffic_parameters_req.user_priority, user_priority);
fapi_set_u16(req, u.mlme_set_traffic_parameters_req.medium_time, medium_time);
fapi_set_u16(req, u.mlme_set_traffic_parameters_req.minimum_data_rate, minimun_data_rate);
if (mac)
fapi_set_memcpy(req, u.mlme_set_traffic_parameters_req.peer_address, mac);
else
fapi_set_memset(req, u.mlme_set_traffic_parameters_req.peer_address, 0);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_set_traffic_parameters_req(vif:%u, user_priority:%d, medium_time:%d)\n", ndev_vif->ifnum, user_priority, medium_time);
rx = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_TRAFFIC_PARAMETERS_CFM);
if (!rx)
return -EIO;
if (fapi_get_u16(rx, u.mlme_set_traffic_parameters_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_traffic_parameters_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_set_traffic_parameters_cfm.result_code));
r = -EINVAL;
}
return r;
}
int slsi_mlme_del_traffic_parameters(struct slsi_dev *sdev, struct net_device *dev, u16 user_priority)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (WARN_ON(!ndev_vif->activated))
return -EINVAL;
if (WARN_ON(ndev_vif->vif_type != FAPI_VIFTYPE_STATION && ndev_vif->iftype == NL80211_IFTYPE_STATION))
return -EINVAL;
req = fapi_alloc(mlme_del_traffic_parameters_req, MLME_DEL_TRAFFIC_PARAMETERS_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_del_traffic_parameters_req.user_priority, user_priority);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_del_traffic_parameters_req(vif:%u, user_priority:%d)\n", ndev_vif->ifnum, user_priority);
rx = slsi_mlme_req_cfm(sdev, dev, req, MLME_DEL_TRAFFIC_PARAMETERS_CFM);
if (!rx)
return -EIO;
if (fapi_get_u16(rx, u.mlme_del_traffic_parameters_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_del_traffic_parameters_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_del_traffic_parameters_cfm.result_code));
r = -EINVAL;
}
return r;
}
int slsi_mlme_set_ext_capab(struct slsi_dev *sdev, struct net_device *dev, u8 *data, int datalength)
{
struct slsi_mib_data mib_data = { 0, NULL };
int error = 0;
error = slsi_mib_encode_octet(&mib_data, SLSI_PSID_UNIFI_EXTENDED_CAPABILITIES, datalength, data, 0);
if (error != SLSI_MIB_STATUS_SUCCESS) {
error = -ENOMEM;
goto exit;
}
if (WARN_ON(mib_data.dataLength == 0)) {
error = -EINVAL;
goto exit;
}
error = slsi_mlme_set(sdev, NULL, mib_data.data, mib_data.dataLength);
kfree(mib_data.data);
if (!error)
return 0;
exit:
SLSI_ERR(sdev, "Error in setting ext capab. error = %d\n", error);
return error;
}
int slsi_mlme_tdls_peer_resp(struct slsi_dev *sdev, struct net_device *dev, u16 pid, u16 tdls_event)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
req = fapi_alloc(mlme_tdls_peer_res, MLME_TDLS_PEER_RES, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_tdls_peer_res.peer_index, pid);
fapi_set_u16(req, u.mlme_tdls_peer_res.tdls_event, tdls_event);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_tdls_peer_res(vif:%d)\n", ndev_vif->ifnum);
cfm = slsi_mlme_req_no_cfm(sdev, dev, req);
WARN_ON(cfm);
return 0;
}
int slsi_mlme_tdls_action(struct slsi_dev *sdev, struct net_device *dev, const u8 *peer, int action, u16 center_freq, u16 chan_info)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_tdls_action_req(action:%u)\n", action);
req = fapi_alloc(mlme_tdls_action_req, MLME_TDLS_ACTION_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
center_freq = SLSI_FREQ_HOST_TO_FW(center_freq);
fapi_set_memcpy(req, u.mlme_tdls_action_req.peer_sta_address, peer);
fapi_set_u16(req, u.mlme_tdls_action_req.tdls_action, action);
fapi_set_u16(req, u.mlme_tdls_action_req.channel_frequency, center_freq);
fapi_set_u16(req, u.mlme_tdls_action_req.channel_information, chan_info);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_TDLS_ACTION_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_tdls_action_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_tdls_action_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_tdls_action_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_reassociate(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (WARN_ON(!ndev_vif->activated))
return -EINVAL;
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_reassoc_req(vif:%u)\n", ndev_vif->ifnum);
req = fapi_alloc(mlme_reassociate_req, MLME_REASSOCIATE_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_REASSOCIATE_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_reassociate_cfm.result_code) == FAPI_RESULTCODE_HOST_REQUEST_SUCCESS) {
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_reassoc_cfm(result:0x%04x)\n",
fapi_get_u16(cfm, u.mlme_reassociate_cfm.result_code));
} else {
SLSI_NET_ERR(dev, "mlme_reassoc_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_reassociate_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
void slsi_mlme_reassociate_resp(struct slsi_dev *sdev, struct net_device *dev)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_REASSOCIATE_RESP\n");
return;
}
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_reassociate_resp(vif:%d)\n", ndev_vif->ifnum);
req = fapi_alloc(mlme_reassociate_res, MLME_REASSOCIATE_RES, ndev_vif->ifnum, 0);
if (!req)
return;
cfm = slsi_mlme_req_no_cfm(sdev, dev, req);
WARN_ON(cfm);
}
int slsi_mlme_add_range_req(struct slsi_dev *sdev, struct net_device *dev, u8 count,
struct slsi_rtt_config *nl_rtt_params, u16 rtt_id, u8 *source_addr)
{
struct sk_buff *req;
struct sk_buff *rx;
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0, i;
size_t alloc_data_size = 0;
u8 fapi_ie_generic[] = { 0xdd, 0x1c, 0x00, 0x16, 0x32, 0x0a, 0x01 };
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
/* calculate data size */
alloc_data_size += count * (fapi_ie_generic[1] + 2);
req = fapi_alloc(mlme_add_range_req, MLME_ADD_RANGE_REQ, 0, alloc_data_size);
if (!req) {
SLSI_ERR(sdev, "failed to alloc %zd\n", alloc_data_size);
return -ENOMEM;
}
SLSI_DBG2(sdev, SLSI_MLME, "count:%d allocated data size: %d, source_addr:%pM\n",
count, alloc_data_size, source_addr);
/*fill the data */
fapi_set_u16(req, u.mlme_add_range_req.vif, 0);
fapi_set_u16(req, u.mlme_add_range_req.rtt_id, rtt_id);
fapi_set_memcpy(req, u.mlme_add_range_req.device_address, source_addr);
for (i = 0; i < count; i++) {
fapi_append_data(req, fapi_ie_generic, sizeof(fapi_ie_generic));
fapi_append_data(req, nl_rtt_params[i].peer_addr, ETH_ALEN);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].rtt_peer, 1);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].rtt_type, 1);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].channel_freq, 2);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].burst_period, 1);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].num_burst, 1);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].num_frames_per_burst, 1);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].num_retries_per_ftmr, 1);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].burst_duration, 1);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].preamble, 2);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].bw, 2);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].LCI_request, 2);
fapi_append_data(req, (u8 *)&nl_rtt_params[i].LCR_request, 2);
}
rx = slsi_mlme_req_cfm(sdev, NULL, req, MLME_ADD_RANGE_CFM);
SLSI_DBG2(sdev, SLSI_MLME, "(After mlme req cfm for rtt config)\n");
if (!rx)
return -EIO;
if (fapi_get_u16(rx, u.mlme_add_range_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_ERR(sdev, "mlme_add_range_cfm(ERROR:0x%04x)",
fapi_get_u16(rx, u.mlme_add_range_cfm.result_code));
r = -EINVAL;
}
kfree_skb(rx);
return r;
}
bool slsi_del_range_cfm_validate(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *cfm)
{
int result = fapi_get_u16(cfm, u.mlme_del_range_cfm.result_code);
bool r = false;
SLSI_UNUSED_PARAMETER(sdev);
if (WARN_ON(!dev))
goto exit;
if (result == FAPI_RESULTCODE_SUCCESS)
r = true;
else
SLSI_NET_ERR(dev, "mlme_del_range_cfm(result:0x%04x) ERROR\n", result);
exit:
kfree_skb(cfm);
return r;
}
int slsi_mlme_del_range_req(struct slsi_dev *sdev, struct net_device *dev, u16 count,
u8 *addr, u16 rtt_id)
{
struct sk_buff *req;
struct sk_buff *rx;
struct netdev_vif *ndev_vif = netdev_priv(dev);
int r = 0, i;
size_t alloc_data_size = 0;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
/* calculate data size-->2 bytes for vif */
alloc_data_size += count * sizeof(ETH_ALEN);
/* Alloc data size */
req = fapi_alloc(mlme_del_range_req, MLME_DEL_RANGE_REQ, 0, alloc_data_size);
if (!req) {
SLSI_ERR(sdev, "failed to alloc %zd\n", alloc_data_size);
return -ENOMEM;
}
/*fill the data */
fapi_set_u16(req, u.mlme_del_range_req.vif, 0);
fapi_set_u16(req, u.mlme_del_range_req.rtt_id, rtt_id);
fapi_set_u16(req, u.mlme_del_range_req.entries, count);
SLSI_INFO(sdev, "rtt_id:%d,count:%d\n", rtt_id, count);
for (i = 0; i < count; i++)
fapi_append_data(req, &addr[i * ETH_ALEN], ETH_ALEN);
rx = slsi_mlme_req_cfm_ind(sdev, dev, req, MLME_DEL_RANGE_CFM, MLME_RANGE_IND, slsi_del_range_cfm_validate);
if (!rx) {
SLSI_NET_ERR(dev, "mlme_del_range_cfm() ERROR\n");
r = -EINVAL;
}
kfree_skb(rx);
return r;
}
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
#define SLSI_FAPI_EPNO_NETWORK_MIN_SIZE (3)
int slsi_mlme_set_pno_list(struct slsi_dev *sdev, int count,
struct slsi_epno_param *epno_param, struct slsi_epno_hs2_param *epno_hs2_param)
{
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
size_t alloc_data_size = 0;
u32 i, j;
u8 fapi_ie_generic[] = { 0xdd, 0, 0x00, 0x16, 0x32, 0x01, 0x00 };
u8 *buff_ptr, *ie_start_pos;
size_t str_len = 0;
str_len = strnlen(epno_hs2_param->realm, ARRAY_SIZE(epno_hs2_param->realm));
if (count) {
/* calculate data size */
if (epno_param) {
alloc_data_size += sizeof(fapi_ie_generic) + SLSI_FAPI_EPNO_NETWORK_MIN_SIZE * count + 11;
for (i = 0; i < count; i++)
alloc_data_size += epno_param->epno_ssid[i].ssid_len;
} else if (epno_hs2_param) {
for (i = 0; i < count; i++) {
/* fapi_ie_generic + Network_block_ID(1) + Realm_length(1) + realm_data(x)
* + Roaming_Consortium_Count(1) + Roaming Consortium data(16 * 8) +
* PLMN length(1) + PLMN data(6)
*/
if (str_len)
alloc_data_size += sizeof(fapi_ie_generic) + 1 + 1 + (str_len + 1)
+ 1 + 16 * 8 + 1 + 6;
else
alloc_data_size += sizeof(fapi_ie_generic) + 1 + 1 + 0
+ 1 + 16 * 8 + 1 + 6;
}
}
}
/* Alloc data size */
req = fapi_alloc(mlme_set_pno_list_req, MLME_SET_PNO_LIST_REQ, 0, alloc_data_size);
if (!req) {
SLSI_ERR(sdev, "failed to alloc %zd\n", alloc_data_size);
return -ENOMEM;
}
if (count) {
/* Fill data */
if (epno_param) {
fapi_ie_generic[1] = alloc_data_size - 2;
fapi_ie_generic[6] = 9; /* OUI */
fapi_append_data(req, fapi_ie_generic, sizeof(fapi_ie_generic));
fapi_append_data(req, (u8 *)epno_param, (sizeof(*epno_param) - 1));
for (i = 0; i < count; i++) {
fapi_append_data(req, (u8 *)&epno_param->epno_ssid[i].flags, 2);
fapi_append_data(req, (u8 *)&epno_param->epno_ssid[i].ssid_len, 1);
fapi_append_data(req, (u8 *)epno_param->epno_ssid[i].ssid,
epno_param->epno_ssid[i].ssid_len);
}
} else if (epno_hs2_param) {
u8 realm_length;
u8 roaming_consortium_count = 16;
u8 plmn_length = 6;
u8 plmn_digit[6];
fapi_ie_generic[6] = 0x10; /* OUI subtype = Passpoint Network */
for (i = 0; i < count; i++) {
buff_ptr = fapi_append_data(req, fapi_ie_generic, sizeof(fapi_ie_generic));
if (!buff_ptr) {
SLSI_ERR(sdev, "failed append data\n");
kfree_skb(req);
return -EINVAL;
}
ie_start_pos = buff_ptr;
fapi_append_data(req, (u8 *)&epno_hs2_param[i].id, 1);
realm_length = strlen(epno_hs2_param[i].realm);
if (realm_length) {
realm_length++;
fapi_append_data(req, &realm_length, 1);
fapi_append_data(req, epno_hs2_param[i].realm, realm_length);
} else {
fapi_append_data(req, &realm_length, 1);
}
fapi_append_data(req, &roaming_consortium_count, 1);
fapi_append_data(req, (u8 *)&epno_hs2_param[i].roaming_consortium_ids, 16 * 8);
fapi_append_data(req, &plmn_length, 1);
for (j = 0; j < 3; j++) {
plmn_digit[j * 2] = epno_hs2_param[i].plmn[i] & 0x0F;
plmn_digit[(j * 2) + 1] = epno_hs2_param[i].plmn[i] & 0xF0 >> 4;
}
buff_ptr = fapi_append_data(req, plmn_digit, sizeof(plmn_digit));
if (!buff_ptr) {
SLSI_ERR(sdev, "failed append data\n");
kfree_skb(req);
return -EINVAL;
}
buff_ptr += sizeof(plmn_digit);
ie_start_pos[1] = buff_ptr - ie_start_pos - 2; /* fill ie length field */
}
}
}
/* Send signal */
/* Use the Global sig_wait not the Interface specific for mlme-set-pno.list */
rx = slsi_mlme_req_cfm(sdev, NULL, req, MLME_SET_PNO_LIST_CFM);
if (!rx)
return -EIO;
if (fapi_get_u16(rx, u.mlme_set_pno_list_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_ERR(sdev, "mlme_set_pno_list_cfm(ERROR:0x%04x)",
fapi_get_u16(rx, u.mlme_set_pno_list_cfm.result_code));
r = -EINVAL;
}
kfree_skb(rx);
return r;
}
int slsi_mlme_start_link_stats_req(struct slsi_dev *sdev, u16 mpdu_size_threshold, bool aggressive_stats_enabled)
{
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
req = fapi_alloc(mlme_start_link_statistics_req, MLME_START_LINK_STATISTICS_REQ, 0, 0);
if (!req) {
SLSI_ERR(sdev, "memory allocation failed for signal\n");
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_start_link_statistics_req.mpdu_size_threshold, mpdu_size_threshold);
fapi_set_u16(req, u.mlme_start_link_statistics_req.aggressive_statistics_gathering_enabled,
aggressive_stats_enabled);
SLSI_DBG2(sdev, SLSI_MLME, "(mpdu_size_threshold:%d, aggressive_stats_enabled:%d)\n",
mpdu_size_threshold, aggressive_stats_enabled);
cfm = slsi_mlme_req_cfm(sdev, NULL, req, MLME_START_LINK_STATISTICS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_start_link_statistics_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_ERR(sdev, "mlme_start_link_statistics_cfm (result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_start_link_statistics_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_stop_link_stats_req(struct slsi_dev *sdev, u16 stats_stop_mask)
{
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
req = fapi_alloc(mlme_stop_link_statistics_req, MLME_STOP_LINK_STATISTICS_REQ, 0, 0);
if (!req) {
SLSI_ERR(sdev, "memory allocation failed for signal\n");
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_stop_link_statistics_req.statistics_stop_bitmap, stats_stop_mask);
SLSI_DBG2(sdev, SLSI_MLME, "statistics_stop_bitmap:%d\n", stats_stop_mask);
cfm = slsi_mlme_req_cfm(sdev, NULL, req, MLME_STOP_LINK_STATISTICS_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_stop_link_statistics_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_ERR(sdev, "mlme_stop_link_statistics_cfm (result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_stop_link_statistics_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
#endif
int slsi_mlme_set_rssi_monitor(struct slsi_dev *sdev, struct net_device *dev, u8 enable, s8 low_rssi_threshold, s8 high_rssi_threshold)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_set_rssi_monitor(vif:%u), enable =%d, low_rssi_threshold = %d,high_rssi_threshold =%d\n",
ndev_vif->ifnum, enable, low_rssi_threshold, high_rssi_threshold);
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
req = fapi_alloc(mlme_monitor_rssi_req, MLME_MONITOR_RSSI_REQ, ndev_vif->ifnum, 0);
if (!req) {
SLSI_NET_ERR(dev, "fapi alloc failure\n");
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_monitor_rssi_req.rssi_monitoring_enabled, enable);
fapi_set_u16(req, u.mlme_monitor_rssi_req.low_rssi_threshold, low_rssi_threshold);
fapi_set_u16(req, u.mlme_monitor_rssi_req.high_rssi_threshold, high_rssi_threshold);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_MONITOR_RSSI_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_monitor_rssi_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_monitor_rssi_cfm(result:0x%04x) ERROR\n", fapi_get_u16(cfm, u.mlme_monitor_rssi_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
struct slsi_mib_value *slsi_read_mibs(struct slsi_dev *sdev, struct net_device *dev,
struct slsi_mib_get_entry *mib_entries, int mib_count, struct slsi_mib_data *mibrsp)
{
struct slsi_mib_data mibreq = { 0, NULL };
struct slsi_mib_value *values;
int rx_length, r;
r = slsi_mib_encode_get_list(&mibreq, mib_count, mib_entries);
if (r != SLSI_MIB_STATUS_SUCCESS) {
SLSI_WARN(sdev, "slsi_mib_encode_get_list fail %d\n", r);
return NULL;
}
r = slsi_mlme_get(sdev, dev, mibreq.data, mibreq.dataLength, mibrsp->data, mibrsp->dataLength, &rx_length);
kfree(mibreq.data);
if (r != 0) {
SLSI_ERR(sdev, "Mib (err:%d)\n", r);
return NULL;
}
mibrsp->dataLength = (u32)rx_length;
values = slsi_mib_decode_get_list(mibrsp, mib_count, mib_entries);
if (!values)
SLSI_WARN(sdev, "decode error\n");
return values;
}
int slsi_mlme_set_ctwindow(struct slsi_dev *sdev, struct net_device *dev, unsigned int ct_param)
{
struct netdev_vif *ndev_vif;
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_set_ctwindow(ct_param = %d)\n", ct_param);
ndev_vif = netdev_priv(dev);
req = fapi_alloc(mlme_set_ctwindow_req, MLME_SET_CTWINDOW_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_set_ctwindow_req.ctwindow, ct_param);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_CTWINDOW_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_ctwindow_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_ctwindow_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_ctwindow_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_set_p2p_noa(struct slsi_dev *sdev, struct net_device *dev, unsigned int noa_count,
unsigned int interval, unsigned int duration)
{
struct netdev_vif *ndev_vif;
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_set_noa_req(noa_count = %d, interval = %d, duration = %d)\n",
noa_count, interval, duration);
ndev_vif = netdev_priv(dev);
req = fapi_alloc(mlme_set_noa_req, MLME_SET_NOA_REQ, ndev_vif->ifnum, 0);
if (!req)
return -ENOMEM;
fapi_set_u16(req, u.mlme_set_noa_req.request_id, SLSI_NOA_CONFIG_REQUEST_ID);
fapi_set_u16(req, u.mlme_set_noa_req.noa_count, noa_count);
if (!interval)
fapi_set_u32(req, u.mlme_set_noa_req.interval, (1 * 1024 * ndev_vif->ap.beacon_interval));
else
fapi_set_u32(req, u.mlme_set_noa_req.interval, interval * 1000);
fapi_set_u32(req, u.mlme_set_noa_req.duration, duration * 1000);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_NOA_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_noa_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_set_noa_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_noa_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_set_host_state(struct slsi_dev *sdev, struct net_device *dev, u8 host_state)
{
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_INFO(dev, "Skip MLME_HOST_STATE_REQ in wlanlite mode\n");
return -EOPNOTSUPP;
}
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_set_host_state(state = 0x%04x)\n", host_state);
req = fapi_alloc(mlme_host_state_req, MLME_HOST_STATE_REQ, 0, 0);
if (!req) {
SLSI_NET_ERR(dev, "fapi alloc failure\n");
return -ENOMEM;
}
fapi_set_u16(req, u.mlme_host_state_req.host_state, host_state);
cfm = slsi_mlme_req_cfm(sdev, NULL, req, MLME_HOST_STATE_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_host_state_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_host_state_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_host_state_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_read_apf_request(struct slsi_dev *sdev, struct net_device *dev, u8 **host_dst, int *datalen)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated) {
SLSI_ERR(sdev, "ndev_vif is not activated\n");
r = -EINVAL;
goto exit;
}
if (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) {
SLSI_ERR(sdev, "vif_type is not FAPI_VIFTYPE_STATION\n");
r = -EINVAL;
goto exit;
}
req = fapi_alloc(mlme_read_apf_req, MLME_READ_APF_REQ, ndev_vif->ifnum, 0);
if (!req) {
r = -ENOMEM;
goto exit;
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_read_apf_req(vif:%u)\n", ndev_vif->ifnum);
rx = slsi_mlme_req_cfm(sdev, dev, req, MLME_READ_APF_CFM);
if (!rx) {
r = -EIO;
goto exit;
}
if (fapi_get_u16(rx, u.mlme_read_apf_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_read_apf_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_read_apf_cfm.result_code));
r = -EINVAL;
}
*datalen = fapi_get_datalen(rx);
*host_dst = fapi_get_data(rx);
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
int slsi_mlme_install_apf_request(struct slsi_dev *sdev, struct net_device *dev,
u8 *program, u32 program_len)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if (!ndev_vif->activated) {
SLSI_ERR(sdev, "ndev_vif is not activated\n");
r = -EINVAL;
goto exit;
}
if (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) {
SLSI_ERR(sdev, "vif_type is not FAPI_VIFTYPE_STATION\n");
r = -EINVAL;
goto exit;
}
req = fapi_alloc(mlme_install_apf_req, MLME_INSTALL_APF_REQ, ndev_vif->ifnum, program_len);
if (!req) {
r = -ENOMEM;
goto exit;
}
/* filter_mode will be "don't care" for FW */
fapi_set_u16(req, u.mlme_install_apf_req.filter_mode, FAPI_APFFILTERMODE_SUSPEND);
fapi_append_data(req, program, program_len);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_install_apf_req(vif:%u, filter_mode:%d)\n",
ndev_vif->ifnum, FAPI_APFFILTERMODE_SUSPEND);
rx = slsi_mlme_req_cfm(sdev, dev, req, MLME_INSTALL_APF_CFM);
if (!rx) {
r = -EIO;
goto exit;
}
if (fapi_get_u16(rx, u.mlme_install_apf_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_install_apf_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_install_apf_cfm.result_code));
r = -EINVAL;
}
exit:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
return r;
}
#ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT
int slsi_mlme_arp_detect_request(struct slsi_dev *sdev, struct net_device *dev, u16 action, u8 *target_ipaddr)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *rx;
int r = 0;
u32 ipaddress = 0x0;
int i = 0;
if (!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex)) {
SLSI_ERR(sdev, "ndev_vif mutex is not locked\n");
r = -EINVAL;
goto exit;
}
if (!ndev_vif->activated) {
SLSI_ERR(sdev, "ndev_vif is not activated\n");
r = -EINVAL;
goto exit;
}
if ((ndev_vif->vif_type != FAPI_VIFTYPE_STATION) && (ndev_vif->iftype == NL80211_IFTYPE_STATION)) {
SLSI_ERR(sdev, "vif_type is not FAPI_VIFTYPE_STATION\n");
r = -EINVAL;
goto exit;
}
req = fapi_alloc(mlme_arp_detect_req, MLME_ARP_DETECT_REQ, ndev_vif->ifnum, 0);
if (!req) {
r = -ENOMEM;
goto exit;
}
for (i = 0; i < 4; i++)
ipaddress = (ipaddress << 8) | ((unsigned char)target_ipaddr[i]);
ipaddress = htonl(ipaddress);
fapi_set_u16(req, u.mlme_arp_detect_req.arp_detect_action, action);
fapi_append_data(req, (const u8 *)&ipaddress, 4);
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_arp_detect_req(vif:%u, action:%d IP Address:%d.%d.%d.%d)\n",
ndev_vif->ifnum, action, ndev_vif->target_ip_addr[0], ndev_vif->target_ip_addr[1],
ndev_vif->target_ip_addr[2], ndev_vif->target_ip_addr[3]);
rx = slsi_mlme_req_cfm(sdev, dev, req, MLME_ARP_DETECT_CFM);
if (!rx) {
r = -EIO;
goto exit;
}
if (fapi_get_u16(rx, u.mlme_arp_detect_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_arp_detect_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(rx, u.mlme_arp_detect_cfm.result_code));
r = -EINVAL;
}
exit:
return r;
}
#endif
#define SLSI_TEST_CONFIG_MONITOR_MODE_DESCRIPTOR_SIZE (12)
int slsi_test_sap_configure_monitor_mode(struct slsi_dev *sdev, struct net_device *dev, struct cfg80211_chan_def *chandef)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
u8 *p = NULL;
size_t alloc_data_size = 0;
u16 center_freq1;
u16 center_freq2;
u16 chan_info;
int r = 0;
const u8 monitor_config_ie_header[] = {
0xDD, /* Element ID: Vendor Specific */
0x11, /* Length */
0x00, 0x16, 0x32, /* OUI: Samsung Electronics Co. */
0x10, /* OUI Type: Monitor mode parameters */
0x01 /* OUI Subtype: configuration */
};
if (slsi_is_test_mode_enabled()) {
SLSI_NET_WARN(dev, "WlanLite: NOT supported\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
chan_info = slsi_get_chann_info(sdev, chandef);
SLSI_NET_DBG2(dev, SLSI_MLME, "test_configure_monitor_mode_req(center_freq1:%u, chan_info:%u, center_freq2:%u)\n",
chandef->center_freq1,
chan_info,
chandef->center_freq2);
center_freq1 = SLSI_FREQ_HOST_TO_FW(chandef->center_freq1);
center_freq1 = cpu_to_le16(center_freq1);
center_freq2 = SLSI_FREQ_HOST_TO_FW(chandef->center_freq2);
center_freq2 = cpu_to_le16(center_freq2);
alloc_data_size = sizeof(monitor_config_ie_header) + SLSI_TEST_CONFIG_MONITOR_MODE_DESCRIPTOR_SIZE;
req = fapi_alloc(test_configure_monitor_mode_req, TEST_CONFIGURE_MONITOR_MODE_REQ, ndev_vif->ifnum, alloc_data_size);
if (!req) {
SLSI_NET_ERR(dev, "failed to alloc Monitor mode request (len:%d)\n", alloc_data_size);
return -ENOMEM;
}
fapi_append_data(req, monitor_config_ie_header, sizeof(monitor_config_ie_header));
fapi_append_data(req, (const u8 *)&center_freq1, 2);
fapi_append_data(req, (const u8 *)&chan_info, 2);
fapi_append_data(req, (const u8 *)&center_freq2, 2);
/* MAC address filtering is not supported yet; so fill in zeros */
p = fapi_append_data(req, NULL, 6);
if (!p) {
SLSI_NET_ERR(dev, "mac address filtering append failed\n");
kfree_skb(req);
return -EINVAL;
}
memset(p, 0, 6);
cfm = slsi_mlme_req_cfm(sdev, dev, req, TEST_CONFIGURE_MONITOR_MODE_CFM);
if (!cfm) {
SLSI_NET_ERR(dev, "failed to receive Confirm\n");
return -EIO;
}
if (fapi_get_u16(cfm, u.mlme_set_channel_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "test_configure_monitor_mode_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.test_configure_monitor_mode_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_delba_req(struct slsi_dev *sdev, struct net_device *dev, u8 *peer_qsta_address, u16 priority, u16 direction, u16 sequence_number, u16 reason_code)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
int r = 0;
if (slsi_is_test_mode_enabled()) {
SLSI_NET_WARN(dev, "WlanLite: NOT supported\n");
return -EOPNOTSUPP;
}
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
req = fapi_alloc(mlme_delba_req, MLME_DELBA_REQ, ndev_vif->ifnum, 0);
if (!req) {
SLSI_NET_ERR(dev, "failed to alloc DELBA request\n");
return -ENOMEM;
}
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_delba_req(vif:%d, peer:%pM, priority:%d, direction:%d, sn:%d, reason_code:%d)\n",
ndev_vif->ifnum, peer_qsta_address, priority, direction, sequence_number, reason_code);
fapi_set_memcpy(req, u.mlme_delba_req.peer_qsta_address, peer_qsta_address);
fapi_set_u16(req, u.mlme_delba_req.user_priority, priority);
fapi_set_u16(req, u.mlme_delba_req.direction, direction);
fapi_set_u16(req, u.mlme_delba_req.sequence_number, sequence_number);
fapi_set_u16(req, u.mlme_delba_req.reason, reason_code);
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_DELBA_CFM);
if (!cfm) {
SLSI_NET_ERR(dev, "failed to receive Confirm\n");
return -EIO;
}
if (fapi_get_u16(cfm, u.mlme_delba_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_NET_ERR(dev, "mlme_delba_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_delba_cfm.result_code));
r = -EINVAL;
}
kfree_skb(cfm);
return r;
}
int slsi_mlme_set_country(struct slsi_dev *sdev, char *alpha2)
{
struct slsi_mib_data mib_data = { 0, NULL };
struct sk_buff *req;
struct sk_buff *cfm;
int country_index = -1;
u32 rules_len = 0;
u16 country_code = 0;
u8 append_byte = 0;
u8 dfs_region = 0;
int i = 0;
int error = 0;
int freq_range = 0;
if (sdev->regdb.regdb_state == SLSI_REG_DB_SET) {
for (i = 0; i < sdev->regdb.num_countries; i++) {
if ((sdev->regdb.country[i].alpha2[0] == alpha2[0]) &&
(sdev->regdb.country[i].alpha2[1] == alpha2[1])) {
country_index = i;
break;
}
}
if (country_index == -1) {
SLSI_ERR(sdev, "Invalid country code!\n");
return -EINVAL;
}
/* 7 octets for each rule */
rules_len = 7 * sdev->regdb.country[country_index].collection->reg_rule_num;
dfs_region = sdev->regdb.country[country_index].dfs_region;
}
/* last parameter should be length of bulk data */
req = fapi_alloc(mlme_set_country_req, MLME_SET_COUNTRY_REQ, 0, rules_len);
if (!req)
return -ENOMEM;
country_code = (((u16)alpha2[0] << 8) | (u16)alpha2[1]);
fapi_set_u16(req, u.mlme_set_country_req.country_code, country_code);
fapi_set_u16(req, u.mlme_set_country_req.dfs_regulatory_domain, dfs_region);
if (rules_len) {
for (i = 0; i < sdev->regdb.country[country_index].collection->reg_rule_num; i++) {
if (sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->start_freq >= 57000)
continue;
append_byte = (sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->start_freq * 2) & 0xFF;
fapi_append_data(req, &append_byte, 1);
append_byte = ((sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->start_freq * 2) >> 8) & 0xFF;
fapi_append_data(req, &append_byte, 1);
append_byte = (sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->end_freq * 2) & 0xFF;
fapi_append_data(req, &append_byte, 1);
append_byte = ((sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->end_freq * 2) >> 8) & 0xFF;
fapi_append_data(req, &append_byte, 1);
freq_range = sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->end_freq -
sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->start_freq;
if (((sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->start_freq / 1000) == 5) &&
sdev->forced_bandwidth &&
(freq_range >= sdev->forced_bandwidth))
append_byte = sdev->forced_bandwidth;
else
append_byte = sdev->regdb.country[country_index].collection->reg_rule[i]->freq_range->max_bandwidth & 0xFF;
fapi_append_data(req, &append_byte, 1);
append_byte = sdev->regdb.country[country_index].collection->reg_rule[i]->max_eirp & 0xFF;
fapi_append_data(req, &append_byte, 1);
append_byte = sdev->regdb.country[country_index].collection->reg_rule[i]->flags & 0xFF;
fapi_append_data(req, &append_byte, 1);
}
}
SLSI_DBG2(sdev, SLSI_MLME, "mlme_set_country_req(country:%c%c, dfs_regulatory_domain:%x)\n", alpha2[0], alpha2[1], dfs_region);
cfm = slsi_mlme_req_cfm(sdev, NULL, req, MLME_SET_COUNTRY_CFM);
if (!cfm)
return -EIO;
if (fapi_get_u16(cfm, u.mlme_set_country_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
SLSI_ERR(sdev, "mlme_set_country_cfm(result:0x%04x) ERROR\n",
fapi_get_u16(cfm, u.mlme_set_country_cfm.result_code));
kfree_skb(cfm);
error = slsi_mib_encode_octet(&mib_data, SLSI_PSID_UNIFI_DEFAULT_COUNTRY, 3, alpha2, 0);
if (error != SLSI_MIB_STATUS_SUCCESS)
return -ENOMEM;
if (WARN_ON(mib_data.dataLength == 0))
return -EINVAL;
error = slsi_mlme_set(sdev, NULL, mib_data.data, mib_data.dataLength);
if (error)
return -EINVAL;
kfree(mib_data.data);
return 0;
}
kfree_skb(cfm);
return 0;
}
#ifdef CONFIG_SCSC_WLAN_FAST_RECOVERY
void slsi_mlme_set_country_for_recovery(struct slsi_dev *sdev)
{
int ret = 0;
struct slsi_mib_data mib_data = { 0, NULL };
SLSI_MUTEX_LOCK(sdev->device_config_mutex);
ret = slsi_mib_encode_octet(&mib_data, SLSI_PSID_UNIFI_DEFAULT_COUNTRY, 3, sdev->device_config.domain_info.regdomain->alpha2, 0);
if (ret != SLSI_MIB_STATUS_SUCCESS) {
ret = -ENOMEM;
SLSI_ERR(sdev, "Err setting country error = %d\n", ret);
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
return;
}
if (mib_data.dataLength == 0) {
ret = -EINVAL;
SLSI_ERR(sdev, "Err setting country error = %d\n", ret);
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
return;
}
ret = slsi_mlme_set(sdev, NULL, mib_data.data, mib_data.dataLength);
kfree(mib_data.data);
if (ret)
SLSI_ERR(sdev, "Err setting country error = %d\n", ret);
SLSI_MUTEX_UNLOCK(sdev->device_config_mutex);
}
#endif