aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c88
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c86
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h6
5 files changed, 87 insertions, 99 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 69d0f997298..a31cdef8484 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -395,6 +395,77 @@ drop_unlock_priv:
395 return -1; 395 return -1;
396} 396}
397 397
398int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
399 struct ieee80211_sta *sta, u16 tid)
400{
401 struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
402 struct iwl_tid_data *tid_data;
403 unsigned long flags;
404 int sta_id;
405
406 sta_id = iwl_sta_id(sta);
407
408 if (sta_id == IWL_INVALID_STATION) {
409 IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
410 return -ENXIO;
411 }
412
413 spin_lock_irqsave(&priv->shrd->sta_lock, flags);
414
415 tid_data = &priv->shrd->tid_data[sta_id][tid];
416
417 switch (priv->shrd->tid_data[sta_id][tid].agg.state) {
418 case IWL_EMPTYING_HW_QUEUE_ADDBA:
419 /*
420 * This can happen if the peer stops aggregation
421 * again before we've had a chance to drain the
422 * queue we selected previously, i.e. before the
423 * session was really started completely.
424 */
425 IWL_DEBUG_HT(priv, "AGG stop before setup done\n");
426 goto turn_off;
427 case IWL_AGG_ON:
428 break;
429 default:
430 IWL_WARN(priv, "Stopping AGG while state not ON "
431 "or starting for %d on %d (%d)\n", sta_id, tid,
432 priv->shrd->tid_data[sta_id][tid].agg.state);
433 spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
434 return 0;
435 }
436
437 tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
438
439 /* There are still packets for this RA / TID in the HW */
440 if (tid_data->agg.ssn != tid_data->next_reclaimed) {
441 IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
442 "next_recl = %d",
443 tid_data->agg.ssn,
444 tid_data->next_reclaimed);
445 priv->shrd->tid_data[sta_id][tid].agg.state =
446 IWL_EMPTYING_HW_QUEUE_DELBA;
447 spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
448 return 0;
449 }
450
451 IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d",
452 tid_data->agg.ssn);
453turn_off:
454 priv->shrd->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF;
455
456 /* do not restore/save irqs */
457 spin_unlock(&priv->shrd->sta_lock);
458 spin_lock(&priv->shrd->lock);
459
460 iwl_trans_tx_agg_disable(trans(priv), sta_id, tid);
461
462 spin_unlock_irqrestore(&priv->shrd->lock, flags);
463
464 iwl_stop_tx_ba_trans_ready(priv, vif_priv->ctx->ctxid, sta_id, tid);
465
466 return 0;
467}
468
398int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, 469int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
399 struct ieee80211_sta *sta, u16 tid, u16 *ssn) 470 struct ieee80211_sta *sta, u16 tid, u16 *ssn)
400{ 471{
@@ -428,23 +499,6 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
428 return ret; 499 return ret;
429} 500}
430 501
431int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
432 struct ieee80211_sta *sta, u16 tid)
433{
434 int sta_id;
435 struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
436
437 sta_id = iwl_sta_id(sta);
438
439 if (sta_id == IWL_INVALID_STATION) {
440 IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
441 return -ENXIO;
442 }
443
444 return iwl_trans_tx_agg_disable(trans(priv), vif_priv->ctx->ctxid,
445 sta_id, tid);
446}
447
448static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, 502static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
449 struct iwl_rxon_context *ctx, 503 struct iwl_rxon_context *ctx,
450 const u8 *addr1) 504 const u8 *addr1)
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
index 5a384b309b0..342ee2df213 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
@@ -280,10 +280,8 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans,
280void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, 280void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
281 struct iwl_tx_queue *txq, 281 struct iwl_tx_queue *txq,
282 u16 byte_cnt); 282 u16 byte_cnt);
283void iwl_trans_pcie_txq_agg_disable(struct iwl_trans *trans, int txq_id);
284int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, 283int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans,
285 enum iwl_rxon_context_id ctx, int sta_id, 284 int sta_id, int tid);
286 int tid);
287void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index); 285void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index);
288void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, 286void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
289 struct iwl_tx_queue *txq, 287 struct iwl_tx_queue *txq,
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
index 58ee0ac5706..4ee5f50643f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
@@ -578,35 +578,11 @@ int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans,
578 return 0; 578 return 0;
579} 579}
580 580
581void iwl_trans_pcie_txq_agg_disable(struct iwl_trans *trans, int txq_id) 581int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int sta_id, int tid)
582{ 582{
583 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 583 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
584 iwlagn_tx_queue_stop_scheduler(trans, txq_id); 584 /* TODO: the transport layer shouldn't access the tid_data */
585 585 int txq_id = trans->shrd->tid_data[sta_id][tid].agg.txq_id;
586 iwl_clear_bits_prph(bus(trans), SCD_AGGR_SEL, (1 << txq_id));
587
588 trans_pcie->txq[txq_id].q.read_ptr = 0;
589 trans_pcie->txq[txq_id].q.write_ptr = 0;
590 /* supposes that ssn_idx is valid (!= 0xFFF) */
591 iwl_trans_set_wr_ptrs(trans, txq_id, 0);
592
593 iwl_clear_bits_prph(bus(trans), SCD_INTERRUPT_MASK, (1 << txq_id));
594 iwl_txq_ctx_deactivate(trans_pcie, txq_id);
595 iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], 0, 0);
596}
597
598int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans,
599 enum iwl_rxon_context_id ctx, int sta_id,
600 int tid)
601{
602 struct iwl_tid_data *tid_data;
603 unsigned long flags;
604 int txq_id;
605
606 spin_lock_irqsave(&trans->shrd->sta_lock, flags);
607
608 tid_data = &trans->shrd->tid_data[sta_id][tid];
609 txq_id = tid_data->agg.txq_id;
610 586
611 if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) || 587 if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
612 (IWLAGN_FIRST_AMPDU_QUEUE + 588 (IWLAGN_FIRST_AMPDU_QUEUE +
@@ -616,59 +592,21 @@ int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans,
616 txq_id, IWLAGN_FIRST_AMPDU_QUEUE, 592 txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
617 IWLAGN_FIRST_AMPDU_QUEUE + 593 IWLAGN_FIRST_AMPDU_QUEUE +
618 hw_params(trans).num_ampdu_queues - 1); 594 hw_params(trans).num_ampdu_queues - 1);
619 spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
620 return -EINVAL; 595 return -EINVAL;
621 } 596 }
622 597
623 switch (trans->shrd->tid_data[sta_id][tid].agg.state) { 598 iwlagn_tx_queue_stop_scheduler(trans, txq_id);
624 case IWL_EMPTYING_HW_QUEUE_ADDBA:
625 /*
626 * This can happen if the peer stops aggregation
627 * again before we've had a chance to drain the
628 * queue we selected previously, i.e. before the
629 * session was really started completely.
630 */
631 IWL_DEBUG_HT(trans, "AGG stop before setup done\n");
632 goto turn_off;
633 case IWL_AGG_ON:
634 break;
635 default:
636 IWL_WARN(trans, "Stopping AGG while state not ON "
637 "or starting for %d on %d (%d)\n", sta_id, tid,
638 trans->shrd->tid_data[sta_id][tid].agg.state);
639 spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
640 return 0;
641 }
642
643 tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
644
645 /* There are still packets for this RA / TID in the HW */
646 if (tid_data->agg.ssn != tid_data->next_reclaimed) {
647 IWL_DEBUG_TX_QUEUES(trans, "Can't proceed: ssn %d, "
648 "next_recl = %d",
649 tid_data->agg.ssn,
650 tid_data->next_reclaimed);
651 trans->shrd->tid_data[sta_id][tid].agg.state =
652 IWL_EMPTYING_HW_QUEUE_DELBA;
653 spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
654 return 0;
655 }
656
657 IWL_DEBUG_TX_QUEUES(trans, "Can proceed: ssn = next_recl = %d",
658 tid_data->agg.ssn);
659turn_off:
660 trans->shrd->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF;
661
662 /* do not restore/save irqs */
663 spin_unlock(&trans->shrd->sta_lock);
664 spin_lock(&trans->shrd->lock);
665
666 iwl_trans_pcie_txq_agg_disable(trans, txq_id);
667 599
668 spin_unlock_irqrestore(&trans->shrd->lock, flags); 600 iwl_clear_bits_prph(bus(trans), SCD_AGGR_SEL, (1 << txq_id));
669 601
670 iwl_stop_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid); 602 trans_pcie->txq[txq_id].q.read_ptr = 0;
603 trans_pcie->txq[txq_id].q.write_ptr = 0;
604 /* supposes that ssn_idx is valid (!= 0xFFF) */
605 iwl_trans_set_wr_ptrs(trans, txq_id, 0);
671 606
607 iwl_clear_bits_prph(bus(trans), SCD_INTERRUPT_MASK, (1 << txq_id));
608 iwl_txq_ctx_deactivate(trans_pcie, txq_id);
609 iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], 0, 0);
672 return 0; 610 return 0;
673} 611}
674 612
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
index ef057ff219e..15bee2b97c0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
@@ -1290,7 +1290,7 @@ static int iwlagn_txq_check_empty(struct iwl_trans *trans,
1290 IWL_DEBUG_TX_QUEUES(trans, 1290 IWL_DEBUG_TX_QUEUES(trans,
1291 "Can continue DELBA flow ssn = next_recl =" 1291 "Can continue DELBA flow ssn = next_recl ="
1292 " %d", tid_data->next_reclaimed); 1292 " %d", tid_data->next_reclaimed);
1293 iwl_trans_pcie_txq_agg_disable(trans, txq_id); 1293 iwl_trans_pcie_tx_agg_disable(trans, sta_id, tid);
1294 tid_data->agg.state = IWL_AGG_OFF; 1294 tid_data->agg.state = IWL_AGG_OFF;
1295 iwl_stop_tx_ba_trans_ready(priv(trans), 1295 iwl_stop_tx_ba_trans_ready(priv(trans),
1296 NUM_IWL_RXON_CTX, 1296 NUM_IWL_RXON_CTX,
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index f94a6ee5f82..32c1deb3f7b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -184,8 +184,7 @@ struct iwl_trans_ops {
184 struct sk_buff_head *skbs); 184 struct sk_buff_head *skbs);
185 185
186 int (*tx_agg_disable)(struct iwl_trans *trans, 186 int (*tx_agg_disable)(struct iwl_trans *trans,
187 enum iwl_rxon_context_id ctx, int sta_id, 187 int sta_id, int tid);
188 int tid);
189 int (*tx_agg_alloc)(struct iwl_trans *trans, 188 int (*tx_agg_alloc)(struct iwl_trans *trans,
190 enum iwl_rxon_context_id ctx, int sta_id, int tid, 189 enum iwl_rxon_context_id ctx, int sta_id, int tid,
191 u16 *ssn); 190 u16 *ssn);
@@ -318,10 +317,9 @@ static inline void iwl_trans_reclaim(struct iwl_trans *trans, int sta_id,
318} 317}
319 318
320static inline int iwl_trans_tx_agg_disable(struct iwl_trans *trans, 319static inline int iwl_trans_tx_agg_disable(struct iwl_trans *trans,
321 enum iwl_rxon_context_id ctx,
322 int sta_id, int tid) 320 int sta_id, int tid)
323{ 321{
324 return trans->ops->tx_agg_disable(trans, ctx, sta_id, tid); 322 return trans->ops->tx_agg_disable(trans, sta_id, tid);
325} 323}
326 324
327static inline int iwl_trans_tx_agg_alloc(struct iwl_trans *trans, 325static inline int iwl_trans_tx_agg_alloc(struct iwl_trans *trans,