static A_BOOL
rcIsValidPhyRate(A_UINT32 phy, A_UINT32 capflag, A_BOOL ignoreCW)
{
	if (WLAN_RC_PHY_HT(phy) && !(capflag & WLAN_RC_HT_FLAG)) {
		return FALSE;
	}

	if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG))  {
		return FALSE;
	}
	if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_HT40_SGI_FLAG)) {
		return FALSE;
	}

	if (!ignoreCW && WLAN_RC_PHY_HT(phy)) {
		if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG)) {
			return FALSE;
		}

		if (!WLAN_RC_PHY_40(phy) && (capflag & WLAN_RC_40_FLAG)) {
			return FALSE;
		}
	}
    
	return TRUE;
}
/* 
 * Initialize the Valid Rate Index from Rate Set 
 */
static A_UINT8
rcSibSetValidRates(const RATE_TABLE_11N *pRateTable,
		   TX_RATE_CTRL *pRc, 
                   struct ieee80211_rateset *pRateSet,
		   A_UINT32 capflag,
		   struct ath_node_target *an,
		   PHY_STATE_CTRL *pPhyStateCtrl)
{
	A_UINT8 i, j, hi = 0;
	A_UINT8 singleStream = (capflag & WLAN_RC_DS_FLAG) ? 0 : 1;
	A_UINT32 valid;
	struct atheros_node *pSib = ATH_NODE_ATHEROS(an);
       
	/* Use intersection of working rates and valid rates */
	for (i = 0; i < pRateSet->rs_nrates; i++) {
		for (j = 0; j < pRateTable->rateCount; j++) {
			A_UINT32 phy = pRateTable->info[j].phy;

#ifdef MAGPIE_MERLIN
			if (pSib->stbc) {
				valid = pRateTable->info[j].validSTBC;
			} else if (singleStream) {
#else
			if (singleStream) {
#endif            
				valid = pRateTable->info[j].validSingleStream;
			} else {
				valid = pRateTable->info[j].valid;
			}
        
			/*
			 * We allow a rate only if its valid and the capflag matches one of
			 * the validity (TRUE/TRUE_20/TRUE_40) flags
			 */

			if (((pRateSet->rs_rates[i] & 0x7F) == 
			     (pRateTable->info[j].dot11Rate & 0x7F))
			    && ((valid & WLAN_RC_CAP_MODE(capflag)) == 
				WLAN_RC_CAP_MODE(capflag)) && !WLAN_RC_PHY_HT(phy)) {
				if (!rcIsValidPhyRate(phy, capflag, FALSE)) 
					continue;

				pPhyStateCtrl->validPhyRateIndex[phy][pPhyStateCtrl->validPhyRateCount[phy]] = j;
				pPhyStateCtrl->validPhyRateCount[phy] += 1;

				rcSetValidTxMask(pRc, j, TRUE);
				hi = A_MAX(hi, j);
			}
		}
	}
  
	return hi;
}

static A_UINT8
rcSibSetValidHtRates(const RATE_TABLE_11N *pRateTable,
		     TX_RATE_CTRL *pRc, 
                     A_UINT8 *pMcsSet,
		     A_UINT32 capflag,
		     struct ath_node_target *an,
		     PHY_STATE_CTRL *pPhyStateCtrl)
{
	A_UINT8 i, j, hi = 0;
	A_UINT8 singleStream = (capflag & WLAN_RC_DS_FLAG) ? 0 : 1;
	A_UINT8 valid;
	struct atheros_node *pSib = ATH_NODE_ATHEROS(an);
    
	/* Use intersection of working rates and valid rates */
	for (i = 0; i <  ((struct ieee80211_rateset *)pMcsSet)->rs_nrates; i++) {
		for (j = 0; j < pRateTable->rateCount; j++) {
			A_UINT32 phy = pRateTable->info[j].phy;

#ifdef MAGPIE_MERLIN
			if (pSib->stbc) {
				valid = pRateTable->info[j].validSTBC;
			} else if (singleStream) {
#else
			if (singleStream) {
#endif
				valid = pRateTable->info[j].validSingleStream;
			} else {
				valid = pRateTable->info[j].valid;
			}
                           
			if (((((struct ieee80211_rateset *)pMcsSet)->rs_rates[i] & 0x7F) 
			     != (pRateTable->info[j].dot11Rate & 0x7F)) 
			    || !WLAN_RC_PHY_HT(phy) 
			    || !WLAN_RC_PHY_HT_VALID(valid, capflag)
			    || ((pRateTable->info[j].dot11Rate == 15) && 
				(valid & TRUE_20) && 
				(capflag & WLAN_RC_WEP_TKIP_FLAG)) )
			{
				continue;
			}
    
			if (!rcIsValidPhyRate(phy, capflag, FALSE)) 
				continue;
    
			pPhyStateCtrl->validPhyRateIndex[phy][pPhyStateCtrl->validPhyRateCount[phy]] = j;
			pPhyStateCtrl->validPhyRateCount[phy] += 1;

			rcSetValidTxMask(pRc, j, TRUE);
			hi = A_MAX(hi, j);
		}
	}

	return hi;
}

/*
 *  Update the SIB's rate control information
 *
 *  This should be called when the supported rates change
 *  (e.g. SME operation, wireless mode change)
 *
 *  It will determine which rates are valid for use.
 */
static void
rcSibUpdate_ht(struct ath_softc_tgt *sc, struct ath_node_target *an,
	       A_UINT32 capflag, A_BOOL keepState, struct ieee80211_rate  *pRateSet)
{
	RATE_TABLE_11N *pRateTable = 0;
	struct atheros_node *pSib = ATH_NODE_ATHEROS(an);
	struct atheros_softc *asc = (struct atheros_softc*)sc->sc_rc;
	A_UINT8 *phtMcs = (A_UINT8*)&pRateSet->htrates;
	TX_RATE_CTRL *pRc = (TX_RATE_CTRL *)(pSib);
	PHY_STATE_CTRL mPhyCtrlState;  

	A_UINT8 i, j, k, hi = 0, htHi = 0;

	pRateTable = (RATE_TABLE_11N*)asc->hwRateTable[sc->sc_curmode];

	/* Initial rate table size. Will change depending on the working rate set */
	pRc->rateTableSize = MAX_TX_RATE_TBL;

	/* Initialize thresholds according to the global rate table */
	for (i = 0 ; (i < pRc->rateTableSize) && (!keepState); i++) {
		pRc->state[i].per       = 0;
	}

	/* Determine the valid rates */
	rcInitValidTxMask(pRc);

	for (i = 0; i < WLAN_RC_PHY_MAX; i++) {
		for (j = 0; j < MAX_TX_RATE_PHY; j++) {
			mPhyCtrlState.validPhyRateIndex[i][j] = 0;
		}   
		mPhyCtrlState.validPhyRateCount[i] = 0;
	}

	pRc->rcPhyMode = (capflag & WLAN_RC_40_FLAG);

	if (pRateSet == NULL || !pRateSet->rates.rs_nrates) {
		/* No working rate, just initialize valid rates */
		hi = rcSibInitValidRates(pRateTable, pRc, capflag, &mPhyCtrlState);
	} else {
		/* Use intersection of working rates and valid rates */
		hi = rcSibSetValidRates(pRateTable, pRc, &(pRateSet->rates),
					capflag, an, &mPhyCtrlState);

		if (capflag & WLAN_RC_HT_FLAG) {
			htHi = rcSibSetValidHtRates(pRateTable, pRc, phtMcs,
						    capflag, an, &mPhyCtrlState);
		}

		hi = A_MAX(hi, htHi);
	}

	pRc->rateTableSize = hi + 1;
	pRc->rateMaxPhy    = 0;
    
	ASSERT(pRc->rateTableSize <= MAX_TX_RATE_TBL);

	for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) {
		for (j = 0; j < mPhyCtrlState.validPhyRateCount[i]; j++) {
			pRc->validRateIndex[k++] = mPhyCtrlState.validPhyRateIndex[i][j];
		}   

		if (!rcIsValidPhyRate(i, pRateTable->initialRateMax, TRUE) ||
		    !mPhyCtrlState.validPhyRateCount[i]) 
			continue;

		pRc->rateMaxPhy = mPhyCtrlState.validPhyRateIndex[i][j-1];	
	}
    
	ASSERT(pRc->rateTableSize <= MAX_TX_RATE_TBL);
	ASSERT(k <= MAX_TX_RATE_TBL);

	pRc->rateMaxPhy = pRc->validRateIndex[k-4];
	pRc->maxValidRate = k;

	rcSortValidRates(pRateTable, pRc);
}
static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
				size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	char *buf;
	unsigned int len = 0, max;
	int i = 0;
	ssize_t retval;

	if (sc->cur_rate_table == NULL)
		return 0;

	max = 80 + sc->cur_rate_table->rate_cnt * 1024;
	buf = kmalloc(max + 1, GFP_KERNEL);
	if (buf == NULL)
		return 0;
	buf[max] = 0;

	len += sprintf(buf, "%6s %6s %6s "
		       "%10s %10s %10s %10s\n",
		       "HT", "MCS", "Rate",
		       "Success", "Retries", "XRetries", "PER");

	for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) {
		u32 ratekbps = sc->cur_rate_table->info[i].ratekbps;
		struct ath_rc_stats *stats = &sc->debug.stats.rcstats[i];
		char mcs[5];
		char htmode[5];
		int used_mcs = 0, used_htmode = 0;

		if (WLAN_RC_PHY_HT(sc->cur_rate_table->info[i].phy)) {
			used_mcs = snprintf(mcs, 5, "%d",
				sc->cur_rate_table->info[i].ratecode);

			if (WLAN_RC_PHY_40(sc->cur_rate_table->info[i].phy))
				used_htmode = snprintf(htmode, 5, "HT40");
			else if (WLAN_RC_PHY_20(sc->cur_rate_table->info[i].phy))
				used_htmode = snprintf(htmode, 5, "HT20");
			else
				used_htmode = snprintf(htmode, 5, "????");
		}

		mcs[used_mcs] = '\0';
		htmode[used_htmode] = '\0';

		len += snprintf(buf + len, max - len,
			"%6s %6s %3u.%d: "
			"%10u %10u %10u %10u\n",
			htmode,
			mcs,
			ratekbps / 1000,
			(ratekbps % 1000) / 100,
			stats->success,
			stats->retries,
			stats->xretries,
			stats->per);
	}

	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
	kfree(buf);
	return retval;
}