]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: ath12k: implement remain on channel for P2P mode
authorKang Yang <quic_kangyang@quicinc.com>
Mon, 5 Feb 2024 17:03:29 +0000 (19:03 +0200)
committerKalle Valo <quic_kvalo@quicinc.com>
Wed, 7 Feb 2024 15:06:59 +0000 (17:06 +0200)
Implement remain on channel for p2p mode in ath12k_ops:
        ath12k_mac_op_remain_on_channel
        ath12k_mac_op_cancel_remain_on_channel

P2P device can trigger ROC scan. Then keep listening or sending management
frames on particular channels.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1

Signed-off-by: Kang Yang <quic_kangyang@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://msgid.link/20240130040303.370590-7-quic_kangyang@quicinc.com
drivers/net/wireless/ath/ath12k/core.c
drivers/net/wireless/ath/ath12k/mac.c
drivers/net/wireless/ath/ath12k/wmi.c

index b82550b35149ae4cccd883626e16357d1c195f1d..391b6fb2bd42674423c153c11cc52a905ff6837a 100644 (file)
@@ -988,6 +988,7 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
                ath12k_mac_drain_tx(ar);
                complete(&ar->scan.started);
                complete(&ar->scan.completed);
+               complete(&ar->scan.on_channel);
                complete(&ar->peer_assoc_done);
                complete(&ar->peer_delete_done);
                complete(&ar->install_key_done);
index ceb5be5a6a95b9cca91084e2d3f13a41ca340e9f..dbaef009e9724f46b005fdbfb85007e9daf81180 100644 (file)
@@ -7451,6 +7451,125 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
        sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
 }
 
+static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw,
+                                                 struct ieee80211_vif *vif)
+{
+       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
+       struct ath12k *ar;
+
+       ar = ath12k_ah_to_ar(ah);
+
+       mutex_lock(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       ar->scan.roc_notify = false;
+       spin_unlock_bh(&ar->data_lock);
+
+       ath12k_scan_abort(ar);
+
+       mutex_unlock(&ar->conf_mutex);
+
+       cancel_delayed_work_sync(&ar->scan.timeout);
+
+       return 0;
+}
+
+static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
+                                          struct ieee80211_vif *vif,
+                                          struct ieee80211_channel *chan,
+                                          int duration,
+                                          enum ieee80211_roc_type type)
+{
+       struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
+       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
+       struct ath12k_wmi_scan_req_arg arg;
+       struct ath12k *ar;
+       u32 scan_time_msec;
+       int ret;
+
+       ar = ath12k_ah_to_ar(ah);
+
+       mutex_lock(&ar->conf_mutex);
+       spin_lock_bh(&ar->data_lock);
+
+       switch (ar->scan.state) {
+       case ATH12K_SCAN_IDLE:
+               reinit_completion(&ar->scan.started);
+               reinit_completion(&ar->scan.completed);
+               reinit_completion(&ar->scan.on_channel);
+               ar->scan.state = ATH12K_SCAN_STARTING;
+               ar->scan.is_roc = true;
+               ar->scan.vdev_id = arvif->vdev_id;
+               ar->scan.roc_freq = chan->center_freq;
+               ar->scan.roc_notify = true;
+               ret = 0;
+               break;
+       case ATH12K_SCAN_STARTING:
+       case ATH12K_SCAN_RUNNING:
+       case ATH12K_SCAN_ABORTING:
+               ret = -EBUSY;
+               break;
+       }
+
+       spin_unlock_bh(&ar->data_lock);
+
+       if (ret)
+               goto exit;
+
+       scan_time_msec = hw->wiphy->max_remain_on_channel_duration * 2;
+
+       memset(&arg, 0, sizeof(arg));
+       ath12k_wmi_start_scan_init(ar, &arg);
+       arg.num_chan = 1;
+       arg.chan_list = kcalloc(arg.num_chan, sizeof(*arg.chan_list),
+                               GFP_KERNEL);
+       if (!arg.chan_list) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       arg.vdev_id = arvif->vdev_id;
+       arg.scan_id = ATH12K_SCAN_ID;
+       arg.chan_list[0] = chan->center_freq;
+       arg.dwell_time_active = scan_time_msec;
+       arg.dwell_time_passive = scan_time_msec;
+       arg.max_scan_time = scan_time_msec;
+       arg.scan_flags |= WMI_SCAN_FLAG_PASSIVE;
+       arg.burst_duration = duration;
+
+       ret = ath12k_start_scan(ar, &arg);
+       if (ret) {
+               ath12k_warn(ar->ab, "failed to start roc scan: %d\n", ret);
+
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.state = ATH12K_SCAN_IDLE;
+               spin_unlock_bh(&ar->data_lock);
+               goto free_chan_list;
+       }
+
+       ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ);
+       if (ret == 0) {
+               ath12k_warn(ar->ab, "failed to switch to channel for roc scan\n");
+               ret = ath12k_scan_stop(ar);
+               if (ret)
+                       ath12k_warn(ar->ab, "failed to stop scan: %d\n", ret);
+               ret = -ETIMEDOUT;
+               goto free_chan_list;
+       }
+
+       ieee80211_queue_delayed_work(hw, &ar->scan.timeout,
+                                    msecs_to_jiffies(duration));
+
+       ret = 0;
+
+free_chan_list:
+       kfree(arg.chan_list);
+exit:
+       mutex_unlock(&ar->conf_mutex);
+
+       return ret;
+}
+
 static const struct ieee80211_ops ath12k_ops = {
        .tx                             = ath12k_mac_op_tx,
        .wake_tx_queue                  = ieee80211_handle_wake_tx_queue,
@@ -7485,6 +7604,8 @@ static const struct ieee80211_ops ath12k_ops = {
        .get_survey                     = ath12k_mac_op_get_survey,
        .flush                          = ath12k_mac_op_flush,
        .sta_statistics                 = ath12k_mac_op_sta_statistics,
+       .remain_on_channel              = ath12k_mac_op_remain_on_channel,
+       .cancel_remain_on_channel       = ath12k_mac_op_cancel_remain_on_channel,
 };
 
 static void ath12k_mac_update_ch_list(struct ath12k *ar,
@@ -8012,6 +8133,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
        init_completion(&ar->bss_survey_done);
        init_completion(&ar->scan.started);
        init_completion(&ar->scan.completed);
+       init_completion(&ar->scan.on_channel);
 
        INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
        INIT_WORK(&ar->regd_update_work, ath12k_regd_update_work);
index ff086428fb0ca05511389f90ab410db9879918c8..37cfd607c0431c7c27dfdd429db065b686e8138c 100644 (file)
@@ -5059,6 +5059,10 @@ static void ath12k_wmi_event_scan_started(struct ath12k *ar)
                break;
        case ATH12K_SCAN_STARTING:
                ar->scan.state = ATH12K_SCAN_RUNNING;
+
+               if (ar->scan.is_roc)
+                       ieee80211_ready_on_channel(ath12k_ar_to_hw(ar));
+
                complete(&ar->scan.started);
                break;
        }
@@ -5143,6 +5147,10 @@ static void ath12k_wmi_event_scan_foreign_chan(struct ath12k *ar, u32 freq)
        case ATH12K_SCAN_RUNNING:
        case ATH12K_SCAN_ABORTING:
                ar->scan_channel = ieee80211_get_channel(hw->wiphy, freq);
+
+               if (ar->scan.is_roc && ar->scan.roc_freq == freq)
+                       complete(&ar->scan.on_channel);
+
                break;
        }
 }