Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e7824a50 authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by John W. Linville
Browse files

ath9k: fix processing of TX PS null data frames



When mac80211 was telling us to go into Powersave we listened
and immediately turned RX off. This meant hardware would not
see the ACKs from the AP we're associated with and hardware
we'd end up retransmiting the null data frame in a loop
helplessly.

Fix this by keeping track of the transmitted nullfunc frames
and only when we are sure the AP has sent back an ACK do we
go ahead and shut RX off.

Signed-off-by: default avatarVasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: default avatarVivek Natarajan <Vivek.Natarajan@atheros.com>
Signed-off-by: default avatarLuis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 6b65b6ad
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -420,6 +420,8 @@ struct ath_led {
#define SC_OP_WAIT_FOR_TX_ACK   BIT(18)
#define SC_OP_BEACON_SYNC       BIT(19)
#define SC_OP_BT_PRIORITY_DETECTED BIT(21)
#define SC_OP_NULLFUNC_COMPLETED BIT(22)
#define SC_OP_PS_ENABLED	BIT(23)

struct ath_wiphy;
struct ath_rate_table;
+1 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ struct ath_buf {
	dma_addr_t bf_daddr;		/* physical addr of desc */
	dma_addr_t bf_buf_addr;		/* physical addr of data buffer */
	bool bf_stale;
	bool bf_isnullfunc;
	u16 bf_flags;
	struct ath_buf_state bf_state;
	dma_addr_t bf_dmacontext;
+9 −0
Original line number Diff line number Diff line
@@ -231,6 +231,8 @@ int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds)
	ds->ds_txstat.ts_status = 0;
	ds->ds_txstat.ts_flags = 0;

	if (ads->ds_txstatus1 & AR_FrmXmitOK)
		ds->ds_txstat.ts_status |= ATH9K_TX_ACKED;
	if (ads->ds_txstatus1 & AR_ExcessiveRetries)
		ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY;
	if (ads->ds_txstatus1 & AR_Filtered)
@@ -926,6 +928,13 @@ void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds,
}
EXPORT_SYMBOL(ath9k_hw_setuprxdesc);

/*
 * This can stop or re-enables RX.
 *
 * If bool is set this will kill any frame which is currently being
 * transferred between the MAC and baseband and also prevent any new
 * frames from getting started.
 */
bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set)
{
	u32 reg;
+6 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@
#define ATH9K_TXERR_FIFO           0x04
#define ATH9K_TXERR_XTXOP          0x08
#define ATH9K_TXERR_TIMER_EXPIRED  0x10
#define ATH9K_TX_ACKED		   0x20

#define ATH9K_TX_BA                0x01
#define ATH9K_TX_PWRMGMT           0x02
@@ -380,6 +381,11 @@ struct ar5416_desc {
#define AR_TxBaStatus       0x40000000
#define AR_TxStatusRsvd01   0x80000000

/*
 * AR_FrmXmitOK - Frame transmission success flag. If set, the frame was
 * transmitted successfully. If clear, no ACK or BA was received to indicate
 * successful transmission when we were expecting an ACK or BA.
 */
#define AR_FrmXmitOK            0x00000001
#define AR_ExcessiveRetries     0x00000002
#define AR_FIFOUnderrun         0x00000004
+18 −2
Original line number Diff line number Diff line
@@ -2701,8 +2701,15 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
		}
	}

	/*
	 * We just prepare to enable PS. We have to wait until our AP has
	 * ACK'd our null data frame to disable RX otherwise we'll ignore
	 * those ACKs and end up retransmitting the same null data frames.
	 * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
	 */
	if (changed & IEEE80211_CONF_CHANGE_PS) {
		if (conf->flags & IEEE80211_CONF_PS) {
			sc->sc_flags |= SC_OP_PS_ENABLED;
			if (!(ah->caps.hw_caps &
			      ATH9K_HW_CAP_AUTOSLEEP)) {
				if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
@@ -2710,11 +2717,20 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
					ath9k_hw_set_interrupts(sc->sc_ah,
							sc->imask);
				}
				ath9k_hw_setrxabort(sc->sc_ah, 1);
			}
			/*
			 * At this point we know hardware has received an ACK
			 * of a previously sent null data frame.
			 */
			if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) {
				sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED;
				sc->ps_enabled = true;
				ath9k_hw_setrxabort(sc->sc_ah, 1);
                        }
		} else {
			sc->ps_enabled = false;
			sc->sc_flags &= ~(SC_OP_PS_ENABLED |
					  SC_OP_NULLFUNC_COMPLETED);
			ath9k_setpower(sc, ATH9K_PM_AWAKE);
			if (!(ah->caps.hw_caps &
			      ATH9K_HW_CAP_AUTOSLEEP)) {
Loading