blob: 542069c3d75ae1ad67ca6edf9f89fa2907bc2ff7 [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 main green ap function definitions
*/
#include "wlan_green_ap_main_i.h"
/*
* wlan_green_ap_ant_ps_reset() - Reset function
* @green_ap - green ap context
*
* Reset fiunction, so that Antenna Mask can come into effect.
* This applies for only few of the hardware chips
*
* Return: QDF_STATUS
*/
static QDF_STATUS wlan_green_ap_ant_ps_reset
(struct wlan_pdev_green_ap_ctx *green_ap_ctx)
{
struct wlan_lmac_if_green_ap_tx_ops *green_ap_tx_ops;
struct wlan_objmgr_pdev *pdev;
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
pdev = green_ap_ctx->pdev;
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_FAILURE;
}
if (!green_ap_tx_ops->reset_dev)
return QDF_STATUS_SUCCESS;
/*
* Add protection against green AP enabling interrupts
* when not valid or no VAPs exist
*/
if (wlan_util_is_vap_active(pdev) == QDF_STATUS_SUCCESS)
green_ap_tx_ops->reset_dev(pdev);
else
green_ap_err("Green AP tried to enable IRQs when invalid");
return QDF_STATUS_SUCCESS;
}
struct wlan_lmac_if_green_ap_tx_ops *
wlan_psoc_get_green_ap_tx_ops(struct wlan_pdev_green_ap_ctx *green_ap_ctx)
{
struct wlan_objmgr_psoc *psoc;
struct wlan_objmgr_pdev *pdev = green_ap_ctx->pdev;
if (!pdev) {
green_ap_err("pdev context obtained is NULL");
return NULL;
}
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
green_ap_err("pdev context obtained is NULL");
return NULL;
}
return &((psoc->soc_cb.tx_ops.green_ap_tx_ops));
}
bool wlan_is_egap_enabled(struct wlan_pdev_green_ap_ctx *green_ap_ctx)
{
struct wlan_green_ap_egap_params *egap_params;
if (!green_ap_ctx) {
green_ap_err("green ap context passed is NULL");
return QDF_STATUS_E_INVAL;
}
egap_params = &green_ap_ctx->egap_params;
if (egap_params->fw_egap_support &&
egap_params->host_enable_egap &&
egap_params->egap_feature_flags)
return true;
return false;
}
qdf_export_symbol(wlan_is_egap_enabled);
/**
* wlan_green_ap_ps_event_state_update() - Update PS state and event
* @pdev: pdev pointer
* @state: ps state
* @event: ps event
*
* @Return: Success or Failure
*/
static QDF_STATUS wlan_green_ap_ps_event_state_update(
struct wlan_pdev_green_ap_ctx *green_ap_ctx,
enum wlan_green_ap_ps_state state,
enum wlan_green_ap_ps_event event)
{
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
green_ap_ctx->ps_state = state;
green_ap_ctx->ps_event = event;
return QDF_STATUS_SUCCESS;
}
QDF_STATUS wlan_green_ap_state_mc(struct wlan_pdev_green_ap_ctx *green_ap_ctx,
enum wlan_green_ap_ps_event event)
{
struct wlan_lmac_if_green_ap_tx_ops *green_ap_tx_ops;
uint8_t pdev_id;
/*
* Remove the assignments once channel info is available for
* converged component.
*/
uint16_t channel = 1;
uint32_t channel_flags = 1;
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
if (!green_ap_ctx->pdev) {
green_ap_err("pdev obtained is NULL");
return QDF_STATUS_E_FAILURE;
}
pdev_id = wlan_objmgr_pdev_get_pdev_id(green_ap_ctx->pdev);
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_FAILURE;
}
if (!green_ap_tx_ops->ps_on_off_send) {
green_ap_err("tx op for sending enbale/disable green ap is NULL");
return QDF_STATUS_E_FAILURE;
}
qdf_spin_lock_bh(&green_ap_ctx->lock);
if (green_ap_tx_ops->get_current_channel)
channel = green_ap_tx_ops->get_current_channel(
green_ap_ctx->pdev);
if (green_ap_tx_ops->get_current_channel_flags)
channel_flags = green_ap_tx_ops->get_current_channel_flags(
green_ap_ctx->pdev);
/* handle the green ap ps event */
switch (event) {
case WLAN_GREEN_AP_ADD_STA_EVENT:
green_ap_ctx->num_nodes++;
break;
case WLAN_GREEN_AP_DEL_STA_EVENT:
if (green_ap_ctx->num_nodes)
green_ap_ctx->num_nodes--;
break;
case WLAN_GREEN_AP_PS_START_EVENT:
case WLAN_GREEN_AP_PS_STOP_EVENT:
case WLAN_GREEN_AP_PS_ON_EVENT:
case WLAN_GREEN_AP_PS_WAIT_EVENT:
break;
default:
green_ap_err("Invalid event: %d", event);
break;
}
green_ap_debug("Green-AP event: %d, state: %d, num_nodes: %d",
event, green_ap_ctx->ps_state, green_ap_ctx->num_nodes);
/* Confirm that power save is enabled before doing state transitions */
if (!green_ap_ctx->ps_enable) {
green_ap_debug("Green-AP is disabled");
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_IDLE_STATE,
WLAN_GREEN_AP_PS_WAIT_EVENT);
if (green_ap_ctx->ps_state == WLAN_GREEN_AP_PS_ON_STATE) {
if (green_ap_tx_ops->ps_on_off_send(green_ap_ctx->pdev,
false, pdev_id))
green_ap_err("failed to set green ap mode");
wlan_green_ap_ant_ps_reset(green_ap_ctx);
}
goto done;
}
/* handle the green ap ps state */
switch (green_ap_ctx->ps_state) {
case WLAN_GREEN_AP_PS_IDLE_STATE:
if (green_ap_ctx->num_nodes) {
/* Active nodes present, Switchoff the power save */
green_ap_info("Transition to OFF from IDLE");
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_OFF_STATE,
WLAN_GREEN_AP_PS_WAIT_EVENT);
} else {
/* No Active nodes, get into power save */
green_ap_info("Transition to WAIT from IDLE");
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_WAIT_STATE,
WLAN_GREEN_AP_PS_WAIT_EVENT);
qdf_timer_start(&green_ap_ctx->ps_timer,
green_ap_ctx->ps_trans_time);
}
break;
case WLAN_GREEN_AP_PS_OFF_STATE:
if (!green_ap_ctx->num_nodes) {
green_ap_info("Transition to WAIT from OFF");
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_WAIT_STATE,
WLAN_GREEN_AP_PS_WAIT_EVENT);
qdf_timer_start(&green_ap_ctx->ps_timer,
green_ap_ctx->ps_trans_time);
}
break;
case WLAN_GREEN_AP_PS_WAIT_STATE:
if (!green_ap_ctx->num_nodes) {
if ((channel == 0) || (channel_flags == 0)) {
/*
* Stay in the current state and restart the
* timer to check later.
*/
qdf_timer_start(&green_ap_ctx->ps_timer,
green_ap_ctx->ps_on_time);
} else {
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_ON_STATE,
WLAN_GREEN_AP_PS_WAIT_EVENT);
green_ap_info("Transition to ON from WAIT");
green_ap_tx_ops->ps_on_off_send(
green_ap_ctx->pdev, true, pdev_id);
wlan_green_ap_ant_ps_reset(green_ap_ctx);
if (green_ap_ctx->ps_on_time)
qdf_timer_start(&green_ap_ctx->ps_timer,
green_ap_ctx->ps_on_time);
}
} else {
green_ap_info("Transition to OFF from WAIT");
qdf_timer_stop(&green_ap_ctx->ps_timer);
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_OFF_STATE,
WLAN_GREEN_AP_PS_WAIT_EVENT);
}
break;
case WLAN_GREEN_AP_PS_ON_STATE:
if (green_ap_ctx->num_nodes) {
qdf_timer_stop(&green_ap_ctx->ps_timer);
if (green_ap_tx_ops->ps_on_off_send(
green_ap_ctx->pdev, false, pdev_id)) {
green_ap_err("Failed to set Green AP mode");
goto done;
}
wlan_green_ap_ant_ps_reset(green_ap_ctx);
green_ap_info("Transition to OFF from ON\n");
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_OFF_STATE,
WLAN_GREEN_AP_PS_WAIT_EVENT);
} else if ((green_ap_ctx->ps_event ==
WLAN_GREEN_AP_PS_WAIT_EVENT) &&
(green_ap_ctx->ps_on_time)) {
/* ps_on_time timeout, switch to ps wait */
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_WAIT_STATE,
WLAN_GREEN_AP_PS_ON_EVENT);
if (green_ap_tx_ops->ps_on_off_send(
green_ap_ctx->pdev, false, pdev_id)) {
green_ap_err("Failed to set Green AP mode");
goto done;
}
wlan_green_ap_ant_ps_reset(green_ap_ctx);
green_ap_info("Transition to WAIT from ON\n");
qdf_timer_start(&green_ap_ctx->ps_timer,
green_ap_ctx->ps_trans_time);
}
break;
default:
green_ap_err("invalid state %d", green_ap_ctx->ps_state);
wlan_green_ap_ps_event_state_update(
green_ap_ctx,
WLAN_GREEN_AP_PS_OFF_STATE,
WLAN_GREEN_AP_PS_WAIT_EVENT);
break;
}
done:
qdf_spin_unlock_bh(&green_ap_ctx->lock);
return QDF_STATUS_SUCCESS;
}
void wlan_green_ap_timer_fn(void *pdev)
{
struct wlan_pdev_green_ap_ctx *green_ap_ctx;
struct wlan_objmgr_pdev *pdev_ctx = (struct wlan_objmgr_pdev *)pdev;
if (!pdev_ctx) {
green_ap_err("pdev context passed is NULL");
return;
}
green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
pdev_ctx, WLAN_UMAC_COMP_GREEN_AP);
if (!green_ap_ctx) {
green_ap_err("green ap context obtained is NULL");
return;
}
wlan_green_ap_state_mc(green_ap_ctx, green_ap_ctx->ps_event);
}
void wlan_green_ap_check_mode(struct wlan_objmgr_pdev *pdev,
void *object,
void *arg)
{
struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object;
uint8_t *flag = (uint8_t *)arg;
wlan_vdev_obj_lock(vdev);
if (wlan_vdev_mlme_get_opmode(vdev) != QDF_SAP_MODE)
*flag = 1;
wlan_vdev_obj_unlock(vdev);
}