diff options
author | Tomas Winkler <tomas.winkler@intel.com> | 2008-05-29 04:35:16 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-06-03 15:00:25 -0400 |
commit | 30e553e3ea3572bee3118fcde36e043c0cb2174c (patch) | |
tree | 5cb6bd4634e4dba343b69df0b1971ff275ec25fa /drivers/net/wireless | |
parent | 5083e56326208f9a1d4e597529912004968c77d7 (diff) |
iwlwifi: move aggregation code to iwl-tx.c
This patch moves aggregation action code to iwl-tx.c in iwlcore.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-4965.c | 224 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-5000.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 14 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-prph.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-tx.c | 190 |
5 files changed, 228 insertions, 210 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index b719d19361ac..556e59ef70e1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c | |||
@@ -55,30 +55,6 @@ static struct iwl_mod_params iwl4965_mod_params = { | |||
55 | /* the rest are 0 by default */ | 55 | /* the rest are 0 by default */ |
56 | }; | 56 | }; |
57 | 57 | ||
58 | #ifdef CONFIG_IWL4965_HT | ||
59 | |||
60 | static const u16 default_tid_to_tx_fifo[] = { | ||
61 | IWL_TX_FIFO_AC1, | ||
62 | IWL_TX_FIFO_AC0, | ||
63 | IWL_TX_FIFO_AC0, | ||
64 | IWL_TX_FIFO_AC1, | ||
65 | IWL_TX_FIFO_AC2, | ||
66 | IWL_TX_FIFO_AC2, | ||
67 | IWL_TX_FIFO_AC3, | ||
68 | IWL_TX_FIFO_AC3, | ||
69 | IWL_TX_FIFO_NONE, | ||
70 | IWL_TX_FIFO_NONE, | ||
71 | IWL_TX_FIFO_NONE, | ||
72 | IWL_TX_FIFO_NONE, | ||
73 | IWL_TX_FIFO_NONE, | ||
74 | IWL_TX_FIFO_NONE, | ||
75 | IWL_TX_FIFO_NONE, | ||
76 | IWL_TX_FIFO_NONE, | ||
77 | IWL_TX_FIFO_AC3 | ||
78 | }; | ||
79 | |||
80 | #endif /*CONFIG_IWL4965_HT */ | ||
81 | |||
82 | /* check contents of special bootstrap uCode SRAM */ | 58 | /* check contents of special bootstrap uCode SRAM */ |
83 | static int iwl4965_verify_bsm(struct iwl_priv *priv) | 59 | static int iwl4965_verify_bsm(struct iwl_priv *priv) |
84 | { | 60 | { |
@@ -2986,8 +2962,8 @@ static void iwl4965_tx_queue_stop_scheduler(struct iwl_priv *priv, | |||
2986 | * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID | 2962 | * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID |
2987 | * priv->lock must be held by the caller | 2963 | * priv->lock must be held by the caller |
2988 | */ | 2964 | */ |
2989 | static int iwl4965_tx_queue_agg_disable(struct iwl_priv *priv, u16 txq_id, | 2965 | static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, |
2990 | u16 ssn_idx, u8 tx_fifo) | 2966 | u16 ssn_idx, u8 tx_fifo) |
2991 | { | 2967 | { |
2992 | int ret = 0; | 2968 | int ret = 0; |
2993 | 2969 | ||
@@ -3019,39 +2995,6 @@ static int iwl4965_tx_queue_agg_disable(struct iwl_priv *priv, u16 txq_id, | |||
3019 | return 0; | 2995 | return 0; |
3020 | } | 2996 | } |
3021 | 2997 | ||
3022 | int iwl4965_check_empty_hw_queue(struct iwl_priv *priv, int sta_id, | ||
3023 | u8 tid, int txq_id) | ||
3024 | { | ||
3025 | struct iwl_queue *q = &priv->txq[txq_id].q; | ||
3026 | u8 *addr = priv->stations[sta_id].sta.sta.addr; | ||
3027 | struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid]; | ||
3028 | |||
3029 | switch (priv->stations[sta_id].tid[tid].agg.state) { | ||
3030 | case IWL_EMPTYING_HW_QUEUE_DELBA: | ||
3031 | /* We are reclaiming the last packet of the */ | ||
3032 | /* aggregated HW queue */ | ||
3033 | if (txq_id == tid_data->agg.txq_id && | ||
3034 | q->read_ptr == q->write_ptr) { | ||
3035 | u16 ssn = SEQ_TO_SN(tid_data->seq_number); | ||
3036 | int tx_fifo = default_tid_to_tx_fifo[tid]; | ||
3037 | IWL_DEBUG_HT("HW queue empty: continue DELBA flow\n"); | ||
3038 | iwl4965_tx_queue_agg_disable(priv, txq_id, | ||
3039 | ssn, tx_fifo); | ||
3040 | tid_data->agg.state = IWL_AGG_OFF; | ||
3041 | ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, addr, tid); | ||
3042 | } | ||
3043 | break; | ||
3044 | case IWL_EMPTYING_HW_QUEUE_ADDBA: | ||
3045 | /* We are reclaiming the last packet of the queue */ | ||
3046 | if (tid_data->tfds_in_queue == 0) { | ||
3047 | IWL_DEBUG_HT("HW queue empty: continue ADDBA flow\n"); | ||
3048 | tid_data->agg.state = IWL_AGG_ON; | ||
3049 | ieee80211_start_tx_ba_cb_irqsafe(priv->hw, addr, tid); | ||
3050 | } | ||
3051 | break; | ||
3052 | } | ||
3053 | return 0; | ||
3054 | } | ||
3055 | 2998 | ||
3056 | /** | 2999 | /** |
3057 | * iwl4965_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA | 3000 | * iwl4965_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA |
@@ -3122,8 +3065,9 @@ static void iwl4965_rx_reply_compressed_ba(struct iwl_priv *priv, | |||
3122 | priv->mac80211_registered && | 3065 | priv->mac80211_registered && |
3123 | agg->state != IWL_EMPTYING_HW_QUEUE_DELBA) | 3066 | agg->state != IWL_EMPTYING_HW_QUEUE_DELBA) |
3124 | ieee80211_wake_queue(priv->hw, ampdu_q); | 3067 | ieee80211_wake_queue(priv->hw, ampdu_q); |
3125 | iwl4965_check_empty_hw_queue(priv, ba_resp->sta_id, | 3068 | |
3126 | ba_resp->tid, scd_flow); | 3069 | iwl_txq_check_empty(priv, ba_resp->sta_id, |
3070 | ba_resp->tid, scd_flow); | ||
3127 | } | 3071 | } |
3128 | } | 3072 | } |
3129 | 3073 | ||
@@ -3137,7 +3081,7 @@ static int iwl4965_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid, | |||
3137 | u32 tbl_dw; | 3081 | u32 tbl_dw; |
3138 | u16 scd_q2ratid; | 3082 | u16 scd_q2ratid; |
3139 | 3083 | ||
3140 | scd_q2ratid = ra_tid & IWL49_SCD_QUEUE_RA_TID_MAP_RATID_MSK; | 3084 | scd_q2ratid = ra_tid & IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK; |
3141 | 3085 | ||
3142 | tbl_dw_addr = priv->scd_base_addr + | 3086 | tbl_dw_addr = priv->scd_base_addr + |
3143 | IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); | 3087 | IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); |
@@ -3161,12 +3105,11 @@ static int iwl4965_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid, | |||
3161 | * NOTE: txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID, | 3105 | * NOTE: txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID, |
3162 | * i.e. it must be one of the higher queues used for aggregation | 3106 | * i.e. it must be one of the higher queues used for aggregation |
3163 | */ | 3107 | */ |
3164 | static int iwl4965_tx_queue_agg_enable(struct iwl_priv *priv, int txq_id, | 3108 | static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id, |
3165 | int tx_fifo, int sta_id, int tid, | 3109 | int tx_fifo, int sta_id, int tid, u16 ssn_idx) |
3166 | u16 ssn_idx) | ||
3167 | { | 3110 | { |
3168 | unsigned long flags; | 3111 | unsigned long flags; |
3169 | int rc; | 3112 | int ret; |
3170 | u16 ra_tid; | 3113 | u16 ra_tid; |
3171 | 3114 | ||
3172 | if (IWL_BACK_QUEUE_FIRST_ID > txq_id) | 3115 | if (IWL_BACK_QUEUE_FIRST_ID > txq_id) |
@@ -3179,10 +3122,10 @@ static int iwl4965_tx_queue_agg_enable(struct iwl_priv *priv, int txq_id, | |||
3179 | iwl_sta_modify_enable_tid_tx(priv, sta_id, tid); | 3122 | iwl_sta_modify_enable_tid_tx(priv, sta_id, tid); |
3180 | 3123 | ||
3181 | spin_lock_irqsave(&priv->lock, flags); | 3124 | spin_lock_irqsave(&priv->lock, flags); |
3182 | rc = iwl_grab_nic_access(priv); | 3125 | ret = iwl_grab_nic_access(priv); |
3183 | if (rc) { | 3126 | if (ret) { |
3184 | spin_unlock_irqrestore(&priv->lock, flags); | 3127 | spin_unlock_irqrestore(&priv->lock, flags); |
3185 | return rc; | 3128 | return ret; |
3186 | } | 3129 | } |
3187 | 3130 | ||
3188 | /* Stop this Tx queue before configuring it */ | 3131 | /* Stop this Tx queue before configuring it */ |
@@ -3269,137 +3212,6 @@ static int iwl4965_rx_agg_stop(struct iwl_priv *priv, | |||
3269 | CMD_ASYNC); | 3212 | CMD_ASYNC); |
3270 | } | 3213 | } |
3271 | 3214 | ||
3272 | /* | ||
3273 | * Find first available (lowest unused) Tx Queue, mark it "active". | ||
3274 | * Called only when finding queue for aggregation. | ||
3275 | * Should never return anything < 7, because they should already | ||
3276 | * be in use as EDCA AC (0-3), Command (4), HCCA (5, 6). | ||
3277 | */ | ||
3278 | static int iwl4965_txq_ctx_activate_free(struct iwl_priv *priv) | ||
3279 | { | ||
3280 | int txq_id; | ||
3281 | |||
3282 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) | ||
3283 | if (!test_and_set_bit(txq_id, &priv->txq_ctx_active_msk)) | ||
3284 | return txq_id; | ||
3285 | return -1; | ||
3286 | } | ||
3287 | |||
3288 | static int iwl4965_tx_agg_start(struct ieee80211_hw *hw, const u8 *ra, | ||
3289 | u16 tid, u16 *start_seq_num) | ||
3290 | { | ||
3291 | struct iwl_priv *priv = hw->priv; | ||
3292 | int sta_id; | ||
3293 | int tx_fifo; | ||
3294 | int txq_id; | ||
3295 | int ssn = -1; | ||
3296 | int ret = 0; | ||
3297 | unsigned long flags; | ||
3298 | struct iwl_tid_data *tid_data; | ||
3299 | DECLARE_MAC_BUF(mac); | ||
3300 | |||
3301 | if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) | ||
3302 | tx_fifo = default_tid_to_tx_fifo[tid]; | ||
3303 | else | ||
3304 | return -EINVAL; | ||
3305 | |||
3306 | IWL_WARNING("%s on ra = %s tid = %d\n", | ||
3307 | __func__, print_mac(mac, ra), tid); | ||
3308 | |||
3309 | sta_id = iwl_find_station(priv, ra); | ||
3310 | if (sta_id == IWL_INVALID_STATION) | ||
3311 | return -ENXIO; | ||
3312 | |||
3313 | if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_OFF) { | ||
3314 | IWL_ERROR("Start AGG when state is not IWL_AGG_OFF !\n"); | ||
3315 | return -ENXIO; | ||
3316 | } | ||
3317 | |||
3318 | txq_id = iwl4965_txq_ctx_activate_free(priv); | ||
3319 | if (txq_id == -1) | ||
3320 | return -ENXIO; | ||
3321 | |||
3322 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
3323 | tid_data = &priv->stations[sta_id].tid[tid]; | ||
3324 | ssn = SEQ_TO_SN(tid_data->seq_number); | ||
3325 | tid_data->agg.txq_id = txq_id; | ||
3326 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
3327 | |||
3328 | *start_seq_num = ssn; | ||
3329 | ret = iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo, | ||
3330 | sta_id, tid, ssn); | ||
3331 | if (ret) | ||
3332 | return ret; | ||
3333 | |||
3334 | ret = 0; | ||
3335 | if (tid_data->tfds_in_queue == 0) { | ||
3336 | printk(KERN_ERR "HW queue is empty\n"); | ||
3337 | tid_data->agg.state = IWL_AGG_ON; | ||
3338 | ieee80211_start_tx_ba_cb_irqsafe(hw, ra, tid); | ||
3339 | } else { | ||
3340 | IWL_DEBUG_HT("HW queue is NOT empty: %d packets in HW queue\n", | ||
3341 | tid_data->tfds_in_queue); | ||
3342 | tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; | ||
3343 | } | ||
3344 | return ret; | ||
3345 | } | ||
3346 | |||
3347 | static int iwl4965_tx_agg_stop(struct ieee80211_hw *hw, const u8 *ra, u16 tid) | ||
3348 | { | ||
3349 | struct iwl_priv *priv = hw->priv; | ||
3350 | int tx_fifo_id, txq_id, sta_id, ssn = -1; | ||
3351 | struct iwl_tid_data *tid_data; | ||
3352 | int ret, write_ptr, read_ptr; | ||
3353 | unsigned long flags; | ||
3354 | DECLARE_MAC_BUF(mac); | ||
3355 | |||
3356 | if (!ra) { | ||
3357 | IWL_ERROR("ra = NULL\n"); | ||
3358 | return -EINVAL; | ||
3359 | } | ||
3360 | |||
3361 | if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) | ||
3362 | tx_fifo_id = default_tid_to_tx_fifo[tid]; | ||
3363 | else | ||
3364 | return -EINVAL; | ||
3365 | |||
3366 | sta_id = iwl_find_station(priv, ra); | ||
3367 | |||
3368 | if (sta_id == IWL_INVALID_STATION) | ||
3369 | return -ENXIO; | ||
3370 | |||
3371 | if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON) | ||
3372 | IWL_WARNING("Stopping AGG while state not IWL_AGG_ON\n"); | ||
3373 | |||
3374 | tid_data = &priv->stations[sta_id].tid[tid]; | ||
3375 | ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; | ||
3376 | txq_id = tid_data->agg.txq_id; | ||
3377 | write_ptr = priv->txq[txq_id].q.write_ptr; | ||
3378 | read_ptr = priv->txq[txq_id].q.read_ptr; | ||
3379 | |||
3380 | /* The queue is not empty */ | ||
3381 | if (write_ptr != read_ptr) { | ||
3382 | IWL_DEBUG_HT("Stopping a non empty AGG HW QUEUE\n"); | ||
3383 | priv->stations[sta_id].tid[tid].agg.state = | ||
3384 | IWL_EMPTYING_HW_QUEUE_DELBA; | ||
3385 | return 0; | ||
3386 | } | ||
3387 | |||
3388 | IWL_DEBUG_HT("HW queue is empty\n"); | ||
3389 | priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; | ||
3390 | |||
3391 | spin_lock_irqsave(&priv->lock, flags); | ||
3392 | ret = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id); | ||
3393 | spin_unlock_irqrestore(&priv->lock, flags); | ||
3394 | |||
3395 | if (ret) | ||
3396 | return ret; | ||
3397 | |||
3398 | ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, ra, tid); | ||
3399 | |||
3400 | return 0; | ||
3401 | } | ||
3402 | |||
3403 | int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, | 3215 | int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, |
3404 | enum ieee80211_ampdu_mlme_action action, | 3216 | enum ieee80211_ampdu_mlme_action action, |
3405 | const u8 *addr, u16 tid, u16 *ssn) | 3217 | const u8 *addr, u16 tid, u16 *ssn) |
@@ -3419,10 +3231,10 @@ int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, | |||
3419 | return iwl4965_rx_agg_stop(priv, addr, tid); | 3231 | return iwl4965_rx_agg_stop(priv, addr, tid); |
3420 | case IEEE80211_AMPDU_TX_START: | 3232 | case IEEE80211_AMPDU_TX_START: |
3421 | IWL_DEBUG_HT("start Tx\n"); | 3233 | IWL_DEBUG_HT("start Tx\n"); |
3422 | return iwl4965_tx_agg_start(hw, addr, tid, ssn); | 3234 | return iwl_tx_agg_start(priv, addr, tid, ssn); |
3423 | case IEEE80211_AMPDU_TX_STOP: | 3235 | case IEEE80211_AMPDU_TX_STOP: |
3424 | IWL_DEBUG_HT("stop Tx\n"); | 3236 | IWL_DEBUG_HT("stop Tx\n"); |
3425 | return iwl4965_tx_agg_stop(hw, addr, tid); | 3237 | return iwl_tx_agg_stop(priv, addr, tid); |
3426 | default: | 3238 | default: |
3427 | IWL_DEBUG_HT("unknown\n"); | 3239 | IWL_DEBUG_HT("unknown\n"); |
3428 | return -EINVAL; | 3240 | return -EINVAL; |
@@ -3670,7 +3482,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, | |||
3670 | else | 3482 | else |
3671 | ieee80211_wake_queue(priv->hw, ampdu_q); | 3483 | ieee80211_wake_queue(priv->hw, ampdu_q); |
3672 | } | 3484 | } |
3673 | iwl4965_check_empty_hw_queue(priv, sta_id, tid, txq_id); | 3485 | iwl_txq_check_empty(priv, sta_id, tid, txq_id); |
3674 | } | 3486 | } |
3675 | } else { | 3487 | } else { |
3676 | #endif /* CONFIG_IWL4965_HT */ | 3488 | #endif /* CONFIG_IWL4965_HT */ |
@@ -3695,7 +3507,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, | |||
3695 | (txq_id >= 0) && priv->mac80211_registered) | 3507 | (txq_id >= 0) && priv->mac80211_registered) |
3696 | ieee80211_wake_queue(priv->hw, txq_id); | 3508 | ieee80211_wake_queue(priv->hw, txq_id); |
3697 | if (tid != MAX_TID_COUNT) | 3509 | if (tid != MAX_TID_COUNT) |
3698 | iwl4965_check_empty_hw_queue(priv, sta_id, tid, txq_id); | 3510 | iwl_txq_check_empty(priv, sta_id, tid, txq_id); |
3699 | } | 3511 | } |
3700 | } | 3512 | } |
3701 | #endif /* CONFIG_IWL4965_HT */ | 3513 | #endif /* CONFIG_IWL4965_HT */ |
@@ -3761,6 +3573,10 @@ static struct iwl_lib_ops iwl4965_lib = { | |||
3761 | .shared_mem_rx_idx = iwl4965_shared_mem_rx_idx, | 3573 | .shared_mem_rx_idx = iwl4965_shared_mem_rx_idx, |
3762 | .txq_update_byte_cnt_tbl = iwl4965_txq_update_byte_cnt_tbl, | 3574 | .txq_update_byte_cnt_tbl = iwl4965_txq_update_byte_cnt_tbl, |
3763 | .txq_set_sched = iwl4965_txq_set_sched, | 3575 | .txq_set_sched = iwl4965_txq_set_sched, |
3576 | #ifdef CONFIG_IWL4965_HT | ||
3577 | .txq_agg_enable = iwl4965_txq_agg_enable, | ||
3578 | .txq_agg_disable = iwl4965_txq_agg_disable, | ||
3579 | #endif | ||
3764 | .rx_handler_setup = iwl4965_rx_handler_setup, | 3580 | .rx_handler_setup = iwl4965_rx_handler_setup, |
3765 | .is_valid_rtc_data_addr = iwl4965_hw_valid_rtc_data_addr, | 3581 | .is_valid_rtc_data_addr = iwl4965_hw_valid_rtc_data_addr, |
3766 | .alive_notify = iwl4965_alive_notify, | 3582 | .alive_notify = iwl4965_alive_notify, |
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 217c6a9596cd..9ef4468327af 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c | |||
@@ -1193,7 +1193,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, | |||
1193 | else | 1193 | else |
1194 | ieee80211_wake_queue(priv->hw, ampdu_q); | 1194 | ieee80211_wake_queue(priv->hw, ampdu_q); |
1195 | } | 1195 | } |
1196 | iwl4965_check_empty_hw_queue(priv, sta_id, tid, txq_id); | 1196 | iwl_txq_check_empty(priv, sta_id, tid, txq_id); |
1197 | } | 1197 | } |
1198 | } else { | 1198 | } else { |
1199 | #endif /* CONFIG_IWL4965_HT */ | 1199 | #endif /* CONFIG_IWL4965_HT */ |
@@ -1218,7 +1218,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, | |||
1218 | (txq_id >= 0) && priv->mac80211_registered) | 1218 | (txq_id >= 0) && priv->mac80211_registered) |
1219 | ieee80211_wake_queue(priv->hw, txq_id); | 1219 | ieee80211_wake_queue(priv->hw, txq_id); |
1220 | if (tid != MAX_TID_COUNT) | 1220 | if (tid != MAX_TID_COUNT) |
1221 | iwl4965_check_empty_hw_queue(priv, sta_id, tid, txq_id); | 1221 | iwl_txq_check_empty(priv, sta_id, tid, txq_id); |
1222 | } | 1222 | } |
1223 | } | 1223 | } |
1224 | #endif /* CONFIG_IWL4965_HT */ | 1224 | #endif /* CONFIG_IWL4965_HT */ |
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 1941a9184aa2..004c68444006 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h | |||
@@ -104,15 +104,22 @@ struct iwl_lib_ops { | |||
104 | int (*alloc_shared_mem)(struct iwl_priv *priv); | 104 | int (*alloc_shared_mem)(struct iwl_priv *priv); |
105 | void (*free_shared_mem)(struct iwl_priv *priv); | 105 | void (*free_shared_mem)(struct iwl_priv *priv); |
106 | int (*shared_mem_rx_idx)(struct iwl_priv *priv); | 106 | int (*shared_mem_rx_idx)(struct iwl_priv *priv); |
107 | /* Handling TX */ | ||
107 | void (*txq_update_byte_cnt_tbl)(struct iwl_priv *priv, | 108 | void (*txq_update_byte_cnt_tbl)(struct iwl_priv *priv, |
108 | struct iwl_tx_queue *txq, | 109 | struct iwl_tx_queue *txq, |
109 | u16 byte_cnt); | 110 | u16 byte_cnt); |
110 | void (*txq_inval_byte_cnt_tbl)(struct iwl_priv *priv, | 111 | void (*txq_inval_byte_cnt_tbl)(struct iwl_priv *priv, |
111 | struct iwl_tx_queue *txq); | 112 | struct iwl_tx_queue *txq); |
112 | void (*txq_set_sched)(struct iwl_priv *priv, u32 mask); | 113 | void (*txq_set_sched)(struct iwl_priv *priv, u32 mask); |
114 | #ifdef CONFIG_IWL4965_HT | ||
115 | /* aggregations */ | ||
116 | int (*txq_agg_enable)(struct iwl_priv *priv, int txq_id, int tx_fifo, | ||
117 | int sta_id, int tid, u16 ssn_idx); | ||
118 | int (*txq_agg_disable)(struct iwl_priv *priv, u16 txq_id, u16 ssn_idx, | ||
119 | u8 tx_fifo); | ||
120 | #endif /* CONFIG_IWL4965_HT */ | ||
113 | /* setup Rx handler */ | 121 | /* setup Rx handler */ |
114 | void (*rx_handler_setup)(struct iwl_priv *priv); | 122 | void (*rx_handler_setup)(struct iwl_priv *priv); |
115 | /* nic Tx fifo handling */ | ||
116 | /* alive notification after init uCode load */ | 123 | /* alive notification after init uCode load */ |
117 | void (*init_alive_start)(struct iwl_priv *priv); | 124 | void (*init_alive_start)(struct iwl_priv *priv); |
118 | /* alive notification */ | 125 | /* alive notification */ |
@@ -226,6 +233,11 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv); | |||
226 | int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *tfd, | 233 | int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *tfd, |
227 | dma_addr_t addr, u16 len); | 234 | dma_addr_t addr, u16 len); |
228 | int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq); | 235 | int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq); |
236 | #ifdef CONFIG_IWL4965_HT | ||
237 | int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn); | ||
238 | int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid); | ||
239 | int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id); | ||
240 | #endif | ||
229 | 241 | ||
230 | /***************************************************** | 242 | /***************************************************** |
231 | * S e n d i n g H o s t C o m m a n d s * | 243 | * S e n d i n g H o s t C o m m a n d s * |
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 15cb7ff4f7aa..70d9c7568b98 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h | |||
@@ -507,9 +507,9 @@ | |||
507 | #define IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \ | 507 | #define IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \ |
508 | ((IWL49_SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc) | 508 | ((IWL49_SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc) |
509 | 509 | ||
510 | #define IWL49_SCD_TXFIFO_POS_TID (0) | 510 | #define IWL_SCD_TXFIFO_POS_TID (0) |
511 | #define IWL49_SCD_TXFIFO_POS_RA (4) | 511 | #define IWL_SCD_TXFIFO_POS_RA (4) |
512 | #define IWL49_SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) | 512 | #define IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) |
513 | 513 | ||
514 | /* 5000 SCD */ | 514 | /* 5000 SCD */ |
515 | #define IWL50_SCD_QUEUE_STTS_REG_POS_TXF (0) | 515 | #define IWL50_SCD_QUEUE_STTS_REG_POS_TXF (0) |
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 224bcec21e5b..cfe6f4b233dd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c | |||
@@ -36,6 +36,32 @@ | |||
36 | #include "iwl-io.h" | 36 | #include "iwl-io.h" |
37 | #include "iwl-helpers.h" | 37 | #include "iwl-helpers.h" |
38 | 38 | ||
39 | #ifdef CONFIG_IWL4965_HT | ||
40 | |||
41 | static const u16 default_tid_to_tx_fifo[] = { | ||
42 | IWL_TX_FIFO_AC1, | ||
43 | IWL_TX_FIFO_AC0, | ||
44 | IWL_TX_FIFO_AC0, | ||
45 | IWL_TX_FIFO_AC1, | ||
46 | IWL_TX_FIFO_AC2, | ||
47 | IWL_TX_FIFO_AC2, | ||
48 | IWL_TX_FIFO_AC3, | ||
49 | IWL_TX_FIFO_AC3, | ||
50 | IWL_TX_FIFO_NONE, | ||
51 | IWL_TX_FIFO_NONE, | ||
52 | IWL_TX_FIFO_NONE, | ||
53 | IWL_TX_FIFO_NONE, | ||
54 | IWL_TX_FIFO_NONE, | ||
55 | IWL_TX_FIFO_NONE, | ||
56 | IWL_TX_FIFO_NONE, | ||
57 | IWL_TX_FIFO_NONE, | ||
58 | IWL_TX_FIFO_AC3 | ||
59 | }; | ||
60 | |||
61 | #endif /*CONFIG_IWL4965_HT */ | ||
62 | |||
63 | |||
64 | |||
39 | /** | 65 | /** |
40 | * iwl_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] | 66 | * iwl_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] |
41 | * | 67 | * |
@@ -1171,6 +1197,170 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) | |||
1171 | EXPORT_SYMBOL(iwl_tx_cmd_complete); | 1197 | EXPORT_SYMBOL(iwl_tx_cmd_complete); |
1172 | 1198 | ||
1173 | 1199 | ||
1200 | #ifdef CONFIG_IWL4965_HT | ||
1201 | /* | ||
1202 | * Find first available (lowest unused) Tx Queue, mark it "active". | ||
1203 | * Called only when finding queue for aggregation. | ||
1204 | * Should never return anything < 7, because they should already | ||
1205 | * be in use as EDCA AC (0-3), Command (4), HCCA (5, 6). | ||
1206 | */ | ||
1207 | static int iwl_txq_ctx_activate_free(struct iwl_priv *priv) | ||
1208 | { | ||
1209 | int txq_id; | ||
1210 | |||
1211 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) | ||
1212 | if (!test_and_set_bit(txq_id, &priv->txq_ctx_active_msk)) | ||
1213 | return txq_id; | ||
1214 | return -1; | ||
1215 | } | ||
1216 | |||
1217 | int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn) | ||
1218 | { | ||
1219 | int sta_id; | ||
1220 | int tx_fifo; | ||
1221 | int txq_id; | ||
1222 | int ret; | ||
1223 | unsigned long flags; | ||
1224 | struct iwl_tid_data *tid_data; | ||
1225 | DECLARE_MAC_BUF(mac); | ||
1226 | |||
1227 | if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) | ||
1228 | tx_fifo = default_tid_to_tx_fifo[tid]; | ||
1229 | else | ||
1230 | return -EINVAL; | ||
1231 | |||
1232 | IWL_WARNING("%s on ra = %s tid = %d\n", | ||
1233 | __func__, print_mac(mac, ra), tid); | ||
1234 | |||
1235 | sta_id = iwl_find_station(priv, ra); | ||
1236 | if (sta_id == IWL_INVALID_STATION) | ||
1237 | return -ENXIO; | ||
1238 | |||
1239 | if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_OFF) { | ||
1240 | IWL_ERROR("Start AGG when state is not IWL_AGG_OFF !\n"); | ||
1241 | return -ENXIO; | ||
1242 | } | ||
1243 | |||
1244 | txq_id = iwl_txq_ctx_activate_free(priv); | ||
1245 | if (txq_id == -1) | ||
1246 | return -ENXIO; | ||
1247 | |||
1248 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
1249 | tid_data = &priv->stations[sta_id].tid[tid]; | ||
1250 | *ssn = SEQ_TO_SN(tid_data->seq_number); | ||
1251 | tid_data->agg.txq_id = txq_id; | ||
1252 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
1253 | |||
1254 | ret = priv->cfg->ops->lib->txq_agg_enable(priv, txq_id, tx_fifo, | ||
1255 | sta_id, tid, *ssn); | ||
1256 | if (ret) | ||
1257 | return ret; | ||
1258 | |||
1259 | if (tid_data->tfds_in_queue == 0) { | ||
1260 | printk(KERN_ERR "HW queue is empty\n"); | ||
1261 | tid_data->agg.state = IWL_AGG_ON; | ||
1262 | ieee80211_start_tx_ba_cb_irqsafe(priv->hw, ra, tid); | ||
1263 | } else { | ||
1264 | IWL_DEBUG_HT("HW queue is NOT empty: %d packets in HW queue\n", | ||
1265 | tid_data->tfds_in_queue); | ||
1266 | tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; | ||
1267 | } | ||
1268 | return ret; | ||
1269 | } | ||
1270 | EXPORT_SYMBOL(iwl_tx_agg_start); | ||
1271 | |||
1272 | int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) | ||
1273 | { | ||
1274 | int tx_fifo_id, txq_id, sta_id, ssn = -1; | ||
1275 | struct iwl_tid_data *tid_data; | ||
1276 | int ret, write_ptr, read_ptr; | ||
1277 | unsigned long flags; | ||
1278 | DECLARE_MAC_BUF(mac); | ||
1279 | |||
1280 | if (!ra) { | ||
1281 | IWL_ERROR("ra = NULL\n"); | ||
1282 | return -EINVAL; | ||
1283 | } | ||
1284 | |||
1285 | if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) | ||
1286 | tx_fifo_id = default_tid_to_tx_fifo[tid]; | ||
1287 | else | ||
1288 | return -EINVAL; | ||
1289 | |||
1290 | sta_id = iwl_find_station(priv, ra); | ||
1291 | |||
1292 | if (sta_id == IWL_INVALID_STATION) | ||
1293 | return -ENXIO; | ||
1294 | |||
1295 | if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON) | ||
1296 | IWL_WARNING("Stopping AGG while state not IWL_AGG_ON\n"); | ||
1297 | |||
1298 | tid_data = &priv->stations[sta_id].tid[tid]; | ||
1299 | ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; | ||
1300 | txq_id = tid_data->agg.txq_id; | ||
1301 | write_ptr = priv->txq[txq_id].q.write_ptr; | ||
1302 | read_ptr = priv->txq[txq_id].q.read_ptr; | ||
1303 | |||
1304 | /* The queue is not empty */ | ||
1305 | if (write_ptr != read_ptr) { | ||
1306 | IWL_DEBUG_HT("Stopping a non empty AGG HW QUEUE\n"); | ||
1307 | priv->stations[sta_id].tid[tid].agg.state = | ||
1308 | IWL_EMPTYING_HW_QUEUE_DELBA; | ||
1309 | return 0; | ||
1310 | } | ||
1311 | |||
1312 | IWL_DEBUG_HT("HW queue is empty\n"); | ||
1313 | priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; | ||
1314 | |||
1315 | spin_lock_irqsave(&priv->lock, flags); | ||
1316 | ret = priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, ssn, | ||
1317 | tx_fifo_id); | ||
1318 | spin_unlock_irqrestore(&priv->lock, flags); | ||
1319 | |||
1320 | if (ret) | ||
1321 | return ret; | ||
1322 | |||
1323 | ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, ra, tid); | ||
1324 | |||
1325 | return 0; | ||
1326 | } | ||
1327 | EXPORT_SYMBOL(iwl_tx_agg_stop); | ||
1328 | |||
1329 | int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id) | ||
1330 | { | ||
1331 | struct iwl_queue *q = &priv->txq[txq_id].q; | ||
1332 | u8 *addr = priv->stations[sta_id].sta.sta.addr; | ||
1333 | struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid]; | ||
1334 | |||
1335 | switch (priv->stations[sta_id].tid[tid].agg.state) { | ||
1336 | case IWL_EMPTYING_HW_QUEUE_DELBA: | ||
1337 | /* We are reclaiming the last packet of the */ | ||
1338 | /* aggregated HW queue */ | ||
1339 | if (txq_id == tid_data->agg.txq_id && | ||
1340 | q->read_ptr == q->write_ptr) { | ||
1341 | u16 ssn = SEQ_TO_SN(tid_data->seq_number); | ||
1342 | int tx_fifo = default_tid_to_tx_fifo[tid]; | ||
1343 | IWL_DEBUG_HT("HW queue empty: continue DELBA flow\n"); | ||
1344 | priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, | ||
1345 | ssn, tx_fifo); | ||
1346 | tid_data->agg.state = IWL_AGG_OFF; | ||
1347 | ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, addr, tid); | ||
1348 | } | ||
1349 | break; | ||
1350 | case IWL_EMPTYING_HW_QUEUE_ADDBA: | ||
1351 | /* We are reclaiming the last packet of the queue */ | ||
1352 | if (tid_data->tfds_in_queue == 0) { | ||
1353 | IWL_DEBUG_HT("HW queue empty: continue ADDBA flow\n"); | ||
1354 | tid_data->agg.state = IWL_AGG_ON; | ||
1355 | ieee80211_start_tx_ba_cb_irqsafe(priv->hw, addr, tid); | ||
1356 | } | ||
1357 | break; | ||
1358 | } | ||
1359 | return 0; | ||
1360 | } | ||
1361 | EXPORT_SYMBOL(iwl_txq_check_empty); | ||
1362 | #endif /* CONFIG_IWL4965_HT */ | ||
1363 | |||
1174 | #ifdef CONFIG_IWLWIF_DEBUG | 1364 | #ifdef CONFIG_IWLWIF_DEBUG |
1175 | #define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x | 1365 | #define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x |
1176 | 1366 | ||