diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-tx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-tx.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 7296e2846ec3..032641d4c7d1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c | |||
@@ -1350,6 +1350,149 @@ int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id) | |||
1350 | } | 1350 | } |
1351 | EXPORT_SYMBOL(iwl_txq_check_empty); | 1351 | EXPORT_SYMBOL(iwl_txq_check_empty); |
1352 | 1352 | ||
1353 | /** | ||
1354 | * iwl_tx_status_reply_compressed_ba - Update tx status from block-ack | ||
1355 | * | ||
1356 | * Go through block-ack's bitmap of ACK'd frames, update driver's record of | ||
1357 | * ACK vs. not. This gets sent to mac80211, then to rate scaling algo. | ||
1358 | */ | ||
1359 | static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv, | ||
1360 | struct iwl_ht_agg *agg, | ||
1361 | struct iwl_compressed_ba_resp *ba_resp) | ||
1362 | |||
1363 | { | ||
1364 | int i, sh, ack; | ||
1365 | u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); | ||
1366 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); | ||
1367 | u64 bitmap; | ||
1368 | int successes = 0; | ||
1369 | struct ieee80211_tx_info *info; | ||
1370 | |||
1371 | if (unlikely(!agg->wait_for_ba)) { | ||
1372 | IWL_ERROR("Received BA when not expected\n"); | ||
1373 | return -EINVAL; | ||
1374 | } | ||
1375 | |||
1376 | /* Mark that the expected block-ack response arrived */ | ||
1377 | agg->wait_for_ba = 0; | ||
1378 | IWL_DEBUG_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl); | ||
1379 | |||
1380 | /* Calculate shift to align block-ack bits with our Tx window bits */ | ||
1381 | sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl>>4); | ||
1382 | if (sh < 0) /* tbw something is wrong with indices */ | ||
1383 | sh += 0x100; | ||
1384 | |||
1385 | /* don't use 64-bit values for now */ | ||
1386 | bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; | ||
1387 | |||
1388 | if (agg->frame_count > (64 - sh)) { | ||
1389 | IWL_DEBUG_TX_REPLY("more frames than bitmap size"); | ||
1390 | return -1; | ||
1391 | } | ||
1392 | |||
1393 | /* check for success or failure according to the | ||
1394 | * transmitted bitmap and block-ack bitmap */ | ||
1395 | bitmap &= agg->bitmap; | ||
1396 | |||
1397 | /* For each frame attempted in aggregation, | ||
1398 | * update driver's record of tx frame's status. */ | ||
1399 | for (i = 0; i < agg->frame_count ; i++) { | ||
1400 | ack = bitmap & (1 << i); | ||
1401 | successes += !!ack; | ||
1402 | IWL_DEBUG_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", | ||
1403 | ack? "ACK":"NACK", i, (agg->start_idx + i) & 0xff, | ||
1404 | agg->start_idx + i); | ||
1405 | } | ||
1406 | |||
1407 | info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb[0]); | ||
1408 | memset(&info->status, 0, sizeof(info->status)); | ||
1409 | info->flags = IEEE80211_TX_STAT_ACK; | ||
1410 | info->flags |= IEEE80211_TX_STAT_AMPDU; | ||
1411 | info->status.ampdu_ack_map = successes; | ||
1412 | info->status.ampdu_ack_len = agg->frame_count; | ||
1413 | iwl_hwrate_to_tx_control(priv, agg->rate_n_flags, info); | ||
1414 | |||
1415 | IWL_DEBUG_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap); | ||
1416 | |||
1417 | return 0; | ||
1418 | } | ||
1419 | |||
1420 | /** | ||
1421 | * iwl_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA | ||
1422 | * | ||
1423 | * Handles block-acknowledge notification from device, which reports success | ||
1424 | * of frames sent via aggregation. | ||
1425 | */ | ||
1426 | void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, | ||
1427 | struct iwl_rx_mem_buffer *rxb) | ||
1428 | { | ||
1429 | struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; | ||
1430 | struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; | ||
1431 | int index; | ||
1432 | struct iwl_tx_queue *txq = NULL; | ||
1433 | struct iwl_ht_agg *agg; | ||
1434 | DECLARE_MAC_BUF(mac); | ||
1435 | |||
1436 | /* "flow" corresponds to Tx queue */ | ||
1437 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); | ||
1438 | |||
1439 | /* "ssn" is start of block-ack Tx window, corresponds to index | ||
1440 | * (in Tx queue's circular buffer) of first TFD/frame in window */ | ||
1441 | u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); | ||
1442 | |||
1443 | if (scd_flow >= priv->hw_params.max_txq_num) { | ||
1444 | IWL_ERROR("BUG_ON scd_flow is bigger than number of queues"); | ||
1445 | return; | ||
1446 | } | ||
1447 | |||
1448 | txq = &priv->txq[scd_flow]; | ||
1449 | agg = &priv->stations[ba_resp->sta_id].tid[ba_resp->tid].agg; | ||
1450 | |||
1451 | /* Find index just before block-ack window */ | ||
1452 | index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); | ||
1453 | |||
1454 | /* TODO: Need to get this copy more safely - now good for debug */ | ||
1455 | |||
1456 | IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA [%d]Received from %s, " | ||
1457 | "sta_id = %d\n", | ||
1458 | agg->wait_for_ba, | ||
1459 | print_mac(mac, (u8 *) &ba_resp->sta_addr_lo32), | ||
1460 | ba_resp->sta_id); | ||
1461 | IWL_DEBUG_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = " | ||
1462 | "%d, scd_ssn = %d\n", | ||
1463 | ba_resp->tid, | ||
1464 | ba_resp->seq_ctl, | ||
1465 | (unsigned long long)le64_to_cpu(ba_resp->bitmap), | ||
1466 | ba_resp->scd_flow, | ||
1467 | ba_resp->scd_ssn); | ||
1468 | IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx \n", | ||
1469 | agg->start_idx, | ||
1470 | (unsigned long long)agg->bitmap); | ||
1471 | |||
1472 | /* Update driver's record of ACK vs. not for each frame in window */ | ||
1473 | iwl_tx_status_reply_compressed_ba(priv, agg, ba_resp); | ||
1474 | |||
1475 | /* Release all TFDs before the SSN, i.e. all TFDs in front of | ||
1476 | * block-ack window (we assume that they've been successfully | ||
1477 | * transmitted ... if not, it's too late anyway). */ | ||
1478 | if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) { | ||
1479 | /* calculate mac80211 ampdu sw queue to wake */ | ||
1480 | int ampdu_q = | ||
1481 | scd_flow - priv->hw_params.first_ampdu_q + priv->hw->queues; | ||
1482 | int freed = iwl_tx_queue_reclaim(priv, scd_flow, index); | ||
1483 | priv->stations[ba_resp->sta_id]. | ||
1484 | tid[ba_resp->tid].tfds_in_queue -= freed; | ||
1485 | if (iwl_queue_space(&txq->q) > txq->q.low_mark && | ||
1486 | priv->mac80211_registered && | ||
1487 | agg->state != IWL_EMPTYING_HW_QUEUE_DELBA) | ||
1488 | ieee80211_wake_queue(priv->hw, ampdu_q); | ||
1489 | |||
1490 | iwl_txq_check_empty(priv, ba_resp->sta_id, | ||
1491 | ba_resp->tid, scd_flow); | ||
1492 | } | ||
1493 | } | ||
1494 | EXPORT_SYMBOL(iwl_rx_reply_compressed_ba); | ||
1495 | |||
1353 | #ifdef CONFIG_IWLWIF_DEBUG | 1496 | #ifdef CONFIG_IWLWIF_DEBUG |
1354 | #define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x | 1497 | #define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x |
1355 | 1498 | ||