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> --- .../net/wireless/celeno/cl8k/utils/utils.c | 388 ++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/utils/utils.c diff --git a/drivers/net/wireless/celeno/cl8k/utils/utils.c b/drivers/net/wireless/celeno/cl8k/utils/utils.c new file mode 100644 index 000000000000..6d6f913ae95a --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/utils/utils.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: MIT +/* Copyright(c) 2019-2021, Celeno Communications Ltd. */ + +#include <linux/dma-mapping.h> +#include <linux/list.h> +#include <linux/jiffies.h> +#include <linux/kthread.h> +#include <net/mac80211.h> +#include <linux/sched/signal.h> + +#include "utils/utils.h" +#include "rx/rx.h" +#include "tx/tx.h" +#include "fw/msg_tx.h" +#include "debugfs.h" +#include "ipc_shared.h" +#include "rssi.h" +#include "traffic.h" +#include "reg/reg_riu.h" +#include "reg/reg_mac_hw.h" +#include "utils/ip.h" + +#define GI_08 0 +#define GI_16 1 +#define GI_32 2 +#define GI_04 3 + +#define GI_MAX_FW 4 +#define GI_MAX_HE 3 +#define GI_MAX_HT_VHT 2 + +#define CL_TSF_LOW_MIGHT_OVERFLOW_TH 0xFFFFF000 + +static u8 conv_wrs_gi_ht_vht[GI_MAX_HT_VHT] = { + [WRS_GI_LONG] = GI_08, + [WRS_GI_SHORT] = GI_04 +}; + +static u8 conv_wrs_gi_he[GI_MAX_HE] = { + [WRS_GI_LONG] = GI_32, + [WRS_GI_SHORT] = GI_16, + [WRS_GI_VSHORT] = GI_08 +}; + +static u8 conv_fw_gi_ht_vht[GI_MAX_FW] = { + [GI_08] = WRS_GI_LONG, + [GI_16] = 0, + [GI_32] = 0, + [GI_04] = WRS_GI_SHORT, +}; + +static u8 conv_fw_gi_he[GI_MAX_FW] = { + [GI_08] = WRS_GI_VSHORT, + [GI_16] = WRS_GI_SHORT, + [GI_32] = WRS_GI_LONG, + [GI_04] = 0, +}; + +void cl_hex_dump(char *caption, u8 *buffer, u32 length, u32 offset, bool is_byte) +{ + u8 *pt = buffer; + u32 i; + bool end_nl = false; + char buf[STR_LEN_256B] = {0}; + int len = 0; + + if (caption) + pr_debug("%s: %p, len = %u\n", caption, buffer, length); + + if (is_byte) { + for (i = 0; i < length; i++) { + if (i % 16 == 0) + len += snprintf(buf + len, sizeof(buf) - len, + "0x%04x : ", i + offset); + len += snprintf(buf + len, sizeof(buf) - len, + "%02x ", ((u8)pt[i])); + end_nl = true; + if (i % 16 == 15) { + pr_debug("%s", buf); + len = 0; + end_nl = false; + } + } + } else { + for (i = 0; i < (length / sizeof(u32)); i++) { + if (i % 4 == 0) + len += snprintf(buf + len, sizeof(buf) - len, + "0x%04x : ", + (u32)(i * sizeof(u32) + offset)); + len += snprintf(buf + len, sizeof(buf) - len, + "%08x ", *((u32 *)(pt + i * sizeof(u32)))); + end_nl = true; + if (i % 4 == 3) { + pr_debug("%s", buf); + len = 0; + end_nl = false; + } + } + } + + if (end_nl) + pr_debug("%s", buf); +} + +u8 convert_gi_format_wrs_to_fw(u8 wrs_mode, u8 gi) +{ + if (wrs_mode == WRS_MODE_HE && gi < GI_MAX_HE) + return conv_wrs_gi_he[gi]; + else if (wrs_mode > WRS_MODE_OFDM && gi < GI_MAX_HT_VHT) + return conv_wrs_gi_ht_vht[gi]; + else + return 0; +} + +u8 convert_gi_format_fw_to_wrs(u8 format_mode, u8 gi) +{ + if (gi < GI_MAX_FW) { + if (format_mode >= FORMATMOD_HE_SU) + return conv_fw_gi_he[gi]; + else if (format_mode >= FORMATMOD_HT_MF) + return conv_fw_gi_ht_vht[gi]; + } + + return 0; +} + +static u8 map_gi_to_ltf[WRS_GI_MAX] = { + [WRS_GI_LONG] = LTF_X4, + [WRS_GI_SHORT] = LTF_X2, + [WRS_GI_VSHORT] = LTF_X2, +}; + +u8 cl_map_gi_to_ltf(u8 mode, u8 gi) +{ + if (mode == WRS_MODE_HE && gi < WRS_GI_MAX) + return map_gi_to_ltf[gi]; + + return 0; +} + +/* This table holds 10^(-110 -> 0) Q39 values for rx RSSI and noise floor calculations */ +#define CL_EXP_TBL_SIZE 111 /* 10^x table size (-110 -> 0dBm) */ + +static u64 CL_EXP_10[CL_EXP_TBL_SIZE] = { + 0x7FFFFFFFFF, 0x65AC8C2F36, 0x50C335D3DB, 0x4026E73CCD, 0x32F52CFEEA, 0x287A26C490, + 0x2026F30FBB, 0x198A13577C, 0x144960C577, 0x101D3F2D96, 0x0CCCCCCCCD, 0x0A2ADAD185, + 0x08138561FC, 0x066A4A52E1, 0x0518847FE4, 0x040C3713A8, 0x0337184E5F, 0x028DCEBBF3, + 0x0207567A25, 0x019C86515C, 0x0147AE147B, 0x01044914F4, 0x00CEC089CC, 0x00A43AA1E3, + 0x008273A664, 0x00679F1B91, 0x00524F3B0A, 0x0041617932, 0x0033EF0C37, 0x002940A1BC, + 0x0020C49BA6, 0x001A074EE5, 0x0014ACDA94, 0x00106C4364, 0x000D0B90A4, 0x000A5CB5F5, + 0x00083B1F81, 0x000689BF52, 0x0005318139, 0x000420102C, 0x000346DC5D, 0x00029A54B1, + 0x000211490F, 0x0001A46D24, 0x00014DF4DD, 0x0001094565, 0x0000D2B65A, 0x0000A75FEF, + 0x000084F352, 0x0000699B38, 0x000053E2D6, 0x000042A212, 0x000034EDB5, 0x00002A0AEA, + 0x0000216549, 0x00001A86F1, 0x000015123C, 0x000010BCCB, 0x00000D4B88, 0x00000A8F86, + 0x000008637C, 0x000006A9CF, 0x0000054AF8, 0x000004344B, 0x00000356EE, 0x000002A718, + 0x0000021B6C, 0x000001AC7B, 0x000001545A, 0x0000010E5A, 0x000000D6C0, 0x000000AA95, + 0x000000877F, 0x0000006BA1, 0x000000557E, 0x00000043E9, 0x00000035F1, 0x0000002AD9, + 0x0000002209, 0x0000001B09, 0x000000157A, 0x000000110F, 0x0000000D8D, 0x0000000AC3, + 0x000000088D, 0x00000006CA, 0x0000000565, 0x0000000449, 0x0000000367, 0x00000002B4, + 0x0000000226, 0x00000001B5, 0x000000015B, 0x0000000114, 0x00000000DB, 0x00000000AE, + 0x000000008A, 0x000000006E, 0x0000000057, 0x0000000045, 0x0000000037, 0x000000002C, + 0x0000000023, 0x000000001C, 0x0000000016, 0x0000000011, 0x000000000E, 0x000000000B, + 0x0000000009, 0x0000000007, 0x0000000005 +}; + +static s8 cl_eng_to_noise_floor(u64 eng) +{ + s8 i = 0; + s8 noise = 0; + s64 min_delta = S64_MAX; + + for (i = ARRAY_SIZE(CL_EXP_10) - 1; i >= 0; i--) { + if (abs((s64)(((s64)eng) - ((s64)CL_EXP_10[i]))) < min_delta) { + min_delta = abs((s64)(((s64)eng) - ((s64)CL_EXP_10[i]))); + noise = i; + } + } + + return (-noise); +} + +static void cl_read_reg_noise(struct cl_hw *cl_hw, s8 res[4]) +{ + u32 reg_val = riu_agcinbdpow_20_pnoisestat_get(cl_hw); + u8 i = 0; + + for (i = 0; i < 4; i++) { + u8 curr_val = (reg_val >> (i * 8)) & 0xFF; + /* Convert reg value to real value */ + res[i] = curr_val - 0xFF; + } +} + +s8 cl_calc_noise_floor(struct cl_hw *cl_hw, const s8 *reg_noise_floor) +{ + s8 noise_floor[4] = {0}; + u64 noise_floor_eng = 0; + + if (reg_noise_floor) + memcpy(noise_floor, reg_noise_floor, sizeof(noise_floor)); + else + cl_read_reg_noise(cl_hw, noise_floor); + + noise_floor[0] = abs(noise_floor[0]); + noise_floor[1] = abs(noise_floor[1]); + noise_floor[2] = abs(noise_floor[2]); + noise_floor[3] = abs(noise_floor[3]); + + BUILD_BUG_ON(CL_EXP_TBL_SIZE > S8_MAX); + noise_floor_eng = (CL_EXP_10[min_t(s8, noise_floor[0], CL_EXP_TBL_SIZE - 1)] + + CL_EXP_10[min_t(s8, noise_floor[1], CL_EXP_TBL_SIZE - 1)] + + CL_EXP_10[min_t(s8, noise_floor[2], CL_EXP_TBL_SIZE - 1)] + + CL_EXP_10[min_t(s8, noise_floor[3], CL_EXP_TBL_SIZE - 1)]); + + noise_floor_eng = div64_u64(noise_floor_eng, 4); + + return cl_eng_to_noise_floor(noise_floor_eng); +} + +u8 cl_convert_signed_to_reg_value(s8 val) +{ + bool sign = (val < 0 ? true : false); + u8 res = abs(val); + + if (sign) + res |= (1 << 7); + + return res; +} + +static const int nl_width_to_phy_bw[] = { + [NL80211_CHAN_WIDTH_20_NOHT] = CHNL_BW_20, + [NL80211_CHAN_WIDTH_20] = CHNL_BW_20, + [NL80211_CHAN_WIDTH_40] = CHNL_BW_40, + [NL80211_CHAN_WIDTH_80] = CHNL_BW_80, + [NL80211_CHAN_WIDTH_80P80] = CHNL_BW_20, + [NL80211_CHAN_WIDTH_160] = CHNL_BW_160, + [NL80211_CHAN_WIDTH_5] = CHNL_BW_20, + [NL80211_CHAN_WIDTH_10] = CHNL_BW_20, +}; + +u8 width_to_bw(enum nl80211_chan_width width) +{ + if (width <= NL80211_CHAN_WIDTH_10) + return nl_width_to_phy_bw[width]; + + return NL80211_CHAN_WIDTH_20; +} + +static const int phy_bw_to_nl_width[] = { + [CHNL_BW_20] = NL80211_CHAN_WIDTH_20, + [CHNL_BW_40] = NL80211_CHAN_WIDTH_40, + [CHNL_BW_80] = NL80211_CHAN_WIDTH_80, + [CHNL_BW_160] = NL80211_CHAN_WIDTH_160, +}; + +enum nl80211_chan_width bw_to_width(u8 bw) +{ + if (bw < CHNL_BW_MAX) + return phy_bw_to_nl_width[bw]; + + return CHNL_BW_20; +} + +bool cl_is_valid_auth_mode(bool is_wpa, u8 auth_mode) +{ + return is_wpa ? (auth_mode <= CL_AKM_SUITE_PSK) : + (auth_mode <= CL_AKM_SUITE_FT_FILS_SHA384); +} + +bool cl_is_open_auth_mode(u8 auth_mode) +{ + return auth_mode == CL_AKM_SUITE_OPEN; +} + +u64 cl_get_tsf_u64(struct cl_hw *cl_hw) +{ + u32 tsf_low = mac_hw_tsf_lo_get(cl_hw); + u32 tsf_high = mac_hw_tsf_hi_get(cl_hw); + u64 tsf; + + if (tsf_low > CL_TSF_LOW_MIGHT_OVERFLOW_TH) { + u32 tmp_tsf_low = mac_hw_tsf_lo_get(cl_hw); + + /* Overflow of tsf_low occurred */ + if (tmp_tsf_low < 0xFFFFF000) + tsf_high++; + } + + tsf = ((u64)tsf_high << 32) | (u64)tsf_low; + + return tsf; +} + +u8 cl_center_freq_offset(u8 bw) +{ + if (bw == CHNL_BW_160) + return 70; + + if (bw == CHNL_BW_80) + return 30; + + if (bw == CHNL_BW_40) + return 10; + + return 0; +} + +u8 max_bw_idx(u8 wrs_mode, bool is_24g) +{ + if (wrs_mode < WRS_MODE_HT) + return CHNL_BW_20 + 1; + + if (wrs_mode == WRS_MODE_HT || is_24g) + return CHNL_BW_40 + 1; + + return CHNL_BW_MAX; +} + +bool cl_hw_mode_is_b_or_bg(struct cl_hw *cl_hw) +{ + return (cl_hw->conf->ha_hw_mode == HW_MODE_B || + cl_hw->conf->ha_hw_mode == HW_MODE_BG); +} + +void cl_snprintf(char **buf, int *offset, size_t *size, const char *fmt, ...) +{ + void *new_buf = NULL; + va_list args; + u16 str_len = strlen(fmt); + u16 new_size; + + if (!*buf) { + *size = PAGE_SIZE; + *buf = kzalloc(*size, GFP_KERNEL); + if (!*buf) { + pr_err("Buffer allocation failed (%u)\n", (u32)*size); + return; + } + } + + /* Additional space is required */ + if (str_len > *size - *offset) { + new_size = *size * 2; + new_buf = kvzalloc(new_size, GFP_KERNEL); + if (new_buf) { + *size = new_size; + memcpy(new_buf, *buf, strlen(*buf)); + kvfree(*buf); + *buf = new_buf; + } else { + pr_err("Buffer allocation failed (%u)\n", (u32)*size); + return; + } + } + + va_start(args, fmt); + *offset += vsnprintf(*buf + *offset, *size, fmt, args); + va_end(args); +} + +bool cl_is_eapol(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + __le16 fc = hdr->frame_control; + unsigned int hdrlen = 0; + unsigned short ethertype = 0; + u8 *temp = NULL; + + /* Find the wireless header size */ + hdrlen = ieee80211_has_a4(fc) ? 30 : 24; + + if (ieee80211_is_data_qos(fc)) { + hdrlen += IEEE80211_QOS_CTL_LEN; + + if (ieee80211_has_order(fc)) + hdrlen += IEEE80211_HT_CTL_LEN; + } + + /* Skip wireless header */ + temp = (u8 *)(skb->data + hdrlen); + + /* Skip LLC and SNAP header */ + if (PKT_HAS_LLC_HDR(temp)) + ethertype = get_ether_type(LENGTH_LLC + LENGTH_SSNAP - 2, temp); + + return (ethertype == ETH_P_PAE) ? true : false; +} -- 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. ________________________________