diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2011-08-26 02:11:00 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-08-29 15:25:34 -0400 |
commit | a0eaad713f6fc1f63fe293ad6ce63cb01e05c03c (patch) | |
tree | 72dee57cdee0bfb9e796d6b12159b75eae17f5c7 /drivers/net/wireless/iwlwifi | |
parent | 1f7b6172db86e9ab2b4cd794441bb2c40ab287fc (diff) |
iwlagn: reclaim the packets in transport layer
The reclaim flow is really transport related. Define a simple API to allow the
upper layer to request from the transport layer to reclaim packets until an
index written in the Tx response / BA notification.
The transport layer prepares a list of the packets that are being freed and
passes this list to the upper layer.
Between the two layers, the CB of the skb is used to pass a pointer to the
context (BSS / PAN) in which the skb was sent.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 271 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 428 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-helpers.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c | 55 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.c | 31 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.h | 10 |
8 files changed, 372 insertions, 442 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 57839cab92ae..52ddb49d2017 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c | |||
@@ -42,12 +42,6 @@ | |||
42 | #include "iwl-trans.h" | 42 | #include "iwl-trans.h" |
43 | #include "iwl-shared.h" | 43 | #include "iwl-shared.h" |
44 | 44 | ||
45 | static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) | ||
46 | { | ||
47 | return le32_to_cpup((__le32 *)&tx_resp->status + | ||
48 | tx_resp->frame_count) & MAX_SN; | ||
49 | } | ||
50 | |||
51 | static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) | 45 | static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) |
52 | { | 46 | { |
53 | status &= TX_STATUS_MSK; | 47 | status &= TX_STATUS_MSK; |
@@ -125,7 +119,7 @@ static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) | |||
125 | } | 119 | } |
126 | } | 120 | } |
127 | 121 | ||
128 | static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) | 122 | void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) |
129 | { | 123 | { |
130 | status &= AGG_TX_STATUS_MSK; | 124 | status &= AGG_TX_STATUS_MSK; |
131 | 125 | ||
@@ -172,11 +166,10 @@ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) | |||
172 | } | 166 | } |
173 | } | 167 | } |
174 | 168 | ||
175 | static void iwlagn_set_tx_status(struct iwl_priv *priv, | 169 | void iwlagn_set_tx_status(struct iwl_priv *priv, |
176 | struct ieee80211_tx_info *info, | 170 | struct ieee80211_tx_info *info, |
177 | struct iwl_rxon_context *ctx, | ||
178 | struct iwlagn_tx_resp *tx_resp, | 171 | struct iwlagn_tx_resp *tx_resp, |
179 | int txq_id, bool is_agg) | 172 | bool is_agg) |
180 | { | 173 | { |
181 | u16 status = le16_to_cpu(tx_resp->status.status); | 174 | u16 status = le16_to_cpu(tx_resp->status.status); |
182 | 175 | ||
@@ -188,20 +181,6 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv, | |||
188 | info); | 181 | info); |
189 | if (!iwl_is_tx_success(status)) | 182 | if (!iwl_is_tx_success(status)) |
190 | iwlagn_count_tx_err_status(priv, status); | 183 | iwlagn_count_tx_err_status(priv, status); |
191 | |||
192 | if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && | ||
193 | iwl_is_associated_ctx(ctx) && ctx->vif && | ||
194 | ctx->vif->type == NL80211_IFTYPE_STATION) { | ||
195 | ctx->last_tx_rejected = true; | ||
196 | iwl_stop_queue(priv, &priv->txq[txq_id]); | ||
197 | } | ||
198 | |||
199 | IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags " | ||
200 | "0x%x retries %d\n", | ||
201 | txq_id, | ||
202 | iwl_get_tx_fail_reason(status), status, | ||
203 | le32_to_cpu(tx_resp->rate_n_flags), | ||
204 | tx_resp->failure_frame); | ||
205 | } | 184 | } |
206 | 185 | ||
207 | #ifdef CONFIG_IWLWIFI_DEBUG | 186 | #ifdef CONFIG_IWLWIFI_DEBUG |
@@ -231,157 +210,6 @@ const char *iwl_get_agg_tx_fail_reason(u16 status) | |||
231 | } | 210 | } |
232 | #endif /* CONFIG_IWLWIFI_DEBUG */ | 211 | #endif /* CONFIG_IWLWIFI_DEBUG */ |
233 | 212 | ||
234 | static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv, | ||
235 | struct iwl_ht_agg *agg, | ||
236 | struct iwlagn_tx_resp *tx_resp, | ||
237 | int txq_id, u16 start_idx) | ||
238 | { | ||
239 | u16 status; | ||
240 | struct agg_tx_status *frame_status = &tx_resp->status; | ||
241 | struct ieee80211_hdr *hdr = NULL; | ||
242 | int i, sh, idx; | ||
243 | u16 seq; | ||
244 | |||
245 | if (agg->wait_for_ba) | ||
246 | IWL_DEBUG_TX_REPLY(priv, "got tx response w/o block-ack\n"); | ||
247 | |||
248 | agg->frame_count = tx_resp->frame_count; | ||
249 | agg->start_idx = start_idx; | ||
250 | agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); | ||
251 | agg->bitmap = 0; | ||
252 | |||
253 | /* # frames attempted by Tx command */ | ||
254 | if (agg->frame_count == 1) { | ||
255 | struct iwl_tx_info *txb; | ||
256 | |||
257 | /* Only one frame was attempted; no block-ack will arrive */ | ||
258 | idx = start_idx; | ||
259 | |||
260 | IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n", | ||
261 | agg->frame_count, agg->start_idx, idx); | ||
262 | txb = &priv->txq[txq_id].txb[idx]; | ||
263 | iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(txb->skb), | ||
264 | txb->ctx, tx_resp, txq_id, true); | ||
265 | agg->wait_for_ba = 0; | ||
266 | } else { | ||
267 | /* Two or more frames were attempted; expect block-ack */ | ||
268 | u64 bitmap = 0; | ||
269 | |||
270 | /* | ||
271 | * Start is the lowest frame sent. It may not be the first | ||
272 | * frame in the batch; we figure this out dynamically during | ||
273 | * the following loop. | ||
274 | */ | ||
275 | int start = agg->start_idx; | ||
276 | |||
277 | /* Construct bit-map of pending frames within Tx window */ | ||
278 | for (i = 0; i < agg->frame_count; i++) { | ||
279 | u16 sc; | ||
280 | status = le16_to_cpu(frame_status[i].status); | ||
281 | seq = le16_to_cpu(frame_status[i].sequence); | ||
282 | idx = SEQ_TO_INDEX(seq); | ||
283 | txq_id = SEQ_TO_QUEUE(seq); | ||
284 | |||
285 | if (status & AGG_TX_STATUS_MSK) | ||
286 | iwlagn_count_agg_tx_err_status(priv, status); | ||
287 | |||
288 | if (status & (AGG_TX_STATE_FEW_BYTES_MSK | | ||
289 | AGG_TX_STATE_ABORT_MSK)) | ||
290 | continue; | ||
291 | |||
292 | IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, txq_id=%d idx=%d\n", | ||
293 | agg->frame_count, txq_id, idx); | ||
294 | IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), " | ||
295 | "try-count (0x%08x)\n", | ||
296 | iwl_get_agg_tx_fail_reason(status), | ||
297 | status & AGG_TX_STATUS_MSK, | ||
298 | status & AGG_TX_TRY_MSK); | ||
299 | |||
300 | hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx); | ||
301 | if (!hdr) { | ||
302 | IWL_ERR(priv, | ||
303 | "BUG_ON idx doesn't point to valid skb" | ||
304 | " idx=%d, txq_id=%d\n", idx, txq_id); | ||
305 | return -1; | ||
306 | } | ||
307 | |||
308 | sc = le16_to_cpu(hdr->seq_ctrl); | ||
309 | if (idx != (SEQ_TO_SN(sc) & 0xff)) { | ||
310 | IWL_ERR(priv, | ||
311 | "BUG_ON idx doesn't match seq control" | ||
312 | " idx=%d, seq_idx=%d, seq=%d\n", | ||
313 | idx, SEQ_TO_SN(sc), | ||
314 | hdr->seq_ctrl); | ||
315 | return -1; | ||
316 | } | ||
317 | |||
318 | IWL_DEBUG_TX_REPLY(priv, "AGG Frame i=%d idx %d seq=%d\n", | ||
319 | i, idx, SEQ_TO_SN(sc)); | ||
320 | |||
321 | /* | ||
322 | * sh -> how many frames ahead of the starting frame is | ||
323 | * the current one? | ||
324 | * | ||
325 | * Note that all frames sent in the batch must be in a | ||
326 | * 64-frame window, so this number should be in [0,63]. | ||
327 | * If outside of this window, then we've found a new | ||
328 | * "first" frame in the batch and need to change start. | ||
329 | */ | ||
330 | sh = idx - start; | ||
331 | |||
332 | /* | ||
333 | * If >= 64, out of window. start must be at the front | ||
334 | * of the circular buffer, idx must be near the end of | ||
335 | * the buffer, and idx is the new "first" frame. Shift | ||
336 | * the indices around. | ||
337 | */ | ||
338 | if (sh >= 64) { | ||
339 | /* Shift bitmap by start - idx, wrapped */ | ||
340 | sh = 0x100 - idx + start; | ||
341 | bitmap = bitmap << sh; | ||
342 | /* Now idx is the new start so sh = 0 */ | ||
343 | sh = 0; | ||
344 | start = idx; | ||
345 | /* | ||
346 | * If <= -64 then wraps the 256-pkt circular buffer | ||
347 | * (e.g., start = 255 and idx = 0, sh should be 1) | ||
348 | */ | ||
349 | } else if (sh <= -64) { | ||
350 | sh = 0x100 - start + idx; | ||
351 | /* | ||
352 | * If < 0 but > -64, out of window. idx is before start | ||
353 | * but not wrapped. Shift the indices around. | ||
354 | */ | ||
355 | } else if (sh < 0) { | ||
356 | /* Shift by how far start is ahead of idx */ | ||
357 | sh = start - idx; | ||
358 | bitmap = bitmap << sh; | ||
359 | /* Now idx is the new start so sh = 0 */ | ||
360 | start = idx; | ||
361 | sh = 0; | ||
362 | } | ||
363 | /* Sequence number start + sh was sent in this batch */ | ||
364 | bitmap |= 1ULL << sh; | ||
365 | IWL_DEBUG_TX_REPLY(priv, "start=%d bitmap=0x%llx\n", | ||
366 | start, (unsigned long long)bitmap); | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * Store the bitmap and possibly the new start, if we wrapped | ||
371 | * the buffer above | ||
372 | */ | ||
373 | agg->bitmap = bitmap; | ||
374 | agg->start_idx = start; | ||
375 | IWL_DEBUG_TX_REPLY(priv, "Frames %d start_idx=%d bitmap=0x%llx\n", | ||
376 | agg->frame_count, agg->start_idx, | ||
377 | (unsigned long long)agg->bitmap); | ||
378 | |||
379 | if (bitmap) | ||
380 | agg->wait_for_ba = 1; | ||
381 | } | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | void iwl_check_abort_status(struct iwl_priv *priv, | 213 | void iwl_check_abort_status(struct iwl_priv *priv, |
386 | u8 frame_count, u32 status) | 214 | u8 frame_count, u32 status) |
387 | { | 215 | { |
@@ -392,99 +220,6 @@ void iwl_check_abort_status(struct iwl_priv *priv, | |||
392 | } | 220 | } |
393 | } | 221 | } |
394 | 222 | ||
395 | void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) | ||
396 | { | ||
397 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
398 | u16 sequence = le16_to_cpu(pkt->hdr.sequence); | ||
399 | int txq_id = SEQ_TO_QUEUE(sequence); | ||
400 | int index = SEQ_TO_INDEX(sequence); | ||
401 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | ||
402 | struct ieee80211_tx_info *info; | ||
403 | struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; | ||
404 | struct ieee80211_hdr *hdr; | ||
405 | struct iwl_tx_info *txb; | ||
406 | u32 status = le16_to_cpu(tx_resp->status.status); | ||
407 | int tid; | ||
408 | int sta_id; | ||
409 | int freed; | ||
410 | unsigned long flags; | ||
411 | |||
412 | if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) { | ||
413 | IWL_ERR(priv, "%s: Read index for DMA queue txq_id (%d) " | ||
414 | "index %d is out of range [0-%d] %d %d\n", __func__, | ||
415 | txq_id, index, txq->q.n_bd, txq->q.write_ptr, | ||
416 | txq->q.read_ptr); | ||
417 | return; | ||
418 | } | ||
419 | |||
420 | txq->time_stamp = jiffies; | ||
421 | txb = &txq->txb[txq->q.read_ptr]; | ||
422 | info = IEEE80211_SKB_CB(txb->skb); | ||
423 | memset(&info->status, 0, sizeof(info->status)); | ||
424 | |||
425 | tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> | ||
426 | IWLAGN_TX_RES_TID_POS; | ||
427 | sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> | ||
428 | IWLAGN_TX_RES_RA_POS; | ||
429 | |||
430 | spin_lock_irqsave(&priv->shrd->sta_lock, flags); | ||
431 | |||
432 | hdr = (void *)txb->skb->data; | ||
433 | if (!ieee80211_is_data_qos(hdr->frame_control)) | ||
434 | priv->last_seq_ctl = tx_resp->seq_ctl; | ||
435 | |||
436 | if (txq->sched_retry) { | ||
437 | const u32 scd_ssn = iwlagn_get_scd_ssn(tx_resp); | ||
438 | struct iwl_ht_agg *agg; | ||
439 | |||
440 | agg = &priv->stations[sta_id].tid[tid].agg; | ||
441 | /* | ||
442 | * If the BT kill count is non-zero, we'll get this | ||
443 | * notification again. | ||
444 | */ | ||
445 | if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && | ||
446 | priv->cfg->bt_params && | ||
447 | priv->cfg->bt_params->advanced_bt_coexist) { | ||
448 | IWL_DEBUG_COEX(priv, "receive reply tx with bt_kill\n"); | ||
449 | } | ||
450 | iwlagn_tx_status_reply_tx(priv, agg, tx_resp, txq_id, index); | ||
451 | |||
452 | /* check if BAR is needed */ | ||
453 | if ((tx_resp->frame_count == 1) && !iwl_is_tx_success(status)) | ||
454 | info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; | ||
455 | |||
456 | if (txq->q.read_ptr != (scd_ssn & 0xff)) { | ||
457 | index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd); | ||
458 | IWL_DEBUG_TX_REPLY(priv, "Retry scheduler reclaim " | ||
459 | "scd_ssn=%d idx=%d txq=%d swq=%d\n", | ||
460 | scd_ssn , index, txq_id, txq->swq_id); | ||
461 | |||
462 | freed = iwlagn_tx_queue_reclaim(priv, txq_id, index); | ||
463 | iwl_free_tfds_in_queue(priv, sta_id, tid, freed); | ||
464 | |||
465 | if (priv->mac80211_registered && | ||
466 | (iwl_queue_space(&txq->q) > txq->q.low_mark) && | ||
467 | (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) | ||
468 | iwl_wake_queue(priv, txq); | ||
469 | } | ||
470 | } else { | ||
471 | iwlagn_set_tx_status(priv, info, txb->ctx, tx_resp, | ||
472 | txq_id, false); | ||
473 | freed = iwlagn_tx_queue_reclaim(priv, txq_id, index); | ||
474 | iwl_free_tfds_in_queue(priv, sta_id, tid, freed); | ||
475 | |||
476 | if (priv->mac80211_registered && | ||
477 | iwl_queue_space(&txq->q) > txq->q.low_mark && | ||
478 | status != TX_STATUS_FAIL_PASSIVE_NO_RX) | ||
479 | iwl_wake_queue(priv, txq); | ||
480 | } | ||
481 | |||
482 | iwlagn_txq_check_empty(priv, sta_id, tid, txq_id); | ||
483 | |||
484 | iwl_check_abort_status(priv, tx_resp->frame_count, status); | ||
485 | spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); | ||
486 | } | ||
487 | |||
488 | int iwlagn_hw_valid_rtc_data_addr(u32 addr) | 223 | int iwlagn_hw_valid_rtc_data_addr(u32 addr) |
489 | { | 224 | { |
490 | return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && | 225 | return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && |
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 0e9deb7b64d3..b56a269aa5f4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
32 | #include <linux/init.h> | 32 | #include <linux/init.h> |
33 | #include <linux/sched.h> | 33 | #include <linux/sched.h> |
34 | #include <linux/ieee80211.h> | ||
34 | 35 | ||
35 | #include "iwl-dev.h" | 36 | #include "iwl-dev.h" |
36 | #include "iwl-core.h" | 37 | #include "iwl-core.h" |
@@ -696,147 +697,224 @@ static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, | |||
696 | rcu_read_unlock(); | 697 | rcu_read_unlock(); |
697 | } | 698 | } |
698 | 699 | ||
699 | static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info, | 700 | /** |
700 | bool is_agg) | 701 | * translate ucode response to mac80211 tx status control values |
702 | */ | ||
703 | void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, | ||
704 | struct ieee80211_tx_info *info) | ||
701 | { | 705 | { |
702 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx_info->skb->data; | 706 | struct ieee80211_tx_rate *r = &info->control.rates[0]; |
703 | |||
704 | if (!is_agg) | ||
705 | iwlagn_non_agg_tx_status(priv, tx_info->ctx, hdr->addr1); | ||
706 | 707 | ||
707 | ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb); | 708 | info->antenna_sel_tx = |
709 | ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); | ||
710 | if (rate_n_flags & RATE_MCS_HT_MSK) | ||
711 | r->flags |= IEEE80211_TX_RC_MCS; | ||
712 | if (rate_n_flags & RATE_MCS_GF_MSK) | ||
713 | r->flags |= IEEE80211_TX_RC_GREEN_FIELD; | ||
714 | if (rate_n_flags & RATE_MCS_HT40_MSK) | ||
715 | r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; | ||
716 | if (rate_n_flags & RATE_MCS_DUP_MSK) | ||
717 | r->flags |= IEEE80211_TX_RC_DUP_DATA; | ||
718 | if (rate_n_flags & RATE_MCS_SGI_MSK) | ||
719 | r->flags |= IEEE80211_TX_RC_SHORT_GI; | ||
720 | r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band); | ||
708 | } | 721 | } |
709 | 722 | ||
710 | int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) | 723 | #ifdef CONFIG_IWLWIFI_DEBUG |
724 | const char *iwl_get_tx_fail_reason(u32 status) | ||
711 | { | 725 | { |
712 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | 726 | #define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x |
713 | struct iwl_queue *q = &txq->q; | 727 | #define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x |
714 | struct iwl_tx_info *tx_info; | ||
715 | int nfreed = 0; | ||
716 | struct ieee80211_hdr *hdr; | ||
717 | 728 | ||
718 | if ((index >= q->n_bd) || (iwl_queue_used(q, index) == 0)) { | 729 | switch (status & TX_STATUS_MSK) { |
719 | IWL_ERR(priv, "%s: Read index for DMA queue txq id (%d), " | 730 | case TX_STATUS_SUCCESS: |
720 | "index %d is out of range [0-%d] %d %d.\n", __func__, | 731 | return "SUCCESS"; |
721 | txq_id, index, q->n_bd, q->write_ptr, q->read_ptr); | 732 | TX_STATUS_POSTPONE(DELAY); |
722 | return 0; | 733 | TX_STATUS_POSTPONE(FEW_BYTES); |
734 | TX_STATUS_POSTPONE(BT_PRIO); | ||
735 | TX_STATUS_POSTPONE(QUIET_PERIOD); | ||
736 | TX_STATUS_POSTPONE(CALC_TTAK); | ||
737 | TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); | ||
738 | TX_STATUS_FAIL(SHORT_LIMIT); | ||
739 | TX_STATUS_FAIL(LONG_LIMIT); | ||
740 | TX_STATUS_FAIL(FIFO_UNDERRUN); | ||
741 | TX_STATUS_FAIL(DRAIN_FLOW); | ||
742 | TX_STATUS_FAIL(RFKILL_FLUSH); | ||
743 | TX_STATUS_FAIL(LIFE_EXPIRE); | ||
744 | TX_STATUS_FAIL(DEST_PS); | ||
745 | TX_STATUS_FAIL(HOST_ABORTED); | ||
746 | TX_STATUS_FAIL(BT_RETRY); | ||
747 | TX_STATUS_FAIL(STA_INVALID); | ||
748 | TX_STATUS_FAIL(FRAG_DROPPED); | ||
749 | TX_STATUS_FAIL(TID_DISABLE); | ||
750 | TX_STATUS_FAIL(FIFO_FLUSHED); | ||
751 | TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); | ||
752 | TX_STATUS_FAIL(PASSIVE_NO_RX); | ||
753 | TX_STATUS_FAIL(NO_BEACON_ON_RADAR); | ||
723 | } | 754 | } |
724 | 755 | ||
725 | for (index = iwl_queue_inc_wrap(index, q->n_bd); | 756 | return "UNKNOWN"; |
726 | q->read_ptr != index; | ||
727 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { | ||
728 | 757 | ||
729 | tx_info = &txq->txb[txq->q.read_ptr]; | 758 | #undef TX_STATUS_FAIL |
759 | #undef TX_STATUS_POSTPONE | ||
760 | } | ||
761 | #endif /* CONFIG_IWLWIFI_DEBUG */ | ||
730 | 762 | ||
731 | if (WARN_ON_ONCE(tx_info->skb == NULL)) | 763 | static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, |
732 | continue; | 764 | struct iwlagn_tx_resp *tx_resp) |
765 | { | ||
766 | struct agg_tx_status *frame_status = &tx_resp->status; | ||
767 | int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> | ||
768 | IWLAGN_TX_RES_TID_POS; | ||
769 | int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> | ||
770 | IWLAGN_TX_RES_RA_POS; | ||
771 | struct iwl_ht_agg *agg = &priv->stations[sta_id].tid[tid].agg; | ||
772 | u32 status = le16_to_cpu(tx_resp->status.status); | ||
773 | int i; | ||
774 | |||
775 | if (agg->wait_for_ba) | ||
776 | IWL_DEBUG_TX_REPLY(priv, | ||
777 | "got tx response w/o block-ack\n"); | ||
733 | 778 | ||
734 | hdr = (struct ieee80211_hdr *)tx_info->skb->data; | 779 | agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); |
735 | if (ieee80211_is_data_qos(hdr->frame_control)) | 780 | agg->wait_for_ba = (tx_resp->frame_count > 1); |
736 | nfreed++; | 781 | |
782 | /* | ||
783 | * If the BT kill count is non-zero, we'll get this | ||
784 | * notification again. | ||
785 | */ | ||
786 | if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && | ||
787 | priv->cfg->bt_params && | ||
788 | priv->cfg->bt_params->advanced_bt_coexist) { | ||
789 | IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); | ||
790 | } | ||
737 | 791 | ||
738 | iwlagn_tx_status(priv, tx_info, | 792 | /* Construct bit-map of pending frames within Tx window */ |
739 | txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); | 793 | for (i = 0; i < tx_resp->frame_count; i++) { |
740 | tx_info->skb = NULL; | 794 | u16 fstatus = le16_to_cpu(frame_status[i].status); |
741 | 795 | ||
742 | iwlagn_txq_inval_byte_cnt_tbl(priv, txq); | 796 | if (status & AGG_TX_STATUS_MSK) |
797 | iwlagn_count_agg_tx_err_status(priv, fstatus); | ||
743 | 798 | ||
744 | iwlagn_txq_free_tfd(priv, txq, txq->q.read_ptr); | 799 | if (status & (AGG_TX_STATE_FEW_BYTES_MSK | |
800 | AGG_TX_STATE_ABORT_MSK)) | ||
801 | continue; | ||
802 | |||
803 | IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), " | ||
804 | "try-count (0x%08x)\n", | ||
805 | iwl_get_agg_tx_fail_reason(fstatus), | ||
806 | fstatus & AGG_TX_STATUS_MSK, | ||
807 | fstatus & AGG_TX_TRY_MSK); | ||
745 | } | 808 | } |
746 | return nfreed; | ||
747 | } | 809 | } |
748 | 810 | ||
749 | /** | 811 | static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) |
750 | * iwlagn_tx_status_reply_compressed_ba - Update tx status from block-ack | 812 | { |
751 | * | 813 | return le32_to_cpup((__le32 *)&tx_resp->status + |
752 | * Go through block-ack's bitmap of ACK'd frames, update driver's record of | 814 | tx_resp->frame_count) & MAX_SN; |
753 | * ACK vs. not. This gets sent to mac80211, then to rate scaling algo. | 815 | } |
754 | */ | ||
755 | static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv, | ||
756 | struct iwl_ht_agg *agg, | ||
757 | struct iwl_compressed_ba_resp *ba_resp) | ||
758 | 816 | ||
817 | void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) | ||
759 | { | 818 | { |
760 | int sh; | 819 | struct iwl_rx_packet *pkt = rxb_addr(rxb); |
761 | u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); | 820 | u16 sequence = le16_to_cpu(pkt->hdr.sequence); |
762 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); | 821 | int txq_id = SEQ_TO_QUEUE(sequence); |
822 | int cmd_index = SEQ_TO_INDEX(sequence); | ||
823 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | ||
824 | struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; | ||
825 | struct ieee80211_hdr *hdr; | ||
826 | u32 status = le16_to_cpu(tx_resp->status.status); | ||
827 | u32 ssn = iwlagn_get_scd_ssn(tx_resp); | ||
828 | int tid; | ||
829 | int sta_id; | ||
830 | int freed; | ||
763 | struct ieee80211_tx_info *info; | 831 | struct ieee80211_tx_info *info; |
764 | u64 bitmap, sent_bitmap; | 832 | unsigned long flags; |
833 | struct sk_buff_head skbs; | ||
834 | struct sk_buff *skb; | ||
835 | struct iwl_rxon_context *ctx; | ||
765 | 836 | ||
766 | if (unlikely(!agg->wait_for_ba)) { | 837 | if ((cmd_index >= txq->q.n_bd) || |
767 | if (unlikely(ba_resp->bitmap)) | 838 | (iwl_queue_used(&txq->q, cmd_index) == 0)) { |
768 | IWL_ERR(priv, "Received BA when not expected\n"); | 839 | IWL_ERR(priv, "%s: Read index for DMA queue txq_id (%d) " |
769 | return -EINVAL; | 840 | "cmd_index %d is out of range [0-%d] %d %d\n", |
841 | __func__, txq_id, cmd_index, txq->q.n_bd, | ||
842 | txq->q.write_ptr, txq->q.read_ptr); | ||
843 | return; | ||
770 | } | 844 | } |
771 | 845 | ||
772 | /* Mark that the expected block-ack response arrived */ | 846 | txq->time_stamp = jiffies; |
773 | agg->wait_for_ba = 0; | ||
774 | IWL_DEBUG_TX_REPLY(priv, "BA %d %d\n", agg->start_idx, ba_resp->seq_ctl); | ||
775 | |||
776 | /* Calculate shift to align block-ack bits with our Tx window bits */ | ||
777 | sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl >> 4); | ||
778 | if (sh < 0) | ||
779 | sh += 0x100; | ||
780 | |||
781 | /* | ||
782 | * Check for success or failure according to the | ||
783 | * transmitted bitmap and block-ack bitmap | ||
784 | */ | ||
785 | bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; | ||
786 | sent_bitmap = bitmap & agg->bitmap; | ||
787 | 847 | ||
788 | /* Sanity check values reported by uCode */ | 848 | tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> |
789 | if (ba_resp->txed_2_done > ba_resp->txed) { | 849 | IWLAGN_TX_RES_TID_POS; |
790 | IWL_DEBUG_TX_REPLY(priv, | 850 | sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> |
791 | "bogus sent(%d) and ack(%d) count\n", | 851 | IWLAGN_TX_RES_RA_POS; |
792 | ba_resp->txed, ba_resp->txed_2_done); | ||
793 | /* | ||
794 | * set txed_2_done = txed, | ||
795 | * so it won't impact rate scale | ||
796 | */ | ||
797 | ba_resp->txed = ba_resp->txed_2_done; | ||
798 | } | ||
799 | IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n", | ||
800 | ba_resp->txed, ba_resp->txed_2_done); | ||
801 | 852 | ||
802 | /* Find the first ACKed frame to store the TX status */ | 853 | spin_lock_irqsave(&priv->shrd->sta_lock, flags); |
803 | while (sent_bitmap && !(sent_bitmap & 1)) { | ||
804 | agg->start_idx = (agg->start_idx + 1) & 0xff; | ||
805 | sent_bitmap >>= 1; | ||
806 | } | ||
807 | 854 | ||
808 | info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb); | 855 | if (txq->sched_retry) |
809 | memset(&info->status, 0, sizeof(info->status)); | 856 | iwl_rx_reply_tx_agg(priv, tx_resp); |
810 | info->flags |= IEEE80211_TX_STAT_ACK; | 857 | |
811 | info->flags |= IEEE80211_TX_STAT_AMPDU; | 858 | if (tx_resp->frame_count == 1) { |
812 | info->status.ampdu_ack_len = ba_resp->txed_2_done; | 859 | bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); |
813 | info->status.ampdu_len = ba_resp->txed; | 860 | |
814 | iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info); | 861 | __skb_queue_head_init(&skbs); |
862 | /*we can free until ssn % q.n_bd not inclusive */ | ||
863 | iwl_trans_reclaim(trans(priv), txq_id, ssn, status, &skbs); | ||
864 | freed = 0; | ||
865 | while (!skb_queue_empty(&skbs)) { | ||
866 | skb = __skb_dequeue(&skbs); | ||
867 | hdr = (struct ieee80211_hdr *)skb->data; | ||
868 | |||
869 | if (!ieee80211_is_data_qos(hdr->frame_control)) | ||
870 | priv->last_seq_ctl = tx_resp->seq_ctl; | ||
871 | |||
872 | info = IEEE80211_SKB_CB(skb); | ||
873 | ctx = info->driver_data[0]; | ||
874 | |||
875 | memset(&info->status, 0, sizeof(info->status)); | ||
876 | |||
877 | if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && | ||
878 | iwl_is_associated_ctx(ctx) && ctx->vif && | ||
879 | ctx->vif->type == NL80211_IFTYPE_STATION) { | ||
880 | ctx->last_tx_rejected = true; | ||
881 | iwl_stop_queue(priv, &priv->txq[txq_id]); | ||
882 | |||
883 | IWL_DEBUG_TX_REPLY(priv, | ||
884 | "TXQ %d status %s (0x%08x) " | ||
885 | "rate_n_flags 0x%x retries %d\n", | ||
886 | txq_id, | ||
887 | iwl_get_tx_fail_reason(status), | ||
888 | status, | ||
889 | le32_to_cpu(tx_resp->rate_n_flags), | ||
890 | tx_resp->failure_frame); | ||
891 | |||
892 | IWL_DEBUG_TX_REPLY(priv, | ||
893 | "FrameCnt = %d, idx=%d\n", | ||
894 | tx_resp->frame_count, cmd_index); | ||
895 | } | ||
896 | |||
897 | /* check if BAR is needed */ | ||
898 | if (is_agg && !iwl_is_tx_success(status)) | ||
899 | info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; | ||
900 | iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), | ||
901 | tx_resp, is_agg); | ||
902 | if (!is_agg) | ||
903 | iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); | ||
904 | |||
905 | ieee80211_tx_status_irqsafe(priv->hw, skb); | ||
906 | |||
907 | freed++; | ||
908 | } | ||
815 | 909 | ||
816 | return 0; | 910 | WARN_ON(!is_agg && freed != 1); |
817 | } | ||
818 | 911 | ||
819 | /** | 912 | iwl_free_tfds_in_queue(priv, sta_id, tid, freed); |
820 | * translate ucode response to mac80211 tx status control values | 913 | iwlagn_txq_check_empty(priv, sta_id, tid, txq_id); |
821 | */ | 914 | } |
822 | void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, | ||
823 | struct ieee80211_tx_info *info) | ||
824 | { | ||
825 | struct ieee80211_tx_rate *r = &info->control.rates[0]; | ||
826 | 915 | ||
827 | info->antenna_sel_tx = | 916 | iwl_check_abort_status(priv, tx_resp->frame_count, status); |
828 | ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); | 917 | spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); |
829 | if (rate_n_flags & RATE_MCS_HT_MSK) | ||
830 | r->flags |= IEEE80211_TX_RC_MCS; | ||
831 | if (rate_n_flags & RATE_MCS_GF_MSK) | ||
832 | r->flags |= IEEE80211_TX_RC_GREEN_FIELD; | ||
833 | if (rate_n_flags & RATE_MCS_HT40_MSK) | ||
834 | r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; | ||
835 | if (rate_n_flags & RATE_MCS_DUP_MSK) | ||
836 | r->flags |= IEEE80211_TX_RC_DUP_DATA; | ||
837 | if (rate_n_flags & RATE_MCS_SGI_MSK) | ||
838 | r->flags |= IEEE80211_TX_RC_SHORT_GI; | ||
839 | r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band); | ||
840 | } | 918 | } |
841 | 919 | ||
842 | /** | 920 | /** |
@@ -852,10 +930,15 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, | |||
852 | struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; | 930 | struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; |
853 | struct iwl_tx_queue *txq = NULL; | 931 | struct iwl_tx_queue *txq = NULL; |
854 | struct iwl_ht_agg *agg; | 932 | struct iwl_ht_agg *agg; |
933 | struct sk_buff_head reclaimed_skbs; | ||
934 | struct ieee80211_tx_info *info; | ||
935 | struct ieee80211_hdr *hdr; | ||
936 | struct sk_buff *skb; | ||
937 | unsigned long flags; | ||
855 | int index; | 938 | int index; |
856 | int sta_id; | 939 | int sta_id; |
857 | int tid; | 940 | int tid; |
858 | unsigned long flags; | 941 | int freed; |
859 | 942 | ||
860 | /* "flow" corresponds to Tx queue */ | 943 | /* "flow" corresponds to Tx queue */ |
861 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); | 944 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); |
@@ -874,6 +957,12 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, | |||
874 | sta_id = ba_resp->sta_id; | 957 | sta_id = ba_resp->sta_id; |
875 | tid = ba_resp->tid; | 958 | tid = ba_resp->tid; |
876 | agg = &priv->stations[sta_id].tid[tid].agg; | 959 | agg = &priv->stations[sta_id].tid[tid].agg; |
960 | |||
961 | /* Find index of block-ack window */ | ||
962 | index = ba_resp_scd_ssn & (txq->q.n_bd - 1); | ||
963 | |||
964 | spin_lock_irqsave(&priv->shrd->sta_lock, flags); | ||
965 | |||
877 | if (unlikely(agg->txq_id != scd_flow)) { | 966 | if (unlikely(agg->txq_id != scd_flow)) { |
878 | /* | 967 | /* |
879 | * FIXME: this is a uCode bug which need to be addressed, | 968 | * FIXME: this is a uCode bug which need to be addressed, |
@@ -884,88 +973,83 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, | |||
884 | IWL_DEBUG_TX_REPLY(priv, | 973 | IWL_DEBUG_TX_REPLY(priv, |
885 | "BA scd_flow %d does not match txq_id %d\n", | 974 | "BA scd_flow %d does not match txq_id %d\n", |
886 | scd_flow, agg->txq_id); | 975 | scd_flow, agg->txq_id); |
976 | spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); | ||
887 | return; | 977 | return; |
888 | } | 978 | } |
889 | 979 | ||
890 | /* Find index just before block-ack window */ | 980 | if (unlikely(!agg->wait_for_ba)) { |
891 | index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); | 981 | if (unlikely(ba_resp->bitmap)) |
892 | 982 | IWL_ERR(priv, "Received BA when not expected\n"); | |
893 | spin_lock_irqsave(&priv->shrd->sta_lock, flags); | 983 | spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); |
984 | return; | ||
985 | } | ||
894 | 986 | ||
895 | IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " | 987 | IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " |
896 | "sta_id = %d\n", | 988 | "sta_id = %d\n", |
897 | agg->wait_for_ba, | 989 | agg->wait_for_ba, |
898 | (u8 *) &ba_resp->sta_addr_lo32, | 990 | (u8 *) &ba_resp->sta_addr_lo32, |
899 | ba_resp->sta_id); | 991 | ba_resp->sta_id); |
900 | IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = " | 992 | IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, " |
901 | "%d, scd_ssn = %d\n", | 993 | "scd_flow = %d, scd_ssn = %d\n", |
902 | ba_resp->tid, | 994 | ba_resp->tid, |
903 | ba_resp->seq_ctl, | 995 | ba_resp->seq_ctl, |
904 | (unsigned long long)le64_to_cpu(ba_resp->bitmap), | 996 | (unsigned long long)le64_to_cpu(ba_resp->bitmap), |
905 | ba_resp->scd_flow, | 997 | ba_resp->scd_flow, |
906 | ba_resp->scd_ssn); | 998 | ba_resp->scd_ssn); |
907 | IWL_DEBUG_TX_REPLY(priv, "DAT start_idx = %d, bitmap = 0x%llx\n", | ||
908 | agg->start_idx, | ||
909 | (unsigned long long)agg->bitmap); | ||
910 | 999 | ||
911 | /* Update driver's record of ACK vs. not for each frame in window */ | 1000 | /* Mark that the expected block-ack response arrived */ |
912 | iwlagn_tx_status_reply_compressed_ba(priv, agg, ba_resp); | 1001 | agg->wait_for_ba = 0; |
1002 | |||
1003 | /* Sanity check values reported by uCode */ | ||
1004 | if (ba_resp->txed_2_done > ba_resp->txed) { | ||
1005 | IWL_DEBUG_TX_REPLY(priv, | ||
1006 | "bogus sent(%d) and ack(%d) count\n", | ||
1007 | ba_resp->txed, ba_resp->txed_2_done); | ||
1008 | /* | ||
1009 | * set txed_2_done = txed, | ||
1010 | * so it won't impact rate scale | ||
1011 | */ | ||
1012 | ba_resp->txed = ba_resp->txed_2_done; | ||
1013 | } | ||
1014 | IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n", | ||
1015 | ba_resp->txed, ba_resp->txed_2_done); | ||
1016 | |||
1017 | __skb_queue_head_init(&reclaimed_skbs); | ||
913 | 1018 | ||
914 | /* Release all TFDs before the SSN, i.e. all TFDs in front of | 1019 | /* Release all TFDs before the SSN, i.e. all TFDs in front of |
915 | * block-ack window (we assume that they've been successfully | 1020 | * block-ack window (we assume that they've been successfully |
916 | * transmitted ... if not, it's too late anyway). */ | 1021 | * transmitted ... if not, it's too late anyway). */ |
917 | if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) { | 1022 | iwl_trans_reclaim(trans(priv), scd_flow, ba_resp_scd_ssn, 0, |
918 | /* calculate mac80211 ampdu sw queue to wake */ | 1023 | &reclaimed_skbs); |
919 | int freed = iwlagn_tx_queue_reclaim(priv, scd_flow, index); | 1024 | freed = 0; |
920 | iwl_free_tfds_in_queue(priv, sta_id, tid, freed); | 1025 | while (!skb_queue_empty(&reclaimed_skbs)) { |
921 | |||
922 | if ((iwl_queue_space(&txq->q) > txq->q.low_mark) && | ||
923 | priv->mac80211_registered && | ||
924 | (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) | ||
925 | iwl_wake_queue(priv, txq); | ||
926 | 1026 | ||
927 | iwlagn_txq_check_empty(priv, sta_id, tid, scd_flow); | 1027 | skb = __skb_dequeue(&reclaimed_skbs); |
928 | } | 1028 | hdr = (struct ieee80211_hdr *)skb->data; |
929 | |||
930 | spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); | ||
931 | } | ||
932 | 1029 | ||
933 | #ifdef CONFIG_IWLWIFI_DEBUG | 1030 | if (ieee80211_is_data_qos(hdr->frame_control)) |
934 | const char *iwl_get_tx_fail_reason(u32 status) | 1031 | freed++; |
935 | { | 1032 | else |
936 | #define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x | 1033 | WARN_ON_ONCE(1); |
937 | #define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x | 1034 | |
1035 | if (freed == 0) { | ||
1036 | /* this is the first skb we deliver in this batch */ | ||
1037 | /* put the rate scaling data there */ | ||
1038 | info = IEEE80211_SKB_CB(skb); | ||
1039 | memset(&info->status, 0, sizeof(info->status)); | ||
1040 | info->flags |= IEEE80211_TX_STAT_ACK; | ||
1041 | info->flags |= IEEE80211_TX_STAT_AMPDU; | ||
1042 | info->status.ampdu_ack_len = ba_resp->txed_2_done; | ||
1043 | info->status.ampdu_len = ba_resp->txed; | ||
1044 | iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, | ||
1045 | info); | ||
1046 | } | ||
938 | 1047 | ||
939 | switch (status & TX_STATUS_MSK) { | 1048 | ieee80211_tx_status_irqsafe(priv->hw, skb); |
940 | case TX_STATUS_SUCCESS: | ||
941 | return "SUCCESS"; | ||
942 | TX_STATUS_POSTPONE(DELAY); | ||
943 | TX_STATUS_POSTPONE(FEW_BYTES); | ||
944 | TX_STATUS_POSTPONE(BT_PRIO); | ||
945 | TX_STATUS_POSTPONE(QUIET_PERIOD); | ||
946 | TX_STATUS_POSTPONE(CALC_TTAK); | ||
947 | TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); | ||
948 | TX_STATUS_FAIL(SHORT_LIMIT); | ||
949 | TX_STATUS_FAIL(LONG_LIMIT); | ||
950 | TX_STATUS_FAIL(FIFO_UNDERRUN); | ||
951 | TX_STATUS_FAIL(DRAIN_FLOW); | ||
952 | TX_STATUS_FAIL(RFKILL_FLUSH); | ||
953 | TX_STATUS_FAIL(LIFE_EXPIRE); | ||
954 | TX_STATUS_FAIL(DEST_PS); | ||
955 | TX_STATUS_FAIL(HOST_ABORTED); | ||
956 | TX_STATUS_FAIL(BT_RETRY); | ||
957 | TX_STATUS_FAIL(STA_INVALID); | ||
958 | TX_STATUS_FAIL(FRAG_DROPPED); | ||
959 | TX_STATUS_FAIL(TID_DISABLE); | ||
960 | TX_STATUS_FAIL(FIFO_FLUSHED); | ||
961 | TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); | ||
962 | TX_STATUS_FAIL(PASSIVE_NO_RX); | ||
963 | TX_STATUS_FAIL(NO_BEACON_ON_RADAR); | ||
964 | } | 1049 | } |
965 | 1050 | ||
966 | return "UNKNOWN"; | 1051 | iwl_free_tfds_in_queue(priv, sta_id, tid, freed); |
1052 | iwlagn_txq_check_empty(priv, sta_id, tid, scd_flow); | ||
967 | 1053 | ||
968 | #undef TX_STATUS_FAIL | 1054 | spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); |
969 | #undef TX_STATUS_POSTPONE | ||
970 | } | 1055 | } |
971 | #endif /* CONFIG_IWLWIFI_DEBUG */ | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index d2fa77adbf22..f34590765074 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h | |||
@@ -146,6 +146,11 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv, | |||
146 | enum iwlagn_ucode_type ucode_type); | 146 | enum iwlagn_ucode_type ucode_type); |
147 | 147 | ||
148 | /* lib */ | 148 | /* lib */ |
149 | void iwlagn_set_tx_status(struct iwl_priv *priv, | ||
150 | struct ieee80211_tx_info *info, | ||
151 | struct iwlagn_tx_resp *tx_resp, | ||
152 | bool is_agg); | ||
153 | void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status); | ||
149 | void iwl_check_abort_status(struct iwl_priv *priv, | 154 | void iwl_check_abort_status(struct iwl_priv *priv, |
150 | u8 frame_count, u32 status); | 155 | u8 frame_count, u32 status); |
151 | int iwlagn_hw_valid_rtc_data_addr(u32 addr); | 156 | int iwlagn_hw_valid_rtc_data_addr(u32 addr); |
@@ -178,7 +183,8 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv, | |||
178 | void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, | 183 | void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, |
179 | struct iwl_rx_mem_buffer *rxb); | 184 | struct iwl_rx_mem_buffer *rxb); |
180 | void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); | 185 | void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); |
181 | int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index); | 186 | void iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, |
187 | struct sk_buff_head *skbs); | ||
182 | 188 | ||
183 | static inline u32 iwl_tx_status_to_mac80211(u32 status) | 189 | static inline u32 iwl_tx_status_to_mac80211(u32 status) |
184 | { | 190 | { |
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index f3852edaccf5..022cfc738903 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h | |||
@@ -166,6 +166,8 @@ struct iwl_tx_info { | |||
166 | * @time_stamp: time (in jiffies) of last read_ptr change | 166 | * @time_stamp: time (in jiffies) of last read_ptr change |
167 | * @need_update: indicates need to update read/write index | 167 | * @need_update: indicates need to update read/write index |
168 | * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled | 168 | * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled |
169 | * @sta_id: valid if sched_retry is set | ||
170 | * @tid: valid if sched_retry is set | ||
169 | * | 171 | * |
170 | * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame | 172 | * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame |
171 | * descriptors) and required locking structures. | 173 | * descriptors) and required locking structures. |
@@ -184,6 +186,9 @@ struct iwl_tx_queue { | |||
184 | u8 sched_retry; | 186 | u8 sched_retry; |
185 | u8 active; | 187 | u8 active; |
186 | u8 swq_id; | 188 | u8 swq_id; |
189 | |||
190 | u16 sta_id; | ||
191 | u16 tid; | ||
187 | }; | 192 | }; |
188 | 193 | ||
189 | #define IWL_NUM_SCAN_RATES (2) | 194 | #define IWL_NUM_SCAN_RATES (2) |
diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index 09ed7af19889..f744fdc39a4d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h | |||
@@ -90,6 +90,9 @@ static inline void iwl_wake_queue(struct iwl_priv *priv, | |||
90 | u8 ac = queue & 3; | 90 | u8 ac = queue & 3; |
91 | u8 hwq = (queue >> 2) & 0x1f; | 91 | u8 hwq = (queue >> 2) & 0x1f; |
92 | 92 | ||
93 | if (unlikely(!priv->mac80211_registered)) | ||
94 | return; | ||
95 | |||
93 | if (test_and_clear_bit(hwq, priv->queue_stopped)) | 96 | if (test_and_clear_bit(hwq, priv->queue_stopped)) |
94 | if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0) | 97 | if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0) |
95 | ieee80211_wake_queue(priv->hw, ac); | 98 | ieee80211_wake_queue(priv->hw, ac); |
@@ -102,6 +105,9 @@ static inline void iwl_stop_queue(struct iwl_priv *priv, | |||
102 | u8 ac = queue & 3; | 105 | u8 ac = queue & 3; |
103 | u8 hwq = (queue >> 2) & 0x1f; | 106 | u8 hwq = (queue >> 2) & 0x1f; |
104 | 107 | ||
108 | if (unlikely(!priv->mac80211_registered)) | ||
109 | return; | ||
110 | |||
105 | if (!test_and_set_bit(hwq, priv->queue_stopped)) | 111 | if (!test_and_set_bit(hwq, priv->queue_stopped)) |
106 | if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0) | 112 | if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0) |
107 | ieee80211_stop_queue(priv->hw, ac); | 113 | ieee80211_stop_queue(priv->hw, ac); |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c index 9d7287e7a992..7438aaedbd5c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c | |||
@@ -480,6 +480,9 @@ void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv, int sta_id, int tid, | |||
480 | /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ | 480 | /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ |
481 | iwl_trans_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1); | 481 | iwl_trans_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1); |
482 | 482 | ||
483 | priv->txq[txq_id].sta_id = sta_id; | ||
484 | priv->txq[txq_id].tid = tid; | ||
485 | |||
483 | spin_unlock_irqrestore(&priv->shrd->lock, flags); | 486 | spin_unlock_irqrestore(&priv->shrd->lock, flags); |
484 | } | 487 | } |
485 | 488 | ||
@@ -1035,3 +1038,55 @@ int iwl_trans_pcie_send_cmd_pdu(struct iwl_priv *priv, u8 id, u32 flags, | |||
1035 | 1038 | ||
1036 | return iwl_trans_pcie_send_cmd(priv, &cmd); | 1039 | return iwl_trans_pcie_send_cmd(priv, &cmd); |
1037 | } | 1040 | } |
1041 | |||
1042 | /* Frees buffers until index _not_ inclusive */ | ||
1043 | void iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, | ||
1044 | struct sk_buff_head *skbs) | ||
1045 | |||
1046 | { | ||
1047 | struct iwl_tx_queue *txq = &priv(trans)->txq[txq_id]; | ||
1048 | struct iwl_queue *q = &txq->q; | ||
1049 | struct iwl_tx_info *tx_info; | ||
1050 | struct ieee80211_tx_info *info; | ||
1051 | int last_to_free; | ||
1052 | |||
1053 | /*Since we free until index _not_ inclusive, the one before index is | ||
1054 | * the last we will free. This one must be used */ | ||
1055 | last_to_free = iwl_queue_dec_wrap(index, q->n_bd); | ||
1056 | |||
1057 | if ((index >= q->n_bd) || | ||
1058 | (iwl_queue_used(q, last_to_free) == 0)) { | ||
1059 | IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), " | ||
1060 | "last_to_free %d is out of range [0-%d] %d %d.\n", | ||
1061 | __func__, txq_id, last_to_free, q->n_bd, | ||
1062 | q->write_ptr, q->read_ptr); | ||
1063 | return; | ||
1064 | } | ||
1065 | |||
1066 | IWL_DEBUG_TX_REPLY(trans, "reclaim: [%d, %d, %d]\n", txq_id, | ||
1067 | q->read_ptr, index); | ||
1068 | |||
1069 | if (WARN_ON(!skb_queue_empty(skbs))) | ||
1070 | return; | ||
1071 | |||
1072 | for (; | ||
1073 | q->read_ptr != index; | ||
1074 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { | ||
1075 | |||
1076 | tx_info = &txq->txb[txq->q.read_ptr]; | ||
1077 | |||
1078 | if (WARN_ON_ONCE(tx_info->skb == NULL)) | ||
1079 | continue; | ||
1080 | |||
1081 | info = IEEE80211_SKB_CB(tx_info->skb); | ||
1082 | info->driver_data[0] = tx_info->ctx; | ||
1083 | |||
1084 | __skb_queue_tail(skbs, tx_info->skb); | ||
1085 | |||
1086 | tx_info->skb = NULL; | ||
1087 | |||
1088 | iwlagn_txq_inval_byte_cnt_tbl(priv(trans), txq); | ||
1089 | |||
1090 | iwlagn_txq_free_tfd(priv(trans), txq, txq->q.read_ptr); | ||
1091 | } | ||
1092 | } | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c index 63a310135f76..06cec8268c6c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans.c | |||
@@ -1106,7 +1106,7 @@ static int iwl_trans_pcie_tx(struct iwl_priv *priv, struct sk_buff *skb, | |||
1106 | * regardless of the value of ret. "ret" only indicates | 1106 | * regardless of the value of ret. "ret" only indicates |
1107 | * whether or not we should update the write pointer. | 1107 | * whether or not we should update the write pointer. |
1108 | */ | 1108 | */ |
1109 | if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { | 1109 | if (iwl_queue_space(q) < q->high_mark) { |
1110 | if (wait_write_ptr) { | 1110 | if (wait_write_ptr) { |
1111 | txq->need_update = 1; | 1111 | txq->need_update = 1; |
1112 | iwl_txq_update_write_ptr(priv, txq); | 1112 | iwl_txq_update_write_ptr(priv, txq); |
@@ -1148,6 +1148,34 @@ static int iwl_trans_pcie_request_irq(struct iwl_trans *trans) | |||
1148 | return 0; | 1148 | return 0; |
1149 | } | 1149 | } |
1150 | 1150 | ||
1151 | static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, | ||
1152 | int ssn, u32 status, struct sk_buff_head *skbs) | ||
1153 | { | ||
1154 | struct iwl_priv *priv = priv(trans); | ||
1155 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | ||
1156 | /* n_bd is usually 256 => n_bd - 1 = 0xff */ | ||
1157 | int tfd_num = ssn & (txq->q.n_bd - 1); | ||
1158 | u8 agg_state; | ||
1159 | bool cond; | ||
1160 | |||
1161 | if (txq->sched_retry) { | ||
1162 | agg_state = | ||
1163 | priv->stations[txq->sta_id].tid[txq->tid].agg.state; | ||
1164 | cond = (agg_state != IWL_EMPTYING_HW_QUEUE_DELBA); | ||
1165 | } else { | ||
1166 | cond = (status != TX_STATUS_FAIL_PASSIVE_NO_RX); | ||
1167 | } | ||
1168 | |||
1169 | if (txq->q.read_ptr != tfd_num) { | ||
1170 | IWL_DEBUG_TX_REPLY(trans, "Retry scheduler reclaim " | ||
1171 | "scd_ssn=%d idx=%d txq=%d swq=%d\n", | ||
1172 | ssn , tfd_num, txq_id, txq->swq_id); | ||
1173 | iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); | ||
1174 | if (iwl_queue_space(&txq->q) > txq->q.low_mark && cond) | ||
1175 | iwl_wake_queue(priv, txq); | ||
1176 | } | ||
1177 | } | ||
1178 | |||
1151 | static void iwl_trans_pcie_disable_sync_irq(struct iwl_trans *trans) | 1179 | static void iwl_trans_pcie_disable_sync_irq(struct iwl_trans *trans) |
1152 | { | 1180 | { |
1153 | unsigned long flags; | 1181 | unsigned long flags; |
@@ -1626,6 +1654,7 @@ const struct iwl_trans_ops trans_ops_pcie = { | |||
1626 | 1654 | ||
1627 | .get_tx_cmd = iwl_trans_pcie_get_tx_cmd, | 1655 | .get_tx_cmd = iwl_trans_pcie_get_tx_cmd, |
1628 | .tx = iwl_trans_pcie_tx, | 1656 | .tx = iwl_trans_pcie_tx, |
1657 | .reclaim = iwl_trans_pcie_reclaim, | ||
1629 | 1658 | ||
1630 | .txq_agg_disable = iwl_trans_pcie_txq_agg_disable, | 1659 | .txq_agg_disable = iwl_trans_pcie_txq_agg_disable, |
1631 | .txq_agg_setup = iwl_trans_pcie_txq_agg_setup, | 1660 | .txq_agg_setup = iwl_trans_pcie_txq_agg_setup, |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index b4c7166a70e3..519ad91bda1c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h | |||
@@ -90,6 +90,7 @@ struct iwl_shared; | |||
90 | * @send_cmd_pdu:send a host command: flags can be CMD_* | 90 | * @send_cmd_pdu:send a host command: flags can be CMD_* |
91 | * @get_tx_cmd: returns a pointer to a new Tx cmd for the upper layer use | 91 | * @get_tx_cmd: returns a pointer to a new Tx cmd for the upper layer use |
92 | * @tx: send an skb | 92 | * @tx: send an skb |
93 | * @reclaim: free packet until ssn. Returns a list of freed packets. | ||
93 | * @txq_agg_setup: setup a tx queue for AMPDU - will be called once the HW is | 94 | * @txq_agg_setup: setup a tx queue for AMPDU - will be called once the HW is |
94 | * ready and a successful ADDBA response has been received. | 95 | * ready and a successful ADDBA response has been received. |
95 | * @txq_agg_disable: de-configure a Tx queue to send AMPDUs | 96 | * @txq_agg_disable: de-configure a Tx queue to send AMPDUs |
@@ -123,6 +124,8 @@ struct iwl_trans_ops { | |||
123 | int (*tx)(struct iwl_priv *priv, struct sk_buff *skb, | 124 | int (*tx)(struct iwl_priv *priv, struct sk_buff *skb, |
124 | struct iwl_tx_cmd *tx_cmd, int txq_id, __le16 fc, bool ampdu, | 125 | struct iwl_tx_cmd *tx_cmd, int txq_id, __le16 fc, bool ampdu, |
125 | struct iwl_rxon_context *ctx); | 126 | struct iwl_rxon_context *ctx); |
127 | void (*reclaim)(struct iwl_trans *trans, int txq_id, int ssn, | ||
128 | u32 status, struct sk_buff_head *skbs); | ||
126 | 129 | ||
127 | int (*txq_agg_disable)(struct iwl_priv *priv, u16 txq_id, | 130 | int (*txq_agg_disable)(struct iwl_priv *priv, u16 txq_id, |
128 | u16 ssn_idx, u8 tx_fifo); | 131 | u16 ssn_idx, u8 tx_fifo); |
@@ -213,6 +216,13 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, | |||
213 | return trans->ops->tx(priv(trans), skb, tx_cmd, txq_id, fc, ampdu, ctx); | 216 | return trans->ops->tx(priv(trans), skb, tx_cmd, txq_id, fc, ampdu, ctx); |
214 | } | 217 | } |
215 | 218 | ||
219 | static inline void iwl_trans_reclaim(struct iwl_trans *trans, int txq_id, | ||
220 | int ssn, u32 status, | ||
221 | struct sk_buff_head *skbs) | ||
222 | { | ||
223 | trans->ops->reclaim(trans, txq_id, ssn, status, skbs); | ||
224 | } | ||
225 | |||
216 | static inline int iwl_trans_txq_agg_disable(struct iwl_trans *trans, u16 txq_id, | 226 | static inline int iwl_trans_txq_agg_disable(struct iwl_trans *trans, u16 txq_id, |
217 | u16 ssn_idx, u8 tx_fifo) | 227 | u16 ssn_idx, u8 tx_fifo) |
218 | { | 228 | { |