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

#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/version.h>
#include <linux/module.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
#endif

#include "atsc30_log.h"
#include "alp_parser_if.h"
#include "alp_hal_hw.h"
#include "alp_packet.h"
#include "alp_priv_packet.h"
#include "alp_desc.h"
#include "alp_lmt.h"
#include "alp_lmt.h"
#include "ring_buf.h"
#include "bits_op.h"
#include "bb_packet.h"

#include "mdrv_atsc30_if.h"
#include "mdrv_atsc30_if_austin.h"
#include "mdrv_atsc30_internal.h"
#include "mdrv_atsc30_alp_si.h"
#include "mdrv_atsc30_udp_ip.h"
#include "mdrv_atsc30_ptp.h"

#include "mtk_dmx_alp.h"

#include "mtk_dmx_common.h"
#include "mtk_dmx_log.h"
#include "mtk_dmx_dev.h"
#include "mtk_dmx_pvr.h"

#if (LOG_TO_FILE)
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/syscalls.h>

static struct file *pfd = NULL;
static struct file *pfd1 = NULL;
static struct file *pfd2 = NULL;
#endif

#include "mtk_dmx_atsc3.h"
#include "demod_drv_alppreparse.h"

//-------------------------------------------------------------------------------------------------
//  Macros
//-------------------------------------------------------------------------------------------------
#define MDRV_AUSTIN_ATSC30_IF_PROFILE_LOG_US_MIN (3000)
#define KEYWORD_LEN_MAX 8
#define ALPPKT_LEN_MAX 0x20000
#define MDRV_ATSC30_IF_AUSTIN_PVR_FLUSH_WAIT_TIME (100) //ms
#define MDRV_ATSC30_IF_AUSTIN_PVR_FINALIZE_WAIT_TIME_FOR_EACH (100) //ms

//-------------------------------------------------------------------------------------------------
//  Structs and Enums
//-------------------------------------------------------------------------------------------------
struct austin_timestamp_info {
	size_t length;
	uintptr_t timestamp;
	uintptr_t startaddr;
	uintptr_t endaddr;
}austin_timestamp_info_t;
#define AUSTIN_TIMESTAMP_RING_BUF_SIZE (sizeof(austin_timestamp_info_t) * 2048)

//-------------------------------------------------------------------------------------------------
//  Global var
//-------------------------------------------------------------------------------------------------
static void *timestamp_ring_buf = NULL;

//-------------------------------------------------------------------------------------------------
//  Functions Prototype
//-------------------------------------------------------------------------------------------------
extern int if_pvr_callback(
		const u8 *buffer1,
		size_t buffer1_len,
		const u8 *buffer2,
		size_t buffer2_len,
		struct dvb_dvr2 *dvb_dvr2,
		u32 *buf_flags);

extern int if_check_keywords(
		ring_buf_t ring_buf,
		const u8 **keywords_found,
		struct timestamp_info *timestamp_info);

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static int if_do_pvr_task_once_austin(
		struct mtk_dvr2 *mtk_dvr2)
{
	int ret = 0;
	u64 wptr = 0;

	if (mtk_dvr2_is_running(mtk_dvr2)) {
		wptr = call_op(mtk_dvr2, buf, get_wptr, mtk_dvr2);
		if (wptr != 0) {
			if (cast_to_dvr_parent(mtk_dvr2))
				ret = cast_to_dvr_parent(mtk_dvr2)->callback(
					(const u8 *)&wptr, 0,
					NULL, 0,
					cast_to_dvr_parent(mtk_dvr2),
					NULL);
		}
	}

	return ret;
}

static int if_is_keyword_enabled_austin(bool *enabled)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	int ret_tmp = MTK_DMX_SUCCESS;
	bool bIsInserted = false;
	demux_alp_key_words_t stKeyWords = {0};

	ret_tmp = mtk_dmx_alp_is_timestamp_keyword_sideband_inserted(&bIsInserted);
	if (ret_tmp != MTK_DMX_SUCCESS) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"mtk_dmx_alp_is_timestamp_keyword_sideband_inserted is failed\n");

		return MDRV_ATSC30_RETURN_CODE_FAILURE;
	}
	ret_tmp = mtk_dmx_alp_get_keywords(&stKeyWords);
	if (ret_tmp != MTK_DMX_SUCCESS) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"mtk_dmx_alp_get_keywords is failed\n");

		return MDRV_ATSC30_RETURN_CODE_FAILURE;
	}

	*enabled = bIsInserted
			&& (stKeyWords.eKeyWordType != DEMUX_ALP_KEY_WORD_TYPE_TIMESTAMP_4B);

	ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
		"keyword enabled is %s\n",
		ATSC30_BOOL_2_STR(*enabled));

	return ret;
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
int if_austin_pvr_callback(
		const u8 *buffer1,
		size_t buffer1_len,
		const u8 *buffer2,
		size_t buffer2_len,
		struct dvb_dvr2 *dvb_dvr2,
		u32 *buf_flags)
{
	int ret = 0;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private = NULL;
	demux_alp_instance_e alp_instance;
	struct mdrv_atsc30_if_context *atsc30_context = NULL;
	struct dvb_ringbuffer *ringbuf = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;
	u64 wptr = *((u64 *)buffer1);
	u64 prev_wptr = 0;
	u64 payload_wptr = 0;
	bool is_ovf = false;
	struct atsc30_profile profile = {
		.log_level = A30_DBG_LVL_INFO,
		.log_module = ATSC30_LOG_MODULE_DRVIF,
		.log_prefix = __func__,
		.log_us = MDRV_AUSTIN_ATSC30_IF_PROFILE_LOG_US_MIN,
	};

	if (ATSC30_DRVIF_VERIFY(dvb_dvr2 == NULL))
		return -1;
	if (ATSC30_DRVIF_VERIFY(buffer1 == NULL))
		return -1;

	mtk_dvr2 = cast_to_mtk_dvr(dvb_dvr2);
	alp_instance = mtk_dmx_alp_tsio_path_2_alp_instance(mtk_dvr2->path_id);
	if (ATSC30_DRVIF_VERIFY(alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -1;
	ret = mdrv_atsc30_if_get_context(alp_instance, &atsc30_context);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"mdrv_atsc30_if_get_context(%d, ...) is failed\n",
			alp_instance);
		return -1;
	}

	mtk_atsc3_dvr_private = dvb_dvr2->priv;
	if (MTK_DMX_VERIFY(mtk_atsc3_dvr_private == NULL))
		return 0;

	bc_ctx = &(mtk_atsc3_dvr_private->bc_ctx_austin);
	ringbuf = &bc_ctx->d_ctx.buf.ring;

	prev_wptr = atsc30_context->austin_preparser_ring_buf.wp;
	if (prev_wptr == wptr) {
		ATSC30_DRVIF_LOG_ENHANCED_INFO(
			"prev_wptr == wptr\n");

		return 0;
	}

	ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
			"wptr = 0x%llx\n",
			wptr);
	dvb_ringbuffer_dump_info(
			LOG_LEVEL_DEBUG,
			ringbuf);
	ring_buf_dump_info(
			A30_DBG_LVL_DEBUG,
			&atsc30_context->payload_ring_buf);
	ring_buf_dump_info(
			A30_DBG_LVL_DEBUG,
			&atsc30_context->austin_preparser_ring_buf);

	// calculate ring buffer address
	if (prev_wptr < wptr) {
		buffer1 = (u8 *)prev_wptr;
		buffer1_len = wptr - (u64)buffer1;
		buffer2 = NULL;
		buffer2_len = 0;
	} else {
		buffer1 = (u8 *)prev_wptr;
		buffer1_len = (u64)(ringbuf->data + ringbuf->size) - (u64)buffer1;
		buffer2 = ringbuf->data;
		buffer2_len = wptr - (u64)buffer2;
	}

	// update r/w point to atsc30_context ring buffer
	if (!ring_buf_update_rp_addr(
		&atsc30_context->austin_preparser_ring_buf,
		(uintptr_t)buffer1)) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"ring_buf_update_rp_addr(...) of %d is failed\n",
			alp_instance);

		return -1;
	}

	is_ovf = ring_buf_check_overflow(
				atsc30_context->austin_preparser_ring_buf,
				ring_buf_calculate_addr_increase(
					atsc30_context->austin_preparser_ring_buf,
					(uintptr_t)buffer1,
					buffer1_len + buffer2_len));
	if (is_ovf == true) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR("Overflow happen!!!!!!\n");
		ring_buf_dump_info(
			A30_DBG_LVL_ERROR,
			&atsc30_context->austin_preparser_ring_buf);
	}

	if (!ring_buf_update_wp_addr(
		&atsc30_context->austin_preparser_ring_buf,
		ring_buf_calculate_addr_increase(
			atsc30_context->austin_preparser_ring_buf,
			(uintptr_t)buffer1,
			buffer1_len + buffer2_len))) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"ring_buf_update_wp_addr(...) of %d is failed\n",
			alp_instance);

		return -1;
	}

	if ((buffer1_len+buffer2_len<=0) || ((buffer1_len+buffer2_len)>ringbuf->size))
		return -1;

	ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
		"%s austin_preparser_ring_buf.wp=0x%lx size=%d buffer2=0x%lx size=%d\n",
		__func__,
		(uintptr_t)buffer1,
		(int)buffer1_len,
		(uintptr_t)buffer2,
		(int)buffer2_len);

	atsc30_profile_get_begin_time(&profile);
	ret = if_austin_prepare_payloadbuf(
		ringbuf,
		atsc30_context,
		&payload_wptr);

	ATSC30_PROFILE_GET_END_TIME(&profile);
	if (ret < 0)
		return ret;

	if ((payload_wptr != atsc30_context->payload_ring_buf.wp) && (payload_wptr != 0))
	{
		ret = if_pvr_callback(
			(const u8 *)&payload_wptr,
			0,
			NULL,
			0,
			dvb_dvr2,
			buf_flags);
		if (ret < 0) {
			ring_buf_update_wp_addr(
				&atsc30_context->payload_ring_buf,
				payload_wptr);
			ATSC30_DRVIF_LOG_ENHANCED_ERROR("Drop packet!!!");
		}
	}
	return ret;
}

int if_austin_pvr_finalize_callback(
		struct mtk_dvr2 *mtk_dvr2,
		int timeout)
{
	int ret = 0;
	demux_alp_instance_e alp_instance;
	struct mdrv_atsc30_if_context *atsc30_context = NULL;
	int tmp_timeout = 0;

	if (ATSC30_DRVIF_VERIFY(mtk_dvr2 == NULL))
		return -1;
	alp_instance = mtk_dmx_alp_tsio_path_2_alp_instance(mtk_dvr2->path_id);
	if (ATSC30_DRVIF_VERIFY(alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -1;

	ret = mdrv_atsc30_if_get_context(alp_instance, &atsc30_context);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"mdrv_atsc30_if_get_context(%d, ...) is failed\n",
			alp_instance);

		return -1;
	}

	mtk_pvr_flush_data(cast_to_dvr_parent(mtk_dvr2)->index);
	mdelay(MDRV_ATSC30_IF_AUSTIN_PVR_FLUSH_WAIT_TIME);
	if_do_pvr_task_once_austin(mtk_dvr2);

	do {
		ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
			"wait payload_ring_buf handled\n");
		mdelay(MDRV_ATSC30_IF_AUSTIN_PVR_FINALIZE_WAIT_TIME_FOR_EACH);
		tmp_timeout++;

		if ((timeout != PVR_FLUSH_TIMEOUT_NEVER) &&
			(tmp_timeout > timeout)) {
			ATSC30_DRVIF_LOG_ENHANCED_WARN(
				"payload_ring_buf.rp = 0x%llx\n",
				(u64)(atsc30_context->payload_ring_buf.rp));
			ATSC30_DRVIF_LOG_ENHANCED_WARN(
				"payload_ring_buf_sent_to_msg.rp = 0x%llx\n",
				(u64)(atsc30_context->payload_ring_buf_sent_to_msg.rp));

			ATSC30_DRVIF_LOG_ENHANCED_WARN("timeout\n");

			ret = -1;
			break;
		}
	} while (atsc30_context->payload_ring_buf.rp !=
		 atsc30_context->payload_ring_buf_sent_to_msg.rp);

	return ret;
}

int if_context_allocate_resource_austin(
		u8 framer_idx,
		struct mdrv_atsc30_if_context *atsc30_if_context,
		ring_buf_init_config_t austin_preparser_buf_config)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;

	do {
		ret = ring_buf_set(
				&atsc30_if_context->austin_preparser_ring_buf,
				(u8 *)austin_preparser_buf_config.start_addr,
				austin_preparser_buf_config.size);

		if (ret != 0) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR("%s %d austin start=0x%lx end=0x%lx wp=0x%lx rp=0x%lx size=%d\n",
				__FUNCTION__,__LINE__,
				atsc30_if_context->austin_preparser_ring_buf.start,
				atsc30_if_context->austin_preparser_ring_buf.end,
				atsc30_if_context->austin_preparser_ring_buf.wp,
				atsc30_if_context->austin_preparser_ring_buf.rp,
				atsc30_if_context->austin_preparser_ring_buf.tot_size);
			ret = MDRV_ATSC30_RETURN_CODE_FAILURE;
			break;
		}

		ATSC30_DRVIF_LOG_ENHANCED_DEBUG("%s %d austin start=0x%lx end=0x%lx wp=0x%lx rp=0x%lx size=%d\n",
				__FUNCTION__,__LINE__,
				atsc30_if_context->austin_preparser_ring_buf.start,
				atsc30_if_context->austin_preparser_ring_buf.end,
				atsc30_if_context->austin_preparser_ring_buf.wp,
				atsc30_if_context->austin_preparser_ring_buf.rp,
				atsc30_if_context->austin_preparser_ring_buf.tot_size);
		atsc30_if_context->austin_preparser_ring_buf.name = "austin_preparser_ring_buf";
		ATSC30_DRVIF_LOG_ENHANCED_DEBUG("austin_preparser_ring_buf\n");
		ring_buf_dump_info(
			A30_DBG_LVL_DEBUG,
			&atsc30_if_context->austin_preparser_ring_buf);

		timestamp_ring_buf = vmalloc(AUSTIN_TIMESTAMP_RING_BUF_SIZE);
		atsc30_if_context->alp_preparser.bb_pkt_buf = vzalloc(ALPPKT_LEN_MAX);
		atsc30_if_context->alp_preparser.alp_pkt_buf = vzalloc(ALPPKT_LEN_MAX);
		if ((timestamp_ring_buf == NULL) ||
			(atsc30_if_context->alp_preparser.bb_pkt_buf == NULL) ||
			(atsc30_if_context->alp_preparser.alp_pkt_buf == NULL)) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"allocate memory(0x%zx) of %d is failed\n",
				AUSTIN_TIMESTAMP_RING_BUF_SIZE +
				ALPPKT_LEN_MAX +
				ALPPKT_LEN_MAX,
				framer_idx);
			vfree(timestamp_ring_buf);
			vfree(atsc30_if_context->alp_preparser.bb_pkt_buf);
			atsc30_if_context->alp_preparser.bb_pkt_buf = NULL;
			vfree(atsc30_if_context->alp_preparser.alp_pkt_buf);
			atsc30_if_context->alp_preparser.alp_pkt_buf = NULL;

			ret = MDRV_ATSC30_RETURN_CODE_RESOURCE;
			break;
		}
		ret = ring_buf_set(
				&atsc30_if_context->austin_timestamp_ring_buf,
				timestamp_ring_buf,
				AUSTIN_TIMESTAMP_RING_BUF_SIZE);
		if (ret != 0) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"It is failed to set austin timestamp ring buffer of %d\n",
				framer_idx);
			vfree((const void *)timestamp_ring_buf);
			ret = MDRV_ATSC30_RETURN_CODE_FAILURE;
			break;
		}

		atsc30_if_context->austin_timestamp_ring_buf.name = "austin_timestamp_ring_buf";
		ATSC30_DRVIF_LOG_ENHANCED_DEBUG("austin_timestamp_ring_buf\n");
		ring_buf_dump_info(
			A30_DBG_LVL_DEBUG,
			&atsc30_if_context->austin_timestamp_ring_buf);

		atsc30_if_context->rec_task_callback =
			if_austin_pvr_callback;
		atsc30_if_context->rec_task_finalize_callback =
			if_austin_pvr_finalize_callback;
		atsc30_if_context->is_austin = true;
	} while (0);

	return ret;
}

int if_context_free_resource_austin(
		struct mdrv_atsc30_if_context *atsc30_if_context)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	int ret_tmp = MDRV_ATSC30_RETURN_CODE_OK;

	if (ATSC30_DRVIF_VERIFY(atsc30_if_context == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (!atsc30_if_context->is_austin)
		return MDRV_ATSC30_RETURN_CODE_RESOURCE;

	if (atsc30_if_context->austin_preparser_ring_buf.start != (uintptr_t)NULL) {
		ret_tmp = ring_buf_set(
				&atsc30_if_context->austin_preparser_ring_buf,
				NULL,
				0);
		if (ret_tmp != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"It is failed to free austin_preparser_ring_buf\n");

			ret = ret_tmp;
		}
	}

	if (atsc30_if_context->austin_timestamp_ring_buf.start != (uintptr_t)NULL) {
		vfree((const void *)timestamp_ring_buf);
		ret_tmp = ring_buf_set(
				&atsc30_if_context->austin_timestamp_ring_buf,
				NULL,
				0);
		if (ret_tmp != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"It is failed to free austin_timestamp_ring_buf\n");

			ret = ret_tmp;
		}
	}
	vfree(atsc30_if_context->alp_preparser.bb_pkt_buf);
	atsc30_if_context->alp_preparser.bb_pkt_buf = NULL;
	vfree(atsc30_if_context->alp_preparser.alp_pkt_buf);
	atsc30_if_context->alp_preparser.alp_pkt_buf = NULL;

	return ret;
}

int if_alp_austin_timestampinfo_prepare(
	struct mdrv_atsc30_if_context *atsc30_if_context,
	uintptr_t alp_packet_start,
	struct timestamp_info *timestamp_info)
{
	int iret = MDRV_ATSC30_RETURN_CODE_FAILURE;
	struct austin_timestamp_info *austin_timestamp_info = NULL;
	if (!atsc30_if_context->is_austin)
		return MDRV_ATSC30_RETURN_CODE_OK;

	while (1) {
		if (atsc30_if_context->austin_timestamp_ring_buf.rp ==
			atsc30_if_context->austin_timestamp_ring_buf.wp) {
			// find nothing, revert austin_timestamp_ring_buf rp
			iret = MDRV_ATSC30_RETURN_CODE_FAILURE;
			break;
		}

		austin_timestamp_info =
			(struct austin_timestamp_info *)
			atsc30_if_context->austin_timestamp_ring_buf.rp;
		if (ring_buf_check_range_valid(
			atsc30_if_context->payload_ring_buf,
			austin_timestamp_info->startaddr,
			austin_timestamp_info->endaddr)) {
			ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
				"%s %d start=0x%lx end=0x%lx timestamp=0x%lx",
				__func__,
				__LINE__,
				(uintptr_t)austin_timestamp_info->startaddr,
				(uintptr_t)austin_timestamp_info->endaddr,
				(austin_timestamp_info->timestamp));
			if (austin_timestamp_info->endaddr > austin_timestamp_info->startaddr) {
				if ((alp_packet_start < austin_timestamp_info->endaddr) &&
					(alp_packet_start >= austin_timestamp_info->startaddr)) {
					timestamp_info->timestamp =
						(u8 *)austin_timestamp_info->timestamp;
					timestamp_info->length = austin_timestamp_info->length;
					if (mtk_dmx_atsc30_if_parse_copy_timestamp_info(
						atsc30_if_context->austin_preparser_ring_buf,
						timestamp_info) != MDRV_ATSC30_RETURN_CODE_OK) {
						ATSC30_DRVIF_LOG_ENHANCED_ERROR(
							"if_parse_copy_timestamp_info is failed\n");

						return MDRV_ATSC30_RETURN_CODE_FAILURE;
					}

					iret = MDRV_ATSC30_RETURN_CODE_OK;
					break;
				}
			} else {
				if (!((alp_packet_start >= austin_timestamp_info->endaddr) &&
					(alp_packet_start < austin_timestamp_info->startaddr))) {
					timestamp_info->timestamp =
						(u8 *)austin_timestamp_info->timestamp;
					timestamp_info->length = austin_timestamp_info->length;
					if (mtk_dmx_atsc30_if_parse_copy_timestamp_info(
						atsc30_if_context->austin_preparser_ring_buf,
						timestamp_info) != MDRV_ATSC30_RETURN_CODE_OK) {
						ATSC30_DRVIF_LOG_ENHANCED_ERROR(
							"if_parse_copy_timestamp_info is failed\n");

						return MDRV_ATSC30_RETURN_CODE_FAILURE;
					}

					iret = MDRV_ATSC30_RETURN_CODE_OK;
					break;
				}
			}
		}

		ring_buf_update_rp_addr(
			&atsc30_if_context->austin_timestamp_ring_buf,
			ring_buf_calculate_addr_increase(
				atsc30_if_context->austin_timestamp_ring_buf,
				(uintptr_t)
				atsc30_if_context->austin_timestamp_ring_buf.rp,
				sizeof(austin_timestamp_info_t)));
	}

	return iret;
}

int if_austin_prepare_payloadbuf(
	struct dvb_ringbuffer *ringbuf,
	struct mdrv_atsc30_if_context *atsc30_context,
	u64 *payload_wptr)
{
	int ret = 0;
	bool enable_keyword = true;
	u32 bb_packet_size = 0;
	const u8 *keywords1_found = NULL;
	const u8 *keywords2_found = NULL;
	const u8 *last_keywords2_found = NULL;
	u8 *bb_pkt_buf = NULL;
	u8 *bb_pkt_buf_used = NULL;
	u8 *alp_pkt_buf = NULL;
	u8 *alp_pkt_buf_used = NULL;
	u32 alp_pkt_buf_size = 0;
	u32 total_size = 0;
	struct timestamp_info timestamp_info = { 0 };
	struct austin_timestamp_info *austin_timestamp_info = NULL;
#if BBP_MOVE
	u8 bb_header_buf[BB_PARSER_HEADER_SIZE] = { 0 };
	struct bb_header bb_header = { 0 };
	net_buf_t bb_header_net_buf = { 0 };
#endif //#if BBP_MOVE
	uintptr_t start_wp = atsc30_context->payload_ring_buf.wp;

	// check keyword
	ret = if_is_keyword_enabled_austin(&enable_keyword);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"if_is_keyword_enabled is failed\n");

		return MDRV_ATSC30_RETURN_CODE_FAILURE;
	}

#if (LOG_TO_FILE)
#if LOG_TO_FILE_BB
	if (pfd == NULL) {
		pfd = filp_open("/mnt/usb/BB.bin", O_RDWR | O_CREAT, 0644);
		if (IS_ERR(pfd)) {
			printk ("Open BB.bin failed %d\n", IS_ERR(pfd));
			pfd = NULL;
		}
	}
#endif //#if LOG_TO_FILE_BB
#if LOG_TO_FILE_ALP
	if (pfd1== NULL) {
		pfd1= filp_open("/mnt/usb/ALP.bin", O_RDWR | O_CREAT, 0644);
		if (IS_ERR(pfd1)) {
			printk ("Open ALP.bin failed %d\n", IS_ERR(pfd1));
			pfd1 = NULL;
		}
	}
#endif //#ifdef LOG_TO_FILE_ALP
#if LOG_TO_FILE_ORI
	if (pfd2 == NULL) {
		pfd2 = filp_open("/mnt/usb/ORI.bin", O_RDWR | O_CREAT, 0644);
		if (IS_ERR(pfd2)) {
			printk ("Open ORI.bin failed %d\n", IS_ERR(pfd2));
			pfd2 = NULL;
		}
	}
#endif //#if LOG_TO_FILE_ORI
	if ((pfd2 != NULL)) {
		if (atsc30_context->austin_preparser_ring_buf.rp < atsc30_context->austin_preparser_ring_buf.wp) {
			ssize_t ret = 0;
			loff_t pos = pfd2->f_pos;
			ret = kernel_write(pfd2, (void *)atsc30_context->austin_preparser_ring_buf.rp,
				(atsc30_context->austin_preparser_ring_buf.wp - atsc30_context->austin_preparser_ring_buf.rp), &pos);
			ATSC30_DRVIF_LOG_ENHANCED_DEBUG("kernel_write ret %d", ret);
			pfd2->f_pos = pos;
			vfs_fsync(pfd2, 0);
		}
		else {
			ssize_t ret = 0;
			loff_t pos = pfd2->f_pos;
			ret = kernel_write(pfd2, (void *)atsc30_context->austin_preparser_ring_buf.rp,
				(atsc30_context->austin_preparser_ring_buf.end - atsc30_context->austin_preparser_ring_buf.rp), &pos);
			ATSC30_DRVIF_LOG_ENHANCED_DEBUG("kernel_write ret %d", ret);
			pfd2->f_pos = pos;
			vfs_fsync(pfd2, 0);
			pos = pfd2->f_pos;
			ret = kernel_write(pfd2, (void *)atsc30_context->austin_preparser_ring_buf.start,
				(atsc30_context->austin_preparser_ring_buf.wp - atsc30_context->austin_preparser_ring_buf.start), &pos);
			ATSC30_DRVIF_LOG_ENHANCED_DEBUG("kernel_write ret %d", ret);
			pfd2->f_pos = pos;
			vfs_fsync(pfd2, 0);
		}
	}
#endif

	while(1) {
		bb_pkt_buf = atsc30_context->alp_preparser.bb_pkt_buf;
		alp_pkt_buf = atsc30_context->alp_preparser.alp_pkt_buf;
		keywords1_found = NULL;
		keywords2_found = NULL;
		alp_pkt_buf_size = 0;
		bb_packet_size = 0;

		if (enable_keyword) {
			ret = if_check_keywords(
				atsc30_context->austin_preparser_ring_buf,
				&keywords1_found,
				&timestamp_info);

			if ((ret == MDRV_ATSC30_RETURN_CODE_OK) && (keywords1_found != NULL)) {
				ring_buf_update_rp_addr(
					&atsc30_context->austin_preparser_ring_buf,
					(uintptr_t) keywords1_found);
			} else {
				ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
					"if_check_keyword1 is failed\n");
				ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
					"%s austin_preparser_ring_buf.rp = 0x%lx\n",
					__func__,
					atsc30_context->austin_preparser_ring_buf.rp);
				atsc30_log_dump_hex_array(
					A30_DBG_LVL_DEBUG,
					ATSC30_LOG_MODULE_DRVIF,
					(const void *)&atsc30_context->austin_preparser_ring_buf.rp,
					min((size_t)16, (size_t)ring_buf_calculate_range_size(atsc30_context->austin_preparser_ring_buf,
											atsc30_context->austin_preparser_ring_buf.rp,
											atsc30_context->austin_preparser_ring_buf.end)),
					sizeof(u8),
					"keyword1 buffer");
				break;
			}

#if BBP_MOVE
			// Parse BB packet header
			if (ring_buf_calculate_range_size(
				atsc30_context->austin_preparser_ring_buf,
				atsc30_context->austin_preparser_ring_buf.rp,
				atsc30_context->austin_preparser_ring_buf.wp) <=
					BB_PARSER_HEADER_SIZE) {
				ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
					"austin_preparser_ring_buf is not enough\n");
				ring_buf_update_rp_addr(
					&atsc30_context->austin_preparser_ring_buf,
					(uintptr_t) ring_buf_calculate_addr_decrease(
					atsc30_context->austin_preparser_ring_buf,
					(uintptr_t)keywords1_found,
					KEYWORD_LEN_MAX));
				ringbuf->pread = keywords1_found - KEYWORD_LEN_MAX - ringbuf->data;
				break;
			}

			ring_buf_copy_range_by_start_and_size(
				bb_header_buf,
				atsc30_context->austin_preparser_ring_buf,
				atsc30_context->austin_preparser_ring_buf.rp,
				BB_PARSER_HEADER_SIZE);
			bb_header_net_buf =
				net_buf_init_full(
					bb_header_buf,
					sizeof(bb_header_buf));
			if (bb_parser_parse_header(
				&bb_header,
				&bb_header_net_buf) != ALP_SUCCESS) {
				ATSC30_DRVIF_LOG_ENHANCED_ERROR(
					"bb_parser_parse_header is failed\n");
				ring_buf_update_rp_addr(
					&atsc30_context->austin_preparser_ring_buf,
					(uintptr_t) ring_buf_calculate_addr_decrease(
					atsc30_context->austin_preparser_ring_buf,
					(uintptr_t)keywords1_found,
					KEYWORD_LEN_MAX));
				ringbuf->pread = keywords1_found - KEYWORD_LEN_MAX - ringbuf->data;
				break;
			}
			bb_parser_dump_header(A30_DBG_LVL_DEBUG, &bb_header);

			if (ring_buf_calculate_range_size(
				atsc30_context->austin_preparser_ring_buf,
				atsc30_context->austin_preparser_ring_buf.rp,
				atsc30_context->austin_preparser_ring_buf.wp) <=
				BB_PARSER_HEADER_SIZE + bb_header.l2) {
				ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
					"austin_preparser_ring_buf is not enough\n");
				ring_buf_update_rp_addr(
					&atsc30_context->austin_preparser_ring_buf,
					(uintptr_t) ring_buf_calculate_addr_decrease(
					atsc30_context->austin_preparser_ring_buf,
					(uintptr_t)keywords1_found,
					KEYWORD_LEN_MAX));
				ringbuf->pread = keywords1_found - KEYWORD_LEN_MAX - ringbuf->data;
				break;
			}

			// Move to the end of BB packet
			ring_buf_update_rp_addr(
				&atsc30_context->austin_preparser_ring_buf,
				ring_buf_calculate_addr_increase(
					atsc30_context->austin_preparser_ring_buf,
					(uintptr_t) keywords1_found,
					BB_PARSER_HEADER_SIZE + bb_header.l2));
#endif //#if BBP_MOVE

			ret = if_check_keywords(
					atsc30_context->austin_preparser_ring_buf,
					&keywords2_found,
					&timestamp_info);

			if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
				ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
					"if_check_keyword2 not found, try next\n");
				ring_buf_update_rp_addr(
					&atsc30_context->austin_preparser_ring_buf,
					(uintptr_t) ring_buf_calculate_addr_decrease(
					atsc30_context->austin_preparser_ring_buf,
					(uintptr_t)keywords1_found,
					KEYWORD_LEN_MAX));
				ringbuf->pread = keywords1_found - KEYWORD_LEN_MAX - ringbuf->data;
				break;
			}
		} else {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR("Not Enable keyword!!!!\n");
			break;
		}

		ATSC30_DRVIF_LOG_ENHANCED_DEBUG("%s keyword1 = 0x%lx keyword2 = 0x%lx",
			__FUNCTION__,(uintptr_t)keywords1_found,(uintptr_t)keywords2_found);

		atsc30_log_dump_hex_array(
			A30_DBG_LVL_DEBUG,
			ATSC30_LOG_MODULE_DRVIF,
			(const void *)ring_buf_calculate_addr_decrease(
			atsc30_context->austin_preparser_ring_buf,
			(uintptr_t)keywords1_found,
			KEYWORD_LEN_MAX),
			min((size_t)16, (size_t)ring_buf_calculate_range_size(atsc30_context->austin_preparser_ring_buf,
								(uintptr_t)keywords1_found, atsc30_context->austin_preparser_ring_buf.end)),
			sizeof(u8),
			"keyword1 buffer");

		// prepare buffer to austin preparser
		// Decrease timestamp(4) + keyword(4)
		keywords2_found = (u8 *)ring_buf_calculate_addr_decrease(
			atsc30_context->austin_preparser_ring_buf,
			(uintptr_t)keywords2_found,
			KEYWORD_LEN_MAX);

		if (keywords2_found > keywords1_found) {
			bb_packet_size = (keywords2_found - keywords1_found);
		} else {
			bb_packet_size = (((uintptr_t)(atsc30_context->austin_preparser_ring_buf.end)
				- (uintptr_t)keywords1_found)
				+ ((uintptr_t)keywords2_found - (uintptr_t)atsc30_context->austin_preparser_ring_buf.start));
		}

		if ((bb_pkt_buf != NULL) && (alp_pkt_buf != NULL) && (bb_packet_size < ALPPKT_LEN_MAX)) {
			if (keywords2_found > keywords1_found) {
#if BB_PKT_BUF_MEMCOPY
				memcpy (bb_pkt_buf, (u8 *)keywords1_found, bb_packet_size);
				bb_pkt_buf_used = bb_pkt_buf;
#else
				bb_pkt_buf_used = (u8 *)keywords1_found;
#endif
			} else {
				u32 buf1size = (u32)((uintptr_t)atsc30_context->austin_preparser_ring_buf.end
						- (uintptr_t)keywords1_found);
				memcpy (bb_pkt_buf, (u8 *)keywords1_found, buf1size);
				memcpy ((u8 *)(bb_pkt_buf+buf1size),
					(u8 *)atsc30_context->austin_preparser_ring_buf.start,
					(bb_packet_size - buf1size));
				bb_pkt_buf_used = bb_pkt_buf;
			}
		} else {
			if (bb_packet_size < atsc30_context->austin_preparser_ring_buf.tot_size) {
				ring_buf_update_rp_addr(
					&atsc30_context->austin_preparser_ring_buf,
					(uintptr_t) ring_buf_calculate_addr_decrease(
					atsc30_context->austin_preparser_ring_buf,
					(uintptr_t)keywords1_found,
					KEYWORD_LEN_MAX));
				ringbuf->pread = (size_t)(keywords1_found - KEYWORD_LEN_MAX - ringbuf->data);
			} else {
				ring_buf_update_rp_addr(
					&atsc30_context->austin_preparser_ring_buf,
					(uintptr_t)keywords1_found);
				ringbuf->pread = (size_t)(keywords1_found - ringbuf->data);
			}
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"Allocate bb_pkt_buf/alp_pkt_buf failed\n");
			continue; // Mark need change to continue
		}

#if (LOG_TO_FILE)
		if ((pfd != NULL)) {
			ssize_t ret = 0;
			loff_t pos = 0;
			pos = pfd->f_pos;
			ret = kernel_write(pfd, bb_pkt_buf_used, bb_packet_size, &pos);
			ATSC30_DRVIF_LOG_ENHANCED_DEBUG("kernel_write ret %d", ret);
			pfd->f_pos = pos;
			vfs_fsync(pfd, 0);
		}
#endif

		// keep last keyword
		last_keywords2_found = keywords2_found;

		atsc30_log_dump_hex_array(
			A30_DBG_LVL_DEBUG,
			ATSC30_LOG_MODULE_DRVIF,
			(const void *)bb_pkt_buf_used,
			min((size_t)16, (size_t)(bb_packet_size)),
			sizeof(u8),
			"bb buffer");

		alp_pkt_buf_used = alp_pkt_buf;
#if ALP_PKT_BUF_MEMCOPY == 0
		if ((atsc30_context->payload_ring_buf.wp != 0) &&
			(atsc30_context->payload_ring_buf.start != 0)) {
			if ((atsc30_context->payload_ring_buf.end -
				atsc30_context->payload_ring_buf.wp) >= ALPPKT_LEN_MAX) {
				alp_pkt_buf_used = (u8 *)atsc30_context->payload_ring_buf.wp;
			}
		}
#endif

		if (!mdrv_atsc3_bb2alp_parser(
			bb_pkt_buf_used, bb_packet_size, alp_pkt_buf_used, &alp_pkt_buf_size))
		{
			ATSC30_DRVIF_LOG_ENHANCED_ERROR("%s mdrv_atsc3_bb2alp_parser fail! K1=0x%lx K2=0x%lx \n",
					__FUNCTION__, (uintptr_t)keywords1_found, (uintptr_t)keywords2_found);
			ring_buf_update_rp_addr(
				&atsc30_context->austin_preparser_ring_buf,
				(uintptr_t) keywords2_found);

			// update pread
			ringbuf->pread = keywords2_found - ringbuf->data;
			continue;
		}

		ATSC30_DRVIF_LOG_ENHANCED_DEBUG("%s bb_packet_size = %d alp_pkt_buf_size = %d",
			__FUNCTION__,bb_packet_size,alp_pkt_buf_size);

		if (alp_pkt_buf_size > 0) {
			atsc30_log_dump_hex_array(
				A30_DBG_LVL_DEBUG,
				ATSC30_LOG_MODULE_DRVIF,
				(const void *)alp_pkt_buf_used,
				min((size_t)16, (size_t)(alp_pkt_buf_size)),
				sizeof(u8),
				"alp buffer");
		}

		if (!ring_buf_update_rp_addr(
				&atsc30_context->austin_preparser_ring_buf,
				(uintptr_t) keywords2_found)) {
				ATSC30_DRVIF_LOG_ENHANCED_ERROR(
					"Update austin_preparser_ring_buf rp failed\n");
		}

		// update pread
		ringbuf->pread = keywords2_found - ringbuf->data;

		// prepare payload buffer to adapter & sw parser
		if ((atsc30_context->payload_ring_buf.wp != 0) &&
			(atsc30_context->payload_ring_buf.start != 0)) {
			if (alp_pkt_buf_size >
				(atsc30_context->payload_ring_buf.end -
					atsc30_context->payload_ring_buf.wp)) {
				memcpy ((void *)atsc30_context->payload_ring_buf.wp,
					alp_pkt_buf_used,
					(atsc30_context->payload_ring_buf.end -
						atsc30_context->payload_ring_buf.wp));
				memcpy ((void *)atsc30_context->payload_ring_buf.start,
					alp_pkt_buf_used +
					(atsc30_context->payload_ring_buf.end -
						atsc30_context->payload_ring_buf.wp),
					(alp_pkt_buf_size -
						(atsc30_context->payload_ring_buf.end -
							atsc30_context->payload_ring_buf.wp)));
			} else {
				if (alp_pkt_buf_used == alp_pkt_buf)
					memcpy((void *)atsc30_context->payload_ring_buf.wp,
						alp_pkt_buf_used, alp_pkt_buf_size);
			}
		} else {
			ring_buf_update_rp_addr(
				&atsc30_context->austin_preparser_ring_buf,
				(uintptr_t) ring_buf_calculate_addr_decrease(
				atsc30_context->austin_preparser_ring_buf,
				(uintptr_t) keywords1_found,
				KEYWORD_LEN_MAX));
			ringbuf->pread = keywords1_found - KEYWORD_LEN_MAX - ringbuf->data;
			ATSC30_DRVIF_LOG_ENHANCED_ERROR("payload buffer write failed\n");
			break;
		}

		austin_timestamp_info = (struct austin_timestamp_info *)atsc30_context->austin_timestamp_ring_buf.wp;
		austin_timestamp_info->timestamp = ring_buf_calculate_addr_decrease(
			atsc30_context->austin_preparser_ring_buf,
			(uintptr_t)keywords1_found,
			TIMESTAMP_LEN);
		austin_timestamp_info->length = TIMESTAMP_LEN;
		austin_timestamp_info->startaddr = atsc30_context->payload_ring_buf.wp;
		austin_timestamp_info->endaddr = ring_buf_calculate_addr_increase(
			atsc30_context->payload_ring_buf,
			(uintptr_t)atsc30_context->payload_ring_buf.wp,
			alp_pkt_buf_size);

		ATSC30_DRVIF_LOG_ENHANCED_DEBUG("%s %d start = 0x%lx end = 0x%lx len = %d timestamp = 0x%lx",
			__FUNCTION__,__LINE__,
			(uintptr_t)austin_timestamp_info->startaddr,
			(uintptr_t)austin_timestamp_info->endaddr,
			(int)austin_timestamp_info->length,
			austin_timestamp_info->timestamp);

#if (LOG_TO_FILE)
		if ((pfd1 != NULL)) {
			ssize_t ret = 0;
			loff_t pos = pfd1->f_pos;
			ret = kernel_write(pfd1, alp_pkt_buf_used, alp_pkt_buf_size, &pos);
			ATSC30_DRVIF_LOG_ENHANCED_DEBUG("kernel_write ret %d", ret);
			pfd1->f_pos = pos;
			vfs_fsync(pfd1, 0);
		}
#endif
		ring_buf_update_wp_addr(
			&atsc30_context->austin_timestamp_ring_buf,
			ring_buf_calculate_addr_increase(
			atsc30_context->austin_timestamp_ring_buf,
			(uintptr_t)atsc30_context->austin_timestamp_ring_buf.wp,
			sizeof(austin_timestamp_info_t)));

		ring_buf_update_wp_addr(
			&atsc30_context->payload_ring_buf,
			ring_buf_calculate_addr_increase(
			atsc30_context->payload_ring_buf,
			(uintptr_t)atsc30_context->payload_ring_buf.wp,
			alp_pkt_buf_size));

		total_size += alp_pkt_buf_size;
	}

	// update preparser last wp
	if (last_keywords2_found != NULL) {
#if (LOG_TO_FILE)
		if ((pfd2 != NULL)) {
			pfd2->f_pos -= ring_buf_calculate_range_size(
				atsc30_context->austin_preparser_ring_buf,
				(uintptr_t)last_keywords2_found,(uintptr_t)atsc30_context->austin_preparser_ring_buf.wp);
			vfs_fsync(pfd2, 0);
		}
#endif
		ring_buf_update_wp_addr(
			&atsc30_context->austin_preparser_ring_buf,
			(uintptr_t) last_keywords2_found);
	} else {
		ring_buf_update_wp_addr(
			&atsc30_context->austin_preparser_ring_buf,
			(uintptr_t) atsc30_context->austin_preparser_ring_buf.rp);
	}

	if (start_wp != 0) {
		ring_buf_update_wp_addr(
			&atsc30_context->payload_ring_buf,
			(uintptr_t)start_wp);
	}

	if (total_size != 0) {
		*payload_wptr = ring_buf_calculate_addr_increase(
			atsc30_context->payload_ring_buf,
			(uintptr_t)atsc30_context->payload_ring_buf.wp,
			total_size);
	}

	ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
		"%s payload_ring_buf.wp=0x%lx payload_wptr=0x%llx preparser_ring_buf.wp=0x%lx\n",
		__func__,
		atsc30_context->payload_ring_buf.wp,
		*payload_wptr,atsc30_context->austin_preparser_ring_buf.wp);

	if (atsc30_context->payload_ring_buf.wp != (uintptr_t)*payload_wptr)
		return MDRV_ATSC30_RETURN_CODE_OK;
	else
		return MDRV_ATSC30_RETURN_CODE_FAILURE;
}


