/****************************************************************************** * * 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 #include "phl_scan.h" #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 SCAN_PROBE_TIMES 3 /* issue max 3 probe_req per channel */ #define SCAN_PROBE_INTERVAL 10 /* preobe_req interval 10ms */ #define SCAN_NULL_PKT_TIME 50 /* 50ms */ #define ALL_TOKEN 0 #define MAX_POWER_ON_TIME 500 #define BACK_OP_CH_DUR_MS 100 #define ALM_MAX_SCAN_TIME 1 #define ALM_PROBE_REQ 2 #define ALM_NEXT_CH 3 #define ALM_BKOP_OFF_CH 4 #ifndef pstr #define pstr(s) (s + _os_strlen((u8 *)s)) #endif #ifndef lstr #define lstr(s, l) (size_t)(l - _os_strlen((u8 *)s)) #endif enum SCAN_EV_ID { SCAN_EV_START, SCAN_EV_REQ_PWR_OK, SCAN_EV_REQ_PWR_FAIL, SCAN_EV_REQ_PWR_TIMEOUT, SCAN_EV_NEXT_CH, SCAN_EV_NEXT_PROBE_REQ, SCAN_EV_PAUSE, SCAN_EV_RESUME, SCAN_EV_FORCE_ACTIVE, SCAN_EV_NOTIFY_PENDING_SCAN_REQ, SCAN_EV_PS_ANN_DONE, SCAN_EV_BKOP_OFF_CH_EXPIRE, SCAN_EV_BKOP_ON_CH_EXPIRE, SCAN_EV_NOTIFY_OFF_CH_CMD, SCAN_EV_MAX }; enum SCAN_STATE_ST { SCAN_ST_IDLE, SCAN_ST_REQ_PWR, SCAN_ST_OFF_CH, SCAN_ST_BACK_OP }; static int scan_idle_st_hdl(void *obj, u16 event, void *param); static int scan_req_pwr_st_hdl(void *obj, u16 event, void *param); static int scan_off_ch_st_hdl(void *obj, u16 event, void *param); static int scan_back_op_st_hdl(void *obj, u16 event, void *param); /* STATE table */ static struct fsm_state_ent scan_state_tbl[] = { ST_ENT(SCAN_ST_IDLE, scan_idle_st_hdl), ST_ENT(SCAN_ST_REQ_PWR, scan_req_pwr_st_hdl), ST_ENT(SCAN_ST_OFF_CH, scan_off_ch_st_hdl), ST_ENT(SCAN_ST_BACK_OP, scan_back_op_st_hdl) }; /* EVENT table */ static struct fsm_event_ent scan_event_tbl[] = { EV_ENT(SCAN_EV_START), EV_ENT(SCAN_EV_REQ_PWR_OK), EV_ENT(SCAN_EV_REQ_PWR_FAIL), EV_ENT(SCAN_EV_REQ_PWR_TIMEOUT), EV_ENT(SCAN_EV_NEXT_CH), EV_ENT(SCAN_EV_NEXT_PROBE_REQ), EV_ENT(SCAN_EV_PAUSE), EV_ENT(SCAN_EV_RESUME), EV_ENT(SCAN_EV_FORCE_ACTIVE), EV_ENT(SCAN_EV_NOTIFY_PENDING_SCAN_REQ), EV_ENT(SCAN_EV_PS_ANN_DONE), EV_ENT(SCAN_EV_BKOP_OFF_CH_EXPIRE), EV_ENT(SCAN_EV_BKOP_ON_CH_EXPIRE), EV_ENT(SCAN_EV_NOTIFY_OFF_CH_CMD), EV_ENT(SCAN_EV_MAX) }; struct scan_obj { struct fsm_main *fsm; struct fsm_obj *fsm_obj; struct rtw_phl_scan_param *param; struct phl_info_t *phl_info; struct phl_queue req_q; struct phl_queue off_ch_cmd_q; u32 token; /* global increase when new scan request is comming */ u32 token_running; /* token of running scan */ u8 fltr_mode; /* backup filter mode before off channel */ u8 candidate; /* Guarantee we have one candidate */ u8 probe_cnts; u8 off_ch_step; /* back op */ bool back_op_is_required; struct phl_scan_channel back_op_ch; struct rtw_phl_scan_ops *ops; }; /* * SCAN state sub function */ #define OFF_CH_STEP_CLEAR 0x00 #define OFF_CH_SET_FILTER 0x01 #define OFF_CH_PAUSE_TX 0x02 void off_ch_set_step(struct scan_obj *pscan, u8 step) { pscan->off_ch_step |= step; } void off_ch_clr_step(struct scan_obj *pscan, u8 step) { pscan->off_ch_step &= ~step; } bool off_ch_chk_step(struct scan_obj *pscan, u8 step) { if (pscan->off_ch_step & step) return true; return false; } /* * stop netif queue * notify the AP about us leaving the channel and stop all STA interfaces. * Stop queues and transmit all frames queued by the driver before * sending nullfunc to enable powersave at the AP. */ static int drv_offchannel_stop_vifs(struct scan_obj *pscan) { // reference ieee80211_offchannel_stop_vifs(); /* TODO stop upper netif ac queues */ /* stop lower macid */ return 0; } /* return to OP channel */ static int off_ch_return_set_ch_bw(struct scan_obj *pscan) { enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = pscan->phl_info; struct rtw_wifi_role_t *wrole = pscan->param->wifi_role; struct rtw_chan_def chandef = {0}; if (wrole == NULL) return -1; phl_sts = phl_mr_get_chandef(phl_info, wrole, false, &chandef); if (phl_sts != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("%s phl_mr_get_chandef failed\n", __func__); return -2; } PHL_DUMP_CHAN_DEF_EX(&chandef); phl_set_ch_bw(wrole, &chandef, false); FSM_INFO(pscan->fsm, "%s %s() channel=%d, bw=%d, offest=%d\n", phl_fsm_obj_name(pscan->fsm_obj), __func__, chandef.chan, chandef.bw, chandef.offset); return 0; } #ifdef CONFIG_RTW_ACS static void scan_acs_mntr_trigger(struct scan_obj *pscan, struct phl_scan_channel *scan_ch) { u16 monitor_time = scan_ch->duration - MONITOR_TIME_TOLERANCE; phl_acs_mntr_trigger(pscan->phl_info, (u8)pscan->param->ch_idx, scan_ch->channel, monitor_time); } static void scan_acs_mntr_result(struct scan_obj *pscan) { if (pscan->param->ch_idx < 0) return; phl_acs_mntr_result(pscan->phl_info); } #endif /* CONFIG_RTW_ACS */ /* inform CORE ahd HAL SCAN is complete */ static int scan_complete(struct scan_obj *pscan) { struct phl_info_t *phl_info = (struct phl_info_t *)pscan->phl_info; struct rtw_phl_scan_param *param = pscan->param; u8 band_idx = 0; enum phl_phy_idx phy_idx = HW_PHY_0; if (param->ch_num == (pscan->param->ch_idx + 1)) param->result = SCAN_REQ_COMPLETE; param->end_time = _os_get_cur_time_ms(); /* dump scan time */ param->total_scan_time = phl_fsm_time_pass(param->enqueue_time); FSM_INFO(pscan->fsm, "%s takes %d ms to scan %d/%d channels\n", phl_fsm_obj_name(pscan->fsm_obj), param->total_scan_time, param->ch_idx + 1, param->ch_num); if (pscan->param->wifi_role) band_idx = pscan->param->wifi_role->hw_band; else band_idx = 0; if (band_idx == HW_BAND_1) phy_idx = HW_PHY_1; rtw_hal_btc_scan_finish_ntfy(phl_info->hal, phy_idx); rtw_hal_notification(phl_info->hal, MSG_EVT_SCAN_END, band_idx); phl_p2pps_noa_resume_all(phl_info, param->wifi_role); #ifdef CONFIG_MCC_SUPPORT if (phl_mr_coex_handle(phl_info, param->wifi_role, 0, param->wifi_role->hw_band, MR_COEX_TRIG_BY_SCAN) != RTW_PHL_STATUS_SUCCESS) { FSM_ERR(pscan->fsm, "%s enable MCC failed\n", phl_fsm_obj_name(pscan->fsm_obj)); return -1; } #endif /* CONFIG_MCC_SUPPORT */ if (pscan->ops && pscan->ops->scan_complete) pscan->ops->scan_complete(pscan->param->priv, pscan->param); return 0; } static void scan_set_timer(struct scan_obj *pscan, u32 duration, u16 event) { FSM_DBG(pscan->fsm, "%s %s() duration=%d\n", phl_fsm_obj_name(pscan->fsm_obj), __func__, duration); phl_fsm_set_alarm(pscan->fsm_obj, duration, event); } /* Announce null data if needing * @pscan: scan obj * @off_ch: * true: go off channel (issue null-1) * false: back to OP channel (issue null-0) * need_wait: * false: issue null packet is not necessary * true: null packet issued (caller have to wait null data transmission) * * return: 0 success, negativ value: fail */ static int scan_off_chan(struct scan_obj *pscan, bool off_ch, bool *need_wait) { struct rtw_phl_com_t *phl_com = pscan->phl_info->phl_com; struct mr_ctl_t *mr_ctl = phlcom_to_mr_ctrl(phl_com); struct rtw_wifi_role_t *wrole = pscan->param->wifi_role; struct hw_band_ctl_t *band_ctl = &(mr_ctl->band_ctrl[wrole->hw_band]); enum rtw_phl_status psts = RTW_PHL_STATUS_SUCCESS; *need_wait = false; psts = phl_mr_offch_hdl(pscan->phl_info, pscan->param->wifi_role, off_ch, pscan->param->priv, pscan->ops->scan_issue_null_data); /* call back fail */ if (psts != RTW_PHL_STATUS_SUCCESS) return -1; if ((band_ctl->cur_info.ld_sta_num) > 0) *need_wait = false; /* ap is currently operating */ if ((band_ctl->cur_info.ap_num) > 0) pscan->back_op_is_required = true; if ((band_ctl->cur_info.ld_sta_num + band_ctl->cur_info.ld_ap_num) > 0) pscan->back_op_is_required = true; FSM_INFO(pscan->fsm, "%s %s() off_ch=%d, back_op_is_requred=%d\n", phl_fsm_obj_name(pscan->fsm_obj), __func__, off_ch, pscan->back_op_is_required); return 0; } /* Move channel index and decide scan channel */ static struct phl_scan_channel *scan_select_channel( struct scan_obj *pscan) { struct rtw_phl_scan_param *p = pscan->param; if (p->ch_idx == -1) goto next_ch; /* back_op acording to op_ch_count */ if (p->back_op_mode == SCAN_BKOP_CNT && pscan->back_op_is_required && p->scan_ch && (p->scan_ch->scan_mode != BACKOP_MODE) && ((p->ch_idx + 1) % p->back_op_ch_cnt == 0)) { /* goto back op channel */ p->scan_ch = &pscan->back_op_ch; goto done; /* do back_op */ } else if (p->back_op_mode == SCAN_BKOP_TIMER && p->scan_ch && (p->scan_ch->scan_mode == BACKOP_MODE)) { /* we just return form op ch; * stay at the same channel for now */ p->scan_ch = &p->ch[p->ch_idx]; goto done; } next_ch: p->ch_idx++; if (p->ch_idx == p->ch_num) { /* no more channel for now */ if (p->repeat > 0) { /* go back to the first channel */ p->ch_idx = 0; FSM_INFO(pscan->fsm, "%s repeat=%d\n", phl_fsm_obj_name(pscan->fsm_obj), p->repeat); /* 255 means loop forever */ if (p->repeat != 255) p->repeat--; } else { p->ch_idx--; return NULL; /* we are done */ } } p->scan_ch = &p->ch[p->ch_idx]; if (p->scan_ch->scan_mode == BACKOP_MODE && pscan->back_op_is_required == false) goto next_ch; done: FSM_INFO(pscan->fsm, "%s ch_num = %d/%d, ch = %d\n", phl_fsm_obj_name(pscan->fsm_obj), (p->ch_idx + 1), p->ch_num, p->scan_ch->channel); return p->scan_ch; } /* Switch channel */ static void scan_set_channel_bw(struct scan_obj *pscan, u16 ch, enum channel_width bw, enum chan_offset offset, u8 phy_idx) { struct rtw_chan_def chdef = {0}; FSM_INFO(pscan->fsm, "%s %s() ch=%d, bw=%d, offest=%d, duration=%d\n", phl_fsm_obj_name(pscan->fsm_obj), __func__, ch, bw, offset, pscan->param->scan_ch->duration); chdef.chan = (u8)ch; chdef.bw = bw; chdef.offset = offset; phl_set_ch_bw(pscan->param->wifi_role, &chdef, false); if (pscan->ops->scan_ch_ready) pscan->ops->scan_ch_ready(pscan->param->priv, pscan->param); } /* call back caller to issue probe request */ static void scan_issue_probereq(struct scan_obj *pscan) { if (pscan->param->scan_ch->type == RTW_PHL_SCAN_PASSIVE) return; if (pscan->ops->scan_issue_pbreq) pscan->ops->scan_issue_pbreq(pscan->param->priv, pscan->param); } /* flush NIC TX FIFO */ static int scan_flush_tx_queue(struct scan_obj *pscan) { enum rtw_hal_status hal_status = RTW_HAL_STATUS_SUCCESS; hal_status = rtw_hal_scan_flush_queue(pscan->phl_info->hal, pscan->param->wifi_role); if (hal_status != RTW_HAL_STATUS_SUCCESS) FSM_WARN(pscan->fsm, "%s flush tx queue fail\n", phl_fsm_obj_name(pscan->fsm_obj)); return hal_status; } /* Pause NIC TX FIFO */ static int scan_pause_tx(struct scan_obj *pscan) { struct phl_info_t *phl_info = (struct phl_info_t *)pscan->phl_info; enum rtw_hal_status hal_status = RTW_HAL_STATUS_SUCCESS; bool off_ch = true; /* we want to off-ch - pause TX */ hal_status = rtw_hal_scan_pause_tx_fifo(phl_info->hal, pscan->param->wifi_role->hw_band, off_ch); if (hal_status == RTW_HAL_STATUS_SUCCESS) off_ch_set_step(pscan, OFF_CH_PAUSE_TX); else FSM_WARN(pscan->fsm, "%s pause tx fifo fail\n", phl_fsm_obj_name(pscan->fsm_obj)); return hal_status; } static int scan_resume_tx(struct scan_obj *pscan) { struct phl_info_t *phl_info = (struct phl_info_t *)pscan->phl_info; enum rtw_hal_status hal_status = RTW_HAL_STATUS_SUCCESS; bool off_ch = false; /* we want to op-ch - resume TX */ /* Do not resume un-paused TX */ if (!off_ch_chk_step(pscan, OFF_CH_PAUSE_TX)) return RTW_HAL_STATUS_SUCCESS; hal_status = rtw_hal_scan_pause_tx_fifo(phl_info->hal, pscan->param->wifi_role->hw_band, off_ch); off_ch_clr_step(pscan, OFF_CH_PAUSE_TX); if (hal_status != RTW_HAL_STATUS_SUCCESS) FSM_WARN(pscan->fsm, "%s resume tx fifo fail\n", phl_fsm_obj_name(pscan->fsm_obj)); return hal_status; } static int scan_set_filter(struct scan_obj *pscan) { enum rtw_hal_status hal_status = RTW_HAL_STATUS_SUCCESS; bool off_ch = true; hal_status = rtw_hal_scan_set_rxfltr_by_mode(pscan->phl_info->hal, pscan->param->wifi_role->hw_band, off_ch, &pscan->fltr_mode); if (hal_status == RTW_HAL_STATUS_SUCCESS) off_ch_set_step(pscan, OFF_CH_SET_FILTER); return hal_status; } static int scan_clr_filter(struct scan_obj *pscan) { enum rtw_hal_status hal_status = RTW_HAL_STATUS_SUCCESS; bool off_ch = false; /* Do not clear clear filter */ if (!off_ch_chk_step(pscan, OFF_CH_SET_FILTER)) return hal_status; hal_status = rtw_hal_scan_set_rxfltr_by_mode(pscan->phl_info->hal, pscan->param->wifi_role->hw_band, off_ch, &pscan->fltr_mode); off_ch_clr_step(pscan, OFF_CH_SET_FILTER); if (hal_status != RTW_HAL_STATUS_SUCCESS) FSM_WARN(pscan->fsm, "%s clear filter fail\n", phl_fsm_obj_name(pscan->fsm_obj)); return hal_status; } /* inform CORE and HAL SCAN is starting */ static int scan_start(struct scan_obj *pscan) { struct phl_info_t *phl_info = (struct phl_info_t *)pscan->phl_info; struct rtw_hal_com_t *hal_com = rtw_hal_get_halcom(phl_info->hal); struct rtw_phl_scan_param *param = pscan->param; u32 pass_time, remain_time, end_time; enum band_type band = BAND_ON_5G; u8 band_idx = 0; enum phl_phy_idx phy_idx = HW_PHY_0; FSM_INFO(pscan->fsm, "%s %s()\n", phl_fsm_obj_name(pscan->fsm_obj), __func__); #ifdef CONFIG_MCC_SUPPORT if (phl_mr_coex_disable(phl_info, param->wifi_role, param->wifi_role->hw_band, MR_COEX_TRIG_BY_SCAN) != RTW_PHL_STATUS_SUCCESS) { FSM_ERR(pscan->fsm, "%s disable MCC failed\n", phl_fsm_obj_name(pscan->fsm_obj)); return -1; } #endif /* CONFIG_MCC_SUPPORT */ phl_p2pps_noa_pause_all(phl_info, param->wifi_role); param->start_time = _os_get_cur_time_ms(); /* check max scan time */ if (param->max_scan_time > 0) { end_time = param->enqueue_time + param->max_scan_time; pass_time = phl_fsm_time_pass(param->enqueue_time); if (pass_time >= param->max_scan_time) { FSM_ERR(pscan->fsm, "%s Timeout! pass_time %d > max_time %d\n", phl_fsm_obj_name(pscan->fsm_obj), pass_time, param->max_scan_time); return -1; } remain_time = phl_fsm_time_left(param->enqueue_time, end_time); FSM_INFO(pscan->fsm, "%s: max_time = %d ms, remain = %d ms\n", phl_fsm_obj_name(pscan->fsm_obj), param->max_scan_time, remain_time); phl_fsm_set_alarm_ext(pscan->fsm_obj, remain_time, FSM_EV_CANCEL, ALM_MAX_SCAN_TIME, NULL); } if (param->wifi_role) band_idx = param->wifi_role->hw_band; else band_idx = 0; if (band_idx == HW_BAND_1) phy_idx = HW_PHY_1; band = hal_com->band[band_idx].cur_chandef.band; rtw_hal_btc_scan_start_ntfy(phl_info->hal, phy_idx, band); rtw_hal_notification(phl_info->hal, MSG_EVT_SCAN_START, band_idx); /* [scan start notify] */ if (pscan->ops->scan_start) pscan->ops->scan_start(pscan->param->priv, param); pscan->token_running = param->token; pscan->param->result = SCAN_REQ_CANCEL; return 0; } static void scan_free_req(struct scan_obj *pscan, struct fsm_msg *msg) { void *d = phl_to_drvpriv(pscan->phl_info); struct rtw_phl_scan_param *scan_param = (struct rtw_phl_scan_param *)msg->param; if (msg->param != NULL) { if (scan_param->ch != NULL) _os_kmem_free(d, scan_param->ch, scan_param->ch_sz); _os_kmem_free(d, msg->param, msg->param_sz); } _os_kmem_free(d, (void *)msg, sizeof(*msg)); } static void scan_free_msg(struct scan_obj *pscan, struct fsm_msg *msg) { void *d = phl_to_drvpriv(pscan->phl_info); _os_kmem_free(d, msg->param, msg->param_sz); _os_kmem_free(d, msg, sizeof(*msg)); } void scan_ps_cb(void *phl, void *hdl, void *ctx, enum rtw_phl_status stat) { struct scan_obj *pscan = (struct scan_obj *)ctx; if (stat == RTW_PHL_STATUS_SUCCESS) phl_fsm_gen_msg(phl, pscan->fsm_obj, NULL, 0, SCAN_EV_REQ_PWR_OK); else phl_fsm_gen_msg(phl, pscan->fsm_obj, NULL, 0, SCAN_EV_REQ_PWR_FAIL); } static bool chk_pending_req(struct scan_obj *pscan) { void *d = phl_to_drvpriv(pscan->phl_info); _os_list *obj; /* Make sure we only have one candidate */ if (pscan->candidate) return true; /* Check pending scan request * Dequeue extra_queue and enqueue back to msg_queue */ if (!pq_pop(d, &pscan->req_q, &obj, _first, _bh)) return false; pscan->candidate = 1; if (phl_fsm_sent_msg(pscan->fsm_obj, (struct fsm_msg *)obj) != RTW_PHL_STATUS_SUCCESS) { scan_free_req(pscan, (struct fsm_msg *)obj); return false; } return true; /* has pending req */ } /* * SCAN state handler */ /* * scan idle handler */ static int scan_idle_st_hdl(void *obj, u16 event, void *param) { struct scan_obj *pscan = (struct scan_obj *)obj; struct rtw_phl_scan_param *scan_param; void *d = phl_to_drvpriv(pscan->phl_info); int rtn = FSM_FREE_PARAM; switch (event) { case FSM_EV_STATE_IN: phl_fsm_cancel_alarm_ext(pscan->fsm_obj, ALM_MAX_SCAN_TIME); scan_complete(pscan); /* clear all used data */ _os_kmem_free(d, pscan->param->ch, pscan->param->ch_sz); _os_kmem_free(d, pscan->param, sizeof(*pscan->param)); pscan->param = NULL; pscan->token_running = 0; pscan->back_op_is_required = false; pscan->back_op_ch.duration = BACK_OP_CH_DUR_MS; if (chk_pending_req(pscan)) break; break; case SCAN_EV_START: /* save param from caller */ scan_param = (struct rtw_phl_scan_param *)param; pscan->param = scan_param; pscan->ops = scan_param->ops; if (scan_param->back_op_ch_dur_ms) pscan->back_op_ch.duration = scan_param->back_op_ch_dur_ms; /* Tell FSM framework DON'T free param * Scan fsm will free it when scan is done */ rtn = FSM_KEEP_PARAM; phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_REQ_PWR); pscan->candidate = 0; break; case SCAN_EV_NOTIFY_PENDING_SCAN_REQ: chk_pending_req(pscan); break; case FSM_EV_STATE_OUT: phl_fsm_cancel_alarm(pscan->fsm_obj); break; default: break; } return rtn; } static int scan_req_pwr_st_hdl(void *obj, u16 event, void *param) { struct scan_obj *pscan = (struct scan_obj *)obj; int rtn = FSM_FREE_PARAM; enum rtw_phl_status phl_st = RTW_PHL_STATUS_SUCCESS; switch (event) { case FSM_EV_STATE_IN: if (phl_st == RTW_PHL_STATUS_PENDING) { /* we have to wait SCAN_EV_REQ_PWR_OK */ phl_fsm_set_alarm(pscan->fsm_obj, MAX_POWER_ON_TIME, SCAN_EV_REQ_PWR_TIMEOUT); break; } if (phl_st != RTW_PHL_STATUS_SUCCESS) { FSM_ERR(pscan->fsm, "%s power on fail(%d)\n", phl_fsm_obj_name(pscan->fsm_obj), phl_st); phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_IDLE); break; } /* fall through */ case SCAN_EV_REQ_PWR_OK: if (scan_start(pscan) < 0) { phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_IDLE); break; } phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_OFF_CH); break; case SCAN_EV_REQ_PWR_FAIL: case SCAN_EV_REQ_PWR_TIMEOUT: FSM_ERR(pscan->fsm, "%s power on fail\n", phl_fsm_obj_name(pscan->fsm_obj)); /* fall through */ case FSM_EV_CANCEL: phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_IDLE); break; case FSM_EV_STATE_OUT: phl_fsm_cancel_alarm(pscan->fsm_obj); break; default: break; } return rtn; } int handle_off_ch_cmd(struct scan_obj *pscan) { struct rtw_phl_scan_param *param = pscan->param; void *d = phl_to_drvpriv(pscan->phl_info); int off_ch_tx_num = pscan->off_ch_cmd_q.cnt; struct fsm_msg *msg; _os_list *obj; while (pq_pop(d, &pscan->off_ch_cmd_q, &obj, _first, _bh)) { msg = (struct fsm_msg *)obj; if (param && param->ops && param->ops->scan_off_ch_tx) param->ops->scan_off_ch_tx(param->priv, param, msg->param); scan_free_msg(pscan, msg); } return off_ch_tx_num; } /* swtich channel and scan */ static int scan_off_ch_st_hdl(void *obj, u16 event, void *param) { struct scan_obj *pscan = (struct scan_obj *)obj; struct rtw_phl_scan_param *p = pscan->param; struct phl_scan_channel *scan_ch; bool need_wait; switch (event) { case FSM_EV_STATE_IN: pscan->off_ch_step = OFF_CH_STEP_CLEAR; if (scan_off_chan(pscan, true, &need_wait) < 0) { /* issue Null-1 fail */ phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_IDLE); break; } if (need_wait == true) { /* TODO wait tx report */ scan_set_timer(pscan, SCAN_NULL_PKT_TIME, SCAN_EV_PS_ANN_DONE); break; } /* fall through */ case SCAN_EV_PS_ANN_DONE: scan_flush_tx_queue(pscan); scan_pause_tx(pscan); scan_set_filter(pscan); /* start back_op_off_ch_dur */ if ((p->back_op_mode == SCAN_BKOP_TIMER) && pscan->back_op_is_required) phl_fsm_set_alarm_ext(pscan->fsm_obj, p->back_op_off_ch_dur_ms, SCAN_EV_BKOP_OFF_CH_EXPIRE, ALM_BKOP_OFF_CH, NULL); /* dequeue off_ch_cmd */ if (handle_off_ch_cmd(pscan) > 0) { if (pscan->back_op_is_required == true) phl_fsm_extend_alarm_ext(pscan->fsm_obj, pscan->param->back_op_off_ch_ext_dur_ms, ALM_BKOP_OFF_CH); else phl_fsm_extend_alarm_ext(pscan->fsm_obj, pscan->param->back_op_off_ch_ext_dur_ms, ALM_NEXT_CH); } /* fall through */ case SCAN_EV_NEXT_CH: phl_fsm_cancel_alarm_ext(pscan->fsm_obj, ALM_PROBE_REQ); #ifdef CONFIG_RTW_ACS scan_acs_mntr_result(pscan); #endif scan_ch = scan_select_channel(pscan); if (scan_ch == NULL) { /* no more channel, we are done */ phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_IDLE); break; } if (scan_ch->scan_mode == BACKOP_MODE) { phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_BACK_OP); break; } pscan->probe_cnts = SCAN_PROBE_TIMES; scan_set_channel_bw(pscan, scan_ch->channel, CHANNEL_WIDTH_20, CHAN_OFFSET_NO_EXT, 0); #ifdef CONFIG_RTW_ACS scan_acs_mntr_trigger(pscan, scan_ch); #endif if (phl_fsm_is_alarm_off_ext( pscan->fsm_obj, ALM_NEXT_CH)) { phl_fsm_set_alarm_ext(pscan->fsm_obj, scan_ch->duration, SCAN_EV_NEXT_CH, ALM_NEXT_CH, NULL); } /* fall through */ case SCAN_EV_NEXT_PROBE_REQ: if (pscan->probe_cnts-- == 0) break; if (pscan->ops->scan_issue_pbreq == NULL || p->scan_ch->type == RTW_PHL_SCAN_PASSIVE) break; scan_issue_probereq(pscan); phl_fsm_set_alarm_ext(pscan->fsm_obj, SCAN_PROBE_INTERVAL, SCAN_EV_NEXT_PROBE_REQ, ALM_PROBE_REQ, NULL); break; case SCAN_EV_BKOP_OFF_CH_EXPIRE: p->scan_ch = &pscan->back_op_ch; phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_BACK_OP); break; case SCAN_EV_FORCE_ACTIVE: /* Are we in active scan channel */ if (p->scan_ch->type == RTW_PHL_SCAN_ACTIVE) break; scan_ch = (struct phl_scan_channel *)param; /* request ch mismaches current scan ch */ if (scan_ch->channel != p->scan_ch->channel) break; #ifdef RTW_WKARD_PHL_FSM_SCAN_PASSIVE_TO_ACTIVE if (p->scan_ch->ext_act_scan == EXT_ACT_SCAN_ENABLE) p->scan_ch->ext_act_scan = EXT_ACT_SCAN_DONE; else { FSM_INFO(pscan->fsm, "ch(%d) scan type not changed, ext_act_scan(%d) \n", scan_ch->channel, p->scan_ch->ext_act_scan); break; } #endif FSM_INFO(pscan->fsm, "ch(%d) PASSIVE->ACTIVE!\n", scan_ch->channel); p->scan_ch->type = RTW_PHL_SCAN_ACTIVE; scan_issue_probereq(pscan); phl_fsm_set_alarm_ext(pscan->fsm_obj, SCAN_PROBE_INTERVAL, SCAN_EV_NEXT_PROBE_REQ, ALM_PROBE_REQ, NULL); break; case SCAN_EV_NOTIFY_OFF_CH_CMD: /* dequeue off_ch_cmd */ if (handle_off_ch_cmd(pscan) > 0) { if (pscan->back_op_is_required == true) phl_fsm_extend_alarm_ext(pscan->fsm_obj, pscan->param->back_op_off_ch_ext_dur_ms, ALM_BKOP_OFF_CH); else phl_fsm_extend_alarm_ext(pscan->fsm_obj, pscan->param->back_op_off_ch_ext_dur_ms, ALM_NEXT_CH); } break; case SCAN_EV_PAUSE: phl_fsm_pause_alarm(pscan->fsm_obj); phl_fsm_pause_alarm_ext(pscan->fsm_obj, ALM_NEXT_CH); phl_fsm_pause_alarm_ext(pscan->fsm_obj, ALM_PROBE_REQ); phl_fsm_pause_alarm_ext(pscan->fsm_obj, ALM_BKOP_OFF_CH); break; case SCAN_EV_RESUME: phl_fsm_resume_alarm(pscan->fsm_obj); phl_fsm_resume_alarm_ext(pscan->fsm_obj, ALM_NEXT_CH); phl_fsm_resume_alarm_ext(pscan->fsm_obj, ALM_PROBE_REQ); phl_fsm_resume_alarm_ext(pscan->fsm_obj, ALM_BKOP_OFF_CH); break; case FSM_EV_CANCEL: phl_fsm_cancel_alarm_ext(pscan->fsm_obj, ALM_NEXT_CH); phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_IDLE); break; case FSM_EV_STATE_OUT: phl_fsm_cancel_alarm(pscan->fsm_obj); phl_fsm_cancel_alarm_ext(pscan->fsm_obj, ALM_PROBE_REQ); phl_fsm_cancel_alarm_ext(pscan->fsm_obj, ALM_BKOP_OFF_CH); scan_clr_filter(pscan); off_ch_return_set_ch_bw(pscan); scan_resume_tx(pscan); scan_off_chan(pscan, false, &need_wait); if (p->scan_ch->scan_mode != BACKOP_MODE) phl_fsm_cancel_alarm_ext(pscan->fsm_obj, ALM_NEXT_CH); break; default: break; } return 0; } /* Stay in OP channel for a while * OP channel normal data TRx */ static int scan_back_op_st_hdl(void *obj, u16 event, void *param) { struct scan_obj *pscan = (struct scan_obj *)obj; u32 start_time = _os_get_cur_time_ms(); switch (event) { case FSM_EV_STATE_IN: scan_set_timer(pscan, pscan->param->scan_ch->duration, SCAN_EV_BKOP_ON_CH_EXPIRE); break; case SCAN_EV_BKOP_ON_CH_EXPIRE: /* leve back_op channel */ phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_OFF_CH); break; case SCAN_EV_NEXT_CH: phl_fsm_set_alarm_ext(pscan->fsm_obj, phl_fsm_time_pass(start_time) + SCAN_NULL_PKT_TIME, SCAN_EV_NEXT_CH, ALM_NEXT_CH, NULL); phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_OFF_CH); break; case SCAN_EV_NOTIFY_OFF_CH_CMD: phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_OFF_CH); break; case SCAN_EV_PAUSE: phl_fsm_pause_alarm(pscan->fsm_obj); break; case SCAN_EV_RESUME: phl_fsm_resume_alarm(pscan->fsm_obj); break; case FSM_EV_CANCEL: phl_fsm_state_goto(pscan->fsm_obj, SCAN_ST_IDLE); break; case FSM_EV_STATE_OUT: phl_fsm_cancel_alarm(pscan->fsm_obj); break; default: break; } return 0; } static u32 scan_get_token(struct scan_obj *pscan) { pscan->token++; if (pscan->token == ALL_TOKEN) pscan->token++; return pscan->token; } static void scan_dump_obj(void *obj, char *s, int *sz) { /* nothing to do for now */ } static void scan_dump_fsm(void *fsm, char *s, int *sz) { /* nothing to do for now */ } static void scan_dbg_help(struct scan_obj *pscan, char *p, int *sz) { int len = *sz; _os_snprintf(pstr(p), lstr(p, len), "usage:\n\t<%s> req del <0|token>\n\t<%s> req \n", phl_fsm_obj_name(pscan->fsm_obj), phl_fsm_obj_name(pscan->fsm_obj)); *sz = len; } static void scan_debug(void *obj, char input[][MAX_ARGV], u32 input_num, char *output, u32 *out_len) { struct scan_obj *pscan = (struct scan_obj *)obj; char *ptr = output; int len = *out_len; int token; if (input_num < 2) { scan_dbg_help(pscan, ptr, &len); goto done; } if (!_os_strcmp(input[0], "req")) { if (!_os_strcmp(input[1], "del")) { /* del scan request */ /* cmd: scan-1 req del */ /* read token */ if (_os_sscanf(input[2], "%d", &token) != 1) { _os_snprintf(pstr(ptr), lstr(ptr, len), "%s Err missing token\n", phl_fsm_obj_name(pscan->fsm_obj)); goto done; } _os_snprintf(pstr(ptr), lstr(ptr, len), "%s del token %d\n", phl_fsm_obj_name(pscan->fsm_obj), token); rtw_phl_scan_del_request(pscan->phl_info, token); } else if (!_os_strcmp(input[1], "pause")) { /* cmd: scan-1 req pauase */ _os_snprintf(pstr(ptr), lstr(ptr, len), "%s pause\n", phl_fsm_obj_name(pscan->fsm_obj)); rtw_phl_scan_pause(pscan->phl_info); } else if (!_os_strcmp(input[1], "resume")) { /* cmd: scan-1 req resume */ _os_snprintf(pstr(ptr), lstr(ptr, len), "%s resume\n", phl_fsm_obj_name(pscan->fsm_obj)); rtw_phl_scan_resume(pscan->phl_info); } } else scan_dbg_help(pscan, ptr, &len); done: *out_len = len; } /* For EXTERNAL application to create a scan FSM */ /* @root: FSM root structure * @phl: private data structure to invoke hal/phl function * * return * fsm_main: FSM main structure (Do NOT expose) */ struct fsm_main *phl_scan_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(scan_state_tbl)/sizeof(scan_state_tbl[0]); tb.max_event = sizeof(scan_event_tbl)/sizeof(scan_event_tbl[0]); tb.state_tbl = scan_state_tbl; tb.evt_tbl = scan_event_tbl; tb.dump_obj = scan_dump_obj; tb.dump_fsm = scan_dump_fsm; tb.debug = scan_debug; tb.dbg_level = FSM_DBG_INFO; tb.evt_level = FSM_DBG_INFO; fsm = phl_fsm_init_fsm(root, "scan", phl_info, &tb); return fsm; } /* For EXTERNAL application to destory scan fsm */ /* @fsm: see fsm_main */ void phl_scan_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 scan object */ /* @fsm: FSM main structure which created by phl_scan_new_fsm() * @phl_info: private data structure of caller * * return * scan_obj: structure of scan object (Do NOT expose) */ struct scan_obj *phl_scan_new_obj(struct fsm_main *fsm, struct phl_info_t *phl_info) { void *d = phl_to_drvpriv(phl_info); struct fsm_obj *obj; struct scan_obj *pscan; pscan = phl_fsm_new_obj(fsm, (void **)&obj, sizeof(*pscan)); if (pscan == NULL) { /* TODO free fsm; currently will be freed in deinit process */ FSM_ERR(fsm, "scan: malloc obj fail\n"); return NULL; } pscan->fsm = fsm; pscan->fsm_obj = obj; pscan->phl_info = phl_info; /* init obj local use variable */ pq_init(d, &pscan->req_q); pq_init(d, &pscan->off_ch_cmd_q); pscan->param = NULL; pscan->token_running = 0; pscan->off_ch_step = OFF_CH_STEP_CLEAR; pscan->back_op_is_required = false; pscan->back_op_ch.scan_mode = BACKOP_MODE; pscan->back_op_ch.duration = BACK_OP_CH_DUR_MS; return pscan; } /* For EXTERNAL application to destory scan object */ /* @pscan: local created scan object */ void phl_scan_destory_obj(struct scan_obj *pscan) { void *d; _os_list *obj; if (pscan == NULL) return; d = phl_to_drvpriv(pscan->phl_info); /* deinit and free all local variables */ while (pq_pop(d, &pscan->req_q, &obj, _first, _bh)) scan_free_req(pscan, (struct fsm_msg *)obj); pq_deinit(d, &pscan->req_q); while (pq_pop(d, &pscan->off_ch_cmd_q, &obj, _first, _bh)) scan_free_msg(pscan, (struct fsm_msg *)obj); pq_deinit(d, &pscan->off_ch_cmd_q); /* clear scan param */ if (pscan->param) { if (pscan->param && pscan->param->ch && pscan->param->ch_sz) _os_kmem_free(d, pscan->param->ch, pscan->param->ch_sz); _os_kmem_free(d, pscan->param, sizeof(*pscan->param)); pscan->param = NULL; } /* inform FSM framewory to recycle fsm_obj */ phl_fsm_destory_obj(pscan->fsm_obj); } /* For EXTERNAL application to request scan (expose) */ /* @pscan: scan object * @pbuf: scan parameter, will be freed by caller after retrun * @order: queuing order */ enum rtw_phl_status rtw_phl_scan_request(void *phl, struct rtw_phl_scan_param *pbuf, enum PRECEDE order) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; void *d = phl_to_drvpriv(pscan->phl_info); struct rtw_phl_scan_param *param = NULL; struct phl_scan_channel *pch = NULL; struct fsm_msg *msg; pch = (struct phl_scan_channel *)_os_kmem_alloc(d, pbuf->ch_sz); if (pch == NULL) goto pch_fail; param = (struct rtw_phl_scan_param *)_os_kmem_alloc(d, sizeof(*pbuf)); if (param == NULL) goto param_fail; /* NEW message to start scan */ msg = phl_fsm_new_msg(pscan->fsm_obj, SCAN_EV_START); if (msg == NULL) goto msg_fail; /* fill token */ pbuf->token = scan_get_token(pscan); _os_mem_cpy(d, pch, pbuf->ch, pbuf->ch_sz); _os_mem_cpy(d, param, pbuf, sizeof(*param)); param->ch = pch; param->ch_idx = -1; param->scan_ch = ¶m->ch[0]; param->result = SCAN_REQ_ABORT; param->enqueue_time = _os_get_cur_time_ms(); msg->param = param; msg->param_sz = sizeof(*param); /* * Enqueue scan_request into extra queue (pending scan) */ switch (order) { case IMMEDIATE: pq_push(d, &pscan->req_q, (_os_list *)msg, _first, _bh); /* Cancel running scan process */ phl_fsm_gen_msg(phl, pscan->fsm_obj, NULL, 0, FSM_EV_CANCEL); /* fall through */ break; case TO_HEAD: pq_push(d, &pscan->req_q, (_os_list *)msg, _first, _bh); break; case TO_TAIL: default: pq_push(d, &pscan->req_q, (_os_list *)msg, _tail, _bh); break; } /* notify scan-obj to dequeue from extra queue */ return phl_fsm_gen_msg(phl, pscan->fsm_obj, NULL, 0, SCAN_EV_NOTIFY_PENDING_SCAN_REQ); msg_fail: _os_kmem_free(d, param, sizeof(*param)); param_fail: _os_kmem_free(d, pch, sizeof(*pch)); pch_fail: FSM_ERR(pscan->fsm, "%s: %s() malloc fail\n", phl_fsm_obj_name(pscan->fsm_obj), __func__); return RTW_PHL_STATUS_RESOURCE; } /* For EXTERNAL application to del scan request (expose) */ /* @phl: phl_info_t * @token: token to be deleted * 0 means del all requests and cancel running scan */ enum rtw_phl_status rtw_phl_scan_del_request(void *phl, u32 token) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; void *d = phl_to_drvpriv(phl_info); struct fsm_msg *msg, *msg_t; struct rtw_phl_scan_param *param = NULL; _os_list *obj; if (token == ALL_TOKEN) { while (pq_pop(d, &pscan->req_q, &obj, _first, _bh)) { msg = (struct fsm_msg *)obj; param = msg->param; FSM_INFO(pscan->fsm, "%s abort token %d\n", phl_fsm_obj_name(pscan->fsm_obj), param->token); if (param && param->ops && param->ops->scan_complete) param->ops->scan_complete(param->priv, param); scan_free_req(pscan, (struct fsm_msg *)obj); } } else { /* search token in scan request queue */ _os_spinlock(d, &pscan->req_q.lock, _bh, NULL); phl_list_for_loop_safe(msg, msg_t, struct fsm_msg, &pscan->req_q.queue, list) { param = (struct rtw_phl_scan_param *)msg->param; if (param && param->token == token) { list_del(&msg->list); pscan->req_q.cnt--; _os_spinunlock(d, &pscan->req_q.lock, _bh, NULL); FSM_INFO(pscan->fsm, "%s abort token %d\n", phl_fsm_obj_name(pscan->fsm_obj), token); /* callback to caller */ if (param->ops && param->ops->scan_complete) param->ops->scan_complete(param->priv, param); scan_free_req(pscan, msg); return RTW_PHL_STATUS_SUCCESS; } } _os_spinunlock(d, &pscan->req_q.lock, _bh, NULL); FSM_INFO(pscan->fsm, "%s del token %d not found\n", phl_fsm_obj_name(pscan->fsm_obj), token); } if ((pscan->param) && ((pscan->param->token == token) || ALL_TOKEN == token)) { /* we have a running scan, cancel it */ FSM_INFO(pscan->fsm, "%s cancel running scan token %d\n", phl_fsm_obj_name(pscan->fsm_obj), pscan->param->token); phl_fsm_cancel_obj(pscan->fsm_obj); } return RTW_PHL_STATUS_SUCCESS; } /* For EXTERNAL application to cancel scan (expose) */ /* @pscan: scan object will be cancelled */ enum rtw_phl_status rtw_phl_scan_cancel(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; return phl_fsm_cancel_obj(pscan->fsm_obj); } /* For EXTERNAL application to pause scan (expose) * @phl: phl pirvate */ enum rtw_phl_status rtw_phl_scan_pause(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; return phl_fsm_gen_msg(phl, pscan->fsm_obj, NULL, 0, SCAN_EV_PAUSE); } /* For EXTERNAL application to resume scan (expose) * @phl: phl pirvate */ enum rtw_phl_status rtw_phl_scan_resume(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; return phl_fsm_gen_msg(phl, pscan->fsm_obj, NULL, 0, SCAN_EV_RESUME); } /* For EXTERNAL application to change passive scan to active scan (expose) * @phl: phl pirvate * @ch: channel to be changed to active scan * @duration: scan time for the channel; 0: half of original duration * * Usually triggered when receiving Beacon in a passive scan channel. * Changing current scan ch from passive to active scan. * (issue probe request in current scan channel) * More easy to find a hidden ssid AP in passive scan channel. */ enum rtw_phl_status rtw_phl_scan_force_active_scan(void *phl, u16 channel, u16 duration) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; struct phl_scan_channel ch; _os_mem_set(phl_to_drvpriv(phl_info), &ch, 0, sizeof(ch)); ch.channel = channel; ch.duration = duration; return phl_fsm_gen_msg(phl, pscan->fsm_obj, &ch, sizeof(ch), SCAN_EV_FORCE_ACTIVE); } /* For EXTERNAL application to get inprogress scan and requests num (expose) * @phl: phl pirvate * * return: 0: no scan is inprogressing * otherwise: number of queuing scan requests + inprogress scan */ int rtw_phl_scan_inprogress_req_num(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; u8 running = 0; if (pscan->param != NULL) running = 1; return pscan->req_q.cnt + running; } bool rtw_phl_scan_scanning_req(void *phl, u32 token, u32 *token_running) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; *token_running = pscan->token_running; return (token == *token_running); } enum rtw_phl_status rtw_phl_scan_off_ch_tx(void *phl, void *data, int len) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct scan_obj *pscan = phl_info->scan_obj; struct rtw_phl_scan_param *param = pscan->param; void *d = phl_to_drvpriv(pscan->phl_info); struct fsm_msg *msg; void *buf; if (!param || !param->ops || !param->ops->scan_off_ch_tx) return RTW_PHL_STATUS_FAILURE; buf = _os_kmem_alloc(d, len); if (buf == NULL) goto buf_fail; msg = phl_fsm_new_msg(pscan->fsm_obj, SCAN_EV_NOTIFY_OFF_CH_CMD); if (msg == NULL) goto msg_fail; _os_mem_cpy(d, buf, data, len); msg->param = buf; msg->param_sz = len; pq_push(d, &pscan->off_ch_cmd_q, (_os_list *)msg, _tail, _bh); FSM_INFO(pscan->fsm, "%s %s(), q_cnt = %d\n", phl_fsm_obj_name(pscan->fsm_obj), __func__, pscan->off_ch_cmd_q.cnt); /* notify scan-obj there is an off_ch_cmd */ return phl_fsm_gen_msg(phl, pscan->fsm_obj, NULL, 0, SCAN_EV_NOTIFY_OFF_CH_CMD); msg_fail: _os_kmem_free(d, buf, len); buf_fail: FSM_ERR(pscan->fsm, "%s %s() malloc fail\n", phl_fsm_obj_name(pscan->fsm_obj), __func__); return RTW_PHL_STATUS_RESOURCE; } #endif /*CONFIG_FSM*/