// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2016 MediaTek Inc.
 */
#include "udc.h"

struct udc_func_info udc_func_table;

/* Register UDC API */
int register_udc_func_deflateInit2(deflateInit2_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.deflateInit2 == NULL) {
		udc_func_table.deflateInit2 = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int deflateInit2_cb(struct z_stream_s *strm,
			int  level, int  method,
			int windowBits, int memLevel,
			int strategy)
{
	deflateInit2_cb_func_t func;
	int ret = 0;

	func = udc_func_table.deflateInit2;
	if (func != NULL) {
		ret = func(strm, level, method, windowBits, memLevel, strategy,
				ZLIB_VERSION, (int)sizeof(struct z_stream_s));
	} else {
		ret = -1;/* E_NO_EXIST */
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_func_deflateSetDict(
	deflateSetDict_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.deflateSetDict == NULL) {
		udc_func_table.deflateSetDict = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int deflateSetDictionary_cb(struct z_stream_s *strm,
	const char *dictionary, unsigned int dictLength)
{
	deflateSetDict_cb_func_t func;
	int ret = 0;

	func = udc_func_table.deflateSetDict;
	if (func != NULL) {
		ret = func(strm, dictionary, dictLength);
	} else {
		ret = -1;/* E_NO_EXIST */
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_func_deflate(deflate_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.deflate == NULL) {
		udc_func_table.deflate = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int deflate_cb(struct z_stream_s *strm, int flush)
{
	deflate_cb_func_t func;
	int ret = 0;

	func = udc_func_table.deflate;
	if (func != NULL) {
		ret = func(strm, flush);
	} else {
		ret = -1;//E_NO_EXIST
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_func_deflateEnd(deflateEnd_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.deflateEnd == NULL) {
		udc_func_table.deflateEnd = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int deflateEnd_cb(struct z_stream_s *strm)
{
	deflateEnd_cb_func_t func;
	int ret = 0;

	func = udc_func_table.deflateEnd;
	if (func != NULL) {
		ret = func(strm);
	} else {
		ret = -1;//E_NO_EXIST
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_func_deflateReset(deflateReset_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.deflateReset == NULL) {
		udc_func_table.deflateReset = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int deflateReset_cb(struct z_stream_s *strm)
{
	deflateReset_cb_func_t func;
	int ret = 0;

	func = udc_func_table.deflateReset;
	if (func != NULL) {
		ret = func(strm);
	} else {
		ret = -1;//E_NO_EXIST
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_func_deflateBound(deflateBound_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.deflateBound == NULL) {
		udc_func_table.deflateBound = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int deflateBound_cb(struct z_stream_s *strm, unsigned long sourceLen)
{
	deflateBound_cb_func_t func;
	int ret = 0;

	func = udc_func_table.deflateBound;
	if (func != NULL) {
		ret = func(strm, sourceLen);
	} else {
		ret = -1;//E_NO_EXIST
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_func_udc_chksum(udc_chksum_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.udc_chksum == NULL) {
		udc_func_table.udc_chksum = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int udc_chksum_cb(struct z_stream_s *strm)
{
	udc_chksum_cb_func_t func;
	unsigned int ret = 0;

	func = udc_func_table.udc_chksum;
	if (func != NULL) {
		ret = func(strm);
	} else {
		ret = -1;//E_NO_EXIST
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_func_udc_QueryPara(udc_QueryPara_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.udc_QueryPara == NULL) {
		udc_func_table.udc_QueryPara = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int udc_QueryPara_cb(struct z_stream_s *strm, int id, void *param)
{
	udc_QueryPara_cb_func_t func;
	unsigned int ret = 0;

	func = udc_func_table.udc_QueryPara;
	if (func != NULL) {
		ret = func(strm, id, param);
	} else {
		ret = -1;//E_NO_EXIST
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_func_udc_GetCmpLen(udc_GetCmpLen_cb_func_t func)
{
	int ret = 0;

	if (udc_func_table.udc_GetCmpLen == NULL) {
		udc_func_table.udc_GetCmpLen = func;
		pr_notice("%s success\n", __func__);
	} else {
		pr_notice("%s fail: registered!\n", __func__);
		ret = -1;
	}

	return ret;
}

int udc_GetCmpLen_cb(struct z_stream_s *strm,
	unsigned char *start, unsigned char *end)
{
	udc_GetCmpLen_cb_func_t func;
	unsigned int ret = 0;

	func = udc_func_table.udc_GetCmpLen;
	if (func != NULL) {
		ret = func(strm, start, end);
	} else {
		ret = -1;//E_NO_EXIST
		pr_notice("exec %s fail: not register!\n", __func__);
	}
	return ret;
}

int register_udc_functions(unsigned int id, void *f)
{
	switch (id) {
	case ID_deflateInit2:
	{
		deflateInit2_cb_func_t func =
			(deflateInit2_cb_func_t)f;

		return register_udc_func_deflateInit2(func);
	}
	case ID_deflateSetDict:
	{
		deflateSetDict_cb_func_t func =
			(deflateSetDict_cb_func_t)f;

		return register_udc_func_deflateSetDict(func);
	}
	case ID_deflateEnd:
	{
		deflateEnd_cb_func_t func =
			(deflateEnd_cb_func_t)f;

		return register_udc_func_deflateEnd(func);
	}
	case ID_deflateReset:
	{
		deflateReset_cb_func_t func =
			(deflateReset_cb_func_t)f;

		return register_udc_func_deflateReset(func);
	}
	case ID_deflate:
	{
		deflate_cb_func_t func =
			(deflate_cb_func_t)f;

		return register_udc_func_deflate(func);
	}
	case ID_deflateBound:
	{
		deflateBound_cb_func_t func =
			(deflateBound_cb_func_t)f;

		return register_udc_func_deflateBound(func);
	}
	case ID_udc_chksum:
	{
		udc_chksum_cb_func_t func =
			(udc_chksum_cb_func_t)f;

		return register_udc_func_udc_chksum(func);
	}
	case ID_udc_QueryPara:
	{
		udc_QueryPara_cb_func_t func =
			(udc_QueryPara_cb_func_t)f;

		return register_udc_func_udc_QueryPara(func);
	}
	case ID_udc_GetCmpLen:
	{
		udc_GetCmpLen_cb_func_t func =
			(udc_GetCmpLen_cb_func_t)f;

		return register_udc_func_udc_GetCmpLen(func);
	}
	default:
		pr_notice("udc func id %d not found\n", id);
		return -1;
	}
}
EXPORT_SYMBOL(register_udc_functions);

void my_free(void *my_param, void *ptr)
{
	struct udc_private_data *p = (struct udc_private_data *)my_param;

	if (p != NULL && p->mem != NULL && p->used > 0) {
		if ((unsigned char *)ptr >= p->mem &&
			(unsigned char *)ptr < p->mem + p->used) {
			p->used = (unsigned char *)ptr - p->mem;
		}
	}
}

void *my_malloc(void *my_param, unsigned int size, unsigned int count)
{
	struct udc_private_data *p = (struct udc_private_data *)my_param;
	unsigned char *ptr;

	if (p == NULL)
		return NULL;
	if (p->mem == NULL || p->size == 0)
		return NULL;
	if (p->size < p->used + size*count)
		return NULL;
	ptr = p->mem + p->used;
	p->used += size*count;
	return (void *)ptr;
}

int udc_init(struct z_stream_s *zcpr, struct udc_private_data *my_param)
{
	int ret = 0;

	my_param->used = 0;
	ret = udc_QueryPara_cb(NULL, UDC_QUERY_WORKSPACE_SIZE, &my_param->size);
	my_param->mem = vmalloc(my_param->size);

	pr_debug("%s:alloc memory:%p\n",  __func__, my_param->mem);

	(*zcpr).zalloc = &my_malloc;
	(*zcpr).zfree = &my_free;
	(*zcpr).opaque = my_param;

	return ret;
}

void udc_deinit(struct z_stream_s *zcpr)
{
	struct udc_private_data *p =
		(struct udc_private_data *)((*zcpr).opaque);

	pr_debug("%s:free memory:%p\n",  __func__, p->mem);
	vfree(p->mem);
}
