📄 ar5211_xmit.c
字号:
/* * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * Copyright (c) 2002-2005 Atheros Communications, Inc. * All rights reserved. * * $Id: ar5211_xmit.c,v 1.1.1.1 2006/09/12 03:45:24 steven Exp $ */#include "opt_ah.h"#ifdef AH_SUPPORT_AR5211#include "ah.h"#include "ah_internal.h"#include "ah_desc.h"#include "ar5211/ar5211.h"#include "ar5211/ar5211reg.h"#include "ar5211/ar5211desc.h"/* * Update Tx FIFO trigger level. * * Set bIncTrigLevel to TRUE to increase the trigger level. * Set bIncTrigLevel to FALSE to decrease the trigger level. * * Returns TRUE if the trigger level was updated */HAL_BOOLar5211UpdateTxTrigLevel(struct ath_hal *ah, HAL_BOOL bIncTrigLevel){ u_int32_t curTrigLevel, txcfg; HAL_INT ints = ar5211GetInterrupts(ah); /* * Disable chip interrupts. This is because halUpdateTxTrigLevel * is called from both ISR and non-ISR contexts. */ ar5211SetInterrupts(ah, ints &~ HAL_INT_GLOBAL); txcfg = OS_REG_READ(ah, AR_TXCFG); curTrigLevel = (txcfg & AR_TXCFG_FTRIG_M) >> AR_TXCFG_FTRIG_S; if (bIncTrigLevel){ /* increase the trigger level */ curTrigLevel = curTrigLevel + ((MAX_TX_FIFO_THRESHOLD - curTrigLevel) / 2); } else { /* decrease the trigger level if not already at the minimum */ if (curTrigLevel > MIN_TX_FIFO_THRESHOLD) { /* decrease the trigger level */ curTrigLevel--; } else { /* no update to the trigger level */ /* re-enable chip interrupts */ ar5211SetInterrupts(ah, ints); return AH_FALSE; } } /* Update the trigger level */ OS_REG_WRITE(ah, AR_TXCFG, (txcfg &~ AR_TXCFG_FTRIG_M) | ((curTrigLevel << AR_TXCFG_FTRIG_S) & AR_TXCFG_FTRIG_M)); /* re-enable chip interrupts */ ar5211SetInterrupts(ah, ints); return AH_TRUE;}/* * Set the properties of the tx queue with the parameters * from qInfo. The queue must previously have been setup * with a call to ar5211SetupTxQueue. */HAL_BOOLar5211SetTxQueueProps(struct ath_hal *ah, int q, const HAL_TXQ_INFO *qInfo){ struct ath_hal_5211 *ahp = AH5211(ah); if (q >= HAL_NUM_TX_QUEUES) { HALDEBUG(ah, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } return ath_hal_setTxQProps(ah, &ahp->ah_txq[q], qInfo);}/* * Return the properties for the specified tx queue. */HAL_BOOLar5211GetTxQueueProps(struct ath_hal *ah, int q, HAL_TXQ_INFO *qInfo){ struct ath_hal_5211 *ahp = AH5211(ah); if (q >= HAL_NUM_TX_QUEUES) { HALDEBUG(ah, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } return ath_hal_getTxQProps(ah, qInfo, &ahp->ah_txq[q]);}/* * Allocate and initialize a tx DCU/QCU combination. */intar5211SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type, const HAL_TXQ_INFO *qInfo){ struct ath_hal_5211 *ahp = AH5211(ah); HAL_TX_QUEUE_INFO *qi; int q; switch (type) { case HAL_TX_QUEUE_BEACON: q = 9; break; case HAL_TX_QUEUE_CAB: q = 8; break; case HAL_TX_QUEUE_DATA: q = 0; if (ahp->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE) return q; break; default: HALDEBUG(ah, "%s: bad tx queue type %u\n", __func__, type); return -1; } HALDEBUG(ah, "%s: queue %u\n", __func__, q); qi = &ahp->ah_txq[q]; if (qi->tqi_type != HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, "%s: tx queue %u already active\n", __func__, q); return -1; } OS_MEMZERO(qi, sizeof(HAL_TX_QUEUE_INFO)); qi->tqi_type = type; if (qInfo == AH_NULL) { /* by default enable OK+ERR+DESC+URN interrupts */ qi->tqi_qflags = TXQ_FLAG_TXOKINT_ENABLE | TXQ_FLAG_TXERRINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE ; qi->tqi_aifs = INIT_AIFS; qi->tqi_cwmin = HAL_TXQ_USEDEFAULT; /* NB: do at reset */ qi->tqi_cwmax = INIT_CWMAX; qi->tqi_shretry = INIT_SH_RETRY; qi->tqi_lgretry = INIT_LG_RETRY; } else (void) ar5211SetTxQueueProps(ah, q, qInfo); return q;}/* * Update the h/w interrupt registers to reflect a tx q's configuration. */static voidsetTxQInterrupts(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi){ struct ath_hal_5211 *ahp = AH5211(ah); HALDEBUG(ah, "%s: tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n" , __func__ , ahp->ah_txOkInterruptMask , ahp->ah_txErrInterruptMask , ahp->ah_txDescInterruptMask , ahp->ah_txEolInterruptMask , ahp->ah_txUrnInterruptMask ); OS_REG_WRITE(ah, AR_IMR_S0, SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK) | SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC) ); OS_REG_WRITE(ah, AR_IMR_S1, SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR) | SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL) ); OS_REG_RMW_FIELD(ah, AR_IMR_S2, AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask);}/* * Free a tx DCU/QCU combination. */HAL_BOOLar5211ReleaseTxQueue(struct ath_hal *ah, u_int q){ struct ath_hal_5211 *ahp = AH5211(ah); HAL_TX_QUEUE_INFO *qi; if (q >= HAL_NUM_TX_QUEUES) { HALDEBUG(ah, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } qi = &ahp->ah_txq[q]; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, "%s: inactive queue %u\n", __func__, q); return AH_FALSE; } HALDEBUG(ah, "%s: release queue %u\n", __func__, q); qi->tqi_type = HAL_TX_QUEUE_INACTIVE; ahp->ah_txOkInterruptMask &= ~(1 << q); ahp->ah_txErrInterruptMask &= ~(1 << q); ahp->ah_txDescInterruptMask &= ~(1 << q); ahp->ah_txEolInterruptMask &= ~(1 << q); ahp->ah_txUrnInterruptMask &= ~(1 << q); setTxQInterrupts(ah, qi); return AH_TRUE;}/* * Set the retry, aifs, cwmin/max, readyTime regs for specified queue */HAL_BOOLar5211ResetTxQueue(struct ath_hal *ah, u_int q){ struct ath_hal_5211 *ahp = AH5211(ah); HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan; HAL_TX_QUEUE_INFO *qi; u_int32_t cwMin, chanCwMin, value; if (q >= HAL_NUM_TX_QUEUES) { HALDEBUG(ah, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } qi = &ahp->ah_txq[q]; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, "%s: inactive queue %u\n", __func__, q); return AH_TRUE; /* XXX??? */ } if (qi->tqi_cwmin == HAL_TXQ_USEDEFAULT) { /* * Select cwmin according to channel type. * NB: chan can be NULL during attach */ if (chan && IS_CHAN_B(chan)) chanCwMin = INIT_CWMIN_11B; else chanCwMin = INIT_CWMIN; /* make sure that the CWmin is of the form (2^n - 1) */ for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1) ; } else cwMin = qi->tqi_cwmin; /* set cwMin/Max and AIFS values */ OS_REG_WRITE(ah, AR_DLCL_IFS(q), SM(cwMin, AR_D_LCL_IFS_CWMIN) | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); /* Set retry limit values */ OS_REG_WRITE(ah, AR_DRETRY_LIMIT(q), SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) | SM(qi->tqi_lgretry, AR_D_RETRY_LIMIT_FR_LG) | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH) ); /* enable early termination on the QCU */ OS_REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ); if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU) { /* Configure DCU to use the global sequence count */ OS_REG_WRITE(ah, AR_DMISC(q), AR5311_D_MISC_SEQ_NUM_CONTROL); } /* multiqueue support */ if (qi->tqi_cbrPeriod) { OS_REG_WRITE(ah, AR_QCBRCFG(q), SM(qi->tqi_cbrPeriod,AR_Q_CBRCFG_CBR_INTERVAL) | SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_CBR_OVF_THRESH)); OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_CBR | (qi->tqi_cbrOverflowLimit ? AR_Q_MISC_CBR_EXP_CNTR_LIMIT : 0)); } if (qi->tqi_readyTime) { OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) | AR_Q_RDYTIMECFG_EN); } if (qi->tqi_burstTime) { OS_REG_WRITE(ah, AR_DCHNTIME(q), SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) | AR_D_CHNTIME_EN); if (qi->tqi_qflags & TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) { OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_RDYTIME_EXP_POLICY); } } if (qi->tqi_qflags & TXQ_FLAG_BACKOFF_DISABLE) { OS_REG_WRITE(ah, AR_DMISC(q), OS_REG_READ(ah, AR_DMISC(q)) | AR_D_MISC_POST_FR_BKOFF_DIS); } if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) { OS_REG_WRITE(ah, AR_DMISC(q), OS_REG_READ(ah, AR_DMISC(q)) | AR_D_MISC_FRAG_BKOFF_EN); } switch (qi->tqi_type) { case HAL_TX_QUEUE_BEACON: /* Configure QCU for beacons */ OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_BEACON_USE | AR_Q_MISC_CBR_INCR_DIS1); /* Configure DCU for beacons */ value = (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << AR_D_MISC_ARB_LOCKOUT_CNTRL_S) | AR_D_MISC_BEACON_USE | AR_D_MISC_POST_FR_BKOFF_DIS; if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU) value |= AR5311_D_MISC_SEQ_NUM_CONTROL; OS_REG_WRITE(ah, AR_DMISC(q), value); break; case HAL_TX_QUEUE_CAB: /* Configure QCU for CAB (Crap After Beacon) frames */ OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_CBR_INCR_DIS1 | AR_Q_MISC_CBR_INCR_DIS0 | AR_Q_MISC_RDYTIME_EXP_POLICY); value = (ahp->ah_beaconInterval - (ath_hal_sw_beacon_response_time - ath_hal_dma_beacon_response_time) - ath_hal_additional_swba_backoff) * 1024; OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_EN); /* Configure DCU for CAB */ value = (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << AR_D_MISC_ARB_LOCKOUT_CNTRL_S); if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -