// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2021 MediaTek Inc.
 */

#include "alp_priv_packet.h"

#include "atsc30_utils.h"
#include "atsc30_log.h"

//-------------------------------------------------------------------------------------------------
//  Debug Constants and Macros
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Constants
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Macros
//-------------------------------------------------------------------------------------------------
// nanosec / 10^9 * freq
#define NS_2_STC(freq, val) \
	_alp_div64by32((((u64) val) * (freq)), NS_PER_SEC)
// sec * freq
#define SEC_2_STC(freq, val) \
	(((u64) val) * (freq))
#define PTP_2_STC(freq, sec, msec, usec, nsec) \
	(SEC_2_STC(freq, sec) \
		+ NS_2_STC( \
			(freq), \
			MS_US_NS_2_NS(msec, usec, nsec)))

//-------------------------------------------------------------------------------------------------
//  Structs and Enums
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Functions Prototype
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Variables
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static inline u64 _u8Div6432(
		u64 u8Dividend,
		u64 u8Divider,
		u64 *pu8Remainder)
{
	return div_u64_rem(
			u8Dividend,
			u8Divider,
			(u32 *)pu8Remainder);
}

static inline u64 _alp_div64by32(
		u64 dividend_64,
		u32 divider_32)
{
	u64 remainder;

	if (ALP_PARSER_VERIFY(divider_32 == 0))
		return 0;

	return _u8Div6432(dividend_64, divider_32, &remainder);
}


//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
ALP_RET_CODE_T alp_priv_packet_check(
		alp_plp_context_t *plp_ctx,
		alp_header_t *alp_hdr,
		alp_parser_ptp_update_stc_cb ptp_update_stc_cb,
		const struct alp_priv_packet_operations *alp_priv_packet_ops,
		const alp_parser_input_info_t *input_info)
{
	ALP_RET_CODE_T ret = ALP_SUCCESS;
	enum alp_priv_packet_pud_packet_type
		pud_packet_type = ALP_PRIV_PACKET_PUD_PACKET_TYPE_UNKNOWN;
	struct time_info time_info = { 0 };

	if (ALP_PARSER_VERIFY(plp_ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(alp_hdr == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(
		ALP_PRIV_PACKET_OPERATIONS_IS_INVALID(alp_priv_packet_ops)))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(input_info == NULL))
		return ALP_NULL_PTR;

	do {
		pud_packet_type =
			(*alp_priv_packet_ops->get_pud_packet_type)
				(alp_hdr);
		if (pud_packet_type == ALP_PRIV_PACKET_PUD_PACKET_TYPE_PLP_ID) {
			u8 plp_id =
				(*alp_priv_packet_ops->parse_plp_id)
					(alp_hdr);

			ALP_PARSER_LOG_DEBUG(
				"plp_id %d priv pkt\n",
				plp_id);

			if (!alp_lls_plp_context_switch(plp_ctx, plp_id)) {
				ALP_PARSER_LOG_ENHANCED_ERROR(
					"alp_lls_plp_context_switch is failed\n");

				ret = ALP_HANDLE_PRIV_PLP_ID_ERROR;
				break;
			}
			ret = ALP_PRIVATE_PKT_PLP_ID;
		} else if (pud_packet_type == ALP_PRIV_PACKET_PUD_PACKET_TYPE_PTP) {
			ret = (*alp_priv_packet_ops->parse_ptp)(
					alp_hdr,
					&time_info);
			if (ret != ALP_SUCCESS) {
				ALP_PARSER_LOG_ENHANCED_ERROR("parse_ptp is failed\n");

				break;
			}

			ALP_PTP_LOG_DEBUG(
				"ptp packet:time_info_flag 0x%x sec 0x%x msec 0x%x usec 0x%x nsec 0x%x\n",
				time_info.time_info_flag,
				time_info.sec,
				time_info.msec,
				time_info.usec,
				time_info.nsec);

			if (ptp_update_stc_cb != NULL) {
				u64 ptp_stc_val = 0;
				struct atsc30_profile profile = {
					.log_level = A30_DBG_LVL_INFO,
					.log_module = ATSC30_LOG_MODULE_PTP,
					.log_prefix = __func__,
					.log_us = ATSC30_PROFILE_LOG_US_DEFAULT,
				};
				ptp_packet_descriptor_t desc = {
					.framer_idx = input_info->framer_idx,
					.timestamp = input_info->timestamp,
					.stc_frequency = alp_priv_packet_ops->stc_frequency,
				};

				desc.stc_up_bound =
					(*alp_priv_packet_ops->get_stc_up_bound)
						(desc.stc_frequency);
				ALP_PTP_LOG_ENHANCED_DEBUG(
					"desc.stc_up_bound 0x%llx, desc.stc_frequency 0x%llx\n",
					desc.stc_up_bound,
					desc.stc_frequency);
				ptp_stc_val =
					PTP_2_STC(
						desc.stc_frequency,
						time_info.sec,
						time_info.msec,
						time_info.usec,
						time_info.nsec);

				ALP_PTP_LOG_ENHANCED_DEBUG(
					"ptp cb framer(%d), stc(0x%llx)\n",
					input_info->framer_idx,
					ptp_stc_val);

				atsc30_profile_get_begin_time(&profile);
				if (!(*ptp_update_stc_cb)(ptp_stc_val, &desc))
					ALP_PARSER_LOG_ENHANCED_TRACE(
						"ptp_update_stc_cb fail\n");
				ATSC30_PROFILE_GET_END_TIME(&profile);
			}

			ret = ALP_PRIVATE_PKT_PTP;
		}
	} while (0);

	return ret;
}

bool alp_priv_packet_is_pud_packet(const alp_header_t *const alp_hdr)
{
	if ((alp_hdr != NULL)
		&& (alp_hdr->pkt_type == ALP_PACKET_IPV4)
		&& (alp_hdr->encap_type  == ENCAP_TYPE_HEADER_MODE)
		&& (alp_hdr->add_hdr.header_mode.HEF == true)
		&& (alp_hdr->payload_length == 0))
		return true;

	return false;
}

u8 alp_priv_packet_get_pud_type(const alp_header_t *const alp_hdr)
{
	if (alp_hdr != NULL)
		return 0;
	if (!alp_priv_packet_is_pud_packet(alp_hdr))
		return 0;

	return alp_hdr->add_hdr.header_mode.hdr_ext.extension_type;
}
