// SPDX-License-Identifier: GPL-2.0-only /****************************************************************************** * * Copyright(c) 2003 - 2014, 2022 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files.
*****************************************************************************/ #include <linux/etherdevice.h> #include <net/mac80211.h> #include"iwl-trans.h" #include"dev.h" #include"agn.h"
if (sta_id >= IWLAGN_STATION_COUNT) {
IWL_ERR(priv, "invalid sta_id %u\n", sta_id); return -EINVAL;
} if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u " "addr %pM\n",
sta_id, priv->stations[sta_id].sta.sta.addr);
if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) {
IWL_DEBUG_ASSOC(priv, "STA id %u addr %pM already present in uCode " "(according to driver)\n",
sta_id, priv->stations[sta_id].sta.sta.addr);
} else {
priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE;
IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n",
sta_id, priv->stations[sta_id].sta.sta.addr);
} return 0;
}
IWL_DEBUG_INFO(priv, "Processing response for adding station\n");
spin_lock_bh(&priv->sta_lock);
switch (add_sta_resp->status) { case ADD_STA_SUCCESS_MSK:
IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); break; case ADD_STA_NO_ROOM_IN_TABLE:
IWL_ERR(priv, "Adding station failed, no room in table.\n"); break; case ADD_STA_NO_BLOCK_ACK_RESOURCE:
IWL_ERR(priv, "Adding station failed, no block ack resource.\n"); break; case ADD_STA_MODIFY_NON_EXIST_STA:
IWL_ERR(priv, "Attempting to modify non-existing station\n"); break; default:
IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n",
add_sta_resp->status); break;
}
/* debug messages are printed in the handler */ if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) {
spin_lock_bh(&priv->sta_lock);
ret = iwl_sta_ucode_activate(priv, sta_id);
spin_unlock_bh(&priv->sta_lock);
} else {
ret = -EIO;
}
/* * iwl_prep_station - Prepare station information for addition * * should be called with sta_lock held
*/
u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, bool is_ap, struct ieee80211_sta *sta)
{ struct iwl_station_entry *station; int i;
u8 sta_id = IWL_INVALID_STATION;
if (is_ap)
sta_id = ctx->ap_sta_id; elseif (is_broadcast_ether_addr(addr))
sta_id = ctx->bcast_sta_id; else for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) { if (ether_addr_equal(priv->stations[i].sta.sta.addr,
addr)) {
sta_id = i; break;
}
if (!priv->stations[i].used &&
sta_id == IWL_INVALID_STATION)
sta_id = i;
}
/* * These two conditions have the same outcome, but keep them * separate
*/ if (unlikely(sta_id == IWL_INVALID_STATION)) return sta_id;
/* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another.
*/ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
IWL_DEBUG_INFO(priv, "STA %d already in process of being " "added.\n", sta_id); return sta_id;
}
station = &priv->stations[sta_id];
station->used = IWL_STA_DRIVER_ACTIVE;
IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
sta_id, addr);
priv->num_stations++;
/* Set up the REPLY_ADD_STA command to send to device */
memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
memcpy(station->sta.sta.addr, addr, ETH_ALEN);
station->sta.mode = 0;
station->sta.sta.sta_id = sta_id;
station->sta.station_flags = ctx->station_flags;
station->ctxid = ctx->ctxid;
/* * OK to call unconditionally, since local stations (IBSS BSSID * STA and broadcast STA) pass in a NULL sta, and mac80211 * doesn't allow HT IBSS.
*/
iwl_set_ht_add_station(priv, sta_id, sta, ctx);
*sta_id_r = 0;
spin_lock_bh(&priv->sta_lock);
sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); if (sta_id == IWL_INVALID_STATION) {
IWL_ERR(priv, "Unable to prepare station %pM for addition\n",
addr);
spin_unlock_bh(&priv->sta_lock); return -EINVAL;
}
/* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another.
*/ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
IWL_DEBUG_INFO(priv, "STA %d already in process of being " "added.\n", sta_id);
spin_unlock_bh(&priv->sta_lock); return -EEXIST;
}
/* Add station to device's station table */
ret = iwl_send_add_sta(priv, &sta_cmd, 0); if (ret) {
spin_lock_bh(&priv->sta_lock);
IWL_ERR(priv, "Adding station %pM failed.\n",
priv->stations[sta_id].sta.sta.addr);
priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
spin_unlock_bh(&priv->sta_lock);
}
*sta_id_r = sta_id; return ret;
}
/* * iwl_sta_ucode_deactivate - deactivate ucode status for a station
*/ staticvoid iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id)
{
lockdep_assert_held(&priv->sta_lock);
/* Ucode must be active and driver must be non active */ if ((priv->stations[sta_id].used &
(IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) !=
IWL_STA_UCODE_ACTIVE)
IWL_ERR(priv, "removed non active STA %u\n", sta_id);
if (!iwl_is_ready(priv)) {
IWL_DEBUG_INFO(priv, "Unable to remove station %pM, device not ready.\n",
addr); /* * It is typical for stations to be removed when we are * going down. Return success since device will be down * soon anyway
*/ return 0;
}
IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n",
sta_id, addr);
if (WARN_ON(sta_id == IWL_INVALID_STATION)) return -EINVAL;
spin_lock_bh(&priv->sta_lock);
if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
addr); goto out_err;
}
if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
addr); goto out_err;
}
if (priv->stations[sta_id].used & IWL_STA_LOCAL) {
kfree(priv->stations[sta_id].lq);
priv->stations[sta_id].lq = NULL;
}
for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
memset(&priv->tid_data[sta_id][tid], 0, sizeof(priv->tid_data[sta_id][tid]));
/* Set up the rate scaling to start at selected rate, fall back
* all the way down to 1M in IEEE order, and then spin on 1M */ if (priv->band == NL80211_BAND_5GHZ)
r = IWL_RATE_6M_INDEX; elseif (ctx && ctx->vif && ctx->vif->p2p)
r = IWL_RATE_6M_INDEX; else
r = IWL_RATE_1M_INDEX;
if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
rate_flags |= RATE_MCS_CCK_MSK;
rate_flags |= first_antenna(priv->nvm_data->valid_tx_ant) <<
RATE_MCS_ANT_POS;
rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
/* * iwl_clear_ucode_stations - clear ucode station table bits * * This function clears all the bits in the driver indicating * which stations are active in the ucode. Call when something * other than explicit station management would cause this in * the ucode, e.g. unassociated RXON.
*/ void iwl_clear_ucode_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{ int i; bool cleared = false;
IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n");
spin_lock_bh(&priv->sta_lock); for (i = 0; i < IWLAGN_STATION_COUNT; i++) { if (ctx && ctx->ctxid != priv->stations[i].ctxid) continue;
if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) {
IWL_DEBUG_INFO(priv, "Clearing ucode active for station %d\n", i);
priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
cleared = true;
}
}
spin_unlock_bh(&priv->sta_lock);
if (!cleared)
IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n");
}
/* * iwl_restore_stations() - Restore driver known stations to device * * All stations considered active by driver, but not present in ucode, is * restored. * * Function sleeps.
*/ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{ struct iwl_addsta_cmd sta_cmd; staticconststruct iwl_link_quality_cmd zero_lq = {}; struct iwl_link_quality_cmd lq; int i; bool found = false; int ret; bool send_lq;
if (!iwl_is_ready(priv)) {
IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n"); return;
}
IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n");
spin_lock_bh(&priv->sta_lock); for (i = 0; i < IWLAGN_STATION_COUNT; i++) { if (ctx->ctxid != priv->stations[i].ctxid) continue; if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) &&
!(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) {
IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n",
priv->stations[i].sta.sta.addr);
priv->stations[i].sta.mode = 0;
priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS;
found = true;
}
}
for (i = 0; i < IWLAGN_STATION_COUNT; i++) { if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) {
memcpy(&sta_cmd, &priv->stations[i].sta, sizeof(struct iwl_addsta_cmd));
send_lq = false; if (priv->stations[i].lq) { if (priv->wowlan)
iwl_sta_fill_lq(priv, ctx, i, &lq); else
memcpy(&lq, priv->stations[i].lq, sizeof(struct iwl_link_quality_cmd));
if (memcmp(&lq, &zero_lq, sizeof(lq)))
send_lq = true;
}
spin_unlock_bh(&priv->sta_lock);
ret = iwl_send_add_sta(priv, &sta_cmd, 0); if (ret) {
spin_lock_bh(&priv->sta_lock);
IWL_ERR(priv, "Adding station %pM failed.\n",
priv->stations[i].sta.sta.addr);
priv->stations[i].used &=
~IWL_STA_DRIVER_ACTIVE;
priv->stations[i].used &=
~IWL_STA_UCODE_INPROGRESS; continue;
} /* * Rate scaling has already been initialized, send * current LQ command
*/ if (send_lq)
iwl_send_lq_cmd(priv, ctx, &lq, 0, true);
spin_lock_bh(&priv->sta_lock);
priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
}
}
spin_unlock_bh(&priv->sta_lock); if (!found)
IWL_DEBUG_INFO(priv, "Restoring all known stations .... " "no stations to be restored.\n"); else
IWL_DEBUG_INFO(priv, "Restoring all known stations .... " "complete.\n");
}
int iwl_get_free_ucode_key_offset(struct iwl_priv *priv)
{ int i;
for (i = 0; i < priv->sta_key_max_num; i++) if (!test_and_set_bit(i, &priv->ucode_key_table)) return i;
return WEP_INVALID_OFFSET;
}
void iwl_dealloc_bcast_stations(struct iwl_priv *priv)
{ int i;
spin_lock_bh(&priv->sta_lock); for (i = 0; i < IWLAGN_STATION_COUNT; i++) { if (!(priv->stations[i].used & IWL_STA_BCAST)) continue;
#ifdef CONFIG_IWLWIFI_DEBUG staticvoid iwl_dump_lq_cmd(struct iwl_priv *priv, struct iwl_link_quality_cmd *lq)
{ int i;
IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id);
IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n",
lq->general_params.single_stream_ant_msk,
lq->general_params.dual_stream_ant_msk);
for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n",
i, lq->rs_table[i].rate_n_flags);
} #else staticinlinevoid iwl_dump_lq_cmd(struct iwl_priv *priv, struct iwl_link_quality_cmd *lq)
{
} #endif
/* * is_lq_table_valid() - Test one aspect of LQ cmd for validity * * It sometimes happens when a HT rate has been in use and we * loose connectivity with AP then mac80211 will first tell us that the * current channel is not HT anymore before removing the station. In such a * scenario the RXON flags will be updated to indicate we are not * communicating HT anymore, but the LQ command may still contain HT rates. * Test for this to prevent driver from sending LQ command between the time * RXON flags are updated and when LQ command is updated.
*/ staticbool is_lq_table_valid(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_link_quality_cmd *lq)
{ int i;
if (ctx->ht.enabled) returntrue;
IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n",
ctx->active.channel); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { if (le32_to_cpu(lq->rs_table[i].rate_n_flags) &
RATE_MCS_HT_MSK) {
IWL_DEBUG_INFO(priv, "index %d of LQ expects HT channel\n",
i); returnfalse;
}
} returntrue;
}
/* * iwl_send_lq_cmd() - Send link quality command * @init: This command is sent as part of station initialization right * after station has been added. * * The link quality command is sent as the last step of station creation. * This is the special case in which init is set and we call a callback in * this case to clear the state indicating that station creation is in * progress.
*/ int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_link_quality_cmd *lq, u8 flags, bool init)
{ int ret = 0; struct iwl_host_cmd cmd = {
.id = REPLY_TX_LINK_QUALITY_CMD,
.len = { sizeof(struct iwl_link_quality_cmd), },
.flags = flags,
.data = { lq, },
};
if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) return -EINVAL;
/* Set up default rate scaling table in device's station table */
link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); if (!link_cmd) {
IWL_ERR(priv, "Unable to initialize rate scaling for station %pM.\n",
addr); return -ENOMEM;
}
ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true); if (ret)
IWL_ERR(priv, "Link quality command failed (%d)\n", ret);
/* * static WEP keys * * For each context, the device has a table of 4 static WEP keys * (one for each key index) that is updated with the following * commands.
*/
/* * dynamic (per-station) keys * * The dynamic keys are a little more complicated. The device has * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys. * These are linked to stations by a table that contains an index * into the key table for each station/key index/{mcast,unicast}, * i.e. it's basically an array of pointers like this: * key_offset_t key_mapping[NUM_STATIONS][4][2]; * (it really works differently, but you can think of it as such) * * The key uploading and linking happens in the same command, the * add station command with STA_MODIFY_KEY_MASK.
*/
/* * The device expects GTKs for station interfaces to be * installed as GTKs for the AP station. If we have no * station ID, then use the ap_sta_id in that case.
*/ if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx) return vif_priv->ctx->ap_sta_id;
/* * iwlagn_alloc_bcast_station - add broadcast station into driver's station table. * * This adds the broadcast station into the driver's station table * and marks it driver active, so that it will be restored to the * device at the next best time.
*/ int iwlagn_alloc_bcast_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{ struct iwl_link_quality_cmd *link_cmd;
u8 sta_id;
spin_lock_bh(&priv->sta_lock);
sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); if (sta_id == IWL_INVALID_STATION) {
IWL_ERR(priv, "Unable to prepare broadcast station\n");
spin_unlock_bh(&priv->sta_lock);
/* * iwl_update_bcast_station - update broadcast station's LQ command * * Only used by iwlagn. Placed here to have all bcast station management * code together.
*/ int iwl_update_bcast_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{ struct iwl_link_quality_cmd *link_cmd;
u8 sta_id = ctx->bcast_sta_id;
link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); if (!link_cmd) {
IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); return -ENOMEM;
}
spin_lock_bh(&priv->sta_lock); if (priv->stations[sta_id].lq)
kfree(priv->stations[sta_id].lq); else
IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n");
priv->stations[sta_id].lq = link_cmd;
spin_unlock_bh(&priv->sta_lock);
return 0;
}
int iwl_update_bcast_stations(struct iwl_priv *priv)
{ struct iwl_rxon_context *ctx; int ret = 0;
for_each_context(priv, ctx) {
ret = iwl_update_bcast_station(priv, ctx); if (ret) break;
}
return ret;
}
/* * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
*/ int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
{ struct iwl_addsta_cmd sta_cmd;
lockdep_assert_held(&priv->mutex);
/* Remove "disable" flag, to enable Tx for this TID */
spin_lock_bh(&priv->sta_lock);
priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
spin_unlock_bh(&priv->sta_lock);
return iwl_send_add_sta(priv, &sta_cmd, 0);
}
int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, int tid, u16 ssn)
{ int sta_id; struct iwl_addsta_cmd sta_cmd;
lockdep_assert_held(&priv->mutex);
sta_id = iwl_sta_id(sta); if (sta_id == IWL_INVALID_STATION) return -ENXIO;
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.