kernel_samsung_a34x-permissive/drivers/misc/mediatek/mddp/ctrl/mddp_sm.c
2024-04-28 15:51:13 +02:00

422 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* mddp_sm.c - MDDP state machine.
*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/slab.h>
#include <linux/mutex.h>
#include "mddp_ctrl.h"
#include "mddp_debug.h"
#include "mddp_filter.h"
#include "mddp_sm.h"
#include "mddp_usage.h"
//------------------------------------------------------------------------------
// Struct definition.
// -----------------------------------------------------------------------------
typedef int32_t (*mddp_sm_init_func_t)(struct mddp_app_t *app);
//------------------------------------------------------------------------------
// Global variables.
//------------------------------------------------------------------------------
static const mddp_sm_init_func_t mddp_sm_init_func_list_s[] = {
#undef MDDP_MODULE_ID
#undef MDDP_MODULE_PREFIX
#define MDDP_MODULE_ID(_id)
#define MDDP_MODULE_PREFIX(_prefix) _prefix ## _sm_init,
#include "mddp_app_config.h"
};
//------------------------------------------------------------------------------
// Private helper macro.
//------------------------------------------------------------------------------
#define MDDP_SM_LOCK_FLAG unsigned long flags
#define MDDP_SM_LOCK_INIT(_locker) spin_lock_init(&(_locker))
#define MDDP_SM_LOCK(_locker) spin_lock_irqsave(&(_locker), flags)
#define MDDP_SM_UNLOCK(_locker) spin_unlock_irqrestore(&(_locker), flags)
//------------------------------------------------------------------------------
// Private variables.
//------------------------------------------------------------------------------
static struct mddp_app_t mddp_app_inst_s[MDDP_APP_TYPE_CNT];
static struct mutex mddp_state_handler_mtx;
//------------------------------------------------------------------------------
// Private functions.
//------------------------------------------------------------------------------
static void _mddp_set_state(struct mddp_app_t *app, enum mddp_state_e new_state)
{
MDDP_SM_LOCK_FLAG;
MDDP_SM_LOCK(app->locker);
app->state = new_state;
MDDP_SM_UNLOCK(app->locker);
}
void mddp_check_feature(void)
{
struct mddp_md_msg_t *md_msg;
struct mddp_app_t *app;
md_msg = kzalloc(sizeof(struct mddp_md_msg_t), GFP_ATOMIC);
if (unlikely(!md_msg)) {
MDDP_F_LOG(MDDP_LL_NOTICE,
"%s: failed to alloc md_msg bug!\n", __func__);
return;
}
md_msg->msg_id = IPC_MSG_ID_MDFPM_CHECK_FEATURE_REQ;
md_msg->data_len = 0;
app = mddp_get_app_inst(MDDP_APP_TYPE_WH);
app->abnormal_flags |= MDDP_ABNORMAL_CHECK_FEATURE_ABSENT;
mddp_ipc_send_md(app, md_msg, MDFPM_USER_ID_MDFPM);
}
static void mddp_handshake_done(uint32_t feature)
{
struct mddp_app_t *app;
app = mddp_get_app_inst(MDDP_APP_TYPE_WH);
atomic_set(&app->feature, feature);
app->abnormal_flags &= ~MDDP_ABNORMAL_CHECK_FEATURE_ABSENT;
}
static int32_t mddp_sm_ctrl_msg_hdlr(
uint32_t msg_id,
void *buf,
uint32_t buf_len)
{
int32_t ret = 0;
switch (msg_id) {
case IPC_MSG_ID_MDFPM_CHECK_FEATURE_RSP:
mddp_handshake_done(*(uint32_t *)buf);
break;
default:
MDDP_S_LOG(MDDP_LL_ERR,
"%s: Unaccepted msg_id(%d)!\n",
__func__, msg_id);
ret = -EINVAL;
break;
}
return ret;
}
//------------------------------------------------------------------------------
// Public functions.
//------------------------------------------------------------------------------
int32_t mddp_sm_init(void)
{
struct mddp_app_t *app;
uint32_t type;
uint32_t idx;
memset(&mddp_app_inst_s, 0, sizeof(mddp_app_inst_s));
for (idx = 0; idx < MDDP_MOD_CNT; idx++) {
type = mddp_sm_module_list_s[idx];
app = mddp_get_app_inst(type);
app->type = type;
MDDP_SM_LOCK_INIT(app->locker);
_mddp_set_state(app, MDDP_STATE_UNINIT);
mddp_sm_init_func_list_s[idx](app);
atomic_set(&app->feature, 0);
}
mutex_init(&mddp_state_handler_mtx);
return 0;
}
void mddp_sm_uninit(void)
{
struct mddp_app_t *app;
uint32_t type;
uint32_t idx;
for (idx = 0; idx < MDDP_MOD_CNT; idx++) {
type = mddp_sm_module_list_s[idx];
app = mddp_get_app_inst(type);
mddp_sm_on_event(app, MDDP_EVT_FUNC_DISABLE);
}
memset(mddp_app_inst_s, 0, sizeof(mddp_app_inst_s));
}
struct mddp_app_t *mddp_get_default_app_inst(void)
{
return &mddp_app_inst_s[0];
}
struct mddp_app_t *mddp_get_app_inst(enum mddp_app_type_e type)
{
struct mddp_app_t *app;
app = &mddp_app_inst_s[type];
return app;
}
static enum mddp_state_e mddp_get_state(struct mddp_app_t *app)
{
return app->state;
}
enum mddp_state_e mddp_sm_set_state_by_md_rsp(struct mddp_app_t *app,
enum mddp_state_e prev_state,
bool md_rsp_result)
{
enum mddp_state_e curr_state;
enum mddp_state_e new_state = MDDP_STATE_DUMMY;
enum mddp_event_e event;
complete(&app->md_resp_comp);
curr_state = mddp_get_state(app);
event = (md_rsp_result) ? MDDP_EVT_MD_RSP_OK : MDDP_EVT_MD_RSP_FAIL;
if (curr_state == prev_state) {
/* OK.
* There is no interrupt event from upper module
* when MD handles this request.
*/
new_state = mddp_sm_on_event(app, event);
MDDP_S_LOG(MDDP_LL_NOTICE,
"%s: OK. event(%d), prev_state(%d) -> new_state(%d).\n",
__func__, event, prev_state, new_state);
return new_state;
}
/* DC (Don't Care).
* There are interrupt events from upper module
* when MD handles this request.
*/
MDDP_S_LOG(MDDP_LL_WARN,
"%s: DC. event(%d), prev_state(%d) -> new_state(%d).\n",
__func__, event, prev_state, new_state);
return MDDP_STATE_DUMMY;
}
#ifdef __MDDP_DEBUG__
void mddp_dump_sm_table(struct mddp_app_t *app)
{
uint32_t i, j;
struct mddp_sm_entry_t *state_machine;
struct mddp_sm_entry_t *entry;
MDDP_S_LOG(MDDP_LL_DEBUG,
"\n\n\t%s:\n==============================\n",
__func__);
for (i = 0; i < MDDP_STATE_CNT; i++) {
state_machine = app->state_machines[i];
MDDP_S_LOG(MDDP_LL_DEBUG, "\n=====(%d)=====\n", i);
for (j = 0; j < MDDP_EVT_CNT; j++) {
entry = state_machine + j;
if (entry->event == MDDP_EVT_DUMMY)
break;
MDDP_S_LOG(MDDP_LL_DEBUG,
"\tevt(%d), new_state(%d)\n",
entry->event, entry->new_state);
}
}
MDDP_S_LOG(MDDP_LL_DEBUG,
"\n ==============================\n\n");
}
#endif
enum mddp_state_e mddp_sm_on_event(struct mddp_app_t *app,
enum mddp_event_e event)
{
uint32_t idx;
enum mddp_state_e old_state;
enum mddp_state_e new_state;
struct mddp_sm_entry_t *state_machine;
struct mddp_sm_entry_t *entry;
mutex_lock(&mddp_state_handler_mtx);
new_state = old_state = mddp_get_state(app);
state_machine = app->state_machines[old_state];
for (idx = 0; idx < MDDP_EVT_CNT; idx++) {
entry = state_machine + idx;
if (event == entry->event) {
/*
* OK. Valid event for this state.
*/
new_state = entry->new_state;
if (new_state != MDDP_STATE_DUMMY)
_mddp_set_state(app, new_state);
mddp_dump_sm_table(app);
MDDP_S_LOG(MDDP_LL_WARN,
"%s: event(%d), old_state(%d) -> new_state(%d).\n",
__func__, event, old_state, new_state);
if (entry->action)
entry->action(app);
break;
} else if (entry->event == MDDP_EVT_DUMMY) {
/*
* NG. Unexpected event for this state!
*/
MDDP_S_LOG(MDDP_LL_WARN,
"%s: Invalid event(%d) for current state(%d)!\n",
__func__, event, old_state);
break;
}
}
mutex_unlock(&mddp_state_handler_mtx);
return new_state;
}
void mddp_sm_wait_pre(struct mddp_app_t *app)
{
init_completion(&app->md_resp_comp);
}
void mddp_sm_wait(struct mddp_app_t *app, enum mddp_event_e event)
{
enum mddp_state_e state;
state = mddp_get_state(app);
if ((state == MDDP_STATE_DISABLED) && (event != MDDP_EVT_FUNC_ENABLE))
return;
if ((state == MDDP_STATE_DEACTIVATED) && (event == MDDP_EVT_FUNC_DEACT))
return;
if (wait_for_completion_timeout(&app->md_resp_comp, msecs_to_jiffies(150)) == 0)
mddp_sm_on_event(app, MDDP_EVT_MD_RSP_TIMEOUT);
}
int32_t mddp_sm_msg_hdlr(
uint32_t user_id,
uint32_t msg_id,
void *buf,
uint32_t buf_len)
{
struct mddp_app_t *app = NULL;
int32_t ret = -ENODEV;
switch (user_id) {
case MDFPM_USER_ID_MDFPM:
ret = mddp_sm_ctrl_msg_hdlr(msg_id, buf, buf_len);
goto _done;
case MDFPM_USER_ID_WFPM:
app = mddp_get_app_inst(MDDP_APP_TYPE_WH);
break;
case MDFPM_USER_ID_DPFM:
ret = mddp_f_msg_hdlr(msg_id, buf, buf_len);
if (ret)
ret = mddp_u_msg_hdlr(msg_id, buf, buf_len);
goto _done;
default:
/*
* NG. Receive invalid ctrl_msg!
*/
MDDP_S_LOG(MDDP_LL_WARN,
"%s: Unaccepted user_id(%d)!\n",
__func__, user_id);
ret = -EINVAL;
goto _done;
}
/*
* OK. This app_type is configured.
*/
if (app && app->is_config) {
ret = app->md_recv_msg_hdlr(msg_id, buf, buf_len);
goto _done;
}
/*
* NG. This app_type is not configured!
*/
MDDP_S_LOG(MDDP_LL_ERR,
"%s: app is not configured, app(%p), user_id(%d), msg_id(%d)!\n",
__func__, app, user_id, msg_id);
_done:
return ret;
}
int32_t mddp_sm_reg_callback(
struct mddp_drv_conf_t *conf,
struct mddp_drv_handle_t *handle)
{
struct mddp_app_t *app;
app = mddp_get_app_inst(conf->app_type);
/*
* OK. This app_type is configured.
*/
if (app->is_config && app->reg_drv_callback) {
app->reg_drv_callback(handle);
memcpy(&app->drv_hdlr,
handle, sizeof(struct mddp_drv_handle_t));
app->drv_reg = 1;
app->abnormal_flags &= ~MDDP_ABNORMAL_WIFI_DRV_GET_FEATURE_BEFORE_MD_READY;
return 0;
}
/*
* NG. MDDP is not ready!
*/
MDDP_S_LOG(MDDP_LL_ERR,
"%s: Failed to reg callback, type(%d), config(%d)!\n",
__func__, conf->app_type, app->is_config);
return -EPERM;
}
void mddp_sm_dereg_callback(
struct mddp_drv_conf_t *conf,
struct mddp_drv_handle_t *handle)
{
struct mddp_app_t *app;
app = mddp_get_app_inst(conf->app_type);
/*
* OK. This app_type is configured.
*/
if (app->is_config && app->dereg_drv_callback) {
app->dereg_drv_callback(handle);
memset(&app->drv_hdlr,
0, sizeof(struct mddp_drv_handle_t));
app->drv_reg = 0;
return;
}
/*
* NG. MDDP is not ready!
*/
MDDP_S_LOG(MDDP_LL_ERR,
"%s: Failed to dereg callback, type(%d), config(%d)!\n",
__func__, conf->app_type, app->is_config);
return;
}