|
@@ -638,7 +638,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int iwlagn_txq_check_empty(struct iwl_priv *priv,
|
|
|
+static int iwlagn_txq_check_empty(struct iwl_priv *priv,
|
|
|
int sta_id, u8 tid, int txq_id)
|
|
|
{
|
|
|
struct iwl_queue *q = &priv->txq[txq_id].q;
|
|
@@ -700,7 +700,7 @@ static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
|
|
|
/**
|
|
|
* translate ucode response to mac80211 tx status control values
|
|
|
*/
|
|
|
-void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
|
|
|
+static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
|
|
|
struct ieee80211_tx_info *info)
|
|
|
{
|
|
|
struct ieee80211_tx_rate *r = &info->control.rates[0];
|
|
@@ -760,6 +760,53 @@ const char *iwl_get_tx_fail_reason(u32 status)
|
|
|
}
|
|
|
#endif /* CONFIG_IWLWIFI_DEBUG */
|
|
|
|
|
|
+static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
|
|
|
+{
|
|
|
+ status &= AGG_TX_STATUS_MSK;
|
|
|
+
|
|
|
+ switch (status) {
|
|
|
+ case AGG_TX_STATE_UNDERRUN_MSK:
|
|
|
+ priv->reply_agg_tx_stats.underrun++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_BT_PRIO_MSK:
|
|
|
+ priv->reply_agg_tx_stats.bt_prio++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_FEW_BYTES_MSK:
|
|
|
+ priv->reply_agg_tx_stats.few_bytes++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_ABORT_MSK:
|
|
|
+ priv->reply_agg_tx_stats.abort++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_LAST_SENT_TTL_MSK:
|
|
|
+ priv->reply_agg_tx_stats.last_sent_ttl++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK:
|
|
|
+ priv->reply_agg_tx_stats.last_sent_try++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK:
|
|
|
+ priv->reply_agg_tx_stats.last_sent_bt_kill++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_SCD_QUERY_MSK:
|
|
|
+ priv->reply_agg_tx_stats.scd_query++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_TEST_BAD_CRC32_MSK:
|
|
|
+ priv->reply_agg_tx_stats.bad_crc32++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_RESPONSE_MSK:
|
|
|
+ priv->reply_agg_tx_stats.response++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_DUMP_TX_MSK:
|
|
|
+ priv->reply_agg_tx_stats.dump_tx++;
|
|
|
+ break;
|
|
|
+ case AGG_TX_STATE_DELAY_TX_MSK:
|
|
|
+ priv->reply_agg_tx_stats.delay_tx++;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ priv->reply_agg_tx_stats.unknown++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
|
|
|
struct iwlagn_tx_resp *tx_resp)
|
|
|
{
|
|
@@ -808,12 +855,158 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
|
+#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x
|
|
|
+
|
|
|
+const char *iwl_get_agg_tx_fail_reason(u16 status)
|
|
|
+{
|
|
|
+ status &= AGG_TX_STATUS_MSK;
|
|
|
+ switch (status) {
|
|
|
+ case AGG_TX_STATE_TRANSMITTED:
|
|
|
+ return "SUCCESS";
|
|
|
+ AGG_TX_STATE_FAIL(UNDERRUN_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(BT_PRIO_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(FEW_BYTES_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(ABORT_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(SCD_QUERY_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(RESPONSE_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(DUMP_TX_MSK);
|
|
|
+ AGG_TX_STATE_FAIL(DELAY_TX_MSK);
|
|
|
+ }
|
|
|
+
|
|
|
+ return "UNKNOWN";
|
|
|
+}
|
|
|
+#endif /* CONFIG_IWLWIFI_DEBUG */
|
|
|
+
|
|
|
static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp)
|
|
|
{
|
|
|
return le32_to_cpup((__le32 *)&tx_resp->status +
|
|
|
tx_resp->frame_count) & MAX_SN;
|
|
|
}
|
|
|
|
|
|
+static void iwl_free_tfds_in_queue(struct iwl_priv *priv,
|
|
|
+ int sta_id, int tid, int freed)
|
|
|
+{
|
|
|
+ lockdep_assert_held(&priv->shrd->sta_lock);
|
|
|
+
|
|
|
+ if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed)
|
|
|
+ priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
|
|
|
+ else {
|
|
|
+ IWL_DEBUG_TX(priv, "free more than tfds_in_queue (%u:%d)\n",
|
|
|
+ priv->stations[sta_id].tid[tid].tfds_in_queue,
|
|
|
+ freed);
|
|
|
+ priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status)
|
|
|
+{
|
|
|
+ status &= TX_STATUS_MSK;
|
|
|
+
|
|
|
+ switch (status) {
|
|
|
+ case TX_STATUS_POSTPONE_DELAY:
|
|
|
+ priv->reply_tx_stats.pp_delay++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_POSTPONE_FEW_BYTES:
|
|
|
+ priv->reply_tx_stats.pp_few_bytes++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_POSTPONE_BT_PRIO:
|
|
|
+ priv->reply_tx_stats.pp_bt_prio++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_POSTPONE_QUIET_PERIOD:
|
|
|
+ priv->reply_tx_stats.pp_quiet_period++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_POSTPONE_CALC_TTAK:
|
|
|
+ priv->reply_tx_stats.pp_calc_ttak++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
|
|
|
+ priv->reply_tx_stats.int_crossed_retry++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_SHORT_LIMIT:
|
|
|
+ priv->reply_tx_stats.short_limit++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_LONG_LIMIT:
|
|
|
+ priv->reply_tx_stats.long_limit++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_FIFO_UNDERRUN:
|
|
|
+ priv->reply_tx_stats.fifo_underrun++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_DRAIN_FLOW:
|
|
|
+ priv->reply_tx_stats.drain_flow++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_RFKILL_FLUSH:
|
|
|
+ priv->reply_tx_stats.rfkill_flush++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_LIFE_EXPIRE:
|
|
|
+ priv->reply_tx_stats.life_expire++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_DEST_PS:
|
|
|
+ priv->reply_tx_stats.dest_ps++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_HOST_ABORTED:
|
|
|
+ priv->reply_tx_stats.host_abort++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_BT_RETRY:
|
|
|
+ priv->reply_tx_stats.bt_retry++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_STA_INVALID:
|
|
|
+ priv->reply_tx_stats.sta_invalid++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_FRAG_DROPPED:
|
|
|
+ priv->reply_tx_stats.frag_drop++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_TID_DISABLE:
|
|
|
+ priv->reply_tx_stats.tid_disable++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_FIFO_FLUSHED:
|
|
|
+ priv->reply_tx_stats.fifo_flush++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL:
|
|
|
+ priv->reply_tx_stats.insuff_cf_poll++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_PASSIVE_NO_RX:
|
|
|
+ priv->reply_tx_stats.fail_hw_drop++;
|
|
|
+ break;
|
|
|
+ case TX_STATUS_FAIL_NO_BEACON_ON_RADAR:
|
|
|
+ priv->reply_tx_stats.sta_color_mismatch++;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ priv->reply_tx_stats.unknown++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void iwlagn_set_tx_status(struct iwl_priv *priv,
|
|
|
+ struct ieee80211_tx_info *info,
|
|
|
+ struct iwlagn_tx_resp *tx_resp,
|
|
|
+ bool is_agg)
|
|
|
+{
|
|
|
+ u16 status = le16_to_cpu(tx_resp->status.status);
|
|
|
+
|
|
|
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
|
|
|
+ if (is_agg)
|
|
|
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
|
|
|
+ info->flags |= iwl_tx_status_to_mac80211(status);
|
|
|
+ iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
|
|
|
+ info);
|
|
|
+ if (!iwl_is_tx_success(status))
|
|
|
+ iwlagn_count_tx_err_status(priv, status);
|
|
|
+}
|
|
|
+
|
|
|
+static void iwl_check_abort_status(struct iwl_priv *priv,
|
|
|
+ u8 frame_count, u32 status)
|
|
|
+{
|
|
|
+ if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
|
|
|
+ IWL_ERR(priv, "Tx flush command to flush out all frames\n");
|
|
|
+ if (!test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
|
|
|
+ queue_work(priv->shrd->workqueue, &priv->tx_flush);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
|
|
|
{
|
|
|
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|