blob: 77d14d5445daab5c1a1dca7fe33111f4e603e9a5 [file] [log] [blame]
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2015, 2021 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the
* disclaimer below) provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "debug.h"
#include "peer.h"
#include "smart_ant.h"
#include "wmi.h"
#include "dp_tx.h"
#include "ath11k_smart_ant_api.h"
struct ath11k_smart_ant_handler {
struct ath11k_base *ab;
struct ath11k_smartantenna_ops ops;
};
static struct ath11k_smart_ant_handler g_smart_ant_obj = {
.ab = NULL, .ops = {.sa_api_version = SA_API_VERSION_UNDEF}};
bool ath11k_enable_sa_api = true;
static int ath11k_smart_ant_msg_config_tx(struct ath11k *ar,
struct ath11k_sta *arsta,
struct ieee80211_sta *sta);
static int ath11k_smart_ant_msg_config_rx(struct ath11k *ar,
struct ath11k_sta *arsta);
static void ath11k_smart_ant_sa_training(struct ath11k *ar,
struct ath11k_sta *arsta,
struct ieee80211_sta *sta);
/**
* Smart antenna api callback register
*
* This function register the 3rd-party smart antenna module callback
* functions to the WLAN driver.
*
* Returns 0 for success, -1 in case of failure
*/
int ath11k_smart_ant_api_ops_register(struct ath11k_smartantenna_ops *ops)
{
struct ath11k_base *ab = g_smart_ant_obj.ab;
struct ath11k_smartantenna_ops *clb = &(g_smart_ant_obj.ops);
if (ab == NULL) {
printk("%s: ERROR: Smart Antenna API not ready\n", __func__);
return -1;
}
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "%s:+\n", __func__);
if (clb->sa_api_version != SA_API_VERSION_UNDEF) {
ath11k_warn(ab, "WARN: reject double sa api ops registration\n");
return -1;
}
if (ops == NULL) {
ath11k_warn(ab, "WARN: reject empty clb registration\n");
return -1;
}
if (ops->sa_api_version != SA_API_VERSION) {
ath11k_warn(ab,
"WARN: reject callbacks for sa_api version mismatch: "
"exp:%d got %d\n",
SA_API_VERSION, ops->sa_api_version);
return -1;
}
clb->sa_api_version = ops->sa_api_version;
clb->sa_radio_init = ops->sa_radio_init;
clb->sa_radio_deinit = ops->sa_radio_deinit;
clb->sa_node_connect = ops->sa_node_connect;
clb->sa_node_disconnect = ops->sa_node_disconnect;
clb->sa_update_txfeedback = ops->sa_update_txfeedback;
clb->sa_update_rxfeedback = ops->sa_update_rxfeedback;
clb->sa_get_txantenna = ops->sa_get_txantenna;
clb->sa_get_rxantenna = ops->sa_get_rxantenna;
clb->sa_get_txantenna_default = ops->sa_get_txantenna_default;
clb->sa_get_training_info = ops->sa_get_training_info;
ath11k_info(ab,
"Smart Antenna API layer: "
"clb registration successful (ver:%d)\n",
clb->sa_api_version);
return 0;
}
EXPORT_SYMBOL(ath11k_smart_ant_api_ops_register);
/**
* Smart antenna api callback deregister
*
* This function remove the hooks to the 3rd party smart antenna module
* attached to the WLAN driver.
*
* Returns 0 for success, -1 in case of failure
*/
int ath11k_smart_ant_api_ops_deregister(void)
{
struct ath11k_base *ab = g_smart_ant_obj.ab;
struct ath11k_smartantenna_ops *clb = &(g_smart_ant_obj.ops);
if (ab == NULL) {
// Smart Antenna component is not enabled yet
// impossible to deregister the callbacks
return -1;
}
if (clb->sa_api_version == SA_API_VERSION_UNDEF) {
ath11k_warn(ab,
"Smart Antenna API layer: "
"clb deregistering attempt with no effect\n");
}
// clean-up the callback pointers
memset(clb, 0, sizeof(*clb));
clb->sa_api_version = SA_API_VERSION_UNDEF;
ath11k_info(ab, "Smart Antenna API layer: callback deregistered.\n");
return 0;
}
EXPORT_SYMBOL(ath11k_smart_ant_api_ops_deregister);
/**
* ath11k smart antenna api object handler: attach function
*
* This function will be called by the ath11k base initializer
* and init the sa_api component.
* The component allows 3rd party module to attach the WLAN driver.
*
* Returns 0 for success
*/
int ath11k_sa_api_attach(struct ath11k_base *ab)
{
if (ab == NULL) {
return -1;
}
if (!test_bit(WMI_TLV_SERVICE_SMART_ANTENNA_SW_SUPPORT,
ab->wmi_ab.svc_map)) {
ath11k_warn(ab, "test hw capabilities failed\n");
return -1;
}
if (!test_bit(WMI_TLV_SERVICE_SMART_ANTENNA_HW_SUPPORT,
ab->wmi_ab.svc_map)) {
ath11k_warn(ab, "test sw failed\n");
return -1;
}
if (!ath11k_enable_sa_api) {
ath11k_warn(ab, "configuration capabilities failed\n");
return -1;
}
g_smart_ant_obj.ab = ab;
ath11k_info(ab,
"Smart Antenna API layer: Callback registration available\n");
return 0;
}
/**
* ath11k smart antenna api object handler: detach function
*
* This function can be called by the ath11k base initializer
* in case of deinitialization.
*
* Returns 0 for success
*/
int ath11k_sa_api_detach(struct ath11k_base *ab)
{
ath11k_smart_ant_api_ops_deregister();
g_smart_ant_obj.ab = NULL;
g_smart_ant_obj.ops.sa_api_version = SA_API_VERSION_UNDEF;
ath11k_info(ab, "Smart Antenna API layer: disabled.\n");
return 0;
}
static void ath11k_sa_api_radio_info_fill(struct ath11k_vif *arvif,
struct sa_radio_info *info)
{
struct ath11k *ar = NULL;
if ((arvif == NULL) || (info == NULL)) {
return;
}
ar = arvif->ar;
if (ar == NULL) {
return;
}
info->opmode = arvif->vdev_type;
info->radio_id = arvif->vdev_id;
info->chainmask = get_num_chains(ar->cfg_tx_chainmask) ?: 1;
}
int ath11k_sa_api_radio_init(struct ath11k_smart_ant_handler *sa_api,
struct ath11k_vif *arvif)
{
struct sa_radio_info info = {0};
if ((arvif == NULL) || (sa_api == NULL)) {
return SMART_ANT_STATUS_FAILURE;
}
if (sa_api->ops.sa_radio_init) {
ath11k_sa_api_radio_info_fill(arvif, &info);
return sa_api->ops.sa_radio_init(&info);
}
return SMART_ANT_STATUS_FAILURE;
}
int ath11k_sa_api_radio_deinit(struct ath11k_smart_ant_handler *sa_api,
struct ath11k_vif *arvif)
{
struct sa_radio_info info = {0};
if ((arvif == NULL) || (sa_api == NULL)) {
return SMART_ANT_STATUS_FAILURE;
}
if (sa_api->ops.sa_radio_deinit) {
ath11k_sa_api_radio_info_fill(arvif, &info);
return sa_api->ops.sa_radio_deinit(&info);
}
return SMART_ANT_STATUS_FAILURE;
}
int ath11k_sa_api_node_connect(struct ath11k_smart_ant_handler *sa_api,
struct ieee80211_sta *sta,
struct ath11k *ar)
{
struct sa_node_info node = {0};
if ((sa_api == NULL) || (sa_api->ab == NULL)) {
return SMART_ANT_STATUS_FAILURE;
}
if (sta == NULL) {
ath11k_warn(sa_api->ab, "Station is NULL!!\n");
return SMART_ANT_STATUS_FAILURE;
}
ether_addr_copy(node.mac_addr, sta->addr);
node.radio_id = 0;
if (sa_api->ops.sa_node_connect == NULL) {
ath11k_warn(sa_api->ab, "sa_node_connect clb is NULL!!\n");
return SMART_ANT_STATUS_FAILURE;
}
return sa_api->ops.sa_node_connect(&node);
}
int ath11k_sa_api_node_disconnect(struct ath11k_smart_ant_handler *sa_api,
struct ath11k *ar,
struct ieee80211_sta *sta)
{
struct sa_node_info node = {0};
struct ath11k_sta *arsta = NULL;
uint8_t status = 0;
int ret;
if ((sa_api == NULL) || (ar == NULL) || (ar->ab == NULL)) {
return SMART_ANT_STATUS_FAILURE;
}
if (sta == NULL) {
ath11k_warn(ar->ab, "Station is NULL!!\n");
return SMART_ANT_STATUS_FAILURE;
}
ether_addr_copy(node.mac_addr, sta->addr);
node.radio_id = 0;
if (sa_api->ops.sa_node_disconnect == NULL) {
ath11k_warn(ar->ab, "sa_node_disconnect clb is NULL!!\n");
return SMART_ANT_STATUS_FAILURE;
}
ret = sa_api->ops.sa_node_disconnect(&node, &status);
if (status & SMART_ANT_RX_CONFIG_REQUIRED) {
arsta = (struct ath11k_sta *)sta->drv_priv;
if (arsta == NULL) {
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT, "%s: missing operand: %p\n",
__func__, arsta);
return SMART_ANT_STATUS_FAILURE;
}
ath11k_smart_ant_msg_config_rx(ar, arsta);
}
return ret;
}
int ath11k_sa_api_get_txantenna(struct ath11k_smart_ant_handler *sa_api,
struct ieee80211_sta *sta, uint32_t *antenna)
{
struct sa_node_info info = {0};
if ((sa_api == NULL) || (sa_api->ab == NULL)) {
return SMART_ANT_STATUS_FAILURE;
}
info.radio_id = 0;
ether_addr_copy(info.mac_addr, sta->addr);
if (sa_api->ops.sa_get_txantenna == NULL) {
ath11k_warn(sa_api->ab, "sa_get_txantenna clb is NULL!!\n");
return SMART_ANT_STATUS_FAILURE;
}
return sa_api->ops.sa_get_txantenna(&info, antenna);
}
int ath11k_sa_api_get_rxantenna(struct ath11k_smart_ant_handler *sa_api,
struct ath11k_vif *arvif, uint32_t *antenna)
{
struct sa_radio_info info = {0};
if ((arvif == NULL) || (sa_api == NULL)) {
return SMART_ANT_STATUS_FAILURE;
}
if (sa_api->ops.sa_get_rxantenna == NULL) {
return SMART_ANT_STATUS_FAILURE;
}
ath11k_sa_api_radio_info_fill(arvif, &info);
if (sa_api->ops.sa_get_rxantenna == NULL) {
ath11k_warn(sa_api->ab, "sa_get_rxantenna clb is NULL!!\n");
return SMART_ANT_STATUS_FAILURE;
}
return sa_api->ops.sa_get_rxantenna(&info, antenna);
}
int ath11k_sa_api_get_txdefault_antenna(struct ath11k_smart_ant_handler *sa_api,
struct ath11k_vif *arvif,
uint32_t *antenna)
{
struct sa_radio_info info = {0};
if ((arvif == NULL) || (sa_api == NULL) || (sa_api->ab == NULL)) {
return SMART_ANT_STATUS_FAILURE;
}
if (sa_api->ops.sa_get_txantenna_default == NULL) {
return SMART_ANT_STATUS_FAILURE;
}
ath11k_sa_api_radio_info_fill(arvif, &info);
if (sa_api->ops.sa_get_txantenna_default == NULL) {
ath11k_warn(sa_api->ab, "sa_get_txantenna_default clb is NULL!!\n");
return SMART_ANT_STATUS_FAILURE;
}
return sa_api->ops.sa_get_txantenna_default(&info, antenna);
}
static int ath11k_sa_api_update_txfeedback(
struct ath11k_smart_ant_handler *sa_api,
struct ath11k *ar, struct ieee80211_sta *sta,
struct ath11k_smart_ant_tx_feedback *fb)
{
struct ath11k_base *ab = NULL;
struct ath11k_sta *arsta = NULL;
struct sa_node_info node = {0};
int ret = 0;
uint8_t status = 0;
if ((sa_api == NULL) || (sta == NULL) || (fb == NULL)) {
return SMART_ANT_STATUS_FAILURE;
}
ab = sa_api->ab;
if (ab == NULL) {
return SMART_ANT_STATUS_FAILURE;
}
// fill sa_node_info structure
memset(&node, 0, sizeof(node));
ether_addr_copy(node.mac_addr, sta->addr);
if (sa_api->ops.sa_update_txfeedback == NULL) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT,
"sa_update_txfeedback missing clb\n");
return SMART_ANT_STATUS_FAILURE;
}
ret = sa_api->ops.sa_update_txfeedback(&node, fb, &status);
if (ret < 0) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "failure: %d\n", ret);
return ret;
}
arsta = (struct ath11k_sta *)sta->drv_priv;
if ((arsta == NULL) || (ar == NULL)) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "%s: missing operand: %p\n",
__func__, arsta);
return SMART_ANT_STATUS_FAILURE;
}
// processing status information
if (status & SMART_ANT_RX_CONFIG_REQUIRED) {
ath11k_smart_ant_msg_config_rx(ar, arsta);
}
if (status & SMART_ANT_TX_CONFIG_REQUIRED) {
ath11k_smart_ant_msg_config_tx(ar, arsta, sta);
}
if (status & SMART_ANT_TRAINING_REQUIRED)
ath11k_smart_ant_sa_training(ar, arsta, sta);
return 0;
}
static int ath11k_sa_api_update_rxfeedback(
struct ath11k_smart_ant_handler *sa_api,
struct ath11k *ar, struct ieee80211_sta *sta,
struct ath11k_smart_ant_rx_feedback *fb)
{
struct ath11k_base *ab = NULL;
struct ath11k_sta *arsta = NULL;
struct sa_node_info node = {0};
int ret = 0;
uint8_t status = 0;
if ((sa_api == NULL) || (sta == NULL) || (fb == NULL)) {
return -1;
}
ab = sa_api->ab;
if (ab == NULL) {
return SMART_ANT_STATUS_FAILURE;
}
if (sa_api->ops.sa_update_rxfeedback == NULL) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT,
"sa_update_rxfeedback missing clb\n");
return SMART_ANT_STATUS_FAILURE;
}
// fill sa_node_info structure
memset(&node, 0, sizeof(node));
ether_addr_copy(node.mac_addr, sta->addr);
ret = sa_api->ops.sa_update_rxfeedback(&node, fb, &status);
if (ret < 0) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "%s: failure:%d\n", __func__, ret);
return ret;
}
arsta = (struct ath11k_sta *)sta->drv_priv;
if ((arsta == NULL) || (ar == NULL)) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "%s: missing operand: %p\n",
__func__, arsta);
return SMART_ANT_STATUS_FAILURE;
}
if (status & SMART_ANT_RX_CONFIG_REQUIRED) {
ath11k_smart_ant_msg_config_rx(ar, arsta);
}
if (status & SMART_ANT_TX_CONFIG_REQUIRED) {
ath11k_smart_ant_msg_config_tx(ar, arsta, sta);
}
return 0;
}
static void ath11k_smart_ant_msg_worker(struct work_struct *work)
{
struct ath11k_smart_ant_info *info =
container_of(work, struct ath11k_smart_ant_info, sa_msg_worker);
struct ath11k *ar = container_of(info, struct ath11k, smart_ant_info);
struct ath11k_smart_ant_cfg_msg *msg, *tmp;
struct ath11k_peer *peer;
u32 tx_ants[ATH11K_SMART_ANT_MAX_RATE_SERIES];
int i, ret = 0;
spin_lock_bh(&info->msg_lock);
list_for_each_entry_safe (msg, tmp, &info->msg_list, list) {
list_del(&msg->list);
spin_unlock_bh(&info->msg_lock);
switch (msg->type) {
case ATH11K_SMART_ANT_TYPE_RX_CFG:
ret = ath11k_wmi_pdev_set_rx_ant(
ar, msg->rx_ant_cfg.rx_ant);
if (ret) {
ath11k_warn(ar->ab,
"Failed to set rx antenna\n");
}
ret = ath11k_wmi_pdev_set_param(
ar,
WMI_PDEV_PARAM_SMART_ANTENNA_DEFAULT_ANTENNA,
msg->rx_ant_cfg.rx_ant, ar->pdev->pdev_id);
if (ret) {
ath11k_warn(
ar->ab,
"Failed to set default antenna: %d\n",
ret);
}
ret = ath11k_wmi_pdev_set_param(
ar,
WMI_PDEV_PARAM_SA_PARALLEL_MODE_GPIO_DRIVE_CFG,
msg->rx_ant_cfg.rx_ant, ar->pdev->pdev_id);
if (ret) {
ath11k_warn(
ar->ab,
"Failed to set SA_PARALLEL_MODE antenna: %d\n",
ret);
}
for (i = 0; i < info->num_fallback_rate; i++)
tx_ants[i] = msg->rx_ant_cfg.tx_default_ant;
ret = ath11k_wmi_peer_set_smart_tx_ant(
ar, msg->rx_ant_cfg.vdev_id,
msg->rx_ant_cfg.addr, tx_ants);
if (ret) {
ath11k_warn(
ar->ab,
"Failed to set tx default antenna\n");
}
break;
case ATH11K_SMART_ANT_TYPE_TX_CFG:
rcu_read_lock();
peer = ath11k_peer_find_by_addr(
ar->ab, msg->tx_ant_cfg.peer_mac);
if (!peer) {
ath11k_err(ar->ab, "peer not found\n");
rcu_read_unlock();
break;
}
rcu_read_unlock();
ret = ath11k_wmi_peer_set_smart_tx_ant(
ar, msg->tx_ant_cfg.vdev_id,
msg->tx_ant_cfg.peer_mac,
msg->tx_ant_cfg.tx_ants);
if (ret) {
ath11k_warn(ar->ab,
"Failed to set tx antenna\n", ret);
}
break;
case ATH11K_SMART_ANT_TYPE_TRAIN_INFO:
rcu_read_lock();
peer = ath11k_peer_find_by_addr(ar->ab,
msg->train_info_cfg.peer_mac);
if (!peer) {
ath11k_err(ar->ab, "peer not found\n");
rcu_read_unlock();
break;
}
rcu_read_unlock();
ret = ath11k_wmi_peer_set_smart_ant_train_info(
ar, msg->train_info_cfg.train_info.vdev_id,
msg->train_info_cfg.peer_mac,
&msg->train_info_cfg.train_info);
if (ret)
ath11k_warn(ar->ab,
"Failed to set train info: %d\n", ret);
break;
default:
case ATH11K_SMART_ANT_TYPE_MAX:
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT,
"Not a supported config type :%d\n",
msg->type);
}
kfree(msg);
spin_lock_bh(&info->msg_lock);
}
spin_unlock_bh(&info->msg_lock);
}
static int ath11k_smart_ant_msg_config_rx(struct ath11k *ar,
struct ath11k_sta *arsta)
{
struct ath11k_smart_ant_cfg_msg *msg = NULL;
struct ath11k_smart_ant_info *info = NULL;
struct ath11k_vif *arvif = NULL;
if (ar == NULL) {
return -EINVAL;
}
if ((arsta == NULL) || (arsta->arvif == NULL)) {
ath11k_err(ar->ab, "Invalid argument arsta:%p\n", arsta);
return -EINVAL;
}
info = &(ar->smart_ant_info);
arvif = arsta->arvif;
if (!ath11k_smart_ant_enabled(ar))
return 0;
if (!info->enabled)
return 0;
if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
arvif->vdev_subtype != WMI_VDEV_SUBTYPE_NONE) {
ath11k_dbg(
ar->ab, ATH11K_DBG_SMART_ANT,
"Smart antenna logic not enabled for non-AP interface\n");
return 0;
}
msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
if (!msg) {
ath11k_err(
ar->ab,
"Failed to allocate memory for smart antenna config msg\n");
return -ENOMEM;
}
msg->type = ATH11K_SMART_ANT_TYPE_RX_CFG;
ath11k_sa_api_get_rxantenna(&g_smart_ant_obj, arvif,
&info->default_ant);
msg->rx_ant_cfg.rx_ant = info->default_ant;
msg->rx_ant_cfg.vdev_id = arvif->vdev_id;
ether_addr_copy(msg->rx_ant_cfg.addr, arvif->vif->addr);
ath11k_sa_api_get_txdefault_antenna(&g_smart_ant_obj, arvif,
&info->default_ant);
msg->rx_ant_cfg.tx_default_ant = info->default_ant;
spin_lock_bh(&info->msg_lock);
list_add_tail(&msg->list, &info->msg_list);
spin_unlock_bh(&info->msg_lock);
ieee80211_queue_work(ar->hw, &info->sa_msg_worker);
return 0;
}
static int ath11k_smart_ant_msg_config_tx(struct ath11k *ar,
struct ath11k_sta *arsta,
struct ieee80211_sta *sta)
{
struct ath11k_smart_ant_info *info = NULL;
struct ath11k_smart_ant_cfg_msg *msg = NULL;
if (ar == NULL) {
return -EINVAL;
}
info = &(ar->smart_ant_info);
if ((arsta == NULL) || (arsta->arvif == NULL)) {
ath11k_err(ar->ab, "Invalid argument %p\n", arsta);
return -EINVAL;
}
msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
if (!msg) {
ath11k_err(
ar->ab,
"Failed to allocate memory for smart antenna config msg\n");
return -ENOMEM;
}
msg->type = ATH11K_SMART_ANT_TYPE_TX_CFG;
ether_addr_copy(msg->tx_ant_cfg.peer_mac, sta->addr);
msg->tx_ant_cfg.vdev_id = arsta->arvif->vdev_id;
ath11k_sa_api_get_txantenna(&g_smart_ant_obj, sta,
msg->tx_ant_cfg.tx_ants);
spin_lock_bh(&info->msg_lock);
list_add_tail(&msg->list, &info->msg_list);
spin_unlock_bh(&info->msg_lock);
ieee80211_queue_work(ar->hw, &info->sa_msg_worker);
return 0;
}
static int smart_ant_alg_get_streams(u32 nss)
{
u32 num_chains = 0, supp_tx_chainmask = (1 << nss) - 1;
int i;
for (i = 0; i < ATH11K_SMART_ANT_MAX_CHAINS; i++) {
if (supp_tx_chainmask & (1 << i))
num_chains++;
}
return min(num_chains, nss);
}
static void smart_ant_alg_init_param(struct ath11k *ar)
{
struct ath11k_smart_ant_info *info = &ar->smart_ant_info;
struct ath11k_smart_ant_params *sa_params =
&ar->smart_ant_info.smart_ant_params;
u32 nss;
nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1;
info->mode = WMI_SMART_ANT_MODE_PARALLEL;
info->default_ant = ATH11K_SMART_ANT_DEFAULT_ANT;
info->num_fallback_rate = ATH11K_SMART_ANT_FALLBACK_RATE_DEFAULT;
info->txrx_feedback = ATH11K_SMART_ANT_TX_FEEDBACK |
ATH11K_SMART_ANT_RX_FEEDBACK;
sa_params->low_rate_threshold = ATH11K_SMART_ANT_PER_MIN_THRESHOLD;
sa_params->hi_rate_threshold = ATH11K_SMART_ANT_PER_MAX_THRESHOLD;
sa_params->per_diff_threshold = ATH11K_SMART_ANT_PER_DIFF_THRESHOLD;
sa_params->num_train_pkts = 0;
sa_params->pkt_len = ATH11K_SMART_ANT_PKT_LEN_DEFAULT;
sa_params->num_tx_ant_comb = 1 << smart_ant_alg_get_streams(nss);
sa_params->num_min_pkt = ATH11K_SMART_ANT_NUM_PKT_MIN;
sa_params->retrain_interval = msecs_to_jiffies(
ATH11K_SMART_ANT_RETRAIN_INTVL);
sa_params->perf_train_interval = msecs_to_jiffies(
ATH11K_SMART_ANT_PERF_TRAIN_INTVL);
sa_params->max_perf_delta = ATH11K_SMART_ANT_TPUT_DELTA_DEFAULT;
sa_params->hysteresis = ATH11K_SMART_ANT_HYSTERISYS_DEFAULT;
sa_params->min_goodput_threshold =
ATH11K_SMART_ANT_MIN_GOODPUT_THRESHOLD;
sa_params->avg_goodput_interval = ATH11K_SMART_ANT_GOODPUT_INTVL_AVG;
sa_params->ignore_goodput_interval =
ATH11K_SMART_ANT_IGNORE_GOODPUT_INTVL;
sa_params->num_pretrain_pkts = ATH11K_SMART_ANT_PRETRAIN_PKTS_MAX;
sa_params->num_other_bw_pkts_threshold = ATH11K_SMART_ANT_BW_THRESHOLD;
sa_params->enabled_train = ATH11K_SMART_ANT_TRAIN_INIT |
ATH11K_SMART_ANT_TRAIN_TRIGGER_PERIODIC |
ATH11K_SMART_ANT_TRAIN_TRIGGER_PERF |
ATH11K_SMART_ANT_TRAIN_TRIGGER_RX;
sa_params->num_pkt_min_threshod[ATH11K_SMART_ANT_BW_20] =
ATH11K_SMART_ANT_NUM_PKT_THRESHOLD_20;
sa_params->num_pkt_min_threshod[ATH11K_SMART_ANT_BW_40] =
ATH11K_SMART_ANT_NUM_PKT_THRESHOLD_40;
sa_params->num_pkt_min_threshod[ATH11K_SMART_ANT_BW_80] =
ATH11K_SMART_ANT_NUM_PKT_THRESHOLD_80;
sa_params->default_tx_ant = ATH11K_SMART_ANT_DEFAULT_ANT;
sa_params->ant_change_ind = 0;
sa_params->max_train_ppdu = ATH11K_SMART_ANT_NUM_TRAIN_PPDU_MAX;
sa_params->num_rx_chain = nss;
if (nss > ATH11K_SMART_ANT_MAX_RX_CHAIN)
sa_params->num_rx_chain = ATH11K_SMART_ANT_MAX_RX_CHAIN;
}
void ath11k_smart_ant_alg_sta_disconnect(struct ath11k *ar,
struct ieee80211_sta *sta)
{
struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
if (!ath11k_smart_ant_enabled(ar) || !arsta->smart_ant_sta)
return;
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT,
"Smart antenna disconnect for %pM\n", sta->addr);
// smart antenna callback node disconnect
ath11k_sa_api_node_disconnect(&g_smart_ant_obj, ar, sta);
kfree(arsta->smart_ant_sta);
arsta->smart_ant_sta = NULL;
}
int ath11k_smart_ant_alg_sta_connect(struct ath11k *ar,
struct ath11k_vif *arvif,
struct ieee80211_sta *sta)
{
struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
struct ath11k_smart_ant_node_config_params params;
struct ath11k_smart_ant_sta *smart_ant_sta;
int ret;
u8 mac_addr[ETH_ALEN];
if( g_smart_ant_obj.ops.sa_api_version == SA_API_VERSION_UNDEF)
return 0;
lockdep_assert_held(&ar->conf_mutex);
if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
arvif->vdev_subtype != WMI_VDEV_SUBTYPE_NONE)
return 0;
memset(&params, 0, sizeof(params));
params.cmd_id = 1;
params.arg_count = 1;
params.vdev_id = arsta->arvif->vdev_id;
params.arg_arr = ATH11K_SMART_ANT_TX_FEEDBACK_CONFIG_DEFAULT;
ether_addr_copy(mac_addr, sta->addr);
ret = ath11k_wmi_peer_set_smart_ant_node_config(ar, mac_addr, &params);
if (ret) {
ath11k_warn(ar->ab, "Failed to set feedback config\n");
return ret;
}
smart_ant_sta = kzalloc(sizeof(struct ath11k_smart_ant_sta), GFP_ATOMIC);
if (!smart_ant_sta) {
ath11k_warn(ar->ab, "Failed to allocate smart ant sta\n");
ret = -EINVAL;
return ret;
}
arsta->smart_ant_sta = smart_ant_sta;
// smart antenna callback node connect
ath11k_sa_api_node_connect(&g_smart_ant_obj, sta, ar);
return 0;
}
int ath11k_smart_ant_alg_set_txantenna(struct ieee80211_sta *sta,
const u32 *tx_antenna)
{
struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
struct ath11k_vif *arvif = arsta->arvif;
struct ath11k *ar = arvif->ar;
u32 tx_ants[ATH11K_SMART_ANT_MAX_RATE_SERIES] = {0};
u8 macaddr[ETH_ALEN];
ether_addr_copy(macaddr, sta->addr);
if (tx_antenna == NULL) { // tx_antenna array input is ignored
// smart antenna callback get tx antenna
ath11k_sa_api_get_txantenna(&g_smart_ant_obj, sta, tx_ants);
}
return ath11k_wmi_peer_set_smart_tx_ant(ar, arvif->vdev_id, macaddr,
tx_ants);
}
void ath11k_smart_ant_self_gen_frame(struct ath11k *ar,
struct ieee80211_sta *sta)
{
struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
struct ath11k_skb_cb *skb_cb;
struct ath11k_vif *arvif = NULL;
struct ieee80211_qos_hdr *nullfunc;
struct ieee80211_tx_info *info;
struct sk_buff *skb;
struct wireless_dev *wdev = NULL;
int size = sizeof(*nullfunc);
__le16 fc;
int ret;
if (!arsta)
return;
else
arvif = arsta->arvif;
if (!arvif && !arvif->vif)
return;
else
wdev = ieee80211_vif_to_wdev(arvif->vif);
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_QOS_NULLFUNC |
IEEE80211_FCTL_FROMDS);
skb = dev_alloc_skb(ar->hw->extra_tx_headroom + size);
if (!skb)
return;
if (wdev && wdev->netdev)
skb->dev = wdev->netdev;
skb_reserve(skb, ar->hw->extra_tx_headroom);
nullfunc = skb_put(skb, size);
nullfunc->frame_control = fc;
nullfunc->duration_id = 0;
memcpy(nullfunc->addr1, sta->addr, ETH_ALEN);
memcpy(nullfunc->addr2, arvif->vif->addr, ETH_ALEN);
memcpy(nullfunc->addr3, arvif->vif->addr, ETH_ALEN);
nullfunc->seq_ctrl = 0;
info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
IEEE80211_TX_INTFL_NL80211_FRAME_TX;
info->band = NL80211_BAND_5GHZ;
skb_set_queue_mapping(skb, IEEE80211_AC_BK);
skb->priority = WME_AC_BK;
nullfunc->qos_ctrl = cpu_to_le16(WME_AC_BK);
skb_cb = ATH11K_SKB_CB(skb);
skb_cb->flags |= ATH11K_SKB_SELF_GEN_FRAME;
ret = ath11k_dp_tx(ar, arvif, skb, sta);
if (unlikely(ret)) {
ath11k_warn(ar->ab, "failed to transmit self gen frame %d\n", ret);
ieee80211_free_txskb(ar->hw, skb);
}
return;
}
int ath11k_sa_api_get_training_info(struct ath11k_smart_ant_handler *sa_api,
struct ieee80211_sta *sta,
struct sa_training_info *train_info)
{
struct sa_node_info info = {0};
if ((sa_api == NULL) || (sa_api->ab == NULL))
return SMART_ANT_STATUS_FAILURE;
info.radio_id = 0;
ether_addr_copy(info.mac_addr, sta->addr);
if (sa_api->ops.sa_get_training_info == NULL) {
ath11k_warn(sa_api->ab, "sa_get_training_info clb is NULL!!\n");
return SMART_ANT_STATUS_FAILURE;
}
return sa_api->ops.sa_get_training_info(&info, train_info);
}
void ath11k_smart_ant_sa_training(struct ath11k *ar,
struct ath11k_sta *arsta,
struct ieee80211_sta *sta)
{
struct sa_training_info sa_train_info;
struct ath11k_smart_ant_cfg_msg *msg = NULL;
struct ath11k_smart_ant_info *info = NULL;
u32 rate_mask = 0, rate_code;
int i, ret;
if (ar == NULL)
return;
if ((arsta == NULL) || (arsta->arvif == NULL)) {
ath11k_err(ar->ab, "Invalid argument %p\n", arsta);
return;
}
info = &(ar->smart_ant_info);
ret = ath11k_sa_api_get_training_info(&g_smart_ant_obj, sta, &sa_train_info);
if (ret < 0) {
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT, "SA get training info failure: %d\n", ret);
return;
}
if (sa_train_info.tx_antenna >
((1 << ar->ab->target_caps.num_rf_chains) - 1)) {
ath11k_err(ar->ab, "Invalid tx ant for trianing\n");
return;
}
if ((sa_train_info.pream < 0) && (sa_train_info.nss < 0) && (sa_train_info.mcs < 0)) {
rate_code = ATH11K_SA_AUTO_RCODE_MASK;
} else if ((sa_train_info.pream < 0) || (sa_train_info.nss < 0) || (sa_train_info.mcs < 0)) {
ath11k_warn(ar->ab, "Invalid rate code parameters preamble: %d nss: %d mcs: %d\n",
sa_train_info.pream, sa_train_info.nss, sa_train_info.mcs);
return;
} else {
// processing sa_train_info information
rate_code = ATH11K_SMART_ANT_RATE_CODE(sa_train_info.pream,
sa_train_info.nss,
sa_train_info.mcs);
}
for (i = 0; i <= sta->bandwidth; i++)
rate_mask |= (0xff << (8 * i));
if (((rate_code & rate_mask) != rate_code) &&
(rate_code != ATH11K_SA_AUTO_RCODE_MASK)) {
ath11k_err(ar->ab, "Invalid rates for training\n");
return;
}
msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
if (!msg) {
ath11k_err(ar->ab,
"Failed to allocate memory for smart antenna config msg\n");
return;
}
msg->type = ATH11K_SMART_ANT_TYPE_TRAIN_INFO;
ether_addr_copy(msg->train_info_cfg.peer_mac, sta->addr);
msg->train_info_cfg.train_info.numpkts = sa_train_info.packet_num;
msg->train_info_cfg.train_info.vdev_id = arsta->arvif->vdev_id;
for (i = 0; i < WMI_SMART_MAX_RATE_SERIES; i++) {
msg->train_info_cfg.train_info.rate_array[i] = rate_code;
if (i > ATH11K_SMART_ANT_RATE_UNUSED)
msg->train_info_cfg.train_info.rate_array[i] = ATH11K_SMART_ANT_RATE_UNUSED;
msg->train_info_cfg.train_info.antenna_array[i] = sa_train_info.tx_antenna;
}
spin_lock_bh(&info->msg_lock);
list_add_tail(&msg->list, &info->msg_list);
spin_unlock_bh(&info->msg_lock);
ieee80211_queue_work(ar->hw, &info->sa_msg_worker);
if (sa_train_info.frame_type != ATH11K_SA_SELF_GEN_FRAME_TYPE)
return;
for (i = 0; i < sa_train_info.packet_num; i++)
ath11k_smart_ant_self_gen_frame(ar, sta);
return;
}
int ath11k_smart_ant_alg_set_default(struct ath11k_vif *arvif)
{
struct ath11k *ar = arvif->ar;
struct ath11k_smart_ant_info *info = &ar->smart_ant_info;
int ret, i;
u32 tx_ants[ATH11K_SMART_ANT_MAX_RATE_SERIES];
lockdep_assert_held(&ar->conf_mutex);
if (!ath11k_smart_ant_enabled(ar))
return 0;
if (!info->enabled)
return 0;
if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
arvif->vdev_subtype != WMI_VDEV_SUBTYPE_NONE) {
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT,
"Smart antenna logic not enabled for non-AP interface\n");
return 0;
}
// smart antenna callback get rx antenna
ath11k_sa_api_get_rxantenna(&g_smart_ant_obj, arvif, &info->default_ant);
/* Set default tx/rx antennas to start with */
ret = ath11k_wmi_pdev_set_rx_ant(ar, info->default_ant);
if (ret) {
ath11k_warn(ar->ab, "Failed to set rx antenna\n");
return ret;
}
ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_SMART_ANTENNA_DEFAULT_ANTENNA,
info->default_ant,
ar->pdev->pdev_id);
if (ret) {
ath11k_warn(ar->ab,
"Failed to set default antenna\n",
ret);
}
ath11k_wmi_pdev_set_param(ar,
WMI_PDEV_PARAM_SA_PARALLEL_MODE_GPIO_DRIVE_CFG,
info->default_ant, ar->pdev->pdev_id);
// smart antenna callback get default tx antenna
ath11k_sa_api_get_txdefault_antenna(&g_smart_ant_obj, arvif, &info->default_ant);
/* Tx antenna for every fallback rate series */
for (i = 0; i < info->num_fallback_rate; i++)
tx_ants[i] = info->default_ant;
ret = ath11k_wmi_peer_set_smart_tx_ant(ar, arvif->vdev_id,
arvif->vif->addr, tx_ants);
if (ret)
ath11k_warn(ar->ab, "Failed to set tx antenna\n");
return ret;
}
void ath11k_smart_ant_alg_disable(struct ath11k_vif *arvif)
{
struct ath11k *ar = arvif->ar;
struct ath11k_smart_ant_info *info = &ar->smart_ant_info;
struct ath11k_smart_ant_cfg_msg *cfg, *tmp;
int ret;
lockdep_assert_held(&ar->conf_mutex);
if (!ath11k_smart_ant_enabled(ar))
return;
if (!info->enabled)
return;
if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
arvif->vdev_subtype != WMI_VDEV_SUBTYPE_NONE)
return;
/* See if this is the last vif requesting to disable smart antenna */
info->num_enabled_vif--;
if (info->num_enabled_vif != 0)
return;
/* Disable smart antenna logic in fw */
ret = ath11k_wmi_pdev_disable_smart_ant(ar, ATH11K_SMART_ANT_DISABLE, info);
if (ret) {
ath11k_err(ar->ab, "Wmi command to disable smart antenna is failed\n");
return;
}
// smart antenna callback radio deinit
ath11k_sa_api_radio_deinit(&g_smart_ant_obj, arvif);
// ath11k_smart_ant_info: wmi work-queue
cancel_work_sync(&info->sa_msg_worker);
spin_lock_bh(&info->msg_lock);
list_for_each_entry_safe(cfg, tmp, &info->msg_list, list) {
list_del(&cfg->list);
kfree(cfg);
}
spin_unlock_bh(&info->msg_lock);
info->enabled = false;
}
int ath11k_smart_ant_alg_enable(struct ath11k_vif *arvif)
{
struct ath11k *ar = arvif->ar;
struct ath11k_smart_ant_info *info = &ar->smart_ant_info;
int ret;
lockdep_assert_held(&ar->conf_mutex);
if (!ath11k_smart_ant_enabled(ar))
return 0;
/* Smart antenna is tested with only AP mode, it can also be enabled
* for other modes, just needs more testing.
*/
if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
arvif->vdev_subtype != WMI_VDEV_SUBTYPE_NONE) {
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT,
"Smart antenna logic not enabled for non-AP interface\n");
return 0;
}
info->num_enabled_vif++;
if (info->enabled)
return 0;
smart_ant_alg_init_param(ar);
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT,
"Hw supports Smart antenna, enabling it in driver\n");
info->enabled = true;
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT,
"smart mode: %d num_fallback_rate: %d num_rx_chain: %d info_enable: %d\n",
ar->smart_ant_info.mode, ar->smart_ant_info.num_fallback_rate,
ar->smart_ant_info.smart_ant_params.num_rx_chain, info->enabled);
/* Enable smart antenna logic in fw with mode and default antenna */
ret = ath11k_wmi_pdev_enable_smart_ant(ar, ATH11K_SMART_ANT_ENABLE, info);
if (ret) {
ath11k_err(ar->ab, "Wmi command to enable smart antenna is failed\n");
return ret;
}
/* Set the smart antenna GPIO 14 configuration in fw */
ret = ath11k_wmi_set_gpio_smart_ant(ar, info, ATH11K_SMART_ANT_PIN0);
if (ret) {
ath11k_err(ar->ab, "Wmi command to set gpio %d for smart antenna is failed\n",
ATH11K_SMART_ANT_PIN0);
return ret;
}
/* Set the smart antenna GPIO 15 configuration in fw */
ret = ath11k_wmi_set_gpio_smart_ant(ar, info, ATH11K_SMART_ANT_PIN1);
if (ret) {
ath11k_err(ar->ab, "Wmi command to set gpio %d for smart antenna is failed\n",
ATH11K_SMART_ANT_PIN1);
return ret;
}
ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_SA_PARALLEL_MODE_GPIO_DRIVE_CFG,
ATH11K_SMART_ANT_GPIO_VALUE,
ar->pdev->pdev_id);
if (ret) {
ath11k_warn(ar->ab,
"Failed to set sa parallel mode gpio drive config\n",
ret);
return ret;
}
// ath11k_smart_ant_info: wmi work-queue
spin_lock_init(&info->msg_lock);
INIT_LIST_HEAD(&info->msg_list);
INIT_WORK(&info->sa_msg_worker, ath11k_smart_ant_msg_worker);
// smart antenna callback radio init
ath11k_sa_api_radio_init(&g_smart_ant_obj, arvif);
return 0;
}
static void ath11k_smart_ant_dbg_tx_feedback(struct ath11k_base *ab,
struct ath11k_smart_ant_tx_feedback *fb)
{
if (fb->npackets)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT,
"Tx feedback npkts: %d nbad: %d S-Retry: %d L-Retry: %d "
"tx_antenna: %d train pkt: %s goodput: %d rate_maxphy: [0x%x|0x%x|0x%x|0x%x] "
"RSSI: [%d %d %d %d %d %d %d %d] rate_mcs: %d rate_nss: %d rate_index: %d\n",
fb->npackets, fb->nbad, fb->nshort_retries, fb->nlong_retries,
fb->tx_antenna, fb->is_trainpkt ? "True" : "False", fb->goodput,
fb->ratemaxphy[0], fb->ratemaxphy[1], fb->ratemaxphy[2],
fb->ratemaxphy[3], fb->rssi[0], fb->rssi[1], fb->rssi[2],
fb->rssi[3], fb->rssi[4], fb->rssi[5], fb->rssi[6],
fb->rssi[7], fb->rate_mcs[0], fb->rate_mcs[1],
fb->rate_index);
}
static void ath11k_smart_ant_tx_fb_fill(struct ath11k *ar, u32 *data,
struct ath11k_smart_ant_tx_feedback *fb,
struct ath11k_sta *arsta)
{
struct htt_ppdu_stats_usr_cmpltn_cmn *cmpltn_cmn;
int i;
cmpltn_cmn = (struct htt_ppdu_stats_usr_cmpltn_cmn *)data;
fb->npackets = cmpltn_cmn->mpdu_tried;
fb->nbad = cmpltn_cmn->mpdu_tried - cmpltn_cmn->mpdu_success;
fb->rate_mcs[0] = arsta->txrate.mcs;
fb->rate_mcs[1] = arsta->txrate.nss;
fb->rate_mcs[2] = (cmpltn_cmn->status & 0x1U);
fb->rate_index = arsta->txrate.bw;
fb->nlong_retries = TXFD_MS(cmpltn_cmn->flags, ATH11K_SMART_ANT_LRETRY);
fb->nshort_retries = TXFD_MS(cmpltn_cmn->flags, ATH11K_SMART_ANT_SRETRY);
for (i = 0; i < ATH11K_SMART_ANT_MAX_SA_RSSI_CHAINS; i++)
fb->rssi[i] = cmpltn_cmn->chain_rssi[i];
fb->tx_antenna = cmpltn_cmn->tx_antenna_mask;
fb->is_trainpkt = TXFD_MS(cmpltn_cmn->pending_training_pkts,
ATH11K_SMART_ANT_IS_TRAIN_PKT);
fb->goodput = TXFD_MS(cmpltn_cmn->pending_training_pkts,
ATH11K_SMART_ANT_TRAIN_PKTS);
for (i = 0; i < ATH11K_SMART_ANT_MAX_RATE_COUNTERS; i++)
fb->ratemaxphy[i] = cmpltn_cmn->max_rates[i];
if (fb->npackets)
ath11k_dbg(ar->ab, ATH11K_DBG_SMART_ANT, "Tx feedback from sta: %pM\n",
arsta->smart_ant_sta->mac_addr);
ath11k_smart_ant_dbg_tx_feedback(ar->ab, fb);
}
void
ath11k_smart_ant_alg_proc_tx_feedback(struct ath11k_base *ab,
u32 *data,
u16 peer_id)
{
struct ath11k_sta *arsta;
struct ath11k_peer *peer = NULL;
struct ath11k *ar;
struct ath11k_smart_ant_tx_feedback tx_feedback;
struct ieee80211_supported_band *band;
rcu_read_lock();
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_by_id(ab, peer_id);
if(peer && peer->sta) {
arsta = (struct ath11k_sta *)peer->sta->drv_priv;
if (!arsta->smart_ant_sta)
goto exit;
memset(&tx_feedback, 0, sizeof(tx_feedback));
ar = arsta->arvif->ar;
if (!ar)
goto exit;
band = &ar->mac.sbands[NL80211_BAND_5GHZ];
if (band->band == NL80211_BAND_5GHZ) {
ath11k_smart_ant_tx_fb_fill(ar, data, &tx_feedback, arsta);
// smart antenna callback tx feedback
if (ath11k_sa_api_update_txfeedback(&g_smart_ant_obj, ar, peer->sta,
&tx_feedback) < 0) {
ath11k_warn(ab, ATH11K_DBG_SMART_ANT,
"Tx feedback from sta: %pM: FAILURE\n", peer->addr);
}
}
}
exit:
spin_unlock_bh(&ab->base_lock);
rcu_read_unlock();
}
static void ath11k_smart_ant_dbg_rx_feedback(struct ath11k_base *ab,
struct ath11k_smart_ant_rx_feedback *fb,
u32 evm_count)
{
int i, j;
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT,
"Rx feedback npkts: %d rx_antenna: %d rate_index: %d "
"rx_rate_mcs: %d rx_rate_nss: %d pilot_count: %d nss_count: %d",
fb->npackets, fb->rx_antenna, fb->rx_rate_index,
fb->rx_rate_mcs, fb->rx_rate_nss, fb->pilot_count, fb->nss_count);
if (fb->pilot_count * fb->nss_count) {
switch (fb->rx_rate_index) {
case ATH11K_BW_20:
if (evm_count == ATH11K_SMART_ANT_MAX_EVM_1SS_SIZE)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback 20MHZ NSS1 rx_acc_linear_evm | pilot_error\n");
if (evm_count == ATH11K_SMART_ANT_MAX_EVM_2SS_SIZE)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback 20MHZ NSS2 rx_acc_linear_evm | pilot_error\n");
break;
case ATH11K_BW_40:
if (evm_count == ATH11K_SMART_ANT_MAX_EVM_1SS_SIZE)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback 40MHZ NSS1 rx_acc_linear_evm | pilot_error\n");
if (evm_count == ATH11K_SMART_ANT_MAX_EVM_2SS_SIZE)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback 40MHZ NSS2 rx_acc_linear_evm | pilot_error\n");
break;
case ATH11K_BW_80:
if (evm_count == ATH11K_SMART_ANT_MAX_EVM_1SS_SIZE)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback 80MHZ NSS1 rx_acc_linear_evm | pilot_error\n");
if (evm_count == ATH11K_SMART_ANT_MAX_EVM_2SS_SIZE)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback 80MHZ NSS2 rx_acc_linear_evm | pilot_error\n");
break;
case ATH11K_BW_160:
if (evm_count == ATH11K_SMART_ANT_MAX_EVM_1SS_SIZE)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback 160MHZ NSS1 rx_acc_linear_evm | pilot_error\n");
if (evm_count == ATH11K_SMART_ANT_MAX_EVM_2SS_SIZE)
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback 160MHZ NSS2 rx_acc_linear_evm | pilot_error\n");
break;
}
for (i = 0; i < fb->pilot_count * fb->nss_count; i++) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback %-4d | %-4d ",
fb->rx_evm[i], fb->rx_pilot_err[i]);
}
}
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback RSSI: %-5s %-5s %-5s %-5s \n",
"bw0", "bw1", "bw2", "bw4");
for (i = 0; i < ATH11K_SMART_ANT_MAX_SA_RSSI_CHAINS; i++) {
for (j = 0; j < ATH11K_SMART_ANT_MAX_BW; j++) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT,
"Rx feedback Chain%d [%-5d %-5d %-5d %-5d]",
i, fb->rx_rssi[i][j], fb->rx_rssi[i][j + 1],
fb->rx_rssi[i][j + 2], fb->rx_rssi[i][j + 3]);
j += 3;
}
}
}
static void ath11k_smart_ant_rx_fb_fill(struct ath11k_base *ab,
struct hal_rx_mon_ppdu_info *ppdu_info,
struct ath11k_smart_ant_rx_feedback *fb,
struct ath11k_sta *arsta)
{
u32 evm_count;
int i, j;
fb->rx_rate_index = arsta->smart_ant_sta->rx_feedback.rx_rate_index;
fb->rx_rate_mcs = arsta->smart_ant_sta->rx_feedback.rx_rate_mcs;
fb->rx_rate_nss = arsta->smart_ant_sta->rx_feedback.rx_rate_nss;
fb->rx_antenna = arsta->smart_ant_sta->rx_feedback.rx_antenna;
fb->npackets = arsta->smart_ant_sta->rx_feedback.npackets;
arsta->smart_ant_sta->rx_feedback.npackets = 0;
fb->nss_count = ppdu_info->evm_info.nss_count;
fb->pilot_count = ppdu_info->evm_info.pilot_count;
if (ppdu_info->evm_info.nss_count == 1) {
evm_count = HAL_RX_MAX_SU_EVM_COUNT_1SS;
for (i = 0; i < evm_count; i++) {
fb->rx_evm[i] =
ppdu_info->evm_info.evm_1ss[i].acc_linear_evm;
fb->rx_pilot_err[i]=
ppdu_info->evm_info.evm_1ss[i].pilot_error;
}
}
if (ppdu_info->evm_info.nss_count == 2) {
evm_count = HAL_RX_MAX_SU_EVM_COUNT_2SS;
for (i = 0; i < evm_count; i++) {
fb->rx_evm[i] =
ppdu_info->evm_info.evm_2ss[i].acc_linear_evm;
fb->rx_pilot_err[i]=
ppdu_info->evm_info.evm_2ss[i].pilot_error;
}
}
for (i = 0; i < ATH11K_SMART_ANT_MAX_SA_RSSI_CHAINS; i++) {
for (j = 0; j < ATH11K_SMART_ANT_MAX_BW; j++) {
fb->rx_rssi[i][j] = ppdu_info->rssi_chain[i][j];
}
}
ath11k_smart_ant_dbg_rx_feedback(ab, fb, evm_count);
}
void
ath11k_smart_ant_alg_proc_rx_feedback(struct ath11k_base *ab,
struct hal_rx_mon_ppdu_info *ppdu_info)
{
struct ath11k_sta *arsta;
struct ath11k_peer *peer = NULL;
struct ath11k *ar;
struct ath11k_smart_ant_rx_feedback rx_feedback;
struct ieee80211_supported_band *band;
rcu_read_lock();
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_by_id(ab, ppdu_info->peer_id);
if (peer && peer->sta) {
arsta = (struct ath11k_sta *)peer->sta->drv_priv;
if ((!arsta) || (!arsta->smart_ant_sta))
goto exit;
if (!arsta->arvif)
goto exit;
ar = arsta->arvif->ar;
if (!ar)
goto exit;
band = &ar->mac.sbands[NL80211_BAND_5GHZ];
if ((band) && (band->band == NL80211_BAND_5GHZ)) {
memset(&rx_feedback, 0, sizeof(rx_feedback));
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "Rx feedback from sta: %pM\n", peer->addr);
ath11k_smart_ant_rx_fb_fill(ab, ppdu_info, &rx_feedback, arsta);
// smart antenna callback rx feedback
ath11k_sa_api_update_rxfeedback(&g_smart_ant_obj, ar, peer->sta, &rx_feedback);
}
}
exit:
spin_unlock_bh(&ab->base_lock);
rcu_read_unlock();
}