qcacld-3.0: Add Suspend and Resume support in PMO
Add Suspend and Resume support in PMO. Change-Id: Iba16643adf1cbd997138c740345e40c00c8e81f6 Crs-Fixed: 2015366
This commit is contained in:
10
Kbuild
10
Kbuild
@@ -842,6 +842,8 @@ PMO_OBJS := $(PMO_DIR)/core/src/wlan_pmo_main.o \
|
|||||||
$(PMO_DIR)/core/src/wlan_pmo_mc_addr_filtering.o \
|
$(PMO_DIR)/core/src/wlan_pmo_mc_addr_filtering.o \
|
||||||
$(PMO_DIR)/core/src/wlan_pmo_static_config.o \
|
$(PMO_DIR)/core/src/wlan_pmo_static_config.o \
|
||||||
$(PMO_DIR)/core/src/wlan_pmo_wow.o \
|
$(PMO_DIR)/core/src/wlan_pmo_wow.o \
|
||||||
|
$(PMO_DIR)/core/src/wlan_pmo_lphb.o \
|
||||||
|
$(PMO_DIR)/core/src/wlan_pmo_suspend_resume.o \
|
||||||
$(PMO_DIR)/dispatcher/src/wlan_pmo_obj_mgmt_api.o \
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_obj_mgmt_api.o \
|
||||||
$(PMO_DIR)/dispatcher/src/wlan_pmo_ucfg_api.o \
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_ucfg_api.o \
|
||||||
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_arp.o \
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_arp.o \
|
||||||
@@ -849,7 +851,9 @@ PMO_OBJS := $(PMO_DIR)/core/src/wlan_pmo_main.o \
|
|||||||
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_gtk.o \
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_gtk.o \
|
||||||
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_wow.o \
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_wow.o \
|
||||||
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_static_config.o \
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_static_config.o \
|
||||||
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_mc_addr_filtering.o
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_mc_addr_filtering.o \
|
||||||
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_lphb.o \
|
||||||
|
$(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_suspend_resume.o
|
||||||
|
|
||||||
###### UMAC POLICY MGR ########
|
###### UMAC POLICY MGR ########
|
||||||
UMAC_POLICY_MGR_DIR := $(WLAN_COMMON_ROOT)/umac/cmn_services/policy_mgr
|
UMAC_POLICY_MGR_DIR := $(WLAN_COMMON_ROOT)/umac/cmn_services/policy_mgr
|
||||||
@@ -890,7 +894,9 @@ TARGET_IF_OBJ := $(TARGET_IF_DIR)/core/src/target_if_main.o \
|
|||||||
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_gtk.o \
|
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_gtk.o \
|
||||||
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_wow.o \
|
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_wow.o \
|
||||||
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_mc_addr_filtering.o \
|
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_mc_addr_filtering.o \
|
||||||
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_static_config.o
|
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_static_config.o \
|
||||||
|
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_lphb.o \
|
||||||
|
$(TARGET_IF_DIR)/pmo/src/target_if_pmo_suspend_resume.o
|
||||||
|
|
||||||
########### GLOBAL_LMAC_IF ##########
|
########### GLOBAL_LMAC_IF ##########
|
||||||
GLOBAL_LMAC_IF_DIR := $(WLAN_COMMON_ROOT)/global_lmac_if
|
GLOBAL_LMAC_IF_DIR := $(WLAN_COMMON_ROOT)/global_lmac_if
|
||||||
|
@@ -376,8 +376,10 @@ QDF_STATUS cds_open(struct wlan_objmgr_psoc *psoc)
|
|||||||
}
|
}
|
||||||
htcInfo.pContext = ol_ctx;
|
htcInfo.pContext = ol_ctx;
|
||||||
htcInfo.TargetFailure = ol_target_failure;
|
htcInfo.TargetFailure = ol_target_failure;
|
||||||
htcInfo.TargetSendSuspendComplete = wma_target_suspend_acknowledge;
|
htcInfo.TargetSendSuspendComplete =
|
||||||
htcInfo.target_initial_wakeup_cb = wma_handle_initial_wake_up;
|
pmo_ucfg_psoc_target_suspend_acknowledge;
|
||||||
|
htcInfo.target_initial_wakeup_cb = pmo_ucfg_psoc_handle_initial_wake_up;
|
||||||
|
htcInfo.target_psoc = (void *)psoc;
|
||||||
qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
|
qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
|
||||||
|
|
||||||
/* Create HTC */
|
/* Create HTC */
|
||||||
@@ -388,6 +390,7 @@ QDF_STATUS cds_open(struct wlan_objmgr_psoc *psoc)
|
|||||||
"%s: Failed to Create HTC", __func__);
|
"%s: Failed to Create HTC", __func__);
|
||||||
goto err_bmi_close;
|
goto err_bmi_close;
|
||||||
}
|
}
|
||||||
|
pmo_ucfg_psoc_update_htc_handle(psoc, (void *)gp_cds_context->htc_ctx);
|
||||||
|
|
||||||
if (bmi_done(ol_ctx)) {
|
if (bmi_done(ol_ctx)) {
|
||||||
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
|
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
|
||||||
@@ -837,7 +840,7 @@ static inline void cds_suspend_target(tp_wma_handle wma_handle)
|
|||||||
{
|
{
|
||||||
QDF_STATUS status;
|
QDF_STATUS status;
|
||||||
/* Suspend the target and disable interrupt */
|
/* Suspend the target and disable interrupt */
|
||||||
status = wma_suspend_target(wma_handle, 0);
|
status = pmo_ucfg_psoc_suspend_target(wma_handle->psoc, 0);
|
||||||
if (status)
|
if (status)
|
||||||
cds_err("Failed to suspend target, status = %d", status);
|
cds_err("Failed to suspend target, status = %d", status);
|
||||||
}
|
}
|
||||||
@@ -846,7 +849,7 @@ static inline void cds_suspend_target(tp_wma_handle wma_handle)
|
|||||||
{
|
{
|
||||||
QDF_STATUS status;
|
QDF_STATUS status;
|
||||||
/* Suspend the target and disable interrupt */
|
/* Suspend the target and disable interrupt */
|
||||||
status = wma_suspend_target(wma_handle, 1);
|
status = pmo_ucfg_psoc_suspend_target(wma_handle->psoc, 1);
|
||||||
if (status)
|
if (status)
|
||||||
cds_err("Failed to suspend target, status = %d", status);
|
cds_err("Failed to suspend target, status = %d", status);
|
||||||
}
|
}
|
||||||
@@ -917,6 +920,7 @@ QDF_STATUS cds_close(struct wlan_objmgr_psoc *psoc, v_CONTEXT_t cds_context)
|
|||||||
if (gp_cds_context->htc_ctx) {
|
if (gp_cds_context->htc_ctx) {
|
||||||
htc_stop(gp_cds_context->htc_ctx);
|
htc_stop(gp_cds_context->htc_ctx);
|
||||||
htc_destroy(gp_cds_context->htc_ctx);
|
htc_destroy(gp_cds_context->htc_ctx);
|
||||||
|
pmo_ucfg_psoc_update_htc_handle(psoc, NULL);
|
||||||
gp_cds_context->htc_ctx = NULL;
|
gp_cds_context->htc_ctx = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -549,6 +549,8 @@ static int __wlan_hdd_bus_suspend(pm_message_t state,
|
|||||||
int err;
|
int err;
|
||||||
int status;
|
int status;
|
||||||
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
|
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
|
||||||
|
QDF_STATUS qdf_status;
|
||||||
|
struct pmo_wow_enable_params *params = NULL;
|
||||||
|
|
||||||
hdd_info("starting bus suspend; event:%d", state.event);
|
hdd_info("starting bus suspend; event:%d", state.event);
|
||||||
|
|
||||||
@@ -576,7 +578,16 @@ static int __wlan_hdd_bus_suspend(pm_message_t state,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = wma_bus_suspend(wow_params);
|
params = (struct pmo_wow_enable_params *)qdf_mem_malloc(
|
||||||
|
sizeof(*params));
|
||||||
|
if (!params) {
|
||||||
|
hdd_err("params is Null");
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
qdf_status = pmo_ucfg_psoc_bus_suspend_req(hdd_ctx->hdd_psoc,
|
||||||
|
QDF_SYSTEM_SUSPEND, params);
|
||||||
|
err = qdf_status_to_os_return(qdf_status);
|
||||||
if (err) {
|
if (err) {
|
||||||
hdd_err("Failed wma bus suspend");
|
hdd_err("Failed wma bus suspend");
|
||||||
goto resume_oltxrx;
|
goto resume_oltxrx;
|
||||||
@@ -592,12 +603,15 @@ static int __wlan_hdd_bus_suspend(pm_message_t state,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
resume_wma:
|
resume_wma:
|
||||||
status = wma_bus_resume();
|
qdf_status = pmo_ucfg_psoc_bus_resume_req(hdd_ctx->hdd_psoc,
|
||||||
QDF_BUG(!status);
|
QDF_SYSTEM_SUSPEND);
|
||||||
|
QDF_BUG(!qdf_status);
|
||||||
resume_oltxrx:
|
resume_oltxrx:
|
||||||
status = cdp_bus_resume(soc);
|
status = cdp_bus_resume(soc);
|
||||||
QDF_BUG(!status);
|
QDF_BUG(!status);
|
||||||
done:
|
done:
|
||||||
|
if (params)
|
||||||
|
qdf_mem_free(params);
|
||||||
hdd_err("suspend failed, status = %d", err);
|
hdd_err("suspend failed, status = %d", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -666,7 +680,8 @@ static int __wlan_hdd_bus_suspend_noirq(void)
|
|||||||
if (err)
|
if (err)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
err = wma_is_target_wake_up_received();
|
err = pmo_ucfg_psoc_is_target_wake_up_received(
|
||||||
|
hdd_ctx->hdd_psoc);
|
||||||
if (err)
|
if (err)
|
||||||
goto resume_hif_noirq;
|
goto resume_hif_noirq;
|
||||||
|
|
||||||
@@ -749,7 +764,9 @@ static int __wlan_hdd_bus_resume(void)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = wma_bus_resume();
|
qdf_status = pmo_ucfg_psoc_bus_resume_req(hdd_ctx->hdd_psoc,
|
||||||
|
QDF_SYSTEM_SUSPEND);
|
||||||
|
status = qdf_status_to_os_return(qdf_status);
|
||||||
if (status) {
|
if (status) {
|
||||||
hdd_err("Failed wma bus resume");
|
hdd_err("Failed wma bus resume");
|
||||||
goto out;
|
goto out;
|
||||||
@@ -799,6 +816,7 @@ static int __wlan_hdd_bus_resume_noirq(void)
|
|||||||
hdd_context_t *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
|
hdd_context_t *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
|
||||||
void *hif_ctx;
|
void *hif_ctx;
|
||||||
int status;
|
int status;
|
||||||
|
QDF_STATUS qdf_status;
|
||||||
|
|
||||||
if (cds_is_driver_recovering())
|
if (cds_is_driver_recovering())
|
||||||
return 0;
|
return 0;
|
||||||
@@ -818,8 +836,8 @@ static int __wlan_hdd_bus_resume_noirq(void)
|
|||||||
if (NULL == hif_ctx)
|
if (NULL == hif_ctx)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
status = wma_clear_target_wake_up();
|
qdf_status = pmo_ucfg_psoc_clear_target_wake_up(hdd_ctx->hdd_psoc);
|
||||||
QDF_BUG(!status);
|
QDF_BUG(!qdf_status);
|
||||||
|
|
||||||
status = hif_bus_resume_noirq(hif_ctx);
|
status = hif_bus_resume_noirq(hif_ctx);
|
||||||
QDF_BUG(!status);
|
QDF_BUG(!status);
|
||||||
|
@@ -974,57 +974,6 @@ void hdd_disable_and_flush_mc_addr_list(hdd_adapter_t *adapter,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* hdd_conf_suspend_ind() - Send Suspend notification
|
|
||||||
* @pHddCtx: HDD Global context
|
|
||||||
* @pAdapter: adapter being suspended
|
|
||||||
* @callback: callback function to be called upon completion
|
|
||||||
* @callbackContext: callback context to be passed back to callback function
|
|
||||||
*
|
|
||||||
* Return: None.
|
|
||||||
*/
|
|
||||||
static void hdd_send_suspend_ind(hdd_context_t *pHddCtx,
|
|
||||||
uint32_t conn_state_mask,
|
|
||||||
void (*callback)(void *callbackContext,
|
|
||||||
bool suspended),
|
|
||||||
void *callbackContext)
|
|
||||||
{
|
|
||||||
QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
|
|
||||||
|
|
||||||
hdd_info("send wlan suspend indication");
|
|
||||||
|
|
||||||
qdf_ret_status =
|
|
||||||
sme_configure_suspend_ind(pHddCtx->hHal, conn_state_mask,
|
|
||||||
callback, callbackContext);
|
|
||||||
|
|
||||||
if (QDF_STATUS_SUCCESS != qdf_ret_status)
|
|
||||||
hdd_err("sme_configure_suspend_ind returned failure %d",
|
|
||||||
qdf_ret_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* hdd_conf_suspend_ind() - Send Resume notification
|
|
||||||
* @pAdapter: adapter being resumed
|
|
||||||
*
|
|
||||||
* Return: None.
|
|
||||||
*/
|
|
||||||
static void hdd_conf_resume_ind(hdd_adapter_t *pAdapter)
|
|
||||||
{
|
|
||||||
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
|
|
||||||
QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
|
|
||||||
|
|
||||||
qdf_ret_status = sme_configure_resume_req(pHddCtx->hHal, NULL);
|
|
||||||
|
|
||||||
if (QDF_STATUS_SUCCESS != qdf_ret_status) {
|
|
||||||
hdd_err("sme_configure_resume_req return failure %d", qdf_ret_status);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
hdd_notice("send wlan resume indication");
|
|
||||||
/* Disable supported OffLoads */
|
|
||||||
hdd_disable_host_offloads(pAdapter, pmo_apps_resume);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hdd_update_conn_state_mask(): record info needed by wma_suspend_req
|
* hdd_update_conn_state_mask(): record info needed by wma_suspend_req
|
||||||
* @adapter: adapter to get info from
|
* @adapter: adapter to get info from
|
||||||
@@ -1051,11 +1000,10 @@ hdd_update_conn_state_mask(hdd_adapter_t *adapter, uint32_t *conn_state_mask)
|
|||||||
* @callback: Callback function to invoke when driver is ready to suspend
|
* @callback: Callback function to invoke when driver is ready to suspend
|
||||||
* @callbackContext: Context to pass back to @callback function
|
* @callbackContext: Context to pass back to @callback function
|
||||||
*
|
*
|
||||||
* Return: None.
|
* Return: 0 on success else error code.
|
||||||
*/
|
*/
|
||||||
static void
|
static int
|
||||||
hdd_suspend_wlan(void (*callback)(void *callbackContext, bool suspended),
|
hdd_suspend_wlan(void)
|
||||||
void *callbackContext)
|
|
||||||
{
|
{
|
||||||
hdd_context_t *pHddCtx;
|
hdd_context_t *pHddCtx;
|
||||||
|
|
||||||
@@ -1068,18 +1016,22 @@ hdd_suspend_wlan(void (*callback)(void *callbackContext, bool suspended),
|
|||||||
pHddCtx = cds_get_context(QDF_MODULE_ID_HDD);
|
pHddCtx = cds_get_context(QDF_MODULE_ID_HDD);
|
||||||
if (!pHddCtx) {
|
if (!pHddCtx) {
|
||||||
hdd_alert("HDD context is Null");
|
hdd_alert("HDD context is Null");
|
||||||
return;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cds_is_driver_recovering()) {
|
if (cds_is_driver_recovering()) {
|
||||||
hdd_err("Recovery in Progress. State: 0x%x Ignore suspend!!!",
|
hdd_err("Recovery in Progress. State: 0x%x Ignore suspend!!!",
|
||||||
cds_get_driver_state());
|
cds_get_driver_state());
|
||||||
return;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
|
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
|
||||||
while (NULL != pAdapterNode && QDF_STATUS_SUCCESS == status) {
|
while (NULL != pAdapterNode && QDF_STATUS_SUCCESS == status) {
|
||||||
pAdapter = pAdapterNode->pAdapter;
|
pAdapter = pAdapterNode->pAdapter;
|
||||||
|
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
|
||||||
|
hdd_err("invalid session id: %d", pAdapter->sessionId);
|
||||||
|
goto next_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
/* stop all TX queues before suspend */
|
/* stop all TX queues before suspend */
|
||||||
hdd_notice("Disabling queues");
|
hdd_notice("Disabling queues");
|
||||||
@@ -1089,27 +1041,28 @@ hdd_suspend_wlan(void (*callback)(void *callbackContext, bool suspended),
|
|||||||
/* Configure supported OffLoads */
|
/* Configure supported OffLoads */
|
||||||
hdd_enable_host_offloads(pAdapter, pmo_apps_suspend);
|
hdd_enable_host_offloads(pAdapter, pmo_apps_suspend);
|
||||||
hdd_update_conn_state_mask(pAdapter, &conn_state_mask);
|
hdd_update_conn_state_mask(pAdapter, &conn_state_mask);
|
||||||
|
next_adapter:
|
||||||
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
|
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
|
||||||
|
|
||||||
pAdapterNode = pNext;
|
pAdapterNode = pNext;
|
||||||
}
|
}
|
||||||
|
|
||||||
hdd_send_suspend_ind(pHddCtx, conn_state_mask, callback,
|
status = pmo_ucfg_psoc_user_space_suspend_req(pHddCtx->hdd_psoc,
|
||||||
callbackContext);
|
QDF_SYSTEM_SUSPEND);
|
||||||
|
if (status != QDF_STATUS_SUCCESS)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
pHddCtx->hdd_wlan_suspended = true;
|
pHddCtx->hdd_wlan_suspended = true;
|
||||||
hdd_wlan_suspend_resume_event(HDD_WLAN_EARLY_SUSPEND);
|
hdd_wlan_suspend_resume_event(HDD_WLAN_EARLY_SUSPEND);
|
||||||
|
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hdd_resume_wlan() - Driver resume function
|
* hdd_resume_wlan() - Driver resume function
|
||||||
*
|
*
|
||||||
* Return: None.
|
* Return: 0 on success else error code.
|
||||||
*/
|
*/
|
||||||
static void hdd_resume_wlan(void)
|
static int hdd_resume_wlan(void)
|
||||||
{
|
{
|
||||||
hdd_context_t *pHddCtx;
|
hdd_context_t *pHddCtx;
|
||||||
hdd_adapter_t *pAdapter = NULL;
|
hdd_adapter_t *pAdapter = NULL;
|
||||||
@@ -1121,13 +1074,13 @@ static void hdd_resume_wlan(void)
|
|||||||
pHddCtx = cds_get_context(QDF_MODULE_ID_HDD);
|
pHddCtx = cds_get_context(QDF_MODULE_ID_HDD);
|
||||||
if (!pHddCtx) {
|
if (!pHddCtx) {
|
||||||
hdd_err("HDD context is Null");
|
hdd_err("HDD context is Null");
|
||||||
return;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cds_is_driver_recovering()) {
|
if (cds_is_driver_recovering()) {
|
||||||
hdd_err("Recovery in Progress. State: 0x%x Ignore resume!!!",
|
hdd_err("Recovery in Progress. State: 0x%x Ignore resume!!!",
|
||||||
cds_get_driver_state());
|
cds_get_driver_state());
|
||||||
return;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pHddCtx->hdd_wlan_suspended = false;
|
pHddCtx->hdd_wlan_suspended = false;
|
||||||
@@ -1138,6 +1091,12 @@ static void hdd_resume_wlan(void)
|
|||||||
|
|
||||||
while (NULL != pAdapterNode && QDF_STATUS_SUCCESS == status) {
|
while (NULL != pAdapterNode && QDF_STATUS_SUCCESS == status) {
|
||||||
pAdapter = pAdapterNode->pAdapter;
|
pAdapter = pAdapterNode->pAdapter;
|
||||||
|
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
|
||||||
|
hdd_err("invalid session id: %d", pAdapter->sessionId);
|
||||||
|
goto next_adapter;
|
||||||
|
}
|
||||||
|
/* Disable supported OffLoads */
|
||||||
|
hdd_disable_host_offloads(pAdapter, pmo_apps_resume);
|
||||||
|
|
||||||
/* wake the tx queues */
|
/* wake the tx queues */
|
||||||
hdd_info("Enabling queues");
|
hdd_info("Enabling queues");
|
||||||
@@ -1145,14 +1104,17 @@ static void hdd_resume_wlan(void)
|
|||||||
WLAN_WAKE_ALL_NETIF_QUEUE,
|
WLAN_WAKE_ALL_NETIF_QUEUE,
|
||||||
WLAN_CONTROL_PATH);
|
WLAN_CONTROL_PATH);
|
||||||
|
|
||||||
hdd_conf_resume_ind(pAdapter);
|
next_adapter:
|
||||||
|
|
||||||
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
|
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
|
||||||
pAdapterNode = pNext;
|
pAdapterNode = pNext;
|
||||||
}
|
}
|
||||||
hdd_ipa_resume(pHddCtx);
|
hdd_ipa_resume(pHddCtx);
|
||||||
|
status = pmo_ucfg_psoc_user_space_resume_req(pHddCtx->hdd_psoc,
|
||||||
|
QDF_SYSTEM_SUSPEND);
|
||||||
|
if (status != QDF_STATUS_SUCCESS)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1631,6 +1593,11 @@ static int __wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy)
|
|||||||
|
|
||||||
pld_request_bus_bandwidth(pHddCtx->parent_dev, PLD_BUS_WIDTH_MEDIUM);
|
pld_request_bus_bandwidth(pHddCtx->parent_dev, PLD_BUS_WIDTH_MEDIUM);
|
||||||
|
|
||||||
|
status = hdd_resume_wlan();
|
||||||
|
if (status != QDF_STATUS_SUCCESS) {
|
||||||
|
exit_code = 0;
|
||||||
|
goto exit_with_code;
|
||||||
|
}
|
||||||
/* Resume control path scheduler */
|
/* Resume control path scheduler */
|
||||||
if (pHddCtx->is_scheduler_suspended) {
|
if (pHddCtx->is_scheduler_suspended) {
|
||||||
scheduler_resume();
|
scheduler_resume();
|
||||||
@@ -1643,7 +1610,6 @@ static int __wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy)
|
|||||||
pHddCtx->is_ol_rx_thread_suspended = false;
|
pHddCtx->is_ol_rx_thread_suspended = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
hdd_resume_wlan();
|
|
||||||
|
|
||||||
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
|
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
|
||||||
TRACE_CODE_HDD_CFG80211_RESUME_WLAN,
|
TRACE_CODE_HDD_CFG80211_RESUME_WLAN,
|
||||||
@@ -1697,21 +1663,6 @@ exit_with_code:
|
|||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* wlan_hdd_cfg80211_ready_to_suspend() - set cfg80211 ready to suspend event
|
|
||||||
* @callbackContext: Pointer to callback context
|
|
||||||
* @suspended: Suspend flag
|
|
||||||
*
|
|
||||||
* Return: none
|
|
||||||
*/
|
|
||||||
static void wlan_hdd_cfg80211_ready_to_suspend(void *callbackContext,
|
|
||||||
bool suspended)
|
|
||||||
{
|
|
||||||
hdd_context_t *pHddCtx = (hdd_context_t *) callbackContext;
|
|
||||||
pHddCtx->suspended = suspended;
|
|
||||||
complete(&pHddCtx->ready_to_suspend);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wlan_hdd_cfg80211_resume_wlan() - cfg80211 resume callback
|
* wlan_hdd_cfg80211_resume_wlan() - cfg80211 resume callback
|
||||||
* @wiphy: Pointer to wiphy
|
* @wiphy: Pointer to wiphy
|
||||||
@@ -1878,24 +1829,6 @@ next_adapter:
|
|||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for the target to be ready for suspend */
|
|
||||||
INIT_COMPLETION(pHddCtx->ready_to_suspend);
|
|
||||||
|
|
||||||
hdd_suspend_wlan(&wlan_hdd_cfg80211_ready_to_suspend, pHddCtx);
|
|
||||||
|
|
||||||
rc = wait_for_completion_timeout(&pHddCtx->ready_to_suspend,
|
|
||||||
msecs_to_jiffies(WLAN_WAIT_TIME_READY_TO_SUSPEND));
|
|
||||||
if (!rc) {
|
|
||||||
hdd_err("Failed to get ready to suspend");
|
|
||||||
goto resume_tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pHddCtx->suspended) {
|
|
||||||
hdd_err("Faied as suspend_status is wrong:%d",
|
|
||||||
pHddCtx->suspended);
|
|
||||||
goto resume_tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Suspend control path scheduler */
|
/* Suspend control path scheduler */
|
||||||
scheduler_register_hdd_suspend_callback(hdd_suspend_cb);
|
scheduler_register_hdd_suspend_callback(hdd_suspend_cb);
|
||||||
scheduler_set_event_mask(MC_SUSPEND_EVENT_MASK);
|
scheduler_set_event_mask(MC_SUSPEND_EVENT_MASK);
|
||||||
@@ -1909,7 +1842,6 @@ next_adapter:
|
|||||||
hdd_err("Failed to stop mc thread");
|
hdd_err("Failed to stop mc thread");
|
||||||
goto resume_tx;
|
goto resume_tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
pHddCtx->is_scheduler_suspended = true;
|
pHddCtx->is_scheduler_suspended = true;
|
||||||
|
|
||||||
#ifdef QCA_CONFIG_SMP
|
#ifdef QCA_CONFIG_SMP
|
||||||
@@ -1928,6 +1860,9 @@ next_adapter:
|
|||||||
}
|
}
|
||||||
pHddCtx->is_ol_rx_thread_suspended = true;
|
pHddCtx->is_ol_rx_thread_suspended = true;
|
||||||
#endif
|
#endif
|
||||||
|
if (hdd_suspend_wlan() < 0)
|
||||||
|
goto resume_all;
|
||||||
|
|
||||||
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
|
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
|
||||||
TRACE_CODE_HDD_CFG80211_SUSPEND_WLAN,
|
TRACE_CODE_HDD_CFG80211_SUSPEND_WLAN,
|
||||||
NO_SESSION, pHddCtx->isWiphySuspended));
|
NO_SESSION, pHddCtx->isWiphySuspended));
|
||||||
|
@@ -498,12 +498,6 @@ QDF_STATUS sme_deregister_mgmt_frame(tHalHandle hHal, uint8_t sessionId,
|
|||||||
uint16_t frameType, uint8_t *matchData,
|
uint16_t frameType, uint8_t *matchData,
|
||||||
uint16_t matchLen);
|
uint16_t matchLen);
|
||||||
QDF_STATUS sme_ConfigureAppsCpuWakeupState(tHalHandle hHal, bool isAppsAwake);
|
QDF_STATUS sme_ConfigureAppsCpuWakeupState(tHalHandle hHal, bool isAppsAwake);
|
||||||
QDF_STATUS sme_configure_suspend_ind(tHalHandle hHal,
|
|
||||||
uint32_t conn_state_mask,
|
|
||||||
csr_readyToSuspendCallback,
|
|
||||||
void *callbackContext);
|
|
||||||
QDF_STATUS sme_configure_resume_req(tHalHandle hHal,
|
|
||||||
tpSirWlanResumeParam wlanResumeParam);
|
|
||||||
#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
|
#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
|
||||||
QDF_STATUS sme_configure_ext_wow(tHalHandle hHal,
|
QDF_STATUS sme_configure_ext_wow(tHalHandle hHal,
|
||||||
tpSirExtWoWParams wlanExtParams,
|
tpSirExtWoWParams wlanExtParams,
|
||||||
|
@@ -6501,109 +6501,6 @@ QDF_STATUS sme_p2p_set_ps(tHalHandle hHal, tP2pPsConfig *data)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
\fn sme_configure_suspend_ind
|
|
||||||
|
|
||||||
\brief
|
|
||||||
SME will pass this request to lower mac to Indicate that the wlan needs to
|
|
||||||
be suspended
|
|
||||||
|
|
||||||
\param
|
|
||||||
|
|
||||||
hHal - The handle returned by mac_open.
|
|
||||||
|
|
||||||
wlanSuspendParam- Depicts the wlan suspend params
|
|
||||||
|
|
||||||
csr_readyToSuspendCallback - Callback to be called when ready to suspend
|
|
||||||
event is received.
|
|
||||||
callback_context - Context associated with csr_readyToSuspendCallback.
|
|
||||||
|
|
||||||
\return QDF_STATUS
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------- */
|
|
||||||
QDF_STATUS sme_configure_suspend_ind(tHalHandle hHal,
|
|
||||||
uint32_t conn_state_mask,
|
|
||||||
csr_readyToSuspendCallback callback,
|
|
||||||
void *callback_context)
|
|
||||||
{
|
|
||||||
QDF_STATUS status = QDF_STATUS_SUCCESS;
|
|
||||||
QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
|
|
||||||
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
|
|
||||||
struct scheduler_msg message;
|
|
||||||
|
|
||||||
MTRACE(qdf_trace(QDF_MODULE_ID_SME,
|
|
||||||
TRACE_CODE_SME_RX_HDD_CONFIG_SUSPENDIND, NO_SESSION,
|
|
||||||
0));
|
|
||||||
|
|
||||||
pMac->readyToSuspendCallback = callback;
|
|
||||||
pMac->readyToSuspendContext = callback_context;
|
|
||||||
|
|
||||||
status = sme_acquire_global_lock(&pMac->sme);
|
|
||||||
if (QDF_IS_STATUS_SUCCESS(status)) {
|
|
||||||
/* serialize the req through MC thread */
|
|
||||||
message.bodyval = conn_state_mask;
|
|
||||||
message.type = WMA_WLAN_SUSPEND_IND;
|
|
||||||
MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG,
|
|
||||||
NO_SESSION, message.type));
|
|
||||||
qdf_status = scheduler_post_msg(QDF_MODULE_ID_WMA,
|
|
||||||
&message);
|
|
||||||
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
|
|
||||||
pMac->readyToSuspendCallback = NULL;
|
|
||||||
pMac->readyToSuspendContext = NULL;
|
|
||||||
status = QDF_STATUS_E_FAILURE;
|
|
||||||
}
|
|
||||||
sme_release_global_lock(&pMac->sme);
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
\fn sme_configure_resume_req
|
|
||||||
|
|
||||||
\brief
|
|
||||||
SME will pass this request to lower mac to Indicate that the wlan needs to
|
|
||||||
be Resumed
|
|
||||||
|
|
||||||
\param
|
|
||||||
|
|
||||||
hHal - The handle returned by mac_open.
|
|
||||||
|
|
||||||
wlanResumeParam- Depicts the wlan resume params
|
|
||||||
|
|
||||||
\return QDF_STATUS
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------- */
|
|
||||||
QDF_STATUS sme_configure_resume_req(tHalHandle hHal,
|
|
||||||
tpSirWlanResumeParam wlanResumeParam)
|
|
||||||
{
|
|
||||||
QDF_STATUS status = QDF_STATUS_SUCCESS;
|
|
||||||
QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
|
|
||||||
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
|
|
||||||
struct scheduler_msg message;
|
|
||||||
|
|
||||||
MTRACE(qdf_trace(QDF_MODULE_ID_SME,
|
|
||||||
TRACE_CODE_SME_RX_HDD_CONFIG_RESUMEREQ, NO_SESSION,
|
|
||||||
0));
|
|
||||||
status = sme_acquire_global_lock(&pMac->sme);
|
|
||||||
if (QDF_IS_STATUS_SUCCESS(status)) {
|
|
||||||
/* serialize the req through MC thread */
|
|
||||||
message.bodyptr = wlanResumeParam;
|
|
||||||
message.type = WMA_WLAN_RESUME_REQ;
|
|
||||||
MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG,
|
|
||||||
NO_SESSION, message.type));
|
|
||||||
qdf_status = scheduler_post_msg(QDF_MODULE_ID_WMA,
|
|
||||||
&message);
|
|
||||||
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
|
|
||||||
status = QDF_STATUS_E_FAILURE;
|
|
||||||
}
|
|
||||||
sme_release_global_lock(&pMac->sme);
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
|
#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
|
||||||
/**
|
/**
|
||||||
* sme_configure_ext_wow() - configure Extr WoW
|
* sme_configure_ext_wow() - configure Extr WoW
|
||||||
|
@@ -1479,7 +1479,6 @@ typedef struct {
|
|||||||
#endif
|
#endif
|
||||||
qdf_wake_lock_t wow_wake_lock;
|
qdf_wake_lock_t wow_wake_lock;
|
||||||
int wow_nack;
|
int wow_nack;
|
||||||
bool wow_initial_wake_up;
|
|
||||||
qdf_atomic_t is_wow_bus_suspended;
|
qdf_atomic_t is_wow_bus_suspended;
|
||||||
qdf_mc_timer_t wma_scan_comp_timer;
|
qdf_mc_timer_t wma_scan_comp_timer;
|
||||||
uint8_t dfs_phyerr_filter_offload;
|
uint8_t dfs_phyerr_filter_offload;
|
||||||
|
@@ -138,21 +138,6 @@ QDF_STATUS wma_get_wcnss_software_version(void *p_cds_gctx,
|
|||||||
int wma_runtime_suspend(struct wow_enable_params wow_params);
|
int wma_runtime_suspend(struct wow_enable_params wow_params);
|
||||||
int wma_runtime_resume(void);
|
int wma_runtime_resume(void);
|
||||||
|
|
||||||
int wma_bus_suspend(struct wow_enable_params wow_params);
|
|
||||||
int wma_is_target_wake_up_received(void);
|
|
||||||
int wma_clear_target_wake_up(void);
|
|
||||||
QDF_STATUS wma_suspend_target(WMA_HANDLE handle, int disable_target_intr);
|
|
||||||
void wma_target_suspend_acknowledge(void *context, bool wow_nack);
|
|
||||||
void wma_handle_initial_wake_up(void);
|
|
||||||
int wma_bus_resume(void);
|
|
||||||
QDF_STATUS wma_resume_target(WMA_HANDLE handle);
|
|
||||||
QDF_STATUS wma_disable_wow_in_fw(WMA_HANDLE handle);
|
|
||||||
QDF_STATUS wma_disable_d0wow_in_fw(WMA_HANDLE handle);
|
|
||||||
bool wma_is_wow_mode_selected(WMA_HANDLE handle);
|
|
||||||
QDF_STATUS wma_enable_wow_in_fw(WMA_HANDLE handle,
|
|
||||||
struct wow_enable_params wow_params);
|
|
||||||
QDF_STATUS wma_enable_d0wow_in_fw(WMA_HANDLE handle,
|
|
||||||
struct wow_enable_params wow_params);
|
|
||||||
bool wma_check_scan_in_progress(WMA_HANDLE handle);
|
bool wma_check_scan_in_progress(WMA_HANDLE handle);
|
||||||
void wma_set_peer_authorized_cb(void *wma_ctx, wma_peer_authorized_fp auth_cb);
|
void wma_set_peer_authorized_cb(void *wma_ctx, wma_peer_authorized_fp auth_cb);
|
||||||
QDF_STATUS wma_set_peer_param(void *wma_ctx, uint8_t *peer_addr,
|
QDF_STATUS wma_set_peer_param(void *wma_ctx, uint8_t *peer_addr,
|
||||||
|
@@ -964,18 +964,6 @@ int wma_wow_wakeup_host_event(void *handle, uint8_t *event,
|
|||||||
uint32_t len);
|
uint32_t len);
|
||||||
int wma_pdev_resume_event_handler(void *handle, uint8_t *event, uint32_t len);
|
int wma_pdev_resume_event_handler(void *handle, uint8_t *event, uint32_t len);
|
||||||
|
|
||||||
/**
|
|
||||||
* wma_get_wow_bus_suspend() - check is wow bus suspended or not
|
|
||||||
* @wma: wma handle
|
|
||||||
*
|
|
||||||
* Return: true/false
|
|
||||||
*/
|
|
||||||
static inline int wma_get_wow_bus_suspend(tp_wma_handle wma)
|
|
||||||
{
|
|
||||||
|
|
||||||
return qdf_atomic_read(&wma->is_wow_bus_suspended);
|
|
||||||
}
|
|
||||||
|
|
||||||
QDF_STATUS wma_resume_req(tp_wma_handle wma, enum qdf_suspend_type type);
|
QDF_STATUS wma_resume_req(tp_wma_handle wma, enum qdf_suspend_type type);
|
||||||
|
|
||||||
QDF_STATUS wma_wow_add_pattern(tp_wma_handle wma,
|
QDF_STATUS wma_wow_add_pattern(tp_wma_handle wma,
|
||||||
@@ -989,6 +977,7 @@ QDF_STATUS wma_wow_enter(tp_wma_handle wma, tpSirHalWowlEnterParams info);
|
|||||||
QDF_STATUS wma_wow_exit(tp_wma_handle wma, tpSirHalWowlExitParams info);
|
QDF_STATUS wma_wow_exit(tp_wma_handle wma, tpSirHalWowlExitParams info);
|
||||||
|
|
||||||
QDF_STATUS wma_suspend_req(tp_wma_handle wma, enum qdf_suspend_type type);
|
QDF_STATUS wma_suspend_req(tp_wma_handle wma, enum qdf_suspend_type type);
|
||||||
|
|
||||||
void wma_calculate_and_update_conn_state(tp_wma_handle wma);
|
void wma_calculate_and_update_conn_state(tp_wma_handle wma);
|
||||||
void wma_update_conn_state(tp_wma_handle wma, uint32_t conn_mask);
|
void wma_update_conn_state(tp_wma_handle wma, uint32_t conn_mask);
|
||||||
void wma_update_conn_state(tp_wma_handle wma, uint32_t conn_mask);
|
void wma_update_conn_state(tp_wma_handle wma, uint32_t conn_mask);
|
||||||
@@ -1121,7 +1110,6 @@ QDF_STATUS wma_process_ch_avoid_update_req(tp_wma_handle wma_handle,
|
|||||||
ch_avoid_update_req);
|
ch_avoid_update_req);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QDF_STATUS wma_suspend_target(WMA_HANDLE handle, int disable_target_intr);
|
|
||||||
|
|
||||||
#ifdef FEATURE_WLAN_TDLS
|
#ifdef FEATURE_WLAN_TDLS
|
||||||
|
|
||||||
|
@@ -1617,7 +1617,7 @@ int wma_mcc_vdev_tx_pause_evt_handler(void *handle, uint8_t *event,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wma_get_wow_bus_suspend(wma)) {
|
if (pmo_ucfg_get_wow_bus_suspend(wma->psoc)) {
|
||||||
WMA_LOGD(" Suspend is in progress: Pause/Unpause Tx is NoOp");
|
WMA_LOGD(" Suspend is in progress: Pause/Unpause Tx is NoOp");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1720,6 +1720,7 @@ static void wma_shutdown_notifier_cb(void *priv)
|
|||||||
|
|
||||||
qdf_event_set(&wma_handle->wma_resume_event);
|
qdf_event_set(&wma_handle->wma_resume_event);
|
||||||
wma_cleanup_vdev_resp_queue(wma_handle);
|
wma_cleanup_vdev_resp_queue(wma_handle);
|
||||||
|
pmo_ucfg_psoc_wakeup_host_event_received(wma_handle->psoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wma_version_info g_wmi_version_info;
|
struct wma_version_info g_wmi_version_info;
|
||||||
@@ -6575,10 +6576,6 @@ QDF_STATUS wma_mc_process_msg(void *cds_context, struct scheduler_msg *msg)
|
|||||||
wma_resume_req(wma_handle, QDF_RUNTIME_SUSPEND);
|
wma_resume_req(wma_handle, QDF_RUNTIME_SUSPEND);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WMA_WLAN_SUSPEND_IND:
|
|
||||||
wma_update_conn_state(wma_handle, msg->bodyval);
|
|
||||||
wma_suspend_req(wma_handle, QDF_SYSTEM_SUSPEND);
|
|
||||||
break;
|
|
||||||
case WMA_8023_MULTICAST_LIST_REQ:
|
case WMA_8023_MULTICAST_LIST_REQ:
|
||||||
wma_process_mcbc_set_filter_req(wma_handle,
|
wma_process_mcbc_set_filter_req(wma_handle,
|
||||||
(tpSirRcvFltMcAddrList) msg->bodyptr);
|
(tpSirRcvFltMcAddrList) msg->bodyptr);
|
||||||
@@ -6717,10 +6714,6 @@ QDF_STATUS wma_mc_process_msg(void *cds_context, struct scheduler_msg *msg)
|
|||||||
(tSirModemPowerStateInd *) msg->bodyptr);
|
(tSirModemPowerStateInd *) msg->bodyptr);
|
||||||
qdf_mem_free(msg->bodyptr);
|
qdf_mem_free(msg->bodyptr);
|
||||||
break;
|
break;
|
||||||
case WMA_WLAN_RESUME_REQ:
|
|
||||||
wma_resume_req(wma_handle, QDF_SYSTEM_SUSPEND);
|
|
||||||
break;
|
|
||||||
|
|
||||||
#ifdef WLAN_FEATURE_STATS_EXT
|
#ifdef WLAN_FEATURE_STATS_EXT
|
||||||
case WMA_STATS_EXT_REQUEST:
|
case WMA_STATS_EXT_REQUEST:
|
||||||
wma_stats_ext_req(wma_handle,
|
wma_stats_ext_req(wma_handle,
|
||||||
|
Reference in New Issue
Block a user