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