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

464 lines
13 KiB
C
Executable File

/******************************************************************************
*
* Copyright(c) 2019 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_CUSTOM_C_
#include "../phl_headers.h"
#include "phl_custom_fb.h"
#ifdef CONFIG_PHL_CUSTOM_FEATURE
#define MAX_DATA_SIZE (512)
enum custom_status {
CUS_STAT_CMD_USED = BIT0,
CUS_STAT_RPT_USED = BIT1,
CUS_STAT_RPT_SENDING = BIT2,
};
struct phl_custom_ctx {
struct phl_info_t *phl_info;
u8 status; /* refer to enum custom_status*/
u8 cmd_buf[MAX_DATA_SIZE];
u8 rpt_buf[MAX_DATA_SIZE];
#ifdef CONFIG_PHL_CUSTOM_FEATURE_FB
struct _custom_facebook_ctx fb_ctx;
#endif
};
bool
_custom_rpt_notify_check(void* priv, struct phl_msg* msg)
{
struct phl_custom_ctx *ctx = (struct phl_custom_ctx *)priv;
bool ret = true;
if (!TEST_STATUS_FLAG(ctx->status, CUS_STAT_RPT_USED) ||
TEST_STATUS_FLAG(ctx->status, CUS_STAT_RPT_SENDING)) {
ret = false;
}
return ret;
}
void
_custom_rpt_notify_complete(void* priv, struct phl_msg* msg)
{
struct phl_custom_ctx *ctx = (struct phl_custom_ctx *)priv;
CLEAR_STATUS_FLAG(ctx->status, CUS_STAT_RPT_USED);
CLEAR_STATUS_FLAG(ctx->status, CUS_STAT_RPT_SENDING);
}
void
_custom_rpt_notify_start(void* priv,
struct phl_msg* msg,
struct phl_msg_attribute* attr)
{
struct phl_custom_ctx *ctx = (struct phl_custom_ctx *)priv;
struct rtw_custom_decrpt *evt_rpt = (struct rtw_custom_decrpt *)ctx->rpt_buf;
SET_STATUS_FLAG(ctx->status, CUS_STAT_RPT_SENDING);
SET_MSG_MDL_ID_FIELD(msg->msg_id, PHL_MDL_CUSTOM);
SET_MSG_EVT_ID_FIELD(msg->msg_id, MSG_EVT_CUSTOM_CMD_DONE);
msg->inbuf = (u8*) evt_rpt;
msg->inlen = evt_rpt->len + sizeof(struct rtw_custom_decrpt);
attr->completion.completion = _custom_rpt_notify_complete;
attr->completion.priv = ctx;
}
static void
_indicate_custome_evt_rpt(struct phl_custom_ctx *ctx)
{
struct phl_msg msg = {0};
struct phl_msg_attribute attr = {0};
if (!_custom_rpt_notify_check((void*)ctx, &msg))
return;
_custom_rpt_notify_start((void*)ctx, &msg, &attr);
if (phl_msg_hub_send(ctx->phl_info,
&attr, &msg) != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("%s send msg fail\n", __func__);
_custom_rpt_notify_complete((void*)ctx, &msg);
}
}
enum rtw_phl_status
_phl_custom_prepare_default_fail_rpt(struct phl_custom_ctx *ctx,
struct phl_msg* msg)
{
struct rtw_custom_decrpt *cmd = (struct rtw_custom_decrpt *)(msg->inbuf);
u8 ret = MDL_RET_FAIL;
if (TEST_STATUS_FLAG(ctx->status, CUS_STAT_RPT_USED))
return RTW_PHL_STATUS_RESOURCE;
phl_custom_prepare_evt_rpt(ctx,
cmd->evt_id,
cmd->customer_id,
&ret,
1);
return RTW_PHL_STATUS_SUCCESS;
}
static enum phl_mdl_ret_code
_phl_custom_hdl_fail_evt(void* dispr,
struct phl_custom_ctx* ctx,
struct phl_msg* msg)
{
enum phl_mdl_ret_code ret = MDL_RET_IGNORE;
struct rtw_custom_decrpt *cmd = NULL;
cmd = (struct rtw_custom_decrpt *)(msg->inbuf);
switch (MSG_EVT_ID_FIELD(msg->msg_id)) {
case MSG_EVT_EDCA_ADJUST:
#ifdef CONFIG_PHL_CUSTOM_FEATURE_FB
if(cmd->customer_id == CUS_ID_FB) {
ret = phl_custom_hdl_fb_fail_evt(dispr,
ctx,
&(ctx->fb_ctx),
msg);
}
#else
ret = MDL_RET_IGNORE;
#endif
break;
default:
ret = MDL_RET_IGNORE;
break;
}
return ret;
}
static enum phl_mdl_ret_code
_phl_custom_hdl_internal_evt(void* dispr,
struct phl_custom_ctx* ctx,
struct phl_msg* msg)
{
enum phl_mdl_ret_code ret = MDL_RET_FAIL;
struct rtw_custom_decrpt *cmd = NULL;
cmd = (struct rtw_custom_decrpt *)(msg->inbuf);
switch (MSG_EVT_ID_FIELD(msg->msg_id)) {
case MSG_EVT_CUSTOME_FEATURE_ENABLE:
case MSG_EVT_CUSTOME_FEATURE_QUERY:
case MSG_EVT_CUSTOME_TESTMODE_PARAM:
case MSG_EVT_CUSTOME_SET_WIFI_ROLE:
case MSG_EVT_AMPDU_CFG:
case MSG_EVT_AMPDU_QUERY:
case MSG_EVT_PDTHR_CFG:
case MSG_EVT_PDTHR_QUERY:
case MSG_EVT_POP_CFG:
case MSG_EVT_POP_QUERY:
#ifdef CONFIG_PHL_CUSTOM_FEATURE_FB
if(cmd->customer_id == CUS_ID_FB) {
ret = phl_custom_hdl_fb_evt(dispr,
ctx,
&(ctx->fb_ctx),
msg);
}
#else
ret = MDL_RET_IGNORE;
#endif
break;
default:
ret = MDL_RET_IGNORE;
break;
}
return ret;
}
static enum phl_mdl_ret_code
_phl_custom_hdl_external_evt(void* dispr,
struct phl_custom_ctx* ctx,
struct phl_msg* msg)
{
return MDL_RET_IGNORE;
}
void
_phl_custom_cmd_completion(void* priv, struct phl_msg* msg)
{
struct phl_custom_ctx *ctx = (struct phl_custom_ctx *)priv;
CLEAR_STATUS_FLAG(ctx->status, CUS_STAT_CMD_USED);
}
enum phl_mdl_ret_code
_phl_custom_mdl_init(void* phl_info,
void* dispr,
void** priv)
{
struct phl_info_t *phl = (struct phl_info_t *)phl_info;
struct phl_custom_ctx* ctx = NULL;
void *d = phl_to_drvpriv(phl);
FUNCIN();
if (priv == NULL)
return MDL_RET_FAIL;
(*priv) = NULL;
ctx = (struct phl_custom_ctx *)_os_mem_alloc(d, sizeof(struct phl_custom_ctx));
if (ctx == NULL) {
PHL_ERR(" %s, alloc fail\n",__FUNCTION__);
return MDL_RET_FAIL;
}
ctx->phl_info = phl_info;
(*priv) = (void*)ctx;
PHL_INFO(" %s, size phl_custom_ctx(%d)\n",
__FUNCTION__, (int)sizeof(struct phl_custom_ctx));
return MDL_RET_SUCCESS;
}
void
_phl_custom_mdl_deinit(void* dispr, void* priv)
{
struct phl_custom_ctx *ctx = (struct phl_custom_ctx*)priv;
void *d = phl_to_drvpriv(ctx->phl_info);
_os_mem_free(d, ctx, sizeof(struct phl_custom_ctx));
PHL_INFO(" %s\n", __FUNCTION__);
}
enum phl_mdl_ret_code
_phl_custom_mdl_start(void* dispr, void* priv)
{
enum phl_mdl_ret_code ret = MDL_RET_FAIL;
FUNCIN();
FUNCOUT();
ret = MDL_RET_SUCCESS;
return ret;
}
enum phl_mdl_ret_code
_phl_custom_mdl_stop(void* dispr, void* priv)
{
enum phl_mdl_ret_code ret = MDL_RET_FAIL;
FUNCIN();
FUNCOUT();
ret = MDL_RET_SUCCESS;
return ret;
}
enum phl_mdl_ret_code
_phl_custom_mdl_msg_hdlr(void* dispr,
void* priv,
struct phl_msg* msg)
{
enum phl_mdl_ret_code ret = MDL_RET_IGNORE;
struct phl_custom_ctx *ctx = (struct phl_custom_ctx*)priv;
FUNCIN();
if (IS_MSG_FAIL(msg->msg_id)) {
ret = _phl_custom_hdl_fail_evt(dispr, priv, msg);
_indicate_custome_evt_rpt(ctx);
return MDL_RET_IGNORE;
}
switch (MSG_MDL_ID_FIELD(msg->msg_id)) {
case PHL_MDL_CUSTOM:
ret = _phl_custom_hdl_internal_evt(dispr, priv, msg);
break;
default:
ret = _phl_custom_hdl_external_evt(dispr, priv, msg);
break;
}
_indicate_custome_evt_rpt(ctx);
FUNCOUT();
return ret;
}
enum phl_mdl_ret_code
_phl_custom_mdl_set_info(void* dispr,
void* priv,
struct phl_module_op_info* info)
{
enum phl_mdl_ret_code ret = MDL_RET_IGNORE;
struct phl_custom_ctx *ctx = (struct phl_custom_ctx *)priv;
void *d = phl_to_drvpriv(ctx->phl_info);
struct phl_msg msg = {0};
struct phl_msg_attribute attr = {0};
struct rtw_custom_decrpt *cmd = NULL;
u8 idx = 0xff;
phl_dispr_get_idx(dispr, &idx);
FUNCIN();
switch (info->op_code) {
case BK_MODL_OP_INPUT_CMD:
if (TEST_STATUS_FLAG(ctx->status, CUS_STAT_CMD_USED) ||
info->inlen > MAX_DATA_SIZE) {
PHL_ERR("%s buf len err or used\n", __func__);
break;
}
SET_STATUS_FLAG(ctx->status, CUS_STAT_CMD_USED);
_os_mem_cpy(d, ctx->cmd_buf, info->inbuf, info->inlen);
cmd = (struct rtw_custom_decrpt *)ctx->cmd_buf;
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_CUSTOM);
SET_MSG_EVT_ID_FIELD(msg.msg_id, (u16)cmd->evt_id);
msg.inbuf = ctx->cmd_buf;
msg.inlen = info->inlen;
attr.completion.priv = priv;
attr.completion.completion = _phl_custom_cmd_completion;
msg.band_idx = idx;
if (phl_disp_eng_send_msg(ctx->phl_info,
&msg,
&attr,
NULL) == RTW_PHL_STATUS_SUCCESS) {
ret = MDL_RET_SUCCESS;
} else {
CLEAR_STATUS_FLAG(ctx->status, CUS_STAT_CMD_USED);
PHL_ERR("%s send msg fail\n", __func__);
ret = MDL_RET_FAIL;
}
break;
case BK_MODL_OP_CUS_SET_ROLE_CAP:
ret = phl_custom_fb_set_role_cap(dispr,
(void *)ctx,
&ctx->fb_ctx,
info);
break;
default:
break;
}
FUNCOUT();
return ret;
}
enum phl_mdl_ret_code
_phl_custom_mdl_query_info(void* dispr,
void* priv,
struct phl_module_op_info* info)
{
enum phl_mdl_ret_code ret = MDL_RET_IGNORE;
struct phl_custom_ctx *ctx = (struct phl_custom_ctx *)priv;
void *d = phl_to_drvpriv(ctx->phl_info);
struct phl_msg msg = {0};
struct phl_msg_attribute attr = {0};
struct rtw_custom_decrpt *cmd = NULL;
u8 idx = 0xff;
FUNCIN();
switch(info->op_code) {
case BK_MODL_OP_INPUT_CMD:
if (TEST_STATUS_FLAG(ctx->status, CUS_STAT_CMD_USED) ||
info->inlen > MAX_DATA_SIZE) {
PHL_ERR("%s buf len err or used\n", __func__);
break;
}
SET_STATUS_FLAG(ctx->status, CUS_STAT_CMD_USED);
_os_mem_cpy(d, ctx->cmd_buf, info->inbuf, info->inlen);
_os_mem_cpy(d, ctx->rpt_buf, info->outbuf, info->outlen);
cmd = (struct rtw_custom_decrpt *)ctx->cmd_buf;
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_CUSTOM);
SET_MSG_EVT_ID_FIELD(msg.msg_id, (u16)cmd->evt_id);
msg.inbuf = ctx->cmd_buf;
msg.inlen = info->inlen;
msg.outbuf = ctx->rpt_buf;
msg.outlen = info->outlen;
attr.completion.priv = priv;
attr.completion.completion = _phl_custom_cmd_completion;
msg.band_idx = idx;
if (phl_disp_eng_send_msg(ctx->phl_info,
&msg,
&attr,
NULL) == RTW_PHL_STATUS_SUCCESS) {
ret = MDL_RET_SUCCESS;
} else {
CLEAR_STATUS_FLAG(ctx->status, CUS_STAT_CMD_USED);
PHL_ERR("%s send msg fail\n", __func__);
ret = MDL_RET_FAIL;
}
break;
case BK_MODL_OP_CUS_UPDATE_ROLE_CAP:
ret = phl_custom_fb_update_opt_ie(dispr, (void*)ctx, &ctx->fb_ctx, info);
break;
default:
break;
}
FUNCOUT();
return ret;
}
enum rtw_phl_status
phl_register_custom_module(struct phl_info_t *phl_info, u8 band_idx)
{
enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE;
struct phl_bk_module_ops bk_ops = {0};
bk_ops.init = _phl_custom_mdl_init;
bk_ops.deinit = _phl_custom_mdl_deinit;
bk_ops.start = _phl_custom_mdl_start;
bk_ops.stop = _phl_custom_mdl_stop;
bk_ops.msg_hdlr = _phl_custom_mdl_msg_hdlr;
bk_ops.query_info = _phl_custom_mdl_query_info;
bk_ops.set_info = _phl_custom_mdl_set_info;
phl_status = phl_disp_eng_register_module(phl_info,
band_idx,
PHL_MDL_CUSTOM,
&bk_ops);
if (RTW_PHL_STATUS_SUCCESS != phl_status)
PHL_ERR("%s register Custom module in cmd disp failed\n", __func__);
return phl_status;
}
enum rtw_phl_status
phl_custom_prepare_evt_rpt(void *custom_ctx,
u32 evt_id,
u32 customer_id,
u8 *rpt,
u32 rpt_len)
{
struct phl_custom_ctx *ctx = (struct phl_custom_ctx *)custom_ctx;
struct rtw_custom_decrpt *evt_rpt = (struct rtw_custom_decrpt *)ctx->rpt_buf;
void *d = phl_to_drvpriv(ctx->phl_info);
u8* val = (u8*)(evt_rpt +1);
if ((rpt_len > (MAX_DATA_SIZE - sizeof(struct rtw_custom_decrpt))) ||
TEST_STATUS_FLAG(ctx->status, CUS_STAT_RPT_USED))
return RTW_PHL_STATUS_RESOURCE;
SET_STATUS_FLAG(ctx->status, CUS_STAT_RPT_USED);
evt_rpt->evt_id = evt_id;
evt_rpt->customer_id = customer_id;
evt_rpt->len = rpt_len;
_os_mem_cpy(d, val, rpt, rpt_len);
return RTW_PHL_STATUS_SUCCESS;
}
struct phl_info_t*
phl_custom_get_phl_info(void *custom_ctx)
{
struct phl_custom_ctx *ctx = (struct phl_custom_ctx *)custom_ctx;
return ctx->phl_info;
}
/**
* phl_custom_init_role_cap
* 1. role cap customization
* input:
* @phl_info: (struct phl_info_t *)
* @hw_band:hw_band
* @role_cap: (struct role_cap_t)
* return: rtw_phl_status
*/
enum rtw_phl_status
phl_custom_init_role_cap(struct phl_info_t *phl_info,
u8 hw_band,
struct role_cap_t *role_cap)
{
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
status = phl_custom_fb_init_role_cap(phl_info, hw_band, role_cap);
return status;
}
#endif