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@xxxxxxxxxxx> > Signed-off-by: Vignesh C <quic_vignc@xxxxxxxxxxx> > Signed-off-by: Rameshkumar Sundaram <rameshkumar.sundaram@xxxxxxxxxxxxxxxx> > --- > 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@xxxxxxxxxxx>