luckfox-pico-sdk/sysdrv/drv_ko/wifi/ssv6115/fmac/fmac_msg_tx.c
luckfox-eng29 8f34c2760d project:build.sh: Added fastboot support; custom modifications to U-Boot and kernel implemented using patches.
project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between
the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door
for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more
usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp:
Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for
compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem.
sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device
tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately.
sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick
boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades.
sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for
fastboot.

Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
2024-10-14 09:47:04 +08:00

2107 lines
71 KiB
C

/*
* Copyright (c) 2021 iComm-semi Ltd.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* @file fmac_msg_tx.c
* @brief TX function definitions
*/
/*******************************************************************************
* Include Files
******************************************************************************/
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kthread.h>
#include "ssvdevice/rftool/ssv_efuse.h"
#include "ssv_cfg.h"
#include "lmac_types.h"
#include "lmac_mac.h"
#include "lmac_msg.h"
#include "fmac.h"
#include "fmac_utils.h"
#include "fmac_msg_tx.h"
#include "ipc_msg.h"
#include "hci/drv_hci_ops.h"
#include "ssv_debug.h"
/*******************************************************************************
* Local Defines
******************************************************************************/
/*******************************************************************************
* Local Enumerations
******************************************************************************/
/*******************************************************************************
* Local Structures
******************************************************************************/
/*******************************************************************************
* Global Variables
******************************************************************************/
extern struct ssv6xxx_cfg ssv_cfg;
/*******************************************************************************
* Local Variables
******************************************************************************/
const struct mac_addr mac_addr_bcst = {{0xFFFF, 0xFFFF, 0xFFFF}};
static const int bw2chnl[] = {
[NL80211_CHAN_WIDTH_20_NOHT] = PHY_CHNL_BW_20,
[NL80211_CHAN_WIDTH_20] = PHY_CHNL_BW_20,
[NL80211_CHAN_WIDTH_40] = PHY_CHNL_BW_40,
[NL80211_CHAN_WIDTH_80] = PHY_CHNL_BW_80,
[NL80211_CHAN_WIDTH_160] = PHY_CHNL_BW_160,
[NL80211_CHAN_WIDTH_80P80] = PHY_CHNL_BW_80P80,
};
static const int chnl2bw[] = {
[PHY_CHNL_BW_20] = NL80211_CHAN_WIDTH_20,
[PHY_CHNL_BW_40] = NL80211_CHAN_WIDTH_40,
[PHY_CHNL_BW_80] = NL80211_CHAN_WIDTH_80,
[PHY_CHNL_BW_160] = NL80211_CHAN_WIDTH_160,
[PHY_CHNL_BW_80P80] = NL80211_CHAN_WIDTH_80P80,
};
/*******************************************************************************
* Local Functions
******************************************************************************/
/*****************************************************************************/
/*
* Parse the ampdu density to retrieve the value in usec, according to the
* values defined in ieee80211.h
*/
static inline u8 ssv_ampdudensity2usec(u8 ampdudensity)
{
switch (ampdudensity) {
case IEEE80211_HT_MPDU_DENSITY_NONE:
return 0;
/* 1 microsecond is our granularity */
case IEEE80211_HT_MPDU_DENSITY_0_25:
case IEEE80211_HT_MPDU_DENSITY_0_5:
case IEEE80211_HT_MPDU_DENSITY_1:
return 1;
case IEEE80211_HT_MPDU_DENSITY_2:
return 2;
case IEEE80211_HT_MPDU_DENSITY_4:
return 4;
case IEEE80211_HT_MPDU_DENSITY_8:
return 8;
case IEEE80211_HT_MPDU_DENSITY_16:
return 16;
default:
return 0;
}
}
static inline bool _ssv_use_pairwise_key(struct cfg80211_crypto_settings *crypto)
{
if ((crypto->cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
(crypto->cipher_group == WLAN_CIPHER_SUITE_WEP104))
return false;
return true;
}
static inline bool _ssv_is_non_blocking_msg(int id) {
return ((id == MM_TIM_UPDATE_REQ) || (id == ME_RC_SET_RATE_REQ) ||
(id == ME_TRAFFIC_IND_REQ));
}
static inline uint8_t _ssv_passive_scan_flag(uint32_t flags) {
SSV_LOG_ERR("%s: return 0\n", __func__);
return 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
if (flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))
#else
if (flags & (IEEE80211_CHAN_RADAR))
#endif
return SCAN_PASSIVE_BIT;
return 0;
}
static inline void ssv_msg_free(void *pParam)
{
struct lmac_msg *msg = NULL;
msg = container_of((void *)pParam, struct lmac_msg, param);
if (msg)
kfree(msg);
}
/**
******************************************************************************
* @brief Allocate memory for a message
*
* This primitive allocates memory for a message that has to be sent. The memory
* is allocated dynamically on the heap and the length of the variable parameter
* structure has to be provided in order to allocate the correct size.
*
* Several additional parameters are provided which will be preset in the message
* and which may be used internally to choose the kind of memory to allocate.
*
* The memory allocated will be automatically freed by the kernel, after the
* pointer has been sent to ke_msg_send(). If the message is not sent, it must
* be freed explicitly with ke_msg_free().
*
* Allocation failure is considered critical and should not happen.
*
* @param[in] id Message identifier
* @param[in] dest_id Destination Task Identifier
* @param[in] src_id Source Task Identifier
* @param[in] param_len Size of the message parameters to be allocated
*
* @return Pointer to the parameter member of the ke_msg. If the parameter
* structure is empty, the pointer will point to the end of the message
* and should not be used (except to retrieve the message pointer or to
* send the message)
******************************************************************************
*/
static inline void *ssv_msg_zalloc(lmac_msg_id_t const id,
lmac_task_id_t const dest_id,
lmac_task_id_t const src_id,
uint16_t const param_len)
{
struct lmac_msg *msg;
gfp_t flags;
if (_ssv_is_non_blocking_msg(id) && in_softirq())
flags = GFP_ATOMIC;
else
flags = GFP_KERNEL;
msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len,
flags);
if (msg == NULL) {
SSV_LOG_ERR("%s: msg allocation failed\n", __func__);
return NULL;
}
msg->dummy = 0;
msg->id = id;
msg->dest_id = dest_id;
msg->src_id = src_id;
msg->param_len = param_len;
return msg->param;
}
static int ssv_send_msg(struct ssv_softc *sc, const void *msg_params,
int reqcfm, lmac_msg_id_t reqid, void *cfm)
{
struct lmac_msg *msg;
struct ssv_cmd *cmd;
bool nonblock;
int ret;
msg = container_of((void *)msg_params, struct lmac_msg, param);
if ((SSV_DEV_STARTED < (sizeof(sc->drv_flags) * 8)) &&
!test_bit(SSV_DEV_STARTED, &sc->drv_flags) &&
reqid != MM_RESET_CFM && reqid != MM_VERSION_CFM &&
reqid != MM_START_CFM && reqid != MM_SET_IDLE_CFM &&
reqid != ME_CONFIG_CFM && reqid != MM_SET_PS_MODE_CFM &&
reqid != ME_CHAN_CONFIG_CFM && reqid != 0) {
SSV_LOG_ERR("%s: bypassing (SSV_DEV_RESTARTING set) 0x%02x\n",
__func__, reqid);
kfree(msg);
return -EBUSY;
} else if (!sc->ipc_env) {
SSV_LOG_DBG("%s: bypassing (restart must have failed)\n", __func__);
kfree(msg);
return -EBUSY;
}
nonblock = _ssv_is_non_blocking_msg(msg->id);
cmd = kzalloc(sizeof(struct ssv_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
cmd->result = -EINTR;
cmd->id = msg->id;
cmd->reqid = reqid;
cmd->a2e_msg = msg;
cmd->e2a_msg = cfm;
if (nonblock)
cmd->flags = SSV_CMD_FLAG_NONBLOCK;
if (reqcfm)
cmd->flags |= SSV_CMD_FLAG_REQ_CFM;
// SSV_LOG_DBG("[%s][%d] cmd = %p\n", __FUNCTION__, __LINE__, cmd);
ret = sc->cmd_mgr.queue(&sc->cmd_mgr, cmd);
if (!nonblock)
kfree(cmd);
else
ret = cmd->result;
return ret;
}
/*******************************************************************************
* Global Functions
******************************************************************************/
/******************************************************************************
* Control messages handling functions (SOFTMAC and FULLMAC)
*****************************************************************************/
int ssv_send_reset(struct ssv_softc *sc)
{
void *void_param;
/* RESET REQ has no parameter */
void_param = ssv_msg_zalloc(MM_RESET_REQ, TASK_MM, DRV_TASK_ID, 0);
if (!void_param)
return -ENOMEM;
return ssv_send_msg(sc, void_param, 1, MM_RESET_CFM, NULL);
}
int ssv_send_start(struct ssv_softc *sc)
{
struct mm_start_req *start_req_param;
/* Build the START REQ message */
start_req_param = ssv_msg_zalloc(MM_START_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_start_req));
if (!start_req_param)
return -ENOMEM;
/* Set parameters for the START message */
memcpy(&start_req_param->phy_cfg, &sc->phy_config, sizeof(sc->phy_config));
start_req_param->uapsd_timeout = (u32_l)sc->mod_params->uapsd_timeout;
start_req_param->lp_clk_accuracy = (u16_l)sc->mod_params->lp_clk_ppm;
/* Send the START REQ message to LMAC FW */
return ssv_send_msg(sc, start_req_param, 1, MM_START_CFM, NULL);
}
int ssv_send_version_req(struct ssv_softc *sc, struct mm_version_cfm *cfm)
{
void *void_param;
/* VERSION REQ has no parameter */
void_param = ssv_msg_zalloc(MM_VERSION_REQ, TASK_MM, DRV_TASK_ID, 0);
if (!void_param)
return -ENOMEM;
return ssv_send_msg(sc, void_param, 1, MM_VERSION_CFM, cfm);
}
int ssv_send_add_if(struct ssv_softc *sc, const unsigned char *mac,
enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm)
{
struct mm_add_if_req *add_if_req_param;
/* Build the ADD_IF_REQ message */
add_if_req_param = ssv_msg_zalloc(MM_ADD_IF_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_add_if_req));
if (!add_if_req_param)
return -ENOMEM;
/* Set parameters for the ADD_IF_REQ message */
memcpy(&(add_if_req_param->addr.array[0]), mac, ETH_ALEN);
switch (iftype) {
case NL80211_IFTYPE_P2P_CLIENT:
add_if_req_param->p2p = true;
add_if_req_param->type = VIF_STA;
break;
case NL80211_IFTYPE_STATION:
add_if_req_param->type = VIF_STA;
break;
case NL80211_IFTYPE_ADHOC:
add_if_req_param->type = VIF_IBSS;
break;
case NL80211_IFTYPE_P2P_GO:
add_if_req_param->p2p = true;
add_if_req_param->type = VIF_AP;
break;
case NL80211_IFTYPE_AP:
add_if_req_param->type = VIF_AP;
break;
case NL80211_IFTYPE_MESH_POINT:
add_if_req_param->type = VIF_MESH_POINT;
break;
case NL80211_IFTYPE_AP_VLAN:
ssv_msg_free((void *)add_if_req_param);
return -1;
case NL80211_IFTYPE_MONITOR:
add_if_req_param->type = VIF_MONITOR;
break;
default:
add_if_req_param->type = VIF_STA;
break;
}
/* Send the ADD_IF_REQ message to LMAC FW */
return ssv_send_msg(sc, add_if_req_param, 1, MM_ADD_IF_CFM, cfm);
}
int ssv_send_remove_if(struct ssv_softc *sc, u8 drv_vif_index)
{
struct mm_remove_if_req *remove_if_req;
SSV_LOG_DBG("[%s][%d] drv_vif_index = %u\n", __FUNCTION__, __LINE__, drv_vif_index);
/* Build the MM_REMOVE_IF_REQ message */
remove_if_req = ssv_msg_zalloc(MM_REMOVE_IF_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_remove_if_req));
if (!remove_if_req)
return -ENOMEM;
/* Set parameters for the MM_REMOVE_IF_REQ message */
remove_if_req->inst_nbr = drv_vif_index;
/* Send the MM_REMOVE_IF_REQ message to LMAC FW */
return ssv_send_msg(sc, remove_if_req, 1, MM_REMOVE_IF_CFM, NULL);
}
int ssv_send_set_channel(struct ssv_softc *sc, int phy_idx,
struct mm_set_channel_cfm *cfm)
{
struct mm_set_channel_req *set_chnl_par;
enum nl80211_chan_width width;
u16 center_freq, center_freq1, center_freq2;
s8 tx_power = 0;
enum nl80211_band band;
if (phy_idx >= sc->phy_cnt)
return -ENOTSUPP;
set_chnl_par = ssv_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_set_channel_req));
if (!set_chnl_par)
return -ENOMEM;
if (phy_idx == 0) {
/* On FULLMAC only setting channel of secondary chain */
wiphy_err(sc->wiphy, "Trying to set channel of primary chain");
ssv_msg_free((void *)set_chnl_par);
return 0;
} else {
struct ssv_sec_phy_chan *chan = &sc->sec_phy_chan;
width = chnl2bw[chan->type];
band = chan->band;
center_freq = chan->prim20_freq;
center_freq1 = chan->center_freq1;
center_freq2 = chan->center_freq2;
}
set_chnl_par->chan.band = band;
set_chnl_par->chan.type = bw2chnl[width];
set_chnl_par->chan.prim20_freq = center_freq;
set_chnl_par->chan.center1_freq = center_freq1;
set_chnl_par->chan.center2_freq = center_freq2;
set_chnl_par->chan.tx_power = tx_power;
set_chnl_par->index = phy_idx;
if (sc->use_phy_bw_tweaks) {
/* XXX Tweak for 80MHz VHT */
if (width > NL80211_CHAN_WIDTH_40) {
int _offs = center_freq1 - center_freq;
set_chnl_par->chan.type = PHY_CHNL_BW_40;
set_chnl_par->chan.center1_freq = center_freq + 10 *
(_offs > 0 ? 1 : -1) * (abs(_offs) > 10 ? 1 : -1);
}
}
SSV_LOG_DBG("mac80211: freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
" hw(%d): prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
center_freq, center_freq1,
center_freq2, width, band,
phy_idx, set_chnl_par->chan.prim20_freq, set_chnl_par->chan.center1_freq,
set_chnl_par->chan.center2_freq, set_chnl_par->chan.type, set_chnl_par->chan.band);
/* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */
return ssv_send_msg(sc, set_chnl_par, 1, MM_SET_CHANNEL_CFM, cfm);
}
int ssv_send_key_add(struct ssv_softc *sc, u8 vif_idx, u8 sta_idx, bool pairwise,
u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
struct mm_key_add_cfm *cfm)
{
struct mm_key_add_req *key_add_req;
/* Build the MM_KEY_ADD_REQ message */
key_add_req = ssv_msg_zalloc(MM_KEY_ADD_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_key_add_req));
if (!key_add_req)
return -ENOMEM;
/* Set parameters for the MM_KEY_ADD_REQ message */
if (sta_idx != 0xFF) {
/* Pairwise key */
key_add_req->sta_idx = sta_idx;
} else {
/* Default key */
key_add_req->sta_idx = sta_idx;
key_add_req->key_idx = (u8_l)key_idx; /* only useful for default keys */
}
key_add_req->pairwise = pairwise;
key_add_req->inst_nbr = vif_idx;
key_add_req->key.length = key_len;
memcpy(&(key_add_req->key.array[0]), key, key_len);
key_add_req->cipher_suite = cipher_suite;
#if 0
SSV_LOG_DBG("%s: sta_idx:%d key_idx:%d inst_nbr:%d cipher:%d key_len:%d\n", __func__,
key_add_req->sta_idx, key_add_req->key_idx, key_add_req->inst_nbr,
key_add_req->cipher_suite, key_add_req->key.length);
#if defined(CONFIG_SSV_DBG) || defined(CONFIG_DYNAMIC_DEBUG)
print_ssv_hex_dump_bytes("key: ", DUMP_PREFIX_OFFSET, key_add_req->key.array, key_add_req->key.length);
#endif
#endif
/* Send the MM_KEY_ADD_REQ message to LMAC FW */
return ssv_send_msg(sc, key_add_req, 1, MM_KEY_ADD_CFM, cfm);
}
int ssv_send_key_del(struct ssv_softc *sc, uint8_t hw_key_idx)
{
struct mm_key_del_req *key_del_req;
/* Build the MM_KEY_DEL_REQ message */
key_del_req = ssv_msg_zalloc(MM_KEY_DEL_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_key_del_req));
if (!key_del_req)
return -ENOMEM;
/* Set parameters for the MM_KEY_DEL_REQ message */
key_del_req->hw_key_idx = hw_key_idx;
/* Send the MM_KEY_DEL_REQ message to LMAC FW */
#if 0
///@FIXME: Does it need cfm?
return ssv_send_msg(sc, key_del_req, 0, 0, NULL);
#else
return ssv_send_msg(sc, key_del_req, 1, MM_KEY_DEL_CFM, NULL);
#endif
}
int ssv_send_bcn_change(struct ssv_softc *sc, u8 vif_idx, u8 *buf,
u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft)
{
struct mm_bcn_change_req *req;
/* Build the MM_BCN_CHANGE_REQ message */
req = ssv_msg_zalloc(MM_BCN_CHANGE_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_bcn_change_req) + bcn_len);
if (!req)
return -ENOMEM;
memcpy(req->bcn_buf, buf, bcn_len);
/* Set parameters for the MM_BCN_CHANGE_REQ message */
req->bcn_len = bcn_len;
req->tim_oft = tim_oft;
req->tim_len = tim_len;
req->inst_nbr = vif_idx;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
if (csa_oft) {
int i;
for (i = 0; i < BCN_MAX_CSA_CPT; i++) {
req->csa_oft[i] = csa_oft[i];
}
}
#endif /* VERSION >= 3.16.0 */
/* Send the MM_BCN_CHANGE_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, MM_BCN_CHANGE_CFM, NULL);
}
int ssv_send_roc(struct ssv_softc *sc, struct ssv_vif *vif,
struct ieee80211_channel *chan, unsigned int duration)
{
struct mm_remain_on_channel_req *req;
struct cfg80211_chan_def chandef;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
/* Create channel definition structure */
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
#endif
/* Build the MM_REMAIN_ON_CHANNEL_REQ message */
req = ssv_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_remain_on_channel_req));
if (!req)
return -ENOMEM;
/* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
req->op_code = MM_ROC_OP_START;
req->vif_index = vif->drv_vif_index;
req->duration_ms = duration;
req->chan.band = chan->band;
req->chan.type = bw2chnl[chandef.width];
req->chan.prim20_freq = chan->center_freq;
req->chan.center1_freq = chandef.center_freq1;
req->chan.center2_freq = chandef.center_freq2;
req->chan.tx_power = chan->max_power;
/* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
}
int ssv_send_cancel_roc(struct ssv_softc *sc)
{
struct mm_remain_on_channel_req *req;
/* Build the MM_REMAIN_ON_CHANNEL_REQ message */
req = ssv_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_remain_on_channel_req));
if (!req)
return -ENOMEM;
/* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
req->op_code = MM_ROC_OP_CANCEL;
/* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 0, 0, NULL);
}
int ssv_send_set_power(struct ssv_softc *sc, u8 vif_idx, s8 pwr,
struct mm_set_power_cfm *cfm)
{
struct mm_set_power_req *req;
/* Build the MM_SET_POWER_REQ message */
req = ssv_msg_zalloc(MM_SET_POWER_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_set_power_req));
if (!req)
return -ENOMEM;
/* Set parameters for the MM_SET_POWER_REQ message */
req->inst_nbr = vif_idx;
req->power = pwr;
/* Send the MM_SET_POWER_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, MM_SET_POWER_CFM, cfm);
}
int ssv_send_set_edca(struct ssv_softc *sc, u8 hw_queue, u32 param,
bool uapsd, u8 inst_nbr)
{
struct mm_set_edca_req *set_edca_req;
/* Build the MM_SET_EDCA_REQ message */
set_edca_req = ssv_msg_zalloc(MM_SET_EDCA_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_set_edca_req));
if (!set_edca_req)
return -ENOMEM;
/* Set parameters for the MM_SET_EDCA_REQ message */
set_edca_req->ac_param = param;
set_edca_req->uapsd = uapsd;
set_edca_req->hw_queue = hw_queue;
set_edca_req->inst_nbr = inst_nbr;
/* Send the MM_SET_EDCA_REQ message to LMAC FW */
return ssv_send_msg(sc, set_edca_req, 1, MM_SET_EDCA_CFM, NULL);
}
/******************************************************************************
* Control messages handling functions (FULLMAC only)
*****************************************************************************/
#if 0 //Debug
__INLINE void _ssv_me_config_dump_cap(struct me_config_req *req)
{
int i = 0;
SSV_LOG_DBG("\n===== [%s][%d] =====\n", __FUNCTION__, __LINE__);
SSV_LOG_DBG("req->phy_bw_max = %u\n", req->phy_bw_max);
SSV_LOG_DBG("===== HT capabilities =====\n") ;
SSV_LOG_DBG("req->ht_supp = %u\n", req->ht_supp);
SSV_LOG_DBG("req->ht_cap.ht_capa_info = 0x%04x\n", req->ht_cap.ht_capa_info);
SSV_LOG_DBG("req->ht_cap.a_mpdu_param = 0x%02x\n", req->ht_cap.a_mpdu_param);
for (i = 0; i < MAX_MCS_LEN; i++)
{
SSV_LOG_DBG("req->ht_cap.mcs_rate[%d] = 0x%02x\n", i, req->ht_cap.mcs_rate[i]);
}
SSV_LOG_DBG("===== VHT capabilities =====\n");
SSV_LOG_DBG("req->vht_supp = %u\n", req->vht_supp);
SSV_LOG_DBG("req->vht_cap.vht_capa_info = 0x%08x\n", req->vht_cap.vht_capa_info);
SSV_LOG_DBG("req->vht_cap.rx_mcs_map = 0x%04x\n", req->vht_cap.rx_mcs_map);
SSV_LOG_DBG("req->vht_cap.rx_highest = 0x%04x\n", req->vht_cap.rx_highest);
SSV_LOG_DBG("req->vht_cap.tx_mcs_map = 0x%04x\n", req->vht_cap.tx_mcs_map);
SSV_LOG_DBG("req->vht_cap.tx_highest = 0x%04x\n", req->vht_cap.tx_highest);
SSV_LOG_DBG("===== [%s][%d] =====\n\n", __FUNCTION__, __LINE__);
SSV_LOG_DBG("===== HE capabilities =====\n");
SSV_LOG_DBG("[%s][%d] req->he_supp = %u\n", __FUNCTION__, __LINE__, req->he_supp);
for (i = 0; i < MAC_HE_MAC_CAPA_LEN; i++)
{
SSV_LOG_DBG("req->he_cap.mac_cap_info[%d] = 0x%02x\n", i, req->he_cap.mac_cap_info[i]);
}
for (i = 0; i < MAC_HE_PHY_CAPA_LEN; i++)
{
SSV_LOG_DBG("req->he_cap.phy_cap_info[%d] = 0x%02x\n", i, req->he_cap.phy_cap_info[i]);
}
SSV_LOG_DBG("[%s][%d] req->he_cap.mcs_supp.rx_mcs_80 = 0x%04x\n", __FUNCTION__, __LINE__, req->he_cap.mcs_supp.rx_mcs_80);
SSV_LOG_DBG("[%s][%d] req->he_cap.mcs_supp.tx_mcs_80 = 0x%04x\n", __FUNCTION__, __LINE__, req->he_cap.mcs_supp.tx_mcs_80);
SSV_LOG_DBG("[%s][%d] req->he_cap.mcs_supp.rx_mcs_160 = 0x%04x\n", __FUNCTION__, __LINE__, req->he_cap.mcs_supp.rx_mcs_160);
SSV_LOG_DBG("[%s][%d] req->he_cap.mcs_supp.tx_mcs_160 = 0x%04x\n", __FUNCTION__, __LINE__, req->he_cap.mcs_supp.tx_mcs_160);
SSV_LOG_DBG("[%s][%d] req->he_cap.mcs_supp.rx_mcs_80p80 = 0x%04x\n", __FUNCTION__, __LINE__, req->he_cap.mcs_supp.rx_mcs_80p80);
SSV_LOG_DBG("[%s][%d] req->he_cap.mcs_supp.tx_mcs_80p80 = 0x%04x\n", __FUNCTION__, __LINE__, req->he_cap.mcs_supp.tx_mcs_80p80);
for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++)
{
SSV_LOG_DBG("req->he_cap.ppe_thres[%d] = 0x%02x\n", i, req->he_cap.ppe_thres[i]);
}
}
#endif
__INLINE void _ssv_me_config_set_he_cap(struct ssv_softc *sc, struct me_config_req *req)
{
#define NX_MDM_VER 32 ///@FIXME: hard-code to sync firmware version
struct mac_hecapability *he_cap = &req->he_cap;
int ppe_ru_cnt = 1;
int i = 0;
u8 he_mcs = (u8)MAC_HE_MCS_MAP_0_9; //MAC_HE_MCS_MAP_0_9(0x01)
u8 nss = sc->mod_params->nss;
//bool stbc = true;
HE_MAC_CAPA_BIT_SET(he_cap, HTC_HE);
if(sc->mod_params->twt_on)
HE_MAC_CAPA_BIT_SET(he_cap, TWT_REQ);
if (sc->mod_params->use_2040) {
HE_PHY_CAPA_VAL_SET(he_cap, CHAN_WIDTH_SET, RU_MAPPING_40MHZ_IN_2G);
HE_PHY_CAPA_VAL_SET(he_cap, DCM_MAX_BW, 40MHZ);
}else
{
HE_PHY_CAPA_VAL_SET(he_cap, CHAN_WIDTH_SET, RU_MAPPING_IN_2G);
HE_PHY_CAPA_VAL_SET(he_cap, DCM_MAX_BW, 20MHZ);
}
HE_PHY_CAPA_BIT_SET(he_cap, DEVICE_CLASS_A);
if(sc->mod_params->ldpc_on)
HE_PHY_CAPA_BIT_SET(he_cap, LDPC_CODING_IN_PAYLOAD);
HE_PHY_CAPA_BIT_SET(he_cap, HE_SU_PPDU_1x_LTF_AND_GI_0_8US);
HE_PHY_CAPA_VAL_SET(he_cap, MIDAMBLE_RX_MAX_NSTS, 4_STS);
HE_PHY_CAPA_BIT_SET(he_cap, NDP_4x_LTF_AND_3_2US);
HE_PHY_CAPA_BIT_SET(he_cap, DOPPLER_TX);
HE_PHY_CAPA_BIT_SET(he_cap, DOPPLER_RX);
HE_PHY_CAPA_VAL_SET(he_cap, DCM_MAX_CONST_TX, 16_QAM);
HE_PHY_CAPA_VAL_SET(he_cap, DCM_MAX_CONST_RX, 16_QAM);
HE_PHY_CAPA_BIT_SET(he_cap, RX_HE_MU_PPDU_FRM_NON_AP);
// HE_PHY_CAPA_BIT_SET(he_cap, DCM_MAX_NSS_RX);
// HE_PHY_CAPA_BIT_SET(he_cap, SU_BEAMFORMER);
if (sc->mod_params->bfmee)
{
HE_PHY_CAPA_BIT_SET(he_cap, SU_BEAMFORMEE);
HE_PHY_CAPA_VAL_SET(he_cap, BFMEE_MAX_STS_UNDER_80MHZ, 4);
}
HE_PHY_CAPA_BIT_SET(he_cap, NG16_SU_FEEDBACK);
#if (NX_MDM_VER > 30)
HE_PHY_CAPA_BIT_SET(he_cap, PARTIAL_BW_EXT_RANGE);
#endif
HE_PHY_CAPA_BIT_SET(he_cap, CODEBOOK_SIZE_42_SU);
HE_PHY_CAPA_BIT_SET(he_cap, TRIG_SU_BEAMFORMER_FB);
HE_PHY_CAPA_BIT_SET(he_cap, TRIG_CQI_FB);
HE_PHY_CAPA_BIT_SET(he_cap, PPE_THRESHOLD_PRESENT);
HE_PHY_CAPA_BIT_SET(he_cap, HE_SU_MU_PPDU_4x_LTF_AND_08_US_GI);
HE_PHY_CAPA_VAL_SET(he_cap, MAX_NC, 1);
#if (NX_MDM_VER > 30)
HE_PHY_CAPA_BIT_SET(he_cap, HE_ER_SU_PPDU_4x_LTF_AND_08_US_GI);
#endif
HE_PHY_CAPA_BIT_SET(he_cap, 20MHZ_IN_40MHZ_HE_PPDU_IN_2G);
#if (NX_MDM_VER > 30)
HE_PHY_CAPA_BIT_SET(he_cap, HE_ER_SU_PPDU_4x_LTF_AND_08_US_GI);
#endif
HE_PHY_CAPA_BIT_SET(he_cap, NON_TRIG_CQI_FEEDBACK);
HE_PHY_CAPA_BIT_SET(he_cap, RX_FULL_BW_SU_COMP_SIGB);
HE_PHY_CAPA_BIT_SET(he_cap, RX_FULL_BW_SU_NON_COMP_SIGB);
HE_PHY_CAPA_VAL_SET(he_cap, NOMINAL_PACKET_PADDING, RESERVED);
if (!sc->mod_params->ldpc_on)
// If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
// for MCS 10 and 11
he_mcs = min(he_mcs, (u8)MAC_HE_MCS_MAP_0_7);
memset(&he_cap->mcs_supp, 0, sizeof(he_cap->mcs_supp));
he_cap->mcs_supp.rx_mcs_80 = he_mcs;
for (i = 1; i < nss; i++) {
uint16_t unsup_for_ss = MAC_HE_MCS_MAP_NONE << (i*2);
he_cap->mcs_supp.rx_mcs_80 |= MAC_HE_MCS_MAP_0_7 << (i*2);
he_cap->mcs_supp.rx_mcs_160 |= unsup_for_ss;
he_cap->mcs_supp.rx_mcs_80p80 |= unsup_for_ss;
}
for (; i < 8; i++) {
uint16_t unsup_for_ss = MAC_HE_MCS_MAP_NONE << (i*2);
he_cap->mcs_supp.rx_mcs_80 |= unsup_for_ss;
he_cap->mcs_supp.rx_mcs_160 |= unsup_for_ss;
he_cap->mcs_supp.rx_mcs_80p80 |= unsup_for_ss;
}
he_cap->mcs_supp.tx_mcs_80 = he_mcs;
for (i = 1; i < nss; i++) {
uint16_t unsup_for_ss = MAC_HE_MCS_MAP_NONE << (i*2);
he_cap->mcs_supp.tx_mcs_80 |= MAC_HE_MCS_MAP_0_7 << (i*2);
he_cap->mcs_supp.tx_mcs_160 |= unsup_for_ss;
he_cap->mcs_supp.tx_mcs_80p80 |= unsup_for_ss;
}
for (; i < 8; i++) {
uint16_t unsup_for_ss = MAC_HE_MCS_MAP_NONE << (i*2);
he_cap->mcs_supp.tx_mcs_80 |= unsup_for_ss;
he_cap->mcs_supp.tx_mcs_160 |= unsup_for_ss;
he_cap->mcs_supp.tx_mcs_80p80 |= unsup_for_ss;
}
// PPE threshold
if (sc->mod_params->vht_on)
ppe_ru_cnt = 3;
else if (sc->mod_params->ht_on)
ppe_ru_cnt = 2;
else
ppe_ru_cnt = 1;
_ssv_co_val_set(he_cap->ppe_thres, HE_PPE_CAPA_NSTS_OFT, HE_PPE_CAPA_NSTS_WIDTH, nss - 1);
_ssv_co_val_set(he_cap->ppe_thres, HE_PPE_CAPA_RU_INDEX_BITMAP_OFT,
HE_PPE_CAPA_RU_INDEX_BITMAP_WIDTH, (1 << ppe_ru_cnt) - 1);
for (i = HE_PPE_CAPA_PPE_THRES_INFO_OFT;
i < HE_PPE_CAPA_PPE_THRES_INFO_OFT + (ppe_ru_cnt * 6 * nss);
i += 6)
{
_ssv_co_val_set(he_cap->ppe_thres, i, 6, HE_PPE_CAPA_BPSK | (HE_PPE_CAPA_NONE << 3));
}
if(sc->mod_params->he_on)
req->he_supp = true;
}
int ssv_send_me_config_req(struct ssv_softc *sc)
{
struct me_config_req *req;
struct wiphy *wiphy = sc->wiphy;
struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
uint8_t *ht_mcs = (uint8_t *)&ht_cap->mcs;
int i;
/* Build the ME_CONFIG_REQ message */
req = ssv_msg_zalloc(ME_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_config_req));
if (!req)
return -ENOMEM;
/* Set parameters for the ME_CONFIG_REQ message */
req->ps_on = sc->mod_params->ps_on;
req->tx_lft = sc->mod_params->tx_lft;
if (ssv_mod_params.vht_on)
{
req->phy_bw_max = PHY_CHNL_BW_80;
}
else if (ssv_mod_params.ht_on)
{
req->phy_bw_max = PHY_CHNL_BW_40;
}
else
{
req->phy_bw_max = PHY_CHNL_BW_20;
}
{ // HT capabilities
req->ht_supp = ht_cap->ht_supported;
req->ht_cap.ht_capa_info = cpu_to_le16(ht_cap->cap);
req->ht_cap.a_mpdu_param = ht_cap->ampdu_factor |
(ht_cap->ampdu_density <<
IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
for (i = 0; i < sizeof(ht_cap->mcs); i++)
req->ht_cap.mcs_rate[i] = ht_mcs[i];
req->ht_cap.ht_extended_capa = 0;
req->ht_cap.tx_beamforming_capa = 0;
req->ht_cap.asel_capa = 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) //VHT capabilities
{
struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap;
req->vht_supp = vht_cap->vht_supported;
memcpy(&req->vht_cap, &vht_cap->cap, sizeof(struct mac_vhtcapability));
}
#endif
_ssv_me_config_set_he_cap(sc, req);
//Dump capabilites.
// _ssv_me_config_dump_cap(req);
//Display chip ID, HT40, and HE
{
const char *chip_id = efuse_get_chip_id();
SSV_LOG_INFO("\n\033[1;33mCHIP ID: \033[1;35m%s\033[0m, \033[1;33mHT40: %s\033[0m, \033[1;33mHE(802.11ax): %s\033[0m\n"
, chip_id
, (req->ht_cap.ht_capa_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40)?"\033[1;32mON":"\033[1;31mOFF"
, (req->he_supp)?"\033[1;32mON":"\033[1;31mOFF");
SSV_LOG_INFO("\033[1;33mCHIP ID: \033[1;35m%s\033[0m, \033[1;33mHT40: %s\033[0m, \033[1;33mHE(802.11ax): %s\033[0m\n"
, chip_id
, (req->ht_cap.ht_capa_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40)?"\033[1;32mON":"\033[1;31mOFF"
, (req->he_supp)?"\033[1;32mON":"\033[1;31mOFF");
SSV_LOG_INFO("\033[1;33mCHIP ID: \033[1;35m%s\033[0m, \033[1;33mHT40: %s\033[0m, \033[1;33mHE(802.11ax): %s\033[0m\n\n"
, chip_id
, (req->ht_cap.ht_capa_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40)?"\033[1;32mON":"\033[1;31mOFF"
, (req->he_supp)?"\033[1;32mON":"\033[1;31mOFF");
}
/* Send the ME_CONFIG_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, ME_CONFIG_CFM, NULL);
}
int ssv_send_me_chan_config_req(struct ssv_softc *sc)
{
struct me_chan_config_req *req;
struct wiphy *wiphy = sc->wiphy;
int i;
/* Build the ME_CHAN_CONFIG_REQ message */
req = ssv_msg_zalloc(ME_CHAN_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_chan_config_req));
if (!req)
return -ENOMEM;
req->chan2G4_cnt= 0;
if (wiphy->bands[NL80211_BAND_2GHZ] != NULL) {
struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_2GHZ];
for (i = 0; i < b->n_channels; i++) {
req->chan2G4[req->chan2G4_cnt].flags = 0;
if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
req->chan2G4[req->chan2G4_cnt].flags |= SCAN_DISABLED_BIT;
req->chan2G4[req->chan2G4_cnt].flags |= _ssv_passive_scan_flag(b->channels[i].flags);
req->chan2G4[req->chan2G4_cnt].band = NL80211_BAND_2GHZ;
req->chan2G4[req->chan2G4_cnt].freq = b->channels[i].center_freq;
req->chan2G4[req->chan2G4_cnt].tx_power = b->channels[i].max_power;
req->chan2G4_cnt++;
if (req->chan2G4_cnt == SCAN_CHANNEL_2G4)
break;
}
}
req->chan5G_cnt = 0;
if (wiphy->bands[NL80211_BAND_5GHZ] != NULL) {
struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_5GHZ];
for (i = 0; i < b->n_channels; i++) {
req->chan5G[req->chan5G_cnt].flags = 0;
if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
req->chan5G[req->chan5G_cnt].flags |= SCAN_DISABLED_BIT;
req->chan5G[req->chan5G_cnt].flags |= _ssv_passive_scan_flag(b->channels[i].flags);
req->chan5G[req->chan5G_cnt].band = NL80211_BAND_5GHZ;
req->chan5G[req->chan5G_cnt].freq = b->channels[i].center_freq;
req->chan5G[req->chan5G_cnt].tx_power = b->channels[i].max_power;
req->chan5G_cnt++;
if (req->chan5G_cnt == SCAN_CHANNEL_5G)
break;
}
}
/* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, ME_CHAN_CONFIG_CFM, NULL);
}
int ssv_send_me_set_control_port_req(struct ssv_softc *sc, bool opened, u8 sta_idx)
{
struct me_set_control_port_req *req;
/* Build the ME_SET_CONTROL_PORT_REQ message */
req = ssv_msg_zalloc(ME_SET_CONTROL_PORT_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_set_control_port_req));
if (!req)
return -ENOMEM;
/* Set parameters for the ME_SET_CONTROL_PORT_REQ message */
req->sta_idx = sta_idx;
req->control_port_open = opened;
/* Send the ME_SET_CONTROL_PORT_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, ME_SET_CONTROL_PORT_CFM, NULL);
}
int ssv_send_me_sta_add(struct ssv_softc *sc, struct station_parameters *params,
const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm)
{
struct me_sta_add_req *req;
u8 *ht_mcs = (u8 *)&params->ht_capa->mcs;
int i;
/* Build the MM_STA_ADD_REQ message */
req = ssv_msg_zalloc(ME_STA_ADD_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_sta_add_req));
if (!req)
return -ENOMEM;
/* Set parameters for the MM_STA_ADD_REQ message */
memcpy(&(req->mac_addr.array[0]), mac, ETH_ALEN);
req->rate_set.length = params->supported_rates_len;
for (i = 0; i < params->supported_rates_len; i++)
req->rate_set.array[i] = params->supported_rates[i];
req->flags = 0;
if (params->ht_capa) {
const struct ieee80211_ht_cap *ht_capa = params->ht_capa;
req->flags |= STA_HT_CAPA;
req->ht_cap.ht_capa_info = cpu_to_le16(ht_capa->cap_info);
req->ht_cap.a_mpdu_param = ht_capa->ampdu_params_info;
//this is an error handler for samsung smart phone, it sends the wrong ht_capa with assoc request frame sometimes
//so we force to hack the content
if(0==ht_mcs[0])
{
memset(ht_mcs,0,MAX_MCS_LEN);
ht_mcs[0]=0xFF; //spactial stream 1
ht_mcs[MAX_MCS_LEN-4]=0x01; //spactial stream
}
for (i = 0; i < sizeof(ht_capa->mcs); i++){
req->ht_cap.mcs_rate[i] = ht_mcs[i];
}
req->ht_cap.ht_extended_capa = cpu_to_le16(ht_capa->extended_ht_cap_info);
req->ht_cap.tx_beamforming_capa = cpu_to_le32(ht_capa->tx_BF_cap_info);
req->ht_cap.asel_capa = ht_capa->antenna_selection_info;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (params->vht_capa) {
const struct ieee80211_vht_cap *vht_capa = params->vht_capa;
req->flags |= STA_VHT_CAPA;
req->vht_cap.vht_capa_info = cpu_to_le32(vht_capa->vht_cap_info);
req->vht_cap.rx_highest = cpu_to_le16(vht_capa->supp_mcs.rx_highest);
req->vht_cap.rx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.rx_mcs_map);
req->vht_cap.tx_highest = cpu_to_le16(vht_capa->supp_mcs.tx_highest);
req->vht_cap.tx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.tx_mcs_map);
}
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) || defined(IEEE80211_HE_MAC_CAP2_TRS)
if (params->he_capa) {
const struct ieee80211_he_cap_elem *he_capa = params->he_capa;
struct ieee80211_he_mcs_nss_supp *mcs_nss_supp =
(struct ieee80211_he_mcs_nss_supp *)(he_capa + 1);
req->flags |= STA_HE_CAPA;
for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++) {
req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i];
}
for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++) {
req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i];
}
req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80;
req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80;
req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160;
req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160;
req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80;
req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80;
}
#endif
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))
req->flags |= STA_QOS_CAPA;
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP))
req->flags |= STA_MFP_CAPA;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (params->opmode_notif_used) {
req->flags |= STA_OPMOD_NOTIF;
req->opmode = params->opmode_notif;
}
#endif
req->aid = cpu_to_le16(params->aid);
req->uapsd_queues = params->uapsd_queues;
req->max_sp_len = params->max_sp * 2;
req->vif_idx = inst_nbr;
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
req->tdls_sta = true;
/* Send the ME_STA_ADD_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, ME_STA_ADD_CFM, cfm);
}
int ssv_send_me_sta_del(struct ssv_softc *sc, u8 sta_idx, bool tdls_sta)
{
struct me_sta_del_req *req;
/* Build the MM_STA_DEL_REQ message */
req = ssv_msg_zalloc(ME_STA_DEL_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_sta_del_req));
if (!req)
return -ENOMEM;
/* Set parameters for the MM_STA_DEL_REQ message */
req->sta_idx = sta_idx;
req->tdls_sta = tdls_sta;
/* Send the ME_STA_DEL_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, ME_STA_DEL_CFM, NULL);
}
int ssv_send_me_traffic_ind(struct ssv_softc *sc, u8 sta_idx, bool uapsd, u8 tx_status)
{
struct me_traffic_ind_req *req;
/* Build the ME_UTRAFFIC_IND_REQ message */
req = ssv_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_traffic_ind_req));
if (!req)
return -ENOMEM;
/* Set parameters for the ME_TRAFFIC_IND_REQ message */
req->sta_idx = sta_idx;
req->tx_avail = tx_status;
req->uapsd = uapsd;
/* Send the ME_TRAFFIC_IND_REQ to UMAC FW */
ssv_msg_to_hci(sc, (void *)req);
return 0;
}
int ssv_send_me_rc_stats(struct ssv_softc *sc,
u8 sta_idx,
struct me_rc_stats_cfm *cfm)
{
struct me_rc_stats_req *req;
/* Build the ME_RC_STATS_REQ message */
req = ssv_msg_zalloc(ME_RC_STATS_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_rc_stats_req));
if (!req)
return -ENOMEM;
/* Set parameters for the ME_RC_STATS_REQ message */
req->sta_idx = sta_idx;
/* Send the ME_RC_STATS_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, ME_RC_STATS_CFM, cfm);
}
int ssv_send_me_rc_set_rate(struct ssv_softc *sc,
u8 sta_idx,
u16 rate_cfg)
{
struct me_rc_set_rate_req *req;
/* Build the ME_RC_SET_RATE_REQ message */
req = ssv_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_rc_set_rate_req));
if (!req)
return -ENOMEM;
/* Set parameters for the ME_RC_SET_RATE_REQ message */
req->sta_idx = sta_idx;
req->mode = RC_MODE_FIXEDRATE;
req->fixed_rate_cfg = rate_cfg;
/* Send the ME_RC_SET_RATE_REQ message to FW */
return ssv_send_msg(sc, req, 0, 0, NULL);
}
int ssv_send_me_rc_set_ldpc(struct ssv_softc *sc,
u8 sta_idx,
bool is_enable)
{
struct me_rc_set_rate_req *req;
/* Build the ME_RC_SET_RATE_REQ message */
req = ssv_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_rc_set_rate_req));
if (!req)
return -ENOMEM;
/* Set parameters for the ME_RC_SET_RATE_REQ message */
req->sta_idx = sta_idx;
req->mode = RC_MODE_LDPC;
req->fixed_rate_cfg = is_enable;
/* Send the ME_RC_SET_RATE_REQ message to FW */
return ssv_send_msg(sc, req, 0, 0, NULL);
}
int ssv_send_me_rc_set_ltf_gi(struct ssv_softc *sc,
u8 sta_idx,
u8 ltf_ig_type)
{
struct me_rc_set_rate_req *req;
/* Build the ME_RC_SET_RATE_REQ message */
req = ssv_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_rc_set_rate_req));
if (!req)
return -ENOMEM;
/* Set parameters for the ME_RC_SET_RATE_REQ message */
req->sta_idx = sta_idx;
req->mode = RC_MODE_LTF_GI;
req->fixed_rate_cfg = ltf_ig_type;
/* Send the ME_RC_SET_RATE_REQ message to FW */
return ssv_send_msg(sc, req, 0, 0, NULL);
}
int ssv_send_me_rc_sta_reset_default(struct ssv_softc *sc,
u8 sta_idx)
{
struct me_rc_set_rate_req *req;
/* Build the ME_RC_SET_RATE_REQ message */
req = ssv_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_rc_set_rate_req));
if (!req)
return -ENOMEM;
/* Set parameters for the ME_RC_SET_RATE_REQ message */
req->sta_idx = sta_idx;
req->mode = RC_MODE_RESET_DEFAULT;
/* Send the ME_RC_SET_RATE_REQ message to FW */
return ssv_send_msg(sc, req, 0, 0, NULL);
}
int ssv_send_sm_connect_req(struct ssv_softc *sc,
struct ssv_vif *ssv_vif,
struct cfg80211_connect_params *sme,
struct sm_connect_cfm *cfm)
{
struct sm_connect_req *req;
int i, auth_type = MAC_AUTH_ALGO_OPEN;
u32_l flags = 0;
if (WARN_ON(sme->ie_len > MAX_SM_CONNECT_REQ_IE_LEN))
return -EINVAL;
switch (sme->auth_type) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
case NL80211_AUTHTYPE_AUTOMATIC:
#endif
case NL80211_AUTHTYPE_OPEN_SYSTEM:
auth_type = MAC_AUTH_ALGO_OPEN;
break;
case NL80211_AUTHTYPE_SHARED_KEY:
auth_type = MAC_AUTH_ALGO_SHARED;
break;
case NL80211_AUTHTYPE_FT:
auth_type = MAC_AUTH_ALGO_FT;
break;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
case NL80211_AUTHTYPE_SAE:
auth_type = MAC_AUTH_ALGO_SAE;
break;
#elif defined(CONFIG_WPA_SUPPLICANT_CTL)
case SSV_NL80211_AUTHTYPE_SAE:
if (sme->auth_type != NL80211_AUTHTYPE_AUTOMATIC)
auth_type = MAC_AUTH_ALGO_SAE;
else
auth_type = MAC_AUTH_ALGO_OPEN;
break;
#endif
case NL80211_AUTHTYPE_NETWORK_EAP:
default:
SSV_LOG_DBG("%s: Cannot support auth type %d\n", __FUNCTION__, (int)sme->auth_type);
return -EINVAL;
}
/* Build the SM_CONNECT_REQ message */
req = ssv_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID,
sizeof(struct sm_connect_req));
if (!req)
return -ENOMEM;
/* Set parameters for the SM_CONNECT_REQ message */
if (sme->crypto.n_ciphers_pairwise &&
((sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
(sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_TKIP) ||
(sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104)))
flags |= DISABLE_HT;
if (sme->crypto.control_port)
flags |= CONTROL_PORT_HOST;
if (sme->crypto.control_port_no_encrypt)
flags |= CONTROL_PORT_NO_ENC;
if (_ssv_use_pairwise_key(&sme->crypto))
flags |= WPA_WPA2_IN_USE;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
if (sme->mfp == NL80211_MFP_REQUIRED)
flags |= MFP_IN_USE;
#endif
if (sme->crypto.control_port_ethertype)
req->ctrl_port_ethertype = sme->crypto.control_port_ethertype;
else
req->ctrl_port_ethertype = ETH_P_PAE;
if (sme->bssid)
memcpy(&req->bssid, sme->bssid, ETH_ALEN);
else
req->bssid = mac_addr_bcst;
req->vif_idx = ssv_vif->drv_vif_index;
if (sme->channel) {
req->chan.band = sme->channel->band;
req->chan.freq = sme->channel->center_freq;
req->chan.flags = _ssv_passive_scan_flag(sme->channel->flags);
} else {
req->chan.freq = (u16_l)-1;
}
for (i = 0; i < sme->ssid_len; i++)
req->ssid.array[i] = sme->ssid[i];
req->ssid.length = sme->ssid_len;
req->flags = flags;
if (sme->ie_len)
memcpy(req->ie_buf, sme->ie, sme->ie_len);
req->ie_len = sme->ie_len;
#if 0 //Debug
SSV_LOG_DBG("\n\nreq->ssid = ");
for (i = 0; i < req->ssid.length; i++)
{
SSV_LOG_DBG("%c", req->ssid.array[i]);
}
SSV_LOG_DBG("\n\n");
SSV_LOG_DBG("req->vif_idx = %u\n", req->vif_idx);
SSV_LOG_DBG("sme->ie_len = %u, req->ie_len = %u\n", (u16_l)sme->ie_len, (u16_l)req->ie_len);
#endif
req->listen_interval = ssv_mod_params.listen_itv;
req->dont_wait_bcmc = !ssv_mod_params.listen_bcmc;
/* Set auth_type */
req->auth_type = auth_type;
/* Set UAPSD queues */
req->uapsd_queues = ssv_mod_params.uapsd_queues;
/* Send the SM_CONNECT_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, SM_CONNECT_CFM, cfm);
}
int ssv_send_sm_disconnect_req(struct ssv_softc *sc,
struct ssv_vif *ssv_vif,
u16 reason)
{
struct sm_disconnect_req *req;
u8 sta_idx=0;
mutex_lock(&sc->cb_lock);
if(NULL==ssv_vif->sta.ap){
mutex_unlock(&sc->cb_lock);
return 0;
}
sta_idx=ssv_vif->sta.ap->sta_idx;
mutex_unlock(&sc->cb_lock);
ssv_drv_hci_tx_pause_by_sta(sc->hci_priv, sc->hci_ops, sta_idx); //pause hci tx queue by sta
ssv_drv_hci_tx_inactive_by_sta(sc->hci_priv, sc->hci_ops, sta_idx); //inactive hci tx queue by sta
memset(&sc->rx_bysta[sta_idx], 0 , sizeof(struct ssv_rx));
memset(&sc->tx_bysta[sta_idx], 0 , sizeof(struct ssv_tx));
/* Build the SM_DISCONNECT_REQ message */
req = ssv_msg_zalloc(SM_DISCONNECT_REQ, TASK_SM, DRV_TASK_ID,
sizeof(struct sm_disconnect_req));
if (!req)
return -ENOMEM;
/* Set parameters for the SM_DISCONNECT_REQ message */
req->reason_code = reason;
req->vif_idx = ssv_vif->drv_vif_index;
SSV_LOG_DBG("\33[31m%s():vif_idx=%d sta_idx=%d\33[0m\r\n",__FUNCTION__ ,req->vif_idx,sta_idx);
/* Send the SM_DISCONNECT_REQ message to LMAC FW */
//return ssv_send_msg(sc, req, 1, SM_DISCONNECT_IND, NULL);
return ssv_send_msg(sc, req, 1, SM_DISCONNECT_CFM, NULL);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_SUPPORT_WPA3)) || defined(CONFIG_WPA_SUPPLICANT_CTL)
int ssv_send_sm_external_auth_required_rsp(struct ssv_softc *sc,
struct ssv_vif *ssv_vif,
u16 status)
{
struct sm_external_auth_required_rsp *req;
/* Build the SM_EXTERNAL_AUTH_REQUIRED_RSP message */
req = ssv_msg_zalloc(SM_EXTERNAL_AUTH_REQUIRED_RSP, TASK_SM, DRV_TASK_ID,
sizeof(struct sm_external_auth_required_rsp));
if (!req)
return -ENOMEM;
/* Set parameters for the SM_DISCONNECT_REQ message */
req->status = status;
req->vif_idx = ssv_vif->drv_vif_index;
/* Send the SM_EXTERNAL_AUTH_REQUIRED_RSP message to LMAC FW */
return ssv_send_msg(sc, req, 0, 0, NULL);
}
#endif
int ssv_send_apm_start_req(struct ssv_softc *sc, struct ssv_vif *vif,
struct cfg80211_ap_settings *settings,
struct apm_start_cfm *cfm, u8 **bcn_buf)
{
struct apm_start_req *req;
struct ssv_bcn *bcn = &vif->ap.bcn;
u8 *buf;
u32 flags = 0;
const u8 *rate_ie;
u8 rate_len = 0;
int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
const u8 *var_pos;
int len, i;
// Build the beacon
bcn->dtim = (u8)settings->dtim_period;
buf = _ssv_build_bcn(bcn, &settings->beacon);
if (!buf) {
return -ENOMEM;
}
*bcn_buf = buf;
/* Build the APM_START_REQ message */
req = ssv_msg_zalloc(APM_START_REQ, TASK_APM, DRV_TASK_ID,
sizeof(struct apm_start_req) + bcn->len);
if (!req)
return -ENOMEM;
// Retrieve the basic rate set from the beacon buffer
len = bcn->len - var_offset;
var_pos = buf + var_offset;
rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
if (rate_ie) {
const u8 *rates = rate_ie + 2;
for (i = 0; i < rate_ie[1]; i++) {
if (rates[i] & 0x80)
req->basic_rates.array[rate_len++] = rates[i];
}
}
rate_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, var_pos, len);
if (rate_ie) {
const u8 *rates = rate_ie + 2;
for (i = 0; i < rate_ie[1]; i++) {
if (rates[i] & 0x80)
req->basic_rates.array[rate_len++] = rates[i];
}
}
req->basic_rates.length = rate_len;
memcpy(req->bcn_buf, buf, bcn->len);
/* Set parameters for the APM_START_REQ message */
req->vif_idx = vif->drv_vif_index;
//req->bcn_addr = elem->dma_addr;
req->bcn_len = bcn->len;
req->tim_oft = bcn->head_len;
req->tim_len = bcn->tim_len;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
req->chan.band = settings->chandef.chan->band;
req->chan.prim20_freq = settings->chandef.chan->center_freq;
req->chan.flags = 0;
req->chan.tx_power = settings->chandef.chan->max_power;
req->chan.center1_freq = settings->chandef.center_freq1;
req->chan.center2_freq = settings->chandef.center_freq2;
req->chan.type = bw2chnl[settings->chandef.width];
//Store ap setting param for fw reset use
sc->chan_type = cfg80211_get_chandef_type(&settings->chandef);
#else
if (vif->bchan_setting) {
req->chan.band = vif->chan_setting.band;
req->chan.prim20_freq = vif->chan_setting.center_freq;
req->chan.flags = 0;
req->chan.tx_power = vif->chan_setting.max_power;
if ((NL80211_CHAN_NO_HT == vif->chan_type_setting) || (NL80211_CHAN_HT20 == vif->chan_type_setting)) {
req->chan.center1_freq = vif->chan_setting.center_freq;
req->chan.center2_freq = 0;
req->chan.type = PHY_CHNL_BW_20;
} else if (NL80211_CHAN_HT40MINUS == vif->chan_type_setting) {
req->chan.center1_freq = vif->chan_setting.center_freq - 5 * 2;
req->chan.center2_freq = 0;
req->chan.type = PHY_CHNL_BW_40;
} else {
req->chan.center1_freq = vif->chan_setting.center_freq + 5 * 2;
req->chan.center2_freq = 0;
req->chan.type = PHY_CHNL_BW_40;
}
//Store ap setting param for fw reset use
sc->chan_type = vif->chan_type_setting;
} else {
BUG_ON(1);
}
#endif
//Store ap setting param for fw reset use
sc->center_freq = req->chan.prim20_freq;
req->bcn_int = settings->beacon_interval;
if (settings->crypto.control_port)
flags |= CONTROL_PORT_HOST;
if (settings->crypto.control_port_no_encrypt)
flags |= CONTROL_PORT_NO_ENC;
if (_ssv_use_pairwise_key(&settings->crypto))
flags |= WPA_WPA2_IN_USE;
if (settings->crypto.control_port_ethertype)
req->ctrl_port_ethertype = settings->crypto.control_port_ethertype;
else
req->ctrl_port_ethertype = ETH_P_PAE;
req->flags = flags;
/* Send the APM_START_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, APM_START_CFM, cfm);
}
int ssv_send_apm_stop_req(struct ssv_softc *sc, struct ssv_vif *vif)
{
struct apm_stop_req *req;
/* Build the APM_STOP_REQ message */
req = ssv_msg_zalloc(APM_STOP_REQ, TASK_APM, DRV_TASK_ID,
sizeof(struct apm_stop_req));
if (!req)
return -ENOMEM;
/* Set parameters for the APM_STOP_REQ message */
req->vif_idx = vif->drv_vif_index;
/* Send the APM_STOP_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, APM_STOP_CFM, NULL);
}
int ssv_send_scanu_req(struct ssv_softc *sc, struct ssv_vif *ssv_vif,
struct cfg80211_scan_request *param)
{
struct scanu_start_req *req;
int i;
uint8_t chan_flags = 0;
u16_l len;
len = (param->ie == NULL) ? 0: param->ie_len;
/* Build the SCANU_START_REQ message */
req = ssv_msg_zalloc(SCANU_START_REQ, TASK_SCANU, DRV_TASK_ID,
sizeof(struct scanu_start_req) + len);
if (!req)
return -ENOMEM;
/* Set parameters */
req->vif_idx = ssv_vif->drv_vif_index;
req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
req->bssid = mac_addr_bcst;
req->no_cck = param->no_cck;
if (req->ssid_cnt == 0)
chan_flags |= SCAN_PASSIVE_BIT;
for (i = 0; i < req->ssid_cnt; i++) {
int j;
for (j = 0; j < param->ssids[i].ssid_len; j++)
req->ssid[i].array[j] = param->ssids[i].ssid[j];
req->ssid[i].length = param->ssids[i].ssid_len;
}
if (param->ie){
memcpy(req->add_ies_buf, param->ie, param->ie_len);
req->add_ie_len = param->ie_len;
}
for (i = 0; i < req->chan_cnt; i++) {
struct ieee80211_channel *chan = param->channels[i];
req->chan[i].band = chan->band;
req->chan[i].freq = chan->center_freq;
req->chan[i].flags = chan_flags | _ssv_passive_scan_flag(chan->flags);
req->chan[i].tx_power = chan->max_reg_power;
}
req->duration = ssv_cfg.scan_period*1000; //us
/* Send the SCANU_START_REQ message to LMAC FW */
// sc->cmd_mgr.state = SSV_CMD_MGR_STATE_INITED;
return ssv_send_msg(sc, req, 0, 0, NULL);
}
int ssv_send_apm_start_cac_req(struct ssv_softc *sc, struct ssv_vif *vif,
struct cfg80211_chan_def *chandef,
struct apm_start_cac_cfm *cfm)
{
struct apm_start_cac_req *req;
/* Build the APM_START_CAC_REQ message */
req = ssv_msg_zalloc(APM_START_CAC_REQ, TASK_APM, DRV_TASK_ID,
sizeof(struct apm_start_cac_req));
if (!req)
return -ENOMEM;
/* Set parameters for the APM_START_CAC_REQ message */
req->vif_idx = vif->drv_vif_index;
req->chan.band = chandef->chan->band;
req->chan.prim20_freq = chandef->chan->center_freq;
req->chan.flags = 0;
req->chan.center1_freq = chandef->center_freq1;
req->chan.center2_freq = chandef->center_freq2;
req->chan.type = bw2chnl[chandef->width];
/* Send the APM_START_CAC_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, APM_START_CAC_CFM, cfm);
}
int ssv_send_apm_stop_cac_req(struct ssv_softc *sc, struct ssv_vif *vif)
{
struct apm_stop_cac_req *req;
/* Build the APM_STOP_CAC_REQ message */
req = ssv_msg_zalloc(APM_STOP_CAC_REQ, TASK_APM, DRV_TASK_ID,
sizeof(struct apm_stop_cac_req));
if (!req)
return -ENOMEM;
/* Set parameters for the APM_STOP_CAC_REQ message */
req->vif_idx = vif->drv_vif_index;
/* Send the APM_STOP_CAC_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, APM_STOP_CAC_CFM, NULL);
}
/**********************************************************************
* Debug Messages
*********************************************************************/
int ssv_send_dbg_trigger_req(struct ssv_softc *sc, char *msg)
{
struct mm_dbg_trigger_req *req;
/* Build the MM_DBG_TRIGGER_REQ message */
req = ssv_msg_zalloc(MM_DBG_TRIGGER_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_dbg_trigger_req));
if (!req)
return -ENOMEM;
/* Set parameters for the MM_DBG_TRIGGER_REQ message */
memcpy(req->error, msg, sizeof(req->error));
/* Send the MM_DBG_TRIGGER_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 0, -1, NULL);
}
int ssv_send_dbg_mem_read_req(struct ssv_softc *sc, u32 mem_addr,
struct dbg_mem_read_cfm *cfm)
{
struct dbg_mem_read_req *mem_read_req;
/* Build the DBG_MEM_READ_REQ message */
mem_read_req = ssv_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID,
sizeof(struct dbg_mem_read_req));
if (!mem_read_req)
return -ENOMEM;
/* Set parameters for the DBG_MEM_READ_REQ message */
mem_read_req->memaddr = mem_addr;
/* Send the DBG_MEM_READ_REQ message to LMAC FW */
return ssv_send_msg(sc, mem_read_req, 1, DBG_MEM_READ_CFM, cfm);
}
int ssv_send_dbg_mem_write_req(struct ssv_softc *sc, u32 mem_addr,
u32 mem_data)
{
struct dbg_mem_write_req *mem_write_req;
/* Build the DBG_MEM_WRITE_REQ message */
mem_write_req = ssv_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
sizeof(struct dbg_mem_write_req));
if (!mem_write_req)
return -ENOMEM;
/* Set parameters for the DBG_MEM_WRITE_REQ message */
mem_write_req->memaddr = mem_addr;
mem_write_req->memdata = mem_data;
/* Send the DBG_MEM_WRITE_REQ message to LMAC FW */
return ssv_send_msg(sc, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL);
}
int ssv_send_dbg_set_mod_filter_req(struct ssv_softc *sc, u32 filter)
{
struct dbg_set_mod_filter_req *set_mod_filter_req;
/* Build the DBG_SET_MOD_FILTER_REQ message */
set_mod_filter_req =
ssv_msg_zalloc(DBG_SET_MOD_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
sizeof(struct dbg_set_mod_filter_req));
if (!set_mod_filter_req)
return -ENOMEM;
/* Set parameters for the DBG_SET_MOD_FILTER_REQ message */
set_mod_filter_req->mod_filter = filter;
/* Send the DBG_SET_MOD_FILTER_REQ message to LMAC FW */
return ssv_send_msg(sc, set_mod_filter_req, 1, DBG_SET_MOD_FILTER_CFM, NULL);
}
int ssv_send_dbg_set_sev_filter_req(struct ssv_softc *sc, u32 filter)
{
struct dbg_set_sev_filter_req *set_sev_filter_req;
/* Build the DBG_SET_SEV_FILTER_REQ message */
set_sev_filter_req =
ssv_msg_zalloc(DBG_SET_SEV_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
sizeof(struct dbg_set_sev_filter_req));
if (!set_sev_filter_req)
return -ENOMEM;
/* Set parameters for the DBG_SET_SEV_FILTER_REQ message */
set_sev_filter_req->sev_filter = filter;
/* Send the DBG_SET_SEV_FILTER_REQ message to LMAC FW */
return ssv_send_msg(sc, set_sev_filter_req, 1, DBG_SET_SEV_FILTER_CFM, NULL);
}
int ssv_send_dbg_get_sys_stat_req(struct ssv_softc *sc,
struct dbg_get_sys_stat_cfm *cfm)
{
void *req;
/* Allocate the message */
req = ssv_msg_zalloc(DBG_GET_SYS_STAT_REQ, TASK_DBG, DRV_TASK_ID, 0);
if (!req)
return -ENOMEM;
/* Send the DBG_MEM_READ_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, DBG_GET_SYS_STAT_CFM, cfm);
}
int ssv_send_cfg_rssi_req(struct ssv_softc *sc, u8 vif_index, int rssi_thold, u32 rssi_hyst)
{
struct mm_cfg_rssi_req *req;
/* Build the MM_CFG_RSSI_REQ message */
req = ssv_msg_zalloc(MM_CFG_RSSI_REQ, TASK_MM, DRV_TASK_ID,
sizeof(struct mm_cfg_rssi_req));
if (!req)
return -ENOMEM;
/* Set parameters for the MM_CFG_RSSI_REQ message */
req->vif_index = vif_index;
req->rssi_thold = (s8)rssi_thold;
req->rssi_hyst = (u8)rssi_hyst;
/* Send the MM_CFG_RSSI_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 0, 0, NULL);
}
int ssv_private_msg_to_hci(struct ssv_softc *sc, u8 *msg_buffer, u32 msg_len);
int ssv_send_priv_msg_ampdu_setting(struct ssv_softc *sc, u32 param)
{
ST_IPC_SET_AMPDU_REQ req;
req.priv_type = E_IPC_PRIV_MSG_TYPE_SET_AMPDU;
req.length = sizeof(u32_l);
req.hw_cap = param;
ssv_private_msg_to_hci(sc, (u8*)&req, sizeof(ST_IPC_SET_AMPDU_REQ));
return 0;
}
#ifdef CONFIG_MIFI_LOWPOWER
int ssv_send_priv_msg_mifi_setting(struct ssv_softc *sc, u32 mifi_feature_setting, u32 mifi_no_traffic_duration_setting)
{
ST_IPC_PRIV_MSG *msg = NULL;
ST_IPC_SET_MIFI param = {0};
u32 msg_total_len = 0;
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(ST_IPC_SET_MIFI));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to alloc duplicate rx msg\n");
return 0;
}
// set param
param.status = mifi_feature_setting;
param.no_traffic_duration = mifi_no_traffic_duration_setting;
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_SET_MIFI_FEATURE;
msg->msglen = sizeof(ST_IPC_SET_MIFI);
memcpy((void *)&msg->data[0], (const void *)&param, sizeof(ST_IPC_SET_MIFI));
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to send private command\n");
}
kfree(msg);
return 0;
}
#endif
int ssv_send_me_config_monitor_req(struct ssv_softc *sc, struct cfg80211_chan_def *chandef,
bool chan_set, bool uf,
struct me_config_monitor_cfm *cfm)
{
struct me_config_monitor_req *req = NULL;
struct ieee80211_channel *chan = chandef->chan;
if (!chan)
return -EINVAL;
/* Build the ME_CHAN_CONFIG_REQ message */
req = ssv_msg_zalloc(ME_CONFIG_MONITOR_REQ, TASK_ME, DRV_TASK_ID,
sizeof(struct me_config_monitor_req));
if (!req)
return -ENOMEM;
req->chan.band = chan->band;
req->chan.type = bw2chnl[chandef->width];
req->chan.prim20_freq = chan->center_freq;
req->chan.center1_freq = chandef->center_freq1;
req->chan.center2_freq = chandef->center_freq2;
req->chan.tx_power = chan->max_power;
req->chan_set = chan_set;
req->uf = uf;
/* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */
return ssv_send_msg(sc, req, 1, ME_CONFIG_MONITOR_CFM, cfm);
}
int ssv_send_filter_duplicate_rx(struct ssv_softc *sc, u32 filter_duplicate_rx)
{
ST_IPC_PRIV_MSG *msg = NULL;
ST_IPC_FILTER_DUP_RX_PARAM param = {0};
u32 msg_total_len = 0;
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(ST_IPC_FILTER_DUP_RX_PARAM));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to alloc duplicate rx msg\n");
return 0;
}
// set param
param.filter_duplicate_rx = filter_duplicate_rx;
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_FILTER_DUP_RX_REQ;
msg->msglen = sizeof(ST_IPC_FILTER_DUP_RX_PARAM);
memcpy((void *)&msg->data[0], (const void *)&param, sizeof(ST_IPC_FILTER_DUP_RX_PARAM));
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to send private command\n");
}
kfree(msg);
return 0;
}
int ssv_send_wmm_follow_vo(struct ssv_softc *sc, u32 wmm_follow_vo)
{
ST_IPC_PRIV_MSG *msg = NULL;
ST_IPC_WMM_FOLLOW_VO_PARAM param = {0};
u32 msg_total_len = 0;
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(ST_IPC_WMM_FOLLOW_VO_PARAM));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to wmm follow vo msg\n");
return 0;
}
// set param
param.wmm_follow_vo = wmm_follow_vo;
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_WMM_FOLLOW_VO_REQ;
msg->msglen = sizeof(ST_IPC_WMM_FOLLOW_VO_PARAM);
memcpy((void *)&msg->data[0], (const void *)&param, sizeof(ST_IPC_WMM_FOLLOW_VO_PARAM));
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to send private command\n");
}
kfree(msg);
return 0;
}
int ssv_send_macaddr(struct ssv_softc *sc, u8 *mac0, u8 *mac1)
{
ST_IPC_PRIV_MSG *msg = NULL;
ST_IPC_MAC_ADDR_PARAM *param = NULL;
u32 msg_total_len = 0;
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(ST_IPC_MAC_ADDR_PARAM));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to alloc duplicate rx msg\n");
return 0;
}
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_MACADDR_REQ;
msg->msglen = sizeof(ST_IPC_MAC_ADDR_PARAM);
// set param
param = (ST_IPC_MAC_ADDR_PARAM *)msg->data;
memcpy(param->mac_addr0, mac0, ETH_ALEN);
memcpy(param->mac_addr1, mac1, ETH_ALEN);
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to send private command\n");
}
kfree(msg);
return 0;
}
int ssv_send_ipc_tx_use_one_hwq(struct ssv_softc *sc, u32 ac)
{
ST_IPC_PRIV_MSG *msg = NULL;
u32 *msg_data = NULL;
u32 msg_total_len = 0;
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(u32));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to set ipc tx use one hwq\n");
return -1;
}
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_IPC_TX_USE_ONE_HWQ;
msg->msglen = sizeof(u32);
// set param
msg_data = (u32 *)&msg->data[0];
*msg_data = ac;
//SSV_LOG_DBG("[%s][%d] *msg_data = %u\n", __FUNCTION__, __LINE__, *msg_data);
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to send private command\n");
}
kfree(msg);
return 0;
}
int ssv_send_set_policy_tbl(struct ssv_softc *sc, u8 set_rts_method)
{
ST_IPC_PRIV_MSG *msg = NULL;
ST_IPC_SET_POLICY_TBL_PARAM param = {0};
u32 msg_total_len = 0;
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(ST_IPC_SET_POLICY_TBL_PARAM));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to alloc set rts method msg\n");
return 0;
}
// set param
param.set_rts_method = set_rts_method;
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_SET_POLICY_TBL_REQ;
msg->msglen = sizeof(ST_IPC_SET_POLICY_TBL_PARAM);
memcpy((void *)&msg->data[0], (const void *)&param, sizeof(ST_IPC_SET_POLICY_TBL_PARAM));
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to set_rts_method command\n");
}
kfree(msg);
return 0;
}
int ssv_send_txq_credit_boundary(struct ssv_softc *sc)
{
ST_IPC_PRIV_MSG *msg = NULL;
ST_IPC_TXQ_CREDIT_BOUNDARY_PARAM *param = NULL;
u32 msg_total_len = 0;
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(ST_IPC_TXQ_CREDIT_BOUNDARY_PARAM));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to alloc duplicate rx msg\n");
return 0;
}
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_TXQ_CREDIT_BOUNDARY;
msg->msglen = sizeof(ST_IPC_TXQ_CREDIT_BOUNDARY_PARAM);
// set param
param = (ST_IPC_TXQ_CREDIT_BOUNDARY_PARAM *)msg->data;
if(ssv_cfg.txq_credit_boundary<=32)
{
param->boundary=ssv_cfg.txq_credit_boundary;
}
else
{
param->boundary=0xFF;
}
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to send private command\n");
}
kfree(msg);
return 0;
}
int ssv_send_io_aggr_setting(struct ssv_softc *sc, bool is_rx, bool enable)
{
ST_IPC_PRIV_MSG *msg = NULL;
ST_IPC_IO_AGGR_PARAM *param = NULL;
u32 msg_total_len = 0;
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(ST_IPC_IO_AGGR_PARAM));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to alloc duplicate rx msg\n");
return 0;
}
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_IO_AGGR;
msg->msglen = sizeof(ST_IPC_IO_AGGR_PARAM);
// set param
param = (ST_IPC_IO_AGGR_PARAM *)msg->data;
param->is_rx = (u32)is_rx;
param->enable = (u32)enable;
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to send private command\n");
}
kfree(msg);
return 0;
}
static u32 _check_mask(u32 value)
{
u32 i=0;
u8 count=0;
for(i=0;i<32;i++)
{
if((value&(1<<i)))
count++;
}
return count;
}
int ssv_send_mac_addr_rule_setting(struct ssv_softc *sc, u32 mac_low_mask, u32 mac_high_mask)
{
ST_IPC_PRIV_MSG *msg = NULL;
ST_MAC_ADDR_RULE_PARAM *param = NULL;
u32 msg_total_len = 0;
if((_check_mask(mac_low_mask)+_check_mask(mac_high_mask))>1)
{
SSV_LOG_ERR("In mac_low_mask and mac_high_mask, just only one bit can be 1\n");
return -1;
}
msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+sizeof(ST_MAC_ADDR_RULE_PARAM));
msg = kzalloc(msg_total_len+1, GFP_KERNEL);
if (!msg) {
SSV_LOG_DBG("Fail to alloc duplicate rx msg\n");
return 0;
}
// set msg id
msg->msgid = E_IPC_PRIV_MSG_TYPE_MAC_ADDR_RULE;
msg->msglen = sizeof(ST_MAC_ADDR_RULE_PARAM);
// set param
param = (ST_MAC_ADDR_RULE_PARAM *)msg->data;
param->mac_low_mask = mac_low_mask;
param->mac_high_mask = mac_high_mask;
if (0 > ssv_private_msg_to_hci(sc, (u8*)msg, msg_total_len)) {
SSV_LOG_DBG("Fail to send private command\n");
}
kfree(msg);
return 0;
}