/*
 * Copyright (c) 2017 The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
/**
 * DOC: target_if_pmo_lphb.c
 *
 * Target interface file for pmo component to
 * send lphb offload related cmd and process event.
 */
#ifdef FEATURE_WLAN_LPHB

#include "target_if.h"
#include "target_if_pmo.h"
#include "wmi_unified_pmo_api.h"

QDF_STATUS target_if_pmo_send_lphb_enable(struct wlan_objmgr_psoc *psoc,
			struct pmo_lphb_enable_req *ts_lphb_enable)
{
	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
	int status = 0;
	wmi_hb_set_enable_cmd_fixed_param hb_enable_fp;

	if (ts_lphb_enable == NULL) {
		target_if_err("LPHB Enable configuration is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	target_if_info("PMO_HB_SET_ENABLE enable=%d, item=%d, session=%d",
		ts_lphb_enable->enable,
		ts_lphb_enable->item, ts_lphb_enable->session);

	if ((ts_lphb_enable->item != 1) && (ts_lphb_enable->item != 2)) {
		target_if_err("LPHB configuration wrong item %d",
			ts_lphb_enable->item);
		return QDF_STATUS_E_FAILURE;
	}

	/* fill in values */
	hb_enable_fp.vdev_id = ts_lphb_enable->session;
	hb_enable_fp.enable = ts_lphb_enable->enable;
	hb_enable_fp.item = ts_lphb_enable->item;
	hb_enable_fp.session = ts_lphb_enable->session;

	status = wmi_unified_lphb_config_hbenable_cmd(
			GET_WMI_HDL_FROM_PSOC(psoc),
			&hb_enable_fp);
	if (status != EOK) {
		qdf_status = QDF_STATUS_E_FAILURE;
		goto error;
	}
	return QDF_STATUS_SUCCESS;
error:

	return qdf_status;
}

QDF_STATUS target_if_pmo_send_lphb_tcp_params(struct wlan_objmgr_psoc *psoc,
			struct pmo_lphb_tcp_params *ts_lphb_tcp_param)
{
	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
	int status = 0;
	wmi_hb_set_tcp_params_cmd_fixed_param hb_tcp_params_fp = {0};

	if (ts_lphb_tcp_param == NULL) {
		target_if_err("TCP params LPHB configuration is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	target_if_info("PMO --> WMI_HB_SET_TCP_PARAMS srv_ip=%08x, "
		"dev_ip=%08x, src_port=%d, dst_port=%d, timeout=%d, "
		"session=%d, gateway_mac= %pM, time_period_sec=%d,"
		"tcp_sn=%d", ts_lphb_tcp_param->srv_ip,
		ts_lphb_tcp_param->dev_ip, ts_lphb_tcp_param->src_port,
		ts_lphb_tcp_param->dst_port, ts_lphb_tcp_param->timeout,
		ts_lphb_tcp_param->session,
		ts_lphb_tcp_param->gateway_mac.bytes,
		ts_lphb_tcp_param->time_period_sec, ts_lphb_tcp_param->tcp_sn);

	/* fill in values */
	hb_tcp_params_fp.vdev_id = ts_lphb_tcp_param->session;
	hb_tcp_params_fp.srv_ip = ts_lphb_tcp_param->srv_ip;
	hb_tcp_params_fp.dev_ip = ts_lphb_tcp_param->dev_ip;
	hb_tcp_params_fp.seq = ts_lphb_tcp_param->tcp_sn;
	hb_tcp_params_fp.src_port = ts_lphb_tcp_param->src_port;
	hb_tcp_params_fp.dst_port = ts_lphb_tcp_param->dst_port;
	hb_tcp_params_fp.interval = ts_lphb_tcp_param->time_period_sec;
	hb_tcp_params_fp.timeout = ts_lphb_tcp_param->timeout;
	hb_tcp_params_fp.session = ts_lphb_tcp_param->session;
	WMI_CHAR_ARRAY_TO_MAC_ADDR(ts_lphb_tcp_param->gateway_mac.bytes,
				   &hb_tcp_params_fp.gateway_mac);

	status = wmi_unified_lphb_config_tcp_params_cmd(
			GET_WMI_HDL_FROM_PSOC(psoc),
			&hb_tcp_params_fp);
	if (status != EOK) {
		qdf_status = QDF_STATUS_E_FAILURE;
		goto error;
	}

	return QDF_STATUS_SUCCESS;
error:
	return qdf_status;
}

QDF_STATUS target_if_pmo_send_lphb_tcp_pkt_filter(struct wlan_objmgr_psoc *psoc,
			struct pmo_lphb_tcp_filter_req *ts_lphb_tcp_filter)
{
	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
	int status = 0;
	wmi_hb_set_tcp_pkt_filter_cmd_fixed_param hb_tcp_filter_fp = {0};

	if (ts_lphb_tcp_filter == NULL) {
		target_if_err("TCP PKT FILTER LPHB configuration is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	target_if_info("SET_TCP_PKT_FILTER length=%d, offset=%d, session=%d, "
		"filter=%2x:%2x:%2x:%2x:%2x:%2x ...",
		ts_lphb_tcp_filter->length, ts_lphb_tcp_filter->offset,
		ts_lphb_tcp_filter->session, ts_lphb_tcp_filter->filter[0],
		ts_lphb_tcp_filter->filter[1], ts_lphb_tcp_filter->filter[2],
		ts_lphb_tcp_filter->filter[3], ts_lphb_tcp_filter->filter[4],
		ts_lphb_tcp_filter->filter[5]);

	/* fill in values */
	hb_tcp_filter_fp.vdev_id = ts_lphb_tcp_filter->session;
	hb_tcp_filter_fp.length = ts_lphb_tcp_filter->length;
	hb_tcp_filter_fp.offset = ts_lphb_tcp_filter->offset;
	hb_tcp_filter_fp.session = ts_lphb_tcp_filter->session;
	memcpy((void *)&hb_tcp_filter_fp.filter,
	       (void *)&ts_lphb_tcp_filter->filter,
	       WMI_WLAN_HB_MAX_FILTER_SIZE);

	status = wmi_unified_lphb_config_tcp_pkt_filter_cmd(
			GET_WMI_HDL_FROM_PSOC(psoc),
			&hb_tcp_filter_fp);
	if (status != EOK) {
		qdf_status = QDF_STATUS_E_FAILURE;
		goto error;
	}

	return QDF_STATUS_SUCCESS;
error:
	return qdf_status;
}

QDF_STATUS target_if_pmo_send_lphb_udp_params(struct wlan_objmgr_psoc *psoc,
			struct pmo_lphb_udp_params *ts_lphb_udp_param)
{
	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
	int status = 0;
	wmi_hb_set_udp_params_cmd_fixed_param hb_udp_params_fp = {0};

	if (ts_lphb_udp_param == NULL) {
		target_if_err("UDP param for LPHB configuration is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	target_if_info("HB_SET_UDP_PARAMS srv_ip=%d, dev_ip=%d, src_port=%d, "
		"dst_port=%d, interval=%d, timeout=%d, session=%d, "
		"gateway_mac= %pM",
		ts_lphb_udp_param->srv_ip, ts_lphb_udp_param->dev_ip,
		ts_lphb_udp_param->src_port, ts_lphb_udp_param->dst_port,
		ts_lphb_udp_param->interval, ts_lphb_udp_param->timeout,
		ts_lphb_udp_param->session,
		ts_lphb_udp_param->gateway_mac.bytes);

	/* fill in values */
	hb_udp_params_fp.vdev_id = ts_lphb_udp_param->session;
	hb_udp_params_fp.srv_ip = ts_lphb_udp_param->srv_ip;
	hb_udp_params_fp.dev_ip = ts_lphb_udp_param->dev_ip;
	hb_udp_params_fp.src_port = ts_lphb_udp_param->src_port;
	hb_udp_params_fp.dst_port = ts_lphb_udp_param->dst_port;
	hb_udp_params_fp.interval = ts_lphb_udp_param->interval;
	hb_udp_params_fp.timeout = ts_lphb_udp_param->timeout;
	hb_udp_params_fp.session = ts_lphb_udp_param->session;
	WMI_CHAR_ARRAY_TO_MAC_ADDR(ts_lphb_udp_param->gateway_mac.bytes,
				   &hb_udp_params_fp.gateway_mac);

	status = wmi_unified_lphb_config_udp_params_cmd(
			GET_WMI_HDL_FROM_PSOC(psoc),
			&hb_udp_params_fp);
	if (status != EOK) {
		qdf_status = QDF_STATUS_E_FAILURE;
		goto error;
	}

	return QDF_STATUS_SUCCESS;
error:
	return qdf_status;
}

/**
 * target_if_pmo_lphb_send_udp_pkt_filter() - Send LPHB udp pkt filter req
 * @psoc: objmgr psoc handle
 * @ts_lphb_udp_filter: lphb udp filter request which needs to configure in fwr
 *
 * Return: QDF status
 */
QDF_STATUS target_if_pmo_send_lphb_udp_pkt_filter(struct wlan_objmgr_psoc *psoc,
			struct pmo_lphb_udp_filter_req *ts_lphb_udp_filter)
{
	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
	int status = 0;
	wmi_hb_set_udp_pkt_filter_cmd_fixed_param hb_udp_filter_fp = {0};

	if (ts_lphb_udp_filter == NULL) {
		target_if_err("LPHB UDP packet filter configuration is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	target_if_info("SET_UDP_PKT_FILTER length=%d, offset=%d, session=%d, "
		"filter=%2x:%2x:%2x:%2x:%2x:%2x ...",
		ts_lphb_udp_filter->length, ts_lphb_udp_filter->offset,
		ts_lphb_udp_filter->session, ts_lphb_udp_filter->filter[0],
		ts_lphb_udp_filter->filter[1], ts_lphb_udp_filter->filter[2],
		ts_lphb_udp_filter->filter[3], ts_lphb_udp_filter->filter[4],
		ts_lphb_udp_filter->filter[5]);

	/* fill in values */
	hb_udp_filter_fp.vdev_id = ts_lphb_udp_filter->session;
	hb_udp_filter_fp.length = ts_lphb_udp_filter->length;
	hb_udp_filter_fp.offset = ts_lphb_udp_filter->offset;
	hb_udp_filter_fp.session = ts_lphb_udp_filter->session;
	qdf_mem_copy((void *)&hb_udp_filter_fp.filter,
	       (void *)&ts_lphb_udp_filter->filter,
	       WMI_WLAN_HB_MAX_FILTER_SIZE);

	status = wmi_unified_lphb_config_udp_pkt_filter_cmd(
			GET_WMI_HDL_FROM_PSOC(psoc),
			&hb_udp_filter_fp);
	if (status != EOK) {
		qdf_status = QDF_STATUS_E_FAILURE;
		goto error;
	}

	return QDF_STATUS_SUCCESS;
error:
	return qdf_status;
}

QDF_STATUS target_if_pmo_lphb_evt_handler(struct wlan_objmgr_psoc *psoc,
		uint8_t *event)
{
	wmi_hb_ind_event_fixed_param *hb_fp;
	struct pmo_lphb_rsp *slphb_indication = NULL;
	QDF_STATUS qdf_status;

	TARGET_IF_ENTER();
	if (!psoc) {
		target_if_err("psoc ptr is NULL");
		qdf_status = QDF_STATUS_E_NULL_VALUE;
		goto out;
	}

	hb_fp = (wmi_hb_ind_event_fixed_param *) event;
	if (!hb_fp) {
		target_if_err("Invalid wmi_hb_ind_event_fixed_param buffer");
		qdf_status = QDF_STATUS_E_INVAL;
		goto out;
	}

	target_if_debug("lphb indication received with\n"
		  "vdev_id=%d, session=%d, reason=%d",
		hb_fp->vdev_id, hb_fp->session, hb_fp->reason);

	slphb_indication = (struct pmo_lphb_rsp *)qdf_mem_malloc(
				sizeof(struct pmo_lphb_rsp));

	if (!slphb_indication) {
		target_if_err("Invalid LPHB indication buffer");
		qdf_status = QDF_STATUS_E_NOMEM;
		goto out;
	}

	slphb_indication->session_idx = hb_fp->session;
	slphb_indication->protocol_type = hb_fp->reason;
	slphb_indication->event_reason = hb_fp->reason;

	qdf_status = pmo_tgt_lphb_rsp_evt(psoc, slphb_indication);
	if (qdf_status != QDF_STATUS_SUCCESS)
		target_if_err("Failed to lphb_rsp_event");
out:
	if (slphb_indication)
		qdf_mem_free(slphb_indication);

	return qdf_status;
}
#endif

