diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/scan.c')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 625 | 
1 files changed, 551 insertions, 74 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index fcafa22ec6ce..a046ac9fa852 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -79,9 +79,6 @@  #define IWL_SCAN_NUM_OF_FRAGS		3  #define IWL_SCAN_LAST_2_4_CHN		14 -#define IWL_SCAN_BAND_5_2		0 -#define IWL_SCAN_BAND_2_4		1 -  /* adaptive dwell max budget time [TU] for full scan */  #define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300  /* adaptive dwell max budget time [TU] for directed scan */ @@ -92,6 +89,10 @@  #define IWL_SCAN_ADWELL_DEFAULT_LB_N_APS 2  /* adaptive dwell default APs number in social channels (1, 6, 11) */  #define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10 +/* number of scan channels */ +#define IWL_SCAN_NUM_CHANNELS 112 +/* adaptive dwell default number of APs override */ +#define IWL_SCAN_ADWELL_DEFAULT_N_APS_OVERRIDE 10  struct iwl_mvm_scan_timing_params {  	u32 suspend_time; @@ -196,14 +197,6 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)  	return cpu_to_le16(rx_chain);  } -static __le32 iwl_mvm_scan_rxon_flags(enum nl80211_band band) -{ -	if (band == NL80211_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 nl80211_band band,  			  bool no_cck) @@ -550,6 +543,7 @@ static void iwl_scan_build_ssids(struct iwl_mvm_scan_params *params,  {  	int i, j;  	int index; +	u32 tmp_bitmap = 0;  	/*  	 * copy SSIDs from match list. @@ -569,7 +563,6 @@ static void iwl_scan_build_ssids(struct iwl_mvm_scan_params *params,  	}  	/* add SSIDs from scan SSID list */ -	*ssid_bitmap = 0;  	for (j = params->n_ssids - 1;  	     j >= 0 && i < PROBE_OPTION_MAX;  	     i++, j--) { @@ -581,11 +574,13 @@ static void iwl_scan_build_ssids(struct iwl_mvm_scan_params *params,  			ssids[i].len = params->ssids[j].ssid_len;  			memcpy(ssids[i].ssid, params->ssids[j].ssid,  			       ssids[i].len); -			*ssid_bitmap |= BIT(i); +			tmp_bitmap |= BIT(i);  		} else { -			*ssid_bitmap |= BIT(index); +			tmp_bitmap |= BIT(index);  		}  	} +	if (ssid_bitmap) +		*ssid_bitmap = tmp_bitmap;  }  static int @@ -981,10 +976,7 @@ static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			 mvm->fw->ucode_capa.n_scan_channels);  	u32 ssid_bitmap = 0;  	int i; - -	lockdep_assert_held(&mvm->mutex); - -	memset(cmd, 0, ksize(cmd)); +	u8 band;  	if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))  		return -EINVAL; @@ -1000,7 +992,8 @@ static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params,  							      vif)); -	cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band); +	band = iwl_mvm_phy_band_from_nl80211(params->channels[0]->band); +	cmd->flags = cpu_to_le32(band);  	cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |  					MAC_FILTER_IN_BEACON);  	iwl_mvm_scan_fill_tx_cmd(mvm, cmd->tx_cmd, params->no_cck); @@ -1414,21 +1407,176 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,  		cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2);  } +static u32 iwl_mvm_scan_umac_ooc_priority(struct iwl_mvm_scan_params *params) +{ +	return iwl_mvm_is_regular_scan(params) ? +		IWL_SCAN_PRIORITY_EXT_6 : +		IWL_SCAN_PRIORITY_EXT_2; +} + +static void +iwl_mvm_scan_umac_dwell_v10(struct iwl_mvm *mvm, +			    struct iwl_scan_general_params_v10 *general_params, +			    struct iwl_mvm_scan_params *params) +{ +	struct iwl_mvm_scan_timing_params *timing, *hb_timing; +	u8 active_dwell, passive_dwell; + +	timing = &scan_timing[params->type]; +	active_dwell = params->measurement_dwell ? +		params->measurement_dwell : IWL_SCAN_DWELL_ACTIVE; +	passive_dwell = params->measurement_dwell ? +		params->measurement_dwell : IWL_SCAN_DWELL_PASSIVE; + +	general_params->adwell_default_social_chn = +		IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL; +	general_params->adwell_default_2g = IWL_SCAN_ADWELL_DEFAULT_LB_N_APS; +	general_params->adwell_default_5g = IWL_SCAN_ADWELL_DEFAULT_HB_N_APS; + +	/* if custom max budget was configured with debugfs */ +	if (IWL_MVM_ADWELL_MAX_BUDGET) +		general_params->adwell_max_budget = +			cpu_to_le16(IWL_MVM_ADWELL_MAX_BUDGET); +	else if (params->ssids && params->ssids[0].ssid_len) +		general_params->adwell_max_budget = +			cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); +	else +		general_params->adwell_max_budget = +			cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN); + +	general_params->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); +	general_params->max_out_of_time[SCAN_LB_LMAC_IDX] = +		cpu_to_le32(timing->max_out_time); +	general_params->suspend_time[SCAN_LB_LMAC_IDX] = +		cpu_to_le32(timing->suspend_time); + +	hb_timing = &scan_timing[params->hb_type]; + +	general_params->max_out_of_time[SCAN_HB_LMAC_IDX] = +		cpu_to_le32(hb_timing->max_out_time); +	general_params->suspend_time[SCAN_HB_LMAC_IDX] = +		cpu_to_le32(hb_timing->suspend_time); + +	general_params->active_dwell[SCAN_LB_LMAC_IDX] = active_dwell; +	general_params->passive_dwell[SCAN_LB_LMAC_IDX] = passive_dwell; +	general_params->active_dwell[SCAN_HB_LMAC_IDX] = active_dwell; +	general_params->passive_dwell[SCAN_HB_LMAC_IDX] = passive_dwell; +} + +struct iwl_mvm_scan_channel_segment { +	u8 start_idx; +	u8 end_idx; +	u8 first_channel_id; +	u8 last_channel_id; +	u8 channel_spacing_shift; +	u8 band; +}; + +static const struct iwl_mvm_scan_channel_segment scan_channel_segments[] = { +	{ +		.start_idx = 0, +		.end_idx = 13, +		.first_channel_id = 1, +		.last_channel_id = 14, +		.channel_spacing_shift = 0, +		.band = PHY_BAND_24 +	}, +	{ +		.start_idx = 14, +		.end_idx = 41, +		.first_channel_id = 36, +		.last_channel_id = 144, +		.channel_spacing_shift = 2, +		.band = PHY_BAND_5 +	}, +	{ +		.start_idx = 42, +		.end_idx = 50, +		.first_channel_id = 149, +		.last_channel_id = 181, +		.channel_spacing_shift = 2, +		.band = PHY_BAND_5 +	}, +}; + +static int iwl_mvm_scan_ch_and_band_to_idx(u8 channel_id, u8 band) +{ +	int i, index; + +	if (!channel_id) +		return -EINVAL; + +	for (i = 0; i < ARRAY_SIZE(scan_channel_segments); i++) { +		const struct iwl_mvm_scan_channel_segment *ch_segment = +			&scan_channel_segments[i]; +		u32 ch_offset; + +		if (ch_segment->band != band || +		    ch_segment->first_channel_id > channel_id || +		    ch_segment->last_channel_id < channel_id) +			continue; + +		ch_offset = (channel_id - ch_segment->first_channel_id) >> +			ch_segment->channel_spacing_shift; + +		index = scan_channel_segments[i].start_idx + ch_offset; +		if (index < IWL_SCAN_NUM_CHANNELS) +			return index; + +		break; +	} + +	return -EINVAL; +} + +static void iwl_mvm_scan_ch_add_n_aps_override(enum nl80211_iftype vif_type, +					       u8 ch_id, u8 band, u8 *ch_bitmap, +					       size_t bitmap_n_entries) +{ +	int i; +	static const u8 p2p_go_friendly_chs[] = { +		36, 40, 44, 48, 149, 153, 157, 161, 165, +	}; + +	if (vif_type != NL80211_IFTYPE_P2P_DEVICE) +		return; + +	for (i = 0; i < ARRAY_SIZE(p2p_go_friendly_chs); i++) { +		if (p2p_go_friendly_chs[i] == ch_id) { +			int ch_idx, bitmap_idx; + +			ch_idx = iwl_mvm_scan_ch_and_band_to_idx(ch_id, band); +			if (ch_idx < 0) +				return; + +			bitmap_idx = ch_idx / 8; +			if (bitmap_idx >= bitmap_n_entries) +				return; + +			ch_idx = ch_idx % 8; +			ch_bitmap[bitmap_idx] |= BIT(ch_idx); + +			return; +		} +	} +} +  static void  iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm,  			       struct ieee80211_channel **channels, -			       int n_channels, u32 ssid_bitmap, +			       int n_channels, u32 flags,  			       struct iwl_scan_channel_cfg_umac *channel_cfg)  {  	int i;  	for (i = 0; i < n_channels; i++) { -		channel_cfg[i].flags = cpu_to_le32(ssid_bitmap); +		channel_cfg[i].flags = cpu_to_le32(flags);  		channel_cfg[i].v1.channel_num = channels[i]->hw_value;  		if (iwl_mvm_is_scan_ext_chan_supported(mvm)) { +			enum nl80211_band band = channels[i]->band; +  			channel_cfg[i].v2.band = -				channels[i]->hw_value <= IWL_SCAN_LAST_2_4_CHN ? -					IWL_SCAN_BAND_2_4 : IWL_SCAN_BAND_5_2; +				iwl_mvm_phy_band_from_nl80211(band);  			channel_cfg[i].v2.iter_count = 1;  			channel_cfg[i].v2.iter_interval = 0;  		} else { @@ -1438,6 +1586,92 @@ iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm,  	}  } +static void +iwl_mvm_umac_scan_cfg_channels_v4(struct iwl_mvm *mvm, +				  struct ieee80211_channel **channels, +				  struct iwl_scan_channel_params_v4 *cp, +				  int n_channels, u32 flags, +				  enum nl80211_iftype vif_type) +{ +	u8 *bitmap = cp->adwell_ch_override_bitmap; +	size_t bitmap_n_entries = ARRAY_SIZE(cp->adwell_ch_override_bitmap); +	int i; + +	for (i = 0; i < n_channels; i++) { +		enum nl80211_band band = channels[i]->band; +		struct iwl_scan_channel_cfg_umac *cfg = +			&cp->channel_config[i]; + +		cfg->flags = cpu_to_le32(flags); +		cfg->v2.channel_num = channels[i]->hw_value; +		cfg->v2.band = iwl_mvm_phy_band_from_nl80211(band); +		cfg->v2.iter_count = 1; +		cfg->v2.iter_interval = 0; + +		iwl_mvm_scan_ch_add_n_aps_override(vif_type, +						   cfg->v2.channel_num, +						   cfg->v2.band, bitmap, +						   bitmap_n_entries); +	} +} + +static u8 iwl_mvm_scan_umac_chan_flags_v2(struct iwl_mvm *mvm, +					  struct iwl_mvm_scan_params *params, +					  struct ieee80211_vif *vif) +{ +	u8 flags = 0; + +	flags |= IWL_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER; + +	if (iwl_mvm_scan_use_ebs(mvm, vif)) +		flags |= IWL_SCAN_CHANNEL_FLAG_EBS | +			IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | +			IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; + +	/* set fragmented ebs for fragmented scan on HB channels */ +	if (iwl_mvm_is_scan_fragmented(params->hb_type)) +		flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG; + +	return flags; +} + +static u16 iwl_mvm_scan_umac_flags_v2(struct iwl_mvm *mvm, +				      struct iwl_mvm_scan_params *params, +				      struct ieee80211_vif *vif, +				      int type) +{ +	u16 flags = 0; + +	if (params->n_ssids == 0) +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE; + +	if (iwl_mvm_is_scan_fragmented(params->type)) +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1; + +	if (iwl_mvm_is_scan_fragmented(params->hb_type)) +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC2; + +	if (params->pass_all) +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PASS_ALL; +	else +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_MATCH; + +	if (!iwl_mvm_is_regular_scan(params)) +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PERIODIC; + +	if (params->measurement_dwell || +	    mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED) +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTFY_ITER_COMPLETE; + +	if (IWL_MVM_ADWELL_ENABLE) +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_ADAPTIVE_DWELL; + +	if (type == IWL_MVM_SCAN_SCHED || type == IWL_MVM_SCAN_NETDETECT) +		flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PREEMPTIVE; + +	return flags; +} +  static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,  				   struct iwl_mvm_scan_params *params,  				   struct ieee80211_vif *vif) @@ -1481,8 +1715,7 @@ static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,  	if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED)  		flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; -	if (iwl_mvm_is_adaptive_dwell_supported(mvm) && IWL_MVM_ADWELL_ENABLE && -	    vif->type != NL80211_IFTYPE_P2P_DEVICE) +	if (iwl_mvm_is_adaptive_dwell_supported(mvm) && IWL_MVM_ADWELL_ENABLE)  		flags |= IWL_UMAC_SCAN_GEN_FLAGS_ADAPTIVE_DWELL;  	/* @@ -1517,9 +1750,42 @@ static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,  	return flags;  } +static int +iwl_mvm_fill_scan_sched_params(struct iwl_mvm_scan_params *params, +			       struct iwl_scan_umac_schedule *schedule, +			       __le16 *delay) +{ +	int i; +	if (WARN_ON(!params->n_scan_plans || +		    params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) +		return -EINVAL; + +	for (i = 0; i < params->n_scan_plans; i++) { +		struct cfg80211_sched_scan_plan *scan_plan = +			¶ms->scan_plans[i]; + +		schedule[i].iter_count = scan_plan->iterations; +		schedule[i].interval = +			cpu_to_le16(scan_plan->interval); +	} + +	/* +	 * If the number of iterations of the last scan plan is set to +	 * zero, it should run infinitely. However, this is not always the case. +	 * For example, when regular scan is requested the driver sets one scan +	 * plan with one iteration. +	 */ +	if (!schedule[params->n_scan_plans - 1].iter_count) +		schedule[params->n_scan_plans - 1].iter_count = 0xff; + +	*delay = cpu_to_le16(params->delay); + +	return 0; +} +  static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			     struct iwl_mvm_scan_params *params, -			     int type) +			     int type, int uid)  {  	struct iwl_scan_req_umac *cmd = mvm->scan_cmd;  	struct iwl_scan_umac_chan_param *chan_param; @@ -1530,7 +1796,7 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  		(struct iwl_scan_req_umac_tail_v2 *)sec_part;  	struct iwl_scan_req_umac_tail_v1 *tail_v1;  	struct iwl_ssid_ie *direct_scan; -	int uid, i; +	int ret = 0;  	u32 ssid_bitmap = 0;  	u8 channel_flags = 0;  	u16 gen_flags; @@ -1538,17 +1804,6 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	chan_param = iwl_mvm_get_scan_req_umac_channel(mvm); -	lockdep_assert_held(&mvm->mutex); - -	if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) -		return -EINVAL; - -	uid = iwl_mvm_scan_uid_by_status(mvm, 0); -	if (uid < 0) -		return uid; - -	memset(cmd, 0, ksize(cmd)); -  	iwl_mvm_scan_umac_dwell(mvm, cmd, params);  	mvm->scan_uid_status[uid] = type; @@ -1591,25 +1846,10 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	chan_param->flags = channel_flags;  	chan_param->count = params->n_channels; -	for (i = 0; i < params->n_scan_plans; i++) { -		struct cfg80211_sched_scan_plan *scan_plan = -			¶ms->scan_plans[i]; - -		tail_v2->schedule[i].iter_count = scan_plan->iterations; -		tail_v2->schedule[i].interval = -			cpu_to_le16(scan_plan->interval); -	} - -	/* -	 * If the number of iterations of the last scan plan is set to -	 * zero, it should run infinitely. However, this is not always the case. -	 * For example, when regular scan is requested the driver sets one scan -	 * plan with one iteration. -	 */ -	if (!tail_v2->schedule[i - 1].iter_count) -		tail_v2->schedule[i - 1].iter_count = 0xff; - -	tail_v2->delay = cpu_to_le16(params->delay); +	ret = iwl_mvm_fill_scan_sched_params(params, tail_v2->schedule, +					     &tail_v2->delay); +	if (ret) +		return ret;  	if (iwl_mvm_is_scan_ext_chan_supported(mvm)) {  		tail_v2->preq = params->preq; @@ -1627,6 +1867,174 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	return 0;  } +static void +iwl_mvm_scan_umac_fill_general_p_v10(struct iwl_mvm *mvm, +				     struct iwl_mvm_scan_params *params, +				     struct ieee80211_vif *vif, +				     struct iwl_scan_general_params_v10 *gp, +				     u16 gen_flags) +{ +	struct iwl_mvm_vif *scan_vif = iwl_mvm_vif_from_mac80211(vif); + +	iwl_mvm_scan_umac_dwell_v10(mvm, gp, params); + +	gp->flags = cpu_to_le16(gen_flags); + +	if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1) +		gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; +	if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC2) +		gp->num_of_fragments[SCAN_HB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; + +	gp->scan_start_mac_id = scan_vif->id; +} + +static void +iwl_mvm_scan_umac_fill_probe_p_v3(struct iwl_mvm_scan_params *params, +				  struct iwl_scan_probe_params_v3 *pp) +{ +	pp->preq = params->preq; +	pp->ssid_num = params->n_ssids; +	iwl_scan_build_ssids(params, pp->direct_scan, NULL); +} + +static void +iwl_mvm_scan_umac_fill_probe_p_v4(struct iwl_mvm_scan_params *params, +				  struct iwl_scan_probe_params_v4 *pp, +				  u32 *bitmap_ssid) +{ +	pp->preq = params->preq; +	iwl_scan_build_ssids(params, pp->direct_scan, bitmap_ssid); +} + +static void +iwl_mvm_scan_umac_fill_ch_p_v3(struct iwl_mvm *mvm, +			       struct iwl_mvm_scan_params *params, +			       struct ieee80211_vif *vif, +			       struct iwl_scan_channel_params_v3 *cp) +{ +	cp->flags = iwl_mvm_scan_umac_chan_flags_v2(mvm, params, vif); +	cp->count = params->n_channels; + +	iwl_mvm_umac_scan_cfg_channels(mvm, params->channels, +				       params->n_channels, 0, +				       cp->channel_config); +} + +static void +iwl_mvm_scan_umac_fill_ch_p_v4(struct iwl_mvm *mvm, +			       struct iwl_mvm_scan_params *params, +			       struct ieee80211_vif *vif, +			       struct iwl_scan_channel_params_v4 *cp, +			       u32 channel_cfg_flags) +{ +	cp->flags = iwl_mvm_scan_umac_chan_flags_v2(mvm, params, vif); +	cp->count = params->n_channels; +	cp->num_of_aps_override = IWL_SCAN_ADWELL_DEFAULT_N_APS_OVERRIDE; + +	iwl_mvm_umac_scan_cfg_channels_v4(mvm, params->channels, cp, +					  params->n_channels, +					  channel_cfg_flags, +					  vif->type); +} + +static int iwl_mvm_scan_umac_v11(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +				 struct iwl_mvm_scan_params *params, int type, +				 int uid) +{ +	struct iwl_scan_req_umac_v11 *cmd = mvm->scan_cmd; +	struct iwl_scan_req_params_v11 *scan_p = &cmd->scan_params; +	int ret; +	u16 gen_flags; + +	mvm->scan_uid_status[uid] = type; + +	cmd->ooc_priority = cpu_to_le32(iwl_mvm_scan_umac_ooc_priority(params)); +	cmd->uid = cpu_to_le32(uid); + +	gen_flags = iwl_mvm_scan_umac_flags_v2(mvm, params, vif, type); +	iwl_mvm_scan_umac_fill_general_p_v10(mvm, params, vif, +					     &scan_p->general_params, +					     gen_flags); + +	 ret = iwl_mvm_fill_scan_sched_params(params, +					      scan_p->periodic_params.schedule, +					      &scan_p->periodic_params.delay); +	if (ret) +		return ret; + +	iwl_mvm_scan_umac_fill_probe_p_v3(params, &scan_p->probe_params); +	iwl_mvm_scan_umac_fill_ch_p_v3(mvm, params, vif, +				       &scan_p->channel_params); + +	return 0; +} + +static int iwl_mvm_scan_umac_v12(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +				 struct iwl_mvm_scan_params *params, int type, +				 int uid) +{ +	struct iwl_scan_req_umac_v12 *cmd = mvm->scan_cmd; +	struct iwl_scan_req_params_v12 *scan_p = &cmd->scan_params; +	int ret; +	u16 gen_flags; + +	mvm->scan_uid_status[uid] = type; + +	cmd->ooc_priority = cpu_to_le32(iwl_mvm_scan_umac_ooc_priority(params)); +	cmd->uid = cpu_to_le32(uid); + +	gen_flags = iwl_mvm_scan_umac_flags_v2(mvm, params, vif, type); +	iwl_mvm_scan_umac_fill_general_p_v10(mvm, params, vif, +					     &scan_p->general_params, +					     gen_flags); + +	 ret = iwl_mvm_fill_scan_sched_params(params, +					      scan_p->periodic_params.schedule, +					      &scan_p->periodic_params.delay); +	if (ret) +		return ret; + +	iwl_mvm_scan_umac_fill_probe_p_v3(params, &scan_p->probe_params); +	iwl_mvm_scan_umac_fill_ch_p_v4(mvm, params, vif, +				       &scan_p->channel_params, 0); + +	return 0; +} + +static int iwl_mvm_scan_umac_v13(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +				 struct iwl_mvm_scan_params *params, int type, +				 int uid) +{ +	struct iwl_scan_req_umac_v13 *cmd = mvm->scan_cmd; +	struct iwl_scan_req_params_v13 *scan_p = &cmd->scan_params; +	int ret; +	u16 gen_flags; +	u32 bitmap_ssid = 0; + +	mvm->scan_uid_status[uid] = type; + +	cmd->ooc_priority = cpu_to_le32(iwl_mvm_scan_umac_ooc_priority(params)); +	cmd->uid = cpu_to_le32(uid); + +	gen_flags = iwl_mvm_scan_umac_flags_v2(mvm, params, vif, type); +	iwl_mvm_scan_umac_fill_general_p_v10(mvm, params, vif, +					     &scan_p->general_params, +					     gen_flags); + +	 ret = iwl_mvm_fill_scan_sched_params(params, +					      scan_p->periodic_params.schedule, +					      &scan_p->periodic_params.delay); +	if (ret) +		return ret; + +	iwl_mvm_scan_umac_fill_probe_p_v4(params, &scan_p->probe_params, +					  &bitmap_ssid); +	iwl_mvm_scan_umac_fill_ch_p_v4(mvm, params, vif, +				       &scan_p->channel_params, bitmap_ssid); + +	return 0; +} +  static int iwl_mvm_num_scans(struct iwl_mvm *mvm)  {  	return hweight32(mvm->scan_status & IWL_MVM_SCAN_MASK); @@ -1729,6 +2137,64 @@ static void iwl_mvm_fill_scan_type(struct iwl_mvm *mvm,  	}  } +struct iwl_scan_umac_handler { +	u8 version; +	int (*handler)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		       struct iwl_mvm_scan_params *params, int type, int uid); +}; + +#define IWL_SCAN_UMAC_HANDLER(_ver) {		\ +	.version = _ver,			\ +	.handler = iwl_mvm_scan_umac_v##_ver,	\ +} + +static const struct iwl_scan_umac_handler iwl_scan_umac_handlers[] = { +	/* set the newest version first to shorten the list traverse time */ +	IWL_SCAN_UMAC_HANDLER(13), +	IWL_SCAN_UMAC_HANDLER(12), +	IWL_SCAN_UMAC_HANDLER(11), +}; + +static int iwl_mvm_build_scan_cmd(struct iwl_mvm *mvm, +				  struct ieee80211_vif *vif, +				  struct iwl_host_cmd *hcmd, +				  struct iwl_mvm_scan_params *params, +				  int type) +{ +	int uid, i; +	u8 scan_ver; + +	lockdep_assert_held(&mvm->mutex); +	memset(mvm->scan_cmd, 0, ksize(mvm->scan_cmd)); + +	if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { +		hcmd->id = SCAN_OFFLOAD_REQUEST_CMD; + +		return iwl_mvm_scan_lmac(mvm, vif, params); +	} + +	uid = iwl_mvm_scan_uid_by_status(mvm, 0); +	if (uid < 0) +		return uid; + +	hcmd->id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); + +	scan_ver = iwl_mvm_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, +					  SCAN_REQ_UMAC); + +	for (i = 0; i < ARRAY_SIZE(iwl_scan_umac_handlers); i++) { +		const struct iwl_scan_umac_handler *ver_handler = +			&iwl_scan_umac_handlers[i]; + +		if (ver_handler->version != scan_ver) +			continue; + +		return ver_handler->handler(mvm, vif, params, type, uid); +	} + +	return iwl_mvm_scan_umac(mvm, vif, params, type, uid); +} +  int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			   struct cfg80211_scan_request *req,  			   struct ieee80211_scan_ies *ies) @@ -1786,14 +2252,8 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); -	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { -		hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); -		ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, -					IWL_MVM_SCAN_REGULAR); -	} else { -		hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; -		ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); -	} +	ret = iwl_mvm_build_scan_cmd(mvm, vif, &hcmd, ¶ms, +				     IWL_MVM_SCAN_REGULAR);  	if (ret)  		return ret; @@ -1891,13 +2351,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,  	iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); -	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { -		hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); -		ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, type); -	} else { -		hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; -		ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); -	} +	ret = iwl_mvm_build_scan_cmd(mvm, vif, &hcmd, ¶ms, type);  	if (ret)  		return ret; @@ -2048,10 +2502,31 @@ static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type)  				     1 * HZ);  } +#define IWL_SCAN_REQ_UMAC_HANDLE_SIZE(_ver) {				\ +	case (_ver): return sizeof(struct iwl_scan_req_umac_v##_ver);	\ +} + +static int iwl_scan_req_umac_get_size(u8 scan_ver) +{ +	switch (scan_ver) { +		IWL_SCAN_REQ_UMAC_HANDLE_SIZE(13); +		IWL_SCAN_REQ_UMAC_HANDLE_SIZE(12); +		IWL_SCAN_REQ_UMAC_HANDLE_SIZE(11); +	} + +	return 0; +} +  int iwl_mvm_scan_size(struct iwl_mvm *mvm)  { -	int base_size = IWL_SCAN_REQ_UMAC_SIZE_V1; -	int tail_size; +	int base_size, tail_size; +	u8 scan_ver = iwl_mvm_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, +					     SCAN_REQ_UMAC); + +	base_size = iwl_scan_req_umac_get_size(scan_ver); +	if (base_size) +		return base_size; +  	if (iwl_mvm_is_adaptive_dwell_v2_supported(mvm))  		base_size = IWL_SCAN_REQ_UMAC_SIZE_V8; @@ -2059,6 +2534,8 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm)  		base_size = IWL_SCAN_REQ_UMAC_SIZE_V7;  	else if (iwl_mvm_cdb_scan_api(mvm))  		base_size = IWL_SCAN_REQ_UMAC_SIZE_V6; +	else +		base_size = IWL_SCAN_REQ_UMAC_SIZE_V1;  	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {  		if (iwl_mvm_is_scan_ext_chan_supported(mvm))  |