[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