/* 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;
/** * If the current TX rate stored in mac80211 has the MCS bit set, it's * not really a TX rate. Thus, we use the lowest supported rate for * this band. Also use the lowest supported rate if the stored rate * index is invalid.
*/
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);
}
/** * iwl_sta_id_or_broadcast - return sta_id or broadcast sta * @context: the current context * @sta: mac80211 station * * In certain circumstances mac80211 passes a station pointer * that may be %NULL, for example during TX or key setup. In * that case, we need to use the broadcast station, so this * inline wraps that pattern. * * Return: station ID for mac80211 station (or broadcast if %NULL)
*/ staticint iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, struct ieee80211_sta *sta)
{ int sta_id;
if (!sta) return context->bcast_sta_id;
sta_id = iwl_sta_id(sta);
/* * mac80211 should not be passing a partially * initialised station!
*/
WARN_ON(sta_id == IWL_INVALID_STATION);
/* 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;
}
}
if (sta)
sta_priv = (void *)sta->drv_priv;
if (sta_priv && sta_priv->asleep &&
(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the * next frame is processed -- and the next frame to * this station is the one that will consume this * counter. * For now set the counter to just 1 since we do not * support uAPSD yet. * * FIXME: If we get two non-bufferable frames one * after the other, we might only send out one of * them because this is racy.
*/
iwl_sta_modify_sleep_tx_count(priv, sta_id, 1);
}
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. Check this here.
*/ 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;
/* Copy MAC header from skb into command buffer */
memcpy(tx_cmd->hdr, hdr, hdr_len);
txq_id = info->hw_queue;
if (is_agg)
txq_id = priv->tid_data[sta_id][tid].agg.txq_id; elseif (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* * The microcode will clear the more data * bit in the last frame it transmits.
*/
hdr->frame_control |=
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id)) goto drop_unlock_sta;
if (is_data_qos && !ieee80211_has_morefrags(fc))
priv->tid_data[sta_id][tid].seq_number = seq_number;
spin_unlock(&priv->sta_lock);
/* * Avoid atomic ops if it isn't an associated client. * Also, if this is a packet for aggregation, don't * increase the counter because the ucode will stop * aggregation queues when their respective station * goes to sleep.
*/ if (sta_priv && sta_priv->client && !is_agg)
atomic_inc(&sta_priv->pending_frames);
return 0;
drop_unlock_sta: if (dev_cmd)
iwl_trans_free_tx_cmd(priv->trans, dev_cmd);
spin_unlock(&priv->sta_lock);
drop_unlock_priv: return -1;
}
staticint iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq)
{ int q;
for (q = IWLAGN_FIRST_AMPDU_QUEUE;
q < priv->trans->mac_cfg->base->num_of_queues; q++) { if (!test_and_set_bit(q, priv->agg_q_alloc)) {
priv->queue_to_mac80211[q] = mq; return q;
}
}
switch (tid_data->agg.state) { case IWL_EMPTYING_HW_QUEUE_ADDBA: /* * This can happen if the peer stops aggregation * again before we've had a chance to drain the * queue we selected previously, i.e. before the * session was really started completely.
*/
IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); goto turn_off; case IWL_AGG_STARTING: /* * This can happen when the session is stopped before * we receive ADDBA response
*/
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); return 0;
}
/* 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); return 0;
}
if (test_bit(txq_id, priv->agg_q_alloc)) { /* * If the transport didn't know that we wanted to start * agreggation, don't tell it that we want to stop them. * This can happen when we don't get the addBA response on * time, or we hadn't time to drain the AC queues.
*/ if (agg_state == IWL_AGG_ON)
iwl_trans_txq_disable(priv->trans, txq_id, true); else
IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n",
agg_state);
iwlagn_dealloc_agg_txq(priv, txq_id);
}
/* * First set the agg state to OFF to avoid calling * ieee80211_stop_tx_ba_cb in iwlagn_check_ratid_empty.
*/
spin_lock_bh(&priv->sta_lock);
tid_data = &priv->tid_data[sta_id][tid];
txq_id = tid_data->agg.txq_id;
agg_state = tid_data->agg.state;
IWL_DEBUG_TX_QUEUES(priv, "Flush AGG: sta %d tid %d q %d state %d\n",
sta_id, tid, txq_id, tid_data->agg.state);
tid_data->agg.state = IWL_AGG_OFF;
spin_unlock_bh(&priv->sta_lock);
if (iwlagn_txfifo_flush(priv, BIT(txq_id)))
IWL_ERR(priv, "Couldn't flush the AGG queue\n");
if (test_bit(txq_id, priv->agg_q_alloc)) { /* * If the transport didn't know that we wanted to start * agreggation, don't tell it that we want to stop them. * This can happen when we don't get the addBA response on * time, or we hadn't time to drain the AC queues.
*/ if (agg_state == IWL_AGG_ON)
iwl_trans_txq_disable(priv->trans, txq_id, true); else
IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n",
agg_state);
iwlagn_dealloc_agg_txq(priv, txq_id);
}
iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid,
buf_size, ssn, 0);
/* * If the limit is 0, then it wasn't initialised yet, * use the default. We can do that since we take the * minimum below, and we don't want to go above our * default due to hardware restrictions.
*/ if (sta_priv->max_agg_bufsize == 0)
sta_priv->max_agg_bufsize =
LINK_QUAL_AGG_FRAME_LIMIT_DEF;
/* * Even though in theory the peer could have different * aggregation reorder buffer sizes for different sessions, * our ucode doesn't allow for that and has a global limit * for each station. Therefore, use the minimum of all the * aggregation sessions and our default value.
*/
sta_priv->max_agg_bufsize =
min(sta_priv->max_agg_bufsize, buf_size);
if (priv->hw_params.use_rts_for_aggregation) { /* * switch to RTS/CTS if it is the prefer protection * method for HT traffic
*/
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 * ssn since the wifi sequence number corresponds to * the index in the TFD ring (%256). * The seq_ctl is the sequence control of the packet * to which this Tx response relates. But if there is a * hole in the bitmap of the BA we received, this Tx * response may allow to reclaim the hole and all the * subsequent packets that were already acked. * In that case, seq_ctl != ssn, and the next packet * to be reclaimed will be ssn and not seq_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;
}
if (unlikely(scd_flow != agg->txq_id)) { /* * FIXME: this is a uCode bug which need to be addressed, * log the information and return for now. * Since it is can possibly happen very often and in order * not to fill the syslog, don't use IWL_ERR or IWL_WARN
*/
IWL_DEBUG_TX_QUEUES(priv, "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n",
scd_flow, sta_id, tid, agg->txq_id);
spin_unlock_bh(&priv->sta_lock); return;
}
__skb_queue_head_init(&reclaimed_skbs);
/* Release all TFDs before the SSN, i.e. all TFDs in front of * block-ack window (we assume that they've been successfully
* 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 * frames because before failing a frame the firmware transmits * it without aggregation at least once.
*/
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);
}
}
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.