/******************************************************************************
 *
 * Copyright(c) 2016 - 2019 Realtek Corporation. All rights reserved.
 *
 * 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 "halmac_bb_rf_87xx.h"
#include "halmac_87xx_cfg.h"
#include "halmac_common_87xx.h"
#include "halmac_init_87xx.h"

#if HALMAC_87XX_SUPPORT
/**
 * start_dpk_87xx() -trigger FW DPK
 * @adapter : the adapter of halmac
 * Author : Yong-Ching Lin/KaiYuan Chang/Ivan Lin
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
start_dpk_87xx(struct halmac_adapter *adapter)
{
	u8 h2c_buf[H2C_PKT_SIZE_87XX] = { 0 };
	u16 seq_num = 0;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	struct halmac_h2c_header_info hdr_info;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.dpk_state.proc_status;

	if (halmac_fw_validate(adapter) != HALMAC_RET_SUCCESS)
		return HALMAC_RET_NO_DLFW;

	if (adapter->fw_ver.h2c_version < 15)
		return HALMAC_RET_FW_NO_SUPPORT;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_TRACE("[TRACE]Wait event(dpk)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	*proc_status = HALMAC_CMD_PROCESS_SENDING;

	hdr_info.sub_cmd_id = SUB_CMD_ID_DPK;
	hdr_info.content_size = 1;
	hdr_info.ack = 1;
	set_h2c_pkt_hdr_87xx(adapter, h2c_buf, &hdr_info, &seq_num);

	adapter->halmac_state.dpk_state.seq_num = seq_num;

	status = send_h2c_pkt_87xx(adapter, h2c_buf);

	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]send h2c pkt fail!!\n");
		reset_ofld_feature_87xx(adapter, HALMAC_FEATURE_DPK);
		return status;
	}

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * start_iqk_87xx() -trigger FW IQK
 * @adapter : the adapter of halmac
 * @param : IQK parameter
 * Author : KaiYuan Chang/Ivan Lin
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
start_iqk_87xx(struct halmac_adapter *adapter, struct halmac_iqk_para *param)
{
	u8 h2c_buf[H2C_PKT_SIZE_87XX] = { 0 };
	u16 seq_num = 0;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	struct halmac_h2c_header_info hdr_info;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.iqk_state.proc_status;

	if (halmac_fw_validate(adapter) != HALMAC_RET_SUCCESS)
		return HALMAC_RET_NO_DLFW;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_TRACE("[TRACE]Wait event(iqk)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	*proc_status = HALMAC_CMD_PROCESS_SENDING;

	IQK_SET_CLEAR(h2c_buf, param->clear);
	IQK_SET_SEGMENT_IQK(h2c_buf, param->segment_iqk);

	hdr_info.sub_cmd_id = SUB_CMD_ID_IQK;
	hdr_info.content_size = 1;
	hdr_info.ack = 1;
	set_h2c_pkt_hdr_87xx(adapter, h2c_buf, &hdr_info, &seq_num);

	adapter->halmac_state.iqk_state.seq_num = seq_num;

	status = send_h2c_pkt_87xx(adapter, h2c_buf);

	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]send h2c pkt fail!!\n");
		reset_ofld_feature_87xx(adapter, HALMAC_FEATURE_IQK);
		return status;
	}

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * ctrl_pwr_tracking_87xx() -trigger FW power tracking
 * @adapter : the adapter of halmac
 * @opt : power tracking option
 * Author : KaiYuan Chang/Ivan Lin
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
ctrl_pwr_tracking_87xx(struct halmac_adapter *adapter,
		       struct halmac_pwr_tracking_option *opt)
{
	u8 h2c_buf[H2C_PKT_SIZE_87XX] = { 0 };
	u16 seq_num = 0;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	struct halmac_h2c_header_info hdr_info;
	struct halmac_pwr_tracking_para *param;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.pwr_trk_state.proc_status;

	if (halmac_fw_validate(adapter) != HALMAC_RET_SUCCESS)
		return HALMAC_RET_NO_DLFW;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_TRACE("[TRACE]Wait event(pwr tracking)...\n");
		return HALMAC_RET_BUSY_STATE;
	}

	*proc_status = HALMAC_CMD_PROCESS_SENDING;

	PWR_TRK_SET_TYPE(h2c_buf, opt->type);
	PWR_TRK_SET_BBSWING_INDEX(h2c_buf, opt->bbswing_index);

	param = &opt->pwr_tracking_para[HALMAC_RF_PATH_A];
	PWR_TRK_SET_ENABLE_A(h2c_buf, param->enable);
	PWR_TRK_SET_TX_PWR_INDEX_A(h2c_buf, param->tx_pwr_index);
	PWR_TRK_SET_TSSI_VALUE_A(h2c_buf, param->tssi_value);
	PWR_TRK_SET_OFFSET_VALUE_A(h2c_buf, param->pwr_tracking_offset_value);

	param = &opt->pwr_tracking_para[HALMAC_RF_PATH_B];
	PWR_TRK_SET_ENABLE_B(h2c_buf, param->enable);
	PWR_TRK_SET_TX_PWR_INDEX_B(h2c_buf, param->tx_pwr_index);
	PWR_TRK_SET_TSSI_VALUE_B(h2c_buf, param->tssi_value);
	PWR_TRK_SET_OFFSET_VALUE_B(h2c_buf, param->pwr_tracking_offset_value);

	param = &opt->pwr_tracking_para[HALMAC_RF_PATH_C];
	PWR_TRK_SET_ENABLE_C(h2c_buf, param->enable);
	PWR_TRK_SET_TX_PWR_INDEX_C(h2c_buf, param->tx_pwr_index);
	PWR_TRK_SET_TSSI_VALUE_C(h2c_buf, param->tssi_value);
	PWR_TRK_SET_OFFSET_VALUE_C(h2c_buf, param->pwr_tracking_offset_value);

	param = &opt->pwr_tracking_para[HALMAC_RF_PATH_D];
	PWR_TRK_SET_ENABLE_D(h2c_buf, param->enable);
	PWR_TRK_SET_TX_PWR_INDEX_D(h2c_buf, param->tx_pwr_index);
	PWR_TRK_SET_TSSI_VALUE_D(h2c_buf, param->tssi_value);
	PWR_TRK_SET_OFFSET_VALUE_D(h2c_buf, param->pwr_tracking_offset_value);

	hdr_info.sub_cmd_id = SUB_CMD_ID_PWR_TRK;
	hdr_info.content_size = 20;
	hdr_info.ack = 1;
	set_h2c_pkt_hdr_87xx(adapter, h2c_buf, &hdr_info, &seq_num);

	adapter->halmac_state.pwr_trk_state.seq_num = seq_num;

	status = send_h2c_pkt_87xx(adapter, h2c_buf);

	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]send h2c pkt fail!!\n");
		reset_ofld_feature_87xx(adapter, HALMAC_FEATURE_POWER_TRACKING);
		return status;
	}

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_iqk_status_87xx(struct halmac_adapter *adapter,
		    enum halmac_cmd_process_status *proc_status)
{
	*proc_status = adapter->halmac_state.iqk_state.proc_status;

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_dpk_status_87xx(struct halmac_adapter *adapter,
		    enum halmac_cmd_process_status *proc_status, u8 *data,
		    u32 *size)
{
	struct halmac_dpk_state *state = &adapter->halmac_state.dpk_state;

	*proc_status = adapter->halmac_state.dpk_state.proc_status;

	if (!data)
		return HALMAC_RET_NULL_POINTER;

	if (!size)
		return HALMAC_RET_NULL_POINTER;

	if (*proc_status == HALMAC_CMD_PROCESS_DONE) {
		if (*size < state->data_size) {
			*size = state->data_size;
			return HALMAC_RET_BUFFER_TOO_SMALL;
		}

		*size = state->data_size;
		PLTFM_MEMCPY(data, state->data, *size);
	}

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_pwr_trk_status_87xx(struct halmac_adapter *adapter,
			enum halmac_cmd_process_status *proc_status)
{
	*proc_status = adapter->halmac_state.pwr_trk_state.proc_status;

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_psd_status_87xx(struct halmac_adapter *adapter,
		    enum halmac_cmd_process_status *proc_status, u8 *data,
		    u32 *size)
{
	struct halmac_psd_state *state = &adapter->halmac_state.psd_state;

	*proc_status = state->proc_status;

	if (!data)
		return HALMAC_RET_NULL_POINTER;

	if (!size)
		return HALMAC_RET_NULL_POINTER;

	if (*proc_status == HALMAC_CMD_PROCESS_DONE) {
		if (*size < state->data_size) {
			*size = state->data_size;
			return HALMAC_RET_BUFFER_TOO_SMALL;
		}

		*size = state->data_size;
		PLTFM_MEMCPY(data, state->data, *size);
	}

	return HALMAC_RET_SUCCESS;
}

/**
 * psd_87xx() - trigger fw psd
 * @adapter : the adapter of halmac
 * @start_psd : start PSD
 * @end_psd : end PSD
 * Author : KaiYuan Chang/Ivan Lin
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
psd_87xx(struct halmac_adapter *adapter, u16 start_psd, u16 end_psd)
{
	u8 h2c_buf[H2C_PKT_SIZE_87XX] = { 0 };
	u16 seq_num = 0;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	struct halmac_h2c_header_info hdr_info;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.psd_state.proc_status;

	if (halmac_fw_validate(adapter) != HALMAC_RET_SUCCESS)
		return HALMAC_RET_NO_DLFW;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_TRACE("[TRACE]Wait event(psd)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	if (adapter->halmac_state.psd_state.data) {
		PLTFM_FREE(adapter->halmac_state.psd_state.data,
			   adapter->halmac_state.psd_state.data_size);
		adapter->halmac_state.psd_state.data = (u8 *)NULL;
	}

	adapter->halmac_state.psd_state.data_size = 0;
	adapter->halmac_state.psd_state.seg_size = 0;

	*proc_status = HALMAC_CMD_PROCESS_SENDING;

	PSD_SET_START_PSD(h2c_buf, start_psd);
	PSD_SET_END_PSD(h2c_buf, end_psd);

	hdr_info.sub_cmd_id = SUB_CMD_ID_PSD;
	hdr_info.content_size = 4;
	hdr_info.ack = 1;
	set_h2c_pkt_hdr_87xx(adapter, h2c_buf, &hdr_info, &seq_num);

	status = send_h2c_pkt_87xx(adapter, h2c_buf);

	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]send h2c pkt fail!!\n");
		reset_ofld_feature_87xx(adapter, HALMAC_FEATURE_PSD);
		return status;
	}

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_h2c_ack_iqk_87xx(struct halmac_adapter *adapter, u8 *buf, u32 size)
{
	u8 seq_num;
	u8 fw_rc;
	struct halmac_iqk_state *state = &adapter->halmac_state.iqk_state;
	enum halmac_cmd_process_status proc_status;

	seq_num = (u8)H2C_ACK_HDR_GET_H2C_SEQ(buf);
	PLTFM_MSG_TRACE("[TRACE]Seq num : h2c->%d c2h->%d\n",
			state->seq_num, seq_num);
	if (seq_num != state->seq_num) {
		PLTFM_MSG_ERR("[ERR]Seq num mismatch : h2c->%d c2h->%d\n",
			      state->seq_num, seq_num);
		return HALMAC_RET_SUCCESS;
	}

	if (state->proc_status != HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_ERR("[ERR]not cmd sending\n");
		return HALMAC_RET_SUCCESS;
	}

	fw_rc = (u8)H2C_ACK_HDR_GET_H2C_RETURN_CODE(buf);
	state->fw_rc = fw_rc;

	if ((enum halmac_h2c_return_code)fw_rc == HALMAC_H2C_RETURN_SUCCESS) {
		proc_status = HALMAC_CMD_PROCESS_DONE;
		state->proc_status = proc_status;
		PLTFM_EVENT_SIG(HALMAC_FEATURE_IQK, proc_status, NULL, 0);
	} else {
		proc_status = HALMAC_CMD_PROCESS_ERROR;
		state->proc_status = proc_status;
		PLTFM_EVENT_SIG(HALMAC_FEATURE_IQK, proc_status, &fw_rc, 1);
	}

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_h2c_ack_dpk_87xx(struct halmac_adapter *adapter, u8 *buf, u32 size)
{
	u8 seq_num;
	u8 fw_rc;
	struct halmac_dpk_state *state = &adapter->halmac_state.dpk_state;
	enum halmac_cmd_process_status proc_status;

	seq_num = (u8)H2C_ACK_HDR_GET_H2C_SEQ(buf);
	PLTFM_MSG_TRACE("[TRACE]Seq num : h2c->%d c2h->%d\n",
			state->seq_num, seq_num);
	if (seq_num != state->seq_num) {
		PLTFM_MSG_ERR("[ERR]Seq num mismatch : h2c->%d c2h->%d\n",
			      state->seq_num, seq_num);
		return HALMAC_RET_SUCCESS;
	}

	if (state->proc_status != HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_ERR("[ERR]not cmd sending\n");
		return HALMAC_RET_SUCCESS;
	}

	fw_rc = (u8)H2C_ACK_HDR_GET_H2C_RETURN_CODE(buf);
	state->fw_rc = fw_rc;

	if ((enum halmac_h2c_return_code)fw_rc == HALMAC_H2C_RETURN_SUCCESS) {
		proc_status = HALMAC_CMD_PROCESS_RCVD;
		state->proc_status = proc_status;
		PLTFM_EVENT_SIG(HALMAC_FEATURE_DPK, proc_status, NULL, 0);
	} else {
		proc_status = HALMAC_CMD_PROCESS_ERROR;
		state->proc_status = proc_status;
		PLTFM_EVENT_SIG(HALMAC_FEATURE_DPK, proc_status, &fw_rc, 1);
	}

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_dpk_data_87xx(struct halmac_adapter *adapter, u8 *buf, u32 size)
{
	u8 seq_num;
	u8 seg_id;
	u8 seg_size;
	u16 total_size;
	enum halmac_cmd_process_status proc_status;
	struct halmac_dpk_state *state = &adapter->halmac_state.dpk_state;

	seq_num = (u8)DPK_DATA_GET_H2C_SEQ(buf);
	PLTFM_MSG_TRACE("[TRACE]seq num : h2c->%d c2h->%d\n",
			state->seq_num, seq_num);
	if (seq_num != state->seq_num) {
		PLTFM_MSG_ERR("[ERR]seq num mismatch : h2c->%d c2h->%d\n",
			      state->seq_num, seq_num);
		return HALMAC_RET_SUCCESS;
	}

	if (state->proc_status != HALMAC_CMD_PROCESS_RCVD) {
		PLTFM_MSG_ERR("[ERR]not receive dpk ack\n");
		if (state->proc_status != HALMAC_CMD_PROCESS_SENDING) {
			PLTFM_MSG_ERR("[ERR]not cmd sending\n");
			return HALMAC_RET_SUCCESS;
		}
	}

	total_size = (u16)DPK_DATA_GET_TOTAL_SIZE(buf);
	seg_id = (u8)DPK_DATA_GET_SEGMENT_ID(buf);
	seg_size = (u8)DPK_DATA_GET_SEGMENT_SIZE(buf);
	state->data_size = total_size;

	if (!state->data) {
		state->data = (u8 *)PLTFM_MALLOC(state->data_size);
		if (!state->data) {
			PLTFM_MSG_ERR("[ERR]malloc fail!!\n");
			return HALMAC_RET_NULL_POINTER;
		}
	}

	if (seg_id == 0)
		state->seg_size = seg_size;

	PLTFM_MEMCPY(state->data + seg_id * state->seg_size,
		     buf + C2H_DATA_OFFSET_87XX, seg_size);

	if (DPK_DATA_GET_END_SEGMENT(buf) == 0)
		return HALMAC_RET_SUCCESS;

	proc_status = HALMAC_CMD_PROCESS_DONE;
	state->proc_status = proc_status;

	PLTFM_EVENT_SIG(HALMAC_FEATURE_DPK, state->proc_status, state->data,
			state->data_size);

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_h2c_ack_pwr_trk_87xx(struct halmac_adapter *adapter, u8 *buf, u32 size)
{
	u8 seq_num;
	u8 fw_rc;
	struct halmac_pwr_tracking_state *state;
	enum halmac_cmd_process_status proc_status;

	state = &adapter->halmac_state.pwr_trk_state;

	seq_num = (u8)H2C_ACK_HDR_GET_H2C_SEQ(buf);
	PLTFM_MSG_TRACE("[TRACE]Seq num : h2c->%d c2h->%d\n",
			state->seq_num, seq_num);
	if (seq_num != state->seq_num) {
		PLTFM_MSG_ERR("[ERR]Seq num mismatch : h2c->%d c2h->%d\n",
			      state->seq_num, seq_num);
		return HALMAC_RET_SUCCESS;
	}

	if (state->proc_status != HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_ERR("[ERR]not cmd sending\n");
		return HALMAC_RET_SUCCESS;
	}

	fw_rc = (u8)H2C_ACK_HDR_GET_H2C_RETURN_CODE(buf);
	state->fw_rc = fw_rc;

	if ((enum halmac_h2c_return_code)fw_rc == HALMAC_H2C_RETURN_SUCCESS) {
		proc_status = HALMAC_CMD_PROCESS_DONE;
		state->proc_status = proc_status;
		PLTFM_EVENT_SIG(HALMAC_FEATURE_POWER_TRACKING, proc_status,
				NULL, 0);
	} else {
		proc_status = HALMAC_CMD_PROCESS_ERROR;
		state->proc_status = proc_status;
		PLTFM_EVENT_SIG(HALMAC_FEATURE_POWER_TRACKING, proc_status,
				&fw_rc, 1);
	}

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_psd_data_87xx(struct halmac_adapter *adapter, u8 *buf, u32 size)
{
	u8 seg_id;
	u8 seg_size;
	u8 seq_num;
	u16 total_size;
	enum halmac_cmd_process_status proc_status;
	struct halmac_psd_state *state = &adapter->halmac_state.psd_state;

	seq_num = (u8)PSD_DATA_GET_H2C_SEQ(buf);
	PLTFM_MSG_TRACE("[TRACE]seq num : h2c->%d c2h->%d\n",
			state->seq_num, seq_num);
	if (seq_num != state->seq_num) {
		PLTFM_MSG_ERR("[ERR]seq num mismatch : h2c->%d c2h->%d\n",
			      state->seq_num, seq_num);
		return HALMAC_RET_SUCCESS;
	}

	if (state->proc_status != HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_ERR("[ERR]not cmd sending\n");
		return HALMAC_RET_SUCCESS;
	}

	total_size = (u16)PSD_DATA_GET_TOTAL_SIZE(buf);
	seg_id = (u8)PSD_DATA_GET_SEGMENT_ID(buf);
	seg_size = (u8)PSD_DATA_GET_SEGMENT_SIZE(buf);
	state->data_size = total_size;

	if (!state->data)
		state->data = (u8 *)PLTFM_MALLOC(state->data_size);

	if (seg_id == 0)
		state->seg_size = seg_size;

	PLTFM_MEMCPY(state->data + seg_id * state->seg_size,
		     buf + C2H_DATA_OFFSET_87XX, seg_size);

	if (PSD_DATA_GET_END_SEGMENT(buf) == 0)
		return HALMAC_RET_SUCCESS;

	proc_status = HALMAC_CMD_PROCESS_DONE;
	state->proc_status = proc_status;

	PLTFM_EVENT_SIG(HALMAC_FEATURE_PSD, proc_status, state->data,
			state->data_size);

	return HALMAC_RET_SUCCESS;
}

#endif /* HALMAC_87XX_SUPPORT */
