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

#include "rohc_decomp.h"
#include "rohc_decomp_internal.h"
#include "rohc_packet.h"

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

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

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

//-------------------------------------------------------------------------------------------------
//  Macros
//-------------------------------------------------------------------------------------------------

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

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

//-------------------------------------------------------------------------------------------------
//  Variables
//-------------------------------------------------------------------------------------------------
static rohc_decompressor_t g_decompressors[MAX_DECOMPRESSOR_NUM];

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static inline bool rohc_validate_cid(
		const rohc_cid_type_t cid_type,
		const u16 max_cid)
{
	switch (cid_type) {
	case ROHC_CID_TYPE_SMALL:
		return (max_cid <= ROHC_SMALL_CID_MAX);

	case ROHC_CID_TYPE_LARGE:
		return (max_cid <= ROHC_LARGE_CID_MAX);

	default:
		return false;
	}

	return false;
}

static bool init_context_management(
		rohc_decomp_context_management_t *const mm,
		const u16 tot_ctx_num)
{
	if (ATSC30_NULL_PTR_CHECK(mm))
		return false;

	memset(mm, 0, sizeof(rohc_decomp_context_management_t));

	if (tot_ctx_num == 0)
		mm->total_num = DEFAULT_CONTEXT_NUM_PER_COMPRESSOR;
	else if (tot_ctx_num >= MAX_CONTEXT_NUM_PER_COMPRESSOR)
		mm->total_num = MAX_CONTEXT_NUM_PER_COMPRESSOR;
	else
		mm->total_num = tot_ctx_num;

	mm->ctx_array =
		(rohc_decomp_context_t *)
		vmalloc(tot_ctx_num * sizeof(rohc_decomp_context_t));

	if (mm->ctx_array == NULL) {
		ROHC_LOG_ERROR(
			"%s init context memory fail\n",
			__func__);

		return false;
	}

	memset(mm->ctx_array,
		0,
		tot_ctx_num * sizeof(rohc_decomp_context_t));

	mm->lru_record =
		(size_t *)
		vmalloc(tot_ctx_num * sizeof(size_t));

	if (mm->lru_record == NULL) {
		ROHC_LOG_ERROR(
			"%s init lru context memory fail\n",
			__func__);

		return false;
	}

	memset(mm->lru_record, 0, tot_ctx_num * sizeof(size_t));

	ROHC_LOG_INFO(
		"%s init context num %d\n",
		__func__,
		tot_ctx_num);

	return true;
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
/**
 * @brief Allocate decompressor API
 *
 * There are MAX_DECOMPRESSOR_NUM(8) global g_decompressors for ROHC
 * decompress, and allocate a free decompressor.
 *
 * @see
 *
 * @param		  cid type and max cid info
 * @return		  the index of decompressor
 */
rohc_decomp_id_t rohc_decomp_allocate_decompressor(
		const rohc_cid_type_t cid_type,
		const u8  max_cid,
		const aux_decomp_config_t aux_cfg)
{
	rohc_decomp_id_t idx;
	rohc_decompressor_t *decomp = NULL;

	if (!rohc_validate_cid(cid_type, max_cid))
		return MAX_DECOMPRESSOR_NUM;

	for (idx = 0; idx < MAX_DECOMPRESSOR_NUM; ++idx) {
		if (!g_decompressors[idx].in_use) {
			decomp = &g_decompressors[idx];
			ROHC_LOG_INFO(
				"alloc decompressor[%d]\n",
				idx);

			break;
		}
	}

	if (decomp == NULL)
		return MAX_DECOMPRESSOR_NUM;

	decomp->cid_type = cid_type;
	decomp->max_cid = max_cid;
	decomp->aux_cfg = aux_cfg;

	if (!init_context_management(
		&decomp->ctx_mm,
		((u16)max_cid) + 1))
		return MAX_DECOMPRESSOR_NUM;

	decomp->in_use = true;

	return idx;
}

void rohc_decomp_release_decompressor(const rohc_decomp_id_t id)
{
	if ((id < MAX_DECOMPRESSOR_NUM)
		&& (g_decompressors[id].in_use)) {
		rohc_decompressor_t *decomp = &g_decompressors[id];

		vfree(decomp->ctx_mm.ctx_array);
		vfree(decomp->ctx_mm.lru_record);

		decomp->in_use = false;
		ROHC_LOG_INFO(
			"rohc decompressor %d released\n",
			id);
	}
}

rohc_decomp_status_t rohc_decomp_decompress(
		const rohc_decomp_id_t id,
		net_buf_t *const decomp_pkt,
		const net_buf_t rohc_pkt)
{
	rohc_decompressor_t *decomp = NULL;
	rohc_decomp_status_t ret;

	if ((id >= MAX_DECOMPRESSOR_NUM)
		|| (!g_decompressors[id].in_use))
		return ROHC_DECOMP_INVALID_COMPRESSOR;

	decomp = &g_decompressors[id];

	ret = rohc_decomp_priv_decompress(decomp, decomp_pkt, rohc_pkt);

	return ret;
}

