/******************************************************************************
版权所有：	
文件名称：	SNTP.c
文件版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-02-10
功能说明：	SNTP对时处理
其它说明：
修改记录：
*/
#include "head.h"
#ifdef SNTP_FUNC

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/wait.h> 
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/ioctls.h>
//#include <asm/semaphore.h>
#include <linux/if.h> 
#include <linux/tcp.h>
#include <termios.h>
#include <linux/syscalls.h>
#include "sntp.h"


#define SNTP_IS_NTPD

#ifdef SNTP_IS_NTPD
//应用程序名称
#define SNTP_PROGNAME			"ntpd"
#else
#define SNTP_PROGNAME			"sntp"
#endif
//sntp同步参数数据结构
struct sntp_para
{
	unsigned int serverip; //服务器IP
	int tm;	//同步钟间隔
}; 

//sntp同步参数
static struct sntp_para  g_sntp_para = {0, 0};
//completion变量，用于等待任务退出
static struct completion g_exit_sntp_proc;
//修改参数标志
static int g_sntp_paraflag = 0; 
//参数初始化
static int g_sntp_para_init = 0; 


int main_mod_is_exit(void);


/******************************************************************************
函数名称：	sntp_run
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-02-10
函数说明：	运行ntpd,需要指定服务器地址。
参数说明：	无
返回值：  	成功返回0，失败返回负数。
修改记录：	
*/
// sunxi 20201027 added start sntp时间同步标志
static unsigned char sntp_time_synchronized = 0;
unsigned char sntp_get_time_sync_flag(void)
{
	unsigned char sync = sntp_time_synchronized;
	if(sntp_time_synchronized)
		sntp_time_synchronized = 0;
	return sync;
}
// sunxi 20201027 added end sntp时间同步标志

static int sntp_run(void)
{
	int i = 0, j = 0;
	char buf[16];
	int ret = 0;	
	int tm = g_sntp_para.tm;
	unsigned int serverip = g_sntp_para.serverip;
	struct task_struct *task; // sunxi 20201027 added
	unsigned char sync = 0;
	
	#ifdef SNTP_IS_NTPD
	
	char	cmd_path[] = "/usr/sbin/ntpd";
	char* cmd_argv[] = {cmd_path, "-p", buf, "-q", NULL};
	char* cmd_envp[] = {NULL};			
	
	#else
	
	char	cmd_path[] = "/app/sntp";
	char* cmd_argv[] = {cmd_path, "-s", buf, NULL};
	char* cmd_envp[] = {NULL};	
		
	#endif
	sprintf(buf, "%d.%d.%d.%d", (serverip>>24) & 0xFF, (serverip>>16) & 0xFF, (serverip>>8) & 0xFF, (serverip>>0) & 0xFF);
	sntp_time_synchronized = 0;	// sunxi 20201027 added
	// sunxi 20201027 ret = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_NO_WAIT);
	ret = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC);
	
	//已经同步时钟,置参数未修改标志
	g_sntp_paraflag = 0;
	
	for(i=0; i<tm; i++)
	{
		for(j=0; j<10; j++)
		{
			//检查程序是否退出
			if(main_mod_is_exit())
			{
				complete_and_exit(&g_exit_sntp_proc, 0);		
			}
			//检查是否修改了参数
			if(g_sntp_paraflag)
			{
				sntp_time_synchronized = 0;// sunxi 20201027 added 修改了参数后，对时成功标识重新清零。
				return 0;	
			}
			// sunxi 20201027 added start 判断 ntpd 程序是否退出了，退出了说明对时成功了
			if(!sync)
			{
				//当前进程遍历起点
				task = current;
				//遍历进程链表,找到进程name
				do {
					if(strncmp(task->comm, "ntpd", strlen("ntpd"))==0)
						break;

				} while( (task = next_task(task)) != current );
				if(task == current)
				{
					sync = 1;
					sntp_time_synchronized = 1;
				}
			}
			// sunxi 20201027 added end 判断 ntpd 程序是否退出了，退出了说明对时成功了
			msleep(100);
		}		
	}
	
	return 0;	
}
/******************************************************************************
函数名称：	sntp_check_run_and_kill
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-02-10
函数说明：	检查进程是否运行,如果运行，kill掉进程。
参数说明：	无
返回值：  	成功返回0，失败返回负数。
修改记录：	
*/
static int sntp_check_run_and_kill(char *name)
{
	char buf[16];
	char	cmd_path[] = "/bin/kill";
	char* cmd_argv[] = {cmd_path, "-INT", buf, NULL};
	char* cmd_envp[] = {NULL};	
  //当前进程遍历起点
  struct task_struct *task = current;
	int ret = -1;

	//遍历进程链表,找到进程name
	do {
	
		if(strncmp(task->comm, name, strlen(name))==0)
		{
			//printk("*** %s [%d] parent %s\n",
			 //task->comm, task->pid, task->parent->comm );
			sprintf(buf, "%d", task->pid);
			ret = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC);	
			break;
		}    
	
	} while( (task = next_task(task)) != current );
  
	return ret;
}



/******************************************************************************
函数名称：	sntp_proc
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-02-10
函数说明：	SNTP对时处理任务。
参数说明：	无
返回值：  	成功返回0，失败返回负数。
修改记录：	
*/
static int sntp_proc(void *arg)
{

	while(1)
	{
		//设置服务器IP才可以对时，否则什么都不做
		if(g_sntp_para.serverip)
		{
		
			//参数没有初始化,等待.(这是因为任务可能已经运行，但参数没有初始化)
			if(!g_sntp_para_init)
			{
				//检查程序是否退出
				if(main_mod_is_exit())
				{
					complete_and_exit(&g_exit_sntp_proc, 0);		
				}				
				msleep(100);
				continue;
			}
			
			
			//检查SNTP是否运行,如果运行，kill掉进程
			sntp_check_run_and_kill(SNTP_PROGNAME);
			//运行SNTP(ntpd)
			sntp_run();
		}
		else
		{
			//检查程序是否退出
			if(main_mod_is_exit())
			{
				complete_and_exit(&g_exit_sntp_proc, 0);		
			}
			
			msleep(100);	
		}
			
	}
	
	return 0;		
}


/******************************************************************************
函数名称：	sntp_para
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-02-10
函数说明：	设置SNTP同步对时参数.						
参数说明：	
						serverip: 服务器IP
						tm: SNTP同步对时间隔
返回值：  	成功返回0，失败返回负数。
修改记录：	
*/
int sntp_setpara(unsigned int serverip, int tm)
{
	//置未初始化标志
	g_sntp_para_init = 0;
	//置参数修改标志
	g_sntp_paraflag = 1;

	//服务器IP
	g_sntp_para.serverip = serverip;
	
	if(tm <= 0)
	{
		tm = 64;
	}
	//同步时钟间隔
	g_sntp_para.tm = tm;
	
	//置初始化标志
	g_sntp_para_init = 1;
	
	return 0;	
}
/******************************************************************************
函数名称：	sntp_exit
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-02-10
函数说明：	SNTP对时退出.						
参数说明：	
						无
返回值：  	成功返回0，失败返回负数。
修改记录：	
*/
int sntp_exit(void)
{
	wait_for_completion(&g_exit_sntp_proc);
	return 0;	
}

/******************************************************************************
函数名称：	sntp_init
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-02-10
函数说明：	SNTP对时初始化.						
参数说明：	
						无
返回值：  	成功返回0，失败返回负数。
修改记录：	
*/
int sntp_init(void)
{
	pid_t pid;
	struct task_struct * ts;
	struct sched_param sp;
 
	init_completion(&g_exit_sntp_proc); 
	//创建SNTP处理任务
	pid = kernel_thread(sntp_proc, NULL,CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
	ts = find_task_by_vpid(pid);
	if(ts == NULL)
	{
		printk("%s:pid=%d,ts=%p\r\n",__func__,pid,ts);
		return -1;
	}
		
	// 设置调度策略和优先级
	sp.sched_priority = 13;
	sched_setscheduler(ts,SCHED_FIFO,&sp);		
	// 设置进程名字
	set_task_comm(ts, "sntp_proc");			
	
	
	return 0;	
}

#endif
