diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-tx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 142 |
1 files changed, 63 insertions, 79 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index a709d05c5868..2816b432c62f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * | 2 | * |
3 | * GPL LICENSE SUMMARY | 3 | * GPL LICENSE SUMMARY |
4 | * | 4 | * |
5 | * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. | 5 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of version 2 of the GNU General Public License as | 8 | * it under the terms of version 2 of the GNU General Public License as |
@@ -222,13 +222,8 @@ void iwlagn_tx_queue_set_status(struct iwl_priv *priv, | |||
222 | scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id); | 222 | scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id); |
223 | } | 223 | } |
224 | 224 | ||
225 | int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, | 225 | static int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, int sta_id, int tid) |
226 | int tx_fifo, int sta_id, int tid, u16 ssn_idx) | ||
227 | { | 226 | { |
228 | unsigned long flags; | ||
229 | u16 ra_tid; | ||
230 | int ret; | ||
231 | |||
232 | if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) || | 227 | if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) || |
233 | (IWLAGN_FIRST_AMPDU_QUEUE + | 228 | (IWLAGN_FIRST_AMPDU_QUEUE + |
234 | priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) { | 229 | priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) { |
@@ -240,12 +235,33 @@ int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, | |||
240 | return -EINVAL; | 235 | return -EINVAL; |
241 | } | 236 | } |
242 | 237 | ||
243 | ra_tid = BUILD_RAxTID(sta_id, tid); | ||
244 | |||
245 | /* Modify device's station table to Tx this TID */ | 238 | /* Modify device's station table to Tx this TID */ |
246 | ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); | 239 | return iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); |
247 | if (ret) | 240 | } |
248 | return ret; | 241 | |
242 | void iwlagn_txq_agg_queue_setup(struct iwl_priv *priv, | ||
243 | struct ieee80211_sta *sta, | ||
244 | int tid, int frame_limit) | ||
245 | { | ||
246 | int sta_id, tx_fifo, txq_id, ssn_idx; | ||
247 | u16 ra_tid; | ||
248 | unsigned long flags; | ||
249 | struct iwl_tid_data *tid_data; | ||
250 | |||
251 | sta_id = iwl_sta_id(sta); | ||
252 | if (WARN_ON(sta_id == IWL_INVALID_STATION)) | ||
253 | return; | ||
254 | if (WARN_ON(tid >= MAX_TID_COUNT)) | ||
255 | return; | ||
256 | |||
257 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
258 | tid_data = &priv->stations[sta_id].tid[tid]; | ||
259 | ssn_idx = SEQ_TO_SN(tid_data->seq_number); | ||
260 | txq_id = tid_data->agg.txq_id; | ||
261 | tx_fifo = tid_data->agg.tx_fifo; | ||
262 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
263 | |||
264 | ra_tid = BUILD_RAxTID(sta_id, tid); | ||
249 | 265 | ||
250 | spin_lock_irqsave(&priv->lock, flags); | 266 | spin_lock_irqsave(&priv->lock, flags); |
251 | 267 | ||
@@ -271,10 +287,10 @@ int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, | |||
271 | iwl_write_targ_mem(priv, priv->scd_base_addr + | 287 | iwl_write_targ_mem(priv, priv->scd_base_addr + |
272 | IWLAGN_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + | 288 | IWLAGN_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + |
273 | sizeof(u32), | 289 | sizeof(u32), |
274 | ((SCD_WIN_SIZE << | 290 | ((frame_limit << |
275 | IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & | 291 | IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & |
276 | IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | | 292 | IWLAGN_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | |
277 | ((SCD_FRAME_LIMIT << | 293 | ((frame_limit << |
278 | IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & | 294 | IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & |
279 | IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); | 295 | IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); |
280 | 296 | ||
@@ -284,12 +300,10 @@ int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, | |||
284 | iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1); | 300 | iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1); |
285 | 301 | ||
286 | spin_unlock_irqrestore(&priv->lock, flags); | 302 | spin_unlock_irqrestore(&priv->lock, flags); |
287 | |||
288 | return 0; | ||
289 | } | 303 | } |
290 | 304 | ||
291 | int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, | 305 | static int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, |
292 | u16 ssn_idx, u8 tx_fifo) | 306 | u16 ssn_idx, u8 tx_fifo) |
293 | { | 307 | { |
294 | if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) || | 308 | if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) || |
295 | (IWLAGN_FIRST_AMPDU_QUEUE + | 309 | (IWLAGN_FIRST_AMPDU_QUEUE + |
@@ -1034,11 +1048,11 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, | |||
1034 | tid_data = &priv->stations[sta_id].tid[tid]; | 1048 | tid_data = &priv->stations[sta_id].tid[tid]; |
1035 | *ssn = SEQ_TO_SN(tid_data->seq_number); | 1049 | *ssn = SEQ_TO_SN(tid_data->seq_number); |
1036 | tid_data->agg.txq_id = txq_id; | 1050 | tid_data->agg.txq_id = txq_id; |
1051 | tid_data->agg.tx_fifo = tx_fifo; | ||
1037 | iwl_set_swq_id(&priv->txq[txq_id], get_ac_from_tid(tid), txq_id); | 1052 | iwl_set_swq_id(&priv->txq[txq_id], get_ac_from_tid(tid), txq_id); |
1038 | spin_unlock_irqrestore(&priv->sta_lock, flags); | 1053 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
1039 | 1054 | ||
1040 | ret = priv->cfg->ops->lib->txq_agg_enable(priv, txq_id, tx_fifo, | 1055 | ret = iwlagn_txq_agg_enable(priv, txq_id, sta_id, tid); |
1041 | sta_id, tid, *ssn); | ||
1042 | if (ret) | 1056 | if (ret) |
1043 | return ret; | 1057 | return ret; |
1044 | 1058 | ||
@@ -1125,8 +1139,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, | |||
1125 | * to deactivate the uCode queue, just return "success" to allow | 1139 | * to deactivate the uCode queue, just return "success" to allow |
1126 | * mac80211 to clean up it own data. | 1140 | * mac80211 to clean up it own data. |
1127 | */ | 1141 | */ |
1128 | priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, ssn, | 1142 | iwlagn_txq_agg_disable(priv, txq_id, ssn, tx_fifo_id); |
1129 | tx_fifo_id); | ||
1130 | spin_unlock_irqrestore(&priv->lock, flags); | 1143 | spin_unlock_irqrestore(&priv->lock, flags); |
1131 | 1144 | ||
1132 | ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); | 1145 | ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); |
@@ -1155,8 +1168,7 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv, | |||
1155 | u16 ssn = SEQ_TO_SN(tid_data->seq_number); | 1168 | u16 ssn = SEQ_TO_SN(tid_data->seq_number); |
1156 | int tx_fifo = get_fifo_from_tid(ctx, tid); | 1169 | int tx_fifo = get_fifo_from_tid(ctx, tid); |
1157 | IWL_DEBUG_HT(priv, "HW queue empty: continue DELBA flow\n"); | 1170 | IWL_DEBUG_HT(priv, "HW queue empty: continue DELBA flow\n"); |
1158 | priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, | 1171 | iwlagn_txq_agg_disable(priv, txq_id, ssn, tx_fifo); |
1159 | ssn, tx_fifo); | ||
1160 | tid_data->agg.state = IWL_AGG_OFF; | 1172 | tid_data->agg.state = IWL_AGG_OFF; |
1161 | ieee80211_stop_tx_ba_cb_irqsafe(ctx->vif, addr, tid); | 1173 | ieee80211_stop_tx_ba_cb_irqsafe(ctx->vif, addr, tid); |
1162 | } | 1174 | } |
@@ -1251,11 +1263,11 @@ static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv, | |||
1251 | struct iwl_compressed_ba_resp *ba_resp) | 1263 | struct iwl_compressed_ba_resp *ba_resp) |
1252 | 1264 | ||
1253 | { | 1265 | { |
1254 | int i, sh, ack; | 1266 | int sh; |
1255 | u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); | 1267 | u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); |
1256 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); | 1268 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); |
1257 | int successes = 0; | ||
1258 | struct ieee80211_tx_info *info; | 1269 | struct ieee80211_tx_info *info; |
1270 | u64 bitmap, sent_bitmap; | ||
1259 | 1271 | ||
1260 | if (unlikely(!agg->wait_for_ba)) { | 1272 | if (unlikely(!agg->wait_for_ba)) { |
1261 | if (unlikely(ba_resp->bitmap)) | 1273 | if (unlikely(ba_resp->bitmap)) |
@@ -1269,70 +1281,42 @@ static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv, | |||
1269 | 1281 | ||
1270 | /* Calculate shift to align block-ack bits with our Tx window bits */ | 1282 | /* Calculate shift to align block-ack bits with our Tx window bits */ |
1271 | sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl >> 4); | 1283 | sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl >> 4); |
1272 | if (sh < 0) /* tbw something is wrong with indices */ | 1284 | if (sh < 0) |
1273 | sh += 0x100; | 1285 | sh += 0x100; |
1274 | 1286 | ||
1275 | if (agg->frame_count > (64 - sh)) { | 1287 | /* |
1276 | IWL_DEBUG_TX_REPLY(priv, "more frames than bitmap size"); | 1288 | * Check for success or failure according to the |
1277 | return -1; | 1289 | * transmitted bitmap and block-ack bitmap |
1278 | } | 1290 | */ |
1279 | if (!priv->cfg->base_params->no_agg_framecnt_info && ba_resp->txed) { | 1291 | bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; |
1292 | sent_bitmap = bitmap & agg->bitmap; | ||
1293 | |||
1294 | /* Sanity check values reported by uCode */ | ||
1295 | if (ba_resp->txed_2_done > ba_resp->txed) { | ||
1296 | IWL_DEBUG_TX_REPLY(priv, | ||
1297 | "bogus sent(%d) and ack(%d) count\n", | ||
1298 | ba_resp->txed, ba_resp->txed_2_done); | ||
1280 | /* | 1299 | /* |
1281 | * sent and ack information provided by uCode | 1300 | * set txed_2_done = txed, |
1282 | * use it instead of figure out ourself | 1301 | * so it won't impact rate scale |
1283 | */ | 1302 | */ |
1284 | if (ba_resp->txed_2_done > ba_resp->txed) { | 1303 | ba_resp->txed = ba_resp->txed_2_done; |
1285 | IWL_DEBUG_TX_REPLY(priv, | 1304 | } |
1286 | "bogus sent(%d) and ack(%d) count\n", | 1305 | IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n", |
1287 | ba_resp->txed, ba_resp->txed_2_done); | 1306 | ba_resp->txed, ba_resp->txed_2_done); |
1288 | /* | ||
1289 | * set txed_2_done = txed, | ||
1290 | * so it won't impact rate scale | ||
1291 | */ | ||
1292 | ba_resp->txed = ba_resp->txed_2_done; | ||
1293 | } | ||
1294 | IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n", | ||
1295 | ba_resp->txed, ba_resp->txed_2_done); | ||
1296 | } else { | ||
1297 | u64 bitmap, sent_bitmap; | ||
1298 | |||
1299 | /* don't use 64-bit values for now */ | ||
1300 | bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; | ||
1301 | |||
1302 | /* check for success or failure according to the | ||
1303 | * transmitted bitmap and block-ack bitmap */ | ||
1304 | sent_bitmap = bitmap & agg->bitmap; | ||
1305 | |||
1306 | /* For each frame attempted in aggregation, | ||
1307 | * update driver's record of tx frame's status. */ | ||
1308 | i = 0; | ||
1309 | while (sent_bitmap) { | ||
1310 | ack = sent_bitmap & 1ULL; | ||
1311 | successes += ack; | ||
1312 | IWL_DEBUG_TX_REPLY(priv, "%s ON i=%d idx=%d raw=%d\n", | ||
1313 | ack ? "ACK" : "NACK", i, | ||
1314 | (agg->start_idx + i) & 0xff, | ||
1315 | agg->start_idx + i); | ||
1316 | sent_bitmap >>= 1; | ||
1317 | ++i; | ||
1318 | } | ||
1319 | 1307 | ||
1320 | IWL_DEBUG_TX_REPLY(priv, "Bitmap %llx\n", | 1308 | /* Find the first ACKed frame to store the TX status */ |
1321 | (unsigned long long)bitmap); | 1309 | while (sent_bitmap && !(sent_bitmap & 1)) { |
1310 | agg->start_idx = (agg->start_idx + 1) & 0xff; | ||
1311 | sent_bitmap >>= 1; | ||
1322 | } | 1312 | } |
1323 | 1313 | ||
1324 | info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb); | 1314 | info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb); |
1325 | memset(&info->status, 0, sizeof(info->status)); | 1315 | memset(&info->status, 0, sizeof(info->status)); |
1326 | info->flags |= IEEE80211_TX_STAT_ACK; | 1316 | info->flags |= IEEE80211_TX_STAT_ACK; |
1327 | info->flags |= IEEE80211_TX_STAT_AMPDU; | 1317 | info->flags |= IEEE80211_TX_STAT_AMPDU; |
1328 | if (!priv->cfg->base_params->no_agg_framecnt_info && ba_resp->txed) { | 1318 | info->status.ampdu_ack_len = ba_resp->txed_2_done; |
1329 | info->status.ampdu_ack_len = ba_resp->txed_2_done; | 1319 | info->status.ampdu_len = ba_resp->txed; |
1330 | info->status.ampdu_len = ba_resp->txed; | ||
1331 | |||
1332 | } else { | ||
1333 | info->status.ampdu_ack_len = successes; | ||
1334 | info->status.ampdu_len = agg->frame_count; | ||
1335 | } | ||
1336 | iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info); | 1320 | iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info); |
1337 | 1321 | ||
1338 | return 0; | 1322 | return 0; |