aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2011-08-26 02:11:25 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-08-29 15:30:32 -0400
commit288712a6ccf47b9df104f800616f6659ecadc940 (patch)
tree5d2d15b7f53db59dc76a3753eb2db3c92c981f18 /drivers/net/wireless/iwlwifi
parente13c0c59e0ec38558ac853d56555e915b4dc7dc2 (diff)
iwlagn: allocate resources for TX BA session in transport
The queues and all the related logic suits to the transport layer. 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-tx.c77
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-shared.h12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c53
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h12
7 files changed, 93 insertions, 73 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 32e39059b9bf..e91a0ee1189b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -42,33 +42,6 @@
42#include "iwl-agn.h" 42#include "iwl-agn.h"
43#include "iwl-trans.h" 43#include "iwl-trans.h"
44 44
45static inline int get_ac_from_tid(u16 tid)
46{
47 if (likely(tid < ARRAY_SIZE(tid_to_ac)))
48 return tid_to_ac[tid];
49
50 /* no support for TIDs 8-15 yet */
51 return -EINVAL;
52}
53
54static int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, int sta_id,
55 int tid)
56{
57 if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
58 (IWLAGN_FIRST_AMPDU_QUEUE +
59 hw_params(priv).num_ampdu_queues <= txq_id)) {
60 IWL_WARN(priv,
61 "queue number out of range: %d, must be %d to %d\n",
62 txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
63 IWLAGN_FIRST_AMPDU_QUEUE +
64 hw_params(priv).num_ampdu_queues - 1);
65 return -EINVAL;
66 }
67
68 /* Modify device's station table to Tx this TID */
69 return iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
70}
71
72static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, 45static void iwlagn_tx_cmd_protection(struct iwl_priv *priv,
73 struct ieee80211_tx_info *info, 46 struct ieee80211_tx_info *info,
74 __le16 fc, __le32 *tx_flags) 47 __le16 fc, __le32 *tx_flags)
@@ -399,30 +372,12 @@ drop_unlock_priv:
399 return -1; 372 return -1;
400} 373}
401 374
402/*
403 * Find first available (lowest unused) Tx Queue, mark it "active".
404 * Called only when finding queue for aggregation.
405 * Should never return anything < 7, because they should already
406 * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
407 */
408static int iwlagn_txq_ctx_activate_free(struct iwl_priv *priv)
409{
410 int txq_id;
411
412 for (txq_id = 0; txq_id < hw_params(priv).max_txq_num; txq_id++)
413 if (!test_and_set_bit(txq_id, &priv->txq_ctx_active_msk))
414 return txq_id;
415 return -1;
416}
417
418int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, 375int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
419 struct ieee80211_sta *sta, u16 tid, u16 *ssn) 376 struct ieee80211_sta *sta, u16 tid, u16 *ssn)
420{ 377{
378 struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
421 int sta_id; 379 int sta_id;
422 int txq_id;
423 int ret; 380 int ret;
424 unsigned long flags;
425 struct iwl_tid_data *tid_data;
426 381
427 IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n", 382 IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n",
428 sta->addr, tid); 383 sta->addr, tid);
@@ -440,35 +395,13 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
440 return -ENXIO; 395 return -ENXIO;
441 } 396 }
442 397
443 txq_id = iwlagn_txq_ctx_activate_free(priv); 398 ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
444 if (txq_id == -1) {
445 IWL_ERR(priv, "No free aggregation queue available\n");
446 return -ENXIO;
447 }
448
449 spin_lock_irqsave(&priv->shrd->sta_lock, flags);
450 tid_data = &priv->shrd->tid_data[sta_id][tid];
451 *ssn = SEQ_TO_SN(tid_data->seq_number);
452 tid_data->agg.txq_id = txq_id;
453 iwl_set_swq_id(&priv->txq[txq_id], get_ac_from_tid(tid), txq_id);
454 spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
455
456 ret = iwlagn_txq_agg_enable(priv, txq_id, sta_id, tid);
457 if (ret) 399 if (ret)
458 return ret; 400 return ret;
459 401
460 spin_lock_irqsave(&priv->shrd->sta_lock, flags); 402 ret = iwl_trans_tx_agg_alloc(trans(priv), vif_priv->ctx->ctxid, sta_id,
461 tid_data = &priv->shrd->tid_data[sta_id][tid]; 403 tid, ssn);
462 if (tid_data->tfds_in_queue == 0) { 404
463 IWL_DEBUG_HT(priv, "HW queue is empty\n");
464 tid_data->agg.state = IWL_AGG_ON;
465 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
466 } else {
467 IWL_DEBUG_HT(priv, "HW queue is NOT empty: %d packets in HW queue\n",
468 tid_data->tfds_in_queue);
469 tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
470 }
471 spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
472 return ret; 405 return ret;
473} 406}
474 407
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 411edc8f3f9d..38a3c3187ea3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -1858,3 +1858,11 @@ __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base,
1858 return cpu_to_le32(res); 1858 return cpu_to_le32(res);
1859} 1859}
1860 1860
1861void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, u8 ctx,
1862 u8 sta_id, u8 tid)
1863{
1864 struct ieee80211_vif *vif = priv->contexts[ctx].vif;
1865 u8 *addr = priv->stations[sta_id].sta.sta.addr;
1866
1867 ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
1868}
diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h
index 4cfa31e2529d..a2be28a925ee 100644
--- a/drivers/net/wireless/iwlwifi/iwl-shared.h
+++ b/drivers/net/wireless/iwlwifi/iwl-shared.h
@@ -321,6 +321,15 @@ static const u8 tid_to_ac[] = {
321 IEEE80211_AC_VO 321 IEEE80211_AC_VO
322}; 322};
323 323
324static inline int get_ac_from_tid(u16 tid)
325{
326 if (likely(tid < ARRAY_SIZE(tid_to_ac)))
327 return tid_to_ac[tid];
328
329 /* no support for TIDs 8-15 yet */
330 return -EINVAL;
331}
332
324enum iwl_rxon_context_id { 333enum iwl_rxon_context_id {
325 IWL_RXON_CTX_BSS, 334 IWL_RXON_CTX_BSS,
326 IWL_RXON_CTX_PAN, 335 IWL_RXON_CTX_PAN,
@@ -337,6 +346,9 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops,
337 struct iwl_cfg *cfg); 346 struct iwl_cfg *cfg);
338void __devexit iwl_remove(struct iwl_priv * priv); 347void __devexit iwl_remove(struct iwl_priv * priv);
339 348
349void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, u8 ctx,
350 u8 sta_id, u8 tid);
351
340/***************************************************** 352/*****************************************************
341* DRIVER STATUS FUNCTIONS 353* DRIVER STATUS FUNCTIONS
342******************************************************/ 354******************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h b/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
index d73ebefa7d05..ece9408262b1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
@@ -194,6 +194,9 @@ void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index);
194void iwl_trans_tx_queue_set_status(struct iwl_priv *priv, 194void iwl_trans_tx_queue_set_status(struct iwl_priv *priv,
195 struct iwl_tx_queue *txq, 195 struct iwl_tx_queue *txq,
196 int tx_fifo_id, int scd_retry); 196 int tx_fifo_id, int scd_retry);
197int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans,
198 enum iwl_rxon_context_id ctx, int sta_id,
199 int tid, u16 *ssn);
197void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv, 200void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv,
198 enum iwl_rxon_context_id ctx, 201 enum iwl_rxon_context_id ctx,
199 int sta_id, int tid, int frame_limit); 202 int sta_id, int tid, int frame_limit);
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
index 6af33104228a..93922265feb3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
@@ -29,7 +29,6 @@
29#include <linux/etherdevice.h> 29#include <linux/etherdevice.h>
30#include <linux/slab.h> 30#include <linux/slab.h>
31#include <linux/sched.h> 31#include <linux/sched.h>
32#include <net/mac80211.h>
33 32
34#include "iwl-agn.h" 33#include "iwl-agn.h"
35#include "iwl-dev.h" 34#include "iwl-dev.h"
@@ -509,6 +508,58 @@ void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv,
509 spin_unlock_irqrestore(&priv->shrd->lock, flags); 508 spin_unlock_irqrestore(&priv->shrd->lock, flags);
510} 509}
511 510
511/*
512 * Find first available (lowest unused) Tx Queue, mark it "active".
513 * Called only when finding queue for aggregation.
514 * Should never return anything < 7, because they should already
515 * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
516 */
517static int iwlagn_txq_ctx_activate_free(struct iwl_trans *trans)
518{
519 int txq_id;
520
521 for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++)
522 if (!test_and_set_bit(txq_id,
523 &priv(trans)->txq_ctx_active_msk))
524 return txq_id;
525 return -1;
526}
527
528int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans,
529 enum iwl_rxon_context_id ctx, int sta_id,
530 int tid, u16 *ssn)
531{
532 struct iwl_tid_data *tid_data;
533 unsigned long flags;
534 u16 txq_id;
535 struct iwl_priv *priv = priv(trans);
536
537 txq_id = iwlagn_txq_ctx_activate_free(trans);
538 if (txq_id == -1) {
539 IWL_ERR(trans, "No free aggregation queue available\n");
540 return -ENXIO;
541 }
542
543 spin_lock_irqsave(&trans->shrd->sta_lock, flags);
544 tid_data = &trans->shrd->tid_data[sta_id][tid];
545 *ssn = SEQ_TO_SN(tid_data->seq_number);
546 tid_data->agg.txq_id = txq_id;
547 iwl_set_swq_id(&priv->txq[txq_id], get_ac_from_tid(tid), txq_id);
548
549 tid_data = &trans->shrd->tid_data[sta_id][tid];
550 if (tid_data->tfds_in_queue == 0) {
551 IWL_DEBUG_HT(trans, "HW queue is empty\n");
552 tid_data->agg.state = IWL_AGG_ON;
553 iwl_start_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid);
554 } else {
555 IWL_DEBUG_HT(trans, "HW queue is NOT empty: %d packets in HW"
556 "queue\n", tid_data->tfds_in_queue);
557 tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
558 }
559 spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
560
561 return 0;
562}
512int iwl_trans_pcie_txq_agg_disable(struct iwl_priv *priv, u16 txq_id) 563int iwl_trans_pcie_txq_agg_disable(struct iwl_priv *priv, u16 txq_id)
513{ 564{
514 struct iwl_trans *trans = trans(priv); 565 struct iwl_trans *trans = trans(priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c
index 133b227cb64d..13e8fdc4c012 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.c
@@ -1957,6 +1957,7 @@ const struct iwl_trans_ops trans_ops_pcie = {
1957 .reclaim = iwl_trans_pcie_reclaim, 1957 .reclaim = iwl_trans_pcie_reclaim,
1958 1958
1959 .txq_agg_disable = iwl_trans_pcie_txq_agg_disable, 1959 .txq_agg_disable = iwl_trans_pcie_txq_agg_disable,
1960 .tx_agg_alloc = iwl_trans_pcie_tx_agg_alloc,
1960 .txq_agg_setup = iwl_trans_pcie_txq_agg_setup, 1961 .txq_agg_setup = iwl_trans_pcie_txq_agg_setup,
1961 1962
1962 .kick_nic = iwl_trans_pcie_kick_nic, 1963 .kick_nic = iwl_trans_pcie_kick_nic,
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 0fee8840c0aa..8aaab087ba54 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -94,6 +94,7 @@ struct iwl_device_cmd;
94 * @send_cmd_pdu:send a host command: flags can be CMD_* 94 * @send_cmd_pdu:send a host command: flags can be CMD_*
95 * @tx: send an skb 95 * @tx: send an skb
96 * @reclaim: free packet until ssn. Returns a list of freed packets. 96 * @reclaim: free packet until ssn. Returns a list of freed packets.
97 * @tx_agg_alloc: allocate resources for a TX BA session
97 * @txq_agg_setup: setup a tx queue for AMPDU - will be called once the HW is 98 * @txq_agg_setup: setup a tx queue for AMPDU - will be called once the HW is
98 * ready and a successful ADDBA response has been received. 99 * ready and a successful ADDBA response has been received.
99 * @txq_agg_disable: de-configure a Tx queue to send AMPDUs 100 * @txq_agg_disable: de-configure a Tx queue to send AMPDUs
@@ -126,6 +127,9 @@ struct iwl_trans_ops {
126 u32 status, struct sk_buff_head *skbs); 127 u32 status, struct sk_buff_head *skbs);
127 128
128 int (*txq_agg_disable)(struct iwl_priv *priv, u16 txq_id); 129 int (*txq_agg_disable)(struct iwl_priv *priv, u16 txq_id);
130 int (*tx_agg_alloc)(struct iwl_trans *trans,
131 enum iwl_rxon_context_id ctx, int sta_id, int tid,
132 u16 *ssn);
129 void (*txq_agg_setup)(struct iwl_priv *priv, 133 void (*txq_agg_setup)(struct iwl_priv *priv,
130 enum iwl_rxon_context_id ctx, int sta_id, 134 enum iwl_rxon_context_id ctx, int sta_id,
131 int tid, int frame_limit); 135 int tid, int frame_limit);
@@ -216,6 +220,14 @@ static inline int iwl_trans_txq_agg_disable(struct iwl_trans *trans, u16 txq_id)
216 return trans->ops->txq_agg_disable(priv(trans), txq_id); 220 return trans->ops->txq_agg_disable(priv(trans), txq_id);
217} 221}
218 222
223static inline int iwl_trans_tx_agg_alloc(struct iwl_trans *trans,
224 enum iwl_rxon_context_id ctx,
225 int sta_id, int tid, u16 *ssn)
226{
227 return trans->ops->tx_agg_alloc(trans, ctx, sta_id, tid, ssn);
228}
229
230
219static inline void iwl_trans_txq_agg_setup(struct iwl_trans *trans, 231static inline void iwl_trans_txq_agg_setup(struct iwl_trans *trans,
220 enum iwl_rxon_context_id ctx, 232 enum iwl_rxon_context_id ctx,
221 int sta_id, int tid, 233 int sta_id, int tid,