/******************************************************************************
 *
 * Copyright(c) 2007 - 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.
 *
 * The full GNU General Public License is included in this distribution in the
 * file called LICENSE.
 *
 * Contact Information:
 * wlanfae <wlanfae@realtek.com>
 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
 * Hsinchu 300, Taiwan.
 *
 * Larry Finger <Larry.Finger@lwfinger.net>
 *
 *****************************************************************************/
#include "halbb_precomp.h"

void halbb_supportability_dbg(struct bb_info *bb, char input[][16], u32 *_used,
			     char *output, u32 *_out_len)
{
	u32 val[10] = {0};
	u64 pre_support_ability, one = 1;
	u64 comp = 0;
	u32 used = *_used;
	u32 out_len = *_out_len;
	u8 i;

	for (i = 0; i < 5; i++) {
		if (input[i + 1])
			HALBB_SCAN(input[i + 1], DCMD_DECIMAL, &val[i]);
	}

	pre_support_ability = bb->support_ability;
	comp = bb->support_ability;

	BB_DBG_CNSL(out_len, used, output + used, out_len - used,
		 "\n================================\n");

	if (val[0] == 100) {
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "[Supportability] Selection\n");
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "================================\n");
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "00. (( %s ))RA\n",
			 ((comp & BB_RA) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "01. (( %s ))FA_CNT\n",
			 ((comp & BB_FA_CNT) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "02. (( %s ))RSSI_MNTR\n",
			 ((comp & HALBB_FUN_RSVD_2) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "03. (( %s ))DFS\n",
			 ((comp & BB_DFS) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "04. (( %s ))EDCCA\n",
			 ((comp & BB_EDCCA) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "05. (( %s ))ENV_MNTR\n",
			 ((comp & BB_ENVMNTR) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "06. (( %s ))CFO_TRK\n",
			 ((comp & BB_CFO_TRK) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "07. (( %s ))PWR_CTRL\n",
			 ((comp & BB_PWR_CTRL) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "10. (( %s ))ANT_DIV\n",
			 ((comp & DBG_ANT_DIV) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "11. (( %s ))DIG\n",
			 ((comp & BB_DIG) ? ("V") : (".")));
		
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "31. (( %s ))Dyn CSI RSP\n",
			 ((comp & BB_DCR) ? ("V") : (".")));
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "================================\n");

	} else if (val[0] == 101) {
		bb->support_ability = 0;
		BB_DBG_CNSL(out_len, used, output + used, out_len - used,
			 "Disable all support_ability components\n");
	} else {
		if (val[1] == 1) { /* @enable */
			bb->support_ability |= (one << val[0]);
		} else if (val[1] == 2) {/* @disable */
			bb->support_ability &= ~(one << val[0]);
		} else {
			BB_DBG_CNSL(out_len, used, output + used, out_len - used,
				 "[Warning!!!]  1:enable,  2:disable\n");
		}
	}
	BB_DBG_CNSL(out_len, used, output + used, out_len - used,
		 "pre-supportability = 0x%llx\n", pre_support_ability);
	BB_DBG_CNSL(out_len, used, output + used, out_len - used,
		 "Cur-supportability = 0x%llx\n", bb->support_ability);
	BB_DBG_CNSL(out_len, used, output + used, out_len - used,
		 "================================\n");

	*_used = used;
	*_out_len = out_len;
}

bool halbb_sta_info_init(struct bb_info *bb,
			 struct rtw_phl_stainfo_t *phl_sta_info)
{
	struct bb_sta_info *bb_sta;

	if (!bb) {
		BB_WARNING("[%s]*bb = NULL\n", __func__);
		return false;
	}

	if (!phl_sta_info)
		return false;

	if (!phl_sta_info->hal_sta)
		return false;

	bb_sta = halbb_mem_alloc(bb, sizeof(struct bb_sta_info));

	if (!bb_sta) {
		BB_WARNING("*bb_sta = NULL\n");
		return RTW_HAL_STATUS_BB_INIT_FAILURE;
	}
	phl_sta_info->hal_sta->bb_sta = (void *)bb_sta;
	
	return true;
}

bool halbb_sta_info_deinit(struct bb_info *bb,
			   struct rtw_phl_stainfo_t *phl_sta_info)
{
	if (!bb) {
		BB_WARNING("*bb = NULL\n");
		return false;
	}

	if (!phl_sta_info)
		return false;

	if (!phl_sta_info->hal_sta)
		return false;

	if (!phl_sta_info->hal_sta->bb_sta)
		return false;

	halbb_mem_free(bb, phl_sta_info->hal_sta->bb_sta, sizeof(struct bb_sta_info));
	return true;
}

bool halbb_sta_info_add_entry(struct bb_info *bb,
				      struct rtw_phl_stainfo_t *phl_sta_info)
{
	if (!bb) {
		BB_WARNING("[%s]*bb = NULL\n", __func__);
		return false;
	}

	if (!phl_sta_info)
		return false;

	if ((phl_sta_info->macid) >= PHL_MAX_STA_NUM)
		return false;

	bb->phl2bb_macid_table[phl_sta_info->macid] = (u8)phl_sta_info->macid;
	bb->phl_sta_info[phl_sta_info->macid] = phl_sta_info;

	return true;
}

bool halbb_sta_info_delete_entry(struct bb_info *bb,
					struct rtw_phl_stainfo_t *phl_sta_info)
{
	if (!bb) {
		BB_WARNING("*bb = NULL\n");
		return false;
	}

	if (!phl_sta_info)
		return false;

	if ((phl_sta_info->macid) >= PHL_MAX_STA_NUM)
		return false;

	if (!phl_sta_info->hal_sta)
			return false;

	if (!phl_sta_info->hal_sta->bb_sta)
		return false;

	bb->sta_exist[phl_sta_info->macid] = false;
	bb->phl_sta_info[phl_sta_info->macid] = NULL;

	return true;
}

void halbb_media_status_update(struct bb_info *bb,
			       struct rtw_phl_stainfo_t *phl_sta_info,
			       bool is_connected)
{
	bb->sta_exist[phl_sta_info->macid] = is_connected;

	/*Reset MA RSSI*/
	if (!is_connected) {
		phl_sta_info->hal_sta->rssi_stat.rssi = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_ma = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_ma_path[0] = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_ma_path[1] = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_ma_path[2] = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_ma_path[3] = 0;
		phl_sta_info->hal_sta->rssi_stat.pkt_cnt_data = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_bcn = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_bcn_ma = 0;
		phl_sta_info->hal_sta->rssi_stat.pkt_cnt_bcn = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_ofdm = 0;
		phl_sta_info->hal_sta->rssi_stat.rssi_cck = 0;
		phl_sta_info->hal_sta->rssi_stat.snr_ma = 0;
	} else {
		phl_sta_info->hal_sta->rssi_stat.ma_factor = RSSI_MA_L;
		phl_sta_info->hal_sta->rssi_stat.ma_factor_bcn = RSSI_MA_L;
	}
}

void halbb_sta_info_dbg(struct bb_info *bb, char input[][16], u32 *_used,
			char *output, u32 *_out_len)
{
	struct rtw_hal_com_t *hal = bb->hal_com;
	struct rtw_phl_stainfo_t *phl_sta;
	struct rtw_rssi_info *rssi_t = NULL;
	struct rtw_ra_sta_info	*ra;
	char dbg_buf[HALBB_SNPRINT_SIZE] = {0};
	u32 val[10] = {0};
	u32 tmp = 0;
	u16 curr_tx_rt = 0;
	u8 i = 0, j = 0;

	if (_os_strcmp(input[1], "-h") == 0) {
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			 "all\n");
		return;
	}	

	if (_os_strcmp(input[1], "all") == 0) {

		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			 " Assoc_sta_cnt=%d\n\n", hal->assoc_sta_cnt);
		for (i = 0; i < PHL_MAX_STA_NUM; i++) {
			if (!bb->sta_exist[i])
				continue;

			phl_sta = bb->phl_sta_info[i];

			if (!is_sta_active(phl_sta))
				continue;

			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "[%d][active=%d] PHL_macid=%d =====================\n",
				    i, phl_sta->active, phl_sta->macid);

			rssi_t = &phl_sta->hal_sta->rssi_stat;

			halbb_print_sign_frac_digit(bb, rssi_t->rssi_ma, 16, 5, dbg_buf, HALBB_SNPRINT_SIZE_S);
			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "[Data] rssi_avg=%s, MA=1/%02d\n",
				    dbg_buf, 1 << rssi_t->ma_factor);

			for (j = 0; j < HALBB_MAX_PATH; j++) {
				halbb_print_sign_frac_digit(bb, rssi_t->rssi_ma_path[j], 16, 5, dbg_buf, HALBB_SNPRINT_SIZE_S);
				BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
					    "       rssi[%d]= %s\n", j, dbg_buf);
			}

			halbb_print_sign_frac_digit(bb, rssi_t->rssi_bcn_ma, 16, 5, dbg_buf, HALBB_SNPRINT_SIZE_S);
			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "[Bcn]  rssi_avg=%s, MA=1/%02d\n",
				    dbg_buf, 1 << rssi_t->ma_factor_bcn);

			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "rssi_cck=%02d.%d, rssi_ofdm=%02d.%d\n",
				    rssi_t->rssi_cck >> 1, (rssi_t->rssi_cck & 1) * 5,
				    rssi_t->rssi_ofdm >> 1, (rssi_t->rssi_ofdm & 1) * 5);

			halbb_print_sign_frac_digit(bb, rssi_t->snr_ma, 16, 4, dbg_buf, HALBB_SNPRINT_SIZE_S);
			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "SNR_avg=%s dB\n", dbg_buf);

			ra = &phl_sta->hal_sta->ra_info;
			curr_tx_rt = (u16)(ra->rpt_rt_i.mcs_ss_idx) | ((u16)(ra->rpt_rt_i.mode) << 7);

			halbb_print_rate_2_buff(bb, curr_tx_rt, ra->rpt_rt_i.gi_ltf, bb->dbg_buf, HALBB_SNPRINT_SIZE);
			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "Tx_Rate=%s (0x%x-%d), PER=(%d), TXBW=(%d)\n",
				    bb->dbg_buf, curr_tx_rt, ra->rpt_rt_i.gi_ltf,
				    ra->curr_retry_ratio, (20 << ra->rpt_rt_i.bw));

			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "======================================\n");

		}
	}
}

void halbb_traffic_load_decision(struct bb_info *bb)
{
	struct rtw_stats *stat = &bb->phl_com->phl_stats;
	struct bb_link_info *link = &bb->bb_link_i;
	u32 max_tp; /*Mbps*/

	/*@---TP & Trafic-load caln---*/
	link->tx_tp = KB_2_MB(stat->tx_tp_kbits);
	link->rx_tp = KB_2_MB(stat->rx_tp_kbits);
	link->total_tp = link->tx_tp + link->rx_tp;
	max_tp = MAX_2(link->tx_tp, link->rx_tp); /*Mbps*/

	BB_DBG(bb, DBG_COMMON_FLOW,
		"byte_uni{tx,rx}={%llu,%llu}, byte_total{tx,rx}={%llu,%llu}\n", 
		stat->tx_byte_uni, stat->rx_byte_uni,
		stat->tx_byte_total, stat->rx_byte_total);

	BB_DBG(bb, DBG_COMMON_FLOW,
		"TP_kbit{tx,rx}={%d,%d}, TP_MA{tx,rx}={%d,%d}\n", 
		stat->tx_tp_kbits, stat->rx_tp_kbits,
		stat->tx_moving_average_tp, stat->rx_moving_average_tp);

	/*@[Calculate TX/RX state]*/
	if (link->tx_tp > (link->rx_tp << 1))
		link->txrx_state_all = BB_TX_STATE;
	else if (link->rx_tp > (link->tx_tp << 1))
		link->txrx_state_all = BB_RX_STATE;
	else
		link->txrx_state_all = BB_BI_DIR_STATE;

	/*@[Traffic load decision]*/
	link->traffic_load_pre = link->traffic_load;

	if (max_tp > 20) {
		link->traffic_load = TRAFFIC_HIGH;
	} else if (max_tp > 5) {
		link->traffic_load = TRAFFIC_MID;
	} else if (max_tp > 1) {
		link->traffic_load = TRAFFIC_LOW;
	} else if (stat->tx_tp_kbits > 100 || stat->rx_tp_kbits > 100) { /*100Kb*/
		link->traffic_load = TRAFFIC_ULTRA_LOW;
	} else {
		link->traffic_load = TRAFFIC_NO_TP;
	}

	/*@[Calculate consecutive idlel time]*/
	if (link->traffic_load == TRAFFIC_NO_TP)
		link->consecutive_idle_time = 0;
	else
		link->consecutive_idle_time += HALBB_WATCHDOG_PERIOD;
}

void halbb_cmn_info_self_reset(struct bb_info *bb) {

	struct bb_link_info *link = &bb->bb_link_i;

	bb->bb_ch_i.rssi_max = 0;
	bb->bb_ch_i.rssi_min = 0;

	link->is_one_entry_only = false;
	link->one_entry_macid = 0;
	link->one_entry_tp = 0;
	link->one_entry_tp_active_occur = false;
	link->one_entry_tp_pre = 0;
	link->num_linked_client_pre = 0;
	link->num_active_client_pre = 0;
	link->num_linked_client = 0;
	link->num_active_client = 0;
}

u8 halbb_get_rssi_min(struct bb_info *bb)
{
	struct rtw_hal_com_t *hal = bb->hal_com;
	struct rtw_phl_stainfo_t *sta;
	struct rtw_rssi_info *sta_rssi = NULL;
	u8 sta_cnt = 0;
	u8 rssi_min = 0xff, rssi_curr = 0;
	u32 i = 0;

	if (hal->assoc_sta_cnt == 0) {
		BB_WARNING("[%s] assoc_sta_cnt=0\n", __func__);
		return 0;
	}

	for (i = 0; i < PHL_MAX_STA_NUM; i++) {
		if (!bb->sta_exist[i])
			continue;

		sta = bb->phl_sta_info[i];

		if (!is_sta_active(sta))
			continue;

		BB_DBG(bb, DBG_COMMON_FLOW, "[%d] macid=%d\n", i, sta->macid);

		sta_cnt++;

		sta_rssi = &sta->hal_sta->rssi_stat;

		if (sta_rssi->rssi != 0)
			rssi_curr = sta_rssi->rssi;
		else
			rssi_curr = sta_rssi->rssi_bcn;

		/*[RSSI min]*/
		if (rssi_curr <= rssi_min) {
			rssi_min = rssi_curr;
		}

		BB_DBG(bb, DBG_COMMON_FLOW,
		       "rssi_min = %d", rssi_min);
		
		if (sta_cnt >= hal->assoc_sta_cnt)
			break;
	}

	if (sta_cnt == 0) {
		BB_WARNING("[%s] sta_cnt=0\n", __func__);
		return 0;
	}

	return rssi_min;
}

void halbb_cmn_info_self_update(struct bb_info *bb)
{
	struct rtw_hal_com_t *hal = bb->hal_com;
	struct bb_link_info *link = &bb->bb_link_i;
	struct rtw_phl_com_t *phl = bb->phl_com;
	struct dev_cap_t *dev = &phl->dev_cap;
	struct rtw_phl_stainfo_t *sta;
	struct rtw_rssi_info *sta_rssi = NULL;
	struct halbb_mcc_dm *mcc_dm = &bb->mcc_dm;
	u8 sta_cnt = 0, num_active_client = 0;
	u8 rssi_min = 0xff, rssi_max = 0, rssi_curr = 0;
	u8 mcc_rssi_min[MCC_BAND_NUM], mcc_sta_cnt[MCC_BAND_NUM];
	u8 j = 0, band_idx = MCC_BAND_NUM, role_ch = 0;
	u32 i = 0, one_entry_macid_tmp = 0;
	u32 trx_tp = 0;
	u32 tp_diff = 0;

	/*[Link Status Check]*/
	link->is_linked = (hal->assoc_sta_cnt != 0) ? true : false;
	link->first_connect = link->is_linked && !link->is_linked_pre;
	link->first_disconnect = !link->is_linked && link->is_linked_pre;
	link->is_linked_pre = link->is_linked;

	BB_DBG(bb, DBG_COMMON_FLOW, "is_linked = %d, 1st_connect=%d, 1st_disconnect=%d, assoc_sta_cnt=%d\n",
	       link->is_linked, link->first_connect,
	       link->first_disconnect, hal->assoc_sta_cnt);

	/*[Traffic load information]*/
	halbb_traffic_load_decision(bb);

	link->rx_rate_plurality = halbb_get_plurality_rx_rate_su(bb);
	link->rx_rate_plurality_mu = halbb_get_plurality_rx_rate_mu(bb);

	if (!link->is_linked) {
		if (link->first_disconnect)
			halbb_cmn_info_self_reset(bb);

		return;
	}

	if (mcc_dm->mcc_status_en) {
		for (i = 0; i < MCC_BAND_NUM; i++) {
			mcc_rssi_min[i] = rssi_min;
			mcc_sta_cnt[i] = 0;
		}
	}

	bb->bb_ch_i.pre_rssi_min = bb->bb_ch_i.rssi_min;

	for (i = 0; i < PHL_MAX_STA_NUM; i++) {
		if (!bb->sta_exist[i])
			continue;

		sta = bb->phl_sta_info[i];

		if (!is_sta_active(sta))
			continue;

		if ((dev->rfe_type >= 50) && (sta->macid == 0))
			continue;

		BB_DBG(bb, DBG_COMMON_FLOW, "[%d] macid=%d\n", i, sta->macid);

		sta_cnt++;

		if (sta_cnt == 1)
			one_entry_macid_tmp = i;

		trx_tp = KB_2_MB(sta->stats.tx_tp_kbits +
				 sta->stats.rx_tp_kbits); /*Mbit*/
		
		sta_rssi = &sta->hal_sta->rssi_stat;

		if (bb->bb_watchdog_mode != BB_WATCHDOG_NORMAL) {
			rssi_curr = sta_rssi->rssi_bcn;
		} else {
			if (sta_rssi->rssi == 0 && sta_rssi->rssi_bcn != 0)
				rssi_curr = sta_rssi->rssi_bcn;
			else
				rssi_curr = sta_rssi->rssi;
		}

		if (sta_rssi->pkt_cnt_data > 100)
			sta_rssi->ma_factor = RSSI_MA_H;
		else if (sta_rssi->pkt_cnt_data > 20)
			sta_rssi->ma_factor = RSSI_MA_M;
		else if (sta_rssi->pkt_cnt_data > 5)
			sta_rssi->ma_factor = RSSI_MA_L;
		else
			sta_rssi->ma_factor = RSSI_MA_UL;

		if (sta_rssi->pkt_cnt_bcn > 5)
			sta_rssi->ma_factor_bcn = RSSI_MA_L;
		else
			sta_rssi->ma_factor_bcn = RSSI_MA_UL;

		BB_DBG(bb, DBG_COMMON_FLOW,
		       "pkt_cnt_data=%d, pkt_cnt_bcn=%d, ma_factor=%d, ma_factor_bcn=%d\n",
		       sta_rssi->pkt_cnt_data, sta_rssi->pkt_cnt_bcn,
		       sta_rssi->ma_factor, sta_rssi->ma_factor_bcn);

		sta_rssi->pkt_cnt_data = 0;
		sta_rssi->pkt_cnt_bcn = 0;

		BB_DBG(bb, DBG_COMMON_FLOW,
		       "rssi = %d, rssi_ma = %d",
		       sta->hal_sta->rssi_stat.rssi,
		       sta->hal_sta->rssi_stat.rssi_ma);

		/*[RSSI min]*/
		if (rssi_curr <= rssi_min) {
			bb->bb_ch_i.rssi_min = rssi_curr;
			bb->bb_ch_i.rssi_min_macid = sta->macid;
			rssi_min = rssi_curr;
		}
		/*[RSSI max]*/
		if (rssi_curr >= rssi_max) {
			bb->bb_ch_i.rssi_max = rssi_curr;
			bb->bb_ch_i.rssi_max_macid = sta->macid;
			rssi_max = rssi_curr;
		}

		//BB_DBG(bb, DBG_COMMON_FLOW, "TP: TRX=%d Mb/sec\n", trx_tp);

		//BB_DBG(bb, DBG_COMMON_FLOW, "TP: TX=%d, RX=%d, kb/sec\n",
		//       sta->stats.tx_tp_kbits, sta->stats.rx_tp_kbits);

		BB_DBG(bb, DBG_COMMON_FLOW,
		       "rssi_min = %d, rssi_max = %d", rssi_min, rssi_max);

		if (mcc_dm->mcc_status_en) {
			if (i == mcc_dm->softap_macid)
				continue;

			band_idx = MCC_BAND_NUM;
			role_ch = sta->wrole->chandef.center_ch;

			for (j = 0; j < MCC_BAND_NUM; j++) {
				if (mcc_dm->mcc_rf_ch[j].center_ch == role_ch) {
					band_idx = j;
					break;
				}
			}

			if (band_idx == MCC_BAND_NUM) {
				BB_WARNING("%s, band_idx = %d", __func__,
					   band_idx);
				continue;
			}

			if (rssi_curr <= mcc_rssi_min[band_idx])
				mcc_rssi_min[band_idx] = rssi_curr;

			mcc_sta_cnt[band_idx]++;
		}

		if (trx_tp > ACTIVE_TP_THRESHOLD)
			num_active_client++;
		
		if (sta_cnt >= bb->hal_com->assoc_sta_cnt)
			break;
	}

	if (mcc_dm->mcc_status_en) {
		for (i = 0; i < MCC_BAND_NUM; i++) {
			mcc_dm->rssi_min[i] = mcc_rssi_min[i];
			mcc_dm->sta_cnt[i] = mcc_sta_cnt[i];
		}
	}


	link->is_one_entry_only = (hal->assoc_sta_cnt == 1) ? true : false;

	if (link->is_one_entry_only) {
		link->one_entry_macid = one_entry_macid_tmp;
		link->one_entry_tp = trx_tp;
		link->one_entry_tp_active_occur = false;

		//BB_DBG(bb, DBG_COMMON_FLOW, "one_entry_tp=((%d)), one_entry_tp_pre=((%d))\n",
		//	  link->one_entry_tp, link->one_entry_tp_pre);

		if (link->one_entry_tp > link->one_entry_tp_pre &&
		    link->one_entry_tp_pre <= 2) {
			tp_diff = link->one_entry_tp - link->one_entry_tp_pre;

			if (tp_diff > link->tp_active_th)
				link->one_entry_tp_active_occur = true;
		}

		link->one_entry_tp_pre = link->one_entry_tp;
	}

	link->num_linked_client_pre = link->num_linked_client;
	link->num_active_client_pre = link->num_active_client;
	link->num_linked_client = sta_cnt;
	link->num_active_client = num_active_client;
}

void halbb_watchdog_reset(struct bb_info *bb)
{

}

void halbb_update_hal_info(struct bb_info *bb)
{
	struct rtw_hal_com_t *hal = bb->hal_com;

	hal->trx_stat.rx_rate_plurality = bb->bb_link_i.rx_rate_plurality;
}

void halbb_store_data(struct bb_info *bb)
{
	halbb_cmn_info_rpt_store_data(bb);
}

void halbb_reset(struct bb_info *bb)
{
	halbb_store_data(bb);
	#ifdef HALBB_STATISTICS_SUPPORT
	halbb_statistics_reset(bb);
	#endif
	halbb_cmn_info_rpt_reset(bb);
}

void halbb_watchdog_normal(struct bb_info *bb, enum phl_phy_idx phy_idx)
{
	halbb_cmn_info_self_update(bb);
	halbb_ic_hw_setting(bb);
	#ifdef HALBB_ENV_MNTR_SUPPORT
	halbb_env_mntr(bb);
	#endif
	#ifdef HALBB_DIG_SUPPORT
	halbb_dig(bb);
	#endif
	#ifdef HALBB_STATISTICS_SUPPORT
	halbb_statistics(bb);
	#endif
	halbb_basic_dbg_message(bb);
	halbb_physts_watchdog(bb);

	if (!bb->adv_bb_dm_en) {
		BB_DBG(bb, DBG_COMMON_FLOW, "Disable adv halbb dm\n");
		halbb_reset(bb);
		return;
	}

	#ifdef HALBB_EDCCA_SUPPORT
	halbb_edcca(bb);
	#endif
	#ifdef HALBB_DFS_SUPPORT
	halbb_dfs(bb);
	#endif
	#ifdef HALBB_CFO_TRK_SUPPORT
	halbb_cfo_watchdog(bb);
	#endif
	#ifdef HALBB_RA_SUPPORT
	halbb_ra_watchdog(bb);
	#endif
	#ifdef HALBB_PWR_CTRL_SUPPORT
	halbb_pwr_ctrl(bb);
	#endif
	#ifdef HALBB_LA_MODE_SUPPORT
	halbb_la_re_trig_watchdog(bb);
	#endif
	#ifdef HALBB_ANT_DIV_SUPPORT
	halbb_antenna_diversity(bb);
	#endif
	halbb_update_hal_info(bb);
	#ifdef HALBB_DIG_MCC_SUPPORT
	halbb_mccdm_switch(bb);
	#endif
	/*[Rest all counter]*/
	halbb_reset(bb);
}

void halbb_watchdog_low_io(struct bb_info *bb, enum phl_phy_idx phy_idx)
{
	halbb_cmn_info_self_update(bb);
	halbb_ic_hw_setting_low_io(bb);
	halbb_basic_dbg_message(bb);
	#ifdef HALBB_DIG_SUPPORT
	halbb_dig_lps(bb);
	#endif

	#if 0//def HALBB_EDCCA_SUPPORT
	halbb_edcca(bb);
	#endif

	#if 0//def HALBB_DFS_SUPPORT
	halbb_dfs(bb);
	#endif
	/*[Rest all counter]*/
	halbb_reset(bb);
}

void halbb_watchdog_non_io(struct bb_info *bb, enum phl_phy_idx phy_idx)
{
	halbb_cmn_info_self_update(bb);
	halbb_ic_hw_setting_non_io(bb);
	halbb_basic_dbg_message(bb);
	/*[Rest all counter]*/
	halbb_reset(bb);
}

void halbb_watchdog(struct bb_info *bb, enum bb_watchdog_mode_t mode, enum phl_phy_idx phy_idx)
{

#ifdef HALBB_DBCC_SUPPORT
	#ifdef HALBB_DBCC_DVLP_FLAG
	if (phy_idx == HW_PHY_1)
		return;
	#endif
	bb = halbb_get_curr_bb_pointer(bb, phy_idx);
	BB_DBG(bb, DBG_COMMON_FLOW, "[%s] phy_idx=%d\n", __func__, bb->bb_phy_idx);
#endif

	bb->bb_sys_up_time += BB_WATCH_DOG_PERIOD;

	if ((bb->bb_sys_up_time % bb->bb_watchdog_period) != 0)
		return;

	if (bb->bb_dbg_i.cr_recorder_en)
		BB_TRACE("[%s] up_time:%d \n", __func__, bb->bb_sys_up_time);

	bb->bb_watchdog_mode = mode;
	BB_DBG(bb, DBG_COMMON_FLOW, "mode=%s\n",
	       ((mode == BB_WATCHDOG_NORMAL) ? "Normal" :
	       ((mode == BB_WATCHDOG_LOW_IO) ? "LowIO" : "NonIO")));

	/*=== [HALBB Watchdog] ===============================================*/

	if (mode == BB_WATCHDOG_NORMAL) {
		if(phl_is_mp_mode(bb->phl_com)) {
			BB_WARNING("[%s] mode=%d", __func__,
				   bb->phl_com->drv_mode);
			return;
		}
		halbb_watchdog_normal(bb, phy_idx);
	} else if (mode == BB_WATCHDOG_LOW_IO) {
		halbb_watchdog_low_io(bb, phy_idx);
	} else { /*if (mode == BB_WATCHDOG_NON_IO)*/
		halbb_watchdog_non_io(bb, phy_idx);
	}

	if (bb->bb_dbg_i.cr_recorder_en)
		BB_TRACE("[%s] end\n", __func__);
}

void halbb_bb_cmd_notify(struct bb_info *bb, void *bb_cmd, enum phl_phy_idx phy_idx)
{
	enum halbb_event_idx_t event_idx = *((enum halbb_event_idx_t *)bb_cmd);

	BB_DBG(bb, DBG_COMMON_FLOW, "[%s][phy_idx=%d] event_idx=%d\n", __func__, phy_idx, event_idx);

	if (event_idx == BB_EVENT_TIMER_DIG) {
		#ifdef HALBB_DIG_TDMA_SUPPORT
		halbb_tdmadig_io_en(bb);
		#endif
	} else if (event_idx == BB_EVENT_TIMER_CFO) {
		#ifdef HALBB_CFO_TRK_SUPPORT
		halbb_cfo_acc_io_en(bb);
		#endif
	} else if (event_idx == BB_EVENT_TIMER_ANTDIV) {
		#ifdef HALBB_ANT_DIV_SUPPORT
		halbb_antdiv_io_en(bb);
		#endif
	} else if (event_idx == BB_EVENT_TIMER_TDMA_CR) {
		#ifdef HALBB_TDMA_CR_SUPPORT
		halbb_tdma_cr_sel_io_en(bb);
		#endif
	} else {
		BB_WARNING("[%s] event_idx=%d\n", __func__, event_idx);
	}
}

u8 halbb_pause_func(struct bb_info *bb, enum habb_fun_t pause_func,
		    enum halbb_pause_type pause_type,
		    enum halbb_pause_lv_type lv,
		    u8 val_lehgth,
		    u32 *val_buf)
{
	struct bb_func_hooker_info *func_t = &bb->bb_cmn_hooker->bb_func_hooker_i;
	s8 *pause_lv_pre = &bb->u8_dummy;
	u32 *bkp_val = &bb->u32_dummy;
	u32 ori_val[5] = {0};
	u64 pause_func_bitmap = (u64)BIT(pause_func);
	u8 i = 0;
	u8 pause_result = PAUSE_FAIL;

	BB_DBG(bb, DBG_DBG_API, "[%s][%s] LV=%d, Len=%d\n", __func__,
		((pause_type == HALBB_PAUSE) ? "Pause" :
		((pause_type == HALBB_RESUME) ? "Resume" :
		((pause_type == HALBB_PAUSE_NO_SET) ? "Pause no_set" : "Resume_no_recovery"))),
		lv, val_lehgth);

	if (lv >= HALBB_PAUSE_MAX_NUM) {
		BB_WARNING("[%s] LV=%d\n", __func__, lv);
		return PAUSE_FAIL;
	}
	if (pause_func == F_CFO_TRK) {
		BB_DBG(bb, DBG_DBG_API, "[CFO]\n");

		if (val_lehgth > 1) {
			BB_WARNING("CFO length > 1\n");
			return PAUSE_FAIL;
		}

		ori_val[0] = (u32)(bb->bb_cfo_trk_i.crystal_cap); //which value?
		pause_lv_pre = &bb->pause_lv_table.lv_cfo;
		bkp_val = (u32 *)(&bb->bb_cfo_trk_i.rvrt_val);
		/*@function pointer hook*/
		func_t->pause_bb_dm_handler = halbb_set_cfo_pause_val;
	}
#ifdef HALBB_DIG_SUPPORT
	else if (pause_func == F_DIG) {
		BB_DBG(bb, DBG_DBG_API, "[DIG]\n");

		if (val_lehgth > DIG_PAUSE_INFO_SIZE) {
			BB_WARNING("DIG length > %d\n", DIG_PAUSE_INFO_SIZE);
			return PAUSE_FAIL;
		}
		/* {equivalent_rssi, en_pause_by_igi, en_pause_by_pd_low} */
		ori_val[0] = (u32)(RSSI_MAX - bb->bb_dig_i.p_cur_dig_unit->igi_fa_rssi);
		ori_val[1] = val_buf[1];
		pause_lv_pre = &bb->pause_lv_table.lv_dig;
		bkp_val = (u32 *)(&bb->bb_dig_i.rvrt_val);
		/*@function pointer hook*/
		func_t->pause_bb_dm_handler = halbb_set_dig_pause_val;
	}
#endif
#ifdef HALBB_EDCCA_SUPPORT
	else if (pause_func == F_EDCCA) {
		BB_DBG(bb, DBG_DBG_API, "[EDCCA]\n");

		if (val_lehgth > 1) {
			BB_WARNING("EDCCA length > 1\n");
			return PAUSE_FAIL;
		}
		ori_val[0] = (u32)(bb->bb_edcca_i.th_h);
		pause_lv_pre = &bb->pause_lv_table.lv_edcca;
		bkp_val = (u32 *)(&bb->bb_edcca_i.rvrt_val);
		/*@function pointer hook*/
		func_t->pause_bb_dm_handler = halbb_set_edcca_pause_val;
	}
#endif
	else {
		BB_WARNING("Error func idx\n");
		return PAUSE_FAIL;
	}

	BB_DBG(bb, DBG_DBG_API, "Pause_LV{new , pre} = {%d ,%d}\n",
	       lv, *pause_lv_pre);

	if (pause_type == HALBB_PAUSE || pause_type == HALBB_PAUSE_NO_SET) {
		if (lv <= *pause_lv_pre) {
			BB_DBG(bb, DBG_DBG_API, "[PAUSE FAIL] Pre_LV >= Curr_LV\n");
			return PAUSE_FAIL;
		}

		if (!(bb->pause_ability & pause_func_bitmap)) {
			for (i = 0; i < val_lehgth; i++)
				bkp_val[i] = ori_val[i];
		}

		bb->pause_ability |= pause_func_bitmap;
		BB_DBG(bb, DBG_DBG_API, "pause_ability=0x%llx\n", bb->pause_ability);

		if (pause_type == HALBB_PAUSE) {
			for (i = 0; i < val_lehgth; i++)
				BB_DBG(bb, DBG_DBG_API,
				       "[PAUSE SUCCESS] val_idx[%d]{New, Ori}={0x%x, 0x%x}\n",
				       i, val_buf[i], bkp_val[i]);
			
			func_t->pause_bb_dm_handler(bb, val_buf, val_lehgth);
		} else {
			for (i = 0; i < val_lehgth; i++)
				BB_DBG(bb, DBG_DBG_API,
				       "[PAUSE NO Set: SUCCESS] val_idx[%d]{Ori}={0x%x}\n",
				       i, bkp_val[i]);
		}

		*pause_lv_pre = lv;
		pause_result = PAUSE_SUCCESS;
	} else if (pause_type == HALBB_RESUME) {
		if (lv < *pause_lv_pre) {
			BB_DBG(bb, DBG_DBG_API, "[Resume FAIL] Pre_LV >= Curr_LV\n");
			return PAUSE_FAIL;
		}

		if ((bb->pause_ability & pause_func_bitmap) == 0) {
			BB_DBG(bb, DBG_DBG_API, "[RESUME] No Need to Revert\n");
			return PAUSE_SUCCESS;
		}

		bb->pause_ability &= ~pause_func_bitmap;
		BB_DBG(bb, DBG_DBG_API, "pause_ability=0x%llx\n", bb->pause_ability);

		*pause_lv_pre = HALBB_PAUSE_RELEASE;

		for (i = 0; i < val_lehgth; i++) {
			BB_DBG(bb, DBG_DBG_API,
			       "[RESUME] val_idx[%d]={0x%x}\n", i, bkp_val[i]);
		}

		func_t->pause_bb_dm_handler(bb, bkp_val, val_lehgth);

		pause_result = PAUSE_SUCCESS;
	} else if (pause_type == HALBB_RESUME_NO_RECOVERY) {
		if (lv < *pause_lv_pre) {
			BB_DBG(bb, DBG_DBG_API, "[Resume FAIL] Pre_LV >= Curr_LV\n");
			return PAUSE_FAIL;
		}

		if ((bb->pause_ability & pause_func_bitmap) == 0) {
			BB_DBG(bb, DBG_DBG_API, "[RESUME] No Need to Revert\n");
			return PAUSE_SUCCESS;
		}

		bb->pause_ability &= ~pause_func_bitmap;
		BB_DBG(bb, DBG_DBG_API, "pause_ability=0x%llx\n", bb->pause_ability);

		*pause_lv_pre = HALBB_PAUSE_RELEASE;

		pause_result = PAUSE_SUCCESS;
	} else {
		BB_WARNING("error pause_type\n");
		pause_result = PAUSE_FAIL;
	}

	return pause_result;
}

void halbb_pause_func_dbg(struct bb_info *bb, char input[][16], u32 *_used,
			  char *output, u32 *_out_len)
{
	u32 val[10] = {0};
	u32 i;
	u8 len = 0;
	u32 buf[5] = {0};
	u8 pause_result = 0;
	enum halbb_pause_type type = 0;
	enum halbb_pause_lv_type lv = 0;
	u8 halbb_ary_size = bb->bb_cmn_hooker->bb_dm_number;
	enum habb_fun_t id = F_DEFAULT;

	/* ==== [Help] ====]*/
	if (_os_strcmp(input[1], "-h") == 0) {
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			    "{Func} {p:pause, pn:pause_no_set, r:Resume, rnc: Resume_no_recov} {lv:0~3} Val[0],...,Val[5]\n");
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			    "{dig} {p/pn/r} {lv} {Pwr(|dBm|),hex} {0:apply to ofdm, 1:apply to cck and ofdm}\n");
		for (i = 0; i < halbb_ary_size; i++)
			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "*%s\n", halbb_func_i[i].name);
		return;
	}
	/* ==== [Function] ====]*/
	for (i = 0; i < halbb_ary_size; i++) {
		if (_os_strcmp(halbb_func_i[i].name, input[1]) == 0) {
			id = halbb_func_i[i].id;

			BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
				    "[%s]===>\n", halbb_func_i[i].name);
			break;
		}
	}

	if (i == halbb_ary_size) {
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			    "Func not found!\n");
		return;
	}
	/* ==== [Type] ====]*/
	if (_os_strcmp(input[2], "p") == 0) {
		type = HALBB_PAUSE;
	} else if (_os_strcmp(input[2], "pn") == 0) {
		type = HALBB_PAUSE_NO_SET;
	} else if (_os_strcmp(input[2], "r") == 0) {
		type = HALBB_RESUME;
	} else if (_os_strcmp(input[2], "rnc") == 0) {
		type = HALBB_RESUME_NO_RECOVERY;
	} else {
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			    "Set Err\n");
		return;
	}

	for (i = 1; i < 10; i++) {
		HALBB_SCAN(input[i + 1], DCMD_HEX, &val[i]);
	}

	lv = (enum halbb_pause_lv_type)val[2];

	for (i = 0; i < 5; i++)
		buf[i] = val[3 + i];

	if (id == F_CFO_TRK) {
		len = 1;
	} else if (id == F_DIG) {
		len = DIG_PAUSE_INFO_SIZE;
	} else if (id == F_EDCCA) {
		len = 1;
	} else {
		return;
	}

	if (len) {
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			    "{%s in lv=%d}, pause_val = {%d, %d}\n",
			   ((type == HALBB_PAUSE) ? "Pause" :
			   ((type == HALBB_RESUME) ? "Resume" : "Pause no set")),
			   lv, val[3], val[4]);

		pause_result = halbb_pause_func(bb, id, type, lv, len, buf);
	}

	BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
		    "Set %s\n", (pause_result) ? "Success" : "Fail");
}
