/****************************************************************************
Copyright(c) 2013 DTS INSIGHT CORPORATION
 *  Copyright(c) 2013 DTS INSIGHT CORPORATION
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 Function: System Macro Trace Device Driver : Software Model
*****************************************************************************/

/* Include specification ****************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/sched.h>

#include <linux/syscalls.h>
#include <linux/hrtimer.h>

#include <linux/kthread.h>
#include <linux/delay.h>
#include <smt/SMTAPI.h>

#include <linux/version.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include <linux/vmalloc.h>
#include "SMTDef.h"
#include "smtBufferConfig.h"

#if KERNEL_VERSION(5,4,83) <= LINUX_VERSION_CODE
#include <linux/ktime.h>
#include <linux/timekeeping.h>
#endif

//#define DEV_SMTIF // Switch smtif from the /proc directory to the /dev directory
#ifdef DEV_SMTIF
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#define DEVICE_NAME "smtif"
#define CLASS_NAME "smtif_class"
#endif // DEV_SMTIF


/* Constant definition ******************************************************/
/* Select SMT Interval Process Info. output */

/*------------------------------------------*/

#define	PROC_NAME	"driver/smtdat"
#define	IF_PROC_NAME	"driver/smtif"
#define	SW_PROC_NAME	"smtsw"

#define	_SMT_OS_SWITCH_DATA_SIZE_IDLE		1
#define	_SMT_OS_SWITCH_DATA_SIZE_THREAD		3
#define	_SMT_OS_SWITCH_TYPE_MASK			0x000F0000
#define SMT_PS_BUF_INIT						-1
#define SMT_SW_IDLE							-1
#define SMT_CORE_DEF						4

#define SMT_PUL_SIZ		0x40000
#define SMT_DAT_OFFSET	3			/* Timer Stamp(2)+Core ID(1) */
#define SMT_MAX_DATA_SIZE  0xFFFFFFFF

#define SMT_INIT_PID -1
#define SMT_FILE_WRITTEING_STATUS_PID -2

#define SMT_CNTVCT_HEADER       0xAD250000
#define SMT_CNTVCT_HEADER_SIZ   0x3

#define ONE_TIME_MODE   0
#define FILE_RING_MODE  1
#define CONTINUOUS_MODE 2
#define FILE_KRING_MODE 3
#define BOOT_MODE       0xFFFFFFFF

#define SMT_BUF_NORMAL  0
#define SMT_BUF_RING    1

//#define SMT_SEM_CONTROL
//#define SMT_LOGMODE_GVM

/* Internal variables *******************************************************/
struct smt_buffer {
	_SMT_UNSIGNED_32BIT_INTEGER vacant;
	_SMT_UNSIGNED_32BIT_INTEGER wp;
	_SMT_UNSIGNED_32BIT_INTEGER rp;
	_SMT_UNSIGNED_32BIT_INTEGER sts;
	_SMT_UNSIGNED_32BIT_INTEGER old_p;
	_SMT_UNSIGNED_32BIT_INTEGER new_p;
	_SMT_UNSIGNED_32BIT_INTEGER mode;
	_SMT_UNSIGNED_32BIT_INTEGER data[SMT_BUF_SIZE];
	_SMT_UNSIGNED_32BIT_INTEGER buf_type;
	_SMT_UNSIGNED_32BIT_INTEGER cntvct;
};
struct smt_buffer	*smt_buf = NULL;


int smt_daemon_pid = SMT_INIT_PID;


struct smt_device {
	spinlock_t 				lock;
	int						opencount;
};
struct smt_device *smt_device = NULL;


static int  isFuncData(_SMT_UNSIGNED_32BIT_INTEGER *data);
void update_oldest_rp(_SMT_UNSIGNED_32BIT_INTEGER length);
void next_packet_pos(_SMT_UNSIGNED_32BIT_INTEGER *next_packet);
static int  getAPIDataSize(_SMT_UNSIGNED_32BIT_INTEGER APIHeadData);

#if KERNEL_VERSION(5,4,83) <= LINUX_VERSION_CODE
void _SMT_GetTimeStamp(_SMT_UNSIGNED_32BIT_INTEGER *sec, _SMT_UNSIGNED_32BIT_INTEGER *nano_sec);
#endif

int smt_output_flag = _SMT_DISABLE;
int	smt_sigtrap_flg = 0;
#ifdef SMT_SEM_CONTROL
static struct semaphore smt_sem;
#endif

_SMT_UNSIGNED_32BIT_INTEGER g_ins_header = 0;   // @cntvct
_SMT_UNSIGNED_32BIT_INTEGER g_cntvct_header[SMT_CNTVCT_HEADER_SIZ] = {0};
static void smt_output_enable_cntvct(void);     // @cntvct
static void push_smtbuf(_SMT_UNSIGNED_32BIT_INTEGER *data, _SMT_UNSIGNED_32BIT_INTEGER len);    // @cntvct

#ifdef DEV_SMTIF
static dev_t dev;
static struct class *smtif_class;
static struct cdev smtif_cdev;
#endif // DEV_SMTIF

/*****************************************************************************
1.Function: output Stack pointer address

2.Restriction, Cautions:
	not support 64bit address

3.Argument:
I/O|Variable Name			   |Explanation
---+---------------------------+----------------------------------------------

4.Return Value:

*****************************************************************************/
#if _SMT_OUTPUT_SP_ADDR == _SMT_ON
_SMT_UNSIGNED_32BIT_INTEGER _SMT_get_SP_address(void){
	_SMT_UNSIGNED_32BIT_INTEGER sp_addr = 0 ;

	<<< Caution >>> Please delete this line, and code the program.
//	#ifdef __GNUC__
//  sp_addr = __builtin_frame_address(0);
//	#endif

	return sp_addr;
}

#endif
/****************************************************************************
* Innterrupt Control
*****************************************************************************/
unsigned long smt_disable_int(void)
{
	unsigned long  flag;

	spin_lock_irqsave(&smt_device->lock,flag);
	return( flag );
}

void smt_enable_int(_SMT_UNSIGNED_32BIT_INTEGER flag)
{
	spin_unlock_irqrestore(&smt_device->lock, flag);
}

/****************************************************************************
* Snend Signal
*****************************************************************************/
int smt_send_sig(int sig)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,20,0)
	struct pid *p;
	struct task_struct *t;
	struct siginfo info;

	if ( 0 < smt_daemon_pid ) {
		info.si_errno = 0;
		info.si_code = SI_KERNEL;
		info.si_pid = 0;
		info.si_uid = 0;
		info.si_signo = sig;

		p = find_vpid(smt_daemon_pid);
		if (p == NULL) {
			return _SMT_NG;
		}
		t = pid_task(p, PIDTYPE_PID);

		send_sig_info(sig, &info, t);
	}
#else	/* LINUX_VERSION_CODE >= KERNEL_VERSION(4,20,0) */
	struct pid *p;
	struct task_struct *t;
	struct kernel_siginfo info;

	if ( 0 < smt_daemon_pid ) {
		clear_siginfo(&info);
		info.si_signo = sig;
		info.si_errno = 0;
		info.si_code = SI_KERNEL;
		info.si_pid = 0;
		info.si_uid = 0;

		p = find_vpid(smt_daemon_pid);
		if (p == NULL) {
			return _SMT_NG;
		}
		t = pid_task(p, PIDTYPE_PID);

		send_sig_info(sig, &info, t);
	}
#endif	
	return _SMT_OK;
}

/****************************************************************************
* SMT Output Enable.
*****************************************************************************/
void smt_output_enable(void)
{
	int ret = 0;
	if((smt_output_flag == _SMT_DISABLE) ||
		((smt_output_flag == _SMT_ENABLE) && (smt_buf->mode != FILE_KRING_MODE) && (smt_buf->buf_type == SMT_BUF_RING))) {
		smt_output_flag = _SMT_DISABLE;
		smt_buf->wp = 0;
		smt_buf->rp = 0;
		smt_buf->sts = 0;
		smt_buf->old_p = 0;
		smt_buf->new_p = 0;
		smt_buf->vacant = SMT_BUF_SIZE;
		if (smt_output_flag == _SMT_DISABLE) {
			if ((smt_buf->mode == FILE_KRING_MODE) || (smt_buf->mode == BOOT_MODE))
				smt_buf->buf_type = SMT_BUF_RING;
			else
				smt_buf->buf_type = SMT_BUF_NORMAL;
		} else {
			smt_buf->buf_type = SMT_BUF_NORMAL;
		}
		smt_sigtrap_flg = 0;

		if (smt_buf->cntvct == 1) {
			smt_output_enable_cntvct();
		}
		smt_output_flag = _SMT_ENABLE;
		ret = smt_send_sig( SIGUSR1);
		if (ret != 0) {
			smt_output_flag = _SMT_DISABLE;
		}
	}
}

/****************************************************************************
* SMT Output Enable. & Arm Generic Timer mode ON @cntvct
*****************************************************************************/
void smt_output_enable_cntvct(void)
{
	u64 l_cntvct;
	unsigned long  lock_flag;
	_SMT_UNSIGNED_32BIT_INTEGER offset = sizeof(_SMT_UNSIGNED_32BIT_INTEGER) * 3;
	_SMT_UNSIGNED_32BIT_INTEGER cntfrq = 0;

	/* Lock */
	lock_flag = smt_disable_int();					// Interrupt Disable
#ifdef SMT_SEM_CONTROL
	down(&smt_sem);
#endif

	/* cntvct */
	if( g_cntvct_header[0] == 0x0 ){
		cntfrq = (_SMT_UNSIGNED_32BIT_INTEGER)arch_timer_get_cntfrq();
		g_cntvct_header[0] = SMT_CNTVCT_HEADER;
		g_cntvct_header[1] = offset;
		g_cntvct_header[2] = cntfrq;
		// -> debug
#if KERNEL_VERSION(5,2,0) <= LINUX_VERSION_CODE
		l_cntvct = __arch_counter_get_cntvct_stable();
#else
		l_cntvct = arch_counter_get_cntvct();
#endif
	}
	if (smt_buf->buf_type != SMT_BUF_RING) {
		push_smtbuf(g_cntvct_header, SMT_CNTVCT_HEADER_SIZ);
		g_ins_header = 1;
	}


	/* Unlock */
#ifdef SMT_SEM_CONTROL
	up(&smt_sem);
#endif
	smt_enable_int(lock_flag);					// Interrupt Enable
}

/****************************************************************************
* SMT Output Disable.
*****************************************************************************/
void smt_output_disable(void)
{
	if( (smt_output_flag == _SMT_ENABLE) || (smt_output_flag == _SMT_BUFFER_FULL) ){
		if (smt_output_flag == _SMT_BUFFER_FULL) {
			printk( KERN_INFO "[SMT] buffer full\n" );

		}
		smt_output_flag = _SMT_DISABLE;
		smt_send_sig( SIGUSR2 );
		smt_daemon_pid = SMT_FILE_WRITTEING_STATUS_PID;
		printk( KERN_INFO "[SMT] smt sw off\n" );

		/* update rp info */
		if(smt_buf->sts & 1){
			if(smt_buf->wp)
				smt_buf->new_p = smt_buf->wp - 1;
			else 
				smt_buf->new_p = SMT_BUF_SIZE - 1;
		}
		else {
			smt_buf->old_p = 0;
			smt_buf->new_p = smt_buf->wp - 1;
		}

		if(smt_buf->buf_type == SMT_BUF_RING){
			smt_buf->rp = smt_buf->old_p;
		}

#ifdef SMT_LOGMODE_GVM
		// cntvct is default on
		smt_buf->cntvct = 1;
		g_ins_header = 0;
#endif
	}
}
/****************************************************************************
*
*****************************************************************************/
void push_smtbuf(_SMT_UNSIGNED_32BIT_INTEGER *data, _SMT_UNSIGNED_32BIT_INTEGER len)
{
	_SMT_UNSIGNED_32BIT_INTEGER i;
	for( i=0; i<len; i++){
		smt_buf->data[smt_buf->wp] = data[i];
		smt_buf->wp++;
		if(SMT_BUF_SIZE <= smt_buf->wp) {
			smt_buf->wp = 0;
			smt_buf->sts |= 1;
		}
	}
}

int pull_smtbuf(_SMT_UNSIGNED_32BIT_INTEGER *data)
{
	int count = 0;
	_SMT_UNSIGNED_32BIT_INTEGER wp = 0;
	_SMT_UNSIGNED_32BIT_INTEGER i = 0;
	wp = smt_buf->wp;

	while(1){
		/* size check */
		if( count == SMT_PUL_SIZ ) {	// eq.Buffer size MAX
			break;
		}
		if(smt_buf->buf_type != SMT_BUF_RING){
			if( wp == smt_buf->rp ) {
				break;
			}
		}
		else {
			if(( wp == smt_buf->rp ) && (smt_buf->sts & 2)) {
				break;
			}
		}

		if ((smt_buf->cntvct == 1) && (smt_buf->buf_type == SMT_BUF_RING) && ((smt_buf->sts & 2) == 0)) {
			/* pull header data */
			for (i = 0; i < SMT_CNTVCT_HEADER_SIZ; i++) {
				//*data = g_cntvct_header[i];
				if (copy_to_user( data, &g_cntvct_header[i], sizeof(_SMT_UNSIGNED_32BIT_INTEGER) ) != 0) {
					printk( KERN_ERR "pull_smtbuf read fail header data ng\n" );
					break;
				}
				data++;
				count++;
			}
			if( i < SMT_CNTVCT_HEADER_SIZ ) {
				break;
			}
			g_ins_header = 1;
		}

		/* pull data */
		//*data = smt_buf->data[smt_buf->rp];
		if (copy_to_user( data, &smt_buf->data[smt_buf->rp], sizeof(_SMT_UNSIGNED_32BIT_INTEGER) ) != 0) {
			printk( KERN_ERR "pull_smtbuf read fail data ng\n" );
			break;
		}
		data++;
		smt_buf->rp++;
		if(smt_buf->rp >= SMT_BUF_SIZE){
			smt_buf->rp = 0;
		}
		count++;
		smt_buf->sts |= 2;
	}

	return count;
}


#if KERNEL_VERSION(5,4,83) <= LINUX_VERSION_CODE
/****************************************************************************
1.Function: temporary timestam maker

2.Restriction, Cautions:

3.Argument
I/O|Variable Name		|Explanation
---+----------------------------+---------------------------------------------
4.Return Value:

*****************************************************************************/
void _SMT_GetTimeStamp(_SMT_UNSIGNED_32BIT_INTEGER *sec, _SMT_UNSIGNED_32BIT_INTEGER *nano_sec) {

	struct timespec64 ts;
	ts = ns_to_timespec64( ktime_get());
	*sec = ts.tv_sec;
	*nano_sec = ts.tv_nsec;
	return ;
}
#endif


/****************************************************************************
* SDIF output transaction. Stand Alone IF CMD save.
*****************************************************************************/
int  _SMT_Output( _SMT_UNSIGNED_32BIT_INTEGER length, _SMT_UNSIGNED_32BIT_INTEGER *data)
{
	unsigned long  flag;
	_SMT_UNSIGNED_32BIT_INTEGER write_size = 0;
	_SMT_UNSIGNED_32BIT_INTEGER core_id = 0;
	_SMT_UNSIGNED_32BIT_INTEGER rp,wp;
#if KERNEL_VERSION(5,4,83) <= LINUX_VERSION_CODE
	unsigned int sec=0, nano_sec=0;
#else
	struct timespec ts;
#endif
	u64 l_cntvct = 0; //@cntvct

	if( (smt_output_flag == _SMT_DISABLE) || (smt_output_flag == _SMT_BUFFER_FULL) ){
		return _SMT_NG;
	}
	if( length==0 ){
		return _SMT_NG;
	}

	/* Calc. vacant */
	wp = smt_buf->wp;
	rp = smt_buf->rp;
	if( wp == rp ){
		smt_buf->vacant = SMT_BUF_SIZE;
	} else if( wp > rp ){
		smt_buf->vacant = SMT_BUF_SIZE - (wp - rp);
	} else {
		smt_buf->vacant = rp - wp;
	}

 	if(smt_buf->buf_type != SMT_BUF_RING){
		/* size check */
		write_size = SMT_DAT_OFFSET + length;
		if(write_size >= smt_buf->vacant) {
			smt_output_flag = _SMT_BUFFER_FULL;
			return _SMT_NG;
		}
	}
	

	/* Lock */
	flag = smt_disable_int();										// Interrupt Disable
#ifdef SMT_SEM_CONTROL
	down(&smt_sem);
#endif

	/* set oldest rp */
	if(smt_buf->buf_type == SMT_BUF_RING){
		update_oldest_rp(length);
	}

	if (data[0] & _SMT_OS_SWITCH_THREAD_NAME) {
		/* Domain PID/TID */
		data[1] |= SMT_OUT_GUEST_NUM;
		data[2] |= SMT_OUT_GUEST_NUM;
	} else if ((data[0] & _SMT_OS_SWITCH_IRQ_IN) || (data[0] & _SMT_OS_SWITCH_IRQ_OUT)) {
		/* Domain IRQ */
		data[1] |= SMT_OUT_GUEST_NUM;
	}

	/* Time Stamp */
	if (smt_buf->cntvct == 1) {
#if KERNEL_VERSION(5,2,0) <= LINUX_VERSION_CODE
		l_cntvct = __arch_counter_get_cntvct_stable();
#else
		l_cntvct = arch_counter_get_cntvct();
#endif
		push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&l_cntvct, 2);
	}
	else{
#if KERNEL_VERSION(5,4,83) <= LINUX_VERSION_CODE
		_SMT_GetTimeStamp(&sec, &nano_sec);
		push_smtbuf(&sec, 1);
		push_smtbuf(&nano_sec, 1);
#else
		getnstimeofday(&ts);
		push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&ts.tv_sec,  1);
		push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&ts.tv_nsec, 1);
#endif
	}

	/* Core ID */
#ifdef CONFIG_SMP
	core_id = raw_smp_processor_id();
#endif
	push_smtbuf(&core_id, 1);

	/* data */
	push_smtbuf(data, length);

	/* Unlock */
#ifdef SMT_SEM_CONTROL
	up(&smt_sem);
#endif
	smt_enable_int(flag);											// Interrupt Enable
	return _SMT_OK;
}

int _SMT_Output_SP( _SMT_UNSIGNED_32BIT_INTEGER length, _SMT_UNSIGNED_32BIT_INTEGER *data,_SMT_UNSIGNED_32BIT_INTEGER sp_output_flag,_SMT_UNSIGNED_32BIT_INTEGER sp_addr)
{
	unsigned long  flag;
	_SMT_UNSIGNED_32BIT_INTEGER write_size = 0;
	_SMT_UNSIGNED_32BIT_INTEGER core_id = 0;
	_SMT_UNSIGNED_32BIT_INTEGER rp,wp;
	_SMT_UNSIGNED_32BIT_INTEGER sp_data[2] __attribute__ ((aligned (4)));
#if KERNEL_VERSION(5,4,83) <= LINUX_VERSION_CODE
	unsigned int sec=0, nano_sec=0;
#else
	struct timespec ts;
#endif
	u64 l_cntvct = 0; //@cntvct

	if( (smt_output_flag == _SMT_DISABLE) || (smt_output_flag == _SMT_BUFFER_FULL) ){
		return _SMT_NG;
	}
	if( length==0 ){
		return _SMT_NG;
	}

	/* Calc. vacant */
	wp = smt_buf->wp;
	rp = smt_buf->rp;
	if( wp == rp ){
		smt_buf->vacant = SMT_BUF_SIZE;
	} else if( wp > rp ){
		smt_buf->vacant = SMT_BUF_SIZE - (wp - rp);
	} else {
		smt_buf->vacant = rp - wp;
	}


	/* size check */
	if( sp_output_flag == _SMT_SP_OUTPUT_ENABLE){
		write_size = SMT_DAT_OFFSET + length + SMT_DAT_OFFSET + 2;
		sp_data[0] = _SMT_ATR_PRINT | _SMT_OUT_SIZE_USRMSGTAG_1 | _SMT_MSG_TAG_ON | ((0 & _SMT_DBG_LV_MSK) << _SMT_DBG_LV_POS) | (0xFFFC & _SMT_MASK_MSGNO);
		sp_data[1] = sp_addr;
	} else {
		write_size = SMT_DAT_OFFSET + length;
	}

	if(smt_buf->buf_type != SMT_BUF_RING){
		if(write_size >= smt_buf->vacant) {
			smt_output_flag = _SMT_BUFFER_FULL;
			return _SMT_NG;
		}
	}
	
	/* Lock */
	flag = smt_disable_int();										// Interrupt Disable
#ifdef SMT_SEM_CONTROL
	down(&smt_sem);
#endif

	/* set oldest rp */
	if(smt_buf->buf_type == SMT_BUF_RING){
		update_oldest_rp(length + SMT_DAT_OFFSET + _SMT_USRMSGTAG_DATA_SIZE_1);
	}

	/* Time Stamp */
	if (smt_buf->cntvct == 1) {
#if KERNEL_VERSION(5,2,0) <= LINUX_VERSION_CODE
		l_cntvct = __arch_counter_get_cntvct_stable();
#else
		l_cntvct = arch_counter_get_cntvct();
#endif
		push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&l_cntvct, 2);
	}
	else {
#if KERNEL_VERSION(5,4,83) <= LINUX_VERSION_CODE
		_SMT_GetTimeStamp(&sec, &nano_sec);
		push_smtbuf(&sec, 1);
		push_smtbuf(&nano_sec, 1);
#else
		getnstimeofday(&ts);
		push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&ts.tv_sec,  1);
		push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&ts.tv_nsec, 1);
#endif
	}

	/* Core ID */
#ifdef CONFIG_SMP
	core_id = raw_smp_processor_id();
#endif
	push_smtbuf(&core_id, 1);

	/* data */
	push_smtbuf(data, length);

	if(sp_output_flag == _SMT_SP_OUTPUT_ENABLE){
		if (smt_buf->cntvct == 1) {
			push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&l_cntvct, 2);
		}
		else {
#if KERNEL_VERSION(5,4,83) <= LINUX_VERSION_CODE
			_SMT_GetTimeStamp(&sec, &nano_sec);
			push_smtbuf(&sec, 1);
			push_smtbuf(&nano_sec, 1);
#else
			push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&ts.tv_sec,  1);
			push_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)&ts.tv_nsec, 1);
#endif
		}
		push_smtbuf(&core_id, 1);
		push_smtbuf(sp_data, _SMT_USRMSGTAG_DATA_SIZE_1);
	}

	/* Unlock */
#ifdef SMT_SEM_CONTROL
	up(&smt_sem);
#endif
	smt_enable_int(flag);											// Interrupt Enable
	return _SMT_OK;
}


/****************************************************************************
* Oldest ReadPoint correction
*****************************************************************************/
void update_oldest_rp(_SMT_UNSIGNED_32BIT_INTEGER length)
{
	_SMT_UNSIGNED_32BIT_INTEGER wp = smt_buf->wp;
	
	static _SMT_UNSIGNED_32BIT_INTEGER next_packet;						// Next to Oldest packet
	_SMT_UNSIGNED_32BIT_INTEGER next_end_point = wp + SMT_DAT_OFFSET + length;	// Next finished writing point
	_SMT_UNSIGNED_32BIT_INTEGER capacity = 0;							// Remaining capacity

	static int	round_check = 0;										// Overwrite state
	static int	nxtend_loop_flg = 0;									// Flag for next_end_point out of memory area
	static int	befloopstart_loop_flg = 0;								// Flag for old_p out of memory area
	static int	nxtpkt_loop_flg = 0;									// Flag for next_packet out of memory area

	// Positioning next_end_point
	if(next_end_point >= SMT_BUF_SIZE){
		capacity = SMT_BUF_SIZE - wp;
		next_end_point = SMT_DAT_OFFSET + length - capacity;
		if(round_check == 0){											// First->Second loop 
			round_check = 1;
		}else{															// After Second->Third loop
			nxtend_loop_flg = 1;
		}
	}

	// Positioning oldest packet
	if(round_check == 1){												// Overwrite state
		if(befloopstart_loop_flg == 0){
			while(1){
				if(nxtend_loop_flg == 0){
					if((nxtpkt_loop_flg == 0) && (smt_buf->old_p >= next_end_point)){
						break;											// No adjustment required to oldest packet
					}
					next_packet_pos(&next_packet);
					if(next_packet < SMT_BUF_SIZE){
						if( next_packet >= next_end_point ){
							smt_buf->old_p = next_packet;				// Oldest packet position fix
							nxtpkt_loop_flg = 0;
							break;
						}else{
							continue;									// Continue to adjust next_packet
						}
					}else{
						next_packet -= SMT_BUF_SIZE;
						smt_buf->old_p = next_packet;					// Oldest packet position fix
						befloopstart_loop_flg = 1;
						break;
					}
				}else{
					next_packet_pos(&next_packet);
					if(next_packet < SMT_BUF_SIZE){
						continue;										// Continue to adjust next_packet
					}else{
						next_packet -= SMT_BUF_SIZE;
						nxtpkt_loop_flg = 1;
						nxtend_loop_flg = 0;
						if( next_packet >= next_end_point ){
							smt_buf->old_p = next_packet;				// Oldest packet position fix
							break;
						}else{
							continue;									// Continue to adjust next_packet
						}
					}
				}
			}
		}else{
		//'Next to Oldest packet' loop before next_end_point
			if(nxtend_loop_flg == 1){
				nxtend_loop_flg = 0;
				befloopstart_loop_flg = 0;
				if(smt_buf->old_p < next_end_point){
					while(1){
						next_packet_pos(&next_packet);
						if( next_packet >= next_end_point ){
							smt_buf->old_p = next_packet;				// Oldest packet position fix
							break;
						}
					}
				}
			}
		}
	}

	return;
}

void next_packet_pos(_SMT_UNSIGNED_32BIT_INTEGER *next_packet)
{
	static _SMT_UNSIGNED_32BIT_INTEGER api_head_point =0;
	_SMT_UNSIGNED_32BIT_INTEGER api_head_point_tmp = 0;

	api_head_point = *next_packet + SMT_DAT_OFFSET;
	if(api_head_point >= SMT_BUF_SIZE){
		api_head_point_tmp = api_head_point - SMT_BUF_SIZE;
	}else{
		api_head_point_tmp = api_head_point;
	}
	*next_packet = getAPIDataSize(smt_buf->data[api_head_point_tmp]) + api_head_point;

	return;
}

int getAPIDataSize(_SMT_UNSIGNED_32BIT_INTEGER APIHeadData)
{
	unsigned int APIAttribute = 0;
	int APIDataSize = 0;
	APIAttribute = (APIHeadData & 0xF0000000) >> _SMT_ATR_POS;
	switch (APIAttribute) {
		case 0x0:
		case 0x6:
		case 0xD:
		case 0xF:
			APIDataSize = 1;
			break;
		case 0x2:
		case 0x3:
		case 0x4:
		case 0x5:
		case 0x7:
		case 0x8:
		case 0x9:
		case 0xA:
		case 0xB:
		case 0xC:
			APIDataSize = ((0x0FE00000 & APIHeadData) >>21) + 1;
			break;
		case 0xE:
			APIDataSize = 2;
	}
	return APIDataSize;
}


/****************************************************************************
* from User-Land  by /proc/smt/smtdat device.
*****************************************************************************/
static ssize_t proc_write(struct file *filp, const char *buf, size_t len,  loff_t  *data)
{
	union rcv_data {
		unsigned char c[512];
		_SMT_UNSIGNED_32BIT_INTEGER l[64];
	} rcv;

	// Receive SMT Daemon Process ID

	if( copy_from_user(rcv.c, buf, (len<<2)) )
	{
		printk( KERN_ERR "smtdat write fail data ng\n" );
		return -EFAULT;
	}
	smt_daemon_pid = rcv.l[0];
	smt_buf->mode = rcv.l[1];
	return len;
}
#if KERNEL_VERSION(3,10,0) <= LINUX_VERSION_CODE
static ssize_t  proc_read(struct file *filp,char *buf,size_t count,loff_t *offp )
#else
static int proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data)
#endif
{
	int ret = 0;
	if( ( smt_output_flag == _SMT_DISABLE) || (smt_output_flag ==  _SMT_BUFFER_FULL) ){
		smt_output_disable();
	}

	/* size check */
	ret = pull_smtbuf((_SMT_UNSIGNED_32BIT_INTEGER *)buf);
	if( ret == 0 ) {
		smt_sigtrap_flg = 0;
		ret = 0;
	}
#if KERNEL_VERSION(3,10,0) > LINUX_VERSION_CODE
   	*eof = 1;
#endif
	return (sizeof(_SMT_UNSIGNED_32BIT_INTEGER) * ret);
}
/****************************************************************************
* SMT control Switch by /proc/smtsw device.
*****************************************************************************/
static ssize_t sw_proc_write(struct file *filp, const char  *buf, size_t len, loff_t *data)
{
	unsigned char proc_data[16];


	if(	len >= (sizeof(proc_data)-1) ) {
		printk(KERN_INFO "%s length is over\n", __func__);
		return -EFAULT;
	}
	if (smt_daemon_pid == SMT_INIT_PID) {
	  printk(KERN_ERR "Please Exec smtex(daemon for outputting SMT log file)\n");
	  return -1;
	} else if ((smt_daemon_pid == SMT_FILE_WRITTEING_STATUS_PID) ||(smt_output_flag ==  _SMT_BUFFER_FULL)) {
	  printk(KERN_ERR "SMT log file output ...\n");
	  return -1;
	}
	if( copy_from_user( proc_data, buf, len )) {
		printk( KERN_ERR "smtsw write fail data ng\n" );
		return -EFAULT;
	}
	proc_data[len] = '\0';

	if ( !strncmp(proc_data, "1", 1) )  {
		smt_output_enable();
	}
	else if ( !strncmp(proc_data, "0", 1) ) {
		smt_output_disable();
	}
	else if ( !strncmp(proc_data, "on", 2) ) {
		smt_output_enable();
	}
	else if ( !strncmp(proc_data, "off", 3) ) {
		smt_output_disable();
	}

	if( smt_output_flag == _SMT_ENABLE ){
	    printk(KERN_INFO "[smtsw] on\n" );
	}
	else {
	    printk(KERN_INFO "[smtsw] off\n" );
	}

	return len;
}

static int get_status_bootmode(int *buf)
{
	int ret = 0;
	int status = (smt_buf->mode == BOOT_MODE) ? 1 : 0;
	if(copy_to_user(buf, &status, sizeof(int)) != 0) {
		printk( KERN_ERR "smtsw read fail data ng\n" );
		ret = -EFAULT;
	}
	return ret;
}

#if KERNEL_VERSION(3,10,0) <= LINUX_VERSION_CODE
static ssize_t sw_proc_read(struct file *filp,char *buf,size_t count,loff_t *offp )
{
	int ret = 0;
	if( smt_output_flag == _SMT_ENABLE ){
		printk(KERN_INFO "[smtsw] on\n" );
	} else {
		printk(KERN_INFO "[smtsw] off\n" );
	}

	ret = get_status_bootmode((int *)buf);
	return ret;
}
#else
static int sw_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data)
{
	_SMT_UNSIGNED_32BIT_INTEGER outbyte = 0;
	
	if (offset > 0) {
		*eof = 1;
		return 0;
	}

	if ((smt_output_flag == _SMT_ENABLE) || ( smt_output_flag ==  _SMT_BUFFER_FULL) ) {
		outbyte = sprintf(buf, "[smtsw] on\n");
	}
	else {
		outbyte = sprintf(buf, "[smtsw] off\n");
	}

	*eof = 1;

	return outbyte;
}
#endif
/****************************************************************************
* from User-Land  by /proc/smt/smtif device.
*****************************************************************************/
#ifdef DEV_SMTIF
static ssize_t smtif_write(struct file *filp, const char  *buf, size_t len, loff_t *data){
	unsigned char bus_out_data[512];
	int attr_check = _SMT_FALSE;
	_SMT_UNSIGNED_32BIT_INTEGER sp_output_flg = 0;
	_SMT_UNSIGNED_32BIT_INTEGER sp_addr = 0;
	if( (smt_output_flag == _SMT_DISABLE) || (smt_output_flag == _SMT_BUFFER_FULL) ){
		return -EFAULT;
	}
	if( copy_from_user(bus_out_data, buf, (len<<2)) )
	{
		printk( KERN_ERR "smtif write fail data ng\n" );
		return -EFAULT;
	}

	attr_check = isFuncData((_SMT_UNSIGNED_32BIT_INTEGER *)bus_out_data);
	if(attr_check == _SMT_TRUE){
		sp_addr = bus_out_data[(len-1)*sizeof(_SMT_UNSIGNED_32BIT_INTEGER)];
		sp_output_flg = bus_out_data[(len-2)*sizeof(_SMT_UNSIGNED_32BIT_INTEGER)];
		_SMT_Output_SP(len -2,(_SMT_UNSIGNED_32BIT_INTEGER *)&bus_out_data[0],sp_output_flg,sp_addr);
	} else {
		_SMT_Output( len, (_SMT_UNSIGNED_32BIT_INTEGER *)&bus_out_data[0]);
	}

	return len;
}

static int smtif_open(struct inode *inode, struct file *file){
	pr_info("smtif: open\n");
	return 0;
}

static ssize_t smtif_read(struct file *filp,char *buf,size_t count,loff_t *offp ){
	return 0;
}

static struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = smtif_open,
	.read = smtif_read,
	.write = smtif_write,
};

static const struct proc_ops smtdat_proc_fops = {
	.proc_read = proc_read,
	.proc_write = proc_write,
	.proc_lseek = default_llseek,
};

static const struct proc_ops smtsw_proc_fops = {
	.proc_read = sw_proc_read,
	.proc_write = sw_proc_write,
	.proc_lseek = default_llseek,
};

#else	// DEV_SMTIF

static ssize_t if_proc_write(struct file *filp, const char  *buf, size_t len, loff_t *data)
{
	unsigned char bus_out_data[512];
	int attr_check = _SMT_FALSE;
	_SMT_UNSIGNED_32BIT_INTEGER sp_output_flg = 0;
	_SMT_UNSIGNED_32BIT_INTEGER sp_addr = 0;
	if( (smt_output_flag == _SMT_DISABLE) || (smt_output_flag == _SMT_BUFFER_FULL) ){
		return -EFAULT;
	}
	if( copy_from_user(bus_out_data, buf, (len<<2)) )
	{
		printk( KERN_ERR "smtif write fail data ng\n" );
		return -EFAULT;
	}

	attr_check = isFuncData((_SMT_UNSIGNED_32BIT_INTEGER *)bus_out_data);
	if(attr_check == _SMT_TRUE){
		sp_addr = bus_out_data[(len-1)*sizeof(_SMT_UNSIGNED_32BIT_INTEGER)];
		sp_output_flg = bus_out_data[(len-2)*sizeof(_SMT_UNSIGNED_32BIT_INTEGER)];
		_SMT_Output_SP(len -2,(_SMT_UNSIGNED_32BIT_INTEGER *)&bus_out_data[0],sp_output_flg,sp_addr);
	} else {
		_SMT_Output( len, (_SMT_UNSIGNED_32BIT_INTEGER *)&bus_out_data[0]);
	}

	return len;
}

#if KERNEL_VERSION(3,10,0) <= LINUX_VERSION_CODE
static ssize_t if_proc_read(struct file *filp,char *buf,size_t count,loff_t *offp )
#else
static int if_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data)
#endif
{
#if KERNEL_VERSION(3,10,0) > LINUX_VERSION_CODE
   	*eof = 1;
#endif
	return 0;
}


#if KERNEL_VERSION(5,6,19) <= LINUX_VERSION_CODE
static const struct proc_ops smtdat_proc_fops = {
	.proc_read = proc_read,
	.proc_write = proc_write,
	.proc_lseek = default_llseek,
};

static const struct proc_ops smtif_proc_fops = {
	.proc_read = if_proc_read,
	.proc_write = if_proc_write,
	.proc_lseek = default_llseek,
};

static const struct proc_ops smtsw_proc_fops = {
	.proc_read = sw_proc_read,
	.proc_write = sw_proc_write,
	.proc_lseek = default_llseek,
};
#elif KERNEL_VERSION(3,10,0) <= LINUX_VERSION_CODE
static const struct file_operations smtdat_proc_fops = {
	.owner = THIS_MODULE,

	.read = proc_read,
	.write = proc_write
};

static const struct file_operations smtif_proc_fops = {
	.owner = THIS_MODULE,
	.read = if_proc_read,
	.write = if_proc_write
};

static const struct file_operations smtsw_proc_fops = {
	.owner = THIS_MODULE,
	.read = sw_proc_read,
	.write = sw_proc_write
};
#endif
#endif //DEV_SMTIF
/****************************************************************************
* Modules.  init/exit
*****************************************************************************/
static int __init smt_dat_init(void)
{
	struct proc_dir_entry* entry;
	struct proc_dir_entry* sw_entry;
	u32 l_cntfrq;
	u64 l_cntvct;
	ktime_t t_monotonic;
	ktime_t t_boottime;
#ifdef DEV_SMTIF
	int ret;
#else // DEV_SMTIF
	struct proc_dir_entry* if_entry;
#endif // DEV_SMTIF

	t_monotonic = ktime_get();
#if KERNEL_VERSION(5,2,0) <= LINUX_VERSION_CODE
	l_cntvct = __arch_counter_get_cntvct_stable();
#else
	l_cntvct = arch_counter_get_cntvct();
#endif
	l_cntfrq = arch_timer_get_cntfrq();
	t_boottime = ktime_get_boottime();
	printk( KERN_INFO "***** SMT Init *****\n");

#if KERNEL_VERSION(3,10,0) <= LINUX_VERSION_CODE
	entry = proc_create(PROC_NAME,0666,NULL,&smtdat_proc_fops);
	if (!entry) {
		return -ENOMEM;
	}
#else
	entry = create_proc_entry(PROC_NAME, 0666, NULL);

	if( entry )
	{
		entry->write_proc = proc_write;
		entry->read_proc = proc_read;
	}
	else
	{
		printk( KERN_ERR "proc entry fail\n" );
		return -EBUSY;
	}
#endif
#ifdef SMT_SEM_CONTROL
	sema_init(&smt_sem, 1);
#endif

	smt_device = kmalloc(sizeof(struct smt_device), GFP_KERNEL);
	if (!smt_device) {
		if (smt_device) {
			kfree(smt_device);
			smt_device = NULL;
		}
		return(-ENOMEM);
	}
	memset(smt_device,0,sizeof(struct smt_device));
	spin_lock_init(&smt_device->lock);

	// ---------------------------
	// Initialize SMT Buff
	// ---------------------------
	//smt_buf = kmalloc((_SMT_UNSIGNED_32BIT_INTEGER)sizeof(struct smt_buffer),GFP_KERNEL);
	smt_buf = vmalloc((_SMT_UNSIGNED_32BIT_INTEGER)sizeof(struct smt_buffer));
	if (!smt_buf) {
		if (smt_buf) {
			vfree(smt_buf);
			smt_buf = NULL;
		}
		printk( KERN_ERR "vmalloc Error\n");
		printk( KERN_ERR "Please reduce SMT_BUF_SIZE");
		return(-ENOMEM);
	}
	memset(smt_buf,0,sizeof(struct smt_buffer));
	//initial setup for kring mode
	smt_buf->vacant = SMT_BUF_SIZE;
	smt_buf->mode = BOOT_MODE;
#ifdef SMT_LOGMODE_GVM
	smt_buf->cntvct = 1;
#endif

	printk( KERN_INFO "smtdat module loaded \n" );

	printk( KERN_INFO "***** SMT if_entry Init *****\n" );

#ifdef DEV_SMTIF
	ret = alloc_chrdev_region(&dev, 0, 1, "smtif"); 
	if(ret) return -EBUSY;

	cdev_init(&smtif_cdev, &fops);
	cdev_add(&smtif_cdev, dev, 1);

#if KERNEL_VERSION(6,4,0) <= LINUX_VERSION_CODE
	smtif_class = class_create("smtif_class");
#else 
	smtif_class = class_create(THIS_MODULE, "smtif_class");
#endif
	device_create(smtif_class, NULL, dev, NULL, "smtif");

#else // DEV_SMTIF
#if KERNEL_VERSION(3,10,0) <= LINUX_VERSION_CODE
	if_entry = proc_create(IF_PROC_NAME,0666,NULL,&smtif_proc_fops);
	if( !if_entry ) {
		printk( KERN_ERR "proc entry fail\n" );
		return -EBUSY;
	}
#else
	if_entry = create_proc_entry(IF_PROC_NAME, 0666, NULL);
	if( if_entry )
	{
		if_entry->write_proc = if_proc_write;
		if_entry->read_proc = if_proc_read;
	}
	else
	{
		printk( KERN_ERR "proc entry fail\n" );
		return -EBUSY;
	}
#endif

#endif // DEV_SMTIF
	printk( KERN_INFO "smtif module loaded \n" );

	printk( KERN_INFO "***** SMT SW Init *****\n" );

#if KERNEL_VERSION(3,10,0) <= LINUX_VERSION_CODE
	sw_entry = proc_create(SW_PROC_NAME,0666,NULL,&smtsw_proc_fops);
	if( !sw_entry ) {
		printk( KERN_ERR "proc entry fail\n" );
		return -EBUSY;
	}
#else
	sw_entry = create_proc_entry(SW_PROC_NAME, 0666, NULL);
	if( sw_entry )
	{
		sw_entry->write_proc = sw_proc_write;
		sw_entry->read_proc = sw_proc_read;
	}
	else
	{
		printk( KERN_ERR "proc entry fail\n" );
		return -EBUSY;
	}
#endif
	printk( KERN_INFO "smtsw module loaded \n" );

	// enable for kring mode
	smt_output_enable();

	return 0;
}

static void __exit smt_dat_exit(void)
{
#ifdef DEV_SMTIF
	device_destroy(smtif_class, dev);
	class_destroy(smtif_class);
	cdev_del(&smtif_cdev);
	unregister_chrdev_region(dev, 1);
#endif
	remove_proc_entry(PROC_NAME, NULL);
	remove_proc_entry(IF_PROC_NAME, NULL);
	remove_proc_entry(SW_PROC_NAME, NULL);
	vfree(smt_buf);
	kfree(smt_device);
	printk( KERN_INFO "smtdat module unload\n" );
}

static int  isFuncData(_SMT_UNSIGNED_32BIT_INTEGER *data){
	_SMT_UNSIGNED_32BIT_INTEGER attr = *data & 0xF0000000;
	int ret_val = _SMT_FALSE;
	switch (attr) {
	case _SMT_ATR_FUNC_NA:
	case _SMT_ATR_FUNC_NF:
	case _SMT_ATR_FUNC   :
	case _SMT_ATR_FUNC_0 :
	case _SMT_ATR_FUNC_1 :
		ret_val = _SMT_TRUE;
		break;
	default:
		ret_val = _SMT_FALSE;
	}
	
	return ret_val;
	
}
/****************************************************************************
*
*****************************************************************************/

module_init( smt_dat_init );
module_exit( smt_dat_exit );

MODULE_DESCRIPTION("smtdat");
MODULE_LICENSE("GPL v2");
