[PATCH ath-next 2/2] wifi: ath12k: Split scan request for split band device

Mahendran P quic_mahep at quicinc.com
Mon Apr 28 21:36:31 PDT 2025


On 4/28/2025 8:49 PM, Rameshkumar Sundaram wrote:
> When two split-phy devices having supported frequency range in same band
> (as mentioned below) are combined into an ath12k HW group, they will be
> part of same wiphy and hence the channel list (wiphy->bands[]) will be
> common for all of the radios (ar).
> 
> 1 - 2.4 GHz + 5 GHz Low band
> 2 - 5 GHz High band + 6 GHz
> 
> When a scan is triggered with frequency list containing frequencies of
> both  5 GHz low and 5 GHz high, mac80211 generates a single scan request
> to driver with both the frequencies. This is because mac80211 splits the
> scan request based on band.
> 
> ath12k checks the first frequency in the requested scan frequency list and
> initiates scan to corresponding radio's(ar) firmware with all the
> frequencies. Firmware rejects this scan as some frequencies in the scan
> request are not supported, resulting is scan failure.
> 
> Fix this by splitting the scan request into multiples scans in driver
> based on the supported frequency range of different radios in a band and
> schedule scans in parallel to them.
> Finally send scan completion/abort notification to mac80211 after all the
> radios complete their scheduled scan.
> 
> Also, last_scan_link is not needed anymore as ath12k internally schedules
> multiple scans, remove the same and use ahvif->links_map to identify
> scan links when scan is cancelled.
> 
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1
> 
> Co-developed-by: Vignesh C <quic_vignc at quicinc.com>
> Signed-off-by: Vignesh C <quic_vignc at quicinc.com>
> Signed-off-by: Rameshkumar Sundaram <rameshkumar.sundaram at oss.qualcomm.com>
> ---
>  drivers/net/wireless/ath/ath12k/core.h |   2 -
>  drivers/net/wireless/ath/ath12k/mac.c  | 148 ++++++++++++++++++++-----
>  2 files changed, 118 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index 0d512818ee96..c8fd0b29aaa1 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -353,8 +353,6 @@ struct ath12k_vif {
>  	struct ath12k_vif_cache *cache[IEEE80211_MLD_MAX_NUM_LINKS];
>  	/* indicates bitmap of link vif created in FW */
>  	u32 links_map;
> -	u8 last_scan_link;
> -
>  	/* Must be last - ends in a flexible-array member.
>  	 *
>  	 * FIXME: Driver should not copy struct ieee80211_chanctx_conf,
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index 6dab2f3a9e0d..236e3ee22d4d 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -4249,6 +4249,23 @@ static void ath12k_scan_timeout_work(struct work_struct *work)
>  	wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy);
>  }
>  
> +static void ath12k_mac_scan_send_complete(struct ath12k *ar,
> +					  struct cfg80211_scan_info *info)
> +{
> +	struct ath12k_hw *ah = ar->ah;
> +	struct ath12k *partner_ar;
> +	int i;
> +
> +	lockdep_assert_wiphy(ah->hw->wiphy);
> +
> +	for_each_ar(ah, partner_ar, i)
> +		if (partner_ar != ar &&
> +		    partner_ar->scan.state == ATH12K_SCAN_RUNNING)
> +			return;
> +
> +	ieee80211_scan_completed(ah->hw, info);
> +}
> +
>  static void ath12k_scan_vdev_clean_work(struct wiphy *wiphy, struct wiphy_work *work)
>  {
>  	struct ath12k *ar = container_of(work, struct ath12k,
> @@ -4287,7 +4304,7 @@ static void ath12k_scan_vdev_clean_work(struct wiphy *wiphy, struct wiphy_work *
>  				    ATH12K_SCAN_STARTING)),
>  		};
>  
> -		ieee80211_scan_completed(ar->ah->hw, &info);
> +		ath12k_mac_scan_send_complete(ar, &info);
>  	}
>  
>  	ar->scan.state = ATH12K_SCAN_IDLE;
> @@ -4506,12 +4523,14 @@ ath12k_mac_find_link_id_by_ar(struct ath12k_vif *ahvif, struct ath12k *ar)
>  	return ATH12K_FIRST_SCAN_LINK;
>  }
>  
> -static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
> -				 struct ieee80211_vif *vif,
> -				 struct ieee80211_scan_request *hw_req)
> +static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
> +				       struct ieee80211_vif *vif,
> +				       struct ieee80211_scan_request *hw_req,
> +				       int n_channels,
> +				       struct ieee80211_channel **chan_list,
> +				       struct ath12k *ar)
>  {
>  	struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
> -	struct ath12k *ar;
>  	struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
>  	struct ath12k_link_vif *arvif;
>  	struct cfg80211_scan_request *req = &hw_req->req;
> @@ -4525,13 +4544,6 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
>  
>  	arvif = &ahvif->deflink;
>  
> -	/* Since the targeted scan device could depend on the frequency
> -	 * requested in the hw_req, select the corresponding radio
> -	 */
> -	ar = ath12k_mac_select_scan_device(hw, vif, hw_req->req.channels[0]->center_freq);
> -	if (!ar)
> -		return -EINVAL;
> -
>  	/* check if any of the links of ML VIF is already started on
>  	 * radio(ar) corresponding to given scan frequency and use it,
>  	 * if not use scan link (link id >= 15) for scan purpose.
> @@ -4634,8 +4646,8 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
>  		arg->scan_f_passive = 1;
>  	}
>  
> -	if (req->n_channels) {
> -		arg->num_chan = req->n_channels;
> +	if (n_channels) {
> +		arg->num_chan = n_channels;
>  		arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list),
>  					 GFP_KERNEL);
>  		if (!arg->chan_list) {
> @@ -4644,7 +4656,7 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
>  		}
>  
>  		for (i = 0; i < arg->num_chan; i++)
> -			arg->chan_list[i] = req->channels[i]->center_freq;
> +			arg->chan_list[i] = chan_list[i]->center_freq;
>  	}
>  
>  	ret = ath12k_start_scan(ar, arg);
> @@ -4662,13 +4674,6 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
>  
>  	ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac scan started");
>  
> -	/* As per cfg80211/mac80211 scan design, it allows only one
> -	 * scan at a time. Hence last_scan link id is used for
> -	 * tracking the link id on which the scan is been done on
> -	 * this vif.
> -	 */
> -	ahvif->last_scan_link = arvif->link_id;
> -
>  	/* Add a margin to account for event/command processing */
>  	ieee80211_queue_delayed_work(ath12k_ar_to_hw(ar), &ar->scan.timeout,
>  				     msecs_to_jiffies(arg->max_scan_time +
> @@ -4689,25 +4694,108 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
>  	return ret;
>  }
>  
> +static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
> +				 struct ieee80211_vif *vif,
> +				 struct ieee80211_scan_request *hw_req)
> +{
> +	struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
> +	struct ieee80211_channel **chan_list, *chan;
> +	struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
> +	unsigned long links_map, link_id;
> +	struct ath12k_link_vif *arvif;
> +	struct ath12k *ar, *scan_ar;
> +	int i, j, ret = 0;
> +
> +	lockdep_assert_wiphy(hw->wiphy);
> +
> +	chan_list = kcalloc(hw_req->req.n_channels, sizeof(*chan_list), GFP_KERNEL);
> +	if (!chan_list)
> +		return -ENOMEM;
> +
> +	/* There could be channels that belong to multiple underlying radio
> +	 * in same scan request as mac80211 sees it as single band. In that
> +	 * case split the hw_req based on frequency range and schedule scans to
> +	 * corresponding radio.
> +	 */
> +	for_each_ar(ah, ar, i) {
> +		int n_chans = 0;
> +
> +		for (j = 0; j < hw_req->req.n_channels; j++) {
> +			chan = hw_req->req.channels[j];
> +			scan_ar = ath12k_mac_select_scan_device(hw, vif,
> +								chan->center_freq);
> +			if (!scan_ar) {
> +				ath12k_hw_warn(ah, "unable to select scan device for freq %d\n",
> +					       chan->center_freq);
> +				ret = -EINVAL;
> +				goto abort;
> +			}
> +			if (ar != scan_ar)
> +				continue;
> +
> +			chan_list[n_chans++] = chan;
> +		}
> +		if (n_chans) {
> +			ret = ath12k_mac_initiate_hw_scan(hw, vif, hw_req, n_chans,
> +							  chan_list, ar);
> +			if (ret)
> +				goto abort;
> +		}
> +	}
> +abort:
> +	/* If any of the parallel scans initiated fails, abort all and
> +	 * remove the scan interfaces created. Return complete scan
> +	 * failure as mac80211 assumes this as single scan request.
> +	 */
> +	if (ret) {
> +		ath12k_hw_warn(ah, "Scan failed %d , cleanup all scan vdevs\n", ret);
> +		links_map = ahvif->links_map;
> +		for_each_set_bit(link_id, &links_map, ATH12K_NUM_MAX_LINKS) {
> +			arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
> +			if (!arvif)
> +				continue;
> +
> +			ar = arvif->ar;
> +			if (ar->scan.arvif == arvif) {
> +				wiphy_work_cancel(hw->wiphy, &ar->scan.vdev_clean_wk);
> +				spin_lock_bh(&ar->data_lock);
> +				ar->scan.arvif = NULL;
> +				ar->scan.state = ATH12K_SCAN_IDLE;
> +				ar->scan_channel = NULL;
> +				ar->scan.roc_freq = 0;
> +				spin_unlock_bh(&ar->data_lock);
> +			}
> +			if (link_id >= ATH12K_FIRST_SCAN_LINK) {
> +				ath12k_mac_remove_link_interface(hw, arvif);
> +				ath12k_mac_unassign_link_vif(arvif);
> +			}
> +		}
> +	}
> +	kfree(chan_list);
> +	return ret;
> +}
> +
>  static void ath12k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw,
>  					 struct ieee80211_vif *vif)
>  {
>  	struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
> -	u16 link_id = ahvif->last_scan_link;
> +	unsigned long link_id, links_map = ahvif->links_map;
>  	struct ath12k_link_vif *arvif;
>  	struct ath12k *ar;
>  
>  	lockdep_assert_wiphy(hw->wiphy);
>  
> -	arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
> -	if (!arvif || arvif->is_started)
> -		return;
> +	for_each_set_bit(link_id, &links_map, ATH12K_NUM_MAX_LINKS) {
> +		arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
> +		if (!arvif || arvif->is_started)
> +			continue;
>  
> -	ar = arvif->ar;
> +		ar = arvif->ar;
>  
> -	ath12k_scan_abort(ar);
> +		ath12k_scan_abort(ar);
>  
> -	cancel_delayed_work_sync(&ar->scan.timeout);
> +		cancel_delayed_work_sync(&ar->scan.timeout);
> +	}
>  }
>  
>  static int ath12k_install_key(struct ath12k_link_vif *arvif,
> @@ -8917,7 +9005,7 @@ static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw,
>  					.aborted = true,
>  				};
>  
> -				ieee80211_scan_completed(ar->ah->hw, &info);
> +				ath12k_mac_scan_send_complete(ar, &info);
>  			}
>  
>  			ar->scan.state = ATH12K_SCAN_IDLE;

Reviewed-by: Mahendran P <quic_mahep at quicinc.com>




More information about the ath12k mailing list