Search Linux Wireless

[RFC v1 011/256] cl8k: add ate.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Viktor Barna <viktor.barna@xxxxxxxxxx>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx>
---
 drivers/net/wireless/celeno/cl8k/ate.c | 841 +++++++++++++++++++++++++
 1 file changed, 841 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/ate.c

diff --git a/drivers/net/wireless/celeno/cl8k/ate.c b/drivers/net/wireless/celeno/cl8k/ate.c
new file mode 100644
index 000000000000..95e4e73cd9c0
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/ate.c
@@ -0,0 +1,841 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "ate.h"
+#include "tx/tx_inject.h"
+#include "calib.h"
+#include "rate_ctrl.h"
+#include "fw/msg_tx.h"
+#include "mib.h"
+#include "edca.h"
+#include "reg/reg_mac_hw.h"
+#include "reg/reg_macdsp_api.h"
+#include "reg/reg_riu.h"
+#include "tx/tx_queue.h"
+#include "utils/utils.h"
+#include "band.h"
+#include "fem.h"
+#include "chandef.h"
+#include "mac_addr.h"
+#include "power.h"
+#include "e2p.h"
+
+#define DIFF(_diff, _new, _old, _member)\
+       ((_diff)._member = (_new)._member - (_old)._member)
+
+/* Max freq delta is 100MHz in Q2 */
+#define MAX_FREQ_DELTA (100 << 2)
+
+static void set_fixed_rate(struct cl_hw *cl_hw)
+{
+       struct cl_ate_db *ate_db = &cl_hw->ate_db;
+       union cl_rate_ctrl_info_he rate_ctrl_he = {.word = 0};
+       u8 ltf = 0;
+
+       if (ate_db->mode == WRS_MODE_HE) {
+               rate_ctrl_he.field.spatial_conf = RATE_CNTRL_HE_SPATIAL_CONF_DEF;
+
+               if (ate_db->ltf == LTF_MAX)
+                       ltf = cl_map_gi_to_ltf(WRS_MODE_HE, ate_db->gi);
+               else
+                       ltf = ate_db->ltf;
+       }
+
+       cl_hw->entry_fixed_rate = true;
+
+       cl_rate_ctrl_set_fixed(cl_hw, rate_ctrl_he.word, ate_db->mode, ate_db->mcs,
+                              ate_db->nss, ate_db->bw, ate_db->gi, ltf);
+}
+
+static inline void read_stat(struct cl_hw *cl_hw, struct ate_stats *stats)
+{
+       stats->tx_bw20 = cl_mib_cntr_read(cl_hw, MIB_DOT11_20MHZ_FRAME_TRANSMITTED_COUNT);
+       stats->tx_bw40 = cl_mib_cntr_read(cl_hw, MIB_DOT11_40MHZ_FRAME_TRANSMITTED_COUNT);
+       stats->tx_bw80 = cl_mib_cntr_read(cl_hw, MIB_DOT11_80MHZ_FRAME_TRANSMITTED_COUNT);
+       stats->tx_bw160 = cl_mib_cntr_read(cl_hw, MIB_DOT11_160MHZ_FRAME_TRANSMITTED_COUNT);
+       stats->rx_bw20 = cl_mib_cntr_read(cl_hw, MIB_DOT11_20MHZ_FRAME_RECEIVED_COUNT);
+       stats->rx_bw40 = cl_mib_cntr_read(cl_hw, MIB_DOT11_40MHZ_FRAME_RECEIVED_COUNT);
+       stats->rx_bw80 = cl_mib_cntr_read(cl_hw, MIB_DOT11_80MHZ_FRAME_RECEIVED_COUNT);
+       stats->rx_bw160 = cl_mib_cntr_read(cl_hw, MIB_DOT11_160MHZ_FRAME_RECEIVED_COUNT);
+       stats->fcs_err = cl_mib_cntr_read(cl_hw, MIB_DOT11_FCS_ERROR_COUNT);
+       stats->phy_err = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_PHY_ERROR_COUNT);
+       stats->delimiter_err = cl_mib_cntr_read(cl_hw, MIB_DOT11_AMPDU_DELIMITER_CRC_ERROR_COUNT);
+}
+
+static bool is_valid_rate_he(struct cl_hw *cl_hw, u8 bw, u8 nss, u8 mcs, u8 gi)
+{
+       u8 ltf = cl_hw->ate_db.ltf;
+
+       /* BW */
+       if (!cl_hw->conf->ce_txldpc_en) {
+               if (bw > CHNL_BW_20) {
+                       u8 bw_mhz = BW_TO_MHZ(bw);
+
+                       cl_dbg_err(cl_hw, "Invalid bw [%u] - must be 20 when tx ldpc disabled\n",
+                                  bw_mhz);
+                       return false;
+               }
+       }
+
+       /* NSS */
+       if (nss >= cl_hw->conf->ce_tx_nss) {
+               cl_dbg_err(cl_hw, "Invalid nss [%u] - must be < %u\n",
+                          nss, cl_hw->conf->ce_tx_nss);
+               return false;
+       }
+
+       /* MCS */
+       if (cl_hw->conf->ce_txldpc_en) {
+               if (mcs >= WRS_MCS_MAX_HE) {
+                       cl_dbg_err(cl_hw, "Invalid mcs [%u] - must be 0 - 11\n", mcs);
+                       return false;
+               }
+       } else {
+               if (mcs >= WRS_MCS_10) {
+                       cl_dbg_err(cl_hw, "Invalid mcs [%u] - must be 0-9 when tx ldpc disabled\n",
+                                  mcs);
+                       return false;
+               }
+       }
+
+       /* GI */
+       if (gi >= WRS_GI_MAX_HE) {
+               cl_dbg_err(cl_hw, "Invalid gi [%u] - must be 0(0.8u)/1(1.6u)/2(3.2u)\n", gi);
+               return false;
+       }
+
+       /* LTF */
+       if (ltf > LTF_MAX) {
+               cl_dbg_err(cl_hw, "Invalid ltf [%u] - must be 0(X1)/1(X2)/2(X4)\n", ltf);
+               return -EINVAL;
+       } else if (ltf < LTF_MAX) {
+               /*
+                * Supported GI/LTF combinations:
+                * GI = 3.2: LTF_X4
+                * GI = 1.6: LTF_X2
+                * GI = 0.8: LTF_X1, LTF_X2, LTF_X4
+                */
+               if (gi == WRS_GI_LONG) {
+                       if (ltf != LTF_X4) {
+                               cl_dbg_err(cl_hw, "ltf must be 2 (=X4) for gi=0\n");
+                               return false;
+                       }
+               } else if (gi == WRS_GI_SHORT) {
+                       if (ltf != LTF_X2) {
+                               cl_dbg_err(cl_hw, "ltf must be 1 (=X2) for gi=1\n");
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
+static bool is_valid_rate_vht(struct cl_hw *cl_hw, u8 bw, u8 nss, u8 mcs, u8 gi)
+{
+       /* BW */
+       if (bw == CHNL_BW_160 && nss >= WRS_SS_3) {
+               cl_dbg_err(cl_hw, "bw 160 is invalid in 3/4 nss\n");
+               return false;
+       }
+
+       /* NSS */
+       if (nss >= cl_hw->conf->ce_tx_nss) {
+               cl_dbg_err(cl_hw, "Invalid nss [%u] - must be < %u\n",
+                          nss, cl_hw->conf->ce_tx_nss);
+               return false;
+       }
+
+       /* MCS */
+       if (mcs >= WRS_MCS_MAX_VHT) {
+               cl_dbg_err(cl_hw, "Invalid mcs [%u] - must be 0-9\n", mcs);
+               return false;
+       }
+
+       /* GI */
+       if (gi >= WRS_GI_MAX_VHT) {
+               cl_dbg_err(cl_hw, "Invalid gi [%u] - must be 0(0.8u)/1(0.4u)\n", gi);
+               return false;
+       }
+
+       /* Make sure it is not an invalid VHT rate */
+       if (bw == CHNL_BW_20 && mcs == WRS_MCS_9)
+               if (nss == WRS_SS_1 || nss == WRS_SS_2 || nss == WRS_SS_4) {
+                       cl_dbg_err(cl_hw, "nss 1/2/4 are invalid in bw 20, mcs 9\n");
+                       return false;
+               }
+
+       if (bw == CHNL_BW_80 && mcs == WRS_MCS_6 && nss == WRS_SS_3) {
+               cl_dbg_err(cl_hw, "bw 80, mcs 6, nss 3 is invalid\n");
+               return false;
+       }
+
+       return true;
+}
+
+static bool is_valid_rate_ht(struct cl_hw *cl_hw, u8 bw, u8 nss, u8 mcs, u8 gi)
+{
+       /* BW */
+       if (bw > CHNL_BW_40) {
+               u8 bw_mhz = BW_TO_MHZ(bw);
+
+               cl_dbg_err(cl_hw, "Invalid bw [%u] - must be 20/40\n", bw_mhz);
+               return false;
+       }
+
+       /* NSS */
+       if (nss >= cl_hw->conf->ce_tx_nss) {
+               cl_dbg_err(cl_hw, "Invalid nss [%u] - must be < %u\n",
+                          nss, cl_hw->conf->ce_tx_nss);
+               return false;
+       }
+
+       /* MCS */
+       if (mcs >= WRS_MCS_MAX_HT) {
+               cl_dbg_err(cl_hw, "Invalid mcs [%u] - must be 0 - 7\n", mcs);
+               return false;
+       }
+
+       /* GI */
+       if (gi >= WRS_GI_MAX_HT) {
+               cl_dbg_err(cl_hw, "Invalid gi [%u] - must be 0(0.8u)/1(0.4u)\n", gi);
+               return false;
+       }
+
+       return true;
+}
+
+static bool is_valid_rate_ofdm(struct cl_hw *cl_hw, u8 bw, u8 nss, u8 mcs, u8 gi)
+{
+       /*
+        * BW
+        * There is no need to check if bw is valid.
+        * It was already done in is_valid_bw_mhz().
+        * For ofdm we allow bw to be > 20, for FORMAT_NON_HT_DUP.
+        */
+
+       /* NSS */
+       if (nss != 0) {
+               cl_dbg_err(cl_hw, "Invalid  nss [%u] - must be 0\n", nss);
+               return false;
+       }
+
+       /* MCS */
+       if (mcs >= WRS_MCS_MAX_OFDM) {
+               cl_dbg_err(cl_hw, "Invalid  mcs [%u] - must be 0 - 7\n", mcs);
+               return false;
+       }
+
+       /* GI */
+       if (gi != 0) {
+               cl_dbg_err(cl_hw, "Invalid  gi [%u] - nust be 0\n", gi);
+               return false;
+       }
+
+       return true;
+}
+
+static bool is_valid_rate_cck(struct cl_hw *cl_hw, u8 bw, u8 nss, u8 mcs, u8 gi)
+{
+       /* BW */
+       if (bw > CHNL_BW_20) {
+               u8 bw_mhz = BW_TO_MHZ(bw);
+
+               cl_dbg_err(cl_hw, "Invalid bw [%u] - must be 20\n", bw_mhz);
+               return false;
+       }
+
+       /* NSS */
+       if (nss != 0) {
+               cl_dbg_err(cl_hw, "Invalid nss [%u] - must be 0\n", nss);
+               return false;
+       }
+
+       /* MCS */
+       if (mcs >= WRS_MCS_MAX_CCK) {
+               cl_dbg_err(cl_hw, "Invalid mcs [%u] - must be 0 - 3\n", mcs);
+               return false;
+       }
+
+       /* GI */
+       if (gi != 0) {
+               cl_dbg_err(cl_hw, "Invalid gi [%u] - nust be 0\n", gi);
+               return false;
+       }
+
+       return true;
+}
+
+static bool is_valid_rate(struct cl_hw *cl_hw)
+{
+       u8 mode = cl_hw->ate_db.mode;
+       u8 bw = cl_hw->ate_db.bw;
+       u8 nss = cl_hw->ate_db.nss;
+       u8 mcs = cl_hw->ate_db.mcs;
+       u8 gi = cl_hw->ate_db.gi;
+
+       switch (mode) {
+       case WRS_MODE_HE:
+               return is_valid_rate_he(cl_hw, bw, nss, mcs, gi);
+       case WRS_MODE_VHT:
+               return is_valid_rate_vht(cl_hw, bw, nss, mcs, gi);
+       case WRS_MODE_HT:
+               return is_valid_rate_ht(cl_hw, bw, nss, mcs, gi);
+       case WRS_MODE_OFDM:
+               return is_valid_rate_ofdm(cl_hw, bw, nss, mcs, gi);
+       case WRS_MODE_CCK:
+               return is_valid_rate_cck(cl_hw, bw, nss, mcs, gi);
+       default:
+               cl_dbg_err(cl_hw,
+                          "Invalid mode [%u] - must be: 0(cck)/1(ofdm)/2(ht)/3(vht)/4(he)\n",
+                          mode);
+               break;
+       }
+
+       return false;
+}
+
+static bool is_valid_bw(struct cl_hw *cl_hw)
+{
+       if (cl_hw->bw < cl_hw->ate_db.bw) {
+               cl_dbg_err(cl_hw, "TX bw [%u] can't be greater than channel bw [%u]\n",
+                          BW_TO_MHZ(cl_hw->ate_db.bw), BW_TO_MHZ(cl_hw->bw));
+               return false;
+       }
+
+       return true;
+}
+
+static bool is_valid_bw_mhz(struct cl_hw *cl_hw, u8 bw_mhz)
+{
+       if (BAND_IS_5G_6G(cl_hw)) {
+               if (bw_mhz != BW_TO_MHZ(CHNL_BW_20) &&
+                   bw_mhz != BW_TO_MHZ(CHNL_BW_40) &&
+                   bw_mhz != BW_TO_MHZ(CHNL_BW_80) &&
+                   bw_mhz != BW_TO_MHZ(CHNL_BW_160)) {
+                       cl_dbg_err(cl_hw,
+                                  "Invalid bw [%u] - must be 20/40/80/160\n", bw_mhz);
+                       return false;
+               }
+       } else {
+               if (bw_mhz != BW_TO_MHZ(CHNL_BW_20) &&
+                   bw_mhz != BW_TO_MHZ(CHNL_BW_40)) {
+                       cl_dbg_err(cl_hw, "Invalid bw [%u] - must be 20/40\n", bw_mhz);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+int cl_ate_reset(struct wiphy *wiphy, struct wireless_dev *wdev,
+                const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       if (cl_tx_inject_is_running(cl_hw)) {
+               tasklet_kill(&cl_hw->tx_inject.tasklet);
+               cl_ate_stop(wiphy, NULL, NULL, 0);
+       }
+
+       /* Reset rate parameters */
+       cl_hw->ate_db.mode = 0;
+       cl_hw->ate_db.bw = 0;
+       cl_hw->ate_db.nss = 0;
+       cl_hw->ate_db.mcs = 0;
+       cl_hw->ate_db.gi = 0;
+       cl_hw->ate_db.ltf = LTF_MAX;
+
+       cl_hw->entry_fixed_rate = false;
+
+       /* Reset TX power */
+       cl_hw->ate_db.tx_power = S8_MAX;
+       memset(cl_hw->ate_db.tx_power_offset, S8_MAX, MAX_ANTENNAS);
+
+       cl_tx_inject_reset(cl_hw);
+
+       /* Go to ACTIVE state */
+       if (cl_hw->chip->conf->ce_production_mode)
+               cl_msg_tx_set_idle(cl_hw, MAC_ACTIVE);
+
+       if (cl_hw->ate_db.ant_mask) {
+               u8 default_ant_mask = ANT_MASK(cl_hw->num_antennas);
+
+               cl_msg_tx_set_ant_bitmap(cl_hw, default_ant_mask);
+               cl_hw->ate_db.ant_mask = 0;
+       }
+
+       cl_hw->ate_db.active = true;
+
+       /*
+        * Rearm last_tbtt_irq so that error message will
+        * not be printed in cl_irq_status_tbtt()
+        */
+       cl_hw->last_tbtt_irq = jiffies;
+
+       cl_dbg_trace(cl_hw, "\n");
+
+       return 0;
+}
+
+int cl_ate_mode(struct wiphy *wiphy, struct wireless_dev *wdev,
+               const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       cl_hw->ate_db.mode = *(u8 *)data;
+
+       cl_dbg_trace(cl_hw, "mode = %u\n", cl_hw->ate_db.mode);
+
+       return 0;
+}
+
+int cl_ate_bw(struct wiphy *wiphy, struct wireless_dev *wdev,
+             const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u8 bw_mhz = *(u8 *)data;
+
+       if (!is_valid_bw_mhz(cl_hw, bw_mhz))
+               return -EINVAL;
+
+       cl_hw->ate_db.bw = MHZ_TO_BW(bw_mhz);
+
+       cl_dbg_trace(cl_hw, "bw = %u\n", bw_mhz);
+
+       return 0;
+}
+
+int cl_ate_mcs(struct wiphy *wiphy, struct wireless_dev *wdev,
+              const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       cl_hw->ate_db.mcs = *(u8 *)data;
+
+       cl_dbg_trace(cl_hw, "mcs = %u\n", cl_hw->ate_db.mcs);
+
+       return 0;
+}
+
+int cl_ate_nss(struct wiphy *wiphy, struct wireless_dev *wdev,
+              const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       cl_hw->ate_db.nss = *(u8 *)data;
+
+       cl_dbg_trace(cl_hw, "nss = %u\n", cl_hw->ate_db.nss);
+
+       return 0;
+}
+
+int cl_ate_gi(struct wiphy *wiphy, struct wireless_dev *wdev,
+             const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       cl_hw->ate_db.gi = *(u8 *)data;
+       cl_dbg_trace(cl_hw, "gi = %u\n", cl_hw->ate_db.gi);
+
+       return 0;
+}
+
+int cl_ate_ltf(struct wiphy *wiphy, struct wireless_dev *wdev,
+              const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       cl_hw->ate_db.ltf = *(u8 *)data;
+
+       cl_dbg_trace(cl_hw, "ltf = %u\n", cl_hw->ate_db.ltf);
+
+       return 0;
+}
+
+int cl_ate_ldpc(struct wiphy *wiphy, struct wireless_dev *wdev,
+               const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       cl_hw->conf->ce_txldpc_en = (bool)(*(u8 *)data);
+
+       cl_dbg_trace(cl_hw, "ldpc = %u\n", cl_hw->conf->ce_txldpc_en);
+
+       return 0;
+}
+
+int cl_ate_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+                  const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u32 channel = ((u32 *)data)[0];
+       u32 bw_mhz = ((u32 *)data)[1];
+       u32 bw = 0;
+       u32 primary = 0;
+       u32 center = 0;
+       enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
+
+       if (!is_valid_bw_mhz(cl_hw, bw_mhz))
+               return -EINVAL;
+
+       if (cl_band_is_6g(cl_hw) && channel == 2 &&
+           bw_mhz != BW_TO_MHZ(CHNL_BW_20)) {
+               cl_dbg_err(cl_hw, "Only 20Mhz is allowed for channel 2\n");
+               return -EINVAL;
+       }
+
+       bw = MHZ_TO_BW(bw_mhz);
+
+       if (cl_chandef_calc(cl_hw, channel, bw, &width, &primary, &center)) {
+               cl_dbg_err(cl_hw, "cl_chandef_calc failed\n");
+               return -EINVAL;
+       }
+
+       if (cl_hw->set_calib) {
+               cl_hw->set_calib = false;
+               cl_calib_power_read(cl_hw);
+       }
+
+       cl_msg_tx_set_channel(cl_hw, channel, bw, primary, center);
+
+       return 0;
+}
+
+int cl_ate_ant(struct wiphy *wiphy, struct wireless_dev *wdev,
+              const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u8 ant = *(u8 *)data;
+       u8 mask;
+
+       if (ant >= MAX_ANTENNAS) {
+               cl_dbg_err(cl_hw, "Invalid antenna value [%u]", ant);
+               return -EINVAL;
+       }
+
+       mask = (1 << ant);
+
+       if (mask != cl_hw->ate_db.ant_mask) {
+               cl_hw->ate_db.ant_mask = mask;
+               cl_msg_tx_set_ant_bitmap(cl_hw, mask);
+       }
+
+       cl_dbg_trace(cl_hw, "ant = %u, mask = 0x%x\n", ant, mask);
+
+       return 0;
+}
+
+#define FULL_ANT_MASK ((1 << MAX_ANTENNAS) - 1)
+
+int cl_ate_multi_ant(struct wiphy *wiphy, struct wireless_dev *wdev,
+                    const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u8 mask = *(u8 *)data;
+
+       if (mask == 0 || mask > FULL_ANT_MASK) {
+               cl_dbg_err(cl_hw, "Invalid antenna bitmap [0x%x]", mask);
+               return -EINVAL;
+       }
+
+       if (mask != cl_hw->ate_db.ant_mask) {
+               cl_hw->ate_db.ant_mask = mask;
+               cl_msg_tx_set_ant_bitmap(cl_hw, mask);
+       }
+
+       cl_dbg_trace(cl_hw, "mask = 0x%x\n", mask);
+
+       return 0;
+}
+
+int cl_ate_packet_len(struct wiphy *wiphy, struct wireless_dev *wdev,
+                     const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u32 packet_len = *(u32 *)data;
+
+       cl_dbg_trace(cl_hw, "packet_len = %u\n", packet_len);
+
+       return cl_tx_inject_set_length(cl_hw, packet_len);
+}
+
+int cl_ate_vector(struct wiphy *wiphy, struct wireless_dev *wdev,
+                 const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u32 size = data_len / sizeof(u32);
+       int ret = 0;
+
+       cl_dbg_trace(cl_hw, "\n");
+
+       ret = cl_calib_pivot_channels_set(cl_hw, data, size);
+
+       /* Write EEPROM version when starting calibration process */
+       if (!ret)
+               return cl_e2p_write_version(cl_hw->chip);
+
+       return ret;
+}
+
+int cl_ate_vector_reset(struct wiphy *wiphy, struct wireless_dev *wdev,
+                       const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       cl_dbg_trace(cl_hw, "\n");
+
+       return cl_calib_pivot_channels_reset(cl_hw);
+}
+
+#define FREQ_OFST_MAX  959
+
+int cl_ate_freq_offset(struct wiphy *wiphy, struct wireless_dev *wdev,
+                      const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u16 freq_offset = *(u16 *)data;
+
+       if (freq_offset > FREQ_OFST_MAX) {
+               cl_dbg_err(cl_hw, "Invalid freq offset 0x%04x\n", freq_offset);
+               return -1;
+       }
+
+       cl_dbg_trace(cl_hw, "Freq offset 0x%04x\n", freq_offset);
+
+       return cl_msg_tx_set_freq_offset(cl_hw, freq_offset);
+}
+
+int cl_ate_stat(struct wiphy *wiphy, struct wireless_dev *wdev,
+               const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       struct ate_stats new_stats;
+       struct ate_stats ret_stats;
+
+       read_stat(cl_hw, &new_stats);
+
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, tx_bw20);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, tx_bw40);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, tx_bw80);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, tx_bw160);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, rx_bw20);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, rx_bw40);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, rx_bw80);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, rx_bw160);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, fcs_err);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, phy_err);
+       DIFF(ret_stats, new_stats, cl_hw->ate_db.stats, delimiter_err);
+
+       /* Present rx seccess of the defined bw */
+       switch (cl_hw->ate_db.bw) {
+       case CHNL_BW_20:
+               ret_stats.rx_success = ret_stats.rx_bw20;
+               break;
+       case CHNL_BW_40:
+               ret_stats.rx_success = ret_stats.rx_bw40;
+               break;
+       case CHNL_BW_80:
+               ret_stats.rx_success = ret_stats.rx_bw80;
+               break;
+       case CHNL_BW_160:
+               ret_stats.rx_success = ret_stats.rx_bw160;
+               break;
+       default:
+               /* Should not get here */
+               return -EINVAL;
+       }
+
+       /* Read rssi */
+       macdsp_api_inbdpow_20_unpack(cl_hw, &ret_stats.rssi3, &ret_stats.rssi2,
+                                    &ret_stats.rssi1, &ret_stats.rssi0);
+       ret_stats.rssi4 = S8_MIN;
+       ret_stats.rssi5 = S8_MIN;
+
+       cl_dbg_trace(cl_hw, "\n");
+
+       return cl_vendor_reply(cl_hw, &ret_stats, sizeof(struct ate_stats));
+}
+
+int cl_ate_stat_reset(struct wiphy *wiphy, struct wireless_dev *wdev,
+                     const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       read_stat(cl_hw, &cl_hw->ate_db.stats);
+
+       cl_dbg_trace(cl_hw, "\n");
+
+       return 0;
+}
+
+int cl_ate_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+                const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       s8 tx_power = *(s8 *)data;
+       s8 tx_power_q1 = 0;
+
+       if (tx_power < POWER_MIN_DB || tx_power > POWER_MAX_DB) {
+               cl_dbg_err(cl_hw, "Invalid power (%d). Must be between %d and %d\n",
+                          tx_power, POWER_MIN_DB, POWER_MAX_DB);
+               return 0;
+       }
+
+       cl_hw->ate_db.tx_power = tx_power;
+       tx_power_q1 = tx_power << 1;
+
+       cl_dbg_trace(cl_hw, "ate_power = %u\n", tx_power);
+
+       memset(&cl_hw->phy_data_info.data->pwr_tables,
+              tx_power_q1, sizeof(struct cl_pwr_tables));
+
+       cl_msg_tx_refresh_power(cl_hw);
+
+       return 0;
+}
+
+int cl_ate_power_offset(struct wiphy *wiphy, struct wireless_dev *wdev,
+                       const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       s8 *pwr_offset = cl_hw->ate_db.tx_power_offset;
+       int i;
+
+       for (i = 0; i < MAX_ANTENNAS; i++) {
+               pwr_offset[i] = ((s8 *)data)[i];
+
+               if (pwr_offset[i] < POWER_OFFSET_MIN_Q2 ||
+                   pwr_offset[i] > POWER_OFFSET_MAX_Q2) {
+                       cl_dbg_err(cl_hw, "Invalid power offset (%d). Valid range (%d - %d)\n",
+                                  pwr_offset[i], POWER_OFFSET_MIN_Q2, POWER_OFFSET_MAX_Q2);
+                       memset(pwr_offset, S8_MAX, MAX_ANTENNAS);
+                       return -1;
+               }
+       }
+
+       cl_dbg_trace(cl_hw, "power_offset = %d,%d,%d,%d,%d,%d\n",
+                    pwr_offset[0], pwr_offset[1], pwr_offset[2],
+                    pwr_offset[3], pwr_offset[4], pwr_offset[5]);
+
+       return cl_msg_tx_set_ant_pwr_offset(cl_hw, pwr_offset);
+}
+
+int cl_ate_tx_start(struct wiphy *wiphy, struct wireless_dev *wdev,
+                   const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       u32 tx_cnt = *(u32 *)data;
+
+       if (!cl_hw->ate_db.active) {
+               cl_dbg_err(cl_hw, "Must call 'ATE reset' first.\n");
+               return -EPERM;
+       }
+
+       if (tx_cnt == 0) {
+               cl_tx_inject_stop_traffic(cl_hw);
+               return 0;
+       }
+
+       if (cl_tx_inject_is_running(cl_hw)) {
+               cl_dbg_err(cl_hw, "TX already running.\n");
+               return -EPERM;
+       }
+
+       if (!is_valid_rate(cl_hw) || !is_valid_bw(cl_hw))
+               return -EPERM;
+
+       set_fixed_rate(cl_hw);
+       cl_tx_inject_start(cl_hw, tx_cnt);
+
+       cl_dbg_trace(cl_hw, "\n");
+
+       return 0;
+}
+
+int cl_ate_tx_continuous(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       if (!cl_hw->ate_db.active) {
+               cl_dbg_err(cl_hw, "Must call 'ATE reset' first.\n");
+               return -EPERM;
+       }
+
+       if (cl_tx_inject_is_running(cl_hw)) {
+               cl_dbg_err(cl_hw, "TX already running.\n");
+               return -EPERM;
+       }
+
+       if (!is_valid_rate(cl_hw) || !is_valid_bw(cl_hw))
+               return -EPERM;
+
+       set_fixed_rate(cl_hw);
+       cl_tx_inject_start_continuous(cl_hw);
+
+       cl_dbg_trace(cl_hw, "\n");
+
+       return 0;
+}
+
+int cl_ate_stop(struct wiphy *wiphy, struct wireless_dev *wdev,
+               const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       cl_tx_inject_stop(cl_hw);
+
+       /* Go back to IDLE state */
+       if (cl_hw->chip->conf->ce_production_mode)
+               cl_msg_tx_set_idle(cl_hw, MAC_IDLE_SYNC);
+
+       cl_hw->ate_db.active = false;
+
+       cl_dbg_trace(cl_hw, "\n");
+
+       return 0;
+}
+
+int cl_ate_help(struct wiphy *wiphy, struct wireless_dev *wdev,
+               const void *data, int data_len)
+{
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       char *ret_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int err = 0;
+
+       if (!ret_buf)
+               return -ENOMEM;
+
+       snprintf(ret_buf, PAGE_SIZE,
+                "usage:\n"
+                "reset - Reset ATE configuration\n"
+                "mode <0=CCK,1=OFDM,2=HT,3=VHT,4=HE> - Set mode\n"
+                "bw <20/40/80/160> - Set TX bandwidth parameter\n"
+                "mcs <CCK=0-3, OFDM/HT=0-7, VHT=0-9, HE=0-11> - set mcs parameter\n"
+                "nss <0-3> - set nss parameter\n"
+                "gi <CCK/OFDM=0, HT/VHT=0-1, HE=0-2> - set gi\n"
+                "ltf <HE-LTF: 0=LTF_X1,1=LTF_X2,2=LTF_X4> - set ltf\n"
+                "ldpc <0=Disable, 1=Enable> - set ldpc parameter\n"
+                "channel <ch number> <ch bw [20/40/80/160]> <Frequency delta"
+                " from center Frequency (optional)> - change channel\n"
+                "ant <Antenna index 0-5> - Enable single antenna\n"
+                "multi_ant <Ant bitmap> - Enable multiple antennas\n"
+                "packet_len <packet length (16-4096)> - Set length of packets to inject\n"
+                "vector <Channel vector separated by space> - Set"
+                " vector of channels to calibrate\n"
+                "freq_offset <0-959> - Set frequency offset\n"
+                "stat <reset (optional)> - Display/Reset statistics\n"
+                "power <-10dB - 30dB> - Set tx power\n"
+                "power_offset <offset_ant1 ... offset_ant6> - Power"
+                " offset per anthenna [range +/-64][units=0.25dB]\n"
+                "tx_start <Num of packets> - Start TX packets\n"
+                "tx_continuous - Start transmitting infinite packets\n"
+                "stop - Stop transmission\n");
+
+       err = cl_vendor_reply(cl_hw, ret_buf, strlen(ret_buf));
+       kfree(ret_buf);
+
+       return err;
+}
+
--
2.30.0

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________





[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux