blob: a74daed54e211e390fdc312848dbbf854830ce4c [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 "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;
/**
* 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;
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 ieee80211_sta *sta)
{
struct sa_node_info node = {0};
struct ath11k_sta *arsta = NULL;
uint8_t status = 0;
int ret;
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_disconnect == NULL) {
ath11k_warn(sa_api->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) || (arsta->arvif == NULL)) {
ath11k_dbg(sa_api->ab, ATH11K_DBG_SMART_ANT, "%s: missing operand: %p\n",
__func__, arsta);
return SMART_ANT_STATUS_FAILURE;
}
ath11k_smart_ant_alg_set_default(arsta->arvif);
}
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 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;
}
// processing status information
if (status & SMART_ANT_RX_CONFIG_REQUIRED) {
arsta = (struct ath11k_sta *)sta->drv_priv;
if ((arsta == NULL) || (arsta->arvif == NULL)) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "%s: missing operand: %p\n",
__func__, arsta);
return SMART_ANT_STATUS_FAILURE;
}
spin_unlock_bh(&ab->base_lock);
ath11k_smart_ant_alg_set_default(arsta->arvif);
spin_lock_bh(&ab->base_lock);
}
if (status & SMART_ANT_TX_CONFIG_REQUIRED) {
spin_unlock_bh(&ab->base_lock);
ath11k_smart_ant_alg_set_txantenna(sta, NULL);
spin_lock_bh(&ab->base_lock);
}
return 0;
}
static int ath11k_sa_api_update_rxfeedback(
struct ath11k_smart_ant_handler *sa_api, 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;
}
if (status & SMART_ANT_RX_CONFIG_REQUIRED) {
arsta = (struct ath11k_sta *)sta->drv_priv;
if ((arsta == NULL) || (arsta->arvif == NULL)) {
ath11k_dbg(ab, ATH11K_DBG_SMART_ANT, "%s: missing operand: %p\n",
__func__, arsta);
return -1;
}
spin_unlock_bh(&ab->base_lock);
ath11k_smart_ant_alg_set_default(arsta->arvif);
spin_lock_bh(&ab->base_lock);
}
// NOTYET: this can be enabled too
/* if (status & SMART_ANT_TX_CONFIG_REQUIRED) {
ath11k_smart_ant_alg_set_txantenna(sta, NULL);
}*/
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);
kfree(arsta->smart_ant_sta);
// smart antenna callback node disconnect
ath11k_sa_api_node_disconnect(&g_smart_ant_obj, sta);
}
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);
}
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;
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);
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;
}
// 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, 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, peer->sta, &rx_feedback);
}
}
exit:
spin_unlock_bh(&ab->base_lock);
rcu_read_unlock();
}