/****************************************************************************** * * 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 _HAL_BTC_C_ #include "../hal_headers_le.h" #include "hal_btc.h" #include "halbtc_fw.h" #include "halbtc_def.h" #include "halbtc_action.h" #ifdef CONFIG_BTCOEX /* [31:24] -> main-version * [23:16] -> sub-version * [15:8] -> Hot-Fix-version * [7:0] -> branch ID, ex: 0x00-> Main-Branch * Modify bt_8852x.c chip_8852x member: btcx_desired, wlcx_desired if required * btcx_desired: BT FW coex version -> main-version + 1 if update. * wlcx_desired: WL FW coex version -> sub-version + 1 if update */ const u32 coex_ver = 0x0601040f; static struct btc_ops _btc_ops = { _send_fw_cmd, _ntfy_power_on, _ntfy_power_off, _ntfy_init_coex, _ntfy_scan_start, _ntfy_scan_finish, _ntfy_switch_band, _ntfy_specific_packet, _ntfy_role_info, _ntfy_radio_state, _ntfy_customerize, _ntfy_wl_rfk, _ntfy_wl_sta, _ntfy_fwinfo, _ntfy_timer }; #define _update_dbcc_band(phy_idx) \ btc->cx.wl.dbcc_info.real_band[phy_idx] =\ (btc->cx.wl.scan_info.phy_map & BIT(phy_idx) ?\ btc->cx.wl.dbcc_info.scan_band[phy_idx] :\ btc->cx.wl.dbcc_info.op_band[phy_idx]) /****************************************************************************** * * coex internal functions * *****************************************************************************/ static void _set_btc_timer(struct btc_t *btc, u16 tmr_id, u32 ms) { struct btc_tmr *btmr = NULL; if (tmr_id < BTC_TIMER_MAX) { btmr = &btc->timer[tmr_id]; _os_set_timer(halcom_to_drvpriv(btc->hal), &btmr->tmr, ms); } } static void _btmr_stop(struct btc_t *btc) { struct btc_tmr *btmr = NULL; u8 i = 0; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(): stop btc timers!!\n", __func__); btc->tmr_stop = true; for (i = 0; i < BTC_TIMER_MAX; i++) { btmr = &btc->timer[i]; _os_cancel_timer(halcom_to_drvpriv(btc->hal), &btmr->tmr); } _os_cancel_timer(halcom_to_drvpriv(btc->hal), &btc->delay_tmr); } static void _btmr_release(struct btc_t *btc) { struct btc_tmr *btmr = NULL; u8 i = 0; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(): release btc timers!!\n", __func__); for (i = 0; i < BTC_TIMER_MAX; i++) { btmr = &btc->timer[i]; _os_release_timer(halcom_to_drvpriv(btc->hal), &btmr->tmr); } _os_release_timer(halcom_to_drvpriv(btc->hal), &btc->delay_tmr); } static void _btmr_start(struct btc_t *btc) { PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(): start btc periodic timer!!\n", __func__); btc->tmr_stop = false; /* Wait 2 sec for phl starting then periodic timer will be started */ _os_set_timer(halcom_to_drvpriv(btc->hal), &btc->delay_tmr, BTC_DELAYED_PERIODIC_TIME); } static void _delay_tmr_cb(void *ctx) { struct btc_t *btc = NULL; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s() !!\n", __func__); if (!ctx) return; btc = (struct btc_t *)ctx; _set_btc_timer(btc, BTC_TIMER_PERIODIC, BTC_PERIODIC_TIME); } static void _btmr_cb(void *ctx) { struct btc_tmr *btmr = NULL; struct btc_t *btc = NULL; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(), ctx = 0x%p !!\n", __func__, ctx); if (!ctx) return; btmr = (struct btc_tmr *)ctx; btc = (struct btc_t *)btmr->btc; if (!btc->tmr_init || btc->tmr_stop) return; hal_btc_send_event(btc, (u8 *)btmr, sizeof(struct btc_tmr *), BTC_HMSG_TMR_EN); } static void _btmr_init(struct btc_t *btc) { struct btc_tmr *btmr = NULL; u8 i = 0; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(): !!\n", __func__); for (i = 0; i < BTC_TIMER_MAX; i++) { btmr = &btc->timer[i]; btmr->btc = btc; btmr->id = i; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], init btc timer(%d) = 0x%p !!\n", i, btmr); _os_init_timer(halcom_to_drvpriv(btc->hal), &btmr->tmr, _btmr_cb, btmr, "btc_tmr"); } _os_init_timer(halcom_to_drvpriv(btc->hal), &btc->delay_tmr, _delay_tmr_cb, btc, NULL); btc->tmr_init = true; btc->tmr_stop = true; } static void _btmr_deinit(struct btc_t *btc) { if (btc->tmr_init) { _btmr_stop(btc); _btmr_release(btc); btc->tmr_init = false; } } static void _send_fw_cmd(struct btc_t *btc, u8 h2c_class, u8 h2c_func, u8 *param, u16 len) { struct rtw_hal_com_t *h = btc->hal; struct btc_wl_info *wl = &btc->cx.wl; struct rtw_g6_h2c_hdr hdr = {0}; hdr.h2c_class = h2c_class; hdr.h2c_func = h2c_func; hdr.type = H2CB_TYPE_DATA; hdr.content_len = len; hdr.done_ack = 1; if (!wl->status.map.init_ok) { PHL_TRACE(COMP_PHL_BTC, _PHL_ERR_, "[BTC], %s(): return by btc not init!!\n", __func__); btc->fwinfo.cnt_h2c_fail++; return; } else if ((wl->status.map.rf_off_pre == 1 && wl->status.map.rf_off == 1) || (wl->status.map.lps_pre == 1 && wl->status.map.lps == 1)) { PHL_TRACE(COMP_PHL_BTC, _PHL_ERR_, "[BTC], %s(): return by wl off!!\n", __func__); btc->fwinfo.cnt_h2c_fail++; return; } btc->fwinfo.cnt_h2c++; if (rtw_hal_mac_send_h2c(h, &hdr, (u32 *)param) != 0) btc->fwinfo.cnt_h2c_fail++; } u32 _read_cx_reg(struct btc_t *btc, u32 offset) { u32 val = 0; rtw_hal_mac_coex_reg_read(btc->hal, offset, &val); return val; } u8 _read_cx_ctrl(struct btc_t *btc) { u32 val = 0; rtw_hal_mac_get_coex_ctrl(btc->hal, &val); return ((u8)val); } u32 _read_scbd(struct btc_t *btc) { const struct btc_chip *chip = btc->chip; u32 scbd_val = 0; if (!chip->scbd) return 0; rtw_hal_mac_get_scoreboard(btc->hal, &scbd_val); PHL_INFO("[BTC], read scbd : 0x%08x \n", scbd_val); btc->cx.cnt_bt[BTC_BCNT_SCBDREAD]++; return (scbd_val); } void _write_scbd(struct btc_t *btc, u32 val, bool state) { const struct btc_chip *chip = btc->chip; struct btc_wl_info *wl = &btc->cx.wl; u32 scbd_val = 0; if (!chip->scbd) return; /* only use bit23~0 */ scbd_val = (state ? (wl->scbd | val) : (wl->scbd & (~val))); if (scbd_val != wl->scbd) { rtw_hal_mac_set_scoreboard(btc->hal, &scbd_val); PHL_INFO("[BTC], write scbd : 0x%08x \n", scbd_val); wl->scbd = scbd_val; btc->cx.cnt_wl[BTC_WCNT_SCBDUPDATE]++; /* Add delay to avoid BT FW loss information */ _os_delay_us(btc->hal, BTC_SCBD_REWRITE_DELAY); } } static u8 _update_rssi_state(struct btc_t *btc, u8 pre_state, u8 rssi, u8 thresh) { u8 next_state, tol = btc->chip->rssi_tol; if (pre_state == BTC_RSSI_ST_LOW || pre_state == BTC_RSSI_ST_STAY_LOW) { if (rssi >= (thresh + tol)) next_state = BTC_RSSI_ST_HIGH; else next_state = BTC_RSSI_ST_STAY_LOW; } else { if (rssi < thresh) next_state = BTC_RSSI_ST_LOW; else next_state = BTC_RSSI_ST_STAY_HIGH; } return next_state; } void _write_bt_reg(struct btc_t *btc, u8 reg_type, u16 addr, u32 val) { u8 buf[4] = {0}; /* set write address */ buf[0] = reg_type; buf[1] = addr & bMASKB0; buf[2] = (addr & bMASKB1) >> 8; hal_btc_fw_set_bt(btc, SET_BT_WREG_ADDR, 3, buf); /* set write value */ buf[0] = val & bMASKB0; buf[1] = (val & bMASKB1) >> 8; buf[2] = (val & bMASKB2) >> 16; buf[3] = (val & bMASKB3) >> 23; hal_btc_fw_set_bt(btc, SET_BT_WREG_VAL, 4, buf); } void _read_bt_reg(struct btc_t *btc, u8 reg_type, u16 addr) { /* this function is only for API call. * If BTC should use hal_btc_fw_set_monreg to read bt reg. */ u8 buf[3] = {0}; /* set write address */ buf[0] = reg_type; buf[1] = addr & bMASKB0; buf[2] = (addr & bMASKB1) >> 8; hal_btc_fw_set_bt(btc, SET_BT_RREG_ADDR, sizeof(buf), buf); /* To do wait FW event -> BTF_EVNT_BT_REG*/ } void _set_bt_psd_report(struct btc_t *btc, u8 start_idx, u8 rpt_type) { u8 buf[2] = {0}; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(): set bt psd\n", __func__); buf[0] = start_idx; buf[1] = rpt_type; hal_btc_fw_set_bt(btc, SET_BT_PSD_REPORT, 2, buf); } static void _set_bt_info_report(struct btc_t *btc, u8 trigger) { u8 buf = 0; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(): query bt info\n", __func__); buf = trigger; hal_btc_fw_set_bt(btc, SET_BT_INFO_REPORT, 1, &buf); } static void _reset_btc_var(struct btc_t *btc, u8 type) { struct rtw_hal_com_t *h = btc->hal; struct btc_cx *cx = &btc->cx; struct btc_wl_info *wl = &cx->wl; struct btc_bt_info *bt = &cx->bt; struct btc_bt_link_info *bt_linfo = &bt->link_info; struct btc_wl_link_info *wl_linfo = wl->link_info; u8 i; PHL_INFO("[BTC], %s()\n", __func__); /* Reset Coex variable */ if (type & BTC_RESET_CX) hal_mem_set(h, cx, 0, sizeof(struct btc_cx)); else if (type & BTC_RESET_BTINFO) /* only for BT enable */ hal_mem_set(h, bt, 0, sizeof(struct btc_bt_info)); if (type & BTC_RESET_CTRL) hal_mem_set(h, &btc->ctrl, 0, sizeof(struct btc_ctrl)); /* Init Coex variables that are not zero */ if (type & BTC_RESET_DM) { hal_mem_set(h, &btc->dm, 0, sizeof(struct btc_dm)); hal_mem_set(h, bt_linfo->rssi_state, 0, BTC_BT_RSSI_THMAX); for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) hal_mem_set(h, wl_linfo[i].rssi_state, 0, BTC_WL_RSSI_THMAX); /* set the slot_now table to original */ _tdma_cpy(&btc->dm.tdma_now, &t_def[CXTD_OFF]); _tdma_cpy(&btc->dm.tdma, &t_def[CXTD_OFF]); _slots_cpy(btc->dm.slot_now, s_def); _slots_cpy(btc->dm.slot, s_def); btc->policy_len = 0; btc->bt_req_len = 0; btc->hubmsg_cnt = 0; btc->dm.coex_info_map = BTC_COEX_INFO_ALL; btc->dm.wl_tx_limit.tx_time = BTC_MAX_TX_TIME_DEF; btc->dm.wl_tx_limit.tx_retry = BTC_MAX_TX_RETRY_DEF; } if (type & BTC_RESET_MDINFO) hal_mem_set(h, &btc->mdinfo, 0, sizeof(struct btc_module)); } static bool _chk_wl_rfk_request(struct btc_t *btc) { struct btc_cx *cx = &btc->cx; struct btc_bt_info *bt = &cx->bt; _update_bt_scbd(btc, true); cx->cnt_wl[BTC_WCNT_RFK_REQ]++; if ((bt->rfk_info.map.run || bt->rfk_info.map.req) && (!bt->rfk_info.map.timeout)) { cx->cnt_wl[BTC_WCNT_RFK_REJECT]++; return BTC_WRFK_REJECT; } else { cx->cnt_wl[BTC_WCNT_RFK_GO]++; return BTC_WRFK_ALLOW; } } void _set_init_info(struct btc_t *btc) { struct btc_dm *dm = &btc->dm; struct btc_wl_info *wl = &btc->cx.wl; dm->init_info.wl_only = (u8)dm->wl_only; dm->init_info.bt_only = (u8)dm->bt_only; dm->init_info.wl_init_ok = (u8)wl->status.map.init_ok; dm->init_info.dbcc_en = btc->hal->dbcc_en; dm->init_info.cx_other = btc->cx.other.type; dm->init_info.wl_guard_ch = btc->chip->afh_guard_ch; dm->init_info.module = btc->mdinfo; } u8 _get_role_link_mode(struct btc_t *btc, u8 role) { switch(role) { case PHL_RTYPE_STATION: return BTC_WLINK_2G_STA; case PHL_RTYPE_P2P_GO: return BTC_WLINK_2G_GO; case PHL_RTYPE_P2P_GC: return BTC_WLINK_2G_GC; case PHL_RTYPE_AP: return BTC_WLINK_2G_AP; default: return BTC_WLINK_OTHER; } } u8 _get_wl_role_idx(struct btc_t *btc, u8 role) { struct btc_wl_role_info *wl_rinfo = &btc->cx.wl.role_info; u8 i, pid = 0; for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) { if (wl_rinfo->active_role[i].role == role) break; } pid = i; return pid; /*cation: return MAX_WIFI_ROLE_NUMBER if role not found */ } void _set_wl_req_mac(struct btc_t *btc, u8 mac_id) { struct btc_wl_info *wl = &btc->cx.wl; struct btc_chip_ops *ops = btc->chip->ops; if (mac_id == wl->pta_req_mac) return; if (ops && ops->wl_req_mac) ops->wl_req_mac(btc, mac_id); wl->pta_req_mac = mac_id; } static bool _chk_role_ch_group(struct btc_t *btc, struct rtw_chan_def r1, struct rtw_chan_def r2) { bool is_grouped = false; if (r1.chan != r2.chan) { /* primary ch is different */ goto exit; } else if (r1.bw == CHANNEL_WIDTH_40 && r2.bw == CHANNEL_WIDTH_40) { if (r1.offset != r2.offset) goto exit; } is_grouped = true; exit: return is_grouped; } static void _update_wl_info(struct btc_t *btc) { struct btc_wl_info *wl = &btc->cx.wl; struct btc_wl_link_info *wl_linfo = wl->link_info; struct btc_wl_role_info *wl_rinfo = &wl->role_info; struct btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; struct btc_wl_active_role *active_role = NULL; struct rtw_hal_com_t *h = btc->hal; struct rtw_chan_def cid_ch[MAX_WIFI_ROLE_NUMBER]; u8 i, j, k, cnt_connect = 0, cnt_connecting = 0, cnt_active = 0; u8 cnt_2g = 0, cnt_5g = 0, max_role_cnt = BTC_TDMA_WLROLE_MAX; u8 cid_phy[MAX_WIFI_ROLE_NUMBER] = {0}; u8 cid_role[MAX_WIFI_ROLE_NUMBER] = {0}; u8 dbcc_2g_phy = 0, dbcc_2g_cid = 0, dbcc_2g_role = 0; u32 noa_duration = 0; bool b2g = false, b5g = false, client_joined = false, noa_exist = false; hal_mem_set(h, wl_rinfo, 0, sizeof(struct btc_wl_role_info)); hal_mem_set(h, wl_dinfo, 0, sizeof(struct btc_wl_dbcc_info)); hal_mem_set(h, cid_ch, 0, MAX_WIFI_ROLE_NUMBER * sizeof(struct rtw_chan_def)); for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) { /* check if role active? */ if (!wl_linfo[i].active || wl_linfo[i].phy >= HW_PHY_MAX) continue; cnt_active++; active_role = &wl_rinfo->active_role[cnt_active-1]; active_role->role = wl_linfo[i].role; active_role->pid = wl_linfo[i].pid; active_role->phy = wl_linfo[i].phy; active_role->band = wl_linfo[i].chdef.band; active_role->noa = (u8)wl_linfo[i].noa; active_role->noa_duration = wl_linfo[i].noa_duration/1000; /* check if role connect? */ if (wl_linfo[i].connected == MLME_NO_LINK) { wl_rinfo->active_role[cnt_active-1].connected = 0; continue; } else if (wl_linfo[i].connected == MLME_LINKING) { cnt_connecting++; } else { cnt_connect++; /* only if client connect for p2p-Go/AP */ if ((wl_linfo[i].role == PHL_RTYPE_P2P_GO || wl_linfo[i].role == PHL_RTYPE_AP) && wl_linfo[i].client_cnt > 1) client_joined = true; cid_phy[cnt_connect-1] = wl_linfo[i].phy; hal_mem_cpy(h, &cid_ch[cnt_connect-1], &wl_linfo[i].chdef, sizeof(struct rtw_chan_def)); cid_role[cnt_connect-1] = wl_linfo[i].role; } wl_rinfo->role_map.val |= BIT(wl_linfo[i].role); active_role->ch = wl_linfo[i].chdef.center_ch; active_role->bw = wl_linfo[i].chdef.bw; active_role->connected = 1; /* only one noa-role exist */ if (active_role->noa && active_role->noa_duration > 0) { noa_exist = true; noa_duration = active_role->noa_duration; } /* Check dbcc role,the last role may overwrite the former role, * This will be modified in the below code. */ if (h->dbcc_en) { wl_dinfo->role[wl_linfo[i].phy] = wl_linfo[i].role; wl_dinfo->op_band[wl_linfo[i].phy] = wl_linfo[i].chdef.band; max_role_cnt = BTC_TDMA_WLROLE_MAX + 1; } if (wl_linfo[i].chdef.band != BAND_ON_24G) { cnt_5g++; b5g = true; } else { cnt_2g++; b2g = true; } } wl_rinfo->connect_cnt = cnt_connect; /* Be careful to change the following sequence!! */ if (cnt_connect == 0) { wl_rinfo->link_mode = BTC_WLINK_NOLINK; wl_rinfo->role_map.role.none = 1; } else if (!b2g && b5g) { wl_rinfo->link_mode = BTC_WLINK_5G; } else if (wl_rinfo->role_map.role.nan) { wl_rinfo->link_mode = BTC_WLINK_2G_NAN; } else if (cnt_connect > max_role_cnt) { wl_rinfo->link_mode = BTC_WLINK_OTHER; } else if (h->dbcc_en) { /* find out the 2G-PHY by connect-id ->ch */ for (j = 0; j < cnt_connect; j++) { if (cid_ch[j].center_ch <= 14 || j == cnt_connect-1) break; } dbcc_2g_cid = j; dbcc_2g_phy = cid_phy[j]; dbcc_2g_role = cid_role[j]; /* find out the other role in the 2.4G_band-PHY*/ if (cnt_connect == 3) { for (k = 0; k < cnt_connect; k++) { if (k == j) continue; else if (cid_phy[k] == dbcc_2g_phy) break; } if (k < cnt_connect) { /* if find -> DBCC + MCC/SCC */ if (cid_ch[k].center_ch > 14) wl_rinfo->link_mode = BTC_WLINK_25G_MCC; else if (_chk_role_ch_group(btc, cid_ch[j], cid_ch[k])) wl_rinfo->link_mode = BTC_WLINK_2G_SCC; else wl_rinfo->link_mode = BTC_WLINK_2G_MCC; /* correct 2G-located PHY role/band * for gnt ctrl. */ wl_dinfo->role[dbcc_2g_phy] = dbcc_2g_role; wl_dinfo->op_band[dbcc_2g_phy] = BAND_ON_24G; goto exit; } } wl_rinfo->link_mode = _get_role_link_mode(btc, cid_role[j]); } else if (b2g && b5g && cnt_connect == 2) { wl_rinfo->link_mode = BTC_WLINK_25G_MCC; } else if (!b5g && cnt_connect == 2) { /* cnt_connect = 2 */ if (_chk_role_ch_group(btc, cid_ch[0], cid_ch[cnt_connect - 1])) wl_rinfo->link_mode = BTC_WLINK_2G_SCC; else wl_rinfo->link_mode= BTC_WLINK_2G_MCC; } else if (!b5g && cnt_connect == 1) { /* cnt_connect = 1 */ wl_rinfo->link_mode = _get_role_link_mode(btc, cid_role[0]); } /* if no client_joined, don't care P2P-GO/AP role */ if ((wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) && !client_joined) { #if 0 /* work-around for Go/AP(no-client) can not move channel to same as STA*/ if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC || wl_rinfo->link_mode == BTC_WLINK_2G_MCC) #else if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC) #endif { wl_rinfo->link_mode = BTC_WLINK_2G_STA; wl_rinfo->connect_cnt--; } else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO || wl_rinfo->link_mode == BTC_WLINK_2G_AP) { wl_rinfo->link_mode = BTC_WLINK_NOLINK; wl_rinfo->connect_cnt--; } } exit: /* Identify 2-Role type */ if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC || wl_rinfo->link_mode == BTC_WLINK_2G_MCC || wl_rinfo->link_mode == BTC_WLINK_25G_MCC || (wl_rinfo->link_mode == BTC_WLINK_5G && cnt_connect > 1)) { if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) { if (noa_exist) wl_rinfo->mrole_type = BTC_WLMROLE_STA_GO_NOA; else wl_rinfo->mrole_type = BTC_WLMROLE_STA_GO; } else if (wl_rinfo->role_map.role.p2p_gc) { if (noa_exist) wl_rinfo->mrole_type = BTC_WLMROLE_STA_GC_NOA; else wl_rinfo->mrole_type = BTC_WLMROLE_STA_GC; } else { wl_rinfo->mrole_type = BTC_WLMROLE_STA_STA; } wl_rinfo->mrole_noa_duration = noa_duration; } else { wl_rinfo->mrole_type = BTC_WLMROLE_NONE; wl_rinfo->mrole_noa_duration = 0; } hal_btc_fw_set_drv_info(btc, CXDRVINFO_ROLE); if (h->dbcc_en) { _set_wl_req_mac(btc, dbcc_2g_phy); _update_dbcc_band(HW_PHY_0); _update_dbcc_band(HW_PHY_1); hal_btc_fw_set_drv_info(btc, CXDRVINFO_DBCC); } else { _set_wl_req_mac(btc, HW_PHY_0); } } void _run_coex(struct btc_t *btc, const char *reason) { struct btc_dm *dm = &btc->dm; struct btc_cx *cx = &btc->cx; struct btc_wl_info *wl = &cx->wl; struct btc_bt_info *bt = &cx->bt; struct btc_wl_role_info *wl_rinfo = &wl->role_info; u8 mode = wl_rinfo->link_mode; PHL_INFO("[BTC], %s(): reason = %s, mode=%d\n", __func__, reason, mode); _rsn_cpy(dm->run_reason, (char*)reason); _update_dm_step(btc, reason); #if BTC_CX_FW_OFFLOAD if (wl->rfk_info.state != BTC_WRFK_STOP && mode != BTC_WLINK_5G) { _action_wl_rfk(btc); } else if (wl->rfk_info.state == BTC_WRFK_STOP) { PHL_INFO("[BTC], %s(): offload to WL_FW\n", __func__); hal_btc_fw_set_drv_info(btc, CXDRVINFO_SMAP); hal_btc_fw_set_drv_info(btc, CXDRVINFO_RUN); } goto exit; #else _update_btc_state_map(btc); _get_wl_nhm_dbm(btc); /* Be careful to change the following function sequence!! */ if (btc->ctrl.manual) { PHL_INFO("[BTC], %s(): return for Manual CTRL!!\n", __func__); return; } if (btc->ctrl.igno_bt && (run_rsn("_update_bt_info") || run_rsn("_update_bt_scbd"))) { PHL_INFO("[BTC], %s(): return for Stop Coex DM!!\n", __func__); return; } if (!wl->status.map.init_ok) { PHL_INFO("[BTC], %s(): return for WL init fail!!\n", __func__); return; } if (wl->status.map.rf_off_pre == wl->status.map.rf_off && wl->status.map.lps_pre == wl->status.map.lps) { if (run_rsn("_ntfy_power_off") || run_rsn("_ntfy_radio_state")) { PHL_INFO("[BTC], %s(): return for WL rf off state no change!!\n", __func__); return; } if (wl->status.map.rf_off == 1 || wl->status.map.lps == BTC_LPS_RF_OFF) { PHL_INFO("[BTC], %s(): return for WL rf off state!!\n", __func__); return; } } dm->cnt_dm[BTC_DCNT_RUN]++; dm->freerun = false; btc->ctrl.igno_bt = false; bt->scan_rx_low_pri = false; if (btc->ctrl.always_freerun) { _action_freerun(btc); btc->ctrl.igno_bt = true; goto exit; } if (dm->wl_only) { _action_wl_only(btc); btc->ctrl.igno_bt = true; goto exit; } if (wl->status.map.rf_off || wl->status.map.lps || dm->bt_only) { _action_wl_off(btc); btc->ctrl.igno_bt = true; goto exit; } if (run_rsn("_ntfy_init_coex")) { _action_wl_init(btc); goto exit; } if (!cx->bt.enable.now && !cx->other.type) { _action_bt_off(btc); goto exit; } if (cx->bt.whql_test) { _action_bt_whql(btc); goto exit; } if (wl->rfk_info.state != BTC_WRFK_STOP) { _action_wl_rfk(btc); goto exit; } if (cx->state_map == BTC_WLINKING && (mode == BTC_WLINK_NOLINK || mode == BTC_WLINK_2G_STA || mode == BTC_WLINK_5G)) { _action_wl_scan(btc); goto exit; } if (wl->status.map.scan) { _action_wl_scan(btc); goto exit; } switch (mode) { case BTC_WLINK_NOLINK: _action_wl_nc(btc); break; case BTC_WLINK_2G_STA: _action_wl_2g_sta(btc); break; case BTC_WLINK_2G_AP: bt->scan_rx_low_pri = true; _action_wl_2g_ap(btc); break; case BTC_WLINK_2G_GO: bt->scan_rx_low_pri = true; _action_wl_2g_go(btc); break; case BTC_WLINK_2G_GC: bt->scan_rx_low_pri = true; _action_wl_2g_gc(btc); break; case BTC_WLINK_2G_SCC: bt->scan_rx_low_pri = true; _action_wl_2g_scc(btc); break; case BTC_WLINK_2G_MCC: bt->scan_rx_low_pri = true; _action_wl_2g_mcc(btc); break; case BTC_WLINK_25G_MCC: bt->scan_rx_low_pri = true; _action_wl_25g_mcc(btc); break; case BTC_WLINK_5G: _action_wl_5g(btc); break; case BTC_WLINK_2G_NAN: _action_wl_2g_nan(btc); break; default: _action_wl_other(btc); break; } #endif exit: _action_common(btc); } static void _update_offload_runinfo(struct btc_t *btc, u8 *buf, u32 len) { #if BTC_CX_FW_OFFLOAD u32 val; struct btc_cxr_result *r = NULL; if (!buf || buf[0] >= BTC_CXR_MAX) return; switch(buf[0]) { case BTC_CXR_WSCBD: val = (buf[4] << 24) + (buf[3] << 16) + (buf[2] << 8) + buf[1]; if (val & BIT(31)) /* if val & BIT(31) --> write scbd bit false */ _write_scbd(btc, val & 0x7fffffff, false); else _write_scbd(btc, val, true); break; case BTC_CXR_RESULT: r = (struct btc_cxr_result*) &buf[1]; btc->dm.freerun = r->dm.freerun; btc->dm.wl_ps_ctrl = r->dm.wl_ps_ctrl; btc->dm.leak_ap = r->dm.leak_ap; btc->ctrl.igno_bt = (u16)r->dm.igno_bt; btc->dm.noisy_level = r->dm.noisy_level; btc->dm.set_ant_path = r->dm.set_ant_path; btc->dm.rf_trx_para = r->rf_trx_para; btc->cx.state_map = r->cx_state_map; btc->policy_type = (u16)r->policy_type; btc->dm.cnt_dm[BTC_DCNT_RUN] = r->run_cnt; hal_mem_cpy(btc->hal, btc->dm.run_reason, r->run_reason, BTC_RSN_MAXLEN); hal_mem_cpy(btc->hal, btc->dm.run_action, r->run_action, BTC_ACT_MAXLEN); break; } #endif } void _update_bt_scbd(struct btc_t *btc, bool only_update) { struct btc_cx *cx = &btc->cx; struct btc_bt_info *bt = &cx->bt; u32 val; bool status_change = false, bt_link_change = false; if (!btc->chip->scbd) return; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s()\n", __func__); val = _read_scbd(btc); if (val == 0xffffffff) { PHL_TRACE(COMP_PHL_BTC, _PHL_ERR_, "[BTC], %s return by invalid scbd value\n", __func__); return; } if (!(val & BTC_BSCB_ON) || btc->dm.cnt_dm[BTC_DCNT_BTCNT_FREEZE] >= BTC_CHK_HANG_MAX) bt->enable.now = 0; else bt->enable.now = 1; if (bt->enable.now != bt->enable.last) { status_change = true; bt_link_change = true; } /* reset bt info if bt re-enable */ if (bt->enable.now && !bt->enable.last) { _reset_btc_var(btc, BTC_RESET_BTINFO); cx->cnt_bt[BTC_BCNT_REENABLE]++; bt->enable.now = 1; } bt->enable.last = bt->enable.now; bt->scbd = val; bt->mbx_avl = !!(val & BTC_BSCB_ACT); if (bt->whql_test != (u32)(!!(val & BTC_BSCB_WHQL))) status_change = true; bt->whql_test = !!(val & BTC_BSCB_WHQL); bt->btg_type = (val & BTC_BSCB_BT_S1 ? BTC_BT_BTG: BTC_BT_ALONE); bt->link_info.a2dp_desc.active = !!(val & BTC_BSCB_A2DP_ACT); /* if rfk run 1->0 */ if (bt->rfk_info.map.run && !(val & BTC_BSCB_RFK_RUN)) status_change = true; bt->rfk_info.map.run = !!(val & BTC_BSCB_RFK_RUN); bt->rfk_info.map.req = !!(val & BTC_BSCB_RFK_REQ); bt->hi_lna_rx = !!(val & BTC_BSCB_BT_HILNA); /* if connect change */ if ((bt->link_info.status.map.connect && (!(val & BTC_BSCB_BT_CONNECT))) || (!bt->link_info.status.map.connect && (val & BTC_BSCB_BT_CONNECT))) { status_change = true; bt_link_change = true; } bt->link_info.status.map.connect = !!(val & BTC_BSCB_BT_CONNECT); bt->run_patch_code = !!(val & BTC_BSCB_PATCH_CODE); #if !BTC_CX_FW_OFFLOAD if (bt_link_change) { PHL_INFO("[BTC], %s: bt status change!!\n", __func__); hal_btc_send_event(btc, NULL, 0, BTC_HMSG_BT_LINK_CHG); } if (!only_update && status_change) _run_coex(btc, __func__); #endif } /****************************************************************************** * * coexistence operations for external notifications * *****************************************************************************/ static void _ntfy_power_on(struct btc_t *btc) { /* no action for power on, beacuse power-on move halmac API * the _ntfy_power_on = _ntfy_init_coex */ } static void _ntfy_power_off(struct btc_t *btc) { PHL_INFO("[BTC], %s()\n", __func__); btc->dm.cnt_notify[BTC_NCNT_POWER_OFF]++; btc->cx.wl.status.map.rf_off = 1; btc->cx.wl.status.map.busy = 0; _write_scbd(btc, BTC_WSCB_ALL, false); _run_coex(btc, __func__); hal_btc_fw_en_rpt(btc, RPT_EN_ALL, 0); btc->cx.wl.status.map.rf_off_pre = btc->cx.wl.status.map.rf_off; } static void _ntfy_init_coex(struct btc_t *btc, u8 mode) { struct btc_dm *dm = &btc->dm; struct btc_wl_info *wl = &btc->cx.wl; struct btc_chip_ops *ops = btc->chip->ops; PHL_INFO("[BTC], %s(): mode=%d\n", __func__, mode); dm->cnt_notify[BTC_NCNT_INIT_COEX]++; dm->wl_only = (mode == BTC_MODE_WL? 1 : 0); dm->bt_only = (mode == BTC_MODE_BT? 1 : 0); wl->status.map.rf_off = (mode == BTC_MODE_WLOFF? 1 : 0); if (!wl->status.map.init_ok) { PHL_TRACE(COMP_PHL_BTC, _PHL_ERR_, "[BTC], %s(): return for WL init fail!!\n", __func__); dm->error.map.init = true; return; } /* Setup RF front end type from EFuse RFE type*/ if (ops && ops->set_rfe) ops->set_rfe(btc); if (ops && ops->init_cfg) ops->init_cfg(btc); _write_scbd(btc, BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG, true); _update_bt_scbd(btc, true); /* check PTA control owner to avoid BT coex issue */ if (_read_cx_ctrl(btc) == BTC_CTRL_BY_WL) { PHL_TRACE(COMP_PHL_BTC, _PHL_ERR_, "[BTC], %s(): PTA owner warning!!\n", __func__); dm->error.map.pta_owner = true; } _set_init_info(btc); _set_wl_tx_power(btc, BTC_WL_DEF_TX_PWR); /* original tx power, no Tx power adjust */ hal_btc_fw_set_slots(btc, CXST_MAX, dm->slot); hal_btc_fw_set_monreg(btc); hal_btc_fw_set_drv_info(btc, CXDRVINFO_INIT); hal_btc_fw_set_drv_info(btc, CXDRVINFO_CTRL); _run_coex(btc, __func__); _btmr_start(btc); #if 0 hal_btc_fw_set_gpio_dbg(btc, CXDGPIO_EN_MAP, BIT(BTC_DBG_GNT_WL) | BIT(BTC_DBG_GNT_BT)); #endif } static void _ntfy_scan_start(struct btc_t *btc, u8 phy_idx, u8 band) { struct btc_wl_info *wl = &btc->cx.wl; PHL_INFO("[BTC], %s(): phy_idx=%d, band=%d\n", __func__, phy_idx, band); btc->dm.cnt_notify[BTC_NCNT_SCAN_START]++; wl->status.map.scan = true; wl->scan_info.band[phy_idx] = band; wl->scan_info.phy_map |= BIT(phy_idx); hal_btc_fw_set_drv_info(btc, CXDRVINFO_SCAN); if (btc->hal->dbcc_en) { wl->dbcc_info.scan_band[phy_idx] = band; _update_dbcc_band(phy_idx); hal_btc_fw_set_drv_info(btc, CXDRVINFO_DBCC); } _run_coex(btc, __func__); } static void _ntfy_scan_finish(struct btc_t *btc, u8 phy_idx) { struct btc_wl_info *wl = &btc->cx.wl; PHL_INFO("[BTC], %s(): phy_idx=%d\n", __func__, phy_idx); btc->dm.cnt_notify[BTC_NCNT_SCAN_FINISH]++; wl->status.map.scan = false; wl->scan_info.phy_map &= ~BIT(phy_idx); hal_btc_fw_set_drv_info(btc, CXDRVINFO_SCAN); if (btc->hal->dbcc_en) { _update_dbcc_band(phy_idx); hal_btc_fw_set_drv_info(btc, CXDRVINFO_DBCC); } _run_coex(btc, __func__); } static void _ntfy_switch_band(struct btc_t *btc, u8 phy_idx, u8 band) { struct btc_wl_info *wl = &btc->cx.wl; PHL_INFO("[BTC], %s(): phy_idx=%d, band=%d\n", __func__, phy_idx, band); btc->dm.cnt_notify[BTC_NCNT_SWITCH_BAND]++; wl->scan_info.band[phy_idx] = band; wl->scan_info.phy_map |= BIT(phy_idx); hal_btc_fw_set_drv_info(btc, CXDRVINFO_SCAN); if (btc->hal->dbcc_en) { wl->dbcc_info.scan_band[phy_idx] = band; _update_dbcc_band(phy_idx); hal_btc_fw_set_drv_info(btc, CXDRVINFO_DBCC); } _run_coex(btc, __func__); } static void _ntfy_specific_packet(struct btc_t *btc, u8 pkt_type) { struct btc_cx *cx = &btc->cx; struct btc_wl_info *wl = &cx->wl; struct btc_bt_link_info *b = &cx->bt.link_info; struct btc_bt_hfp_desc *hfp = &b->hfp_desc; struct btc_bt_hid_desc *hid = &b->hid_desc; u32 cnt, delay = BTC_SPECPKT_MAXT; switch (pkt_type) { case BTC_PKT_EVT_DHCP: cnt = ++cx->cnt_wl[BTC_WCNT_DHCP]; PHL_INFO("[BTC], %s(): pkt_type=%d, DHCP cnt=%d \n", __func__, pkt_type, cnt); wl->status.map.connecting = true; _set_btc_timer(btc, BTC_TIMER_WL_SPECPKT, delay); break; case BTC_PKT_EVT_EAPOL: cnt = ++cx->cnt_wl[BTC_WCNT_EAPOL]; PHL_INFO("[BTC], %s(): pkt_type=%d, EAPOL cnt=%d \n", __func__, pkt_type, cnt); wl->status.map._4way = true; if (hfp->exist || hid->exist) delay = delay / 2; _set_btc_timer(btc, BTC_TIMER_WL_SPECPKT, delay); break; case BTC_PKT_EVT_ADD_KEY: cnt = ++cx->cnt_wl[BTC_WCNT_EAPOL]; PHL_INFO("[BTC], %s(): pkt_type=%d, EAPOL_End cnt=%d \n", __func__, pkt_type, cnt); wl->status.map._4way = false; break; default: case BTC_PKT_EVT_ARP: cnt = ++cx->cnt_wl[BTC_WCNT_ARP]; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(): pkt_type=%d, ARP cnt=%d\n", __func__, pkt_type, cnt); return; } btc->dm.cnt_notify[BTC_NCNT_SPECIAL_PACKET]++; _run_coex(btc, __func__); } static void _update_bt_psd(struct btc_t *btc, u8 *buf, u32 len) { PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s():\n", __func__); } void _update_dm_step(struct btc_t *btc, const char *strin) { struct btc_dm *dm = &btc->dm; u32 store_index = 0; dm->dm_step.cnt++; if (dm->dm_step.cnt == 0) dm->dm_step.cnt = 1; store_index = ((dm->dm_step.cnt-1) % BTC_DM_MAXSTEP); _rsn_cpy(dm->dm_step.step[store_index], (char*)strin); } static void _update_bt_info(struct btc_t *btc, u8 *buf, u32 len) { struct rtw_hal_com_t *h = btc->hal; struct btc_cx *cx = &btc->cx; struct btc_bt_info *bt = &cx->bt; struct btc_bt_link_info *b = &bt->link_info; struct btc_bt_hfp_desc *hfp = &b->hfp_desc; struct btc_bt_hid_desc *hid = &b->hid_desc; struct btc_bt_a2dp_desc *a2dp = &b->a2dp_desc; struct btc_bt_pan_desc *pan = &b->pan_desc; union btc_btinfo btinfo; bool bt_link_change = false; if (buf[BTC_BTINFO_L1] != BTC_BT_INFO_LEN) return; /* return if bt info match last bt-info */ if (!hal_mem_cmp(h, bt->raw_info, buf, BTC_BTINFO_MAX)) { PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s return by bt-info duplicate!!\n", __func__); cx->cnt_bt[BTC_BCNT_INFOSAME]++; return; } hal_mem_cpy(h, bt->raw_info, buf, BTC_BTINFO_MAX); PHL_INFO("[BTC], %s: bt_info[2]=0x%02x\n", __func__, bt->raw_info[2]); /* reset to mo-connect before update */ b->profile_cnt.last = b->profile_cnt.now; b->relink.last = b->relink.now; a2dp->exist_last = a2dp->exist; b->multi_link.last = b->multi_link.now; bt->inq_pag.last = bt->inq_pag.now; b->profile_cnt.now = 0; hid->type = 0; /* ======= parse raw info low-Byte2 ======= */ btinfo.val = bt->raw_info[BTC_BTINFO_L2]; b->status.map.connect = btinfo.lb2.connect; b->status.map.sco_busy = btinfo.lb2.sco_busy; b->status.map.acl_busy = btinfo.lb2.acl_busy; b->status.map.inq_pag = btinfo.lb2.inq_pag; bt->inq_pag.now = btinfo.lb2.inq_pag; cx->cnt_bt[BTC_BCNT_INQPAG] += !!(bt->inq_pag.now && !bt->inq_pag.last); hfp->exist = btinfo.lb2.hfp; b->profile_cnt.now += (u8)hfp->exist; hid->exist = btinfo.lb2.hid; b->profile_cnt.now += (u8)hid->exist; a2dp->exist = btinfo.lb2.a2dp; b->profile_cnt.now += (u8)a2dp->exist; pan->exist = btinfo.lb2.pan; b->profile_cnt.now += (u8)pan->exist; /* ======= parse raw info low-Byte3 ======= */ btinfo.val = bt->raw_info[BTC_BTINFO_L3]; if (btinfo.lb3.retry != 0) cx->cnt_bt[BTC_BCNT_RETRY]++; b->cqddr = btinfo.lb3.cqddr; cx->cnt_bt[BTC_BCNT_INQ] += !!(btinfo.lb3.inq && !bt->inq); bt->inq = btinfo.lb3.inq; cx->cnt_bt[BTC_BCNT_PAGE] += !!(btinfo.lb3.pag && !bt->pag); bt->pag = btinfo.lb3.pag; b->status.map.mesh_busy = btinfo.lb3.mesh_busy; /* ======= parse raw info high-Byte0 ======= */ btinfo.val = bt->raw_info[BTC_BTINFO_H0]; /* raw val is dBm unit, translate from -100~ 0dBm to 0~100%*/ b->rssi = btc->chip->ops->bt_rssi(btc, btinfo.hb0.rssi); /* ======= parse raw info high-Byte1 ======= */ btinfo.val = bt->raw_info[BTC_BTINFO_H1]; b->status.map.ble_connect = btinfo.hb1.ble_connect; if (btinfo.hb1.ble_connect) hid->type |= (hid->exist? BTC_HID_BLE : BTC_HID_RCU); cx->cnt_bt[BTC_BCNT_REINIT] += !!(btinfo.hb1.reinit && !bt->reinit); bt->reinit = btinfo.hb1.reinit; cx->cnt_bt[BTC_BCNT_RELINK] += !!(btinfo.hb1.relink && !b->relink.now); b->relink.now = btinfo.hb1.relink; cx->cnt_bt[BTC_BCNT_IGNOWL] += !!(btinfo.hb1.igno_wl && !bt->igno_wl); bt->igno_wl = btinfo.hb1.igno_wl; hid->type |= (btinfo.hb1.voice? BTC_HID_RCU_VOICE : 0); bt->ble_scan_en = btinfo.hb1.ble_scan; cx->cnt_bt[BTC_BCNT_ROLESW] += !!(btinfo.hb1.role_sw && !b->role_sw); b->role_sw = btinfo.hb1.role_sw; b->multi_link.now = btinfo.hb1.multi_link; if (b->multi_link.now != b->multi_link.last) bt_link_change = true; /* ======= parse raw info high-Byte2 ======= */ btinfo.val = bt->raw_info[BTC_BTINFO_H2]; pan->active = !!btinfo.hb2.pan_active; cx->cnt_bt[BTC_BCNT_AFH] += !!(btinfo.hb2.afh_update && !b->afh_update); b->afh_update = btinfo.hb2.afh_update; a2dp->active = btinfo.hb2.a2dp_active; b->slave_role = btinfo.hb2.slave; hid->slot_info = btinfo.hb2.hid_slot; if (hid->pair_cnt != btinfo.hb2.hid_cnt) bt_link_change = true; hid->pair_cnt = btinfo.hb2.hid_cnt; hid->type |= (hid->slot_info == BTC_HID_218? BTC_HID_218 : BTC_HID_418); /* ======= parse raw info high-Byte3 ======= */ btinfo.val = bt->raw_info[BTC_BTINFO_H3]; a2dp->bitpool = btinfo.hb3.a2dp_bitpool; if (b->tx_3M != (u32)btinfo.hb3.tx_3M) cx->cnt_bt[BTC_BCNT_RATECHG]++; b->tx_3M = (u32)btinfo.hb3.tx_3M; a2dp->sink = btinfo.hb3.a2dp_sink; #if !BTC_CX_FW_OFFLOAD if (bt->igno_wl && !cx->wl.status.map.rf_off) _set_bt_ignore_wlan_act(btc, false); if (b->profile_cnt.now || b->status.map.ble_connect) hal_btc_fw_en_rpt(btc, RPT_EN_BT_AFH_MAP, 1); else hal_btc_fw_en_rpt(btc, RPT_EN_BT_AFH_MAP, 0); if (bt_link_change) { PHL_INFO("[BTC], %s: bt link change!!\n", __func__); hal_btc_send_event(btc, NULL, 0, BTC_HMSG_BT_LINK_CHG); } /* reset after A2DP stop->play */ if (!a2dp->exist_last && a2dp->exist) { a2dp->vendor_id = 0; a2dp->flush_time = 0; a2dp->play_latency = 1; _set_btc_timer(btc, BTC_TIMER_BT_A2DPPLAY, BTC_A2DP_RESUME_MAXT); } if (a2dp->exist && (a2dp->flush_time == 0 || a2dp->vendor_id == 0 || a2dp->play_latency == 1)) hal_btc_fw_en_rpt(btc, RPT_EN_BT_DEVICE_INFO, 1); else hal_btc_fw_en_rpt(btc, RPT_EN_BT_DEVICE_INFO, 0); _run_coex(btc, __func__); #endif } static void _ntfy_role_info(struct btc_t *btc, u8 rid, struct rtw_wifi_role_t *wrole, struct rtw_phl_stainfo_t *sta, enum role_state reason) { struct rtw_hal_com_t *h = btc->hal; struct btc_wl_info *wl = &btc->cx.wl; struct btc_wl_link_info *r = NULL; #ifdef CONFIG_PHL_P2PPS u8 i =0; #endif /* CONFIG_PHL_P2PPS */ PHL_INFO("[BTC], %s(), role_id=%d, reason=%d\n", __func__, rid, reason); if (rid >= MAX_WIFI_ROLE_NUMBER) return; btc->dm.cnt_notify[BTC_NCNT_ROLE_INFO]++; r = &wl->link_info[rid]; if (wrole) { r->role = wrole->type; #ifdef RTW_WKARD_ROLE_TYPE if (wrole->mstate != MLME_NO_LINK && wrole->real_type != PHL_RTYPE_NONE) { r->role = wrole->real_type; PHL_INFO("[BTC], rtw_hal_btc_update_role_info_ntfy(): set r.role from type(%d) to real_type(%d)\n", wrole->type, wrole->real_type); } #endif /* RTW_WKARD_ROLE_TYPE */ #ifdef CONFIG_PHL_P2PPS r->noa = 0; r->noa_duration = 0; for (i = 0; i < MAX_NOA_DESC; i++) { if (wrole->noa_desc[i].enable) { r->noa = 1; r->noa_duration = wrole->noa_desc[i].duration; break; } } #endif /* CONFIG_PHL_P2PPS */ r->phy = wrole->hw_band; r->pid = wrole->hw_port; r->active = wrole->active; r->connected = wrole->mstate; r->mode = wrole->cap.wmode; r->client_cnt = wrole->assoc_sta_queue.cnt; #ifdef RTW_PHL_BCN r->bcn_period = wrole->bcn_cmn.bcn_interval; r->dtim_period = wrole->dtim_period; #endif hal_mem_cpy(h, &r->chdef, &wrole->chandef, sizeof(struct rtw_chan_def)); hal_mem_cpy(h, r->mac_addr, wrole->mac_addr, MAC_ALEN); } if (sta && wrole->type == PHL_RTYPE_STATION) {/*associated node info??*/ r->mac_id = sta->macid; r->mode = (u8)sta->wmode; r->stbc_he_tx = (u32)sta->asoc_cap.stbc_he_tx; r->stbc_vht_tx = (u32)sta->asoc_cap.stbc_vht_tx; r->stbc_ht_tx = (u32)sta->asoc_cap.stbc_ht_tx; } /* refresh wifi info */ _update_wl_info(btc); if (r->role == PHL_RTYPE_STATION && r->connected == MLME_NO_LINK) btc->dm.leak_ap = 0; if (reason == PHL_ROLE_MSTS_STA_CONN_START) wl->status.map.connecting = 1; else wl->status.map.connecting = 0; if (reason == PHL_ROLE_MSTS_STA_DIS_CONN) wl->status.map._4way = false; _run_coex(btc, __func__); } static void _ntfy_radio_state(struct btc_t *btc, u8 rf_state) { struct btc_wl_info *wl = &btc->cx.wl; struct btc_chip_ops *ops = btc->chip->ops; PHL_INFO("[BTC], %s(): rf_state =%d\n", __func__, rf_state); btc->dm.cnt_notify[BTC_NCNT_RADIO_STATE]++; switch(rf_state) { case BTC_RFCTRL_WL_OFF: wl->status.map.rf_off = 1; wl->status.map.lps = BTC_LPS_OFF; wl->status.map.busy = 0; break; case BTC_RFCTRL_FW_CTRL: /* LPS-PG, LPS-CG */ wl->status.map.rf_off = 0; wl->status.map.lps = BTC_LPS_RF_OFF; wl->status.map.busy = 0; break; case BTC_RFCTRL_LPS_WL_ON: /* LPS-Protocol (RFon) */ wl->status.map.rf_off = 0; wl->status.map.lps = BTC_LPS_RF_ON; wl->status.map.busy = 0; break; case BTC_RFCTRL_WL_ON: default: wl->status.map.rf_off = 0; wl->status.map.lps = BTC_LPS_OFF; break; } if (rf_state == BTC_RFCTRL_WL_ON) { hal_btc_fw_en_rpt(btc, RPT_EN_MREG, 1); _write_scbd(btc, BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG, true); _update_bt_scbd(btc, true); if (ops && ops->init_cfg) ops->init_cfg(btc); } else { hal_btc_fw_en_rpt(btc, RPT_EN_ALL, 0); /* for BT only isolated issue, * clear all scbd if LPS-PG or "LPS-off to LPS-Protocol" */ if (rf_state == BTC_RFCTRL_LPS_WL_ON && wl->status.map.lps_pre != 0) { _write_scbd(btc, BTC_WSCB_ON, true); } else { _write_scbd(btc, BTC_WSCB_ALL, false); } } _run_coex(btc, __func__); wl->status.map.rf_off_pre = wl->status.map.rf_off; wl->status.map.lps_pre = wl->status.map.lps; } static void _ntfy_customerize(struct btc_t *btc, u8 type, u16 len, u8 *buf) { struct btc_bt_info *bt = &btc->cx.bt; if (!buf) return; PHL_INFO("[BTC], %s !! \n", __func__); btc->dm.cnt_notify[BTC_NCNT_CUSTOMERIZE]++; switch (type) { case PHL_BTC_CNTFY_BTINFO: if (len != 1) return; buf[0] = bt->raw_info[BTC_BTINFO_L2]; break; } } static u8 _ntfy_wl_rfk(struct btc_t *btc, u8 phy_path, u8 type, u8 state) { struct btc_cx *cx = &btc->cx; struct btc_wl_info *wl = &cx->wl; bool result = BTC_WRFK_REJECT; wl->rfk_info.type = type; wl->rfk_info.path_map = phy_path & BTC_RFK_PATH_MAP; wl->rfk_info.phy_map = (phy_path & BTC_RFK_PHY_MAP) >> 4; wl->rfk_info.band = (phy_path & BTC_RFK_BAND_MAP) >> 6; state &= (BIT(0) | BIT(1)); PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s()_start: phy=0x%x, path=0x%x, type=%d, state=%d\n", __func__, wl->rfk_info.phy_map, wl->rfk_info.path_map, type, state); switch (state) { case BTC_WRFK_START: result = _chk_wl_rfk_request(btc); wl->rfk_info.state = (result? BTC_WRFK_START : BTC_WRFK_STOP); #if 0 hal_btc_fw_set_drv_info(btc, CXDRVINFO_RFK); _write_scbd(btc, BTC_WSCB_WLRFK, result); #endif btc->dm.cnt_notify[BTC_NCNT_WL_RFK]++; break; case BTC_WRFK_ONESHOT_START: case BTC_WRFK_ONESHOT_STOP: if (wl->rfk_info.state == BTC_WRFK_STOP) { result = BTC_WRFK_REJECT; } else { result = BTC_WRFK_ALLOW; wl->rfk_info.state = state; } break; case BTC_WRFK_STOP: result = BTC_WRFK_ALLOW; wl->rfk_info.state = BTC_WRFK_STOP; #if 0 hal_btc_fw_set_drv_info(btc, CXDRVINFO_RFK); _write_scbd(btc, BTC_WSCB_WLRFK, false); #endif break; } if (result == BTC_WRFK_ALLOW) { /* Only update coex for RFK START and STOP * because Start -> OneSHOT_START time is short */ if (wl->rfk_info.state == BTC_WRFK_START || wl->rfk_info.state == BTC_WRFK_STOP) _run_coex(btc, __func__); if (wl->rfk_info.state == BTC_WRFK_START) /* wait 300ms */ _set_btc_timer(btc, BTC_TIMER_WL_RFKTO, BTC_WRFK_MAXT); } PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_,"[BTC], %s()_finish: rfk_cnt=%d, result=%d\n", __func__, btc->dm.cnt_notify[BTC_NCNT_WL_RFK], result); return result; } static void _ntfy_wl_sta(struct btc_t *btc, struct rtw_stats *phl_stats, u8 ntfy_num, struct rtw_phl_stainfo_t *sta[], u8 reason) { struct rtw_hal_com_t *h = btc->hal; struct btc_wl_info *wl = &btc->cx.wl; struct btc_dm *dm = &btc->dm; struct btc_module *module = &btc->mdinfo; struct btc_wl_stat_info w[MAX_WIFI_ROLE_NUMBER] = {0}; struct btc_traffic *t = NULL, *link_info_t = NULL; struct btc_wl_link_info *link_info = NULL; struct rtw_phl_rainfo ra_info = {0}; u8 i, j, busy = 0, dir = 0, rssi_map = 0; u8 busy_all = 0, dir_all = 0, rssi_map_all = 0; u8 *rssi_state = NULL, rssi_thres = 0; bool is_sta_change = false, is_traffic_change = false; u16 last_tx_rate, last_rx_rate, last_tx_lvl, last_rx_lvl; u32 chk_intvl = 20; /* rssi_map = 4 bits for rssi locate in which {60, 50, 40, 30} * if rssi >= 60% (-50dBm) --> map = 4b'0000 --> rssi_level = 0 * if 50% <= rssi < 60% --> map = 4b'0001 --> rssi_level = 1 * if 40% <= rssi < 50% --> map = 4b'0011 --> rssi_level = 2 * if 30% <= rssi < 40% --> map = 4b'0111 --> rssi_level = 3 * if rssi < 20% --> map = 4b'1111 --> rssi_level = 4 */ dm->cnt_notify[BTC_NCNT_WL_STA]++; for (i = 0; i < ntfy_num; i++) { /* Extract btc_wl_stat_info from rtw_phl_stainfo_t */ hal_mem_set(h, &ra_info, 0, sizeof(ra_info)); w[i].pid = sta[i]->wrole->id; w[i].stat.rssi = sta[i]->hal_sta->rssi_stat.rssi >> 1; t = &w[i].stat.traffic; t->tx_lvl = phl_stats->tx_traffic.lvl; t->tx_sts = phl_stats->tx_traffic.sts; t->rx_lvl = phl_stats->rx_traffic.lvl; t->rx_sts = phl_stats->rx_traffic.sts; if (RTW_HAL_STATUS_SUCCESS == rtw_hal_bb_query_rainfo(h, sta[i]->hal_sta, &ra_info)) t->tx_rate = ra_info.rate; else t->tx_rate = RTW_DATA_RATE_MAX; t->rx_rate = h->trx_stat.rx_rate_plurality; /* transfer btc_wl_stat_info to btc_wl_link_info */ link_info = &wl->link_info[w[i].pid]; link_info_t = &link_info->stat.traffic; if (link_info->connected == MLME_NO_LINK) { link_info->rx_rate_drop_cnt = 0; rssi_map_all |= rssi_map; continue; } last_tx_rate = link_info_t->tx_rate; last_rx_rate = link_info_t->rx_rate; last_tx_lvl = (u16)link_info_t->tx_lvl; last_rx_lvl = (u16)link_info_t->rx_lvl; /* refresh link_info traffic/rssi related info from t */ hal_mem_cpy(h, (void *)link_info_t, (void *)t, sizeof(struct btc_traffic)); link_info->stat.rssi = w[i].stat.rssi; rssi_map = 0; /* check if rssi across wl_rssi_thres boundary */ for (j = 0; j < BTC_WL_RSSI_THMAX; j++) { if (module->ant.type == BTC_ANT_SHARED && j == 0) rssi_thres = BTC_WL_RSSI_MAX_BTG; else rssi_thres = btc->chip->wl_rssi_thres[j]; rssi_state = &link_info->rssi_state[j]; *rssi_state = _update_rssi_state(btc, *rssi_state, link_info->stat.rssi, rssi_thres); /* fill rssi bit map 0~3 if rssi < threshold */ if (BTC_RSSI_LOW(*rssi_state)) rssi_map |= BIT(j); if (module->ant.type == BTC_ANT_DEDICATED && BTC_RSSI_CHANGE(*rssi_state)) is_sta_change = true; } /* OR STA/GC role rssi map */ if (link_info->role == PHL_RTYPE_STATION || link_info->role == PHL_RTYPE_P2P_GC) rssi_map_all |= rssi_map; /* set busy once idle->busy immediately */ if (t->tx_lvl != RTW_TFC_IDLE || t->rx_lvl != RTW_TFC_IDLE) { busy = 1; link_info->busy_t = _os_get_cur_time_ms(); if (t->tx_lvl > t->rx_lvl) dir = TRAFFIC_UL; else dir = TRAFFIC_DL; } else {/*set idle if busy -> idle after BTC_BUSY2IDLE_THRES*/ if (phl_get_passing_time_ms(link_info->busy_t) > BTC_BUSY2IDLE_THRES || !wl->role_info.role_map.role.station || wl->role_info.link_mode == BTC_WLINK_NOLINK) { busy = 0; dir = TRAFFIC_DL; } else { busy = link_info->busy; dir = link_info->dir; } } if (link_info->busy != busy || link_info->dir != dir) { is_sta_change = true; link_info->busy = busy; link_info->dir = dir; } /* OR all role busy/dir state */ busy_all |= link_info->busy; dir_all |= BIT(link_info->dir); if (link_info_t->rx_rate <= RTW_DATA_RATE_CCK2 && last_rx_rate > RTW_DATA_RATE_CCK2 && link_info_t->rx_lvl > 0) link_info->rx_rate_drop_cnt++; if (last_tx_rate != link_info_t->tx_rate || last_rx_rate != link_info_t->rx_rate || last_tx_lvl != link_info_t->tx_lvl || last_rx_lvl != link_info_t->rx_lvl ) is_traffic_change = true; wl->role_info.active_role[w[i].pid].tx_lvl = (u16)t->tx_lvl; wl->role_info.active_role[w[i].pid].rx_lvl = (u16)t->rx_lvl; wl->role_info.active_role[w[i].pid].tx_rate = t->tx_rate; wl->role_info.active_role[w[i].pid].rx_rate = t->rx_rate; } wl->rssi_level = 0; for (j = BTC_WL_RSSI_THMAX; j > 0; j--) { /* set RSSI level 4 ~ 0 if rssi bit map match */ if (rssi_map_all & BIT(j-1)) { wl->rssi_level = j; break; } } chk_intvl = BTC_RPT_PERIOD/BTC_PERIODIC_TIME; if (dm->cnt_notify[BTC_NCNT_WL_STA] >= dm->cnt_dm[BTC_DCNT_WL_STA_LAST] + chk_intvl) _get_wl_nhm_dbm(btc); dm->cnt_dm[BTC_DCNT_WL_STA_LAST] = dm->cnt_notify[BTC_NCNT_WL_STA]; /* for TDD/FDD packet estimation in WL FW */ if (is_traffic_change) hal_btc_fw_set_drv_info(btc, CXDRVINFO_ROLE); if (is_sta_change) { wl->status.map.busy = (u32)busy_all; wl->status.map.traffic_dir = (u32)dir_all; _write_scbd(btc, BTC_WSCB_WLBUSY, (bool)(!!busy_all)); _run_coex(btc, __func__); } } static void _ntfy_fwinfo(struct btc_t *btc, u8 *buf, u32 len, u8 cls, u8 func) { struct btf_fwinfo *pfwinfo = &btc->fwinfo; if (!buf || !len) return; btc->dm.cnt_notify[BTC_NCNT_FWINFO]++; pfwinfo->cnt_c2h++; if (cls == BTFC_FW_EVENT) { switch (func) { case BTF_EVNT_RPT: case BTF_EVNT_BUF_OVERFLOW: pfwinfo->event[func]++; hal_btc_fw_event(btc, func, buf, len); break; case BTF_EVNT_BT_INFO: btc->cx.cnt_bt[BTC_BCNT_INFOUPDATE]++; _update_bt_info(btc, buf, len); break; case BTF_EVNT_BT_SCBD: btc->cx.cnt_bt[BTC_BCNT_SCBDUPDATE]++; _update_bt_scbd(btc, false); break; case BTF_EVNT_BT_PSD: _update_bt_psd(btc, buf, len); break; case BTF_EVNT_BT_REG: btc->dbg.rb_done = true; btc->dbg.rb_val = ((buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0])); break; case BTF_EVNT_C2H_LOOPBACK: btc->dbg.rb_done = true; btc->dbg.rb_val = buf[0]; break; case BTF_EVNT_CX_RUNINFO: btc->dm.cnt_dm[BTC_DCNT_CX_RUNINFO]++; _update_offload_runinfo(btc, buf, len); break; } } } static void _ntfy_timer(struct btc_t *btc, u16 tmr_id) { struct btc_dm *dm = &btc->dm; struct btc_cx *cx = &btc->cx; struct btc_wl_info *wl = &cx->wl; struct btc_bt_a2dp_desc *a2dp = &cx->bt.link_info.a2dp_desc; bool is_sta_change = false; PHL_TRACE(COMP_PHL_BTC, _PHL_DEBUG_, "[BTC], %s(): tmr_id =%d\n", __func__, tmr_id); dm->cnt_notify[BTC_NCNT_TIMER]++; if (tmr_id == BTC_TIMER_PERIODIC) { /* start next periodic timer */ _set_btc_timer(btc, BTC_TIMER_PERIODIC, BTC_PERIODIC_TIME); } else if (tmr_id == BTC_TIMER_WL_RFKTO) { if (wl->rfk_info.state != BTC_WRFK_STOP) { cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]++; dm->error.map.wl_rfk_timeout = true; wl->rfk_info.state = BTC_WRFK_STOP; /* _write_scbd(btc, BTC_WSCB_WLRFK, false); */ is_sta_change = true; } } else if (tmr_id == BTC_TIMER_WL_SPECPKT) { wl->status.map._4way = false; wl->status.map.connecting = 0; is_sta_change = true; } else if (tmr_id == BTC_TIMER_BT_A2DPPLAY) { a2dp->play_latency = 0; is_sta_change = true; } if (is_sta_change) _run_coex(btc, __func__); } /****************************************************************************** * * coexistence extern functions * *****************************************************************************/ /* * btc related sw initialization */ bool hal_btc_init(struct btc_t *btc) { switch (btc->hal->chip_id) { #ifdef BTC_8852A_SUPPORT case CHIP_WIFI6_8852A: PHL_INFO("[BTC], %s(): Init 8852A!!\n", __func__); btc->chip = &chip_8852a; break; #endif #ifdef BTC_8852B_SUPPORT case CHIP_WIFI6_8852B: PHL_INFO("[BTC], %s(): Init 8852B!!\n", __func__); btc->chip = &chip_8852b; break; #endif #ifdef BTC_8852C_SUPPORT case CHIP_WIFI6_8852C: PHL_INFO("[BTC], %s(): Init 8852C!!\n", __func__); btc->chip = &chip_8852c; break; #endif default: PHL_ERR("[BTC], %s(): no matched IC!!\n", __func__); btc->cx.wl.status.map.init_ok = false; return false; } _reset_btc_var(btc, BTC_RESET_ALL); _btmr_init(btc); _rsn_cpy(btc->dm.run_reason, "None"); _act_cpy(btc->dm.run_action, "None"); btc->hal->btc_vc.btc_ver = coex_ver; btc->ops = &_btc_ops; btc->mlen = BTC_MSG_MAXLEN; btc->ctrl.igno_bt = true; btc->cx.wl.status.map.init_ok = true; return true; } void hal_btc_deinit(struct btc_t *btc) { _btmr_deinit(btc); } #endif