/* ************************************************************************* * 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; if (!pTxD) return 0; 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); }