/****************************************************************************** * * Copyright(c) 2022 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. * *****************************************************************************/ #include "phl_headers.h" #include "phl_scan.h" #ifdef CONFIG_PHL_SCANOFLD static bool _chk_op_required(struct rtw_phl_scan_param *param, struct cmd_scan_ctrl *sctrl, u8 cur_idx, u8 cur_ch_period, u32 *cur_time) { bool required = false; required = (sctrl->back_op_ch[0].channel)? true : false; if (!required) return false; if (param->back_op.mode == SCAN_BKOP_CNT) { if (cur_idx && (cur_idx % param->back_op.ch_intv) == 0) return true; } if (param->back_op.mode == SCAN_BKOP_TIMER) { if (*cur_time > param->back_op.ch_intv) { *cur_time = 0; return true; } *cur_time += cur_ch_period; } return false; } static void _add_opchnl(struct phl_info_t *phl_info, struct rtw_phl_scan_param *param, struct cmd_scan_ctrl *sctrl, u8 hw_band) { struct scan_ofld_ch_info ch_info = {0}; struct rtw_chan_def chan_def = {0}; enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS; u8 idx = 0; for(idx = 0; idx < MAX_WIFI_ROLE_NUMBER; idx ++) { if (0 == sctrl->back_op_ch[idx].channel) break; ch_info.chan = (u8)sctrl->back_op_ch[idx].channel; ch_info.band = sctrl->back_op_ch[idx].band; ch_info.bw = sctrl->back_op_ch[idx].bw; chan_def.offset = sctrl->back_op_ch[idx].offset; chan_def.chan = ch_info.chan; chan_def.band = ch_info.band; chan_def.bw = ch_info.bw; ch_info.center_chan = rtw_phl_get_center_ch(&chan_def); ch_info.period = (u8)sctrl->back_op_ch[idx].duration; ch_info.tx_null = true; ch_info.enter_notify = true; ch_info.chkpt_time = param->chkpt_time; ch_info.tx_data_pause = false; hstatus = rtw_hal_scan_ofld_add_ch(phl_info->hal, hw_band, &ch_info, false); if (hstatus != RTW_HAL_STATUS_SUCCESS) PHL_INFO("%s failed \n", __func__); } } static enum rtw_phl_status _scanofld_add_chnl(struct phl_info_t *phl_info, struct rtw_wifi_role_link_t *rlink, struct rtw_phl_scan_param *param) { u8 ch_num = 0, i = 0; struct phl_scan_channel *ch = NULL; struct cmd_scan_ctrl *sctrl = NULL; enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS; void *d = phl_to_drvpriv(phl_info); u8 sctrl_idx = 0; struct scan_ofld_ch_info ch_info = {0}; _os_list *node = NULL; struct rtw_chan_def chan_def = {0}; u32 time = 0; sctrl_idx = phl_cmd_scan_ctrl(param, rlink->hw_band, &sctrl); if(sctrl == NULL) { PHL_ERR("%s: find sctrl failed\n", __func__); return RTW_PHL_STATUS_FAILURE; } ch_num = (u8)sctrl->chlist.cnt; if (pq_get_front(d, &sctrl->chlist, &node, _ps) == false) return RTW_PHL_STATUS_SUCCESS; do { ch = (struct phl_scan_channel*)node; _os_mem_set(d, &ch_info, 0, sizeof(struct scan_ofld_ch_info)); _os_mem_set(d, &chan_def, 0, sizeof(struct rtw_chan_def)); ch_info.band = chan_def.band = ch->band; ch_info.bw = chan_def.bw = ch->bw; chan_def.offset = ch->offset; chan_def.chan = (u8)ch->channel; ch_info.center_chan = rtw_phl_get_center_ch(&chan_def); ch_info.chan = (u8)ch->channel; ch_info.period = (u8)ch->duration; /* extend first, if not transforming to active, this channel will also be cancelled by probing */ if (ch->type == RTW_PHL_SCAN_PASSIVE && param->ops->scan_ch_cancel) { ch_info.period += (u8)param->ext_act_scan_period; } ch_info.tx_null = ch->scan_mode == BACKOP_MODE ? true : false; ch_info.enter_notify = true; ch_info.chkpt_time = param->chkpt_time; if (rlink->mstate == MLME_LINKED && rlink->chandef.chan == ch->channel) ch_info.tx_data_pause = false; else ch_info.tx_data_pause = true; /* driver process probe request */ ch_info.tx_pkt = false; /* add op channel before adding scanned channel */ if (_chk_op_required(param, sctrl, i, ch_info.period, &time)) _add_opchnl(phl_info, param, sctrl, rlink->hw_band); hstatus = rtw_hal_scan_ofld_add_ch(phl_info->hal, rlink->hw_band, &ch_info, i == (ch_num - 1) ? true : false); if (hstatus != RTW_HAL_STATUS_SUCCESS) { PHL_INFO("%s failed \n", __func__); return RTW_PHL_STATUS_FAILURE; } i++; if (!pq_get_next(d, &sctrl->chlist, node, &node, _ps)) break; } while (1); PHL_INFO("%s succeeded \n", __func__); return RTW_PHL_STATUS_SUCCESS; } static enum rtw_phl_status _scanofld_start(struct phl_info_t *phl_info, struct rtw_wifi_role_link_t *rlink, u16 mac_id, struct rtw_phl_scan_param *param) { struct scan_ofld_info cfg = {0}; enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS; enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS; struct rtw_chan_def chdef = {0}; /* add channel */ pstatus = _scanofld_add_chnl(phl_info, rlink, param); if (RTW_PHL_STATUS_SUCCESS != pstatus) return pstatus; rtw_hal_notification(phl_info->hal, MSG_EVT_SCANOFLD_START, rlink->hw_band); rtw_hal_en_fw_log_comp(phl_info->hal, FL_COMP_SCAN, true); /* trigger fw to start scan */ cfg.operation = SCAN_OFLD_OP_START; cfg.mode = SCAN_OFLD_MD_ONCE; cfg.end_notify = true; cfg.tgt_mode = true; rtw_hal_get_cur_chdef(phl_info->hal, rlink->hw_band, &chdef); _os_mem_cpy(phl_to_drvpriv(phl_info), &cfg.tgt_chandef, &chdef, sizeof(struct rtw_chan_def)); if (cfg.tgt_chandef.chan == 0) cfg.tgt_mode = false; hstatus = rtw_hal_scan_ofld(phl_info->hal, mac_id, rlink->hw_band, rlink->hw_port, &cfg); return hstatus == RTW_HAL_STATUS_SUCCESS ? RTW_PHL_STATUS_SUCCESS : RTW_PHL_STATUS_FAILURE; } static void _scanofld_stop(struct phl_info_t *phl_info, struct rtw_wifi_role_link_t *rlink, struct rtw_phl_scan_param *param) { struct scan_ofld_info cfg = {0}; struct rtw_phl_stainfo_t *sta = rtw_phl_get_stainfo_self(phl_info, rlink); cfg.operation = SCAN_OFLD_OP_STOP; rtw_hal_scan_ofld(phl_info->hal, sta->macid, rlink->hw_band, rlink->hw_port, &cfg); rtw_hal_notification(phl_info->hal, MSG_EVT_SCANOFLD_END, rlink->hw_band); rtw_hal_en_fw_log_comp(phl_info->hal, FL_COMP_SCAN, false); } static void _cmd_scan_req_deinit(struct phl_info_t *phl_info, struct rtw_phl_scan_param *param) { void *drv = phl_to_drvpriv(phl_info); struct cmd_scan_ctrl *sctrl = NULL; u8 sctrl_idx = 0; for(sctrl_idx = 0; sctrl_idx < param->sctrl_num; sctrl_idx++) { sctrl = ¶m->sctrl[sctrl_idx]; pq_deinit(drv, &sctrl->chlist); sctrl->scan_ch = NULL; } } void phl_cmd_scanofld_end(void *drv, void *_param, u8 band_idx) { struct rtw_phl_scan_param *param = (struct rtw_phl_scan_param *)_param; struct rtw_phl_com_t *phl_com = param->wifi_role->phl_com; struct phl_info_t *phl_info = phl_com->phl_priv; void *d = phlcom_to_drvpriv(phl_com); struct cmd_scan_ctrl *sctrl = NULL; bool indicate = true; u8 sctrl_idx = 0xff; PHL_INFO("%s \n", __func__); if((param->sctrl_num > 1) && !TEST_SCAN_FLAGS(param->state, CMD_SCAN_END)) { indicate = false; } SET_SCAN_FLAG(param->state, band_idx, CMD_SCAN_END); param->end_time = _os_get_cur_time_ms(); /* dump scan time */ param->total_scan_time = phl_get_passing_time_ms(param->enqueue_time); sctrl_idx = phl_cmd_scan_ctrl(param, band_idx, &sctrl); if(sctrl == NULL) { PHL_ERR("%s: find sctrl failed\n", __func__); goto error; } sctrl->scan_ch = NULL; _os_release_timer(d, &sctrl->scan_timer); if(TEST_SCAN_FLAG(param->state, band_idx, CMD_SCAN_STARTED) && !TEST_SCAN_FLAG(param->state, band_idx, CMD_SCAN_DF_IO) ) { _scanofld_stop(phl_info, &sctrl->wrole->rlink[RTW_RLINK_PRIMARY], param); rtw_hal_com_scan_restore_tx_lifetime(phl_info->hal, band_idx); rtw_hal_scan_set_rxfltr_by_mode(phl_info->hal, band_idx, false, &sctrl->fltr_mode); rtw_hal_notification(phl_info->hal, MSG_EVT_SCAN_END, band_idx); } error: if (indicate) { rtw_hal_scan_ofld_cfg_en(phl_info->hal, false); _cmd_scan_req_deinit(phl_info, param); param->sctrl_num = 0; } PHL_INFO("[cmd_scan][%d]:: sctrl_idx[%d] param->state(0x%X), result(0x%X)\n", band_idx, sctrl_idx, param->state, param->result); if (indicate && param->ops->scan_complete) param->ops->scan_complete(param->priv, param); } #define DEF_PROBE_PERIOD 20 /* * Return value : * false : cancel cur channel. * true : keep scan on cur channel. */ static bool _scanofld_handle_probing(void *d, struct rtw_phl_scan_param *param, u8 sctrl_idx) { struct cmd_scan_ctrl *sctrl = ¶m->sctrl[sctrl_idx]; struct phl_scan_channel *scan_ch = sctrl->scan_ch; u32 dur = 0, diff_t = 0; bool check_cancel = true; /* no probe handler */ if (!param->ops->scan_probe) return false; if (scan_ch == NULL) return false; scan_ch->pass_t = phl_get_passing_time_ms(scan_ch->start_t); if (scan_ch->pass_t >= scan_ch->last_t) diff_t = scan_ch->pass_t - scan_ch->last_t; PHL_INFO("[probing], band %d, ch %d, pass_t %d, last_t %d, diff_t %d, duration %d\n", scan_ch->band, scan_ch->channel, scan_ch->pass_t, scan_ch->last_t, diff_t, scan_ch->duration); /* DFS, tx probe and extend scan period */ if (phl_cmd_chk_ext_act_scan(param, sctrl_idx)) { scan_ch->duration += (u16)param->ext_act_scan_period; check_cancel = false; PHL_INFO("[probing], DFS extend period to %d !\n", scan_ch->duration); } dur = scan_ch->duration; if (scan_ch->pass_t >= dur) { PHL_INFO("[probing], expired, channel end !!\n"); return false; } scan_ch->remain_t = (u32)(dur - scan_ch->pass_t); PHL_INFO("[probing], remain_t %d \n", scan_ch->remain_t); if (diff_t >= DEF_PROBE_PERIOD) { scan_ch->last_t = scan_ch->pass_t; if (param->ops->scan_ch_cancel && check_cancel) { if (param->ops->scan_ch_cancel(param->priv, param, sctrl_idx)) { PHL_INFO("[probing], channel canceled !\n"); return false; } } /* core probe handler */ param->ops->scan_probe(param->priv, param, sctrl_idx); } return true; } static void _cmd_scanofld_start(struct phl_info_t *phl_info, struct cmd_scan_ctrl *sctrl, u8 band_idx) { rtw_hal_scan_set_rxfltr_by_mode(phl_info->hal, band_idx, true, &sctrl->fltr_mode); rtw_hal_com_scan_set_tx_lifetime(phl_info->hal, band_idx); rtw_hal_scan_ofld_cfg_en(phl_info->hal, true); rtw_hal_notification(phl_info->hal, MSG_EVT_SCAN_START, band_idx); } static void _scanofld_get_scan_ch_info(struct phl_info_t *phl_info, struct rtw_phl_scan_param *param, enum band_type band, u8 chnl, struct phl_scan_channel *scan_ch) { u8 i = 0; for (i = 0; i < param->ch_num; i++) { if (param->ch[i].band == band && param->ch[i].channel == chnl) { scan_ch = ¶m->ch[i]; return; } } scan_ch = NULL; } enum phl_mdl_ret_code phl_cmd_scanofld_hdl_internal_evt(void* dispr, void* priv, void* _msg) { struct phl_msg *msg = (struct phl_msg *)_msg; struct rtw_phl_scan_param *param = (struct rtw_phl_scan_param*)priv; struct rtw_wifi_role_t *wifi_role = param->wifi_role; struct rtw_phl_com_t *phl_com = wifi_role->phl_com; struct phl_info_t *phl_info = phl_com->phl_priv; void *d = phlcom_to_drvpriv(phl_com); u32 diff_time = 0; struct phl_msg nextmsg = {0}; struct phl_msg_attribute attr = {0}; enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS; struct rtw_phl_stainfo_t *sta = NULL; struct rtw_scanofld_rsp *rsp = NULL; struct phl_scan_channel *scan_ch = NULL; u8 band_idx = 0, sctrl_idx = 0xff; struct cmd_scan_ctrl *sctrl = NULL; u8 i = 0; diff_time = phl_get_passing_time_ms(param->enqueue_time); if (param->max_scan_time && diff_time >= param->max_scan_time) { PHL_WARN("%s:: Timeout! %d > max_time %d\n", __func__, diff_time, param->max_scan_time); } else { PHL_INFO("%s:: TimeIntvl: %u \n", __func__, diff_time); } phl_dispr_get_idx(dispr, &band_idx); sctrl_idx = phl_cmd_scan_ctrl(param, band_idx, &sctrl); if(sctrl == NULL) { PHL_ERR("%s: find sctrl failed\n", __func__); return MDL_RET_FAIL; } sta = rtw_phl_get_stainfo_self(phl_info, &sctrl->wrole->rlink[RTW_RLINK_PRIMARY]); SET_MSG_MDL_ID_FIELD(nextmsg.msg_id, PHL_FG_MDL_SCAN); nextmsg.band_idx = band_idx; PHL_INFO("%s phy_idx %u \n", __func__, band_idx); switch(MSG_EVT_ID_FIELD(msg->msg_id)) { case MSG_EVT_SCAN_START: _cmd_scanofld_start(phl_info, sctrl, band_idx); /* [scan start notify] */ if (!TEST_SCAN_FLAGS(param->state, CMD_SCAN_STARTED)) { if (param->ops->scan_start) param->ops->scan_start(param->priv, param); } SET_SCAN_FLAG(param->state, band_idx, CMD_SCAN_STARTED); pstatus = _scanofld_start(phl_info, &sctrl->wrole->rlink[RTW_RLINK_PRIMARY], sta->macid, param); if (pstatus != RTW_PHL_STATUS_SUCCESS) { SET_MSG_EVT_ID_FIELD(nextmsg.msg_id, MSG_EVT_SCAN_END); nextmsg.rsvd[0].ptr = (u8*)sctrl->wrole; pstatus = phl_disp_eng_send_msg(phl_info, &nextmsg, &attr, NULL); if (pstatus != RTW_PHL_STATUS_SUCCESS) return MDL_RET_FAIL; } break; case MSG_EVT_SCANOFLD_SWITCH: rsp = (struct rtw_scanofld_rsp *)msg->inbuf; scan_ch = NULL; for (i = 0; i < param->ch_num; i++) { if (param->ch[i].band == rsp->band && param->ch[i].channel == rsp->pri_ch) { scan_ch = ¶m->ch[i]; break; } } sctrl->scan_ch = scan_ch; if (sctrl->scan_ch != NULL) { if (!BAND_6GHZ(scan_ch->band)) { if ((scan_ch->scan_mode != BACKOP_MODE) && (scan_ch->type == RTW_PHL_SCAN_ACTIVE)) { if (param->ops->scan_issue_pbreq) param->ops->scan_issue_pbreq(param->priv, param, sctrl_idx); } } if (param->ops->scan_probe) { scan_ch->start_t = _os_get_cur_time_ms(); scan_ch->last_t = 0; scan_ch->pass_t = 0; scan_ch->remain_t = scan_ch->duration; } if (param->ops->scan_ch_ready) param->ops->scan_ch_ready(param->priv, param, sctrl_idx); } break; case MSG_EVT_SCANOFLD_CHKPT_TIMER: rsp = (struct rtw_scanofld_rsp *)msg->inbuf; if (!_scanofld_handle_probing(d, param, band_idx)) { PHL_INFO("%s leave cur chnl \n", __func__); rtw_hal_scan_ofld_leave_cur_chnl(phl_info->hal, band_idx, rsp->band, rsp->pri_ch); } break; case MSG_EVT_SCANOFLD_END: PHL_INFO("%s: MSG_EVT_SCANOFLD_END \n", __func__); /* scan finished */ SET_MSG_EVT_ID_FIELD(nextmsg.msg_id, MSG_EVT_SCAN_END); nextmsg.rsvd[0].ptr = (u8*)sctrl->wrole; pstatus = phl_disp_eng_send_msg(phl_info, &nextmsg, &attr, NULL); if (pstatus != RTW_PHL_STATUS_SUCCESS) PHL_ERR("%s :: [MSG_EVT_SCANOFLD_END] dispr_send_msg failed\n", __func__); break; case MSG_EVT_SCAN_END: PHL_INFO("%s: MSG_EVT_SCAN_END \n", __func__); pstatus = phl_disp_eng_free_token(phl_info, band_idx, &sctrl->token); if(pstatus == RTW_PHL_STATUS_SUCCESS) { param->result = SCAN_REQ_COMPLETE; phl_cmd_scanofld_end(d, param, band_idx); } else { PHL_WARN("%s :: [SCAN_END] Abort occurred, skip!\n", __func__); } break; default: /* unknown state */ break; } return MDL_RET_SUCCESS; } u8 rtw_phl_scanofld_support(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; return phl_info->phl_com->dev_cap.scan_ofld; } #endif