/* DATA packets will use the uCode station table for rate/antenna
* selection */ if (ieee80211_is_data(fc)) {
tx_cmd->initial_rate_index = 0;
tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; return;
} elseif (ieee80211_is_back_req(fc))
tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
/** *IfthecurrentTXratestoredinmac80211hastheMCSbitset,it's *notreallyaTXrate.Thus,weusethelowestsupportedratefor *thisband.Alsousethelowestsupportedrateifthestoredrate *indexisinvalid.
*/
rate_idx = info->control.rates[0].idx; if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS ||
(rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY))
rate_idx = rate_lowest_index(
&priv->nvm_data->bands[info->band], sta); /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ if (info->band == NL80211_BAND_5GHZ)
rate_idx += IWL_FIRST_OFDM_RATE; /* Get PLCP rate for tx_cmd->rate_n_flags */
rate_plcp = iwl_rates[rate_idx].plcp; /* Zero out flags for this packet */
rate_flags = 0;
/* Set CCK flag as needed */ if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
rate_flags |= RATE_MCS_CCK_MSK;
/* Set up antennas */ if (priv->lib->bt_params &&
priv->lib->bt_params->advanced_bt_coexist &&
priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
first_antenna(priv->nvm_data->valid_tx_ant));
} else
priv->mgmt_tx_ant = iwl_toggle_tx_ant(
priv, priv->mgmt_tx_ant,
priv->nvm_data->valid_tx_ant);
rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
/* Set the rate in the TX cmd */
tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags);
}
/* For management frames use broadcast id to do not break aggregation */ if (!ieee80211_is_data(fc))
sta_id = ctx->bcast_sta_id; else { /* Find index into station table for destination station */
sta_id = iwl_sta_id_or_broadcast(ctx, sta); if (sta_id == IWL_INVALID_STATION) {
IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
hdr->addr1); goto drop_unlock_priv;
}
}
info->driver_data[0] = ctx;
info->driver_data[1] = dev_cmd; /* From now on, we cannot access info->control */
spin_lock(&priv->sta_lock);
if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
u8 *qc = NULL; struct iwl_tid_data *tid_data;
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) goto drop_unlock_sta;
tid_data = &priv->tid_data[sta_id][tid];
/* aggregation is on for this <sta,tid> */ if (info->flags & IEEE80211_TX_CTL_AMPDU &&
tid_data->agg.state != IWL_AGG_ON) {
IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n",
info->flags, tid_data->agg.state);
IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n",
sta_id, tid,
IEEE80211_SEQ_TO_SN(tid_data->seq_number)); goto drop_unlock_sta;
}
/* We can receive packets from the stack in IWL_AGG_{ON,OFF} *only.Checkthishere.
*/ if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON &&
tid_data->agg.state != IWL_AGG_OFF, "Tx while agg.state = %d\n", tid_data->agg.state)) goto drop_unlock_sta;
switch (tid_data->agg.state) { case IWL_EMPTYING_HW_QUEUE_ADDBA: /* *Thiscanhappenifthepeerstopsaggregation *againbeforewe'vehadachancetodrainthe *queueweselectedpreviously,i.e.beforethe *sessionwasreallystartedcompletely.
*/
IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); goto turn_off; case IWL_AGG_STARTING: /* *Thiscanhappenwhenthesessionisstoppedbefore *wereceiveADDBAresponse
*/
IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n"); goto turn_off; case IWL_AGG_ON: break; default:
IWL_WARN(priv, "Stopping AGG while state not ON or starting for %d on %d (%d)\n",
sta_id, tid, tid_data->agg.state);
spin_unlock_bh(&priv->sta_lock); return0;
}
/* There are still packets for this RA / TID in the HW */ if (!test_bit(txq_id, priv->agg_q_alloc)) {
IWL_DEBUG_TX_QUEUES(priv, "stopping AGG on STA/TID %d/%d but hwq %d not used\n",
sta_id, tid, txq_id);
} elseif (tid_data->agg.ssn != tid_data->next_reclaimed) {
IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, next_recl = %d\n",
tid_data->agg.ssn,
tid_data->next_reclaimed);
tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_DELBA;
spin_unlock_bh(&priv->sta_lock); return0;
}
switch (priv->tid_data[sta_id][tid].agg.state) { case IWL_EMPTYING_HW_QUEUE_DELBA: /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) {
IWL_DEBUG_TX_QUEUES(priv, "Can continue DELBA flow ssn = next_recl = %d\n",
tid_data->next_reclaimed);
iwl_trans_txq_disable(priv->trans,
tid_data->agg.txq_id, true);
iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id);
tid_data->agg.state = IWL_AGG_OFF;
ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
} break; case IWL_EMPTYING_HW_QUEUE_ADDBA: /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) {
IWL_DEBUG_TX_QUEUES(priv, "Can continue ADDBA flow ssn = next_recl = %d\n",
tid_data->next_reclaimed);
tid_data->agg.state = IWL_AGG_STARTING;
ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
} break; default: break;
}
}
staticvoid 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;
}
}
staticvoid 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;
}
}
if (is_agg) { /* If this is an aggregation queue, we can rely on the *ssnsincethewifisequencenumbercorrespondsto *theindexintheTFDring(%256). *Theseq_ctlisthesequencecontrolofthepacket *towhichthisTxresponserelates.Butifthereisa *holeinthebitmapoftheBAwereceived,thisTx *responsemayallowtoreclaimtheholeandallthe *subsequentpacketsthatwerealreadyacked. *Inthatcase,seq_ctl!=ssn,andthenextpacket *tobereclaimedwillbessnandnotseq_ctl.
*/
next_reclaimed = ssn;
}
/* check if BAR is needed */ if (is_agg && !iwl_is_tx_success(status))
info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb),
tx_resp); if (!is_agg)
iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
/* "ssn" is start of block-ack Tx window, corresponds to index
* (in Tx queue's circular buffer) of first TFD/frame in window */
u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
if (scd_flow >= priv->trans->mac_cfg->base->num_of_queues) {
IWL_ERR(priv, "BUG_ON scd_flow is bigger than number of queues\n"); return;
}
sta_id = ba_resp->sta_id;
tid = ba_resp->tid;
agg = &priv->tid_data[sta_id][tid].agg;
spin_lock_bh(&priv->sta_lock);
if (unlikely(!agg->wait_for_ba)) { if (unlikely(ba_resp->bitmap))
IWL_ERR(priv, "Received BA when not expected\n");
spin_unlock_bh(&priv->sta_lock); return;
}
/* Release all TFDs before the SSN, i.e. all TFDs in front of *block-ackwindow(weassumethatthey'vebeensuccessfully
* transmitted ... if not, it's too late anyway). */
iwl_trans_reclaim(priv->trans, scd_flow, ba_resp_scd_ssn,
&reclaimed_skbs, false);
memset(&info->status, 0, sizeof(info->status)); /* Packet was transmitted successfully, failures come as single *framesbecausebeforefailingaframethefirmwaretransmits *itwithoutaggregationatleastonce.
*/
info->flags |= IEEE80211_TX_STAT_ACK;
if (freed == 1) { /* this is the first skb we deliver in this batch */ /* put the rate scaling data there */
info = IEEE80211_SKB_CB(skb);
memset(&info->status, 0, sizeof(info->status));
info->flags |= IEEE80211_TX_STAT_AMPDU;
info->status.ampdu_ack_len = ba_resp->txed_2_done;
info->status.ampdu_len = ba_resp->txed;
iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags,
info);
}
}
spin_unlock_bh(&priv->sta_lock);
while (!skb_queue_empty(&reclaimed_skbs)) {
skb = __skb_dequeue(&reclaimed_skbs);
ieee80211_tx_status_skb(priv->hw, skb);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.185 Sekunden
(vorverarbeitet am 2026-10-12)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.