|
@@ -2,12 +2,14 @@
|
|
|
/*
|
|
|
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
|
|
|
*
|
|
|
- * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
|
|
|
+ * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
|
|
*/
|
|
|
|
|
|
#include "ipa_wdi3.h"
|
|
|
#include <linux/msm_ipa.h>
|
|
|
#include <linux/string.h>
|
|
|
+#include <linux/workqueue.h>
|
|
|
+#include <linux/sched.h>
|
|
|
#include "ipa_common_i.h"
|
|
|
#include "ipa_pm.h"
|
|
|
#include "ipa_i.h"
|
|
@@ -48,6 +50,20 @@
|
|
|
#define DEFAULT_INSTANCE_ID (-1)
|
|
|
#define INVALID_INSTANCE_ID (-2)
|
|
|
|
|
|
+/**
|
|
|
+ * Time delay value for delayed queue.
|
|
|
+ */
|
|
|
+#define QUEUE_DELAY_TIME 100
|
|
|
+
|
|
|
+/**
|
|
|
+ * No. of filters reserving at wlan for IPA-XR usecase.
|
|
|
+ */
|
|
|
+#define NO_OF_FILTERS 4
|
|
|
+
|
|
|
+static void ipa_xr_wdi_opt_dpath_rsrv_filter_wq_handler(struct work_struct *work);
|
|
|
+static struct workqueue_struct *wlan_flt_rsrv_wq = NULL;
|
|
|
+static DECLARE_DELAYED_WORK(wlan_flt_rsrv_handle, ipa_xr_wdi_opt_dpath_rsrv_filter_wq_handler);
|
|
|
+
|
|
|
struct ipa_wdi_intf_info {
|
|
|
char netdev_name[IPA_RESOURCE_NAME_MAX];
|
|
|
u8 hdr_len;
|
|
@@ -63,6 +79,7 @@ struct ipa_wdi_opt_dpath_info {
|
|
|
u32 hdr_len;
|
|
|
atomic_t rsrv_req;
|
|
|
atomic_t is_opt_dp_cb_registered;
|
|
|
+ atomic_t is_xr_opt_dp_cb_registered;
|
|
|
void *priv;
|
|
|
int ipa_ep_idx_tx, ipa_ep_idx_rx;
|
|
|
u32 ipa_pm_hdl;
|
|
@@ -85,6 +102,13 @@ struct ipa_wdi_context {
|
|
|
ipa_wdi_meter_notifier_cb wdi_notify;
|
|
|
#endif
|
|
|
};
|
|
|
+
|
|
|
+struct ipa_wdi_opt_dpath_add_flt_handle {
|
|
|
+ uint64_t filter_handle;
|
|
|
+};
|
|
|
+
|
|
|
+struct ipa_wdi_opt_dpath_add_flt_handle add_flt_hndl[4];
|
|
|
+
|
|
|
/**
|
|
|
* opt_dpath_info contains fn callbacks which are set by WLAN context and
|
|
|
* accessed by QMI context. To avoid race condition between these 2,
|
|
@@ -850,6 +874,23 @@ int ipa_wdi_enable_pipes_per_inst(ipa_wdi_hdl_t hdl)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_XR) {
|
|
|
+ if (wlan_flt_rsrv_wq == NULL) {
|
|
|
+ wlan_flt_rsrv_wq = create_singlethread_workqueue("wlan_flt_rsrv_wq");
|
|
|
+ if (wlan_flt_rsrv_wq) {
|
|
|
+ IPA_WDI_ERR("failed to create wq\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = queue_delayed_work(wlan_flt_rsrv_wq, &wlan_flt_rsrv_handle,
|
|
|
+ msecs_to_jiffies(QUEUE_DELAY_TIME));
|
|
|
+ if (ret) {
|
|
|
+ IPA_WDI_ERR("failed to queue delayed wq\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL(ipa_wdi_enable_pipes_per_inst);
|
|
@@ -1056,6 +1097,12 @@ int ipa_wdi_opt_dpath_register_flt_cb_per_inst(
|
|
|
opt_dpath_info[hdl].flt_add_cb = flt_add_cb;
|
|
|
opt_dpath_info[hdl].flt_rem_cb = flt_rem_cb;
|
|
|
|
|
|
+ if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_XR) {
|
|
|
+ IPADBG("wdi_xr_opt_dpath_register_flt_cb: callbacks registered.\n");
|
|
|
+ atomic_set(&opt_dpath_info[hdl].is_xr_opt_dp_cb_registered, 1);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
atomic_set(&opt_dpath_info[hdl].is_opt_dp_cb_registered, 1);
|
|
|
|
|
|
IPADBG("wdi_opt_dpath_register_flt_cb: callbacks registered.\n");
|
|
@@ -1096,6 +1143,11 @@ int ipa_wdi_opt_dpath_notify_flt_rsvd_per_inst
|
|
|
return -EPERM;
|
|
|
}
|
|
|
|
|
|
+ if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_XR) {
|
|
|
+ ipa3_ctx->ipa_xr_wdi_flt_rsv_status = is_success;
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
memset(&ind, 0, sizeof(ind));
|
|
|
ind.rsrv_filter_status.result = (is_success == true) ? IPA_QMI_RESULT_SUCCESS_V01:IPA_QMI_RESULT_FAILURE_V01;
|
|
|
ind.rsrv_filter_status.error = IPA_QMI_ERR_NONE_V01;
|
|
@@ -1130,6 +1182,8 @@ int ipa_wdi_opt_dpath_notify_flt_rlsd_per_inst
|
|
|
}
|
|
|
|
|
|
ret = ipa_pm_deferred_deactivate(ipa_wdi_ctx_list[0]->ipa_pm_hdl);
|
|
|
+ if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_XR)
|
|
|
+ return ret;
|
|
|
|
|
|
memset(&ind, 0, sizeof(ind));
|
|
|
ind.filter_removal_all_status.result =
|
|
@@ -1411,6 +1465,157 @@ int ipa_wdi_opt_dpath_remove_all_filter_req(
|
|
|
EXPORT_SYMBOL(ipa_wdi_opt_dpath_remove_all_filter_req);
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * ipa_xr_wdi_opt_dpath_rsrv_filter_req() - Sends WLAN DP filter reservation
|
|
|
+ * from IPA Driver to WLAN
|
|
|
+ *
|
|
|
+ * Returns: 0 on success, negative on failure
|
|
|
+ *
|
|
|
+ */
|
|
|
+int ipa_xr_wdi_opt_dpath_rsrv_filter_req(void)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ struct ipa_wdi_opt_dpath_flt_rsrv_cb_params rsrv_filter_req;
|
|
|
+
|
|
|
+ memset(&rsrv_filter_req, 0, sizeof(struct ipa_wdi_opt_dpath_flt_rsrv_cb_params));
|
|
|
+ if (!atomic_read(&opt_dpath_info[0].is_xr_opt_dp_cb_registered)) {
|
|
|
+ IPAERR("filter reserve cb not registered");
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (opt_dpath_info[0].ipa_ep_idx_tx <= 0 || opt_dpath_info[0].ipa_ep_idx_rx <= 0) {
|
|
|
+ IPA_WDI_ERR("Either TX/RX ep is not configured.\n");
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ipa_pm_activate_sync(opt_dpath_info[0].ipa_pm_hdl);
|
|
|
+ if (ret) {
|
|
|
+ IPA_WDI_DBG("fail to activate ipa pm\n");
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ rsrv_filter_req.num_filters = NO_OF_FILTERS;
|
|
|
+ ret = opt_dpath_info[0].flt_rsrv_cb(
|
|
|
+ opt_dpath_info[0].priv, &rsrv_filter_req);
|
|
|
+ if (!ret) {
|
|
|
+ atomic_set(&opt_dpath_info[0].rsrv_req, 1);
|
|
|
+ } else {
|
|
|
+ if (ipa_pm_deferred_deactivate(opt_dpath_info[0].ipa_pm_hdl))
|
|
|
+ IPA_WDI_DBG("fail to deactivate ipa pm\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ IPA_WDI_DBG("reserved filter callbacks called.\n");
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ipa_xr_wdi_opt_dpath_rsrv_filter_req);
|
|
|
+
|
|
|
+/**
|
|
|
+ * ipa_xr_wdi_opt_dpath_add_filter_req() - Sends WLAN DP filter info
|
|
|
+ * from IPA Driver to WLAN
|
|
|
+ * @req: [in] filter add parameters from IPA Driver
|
|
|
+ *
|
|
|
+ * Returns: 0 on success, negative on failure
|
|
|
+ *
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+int ipa_xr_wdi_opt_dpath_add_filter_req(struct ipa_wdi_opt_dpath_flt_add_cb_params *req,
|
|
|
+ u32 stream_id)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!atomic_read(&opt_dpath_info[0].is_xr_opt_dp_cb_registered)) {
|
|
|
+ IPAERR("filter add cb not registered");
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret =
|
|
|
+ opt_dpath_info[0].flt_add_cb
|
|
|
+ (opt_dpath_info[0].priv, req);
|
|
|
+ add_flt_hndl[stream_id].filter_handle = req->flt_info[0].out_hdl;
|
|
|
+ IPA_WDI_DBG("add filter callbacks called, stream id %d\n", stream_id);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ipa_xr_wdi_opt_dpath_add_filter_req);
|
|
|
+
|
|
|
+/**
|
|
|
+ * ipa_xr_wdi_opt_dpath_remove_filter_req() - Sends WLAN DP filter info
|
|
|
+ * from IPA Driver to WLAN
|
|
|
+ *
|
|
|
+ * Returns: 0 on success, negative on failure
|
|
|
+ *
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+int ipa_xr_wdi_opt_dpath_remove_filter_req(u32 stream_id)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ struct ipa_wdi_opt_dpath_flt_rem_cb_params flt_rem_req;
|
|
|
+
|
|
|
+ memset(&flt_rem_req, 0, sizeof(struct ipa_wdi_opt_dpath_flt_rem_cb_params));
|
|
|
+ if (!atomic_read(&opt_dpath_info[0].is_xr_opt_dp_cb_registered)) {
|
|
|
+ IPAERR("filter remove cb not registered");
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+
|
|
|
+ flt_rem_req.num_tuples = 1;
|
|
|
+ flt_rem_req.hdl_info[0] = add_flt_hndl[stream_id].filter_handle;
|
|
|
+
|
|
|
+ ret = opt_dpath_info[0].flt_rem_cb
|
|
|
+ (opt_dpath_info[0].priv, &flt_rem_req);
|
|
|
+ IPA_WDI_DBG("remove filter callbacks called, stream id %d\n", stream_id);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ipa_xr_wdi_opt_dpath_remove_filter_req);
|
|
|
+
|
|
|
+/**
|
|
|
+ * ipa_xr_wdi_opt_dpath_remove_all_filter_req() - Sends WLAN DP filter info
|
|
|
+ * from IPAs to WLAN
|
|
|
+ *
|
|
|
+ * Returns: 0 on success, negative on failure
|
|
|
+ *
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+int ipa_xr_wdi_opt_dpath_remove_all_filter_req(void)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!atomic_read(&opt_dpath_info[0].is_xr_opt_dp_cb_registered)) {
|
|
|
+ IPAERR("filter release cb not registered");
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!atomic_read(&opt_dpath_info[0].rsrv_req)) {
|
|
|
+ IPAERR("Reservation request not sent. IGNORE");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ atomic_set(&opt_dpath_info[0].rsrv_req, 0);
|
|
|
+
|
|
|
+ ret = opt_dpath_info[0].flt_rsrv_rel_cb(
|
|
|
+ opt_dpath_info[0].priv);
|
|
|
+
|
|
|
+ if (opt_dpath_info[0].ipa_ep_idx_rx <= 0 || opt_dpath_info[0].ipa_ep_idx_tx <= 0) {
|
|
|
+ IPA_WDI_ERR("Either RX ep or TX ep is not configured.\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ IPA_WDI_DBG("remove all filter callbacks called.\n");
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ipa_xr_wdi_opt_dpath_remove_all_filter_req);
|
|
|
+
|
|
|
+
|
|
|
+static void ipa_xr_wdi_opt_dpath_rsrv_filter_wq_handler(struct work_struct *work)
|
|
|
+{
|
|
|
+ int res = 0;
|
|
|
+
|
|
|
+ res = ipa_xr_wdi_opt_dpath_rsrv_filter_req();
|
|
|
+ if (!res)
|
|
|
+ IPAERR("Failed to reserve the filters in wlan\n");
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* clean up WDI IPA offload data path
|
|
|
*
|
|
@@ -1450,6 +1655,7 @@ int ipa_wdi_cleanup_per_inst(ipa_wdi_hdl_t hdl)
|
|
|
}
|
|
|
mutex_destroy(&ipa_wdi_ctx_list[hdl]->lock);
|
|
|
kfree(ipa_wdi_ctx_list[hdl]);
|
|
|
+ atomic_set(&opt_dpath_info[hdl].is_xr_opt_dp_cb_registered, 0);
|
|
|
atomic_set(&opt_dpath_info[hdl].is_opt_dp_cb_registered, 0);
|
|
|
opt_dpath_info[0].ipa_ep_idx_rx = 0;
|
|
|
opt_dpath_info[0].ipa_ep_idx_tx = 0;
|
|
@@ -1567,6 +1773,10 @@ int ipa_wdi_disconn_pipes_per_inst(ipa_wdi_hdl_t hdl)
|
|
|
return -EPERM;
|
|
|
}
|
|
|
|
|
|
+ if (wlan_flt_rsrv_wq) {
|
|
|
+ destroy_workqueue(wlan_flt_rsrv_wq);
|
|
|
+ wlan_flt_rsrv_wq = NULL;
|
|
|
+ }
|
|
|
|
|
|
if (ipa_wdi_ctx_list[hdl]->wdi_version >= IPA_WDI_1 &&
|
|
|
ipa_wdi_ctx_list[hdl]->wdi_version < IPA_WDI_3 &&
|