/*
 * time.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 */


#include<types.h>
#include<lib.h>
#include<errno.h>
#include<interrupt.h>
#include<proc.h>
#include<sp.h>
#include<mp.h>
#include<lock.h>
#include<mm.h>
#include<signal.h>
#include<debug.h>
#include<time.h>
#include<test.h>


//#define DEBUG_TIME 1

#ifdef DEBUG_TIME
	#define STATIC
	#define INLINE
#else
	#define STATIC static
	#define INLINE inline
#endif


/***************************************************************************************************
 *
 * 共通
 *
 ***************************************************************************************************/


enum{
	/* Timer flags. */
	TIMER_WAKE =	1<<0,	/* Process wake timer. */
	TIMER_SIG =		1<<1,	/* Send signal timer. */
	TIMER_DEL =		1<<2,	/* Deleted timer. */
};


#define GATE flag 		/* TIMER構造体用、flagメンバーをスピンロックゲートに読み変える。 */

static void linkTimer(TIMER *timer,TIMER *link,uint time);


/***************************************************************************************************
 *
 * クロックの測定
 *
 ***************************************************************************************************/


int clock_1micro;	/* 1micros */
int clock_1m;		/* 1ms */


/*
 * GLOBAL
 * cpuクロック数を計測する
 * 返り値 : クロック数
 */
int count_cpu_clock()
{
	uint cpu_clock;
	int low,high;
	long long r_beg,r_end;
	double time;


 	outb(PIT_CTR,0x30);	/* CLK0,LSB.MSB,モード0,binary */
	outb(PIT_CNR0,0);	/* LSB */
	outb(PIT_CNR0,0);	/* MSB */

 	r_beg=rdtsc();		/* clock counter */

	for(;;)if(rdtsc()-r_beg>0x400000)break;		/* 遅延 */

	r_end=rdtsc();

	low=inb(PIT_CNR0);
	high=inb(PIT_CNR0);

	time=(0xffff-low-(high<<8))/(double)PIT_HZ;
	cpu_clock=(r_end-r_beg)/time;

 	clock_1micro = cpu_clock / 1000000 + 1;		/* 1micros */
 	clock_1m = cpu_clock / 1000 + 1;			/* 1ms */

	return cpu_clock;
}


/***************************************************************************************************
 *
 * 8254 interval timer
 *
 ***************************************************************************************************/


//=================================== プライベート =======================================


static uint tasking_count=				/* シングルプロセッサタスキング用タイマーカウント */
	PIT_HZ/(1000/TASK_TIME);
static TIMER intervalTimer;				/* Interval timer link. */
static TIMER *intervalLinkWait;
static int interval_current_count;		/* タイマーの現在タイムカウント値. */


/*
 * 8254 interval timer handler
 */
STATIC int interval_timer_handler()
{
	TIMER *p;


	enter_spinlock(&intervalTimer.GATE);
	{
		if (intervalTimer.count > 0)
		{
			p = intervalTimer.next;

			if (p->count == interval_current_count)
			{
				p->next->prev = p->prev;
				p->prev->next = p->next;
				p->count = 0;

				/* 起床。 */
				if (p->flag != TIMER_DEL)
						p->handle(p);
				p->flag = 0;

				/* 初期値があるときは待ちリンクにリンクさせて後でセットする */
				if (p->init != 0)
				{
					p->next = intervalLinkWait;
					intervalLinkWait = p;
				}

				intervalTimer.count -= 1;
			}
			else
				p->count -= interval_current_count;
		}

		/* 初期値があるものをリンクし直す */
		for (p = intervalLinkWait; p != NULL; p = intervalLinkWait)
		{
			intervalLinkWait = p->next;
			linkTimer(p, &intervalTimer, p->init);
		}

		/* No timer */
		if(intervalTimer.count==0)
		{
			if(MFPS_addres==0)
			{
				tasking_count=PIT_HZ/(1000/TASK_TIME);
				init_sp_task();
			}
		}
		else
		{
			interval_current_count=intervalTimer.next->count;

			if(MFPS_addres==0)
			{
				if((tasking_count-=interval_current_count)==0)
					tasking_count=PIT_HZ/(1000/TASK_TIME);

				if(tasking_count<interval_current_count)
					interval_current_count=tasking_count;
			}

			/* タイマーを設定する */
			outb(PIT_CNR0,interval_current_count&0xff);
			outb(PIT_CNR0,interval_current_count>>8);
		}
	}
	exit_spinlock(&intervalTimer.GATE);

	return 1;
}


//=================================== パブリック =======================================


/*
 * クロック数に変換。
 * parameters : time(ms)
 * return : クロック数
 */
STATIC INLINE uint clockTime(uint time)
{
	return PIT_HZ*time/1000;
}


/*
 * Set interval timer.
 * parameters : time(クロック数)
 * rturn : new time
 */
STATIC uint setIntervalTimer1(uint time)
{
	uint count;


	if(intervalTimer.count==0)
	{
		count=time;

		if(MFPS_addres==0)
		{
			irq_entry[IRQ0]=interval_timer_handler;
			outb(PIT_CTR,0x30);					/* One shot timer(mode 0) */
			if(time>PIT_HZ/(1000/TASK_TIME))
				count=PIT_HZ/(1000/TASK_TIME);
		}

		/* タイマー設定 */
		if(count>0x10000)interval_current_count=0x10000;
		else interval_current_count=count;
		outb(PIT_CNR0,interval_current_count&0xff);
		outb(PIT_CNR0,interval_current_count>>8);
	}
	else
	{
		count=inb(PIT_CNR0);
		count+=inb(PIT_CNR0)<<8;
		time+=count;
	}

	return time;
}


/*
 * parameters : time(クロック数),setIntervalTimer1()の引数のtime
 */
STATIC void setIntervalTimer2(uint time,uint first_time)
{
	if((time!=first_time)&&(time<interval_current_count))
	{
		/* タイマー設定 */
		interval_current_count=time;
		outb(PIT_CNR0,first_time&0xff);
		outb(PIT_CNR0,first_time>>8);
	}
}


/*
 * parameters : time(クロック数),setIntervalTimer1()の引数のtime
 */
STATIC void initIntervalTimer()
{
	if(MFPS_addres)
	{
		irq_entry[IRQ0]=interval_timer_handler;
		outb(PIT_CTR,0x30);						/* One shot timer(mode 0). */
		release_irq_mask(0);
	}

	intervalTimer.next = intervalTimer.prev = &intervalTimer;
}


/***************************************************************************************************
 *
 * MC146818 realclock timer
 *
 ***************************************************************************************************/


enum{
	REAL_TIMER_RATE=500,	/* Real-clock timer rate(ms) */
};


//================================== プライベート ========================================


static TIMER realTimer;			/* Real timer link queue */
static uint utc;				/* current UTC(number of seconds since 00:00:00.1.1.1970). */
static uint64 curRdtsc;			/* 現在のRDTSC値。 */


/*
 * 十進数を二進数に変換する。
 */
STATIC INLINE uchar bcd_to_bin(uchar value)
{
	return value - 6 * (value >> 4);
}


/*
 * MC146818 real timer handler
 * return : プロセススイッチしない
 */
STATIC int real_timer_handler()
{
	static int half = 0;
	uint64 half64;
	TIMER *timer,*save;


	// Status register C を読んで割り込みをリセットする。
	read_cmos(CMOS_STRC);

	if (0 < realTimer.count){
		TIMER *realLinkWait = NULL;
		int tm;

		enter_spinlock(&realTimer.GATE);
		{
			tm = REAL_TIMER_RATE;
			for (timer = realTimer.next; timer != &realTimer;){
				if (timer->count <= tm){
					save = timer->next;
					timer->next->prev = timer->prev;
					timer->prev->next = timer->next;
					timer->next = timer->prev = timer;
					tm -= timer->count;
					timer->count = 0;
					if (timer->flag != TIMER_DEL){
						timer->handle(timer);
					}
					realTimer.count -= 1;

					// 初期値があるときは待ちリンクにつないで後でセットする
					if (timer->init != 0){
						timer->next = realLinkWait;
						realLinkWait = timer;
					}
					else{
						timer->flag = 0;
					}

					timer = save;
				}
				else{
					timer->count -= tm;
					break;
				}
			}

			// 初期値があるものをリンクし直す
			for (timer = realLinkWait; timer != NULL; timer = save){
				save = timer->next;
				linkTimer(timer, &realTimer, timer->init);
			}
		}
		exit_spinlock(&realTimer.GATE);
	}

	/* UTCを更新する。 */
	half ^= 1;				/* 500msごと更新。 */
	utc += half;
	
	/* 現在のRDTSC値を代入。 */
	half64 = half;
	curRdtsc *= half64 ^ 1;
	curRdtsc += rdtsc() * half64;
	
	return 0;
}


//================================== パブリック ========================================


/*
 * UTCを設定する。
 * parameters : seconds
 */
STATIC void INLINE setUtc(uint seconds)
{
	utc = seconds;
}


/*
 * get current time from cmos
 * parameters : time structure
 */
STATIC void get_time_from_cmos()
{
	/* 月の日数の合計 */
	static ushort sumMonth[2][13]=
	{{0,0,31,59,90,120,151,181,212,243,273,304,334},
	 {0,0,31,60,91,121,152,182,213,244,274,305,335},
	};

	struct TIME{
		uchar second;		/* seconds */
		uchar minute;		/* minutes */
		uchar hour;			/* hours */
		uchar day;			/* days */
		uchar month;		/* months */
		ushort year;		/* years */
	}time;
	int days;

	do
	{
		time.second = bcd_to_bin(read_cmos(CMOS_SCD));
		time.minute = bcd_to_bin(read_cmos(CMOS_MNT));
		time.hour = bcd_to_bin(read_cmos(CMOS_HOR));
		time.day = bcd_to_bin(read_cmos(CMOS_DAY));
		time.month = bcd_to_bin(read_cmos(CMOS_MTH));
		time.year = bcd_to_bin(read_cmos(CMOS_YER));
	}while (bcd_to_bin(read_cmos(CMOS_SCD)) < time.second);

	time.year += (time.year < 70)? 2000 : 1900;

	/* UTCの計算 */
	days = (time.year - 1970) * 365;
	days += (time.year - 1 - 1968) / 4;
	days += sumMonth[(time.year % 4) == 0][time.month];
	days += time.day - 1;
	utc = ((days * 24 + time.hour) * 60 + time.minute) * 60 + time.second;

	/* GMTタイムに調整 */
	utc -= LOCAL_TIME_TOKYO;
}


/*
 *
 */
STATIC void initRealTimer()
{
	irq_entry[IRQ8]=real_timer_handler;
	get_time_from_cmos();				/* get current time from cmos. */
	write_cmos(CMOS_STRA,0x2f);			/* time bese 0x010(default)|周期タイマーレート65536/(2の0xf乗)=2HZ(500ms) */
	read_cmos(CMOS_STRC);				/* Status register C を読んで割り込みをリセットする。 */
	write_cmos(CMOS_STRB,0x42);			/* 24hour,周期割り込みオン。 */
	release_irq_mask(8);
	
	realTimer.next = realTimer.prev = &realTimer;
}


//================================== グローバル ========================================


void gettimeofday(struct timeval *tp)
{
	tp->tv_sec = utc;
	tp->tv_usec = (rdtsc() - curRdtsc) / clock_1micro;
	if (1000000 <= tp->tv_usec){
		tp->tv_sec += 1;
		tp->tv_usec -= 1000000;
	}
}


/***************************************************************************************************
 *
 * itimer
 *
 ***************************************************************************************************/


//================================== パブリック ========================================


STATIC void getItimer(int which,struct itimerval *value)
{
	uint64 sec,micro;
	ITIMER *itimer = get_current_task()->itimer;
	
	
	micro = itimer[which].init / clock_1micro;
	sec = micro / 1000000;
	value->it_interval.tv_sec = sec;
	value->it_interval.tv_usec = micro - sec * 1000000;

	micro = itimer[which].cur / clock_1micro;
	sec = micro / 1000000;
	value->it_value.tv_sec = sec;
	value->it_value.tv_usec = micro - sec * 1000000;
}


STATIC void setItimer(int which,const struct itimerval *value, struct itimerval *ovalue)
{
	uint64 micro;
	ITIMER *itimer = get_current_task()->itimer;
	
	
	/* 前タイマー値の代入 */
	if (ovalue != NULL)
		getItimer(which,ovalue);
	
	/* タイマー値の設定 */
	micro = value->it_interval.tv_sec * 1000000 + value->it_interval.tv_usec;
	itimer[which].init = micro * clock_1micro;

	micro = value->it_value.tv_sec * 1000000 + value->it_value.tv_usec;
	itimer[which].cur = micro * clock_1micro;

}


//================================== グローバル ========================================


/*
 * parameters : ITIMER,経過時間（rdtsc値）,0=システムタイム
 */
void doItimer(ITIMER *itimer, uint64 time, int flag)
{
	/* タイマーが設定されているか */
	if ((itimer[0].cur | itimer[1].cur) == 0)
		return;
	
	if (flag == ITIME_SYS)
	{
		if (itimer[0].cur != 0)
		{
			itimer[0].cur -= time;
			if (itimer[0].cur <= 0)
			{
				sendSignal(get_current_task(),SIGPROF);
				itimer[0].cur = itimer[0].init;
			}
		}
	}
	if (itimer[1].cur != 0)
	{
		itimer[1].cur -= time;
		if (itimer[1].cur <= 0)
		{
			sendSignal(get_current_task(),SIGVTALRM);
			itimer[1].cur = itimer[1].init;
		}
	}
}


/***************************************************************************************************
 *
 * タイマー操作
 *
 ***************************************************************************************************/


//================================== パブリック ========================================


//《割》
// タイマーハンドラ
STATIC void handleWake(TIMER *timer)
{
/****************************************************************************************************************/
//printk("Time out!\n");
/****************************************************************************************************************/
	forceSendSignal(timer->proc, SIGWAKE, TASK_SIGNAL_WAIT | TASK_DEVICE_WAIT);
}


//《割》
// アラームハンドラ
STATIC void handleAlarm(TIMER *timer)
{
	sendSignal(timer->proc,SIGALRM);
}


//《注意》link->GATEで排他制御された中から呼び出されること
// タイマーリンクにinit接続する
STATIC void linkTimer(TIMER *timer, TIMER *link, uint time)
{
	uint tm = time;
	TIMER *p;
	
	for (p = link->next; p != link; p = p->next){
		if (tm < p->count){
			p->count -= tm;
			break;
		}
		else{
			tm -= p->count;
		}
	}
	timer->prev = p->prev;
	timer->next = p;
	p->prev->next = timer;
	p->prev = timer;
	timer->count = tm;
	link->count += 1;
}


/*
 * タイマーリンクから削除
 * 《注意》link->GATEで排他制御された中から呼び出されること
 */
STATIC void unlinkTimer(TIMER *timer, TIMER *link)
{
	if (timer->next == timer){
		return;
	}
	if (timer->next != link){
		timer->next->count += timer->count;
	}
	timer->next->prev = timer->prev;
	timer->prev->next = timer->next;
	timer->next = timer->prev = timer;
	timer->flag = 0;
	timer->init = 0;
	timer->count = 0;
	link->count -= 1;
}


/*
 * Set timer
 * 《注意》
 *  この関数は必ず del_from_schedule() の後で使うこと。もし、先にタイムアウトが起きると
 *  プロセスが復帰しない危険がある。
 * rturn : 0 or error number
 */
STATIC int setTimer(int flag, uint time, uint init, void (*handle)())
{
	uint first_time = 0;
	TIMER *procTimer = get_current_task()->timer;
	TIMER  *link;
	TIMER  *timer = NULL;
	int eflag;
	int i;
	
	if (time == 0){
		return 0;
	}

	// 空きタイマーの検索
	// 同タイマーフラグを探しそれに上書きする
	for (i = 0; i < TIMER_MAX ; ++i){
		if ((procTimer[i].flag == 0) || (procTimer[i].flag == TIMER_DEL)){
			timer = &procTimer[i];
		}
		else if (procTimer[i].flag == flag){
			timer = &procTimer[i];
			break;
		}
	}

	ASSERT(timer != NULL);

	if (time < REAL_TIMER_RATE){
		link = &intervalTimer;
		first_time = time = clockTime(time);
		time = setIntervalTimer1(time);
	}
	else{
		link = &realTimer;
	}

	enterCli(&eflag);
	enter_spinlock(&link->GATE);
	{
		/* リンクから削除。 */
		unlinkTimer(timer,link);

		/* リンクに接続。 */
		linkTimer(timer, link, time);
		if (link == &intervalTimer){
			setIntervalTimer2(time,first_time);
		}
		timer->flag = flag;
		timer->init = init;
		timer->proc = get_current_task();
		timer->handle = handle;
		timer->link = link;
    }
	exit_spinlock(&link->GATE);
	exitCli(&eflag);

	return 0;
}


STATIC void getTimer(int flag,struct itimerval *value)
{
	uint sec,mili,time = 0;
	TIMER *procTimer = get_current_task()->timer;
	TIMER *timer = NULL;
	int i;
	
	if (value == NULL){
		return;
	}
		
	for (i = 0; i < TIMER_MAX ; ++i){
		if ((procTimer[i].flag == 0) || (procTimer[i].flag == TIMER_DEL)){
			timer = &procTimer[i];
		}
		else if (procTimer[i].flag == flag){
			timer = &procTimer[i];

			cli();
			enter_spinlock(&timer->link->GATE);
			{
				if (timer->next != timer){
					TIMER *p;

					for (p = timer; p != timer->link; p = p->prev){
						time += p->count;
					}
				}
			}
			exit_spinlock(&timer->link->GATE);
			sti();
			break;
		}
	}

	ASSERT(timer != NULL)
	
	sec = timer->init / 1000;
	mili = timer->init - sec * 1000;
	value->it_interval.tv_sec = sec;
	value->it_interval.tv_usec = mili * 1000;

	sec = time / 1000;
	mili = time - sec * 1000;
	value->it_value.tv_sec = sec;
	value->it_value.tv_usec = mili * 1000;
}


void delAlarmTimer(){
	TIMER *timer = get_current_task()->timer;
	int i;
	
	/* 検索 */
	for (i = 0; i < TIMER_MAX ; ++i){
		if (timer[i].flag == TIMER_SIG)
		{
			timer[i].flag = TIMER_DEL;
			timer[i].init = 0;
			break;
		}
	}
}


//================================== グローバル ========================================


/*
 * Set wake timer.
 * parameters : time(ms)
 * 0 or error number
 */
int setWake(uint time)
{
	return setTimer(TIMER_WAKE,time,0,handleWake);
}


/*
 * Delete timer from timer link.
 * parameters : Process timer
 */
void delWakeTimer(TIMER *timer)
{
	int i;
	
	/* 検索 */
	for (i = 0; i < TIMER_MAX ; ++i){
		if (timer[i].flag == TIMER_WAKE)
		{
			timer[i].flag = TIMER_DEL;
			timer[i].init = 0;
			break;
		}
	}
}


/*
 * プロセスタイマーの初期化
 */
void initTimer(TIMER *timer, ITIMER *itimer)
{
	int i;
	
	memset(timer, 0, sizeof(TIMER) * TIMER_MAX);
	for (i = 0; i < TIMER_MAX; ++i){
		timer[i].next = timer[i].prev = &timer[i];
	}

	memset(itimer, 0, sizeof(ITIMER) * ITIMER_MAX);
}


/*
 * プロセスタイマーの開放
 */
void releaseTimer(TIMER *timer)
{
	int i;
	
	for (i = 0; i < TIMER_MAX; ++i){
		if (timer[i].next != &timer[i]){
			cli();
			enter_spinlock(&timer[i].link->GATE);
			{
				unlinkTimer(&timer[i], timer[i].link);
			}
			exit_spinlock(&timer[i].link->GATE);
			sti();
		}
	}
}


/***************************************************************************************************
 *
 * < システムコール >
 *
 ***************************************************************************************************/


/*
 * get Coordinated Universal Time(number of seconds since 00:00:00.1.1.1970)
 */
int sys_time(int *tp)
{
	if(tp != NULL)
	{
		if (checkMem(tp,sizeof(int)) == -1)
			return -EFAULT;
		*tp = utc;
	}

	return (int)utc;
}


int sys_sleep(uint seconds)
{
	int time;
	PROC *proc = get_current_task();
	struct itimerval value;

	setTimer(TIMER_WAKE,seconds*1000,0,handleWake);
	sigWait();
	getTimer(TIMER_WAKE,&value);
	time = value.it_value.tv_sec;
	delWakeTimer(proc->timer);

	return time;
}


//《割》
void sys_wake(void *proc)
{
	forceSendSignal(proc, SIGWAKE, TASK_SIGNAL_WAIT);
}


int sys_alarm(uint seconds)
{
	if (0 < seconds){
		setTimer(TIMER_SIG, seconds*1000, 0, handleAlarm);
	}
	else{
		delAlarmTimer();
	}
	
	return 0;
}


int sys_settime(uint seconds)
{
	if(get_current_task()->uid!=0)return -EPERM;

	setUtc(seconds);

	return 0;
}


int sys_gettimeofday(struct timeval *tp, void *tzp)
{
	if (checkMem(tp, sizeof(struct timeval)) == -1){
		return 0;
	}

	gettimeofday(tp);

	return 0;	
}


int sys_getitimer(int which, struct itimerval *value)
{
	if (checkMem(value,sizeof(struct itimerval)) == -1)
		return -EFAULT;

	switch (which)
	{
		case ITIMER_REAL:
			getTimer(TIMER_SIG,value);
			break;
		case ITIMER_VIRTUAL:
			getItimer(ITIME_PRC,value);
			break;
		case ITIMER_PROF:
			getItimer(ITIME_SYS,value);
			break;
		default:
			return -EINVAL;
	}
	
	return 0;
}


int sys_setitimer(int which, const struct itimerval *value, struct itimerval *ovalue)
{
	uint mili_cur,mili_init;
	
	
	if (checkMem((void*)value,sizeof(struct itimerval)) == -1)
		return -EFAULT;
	if ((ovalue != NULL) && (checkMem(ovalue,sizeof(struct itimerval)) == -1))
		return -EFAULT;

	switch (which)
	{
		case ITIMER_REAL:
			getTimer(TIMER_SIG,ovalue);
			mili_init = value->it_interval.tv_sec * 1000;
			mili_init += value->it_interval.tv_usec / 1000;
			mili_cur = value->it_value.tv_sec * 1000;
			mili_cur += value->it_value.tv_usec / 1000;
			if ((0 < mili_init) && (0 < mili_cur)){
				setTimer(TIMER_SIG, mili_cur, mili_init, handleAlarm);
			}
			else{
				delAlarmTimer();
			}
			break;
		case ITIMER_VIRTUAL:
			setItimer(ITIME_PRC,value,ovalue);
			break;
		case ITIMER_PROF:
			setItimer(ITIME_SYS,value,ovalue);
			break;
		default:
			return -EINVAL;
	}
	
	return 0;
}


/***************************************************************************************************
 *
 * 初期化
 *
 ***************************************************************************************************/


//================================== グローバル ========================================


/*
 * Init time
 */
void init_time()
{
	// Init interval timer.
	initIntervalTimer();

	// Init MC146818 real timer.
	initRealTimer();
}

/*************************************************************************************/
void test_time(uint signal)
{
	if(get_current_task()==(PROC*)0x150400)
	{
		printk("dosignal signal=%x ",signal);
	}	
}
/**************************************************************************************/
