| /** |
| * @file |
| * @brief |
| * PHY module Power-per-rate API. Provides interface functions and definitions for |
| * ppr structure for use containing regulatory and board limits and tx power targets. |
| * |
| * Broadcom Proprietary and Confidential. Copyright (C) 2017, |
| * All Rights Reserved. |
| * |
| * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom; |
| * the contents of this file may not be disclosed to third parties, copied |
| * or duplicated in any form, in whole or in part, without the prior |
| * written permission of Broadcom. |
| * |
| * |
| * <<Broadcom-WL-IPTag/Proprietary:>> |
| * |
| * $Id: $ |
| */ |
| |
| /** |
| * @file |
| * @brief |
| * Sometimes not all rates should reach the same max power, in order to comply with FCC. Different |
| * rates (even different sub-band bands) may want different max power. |
| * |
| * real_max_power[antenna, rate] = max_power[antenna] - power_offset[rate] |
| * |
| * Both regulatory restrictions (by wlc_channel.c) and SPROM restrictions (SPROm is read by phy |
| * code) determine this power offset. |
| */ |
| |
| |
| #if defined(__FreeBSD__) |
| #if defined(_KERNEL) |
| #include <wlc_cfg.h> |
| #endif /* defined(_KERNEL) */ |
| #endif |
| |
| #include <typedefs.h> |
| #include <bcmendian.h> |
| #include <bcmwifi_channels.h> |
| #include <wlc_ppr.h> |
| #include <bcmutils.h> |
| |
| #ifndef BCMDRIVER |
| |
| #ifndef WL_BEAMFORMING |
| #define WL_BEAMFORMING /* enable TxBF definitions for utility code */ |
| #endif |
| |
| #ifndef WL11AC_160 |
| #define WL11AC_160 /* Enable WL11AC_160 for utility code */ |
| #endif |
| |
| #ifndef WL11AC_80P80 |
| #define WL11AC_80P80 /* Enable WL11AC_80P80 for utility code */ |
| #endif |
| |
| #ifndef bcopy |
| #include <string.h> |
| #include <stdlib.h> |
| #define bcopy(src, dst, len) memcpy((dst), (src), (len)) |
| #endif |
| |
| #ifndef ASSERT |
| #define ASSERT(exp) do {} while (0) |
| #endif |
| #endif /* BCMDRIVER */ |
| |
| /* ppr local TXBF_ENAB() macro because wlc->pub struct is not accessible */ |
| #ifdef WL_BEAMFORMING |
| #if defined(WLTXBF_DISABLED) |
| #define PPR_TXBF_ENAB() (0) |
| #else |
| #define PPR_TXBF_ENAB() (1) |
| #endif |
| #else |
| #define PPR_TXBF_ENAB() (0) |
| #endif /* WL_BEAMFORMING */ |
| |
| typedef enum ppr_tlv_id { |
| PPR_RGRP_DSSS_ID = 1, |
| PPR_RGRP_OFDM_ID, |
| PPR_RGRP_MCS_ID |
| } ppr_tlv_id_t; |
| |
| /* This marks the start of a packed structure section. */ |
| #include <packed_section_start.h> |
| |
| #define PPR_SERIALIZATION_VER 3 |
| #define PPR_TLV_VER (PPR_SERIALIZATION_VER + 1) |
| |
| /** ppr deserialization header */ |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_deser_header { |
| uint8 version; |
| uint8 bw; |
| uint16 per_band_size; |
| uint32 flags; |
| uint16 chain3size; /* ppr data size of 3 Tx chains, needed in deserialisation process */ |
| } BWL_POST_PACKED_STRUCT ppr_deser_header_t; |
| |
| |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_ser_mem_flag { |
| uint32 magic_word; |
| uint32 flag; |
| } BWL_POST_PACKED_STRUCT ppr_ser_mem_flag_t; |
| |
| |
| #define WLC_TXPWR_DB_FACTOR 4 /* conversion for phy txpwr cacluations that use .25 dB units */ |
| |
| |
| /* QDB() macro takes a dB value and converts to a quarter dB value */ |
| #ifdef QDB |
| #undef QDB |
| #endif |
| #define QDB(n) ((n) * WLC_TXPWR_DB_FACTOR) |
| |
| #define PPR_HDR_CUR_BW_MASK 0x000000FF |
| #define PPR_HDR_ALLOC_BW_MASK 0x0000FF00 |
| #define PPR_HDR_FLAG_MASK 0xFFFF0000 |
| #define PPR_HDR_ALLOC_BW_SHIFT 8 |
| |
| /* PPR flags, start defining from left to right, in case bandwidth needs more bits */ |
| #define PPR_HDR_FLAG_PREALLOC 0x80000000 /* ppr structure is on a pre-allocaed memory */ |
| |
| /* Flag bits in serialization/deserialization */ |
| #define PPR_MAX_TX_CHAIN_MASK 0x0000007 /* mask of Tx chains */ |
| #define PPR_SER_MEM_WORD 0xBEEFC0FF /* magic word indicates serialization start */ |
| |
| |
| /* size of serialization header */ |
| #define SER_HDR_LEN sizeof(ppr_deser_header_t) |
| |
| /** Per band tx powers */ |
| typedef BWL_PRE_PACKED_STRUCT struct pprpb { |
| /* start of 20MHz tx power limits */ |
| int8 p_1x1dsss[WL_RATESET_SZ_DSSS]; /* Legacy CCK/DSSS */ |
| int8 p_1x1ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM transmission */ |
| int8 p_1x1vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 1x1mcs0 */ |
| #if (PPR_MAX_TX_CHAINS > 1) |
| int8 p_1x2dsss[WL_RATESET_SZ_DSSS]; /* Legacy CCK/DSSS */ |
| int8 p_1x2cdd_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM CDD transmission */ |
| int8 p_1x2cdd_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 1x2cdd_mcs0 */ |
| int8 p_2x2stbc_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x2stbc_mcs0 */ |
| int8 p_2x2vhtss2[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x2sdm_mcs8 */ |
| int8 p_1x2txbf_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM TXBF transmission */ |
| int8 p_1x2txbf_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 1x2txbf_mcs0 */ |
| int8 p_2x2txbf_vhtss2[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x2txbf_mcs8 */ |
| #if (PPR_MAX_TX_CHAINS > 2) |
| int8 p_1x3dsss[WL_RATESET_SZ_DSSS]; /* Legacy CCK/DSSS */ |
| int8 p_1x3cdd_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM CDD transmission */ |
| int8 p_1x3cdd_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 1x3cdd_mcs0 */ |
| int8 p_2x3stbc_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x3stbc_mcs0 */ |
| int8 p_2x3vhtss2[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x3sdm_mcs8 spexp1 */ |
| int8 p_3x3vhtss3[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 3x3sdm_mcs16 */ |
| int8 p_1x3txbf_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM TXBF transmission */ |
| int8 p_1x3txbf_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 1x3txbf_mcs0 */ |
| int8 p_2x3txbf_vhtss2[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x3txbf_mcs8 */ |
| int8 p_3x3txbf_vhtss3[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 3x3txbf_mcs16 */ |
| #if (PPR_MAX_TX_CHAINS > 3) |
| int8 p_1x4dsss[WL_RATESET_SZ_DSSS]; /* Legacy CCK/DSSS */ |
| int8 p_1x4cdd_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM CDD transmission */ |
| int8 p_1x4cdd_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 1x4cdd_mcs0 */ |
| int8 p_2x4stbc_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x4stbc_mcs0 */ |
| int8 p_2x4vhtss2[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x4sdm_mcs8 spexp1 */ |
| int8 p_3x4vhtss3[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 3x4 ht_vht */ |
| int8 p_4x4vhtss4[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 4x4 */ |
| int8 p_1x4txbf_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM TXBF transmission */ |
| int8 p_1x4txbf_vhtss1[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 1x4txbf_mcs0 */ |
| int8 p_2x4txbf_vhtss2[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 2x4txbf_mcs8 */ |
| int8 p_3x4txbf_vhtss3[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 3x4txbf_mcs16 */ |
| int8 p_4x4txbf_vhtss4[WL_RATESET_SZ_VHT_MCS_P]; /* 10HT/12VHT pwrs from 4x4txbf_mcs16 */ |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| } BWL_POST_PACKED_STRUCT pprpbw_t; |
| |
| |
| #define PPR_CHAIN1_FIRST OFFSETOF(pprpbw_t, p_1x1dsss) |
| #define PPR_CHAIN1_END (OFFSETOF(pprpbw_t, p_1x1vhtss1) + sizeof(((pprpbw_t *)0)->p_1x1vhtss1)) |
| #define PPR_CHAIN1_SIZE PPR_CHAIN1_END |
| #if (PPR_MAX_TX_CHAINS > 1) |
| #define PPR_CHAIN2_FIRST OFFSETOF(pprpbw_t, p_1x2dsss) |
| #define PPR_CHAIN2_FIRST_MCS OFFSETOF(pprpbw_t, p_1x2cdd_vhtss1) |
| #define PPR_CHAIN2_END (OFFSETOF(pprpbw_t, p_2x2txbf_vhtss2) + \ |
| sizeof(((pprpbw_t *)0)->p_2x2txbf_vhtss2)) |
| #define PPR_CHAIN2_SIZE (PPR_CHAIN2_END - PPR_CHAIN2_FIRST) |
| #define PPR_CHAIN2_MCS_END (OFFSETOF(pprpbw_t, p_2x2vhtss2) + sizeof(((pprpbw_t *)0)->p_2x2vhtss2)) |
| #define PPR_CHAIN2_MCS_SIZE (PPR_CHAIN2_MCS_END - PPR_CHAIN2_FIRST_MCS) |
| #define PPR_BF_CHAIN2_FIRST OFFSETOF(pprpbw_t, p_1x2txbf_ofdm) |
| #define PPR_BF_CHAIN2_FIRST_MCS OFFSETOF(pprpbw_t, p_1x2txbf_vhtss1) |
| #define PPR_BF_CHAIN2_END (OFFSETOF(pprpbw_t, p_2x2txbf_vhtss2) + \ |
| sizeof(((pprpbw_t *)0)->p_2x2txbf_vhtss2)) |
| #define PPR_BF_CHAIN2_SIZE (PPR_BF_CHAIN2_END - PPR_BF_CHAIN2_FIRST) |
| #define PPR_BF_CHAIN2_MCS_SIZE (PPR_BF_CHAIN2_END - PPR_BF_CHAIN2_FIRST_MCS) |
| #if (PPR_MAX_TX_CHAINS > 2) |
| #define PPR_CHAIN3_FIRST OFFSETOF(pprpbw_t, p_1x3dsss) |
| #define PPR_CHAIN3_FIRST_MCS OFFSETOF(pprpbw_t, p_1x3cdd_vhtss1) |
| #define PPR_CHAIN3_END (OFFSETOF(pprpbw_t, p_3x3txbf_vhtss3) + \ |
| sizeof(((pprpbw_t *)0)->p_3x3txbf_vhtss3)) |
| #define PPR_CHAIN3_SIZE (PPR_CHAIN3_END - PPR_CHAIN3_FIRST) |
| #define PPR_CHAIN3_MCS_END (OFFSETOF(pprpbw_t, p_3x3vhtss3) + sizeof(((pprpbw_t *)0)->p_3x3vhtss3)) |
| #define PPR_CHAIN3_MCS_SIZE (PPR_CHAIN3_MCS_END - PPR_CHAIN3_FIRST_MCS) |
| #define PPR_BF_CHAIN3_FIRST OFFSETOF(pprpbw_t, p_1x3txbf_ofdm) |
| #define PPR_BF_CHAIN3_FIRST_MCS OFFSETOF(pprpbw_t, p_1x3txbf_vhtss1) |
| #define PPR_BF_CHAIN3_END (OFFSETOF(pprpbw_t, p_3x3txbf_vhtss3) + \ |
| sizeof(((pprpbw_t *)0)->p_3x3txbf_vhtss3)) |
| #define PPR_BF_CHAIN3_SIZE (PPR_BF_CHAIN3_END - PPR_BF_CHAIN3_FIRST) |
| #define PPR_BF_CHAIN3_MCS_SIZE (PPR_BF_CHAIN3_END - PPR_BF_CHAIN3_FIRST_MCS) |
| #if (PPR_MAX_TX_CHAINS > 3) |
| #define PPR_CHAIN4_FIRST OFFSETOF(pprpbw_t, p_1x4dsss) |
| #define PPR_CHAIN4_FIRST_MCS OFFSETOF(pprpbw_t, p_1x4cdd_vhtss1) |
| #define PPR_CHAIN4_END (OFFSETOF(pprpbw_t, p_4x4txbf_vhtss4) + \ |
| sizeof(((pprpbw_t *)0)->p_4x4txbf_vhtss4)) |
| #define PPR_CHAIN4_SIZE (PPR_CHAIN4_END - PPR_CHAIN4_FIRST) |
| #define PPR_CHAIN4_MCS_END (OFFSETOF(pprpbw_t, p_4x4vhtss4) + sizeof(((pprpbw_t *)0)->p_4x4vhtss4)) |
| #define PPR_CHAIN4_MCS_SIZE (PPR_CHAIN4_MCS_END - PPR_CHAIN4_FIRST_MCS) |
| #define PPR_BF_CHAIN4_FIRST OFFSETOF(pprpbw_t, p_1x4txbf_ofdm) |
| #define PPR_BF_CHAIN4_FIRST_MCS OFFSETOF(pprpbw_t, p_1x4txbf_vhtss1) |
| #define PPR_BF_CHAIN4_END (OFFSETOF(pprpbw_t, p_4x4txbf_vhtss4) + \ |
| sizeof(((pprpbw_t *)0)->p_4x4txbf_vhtss4)) |
| #define PPR_BF_CHAIN4_SIZE (PPR_BF_CHAIN4_END - PPR_BF_CHAIN4_FIRST) |
| #define PPR_BF_CHAIN4_MCS_SIZE (PPR_BF_CHAIN4_END - PPR_BF_CHAIN4_FIRST_MCS) |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| |
| /* Maximum supported bandwidth */ |
| #if defined(WL11AC_80P80) |
| #define PPR_BW_MAX WL_TX_BW_8080 |
| #define MAX_PPR_SIZE (sizeof(ppr_bw_8080_t) + sizeof(wl_tx_bw_t)) |
| #elif defined(WL11AC_160) |
| #define PPR_BW_MAX WL_TX_BW_160 |
| #define MAX_PPR_SIZE (sizeof(ppr_bw_160_t) + sizeof(wl_tx_bw_t)) |
| #else |
| #define PPR_BW_MAX WL_TX_BW_80 |
| #define MAX_PPR_SIZE (sizeof(ppr_bw_80_t) + sizeof(wl_tx_bw_t)) |
| #endif |
| |
| /* If a bw is ULB */ |
| #define IS_ULB_BW(bw) ((bw == WL_TX_BW_2P5) || (bw == WL_TX_BW_5) || (bw == WL_TX_BW_10)) |
| |
| /** Structure to contain ppr values for a 20MHz channel */ |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_bw_20 { |
| /* 20MHz tx power limits */ |
| pprpbw_t b20; |
| } BWL_POST_PACKED_STRUCT ppr_bw_20_t; |
| |
| /** Structure to contain ppr values for a 40MHz channel */ |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_bw_40 { |
| /* 40MHz tx power limits */ |
| pprpbw_t b40; |
| /* 20in40MHz tx power limits */ |
| pprpbw_t b20in40; |
| } BWL_POST_PACKED_STRUCT ppr_bw_40_t; |
| |
| |
| /** Structure to contain ppr values for an 80MHz channel */ |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_bw_80 { |
| /* 80MHz tx power limits */ |
| pprpbw_t b80; |
| /* 20in80MHz tx power limits */ |
| pprpbw_t b20in80; |
| /* 40in80MHz tx power limits */ |
| pprpbw_t b40in80; |
| } BWL_POST_PACKED_STRUCT ppr_bw_80_t; |
| |
| /** Structure to contain ppr values for a 160MHz channel */ |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_bw_160 { |
| /* 160MHz tx power limits */ |
| pprpbw_t b160; |
| /* 20in160MHz tx power limits */ |
| pprpbw_t b20in160; |
| /* 40in160MHz tx power limits */ |
| pprpbw_t b40in160; |
| /* 80in160MHz tx power limits */ |
| pprpbw_t b80in160; |
| } BWL_POST_PACKED_STRUCT ppr_bw_160_t; |
| |
| |
| /** Structure to contain ppr values for an 80+80MHz channel */ |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_bw_8080 { |
| /* 80+80MHz chan1 tx power limits */ |
| pprpbw_t b80ch1; |
| /* 80+80MHz chan2 tx power limits */ |
| pprpbw_t b80ch2; |
| /* 80in80+80MHz (chan1 subband) tx power limits */ |
| pprpbw_t b80in8080; |
| /* 20in80+80MHz (chan1 subband) tx power limits */ |
| pprpbw_t b20in8080; |
| /* 40in80+80MHz (chan1 subband) tx power limits */ |
| pprpbw_t b40in8080; |
| } BWL_POST_PACKED_STRUCT ppr_bw_8080_t; |
| |
| /** |
| * This is the initial implementation of the structure we're hiding. It is sized to contain only |
| * the set of powers it requires, so the union is not necessarily the size of the largest member. |
| */ |
| BWL_PRE_PACKED_STRUCT struct ppr { |
| uint32 hdr; |
| |
| BWL_PRE_PACKED_STRUCT union { |
| ppr_bw_20_t ch20; |
| ppr_bw_40_t ch40; |
| ppr_bw_80_t ch80; |
| ppr_bw_160_t ch160; |
| ppr_bw_8080_t ch8080; |
| } ppr_bw; |
| } BWL_POST_PACKED_STRUCT; |
| |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_dsss_tlv { |
| uint8 bw; |
| uint8 chains; |
| uint8 pwr[]; |
| } BWL_POST_PACKED_STRUCT ppr_dsss_tlv_t; |
| |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_ofdm_tlv { |
| uint8 bw; |
| uint8 chains; |
| uint8 mode; |
| uint8 pwr[]; |
| } BWL_POST_PACKED_STRUCT ppr_ofdm_tlv_t; |
| |
| typedef BWL_PRE_PACKED_STRUCT struct ppr_mcs_tlv { |
| uint8 bw; |
| uint8 chains; |
| uint8 mode; |
| uint8 nss; |
| uint8 pwr[]; |
| } BWL_POST_PACKED_STRUCT ppr_mcs_tlv_t; |
| |
| /* This marks the end of a packed structure section. */ |
| #include <packed_section_end.h> |
| |
| static wl_tx_bw_t ppr_get_cur_bw(const ppr_t* p) |
| { |
| return ((p->hdr) & PPR_HDR_CUR_BW_MASK); |
| } |
| |
| #ifdef BCMDRIVER |
| static wl_tx_bw_t ppr_get_alloc_bw(uint32 hdr) |
| { |
| return (((hdr) & PPR_HDR_ALLOC_BW_MASK) >> PPR_HDR_ALLOC_BW_SHIFT); |
| } |
| #endif |
| |
| #if (defined(BCMDBG_ASSERT) || defined(BCMASSERT_LOG)) && defined(BCMDRIVER) |
| static bool ppr_is_valid_bw(wl_tx_bw_t bw) |
| { |
| bool ret = FALSE; |
| if ((bw == WL_TX_BW_20) || (bw == WL_TX_BW_40) || (bw == WL_TX_BW_80) || |
| (bw == WL_TX_BW_160) || (bw == WL_TX_BW_8080) || (bw == PPR_BW_MAX) || |
| IS_ULB_BW(bw)) { |
| ret = TRUE; |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| /** Returns a flag of ppr conditions (chains, txbf etc.) */ |
| static uint32 ppr_get_flag(void) |
| { |
| uint32 flag = 0; |
| flag |= PPR_MAX_TX_CHAINS & PPR_MAX_TX_CHAIN_MASK; |
| return flag; |
| } |
| |
| static uint16 ppr_ser_size_per_band(uint32 flags) |
| { |
| uint16 ret = PPR_CHAIN1_SIZE; /* at least 1 chain rates should be there */ |
| uint8 chain = flags & PPR_MAX_TX_CHAIN_MASK; |
| BCM_REFERENCE(chain); |
| #if (PPR_MAX_TX_CHAINS > 1) |
| if (chain > 1) { |
| ret += PPR_CHAIN2_SIZE; |
| } |
| #if (PPR_MAX_TX_CHAINS > 2) |
| if (chain > 2) { |
| ret += PPR_CHAIN3_SIZE; |
| } |
| #if (PPR_MAX_TX_CHAINS > 3) |
| if (chain > 3) { |
| ret += PPR_CHAIN4_SIZE; |
| } |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| return ret; |
| } |
| |
| /** Returns the number of bands for a specific bandwidth bw */ |
| static uint ppr_bands_by_bw(wl_tx_bw_t bw) |
| { |
| switch (bw) { |
| case WL_TX_BW_2P5: |
| case WL_TX_BW_5: |
| case WL_TX_BW_10: |
| case WL_TX_BW_20: |
| return sizeof(ppr_bw_20_t)/sizeof(pprpbw_t); |
| case WL_TX_BW_40: |
| return sizeof(ppr_bw_40_t)/sizeof(pprpbw_t); |
| case WL_TX_BW_80: |
| return sizeof(ppr_bw_80_t)/sizeof(pprpbw_t); |
| case WL_TX_BW_160: |
| return sizeof(ppr_bw_160_t)/sizeof(pprpbw_t); |
| case WL_TX_BW_8080: |
| return sizeof(ppr_bw_8080_t)/sizeof(pprpbw_t); |
| default: |
| ASSERT(0); |
| return 0; |
| } |
| } |
| |
| /** Return the required serialization size based on the flag field. */ |
| static uint ppr_ser_size_by_flag(uint32 flag, wl_tx_bw_t bw) |
| { |
| return ppr_ser_size_per_band(flag) * ppr_bands_by_bw(bw); |
| } |
| |
| #define COPY_PPR_TOBUF(x, y) do { bcopy(&pprbuf[x], *buf, y); \ |
| *buf += y; ret += y; } while (0); |
| |
| |
| /** Serialize ppr data of a bandwidth into the given buffer */ |
| static uint ppr_serialize_block(const uint8* pprbuf, uint8** buf, uint32 serflag) |
| { |
| uint ret = 0; |
| #if (PPR_MAX_TX_CHAINS > 1) |
| uint chain = serflag & PPR_MAX_TX_CHAIN_MASK; /* chain number in serialized block */ |
| #endif |
| |
| COPY_PPR_TOBUF(PPR_CHAIN1_FIRST, PPR_CHAIN1_SIZE); |
| #if (PPR_MAX_TX_CHAINS > 1) |
| if (chain > 1) { |
| COPY_PPR_TOBUF(PPR_CHAIN2_FIRST, PPR_CHAIN2_SIZE); |
| } |
| #if (PPR_MAX_TX_CHAINS > 2) |
| if (chain > 2) { |
| COPY_PPR_TOBUF(PPR_CHAIN3_FIRST, PPR_CHAIN3_SIZE); |
| } |
| #if (PPR_MAX_TX_CHAINS > 3) |
| if (chain > 3) { |
| COPY_PPR_TOBUF(PPR_CHAIN4_FIRST, PPR_CHAIN4_SIZE); |
| } |
| #endif /* (PPR_MAX_TX_CHAINS > 2) */ |
| #endif /* (PPR_MAX_TX_CHAINS > 2) */ |
| #endif /* (PPR_MAX_TX_CHAINS > 1) */ |
| return ret; |
| } |
| |
| |
| /** Serialize ppr data of each bandwidth into the given buffer, returns bytes copied */ |
| static uint ppr_serialize_data(const ppr_t *pprptr, uint8* buf, uint32 serflag) |
| { |
| uint i; |
| uint bands; |
| const uint8* pprbuf; |
| |
| uint ret = sizeof(ppr_deser_header_t); |
| ppr_deser_header_t* header = (ppr_deser_header_t*)buf; |
| ASSERT(pprptr && buf); |
| header->version = PPR_SERIALIZATION_VER; |
| header->bw = (uint8)ppr_get_cur_bw(pprptr); |
| header->flags = HTON32(ppr_get_flag()); |
| header->per_band_size = HTON16(ppr_ser_size_per_band(serflag)); |
| |
| buf += sizeof(*header); |
| |
| bands = ppr_bands_by_bw(header->bw); |
| pprbuf = (const uint8*)&pprptr->ppr_bw; |
| for (i = 0; i < bands; i++) { |
| ret += ppr_serialize_block(pprbuf, &buf, serflag); |
| pprbuf += sizeof(pprpbw_t); /* Jump to next band */ |
| } |
| |
| return ret; |
| } |
| |
| |
| /** Copy serialized ppr data of a bandwidth */ |
| static void |
| ppr_copy_serdata(uint8* pobuf, const uint8** inbuf, uint32 flag, uint16 per_band_size) |
| { |
| uint chain = flag & PPR_MAX_TX_CHAIN_MASK; |
| uint16 len = PPR_CHAIN1_SIZE; |
| BCM_REFERENCE(chain); |
| bcopy(*inbuf, pobuf, PPR_CHAIN1_SIZE); |
| *inbuf += PPR_CHAIN1_SIZE; |
| #if (PPR_MAX_TX_CHAINS > 1) |
| if (chain > 1) { |
| bcopy(*inbuf, &pobuf[PPR_CHAIN2_FIRST], PPR_CHAIN2_SIZE); |
| *inbuf += PPR_CHAIN2_SIZE; |
| len += PPR_CHAIN2_SIZE; |
| } |
| #if (PPR_MAX_TX_CHAINS > 2) |
| if (chain > 2) { |
| bcopy(*inbuf, &pobuf[PPR_CHAIN3_FIRST], PPR_CHAIN3_SIZE); |
| *inbuf += PPR_CHAIN3_SIZE; |
| len += PPR_CHAIN3_SIZE; |
| } |
| #if (PPR_MAX_TX_CHAINS > 3) |
| if (chain > 3) { |
| bcopy(*inbuf, &pobuf[PPR_CHAIN4_FIRST], PPR_CHAIN4_SIZE); |
| *inbuf += PPR_CHAIN4_SIZE; |
| len += PPR_CHAIN4_SIZE; |
| } |
| #endif /* (PPR_MAX_TX_CHAINS > 3) */ |
| #endif /* (PPR_MAX_TX_CHAINS > 2) */ |
| #endif /* (PPR_MAX_TX_CHAINS > 1) */ |
| if (len < per_band_size) { |
| *inbuf += (per_band_size - len); |
| } |
| } |
| |
| |
| /* Deserialize data into a ppr_t structure */ |
| static void |
| ppr_deser_cpy(ppr_t* pptr, const uint8* inbuf, uint32 flag, wl_tx_bw_t bw, uint16 per_band_size) |
| { |
| uint i; |
| uint bands; |
| uint8* pobuf; |
| |
| ppr_set_ch_bw(pptr, bw); |
| bands = ppr_bands_by_bw(bw); |
| pobuf = (uint8*)&pptr->ppr_bw; |
| for (i = 0; i < bands; i++) { |
| ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size); |
| pobuf += sizeof(pprpbw_t); /* Jump to next band */ |
| } |
| } |
| |
| |
| /** Get a pointer to the power values for a given channel bandwidth */ |
| static pprpbw_t* ppr_get_bw_powers_20(ppr_t* p, wl_tx_bw_t bw) |
| { |
| pprpbw_t* pwrs = NULL; |
| |
| if (bw == WL_TX_BW_20 || IS_ULB_BW(bw)) |
| pwrs = &p->ppr_bw.ch20.b20; |
| /* else */ |
| /* ASSERT(0); */ |
| return pwrs; |
| } |
| |
| |
| /** Get a pointer to the power values for a given channel bandwidth */ |
| static pprpbw_t* ppr_get_bw_powers_40(ppr_t* p, wl_tx_bw_t bw) |
| { |
| pprpbw_t* pwrs = NULL; |
| |
| switch (bw) { |
| case WL_TX_BW_40: |
| pwrs = &p->ppr_bw.ch40.b40; |
| break; |
| case WL_TX_BW_2P5: |
| case WL_TX_BW_5: |
| case WL_TX_BW_10: |
| case WL_TX_BW_20: |
| |
| case WL_TX_BW_20IN40: |
| pwrs = &p->ppr_bw.ch40.b20in40; |
| break; |
| default: |
| /* ASSERT(0); */ |
| break; |
| } |
| return pwrs; |
| } |
| |
| |
| /** Get a pointer to the power values for a given channel bandwidth */ |
| static pprpbw_t* ppr_get_bw_powers_80(ppr_t* p, wl_tx_bw_t bw) |
| { |
| pprpbw_t* pwrs = NULL; |
| |
| switch (bw) { |
| case WL_TX_BW_80: |
| pwrs = &p->ppr_bw.ch80.b80; |
| break; |
| case WL_TX_BW_2P5: |
| case WL_TX_BW_5: |
| case WL_TX_BW_10: |
| case WL_TX_BW_20: |
| case WL_TX_BW_20IN40: |
| case WL_TX_BW_20IN80: |
| pwrs = &p->ppr_bw.ch80.b20in80; |
| break; |
| case WL_TX_BW_40: |
| case WL_TX_BW_40IN80: |
| pwrs = &p->ppr_bw.ch80.b40in80; |
| break; |
| default: |
| /* ASSERT(0); */ |
| break; |
| } |
| return pwrs; |
| } |
| |
| /** Get a pointer to the power values for a given channel bandwidth */ |
| static pprpbw_t* ppr_get_bw_powers_160(ppr_t* p, wl_tx_bw_t bw) |
| { |
| pprpbw_t* pwrs = NULL; |
| |
| switch (bw) { |
| case WL_TX_BW_160: |
| case WL_TX_BW_8080: // stf function doesn't care much if we're 160 or 80p80 |
| pwrs = &p->ppr_bw.ch160.b160; |
| break; |
| case WL_TX_BW_2P5: |
| case WL_TX_BW_5: |
| case WL_TX_BW_10: |
| case WL_TX_BW_20: |
| case WL_TX_BW_20IN40: |
| case WL_TX_BW_20IN80: |
| case WL_TX_BW_20IN160: |
| pwrs = &p->ppr_bw.ch160.b20in160; |
| break; |
| case WL_TX_BW_40: |
| case WL_TX_BW_40IN80: |
| case WL_TX_BW_40IN160: |
| pwrs = &p->ppr_bw.ch160.b40in160; |
| break; |
| case WL_TX_BW_80: |
| case WL_TX_BW_80IN160: |
| pwrs = &p->ppr_bw.ch160.b80in160; |
| break; |
| default: |
| /* ASSERT(0); */ |
| break; |
| } |
| return pwrs; |
| } |
| |
| |
| /** Get a pointer to the power values for a given channel bandwidth */ |
| static pprpbw_t* ppr_get_bw_powers_8080(ppr_t* p, wl_tx_bw_t bw) |
| { |
| pprpbw_t* pwrs = NULL; |
| |
| switch (bw) { |
| case WL_TX_BW_160: // stf function doesn't care much if we're 160 or 80p80 |
| case WL_TX_BW_8080: |
| pwrs = &p->ppr_bw.ch8080.b80ch1; |
| break; |
| case WL_TX_BW_8080CHAN2: |
| pwrs = &p->ppr_bw.ch8080.b80ch2; |
| break; |
| case WL_TX_BW_2P5: |
| case WL_TX_BW_5: |
| case WL_TX_BW_10: |
| case WL_TX_BW_20: |
| case WL_TX_BW_20IN40: |
| case WL_TX_BW_20IN80: |
| case WL_TX_BW_20IN160: |
| case WL_TX_BW_20IN8080: |
| pwrs = &p->ppr_bw.ch8080.b20in8080; |
| break; |
| case WL_TX_BW_40: |
| case WL_TX_BW_40IN80: |
| case WL_TX_BW_40IN160: |
| case WL_TX_BW_40IN8080: |
| pwrs = &p->ppr_bw.ch8080.b40in8080; |
| break; |
| case WL_TX_BW_80: |
| case WL_TX_BW_80IN160: |
| case WL_TX_BW_80IN8080: |
| pwrs = &p->ppr_bw.ch8080.b80in8080; |
| break; |
| default: |
| /* ASSERT(0); */ |
| break; |
| } |
| return pwrs; |
| } |
| |
| typedef pprpbw_t* (*wlc_ppr_get_bw_pwrs_fn_t)(ppr_t* p, wl_tx_bw_t bw); |
| |
| typedef struct { |
| wl_tx_bw_t ch_bw; /* Bandwidth of the channel for which powers are stored */ |
| /* Function to retrieve the powers for the requested bandwidth */ |
| wlc_ppr_get_bw_pwrs_fn_t fn; |
| } wlc_ppr_get_bw_pwrs_pair_t; |
| |
| |
| static const wlc_ppr_get_bw_pwrs_pair_t ppr_get_bw_pwrs_fn[] = { |
| {WL_TX_BW_20, ppr_get_bw_powers_20}, |
| {WL_TX_BW_40, ppr_get_bw_powers_40}, |
| {WL_TX_BW_80, ppr_get_bw_powers_80}, |
| {WL_TX_BW_160, ppr_get_bw_powers_160}, |
| {WL_TX_BW_8080, ppr_get_bw_powers_8080}, |
| {WL_TX_BW_2P5, ppr_get_bw_powers_20}, |
| {WL_TX_BW_5, ppr_get_bw_powers_20}, |
| {WL_TX_BW_10, ppr_get_bw_powers_20}, |
| }; |
| |
| |
| /** Get a pointer to the power values for a given channel bandwidth */ |
| static pprpbw_t* ppr_get_bw_powers(ppr_t* p, wl_tx_bw_t bw) |
| { |
| uint32 i; |
| |
| if (p == NULL) { |
| return NULL; |
| } |
| |
| for (i = 0; i < (int)ARRAYSIZE(ppr_get_bw_pwrs_fn); i++) { |
| if (ppr_get_bw_pwrs_fn[i].ch_bw == ppr_get_cur_bw(p)) |
| return ppr_get_bw_pwrs_fn[i].fn(p, bw); |
| } |
| |
| ASSERT(0); |
| return NULL; |
| } |
| |
| |
| /** |
| * Rate group power finder functions: ppr_get_xxx_group() |
| * To preserve the opacity of the PPR struct, even inside the API we try to limit knowledge of |
| * its details. Almost all API functions work on the powers for individual rate groups, rather than |
| * directly accessing the struct. Once the section of the structure corresponding to the bandwidth |
| * has been identified using ppr_get_bw_powers(), the ppr_get_xxx_group() functions use knowledge |
| * of the number of spatial streams, the number of tx chains, and the expansion mode to return a |
| * pointer to the required group of power values. |
| */ |
| |
| /** Get a pointer to the power values for the given dsss rate group for a given channel bandwidth */ |
| static int8* ppr_get_dsss_group(pprpbw_t* bw_pwrs, wl_tx_chains_t tx_chains) |
| { |
| int8* group_pwrs = NULL; |
| |
| switch (tx_chains) { |
| #if (PPR_MAX_TX_CHAINS > 1) |
| #if (PPR_MAX_TX_CHAINS > 2) |
| #if (PPR_MAX_TX_CHAINS > 3) |
| case WL_TX_CHAINS_4: |
| group_pwrs = bw_pwrs->p_1x4dsss; |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| case WL_TX_CHAINS_3: |
| group_pwrs = bw_pwrs->p_1x3dsss; |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| case WL_TX_CHAINS_2: |
| group_pwrs = bw_pwrs->p_1x2dsss; |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| case WL_TX_CHAINS_1: |
| group_pwrs = bw_pwrs->p_1x1dsss; |
| break; |
| default: |
| ASSERT(0); |
| break; |
| } |
| return group_pwrs; |
| } |
| |
| |
| /** Get a pointer to the power values for the given ofdm rate group for a given channel bandwidth */ |
| static int8* ppr_get_ofdm_group(pprpbw_t* bw_pwrs, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains) |
| { |
| int8* group_pwrs = NULL; |
| BCM_REFERENCE(mode); |
| switch (tx_chains) { |
| #if (PPR_MAX_TX_CHAINS > 1) |
| #if (PPR_MAX_TX_CHAINS > 2) |
| #if (PPR_MAX_TX_CHAINS > 3) |
| case WL_TX_CHAINS_4: |
| if (mode == WL_TX_MODE_TXBF) { |
| group_pwrs = bw_pwrs->p_1x4txbf_ofdm; |
| } else { |
| group_pwrs = bw_pwrs->p_1x4cdd_ofdm; |
| } |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| case WL_TX_CHAINS_3: |
| if (mode == WL_TX_MODE_TXBF) |
| group_pwrs = bw_pwrs->p_1x3txbf_ofdm; |
| else |
| group_pwrs = bw_pwrs->p_1x3cdd_ofdm; |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| case WL_TX_CHAINS_2: |
| if (mode == WL_TX_MODE_TXBF) |
| group_pwrs = bw_pwrs->p_1x2txbf_ofdm; |
| else |
| group_pwrs = bw_pwrs->p_1x2cdd_ofdm; |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| case WL_TX_CHAINS_1: |
| group_pwrs = bw_pwrs->p_1x1ofdm; |
| break; |
| default: |
| ASSERT(0); |
| break; |
| } |
| return group_pwrs; |
| } |
| |
| |
| /** |
| * Tables to provide access to HT/VHT rate group powers. This avoids an ugly nested switch with |
| * messy conditional compilation. |
| * |
| * Access to a given table entry is via table[chains - Nss][mode], except for the Nss3 table, which |
| * only has one row, so it can be indexed directly by table[mode]. |
| * |
| * Separate tables are provided for each of Nss1, Nss2 and Nss3 because they are all different |
| * sizes. A combined table would be very sparse, and this arrangement also simplifies the |
| * conditional compilation. |
| * |
| * Each row represents a given number of chains, so there's no need for a zero row. Because |
| * chains >= Nss is always true, there is no one-chain row for Nss2 and there are no one- or |
| * two-chain rows for Nss3. With the tables correctly sized, we can index the rows |
| * using [chains - Nss]. |
| * |
| * Then, inside each row, we index by mode: |
| * WL_TX_MODE_NONE, WL_TX_MODE_STBC, WL_TX_MODE_CDD, WL_TX_MODE_TXBF. |
| */ |
| |
| #define OFFSNONE (-1) |
| |
| static const int mcs_groups_nss1[PPR_MAX_TX_CHAINS][WL_NUM_TX_MODES] = { |
| /* WL_TX_MODE_NONE |
| WL_TX_MODE_STBC |
| WL_TX_MODE_CDD |
| WL_TX_MODE_TXBF |
| */ |
| /* 1 chain */ |
| {OFFSETOF(pprpbw_t, p_1x1vhtss1), |
| OFFSNONE, |
| OFFSNONE, |
| OFFSNONE}, |
| #if (PPR_MAX_TX_CHAINS > 1) |
| /* 2 chain */ |
| {OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_1x2cdd_vhtss1), |
| OFFSNONE}, |
| #if (PPR_MAX_TX_CHAINS > 2) |
| /* 3 chain */ |
| {OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_1x3cdd_vhtss1), |
| OFFSNONE}, |
| #if (PPR_MAX_TX_CHAINS > 3) |
| /* 4 chain */ |
| {OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_1x4cdd_vhtss1), |
| OFFSNONE} |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| }; |
| |
| /** mcs group with TXBF data */ |
| static const int mcs_groups_nss1_txbf[PPR_MAX_TX_CHAINS][WL_NUM_TX_MODES] = { |
| /* WL_TX_MODE_NONE |
| WL_TX_MODE_STBC |
| WL_TX_MODE_CDD |
| WL_TX_MODE_TXBF |
| */ |
| /* 1 chain */ |
| {OFFSETOF(pprpbw_t, p_1x1vhtss1), |
| OFFSNONE, |
| OFFSNONE, |
| OFFSNONE}, |
| #if (PPR_MAX_TX_CHAINS > 1) |
| /* 2 chain */ |
| {OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_1x2cdd_vhtss1), |
| OFFSETOF(pprpbw_t, p_1x2txbf_vhtss1)}, |
| #if (PPR_MAX_TX_CHAINS > 2) |
| /* 3 chain */ |
| {OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_1x3cdd_vhtss1), |
| OFFSETOF(pprpbw_t, p_1x3txbf_vhtss1)}, |
| #if (PPR_MAX_TX_CHAINS > 3) |
| /* 4 chain */ |
| {OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_1x4cdd_vhtss1), |
| OFFSETOF(pprpbw_t, p_1x4txbf_vhtss1)} |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| }; |
| |
| #if (PPR_MAX_TX_CHAINS > 1) |
| static const int mcs_groups_nss2[PPR_MAX_TX_CHAINS - 1][WL_NUM_TX_MODES] = { |
| /* 2 chain */ |
| {OFFSETOF(pprpbw_t, p_2x2vhtss2), |
| OFFSETOF(pprpbw_t, p_2x2stbc_vhtss1), |
| OFFSNONE, |
| OFFSNONE}, |
| #if (PPR_MAX_TX_CHAINS > 2) |
| /* 3 chain */ |
| {OFFSETOF(pprpbw_t, p_2x3vhtss2), |
| OFFSETOF(pprpbw_t, p_2x3stbc_vhtss1), |
| OFFSNONE, |
| OFFSNONE}, |
| #if (PPR_MAX_TX_CHAINS > 3) |
| /* 4 chain */ |
| {OFFSETOF(pprpbw_t, p_2x4vhtss2), |
| OFFSETOF(pprpbw_t, p_2x4stbc_vhtss1), |
| OFFSNONE, |
| OFFSNONE} |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| }; |
| |
| /** mcs group with TXBF data */ |
| static const int mcs_groups_nss2_txbf[PPR_MAX_TX_CHAINS - 1][WL_NUM_TX_MODES] = { |
| /* 2 chain */ |
| {OFFSETOF(pprpbw_t, p_2x2vhtss2), |
| OFFSETOF(pprpbw_t, p_2x2stbc_vhtss1), |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_2x2txbf_vhtss2)}, |
| #if (PPR_MAX_TX_CHAINS > 2) |
| /* 3 chain */ |
| {OFFSETOF(pprpbw_t, p_2x3vhtss2), |
| OFFSETOF(pprpbw_t, p_2x3stbc_vhtss1), |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_2x3txbf_vhtss2)}, |
| #if (PPR_MAX_TX_CHAINS > 3) |
| /* 4 chain */ |
| {OFFSETOF(pprpbw_t, p_2x4vhtss2), |
| OFFSETOF(pprpbw_t, p_2x4stbc_vhtss1), |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_2x4txbf_vhtss2)} |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| }; |
| #if (PPR_MAX_TX_CHAINS > 2) |
| static const int mcs_groups_nss3[PPR_MAX_TX_CHAINS - 2][WL_NUM_TX_MODES] = { |
| /* 3 chains */ |
| {OFFSETOF(pprpbw_t, p_3x3vhtss3), |
| OFFSNONE, |
| OFFSNONE, |
| OFFSNONE}, |
| #if (PPR_MAX_TX_CHAINS > 3) |
| {OFFSETOF(pprpbw_t, p_3x4vhtss3), |
| OFFSNONE, |
| OFFSNONE, |
| OFFSNONE} |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| }; |
| |
| /** mcs group with TXBF data */ |
| static const int mcs_groups_nss3_txbf[PPR_MAX_TX_CHAINS - 2][WL_NUM_TX_MODES] = { |
| /* 3 chains */ |
| {OFFSETOF(pprpbw_t, p_3x3vhtss3), |
| OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_3x3txbf_vhtss3)}, |
| #if (PPR_MAX_TX_CHAINS > 3) |
| {OFFSETOF(pprpbw_t, p_3x4vhtss3), |
| OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_3x4txbf_vhtss3)} |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| }; |
| #if (PPR_MAX_TX_CHAINS > 3) |
| static const int mcs_groups_nss4[WL_NUM_TX_MODES] = { |
| /* 4 chains only */ |
| OFFSETOF(pprpbw_t, p_4x4vhtss4), |
| OFFSNONE, |
| OFFSNONE, |
| OFFSNONE, |
| }; |
| /** mcs group with TXBF data */ |
| static const int mcs_groups_nss4_txbf[WL_NUM_TX_MODES] = { |
| /* 4 chains only */ |
| OFFSETOF(pprpbw_t, p_4x4vhtss4), |
| OFFSNONE, |
| OFFSNONE, |
| OFFSETOF(pprpbw_t, p_4x4txbf_vhtss4) |
| }; |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| |
| /** Get a pointer to the power values for the given rate group for a given channel bandwidth */ |
| static int8* ppr_get_mcs_group(pprpbw_t* bw_pwrs, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains) |
| { |
| int8* group_pwrs = NULL; |
| int offset; |
| |
| switch (Nss) { |
| #if (PPR_MAX_TX_CHAINS > 1) |
| #if (PPR_MAX_TX_CHAINS > 2) |
| #if (PPR_MAX_TX_CHAINS > 3) |
| case WL_TX_NSS_4: |
| if (tx_chains == WL_TX_CHAINS_4) { |
| if (mode == WL_TX_MODE_TXBF && PPR_TXBF_ENAB()) { |
| offset = mcs_groups_nss4_txbf[mode]; |
| } else { |
| offset = mcs_groups_nss4[mode]; |
| } |
| if (offset != OFFSNONE) { |
| group_pwrs = (int8*)bw_pwrs + offset; |
| } |
| } |
| else |
| ASSERT(0); |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 3 */ |
| case WL_TX_NSS_3: |
| if ((tx_chains >= WL_TX_CHAINS_3) && (tx_chains <= PPR_MAX_TX_CHAINS)) { |
| if (PPR_TXBF_ENAB()) { |
| offset = mcs_groups_nss3_txbf[tx_chains - Nss][mode]; |
| } else { |
| offset = mcs_groups_nss3[tx_chains - Nss][mode]; |
| } |
| if (offset != OFFSNONE) { |
| group_pwrs = (int8*)bw_pwrs + offset; |
| } |
| } |
| else |
| ASSERT(0); |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| case WL_TX_NSS_2: |
| if ((tx_chains >= WL_TX_CHAINS_2) && (tx_chains <= PPR_MAX_TX_CHAINS)) { |
| if (PPR_TXBF_ENAB()) { |
| offset = mcs_groups_nss2_txbf[tx_chains - Nss][mode]; |
| } else { |
| offset = mcs_groups_nss2[tx_chains - Nss][mode]; |
| } |
| if (offset != OFFSNONE) { |
| group_pwrs = (int8*)bw_pwrs + offset; |
| } |
| } |
| else |
| ASSERT(0); |
| break; |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| case WL_TX_NSS_1: |
| if (tx_chains <= PPR_MAX_TX_CHAINS) { |
| if (PPR_TXBF_ENAB()) { |
| offset = mcs_groups_nss1_txbf[tx_chains - Nss][mode]; |
| } else { |
| offset = mcs_groups_nss1[tx_chains - Nss][mode]; |
| } |
| if (offset != OFFSNONE) { |
| group_pwrs = (int8*)bw_pwrs + offset; |
| } |
| } |
| else |
| ASSERT(0); |
| break; |
| default: |
| #ifdef BCMQT |
| printf("%s: %d: WL_TX_CHAINS_4 not supported yet, ignoring for now!!\n", |
| __FUNCTION__, __LINE__); |
| #else |
| ASSERT(0); |
| #endif |
| break; |
| } |
| return group_pwrs; |
| } |
| |
| /** Size routine for user alloc/dealloc */ |
| static uint32 ppr_pwrs_size(uint32 hdr) |
| { |
| uint32 size; |
| |
| switch ((hdr & PPR_HDR_CUR_BW_MASK)) { |
| case WL_TX_BW_2P5: |
| case WL_TX_BW_5: |
| case WL_TX_BW_10: |
| case WL_TX_BW_20: |
| size = sizeof(ppr_bw_20_t); |
| break; |
| case WL_TX_BW_40: |
| size = sizeof(ppr_bw_40_t); |
| break; |
| case WL_TX_BW_80: |
| size = sizeof(ppr_bw_80_t); |
| break; |
| #ifdef WL11AC_160 |
| case WL_TX_BW_160: |
| size = sizeof(ppr_bw_160_t); |
| break; |
| #endif |
| #ifdef WL11AC_80P80 |
| case WL_TX_BW_8080: |
| size = sizeof(ppr_bw_8080_t); |
| break; |
| #endif |
| default: |
| ASSERT(0); |
| size = 0; |
| break; |
| } |
| return size; |
| } |
| |
| |
| /** Initialization routine */ |
| void ppr_init(ppr_t* pprptr, wl_tx_bw_t bw) |
| { |
| memset(pprptr, (int8)WL_RATE_DISABLED, ppr_size(bw)); |
| pprptr->hdr = 0; |
| pprptr->hdr |= (bw & PPR_HDR_CUR_BW_MASK); |
| pprptr->hdr |= ((bw << PPR_HDR_ALLOC_BW_SHIFT) & PPR_HDR_ALLOC_BW_MASK); |
| } |
| |
| |
| /** Reinitialization routine for opaque PPR struct */ |
| void ppr_clear(ppr_t* pprptr) |
| { |
| memset((uchar*)&pprptr->ppr_bw, (int8)WL_RATE_DISABLED, |
| ppr_pwrs_size(pprptr->hdr)); |
| } |
| |
| |
| /** Size routine for user alloc/dealloc */ |
| uint32 ppr_size(wl_tx_bw_t bw) |
| { |
| uint32 ret = ppr_pwrs_size(bw) + sizeof(wl_tx_bw_t); |
| ASSERT(ret <= MAX_PPR_SIZE); |
| return ret; |
| } |
| |
| |
| /** Size routine for user serialization alloc */ |
| uint32 ppr_ser_size(const ppr_t* pprptr) |
| { |
| return ppr_pwrs_size(pprptr->hdr) + SER_HDR_LEN; /* struct size plus headers */ |
| } |
| |
| |
| /** Size routine for user serialization alloc */ |
| uint32 ppr_ser_size_by_bw(wl_tx_bw_t bw) |
| { |
| return ppr_pwrs_size(bw) + SER_HDR_LEN; |
| } |
| |
| |
| /** Constructor routine for opaque PPR struct */ |
| ppr_t* ppr_create(osl_t *osh, wl_tx_bw_t bw) |
| { |
| ppr_t* pprptr; |
| |
| ASSERT(ppr_is_valid_bw(bw)); |
| #ifndef BCMDRIVER |
| BCM_REFERENCE(osh); |
| if ((pprptr = (ppr_t*)malloc((uint)ppr_size(bw))) != NULL) { |
| #else |
| if ((pprptr = (ppr_t*)MALLOC_NOPERSIST(osh, (uint)ppr_size(bw))) != NULL) { |
| #endif |
| ppr_init(pprptr, bw); |
| } else { |
| PPR_ERROR(("%s: MALLOC(%d) failed\n", __FUNCTION__, (int)ppr_size(bw))); |
| } |
| return pprptr; |
| } |
| |
| |
| /* Constructor routine for opaque PPR struct on pre-alloc memory */ |
| ppr_t* ppr_create_prealloc(wl_tx_bw_t bw, int8 *buf, uint len) |
| { |
| ppr_t* pprptr = NULL; |
| ASSERT(ppr_is_valid_bw(bw)); |
| |
| if (ppr_size(bw) <= len) { |
| pprptr = (ppr_t*) buf; |
| ppr_init(pprptr, bw); |
| pprptr->hdr |= PPR_HDR_FLAG_PREALLOC; |
| } else { |
| PPR_ERROR(("%s: Insufficient mem(%d), need %d\n", __FUNCTION__, len, |
| (int)ppr_size(bw))); |
| ASSERT(0); |
| } |
| return pprptr; |
| } |
| |
| /** |
| * Init flags in the memory block for serialization, the serializer will check |
| * the flag to decide which ppr to be copied |
| */ |
| int ppr_init_ser_mem_by_bw(uint8* pbuf, wl_tx_bw_t bw, uint32 len) |
| { |
| ppr_ser_mem_flag_t *pmflag; |
| |
| if (pbuf == NULL || ppr_ser_size_by_bw(bw) > len) |
| return BCME_BADARG; |
| |
| pmflag = (ppr_ser_mem_flag_t *)pbuf; |
| pmflag->magic_word = HTON32(PPR_SER_MEM_WORD); |
| pmflag->flag = HTON32(ppr_get_flag()); |
| |
| /* init the memory */ |
| memset(pbuf + sizeof(*pmflag), (uint8)WL_RATE_DISABLED, len-sizeof(*pmflag)); |
| return BCME_OK; |
| } |
| |
| |
| int ppr_init_ser_mem(uint8* pbuf, ppr_t * ppr, uint32 len) |
| { |
| return ppr_init_ser_mem_by_bw(pbuf, ppr->hdr, len); |
| } |
| |
| /** Destructor routine for opaque PPR struct */ |
| void ppr_delete(osl_t *osh, ppr_t* pprptr) |
| { |
| ASSERT(ppr_is_valid_bw(ppr_get_cur_bw(pprptr))); |
| |
| if (pprptr->hdr & PPR_HDR_FLAG_PREALLOC) |
| return; |
| |
| #ifndef BCMDRIVER |
| BCM_REFERENCE(osh); |
| free(pprptr); |
| #else |
| MFREE(osh, pprptr, (uint)ppr_size(ppr_get_alloc_bw(pprptr->hdr))); |
| #endif |
| } |
| |
| /* Update the bw for the given opaque PPR struct |
| * This function is used when an opaque PPR struct has been allocated |
| * with enough space for the given bandwidth. |
| * USE WITH CAUTION |
| */ |
| void ppr_set_ch_bw(ppr_t* pprptr, wl_tx_bw_t bw) |
| { |
| ASSERT(ppr_size(ppr_get_alloc_bw(pprptr->hdr)) >= ppr_size(bw)); |
| pprptr->hdr &= ~PPR_HDR_CUR_BW_MASK; |
| pprptr->hdr |= (bw & PPR_HDR_CUR_BW_MASK); |
| } |
| |
| /** Type routine for inferring opaque structure size */ |
| wl_tx_bw_t ppr_get_ch_bw(const ppr_t* pprptr) |
| { |
| return ppr_get_cur_bw(pprptr); |
| } |
| |
| /** Type routine to get ppr supported maximum bw */ |
| wl_tx_bw_t ppr_get_max_bw(void) |
| { |
| return PPR_BW_MAX; |
| } |
| |
| |
| /** Get the dsss values for the given number of tx_chains and 20, 20in40, etc. */ |
| int ppr_get_dsss(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_chains_t tx_chains, |
| ppr_dsss_rateset_t* dsss) |
| { |
| pprpbw_t* bw_pwrs; |
| const int8* powers; |
| int cnt = 0; |
| |
| ASSERT(pprptr); |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_dsss_group(bw_pwrs, tx_chains); |
| if (powers != NULL) { |
| bcopy(powers, dsss->pwr, sizeof(*dsss)); |
| cnt = sizeof(*dsss); |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d TX_Chain:%d rateset:%p\n", |
| __FUNCTION__, pprptr, bw, tx_chains, dsss)); |
| memset(dsss->pwr, (int8)WL_RATE_DISABLED, sizeof(*dsss)); |
| } |
| return cnt; |
| } |
| |
| |
| /** Get the ofdm values for the given number of tx_chains and 20, 20in40, etc. */ |
| int ppr_get_ofdm(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_mode_t mode, wl_tx_chains_t tx_chains, |
| ppr_ofdm_rateset_t* ofdm) |
| { |
| pprpbw_t* bw_pwrs; |
| const int8* powers; |
| int cnt = 0; |
| |
| ASSERT(pprptr); |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_ofdm_group(bw_pwrs, mode, tx_chains); |
| if (powers != NULL) { |
| bcopy(powers, ofdm->pwr, sizeof(*ofdm)); |
| cnt = sizeof(*ofdm); |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d mode:%d TX_Chain:%d rateset:%p\n", |
| __FUNCTION__, pprptr, bw, mode, tx_chains, ofdm)); |
| memset(ofdm->pwr, (int8)WL_RATE_DISABLED, sizeof(*ofdm)); |
| } |
| return cnt; |
| } |
| |
| |
| /** |
| * Get the HT MCS values for the group specified by Nss, with the given bw and tx chains. Function |
| * is not called by ACPHY code, but even in case of ACPHY, function is called by wlc_channel.c. |
| */ |
| int ppr_get_ht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains, ppr_ht_mcs_rateset_t* mcs) |
| { |
| pprpbw_t* bw_pwrs; |
| const int8* powers; |
| int cnt = 0; |
| |
| ASSERT(pprptr); |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); |
| if (powers != NULL) { |
| bcopy(powers, mcs->pwr, sizeof(*mcs)); |
| cnt = sizeof(*mcs); |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d Nss:%d mode:%d TX_Chain:%d rateset:%p\n", |
| __FUNCTION__, pprptr, bw, Nss, mode, tx_chains, mcs)); |
| memset(mcs->pwr, (int8)WL_RATE_DISABLED, sizeof(*mcs)); |
| } |
| |
| return cnt; |
| } |
| |
| |
| /** Get the VHT MCS values for the group specified by Nss, with the given bw and tx chains */ |
| int ppr_get_vht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains, ppr_vht_mcs_rateset_t* mcs) |
| { |
| pprpbw_t* bw_pwrs; |
| const int8* powers; |
| int cnt = 0; |
| |
| ASSERT(pprptr); |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); |
| if (powers != NULL) { |
| bcopy(powers, mcs->pwr, sizeof(*mcs)); |
| cnt = sizeof(*mcs); |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d Nss:%d mode:%d TX_Chain:%d rateset:%p\n", |
| __FUNCTION__, pprptr, bw, Nss, mode, tx_chains, mcs)); |
| memset(mcs->pwr, (int8)WL_RATE_DISABLED, sizeof(*mcs)); |
| } |
| return cnt; |
| } |
| |
| |
| #define TXPPR_TXPWR_MAX 0x7f /* WLC_TXPWR_MAX */ |
| |
| /** |
| * Get the minimum power for a VHT MCS rate specified by Nss, with the given bw and tx chains. |
| * Disabled rates are ignored |
| */ |
| int ppr_get_vht_mcs_min(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains, int8* mcs_min) |
| { |
| pprpbw_t* bw_pwrs; |
| const int8* powers; |
| int result = BCME_ERROR; |
| uint i = 0; |
| |
| *mcs_min = TXPPR_TXPWR_MAX; |
| |
| ASSERT(pprptr); |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); |
| if (powers != NULL) { |
| for (i = 0; i < sizeof(ppr_vht_mcs_rateset_t); i++) { |
| /* ignore disabled rates! */ |
| if (powers[i] != WL_RATE_DISABLED) |
| *mcs_min = MIN(*mcs_min, powers[i]); |
| } |
| result = BCME_OK; |
| } |
| } |
| return result; |
| } |
| |
| |
| /* Routines to set target powers per rate in a group */ |
| |
| /** Set the dsss values for the given number of tx_chains and 20, 20in40, etc. */ |
| int ppr_set_dsss(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_chains_t tx_chains, |
| const ppr_dsss_rateset_t* dsss) |
| { |
| pprpbw_t* bw_pwrs; |
| int8* powers; |
| int cnt = 0; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = (int8*)ppr_get_dsss_group(bw_pwrs, tx_chains); |
| if (powers != NULL) { |
| bcopy(dsss->pwr, powers, sizeof(*dsss)); |
| cnt = sizeof(*dsss); |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d TX_Chain:%d rateset:%p\n", |
| __FUNCTION__, pprptr, bw, tx_chains, dsss)); |
| } |
| return cnt; |
| } |
| |
| |
| /** Set the ofdm values for the given number of tx_chains and 20, 20in40, etc. */ |
| int ppr_set_ofdm(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_mode_t mode, wl_tx_chains_t tx_chains, |
| const ppr_ofdm_rateset_t* ofdm) |
| { |
| pprpbw_t* bw_pwrs; |
| int8* powers; |
| int cnt = 0; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = (int8*)ppr_get_ofdm_group(bw_pwrs, mode, tx_chains); |
| if (powers != NULL) { |
| bcopy(ofdm->pwr, powers, sizeof(*ofdm)); |
| cnt = sizeof(*ofdm); |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d mode:%d TX_Chain:%d rateset:%p\n", |
| __FUNCTION__, pprptr, bw, mode, tx_chains, ofdm)); |
| } |
| return cnt; |
| } |
| |
| /** |
| * Set the HT MCS values for the group specified by Nss, with the given bw and tx chains. Function |
| * is not called by ACPHY code, but even in case of ACPHY, function is called by wlc_channel.c. |
| */ |
| int ppr_set_ht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains, const ppr_ht_mcs_rateset_t* mcs) |
| { |
| pprpbw_t* bw_pwrs; |
| int8* powers; |
| int cnt = 0; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = (int8*)ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); |
| if (powers != NULL) { |
| bcopy(mcs->pwr, powers, sizeof(*mcs)); |
| cnt = sizeof(*mcs); |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d Nss:%d mode:%d TX_Chain:%d rateset:%p\n", |
| __FUNCTION__, pprptr, bw, Nss, mode, tx_chains, mcs)); |
| } |
| return cnt; |
| } |
| |
| |
| /** Set the VHT MCS values for the group specified by Nss, with the given bw and tx chains */ |
| int ppr_set_vht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains, const ppr_vht_mcs_rateset_t* mcs) |
| { |
| pprpbw_t* bw_pwrs; |
| int8* powers; |
| int cnt = 0; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| powers = (int8*)ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); |
| if (powers != NULL) { |
| bcopy(mcs->pwr, powers, sizeof(*mcs)); |
| cnt = sizeof(*mcs); |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d Nss:%d mode:%d TX_Chain:%d rateset:%p\n", |
| __FUNCTION__, pprptr, bw, Nss, mode, tx_chains, mcs)); |
| } |
| return cnt; |
| } |
| |
| |
| /* Routines to set rate groups to a single target value */ |
| |
| /** Set the dsss values for the given number of tx_chains and 20, 20in40, etc. */ |
| int ppr_set_same_dsss(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_chains_t tx_chains, const int8 power) |
| { |
| pprpbw_t* bw_pwrs; |
| int8* dest_group; |
| int cnt = 0; |
| int i; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| dest_group = (int8*)ppr_get_dsss_group(bw_pwrs, tx_chains); |
| if (dest_group != NULL) { |
| cnt = sizeof(ppr_dsss_rateset_t); |
| for (i = 0; i < cnt; i++) |
| *dest_group++ = power; |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d TX_Chain:%d power:%d\n", |
| __FUNCTION__, pprptr, bw, tx_chains, power)); |
| } |
| return cnt; |
| } |
| |
| |
| /** Set the ofdm values for the given number of tx_chains and 20, 20in40, etc. */ |
| int ppr_set_same_ofdm(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_mode_t mode, wl_tx_chains_t tx_chains, |
| const int8 power) |
| { |
| pprpbw_t* bw_pwrs; |
| int8* dest_group; |
| int cnt = 0; |
| int i; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| dest_group = (int8*)ppr_get_ofdm_group(bw_pwrs, mode, tx_chains); |
| if (dest_group != NULL) { |
| cnt = sizeof(ppr_ofdm_rateset_t); |
| for (i = 0; i < cnt; i++) |
| *dest_group++ = power; |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d mode:%d TX_Chain:%d power:%d\n", |
| __FUNCTION__, pprptr, bw, mode, tx_chains, power)); |
| } |
| return cnt; |
| } |
| |
| |
| /** Set the HT MCS values for the group specified by Nss, with the given bw and tx chains */ |
| int ppr_set_same_ht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains, const int8 power) |
| { |
| pprpbw_t* bw_pwrs; |
| int8* dest_group; |
| int cnt = 0; |
| int i; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| dest_group = (int8*)ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); |
| if (dest_group != NULL) { |
| cnt = sizeof(ppr_ht_mcs_rateset_t); |
| for (i = 0; i < cnt; i++) |
| *dest_group++ = power; |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d Nss:%d mode:%d TX_Chain:%d power:%d\n", |
| __FUNCTION__, pprptr, bw, Nss, mode, tx_chains, power)); |
| } |
| return cnt; |
| } |
| |
| |
| /** Set the HT MCS values for the group specified by Nss, with the given bw and tx chains */ |
| int ppr_set_same_vht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains, const int8 power) |
| { |
| pprpbw_t* bw_pwrs; |
| int8* dest_group; |
| int cnt = 0; |
| int i; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| dest_group = (int8*)ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); |
| if (dest_group != NULL) { |
| cnt = sizeof(ppr_vht_mcs_rateset_t); |
| for (i = 0; i < cnt; i++) |
| *dest_group++ = power; |
| } |
| } |
| if (cnt == 0) { |
| PPR_ERROR(("%s: Failed ppr_t:%p BW:%d Nss:%d mode:%d TX_Chain:%d power:%d\n", |
| __FUNCTION__, pprptr, bw, Nss, mode, tx_chains, power)); |
| } |
| return cnt; |
| } |
| |
| |
| /* Helper routines to operate on the entire ppr set */ |
| |
| /** Ensure no rate limit is greater than the cap */ |
| uint ppr_apply_max(ppr_t* pprptr, int8 maxval) |
| { |
| uint i; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| |
| for (i = 0; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++) { |
| *rptr = MIN(*rptr, maxval); |
| } |
| return i; |
| } |
| |
| /** number of non proprietary 802.11n single stream MCS'es excluding MCS32 */ |
| #define N_HT_SS_MCS 8 |
| |
| /** |
| * Before the OLPC calibration (needs to send out some training frames to converge TX Power control) |
| * rates need to be disabled that cannot be controlled to with the accuracy needed to avoid |
| * regulatory violations. Once the calibration completes a newer reduced threshold is set to enable |
| * all valid rates. |
| * |
| * Check for any power in the rate group at or below the threshold. If any is found, set the entire |
| * group to WL_RATE_DISABLED. An exception is made for HT 87 and 88 and VHT 8 and 9 which will not |
| * cause the entire group to be disabled if they are disabled or below the threshold. |
| */ |
| static void ppr_force_disabled_group(int8* powers, int8 threshold, uint len) |
| { |
| uint i; |
| |
| for (i = 0; (i < len) && (i < N_HT_SS_MCS); i++) { |
| /* if we find a below-threshold rate in the set... */ |
| if (powers[i] < threshold) { |
| /* disable the entire rate set and return */ |
| for (i = 0; i < len; i++) { |
| powers[i] = WL_RATE_DISABLED; |
| } |
| return; |
| } |
| } |
| /* VHT 8 and 11 can be disabled separately */ |
| for (; i < len; i++) { |
| if (powers[i] < threshold) { |
| powers[i] = WL_RATE_DISABLED; |
| } |
| } |
| } |
| |
| |
| /** |
| * Before the OLPC calibration (needs to send out some training frames to converge TX Power control) |
| * rates need to be disabled that cannot be controlled to with the accuracy needed to avoid |
| * regulatory violations. Once the calibration completes a newer reduced threshold is set to enable |
| * all valid rates. |
| * |
| * Make low power rates (below the threshold) explicitly disabled. If one rate in a group is |
| * disabled, disable the whole group. |
| */ |
| int ppr_force_disabled(ppr_t* pprptr, int8 threshold) |
| { |
| wl_tx_bw_t bw; |
| |
| for (bw = WL_TX_BW_20; bw <= WL_TX_BW_80; bw++) { |
| pprpbw_t* bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| |
| if (bw_pwrs != NULL) { |
| int8* powers; |
| #if (PPR_MAX_TX_CHAINS > 1) |
| int8* mcs_powers; |
| #endif |
| powers = (int8*)ppr_get_dsss_group(bw_pwrs, WL_TX_CHAINS_1); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_DSSS); |
| |
| powers = (int8*)ppr_get_ofdm_group(bw_pwrs, |
| WL_TX_MODE_NONE, WL_TX_CHAINS_1); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_OFDM); |
| |
| powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, WL_TX_MODE_NONE, |
| WL_TX_CHAINS_1); |
| ASSERT(powers); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_VHT_MCS_P); |
| |
| #if (PPR_MAX_TX_CHAINS > 1) |
| powers = (int8*)ppr_get_dsss_group(bw_pwrs, WL_TX_CHAINS_2); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_DSSS); |
| |
| powers = (int8*)ppr_get_ofdm_group(bw_pwrs, WL_TX_MODE_CDD, WL_TX_CHAINS_2); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_OFDM); |
| |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, WL_TX_MODE_CDD, |
| WL_TX_CHAINS_2); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; powers < &mcs_powers[PPR_CHAIN2_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS_P) { |
| ppr_force_disabled_group(powers, threshold, |
| WL_RATESET_SZ_VHT_MCS_P); |
| } |
| |
| #ifdef WL_BEAMFORMING |
| if (PPR_TXBF_ENAB()) { |
| powers = (int8*)ppr_get_ofdm_group(bw_pwrs, WL_TX_MODE_TXBF, |
| WL_TX_CHAINS_2); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_OFDM); |
| |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, |
| WL_TX_MODE_TXBF, WL_TX_CHAINS_2); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; |
| powers < &mcs_powers[PPR_BF_CHAIN2_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS_P) { |
| ppr_force_disabled_group(powers, threshold, |
| WL_RATESET_SZ_VHT_MCS_P); |
| } |
| } |
| #endif /* WL_BEAMFORMING */ |
| |
| #if (PPR_MAX_TX_CHAINS > 2) |
| powers = (int8*)ppr_get_dsss_group(bw_pwrs, WL_TX_CHAINS_3); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_DSSS); |
| |
| powers = (int8*)ppr_get_ofdm_group(bw_pwrs, WL_TX_MODE_CDD, WL_TX_CHAINS_3); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_OFDM); |
| |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, WL_TX_MODE_CDD, |
| WL_TX_CHAINS_3); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; powers < &mcs_powers[PPR_CHAIN3_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS_P) { |
| ppr_force_disabled_group(powers, threshold, |
| WL_RATESET_SZ_VHT_MCS_P); |
| } |
| |
| #ifdef WL_BEAMFORMING |
| if (PPR_TXBF_ENAB()) { |
| powers = (int8*)ppr_get_ofdm_group(bw_pwrs, WL_TX_MODE_TXBF, |
| WL_TX_CHAINS_3); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_OFDM); |
| |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, |
| WL_TX_MODE_TXBF, WL_TX_CHAINS_3); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; |
| powers < &mcs_powers[PPR_BF_CHAIN3_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS_P) { |
| ppr_force_disabled_group(powers, threshold, |
| WL_RATESET_SZ_VHT_MCS_P); |
| } |
| } |
| #endif /* WL_BEAMFORMING */ |
| #if (PPR_MAX_TX_CHAINS > 3) |
| powers = (int8*)ppr_get_dsss_group(bw_pwrs, WL_TX_CHAINS_4); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_DSSS); |
| |
| powers = (int8*)ppr_get_ofdm_group(bw_pwrs, WL_TX_MODE_CDD, WL_TX_CHAINS_4); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_OFDM); |
| |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, WL_TX_MODE_CDD, |
| WL_TX_CHAINS_4); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; powers < &mcs_powers[PPR_CHAIN4_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS_P) { |
| ppr_force_disabled_group(powers, threshold, |
| WL_RATESET_SZ_VHT_MCS_P); |
| } |
| |
| #ifdef WL_BEAMFORMING |
| if (PPR_TXBF_ENAB()) { |
| powers = (int8*)ppr_get_ofdm_group(bw_pwrs, WL_TX_MODE_TXBF, |
| WL_TX_CHAINS_4); |
| ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_OFDM); |
| |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, |
| WL_TX_MODE_TXBF, WL_TX_CHAINS_4); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; |
| powers < &mcs_powers[PPR_BF_CHAIN4_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS_P) { |
| ppr_force_disabled_group(powers, threshold, |
| WL_RATESET_SZ_VHT_MCS_P); |
| } |
| } |
| #endif /* WL_BEAMFORMING */ |
| #endif /* (PPR_MAX_TX_CHAINS > 3) */ |
| #endif /* (PPR_MAX_TX_CHAINS > 2) */ |
| #endif /* (PPR_MAX_TX_CHAINS > 1) */ |
| } |
| } |
| return BCME_OK; |
| } |
| |
| |
| #if (PPR_MAX_TX_CHAINS > 1) |
| /** |
| * LCN20 PHY doesn't support VHT rates, but it seems some of its regulatory data does. We need |
| * to explicitly disable the VHT rates so there's no temptation to use them. |
| */ |
| static void ppr_force_disabled_vht_in_group(int8* powers, uint len) |
| { |
| uint i; |
| |
| for (i = N_HT_SS_MCS; (i < len); i++) { |
| powers[i] = WL_RATE_DISABLED; |
| } |
| } |
| #endif /* (PPR_MAX_TX_CHAINS > 1) */ |
| |
| |
| /** |
| * LCN20 PHY doesn't support VHT MIMO rates, but it seems some of its regulatory data does. We need |
| * to explicitly disable the VHT MIMO rates so there's no temptation to use them. |
| */ |
| int |
| ppr_disable_vht_mimo_rates(ppr_t* pprptr) |
| { |
| #if (PPR_MAX_TX_CHAINS > 1) |
| wl_tx_bw_t bw; |
| |
| for (bw = WL_TX_BW_20; bw <= WL_TX_BW_80; bw++) { |
| pprpbw_t* bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| |
| if (bw_pwrs != NULL) { |
| int8* mcs_powers; |
| int8* powers; |
| |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, WL_TX_MODE_CDD, |
| WL_TX_CHAINS_2); |
| ASSERT(mcs_powers); |
| /* skip CDD */ |
| /* mcs_powers += WL_RATESET_SZ_VHT_MCS; */ |
| for (powers = mcs_powers; powers < &mcs_powers[PPR_CHAIN2_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS) { |
| ppr_force_disabled_vht_in_group(powers, WL_RATESET_SZ_VHT_MCS); |
| } |
| |
| #ifdef WL_BEAMFORMING |
| if (PPR_TXBF_ENAB()) { |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, |
| WL_TX_MODE_TXBF, WL_TX_CHAINS_2); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; |
| powers < &mcs_powers[PPR_BF_CHAIN2_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS) { |
| ppr_force_disabled_vht_in_group(powers, |
| WL_RATESET_SZ_VHT_MCS); |
| } |
| } |
| #endif /* WL_BEAMFORMING */ |
| |
| #if (PPR_MAX_TX_CHAINS > 2) |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, WL_TX_MODE_CDD, |
| WL_TX_CHAINS_3); |
| ASSERT(mcs_powers); |
| /* skip CDD */ |
| /* mcs_powers += WL_RATESET_SZ_VHT_MCS; */ |
| for (powers = mcs_powers; powers < &mcs_powers[PPR_CHAIN3_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS) { |
| ppr_force_disabled_vht_in_group(powers, WL_RATESET_SZ_VHT_MCS); |
| } |
| |
| #ifdef WL_BEAMFORMING |
| if (PPR_TXBF_ENAB()) { |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, |
| WL_TX_MODE_TXBF, WL_TX_CHAINS_3); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; |
| powers < &mcs_powers[PPR_BF_CHAIN3_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS) { |
| ppr_force_disabled_vht_in_group(powers, |
| WL_RATESET_SZ_VHT_MCS); |
| } |
| } |
| #endif /* WL_BEAMFORMING */ |
| |
| #if (PPR_MAX_TX_CHAINS > 3) |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, WL_TX_MODE_CDD, |
| WL_TX_CHAINS_4); |
| ASSERT(mcs_powers); |
| /* skip CDD */ |
| /* mcs_powers += WL_RATESET_SZ_VHT_MCS; */ |
| for (powers = mcs_powers; powers < &mcs_powers[PPR_CHAIN4_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS) { |
| ppr_force_disabled_vht_in_group(powers, WL_RATESET_SZ_VHT_MCS); |
| } |
| |
| #ifdef WL_BEAMFORMING |
| if (PPR_TXBF_ENAB()) { |
| mcs_powers = (int8*)ppr_get_mcs_group(bw_pwrs, WL_TX_NSS_1, |
| WL_TX_MODE_TXBF, WL_TX_CHAINS_4); |
| ASSERT(mcs_powers); |
| for (powers = mcs_powers; |
| powers < &mcs_powers[PPR_BF_CHAIN4_MCS_SIZE]; |
| powers += WL_RATESET_SZ_VHT_MCS) { |
| ppr_force_disabled_vht_in_group(powers, |
| WL_RATESET_SZ_VHT_MCS); |
| } |
| } |
| #endif /* WL_BEAMFORMING */ |
| #endif /* (PPR_MAX_TX_CHAINS > 3) */ |
| #endif /* (PPR_MAX_TX_CHAINS > 2) */ |
| } |
| } |
| #endif /* (PPR_MAX_TX_CHAINS > 1) */ |
| return BCME_OK; |
| } |
| |
| |
| #if (PPR_MAX_TX_CHAINS > 1) |
| #define APPLY_CONSTRAINT(x, y, max) do { \ |
| ret += (y - x); \ |
| for (i = x; i < y; i++) \ |
| pprbuf[i] = MIN(pprbuf[i], max); \ |
| } while (0); |
| |
| |
| /** Apply appropriate single-, two- and three-chain constraints across the appropriate ppr block */ |
| static uint ppr_apply_constraint_to_block(int8* pprbuf, int8 constraint) |
| { |
| uint ret = 0; |
| uint i = 0; |
| int8 constraint_2chain = constraint - QDB(3); |
| #if (PPR_MAX_TX_CHAINS > 2) |
| int8 constraint_3chain = constraint - (QDB(4) + 3); /* - 4.75dBm */ |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| |
| APPLY_CONSTRAINT(PPR_CHAIN1_FIRST, PPR_CHAIN1_END, constraint); |
| APPLY_CONSTRAINT(PPR_CHAIN2_FIRST, PPR_CHAIN2_END, constraint_2chain); |
| #if (PPR_MAX_TX_CHAINS > 2) |
| APPLY_CONSTRAINT(PPR_CHAIN3_FIRST, PPR_CHAIN3_END, constraint_3chain); |
| #endif /* PPR_MAX_TX_CHAINS > 2 */ |
| return ret; |
| } |
| #endif /* (PPR_MAX_TX_CHAINS > 1) */ |
| |
| |
| /** |
| * Reduce total transmitted power to level of constraint. |
| * For two chain rates, the per-antenna power must be halved. |
| * For three chain rates, it must be a third of the constraint. |
| */ |
| uint ppr_apply_constraint_total_tx(ppr_t* pprptr, int8 constraint) |
| { |
| uint ret = 0; |
| |
| #if (PPR_MAX_TX_CHAINS > 1) |
| int8* pprbuf; |
| ASSERT(pprptr); |
| |
| /** |
| * TXPPR_TXPWR_MAX implies no constrains applied |
| * so skip applying constrains for multiple chains |
| */ |
| if (constraint == TXPPR_TXPWR_MAX) |
| return ret; |
| |
| switch (ppr_get_cur_bw(pprptr)) { |
| case WL_TX_BW_20: |
| { |
| pprbuf = (int8*)&pprptr->ppr_bw.ch20.b20; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| } |
| break; |
| case WL_TX_BW_40: |
| { |
| pprbuf = (int8*)&pprptr->ppr_bw.ch40.b40; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch40.b20in40; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| } |
| break; |
| case WL_TX_BW_80: |
| { |
| pprbuf = (int8*)&pprptr->ppr_bw.ch80.b80; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch80.b20in80; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch80.b40in80; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| } |
| break; |
| #ifdef WL11AC_160 |
| case WL_TX_BW_160: |
| { |
| pprbuf = (int8*)&pprptr->ppr_bw.ch160.b160; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch160.b20in160; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch160.b40in160; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch160.b80in160; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| } |
| break; |
| #endif /* WL11AC_160 */ |
| #ifdef WL11AC_80P80 |
| case WL_TX_BW_8080: |
| { |
| pprbuf = (int8*)&pprptr->ppr_bw.ch8080.b80ch1; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch8080.b80ch2; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch8080.b80in8080; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch8080.b20in8080; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| pprbuf = (int8*)&pprptr->ppr_bw.ch8080.b40in8080; |
| ret += ppr_apply_constraint_to_block(pprbuf, constraint); |
| } |
| break; |
| #endif /* WL11AC_80P80 */ |
| default: |
| ASSERT(0); |
| } |
| |
| #else |
| ASSERT(pprptr); |
| ret = ppr_apply_max(pprptr, constraint); |
| #endif /* PPR_MAX_TX_CHAINS > 1 */ |
| return ret; |
| } |
| |
| |
| /** Ensure no rate limit is lower than the specified minimum */ |
| uint ppr_apply_min(ppr_t* pprptr, int8 minval) |
| { |
| uint i; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| |
| for (i = 0; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++) { |
| *rptr = MAX(*rptr, minval); |
| } |
| return i; |
| } |
| |
| |
| /** Ensure no rate limit in this ppr set is greater than the corresponding limit in ppr_cap */ |
| uint ppr_apply_vector_ceiling(ppr_t* pprptr, const ppr_t* ppr_cap) |
| { |
| uint i = 0; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| const int8* capptr = (const int8*)&ppr_cap->ppr_bw; |
| |
| if (ppr_get_cur_bw(pprptr) == ppr_get_cur_bw(ppr_cap)) { |
| for (i = 0; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++, capptr++) { |
| *rptr = MIN(*rptr, *capptr); |
| } |
| } |
| return i; |
| } |
| |
| |
| /** Ensure no rate limit in this ppr set is lower than the corresponding limit in ppr_min */ |
| uint ppr_apply_vector_floor(ppr_t* pprptr, const ppr_t* ppr_min) |
| { |
| uint i = 0; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| const int8* minptr = (const int8*)&ppr_min->ppr_bw; |
| |
| if (ppr_get_cur_bw(pprptr) == ppr_get_cur_bw(ppr_min)) { |
| for (i = 0; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++, minptr++) { |
| *rptr = MAX((uint8)*rptr, (uint8)*minptr); |
| } |
| } |
| return i; |
| } |
| |
| |
| /** Get the maximum power in the ppr set */ |
| int8 ppr_get_max(ppr_t* pprptr) |
| { |
| uint i; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| int8 maxval = *rptr++; |
| |
| for (i = 1; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++) { |
| maxval = MAX(maxval, *rptr); |
| } |
| return maxval; |
| } |
| |
| /* Get the maximum power in the dsss ppr set */ |
| int8 ppr_get_dsss_max(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_chains_t tx_chains) |
| { |
| ppr_dsss_rateset_t dsss; |
| uint8 rate; |
| int8 maxval = 0; |
| ppr_get_dsss(pprptr, bw, tx_chains, &dsss); |
| maxval = dsss.pwr[0]; |
| for (rate = 1; rate < WL_RATESET_SZ_DSSS; rate++) { |
| maxval = MAX(maxval, dsss.pwr[rate]); |
| } |
| return maxval; |
| } |
| /** |
| * Get the minimum power in the ppr set, excluding disallowed |
| * rates and (possibly) powers set to the minimum for the phy |
| */ |
| int8 ppr_get_min(ppr_t* pprptr, int8 floor) |
| { |
| uint i; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| int8 minval = WL_RATE_DISABLED; |
| |
| for (i = 0; (i < ppr_pwrs_size(pprptr->hdr)) && ((minval == WL_RATE_DISABLED) || |
| (minval == floor)); i++, rptr++) { |
| minval = *rptr; |
| } |
| for (; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++) { |
| if ((*rptr != WL_RATE_DISABLED) && (*rptr != floor)) |
| minval = MIN(minval, *rptr); |
| } |
| return minval; |
| } |
| |
| |
| /** Get the maximum power for a given bandwidth in the ppr set */ |
| int8 ppr_get_max_for_bw(ppr_t* pprptr, wl_tx_bw_t bw) |
| { |
| uint i; |
| const pprpbw_t* bw_pwrs; |
| const int8* rptr; |
| int8 maxval; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| rptr = (const int8*)bw_pwrs; |
| maxval = *rptr++; |
| |
| for (i = 1; i < sizeof(*bw_pwrs); i++, rptr++) { |
| maxval = MAX(maxval, *rptr); |
| } |
| } else { |
| maxval = WL_RATE_DISABLED; |
| } |
| return maxval; |
| } |
| |
| |
| /** Get the minimum power for a given bandwidth in the ppr set */ |
| int8 ppr_get_min_for_bw(ppr_t* pprptr, wl_tx_bw_t bw) |
| { |
| uint i; |
| const pprpbw_t* bw_pwrs; |
| const int8* rptr; |
| int8 minval; |
| |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| if (bw_pwrs != NULL) { |
| rptr = (const int8*)bw_pwrs; |
| minval = *rptr++; |
| |
| for (i = 1; i < sizeof(*bw_pwrs); i++, rptr++) { |
| minval = MIN(minval, *rptr); |
| } |
| } else |
| minval = WL_RATE_DISABLED; |
| return minval; |
| } |
| |
| |
| /** Map the given function with its context value over the two power vectors */ |
| void |
| ppr_map_vec_dsss(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, ppr_t* pprptr2, |
| wl_tx_bw_t bw, wl_tx_chains_t tx_chains) |
| { |
| pprpbw_t* bw_pwrs1; |
| pprpbw_t* bw_pwrs2; |
| int8* powers1; |
| int8* powers2; |
| uint i; |
| |
| ASSERT(pprptr1); |
| ASSERT(pprptr2); |
| |
| bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); |
| bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); |
| if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { |
| powers1 = (int8*)ppr_get_dsss_group(bw_pwrs1, tx_chains); |
| powers2 = (int8*)ppr_get_dsss_group(bw_pwrs2, tx_chains); |
| if ((powers1 != NULL) && (powers2 != NULL)) { |
| for (i = 0; i < WL_RATESET_SZ_DSSS; i++) |
| (fn)(context, (uint8*)powers1++, (uint8*)powers2++); |
| } |
| } |
| } |
| |
| |
| /** Map the given function with its context value over the two power vectors */ |
| void |
| ppr_map_vec_ofdm(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, ppr_t* pprptr2, |
| wl_tx_bw_t bw, wl_tx_mode_t mode, wl_tx_chains_t tx_chains) |
| { |
| pprpbw_t* bw_pwrs1; |
| pprpbw_t* bw_pwrs2; |
| int8* powers1; |
| int8* powers2; |
| uint i; |
| |
| bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); |
| bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); |
| if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { |
| powers1 = (int8*)ppr_get_ofdm_group(bw_pwrs1, mode, tx_chains); |
| powers2 = (int8*)ppr_get_ofdm_group(bw_pwrs2, mode, tx_chains); |
| if ((powers1 != NULL) && (powers2 != NULL)) { |
| for (i = 0; i < WL_RATESET_SZ_OFDM; i++) |
| (fn)(context, (uint8*)powers1++, (uint8*)powers2++); |
| } |
| } |
| } |
| |
| |
| /** Map the given function with its context value over the two power vectors */ |
| void |
| ppr_map_vec_ht_mcs(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, |
| ppr_t* pprptr2, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, |
| wl_tx_chains_t tx_chains) |
| { |
| pprpbw_t* bw_pwrs1; |
| pprpbw_t* bw_pwrs2; |
| int8* powers1; |
| int8* powers2; |
| uint i; |
| |
| bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); |
| bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); |
| if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { |
| powers1 = (int8*)ppr_get_mcs_group(bw_pwrs1, Nss, mode, tx_chains); |
| powers2 = (int8*)ppr_get_mcs_group(bw_pwrs2, Nss, mode, tx_chains); |
| if ((powers1 != NULL) && (powers2 != NULL)) { |
| for (i = 0; i < WL_RATESET_SZ_HT_MCS; i++) |
| (fn)(context, (uint8*)powers1++, (uint8*)powers2++); |
| } |
| } |
| } |
| |
| |
| /** Map the given function with its context value over the two power vectors */ |
| void |
| ppr_map_vec_vht_mcs(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, |
| ppr_t* pprptr2, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, wl_tx_chains_t |
| tx_chains) |
| { |
| pprpbw_t* bw_pwrs1; |
| pprpbw_t* bw_pwrs2; |
| int8* powers1; |
| int8* powers2; |
| uint i; |
| |
| bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); |
| bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); |
| if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { |
| powers1 = (int8*)ppr_get_mcs_group(bw_pwrs1, Nss, mode, tx_chains); |
| powers2 = (int8*)ppr_get_mcs_group(bw_pwrs2, Nss, mode, tx_chains); |
| if ((powers1 != NULL) && (powers2 != NULL)) { |
| for (i = 0; i < WL_RATESET_SZ_VHT_MCS; i++) |
| (fn)(context, (uint8*)powers1++, (uint8*)powers2++); |
| } |
| } |
| } |
| |
| static bool |
| ppr_map_vec_per_bw(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, ppr_t* pprptr2, wl_tx_bw_t bw) |
| { |
| uint i; |
| pprpbw_t* bw_pwrs1; |
| pprpbw_t* bw_pwrs2; |
| int8* rptr1 = (int8*)&pprptr1->ppr_bw; |
| int8* rptr2 = (int8*)&pprptr2->ppr_bw; |
| bool ret = TRUE; |
| |
| bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); |
| bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); |
| |
| if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { |
| rptr1 = (int8*)bw_pwrs1; |
| rptr2 = (int8*)bw_pwrs2; |
| for (i = 0; i < sizeof(pprpbw_t); i++, rptr1++, rptr2++) { |
| (fn)(context, (uint8*)rptr1, (uint8*)rptr2); |
| } |
| } else { |
| ret = FALSE; |
| } |
| return ret; |
| } |
| |
| /** Map the given function with its context value over the two power vectors */ |
| |
| void |
| ppr_map_vec_all(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, ppr_t* pprptr2) |
| { |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_20); |
| |
| if (ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_40)) { |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_20IN40); |
| } |
| |
| if (ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_80)) { |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_20IN80); |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_40IN80); |
| } |
| |
| if (ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_160)) { |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_20IN160); |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_40IN160); |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_80IN160); |
| } |
| |
| if (ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_8080)) { |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_20IN8080); |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_40IN8080); |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_80IN8080); |
| (void)ppr_map_vec_per_bw(fn, context, pprptr1, pprptr2, WL_TX_BW_8080CHAN2); |
| } |
| } |
| |
| |
| /** Set PPR struct to a certain power level */ |
| void |
| ppr_set_cmn_val(ppr_t* pprptr, int8 val) |
| { |
| memset((uchar*)&pprptr->ppr_bw, val, ppr_pwrs_size(pprptr->hdr)); |
| } |
| |
| |
| /** Make an identical copy of a ppr structure (for ppr_bw==all case) */ |
| void |
| ppr_copy_struct(ppr_t* pprptr_s, ppr_t* pprptr_d) |
| { |
| int8* rptr_s = (int8*)&pprptr_s->ppr_bw; |
| int8* rptr_d = (int8*)&pprptr_d->ppr_bw; |
| /* ASSERT(ppr_pwrs_size(pprptr_d->hdr) >= ppr_pwrs_size(pprptr_s->hdr)); */ |
| |
| if (ppr_get_cur_bw(pprptr_s) == |
| ppr_get_cur_bw(pprptr_d)) |
| memcpy(rptr_d, rptr_s, ppr_pwrs_size(pprptr_s->hdr)); |
| else { |
| const pprpbw_t* src_bw_pwrs; |
| pprpbw_t* dest_bw_pwrs; |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_20); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_20); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_40); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_40); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_20IN40); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_20IN40); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_80); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_80); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_20IN80); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_20IN80); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_40IN80); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_40IN80); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_160); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_160); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_20IN160); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_20IN160); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_40IN160); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_40IN160); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_80IN160); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_80IN160); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_8080); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_8080); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_8080CHAN2); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_8080CHAN2); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_20IN8080); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_20IN8080); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_40IN8080); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_40IN8080); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| |
| src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_80IN8080); |
| dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_80IN8080); |
| if (src_bw_pwrs && dest_bw_pwrs) |
| bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, |
| sizeof(*src_bw_pwrs)); |
| } |
| } |
| |
| |
| /** Subtract each power from a common value and re-store */ |
| void |
| ppr_cmn_val_minus(ppr_t* pprptr, int8 val) |
| { |
| uint i; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| |
| for (i = 0; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++) { |
| if (*rptr != (int8)WL_RATE_DISABLED) |
| *rptr = val - *rptr; |
| } |
| |
| } |
| |
| |
| /** Subtract a common value from each power and re-store */ |
| void |
| ppr_minus_cmn_val(ppr_t* pprptr, int8 val) |
| { |
| uint i; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| |
| for (i = 0; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++) { |
| if (*rptr != (int8)WL_RATE_DISABLED) |
| *rptr = *rptr - val; |
| } |
| |
| } |
| |
| |
| /** Add a common value to each power and re-store */ |
| void |
| ppr_plus_cmn_val(ppr_t* pprptr, int8 val) |
| { |
| uint i; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| |
| for (i = 0; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++) { |
| if (*rptr != (int8)WL_RATE_DISABLED) |
| *rptr += val; |
| } |
| |
| } |
| |
| |
| /** Multiply by a percentage */ |
| void |
| ppr_multiply_percentage(ppr_t* pprptr, uint8 val) |
| { |
| uint i; |
| int8* rptr = (int8*)&pprptr->ppr_bw; |
| |
| for (i = 0; i < ppr_pwrs_size(pprptr->hdr); i++, rptr++) { |
| if (*rptr != (int8)WL_RATE_DISABLED) |
| *rptr = (*rptr * val) / 100; |
| } |
| |
| } |
| |
| |
| /** |
| * Compare two ppr variables p1 and p2, save the min value of each |
| * contents to variable p1 |
| */ |
| void |
| ppr_compare_min(ppr_t* p1, ppr_t* p2) |
| { |
| uint i; |
| int8* rptr1 = NULL; |
| int8* rptr2 = NULL; |
| uint32 pprsize = 0; |
| |
| if (ppr_get_cur_bw(p1) == ppr_get_cur_bw(p2)) { |
| rptr1 = (int8*)&p1->ppr_bw; |
| rptr2 = (int8*)&p2->ppr_bw; |
| pprsize = ppr_pwrs_size(p1->hdr); |
| } |
| |
| for (i = 0; i < pprsize; i++, rptr1++, rptr2++) { |
| *rptr1 = MIN(*rptr1, *rptr2); |
| } |
| } |
| |
| |
| /** |
| * Compare two ppr variables p1 and p2, save the max. value of each |
| * contents to variable p1 |
| */ |
| void |
| ppr_compare_max(ppr_t* p1, ppr_t* p2) |
| { |
| uint i; |
| int8* rptr1 = NULL; |
| int8* rptr2 = NULL; |
| uint32 pprsize = 0; |
| |
| if (ppr_get_cur_bw(p1) == ppr_get_cur_bw(p2)) { |
| rptr1 = (int8*)&p1->ppr_bw; |
| rptr2 = (int8*)&p2->ppr_bw; |
| pprsize = ppr_pwrs_size(p1->hdr); |
| } |
| |
| for (i = 0; i < pprsize; i++, rptr1++, rptr2++) { |
| *rptr1 = MAX(*rptr1, *rptr2); |
| } |
| } |
| |
| |
| /** |
| * Serialize the contents of the opaque ppr struct. |
| * Writes number of bytes copied, zero on error. |
| * Returns error code, BCME_OK if successful. |
| */ |
| int |
| ppr_serialize(const ppr_t* pprptr, uint8* buf, uint buflen, uint* bytes_copied) |
| { |
| int err = BCME_OK; |
| if (buflen <= sizeof(ppr_ser_mem_flag_t)) { |
| err = BCME_BUFTOOSHORT; |
| } else { |
| ppr_ser_mem_flag_t *smem_flag = (ppr_ser_mem_flag_t *)buf; |
| uint32 flag = NTOH32(smem_flag->flag); |
| |
| /* check if memory contains a valid flag, if not, use current |
| * condition (num of chains, txbf etc.) to serialize data. |
| */ |
| if (NTOH32(smem_flag->magic_word) != PPR_SER_MEM_WORD) { |
| flag = ppr_get_flag(); |
| } |
| |
| if (buflen >= ppr_ser_size_by_flag(flag, ppr_get_cur_bw(pprptr))) { |
| *bytes_copied = ppr_serialize_data(pprptr, buf, flag); |
| } else { |
| err = BCME_BUFTOOSHORT; |
| } |
| } |
| return err; |
| } |
| |
| |
| /** |
| * Deserialize the contents of a buffer into an opaque ppr struct. |
| * Creates an opaque structure referenced by *pptrptr, NULL on error. |
| * Returns error code, BCME_OK if successful. |
| */ |
| int |
| ppr_deserialize_create(osl_t *osh, const uint8* buf, uint buflen, ppr_t** pprptr) |
| { |
| const uint8* bptr = buf; |
| int err = BCME_OK; |
| ppr_t* lpprptr = NULL; |
| |
| if ((buflen > SER_HDR_LEN) && (bptr != NULL) && (*bptr == PPR_SERIALIZATION_VER)) { |
| const ppr_deser_header_t * ser_head = (const ppr_deser_header_t *)bptr; |
| wl_tx_bw_t ch_bw = ser_head->bw; |
| /* struct size plus header */ |
| uint32 ser_size = ppr_pwrs_size((ch_bw)) + SER_HDR_LEN; |
| |
| if ((lpprptr = ppr_create(osh, ch_bw)) != NULL) { |
| uint32 flags = NTOH32(ser_head->flags); |
| uint16 per_band_size = NTOH16(ser_head->per_band_size); |
| /* set the data with default value before deserialize */ |
| ppr_set_cmn_val(lpprptr, WL_RATE_DISABLED); |
| |
| ppr_deser_cpy(lpprptr, bptr + sizeof(*ser_head), flags, ch_bw, |
| per_band_size); |
| } else if (buflen < ser_size) { |
| err = BCME_BUFTOOSHORT; |
| } else { |
| err = BCME_NOMEM; |
| } |
| } else if (buflen <= SER_HDR_LEN) { |
| err = BCME_BUFTOOSHORT; |
| } else if (bptr == NULL) { |
| err = BCME_BADARG; |
| } else { |
| err = BCME_VERSION; |
| } |
| *pprptr = lpprptr; |
| return err; |
| } |
| |
| |
| /** |
| * Deserialize the contents of a buffer into an opaque ppr struct. |
| * Creates an opaque structure referenced by *pptrptr, NULL on error. |
| * Returns error code, BCME_OK if successful. |
| */ |
| int |
| ppr_deserialize(ppr_t* pprptr, const uint8* buf, uint buflen) |
| { |
| const uint8* bptr = buf; |
| int err = BCME_OK; |
| ASSERT(pprptr); |
| if ((buflen > SER_HDR_LEN) && (bptr != NULL) && (*bptr == PPR_SERIALIZATION_VER)) { |
| const ppr_deser_header_t * ser_head = (const ppr_deser_header_t *)bptr; |
| wl_tx_bw_t ch_bw = ser_head->bw; |
| |
| if (ch_bw == ppr_get_cur_bw(pprptr)) { |
| uint32 flags = NTOH32(ser_head->flags); |
| uint16 per_band_size = NTOH16(ser_head->per_band_size); |
| ppr_set_cmn_val(pprptr, WL_RATE_DISABLED); |
| ppr_deser_cpy(pprptr, bptr + sizeof(*ser_head), flags, ch_bw, |
| per_band_size); |
| } else { |
| err = BCME_BADARG; |
| } |
| } else if (buflen <= SER_HDR_LEN) { |
| err = BCME_BUFTOOSHORT; |
| } else if (bptr == NULL) { |
| err = BCME_BADARG; |
| } else { |
| err = BCME_VERSION; |
| } |
| return err; |
| } |
| |
| /* Get transmit channel bandwidths from chanspec */ |
| wl_tx_bw_t ppr_chanspec_bw(chanspec_t chspec) |
| { |
| uint chspec_bw = CHSPEC_BW(chspec); |
| wl_tx_bw_t ret = WL_TX_BW_20; |
| switch (chspec_bw) { |
| case WL_CHANSPEC_BW_2P5: |
| case WL_CHANSPEC_BW_5: |
| case WL_CHANSPEC_BW_10: |
| case WL_CHANSPEC_BW_20: |
| ret = WL_TX_BW_20; |
| break; |
| case WL_CHANSPEC_BW_40: |
| ret = WL_TX_BW_40; |
| break; |
| case WL_CHANSPEC_BW_80: |
| ret = WL_TX_BW_80; |
| break; |
| case WL_CHANSPEC_BW_160: |
| ret = WL_TX_BW_160; |
| break; |
| case WL_CHANSPEC_BW_8080: |
| ret = WL_TX_BW_8080; |
| break; |
| default : |
| ASSERT(0); |
| } |
| return ret; |
| } |
| |
| #if defined(WL_EXPORT_CURPOWER) || !defined(BCMDRIVER) |
| |
| #define DSSS_TLV_LEN (sizeof(ppr_dsss_tlv_t) + sizeof(ppr_dsss_rateset_t)) |
| #define OFDM_TLV_LEN (sizeof(ppr_ofdm_tlv_t) + sizeof(ppr_ofdm_rateset_t)) |
| #define MCS_TLV_LEN (sizeof(ppr_mcs_tlv_t) + sizeof(ppr_vht_mcs_rateset_t)) |
| #define PPR_TLV_VER_SIZE (sizeof(int8)) |
| |
| /* Fill DSSS TLV data into the given buffer */ |
| static uint32 |
| ppr_to_dssstlv_per_band(ppr_t* pprptr, wl_tx_bw_t bw, uint8 **to_tlv_buf, uint32 tlv_buf_len, |
| wl_tx_chains_t max_chain) |
| { |
| uint32 len = 0; |
| wl_tx_chains_t chain; |
| |
| pprpbw_t* bw_pwrs; |
| bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| for (chain = WL_TX_CHAINS_1; chain <= max_chain; chain++) { |
| bcm_tlv_t *tlv_hdr = (bcm_tlv_t *)(*to_tlv_buf); |
| ppr_dsss_tlv_t *dsss_tlv = (ppr_dsss_tlv_t *)tlv_hdr->data; |
| if (len + DSSS_TLV_LEN + BCM_TLV_HDR_SIZE <= tlv_buf_len) { |
| const int8* powers; |
| bool found = FALSE; |
| |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_dsss_group(bw_pwrs, chain); |
| if (powers) { |
| memcpy(dsss_tlv->pwr, powers, sizeof(ppr_dsss_rateset_t)); |
| found = TRUE; |
| } |
| } |
| |
| if (found) { |
| tlv_hdr->id = PPR_RGRP_DSSS_ID; |
| tlv_hdr->len = DSSS_TLV_LEN; |
| dsss_tlv->bw = bw; |
| dsss_tlv->chains = (uint8)chain; |
| len += (DSSS_TLV_LEN + BCM_TLV_HDR_SIZE); |
| *to_tlv_buf += (DSSS_TLV_LEN + BCM_TLV_HDR_SIZE); |
| } |
| } else { |
| break; |
| } |
| } |
| return len; |
| } |
| |
| /* Fill OFDM TLV data into the given buffer */ |
| static uint32 |
| ppr_to_ofdmtlv_per_band(ppr_t* pprptr, wl_tx_bw_t bw, uint8 **to_tlv_buf, uint32 tlv_buf_len, |
| wl_tx_chains_t max_chain) |
| { |
| uint32 len = 0; |
| wl_tx_chains_t chain; |
| wl_tx_mode_t mode; |
| pprpbw_t* bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| for (chain = WL_TX_CHAINS_1; chain <= max_chain; chain++) { |
| for (mode = WL_TX_MODE_NONE; mode < WL_NUM_TX_MODES; mode ++) { |
| bcm_tlv_t *tlv_hdr = (bcm_tlv_t *)(*to_tlv_buf); |
| ppr_ofdm_tlv_t *ofdm_tlv = (ppr_ofdm_tlv_t *)tlv_hdr->data; |
| const int8* powers; |
| bool found = FALSE; |
| if (len + OFDM_TLV_LEN + BCM_TLV_HDR_SIZE <= tlv_buf_len) { |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_ofdm_group(bw_pwrs, mode, chain); |
| if (powers != NULL) { |
| memcpy(ofdm_tlv->pwr, powers, |
| sizeof(ppr_ofdm_rateset_t)); |
| found = TRUE; |
| } |
| } |
| if (found) { |
| tlv_hdr->id = PPR_RGRP_OFDM_ID; |
| tlv_hdr->len = OFDM_TLV_LEN; |
| ofdm_tlv->bw = bw; |
| ofdm_tlv->chains = (uint8)chain; |
| ofdm_tlv->mode = mode; |
| |
| len += (OFDM_TLV_LEN + BCM_TLV_HDR_SIZE); |
| *to_tlv_buf += (OFDM_TLV_LEN + BCM_TLV_HDR_SIZE); |
| } |
| } else { |
| return len; |
| } |
| } |
| |
| } |
| return len; |
| } |
| |
| /* Fill MCS TLV data into the given buffer */ |
| static uint32 |
| ppr_to_mcstlv_per_band(ppr_t* pprptr, wl_tx_bw_t bw, uint8 **to_tlv_buf, uint32 tlv_buf_len, |
| wl_tx_chains_t max_chain) |
| { |
| uint32 len = 0; |
| wl_tx_chains_t chain; |
| wl_tx_mode_t mode; |
| uint8 nss; |
| pprpbw_t* bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| for (chain = WL_TX_CHAINS_1; chain <= max_chain; chain++) { |
| for (nss = WL_TX_NSS_1; nss <= chain; nss++) { |
| for (mode = WL_TX_MODE_NONE; mode < WL_NUM_TX_MODES; mode ++) { |
| bcm_tlv_t *tlv_hdr = (bcm_tlv_t *)(*to_tlv_buf); |
| ppr_mcs_tlv_t *mcs_tlv = (ppr_mcs_tlv_t *)tlv_hdr->data; |
| if (len + MCS_TLV_LEN + BCM_TLV_HDR_SIZE <= tlv_buf_len) { |
| const int8* powers; |
| bool found = FALSE; |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_mcs_group(bw_pwrs, nss, mode, |
| chain); |
| if (powers != NULL) { |
| memcpy(mcs_tlv->pwr, powers, |
| sizeof(ppr_vht_mcs_rateset_t)); |
| found = TRUE; |
| } |
| } |
| |
| if (found) { |
| tlv_hdr->id = PPR_RGRP_MCS_ID; |
| tlv_hdr->len = MCS_TLV_LEN; |
| mcs_tlv->bw = bw; |
| mcs_tlv->chains = (uint8)chain; |
| mcs_tlv->mode = mode; |
| mcs_tlv->nss = nss; |
| len += (MCS_TLV_LEN + BCM_TLV_HDR_SIZE); |
| *to_tlv_buf += (MCS_TLV_LEN + BCM_TLV_HDR_SIZE); |
| } |
| } else { |
| return len; |
| } |
| } |
| } |
| } |
| return len; |
| } |
| |
| /* Convert ppr structure to TLV data */ |
| void ppr_convert_to_tlv(ppr_t* pprptr, wl_tx_bw_t bw, uint8 *to_tlv_buf, uint32 tlv_buf_len, |
| wl_tx_chains_t max_chain) |
| { |
| uint32 len = tlv_buf_len; |
| wl_tx_bw_t check_bw; |
| ASSERT(pprptr && to_tlv_buf); |
| to_tlv_buf[0] = PPR_TLV_VER; |
| to_tlv_buf++; |
| switch (bw) { |
| case WL_TX_BW_2P5: |
| case WL_TX_BW_5: |
| case WL_TX_BW_10: |
| case WL_TX_BW_20: |
| len -= ppr_to_dssstlv_per_band(pprptr, bw, &to_tlv_buf, len, max_chain); |
| len -= ppr_to_ofdmtlv_per_band(pprptr, bw, &to_tlv_buf, len, max_chain); |
| ppr_to_mcstlv_per_band(pprptr, bw, &to_tlv_buf, len, max_chain); |
| break; |
| case WL_TX_BW_40: |
| len -= ppr_to_dssstlv_per_band(pprptr, WL_TX_BW_20IN40, &to_tlv_buf, len, |
| max_chain); |
| len -= ppr_to_ofdmtlv_per_band(pprptr, WL_TX_BW_20IN40, &to_tlv_buf, len, |
| max_chain); |
| len -= ppr_to_mcstlv_per_band(pprptr, WL_TX_BW_20IN40, &to_tlv_buf, len, |
| max_chain); |
| len -= ppr_to_ofdmtlv_per_band(pprptr, WL_TX_BW_40, &to_tlv_buf, len, max_chain); |
| len -= ppr_to_mcstlv_per_band(pprptr, WL_TX_BW_40, &to_tlv_buf, len, max_chain); |
| break; |
| case WL_TX_BW_80: |
| for (check_bw = WL_TX_BW_80; check_bw <= WL_TX_BW_40IN80; check_bw++) { |
| if (check_bw == WL_TX_BW_20IN40) |
| continue; |
| if (check_bw == WL_TX_BW_20IN80) { |
| len -= ppr_to_dssstlv_per_band(pprptr, check_bw, &to_tlv_buf, len, |
| max_chain); |
| } |
| len -= ppr_to_ofdmtlv_per_band(pprptr, check_bw, &to_tlv_buf, len, |
| max_chain); |
| len -= ppr_to_mcstlv_per_band(pprptr, check_bw, &to_tlv_buf, len, |
| max_chain); |
| } |
| break; |
| case WL_TX_BW_160: |
| for (check_bw = WL_TX_BW_160; check_bw <= WL_TX_BW_80IN160; check_bw++) { |
| if (check_bw == WL_TX_BW_20IN160) { |
| len -= ppr_to_dssstlv_per_band(pprptr, check_bw, &to_tlv_buf, len, |
| max_chain); |
| } |
| len -= ppr_to_ofdmtlv_per_band(pprptr, check_bw, &to_tlv_buf, len, |
| max_chain); |
| len -= ppr_to_mcstlv_per_band(pprptr, check_bw, &to_tlv_buf, len, |
| max_chain); |
| } |
| break; |
| case WL_TX_BW_8080: |
| for (check_bw = WL_TX_BW_8080; check_bw <= WL_TX_BW_80IN8080; check_bw++) { |
| if (check_bw == WL_TX_BW_20IN8080) { |
| len -= ppr_to_dssstlv_per_band(pprptr, WL_TX_BW_8080, &to_tlv_buf, |
| len, max_chain); |
| } |
| len -= ppr_to_ofdmtlv_per_band(pprptr, check_bw, &to_tlv_buf, len, |
| max_chain); |
| len -= ppr_to_mcstlv_per_band(pprptr, check_bw, &to_tlv_buf, len, |
| max_chain); |
| } |
| break; |
| default: |
| break; |
| }; |
| } |
| |
| /* Convert TLV data to ppr structure */ |
| int ppr_convert_from_tlv(ppr_t* pprptr, uint8 *from_tlv_buf, uint32 tlv_buf_len) |
| { |
| uint8 ser_ver = from_tlv_buf[0]; |
| bcm_tlv_t *elt = (bcm_tlv_t *)(&from_tlv_buf[1]); |
| int ret = BCME_OK; |
| if (ser_ver != PPR_TLV_VER) { |
| ret = BCME_VERSION; |
| } else { |
| do { |
| switch (elt->id) { |
| case PPR_RGRP_DSSS_ID: |
| { |
| ppr_dsss_tlv_t *dsss_tlv = (ppr_dsss_tlv_t *)elt->data; |
| ppr_set_dsss(pprptr, dsss_tlv->bw, dsss_tlv->chains, |
| (ppr_dsss_rateset_t*)dsss_tlv->pwr); |
| } |
| break; |
| case PPR_RGRP_OFDM_ID: |
| { |
| ppr_ofdm_tlv_t *ofdm_tlv = (ppr_ofdm_tlv_t *)elt->data; |
| ppr_set_ofdm(pprptr, ofdm_tlv->bw, ofdm_tlv->mode, |
| ofdm_tlv->chains, |
| (ppr_ofdm_rateset_t*)(ofdm_tlv->pwr)); |
| } |
| break; |
| case PPR_RGRP_MCS_ID: |
| { |
| ppr_mcs_tlv_t *mcs_tlv = (ppr_mcs_tlv_t *)elt->data; |
| ppr_set_vht_mcs(pprptr, mcs_tlv->bw, mcs_tlv->nss, |
| mcs_tlv->mode, mcs_tlv->chains, |
| (ppr_vht_mcs_rateset_t*)mcs_tlv->pwr); |
| } |
| break; |
| default: |
| ASSERT(0); |
| break; |
| } |
| |
| } while ((elt = bcm_next_tlv(elt, (int *)&tlv_buf_len)) != NULL); |
| } |
| return ret; |
| } |
| |
| /* Get the required buffer size for DSSS TLV data */ |
| static uint32 ppr_get_tlv_size_dsss(uint32 max_chain) |
| { |
| uint32 size = 0; |
| uint32 chain; |
| for (chain = 1; chain <= max_chain; chain++) { |
| size += (DSSS_TLV_LEN + BCM_TLV_HDR_SIZE); |
| } |
| return size; |
| } |
| |
| /* Get the required buffer size for MCS/OFDM TLV data */ |
| static uint32 ppr_get_ofdmmcs_size(ppr_t* pprptr, uint32 max_chain, wl_tx_bw_t bw) |
| { |
| uint32 size = 0; |
| uint32 chain; |
| wl_tx_mode_t mode; |
| uint32 nss; |
| const int8* powers; |
| pprpbw_t* bw_pwrs = ppr_get_bw_powers(pprptr, bw); |
| for (chain = 1; chain <= max_chain; chain++) { |
| for (mode = WL_TX_MODE_NONE; mode < WL_NUM_TX_MODES; mode ++) { |
| if (bw_pwrs != NULL) { |
| powers = ppr_get_ofdm_group(bw_pwrs, mode, chain); |
| if (powers != NULL) { |
| size += (OFDM_TLV_LEN + BCM_TLV_HDR_SIZE); |
| } |
| for (nss = WL_TX_NSS_1; nss <= chain; nss++) { |
| powers = ppr_get_mcs_group(bw_pwrs, nss, mode, |
| chain); |
| if (powers != NULL) { |
| size += (MCS_TLV_LEN + BCM_TLV_HDR_SIZE); |
| } |
| } |
| } |
| } |
| } |
| return size; |
| } |
| |
| /* Get the total TLV buffer size for the given ppr data of bandwidth and max chains */ |
| uint32 ppr_get_tlv_size(ppr_t* pprptr, wl_tx_bw_t bw, uint32 max_chain) |
| { |
| uint32 dsss_size = ppr_get_tlv_size_dsss(max_chain); |
| uint32 mcs_ofdm_size = ppr_get_ofdmmcs_size(pprptr, max_chain, bw); |
| uint32 ret = PPR_TLV_VER_SIZE; |
| switch (bw) { |
| case WL_TX_BW_2P5: |
| case WL_TX_BW_5: |
| case WL_TX_BW_10: |
| case WL_TX_BW_20: |
| ret += (dsss_size + mcs_ofdm_size); |
| break; |
| case WL_TX_BW_40: |
| /* 20IN40 and 40 */ |
| ret += (dsss_size + 2*mcs_ofdm_size); |
| break; |
| case WL_TX_BW_80: |
| /* 20IN80, 40IN80,80 */ |
| ret += (dsss_size + 3*mcs_ofdm_size); |
| break; |
| case WL_TX_BW_160: |
| /* 20IN160, 40IN160, 80IN160, 160 */ |
| ret += (dsss_size + 4*mcs_ofdm_size); |
| break; |
| case WL_TX_BW_8080: |
| /* 20IN8080, 40IN8080, 80IN8080, 8080, 8080CHAN2 */ |
| ret += (dsss_size + 5*mcs_ofdm_size); |
| break; |
| default: |
| ret = 0; |
| ASSERT(0); |
| break; |
| }; |
| |
| return ret; |
| } |
| |
| /* Get current PPR TLV version */ |
| uint32 ppr_get_tlv_ver(void) |
| { |
| return PPR_TLV_VER; |
| } |
| |
| #endif /* WL_EXPORT_CURPOWER || !BCMDRIVER */ |
| |
| #ifdef WLTXPWR_CACHE |
| |
| #define MAX_TXPWR_CACHE_ENTRIES 2 |
| #define TXPWR_ALL_INVALID 0xff |
| #define TXPWR_CACHE_TXPWR_MAX 0x7f /* WLC_TXPWR_MAX; */ |
| #define TXPWR_CACHE_SARLIMS_MAX ((TXPWR_CACHE_TXPWR_MAX << 24) \ |
| | (TXPWR_CACHE_TXPWR_MAX << 16) | (TXPWR_CACHE_TXPWR_MAX << 8) \ |
| | TXPWR_CACHE_TXPWR_MAX) |
| |
| /** transmit power cache */ |
| struct tx_pwr_cache_entry { |
| chanspec_t chanspec; |
| ppr_t* cache_pwrs[TXPWR_CACHE_NUM_TYPES]; |
| uint8 tx_pwr_max[PPR_MAX_TX_CHAINS]; |
| uint8 tx_pwr_min[PPR_MAX_TX_CHAINS]; |
| int8 txchain_offsets[PPR_MAX_TX_CHAINS]; |
| uint8 data_invalid_flags; |
| uint8 stf_tx_max_offset; |
| #ifdef WL_SARLIMIT |
| uint32 sar_lims; |
| #endif |
| int8 tx_pwr_max_boardlim[PPR_MAX_TX_CHAINS]; |
| }; |
| |
| #ifdef TXPWR_CACHE_IN_ROM |
| uint8 txpwr_cache[MAX_TXPWR_CACHE_ENTRIES*16] = {0}; |
| #endif |
| |
| #ifndef WLTXPWR_CACHE_PHY_ONLY |
| static int stf_tx_pwr_min = TXPWR_CACHE_TXPWR_MAX; |
| #endif |
| |
| static tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_get_entry(tx_pwr_cache_entry_t* cacheptr, |
| chanspec_t chanspec); |
| static tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_get_diff_entry(tx_pwr_cache_entry_t* cacheptr, |
| chanspec_t chanspec); |
| static void wlc_phy_txpwr_cache_clear_entry(osl_t *osh, tx_pwr_cache_entry_t* entryptr); |
| static tx_pwr_cache_entry_t* tx_pwr_cache_get(tx_pwr_cache_entry_t* cacheptr, uint32 idx); |
| |
| static tx_pwr_cache_entry_t* |
| BCMRAMFN(tx_pwr_cache_get)(tx_pwr_cache_entry_t* tx_pwr_cache, uint32 idx) |
| { |
| return (&tx_pwr_cache[idx]); |
| } |
| |
| /** Find a cache entry for the specified chanspec. */ |
| static tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_get_entry(tx_pwr_cache_entry_t* cacheptr, |
| chanspec_t chanspec) |
| { |
| uint i; |
| tx_pwr_cache_entry_t* entryptr = NULL; |
| |
| for (i = 0; i < (MAX_TXPWR_CACHE_ENTRIES); i++) { |
| entryptr = tx_pwr_cache_get(cacheptr, i); |
| if (entryptr->chanspec == chanspec) { |
| return entryptr; |
| } |
| } |
| return NULL; |
| } |
| |
| |
| /** Find a cache entry that's NOT for the specified chanspec. */ |
| static tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_get_diff_entry(tx_pwr_cache_entry_t* cacheptr, |
| chanspec_t chanspec) |
| { |
| uint i; |
| tx_pwr_cache_entry_t* entryptr = NULL; |
| |
| for (i = 0; i < (MAX_TXPWR_CACHE_ENTRIES) && (entryptr == NULL); i++) { |
| if (tx_pwr_cache_get(cacheptr, i)->chanspec != chanspec) { |
| entryptr = tx_pwr_cache_get(cacheptr, i); |
| } |
| } |
| return entryptr; |
| } |
| |
| |
| /** Clear a specific cache entry. Delete any ppr_t structs and clear the pointers. */ |
| static void wlc_phy_txpwr_cache_clear_entry(osl_t *osh, tx_pwr_cache_entry_t* entryptr) |
| { |
| uint i; |
| |
| entryptr->chanspec = 0; |
| |
| ASSERT(entryptr != NULL); |
| for (i = 0; i < TXPWR_CACHE_NUM_TYPES; i++) { |
| if (entryptr->cache_pwrs[i] != NULL) { |
| ppr_delete(osh, entryptr->cache_pwrs[i]); |
| entryptr->cache_pwrs[i] = NULL; |
| } |
| } |
| /* |
| * Don't bother with max, min and txchain_offsets, as they need to be |
| * initialised when the entry is setup for a new chanspec |
| */ |
| } |
| |
| |
| /** |
| * Get a ppr_t struct of a given type from the cache for the specified chanspec. |
| * Don't return the pointer if the cached data is invalid. |
| */ |
| bool wlc_phy_get_cached_pwr(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, uint pwr_type, |
| ppr_t* pprptr) |
| { |
| bool ret = FALSE; |
| if (pwr_type < TXPWR_CACHE_NUM_TYPES) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if ((entryptr != NULL) && |
| ((entryptr->data_invalid_flags & (0x01 << pwr_type)) == 0)) { |
| ppr_copy_struct(entryptr->cache_pwrs[pwr_type], pprptr); |
| ret = TRUE; |
| } |
| } |
| |
| return ret; |
| } |
| |
| |
| int wlc_phy_set_cached_pwr(osl_t* osh, tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, |
| uint pwr_type, ppr_t* pwrptr) |
| { |
| int result = BCME_NOTFOUND; |
| |
| if (pwr_type < TXPWR_CACHE_NUM_TYPES) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) { |
| ppr_copy_struct(pwrptr, entryptr->cache_pwrs[pwr_type]); |
| entryptr->data_invalid_flags &= ~(0x01 << pwr_type); /* now valid */ |
| result = BCME_OK; |
| } |
| } |
| return result; |
| } |
| |
| |
| void wlc_phy_txpwr_cache_clear(osl_t* osh, tx_pwr_cache_entry_t* cacheptr, |
| chanspec_t chanspec) |
| { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| if (entryptr != NULL) { |
| entryptr->chanspec = 0; |
| entryptr->data_invalid_flags = TXPWR_ALL_INVALID; |
| } |
| } |
| |
| |
| void wlc_phy_txpwr_cache_close(osl_t* osh, tx_pwr_cache_entry_t* cacheptr) |
| { |
| uint i; |
| |
| for (i = 0; i < MAX_TXPWR_CACHE_ENTRIES; i++) { |
| tx_pwr_cache_entry_t* entryptr = tx_pwr_cache_get(cacheptr, i); |
| wlc_phy_txpwr_cache_clear_entry(osh, entryptr); |
| } |
| MFREE(osh, cacheptr, (uint)sizeof(*cacheptr) * MAX_TXPWR_CACHE_ENTRIES); |
| } |
| |
| |
| tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_create(osl_t* osh) |
| { |
| int i, j; |
| tx_pwr_cache_entry_t* cacheptr = |
| (tx_pwr_cache_entry_t*)MALLOC(osh, |
| (uint)sizeof(tx_pwr_cache_entry_t) * MAX_TXPWR_CACHE_ENTRIES); |
| if (cacheptr != NULL) { |
| memset(cacheptr, 0, (uint)sizeof(tx_pwr_cache_entry_t) * MAX_TXPWR_CACHE_ENTRIES); |
| /* Allocate memory for each entry */ |
| for (i = 0; i < MAX_TXPWR_CACHE_ENTRIES; i++) { |
| tx_pwr_cache_entry_t* entryptr = tx_pwr_cache_get(cacheptr, i); |
| entryptr->data_invalid_flags = TXPWR_ALL_INVALID; |
| for (j = 0; j < TXPWR_CACHE_NUM_TYPES; j++) { |
| entryptr->cache_pwrs[j] = ppr_create(osh, ppr_get_max_bw()); |
| if (!entryptr->cache_pwrs[j]) { |
| wlc_phy_txpwr_cache_close(osh, cacheptr); |
| return NULL; |
| } |
| } |
| } |
| } |
| return cacheptr; |
| } |
| |
| |
| /* |
| * Get a ppr_t struct of a given type from the cache for the specified chanspec. |
| * Return the pointer even if the cached data is invalid. |
| */ |
| ppr_t* wlc_phy_get_cached_ppr_ptr(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, |
| uint pwr_type) |
| { |
| ppr_t* pwrptr = NULL; |
| |
| if (pwr_type < TXPWR_CACHE_NUM_TYPES) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) |
| pwrptr = entryptr->cache_pwrs[pwr_type]; |
| } |
| |
| return pwrptr; |
| } |
| |
| |
| /** Indicate if we have cached a particular ppr_t struct for any chanspec. */ |
| bool wlc_phy_is_pwr_cached(tx_pwr_cache_entry_t* cacheptr, uint pwr_type, ppr_t* pwrptr) |
| { |
| bool result = FALSE; |
| uint i; |
| |
| if (pwr_type < TXPWR_CACHE_NUM_TYPES) { |
| for (i = 0; (i < MAX_TXPWR_CACHE_ENTRIES) && (result == FALSE); i++) { |
| if (tx_pwr_cache_get(cacheptr, i)->cache_pwrs[pwr_type] == pwrptr) { |
| result = TRUE; |
| } |
| } |
| } |
| return result; |
| } |
| |
| |
| /* Get the maximum boardlim for the specified core and chanspec. */ |
| int8 wlc_phy_get_cached_boardlim(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, uint core) |
| { |
| uint8 board_lim = WL_RATE_DISABLED; |
| |
| if (core < PPR_MAX_TX_CHAINS) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) |
| board_lim = entryptr->tx_pwr_max_boardlim[core]; |
| } |
| |
| return board_lim; |
| } |
| |
| |
| /* Set the maximum boardlim for the specified core and chanspec. */ |
| int wlc_phy_set_cached_boardlim(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, uint core, |
| int8 board_lim) |
| { |
| int result = BCME_NOTFOUND; |
| |
| if (core < PPR_MAX_TX_CHAINS) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) { |
| entryptr->tx_pwr_max_boardlim[core] = board_lim; |
| result = BCME_OK; |
| } |
| } |
| return result; |
| } |
| |
| /** Get the maximum power for the specified core and chanspec. */ |
| uint8 wlc_phy_get_cached_pwr_max(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, uint core) |
| { |
| uint8 max_pwr = WL_RATE_DISABLED; |
| |
| if (core < PPR_MAX_TX_CHAINS) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) |
| max_pwr = entryptr->tx_pwr_max[core]; |
| } |
| |
| return max_pwr; |
| } |
| |
| |
| /** Set the maximum power for the specified core and chanspec. */ |
| int wlc_phy_set_cached_pwr_max(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, uint core, |
| uint8 max_pwr) |
| { |
| int result = BCME_NOTFOUND; |
| |
| if (core < PPR_MAX_TX_CHAINS) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) { |
| entryptr->tx_pwr_max[core] = max_pwr; |
| result = BCME_OK; |
| } |
| } |
| return result; |
| } |
| |
| |
| /** Get the minimum power for the specified core and chanspec. */ |
| uint8 wlc_phy_get_cached_pwr_min(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, uint core) |
| { |
| uint8 min_pwr = WL_RATE_DISABLED; |
| |
| if (core < PPR_MAX_TX_CHAINS) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) |
| min_pwr = entryptr->tx_pwr_min[core]; |
| } |
| |
| return min_pwr; |
| } |
| |
| |
| /** Set the minimum power for the specified core and chanspec. */ |
| int wlc_phy_set_cached_pwr_min(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, uint core, |
| uint8 min_pwr) |
| { |
| int result = BCME_NOTFOUND; |
| |
| if (core < PPR_MAX_TX_CHAINS) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) { |
| entryptr->tx_pwr_min[core] = min_pwr; |
| result = BCME_OK; |
| } |
| } |
| return result; |
| } |
| |
| |
| /** Get the txchain offsets for the specified chanspec. */ |
| int8 wlc_phy_get_cached_txchain_offsets(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, |
| uint core) |
| { |
| uint8 offset = WL_RATE_DISABLED; |
| |
| if (core < PPR_MAX_TX_CHAINS) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) |
| offset = entryptr->txchain_offsets[core]; |
| } |
| |
| return offset; |
| } |
| |
| |
| /** Set the txchain offsets for the specified chanspec. */ |
| int wlc_phy_set_cached_txchain_offsets(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, |
| uint core, int8 offset) |
| { |
| int result = BCME_NOTFOUND; |
| |
| if (core < PPR_MAX_TX_CHAINS) { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) { |
| entryptr->txchain_offsets[core] = offset; |
| result = BCME_OK; |
| } |
| } |
| return result; |
| } |
| |
| |
| /** Indicate if we have a cache entry for the specified chanspec. */ |
| bool wlc_phy_txpwr_cache_is_cached(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec) |
| { |
| bool result = FALSE; |
| |
| if (wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec)) { |
| result = TRUE; |
| } |
| return result; |
| } |
| |
| |
| /** Find a cache entry that's NOT for the specified chanspec. Return the chanspec. */ |
| chanspec_t wlc_phy_txpwr_cache_find_other_cached_chanspec(tx_pwr_cache_entry_t* cacheptr, |
| chanspec_t chanspec) |
| { |
| chanspec_t chan = 0; |
| |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_diff_entry(cacheptr, chanspec); |
| if (entryptr != NULL) { |
| chan = entryptr->chanspec; |
| } |
| return chan; |
| } |
| |
| |
| /** Invalidate all cached data. */ |
| void wlc_phy_txpwr_cache_invalidate(tx_pwr_cache_entry_t* cacheptr) |
| { |
| uint j; |
| |
| for (j = 0; j < MAX_TXPWR_CACHE_ENTRIES; j++) { |
| tx_pwr_cache_entry_t* entryptr = tx_pwr_cache_get(cacheptr, j); |
| if (entryptr->chanspec != 0) { |
| uint i; |
| entryptr->data_invalid_flags = TXPWR_ALL_INVALID; |
| for (i = 0; i < PPR_MAX_TX_CHAINS; i++) { |
| entryptr->tx_pwr_min[i] = TXPWR_CACHE_TXPWR_MAX; |
| entryptr->tx_pwr_max[i] = WL_RATE_DISABLED; |
| entryptr->txchain_offsets[i] = WL_RATE_DISABLED; |
| } |
| entryptr->stf_tx_max_offset = TXPWR_CACHE_TXPWR_MAX; |
| #ifdef WL_SARLIMIT |
| entryptr->sar_lims = TXPWR_CACHE_SARLIMS_MAX; |
| #endif |
| } |
| } |
| } |
| |
| |
| /** Find an empty cache entry and initialise it. */ |
| int wlc_phy_txpwr_setup_entry(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec) |
| { |
| int result = BCME_NOTFOUND; |
| |
| /* find an empty entry */ |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, 0); |
| if (entryptr != NULL) { |
| uint i; |
| |
| entryptr->chanspec = chanspec; |
| for (i = 0; i < PPR_MAX_TX_CHAINS; i++) { |
| entryptr->tx_pwr_min[i] = TXPWR_CACHE_TXPWR_MAX; /* WLC_TXPWR_MAX; */ |
| entryptr->tx_pwr_max[i] = WL_RATE_DISABLED; |
| entryptr->txchain_offsets[i] = WL_RATE_DISABLED; |
| } |
| entryptr->stf_tx_max_offset = TXPWR_CACHE_TXPWR_MAX; |
| #ifdef WL_SARLIMIT |
| entryptr->sar_lims = TXPWR_CACHE_SARLIMS_MAX; |
| #endif |
| result = BCME_OK; |
| } |
| return result; |
| } |
| |
| #ifdef WL_SARLIMIT |
| void wlc_phy_set_cached_sar_lims(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, |
| uint32 sar_lims) |
| { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) { |
| entryptr->sar_lims = sar_lims; |
| } |
| } |
| |
| |
| uint32 wlc_phy_get_cached_sar_lims(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec) |
| { |
| uint32 sar_lims = TXPWR_CACHE_SARLIMS_MAX; |
| |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) |
| sar_lims = entryptr->sar_lims; |
| |
| return sar_lims; |
| } |
| #endif /* WL_SARLIMIT */ |
| |
| |
| #ifndef WLTXPWR_CACHE_PHY_ONLY |
| |
| /** Get the cached minimum Tx power */ |
| int wlc_phy_get_cached_stf_pwr_min_dbm(tx_pwr_cache_entry_t* cacheptr) |
| { |
| return stf_tx_pwr_min; |
| } |
| |
| /** set the cached minimum Tx power */ |
| void wlc_phy_set_cached_stf_pwr_min_dbm(tx_pwr_cache_entry_t* cacheptr, int min_pwr) |
| { |
| stf_tx_pwr_min = min_pwr; |
| } |
| |
| void wlc_phy_set_cached_stf_max_offset(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec, |
| uint8 max_offset) |
| { |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) { |
| entryptr->stf_tx_max_offset = max_offset; |
| } |
| } |
| |
| uint8 wlc_phy_get_cached_stf_max_offset(tx_pwr_cache_entry_t* cacheptr, chanspec_t chanspec) |
| { |
| uint8 max_offset = TXPWR_CACHE_TXPWR_MAX; |
| |
| tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(cacheptr, chanspec); |
| |
| if (entryptr != NULL) |
| max_offset = entryptr->stf_tx_max_offset; |
| |
| return max_offset; |
| |
| } |
| #endif /* WLTXPWR_CACHE_PHY_ONLY */ |
| #endif /* WLTXPWR_CACHE */ |