aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi
diff options
context:
space:
mode:
authorDaniel Halperin <dhalperi@cs.washington.edu>2010-05-25 13:22:49 -0400
committerReinette Chatre <reinette.chatre@intel.com>2010-06-06 02:21:26 -0400
commitf668da2f150948a961d359c65b5e9d62da1290e2 (patch)
treeb0d23bd8b858751b5e0af473c521945185cf9acf /drivers/net/wireless/iwlwifi
parent02cd8dee6e10d6ab7161a3c6f36a60f8894fafdd (diff)
iwlwifi: fix wrapping when handling aggregated batches
Fairly complex code in iwlagn_tx_status_reply_tx handle the status reports for aggregated packet batches sent by the NIC. This code aims to handle the case where the NIC retransmits failed packets from a previous batch; the status information for these packets can sometimes be inserted in the middle of a batch and are actually not in order by sequence number! (I verified this can happen with printk's in the function.) The code in question adaptively identifies the "first" frame of the batch, taking into account that it may not be the one corresponding to the first agg status report, and also handles the case when the set of sent packets wraps the 256-character entry buffer. It generates the agg->bitmap field of sent packets which is later compared to the BlockAck response from the receiver to see which frames of those sent in this batch were ACKed. A small logic error (wrapping by 0xff==255 instead of 0x100==256) was causing the agg->bitmap to be set incorrectly. Fix this wrapping code, and add extensive comments to clarify what is going on. Signed-off-by: Daniel Halperin <dhalperi@cs.washington.edu> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-lib.c51
1 files changed, 45 insertions, 6 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index d339881e1d8f..3577c1eeb77b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -93,6 +93,12 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
93 } else { 93 } else {
94 /* Two or more frames were attempted; expect block-ack */ 94 /* Two or more frames were attempted; expect block-ack */
95 u64 bitmap = 0; 95 u64 bitmap = 0;
96
97 /*
98 * Start is the lowest frame sent. It may not be the first
99 * frame in the batch; we figure this out dynamically during
100 * the following loop.
101 */
96 int start = agg->start_idx; 102 int start = agg->start_idx;
97 103
98 /* Construct bit-map of pending frames within Tx window */ 104 /* Construct bit-map of pending frames within Tx window */
@@ -131,25 +137,58 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
131 IWL_DEBUG_TX_REPLY(priv, "AGG Frame i=%d idx %d seq=%d\n", 137 IWL_DEBUG_TX_REPLY(priv, "AGG Frame i=%d idx %d seq=%d\n",
132 i, idx, SEQ_TO_SN(sc)); 138 i, idx, SEQ_TO_SN(sc));
133 139
140 /*
141 * sh -> how many frames ahead of the starting frame is
142 * the current one?
143 *
144 * Note that all frames sent in the batch must be in a
145 * 64-frame window, so this number should be in [0,63].
146 * If outside of this window, then we've found a new
147 * "first" frame in the batch and need to change start.
148 */
134 sh = idx - start; 149 sh = idx - start;
135 if (sh > 64) { 150
136 sh = (start - idx) + 0xff; 151 /*
152 * If >= 64, out of window. start must be at the front
153 * of the circular buffer, idx must be near the end of
154 * the buffer, and idx is the new "first" frame. Shift
155 * the indices around.
156 */
157 if (sh >= 64) {
158 /* Shift bitmap by start - idx, wrapped */
159 sh = 0x100 - idx + start;
137 bitmap = bitmap << sh; 160 bitmap = bitmap << sh;
161 /* Now idx is the new start so sh = 0 */
138 sh = 0; 162 sh = 0;
139 start = idx; 163 start = idx;
140 } else if (sh < -64) 164 /*
141 sh = 0xff - (start - idx); 165 * If <= -64 then wraps the 256-pkt circular buffer
142 else if (sh < 0) { 166 * (e.g., start = 255 and idx = 0, sh should be 1)
167 */
168 } else if (sh <= -64) {
169 sh = 0x100 - start + idx;
170 /*
171 * If < 0 but > -64, out of window. idx is before start
172 * but not wrapped. Shift the indices around.
173 */
174 } else if (sh < 0) {
175 /* Shift by how far start is ahead of idx */
143 sh = start - idx; 176 sh = start - idx;
144 start = idx;
145 bitmap = bitmap << sh; 177 bitmap = bitmap << sh;
178 /* Now idx is the new start so sh = 0 */
179 start = idx;
146 sh = 0; 180 sh = 0;
147 } 181 }
182 /* Sequence number start + sh was sent in this batch */
148 bitmap |= 1ULL << sh; 183 bitmap |= 1ULL << sh;
149 IWL_DEBUG_TX_REPLY(priv, "start=%d bitmap=0x%llx\n", 184 IWL_DEBUG_TX_REPLY(priv, "start=%d bitmap=0x%llx\n",
150 start, (unsigned long long)bitmap); 185 start, (unsigned long long)bitmap);
151 } 186 }
152 187
188 /*
189 * Store the bitmap and possibly the new start, if we wrapped
190 * the buffer above
191 */
153 agg->bitmap = bitmap; 192 agg->bitmap = bitmap;
154 agg->start_idx = start; 193 agg->start_idx = start;
155 IWL_DEBUG_TX_REPLY(priv, "Frames %d start_idx=%d bitmap=0x%llx\n", 194 IWL_DEBUG_TX_REPLY(priv, "Frames %d start_idx=%d bitmap=0x%llx\n",