1/* 2 * PHY module Power-per-rate API. Provides interface functions and definitions for 3 * ppr structure for use containing regulatory and board limits and tx power targets. 4 * 5 * Copyright (C) 2015, Broadcom Corporation 6 * All Rights Reserved. 7 * 8 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; 9 * the contents of this file may not be disclosed to third parties, copied 10 * or duplicated in any form, in whole or in part, without the prior 11 * written permission of Broadcom Corporation. 12 * 13 * $Id: $ 14 */ 15 16#if defined(__NetBSD__) || defined(__FreeBSD__) 17#if defined(_KERNEL) 18#include <wlc_cfg.h> 19#endif /* defined(_KERNEL) */ 20#endif /* defined(__NetBSD__) || defined(__FreeBSD__) */ 21 22#include <typedefs.h> 23#include <bcmendian.h> 24#include <bcmwifi_channels.h> 25#include <wlc_ppr.h> 26 27#ifndef BCMDRIVER 28 29#ifndef WL_BEAMFORMING 30#define WL_BEAMFORMING /* enable TxBF definitions for utility code */ 31#endif 32 33#ifndef bcopy 34#include <string.h> 35#include <stdlib.h> 36#define bcopy(src, dst, len) memcpy((dst), (src), (len)) 37#endif 38 39#ifndef ASSERT 40#define ASSERT(exp) do {} while (0) 41#endif 42#endif /* BCMDRIVER */ 43 44/* ppr local TXBF_ENAB() macro because wlc->pub struct is not accessible */ 45#ifdef WL_BEAMFORMING 46#if defined(WLTXBF_DISABLED) 47#define PPR_TXBF_ENAB() (0) 48#else 49#define PPR_TXBF_ENAB() (1) 50#endif 51#else 52#define PPR_TXBF_ENAB() (0) 53#endif /* WL_BEAMFORMING */ 54 55/* This marks the start of a packed structure section. */ 56#include <packed_section_start.h> 57 58#define PPR_SERIALIZATION_VER 2 59 60/* ppr deserialization header */ 61typedef BWL_PRE_PACKED_STRUCT struct ppr_deser_header { 62 uint8 version; 63 uint8 bw; 64 uint16 per_band_size; 65 uint32 flags; 66 uint16 chain3size; /* ppr data size of 3 Tx chains, needed in deserialisation process */ 67} BWL_POST_PACKED_STRUCT ppr_deser_header_t; 68 69 70typedef BWL_PRE_PACKED_STRUCT struct ppr_ser_mem_flag { 71 uint32 magic_word; 72 uint32 flag; 73} BWL_POST_PACKED_STRUCT ppr_ser_mem_flag_t; 74 75 76#define WLC_TXPWR_DB_FACTOR 4 /* conversion for phy txpwr cacluations that use .25 dB units */ 77 78 79/* QDB() macro takes a dB value and converts to a quarter dB value */ 80#ifdef QDB 81#undef QDB 82#endif 83#define QDB(n) ((n) * WLC_TXPWR_DB_FACTOR) 84 85 86/* Flag bits in serialization/deserialization */ 87#define PPR_MAX_TX_CHAIN_MASK 0x00000003 /* mask of Tx chains */ 88#define PPR_BEAMFORMING 0x00000004 /* bit indicates BF is on */ 89#define PPR_SER_MEM_WORD 0xBEEFC0FF /* magic word indicates serialization start */ 90 91 92/* size of serialization header */ 93#define SER_HDR_LEN sizeof(ppr_deser_header_t) 94 95 96/* Per band tx powers */ 97typedef BWL_PRE_PACKED_STRUCT struct pprpb { 98 /* start of 20MHz tx power limits */ 99 int8 p_1x1dsss[WL_RATESET_SZ_DSSS]; /* Legacy CCK/DSSS */ 100 int8 p_1x1ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM transmission */ 101 int8 p_1x1vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 1x1mcs0 */ 102#if (PPR_MAX_TX_CHAINS > 1) 103 int8 p_1x2dsss[WL_RATESET_SZ_DSSS]; /* Legacy CCK/DSSS */ 104 int8 p_1x2cdd_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM CDD transmission */ 105 int8 p_1x2cdd_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 1x2cdd_mcs0 */ 106 int8 p_2x2stbc_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x2stbc_mcs0 */ 107 int8 p_2x2vhtss2[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x2sdm_mcs8 */ 108#if (PPR_MAX_TX_CHAINS > 2) 109 int8 p_1x3dsss[WL_RATESET_SZ_DSSS]; /* Legacy CCK/DSSS */ 110 int8 p_1x3cdd_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM CDD transmission */ 111 int8 p_1x3cdd_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 1x3cdd_mcs0 */ 112 int8 p_2x3stbc_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x3stbc_mcs0 */ 113 int8 p_2x3vhtss2[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x3sdm_mcs8 spexp1 */ 114 int8 p_3x3vhtss3[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 3x3sdm_mcs16 */ 115#endif 116 117#ifdef WL_BEAMFORMING 118 int8 p_1x2txbf_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM TXBF transmission */ 119 int8 p_1x2txbf_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 1x2txbf_mcs0 */ 120 int8 p_2x2txbf_vhtss2[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x2txbf_mcs8 */ 121#if (PPR_MAX_TX_CHAINS > 2) 122 int8 p_1x3txbf_ofdm[WL_RATESET_SZ_OFDM]; /* 20 MHz Legacy OFDM TXBF transmission */ 123 int8 p_1x3txbf_vhtss1[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 1x3txbf_mcs0 */ 124 int8 p_2x3txbf_vhtss2[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 2x3txbf_mcs8 */ 125 int8 p_3x3txbf_vhtss3[WL_RATESET_SZ_VHT_MCS]; /* 8HT/10VHT pwrs starting at 3x3txbf_mcs16 */ 126#endif 127#endif /* WL_BEAMFORMING */ 128#endif /* PPR_MAX_TX_CHAINS > 1 */ 129} BWL_POST_PACKED_STRUCT pprpbw_t; 130 131 132#define PPR_CHAIN1_FIRST OFFSETOF(pprpbw_t, p_1x1dsss) 133#define PPR_CHAIN1_END (OFFSETOF(pprpbw_t, p_1x1vhtss1) + sizeof(((pprpbw_t *)0)->p_1x1vhtss1)) 134#define PPR_CHAIN1_SIZE PPR_CHAIN1_END 135#if (PPR_MAX_TX_CHAINS > 1) 136#define PPR_CHAIN2_FIRST OFFSETOF(pprpbw_t, p_1x2dsss) 137#define PPR_CHAIN2_END (OFFSETOF(pprpbw_t, p_2x2vhtss2) + sizeof(((pprpbw_t *)0)->p_2x2vhtss2)) 138#define PPR_CHAIN2_SIZE (PPR_CHAIN2_END - PPR_CHAIN2_FIRST) 139#if (PPR_MAX_TX_CHAINS > 2) 140#define PPR_CHAIN3_FIRST OFFSETOF(pprpbw_t, p_1x3dsss) 141#define PPR_CHAIN3_END (OFFSETOF(pprpbw_t, p_3x3vhtss3) + sizeof(((pprpbw_t *)0)->p_3x3vhtss3)) 142#define PPR_CHAIN3_SIZE (PPR_CHAIN3_END - PPR_CHAIN3_FIRST) 143#endif 144 145#ifdef WL_BEAMFORMING 146#define PPR_BF_CHAIN2_FIRST OFFSETOF(pprpbw_t, p_1x2txbf_ofdm) 147#define PPR_BF_CHAIN2_END (OFFSETOF(pprpbw_t, p_2x2txbf_vhtss2) + \ 148 sizeof(((pprpbw_t *)0)->p_2x2txbf_vhtss2)) 149#define PPR_BF_CHAIN2_SIZE (PPR_BF_CHAIN2_END - PPR_BF_CHAIN2_FIRST) 150#if (PPR_MAX_TX_CHAINS > 2) 151#define PPR_BF_CHAIN3_FIRST OFFSETOF(pprpbw_t, p_1x3txbf_ofdm) 152#define PPR_BF_CHAIN3_END (OFFSETOF(pprpbw_t, p_3x3txbf_vhtss3) + \ 153 sizeof(((pprpbw_t *)0)->p_3x3txbf_vhtss3)) 154#define PPR_BF_CHAIN3_SIZE (PPR_BF_CHAIN3_END - PPR_BF_CHAIN3_FIRST) 155#endif 156 157#endif /* WL_BEAMFORMING */ 158#endif /* PPR_MAX_TX_CHAINS > 1 */ 159 160 161#define PPR_BW_MAX WL_TX_BW_80 /* Maximum supported bandwidth */ 162 163/* Structure to contain ppr values for a 20MHz channel */ 164typedef BWL_PRE_PACKED_STRUCT struct ppr_bw_20 { 165 /* 20MHz tx power limits */ 166 pprpbw_t b20; 167} BWL_POST_PACKED_STRUCT ppr_bw_20_t; 168 169 170/* Structure to contain ppr values for a 40MHz channel */ 171typedef BWL_PRE_PACKED_STRUCT struct ppr_bw_40 { 172 /* 40MHz tx power limits */ 173 pprpbw_t b40; 174 /* 20in40MHz tx power limits */ 175 pprpbw_t b20in40; 176} BWL_POST_PACKED_STRUCT ppr_bw_40_t; 177 178 179/* Structure to contain ppr values for an 80MHz channel */ 180typedef BWL_PRE_PACKED_STRUCT struct ppr_bw_80 { 181 /* 80MHz tx power limits */ 182 pprpbw_t b80; 183 /* 20in80MHz tx power limits */ 184 pprpbw_t b20in80; 185 /* 40in80MHz tx power limits */ 186 pprpbw_t b40in80; 187} BWL_POST_PACKED_STRUCT ppr_bw_80_t; 188 189 190/* 191 * This is the initial implementation of the structure we're hiding. It is sized to contain only 192 * the set of powers it requires, so the union is not necessarily the size of the largest member. 193 */ 194 195BWL_PRE_PACKED_STRUCT struct ppr { 196 wl_tx_bw_t ch_bw; 197 198 BWL_PRE_PACKED_STRUCT union { 199 ppr_bw_20_t ch20; 200 ppr_bw_40_t ch40; 201 ppr_bw_80_t ch80; 202 } ppr_bw; 203} BWL_POST_PACKED_STRUCT; 204 205/* This marks the end of a packed structure section. */ 206#include <packed_section_end.h> 207 208 209/* Returns a flag of ppr conditions (chains, txbf etc.) */ 210static uint32 ppr_get_flag(void) 211{ 212 uint32 flag = 0; 213 flag |= PPR_MAX_TX_CHAINS & PPR_MAX_TX_CHAIN_MASK; 214#if PPR_MAX_TX_CHAINS > 1 215 if (PPR_TXBF_ENAB()) { 216 flag |= PPR_BEAMFORMING; 217 } 218#endif 219 return flag; 220} 221 222static uint16 ppr_ser_size_per_band(uint32 flags) 223{ 224 uint16 ret = PPR_CHAIN1_SIZE; /* at least 1 chain rates should be there */ 225 uint8 chain = flags & PPR_MAX_TX_CHAIN_MASK; 226 bool bf = (flags & PPR_BEAMFORMING) != 0; 227 BCM_REFERENCE(chain); 228 BCM_REFERENCE(bf); 229#if (PPR_MAX_TX_CHAINS > 1) 230 if (chain > 1) { 231 ret += PPR_CHAIN2_SIZE; 232 } 233#if (PPR_MAX_TX_CHAINS > 2) 234 if (chain > 2) { 235 ret += PPR_CHAIN3_SIZE; 236 } 237#endif 238 239#ifdef WL_BEAMFORMING 240 if (bf) { 241 ret += PPR_BF_CHAIN2_SIZE; 242 } 243#if (PPR_MAX_TX_CHAINS > 2) 244 if (bf && chain > 2) { 245 ret += PPR_BF_CHAIN3_SIZE; 246 } 247#endif 248#endif /* WL_BEAMFORMING */ 249#endif /* PPR_MAX_TX_CHAINS > 1 */ 250 return ret; 251} 252 253/* Return the required serialization size based on the flag field. */ 254static uint ppr_ser_size_by_flag(uint32 flag, wl_tx_bw_t bw) 255{ 256 uint ret = ppr_ser_size_per_band(flag); 257 switch (bw) { 258 case WL_TX_BW_20: 259 break; 260 case WL_TX_BW_40: 261 ret *= sizeof(ppr_bw_40_t)/sizeof(pprpbw_t); 262 break; 263 case WL_TX_BW_80: 264 ret *= sizeof(ppr_bw_80_t)/sizeof(pprpbw_t); 265 break; 266 default: 267 ASSERT(0); 268 } 269 return ret; 270} 271 272#define COPY_PPR_TOBUF(x, y) do { bcopy(&pprbuf[x], *buf, y); \ 273 *buf += y; ret += y; } while (0); 274 275 276/* Serialize ppr data of a bandwidth into the given buffer */ 277static uint ppr_serialize_block(const uint8* pprbuf, uint8** buf, uint32 serflag) 278{ 279 uint ret = 0; 280#if (PPR_MAX_TX_CHAINS > 1) 281 uint chain = serflag & PPR_MAX_TX_CHAIN_MASK; /* chain number in serialized block */ 282 bool bf = (serflag & PPR_BEAMFORMING) != 0; 283#endif 284 285 COPY_PPR_TOBUF(PPR_CHAIN1_FIRST, PPR_CHAIN1_SIZE); 286#if (PPR_MAX_TX_CHAINS > 1) 287 BCM_REFERENCE(bf); 288 if (chain > 1) { 289 COPY_PPR_TOBUF(PPR_CHAIN2_FIRST, PPR_CHAIN2_SIZE); 290 } 291#if (PPR_MAX_TX_CHAINS > 2) 292 if (chain > 2) { 293 COPY_PPR_TOBUF(PPR_CHAIN3_FIRST, PPR_CHAIN3_SIZE); 294 } 295#endif 296#ifdef WL_BEAMFORMING 297 if (PPR_TXBF_ENAB() && bf) { 298 COPY_PPR_TOBUF(PPR_BF_CHAIN2_FIRST, PPR_BF_CHAIN2_SIZE); 299 } 300#if (PPR_MAX_TX_CHAINS > 2) 301 if (PPR_TXBF_ENAB() && bf && chain > 2) { 302 COPY_PPR_TOBUF(PPR_BF_CHAIN3_FIRST, PPR_BF_CHAIN3_SIZE); 303 } 304#endif 305#endif /* WL_BEAMFORMING */ 306#endif /* (PPR_MAX_TX_CHAINS > 1) */ 307 return ret; 308} 309 310 311/* Serialize ppr data of each bandwidth into the given buffer, returns bytes copied */ 312static uint ppr_serialize_data(const ppr_t *pprptr, uint8* buf, uint32 serflag) 313{ 314 uint ret = sizeof(ppr_deser_header_t); 315 ppr_deser_header_t* header = (ppr_deser_header_t*)buf; 316 ASSERT(pprptr && buf); 317 header->version = PPR_SERIALIZATION_VER; 318 header->bw = (uint8)pprptr->ch_bw; 319 header->flags = HTON32(ppr_get_flag()); 320 header->per_band_size = HTON16(ppr_ser_size_per_band(serflag)); 321#if (PPR_MAX_TX_CHAINS > 2) 322 header->chain3size = HTON16(PPR_CHAIN3_SIZE); 323#else 324 header->chain3size = 0; 325#endif 326 327 buf += sizeof(*header); 328 switch (header->bw) { 329 case WL_TX_BW_20: 330 { 331 const uint8* pprbuf = (const uint8*)&pprptr->ppr_bw.ch20.b20; 332 ret += ppr_serialize_block(pprbuf, &buf, serflag); 333 } 334 break; 335 case WL_TX_BW_40: 336 { 337 const uint8* pprbuf = (const uint8*)&pprptr->ppr_bw.ch40.b40; 338 ret += ppr_serialize_block(pprbuf, &buf, serflag); 339 pprbuf = (const uint8*)&pprptr->ppr_bw.ch40.b20in40; 340 ret += ppr_serialize_block(pprbuf, &buf, serflag); 341 } 342 break; 343 case WL_TX_BW_80: 344 { 345 const uint8* pprbuf = (const uint8*)&pprptr->ppr_bw.ch80.b80; 346 ret += ppr_serialize_block(pprbuf, &buf, serflag); 347 pprbuf = (const uint8*)&pprptr->ppr_bw.ch80.b20in80; 348 ret += ppr_serialize_block(pprbuf, &buf, serflag); 349 pprbuf = (const uint8*)&pprptr->ppr_bw.ch80.b40in80; 350 ret += ppr_serialize_block(pprbuf, &buf, serflag); 351 } 352 break; 353 default: 354 ASSERT(0); 355 } 356 return ret; 357} 358 359 360/* Copy serialized ppr data of a bandwidth */ 361static void 362ppr_copy_serdata(uint8* pobuf, const uint8** inbuf, uint32 flag, uint16 per_band_size, 363 uint16 chain3size) 364{ 365 uint chain = flag & PPR_MAX_TX_CHAIN_MASK; 366 bool bf = (flag & PPR_BEAMFORMING) != 0; 367 uint16 len = PPR_CHAIN1_SIZE; 368 BCM_REFERENCE(chain); 369 BCM_REFERENCE(bf); 370 BCM_REFERENCE(chain3size); 371 bcopy(*inbuf, pobuf, PPR_CHAIN1_SIZE); 372 *inbuf += PPR_CHAIN1_SIZE; 373#if (PPR_MAX_TX_CHAINS > 1) 374 if (chain > 1) { 375 bcopy(*inbuf, &pobuf[PPR_CHAIN2_FIRST], PPR_CHAIN2_SIZE); 376 *inbuf += PPR_CHAIN2_SIZE; 377 len += PPR_CHAIN2_SIZE; 378 } 379#if (PPR_MAX_TX_CHAINS > 2) 380 if (chain > 2) { 381 bcopy(*inbuf, &pobuf[PPR_CHAIN3_FIRST], PPR_CHAIN3_SIZE); 382 *inbuf += PPR_CHAIN3_SIZE; 383 len += PPR_CHAIN3_SIZE; 384 } 385#else 386 if (chain > 2) { 387 *inbuf += chain3size; 388 len += chain3size; 389 } 390#endif 391 392#ifdef WL_BEAMFORMING 393 if (PPR_TXBF_ENAB() && bf) { 394 bcopy(*inbuf, &pobuf[PPR_BF_CHAIN2_FIRST], PPR_BF_CHAIN2_SIZE); 395 *inbuf += PPR_BF_CHAIN2_SIZE; 396 len += PPR_BF_CHAIN2_SIZE; 397 } 398#if (PPR_MAX_TX_CHAINS > 2) 399 if (PPR_TXBF_ENAB() && bf && chain > 2) { 400 bcopy(*inbuf, &pobuf[PPR_BF_CHAIN3_FIRST], PPR_BF_CHAIN3_SIZE); 401 *inbuf += PPR_BF_CHAIN3_SIZE; 402 len += PPR_BF_CHAIN3_SIZE; 403 } 404#endif 405#endif /* WL_BEAMFORMING */ 406#endif /* (PPR_MAX_TX_CHAINS > 1) */ 407 if (len < per_band_size) { 408 *inbuf += (per_band_size - len); 409 } 410} 411 412 413/* Deserialize data into a ppr_t structure */ 414static void 415ppr_deser_cpy(ppr_t* pptr, const uint8* inbuf, uint32 flag, wl_tx_bw_t bw, uint16 per_band_size, 416 uint16 chain3size) 417{ 418 pptr->ch_bw = bw; 419 switch (bw) { 420 case WL_TX_BW_20: 421 { 422 uint8* pobuf = (uint8*)&pptr->ppr_bw.ch20; 423 ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size, chain3size); 424 } 425 break; 426 case WL_TX_BW_40: 427 { 428 uint8* pobuf = (uint8*)&pptr->ppr_bw.ch40.b40; 429 ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size, chain3size); 430 pobuf = (uint8*)&pptr->ppr_bw.ch40.b20in40; 431 ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size, chain3size); 432 } 433 break; 434 case WL_TX_BW_80: 435 { 436 uint8* pobuf = (uint8*)&pptr->ppr_bw.ch80.b80; 437 ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size, chain3size); 438 pobuf = (uint8*)&pptr->ppr_bw.ch80.b20in80; 439 ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size, chain3size); 440 pobuf = (uint8*)&pptr->ppr_bw.ch80.b40in80; 441 ppr_copy_serdata(pobuf, &inbuf, flag, per_band_size, chain3size); 442 } 443 break; 444 default: 445 ASSERT(0); 446 } 447} 448 449 450/* Get a pointer to the power values for a given channel bandwidth */ 451static pprpbw_t* ppr_get_bw_powers_20(ppr_t* p, wl_tx_bw_t bw) 452{ 453 pprpbw_t* pwrs = NULL; 454 455 if (bw == WL_TX_BW_20) 456 pwrs = &p->ppr_bw.ch20.b20; 457 /* else */ 458 /* ASSERT(0); */ 459 return pwrs; 460} 461 462 463/* Get a pointer to the power values for a given channel bandwidth */ 464static pprpbw_t* ppr_get_bw_powers_40(ppr_t* p, wl_tx_bw_t bw) 465{ 466 pprpbw_t* pwrs = NULL; 467 468 switch (bw) { 469 case WL_TX_BW_40: 470 pwrs = &p->ppr_bw.ch40.b40; 471 break; 472 case WL_TX_BW_20: 473 474 case WL_TX_BW_20IN40: 475 pwrs = &p->ppr_bw.ch40.b20in40; 476 break; 477 default: 478 /* ASSERT(0); */ 479 break; 480 } 481 return pwrs; 482} 483 484 485/* Get a pointer to the power values for a given channel bandwidth */ 486static pprpbw_t* ppr_get_bw_powers_80(ppr_t* p, wl_tx_bw_t bw) 487{ 488 pprpbw_t* pwrs = NULL; 489 490 switch (bw) { 491 case WL_TX_BW_80: 492 pwrs = &p->ppr_bw.ch80.b80; 493 break; 494 case WL_TX_BW_20: 495 case WL_TX_BW_20IN40: 496 case WL_TX_BW_20IN80: 497 pwrs = &p->ppr_bw.ch80.b20in80; 498 break; 499 case WL_TX_BW_40: 500 case WL_TX_BW_40IN80: 501 pwrs = &p->ppr_bw.ch80.b40in80; 502 break; 503 default: 504 /* ASSERT(0); */ 505 break; 506 } 507 return pwrs; 508} 509 510 511typedef pprpbw_t* (*wlc_ppr_get_bw_pwrs_fn_t)(ppr_t* p, wl_tx_bw_t bw); 512 513typedef struct { 514 wl_tx_bw_t ch_bw; /* Bandwidth of the channel for which powers are stored */ 515 /* Function to retrieve the powers for the requested bandwidth */ 516 wlc_ppr_get_bw_pwrs_fn_t fn; 517} wlc_ppr_get_bw_pwrs_pair_t; 518 519 520static const wlc_ppr_get_bw_pwrs_pair_t ppr_get_bw_pwrs_fn[] = { 521 {WL_TX_BW_20, ppr_get_bw_powers_20}, 522 {WL_TX_BW_40, ppr_get_bw_powers_40}, 523 {WL_TX_BW_80, ppr_get_bw_powers_80} 524}; 525 526 527/* Get a pointer to the power values for a given channel bandwidth */ 528static pprpbw_t* ppr_get_bw_powers(ppr_t* p, wl_tx_bw_t bw) 529{ 530 uint32 i; 531 532 if (p == NULL) { 533 return NULL; 534 } 535 536 for (i = 0; i < (int)ARRAYSIZE(ppr_get_bw_pwrs_fn); i++) { 537 if (ppr_get_bw_pwrs_fn[i].ch_bw == p->ch_bw) 538 return ppr_get_bw_pwrs_fn[i].fn(p, bw); 539 } 540 541 ASSERT(0); 542 return NULL; 543} 544 545 546/* 547 * Rate group power finder functions: ppr_get_xxx_group() 548 * To preserve the opacity of the PPR struct, even inside the API we try to limit knowledge of 549 * its details. Almost all API functions work on the powers for individual rate groups, rather than 550 * directly accessing the struct. Once the section of the structure corresponding to the bandwidth 551 * has been identified using ppr_get_bw_powers(), the ppr_get_xxx_group() functions use knowledge 552 * of the number of spatial streams, the number of tx chains, and the expansion mode to return a 553 * pointer to the required group of power values. 554 */ 555 556/* Get a pointer to the power values for the given dsss rate group for a given channel bandwidth */ 557static int8* ppr_get_dsss_group(pprpbw_t* bw_pwrs, wl_tx_chains_t tx_chains) 558{ 559 int8* group_pwrs = NULL; 560 561 switch (tx_chains) { 562#if (PPR_MAX_TX_CHAINS > 1) 563#if (PPR_MAX_TX_CHAINS > 2) 564 case WL_TX_CHAINS_3: 565 group_pwrs = bw_pwrs->p_1x3dsss; 566 break; 567#endif 568 case WL_TX_CHAINS_2: 569 group_pwrs = bw_pwrs->p_1x2dsss; 570 break; 571#endif /* PPR_MAX_TX_CHAINS > 1 */ 572 case WL_TX_CHAINS_1: 573 group_pwrs = bw_pwrs->p_1x1dsss; 574 break; 575 default: 576 ASSERT(0); 577 break; 578 } 579 return group_pwrs; 580} 581 582 583/* Get a pointer to the power values for the given ofdm rate group for a given channel bandwidth */ 584static int8* ppr_get_ofdm_group(pprpbw_t* bw_pwrs, wl_tx_mode_t mode, 585 wl_tx_chains_t tx_chains) 586{ 587 int8* group_pwrs = NULL; 588 BCM_REFERENCE(mode); 589 switch (tx_chains) { 590#if (PPR_MAX_TX_CHAINS > 1) 591#if (PPR_MAX_TX_CHAINS > 2) 592 case WL_TX_CHAINS_3: 593#ifdef WL_BEAMFORMING 594 if (mode == WL_TX_MODE_TXBF) 595 group_pwrs = bw_pwrs->p_1x3txbf_ofdm; 596 else 597#endif 598 group_pwrs = bw_pwrs->p_1x3cdd_ofdm; 599 break; 600#endif /* PPR_MAX_TX_CHAINS > 2 */ 601 case WL_TX_CHAINS_2: 602#ifdef WL_BEAMFORMING 603 if (mode == WL_TX_MODE_TXBF) 604 group_pwrs = bw_pwrs->p_1x2txbf_ofdm; 605 else 606#endif 607 group_pwrs = bw_pwrs->p_1x2cdd_ofdm; 608 break; 609#endif /* PPR_MAX_TX_CHAINS > 1 */ 610 case WL_TX_CHAINS_1: 611 group_pwrs = bw_pwrs->p_1x1ofdm; 612 break; 613 default: 614 ASSERT(0); 615 break; 616 } 617 return group_pwrs; 618} 619 620 621/* 622 * Tables to provide access to HT/VHT rate group powers. This avoids an ugly nested switch with 623 * messy conditional compilation. 624 * 625 * Access to a given table entry is via table[chains - Nss][mode], except for the Nss3 table, which 626 * only has one row, so it can be indexed directly by table[mode]. 627 * 628 * Separate tables are provided for each of Nss1, Nss2 and Nss3 because they are all different 629 * sizes. A combined table would be very sparse, and this arrangement also simplifies the 630 * conditional compilation. 631 * 632 * Each row represents a given number of chains, so there's no need for a zero row. Because 633 * chains >= Nss is always true, there is no one-chain row for Nss2 and there are no one- or 634 * two-chain rows for Nss3. With the tables correctly sized, we can index the rows 635 * using [chains - Nss]. 636 * 637 * Then, inside each row, we index by mode: 638 * WL_TX_MODE_NONE, WL_TX_MODE_STBC, WL_TX_MODE_CDD, WL_TX_MODE_TXBF. 639 */ 640 641#define OFFSNONE (-1) 642 643static const int mcs_groups_nss1[PPR_MAX_TX_CHAINS][WL_NUM_TX_MODES] = { 644 /* WL_TX_MODE_NONE 645 WL_TX_MODE_STBC 646 WL_TX_MODE_CDD 647 WL_TX_MODE_TXBF 648 */ 649 /* 1 chain */ 650 {OFFSETOF(pprpbw_t, p_1x1vhtss1), 651 OFFSNONE, 652 OFFSNONE, 653 OFFSNONE}, 654#if (PPR_MAX_TX_CHAINS > 1) 655 /* 2 chain */ 656 {OFFSNONE, 657 OFFSNONE, 658 OFFSETOF(pprpbw_t, p_1x2cdd_vhtss1), 659 OFFSNONE}, 660#if (PPR_MAX_TX_CHAINS > 2) 661 /* 3 chain */ 662 {OFFSNONE, 663 OFFSNONE, 664 OFFSETOF(pprpbw_t, p_1x3cdd_vhtss1), 665 OFFSNONE} 666#endif 667#endif /* PPR_MAX_TX_CHAINS > 1 */ 668}; 669 670#ifdef WL_BEAMFORMING 671/* mcs group with TXBF data */ 672static const int mcs_groups_nss1_txbf[PPR_MAX_TX_CHAINS][WL_NUM_TX_MODES] = { 673 /* WL_TX_MODE_NONE 674 WL_TX_MODE_STBC 675 WL_TX_MODE_CDD 676 WL_TX_MODE_TXBF 677 */ 678 /* 1 chain */ 679 {OFFSETOF(pprpbw_t, p_1x1vhtss1), 680 OFFSNONE, 681 OFFSNONE, 682 OFFSNONE}, 683#if (PPR_MAX_TX_CHAINS > 1) 684 /* 2 chain */ 685 {OFFSNONE, 686 OFFSNONE, 687 OFFSETOF(pprpbw_t, p_1x2cdd_vhtss1), 688 OFFSETOF(pprpbw_t, p_1x2txbf_vhtss1)}, 689#if (PPR_MAX_TX_CHAINS > 2) 690 /* 3 chain */ 691 {OFFSNONE, 692 OFFSNONE, 693 OFFSETOF(pprpbw_t, p_1x3cdd_vhtss1), 694 OFFSETOF(pprpbw_t, p_1x3txbf_vhtss1)} 695#endif 696#endif /* PPR_MAX_TX_CHAINS > 1 */ 697}; 698#endif /* WL_BEAMFORMING */ 699 700#if (PPR_MAX_TX_CHAINS > 1) 701static const int mcs_groups_nss2[PPR_MAX_TX_CHAINS - 1][WL_NUM_TX_MODES] = { 702 /* 2 chain */ 703 {OFFSETOF(pprpbw_t, p_2x2vhtss2), 704 OFFSETOF(pprpbw_t, p_2x2stbc_vhtss1), 705 OFFSNONE, 706 OFFSNONE}, 707#if (PPR_MAX_TX_CHAINS > 2) 708 /* 3 chain */ 709 {OFFSETOF(pprpbw_t, p_2x3vhtss2), 710 OFFSETOF(pprpbw_t, p_2x3stbc_vhtss1), 711 OFFSNONE, 712 OFFSNONE} 713#endif 714}; 715 716#ifdef WL_BEAMFORMING 717/* mcs group with TXBF data */ 718static const int mcs_groups_nss2_txbf[PPR_MAX_TX_CHAINS - 1][WL_NUM_TX_MODES] = { 719 /* 2 chain */ 720 {OFFSETOF(pprpbw_t, p_2x2vhtss2), 721 OFFSETOF(pprpbw_t, p_2x2stbc_vhtss1), 722 OFFSNONE, 723 OFFSETOF(pprpbw_t, p_2x2txbf_vhtss2)}, 724#if (PPR_MAX_TX_CHAINS > 2) 725 /* 3 chain */ 726 {OFFSETOF(pprpbw_t, p_2x3vhtss2), 727 OFFSETOF(pprpbw_t, p_2x3stbc_vhtss1), 728 OFFSNONE, 729 OFFSETOF(pprpbw_t, p_2x3txbf_vhtss2)} 730#endif 731}; 732#endif /* WL_BEAMFORMING */ 733 734#if (PPR_MAX_TX_CHAINS > 2) 735static const int mcs_groups_nss3[WL_NUM_TX_MODES] = { 736/* 3 chains only */ 737 OFFSETOF(pprpbw_t, p_3x3vhtss3), 738 OFFSNONE, 739 OFFSNONE, 740 OFFSNONE, 741}; 742 743#ifdef WL_BEAMFORMING 744/* mcs group with TXBF data */ 745static const int mcs_groups_nss3_txbf[WL_NUM_TX_MODES] = { 746/* 3 chains only */ 747 OFFSETOF(pprpbw_t, p_3x3vhtss3), 748 OFFSNONE, 749 OFFSNONE, 750 OFFSETOF(pprpbw_t, p_3x3txbf_vhtss3) 751}; 752#endif /* WL_BEAMFORMING */ 753#endif /* PPR_MAX_TX_CHAINS > 2 */ 754#endif /* PPR_MAX_TX_CHAINS > 1 */ 755 756/* Get a pointer to the power values for the given rate group for a given channel bandwidth */ 757static int8* ppr_get_mcs_group(pprpbw_t* bw_pwrs, wl_tx_nss_t Nss, wl_tx_mode_t mode, 758 wl_tx_chains_t tx_chains) 759{ 760 int8* group_pwrs = NULL; 761 int offset; 762 763 switch (Nss) { 764#if (PPR_MAX_TX_CHAINS > 1) 765#if (PPR_MAX_TX_CHAINS > 2) 766 case WL_TX_NSS_3: 767 if (tx_chains == WL_TX_CHAINS_3) { 768#ifdef WL_BEAMFORMING 769 if (PPR_TXBF_ENAB()) { 770 offset = mcs_groups_nss3_txbf[mode]; 771 } else 772#endif /* WL_BEAMFORMING */ 773 { 774 offset = mcs_groups_nss3[mode]; 775 } 776 if (offset != OFFSNONE) { 777 group_pwrs = (int8*)bw_pwrs + offset; 778 } 779 } 780 else 781 ASSERT(0); 782 break; 783#endif /* PPR_MAX_TX_CHAINS > 2 */ 784 case WL_TX_NSS_2: 785 if ((tx_chains >= WL_TX_CHAINS_2) && (tx_chains <= PPR_MAX_TX_CHAINS)) { 786#ifdef WL_BEAMFORMING 787 if (PPR_TXBF_ENAB()) { 788 offset = mcs_groups_nss2_txbf[tx_chains - Nss][mode]; 789 } else 790#endif /* WL_BEAMFORMING */ 791 { 792 offset = mcs_groups_nss2[tx_chains - Nss][mode]; 793 } 794 if (offset != OFFSNONE) { 795 group_pwrs = (int8*)bw_pwrs + offset; 796 } 797 } 798 else 799 ASSERT(0); 800 break; 801#endif /* PPR_MAX_TX_CHAINS > 1 */ 802 case WL_TX_NSS_1: 803 if (tx_chains <= PPR_MAX_TX_CHAINS) { 804#ifdef WL_BEAMFORMING 805 if (PPR_TXBF_ENAB()) { 806 offset = mcs_groups_nss1_txbf[tx_chains - Nss][mode]; 807 } else 808#endif /* WL_BEAMFORMING */ 809 { 810 offset = mcs_groups_nss1[tx_chains - Nss][mode]; 811 } 812 if (offset != OFFSNONE) { 813 group_pwrs = (int8*)bw_pwrs + offset; 814 } 815 } 816 else 817 ASSERT(0); 818 break; 819 default: 820 ASSERT(0); 821 break; 822 } 823 return group_pwrs; 824} 825 826/* Size routine for user alloc/dealloc */ 827static uint32 ppr_pwrs_size(wl_tx_bw_t bw) 828{ 829 uint32 size; 830 831 switch (bw) { 832 case WL_TX_BW_20: 833 size = sizeof(ppr_bw_20_t); 834 break; 835 case WL_TX_BW_40: 836 size = sizeof(ppr_bw_40_t); 837 break; 838 case WL_TX_BW_80: 839 size = sizeof(ppr_bw_80_t); 840 break; 841 default: 842 ASSERT(0); 843 size = 0; 844 break; 845 } 846 return size; 847} 848 849 850/* Initialization routine */ 851void ppr_init(ppr_t* pprptr, wl_tx_bw_t bw) 852{ 853 memset(pprptr, (int8)WL_RATE_DISABLED, ppr_size(bw)); 854 pprptr->ch_bw = bw; 855} 856 857 858/* Reinitialization routine for opaque PPR struct */ 859void ppr_clear(ppr_t* pprptr) 860{ 861 memset((uchar*)&pprptr->ppr_bw, (int8)WL_RATE_DISABLED, ppr_pwrs_size(pprptr->ch_bw)); 862} 863 864 865/* Size routine for user alloc/dealloc */ 866uint32 ppr_size(wl_tx_bw_t bw) 867{ 868 return ppr_pwrs_size(bw) + sizeof(wl_tx_bw_t); 869} 870 871 872/* Size routine for user serialization alloc */ 873uint32 ppr_ser_size(const ppr_t* pprptr) 874{ 875 return ppr_pwrs_size(pprptr->ch_bw) + SER_HDR_LEN; /* struct size plus headers */ 876} 877 878 879/* Size routine for user serialization alloc */ 880uint32 ppr_ser_size_by_bw(wl_tx_bw_t bw) 881{ 882 return ppr_pwrs_size(bw) + SER_HDR_LEN; /* struct size plus headers */ 883} 884 885 886/* Constructor routine for opaque PPR struct */ 887ppr_t* ppr_create(osl_t *osh, wl_tx_bw_t bw) 888{ 889 ppr_t* pprptr; 890 891 ASSERT((bw == WL_TX_BW_20) || (bw == WL_TX_BW_40) || (bw == WL_TX_BW_80)); 892#ifndef BCMDRIVER 893 BCM_REFERENCE(osh); 894 if ((pprptr = (ppr_t*)malloc((uint)ppr_size(bw))) != NULL) { 895#else 896 if ((pprptr = (ppr_t*)MALLOC(osh, (uint)ppr_size(bw))) != NULL) { 897#endif 898 ppr_init(pprptr, bw); 899 } 900 return pprptr; 901} 902 903 904/* Init flags in the memory block for serialization, the serializer will check 905 * the flag to decide which ppr to be copied 906 */ 907int ppr_init_ser_mem_by_bw(uint8* pbuf, wl_tx_bw_t bw, uint32 len) 908{ 909 ppr_ser_mem_flag_t *pmflag; 910 911 if (pbuf == NULL || ppr_ser_size_by_bw(bw) > len) 912 return BCME_BADARG; 913 914 pmflag = (ppr_ser_mem_flag_t *)pbuf; 915 pmflag->magic_word = HTON32(PPR_SER_MEM_WORD); 916 pmflag->flag = HTON32(ppr_get_flag()); 917 918 /* init the memory */ 919 memset(pbuf + sizeof(*pmflag), (uint8)WL_RATE_DISABLED, len-sizeof(*pmflag)); 920 return BCME_OK; 921} 922 923 924int ppr_init_ser_mem(uint8* pbuf, ppr_t * ppr, uint32 len) 925{ 926 return ppr_init_ser_mem_by_bw(pbuf, ppr->ch_bw, len); 927} 928 929 930/* Destructor routine for opaque PPR struct */ 931void ppr_delete(osl_t *osh, ppr_t* pprptr) 932{ 933 ASSERT((pprptr->ch_bw == WL_TX_BW_20) || (pprptr->ch_bw == WL_TX_BW_40) || 934 (pprptr->ch_bw == WL_TX_BW_80)); 935#ifndef BCMDRIVER 936 BCM_REFERENCE(osh); 937 free(pprptr); 938#else 939 MFREE(osh, pprptr, (uint)ppr_size(pprptr->ch_bw)); 940#endif 941} 942 943 944/* Type routine for inferring opaque structure size */ 945wl_tx_bw_t ppr_get_ch_bw(const ppr_t* pprptr) 946{ 947 return pprptr->ch_bw; 948} 949 950 951/* Type routine to get ppr supported maximum bw */ 952wl_tx_bw_t ppr_get_max_bw(void) 953{ 954 return PPR_BW_MAX; 955} 956 957 958/* Get the dsss values for the given number of tx_chains and 20, 20in40, etc. */ 959int ppr_get_dsss(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_chains_t tx_chains, 960 ppr_dsss_rateset_t* dsss) 961{ 962 pprpbw_t* bw_pwrs; 963 const int8* powers; 964 int cnt = 0; 965 966 ASSERT(pprptr); 967 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 968 if (bw_pwrs != NULL) { 969 powers = ppr_get_dsss_group(bw_pwrs, tx_chains); 970 if (powers != NULL) { 971 bcopy(powers, dsss->pwr, sizeof(*dsss)); 972 cnt = sizeof(*dsss); 973 } 974 } 975 if (cnt == 0) { 976 memset(dsss->pwr, (int8)WL_RATE_DISABLED, sizeof(*dsss)); 977 } 978 return cnt; 979} 980 981 982/* Get the ofdm values for the given number of tx_chains and 20, 20in40, etc. */ 983int ppr_get_ofdm(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_mode_t mode, wl_tx_chains_t tx_chains, 984 ppr_ofdm_rateset_t* ofdm) 985{ 986 pprpbw_t* bw_pwrs; 987 const int8* powers; 988 int cnt = 0; 989 990 ASSERT(pprptr); 991 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 992 if (bw_pwrs != NULL) { 993 powers = ppr_get_ofdm_group(bw_pwrs, mode, tx_chains); 994 if (powers != NULL) { 995 bcopy(powers, ofdm->pwr, sizeof(*ofdm)); 996 cnt = sizeof(*ofdm); 997 } 998 } 999 if (cnt == 0) { 1000 memset(ofdm->pwr, (int8)WL_RATE_DISABLED, sizeof(*ofdm)); 1001 } 1002 return cnt; 1003} 1004 1005 1006/* Get the HT MCS values for the group specified by Nss, with the given bw and tx chains */ 1007int ppr_get_ht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, 1008 wl_tx_chains_t tx_chains, ppr_ht_mcs_rateset_t* mcs) 1009{ 1010 pprpbw_t* bw_pwrs; 1011 const int8* powers; 1012 int cnt = 0; 1013 1014 ASSERT(pprptr); 1015 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1016 if (bw_pwrs != NULL) { 1017 powers = ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); 1018 if (powers != NULL) { 1019 bcopy(powers, mcs->pwr, sizeof(*mcs)); 1020 cnt = sizeof(*mcs); 1021 } 1022 } 1023 if (cnt == 0) { 1024 memset(mcs->pwr, (int8)WL_RATE_DISABLED, sizeof(*mcs)); 1025 } 1026 1027 return cnt; 1028} 1029 1030 1031/* Get the VHT MCS values for the group specified by Nss, with the given bw and tx chains */ 1032int ppr_get_vht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, 1033 wl_tx_chains_t tx_chains, ppr_vht_mcs_rateset_t* mcs) 1034{ 1035 pprpbw_t* bw_pwrs; 1036 const int8* powers; 1037 int cnt = 0; 1038 1039 ASSERT(pprptr); 1040 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1041 if (bw_pwrs != NULL) { 1042 powers = ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); 1043 if (powers != NULL) { 1044 bcopy(powers, mcs->pwr, sizeof(*mcs)); 1045 cnt = sizeof(*mcs); 1046 } 1047 } 1048 if (cnt == 0) { 1049 memset(mcs->pwr, (int8)WL_RATE_DISABLED, sizeof(*mcs)); 1050 } 1051 return cnt; 1052} 1053 1054 1055/* Routines to set target powers per rate in a group */ 1056 1057/* Set the dsss values for the given number of tx_chains and 20, 20in40, etc. */ 1058int ppr_set_dsss(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_chains_t tx_chains, 1059 const ppr_dsss_rateset_t* dsss) 1060{ 1061 pprpbw_t* bw_pwrs; 1062 int8* powers; 1063 int cnt = 0; 1064 1065 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1066 if (bw_pwrs != NULL) { 1067 powers = (int8*)ppr_get_dsss_group(bw_pwrs, tx_chains); 1068 if (powers != NULL) { 1069 bcopy(dsss->pwr, powers, sizeof(*dsss)); 1070 cnt = sizeof(*dsss); 1071 } 1072 } 1073 return cnt; 1074} 1075 1076 1077/* Set the ofdm values for the given number of tx_chains and 20, 20in40, etc. */ 1078int ppr_set_ofdm(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_mode_t mode, wl_tx_chains_t tx_chains, 1079 const ppr_ofdm_rateset_t* ofdm) 1080{ 1081 pprpbw_t* bw_pwrs; 1082 int8* powers; 1083 int cnt = 0; 1084 1085 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1086 if (bw_pwrs != NULL) { 1087 powers = (int8*)ppr_get_ofdm_group(bw_pwrs, mode, tx_chains); 1088 if (powers != NULL) { 1089 bcopy(ofdm->pwr, powers, sizeof(*ofdm)); 1090 cnt = sizeof(*ofdm); 1091 } 1092 } 1093 return cnt; 1094} 1095 1096 1097/* Set the HT MCS values for the group specified by Nss, with the given bw and tx chains */ 1098int ppr_set_ht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, 1099 wl_tx_chains_t tx_chains, const ppr_ht_mcs_rateset_t* mcs) 1100{ 1101 pprpbw_t* bw_pwrs; 1102 int8* powers; 1103 int cnt = 0; 1104 1105 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1106 if (bw_pwrs != NULL) { 1107 powers = (int8*)ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); 1108 if (powers != NULL) { 1109 bcopy(mcs->pwr, powers, sizeof(*mcs)); 1110 cnt = sizeof(*mcs); 1111 } 1112 } 1113 return cnt; 1114} 1115 1116 1117/* Set the VHT MCS values for the group specified by Nss, with the given bw and tx chains */ 1118int ppr_set_vht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, 1119 wl_tx_chains_t tx_chains, const ppr_vht_mcs_rateset_t* mcs) 1120{ 1121 pprpbw_t* bw_pwrs; 1122 int8* powers; 1123 int cnt = 0; 1124 1125 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1126 if (bw_pwrs != NULL) { 1127 powers = (int8*)ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); 1128 if (powers != NULL) { 1129 bcopy(mcs->pwr, powers, sizeof(*mcs)); 1130 cnt = sizeof(*mcs); 1131 } 1132 } 1133 return cnt; 1134} 1135 1136 1137/* Routines to set rate groups to a single target value */ 1138 1139/* Set the dsss values for the given number of tx_chains and 20, 20in40, etc. */ 1140int ppr_set_same_dsss(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_chains_t tx_chains, const int8 power) 1141{ 1142 pprpbw_t* bw_pwrs; 1143 int8* dest_group; 1144 int cnt = 0; 1145 int i; 1146 1147 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1148 if (bw_pwrs != NULL) { 1149 dest_group = (int8*)ppr_get_dsss_group(bw_pwrs, tx_chains); 1150 if (dest_group != NULL) { 1151 cnt = sizeof(ppr_dsss_rateset_t); 1152 for (i = 0; i < cnt; i++) 1153 *dest_group++ = power; 1154 } 1155 } 1156 return cnt; 1157} 1158 1159 1160/* Set the ofdm values for the given number of tx_chains and 20, 20in40, etc. */ 1161int ppr_set_same_ofdm(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_mode_t mode, wl_tx_chains_t tx_chains, 1162 const int8 power) 1163{ 1164 pprpbw_t* bw_pwrs; 1165 int8* dest_group; 1166 int cnt = 0; 1167 int i; 1168 1169 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1170 if (bw_pwrs != NULL) { 1171 dest_group = (int8*)ppr_get_ofdm_group(bw_pwrs, mode, tx_chains); 1172 if (dest_group != NULL) { 1173 cnt = sizeof(ppr_ofdm_rateset_t); 1174 for (i = 0; i < cnt; i++) 1175 *dest_group++ = power; 1176 } 1177 } 1178 return cnt; 1179} 1180 1181 1182/* Set the HT MCS values for the group specified by Nss, with the given bw and tx chains */ 1183int ppr_set_same_ht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, 1184 wl_tx_chains_t tx_chains, const int8 power) 1185{ 1186 pprpbw_t* bw_pwrs; 1187 int8* dest_group; 1188 int cnt = 0; 1189 int i; 1190 1191 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1192 if (bw_pwrs != NULL) { 1193 dest_group = (int8*)ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); 1194 if (dest_group != NULL) { 1195 cnt = sizeof(ppr_ht_mcs_rateset_t); 1196 for (i = 0; i < cnt; i++) 1197 *dest_group++ = power; 1198 } 1199 } 1200 return cnt; 1201} 1202 1203 1204/* Set the HT MCS values for the group specified by Nss, with the given bw and tx chains */ 1205int ppr_set_same_vht_mcs(ppr_t* pprptr, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, 1206 wl_tx_chains_t tx_chains, const int8 power) 1207{ 1208 pprpbw_t* bw_pwrs; 1209 int8* dest_group; 1210 int cnt = 0; 1211 int i; 1212 1213 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1214 if (bw_pwrs != NULL) { 1215 dest_group = (int8*)ppr_get_mcs_group(bw_pwrs, Nss, mode, tx_chains); 1216 if (dest_group != NULL) { 1217 cnt = sizeof(ppr_vht_mcs_rateset_t); 1218 for (i = 0; i < cnt; i++) 1219 *dest_group++ = power; 1220 } 1221 } 1222 return cnt; 1223} 1224 1225 1226/* Helper routines to operate on the entire ppr set */ 1227 1228/* Ensure no rate limit is greater than the cap */ 1229uint ppr_apply_max(ppr_t* pprptr, int8 maxval) 1230{ 1231 uint i; 1232 int8* rptr = (int8*)&pprptr->ppr_bw; 1233 1234 for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++) { 1235 *rptr = MIN(*rptr, maxval); 1236 } 1237 return i; 1238} 1239 1240#if (PPR_MAX_TX_CHAINS > 1) 1241#define APPLY_CONSTRAINT(x, y, max) do { \ 1242 ret += (y - x); \ 1243 for (i = x; i < y; i++) \ 1244 pprbuf[i] = MIN(pprbuf[i], max); \ 1245 } while (0); 1246 1247 1248/* Apply appropriate single-, two- and three-chain constraints across the appropriate ppr block */ 1249static uint ppr_apply_constraint_to_block(int8* pprbuf, int8 constraint) 1250{ 1251 uint ret = 0; 1252 uint i = 0; 1253 int8 constraint_2chain = constraint - QDB(3); 1254#if (PPR_MAX_TX_CHAINS > 2) 1255 int8 constraint_3chain = constraint - (QDB(4) + 3); /* - 4.75dBm */ 1256#endif 1257 1258 APPLY_CONSTRAINT(PPR_CHAIN1_FIRST, PPR_CHAIN1_END, constraint); 1259 APPLY_CONSTRAINT(PPR_CHAIN2_FIRST, PPR_CHAIN2_END, constraint_2chain); 1260#if (PPR_MAX_TX_CHAINS > 2) 1261 APPLY_CONSTRAINT(PPR_CHAIN3_FIRST, PPR_CHAIN3_END, constraint_3chain); 1262#endif 1263#ifdef WL_BEAMFORMING 1264 APPLY_CONSTRAINT(PPR_BF_CHAIN2_FIRST, PPR_BF_CHAIN2_END, constraint_2chain); 1265#if (PPR_MAX_TX_CHAINS > 2) 1266 APPLY_CONSTRAINT(PPR_BF_CHAIN3_FIRST, PPR_BF_CHAIN3_END, constraint_3chain); 1267#endif 1268#endif /* WL_BEAMFORMING */ 1269 return ret; 1270} 1271#endif /* (PPR_MAX_TX_CHAINS > 1) */ 1272 1273 1274/* 1275 * Reduce total transmitted power to level of constraint. 1276 * For two chain rates, the per-antenna power must be halved. 1277 * For three chain rates, it must be a third of the constraint. 1278 */ 1279uint ppr_apply_constraint_total_tx(ppr_t* pprptr, int8 constraint) 1280{ 1281 uint ret = 0; 1282 1283#if (PPR_MAX_TX_CHAINS > 1) 1284 int8* pprbuf; 1285 ASSERT(pprptr); 1286 1287 switch (pprptr->ch_bw) { 1288 case WL_TX_BW_20: 1289 { 1290 pprbuf = (int8*)&pprptr->ppr_bw.ch20.b20; 1291 ret += ppr_apply_constraint_to_block(pprbuf, constraint); 1292 } 1293 break; 1294 case WL_TX_BW_40: 1295 { 1296 pprbuf = (int8*)&pprptr->ppr_bw.ch40.b40; 1297 ret += ppr_apply_constraint_to_block(pprbuf, constraint); 1298 pprbuf = (int8*)&pprptr->ppr_bw.ch40.b20in40; 1299 ret += ppr_apply_constraint_to_block(pprbuf, constraint); 1300 } 1301 break; 1302 case WL_TX_BW_80: 1303 { 1304 pprbuf = (int8*)&pprptr->ppr_bw.ch80.b80; 1305 ret += ppr_apply_constraint_to_block(pprbuf, constraint); 1306 pprbuf = (int8*)&pprptr->ppr_bw.ch80.b20in80; 1307 ret += ppr_apply_constraint_to_block(pprbuf, constraint); 1308 pprbuf = (int8*)&pprptr->ppr_bw.ch80.b40in80; 1309 ret += ppr_apply_constraint_to_block(pprbuf, constraint); 1310 } 1311 break; 1312 default: 1313 ASSERT(0); 1314 } 1315 1316#else 1317 ASSERT(pprptr); 1318 ret = ppr_apply_max(pprptr, constraint); 1319#endif /* PPR_MAX_TX_CHAINS > 1 */ 1320 return ret; 1321} 1322 1323 1324/* Ensure no rate limit is lower than the specified minimum */ 1325uint ppr_apply_min(ppr_t* pprptr, int8 minval) 1326{ 1327 uint i; 1328 int8* rptr = (int8*)&pprptr->ppr_bw; 1329 1330 for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++) { 1331 *rptr = MAX(*rptr, minval); 1332 } 1333 return i; 1334} 1335 1336 1337/* Ensure no rate limit in this ppr set is greater than the corresponding limit in ppr_cap */ 1338uint ppr_apply_vector_ceiling(ppr_t* pprptr, const ppr_t* ppr_cap) 1339{ 1340 uint i = 0; 1341 int8* rptr = (int8*)&pprptr->ppr_bw; 1342 const int8* capptr = (const int8*)&ppr_cap->ppr_bw; 1343 1344 if (pprptr->ch_bw == ppr_cap->ch_bw) { 1345 for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++, capptr++) { 1346 *rptr = MIN(*rptr, *capptr); 1347 } 1348 } 1349 return i; 1350} 1351 1352 1353/* Ensure no rate limit in this ppr set is lower than the corresponding limit in ppr_min */ 1354uint ppr_apply_vector_floor(ppr_t* pprptr, const ppr_t* ppr_min) 1355{ 1356 uint i = 0; 1357 int8* rptr = (int8*)&pprptr->ppr_bw; 1358 const int8* minptr = (const int8*)&ppr_min->ppr_bw; 1359 1360 if (pprptr->ch_bw == ppr_min->ch_bw) { 1361 for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++, minptr++) { 1362 *rptr = MAX((uint8)*rptr, (uint8)*minptr); 1363 } 1364 } 1365 return i; 1366} 1367 1368 1369/* Get the maximum power in the ppr set */ 1370int8 ppr_get_max(ppr_t* pprptr) 1371{ 1372 uint i; 1373 int8* rptr = (int8*)&pprptr->ppr_bw; 1374 int8 maxval = *rptr++; 1375 1376 for (i = 1; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++) { 1377 maxval = MAX(maxval, *rptr); 1378 } 1379 return maxval; 1380} 1381 1382 1383/* 1384 * Get the minimum power in the ppr set, excluding disallowed 1385 * rates and (possibly) powers set to the minimum for the phy 1386 */ 1387int8 ppr_get_min(ppr_t* pprptr, int8 floor) 1388{ 1389 uint i; 1390 int8* rptr = (int8*)&pprptr->ppr_bw; 1391 int8 minval = WL_RATE_DISABLED; 1392 1393 for (i = 0; (i < ppr_pwrs_size(pprptr->ch_bw)) && ((minval == WL_RATE_DISABLED) || 1394 (minval == floor)); i++, rptr++) { 1395 minval = *rptr; 1396 } 1397 for (; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++) { 1398 if ((*rptr != WL_RATE_DISABLED) && (*rptr != floor)) 1399 minval = MIN(minval, *rptr); 1400 } 1401 return minval; 1402} 1403 1404 1405/* Get the maximum power for a given bandwidth in the ppr set */ 1406int8 ppr_get_max_for_bw(ppr_t* pprptr, wl_tx_bw_t bw) 1407{ 1408 uint i; 1409 const pprpbw_t* bw_pwrs; 1410 const int8* rptr; 1411 int8 maxval; 1412 1413 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1414 if (bw_pwrs != NULL) { 1415 rptr = (const int8*)bw_pwrs; 1416 maxval = *rptr++; 1417 1418 for (i = 1; i < sizeof(*bw_pwrs); i++, rptr++) { 1419 maxval = MAX(maxval, *rptr); 1420 } 1421 } else { 1422 maxval = WL_RATE_DISABLED; 1423 } 1424 return maxval; 1425} 1426 1427 1428/* Get the minimum power for a given bandwidth in the ppr set */ 1429int8 ppr_get_min_for_bw(ppr_t* pprptr, wl_tx_bw_t bw) 1430{ 1431 uint i; 1432 const pprpbw_t* bw_pwrs; 1433 const int8* rptr; 1434 int8 minval; 1435 1436 bw_pwrs = ppr_get_bw_powers(pprptr, bw); 1437 if (bw_pwrs != NULL) { 1438 rptr = (const int8*)bw_pwrs; 1439 minval = *rptr++; 1440 1441 for (i = 1; i < sizeof(*bw_pwrs); i++, rptr++) { 1442 minval = MIN(minval, *rptr); 1443 } 1444 } else 1445 minval = WL_RATE_DISABLED; 1446 return minval; 1447} 1448 1449 1450/* Map the given function with its context value over the two power vectors */ 1451void 1452ppr_map_vec_dsss(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, ppr_t* pprptr2, 1453 wl_tx_bw_t bw, wl_tx_chains_t tx_chains) 1454{ 1455 pprpbw_t* bw_pwrs1; 1456 pprpbw_t* bw_pwrs2; 1457 int8* powers1; 1458 int8* powers2; 1459 uint i; 1460 1461 ASSERT(pprptr1); 1462 ASSERT(pprptr2); 1463 1464 bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); 1465 bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); 1466 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1467 powers1 = (int8*)ppr_get_dsss_group(bw_pwrs1, tx_chains); 1468 powers2 = (int8*)ppr_get_dsss_group(bw_pwrs2, tx_chains); 1469 if ((powers1 != NULL) && (powers2 != NULL)) { 1470 for (i = 0; i < WL_RATESET_SZ_DSSS; i++) 1471 (fn)(context, (uint8*)powers1++, (uint8*)powers2++); 1472 } 1473 } 1474} 1475 1476 1477/* Map the given function with its context value over the two power vectors */ 1478void 1479ppr_map_vec_ofdm(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, ppr_t* pprptr2, 1480 wl_tx_bw_t bw, wl_tx_mode_t mode, wl_tx_chains_t tx_chains) 1481{ 1482 pprpbw_t* bw_pwrs1; 1483 pprpbw_t* bw_pwrs2; 1484 int8* powers1; 1485 int8* powers2; 1486 uint i; 1487 1488 bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); 1489 bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); 1490 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1491 powers1 = (int8*)ppr_get_ofdm_group(bw_pwrs1, mode, tx_chains); 1492 powers2 = (int8*)ppr_get_ofdm_group(bw_pwrs2, mode, tx_chains); 1493 if ((powers1 != NULL) && (powers2 != NULL)) { 1494 for (i = 0; i < WL_RATESET_SZ_OFDM; i++) 1495 (fn)(context, (uint8*)powers1++, (uint8*)powers2++); 1496 } 1497 } 1498} 1499 1500 1501/* Map the given function with its context value over the two power vectors */ 1502void 1503ppr_map_vec_ht_mcs(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, 1504 ppr_t* pprptr2, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, 1505 wl_tx_chains_t tx_chains) 1506{ 1507 pprpbw_t* bw_pwrs1; 1508 pprpbw_t* bw_pwrs2; 1509 int8* powers1; 1510 int8* powers2; 1511 uint i; 1512 1513 bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); 1514 bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); 1515 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1516 powers1 = (int8*)ppr_get_mcs_group(bw_pwrs1, Nss, mode, tx_chains); 1517 powers2 = (int8*)ppr_get_mcs_group(bw_pwrs2, Nss, mode, tx_chains); 1518 if ((powers1 != NULL) && (powers2 != NULL)) { 1519 for (i = 0; i < WL_RATESET_SZ_HT_MCS; i++) 1520 (fn)(context, (uint8*)powers1++, (uint8*)powers2++); 1521 } 1522 } 1523} 1524 1525 1526/* Map the given function with its context value over the two power vectors */ 1527void 1528ppr_map_vec_vht_mcs(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, 1529 ppr_t* pprptr2, wl_tx_bw_t bw, wl_tx_nss_t Nss, wl_tx_mode_t mode, wl_tx_chains_t 1530 tx_chains) 1531{ 1532 pprpbw_t* bw_pwrs1; 1533 pprpbw_t* bw_pwrs2; 1534 int8* powers1; 1535 int8* powers2; 1536 uint i; 1537 1538 bw_pwrs1 = ppr_get_bw_powers(pprptr1, bw); 1539 bw_pwrs2 = ppr_get_bw_powers(pprptr2, bw); 1540 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1541 powers1 = (int8*)ppr_get_mcs_group(bw_pwrs1, Nss, mode, tx_chains); 1542 powers2 = (int8*)ppr_get_mcs_group(bw_pwrs2, Nss, mode, tx_chains); 1543 if ((powers1 != NULL) && (powers2 != NULL)) { 1544 for (i = 0; i < WL_RATESET_SZ_VHT_MCS; i++) 1545 (fn)(context, (uint8*)powers1++, (uint8*)powers2++); 1546 } 1547 } 1548} 1549 1550 1551/* Map the given function with its context value over the two power vectors */ 1552 1553void 1554ppr_map_vec_all(ppr_mapfn_t fn, void* context, ppr_t* pprptr1, ppr_t* pprptr2) 1555{ 1556 uint i; 1557 pprpbw_t* bw_pwrs1; 1558 pprpbw_t* bw_pwrs2; 1559 int8* rptr1 = (int8*)&pprptr1->ppr_bw; 1560 int8* rptr2 = (int8*)&pprptr2->ppr_bw; 1561 1562 bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_20); 1563 bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_20); 1564 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1565 rptr1 = (int8*)bw_pwrs1; 1566 rptr2 = (int8*)bw_pwrs2; 1567 for (i = 0; i < sizeof(pprpbw_t); i++, rptr1++, rptr2++) { 1568 (fn)(context, (uint8*)rptr1, (uint8*)rptr2); 1569 } 1570 } 1571 1572 bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_40); 1573 bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_40); 1574 1575 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1576 rptr1 = (int8*)bw_pwrs1; 1577 rptr2 = (int8*)bw_pwrs2; 1578 for (i = 0; i < sizeof(pprpbw_t); i++, rptr1++, rptr2++) { 1579 (fn)(context, (uint8*)rptr1, (uint8*)rptr2); 1580 } 1581 1582 bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_20IN40); 1583 bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_20IN40); 1584 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1585 rptr1 = (int8*)bw_pwrs1; 1586 rptr2 = (int8*)bw_pwrs2; 1587 for (i = 0; i < sizeof(pprpbw_t); i++, rptr1++, rptr2++) { 1588 (fn)(context, (uint8*)rptr1, (uint8*)rptr2); 1589 } 1590 } 1591 } 1592 1593 bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_80); 1594 bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_80); 1595 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1596 rptr1 = (int8*)bw_pwrs1; 1597 rptr2 = (int8*)bw_pwrs2; 1598 for (i = 0; i < sizeof(pprpbw_t); i++, rptr1++, rptr2++) { 1599 (fn)(context, (uint8*)rptr1, (uint8*)rptr2); 1600 } 1601 1602 bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_20IN80); 1603 bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_20IN80); 1604 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1605 rptr1 = (int8*)bw_pwrs1; 1606 rptr2 = (int8*)bw_pwrs2; 1607 for (i = 0; i < sizeof(pprpbw_t); i++, rptr1++, rptr2++) { 1608 (fn)(context, (uint8*)rptr1, (uint8*)rptr2); 1609 } 1610 } 1611 1612 bw_pwrs1 = ppr_get_bw_powers(pprptr1, WL_TX_BW_40IN80); 1613 bw_pwrs2 = ppr_get_bw_powers(pprptr2, WL_TX_BW_40IN80); 1614 if ((bw_pwrs1 != NULL) && (bw_pwrs2 != NULL)) { 1615 rptr1 = (int8*)bw_pwrs1; 1616 rptr2 = (int8*)bw_pwrs2; 1617 for (i = 0; i < sizeof(pprpbw_t); i++, rptr1++, rptr2++) { 1618 (fn)(context, (uint8*)rptr1, (uint8*)rptr2); 1619 } 1620 } 1621 } 1622} 1623 1624 1625/* Set PPR struct to a certain power level */ 1626void 1627ppr_set_cmn_val(ppr_t* pprptr, int8 val) 1628{ 1629 memset((uchar*)&pprptr->ppr_bw, val, ppr_pwrs_size(pprptr->ch_bw)); 1630} 1631 1632 1633/* Make an identical copy of a ppr structure (for ppr_bw==all case) */ 1634void 1635ppr_copy_struct(ppr_t* pprptr_s, ppr_t* pprptr_d) 1636{ 1637 int8* rptr_s = (int8*)&pprptr_s->ppr_bw; 1638 int8* rptr_d = (int8*)&pprptr_d->ppr_bw; 1639 /* ASSERT(ppr_pwrs_size(pprptr_d->ch_bw) >= ppr_pwrs_size(pprptr_s->ch_bw)); */ 1640 1641 if (pprptr_s->ch_bw == pprptr_d->ch_bw) 1642 bcopy(rptr_s, rptr_d, ppr_pwrs_size(pprptr_s->ch_bw)); 1643 else { 1644 const pprpbw_t* src_bw_pwrs; 1645 pprpbw_t* dest_bw_pwrs; 1646 1647 src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_20); 1648 dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_20); 1649 if (src_bw_pwrs && dest_bw_pwrs) 1650 bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, 1651 sizeof(*src_bw_pwrs)); 1652 1653 src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_40); 1654 dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_40); 1655 if (src_bw_pwrs && dest_bw_pwrs) 1656 bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, 1657 sizeof(*src_bw_pwrs)); 1658 1659 src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_20IN40); 1660 dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_20IN40); 1661 if (src_bw_pwrs && dest_bw_pwrs) 1662 bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, 1663 sizeof(*src_bw_pwrs)); 1664 1665 src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_80); 1666 dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_80); 1667 if (src_bw_pwrs && dest_bw_pwrs) 1668 bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, 1669 sizeof(*src_bw_pwrs)); 1670 1671 src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_20IN80); 1672 dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_20IN80); 1673 if (src_bw_pwrs && dest_bw_pwrs) 1674 bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, 1675 sizeof(*src_bw_pwrs)); 1676 1677 src_bw_pwrs = ppr_get_bw_powers(pprptr_s, WL_TX_BW_40IN80); 1678 dest_bw_pwrs = ppr_get_bw_powers(pprptr_d, WL_TX_BW_40IN80); 1679 if (src_bw_pwrs && dest_bw_pwrs) 1680 bcopy((const uint8*)src_bw_pwrs, (uint8*)dest_bw_pwrs, 1681 sizeof(*src_bw_pwrs)); 1682 } 1683} 1684 1685 1686/* Subtract each power from a common value and re-store */ 1687void 1688ppr_cmn_val_minus(ppr_t* pprptr, int8 val) 1689{ 1690 uint i; 1691 int8* rptr = (int8*)&pprptr->ppr_bw; 1692 1693 for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++) { 1694 if (*rptr != (int8)WL_RATE_DISABLED) 1695 *rptr = val - *rptr; 1696 } 1697 1698} 1699 1700 1701/* Subtract a common value from each power and re-store */ 1702void 1703ppr_minus_cmn_val(ppr_t* pprptr, int8 val) 1704{ 1705 uint i; 1706 int8* rptr = (int8*)&pprptr->ppr_bw; 1707 1708 for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++) { 1709 if (*rptr != (int8)WL_RATE_DISABLED) 1710 *rptr = (*rptr > val) ? (*rptr - val) : 0; 1711 } 1712 1713} 1714 1715 1716/* Add a common value to each power and re-store */ 1717void 1718ppr_plus_cmn_val(ppr_t* pprptr, int8 val) 1719{ 1720 uint i; 1721 int8* rptr = (int8*)&pprptr->ppr_bw; 1722 1723 for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++) { 1724 if (*rptr != (int8)WL_RATE_DISABLED) 1725 *rptr += val; 1726 } 1727 1728} 1729 1730 1731/* Multiply by a percentage */ 1732void 1733ppr_multiply_percentage(ppr_t* pprptr, uint8 val) 1734{ 1735 uint i; 1736 int8* rptr = (int8*)&pprptr->ppr_bw; 1737 1738 for (i = 0; i < ppr_pwrs_size(pprptr->ch_bw); i++, rptr++) { 1739 if (*rptr != (int8)WL_RATE_DISABLED) 1740 *rptr = (*rptr * val) / 100; 1741 } 1742 1743} 1744 1745 1746/* Compare two ppr variables p1 and p2, save the min value of each 1747 * contents to variable p1 1748 */ 1749void 1750ppr_compare_min(ppr_t* p1, ppr_t* p2) 1751{ 1752 uint i; 1753 int8* rptr1 = NULL; 1754 int8* rptr2 = NULL; 1755 uint32 pprsize = 0; 1756 1757 if (p1->ch_bw == p2->ch_bw) { 1758 rptr1 = (int8*)&p1->ppr_bw; 1759 rptr2 = (int8*)&p2->ppr_bw; 1760 pprsize = ppr_pwrs_size(p1->ch_bw); 1761 } 1762 1763 for (i = 0; i < pprsize; i++, rptr1++, rptr2++) { 1764 *rptr1 = MIN(*rptr1, *rptr2); 1765 } 1766} 1767 1768 1769/* Compare two ppr variables p1 and p2, save the max. value of each 1770 * contents to variable p1 1771 */ 1772void 1773ppr_compare_max(ppr_t* p1, ppr_t* p2) 1774{ 1775 uint i; 1776 int8* rptr1 = NULL; 1777 int8* rptr2 = NULL; 1778 uint32 pprsize = 0; 1779 1780 if (p1->ch_bw == p2->ch_bw) { 1781 rptr1 = (int8*)&p1->ppr_bw; 1782 rptr2 = (int8*)&p2->ppr_bw; 1783 pprsize = ppr_pwrs_size(p1->ch_bw); 1784 } 1785 1786 for (i = 0; i < pprsize; i++, rptr1++, rptr2++) { 1787 *rptr1 = MAX(*rptr1, *rptr2); 1788 } 1789} 1790 1791 1792/* Serialize the contents of the opaque ppr struct. 1793 * Writes number of bytes copied, zero on error. 1794 * Returns error code, BCME_OK if successful. 1795 */ 1796int 1797ppr_serialize(const ppr_t* pprptr, uint8* buf, uint buflen, uint* bytes_copied) 1798{ 1799 int err = BCME_OK; 1800 if (buflen <= sizeof(ppr_ser_mem_flag_t)) { 1801 err = BCME_BUFTOOSHORT; 1802 } else { 1803 ppr_ser_mem_flag_t *smem_flag = (ppr_ser_mem_flag_t *)buf; 1804 uint32 flag = NTOH32(smem_flag->flag); 1805 1806 /* check if memory contains a valid flag, if not, use current 1807 * condition (num of chains, txbf etc.) to serialize data. 1808 */ 1809 if (NTOH32(smem_flag->magic_word) != PPR_SER_MEM_WORD) { 1810 flag = ppr_get_flag(); 1811 } 1812 1813 if (buflen >= ppr_ser_size_by_flag(flag, pprptr->ch_bw)) { 1814 *bytes_copied = ppr_serialize_data(pprptr, buf, flag); 1815 } else { 1816 err = BCME_BUFTOOSHORT; 1817 } 1818 } 1819 return err; 1820} 1821 1822 1823/* Deserialize the contents of a buffer into an opaque ppr struct. 1824 * Creates an opaque structure referenced by *pptrptr, NULL on error. 1825 * Returns error code, BCME_OK if successful. 1826 */ 1827int 1828ppr_deserialize_create(osl_t *osh, const uint8* buf, uint buflen, ppr_t** pprptr) 1829{ 1830 const uint8* bptr = buf; 1831 int err = BCME_OK; 1832 ppr_t* lpprptr = NULL; 1833 1834 if ((buflen > SER_HDR_LEN) && (bptr != NULL) && (*bptr == PPR_SERIALIZATION_VER)) { 1835 const ppr_deser_header_t * ser_head = (const ppr_deser_header_t *)bptr; 1836 wl_tx_bw_t ch_bw = ser_head->bw; 1837 /* struct size plus header */ 1838 uint32 ser_size = ppr_pwrs_size(ch_bw) + SER_HDR_LEN; 1839 1840 if ((lpprptr = ppr_create(osh, ch_bw)) != NULL) { 1841 uint32 flags = NTOH32(ser_head->flags); 1842 uint16 per_band_size = NTOH16(ser_head->per_band_size); 1843 uint16 chain3size = NTOH16(ser_head->chain3size); 1844 /* set the data with default value before deserialize */ 1845 ppr_set_cmn_val(lpprptr, WL_RATE_DISABLED); 1846 1847 ppr_deser_cpy(lpprptr, bptr + sizeof(*ser_head), flags, ch_bw, 1848 per_band_size, chain3size); 1849 } else if (buflen < ser_size) { 1850 err = BCME_BUFTOOSHORT; 1851 } else { 1852 err = BCME_NOMEM; 1853 } 1854 } else if (buflen <= SER_HDR_LEN) { 1855 err = BCME_BUFTOOSHORT; 1856 } else if (bptr == NULL) { 1857 err = BCME_BADARG; 1858 } else { 1859 err = BCME_VERSION; 1860 } 1861 *pprptr = lpprptr; 1862 return err; 1863} 1864 1865 1866/* Deserialize the contents of a buffer into an opaque ppr struct. 1867 * Creates an opaque structure referenced by *pptrptr, NULL on error. 1868 * Returns error code, BCME_OK if successful. 1869 */ 1870int 1871ppr_deserialize(ppr_t* pprptr, const uint8* buf, uint buflen) 1872{ 1873 const uint8* bptr = buf; 1874 int err = BCME_OK; 1875 ASSERT(pprptr); 1876 if ((buflen > SER_HDR_LEN) && (bptr != NULL) && (*bptr == PPR_SERIALIZATION_VER)) { 1877 const ppr_deser_header_t * ser_head = (const ppr_deser_header_t *)bptr; 1878 wl_tx_bw_t ch_bw = ser_head->bw; 1879 1880 if (ch_bw == pprptr->ch_bw) { 1881 uint32 flags = NTOH32(ser_head->flags); 1882 uint16 per_band_size = NTOH16(ser_head->per_band_size); 1883 uint16 chain3size = NTOH16(ser_head->chain3size); 1884 ppr_set_cmn_val(pprptr, WL_RATE_DISABLED); 1885 ppr_deser_cpy(pprptr, bptr + sizeof(*ser_head), flags, ch_bw, 1886 per_band_size, chain3size); 1887 } else { 1888 err = BCME_BADARG; 1889 } 1890 } else if (buflen <= SER_HDR_LEN) { 1891 err = BCME_BUFTOOSHORT; 1892 } else if (bptr == NULL) { 1893 err = BCME_BADARG; 1894 } else { 1895 err = BCME_VERSION; 1896 } 1897 return err; 1898} 1899 1900 1901#ifdef WLTXPWR_CACHE 1902 1903#define MAX_TXPWR_CACHE_ENTRIES 2 1904#define TXPWR_ALL_INVALID 0xff 1905 1906#define TXPWR_CACHE_TXPWR_MAX 0x7f /* WLC_TXPWR_MAX; */ 1907 1908/* transmit power cache */ 1909typedef struct txpwr_cache_entry { 1910 chanspec_t chanspec; 1911 ppr_t* cache_pwrs[TXPWR_CACHE_NUM_TYPES]; 1912 uint8 tx_pwr_max[PPR_MAX_TX_CHAINS]; 1913 uint8 tx_pwr_min[PPR_MAX_TX_CHAINS]; 1914 int8 txchain_offsets[PPR_MAX_TX_CHAINS]; 1915 uint8 data_invalid_flags; 1916#if !defined(WLC_LOW) || !defined(WLC_HIGH) 1917 int stf_tx_target_pwr_min; 1918#endif 1919} txpwr_cache_entry_t; 1920 1921txpwr_cache_entry_t txpwr_cache[MAX_TXPWR_CACHE_ENTRIES] = {{0}, {0}}; 1922 1923 1924static txpwr_cache_entry_t* wlc_phy_txpwr_cache_get_entry(chanspec_t chanspec); 1925static txpwr_cache_entry_t* wlc_phy_txpwr_cache_get_diff_entry(chanspec_t chanspec); 1926static void wlc_phy_txpwr_cache_clear_entry(osl_t *osh, txpwr_cache_entry_t* entryptr); 1927 1928 1929/* Find a cache entry for the specified chanspec. */ 1930static txpwr_cache_entry_t* wlc_phy_txpwr_cache_get_entry(chanspec_t chanspec) 1931{ 1932 uint i; 1933 txpwr_cache_entry_t* entryptr = NULL; 1934 1935 for (i = 0; i < (MAX_TXPWR_CACHE_ENTRIES) && (entryptr == NULL); i++) { 1936 if (txpwr_cache[i].chanspec == chanspec) { 1937 entryptr = &txpwr_cache[i]; 1938 } 1939 } 1940 return entryptr; 1941} 1942 1943 1944/* Find a cache entry that's NOT for the specified chanspec. */ 1945static txpwr_cache_entry_t* wlc_phy_txpwr_cache_get_diff_entry(chanspec_t chanspec) 1946{ 1947 uint i; 1948 txpwr_cache_entry_t* entryptr = NULL; 1949 1950 for (i = 0; i < (MAX_TXPWR_CACHE_ENTRIES) && (entryptr == NULL); i++) { 1951 if (txpwr_cache[i].chanspec != chanspec) { 1952 entryptr = &txpwr_cache[i]; 1953 } 1954 } 1955 return entryptr; 1956} 1957 1958 1959/* Clear a specific cache entry. Delete any ppr_t structs and clear the pointers. */ 1960static void wlc_phy_txpwr_cache_clear_entry(osl_t *osh, txpwr_cache_entry_t* entryptr) 1961{ 1962 uint i; 1963 1964 entryptr->chanspec = 0; 1965 1966 ASSERT(entryptr != NULL); 1967 for (i = 0; i < TXPWR_CACHE_NUM_TYPES; i++) { 1968 if (entryptr->cache_pwrs[i] != NULL) { 1969 ppr_delete(osh, entryptr->cache_pwrs[i]); 1970 entryptr->cache_pwrs[i] = NULL; 1971 } 1972 } 1973 /* 1974 * Don't bother with max, min and txchain_offsets, as they need to be 1975 * initialised when the entry is setup for a new chanspec 1976 */ 1977} 1978 1979 1980/* 1981 * Get a ppr_t struct of a given type from the cache for the specified chanspec. 1982 * Don't return the pointer if the cached data is invalid. 1983 */ 1984ppr_t* wlc_phy_get_cached_pwr(chanspec_t chanspec, uint pwr_type) 1985{ 1986 ppr_t* pwrptr = NULL; 1987 1988 if (pwr_type < TXPWR_CACHE_NUM_TYPES) { 1989 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 1990 1991 if ((entryptr != NULL) && 1992 ((entryptr->data_invalid_flags & (0x01 << pwr_type)) == 0)) 1993 pwrptr = entryptr->cache_pwrs[pwr_type]; 1994 } 1995 1996 return pwrptr; 1997} 1998 1999 2000/* Add a ppr_t struct of a given type to the cache for the specified chanspec. */ 2001int wlc_phy_set_cached_pwr(osl_t *osh, chanspec_t chanspec, uint pwr_type, ppr_t* pwrptr) 2002{ 2003 int result = BCME_NOTFOUND; 2004 2005 if (pwr_type < TXPWR_CACHE_NUM_TYPES) { 2006 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2007 2008 if (entryptr != NULL) { 2009 if ((entryptr->cache_pwrs[pwr_type] != NULL) && 2010 (entryptr->cache_pwrs[pwr_type] != pwrptr)) { 2011 ppr_delete(osh, entryptr->cache_pwrs[pwr_type]); 2012 } 2013 entryptr->cache_pwrs[pwr_type] = pwrptr; 2014 entryptr->data_invalid_flags &= ~(0x01 << pwr_type); /* now valid */ 2015 result = BCME_OK; 2016 } 2017 } 2018 return result; 2019} 2020 2021/* Indicate if we have cached a particular ppr_t struct for any chanspec. */ 2022bool wlc_phy_is_pwr_cached(uint pwr_type, ppr_t* pwrptr) 2023{ 2024 bool result = FALSE; 2025 uint i; 2026 2027 if (pwr_type < TXPWR_CACHE_NUM_TYPES) { 2028 for (i = 0; (i < MAX_TXPWR_CACHE_ENTRIES) && (result == FALSE); i++) { 2029 if (txpwr_cache[i].cache_pwrs[pwr_type] == pwrptr) { 2030 result = TRUE; 2031 } 2032 } 2033 } 2034 return result; 2035} 2036 2037#if !defined(WLC_LOW) || !defined(WLC_HIGH) 2038/* Get the minimum target power for all cores for the chanspec. */ 2039int wlc_phy_get_cached_stf_target_pwr_min(chanspec_t chanspec) 2040{ 2041 int min_pwr = TXPWR_CACHE_TXPWR_MAX; 2042 2043 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2044 2045 if ((entryptr != NULL) && 2046 ((entryptr->data_invalid_flags & (TXPWR_STF_TARGET_PWR_MIN_INVALID)) == 0)) 2047 min_pwr = entryptr->stf_tx_target_pwr_min; 2048 2049 return min_pwr; 2050} 2051 2052 2053/* set the minimum target power for all cores for the chanspec. */ 2054int wlc_phy_set_cached_stf_target_pwr_min(chanspec_t chanspec, int min_pwr) 2055{ 2056 int result = BCME_NOTFOUND; 2057 2058 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2059 2060 if (entryptr != NULL) { 2061 entryptr->stf_tx_target_pwr_min = min_pwr; 2062 entryptr->data_invalid_flags &= ~TXPWR_STF_TARGET_PWR_MIN_INVALID; /* now valid */ 2063 result = BCME_OK; 2064 } 2065 return result; 2066} 2067#endif /* !WLC_HIGH || !WLC_LOW */ 2068 2069/* Get the maximum power for the specified core and chanspec. */ 2070uint8 wlc_phy_get_cached_pwr_max(chanspec_t chanspec, uint core) 2071{ 2072 uint8 max_pwr = WL_RATE_DISABLED; 2073 2074 if (core < PPR_MAX_TX_CHAINS) { 2075 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2076 2077 if (entryptr != NULL) 2078 max_pwr = entryptr->tx_pwr_max[core]; 2079 } 2080 2081 return max_pwr; 2082} 2083 2084 2085/* Set the maximum power for the specified core and chanspec. */ 2086int wlc_phy_set_cached_pwr_max(chanspec_t chanspec, uint core, uint8 max_pwr) 2087{ 2088 int result = BCME_NOTFOUND; 2089 2090 if (core < PPR_MAX_TX_CHAINS) { 2091 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2092 2093 if (entryptr != NULL) { 2094 entryptr->tx_pwr_max[core] = max_pwr; 2095 result = BCME_OK; 2096 } 2097 } 2098 return result; 2099} 2100 2101 2102/* Get the minimum power for the specified core and chanspec. */ 2103uint8 wlc_phy_get_cached_pwr_min(chanspec_t chanspec, uint core) 2104{ 2105 uint8 min_pwr = WL_RATE_DISABLED; 2106 2107 if (core < PPR_MAX_TX_CHAINS) { 2108 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2109 2110 if (entryptr != NULL) 2111 min_pwr = entryptr->tx_pwr_min[core]; 2112 } 2113 2114 return min_pwr; 2115} 2116 2117 2118/* Set the minimum power for the specified core and chanspec. */ 2119int wlc_phy_set_cached_pwr_min(chanspec_t chanspec, uint core, uint8 min_pwr) 2120{ 2121 int result = BCME_NOTFOUND; 2122 2123 if (core < PPR_MAX_TX_CHAINS) { 2124 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2125 2126 if (entryptr != NULL) { 2127 entryptr->tx_pwr_min[core] = min_pwr; 2128 result = BCME_OK; 2129 } 2130 } 2131 return result; 2132} 2133 2134 2135/* Get the txchain offsets for the specified chanspec. */ 2136int8 wlc_phy_get_cached_txchain_offsets(chanspec_t chanspec, uint core) 2137{ 2138 uint8 offset = WL_RATE_DISABLED; 2139 2140 if (core < PPR_MAX_TX_CHAINS) { 2141 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2142 2143 if (entryptr != NULL) 2144 offset = entryptr->txchain_offsets[core]; 2145 } 2146 2147 return offset; 2148} 2149 2150 2151/* Set the txchain offsets for the specified chanspec. */ 2152int wlc_phy_set_cached_txchain_offsets(chanspec_t chanspec, uint core, int8 offset) 2153{ 2154 int result = BCME_NOTFOUND; 2155 2156 if (core < PPR_MAX_TX_CHAINS) { 2157 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2158 2159 if (entryptr != NULL) { 2160 entryptr->txchain_offsets[core] = offset; 2161 result = BCME_OK; 2162 } 2163 } 2164 return result; 2165} 2166 2167 2168/* Indicate if we have a cache entry for the specified chanspec. */ 2169bool wlc_phy_txpwr_cache_is_cached(chanspec_t chanspec) 2170{ 2171 bool result = FALSE; 2172 2173 if (wlc_phy_txpwr_cache_get_entry(chanspec)) { 2174 result = TRUE; 2175 } 2176 return result; 2177} 2178 2179 2180/* Find a cache entry that's NOT for the specified chanspec. Return the chanspec. */ 2181chanspec_t wlc_phy_txpwr_cache_find_other_cached_chanspec(chanspec_t chanspec) 2182{ 2183 chanspec_t chan = 0; 2184 2185 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_diff_entry(chanspec); 2186 if (entryptr != NULL) { 2187 chan = entryptr->chanspec; 2188 } 2189 return chan; 2190} 2191 2192 2193/* Find a specific cache entry and clear it. */ 2194void wlc_phy_txpwr_cache_clear(osl_t *osh, chanspec_t chanspec) 2195{ 2196 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2197 if (entryptr != NULL) { 2198 wlc_phy_txpwr_cache_clear_entry(osh, entryptr); 2199 } 2200} 2201 2202 2203/* Invalidate all cached data. */ 2204void wlc_phy_txpwr_cache_invalidate(void) 2205{ 2206 uint j; 2207 2208 for (j = 0; j < MAX_TXPWR_CACHE_ENTRIES; j++) { 2209 txpwr_cache_entry_t* entryptr = &txpwr_cache[j]; 2210 if (entryptr->chanspec != 0) { 2211 uint i; 2212 entryptr->data_invalid_flags = TXPWR_ALL_INVALID; 2213 for (i = 0; i < PPR_MAX_TX_CHAINS; i++) { 2214 entryptr->tx_pwr_min[i] = TXPWR_CACHE_TXPWR_MAX; 2215 entryptr->tx_pwr_max[i] = WL_RATE_DISABLED; 2216 entryptr->txchain_offsets[i] = WL_RATE_DISABLED; 2217 } 2218#if !defined(WLC_LOW) || !defined(WLC_HIGH) 2219 entryptr->stf_tx_target_pwr_min = TXPWR_CACHE_TXPWR_MAX; 2220#endif 2221 } 2222 } 2223} 2224 2225 2226/* Clear all cache entries. */ 2227void wlc_phy_txpwr_cache_close(osl_t *osh) 2228{ 2229 uint i; 2230 2231 for (i = 0; i < MAX_TXPWR_CACHE_ENTRIES; i++) { 2232 txpwr_cache_entry_t* entryptr = &txpwr_cache[i]; 2233 if (entryptr->chanspec != 0) { 2234 wlc_phy_txpwr_cache_clear_entry(osh, entryptr); 2235 } 2236 } 2237} 2238 2239 2240/* Find an empty cache entry and initialise it. */ 2241int wlc_phy_txpwr_setup_entry(chanspec_t chanspec) 2242{ 2243 int result = BCME_NOTFOUND; 2244 /* find an empty entry */ 2245 txpwr_cache_entry_t* entryptr = wlc_phy_txpwr_cache_get_entry(0); 2246 if (entryptr != NULL) { 2247 uint i; 2248 2249 entryptr->chanspec = chanspec; 2250 for (i = 0; i < PPR_MAX_TX_CHAINS; i++) { 2251 entryptr->tx_pwr_min[i] = TXPWR_CACHE_TXPWR_MAX; /* WLC_TXPWR_MAX; */ 2252 entryptr->tx_pwr_max[i] = WL_RATE_DISABLED; 2253 entryptr->txchain_offsets[i] = WL_RATE_DISABLED; 2254 } 2255#if !defined(WLC_LOW) || !defined(WLC_HIGH) 2256 entryptr->stf_tx_target_pwr_min = TXPWR_CACHE_TXPWR_MAX; 2257#endif 2258#if !defined(WLC_HIGH) 2259 entryptr->data_invalid_flags |= TXPWR_STF_TARGET_PWR_NOT_CACHED; 2260#endif 2261 result = BCME_OK; 2262 } 2263 return result; 2264} 2265 2266#ifndef WLC_LOW 2267/* Drop any reference to a particular ppr_t struct from the cache. */ 2268void wlc_phy_uncache_pwr(uint pwr_type, ppr_t* pwrptr) 2269{ 2270 bool result = FALSE; 2271 uint i; 2272 2273 if (pwr_type < TXPWR_CACHE_NUM_TYPES) { 2274 for (i = 0; (i < MAX_TXPWR_CACHE_ENTRIES) && (result == FALSE); i++) { 2275 if (txpwr_cache[i].cache_pwrs[pwr_type] == pwrptr) { 2276 txpwr_cache[i].cache_pwrs[pwr_type] = NULL; 2277 } 2278 } 2279 } 2280} 2281#endif 2282 2283#if !defined(WLC_HIGH) 2284bool wlc_phy_get_stf_ppr_cached(chanspec_t chanspec) 2285{ 2286 bool ret = FALSE; 2287 txpwr_cache_entry_t *entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2288 if (entryptr != NULL) 2289 ret = !(entryptr->data_invalid_flags & TXPWR_STF_TARGET_PWR_NOT_CACHED); 2290 return ret; 2291} 2292 2293void wlc_phy_set_stf_ppr_cached(chanspec_t chanspec, bool bcached) 2294{ 2295 txpwr_cache_entry_t *entryptr = wlc_phy_txpwr_cache_get_entry(chanspec); 2296 if (entryptr != NULL) { 2297 if (bcached) 2298 entryptr->data_invalid_flags &= ~TXPWR_STF_TARGET_PWR_NOT_CACHED; 2299 else 2300 entryptr->data_invalid_flags |= TXPWR_STF_TARGET_PWR_NOT_CACHED; 2301 } 2302} 2303#endif /* !defined(WLC_HIGH) */ 2304 2305#endif /* WLTXPWR_CACHE */ 2306