/******************************************************************************
 *
 * 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"

#ifdef HALBB_DYN_1R_CCA_SUPPORT

void halbb_1r_cca_cr_cfg(struct bb_info *bb, enum rf_path rx_path)
{
	struct bb_dyn_1r_cca_info *dyn_1r_cca = &bb->bb_dyn_1r_cca_i;

	BB_DBG(bb, DBG_IC_API, "[%s]rx_path=%d\n", __func__, rx_path);

	switch (bb->ic_type) {

#ifdef BB_8852A_2_SUPPORT
	case BB_RTL8852A:
		halbb_1r_cca_cr_cfg_8852a_2(bb, rx_path);
		break;
#endif
	default:
		break;
	}
}

void halbb_dyn_1r_cca_cfg(struct bb_info *bb, enum rf_path rx_path)
{
	struct bb_dyn_1r_cca_info *dyn_1r_cca = &bb->bb_dyn_1r_cca_i;

	BB_DBG(bb, DBG_IC_API, "[%s]rx_path=%d\n", __func__, rx_path);

	if (dyn_1r_cca->dyn_1r_cca_cfg == rx_path)
		return;

	dyn_1r_cca->dyn_1r_cca_cfg = rx_path;

	halbb_1r_cca_cr_cfg(bb, rx_path);
}

void halbb_dyn_1r_cca(struct bb_info *bb)
{
	struct bb_dyn_1r_cca_info *dyn_1r_cca = &bb->bb_dyn_1r_cca_i;
	struct bb_link_info *bb_link = &bb->bb_link_i;
	struct rtw_phl_stainfo_t *phl_sta;
	struct rtw_rssi_info *rssi_t = NULL;
	enum rf_path cfg = RF_PATH_AB;
	bool sta_found = false;
	u16 rssi_diff, rssi_min; /*RSSI  u(16,5)*/
	u16 i = 0, j = 0;

	if (!dyn_1r_cca->dyn_1r_cca_en)
		return;

	BB_DBG(bb, DBG_IC_API, "[%s]\n", __func__);

	if (!bb_link->is_linked || !bb_link->is_one_entry_only) {
		BB_DBG(bb, DBG_IC_API, "is_linked=%d, one_entry_only=%d\n",
		       bb_link->is_linked, bb_link->is_one_entry_only);

		halbb_dyn_1r_cca_cfg(bb, RF_PATH_AB);
		return;
	}

	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;

		rssi_t = &phl_sta->hal_sta->rssi_stat;

		for (j = 0; j < HALBB_MAX_PATH; j++) {
			halbb_print_sign_frac_digit(bb, rssi_t->rssi_ma_path[j], 16, 5, bb->dbg_buf, HALBB_SNPRINT_SIZE_S);
			BB_DBG(bb, DBG_IC_API, "[id:%d]rssi[%d]= %s\n",
			       i, j, bb->dbg_buf);
		}
		sta_found = true;
		break;
	}

	if (!sta_found)
		return;

	if (rssi_t->rssi_ma_path[0] >= rssi_t->rssi_ma_path[1]) {
		cfg = RF_PATH_A;
		rssi_diff = rssi_t->rssi_ma_path[0] - rssi_t->rssi_ma_path[1];
		rssi_min = rssi_t->rssi_ma_path[1];
	} else {
		cfg = RF_PATH_B;
		rssi_diff = rssi_t->rssi_ma_path[1] - rssi_t->rssi_ma_path[0];
		rssi_min = rssi_t->rssi_ma_path[0];
	}

	BB_DBG(bb, DBG_IC_API, "[Dyn 1R CCA]en=%d, diff_th=%d, min_th=%d\n",
	       dyn_1r_cca->dyn_1r_cca_en,
	       dyn_1r_cca->dyn_1r_cca_rssi_diff_th >> 5,
	       dyn_1r_cca->dyn_1r_cca_rssi_min_th >> 5);

	if (rssi_diff < dyn_1r_cca->dyn_1r_cca_rssi_diff_th &&
	    rssi_min > dyn_1r_cca->dyn_1r_cca_rssi_min_th) {
		BB_DBG(bb, DBG_IC_API, "[1R_CCA] path=%s\n", (cfg == RF_PATH_A) ? "A" : "B");
	} else {
		BB_DBG(bb, DBG_IC_API, "[2R_CCA]\n");
		cfg = RF_PATH_AB;
	}

	halbb_dyn_1r_cca_cfg(bb, cfg);
}

void halbb_dyn_1r_cca_rst(struct bb_info *bb)
{
	struct bb_dyn_1r_cca_info *dyn_1r_cca = &bb->bb_dyn_1r_cca_i;

	dyn_1r_cca->dyn_1r_cca_cfg = RF_PATH_ABCD;
}

void halbb_dyn_1r_cca_init(struct bb_info *bb)
{
	struct bb_dyn_1r_cca_info *dyn_1r_cca = &bb->bb_dyn_1r_cca_i;

	BB_DBG(bb, DBG_IC_API, "[%s]\n", __func__);

	dyn_1r_cca->dyn_1r_cca_en = false;
	dyn_1r_cca->dyn_1r_cca_cfg = RF_PATH_ABCD;
	dyn_1r_cca->dyn_1r_cca_rssi_diff_th= 10 << 5;
	dyn_1r_cca->dyn_1r_cca_rssi_min_th= 40 << 5;
}

void halbb_dyn_1r_cca_en(struct bb_info *bb, bool en)
{
	struct bb_dyn_1r_cca_info *dyn_1r_cca = &bb->bb_dyn_1r_cca_i;

	dyn_1r_cca->dyn_1r_cca_en = en;
}

void halbb_dyn_1r_cca_dbg(struct bb_info *bb, char input[][16], 
			  u32 *_used, char *output, u32 *_out_len)
{
	struct bb_dyn_1r_cca_info *dyn_1r_cca = &bb->bb_dyn_1r_cca_i;
	u32 val[5] = {0};
	enum rf_path cca_path;

	if (_os_strcmp(input[1], "-h") == 0) {
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			 "en {0/1}\n");
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			 "force {1:A, 2:B, 3:AB}\n");
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			 "diff_th {val}\n");
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			 "min_th {val}\n");

		return;
	}

	HALBB_SCAN(input[2], DCMD_DECIMAL, &val[0]);
	if (_os_strcmp(input[1], "en") == 0) {
		halbb_dyn_1r_cca_en(bb, (bool)val[0]);
	} else if (_os_strcmp(input[1], "force") == 0) {
		halbb_dyn_1r_cca_en(bb, false);

		if (val[0] == 1)
			cca_path = RF_PATH_A;
		else if (val[0] == 2)
			cca_path = RF_PATH_B;
		else if (val[0] == 3)
			cca_path = RF_PATH_AB;
		else
			return;
		halbb_dyn_1r_cca_cfg(bb, cca_path);
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
		    "Force cca_path=%d\n", cca_path);
	} else if (_os_strcmp(input[1], "diff_th") == 0) {
		dyn_1r_cca->dyn_1r_cca_rssi_diff_th= (u16)(val[0] << 5);
	} else if (_os_strcmp(input[1], "min_th") == 0) {
		dyn_1r_cca->dyn_1r_cca_rssi_min_th= (u16)(val[0] << 5);
	} else {
		BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
			"Set Err\n");
	}

	BB_DBG_CNSL(*_out_len, *_used, output + *_used, *_out_len - *_used,
		    "[Dyn 1R CCA]en=%d, diff_th=%d, min_th=%d\n",
		    dyn_1r_cca->dyn_1r_cca_en,
		    dyn_1r_cca->dyn_1r_cca_rssi_diff_th >> 5,
		    dyn_1r_cca->dyn_1r_cca_rssi_min_th >> 5);
}

#endif
