/* *************************************************************************** * Ralink Tech Inc. * 4F, No. 2 Technology 5th Rd. * Science-based Industrial Park * Hsin-chu, Taiwan, R.O.C. * * (c) Copyright 2002-2004, Ralink Technology, Inc. * * All rights reserved. Ralink's source code is an unpublished work and the * use of a copyright notice does not imply otherwise. This source code * contains confidential trade secret material of Ralink Tech. Any attemp * or participation in deciphering, decoding, reverse engineering or in any * way altering the source code is stricitly prohibited, unless the prior * written consent of Ralink Technology, Inc. is obtained. *************************************************************************** Module Name: smartant.c Abstract: Smart Antenna related functions. Revision History: Who When What -------- ---------- ---------------------------------------------- */ #include "rt_config.h" #ifdef SMART_ANTENNA /* Following register offset definition used for GPIO95_72 */ #define BIT(_X) (_X) //(1 << (_X)) #define SA_HDR_PIN(_X, _Y) (((_X) << 8) | (_Y)) #define SA_REG_GPIO_BASE 0xb0000600 #define SA_REG_GPIO_MODE 0xb0000060 #define SA_REG_GPIO_95_72_DATA 0x98 #define SA_REG_GPIO_OFFSET_DIR 0x4 // When access the GPIO_XX_XX_DIR, need to add GPIO_XX_XX_DATA first #define SA_REG_GPIO_OFFSET_SET 0xc // When access the GPIO_XX_XX_SET, need to add GPIO_XX_XX_DATA first #define SA_REG_GPIO_OFFSET_RESET 0x10// When access the GPIO_XX_XX_RESET, need to add GPIO_XX_XX_DATA first #define SA_REG_GPIO_DIR_OUTPUT_MASK 0xfff #define SA_GPIO_72 BIT(0) // 1 #define SA_GPIO_73 BIT(1) // 2 #define SA_GPIO_74 BIT(2) // 4 #define SA_GPIO_75 BIT(3) // 8 #define SA_GPIO_76 BIT(4) // 10 #define SA_GPIO_77 BIT(5) // 20 #define SA_GPIO_78 BIT(6) // 40 #define SA_GPIO_79 BIT(7) // 80 #define SA_GPIO_80 BIT(8) // 100 #define SA_GPIO_81 BIT(9) // 200 #define SA_GPIO_82 BIT(10) // 400 #define SA_GPIO_83 BIT(11) // 800 #define GE2_TXD0 SA_GPIO_72 //1 #define GE2_TXD1 SA_GPIO_73 //2 #define GE2_TXD2 SA_GPIO_74 //4 #define GE2_TXD3 SA_GPIO_75 //8 #define GE2_TXEN SA_GPIO_76 //10 #define GE2_TXCLK SA_GPIO_77 //20 #define GE2_RXD0 SA_GPIO_78 //40 #define GE2_RXD1 SA_GPIO_79 //80 #define GE2_RXD2 SA_GPIO_80 //100 #define GE2_RXD3 SA_GPIO_81 //200 #define GE2_RXDV SA_GPIO_82 //400 #define GE2_RXCLK SA_GPIO_83 //800 #define SA_GPIO_VALUE_MASK (GE2_TXD1 |GE2_TXD3 | GE2_TXEN | GE2_RXD3 |GE2_RXD2 |GE2_RXD1 |GE2_RXD0 | GE2_RXDV | GE2_RXCLK) #define GND 0 #define VC 0 /* The NULL terminator for the agsp list */ static RTMP_SA_AGSP_MAP AGSP_ENTRY_END = {0,0,0}; static UINT32 AGSP_TRAIN_SEQ_END = 0; /* The default entry table for the agsp list */ static RTMP_SA_AGSP_MAP DefaultAGSP[]= { {1, 0, GND}, {2, SA_REG_GPIO_95_72_DATA, GE2_RXDV}, {3, SA_REG_GPIO_95_72_DATA, GE2_RXD1}, {4, SA_REG_GPIO_95_72_DATA, GE2_RXD3}, {5, SA_REG_GPIO_95_72_DATA, GE2_TXEN}, {6, SA_REG_GPIO_95_72_DATA, GE2_TXD1}, {7, SA_REG_GPIO_95_72_DATA, GE2_TXD3}, {0,0,0} }; /* By default configuration, RT3662 only use two txAntenna(tx0, tx1) */ static UINT32 DefaultTrainSeq[]={ SA_HDR_PIN(2,5)/* 0x410 */, SA_HDR_PIN(2,6) /* 0x402 */, SA_HDR_PIN(2,7) /* 0x408 */, SA_HDR_PIN(3,5)/* 0x090 */, SA_HDR_PIN(3,6) /* 0x082 */, SA_HDR_PIN(3,7) /* 0x88 */, SA_HDR_PIN(4,5)/* 0x210 */, SA_HDR_PIN(4,6) /* 0x202 */, SA_HDR_PIN(4,7) /* 0x208 */, 0}; static char *SA_MODE[]={"None", "Manual", "OneShot", "Auto"}; static char *phyMode[4]={"CCK", "OFDM", "HTMIX", "GF"}; static char *saStage[4]={"None", "Init","Confirm", "Monitor"}; VOID sa_switch_exec( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3); DECLARE_TIMER_FUNCTION(sa_switch_exec); BUILD_TIMER_FUNCTION(sa_switch_exec); /* When use this function 1. User shall guarantee the input "pSrc" is not NULL 1. This tokenParser will break the original content of the pSrc 2. If the token is parsing finished, the pSrc shall return as NULL 3. This function always return a non-NULL pointer to indicate some position of the pSrc 4. The return pointer may be a string with strlen() as 0 */ char *tokenParser(char **pSrc, char delim) { char *token, *pos; int i, len = strlen(*pSrc); token = pos = *pSrc; for (i = 0; i < len; i++) { if(pos[i] == delim) { pos[i] = '\0'; break; } } *pSrc = (( i != len) ? (*pSrc+i+1) : NULL); return token; } int rtstrtomac(RTMP_STRING *macStr, UCHAR *macAddr, char delimiter) { int mac_len, i; RTMP_STRING *ptr = macStr; /* Mac address acceptable format 01:02:03:04:05:06 length 17 */ if((mac_len = strlen(macStr)) < 17) { DBGPRINT(RT_DEBUG_ERROR, ("%s : invalid length (%d)\n", __FUNCTION__, mac_len)); return FALSE; } for (i = 0; i < MAC_ADDR_LEN; i++) { AtoH(ptr, &macAddr[i], 1); ptr = ptr + 3; } return TRUE; } BOOLEAN is_valid_agsp_entry( IN RTMP_ADAPTER *pAd, IN RTMP_SA_AGSP_MAP *pAgspEntry) { return TRUE; /* if (pAgspEntry->gpioBit <= 32) return FALSE; if (pAgspEntry->hdrPin > 255) return FALSE; if (pAgspEntry->regOffset) return FALSE; */ } BOOLEAN is_valid_train_entry( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pSAStaEntry) { MAC_TABLE_ENTRY *pMacEntry; // check mac address if (NdisEqualMemory(&pSAStaEntry->macAddr[0], ZERO_MAC_ADDR, MAC_ADDR_LEN)) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():pSAStaEntry->macAddr is all zero!\n", __FUNCTION__)); return FALSE; } // check mac entry pMacEntry = MacTableLookup(pAd, &pSAStaEntry->macAddr[0]); if (pMacEntry == NULL) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():Cannot found Sta(%02x:%02x:%02x:%02x:%02x:%02x) in MacTable\n", __FUNCTION__, PRINT_MAC(pSAStaEntry->macAddr))); return FALSE; } // check antPattern if ((pSAParam->saMode == SA_MODE_MANUAL) && (pSAStaEntry->curAntPattern == 0)) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():SATrainMode is manual but not set antPattern\n", __FUNCTION__)); return FALSE; } // TODO: Other check?? pSAStaEntry->pMacEntry = pMacEntry; return TRUE; } BOOLEAN is_ant_equal(UINT32 ant1, UINT32 ant2) { UINT32 swapAnt1; swapAnt1 = ((ant1 & 0xff) << 8 ) | ((ant1 >> 8) & 0xff); return ((ant1 == ant2) || (swapAnt1 == ant2)); } VOID reset_mac_entry_stats( IN MAC_TABLE_ENTRY *pEntry) { //NdisZeroMemory(pEntry->baseAvgRSSI, 3); NdisZeroMemory(pEntry->curRSSI, 3 * sizeof(UINT32)); NdisZeroMemory(pEntry->cntRSSI, 3 * sizeof(UINT32)); NdisZeroMemory(pEntry->rssi_zero_cnt, 3 * sizeof(UINT32)); NdisZeroMemory(pEntry->sumSNR, 3 * sizeof(INT32)); NdisZeroMemory(pEntry->cntSNR, 3 * sizeof(INT32)); NdisZeroMemory(pEntry->sumPreSNR, 3 * sizeof(INT32)); NdisZeroMemory(pEntry->cntPreSNR, 3 * sizeof(INT32)); pEntry->hwTxSucCnt = 0; pEntry->hwTxRtyCnt = 0; pEntry->saTxCnt = 0; pEntry->saRxCnt = 0; NdisZeroMemory(&pEntry->mcsUsage[0], 33 * sizeof(ULONG)); NdisGetSystemUpTime(&pEntry->calcTime); if (pEntry->HTPhyMode.field.MCS <= 32) { pEntry->mcsInUse = pEntry->HTPhyMode.field.MCS; pEntry->curMcsApplyTime = pEntry->calcTime; //printk("%s(): change the pEntry->curMcsApplyTime=0x%lx\n", __FUNCTION__, pEntry->curMcsApplyTime); } } VOID sa_calc_rx_signal_info( IN RTMP_ADAPTER *pAd, IN MAC_TABLE_ENTRY *pEntry) { if (pEntry->saRxCnt) { int i; INT32 avgRssi, avgSNR, avgPreSNR; #error "Need to fix ConvertToRssi() before compile this feature" for(i = 0 ; i < 3; i++) { if (pEntry->cntRSSI[i]) { pEntry->curAvgRSSI[i] = ConvertToRssi(pAd, pEntry->curRSSI[i] / pEntry->cntRSSI[i], i); pEntry->avgRssi[i] = pEntry->curAvgRSSI[i]; } else pEntry->avgRssi[i] = pEntry->curAvgRSSI[i] = 0; if (pEntry->cntSNR[i]) { avgSNR = pEntry->sumSNR[i] / pEntry->cntSNR[i]; pEntry->avgSNR[i] = (avgSNR * 1881) /10000; } else pEntry->avgSNR[i] = 0; if (pEntry->cntPreSNR[i]) { avgPreSNR = pEntry->sumPreSNR[i] / pEntry->cntPreSNR[i]; pEntry->avgPreSNR[i] = ((avgPreSNR / 4) + 22); } else pEntry->avgPreSNR[i] = 0; } DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("Wcid:%d RSSI=%d,%d,%d cntRSSI=%d,%d,%d, prevRssi=%d\n", pEntry->wcid, pEntry->avgRssi[0], pEntry->avgRssi[1], pEntry->avgRssi[2], pEntry->cntRSSI[0], pEntry->cntRSSI[1], pEntry->cntRSSI[2], pEntry->prevAvgRssi[0])); } } VOID sa_dump_stat_result( IN RTMP_ADAPTER *pAd, IN MAC_TABLE_ENTRY *pEntry) { INT32 HwPER = -1; int mcsIdx; unsigned long tv, iv; if (pEntry->hwTxSucCnt) HwPER = (pEntry->hwTxRtyCnt * 1000) / pEntry->hwTxSucCnt; printk("%02x:%02x:%02x:%02x:%02x:%02x ", PRINT_MAC(pEntry->Addr)); printk("%6d %6d %6d %6d\t%d", pEntry->saTxCnt, pEntry->hwTxSucCnt, pEntry->hwTxRtyCnt, pEntry->saRxCnt, HwPER); printk("\t%2d,%2d %2d,%2d\t", pEntry->avgSNR[0], pEntry->avgSNR[1], pEntry->avgRssi[0], pEntry->avgRssi[1]); // Update current mcs usage cnt first if ((pEntry->mcsInUse >= 0) && (pEntry->mcsInUse <= 32)) { NdisGetSystemUpTime(&tv); // if smaller than 1 jiffies, we give it one. if (tv >= pEntry->curMcsApplyTime) iv = tv - pEntry->curMcsApplyTime; else iv = 0xffffffff - pEntry->curMcsApplyTime + tv; if (iv == 0) iv++; pEntry->mcsUsage[pEntry->mcsInUse] += iv; //printk("%s(): Update the mcsUsage[%d]=%ld, TimeInterval=0x%lx-0x%lx!\n", // __FUNCTION__, pEntry->mcsInUse, pEntry->mcsUsage[pEntry->mcsInUse], // pEntry->curMcsApplyTime, tv); } // dump the result out for (mcsIdx = 0; mcsIdx < 33; mcsIdx++) { if (pEntry->mcsUsage[mcsIdx] != 0) { tv = (pEntry->mcsUsage[mcsIdx] * 1000 / OS_HZ); printk("%d:%ld,", mcsIdx, tv); } } printk("\n"); #if 0 printk("\tPreSNR[%d]:%2d(%d-%d), PreSNR[%d]:%2d(%d-%d), PreSNR[%d]:%2d(%d-%d)\n", 0, pEntry->avgPreSNR[0], pEntry->sumPreSNR[0], pEntry->cntPreSNR[0], 1, pEntry->avgPreSNR[1], pEntry->sumPreSNR[1], pEntry->cntPreSNR[1], 2, pEntry->avgPreSNR[2], pEntry->sumPreSNR[2], pEntry->cntPreSNR[2]); #endif printk("\n"); } VOID sa_dump_agsp(RTMP_SA_AGSP_MAP *pAgspList) { RTMP_SA_AGSP_MAP *pAgspEntry; int cnt; pAgspEntry = pAgspList; DBGPRINT(RT_DEBUG_OFF, ("Start Dump AGSP List:\n")); cnt = 0; DBGPRINT(RT_DEBUG_OFF, ("\tHeaderPin GPIO RegOffset GPIOBit\n")); while (memcmp(pAgspEntry, &AGSP_ENTRY_END, sizeof(RTMP_SA_AGSP_MAP))!= 0) { DBGPRINT(RT_DEBUG_OFF, ("\t\t%d\t0x%x\t%d\n", pAgspEntry->hdrPin, pAgspEntry->regOffset,pAgspEntry->gpioBit)); pAgspEntry++; } } VOID sa_dump_train_seq(UINT32 *pTrainSeq) { UINT32 *pAntPair = pTrainSeq; int dbgcnt = 0; DBGPRINT(RT_DEBUG_OFF,("Start Dump SA TrainingSequence:\n")); while(*pAntPair != AGSP_TRAIN_SEQ_END) { DBGPRINT(RT_DEBUG_OFF, ("\t0x%x(%d, %d)\n", *pAntPair, (*pAntPair & 0xff), ((*pAntPair>>8) & 0xff))); pAntPair++; dbgcnt++; if(dbgcnt > 255) break; } } VOID sa_dump_train_log( IN RTMP_ADAPTER *pAd, IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN RTMP_SA_TRAIN_LOG_ELEMENT *pTrainLog, IN UCHAR txNss) { MAC_TABLE_ENTRY *pMacEntry; int i, j; int avgRssi, avgSNR, avgPreSNR; pMacEntry = pTrainEntry->pMacEntry; if (pMacEntry) { DBGPRINT(RT_DEBUG_OFF,("SmartAntTrainLogDump(Ant:0x%x-%d,TimeInterval:0x%lx-0x%lx)\n", pTrainLog->antPattern, pTrainLog->patternOffset, pTrainLog->srtTime, pTrainLog->endTime)); DBGPRINT(RT_DEBUG_OFF,("\tBW:MCS=%d:%d, txMcs=%d\n", pMacEntry->HTPhyMode.field.BW, pMacEntry->HTPhyMode.field.MCS, pTrainLog->txMcs)); DBGPRINT(RT_DEBUG_OFF,("\ttxCnt=%d,noRtyCnt=%d,reTry=%d,rtyFail=%d,PER=%d!\n", pTrainLog->txCnt,pTrainLog->txNoRtyCnt, pTrainLog->txRtyCnt, pTrainLog->txRtyFailCnt, pTrainLog->PER)); if (pTrainLog->rxCnt) { int i; #error "Need to fix ConvertToRssi() before compile this feature" DBGPRINT(RT_DEBUG_OFF,("\tRxCnt=%d\n", pTrainLog->rxCnt)); for(i = 0 ; i < txNss; i++) { avgRssi = avgSNR = avgPreSNR = 0; if (pTrainLog->sumRSSI[i]) { avgRssi = pTrainLog->sumRSSI[i] / pTrainLog->cntRSSI[i]; } if (pTrainLog->cntSNR[i]) { avgSNR = pTrainLog->sumSNR[i] / pTrainLog->cntSNR[i]; } if (pTrainLog->cntPreSNR[i]) { avgPreSNR = pTrainLog->sumPreSNR[i] / pTrainLog->cntPreSNR[i]; } DBGPRINT(RT_DEBUG_OFF,("\t\tAnt%d:Rssi=%d(%d), SNR=%d(%d), preSNR=%d(%d)\n", i, avgRssi, ConvertToRssi(pAd, avgRssi, i), avgSNR, ((avgSNR * 1881) /10000), avgPreSNR, ((avgPreSNR / 4) + 22))); } } else { DBGPRINT(RT_DEBUG_OFF,("\tNo valid RxCnt!\n")); } #ifdef SA_DBG if (pAd->smartAntDbgOn) { DBGPRINT(RT_DEBUG_OFF,("\tRssi Distribution:\n")); for (i = 0 ; i < txNss; i++) { DBGPRINT(RT_DEBUG_OFF,("\t\tRSSI-%d:\n\t\t\t", i)); for ( j =0 ; j < 33; j++) { if (j == 16) { DBGPRINT(RT_DEBUG_OFF,("\n\t\t\t")); } DBGPRINT(RT_DEBUG_OFF,("%6d", pTrainLog->rssiDist[i][j])); } DBGPRINT(RT_DEBUG_OFF, ("\n")); } DBGPRINT(RT_DEBUG_OFF, ("\tSNR Distribution:\n")); for (i = 0 ; i < txNss; i++) { DBGPRINT(RT_DEBUG_OFF, ("\t\tSNR-%d:\n\t\t\t", i)); for ( j =0 ; j < 33; j++) { if (j == 16) { DBGPRINT(RT_DEBUG_OFF, ("\n\t\t\t")); } DBGPRINT(RT_DEBUG_OFF, ("%6d", pTrainLog->SNRDist[i][j])); } DBGPRINT(RT_DEBUG_OFF,("\n")); } DBGPRINT(RT_DEBUG_OFF, ("\tBF_SNR Distribution:\n")); for (i = 0 ; i < txNss; i++) { DBGPRINT(RT_DEBUG_OFF, ("\t\tBF_SNR-%d:\n\t\t\t", i)); for ( j =0 ; j < 33; j++) { if (j == 16) { DBGPRINT(RT_DEBUG_OFF, ("\n\t\t\t")); } DBGPRINT(RT_DEBUG_OFF,("%6d", pTrainLog->preSNRDist[i][j])); } DBGPRINT(RT_DEBUG_OFF, ("\n")); } } #endif // SA_DBG // DBGPRINT(RT_DEBUG_OFF, ("\n\n")); } } VOID reset_trainlog_stats_record( IN RTMP_SA_TRAIN_LOG_ELEMENT *pTrainLog) { unsigned int offset, cleanLen; offset = (unsigned int)(&(((RTMP_SA_TRAIN_LOG_ELEMENT *)NULL)->txCnt)); cleanLen = sizeof(RTMP_SA_TRAIN_LOG_ELEMENT) - offset; NdisZeroMemory((UCHAR *)(&pTrainLog->txCnt), cleanLen); } int sa_rssi_chk_variant( IN MAC_TABLE_ENTRY *pMacEntry, IN UCHAR antCnt, IN UCHAR diffVal) { return TRUE; } BOOLEAN get_gpio_by_hdr_pins( IN RTMP_SA_AGSP_MAP *pAgspList, IN UCHAR hdrPin, OUT SA_GPIO_PAIR *gpioPair) { RTMP_SA_AGSP_MAP *pAgspEntry = pAgspList; while (pAgspEntry->hdrPin != 0) { if (pAgspEntry->hdrPin == hdrPin) { gpioPair->regAddr = pAgspEntry->regOffset; gpioPair->bitMask = (1<< pAgspEntry->gpioBit); return TRUE; } pAgspEntry++; } return FALSE; } INT32 sa_get_offset_by_antp( IN SMART_ANTENNA_STRUCT *pSAParam, IN UINT32 antPattern) { UINT32 *pTrainSeq; UINT32 revPattern = 0, tmpAnt; INT32 offset; UCHAR pinVal; pTrainSeq = pSAParam->pTrainSeq; if (pTrainSeq == NULL) return -1; revPattern = 0; tmpAnt = antPattern; do{ pinVal = (tmpAnt & 0xff); revPattern = ((revPattern << 8) | pinVal); tmpAnt >>= 8; }while(tmpAnt); #if 0 DBGPRINT(RT_DEBUG_OFF, ("%s(): Try find antPattern(0x%x-0x%x) in TrainSeq(0x%x, cnt=%d)\n", __FUNCTION__, antPattern, revPattern, *pTrainSeq, pSAParam->trainSeqCnt)); #endif offset = 0; do{ #if 0 DBGPRINT(RT_DEBUG_OFF, ("%s():check pTrainSeq(0x%x) at offset(%d)\n", __FUNCTION__, *(pTrainSeq + offset), offset)); #endif if ((*(pTrainSeq + offset) == antPattern) || (*(pTrainSeq + offset) == revPattern)) { //printk("\t%s():Found it at offset(%d)\n", __FUNCTION__, offset); return offset; } offset++; } while(offset < pSAParam->trainSeqCnt); return -1; } int sa_gpio_write( IN RTMP_ADAPTER *pAd, IN UINT32 oldAntCfg, IN UINT32 newAntCfg) { UINT32 regVal = 0, regAddr; /* Check the gpio setting first */ RTMP_SYS_IO_READ32(SA_REG_GPIO_MODE, ®Val); if ((regVal & 0x400) != 0x400) { regVal |= 0x400; RTMP_SYS_IO_WRITE32(SA_REG_GPIO_MODE, regVal); DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA,("Change as GPIO Mode(0x%x)\n", regVal)); RTMP_SYS_IO_READ32(SA_REG_GPIO_MODE, ®Val); DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA,("After Change, now GPIO_MODE value is 0x%x\n", regVal)); } /* Check the gpio direction */ regAddr = SA_REG_GPIO_BASE + SA_REG_GPIO_95_72_DATA + SA_REG_GPIO_OFFSET_DIR; RTMP_SYS_IO_READ32(regAddr, ®Val); if ((regVal & SA_REG_GPIO_DIR_OUTPUT_MASK) != SA_REG_GPIO_DIR_OUTPUT_MASK) { RTMP_SYS_IO_WRITE32(regAddr, (regVal | SA_REG_GPIO_DIR_OUTPUT_MASK)); DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA,("The GPIO output direction value is 0x%x, change to 0x%x!\n", regVal, regVal | SA_REG_GPIO_DIR_OUTPUT_MASK)); } /* Read the original GPIO value first */ regAddr = SA_REG_GPIO_BASE + SA_REG_GPIO_95_72_DATA; RTMP_SYS_IO_READ32(regAddr, ®Val); //DBGPRINT(RT_DEBUG_OFF, ("OldGPIOVal=0x%x, oldAntCfg=0x%x, newAntCfg=0x%x!\n",regVal, oldAntCfg, newAntCfg)); #if 0 regVal &= SA_REG_GPIO_DIR_OUTPUT_MASK; /* set the new antenna */ newAntCfg &= SA_REG_GPIO_DIR_OUTPUT_MASK; RTMP_SYS_IO_WRITE32(regAddr + SA_REG_GPIO_OFFSET_SET, newAntCfg); // clear the old antenna setting if (regVal) { regVal &= (~newAntCfg); DBGPRINT(RT_DEBUG_OFF, ("Clean the oldAnt(addr=0x%x, val=0x%x)\n", regAddr + SA_REG_GPIO_OFFSET_RESET, regVal)); RTMP_SYS_IO_WRITE32(regAddr + SA_REG_GPIO_OFFSET_RESET, regVal); } #else regVal &= (~SA_REG_GPIO_DIR_OUTPUT_MASK); newAntCfg &= SA_REG_GPIO_DIR_OUTPUT_MASK; regVal |= newAntCfg; RTMP_SYS_IO_WRITE32(regAddr, regVal); DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA, ("Write the AntPattern(addr=0x%x, val=0x%x)\n", regAddr, regVal)); #endif RTMP_SYS_IO_READ32(regAddr, ®Val); //DBGPRINT(RT_DEBUG_OFF, ("After Cfg, GPIOVal=0x%x!\n",regVal)); return TRUE; } /* When use this function, please make sure the pSAParam is valid! */ UINT32 sa_get_ant_by_gpio( IN SMART_ANTENNA_STRUCT *pSAParam) { RTMP_SA_AGSP_MAP *pAgspEntry; UINT32 regVal, regAddr, antPattern, bitMask; int antCnt; if (pSAParam->agsp == NULL) return 0; /* Check the gpio setting first */ RTMP_SYS_IO_READ32(SA_REG_GPIO_MODE, ®Val); if ((regVal & 0x400) != 0x400) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():Error- GPIOMode(0x%x) not set as GPIO mode\n", __FUNCTION__, regVal)); return 0; } /* Check the gpio direction */ regAddr = SA_REG_GPIO_BASE + SA_REG_GPIO_95_72_DATA + SA_REG_GPIO_OFFSET_DIR; RTMP_SYS_IO_READ32(regAddr, ®Val); if ((regVal & SA_REG_GPIO_DIR_OUTPUT_MASK) != SA_REG_GPIO_DIR_OUTPUT_MASK) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():Error- GPIODirection(0x%x) not set as output\n", __FUNCTION__, regVal)); return 0; } /* Write the new cfg first */ regAddr = SA_REG_GPIO_BASE + SA_REG_GPIO_95_72_DATA; RTMP_SYS_IO_READ32(regAddr, ®Val); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s(): The GPIO output is 0x%x\n", __FUNCTION__, regVal)); antCnt = antPattern = 0; pAgspEntry = pSAParam->agsp; while((antCnt < pSAParam->txNss) && (pAgspEntry->hdrPin != 0)) { bitMask = 1<< (pAgspEntry->gpioBit); if (regVal & bitMask) { antPattern |= (pAgspEntry->hdrPin) << (antCnt * 8); antCnt++; } pAgspEntry++; } DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s(): The antPattern is 0x%x\n", __FUNCTION__, antPattern)); return antPattern; } /* when call this function, please make sure following parameters are valid @pSAParm @pTrainEntry */ BOOLEAN sa_get_curAntPattern( IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry) { UINT32 antPattern; INT32 ret; antPattern = sa_get_ant_by_gpio(pSAParam); if (antPattern != 0) { ret = sa_get_offset_by_antp(pSAParam, antPattern); if (ret >=0) { pTrainEntry->patternOffset = ret; pTrainEntry->curAntPattern = antPattern; return TRUE; } else { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():Cannot found antPattern(0x%x) in TrainSeq\n", __FUNCTION__, antPattern)); } } else { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s(): The antPattern get from GPIO is invalid\n", __FUNCTION__)); } pTrainEntry->curAntPattern = 0; pTrainEntry->patternOffset = 0; return FALSE; } /* when call this function, please make sure following parameters are valid @pSAParm @pTrainEntry */ BOOLEAN sa_get_nextAntPattern( IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN UCHAR weight, IN UINT32 selectRule) { UINT32 *pTrainSeq; RTMP_SA_TRAIN_LOG_ELEMENT *pCurLog, *pBaseInfo = &pTrainEntry->antBaseInfo; pTrainSeq = pSAParam->pTrainSeq; if (pTrainSeq == NULL) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():TrainSeq is NULL\n", __FUNCTION__)); return FALSE; } DBGPRINT(RT_DEBUG_ERROR, ("%s():CurAnt(0x%x-%d),GetNextAntBy(Rule:%d, Weight:%d)\n", __FUNCTION__, pTrainEntry->curAntPattern, pTrainEntry->patternOffset,selectRule, weight)); check: /* If the pTrainEntry->curAntPattern is 0, use the first one */ if (pTrainEntry->curAntPattern) pTrainEntry->patternOffset++; else pTrainEntry->patternOffset = 0; if (pTrainEntry->patternOffset >= pSAParam->trainSeqCnt) { pTrainEntry->curAntPattern = 0; pTrainEntry->patternOffset = 0; } else { pTrainEntry->curAntPattern = *(pTrainSeq + pTrainEntry->patternOffset); } // check weight when weight > 0 pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); DBGPRINT(RT_DEBUG_ERROR, ("\tCheck Ant(0x%x-%d), AntTrainLog->antWeight(%d)\n", pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pCurLog->antWeight)); if (weight && (pTrainEntry->curAntPattern != 0)) { if (pCurLog->antWeight > weight) { if (is_ant_equal(pTrainEntry->curAntPattern, pCurLog->antPattern) == FALSE) { DBGPRINT(RT_DEBUG_ERROR, ("\t\tGPIO of CandAnt(0x%x-%d) in TSeq(0x%x) and TLog(0x%x) are mismatch\n", pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pCurLog->antPattern, pCurLog->patternOffset)); sa_dump_train_seq(pSAParam->pTrainSeq); //sa_dump_train_log(pAd, pTrainEntry, pTrainEntry->pTrainInfo, pSAParam->txNss); pTrainEntry->curAntPattern = 0; pTrainEntry->patternOffset = 0; } DBGPRINT(RT_DEBUG_ERROR, ("\t\tAnt(0x%x-%d) weight(%d) > required(%d), ignore\n", pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pCurLog->antWeight, weight)); goto check; } else { BOOLEAN bTryNext = FALSE; switch (selectRule) { case ANT_SELECT_IGNORE_BASE: if (is_ant_equal(pBaseInfo->antPattern, pCurLog->antPattern)) { DBGPRINT(RT_DEBUG_ERROR, ("\t\tByRule(%d), Ant(0x%x-%d) == BaseAnt(0x%x-%d), ignore\n", selectRule,pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pBaseInfo->antPattern, pBaseInfo->patternOffset)); bTryNext = TRUE; } break; case ANT_SELECT_BASE: if (is_ant_equal(pBaseInfo->antPattern, pCurLog->antPattern) == FALSE) { DBGPRINT(RT_DEBUG_ERROR, ("\t\tByRule(%d), Ant(0x%x-%d) != BaseAnt(0x%x-%d), ignore\n", selectRule, pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pBaseInfo->antPattern, pBaseInfo->patternOffset)); bTryNext = TRUE; } break; default: /* No limition */ break; } if (bTryNext) goto check; } } if ((pTrainEntry->curAntPattern == 0) && (selectRule == ANT_SELECT_BASE)) { goto check; } DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\t=>SelectedAnt=0x%x-%d, weight=%d!\n", pTrainEntry->curAntPattern, pTrainEntry->patternOffset, weight)); return TRUE; } INT sa_set_antPattern( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN UINT32 prevAnt, IN UINT32 newAnt) { int i, cnt = 0; UCHAR pinVal[SA_TX_NSS_MAX_NUM]; SA_GPIO_PAIR gpioPair[SA_TX_NSS_MAX_NUM]; UINT32 oldCfg = 0, newCfg = 0; while (newAnt > 0) { pinVal[cnt] = (newAnt & 0xff); cnt++; newAnt >>= (cnt*8); } NdisZeroMemory(&gpioPair[0], sizeof(gpioPair)); for (i = 0; i < cnt; i++) { // TODO: We need to take care if the previous GPIO register is not the same with current one /* Here we allow the antPattern is 0-0, which it means disable all antennas */ if (get_gpio_by_hdr_pins(pSAParam->agsp, pinVal[i], &gpioPair[i]) == TRUE) { newCfg |= gpioPair[i].bitMask; } pinVal[i] = ((prevAnt >>(i*8)) & 0xff); if (get_gpio_by_hdr_pins(pSAParam->agsp, pinVal[i], &gpioPair[i]) == TRUE) { oldCfg |= gpioPair[i].bitMask; } } //DBGPRINT(RT_DEBUG_OFF, ("newCfg=0x%x, oldCfg=0x%x\n", newCfg, oldCfg)); sa_gpio_write(pAd, oldCfg, newCfg); return TRUE; } INT sa_set_ant_weight( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN UINT32 antPattern, IN UCHAR weight) { RTMP_SA_TRAIN_LOG_ELEMENT *pCurLog; BOOLEAN bSetAll = FALSE; int cnt; if (antPattern == 0) { bSetAll = TRUE; } DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA, ("%s():set weight(%d) for antenna pattern(0x%x)\n", __FUNCTION__, weight, antPattern)); for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); if (is_ant_equal(pCurLog->antPattern, antPattern) || (bSetAll == TRUE)) { pCurLog->antWeight = weight; if (bSetAll == FALSE) break; } } if ((RTDebugFunc & DBG_FUNC_SA) == DBG_FUNC_SA) { for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); DBGPRINT(RT_DEBUG_OFF, ("\tantPattern:0x%x, weight:%d\n", pCurLog->antPattern, pCurLog->antWeight)); } } return TRUE; } INT sa_init_train_log( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN UCHAR weight) { RTMP_SA_TRAIN_LOG_ELEMENT *pCurLog; int cnt; UINT32 *pTrainSeq; if (pTrainEntry->pTrainInfo == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("%s():pTrainInfo is NULL\n", __FUNCTION__)); return FALSE; } NdisZeroMemory(pTrainEntry->pTrainInfo, sizeof(RTMP_SA_TRAIN_LOG_ELEMENT) * pSAParam->trainSeqCnt); for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); pTrainSeq = (UINT32 *)(pSAParam->pTrainSeq + cnt); pCurLog->antPattern = *pTrainSeq; pCurLog->patternOffset = cnt; pCurLog->antWeight = weight; pCurLog->candWeight = weight; } return TRUE; } /* @pAd: Device control block pointer @pMacAddr: the mac address used for condition to find the entry If pMacAddr == BROADCAST_ADDR, find the first non-static assigned train entry If pMacAddr == ZERO, find first empty train entry If pMacAddr == Unicast Addr, find the entry which has the same macAddr return value: If found, return the entry pointer, else return NULL */ RTMP_SA_TRAINING_PARAM *sa_find_train_entry( IN RTMP_ADAPTER *pAd, IN UCHAR *pMacAddr) { RTMP_SA_TRAINING_PARAM *pTrainEntry = NULL; SMART_ANTENNA_STRUCT *pSAParam = pAd->pSAParam; int idx; BOOLEAN bAllowDynamic = FALSE; if (NdisEqualMemory(pMacAddr, &BROADCAST_ADDR[0], MAC_ADDR_LEN)) bAllowDynamic = TRUE; for (idx = 0; idx < SA_ENTRY_MAX_NUM; idx++) { pTrainEntry = &pSAParam->trainEntry[idx]; if (NdisEqualMemory(pMacAddr, &pTrainEntry->macAddr[0], MAC_ADDR_LEN)) break; if ((bAllowDynamic == TRUE) && (pTrainEntry->bStatic == FALSE)) break; } return ((idx < SA_ENTRY_MAX_NUM) ? pTrainEntry : NULL); } INT sa_init_train_entry( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN RTMP_SA_TRAIN_LOG_ELEMENT *pTrainLog, IN UCHAR *pMacAddr) { unsigned int cleanLen, offset; MAC_TABLE_ENTRY *pMacEntry; /* Here we don't clean following fields: macAddr, bStatic, pTrainInfo */ offset = (unsigned int)(&(((RTMP_SA_TRAINING_PARAM *)NULL)->pMacEntry)); cleanLen = sizeof(RTMP_SA_TRAINING_PARAM) - offset; NdisZeroMemory((PUCHAR)(&pTrainEntry->pMacEntry), cleanLen); DBGPRINT(RT_DEBUG_OFF, ("%s():sizeof(RTMP_SA_TRAINING_PARAM)=%d, offset=%d, cleanLen=%d!\n", __FUNCTION__, sizeof(RTMP_SA_TRAINING_PARAM), offset, cleanLen)); // some necessary defualt setting for TrainEntry pTrainEntry->trainStage = SA_INVALID_STAGE; if (pMacAddr) NdisCopyMemory(&pTrainEntry->macAddr[0], pMacAddr, MAC_ADDR_LEN); #if 0 if (pTrainLog) pTrainEntry->pTrainInfo = pTrainLog; #endif // find and update associated mac table entry pMacEntry = MacTableLookup(pAd, &pTrainEntry->macAddr[0]); if (pMacEntry) { pTrainEntry->pMacEntry = pMacEntry; pMacEntry->pTrainEntry = (void *)pTrainEntry; } // init the train log related data structures sa_init_train_log(pAd, pSAParam, pTrainEntry, ANT_WEIGHT_SCAN_ALL); return TRUE; } RTMP_SA_TRAINING_PARAM *sa_add_train_entry( IN RTMP_ADAPTER *pAd, IN UCHAR *pMacAddr, IN BOOLEAN bAddByUser) { RTMP_SA_TRAINING_PARAM *pTrainEntry = NULL; // check if existing entry already has this mac pTrainEntry = sa_find_train_entry(pAd, pMacAddr); // If no existing entry for this mac, find an empty train entry if (pTrainEntry == NULL) pTrainEntry = sa_find_train_entry(pAd, &ZERO_MAC_ADDR[0]); // if no emptry entry, then select first non-static if ((pTrainEntry == NULL) && (bAddByUser == TRUE)) pTrainEntry = sa_find_train_entry(pAd, &BROADCAST_ADDR[0]); if (pTrainEntry) { sa_init_train_entry(pAd, pAd->pSAParam, pTrainEntry, pTrainEntry->pTrainInfo, pMacAddr); if (bAddByUser) pTrainEntry->bStatic = bAddByUser; } return pTrainEntry; } BOOLEAN sa_del_train_entry( IN RTMP_ADAPTER *pAd, IN UCHAR *pMacAddr, IN BOOLEAN bForced) { int idx; SMART_ANTENNA_STRUCT *pSAParam = pAd->pSAParam; RTMP_SA_TRAINING_PARAM *pTrainEntry; MAC_TABLE_ENTRY *pMacEntry; BOOLEAN bDeleted = FALSE; if (pMacAddr) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("%s():Try to del mac(%02x:%02x:%02x:%02x:%02x:%02x), bForced=%d!\n", __FUNCTION__, PRINT_MAC(pMacAddr), bForced)); } for (idx = 0; idx < SA_ENTRY_MAX_NUM; idx++) { pTrainEntry = &pSAParam->trainEntry[idx]; if (NdisEqualMemory(&pTrainEntry->macAddr[0], pMacAddr, MAC_ADDR_LEN)) { unsigned int cleanLen, offset; PUCHAR cleanPtr; DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("%s():find matched trainEntry(Idx=%d), bTraining=%d!\n", __FUNCTION__, idx, pTrainEntry->bTraining)); if (pTrainEntry->pMacEntry) { pMacEntry = pTrainEntry->pMacEntry; pMacEntry->pTrainEntry = NULL; } if (pTrainEntry->bTraining == TRUE) { /* recovery to original antenna */ sa_get_curAntPattern(pSAParam, pTrainEntry); sa_set_antPattern(pAd, pSAParam, pTrainEntry->curAntPattern, pTrainEntry->antBaseInfo.antPattern); } /* Here we don't clean following fields: pTrainInfo */ if ((bForced==TRUE) || (pTrainEntry->bStatic == FALSE)) { offset = (unsigned int)(&(((RTMP_SA_TRAINING_PARAM *)NULL)->macAddr)); cleanPtr = (PUCHAR)(&pTrainEntry->macAddr[0]); } else { offset = (unsigned int)(&(((RTMP_SA_TRAINING_PARAM *)NULL)->pMacEntry)); cleanPtr = (PUCHAR)(&pTrainEntry->pMacEntry); } cleanLen = sizeof(RTMP_SA_TRAINING_PARAM) - offset; NdisZeroMemory(cleanPtr, cleanLen); bDeleted = TRUE; break; } } DBGPRINT(RT_DEBUG_OFF, ("%s(): delete %s\n", __FUNCTION__, (bDeleted == TRUE ? "success" : "failed"))); return bDeleted; } BOOLEAN sa_select_target_train_entry( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam) { MAC_TABLE_ENTRY *pEntry, *pCandEntry; RTMP_SA_TRAINING_PARAM *pTrainEntry; int macIdx; if (pSAParam == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("%s():pSAParam is NULL\n", __FUNCTION__)); return FALSE; } pTrainEntry = &pSAParam->trainEntry[0]; if (pTrainEntry->bStatic == TRUE) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("%s():static target,ignore the selection!\n",__FUNCTION__)); return FALSE; } pCandEntry = NULL; for (macIdx = 0; macIdx < MAX_LEN_OF_MAC_TABLE; macIdx++) { pEntry = &pAd->MacTab.Content[macIdx]; if (IS_ENTRY_CLIENT(pEntry) && (pEntry->Sst == SST_ASSOC)) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("STA(%02x:%02x:%02x:%02x:%02x:%02x): RSSI=%d\n", PRINT_MAC(pEntry->Addr), pEntry->avgRssi[0])); /* ignore the station which has no RSSI result yet */ if (pEntry->avgRssi[0] == 0) continue; /* Check if need to change the candidate */ if (pCandEntry) { if (pCandEntry->avgRssi[0] < pSAParam->rssiThreshold) { /* We only care the stations which RSSI larger than threshold */ if (pEntry->avgRssi[0] > pCandEntry->avgRssi[0]) pCandEntry = pEntry; } else { if (pEntry->avgRssi[0] >= pSAParam->rssiThreshold) { // both sta has rssi larger than thold, select the smaller one. if (pCandEntry->avgRssi[0] > pEntry->avgRssi[0]) pCandEntry = pEntry; } } } else { pCandEntry = pEntry; } } } if (pCandEntry) { DBGPRINT(RT_DEBUG_OFF, ("After Selection, target TrainEntry is %02x:%02x:%02x:%02x:%02x:%02x(RSSI=%d)\n", PRINT_MAC(pCandEntry->Addr), pCandEntry->avgRssi[0])); sa_init_train_entry(pAd, pSAParam, pTrainEntry, pTrainEntry->pTrainInfo, &pCandEntry->Addr[0]); return TRUE; } else { DBGPRINT(RT_DEBUG_OFF, ("After Selection, Cannot find target TrainEntry\n")); return FALSE; } } UCHAR sa_get_trainup_per_by_mcs(UCHAR *pRateTb, UCHAR mcs) { int tbIdx, tbSize, entryStep; RTMP_RA_LEGACY_TB *pCurrTxRate; if (!pRateTb) return 0; #ifdef NEW_RATE_ADAPT_SUPPORT if (ADAPT_RATE_TABLE(pRateTb)) { entryStep = 10; } else #endif /* NEW_RATE_ADAPT_SUPPORT */ { entryStep = 5; } tbSize = pRateTb[0]; for (tbIdx = 0; tbIdx < tbSize; tbIdx++) { pCurrTxRate = (RTMP_RA_LEGACY_TB *) &pRateTb[(tbIdx+1)*entryStep]; if (pCurrTxRate->CurrMCS == mcs) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("The trainUp PER of MCS(%d) is %d\n", mcs, pCurrTxRate->TrainUp)); return pCurrTxRate->TrainUp; } } return 0; } int sa_train_db_exit( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam) { int i; DBGPRINT(RT_DEBUG_OFF,("%s()--->\n", __FUNCTION__)); /* Clean the old debug related structure first */ for (i = 0 ; i < SA_ENTRY_MAX_NUM; i++) pSAParam->trainEntry[i].pTrainInfo = NULL; if (pSAParam->pTrainMem) { os_free_mem(pAd, pSAParam->pTrainMem); pSAParam->pTrainMem = NULL; } /* Clean the TrainSeq list */ pSAParam->trainSeqCnt = 0; if (pSAParam->pTrainSeq && (pSAParam->pTrainSeq != &DefaultTrainSeq[0])) { os_free_mem(pAd, pSAParam->pTrainSeq); } pSAParam->pTrainSeq = NULL; DBGPRINT(RT_DEBUG_OFF,("<---%s():\n", __FUNCTION__)); return TRUE; } int sa_train_db_init( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN UINT32 *pTrainSeqLst) { //RTMP_SA_TRAINING_PARAM *pTrainEntry; UINT32 *ptr; int memSize, cnt = 0; ASSERT(pTrainSeqLst); ptr = pTrainSeqLst; while(*ptr != AGSP_TRAIN_SEQ_END) { cnt++; ptr++; } if (cnt == 0) { DBGPRINT(RT_DEBUG_OFF,("Error:TrainSeqCnt is zero!\n")); return FALSE; } DBGPRINT(RT_DEBUG_OFF,("%s():TrainCnt=%d\n", __FUNCTION__, cnt)); /* Clean the old debug related structure first */ sa_train_db_exit(pAd, pSAParam); /* Allocate memory for TrainMem */ memSize = sizeof(RTMP_SA_TRAIN_LOG_ELEMENT) * cnt; os_alloc_mem(pAd, (UCHAR **)&pSAParam->pTrainMem, memSize * SA_ENTRY_MAX_NUM); if (pSAParam->pTrainMem == NULL) { DBGPRINT(RT_DEBUG_OFF,("%s():malloc failed\n", __FUNCTION__)); return FALSE; } DBGPRINT(RT_DEBUG_OFF,("AllocTrainMemDone(addr=0x%lx,entrySize=%d,totalSize=%d)\n", (ULONG)pSAParam->pTrainMem, sizeof(RTMP_SA_TRAIN_LOG_ELEMENT), memSize * SA_ENTRY_MAX_NUM)); /* Set new parameters */ pSAParam->pTrainSeq = pTrainSeqLst; pSAParam->trainSeqCnt = cnt; /* Init the TrainEntry associated TrainLogInfo space */ for (cnt = 0; cnt < SA_ENTRY_MAX_NUM; cnt++) { pSAParam->trainEntry[cnt].pTrainInfo = pSAParam->pTrainMem + (pSAParam->trainSeqCnt * cnt); } // dump it out for debug DBGPRINT(RT_DEBUG_OFF, ("\tTrainMemoryLayout:\n")); for (cnt = 0; cnt < SA_ENTRY_MAX_NUM; cnt++) { DBGPRINT(RT_DEBUG_OFF,("\t\ttrainInfo[%d]=%p\n", cnt, pSAParam->trainEntry[cnt].pTrainInfo)); } return TRUE; } INT sa_cand_method_2( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry) { RTMP_SA_TRAIN_LOG_ELEMENT *pCurLog, *pCanLog2; INT32 PER[9]={0}; UINT32 txCnt[9]={0}, txRtyCnt[9]={0}; UINT32 curAnt; int cnt, majorPin; UCHAR pinVal; //RTMP_SA_AGSP_MAP *pAgspEntry; // Method 2: // Use group concept do the antenna selection DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA,("Method 2:\n")); pCanLog2 = NULL; memset(&PER[0],0xff, sizeof(PER)); memset(&txCnt[0], 0, sizeof(txCnt)); memset(&txRtyCnt[0], 0, sizeof(txRtyCnt)); for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); curAnt = pCurLog->antPattern; while (curAnt > 0) { pinVal = (curAnt & 0xff); if (pinVal < 9) { txCnt[pinVal] += pCurLog->txCnt; txRtyCnt[pinVal] += pCurLog->txRtyCnt; } else { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\t\t%s():Wrong pinVal!\n", __FUNCTION__)); return FALSE; } curAnt >>= 8; } } /* After we collect all info, now get the PER of each antenna */ //DBGPRINT(RT_DEBUG_OFF,("Now calculate the PER of each antenna!\n")); majorPin = -1; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA,("\tStage 1:\n")); for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { if (txCnt[cnt]) PER[cnt] = (txRtyCnt[cnt] * 1000) / txCnt[cnt]; else PER[cnt] = 0xffff; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\t\tpin[%d].txCnt=%d,rtyCnt=%d, PER=%d!\n", cnt, txCnt[cnt], txRtyCnt[cnt], PER[cnt])); if ((PER[cnt]>=0) && (txCnt[cnt]>0)) { if (majorPin < 0) { majorPin = cnt; } else { if ((PER[cnt] < PER[majorPin]) || ((PER[majorPin] == PER[cnt]) && (txCnt[cnt] > txCnt[majorPin]))) majorPin = cnt; } } } if (majorPin >= 0) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\t\tThe Major antenna pin is %d!\n", majorPin)); } /* Check all pattern which including this major pin */ if (majorPin >= 0) { UINT32 mask, majorVal; majorVal = ((majorPin > 4) ? majorPin : (majorPin<<8)); mask = ((majorPin > 4) ? 0xff : 0xff00); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tStage 2(mask=0x%x):\n", mask)); pCanLog2 = pTrainEntry->pTrainInfo; for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); if ((pCurLog->antPattern & mask) == majorVal) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\t\tCheck antPattern(0x%x-%d)\n", pCurLog->antPattern, pCurLog->patternOffset)); if (pCanLog2 == NULL) { pCanLog2 = pCurLog; } else { if ((pCurLog->PER < pCanLog2->PER) || ((pCurLog->PER == pCanLog2->PER) && (pCurLog->txCnt > pCanLog2->txCnt)) ) { pCanLog2 = pCurLog; } } } } } if (pCanLog2) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Method %d:\n", pSAParam->candMethod)); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tAntCandidate is Ant:0x%x-%d(PER:%d,txCnt:%d)\n", pCanLog2->antPattern, pCanLog2->patternOffset, pCanLog2->PER, pCanLog2->txCnt)); } return TRUE; } // Method 3 // We compare PER first, if PER < 8, then find the max txCnt as the cand! RTMP_SA_TRAIN_LOG_ELEMENT *sa_cand_method_3( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry) { RTMP_SA_TRAIN_LOG_ELEMENT *pCanLog = NULL, *pCurLog; int cnt; // pass 1, check all candidates and get with low PER(i.e., PER < 8) pCanLog = pTrainEntry->pTrainInfo; DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA, ("\tPhase 1(PER threshold=%d):\n", pSAParam->trainCond)); for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); if (pCurLog->PER < pSAParam->trainCond) { pCanLog = pCurLog; pCanLog->candWeight = 1; } else { // when PER > threshold, directly use PER to check the value if ((pCurLog->PER < pCanLog->PER) || ((pCurLog->PER == pCanLog->PER) && (pCurLog->txCnt > pCanLog->txCnt)) ) { pCanLog->candWeight = ANT_WEIGHT_SCAN_ALL; pCanLog = pCurLog; pCurLog->candWeight = 2; } else { pCurLog->candWeight = ANT_WEIGHT_SCAN_ALL; } } DBGPRINT(RT_DEBUG_TRACE|DBG_FUNC_SA, ("\t\tAntPattern:0x%x,txCnt:%d,PER:%d,weight:%d\n", pCurLog->antPattern, pCurLog->txCnt, pCurLog->PER, pCurLog->candWeight)); } // phase 2, get the real candidate depends on weight and txCnt DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA, ("\tPhase 2:\n")); pCanLog = NULL; for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); if (pCurLog->candWeight <=2) { if (pCanLog) { if (pCanLog->candWeight > pCurLog->candWeight) { pCanLog = pCurLog; } else if (pCanLog->candWeight < pCurLog->candWeight) { // do nothing } else if (pCanLog->candWeight == pCurLog->candWeight) { // it must be 1, because only weight 1 may have multiple cand. if (pCurLog->txCnt > pCanLog->txCnt) pCanLog = pCurLog; } } else { pCanLog = pCurLog; } } if (pCanLog) { DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA, ("\t\tpCurLog(0x%x-%d), pCanLog(0x%x-%d)\n", pCurLog->antPattern, pCurLog->candWeight, pCanLog->antPattern, pCanLog->candWeight)); } else { DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA, ("\t\tpCurLog(0x%x-%d), ignore\n", pCurLog->antPattern, pCurLog->candWeight)); } } if (pCanLog) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Method %d:\n", pSAParam->candMethod)); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tAntCandidate is Ant:0x%x-%d(PER:%d,txCnt:%d)\n", pCanLog->antPattern, pCanLog->patternOffset, pCanLog->PER, pCanLog->txCnt)); } return pCanLog; } // Method 4 // We compare PER first, if PER < 8, then find the max txCnt as the cand RTMP_SA_TRAIN_LOG_ELEMENT *sa_cand_method_4( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry) { RTMP_SA_TRAIN_LOG_ELEMENT *pCanLog = NULL, *pCurLog; UINT32 totalTxNoRtyCnt = 0, avgTxNoRtyCnt, tpCnt = 0, PerUBnd = 0xffffffff, mcsPerBnd = 0; int cnt, txNss, NoRtyRatio; INT32 candSNR, curSNR; // phase 1, check all candidates and get with average txCnt DBGPRINT(RT_DEBUG_OFF,("%s():phase 1, get totalTxNoRtyCnt(trainWeight=%d)\n", __FUNCTION__, pTrainEntry->trainWeight)); pCanLog = pTrainEntry->pTrainInfo; for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); pCurLog->candWeight = ANT_WEIGHT_CAND_INIT; if (pCurLog->antWeight <= pTrainEntry->trainWeight) { tpCnt++; totalTxNoRtyCnt += pCurLog->txNoRtyCnt; NoRtyRatio = -1; if (pCurLog->txCnt) NoRtyRatio = (pCurLog->txNoRtyCnt * 100 ) / pCurLog->txCnt; DBGPRINT(RT_DEBUG_OFF, ("\tantPattern(0x%x-%d),AWeight=%3d,CWeight=%3d,txCnt=%d(S:%d,R:%d,F:%d,OSR:%d)\n", pCurLog->antPattern, pCurLog->patternOffset, pCurLog->antWeight, pCurLog->candWeight, pCurLog->txCnt, pCurLog->txNoRtyCnt, pCurLog->txRtyCnt, pCurLog->txRtyFailCnt, NoRtyRatio)); } } avgTxNoRtyCnt = totalTxNoRtyCnt / tpCnt; DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA, ("\tPhase 1, AvgTxNoRtyCnt=%d(total=%d,cnt=%d)\n", avgTxNoRtyCnt, totalTxNoRtyCnt, tpCnt)); /* phase 2, (a).filter the antenna pattern whose txCnt lower than average txcnt (b).get the lowest PER */ avgTxNoRtyCnt = avgTxNoRtyCnt * 3 / 4; DBGPRINT(RT_DEBUG_OFF, ("%s():phase 2, set CWeight by Weighted AvgTxNoRtyCnt(%d)\n", __FUNCTION__, avgTxNoRtyCnt)); for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); if ((pCurLog->antWeight <= pTrainEntry->trainWeight) && (((pTrainEntry->trainWeight == ANT_WEIGHT_SCAN_ALL) && (pCurLog->txNoRtyCnt >= avgTxNoRtyCnt)) || ((pTrainEntry->trainWeight != ANT_WEIGHT_SCAN_ALL)) ) ) { pCurLog->candWeight = ANT_WEIGHT_CAND_LOW; if (pCurLog->PER < PerUBnd) PerUBnd = pCurLog->PER; DBGPRINT(RT_DEBUG_OFF, ("\tantPattern(0x%x-%d),AWeight=%3d,CWeight=%3d,PER=%4d,PERUB=%4d,txCnt=%d(S:%d,R:%d,F:%d)\n", pCurLog->antPattern, pCurLog->patternOffset, pCurLog->antWeight, pCurLog->candWeight, pCurLog->PER, PerUBnd, pCurLog->txCnt, pCurLog->txNoRtyCnt, pCurLog->txRtyCnt, pCurLog->txRtyFailCnt)); } } /* phase 3, (a).find the candidate by candWeight and SNR (b).if SNR euqal, then check PER (c).if SNR and PER both equal, then check totalTxNoRtyCnt */ pCanLog = NULL; candSNR = 0; PerUBnd += pSAParam->trainCond; if (pTrainEntry->pMacEntry) { MAC_TABLE_ENTRY *pMacEntry; pMacEntry = pTrainEntry->pMacEntry; if (pMacEntry->pRateTable) { mcsPerBnd = sa_get_trainup_per_by_mcs(pMacEntry->pRateTable, pMacEntry->HTPhyMode.field.MCS); } mcsPerBnd *= 10; } DBGPRINT(RT_DEBUG_OFF,("%s():phase 3, find candidate by PER(< max[PerUBnd=%d, McsTrainUpBnd=%d]) and Weight(<%d)\n", __FUNCTION__, PerUBnd, mcsPerBnd, ANT_WEIGHT_CAND_LOW)); for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); curSNR = 0; /* calcuate the SNR first */ for (txNss = 0; txNss < pSAParam->txNss; txNss++) { if (pCurLog->cntSNR[txNss]) { curSNR += (((pCurLog->sumSNR[txNss] / pCurLog->cntSNR[txNss]) * 1881) /10000); } } if ((pCurLog->candWeight == ANT_WEIGHT_CAND_LOW) && ((pCurLog->PER <= PerUBnd) || (pCurLog->PER <= mcsPerBnd))) { BOOLEAN bChange = FALSE; DBGPRINT(RT_DEBUG_OFF,("\tCheck Ant(0x%x-%d)=>AWeight=%3d,CWeight=%3d,PER=%4d,SNR=%2d,txCnt=%d(S:%d,R:%d,F:%d)\n", pCurLog->antPattern, pCurLog->patternOffset, pCurLog->antWeight, pCurLog->candWeight,pCurLog->PER, curSNR, pCurLog->txCnt, pCurLog->txNoRtyCnt, pCurLog->txRtyCnt, pCurLog->txRtyFailCnt)); // TODO: move SNR calculation to here! /* Check the curSNR with the selected candidate antenna pattern */ if (pCanLog) { if (curSNR > candSNR) { // SNR first! bChange = TRUE; } else if (curSNR == candSNR) { // check PER if (pCurLog->PER < pCanLog->PER) { bChange = TRUE; } else if (pCurLog->PER == pCanLog->PER) { // check txCnt if (pCurLog->txCnt >= pCanLog->txCnt) bChange = TRUE; } } } else { bChange = TRUE; } if (bChange == TRUE) { pCanLog = pCurLog; candSNR = curSNR; } } if (pCanLog) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\t\tpCurLog(0x%x-%d,PER=%d,SNR=%d,txCnt=%d),pCanLog(0x%x-%d,PER=%d,SNR=%d,txCnt=%d)\n", pCurLog->antPattern, pCurLog->candWeight, pCurLog->PER, curSNR, pCurLog->txCnt, pCanLog->antPattern, pCanLog->candWeight, pCanLog->PER, candSNR, pCanLog->txCnt)); } else { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\t\tpCurLog(0x%x-%d,PER=%d,SNR=%d,txCnt=%d), no candidate!\n", pCurLog->antPattern, pCurLog->candWeight, pCurLog->PER, curSNR, pCurLog->txCnt)); } } // phase 4, depends on the PER of pCanLog, find the final candidate via SNR if (pCanLog) { UINT32 PerUBnd; PerUBnd = pCanLog->PER + 50; } if (pCanLog) { pCanLog->candWeight = ANT_WEIGHT_CAND_HIGH; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tAntCandidate is Ant:0x%x-%d(PER:%d,txCnt:%d)\n", pCanLog->antPattern, pCanLog->patternOffset, pCanLog->PER, pCanLog->txCnt)); } return pCanLog; } // Method 1: RTMP_SA_TRAIN_LOG_ELEMENT *sa_cand_method_1( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry) { RTMP_SA_TRAIN_LOG_ELEMENT *pCanLog = NULL, *pCurLog; int cnt; // check if we can switch this one as the new candidate pCanLog = pTrainEntry->pTrainInfo; for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); if ((pCurLog->PER < pCanLog->PER) || ((pCurLog->PER == pCanLog->PER) && (pCurLog->txCnt > pCanLog->txCnt)) ) { pCanLog = pCurLog; } } if (pCanLog) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Method %d:\n", pSAParam->candMethod)); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tAntCandidate is Ant:0x%x-%d(PER:%d,txCnt:%d)\n", pCanLog->antPattern, pCanLog->patternOffset, pCanLog->PER, pCanLog->txCnt)); } return pCanLog; } INT sa_update_ant_weight( IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN UCHAR candWeight, IN UCHAR antWeight) { int cnt; RTMP_SA_TRAIN_LOG_ELEMENT *pTrainLog; for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pTrainLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); if (pTrainLog->candWeight <= candWeight) { pTrainLog->antWeight = antWeight; } } DBGPRINT(RT_DEBUG_OFF,("Dump antWeight after adjust antenna weight!\n")); for (cnt = 0; cnt < pSAParam->trainSeqCnt; cnt++) { pTrainLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt ); DBGPRINT(RT_DEBUG_OFF,("\tantPattern(0x%x-%d), antWeight=%d, candWeight=%d\n", pTrainLog->antPattern, pTrainLog->patternOffset, pTrainLog->antWeight, pTrainLog->candWeight)); } return TRUE; } BOOLEAN sa_can_do_fast_tune( IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry) { int i; MAC_TABLE_ENTRY *pMacEntry = pTrainEntry->pMacEntry; BOOLEAN bCanDoFT = TRUE; /* check RSSI */ if (pMacEntry) { DBGPRINT(RT_DEBUG_OFF,("%s():dump RSSI result\n", __FUNCTION__)); for (i = 0; i < pSAParam->txNss; i++) { DBGPRINT(RT_DEBUG_OFF,("\tRSSI[%d]: prev=%d, cur=%d\n", i, pMacEntry->baseAvgRSSI[i], pMacEntry->curAvgRSSI[i])); } sa_rssi_chk_variant(pMacEntry, pSAParam->txNss, pSAParam->rssiVar); for (i = 0; i < pSAParam->txNss; i++) { if (pMacEntry->baseAvgRSSI[i] != 0) { if (((pMacEntry->curAvgRSSI[i] - pMacEntry->baseAvgRSSI[i]) > pSAParam->rssiVar) || ((pMacEntry->baseAvgRSSI[i] - pMacEntry->curAvgRSSI[i]) > pSAParam->rssiVar)) { bCanDoFT = FALSE; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s(Rssi[%d]):Hit!!Cannot do FastTuning(OrgRssi=%d,curRssi=%d,rssiVar=%d)\n", __FUNCTION__, i, pMacEntry->baseAvgRSSI[i], pMacEntry->curAvgRSSI[i], pSAParam->rssiVar)); } } } } return bCanDoFT; } BOOLEAN sa_chk_train_consistence( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry) { RTMP_SA_TRAIN_LOG_ELEMENT *pBaseInfo, *pCurLog; BOOLEAN bIsConsisant = TRUE; int i; pBaseInfo = &pTrainEntry->antBaseInfo; pCurLog = pTrainEntry->pCurTrainInfo; DBGPRINT(RT_DEBUG_OFF,("%s():Check consistence of training environment(BaseInfo:0x%x-%d,CurLog:0x%x-%d)\n", __FUNCTION__, pBaseInfo->antPattern, pBaseInfo->patternOffset, pCurLog->antPattern, pCurLog->patternOffset)); for (i = 0; i < pSAParam->txNss; i++) { #error "Need to fix ConvertToRssi() before compile this feature" if (pBaseInfo->cntRSSI[i] != 0) pBaseInfo->avgRSSI[i] = ConvertToRssi(pAd, pBaseInfo->sumRSSI[i] / pBaseInfo->cntRSSI[i], i); else pBaseInfo->avgRSSI[i] = 0; if (pCurLog->cntRSSI[i] != 0) pCurLog->avgRSSI[i] = ConvertToRssi(pAd, pCurLog->sumRSSI[i] / pCurLog->cntRSSI[i], i); else pCurLog->avgRSSI[i] = 0; DBGPRINT(RT_DEBUG_OFF,("\t\tBase-RSSI[%d](cnt:%d,avgRSSI:%d), Current-RSSI[%d](cnt:%d,avgRSSI[%d])\n", i, pBaseInfo->cntRSSI[i], pBaseInfo->avgRSSI[i], i, pCurLog->cntRSSI[i], pCurLog->avgRSSI[i])); } if (!is_ant_equal(pBaseInfo->antPattern,pCurLog->antPattern)) { DBGPRINT(RT_DEBUG_OFF,("%s():ERROR!BaseInfo(0x%x-%d) not match CurLog(0x%x-%d)\n", __FUNCTION__, pBaseInfo->antPattern, pBaseInfo->patternOffset, pCurLog->antPattern, pCurLog->patternOffset)); bIsConsisant = FALSE; } for (i = 0; i < pSAParam->txNss; i++) { if (pBaseInfo->avgRSSI[i] != 0) { if (((pCurLog->avgRSSI[i] - pBaseInfo->avgRSSI[i]) > pSAParam->rssiVar) || ((pBaseInfo->avgRSSI[i] - pCurLog->avgRSSI[i]) > pSAParam->rssiVar)) { bIsConsisant = FALSE; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s(Rssi[%d]):Hit!!Environment not consistant!(OrgRssi=%d,curRssi=%d, rssiVar=%d)\n", __FUNCTION__, i, pBaseInfo->avgRSSI[i], pCurLog->avgRSSI[i], pSAParam->rssiVar)); } } } return bIsConsisant; } VOID sa_read_clean_train_result( IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN RTMP_SA_TRAIN_LOG_ELEMENT *pCurLog) { #ifdef SA_LUMP_SUM pCurLog->txRtyFailCnt = pTrainEntry->sumTxFailCnt; pCurLog->txRtyCnt = pTrainEntry->sumTxRtyCnt + pTrainEntry->sumTxFailCnt; pCurLog->txNoRtyCnt = pTrainEntry->sumTxCnt - pCurLog->txRtyCnt; pCurLog->txCnt = pTrainEntry->sumTxCnt; pTrainEntry->sumTxCnt = 0; pTrainEntry->sumTxRtyCnt = 0; pTrainEntry->sumTxFailCnt = 0; #else MAC_TABLE_ENTRY *pMacEntry = pTrainEntry->pMacEntry; pCurLog->txRtyFailCnt = pMacEntry->saLstTxFailCnt; pCurLog->txRtyCnt = pMacEntry->saLstTxRtyCnt + pMacEntry->saLstTxFailCnt; pCurLog->txNoRtyCnt = pMacEntry->saLstTxNoRtyCnt; pCurLog->txCnt = pCurLog->txRtyCnt + pMacEntry->saLstTxNoRtyCnt; pMacEntry->saLstTxRtyCnt = 0; pMacEntry->saLstTxFailCnt = 0; pMacEntry->saLstTxNoRtyCnt = 0; #endif // SA_LUMP_SUM // } VOID sa_trainInfo_update( IN RTMP_ADAPTER *pAd, IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN RTMP_SA_TRAIN_LOG_ELEMENT *pCurLog, IN UCHAR antCnt) { TX_STA_CNT0_STRUC staTxCnt0; TX_STA_CNT1_STRUC staTxCnt1; // DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\tUpdate TrainInfo of Ant(0x%x-%d)=>\n", // pTrainEntry->curAntPattern, pTrainEntry->patternOffset)); NicGetTxRawCounters(pAd, &staTxCnt0, &staTxCnt1); sa_read_clean_train_result(pTrainEntry,pCurLog); //sa_rssi_update(pAd, pTrainEntry->pMacEntry, antCnt); // DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\t<=Update TrainInfo of Ant(0x%x-%d)\n", // pTrainEntry->curAntPattern, pTrainEntry->patternOffset)); } INT sa_get_cand( IN RTMP_ADAPTER *pAd, IN SMART_ANTENNA_STRUCT *pSAParam, IN RTMP_SA_TRAINING_PARAM *pTrainEntry) { RTMP_SA_TRAIN_LOG_ELEMENT *pCurLog, *pCanLog; int cnt,agspCnt; RTMP_SA_AGSP_MAP *pAgspEntry; if (pTrainEntry->pTrainInfo == NULL) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Error:pTrainEntry->pTrainInfo is NULL\n")); return FALSE; } if (pSAParam->agsp == NULL) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Error: AGSP is not initialized!\n")); return FALSE; } agspCnt = 0; pAgspEntry = pSAParam->agsp; while(memcmp(pAgspEntry, &AGSP_ENTRY_END, sizeof(RTMP_SA_AGSP_MAP))!= 0) { pAgspEntry++; agspCnt++; } DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("agspCnt=%d!\n", agspCnt)); /* first of all, dump all info */ for (cnt = 0 ; cnt < pSAParam->trainSeqCnt; cnt++) { pCurLog = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + cnt); if ((pTrainEntry->trainWeight == 0) || (pCurLog->antWeight <= pTrainEntry->trainWeight)) sa_dump_train_log(pAd, pTrainEntry, pCurLog, pSAParam->txNss); } DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Now decide the candidate by Method %d!\n", pSAParam->candMethod)); switch (pSAParam->candMethod) { case 1: pCanLog = sa_cand_method_1(pAd, pSAParam, pTrainEntry); break; case 2: pCanLog = NULL; //sa_cand_method_2(pAd, pSAParam, pTrainEntry); break; case 3: pCanLog = sa_cand_method_3(pAd, pSAParam, pTrainEntry); break; case 4: pCanLog = sa_cand_method_4(pAd, pSAParam, pTrainEntry); break; default: pCanLog = NULL; DBGPRINT(RT_DEBUG_ERROR, ("%s():Invalid candMethod(%d)\n", __FUNCTION__, pSAParam->candMethod)); break; } if (pCanLog) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("FinalDecision with Method %d(stage=%d):\n", pSAParam->candMethod, pTrainEntry->trainStage)); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tAntCandidate is Ant:0x%x-%d(PER:%d,txCnt:%d)\n", pCanLog->antPattern, pCanLog->patternOffset, pCanLog->PER, pCanLog->txCnt)); pTrainEntry->pCanTrainInfo = pCanLog; pTrainEntry->canAntPattern = pCanLog->antPattern; return TRUE; } else { DBGPRINT(RT_DEBUG_ERROR, ("%s():Cannot found candidate by method %d!\n", __FUNCTION__, pSAParam->candMethod)); return FALSE; } } INT sa_ant_adaptation( IN RTMP_ADAPTER *pAd) { SMART_ANTENNA_STRUCT *pSAParam = NULL; RTMP_SA_TRAINING_PARAM *pTrainEntry; RTMP_SA_TRAIN_LOG_ELEMENT *pCurLog, *pCanLog = NULL; MAC_TABLE_ENTRY *pMacEntry; UINT32 curAnt, bTryNext = FALSE; unsigned long irqFlag; BOOLEAN bConsistantEnv = TRUE; //UCHAR antWeight; RTMP_IRQ_LOCK(&pAd->smartAntLock, irqFlag); if (!RTMP_SA_WORK_ON(pAd)) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():SA status unsync(SAEnable=%d,SAParam=0x%lx)\n", __FUNCTION__, pAd->smartAntEnable, (ULONG)pAd->pSAParam)); RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlag); return FALSE; } pSAParam = pAd->pSAParam; pTrainEntry = &pSAParam->trainEntry[0]; pMacEntry = pTrainEntry->pMacEntry; if ((pMacEntry == NULL) || (pTrainEntry->pTrainInfo == NULL)) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("%s():MacEntry(0x%p) or pTrainInfo(0x%p) is NULL\n", __FUNCTION__, pMacEntry, pTrainEntry->pTrainInfo)); RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlag); return FALSE; } if (IS_ENTRY_CLIENT(pMacEntry)) { RTMP_SA_TRAIN_LOG_ELEMENT *tmpLogPtr; #ifdef SA_LUMP_SUM TX_STA_CNT0_STRUC TxStaCnt0; TX_STA_CNT1_STRUC StaTx1; #endif // SA_LUMP_SUM // /* phase 1. save previously info to the TrainInfo */ #ifdef SA_LUMP_SUM // Update statistic counter NicGetTxRawCounters(pAd, &TxStaCnt0, &StaTx1); #else NICUpdateFifoStaCounters(pAd); #endif // SA_LUMP_SUM // // update all rx signals RtmpSAUpdateRxSignal(pAd); tmpLogPtr = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); if ((pTrainEntry->pCurTrainInfo != tmpLogPtr) || (pTrainEntry->pCurTrainInfo == NULL)) { DBGPRINT(RT_DEBUG_ERROR, ("%s():ERR!!pCurTrainInfo(0x%p) and PatternOffset(0x%p-%d) not equal!\n", __FUNCTION__, pTrainEntry->pCurTrainInfo, tmpLogPtr, pTrainEntry->patternOffset)); pTrainEntry->pCurTrainInfo = tmpLogPtr; } DBGPRINT(RT_DEBUG_OFF,("%s():curAnt=0x%x-%d, TStage=%d,TWeight=%d!\n", __FUNCTION__, pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pTrainEntry->trainStage, pTrainEntry->trainWeight)); curAnt = pTrainEntry->curAntPattern; pCurLog = pTrainEntry->pCurTrainInfo; NdisGetSystemUpTime(&pCurLog->endTime); /* save the statistics and clean for netx round */ pCurLog->antPattern = pTrainEntry->curAntPattern; pCurLog->patternOffset = pTrainEntry->patternOffset; sa_trainInfo_update(pAd, pTrainEntry, pCurLog, pSAParam->txNss); if(pCurLog->txCnt) pCurLog->PER = (pCurLog->txRtyCnt * 1000) / pCurLog->txCnt; else pCurLog->PER = 0xffffffff; //sa_dump_train_log(pAd, pTrainEntry, pCurLog, pSAParam->txNss); if (pTrainEntry->bLastRnd == 1) { ULONG nowTime; pTrainEntry->mcsStableCnt = 0; bTryNext = FALSE; pTrainEntry->bLastRnd = 0; /* check the RSSI of pBaseInfo and pCurLog info to make sure the whole training progress is in a consistant environment */ bConsistantEnv = sa_chk_train_consistence(pAd, pSAParam, pTrainEntry); if (bConsistantEnv == FALSE) { pTrainEntry->trainStage = SA_INIT_STAGE; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("EndOfTraining:\n")); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Ignore this round, env not consistent!\n")); goto ignoreIt; } if (sa_get_cand(pAd, pSAParam, pTrainEntry) == TRUE) { pCanLog = pTrainEntry->pCanTrainInfo; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA,("EndOfTraining:\n")); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("The Candidate Ant is:0x%x\n", pTrainEntry->canAntPattern)); sa_dump_train_log(pAd, pTrainEntry ,pCanLog, pSAParam->txNss); /* Apply this new antenna pattern */ sa_set_antPattern(pAd, pSAParam, curAnt, pTrainEntry->canAntPattern); sa_get_curAntPattern(pSAParam, pTrainEntry); pTrainEntry->pCurTrainInfo = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); } else { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA,("EndOfTraining:\n")); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Cannot found Candidate Ant, use origianl Ant :0x%x\n", pTrainEntry->antBaseInfo.antPattern)); /* go back to original antenna pattern */ sa_set_antPattern(pAd, pSAParam, curAnt, pTrainEntry->antBaseInfo.antPattern); sa_get_curAntPattern(pSAParam, pTrainEntry); pTrainEntry->pCurTrainInfo = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); pTrainEntry->trainStage = SA_INIT_STAGE; goto ignoreIt; } /* If mode is one-shot, change the mode as NONE */ if (pSAParam->saMode == SA_MODE_ONESHOT) { pSAParam->saMode = SA_MODE_NONE; pTrainEntry->trainStage = SA_INVALID_STAGE; } if (pSAParam->saMode == SA_MODE_AUTO) { MAC_TABLE_ENTRY *pEntry = pTrainEntry->pMacEntry; //int cnt; DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("Modify trainStage for AutoMode\n")); DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\tBefore:trainStage=%s,initAnt=0x%x,confirmAnt=0x%x!\n", saStage[pTrainEntry->trainStage], pTrainEntry->ant_init_stage, pTrainEntry->ant_confirm_stage)); if (pTrainEntry->trainStage == SA_INIT_STAGE) { if (pSAParam->bSkipConfStage) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\t\tskip confirm stage, Change from Init to Monitor stage:\n")); pTrainEntry->ant_init_stage = pTrainEntry->canAntPattern; pTrainEntry->ant_confirm_stage = pTrainEntry->canAntPattern; pTrainEntry->trainStage = SA_MONITOR_STAGE; } else { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\t\tChange from Init to Confirm stage:\n")); pTrainEntry->ant_init_stage = pTrainEntry->canAntPattern; pTrainEntry->trainStage = SA_CONFIRM_STAGE; pTrainEntry->ant_confirm_stage = 0; } // update trianing info for antenna patterns which has txCnt larger than average TxCnt sa_update_ant_weight(pSAParam, pTrainEntry, ANT_WEIGHT_CAND_LOW, ANT_WEIGHT_SCAN_AVG); /* We need to record the RSSI sample here for fast tunning */ reset_mac_entry_stats(pEntry); if (pCanLog) { int i; NdisZeroMemory(&pEntry->baseAvgRSSI[0], 3 * sizeof(CHAR)); for(i = 0 ; i < pSAParam->txNss; i++) { if (pCanLog->sumRSSI[i]) { #error "Need to fix ConvertToRssi() before compile this feature" pEntry->baseAvgRSSI[i] = ConvertToRssi(pAd, pCanLog->sumRSSI[i] / pCanLog->cntRSSI[i], i); } DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\t\t\tAnt%d:Rssi=%d(cnt=%d, sum=%d)\n", i,pEntry->baseAvgRSSI[i], pCanLog->cntRSSI[i], pCanLog->sumRSSI[i])); } } } else if (pTrainEntry->trainStage == SA_CONFIRM_STAGE) { if (pTrainEntry->canAntPattern != pTrainEntry->ant_init_stage) { if (pTrainEntry->trainWeight == ANT_WEIGHT_SCAN_ALL) { // no change, we keep in the confirm stage now! pTrainEntry->trainStage = SA_CONFIRM_STAGE; pTrainEntry->ant_init_stage = pTrainEntry->canAntPattern; pTrainEntry->ant_confirm_stage = 0; } else { // we go back to init stage, because previously we just scan the sub-group pTrainEntry->trainStage = SA_INIT_STAGE; } } else { pTrainEntry->ant_confirm_stage = pTrainEntry->canAntPattern; pTrainEntry->trainStage = SA_MONITOR_STAGE; /* reset original RSSI and copy the pCanLog->RSSI to baseRSSI[] */ reset_mac_entry_stats(pEntry); DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\t\tChange from Confirm to Monitor stage:\n")); if (pCanLog) { int i; NdisZeroMemory(&pEntry->baseAvgRSSI[0], 3 * sizeof(CHAR)); for(i = 0 ; i < pSAParam->txNss; i++) { if (pCanLog->sumRSSI[i]) { pEntry->baseAvgRSSI[i] = ConvertToRssi(pAd, pCanLog->sumRSSI[i] / pCanLog->cntRSSI[i], i); } DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\t\t\tAnt%d:Rssi=%d(cnt=%d, sum=%d)\n", i,pEntry->baseAvgRSSI[i], pCanLog->cntRSSI[i], pCanLog->sumRSSI[i])); } } } } DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\tAfter:trainStage=%d,initAnt=0x%x,confirmAnt=0x%x!\n", pTrainEntry->trainStage, pTrainEntry->ant_init_stage, pTrainEntry->ant_confirm_stage)); } ignoreIt: NdisGetSystemUpTime(&nowTime); pTrainEntry->time_to_start = nowTime + pSAParam->trainDelay * OS_HZ; } else { unsigned int offset, cleanLen; /* Select the antPattern from TrainingSeq and set to GPIO */ sa_get_nextAntPattern(pSAParam, pTrainEntry, pTrainEntry->trainWeight, ANT_SELECT_IGNORE_BASE); if (pTrainEntry->curAntPattern == 0) { sa_get_nextAntPattern(pSAParam, pTrainEntry, pTrainEntry->trainWeight, ANT_SELECT_BASE); } /* ready for next antenna training procedure */ pTrainEntry->pCurTrainInfo = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); pCurLog = pTrainEntry->pCurTrainInfo; offset = (unsigned int)(&(((RTMP_SA_TRAIN_LOG_ELEMENT *)NULL)->srtTime)); cleanLen = sizeof(RTMP_SA_TRAIN_LOG_ELEMENT) - offset; NdisZeroMemory((PUCHAR)(&pCurLog->srtTime), cleanLen); NdisGetSystemUpTime(&pCurLog->srtTime); pCurLog->txMcs = pMacEntry->HTPhyMode.field.MCS; sa_set_antPattern(pAd, pSAParam, curAnt, pTrainEntry->curAntPattern); if (is_ant_equal(pTrainEntry->antBaseInfo.antPattern, pTrainEntry->curAntPattern)) pTrainEntry->bLastRnd = 1; bTryNext = TRUE; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("StartToTestAntPattern:0x%x(time=0x%lx!)\n", pTrainEntry->curAntPattern, pTrainEntry->pCurTrainInfo->srtTime)); } // update trainging status of the TrainEntry pTrainEntry->bTraining = bTryNext; } RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlag); if ((bTryNext == TRUE) && (pSAParam)) { RTMPSetTimer(&pSAParam->saSwitchTimer, pSAParam->chkPeriod); } return TRUE; } VOID sa_switch_exec( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3) { RTMP_ADAPTER *pAd = (RTMP_ADAPTER *)FunctionContext;; sa_ant_adaptation(pAd); return; } /****************************************************************************** Public functions ******************************************************************************/ INT RtmpSAChkAndGo( IN RTMP_ADAPTER *pAd) { SMART_ANTENNA_STRUCT *pSAParam; RTMP_SA_TRAINING_PARAM *pTrainEntry; MAC_TABLE_ENTRY *pMacEntry; ULONG nowTime; #ifndef SA_LUMP_SUM UINT32 totalTxCnt; #endif // SA_LUMP_SUM // BOOLEAN bNeedTune = FALSE; unsigned long irqFlag; //UCHAR antWeight = ANT_WEIGHT_SCAN_ALL; RTMP_IRQ_LOCK(&pAd->smartAntLock, irqFlag); if (! RTMP_SA_WORK_ON(pAd)) { RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlag); return FALSE; } pSAParam = pAd->pSAParam; pTrainEntry = &pSAParam->trainEntry[0]; pMacEntry = pTrainEntry->pMacEntry; // update Rx signals if (pTrainEntry->bTraining == FALSE) RtmpSAUpdateRxSignal(pAd); if (pSAParam->bStaChange || pSAParam->bRssiChange) { BOOLEAN bCancel; DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("%s():StaChange=%d,RssiChange=%d!\n", __FUNCTION__, pSAParam->bStaChange, pSAParam->bRssiChange)); if (pTrainEntry->bTraining == TRUE) RTMPCancelTimer(&pSAParam->saSwitchTimer, &bCancel); sa_select_target_train_entry(pAd, pSAParam); pTrainEntry->trainStage = SA_INIT_STAGE; pTrainEntry->trainWeight = ANT_WEIGHT_SCAN_ALL; /* get current physically applied antenna pattern */ sa_get_curAntPattern(pSAParam, pTrainEntry); if (pTrainEntry->curAntPattern == 0) sa_get_nextAntPattern(pSAParam, pTrainEntry, pTrainEntry->trainWeight, ANT_SELECT_FIRST); pTrainEntry->pCurTrainInfo = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); DBGPRINT(RT_DEBUG_OFF, ("%s(): The SmartAnt StaChanged with antPattern(0x%x-%d), pCurLog(0x%p, 0x%x-%d)\n", __FUNCTION__, pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pTrainEntry->pCurTrainInfo, pTrainEntry->pCurTrainInfo->antPattern, pTrainEntry->pCurTrainInfo->patternOffset)); pTrainEntry->mcsStableCnt = 0; NdisGetSystemUpTime(&nowTime); pTrainEntry->time_to_start = nowTime + pSAParam->trainDelay * OS_HZ; DBGPRINT(RT_DEBUG_OFF,("%s():set time_to_start=0x%lx\n", __FUNCTION__, pTrainEntry->time_to_start)); pSAParam->bStaChange = FALSE; pSAParam->bRssiChange = FALSE; // the pMacEntry may changed, re-assign it here pMacEntry = pTrainEntry->pMacEntry; } if ((pMacEntry == NULL) || (pSAParam->saMode < SA_MODE_ONESHOT)) { RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlag); return FALSE; } if (IS_ENTRY_CLIENT(pMacEntry)) { BOOLEAN doTrain = FALSE; int i; #if 0 if (pSAParam->bStaChange) { sa_select_target_train_entry(pAd, pSAParam); pTrainEntry->trainStage = SA_INIT_STAGE; pTrainEntry->trainWeight = ANT_WEIGHT_SCAN_ALL; /* get current physically applied antenna pattern */ sa_get_curAntPattern(pSAParam, pTrainEntry); if (pTrainEntry->curAntPattern == 0) sa_get_nextAntPattern(pSAParam, pTrainEntry, pTrainEntry->trainWeight, ANT_SELECT_FIRST); pTrainEntry->pCurTrainInfo = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); DBGPRINT(RT_DEBUG_OFF,("%s(): The SmartAnt StaChanged with antPattern(0x%x-%d), pCurLog(0x%p, 0x%x-%d)\n", __FUNCTION__, pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pTrainEntry->pCurTrainInfo, pTrainEntry->pCurTrainInfo->antPattern, pTrainEntry->pCurTrainInfo->patternOffset)); pTrainEntry->mcsStableCnt = 0; NdisGetSystemUpTime(&nowTime); pTrainEntry->time_to_start = nowTime + pSAParam->trainDelay * OS_HZ; DBGPRINT(RT_DEBUG_OFF,("%s():set time_to_start=0x%lx\n", __FUNCTION__, pTrainEntry->time_to_start)); pSAParam->bStaChange = FALSE; } #endif if (pTrainEntry->bTraining == TRUE) { RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlag); DBGPRINT(RT_DEBUG_INFO | DBG_FUNC_SA, ("%s():Entry in SA Training, Skip the check!\n", __FUNCTION__)); return FALSE; } /* Update the train log and calculate the RSSI first */ if (pTrainEntry->trainStage != SA_INVALID_STAGE) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("%s(0x%lx):wcid:%d,TS(%s),cAnt(0x%x-%d),bAnt(0x%x-%d),get RSSI Values!\n", __FUNCTION__, nowTime, pMacEntry->wcid, saStage[pTrainEntry->trainStage], pTrainEntry->curAntPattern, pTrainEntry->patternOffset, pTrainEntry->antBaseInfo.antPattern, pTrainEntry->antBaseInfo.patternOffset)); sa_trainInfo_update(pAd, pTrainEntry, pTrainEntry->pCurTrainInfo, pSAParam->txNss); } NdisGetSystemUpTime(&nowTime); /* Depends on training stage, do different check */ if (pTrainEntry->trainStage == SA_MONITOR_STAGE) { BOOLEAN bValidBase = FALSE, bValidRssi = FALSE, bRssiChanged = FALSE; for (i = 0; i < pSAParam->txNss; i++) { bValidRssi |= (pMacEntry->curAvgRSSI[i] != 0); if (pMacEntry->baseAvgRSSI[i]) { bValidBase = TRUE; if (((pMacEntry->curAvgRSSI[i] - pMacEntry->baseAvgRSSI[i]) > pSAParam->rssiVar) || ((pMacEntry->baseAvgRSSI[i] - pMacEntry->curAvgRSSI[i]) > pSAParam->rssiVar) ) { bRssiChanged = TRUE; } } } DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\tMonitorStage,Check RSSI variance only(prevRSSI=%d,%d,%d,cur=%d,%d,%d),bVB:%d,bVR:%d,bRC:%d!\n", pMacEntry->baseAvgRSSI[0], pMacEntry->baseAvgRSSI[1], pMacEntry->baseAvgRSSI[2], pMacEntry->curAvgRSSI[0], pMacEntry->curAvgRSSI[1], pMacEntry->curAvgRSSI[2], bValidBase, bValidRssi, bRssiChanged)); if (bValidRssi && bRssiChanged) { doTrain = FALSE; // here we just set the stage as init, and waiting for next time to do it! pTrainEntry->time_to_start = nowTime + pSAParam->trainDelay * OS_HZ; pTrainEntry->mcsStableCnt = 0; pTrainEntry->trainStage = SA_INIT_STAGE; pTrainEntry->ant_init_stage = pTrainEntry->ant_confirm_stage = 0; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tStageChange(0x%lx, %s -> %s): Condition RSSI variance Hit(rssiVar=%d)!!\n", nowTime, saStage[SA_MONITOR_STAGE], saStage[SA_INIT_STAGE], pSAParam->rssiVar)); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\t\tOrigRssi(%d,%d,%d), curRssi(%d,%d,%d)\n", pMacEntry->baseAvgRSSI[0], pMacEntry->baseAvgRSSI[1],pMacEntry->baseAvgRSSI[2], pMacEntry->curAvgRSSI[0],pMacEntry->curAvgRSSI[1],pMacEntry->curAvgRSSI[2])); } if ((bValidBase == FALSE) && (bValidRssi == TRUE) && (pTrainEntry->trainStage == SA_MONITOR_STAGE)) { // TODO: Shall we consider the case if any RSSI is zero? NdisMoveMemory(&pMacEntry->baseAvgRSSI[0], &pMacEntry->curAvgRSSI[0], 3); DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\tMonitorStage:Init RSSI as (%d,%d,%d)\n", pMacEntry->baseAvgRSSI[0], pMacEntry->baseAvgRSSI[1], pMacEntry->baseAvgRSSI[2])); } DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\t(doTrain=%d):STA(wcid=%d) prevRssi=%d,%d,%d, curRssi=%d,%d,%d!\n", doTrain, pMacEntry->wcid, pMacEntry->baseAvgRSSI[0], pMacEntry->baseAvgRSSI[1], pMacEntry->baseAvgRSSI[2], pMacEntry->curAvgRSSI[0], pMacEntry->curAvgRSSI[1], pMacEntry->curAvgRSSI[2])); } if ((pTrainEntry->trainStage == SA_INIT_STAGE) || (pTrainEntry->trainStage == SA_CONFIRM_STAGE) ) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\tCheck mcsStableCnt(nowT=0x%lx,TtoStart=0x%lx,BW:MCS=%d:%d,MSC:Bnd=%d:%d)\n", nowTime, pTrainEntry->time_to_start, pMacEntry->HTPhyMode.field.BW, pMacEntry->HTPhyMode.field.MCS, pTrainEntry->mcsStableCnt, pSAParam->saMcsBound)); if ((pTrainEntry->mcsStableCnt >= pSAParam->saMsc) && (pMacEntry->HTPhyMode.field.MCS <= pSAParam->saMcsBound) && RTMP_TIME_AFTER(nowTime, pTrainEntry->time_to_start) ) { doTrain = TRUE; } // New factor used for weighted selection for antenna training pTrainEntry->trainWeight = ANT_WEIGHT_SCAN_ALL; if (doTrain == TRUE) { if (pTrainEntry->trainStage == SA_CONFIRM_STAGE) { BOOLEAN bGoFastTune; DBGPRINT(RT_DEBUG_OFF,("\tIn confirm stage, check need to do fast tunning\n")); // check if the RSSI, false CCA, no New STA connected bGoFastTune = sa_can_do_fast_tune(pSAParam, pTrainEntry); if (bGoFastTune) pTrainEntry->trainWeight = ANT_WEIGHT_SCAN_AVG; DBGPRINT(RT_DEBUG_OFF,("\tbGoFastTune=%d!trainWeight=%d!\n", bGoFastTune, pTrainEntry->trainWeight)); } if (pTrainEntry->trainStage == SA_INIT_STAGE) { DBGPRINT(RT_DEBUG_OFF,("\ttrainStage is INIT stage, reset all trainWeight!\n")); // update trianing info for antenna patterns which has txCnt larger than average TxCnt sa_update_ant_weight(pSAParam, pTrainEntry, ANT_WEIGHT_CAND_INIT, ANT_WEIGHT_SCAN_ALL); } } } if (doTrain && /*(totalTxCnt > SA_DEFAULT_TX_CNT) &&*/ (pSAParam->saMode >= SA_MODE_ONESHOT)) { RTMP_SA_TRAIN_LOG_ELEMENT *pBaseInfo = &pTrainEntry->antBaseInfo; RTMP_SA_TRAIN_LOG_ELEMENT *pCurInfo = pTrainEntry->pCurTrainInfo; pMacEntry->orgTxRateCol = pMacEntry->CurrTxRate; sa_get_curAntPattern(pSAParam, pTrainEntry); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\n\nGO!HAWK!GO!(%d-%02x:%02x:%02x:%02x:%02x:%02x, antPattern=0x%x-%d)\n", pMacEntry->wcid, PRINT_MAC(pMacEntry->Addr), pTrainEntry->curAntPattern, pTrainEntry->patternOffset)); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tTxMcs(BW:MCS=%s:%d),mcsStableCnt=%d,TP=%d ms,TStage=%d,TWeight=%d\n", phyMode[pMacEntry->HTPhyMode.field.MODE], pMacEntry->HTPhyMode.field.MCS, pTrainEntry->mcsStableCnt, pSAParam->chkPeriod, pTrainEntry->trainStage, pTrainEntry->trainWeight)); // dump the MCS of each involved station for (i = 0; i < SA_ENTRY_MAX_NUM; i++) { RTMP_SA_TRAINING_PARAM *pTmpTrainEntry; MAC_TABLE_ENTRY *pTmpEntry; pTmpTrainEntry = &pSAParam->trainEntry[i]; pTmpEntry = pTmpTrainEntry->pMacEntry; if (pTmpEntry) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tSTA(%02x:%02x:%02x:%02x:%02x:%02x):TxMcs(PhyMode:BW:MCS=%s:%d:%d), mcsStableCnt=%d\n", PRINT_MAC(pTmpEntry->Addr), phyMode[pTmpEntry->HTPhyMode.field.MODE], pTmpEntry->HTPhyMode.field.BW, pTmpEntry->HTPhyMode.field.MCS, pTmpTrainEntry->mcsStableCnt)); } else { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("\tSTA(%02x:%02x:%02x:%02x:%02x:%02x):No Associated MacEntry!\n", PRINT_MAC(pTmpEntry->Addr))); } } /* save the info to original and zero candidate info */ NdisZeroMemory(pBaseInfo, sizeof(RTMP_SA_TRAIN_LOG_ELEMENT)); if (pCurInfo) { if (is_ant_equal(pTrainEntry->curAntPattern, pCurInfo->antPattern) == FALSE) { DBGPRINT(RT_DEBUG_OFF, ("ERROR! Info of pCurTrainInfo(0x%x-%d) and pTrainEntry(0x%x-%d) not match,Reset BaseInfo!\n", pCurInfo->antPattern, pCurInfo->patternOffset, pTrainEntry->curAntPattern, pTrainEntry->patternOffset)); } NdisMoveMemory(pBaseInfo, pTrainEntry->pCurTrainInfo, sizeof(RTMP_SA_TRAIN_LOG_ELEMENT)); pBaseInfo->txMcs = pMacEntry->HTPhyMode.field.MCS; DBGPRINT(RT_DEBUG_OFF, ("\tUpdate CurLogInfo(Ant:0x%x-%d, RSSI:%d,%d,%d) to baseInfo(Ant:0x%x-%d, RSSI:%d,%d,%d)\n", pCurInfo->antPattern,pCurInfo->patternOffset, pCurInfo->avgRSSI[0],pCurInfo->avgRSSI[1],pCurInfo->avgRSSI[2], pBaseInfo->antPattern, pBaseInfo->patternOffset, pBaseInfo->avgRSSI[0],pBaseInfo->avgRSSI[1],pBaseInfo->avgRSSI[2])); } else { DBGPRINT(RT_DEBUG_OFF, ("ERROR! the TrainInfo of pTrainEntry->pCurTrainInfo is NULL!\n")); } /* Calculate the PER/txRtyCnt base on current info */ #ifdef SA_LUMP_SUM pTrainEntry->sumTxCnt = 0; pTrainEntry->sumTxRtyCnt = 0; pTrainEntry->sumTxFailCnt = 0; #else totalTxCnt = pMacEntry->saLstTxNoRtyCnt + pMacEntry->saLstTxRtyCnt + pMacEntry->saLstTxFailCnt; pBaseInfo->txNoRtyCnt = pMacEntry->saLstTxNoRtyCnt; pBaseInfo->txRtyCnt = pMacEntry->saLstTxRtyCnt + pMacEntry->saLstTxFailCnt; pBaseInfo->txRtyFailCnt = pMacEntry->saLstTxFailCnt; pBaseInfo->txCnt = totalTxCnt; if (pBaseInfo->txCnt != 0) pBaseInfo->PER = (pBaseInfo->txRtyCnt * 100) / pBaseInfo->txCnt; else pBaseInfo->PER = 0xffffffff; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("BaseAntPattern=0x%x,offset=%d,txCnt=(T:%d,S:%d,R:%d,F:%d), PER=%d\n", pBaseInfo->antPattern, pBaseInfo->patternOffset, pBaseInfo->txCnt, pBaseInfo->txNoRtyCnt, pBaseInfo->txRtyCnt, pBaseInfo->txRtyFailCnt, pBaseInfo->PER)); #endif // SA_LUMP_SUM // /* Select the antPattern from TrainingSeq and set to GPIO */ pTrainEntry->curAntPattern = 0; pTrainEntry->patternOffset = 0; pTrainEntry->bLastRnd = 0; sa_get_nextAntPattern(pSAParam, pTrainEntry, pTrainEntry->trainWeight, ANT_SELECT_IGNORE_BASE); sa_set_antPattern(pAd, pSAParam, pBaseInfo->antPattern, pTrainEntry->curAntPattern); pTrainEntry->pCurTrainInfo = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); reset_trainlog_stats_record(pTrainEntry->pCurTrainInfo); NdisGetSystemUpTime(&pTrainEntry->pCurTrainInfo->srtTime); pTrainEntry->pCurTrainInfo->txMcs = pMacEntry->HTPhyMode.field.MCS; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("StartToTestAntPattern:0x%x(time=0x%lx!)\n", pTrainEntry->curAntPattern, pTrainEntry->pCurTrainInfo->srtTime)); bNeedTune = TRUE; } // block the rate adaptation when we start to do antenna training! pTrainEntry->bTraining = bNeedTune; } RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlag); if (bNeedTune) { DBGPRINT(RT_DEBUG_OFF,("Call SA SwitchTimer!\n\n")); RTMPSetTimer(&pSAParam->saSwitchTimer, pSAParam->chkPeriod); } return TRUE; } INT RtmpSATrainInfoUpdate( IN RTMP_ADAPTER *pAd, IN RTMP_SA_TRAINING_PARAM *pTrainEntry, IN RXWI_STRUC *pRxWI, IN UCHAR txNss) { RTMP_SA_TRAIN_LOG_ELEMENT *pTrainLog; UCHAR idx1, idx2, idx3; pTrainLog = pTrainEntry->pCurTrainInfo; if (pTrainLog == NULL) return -1; #ifdef SA_DBG // record the Rssi distribution idx1 = (pRxWI->RXWI_O.RSSI0 >> 2); if (idx1 < 32) pTrainLog->rssiDist[0][idx1]++; else pTrainLog->rssiDist[0][32]++; idx2 = (pRxWI->RXWI_O.RSSI1 >> 2); if (idx2 < 32) pTrainLog->rssiDist[0][idx2]++; else pTrainLog->rssiDist[0][32]++; idx3 = (pRxWI->RXWI_O.RSSI2 >> 2); if (idx3 < 32) pTrainLog->rssiDist[0][idx3]++; else pTrainLog->rssiDist[0][32]++; // record the SNR dirstibution idx1 = (pRxWI->RXWI_O.SNR0 >> 3); if (idx1 < 32) pTrainLog->SNRDist[0][idx1]++; else pTrainLog->SNRDist[0][32]++; idx2 = (pRxWI->RXWI_O.SNR1 >> 3); if (idx2 < 32) pTrainLog->SNRDist[1][idx2]++; else pTrainLog->SNRDist[1][32]++; #ifdef DOT11N_SS3_SUPPORT idx3 = (pRxWI->RXWI_O.SNR2 >> 3); if (idx3 < 32) pTrainLog->SNRDist[2][idx3]++; else pTrainLog->SNRDist[2][32]++; // record the BF_SNR distribution if (pRxWI->RXWI_O.BF_SNR0 < 32) pTrainLog->preSNRDist[0][pRxWI->RXWI_O.BF_SNR0]++; else pTrainLog->preSNRDist[0][32]++; if (pRxWI->RXWI_O.BF_SNR1 < 32) pTrainLog->preSNRDist[1][pRxWI->RXWI_O.BF_SNR1]++; else pTrainLog->preSNRDist[1][32]++; if (pRxWI->RXWI_O.BF_SNR2 < 32) pTrainLog->preSNRDist[2][pRxWI->RXWI_O.BF_SNR2]++; else pTrainLog->preSNRDist[2][32]++; #endif // DOT11N_SS3_SUPPORT // #endif // SA_DBG // // RSSI if (pRxWI->RXWI_O.RSSI0) { pTrainLog->sumRSSI[0] += (pRxWI->RXWI_O.RSSI0 & 0xff); pTrainLog->cntRSSI[0]++; } if (pRxWI->RXWI_O.RSSI1) { pTrainLog->sumRSSI[1] += (pRxWI->RXWI_O.RSSI1 & 0xff); pTrainLog->cntRSSI[1]++; } if (pRxWI->RXWI_O.RSSI2) { pTrainLog->sumRSSI[2] += (pRxWI->RXWI_O.RSSI2 & 0xff); pTrainLog->cntRSSI[2]++; } // SNR if (pRxWI->RXWI_O.SNR0) { pTrainLog->sumSNR[0] += (pRxWI->RXWI_O.SNR0 & 0xff); pTrainLog->cntSNR[0]++; } if (pRxWI->RXWI_O.SNR1) { pTrainLog->sumSNR[1] += (pRxWI->RXWI_O.SNR1 & 0xff); pTrainLog->cntSNR[1]++; } #ifdef DOT11N_SS3_SUPPORT if (pRxWI->RXWI_O.SNR2) { pTrainLog->sumSNR[2] += (pRxWI->RXWI_O.SNR0 & 0xff); pTrainLog->cntSNR[2]++; } // PreSNR if (pRxWI->RXWI_O.BF_SNR0) { pTrainLog->sumPreSNR[0] += (pRxWI->RXWI_O.BF_SNR0 & 0xff); pTrainLog->cntPreSNR[0]++; } if (pRxWI->RXWI_O.BF_SNR1) { pTrainLog->sumPreSNR[1] += (pRxWI->RXWI_O.BF_SNR1 & 0xff); pTrainLog->cntPreSNR[1]++; } if (pRxWI->RXWI_O.BF_SNR2) { pTrainLog->sumPreSNR[2] += (pRxWI->RXWI_O.BF_SNR2 & 0xff); pTrainLog->cntPreSNR[2]++; } #endif // DOT11N_SS3_SUPPORT // pTrainLog->rxCnt++; return 0; } INT sa_pkt_radio_info_update(RTMP_ADAPTER *pAd, RX_BLK *pRxBlk, MAC_TABLE_ENTRY *pEntry) { SMART_ANTENNA_STRUCT *pSAParam = pAd->pSAParam; RTMP_SA_TRAINING_PARAM *pTrainEntry = &pSAParam->trainEntry[0]; if (pRxBlk->rx_signal.raw_rssi[0]) { pEntry->curRSSI[0] += (pRxBlk->rx_signal.raw_rssi[0] & 0xff); pEntry->cntRSSI[0]++; } else { pEntry->rssi_zero_cnt[0]++; DBGPRINT(RT_DEBUG_TRACE, ("Err!Receive A frame with RSSI0=%d!TotalZeroCnt=%d!\n", pRxBlk->rx_signal.raw_rssi[0], pEntry->rssi_zero_cnt[0])); } if (pRxBlk->rx_signal.raw_rssi[1]) { pEntry->curRSSI[1] += (pRxBlk->rx_signal.raw_rssi[1] & 0xff); pEntry->cntRSSI[1]++; } else { pEntry->rssi_zero_cnt[1]++; DBGPRINT(RT_DEBUG_TRACE, ("Err!Receive A frame with RSSI1=%d!TotalZeroCnt=%d!\n", pRxBlk->rx_signal.raw_rssi[1], pEntry->rssi_zero_cnt[1])); } #ifdef DOT11N_SS3_SUPPORT if (pRxBlk->rx_signal.raw_rssi[2]) { pEntry->curRSSI[2] += (pRxBlk->rx_signal.raw_rssi[2] & 0xff); pEntry->cntRSSI[2]++; } #endif /* DOT11N_SS3_SUPPORT */ /* SNR */ if (pRxBlk->rx_signal.raw_snr[0]) { pEntry->sumSNR[0] += (pRxBlk->rx_signal.raw_snr[0] & 0xff); pEntry->cntSNR[0]++; } if (pRxBlk->rx_signal.raw_snr[1]) { pEntry->sumSNR[1] += (pRxBlk->rx_signal.raw_snr[1] & 0xff); pEntry->cntSNR[1]++; } #ifdef RTMP_MAC #ifdef DOT11N_SS3_SUPPORT if (pAd->chipCap.hif_type == HIF_RTMP) { if (pRxBlk->rx_signal.raw_snr[2]) { pEntry->sumSNR[2] += (pRxBlk->rx_signal.raw_snr[2] & 0xff); pEntry->cntSNR[2]++; } /* PreSNR */ if (pRxBlk->pRxWI.RXWI_O.BF_SNR0) { pEntry->sumPreSNR[0] += (pRxBlk->pRxWI.RXWI_O.BF_SNR0 & 0xff); pEntry->cntPreSNR[0]++; } if (pRxBlk->pRxWI.RXWI_O.BF_SNR1) { pEntry->sumPreSNR[1] += (pRxBlk->pRxWI.RXWI_O.BF_SNR1 & 0xff); pEntry->cntPreSNR[1]++; } if (pRxBlk->pRxWI.RXWI_O.BF_SNR2) { pEntry->sumPreSNR[2] += (pRxBlk->pRxWI.RXWI_O.BF_SNR2 & 0xff); pEntry->cntPreSNR[2]++; } } #endif /* DOT11N_SS3_SUPPORT */ #endif /* RTMP_MAC */ pEntry->saRxCnt++; if (pTrainEntry == pEntry->pTrainEntry) RtmpSATrainInfoUpdate(pAd, pTrainEntry, pRxBlk->pRxWI, pSAParam->txNss); } VOID RtmpSAUpdateRxSignal( IN RTMP_ADAPTER *pAd) { unsigned long saflags; BOOLEAN bLocked = FALSE; MAC_TABLE_ENTRY *pEntry; INT macIdx; if (!in_interrupt()) { bLocked = TRUE; RTMP_IRQ_LOCK(pAd, saflags); } if (!RTMP_SA_WORK_ON(pAd)) { if (bLocked) RTMP_IRQ_UNLOCK(pAd, saflags); return; } for (macIdx = 1; macIdx < MAX_LEN_OF_MAC_TABLE; macIdx++) { if ((pEntry = &pAd->MacTab.Content[macIdx]) == NULL) continue; if (!IS_ENTRY_CLIENT(pEntry) || (pEntry->Sst != SST_ASSOC)) continue; /* calculate the RSSI/SNR/PreSNR */ sa_calc_rx_signal_info(pAd, pEntry); if ((pEntry->cntRSSI[0] >= 15) && (pEntry->avgRssi[0] != 0)) { if (pEntry->prevAvgRssi[0] != 0) { CHAR oldRssi, newRssi; oldRssi = pEntry->prevAvgRssi[0]; newRssi = pEntry->avgRssi[0]; if(((oldRssi > newRssi) && ((oldRssi - newRssi) > 15))|| ((oldRssi < newRssi) && ((newRssi - oldRssi) > 15)) ) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("wcid:%d, RSSI variant(O:%d,N:%d) > 15, set bRssiChange!\n", pEntry->wcid, oldRssi, pEntry->avgRssi[0])); pEntry->prevAvgRssi[0] = pEntry->avgRssi[0]; pAd->pSAParam->bRssiChange = TRUE; } } else pAd->pSAParam->bRssiChange = TRUE; if (pAd->pSAParam->bRssiChange == TRUE) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("wcid:%d, Update RSSI0(O:%d,N:%d)\n", pEntry->wcid, pEntry->prevAvgRssi[0], pEntry->avgRssi[0])); pEntry->prevAvgRssi[0] = pEntry->avgRssi[0]; } } /* clean all counters for next record */ reset_mac_entry_stats(pEntry); } if (bLocked) RTMP_IRQ_UNLOCK(pAd, saflags); } /* Call to start the SmartAntenna mechanism */ int RtmpSAStop( IN RTMP_ADAPTER *pAd) { SMART_ANTENNA_STRUCT *pSAParam; unsigned long irqFlags; int retVal = FALSE; BOOLEAN canceled = FALSE; RTMP_IRQ_LOCK(&pAd->smartAntLock, irqFlags); pSAParam = pAd->pSAParam; if (pSAParam) { pSAParam->trainEntry[0].trainStage = SA_INVALID_STAGE; pSAParam->bStaChange = FALSE; RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlags); RTMPCancelTimer(&pSAParam->saSwitchTimer, &canceled); RTMP_IRQ_LOCK(&pAd->smartAntLock, irqFlags); } pAd->smartAntEnable = FALSE; RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlags); return retVal; } /* Call to start the SmartAntenna mechanism, it's mainly enable the flag "smartAntEnable" */ int RtmpSAStart(RTMP_ADAPTER *pAd) { int retVal = TRUE, i; unsigned long irqFlags; SMART_ANTENNA_STRUCT *pSAParam; RTMP_SA_TRAINING_PARAM *pSAStaEntry; //RTMP_SA_TRAIN_LOG_ELEMENT *pTrainLog; RTMP_IRQ_LOCK(&pAd->smartAntLock, irqFlags); pSAParam = pAd->pSAParam; /* sanity check */ if (!RTMP_SA_WORK_ON(pAd)) { DBGPRINT(RT_DEBUG_OFF,("%s():pAd->smartAntEnable=%d, pSAParam=0x%x\n", __FUNCTION__, pAd->smartAntEnable, (UINT32)pSAParam)); pAd->smartAntEnable = FALSE; RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlags); return FALSE; } /* allocate related resource for SA */ if ((pSAParam->agsp == NULL) || (pSAParam->pTrainSeq == NULL)) { DBGPRINT(RT_DEBUG_OFF,("%s():AGSP(0x%x)/TrainSeq(0x%x) not configured, use default!\n", __FUNCTION__, (UINT32)pSAParam->agsp, (UINT32)pSAParam->pTrainSeq)); sa_train_db_exit(pAd, pSAParam); if (pSAParam->agsp && (pSAParam->agsp != DefaultAGSP)) { os_free_mem(pAd, pSAParam->agsp); pSAParam->agsp = NULL; } pSAParam->agsp = &DefaultAGSP[0]; sa_train_db_init(pAd, pSAParam, &DefaultTrainSeq[0]); } if (pSAParam->pTrainMem == NULL) { DBGPRINT(RT_DEBUG_OFF,("Allocate memory for TrainLog failed!\n")); pAd->smartAntEnable = FALSE; RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlags); return FALSE; } /* Init pSAParam parameters */ // set the default candidate selection method if (pSAParam->candMethod == 0) pSAParam->candMethod = 4; // If the candidate selection method is 3, set the default PER threshold as 80 if ((pSAParam->candMethod == 3) && (pSAParam->trainCond == 0)) { pSAParam->trainCond = 80; } // If the candidate selection method is 4, set the default PER Upper bound as 50, i.e, 5% if ((pSAParam->candMethod == 4) && (pSAParam->trainCond == 0)) { pSAParam->trainCond = 100; } /* Start Smart Antenna algorithm for each supported peer */ for (i = 0; i < SA_ENTRY_MAX_NUM; i++) { /* Init the TrainEntry and associated TrainLogInfo space */ pSAStaEntry = &pSAParam->trainEntry[i]; retVal = sa_init_train_entry(pAd, pSAParam, pSAStaEntry, pSAStaEntry->pTrainInfo, NULL); if (retVal == FALSE) { RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlags); goto done; } DBGPRINT(RT_DEBUG_OFF,("%s():Entry[%d]: pTrainLog=0x%lx,Addr=%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, i, (ULONG)pSAStaEntry->pTrainInfo, PRINT_MAC(pSAStaEntry->macAddr))); } sa_select_target_train_entry(pAd, pSAParam); /* Init each specific train station entries */ for (i = 0; i < SA_ENTRY_MAX_NUM; i++) { pSAStaEntry = &pSAParam->trainEntry[i]; //rtmp_sa_cfg_train_entry(pAd, pSAStaEntry); if (NdisEqualMemory(&pSAStaEntry->macAddr[0], ZERO_MAC_ADDR, MAC_ADDR_LEN)) { int j; // find first valid sta as the target for( j = 1; j < MAX_LEN_OF_MAC_TABLE; j++) { MAC_TABLE_ENTRY *pEntry = NULL; pEntry = &pAd->MacTab.Content[j]; if ((NdisEqualMemory(&pEntry->Addr[0], ZERO_MAC_ADDR, MAC_ADDR_LEN) != TRUE) && IS_ENTRY_CLIENT(pEntry) && (pEntry->pTrainEntry == NULL)) { NdisMoveMemory(&pSAStaEntry->macAddr[0], &pEntry->Addr[0], MAC_ADDR_LEN); pSAStaEntry->pMacEntry = pEntry; pEntry->pTrainEntry = (void *)pSAStaEntry; break; } } } if (is_valid_train_entry(pAd, pSAParam, pSAStaEntry)) { ULONG nowTime; DBGPRINT(RT_DEBUG_OFF,("%s():Ready to do SA training for %02x:%02x:%02x:%02x:%02x:%02x!\n", __FUNCTION__, PRINT_MAC(pSAStaEntry->macAddr))); pSAStaEntry->trainStage = SA_INIT_STAGE; pSAStaEntry->trainWeight = ANT_WEIGHT_SCAN_ALL; /* get current physically applied antenna pattern */ sa_get_curAntPattern(pSAParam, pSAStaEntry); if (pSAStaEntry->curAntPattern == 0) sa_get_nextAntPattern(pSAParam, pSAStaEntry, pSAStaEntry->trainWeight, ANT_SELECT_FIRST); pSAStaEntry->pCurTrainInfo = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pSAStaEntry->pTrainInfo + pSAStaEntry->patternOffset); DBGPRINT(RT_DEBUG_OFF,("%s(): The SmartAnt start with antPattern(0x%x-%d), pCurLog(0x%p, 0x%x-%d)\n", __FUNCTION__, pSAStaEntry->curAntPattern, pSAStaEntry->patternOffset, pSAStaEntry->pCurTrainInfo, pSAStaEntry->pCurTrainInfo->antPattern, pSAStaEntry->pCurTrainInfo->patternOffset)); NdisGetSystemUpTime(&nowTime); pSAStaEntry->time_to_start = nowTime + pSAParam->trainDelay * OS_HZ; DBGPRINT(RT_DEBUG_OFF,("%s():set time_to_start=0x%lx\n", __FUNCTION__, pSAStaEntry->time_to_start)); retVal = TRUE; } else { DBGPRINT(RT_DEBUG_OFF,("%s():TrainEntry(%02x:%02x:%02x:%02x:%02x:%02x) is not valid\n", __FUNCTION__, PRINT_MAC(pSAStaEntry->macAddr))); //retVal = FALSE; //pAd->smartAntEnable = FALSE; } } RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlags); done: if (retVal == FALSE) RtmpSAStop(pAd); return retVal; } /* Global functions used to hook to the driver */ int RtmpSAInit(RTMP_ADAPTER *pAd) { SMART_ANTENNA_STRUCT *pSAParam; unsigned long irqFlags; int status; /* allocate/init resources and structures */ os_alloc_mem(pAd, (UCHAR **)&pAd->pSAParam, sizeof(SMART_ANTENNA_STRUCT)); DBGPRINT(RT_DEBUG_OFF,("Allocate SAParam memory(0x%p) with size(0x%x)\n", pAd->pSAParam, sizeof(SMART_ANTENNA_STRUCT))); if (pAd->pSAParam) { /* initailize the lock structure */ NdisAllocateSpinLock(&pAd->smartAntLock); RTMP_IRQ_LOCK(&pAd->smartAntLock, irqFlags); pSAParam = pAd->pSAParam; NdisZeroMemory(pSAParam, sizeof(SMART_ANTENNA_STRUCT)); RTMPInitTimer(pAd, &pSAParam->saSwitchTimer, GET_TIMER_FUNCTION(sa_switch_exec), pAd, FALSE); pSAParam->saMode = SA_MODE_AUTO; // SA_MODE_NONE; pSAParam->txNss = pAd->CommonCfg.TxStream; // SA_DEFAULT_TX_NSS pSAParam->saMsc = SA_DEFAULT_MSC; pSAParam->saMcsBound = (pAd->CommonCfg.TxStream * 8) - 1; // SA_DEFAULT_MCS_BOUND pSAParam->chkPeriod = SA_DEFAULT_CHK_PERIOD; pSAParam->maxAntTry = SA_DEFAULT_ANT_TRIAL; pSAParam->rssiThreshold = SA_DEFAULT_RSSI_THRESHOLD; pSAParam->trainDelay = 0; pSAParam->rssiVar = SA_DEFAULT_RSSI_VAR; pSAParam->bSkipConfStage = TRUE; pSAParam->bStaChange = FALSE; pSAParam->agsp = &DefaultAGSP[0]; #ifdef SA_TRAIN_SBS pSAParam->trainCond = SA_TRAIN_GRADUAL_APPROACH; #endif // SA_TRAIN_SBS // status = sa_train_db_init(pAd, pSAParam, &DefaultTrainSeq[0]); if (status == FALSE) { DBGPRINT(RT_DEBUG_OFF,("%s():DBInit failed!\n", __FUNCTION__)); } //set the first TrainSeq as the default antenna pattern sa_set_antPattern(pAd, pSAParam, 0, DefaultTrainSeq[0]); /* kick to go */ //pAd->smartAntEnable = TRUE; pAd->smartAntEnable = FALSE; RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlags); } else { DBGPRINT(RT_DEBUG_ERROR | DBG_FUNC_SA, ("Allocate Memory for SAParam failed\n")); } //RtmpSAStart(pAd); return TRUE; } int RtmpSAExit(RTMP_ADAPTER *pAd) { SMART_ANTENNA_STRUCT *pSAParam; unsigned long irqFlags; BOOLEAN Cancelled = FALSE; pSAParam = pAd->pSAParam; if (pSAParam) { /* stop it now */ RtmpSAStop(pAd); /* free resources */ RTMP_IRQ_LOCK(&pAd->smartAntLock, irqFlags); sa_train_db_exit(pAd, pSAParam); if (pSAParam->agsp && (pSAParam->agsp != &DefaultAGSP[0])) os_free_mem(pAd, pSAParam->agsp); pSAParam->agsp = NULL; os_free_mem(pAd, pAd->pSAParam); pAd->pSAParam = NULL; RTMP_IRQ_UNLOCK(&pAd->smartAntLock, irqFlags); RTMPReleaseTimer(pAd, &pSAParam->saSwitchTimer, &Cancelled); /* free the lock structure */ NdisFreeSpinLock(pAd->smartAntLock); } return 0; } /* iwpriv ra0 set sa_enable=0~1 */ void show_sa_cmd_usage(RTMP_ADAPTER *pAd) { DBGPRINT(RT_DEBUG_OFF, ("iwpriv usage for SmartAntenna Related Sub Commands:\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpriv ra0 set sa\n")); DBGPRINT(RT_DEBUG_OFF,("\t\tshow this help message out\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpriv ra0 set sa=0~1\n")); DBGPRINT(RT_DEBUG_OFF,("\t\t0: disable the SA mechanism\n")); DBGPRINT(RT_DEBUG_OFF,("\t\t1: enable the SA mechanism\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpirv ra0 set sa_msc=0~255\n")); DBGPRINT(RT_DEBUG_OFF,("\t\tset the condition of stable MCS count for trigger the SA training algorithm\n")); DBGPRINT(RT_DEBUG_OFF,("\t\t\t0: run SA algorithm no matter MCS is in stable stage or not\n")); DBGPRINT(RT_DEBUG_OFF,("\t\t\t1<= x <=254: run SA algorithm when MCS stay in stable stage for x times\n")); DBGPRINT(RT_DEBUG_OFF,("\t\t\t255: reserved and should not use now\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpriv ra0 set sa_sta=xx:xx:xx:xx:xx:xx\n")); DBGPRINT(RT_DEBUG_OFF,("\t\tSet the target station used to support Smart Antenna\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpriv ra0 set sa_agsp\n")); DBGPRINT(RT_DEBUG_OFF,("\t\t\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpriv ra0 set sa_maxAntennaTry\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpriv ra0 set sa_dbg\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpriv ra0 set sa_tp\n")); DBGPRINT(RT_DEBUG_OFF,("\tiwpriv ra0 set sa_ant=T0-T1\n")); DBGPRINT(RT_DEBUG_OFF,("\t\tSet the static tx antenna as Antenna HeaderPin T0 and T1\n")); DBGPRINT(RT_DEBUG_OFF,("\t\t\t 1<= LowestPinBoundaryInAGSP <= T0, T1 <= HighestPinBoundayInAGSP <=255\n")); return; } /****************************************************************************** IOCTL cmds used to set Smart Antenna related paramters ******************************************************************************/ INT Show_SA_DbgInfo_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { return TRUE; } // TODO: // TODO: Following codes is finished!!!!! // TODO: INT Set_DbgLogOn_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { UINT Value = 0; Value = (UINT) simple_strtol(arg, 0, 10); //pAd->smartAntDbgOn = (Value == 1? TRUE : FALSE); RTDebugFunc = (Value & 0xff); DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("%s():Set RTDebugFunc=0x%lx!\n", __FUNCTION__, RTDebugFunc)); return TRUE; } /* iwpriv ra0 set sa_tc=1~4 iwpriv ra0 set sa_tp=x ("x" in unit of ms) Note: sa_tc * sa_tp shall small than 400ms */ INT Set_MaxAntennaTry_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { pAd->pSAParam->maxAntTry = (UCHAR)simple_strtol(arg, 0, 10); return TRUE; } INT Set_TestPeriod_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { pAd->pSAParam->chkPeriod = (int)simple_strtol(arg, 0, 10); return TRUE; } /* iwpriv ra0 show sainfo */ INT Show_SA_CfgInfo_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { SMART_ANTENNA_STRUCT *pSAParam; RTMP_SA_TRAINING_PARAM *pStaEntry; RTMP_SA_AGSP_MAP *pAgspEntry; UINT32 *pTrainingId; int i; pSAParam = pAd->pSAParam; DBGPRINT(RT_DEBUG_OFF, ("Dump the SmartAntenna Configuration Info:\n")); DBGPRINT(RT_DEBUG_OFF,("\tSAEnable=%s\n", pAd->smartAntEnable == TRUE ? "TRUE" : "FALSE")); DBGPRINT(RT_DEBUG_OFF,("\tSADbg=%s\n", pAd->smartAntDbgOn == TRUE ? "ON" : "OFF")); DBGPRINT(RT_DEBUG_OFF,("\tpSAParam:\n")); if (pSAParam == NULL) { DBGPRINT(RT_DEBUG_OFF,("\t\tThe SMART_ANTENNA_STRUCT is not initialized!\n\n")); return 0; } DBGPRINT(RT_DEBUG_OFF,("\t\tsaMode=%s\n",SA_MODE[pAd->pSAParam->saMode])); DBGPRINT(RT_DEBUG_OFF,("\t\tTxNss=%d\n", pSAParam->txNss)); DBGPRINT(RT_DEBUG_OFF,("\t\tsaMcsStableCnt=%d\n", pSAParam->saMsc)); DBGPRINT(RT_DEBUG_OFF,("\t\tsaMcsBound=%d\n", pSAParam->saMcsBound)); DBGPRINT(RT_DEBUG_OFF,("\t\tmaxAntTry=%d\n", pSAParam->maxAntTry)); DBGPRINT(RT_DEBUG_OFF,("\t\tTrainCond=%d\n", pSAParam->trainCond)); DBGPRINT(RT_DEBUG_OFF,("\t\tcandMethod=%d\n", pSAParam->candMethod)); DBGPRINT(RT_DEBUG_OFF,("\t\ttrainDelay=%d\n", pSAParam->trainDelay)); DBGPRINT(RT_DEBUG_OFF,("\t\tTrainingPeriod=%d\n", pSAParam->chkPeriod)); DBGPRINT(RT_DEBUG_OFF,("\t\trssiVar=%d\n", pSAParam->rssiVar)); DBGPRINT(RT_DEBUG_OFF,("\t\tbSkipConfStage=%d\n", pSAParam->bSkipConfStage)); DBGPRINT(RT_DEBUG_OFF,("\t\tbStaChange=%d\n", pSAParam->bStaChange)); DBGPRINT(RT_DEBUG_OFF,("\t\tagspCnt=%d\n", pSAParam->agspCnt)); DBGPRINT(RT_DEBUG_OFF,("\t\ttrainSeqCnt=%d\n", pSAParam->trainSeqCnt)); for (i = 0; i < SA_ENTRY_MAX_NUM; i++) { pStaEntry = &pSAParam->trainEntry[i]; DBGPRINT(RT_DEBUG_OFF,("\t\tTrainEntry:\n")); DBGPRINT(RT_DEBUG_OFF, ("\t\t\tbStatic=%d\n", pStaEntry->bStatic)); DBGPRINT(RT_DEBUG_OFF,("\t\t\tRealTimeCntOfMcsInStableStage=%d\n", pStaEntry->mcsStableCnt)); DBGPRINT(RT_DEBUG_OFF, ("\t\t\ttrainStage=%d\n", pStaEntry->trainStage)); DBGPRINT(RT_DEBUG_OFF,("\t\t\tbTraining=%s\n", pStaEntry->bTraining ? "TRUE" : "FALSE")); if (pStaEntry->curAntPattern == 0) sa_get_curAntPattern(pSAParam, pStaEntry); DBGPRINT(RT_DEBUG_OFF,("\t\t\tcurAntPattern=0x%x-%d\n", pStaEntry->curAntPattern, pStaEntry->patternOffset)); DBGPRINT(RT_DEBUG_OFF,("\t\t\tMacAddr=%02x:%02x:%02x:%02x:%02x:%02x\n", PRINT_MAC(pStaEntry->macAddr))); DBGPRINT(RT_DEBUG_OFF,("\t\t\tMapped MacTableEntry=0x%x\n", (UINT32)pStaEntry->pMacEntry)); if (pStaEntry->pMacEntry) { DBGPRINT(RT_DEBUG_OFF,("\t\t\t\tWCID=%d\n", pStaEntry->pMacEntry->wcid)); DBGPRINT(RT_DEBUG_OFF,("\t\t\t\tBW:MCS=%d:%d\n", pStaEntry->pMacEntry->HTPhyMode.field.BW, pStaEntry->pMacEntry->HTPhyMode.field.MCS)); } DBGPRINT(RT_DEBUG_OFF,("\t\t\tAGSP:\n")); pAgspEntry = pSAParam->agsp; if (pAgspEntry == NULL) { DBGPRINT(RT_DEBUG_OFF,("\t\t\t\tThe AGSP is not initialized, dump default one!\n")); pAgspEntry = &DefaultAGSP[0]; } DBGPRINT(RT_DEBUG_OFF,("\t\t\t\tHeaderPin GpioRegOffset GpioBit\n")); while(memcmp(pAgspEntry, &AGSP_ENTRY_END, sizeof(*pAgspEntry))!= 0) { DBGPRINT(RT_DEBUG_OFF,("\t\t\t\t%d\t0x%x\t%d\n", pAgspEntry->hdrPin, pAgspEntry->regOffset, pAgspEntry->gpioBit)); pAgspEntry++; } DBGPRINT(RT_DEBUG_OFF,("\t\t\tTrainingSequence(cnt=%d):\n", pSAParam->trainSeqCnt)); pTrainingId = pSAParam->pTrainSeq; if (pTrainingId == NULL) { DBGPRINT(RT_DEBUG_OFF,("\t\t\t\tThe TrainingSequence is not initialized, dump default one!\n")); pTrainingId = &DefaultTrainSeq[0]; } DBGPRINT(RT_DEBUG_OFF,("\t\t\t\t")); while(*pTrainingId != 0) { DBGPRINT(RT_DEBUG_OFF,(" 0x%x", *pTrainingId)); pTrainingId++; } DBGPRINT(RT_DEBUG_OFF, ("\n")); } DBGPRINT(RT_DEBUG_OFF,("\n")); return TRUE; } /* iwpriv ra0 set sa_tseq=0102:0304:0506 */ int Set_SA_TrainSeq_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *args) { SMART_ANTENNA_STRUCT *pSAParam; UINT32 antPair, *pTrainSeq, *pTrainSeqLst; int entryCnt, i, bUseDefault = FALSE; char *ptr, *pPos, *token; DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA,("Input string is:%s\n", args)); pSAParam = pAd->pSAParam; if (pSAParam == NULL) return FALSE; if (strcmp(args, "default") == 0) { bUseDefault = TRUE; if (pSAParam->agsp != &DefaultAGSP[0]) { DBGPRINT(RT_DEBUG_ERROR | DBG_FUNC_SA, ("Error:The AGSP list is not specified yet!\n")); return FALSE; } pTrainSeqLst = &DefaultTrainSeq[0]; } else { entryCnt = 1; // reserve one for AGSP_TRAIN_SEQ_END ptr = args; while( (pPos = strchr(ptr, ':'))!= NULL) { entryCnt++; ptr = (pPos +1); } if (strlen(ptr)) { entryCnt+=1; // add the last one } DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA,("entryCnt=%d\n", entryCnt)); if (entryCnt < 2) return FALSE; /* Now it's time to allocate memory for the TrainSeq list and insert those entries to the list */ i = entryCnt * sizeof(UINT32); if (os_alloc_mem(pAd, (UCHAR **)&pTrainSeqLst, i) == NDIS_STATUS_FAILURE) { DBGPRINT(RT_DEBUG_ERROR | DBG_FUNC_SA, ("Alloc mem for pTrainSeq failed\n")); return FALSE; } NdisZeroMemory(pTrainSeqLst, i); DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Allocate memory for pTrainSeq is %d\n", i)); pTrainSeq = pTrainSeqLst; pPos = args; i = 0; do { token = tokenParser(&pPos, ':'); if(strlen(token)) { A2Hex(antPair, token); /* move to next entry tuple */ *pTrainSeq = antPair; pTrainSeq++; } i++; if (i >= (entryCnt - 1)) { DBGPRINT(RT_DEBUG_OFF | DBG_FUNC_SA, ("Parsing whole TrainSeq list down, handle cnt=%d!\n", i)); break; } }while(pPos); } if (pTrainSeqLst) { sa_train_db_init(pAd, pSAParam, pTrainSeqLst); sa_dump_train_seq(pSAParam->pTrainSeq); } return TRUE; } /* iwpriv ra0 set sa_tdelay=x set the training delay */ int Set_SA_TrainDelay_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *args) { UINT32 delayTime; SMART_ANTENNA_STRUCT *pSAParam; delayTime = simple_strtol(args, NULL, 10); pSAParam = pAd->pSAParam; if (pSAParam == NULL || delayTime < 0) return FALSE; DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("SA trainDelay(0x%x) change to(0x%x)\n", pSAParam->trainDelay, delayTime)); pSAParam->trainDelay = delayTime; return TRUE; } int Set_SA_AntCand_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *args) { UINT32 candMethod; SMART_ANTENNA_STRUCT *pSAParam; candMethod = simple_strtol(args, NULL, 10); if ((candMethod < 1) || (candMethod > 4)) candMethod = 0; pSAParam = pAd->pSAParam; if (pSAParam) { pSAParam->candMethod = candMethod; DBGPRINT(RT_DEBUG_OFF,("%s():Set AntennaCandidateSelectMethod=%d!\n",__FUNCTION__, pSAParam->candMethod)); return TRUE; } else { DBGPRINT(RT_DEBUG_OFF,("%s():SmartAntenna feature is not enabled yet\n", __FUNCTION__)); return FALSE; } } /* 0x1: trainUpperMCS instead of train CurrentMCS */ int Set_SA_TrainCond_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *args) { UINT32 trainCond; SMART_ANTENNA_STRUCT *pSAParam; trainCond = simple_strtol(args, NULL, 10); pSAParam = pAd->pSAParam; if (pSAParam == NULL) return FALSE; /* // If the trainCond changed, we stop the on-going training sequence if (trainCond != pSAParam->trainCond) { RtmpSAStop(pAd); } */ pSAParam->trainCond = trainCond; DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("SA TrainCond(0x%x) change to(0x%x)\n", trainCond, pSAParam->trainCond)); return TRUE; } INT Set_SA_RssiVariance_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *args) { UCHAR rssiVar; SMART_ANTENNA_STRUCT *pSAParam; rssiVar = simple_strtol(args, NULL, 10); pSAParam = pAd->pSAParam; if (pSAParam == NULL) return FALSE; DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("SA RSSI variance threshold (0x%x) change to(0x%x)\n", pSAParam->rssiVar, rssiVar)); pSAParam->rssiVar = rssiVar; return TRUE; } INT Set_SA_RssiThreshold_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *args) { int rssiThold; SMART_ANTENNA_STRUCT *pSAParam; rssiThold = simple_strtol(args, NULL, 10); pSAParam = pAd->pSAParam; if (pSAParam == NULL) return FALSE; if (rssiThold <= -99 && rssiThold > -1) rssiThold = SA_DEFAULT_RSSI_THRESHOLD; DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("SA RSSI variance threshold (0x%x) change to(0x%x)\n", pSAParam->rssiThreshold, rssiThold)); pSAParam->rssiThreshold = rssiThold; return TRUE; } INT Set_SA_SkipConfirmStage_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *args) { BOOLEAN bSkipConfStage; SMART_ANTENNA_STRUCT *pSAParam; bSkipConfStage = simple_strtol(args, NULL, 10); pSAParam = pAd->pSAParam; if (pSAParam == NULL) return FALSE; bSkipConfStage = ((bSkipConfStage == 1 ) ? TRUE : FALSE); DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("SA skip Confirm stage (0x%x) change to(0x%x)\n", pSAParam->bSkipConfStage, bSkipConfStage)); pSAParam->bSkipConfStage = bSkipConfStage; return TRUE; } /* The AGSP pin table cmd show set in following format: iwpriv ra0 set sa_agsp=HeadrPin-GPIO_Register_Offset-GPIO_Register_BIT for example: iwpriv ra0 set sa_agsp=1-a4-2:2-a4-3:5-a4-8 */ int Set_SA_AGSP_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *args) { char *ptr, *pPos, *token, *subToken; int entryCnt, subCnt, i; SMART_ANTENNA_STRUCT *pSAParam; RTMP_SA_AGSP_MAP *pAgspEntry, *pAgspList = NULL; if (!args) { DBGPRINT(RT_DEBUG_ERROR | DBG_FUNC_SA, ("Input without arguments\n")); return FALSE; } DBGPRINT(RT_DEBUG_OFF,("Input string is:\n")); DBGPRINT(RT_DEBUG_OFF,("\t\t%s\n", args)); pSAParam = pAd->pSAParam; if (pSAParam == NULL) return FALSE; if (strcmp(args, "default") == 0) { pAgspList = &DefaultAGSP[0]; } else { /* Find how many entries need to added to the agsp list */ entryCnt = 1; // reserve one for AGSP_ENTRY_END ptr = args; while( (pPos = strchr(ptr, ':'))!= NULL) { entryCnt++; ptr = (pPos +1); } if (strlen(ptr)) { entryCnt+=1; // add the last one } DBGPRINT(RT_DEBUG_OFF,("entryCnt=%d\n", entryCnt)); if (entryCnt < 2) return FALSE; /* Now it's time to allocate memory for the agsp list and insert those entries to the list */ i = entryCnt * sizeof(RTMP_SA_AGSP_MAP); if (os_alloc_mem(pAd, (UCHAR **)&pAgspList, i) != NDIS_STATUS_SUCCESS) { DBGPRINT(RT_DEBUG_OFF,("Allocate Memory for AGSP failed\n")); return FALSE; } NdisZeroMemory(pAgspList, i); DBGPRINT(RT_DEBUG_OFF,("Allocate memory for AGSP success(addr=0x%lx, size=%d)\n", (ULONG)pAgspList, i)); /* Now parsing each tokens and insert to the agsp list */ i = 0; pPos = args; pAgspEntry = pAgspList; do { token = tokenParser(&pPos, ':'); if (strlen(token)) { subCnt = 0; ptr = token; do { subToken = tokenParser(&ptr, '-'); if (strlen(subToken)) { switch (subCnt) { case 0: pAgspEntry->hdrPin = (UINT8)simple_strtol(subToken, 0, 10); break; case 1: A2Hex(pAgspEntry->regOffset, subToken); break; case 2: pAgspEntry->gpioBit = (UINT32)simple_strtol(subToken, 0, 10); break; default: break; } subCnt++; } }while((ptr != NULL) && (subCnt < 3)); if ((subCnt == 3) && is_valid_agsp_entry(pAd, pAgspEntry)) { /* move to next entry tuple */ pAgspEntry++; } i++; if (i >= (entryCnt - 1)) { DBGPRINT(RT_DEBUG_OFF,("Parsing whole AGSP list down, handle cnt=%d!\n", i)); break; } } }while(pPos); } /* Remove old one and insert new AGSP */ if (pAgspList) { sa_train_db_exit(pAd, pSAParam); if (pSAParam->agsp && (pSAParam->agsp != &DefaultAGSP[0])) os_free_mem(pAd, pSAParam->agsp); pSAParam->agsp = pAgspList; if (pAgspList == &DefaultAGSP[0]) { sa_train_db_init(pAd, pSAParam, &DefaultTrainSeq[0]); } } if (pSAParam->agsp) { sa_dump_agsp(pSAParam->agsp); } return TRUE; } /* iwpriv ra0 set sa_ant=T0-T1 */ INT Set_SA_StaticAntPair_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { UCHAR pinVal[SA_TX_NSS_MAX_NUM]; SMART_ANTENNA_STRUCT *pSAParam; RTMP_SA_AGSP_MAP *pAgspEntry; RTMP_SA_TRAINING_PARAM *pTrainEntry; char *token, *pPos; int i, cnt = 0; UINT32 antPattern = 0; RTMP_SA_TRAIN_LOG_ELEMENT *pBaseInfo; pSAParam = pAd->pSAParam; if (pSAParam == NULL) { DBGPRINT(RT_DEBUG_OFF,("Smart Antenna is not initialized!\n")); return FALSE; } /* Parsing the pin Header */ pPos = arg; do{ token = tokenParser(&pPos, '-'); if (strlen(token) > 0) { pinVal[cnt] = (UCHAR)simple_strtol(token, 0, 10); cnt++; } if (cnt == SA_TX_NSS_MAX_NUM) break; }while(pPos); /* Check if the antPair is one element of the AGSP table */ if (cnt != pSAParam->txNss) { DBGPRINT(RT_DEBUG_OFF,("Error: AntPairSetting(%d) not equal txNss(%d)!!!\n", cnt, pSAParam->txNss)); return FALSE; } if (pSAParam->agsp) { pAgspEntry = pSAParam->agsp; while(0) //pAgspEntry->hdrPin != 0) { // TODO: check if the AntPair is valid in the AGSP list!! } } else { DBGPRINT(RT_DEBUG_OFF,("%s():Error!! AGSP(0x%lx) or TrainSeq(0x%lx) is not set yet!", __FUNCTION__, (ULONG)pSAParam->agsp, (ULONG)pSAParam->pTrainSeq)); return FALSE; } pTrainEntry = &pSAParam->trainEntry[0]; for (i = 0; i < cnt; i++) { antPattern |= pinVal[i] << (i*8); } DBGPRINT(RT_DEBUG_OFF, ("prevAntPattern=0x%x, newAntPattern=0x%x\n", pTrainEntry->curAntPattern, antPattern)); sa_set_antPattern(pAd, pSAParam, pTrainEntry->curAntPattern, antPattern); /* update antenna related data structures in pTrainEntry */ sa_get_curAntPattern(pSAParam, pTrainEntry); if (pTrainEntry->pTrainInfo) { pTrainEntry->pCurTrainInfo = (RTMP_SA_TRAIN_LOG_ELEMENT *)(pTrainEntry->pTrainInfo + pTrainEntry->patternOffset); NdisZeroMemory(pTrainEntry->pCurTrainInfo, sizeof(RTMP_SA_TRAIN_LOG_ELEMENT)); pTrainEntry->pCurTrainInfo->antPattern = pTrainEntry->curAntPattern; pTrainEntry->pCurTrainInfo->patternOffset = pTrainEntry->patternOffset; } pBaseInfo = &pTrainEntry->antBaseInfo; NdisZeroMemory(pBaseInfo, sizeof(RTMP_SA_TRAIN_LOG_ELEMENT)); pBaseInfo->antPattern = antPattern; pBaseInfo->patternOffset = pTrainEntry->patternOffset; if (pTrainEntry->curAntPattern) { DBGPRINT(RT_DEBUG_OFF,("Static AntPattern=0x%x, AGSP Offset=%d!\n", pTrainEntry->curAntPattern, pTrainEntry->patternOffset)); return TRUE; } else { DBGPRINT(RT_DEBUG_OFF,("Static Set AntPattern(0x%x) failed!\n", pTrainEntry->curAntPattern)); return FALSE; } } /* iwpriv ra0 set sa_txNss=1~4 */ INT set_SA_txNss_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { ULONG value; value = simple_strtol(arg, 0, 10); if ((value > SA_TX_NSS_MAX_NUM) || (value == 0)) { DBGPRINT(RT_DEBUG_OFF,("Error: Request txNss(%ld) out of range(1~%d) of hardware limitation\n", value, SA_TX_NSS_MAX_NUM)); return TRUE; } else { if(pAd->pSAParam) pAd->pSAParam->txNss = (UCHAR)value; else { DBGPRINT(RT_DEBUG_OFF,("Error: Smart Antenna not properly initialized!\n")); } } return TRUE; } /* iwpriv ra0 set sa_mode = 0~3 { SA_MODE_NONE = 0, SA_MODE_MANUAL = 1, SA_MODE_ONESHOT = 2, SA_MODE_AUTO = 3, } */ INT Set_SA_Mode_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { ULONG Value = 0; SMART_ANTENNA_STRUCT *pSAParam; Value = (ULONG)simple_strtol(arg, 0,10); if ((Value < 1) || (Value > 3)) Value = SA_MODE_NONE; pSAParam = pAd->pSAParam; if (pSAParam) { DBGPRINT(RT_DEBUG_OFF,("%s():Disable SA before change the saMode!\n", __FUNCTION__)); RtmpSAStop(pAd); pSAParam->saMode = Value; DBGPRINT(RT_DEBUG_OFF,("%s():saMode=%s!\n",__FUNCTION__, SA_MODE[pSAParam->saMode])); return TRUE; } else { DBGPRINT(RT_DEBUG_OFF,("%s():SmartAntenna feature is not enabled yet\n", __FUNCTION__)); return FALSE; } } /* iwpriv ra0 set sa_sta=add-xx:xx:xx:xx:xx:xx iwpriv ra0 set sa_sta=del-xx:xx:xx:xx:xx:xx */ INT Set_SA_Station_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { UCHAR macAddr[MAC_ADDR_LEN]; RTMP_SA_TRAINING_PARAM *pSAStaEntry; BOOLEAN bDel = FALSE; if (strncmp(arg, "del-", 4) == 0) { bDel = TRUE; } if (pAd->pSAParam == NULL) { DBGPRINT(RT_DEBUG_OFF,("%s():The SmartAntenna funciton is not enabled\n", __FUNCTION__)); return FALSE; } if (rtstrtomac(arg, &macAddr[0], ':') == FALSE) return FALSE; DBGPRINT(RT_DEBUG_OFF,("%s():MacAddr=%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, PRINT_MAC(macAddr))); pSAStaEntry = sa_add_train_entry(pAd, &macAddr[0], TRUE); return TRUE; } INT Set_SA_StationCandRule_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { SMART_ANTENNA_STRUCT *pSAParam; pSAParam = pAd->pSAParam; if (pSAParam) { if (strncmp(arg, "rssi", 4) == 0) pSAParam->candStaRule = SA_STA_BY_RSSI; if (strncmp(arg, "default", 7) == 0) pSAParam->candStaRule = SA_STA_BY_DEFAULT; } else { DBGPRINT(RT_DEBUG_OFF,("%s():The SmartAntenna funciton is not enabled\n", __FUNCTION__)); return FALSE; } return TRUE; } INT Set_SA_McsBound_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { ULONG Value; SMART_ANTENNA_STRUCT *pSAParam; UCHAR txNssUpper; Value = (UINT) simple_strtol(arg, 0, 10); pSAParam = pAd->pSAParam; if (pSAParam) { if (pSAParam->txNss) { txNssUpper = pSAParam->txNss * 8; } else { txNssUpper = SA_TX_NSS_MAX_NUM * 8; } txNssUpper -= 1; pSAParam->saMcsBound = (Value > txNssUpper ? txNssUpper : Value); DBGPRINT(RT_DEBUG_OFF,("%s(): saMCSBound=%d\n", __FUNCTION__, pSAParam->saMcsBound)); return TRUE; } else { DBGPRINT(RT_DEBUG_ERROR | DBG_FUNC_SA, ("The SmartAntenna feature is not enabled yet!\n")); return FALSE; } } /* iwpirv ra0 set sa_msc=0~255 */ INT Set_McsStableCnt_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { ULONG Value; SMART_ANTENNA_STRUCT *pSAParam; Value = (UINT) simple_strtol(arg, 0, 10); pSAParam = pAd->pSAParam; if (pSAParam) { pSAParam->saMsc = (Value > 255 ? 255 : Value); DBGPRINT(RT_DEBUG_OFF,("%s(): saMsc=%d\n", __FUNCTION__, pSAParam->saMsc)); return TRUE; } else { DBGPRINT(RT_DEBUG_ERROR | DBG_FUNC_SA, ("The SmartAntenna feature is not enabled yet!\n")); return FALSE; } } /* iwpriv ra0 set sa=0~1 */ INT Set_SmartAnt_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { ULONG Value; if (arg == NULL) { show_sa_cmd_usage(pAd); return TRUE; } Value = (UINT)simple_strtol(arg, 0, 10); pAd->smartAntEnable = ((Value == 1) ? TRUE : FALSE); DBGPRINT(RT_DEBUG_OFF,("Change the SmartAntenna feature as %s\n", (pAd->smartAntEnable == TRUE ? "TRUE" : "FALSE"))); if (pAd->smartAntEnable == TRUE) { /* Shall we disable it first? */ RtmpSAStart(pAd); } else { if (pAd->pSAParam) { RtmpSAStop(pAd); } } return TRUE; } void pilot_system( IN RTMP_ADAPTER *pAd) { /* For pilot system, we periodically send the Null frame out with highest MCS to measure the success mcs, depends on the successful data rate, we estimate the goodness of antenna. Procedures: 0. When system is idle and no traffic for Tx/Rx. 1. After switch the antenna 2. Send the piolt frame out in some period and fixed MCS 3. Check the statistic FIFO to get the successfully MCS rate. 4. Recode the successfully MCS rate table. 5. Depends on the rule of SNR/MCS and successfully MCS to estimate the possible maximum MCS capability of this antenna Questions: 1. Which kinds of the frames can be treated as the pilot frames. 2. How many pilot frames should be send out in a trainning period. 3. What's the relationship between SNR and TxMCS 4. Others? Implementation: 1. A pilot packet template for each frames. 2. A timer to trigger the pilot frames to delivery to the hardware queue. 3. A table to record the tx fifo info of pilot frames. 4. A table to record the SNR/MCS relationship 5. After training done, checnk pilot-MCS-table and SNR-MCS table to get a conclusion. */ } #if 0 BOOLEAN sa_rssi_update( IN RTMP_ADAPTER *pAd, IN MAC_TABLE_ENTRY *pMacEntry, IN UCHAR antCnt) { int i; for (i = 0; i < antCnt; i++) { if (pMacEntry->cntRSSI[i]) { pMacEntry->curAvgRSSI[i] = ConvertToRssi(pAd, pMacEntry->curRSSI[i] / pMacEntry->cntRSSI[i], i); } else { pMacEntry->curAvgRSSI[i] = 0; } DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("\tcurRSSI[%d]=%d,cntRSSI[%d]=%d, avgRssi[%d]=%d,ZeroRssiCnt=%d!\n", i, pMacEntry->curRSSI[i], i, pMacEntry->cntRSSI[i], i, pMacEntry->curAvgRSSI[i], pMacEntry->rssi_zero_cnt[i])); pMacEntry->cntRSSI[i] = 0; pMacEntry->curRSSI[i] = 0; pMacEntry->rssi_zero_cnt[i] = 0; } return TRUE; } PUCHAR UCHAR rtmp_sa_get_nextMcs(UCHAR *pRateTb, UCHAR mcs) { int tbIdx, tbSize, entryStep; RTMP_RA_LEGACY_TB *pCurrTxRate; if (!pRateTb) return 0; #ifdef NEW_RATE_ADAPT_SUPPORT if (ADAPT_RATE_TABLE(pRateTb)) { entryStep = 10; } else #endif /* NEW_RATE_ADAPT_SUPPORT */ { entryStep = 5; } tbSize = pRateTb[0]; for (tbIdx = 0; tbIdx < tbSize; tbIdx++) { pCurrTxRate = (RTMP_RA_LEGACY_TB *) &pRateTb[(tbIdx+1)*5]; if (pCurrTxRate->CurrMCS == mcs) { DBGPRINT(RT_DEBUG_TRACE | DBG_FUNC_SA, ("The Next MCS(%d) rate table colum is %d\n", mcs, pCurrTxRate->TrainUp)); return pCurrTxRate->TrainUp; } } return 0; } VOID RtmpSADumpAllStatsResult( IN RTMP_ADAPTER *pAd) { BOOLEAN bLocked = FALSE; unsigned long saflags; if (!in_interrupt()) { bLocked = TRUE; RTMP_IRQ_LOCK(pAd, saflags); } if (RTMP_SA_WORK_ON(pAd)) { SMART_ANTENNA_STRUCT *pSAParam; RTMP_SA_TRAINING_PARAM *pTrainEntry; RTMP_SA_TRAIN_LOG_ELEMENT tmpLog; TX_STA_CNT0_STRUC staTxCnt0; TX_STA_CNT1_STRUC staTxCnt1; INT macIdx; pSAParam = pAd->pSAParam; pTrainEntry = &pSAParam->trainEntry[0]; if ((pTrainEntry->bTraining == FALSE) && (pTrainEntry->trainStage == SA_INVALID_STAGE)) { MAC_TABLE_ENTRY *pEntry; UINT32 ant; ant = sa_get_ant_by_gpio(pSAParam); printk("Antenna 0x%x\t\tSwTxCnt HwTxCnt HwRtyCnt SwRxCnt HwPER(1/K) SNR RSSI MCS\n", ant); printk("----------------------------------------------------------------------------\n"); for (macIdx = 1; macIdx < MAX_LEN_OF_MAC_TABLE; macIdx++) { pEntry = &pAd->MacTab.Content[macIdx]; if (!IS_ENTRY_CLIENT(pEntry)) continue; #if defined(RTMP_MAC) || defined(RLT_MAC) #ifdef FIFO_EXT_SUPPORT if ((pAd->chipCap.hif_type == HIF_RTMP) || (pAd->chipCap.hif_type == HIF_RLT)) { /* first read out the status counter from hardware */ NicGetMacFifoTxCnt(pAd, pEntry); #endif /* FIFO_EXT_SUPPORT */ #endif /* defined(RTMP_MAC) || defined(RLT_MAC) */ /* calculate the RSSI/SNR/PreSNR */ sa_calc_rx_signal_info(pAd, pEntry); /* dump the result out */ sa_dump_stat_result(pAd, pEntry); /* clean all counters for next record */ reset_mac_entry_stats(pEntry); } NicGetTxRawCounters(pAd, &staTxCnt0, &staTxCnt1); NdisZeroMemory(&tmpLog, sizeof(RTMP_SA_TRAIN_LOG_ELEMENT)); sa_read_clean_train_result(pTrainEntry, &tmpLog); if(tmpLog.txCnt) tmpLog.PER = (tmpLog.txRtyCnt * 1000) / tmpLog.txCnt; else tmpLog.PER = 0xffffffff; printk("S-Ant Stats Result(LumpSum)\n"); printk("\tLSTxCnt:%d, LSNoRtyCnt:%d, LSRtyCnt:%d, LSRtyFailCnt=%d, LSPER:%d(/1000)\n", tmpLog.txCnt, tmpLog.txNoRtyCnt, tmpLog.txRtyCnt, tmpLog.txRtyFailCnt, tmpLog.PER); printk("\n"); } } if (bLocked) { RTMP_IRQ_UNLOCK(pAd, saflags); } } int Bits2Pin(UINT32 val, UCHAR *antPair) { /* maximum we can parsing 4 pins from the input, e.g., for a input value, maximum can have 4 bits set as 1, others all shall set as zero */ int i, pinHdrCnt, pinHdrVal; //unsigned char pinId; DBGPRINT(RT_DEBUG_OFF,("Input val=0x%x!\n", val)); i = 1; pinHdrCnt = pinHdrVal = 0; while(val) { if (val & 0x1) { DBGPRINT(RT_DEBUG_OFF,("Get a PinBit, val=0x%x, i=%d\n", val, i)); pinHdrVal |= (i << (pinHdrCnt * 8)); pinHdrCnt++; } if (pinHdrCnt == 3) { DBGPRINT(RT_DEBUG_OFF,("Alreay get 4 bits from the input, ignore the rest val(0x%x)\n", val)); break; } val >>= 1; i++; } DBGPRINT(RT_DEBUG_OFF,("For the Result: pinHdrVal=%d, pinHdrCnt=%d, restVal=0x%x!\n", pinHdrVal, pinHdrCnt, val)); return pinHdrVal; } int Pin2Bits(UINT32 pinVal) { return TRUE; } #endif #endif // SMART_ANTENNA //