2025-05-10 21:58:58 +08:00

2065 lines
58 KiB
C

/******************************************************************************
*
* Copyright(c) 2021 Realtek Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*****************************************************************************/
#define _PHL_CMD_PS_C_
#include "phl_headers.h"
#ifdef CONFIG_POWER_SAVE
/* structure of a power request */
struct pwr_req {
_os_list list;
u16 evt_id;
};
struct rt_ps {
enum phl_ps_rt_rson rt_rson;
bool ps_allow;
};
/**
* +----------------------+-------------------+-----------------------+
* | ps_mode | ps_state | pwr_lvl |
* +----------------------+-------------------+-----------------------+
* | PS_MODE_LPS | PS_STATE_LEAVED | PS_PWR_LVL_PWRON |
* | +-------------------+-----------------------+
* | | PS_STATE_PROTO | PS_PWR_LVL_PWRON |
* | +-------------------+-----------------------+
* | | PS_STATE_ENTERED | PS_PWR_LVL_PWR_GATED |
* | | | PS_PWR_LVL_CLK_GATED |
* | | | PS_PWR_LVL_RF_OFF |
* +----------------------+-------------------+-----------------------+
* | PS_MODE_IPS | PS_STATE_LEAVED | PS_PWR_LVL_PWRON |
* | +-------------------+-----------------------+
* | | PS_STATE_ENTERED | PS_PWR_LVL_PWROFF |
* | | | PS_PWR_LVL_PWR_GATED |
* | | | PS_PWR_LVL_CLK_GATED |
* | | | PS_PWR_LVL_RF_OFF |
* +----------------------+-------------------+-----------------------+
*/
enum {
PS_STATE_NONE = 0,
PS_STATE_LEAVED,
/**
* lps: protocol only
* ips: won't in this state
*/
PS_STATE_PROTO,
/**
* lps: protocol + power
* ips: power
*/
PS_STATE_ENTERED
};
static const char *_ps_state_to_str(u8 ps_state)
{
switch (ps_state) {
case PS_STATE_ENTERED:
return "PS_ENTERED";
case PS_STATE_PROTO:
return "PS_PROTOCOL";
case PS_STATE_LEAVED:
return "PS_LEAVED";
default:
return "NONE";
}
}
struct _ps_mr_info {
bool ap_active;
bool gc_active;
};
#define CMD_PS_TIMER_PERIOD 100
#define MAX_PWE_REQ_NUM 16
struct cmd_ps {
struct phl_info_t *phl_info;
void *dispr;
_os_timer ps_timer;
struct phl_queue req_busy_q;
struct phl_queue req_idle_q;
struct pwr_req req_pool[MAX_PWE_REQ_NUM];
bool rej_pwr_req; /* reject all pwr request */
bool btc_req_pwr; /* btc request pwr or not */
bool stop_datapath;
/* current state */
u8 cur_pwr_lvl;
u8 ps_state;
u8 ps_mode;
char enter_rson[MAX_CMD_PS_RSON_LENGTH];
char leave_rson[MAX_CMD_PS_RSON_LENGTH];
struct rtw_phl_stainfo_t *sta; /* sta entering/leaving ps */
u16 macid;
/* lps */
u32 null_token;
bool wdg_leave_ps;
/* refs. enum "phl_ps_rt_rson" */
enum phl_ps_rt_rson rt_stop_rson;
struct _ps_mr_info mr_info;
/* rssi */
u8 rssi_bcn_min;
};
/**
* determine leave lps or not
* return true if rssi variation reach threshold
* @ps: see cmd_ps
*/
static bool _chk_rssi_diff_reach_thld(struct cmd_ps *ps)
{
struct phl_info_t *phl_info = ps->phl_info;
struct rtw_ps_cap_t *ps_cap = _get_ps_cap(ps->phl_info);
u8 cur_rssi_bcn_min = 0;
u8 *rssi_bcn_min = &ps->rssi_bcn_min;
bool leave_ps = false;
cur_rssi_bcn_min = phl_get_min_rssi_bcn(phl_info);
do {
if (*rssi_bcn_min == 0 || *rssi_bcn_min == 0xFF) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): (criteria = %d, cur = %d) criteria invalid, set criteria to cur\n",
__func__, *rssi_bcn_min, cur_rssi_bcn_min);
*rssi_bcn_min = cur_rssi_bcn_min; /* update with current_rssi */
break;
}
if (DIFF(*rssi_bcn_min, cur_rssi_bcn_min) < ps_cap->lps_rssi_diff_threshold) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): (criteria = %d, cur = %d) RSSI diff < %d, do nothing\n",
__func__, *rssi_bcn_min, cur_rssi_bcn_min, ps_cap->lps_rssi_diff_threshold);
break;
}
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): leave ps and update criteria from %d to %d\n", __func__, *rssi_bcn_min, cur_rssi_bcn_min);
leave_ps = true;
*rssi_bcn_min = cur_rssi_bcn_min;
} while (0);
return leave_ps;
}
/**
* determine leave lps or not
* return true if beacon offset changed
* @ps: see cmd_ps
*/
bool _chk_bcn_offset_changed(struct cmd_ps *ps)
{
struct phl_info_t *phl_info = ps->phl_info;
u8 ridx = MAX_WIFI_ROLE_NUMBER;
struct rtw_wifi_role_t *wrole = NULL;
struct rtw_bcn_offset *b_ofst_i = NULL;
bool leave_ps = false;
for (ridx = 0; ridx < MAX_WIFI_ROLE_NUMBER; ridx++) {
wrole = rtw_phl_get_wrole_by_ridx(phl_info->phl_com, ridx);
if (wrole == NULL)
continue;
if (rtw_phl_role_is_client_category(wrole) && wrole->mstate == MLME_LINKED) {
b_ofst_i = phl_get_sta_bcn_offset_info(phl_info, wrole);
if (b_ofst_i->conf_lvl >= CONF_LVL_MID &&
b_ofst_i->offset != b_ofst_i->cr_tbtt_shift) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): update ridx(%d) bcn offset from %d to %d TU\n",
__func__, ridx, b_ofst_i->cr_tbtt_shift, b_ofst_i->offset);
leave_ps = true;
break;
}
}
}
return leave_ps;
}
/**
* check whether to leave/enter lps
* return true if ps need to change to the target state
* @ps: see cmd_ps
* @mac_id: macid of corresponding sta
* @cur_state: currently lps state
* @target_state: the target ps state
*/
static bool
_lps_state_judge_changed(struct cmd_ps *ps, u16 macid, u8 cur_state, u8 target_state)
{
struct phl_info_t *phl_info = ps->phl_info;
struct rtw_ps_cap_t *ps_cap = _get_ps_cap(ps->phl_info);
struct rtw_stats *phl_stats = &phl_info->phl_com->phl_stats;
struct rtw_phl_stainfo_t *sta = NULL;
bool change_state = false;
u8 rssi = 0;
sta = rtw_phl_get_stainfo_by_macid(phl_info, macid);
if (sta == NULL) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): cannot get sta of macid %d.\n", __func__, macid);
return false;
}
rssi = rtw_hal_get_sta_rssi(sta);
if (target_state == PS_STATE_ENTERED) {
if (cur_state == PS_STATE_LEAVED || cur_state == PS_STATE_PROTO) {
if (rssi > ps_cap->lps_rssi_enter_threshold &&
phl_stats->tx_traffic.lvl == RTW_TFC_IDLE &&
phl_stats->rx_traffic.lvl == RTW_TFC_IDLE) {
change_state = true;
}
}
} else {
if (cur_state == PS_STATE_ENTERED || cur_state == PS_STATE_PROTO) {
if (_chk_rssi_diff_reach_thld(ps) ||
_chk_bcn_offset_changed(ps) ||
rssi < ps_cap->lps_rssi_leave_threshold ||
phl_stats->tx_traffic.lvl != RTW_TFC_IDLE ||
phl_stats->rx_traffic.lvl != RTW_TFC_IDLE) {
change_state = true;
}
}
}
if (change_state) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): state %s -> %s, Tx(%s), Rx(%s), RSSI(%d)\n",
__func__, _ps_state_to_str(cur_state), _ps_state_to_str(target_state),
phl_tfc_lvl_to_str(phl_stats->tx_traffic.lvl),
phl_tfc_lvl_to_str(phl_stats->rx_traffic.lvl), rssi);
}
return change_state;
}
static void _set_ps_rson(struct cmd_ps *ps, u8 enter, char *rson)
{
if (enter) {
_os_mem_set(phl_to_drvpriv(ps->phl_info), ps->enter_rson, 0, MAX_CMD_PS_RSON_LENGTH);
_os_mem_cpy(phl_to_drvpriv(ps->phl_info), ps->enter_rson, rson, MAX_CMD_PS_RSON_LENGTH);
} else {
_os_mem_set(phl_to_drvpriv(ps->phl_info), ps->leave_rson, 0, MAX_CMD_PS_RSON_LENGTH);
_os_mem_cpy(phl_to_drvpriv(ps->phl_info), ps->leave_rson, rson, MAX_CMD_PS_RSON_LENGTH);
}
}
/**
* Leave power saving
* return RTW_PHL_STATUS_SUCCESS if leave ps ok
* @ps: see cmd_ps
* @leave_proto: whether to leave protocol
* @rson: the reason of leaving power saving
*/
enum rtw_phl_status _leave_ps(struct cmd_ps *ps, bool leave_proto, char *rson)
{
struct phl_info_t *phl_info = ps->phl_info;
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
struct ps_cfg cfg = {0};
if (ps->ps_state == PS_STATE_LEAVED) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): not in power saving.\n", __func__);
return RTW_PHL_STATUS_SUCCESS;
}
cfg.cur_pwr_lvl = ps->cur_pwr_lvl;
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): leave %s (macid %d).\n",
__func__, phl_ps_ps_mode_to_str(ps->ps_mode), ps->macid);
if (ps->ps_mode == PS_MODE_LPS) {
if (!leave_proto) {
if (ps->ps_state == PS_STATE_PROTO) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): in lps protocal only.\n", __func__);
return RTW_PHL_STATUS_SUCCESS;
}
cfg.pwr_cfg = true;
cfg.proto_cfg = false;
} else {
if (ps->ps_state == PS_STATE_PROTO) {
cfg.pwr_cfg = false;
cfg.proto_cfg = true;
} else {
cfg.pwr_cfg = true;
cfg.proto_cfg = true;
}
}
cfg.macid = ps->macid;
cfg.token = &ps->null_token;
cfg.pwr_lvl = PS_PWR_LVL_PWRON;
cfg.ps_mode = ps->ps_mode;
} else if (ps->ps_mode == PS_MODE_IPS) {
cfg.macid = ps->macid;
cfg.pwr_lvl = PS_PWR_LVL_PWRON;
cfg.proto_cfg = (cfg.cur_pwr_lvl == PS_PWR_LVL_PWROFF) ? false : true;
cfg.pwr_cfg = true;
cfg.ps_mode = ps->ps_mode;
} else {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): unknown ps mode!\n", __func__);
}
_set_ps_rson(ps, false, rson);
status = phl_ps_leave_ps(phl_info, &cfg);
if (status == RTW_PHL_STATUS_SUCCESS) {
ps->cur_pwr_lvl = cfg.pwr_lvl;
if (ps->ps_mode == PS_MODE_LPS) {
ps->ps_mode = (leave_proto != true) ? PS_MODE_LPS : PS_MODE_NONE;
ps->ps_state = (leave_proto != true) ? PS_STATE_PROTO : PS_STATE_LEAVED;
ps->macid = (leave_proto != true) ? ps->macid : PS_MACID_NONE;
} else {
ps->ps_mode = PS_MODE_NONE;
ps->ps_state = PS_STATE_LEAVED;
ps->macid = PS_MACID_NONE;
}
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): macid %d leave ps success, reason(%s).\n", __func__, cfg.macid, rson);
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): ps mode(%s), pwr lvl(%s), ps state(%s)\n",
__func__, phl_ps_ps_mode_to_str(ps->ps_mode),
phl_ps_pwr_lvl_to_str(ps->cur_pwr_lvl), _ps_state_to_str(ps->ps_state));
} else {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): leave ps fail! reason(%s).\n", __func__, rson);
}
return status;
}
/**
* Enter power saving
* return RTW_PHL_STATUS_SUCCESS if enter ps ok
* @ps: see cmd_ps
* @ps_mode: target ps mode to enter
* @macid: target macid to enter lps
* @rson: the reason of entering power saving
*/
enum rtw_phl_status _enter_ps(struct cmd_ps *ps, u8 ps_mode, u16 macid, char *rson)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
struct ps_cfg cfg = {0};
struct rtw_ps_cap_t *ps_cap = _get_ps_cap(ps->phl_info);
if (ps->ps_state == PS_STATE_ENTERED) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): already in power saving.\n", __func__);
return RTW_PHL_STATUS_SUCCESS;
}
cfg.cur_pwr_lvl = ps->cur_pwr_lvl;
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): enter %s (macid %d).\n",
__func__, phl_ps_ps_mode_to_str(ps_mode), macid);
if (ps_mode == PS_MODE_LPS) {
cfg.proto_cfg = (ps->ps_state == PS_STATE_PROTO) ? false : true;
cfg.pwr_cfg = true;
cfg.macid = macid;
cfg.token = &ps->null_token;
cfg.pwr_lvl = phl_ps_judge_pwr_lvl(ps_cap->lps_cap, ps_mode, true);
cfg.ps_mode = ps_mode;
cfg.awake_interval = ps_cap->lps_awake_interval;
cfg.listen_bcn_mode = ps_cap->lps_listen_bcn_mode;
cfg.smart_ps_mode = ps_cap->lps_smart_ps_mode;
} else if (ps_mode == PS_MODE_IPS) {
cfg.macid = macid;
if (macid == PS_MACID_NONE)
cfg.pwr_lvl = PS_PWR_LVL_PWROFF;
else
cfg.pwr_lvl = phl_ps_judge_pwr_lvl(ps_cap->ips_cap, ps_mode, true);
cfg.pwr_cfg = true;
cfg.proto_cfg = (cfg.pwr_lvl == PS_PWR_LVL_PWROFF) ? false : true;
cfg.ps_mode = ps_mode;
} else {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): unknown ps mode!\n", __func__);
}
_set_ps_rson(ps, true, rson);
status = phl_ps_enter_ps(ps->phl_info, &cfg);
if (status == RTW_PHL_STATUS_SUCCESS) {
ps->cur_pwr_lvl = cfg.pwr_lvl;
ps->ps_mode = cfg.ps_mode;
ps->macid = cfg.macid;
ps->ps_state = PS_STATE_ENTERED;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): macid %d enter ps success, reason(%s).\n", __func__, ps->macid, rson);
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): ps mode(%s), pwr lvl(%s), ps state(%s)\n",
__func__, phl_ps_ps_mode_to_str(ps->ps_mode),
phl_ps_pwr_lvl_to_str(ps->cur_pwr_lvl), _ps_state_to_str(ps->ps_state));
} else {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): enter ps fail! reason(%s).\n", __func__, rson);
}
return status;
}
static bool _pop_idle_req(struct cmd_ps *ps, struct pwr_req **req)
{
void *d = phl_to_drvpriv(ps->phl_info);
_os_list *new_req = NULL;
bool ret = false;
(*req) = NULL;
if (pq_pop(d, &(ps->req_idle_q), &new_req, _first, _bh)) {
(*req) = (struct pwr_req *)new_req;
ret = true;
} else {
ret = false;
}
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): remaining idle req cnt %d.\n", __func__, ps->req_idle_q.cnt);
return ret;
}
static void _push_idle_req(struct cmd_ps *ps, struct pwr_req *req)
{
void *d = phl_to_drvpriv(ps->phl_info);
pq_push(d, &(ps->req_idle_q), &(req->list), _tail, _bh);
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): remaining idle req cnt %d.\n", __func__, ps->req_idle_q.cnt);
}
static bool _pop_busy_req(struct cmd_ps *ps, struct pwr_req **req)
{
void *d = phl_to_drvpriv(ps->phl_info);
_os_list *new_req = NULL;
bool ret = false;
(*req) = NULL;
if (pq_pop(d, &(ps->req_busy_q), &new_req, _tail, _bh)) {
(*req) = (struct pwr_req *)new_req;
ret = true;
} else {
ret = false;
}
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): current busy req cnt %d.\n", __func__, ps->req_busy_q.cnt);
return ret;
}
static void _push_busy_req(struct cmd_ps *ps, struct pwr_req *req)
{
void *d = phl_to_drvpriv(ps->phl_info);
pq_push(d, &(ps->req_busy_q), &(req->list), _tail, _bh);
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): current busy req cnt %d.\n", __func__, ps->req_busy_q.cnt);
}
static void _cancel_pwr_req(struct cmd_ps *ps, u16 evt_id)
{
struct pwr_req *req = NULL;
if (!_pop_busy_req(ps, &req)) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): busy queue is empty.\n", __func__);
return;
}
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): evt_id %d\n", __func__, req->evt_id);
if (req->evt_id != evt_id && MSG_EVT_PHY_IDLE != evt_id)
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): evt_id mismatch.\n", __func__);
req->evt_id = MSG_EVT_NONE;
_push_idle_req(ps, req);
}
static enum rtw_phl_status _add_pwr_req(struct cmd_ps *ps, u16 evt_id)
{
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
struct pwr_req *req = NULL;
if (!_pop_idle_req(ps, &req)) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): idle queue is empty.\n", __func__);
return RTW_PHL_STATUS_RESOURCE;
}
req->evt_id = evt_id;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): evt_id %d\n", __func__, evt_id);
_push_busy_req(ps, req);
return status;
}
static void _init_pwr_req_q(struct cmd_ps *ps)
{
void *d = phl_to_drvpriv(ps->phl_info);
u8 i = 0;
pq_init(d, &ps->req_busy_q);
pq_init(d, &ps->req_idle_q);
_os_mem_set(d, ps->req_pool, 0,
sizeof(struct pwr_req) * MAX_PWE_REQ_NUM);
for (i = 0; i < MAX_PWE_REQ_NUM; i++) {
pq_push(d, &(ps->req_idle_q),
&(ps->req_pool[i].list), _tail, _bh);
}
}
static void _reset_pwr_req_q(struct cmd_ps *ps)
{
void *d = phl_to_drvpriv(ps->phl_info);
u8 i = 0;
pq_reset(d, &ps->req_busy_q, _bh);
pq_reset(d, &ps->req_idle_q, _bh);
_os_mem_set(d, ps->req_pool, 0,
sizeof(struct pwr_req) * MAX_PWE_REQ_NUM);
for (i = 0; i < MAX_PWE_REQ_NUM; i++) {
pq_push(d, &(ps->req_idle_q),
&(ps->req_pool[i].list), _tail, _bh);
}
}
static void _deinit_pwr_req_q(struct cmd_ps *ps)
{
void *d = phl_to_drvpriv(ps->phl_info);
pq_deinit(d, &ps->req_busy_q);
pq_deinit(d, &ps->req_idle_q);
}
static void _init_ps_dflt_sw_cap(struct cmd_ps *ps)
{
struct rtw_ps_cap_t *sw_cap = _get_ps_sw_cap(ps->phl_info);
sw_cap->init_rf_state = RTW_RF_ON;
sw_cap->init_rt_stop_rson = PS_RT_RSON_NONE;
sw_cap->leave_fail_act = PS_LEAVE_FAIL_ACT_NONE;
sw_cap->ips_en = PS_OP_MODE_DISABLED;
sw_cap->ips_cap = 0;
sw_cap->ips_wow_en = PS_OP_MODE_DISABLED;
sw_cap->ips_wow_cap = 0;
sw_cap->lps_en = PS_OP_MODE_DISABLED;
sw_cap->lps_cap = 0;
sw_cap->lps_awake_interval = 0;
sw_cap->lps_listen_bcn_mode = RTW_LPS_RLBM_MIN;
sw_cap->lps_smart_ps_mode = RTW_LPS_TRX_PWR0;
sw_cap->lps_rssi_enter_threshold = 40;
sw_cap->lps_rssi_leave_threshold = 35;
sw_cap->lps_rssi_diff_threshold = 5;
sw_cap->lps_wow_en = PS_OP_MODE_DISABLED;
sw_cap->lps_wow_cap = 0;
sw_cap->lps_wow_awake_interval = 0;
sw_cap->lps_wow_listen_bcn_mode = RTW_LPS_RLBM_MAX;
sw_cap->lps_wow_smart_ps_mode = RTW_LPS_TRX_PWR0;
}
static void _cmd_ps_timer_cb(void *context)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
struct cmd_ps *ps = (struct cmd_ps *)context;
struct phl_msg msg = {0};
struct phl_msg_attribute attr = {0};
PHL_DBG("[PS_CMD], %s(): \n", __func__);
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_POWER_MGNT);
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_PS_PERIOD_CHK);
msg.band_idx = HW_BAND_0;
pstatus = phl_disp_eng_send_msg(ps->phl_info, &msg, &attr, NULL);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): fail to send MSG_EVT_PS_PERIOD_CHK.\n", __func__);
}
}
static void _ps_common_info_init(struct phl_info_t *phl_info)
{
struct phl_ps_info *ps_info = &phl_info->ps_info;
if (!ps_info->init) {
PHL_INFO("[PS_CMD], %s(): \n", __func__);
_os_atomic_set(phl_to_drvpriv(phl_info),
&phl_info->ps_info.tx_ntfy,
0);
ps_info->init = true;
}
}
static void _ps_common_info_deinit(struct phl_info_t *phl_info)
{
struct phl_ps_info *ps_info = &phl_info->ps_info;
if (ps_info->init) {
PHL_INFO("[PS_CMD], %s(): \n", __func__);
_os_atomic_set(phl_to_drvpriv(phl_info),
&phl_info->ps_info.tx_ntfy,
0);
ps_info->init = false;
}
}
static enum phl_mdl_ret_code _ps_mdl_init(void *phl, void *dispr, void **priv)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct cmd_ps *ps = NULL;
PHL_INFO("[PS_CMD], %s(): \n", __func__);
if (priv == NULL)
return MDL_RET_FAIL;
(*priv) = NULL;
ps = (struct cmd_ps *)_os_mem_alloc(phl_to_drvpriv(phl_info),
sizeof(struct cmd_ps));
if (ps == NULL) {
PHL_ERR("[PS_CMD], %s(): allocate cmd ps fail.\n", __func__);
return MDL_RET_FAIL;
}
_os_mem_set(phl_to_drvpriv(phl_info), ps, 0, sizeof(struct cmd_ps));
ps->phl_info = phl_info;
ps->dispr = dispr;
(*priv) = (void *)ps;
ps->cur_pwr_lvl = PS_PWR_LVL_PWRON;
ps->ps_state = PS_STATE_LEAVED;
ps->ps_mode = PS_MODE_NONE;
ps->rej_pwr_req = false;
ps->rt_stop_rson = PS_RT_RSON_NONE;
_os_init_timer(phl_to_drvpriv(phl_info),
&ps->ps_timer,
_cmd_ps_timer_cb,
ps,
"phl_cmd_ps_timer");
_init_pwr_req_q(ps);
_init_ps_dflt_sw_cap(ps);
_ps_common_info_init(phl_info);
return MDL_RET_SUCCESS;
}
static void _ps_mdl_deinit(void *dispr, void *priv)
{
struct cmd_ps *ps = (struct cmd_ps *)priv;
PHL_INFO("[PS_CMD], %s(): \n", __func__);
_os_release_timer(phl_to_drvpriv(ps->phl_info), &ps->ps_timer);
_deinit_pwr_req_q(ps);
_ps_common_info_deinit(ps->phl_info);
if (ps != NULL)
_os_mem_free(phl_to_drvpriv(ps->phl_info), ps, sizeof(struct cmd_ps));
}
static void _dump_ps_cap(struct cmd_ps *ps)
{
struct rtw_ps_cap_t *ps_cap = NULL;
ps_cap = _get_ps_cap(ps->phl_info);
PHL_INFO("[PS_CMD], %s(): \n", __func__);
PHL_INFO("[PS_CMD], init_rf_state: %d\n", ps_cap->init_rf_state);
PHL_INFO("[PS_CMD], init_rt_stop_rson: 0x%x\n", ps_cap->init_rt_stop_rson);
PHL_INFO("[PS_CMD], leave_fail_act: 0x%x\n", ps_cap->leave_fail_act);
PHL_INFO("[PS_CMD], ips_en: %d\n", ps_cap->ips_en);
PHL_INFO("[PS_CMD], ips_cap: %d\n", ps_cap->ips_cap);
PHL_INFO("[PS_CMD], ips_wow_en: %d\n", ps_cap->ips_wow_en);
PHL_INFO("[PS_CMD], ips_wow_cap: %d\n", ps_cap->ips_wow_cap);
PHL_INFO("[PS_CMD], lps_en: %d\n", ps_cap->lps_en);
PHL_INFO("[PS_CMD], lps_cap: %d\n", ps_cap->lps_cap);
PHL_INFO("[PS_CMD], lps_awake_interval: %d\n", ps_cap->lps_awake_interval);
PHL_INFO("[PS_CMD], lps_listen_bcn_mode: %d\n", ps_cap->lps_listen_bcn_mode);
PHL_INFO("[PS_CMD], lps_smart_ps_mode: %d\n", ps_cap->lps_smart_ps_mode);
PHL_INFO("[PS_CMD], lps_rssi_enter_threshold: %d\n", ps_cap->lps_rssi_enter_threshold);
PHL_INFO("[PS_CMD], lps_rssi_leave_threshold: %d\n", ps_cap->lps_rssi_leave_threshold);
PHL_INFO("[PS_CMD], lps_rssi_diff_threshold: %d\n", ps_cap->lps_rssi_diff_threshold);
PHL_INFO("[PS_CMD], lps_wow_en: %d\n", ps_cap->lps_wow_en);
PHL_INFO("[PS_CMD], lps_wow_cap: %d\n", ps_cap->lps_wow_cap);
PHL_INFO("[PS_CMD], lps_wow_awake_interval: %d\n", ps_cap->lps_wow_awake_interval);
PHL_INFO("[PS_CMD], lps_wow_listen_bcn_mode: %d\n", ps_cap->lps_wow_listen_bcn_mode);
PHL_INFO("[PS_CMD], lps_wow_smart_ps_mode: %d\n", ps_cap->lps_wow_smart_ps_mode);
PHL_INFO("[PS_CMD], lps_pause_tx: %d\n", ps_cap->lps_pause_tx);
}
static void _leave_success_hdlr(struct cmd_ps *ps)
{
struct phl_data_ctl_t ctl = {0};
if (ps->stop_datapath) {
/* resume tx datapath */
ctl.id = PHL_MDL_POWER_MGNT;
ctl.cmd = PHL_DATA_CTL_SW_TX_RESUME;
phl_data_ctrler(ps->phl_info, &ctl, NULL);
ps->stop_datapath = false;
}
}
static void _leave_fail_hdlr(struct cmd_ps *ps)
{
struct rtw_ps_cap_t *ps_cap = _get_ps_cap(ps->phl_info);
PHL_WARN("[PS_CMD], %s(): action 0x%x\n", __func__, ps_cap->leave_fail_act);
/* reject all power operation */
if (ps_cap->leave_fail_act & PS_LEAVE_FAIL_ACT_REJ_PWR)
ps->rej_pwr_req = true;
/* L2 should be the last one */
if (ps_cap->leave_fail_act & PS_LEAVE_FAIL_ACT_L2)
phl_ser_send_msg(ps->phl_info, RTW_PHL_SER_L2_RESET);
}
static enum phl_mdl_ret_code _ps_mdl_start(void *dispr, void *priv)
{
enum phl_mdl_ret_code ret = MDL_RET_SUCCESS;
struct cmd_ps *ps = (struct cmd_ps *)priv;
struct rtw_ps_cap_t *ps_cap = _get_ps_cap(ps->phl_info);
u8 idx = 0;
ps->rt_stop_rson = ps_cap->init_rt_stop_rson;
_reset_pwr_req_q(ps);
phl_dispr_get_idx(dispr, &idx);
if (idx == 0) {
_dump_ps_cap(ps);
PHL_INFO("[PS_CMD], %s(): init rf state %d, reject pwr req %d\n",
__func__, ps_cap->init_rf_state, ps->rej_pwr_req);
if (ps_cap->init_rf_state == RTW_RF_OFF)
ps->rej_pwr_req = true;
if (ps->rej_pwr_req == true)
_enter_ps(ps, PS_MODE_IPS, PS_MACID_NONE, "mdl start with rf off");
_os_set_timer(phl_to_drvpriv(ps->phl_info), &ps->ps_timer, CMD_PS_TIMER_PERIOD);
}
return ret;
}
static enum phl_mdl_ret_code _ps_mdl_stop(void *dispr, void *priv)
{
enum phl_mdl_ret_code ret = MDL_RET_SUCCESS;
struct cmd_ps *ps = (struct cmd_ps *)priv;
if (ps->ps_state == PS_STATE_ENTERED) {
PHL_WARN("[PS_CMD], %s(): module stop in power saving!\n", __func__);
_leave_ps(ps, true, "mdl stop");
}
_os_cancel_timer(phl_to_drvpriv(ps->phl_info), &ps->ps_timer);
PHL_INFO("[PS_CMD], %s(): \n", __func__);
return ret;
}
static bool _chk_role_all_no_link(struct cmd_ps *ps)
{
struct phl_info_t *phl_info = ps->phl_info;
u8 role_idx = 0;
bool ret = true;
for (role_idx = 0; role_idx < MAX_WIFI_ROLE_NUMBER; role_idx++) {
if (phl_info->phl_com->wifi_roles[role_idx].active == false)
continue;
if (phl_info->phl_com->wifi_roles[role_idx].mstate != MLME_NO_LINK) {
ret = false;
break;
}
}
return ret;
}
static struct rtw_wifi_role_t *_get_role_of_ps_permitted(struct cmd_ps *ps, u8 target_mode)
{
struct phl_info_t *phl_info = ps->phl_info;
struct rtw_phl_com_t *phl_com = phl_info->phl_com;
u8 role_idx = 0;
for (role_idx = 0; role_idx < MAX_WIFI_ROLE_NUMBER; role_idx++) {
if (phl_com->wifi_roles[role_idx].active == false)
continue;
if (phl_com->wifi_roles[role_idx].type == PHL_RTYPE_STATION) {
if (target_mode == PS_MODE_LPS &&
phl_com->wifi_roles[role_idx].mstate == MLME_LINKED)
return &(phl_com->wifi_roles[role_idx]);
else if (target_mode == PS_MODE_IPS &&
phl_com->wifi_roles[role_idx].mstate == MLME_NO_LINK)
return &(phl_com->wifi_roles[role_idx]);
}
}
return NULL;
}
/**
* go through all wifi roles and check whether input
* ps mode desired is allowed with existing wroles
* @ps: see cmd_ps
* @target_mode: the desired ps mode (lps or ips)
* @macid: target macid to enter lps
*/
static bool _chk_wrole_with_ps_mode(struct cmd_ps *ps,
u8 target_mode, u16 *macid)
{
struct rtw_wifi_role_t *role = NULL;
bool ret = false;
struct rtw_phl_stainfo_t *sta = NULL;
if (ps->mr_info.ap_active || ps->mr_info.gc_active) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): ap %d or gc %d, is active.\n", __func__,
ps->mr_info.ap_active, ps->mr_info.gc_active);
return false;
}
if (target_mode == PS_MODE_IPS) {
if (_chk_role_all_no_link(ps)) {
role = _get_role_of_ps_permitted(ps, PS_MODE_IPS);
if (role == NULL) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): there is no suitable role to enter ips.\n", __func__);
return false;
}
sta = rtw_phl_get_stainfo_self(ps->phl_info, role);
if (sta == NULL) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): cannot get sta info.\n", __func__);
return false;
}
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): role id to enter ips (%d).\n", __func__, role->id);
ps->sta = sta;
*macid = sta->macid;
ret = true;
}
} else if (target_mode == PS_MODE_LPS) {
role = _get_role_of_ps_permitted(ps, PS_MODE_LPS);
if (role == NULL) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): there is no suitable role to enter lps.\n", __func__);
return false;
}
sta = rtw_phl_get_stainfo_self(ps->phl_info, role);
if (sta == NULL) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): cannot get sta info.\n", __func__);
return false;
}
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): role id to enter lps (%d).\n", __func__, role->id);
ps->sta = sta;
*macid = sta->macid;
ret = true;
}
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): decide enter ps(%s), target mode(%s).\n",
__func__, (ret) ? "Yes" : "No", phl_ps_ps_mode_to_str(target_mode));
return ret;
}
static enum rtw_phl_status _stop_datapath(struct cmd_ps *ps)
{
struct phl_data_ctl_t ctl = {0};
/* stop tx datapath */
ctl.id = PHL_MDL_POWER_MGNT;
ctl.cmd = PHL_DATA_CTL_SW_TX_PAUSE;
if (phl_data_ctrler(ps->phl_info, &ctl, NULL) == RTW_PHL_STATUS_SUCCESS) {
ps->stop_datapath = true;
return RTW_PHL_STATUS_SUCCESS;
}
return RTW_PHL_STATUS_FAILURE;
}
static bool _is_datapath_active(struct cmd_ps *ps)
{
struct phl_info_t *phl_info = ps->phl_info;
return (_os_atomic_read(phl_to_drvpriv(phl_info), &phl_info->phl_sw_tx_sts)
!= PHL_TX_STATUS_SW_PAUSE) ? true : false;
}
/**
* check current capability of power saving
* return able to enter ps or not
* @ps: see cmd_ps
* @mode: the target ps mode to be check
*/
static bool _chk_ps_cap(struct cmd_ps *ps, u8 mode)
{
struct rtw_ps_cap_t *ps_cap = _get_ps_cap(ps->phl_info);
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): mode(%s), lps_en(%d), ips_en(%d), runtime stop reason(0x%x).\n",
__func__, phl_ps_ps_mode_to_str(mode), ps_cap->lps_en, ps_cap->ips_en, ps->rt_stop_rson);
switch (mode) {
case PS_MODE_LPS:
if (ps_cap->lps_en == PS_OP_MODE_DISABLED) {
return false;
} else if (ps_cap->lps_en == PS_OP_MODE_FORCE_ENABLED) {
/* force enable */
return true;
} else if (ps_cap->lps_en == PS_OP_MODE_AUTO) {
if (ps->rt_stop_rson == PS_RT_RSON_NONE)
return true;
}
break;
case PS_MODE_IPS:
if (ps_cap->ips_en == PS_OP_MODE_DISABLED) {
return false;
} else if (ps_cap->ips_en == PS_OP_MODE_FORCE_ENABLED) {
/* force enable */
return true;
} else if (ps_cap->ips_en == PS_OP_MODE_AUTO) {
if (ps->rt_stop_rson == PS_RT_RSON_NONE)
return true;
}
break;
default:
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): unknown ps mode.\n", __func__);
return false;
}
return false;
}
/**
* check the condition of ips
* return whether to enter ips or not
* @ps: see cmd_ps
*/
static bool _chk_ips_enter(struct cmd_ps *ps, u16 *macid)
{
if (TEST_STATUS_FLAG(ps->phl_info->phl_com->dev_state, RTW_DEV_RESUMING)) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): resume in progress.\n", __func__);
return false;
}
if (ps->rej_pwr_req == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): reject pwr req.\n", __func__);
return false;
}
if (!_chk_ps_cap(ps, PS_MODE_IPS)) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): ips is not allowed.\n", __func__);
return false;
}
if (!phl_disp_eng_is_fg_empty(ps->phl_info, HW_BAND_0)) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): fg exist.\n", __func__);
return false;
}
if (ps->req_busy_q.cnt) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): req q is not empty.\n", __func__);
return false;
}
if (ps->btc_req_pwr) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): btc req pwr.\n", __func__);
return false;
}
if (!_chk_wrole_with_ps_mode(ps, PS_MODE_IPS, macid)) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): no need to enter ips.\n", __func__);
return false;
}
return true;
}
/**
* check the condition of lps
* return whether to enter lps or not
* @ps: see cmd_ps
*/
static bool _chk_lps_enter(struct cmd_ps *ps, u16 *macid)
{
struct rtw_ps_cap_t *ps_cap = _get_ps_cap(ps->phl_info);
/* check enter lps or not */
if (TEST_STATUS_FLAG(ps->phl_info->phl_com->dev_state, RTW_DEV_RESUMING)) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): resume in progress.\n", __func__);
return false;
}
if (ps->rej_pwr_req == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): reject pwr req.\n", __func__);
return false;
}
/* check capability */
if (!_chk_ps_cap(ps, PS_MODE_LPS)) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): lps is not allowed.\n", __func__);
return false;
}
/* check fg module */
if (!phl_disp_eng_is_fg_empty(ps->phl_info, HW_BAND_0)) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): fg exist.\n", __func__);
return false;
}
/* ref cnt */
if (ps->req_busy_q.cnt) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): req q is not empty.\n", __func__);
return false;
}
/* btc */
if (ps->btc_req_pwr) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): btc req pwr.\n", __func__);
return false;
}
if (ps->wdg_leave_ps == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): just leave ps in watchdog prephase.\n", __func__);
return false;
}
/* check wifi role */
if (!_chk_wrole_with_ps_mode(ps, PS_MODE_LPS, macid)) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): no need to enter lps.\n", __func__);
return false;
}
/* lps */
if (_lps_state_judge_changed(ps, *macid, ps->ps_state, PS_STATE_ENTERED)) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): lps state changed, going to enter...\n", __func__);
/* check data path stop or not */
if (ps_cap->lps_pause_tx) {
if (!_is_datapath_active(ps)) {
return true;
} else {
if (_stop_datapath(ps) == RTW_PHL_STATUS_SUCCESS)
return true;
}
} else {
return true;
}
}
return false;
}
static void _ps_watchdog_info_dump(struct cmd_ps *ps)
{
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "========== CMD PS Info ========== \n");
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "ps mode(%s), pwr lvl(%s), ps state(%s)\n",
phl_ps_ps_mode_to_str(ps->ps_mode),
phl_ps_pwr_lvl_to_str(ps->cur_pwr_lvl),
_ps_state_to_str(ps->ps_state));
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "req idle cnt(%d), req busy cnt(%d)\n",
ps->req_idle_q.cnt, ps->req_busy_q.cnt);
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "ap active(%s), gc active(%s)\n",
(ps->mr_info.ap_active ? "yes" : "no"), (ps->mr_info.gc_active ? "yes" : "no"));
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "reject all pwr req(%s), btc req pwr(%s), runtime stop reason(0x%x)\n",
(ps->rej_pwr_req ? "yes" : "no"), (ps->btc_req_pwr ? "yes" : "no"), ps->rt_stop_rson);
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "========== CMD PS Info ========== \n");
}
/**
* pre-phase handler of watchdog
* will check whether to leave lps or not
* @ps: see cmd_ps
*/
static enum phl_mdl_ret_code
_ps_watchdog_pre_hdlr(struct cmd_ps *ps)
{
/* check leave lps or not */
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): pwr lvl(%s).\n", __func__, phl_ps_pwr_lvl_to_str(ps->cur_pwr_lvl));
ps->wdg_leave_ps = false;
if (ps->rej_pwr_req == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): reject pwr req.\n", __func__);
return MDL_RET_CANNOT_IO;
}
if (ps->ps_mode == PS_MODE_IPS) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): under inactive ps.\n", __func__);
return MDL_RET_CANNOT_IO;
}
if (ps->ps_state == PS_STATE_LEAVED) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): not in power saving.\n", __func__);
return MDL_RET_SUCCESS;
} else {
if (_lps_state_judge_changed(ps, ps->macid, ps->ps_state, PS_STATE_LEAVED)) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): lps state changed, going to leave...\n", __func__);
if (_leave_ps(ps, true, "watchdog") == RTW_PHL_STATUS_SUCCESS) {
ps->wdg_leave_ps = true;
_leave_success_hdlr(ps);
return MDL_RET_SUCCESS;
} else {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): going to L2 reset!\n", __func__);
_leave_fail_hdlr(ps);
return MDL_RET_CANNOT_IO;
}
}
/**
* if _lps_state_judge_changed decide not to leave lps and currrent lps
* state is in protocol only, i/o operation is allowable.
*/
if (ps->ps_state == PS_STATE_PROTO)
return MDL_RET_SUCCESS;
else
return MDL_RET_CANNOT_IO;
}
}
/**
* post-phase handler of watchdog
* will check whether to enter lps or not
* @ps: see cmd_ps
*/
static enum phl_mdl_ret_code
_ps_watchdog_post_hdlr(struct cmd_ps *ps)
{
u16 macid = PS_MACID_NONE;
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): pwr lvl(%s).\n", __func__, phl_ps_pwr_lvl_to_str(ps->cur_pwr_lvl));
if (_chk_lps_enter(ps, &macid))
_enter_ps(ps, PS_MODE_LPS, macid, "watchdog");
else if (_chk_ips_enter(ps, &macid))
_enter_ps(ps, PS_MODE_IPS, macid, "watchdog");
_ps_watchdog_info_dump(ps);
return MDL_RET_SUCCESS;
}
static enum phl_mdl_ret_code _phy_on_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
if (ps->rej_pwr_req == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): reject pwr req.\n", __func__);
return MDL_RET_CANNOT_IO;
}
if (IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], MSG_EVT_PHY_ON\n");
if (_leave_ps(ps, true, "phy on") == RTW_PHL_STATUS_SUCCESS) {
_add_pwr_req(ps, MSG_EVT_ID_FIELD(msg->msg_id));
_leave_success_hdlr(ps);
ret = MDL_RET_SUCCESS;
} else {
_leave_fail_hdlr(ps);
}
} else {
ret = MDL_RET_SUCCESS;
}
return ret;
}
static enum phl_mdl_ret_code _tx_pwr_req_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
if (ps->rej_pwr_req == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): reject pwr req.\n", __func__);
return MDL_RET_CANNOT_IO;
}
if (IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], MSG_EVT_TRX_PWR_REQ\n");
if (_leave_ps(ps, false, "tx req") == RTW_PHL_STATUS_SUCCESS) {
_leave_success_hdlr(ps);
ret = MDL_RET_SUCCESS;
} else {
_leave_fail_hdlr(ps);
}
} else {
ret = MDL_RET_SUCCESS;
}
return ret;
}
static enum phl_mdl_ret_code _phy_idle_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
u16 macid = PS_MACID_NONE;
if (!IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], MSG_EVT_PHY_IDLE\n");
_cancel_pwr_req(ps, MSG_EVT_ID_FIELD(msg->msg_id));
/* check enter ips or not */
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): try enter ips.\n", __func__);
if (_chk_ips_enter(ps, &macid)) {
_enter_ps(ps, PS_MODE_IPS, macid, "phy idle");
}
ret = MDL_RET_SUCCESS;
} else {
ret = MDL_RET_SUCCESS;
}
return ret;
}
/**
* pre-phase handler of msg
* leave ps and return corresponding status
* @ps: see cmd_ps
* @evt_id: evt id of msg
*/
static enum phl_mdl_ret_code
_ext_msg_pre_hdlr(struct cmd_ps *ps, u16 evt_id)
{
if (ps->rej_pwr_req == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): reject pwr req.\n", __func__);
return MDL_RET_CANNOT_IO;
}
/* power request */
if (_leave_ps(ps, true, "ext msg req") == RTW_PHL_STATUS_SUCCESS) {
_add_pwr_req(ps, evt_id);
_leave_success_hdlr(ps);
return MDL_RET_SUCCESS;
} else {
_leave_fail_hdlr(ps);
}
return MDL_RET_CANNOT_IO;
}
/**
* post-phase handler of msg
* cancel power req and chk enter ips or not
* @ps: see cmd_ps
* @evt_id: evt id of msg
*/
static enum phl_mdl_ret_code
_ext_msg_post_hdlr(struct cmd_ps *ps, u16 evt_id)
{
u16 macid = PS_MACID_NONE;
/* cancel power request */
_cancel_pwr_req(ps, evt_id);
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): try enter ips.\n", __func__);
if (_chk_ips_enter(ps, &macid)) {
_enter_ps(ps, PS_MODE_IPS, macid, "ext msg done");
}
return MDL_RET_SUCCESS;
}
static void _ps_mr_info_upt(struct cmd_ps *ps, struct rtw_wifi_role_t *role)
{
struct phl_info_t *phl_info = ps->phl_info;
struct rtw_wifi_role_t *wr = NULL;
u8 role_idx = 0;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): id %d, type %d, mstate %d\n", __func__,
role->id, role->type, role->mstate);
#ifdef RTW_WKARD_PHL_NTFY_MEDIA_STS
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): is_gc %d\n", __func__, role->is_gc);
#endif
_os_mem_set(phl_to_drvpriv(phl_info), &ps->mr_info, 0, sizeof(struct _ps_mr_info));
for (role_idx = 0; role_idx < MAX_WIFI_ROLE_NUMBER; role_idx++) {
wr = &(phl_info->phl_com->wifi_roles[role_idx]);
if (wr->active == false)
continue;
#ifdef RTW_WKARD_PHL_NTFY_MEDIA_STS
if (wr->type == PHL_RTYPE_STATION) {
if(wr->is_gc == true && wr->mstate != MLME_NO_LINK)
ps->mr_info.gc_active = true;
}
#endif
if (wr->type == PHL_RTYPE_AP || wr->type == PHL_RTYPE_VAP ||
wr->type == PHL_RTYPE_MESH ||wr->type == PHL_RTYPE_P2P_GO)
ps->mr_info.ap_active = (wr->mstate == MLME_NO_LINK) ? false : true;
}
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): gc_active %d, ap_active %d\n", __func__,
ps->mr_info.gc_active, ps->mr_info.ap_active);
}
static bool _is_ignored_mrc_evt(u16 evt_id)
{
return false;
}
static enum phl_mdl_ret_code
_mrc_mdl_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
struct rtw_role_cmd *rcmd = NULL;
struct rtw_wifi_role_t *role = NULL;
if (_is_ignored_mrc_evt(MSG_EVT_ID_FIELD(msg->msg_id)))
return MDL_RET_SUCCESS;
switch (MSG_EVT_ID_FIELD(msg->msg_id)) {
case MSG_EVT_ROLE_NTFY:
if (!IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
if (msg->inbuf && (msg->inlen == sizeof(struct rtw_role_cmd))) {
rcmd = (struct rtw_role_cmd *)msg->inbuf;
role = rcmd->wrole;
_ps_mr_info_upt(ps, role);
}
}
ret = MDL_RET_SUCCESS;
break;
default:
if (ps->cur_pwr_lvl != PS_PWR_LVL_PWRON) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): MDL_ID(%d)-EVT_ID(%d) get cannot I/O!\n", __func__,
MSG_MDL_ID_FIELD(msg->msg_id), MSG_EVT_ID_FIELD(msg->msg_id));
ret = MDL_RET_CANNOT_IO;
} else {
ret = MDL_RET_SUCCESS;
}
break;
}
return ret;
}
static bool _is_ignored_ser_evt(u16 evt_id)
{
if (MSG_EVT_SER_M9_L2_RESET != evt_id)
return true;
return false;
}
static enum phl_mdl_ret_code
_ser_mdl_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
if (_is_ignored_ser_evt(MSG_EVT_ID_FIELD(msg->msg_id)))
return MDL_RET_SUCCESS;
switch (MSG_EVT_ID_FIELD(msg->msg_id)) {
case MSG_EVT_SER_M9_L2_RESET:
ret = MDL_RET_SUCCESS;
break;
default:
if (ps->cur_pwr_lvl != PS_PWR_LVL_PWRON) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): MDL_ID(%d)-EVT_ID(%d) get cannot I/O!\n",
__func__,
MSG_MDL_ID_FIELD(msg->msg_id),
MSG_EVT_ID_FIELD(msg->msg_id));
ret = MDL_RET_CANNOT_IO;
} else {
ret = MDL_RET_SUCCESS;
}
break;
}
return ret;
}
static bool _is_ignored_general_evt(u16 evt_id)
{
bool ret = false;
switch (evt_id) {
case MSG_EVT_GET_USB_SW_ABILITY:
case MSG_EVT_GET_USB_SPEED:
case MSG_EVT_SW_WATCHDOG:
ret = true;
break;
default:
ret = false;
break;
}
return ret;
}
#ifdef RTW_WKARD_LINUX_CMD_WKARD
static enum phl_mdl_ret_code
_linux_cmd_wkard_hdlr(struct cmd_ps *ps)
{
if (ps->rej_pwr_req == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): reject pwr req.\n", __func__);
return MDL_RET_CANNOT_IO;
}
/* power request */
if (_leave_ps(ps, true, "linux cmd wkard") == RTW_PHL_STATUS_SUCCESS) {
_leave_success_hdlr(ps);
return MDL_RET_SUCCESS;
} else {
_leave_fail_hdlr(ps);
}
return MDL_RET_CANNOT_IO;
}
#endif /* RTW_WKARD_LINUX_CMD_WKARD */
static enum phl_mdl_ret_code
_general_mdl_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
if (_is_ignored_general_evt(MSG_EVT_ID_FIELD(msg->msg_id)))
return MDL_RET_SUCCESS;
switch (MSG_EVT_ID_FIELD(msg->msg_id)) {
case MSG_EVT_CHG_OP_CH_DEF_START:
case MSG_EVT_CHG_OP_CH_DEF_END:
case MSG_EVT_SWCH_START:
case MSG_EVT_PCIE_TRX_MIT:
case MSG_EVT_FORCE_USB_SW:
case MSG_EVT_GET_USB_SPEED:
case MSG_EVT_GET_USB_SW_ABILITY:
case MSG_EVT_CFG_AMPDU:
case MSG_EVT_DFS_PAUSE_TX:
case MSG_EVT_ROLE_RECOVER:
case MSG_EVT_ROLE_SUSPEND:
case MSG_EVT_HAL_SET_L2_LEAVE:
case MSG_EVT_NOTIFY_HAL:
case MSG_EVT_ISSUE_BCN:
case MSG_EVT_STOP_BCN:
case MSG_EVT_SEC_KEY:
case MSG_EVT_ROLE_START:
case MSG_EVT_ROLE_CHANGE:
case MSG_EVT_ROLE_STOP:
case MSG_EVT_STA_INFO_CTRL:
case MSG_EVT_STA_MEDIA_STATUS_UPT:
case MSG_EVT_CFG_CHINFO:
case MSG_EVT_STA_CHG_STAINFO:
#ifdef RTW_WKARD_LINUX_CMD_WKARD
case MSG_EVT_LINUX_CMD_WRK_TRI_PS:
#endif /* RTW_WKARD_LINUX_CMD_WKARD */
case MSG_EVT_DBG_RX_DUMP:
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): MDL_ID(%d)-EVT_ID(%d) in %s phase.\n", __func__,
MSG_MDL_ID_FIELD(msg->msg_id), MSG_EVT_ID_FIELD(msg->msg_id),
(IS_MSG_IN_PRE_PHASE(msg->msg_id) ? "pre-protocol" : "post-protocol"));
if (IS_MSG_IN_PRE_PHASE(msg->msg_id))
ret = _ext_msg_pre_hdlr(ps, MSG_EVT_ID_FIELD(msg->msg_id));
else
ret = _ext_msg_post_hdlr(ps, MSG_EVT_ID_FIELD(msg->msg_id));
break;
#ifdef RTW_WKARD_LINUX_CMD_WKARD
case MSG_EVT_LINUX_CMD_WRK:
if (IS_MSG_IN_PRE_PHASE(msg->msg_id))
ret = _linux_cmd_wkard_hdlr(ps);
else
ret = MDL_RET_SUCCESS;
break;
#endif /* RTW_WKARD_LINUX_CMD_WKARD */
case MSG_EVT_HW_WATCHDOG:
if (IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
ret = _ps_watchdog_pre_hdlr(ps);
if (ret == MDL_RET_SUCCESS) {
ps->rssi_bcn_min = phl_get_min_rssi_bcn(ps->phl_info);
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): update rssi_bcn_min to %d\n", __func__, ps->rssi_bcn_min);
}
} else {
ret = _ps_watchdog_post_hdlr(ps);
}
break;
case MSG_EVT_RF_ON:
if (!IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], MSG_EVT_RF_ON\n");
ps->rej_pwr_req = false;
}
ret = MDL_RET_SUCCESS;
break;
case MSG_EVT_RF_OFF:
if (!IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], MSG_EVT_RF_OFF\n");
if (_leave_ps(ps, true, "msg rf off") == RTW_PHL_STATUS_SUCCESS) {
_leave_success_hdlr(ps);
} else {
_leave_fail_hdlr(ps);
ret = MDL_RET_CANNOT_IO;
break;
}
ps->rej_pwr_req = true;
_enter_ps(ps, PS_MODE_IPS, PS_MACID_NONE, "msg rf off");
}
ret = MDL_RET_SUCCESS;
break;
case MSG_EVT_PHY_ON:
ret = _phy_on_msg_hdlr(ps, msg);
break;
case MSG_EVT_PHY_IDLE:
ret = _phy_idle_msg_hdlr(ps, msg);
break;
default:
if (ps->cur_pwr_lvl != PS_PWR_LVL_PWRON) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): MDL_ID(%d)-EVT_ID(%d) get cannot I/O!\n",
__func__,
MSG_MDL_ID_FIELD(msg->msg_id),
MSG_EVT_ID_FIELD(msg->msg_id));
ret = MDL_RET_CANNOT_IO;
} else {
ret = MDL_RET_SUCCESS;
}
break;
}
return ret;
}
static bool _is_ignored_datapath_evt(u16 evt_id)
{
return false;
}
static enum phl_mdl_ret_code
_datapath_mdl_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
if (_is_ignored_datapath_evt(MSG_EVT_ID_FIELD(msg->msg_id)))
return MDL_RET_SUCCESS;
switch (MSG_EVT_ID_FIELD(msg->msg_id)) {
case MSG_EVT_TRX_PWR_REQ:
ret = _tx_pwr_req_msg_hdlr(ps, msg);
break;
default:
if (ps->cur_pwr_lvl != PS_PWR_LVL_PWRON) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): MDL_ID(%d)-EVT_ID(%d) get cannot I/O!\n",
__func__,
MSG_MDL_ID_FIELD(msg->msg_id),
MSG_EVT_ID_FIELD(msg->msg_id));
ret = MDL_RET_CANNOT_IO;
} else {
ret = MDL_RET_SUCCESS;
}
break;
}
return ret;
}
/**
* bypass msg of specific module
* @msg: see phl_msg
*/
static bool _is_ignored_mdl(struct phl_msg *msg)
{
if (MSG_MDL_ID_FIELD(msg->msg_id) == PHL_MDL_BTC)
return true;
if (MSG_MDL_ID_FIELD(msg->msg_id) == PHL_MDL_LED)
return true;
return false;
}
static enum phl_mdl_ret_code
_ps_mdl_hdl_external_evt(void *dispr, struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
if (_is_ignored_mdl(msg)) {
/* PHL_INFO("[PS_CMD], %s(): ignore MDL_ID(%d)-EVT_ID(%d).\n", __func__,
MSG_MDL_ID_FIELD(msg->msg_id), MSG_EVT_ID_FIELD(msg->msg_id)); */
return MDL_RET_SUCCESS;
}
switch (MSG_MDL_ID_FIELD(msg->msg_id)) {
case PHL_MDL_GENERAL:
ret = _general_mdl_msg_hdlr(ps, msg);
break;
case PHL_MDL_MRC:
ret = _mrc_mdl_msg_hdlr(ps, msg);
break;
case PHL_MDL_TX:
case PHL_MDL_RX:
ret = _datapath_mdl_msg_hdlr(ps, msg);
break;
case PHL_MDL_SER:
ret = _ser_mdl_msg_hdlr(ps, msg);
break;
/* handle ohters mdl here */
default:
if (ps->cur_pwr_lvl != PS_PWR_LVL_PWRON) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): MDL_ID(%d)-EVT_ID(%d) get cannot I/O!\n", __func__,
MSG_MDL_ID_FIELD(msg->msg_id), MSG_EVT_ID_FIELD(msg->msg_id));
ret = MDL_RET_CANNOT_IO;
} else {
ret = MDL_RET_SUCCESS;
}
break;
}
return ret;
}
static enum phl_mdl_ret_code _ps_cap_chg_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_ps_rt_rson rt_rson = PS_RT_RSON_NONE;
bool ps_allow = false;
struct rt_ps *rt_ps_info= NULL;
u16 macid = PS_MACID_NONE;
if (IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
rt_ps_info = (struct rt_ps *)msg->inbuf;
ps_allow = rt_ps_info->ps_allow;
rt_rson = rt_ps_info->rt_rson;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): update -> ps_allow(%d), reason(%d).\n", __func__, ps_allow, rt_rson);
if (ps_allow) {
if (TEST_STATUS_FLAG(ps->rt_stop_rson, rt_rson) == true)
CLEAR_STATUS_FLAG(ps->rt_stop_rson, rt_rson);
else
return MDL_RET_SUCCESS;
} else {
if (TEST_STATUS_FLAG(ps->rt_stop_rson, rt_rson) == false)
SET_STATUS_FLAG(ps->rt_stop_rson, rt_rson);
else
return MDL_RET_SUCCESS;
}
if (ps->rej_pwr_req == true) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): reject pwr req.\n", __func__);
return MDL_RET_CANNOT_IO;
}
if (ps->rt_stop_rson != PS_RT_RSON_NONE) {
if (_leave_ps(ps, true, "cap chg") == RTW_PHL_STATUS_SUCCESS) {
_leave_success_hdlr(ps);
return MDL_RET_SUCCESS;
} else {
_leave_fail_hdlr(ps);
return MDL_RET_CANNOT_IO;
}
}
} else {
if (ps->rt_stop_rson == PS_RT_RSON_NONE) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): try enter ips.\n", __func__);
if (_chk_ips_enter(ps, &macid)) {
_enter_ps(ps, PS_MODE_IPS, macid, "cap chg");
}
}
}
return MDL_RET_SUCCESS;
}
static enum phl_mdl_ret_code _tx_pkt_ntfy_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
if (!IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
PHL_DBG("[PS_CMD], %s(): rpwm with tx req.\n", __func__);
/* rpwm with tx req */
rtw_hal_ps_notify_wake(ps->phl_info->hal);
}
return MDL_RET_SUCCESS;
}
static enum phl_mdl_ret_code _ps_period_chk_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
u16 macid = PS_MACID_NONE;
if (IS_MSG_IN_PRE_PHASE(msg->msg_id))
return MDL_RET_SUCCESS;
ps->wdg_leave_ps = false;
if (ps->ps_state != PS_STATE_ENTERED) {
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): \n", __func__);
if (_chk_lps_enter(ps, &macid))
_enter_ps(ps, PS_MODE_LPS, macid, "period chk");
}
_os_set_timer(phl_to_drvpriv(ps->phl_info), &ps->ps_timer, CMD_PS_TIMER_PERIOD);
return MDL_RET_SUCCESS;
}
static enum phl_mdl_ret_code _ps_dbg_lps_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
u16 macid = PS_MACID_NONE;
u16 evt_id = MSG_EVT_ID_FIELD(msg->msg_id);
if (evt_id == MSG_EVT_PS_DBG_LPS_ENTER) {
if (!IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
if (!_chk_wrole_with_ps_mode(ps, PS_MODE_LPS, &macid))
return MDL_RET_SUCCESS;
_enter_ps(ps, PS_MODE_LPS, macid, "dbg lps enter");
ps->rej_pwr_req = true;
}
} else { /* MSG_EVT_PS_DBG_LPS_LEAVE */
if (IS_MSG_IN_PRE_PHASE(msg->msg_id)) {
_leave_ps(ps, true, "dbg lps leave");
ps->rej_pwr_req = false;
}
}
return MDL_RET_SUCCESS;
}
static enum phl_mdl_ret_code _ps_dbg_ips_msg_hdlr(struct cmd_ps *ps, struct phl_msg *msg)
{
/* fw do not support ips currently */
return MDL_RET_SUCCESS;
}
static enum phl_mdl_ret_code
_ps_mdl_hdl_internal_evt(void *dispr, struct cmd_ps *ps, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_CANNOT_IO;
switch (MSG_EVT_ID_FIELD(msg->msg_id)) {
case MSG_EVT_PHY_ON:
ret = _phy_on_msg_hdlr(ps, msg);
break;
case MSG_EVT_PHY_IDLE:
ret = _phy_idle_msg_hdlr(ps, msg);
break;
case MSG_EVT_PS_CAP_CHG:
ret = _ps_cap_chg_msg_hdlr(ps, msg);
break;
case MSG_EVT_PS_PERIOD_CHK:
ret = _ps_period_chk_hdlr(ps, msg);
break;
case MSG_EVT_PS_DBG_LPS_ENTER:
case MSG_EVT_PS_DBG_LPS_LEAVE:
ret = _ps_dbg_lps_msg_hdlr(ps, msg);
break;
case MSG_EVT_PS_DBG_IPS_ENTER:
case MSG_EVT_PS_DBG_IPS_LEAVE:
ret = _ps_dbg_ips_msg_hdlr(ps, msg);
break;
case MSG_EVT_TX_PKT_NTFY:
ret = _tx_pkt_ntfy_msg_hdlr(ps, msg);
break;
default:
ret = MDL_RET_CANNOT_IO;
break;
}
return ret;
}
static enum phl_mdl_ret_code
_ps_mdl_msg_hdlr(void *dispr, void *priv, struct phl_msg *msg)
{
enum phl_mdl_ret_code ret = MDL_RET_IGNORE;
struct cmd_ps *ps = (struct cmd_ps *)priv;
if (IS_MSG_FAIL(msg->msg_id)) {
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): MDL_ID(%d)-EVT_ID(%d) fail.\n", __func__,
MSG_MDL_ID_FIELD(msg->msg_id), MSG_EVT_ID_FIELD(msg->msg_id));
return MDL_RET_SUCCESS;
}
switch (MSG_MDL_ID_FIELD(msg->msg_id)) {
case PHL_MDL_POWER_MGNT:
ret = _ps_mdl_hdl_internal_evt(dispr, ps, msg);
break;
default:
ret = _ps_mdl_hdl_external_evt(dispr, ps, msg);
break;
}
return ret;
}
static enum phl_mdl_ret_code
_ps_mdl_set_info(void *dispr, void *priv, struct phl_module_op_info *info)
{
struct cmd_ps *ps = (struct cmd_ps *)priv;
switch (info->op_code) {
case PS_MDL_OP_CANCEL_PWR_REQ:
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): cancel pwr req, evt_id %d\n", __func__, *(u16 *)info->inbuf);
if (ps->rej_pwr_req == false)
_cancel_pwr_req(ps, *(u16 *)info->inbuf);
break;
case PS_MDL_OP_BTC_PWR_REQ:
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): btc req pwr %d\n", __func__, *(bool *)info->inbuf);
ps->btc_req_pwr = *(bool *)info->inbuf;
break;
}
return MDL_RET_SUCCESS;
}
static enum phl_mdl_ret_code
_ps_mdl_query_info(void *dispr, void *priv, struct phl_module_op_info *info)
{
struct cmd_ps *ps = (struct cmd_ps *)priv;
u8 pwr_lvl = PS_PWR_LVL_PWROFF;
struct phl_cmd_ps_basic_info *basic_info = NULL;
/* PHL_INFO("[PS_CMD], %s(): opcode %d.\n", __func__, info->op_code); */
switch (info->op_code) {
case PS_MDL_OP_CUR_PWR_LVL:
pwr_lvl = ps->cur_pwr_lvl;
_os_mem_cpy(phl_to_drvpriv(ps->phl_info), (void *)info->inbuf,
&pwr_lvl, sizeof(pwr_lvl));
break;
case PS_MDL_OP_BASIC_INFO:
basic_info = (struct phl_cmd_ps_basic_info *)info->inbuf;
basic_info->ps_mode = ps->ps_mode;
basic_info->cur_pwr_lvl = ps->cur_pwr_lvl;
basic_info->rej_pwr_req = ps->rej_pwr_req;
basic_info->btc_req_pwr = ps->btc_req_pwr;
basic_info->rt_stop_rson = ps->rt_stop_rson;
basic_info->ap_active = ps->mr_info.ap_active;
basic_info->gc_active = ps->mr_info.gc_active;
basic_info->sta = ps->sta;
/* enter/leave ps reason */
_os_mem_set(phl_to_drvpriv(ps->phl_info), basic_info->enter_rson, 0, MAX_CMD_PS_RSON_LENGTH);
_os_mem_cpy(phl_to_drvpriv(ps->phl_info), basic_info->enter_rson, ps->enter_rson, MAX_CMD_PS_RSON_LENGTH);
_os_mem_set(phl_to_drvpriv(ps->phl_info), basic_info->leave_rson, 0, MAX_CMD_PS_RSON_LENGTH);
_os_mem_cpy(phl_to_drvpriv(ps->phl_info), basic_info->leave_rson, ps->leave_rson, MAX_CMD_PS_RSON_LENGTH);
break;
}
return MDL_RET_SUCCESS;
}
enum rtw_phl_status phl_register_ps_module(struct phl_info_t *phl_info)
{
enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE;
struct phl_cmd_dispatch_engine *disp_eng = &(phl_info->disp_eng);
struct phl_bk_module_ops bk_ops = {0};
u8 i = 0;
PHL_INFO("[PS_CMD], %s(): \n", __func__);
bk_ops.init = _ps_mdl_init;
bk_ops.deinit = _ps_mdl_deinit;
bk_ops.start = _ps_mdl_start;
bk_ops.stop = _ps_mdl_stop;
bk_ops.msg_hdlr = _ps_mdl_msg_hdlr;
bk_ops.set_info = _ps_mdl_set_info;
bk_ops.query_info = _ps_mdl_query_info;
for (i = 0; i < disp_eng->phy_num; i++) {
phl_status = phl_disp_eng_register_module(phl_info, i,
PHL_MDL_POWER_MGNT, &bk_ops);
if (phl_status != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("register cmd PS module of phy%d failed.\n", i + 1);
break;
}
}
return phl_status;
}
u8 phl_ps_get_cur_pwr_lvl(struct phl_info_t *phl_info)
{
struct phl_module_op_info op_info = {0};
u8 pwr_lvl = PS_PWR_LVL_MAX;
op_info.op_code = PS_MDL_OP_CUR_PWR_LVL;
op_info.inbuf = (u8 *)&pwr_lvl;
op_info.inlen = sizeof(pwr_lvl);
phl_disp_eng_query_bk_module_info(phl_info, HW_BAND_0,
PHL_MDL_POWER_MGNT, &op_info);
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS_CMD], %s(): pwr lvl(%s)\n", __func__, phl_ps_pwr_lvl_to_str(pwr_lvl));
return pwr_lvl;
}
bool phl_ps_is_datapath_allowed(struct phl_info_t *phl_info)
{
struct phl_module_op_info op_info = {0};
bool io_allowed = false;
u8 pwr_lvl = PS_PWR_LVL_MAX;
struct rtw_ps_cap_t *ps_cap = _get_ps_cap(phl_info);
if (!ps_cap->lps_pause_tx)
return true;
op_info.op_code = PS_MDL_OP_CUR_PWR_LVL;
op_info.inbuf = (u8 *)&pwr_lvl;
op_info.inlen = sizeof(pwr_lvl);
phl_disp_eng_query_bk_module_info(phl_info, HW_BAND_0,
PHL_MDL_POWER_MGNT, &op_info);
if (pwr_lvl == PS_PWR_LVL_PWRON)
io_allowed = true;
return io_allowed;
}
static void _ps_tx_pkt_ntfy_done(void *priv, struct phl_msg *msg)
{
struct phl_info_t *phl_info = (struct phl_info_t *)priv;
PHL_DBG("[PS_CMD], %s(): reset ntfy\n", __func__);
_os_atomic_set(phl_to_drvpriv(phl_info),
&phl_info->ps_info.tx_ntfy,
0);
}
void phl_ps_tx_pkt_ntfy(struct phl_info_t *phl_info)
{
struct phl_msg msg = {0};
struct phl_msg_attribute attr = {0};
if (phl_ps_get_cur_pwr_lvl(phl_info) == PS_PWR_LVL_PWRON)
return;
if (_os_atomic_read(phl_to_drvpriv(phl_info), &phl_info->ps_info.tx_ntfy)) {
PHL_DBG("[PS_CMD], %s(): already ntfy\n", __func__);
return;
}
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_POWER_MGNT);
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_TX_PKT_NTFY);
msg.band_idx = HW_BAND_0;
attr.completion.completion = _ps_tx_pkt_ntfy_done;
attr.completion.priv = phl_info;
_os_atomic_set(phl_to_drvpriv(phl_info),
&phl_info->ps_info.tx_ntfy,
1);
if (phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL) !=
RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): fail to send tx pkt notify.\n", __func__);
_os_atomic_set(phl_to_drvpriv(phl_info),
&phl_info->ps_info.tx_ntfy,
0);
}
return;
}
static void _ps_set_rt_cap_done(void *priv, struct phl_msg *msg)
{
struct phl_info_t *phl_info = (struct phl_info_t *)priv;
if (msg->inbuf && msg->inlen) {
_os_mem_free(phl_to_drvpriv(phl_info),
msg->inbuf, msg->inlen);
}
}
void rtw_phl_ps_set_rt_cap(void *phl, u8 band_idx, bool ps_allow, enum phl_ps_rt_rson rt_rson)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct phl_msg msg = {0};
struct phl_msg_attribute attr = {0};
struct rt_ps *ps_rt_info = NULL;
ps_rt_info = (struct rt_ps *)_os_mem_alloc(phl_to_drvpriv(phl_info), sizeof(struct rt_ps));
if (ps_rt_info == NULL) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): fail to alloc ps_rt_info memory.\n", __func__);
return;
}
ps_rt_info->rt_rson = rt_rson;
ps_rt_info->ps_allow = ps_allow;
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_POWER_MGNT);
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_PS_CAP_CHG);
msg.band_idx = band_idx;
msg.inbuf = (u8*)ps_rt_info;
msg.inlen = sizeof(struct rt_ps);
attr.completion.completion = _ps_set_rt_cap_done;
attr.completion.priv = phl_info;
if (phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL) !=
RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): fail to notify batter change.\n", __func__);
goto cmd_fail;
}
return;
cmd_fail:
_os_mem_free(phl_to_drvpriv(phl_info), ps_rt_info, sizeof(ps_rt_info));
}
void phl_ps_dbg_set_ps(struct phl_info_t *phl_info, u8 ps_mode, bool enter)
{
struct phl_msg msg = {0};
struct phl_msg_attribute attr = {0};
u16 evt_id = 0;
if (ps_mode == PS_MODE_LPS) {
evt_id = (enter ? MSG_EVT_PS_DBG_LPS_ENTER : MSG_EVT_PS_DBG_LPS_LEAVE);
} else if (ps_mode == PS_MODE_IPS) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS_CMD], %s(): dbg ips is not support now.\n", __func__);
/* evt_id = (enter ? MSG_EVT_PS_DBG_IPS_ENTER : MSG_EVT_PS_DBG_IPS_LEAVE); */
} else {
return;
}
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS_CMD], %s(): debug set %s %s\n", __func__,
phl_ps_ps_mode_to_str(ps_mode), (enter ? "enter" : "leave"));
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_POWER_MGNT);
SET_MSG_EVT_ID_FIELD(msg.msg_id, evt_id);
msg.band_idx = HW_BAND_0;
if (phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL) !=
RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS_CMD], %s(): fail to set dbg ps.\n", __func__);
}
return;
}
enum rtw_phl_status
rtw_phl_ps_set_rf_state(void *phl, u8 band_idx, enum rtw_rf_state rf_state)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
enum phl_msg_evt_id evt_id = (rf_state == RTW_RF_ON) ? MSG_EVT_RF_ON : MSG_EVT_RF_OFF;
phl_cmd_enqueue(phl_info, band_idx, evt_id, NULL, 0, NULL, PHL_CMD_WAIT, 0);
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status phl_ps_hal_pwr_req(struct rtw_phl_com_t *phl_com, u8 src, bool pwr_req)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
struct phl_module_op_info op_info = {0};
if (src != HAL_BTC_PWR_REQ)
return RTW_PHL_STATUS_FAILURE;
op_info.op_code = PS_MDL_OP_BTC_PWR_REQ;
op_info.inbuf = (u8 *)&pwr_req;
status = phl_disp_eng_set_bk_module_info(phl_info, HW_BAND_0,
PHL_MDL_POWER_MGNT, &op_info);
return status;
}
#endif