/****************************************************************************** * * Copyright(c) 2019 - 2020 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. * * Author: vincent_fann@realtek.com * *****************************************************************************/ #include "phl_headers.h" #ifdef CONFIG_FSM /* #define DEBUG_CMD_FSM_MODULE */ #define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) #ifdef FSM_DBG_MEM_OVERWRITE #define _os_kmem_alloc(a, b) fsm_kmalloc(b) #define _os_kmem_free(a, b, c) fsm_kfree(b, c) #endif #define pstr(s) (s +_os_strlen((u8 *)s)) #define lstr(s, l) (size_t)(l - _os_strlen((u8 *)s)) #ifdef CONFIG_RTW_SCAN_FSM struct _adapter; #endif #define MAX_POWER_ON_TIME 100 #define WDOG_ALARM_ID 1 #define WD_DURATION 2000 /* 2sec */ enum CMD_STATE_ST { CMD_ST_IDLE, CMD_ST_REQ_PWR, CMD_ST_SERVICE }; enum CMD_EV_ID { CMD_EV_REQ_PWR, CMD_EV_DO_JOB, CMD_EV_JOB_NOTIFY, CMD_EV_PWR_ON_OK, CMD_EV_PWR_ON_TIMEOUT, CMD_EV_PWR_EXPIRE, CMD_EV_WD_EXPIRE, CMD_EV_WD_DO_JOB, CMD_EV_MAX }; static int cmd_idle_st_hdl(void *obj, u16 event, void *param); static int cmd_req_pwr_st_hdl(void *obj, u16 event, void *param); static int cmd_service_st_hdl(void *obj, u16 event, void *param); /* STATE table */ static struct fsm_state_ent cmd_state_tbl[] = { ST_ENT(CMD_ST_IDLE, cmd_idle_st_hdl), ST_ENT(CMD_ST_REQ_PWR, cmd_req_pwr_st_hdl), ST_ENT(CMD_ST_SERVICE, cmd_service_st_hdl), }; /* EVENT table */ static struct fsm_event_ent cmd_event_tbl[] = { EV_ENT(CMD_EV_REQ_PWR), EV_ENT(CMD_EV_DO_JOB), EV_ENT(CMD_EV_JOB_NOTIFY), EV_ENT(CMD_EV_PWR_ON_OK), EV_ENT(CMD_EV_PWR_ON_TIMEOUT), EV_ENT(CMD_EV_PWR_EXPIRE), EV_DBG(CMD_EV_WD_EXPIRE), EV_ENT(CMD_EV_WD_DO_JOB), EV_ENT(CMD_EV_MAX) /* EV_MAX for fsm safety checking */ }; /* * command thread state sub function */ void cmd_pm_cb(void *phl, void *hdl, void *ctx, enum rtw_phl_status stat) { struct _cmd_obj *pcmd = (struct _cmd_obj *)ctx; if (stat == RTW_PHL_STATUS_SUCCESS) phl_fsm_gen_msg(phl, pcmd->fsm_obj, NULL, 0, CMD_EV_PWR_ON_OK); else phl_fsm_gen_msg(phl, pcmd->fsm_obj, NULL, 0, FSM_EV_CANCEL); } int cmd_dequeue_job(struct _cmd_obj *pcmd) { void *d = phl_to_drvpriv(pcmd->phl_info); struct fsm_msg *msg; msg = phl_fsm_dequeue_ext(pcmd->fsm); if (msg == NULL) return -1; if (phl_fsm_sent_msg(pcmd->fsm_obj, msg) != RTW_PHL_STATUS_SUCCESS) { if (msg->param && msg->param_sz) _os_kmem_free(d, (u8 *)msg->param, msg->param_sz); _os_kmem_free(d, (u8 *)msg, sizeof(*msg)); return -1; } return 0; } static void cmd_do_wdog_job(struct _cmd_obj *pcmd) { #if defined(PHL_PLATFORM_LINUX) void *d = phl_to_drvpriv(pcmd->phl_info); struct phl_cmd_job *job; /* Tempoary test in Linux, * make sure Windows doesn't run to here */ _os_mutex_lock(d, &pcmd->wd_q_lock); phl_list_for_loop(job, struct phl_cmd_job, &pcmd->wd_q, list) { job->u.cmd.fptr(job->u.cmd.priv, job->u.cmd.parm, false); #ifdef DEBUG_CMD_FSM_MODULE FSM_INFO(pcmd->fsm, "%s: wdog %s:%s\n", phl_fsm_obj_name(pcmd->fsm_obj), job_name(pcmd, (u8)job->id), (char *)job->u.cmd.name); #endif } _os_mutex_unlock(d, &pcmd->wd_q_lock); rtw_phl_watchdog_callback(pcmd->phl_info); #endif } static void cmd_add_wdog_event(struct _cmd_obj *pcmd) { struct fsm_msg *msg; msg = phl_fsm_new_msg(pcmd->fsm_obj, CMD_EV_WD_DO_JOB); if (msg == NULL) return; /* Always enqueue msg to extra queue */ phl_fsm_enqueue_ext(pcmd->fsm, msg, 0); } /* * CMD state handler */ /* * cmd idle handler * This state has no power; Able to run no_io job * For jobs don't need to request power (no_io job) */ static int cmd_idle_st_hdl(void *obj, u16 event, void *param) { struct _cmd_obj *pcmd = (struct _cmd_obj *)obj; int rtn = FSM_FREE_PARAM; /* has no power */ switch (event) { case FSM_EV_SWITCH_IN: phl_fsm_set_alarm_ext(pcmd->fsm_obj, WD_DURATION, CMD_EV_WD_EXPIRE, WDOG_ALARM_ID, NULL); break; case FSM_EV_STATE_IN: break; case CMD_EV_WD_EXPIRE: /* restart watchdog alarm */ phl_fsm_set_alarm_ext(pcmd->fsm_obj, WD_DURATION, CMD_EV_WD_EXPIRE, WDOG_ALARM_ID, NULL); if (pcmd->wdog_pwr_level < PWR_BASIC_IO) { cmd_do_wdog_job(pcmd); break; } /* We need to request power */ cmd_add_wdog_event(pcmd); /* fall through */ case CMD_EV_REQ_PWR: phl_fsm_state_goto(pcmd->fsm_obj, CMD_ST_REQ_PWR); break; case CMD_EV_JOB_NOTIFY: cmd_dequeue_job(pcmd); break; case CMD_EV_DO_JOB: /* TODO check MUST BE no_io cmd */ rtn = phl_cmd_do_job(pcmd, param); break; case FSM_EV_STATE_OUT: break; case FSM_EV_SWITCH_OUT: phl_fsm_cancel_alarm_ext(pcmd->fsm_obj, WDOG_ALARM_ID); break; default: break; } return rtn; } /* This stete is designed to request power */ static int cmd_req_pwr_st_hdl(void *obj, u16 event, void *param) { struct _cmd_obj *pcmd = (struct _cmd_obj *)obj; struct fsm_msg *msg; int rtn = FSM_FREE_PARAM; enum rtw_phl_status phl_st; switch (event) { case FSM_EV_STATE_IN: /* TODO: request power */ phl_st = RTW_PHL_STATUS_SUCCESS; if (phl_st == RTW_PHL_STATUS_PENDING) { /* we have to wait CMD_EV_PWR_ON_OK */ phl_fsm_set_alarm(pcmd->fsm_obj, MAX_POWER_ON_TIME, CMD_EV_PWR_ON_TIMEOUT); break; } if (phl_st != RTW_PHL_STATUS_SUCCESS) { FSM_ERR(pcmd->fsm, "%s: power on fail(%d)\n", phl_fsm_obj_name(pcmd->fsm_obj), phl_st); phl_fsm_state_goto(pcmd->fsm_obj, CMD_ST_IDLE); /* drop fail cmd */ msg = phl_fsm_dequeue_ext(pcmd->fsm); if (msg != NULL) cmd_discard_msg_job(pcmd, msg); break; } /* RTW_PHL_STATUS_SUCCESS */ /* fall through */ case CMD_EV_PWR_ON_OK: pcmd->has_power = true; phl_fsm_state_goto(pcmd->fsm_obj, CMD_ST_SERVICE); break; case CMD_EV_PWR_ON_TIMEOUT: case FSM_EV_CANCEL: phl_fsm_state_goto(pcmd->fsm_obj, CMD_ST_IDLE); break; case CMD_EV_WD_EXPIRE: /* restart watchdog alarm */ phl_fsm_set_alarm_ext(pcmd->fsm_obj, WD_DURATION, CMD_EV_WD_EXPIRE, WDOG_ALARM_ID, NULL); /* enqueue watchdog job */ cmd_add_wdog_event(pcmd); break; case FSM_EV_STATE_OUT: phl_fsm_cancel_alarm(pcmd->fsm_obj); break; default: break; } return rtn; } /* This state has basic power supply. * Able to do both PWR_BASIC_IO and PWR_NO_IO jobs. */ static int cmd_service_st_hdl(void *obj, u16 event, void *param) { struct _cmd_obj *pcmd = (struct _cmd_obj *)obj; struct phl_cmd_job *job; int rtn = FSM_FREE_PARAM; switch (event) { case FSM_EV_STATE_IN: case CMD_EV_JOB_NOTIFY: cmd_dequeue_job(pcmd); break; case CMD_EV_DO_JOB: rtn = phl_cmd_do_job(pcmd, param); /* hold power for a while */ job = (struct phl_cmd_job *)param; if (job->pwr_level >= PWR_BASIC_IO) phl_fsm_set_alarm(pcmd->fsm_obj, MAX_POWER_ON_TIME, CMD_EV_PWR_EXPIRE); /* dequeue jobs */ cmd_dequeue_job(pcmd); break; case CMD_EV_WD_EXPIRE: case CMD_EV_WD_DO_JOB: cmd_do_wdog_job(pcmd); /* hold power for a while */ phl_fsm_set_alarm(pcmd->fsm_obj, MAX_POWER_ON_TIME, CMD_EV_PWR_EXPIRE); /* restart watchdog alarm */ phl_fsm_set_alarm_ext(pcmd->fsm_obj, WD_DURATION, CMD_EV_WD_EXPIRE, WDOG_ALARM_ID, NULL); break; case CMD_EV_PWR_EXPIRE: /* make sure no more commands */ if (cmd_dequeue_job(pcmd) >= 0) break; /* no more commands */ /* fall through */ case FSM_EV_CANCEL: phl_fsm_state_goto(pcmd->fsm_obj, CMD_ST_IDLE); break; case FSM_EV_STATE_OUT: phl_fsm_cancel_alarm(pcmd->fsm_obj); pcmd->has_power = false; break; default: break; } return rtn; } static void cmd_dump_obj(void *obj, char *p, int *sz) { /* nothing to do for now */ } static void cmd_dump_fsm(void *fsm, char *p, int *sz) { /* nothing to do for now */ } static void cmd_dbg_help(struct _cmd_obj *pcmd, char *p, int *sz) { int len = *sz; _os_snprintf(pstr(p), lstr(p, len), "usage:\n\t<%s> ,\n", phl_fsm_obj_name(pcmd->fsm_obj)); *sz = len; } static void cmd_debug(void *obj, char input[][MAX_ARGV], u32 input_num, char *output, u32 *out_len) { struct _cmd_obj *pcmd = (struct _cmd_obj *)obj; char *ptr = output; int len = *out_len; if (input_num < 2) { cmd_dbg_help(pcmd, ptr, &len); goto done; } if (!_os_strcmp(input[0], "wdog")) { if (!_os_strcmp(input[1], "pause")) { /* wdog,pause */ rtw_phl_cmd_pause_wdog(pcmd->phl_info, "debug cmd"); _os_snprintf(pstr(ptr), lstr(ptr, len), "\n%s: pause watchdog\n", phl_fsm_obj_name(pcmd->fsm_obj)); } else if (!_os_strcmp(input[1], "resume")) { /* wdog,resume */ rtw_phl_cmd_resume_wdog(pcmd->phl_info, "debug cmd"); _os_snprintf(pstr(ptr), lstr(ptr, len), "\n%s: resume watchdog\n", phl_fsm_obj_name(pcmd->fsm_obj)); } } else cmd_dbg_help(pcmd, ptr, &len); done: *out_len = len; } /* return value * 0: timeout * >0: success */ static int wait_completion(void *d, struct phl_cmd_job *job, int m_sec) { job->wait.max_wait_time = m_sec; job->wait.submit_time = _os_get_cur_time_ms(); _os_event_init(d, &(job->wait.done)); /* wait here */ return _os_event_wait(d, &(job->wait.done), m_sec); } /* For EXTERNAL application to create a cmd FSM */ /* @root: FSM root structure * @phl_info: private data structure to invoke hal/phl function * * return * fsm_main: FSM main structure (Do NOT expose) */ struct fsm_main *phl_cmd_new_fsm(struct fsm_root *root, struct phl_info_t *phl_info) { void *d = phl_to_drvpriv(phl_info); struct fsm_main *fsm = NULL; struct rtw_phl_fsm_tb tb; _os_mem_set(d, &tb, 0, sizeof(tb)); tb.max_state = sizeof(cmd_state_tbl)/sizeof(cmd_state_tbl[0]); tb.max_event = sizeof(cmd_event_tbl)/sizeof(cmd_event_tbl[0]); tb.state_tbl = cmd_state_tbl; tb.evt_tbl = cmd_event_tbl; tb.dump_obj = cmd_dump_obj; tb.dump_fsm = cmd_dump_fsm; tb.dbg_level = FSM_DBG_WARN; tb.evt_level = FSM_DBG_WARN; tb.debug = cmd_debug; fsm = phl_fsm_init_fsm(root, "cmd", phl_info, &tb); return fsm; } /* For EXTERNAL application to destory cmd fsm */ /* @fsm: see fsm_main */ void phl_cmd_destory_fsm(struct fsm_main *fsm) { if (fsm == NULL) return; /* deinit fsm local variable if has */ /* call FSM Framewro to deinit fsm */ phl_fsm_deinit_fsm(fsm); } /* For EXTERNAL application to create command object */ /* @fsm: FSM main structure which created by phl_cmd_new_fsm() * @phl_info: private data structure to invoke hal/phl function * * return * _cmd_obj: structure of command object (Do NOT expose) */ struct _cmd_obj *phl_cmd_new_obj(struct fsm_main *fsm, struct phl_info_t *phl_info) { void *d = phl_to_drvpriv(phl_info); struct fsm_obj *obj; struct _cmd_obj *pcmd; pcmd = phl_fsm_new_obj(fsm, (void **)&obj, sizeof(*pcmd)); if (pcmd == NULL) { FSM_ERR(fsm, "cmd: malloc obj fail\n"); return NULL; } pcmd->fsm = fsm; pcmd->fsm_obj = obj; pcmd->phl_info = phl_info; pcmd->has_power = false; cmd_set_job_tbl(pcmd); INIT_LIST_HEAD(&pcmd->wd_q); _os_mutex_init(d, &pcmd->wd_q_lock); return pcmd; } /* For EXTERNAL application to destory command object */ /* @pcmd: local created command object * */ void phl_cmd_destory_obj(struct _cmd_obj *pcmd) { void *d; struct phl_cmd_job *job, *job_t; if (pcmd == NULL) return; d = phl_to_drvpriv(pcmd->phl_info); /* deinit and free all local variables */ /* watchdog job */ /* TODO spin lock is not necessary */ phl_list_for_loop_safe(job, job_t, struct phl_cmd_job, &pcmd->wd_q, list) { _os_mutex_lock(d, &pcmd->wd_q_lock); list_del(&job->list); _os_mutex_unlock(d, &pcmd->wd_q_lock); if (job->id == JOB_RUN_FUNC) { if (job->u.cmd.parm && job->u.cmd.parm_sz != 0) _os_kmem_free(d, (u8 *)job->u.cmd.parm, job->u.cmd.parm_sz); _os_kmem_free(d, (u8 *)job, sizeof(*job)); } else { FSM_ERR(pcmd->fsm, "%s: free wdog %s fail\n", phl_fsm_obj_name(pcmd->fsm_obj), job_name(pcmd, (u8)job->id)); } } _os_mutex_deinit(d, &pcmd->wd_q_lock); /* inform FSM framewory to recycle fsm_obj */ phl_fsm_destory_obj(pcmd->fsm_obj); } /* For EXTERNAL application to pause all watchdog jobs (expose) */ /* @phl: phl private structure * @reason: reason for pause watchdog (option) */ enum rtw_phl_status rtw_phl_cmd_pause_wdog(void *phl, char *reason) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct _cmd_obj *pcmd = phl_info->cmd_obj; phl_fsm_pause_alarm_ext(pcmd->fsm_obj, WDOG_ALARM_ID); pcmd->wdog_pause_num++; FSM_INFO(pcmd->fsm, "%s: pause wdog (%s) %d\n", phl_fsm_obj_name(pcmd->fsm_obj), (reason == NULL) ? "" : reason, pcmd->wdog_pause_num); return RTW_PHL_STATUS_SUCCESS; } /* For EXTERNAL application to resume all watchdog jobs (expose) */ /* @phl: phl private structure * @reason: reason for resume watchdog (option) */ enum rtw_phl_status rtw_phl_cmd_resume_wdog(void *phl, char *reason) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct _cmd_obj *pcmd = phl_info->cmd_obj; FSM_INFO(pcmd->fsm, "%s: resume wdog (%s) %d\n", phl_fsm_obj_name(pcmd->fsm_obj), (reason == NULL) ? "" : reason, pcmd->wdog_pause_num); if (pcmd->wdog_pause_num == 0) return RTW_PHL_STATUS_FAILURE; if (--(pcmd->wdog_pause_num) != 0) return RTW_PHL_STATUS_SUCCESS; /* reset timer to original period */ phl_fsm_set_alarm_ext(pcmd->fsm_obj, WD_DURATION, CMD_EV_WD_EXPIRE, WDOG_ALARM_ID, NULL); phl_fsm_resume_alarm_ext(pcmd->fsm_obj, WDOG_ALARM_ID); return RTW_PHL_STATUS_SUCCESS; } /* For EXTERNAL application to register watchdog job (expose) */ /* @phl: phl private structure * @func: function pointer to be runed * @priv: 1st param of function pointer * @parm: 2nd param of function pointer * @name: function name for debug message (optional) * @pwr: refers to enum PWR_LEVEL */ enum rtw_phl_status rtw_phl_job_reg_wdog(void *phl, int (*fptr)(void *priv, void *param, bool discard), void *priv, void *parm, int parm_sz, char *name, enum PWR_LEVEL pwr) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct _cmd_obj *pcmd = phl_info->cmd_obj; struct phl_cmd_job *job; void *d = phl_to_drvpriv(phl_info); char wd[] = "wdog"; if (phl_info->cmd_obj == NULL) { PHL_ERR("cmd: %s fsm module doesn't init \n", __func__); return -1; } /* allocate memory for job parameter */ job = _os_kmem_alloc(d, sizeof(*job)); if (job == NULL) { FSM_ERR(pcmd->fsm, "%s: alloc wdog job fail\n", phl_fsm_obj_name(pcmd->fsm_obj)); return RTW_PHL_STATUS_RESOURCE; } _os_mem_set(d, job, 0, sizeof(job)); job->id = JOB_RUN_FUNC; job->pwr_level = pwr; job->u.cmd.fptr = fptr; job->u.cmd.priv = priv; job->u.cmd.parm = parm; job->u.cmd.parm_sz = parm_sz; _os_mem_set(d, job->u.cmd.name, 0, RTW_PHL_JOB_NAME_LEN); if (name != NULL) _os_mem_cpy(d, job->u.cmd.name, name, MIN((RTW_PHL_JOB_NAME_LEN - 1), _os_strlen((u8 *)name))); else _os_mem_cpy(d, job->u.cmd.name, wd, MIN((RTW_PHL_JOB_NAME_LEN - 1), _os_strlen((u8 *)wd))); pcmd->wdog_pwr_level = (u8)MAX(pwr, pcmd->wdog_pwr_level); _os_mutex_lock(d, &pcmd->wd_q_lock); list_add_tail(&job->list, &pcmd->wd_q); _os_mutex_unlock(d, &pcmd->wd_q_lock); FSM_INFO(pcmd->fsm, "%s: wdog hooks %s, pwr_level = %d\n", phl_fsm_obj_name(pcmd->fsm_obj), job->u.cmd.name, pwr); return RTW_PHL_STATUS_SUCCESS; } /* For EXTERNAL application to invoke command service (expose) */ /* @pcmd: cmd object * @msg: refert to struct fsm_msg */ enum rtw_phl_status phl_cmd_enqueue_and_wait_job(struct _cmd_obj *pcmd, struct fsm_msg *msg) { struct phl_cmd_job *job = (struct phl_cmd_job *)msg->param; void *d = phl_to_drvpriv(pcmd->phl_info); int max_wait_time = 1000; int remain = 0; #if 0 FSM_INFO(pcmd->fsm, "%s: enqueue %s:%s\n", phl_fsm_obj_name(pcmd->fsm_obj), job_name(pcmd, (u8)job->id), (char *)job->u.cmd.name); #endif /* Always enqueue msg to extra queue */ phl_fsm_enqueue_ext(pcmd->fsm, msg, 0); if ((pcmd->has_power == false) && (job->pwr_level >= PWR_BASIC_IO)) /* request power */ phl_fsm_gen_msg(pcmd->phl_info, pcmd->fsm_obj, NULL, 0, CMD_EV_REQ_PWR); else phl_fsm_gen_msg(pcmd->phl_info, pcmd->fsm_obj, NULL, 0, CMD_EV_JOB_NOTIFY); if (job->wait.sync == JOB_WAIT_COMPLETION) { job->wait.result = JOB_SUCCESS; /* WAIT completion; context switch */ remain = wait_completion(d, job, max_wait_time); if (remain == 0) { job->wait.result = JOB_TIMEOUT; FSM_WARN(pcmd->fsm, "job: %s timeout %d ms\n", job_name(pcmd, (u8)job->id), max_wait_time); return RTW_PHL_STATUS_FAILURE; } } return RTW_PHL_STATUS_SUCCESS; } /* For EXTERNAL application to invoke command service (expose) */ /* @phl: refer to struct phl_info_t * @pjob: job to be completed */ enum rtw_phl_status phl_cmd_complete_job(void *phl, struct phl_cmd_job *pjob) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct _cmd_obj *pcmd = phl_info->cmd_obj; struct phl_cmd_job *job; struct fsm_msg *msg; void *d = phl_to_drvpriv(pcmd->phl_info); if (pjob == NULL) return RTW_PHL_STATUS_FAILURE; #ifdef HAS_NO_COMPLETION if (pjob->wait.sync == JOB_WAIT_COMPLETION) { pjob->wait.sync = JOB_ASYNC; FSM_ERR(pcmd->fsm, "cmd: %s doesn't support SYNC mdoe use ASYNC mode\n", job_name(pcmd, (u8)pjob->id)); } #endif /* NEW message to start cmd service */ msg = phl_fsm_new_msg(pcmd->fsm_obj, CMD_EV_DO_JOB); if (msg == NULL) { FSM_ERR(pcmd->fsm, "cmd: alloc msg fail\n"); return RTW_PHL_STATUS_RESOURCE; } /* allocate memory for command parameter */ if (pjob->wait.sync == JOB_WAIT_COMPLETION) { /* TODO check interrupt context */ /* SYNC mode, use call loacl job varable */ job = pjob; } else { /* ASYNC mode */ /* allocate memory for command parameter */ job = _os_kmem_alloc(d, sizeof(*job)); if (job == NULL) { FSM_ERR(pcmd->fsm, "cmd: alloc job fail\n"); _os_kmem_free(d, (u8 *)msg, sizeof(*msg)); return RTW_PHL_STATUS_RESOURCE; } _os_mem_cpy(d, job, pjob, sizeof(*job)); } msg->param = (void *)job; msg->param_sz = sizeof(*job); return phl_cmd_enqueue_and_wait_job(pcmd, msg); } /* For EXTERNAL application to start command service (expose) */ /* @pcmd: cmd object */ enum rtw_phl_status phl_cmd_start(struct _cmd_obj *pcmd) { /* Start FSM */ return phl_fsm_start_fsm(pcmd->fsm); } /* For EXTERNAL application to stop cmd obj * @phl_info: private data structure to invoke hal/phl function * */ void phl_fsm_cmd_stop(struct phl_info_t *phl_info) { struct _cmd_obj *pcmd = phl_info->cmd_obj; //void *d = phl_to_drvpriv(pcmd->phl_info); struct phl_cmd_job *job; struct fsm_msg *msg; while ((msg = phl_fsm_dequeue_ext(pcmd->fsm)) != NULL) { job = (struct phl_cmd_job *)msg->param; cmd_discard_msg_job(pcmd, msg); } phl_fsm_stop_fsm(phl_info->cmd_fsm); } /* For EXTERNAL application to stop cmd service (expose) */ /* @pcmd: cmd job will be cancelled */ enum rtw_phl_status phl_cmd_cancel(struct _cmd_obj *pcmd) { #ifdef PHL_INCLUDE_FSM return phl_fsm_cancel_obj(pcmd->fsm_obj); #else return RTW_PHL_STATUS_FAILURE; #endif /* PHL_INCLUDE_FSM */ } #endif /*CONFIG_FSM*/