blob: 448821a04a2c9c14adcc8dc1778ae79b3bdfb4c7 [file] [log] [blame]
/*
* 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.
*
* Copyright (C) 2016, Broadcom Corporation
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
* 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 Corporation.
*
* $Id: $
*/
#include <typedefs.h>
#include <bcmendian.h>
#include <bcmwifi_channels.h>
#include <wlc_ppr.h>
#ifndef BCMDRIVER
#ifndef WL_BEAMFORMING
#define WL_BEAMFORMING /* enable TxBF definitions 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 */
/* This marks the start of a packed structure section. */
#include <packed_section_start.h>
#define 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;
} 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)
/* Flag bits in serialization/deserialization */
#define PPR_MAX_TX_CHAIN_MASK 0x00000003 /* mask of Tx chains */
#define PPR_BEAMFORMING 0x00000004 /* bit indicates BF is on */
#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]; /* 8HT/10VHT pwrs starting at 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]; /* 8HT/10VHT pwrs starting at 1x2cdd_mcs0 */
int8 p_2x2stbc_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x2stbc_mcs0 */
int8 p_2x2vhtss2[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x2sdm_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]; /* 8HT/10VHT pwrs starting at 1x3cdd_mcs0 */
int8 p_2x3stbc_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x3stbc_mcs0 */
int8 p_2x3vhtss2[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x3sdm_mcs8 spexp1 */
int8 p_3x3vhtss3[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 3x3sdm_mcs16 */
#endif
#ifdef WL_BEAMFORMING
int8 p_1x2txbf_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM TXBF transmission */
int8 p_1x2txbf_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 1x2txbf_mcs0 */
int8 p_2x2txbf_vhtss2[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x2txbf_mcs8 */
#if (PPR_MAX_TX_CHAINS > 2)
int8 p_1x3txbf_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM TXBF transmission */
int8 p_1x3txbf_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 1x3txbf_mcs0 */
int8 p_2x3txbf_vhtss2[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x3txbf_mcs8 */
int8 p_3x3txbf_vhtss3[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 3x3txbf_mcs16 */
#endif
#endif /* WL_BEAMFORMING */
#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_2x2vhtss2) + sizeof(((pprpbw_t *)0)->p_2x2vhtss2))
#define PPR_CHAIN2_SIZE (PPR_CHAIN2_END - PPR_CHAIN2_FIRST)
#define PPR_CHAIN2_MCS_SIZE (PPR_CHAIN2_END - PPR_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_3x3vhtss3) + sizeof(((pprpbw_t *)0)->p_3x3vhtss3))
#define PPR_CHAIN3_SIZE (PPR_CHAIN3_END - PPR_CHAIN3_FIRST)
#define PPR_CHAIN3_MCS_SIZE (PPR_CHAIN3_END - PPR_CHAIN3_FIRST_MCS)
#endif
#ifdef WL_BEAMFORMING
#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_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)
#endif
#endif /* WL_BEAMFORMING */
#endif /* PPR_MAX_TX_CHAINS > 1 */
#define PPR_BW_MAX WL_TX_BW_80 /* Maximum supported bandwidth */
/* 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;
/*
* 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 {
wl_tx_bw_t ch_bw;
BWL_PRE_PACKED_STRUCT union {
ppr_bw_20_t ch20;
ppr_bw_40_t ch40;
ppr_bw_80_t ch80;
} ppr_bw;
} BWL_POST_PACKED_STRUCT;
/* This marks the end of a packed structure section. */
#include <packed_section_end.h>
/* 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;
#if PPR_MAX_TX_CHAINS > 1
if (PPR_TXBF_ENAB()) {
flag |= PPR_BEAMFORMING;
}
#endif
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;
bool bf = (flags & PPR_BEAMFORMING) != 0;
BCM_REFERENCE(chain);
BCM_REFERENCE(bf);
#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;
}
#endif
#ifdef WL_BEAMFORMING
if (PPR_TXBF_ENAB() && bf) {
ret += PPR_BF_CHAIN2_SIZE;
}
#if (PPR_MAX_TX_CHAINS > 2)
if (PPR_TXBF_ENAB() && bf && chain > 2) {
ret += PPR_BF_CHAIN3_SIZE;
}
#endif
#endif /* WL_BEAMFORMING */
#endif /* PPR_MAX_TX_CHAINS > 1 */
return ret;
}
/* Return the required serialization size based on the flag field. */
static uint ppr_ser_size_by_flag(uint32 flag, wl_tx_bw_t bw)
{
uint ret = ppr_ser_size_per_band(flag);
switch (bw) {
case WL_TX_BW_20:
break;
case WL_TX_BW_40:
ret *= sizeof(ppr_bw_40_t)/sizeof(pprpbw_t);
break;
case WL_TX_BW_80:
ret *= sizeof(ppr_bw_80_t)/sizeof(pprpbw_t);
break;
default:
ASSERT(0);
}
return ret;
}
#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 */
bool bf = (serflag & PPR_BEAMFORMING) != 0;
#endif
COPY_PPR_TOBUF(PPR_CHAIN1_FIRST, PPR_CHAIN1_SIZE);
#if (PPR_MAX_TX_CHAINS > 1)
BCM_REFERENCE(bf);
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);
}
#endif
#ifdef WL_BEAMFORMING
if (PPR_TXBF_ENAB() && bf) {
COPY_PPR_TOBUF(PPR_BF_CHAIN2_FIRST, PPR_BF_CHAIN2_SIZE);
}
#if (PPR_MAX_TX_CHAINS > 2)
if (PPR_TXBF_ENAB() && bf && chain > 2) {
COPY_PPR_TOBUF(PPR_BF_CHAIN3_FIRST, PPR_BF_CHAIN3_SIZE);
}
#endif
#endif /* WL_BEAMFORMING */
#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 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)pprptr->ch_bw;
header->flags = HTON32(ppr_get_flag());
header->per_band_size = HTON16(ppr_ser_size_per_band(serflag));
buf += sizeof(*header);
switch (header->bw) {
case WL_TX_BW_20:
{
const uint8* pprbuf = (const uint8*)&pprptr->ppr_bw.ch20.b20;
ret += ppr_serialize_block(pprbuf, &buf, serflag);
}
break;
case WL_TX_BW_40:
{
const uint8* pprbuf = (const uint8*)&pprptr->ppr_bw.ch40.b40;
ret += ppr_serialize_block(pprbuf, &buf, serflag);
pprbuf = (const uint8*)&pprptr->ppr_bw.ch40.b20in40;
ret += ppr_serialize_block(pprbuf, &buf, serflag);
}
break;
case WL_TX_BW_80:
{
const uint8* pprbuf = (const uint8*)&pprptr->ppr_bw.ch80.b80;
ret += ppr_serialize_block(pprbuf, &buf, serflag);
pprbuf = (const uint8*)&pprptr->ppr_bw.ch80.b20in80;
ret += ppr_serialize_block(pprbuf, &buf, serflag);
pprbuf = (const uint8*)&pprptr->ppr_bw.ch80.b40in80;
ret += ppr_serialize_block(pprbuf, &buf, serflag);
}
break;
default:
ASSERT(0);
}
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;
bool bf = (flag & PPR_BEAMFORMING) != 0;
uint16 len = PPR_CHAIN1_SIZE;
BCM_REFERENCE(chain);
BCM_REFERENCE(bf);
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;
}
#endif
#ifdef WL_BEAMFORMING
if (PPR_TXBF_ENAB() && bf) {
bcopy(*inbuf, &pobuf[PPR_BF_CHAIN2_FIRST], PPR_BF_CHAIN2_SIZE);
*inbuf += PPR_BF_CHAIN2_SIZE;
len += PPR_BF_CHAIN2_SIZE;
}
#if (PPR_MAX_TX_CHAINS > 2)
if (PPR_TXBF_ENAB() && bf && chain > 2) {
bcopy(*inbuf, &pobuf[PPR_BF_CHAIN3_FIRST], PPR_BF_CHAIN3_SIZE);
*inbuf += PPR_BF_CHAIN3_SIZE;
len += PPR_BF_CHAIN3_SIZE;
}
#endif
#endif /* WL_BEAMFORMING */
#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)
{
pptr->ch_bw = bw;
switch (bw) {
case WL_TX_BW_20:
{
uint8* pobuf = (uint8*)&pptr->ppr_bw.ch20;
ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size);
}
break;
case WL_TX_BW_40:
{
uint8* pobuf = (uint8*)&pptr->ppr_bw.ch40.b40;
ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size);
pobuf = (uint8*)&pptr->ppr_bw.ch40.b20in40;
ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size);
}
break;
case WL_TX_BW_80:
{
uint8* pobuf = (uint8*)&pptr->ppr_bw.ch80.b80;
ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size);
pobuf = (uint8*)&pptr->ppr_bw.ch80.b20in80;
ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size);
pobuf = (uint8*)&pptr->ppr_bw.ch80.b40in80;
ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size);
}
break;
default:
ASSERT(0);
}
}
/* 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)
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_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_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;
}
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}
};
/* 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 == p->ch_bw)
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)
case WL_TX_CHAINS_3:
group_pwrs = bw_pwrs->p_1x3dsss;
break;
#endif
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)
case WL_TX_CHAINS_3:
#ifdef WL_BEAMFORMING
if (mode == WL_TX_MODE_TXBF)
group_pwrs = bw_pwrs->p_1x3txbf_ofdm;
else
#endif
group_pwrs = bw_pwrs->p_1x3cdd_ofdm;
break;
#endif /* PPR_MAX_TX_CHAINS > 2 */
case WL_TX_CHAINS_2:
#ifdef WL_BEAMFORMING
if (mode == WL_TX_MODE_TXBF)
group_pwrs = bw_pwrs->p_1x2txbf_ofdm;
else
#endif
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}
#endif
#endif /* PPR_MAX_TX_CHAINS > 1 */
};
#ifdef WL_BEAMFORMING
/* 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)}
#endif
#endif /* PPR_MAX_TX_CHAINS > 1 */
};
#endif /* WL_BEAMFORMING */
#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}
#endif
};
#ifdef WL_BEAMFORMING
/* 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)}
#endif
};
#endif /* WL_BEAMFORMING */
#if (PPR_MAX_TX_CHAINS > 2)
static const int mcs_groups_nss3[WL_NUM_TX_MODES] = {
/* 3 chains only */
OFFSETOF(pprpbw_t, p_3x3vhtss3),
OFFSNONE,
OFFSNONE,
OFFSNONE,
};
#ifdef WL_BEAMFORMING
/* mcs group with TXBF data */
static const int mcs_groups_nss3_txbf[WL_NUM_TX_MODES] = {
/* 3 chains only */
OFFSETOF(pprpbw_t, p_3x3vhtss3),
OFFSNONE,
OFFSNONE,
OFFSETOF(pprpbw_t, p_3x3txbf_vhtss3)
};
#endif /* WL_BEAMFORMING */
#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)
case WL_TX_NSS_3:
if (tx_chains == WL_TX_CHAINS_3) {
#ifdef WL_BEAMFORMING
if (PPR_TXBF_ENAB()) {
offset = mcs_groups_nss3_txbf[mode];
} else
#endif /* WL_BEAMFORMING */
{
offset = mcs_groups_nss3[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)) {
#ifdef WL_BEAMFORMING
if (PPR_TXBF_ENAB()) {
offset = mcs_groups_nss2_txbf[tx_chains - Nss][mode];
} else
#endif /* WL_BEAMFORMING */
{
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) {
#ifdef WL_BEAMFORMING
if (PPR_TXBF_ENAB()) {
offset = mcs_groups_nss1_txbf[tx_chains - Nss][mode];
} else
#endif /* WL_BEAMFORMING */
{
offset = mcs_groups_nss1[tx_chains - Nss][mode];
}
if (offset != OFFSNONE) {
group_pwrs = (int8*)bw_pwrs + offset;
}
}
else
ASSERT(0);
break;
default:
ASSERT(0);
break;
}
return group_pwrs;
}
/* Size routine for user alloc/dealloc */
static uint32 ppr_pwrs_size(wl_tx_bw_t bw)
{
uint32 size;
switch (bw) {
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;
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->ch_bw = bw;
}
/* 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->ch_bw));
}
/* Size routine for user alloc/dealloc */
uint32 ppr_size(wl_tx_bw_t bw)
{
return ppr_pwrs_size(bw) + sizeof(wl_tx_bw_t);
}
/* Size routine for user serialization alloc */
uint32 ppr_ser_size(const ppr_t* pprptr)
{
return ppr_pwrs_size(pprptr->ch_bw) + 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; /* struct size plus headers */
}
/* Constructor routine for opaque PPR struct */
ppr_t* ppr_create(osl_t *osh, wl_tx_bw_t bw)
{
ppr_t* pprptr;
ASSERT((bw == WL_TX_BW_20) || (bw == WL_TX_BW_40) || (bw == WL_TX_BW_80));
#ifndef BCMDRIVER
BCM_REFERENCE(osh);
if ((pprptr = (ppr_t*)malloc((uint)ppr_size(bw))) != NULL) {
#else
if ((pprptr = (ppr_t*)MALLOC(osh, (uint)ppr_size(bw))) != NULL) {
#endif
ppr_init(pprptr, bw);
}
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->ch_bw, len);
}
/* Destructor routine for opaque PPR struct */
void ppr_delete(osl_t *osh, ppr_t* pprptr)
{
ASSERT((pprptr->ch_bw == WL_TX_BW_20) || (pprptr->ch_bw == WL_TX_BW_40) ||
(pprptr->ch_bw == WL_TX_BW_80));
#ifndef BCMDRIVER
BCM_REFERENCE(osh);
free(pprptr);
#else
MFREE(osh, pprptr, (uint)ppr_size(pprptr->ch_bw));
#endif
}
/* Type routine for inferring opaque structure size */
wl_tx_bw_t ppr_get_ch_bw(const ppr_t* pprptr)
{
return pprptr->ch_bw;
}
/* 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) {
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) {
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 */
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) {
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) {
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);
}
}
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);
}
}
return cnt;
}
/* Set the HT MCS values for the group specified by Nss, with the given bw and tx chains */
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);
}
}
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);
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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->ch_bw); i++, rptr++) {
*rptr = MIN(*rptr, maxval);
}
return i;
}
/*
* 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
* 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 < WL_RATESET_SZ_HT_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 9 can be disabled separately */
for (; i < len; i++) {
if (powers[i] <= threshold) {
powers[i] = WL_RATE_DISABLED;
}
}
}
/*
* 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);
#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) {
ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_VHT_MCS);
}
#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) {
ppr_force_disabled_group(powers, threshold,
WL_RATESET_SZ_VHT_MCS);
}
}
#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) {
ppr_force_disabled_group(powers, threshold, WL_RATESET_SZ_VHT_MCS);
}
#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) {
ppr_force_disabled_group(powers, threshold,
WL_RATESET_SZ_VHT_MCS);
}
}
#endif /* WL_BEAMFORMING */
#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
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
#ifdef WL_BEAMFORMING
APPLY_CONSTRAINT(PPR_BF_CHAIN2_FIRST, PPR_BF_CHAIN2_END, constraint_2chain);
#if (PPR_MAX_TX_CHAINS > 2)
APPLY_CONSTRAINT(PPR_BF_CHAIN3_FIRST, PPR_BF_CHAIN3_END, constraint_3chain);
#endif
#endif /* WL_BEAMFORMING */
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);
switch (pprptr->ch_bw) {
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;
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->ch_bw); 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 (pprptr->ch_bw == ppr_cap->ch_bw) {
for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); 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 (pprptr->ch_bw == ppr_min->ch_bw) {
for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); 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->ch_bw); i++, rptr++) {
maxval = MAX(maxval, *rptr);
}
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->ch_bw)) && ((minval == WL_RATE_DISABLED) ||
(minval == floor)); i++, rptr++) {
minval = *rptr;
}
for (; i < ppr_pwrs_size(pprptr->ch_bw); 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++);
}
}
}
/* 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)
{
uint i;
pprpbw_t* bw_pwrs1;
pprpbw_t* bw_pwrs2;
int8* rptr1 = (int8*)&pprptr1->ppr_bw;
int8* rptr2 = (int8*)&pprptr2->ppr_bw;
bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_20);
bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_20);
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);
}
}
bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_40);
bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_40);
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);
}
bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_20IN40);
bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_20IN40);
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);
}
}
}
bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_80);
bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_80);
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);
}
bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_20IN80);
bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_20IN80);
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);
}
}
bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_40IN80);
bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_40IN80);
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);
}
}
}
}
/* 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->ch_bw));
}
/* 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->ch_bw) >= ppr_pwrs_size(pprptr_s->ch_bw)); */
if (pprptr_s->ch_bw == pprptr_d->ch_bw)
bcopy(rptr_s, rptr_d, ppr_pwrs_size(pprptr_s->ch_bw));
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));
}
}
/* 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->ch_bw); 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->ch_bw); i++, rptr++) {
if (*rptr != (int8)WL_RATE_DISABLED)
*rptr = (*rptr > val) ? (*rptr - val) : 0;
}
}
/* 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->ch_bw); 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->ch_bw); 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 (p1->ch_bw == p2->ch_bw) {
rptr1 = (int8*)&p1->ppr_bw;
rptr2 = (int8*)&p2->ppr_bw;
pprsize = ppr_pwrs_size(p1->ch_bw);
}
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 (p1->ch_bw == p2->ch_bw) {
rptr1 = (int8*)&p1->ppr_bw;
rptr2 = (int8*)&p2->ppr_bw;
pprsize = ppr_pwrs_size(p1->ch_bw);
}
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, pprptr->ch_bw)) {
*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 == pprptr->ch_bw) {
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;
}
#ifdef WLTXPWR_CACHE
#define MAX_TXPWR_CACHE_ENTRIES 2
#define TXPWR_ALL_INVALID 0xff
#define TXPWR_CACHE_TXPWR_MAX 0x7f /* WLC_TXPWR_MAX; */
/* transmit power cache */
typedef 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;
int stf_tx_target_pwr_min;
} tx_pwr_cache_entry_t;
#ifdef TXPWR_CACHE_IN_ROM
uint8 txpwr_cache[MAX_TXPWR_CACHE_ENTRIES*16] = {0};
#endif
static tx_pwr_cache_entry_t tx_pwr_cache[MAX_TXPWR_CACHE_ENTRIES] = {{0}, {0}};
static int stf_tx_pwr_min = TXPWR_CACHE_TXPWR_MAX;
static tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_get_entry(chanspec_t chanspec);
static tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_get_diff_entry(chanspec_t chanspec);
static void wlc_phy_txpwr_cache_clear_entry(osl_t *osh, tx_pwr_cache_entry_t* entryptr);
/* Find a cache entry for the specified chanspec. */
static tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_get_entry(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[i].chanspec == chanspec) {
entryptr = &tx_pwr_cache[i];
}
}
return entryptr;
}
/* Find a cache entry that's NOT for the specified chanspec. */
static tx_pwr_cache_entry_t* wlc_phy_txpwr_cache_get_diff_entry(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[i].chanspec != chanspec) {
entryptr = &tx_pwr_cache[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.
*/
ppr_t* wlc_phy_get_cached_pwr(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(chanspec);
if ((entryptr != NULL) &&
((entryptr->data_invalid_flags & (0x01 << pwr_type)) == 0))
pwrptr = entryptr->cache_pwrs[pwr_type];
}
return pwrptr;
}
/* Add a ppr_t struct of a given type to the cache for the specified chanspec. */
int wlc_phy_set_cached_pwr(osl_t *osh, 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(chanspec);
if (entryptr != NULL) {
if ((entryptr->cache_pwrs[pwr_type] != NULL) &&
(entryptr->cache_pwrs[pwr_type] != pwrptr)) {
ppr_delete(osh, entryptr->cache_pwrs[pwr_type]);
}
entryptr->cache_pwrs[pwr_type] = pwrptr;
entryptr->data_invalid_flags &= ~(0x01 << pwr_type); /* now valid */
result = BCME_OK;
}
}
return result;
}
/* Indicate if we have cached a particular ppr_t struct for any chanspec. */
bool wlc_phy_is_pwr_cached(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[i].cache_pwrs[pwr_type] == pwrptr) {
result = TRUE;
}
}
}
return result;
}
/* Get the minimum target power for all cores for the chanspec. */
int wlc_phy_get_cached_stf_target_pwr_min(chanspec_t chanspec)
{
int min_pwr = TXPWR_CACHE_TXPWR_MAX;
tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec);
if ((entryptr != NULL) &&
((entryptr->data_invalid_flags & (TXPWR_STF_TARGET_PWR_MIN_INVALID)) == 0))
min_pwr = entryptr->stf_tx_target_pwr_min;
return min_pwr;
}
/* set the minimum target power for all cores for the chanspec. */
int wlc_phy_set_cached_stf_target_pwr_min(chanspec_t chanspec, int min_pwr)
{
int result = BCME_NOTFOUND;
tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec);
if (entryptr != NULL) {
entryptr->stf_tx_target_pwr_min = min_pwr;
entryptr->data_invalid_flags &= ~TXPWR_STF_TARGET_PWR_MIN_INVALID; /* now valid */
result = BCME_OK;
}
return result;
}
/* Get the maximum power for the specified core and chanspec. */
uint8 wlc_phy_get_cached_pwr_max(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(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(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(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(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(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(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(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(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(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(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(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(chanspec_t chanspec)
{
bool result = FALSE;
if (wlc_phy_txpwr_cache_get_entry(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(chanspec_t chanspec)
{
chanspec_t chan = 0;
tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_diff_entry(chanspec);
if (entryptr != NULL) {
chan = entryptr->chanspec;
}
return chan;
}
/* Find a specific cache entry and clear it. */
void wlc_phy_txpwr_cache_clear(osl_t *osh, chanspec_t chanspec)
{
tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec);
if (entryptr != NULL) {
wlc_phy_txpwr_cache_clear_entry(osh, entryptr);
}
}
/* Invalidate all cached data. */
void wlc_phy_txpwr_cache_invalidate(void)
{
uint j;
for (j = 0; j < MAX_TXPWR_CACHE_ENTRIES; j++) {
tx_pwr_cache_entry_t* entryptr = &tx_pwr_cache[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_target_pwr_min = TXPWR_CACHE_TXPWR_MAX;
}
}
}
/* Clear all cache entries. */
void wlc_phy_txpwr_cache_close(osl_t *osh)
{
uint i;
for (i = 0; i < MAX_TXPWR_CACHE_ENTRIES; i++) {
tx_pwr_cache_entry_t* entryptr = &tx_pwr_cache[i];
if (entryptr->chanspec != 0) {
wlc_phy_txpwr_cache_clear_entry(osh, entryptr);
}
}
}
/* Find an empty cache entry and initialise it. */
int wlc_phy_txpwr_setup_entry(chanspec_t chanspec)
{
int result = BCME_NOTFOUND;
/* find an empty entry */
tx_pwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(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_target_pwr_min = TXPWR_CACHE_TXPWR_MAX;
entryptr->data_invalid_flags |= TXPWR_STF_TARGET_PWR_NOT_CACHED;
result = BCME_OK;
}
return result;
}
/* Drop any reference to a particular ppr_t struct from the cache. */
void wlc_phy_uncache_pwr(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[i].cache_pwrs[pwr_type] == pwrptr) {
tx_pwr_cache[i].cache_pwrs[pwr_type] = NULL;
}
}
}
}
bool wlc_phy_get_stf_ppr_cached(chanspec_t chanspec)
{
bool ret = FALSE;
tx_pwr_cache_entry_t *entryptr = wlc_phy_txpwr_cache_get_entry(chanspec);
if (entryptr != NULL)
ret = !(entryptr->data_invalid_flags & TXPWR_STF_TARGET_PWR_NOT_CACHED);
return ret;
}
void wlc_phy_set_stf_ppr_cached(chanspec_t chanspec, bool bcached)
{
tx_pwr_cache_entry_t *entryptr = wlc_phy_txpwr_cache_get_entry(chanspec);
if (entryptr != NULL) {
if (bcached)
entryptr->data_invalid_flags &= ~TXPWR_STF_TARGET_PWR_NOT_CACHED;
else
entryptr->data_invalid_flags |= TXPWR_STF_TARGET_PWR_NOT_CACHED;
}
}
/* Get the cached minimum Tx power */
int wlc_phy_get_cached_stf_pwr_min_dbm(void)
{
return stf_tx_pwr_min;
}
/* set the cached minimum Tx power */
void wlc_phy_set_cached_stf_pwr_min_dbm(int min_pwr)
{
stf_tx_pwr_min = min_pwr;
}
#endif /* WLTXPWR_CACHE */