diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2011-08-26 02:11:25 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-08-29 15:30:32 -0400 |
commit | 288712a6ccf47b9df104f800616f6659ecadc940 (patch) | |
tree | 5d2d15b7f53db59dc76a3753eb2db3c92c981f18 /drivers/net/wireless/iwlwifi | |
parent | e13c0c59e0ec38558ac853d56555e915b4dc7dc2 (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.c | 77 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-shared.h | 12 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c | 53 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.h | 12 |
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 | ||
45 | static 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 | |||
54 | static 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 | |||
72 | static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, | 45 | static 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 | */ | ||
408 | static 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 | |||
418 | int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, | 375 | int 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 | ||
1861 | void 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 | ||
324 | static 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 | |||
324 | enum iwl_rxon_context_id { | 333 | enum 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); |
338 | void __devexit iwl_remove(struct iwl_priv * priv); | 347 | void __devexit iwl_remove(struct iwl_priv * priv); |
339 | 348 | ||
349 | void 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); | |||
194 | void iwl_trans_tx_queue_set_status(struct iwl_priv *priv, | 194 | void 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); |
197 | int 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); | ||
197 | void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv, | 200 | void 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 | */ | ||
517 | static 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 | |||
528 | int 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 | } | ||
512 | int iwl_trans_pcie_txq_agg_disable(struct iwl_priv *priv, u16 txq_id) | 563 | int 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 | ||
223 | static 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 | |||
219 | static inline void iwl_trans_txq_agg_setup(struct iwl_trans *trans, | 231 | static 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, |