blob: fb1735533b74c1d84091f6f35a36b4c31b9a7734 [file] [log] [blame]
/*
*************************************************************************
* Ralink Tech Inc.
* 5F., No.36, Taiyuan St., Jhubei City,
* Hsinchu County 302,
* Taiwan, R.O.C.
*
* (c) Copyright 2002-2007, Ralink Technology, Inc.
*
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
*************************************************************************
*/
/*
All functions in this file must be PCI-depended, or you should out your function
in other files.
*/
#include "../rt_config.h"
extern RTMP_RF_REGS RF2850RegTable[];
extern UCHAR NUM_OF_2850_CHNL;
USHORT RtmpPCI_WriteTxResource(
IN PRTMP_ADAPTER pAd,
IN TX_BLK *pTxBlk,
IN BOOLEAN bIsLast,
OUT USHORT *FreeNumber)
{
UCHAR *pDMAHeaderBufVA;
USHORT TxIdx, RetTxIdx;
PTXD_STRUC pTxD;
UINT32 BufBasePaLow;
PRTMP_TX_RING pTxRing;
USHORT hwHeaderLen;
//
// get Tx Ring Resource
//
pTxRing = &pAd->TxRing[pTxBlk->QueIdx];
TxIdx = pAd->TxRing[pTxBlk->QueIdx].TxCpuIdx;
pDMAHeaderBufVA = (PUCHAR) pTxRing->Cell[TxIdx].DmaBuf.AllocVa;
BufBasePaLow = RTMP_GetPhysicalAddressLow(pTxRing->Cell[TxIdx].DmaBuf.AllocPa);
// copy TXINFO + TXWI + WLAN Header + LLC into DMA Header Buffer
if (pTxBlk->TxFrameType == TX_AMSDU_FRAME)
{
hwHeaderLen = pTxBlk->MpduHeaderLen - LENGTH_AMSDU_SUBFRAMEHEAD + pTxBlk->HdrPadLen + LENGTH_AMSDU_SUBFRAMEHEAD;
}
else
{
hwHeaderLen = pTxBlk->MpduHeaderLen + pTxBlk->HdrPadLen;
}
NdisMoveMemory(pDMAHeaderBufVA, pTxBlk->HeaderBuf, TXINFO_SIZE + TXWI_SIZE + hwHeaderLen);
pTxRing->Cell[TxIdx].pNdisPacket = pTxBlk->pPacket;
pTxRing->Cell[TxIdx].pNextNdisPacket = NULL;
//
// build Tx Descriptor
//
pTxD = (PTXD_STRUC) pTxRing->Cell[TxIdx].AllocVa;
NdisZeroMemory(pTxD, TXD_SIZE);
pTxD->SDPtr0 = BufBasePaLow;
pTxD->SDLen0 = TXINFO_SIZE + TXWI_SIZE + hwHeaderLen; // include padding
pTxD->SDPtr1 = PCI_MAP_SINGLE(pAd, pTxBlk, 0, 1, PCI_DMA_TODEVICE);;
pTxD->SDLen1 = pTxBlk->SrcBufLen;
pTxD->LastSec0 = 0;
pTxD->LastSec1 = (bIsLast) ? 1 : 0;
RTMPWriteTxDescriptor(pAd, pTxD, FALSE, FIFO_EDCA);
RetTxIdx = TxIdx;
//
// Update Tx index
//
INC_RING_INDEX(TxIdx, TX_RING_SIZE);
pTxRing->TxCpuIdx = TxIdx;
*FreeNumber -= 1;
return RetTxIdx;
}
USHORT RtmpPCI_WriteSingleTxResource(
IN PRTMP_ADAPTER pAd,
IN TX_BLK *pTxBlk,
IN BOOLEAN bIsLast,
OUT USHORT *FreeNumber)
{
UCHAR *pDMAHeaderBufVA;
USHORT TxIdx, RetTxIdx;
PTXD_STRUC pTxD;
UINT32 BufBasePaLow;
PRTMP_TX_RING pTxRing;
USHORT hwHeaderLen;
//
// get Tx Ring Resource
//
pTxRing = &pAd->TxRing[pTxBlk->QueIdx];
TxIdx = pAd->TxRing[pTxBlk->QueIdx].TxCpuIdx;
pDMAHeaderBufVA = (PUCHAR) pTxRing->Cell[TxIdx].DmaBuf.AllocVa;
BufBasePaLow = RTMP_GetPhysicalAddressLow(pTxRing->Cell[TxIdx].DmaBuf.AllocPa);
// copy TXINFO + TXWI + WLAN Header + LLC into DMA Header Buffer
hwHeaderLen = pTxBlk->MpduHeaderLen + pTxBlk->HdrPadLen;
NdisMoveMemory(pDMAHeaderBufVA, pTxBlk->HeaderBuf, TXINFO_SIZE + TXWI_SIZE + hwHeaderLen);
pTxRing->Cell[TxIdx].pNdisPacket = pTxBlk->pPacket;
pTxRing->Cell[TxIdx].pNextNdisPacket = NULL;
//
// build Tx Descriptor
//
pTxD = (PTXD_STRUC) pTxRing->Cell[TxIdx].AllocVa;
NdisZeroMemory(pTxD, TXD_SIZE);
pTxD->SDPtr0 = BufBasePaLow;
pTxD->SDLen0 = TXINFO_SIZE + TXWI_SIZE + hwHeaderLen; // include padding
pTxD->SDPtr1 = PCI_MAP_SINGLE(pAd, pTxBlk, 0, 1, PCI_DMA_TODEVICE);;
pTxD->SDLen1 = pTxBlk->SrcBufLen;
pTxD->LastSec0 = 0;
pTxD->LastSec1 = (bIsLast) ? 1 : 0;
RTMPWriteTxDescriptor(pAd, pTxD, FALSE, FIFO_EDCA);
RetTxIdx = TxIdx;
//
// Update Tx index
//
INC_RING_INDEX(TxIdx, TX_RING_SIZE);
pTxRing->TxCpuIdx = TxIdx;
*FreeNumber -= 1;
return RetTxIdx;
}
USHORT RtmpPCI_WriteMultiTxResource(
IN PRTMP_ADAPTER pAd,
IN TX_BLK *pTxBlk,
IN UCHAR frameNum,
OUT USHORT *FreeNumber)
{
BOOLEAN bIsLast;
UCHAR *pDMAHeaderBufVA;
USHORT TxIdx, RetTxIdx;
PTXD_STRUC pTxD;
UINT32 BufBasePaLow;
PRTMP_TX_RING pTxRing;
USHORT hwHdrLen;
UINT32 firstDMALen;
bIsLast = ((frameNum == (pTxBlk->TotalFrameNum - 1)) ? 1 : 0);
//
// get Tx Ring Resource
//
pTxRing = &pAd->TxRing[pTxBlk->QueIdx];
TxIdx = pAd->TxRing[pTxBlk->QueIdx].TxCpuIdx;
pDMAHeaderBufVA = (PUCHAR) pTxRing->Cell[TxIdx].DmaBuf.AllocVa;
BufBasePaLow = RTMP_GetPhysicalAddressLow(pTxRing->Cell[TxIdx].DmaBuf.AllocPa);
if (frameNum == 0)
{
// copy TXINFO + TXWI + WLAN Header + LLC into DMA Header Buffer
if (pTxBlk->TxFrameType == TX_AMSDU_FRAME)
//hwHdrLen = ROUND_UP(pTxBlk->MpduHeaderLen-LENGTH_AMSDU_SUBFRAMEHEAD, 4)+LENGTH_AMSDU_SUBFRAMEHEAD;
hwHdrLen = pTxBlk->MpduHeaderLen - LENGTH_AMSDU_SUBFRAMEHEAD + pTxBlk->HdrPadLen + LENGTH_AMSDU_SUBFRAMEHEAD;
else if (pTxBlk->TxFrameType == TX_RALINK_FRAME)
//hwHdrLen = ROUND_UP(pTxBlk->MpduHeaderLen-LENGTH_ARALINK_HEADER_FIELD, 4)+LENGTH_ARALINK_HEADER_FIELD;
hwHdrLen = pTxBlk->MpduHeaderLen - LENGTH_ARALINK_HEADER_FIELD + pTxBlk->HdrPadLen + LENGTH_ARALINK_HEADER_FIELD;
else
//hwHdrLen = ROUND_UP(pTxBlk->MpduHeaderLen, 4);
hwHdrLen = pTxBlk->MpduHeaderLen + pTxBlk->HdrPadLen;
firstDMALen = TXINFO_SIZE + TXWI_SIZE + hwHdrLen;
}
else
{
firstDMALen = pTxBlk->MpduHeaderLen;
}
NdisMoveMemory(pDMAHeaderBufVA, pTxBlk->HeaderBuf, firstDMALen);
pTxRing->Cell[TxIdx].pNdisPacket = pTxBlk->pPacket;
pTxRing->Cell[TxIdx].pNextNdisPacket = NULL;
//
// build Tx Descriptor
//
pTxD = (PTXD_STRUC) pTxRing->Cell[TxIdx].AllocVa;
NdisZeroMemory(pTxD, TXD_SIZE);
pTxD->SDPtr0 = BufBasePaLow;
pTxD->SDLen0 = firstDMALen; // include padding
pTxD->SDPtr1 = PCI_MAP_SINGLE(pAd, pTxBlk, 0, 1, PCI_DMA_TODEVICE);;
pTxD->SDLen1 = pTxBlk->SrcBufLen;
pTxD->LastSec0 = 0;
pTxD->LastSec1 = (bIsLast) ? 1 : 0;
RTMPWriteTxDescriptor(pAd, pTxD, FALSE, FIFO_EDCA);
RetTxIdx = TxIdx;
//
// Update Tx index
//
INC_RING_INDEX(TxIdx, TX_RING_SIZE);
pTxRing->TxCpuIdx = TxIdx;
*FreeNumber -= 1;
return RetTxIdx;
}
VOID RtmpPCI_FinalWriteTxResource(
IN PRTMP_ADAPTER pAd,
IN TX_BLK *pTxBlk,
IN USHORT totalMPDUSize,
IN USHORT FirstTxIdx)
{
PTXWI_STRUC pTxWI;
PRTMP_TX_RING pTxRing;
//
// get Tx Ring Resource
//
pTxRing = &pAd->TxRing[pTxBlk->QueIdx];
pTxWI = (PTXWI_STRUC) pTxRing->Cell[FirstTxIdx].DmaBuf.AllocVa;
pTxWI->MPDUtotalByteCount = totalMPDUSize;
}
VOID RtmpPCIDataLastTxIdx(
IN PRTMP_ADAPTER pAd,
IN UCHAR QueIdx,
IN USHORT LastTxIdx)
{
PTXD_STRUC pTxD;
PRTMP_TX_RING pTxRing;
//
// get Tx Ring Resource
//
pTxRing = &pAd->TxRing[QueIdx];
//
// build Tx Descriptor
//
pTxD = (PTXD_STRUC) pTxRing->Cell[LastTxIdx].AllocVa;
pTxD->LastSec1 = 1;
}
USHORT RtmpPCI_WriteFragTxResource(
IN PRTMP_ADAPTER pAd,
IN TX_BLK *pTxBlk,
IN UCHAR fragNum,
OUT USHORT *FreeNumber)
{
UCHAR *pDMAHeaderBufVA;
USHORT TxIdx, RetTxIdx;
PTXD_STRUC pTxD;
UINT32 BufBasePaLow;
PRTMP_TX_RING pTxRing;
USHORT hwHeaderLen;
UINT32 firstDMALen;
//
// Get Tx Ring Resource
//
pTxRing = &pAd->TxRing[pTxBlk->QueIdx];
TxIdx = pAd->TxRing[pTxBlk->QueIdx].TxCpuIdx;
pDMAHeaderBufVA = (PUCHAR) pTxRing->Cell[TxIdx].DmaBuf.AllocVa;
BufBasePaLow = RTMP_GetPhysicalAddressLow(pTxRing->Cell[TxIdx].DmaBuf.AllocPa);
//
// Copy TXINFO + TXWI + WLAN Header + LLC into DMA Header Buffer
//
hwHeaderLen = pTxBlk->MpduHeaderLen + pTxBlk->HdrPadLen;
firstDMALen = TXINFO_SIZE + TXWI_SIZE + hwHeaderLen;
NdisMoveMemory(pDMAHeaderBufVA, pTxBlk->HeaderBuf, firstDMALen);
//
// Build Tx Descriptor
//
pTxD = (PTXD_STRUC) pTxRing->Cell[TxIdx].AllocVa;
NdisZeroMemory(pTxD, TXD_SIZE);
if (fragNum == pTxBlk->TotalFragNum)
{
pTxRing->Cell[TxIdx].pNdisPacket = pTxBlk->pPacket;
pTxRing->Cell[TxIdx].pNextNdisPacket = NULL;
}
pTxD->SDPtr0 = BufBasePaLow;
pTxD->SDLen0 = firstDMALen; // include padding
pTxD->SDPtr1 = PCI_MAP_SINGLE(pAd, pTxBlk, 0, 1, PCI_DMA_TODEVICE);
pTxD->SDLen1 = pTxBlk->SrcBufLen;
pTxD->LastSec0 = 0;
pTxD->LastSec1 = 1;
RTMPWriteTxDescriptor(pAd, pTxD, FALSE, FIFO_EDCA);
RetTxIdx = TxIdx;
pTxBlk->Priv += pTxBlk->SrcBufLen;
//
// Update Tx index
//
INC_RING_INDEX(TxIdx, TX_RING_SIZE);
pTxRing->TxCpuIdx = TxIdx;
*FreeNumber -= 1;
return RetTxIdx;
}
/*
Must be run in Interrupt context
This function handle PCI specific TxDesc and cpu index update and kick the packet out.
*/
int RtmpPCIMgmtKickOut(
IN RTMP_ADAPTER *pAd,
IN UCHAR QueIdx,
IN PNDIS_PACKET pPacket,
IN PUCHAR pSrcBufVA,
IN UINT SrcBufLen)
{
PTXD_STRUC pTxD;
ULONG SwIdx = pAd->MgmtRing.TxCpuIdx;
pTxD = (PTXD_STRUC) pAd->MgmtRing.Cell[SwIdx].AllocVa;
pAd->MgmtRing.Cell[SwIdx].pNdisPacket = pPacket;
pAd->MgmtRing.Cell[SwIdx].pNextNdisPacket = NULL;
RTMPWriteTxDescriptor(pAd, pTxD, TRUE, FIFO_MGMT);
pTxD->LastSec0 = 1;
pTxD->LastSec1 = 1;
pTxD->DMADONE = 0;
pTxD->SDLen1 = 0;
pTxD->SDPtr0 = PCI_MAP_SINGLE(pAd, pSrcBufVA, SrcBufLen, 0, PCI_DMA_TODEVICE);;
pTxD->SDLen0 = SrcBufLen;
pAd->RalinkCounters.KickTxCount++;
pAd->RalinkCounters.OneSecTxDoneCount++;
// Increase TX_CTX_IDX, but write to register later.
INC_RING_INDEX(pAd->MgmtRing.TxCpuIdx, MGMT_RING_SIZE);
RTMP_IO_WRITE32(pAd, TX_MGMTCTX_IDX, pAd->MgmtRing.TxCpuIdx);
return 0;
}
/*
========================================================================
Routine Description:
Check Rx descriptor, return NDIS_STATUS_FAILURE if any error dound
Arguments:
pRxD Pointer to the Rx descriptor
Return Value:
NDIS_STATUS_SUCCESS No err
NDIS_STATUS_FAILURE Error
Note:
========================================================================
*/
NDIS_STATUS RTMPCheckRxError(
IN PRTMP_ADAPTER pAd,
IN PHEADER_802_11 pHeader,
IN PRXWI_STRUC pRxWI,
IN PRT28XX_RXD_STRUC pRxD)
{
PCIPHER_KEY pWpaKey;
INT dBm;
// Phy errors & CRC errors
if (/*(pRxD->PhyErr) ||*/ (pRxD->Crc))
{
// Check RSSI for Noise Hist statistic collection.
dBm = (INT) (pRxWI->RSSI0) - pAd->BbpRssiToDbmDelta;
if (dBm <= -87)
pAd->StaCfg.RPIDensity[0] += 1;
else if (dBm <= -82)
pAd->StaCfg.RPIDensity[1] += 1;
else if (dBm <= -77)
pAd->StaCfg.RPIDensity[2] += 1;
else if (dBm <= -72)
pAd->StaCfg.RPIDensity[3] += 1;
else if (dBm <= -67)
pAd->StaCfg.RPIDensity[4] += 1;
else if (dBm <= -62)
pAd->StaCfg.RPIDensity[5] += 1;
else if (dBm <= -57)
pAd->StaCfg.RPIDensity[6] += 1;
else if (dBm > -57)
pAd->StaCfg.RPIDensity[7] += 1;
return(NDIS_STATUS_FAILURE);
}
// Add Rx size to channel load counter, we should ignore error counts
pAd->StaCfg.CLBusyBytes += (pRxD->SDL0 + 14);
// Drop ToDs promiscous frame, it is opened due to CCX 2 channel load statistics
if (pHeader != NULL)
{
if (pHeader->FC.ToDs)
{
return(NDIS_STATUS_FAILURE);
}
}
// Drop not U2M frames, cant's drop here because we will drop beacon in this case
// I am kind of doubting the U2M bit operation
// if (pRxD->U2M == 0)
// return(NDIS_STATUS_FAILURE);
// drop decyption fail frame
if (pRxD->CipherErr)
{
if (pRxD->CipherErr == 2)
{DBGPRINT_RAW(RT_DEBUG_TRACE,("pRxD ERROR: ICV ok but MICErr "));}
else if (pRxD->CipherErr == 1)
{DBGPRINT_RAW(RT_DEBUG_TRACE,("pRxD ERROR: ICV Err "));}
else if (pRxD->CipherErr == 3)
DBGPRINT_RAW(RT_DEBUG_TRACE,("pRxD ERROR: Key not valid "));
if (((pRxD->CipherErr & 1) == 1) && pAd->CommonCfg.bWirelessEvent && INFRA_ON(pAd))
RTMPSendWirelessEvent(pAd, IW_ICV_ERROR_EVENT_FLAG, pAd->MacTab.Content[BSSID_WCID].Addr, BSS0, 0);
DBGPRINT_RAW(RT_DEBUG_TRACE,(" %d (len=%d, Mcast=%d, MyBss=%d, Wcid=%d, KeyId=%d)\n",
pRxD->CipherErr,
pRxD->SDL0,
pRxD->Mcast | pRxD->Bcast,
pRxD->MyBss,
pRxWI->WirelessCliID,
pRxWI->KeyIndex));
//
// MIC Error
//
if (pRxD->CipherErr == 2)
{
pWpaKey = &pAd->SharedKey[BSS0][pRxWI->KeyIndex];
if (pAd->StaCfg.WpaSupplicantUP)
WpaSendMicFailureToWpaSupplicant(pAd,
(pWpaKey->Type == PAIRWISEKEY) ? TRUE:FALSE);
else
RTMPReportMicError(pAd, pWpaKey);
if (((pRxD->CipherErr & 2) == 2) && pAd->CommonCfg.bWirelessEvent && INFRA_ON(pAd))
RTMPSendWirelessEvent(pAd, IW_MIC_ERROR_EVENT_FLAG, pAd->MacTab.Content[BSSID_WCID].Addr, BSS0, 0);
DBGPRINT_RAW(RT_DEBUG_ERROR,("Rx MIC Value error\n"));
}
if (pHeader == NULL)
return(NDIS_STATUS_SUCCESS);
return(NDIS_STATUS_FAILURE);
}
return(NDIS_STATUS_SUCCESS);
}
/*
==========================================================================
Description:
This routine sends command to firmware and turn our chip to power save mode.
Both RadioOff and .11 power save function needs to call this routine.
Input:
Level = GUIRADIO_OFF : GUI Radio Off mode
Level = DOT11POWERSAVE : 802.11 power save mode
Level = RTMP_HALT : When Disable device.
==========================================================================
*/
VOID RT28xxPciAsicRadioOff(
IN PRTMP_ADAPTER pAd,
IN UCHAR Level,
IN USHORT TbttNumToNextWakeUp)
{
WPDMA_GLO_CFG_STRUC DmaCfg;
UCHAR i, tempBBP_R3 = 0;
BOOLEAN brc = FALSE, Cancelled;
UINT32 TbTTTime = 0;
UINT32 PsPollTime = 0, MACValue;
ULONG BeaconPeriodTime;
UINT32 RxDmaIdx, RxCpuIdx;
DBGPRINT(RT_DEBUG_TRACE, ("AsicRadioOff ===> TxCpuIdx = %d, TxDmaIdx = %d. RxCpuIdx = %d, RxDmaIdx = %d.\n", pAd->TxRing[0].TxCpuIdx, pAd->TxRing[0].TxDmaIdx, pAd->RxRing.RxCpuIdx, pAd->RxRing.RxDmaIdx));
// Check Rx DMA busy status, if more than half is occupied, give up this radio off.
RTMP_IO_READ32(pAd, RX_DRX_IDX , &RxDmaIdx);
RTMP_IO_READ32(pAd, RX_CRX_IDX , &RxCpuIdx);
if ((RxDmaIdx > RxCpuIdx) && ((RxDmaIdx - RxCpuIdx) > RX_RING_SIZE/3))
{
DBGPRINT(RT_DEBUG_TRACE, ("AsicRadioOff ===> return1. RxDmaIdx = %d , RxCpuIdx = %d. \n", RxDmaIdx, RxCpuIdx));
return;
}
else if ((RxCpuIdx >= RxDmaIdx) && ((RxCpuIdx - RxDmaIdx) < RX_RING_SIZE/3))
{
DBGPRINT(RT_DEBUG_TRACE, ("AsicRadioOff ===> return2. RxCpuIdx = %d. RxDmaIdx = %d , \n", RxCpuIdx, RxDmaIdx));
return;
}
// Once go into this function, disable tx because don't want too many packets in queue to prevent HW stops.
RTMP_SET_PSFLAG(pAd, fRTMP_PS_DISABLE_TX);
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
{
RTMPCancelTimer(&pAd->Mlme.RadioOnOffTimer, &Cancelled);
RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
if (Level == DOT11POWERSAVE)
{
RTMP_IO_READ32(pAd, TBTT_TIMER, &TbTTTime);
TbTTTime &= 0x1ffff;
// 00. check if need to do sleep in this DTIM period. If next beacon will arrive within 30ms , ...doesn't necessarily sleep.
// TbTTTime uint = 64us, LEAD_TIME unit = 1024us, PsPollTime unit = 1ms
if (((64*TbTTTime) <((LEAD_TIME*1024) + 40000)) && (TbttNumToNextWakeUp == 0))
{
DBGPRINT(RT_DEBUG_TRACE, ("TbTTTime = 0x%x , give up this sleep. \n", TbTTTime));
OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_DOZE);
RTMP_CLEAR_PSFLAG(pAd, fRTMP_PS_DISABLE_TX);
return;
}
else
{
PsPollTime = (64*TbTTTime- LEAD_TIME*1024)/1000;
PsPollTime -= 3;
BeaconPeriodTime = pAd->CommonCfg.BeaconPeriod*102/100;
if (TbttNumToNextWakeUp > 0)
PsPollTime += ((TbttNumToNextWakeUp -1) * BeaconPeriodTime);
pAd->Mlme.bPsPollTimerRunning = TRUE;
RTMPSetTimer(&pAd->Mlme.PsPollTimer, PsPollTime);
}
}
}
// 0. Disable Tx DMA.
RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &DmaCfg.word);
DmaCfg.field.EnableTxDMA = 0;
RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, DmaCfg.word);
// 1. Wait DMA not busy
i = 0;
do
{
RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &DmaCfg.word);
if ((DmaCfg.field.TxDMABusy == 0) && (DmaCfg.field.RxDMABusy == 0))
break;
RTMPusecDelay(20);
i++;
}while(i < 50);
if (i >= 50)
{
DBGPRINT(RT_DEBUG_TRACE, ("DMA keeps busy. return on RT28xxPciAsicRadioOff ()\n"));
RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &DmaCfg.word);
DmaCfg.field.EnableTxDMA = 1;
RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, DmaCfg.word);
pAd->CheckDmaBusyCount++;
return;
}
else
{
pAd->CheckDmaBusyCount = 0;
}
RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF);
// Set to 1R.
if (pAd->Antenna.field.RxPath > 1)
{
tempBBP_R3 = (pAd->StaCfg.BBPR3 & 0xE7);
RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_R3, tempBBP_R3);
}
// In Radio Off, we turn off RF clk, So now need to call ASICSwitchChannel again.
if (INFRA_ON(pAd) && (pAd->CommonCfg.CentralChannel != pAd->CommonCfg.Channel)
&& (pAd->MlmeAux.HtCapability.HtCapInfo.ChannelWidth == BW_40))
{
// Must using 40MHz.
AsicTurnOffRFClk(pAd, pAd->CommonCfg.CentralChannel);
}
else
{
// Must using 20MHz.
AsicTurnOffRFClk(pAd, pAd->CommonCfg.Channel);
}
if (Level != RTMP_HALT)
{
// Change Interrupt bitmask.
RTMP_IO_WRITE32(pAd, INT_MASK_CSR, AutoWakeupInt);
}
else
{
NICDisableInterrupt(pAd);
}
RTMP_IO_WRITE32(pAd, RX_CRX_IDX, pAd->RxRing.RxCpuIdx);
// Disable MAC Rx
RTMP_IO_READ32(pAd, MAC_SYS_CTRL , &MACValue);
MACValue &= 0xf7;
RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL , MACValue);
// 2. Send Sleep command
RTMP_IO_WRITE32(pAd, H2M_MAILBOX_STATUS, 0xffffffff);
RTMP_IO_WRITE32(pAd, H2M_MAILBOX_CID, 0xffffffff);
// send POWER-SAVE command to MCU. high-byte = 1 save power as much as possible. high byte = 0 save less power
AsicSendCommandToMcu(pAd, 0x30, PowerSafeCID, 0xff, 0x1);
// 2-1. Wait command success
// Status = 1 : success, Status = 2, already sleep, Status = 3, Maybe MAC is busy so can't finish this task.
brc = AsicCheckCommanOk(pAd, PowerSafeCID);
if (brc == FALSE)
{
// try again
AsicSendCommandToMcu(pAd, 0x30, PowerSafeCID, 0xff, 0x01); // send POWER-SAVE command to MCU. Timeout unit:40us.
//RTMPusecDelay(200);
brc = AsicCheckCommanOk(pAd, PowerSafeCID);
}
// 3. After 0x30 command is ok, send radio off command. lowbyte = 0 for power safe.
// If 0x30 command is not ok this time, we can ignore 0x35 command. It will make sure not cause firmware'r problem.
if ((Level == DOT11POWERSAVE) && (brc == TRUE))
{
AsicSendCommandToMcu(pAd, 0x35, PowerRadioOffCID, 0, 0x00); // lowbyte = 0 means to do power safe, NOT turn off radio.
// 3-1. Wait command success
AsicCheckCommanOk(pAd, PowerRadioOffCID);
}
else if (brc == TRUE)
{
AsicSendCommandToMcu(pAd, 0x35, PowerRadioOffCID, 1, 0x00); // lowbyte = 0 means to do power safe, NOT turn off radio.
// 3-1. Wait command success
AsicCheckCommanOk(pAd, PowerRadioOffCID);
}
// Wait DMA not busy
i = 0;
do
{
RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &DmaCfg.word);
if ((DmaCfg.field.RxDMABusy == 0) && (DmaCfg.field.TxDMABusy == 0))
break;
RTMPusecDelay(20);
i++;
}while(i < 50);
if (i >= 50)
{
pAd->CheckDmaBusyCount++;
DBGPRINT(RT_DEBUG_TRACE, ("DMA Rx keeps busy. on RT28xxPciAsicRadioOff ()\n"));
}
else
{
pAd->CheckDmaBusyCount = 0;
}
if (Level == DOT11POWERSAVE)
{
AUTO_WAKEUP_STRUC AutoWakeupCfg;
//RTMPSetTimer(&pAd->Mlme.PsPollTimer, 90);
// we have decided to SLEEP, so at least do it for a BEACON period.
if (TbttNumToNextWakeUp == 0)
TbttNumToNextWakeUp = 1;
AutoWakeupCfg.word = 0;
RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
// 1. Set auto wake up timer.
AutoWakeupCfg.field.NumofSleepingTbtt = TbttNumToNextWakeUp - 1;
AutoWakeupCfg.field.EnableAutoWakeup = 1;
AutoWakeupCfg.field.AutoLeadTime = LEAD_TIME;
RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
}
// 4-1. If it's to disable our device. Need to restore PCI Configuration Space to its original value.
if (Level == RTMP_HALT)
{
if ((brc == TRUE) && (i < 50))
RTMPPCIeLinkCtrlSetting(pAd, 0);
}
// 4. Set PCI configuration Space Link Comtrol fields. Only Radio Off needs to call this function
else
{
if ((brc == TRUE) && (i < 50))
RTMPPCIeLinkCtrlSetting(pAd, 3);
}
RTMP_CLEAR_PSFLAG(pAd, fRTMP_PS_DISABLE_TX);
}
/*
==========================================================================
Description:
This routine sends command to firmware and turn our chip to wake up mode from power save mode.
Both RadioOn and .11 power save function needs to call this routine.
Input:
Level = GUIRADIO_OFF : call this function is from Radio Off to Radio On. Need to restore PCI host value.
Level = other value : normal wake up function.
==========================================================================
*/
BOOLEAN RT28xxPciAsicRadioOn(
IN PRTMP_ADAPTER pAd,
IN UCHAR Level)
{
WPDMA_GLO_CFG_STRUC DmaCfg;
BOOLEAN Cancelled, brv = TRUE;
UINT32 MACValue;
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
{
pAd->Mlme.bPsPollTimerRunning = FALSE;
RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
if ((Level == GUIRADIO_OFF) || (Level == GUI_IDLE_POWER_SAVE)
|| (RTMP_TEST_PSFLAG(pAd, fRTMP_PS_SET_PCI_CLK_OFF_COMMAND)))
{
DBGPRINT(RT_DEBUG_TRACE, ("RT28xxPciAsicRadioOn ()\n"));
// 1. Set PCI Link Control in Configuration Space.
RTMPPCIeLinkCtrlValueRestore(pAd, RESTORE_WAKEUP);
RTMPusecDelay(6000);
}
}
pAd->bPCIclkOff = FALSE;
RTMP_IO_WRITE32(pAd, PBF_SYS_CTRL, 0x3a80);
// 2. Send wake up command.
AsicSendCommandToMcu(pAd, 0x31, PowerWakeCID, 0x00, 0x02);
// 2-1. wait command ok.
brv = AsicCheckCommanOk(pAd, PowerWakeCID);
if (brv)
{
NICEnableInterrupt(pAd);
// 3. Enable Tx DMA.
RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &DmaCfg.word);
DmaCfg.field.EnableTxDMA = 1;
DmaCfg.field.EnableRxDMA = 1;
RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, DmaCfg.word);
// Eable MAC Rx
RTMP_IO_READ32(pAd, MAC_SYS_CTRL , &MACValue);
MACValue |= 0x8;
RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL , MACValue);
RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF);
if (Level == GUI_IDLE_POWER_SAVE)
{
// In Radio Off, we turn off RF clk, So now need to call ASICSwitchChannel again.
if (INFRA_ON(pAd) && (pAd->CommonCfg.CentralChannel != pAd->CommonCfg.Channel)
&& (pAd->MlmeAux.HtCapability.HtCapInfo.ChannelWidth == BW_40))
{
// Must using 40MHz.
AsicSwitchChannel(pAd, pAd->CommonCfg.CentralChannel, FALSE);
AsicLockChannel(pAd, pAd->CommonCfg.CentralChannel);
}
else
{
// Must using 20MHz.
AsicSwitchChannel(pAd, pAd->CommonCfg.Channel, FALSE);
AsicLockChannel(pAd, pAd->CommonCfg.Channel);
}
}
return TRUE;
}
else
return FALSE;
}
VOID RT28xxPciStaAsicForceWakeup(
IN PRTMP_ADAPTER pAd,
IN UCHAR Level)
{
AUTO_WAKEUP_STRUC AutoWakeupCfg;
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_WAKEUP_NOW))
{
DBGPRINT(RT_DEBUG_TRACE, ("waking up now!\n"));
return;
}
OPSTATUS_SET_FLAG(pAd, fOP_STATUS_WAKEUP_NOW);
RTMP_CLEAR_PSFLAG(pAd, fRTMP_PS_GO_TO_SLEEP_NOW);
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
{
// Support PCIe Advance Power Save
if (((Level == FROM_TX) && (pAd->Mlme.bPsPollTimerRunning == TRUE)) ||
(Level == RTMP_HALT))
{
pAd->Mlme.bPsPollTimerRunning = FALSE;
RTMPPCIeLinkCtrlValueRestore(pAd, RESTORE_WAKEUP);
RTMPusecDelay(5000);
DBGPRINT(RT_DEBUG_TRACE, ("=======AsicForceWakeup===bFromTx\n"));
}
AutoWakeupCfg.word = 0;
RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
// If this is called from Halt. ALWAYS force wakeup!!!
if (Level == RTMP_HALT)
{
RT28xxPciAsicRadioOn(pAd, RTMP_HALT);
}
else
{
if (RT28xxPciAsicRadioOn(pAd, DOT11POWERSAVE))
{
// In Radio Off, we turn off RF clk, So now need to call ASICSwitchChannel again.
if (INFRA_ON(pAd) && (pAd->CommonCfg.CentralChannel != pAd->CommonCfg.Channel)
&& (pAd->MlmeAux.HtCapability.HtCapInfo.ChannelWidth == BW_40))
{
// Must using 40MHz.
AsicSwitchChannel(pAd, pAd->CommonCfg.CentralChannel, FALSE);
AsicLockChannel(pAd, pAd->CommonCfg.CentralChannel);
}
else
{
// Must using 20MHz.
AsicSwitchChannel(pAd, pAd->CommonCfg.Channel, FALSE);
AsicLockChannel(pAd, pAd->CommonCfg.Channel);
}
}
}
}
else
{
// PCI, 2860-PCIe
AsicSendCommandToMcu(pAd, 0x31, 0xff, 0x00, 0x00);
AutoWakeupCfg.word = 0;
RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
}
OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_DOZE);
OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_WAKEUP_NOW);
DBGPRINT(RT_DEBUG_TRACE, ("<=======RT28xxPciStaAsicForceWakeup\n"));
}
VOID RT28xxPciStaAsicSleepThenAutoWakeup(
IN PRTMP_ADAPTER pAd,
IN USHORT TbttNumToNextWakeUp)
{
if (pAd->StaCfg.bRadio == FALSE)
{
OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_DOZE);
return;
}
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
{
ULONG Now = 0;
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_WAKEUP_NOW))
{
DBGPRINT(RT_DEBUG_TRACE, ("waking up now!\n"));
OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_DOZE);
return;
}
NdisGetSystemUpTime(&Now);
// If last send NULL fram time is too close to this receiving beacon (within 8ms), don't go to sleep for this DTM.
// Because Some AP can't queuing outgoing frames immediately.
if (((pAd->Mlme.LastSendNULLpsmTime + 8) >= Now) && (pAd->Mlme.LastSendNULLpsmTime <= Now))
{
DBGPRINT(RT_DEBUG_TRACE, ("Now = %lu, LastSendNULLpsmTime=%lu : RxCountSinceLastNULL = %lu. \n", Now, pAd->Mlme.LastSendNULLpsmTime, pAd->RalinkCounters.RxCountSinceLastNULL));
return;
}
else if ((pAd->RalinkCounters.RxCountSinceLastNULL > 0) && ((pAd->Mlme.LastSendNULLpsmTime + pAd->CommonCfg.BeaconPeriod) >= Now))
{
DBGPRINT(RT_DEBUG_TRACE, ("Now = %lu, LastSendNULLpsmTime=%lu: RxCountSinceLastNULL = %lu > 0 \n", Now, pAd->Mlme.LastSendNULLpsmTime, pAd->RalinkCounters.RxCountSinceLastNULL));
return;
}
RT28xxPciAsicRadioOff(pAd, DOT11POWERSAVE, TbttNumToNextWakeUp);
}
else
{
AUTO_WAKEUP_STRUC AutoWakeupCfg;
// we have decided to SLEEP, so at least do it for a BEACON period.
if (TbttNumToNextWakeUp == 0)
TbttNumToNextWakeUp = 1;
AutoWakeupCfg.word = 0;
RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
AutoWakeupCfg.field.NumofSleepingTbtt = TbttNumToNextWakeUp - 1;
AutoWakeupCfg.field.EnableAutoWakeup = 1;
AutoWakeupCfg.field.AutoLeadTime = 5;
RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
AsicSendCommandToMcu(pAd, 0x30, 0xff, 0xff, 0x00); // send POWER-SAVE command to MCU. Timeout 40us.
DBGPRINT(RT_DEBUG_TRACE, ("<-- %s, TbttNumToNextWakeUp=%d \n", __func__, TbttNumToNextWakeUp));
}
OPSTATUS_SET_FLAG(pAd, fOP_STATUS_DOZE);
}
VOID PsPollWakeExec(
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3)
{
RTMP_ADAPTER *pAd = (RTMP_ADAPTER *)FunctionContext;
unsigned long flags;
DBGPRINT(RT_DEBUG_TRACE,("-->PsPollWakeExec \n"));
RTMP_INT_LOCK(&pAd->irq_lock, flags);
if (pAd->Mlme.bPsPollTimerRunning)
{
RTMPPCIeLinkCtrlValueRestore(pAd, RESTORE_WAKEUP);
}
pAd->Mlme.bPsPollTimerRunning = FALSE;
RTMP_INT_UNLOCK(&pAd->irq_lock, flags);
}
VOID RadioOnExec(
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3)
{
RTMP_ADAPTER *pAd = (RTMP_ADAPTER *)FunctionContext;
WPDMA_GLO_CFG_STRUC DmaCfg;
BOOLEAN Cancelled;
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_DOZE))
{
DBGPRINT(RT_DEBUG_TRACE,("-->RadioOnExec() return on fOP_STATUS_DOZE == TRUE; \n"));
RTMPSetTimer(&pAd->Mlme.RadioOnOffTimer, 10);
return;
}
if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_BSS_SCAN_IN_PROGRESS))
{
DBGPRINT(RT_DEBUG_TRACE,("-->RadioOnExec() return on SCAN_IN_PROGRESS; \n"));
RTMPSetTimer(&pAd->Mlme.RadioOnOffTimer, 10);
return;
}
pAd->Mlme.bPsPollTimerRunning = FALSE;
RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
if (pAd->StaCfg.bRadio == TRUE)
{
pAd->bPCIclkOff = FALSE;
RTMPRingCleanUp(pAd, QID_AC_BK);
RTMPRingCleanUp(pAd, QID_AC_BE);
RTMPRingCleanUp(pAd, QID_AC_VI);
RTMPRingCleanUp(pAd, QID_AC_VO);
RTMPRingCleanUp(pAd, QID_HCCA);
RTMPRingCleanUp(pAd, QID_MGMT);
RTMPRingCleanUp(pAd, QID_RX);
// 2. Send wake up command.
AsicSendCommandToMcu(pAd, 0x31, PowerWakeCID, 0x00, 0x02);
// 2-1. wait command ok.
AsicCheckCommanOk(pAd, PowerWakeCID);
// When PCI clock is off, don't want to service interrupt. So when back to clock on, enable interrupt.
NICEnableInterrupt(pAd);
// 3. Enable Tx DMA.
RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &DmaCfg.word);
DmaCfg.field.EnableTxDMA = 1;
RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, DmaCfg.word);
// In Radio Off, we turn off RF clk, So now need to call ASICSwitchChannel again.
if (INFRA_ON(pAd) && (pAd->CommonCfg.CentralChannel != pAd->CommonCfg.Channel)
&& (pAd->MlmeAux.HtCapability.HtCapInfo.ChannelWidth == BW_40))
{
// Must using 40MHz.
AsicSwitchChannel(pAd, pAd->CommonCfg.CentralChannel, FALSE);
AsicLockChannel(pAd, pAd->CommonCfg.CentralChannel);
}
else
{
// Must using 20MHz.
AsicSwitchChannel(pAd, pAd->CommonCfg.Channel, FALSE);
AsicLockChannel(pAd, pAd->CommonCfg.Channel);
}
// Clear Radio off flag
RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF);
// Set LED
RTMPSetLED(pAd, LED_RADIO_ON);
if (pAd->StaCfg.Psm == PWR_ACTIVE)
{
RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_R3, pAd->StaCfg.BBPR3);
}
}
else
{
RT28xxPciAsicRadioOff(pAd, GUIRADIO_OFF, 0);
}
}
VOID RT28xxPciMlmeRadioOn(
IN PRTMP_ADAPTER pAd)
{
if (!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF))
return;
DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __func__));
if ((pAd->OpMode == OPMODE_AP) ||
((pAd->OpMode == OPMODE_STA) && (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))))
{
NICResetFromError(pAd);
/*
RTMPRingCleanUp(pAd, QID_AC_BK);
RTMPRingCleanUp(pAd, QID_AC_BE);
RTMPRingCleanUp(pAd, QID_AC_VI);
RTMPRingCleanUp(pAd, QID_AC_VO);
RTMPRingCleanUp(pAd, QID_HCCA);
RTMPRingCleanUp(pAd, QID_MGMT);
RTMPRingCleanUp(pAd, QID_RX);
*/
// Enable Tx/Rx
RTMPEnableRxTx(pAd);
// Clear Radio off flag
RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF);
// Set LED
RTMPSetLED(pAd, LED_RADIO_ON);
}
if ((pAd->OpMode == OPMODE_STA) &&
(OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)))
{
BOOLEAN Cancelled;
RTMPPCIeLinkCtrlValueRestore(pAd, RESTORE_WAKEUP);
pAd->Mlme.bPsPollTimerRunning = FALSE;
RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
RTMPCancelTimer(&pAd->Mlme.RadioOnOffTimer, &Cancelled);
RTMPSetTimer(&pAd->Mlme.RadioOnOffTimer, 10);
}
}
VOID RT28xxPciMlmeRadioOFF(
IN PRTMP_ADAPTER pAd)
{
WPDMA_GLO_CFG_STRUC GloCfg;
UINT32 i;
if (pAd->StaCfg.bRadio == TRUE)
{
DBGPRINT(RT_DEBUG_TRACE,("-->MlmeRadioOff() return on bRadio == TRUE; \n"));
return;
}
if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF))
return;
DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __func__));
// Set LED
RTMPSetLED(pAd, LED_RADIO_OFF);
{
BOOLEAN Cancelled;
if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_BSS_SCAN_IN_PROGRESS))
{
RTMPCancelTimer(&pAd->MlmeAux.ScanTimer, &Cancelled);
RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_BSS_SCAN_IN_PROGRESS);
}
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
{
BOOLEAN Cancelled;
// Always radio on since the NIC needs to set the MCU command (LED_RADIO_OFF).
if ((pAd->OpMode == OPMODE_STA) &&
(IDLE_ON(pAd)) &&
(RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF)))
{
RT28xxPciAsicRadioOn(pAd, GUI_IDLE_POWER_SAVE);
}
pAd->Mlme.bPsPollTimerRunning = FALSE;
RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
RTMPCancelTimer(&pAd->Mlme.RadioOnOffTimer, &Cancelled);
}
// Link down first if any association exists
if (INFRA_ON(pAd) || ADHOC_ON(pAd))
LinkDown(pAd, FALSE);
RTMPusecDelay(10000);
//==========================================
// Clean up old bss table
BssTableInit(&pAd->ScanTab);
RTMPRingCleanUp(pAd, QID_AC_BK);
RTMPRingCleanUp(pAd, QID_AC_BE);
RTMPRingCleanUp(pAd, QID_AC_VI);
RTMPRingCleanUp(pAd, QID_AC_VO);
RTMPRingCleanUp(pAd, QID_HCCA);
RTMPRingCleanUp(pAd, QID_MGMT);
RTMPRingCleanUp(pAd, QID_RX);
if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
{
RTMPSetTimer(&pAd->Mlme.RadioOnOffTimer, 500);
return;
}
}
// Set Radio off flag
RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF);
// Disable Tx/Rx DMA
RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &GloCfg.word); // disable DMA
GloCfg.field.EnableTxDMA = 0;
GloCfg.field.EnableRxDMA = 0;
RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, GloCfg.word); // abort all TX rings
// MAC_SYS_CTRL => value = 0x0 => 40mA
RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL, 0);
// PWR_PIN_CFG => value = 0x0 => 40mA
RTMP_IO_WRITE32(pAd, PWR_PIN_CFG, 0);
// TX_PIN_CFG => value = 0x0 => 20mA
RTMP_IO_WRITE32(pAd, TX_PIN_CFG, 0);
if (pAd->CommonCfg.BBPCurrentBW == BW_40)
{
// Must using 40MHz.
AsicTurnOffRFClk(pAd, pAd->CommonCfg.CentralChannel);
}
else
{
// Must using 20MHz.
AsicTurnOffRFClk(pAd, pAd->CommonCfg.Channel);
}
// Waiting for DMA idle
i = 0;
do
{
RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &GloCfg.word);
if ((GloCfg.field.TxDMABusy == 0) && (GloCfg.field.RxDMABusy == 0))
break;
RTMPusecDelay(1000);
}while (i++ < 100);
}