Commit dac4df1c authored by Luca Coelho's avatar Luca Coelho

iwlwifi: mvm: support version 7 of the SCAN_REQ_UMAC FW command

Newer firmware versions (such as iwlwifi-8000C-34.ucode) have
introduced an API change in the SCAN_REQ_UMAC command that is not
backwards compatible.  The driver needs to detect and use the new API
format when the firmware reports it, otherwise the scan command will
not work properly, causing a command timeout.

Fix this by adding a TLV that tells the driver that the new API is in
use and use the correct structures for it.

This fixes https://bugzilla.kernel.org/show_bug.cgi?id=197591

Fixes: d7a5b3e9 ("iwlwifi: mvm: bump API to 34 for 8000 and up")
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent fdd0bd88
...@@ -531,6 +531,8 @@ struct iwl_scan_config_v1 { ...@@ -531,6 +531,8 @@ struct iwl_scan_config_v1 {
} __packed; /* SCAN_CONFIG_DB_CMD_API_S */ } __packed; /* SCAN_CONFIG_DB_CMD_API_S */
#define SCAN_TWO_LMACS 2 #define SCAN_TWO_LMACS 2
#define SCAN_LB_LMAC_IDX 0
#define SCAN_HB_LMAC_IDX 1
struct iwl_scan_config { struct iwl_scan_config {
__le32 flags; __le32 flags;
...@@ -578,6 +580,7 @@ enum iwl_umac_scan_general_flags { ...@@ -578,6 +580,7 @@ enum iwl_umac_scan_general_flags {
IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9), IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9),
IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10), IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10),
IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED = BIT(11), IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED = BIT(11),
IWL_UMAC_SCAN_GEN_FLAGS_ADAPTIVE_DWELL = BIT(13),
}; };
/** /**
...@@ -631,12 +634,17 @@ struct iwl_scan_req_umac_tail { ...@@ -631,12 +634,17 @@ struct iwl_scan_req_umac_tail {
* @uid: scan id, &enum iwl_umac_scan_uid_offsets * @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @ooc_priority: out of channel priority - &enum iwl_scan_priority * @ooc_priority: out of channel priority - &enum iwl_scan_priority
* @general_flags: &enum iwl_umac_scan_general_flags * @general_flags: &enum iwl_umac_scan_general_flags
* @reserved2: for future use and alignment
* @scan_start_mac_id: report the scan start TSF time according to this mac TSF * @scan_start_mac_id: report the scan start TSF time according to this mac TSF
* @extended_dwell: dwell time for channels 1, 6 and 11 * @extended_dwell: dwell time for channels 1, 6 and 11
* @active_dwell: dwell time for active scan * @active_dwell: dwell time for active scan
* @passive_dwell: dwell time for passive scan * @passive_dwell: dwell time for passive scan
* @fragmented_dwell: dwell time for fragmented passive scan * @fragmented_dwell: dwell time for fragmented passive scan
* @adwell_default_n_aps: for adaptive dwell the default number of APs
* per channel
* @adwell_default_n_aps_social: for adaptive dwell the default
* number of APs per social (1,6,11) channel
* @adwell_max_budget: for adaptive dwell the maximal budget of TU to be added
* to total scan time
* @max_out_time: max out of serving channel time, per LMAC - for CDB there * @max_out_time: max out of serving channel time, per LMAC - for CDB there
* are 2 LMACs * are 2 LMACs
* @suspend_time: max suspend time, per LMAC - for CDB there are 2 LMACs * @suspend_time: max suspend time, per LMAC - for CDB there are 2 LMACs
...@@ -644,6 +652,8 @@ struct iwl_scan_req_umac_tail { ...@@ -644,6 +652,8 @@ struct iwl_scan_req_umac_tail {
* @channel_flags: &enum iwl_scan_channel_flags * @channel_flags: &enum iwl_scan_channel_flags
* @n_channels: num of channels in scan request * @n_channels: num of channels in scan request
* @reserved: for future use and alignment * @reserved: for future use and alignment
* @reserved2: for future use and alignment
* @reserved3: for future use and alignment
* @data: &struct iwl_scan_channel_cfg_umac and * @data: &struct iwl_scan_channel_cfg_umac and
* &struct iwl_scan_req_umac_tail * &struct iwl_scan_req_umac_tail
*/ */
...@@ -651,41 +661,64 @@ struct iwl_scan_req_umac { ...@@ -651,41 +661,64 @@ struct iwl_scan_req_umac {
__le32 flags; __le32 flags;
__le32 uid; __le32 uid;
__le32 ooc_priority; __le32 ooc_priority;
/* SCAN_GENERAL_PARAMS_API_S_VER_4 */
__le16 general_flags; __le16 general_flags;
u8 reserved2; u8 reserved;
u8 scan_start_mac_id; u8 scan_start_mac_id;
u8 extended_dwell;
u8 active_dwell;
u8 passive_dwell;
u8 fragmented_dwell;
union { union {
struct { struct {
u8 extended_dwell;
u8 active_dwell;
u8 passive_dwell;
u8 fragmented_dwell;
__le32 max_out_time; __le32 max_out_time;
__le32 suspend_time; __le32 suspend_time;
__le32 scan_priority; __le32 scan_priority;
/* SCAN_CHANNEL_PARAMS_API_S_VER_4 */ /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
u8 channel_flags; u8 channel_flags;
u8 n_channels; u8 n_channels;
__le16 reserved; __le16 reserved2;
u8 data[]; u8 data[];
} v1; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ } v1; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */
struct { struct {
u8 extended_dwell;
u8 active_dwell;
u8 passive_dwell;
u8 fragmented_dwell;
__le32 max_out_time[SCAN_TWO_LMACS]; __le32 max_out_time[SCAN_TWO_LMACS];
__le32 suspend_time[SCAN_TWO_LMACS]; __le32 suspend_time[SCAN_TWO_LMACS];
__le32 scan_priority; __le32 scan_priority;
/* SCAN_CHANNEL_PARAMS_API_S_VER_4 */ /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
u8 channel_flags; u8 channel_flags;
u8 n_channels; u8 n_channels;
__le16 reserved; __le16 reserved2;
u8 data[]; u8 data[];
} v6; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_6 */ } v6; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_6 */
struct {
u8 active_dwell;
u8 passive_dwell;
u8 fragmented_dwell;
u8 adwell_default_n_aps;
u8 adwell_default_n_aps_social;
u8 reserved3;
__le16 adwell_max_budget;
__le32 max_out_time[SCAN_TWO_LMACS];
__le32 suspend_time[SCAN_TWO_LMACS];
__le32 scan_priority;
/* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
u8 channel_flags;
u8 n_channels;
__le16 reserved2;
u8 data[];
} v7; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_7 */
}; };
} __packed; } __packed;
#define IWL_SCAN_REQ_UMAC_SIZE sizeof(struct iwl_scan_req_umac) #define IWL_SCAN_REQ_UMAC_SIZE_V7 sizeof(struct iwl_scan_req_umac)
#define IWL_SCAN_REQ_UMAC_SIZE_V6 (sizeof(struct iwl_scan_req_umac) - \
2 * sizeof(u8) - sizeof(__le16))
#define IWL_SCAN_REQ_UMAC_SIZE_V1 (sizeof(struct iwl_scan_req_umac) - \ #define IWL_SCAN_REQ_UMAC_SIZE_V1 (sizeof(struct iwl_scan_req_umac) - \
2 * sizeof(__le32)) 2 * sizeof(__le32) - 2 * sizeof(u8) - \
sizeof(__le16))
/** /**
* struct iwl_umac_scan_abort * struct iwl_umac_scan_abort
......
...@@ -264,6 +264,7 @@ enum iwl_ucode_tlv_api { ...@@ -264,6 +264,7 @@ enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_STA_TYPE = (__force iwl_ucode_tlv_api_t)30, IWL_UCODE_TLV_API_STA_TYPE = (__force iwl_ucode_tlv_api_t)30,
IWL_UCODE_TLV_API_NAN2_VER2 = (__force iwl_ucode_tlv_api_t)31, IWL_UCODE_TLV_API_NAN2_VER2 = (__force iwl_ucode_tlv_api_t)31,
/* API Set 1 */ /* API Set 1 */
IWL_UCODE_TLV_API_ADAPTIVE_DWELL = (__force iwl_ucode_tlv_api_t)32,
IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE = (__force iwl_ucode_tlv_api_t)34, IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE = (__force iwl_ucode_tlv_api_t)34,
IWL_UCODE_TLV_API_NEW_RX_STATS = (__force iwl_ucode_tlv_api_t)35, IWL_UCODE_TLV_API_NEW_RX_STATS = (__force iwl_ucode_tlv_api_t)35,
IWL_UCODE_TLV_API_COEX_ATS_EXTERNAL = (__force iwl_ucode_tlv_api_t)37, IWL_UCODE_TLV_API_COEX_ATS_EXTERNAL = (__force iwl_ucode_tlv_api_t)37,
......
...@@ -1142,6 +1142,12 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) ...@@ -1142,6 +1142,12 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
} }
static inline bool iwl_mvm_is_adaptive_dwell_supported(struct iwl_mvm *mvm)
{
return fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_ADAPTIVE_DWELL);
}
static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm) static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm)
{ {
/* For now we only use this mode to differentiate between /* For now we only use this mode to differentiate between
......
...@@ -130,6 +130,19 @@ struct iwl_mvm_scan_params { ...@@ -130,6 +130,19 @@ struct iwl_mvm_scan_params {
u32 measurement_dwell; u32 measurement_dwell;
}; };
static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm)
{
struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
if (iwl_mvm_is_adaptive_dwell_supported(mvm))
return (void *)&cmd->v7.data;
if (iwl_mvm_has_new_tx_api(mvm))
return (void *)&cmd->v6.data;
return (void *)&cmd->v1.data;
}
static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm) static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm)
{ {
if (mvm->scan_rx_ant != ANT_NONE) if (mvm->scan_rx_ant != ANT_NONE)
...@@ -1075,25 +1088,57 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, ...@@ -1075,25 +1088,57 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
{ {
struct iwl_mvm_scan_timing_params *timing = &scan_timing[params->type]; struct iwl_mvm_scan_timing_params *timing = &scan_timing[params->type];
if (iwl_mvm_is_regular_scan(params))
cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
else
cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2);
if (iwl_mvm_is_adaptive_dwell_supported(mvm)) {
if (params->measurement_dwell) {
cmd->v7.active_dwell = params->measurement_dwell;
cmd->v7.passive_dwell = params->measurement_dwell;
} else {
cmd->v7.active_dwell = IWL_SCAN_DWELL_ACTIVE;
cmd->v7.passive_dwell = IWL_SCAN_DWELL_PASSIVE;
}
cmd->v7.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED;
cmd->v7.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
cmd->v7.max_out_time[SCAN_LB_LMAC_IDX] =
cpu_to_le32(timing->max_out_time);
cmd->v7.suspend_time[SCAN_LB_LMAC_IDX] =
cpu_to_le32(timing->suspend_time);
if (iwl_mvm_is_cdb_supported(mvm)) {
cmd->v7.max_out_time[SCAN_HB_LMAC_IDX] =
cpu_to_le32(timing->max_out_time);
cmd->v7.suspend_time[SCAN_HB_LMAC_IDX] =
cpu_to_le32(timing->suspend_time);
}
return;
}
if (params->measurement_dwell) { if (params->measurement_dwell) {
cmd->active_dwell = params->measurement_dwell; cmd->v1.active_dwell = params->measurement_dwell;
cmd->passive_dwell = params->measurement_dwell; cmd->v1.passive_dwell = params->measurement_dwell;
cmd->extended_dwell = params->measurement_dwell; cmd->v1.extended_dwell = params->measurement_dwell;
} else { } else {
cmd->active_dwell = IWL_SCAN_DWELL_ACTIVE; cmd->v1.active_dwell = IWL_SCAN_DWELL_ACTIVE;
cmd->passive_dwell = IWL_SCAN_DWELL_PASSIVE; cmd->v1.passive_dwell = IWL_SCAN_DWELL_PASSIVE;
cmd->extended_dwell = IWL_SCAN_DWELL_EXTENDED; cmd->v1.extended_dwell = IWL_SCAN_DWELL_EXTENDED;
} }
cmd->fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED; cmd->v1.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED;
if (iwl_mvm_has_new_tx_api(mvm)) { if (iwl_mvm_has_new_tx_api(mvm)) {
cmd->v6.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); cmd->v6.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
cmd->v6.max_out_time[0] = cpu_to_le32(timing->max_out_time); cmd->v6.max_out_time[SCAN_LB_LMAC_IDX] =
cmd->v6.suspend_time[0] = cpu_to_le32(timing->suspend_time); cpu_to_le32(timing->max_out_time);
cmd->v6.suspend_time[SCAN_LB_LMAC_IDX] =
cpu_to_le32(timing->suspend_time);
if (iwl_mvm_is_cdb_supported(mvm)) { if (iwl_mvm_is_cdb_supported(mvm)) {
cmd->v6.max_out_time[1] = cmd->v6.max_out_time[SCAN_HB_LMAC_IDX] =
cpu_to_le32(timing->max_out_time); cpu_to_le32(timing->max_out_time);
cmd->v6.suspend_time[1] = cmd->v6.suspend_time[SCAN_HB_LMAC_IDX] =
cpu_to_le32(timing->suspend_time); cpu_to_le32(timing->suspend_time);
} }
} else { } else {
...@@ -1102,11 +1147,6 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, ...@@ -1102,11 +1147,6 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
cmd->v1.scan_priority = cmd->v1.scan_priority =
cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
} }
if (iwl_mvm_is_regular_scan(params))
cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
else
cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2);
} }
static void static void
...@@ -1178,8 +1218,7 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ...@@ -1178,8 +1218,7 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int type) int type)
{ {
struct iwl_scan_req_umac *cmd = mvm->scan_cmd; struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
void *cmd_data = iwl_mvm_has_new_tx_api(mvm) ? void *cmd_data = iwl_mvm_get_scan_req_umac_data(mvm);
(void *)&cmd->v6.data : (void *)&cmd->v1.data;
struct iwl_scan_req_umac_tail *sec_part = cmd_data + struct iwl_scan_req_umac_tail *sec_part = cmd_data +
sizeof(struct iwl_scan_channel_cfg_umac) * sizeof(struct iwl_scan_channel_cfg_umac) *
mvm->fw->ucode_capa.n_scan_channels; mvm->fw->ucode_capa.n_scan_channels;
...@@ -1216,7 +1255,10 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ...@@ -1216,7 +1255,10 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
if (iwl_mvm_has_new_tx_api(mvm)) { if (iwl_mvm_is_adaptive_dwell_supported(mvm)) {
cmd->v7.channel_flags = channel_flags;
cmd->v7.n_channels = params->n_channels;
} else if (iwl_mvm_has_new_tx_api(mvm)) {
cmd->v6.channel_flags = channel_flags; cmd->v6.channel_flags = channel_flags;
cmd->v6.n_channels = params->n_channels; cmd->v6.n_channels = params->n_channels;
} else { } else {
...@@ -1661,8 +1703,10 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm) ...@@ -1661,8 +1703,10 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm)
{ {
int base_size = IWL_SCAN_REQ_UMAC_SIZE_V1; int base_size = IWL_SCAN_REQ_UMAC_SIZE_V1;
if (iwl_mvm_has_new_tx_api(mvm)) if (iwl_mvm_is_adaptive_dwell_supported(mvm))
base_size = IWL_SCAN_REQ_UMAC_SIZE; base_size = IWL_SCAN_REQ_UMAC_SIZE_V7;
else if (iwl_mvm_has_new_tx_api(mvm))
base_size = IWL_SCAN_REQ_UMAC_SIZE_V6;
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
return base_size + return base_size +
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment