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

#include <linux/version.h>
#include <linux/cdev.h> // struct cdev, cdev_init
#include <linux/slab.h>  // kzalloc()
#include <linux/mutex.h>
#include <linux/syscalls.h> // copy_from_user()

#include "mtk_espool.h"
#include "mtk_espool_kernel.h"

//ESPOOL_DEVICE_NAME defined in mtk_espool.h

// const definition
#define MAX_INSTANCE_NUMBER    16    // mi_vdec.c MI_VDEC_DECODER_MAX
#define MAX_ES_Q_SIZE    64    // mi_video.c MAX_ES_BUFFER_COUNT
#define MAX_DEVICE_NAME_LENGHT    32

#define POOLID_HIGH32BIT_MASK    0xffffffff00000000
#define POOLID_LOW32BIT_MASK    0x00000000ffffffff
#define POOLID_SHIFT    32

// ESPOOL_INTERNAL_FLAG use high 16 bit.
#define ESPOOL_INTERNAL_FLAG_PARTIAL_BUFFER    (1 << 16)

#define ESPOOL_DBG_ERR	  1
#define ESPOOL_DBG_INFO   2
#define ESPOOL_DBG_ALL	  3

static unsigned int espool_loglevel = ESPOOL_DBG_ERR;
module_param(espool_loglevel, uint, 0644);

#define ESPOOL_LOG(level, fmt, args...)                                 \
	{                                                                   \
		do {                                                            \
			if (espool_loglevel >= level) {                             \
				pr_info("[%s %d] "fmt, __func__, __LINE__, ##args); \
			}                                                           \
		} while (0);                                                    \
	}

#define GET_UNIC_ID_FROM_POOL_ID(u32UnicId, u64PoolId)   \
	{                                                    \
		u32UnicId = (uint32_t)(u64PoolId & 0xffffffff);  \
	}

// If dev_major == 0, kernel will set major number automaticlly,
// else dev_major will be the device major number.
// You can specify the dev_major when insmod, example:
// insmod mtk-es-buf-mgr.ko dev_major=253
static int dev_major;
module_param(dev_major, int, 0644);
static struct class *es_pool_class;

struct es_pool_dev {
	struct cdev cdev;
};

enum eClientState {
	ESBUF_CLIENT_IDLE,
	ESBUF_CLIENT_OPEN,
	ESBUF_CLIENT_WORKING,
};

typedef struct ESPool_ESDataList_s {
	uint32_t u32Size;
	uint32_t in;
	uint32_t out;
	struct mutex stMutex;
	ESPool_ESInfo_t astESInfo[MAX_ES_Q_SIZE];
} ESPool_ESDataList_t;

typedef struct ESPool_Client_s {
	uint64_t u64PoolId;
	uint32_t u32State;
	ES_Buffer_Info_t *pstESBuffer;
	struct mutex stMutex;
	uint32_t u32Flags;
	uint32_t bSeqWrite;
	ESPool_ESDataList_t stESList;
	uint64_t u64TotalAllocateSize;
	uint64_t u64TotalFreeSize;
} ESPool_Client_t;

static ESPool_Client_t stLocalESBufferControl[MAX_INSTANCE_NUMBER];
static struct mutex stLocalESButterControlMutx;
static struct es_pool_dev *es_pool_devp;

// driver file_operations
static long _ESPool_ioctl(struct file *file, unsigned int cmd, unsigned long data);
static long _ESPool_compact_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
static int _ESPool_Open(struct inode *inode, struct file *file);
static ssize_t _ESPool_Read(struct file *file, char *buff, size_t size, loff_t *offset);
static ssize_t _ESPool_Write(struct file *file, const char *buff,
	size_t size, loff_t *offset);
static int _ESPool_Release(struct inode *inode, struct file *file);

static ESPool_Client_t *_ESPool_FindClient(uint64_t u64PoolId);
static ESPool_Client_t *_ESPool_AddClient(ES_Buffer_Info_t *pstData);
static int _ESPool_RemoveClient(uint64_t u64PoolId);
static char *getCmdName(long cmd);

// declaration
static ESPool_Client_t *_ESPool_FindClient(uint64_t u64PoolId)
{
	int i = 0;
	ESPool_Client_t *pstClient = NULL;

	if (u64PoolId == 0)
		return pstClient;

	for (i = 0; i < MAX_INSTANCE_NUMBER; i++) {
		if (stLocalESBufferControl[i].u64PoolId == u64PoolId) {
			//ESPOOL_LOG(ESPOOL_DBG_INFO, "Found handle %#x index %d\n", hVideo, i);
			pstClient = &stLocalESBufferControl[i];
			break;
		}
	}
	return pstClient;
}

static ESPool_Client_t *_ESPool_AddClient(ES_Buffer_Info_t *pstData)
{
	int i = 0;
	ESPool_Client_t *pstClient = NULL;

	if (pstData == NULL || pstData->PoolID == 0)
		return NULL;

	for (i = 0; i < MAX_INSTANCE_NUMBER; i++) {
		if (stLocalESBufferControl[i].u64PoolId == 0) {
			ESPOOL_LOG(ESPOOL_DBG_INFO, "Add handle %#x index %d\n",
				pstData->u32UnicID, i);
			stLocalESBufferControl[i].u64PoolId = pstData->PoolID;
			stLocalESBufferControl[i].pstESBuffer = pstData;
			pstClient = &stLocalESBufferControl[i];
			break;
		}
	}
	return pstClient;
}

static int _ESPool_RemoveClient(uint64_t u64PoolId)
{
	int i = 0;
	int err = -EINVAL;

	for (i = 0; i < MAX_INSTANCE_NUMBER; i++) {
		if (stLocalESBufferControl[i].u64PoolId == u64PoolId) {
			ESPOOL_LOG(ESPOOL_DBG_INFO, "Remove client %#llx index %d\n",
				u64PoolId, i);
			stLocalESBufferControl[i].u64PoolId = 0;

			if (stLocalESBufferControl[i].pstESBuffer != NULL)
				kfree(stLocalESBufferControl[i].pstESBuffer);

			memset(&stLocalESBufferControl[i], 0, sizeof(ESPool_Client_t));
			err = 0;
			break;
		}
	}
	return err;
}

int ESPool_Create(ESPool_CreateParam_t *pstCreateParam)
{
	ESPool_Client_t *pstClient = NULL;
	ES_Buffer_Info_t *pstESBuffer = NULL;
	int retVal = 0;

	if (pstCreateParam == NULL ||
		pstCreateParam->u64PoolId == 0 ||
		pstCreateParam->u32UnicId == 0) {
		return -EINVAL;
	}
	ESPOOL_LOG(ESPOOL_DBG_INFO, "PoolID[%#llx], Unic Id [%#x]\n",
		pstCreateParam->u64PoolId, pstCreateParam->u32UnicId);

	mutex_lock(&stLocalESButterControlMutx);

	pstClient =  _ESPool_FindClient(pstCreateParam->u64PoolId);
	if (pstClient == NULL) {
		pstESBuffer = kzalloc(sizeof(ES_Buffer_Info_t), GFP_KERNEL);
		if (pstESBuffer == NULL) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "kzalloc fail PoolID[%#llx]\n",
				pstCreateParam->u64PoolId);
			retVal = -ENOMEM;
			goto EXIT;
		}

		pstESBuffer->PoolID = pstCreateParam->u64PoolId;
		pstESBuffer->u32TotalSize = pstCreateParam->u32Size;
		pstESBuffer->u32Flags = pstCreateParam->u32Flags;
		pstESBuffer->u32UnicID = pstCreateParam->u32UnicId;

		pstClient = _ESPool_AddClient(pstESBuffer);
		if (pstClient == NULL) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "only support %d client, PoolID[%#llx]\n",
				MAX_INSTANCE_NUMBER, pstESBuffer->PoolID);
			retVal = -EBUSY;
			kfree(pstESBuffer);
			goto EXIT;
		}
		pstClient->u32Flags |= pstCreateParam->u32Flags;
		pstClient->bSeqWrite = (bool)(pstCreateParam->u32Flags & ESPOOL_FLAG_SEQWRITE_MODE);

		pstESBuffer->u32FreeSize = pstESBuffer->u32TotalSize;

		// create mutex
		mutex_init(&pstClient->stMutex);
		mutex_init(&pstClient->stESList.stMutex);
	} else {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "already exist PoolID[%#llx]\n",
			pstCreateParam->u64PoolId);
		// retVal = -EEXIST;
		goto EXIT;
	}

	pstClient->u32State = ESBUF_CLIENT_OPEN;
	pstCreateParam->u32UnicId = pstESBuffer->u32UnicID;
	ESPOOL_LOG(ESPOOL_DBG_INFO,
		"PoolID[%#llx], flags[%#x], free[%#x], offset[%#x], total[%#x],UnicID[%#x]\n",
		pstESBuffer->PoolID,
		pstESBuffer->u32Flags,
		pstESBuffer->u32FreeSize,
		pstESBuffer->u32Offset,
		pstESBuffer->u32TotalSize,
		pstESBuffer->u32UnicID);

EXIT:
	mutex_unlock(&stLocalESButterControlMutx);
	return retVal;
}
EXPORT_SYMBOL(ESPool_Create);

int ESPool_QueryPoolInfo(ESPool_QueryPoolInfoParam_t *pstData)
{
	ESPool_Client_t *pstClient = NULL;
	int err = 0;

	if (pstData == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "pstData is null\n");
		return -EINVAL;
	}

	pstClient =  _ESPool_FindClient(pstData->u64PoolId);
	if (pstClient == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "MI_ERR_INVALID_HANDLE, PoolId[%#llx]\n",
			pstData->u64PoolId);
		return -EINVAL;
	}

	ESPOOL_LOG(ESPOOL_DBG_INFO, "PoolID[%#llx]\n", pstData->u64PoolId);
	pstData->u32TotalSize = pstClient->pstESBuffer->u32TotalSize;
	pstData->u32UnicId = pstClient->pstESBuffer->u32UnicID;

	ESPOOL_LOG(ESPOOL_DBG_INFO,
		"PoolID[%#llx], flags[%#x], free[%#x], offset[%#x], total[%#x],UnicID[%#x]\n",
		pstClient->pstESBuffer->PoolID,
		pstClient->pstESBuffer->u32Flags,
		pstClient->pstESBuffer->u32FreeSize,
		pstClient->pstESBuffer->u32Offset,
		pstClient->pstESBuffer->u32TotalSize,
		pstClient->pstESBuffer->u32UnicID);

	return err;
}
EXPORT_SYMBOL(ESPool_QueryPoolInfo);

int ESPool_GetBuf(ESPool_GetBufParam_t *pstGetBufParam)
{
	ESPool_Client_t *pstClient = NULL;
	ES_Buffer_Info_t *pstESBuffer = NULL;
	uint32_t u32ReqSize = 0;
	uint32_t u32FreeSize = 0;
	uint32_t u32NewOffset = 0;
	uint32_t u32TotalSize = 0;
	uint32_t u32CurrentOffset = 0;
	int err = 0;

	if (pstGetBufParam == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "pstGetBufParam is NULL\n");
		return -EINVAL;
	}

	u32ReqSize = pstGetBufParam->u32Size;
	pstClient =  _ESPool_FindClient(pstGetBufParam->u64PoolId);
	if (pstClient == NULL || pstClient->pstESBuffer == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "NOT INITED PoolId %#llx\n", pstGetBufParam->u64PoolId);
		return -EINVAL;
	}

	mutex_lock(&pstClient->stMutex);
	pstESBuffer = pstClient->pstESBuffer;
	u32FreeSize = pstESBuffer->u32FreeSize;
	pstClient->u32State = ESBUF_CLIENT_WORKING;
	u32CurrentOffset = pstESBuffer->u32Offset;
	u32TotalSize = pstESBuffer->u32TotalSize;

	if (u32ReqSize == 0)
		goto EXIT;

	if (u32FreeSize < u32ReqSize) {
		err = -ENOMEM;
		pstGetBufParam->u32Size = 0;
		ESPOOL_LOG(ESPOOL_DBG_ERR, "MI_ERR_MEMORY_ALLOCATE\n");
		goto EXIT;
	}

	if ((u32CurrentOffset + u32ReqSize) > u32TotalSize) {
		if (pstClient->bSeqWrite) {
			/*
			 *  If Sequence Write mode is enabled, the user should
			 *  manage two area of the es buffer by itself.
			 *  Example:
			 *    espool buffer for user A:
			 *      total size : 16
			 *      range : [0 : 15]
			 *      current offset : 12
			 *      free size : 10
			 *
			 *    Request from user A:
			 *      8
			 *
			 *    espool return:
			 *      offset : 12
			 *      size : 8
			 *      buffer range : [12:15] and [0:3].
			 *
			 *    espool state then:
			 *      offset : 4
			 *      free : 2
			 */
			u32NewOffset = u32CurrentOffset + u32ReqSize - u32TotalSize;
			pstESBuffer->u32Flags |= ESPOOL_INTERNAL_FLAG_PARTIAL_BUFFER;
			ESPOOL_LOG(ESPOOL_DBG_INFO,
				"ESPOOL_INTERNAL_FLAG_PARTIAL_BUFFER offset %#x, size %#x\n",
				u32NewOffset, u32ReqSize);
		} else {
			u32FreeSize -= (u32TotalSize - u32CurrentOffset);
			if (u32FreeSize < u32ReqSize) {
				err = -ENOMEM;
				pstGetBufParam->u32Size = 0;
				ESPOOL_LOG(ESPOOL_DBG_ERR, "MI_ERR_MEMORY_ALLOCATE\n");
				goto EXIT;
			}

			pstESBuffer->u32Offset = 0;
			u32NewOffset = u32ReqSize;
		}
	} else {
		//for (pstESBuffer->u32Offset + u32ReqSize) == u32TotalSize ,offset should be zero
		u32NewOffset = (pstESBuffer->u32Offset + u32ReqSize) % u32TotalSize;
	}
	pstESBuffer->u32Size = u32ReqSize;
	pstESBuffer->u32FreeSize -= u32ReqSize;
	ESPOOL_LOG(ESPOOL_DBG_INFO,
		"PoolID[%#llx], UnicID[%#x], total[%#x], offset[%#x], free[%#x], request[%#x]\n",
		pstESBuffer->PoolID,
		pstESBuffer->u32UnicID,
		pstESBuffer->u32TotalSize,
		pstESBuffer->u32Offset,
		pstESBuffer->u32FreeSize,
		u32ReqSize);

	pstGetBufParam->u32Offset = pstESBuffer->u32Offset;
	pstGetBufParam->u32Size = pstESBuffer->u32Size;
	pstClient->u64TotalAllocateSize += u32ReqSize;
	ESPOOL_LOG(ESPOOL_DBG_INFO, "TotalAllocateSize[%#llx], Request size [%#x]\n",
		pstClient->u64TotalAllocateSize, u32ReqSize);
	if (pstClient->u32Flags & ESPOOL_FLAG_AUTO_PUSH_ESQ) {
		ESPool_ESInfo_t stESInfo;

		memset(&stESInfo, 0, sizeof(ESPool_ESInfo_t));
		stESInfo.u64PoolId = pstGetBufParam->u64PoolId;
		stESInfo.u32Offset = pstGetBufParam->u32Offset;
		stESInfo.u32Size = pstGetBufParam->u32Size;
		err = ESPool_PushESQ(&stESInfo);
		if (err) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "ESPool_PushESQ return %d", err);
			// goto EXIT;
		}
	}
	pstESBuffer->u32Offset = u32NewOffset;
	pstESBuffer->u32Flags &= ~ESPOOL_INTERNAL_FLAG_PARTIAL_BUFFER;
EXIT:
	mutex_unlock(&pstClient->stMutex);
	return err;
}
EXPORT_SYMBOL(ESPool_GetBuf);

int ESPool_GetFreeSize(ESPool_GetFreeSizeParam_t *pstFreeSize)
{
	ESPool_Client_t *pstClient = NULL;

	pstClient =  _ESPool_FindClient(pstFreeSize->u64PoolId);
	if (pstClient == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "Client==null PoolId[%#llx]\n", pstFreeSize->u64PoolId);
		return -ENOENT;
	}
	mutex_lock(&pstClient->stMutex);
	pstFreeSize->u32FreeSize = pstClient->pstESBuffer->u32FreeSize;
	mutex_unlock(&pstClient->stMutex);

	ESPOOL_LOG(ESPOOL_DBG_INFO, "PoolID[%#llx], u32FreeSize[%#x]\n",
		pstFreeSize->u64PoolId,
		pstFreeSize->u32FreeSize);
	return 0;
}
EXPORT_SYMBOL(ESPool_GetFreeSize);

int ESPool_FreeBuf(ESPool_FreeBufParam_t *pstData)
{
	ESPool_Client_t *pstClient = NULL;
	uint32_t u32CurrentFreeSize = 0;
	uint32_t u32TryToFreeSize = 0;
	ES_Buffer_Info_t *pstESBuffer = NULL;
	int err = 0;

	if (pstData == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "pstData is NULL\n");
		return -EINVAL;
	}
	u32TryToFreeSize = pstData->u32Size;

	pstClient =  _ESPool_FindClient(pstData->u64PoolId);
	if (pstClient == NULL || pstClient->pstESBuffer == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "MI_ERR_NOT_INITED PoolId [%#llx]\n",
			pstData->u64PoolId);
		return -EINVAL;
	}

	mutex_lock(&pstClient->stMutex);
	u32CurrentFreeSize = pstClient->pstESBuffer->u32FreeSize;
	pstESBuffer = pstClient->pstESBuffer;

	ESPOOL_LOG(ESPOOL_DBG_INFO,
		"PoolID[%#llx], total size[%#x], offset[%#x],free size[%#x], try to free[%#x]\n",
		pstESBuffer->PoolID,
		pstESBuffer->u32TotalSize,
		pstESBuffer->u32Offset,
		pstESBuffer->u32FreeSize,
		u32TryToFreeSize);

	if (u32TryToFreeSize + u32CurrentFreeSize <= pstESBuffer->u32TotalSize) {
		pstESBuffer->u32FreeSize += u32TryToFreeSize;
		ESPOOL_LOG(ESPOOL_DBG_INFO, "Return buffer ok, size %#x\n", u32TryToFreeSize);
	} else {
		// You do not have so much buffer to free !!
		err = -EINVAL;
		ESPOOL_LOG(ESPOOL_DBG_ERR,
			"Error: You do not have so much buffer to free ! PoolID[%#llx]\n",
			pstESBuffer->PoolID);

		goto EXIT;
	}

	pstClient->u64TotalFreeSize += u32TryToFreeSize;
	ESPOOL_LOG(ESPOOL_DBG_INFO, "u64TotalFreeSize[%#llx] Request free size %#x\n",
		pstClient->u64TotalFreeSize, u32TryToFreeSize);

EXIT:
	mutex_unlock(&pstClient->stMutex);
	return err;
}
EXPORT_SYMBOL(ESPool_FreeBuf);

// queue ES data
int ESPool_PushESQ(ESPool_ESInfo_t *pstData)
{
	int err = 0;
	ESPool_Client_t *pstClient = NULL;

	if (pstData == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "pstData is NULL handle\n");
		return -EINVAL;
	}
	pstClient = _ESPool_FindClient(pstData->u64PoolId);

	if (pstClient == NULL || pstClient->pstESBuffer == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "MI_ERR_NOT_INITED PoolId %#llx\n", pstData->u64PoolId);
		return -EINVAL;
	}
	mutex_lock(&pstClient->stESList.stMutex);
	if (pstClient->stESList.u32Size == MAX_ES_Q_SIZE) {
		ESPOOL_LOG(ESPOOL_DBG_INFO, "stESList full PoolId %#llx\n", pstData->u64PoolId);
		err = -EINVAL;
		goto EXIT;
	}
	memcpy(&(pstClient->stESList.astESInfo[pstClient->stESList.in]),
		pstData, sizeof(ESPool_ESInfo_t));
	pstClient->stESList.in++;
	if (pstClient->stESList.in == MAX_ES_Q_SIZE)
		pstClient->stESList.in = 0;

	pstClient->stESList.u32Size++;
EXIT:
	mutex_unlock(&pstClient->stESList.stMutex);
	ESPOOL_LOG(ESPOOL_DBG_INFO,
		"ESQ in[%#x],out[%#x]. Push: PoolId[%#llx], offset[%#x], size[%#x]\n",
		pstClient->stESList.in, pstClient->stESList.out,
		pstData->u64PoolId, pstData->u32Offset, pstData->u32Size);

	return err;
}
EXPORT_SYMBOL(ESPool_PushESQ);

// dequeue ES data
int ESPool_PopESQ(ESPool_ESInfo_t *pstData)
{
	int err = 0;
	ESPool_Client_t *pstClient = NULL;

	if (pstData == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "pstData is NULL\n");
		return -EINVAL;
	}

	pstClient = _ESPool_FindClient(pstData->u64PoolId);
	if (pstClient == NULL || pstClient->pstESBuffer == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "MI_ERR_NOT_INITED\n");
		return -EINVAL;
	}
	mutex_lock(&pstClient->stESList.stMutex);
	if (pstClient->stESList.u32Size == 0) {
		ESPOOL_LOG(ESPOOL_DBG_INFO, "stESList empty\n");
		err = -EINVAL;
		goto EXIT;
	}
	memcpy(pstData, &(pstClient->stESList.astESInfo[pstClient->stESList.out]),
			sizeof(ESPool_ESInfo_t));
	memset(&(pstClient->stESList.astESInfo[pstClient->stESList.out]), 0,
		sizeof(ESPool_ESInfo_t));
	pstClient->stESList.out++;
	if (pstClient->stESList.out == MAX_ES_Q_SIZE)
		pstClient->stESList.out = 0;

	pstClient->stESList.u32Size--;
EXIT:
	mutex_unlock(&pstClient->stESList.stMutex);
	ESPOOL_LOG(ESPOOL_DBG_INFO,
		"ESQ in[%#x],out[%#x]. Pop: PoolId[%#llx], offset[%#x], size[%#x]\n",
		pstClient->stESList.in, pstClient->stESList.out,
		pstData->u64PoolId, pstData->u32Offset, pstData->u32Size);
	return 0;
}
EXPORT_SYMBOL(ESPool_PopESQ);

int ESPool_Reset(uint64_t u64PoolId)
{
	int i = 0;
	ESPool_Client_t *pstClient = NULL;

	ESPOOL_LOG(ESPOOL_DBG_INFO, "reset client %#llx\n", u64PoolId);

	pstClient = _ESPool_FindClient(u64PoolId);
	if (pstClient == NULL || pstClient->pstESBuffer == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "MI_ERR_NOT_INITED\n");
		return -EINVAL;
	}
	mutex_lock(&pstClient->stMutex);
	mutex_lock(&pstClient->stESList.stMutex);

	pstClient->pstESBuffer->u32FreeSize = pstClient->pstESBuffer->u32TotalSize;
	pstClient->pstESBuffer->u32Offset = 0;
	pstClient->u32State = ESBUF_CLIENT_OPEN;
	pstClient->u64TotalAllocateSize = 0;
	pstClient->u64TotalFreeSize = 0;

	if (pstClient->stESList.u32Size == 0) {
		ESPOOL_LOG(ESPOOL_DBG_INFO, "stESList empty\n");
		goto EXIT;
	}
	pstClient->stESList.in = 0;
	pstClient->stESList.out = 0;
	pstClient->stESList.u32Size = 0;
	for (i = 0; i < MAX_ES_Q_SIZE; i++)
		memset(&(pstClient->stESList.astESInfo[i]), 0, sizeof(ESPool_ESInfo_t));

EXIT:
	mutex_unlock(&pstClient->stMutex);
	mutex_unlock(&pstClient->stESList.stMutex);

	return 0;
}
EXPORT_SYMBOL(ESPool_Reset);

int ESPool_Destroy(ESPool_DestroyParam_t *pstData)
{
	int retVal = 0;

	if (pstData == NULL) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "pstData is NULL\n");
		return -EINVAL;
	}

	ESPOOL_LOG(ESPOOL_DBG_INFO, "Destroy client %#llx\n", pstData->u64PoolId);
	mutex_lock(&stLocalESButterControlMutx);
	retVal = _ESPool_RemoveClient(pstData->u64PoolId);
	mutex_unlock(&stLocalESButterControlMutx);
	return retVal;
}
EXPORT_SYMBOL(ESPool_Destroy);

static const struct file_operations es_pool_ops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = _ESPool_ioctl,
	.compat_ioctl = _ESPool_compact_ioctl,
	.open = _ESPool_Open,
	.read = _ESPool_Read,
	.write = _ESPool_Write,
	.release = _ESPool_Release,
};

static char *getCmdName(long cmd)
{
	switch (cmd) {
	case VIDIOC_ESBUFC_OPEN: return "VIDIOC_ESBUFC_OPEN";
	case VIDIOC_ESBUFC_QUERY_POOL_INFO: return "VIDIOC_ESBUFC_QUERY_POOL_INFO";
	case VIDIOC_ESBUFC_ALLOCATE: return "VIDIOC_ESBUFC_ALLOCATE";
	case VIDIOC_ESBUFC_FREE_SIZE: return "VIDIOC_ESBUFC_FREE_SIZE";
	case VIDIOC_ESBUFC_EXIT: return "VIDIOC_ESBUFC_EXIT";
	case VIDIOC_ESBUFC_VACANCY: return "VIDIOC_ESBUFC_VACANCY";
	case VIDIOC_ESBUFC_SET_SEQ_WRITE: return "VIDIOC_ESBUFC_SET_SEQ_WRITE";

	case IOC_ESPOOL_CREATE: return "IOC_ESPOOL_CREATE";
	case IOC_ESPOOL_QUERY_POOL_INFO: return "IOC_ESPOOL_QUERY_POOL_INFO";
	case IOC_ESPOOL_FREE_BUF: return "IOC_ESPOOL_FREE_BUF";
	case IOC_ESPOOL_DESTROY: return "IOC_ESPOOL_DESTROY";
	case IOC_ESPOOL_GET_FREE_SIZE: return "IOC_ESPOOL_GET_FREE_SIZE";
	case IOC_ESPOOL_SET_SEQ_WRITE_MODE: return "IOC_ESPOOL_SET_SEQ_WRITE_MODE";
	case IOC_ESPOOL_PUSH_ESQ: return "IOC_ESPOOL_PUSH_ESQ";
	case IOC_ESPOOL_POP_ESQ: return "IOC_ESPOOL_POP_ESQ";
	case IOC_ESPOOL_RESET: return "IOC_ESPOOL_RESET";
	default:
		break;
	}
	return "unknown";
}

static int _ESPool_DeprecatedIoctl(unsigned int cmd, unsigned long data)
{
	int retVal = 0;
	ES_Buffer_Info_t stESBufferInfo;

	memset(&stESBufferInfo, 0, sizeof(ES_Buffer_Info_t));
	if (copy_from_user(&stESBufferInfo, (ES_Buffer_Info_t *)data,
		sizeof(ES_Buffer_Info_t))) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
		return -EINVAL;
	}
	// For deprecated cmd
	switch (cmd) {
	case VIDIOC_ESBUFC_OPEN:
	{
		ESPool_CreateParam_t stCreate;

		stCreate.u64PoolId = stESBufferInfo.PoolID;
		stCreate.u32Size = stESBufferInfo.u32Size;
		stCreate.u32Flags =  stESBufferInfo.u32Flags;
		GET_UNIC_ID_FROM_POOL_ID(stCreate.u32UnicId, stESBufferInfo.PoolID);
		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stESBufferInfo.PoolID);

		retVal = ESPool_Create(&stCreate);
		if (retVal != 0)
			goto EXIT;

		//stESBufferInfo.PoolID = stCreate.u64PoolId;
		stESBufferInfo.u32TotalSize = stCreate.u32Size;
		stESBufferInfo.u32Flags = stCreate.u32Flags;
		stESBufferInfo.u32UnicID = stCreate.u32UnicId;
		if (copy_to_user((ES_Buffer_Info_t *)data, &stESBufferInfo,
			sizeof(ES_Buffer_Info_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s copy_to_user fail, PoolID[%#llx]\n",
				getCmdName(cmd), stESBufferInfo.PoolID);
			retVal = -EINVAL;
			goto EXIT;
		}

		break;
	}
	case VIDIOC_ESBUFC_VACANCY:  // Caller:MI_VIDEO; INPUT:UnicID; OUTPUT: RemainBufferSize
	{
		ESPool_Client_t *pstClient = NULL;

		pstClient =  _ESPool_FindClient(stESBufferInfo.PoolID);
		if (pstClient == NULL) {
			retVal = -ENOENT;
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s Client==null PoolId[%#llx]\n",
				getCmdName(cmd), stESBufferInfo.PoolID);
			goto EXIT;
		}
		mutex_lock(&pstClient->stMutex);
		stESBufferInfo.u32FreeSize = pstClient->pstESBuffer->u32FreeSize;
		mutex_unlock(&pstClient->stMutex);

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s: PoolID[%#llx], u32TotalSize[%#x]\n",
			getCmdName(cmd),
			stESBufferInfo.PoolID,
			stESBufferInfo.u32TotalSize);
		if (copy_to_user((ES_Buffer_Info_t *)data, &stESBufferInfo,
			sizeof(ES_Buffer_Info_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s copy_to_user fail, PoolID[%#llx]\n",
				getCmdName(cmd), stESBufferInfo.PoolID);
			retVal = -EINVAL;
			goto EXIT;
		}
		break;
	}
	case VIDIOC_ESBUFC_QUERY_POOL_INFO: // Caller:DRM_CA;INPUT:PoolID,SeqWrite;OUTPUT:UnicID
	{
		int err = 0;
		ESPool_QueryPoolInfoParam_t stPoolInfo;

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stESBufferInfo.PoolID);

		stPoolInfo.u64PoolId = stESBufferInfo.PoolID;
		err = ESPool_QueryPoolInfo(&stPoolInfo);
		if (err != 0) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s fail %d, PoolID[%#llx]\n",
				getCmdName(cmd), err, stESBufferInfo.PoolID);
			retVal = -ENOMEM;
			goto EXIT;
		}
		stESBufferInfo.u32TotalSize = stPoolInfo.u32TotalSize;
		stESBufferInfo.u32UnicID = stPoolInfo.u32UnicId;
		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s: PoolID[%#llx], u32TotalSize[%#x]\n",
			getCmdName(cmd),
			stESBufferInfo.PoolID,
			stESBufferInfo.u32TotalSize);
		if (copy_to_user((ES_Buffer_Info_t *)data, &stESBufferInfo,
			sizeof(ES_Buffer_Info_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s copy_to_user fail, PoolID[%#llx]\n",
				getCmdName(cmd), stESBufferInfo.PoolID);
			retVal = -EINVAL;
			goto EXIT;
		}
		break;
	}
	case VIDIOC_ESBUFC_ALLOCATE:
	// Caller:DRM_CA;INPUT:UnicID,RequestBufSize;OUTPUT:Offset,Boundary,flag=true when wrap
	{
		ESPool_GetBufParam_t stGetBuf;

		memset(&stGetBuf, 0, sizeof(ESPool_GetBufParam_t));
		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx] u32Size[%#x]\n",
			getCmdName(cmd), stESBufferInfo.PoolID, stESBufferInfo.u32Size);
		stGetBuf.u64PoolId = stESBufferInfo.PoolID;
		stGetBuf.u32Size = stESBufferInfo.u32Size;
		retVal = ESPool_GetBuf(&stGetBuf);
		if (retVal != 0) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s fail %d, PoolID[%#llx]\n",
				getCmdName(cmd), retVal, stESBufferInfo.PoolID);
			retVal = -ENOMEM;
			goto EXIT;
		}
		stESBufferInfo.u32Size = stGetBuf.u32Size;
		stESBufferInfo.u32Offset = stGetBuf.u32Offset;

		if (copy_to_user((ES_Buffer_Info_t *)data, &stESBufferInfo,
				sizeof(ES_Buffer_Info_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_to_user fail, handle\n");
			retVal = -EINVAL;
			goto EXIT;
		}
		break;
	}

	case VIDIOC_ESBUFC_FREE_SIZE:  // Caller:MI_VIDEO; INPUT:UnicID; OUTPUT:None
	{
		ESPool_FreeBufParam_t stFreeParam;

		stFreeParam.u64PoolId = stESBufferInfo.PoolID;
		stFreeParam.u32Size = stESBufferInfo.u32Size;

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stESBufferInfo.PoolID);
		retVal = ESPool_FreeBuf(&stFreeParam);
		break;
	}
	case VIDIOC_ESBUFC_EXIT:
	{
		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stESBufferInfo.PoolID);
		mutex_lock(&stLocalESButterControlMutx);
		retVal = _ESPool_RemoveClient(stESBufferInfo.PoolID);
		mutex_unlock(&stLocalESButterControlMutx);
		break;
	}
	case VIDIOC_ESBUFC_SET_SEQ_WRITE:  // Caller DRM_CA
	{
		ESPool_Client_t *pstClient = NULL;

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stESBufferInfo.PoolID);

		pstClient =  _ESPool_FindClient(stESBufferInfo.PoolID);
		if (pstClient == NULL) {
			retVal = -ENOENT;
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s Client==null PoolId[%#llx]\n",
				getCmdName(cmd), stESBufferInfo.PoolID);
			goto EXIT;
		}
		mutex_lock(&stLocalESButterControlMutx);
		if (pstClient->u32State == ESBUF_CLIENT_OPEN) {
			pstClient->bSeqWrite =
				(bool)(stESBufferInfo.u32Flags & ESPOOL_FLAG_SEQWRITE_MODE);
			ESPOOL_LOG(ESPOOL_DBG_INFO, "ESPOOL_FLAG_SEQWRITE_MODE\n");
		}

		mutex_unlock(&stLocalESButterControlMutx);
		break;
	}
	default:
		ESPOOL_LOG(ESPOOL_DBG_ERR, "unknown ioctl cmd [%#x]\n", cmd);
		break;
	}
EXIT:
	return retVal;
}

static int _ESPool_DoIoctl(unsigned int cmd, unsigned long data)
{
	int retVal = 0;

	switch (cmd) {
	case IOC_ESPOOL_CREATE:    // Caller:MI_VIDEO; INPUT:ES Buffer fd; OUTPUT: PoolID
	{
		ESPool_CreateParam_t stCreateParam;

		if (copy_from_user(&stCreateParam, (void *)data, sizeof(ESPool_CreateParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			return -EINVAL;
		}

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx] flags[%#x]\n", getCmdName(cmd),
			stCreateParam.u64PoolId, stCreateParam.u32Flags);

		retVal = ESPool_Create(&stCreateParam);

		if (copy_to_user((void *)data, &stCreateParam, sizeof(ESPool_CreateParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s copy_to_user fail, PoolID[%#llx]\n",
				getCmdName(cmd), stCreateParam.u64PoolId);
			retVal = -EINVAL;
			goto EXIT;
		}
		break;
	}
	case IOC_ESPOOL_GET_FREE_SIZE:	// Caller:MI_VIDEO; INPUT:UnicID; OUTPUT: RemainBufferSize
	{
		ESPool_GetFreeSizeParam_t stGetFreeSizeParam;

		if (copy_from_user(&stGetFreeSizeParam, (void *)data,
			sizeof(ESPool_GetFreeSizeParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			return -EINVAL;
		}
		retVal = ESPool_GetFreeSize(&stGetFreeSizeParam);
		if (retVal != 0) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s fail %d, PoolID[%#llx]\n",
				getCmdName(cmd), retVal, stGetFreeSizeParam.u64PoolId);
			goto EXIT;
		}
		if (copy_to_user((void *)data, &stGetFreeSizeParam,
			sizeof(ESPool_GetFreeSizeParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s copy_to_user fail, PoolID[%#llx]\n",
				getCmdName(cmd), stGetFreeSizeParam.u64PoolId);
			retVal = -EINVAL;
			goto EXIT;
		}

		break;
	}
	case IOC_ESPOOL_QUERY_POOL_INFO: // Caller:DRM_CA;INPUT:PoolID,SeqWrite;OUTPUT:UnicID
	{
		ESPool_QueryPoolInfoParam_t stQueryPoolInfo;
		int err = 0;

		if (copy_from_user(&stQueryPoolInfo, (void *)data, sizeof(stQueryPoolInfo))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			return -EINVAL;
		}

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stQueryPoolInfo.u64PoolId);

		err = ESPool_QueryPoolInfo(&stQueryPoolInfo);

		if (err != 0) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s fail %d, PoolID[%#llx]\n",
				getCmdName(cmd), err, stQueryPoolInfo.u64PoolId);
			retVal = -err;
			goto EXIT;
		}

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s: PoolID[%#llx], UnicID[%#x], u32TotalSize[%#x]\n",
			getCmdName(cmd),
			stQueryPoolInfo.u64PoolId,
			stQueryPoolInfo.u32UnicId,
			stQueryPoolInfo.u32TotalSize);
		if (copy_to_user((void *)data, &stQueryPoolInfo,
			sizeof(ESPool_QueryPoolInfoParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s copy_to_user fail, PoolID[%#llx]\n",
				getCmdName(cmd), stQueryPoolInfo.u64PoolId);
			retVal = -EINVAL;
			goto EXIT;
		}
		break;
	}
	case IOC_ESPOOL_GET_BUF:
	// Caller:DRM_CA;INPUT:UnicID,RequestBufSize;OUTPUT:Offset,Boundary,flag=true when wrap
	{
		ESPool_GetBufParam_t stGetBuf;

		if (copy_from_user(&stGetBuf, (void *)data, sizeof(ESPool_GetBufParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			return -EINVAL;
		}
		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx] size[%#x]\n",
			getCmdName(cmd), stGetBuf.u64PoolId, stGetBuf.u32Size);

		retVal = ESPool_GetBuf(&stGetBuf);
		if (retVal) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "ESPool_GetBuf return %d", retVal);
			goto EXIT;
		}

		if (copy_to_user((void *)data, &stGetBuf, sizeof(ESPool_GetBufParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_to_user fail\n");
			retVal = -EINVAL;
			goto EXIT;
		}
		break;
	}
	case IOC_ESPOOL_FREE_BUF:  // Caller:MI_VIDEO; INPUT:UnicID; OUTPUT:None
	{
		ESPool_FreeBufParam_t stFreeBuf;

		if (copy_from_user(&stFreeBuf, (void *)data,
			sizeof(ESPool_FreeBufParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			return -EINVAL;
		}

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx] free size %#x\n",
			getCmdName(cmd), stFreeBuf.u64PoolId, stFreeBuf.u32Size);
		retVal = ESPool_FreeBuf(&stFreeBuf);
		break;
	}

	case IOC_ESPOOL_DESTROY:
	{
		ESPool_DestroyParam_t stDestroy;

		if (copy_from_user(&stDestroy, (void *)data,
			sizeof(ESPool_DestroyParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			return -EINVAL;
		}
		retVal = ESPool_Destroy(&stDestroy);
		if (retVal) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "ESPool_Destroy return %d", retVal);
			goto EXIT;
		}
		break;
	}
	case IOC_ESPOOL_SET_SEQ_WRITE_MODE:  // Caller DRM_CA
	{
		ESPool_SetSeqModeParam_t stSetSeqMode;
		ESPool_Client_t *pstClient = NULL;

		if (copy_from_user(&stSetSeqMode, (void *)data,
			sizeof(ESPool_SetSeqModeParam_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			return -EINVAL;
		}
		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stSetSeqMode.u64PoolId);
		pstClient =  _ESPool_FindClient(stSetSeqMode.u64PoolId);
		if (pstClient == NULL) {
			retVal = -ENOENT;
			ESPOOL_LOG(ESPOOL_DBG_ERR, "%s Client==null PoolId[%#llx]\n",
				getCmdName(cmd), stSetSeqMode.u64PoolId);
			goto EXIT;
		}
		mutex_lock(&stLocalESButterControlMutx);
		if (pstClient->u32State == ESBUF_CLIENT_OPEN) {
			pstClient->bSeqWrite = (uint32_t)stSetSeqMode.u32SeqMode;
			ESPOOL_LOG(ESPOOL_DBG_INFO, "ESPOOL_FLAG_SEQWRITE_MODE : %d\n",
				stSetSeqMode.u32SeqMode);
		}

		mutex_unlock(&stLocalESButterControlMutx);
		break;
	}
	case IOC_ESPOOL_PUSH_ESQ:
	{
		ESPool_ESInfo_t stESInfo;

		if (copy_from_user(&stESInfo, (void *)data, sizeof(ESPool_ESInfo_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			return -EINVAL;
		}

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stESInfo.u64PoolId);
		retVal = ESPool_PushESQ(&stESInfo);
		break;
	}
	case IOC_ESPOOL_POP_ESQ:
	{
		ESPool_ESInfo_t stESInfo;

		if (copy_from_user(&stESInfo, (void *)data, sizeof(ESPool_ESInfo_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			retVal = -EINVAL;
			goto EXIT;
		}

		ESPOOL_LOG(ESPOOL_DBG_INFO, "%s PoolID[%#llx]\n",
			getCmdName(cmd), stESInfo.u64PoolId);

		retVal = ESPool_PopESQ(&stESInfo);
		if (retVal) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "ESPool_PopESQ return %d", retVal);
			goto EXIT;
		}
		if (copy_to_user((void *)data, &stESInfo, sizeof(ESPool_ESInfo_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_to_user fail, PoolId[%#llx]\n",
				stESInfo.u64PoolId);
			retVal = -EINVAL;
			goto EXIT;
		}
		break;
	}
	case IOC_ESPOOL_RESET:
	{
		uint64_t u64PoolId = 0;

		if (copy_from_user(&u64PoolId, (void *)data, sizeof(uint64_t))) {
			ESPOOL_LOG(ESPOOL_DBG_ERR, "copy_from_user fail\n");
			retVal = -EINVAL;
			goto EXIT;
		}
		ESPOOL_LOG(ESPOOL_DBG_INFO, "reset client %#llx\n", u64PoolId);
		retVal = ESPool_Reset(u64PoolId);
		break;
	}
	default:
		ESPOOL_LOG(ESPOOL_DBG_ERR, "unknown ioctl cmd [%#x]\n",
			cmd);
		break;
	};
EXIT:
	return retVal;
}

static long _ESPool_ioctl(struct file *file, unsigned int cmd, unsigned long data)
{
	int retVal = 0;

	switch (cmd) {
	case VIDIOC_ESBUFC_OPEN:
	case VIDIOC_ESBUFC_QUERY_POOL_INFO:
	case VIDIOC_ESBUFC_ALLOCATE:
	case VIDIOC_ESBUFC_FREE_SIZE:
	case VIDIOC_ESBUFC_EXIT:
	case VIDIOC_ESBUFC_VACANCY:
	case VIDIOC_ESBUFC_SET_SEQ_WRITE:
		retVal = _ESPool_DeprecatedIoctl(cmd, data);
		break;

	case IOC_ESPOOL_CREATE:
	case IOC_ESPOOL_QUERY_POOL_INFO:
	case IOC_ESPOOL_GET_BUF:
	case IOC_ESPOOL_FREE_BUF:
	case IOC_ESPOOL_DESTROY:
	case IOC_ESPOOL_GET_FREE_SIZE:
	case IOC_ESPOOL_SET_SEQ_WRITE_MODE:
	case IOC_ESPOOL_PUSH_ESQ:
	case IOC_ESPOOL_POP_ESQ:
	case IOC_ESPOOL_RESET:
		retVal = _ESPool_DoIoctl(cmd, data);
		break;
	default:
		break;
	}
	return retVal;
}

static long _ESPool_compact_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	return filp->f_op->unlocked_ioctl(filp, cmd, arg);
}

static int _ESPool_Open(struct inode *inode, struct file *file)
{
	int retVal = 0;

	ESPOOL_LOG(ESPOOL_DBG_INFO, "open device node\n");
	return retVal;
}
static ssize_t _ESPool_Read(struct file *file, char *buff, size_t size, loff_t *offset)
{
	int retVal = 0;

	ESPOOL_LOG(ESPOOL_DBG_INFO, "buf size %zu\n", size);
	retVal = size;
	return retVal;
}

static ssize_t _ESPool_Write(struct file *file, const char *buff, size_t size, loff_t *offset)
{
	int retVal = 0;

	ESPOOL_LOG(ESPOOL_DBG_INFO, "size %zu\n", size);
	retVal = size;
	return retVal;
}

static int _ESPool_Release(struct inode *inode, struct file *file)
{
	int retVal = 0;

	return retVal;
}
static int __init ESPool_ModuleInit(void)
{
	int retVal = 0;
	dev_t dev_num = MKDEV(dev_major, 0);

	ESPOOL_LOG(ESPOOL_DBG_INFO, " Enter\n");

	if (dev_major != 0) {
		retVal = register_chrdev_region(dev_num, 1, ESPOOL_DEVICE_NAME);
	} else {
		retVal = alloc_chrdev_region(&dev_num, 0, 1, ESPOOL_DEVICE_NAME);
		dev_major = MAJOR(dev_num);
	}
	if (retVal != 0) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "register chrdev fail return %d\n", retVal);
		return retVal;
	}

#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
	es_pool_class = class_create(THIS_MODULE, "es_pool");
#else
	es_pool_class = class_create("es_pool");
#endif
	if (IS_ERR(es_pool_class)) {
		ESPOOL_LOG(ESPOOL_DBG_ERR, "class_create fail\n");
		return PTR_ERR(es_pool_class);
	}

	es_pool_devp = kzalloc(sizeof(struct es_pool_dev), GFP_KERNEL);
	if (!es_pool_devp) {
		retVal = -ENOMEM;
		ESPOOL_LOG(ESPOOL_DBG_ERR, "allocate memory  fail error %d\n", retVal);
		return retVal;
	}

	cdev_init(&es_pool_devp->cdev, &es_pool_ops);
	es_pool_devp->cdev.owner = THIS_MODULE;
	retVal = cdev_add(&es_pool_devp->cdev, dev_num, 1);
	if (retVal != 0)
		ESPOOL_LOG(ESPOOL_DBG_ERR, "cdev_add fail error %d\n", retVal);

	device_create(es_pool_class, NULL, dev_num, NULL, ESPOOL_DEVICE_NAME);

	memset(stLocalESBufferControl, 0, sizeof(stLocalESBufferControl));
	mutex_init(&stLocalESButterControlMutx);
	return retVal;
}

static void __exit ESPool_ModuleDeInit(void)
{
	cdev_del(&es_pool_devp->cdev);
	unregister_chrdev(dev_major, ESPOOL_DEVICE_NAME);
	// WARNING:NEEDLESS_IF: kfree(NULL) is safe and NULL check is probably not required
	kfree(es_pool_devp);

	class_destroy(es_pool_class);
	mutex_destroy(&stLocalESButterControlMutx);
}
const struct file_operations *ESPool_GetOp(void)
{
	return &es_pool_ops;
}

module_init(ESPool_ModuleInit);
module_exit(ESPool_ModuleDeInit);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Mediatek Video ES Buffer Manager");


