mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
4134 lines
136 KiB
C
4134 lines
136 KiB
C
/*****************************************************************************
|
|
*
|
|
* Copyright (c) 2012 - 2016 Samsung Electronics Co., Ltd. All rights reserved
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <linux/delay.h>
|
|
#include <net/cfg80211.h>
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include "dev.h"
|
|
#include "debug.h"
|
|
#include "mlme.h"
|
|
#include "mib.h"
|
|
#include "mgt.h"
|
|
#include "hydra.h"
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_ENABLE
|
|
#include "oxygen_ioctl.h"
|
|
#endif
|
|
|
|
#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_FW_API_RATE_HT_SELECTOR_FIELD 0xc000
|
|
#define SLSI_FW_API_RATE_NON_HT_SELECTED 0x4000
|
|
#define SLSI_FW_API_RATE_HT_SELECTED 0x8000
|
|
#define SLSI_FW_API_RATE_VHT_SELECTED 0xc000
|
|
|
|
#define SLSI_FW_API_RATE_MCS_FIELD 0x000F
|
|
#define SLSI_FW_API_RATE_INDEX_FIELD 0x1fff
|
|
|
|
#define SLSI_FW_API_RATE_BW_FIELD 0x0600
|
|
#define SLSI_FW_API_RATE_BW_40MHZ 0x0200
|
|
#define SLSI_FW_API_RATE_BW_20MHZ 0x0000
|
|
|
|
#define SLSI_FW_API_RATE_SGI 0x0100
|
|
#define SLSI_FW_API_RATE_GF 0x0080
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
|
|
#define SLSI_FW_MAX_BSSID_PER_TRACKING_IE (20)
|
|
#define SLSI_FW_BSSID_DESCRIPTOR_BSS_SIZE (10)
|
|
#endif
|
|
|
|
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");
|
|
|
|
/* indices: 3= BW20->idx_0, BW40->idx_1, BW80->idx_2.
|
|
* 2= noSGI->idx_0, SGI->idx_1
|
|
* 10= mcs index
|
|
* rate units 100kbps
|
|
* This table for single stream Nss=1and does not include 160MHz BW and 80+80MHz BW.
|
|
*/
|
|
static const u16 slsi_mlme_vht_rates_table[3][2][10] = {
|
|
{ /* BW20 */
|
|
{ /* no SGI */
|
|
65, 130, 195, 260, 390, 520, 585, 650, 780, 0
|
|
},
|
|
{ /* SGI */
|
|
72, 144, 217, 289, 433, 578, 650, 722, 867, 0
|
|
}
|
|
},
|
|
{ /* BW40 */
|
|
{ /* no SGI */
|
|
135, 270, 405, 540, 810, 1080, 1215, 1350, 1620, 1800
|
|
},
|
|
{ /* SGI */
|
|
150, 300, 450, 600, 900, 1200, 1350, 1500, 1800, 2000
|
|
}
|
|
},
|
|
{ /* BW80 */
|
|
{ /* no SGI */
|
|
293, 585, 878, 1170, 1755, 2340, 2633, 2925, 3510, 3900
|
|
},
|
|
{ /* SGI */
|
|
325, 650, 975, 1300, 1950, 2600, 2925, 3250, 3900, 4333
|
|
}
|
|
}
|
|
};
|
|
|
|
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 + 2000));
|
|
|
|
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);
|
|
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;
|
|
}
|
|
|
|
if (sdev->mlme_blocked) {
|
|
SLSI_DBG3(sdev, SLSI_TX, "Rejected. mlme_blocked=%d", sdev->mlme_blocked);
|
|
slsi_kfree_skb(skb);
|
|
return NULL;
|
|
}
|
|
|
|
slsi_wakelock(&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);
|
|
slsi_kfree_skb(sig_wait->cfm);
|
|
slsi_kfree_skb(sig_wait->ind);
|
|
slsi_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);
|
|
slsi_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;
|
|
slsi_kfree_skb(sig_wait->cfm);
|
|
slsi_kfree_skb(sig_wait->ind);
|
|
sig_wait->cfm = NULL;
|
|
sig_wait->ind = NULL;
|
|
|
|
if (mib_error)
|
|
*mib_error = sig_wait->mib_error;
|
|
else
|
|
slsi_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_wakeunlock(&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)
|
|
slsi_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:
|
|
slsi_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:
|
|
slsi_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:
|
|
slsi_kfree_skb(skb);
|
|
return NULL;
|
|
}
|
|
|
|
/* NOTE: dev can be NULL! */
|
|
static inline 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:
|
|
slsi_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;
|
|
}
|
|
|
|
#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;
|
|
u16 prim_chan_pos = 0;
|
|
|
|
SLSI_UNUSED_PARAMETER_NOT_DEBUG(sdev);
|
|
|
|
switch (chandef->width) {
|
|
case NL80211_CHAN_WIDTH_20:
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
|
chann_info = 20;
|
|
break;
|
|
case NL80211_CHAN_WIDTH_40:
|
|
chann_info = 40;
|
|
/* Check HT Minus */
|
|
if (chandef->chan != NULL && chandef->center_freq1 < chandef->chan->center_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 + chandef->chan->center_freq - chandef->center_freq1) / 20);
|
|
if (prim_chan_pos > 3) {
|
|
SLSI_ERR(sdev, "Invalid center_freq1 in chandef : %u, primary channel = %u, primary chan pos calculated = %d\n", chandef->center_freq1, chandef->chan->center_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", chandef->width);
|
|
chann_info = 0;
|
|
break;
|
|
}
|
|
|
|
SLSI_DBG3(sdev, SLSI_MLME, "channel_width:%u, chann_info:0x%x\n", chandef->width, chann_info);
|
|
return chann_info;
|
|
}
|
|
|
|
int slsi_check_channelization(struct slsi_dev *sdev, struct cfg80211_chan_def *chandef)
|
|
{
|
|
u8 width;
|
|
struct ieee80211_reg_rule *rule = NULL;
|
|
struct ieee80211_channel *channel = NULL;
|
|
|
|
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;
|
|
} else if (channel->flags & (IEEE80211_CHAN_DISABLED |
|
|
IEEE80211_CHAN_RADAR
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 13)
|
|
| IEEE80211_CHAN_PASSIVE_SCAN
|
|
#endif
|
|
)) {
|
|
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_NOT_DEBUG(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 == NULL) {
|
|
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;
|
|
}
|
|
|
|
slsi_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));
|
|
|
|
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);
|
|
|
|
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);
|
|
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);
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_set_ip_address_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
slsi_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;
|
|
}
|
|
|
|
SLSI_DBG2(sdev, SLSI_MLME, "mlme_set_req(vif:%d, mib_len:%d)\n", ifnum, mib_len);
|
|
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;
|
|
|
|
SLSI_DBG2(sdev, SLSI_MLME, "mlme_set_cfm(datalen:%u)\n", fapi_get_datalen(cfm));
|
|
if (fapi_get_datalen(cfm)) {
|
|
mib_buffer_dump_to_log(sdev, fapi_get_data(cfm), fapi_get_datalen(cfm));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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;
|
|
|
|
SLSI_DBG3(sdev, SLSI_MLME, "mlme_get_req\n");
|
|
|
|
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;
|
|
|
|
SLSI_DBG3(sdev, SLSI_MLME, "mlme_get_cfm (datalen: %u)\n", fapi_get_datalen(cfm));
|
|
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));
|
|
}
|
|
|
|
slsi_kfree_skb(err);
|
|
slsi_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;
|
|
|
|
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));
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_add_vif_req(vif:%d)\n", ndev_vif->ifnum);
|
|
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);
|
|
|
|
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:%u) 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;
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
void 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;
|
|
|
|
if (slsi_is_test_mode_enabled()) {
|
|
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_DEL_VIF.request\n");
|
|
return;
|
|
}
|
|
|
|
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_del_vif_req(vif:%d)\n", ndev_vif->ifnum);
|
|
req = fapi_alloc(mlme_del_vif_req, MLME_DEL_VIF_REQ, ndev_vif->ifnum, 0);
|
|
if (!req)
|
|
return;
|
|
|
|
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_DEL_VIF_CFM);
|
|
if (!cfm)
|
|
return;
|
|
|
|
if (fapi_get_u16(cfm, u.mlme_del_vif_cfm.result_code) != FAPI_RESULTCODE_SUCCESS)
|
|
SLSI_NET_ERR(dev, "mlme_del_vif_cfm(result:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_del_vif_cfm.result_code));
|
|
|
|
if ((ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT) && (sdev->delete_gc_probe_req_ies == true)) {
|
|
kfree(sdev->gc_probe_req_ies);
|
|
sdev->gc_probe_req_ies = NULL;
|
|
sdev->gc_probe_req_ie_len = 0;
|
|
sdev->delete_gc_probe_req_ies = false;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
}
|
|
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_set_channel_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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));
|
|
|
|
scan_res = slsi_skb_dequeue(&ndev_vif->scan[scan_id].scan_results);
|
|
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;
|
|
}
|
|
|
|
slsi_kfree_skb(scan_res);
|
|
scan_res = slsi_skb_dequeue(&ndev_vif->scan[scan_id].scan_results);
|
|
}
|
|
}
|
|
|
|
/* 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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_add_scan_cfm.result_code));
|
|
r = false;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
|
|
int slsi_mlme_set_bssid_hotlist_req(struct slsi_dev *sdev, struct net_device *dev, struct slsi_nl_hotlist_param *nl_hotlist_param)
|
|
{
|
|
struct sk_buff *req;
|
|
struct sk_buff *cfm;
|
|
int i;
|
|
int num_desc;
|
|
int alloc_data_size;
|
|
int remaining_ap;
|
|
u8 bssid_desc[] = { 0xdd, /* Element ID */
|
|
0x00, /* Length */
|
|
0x00, 0x16, 0x32, /* Samsung OUI */
|
|
0x01, /* OUI Type: Scan Parameters */
|
|
0x05, /* OUI Subtype: BSSID Descriptor */
|
|
0x00, /* AP Lost Threshold */
|
|
};
|
|
|
|
if (nl_hotlist_param) {
|
|
/* One BSSID descriptor can contain SLSI_GSCAN_MAX_BSSID_PER_IE AP information */
|
|
num_desc = (nl_hotlist_param->num_bssid / SLSI_GSCAN_MAX_BSSID_PER_IE);
|
|
if (nl_hotlist_param->num_bssid % SLSI_GSCAN_MAX_BSSID_PER_IE)
|
|
num_desc++;
|
|
|
|
alloc_data_size = (num_desc * sizeof(bssid_desc)) + (nl_hotlist_param->num_bssid * SLSI_FW_BSSID_DESCRIPTOR_BSS_SIZE);
|
|
req = fapi_alloc(mlme_set_bssid_hotlist_req, MLME_SET_BSSID_HOTLIST_REQ, 0, alloc_data_size);
|
|
if (!req) {
|
|
SLSI_NET_ERR(dev, "Failed to allocate memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
remaining_ap = nl_hotlist_param->num_bssid;
|
|
bssid_desc[7] = nl_hotlist_param->lost_ap_sample_size;
|
|
for (i = 0; i < nl_hotlist_param->num_bssid; i++) {
|
|
if ((i % SLSI_GSCAN_MAX_BSSID_PER_IE) == 0) {
|
|
if (remaining_ap > SLSI_GSCAN_MAX_BSSID_PER_IE) {
|
|
bssid_desc[1] = sizeof(bssid_desc) - 2 + (SLSI_GSCAN_MAX_BSSID_PER_IE * SLSI_FW_BSSID_DESCRIPTOR_BSS_SIZE); /* Length */
|
|
remaining_ap -= SLSI_GSCAN_MAX_BSSID_PER_IE;
|
|
} else {
|
|
bssid_desc[1] = sizeof(bssid_desc) - 2 + (remaining_ap * SLSI_FW_BSSID_DESCRIPTOR_BSS_SIZE); /* Length */
|
|
}
|
|
|
|
fapi_append_data(req, bssid_desc, sizeof(bssid_desc));
|
|
}
|
|
|
|
fapi_append_data(req, (u8 *)&nl_hotlist_param->ap[i].bssid[0], ETH_ALEN);
|
|
|
|
nl_hotlist_param->ap[i].low = cpu_to_le16(nl_hotlist_param->ap[i].low);
|
|
fapi_append_data(req, (u8 *)&nl_hotlist_param->ap[i].low, 2);
|
|
|
|
nl_hotlist_param->ap[i].high = cpu_to_le16(nl_hotlist_param->ap[i].high);
|
|
fapi_append_data(req, (u8 *)&nl_hotlist_param->ap[i].high, 2);
|
|
}
|
|
} else {
|
|
req = fapi_alloc(mlme_set_bssid_hotlist_req, MLME_SET_BSSID_HOTLIST_REQ, 0, 0);
|
|
if (!req) {
|
|
SLSI_NET_ERR(dev, "Failed to allocate memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
cfm = slsi_mlme_req_cfm(sdev, NULL, req, MLME_SET_BSSID_HOTLIST_CFM);
|
|
if (!cfm)
|
|
return -EIO;
|
|
|
|
SLSI_DBG2(sdev, SLSI_GSCAN, "mlme_set_bssid_hotlist_cfm(datalen:%u)\n", fapi_get_datalen(cfm));
|
|
if (fapi_get_u16(cfm, u.mlme_set_bssid_hotlist_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
|
|
SLSI_NET_ERR(dev, "mlme_set_bssid_hotlist_cfm(result: %#x) ERROR\n",
|
|
fapi_get_u16(cfm, u.mlme_set_bssid_hotlist_cfm.result_code));
|
|
slsi_kfree_skb(cfm);
|
|
return -EINVAL;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return 0;
|
|
}
|
|
|
|
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");
|
|
slsi_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);
|
|
slsi_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");
|
|
slsi_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[])
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
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");
|
|
slsi_kfree_skb(req);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < num_channels; i++) {
|
|
chann = channels[i]->hw_value & 0xFF;
|
|
|
|
if (sdev->device_config.supported_band) {
|
|
if (channels[i]->band == IEEE80211_BAND_2GHZ && sdev->device_config.supported_band != SLSI_FREQ_BAND_2GHZ)
|
|
continue;
|
|
if (channels[i]->band == IEEE80211_BAND_5GHZ && sdev->device_config.supported_band != SLSI_FREQ_BAND_5GHZ)
|
|
continue;
|
|
}
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_MLME, "c:%3d,b:%d,f:%d,om:%d,m:%d,op:%d,p:%d,rp:%d,b:%d,of:%08x,f:%08x\n",
|
|
chann,
|
|
channels[i]->band,
|
|
channels[i]->center_freq,
|
|
channels[i]->orig_mag,
|
|
channels[i]->max_antenna_gain,
|
|
channels[i]->orig_mpwr,
|
|
channels[i]->max_power,
|
|
channels[i]->max_reg_power,
|
|
channels[i]->beacon_found,
|
|
channels[i]->orig_flags,
|
|
channels[i]->flags);
|
|
|
|
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");
|
|
slsi_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 (ndev_vif->ifnum == SLSI_NET_INDEX_WLAN)
|
|
p[2] = FAPI_SCANPOLICY_ANY_RA;
|
|
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");
|
|
slsi_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;
|
|
|
|
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);
|
|
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) {
|
|
slsi_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);
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "SSID: %.*s\n", pssid->ssid_len, pssid->ssid);
|
|
} else {
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "wild card SSID\n");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
|
|
/* 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, 0x01 /* Skip first period: true for scheduled scans*/
|
|
};
|
|
|
|
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));
|
|
|
|
alloc_data_size += sizeof(scan_timing_ie) +
|
|
ies_len +
|
|
SLSI_SCAN_PRIVATE_IE_SSID_FILTER_HEADER_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);
|
|
}
|
|
|
|
for (i = 0; i < request->n_match_sets; i++) {
|
|
/* 1 byte for SSID_length field + variable length SSID */
|
|
alloc_data_size += (1 + request->match_sets[i].ssid.ssid_len);
|
|
}
|
|
|
|
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;
|
|
|
|
SLSI_U32_TO_BUFF_LE(request->interval * 1000, &scan_timing_ie[7]);
|
|
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 = fapi_append_data(req, ssid_filter_ie_hdr, sizeof(ssid_filter_ie_hdr));
|
|
|
|
if (!ssid_filter_ie) {
|
|
slsi_kfree_skb(req);
|
|
SLSI_ERR(sdev, "ssid_filter_ie append failed\n");
|
|
return -EIO;
|
|
}
|
|
for (i = 0; i < request->n_match_sets; i++, match_sets++) {
|
|
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);
|
|
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:%u) ERROR\n", fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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_NET_DBG3(dev, SLSI_MLME, "id:0x%x, n_channels:%d\n", (ndev_vif->ifnum << 8 | SLSI_SCAN_HW_ID), n_channels);
|
|
|
|
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 == NULL) {
|
|
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);
|
|
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:%u) ERROR\n", fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
}
|
|
slsi_kfree_skb(rx);
|
|
return r;
|
|
}
|
|
|
|
int slsi_mlme_del_scan(struct slsi_dev *sdev, struct net_device *dev, u16 scan_id)
|
|
{
|
|
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);
|
|
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_del_scan_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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)
|
|
{
|
|
u8 ext_capa_ie[SLSI_AP_EXT_CAPAB_IE_LEN];
|
|
int i;
|
|
|
|
ext_capa_ie[0] = WLAN_EID_EXT_CAPABILITY;
|
|
ext_capa_ie[1] = SLSI_AP_EXT_CAPAB_IE_LEN - 1 - 1;
|
|
|
|
for (i = 2; i < SLSI_AP_EXT_CAPAB_IE_LEN; i++)
|
|
ext_capa_ie[i] = 0x00;
|
|
|
|
/* For VHT, set the Operating Mode Notification field - Bit 62 (8th Octet) */
|
|
ext_capa_ie[SLSI_AP_EXT_CAPAB_IE_LEN - 1] |= 0x40;
|
|
|
|
fapi_append_data(req, &ext_capa_ie[0], SLSI_AP_EXT_CAPAB_IE_LEN);
|
|
}
|
|
#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;
|
|
u8 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 != NULL) {
|
|
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_OPERATION:
|
|
case WLAN_EID_HT_CAPABILITY:
|
|
case WLAN_EID_VHT_CAPABILITY:
|
|
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;
|
|
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));
|
|
if (vht_capab_ie) {
|
|
size_t len_before_vht;
|
|
|
|
len_before_vht = vht_capab_ie - tail_pos;
|
|
fapi_append_data(req, tail_pos, len_before_vht); /* Append IEs till VHT Capab IE */
|
|
slsi_ap_add_ext_capab_ie(req, ndev_vif);
|
|
fapi_append_data(req, vht_capab_ie, tail_length - len_before_vht); /* Append the VHT Capab IE onwards */
|
|
} else {
|
|
/* VHT IE is added later by driver for P2PGO case (for default 80 MHz) and so it may not be present as well
|
|
* Append Ext Capab at the end for such cases
|
|
*/
|
|
fapi_append_data(req, tail_pos, tail_length);
|
|
slsi_ap_add_ext_capab_ie(req, ndev_vif);
|
|
}
|
|
} 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;
|
|
|
|
#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));
|
|
|
|
if (WARN_ON(!settings->beacon.head_len || !settings->beacon.head))
|
|
return -EINVAL;
|
|
if (WARN_ON(!settings->ssid_len || !settings->ssid))
|
|
return -EINVAL;
|
|
if (WARN_ON(!settings->beacon_interval))
|
|
return -EINVAL;
|
|
|
|
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 */
|
|
if (mgmt->u.beacon.capab_info & WLAN_CAPABILITY_PRIVACY)
|
|
ndev_vif->ap.privacy = 1;
|
|
else
|
|
ndev_vif->ap.privacy = 0;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
if (append_vht_ies)
|
|
vht_ies_len = SLSI_VHT_CAPABILITIES_IE_LEN + SLSI_VHT_OPERATION_IE_LEN;
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
/* 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) {
|
|
slsi_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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_start_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
static void slsi_mlme_connect_rsse_copy(struct slsi_mlme_rsse *rsse, u8 *buf_ptr, int buf_ptr_len)
|
|
{
|
|
u8 *rsse_buf_pos;
|
|
int i;
|
|
u8 rsse_header[] = { 0xDD, 0x00, /* VENDOR_SPECIFIC, LEN */
|
|
0x00, 0x16, 0x32, /* OUI */
|
|
0x05 /* OUT Type: rsse */ };
|
|
|
|
if (WARN_ON(buf_ptr == NULL || buf_ptr_len == 0))
|
|
return;
|
|
|
|
rsse_header[1] = buf_ptr_len - 2;
|
|
|
|
rsse_buf_pos = buf_ptr;
|
|
for (i = 0; i < sizeof(rsse_header); i++)
|
|
rsse_buf_pos[i] = rsse_header[i];
|
|
|
|
rsse_buf_pos += sizeof(rsse_header);
|
|
|
|
*rsse_buf_pos++ = rsse->group_cs_count;
|
|
if (rsse->group_cs_count) {
|
|
memcpy(rsse_buf_pos, rsse->group_cs, rsse->group_cs_count * 4);
|
|
rsse_buf_pos += rsse->group_cs_count * 4;
|
|
}
|
|
|
|
*rsse_buf_pos++ = rsse->pairwise_cs_count;
|
|
if (rsse->pairwise_cs_count) {
|
|
memcpy(rsse_buf_pos, rsse->pairwise_cs, rsse->pairwise_cs_count * 4);
|
|
rsse_buf_pos += rsse->pairwise_cs_count * 4;
|
|
}
|
|
|
|
*rsse_buf_pos++ = rsse->akm_suite_count;
|
|
if (rsse->akm_suite_count) {
|
|
memcpy(rsse_buf_pos, rsse->akm_suite, rsse->akm_suite_count * 4);
|
|
rsse_buf_pos += rsse->akm_suite_count * 4;
|
|
}
|
|
|
|
*rsse_buf_pos++ = rsse->pmkid_count;
|
|
if (rsse->pmkid_count) {
|
|
memcpy(rsse_buf_pos, rsse->pmkid, rsse->pmkid_count * 16);
|
|
rsse_buf_pos += rsse->pmkid_count * 16;
|
|
}
|
|
|
|
if (rsse->group_mgmt_cs)
|
|
memcpy(rsse_buf_pos, rsse->group_mgmt_cs, 4);
|
|
else
|
|
memset(rsse_buf_pos, 0, 4);
|
|
}
|
|
|
|
static int slsi_mlme_connect_build_wapi_rsse(struct cfg80211_connect_params *sme, struct slsi_mlme_rsse *rsse)
|
|
{
|
|
const u8 *ptr = NULL;
|
|
int ptr_len = 0;
|
|
int index = 0;
|
|
const u8 *wapi_ie;
|
|
|
|
memset(rsse, 0, sizeof(struct slsi_mlme_rsse));
|
|
|
|
/* For WAPI IE , refer 7.3.2.9 WAPI information element in WAPI Independent IS Sample Draft Document Number: N13774*/
|
|
wapi_ie = cfg80211_find_ie(SLSI_WLAN_EID_WAPI, sme->ie, sme->ie_len);
|
|
if (wapi_ie) {
|
|
u16 version = wapi_ie[3] << 8 | wapi_ie[2];
|
|
|
|
if (version == 1) {
|
|
ptr_len = wapi_ie[1] - 2;
|
|
ptr = &wapi_ie[4]; /* points to start group cipher */
|
|
}
|
|
}
|
|
if (ptr == NULL) {
|
|
SLSI_ERR_NODEV("crypto.wpa_versions(%d) but no WAPI ie\n", sme->crypto.wpa_versions);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rsse->akm_suite_count = ptr[index];
|
|
index += 2;
|
|
if (rsse->akm_suite_count) {
|
|
rsse->akm_suite = &ptr[index];
|
|
index += rsse->akm_suite_count * 4;
|
|
}
|
|
rsse->pairwise_cs_count = ptr[index]; /*Unicast Cipher Suite Count*/
|
|
index += 2;
|
|
rsse->pairwise_cs = &ptr[index]; /*Unicast Cipher Suite*/
|
|
index += rsse->pairwise_cs_count * 4;
|
|
|
|
rsse->group_cs_count = 1;
|
|
rsse->group_cs = &ptr[index]; /*Multicast Cipher Suite*/
|
|
index += 4;
|
|
|
|
index += 2; /*WAPI Capablities*/
|
|
|
|
if (ptr_len > index) {
|
|
rsse->pmkid_count = ptr[index]; /*BKID count*/
|
|
index += 2;
|
|
if (rsse->pmkid_count)
|
|
rsse->pmkid = &ptr[index];
|
|
}
|
|
|
|
/* group_mgmt_cs is not relevant in WAPI case */
|
|
|
|
/* 6-> header, 4-> group_cs_count,pairwise_cs_count,akm_suite_count,pmkid_count
|
|
* last 4 -> group_mgmt_cs.
|
|
*/
|
|
return 6 + 4 + (rsse->group_cs_count + rsse->pairwise_cs_count + rsse->akm_suite_count) * 4 +
|
|
rsse->pmkid_count * 16 + 4;
|
|
}
|
|
|
|
static int slsi_mlme_connect_build_rsse(struct cfg80211_connect_params *sme, struct slsi_mlme_rsse *rsse, u8 *mfp)
|
|
{
|
|
const u8 *ptr = NULL;
|
|
int ptr_len = 0;
|
|
int index = 0;
|
|
const u8 *ie;
|
|
u8 version_index, oui_len = 0;
|
|
u16 version;
|
|
const u8 *osen_ie = NULL;
|
|
|
|
/* supplicant specific values: ver =0 (wapi/osen) ,version 1 => wpa, version 2 => wpa2 */
|
|
if (sme->crypto.wpa_versions == 0) {
|
|
if (sme->ie[0] == SLSI_WLAN_EID_WAPI)
|
|
return slsi_mlme_connect_build_wapi_rsse(sme, rsse);
|
|
|
|
osen_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, SLSI_WLAN_OUI_TYPE_WFA_OSEN, sme->ie, sme->ie_len);
|
|
if (!osen_ie)
|
|
return 0;
|
|
|
|
ptr_len = osen_ie[1] - 4; /* OUI : 3, type: 1*/
|
|
ptr = &osen_ie[6]; /*point to start of group cipher :skip EID, len, OUI and type */
|
|
} else { /* RSN/WPA2*/
|
|
/* For RSN indices refer "Figure 8-187 RSNE format" in IEEE spec*/
|
|
if (sme->crypto.wpa_versions == 2) {
|
|
ie = cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len);
|
|
version_index = 2;
|
|
} else { /*(sme->crypto.wpa_versions == 1*/
|
|
ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WPA, sme->ie, sme->ie_len);
|
|
oui_len = 4; /*3-> OUI,1- type*/
|
|
version_index = 6;
|
|
}
|
|
|
|
if (ie == NULL) {
|
|
SLSI_ERR_NODEV("crypto.wpa_versions(%d) but no rsn/wpa ie\n", sme->crypto.wpa_versions);
|
|
return -EINVAL;
|
|
}
|
|
version = ie[version_index + 1] << 8 | ie[version_index];
|
|
if (version != 1) {
|
|
SLSI_ERR_NODEV("Unexpected version (%d) in rsn/wpa ie\n", version);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ptr = &ie[version_index + 2]; /* skip version, point to start of group cipher */
|
|
ptr_len = ie[1] - (2 + oui_len); /*version len + oui len(for wpa)*/
|
|
}
|
|
|
|
/* OSEN /WPA/WPA2*/
|
|
memset(rsse, 0, sizeof(struct slsi_mlme_rsse));
|
|
|
|
/* ptr point to -> [ group(4), pairwise_count(2){x}, pairwise_cs(x*4),
|
|
* akm_suite_count(2){y}, akm_suite(y*4), rsn_cap(2), pmkid_count(2){z},
|
|
* pmkid(z*16), group_mgmt_cs(4)]
|
|
* for wpa pts ends at akm_suite.
|
|
* the count fields are 2 bytes but values will be less than 256. So ignoring
|
|
* the higher byte for count.
|
|
*/
|
|
|
|
/* Group Data Cipher suite */
|
|
rsse->group_cs_count = 1;
|
|
rsse->group_cs = ptr;
|
|
index += 4;
|
|
|
|
/* Pairwise Cipher suite count */
|
|
rsse->pairwise_cs_count = ptr[index];
|
|
index += 2;
|
|
|
|
/* if any nonzero length field is absent, then none of the susequent fields are
|
|
* included
|
|
*/
|
|
if (rsse->pairwise_cs_count == 0)
|
|
goto exit;
|
|
|
|
rsse->pairwise_cs = &ptr[index];
|
|
index += rsse->pairwise_cs_count * 4;
|
|
|
|
if (index >= ptr_len)
|
|
goto exit;
|
|
|
|
/* AKM suite count */
|
|
rsse->akm_suite_count = ptr[index];
|
|
index += 2;
|
|
|
|
/* if any nonzero length field is absent, then none of the susequent fields are
|
|
* included
|
|
*/
|
|
if (rsse->akm_suite_count == 0)
|
|
goto exit;
|
|
|
|
rsse->akm_suite = &ptr[index];
|
|
index += rsse->akm_suite_count * 4;
|
|
|
|
if (index >= ptr_len)
|
|
goto exit;
|
|
|
|
/* RSN capabilities if RSN IE or OSEN IE*/
|
|
if (sme->crypto.wpa_versions == 2 || sme->crypto.wpa_versions == 0) {
|
|
/*get the PMF info from rsn capabilities*/
|
|
*mfp = FAPI_PMFCONTROL_DISABLED;
|
|
|
|
if (ptr[index] & (1 << 7))
|
|
*mfp = FAPI_PMFCONTROL_OPTIONAL;
|
|
if (ptr[index] & (1 << 6))
|
|
*mfp = FAPI_PMFCONTROL_MANDATORY;
|
|
|
|
index += 2;
|
|
if (index >= ptr_len)
|
|
goto exit;
|
|
|
|
/* PMKID count (only valid for wpa2) */
|
|
rsse->pmkid_count = ptr[index];
|
|
index += 2;
|
|
|
|
if (rsse->pmkid_count != 0) {
|
|
rsse->pmkid = &ptr[index];
|
|
index += rsse->pmkid_count * 16;
|
|
}
|
|
|
|
if (index >= ptr_len)
|
|
goto exit;
|
|
}
|
|
|
|
rsse->group_mgmt_cs = &ptr[index];
|
|
|
|
exit:
|
|
/* 6-> header, 4-> group_cs_count,pairwise_cs_count,akm_suite_count,pmkid_count ,4 -> group_mgmt_cs. */
|
|
return 6 + 4 + (rsse->group_cs_count + rsse->pairwise_cs_count + rsse->akm_suite_count) * 4 +
|
|
rsse->pmkid_count * 16 + 4;
|
|
}
|
|
|
|
/* 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(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;
|
|
const u8 *wpa_ie_pos = NULL;
|
|
int info_elem_length = 0;
|
|
u16 curr_ie_len;
|
|
u16 wpa_ie_len;
|
|
|
|
if (is_copy && (ie_dest == NULL || ie_dest_len == 0))
|
|
return -EINVAL;
|
|
|
|
/* find interworking ie id:107 */
|
|
ie_pos = cfg80211_find_ie(SLSI_WLAN_EID_INTERWORKING, connect_ie, connect_ie_len);
|
|
if (ie_pos != NULL) {
|
|
curr_ie_len = *(ie_pos + 1) + 2;
|
|
if (is_copy) {
|
|
if (ie_dest_len >= curr_ie_len) {
|
|
memcpy(ie_dest, ie_pos, curr_ie_len);
|
|
ie_dest += curr_ie_len;
|
|
ie_dest_len -= curr_ie_len; /* free space available in ie_dest for next ie */
|
|
} else {
|
|
SLSI_ERR_NODEV("interwork ie extract error (ie_copy_l:%d, c_ie_l:%d):\n", ie_dest_len, curr_ie_len);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
info_elem_length = curr_ie_len;
|
|
}
|
|
}
|
|
|
|
/* vendor specific IEs will be the last elements. */
|
|
ie_pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, connect_ie, connect_ie_len);
|
|
if (ie_pos != NULL) {
|
|
curr_ie_len = connect_ie_len - (ie_pos - connect_ie); /* length of all the vendor specific IEs */
|
|
/* WPA ie will be the first vendor specific ie since wpa_supplicant adds it as first element*/
|
|
wpa_ie_pos = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WPA, connect_ie, connect_ie_len);
|
|
if (wpa_ie_pos) {
|
|
wpa_ie_len = wpa_ie_pos[1] + 2; /* length of WPA vendor specific IE */
|
|
curr_ie_len -= wpa_ie_len;
|
|
ie_pos += wpa_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;
|
|
ie_dest_len -= curr_ie_len;
|
|
} else {
|
|
SLSI_ERR_NODEV("vendor ie extract error (ie_copy_l:%d, c_ie_l:%d):\n", ie_dest_len, curr_ie_len);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
info_elem_length += curr_ie_len;
|
|
}
|
|
}
|
|
|
|
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(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 == NULL)
|
|
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 == NULL) {
|
|
slsi_kfree_skb(req);
|
|
return -EINVAL;
|
|
}
|
|
|
|
(void)slsi_mlme_connect_info_elems_ie_prep(sme->ie, sme->ie_len, true, p, 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 == NULL)
|
|
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:%u) 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;
|
|
|
|
slsi_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;
|
|
struct slsi_mlme_rsse rsse;
|
|
int rsse_len = 0;
|
|
u8 mfp = FAPI_PMFCONTROL_DISABLED;
|
|
u8 mac_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
struct key_params slsi_key;
|
|
|
|
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_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 == NULL)
|
|
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_DEFAULT, 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;
|
|
|
|
rsse_len = slsi_mlme_connect_build_rsse(sme, &rsse, &mfp);
|
|
if (rsse_len < 0) {
|
|
SLSI_NET_ERR(dev, "ERROR preparing RSSE\n");
|
|
return rsse_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*/
|
|
rsse_len /*rsse*/);
|
|
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));
|
|
fapi_set_u16(req, u.mlme_connect_req.pmf_control, mfp);
|
|
|
|
p = fapi_append_data(req, NULL, 2 + sme->ssid_len + rsse_len);
|
|
if (!p) {
|
|
slsi_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 (rsse_len)
|
|
slsi_mlme_connect_rsse_copy(&rsse, p, rsse_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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_connect_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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.peer_index, 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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_disconnect_cfm.result_code));
|
|
|
|
exit:
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
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);
|
|
|
|
/* No data reference required */
|
|
req = fapi_alloc(mlme_disconnect_req, MLME_DISCONNECT_REQ, ndev_vif->ifnum, 0);
|
|
if (!req)
|
|
return -ENOMEM;
|
|
fapi_set_u16(req, u.mlme_disconnect_req.reason_code, reason_code);
|
|
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:%u) ERROR\n", fapi_get_u16(rx, u.mlme_disconnect_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
} else {
|
|
r = -EIO;
|
|
}
|
|
}
|
|
|
|
slsi_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 (sent as part of get_key_sequence).
|
|
* If sequence is say 0x01 0x02 0x03 0x04 with 0x01 as MSB and 0x04 as LSB then
|
|
* it would be encoded as: 0x0304 0x0102 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], 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");
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_setkeys_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
slsi_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:%u) 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;
|
|
}
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
static void slsi_mlme_fw_tx_rate_calc(u16 fw_rate, struct rate_info *tx_rate)
|
|
{
|
|
const int fw_rate_idx_to_80211_rate[] = { 0, 10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 };
|
|
|
|
tx_rate->flags = 0;
|
|
tx_rate->legacy = 0;
|
|
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 < sizeof(fw_rate_idx_to_80211_rate) / sizeof(fw_rate_idx_to_80211_rate[0]))
|
|
tx_rate->legacy = fw_rate_idx_to_80211_rate[fw_rate_idx];
|
|
} else if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_HT_SELECTED) {
|
|
tx_rate->flags |= RATE_INFO_FLAGS_MCS;
|
|
tx_rate->mcs = SLSI_FW_API_RATE_MCS_FIELD & fw_rate;
|
|
if ((fw_rate & SLSI_FW_API_RATE_BW_FIELD) == SLSI_FW_API_RATE_BW_40MHZ)
|
|
tx_rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
|
|
if (fw_rate & SLSI_FW_API_RATE_SGI)
|
|
tx_rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
|
|
} 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;
|
|
|
|
/* 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;
|
|
/* Ignore NSS. Only one stream is supported*/
|
|
mcs_idx = SLSI_FW_API_RATE_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))
|
|
tx_rate->legacy = slsi_mlme_vht_rates_table[chan_bw_idx][gi_idx][mcs_idx];
|
|
else
|
|
SLSI_WARN_NODEV(" FW DATA RATE decode error fw_rate:%x, bw:%x, mcs_idx:%x\n", fw_rate, chan_bw_idx, mcs_idx);
|
|
}
|
|
}
|
|
|
|
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;
|
|
int data_length = 0;
|
|
int r = 0;
|
|
static const struct slsi_mib_get_entry getValues[] = {
|
|
{ SLSI_PSID_UNIFI_TX_DATA_RATE, { 0, 0 } }, /* to get STATION_INFO_TX_BITRATE*/
|
|
{ SLSI_PSID_UNIFI_RSSI, { 0, 0 } }, /* to get STATION_INFO_SIGNAL_AVG*/
|
|
};
|
|
|
|
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
|
|
|
|
if (WARN_ON(!peer)) {
|
|
SLSI_NET_DBG1(dev, SLSI_MLME, "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(getValues) / sizeof(struct slsi_mib_get_entry)),
|
|
getValues);
|
|
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 bytes for SLSI_PSID_UNIFI_TX_DATA_RATE , 1 byte for SLSI_PSID_UNIFI_RSSI
|
|
*/
|
|
mibrsp.dataLength = 14;
|
|
mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL);
|
|
|
|
if (mibrsp.data == NULL) {
|
|
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(getValues) / sizeof(struct slsi_mib_get_entry)), getValues);
|
|
if (values == NULL) {
|
|
SLSI_NET_DBG1(dev, SLSI_MLME, "mib decode list failed\n");
|
|
kfree(mibrsp.data);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (values[0].type != SLSI_MIB_TYPE_NONE) {
|
|
WARN_ON(values[0].type != SLSI_MIB_TYPE_UINT);
|
|
slsi_mlme_fw_tx_rate_calc((u16)values[0].u.uintValue, &peer->sinfo.txrate);
|
|
peer->sinfo.filled |= STATION_INFO_TX_BITRATE;
|
|
SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_TX_DATA_RATE = %d\n",
|
|
values[0].u.uintValue);
|
|
}
|
|
|
|
if (values[1].type != SLSI_MIB_TYPE_NONE) {
|
|
WARN_ON(values[1].type != SLSI_MIB_TYPE_INT);
|
|
peer->sinfo.signal = (s8)values[1].u.intValue;
|
|
peer->sinfo.filled |= STATION_INFO_SIGNAL;
|
|
SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_RSSI = %d\n",
|
|
values[1].u.intValue);
|
|
}
|
|
} else {
|
|
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_get_req failed(result:%u)\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;
|
|
|
|
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);
|
|
cfg80211_scan_done(ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req, true);
|
|
ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req = NULL;
|
|
}
|
|
|
|
if (channel == NULL) {
|
|
enum ieee80211_band band;
|
|
struct wiphy *wiphy = sdev->wiphy;
|
|
|
|
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
|
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 == NULL) {
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
|
|
return -ENOMEM;
|
|
}
|
|
n_channels = 0;
|
|
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
|
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;
|
|
}
|
|
r = slsi_mlme_add_scan(sdev,
|
|
dev,
|
|
FAPI_SCANTYPE_FULL_SCAN,
|
|
FAPI_REPORTMODE_REAL_TIME,
|
|
n_ssids,
|
|
ssids,
|
|
n_channels,
|
|
add_scan_channels,
|
|
NULL,
|
|
sdev->gc_probe_req_ies,
|
|
sdev->gc_probe_req_ie_len,
|
|
true);
|
|
|
|
scan = slsi_skb_dequeue(&ndev_vif->scan[SLSI_SCAN_HW_ID].scan_results);
|
|
while (scan) {
|
|
slsi_rx_scan_pass_to_cfg80211(sdev, dev, scan);
|
|
scan = slsi_skb_dequeue(&ndev_vif->scan[SLSI_SCAN_HW_ID].scan_results);
|
|
}
|
|
|
|
kfree(scan_channels);
|
|
|
|
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* The powermgt_lock mutex is to ensure atomic update of the power management state.
|
|
*/
|
|
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:%u) ERROR\n", fapi_get_u16(rx, u.mlme_powermgt_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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);
|
|
}
|
|
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_register_action_frame_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_channel_switch_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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) {
|
|
slsi_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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_add_info_elements_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
int slsi_mlme_send_frame_data(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, u16 host_tag , u16 msg_type, u32 period)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
u16 len = skb->len;
|
|
struct sk_buff *original_skb = 0;
|
|
int ret;
|
|
|
|
/* 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 = slsi_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_host_tag(sdev);
|
|
|
|
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, 0);
|
|
fapi_set_u32(skb, u.mlme_send_frame_req.period, period);
|
|
|
|
SLSI_DBG2(sdev, SLSI_MLME, "mlme_send_frame_req(vif:%d, message_type:%d, host_tag:%d)\n", ndev_vif->ifnum, msg_type, host_tag);
|
|
ret = slsi_tx_control(sdev, dev, skb);
|
|
if (ret != 0) {
|
|
SLSI_WARN(sdev, "failed to send MLME signal(err=%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (original_skb)
|
|
slsi_kfree_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) {
|
|
slsi_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);
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_send_frame_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_reset_dwell_time_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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) {
|
|
slsi_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;
|
|
|
|
if (fapi_get_u16(cfm, u.mlme_set_packet_filter_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
|
|
SLSI_NET_ERR(dev, "mlme_set_packet_filter_cfm(result:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_set_packet_filter_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_set_pmk_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
int slsi_mlme_roam(struct slsi_dev *sdev, struct net_device *dev, 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));
|
|
|
|
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_ROAM_CFM);
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_roam_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
slsi_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");
|
|
slsi_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) {
|
|
slsi_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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_set_cached_channels_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
int slsi_mlme_set_cckm_key(struct slsi_dev *sdev, struct net_device *dev, u8 *gk_key)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
int r = 0;
|
|
struct sk_buff *req;
|
|
struct sk_buff *cfm;
|
|
/* According to CCX Lite Foundation paragraph F5.5
|
|
* GK is 384 bits (48 bytes) from which BTK and KRK are derived as following
|
|
* KRK = GK[0-127]
|
|
* BTK = GK[128:383]
|
|
*/
|
|
int cckm_key_length = 48;
|
|
|
|
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
|
|
req = fapi_alloc(mlme_set_cckm_keys_req, MLME_SET_CCKM_KEYS_REQ, ndev_vif->ifnum, cckm_key_length);
|
|
if (!req)
|
|
return -ENOMEM;
|
|
|
|
fapi_append_data(req, gk_key, cckm_key_length);
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_set_cckm_keys_req(vif:%d)\n", ndev_vif->ifnum);
|
|
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_SET_CCKM_KEYS_CFM);
|
|
if (!cfm)
|
|
return -EIO;
|
|
|
|
if (fapi_get_u16(cfm, u.mlme_set_cckm_keys_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
|
|
SLSI_NET_ERR(dev, "mlme_set_cckm_keys_cfm(result:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_set_cckm_keys_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
slsi_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, const struct cfg80211_acl_data *params)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct sk_buff *req;
|
|
struct sk_buff *cfm;
|
|
size_t mac_acl_size = 0;
|
|
int i, r = 0;
|
|
|
|
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
|
|
|
|
mac_acl_size = sizeof((params->mac_addrs[0])) * (params->n_acl_entries);
|
|
req = fapi_alloc(mlme_set_acl_req, MLME_SET_ACL_REQ, ndev_vif->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, params->n_acl_entries);
|
|
fapi_set_u16(req, u.mlme_set_acl_req.acl_policy, params->acl_policy);
|
|
|
|
for (i = 0; i < params->n_acl_entries; i++)
|
|
fapi_append_data(req, params->mac_addrs[i].addr, sizeof((params->mac_addrs[i])));
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_set_acl_req(vif:%u, n_acl_entries:%d, mac_addrs[0]:%pM)\n", ndev_vif->ifnum, params->n_acl_entries, params->mac_addrs[0].addr);
|
|
cfm = slsi_mlme_req_cfm(sdev, dev, 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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_set_acl_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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.minimun_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:%u) 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:%u) 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, struct slsi_mib_value *mib_val)
|
|
{
|
|
struct slsi_mib_data mib_data = { 0, NULL };
|
|
int error = 0;
|
|
|
|
error = slsi_mib_encode_octet(&mib_data, SLSI_PSID_UNIFI_EXTENDED_CAPABILITIES, mib_val->u.octetValue.dataLength, mib_val->u.octetValue.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_set_hs2_ext_cap(struct slsi_dev *sdev, struct net_device *dev, const u8 *ies, int ie_len)
|
|
{
|
|
struct slsi_mib_entry mib_entry;
|
|
struct slsi_mib_data mibreq = { 0, NULL };
|
|
struct slsi_mib_data mibrsp = { 0, NULL };
|
|
const u8 *ext_capab_ie;
|
|
int r = 0;
|
|
int rx_length = 0;
|
|
int len = 0;
|
|
|
|
slsi_mib_encode_get(&mibreq, SLSI_PSID_UNIFI_EXTENDED_CAPABILITIES, 0);
|
|
|
|
/* 5 (header) + 9 (data) + 2 (mlme expects 16 (??))*/
|
|
mibrsp.dataLength = 16;
|
|
mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL);
|
|
|
|
if (mibrsp.data == NULL) {
|
|
SLSI_ERR(sdev, "Failed to alloc for Mib response\n");
|
|
kfree(mibreq.data);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
r = slsi_mlme_get(sdev, NULL, mibreq.data, mibreq.dataLength,
|
|
mibrsp.data, mibrsp.dataLength, &rx_length);
|
|
kfree(mibreq.data);
|
|
|
|
if (r == 0) {
|
|
mibrsp.dataLength = rx_length;
|
|
len = slsi_mib_decode(&mibrsp, &mib_entry);
|
|
if (len == 0) {
|
|
SLSI_ERR(sdev, "Mib decode error\n");
|
|
r = -EINVAL;
|
|
goto exit;
|
|
}
|
|
} else {
|
|
SLSI_NET_DBG1(dev, SLSI_MLME, "Mib read failed (error: %d)\n", r);
|
|
goto exit;
|
|
}
|
|
|
|
ext_capab_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ie_len);
|
|
|
|
if (ext_capab_ie) {
|
|
u8 ext_capab_ie_len = ext_capab_ie[1];
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_MLME, "ext_capab_ie_len = %d, Ext capab :0x%2x,0x%2x,0x%2x,0x%2x\n",
|
|
ext_capab_ie_len, ext_capab_ie[2], ext_capab_ie[3], ext_capab_ie[4], ext_capab_ie[5]);
|
|
|
|
ext_capab_ie += 2; /* skip the EID and length*/
|
|
|
|
/*BSS Transition bit is bit 19 ,ie length must be >= 3 */
|
|
if ((ext_capab_ie_len >= 3) && (ext_capab_ie[2] & SLSI_WLAN_EXT_CAPA2_BSS_TRANSISITION_ENABLED))
|
|
mib_entry.value.u.octetValue.data[2] |= SLSI_WLAN_EXT_CAPA2_BSS_TRANSISITION_ENABLED;
|
|
else
|
|
mib_entry.value.u.octetValue.data[2] &= ~SLSI_WLAN_EXT_CAPA2_BSS_TRANSISITION_ENABLED;
|
|
|
|
/*interworking bit is bit 31 ,ie length must be >= 4 */
|
|
if ((ext_capab_ie_len >= 4) && (ext_capab_ie[3] & SLSI_WLAN_EXT_CAPA3_INTERWORKING_ENABLED))
|
|
mib_entry.value.u.octetValue.data[3] |= SLSI_WLAN_EXT_CAPA3_INTERWORKING_ENABLED;
|
|
else
|
|
mib_entry.value.u.octetValue.data[3] &= ~SLSI_WLAN_EXT_CAPA3_INTERWORKING_ENABLED;
|
|
|
|
/*QoS MAP is bit 32 ,ie length must be >= 5 */
|
|
if ((ext_capab_ie_len >= 5) && (ext_capab_ie[4] & SLSI_WLAN_EXT_CAPA4_QOS_MAP_ENABLED))
|
|
mib_entry.value.u.octetValue.data[4] |= SLSI_WLAN_EXT_CAPA4_QOS_MAP_ENABLED;
|
|
else
|
|
mib_entry.value.u.octetValue.data[4] &= ~SLSI_WLAN_EXT_CAPA4_QOS_MAP_ENABLED;
|
|
|
|
/*WNM- Notification bit is bit 46 ,ie length must be >= 6 */
|
|
if ((ext_capab_ie_len >= 6) && (ext_capab_ie[5] & SLSI_WLAN_EXT_CAPA5_WNM_NOTIF_ENABLED))
|
|
mib_entry.value.u.octetValue.data[5] |= SLSI_WLAN_EXT_CAPA5_WNM_NOTIF_ENABLED;
|
|
else
|
|
mib_entry.value.u.octetValue.data[5] &= ~SLSI_WLAN_EXT_CAPA5_WNM_NOTIF_ENABLED;
|
|
} else {
|
|
mib_entry.value.u.octetValue.data[2] &= ~SLSI_WLAN_EXT_CAPA2_BSS_TRANSISITION_ENABLED;
|
|
mib_entry.value.u.octetValue.data[3] &= ~SLSI_WLAN_EXT_CAPA3_INTERWORKING_ENABLED;
|
|
mib_entry.value.u.octetValue.data[4] &= ~SLSI_WLAN_EXT_CAPA4_QOS_MAP_ENABLED;
|
|
mib_entry.value.u.octetValue.data[5] &= ~SLSI_WLAN_EXT_CAPA5_WNM_NOTIF_ENABLED;
|
|
}
|
|
|
|
r = slsi_mlme_set_ext_capab(sdev, dev, &mib_entry.value);
|
|
exit:
|
|
kfree(mibrsp.data);
|
|
return r;
|
|
}
|
|
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_tdls_action_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_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%4x)\n", fapi_get_u16(cfm, u.mlme_reassociate_cfm.result_code));
|
|
} else {
|
|
SLSI_NET_ERR(dev, "mlme_reassoc_cfm(result:0x%4x) ERROR\n", fapi_get_u16(cfm, u.mlme_reassociate_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
slsi_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_blockack_control_req(struct slsi_dev *sdev, struct net_device *dev, u16 blockack_control_bitmap, u16 direction, const u8 *peer_sta_address)
|
|
{
|
|
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 (!peer_sta_address) {
|
|
SLSI_NET_WARN(dev, "INVALID PEER ADDRESS");
|
|
return -EINVAL;
|
|
}
|
|
|
|
req = fapi_alloc(mlme_blockack_control_req, MLME_BLOCKACK_CONTROL_REQ, ndev_vif->ifnum, 0);
|
|
if (!req) {
|
|
SLSI_NET_ERR(dev, "memory allocation failed for blockack control request\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
fapi_set_u16(req, u.mlme_blockack_control_req.blockack_control_bitmap, blockack_control_bitmap);
|
|
fapi_set_u16(req, u.mlme_blockack_control_req.direction, direction);
|
|
fapi_set_memcpy(req, u.mlme_blockack_control_req.peer_sta_address, peer_sta_address);
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_blockack_control_req(vif:%u, direction:%d, blockack_control_bitmap:%d)\n", ndev_vif->ifnum, direction, blockack_control_bitmap);
|
|
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_BLOCKACK_CONTROL_CFM);
|
|
if (!cfm)
|
|
return -EIO;
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_blockack_control_cfm: peer=%pM up=%X dir=%X\n", peer_sta_address, blockack_control_bitmap, direction);
|
|
|
|
if (fapi_get_u16(cfm, u.mlme_blockack_control_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
|
|
SLSI_NET_ERR(dev, "mlme_blockack_control_cfm (result: %u) ERROR\n", fapi_get_u16(cfm, u.mlme_blockack_control_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_OXYGEN_ENABLE
|
|
int slsi_mlme_start_ibss(struct slsi_dev *sdev, struct net_device *dev, const u8 *bssid, struct cfg80211_ibss_params *params)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct sk_buff *req;
|
|
struct sk_buff *cfm;
|
|
int r = 0;
|
|
u8 *p;
|
|
u16 chan_info;
|
|
u16 fw_freq;
|
|
|
|
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));
|
|
|
|
if (WARN_ON(!params->ssid_len || !params->ssid))
|
|
return -EINVAL;
|
|
if (WARN_ON(!params->beacon_interval))
|
|
return -EINVAL;
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_start_req(vif:%u, bssid:%pM, ssid:%.*s : hidden:%d)\n", ndev_vif->ifnum, bssid, (int)params->ssid_len, params->ssid, 0);
|
|
|
|
req = fapi_alloc(mlme_start_req, MLME_START_REQ, ndev_vif->ifnum, params->ie_len);
|
|
params->beacon_interval = 0x64;
|
|
if (!req)
|
|
return -ENOMEM;
|
|
fapi_set_memcpy(req, u.mlme_start_req.bssid, bssid);
|
|
fapi_set_u16(req, u.mlme_start_req.beacon_period, params->beacon_interval);
|
|
fapi_set_u16(req, u.mlme_start_req.dtim_period, 2);
|
|
/* Oxygen IOP with QCOM */
|
|
fapi_set_u16(req, u.mlme_start_req.capability_information, WLAN_CAPABILITY_IBSS | (params->privacy ? WLAN_CAPABILITY_PRIVACY : 0));
|
|
fapi_set_u16(req, u.mlme_start_req.authentication_type, NL80211_AUTHTYPE_OPEN_SYSTEM);
|
|
fapi_set_u16(req, u.mlme_start_req.hidden_ssid, 0);
|
|
|
|
#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
|
|
|
|
fw_freq = ndev_vif->chan->center_freq;
|
|
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);
|
|
|
|
p = fapi_append_data(req, NULL, (size_t)params->ie_len);
|
|
if (!p) {
|
|
slsi_kfree_skb(req);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memcpy(p, params->ie, params->ie_len);
|
|
|
|
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:%u) ERROR\n", fapi_get_u16(cfm, u.mlme_start_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
int slsi_mlme_ibss_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;
|
|
int dataLength = 0;
|
|
int r = 0;
|
|
u16 aid = peer->aid;
|
|
struct slsi_mib_get_entry getValues[] = { { SLSI_PSID_UNIFI_PEER_AVERAGE_TX_DATA_RATE, { aid } }, /* to get Average TX Data Rate */
|
|
{ SLSI_PSID_UNIFI_PEER_RSSI, { aid } }, /* to get STATION_INFO_SIGNAL_AVG*/ };
|
|
|
|
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
|
|
|
|
if (WARN_ON(!peer)) {
|
|
SLSI_NET_ERR(dev, "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(getValues) / sizeof(struct slsi_mib_get_entry)),
|
|
getValues);
|
|
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 : 4 bytes for SLSI_PSID_UNIFI_TX_DATA_RATE , 2 byte for SLSI_PSID_UNIFI_RSSI
|
|
*/
|
|
mibrsp.dataLength = 16;
|
|
mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL);
|
|
|
|
if (mibrsp.data == NULL) {
|
|
SLSI_NET_ERR(dev, "failed to allocate memory\n");
|
|
kfree(mibreq.data);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
r = slsi_mlme_get(sdev, dev, mibreq.data, mibreq.dataLength, mibrsp.data,
|
|
mibrsp.dataLength, &dataLength);
|
|
kfree(mibreq.data);
|
|
|
|
if (r == 0) {
|
|
mibrsp.dataLength = (u32)dataLength;
|
|
values = slsi_mib_decode_get_list(&mibrsp, sizeof(getValues) / sizeof(struct slsi_mib_get_entry), getValues);
|
|
|
|
if (values == NULL) {
|
|
SLSI_NET_ERR(dev, "mib decode list failed\n");
|
|
kfree(mibrsp.data);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (values[0].type != SLSI_MIB_TYPE_NONE) {
|
|
WARN_ON(values[0].type != SLSI_MIB_TYPE_UINT);
|
|
|
|
if ((u16)values[0].u.uintValue != 0)
|
|
peer->txrate = (u16)values[0].u.uintValue;
|
|
SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_TX_DATA_RATE = %hu\n",
|
|
peer->txrate);
|
|
}
|
|
|
|
if (values[1].type != SLSI_MIB_TYPE_NONE) {
|
|
WARN_ON(values[1].type != SLSI_MIB_TYPE_INT);
|
|
peer->signal = (s8)values[1].u.intValue;
|
|
|
|
SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_RSSI = %hhd\n",
|
|
peer->signal);
|
|
}
|
|
} else {
|
|
SLSI_NET_ERR(dev, "mlme_get_req failed(result:%u)\n", r);
|
|
}
|
|
|
|
kfree(mibrsp.data);
|
|
kfree(values);
|
|
return r;
|
|
}
|
|
|
|
int slsi_mlme_enable_ibss_rmc(struct slsi_dev *sdev, struct net_device *dev, const u8 *mcast_addr)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct sk_buff *req = NULL;
|
|
struct sk_buff *cfm = NULL;
|
|
int r = 0;
|
|
|
|
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
|
|
|
|
if (WARN_ON(ndev_vif->vif_type != FAPI_VIFTYPE_ADHOC))
|
|
return -EINVAL;
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_start_txrmc_req(vif:%d) mcast_addr=%pM\n", ndev_vif->ifnum, mcast_addr);
|
|
req = fapi_alloc(mlme_start_txrmc_req, MLME_START_TXRMC_REQ, ndev_vif->ifnum, 0);
|
|
if (!req)
|
|
return -ENOMEM;
|
|
|
|
fapi_set_memcpy(req, u.mlme_start_txrmc_req.multicast_address, mcast_addr);
|
|
|
|
/* TODO: To be fixed slsi_ps_vif_set_multicast_q_state(sdev, dev, false); */
|
|
if (!memcmp(mcast_addr, (const u8 *)SLSI_IBSS_RMC_LEADER, ETH_ALEN))
|
|
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_START_TXRMC_CFM);
|
|
/* TODO: To be fixed slsi_ps_vif_set_multicast_q_state(sdev, dev, true); */
|
|
if (!cfm)
|
|
return -EIO;
|
|
|
|
r = fapi_get_u16(cfm, u.mlme_start_txrmc_cfm.result_code);
|
|
if (r != FAPI_RESULTCODE_SUCCESS) {
|
|
SLSI_ERR(sdev, "Failed mlme_start_txrmc_cfm: %u\n", r);
|
|
slsi_eth_zero_addr(ndev_vif->ibss.rmc_addr);
|
|
/* TODO: To be fixed slsi_ps_vif_set_multicast_q_state(sdev, dev, true); */
|
|
}
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
|
|
int slsi_mlme_disable_ibss_rmc(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->vif_type != FAPI_VIFTYPE_ADHOC))
|
|
return -EINVAL;
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_stop_txrmc_req(vif:%d)\n", ndev_vif->ifnum);
|
|
req = fapi_alloc(mlme_stop_txrmc_req, MLME_STOP_TXRMC_REQ, ndev_vif->ifnum, 0);
|
|
if (!req)
|
|
return -ENOMEM;
|
|
|
|
slsi_eth_zero_addr(ndev_vif->ibss.rmc_addr);
|
|
|
|
cfm = slsi_mlme_req_cfm(sdev, dev, req, MLME_STOP_TXRMC_CFM);
|
|
if (!cfm)
|
|
return -EIO;
|
|
|
|
r = fapi_get_u16(cfm, u.mlme_stop_txrmc_cfm.result_code);
|
|
if (r != FAPI_RESULTCODE_SUCCESS)
|
|
SLSI_ERR(sdev, "Failed mlme_stop_txrmc_cfm: %u\n", r);
|
|
|
|
slsi_kfree_skb(cfm);
|
|
return r;
|
|
}
|
|
#endif /* CONFIG_SCSC_WLAN_OXYGEN_ENABLE */
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
|
|
int slsi_mlme_gscan_append_tracking_channels(struct slsi_dev *sdev,
|
|
struct net_device *dev,
|
|
struct sk_buff *skb,
|
|
struct slsi_gscan *gscan)
|
|
{
|
|
u32 n_channels = 0;
|
|
int len = 0;
|
|
|
|
if (gscan->nl_bucket.band == WIFI_BAND_UNSPECIFIED)
|
|
n_channels = gscan->nl_bucket.num_channels;
|
|
else
|
|
n_channels = 1;
|
|
|
|
SLSI_DBG3(sdev, SLSI_GSCAN, "num chan: %d", n_channels);
|
|
if (skb) {
|
|
int r = slsi_mlme_append_gscan_channel_list(sdev, dev, skb, &gscan->nl_bucket);
|
|
|
|
if (r) {
|
|
SLSI_ERR(sdev, "Error appending channel list [%d]\n", r);
|
|
return r;
|
|
}
|
|
} else {
|
|
len = SLSI_SCAN_PRIVATE_IE_CHANNEL_LIST_HEADER_LEN + (n_channels * SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int slsi_mlme_significant_change_set(struct slsi_dev *sdev, struct net_device *dev,
|
|
struct slsi_nl_significant_change_params *significant_param_ptr)
|
|
{
|
|
struct sk_buff *req;
|
|
struct sk_buff *rx;
|
|
int r = 0;
|
|
size_t alloc_data_size;
|
|
u32 i, j;
|
|
u32 num_bssid_descriptor_ies;
|
|
struct slsi_gscan *gscan = sdev->gscan;
|
|
|
|
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 */
|
|
};
|
|
|
|
u8 change_tracking_ie[] = { /* wifi change tracking ie*/
|
|
0xdd, 0x07, /* VENDOR_SPECIFIC, LEN */
|
|
0x00, 0x16, 0x32, /* OUI */
|
|
0x01, 0x06, /* OUI Type, OUI sub-type */
|
|
0x00, /* The number of samples needed for averaging RSSI */
|
|
0x00 /* Min num of APs breaching low_RSSI_Threshold that are needed before informing the host */
|
|
};
|
|
|
|
u8 bssid_descriptor_ie[] = { /* Bssid descriptor list ie*/
|
|
0xdd, 6 + (SLSI_FW_MAX_BSSID_PER_TRACKING_IE * SLSI_FW_BSSID_DESCRIPTOR_BSS_SIZE), /* VENDOR_SPECIFIC, LEN(update later) */
|
|
0x00, 0x16, 0x32, /* OUI */
|
|
0x01, 0x05, /* OUI Type, OUI sub-type */
|
|
0x00, /* ap lost threshold */
|
|
};
|
|
|
|
if (slsi_is_test_mode_enabled()) {
|
|
SLSI_NET_INFO(dev, "Skip sending signal, WlanLite FW does not support MLME_ADD_SCAN.request\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
if (WARN_ON(!(dev->dev_addr)))
|
|
return -EINVAL;
|
|
|
|
change_tracking_ie[7] = (u8)significant_param_ptr->rssi_sample_size;
|
|
change_tracking_ie[8] = (u8)significant_param_ptr->min_breaching;
|
|
num_bssid_descriptor_ies = significant_param_ptr->num_bssid / SLSI_FW_MAX_BSSID_PER_TRACKING_IE;
|
|
if (significant_param_ptr->num_bssid % SLSI_FW_MAX_BSSID_PER_TRACKING_IE)
|
|
num_bssid_descriptor_ies++;
|
|
|
|
alloc_data_size = sizeof(scan_timing_ie) + sizeof(change_tracking_ie)
|
|
+ sizeof(bssid_descriptor_ie) * num_bssid_descriptor_ies
|
|
+ SLSI_FW_BSSID_DESCRIPTOR_BSS_SIZE * significant_param_ptr->num_bssid
|
|
+ slsi_mlme_gscan_append_tracking_channels(sdev, dev, NULL, gscan);
|
|
|
|
req = fapi_alloc(mlme_add_scan_req, MLME_ADD_SCAN_REQ, 0, alloc_data_size);
|
|
if (!req)
|
|
return -ENOMEM;
|
|
|
|
fapi_set_u16(req, u.mlme_add_scan_req.scan_id, gscan->bucket[0]->scan_id);
|
|
fapi_set_u16(req, u.mlme_add_scan_req.scan_type, FAPI_SCANTYPE_GSCAN);
|
|
fapi_set_memcpy(req, u.mlme_add_scan_req.device_address, dev->dev_addr);
|
|
fapi_set_u16(req, u.mlme_add_scan_req.report_mode_bitmap, FAPI_REPORTMODE_REAL_TIME);
|
|
|
|
r = slsi_mlme_gscan_append_tracking_channels(sdev, dev, req, gscan);
|
|
if (r != 0) {
|
|
slsi_kfree_skb(req);
|
|
return r;
|
|
}
|
|
|
|
SLSI_U32_TO_BUFF_LE((gscan->nl_bucket.period * 1000), &scan_timing_ie[7]);
|
|
fapi_append_data(req, scan_timing_ie, sizeof(scan_timing_ie));
|
|
fapi_append_data(req, change_tracking_ie, sizeof(change_tracking_ie));
|
|
|
|
bssid_descriptor_ie[7] = significant_param_ptr->lost_ap_sample_size;
|
|
for (i = 0; i < num_bssid_descriptor_ies; i++) {
|
|
u8 bss_num = significant_param_ptr->num_bssid - (i * SLSI_FW_MAX_BSSID_PER_TRACKING_IE);
|
|
|
|
if (bss_num < SLSI_FW_MAX_BSSID_PER_TRACKING_IE)
|
|
bssid_descriptor_ie[1] = 6 + (SLSI_FW_BSSID_DESCRIPTOR_BSS_SIZE * bss_num);
|
|
else
|
|
bss_num = SLSI_FW_MAX_BSSID_PER_TRACKING_IE;
|
|
|
|
fapi_append_data(req, bssid_descriptor_ie, sizeof(bssid_descriptor_ie));
|
|
for (j = i * SLSI_FW_MAX_BSSID_PER_TRACKING_IE; j < (i * SLSI_FW_MAX_BSSID_PER_TRACKING_IE + bss_num); j++) {
|
|
fapi_append_data(req, significant_param_ptr->ap[j].bssid, ETH_ALEN);
|
|
fapi_append_data(req, (u8 *)&significant_param_ptr->ap[j].low, 2);
|
|
fapi_append_data(req, (u8 *)&significant_param_ptr->ap[j].high, 2);
|
|
}
|
|
}
|
|
|
|
/* 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) {
|
|
gscan->bucket[0]->for_change_tracking = false;
|
|
return -EIO;
|
|
} else if (fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
|
|
gscan->bucket[0]->for_change_tracking = false;
|
|
SLSI_NET_ERR(dev, "mlme_add_scan_cfm(ERROR:%u)", fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code));
|
|
r = -EINVAL;
|
|
} else {
|
|
gscan->bucket[0]->for_change_tracking = true;
|
|
}
|
|
slsi_kfree_skb(rx);
|
|
|
|
return r;
|
|
}
|
|
|
|
#define SLSI_FAPI_EPNO_NETWORK_MIN_SIZE (6)
|
|
int slsi_mlme_set_pno_list(struct slsi_dev *sdev, int count,
|
|
struct slsi_epno_ssid_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;
|
|
|
|
/* calculate data size */
|
|
if (epno_param) {
|
|
alloc_data_size += sizeof(fapi_ie_generic) + SLSI_FAPI_EPNO_NETWORK_MIN_SIZE * count + 1;
|
|
for (i = 0; i < count; i++)
|
|
alloc_data_size += epno_param[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 (strlen(epno_hs2_param->realm))
|
|
alloc_data_size += sizeof(fapi_ie_generic) + 1 + 1 + (strlen(epno_hs2_param->realm) + 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;
|
|
}
|
|
|
|
/* Fill data */
|
|
if (epno_param) {
|
|
u8 null_terminator = 0;
|
|
|
|
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 *)&count, 1);
|
|
for (i = 0; i < count; i++) {
|
|
epno_param[i].ssid_len++;
|
|
fapi_append_data(req, (u8 *)&epno_param[i].ssid_len, 1);
|
|
fapi_append_data(req, epno_param[i].ssid, epno_param[i].ssid_len - 1);
|
|
fapi_append_data(req, &null_terminator, 1);
|
|
fapi_append_data(req, (u8 *)&epno_param[i].rssi_thresh, 2);
|
|
fapi_append_data(req, (u8 *)&epno_param[i].flags, 2);
|
|
}
|
|
} 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");
|
|
slsi_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");
|
|
slsi_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_add_scan_cfm.result_code) != FAPI_RESULTCODE_SUCCESS) {
|
|
SLSI_ERR(sdev, "mlme_set_pno_list_cfm(ERROR:%u)", fapi_get_u16(rx, u.mlme_add_scan_cfm.result_code));
|
|
r = -EINVAL;
|
|
}
|
|
slsi_kfree_skb(rx);
|
|
|
|
return r;
|
|
}
|
|
#endif
|