diff --git a/components/ipa/core/inc/wlan_ipa_core.h b/components/ipa/core/inc/wlan_ipa_core.h index 825ba55eee65..03608b47f98e 100644 --- a/components/ipa/core/inc/wlan_ipa_core.h +++ b/components/ipa/core/inc/wlan_ipa_core.h @@ -95,10 +95,21 @@ QDF_STATUS wlan_ipa_uc_enable_pipes(struct wlan_ipa_priv *ipa_ctx); /** * wlan_ipa_uc_disable_pipes() - Disable IPA uC pipes * @ipa_ctx: IPA context + * @force_disable: If true, immediately disable IPA pipes. If false, wait for + * pending IPA WLAN TX completions * * Return: QDF_STATUS */ -QDF_STATUS wlan_ipa_uc_disable_pipes(struct wlan_ipa_priv *ipa_ctx); +QDF_STATUS wlan_ipa_uc_disable_pipes(struct wlan_ipa_priv *ipa_ctx, + bool force_disable); + +/** + * wlan_ipa_is_tx_pending() - Check if IPA TX Completions are pending + * @ipa_ctx: IPA context + * + * Return: bool + */ +bool wlan_ipa_is_tx_pending(struct wlan_ipa_priv *ipa_ctx); /** * wlan_ipa_set_perf_level() - Set IPA performance level diff --git a/components/ipa/core/inc/wlan_ipa_main.h b/components/ipa/core/inc/wlan_ipa_main.h index e719001d60a3..6970a811ad6a 100644 --- a/components/ipa/core/inc/wlan_ipa_main.h +++ b/components/ipa/core/inc/wlan_ipa_main.h @@ -333,6 +333,14 @@ QDF_STATUS ipa_uc_ol_init(struct wlan_objmgr_pdev *pdev, */ QDF_STATUS ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev); +/** + * ipa_is_tx_pending() - Check if IPA WLAN TX completions are pending + * @pdev: pdev obj + * + * Return: bool if pending TX for IPA. + */ +bool ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev); + /** * ipa_send_mcc_scc_msg() - Send IPA WLAN_SWITCH_TO_MCC/SCC message * @pdev: pdev obj diff --git a/components/ipa/core/inc/wlan_ipa_priv.h b/components/ipa/core/inc/wlan_ipa_priv.h index f588d3c9941e..c56aee749c4b 100644 --- a/components/ipa/core/inc/wlan_ipa_priv.h +++ b/components/ipa/core/inc/wlan_ipa_priv.h @@ -607,12 +607,25 @@ struct wlan_ipa_priv { qdf_work_t pm_work; qdf_spinlock_t pm_lock; bool suspended; - qdf_spinlock_t q_lock; - - qdf_spinlock_t pipes_down_lock; + qdf_spinlock_t enable_disable_lock; + /* Flag to indicate wait on pending TX completions */ + qdf_atomic_t waiting_on_pending_tx; + /* Timer ticks to keep track of time after which pipes are disabled */ + uint64_t pending_tx_start_ticks; + /* Indicates if cdp_ipa_disable_autonomy is called for IPA pipes */ + qdf_atomic_t autonomy_disabled; + /* Indicates if cdp_disable_ipa_pipes has been called for IPA pipes */ + qdf_atomic_t pipes_disabled; + /* + * IPA pipes are considered "down" when both autonomy_disabled and + * ipa_pipes_disabled are set + */ + bool ipa_pipes_down; + /* Flag for mutual exclusion during IPA disable pipes */ bool pipes_down_in_progress; - + /* Flag for mutual exclusion during IPA enable pipes */ + bool pipes_enable_in_progress; qdf_list_node_t pend_desc_head; struct wlan_ipa_tx_desc *tx_desc_pool; qdf_list_t tx_desc_free_list; @@ -633,7 +646,6 @@ struct wlan_ipa_priv { struct ipa_uc_stas_map assoc_stas_map[WLAN_IPA_MAX_STA_COUNT]; qdf_list_t pending_event; qdf_mutex_t event_lock; - bool ipa_pipes_down; uint32_t ipa_tx_packets_diff; uint32_t ipa_rx_packets_diff; uint32_t ipa_p_tx_packets; diff --git a/components/ipa/core/src/wlan_ipa_core.c b/components/ipa/core/src/wlan_ipa_core.c index 9adf9b246257..a85c1eb0a3fc 100644 --- a/components/ipa/core/src/wlan_ipa_core.c +++ b/components/ipa/core/src/wlan_ipa_core.c @@ -28,6 +28,9 @@ #include "wlan_objmgr_vdev_obj.h" static struct wlan_ipa_priv *gp_ipa; +static void wlan_ipa_set_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx); +static void wlan_ipa_reset_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx); + static struct wlan_ipa_iface_2_client { qdf_ipa_client_type_t cons_client; @@ -1229,75 +1232,107 @@ QDF_STATUS wlan_ipa_resume(struct wlan_ipa_priv *ipa_ctx) QDF_STATUS wlan_ipa_uc_enable_pipes(struct wlan_ipa_priv *ipa_ctx) { int result; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; ipa_debug("enter"); - if (!ipa_ctx->ipa_pipes_down) { - /* - * IPA WDI Pipes are already activated due to - * rm deferred resources grant - */ - ipa_warn("IPA WDI Pipes are already activated"); - goto end; + qdf_spin_lock_bh(&ipa_ctx->enable_disable_lock); + if (ipa_ctx->pipes_enable_in_progress) { + ipa_warn("IPA Pipes Enable in progress"); + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); + return QDF_STATUS_E_ALREADY; } + ipa_ctx->pipes_enable_in_progress = true; + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); - result = cdp_ipa_enable_pipes(ipa_ctx->dp_soc, - ipa_ctx->dp_pdev); - if (result) { - ipa_err("Enable IPA WDI PIPE failed: ret=%d", result); - return QDF_STATUS_E_FAILURE; + if (qdf_atomic_read(&ipa_ctx->pipes_disabled)) { + result = cdp_ipa_enable_pipes(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev); + if (result) { + ipa_err("Enable IPA WDI PIPE failed: ret=%d", result); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + qdf_atomic_set(&ipa_ctx->pipes_disabled, 0); } qdf_event_reset(&ipa_ctx->ipa_resource_comp); - ipa_ctx->ipa_pipes_down = false; - - cdp_ipa_enable_autonomy(ipa_ctx->dp_soc, - ipa_ctx->dp_pdev); + if (qdf_atomic_read(&ipa_ctx->autonomy_disabled)) { + cdp_ipa_enable_autonomy(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev); + qdf_atomic_set(&ipa_ctx->autonomy_disabled, 0); + } end: - ipa_debug("exit: ipa_pipes_down=%d", ipa_ctx->ipa_pipes_down); + qdf_spin_lock_bh(&ipa_ctx->enable_disable_lock); + if (!qdf_atomic_read(&ipa_ctx->autonomy_disabled) && + !qdf_atomic_read(&ipa_ctx->pipes_disabled)) + ipa_ctx->ipa_pipes_down = false; - return QDF_STATUS_SUCCESS; + ipa_ctx->pipes_enable_in_progress = false; + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); + + ipa_debug("exit: ipa_pipes_down=%d", ipa_ctx->ipa_pipes_down); + return qdf_status; } -QDF_STATUS wlan_ipa_uc_disable_pipes(struct wlan_ipa_priv *ipa_ctx) +QDF_STATUS +wlan_ipa_uc_disable_pipes(struct wlan_ipa_priv *ipa_ctx, bool force_disable) { - int result; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; - ipa_debug("enter"); + ipa_debug("enter: force_disable %u autonomy_disabled %u pipes_disabled %u", + force_disable, + qdf_atomic_read(&ipa_ctx->autonomy_disabled), + qdf_atomic_read(&ipa_ctx->pipes_disabled)); - if (ipa_ctx->ipa_pipes_down) { + qdf_spin_lock_bh(&ipa_ctx->enable_disable_lock); + if (ipa_ctx->ipa_pipes_down || ipa_ctx->pipes_down_in_progress) { ipa_warn("IPA WDI Pipes are already deactivated"); - goto end; - } - - qdf_spin_lock_bh(&ipa_ctx->pipes_down_lock); - if (ipa_ctx->pipes_down_in_progress || ipa_ctx->ipa_pipes_down) { - ipa_warn("IPA WDI Pipes down already in progress"); - qdf_spin_unlock_bh(&ipa_ctx->pipes_down_lock); + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); return QDF_STATUS_E_ALREADY; } ipa_ctx->pipes_down_in_progress = true; - qdf_spin_unlock_bh(&ipa_ctx->pipes_down_lock); + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); - cdp_ipa_disable_autonomy(ipa_ctx->dp_soc, - ipa_ctx->dp_pdev); - result = cdp_ipa_disable_pipes(ipa_ctx->dp_soc, - ipa_ctx->dp_pdev); - if (result) { - ipa_err("Disable IPA WDI PIPE failed: ret=%d", result); - ipa_ctx->pipes_down_in_progress = false; - return QDF_STATUS_E_FAILURE; + if (!qdf_atomic_read(&ipa_ctx->autonomy_disabled)) { + cdp_ipa_disable_autonomy(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev); + qdf_atomic_set(&ipa_ctx->autonomy_disabled, 1); } - ipa_ctx->ipa_pipes_down = true; - ipa_ctx->pipes_down_in_progress = false; + if (!qdf_atomic_read(&ipa_ctx->pipes_disabled)) { + if (!force_disable) { + wlan_ipa_set_pending_tx_timer(ipa_ctx); + } else { + qdf_status = cdp_ipa_disable_pipes(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + ipa_err("Disable IPA WDI PIPE failed: ret=%u", + qdf_status); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + qdf_atomic_set(&ipa_ctx->pipes_disabled, 1); + wlan_ipa_reset_pending_tx_timer(ipa_ctx); + } + } end: - ipa_debug("exit: ipa_pipes_down=%d", ipa_ctx->ipa_pipes_down); + qdf_spin_lock_bh(&ipa_ctx->enable_disable_lock); + if (qdf_atomic_read(&ipa_ctx->pipes_disabled) && + qdf_atomic_read(&ipa_ctx->autonomy_disabled)) { + ipa_ctx->ipa_pipes_down = true; + } + ipa_ctx->pipes_down_in_progress = false; + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); - return QDF_STATUS_SUCCESS; + ipa_debug("exit: ipa_pipes_down %u autonomy_disabled %u pipes_disabled %u", + ipa_ctx->ipa_pipes_down, + qdf_atomic_read(&ipa_ctx->autonomy_disabled), + qdf_atomic_read(&ipa_ctx->pipes_disabled)); + return qdf_status; } /** @@ -1601,17 +1636,13 @@ static QDF_STATUS wlan_ipa_uc_handle_first_con(struct wlan_ipa_priv *ipa_ctx) return QDF_STATUS_SUCCESS; } -/** - * wlan_ipa_uc_handle_last_discon() - Handle last uC IPA disconnection - * @ipa_ctx: IPA context - * - * Return: None - */ -static void wlan_ipa_uc_handle_last_discon(struct wlan_ipa_priv *ipa_ctx) +static +void wlan_ipa_uc_handle_last_discon(struct wlan_ipa_priv *ipa_ctx, + bool force_disable) { ipa_debug("enter"); - wlan_ipa_uc_disable_pipes(ipa_ctx); + wlan_ipa_uc_disable_pipes(ipa_ctx, force_disable); ipa_debug("exit: IPA WDI Pipes deactivated"); } @@ -1620,6 +1651,56 @@ bool wlan_ipa_is_fw_wdi_activated(struct wlan_ipa_priv *ipa_ctx) { return !ipa_ctx->ipa_pipes_down; } + +/* Time(ms) to wait for pending TX comps after last SAP client disconnects */ +#define WLAN_IPA_TX_PENDING_TIMEOUT_MS 15000 + +static void wlan_ipa_set_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_ctx->pending_tx_start_ticks = qdf_system_ticks(); + qdf_atomic_set(&ipa_ctx->waiting_on_pending_tx, 1); + ipa_info("done. pending_tx_start_ticks %llu wait_on_pending %u", + ipa_ctx->pending_tx_start_ticks, + qdf_atomic_read(&ipa_ctx->waiting_on_pending_tx)); +} + +bool wlan_ipa_is_tx_pending(struct wlan_ipa_priv *ipa_ctx) +{ + bool ret = false; + uint64_t diff_ms = 0; + uint64_t current_ticks = 0; + + if (!qdf_atomic_read(&ipa_ctx->waiting_on_pending_tx)) { + ipa_debug("nothing pending"); + return false; + } + + current_ticks = qdf_system_ticks(); + + diff_ms = qdf_system_ticks_to_msecs(current_ticks - + ipa_ctx->pending_tx_start_ticks); + + if (diff_ms < WLAN_IPA_TX_PENDING_TIMEOUT_MS) { + ret = true; + } else { + ipa_debug("disabling pipes"); + wlan_ipa_uc_disable_pipes(ipa_ctx, true); + } + + ipa_debug("diff_ms %llu pending_tx_start_ticks %llu current_ticks %llu wait_on_pending %u", + diff_ms, ipa_ctx->pending_tx_start_ticks, current_ticks, + qdf_atomic_read(&ipa_ctx->waiting_on_pending_tx)); + + return ret; +} + +static void wlan_ipa_reset_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_ctx->pending_tx_start_ticks = 0; + qdf_atomic_set(&ipa_ctx->waiting_on_pending_tx, 0); + ipa_info("done"); +} + #else /** @@ -1673,10 +1754,13 @@ static QDF_STATUS wlan_ipa_uc_handle_first_con(struct wlan_ipa_priv *ipa_ctx) /** * wlan_ipa_uc_handle_last_discon() - Handle last uC IPA disconnection * @ipa_ctx: IPA context + * @force_disable: force IPA pipes disablement * * Return: None */ -static void wlan_ipa_uc_handle_last_discon(struct wlan_ipa_priv *ipa_ctx) +static +void wlan_ipa_uc_handle_last_discon(struct wlan_ipa_priv *ipa_ctx, + bool force_disable) { ipa_debug("enter"); @@ -1692,6 +1776,22 @@ bool wlan_ipa_is_fw_wdi_activated(struct wlan_ipa_priv *ipa_ctx) { return (WLAN_IPA_UC_NUM_WDI_PIPE == ipa_ctx->activated_fw_pipe); } + +static inline +void wlan_ipa_set_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx) +{ +} + +bool wlan_ipa_is_tx_pending(struct wlan_ipa_priv *ipa_ctx) +{ + return false; +} + +static inline +void wlan_ipa_reset_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx) +{ +} + #endif static inline @@ -2102,9 +2202,11 @@ static QDF_STATUS __wlan_ipa_wlan_evt(qdf_netdev_t net_dev, uint8_t device_mode, * message will not be processed when * unloading WLAN driver is in progress */ - wlan_ipa_uc_disable_pipes(ipa_ctx); + wlan_ipa_uc_disable_pipes(ipa_ctx, + true); } else { - wlan_ipa_uc_handle_last_discon(ipa_ctx); + wlan_ipa_uc_handle_last_discon(ipa_ctx, + true); } } } @@ -2151,7 +2253,7 @@ static QDF_STATUS __wlan_ipa_wlan_evt(qdf_netdev_t net_dev, uint8_t device_mode, * processed when unloading WLAN driver is in * progress */ - wlan_ipa_uc_disable_pipes(ipa_ctx); + wlan_ipa_uc_disable_pipes(ipa_ctx, true); } else { /* * This shouldn't happen : @@ -2159,7 +2261,7 @@ static QDF_STATUS __wlan_ipa_wlan_evt(qdf_netdev_t net_dev, uint8_t device_mode, * active - force close WDI pipes */ ipa_err("No interface left but WDI pipes are still active"); - wlan_ipa_uc_handle_last_discon(ipa_ctx); + wlan_ipa_uc_handle_last_discon(ipa_ctx, true); } } @@ -2337,9 +2439,17 @@ static QDF_STATUS __wlan_ipa_wlan_evt(qdf_netdev_t net_dev, uint8_t device_mode, * message will not be processed when * unloading WLAN driver is in progress */ - wlan_ipa_uc_disable_pipes(ipa_ctx); + + wlan_ipa_uc_disable_pipes(ipa_ctx, + true); } else { - wlan_ipa_uc_handle_last_discon(ipa_ctx); + /* + * If STA is connected, wait for IPA TX + * completions before disabling + * IPA pipes + */ + wlan_ipa_uc_handle_last_discon(ipa_ctx, + !ipa_ctx->sta_connected); wlan_ipa_uc_bw_monitor(ipa_ctx, true); } } @@ -2890,7 +3000,9 @@ QDF_STATUS wlan_ipa_setup(struct wlan_ipa_priv *ipa_ctx, qdf_create_work(0, &ipa_ctx->pm_work, wlan_ipa_pm_flush, ipa_ctx); qdf_spinlock_create(&ipa_ctx->pm_lock); qdf_spinlock_create(&ipa_ctx->q_lock); - qdf_spinlock_create(&ipa_ctx->pipes_down_lock); + qdf_spinlock_create(&ipa_ctx->enable_disable_lock); + ipa_ctx->pipes_down_in_progress = false; + ipa_ctx->pipes_enable_in_progress = false; qdf_nbuf_queue_init(&ipa_ctx->pm_queue_head); qdf_list_create(&ipa_ctx->pending_event, 1000); qdf_mutex_create(&ipa_ctx->event_lock); @@ -2915,7 +3027,8 @@ QDF_STATUS wlan_ipa_setup(struct wlan_ipa_priv *ipa_ctx, ipa_ctx->resource_unloading = false; ipa_ctx->sta_connected = 0; ipa_ctx->ipa_pipes_down = true; - ipa_ctx->pipes_down_in_progress = false; + qdf_atomic_set(&ipa_ctx->pipes_disabled, 1); + qdf_atomic_set(&ipa_ctx->autonomy_disabled, 1); ipa_ctx->wdi_enabled = false; /* Setup IPA system pipes */ if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) { @@ -2952,7 +3065,7 @@ fail_create_sys_pipe: fail_setup_rm: qdf_spinlock_destroy(&ipa_ctx->pm_lock); qdf_spinlock_destroy(&ipa_ctx->q_lock); - qdf_spinlock_destroy(&ipa_ctx->pipes_down_lock); + qdf_spinlock_destroy(&ipa_ctx->enable_disable_lock); for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { iface_context = &ipa_ctx->iface_context[i]; qdf_spinlock_destroy(&iface_context->interface_lock); @@ -3016,7 +3129,7 @@ QDF_STATUS wlan_ipa_cleanup(struct wlan_ipa_priv *ipa_ctx) qdf_spinlock_destroy(&ipa_ctx->pm_lock); qdf_spinlock_destroy(&ipa_ctx->q_lock); - qdf_spinlock_destroy(&ipa_ctx->pipes_down_lock); + qdf_spinlock_destroy(&ipa_ctx->enable_disable_lock); /* destroy the interface lock */ for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { @@ -3168,7 +3281,7 @@ static void wlan_ipa_uc_op_cb(struct op_msg_type *op_msg, qdf_mutex_acquire(&ipa_ctx->ipa_lock); if (msg->op_code == WLAN_IPA_UC_OPCODE_RX_SUSPEND) { - wlan_ipa_uc_disable_pipes(ipa_ctx); + wlan_ipa_uc_disable_pipes(ipa_ctx, true); ipa_info("Disable FW TX PIPE"); cdp_ipa_set_active(ipa_ctx->dp_soc, ipa_ctx->dp_pdev, false, true); @@ -3389,8 +3502,7 @@ QDF_STATUS wlan_ipa_uc_ol_deinit(struct wlan_ipa_priv *ipa_ctx) if (!wlan_ipa_uc_is_enabled(ipa_ctx->config)) return status; - if (!ipa_ctx->ipa_pipes_down) - wlan_ipa_uc_disable_pipes(ipa_ctx); + wlan_ipa_uc_disable_pipes(ipa_ctx, true); if (true == ipa_ctx->uc_loaded) { status = cdp_ipa_cleanup(ipa_ctx->dp_soc, diff --git a/components/ipa/core/src/wlan_ipa_main.c b/components/ipa/core/src/wlan_ipa_main.c index 424062b69f1b..fcdaf7a2d987 100644 --- a/components/ipa/core/src/wlan_ipa_main.c +++ b/components/ipa/core/src/wlan_ipa_main.c @@ -388,7 +388,7 @@ void ipa_uc_force_pipe_shutdown(struct wlan_objmgr_pdev *pdev) return; } - wlan_ipa_uc_disable_pipes(ipa_obj); + wlan_ipa_uc_disable_pipes(ipa_obj, true); } void ipa_flush(struct wlan_objmgr_pdev *pdev) @@ -464,6 +464,20 @@ QDF_STATUS ipa_uc_ol_init(struct wlan_objmgr_pdev *pdev, return wlan_ipa_uc_ol_init(ipa_obj, osdev); } +bool ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + + return wlan_ipa_is_tx_pending(ipa_obj); +} + QDF_STATUS ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev) { struct wlan_ipa_priv *ipa_obj; diff --git a/components/ipa/dispatcher/inc/wlan_ipa_ucfg_api.h b/components/ipa/dispatcher/inc/wlan_ipa_ucfg_api.h index c0cdc5eeac9f..2991d86b6647 100644 --- a/components/ipa/dispatcher/inc/wlan_ipa_ucfg_api.h +++ b/components/ipa/dispatcher/inc/wlan_ipa_ucfg_api.h @@ -241,6 +241,14 @@ QDF_STATUS ucfg_ipa_uc_ol_init(struct wlan_objmgr_pdev *pdev, */ QDF_STATUS ucfg_ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev); +/** + * ucfg_ipa_is_tx_pending() - Check if IPA WLAN TX completions are pending + * @pdev: pdev obj + * + * Return: bool if pending TX for IPA. + */ +bool ucfg_ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev); + /** * ucfg_ipa_send_mcc_scc_msg() - Send IPA WLAN_SWITCH_TO_MCC/SCC message * @mcc_mode: 0=MCC/1=SCC @@ -502,6 +510,11 @@ QDF_STATUS ucfg_ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev) return QDF_STATUS_SUCCESS; } +static inline bool ucfg_ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev) +{ + return false; +} + static inline QDF_STATUS ucfg_ipa_send_mcc_scc_msg(struct wlan_objmgr_pdev *pdev, bool mcc_mode) diff --git a/components/ipa/dispatcher/src/wlan_ipa_ucfg_api.c b/components/ipa/dispatcher/src/wlan_ipa_ucfg_api.c index aa65a5253ce6..8469702828cd 100644 --- a/components/ipa/dispatcher/src/wlan_ipa_ucfg_api.c +++ b/components/ipa/dispatcher/src/wlan_ipa_ucfg_api.c @@ -148,6 +148,11 @@ QDF_STATUS ucfg_ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev) return ipa_uc_ol_deinit(pdev); } +bool ucfg_ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev) +{ + return ipa_is_tx_pending(pdev); +} + QDF_STATUS ucfg_ipa_send_mcc_scc_msg(struct wlan_objmgr_pdev *pdev, bool mcc_mode) { diff --git a/core/hdd/src/wlan_hdd_driver_ops.c b/core/hdd/src/wlan_hdd_driver_ops.c index ceba7c3067c7..eda2ed0e8eab 100644 --- a/core/hdd/src/wlan_hdd_driver_ops.c +++ b/core/hdd/src/wlan_hdd_driver_ops.c @@ -1010,6 +1010,12 @@ static int __wlan_hdd_bus_suspend(struct wow_enable_params wow_params) return err; } + if (ucfg_ipa_is_tx_pending(hdd_ctx->pdev)) { + hdd_err("failed due to pending IPA TX comps"); + err = -EBUSY; + goto resume_cdp; + } + err = hif_bus_early_suspend(hif_ctx); if (err) { hdd_err("Failed hif bus early suspend");