Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
obj-$(CONFIG_IWLMVM) += iwlmvm.o
iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
iwlmvm-y += scan.o time-event.o rs.o
iwlmvm-y += power.o bt-coex.o
iwlmvm-y += led.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../

View File

@@ -0,0 +1,197 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <net/mac80211.h>
#include "fw-api.h"
#include "mvm.h"
struct iwl_mvm_iface_iterator_data {
struct ieee80211_vif *ignore_vif;
int idx;
struct iwl_mvm_phy_ctxt *phyctxt;
u16 ids[MAX_MACS_IN_BINDING];
u16 colors[MAX_MACS_IN_BINDING];
};
static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action,
struct iwl_mvm_iface_iterator_data *data)
{
struct iwl_binding_cmd cmd;
struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt;
int i, ret;
u32 status;
memset(&cmd, 0, sizeof(cmd));
cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
phyctxt->color));
cmd.action = cpu_to_le32(action);
cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
phyctxt->color));
for (i = 0; i < MAX_MACS_IN_BINDING; i++)
cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
for (i = 0; i < data->idx; i++)
cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i],
data->colors[i]));
status = 0;
ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
sizeof(cmd), &cmd, &status);
if (ret) {
IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n",
action, ret);
return ret;
}
if (status) {
IWL_ERR(mvm, "Binding command failed: %u\n", status);
ret = -EIO;
}
return ret;
}
static void iwl_mvm_iface_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_iface_iterator_data *data = _data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (vif == data->ignore_vif)
return;
if (mvmvif->phy_ctxt != data->phyctxt)
return;
if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING))
return;
data->ids[data->idx] = mvmvif->id;
data->colors[data->idx] = mvmvif->color;
data->idx++;
}
static int iwl_mvm_binding_update(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_mvm_phy_ctxt *phyctxt,
bool add)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_iface_iterator_data data = {
.ignore_vif = vif,
.phyctxt = phyctxt,
};
u32 action = FW_CTXT_ACTION_MODIFY;
lockdep_assert_held(&mvm->mutex);
ieee80211_iterate_active_interfaces_atomic(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_iface_iterator,
&data);
/*
* If there are no other interfaces yet we
* need to create a new binding.
*/
if (data.idx == 0) {
if (add)
action = FW_CTXT_ACTION_ADD;
else
action = FW_CTXT_ACTION_REMOVE;
}
if (add) {
if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING))
return -EINVAL;
data.ids[data.idx] = mvmvif->id;
data.colors[data.idx] = mvmvif->color;
data.idx++;
}
return iwl_mvm_binding_cmd(mvm, action, &data);
}
int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
return -EINVAL;
return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
}
int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
return -EINVAL;
return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
}

View File

@@ -0,0 +1,589 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <net/mac80211.h>
#include "fw-api-bt-coex.h"
#include "iwl-modparams.h"
#include "mvm.h"
#include "iwl-debug.h"
#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \
[(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \
((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
BT_COEX_PRIO_TBL_PRIO_LOW, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
BT_COEX_PRIO_TBL_PRIO_LOW, 1),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
BT_COEX_PRIO_TBL_DISABLED, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
0, 0, 0, 0, 0, 0,
};
#undef EVENT_PRIO_ANT
/* BT Antenna Coupling Threshold (dB) */
#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35)
#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD (3)
#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62)
#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65)
#define BT_REDUCED_TX_POWER_BIT BIT(7)
static inline bool is_loose_coex(void)
{
return iwlwifi_mod_params.ant_coupling >
IWL_BT_ANTENNA_COUPLING_THRESHOLD;
}
int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
{
return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
sizeof(struct iwl_bt_coex_prio_tbl_cmd),
&iwl_bt_prio_tbl);
}
static int iwl_send_bt_env(struct iwl_mvm *mvm, u8 action, u8 type)
{
struct iwl_bt_coex_prot_env_cmd env_cmd;
int ret;
env_cmd.action = action;
env_cmd.type = type;
ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PROT_ENV, CMD_SYNC,
sizeof(env_cmd), &env_cmd);
if (ret)
IWL_ERR(mvm, "failed to send BT env command\n");
return ret;
}
enum iwl_bt_kill_msk {
BT_KILL_MSK_DEFAULT,
BT_KILL_MSK_SCO_HID_A2DP,
BT_KILL_MSK_REDUCED_TXPOW,
BT_KILL_MSK_MAX,
};
static const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
[BT_KILL_MSK_DEFAULT] = 0xffff0000,
[BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
[BT_KILL_MSK_REDUCED_TXPOW] = 0,
};
static const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
[BT_KILL_MSK_DEFAULT] = 0xffff0000,
[BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
[BT_KILL_MSK_REDUCED_TXPOW] = 0,
};
#define IWL_BT_DEFAULT_BOOST (0xf0f0f0f0)
/* Tight Coex */
static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = {
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaeaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xcc00ff28),
cpu_to_le32(0x0000aaaa),
cpu_to_le32(0xcc00aaaa),
cpu_to_le32(0x0000aaaa),
cpu_to_le32(0xc0004000),
cpu_to_le32(0x00000000),
cpu_to_le32(0xf0005000),
cpu_to_le32(0xf0005000),
};
/* Loose Coex */
static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = {
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaeaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xcc00ff28),
cpu_to_le32(0x0000aaaa),
cpu_to_le32(0xcc00aaaa),
cpu_to_le32(0x0000aaaa),
cpu_to_le32(0x00000000),
cpu_to_le32(0x00000000),
cpu_to_le32(0xf0005000),
cpu_to_le32(0xf0005000),
};
/* Full concurrency */
static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = {
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0x00000000),
cpu_to_le32(0x00000000),
cpu_to_le32(0x00000000),
cpu_to_le32(0x00000000),
};
int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
{
struct iwl_bt_coex_cmd cmd = {
.max_kill = 5,
.bt3_time_t7_value = 1,
.bt3_prio_sample_time = 2,
.bt3_timer_t2_value = 0xc,
};
int ret;
cmd.flags = iwlwifi_mod_params.bt_coex_active ?
BT_COEX_NW : BT_COEX_DISABLE;
cmd.flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
cmd.valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
BT_VALID_BT_PRIO_BOOST |
BT_VALID_MAX_KILL |
BT_VALID_3W_TMRS |
BT_VALID_KILL_ACK |
BT_VALID_KILL_CTS |
BT_VALID_REDUCED_TX_POWER |
BT_VALID_LUT);
if (is_loose_coex())
memcpy(&cmd.decision_lut, iwl_loose_lookup,
sizeof(iwl_tight_lookup));
else
memcpy(&cmd.decision_lut, iwl_tight_lookup,
sizeof(iwl_tight_lookup));
cmd.bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
cmd.kill_ack_msk =
cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
cmd.kill_cts_msk =
cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
/* go to CALIB state in internal BT-Coex state machine */
ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
if (ret)
return ret;
ret = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
if (ret)
return ret;
return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
sizeof(cmd), &cmd);
}
static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
bool reduced_tx_power)
{
enum iwl_bt_kill_msk bt_kill_msk;
struct iwl_bt_coex_cmd cmd = {};
struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
lockdep_assert_held(&mvm->mutex);
if (reduced_tx_power) {
/* Reduced Tx power has precedence on the type of the profile */
bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
} else {
/* Low latency BT profile is active: give higher prio to BT */
if (BT_MBOX_MSG(notif, 3, SCO_STATE) ||
BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
BT_MBOX_MSG(notif, 3, SNIFF_STATE))
bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
else
bt_kill_msk = BT_KILL_MSK_DEFAULT;
}
IWL_DEBUG_COEX(mvm,
"Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
bt_kill_msk,
BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
/* Don't send HCMD if there is no update */
if (bt_kill_msk == mvm->bt_kill_msk)
return 0;
mvm->bt_kill_msk = bt_kill_msk;
cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk);
return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
sizeof(cmd), &cmd);
}
static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
bool enable)
{
struct iwl_bt_coex_cmd cmd = {
.valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
.bt_reduced_tx_power = sta_id,
};
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta;
/* This can happen if the station has been removed right now */
if (sta_id == IWL_MVM_STATION_COUNT)
return 0;
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex));
mvmsta = (void *)sta->drv_priv;
/* nothing to do */
if (mvmsta->bt_reduced_txpower == enable)
return 0;
if (enable)
cmd.bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
enable ? "en" : "dis", sta_id);
mvmsta->bt_reduced_txpower = enable;
/* Send ASYNC since this can be sent from an atomic context */
return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_ASYNC,
sizeof(cmd), &cmd);
}
struct iwl_bt_iterator_data {
struct iwl_bt_coex_profile_notif *notif;
struct iwl_mvm *mvm;
u32 num_bss_ifaces;
bool reduced_tx_power;
};
static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_bt_iterator_data *data = _data;
struct iwl_mvm *mvm = data->mvm;
struct ieee80211_chanctx_conf *chanctx_conf;
enum ieee80211_smps_mode smps_mode;
enum ieee80211_band band;
int ave_rssi;
if (vif->type != NL80211_IFTYPE_STATION)
return;
rcu_read_lock();
chanctx_conf = rcu_dereference(vif->chanctx_conf);
if (chanctx_conf && chanctx_conf->def.chan)
band = chanctx_conf->def.chan->band;
else
band = -1;
rcu_read_unlock();
smps_mode = IEEE80211_SMPS_AUTOMATIC;
if (band != IEEE80211_BAND_2GHZ) {
ieee80211_request_smps(vif, smps_mode);
return;
}
if (data->notif->bt_status)
smps_mode = IEEE80211_SMPS_DYNAMIC;
if (data->notif->bt_traffic_load >= IWL_BT_LOAD_FORCE_SISO_THRESHOLD)
smps_mode = IEEE80211_SMPS_STATIC;
IWL_DEBUG_COEX(data->mvm,
"mac %d: bt_status %d traffic_load %d smps_req %d\n",
mvmvif->id, data->notif->bt_status,
data->notif->bt_traffic_load, smps_mode);
ieee80211_request_smps(vif, smps_mode);
/* don't reduce the Tx power if in loose scheme */
if (is_loose_coex())
return;
data->num_bss_ifaces++;
/* reduced Txpower only if there are open BT connections, so ...*/
if (!BT_MBOX_MSG(data->notif, 3, OPEN_CON_2)) {
/* ... cancel reduced Tx power ... */
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
data->reduced_tx_power = false;
/* ... and there is no need to get reports on RSSI any more. */
ieee80211_disable_rssi_reports(vif);
return;
}
ave_rssi = ieee80211_ave_rssi(vif);
/* if the RSSI isn't valid, fake it is very low */
if (!ave_rssi)
ave_rssi = -100;
if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) {
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
/*
* bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
* BSS / P2P clients have rssi above threshold.
* We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
* the iteration, if one interface's rssi isn't good enough,
* bt_kill_msk will be set to default values.
*/
} else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) {
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
/*
* One interface hasn't rssi above threshold, bt_kill_msk must
* be set to default values.
*/
data->reduced_tx_power = false;
}
/* Begin to monitor the RSSI: it may influence the reduced Tx power */
ieee80211_enable_rssi_reports(vif, BT_DISABLE_REDUCED_TXPOWER_THRESHOLD,
BT_ENABLE_REDUCED_TXPOWER_THRESHOLD);
}
static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
{
struct iwl_bt_iterator_data data = {
.mvm = mvm,
.notif = &mvm->last_bt_notif,
.reduced_tx_power = true,
};
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_bt_notif_iterator, &data);
/*
* If there are no BSS / P2P client interfaces, reduced Tx Power is
* irrelevant since it is based on the RSSI coming from the beacon.
* Use BT_KILL_MSK_DEFAULT in that case.
*/
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
}
/* upon association, the fw will send in BT Coex notification */
int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *dev_cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not ");
IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
IWL_DEBUG_COEX(mvm, "\tBT traffic load %d\n", notif->bt_traffic_load);
IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
notif->bt_agg_traffic_load);
IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
/* remember this notification for future use: rssi fluctuations */
memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
iwl_mvm_bt_coex_notif_handle(mvm);
/*
* This is an async handler for a notification, returning anything other
* than 0 doesn't make sense even if HCMD failed.
*/
return 0;
}
static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
struct iwl_bt_iterator_data *data = _data;
struct iwl_mvm *mvm = data->mvm;
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta;
if (vif->type != NL80211_IFTYPE_STATION ||
mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
return;
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
lockdep_is_held(&mvm->mutex));
mvmsta = (void *)sta->drv_priv;
/*
* This interface doesn't support reduced Tx power (because of low
* RSSI probably), then set bt_kill_msk to default values.
*/
if (!mvmsta->bt_reduced_txpower)
data->reduced_tx_power = false;
/* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
}
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event)
{
struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
struct iwl_bt_iterator_data data = {
.mvm = mvm,
.reduced_tx_power = true,
};
int ret;
mutex_lock(&mvm->mutex);
/* Rssi update while not associated ?! */
if (WARN_ON_ONCE(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT))
goto out_unlock;
/* No open connection - reports should be disabled */
if (!BT_MBOX_MSG(&mvm->last_bt_notif, 3, OPEN_CON_2))
goto out_unlock;
IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
/*
* Check if rssi is good enough for reduced Tx power, but not in loose
* scheme.
*/
if (rssi_event == RSSI_EVENT_LOW || is_loose_coex())
ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
false);
else
ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
if (ret)
IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_bt_rssi_iterator, &data);
/*
* If there are no BSS / P2P client interfaces, reduced Tx Power is
* irrelevant since it is based on the RSSI coming from the beacon.
* Use BT_KILL_MSK_DEFAULT in that case.
*/
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
out_unlock:
mutex_unlock(&mvm->mutex);
}
void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct ieee80211_chanctx_conf *chanctx_conf;
enum ieee80211_band band;
rcu_read_lock();
chanctx_conf = rcu_dereference(vif->chanctx_conf);
if (chanctx_conf && chanctx_conf->def.chan)
band = chanctx_conf->def.chan->band;
else
band = -1;
rcu_read_unlock();
/* if we are in 2GHz we will get a notification from the fw */
if (band == IEEE80211_BAND_2GHZ)
return;
/* else, we can remove all the constraints */
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
iwl_mvm_bt_coex_notif_handle(mvm);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,615 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include "mvm.h"
#include "sta.h"
#include "iwl-io.h"
struct iwl_dbgfs_mvm_ctx {
struct iwl_mvm *mvm;
struct ieee80211_vif *vif;
};
static ssize_t iwl_dbgfs_tx_flush_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
char buf[16];
int buf_size, ret;
u32 scd_q_msk;
if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
return -EIO;
memset(buf, 0, sizeof(buf));
buf_size = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
if (sscanf(buf, "%x", &scd_q_msk) != 1)
return -EINVAL;
IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk);
mutex_lock(&mvm->mutex);
ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, true) ? : count;
mutex_unlock(&mvm->mutex);
return ret;
}
static ssize_t iwl_dbgfs_sta_drain_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
struct ieee80211_sta *sta;
char buf[8];
int buf_size, sta_id, drain, ret;
if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
return -EIO;
memset(buf, 0, sizeof(buf));
buf_size = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
if (sscanf(buf, "%d %d", &sta_id, &drain) != 2)
return -EINVAL;
mutex_lock(&mvm->mutex);
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex));
if (IS_ERR_OR_NULL(sta))
ret = -ENOENT;
else
ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? :
count;
mutex_unlock(&mvm->mutex);
return ret;
}
static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
const struct fw_img *img;
int ofs, len, pos = 0;
size_t bufsz, ret;
char *buf;
u8 *ptr;
/* default is to dump the entire data segment */
if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) {
mvm->dbgfs_sram_offset = 0x800000;
if (!mvm->ucode_loaded)
return -EINVAL;
img = &mvm->fw->img[mvm->cur_ucode];
mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
}
len = mvm->dbgfs_sram_len;
bufsz = len * 4 + 256;
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ptr = kzalloc(len, GFP_KERNEL);
if (!ptr) {
kfree(buf);
return -ENOMEM;
}
pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len);
pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
mvm->dbgfs_sram_offset);
iwl_trans_read_mem_bytes(mvm->trans,
mvm->dbgfs_sram_offset,
ptr, len);
for (ofs = 0; ofs < len; ofs += 16) {
pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs);
hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
bufsz - pos, false);
pos += strlen(buf + pos);
if (bufsz - pos > 0)
buf[pos++] = '\n';
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
kfree(ptr);
return ret;
}
static ssize_t iwl_dbgfs_sram_write(struct file *file,
const char __user *user_buf, size_t count,
loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
char buf[64];
int buf_size;
u32 offset, len;
memset(buf, 0, sizeof(buf));
buf_size = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
if ((offset & 0x3) || (len & 0x3))
return -EINVAL;
mvm->dbgfs_sram_offset = offset;
mvm->dbgfs_sram_len = len;
} else {
mvm->dbgfs_sram_offset = 0;
mvm->dbgfs_sram_len = 0;
}
return count;
}
static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
struct ieee80211_sta *sta;
char buf[400];
int i, pos = 0, bufsz = sizeof(buf);
mutex_lock(&mvm->mutex);
for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i);
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
lockdep_is_held(&mvm->mutex));
if (!sta)
pos += scnprintf(buf + pos, bufsz - pos, "N/A\n");
else if (IS_ERR(sta))
pos += scnprintf(buf + pos, bufsz - pos, "%ld\n",
PTR_ERR(sta));
else
pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
sta->addr);
}
mutex_unlock(&mvm->mutex);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
static ssize_t iwl_dbgfs_power_down_allow_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
char buf[8] = {};
int allow;
if (!mvm->ucode_loaded)
return -EIO;
if (copy_from_user(buf, user_buf, sizeof(buf)))
return -EFAULT;
if (sscanf(buf, "%d", &allow) != 1)
return -EINVAL;
IWL_DEBUG_POWER(mvm, "%s device power down\n",
allow ? "allow" : "prevent");
/*
* TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it
*/
return count;
}
static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
char buf[8] = {};
int allow;
if (copy_from_user(buf, user_buf, sizeof(buf)))
return -EFAULT;
if (sscanf(buf, "%d", &allow) != 1)
return -EINVAL;
IWL_DEBUG_POWER(mvm, "%s device power down in d3\n",
allow ? "allow" : "prevent");
/*
* TODO: When WoWLAN FW alive notification happens, driver will send
* REPLY_DEBUG_CMD setting power_down_allow flag according to
* mvm->prevent_power_down_d3
*/
mvm->prevent_power_down_d3 = !allow;
return count;
}
static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_vif *vif = file->private_data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = mvmvif->dbgfs_data;
u8 ap_sta_id;
struct ieee80211_chanctx_conf *chanctx_conf;
char buf[512];
int bufsz = sizeof(buf);
int pos = 0;
int i;
mutex_lock(&mvm->mutex);
ap_sta_id = mvmvif->ap_sta_id;
pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
mvmvif->id, mvmvif->color);
pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
vif->bss_conf.bssid);
pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n");
for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) {
pos += scnprintf(buf+pos, bufsz-pos,
"\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n",
i, mvmvif->queue_params[i].txop,
mvmvif->queue_params[i].cw_min,
mvmvif->queue_params[i].cw_max,
mvmvif->queue_params[i].aifs,
mvmvif->queue_params[i].uapsd);
}
if (vif->type == NL80211_IFTYPE_STATION &&
ap_sta_id != IWL_MVM_STATION_COUNT) {
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvm_sta;
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id],
lockdep_is_held(&mvm->mutex));
mvm_sta = (void *)sta->drv_priv;
pos += scnprintf(buf+pos, bufsz-pos,
"ap_sta_id %d - reduced Tx power %d\n",
ap_sta_id, mvm_sta->bt_reduced_txpower);
}
rcu_read_lock();
chanctx_conf = rcu_dereference(vif->chanctx_conf);
if (chanctx_conf) {
pos += scnprintf(buf+pos, bufsz-pos,
"idle rx chains %d, active rx chains: %d\n",
chanctx_conf->rx_chains_static,
chanctx_conf->rx_chains_dynamic);
}
rcu_read_unlock();
mutex_unlock(&mvm->mutex);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
#define BT_MBOX_MSG(_notif, _num, _field) \
((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
>> BT_MBOX##_num##_##_field##_POS)
#define BT_MBOX_PRINT(_num, _field, _end) \
pos += scnprintf(buf + pos, bufsz - pos, \
"\t%s: %d%s", \
#_field, \
BT_MBOX_MSG(notif, _num, _field), \
true ? "\n" : ", ");
static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
char *buf;
int ret, pos = 0, bufsz = sizeof(char) * 1024;
buf = kmalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&mvm->mutex);
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
BT_MBOX_PRINT(0, LE_PROF1, false);
BT_MBOX_PRINT(0, LE_PROF2, false);
BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
BT_MBOX_PRINT(0, CHL_SEQ_N, false);
BT_MBOX_PRINT(0, INBAND_S, false);
BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
BT_MBOX_PRINT(0, LE_SCAN, false);
BT_MBOX_PRINT(0, LE_ADV, false);
BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
BT_MBOX_PRINT(0, OPEN_CON_1, true);
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
BT_MBOX_PRINT(1, IP_SR, false);
BT_MBOX_PRINT(1, LE_MSTR, false);
BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
BT_MBOX_PRINT(1, MSG_TYPE, false);
BT_MBOX_PRINT(1, SSN, true);
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
BT_MBOX_PRINT(2, SNIFF_ACT, false);
BT_MBOX_PRINT(2, PAG, false);
BT_MBOX_PRINT(2, INQUIRY, false);
BT_MBOX_PRINT(2, CONN, false);
BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
BT_MBOX_PRINT(2, DISC, false);
BT_MBOX_PRINT(2, SCO_TX_ACT, false);
BT_MBOX_PRINT(2, SCO_RX_ACT, false);
BT_MBOX_PRINT(2, ESCO_RE_TX, false);
BT_MBOX_PRINT(2, SCO_DURATION, true);
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
BT_MBOX_PRINT(3, SCO_STATE, false);
BT_MBOX_PRINT(3, SNIFF_STATE, false);
BT_MBOX_PRINT(3, A2DP_STATE, false);
BT_MBOX_PRINT(3, ACL_STATE, false);
BT_MBOX_PRINT(3, MSTR_STATE, false);
BT_MBOX_PRINT(3, OBX_STATE, false);
BT_MBOX_PRINT(3, OPEN_CON_2, false);
BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
BT_MBOX_PRINT(3, INBAND_P, false);
BT_MBOX_PRINT(3, MSG_TYPE_2, false);
BT_MBOX_PRINT(3, SSN_2, false);
BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
notif->bt_status);
pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
notif->bt_open_conn);
pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
notif->bt_traffic_load);
pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
notif->bt_agg_traffic_load);
pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
notif->bt_ci_compliance);
mutex_unlock(&mvm->mutex);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
return ret;
}
#undef BT_MBOX_PRINT
static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
bool restart_fw = iwlwifi_mod_params.restart_fw;
int ret;
iwlwifi_mod_params.restart_fw = true;
mutex_lock(&mvm->mutex);
/* take the return value to make compiler happy - it will fail anyway */
ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL);
mutex_unlock(&mvm->mutex);
iwlwifi_mod_params.restart_fw = restart_fw;
return count;
}
#define MVM_DEBUGFS_READ_FILE_OPS(name) \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.read = iwl_dbgfs_##name##_read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
}
#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name) \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.read = iwl_dbgfs_##name##_read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define MVM_DEBUGFS_WRITE_FILE_OPS(name) \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do { \
if (!debugfs_create_file(#name, mode, parent, mvm, \
&iwl_dbgfs_##name##_ops)) \
goto err; \
} while (0)
#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \
if (!debugfs_create_file(#name, mode, parent, vif, \
&iwl_dbgfs_##name##_ops)) \
goto err; \
} while (0)
/* Device wide debugfs entries */
MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush);
MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram);
MVM_DEBUGFS_READ_FILE_OPS(stations);
MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
/* Interface specific debugfs entries */
MVM_DEBUGFS_READ_FILE_OPS(mac_params);
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
{
char buf[100];
mvm->debugfs_dir = dbgfs_dir;
MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
/*
* Create a symlink with mac80211. It will be removed when mac80211
* exists (before the opmode exists which removes the target.)
*/
snprintf(buf, 100, "../../%s/%s",
dbgfs_dir->d_parent->d_parent->d_name.name,
dbgfs_dir->d_parent->d_name.name);
if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf))
goto err;
return 0;
err:
IWL_ERR(mvm, "Can't create the mvm debugfs directory\n");
return -ENOMEM;
}
void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct dentry *dbgfs_dir = vif->debugfs_dir;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
char buf[100];
if (!dbgfs_dir)
return;
mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir);
mvmvif->dbgfs_data = mvm;
if (!mvmvif->dbgfs_dir) {
IWL_ERR(mvm, "Failed to create debugfs directory under %s\n",
dbgfs_dir->d_name.name);
return;
}
MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir,
S_IRUSR);
/*
* Create symlink for convenience pointing to interface specific
* debugfs entries for the driver. For example, under
* /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/
* find
* netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/
*/
snprintf(buf, 100, "../../../%s/%s/%s/%s",
dbgfs_dir->d_parent->d_parent->d_name.name,
dbgfs_dir->d_parent->d_name.name,
dbgfs_dir->d_name.name,
mvmvif->dbgfs_dir->d_name.name);
mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name,
mvm->debugfs_dir, buf);
if (!mvmvif->dbgfs_slink)
IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n",
dbgfs_dir->d_name.name);
return;
err:
IWL_ERR(mvm, "Can't create debugfs entity\n");
}
void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
debugfs_remove(mvmvif->dbgfs_slink);
mvmvif->dbgfs_slink = NULL;
debugfs_remove_recursive(mvmvif->dbgfs_dir);
mvmvif->dbgfs_dir = NULL;
}

View File

@@ -0,0 +1,319 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __fw_api_bt_coex_h__
#define __fw_api_bt_coex_h__
#include <linux/types.h>
#include <linux/bitops.h>
#define BITS(nb) (BIT(nb) - 1)
/**
* enum iwl_bt_coex_flags - flags for BT_COEX command
* @BT_CH_PRIMARY_EN:
* @BT_CH_SECONDARY_EN:
* @BT_NOTIF_COEX_OFF:
* @BT_COEX_MODE_POS:
* @BT_COEX_MODE_MSK:
* @BT_COEX_DISABLE:
* @BT_COEX_2W:
* @BT_COEX_3W:
* @BT_COEX_NW:
* @BT_USE_DEFAULTS:
* @BT_SYNC_2_BT_DISABLE:
* @BT_COEX_CORUNNING_TBL_EN:
*/
enum iwl_bt_coex_flags {
BT_CH_PRIMARY_EN = BIT(0),
BT_CH_SECONDARY_EN = BIT(1),
BT_NOTIF_COEX_OFF = BIT(2),
BT_COEX_MODE_POS = 3,
BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS,
BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS,
BT_COEX_2W = 0x1 << BT_COEX_MODE_POS,
BT_COEX_3W = 0x2 << BT_COEX_MODE_POS,
BT_COEX_NW = 0x3 << BT_COEX_MODE_POS,
BT_USE_DEFAULTS = BIT(6),
BT_SYNC_2_BT_DISABLE = BIT(7),
/*
* For future use - when the flags will be enlarged
* BT_COEX_CORUNNING_TBL_EN = BIT(8),
*/
};
/*
* indicates what has changed in the BT_COEX command.
*/
enum iwl_bt_coex_valid_bit_msk {
BT_VALID_ENABLE = BIT(0),
BT_VALID_BT_PRIO_BOOST = BIT(1),
BT_VALID_MAX_KILL = BIT(2),
BT_VALID_3W_TMRS = BIT(3),
BT_VALID_KILL_ACK = BIT(4),
BT_VALID_KILL_CTS = BIT(5),
BT_VALID_REDUCED_TX_POWER = BIT(6),
BT_VALID_LUT = BIT(7),
BT_VALID_WIFI_RX_SW_PRIO_BOOST = BIT(8),
BT_VALID_WIFI_TX_SW_PRIO_BOOST = BIT(9),
BT_VALID_MULTI_PRIO_LUT = BIT(10),
BT_VALID_TRM_KICK_FILTER = BIT(11),
BT_VALID_CORUN_LUT_20 = BIT(12),
BT_VALID_CORUN_LUT_40 = BIT(13),
BT_VALID_ANT_ISOLATION = BIT(14),
BT_VALID_ANT_ISOLATION_THRS = BIT(15),
/*
* For future use - when the valid flags will be enlarged
* BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16),
* BT_VALID_TXRX_MAX_FREQ_0 = BIT(17),
*/
};
/**
* enum iwl_bt_reduced_tx_power - allows to reduce txpower for WiFi frames.
* @BT_REDUCED_TX_POWER_CTL: reduce Tx power for control frames
* @BT_REDUCED_TX_POWER_DATA: reduce Tx power for data frames
*
* This mechanism allows to have BT and WiFi run concurrently. Since WiFi
* reduces its Tx power, it can work along with BT, hence reducing the amount
* of WiFi frames being killed by BT.
*/
enum iwl_bt_reduced_tx_power {
BT_REDUCED_TX_POWER_CTL = BIT(0),
BT_REDUCED_TX_POWER_DATA = BIT(1),
};
#define BT_COEX_LUT_SIZE (12)
/**
* struct iwl_bt_coex_cmd - bt coex configuration command
* @flags:&enum iwl_bt_coex_flags
* @lead_time:
* @max_kill:
* @bt3_time_t7_value:
* @kill_ack_msk:
* @kill_cts_msk:
* @bt3_prio_sample_time:
* @bt3_timer_t2_value:
* @bt4_reaction_time:
* @decision_lut[12]:
* @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
* @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
* @bt_prio_boost: values for PTA boost register
* @wifi_tx_prio_boost: SW boost of wifi tx priority
* @wifi_rx_prio_boost: SW boost of wifi rx priority
*
* The structure is used for the BT_COEX command.
*/
struct iwl_bt_coex_cmd {
u8 flags;
u8 lead_time;
u8 max_kill;
u8 bt3_time_t7_value;
__le32 kill_ack_msk;
__le32 kill_cts_msk;
u8 bt3_prio_sample_time;
u8 bt3_timer_t2_value;
__le16 bt4_reaction_time;
__le32 decision_lut[BT_COEX_LUT_SIZE];
u8 bt_reduced_tx_power;
u8 reserved;
__le16 valid_bit_msk;
__le32 bt_prio_boost;
u8 reserved2;
u8 wifi_tx_prio_boost;
__le16 wifi_rx_prio_boost;
} __packed; /* BT_COEX_CMD_API_S_VER_3 */
#define BT_MBOX(n_dw, _msg, _pos, _nbits) \
BT_MBOX##n_dw##_##_msg##_POS = (_pos), \
BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS
enum iwl_bt_mxbox_dw0 {
BT_MBOX(0, LE_SLAVE_LAT, 0, 3),
BT_MBOX(0, LE_PROF1, 3, 1),
BT_MBOX(0, LE_PROF2, 4, 1),
BT_MBOX(0, LE_PROF_OTHER, 5, 1),
BT_MBOX(0, CHL_SEQ_N, 8, 4),
BT_MBOX(0, INBAND_S, 13, 1),
BT_MBOX(0, LE_MIN_RSSI, 16, 4),
BT_MBOX(0, LE_SCAN, 20, 1),
BT_MBOX(0, LE_ADV, 21, 1),
BT_MBOX(0, LE_MAX_TX_POWER, 24, 4),
BT_MBOX(0, OPEN_CON_1, 28, 2),
};
enum iwl_bt_mxbox_dw1 {
BT_MBOX(1, BR_MAX_TX_POWER, 0, 4),
BT_MBOX(1, IP_SR, 4, 1),
BT_MBOX(1, LE_MSTR, 5, 1),
BT_MBOX(1, AGGR_TRFC_LD, 8, 6),
BT_MBOX(1, MSG_TYPE, 16, 3),
BT_MBOX(1, SSN, 19, 2),
};
enum iwl_bt_mxbox_dw2 {
BT_MBOX(2, SNIFF_ACT, 0, 3),
BT_MBOX(2, PAG, 3, 1),
BT_MBOX(2, INQUIRY, 4, 1),
BT_MBOX(2, CONN, 5, 1),
BT_MBOX(2, SNIFF_INTERVAL, 8, 5),
BT_MBOX(2, DISC, 13, 1),
BT_MBOX(2, SCO_TX_ACT, 16, 2),
BT_MBOX(2, SCO_RX_ACT, 18, 2),
BT_MBOX(2, ESCO_RE_TX, 20, 2),
BT_MBOX(2, SCO_DURATION, 24, 6),
};
enum iwl_bt_mxbox_dw3 {
BT_MBOX(3, SCO_STATE, 0, 1),
BT_MBOX(3, SNIFF_STATE, 1, 1),
BT_MBOX(3, A2DP_STATE, 2, 1),
BT_MBOX(3, ACL_STATE, 3, 1),
BT_MBOX(3, MSTR_STATE, 4, 1),
BT_MBOX(3, OBX_STATE, 5, 1),
BT_MBOX(3, OPEN_CON_2, 8, 2),
BT_MBOX(3, TRAFFIC_LOAD, 10, 2),
BT_MBOX(3, CHL_SEQN_LSB, 12, 1),
BT_MBOX(3, INBAND_P, 13, 1),
BT_MBOX(3, MSG_TYPE_2, 16, 3),
BT_MBOX(3, SSN_2, 19, 2),
BT_MBOX(3, UPDATE_REQUEST, 21, 1),
};
#define BT_MBOX_MSG(_notif, _num, _field) \
((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
>> BT_MBOX##_num##_##_field##_POS)
/**
* struct iwl_bt_coex_profile_notif - notification about BT coex
* @mbox_msg: message from BT to WiFi
* @:bt_status: 0 - off, 1 - on
* @:bt_open_conn: number of BT connections open
* @:bt_traffic_load: load of BT traffic
* @:bt_agg_traffic_load: aggregated load of BT traffic
* @:bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
*/
struct iwl_bt_coex_profile_notif {
__le32 mbox_msg[4];
u8 bt_status;
u8 bt_open_conn;
u8 bt_traffic_load;
u8 bt_agg_traffic_load;
u8 bt_ci_compliance;
u8 reserved[3];
} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */
enum iwl_bt_coex_prio_table_event {
BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0,
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1,
BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2,
BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3,
BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4,
BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5,
BT_COEX_PRIO_TBL_EVT_DTIM = 6,
BT_COEX_PRIO_TBL_EVT_SCAN52 = 7,
BT_COEX_PRIO_TBL_EVT_SCAN24 = 8,
BT_COEX_PRIO_TBL_EVT_IDLE = 9,
BT_COEX_PRIO_TBL_EVT_MAX = 16,
}; /* BT_COEX_PRIO_TABLE_EVENTS_API_E_VER_1 */
enum iwl_bt_coex_prio_table_prio {
BT_COEX_PRIO_TBL_DISABLED = 0,
BT_COEX_PRIO_TBL_PRIO_LOW = 1,
BT_COEX_PRIO_TBL_PRIO_HIGH = 2,
BT_COEX_PRIO_TBL_PRIO_BYPASS = 3,
BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4,
BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5,
BT_COEX_PRIO_TBL_PRIO_COEX_IDLE = 6,
BT_COEX_PRIO_TBL_MAX = 8,
}; /* BT_COEX_PRIO_TABLE_PRIORITIES_API_E_VER_1 */
#define BT_COEX_PRIO_TBL_SHRD_ANT_POS (0)
#define BT_COEX_PRIO_TBL_PRIO_POS (1)
#define BT_COEX_PRIO_TBL_RESERVED_POS (4)
/**
* struct iwl_bt_coex_prio_tbl_cmd - priority table for BT coex
* @prio_tbl:
*/
struct iwl_bt_coex_prio_tbl_cmd {
u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
} __packed;
enum iwl_bt_coex_env_action {
BT_COEX_ENV_CLOSE = 0,
BT_COEX_ENV_OPEN = 1,
}; /* BT_COEX_PROT_ENV_ACTION_API_E_VER_1 */
/**
* struct iwl_bt_coex_prot_env_cmd - BT Protection Envelope
* @action: enum %iwl_bt_coex_env_action
* @type: enum %iwl_bt_coex_prio_table_event
*/
struct iwl_bt_coex_prot_env_cmd {
u8 action; /* 0 = closed, 1 = open */
u8 type; /* 0 .. 15 */
u8 reserved[2];
} __packed;
#endif /* __fw_api_bt_coex_h__ */

View File

@@ -0,0 +1,333 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __fw_api_d3_h__
#define __fw_api_d3_h__
/**
* enum iwl_d3_wakeup_flags - D3 manager wakeup flags
* @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert
*/
enum iwl_d3_wakeup_flags {
IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0),
}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */
/**
* struct iwl_d3_manager_config - D3 manager configuration command
* @min_sleep_time: minimum sleep time (in usec)
* @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags
* @wakeup_host_timer: force wakeup after this many seconds
*
* The structure is used for the D3_CONFIG_CMD command.
*/
struct iwl_d3_manager_config {
__le32 min_sleep_time;
__le32 wakeup_flags;
__le32 wakeup_host_timer;
} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */
/* TODO: OFFLOADS_QUERY_API_S_VER_1 */
/**
* enum iwl_d3_proto_offloads - enabled protocol offloads
* @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled
* @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled
*/
enum iwl_proto_offloads {
IWL_D3_PROTO_OFFLOAD_ARP = BIT(0),
IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
};
#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS 2
/**
* struct iwl_proto_offload_cmd - ARP/NS offload configuration
* @enabled: enable flags
* @remote_ipv4_addr: remote address to answer to (or zero if all)
* @host_ipv4_addr: our IPv4 address to respond to queries for
* @arp_mac_addr: our MAC address for ARP responses
* @remote_ipv6_addr: remote address to answer to (or zero if all)
* @solicited_node_ipv6_addr: broken -- solicited node address exists
* for each target address
* @target_ipv6_addr: our target addresses
* @ndp_mac_addr: neighbor soliciation response MAC address
*/
struct iwl_proto_offload_cmd {
__le32 enabled;
__be32 remote_ipv4_addr;
__be32 host_ipv4_addr;
u8 arp_mac_addr[ETH_ALEN];
__le16 reserved1;
u8 remote_ipv6_addr[16];
u8 solicited_node_ipv6_addr[16];
u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS][16];
u8 ndp_mac_addr[ETH_ALEN];
__le16 reserved2;
} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */
/*
* WOWLAN_PATTERNS
*/
#define IWL_WOWLAN_MIN_PATTERN_LEN 16
#define IWL_WOWLAN_MAX_PATTERN_LEN 128
struct iwl_wowlan_pattern {
u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN];
u8 mask_size;
u8 pattern_size;
__le16 reserved;
} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */
#define IWL_WOWLAN_MAX_PATTERNS 20
struct iwl_wowlan_patterns_cmd {
__le32 n_patterns;
struct iwl_wowlan_pattern patterns[];
} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */
enum iwl_wowlan_wakeup_filters {
IWL_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0),
IWL_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1),
IWL_WOWLAN_WAKEUP_BEACON_MISS = BIT(2),
IWL_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3),
IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4),
IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5),
IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6),
IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(7),
IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8),
IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9),
IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10),
/* BIT(11) reserved */
IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12),
}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
struct iwl_wowlan_config_cmd {
__le32 wakeup_filter;
__le16 non_qos_seq;
__le16 qos_seq[8];
u8 wowlan_ba_teardown_tids;
u8 is_11n_connection;
} __packed; /* WOWLAN_CONFIG_API_S_VER_2 */
/*
* WOWLAN_TSC_RSC_PARAMS
*/
#define IWL_NUM_RSC 16
struct tkip_sc {
__le16 iv16;
__le16 pad;
__le32 iv32;
} __packed; /* TKIP_SC_API_U_VER_1 */
struct iwl_tkip_rsc_tsc {
struct tkip_sc unicast_rsc[IWL_NUM_RSC];
struct tkip_sc multicast_rsc[IWL_NUM_RSC];
struct tkip_sc tsc;
} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */
struct aes_sc {
__le64 pn;
} __packed; /* TKIP_AES_SC_API_U_VER_1 */
struct iwl_aes_rsc_tsc {
struct aes_sc unicast_rsc[IWL_NUM_RSC];
struct aes_sc multicast_rsc[IWL_NUM_RSC];
struct aes_sc tsc;
} __packed; /* AES_TSC_RSC_API_S_VER_1 */
union iwl_all_tsc_rsc {
struct iwl_tkip_rsc_tsc tkip;
struct iwl_aes_rsc_tsc aes;
}; /* ALL_TSC_RSC_API_S_VER_2 */
struct iwl_wowlan_rsc_tsc_params_cmd {
union iwl_all_tsc_rsc all_tsc_rsc;
} __packed; /* ALL_TSC_RSC_API_S_VER_2 */
#define IWL_MIC_KEY_SIZE 8
struct iwl_mic_keys {
u8 tx[IWL_MIC_KEY_SIZE];
u8 rx_unicast[IWL_MIC_KEY_SIZE];
u8 rx_mcast[IWL_MIC_KEY_SIZE];
} __packed; /* MIC_KEYS_API_S_VER_1 */
#define IWL_P1K_SIZE 5
struct iwl_p1k_cache {
__le16 p1k[IWL_P1K_SIZE];
} __packed;
#define IWL_NUM_RX_P1K_CACHE 2
struct iwl_wowlan_tkip_params_cmd {
struct iwl_mic_keys mic_keys;
struct iwl_p1k_cache tx;
struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE];
struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE];
} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */
#define IWL_KCK_MAX_SIZE 32
#define IWL_KEK_MAX_SIZE 32
struct iwl_wowlan_kek_kck_material_cmd {
u8 kck[IWL_KCK_MAX_SIZE];
u8 kek[IWL_KEK_MAX_SIZE];
__le16 kck_len;
__le16 kek_len;
__le64 replay_ctr;
} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */
#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87
enum iwl_wowlan_rekey_status {
IWL_WOWLAN_REKEY_POST_REKEY = 0,
IWL_WOWLAN_REKEY_WHILE_REKEY = 1,
}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */
enum iwl_wowlan_wakeup_reason {
IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS = 0,
IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET = BIT(0),
IWL_WOWLAN_WAKEUP_BY_PATTERN = BIT(1),
IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON = BIT(2),
IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH = BIT(3),
IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE = BIT(4),
IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED = BIT(5),
IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR = BIT(6),
IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST = BIT(7),
IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8),
IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9),
IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10),
/* BIT(11) reserved */
IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12),
}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
struct iwl_wowlan_status {
__le64 replay_ctr;
__le16 pattern_number;
__le16 non_qos_seq_ctr;
__le16 qos_seq_ctr[8];
__le32 wakeup_reasons;
__le32 rekey_status;
__le32 num_of_gtk_rekeys;
__le32 transmitted_ndps;
__le32 received_beacons;
__le32 wake_packet_length;
__le32 wake_packet_bufsize;
u8 wake_packet[]; /* can be truncated from _length to _bufsize */
} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64
#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128
#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048
struct iwl_tcp_packet_info {
__le16 tcp_pseudo_header_checksum;
__le16 tcp_payload_length;
} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */
struct iwl_tcp_packet {
struct iwl_tcp_packet_info info;
u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN];
} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
struct iwl_remote_wake_packet {
struct iwl_tcp_packet_info info;
u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN];
} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
struct iwl_wowlan_remote_wake_config {
__le32 connection_max_time; /* unused */
/* TCP_PROTOCOL_CONFIG_API_S_VER_1 */
u8 max_syn_retries;
u8 max_data_retries;
u8 tcp_syn_ack_timeout;
u8 tcp_ack_timeout;
struct iwl_tcp_packet syn_tx;
struct iwl_tcp_packet synack_rx;
struct iwl_tcp_packet keepalive_ack_rx;
struct iwl_tcp_packet fin_tx;
struct iwl_remote_wake_packet keepalive_tx;
struct iwl_remote_wake_packet wake_rx;
/* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */
u8 sequence_number_offset;
u8 sequence_number_length;
u8 token_offset;
u8 token_length;
/* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */
__le32 initial_sequence_number;
__le16 keepalive_interval;
__le16 num_tokens;
u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS];
} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */
/* TODO: NetDetect API */
#endif /* __fw_api_d3_h__ */

View File

@@ -0,0 +1,375 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __fw_api_mac_h__
#define __fw_api_mac_h__
/*
* The first MAC indices (starting from 0)
* are available to the driver, AUX follows
*/
#define MAC_INDEX_AUX 4
#define MAC_INDEX_MIN_DRIVER 0
#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX
enum iwl_ac {
AC_BK,
AC_BE,
AC_VI,
AC_VO,
AC_NUM,
};
/**
* enum iwl_mac_protection_flags - MAC context flags
* @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames,
* this will require CCK RTS/CTS2self.
* RTS/CTS will protect full burst time.
* @MAC_PROT_FLG_HT_PROT: enable HT protection
* @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions
* @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self
*/
enum iwl_mac_protection_flags {
MAC_PROT_FLG_TGG_PROTECT = BIT(3),
MAC_PROT_FLG_HT_PROT = BIT(23),
MAC_PROT_FLG_FAT_PROT = BIT(24),
MAC_PROT_FLG_SELF_CTS_EN = BIT(30),
};
#define MAC_FLG_SHORT_SLOT BIT(4)
#define MAC_FLG_SHORT_PREAMBLE BIT(5)
/**
* enum iwl_mac_types - Supported MAC types
* @FW_MAC_TYPE_FIRST: lowest supported MAC type
* @FW_MAC_TYPE_AUX: Auxiliary MAC (internal)
* @FW_MAC_TYPE_LISTENER: monitor MAC type (?)
* @FW_MAC_TYPE_PIBSS: Pseudo-IBSS
* @FW_MAC_TYPE_IBSS: IBSS
* @FW_MAC_TYPE_BSS_STA: BSS (managed) station
* @FW_MAC_TYPE_P2P_DEVICE: P2P Device
* @FW_MAC_TYPE_P2P_STA: P2P client
* @FW_MAC_TYPE_GO: P2P GO
* @FW_MAC_TYPE_TEST: ?
* @FW_MAC_TYPE_MAX: highest support MAC type
*/
enum iwl_mac_types {
FW_MAC_TYPE_FIRST = 1,
FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST,
FW_MAC_TYPE_LISTENER,
FW_MAC_TYPE_PIBSS,
FW_MAC_TYPE_IBSS,
FW_MAC_TYPE_BSS_STA,
FW_MAC_TYPE_P2P_DEVICE,
FW_MAC_TYPE_P2P_STA,
FW_MAC_TYPE_GO,
FW_MAC_TYPE_TEST,
FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST
}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */
/**
* enum iwl_tsf_id - TSF hw timer ID
* @TSF_ID_A: use TSF A
* @TSF_ID_B: use TSF B
* @TSF_ID_C: use TSF C
* @TSF_ID_D: use TSF D
* @NUM_TSF_IDS: number of TSF timers available
*/
enum iwl_tsf_id {
TSF_ID_A = 0,
TSF_ID_B = 1,
TSF_ID_C = 2,
TSF_ID_D = 3,
NUM_TSF_IDS = 4,
}; /* TSF_ID_API_E_VER_1 */
/**
* struct iwl_mac_data_ap - configuration data for AP MAC context
* @beacon_time: beacon transmit time in system time
* @beacon_tsf: beacon transmit time in TSF
* @bi: beacon interval in TU
* @bi_reciprocal: 2^32 / bi
* @dtim_interval: dtim transmit time in TU
* @dtim_reciprocal: 2^32 / dtim_interval
* @mcast_qid: queue ID for multicast traffic
* @beacon_template: beacon template ID
*/
struct iwl_mac_data_ap {
__le32 beacon_time;
__le64 beacon_tsf;
__le32 bi;
__le32 bi_reciprocal;
__le32 dtim_interval;
__le32 dtim_reciprocal;
__le32 mcast_qid;
__le32 beacon_template;
} __packed; /* AP_MAC_DATA_API_S_VER_1 */
/**
* struct iwl_mac_data_ibss - configuration data for IBSS MAC context
* @beacon_time: beacon transmit time in system time
* @beacon_tsf: beacon transmit time in TSF
* @bi: beacon interval in TU
* @bi_reciprocal: 2^32 / bi
*/
struct iwl_mac_data_ibss {
__le32 beacon_time;
__le64 beacon_tsf;
__le32 bi;
__le32 bi_reciprocal;
} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
/**
* struct iwl_mac_data_sta - configuration data for station MAC context
* @is_assoc: 1 for associated state, 0 otherwise
* @dtim_time: DTIM arrival time in system time
* @dtim_tsf: DTIM arrival time in TSF
* @bi: beacon interval in TU, applicable only when associated
* @bi_reciprocal: 2^32 / bi , applicable only when associated
* @dtim_interval: DTIM interval in TU, applicable only when associated
* @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated
* @listen_interval: in beacon intervals, applicable only when associated
* @assoc_id: unique ID assigned by the AP during association
*/
struct iwl_mac_data_sta {
__le32 is_assoc;
__le32 dtim_time;
__le64 dtim_tsf;
__le32 bi;
__le32 bi_reciprocal;
__le32 dtim_interval;
__le32 dtim_reciprocal;
__le32 listen_interval;
__le32 assoc_id;
__le32 assoc_beacon_arrive_time;
} __packed; /* STA_MAC_DATA_API_S_VER_1 */
/**
* struct iwl_mac_data_go - configuration data for P2P GO MAC context
* @ap: iwl_mac_data_ap struct with most config data
* @ctwin: client traffic window in TU (period after TBTT when GO is present).
* 0 indicates that there is no CT window.
* @opp_ps_enabled: indicate that opportunistic PS allowed
*/
struct iwl_mac_data_go {
struct iwl_mac_data_ap ap;
__le32 ctwin;
__le32 opp_ps_enabled;
} __packed; /* GO_MAC_DATA_API_S_VER_1 */
/**
* struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context
* @sta: iwl_mac_data_sta struct with most config data
* @ctwin: client traffic window in TU (period after TBTT when GO is present).
* 0 indicates that there is no CT window.
*/
struct iwl_mac_data_p2p_sta {
struct iwl_mac_data_sta sta;
__le32 ctwin;
} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */
/**
* struct iwl_mac_data_pibss - Pseudo IBSS config data
* @stats_interval: interval in TU between statistics notifications to host.
*/
struct iwl_mac_data_pibss {
__le32 stats_interval;
} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */
/*
* struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC
* context.
* @is_disc_extended: if set to true, P2P Device discoverability is enabled on
* other channels as well. This should be to true only in case that the
* device is discoverable and there is an active GO. Note that setting this
* field when not needed, will increase the number of interrupts and have
* effect on the platform power, as this setting opens the Rx filters on
* all macs.
*/
struct iwl_mac_data_p2p_dev {
__le32 is_disc_extended;
} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */
/**
* enum iwl_mac_filter_flags - MAC context filter flags
* @MAC_FILTER_IN_PROMISC: accept all data frames
* @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all mangement and
* control frames to the host
* @MAC_FILTER_ACCEPT_GRP: accept multicast frames
* @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames
* @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames
* @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host
* (in station mode when associated)
* @MAC_FILTER_OUT_BCAST: filter out all broadcast frames
* @MAC_FILTER_IN_CRC32: extract FCS and append it to frames
* @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host
*/
enum iwl_mac_filter_flags {
MAC_FILTER_IN_PROMISC = BIT(0),
MAC_FILTER_IN_CONTROL_AND_MGMT = BIT(1),
MAC_FILTER_ACCEPT_GRP = BIT(2),
MAC_FILTER_DIS_DECRYPT = BIT(3),
MAC_FILTER_DIS_GRP_DECRYPT = BIT(4),
MAC_FILTER_IN_BEACON = BIT(6),
MAC_FILTER_OUT_BCAST = BIT(8),
MAC_FILTER_IN_CRC32 = BIT(11),
MAC_FILTER_IN_PROBE_REQUEST = BIT(12),
};
/**
* enum iwl_mac_qos_flags - QoS flags
* @MAC_QOS_FLG_UPDATE_EDCA: ?
* @MAC_QOS_FLG_TGN: HT is enabled
* @MAC_QOS_FLG_TXOP_TYPE: ?
*
*/
enum iwl_mac_qos_flags {
MAC_QOS_FLG_UPDATE_EDCA = BIT(0),
MAC_QOS_FLG_TGN = BIT(1),
MAC_QOS_FLG_TXOP_TYPE = BIT(4),
};
/**
* struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD
* @cw_min: Contention window, start value in numbers of slots.
* Should be a power-of-2, minus 1. Device's default is 0x0f.
* @cw_max: Contention window, max value in numbers of slots.
* Should be a power-of-2, minus 1. Device's default is 0x3f.
* @aifsn: Number of slots in Arbitration Interframe Space (before
* performing random backoff timing prior to Tx). Device default 1.
* @fifos_mask: FIFOs used by this MAC for this AC
* @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0.
*
* One instance of this config struct for each of 4 EDCA access categories
* in struct iwl_qosparam_cmd.
*
* Device will automatically increase contention window by (2*CW) + 1 for each
* transmission retry. Device uses cw_max as a bit mask, ANDed with new CW
* value, to cap the CW value.
*/
struct iwl_ac_qos {
__le16 cw_min;
__le16 cw_max;
u8 aifsn;
u8 fifos_mask;
__le16 edca_txop;
} __packed; /* AC_QOS_API_S_VER_2 */
/**
* struct iwl_mac_ctx_cmd - command structure to configure MAC contexts
* ( MAC_CONTEXT_CMD = 0x28 )
* @id_and_color: ID and color of the MAC
* @action: action to perform, one of FW_CTXT_ACTION_*
* @mac_type: one of FW_MAC_TYPE_*
* @tsd_id: TSF HW timer, one of TSF_ID_*
* @node_addr: MAC address
* @bssid_addr: BSSID
* @cck_rates: basic rates available for CCK
* @ofdm_rates: basic rates available for OFDM
* @protection_flags: combination of MAC_PROT_FLG_FLAG_*
* @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise
* @short_slot: 0x10 for enabling short slots, 0 otherwise
* @filter_flags: combination of MAC_FILTER_*
* @qos_flags: from MAC_QOS_FLG_*
* @ac: one iwl_mac_qos configuration for each AC
* @mac_specific: one of struct iwl_mac_data_*, according to mac_type
*/
struct iwl_mac_ctx_cmd {
/* COMMON_INDEX_HDR_API_S_VER_1 */
__le32 id_and_color;
__le32 action;
/* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */
__le32 mac_type;
__le32 tsf_id;
u8 node_addr[6];
__le16 reserved_for_node_addr;
u8 bssid_addr[6];
__le16 reserved_for_bssid_addr;
__le32 cck_rates;
__le32 ofdm_rates;
__le32 protection_flags;
__le32 cck_short_preamble;
__le32 short_slot;
__le32 filter_flags;
/* MAC_QOS_PARAM_API_S_VER_1 */
__le32 qos_flags;
struct iwl_ac_qos ac[AC_NUM+1];
/* MAC_CONTEXT_COMMON_DATA_API_S */
union {
struct iwl_mac_data_ap ap;
struct iwl_mac_data_go go;
struct iwl_mac_data_sta sta;
struct iwl_mac_data_p2p_sta p2p_sta;
struct iwl_mac_data_p2p_dev p2p_dev;
struct iwl_mac_data_pibss pibss;
struct iwl_mac_data_ibss ibss;
};
} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */
static inline u32 iwl_mvm_reciprocal(u32 v)
{
if (!v)
return 0;
return 0xFFFFFFFF / v;
}
#endif /* __fw_api_mac_h__ */

View File

@@ -0,0 +1,120 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __fw_api_power_h__
#define __fw_api_power_h__
/* Power Management Commands, Responses, Notifications */
/**
* enum iwl_scan_flags - masks for power table command flags
* @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
* receiver and transmitter. '0' - does not allow.
* @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
* '1' Driver enables PM (use rest of parameters)
* @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
* '1' PM could sleep over DTIM till listen Interval.
* @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
* @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
*/
enum iwl_power_flags {
POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1),
POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2),
POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9),
POWER_FLAGS_LPRX_ENA_MSK = BIT(11),
};
#define IWL_POWER_VEC_SIZE 5
/**
* struct iwl_powertable_cmd - Power Table Command
* POWER_TABLE_CMD = 0x77 (command, has simple generic response)
*
* @flags: Power table command flags from POWER_FLAGS_*
* @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
* Minimum allowed:- 3 * DTIM. Keep alive period must be
* set regardless of power scheme or current power state.
* FW use this value also when PM is disabled.
* @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
* PSM transition - legacy PM
* @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
* PSM transition - legacy PM
* @sleep_interval: not in use
* @keep_alive_beacons: not in use
* @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
* Default: 80dbm
*/
struct iwl_powertable_cmd {
/* PM_POWER_TABLE_CMD_API_S_VER_5 */
__le16 flags;
u8 keep_alive_seconds;
u8 debug_flags;
__le32 rx_data_timeout;
__le32 tx_data_timeout;
__le32 sleep_interval[IWL_POWER_VEC_SIZE];
__le32 keep_alive_beacons;
__le32 lprx_rssi_threshold;
} __packed;
#endif

View File

@@ -0,0 +1,312 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __fw_api_rs_h__
#define __fw_api_rs_h__
#include "fw-api-mac.h"
/*
* These serve as indexes into
* struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
*/
enum {
IWL_RATE_1M_INDEX = 0,
IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
IWL_RATE_2M_INDEX,
IWL_RATE_5M_INDEX,
IWL_RATE_11M_INDEX,
IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
IWL_RATE_6M_INDEX,
IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
IWL_RATE_9M_INDEX,
IWL_RATE_12M_INDEX,
IWL_RATE_18M_INDEX,
IWL_RATE_24M_INDEX,
IWL_RATE_36M_INDEX,
IWL_RATE_48M_INDEX,
IWL_RATE_54M_INDEX,
IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX,
IWL_RATE_60M_INDEX,
IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1,
IWL_RATE_COUNT,
};
#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX)
/* fw API values for legacy bit rates, both OFDM and CCK */
enum {
IWL_RATE_6M_PLCP = 13,
IWL_RATE_9M_PLCP = 15,
IWL_RATE_12M_PLCP = 5,
IWL_RATE_18M_PLCP = 7,
IWL_RATE_24M_PLCP = 9,
IWL_RATE_36M_PLCP = 11,
IWL_RATE_48M_PLCP = 1,
IWL_RATE_54M_PLCP = 3,
IWL_RATE_1M_PLCP = 10,
IWL_RATE_2M_PLCP = 20,
IWL_RATE_5M_PLCP = 55,
IWL_RATE_11M_PLCP = 110,
};
/*
* rate_n_flags bit fields
*
* The 32-bit value has different layouts in the low 8 bites depending on the
* format. There are three formats, HT, VHT and legacy (11abg, with subformats
* for CCK and OFDM).
*
* High-throughput (HT) rate format
* bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM)
* Very High-throughput (VHT) rate format
* bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM)
* Legacy OFDM rate format for bits 7:0
* bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM)
* Legacy CCK rate format for bits 7:0:
* bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK)
*/
/* Bit 8: (1) HT format, (0) legacy or VHT format */
#define RATE_MCS_HT_POS 8
#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS)
/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */
#define RATE_MCS_CCK_POS 9
#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS)
/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */
#define RATE_MCS_VHT_POS 26
#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS)
/*
* High-throughput (HT) rate format for bits 7:0
*
* 2-0: MCS rate base
* 0) 6 Mbps
* 1) 12 Mbps
* 2) 18 Mbps
* 3) 24 Mbps
* 4) 36 Mbps
* 5) 48 Mbps
* 6) 54 Mbps
* 7) 60 Mbps
* 4-3: 0) Single stream (SISO)
* 1) Dual stream (MIMO)
* 2) Triple stream (MIMO)
* 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data
* (bits 7-6 are zero)
*
* Together the low 5 bits work out to the MCS index because we don't
* support MCSes above 15/23, and 0-7 have one stream, 8-15 have two
* streams and 16-23 have three streams. We could also support MCS 32
* which is the duplicate 20 MHz MCS (bit 5 set, all others zero.)
*/
#define RATE_HT_MCS_RATE_CODE_MSK 0x7
/* Bit 10: (1) Use Green Field preamble */
#define RATE_HT_MCS_GF_POS 10
#define RATE_HT_MCS_GF_MSK (1 << RATE_HT_MCS_GF_POS)
#define RATE_HT_MCS_INDEX_MSK 0x3f
/*
* Very High-throughput (VHT) rate format for bits 7:0
*
* 3-0: VHT MCS (0-9)
* 5-4: number of streams - 1:
* 0) Single stream (SISO)
* 1) Dual stream (MIMO)
* 2) Triple stream (MIMO)
*/
/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */
#define RATE_VHT_MCS_RATE_CODE_MSK 0xf
#define RATE_VHT_MCS_NSS_POS 4
#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS)
/*
* Legacy OFDM rate format for bits 7:0
*
* 3-0: 0xD) 6 Mbps
* 0xF) 9 Mbps
* 0x5) 12 Mbps
* 0x7) 18 Mbps
* 0x9) 24 Mbps
* 0xB) 36 Mbps
* 0x1) 48 Mbps
* 0x3) 54 Mbps
* (bits 7-4 are 0)
*
* Legacy CCK rate format for bits 7:0:
* bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK):
*
* 6-0: 10) 1 Mbps
* 20) 2 Mbps
* 55) 5.5 Mbps
* 110) 11 Mbps
* (bit 7 is 0)
*/
#define RATE_LEGACY_RATE_MSK 0xff
/*
* Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz
* 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT
*/
#define RATE_MCS_CHAN_WIDTH_POS 11
#define RATE_MCS_CHAN_WIDTH_MSK (3 << RATE_MCS_CHAN_WIDTH_POS)
#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS)
#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS)
#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS)
#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS)
/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */
#define RATE_MCS_SGI_POS 13
#define RATE_MCS_SGI_MSK (1 << RATE_MCS_SGI_POS)
/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */
#define RATE_MCS_ANT_POS 14
#define RATE_MCS_ANT_A_MSK (1 << RATE_MCS_ANT_POS)
#define RATE_MCS_ANT_B_MSK (2 << RATE_MCS_ANT_POS)
#define RATE_MCS_ANT_C_MSK (4 << RATE_MCS_ANT_POS)
#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | \
RATE_MCS_ANT_B_MSK)
#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \
RATE_MCS_ANT_C_MSK)
#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK
#define RATE_MCS_ANT_NUM 3
/* Bit 17-18: (0) SS, (1) SS*2 */
#define RATE_MCS_STBC_POS 17
#define RATE_MCS_STBC_MSK (1 << RATE_MCS_STBC_POS)
/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */
#define RATE_MCS_BF_POS 19
#define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS)
/* Bit 20: (0) ZLF is off, (1) ZLF is on */
#define RATE_MCS_ZLF_POS 20
#define RATE_MCS_ZLF_MSK (1 << RATE_MCS_ZLF_POS)
/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */
#define RATE_MCS_DUP_POS 24
#define RATE_MCS_DUP_MSK (3 << RATE_MCS_DUP_POS)
/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */
#define RATE_MCS_LDPC_POS 27
#define RATE_MCS_LDPC_MSK (1 << RATE_MCS_LDPC_POS)
/* Link Quality definitions */
/* # entries in rate scale table to support Tx retries */
#define LQ_MAX_RETRY_NUM 16
/* Link quality command flags, only this one is available */
#define LQ_FLAG_SET_STA_TLC_RTS_MSK BIT(0)
/**
* struct iwl_lq_cmd - link quality command
* @sta_id: station to update
* @control: not used
* @flags: combination of LQ_FLAG_*
* @mimo_delim: the first SISO index in rs_table, which separates MIMO
* and SISO rates
* @single_stream_ant_msk: best antenna for SISO (can be dual in CDD).
* Should be ANT_[ABC]
* @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC]
* @initial_rate_index: first index from rs_table per AC category
* @agg_time_limit: aggregation max time threshold in usec/100, meaning
* value of 100 is one usec. Range is 100 to 8000
* @agg_disable_start_th: try-count threshold for starting aggregation.
* If a frame has higher try-count, it should not be selected for
* starting an aggregation sequence.
* @agg_frame_cnt_limit: max frame count in an aggregation.
* 0: no limit
* 1: no aggregation (one frame per aggregation)
* 2 - 0x3f: maximal number of frames (up to 3f == 63)
* @rs_table: array of rates for each TX try, each is rate_n_flags,
* meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP
* @bf_params: beam forming params, currently not used
*/
struct iwl_lq_cmd {
u8 sta_id;
u8 reserved1;
u16 control;
/* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */
u8 flags;
u8 mimo_delim;
u8 single_stream_ant_msk;
u8 dual_stream_ant_msk;
u8 initial_rate_index[AC_NUM];
/* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */
__le16 agg_time_limit;
u8 agg_disable_start_th;
u8 agg_frame_cnt_limit;
__le32 reserved2;
__le32 rs_table[LQ_MAX_RETRY_NUM];
__le32 bf_params;
}; /* LINK_QUALITY_CMD_API_S_VER_1 */
#endif /* __fw_api_rs_h__ */

View File

@@ -0,0 +1,560 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __fw_api_scan_h__
#define __fw_api_scan_h__
#include "fw-api.h"
/* Scan Commands, Responses, Notifications */
/* Masks for iwl_scan_channel.type flags */
#define SCAN_CHANNEL_TYPE_ACTIVE BIT(0)
#define SCAN_CHANNEL_NARROW_BAND BIT(22)
/* Max number of IEs for direct SSID scans in a command */
#define PROBE_OPTION_MAX 20
/**
* struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table
* @channel: band is selected by iwl_scan_cmd "flags" field
* @tx_gain: gain for analog radio
* @dsp_atten: gain for DSP
* @active_dwell: dwell time for active scan in TU, typically 5-50
* @passive_dwell: dwell time for passive scan in TU, typically 20-500
* @type: type is broken down to these bits:
* bit 0: 0 = passive, 1 = active
* bits 1-20: SSID direct bit map. If any of these bits is set then
* the corresponding SSID IE is transmitted in probe request
* (bit i adds IE in position i to the probe request)
* bit 22: channel width, 0 = regular, 1 = TGj narrow channel
*
* @iteration_count:
* @iteration_interval:
* This struct is used once for each channel in the scan list.
* Each channel can independently select:
* 1) SSID for directed active scans
* 2) Txpower setting (for rate specified within Tx command)
* 3) How long to stay on-channel (behavior may be modified by quiet_time,
* quiet_plcp_th, good_CRC_th)
*
* To avoid uCode errors, make sure the following are true (see comments
* under struct iwl_scan_cmd about max_out_time and quiet_time):
* 1) If using passive_dwell (i.e. passive_dwell != 0):
* active_dwell <= passive_dwell (< max_out_time if max_out_time != 0)
* 2) quiet_time <= active_dwell
* 3) If restricting off-channel time (i.e. max_out_time !=0):
* passive_dwell < max_out_time
* active_dwell < max_out_time
*/
struct iwl_scan_channel {
__le32 type;
__le16 channel;
__le16 iteration_count;
__le32 iteration_interval;
__le16 active_dwell;
__le16 passive_dwell;
} __packed; /* SCAN_CHANNEL_CONTROL_API_S_VER_1 */
/**
* struct iwl_ssid_ie - directed scan network information element
*
* Up to 20 of these may appear in REPLY_SCAN_CMD,
* selected by "type" bit field in struct iwl_scan_channel;
* each channel may select different ssids from among the 20 entries.
* SSID IEs get transmitted in reverse order of entry.
*/
struct iwl_ssid_ie {
u8 id;
u8 len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
/**
* iwl_scan_flags - masks for scan command flags
*@SCAN_FLAGS_PERIODIC_SCAN:
*@SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX:
*@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
*@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
*@SCAN_FLAGS_FRAGMENTED_SCAN:
*/
enum iwl_scan_flags {
SCAN_FLAGS_PERIODIC_SCAN = BIT(0),
SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX = BIT(1),
SCAN_FLAGS_DELAYED_SCAN_LOWBAND = BIT(2),
SCAN_FLAGS_DELAYED_SCAN_HIGHBAND = BIT(3),
SCAN_FLAGS_FRAGMENTED_SCAN = BIT(4),
};
/**
* enum iwl_scan_type - Scan types for scan command
* @SCAN_TYPE_FORCED:
* @SCAN_TYPE_BACKGROUND:
* @SCAN_TYPE_OS:
* @SCAN_TYPE_ROAMING:
* @SCAN_TYPE_ACTION:
* @SCAN_TYPE_DISCOVERY:
* @SCAN_TYPE_DISCOVERY_FORCED:
*/
enum iwl_scan_type {
SCAN_TYPE_FORCED = 0,
SCAN_TYPE_BACKGROUND = 1,
SCAN_TYPE_OS = 2,
SCAN_TYPE_ROAMING = 3,
SCAN_TYPE_ACTION = 4,
SCAN_TYPE_DISCOVERY = 5,
SCAN_TYPE_DISCOVERY_FORCED = 6,
}; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
/* Maximal number of channels to scan */
#define MAX_NUM_SCAN_CHANNELS 0x24
/**
* struct iwl_scan_cmd - scan request command
* ( SCAN_REQUEST_CMD = 0x80 )
* @len: command length in bytes
* @scan_flags: scan flags from SCAN_FLAGS_*
* @channel_count: num of channels in channel list (1 - MAX_NUM_SCAN_CHANNELS)
* @quiet_time: in msecs, dwell this time for active scan on quiet channels
* @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
* this number of packets were received (typically 1)
* @passive2active: is auto switching from passive to active allowed (0 or 1)
* @rxchain_sel_flags: RXON_RX_CHAIN_*
* @max_out_time: in usecs, max out of serving channel time
* @suspend_time: how long to pause scan when returning to service channel:
* bits 0-19: beacon interal in usecs (suspend before executing)
* bits 20-23: reserved
* bits 24-31: number of beacons (suspend between channels)
* @rxon_flags: RXON_FLG_*
* @filter_flags: RXON_FILTER_*
* @tx_cmd: for active scans (zero for passive), w/o payload,
* no RS so specify TX rate
* @direct_scan: direct scan SSIDs
* @type: one of SCAN_TYPE_*
* @repeats: how many time to repeat the scan
*/
struct iwl_scan_cmd {
__le16 len;
u8 scan_flags;
u8 channel_count;
__le16 quiet_time;
__le16 quiet_plcp_th;
__le16 passive2active;
__le16 rxchain_sel_flags;
__le32 max_out_time;
__le32 suspend_time;
/* RX_ON_FLAGS_API_S_VER_1 */
__le32 rxon_flags;
__le32 filter_flags;
struct iwl_tx_cmd tx_cmd;
struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
__le32 type;
__le32 repeats;
/*
* Probe request frame, followed by channel list.
*
* Size of probe request frame is specified by byte count in tx_cmd.
* Channel list follows immediately after probe request frame.
* Number of channels in list is specified by channel_count.
* Each channel in list is of type:
*
* struct iwl_scan_channel channels[0];
*
* NOTE: Only one band of channels can be scanned per pass. You
* must not mix 2.4GHz channels and 5.2GHz channels, and you must wait
* for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION)
* before requesting another scan.
*/
u8 data[0];
} __packed; /* SCAN_REQUEST_FIXED_PART_API_S_VER_5 */
/* Response to scan request contains only status with one of these values */
#define SCAN_RESPONSE_OK 0x1
#define SCAN_RESPONSE_ERROR 0x2
/*
* SCAN_ABORT_CMD = 0x81
* When scan abort is requested, the command has no fields except the common
* header. The response contains only a status with one of these values.
*/
#define SCAN_ABORT_POSSIBLE 0x1
#define SCAN_ABORT_IGNORED 0x2 /* no pending scans */
/* TODO: complete documentation */
#define SCAN_OWNER_STATUS 0x1
#define MEASURE_OWNER_STATUS 0x2
/**
* struct iwl_scan_start_notif - notifies start of scan in the device
* ( SCAN_START_NOTIFICATION = 0x82 )
* @tsf_low: TSF timer (lower half) in usecs
* @tsf_high: TSF timer (higher half) in usecs
* @beacon_timer: structured as follows:
* bits 0:19 - beacon interval in usecs
* bits 20:23 - reserved (0)
* bits 24:31 - number of beacons
* @channel: which channel is scanned
* @band: 0 for 5.2 GHz, 1 for 2.4 GHz
* @status: one of *_OWNER_STATUS
*/
struct iwl_scan_start_notif {
__le32 tsf_low;
__le32 tsf_high;
__le32 beacon_timer;
u8 channel;
u8 band;
u8 reserved[2];
__le32 status;
} __packed; /* SCAN_START_NTF_API_S_VER_1 */
/* scan results probe_status first bit indicates success */
#define SCAN_PROBE_STATUS_OK 0
#define SCAN_PROBE_STATUS_TX_FAILED BIT(0)
/* error statuses combined with TX_FAILED */
#define SCAN_PROBE_STATUS_FAIL_TTL BIT(1)
#define SCAN_PROBE_STATUS_FAIL_BT BIT(2)
/* How many statistics are gathered for each channel */
#define SCAN_RESULTS_STATISTICS 1
/**
* enum iwl_scan_complete_status - status codes for scan complete notifications
* @SCAN_COMP_STATUS_OK: scan completed successfully
* @SCAN_COMP_STATUS_ABORT: scan was aborted by user
* @SCAN_COMP_STATUS_ERR_SLEEP: sending null sleep packet failed
* @SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT: timeout before channel is ready
* @SCAN_COMP_STATUS_ERR_PROBE: sending probe request failed
* @SCAN_COMP_STATUS_ERR_WAKEUP: sending null wakeup packet failed
* @SCAN_COMP_STATUS_ERR_ANTENNAS: invalid antennas chosen at scan command
* @SCAN_COMP_STATUS_ERR_INTERNAL: internal error caused scan abort
* @SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax
* @SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful
* (not an error!)
* @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver
* asked for
* @SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events
*/
enum iwl_scan_complete_status {
SCAN_COMP_STATUS_OK = 0x1,
SCAN_COMP_STATUS_ABORT = 0x2,
SCAN_COMP_STATUS_ERR_SLEEP = 0x3,
SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT = 0x4,
SCAN_COMP_STATUS_ERR_PROBE = 0x5,
SCAN_COMP_STATUS_ERR_WAKEUP = 0x6,
SCAN_COMP_STATUS_ERR_ANTENNAS = 0x7,
SCAN_COMP_STATUS_ERR_INTERNAL = 0x8,
SCAN_COMP_STATUS_ERR_COEX = 0x9,
SCAN_COMP_STATUS_P2P_ACTION_OK = 0xA,
SCAN_COMP_STATUS_ITERATION_END = 0x0B,
SCAN_COMP_STATUS_ERR_ALLOC_TE = 0x0C,
};
/**
* struct iwl_scan_results_notif - scan results for one channel
* ( SCAN_RESULTS_NOTIFICATION = 0x83 )
* @channel: which channel the results are from
* @band: 0 for 5.2 GHz, 1 for 2.4 GHz
* @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request
* @num_probe_not_sent: # of request that weren't sent due to not enough time
* @duration: duration spent in channel, in usecs
* @statistics: statistics gathered for this channel
*/
struct iwl_scan_results_notif {
u8 channel;
u8 band;
u8 probe_status;
u8 num_probe_not_sent;
__le32 duration;
__le32 statistics[SCAN_RESULTS_STATISTICS];
} __packed; /* SCAN_RESULT_NTF_API_S_VER_2 */
/**
* struct iwl_scan_complete_notif - notifies end of scanning (all channels)
* ( SCAN_COMPLETE_NOTIFICATION = 0x84 )
* @scanned_channels: number of channels scanned (and number of valid results)
* @status: one of SCAN_COMP_STATUS_*
* @bt_status: BT on/off status
* @last_channel: last channel that was scanned
* @tsf_low: TSF timer (lower half) in usecs
* @tsf_high: TSF timer (higher half) in usecs
* @results: all scan results, only "scanned_channels" of them are valid
*/
struct iwl_scan_complete_notif {
u8 scanned_channels;
u8 status;
u8 bt_status;
u8 last_channel;
__le32 tsf_low;
__le32 tsf_high;
struct iwl_scan_results_notif results[MAX_NUM_SCAN_CHANNELS];
} __packed; /* SCAN_COMPLETE_NTF_API_S_VER_2 */
/* scan offload */
#define IWL_MAX_SCAN_CHANNELS 40
#define IWL_SCAN_MAX_BLACKLIST_LEN 64
#define IWL_SCAN_MAX_PROFILES 11
#define SCAN_OFFLOAD_PROBE_REQ_SIZE 512
/* Default watchdog (in MS) for scheduled scan iteration */
#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000)
#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1)
#define CAN_ABORT_STATUS 1
#define IWL_FULL_SCAN_MULTIPLIER 5
#define IWL_FAST_SCHED_SCAN_ITERATIONS 3
/**
* struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6
* @scan_flags: see enum iwl_scan_flags
* @channel_count: channels in channel list
* @quiet_time: dwell time, in milisiconds, on quiet channel
* @quiet_plcp_th: quiet channel num of packets threshold
* @good_CRC_th: passive to active promotion threshold
* @rx_chain: RXON rx chain.
* @max_out_time: max uSec to be out of assoceated channel
* @suspend_time: pause scan this long when returning to service channel
* @flags: RXON flags
* @filter_flags: RXONfilter
* @tx_cmd: tx command for active scan; for 2GHz and for 5GHz.
* @direct_scan: list of SSIDs for directed active scan
* @scan_type: see enum iwl_scan_type.
* @rep_count: repetition count for each scheduled scan iteration.
*/
struct iwl_scan_offload_cmd {
__le16 len;
u8 scan_flags;
u8 channel_count;
__le16 quiet_time;
__le16 quiet_plcp_th;
__le16 good_CRC_th;
__le16 rx_chain;
__le32 max_out_time;
__le32 suspend_time;
/* RX_ON_FLAGS_API_S_VER_1 */
__le32 flags;
__le32 filter_flags;
struct iwl_tx_cmd tx_cmd[2];
/* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
__le32 scan_type;
__le32 rep_count;
} __packed;
enum iwl_scan_offload_channel_flags {
IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE = BIT(0),
IWL_SCAN_OFFLOAD_CHANNEL_NARROW = BIT(22),
IWL_SCAN_OFFLOAD_CHANNEL_FULL = BIT(24),
IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL = BIT(25),
};
/**
* iwl_scan_channel_cfg - SCAN_CHANNEL_CFG_S
* @type: bitmap - see enum iwl_scan_offload_channel_flags.
* 0: passive (0) or active (1) scan.
* 1-20: directed scan to i'th ssid.
* 22: channel width configuation - 1 for narrow.
* 24: full scan.
* 25: partial scan.
* @channel_number: channel number 1-13 etc.
* @iter_count: repetition count for the channel.
* @iter_interval: interval between two innteration on one channel.
* @dwell_time: entry 0 - active scan, entry 1 - passive scan.
*/
struct iwl_scan_channel_cfg {
__le32 type[IWL_MAX_SCAN_CHANNELS];
__le16 channel_number[IWL_MAX_SCAN_CHANNELS];
__le16 iter_count[IWL_MAX_SCAN_CHANNELS];
__le32 iter_interval[IWL_MAX_SCAN_CHANNELS];
u8 dwell_time[IWL_MAX_SCAN_CHANNELS][2];
} __packed;
/**
* iwl_scan_offload_cfg - SCAN_OFFLOAD_CONFIG_API_S
* @scan_cmd: scan command fixed part
* @channel_cfg: scan channel configuration
* @data: probe request frames (one per band)
*/
struct iwl_scan_offload_cfg {
struct iwl_scan_offload_cmd scan_cmd;
struct iwl_scan_channel_cfg channel_cfg;
u8 data[0];
} __packed;
/**
* iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S
* @ssid: MAC address to filter out
* @reported_rssi: AP rssi reported to the host
*/
struct iwl_scan_offload_blacklist {
u8 ssid[ETH_ALEN];
u8 reported_rssi;
u8 reserved;
} __packed;
enum iwl_scan_offload_network_type {
IWL_NETWORK_TYPE_BSS = 1,
IWL_NETWORK_TYPE_IBSS = 2,
IWL_NETWORK_TYPE_ANY = 3,
};
enum iwl_scan_offload_band_selection {
IWL_SCAN_OFFLOAD_SELECT_2_4 = 0x4,
IWL_SCAN_OFFLOAD_SELECT_5_2 = 0x8,
IWL_SCAN_OFFLOAD_SELECT_ANY = 0xc,
};
/**
* iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S
* @ssid_index: index to ssid list in fixed part
* @unicast_cipher: encryption olgorithm to match - bitmap
* @aut_alg: authentication olgorithm to match - bitmap
* @network_type: enum iwl_scan_offload_network_type
* @band_selection: enum iwl_scan_offload_band_selection
*/
struct iwl_scan_offload_profile {
u8 ssid_index;
u8 unicast_cipher;
u8 auth_alg;
u8 network_type;
u8 band_selection;
u8 reserved[3];
} __packed;
/**
* iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1
* @blaclist: AP list to filter off from scan results
* @profiles: profiles to search for match
* @blacklist_len: length of blacklist
* @num_profiles: num of profiles in the list
*/
struct iwl_scan_offload_profile_cfg {
struct iwl_scan_offload_blacklist blacklist[IWL_SCAN_MAX_BLACKLIST_LEN];
struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES];
u8 blacklist_len;
u8 num_profiles;
u8 reserved[2];
} __packed;
/**
* iwl_scan_offload_schedule - schedule of scan offload
* @delay: delay between iterations, in seconds.
* @iterations: num of scan iterations
* @full_scan_mul: number of partial scans before each full scan
*/
struct iwl_scan_offload_schedule {
u16 delay;
u8 iterations;
u8 full_scan_mul;
} __packed;
/*
* iwl_scan_offload_flags
*
* IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID: filter mode - upload every beacon or match
* ssid list.
* IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan.
* IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan
* on A band.
*/
enum iwl_scan_offload_flags {
IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID = BIT(0),
IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL = BIT(2),
IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN = BIT(3),
};
/**
* iwl_scan_offload_req - scan offload request command
* @flags: bitmap - enum iwl_scan_offload_flags.
* @watchdog: maximum scan duration in TU.
* @delay: delay in seconds before first iteration.
* @schedule_line: scan offload schedule, for fast and regular scan.
*/
struct iwl_scan_offload_req {
__le16 flags;
__le16 watchdog;
__le16 delay;
__le16 reserved;
struct iwl_scan_offload_schedule schedule_line[2];
} __packed;
enum iwl_scan_offload_compleate_status {
IWL_SCAN_OFFLOAD_COMPLETED = 1,
IWL_SCAN_OFFLOAD_ABORTED = 2,
};
/**
* iwl_scan_offload_complete - SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1
* @last_schedule_line: last schedule line executed (fast or regular)
* @last_schedule_iteration: last scan iteration executed before scan abort
* @status: enum iwl_scan_offload_compleate_status
*/
struct iwl_scan_offload_complete {
u8 last_schedule_line;
u8 last_schedule_iteration;
u8 status;
u8 reserved;
} __packed;
#endif

View File

@@ -0,0 +1,380 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __fw_api_sta_h__
#define __fw_api_sta_h__
/**
* enum iwl_sta_flags - flags for the ADD_STA host command
* @STA_FLG_REDUCED_TX_PWR_CTRL:
* @STA_FLG_REDUCED_TX_PWR_DATA:
* @STA_FLG_FLG_ANT_MSK: Antenna selection
* @STA_FLG_PS: set if STA is in Power Save
* @STA_FLG_INVALID: set if STA is invalid
* @STA_FLG_DLP_EN: Direct Link Protocol is enabled
* @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs
* @STA_FLG_DRAIN_FLOW: drain flow
* @STA_FLG_PAN: STA is for PAN interface
* @STA_FLG_CLASS_AUTH:
* @STA_FLG_CLASS_ASSOC:
* @STA_FLG_CLASS_MIMO_PROT:
* @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU
* @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation
* @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is
* initialised by driver and can be updated by fw upon reception of
* action frames that can change the channel width. When cleared the fw
* will send all the frames in 20MHz even when FAT channel is requested.
* @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the
* driver and can be updated by fw upon reception of action frames.
* @STA_FLG_MFP_EN: Management Frame Protection
*/
enum iwl_sta_flags {
STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3),
STA_FLG_REDUCED_TX_PWR_DATA = BIT(6),
STA_FLG_FLG_ANT_A = (1 << 4),
STA_FLG_FLG_ANT_B = (2 << 4),
STA_FLG_FLG_ANT_MSK = (STA_FLG_FLG_ANT_A |
STA_FLG_FLG_ANT_B),
STA_FLG_PS = BIT(8),
STA_FLG_INVALID = BIT(9),
STA_FLG_DLP_EN = BIT(10),
STA_FLG_SET_ALL_KEYS = BIT(11),
STA_FLG_DRAIN_FLOW = BIT(12),
STA_FLG_PAN = BIT(13),
STA_FLG_CLASS_AUTH = BIT(14),
STA_FLG_CLASS_ASSOC = BIT(15),
STA_FLG_RTS_MIMO_PROT = BIT(17),
STA_FLG_MAX_AGG_SIZE_SHIFT = 19,
STA_FLG_MAX_AGG_SIZE_8K = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_MAX_AGG_SIZE_16K = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_MAX_AGG_SIZE_32K = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_MAX_AGG_SIZE_64K = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_MAX_AGG_SIZE_128K = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_MAX_AGG_SIZE_256K = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_MAX_AGG_SIZE_512K = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_MAX_AGG_SIZE_1024K = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_MAX_AGG_SIZE_MSK = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
STA_FLG_AGG_MPDU_DENS_SHIFT = 23,
STA_FLG_AGG_MPDU_DENS_2US = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT),
STA_FLG_AGG_MPDU_DENS_4US = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT),
STA_FLG_AGG_MPDU_DENS_8US = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT),
STA_FLG_AGG_MPDU_DENS_16US = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
STA_FLG_AGG_MPDU_DENS_MSK = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
STA_FLG_FAT_EN_20MHZ = (0 << 26),
STA_FLG_FAT_EN_40MHZ = (1 << 26),
STA_FLG_FAT_EN_80MHZ = (2 << 26),
STA_FLG_FAT_EN_160MHZ = (3 << 26),
STA_FLG_FAT_EN_MSK = (3 << 26),
STA_FLG_MIMO_EN_SISO = (0 << 28),
STA_FLG_MIMO_EN_MIMO2 = (1 << 28),
STA_FLG_MIMO_EN_MIMO3 = (2 << 28),
STA_FLG_MIMO_EN_MSK = (3 << 28),
};
/**
* enum iwl_sta_key_flag - key flags for the ADD_STA host command
* @STA_KEY_FLG_EN_MSK: mask for encryption algorithm
* @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
* station info array (1 - n 1X mode)
* @STA_KEY_FLG_KEYID_MSK: the index of the key
* @STA_KEY_NOT_VALID: key is invalid
* @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key
* @STA_KEY_MULTICAST: set for multical key
* @STA_KEY_MFP: key is used for Management Frame Protection
*/
enum iwl_sta_key_flag {
STA_KEY_FLG_NO_ENC = (0 << 0),
STA_KEY_FLG_WEP = (1 << 0),
STA_KEY_FLG_CCM = (2 << 0),
STA_KEY_FLG_TKIP = (3 << 0),
STA_KEY_FLG_CMAC = (6 << 0),
STA_KEY_FLG_ENC_UNKNOWN = (7 << 0),
STA_KEY_FLG_EN_MSK = (7 << 0),
STA_KEY_FLG_WEP_KEY_MAP = BIT(3),
STA_KEY_FLG_KEYID_POS = 8,
STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS),
STA_KEY_NOT_VALID = BIT(11),
STA_KEY_FLG_WEP_13BYTES = BIT(12),
STA_KEY_MULTICAST = BIT(14),
STA_KEY_MFP = BIT(15),
};
/**
* enum iwl_sta_modify_flag - indicate to the fw what flag are being changed
* @STA_MODIFY_KEY: this command modifies %key
* @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx
* @STA_MODIFY_TX_RATE: unused
* @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid
* @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid
* @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count
* @STA_MODIFY_PROT_TH:
* @STA_MODIFY_QUEUES: modify the queues used by this station
*/
enum iwl_sta_modify_flag {
STA_MODIFY_KEY = BIT(0),
STA_MODIFY_TID_DISABLE_TX = BIT(1),
STA_MODIFY_TX_RATE = BIT(2),
STA_MODIFY_ADD_BA_TID = BIT(3),
STA_MODIFY_REMOVE_BA_TID = BIT(4),
STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5),
STA_MODIFY_PROT_TH = BIT(6),
STA_MODIFY_QUEUES = BIT(7),
};
#define STA_MODE_MODIFY 1
/**
* enum iwl_sta_sleep_flag - type of sleep of the station
* @STA_SLEEP_STATE_AWAKE:
* @STA_SLEEP_STATE_PS_POLL:
* @STA_SLEEP_STATE_UAPSD:
*/
enum iwl_sta_sleep_flag {
STA_SLEEP_STATE_AWAKE = 0,
STA_SLEEP_STATE_PS_POLL = BIT(0),
STA_SLEEP_STATE_UAPSD = BIT(1),
};
/* STA ID and color bits definitions */
#define STA_ID_SEED (0x0f)
#define STA_ID_POS (0)
#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS)
#define STA_COLOR_SEED (0x7)
#define STA_COLOR_POS (4)
#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS)
#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \
(((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS)
#define STA_ID_N_COLOR_GET_ID(id_n_color) \
(((id_n_color) & STA_ID_MSK) >> STA_ID_POS)
#define STA_KEY_MAX_NUM (16)
#define STA_KEY_IDX_INVALID (0xff)
#define STA_KEY_MAX_DATA_KEY_NUM (4)
#define IWL_MAX_GLOBAL_KEYS (4)
#define STA_KEY_LEN_WEP40 (5)
#define STA_KEY_LEN_WEP104 (13)
/**
* struct iwl_mvm_keyinfo - key information
* @key_flags: type %iwl_sta_key_flag
* @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
* @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
* @key_offset: key offset in the fw's key table
* @key: 16-byte unicast decryption key
* @tx_secur_seq_cnt: initial RSC / PN needed for replay check
* @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only
* @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only
*/
struct iwl_mvm_keyinfo {
__le16 key_flags;
u8 tkip_rx_tsc_byte2;
u8 reserved1;
__le16 tkip_rx_ttak[5];
u8 key_offset;
u8 reserved2;
u8 key[16];
__le64 tx_secur_seq_cnt;
__le64 hw_tkip_mic_rx_key;
__le64 hw_tkip_mic_tx_key;
} __packed;
/**
* struct iwl_mvm_add_sta_cmd - Add / modify a station in the fw's station table
* ( REPLY_ADD_STA = 0x18 )
* @add_modify: 1: modify existing, 0: add new station
* @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent
* @multicast_tx_key_id: multicast tx key id. Relevant only when multicast key
* sent
* @mac_id_n_color: the Mac context this station belongs to
* @addr[ETH_ALEN]: station's MAC address
* @sta_id: index of station in uCode's station table
* @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave
* alone. 1 - modify, 0 - don't change.
* @key: look at %iwl_mvm_keyinfo
* @station_flags: look at %iwl_sta_flags
* @station_flags_msk: what of %station_flags have changed
* @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
* AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
* @add_immediate_ba_tid: tid for which to add block-ack support (Rx)
* Set %STA_MODIFY_ADD_BA_TID to use this field, and also set
* add_immediate_ba_ssn.
* @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx)
* Set %STA_MODIFY_REMOVE_BA_TID to use this field
* @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with
* add_immediate_ba_tid.
* @sleep_tx_count: number of packets to transmit to station even though it is
* asleep. Used to synchronise PS-poll and u-APSD responses while ucode
* keeps track of STA sleep state.
* @sleep_state_flags: Look at %iwl_sta_sleep_flag.
* @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP
* mac-addr.
* @beamform_flags: beam forming controls
* @tfd_queue_msk: tfd queues used by this station
*
* The device contains an internal table of per-station information, with info
* on security keys, aggregation parameters, and Tx rates for initial Tx
* attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD).
*
* ADD_STA sets up the table entry for one station, either creating a new
* entry, or modifying a pre-existing one.
*/
struct iwl_mvm_add_sta_cmd {
u8 add_modify;
u8 unicast_tx_key_id;
u8 multicast_tx_key_id;
u8 reserved1;
__le32 mac_id_n_color;
u8 addr[ETH_ALEN];
__le16 reserved2;
u8 sta_id;
u8 modify_mask;
__le16 reserved3;
struct iwl_mvm_keyinfo key;
__le32 station_flags;
__le32 station_flags_msk;
__le16 tid_disable_tx;
__le16 reserved4;
u8 add_immediate_ba_tid;
u8 remove_immediate_ba_tid;
__le16 add_immediate_ba_ssn;
__le16 sleep_tx_count;
__le16 sleep_state_flags;
__le16 assoc_id;
__le16 beamform_flags;
__le32 tfd_queue_msk;
} __packed; /* ADD_STA_CMD_API_S_VER_5 */
/**
* enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command
* @ADD_STA_SUCCESS: operation was executed successfully
* @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table
* @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session
* @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that
* doesn't exist.
*/
enum iwl_mvm_add_sta_rsp_status {
ADD_STA_SUCCESS = 0x1,
ADD_STA_STATIONS_OVERLOAD = 0x2,
ADD_STA_IMMEDIATE_BA_FAILURE = 0x4,
ADD_STA_MODIFY_NON_EXISTING_STA = 0x8,
};
/**
* struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table
* ( REMOVE_STA = 0x19 )
* @sta_id: the station id of the station to be removed
*/
struct iwl_mvm_rm_sta_cmd {
u8 sta_id;
u8 reserved[3];
} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */
/**
* struct iwl_mvm_mgmt_mcast_key_cmd
* ( MGMT_MCAST_KEY = 0x1f )
* @ctrl_flags: %iwl_sta_key_flag
* @IGTK:
* @K1: IGTK master key
* @K2: IGTK sub key
* @sta_id: station ID that support IGTK
* @key_id:
* @receive_seq_cnt: initial RSC/PN needed for replay check
*/
struct iwl_mvm_mgmt_mcast_key_cmd {
__le32 ctrl_flags;
u8 IGTK[16];
u8 K1[16];
u8 K2[16];
__le32 key_id;
__le32 sta_id;
__le64 receive_seq_cnt;
} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */
struct iwl_mvm_wep_key {
u8 key_index;
u8 key_offset;
__le16 reserved1;
u8 key_size;
u8 reserved2[3];
u8 key[16];
} __packed;
struct iwl_mvm_wep_key_cmd {
__le32 mac_id_n_color;
u8 num_keys;
u8 decryption_type;
u8 flags;
u8 reserved;
struct iwl_mvm_wep_key wep_key[0];
} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */
#endif /* __fw_api_sta_h__ */

View File

@@ -0,0 +1,586 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __fw_api_tx_h__
#define __fw_api_tx_h__
/**
* enum iwl_tx_flags - bitmasks for tx_flags in TX command
* @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame
* @TX_CMD_FLG_ACK: expect ACK from receiving station
* @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command.
* Otherwise, use rate_n_flags from the TX command
* @TX_CMD_FLG_BA: this frame is a block ack
* @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected
* Must set TX_CMD_FLG_ACK with this flag.
* @TX_CMD_FLG_TXOP_PROT: protect frame with full TXOP protection
* @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
* @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
* @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
* @TX_CMD_FLG_BT_DIS: disable BT priority for this frame
* @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
* Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
* @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU
* @TX_CMD_FLG_NEXT_FRAME: this frame includes information of the next frame
* @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame
* Should be set for beacons and probe responses
* @TX_CMD_FLG_CALIB: activate PA TX power calibrations
* @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count
* @TX_CMD_FLG_AGG_START: allow this frame to start aggregation
* @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header.
* Should be set for 26/30 length MAC headers
* @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
* @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration
* @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation
* @TX_CMD_FLG_CTS_ONLY: send CTS only, no data after that
* @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id
* @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped
* @TX_CMD_FLG_EXEC_PAPD: execute PAPD
* @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power
* @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk
*/
enum iwl_tx_flags {
TX_CMD_FLG_PROT_REQUIRE = BIT(0),
TX_CMD_FLG_ACK = BIT(3),
TX_CMD_FLG_STA_RATE = BIT(4),
TX_CMD_FLG_BA = BIT(5),
TX_CMD_FLG_BAR = BIT(6),
TX_CMD_FLG_TXOP_PROT = BIT(7),
TX_CMD_FLG_VHT_NDPA = BIT(8),
TX_CMD_FLG_HT_NDPA = BIT(9),
TX_CMD_FLG_CSI_FDBK2HOST = BIT(10),
TX_CMD_FLG_BT_DIS = BIT(12),
TX_CMD_FLG_SEQ_CTL = BIT(13),
TX_CMD_FLG_MORE_FRAG = BIT(14),
TX_CMD_FLG_NEXT_FRAME = BIT(15),
TX_CMD_FLG_TSF = BIT(16),
TX_CMD_FLG_CALIB = BIT(17),
TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18),
TX_CMD_FLG_AGG_START = BIT(19),
TX_CMD_FLG_MH_PAD = BIT(20),
TX_CMD_FLG_RESP_TO_DRV = BIT(21),
TX_CMD_FLG_CCMP_AGG = BIT(22),
TX_CMD_FLG_TKIP_MIC_DONE = BIT(23),
TX_CMD_FLG_CTS_ONLY = BIT(24),
TX_CMD_FLG_DUR = BIT(25),
TX_CMD_FLG_FW_DROP = BIT(26),
TX_CMD_FLG_EXEC_PAPD = BIT(27),
TX_CMD_FLG_PAPD_TYPE = BIT(28),
TX_CMD_FLG_HCCA_CHUNK = BIT(31)
}; /* TX_FLAGS_BITS_API_S_VER_1 */
/*
* TX command security control
*/
#define TX_CMD_SEC_WEP 0x01
#define TX_CMD_SEC_CCM 0x02
#define TX_CMD_SEC_TKIP 0x03
#define TX_CMD_SEC_WEP_KEY_IDX_POS 6
#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0
#define TX_CMD_SEC_KEY128 0x08
/* TODO: how does these values are OK with only 16 bit variable??? */
/*
* TX command next frame info
*
* bits 0:2 - security control (TX_CMD_SEC_*)
* bit 3 - immediate ACK required
* bit 4 - rate is taken from STA table
* bit 5 - frame belongs to BA stream
* bit 6 - immediate BA response expected
* bit 7 - unused
* bits 8:15 - Station ID
* bits 16:31 - rate
*/
#define TX_CMD_NEXT_FRAME_ACK_MSK (0x8)
#define TX_CMD_NEXT_FRAME_STA_RATE_MSK (0x10)
#define TX_CMD_NEXT_FRAME_BA_MSK (0x20)
#define TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK (0x40)
#define TX_CMD_NEXT_FRAME_FLAGS_MSK (0xf8)
#define TX_CMD_NEXT_FRAME_STA_ID_MSK (0xff00)
#define TX_CMD_NEXT_FRAME_STA_ID_POS (8)
#define TX_CMD_NEXT_FRAME_RATE_MSK (0xffff0000)
#define TX_CMD_NEXT_FRAME_RATE_POS (16)
/*
* TX command Frame life time in us - to be written in pm_frame_timeout
*/
#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF
#define TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/
#define TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */
#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0
/*
* TID for non QoS frames - to be written in tid_tspec
*/
#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT
/*
* Limits on the retransmissions - to be written in {data,rts}_retry_limit
*/
#define IWL_DEFAULT_TX_RETRY 15
#define IWL_MGMT_DFAULT_RETRY_LIMIT 3
#define IWL_RTS_DFAULT_RETRY_LIMIT 60
#define IWL_BAR_DFAULT_RETRY_LIMIT 60
#define IWL_LOW_RETRY_LIMIT 7
/* TODO: complete documentation for try_cnt and btkill_cnt */
/**
* struct iwl_tx_cmd - TX command struct to FW
* ( TX_CMD = 0x1c )
* @len: in bytes of the payload, see below for details
* @next_frame_len: same as len, but for next frame (0 if not applicable)
* Used for fragmentation and bursting, but not in 11n aggregation.
* @tx_flags: combination of TX_CMD_FLG_*
* @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
* cleared. Combination of RATE_MCS_*
* @sta_id: index of destination station in FW station table
* @sec_ctl: security control, TX_CMD_SEC_*
* @initial_rate_index: index into the the rate table for initial TX attempt.
* Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames.
* @key: security key
* @next_frame_flags: TX_CMD_SEC_* and TX_CMD_NEXT_FRAME_*
* @life_time: frame life time (usecs??)
* @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt +
* btkill_cnd + reserved), first 32 bits. "0" disables usage.
* @dram_msb_ptr: upper bits of the scratch physical address
* @rts_retry_limit: max attempts for RTS
* @data_retry_limit: max attempts to send the data packet
* @tid_spec: TID/tspec
* @pm_frame_timeout: PM TX frame timeout
* @driver_txop: duration od EDCA TXOP, in 32-usec units. Set this if not
* specified by HCCA protocol
*
* The byte count (both len and next_frame_len) includes MAC header
* (24/26/30/32 bytes)
* + 2 bytes pad if 26/30 header size
* + 8 byte IV for CCM or TKIP (not used for WEP)
* + Data payload
* + 8-byte MIC (not used for CCM/WEP)
* It does not include post-MAC padding, i.e.,
* MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.
* Range of len: 14-2342 bytes.
*
* After the struct fields the MAC header is placed, plus any padding,
* and then the actial payload.
*/
struct iwl_tx_cmd {
__le16 len;
__le16 next_frame_len;
__le32 tx_flags;
/* DRAM_SCRATCH_API_U_VER_1 */
u8 try_cnt;
u8 btkill_cnt;
__le16 reserved;
__le32 rate_n_flags;
u8 sta_id;
u8 sec_ctl;
u8 initial_rate_index;
u8 reserved2;
u8 key[16];
__le16 next_frame_flags;
__le16 reserved3;
__le32 life_time;
__le32 dram_lsb_ptr;
u8 dram_msb_ptr;
u8 rts_retry_limit;
u8 data_retry_limit;
u8 tid_tspec;
__le16 pm_frame_timeout;
__le16 driver_txop;
u8 payload[0];
struct ieee80211_hdr hdr[0];
} __packed; /* TX_CMD_API_S_VER_3 */
/*
* TX response related data
*/
/*
* enum iwl_tx_status - status that is returned by the fw after attempts to Tx
* @TX_STATUS_SUCCESS:
* @TX_STATUS_DIRECT_DONE:
* @TX_STATUS_POSTPONE_DELAY:
* @TX_STATUS_POSTPONE_FEW_BYTES:
* @TX_STATUS_POSTPONE_BT_PRIO:
* @TX_STATUS_POSTPONE_QUIET_PERIOD:
* @TX_STATUS_POSTPONE_CALC_TTAK:
* @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
* @TX_STATUS_FAIL_SHORT_LIMIT:
* @TX_STATUS_FAIL_LONG_LIMIT:
* @TX_STATUS_FAIL_UNDERRUN:
* @TX_STATUS_FAIL_DRAIN_FLOW:
* @TX_STATUS_FAIL_RFKILL_FLUSH:
* @TX_STATUS_FAIL_LIFE_EXPIRE:
* @TX_STATUS_FAIL_DEST_PS:
* @TX_STATUS_FAIL_HOST_ABORTED:
* @TX_STATUS_FAIL_BT_RETRY:
* @TX_STATUS_FAIL_STA_INVALID:
* @TX_TATUS_FAIL_FRAG_DROPPED:
* @TX_STATUS_FAIL_TID_DISABLE:
* @TX_STATUS_FAIL_FIFO_FLUSHED:
* @TX_STATUS_FAIL_SMALL_CF_POLL:
* @TX_STATUS_FAIL_FW_DROP:
* @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and
* STA table
* @TX_FRAME_STATUS_INTERNAL_ABORT:
* @TX_MODE_MSK:
* @TX_MODE_NO_BURST:
* @TX_MODE_IN_BURST_SEQ:
* @TX_MODE_FIRST_IN_BURST:
* @TX_QUEUE_NUM_MSK:
*
* Valid only if frame_count =1
* TODO: complete documentation
*/
enum iwl_tx_status {
TX_STATUS_MSK = 0x000000ff,
TX_STATUS_SUCCESS = 0x01,
TX_STATUS_DIRECT_DONE = 0x02,
/* postpone TX */
TX_STATUS_POSTPONE_DELAY = 0x40,
TX_STATUS_POSTPONE_FEW_BYTES = 0x41,
TX_STATUS_POSTPONE_BT_PRIO = 0x42,
TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43,
TX_STATUS_POSTPONE_CALC_TTAK = 0x44,
/* abort TX */
TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81,
TX_STATUS_FAIL_SHORT_LIMIT = 0x82,
TX_STATUS_FAIL_LONG_LIMIT = 0x83,
TX_STATUS_FAIL_UNDERRUN = 0x84,
TX_STATUS_FAIL_DRAIN_FLOW = 0x85,
TX_STATUS_FAIL_RFKILL_FLUSH = 0x86,
TX_STATUS_FAIL_LIFE_EXPIRE = 0x87,
TX_STATUS_FAIL_DEST_PS = 0x88,
TX_STATUS_FAIL_HOST_ABORTED = 0x89,
TX_STATUS_FAIL_BT_RETRY = 0x8a,
TX_STATUS_FAIL_STA_INVALID = 0x8b,
TX_STATUS_FAIL_FRAG_DROPPED = 0x8c,
TX_STATUS_FAIL_TID_DISABLE = 0x8d,
TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f,
TX_STATUS_FAIL_FW_DROP = 0x90,
TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91,
TX_STATUS_INTERNAL_ABORT = 0x92,
TX_MODE_MSK = 0x00000f00,
TX_MODE_NO_BURST = 0x00000000,
TX_MODE_IN_BURST_SEQ = 0x00000100,
TX_MODE_FIRST_IN_BURST = 0x00000200,
TX_QUEUE_NUM_MSK = 0x0001f000,
TX_NARROW_BW_MSK = 0x00060000,
TX_NARROW_BW_1DIV2 = 0x00020000,
TX_NARROW_BW_1DIV4 = 0x00040000,
TX_NARROW_BW_1DIV8 = 0x00060000,
};
/*
* enum iwl_tx_agg_status - TX aggregation status
* @AGG_TX_STATE_STATUS_MSK:
* @AGG_TX_STATE_TRANSMITTED:
* @AGG_TX_STATE_UNDERRUN:
* @AGG_TX_STATE_BT_PRIO:
* @AGG_TX_STATE_FEW_BYTES:
* @AGG_TX_STATE_ABORT:
* @AGG_TX_STATE_LAST_SENT_TTL:
* @AGG_TX_STATE_LAST_SENT_TRY_CNT:
* @AGG_TX_STATE_LAST_SENT_BT_KILL:
* @AGG_TX_STATE_SCD_QUERY:
* @AGG_TX_STATE_TEST_BAD_CRC32:
* @AGG_TX_STATE_RESPONSE:
* @AGG_TX_STATE_DUMP_TX:
* @AGG_TX_STATE_DELAY_TX:
* @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries
* occur if tx failed for this frame when it was a member of a previous
* aggregation block). If rate scaling is used, retry count indicates the
* rate table entry used for all frames in the new agg.
*@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for
* this frame
*
* TODO: complete documentation
*/
enum iwl_tx_agg_status {
AGG_TX_STATE_STATUS_MSK = 0x00fff,
AGG_TX_STATE_TRANSMITTED = 0x000,
AGG_TX_STATE_UNDERRUN = 0x001,
AGG_TX_STATE_BT_PRIO = 0x002,
AGG_TX_STATE_FEW_BYTES = 0x004,
AGG_TX_STATE_ABORT = 0x008,
AGG_TX_STATE_LAST_SENT_TTL = 0x010,
AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020,
AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040,
AGG_TX_STATE_SCD_QUERY = 0x080,
AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100,
AGG_TX_STATE_RESPONSE = 0x1ff,
AGG_TX_STATE_DUMP_TX = 0x200,
AGG_TX_STATE_DELAY_TX = 0x400,
AGG_TX_STATE_TRY_CNT_POS = 12,
AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS,
};
#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \
AGG_TX_STATE_LAST_SENT_TRY_CNT| \
AGG_TX_STATE_LAST_SENT_BT_KILL)
/*
* The mask below describes a status where we are absolutely sure that the MPDU
* wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've
* written the bytes to the TXE, but we know nothing about what the DSP did.
*/
#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \
AGG_TX_STATE_ABORT | \
AGG_TX_STATE_SCD_QUERY)
/*
* REPLY_TX = 0x1c (response)
*
* This response may be in one of two slightly different formats, indicated
* by the frame_count field:
*
* 1) No aggregation (frame_count == 1). This reports Tx results for a single
* frame. Multiple attempts, at various bit rates, may have been made for
* this frame.
*
* 2) Aggregation (frame_count > 1). This reports Tx results for two or more
* frames that used block-acknowledge. All frames were transmitted at
* same rate. Rate scaling may have been used if first frame in this new
* agg block failed in previous agg block(s).
*
* Note that, for aggregation, ACK (block-ack) status is not delivered
* here; block-ack has not been received by the time the device records
* this status.
* This status relates to reasons the tx might have been blocked or aborted
* within the device, rather than whether it was received successfully by
* the destination station.
*/
/**
* struct agg_tx_status - per packet TX aggregation status
* @status: enum iwl_tx_agg_status
* @sequence: Sequence # for this frame's Tx cmd (not SSN!)
*/
struct agg_tx_status {
__le16 status;
__le16 sequence;
} __packed;
/*
* definitions for initial rate index field
* bits [3:0] initial rate index
* bits [6:4] rate table color, used for the initial rate
* bit-7 invalid rate indication
*/
#define TX_RES_INIT_RATE_INDEX_MSK 0x0f
#define TX_RES_RATE_TABLE_COLOR_MSK 0x70
#define TX_RES_INV_RATE_INDEX_MSK 0x80
#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f)
#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4)
/**
* struct iwl_mvm_tx_resp - notifies that fw is TXing a packet
* ( REPLY_TX = 0x1c )
* @frame_count: 1 no aggregation, >1 aggregation
* @bt_kill_count: num of times blocked by bluetooth (unused for agg)
* @failure_rts: num of failures due to unsuccessful RTS
* @failure_frame: num failures due to no ACK (unused for agg)
* @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the
* Tx of all the batch. RATE_MCS_*
* @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK.
* for agg: RTS + CTS + aggregation tx time + block-ack time.
* in usec.
* @pa_status: tx power info
* @pa_integ_res_a: tx power info
* @pa_integ_res_b: tx power info
* @pa_integ_res_c: tx power info
* @measurement_req_id: tx power info
* @tfd_info: TFD information set by the FH
* @seq_ctl: sequence control from the Tx cmd
* @byte_cnt: byte count from the Tx cmd
* @tlc_info: TLC rate info
* @ra_tid: bits [3:0] = ra, bits [7:4] = tid
* @frame_ctrl: frame control
* @status: for non-agg: frame status TX_STATUS_*
* for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields
* follow this one, up to frame_count.
*
* After the array of statuses comes the SSN of the SCD. Look at
* %iwl_mvm_get_scd_ssn for more details.
*/
struct iwl_mvm_tx_resp {
u8 frame_count;
u8 bt_kill_count;
u8 failure_rts;
u8 failure_frame;
__le32 initial_rate;
__le16 wireless_media_time;
u8 pa_status;
u8 pa_integ_res_a[3];
u8 pa_integ_res_b[3];
u8 pa_integ_res_c[3];
__le16 measurement_req_id;
__le16 reserved;
__le32 tfd_info;
__le16 seq_ctl;
__le16 byte_cnt;
u8 tlc_info;
u8 ra_tid;
__le16 frame_ctrl;
struct agg_tx_status status;
} __packed; /* TX_RSP_API_S_VER_3 */
/**
* struct iwl_mvm_ba_notif - notifies about reception of BA
* ( BA_NOTIF = 0xc5 )
* @sta_addr_lo32: lower 32 bits of the MAC address
* @sta_addr_hi16: upper 16 bits of the MAC address
* @sta_id: Index of recipient (BA-sending) station in fw's station table
* @tid: tid of the session
* @seq_ctl:
* @bitmap: the bitmap of the BA notification as seen in the air
* @scd_flow: the tx queue this BA relates to
* @scd_ssn: the index of the last contiguously sent packet
* @txed: number of Txed frames in this batch
* @txed_2_done: number of Acked frames in this batch
*/
struct iwl_mvm_ba_notif {
__le32 sta_addr_lo32;
__le16 sta_addr_hi16;
__le16 reserved;
u8 sta_id;
u8 tid;
__le16 seq_ctl;
__le64 bitmap;
__le16 scd_flow;
__le16 scd_ssn;
u8 txed;
u8 txed_2_done;
__le16 reserved1;
} __packed;
/*
* struct iwl_mac_beacon_cmd - beacon template command
* @tx: the tx commands associated with the beacon frame
* @template_id: currently equal to the mac context id of the coresponding
* mac.
* @tim_idx: the offset of the tim IE in the beacon
* @tim_size: the length of the tim IE
* @frame: the template of the beacon frame
*/
struct iwl_mac_beacon_cmd {
struct iwl_tx_cmd tx;
__le32 template_id;
__le32 tim_idx;
__le32 tim_size;
struct ieee80211_hdr frame[0];
} __packed;
struct iwl_beacon_notif {
struct iwl_mvm_tx_resp beacon_notify_hdr;
__le64 tsf;
__le32 ibss_mgr_status;
} __packed;
/**
* enum iwl_dump_control - dump (flush) control flags
* @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
* and the TFD queues are empty.
*/
enum iwl_dump_control {
DUMP_TX_FIFO_FLUSH = BIT(1),
};
/**
* struct iwl_tx_path_flush_cmd -- queue/FIFO flush command
* @queues_ctl: bitmap of queues to flush
* @flush_ctl: control flags
* @reserved: reserved
*/
struct iwl_tx_path_flush_cmd {
__le32 queues_ctl;
__le16 flush_ctl;
__le16 reserved;
} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */
/**
* iwl_mvm_get_scd_ssn - returns the SSN of the SCD
* @tx_resp: the Tx response from the fw (agg or non-agg)
*
* When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since
* it can't know that everything will go well until the end of the AMPDU, it
* can't know in advance the number of MPDUs that will be sent in the current
* batch. This is why it writes the agg Tx response while it fetches the MPDUs.
* Hence, it can't know in advance what the SSN of the SCD will be at the end
* of the batch. This is why the SSN of the SCD is written at the end of the
* whole struct at a variable offset. This function knows how to cope with the
* variable offset and returns the SSN of the SCD.
*/
static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp)
{
return le32_to_cpup((__le32 *)&tx_resp->status +
tx_resp->frame_count) & 0xfff;
}
#endif /* __fw_api_tx_h__ */

View File

@@ -0,0 +1,978 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __fw_api_h__
#define __fw_api_h__
#include "fw-api-rs.h"
#include "fw-api-tx.h"
#include "fw-api-sta.h"
#include "fw-api-mac.h"
#include "fw-api-power.h"
#include "fw-api-d3.h"
#include "fw-api-bt-coex.h"
/* queue and FIFO numbers by usage */
enum {
IWL_MVM_OFFCHANNEL_QUEUE = 8,
IWL_MVM_CMD_QUEUE = 9,
IWL_MVM_AUX_QUEUE = 15,
IWL_MVM_FIRST_AGG_QUEUE = 16,
IWL_MVM_NUM_QUEUES = 20,
IWL_MVM_LAST_AGG_QUEUE = IWL_MVM_NUM_QUEUES - 1,
IWL_MVM_CMD_FIFO = 7
};
#define IWL_MVM_STATION_COUNT 16
/* commands */
enum {
MVM_ALIVE = 0x1,
REPLY_ERROR = 0x2,
INIT_COMPLETE_NOTIF = 0x4,
/* PHY context commands */
PHY_CONTEXT_CMD = 0x8,
DBG_CFG = 0x9,
/* station table */
ADD_STA = 0x18,
REMOVE_STA = 0x19,
/* TX */
TX_CMD = 0x1c,
TXPATH_FLUSH = 0x1e,
MGMT_MCAST_KEY = 0x1f,
/* global key */
WEP_KEY = 0x20,
/* MAC and Binding commands */
MAC_CONTEXT_CMD = 0x28,
TIME_EVENT_CMD = 0x29, /* both CMD and response */
TIME_EVENT_NOTIFICATION = 0x2a,
BINDING_CONTEXT_CMD = 0x2b,
TIME_QUOTA_CMD = 0x2c,
LQ_CMD = 0x4e,
/* Calibration */
TEMPERATURE_NOTIFICATION = 0x62,
CALIBRATION_CFG_CMD = 0x65,
CALIBRATION_RES_NOTIFICATION = 0x66,
CALIBRATION_COMPLETE_NOTIFICATION = 0x67,
RADIO_VERSION_NOTIFICATION = 0x68,
/* Scan offload */
SCAN_OFFLOAD_REQUEST_CMD = 0x51,
SCAN_OFFLOAD_ABORT_CMD = 0x52,
SCAN_OFFLOAD_COMPLETE = 0x6D,
SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
/* Phy */
PHY_CONFIGURATION_CMD = 0x6a,
CALIB_RES_NOTIF_PHY_DB = 0x6b,
/* PHY_DB_CMD = 0x6c, */
/* Power */
POWER_TABLE_CMD = 0x77,
/* Scanning */
SCAN_REQUEST_CMD = 0x80,
SCAN_ABORT_CMD = 0x81,
SCAN_START_NOTIFICATION = 0x82,
SCAN_RESULTS_NOTIFICATION = 0x83,
SCAN_COMPLETE_NOTIFICATION = 0x84,
/* NVM */
NVM_ACCESS_CMD = 0x88,
SET_CALIB_DEFAULT_CMD = 0x8e,
BEACON_NOTIFICATION = 0x90,
BEACON_TEMPLATE_CMD = 0x91,
TX_ANT_CONFIGURATION_CMD = 0x98,
BT_CONFIG = 0x9b,
STATISTICS_NOTIFICATION = 0x9d,
/* RF-KILL commands and notifications */
CARD_STATE_CMD = 0xa0,
CARD_STATE_NOTIFICATION = 0xa1,
REPLY_RX_PHY_CMD = 0xc0,
REPLY_RX_MPDU_CMD = 0xc1,
BA_NOTIF = 0xc5,
/* BT Coex */
BT_COEX_PRIO_TABLE = 0xcc,
BT_COEX_PROT_ENV = 0xcd,
BT_PROFILE_NOTIFICATION = 0xce,
REPLY_DEBUG_CMD = 0xf0,
DEBUG_LOG_MSG = 0xf7,
MCAST_FILTER_CMD = 0xd0,
/* D3 commands/notifications */
D3_CONFIG_CMD = 0xd3,
PROT_OFFLOAD_CONFIG_CMD = 0xd4,
OFFLOADS_QUERY_CMD = 0xd5,
REMOTE_WAKE_CONFIG_CMD = 0xd6,
/* for WoWLAN in particular */
WOWLAN_PATTERNS = 0xe0,
WOWLAN_CONFIGURATION = 0xe1,
WOWLAN_TSC_RSC_PARAM = 0xe2,
WOWLAN_TKIP_PARAM = 0xe3,
WOWLAN_KEK_KCK_MATERIAL = 0xe4,
WOWLAN_GET_STATUSES = 0xe5,
WOWLAN_TX_POWER_PER_DB = 0xe6,
/* and for NetDetect */
NET_DETECT_CONFIG_CMD = 0x54,
NET_DETECT_PROFILES_QUERY_CMD = 0x56,
NET_DETECT_PROFILES_CMD = 0x57,
NET_DETECT_HOTSPOTS_CMD = 0x58,
NET_DETECT_HOTSPOTS_QUERY_CMD = 0x59,
REPLY_MAX = 0xff,
};
/**
* struct iwl_cmd_response - generic response struct for most commands
* @status: status of the command asked, changes for each one
*/
struct iwl_cmd_response {
__le32 status;
};
/*
* struct iwl_tx_ant_cfg_cmd
* @valid: valid antenna configuration
*/
struct iwl_tx_ant_cfg_cmd {
__le32 valid;
} __packed;
/*
* Calibration control struct.
* Sent as part of the phy configuration command.
* @flow_trigger: bitmap for which calibrations to perform according to
* flow triggers.
* @event_trigger: bitmap for which calibrations to perform according to
* event triggers.
*/
struct iwl_calib_ctrl {
__le32 flow_trigger;
__le32 event_trigger;
} __packed;
/* This enum defines the bitmap of various calibrations to enable in both
* init ucode and runtime ucode through CALIBRATION_CFG_CMD.
*/
enum iwl_calib_cfg {
IWL_CALIB_CFG_XTAL_IDX = BIT(0),
IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(1),
IWL_CALIB_CFG_VOLTAGE_READ_IDX = BIT(2),
IWL_CALIB_CFG_PAPD_IDX = BIT(3),
IWL_CALIB_CFG_TX_PWR_IDX = BIT(4),
IWL_CALIB_CFG_DC_IDX = BIT(5),
IWL_CALIB_CFG_BB_FILTER_IDX = BIT(6),
IWL_CALIB_CFG_LO_LEAKAGE_IDX = BIT(7),
IWL_CALIB_CFG_TX_IQ_IDX = BIT(8),
IWL_CALIB_CFG_TX_IQ_SKEW_IDX = BIT(9),
IWL_CALIB_CFG_RX_IQ_IDX = BIT(10),
IWL_CALIB_CFG_RX_IQ_SKEW_IDX = BIT(11),
IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(12),
IWL_CALIB_CFG_CHAIN_NOISE_IDX = BIT(13),
IWL_CALIB_CFG_DISCONNECTED_ANT_IDX = BIT(14),
IWL_CALIB_CFG_ANT_COUPLING_IDX = BIT(15),
IWL_CALIB_CFG_DAC_IDX = BIT(16),
IWL_CALIB_CFG_ABS_IDX = BIT(17),
IWL_CALIB_CFG_AGC_IDX = BIT(18),
};
/*
* Phy configuration command.
*/
struct iwl_phy_cfg_cmd {
__le32 phy_cfg;
struct iwl_calib_ctrl calib_control;
} __packed;
#define PHY_CFG_RADIO_TYPE (BIT(0) | BIT(1))
#define PHY_CFG_RADIO_STEP (BIT(2) | BIT(3))
#define PHY_CFG_RADIO_DASH (BIT(4) | BIT(5))
#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7))
#define PHY_CFG_TX_CHAIN_A BIT(8)
#define PHY_CFG_TX_CHAIN_B BIT(9)
#define PHY_CFG_TX_CHAIN_C BIT(10)
#define PHY_CFG_RX_CHAIN_A BIT(12)
#define PHY_CFG_RX_CHAIN_B BIT(13)
#define PHY_CFG_RX_CHAIN_C BIT(14)
/* Target of the NVM_ACCESS_CMD */
enum {
NVM_ACCESS_TARGET_CACHE = 0,
NVM_ACCESS_TARGET_OTP = 1,
NVM_ACCESS_TARGET_EEPROM = 2,
};
/* Section types for NVM_ACCESS_CMD */
enum {
NVM_SECTION_TYPE_HW = 0,
NVM_SECTION_TYPE_SW,
NVM_SECTION_TYPE_PAPD,
NVM_SECTION_TYPE_BT,
NVM_SECTION_TYPE_CALIBRATION,
NVM_SECTION_TYPE_PRODUCTION,
NVM_SECTION_TYPE_POST_FCS_CALIB,
NVM_NUM_OF_SECTIONS,
};
/**
* struct iwl_nvm_access_cmd_ver2 - Request the device to send an NVM section
* @op_code: 0 - read, 1 - write
* @target: NVM_ACCESS_TARGET_*
* @type: NVM_SECTION_TYPE_*
* @offset: offset in bytes into the section
* @length: in bytes, to read/write
* @data: if write operation, the data to write. On read its empty
*/
struct iwl_nvm_access_cmd {
u8 op_code;
u8 target;
__le16 type;
__le16 offset;
__le16 length;
u8 data[];
} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */
/**
* struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD
* @offset: offset in bytes into the section
* @length: in bytes, either how much was written or read
* @type: NVM_SECTION_TYPE_*
* @status: 0 for success, fail otherwise
* @data: if read operation, the data returned. Empty on write.
*/
struct iwl_nvm_access_resp {
__le16 offset;
__le16 length;
__le16 type;
__le16 status;
u8 data[];
} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */
/* MVM_ALIVE 0x1 */
/* alive response is_valid values */
#define ALIVE_RESP_UCODE_OK BIT(0)
#define ALIVE_RESP_RFKILL BIT(1)
/* alive response ver_type values */
enum {
FW_TYPE_HW = 0,
FW_TYPE_PROT = 1,
FW_TYPE_AP = 2,
FW_TYPE_WOWLAN = 3,
FW_TYPE_TIMING = 4,
FW_TYPE_WIPAN = 5
};
/* alive response ver_subtype values */
enum {
FW_SUBTYPE_FULL_FEATURE = 0,
FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */
FW_SUBTYPE_REDUCED = 2,
FW_SUBTYPE_ALIVE_ONLY = 3,
FW_SUBTYPE_WOWLAN = 4,
FW_SUBTYPE_AP_SUBTYPE = 5,
FW_SUBTYPE_WIPAN = 6,
FW_SUBTYPE_INITIALIZE = 9
};
#define IWL_ALIVE_STATUS_ERR 0xDEAD
#define IWL_ALIVE_STATUS_OK 0xCAFE
#define IWL_ALIVE_FLG_RFKILL BIT(0)
struct mvm_alive_resp {
__le16 status;
__le16 flags;
u8 ucode_minor;
u8 ucode_major;
__le16 id;
u8 api_minor;
u8 api_major;
u8 ver_subtype;
u8 ver_type;
u8 mac;
u8 opt;
__le16 reserved2;
__le32 timestamp;
__le32 error_event_table_ptr; /* SRAM address for error log */
__le32 log_event_table_ptr; /* SRAM address for event log */
__le32 cpu_register_ptr;
__le32 dbgm_config_ptr;
__le32 alive_counter_ptr;
__le32 scd_base_ptr; /* SRAM address for SCD */
} __packed; /* ALIVE_RES_API_S_VER_1 */
/* Error response/notification */
enum {
FW_ERR_UNKNOWN_CMD = 0x0,
FW_ERR_INVALID_CMD_PARAM = 0x1,
FW_ERR_SERVICE = 0x2,
FW_ERR_ARC_MEMORY = 0x3,
FW_ERR_ARC_CODE = 0x4,
FW_ERR_WATCH_DOG = 0x5,
FW_ERR_WEP_GRP_KEY_INDX = 0x10,
FW_ERR_WEP_KEY_SIZE = 0x11,
FW_ERR_OBSOLETE_FUNC = 0x12,
FW_ERR_UNEXPECTED = 0xFE,
FW_ERR_FATAL = 0xFF
};
/**
* struct iwl_error_resp - FW error indication
* ( REPLY_ERROR = 0x2 )
* @error_type: one of FW_ERR_*
* @cmd_id: the command ID for which the error occured
* @bad_cmd_seq_num: sequence number of the erroneous command
* @error_service: which service created the error, applicable only if
* error_type = 2, otherwise 0
* @timestamp: TSF in usecs.
*/
struct iwl_error_resp {
__le32 error_type;
u8 cmd_id;
u8 reserved1;
__le16 bad_cmd_seq_num;
__le32 error_service;
__le64 timestamp;
} __packed;
/* Common PHY, MAC and Bindings definitions */
#define MAX_MACS_IN_BINDING (3)
#define MAX_BINDINGS (4)
#define AUX_BINDING_INDEX (3)
#define MAX_PHYS (4)
/* Used to extract ID and color from the context dword */
#define FW_CTXT_ID_POS (0)
#define FW_CTXT_ID_MSK (0xff << FW_CTXT_ID_POS)
#define FW_CTXT_COLOR_POS (8)
#define FW_CTXT_COLOR_MSK (0xff << FW_CTXT_COLOR_POS)
#define FW_CTXT_INVALID (0xffffffff)
#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\
(_color << FW_CTXT_COLOR_POS))
/* Possible actions on PHYs, MACs and Bindings */
enum {
FW_CTXT_ACTION_STUB = 0,
FW_CTXT_ACTION_ADD,
FW_CTXT_ACTION_MODIFY,
FW_CTXT_ACTION_REMOVE,
FW_CTXT_ACTION_NUM
}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */
/* Time Events */
/* Time Event types, according to MAC type */
enum iwl_time_event_type {
/* BSS Station Events */
TE_BSS_STA_AGGRESSIVE_ASSOC,
TE_BSS_STA_ASSOC,
TE_BSS_EAP_DHCP_PROT,
TE_BSS_QUIET_PERIOD,
/* P2P Device Events */
TE_P2P_DEVICE_DISCOVERABLE,
TE_P2P_DEVICE_LISTEN,
TE_P2P_DEVICE_ACTION_SCAN,
TE_P2P_DEVICE_FULL_SCAN,
/* P2P Client Events */
TE_P2P_CLIENT_AGGRESSIVE_ASSOC,
TE_P2P_CLIENT_ASSOC,
TE_P2P_CLIENT_QUIET_PERIOD,
/* P2P GO Events */
TE_P2P_GO_ASSOC_PROT,
TE_P2P_GO_REPETITIVE_NOA,
TE_P2P_GO_CT_WINDOW,
/* WiDi Sync Events */
TE_WIDI_TX_SYNC,
TE_MAX
}; /* MAC_EVENT_TYPE_API_E_VER_1 */
/* Time Event dependencies: none, on another TE, or in a specific time */
enum {
TE_INDEPENDENT = 0,
TE_DEP_OTHER = 1,
TE_DEP_TSF = 2,
TE_EVENT_SOCIOPATHIC = 4,
}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */
/*
* Supported Time event notifications configuration.
* A notification (both event and fragment) includes a status indicating weather
* the FW was able to schedule the event or not. For fragment start/end
* notification the status is always success. There is no start/end fragment
* notification for monolithic events.
*
* @TE_NOTIF_NONE: no notifications
* @TE_NOTIF_HOST_EVENT_START: request/receive notification on event start
* @TE_NOTIF_HOST_EVENT_END:request/receive notification on event end
* @TE_NOTIF_INTERNAL_EVENT_START: internal FW use
* @TE_NOTIF_INTERNAL_EVENT_END: internal FW use.
* @TE_NOTIF_HOST_FRAG_START: request/receive notification on frag start
* @TE_NOTIF_HOST_FRAG_END:request/receive notification on frag end
* @TE_NOTIF_INTERNAL_FRAG_START: internal FW use.
* @TE_NOTIF_INTERNAL_FRAG_END: internal FW use.
*/
enum {
TE_NOTIF_NONE = 0,
TE_NOTIF_HOST_EVENT_START = 0x1,
TE_NOTIF_HOST_EVENT_END = 0x2,
TE_NOTIF_INTERNAL_EVENT_START = 0x4,
TE_NOTIF_INTERNAL_EVENT_END = 0x8,
TE_NOTIF_HOST_FRAG_START = 0x10,
TE_NOTIF_HOST_FRAG_END = 0x20,
TE_NOTIF_INTERNAL_FRAG_START = 0x40,
TE_NOTIF_INTERNAL_FRAG_END = 0x80
}; /* MAC_EVENT_ACTION_API_E_VER_2 */
/*
* @TE_FRAG_NONE: fragmentation of the time event is NOT allowed.
* @TE_FRAG_SINGLE: fragmentation of the time event is allowed, but only
* the first fragment is scheduled.
* @TE_FRAG_DUAL: fragmentation of the time event is allowed, but only
* the first 2 fragments are scheduled.
* @TE_FRAG_ENDLESS: fragmentation of the time event is allowed, and any number
* of fragments are valid.
*
* Other than the constant defined above, specifying a fragmentation value 'x'
* means that the event can be fragmented but only the first 'x' will be
* scheduled.
*/
enum {
TE_FRAG_NONE = 0,
TE_FRAG_SINGLE = 1,
TE_FRAG_DUAL = 2,
TE_FRAG_ENDLESS = 0xffffffff
};
/* Repeat the time event endlessly (until removed) */
#define TE_REPEAT_ENDLESS (0xffffffff)
/* If a Time Event has bounded repetitions, this is the maximal value */
#define TE_REPEAT_MAX_MSK (0x0fffffff)
/* If a Time Event can be fragmented, this is the max number of fragments */
#define TE_FRAG_MAX_MSK (0x0fffffff)
/**
* struct iwl_time_event_cmd - configuring Time Events
* ( TIME_EVENT_CMD = 0x29 )
* @id_and_color: ID and color of the relevant MAC
* @action: action to perform, one of FW_CTXT_ACTION_*
* @id: this field has two meanings, depending on the action:
* If the action is ADD, then it means the type of event to add.
* For all other actions it is the unique event ID assigned when the
* event was added by the FW.
* @apply_time: When to start the Time Event (in GP2)
* @max_delay: maximum delay to event's start (apply time), in TU
* @depends_on: the unique ID of the event we depend on (if any)
* @interval: interval between repetitions, in TU
* @interval_reciprocal: 2^32 / interval
* @duration: duration of event in TU
* @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
* @dep_policy: one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
* @is_present: 0 or 1, are we present or absent during the Time Event
* @max_frags: maximal number of fragments the Time Event can be divided to
* @notify: notifications using TE_NOTIF_* (whom to notify when)
*/
struct iwl_time_event_cmd {
/* COMMON_INDEX_HDR_API_S_VER_1 */
__le32 id_and_color;
__le32 action;
__le32 id;
/* MAC_TIME_EVENT_DATA_API_S_VER_1 */
__le32 apply_time;
__le32 max_delay;
__le32 dep_policy;
__le32 depends_on;
__le32 is_present;
__le32 max_frags;
__le32 interval;
__le32 interval_reciprocal;
__le32 duration;
__le32 repeat;
__le32 notify;
} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */
/**
* struct iwl_time_event_resp - response structure to iwl_time_event_cmd
* @status: bit 0 indicates success, all others specify errors
* @id: the Time Event type
* @unique_id: the unique ID assigned (in ADD) or given (others) to the TE
* @id_and_color: ID and color of the relevant MAC
*/
struct iwl_time_event_resp {
__le32 status;
__le32 id;
__le32 unique_id;
__le32 id_and_color;
} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */
/**
* struct iwl_time_event_notif - notifications of time event start/stop
* ( TIME_EVENT_NOTIFICATION = 0x2a )
* @timestamp: action timestamp in GP2
* @session_id: session's unique id
* @unique_id: unique id of the Time Event itself
* @id_and_color: ID and color of the relevant MAC
* @action: one of TE_NOTIF_START or TE_NOTIF_END
* @status: true if scheduled, false otherwise (not executed)
*/
struct iwl_time_event_notif {
__le32 timestamp;
__le32 session_id;
__le32 unique_id;
__le32 id_and_color;
__le32 action;
__le32 status;
} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */
/* Bindings and Time Quota */
/**
* struct iwl_binding_cmd - configuring bindings
* ( BINDING_CONTEXT_CMD = 0x2b )
* @id_and_color: ID and color of the relevant Binding
* @action: action to perform, one of FW_CTXT_ACTION_*
* @macs: array of MAC id and colors which belong to the binding
* @phy: PHY id and color which belongs to the binding
*/
struct iwl_binding_cmd {
/* COMMON_INDEX_HDR_API_S_VER_1 */
__le32 id_and_color;
__le32 action;
/* BINDING_DATA_API_S_VER_1 */
__le32 macs[MAX_MACS_IN_BINDING];
__le32 phy;
} __packed; /* BINDING_CMD_API_S_VER_1 */
/* The maximal number of fragments in the FW's schedule session */
#define IWL_MVM_MAX_QUOTA 128
/**
* struct iwl_time_quota_data - configuration of time quota per binding
* @id_and_color: ID and color of the relevant Binding
* @quota: absolute time quota in TU. The scheduler will try to divide the
* remainig quota (after Time Events) according to this quota.
* @max_duration: max uninterrupted context duration in TU
*/
struct iwl_time_quota_data {
__le32 id_and_color;
__le32 quota;
__le32 max_duration;
} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */
/**
* struct iwl_time_quota_cmd - configuration of time quota between bindings
* ( TIME_QUOTA_CMD = 0x2c )
* @quotas: allocations per binding
*/
struct iwl_time_quota_cmd {
struct iwl_time_quota_data quotas[MAX_BINDINGS];
} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */
/* PHY context */
/* Supported bands */
#define PHY_BAND_5 (0)
#define PHY_BAND_24 (1)
/* Supported channel width, vary if there is VHT support */
#define PHY_VHT_CHANNEL_MODE20 (0x0)
#define PHY_VHT_CHANNEL_MODE40 (0x1)
#define PHY_VHT_CHANNEL_MODE80 (0x2)
#define PHY_VHT_CHANNEL_MODE160 (0x3)
/*
* Control channel position:
* For legacy set bit means upper channel, otherwise lower.
* For VHT - bit-2 marks if the control is lower/upper relative to center-freq
* bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0.
* center_freq
* |
* 40Mhz |_______|_______|
* 80Mhz |_______|_______|_______|_______|
* 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______|
* code 011 010 001 000 | 100 101 110 111
*/
#define PHY_VHT_CTRL_POS_1_BELOW (0x0)
#define PHY_VHT_CTRL_POS_2_BELOW (0x1)
#define PHY_VHT_CTRL_POS_3_BELOW (0x2)
#define PHY_VHT_CTRL_POS_4_BELOW (0x3)
#define PHY_VHT_CTRL_POS_1_ABOVE (0x4)
#define PHY_VHT_CTRL_POS_2_ABOVE (0x5)
#define PHY_VHT_CTRL_POS_3_ABOVE (0x6)
#define PHY_VHT_CTRL_POS_4_ABOVE (0x7)
/*
* @band: PHY_BAND_*
* @channel: channel number
* @width: PHY_[VHT|LEGACY]_CHANNEL_*
* @ctrl channel: PHY_[VHT|LEGACY]_CTRL_*
*/
struct iwl_fw_channel_info {
u8 band;
u8 channel;
u8 width;
u8 ctrl_pos;
} __packed;
#define PHY_RX_CHAIN_DRIVER_FORCE_POS (0)
#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \
(0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS)
#define PHY_RX_CHAIN_VALID_POS (1)
#define PHY_RX_CHAIN_VALID_MSK \
(0x7 << PHY_RX_CHAIN_VALID_POS)
#define PHY_RX_CHAIN_FORCE_SEL_POS (4)
#define PHY_RX_CHAIN_FORCE_SEL_MSK \
(0x7 << PHY_RX_CHAIN_FORCE_SEL_POS)
#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \
(0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS)
#define PHY_RX_CHAIN_CNT_POS (10)
#define PHY_RX_CHAIN_CNT_MSK \
(0x3 << PHY_RX_CHAIN_CNT_POS)
#define PHY_RX_CHAIN_MIMO_CNT_POS (12)
#define PHY_RX_CHAIN_MIMO_CNT_MSK \
(0x3 << PHY_RX_CHAIN_MIMO_CNT_POS)
#define PHY_RX_CHAIN_MIMO_FORCE_POS (14)
#define PHY_RX_CHAIN_MIMO_FORCE_MSK \
(0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS)
/* TODO: fix the value, make it depend on firmware at runtime? */
#define NUM_PHY_CTX 3
/* TODO: complete missing documentation */
/**
* struct iwl_phy_context_cmd - config of the PHY context
* ( PHY_CONTEXT_CMD = 0x8 )
* @id_and_color: ID and color of the relevant Binding
* @action: action to perform, one of FW_CTXT_ACTION_*
* @apply_time: 0 means immediate apply and context switch.
* other value means apply new params after X usecs
* @tx_param_color: ???
* @channel_info:
* @txchain_info: ???
* @rxchain_info: ???
* @acquisition_data: ???
* @dsp_cfg_flags: set to 0
*/
struct iwl_phy_context_cmd {
/* COMMON_INDEX_HDR_API_S_VER_1 */
__le32 id_and_color;
__le32 action;
/* PHY_CONTEXT_DATA_API_S_VER_1 */
__le32 apply_time;
__le32 tx_param_color;
struct iwl_fw_channel_info ci;
__le32 txchain_info;
__le32 rxchain_info;
__le32 acquisition_data;
__le32 dsp_cfg_flags;
} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
#define IWL_RX_INFO_PHY_CNT 8
#define IWL_RX_INFO_AGC_IDX 1
#define IWL_RX_INFO_RSSI_AB_IDX 2
#define IWL_OFDM_AGC_A_MSK 0x0000007f
#define IWL_OFDM_AGC_A_POS 0
#define IWL_OFDM_AGC_B_MSK 0x00003f80
#define IWL_OFDM_AGC_B_POS 7
#define IWL_OFDM_AGC_CODE_MSK 0x3fe00000
#define IWL_OFDM_AGC_CODE_POS 20
#define IWL_OFDM_RSSI_INBAND_A_MSK 0x00ff
#define IWL_OFDM_RSSI_A_POS 0
#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00
#define IWL_OFDM_RSSI_ALLBAND_A_POS 8
#define IWL_OFDM_RSSI_INBAND_B_MSK 0xff0000
#define IWL_OFDM_RSSI_B_POS 16
#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000
#define IWL_OFDM_RSSI_ALLBAND_B_POS 24
/**
* struct iwl_rx_phy_info - phy info
* (REPLY_RX_PHY_CMD = 0xc0)
* @non_cfg_phy_cnt: non configurable DSP phy data byte count
* @cfg_phy_cnt: configurable DSP phy data byte count
* @stat_id: configurable DSP phy data set ID
* @reserved1:
* @system_timestamp: GP2 at on air rise
* @timestamp: TSF at on air rise
* @beacon_time_stamp: beacon at on-air rise
* @phy_flags: general phy flags: band, modulation, ...
* @channel: channel number
* @non_cfg_phy_buf: for various implementations of non_cfg_phy
* @rate_n_flags: RATE_MCS_*
* @byte_count: frame's byte-count
* @frame_time: frame's time on the air, based on byte count and frame rate
* calculation
* @mac_active_msk: what MACs were active when the frame was received
*
* Before each Rx, the device sends this data. It contains PHY information
* about the reception of the packet.
*/
struct iwl_rx_phy_info {
u8 non_cfg_phy_cnt;
u8 cfg_phy_cnt;
u8 stat_id;
u8 reserved1;
__le32 system_timestamp;
__le64 timestamp;
__le32 beacon_time_stamp;
__le16 phy_flags;
__le16 channel;
__le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
__le32 rate_n_flags;
__le32 byte_count;
__le16 mac_active_msk;
__le16 frame_time;
} __packed;
struct iwl_rx_mpdu_res_start {
__le16 byte_count;
__le16 reserved;
} __packed;
/**
* enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags
* @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band
* @RX_RES_PHY_FLAGS_MOD_CCK:
* @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short
* @RX_RES_PHY_FLAGS_NARROW_BAND:
* @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received
* @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU
* @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame
* @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble
* @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame
*/
enum iwl_rx_phy_flags {
RX_RES_PHY_FLAGS_BAND_24 = BIT(0),
RX_RES_PHY_FLAGS_MOD_CCK = BIT(1),
RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2),
RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3),
RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4),
RX_RES_PHY_FLAGS_ANTENNA_POS = 4,
RX_RES_PHY_FLAGS_AGG = BIT(7),
RX_RES_PHY_FLAGS_OFDM_HT = BIT(8),
RX_RES_PHY_FLAGS_OFDM_GF = BIT(9),
RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10),
};
/**
* enum iwl_mvm_rx_status - written by fw for each Rx packet
* @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine
* @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow
* @RX_MPDU_RES_STATUS_SRC_STA_FOUND:
* @RX_MPDU_RES_STATUS_KEY_VALID:
* @RX_MPDU_RES_STATUS_KEY_PARAM_OK:
* @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed
* @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked
* in the driver.
* @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine
* @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or
* alg = CCM only. Checks replay attack for 11w frames. Relevant only if
* %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set.
* @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted
* @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP
* @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM
* @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP
* @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC
* @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted
* @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm
* @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted
* @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP:
* @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP:
* @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT:
* @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame
* @RX_MPDU_RES_STATUS_HASH_INDEX_MSK:
* @RX_MPDU_RES_STATUS_STA_ID_MSK:
* @RX_MPDU_RES_STATUS_RRF_KILL:
* @RX_MPDU_RES_STATUS_FILTERING_MSK:
* @RX_MPDU_RES_STATUS2_FILTERING_MSK:
*/
enum iwl_mvm_rx_status {
RX_MPDU_RES_STATUS_CRC_OK = BIT(0),
RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1),
RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2),
RX_MPDU_RES_STATUS_KEY_VALID = BIT(3),
RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4),
RX_MPDU_RES_STATUS_ICV_OK = BIT(5),
RX_MPDU_RES_STATUS_MIC_OK = BIT(6),
RX_MPDU_RES_STATUS_TTAK_OK = BIT(7),
RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7),
RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8),
RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8),
RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8),
RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8),
RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8),
RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8),
RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8),
RX_MPDU_RES_STATUS_DEC_DONE = BIT(11),
RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = BIT(12),
RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13),
RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14),
RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15),
RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000),
RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000),
RX_MPDU_RES_STATUS_RRF_KILL = BIT(29),
RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000),
RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000),
};
/**
* struct iwl_radio_version_notif - information on the radio version
* ( RADIO_VERSION_NOTIFICATION = 0x68 )
* @radio_flavor:
* @radio_step:
* @radio_dash:
*/
struct iwl_radio_version_notif {
__le32 radio_flavor;
__le32 radio_step;
__le32 radio_dash;
} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */
enum iwl_card_state_flags {
CARD_ENABLED = 0x00,
HW_CARD_DISABLED = 0x01,
SW_CARD_DISABLED = 0x02,
CT_KILL_CARD_DISABLED = 0x04,
HALT_CARD_DISABLED = 0x08,
CARD_DISABLED_MSK = 0x0f,
CARD_IS_RX_ON = 0x10,
};
/**
* struct iwl_radio_version_notif - information on the radio version
* ( CARD_STATE_NOTIFICATION = 0xa1 )
* @flags: %iwl_card_state_flags
*/
struct iwl_card_state_notif {
__le32 flags;
} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
/**
* struct iwl_set_calib_default_cmd - set default value for calibration.
* ( SET_CALIB_DEFAULT_CMD = 0x8e )
* @calib_index: the calibration to set value for
* @length: of data
* @data: the value to set for the calibration result
*/
struct iwl_set_calib_default_cmd {
__le16 calib_index;
__le16 length;
u8 data[0];
} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
#define MAX_PORT_ID_NUM 2
/**
* struct iwl_mcast_filter_cmd - configure multicast filter.
* @filter_own: Set 1 to filter out multicast packets sent by station itself
* @port_id: Multicast MAC addresses array specifier. This is a strange way
* to identify network interface adopted in host-device IF.
* It is used by FW as index in array of addresses. This array has
* MAX_PORT_ID_NUM members.
* @count: Number of MAC addresses in the array
* @pass_all: Set 1 to pass all multicast packets.
* @bssid: current association BSSID.
* @addr_list: Place holder for array of MAC addresses.
* IMPORTANT: add padding if necessary to ensure DWORD alignment.
*/
struct iwl_mcast_filter_cmd {
u8 filter_own;
u8 port_id;
u8 count;
u8 pass_all;
u8 bssid[6];
u8 reserved[2];
u8 addr_list[0];
} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
#endif /* __fw_api_h__ */

View File

@@ -0,0 +1,528 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <net/mac80211.h>
#include "iwl-trans.h"
#include "iwl-op-mode.h"
#include "iwl-fw.h"
#include "iwl-debug.h"
#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */
#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */
#include "iwl-eeprom-parse.h"
#include "mvm.h"
#include "iwl-phy-db.h"
#define MVM_UCODE_ALIVE_TIMEOUT HZ
#define MVM_UCODE_CALIB_TIMEOUT (2*HZ)
#define UCODE_VALID_OK cpu_to_le32(0x1)
/* Default calibration values for WkP - set to INIT image w/o running */
static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 };
static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 };
struct iwl_calib_default_data {
u16 size;
void *data;
};
#define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf}
static const struct iwl_calib_default_data wkp_calib_default_data[12] = {
[9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew),
[11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew),
};
struct iwl_mvm_alive_data {
bool valid;
u32 scd_base_addr;
};
static inline const struct fw_img *
iwl_get_ucode_image(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type)
{
if (ucode_type >= IWL_UCODE_TYPE_MAX)
return NULL;
return &mvm->fw->img[ucode_type];
}
static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
{
struct iwl_tx_ant_cfg_cmd tx_ant_cmd = {
.valid = cpu_to_le32(valid_tx_ant),
};
IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant);
return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC,
sizeof(tx_ant_cmd), &tx_ant_cmd);
}
static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
struct iwl_rx_packet *pkt, void *data)
{
struct iwl_mvm *mvm =
container_of(notif_wait, struct iwl_mvm, notif_wait);
struct iwl_mvm_alive_data *alive_data = data;
struct mvm_alive_resp *palive;
palive = (void *)pkt->data;
mvm->error_event_table = le32_to_cpu(palive->error_event_table_ptr);
mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK;
IWL_DEBUG_FW(mvm,
"Alive ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
le16_to_cpu(palive->status), palive->ver_type,
palive->ver_subtype, palive->flags);
return true;
}
static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait,
struct iwl_rx_packet *pkt, void *data)
{
struct iwl_phy_db *phy_db = data;
if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) {
WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF);
return true;
}
WARN_ON(iwl_phy_db_set_section(phy_db, pkt, GFP_ATOMIC));
return false;
}
static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
enum iwl_ucode_type ucode_type)
{
struct iwl_notification_wait alive_wait;
struct iwl_mvm_alive_data alive_data;
const struct fw_img *fw;
int ret, i;
enum iwl_ucode_type old_type = mvm->cur_ucode;
static const u8 alive_cmd[] = { MVM_ALIVE };
mvm->cur_ucode = ucode_type;
fw = iwl_get_ucode_image(mvm, ucode_type);
mvm->ucode_loaded = false;
if (!fw)
return -EINVAL;
iwl_init_notification_wait(&mvm->notif_wait, &alive_wait,
alive_cmd, ARRAY_SIZE(alive_cmd),
iwl_alive_fn, &alive_data);
ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT);
if (ret) {
mvm->cur_ucode = old_type;
iwl_remove_notification(&mvm->notif_wait, &alive_wait);
return ret;
}
/*
* Some things may run in the background now, but we
* just wait for the ALIVE notification here.
*/
ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait,
MVM_UCODE_ALIVE_TIMEOUT);
if (ret) {
mvm->cur_ucode = old_type;
return ret;
}
if (!alive_data.valid) {
IWL_ERR(mvm, "Loaded ucode is not valid!\n");
mvm->cur_ucode = old_type;
return -EIO;
}
iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
/*
* Note: all the queues are enabled as part of the interface
* initialization, but in firmware restart scenarios they
* could be stopped, so wake them up. In firmware restart,
* mac80211 will have the queues stopped as well until the
* reconfiguration completes. During normal startup, they
* will be empty.
*/
for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
if (i < IWL_MVM_FIRST_AGG_QUEUE && i != IWL_MVM_CMD_QUEUE)
mvm->queue_to_mac80211[i] = i;
else
mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
atomic_set(&mvm->queue_stop_count[i], 0);
}
mvm->transport_queue_stop = 0;
mvm->ucode_loaded = true;
return 0;
}
static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
{
struct iwl_phy_cfg_cmd phy_cfg_cmd;
enum iwl_ucode_type ucode_type = mvm->cur_ucode;
/* Set parameters */
phy_cfg_cmd.phy_cfg = cpu_to_le32(mvm->fw->phy_config);
phy_cfg_cmd.calib_control.event_trigger =
mvm->fw->default_calib[ucode_type].event_trigger;
phy_cfg_cmd.calib_control.flow_trigger =
mvm->fw->default_calib[ucode_type].flow_trigger;
IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n",
phy_cfg_cmd.phy_cfg);
return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, CMD_SYNC,
sizeof(phy_cfg_cmd), &phy_cfg_cmd);
}
static int iwl_set_default_calibrations(struct iwl_mvm *mvm)
{
u8 cmd_raw[16]; /* holds the variable size commands */
struct iwl_set_calib_default_cmd *cmd =
(struct iwl_set_calib_default_cmd *)cmd_raw;
int ret, i;
/* Setting default values for calibrations we don't run */
for (i = 0; i < ARRAY_SIZE(wkp_calib_default_data); i++) {
u16 cmd_len;
if (wkp_calib_default_data[i].size == 0)
continue;
memset(cmd_raw, 0, sizeof(cmd_raw));
cmd_len = wkp_calib_default_data[i].size + sizeof(cmd);
cmd->calib_index = cpu_to_le16(i);
cmd->length = cpu_to_le16(wkp_calib_default_data[i].size);
if (WARN_ONCE(cmd_len > sizeof(cmd_raw),
"Need to enlarge cmd_raw to %d\n", cmd_len))
break;
memcpy(cmd->data, wkp_calib_default_data[i].data,
wkp_calib_default_data[i].size);
ret = iwl_mvm_send_cmd_pdu(mvm, SET_CALIB_DEFAULT_CMD, 0,
sizeof(*cmd) +
wkp_calib_default_data[i].size,
cmd);
if (ret)
return ret;
}
return 0;
}
int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
{
struct iwl_notification_wait calib_wait;
static const u8 init_complete[] = {
INIT_COMPLETE_NOTIF,
CALIB_RES_NOTIF_PHY_DB
};
int ret;
lockdep_assert_held(&mvm->mutex);
if (mvm->init_ucode_run)
return 0;
iwl_init_notification_wait(&mvm->notif_wait,
&calib_wait,
init_complete,
ARRAY_SIZE(init_complete),
iwl_wait_phy_db_entry,
mvm->phy_db);
/* Will also start the device */
ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT);
if (ret) {
IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret);
goto error;
}
ret = iwl_send_bt_prio_tbl(mvm);
if (ret)
goto error;
if (read_nvm) {
/* Read nvm */
ret = iwl_nvm_init(mvm);
if (ret) {
IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
goto error;
}
}
ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
WARN_ON(ret);
/* Send TX valid antennas before triggering calibrations */
ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
if (ret)
goto error;
/* need to set default values */
ret = iwl_set_default_calibrations(mvm);
if (ret)
goto error;
/*
* Send phy configurations command to init uCode
* to start the 16.0 uCode init image internal calibrations.
*/
ret = iwl_send_phy_cfg_cmd(mvm);
if (ret) {
IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n",
ret);
goto error;
}
/*
* Some things may run in the background now, but we
* just wait for the calibration complete notification.
*/
ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
MVM_UCODE_CALIB_TIMEOUT);
if (!ret)
mvm->init_ucode_run = true;
goto out;
error:
iwl_remove_notification(&mvm->notif_wait, &calib_wait);
out:
if (!iwlmvm_mod_params.init_dbg) {
iwl_trans_stop_device(mvm->trans);
} else if (!mvm->nvm_data) {
/* we want to debug INIT and we have no NVM - fake */
mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
sizeof(struct ieee80211_channel) +
sizeof(struct ieee80211_rate),
GFP_KERNEL);
if (!mvm->nvm_data)
return -ENOMEM;
mvm->nvm_data->valid_rx_ant = 1;
mvm->nvm_data->valid_tx_ant = 1;
mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels;
mvm->nvm_data->bands[0].n_channels = 1;
mvm->nvm_data->bands[0].n_bitrates = 1;
mvm->nvm_data->bands[0].bitrates =
(void *)mvm->nvm_data->channels + 1;
mvm->nvm_data->bands[0].bitrates->hw_value = 10;
}
return ret;
}
#define UCODE_CALIB_TIMEOUT (2*HZ)
int iwl_mvm_up(struct iwl_mvm *mvm)
{
int ret, i;
lockdep_assert_held(&mvm->mutex);
ret = iwl_trans_start_hw(mvm->trans);
if (ret)
return ret;
/* If we were in RFKILL during module loading, load init ucode now */
if (!mvm->init_ucode_run) {
ret = iwl_run_init_mvm_ucode(mvm, false);
if (ret && !iwlmvm_mod_params.init_dbg) {
IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
goto error;
}
}
if (iwlmvm_mod_params.init_dbg)
return 0;
ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
if (ret) {
IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
goto error;
}
ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
if (ret)
goto error;
ret = iwl_send_bt_prio_tbl(mvm);
if (ret)
goto error;
ret = iwl_send_bt_init_conf(mvm);
if (ret)
goto error;
/* Send phy db control command and then phy db calibration*/
ret = iwl_send_phy_db_data(mvm->phy_db);
if (ret)
goto error;
ret = iwl_send_phy_cfg_cmd(mvm);
if (ret)
goto error;
/* init the fw <-> mac80211 STA mapping */
for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
/* Add auxiliary station for scanning */
ret = iwl_mvm_add_aux_sta(mvm);
if (ret)
goto error;
IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
return 0;
error:
iwl_trans_stop_device(mvm->trans);
return ret;
}
int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
{
int ret, i;
lockdep_assert_held(&mvm->mutex);
ret = iwl_trans_start_hw(mvm->trans);
if (ret)
return ret;
ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN);
if (ret) {
IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret);
goto error;
}
ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
if (ret)
goto error;
/* Send phy db control command and then phy db calibration*/
ret = iwl_send_phy_db_data(mvm->phy_db);
if (ret)
goto error;
ret = iwl_send_phy_cfg_cmd(mvm);
if (ret)
goto error;
/* init the fw <-> mac80211 STA mapping */
for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
/* Add auxiliary station for scanning */
ret = iwl_mvm_add_aux_sta(mvm);
if (ret)
goto error;
return 0;
error:
iwl_trans_stop_device(mvm->trans);
return ret;
}
int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_card_state_notif *card_state_notif = (void *)pkt->data;
u32 flags = le32_to_cpu(card_state_notif->flags);
IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n",
(flags & HW_CARD_DISABLED) ? "Kill" : "On",
(flags & SW_CARD_DISABLED) ? "Kill" : "On",
(flags & CT_KILL_CARD_DISABLED) ?
"Reached" : "Not reached");
return 0;
}
int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_radio_version_notif *radio_version = (void *)pkt->data;
/* TODO: what to do with that? */
IWL_DEBUG_INFO(mvm,
"Radio version: flavor: 0x%08x, step 0x%08x, dash 0x%08x\n",
le32_to_cpu(radio_version->radio_flavor),
le32_to_cpu(radio_version->radio_step),
le32_to_cpu(radio_version->radio_dash));
return 0;
}

View File

@@ -0,0 +1,134 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/leds.h>
#include "iwl-io.h"
#include "iwl-csr.h"
#include "mvm.h"
/* Set led register on */
static void iwl_mvm_led_enable(struct iwl_mvm *mvm)
{
iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON);
}
/* Set led register off */
static void iwl_mvm_led_disable(struct iwl_mvm *mvm)
{
iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF);
}
static void iwl_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led);
if (brightness > 0)
iwl_mvm_led_enable(mvm);
else
iwl_mvm_led_disable(mvm);
}
int iwl_mvm_leds_init(struct iwl_mvm *mvm)
{
int mode = iwlwifi_mod_params.led_mode;
int ret;
switch (mode) {
case IWL_LED_DEFAULT:
case IWL_LED_RF_STATE:
mode = IWL_LED_RF_STATE;
break;
case IWL_LED_DISABLE:
IWL_INFO(mvm, "Led disabled\n");
return 0;
default:
return -EINVAL;
};
mvm->led.name = kasprintf(GFP_KERNEL, "%s-led",
wiphy_name(mvm->hw->wiphy));
mvm->led.brightness_set = iwl_led_brightness_set;
mvm->led.max_brightness = 1;
if (mode == IWL_LED_RF_STATE)
mvm->led.default_trigger =
ieee80211_get_radio_led_name(mvm->hw);
ret = led_classdev_register(mvm->trans->dev, &mvm->led);
if (ret) {
kfree(mvm->led.name);
IWL_INFO(mvm, "Failed to enable led\n");
return ret;
}
return 0;
}
void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
{
if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE)
return;
led_classdev_unregister(&mvm->led);
kfree(mvm->led.name);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,538 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __IWL_MVM_H__
#define __IWL_MVM_H__
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/leds.h>
#include <linux/in6.h>
#include "iwl-op-mode.h"
#include "iwl-trans.h"
#include "iwl-notif-wait.h"
#include "iwl-eeprom-parse.h"
#include "iwl-test.h"
#include "iwl-trans.h"
#include "sta.h"
#include "fw-api.h"
#define IWL_INVALID_MAC80211_QUEUE 0xff
#define IWL_MVM_MAX_ADDRESSES 5
/* RSSI offset for WkP */
#define IWL_RSSI_OFFSET 50
enum iwl_mvm_tx_fifo {
IWL_MVM_TX_FIFO_BK = 0,
IWL_MVM_TX_FIFO_BE,
IWL_MVM_TX_FIFO_VI,
IWL_MVM_TX_FIFO_VO,
IWL_MVM_TX_FIFO_MCAST = 5,
};
extern struct ieee80211_ops iwl_mvm_hw_ops;
/**
* struct iwl_mvm_mod_params - module parameters for iwlmvm
* @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
* We will register to mac80211 to have testmode working. The NIC must not
* be up'ed after the INIT fw asserted. This is useful to be able to use
* proprietary tools over testmode to debug the INIT fw.
* @power_scheme: CAM(Continuous Active Mode)-1, BPS(Balanced Power
* Save)-2(default), LP(Low Power)-3
*/
struct iwl_mvm_mod_params {
bool init_dbg;
int power_scheme;
};
extern struct iwl_mvm_mod_params iwlmvm_mod_params;
struct iwl_mvm_phy_ctxt {
u16 id;
u16 color;
/*
* TODO: This should probably be removed. Currently here only for rate
* scaling algorithm
*/
struct ieee80211_channel *channel;
};
struct iwl_mvm_time_event_data {
struct ieee80211_vif *vif;
struct list_head list;
unsigned long end_jiffies;
u32 duration;
bool running;
u32 uid;
/*
* The access to the 'id' field must be done when the
* mvm->time_event_lock is held, as it value is used to indicate
* if the te is in the time event list or not (when id == TE_MAX)
*/
u32 id;
};
/* Power management */
/**
* enum iwl_power_scheme
* @IWL_POWER_LEVEL_CAM - Continuously Active Mode
* @IWL_POWER_LEVEL_BPS - Balanced Power Save (default)
* @IWL_POWER_LEVEL_LP - Low Power
*/
enum iwl_power_scheme {
IWL_POWER_SCHEME_CAM = 1,
IWL_POWER_SCHEME_BPS,
IWL_POWER_SCHEME_LP
};
#define IWL_CONN_MAX_LISTEN_INTERVAL 70
/**
* struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
* @id: between 0 and 3
* @color: to solve races upon MAC addition and removal
* @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA
* @uploaded: indicates the MAC context has been added to the device
* @ap_active: indicates that ap context is configured, and that the interface
* should get quota etc.
* @monitor_active: indicates that monitor context is configured, and that the
* interface should get quota etc.
* @queue_params: QoS params for this MAC
* @bcast_sta: station used for broadcast packets. Used by the following
* vifs: P2P_DEVICE, GO and AP.
* @beacon_skb: the skb used to hold the AP/GO beacon template
*/
struct iwl_mvm_vif {
u16 id;
u16 color;
u8 ap_sta_id;
bool uploaded;
bool ap_active;
bool monitor_active;
u32 ap_beacon_time;
enum iwl_tsf_id tsf_id;
/*
* QoS data from mac80211, need to store this here
* as mac80211 has a separate callback but we need
* to have the data for the MAC context
*/
struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
struct iwl_mvm_time_event_data time_event_data;
struct iwl_mvm_int_sta bcast_sta;
/*
* Assigned while mac80211 has the interface in a channel context,
* or, for P2P Device, while it exists.
*/
struct iwl_mvm_phy_ctxt *phy_ctxt;
#ifdef CONFIG_PM_SLEEP
/* WoWLAN GTK rekey data */
struct {
u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
__le64 replay_ctr;
bool valid;
} rekey_data;
int tx_key_idx;
#if IS_ENABLED(CONFIG_IPV6)
/* IPv6 addresses for WoWLAN */
struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS];
int num_target_ipv6_addrs;
#endif
#endif
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct dentry *dbgfs_dir;
struct dentry *dbgfs_slink;
void *dbgfs_data;
#endif
};
static inline struct iwl_mvm_vif *
iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
{
return (void *)vif->drv_priv;
}
enum iwl_mvm_status {
IWL_MVM_STATUS_HW_RFKILL,
IWL_MVM_STATUS_ROC_RUNNING,
IWL_MVM_STATUS_IN_HW_RESTART,
};
enum iwl_scan_status {
IWL_MVM_SCAN_NONE,
IWL_MVM_SCAN_OS,
};
/**
* struct iwl_nvm_section - describes an NVM section in memory.
*
* This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD,
* and saved for later use by the driver. Not all NVM sections are saved
* this way, only the needed ones.
*/
struct iwl_nvm_section {
u16 length;
const u8 *data;
};
struct iwl_mvm {
/* for logger access */
struct device *dev;
struct iwl_trans *trans;
const struct iwl_fw *fw;
const struct iwl_cfg *cfg;
struct iwl_phy_db *phy_db;
struct ieee80211_hw *hw;
/* for protecting access to iwl_mvm */
struct mutex mutex;
struct list_head async_handlers_list;
spinlock_t async_handlers_lock;
struct work_struct async_handlers_wk;
struct work_struct roc_done_wk;
unsigned long status;
enum iwl_ucode_type cur_ucode;
bool ucode_loaded;
bool init_ucode_run;
u32 error_event_table;
u32 log_event_table;
u32 ampdu_ref;
struct iwl_notif_wait_data notif_wait;
unsigned long transport_queue_stop;
u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
struct iwl_nvm_data *nvm_data;
/* NVM sections */
struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS];
/* EEPROM MAC addresses */
struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
/* data related to data path */
struct iwl_rx_phy_info last_phy_info;
struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT];
struct work_struct sta_drained_wk;
unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
atomic_t pending_frames[IWL_MVM_STATION_COUNT];
/* configured by mac80211 */
u32 rts_threshold;
/* Scan status, cmd (pre-allocated) and auxiliary station */
enum iwl_scan_status scan_status;
struct iwl_scan_cmd *scan_cmd;
/* Internal station */
struct iwl_mvm_int_sta aux_sta;
u8 scan_last_antenna_idx; /* to toggle TX between antennas */
u8 mgmt_last_antenna_idx;
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct dentry *debugfs_dir;
u32 dbgfs_sram_offset, dbgfs_sram_len;
bool prevent_power_down_d3;
#endif
struct iwl_mvm_phy_ctxt phy_ctxt_roc;
struct list_head time_event_list;
spinlock_t time_event_lock;
/*
* A bitmap indicating the index of the key in use. The firmware
* can hold 16 keys at most. Reflect this fact.
*/
unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
/*
* This counter of created interfaces is referenced only in conjunction
* with FW limitation related to power management. Currently PM is
* supported only on a single interface.
* IMPORTANT: this variable counts all interfaces except P2P device.
*/
u8 vif_count;
struct led_classdev led;
struct ieee80211_vif *p2p_device_vif;
#ifdef CONFIG_PM_SLEEP
int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
#endif
/* BT-Coex */
u8 bt_kill_msk;
struct iwl_bt_coex_profile_notif last_bt_notif;
};
/* Extract MVM priv from op_mode and _hw */
#define IWL_OP_MODE_GET_MVM(_iwl_op_mode) \
((struct iwl_mvm *)(_iwl_op_mode)->op_mode_specific)
#define IWL_MAC80211_GET_MVM(_hw) \
IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv))
extern const u8 iwl_mvm_ac_to_tx_fifo[];
struct iwl_rate_info {
u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */
u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */
u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
};
/******************
* MVM Methods
******************/
/* uCode */
int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm);
/* Utils */
int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
enum ieee80211_band band);
u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
u8 first_antenna(u8 mask);
u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
/* Tx / Host Commands */
int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm,
struct iwl_host_cmd *cmd);
int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id,
u32 flags, u16 len, const void *data);
int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm,
struct iwl_host_cmd *cmd,
u32 *status);
int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id,
u16 len, const void *data,
u32 *status);
int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_sta *sta);
int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
#ifdef CONFIG_IWLWIFI_DEBUG
const char *iwl_mvm_get_tx_fail_reason(u32 status);
#else
static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; }
#endif
int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync);
void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);
/* Statistics */
int iwl_mvm_rx_reply_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
/* NVM */
int iwl_nvm_init(struct iwl_mvm *mvm);
int iwl_mvm_up(struct iwl_mvm *mvm);
int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm);
/*
* FW notifications / CMD responses handlers
* Convention: iwl_mvm_rx_<NAME OF THE CMD>
*/
int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
/* MVM PHY */
int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic);
int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic);
void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm,
struct iwl_mvm_phy_ctxt *ctxt);
/* MAC (virtual interface) programming */
int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
/* Bindings */
int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
/* Quota management */
int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif);
/* Scanning */
int iwl_mvm_scan_request(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
void iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
/* MVM debugfs */
#ifdef CONFIG_IWLWIFI_DEBUGFS
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
#else
static inline int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm,
struct dentry *dbgfs_dir)
{
return 0;
}
static inline void
iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
}
static inline void
iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
}
#endif /* CONFIG_IWLWIFI_DEBUGFS */
/* rate scaling */
int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
u8 flags, bool init);
/* power managment */
int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct iwl_powertable_cmd *cmd);
int iwl_mvm_leds_init(struct iwl_mvm *mvm);
void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
/* D3 (WoWLAN, NetDetect) */
int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
int iwl_mvm_resume(struct ieee80211_hw *hw);
void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled);
void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_gtk_rekey_data *data);
void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct inet6_dev *idev);
void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int idx);
/* BT Coex */
int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event);
void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
#endif /* __IWL_MVM_H__ */

View File

@@ -0,0 +1,238 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include "iwl-trans.h"
#include "mvm.h"
#include "iwl-eeprom-parse.h"
#include "iwl-eeprom-read.h"
#include "iwl-nvm-parse.h"
/* list of NVM sections we are allowed/need to read */
static const int nvm_to_read[] = {
NVM_SECTION_TYPE_HW,
NVM_SECTION_TYPE_SW,
NVM_SECTION_TYPE_CALIBRATION,
NVM_SECTION_TYPE_PRODUCTION,
};
/* Default NVM size to read */
#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024);
static inline void iwl_nvm_fill_read(struct iwl_nvm_access_cmd *cmd,
u16 offset, u16 length, u16 section)
{
cmd->offset = cpu_to_le16(offset);
cmd->length = cpu_to_le16(length);
cmd->type = cpu_to_le16(section);
}
static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
u16 offset, u16 length, u8 *data)
{
struct iwl_nvm_access_cmd nvm_access_cmd = {};
struct iwl_nvm_access_resp *nvm_resp;
struct iwl_rx_packet *pkt;
struct iwl_host_cmd cmd = {
.id = NVM_ACCESS_CMD,
.flags = CMD_SYNC | CMD_WANT_SKB,
.data = { &nvm_access_cmd, },
};
int ret, bytes_read, offset_read;
u8 *resp_data;
iwl_nvm_fill_read(&nvm_access_cmd, offset, length, section);
cmd.len[0] = sizeof(struct iwl_nvm_access_cmd);
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret)
return ret;
pkt = cmd.resp_pkt;
if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(mvm, "Bad return from NVM_ACCES_COMMAND (0x%08X)\n",
pkt->hdr.flags);
ret = -EIO;
goto exit;
}
/* Extract NVM response */
nvm_resp = (void *)pkt->data;
ret = le16_to_cpu(nvm_resp->status);
bytes_read = le16_to_cpu(nvm_resp->length);
offset_read = le16_to_cpu(nvm_resp->offset);
resp_data = nvm_resp->data;
if (ret) {
IWL_ERR(mvm,
"NVM access command failed with status %d (device: %s)\n",
ret, mvm->cfg->name);
ret = -EINVAL;
goto exit;
}
if (offset_read != offset) {
IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n",
offset_read);
ret = -EINVAL;
goto exit;
}
/* Write data to NVM */
memcpy(data + offset, resp_data, bytes_read);
ret = bytes_read;
exit:
iwl_free_resp(&cmd);
return ret;
}
/*
* Reads an NVM section completely.
* NICs prior to 7000 family doesn't have a real NVM, but just read
* section 0 which is the EEPROM. Because the EEPROM reading is unlimited
* by uCode, we need to manually check in this case that we don't
* overflow and try to read more than the EEPROM size.
* For 7000 family NICs, we supply the maximal size we can read, and
* the uCode fills the response with as much data as we can,
* without overflowing, so no check is needed.
*/
static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
u8 *data)
{
u16 length, offset = 0;
int ret;
/* Set nvm section read length */
length = IWL_NVM_DEFAULT_CHUNK_SIZE;
ret = length;
/* Read the NVM until exhausted (reading less than requested) */
while (ret == length) {
ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
if (ret < 0) {
IWL_ERR(mvm,
"Cannot read NVM from section %d offset %d, length %d\n",
section, offset, length);
return ret;
}
offset += ret;
}
IWL_INFO(mvm, "NVM section %d read completed\n", section);
return offset;
}
static struct iwl_nvm_data *
iwl_parse_nvm_sections(struct iwl_mvm *mvm)
{
struct iwl_nvm_section *sections = mvm->nvm_sections;
const __le16 *hw, *sw, *calib;
/* Checking for required sections */
if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
!mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) {
IWL_ERR(mvm, "Can't parse empty NVM sections\n");
return NULL;
}
if (WARN_ON(!mvm->cfg))
return NULL;
hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data;
sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib);
}
int iwl_nvm_init(struct iwl_mvm *mvm)
{
int ret, i, section;
u8 *nvm_buffer, *temp;
/* TODO: find correct NVM max size for a section */
nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
GFP_KERNEL);
if (!nvm_buffer)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
section = nvm_to_read[i];
/* we override the constness for initial read */
ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
if (ret < 0)
break;
temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
if (!temp) {
ret = -ENOMEM;
break;
}
mvm->nvm_sections[section].data = temp;
mvm->nvm_sections[section].length = ret;
}
kfree(nvm_buffer);
if (ret < 0)
return ret;
ret = 0;
mvm->nvm_data = iwl_parse_nvm_sections(mvm);
return ret;
}

View File

@@ -0,0 +1,677 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/module.h>
#include <net/mac80211.h>
#include "iwl-notif-wait.h"
#include "iwl-trans.h"
#include "iwl-op-mode.h"
#include "iwl-fw.h"
#include "iwl-debug.h"
#include "iwl-drv.h"
#include "iwl-modparams.h"
#include "mvm.h"
#include "iwl-phy-db.h"
#include "iwl-eeprom-parse.h"
#include "iwl-csr.h"
#include "iwl-io.h"
#include "iwl-prph.h"
#include "rs.h"
#include "fw-api-scan.h"
#include "time-event.h"
/*
* module name, copyright, version, etc.
*/
#define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux"
#define DRV_VERSION IWLWIFI_VERSION
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
static const struct iwl_op_mode_ops iwl_mvm_ops;
struct iwl_mvm_mod_params iwlmvm_mod_params = {
.power_scheme = IWL_POWER_SCHEME_BPS,
/* rest of fields are 0 by default */
};
module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO);
MODULE_PARM_DESC(init_dbg,
"set to true to debug an ASSERT in INIT fw (default: false");
module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO);
MODULE_PARM_DESC(power_scheme,
"power management scheme: 1-active, 2-balanced, 3-low power, default: 2");
/*
* module init and exit functions
*/
static int __init iwl_mvm_init(void)
{
int ret;
ret = iwl_mvm_rate_control_register();
if (ret) {
pr_err("Unable to register rate control algorithm: %d\n", ret);
return ret;
}
ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops);
if (ret) {
pr_err("Unable to register MVM op_mode: %d\n", ret);
iwl_mvm_rate_control_unregister();
}
return ret;
}
module_init(iwl_mvm_init);
static void __exit iwl_mvm_exit(void)
{
iwl_opmode_deregister("iwlmvm");
iwl_mvm_rate_control_unregister();
}
module_exit(iwl_mvm_exit);
static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash;
u32 reg_val = 0;
radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >>
FW_PHY_CFG_RADIO_TYPE_POS;
radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >>
FW_PHY_CFG_RADIO_STEP_POS;
radio_cfg_dash = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_DASH) >>
FW_PHY_CFG_RADIO_DASH_POS;
/* SKU control */
reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) <<
CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) <<
CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
/* radio configuration */
reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) &
~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE);
/* silicon bits */
reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP |
CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH |
CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
CSR_HW_IF_CONFIG_REG_BIT_MAC_SI,
reg_val);
IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
radio_cfg_step, radio_cfg_dash);
/*
* W/A : NIC is stuck in a reset state after Early PCIe power off
* (PCIe power is lost before PERST# is asserted), causing ME FW
* to lose ownership and not being able to obtain it back.
*/
iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
}
struct iwl_rx_handlers {
u8 cmd_id;
bool async;
int (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
};
#define RX_HANDLER(_cmd_id, _fn, _async) \
{ .cmd_id = _cmd_id , .fn = _fn , .async = _async }
/*
* Handlers for fw notifications
* Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME
* This list should be in order of frequency for performance purposes.
*
* The handler can be SYNC - this means that it will be called in the Rx path
* which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and
* only in this case!), it should be set as ASYNC. In that case, it will be
* called from a worker with mvm->mutex held.
*/
static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER(REPLY_RX_MPDU_CMD, iwl_mvm_rx_rx_mpdu, false),
RX_HANDLER(REPLY_RX_PHY_CMD, iwl_mvm_rx_rx_phy_cmd, false),
RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
};
#undef RX_HANDLER
#define CMD(x) [x] = #x
static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(MVM_ALIVE),
CMD(REPLY_ERROR),
CMD(INIT_COMPLETE_NOTIF),
CMD(PHY_CONTEXT_CMD),
CMD(MGMT_MCAST_KEY),
CMD(TX_CMD),
CMD(TXPATH_FLUSH),
CMD(MAC_CONTEXT_CMD),
CMD(TIME_EVENT_CMD),
CMD(TIME_EVENT_NOTIFICATION),
CMD(BINDING_CONTEXT_CMD),
CMD(TIME_QUOTA_CMD),
CMD(RADIO_VERSION_NOTIFICATION),
CMD(SCAN_REQUEST_CMD),
CMD(SCAN_ABORT_CMD),
CMD(SCAN_START_NOTIFICATION),
CMD(SCAN_RESULTS_NOTIFICATION),
CMD(SCAN_COMPLETE_NOTIFICATION),
CMD(NVM_ACCESS_CMD),
CMD(PHY_CONFIGURATION_CMD),
CMD(CALIB_RES_NOTIF_PHY_DB),
CMD(SET_CALIB_DEFAULT_CMD),
CMD(CALIBRATION_COMPLETE_NOTIFICATION),
CMD(ADD_STA),
CMD(REMOVE_STA),
CMD(LQ_CMD),
CMD(SCAN_OFFLOAD_CONFIG_CMD),
CMD(SCAN_OFFLOAD_REQUEST_CMD),
CMD(SCAN_OFFLOAD_ABORT_CMD),
CMD(SCAN_OFFLOAD_COMPLETE),
CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
CMD(POWER_TABLE_CMD),
CMD(WEP_KEY),
CMD(REPLY_RX_PHY_CMD),
CMD(REPLY_RX_MPDU_CMD),
CMD(BEACON_NOTIFICATION),
CMD(BEACON_TEMPLATE_CMD),
CMD(STATISTICS_NOTIFICATION),
CMD(TX_ANT_CONFIGURATION_CMD),
CMD(D3_CONFIG_CMD),
CMD(PROT_OFFLOAD_CONFIG_CMD),
CMD(OFFLOADS_QUERY_CMD),
CMD(REMOTE_WAKE_CONFIG_CMD),
CMD(WOWLAN_PATTERNS),
CMD(WOWLAN_CONFIGURATION),
CMD(WOWLAN_TSC_RSC_PARAM),
CMD(WOWLAN_TKIP_PARAM),
CMD(WOWLAN_KEK_KCK_MATERIAL),
CMD(WOWLAN_GET_STATUSES),
CMD(WOWLAN_TX_POWER_PER_DB),
CMD(NET_DETECT_CONFIG_CMD),
CMD(NET_DETECT_PROFILES_QUERY_CMD),
CMD(NET_DETECT_PROFILES_CMD),
CMD(NET_DETECT_HOTSPOTS_CMD),
CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
CMD(CARD_STATE_NOTIFICATION),
CMD(BT_COEX_PRIO_TABLE),
CMD(BT_COEX_PROT_ENV),
CMD(BT_PROFILE_NOTIFICATION),
CMD(BT_CONFIG),
CMD(MCAST_FILTER_CMD),
};
#undef CMD
/* this forward declaration can avoid to export the function */
static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
static struct iwl_op_mode *
iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
const struct iwl_fw *fw, struct dentry *dbgfs_dir)
{
struct ieee80211_hw *hw;
struct iwl_op_mode *op_mode;
struct iwl_mvm *mvm;
struct iwl_trans_config trans_cfg = {};
static const u8 no_reclaim_cmds[] = {
TX_CMD,
};
int err, scan_size;
/********************************
* 1. Allocating and configuring HW data
********************************/
hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) +
sizeof(struct iwl_mvm),
&iwl_mvm_hw_ops);
if (!hw)
return NULL;
op_mode = hw->priv;
op_mode->ops = &iwl_mvm_ops;
op_mode->trans = trans;
mvm = IWL_OP_MODE_GET_MVM(op_mode);
mvm->dev = trans->dev;
mvm->trans = trans;
mvm->cfg = cfg;
mvm->fw = fw;
mvm->hw = hw;
mutex_init(&mvm->mutex);
spin_lock_init(&mvm->async_handlers_lock);
INIT_LIST_HEAD(&mvm->time_event_list);
INIT_LIST_HEAD(&mvm->async_handlers_list);
spin_lock_init(&mvm->time_event_lock);
INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
/*
* Populate the state variables that the transport layer needs
* to know about.
*/
trans_cfg.op_mode = op_mode;
trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE)
trans_cfg.bc_table_dword = true;
if (!iwlwifi_mod_params.wd_disable)
trans_cfg.queue_watchdog_timeout = cfg->base_params->wd_timeout;
else
trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED;
trans_cfg.command_names = iwl_mvm_cmd_strings;
trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE;
trans_cfg.cmd_fifo = IWL_MVM_CMD_FIFO;
snprintf(mvm->hw->wiphy->fw_version,
sizeof(mvm->hw->wiphy->fw_version),
"%s", fw->fw_version);
/* Configure transport layer */
iwl_trans_configure(mvm->trans, &trans_cfg);
trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
/* set up notification wait support */
iwl_notification_wait_init(&mvm->notif_wait);
/* Init phy db */
mvm->phy_db = iwl_phy_db_init(trans);
if (!mvm->phy_db) {
IWL_ERR(mvm, "Cannot init phy_db\n");
goto out_free;
}
IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
mvm->cfg->name, mvm->trans->hw_rev);
err = iwl_trans_start_hw(mvm->trans);
if (err)
goto out_free;
mutex_lock(&mvm->mutex);
err = iwl_run_init_mvm_ucode(mvm, true);
mutex_unlock(&mvm->mutex);
if (err && !iwlmvm_mod_params.init_dbg) {
IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
goto out_free;
}
/* Stop the hw after the ALIVE and NVM has been read */
if (!iwlmvm_mod_params.init_dbg)
iwl_trans_stop_hw(mvm->trans, false);
scan_size = sizeof(struct iwl_scan_cmd) +
mvm->fw->ucode_capa.max_probe_length +
(MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel));
mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
if (!mvm->scan_cmd)
goto out_free;
err = iwl_mvm_mac_setup_register(mvm);
if (err)
goto out_free;
err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir);
if (err)
goto out_unregister;
return op_mode;
out_unregister:
ieee80211_unregister_hw(mvm->hw);
out_free:
iwl_phy_db_free(mvm->phy_db);
kfree(mvm->scan_cmd);
iwl_trans_stop_hw(trans, true);
ieee80211_free_hw(mvm->hw);
return NULL;
}
static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
int i;
iwl_mvm_leds_exit(mvm);
ieee80211_unregister_hw(mvm->hw);
kfree(mvm->scan_cmd);
iwl_trans_stop_hw(mvm->trans, true);
iwl_phy_db_free(mvm->phy_db);
mvm->phy_db = NULL;
iwl_free_nvm_data(mvm->nvm_data);
for (i = 0; i < NVM_NUM_OF_SECTIONS; i++)
kfree(mvm->nvm_sections[i].data);
ieee80211_free_hw(mvm->hw);
}
struct iwl_async_handler_entry {
struct list_head list;
struct iwl_rx_cmd_buffer rxb;
int (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
};
void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm)
{
struct iwl_async_handler_entry *entry, *tmp;
spin_lock_bh(&mvm->async_handlers_lock);
list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) {
iwl_free_rxb(&entry->rxb);
list_del(&entry->list);
kfree(entry);
}
spin_unlock_bh(&mvm->async_handlers_lock);
}
static void iwl_mvm_async_handlers_wk(struct work_struct *wk)
{
struct iwl_mvm *mvm =
container_of(wk, struct iwl_mvm, async_handlers_wk);
struct iwl_async_handler_entry *entry, *tmp;
struct list_head local_list;
INIT_LIST_HEAD(&local_list);
/* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */
mutex_lock(&mvm->mutex);
/*
* Sync with Rx path with a lock. Remove all the entries from this list,
* add them to a local one (lock free), and then handle them.
*/
spin_lock_bh(&mvm->async_handlers_lock);
list_splice_init(&mvm->async_handlers_list, &local_list);
spin_unlock_bh(&mvm->async_handlers_lock);
list_for_each_entry_safe(entry, tmp, &local_list, list) {
if (entry->fn(mvm, &entry->rxb, NULL))
IWL_WARN(mvm,
"returned value from ASYNC handlers are ignored\n");
iwl_free_rxb(&entry->rxb);
list_del(&entry->list);
kfree(entry);
}
mutex_unlock(&mvm->mutex);
}
static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
u8 i;
/*
* Do the notification wait before RX handlers so
* even if the RX handler consumes the RXB we have
* access to it in the notification wait entry.
*/
iwl_notification_wait_notify(&mvm->notif_wait, pkt);
for (i = 0; i < ARRAY_SIZE(iwl_mvm_rx_handlers); i++) {
const struct iwl_rx_handlers *rx_h = &iwl_mvm_rx_handlers[i];
struct iwl_async_handler_entry *entry;
if (rx_h->cmd_id != pkt->hdr.cmd)
continue;
if (!rx_h->async)
return rx_h->fn(mvm, rxb, cmd);
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
/* we can't do much... */
if (!entry)
return 0;
entry->rxb._page = rxb_steal_page(rxb);
entry->rxb._offset = rxb->_offset;
entry->rxb._rx_page_order = rxb->_rx_page_order;
entry->fn = rx_h->fn;
spin_lock(&mvm->async_handlers_lock);
list_add_tail(&entry->list, &mvm->async_handlers_list);
spin_unlock(&mvm->async_handlers_lock);
schedule_work(&mvm->async_handlers_wk);
break;
}
return 0;
}
static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
int mq = mvm->queue_to_mac80211[queue];
if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
return;
if (atomic_inc_return(&mvm->queue_stop_count[mq]) > 1) {
IWL_DEBUG_TX_QUEUES(mvm,
"queue %d (mac80211 %d) already stopped\n",
queue, mq);
return;
}
set_bit(mq, &mvm->transport_queue_stop);
ieee80211_stop_queue(mvm->hw, mq);
}
static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
int mq = mvm->queue_to_mac80211[queue];
if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
return;
if (atomic_dec_return(&mvm->queue_stop_count[mq]) > 0) {
IWL_DEBUG_TX_QUEUES(mvm,
"queue %d (mac80211 %d) already awake\n",
queue, mq);
return;
}
clear_bit(mq, &mvm->transport_queue_stop);
ieee80211_wake_queue(mvm->hw, mq);
}
static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
if (state)
set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
else
clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state);
}
static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
struct ieee80211_tx_info *info;
info = IEEE80211_SKB_CB(skb);
iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
ieee80211_free_txskb(mvm->hw, skb);
}
static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
{
iwl_abort_notification_waits(&mvm->notif_wait);
/*
* If we're restarting already, don't cycle restarts.
* If INIT fw asserted, it will likely fail again.
* If WoWLAN fw asserted, don't restart either, mac80211
* can't recover this since we're already half suspended.
*/
if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
IWL_ERR(mvm, "Firmware error during reconfiguration! Abort.\n");
} else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
iwlwifi_mod_params.restart_fw) {
/*
* This is a bit racy, but worst case we tell mac80211 about
* a stopped/aborted (sched) scan when that was already done
* which is not a problem. It is necessary to abort any scan
* here because mac80211 requires having the scan cleared
* before restarting.
* We'll reset the scan_status to NONE in restart cleanup in
* the next start() call from mac80211.
*/
switch (mvm->scan_status) {
case IWL_MVM_SCAN_NONE:
break;
case IWL_MVM_SCAN_OS:
ieee80211_scan_completed(mvm->hw, true);
break;
}
ieee80211_restart_hw(mvm->hw);
}
}
static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
iwl_mvm_dump_nic_error_log(mvm);
iwl_mvm_nic_restart(mvm);
}
static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
WARN_ON(1);
iwl_mvm_nic_restart(mvm);
}
static const struct iwl_op_mode_ops iwl_mvm_ops = {
.start = iwl_op_mode_mvm_start,
.stop = iwl_op_mode_mvm_stop,
.rx = iwl_mvm_rx_dispatch,
.queue_full = iwl_mvm_stop_sw_queue,
.queue_not_full = iwl_mvm_wake_sw_queue,
.hw_rf_kill = iwl_mvm_set_hw_rfkill_state,
.free_skb = iwl_mvm_free_skb,
.nic_error = iwl_mvm_nic_error,
.cmd_queue_full = iwl_mvm_cmd_queue_full,
.nic_config = iwl_mvm_nic_config,
};

View File

@@ -0,0 +1,286 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <net/mac80211.h>
#include "fw-api.h"
#include "mvm.h"
/* Maps the driver specific channel width definition to the the fw values */
static inline u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
return PHY_VHT_CHANNEL_MODE20;
case NL80211_CHAN_WIDTH_40:
return PHY_VHT_CHANNEL_MODE40;
case NL80211_CHAN_WIDTH_80:
return PHY_VHT_CHANNEL_MODE80;
case NL80211_CHAN_WIDTH_160:
return PHY_VHT_CHANNEL_MODE160;
default:
WARN(1, "Invalid channel width=%u", chandef->width);
return PHY_VHT_CHANNEL_MODE20;
}
}
/*
* Maps the driver specific control channel position (relative to the center
* freq) definitions to the the fw values
*/
static inline u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
{
switch (chandef->chan->center_freq - chandef->center_freq1) {
case -70:
return PHY_VHT_CTRL_POS_4_BELOW;
case -50:
return PHY_VHT_CTRL_POS_3_BELOW;
case -30:
return PHY_VHT_CTRL_POS_2_BELOW;
case -10:
return PHY_VHT_CTRL_POS_1_BELOW;
case 10:
return PHY_VHT_CTRL_POS_1_ABOVE;
case 30:
return PHY_VHT_CTRL_POS_2_ABOVE;
case 50:
return PHY_VHT_CTRL_POS_3_ABOVE;
case 70:
return PHY_VHT_CTRL_POS_4_ABOVE;
default:
WARN(1, "Invalid channel definition");
case 0:
/*
* The FW is expected to check the control channel position only
* when in HT/VHT and the channel width is not 20MHz. Return
* this value as the default one.
*/
return PHY_VHT_CTRL_POS_1_BELOW;
}
}
/*
* Construct the generic fields of the PHY context command
*/
static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
struct iwl_phy_context_cmd *cmd,
u32 action, u32 apply_time)
{
memset(cmd, 0, sizeof(struct iwl_phy_context_cmd));
cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
ctxt->color));
cmd->action = cpu_to_le32(action);
cmd->apply_time = cpu_to_le32(apply_time);
}
/*
* Add the phy configuration to the PHY context command
*/
static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
struct iwl_phy_context_cmd *cmd,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic)
{
u8 active_cnt, idle_cnt;
/* Set the channel info data */
cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
PHY_BAND_24 : PHY_BAND_5);
cmd->ci.channel = chandef->chan->hw_value;
cmd->ci.width = iwl_mvm_get_channel_width(chandef);
cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
/* Set rx the chains */
idle_cnt = chains_static;
active_cnt = chains_dynamic;
cmd->rxchain_info = cpu_to_le32(iwl_fw_valid_rx_ant(mvm->fw) <<
PHY_RX_CHAIN_VALID_POS);
cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
cmd->rxchain_info |= cpu_to_le32(active_cnt <<
PHY_RX_CHAIN_MIMO_CNT_POS);
cmd->txchain_info = cpu_to_le32(iwl_fw_valid_tx_ant(mvm->fw));
}
/*
* Send a command to apply the current phy configuration. The command is send
* only if something in the configuration changed: in case that this is the
* first time that the phy configuration is applied or in case that the phy
* configuration changed from the previous apply.
*/
static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
struct iwl_mvm_phy_ctxt *ctxt,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic,
u32 action, u32 apply_time)
{
struct iwl_phy_context_cmd cmd;
int ret;
/* Set the command header fields */
iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time);
/* Set the command data */
iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
chains_static, chains_dynamic);
ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
sizeof(struct iwl_phy_context_cmd),
&cmd);
if (ret)
IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
return ret;
}
struct phy_ctx_used_data {
unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)];
};
static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx,
void *_data)
{
struct phy_ctx_used_data *data = _data;
struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
__set_bit(phy_ctxt->id, data->used);
}
/*
* Send a command to add a PHY context based on the current HW configuration.
*/
int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic)
{
struct phy_ctx_used_data data = {
.used = { },
};
/*
* If this is a regular PHY context (not the ROC one)
* skip the ROC PHY context's ID.
*/
if (ctxt != &mvm->phy_ctxt_roc)
__set_bit(mvm->phy_ctxt_roc.id, data.used);
lockdep_assert_held(&mvm->mutex);
ctxt->color++;
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
ieee80211_iter_chan_contexts_atomic(
mvm->hw, iwl_mvm_phy_ctx_used_iter, &data);
ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX);
if (WARN_ONCE(ctxt->id == NUM_PHY_CTX,
"Failed to init PHY context - no free ID!\n"))
return -EIO;
}
ctxt->channel = chandef->chan;
return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
chains_static, chains_dynamic,
FW_CTXT_ACTION_ADD, 0);
}
/*
* Send a command to modify the PHY context based on the current HW
* configuration. Note that the function does not check that the configuration
* changed.
*/
int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic)
{
lockdep_assert_held(&mvm->mutex);
ctxt->channel = chandef->chan;
return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
chains_static, chains_dynamic,
FW_CTXT_ACTION_MODIFY, 0);
}
/*
* Send a command to the FW to remove the given phy context.
* Once the command is sent, regardless of success or failure, the context is
* marked as invalid
*/
void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
{
struct iwl_phy_context_cmd cmd;
int ret;
lockdep_assert_held(&mvm->mutex);
iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, FW_CTXT_ACTION_REMOVE, 0);
ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
sizeof(struct iwl_phy_context_cmd),
&cmd);
if (ret)
IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n",
ctxt->id);
}

View File

@@ -0,0 +1,180 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <net/mac80211.h>
#include "iwl-debug.h"
#include "mvm.h"
#include "iwl-modparams.h"
#include "fw-api-power.h"
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
static void iwl_mvm_power_log(struct iwl_mvm *mvm,
struct iwl_powertable_cmd *cmd)
{
IWL_DEBUG_POWER(mvm,
"Sending power table command for power level %d, flags = 0x%X\n",
iwlmvm_mod_params.power_scheme,
le16_to_cpu(cmd->flags));
IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
le32_to_cpu(cmd->rx_data_timeout));
IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
le32_to_cpu(cmd->tx_data_timeout));
IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
cmd->lprx_rssi_threshold);
}
}
void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct iwl_powertable_cmd *cmd)
{
struct ieee80211_hw *hw = mvm->hw;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
int dtimper, dtimper_msec;
int keep_alive;
bool radar_detect = false;
/*
* Regardless of power management state the driver must set
* keep alive period. FW will use it for sending keep alive NDPs
* immediately after association.
*/
cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
if (!vif->bss_conf.ps)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
dtimper = hw->conf.ps_dtim_period ?: 1;
/* Check if radar detection is required on current channel */
rcu_read_lock();
chanctx_conf = rcu_dereference(vif->chanctx_conf);
WARN_ON(!chanctx_conf);
if (chanctx_conf) {
chan = chanctx_conf->def.chan;
radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
}
rcu_read_unlock();
/* Check skip over DTIM conditions */
if (!radar_detect && (dtimper <= 10) &&
(iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP))
cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
/* Check that keep alive period is at least 3 * DTIM */
dtimper_msec = dtimper * vif->bss_conf.beacon_int;
keep_alive = max_t(int, 3 * dtimper_msec,
MSEC_PER_SEC * cmd->keep_alive_seconds);
keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
cmd->keep_alive_seconds = keep_alive;
cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
}
int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_powertable_cmd cmd = {};
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
iwl_mvm_power_log(mvm, &cmd);
return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
}
int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_powertable_cmd cmd = {};
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
iwl_mvm_power_log(mvm, &cmd);
return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
sizeof(cmd), &cmd);
}

View File

@@ -0,0 +1,198 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <net/mac80211.h>
#include "fw-api.h"
#include "mvm.h"
struct iwl_mvm_quota_iterator_data {
int n_interfaces[MAX_BINDINGS];
int colors[MAX_BINDINGS];
struct ieee80211_vif *new_vif;
};
static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_quota_iterator_data *data = _data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u16 id;
/*
* We'll account for the new interface (if any) below,
* skip it here in case we're not called from within
* the add_interface callback (otherwise it won't show
* up in iteration)
*/
if (vif == data->new_vif)
return;
if (!mvmvif->phy_ctxt)
return;
/* currently, PHY ID == binding ID */
id = mvmvif->phy_ctxt->id;
/* need at least one binding per PHY */
BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS);
if (WARN_ON_ONCE(id >= MAX_BINDINGS))
return;
if (data->colors[id] < 0)
data->colors[id] = mvmvif->phy_ctxt->color;
else
WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
switch (vif->type) {
case NL80211_IFTYPE_STATION:
if (vif->bss_conf.assoc)
data->n_interfaces[id]++;
break;
case NL80211_IFTYPE_AP:
if (mvmvif->ap_active)
data->n_interfaces[id]++;
break;
case NL80211_IFTYPE_MONITOR:
if (mvmvif->monitor_active)
data->n_interfaces[id]++;
break;
case NL80211_IFTYPE_P2P_DEVICE:
break;
case NL80211_IFTYPE_ADHOC:
if (vif->bss_conf.ibss_joined)
data->n_interfaces[id]++;
break;
default:
WARN_ON_ONCE(1);
break;
}
}
int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
{
struct iwl_time_quota_cmd cmd;
int i, idx, ret, num_active_bindings, quota, quota_rem;
struct iwl_mvm_quota_iterator_data data = {
.n_interfaces = {},
.colors = { -1, -1, -1, -1 },
.new_vif = newvif,
};
/* update all upon completion */
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
return 0;
BUILD_BUG_ON(data.colors[MAX_BINDINGS - 1] != -1);
lockdep_assert_held(&mvm->mutex);
memset(&cmd, 0, sizeof(cmd));
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_quota_iterator, &data);
if (newvif) {
data.new_vif = NULL;
iwl_mvm_quota_iterator(&data, newvif->addr, newvif);
}
/*
* The FW's scheduling session consists of
* IWL_MVM_MAX_QUOTA fragments. Divide these fragments
* equally between all the bindings that require quota
*/
num_active_bindings = 0;
for (i = 0; i < MAX_BINDINGS; i++) {
cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
if (data.n_interfaces[i] > 0)
num_active_bindings++;
}
if (!num_active_bindings)
goto send_cmd;
quota = IWL_MVM_MAX_QUOTA / num_active_bindings;
quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings;
for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
if (data.n_interfaces[i] <= 0)
continue;
cmd.quotas[idx].id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
cmd.quotas[idx].quota = cpu_to_le32(quota);
cmd.quotas[idx].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA);
idx++;
}
/* Give the remainder of the session to the first binding */
le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
send_cmd:
ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
if (ret)
IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,393 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
#ifndef __rs_h__
#define __rs_h__
#include <net/mac80211.h>
#include "iwl-config.h"
#include "fw-api.h"
#include "iwl-trans.h"
struct iwl_rs_rate_info {
u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */
u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */
u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
u8 prev_ieee; /* previous rate in IEEE speeds */
u8 next_ieee; /* next rate in IEEE speeds */
u8 prev_rs; /* previous rate used in rs algo */
u8 next_rs; /* next rate used in rs algo */
u8 prev_rs_tgg; /* previous rate used in TGG rs algo */
u8 next_rs_tgg; /* next rate used in TGG rs algo */
};
#define IWL_RATE_60M_PLCP 3
enum {
IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
IWL_RATE_INVALID = IWL_RATE_COUNT,
};
#define LINK_QUAL_MAX_RETRY_NUM 16
enum {
IWL_RATE_6M_INDEX_TABLE = 0,
IWL_RATE_9M_INDEX_TABLE,
IWL_RATE_12M_INDEX_TABLE,
IWL_RATE_18M_INDEX_TABLE,
IWL_RATE_24M_INDEX_TABLE,
IWL_RATE_36M_INDEX_TABLE,
IWL_RATE_48M_INDEX_TABLE,
IWL_RATE_54M_INDEX_TABLE,
IWL_RATE_1M_INDEX_TABLE,
IWL_RATE_2M_INDEX_TABLE,
IWL_RATE_5M_INDEX_TABLE,
IWL_RATE_11M_INDEX_TABLE,
IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1,
};
/* #define vs. enum to keep from defaulting to 'large integer' */
#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX)
#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX)
#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX)
#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX)
#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX)
#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX)
#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX)
#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX)
#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX)
#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX)
#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX)
#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX)
#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX)
/* uCode API values for OFDM high-throughput (HT) bit rates */
enum {
IWL_RATE_SISO_6M_PLCP = 0,
IWL_RATE_SISO_12M_PLCP = 1,
IWL_RATE_SISO_18M_PLCP = 2,
IWL_RATE_SISO_24M_PLCP = 3,
IWL_RATE_SISO_36M_PLCP = 4,
IWL_RATE_SISO_48M_PLCP = 5,
IWL_RATE_SISO_54M_PLCP = 6,
IWL_RATE_SISO_60M_PLCP = 7,
IWL_RATE_MIMO2_6M_PLCP = 0x8,
IWL_RATE_MIMO2_12M_PLCP = 0x9,
IWL_RATE_MIMO2_18M_PLCP = 0xa,
IWL_RATE_MIMO2_24M_PLCP = 0xb,
IWL_RATE_MIMO2_36M_PLCP = 0xc,
IWL_RATE_MIMO2_48M_PLCP = 0xd,
IWL_RATE_MIMO2_54M_PLCP = 0xe,
IWL_RATE_MIMO2_60M_PLCP = 0xf,
IWL_RATE_MIMO3_6M_PLCP = 0x10,
IWL_RATE_MIMO3_12M_PLCP = 0x11,
IWL_RATE_MIMO3_18M_PLCP = 0x12,
IWL_RATE_MIMO3_24M_PLCP = 0x13,
IWL_RATE_MIMO3_36M_PLCP = 0x14,
IWL_RATE_MIMO3_48M_PLCP = 0x15,
IWL_RATE_MIMO3_54M_PLCP = 0x16,
IWL_RATE_MIMO3_60M_PLCP = 0x17,
IWL_RATE_SISO_INVM_PLCP,
IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
};
/* MAC header values for bit rates */
enum {
IWL_RATE_6M_IEEE = 12,
IWL_RATE_9M_IEEE = 18,
IWL_RATE_12M_IEEE = 24,
IWL_RATE_18M_IEEE = 36,
IWL_RATE_24M_IEEE = 48,
IWL_RATE_36M_IEEE = 72,
IWL_RATE_48M_IEEE = 96,
IWL_RATE_54M_IEEE = 108,
IWL_RATE_60M_IEEE = 120,
IWL_RATE_1M_IEEE = 2,
IWL_RATE_2M_IEEE = 4,
IWL_RATE_5M_IEEE = 11,
IWL_RATE_11M_IEEE = 22,
};
#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
#define IWL_INVALID_VALUE -1
#define IWL_MIN_RSSI_VAL -100
#define IWL_MAX_RSSI_VAL 0
/* These values specify how many Tx frame attempts before
* searching for a new modulation mode */
#define IWL_LEGACY_FAILURE_LIMIT 160
#define IWL_LEGACY_SUCCESS_LIMIT 480
#define IWL_LEGACY_TABLE_COUNT 160
#define IWL_NONE_LEGACY_FAILURE_LIMIT 400
#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500
#define IWL_NONE_LEGACY_TABLE_COUNT 1500
/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */
#define IWL_RS_GOOD_RATIO 12800 /* 100% */
#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */
#define IWL_RATE_HIGH_TH 10880 /* 85% */
#define IWL_RATE_INCREASE_TH 6400 /* 50% */
#define IWL_RATE_DECREASE_TH 1920 /* 15% */
/* possible actions when in legacy mode */
#define IWL_LEGACY_SWITCH_ANTENNA1 0
#define IWL_LEGACY_SWITCH_ANTENNA2 1
#define IWL_LEGACY_SWITCH_SISO 2
#define IWL_LEGACY_SWITCH_MIMO2_AB 3
#define IWL_LEGACY_SWITCH_MIMO2_AC 4
#define IWL_LEGACY_SWITCH_MIMO2_BC 5
#define IWL_LEGACY_SWITCH_MIMO3_ABC 6
/* possible actions when in siso mode */
#define IWL_SISO_SWITCH_ANTENNA1 0
#define IWL_SISO_SWITCH_ANTENNA2 1
#define IWL_SISO_SWITCH_MIMO2_AB 2
#define IWL_SISO_SWITCH_MIMO2_AC 3
#define IWL_SISO_SWITCH_MIMO2_BC 4
#define IWL_SISO_SWITCH_GI 5
#define IWL_SISO_SWITCH_MIMO3_ABC 6
/* possible actions when in mimo mode */
#define IWL_MIMO2_SWITCH_ANTENNA1 0
#define IWL_MIMO2_SWITCH_ANTENNA2 1
#define IWL_MIMO2_SWITCH_SISO_A 2
#define IWL_MIMO2_SWITCH_SISO_B 3
#define IWL_MIMO2_SWITCH_SISO_C 4
#define IWL_MIMO2_SWITCH_GI 5
#define IWL_MIMO2_SWITCH_MIMO3_ABC 6
/* possible actions when in mimo3 mode */
#define IWL_MIMO3_SWITCH_ANTENNA1 0
#define IWL_MIMO3_SWITCH_ANTENNA2 1
#define IWL_MIMO3_SWITCH_SISO_A 2
#define IWL_MIMO3_SWITCH_SISO_B 3
#define IWL_MIMO3_SWITCH_SISO_C 4
#define IWL_MIMO3_SWITCH_MIMO2_AB 5
#define IWL_MIMO3_SWITCH_MIMO2_AC 6
#define IWL_MIMO3_SWITCH_MIMO2_BC 7
#define IWL_MIMO3_SWITCH_GI 8
#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI
#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC
/*FIXME:RS:add possible actions for MIMO3*/
#define IWL_ACTION_LIMIT 3 /* # possible actions */
#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */
#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000)
#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100)
#define LINK_QUAL_AGG_DISABLE_START_DEF (3)
#define LINK_QUAL_AGG_DISABLE_START_MAX (255)
#define LINK_QUAL_AGG_DISABLE_START_MIN (0)
#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63)
#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63)
#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0)
#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */
/* load per tid defines for A-MPDU activation */
#define IWL_AGG_TPT_THREHOLD 0
#define IWL_AGG_LOAD_THRESHOLD 10
#define IWL_AGG_ALL_TID 0xff
#define TID_QUEUE_CELL_SPACING 50 /*mS */
#define TID_QUEUE_MAX_SIZE 20
#define TID_ROUND_VALUE 5 /* mS */
#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
enum iwl_table_type {
LQ_NONE,
LQ_G, /* legacy types */
LQ_A,
LQ_SISO, /* high-throughput types */
LQ_MIMO2,
LQ_MIMO3,
LQ_MAX,
};
#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
#define is_siso(tbl) ((tbl) == LQ_SISO)
#define is_mimo2(tbl) ((tbl) == LQ_MIMO2)
#define is_mimo3(tbl) ((tbl) == LQ_MIMO3)
#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl))
#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
#define is_a_band(tbl) ((tbl) == LQ_A)
#define is_g_and(tbl) ((tbl) == LQ_G)
#define IWL_MAX_MCS_DISPLAY_SIZE 12
struct iwl_rate_mcs_info {
char mbps[IWL_MAX_MCS_DISPLAY_SIZE];
char mcs[IWL_MAX_MCS_DISPLAY_SIZE];
};
/**
* struct iwl_rate_scale_data -- tx success history for one rate
*/
struct iwl_rate_scale_data {
u64 data; /* bitmap of successful frames */
s32 success_counter; /* number of frames successful */
s32 success_ratio; /* per-cent * 128 */
s32 counter; /* number of frames attempted */
s32 average_tpt; /* success ratio * expected throughput */
unsigned long stamp;
};
/**
* struct iwl_scale_tbl_info -- tx params and success history for all rates
*
* There are two of these in struct iwl_lq_sta,
* one for "active", and one for "search".
*/
struct iwl_scale_tbl_info {
enum iwl_table_type lq_type;
u8 ant_type;
u8 is_SGI; /* 1 = short guard interval */
u8 is_ht40; /* 1 = 40 MHz channel width */
u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
u8 max_search; /* maximun number of tables we can search */
s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
u32 current_rate; /* rate_n_flags, uCode API format */
struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
};
struct iwl_traffic_load {
unsigned long time_stamp; /* age of the oldest statistics */
u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time
* slice */
u32 total; /* total num of packets during the
* last TID_MAX_TIME_DIFF */
u8 queue_count; /* number of queues that has
* been used since the last cleanup */
u8 head; /* start of the circular buffer */
};
/**
* struct iwl_lq_sta -- driver's rate scaling private structure
*
* Pointer to this gets passed back and forth between driver and mac80211.
*/
struct iwl_lq_sta {
u8 active_tbl; /* index of active table, range 0-1 */
u8 enable_counter; /* indicates HT mode */
u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */
u8 search_better_tbl; /* 1: currently trying alternate mode */
s32 last_tpt;
/* The following determine when to search for a new mode */
u32 table_count_limit;
u32 max_failure_limit; /* # failed frames before new search */
u32 max_success_limit; /* # successful frames before new search */
u32 table_count;
u32 total_failed; /* total failed frames, any/all rates */
u32 total_success; /* total successful frames, any/all rates */
u64 flush_timer; /* time staying in mode before new search */
u8 action_counter; /* # mode-switch actions tried */
u8 is_green;
enum ieee80211_band band;
/* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
u32 supp_rates;
u16 active_legacy_rate;
u16 active_siso_rate;
u16 active_mimo2_rate;
u16 active_mimo3_rate;
s8 max_rate_idx; /* Max rate set by user */
u8 missed_rate_counter;
struct iwl_lq_cmd lq;
struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
struct iwl_traffic_load load[IWL_MAX_TID_COUNT];
u8 tx_agg_tid_en;
#ifdef CONFIG_MAC80211_DEBUGFS
struct dentry *rs_sta_dbgfs_scale_table_file;
struct dentry *rs_sta_dbgfs_stats_table_file;
struct dentry *rs_sta_dbgfs_rate_scale_data_file;
struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
u32 dbg_fixed_rate;
#endif
struct iwl_mvm *drv;
/* used to be in sta_info */
int last_txrate_idx;
/* last tx rate_n_flags */
u32 last_rate_n_flags;
/* packets destined for this STA are aggregated */
u8 is_agg;
/* BT traffic this sta was last updated in */
u8 last_bt_traffic;
};
static inline u8 num_of_ant(u8 mask)
{
return !!((mask) & ANT_A) +
!!((mask) & ANT_B) +
!!((mask) & ANT_C);
}
/* Initialize station's rate scaling information after adding station */
extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum ieee80211_band band);
/**
* iwl_rate_control_register - Register the rate control algorithm callbacks
*
* Since the rate control algorithm is hardware specific, there is no need
* or reason to place it as a stand alone module. The driver can call
* iwl_rate_control_register in order to register the rate control callbacks
* with the mac80211 subsystem. This should be performed prior to calling
* ieee80211_register_hw
*
*/
extern int iwl_mvm_rate_control_register(void);
/**
* iwl_rate_control_unregister - Unregister the rate control callbacks
*
* This should be called after calling ieee80211_unregister_hw, but before
* the driver is unloaded.
*/
extern void iwl_mvm_rate_control_unregister(void);
#endif /* __rs__ */

View File

@@ -0,0 +1,365 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "iwl-trans.h"
#include "mvm.h"
#include "fw-api.h"
/*
* iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
*
* Copies the phy information in mvm->last_phy_info, it will be used when the
* actual data will come from the fw in the next packet.
*/
int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
mvm->ampdu_ref++;
return 0;
}
/*
* iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211
*
* Adds the rxb to a new skb and give it to mac80211
*/
static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
struct ieee80211_hdr *hdr, u16 len,
u32 ampdu_status,
struct iwl_rx_cmd_buffer *rxb,
struct ieee80211_rx_status *stats)
{
struct sk_buff *skb;
unsigned int hdrlen, fraglen;
/* Dont use dev_alloc_skb(), we'll have enough headroom once
* ieee80211_hdr pulled.
*/
skb = alloc_skb(128, GFP_ATOMIC);
if (!skb) {
IWL_ERR(mvm, "alloc_skb failed\n");
return;
}
/* If frame is small enough to fit in skb->head, pull it completely.
* If not, only pull ieee80211_hdr so that splice() or TCP coalesce
* are more efficient.
*/
hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
fraglen = len - hdrlen;
if (fraglen) {
int offset = (void *)hdr + hdrlen -
rxb_addr(rxb) + rxb_offset(rxb);
skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
fraglen, rxb->truesize);
}
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
ieee80211_rx_ni(mvm->hw, skb);
}
/*
* iwl_mvm_calc_rssi - calculate the rssi in dBm
* @phy_info: the phy information for the coming packet
*/
static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
struct iwl_rx_phy_info *phy_info)
{
int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
int rssi_all_band_a, rssi_all_band_b;
u32 agc_a, agc_b, max_agc;
u32 val;
/* Find max rssi among 2 possible receivers.
* These values are measured by the Digital Signal Processor (DSP).
* They should stay fairly constant even as the signal strength varies,
* if the radio's Automatic Gain Control (AGC) is working right.
* AGC value (see below) will provide the "interesting" info.
*/
val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS;
agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS;
max_agc = max_t(u32, agc_a, agc_b);
val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
rssi_all_band_a = (val & IWL_OFDM_RSSI_ALLBAND_A_MSK) >>
IWL_OFDM_RSSI_ALLBAND_A_POS;
rssi_all_band_b = (val & IWL_OFDM_RSSI_ALLBAND_B_MSK) >>
IWL_OFDM_RSSI_ALLBAND_B_POS;
/*
* dBm = rssi dB - agc dB - constant.
* Higher AGC (higher radio gain) means lower signal.
*/
rssi_a_dbm = rssi_a - IWL_RSSI_OFFSET - agc_a;
rssi_b_dbm = rssi_b - IWL_RSSI_OFFSET - agc_b;
max_rssi_dbm = max_t(int, rssi_a_dbm, rssi_b_dbm);
IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n",
rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b);
return max_rssi_dbm;
}
/*
* iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format
* @mvm: the mvm object
* @hdr: 80211 header
* @stats: status in mac80211's format
* @rx_pkt_status: status coming from fw
*
* returns non 0 value if the packet should be dropped
*/
static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
struct ieee80211_hdr *hdr,
struct ieee80211_rx_status *stats,
u32 rx_pkt_status)
{
if (!ieee80211_has_protected(hdr->frame_control) ||
(rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
RX_MPDU_RES_STATUS_SEC_NO_ENC)
return 0;
/* packet was encrypted with unknown alg */
if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
RX_MPDU_RES_STATUS_SEC_ENC_ERR)
return 0;
switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) {
case RX_MPDU_RES_STATUS_SEC_CCM_ENC:
/* alg is CCM: check MIC only */
if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
return -1;
stats->flag |= RX_FLAG_DECRYPTED;
IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
return 0;
case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
/* Don't drop the frame and decrypt it in SW */
if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
return 0;
/* fall through if TTAK OK */
case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK))
return -1;
stats->flag |= RX_FLAG_DECRYPTED;
return 0;
default:
IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
}
return 0;
}
/*
* iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler
*
* Handles the actual data of the Rx packet from the fw
*/
int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct ieee80211_hdr *hdr;
struct ieee80211_rx_status rx_status = {};
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_rx_phy_info *phy_info;
struct iwl_rx_mpdu_res_start *rx_res;
u32 len;
u32 ampdu_status;
u32 rate_n_flags;
u32 rx_pkt_status;
phy_info = &mvm->last_phy_info;
rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
len = le16_to_cpu(rx_res->byte_count);
rx_pkt_status = le32_to_cpup((__le32 *)
(pkt->data + sizeof(*rx_res) + len));
memset(&rx_status, 0, sizeof(rx_status));
/*
* drop the packet if it has failed being decrypted by HW
*/
if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
rx_pkt_status);
return 0;
}
if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
phy_info->cfg_phy_cnt);
return 0;
}
if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
!(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
return 0;
}
/* This will be used in several places later */
rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
/* rx_status carries information about the packet to mac80211 */
rx_status.mactime = le64_to_cpu(phy_info->timestamp);
rx_status.device_timestamp = le32_to_cpu(phy_info->system_timestamp);
rx_status.band =
(phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
rx_status.freq =
ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
rx_status.band);
/*
* TSF as indicated by the fw is at INA time, but mac80211 expects the
* TSF at the beginning of the MPDU.
*/
/*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
/* Find max signal strength (dBm) among 3 antenna/receiver chains */
rx_status.signal = iwl_mvm_calc_rssi(mvm, phy_info);
IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
(unsigned long long)rx_status.mactime);
/*
* "antenna number"
*
* It seems that the antenna field in the phy flags value
* is actually a bit field. This is undefined by radiotap,
* it wants an actual antenna number but I always get "7"
* for most legacy frames I receive indicating that the
* same frame was received on all three RX chains.
*
* I think this field should be removed in favor of a
* new 802.11n radiotap field "RX chains" that is defined
* as a bitmask.
*/
rx_status.antenna = (le16_to_cpu(phy_info->phy_flags) &
RX_RES_PHY_FLAGS_ANTENNA)
>> RX_RES_PHY_FLAGS_ANTENNA_POS;
/* set the preamble flag if appropriate */
if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
rx_status.flag |= RX_FLAG_SHORTPRE;
if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
/*
* We know which subframes of an A-MPDU belong
* together since we get a single PHY response
* from the firmware for all of them
*/
rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
rx_status.ampdu_reference = mvm->ampdu_ref;
}
/* Set up the HT phy flags */
switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
case RATE_MCS_CHAN_WIDTH_20:
break;
case RATE_MCS_CHAN_WIDTH_40:
rx_status.flag |= RX_FLAG_40MHZ;
break;
case RATE_MCS_CHAN_WIDTH_80:
rx_status.flag |= RX_FLAG_80MHZ;
break;
case RATE_MCS_CHAN_WIDTH_160:
rx_status.flag |= RX_FLAG_160MHZ;
break;
}
if (rate_n_flags & RATE_MCS_SGI_MSK)
rx_status.flag |= RX_FLAG_SHORT_GI;
if (rate_n_flags & RATE_HT_MCS_GF_MSK)
rx_status.flag |= RX_FLAG_HT_GF;
if (rate_n_flags & RATE_MCS_HT_MSK) {
rx_status.flag |= RX_FLAG_HT;
rx_status.rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
} else if (rate_n_flags & RATE_MCS_VHT_MSK) {
rx_status.vht_nss =
((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
RATE_VHT_MCS_NSS_POS) + 1;
rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
rx_status.flag |= RX_FLAG_VHT;
} else {
rx_status.rate_idx =
iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
rx_status.band);
}
iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status,
rxb, &rx_status);
return 0;
}

View File

@@ -0,0 +1,441 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/etherdevice.h>
#include <net/mac80211.h>
#include "mvm.h"
#include "iwl-eeprom-parse.h"
#include "fw-api-scan.h"
#define IWL_PLCP_QUIET_THRESH 1
#define IWL_ACTIVE_QUIET_TIME 10
static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
{
u16 rx_chain;
u8 rx_ant = iwl_fw_valid_rx_ant(mvm->fw);
rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS;
return cpu_to_le16(rx_chain);
}
static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif)
{
if (vif->bss_conf.assoc)
return cpu_to_le32(200 * 1024);
else
return 0;
}
static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif)
{
if (vif->bss_conf.assoc)
return cpu_to_le32(vif->bss_conf.beacon_int);
else
return 0;
}
static inline __le32
iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req)
{
if (req->channels[0]->band == IEEE80211_BAND_2GHZ)
return cpu_to_le32(PHY_BAND_24);
else
return cpu_to_le32(PHY_BAND_5);
}
static inline __le32
iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
bool no_cck)
{
u32 tx_ant;
mvm->scan_last_antenna_idx =
iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
mvm->scan_last_antenna_idx);
tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
if (band == IEEE80211_BAND_2GHZ && !no_cck)
return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK |
tx_ant);
else
return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant);
}
/*
* We insert the SSIDs in an inverted order, because the FW will
* invert it back. The most prioritized SSID, which is first in the
* request list, is not copied here, but inserted directly to the probe
* request.
*/
static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
struct cfg80211_scan_request *req)
{
int fw_idx, req_idx;
for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx > 0;
req_idx--, fw_idx++) {
cmd->direct_scan[fw_idx].id = WLAN_EID_SSID;
cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len;
memcpy(cmd->direct_scan[fw_idx].ssid,
req->ssids[req_idx].ssid,
req->ssids[req_idx].ssid_len);
}
}
/*
* If req->n_ssids > 0, it means we should do an active scan.
* In case of active scan w/o directed scan, we receive a zero-length SSID
* just to notify that this scan is active and not passive.
* In order to notify the FW of the number of SSIDs we wish to scan (including
* the zero-length one), we need to set the corresponding bits in chan->type,
* one for each SSID, and set the active bit (first).
*/
static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids)
{
if (band == IEEE80211_BAND_2GHZ)
return 30 + 3 * (n_ssids + 1);
return 20 + 2 * (n_ssids + 1);
}
static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band)
{
return band == IEEE80211_BAND_2GHZ ? 100 + 20 : 100 + 10;
}
static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
struct cfg80211_scan_request *req)
{
u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band);
u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band,
req->n_ssids);
struct iwl_scan_channel *chan = (struct iwl_scan_channel *)
(cmd->data + le16_to_cpu(cmd->tx_cmd.len));
int i;
for (i = 0; i < cmd->channel_count; i++) {
chan->channel = cpu_to_le16(req->channels[i]->hw_value);
chan->type = cpu_to_le32(BIT(req->n_ssids) - 1);
if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN)
chan->type &= cpu_to_le32(~SCAN_CHANNEL_TYPE_ACTIVE);
chan->active_dwell = cpu_to_le16(active_dwell);
chan->passive_dwell = cpu_to_le16(passive_dwell);
chan->iteration_count = cpu_to_le16(1);
chan++;
}
}
/*
* Fill in probe request with the following parameters:
* TA is our vif HW address, which mac80211 ensures we have.
* Packet is broadcasted, so this is both SA and DA.
* The probe request IE is made out of two: first comes the most prioritized
* SSID if a directed scan is requested. Second comes whatever extra
* information was given to us as the scan request IE.
*/
static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
int n_ssids, const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len,
int left)
{
int len = 0;
u8 *pos = NULL;
/* Make sure there is enough space for the probe request,
* two mandatory IEs and the data */
left -= 24;
if (left < 0)
return 0;
frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
eth_broadcast_addr(frame->da);
memcpy(frame->sa, ta, ETH_ALEN);
eth_broadcast_addr(frame->bssid);
frame->seq_ctrl = 0;
len += 24;
/* for passive scans, no need to fill anything */
if (n_ssids == 0)
return (u16)len;
/* points to the payload of the request */
pos = &frame->u.probe_req.variable[0];
/* fill in our SSID IE */
left -= ssid_len + 2;
if (left < 0)
return 0;
*pos++ = WLAN_EID_SSID;
*pos++ = ssid_len;
if (ssid && ssid_len) { /* ssid_len may be == 0 even if ssid is valid */
memcpy(pos, ssid, ssid_len);
pos += ssid_len;
}
len += ssid_len + 2;
if (WARN_ON(left < ie_len))
return len;
if (ie && ie_len) {
memcpy(pos, ie, ie_len);
len += ie_len;
}
return (u16)len;
}
int iwl_mvm_scan_request(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
{
struct iwl_host_cmd hcmd = {
.id = SCAN_REQUEST_CMD,
.len = { 0, },
.data = { mvm->scan_cmd, },
.flags = CMD_SYNC,
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
struct iwl_scan_cmd *cmd = mvm->scan_cmd;
int ret;
u32 status;
int ssid_len = 0;
u8 *ssid = NULL;
lockdep_assert_held(&mvm->mutex);
BUG_ON(mvm->scan_cmd == NULL);
IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n");
mvm->scan_status = IWL_MVM_SCAN_OS;
memset(cmd, 0, sizeof(struct iwl_scan_cmd) +
mvm->fw->ucode_capa.max_probe_length +
(MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)));
cmd->channel_count = (u8)req->n_channels;
cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm);
cmd->max_out_time = iwl_mvm_scan_max_out_time(vif);
cmd->suspend_time = iwl_mvm_scan_suspend_time(vif);
cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req);
cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
MAC_FILTER_IN_BEACON);
if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
cmd->type = cpu_to_le32(SCAN_TYPE_DISCOVERY_FORCED);
else
cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
/*
* TODO: This is a WA due to a bug in the FW AUX framework that does not
* properly handle time events that fail to be scheduled
*/
cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
cmd->repeats = cpu_to_le32(1);
/*
* If the user asked for passive scan, don't change to active scan if
* you see any activity on the channel - remain passive.
*/
if (req->n_ssids > 0) {
cmd->passive2active = cpu_to_le16(1);
ssid = req->ssids[0].ssid;
ssid_len = req->ssids[0].ssid_len;
} else {
cmd->passive2active = 0;
}
iwl_mvm_scan_fill_ssids(cmd, req);
cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
cmd->tx_cmd.rate_n_flags =
iwl_mvm_scan_rate_n_flags(mvm, req->channels[0]->band,
req->no_cck);
cmd->tx_cmd.len =
cpu_to_le16(iwl_mvm_fill_probe_req(
(struct ieee80211_mgmt *)cmd->data,
vif->addr,
req->n_ssids, ssid, ssid_len,
req->ie, req->ie_len,
mvm->fw->ucode_capa.max_probe_length));
iwl_mvm_scan_fill_channels(cmd, req);
cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) +
le16_to_cpu(cmd->tx_cmd.len) +
(cmd->channel_count * sizeof(struct iwl_scan_channel)));
hcmd.len[0] = le16_to_cpu(cmd->len);
status = SCAN_RESPONSE_OK;
ret = iwl_mvm_send_cmd_status(mvm, &hcmd, &status);
if (!ret && status == SCAN_RESPONSE_OK) {
IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
} else {
/*
* If the scan failed, it usually means that the FW was unable
* to allocate the time events. Warn on it, but maybe we
* should try to send the command again with different params.
*/
IWL_ERR(mvm, "Scan failed! status 0x%x ret %d\n",
status, ret);
mvm->scan_status = IWL_MVM_SCAN_NONE;
ret = -EIO;
}
return ret;
}
int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_cmd_response *resp = (void *)pkt->data;
IWL_DEBUG_SCAN(mvm, "Scan response received. status 0x%x\n",
le32_to_cpu(resp->status));
return 0;
}
int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scan_complete_notif *notif = (void *)pkt->data;
IWL_DEBUG_SCAN(mvm, "Scan complete: status=0x%x scanned channels=%d\n",
notif->status, notif->scanned_channels);
mvm->scan_status = IWL_MVM_SCAN_NONE;
ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK);
return 0;
}
static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
struct iwl_rx_packet *pkt, void *data)
{
struct iwl_mvm *mvm =
container_of(notif_wait, struct iwl_mvm, notif_wait);
struct iwl_scan_complete_notif *notif;
u32 *resp;
switch (pkt->hdr.cmd) {
case SCAN_ABORT_CMD:
resp = (void *)pkt->data;
if (*resp == CAN_ABORT_STATUS) {
IWL_DEBUG_SCAN(mvm,
"Scan can be aborted, wait until completion\n");
return false;
}
IWL_DEBUG_SCAN(mvm, "Scan cannot be aborted, exit now: %d\n",
*resp);
return true;
case SCAN_COMPLETE_NOTIFICATION:
notif = (void *)pkt->data;
IWL_DEBUG_SCAN(mvm, "Scan aborted: status 0x%x\n",
notif->status);
return true;
default:
WARN_ON(1);
return false;
};
}
void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
{
struct iwl_notification_wait wait_scan_abort;
static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD,
SCAN_COMPLETE_NOTIFICATION };
int ret;
iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
scan_abort_notif,
ARRAY_SIZE(scan_abort_notif),
iwl_mvm_scan_abort_notif, NULL);
ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
if (ret) {
IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
goto out_remove_notif;
}
ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, 1 * HZ);
if (ret)
IWL_ERR(mvm, "%s - failed on timeout\n", __func__);
return;
out_remove_notif:
iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,376 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __sta_h__
#define __sta_h__
#include <linux/spinlock.h>
#include <net/mac80211.h>
#include <linux/wait.h>
#include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */
#include "fw-api.h" /* IWL_MVM_STATION_COUNT */
#include "rs.h"
struct iwl_mvm;
/**
* DOC: station table - introduction
*
* The station table is a list of data structure that reprensent the stations.
* In STA/P2P client mode, the driver will hold one station for the AP/ GO.
* In GO/AP mode, the driver will have as many stations as associated clients.
* All these stations are reflected in the fw's station table. The driver
* keeps the fw's station table up to date with the ADD_STA command. Stations
* can be removed by the REMOVE_STA command.
*
* All the data related to a station is held in the structure %iwl_mvm_sta
* which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area.
* This data includes the index of the station in the fw, per tid information
* (sequence numbers, Block-ack state machine, etc...). The stations are
* created and deleted by the %sta_state callback from %ieee80211_ops.
*
* The driver holds a map: %fw_id_to_mac_id that allows to fetch a
* %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw
* station index. That way, the driver is able to get the tid related data in
* O(1) in time sensitive paths (Tx / Tx response / BA notification). These
* paths are triggered by the fw, and the driver needs to get a pointer to the
* %ieee80211 structure. This map helps to get that pointer quickly.
*/
/**
* DOC: station table - locking
*
* As stated before, the station is created / deleted by mac80211's %sta_state
* callback from %ieee80211_ops which can sleep. The next paragraph explains
* the locking of a single stations, the next ones relates to the station
* table.
*
* The station holds the sequence number per tid. So this data needs to be
* accessed in the Tx path (which is softIRQ). It also holds the Block-Ack
* information (the state machine / and the logic that checks if the queues
* were drained), so it also needs to be accessible from the Tx response flow.
* In short, the station needs to be access from sleepable context as well as
* from tasklets, so the station itself needs a spinlock.
*
* The writers of %fw_id_to_mac_id map are serialized by the global mutex of
* the mvm op_mode. This is possible since %sta_state can sleep.
* The pointers in this map are RCU protected, hence we won't replace the
* station while we have Tx / Tx response / BA notification running.
*
* If a station is deleted while it still has packets in its A-MPDU queues,
* then the reclaim flow will notice that there is no station in the map for
* sta_id and it will dump the responses.
*/
/**
* DOC: station table - internal stations
*
* The FW needs a few internal stations that are not reflected in
* mac80211, such as broadcast station in AP / GO mode, or AUX sta for
* scanning and P2P device (during the GO negotiation).
* For these kind of stations we have %iwl_mvm_int_sta struct which holds the
* data relevant for them from both %iwl_mvm_sta and %ieee80211_sta.
* Usually the data for these stations is static, so no locking is required,
* and no TID data as this is also not needed.
* One thing to note, is that these stations have an ID in the fw, but not
* in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id
* we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of
* pointers from this mapping need to check that the value is not error
* or NULL.
*
* Currently there is only one auxiliary station for scanning, initialized
* on init.
*/
/**
* DOC: station table - AP Station in STA mode
*
* %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
* %ap_sta_id. To get the point to the coresponsding %ieee80211_sta,
* &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
* the AP station from the fw before setting the MAC context as unassociated.
* Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
* removed by mac80211, but the station won't be removed in the fw until the
* VIF is set as unassociated. Then, %ap_sta_id will be invalidated.
*/
/**
* DOC: station table - Drain vs. Flush
*
* Flush means that all the frames in the SCD queue are dumped regardless the
* station to which they were sent. We do that when we disassociate and before
* we remove the STA of the AP. The flush can be done synchronously against the
* fw.
* Drain means that the fw will drop all the frames sent to a specific station.
* This is useful when a client (if we are IBSS / GO or AP) disassociates. In
* that case, we need to drain all the frames for that client from the AC queues
* that are shared with the other clients. Only then, we can remove the STA in
* the fw. In order to do so, we track the non-AMPDU packets for each station.
* If mac80211 removes a STA and if it still has non-AMPDU packets pending in
* the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all
* the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped
* (we know about it with its Tx response), we remove the station in fw and set
* it as %NULL in %fw_id_to_mac_id: this is the purpose of
* %iwl_mvm_sta_drained_wk.
*/
/**
* DOC: station table - fw restart
*
* When the fw asserts, or we have any other issue that requires to reset the
* driver, we require mac80211 to reconfigure the driver. Since the private
* data of the stations is embed in mac80211's %ieee80211_sta, that data will
* not be zeroed and needs to be reinitialized manually.
* %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us
* that we must not allocate a new sta_id but reuse the previous one. This
* means that the stations being re-added after the reset will have the same
* place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id
* map, since the stations aren't in the fw any more. Internal stations that
* are not added by mac80211 will be re-added in the init flow that is called
* after the restart: mac80211 call's %iwl_mvm_mac_start which calls to
* %iwl_mvm_up.
*/
/**
* DOC: AP mode - PS
*
* When a station is asleep, the fw will set it as "asleep". All the
* non-aggregation frames to that station will be dropped by the fw
* (%TX_STATUS_FAIL_DEST_PS failure code).
* AMPDUs are in a separate queue that is stopped by the fw. We just need to
* let mac80211 know how many frames we have in these queues so that it can
* properly handle trigger frames.
* When the a trigger frame is received, mac80211 tells the driver to send
* frames from the AMPDU queues or AC queue depending on which queue are
* delivery-enabled and what TID has frames to transmit (Note that mac80211 has
* all the knowledege since all the non-agg frames are buffered / filtered, and
* the driver tells mac80211 about agg frames). The driver needs to tell the fw
* to let frames out even if the station is asleep. This is done by
* %iwl_mvm_sta_modify_sleep_tx_count.
* When we receive a frame from that station with PM bit unset, the
* driver needs to let the fw know that this station isn't alseep any more.
* This is done by %iwl_mvm_sta_modify_ps_wake.
*
* TODO - EOSP handling
*/
/**
* enum iwl_mvm_agg_state
*
* The state machine of the BA agreement establishment / tear down.
* These states relate to a specific RA / TID.
*
* @IWL_AGG_OFF: aggregation is not used
* @IWL_AGG_STARTING: aggregation are starting (between start and oper)
* @IWL_AGG_ON: aggregation session is up
* @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
* HW queue to be empty from packets for this RA /TID.
* @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
* HW queue to be empty from packets for this RA /TID.
*/
enum iwl_mvm_agg_state {
IWL_AGG_OFF = 0,
IWL_AGG_STARTING,
IWL_AGG_ON,
IWL_EMPTYING_HW_QUEUE_ADDBA,
IWL_EMPTYING_HW_QUEUE_DELBA,
};
/**
* struct iwl_mvm_tid_data - holds the states for each RA / TID
* @seq_number: the next WiFi sequence number to use
* @next_reclaimed: the WiFi sequence number of the next packet to be acked.
* This is basically (last acked packet++).
* @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
* Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
* @state: state of the BA agreement establishment / tear down.
* @txq_id: Tx queue used by the BA session
* @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
* the first packet to be sent in legacy HW queue in Tx AGG stop flow.
* Basically when next_reclaimed reaches ssn, we can tell mac80211 that
* we are ready to finish the Tx AGG stop / start flow.
* @wait_for_ba: Expect block-ack before next Tx reply
*/
struct iwl_mvm_tid_data {
u16 seq_number;
u16 next_reclaimed;
/* The rest is Tx AGG related */
u32 rate_n_flags;
enum iwl_mvm_agg_state state;
u16 txq_id;
u16 ssn;
bool wait_for_ba;
};
/**
* struct iwl_mvm_sta - representation of a station in the driver
* @sta_id: the index of the station in the fw (will be replaced by id_n_color)
* @tfd_queue_msk: the tfd queues used by the station
* @mac_id_n_color: the MAC context this station is linked to
* @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
* tid.
* @max_agg_bufsize: the maximal size of the AGG buffer for this station
* @bt_reduced_txpower: is reduced tx power enabled for this station
* @lock: lock to protect the whole struct. Since %tid_data is access from Tx
* and from Tx response flow, it needs a spinlock.
* @tid_data: per tid data. Look at %iwl_mvm_tid_data.
*
* When mac80211 creates a station it reserves some space (hw->sta_data_size)
* in the structure for use by driver. This structure is placed in that
* space.
*
*/
struct iwl_mvm_sta {
u32 sta_id;
u32 tfd_queue_msk;
u32 mac_id_n_color;
u16 tid_disable_agg;
u8 max_agg_bufsize;
bool bt_reduced_txpower;
spinlock_t lock;
struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
struct iwl_lq_sta lq_sta;
struct ieee80211_vif *vif;
#ifdef CONFIG_PM_SLEEP
u16 last_seq_ctl;
#endif
};
/**
* struct iwl_mvm_int_sta - representation of an internal station (auxiliary or
* broadcast)
* @sta_id: the index of the station in the fw (will be replaced by id_n_color)
* @tfd_queue_msk: the tfd queues used by the station
*/
struct iwl_mvm_int_sta {
u32 sta_id;
u32 tfd_queue_msk;
};
int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
bool update);
int iwl_mvm_add_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int iwl_mvm_update_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u8 sta_id);
int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key,
bool have_key_offset);
int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf);
void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_key_conf *keyconf,
struct ieee80211_sta *sta, u32 iv32,
u16 *phase1key);
/* AMPDU */
int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, u16 ssn, bool start);
int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn);
int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u8 buf_size);
int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid);
int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid);
int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
u32 qmask);
void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm,
struct iwl_mvm_int_sta *sta);
int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct iwl_mvm_int_sta *bsta);
int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm,
struct iwl_mvm_int_sta *bsta);
int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct iwl_mvm_int_sta *bsta);
int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *bsta);
void iwl_mvm_sta_drained_wk(struct work_struct *wk);
void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum ieee80211_frame_release_type reason,
u16 cnt);
int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool drain);
#endif /* __sta_h__ */

View File

@@ -0,0 +1,527 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/jiffies.h>
#include <net/mac80211.h>
#include "iwl-notif-wait.h"
#include "iwl-trans.h"
#include "fw-api.h"
#include "time-event.h"
#include "mvm.h"
#include "iwl-io.h"
#include "iwl-prph.h"
/* A TimeUnit is 1024 microsecond */
#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies((_tu) * 1024))
#define MSEC_TO_TU(_msec) (_msec*1000/1024)
/*
* For the high priority TE use a time event type that has similar priority to
* the FW's action scan priority.
*/
#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE
#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC
void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
struct iwl_mvm_time_event_data *te_data)
{
lockdep_assert_held(&mvm->time_event_lock);
if (te_data->id == TE_MAX)
return;
list_del(&te_data->list);
te_data->running = false;
te_data->uid = 0;
te_data->id = TE_MAX;
te_data->vif = NULL;
}
void iwl_mvm_roc_done_wk(struct work_struct *wk)
{
struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
synchronize_net();
/*
* Flush the offchannel queue -- this is called when the time
* event finishes or is cancelled, so that frames queued for it
* won't get stuck on the queue and be transmitted in the next
* time event.
* We have to send the command asynchronously since this cannot
* be under the mutex for locking reasons, but that's not an
* issue as it will have to complete before the next command is
* executed, and a new time event means a new command.
*/
iwl_mvm_flush_tx_path(mvm, BIT(IWL_MVM_OFFCHANNEL_QUEUE), false);
}
static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
{
/*
* First, clear the ROC_RUNNING status bit. This will cause the TX
* path to drop offchannel transmissions. That would also be done
* by mac80211, but it is racy, in particular in the case that the
* time event actually completed in the firmware (which is handled
* in iwl_mvm_te_handle_notif).
*/
clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
/*
* Of course, our status bit is just as racy as mac80211, so in
* addition, fire off the work struct which will drop all frames
* from the hardware queues that made it through the race. First
* it will of course synchronize the TX path to make sure that
* any *new* TX will be rejected.
*/
schedule_work(&mvm->roc_done_wk);
}
/*
* Handles a FW notification for an event that is known to the driver.
*
* @mvm: the mvm component
* @te_data: the time event data
* @notif: the notification data corresponding the time event data.
*/
static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
struct iwl_mvm_time_event_data *te_data,
struct iwl_time_event_notif *notif)
{
lockdep_assert_held(&mvm->time_event_lock);
IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
le32_to_cpu(notif->unique_id),
le32_to_cpu(notif->action));
/*
* The FW sends the start/end time event notifications even for events
* that it fails to schedule. This is indicated in the status field of
* the notification. This happens in cases that the scheduler cannot
* find a schedule that can handle the event (for example requesting a
* P2P Device discoveribility, while there are other higher priority
* events in the system).
*/
WARN_ONCE(!le32_to_cpu(notif->status),
"Failed to schedule time event\n");
if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) {
IWL_DEBUG_TE(mvm,
"TE ended - current time %lu, estimated end %lu\n",
jiffies, te_data->end_jiffies);
if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
ieee80211_remain_on_channel_expired(mvm->hw);
iwl_mvm_roc_finished(mvm);
}
/*
* By now, we should have finished association
* and know the dtim period.
*/
if (te_data->vif->type == NL80211_IFTYPE_STATION &&
(!te_data->vif->bss_conf.assoc ||
!te_data->vif->bss_conf.dtim_period)) {
IWL_ERR(mvm,
"No assocation and the time event is over already...\n");
ieee80211_connection_loss(te_data->vif);
}
iwl_mvm_te_clear_data(mvm, te_data);
} else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) {
te_data->running = true;
te_data->end_jiffies = jiffies +
TU_TO_JIFFIES(te_data->duration);
if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
ieee80211_ready_on_channel(mvm->hw);
}
} else {
IWL_WARN(mvm, "Got TE with unknown action\n");
}
}
/*
* The Rx handler for time event notifications
*/
int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_time_event_notif *notif = (void *)pkt->data;
struct iwl_mvm_time_event_data *te_data, *tmp;
IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
le32_to_cpu(notif->unique_id),
le32_to_cpu(notif->action));
spin_lock_bh(&mvm->time_event_lock);
list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
if (le32_to_cpu(notif->unique_id) == te_data->uid)
iwl_mvm_te_handle_notif(mvm, te_data, notif);
}
spin_unlock_bh(&mvm->time_event_lock);
return 0;
}
static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
struct iwl_rx_packet *pkt, void *data)
{
struct iwl_mvm *mvm =
container_of(notif_wait, struct iwl_mvm, notif_wait);
struct iwl_mvm_time_event_data *te_data = data;
struct iwl_time_event_resp *resp;
int resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD))
return true;
if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) {
IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n");
return true;
}
resp = (void *)pkt->data;
/* we should never get a response to another TIME_EVENT_CMD here */
if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id))
return false;
te_data->uid = le32_to_cpu(resp->unique_id);
IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
te_data->uid);
return true;
}
static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_mvm_time_event_data *te_data,
struct iwl_time_event_cmd *te_cmd)
{
static const u8 time_event_response[] = { TIME_EVENT_CMD };
struct iwl_notification_wait wait_time_event;
int ret;
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n",
le32_to_cpu(te_cmd->duration));
spin_lock_bh(&mvm->time_event_lock);
if (WARN_ON(te_data->id != TE_MAX)) {
spin_unlock_bh(&mvm->time_event_lock);
return -EIO;
}
te_data->vif = vif;
te_data->duration = le32_to_cpu(te_cmd->duration);
te_data->id = le32_to_cpu(te_cmd->id);
list_add_tail(&te_data->list, &mvm->time_event_list);
spin_unlock_bh(&mvm->time_event_lock);
/*
* Use a notification wait, which really just processes the
* command response and doesn't wait for anything, in order
* to be able to process the response and get the UID inside
* the RX path. Using CMD_WANT_SKB doesn't work because it
* stores the buffer and then wakes up this thread, by which
* time another notification (that the time event started)
* might already be processed unsuccessfully.
*/
iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
time_event_response,
ARRAY_SIZE(time_event_response),
iwl_mvm_time_event_response, te_data);
ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
sizeof(*te_cmd), te_cmd);
if (ret) {
IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
goto out_clear_te;
}
/* No need to wait for anything, so just pass 1 (0 isn't valid) */
ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
/* should never fail */
WARN_ON_ONCE(ret);
if (ret) {
out_clear_te:
spin_lock_bh(&mvm->time_event_lock);
iwl_mvm_te_clear_data(mvm, te_data);
spin_unlock_bh(&mvm->time_event_lock);
}
return ret;
}
void iwl_mvm_protect_session(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 duration, u32 min_duration)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
struct iwl_time_event_cmd time_cmd = {};
lockdep_assert_held(&mvm->mutex);
if (te_data->running &&
time_after(te_data->end_jiffies,
jiffies + TU_TO_JIFFIES(min_duration))) {
IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
jiffies_to_msecs(te_data->end_jiffies - jiffies));
return;
}
if (te_data->running) {
IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
te_data->uid,
jiffies_to_msecs(te_data->end_jiffies - jiffies));
/*
* we don't have enough time
* cancel the current TE and issue a new one
* Of course it would be better to remove the old one only
* when the new one is added, but we don't care if we are off
* channel for a bit. All we need to do, is not to return
* before we actually begin to be on the channel.
*/
iwl_mvm_stop_session_protection(mvm, vif);
}
time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
time_cmd.id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
time_cmd.apply_time =
cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG));
time_cmd.dep_policy = TE_INDEPENDENT;
time_cmd.is_present = cpu_to_le32(1);
time_cmd.max_frags = cpu_to_le32(TE_FRAG_NONE);
time_cmd.max_delay = cpu_to_le32(500);
/* TODO: why do we need to interval = bi if it is not periodic? */
time_cmd.interval = cpu_to_le32(1);
time_cmd.interval_reciprocal = cpu_to_le32(iwl_mvm_reciprocal(1));
time_cmd.duration = cpu_to_le32(duration);
time_cmd.repeat = cpu_to_le32(1);
time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START |
TE_NOTIF_HOST_EVENT_END);
iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
/*
* Explicit request to remove a time event. The removal of a time event needs to
* be synchronized with the flow of a time event's end notification, which also
* removes the time event from the op mode data structures.
*/
void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
struct iwl_mvm_vif *mvmvif,
struct iwl_mvm_time_event_data *te_data)
{
struct iwl_time_event_cmd time_cmd = {};
u32 id, uid;
int ret;
/*
* It is possible that by the time we got to this point the time
* event was already removed.
*/
spin_lock_bh(&mvm->time_event_lock);
/* Save time event uid before clearing its data */
uid = te_data->uid;
id = te_data->id;
/*
* The clear_data function handles time events that were already removed
*/
iwl_mvm_te_clear_data(mvm, te_data);
spin_unlock_bh(&mvm->time_event_lock);
/*
* It is possible that by the time we try to remove it, the time event
* has already ended and removed. In such a case there is no need to
* send a removal command.
*/
if (id == TE_MAX) {
IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", uid);
return;
}
/* When we remove a TE, the UID is to be set in the id field */
time_cmd.id = cpu_to_le32(uid);
time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
time_cmd.id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
sizeof(time_cmd), &time_cmd);
if (WARN_ON(ret))
return;
}
void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
lockdep_assert_held(&mvm->mutex);
iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
}
int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int duration, enum ieee80211_roc_type type)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
struct iwl_time_event_cmd time_cmd = {};
lockdep_assert_held(&mvm->mutex);
if (te_data->running) {
IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
return -EBUSY;
}
/*
* Flush the done work, just in case it's still pending, so that
* the work it does can complete and we can accept new frames.
*/
flush_work(&mvm->roc_done_wk);
time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
time_cmd.id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
switch (type) {
case IEEE80211_ROC_TYPE_NORMAL:
time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
break;
case IEEE80211_ROC_TYPE_MGMT_TX:
time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
break;
default:
WARN_ONCE(1, "Got an invalid ROC type\n");
return -EINVAL;
}
time_cmd.apply_time = cpu_to_le32(0);
time_cmd.dep_policy = cpu_to_le32(TE_INDEPENDENT);
time_cmd.is_present = cpu_to_le32(1);
time_cmd.interval = cpu_to_le32(1);
/*
* The P2P Device TEs can have lower priority than other events
* that are being scheduled by the driver/fw, and thus it might not be
* scheduled. To improve the chances of it being scheduled, allow them
* to be fragmented, and in addition allow them to be delayed.
*/
time_cmd.max_frags = cpu_to_le32(MSEC_TO_TU(duration)/20);
time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
time_cmd.repeat = cpu_to_le32(1);
time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START |
TE_NOTIF_HOST_EVENT_END);
return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
{
struct iwl_mvm_vif *mvmvif;
struct iwl_mvm_time_event_data *te_data;
lockdep_assert_held(&mvm->mutex);
/*
* Iterate over the list of time events and find the time event that is
* associated with a P2P_DEVICE interface.
* This assumes that a P2P_DEVICE interface can have only a single time
* event at any given time and this time event coresponds to a ROC
* request
*/
mvmvif = NULL;
spin_lock_bh(&mvm->time_event_lock);
list_for_each_entry(te_data, &mvm->time_event_list, list) {
if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
break;
}
}
spin_unlock_bh(&mvm->time_event_lock);
if (!mvmvif) {
IWL_WARN(mvm, "P2P_DEVICE no remain on channel event\n");
return;
}
iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
iwl_mvm_roc_finished(mvm);
}

View File

@@ -0,0 +1,215 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __time_event_h__
#define __time_event_h__
#include "fw-api.h"
#include "mvm.h"
/**
* DOC: Time Events - what is it?
*
* Time Events are a fw feature that allows the driver to control the presence
* of the device on the channel. Since the fw supports multiple channels
* concurrently, the fw may choose to jump to another channel at any time.
* In order to make sure that the fw is on a specific channel at a certain time
* and for a certain duration, the driver needs to issue a time event.
*
* The simplest example is for BSS association. The driver issues a time event,
* waits for it to start, and only then tells mac80211 that we can start the
* association. This way, we make sure that the association will be done
* smoothly and won't be interrupted by channel switch decided within the fw.
*/
/**
* DOC: The flow against the fw
*
* When the driver needs to make sure we are in a certain channel, at a certain
* time and for a certain duration, it sends a Time Event. The flow against the
* fw goes like this:
* 1) Driver sends a TIME_EVENT_CMD to the fw
* 2) Driver gets the response for that command. This response contains the
* Unique ID (UID) of the event.
* 3) The fw sends notification when the event starts.
*
* Of course the API provides various options that allow to cover parameters
* of the flow.
* What is the duration of the event?
* What is the start time of the event?
* Is there an end-time for the event?
* How much can the event be delayed?
* Can the event be split?
* If yes what is the maximal number of chunks?
* etc...
*/
/**
* DOC: Abstraction to the driver
*
* In order to simplify the use of time events to the rest of the driver,
* we abstract the use of time events. This component provides the functions
* needed by the driver.
*/
#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500
#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400
/**
* iwl_mvm_protect_session - start / extend the session protection.
* @mvm: the mvm component
* @vif: the virtual interface for which the session is issued
* @duration: the duration of the session in TU.
* @min_duration: will start a new session if the current session will end
* in less than min_duration.
*
* This function can be used to start a session protection which means that the
* fw will stay on the channel for %duration_ms milliseconds. This function
* will block (sleep) until the session starts. This function can also be used
* to extend a currently running session.
* This function is meant to be used for BSS association for example, where we
* want to make sure that the fw stays on the channel during the association.
*/
void iwl_mvm_protect_session(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 duration, u32 min_duration);
/**
* iwl_mvm_stop_session_protection - cancel the session protection.
* @mvm: the mvm component
* @vif: the virtual interface for which the session is issued
*
* This functions cancels the session protection which is an act of good
* citizenship. If it is not needed any more it should be cancelled because
* the other bindings wait for the medium during that time.
* This funtions doesn't sleep.
*/
void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
/*
* iwl_mvm_rx_time_event_notif - handles %TIME_EVENT_NOTIFICATION.
*/
int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
/**
* iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionlity
* @mvm: the mvm component
* @vif: the virtual interface for which the roc is requested. It is assumed
* that the vif type is NL80211_IFTYPE_P2P_DEVICE
* @duration: the requested duration in millisecond for the fw to be on the
* channel that is bound to the vif.
* @type: the remain on channel request type
*
* This function can be used to issue a remain on channel session,
* which means that the fw will stay in the channel for the request %duration
* milliseconds. The function is async, meaning that it only issues the ROC
* request but does not wait for it to start. Once the FW is ready to serve the
* ROC request, it will issue a notification to the driver that it is on the
* requested channel. Once the FW completes the ROC request it will issue
* another notification to the driver.
*/
int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int duration, enum ieee80211_roc_type type);
/**
* iwl_mvm_stop_p2p_roc - stop remain on channel for p2p device functionlity
* @mvm: the mvm component
*
* This function can be used to cancel an ongoing ROC session.
* The function is async, it will instruct the FW to stop serving the ROC
* session, but will not wait for the actual stopping of the session.
*/
void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm);
/**
* iwl_mvm_remove_time_event - general function to clean up of time event
* @mvm: the mvm component
* @vif: the vif to which the time event belongs
* @te_data: the time event data that corresponds to that time event
*
* This function can be used to cancel a time event regardless its type.
* It is useful for cleaning up time events running before removing an
* interface.
*/
void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
struct iwl_mvm_vif *mvmvif,
struct iwl_mvm_time_event_data *te_data);
/**
* iwl_mvm_te_clear_data - remove time event from list
* @mvm: the mvm component
* @te_data: the time event data to remove
*
* This function is mostly internal, it is made available here only
* for firmware restart purposes.
*/
void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
struct iwl_mvm_time_event_data *te_data);
void iwl_mvm_roc_done_wk(struct work_struct *wk);
#endif /* __time_event_h__ */

View File

@@ -0,0 +1,939 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/ieee80211.h>
#include <linux/etherdevice.h>
#include "iwl-trans.h"
#include "iwl-eeprom-parse.h"
#include "mvm.h"
#include "sta.h"
/*
* Sets most of the Tx cmd's fields
*/
static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
struct iwl_tx_cmd *tx_cmd,
struct ieee80211_tx_info *info, u8 sta_id)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
__le16 fc = hdr->frame_control;
u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags);
u32 len = skb->len + FCS_LEN;
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
tx_flags |= TX_CMD_FLG_ACK;
else
tx_flags &= ~TX_CMD_FLG_ACK;
if (ieee80211_is_probe_resp(fc))
tx_flags |= TX_CMD_FLG_TSF;
else if (ieee80211_is_back_req(fc))
tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
/* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */
if (info->band == IEEE80211_BAND_2GHZ &&
(skb->protocol == cpu_to_be16(ETH_P_PAE) ||
is_multicast_ether_addr(hdr->addr1) ||
ieee80211_is_back_req(fc) ||
ieee80211_is_mgmt(fc)))
tx_flags |= TX_CMD_FLG_BT_DIS;
if (ieee80211_has_morefrags(fc))
tx_flags |= TX_CMD_FLG_MORE_FRAG;
if (ieee80211_is_data_qos(fc)) {
u8 *qc = ieee80211_get_qos_ctl(hdr);
tx_cmd->tid_tspec = qc[0] & 0xf;
tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
} else {
tx_cmd->tid_tspec = IWL_TID_NON_QOS;
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
tx_flags |= TX_CMD_FLG_SEQ_CTL;
else
tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
}
if (ieee80211_is_mgmt(fc)) {
if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
tx_cmd->pm_frame_timeout = cpu_to_le16(3);
else
tx_cmd->pm_frame_timeout = cpu_to_le16(2);
/* The spec allows Action frames in A-MPDU, we don't support
* it
*/
WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
} else {
tx_cmd->pm_frame_timeout = 0;
}
if (info->flags & IEEE80211_TX_CTL_AMPDU)
tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
if (ieee80211_is_data(fc) && len > mvm->rts_threshold &&
!is_multicast_ether_addr(ieee80211_get_DA(hdr)))
tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
tx_cmd->driver_txop = 0;
tx_cmd->tx_flags = cpu_to_le32(tx_flags);
/* Total # bytes to be transmitted */
tx_cmd->len = cpu_to_le16((u16)skb->len);
tx_cmd->next_frame_len = 0;
tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
tx_cmd->sta_id = sta_id;
}
/*
* Sets the fields in the Tx cmd that are rate related
*/
static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
struct iwl_tx_cmd *tx_cmd,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
__le16 fc)
{
u32 rate_flags;
int rate_idx;
u8 rate_plcp;
/* Set retry limit on RTS packets */
tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT;
/* Set retry limit on DATA packets and Probe Responses*/
if (ieee80211_is_probe_resp(fc)) {
tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT;
tx_cmd->rts_retry_limit =
min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit);
} else if (ieee80211_is_back_req(fc)) {
tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT;
} else {
tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY;
}
/*
* for data packets, rate info comes from the table inside he fw. This
* table is controlled by LINK_QUALITY commands
*/
if (ieee80211_is_data(fc) && sta) {
tx_cmd->initial_rate_index = 0;
tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
return;
} else if (ieee80211_is_back_req(fc)) {
tx_cmd->tx_flags |=
cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR);
}
/* HT rate doesn't make sense for a non data frame */
WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS,
"Got an HT rate for a non data frame 0x%x\n",
info->control.rates[0].flags);
rate_idx = info->control.rates[0].idx;
/* if the rate isn't a well known legacy rate, take the lowest one */
if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT_LEGACY)
rate_idx = rate_lowest_index(
&mvm->nvm_data->bands[info->band], sta);
/* For 5 GHZ band, remap mac80211 rate indices into driver indices */
if (info->band == IEEE80211_BAND_5GHZ)
rate_idx += IWL_FIRST_OFDM_RATE;
/* For 2.4 GHZ band, check that there is no need to remap */
BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
/* Get PLCP rate for tx_cmd->rate_n_flags */
rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
mvm->mgmt_last_antenna_idx =
iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
mvm->mgmt_last_antenna_idx);
rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
/* 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 the rate in the TX cmd */
tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags);
}
/*
* Sets the fields in the Tx cmd that are crypto related
*/
static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
struct ieee80211_tx_info *info,
struct iwl_tx_cmd *tx_cmd,
struct sk_buff *skb_frag)
{
struct ieee80211_key_conf *keyconf = info->control.hw_key;
switch (keyconf->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
if (info->flags & IEEE80211_TX_CTL_AMPDU)
tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_CCMP_AGG);
break;
case WLAN_CIPHER_SUITE_TKIP:
tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
break;
case WLAN_CIPHER_SUITE_WEP104:
tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
/* fall through */
case WLAN_CIPHER_SUITE_WEP40:
tx_cmd->sec_ctl |= TX_CMD_SEC_WEP |
((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) &
TX_CMD_SEC_WEP_KEY_IDX_MSK);
memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
break;
default:
IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher);
break;
}
}
/*
* Allocates and sets the Tx cmd the driver data pointers in the skb
*/
static struct iwl_device_cmd *
iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_sta *sta, u8 sta_id)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_device_cmd *dev_cmd;
struct iwl_tx_cmd *tx_cmd;
dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans);
if (unlikely(!dev_cmd))
return NULL;
memset(dev_cmd, 0, sizeof(*dev_cmd));
tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
if (info->control.hw_key)
iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb);
iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id);
iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control);
memset(&info->status, 0, sizeof(info->status));
info->driver_data[0] = NULL;
info->driver_data[1] = dev_cmd;
return dev_cmd;
}
int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_device_cmd *dev_cmd;
struct iwl_tx_cmd *tx_cmd;
u8 sta_id;
if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU))
return -1;
if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM &&
(!info->control.vif ||
info->hw_queue != info->control.vif->cab_queue)))
return -1;
/*
* If the interface on which frame is sent is the P2P_DEVICE
* or an AP/GO interface use the broadcast station associated
* with it; otherwise use the AUX station.
*/
if (info->control.vif &&
(info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE ||
info->control.vif->type == NL80211_IFTYPE_AP)) {
struct iwl_mvm_vif *mvmvif =
iwl_mvm_vif_from_mac80211(info->control.vif);
sta_id = mvmvif->bcast_sta.sta_id;
} else {
sta_id = mvm->aux_sta.sta_id;
}
IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue);
dev_cmd = iwl_mvm_set_tx_params(mvm, skb, NULL, sta_id);
if (!dev_cmd)
return -1;
/* From now on, we cannot access info->control */
tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
/* Copy MAC header from skb into command buffer */
memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control));
if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) {
iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
return -1;
}
return 0;
}
/*
* Sets the fields in the Tx cmd that are crypto related
*/
int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_sta *sta)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_mvm_sta *mvmsta;
struct iwl_device_cmd *dev_cmd;
struct iwl_tx_cmd *tx_cmd;
__le16 fc;
u16 seq_number = 0;
u8 tid = IWL_MAX_TID_COUNT;
u8 txq_id = info->hw_queue;
bool is_data_qos = false, is_ampdu = false;
mvmsta = (void *)sta->drv_priv;
fc = hdr->frame_control;
if (WARN_ON_ONCE(!mvmsta))
return -1;
if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
return -1;
dev_cmd = iwl_mvm_set_tx_params(mvm, skb, sta, mvmsta->sta_id);
if (!dev_cmd)
goto drop;
tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
/* From now on, we cannot access info->control */
spin_lock(&mvmsta->lock);
if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
u8 *qc = NULL;
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;
seq_number = mvmsta->tid_data[tid].seq_number;
seq_number &= IEEE80211_SCTL_SEQ;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
hdr->seq_ctrl |= cpu_to_le16(seq_number);
seq_number += 0x10;
is_data_qos = true;
is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU;
}
/* Copy MAC header from skb into command buffer */
memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(fc));
WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
if (is_ampdu) {
if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON))
goto drop_unlock_sta;
txq_id = mvmsta->tid_data[tid].txq_id;
}
IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
tid, txq_id, seq_number);
/* NOTE: aggregation will need changes here (for txq id) */
if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
goto drop_unlock_sta;
if (is_data_qos && !ieee80211_has_morefrags(fc))
mvmsta->tid_data[tid].seq_number = seq_number;
spin_unlock(&mvmsta->lock);
if (txq_id < IWL_MVM_FIRST_AGG_QUEUE)
atomic_inc(&mvm->pending_frames[mvmsta->sta_id]);
return 0;
drop_unlock_sta:
iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
spin_unlock(&mvmsta->lock);
drop:
return -1;
}
static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
struct ieee80211_sta *sta, u8 tid)
{
struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
struct ieee80211_vif *vif = mvmsta->vif;
lockdep_assert_held(&mvmsta->lock);
if (tid_data->ssn != tid_data->next_reclaimed)
return;
switch (tid_data->state) {
case IWL_EMPTYING_HW_QUEUE_ADDBA:
IWL_DEBUG_TX_QUEUES(mvm,
"Can continue addBA flow ssn = next_recl = %d\n",
tid_data->next_reclaimed);
tid_data->state = IWL_AGG_STARTING;
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IWL_EMPTYING_HW_QUEUE_DELBA:
IWL_DEBUG_TX_QUEUES(mvm,
"Can continue DELBA flow ssn = next_recl = %d\n",
tid_data->next_reclaimed);
iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
tid_data->state = IWL_AGG_OFF;
/*
* we can't hold the mutex - but since we are after a sequence
* point (call to iwl_trans_txq_disable), so we don't even need
* a memory barrier.
*/
mvm->queue_to_mac80211[tid_data->txq_id] =
IWL_INVALID_MAC80211_QUEUE;
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
default:
break;
}
}
#ifdef CONFIG_IWLWIFI_DEBUG
const char *iwl_mvm_get_tx_fail_reason(u32 status)
{
#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
switch (status & TX_STATUS_MSK) {
case TX_STATUS_SUCCESS:
return "SUCCESS";
TX_STATUS_POSTPONE(DELAY);
TX_STATUS_POSTPONE(FEW_BYTES);
TX_STATUS_POSTPONE(BT_PRIO);
TX_STATUS_POSTPONE(QUIET_PERIOD);
TX_STATUS_POSTPONE(CALC_TTAK);
TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
TX_STATUS_FAIL(SHORT_LIMIT);
TX_STATUS_FAIL(LONG_LIMIT);
TX_STATUS_FAIL(UNDERRUN);
TX_STATUS_FAIL(DRAIN_FLOW);
TX_STATUS_FAIL(RFKILL_FLUSH);
TX_STATUS_FAIL(LIFE_EXPIRE);
TX_STATUS_FAIL(DEST_PS);
TX_STATUS_FAIL(HOST_ABORTED);
TX_STATUS_FAIL(BT_RETRY);
TX_STATUS_FAIL(STA_INVALID);
TX_STATUS_FAIL(FRAG_DROPPED);
TX_STATUS_FAIL(TID_DISABLE);
TX_STATUS_FAIL(FIFO_FLUSHED);
TX_STATUS_FAIL(SMALL_CF_POLL);
TX_STATUS_FAIL(FW_DROP);
TX_STATUS_FAIL(STA_COLOR_MISMATCH);
}
return "UNKNOWN";
#undef TX_STATUS_FAIL
#undef TX_STATUS_POSTPONE
}
#endif /* CONFIG_IWLWIFI_DEBUG */
/**
* translate ucode response to mac80211 tx status control values
*/
static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags,
struct ieee80211_tx_info *info)
{
struct ieee80211_tx_rate *r = &info->status.rates[0];
info->status.antenna =
((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
if (rate_n_flags & RATE_HT_MCS_GF_MSK)
r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
case RATE_MCS_CHAN_WIDTH_20:
break;
case RATE_MCS_CHAN_WIDTH_40:
r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
break;
case RATE_MCS_CHAN_WIDTH_80:
r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
break;
case RATE_MCS_CHAN_WIDTH_160:
r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH;
break;
}
if (rate_n_flags & RATE_MCS_SGI_MSK)
r->flags |= IEEE80211_TX_RC_SHORT_GI;
if (rate_n_flags & RATE_MCS_HT_MSK) {
r->flags |= IEEE80211_TX_RC_MCS;
r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
} else if (rate_n_flags & RATE_MCS_VHT_MSK) {
ieee80211_rate_set_vht(
r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK,
((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
RATE_VHT_MCS_NSS_POS) + 1);
r->flags |= IEEE80211_TX_RC_VHT_MCS;
} else {
r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
info->band);
}
}
static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{
struct ieee80211_sta *sta;
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
int txq_id = SEQ_TO_QUEUE(sequence);
struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
u32 status = le16_to_cpu(tx_resp->status.status);
u16 ssn = iwl_mvm_get_scd_ssn(tx_resp);
struct iwl_mvm_sta *mvmsta;
struct sk_buff_head skbs;
u8 skb_freed = 0;
u16 next_reclaimed, seq_ctl;
__skb_queue_head_init(&skbs);
seq_ctl = le16_to_cpu(tx_resp->seq_ctl);
/* we can free until ssn % q.n_bd not inclusive */
iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs);
while (!skb_queue_empty(&skbs)) {
struct sk_buff *skb = __skb_dequeue(&skbs);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
skb_freed++;
iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
memset(&info->status, 0, sizeof(info->status));
info->flags &= ~IEEE80211_TX_CTL_AMPDU;
/* inform mac80211 about what happened with the frame */
switch (status & TX_STATUS_MSK) {
case TX_STATUS_SUCCESS:
case TX_STATUS_DIRECT_DONE:
info->flags |= IEEE80211_TX_STAT_ACK;
break;
case TX_STATUS_FAIL_DEST_PS:
info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
break;
default:
break;
}
info->status.rates[0].count = tx_resp->failure_frame + 1;
iwl_mvm_hwrate_to_tx_control(le32_to_cpu(tx_resp->initial_rate),
info);
/* Single frame failure in an AMPDU queue => send BAR */
if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE &&
!(info->flags & IEEE80211_TX_STAT_ACK))
info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
/* W/A FW bug: seq_ctl is wrong when the status isn't success */
if (status != TX_STATUS_SUCCESS) {
struct ieee80211_hdr *hdr = (void *)skb->data;
seq_ctl = le16_to_cpu(hdr->seq_ctrl);
}
ieee80211_tx_status_ni(mvm->hw, skb);
}
if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE) {
/* If this is an aggregation queue, we use the ssn since:
* ssn = wifi seq_num % 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. In that
* case, several packets will be reclaimed even if
* frame_count = 1.
*
* The ssn is the index (% 256) of the latest packet that has
* treated (acked / dropped) + 1.
*/
next_reclaimed = ssn;
} else {
/* The next packet to be reclaimed is the one after this one */
next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10);
}
IWL_DEBUG_TX_REPLY(mvm,
"TXQ %d status %s (0x%08x)\n",
txq_id, iwl_mvm_get_tx_fail_reason(status), status);
IWL_DEBUG_TX_REPLY(mvm,
"\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n",
le32_to_cpu(tx_resp->initial_rate),
tx_resp->failure_frame, SEQ_TO_INDEX(sequence),
ssn, next_reclaimed, seq_ctl);
rcu_read_lock();
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
if (!IS_ERR_OR_NULL(sta)) {
mvmsta = (void *)sta->drv_priv;
if (tid != IWL_TID_NON_QOS) {
struct iwl_mvm_tid_data *tid_data =
&mvmsta->tid_data[tid];
spin_lock_bh(&mvmsta->lock);
tid_data->next_reclaimed = next_reclaimed;
IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n",
next_reclaimed);
iwl_mvm_check_ratid_empty(mvm, sta, tid);
spin_unlock_bh(&mvmsta->lock);
}
#ifdef CONFIG_PM_SLEEP
mvmsta->last_seq_ctl = seq_ctl;
#endif
} else {
sta = NULL;
mvmsta = NULL;
}
/*
* If the txq is not an AMPDU queue, there is no chance we freed
* several skbs. Check that out...
*/
if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && !WARN_ON(skb_freed > 1) &&
atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) {
if (mvmsta) {
/*
* If there are no pending frames for this STA, notify
* mac80211 that this station can go to sleep in its
* STA table.
*/
if (mvmsta->vif->type == NL80211_IFTYPE_AP)
ieee80211_sta_block_awake(mvm->hw, sta, false);
/*
* We might very well have taken mvmsta pointer while
* the station was being removed. The remove flow might
* have seen a pending_frame (because we didn't take
* the lock) even if now the queues are drained. So make
* really sure now that this the station is not being
* removed. If it is, run the drain worker to remove it.
*/
spin_lock_bh(&mvmsta->lock);
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
if (IS_ERR_OR_NULL(sta)) {
/*
* Station disappeared in the meantime:
* so we are draining.
*/
set_bit(sta_id, mvm->sta_drained);
schedule_work(&mvm->sta_drained_wk);
}
spin_unlock_bh(&mvmsta->lock);
} else if (!mvmsta) {
/* Tx response without STA, so we are draining */
set_bit(sta_id, mvm->sta_drained);
schedule_work(&mvm->sta_drained_wk);
}
}
rcu_read_unlock();
}
#ifdef CONFIG_IWLWIFI_DEBUG
#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x
static const char *iwl_get_agg_tx_status(u16 status)
{
switch (status & AGG_TX_STATE_STATUS_MSK) {
AGG_TX_STATE_(TRANSMITTED);
AGG_TX_STATE_(UNDERRUN);
AGG_TX_STATE_(BT_PRIO);
AGG_TX_STATE_(FEW_BYTES);
AGG_TX_STATE_(ABORT);
AGG_TX_STATE_(LAST_SENT_TTL);
AGG_TX_STATE_(LAST_SENT_TRY_CNT);
AGG_TX_STATE_(LAST_SENT_BT_KILL);
AGG_TX_STATE_(SCD_QUERY);
AGG_TX_STATE_(TEST_BAD_CRC32);
AGG_TX_STATE_(RESPONSE);
AGG_TX_STATE_(DUMP_TX);
AGG_TX_STATE_(DELAY_TX);
}
return "UNKNOWN";
}
static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{
struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
struct agg_tx_status *frame_status = &tx_resp->status;
int i;
for (i = 0; i < tx_resp->frame_count; i++) {
u16 fstatus = le16_to_cpu(frame_status[i].status);
IWL_DEBUG_TX_REPLY(mvm,
"status %s (0x%04x), try-count (%d) seq (0x%x)\n",
iwl_get_agg_tx_status(fstatus),
fstatus & AGG_TX_STATE_STATUS_MSK,
(fstatus & AGG_TX_STATE_TRY_CNT_MSK) >>
AGG_TX_STATE_TRY_CNT_POS,
le16_to_cpu(frame_status[i].sequence));
}
}
#else
static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{}
#endif /* CONFIG_IWLWIFI_DEBUG */
static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{
struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
struct ieee80211_sta *sta;
if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_MVM_FIRST_AGG_QUEUE))
return;
if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
return;
iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt);
rcu_read_lock();
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
mvmsta->tid_data[tid].rate_n_flags =
le32_to_cpu(tx_resp->initial_rate);
}
rcu_read_unlock();
}
int iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
if (tx_resp->frame_count == 1)
iwl_mvm_rx_tx_cmd_single(mvm, pkt);
else
iwl_mvm_rx_tx_cmd_agg(mvm, pkt);
return 0;
}
int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data;
struct sk_buff_head reclaimed_skbs;
struct iwl_mvm_tid_data *tid_data;
struct ieee80211_tx_info *info;
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta;
struct ieee80211_hdr *hdr;
struct sk_buff *skb;
int sta_id, tid, freed;
/* "flow" corresponds to Tx queue */
u16 scd_flow = le16_to_cpu(ba_notif->scd_flow);
/* "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_notif->scd_ssn);
sta_id = ba_notif->sta_id;
tid = ba_notif->tid;
rcu_read_lock();
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
/* Reclaiming frames for a station that has been deleted ? */
if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
rcu_read_unlock();
return 0;
}
mvmsta = (void *)sta->drv_priv;
tid_data = &mvmsta->tid_data[tid];
if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d",
tid_data->txq_id, tid, scd_flow)) {
rcu_read_unlock();
return 0;
}
spin_lock_bh(&mvmsta->lock);
__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(mvm->trans, scd_flow, ba_resp_scd_ssn,
&reclaimed_skbs);
IWL_DEBUG_TX_REPLY(mvm,
"BA_NOTIFICATION Received from %pM, sta_id = %d\n",
(u8 *)&ba_notif->sta_addr_lo32,
ba_notif->sta_id);
IWL_DEBUG_TX_REPLY(mvm,
"TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n",
ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl),
(unsigned long long)le64_to_cpu(ba_notif->bitmap),
scd_flow, ba_resp_scd_ssn, ba_notif->txed,
ba_notif->txed_2_done);
tid_data->next_reclaimed = ba_resp_scd_ssn;
iwl_mvm_check_ratid_empty(mvm, sta, tid);
freed = 0;
skb_queue_walk(&reclaimed_skbs, skb) {
hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_data_qos(hdr->frame_control))
freed++;
else
WARN_ON_ONCE(1);
info = IEEE80211_SKB_CB(skb);
iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
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_ACK;
info->flags |= IEEE80211_TX_STAT_AMPDU;
info->status.ampdu_ack_len = ba_notif->txed_2_done;
info->status.ampdu_len = ba_notif->txed;
iwl_mvm_hwrate_to_tx_control(tid_data->rate_n_flags,
info);
}
}
spin_unlock_bh(&mvmsta->lock);
rcu_read_unlock();
while (!skb_queue_empty(&reclaimed_skbs)) {
skb = __skb_dequeue(&reclaimed_skbs);
ieee80211_tx_status_ni(mvm->hw, skb);
}
return 0;
}
int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
{
int ret;
struct iwl_tx_path_flush_cmd flush_cmd = {
.queues_ctl = cpu_to_le32(tfd_msk),
.flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
};
u32 flags = sync ? CMD_SYNC : CMD_ASYNC;
ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
sizeof(flush_cmd), &flush_cmd);
if (ret)
IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret);
return ret;
}

View File

@@ -0,0 +1,473 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <net/mac80211.h>
#include "iwl-debug.h"
#include "iwl-io.h"
#include "mvm.h"
#include "fw-api-rs.h"
/*
* Will return 0 even if the cmd failed when RFKILL is asserted unless
* CMD_WANT_SKB is set in cmd->flags.
*/
int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
{
int ret;
/*
* Synchronous commands from this op-mode must hold
* the mutex, this ensures we don't try to send two
* (or more) synchronous commands at a time.
*/
if (!(cmd->flags & CMD_ASYNC))
lockdep_assert_held(&mvm->mutex);
ret = iwl_trans_send_cmd(mvm->trans, cmd);
/*
* If the caller wants the SKB, then don't hide any problems, the
* caller might access the response buffer which will be NULL if
* the command failed.
*/
if (cmd->flags & CMD_WANT_SKB)
return ret;
/* Silently ignore failures if RFKILL is asserted */
if (!ret || ret == -ERFKILL)
return 0;
return ret;
}
int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id,
u32 flags, u16 len, const void *data)
{
struct iwl_host_cmd cmd = {
.id = id,
.len = { len, },
.data = { data, },
.flags = flags,
};
return iwl_mvm_send_cmd(mvm, &cmd);
}
/*
* We assume that the caller set the status to the sucess value
*/
int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
u32 *status)
{
struct iwl_rx_packet *pkt;
struct iwl_cmd_response *resp;
int ret, resp_len;
lockdep_assert_held(&mvm->mutex);
/*
* Only synchronous commands can wait for status,
* we use WANT_SKB so the caller can't.
*/
if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB),
"cmd flags %x", cmd->flags))
return -EINVAL;
cmd->flags |= CMD_SYNC | CMD_WANT_SKB;
ret = iwl_trans_send_cmd(mvm->trans, cmd);
if (ret == -ERFKILL) {
/*
* The command failed because of RFKILL, don't update
* the status, leave it as success and return 0.
*/
return 0;
} else if (ret) {
return ret;
}
pkt = cmd->resp_pkt;
/* Can happen if RFKILL is asserted */
if (!pkt) {
ret = 0;
goto out_free_resp;
}
if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
ret = -EIO;
goto out_free_resp;
}
resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) {
ret = -EIO;
goto out_free_resp;
}
resp = (void *)pkt->data;
*status = le32_to_cpu(resp->status);
out_free_resp:
iwl_free_resp(cmd);
return ret;
}
/*
* We assume that the caller set the status to the sucess value
*/
int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id, u16 len,
const void *data, u32 *status)
{
struct iwl_host_cmd cmd = {
.id = id,
.len = { len, },
.data = { data, },
};
return iwl_mvm_send_cmd_status(mvm, &cmd, status);
}
#define IWL_DECLARE_RATE_INFO(r) \
[IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
/*
* Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
*/
static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
IWL_DECLARE_RATE_INFO(1),
IWL_DECLARE_RATE_INFO(2),
IWL_DECLARE_RATE_INFO(5),
IWL_DECLARE_RATE_INFO(11),
IWL_DECLARE_RATE_INFO(6),
IWL_DECLARE_RATE_INFO(9),
IWL_DECLARE_RATE_INFO(12),
IWL_DECLARE_RATE_INFO(18),
IWL_DECLARE_RATE_INFO(24),
IWL_DECLARE_RATE_INFO(36),
IWL_DECLARE_RATE_INFO(48),
IWL_DECLARE_RATE_INFO(54),
};
int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
enum ieee80211_band band)
{
int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
int idx;
int band_offset = 0;
/* Legacy rate format, search for match in table */
if (band == IEEE80211_BAND_5GHZ)
band_offset = IWL_FIRST_OFDM_RATE;
for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
if (fw_rate_idx_to_plcp[idx] == rate)
return idx - band_offset;
return -1;
}
u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx)
{
/* Get PLCP rate for tx_cmd->rate_n_flags */
return fw_rate_idx_to_plcp[rate_idx];
}
int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_error_resp *err_resp = (void *)pkt->data;
IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n",
le32_to_cpu(err_resp->error_type), err_resp->cmd_id);
IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n",
le16_to_cpu(err_resp->bad_cmd_seq_num),
le32_to_cpu(err_resp->error_service));
IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n",
le64_to_cpu(err_resp->timestamp));
return 0;
}
/*
* Returns the first antenna as ANT_[ABC], as defined in iwl-config.h.
* The parameter should also be a combination of ANT_[ABC].
*/
u8 first_antenna(u8 mask)
{
BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */
if (WARN_ON_ONCE(!mask)) /* ffs will return 0 if mask is zeroed */
return BIT(0);
return BIT(ffs(mask) - 1);
}
/*
* Toggles between TX antennas to send the probe request on.
* Receives the bitmask of valid TX antennas and the *index* used
* for the last TX, and returns the next valid *index* to use.
* In order to set it in the tx_cmd, must do BIT(idx).
*/
u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
{
u8 ind = last_idx;
int i;
for (i = 0; i < RATE_MCS_ANT_NUM; i++) {
ind = (ind + 1) % RATE_MCS_ANT_NUM;
if (valid & BIT(ind))
return ind;
}
WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid);
return last_idx;
}
static struct {
char *name;
u8 num;
} advanced_lookup[] = {
{ "NMI_INTERRUPT_WDG", 0x34 },
{ "SYSASSERT", 0x35 },
{ "UCODE_VERSION_MISMATCH", 0x37 },
{ "BAD_COMMAND", 0x38 },
{ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
{ "FATAL_ERROR", 0x3D },
{ "NMI_TRM_HW_ERR", 0x46 },
{ "NMI_INTERRUPT_TRM", 0x4C },
{ "NMI_INTERRUPT_BREAK_POINT", 0x54 },
{ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
{ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
{ "NMI_INTERRUPT_HOST", 0x66 },
{ "NMI_INTERRUPT_ACTION_PT", 0x7C },
{ "NMI_INTERRUPT_UNKNOWN", 0x84 },
{ "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
{ "ADVANCED_SYSASSERT", 0 },
};
static const char *desc_lookup(u32 num)
{
int i;
for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++)
if (advanced_lookup[i].num == num)
return advanced_lookup[i].name;
/* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
return advanced_lookup[i].name;
}
/*
* Note: This structure is read from the device with IO accesses,
* and the reading already does the endian conversion. As it is
* read with u32-sized accesses, any members with a different size
* need to be ordered correctly though!
*/
struct iwl_error_event_table {
u32 valid; /* (nonzero) valid, (0) log is empty */
u32 error_id; /* type of error */
u32 pc; /* program counter */
u32 blink1; /* branch link */
u32 blink2; /* branch link */
u32 ilink1; /* interrupt link */
u32 ilink2; /* interrupt link */
u32 data1; /* error-specific data */
u32 data2; /* error-specific data */
u32 data3; /* error-specific data */
u32 bcon_time; /* beacon timer */
u32 tsf_low; /* network timestamp function timer */
u32 tsf_hi; /* network timestamp function timer */
u32 gp1; /* GP1 timer register */
u32 gp2; /* GP2 timer register */
u32 gp3; /* GP3 timer register */
u32 ucode_ver; /* uCode version */
u32 hw_ver; /* HW Silicon version */
u32 brd_ver; /* HW board version */
u32 log_pc; /* log program counter */
u32 frame_ptr; /* frame pointer */
u32 stack_ptr; /* stack pointer */
u32 hcmd; /* last host command header */
u32 isr0; /* isr status register LMPM_NIC_ISR0:
* rxtx_flag */
u32 isr1; /* isr status register LMPM_NIC_ISR1:
* host_flag */
u32 isr2; /* isr status register LMPM_NIC_ISR2:
* enc_flag */
u32 isr3; /* isr status register LMPM_NIC_ISR3:
* time_flag */
u32 isr4; /* isr status register LMPM_NIC_ISR4:
* wico interrupt */
u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
u32 wait_event; /* wait event() caller address */
u32 l2p_control; /* L2pControlField */
u32 l2p_duration; /* L2pDurationField */
u32 l2p_mhvalid; /* L2pMhValidBits */
u32 l2p_addr_match; /* L2pAddrMatchStat */
u32 lmpm_pmg_sel; /* indicate which clocks are turned on
* (LMPM_PMG_SEL) */
u32 u_timestamp; /* indicate when the date and time of the
* compilation */
u32 flow_handler; /* FH read/write pointers, RX credit */
} __packed;
#define ERROR_START_OFFSET (1 * sizeof(u32))
#define ERROR_ELEM_SIZE (7 * sizeof(u32))
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
{
struct iwl_trans *trans = mvm->trans;
struct iwl_error_event_table table;
u32 base;
base = mvm->error_event_table;
if (mvm->cur_ucode == IWL_UCODE_INIT) {
if (!base)
base = mvm->fw->init_errlog_ptr;
} else {
if (!base)
base = mvm->fw->inst_errlog_ptr;
}
if (base < 0x800000 || base >= 0x80C000) {
IWL_ERR(mvm,
"Not valid error log pointer 0x%08X for %s uCode\n",
base,
(mvm->cur_ucode == IWL_UCODE_INIT)
? "Init" : "RT");
return;
}
iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
IWL_ERR(trans, "Start IWL Error Log Dump:\n");
IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
mvm->status, table.valid);
}
trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
table.data1, table.data2, table.data3,
table.blink1, table.blink2, table.ilink1,
table.ilink2, table.bcon_time, table.gp1,
table.gp2, table.gp3, table.ucode_ver,
table.hw_ver, table.brd_ver);
IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id));
IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver);
IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
}
/**
* iwl_mvm_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_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
u8 flags, bool init)
{
struct iwl_host_cmd cmd = {
.id = LQ_CMD,
.len = { sizeof(struct iwl_lq_cmd), },
.flags = flags,
.data = { lq, },
};
if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT))
return -EINVAL;
if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
return -EINVAL;
return iwl_mvm_send_cmd(mvm, &cmd);
}