blob: d396e89d178ab5e05da98648a6fe79cee1da3aa3 [file] [log] [blame]
/*
* Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: This file contains green ap north bound interface definitions
*/
#include <wlan_green_ap_api.h>
#include <../../core/src/wlan_green_ap_main_i.h>
#include <wlan_objmgr_global_obj.h>
QDF_STATUS wlan_green_ap_get_capab(
struct wlan_objmgr_pdev *pdev)
{
struct wlan_lmac_if_green_ap_tx_ops *green_ap_tx_ops;
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(pdev,
WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
green_ap_tx_ops = wlan_psoc_get_green_ap_tx_ops(green_ap_ctx);
if (!green_ap_tx_ops) {
green_ap_err("green ap tx ops obtained are NULL");
return QDF_STATUS_E_EXISTS;
}
if (green_ap_tx_ops->get_capab)
return green_ap_tx_ops->get_capab(pdev);
return QDF_STATUS_SUCCESS;
}
/**
* wlan_green_ap_pdev_obj_create_notification() - called from objmgr when pdev
* is created
* @pdev: pdev context
* @arg: argument
*
* This function gets called from object manager when pdev is being created and
* creates green ap context and attach it to objmgr.
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
static QDF_STATUS wlan_green_ap_pdev_obj_create_notification(
struct wlan_objmgr_pdev *pdev, void *arg)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!pdev) {
green_ap_err("pdev context passed is NULL");
return QDF_STATUS_E_INVAL;
}
green_ap_ctx = qdf_mem_malloc(sizeof(*green_ap_ctx));
if (!green_ap_ctx) {
green_ap_err("Memory allocation for Green AP context failed!");
return QDF_STATUS_E_NOMEM;
}
green_ap_ctx->ps_state = WLAN_GREEN_AP_PS_IDLE_STATE;
green_ap_ctx->ps_event = WLAN_GREEN_AP_PS_WAIT_EVENT;
green_ap_ctx->num_nodes = 0;
green_ap_ctx->ps_on_time = WLAN_GREEN_AP_PS_ON_TIME * 1000;
green_ap_ctx->ps_trans_time = WLAN_GREEN_AP_PS_TRANS_TIME * 1000;
green_ap_ctx->pdev = pdev;
qdf_timer_init(NULL, &green_ap_ctx->ps_timer,
wlan_green_ap_timer_fn,
pdev, QDF_TIMER_TYPE_WAKE_APPS);
qdf_spinlock_create(&green_ap_ctx->lock);
if (wlan_objmgr_pdev_component_obj_attach(pdev,
WLAN_UMAC_COMP_GREEN_AP,
green_ap_ctx, QDF_STATUS_SUCCESS)
!= QDF_STATUS_SUCCESS) {
green_ap_err("Failed to attach green ap ctx in pdev ctx");
status = QDF_STATUS_E_FAILURE;
goto err_pdev_attach;
}
green_ap_info("Green AP creation successful, green ap ctx: %pK, pdev: %pK",
green_ap_ctx, pdev);
return QDF_STATUS_SUCCESS;
err_pdev_attach:
qdf_spinlock_destroy(&green_ap_ctx->lock);
qdf_timer_free(&green_ap_ctx->ps_timer);
qdf_mem_free(green_ap_ctx);
return status;
}
/**
* wlan_green_ap_pdev_obj_destroy_notification() - called from objmgr when
* pdev is destroyed
* @pdev: pdev context
* @arg: argument
*
* This function gets called from object manager when pdev is being destroyed
* and deletes green ap context and detach it from objmgr.
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
static QDF_STATUS wlan_green_ap_pdev_obj_destroy_notification(
struct wlan_objmgr_pdev *pdev, void *arg)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
if (!pdev) {
green_ap_err("pdev context passed is NULL");
return QDF_STATUS_E_INVAL;
}
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
pdev, WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context is already NULL");
return QDF_STATUS_E_FAILURE;
}
green_ap_info("Deleting green ap pdev obj, green ap ctx: %pK, pdev: %pK",
green_ap_ctx, pdev);
if (wlan_objmgr_pdev_component_obj_detach(pdev,
WLAN_UMAC_COMP_GREEN_AP, green_ap_ctx) !=
QDF_STATUS_SUCCESS) {
green_ap_err("Failed to detach green ap ctx in psoc ctx");
return QDF_STATUS_E_FAILURE;
}
qdf_timer_free(&green_ap_ctx->ps_timer);
qdf_spinlock_destroy(&green_ap_ctx->lock);
qdf_mem_free(green_ap_ctx);
green_ap_info("green ap deletion successful, pdev: %pK", pdev);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS wlan_green_ap_init(void)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
status = wlan_objmgr_register_pdev_create_handler(
WLAN_UMAC_COMP_GREEN_AP,
wlan_green_ap_pdev_obj_create_notification,
NULL);
if (status != QDF_STATUS_SUCCESS) {
green_ap_err("Failed to register green ap obj create handler");
goto err_pdev_create;
}
status = wlan_objmgr_register_pdev_destroy_handler(
WLAN_UMAC_COMP_GREEN_AP,
wlan_green_ap_pdev_obj_destroy_notification,
NULL);
if (status != QDF_STATUS_SUCCESS) {
green_ap_err("Failed to register green ap obj destroy handler");
goto err_pdev_delete;
}
green_ap_info("Successfully registered create and destroy handlers with objmgr");
return QDF_STATUS_SUCCESS;
err_pdev_delete:
wlan_objmgr_unregister_pdev_create_handler(
WLAN_UMAC_COMP_GREEN_AP,
wlan_green_ap_pdev_obj_create_notification,
NULL);
err_pdev_create:
return status;
}
QDF_STATUS wlan_green_ap_deinit(void)
{
if (wlan_objmgr_unregister_pdev_create_handler(
WLAN_UMAC_COMP_GREEN_AP,
wlan_green_ap_pdev_obj_create_notification,
NULL)
!= QDF_STATUS_SUCCESS) {
return QDF_STATUS_E_FAILURE;
}
if (wlan_objmgr_unregister_pdev_destroy_handler(
WLAN_UMAC_COMP_GREEN_AP,
wlan_green_ap_pdev_obj_destroy_notification,
NULL)
!= QDF_STATUS_SUCCESS) {
return QDF_STATUS_E_FAILURE;
}
green_ap_info("Successfully unregistered create and destroy handlers with objmgr");
return QDF_STATUS_SUCCESS;
}
QDF_STATUS wlan_green_ap_start(struct wlan_objmgr_pdev *pdev)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
if (!pdev) {
green_ap_err("pdev context passed is NULL");
return QDF_STATUS_E_INVAL;
}
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
pdev, WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
green_ap_debug("Green AP start received");
/* Make sure the start function does not get called 2 times */
qdf_spin_lock_bh(&green_ap_ctx->lock);
if (wlan_is_egap_enabled(green_ap_ctx)) {
qdf_spin_unlock_bh(&green_ap_ctx->lock);
green_ap_debug("enhanced green ap support is enabled");
return QDF_STATUS_SUCCESS;
}
if (green_ap_ctx->ps_state == WLAN_GREEN_AP_PS_IDLE_STATE) {
if (green_ap_ctx->ps_enable) {
qdf_spin_unlock_bh(&green_ap_ctx->lock);
return wlan_green_ap_state_mc(green_ap_ctx,
WLAN_GREEN_AP_PS_START_EVENT);
}
}
qdf_spin_unlock_bh(&green_ap_ctx->lock);
return QDF_STATUS_E_ALREADY;
}
QDF_STATUS wlan_green_ap_stop(struct wlan_objmgr_pdev *pdev)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
if (!pdev) {
green_ap_err("pdev context passed is NULL");
return QDF_STATUS_E_INVAL;
}
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
pdev, WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
green_ap_debug("Green AP stop received");
qdf_spin_lock_bh(&green_ap_ctx->lock);
if (wlan_is_egap_enabled(green_ap_ctx)) {
qdf_spin_unlock_bh(&green_ap_ctx->lock);
green_ap_debug("enhanced green ap support is enabled");
return QDF_STATUS_SUCCESS;
}
/* Delete the timer just to be sure */
qdf_timer_stop(&green_ap_ctx->ps_timer);
/* Disable the power save */
green_ap_ctx->ps_enable = WLAN_GREEN_AP_PS_DISABLE;
qdf_spin_unlock_bh(&green_ap_ctx->lock);
return wlan_green_ap_state_mc(green_ap_ctx,
WLAN_GREEN_AP_PS_STOP_EVENT);
}
QDF_STATUS wlan_green_ap_add_sta(struct wlan_objmgr_pdev *pdev)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
if (!pdev) {
green_ap_err("pdev context passed is NULL");
return QDF_STATUS_E_INVAL;
}
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
pdev, WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
green_ap_debug("Green AP add sta received");
qdf_spin_lock_bh(&green_ap_ctx->lock);
if (wlan_is_egap_enabled(green_ap_ctx)) {
qdf_spin_unlock_bh(&green_ap_ctx->lock);
green_ap_debug("enhanced green ap support is enabled");
return QDF_STATUS_SUCCESS;
}
qdf_spin_unlock_bh(&green_ap_ctx->lock);
return wlan_green_ap_state_mc(green_ap_ctx,
WLAN_GREEN_AP_ADD_STA_EVENT);
}
QDF_STATUS wlan_green_ap_del_sta(struct wlan_objmgr_pdev *pdev)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
if (!pdev) {
green_ap_err("pdev context passed is NULL");
return QDF_STATUS_E_INVAL;
}
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
pdev, WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
green_ap_debug("Green AP del sta received");
qdf_spin_lock_bh(&green_ap_ctx->lock);
if (wlan_is_egap_enabled(green_ap_ctx)) {
qdf_spin_unlock_bh(&green_ap_ctx->lock);
green_ap_info("enhanced green ap support is enabled");
return QDF_STATUS_SUCCESS;
}
qdf_spin_unlock_bh(&green_ap_ctx->lock);
return wlan_green_ap_state_mc(green_ap_ctx,
WLAN_GREEN_AP_DEL_STA_EVENT);
}
bool wlan_green_ap_is_ps_enabled(struct wlan_objmgr_pdev *pdev)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
if (!pdev) {
green_ap_err("pdev context passed is NULL");
return QDF_STATUS_E_INVAL;
}
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
pdev, WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
if ((green_ap_ctx->ps_state == WLAN_GREEN_AP_PS_ON_STATE) &&
(green_ap_ctx->ps_enable))
return true;
return false;
}
void wlan_green_ap_suspend_handle(struct wlan_objmgr_pdev *pdev)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
if (!pdev) {
green_ap_err("pdev context passed is NULL");
return;
}
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
pdev, WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return;
}
wlan_green_ap_stop(pdev);
green_ap_ctx->ps_enable = WLAN_GREEN_AP_PS_SUSPEND;
}